mirror of
https://github.com/dataease/dataease.git
synced 2026-05-14 12:22:10 +08:00
@@ -1195,6 +1195,10 @@ export default {
|
||||
load_data: 'Load Data'
|
||||
},
|
||||
chart: {
|
||||
self: 'Self',
|
||||
total_row: 'Entire Row',
|
||||
custom: 'Custom',
|
||||
apply_to: 'Apply To',
|
||||
align: 'Alignment',
|
||||
reset: 'Reset',
|
||||
chart_refresh_tips: 'Chart refresh settings take precedence over dashboard refresh settings',
|
||||
|
||||
@@ -1158,6 +1158,10 @@ export default {
|
||||
load_data: '載入資料'
|
||||
},
|
||||
chart: {
|
||||
self: '自己',
|
||||
total_row: '整行',
|
||||
custom: '自定義',
|
||||
apply_to: '作用範圍',
|
||||
align: '對齊方式',
|
||||
reset: '重置',
|
||||
chart_refresh_tips: '圖表刷新設定優先於儀表板刷新設定',
|
||||
|
||||
@@ -1163,6 +1163,10 @@ export default {
|
||||
load_data: '加载数据'
|
||||
},
|
||||
chart: {
|
||||
self: '自己',
|
||||
total_row: '整行',
|
||||
custom: '自定义',
|
||||
apply_to: '作用范围',
|
||||
align: '对齐方式',
|
||||
reset: '重置',
|
||||
chart_refresh_tips: '图表刷新设置优先于仪表板刷新设置',
|
||||
|
||||
@@ -1106,7 +1106,7 @@ init()
|
||||
v-model="state.editTableThresholdDialog"
|
||||
:title="t('chart.threshold')"
|
||||
:visible="state.editTableThresholdDialog"
|
||||
width="1050px"
|
||||
width="1250px"
|
||||
class="dialog-css"
|
||||
append-to-body
|
||||
>
|
||||
|
||||
@@ -39,7 +39,9 @@ const thresholdCondition = {
|
||||
type: 'fixed',
|
||||
dynamicField: { summary: 'value' },
|
||||
dynamicMinField: { summary: 'value' },
|
||||
dynamicMaxField: { summary: 'value' }
|
||||
dynamicMaxField: { summary: 'value' },
|
||||
target: 'self',
|
||||
targetFieldId: null
|
||||
}
|
||||
const textOptions = [
|
||||
{
|
||||
@@ -201,6 +203,12 @@ const valueOptions = [
|
||||
]
|
||||
const predefineColors = COLOR_PANEL
|
||||
|
||||
const targetOptions = [
|
||||
{ label: t('chart.self'), value: 'self' },
|
||||
{ label: t('chart.total_row'), value: 'total_row' },
|
||||
{ label: t('chart.custom'), value: 'custom' }
|
||||
]
|
||||
|
||||
const state = reactive({
|
||||
thresholdArr: [] as TableThreshold[],
|
||||
fields: [],
|
||||
@@ -488,7 +496,7 @@ init()
|
||||
class="line-item"
|
||||
:gutter="12"
|
||||
>
|
||||
<el-col :span="!isNotEmptyAndNull(item) ? 15 : 3">
|
||||
<el-col :span="!isNotEmptyAndNull(item) ? 11 : 3">
|
||||
<el-form-item class="form-item">
|
||||
<el-select v-model="item.term" @change="changeThreshold">
|
||||
<el-option-group
|
||||
@@ -530,7 +538,7 @@ init()
|
||||
<!--不是between 不是动态值-->
|
||||
<el-col
|
||||
v-if="isNotEmptyAndNull(item) && !isBetween(item) && !isDynamic(item)"
|
||||
:span="12"
|
||||
:span="6"
|
||||
style="text-align: center"
|
||||
>
|
||||
<el-form-item class="form-item">
|
||||
@@ -582,7 +590,7 @@ init()
|
||||
</el-col>
|
||||
<!--不是between 是动态值-->
|
||||
<!--动态值 字段-->
|
||||
<el-col v-if="isNotEmptyAndNull(item) && !isBetween(item) && isDynamic(item)" :span="6">
|
||||
<el-col v-if="isNotEmptyAndNull(item) && !isBetween(item) && isDynamic(item)" :span="3">
|
||||
<el-form-item class="form-item">
|
||||
<el-select
|
||||
v-model="item.dynamicField.fieldId"
|
||||
@@ -620,7 +628,7 @@ init()
|
||||
<!--动态值聚合方式-->
|
||||
<el-col
|
||||
v-if="isNotEmptyAndNull(item) && !isBetween(item) && isDynamic(item)"
|
||||
:span="6"
|
||||
:span="3"
|
||||
style="text-align: center"
|
||||
>
|
||||
<el-form-item class="form-item">
|
||||
@@ -643,7 +651,7 @@ init()
|
||||
<!--between 开始值-->
|
||||
<el-col
|
||||
v-if="isNotEmptyAndNull(item) && isBetween(item) && !isDynamic(item)"
|
||||
:span="5"
|
||||
:span="2"
|
||||
style="text-align: center"
|
||||
>
|
||||
<el-form-item class="form-item">
|
||||
@@ -669,7 +677,7 @@ init()
|
||||
<!--between 结束值-->
|
||||
<el-col
|
||||
v-if="isNotEmptyAndNull(item) && isBetween(item) && !isDynamic(item)"
|
||||
:span="5"
|
||||
:span="2"
|
||||
style="text-align: center"
|
||||
>
|
||||
<el-form-item class="form-item">
|
||||
@@ -689,7 +697,7 @@ init()
|
||||
<el-col
|
||||
v-if="isNotEmptyAndNull(item) && isBetween(item) && isDynamic(item)"
|
||||
class="minField"
|
||||
:span="3"
|
||||
:span="2"
|
||||
>
|
||||
<el-form-item class="form-item">
|
||||
<el-select v-model="item.dynamicMinField.fieldId" @change="addField(item)">
|
||||
@@ -742,7 +750,7 @@ init()
|
||||
<el-col
|
||||
v-if="isBetween(item) && isDynamic(item)"
|
||||
class="term"
|
||||
:span="2"
|
||||
:span="1"
|
||||
style="margin-top: 4px; text-align: center"
|
||||
>
|
||||
<span style="margin: 0 -5px">
|
||||
@@ -753,7 +761,7 @@ init()
|
||||
<el-col
|
||||
v-if="isNotEmptyAndNull(item) && isBetween(item) && isDynamic(item)"
|
||||
class="maxField"
|
||||
:span="3"
|
||||
:span="2"
|
||||
>
|
||||
<el-form-item class="form-item">
|
||||
<el-select v-model="item.dynamicMaxField.fieldId" @change="addField(item)">
|
||||
@@ -803,26 +811,75 @@ init()
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="3">
|
||||
<el-col :span="item.target === 'custom' ? 3 : 5">
|
||||
<el-form-item class="form-item">
|
||||
<el-select
|
||||
v-model="item.target"
|
||||
style="width: 100%"
|
||||
:placeholder="t('chart.apply_to')"
|
||||
@change="changeThreshold"
|
||||
>
|
||||
<el-option
|
||||
v-for="opt in targetOptions"
|
||||
:key="opt.value"
|
||||
:label="opt.label"
|
||||
:value="opt.value"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="2" v-if="item.target === 'custom'">
|
||||
<el-form-item class="form-item">
|
||||
<el-select
|
||||
v-model="item.targetFieldId"
|
||||
:placeholder="t('chart.field')"
|
||||
style="width: 100%"
|
||||
@change="changeThreshold"
|
||||
>
|
||||
<el-option
|
||||
class="series-select-option"
|
||||
v-for="targetField in state.fields"
|
||||
:key="targetField.id"
|
||||
:label="targetField.name"
|
||||
:value="targetField.id"
|
||||
>
|
||||
<el-icon style="margin-right: 8px">
|
||||
<Icon
|
||||
><component
|
||||
:class="`field-icon-${
|
||||
fieldType[[2, 3].includes(targetField.deType) ? 2 : 0]
|
||||
}`"
|
||||
class="svg-icon"
|
||||
:is="iconFieldMap[fieldType[targetField.deType]]"
|
||||
></component
|
||||
></Icon>
|
||||
</el-icon>
|
||||
{{ targetField.name }}
|
||||
</el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
|
||||
<el-col :span="2">
|
||||
<el-form-item class="form-item" :label="t('chart.textColor')">
|
||||
<el-color-picker
|
||||
is-custom
|
||||
v-model="item.color"
|
||||
show-alpha
|
||||
:trigger-width="68"
|
||||
:trigger-width="54"
|
||||
class="color-picker-style"
|
||||
:predefine="predefineColors"
|
||||
@change="changeThreshold"
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="3">
|
||||
<el-col :span="2">
|
||||
<el-form-item class="form-item" :label="t('chart.backgroundColor')">
|
||||
<el-color-picker
|
||||
is-custom
|
||||
size="default"
|
||||
v-model="item.backgroundColor"
|
||||
:trigger-width="68"
|
||||
:trigger-width="54"
|
||||
show-alpha
|
||||
class="color-picker-style"
|
||||
:predefine="predefineColors"
|
||||
|
||||
@@ -620,6 +620,13 @@ export function getConditions(chart: Chart) {
|
||||
const conditions = threshold.tableThreshold ?? []
|
||||
|
||||
const dimFields = [...chart.xAxis, ...chart.xAxisExt].map(i => i.dataeaseName)
|
||||
const allFields = [...chart.xAxis, ...chart.xAxisExt, ...chart.yAxis, ...chart.yAxisExt]
|
||||
const fieldIdToName = allFields.reduce((acc, f) => {
|
||||
acc[f.id] = f.dataeaseName
|
||||
return acc
|
||||
}, {})
|
||||
const allColumnNames = allFields.map(f => f.dataeaseName)
|
||||
|
||||
if (conditions?.length > 0) {
|
||||
const { tableCell, basicStyle, tableHeader } = parseJson(chart.customAttr)
|
||||
// 合并单元格时斑马纹失效
|
||||
@@ -640,17 +647,53 @@ export function getConditions(chart: Chart) {
|
||||
? tableHeader.tableHeaderBgColor
|
||||
: hexColorToRGBA(tableHeader.tableHeaderBgColor, basicStyle.alpha)
|
||||
const filedValueMap = getFieldValueMap(chart)
|
||||
|
||||
// Build map of target column -> rules
|
||||
const targetRulesMap = {} // columnName -> Array<{ rule, sourceField }>
|
||||
|
||||
for (let i = 0; i < conditions.length; i++) {
|
||||
const field = conditions[i]
|
||||
const fieldItem = conditions[i]
|
||||
if (!fieldItem.conditions) continue;
|
||||
|
||||
for (let j = 0; j < fieldItem.conditions.length; j++) {
|
||||
const rule = fieldItem.conditions[j]
|
||||
let targets = []
|
||||
if (rule.target === 'total_row') {
|
||||
targets = allColumnNames
|
||||
} else if (rule.target === 'custom' && rule.targetFieldId) {
|
||||
const targetName = fieldIdToName[rule.targetFieldId]
|
||||
if (targetName) targets = [targetName]
|
||||
} else {
|
||||
// Default to self
|
||||
targets = [fieldItem.field.dataeaseName]
|
||||
}
|
||||
|
||||
targets.forEach(targetName => {
|
||||
if (!targetRulesMap[targetName]) {
|
||||
targetRulesMap[targetName] = []
|
||||
}
|
||||
targetRulesMap[targetName].push({
|
||||
rule: rule,
|
||||
sourceField: fieldItem.field
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// Generate S2 conditions for each target column
|
||||
for (const targetName in targetRulesMap) {
|
||||
const rules = targetRulesMap[targetName]
|
||||
let defaultValueColor = valueColor
|
||||
let defaultBgColor = valueBgColor
|
||||
// 透视表表头颜色配置
|
||||
if (chart.type === 'table-pivot' && dimFields.includes(field.field.dataeaseName)) {
|
||||
// 透视表表头颜色配置 (Use target column type to decide default color?)
|
||||
// If target is a dimension in pivot table, use header color
|
||||
if (chart.type === 'table-pivot' && dimFields.includes(targetName)) {
|
||||
defaultValueColor = headerValueColor
|
||||
defaultBgColor = headerValueBgColor
|
||||
}
|
||||
|
||||
res.text.push({
|
||||
field: field.field.dataeaseName,
|
||||
field: targetName,
|
||||
mapping(value, rowData) {
|
||||
// 总计小计
|
||||
if (rowData?.isGrandTotals || rowData?.isSubTotals) {
|
||||
@@ -660,13 +703,14 @@ export function getConditions(chart: Chart) {
|
||||
if (rowData?.id && rowData?.field === rowData.id) {
|
||||
return null
|
||||
}
|
||||
|
||||
return {
|
||||
fill: mappingColor(value, defaultValueColor, field, 'color', filedValueMap, rowData)
|
||||
fill: mappingColor(value, defaultValueColor, rules, 'color', filedValueMap, rowData)
|
||||
}
|
||||
}
|
||||
})
|
||||
res.background.push({
|
||||
field: field.field.dataeaseName,
|
||||
field: targetName,
|
||||
mapping(value, rowData) {
|
||||
if (rowData?.isGrandTotals || rowData?.isSubTotals) {
|
||||
return null
|
||||
@@ -677,7 +721,7 @@ export function getConditions(chart: Chart) {
|
||||
const fill = mappingColor(
|
||||
value,
|
||||
defaultBgColor,
|
||||
field,
|
||||
rules,
|
||||
'backgroundColor',
|
||||
filedValueMap,
|
||||
rowData
|
||||
@@ -693,12 +737,26 @@ export function getConditions(chart: Chart) {
|
||||
return res
|
||||
}
|
||||
|
||||
export function mappingColor(value, defaultColor, field, type, filedValueMap?, rowData?) {
|
||||
export function mappingColor(value, defaultColor, rules, type, filedValueMap?, rowData?) {
|
||||
let color = null
|
||||
for (let i = 0; i < field.conditions.length; i++) {
|
||||
|
||||
// If called from old code (rules is a single field object), adapt it
|
||||
if (rules && !Array.isArray(rules) && rules.conditions) {
|
||||
const field = rules;
|
||||
rules = field.conditions.map(c => ({ rule: c, sourceField: field.field }));
|
||||
}
|
||||
|
||||
for (let i = 0; i < rules.length; i++) {
|
||||
const { rule, sourceField } = rules[i]
|
||||
let flag = false
|
||||
const t = field.conditions[i]
|
||||
const t = rule
|
||||
let tv, max, min
|
||||
|
||||
let checkValue = value;
|
||||
if (sourceField.dataeaseName && rowData) {
|
||||
checkValue = rowData[sourceField.dataeaseName];
|
||||
}
|
||||
|
||||
if (t.type === 'dynamic') {
|
||||
if (t.term === 'between') {
|
||||
max = parseFloat(getValue(t.dynamicMaxField, filedValueMap, rowData))
|
||||
@@ -714,40 +772,44 @@ export function mappingColor(value, defaultColor, field, type, filedValueMap?, r
|
||||
tv = t.value
|
||||
}
|
||||
}
|
||||
if (field.field.deType === 2 || field.field.deType === 3 || field.field.deType === 4) {
|
||||
|
||||
const val = checkValue;
|
||||
|
||||
if (sourceField.deType === 2 || sourceField.deType === 3 || sourceField.deType === 4) {
|
||||
tv = parseFloat(tv)
|
||||
const numVal = parseFloat(val)
|
||||
if (t.term === 'eq') {
|
||||
if (value === tv) {
|
||||
if (numVal === tv) {
|
||||
color = t[type]
|
||||
flag = true
|
||||
}
|
||||
} else if (t.term === 'not_eq') {
|
||||
if (value !== tv) {
|
||||
if (numVal !== tv) {
|
||||
color = t[type]
|
||||
flag = true
|
||||
}
|
||||
} else if (t.term === 'lt') {
|
||||
if (value < tv) {
|
||||
if (numVal < tv) {
|
||||
color = t[type]
|
||||
flag = true
|
||||
}
|
||||
} else if (t.term === 'gt') {
|
||||
if (value > tv) {
|
||||
if (numVal > tv) {
|
||||
color = t[type]
|
||||
flag = true
|
||||
}
|
||||
} else if (t.term === 'le') {
|
||||
if (value !== null && value <= tv) {
|
||||
if (val !== null && numVal <= tv) {
|
||||
color = t[type]
|
||||
flag = true
|
||||
}
|
||||
} else if (t.term === 'ge') {
|
||||
if (value !== null && value >= tv) {
|
||||
if (val !== null && numVal >= tv) {
|
||||
color = t[type]
|
||||
flag = true
|
||||
}
|
||||
} else if (t.term === 'between') {
|
||||
if (value !== null && min <= value && value <= max) {
|
||||
if (val !== null && min <= numVal && numVal <= max) {
|
||||
color = t[type]
|
||||
flag = true
|
||||
}
|
||||
@@ -755,49 +817,47 @@ export function mappingColor(value, defaultColor, field, type, filedValueMap?, r
|
||||
color = t[type]
|
||||
flag = true
|
||||
} else if (t.term === 'null') {
|
||||
if (value === null || value === undefined || value === '') {
|
||||
if (val === null || val === undefined || val === '') {
|
||||
color = t[type]
|
||||
flag = true
|
||||
}
|
||||
} else if (t.term === 'not_null') {
|
||||
if (value !== null && value !== undefined && value !== '') {
|
||||
if (val !== null && val !== undefined && val !== '') {
|
||||
color = t[type]
|
||||
flag = true
|
||||
}
|
||||
}
|
||||
if (flag) {
|
||||
break
|
||||
} else if (i === field.conditions.length - 1) {
|
||||
color = defaultColor
|
||||
}
|
||||
} else if (field.field.deType === 0 || field.field.deType === 5) {
|
||||
} else if (sourceField.deType === 0 || sourceField.deType === 5) {
|
||||
if (t.term === 'eq') {
|
||||
if (value === tv) {
|
||||
if (val === tv) {
|
||||
color = t[type]
|
||||
flag = true
|
||||
}
|
||||
} else if (t.term === 'not_eq') {
|
||||
if (value !== tv) {
|
||||
if (val !== tv) {
|
||||
color = t[type]
|
||||
flag = true
|
||||
}
|
||||
} else if (t.term === 'like') {
|
||||
if (value.includes(tv)) {
|
||||
if (val && val.includes(tv)) {
|
||||
color = t[type]
|
||||
flag = true
|
||||
}
|
||||
} else if (t.term === 'not like') {
|
||||
if (!value.includes(tv)) {
|
||||
if (val && !val.includes(tv)) {
|
||||
color = t[type]
|
||||
flag = true
|
||||
}
|
||||
} else if (t.term === 'null') {
|
||||
if (value === null || value === undefined || value === '') {
|
||||
if (val === null || val === undefined || val === '') {
|
||||
color = t[type]
|
||||
flag = true
|
||||
}
|
||||
} else if (t.term === 'not_null') {
|
||||
if (value !== null && value !== undefined && value !== '') {
|
||||
if (val !== null && val !== undefined && val !== '') {
|
||||
color = t[type]
|
||||
flag = true
|
||||
}
|
||||
@@ -807,18 +867,16 @@ export function mappingColor(value, defaultColor, field, type, filedValueMap?, r
|
||||
}
|
||||
if (flag) {
|
||||
break
|
||||
} else if (i === field.conditions.length - 1) {
|
||||
color = defaultColor
|
||||
}
|
||||
} else {
|
||||
const fc = field.conditions[i]
|
||||
const fc = rule
|
||||
if (fc.term === 'null') {
|
||||
if (value === null || value === undefined || value === '') {
|
||||
if (val === null || val === undefined || val === '') {
|
||||
color = fc[type]
|
||||
flag = true
|
||||
}
|
||||
} else if (fc.term === 'not_null') {
|
||||
if (value !== null && value !== undefined && value !== '') {
|
||||
if (val !== null && val !== undefined && val !== '') {
|
||||
color = fc[type]
|
||||
flag = true
|
||||
}
|
||||
@@ -827,61 +885,65 @@ export function mappingColor(value, defaultColor, field, type, filedValueMap?, r
|
||||
break
|
||||
}
|
||||
// time
|
||||
if (!tv || !value) {
|
||||
if (!tv || !val) {
|
||||
break
|
||||
}
|
||||
// 特殊时间格式不转换, 包含时或者包含时、分时(不包含秒), 直接比较字符串,因为new Date转换会有误差
|
||||
const isSpecialTimeFormat = (dateStyle?: string) =>
|
||||
dateStyle === 'H_m_s' || (dateStyle && dateStyle.length > 5 && dateStyle.length < 11)
|
||||
|
||||
let v: number | string
|
||||
if (isSpecialTimeFormat(field?.field?.dateStyle)) {
|
||||
v = value
|
||||
} else {
|
||||
v = new Date(value.replace(/-/g, '/') + ' GMT+8').getTime()
|
||||
tv = new Date(tv.replace(/-/g, '/') + ' GMT+8').getTime()
|
||||
}
|
||||
if (fc.term === 'eq') {
|
||||
if (v === tv) {
|
||||
color = fc[type]
|
||||
flag = true
|
||||
}
|
||||
} else if (fc.term === 'not_eq') {
|
||||
if (v !== tv) {
|
||||
color = fc[type]
|
||||
flag = true
|
||||
}
|
||||
} else if (fc.term === 'lt') {
|
||||
if (v < tv) {
|
||||
color = fc[type]
|
||||
flag = true
|
||||
}
|
||||
} else if (fc.term === 'gt') {
|
||||
if (v > tv) {
|
||||
color = fc[type]
|
||||
flag = true
|
||||
}
|
||||
} else if (fc.term === 'le') {
|
||||
if (v <= tv) {
|
||||
color = fc[type]
|
||||
flag = true
|
||||
}
|
||||
} else if (fc.term === 'ge') {
|
||||
if (v >= tv) {
|
||||
color = fc[type]
|
||||
flag = true
|
||||
}
|
||||
} else if (fc.term === 'default') {
|
||||
color = fc[type]
|
||||
flag = true
|
||||
// 特殊时间格式不转换, 包含时或者包含时、分时(不包含秒), 直接比较字符串,因为new Date转换会有误差
|
||||
const isSpecialTimeFormat = (dateStyle?: string) =>
|
||||
dateStyle === 'H_m_s' || (dateStyle && dateStyle.length > 5 && dateStyle.length < 11)
|
||||
|
||||
let v: number | string
|
||||
let compareTv = tv;
|
||||
if (isSpecialTimeFormat(sourceField?.dateStyle)) {
|
||||
v = val
|
||||
} else {
|
||||
v = new Date(val.replace(/-/g, '/') + ' GMT+8').getTime()
|
||||
compareTv = new Date(tv.toString().replace(/-/g, '/') + ' GMT+8').getTime()
|
||||
}
|
||||
if (fc.term === 'eq') {
|
||||
if (v === compareTv) {
|
||||
color = fc[type]
|
||||
flag = true
|
||||
}
|
||||
} else if (fc.term === 'not_eq') {
|
||||
if (v !== compareTv) {
|
||||
color = fc[type]
|
||||
flag = true
|
||||
}
|
||||
} else if (fc.term === 'lt') {
|
||||
if (v < compareTv) {
|
||||
color = fc[type]
|
||||
flag = true
|
||||
}
|
||||
} else if (fc.term === 'gt') {
|
||||
if (v > compareTv) {
|
||||
color = fc[type]
|
||||
flag = true
|
||||
}
|
||||
} else if (fc.term === 'le') {
|
||||
if (v <= compareTv) {
|
||||
color = fc[type]
|
||||
flag = true
|
||||
}
|
||||
} else if (fc.term === 'ge') {
|
||||
if (v >= compareTv) {
|
||||
color = fc[type]
|
||||
flag = true
|
||||
}
|
||||
} else if (fc.term === 'default') {
|
||||
color = fc[type]
|
||||
flag = true
|
||||
}
|
||||
}
|
||||
if (flag) {
|
||||
break
|
||||
} else if (i === field.conditions.length - 1) {
|
||||
color = defaultColor
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!color) {
|
||||
color = defaultColor;
|
||||
}
|
||||
return color
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user