Skip to content

Commit deaf4b0

Browse files
authored
Merge pull request #26 from byte-power/feature/dev/table_counter_visualization_optimize
table 新增固定列&列规则配置
2 parents 08046fb + 397047a commit deaf4b0

File tree

18 files changed

+516
-65
lines changed

18 files changed

+516
-65
lines changed

viz-lib/package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,7 @@
7272
"leaflet-fullscreen": "^1.0.2",
7373
"leaflet.markercluster": "^1.1.0",
7474
"lodash": "^4.17.10",
75-
"mathjs": "^10.5.0",
75+
"mathjs": "10.5.0",
7676
"moment": "^2.19.3",
7777
"numeral": "^2.0.6",
7878
"plotly.js": "^2.8.3",

viz-lib/src/components/visualizations/editor/control-label.less

+9
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,15 @@
44
margin-bottom: 0;
55
}
66
}
7+
label {
8+
.ant-typography {
9+
display: flex;
10+
width: 100%;
11+
justify-content: space-between;
12+
align-items: center;
13+
}
14+
display: flex;
15+
}
716
}
817

918
.visualization-editor-input {

viz-lib/src/lib/utils.js

+9
Original file line numberDiff line numberDiff line change
@@ -41,3 +41,12 @@ export function formatColumnValue(value, columnType = null) {
4141

4242
return value;
4343
}
44+
45+
export function newEval(str) {
46+
try {
47+
let fn = Function;
48+
return new fn('return ' + str)();
49+
} catch (error) {
50+
return false;
51+
}
52+
}

viz-lib/src/visualizations/table/Editor/ColumnEditor.jsx

+8
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,14 @@ export default function ColumnEditor({ column, onChange }) {
5454
/>
5555
</Section>
5656

57+
<Section>
58+
<Checkbox
59+
defaultChecked={column.isFixed}
60+
onChange={event => handleChange({ isFixed: event.target.checked })}>
61+
Is Fixed (default left)
62+
</Checkbox>
63+
</Section>
64+
5765
<Section>
5866
<Select
5967
label="Display as:"

viz-lib/src/visualizations/table/Renderer.jsx

+2-1
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,7 @@ SearchInput.propTypes = {
7272
};
7373

7474
SearchInput.defaultProps = {
75-
onChange: () => {},
75+
onChange: () => { },
7676
};
7777

7878
export default function Renderer({ options, data }) {
@@ -124,6 +124,7 @@ export default function Renderer({ options, data }) {
124124
hideOnSinglePage: true,
125125
showSizeChanger: false,
126126
}}
127+
scroll={{ x: true }}
127128
showSorterTooltip={false}
128129
/>
129130
</div>

viz-lib/src/visualizations/table/columns/index.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import initTextColumn from "./text";
2-
import initNumberColumn from "./number";
2+
import initNumberColumn from "./number/number";
33
import initDateTimeColumn from "./datetime";
44
import initBooleanColumn from "./boolean";
55
import initLinkColumn from "./link";

viz-lib/src/visualizations/table/columns/number.jsx

-56
This file was deleted.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
const CONDITIONS = {
2+
">": "\u003e",
3+
">=": "\u2265",
4+
"<": "\u003c",
5+
"<=": "\u2264",
6+
"==": "\u003d",
7+
"!=": "\u2260",
8+
};
9+
10+
const TYPEOPTIONS = [
11+
{
12+
key: 'fontSize',
13+
label: 'Font Size'
14+
},
15+
{
16+
key: 'fontStyle',
17+
label: 'Font Style'
18+
},
19+
{
20+
key: 'color',
21+
label: 'Color'
22+
},
23+
{
24+
key: 'background',
25+
label: 'Background'
26+
}
27+
]
28+
29+
export { CONDITIONS, TYPEOPTIONS }
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,219 @@
1+
import React from "react";
2+
import PropTypes from "prop-types";
3+
import { useDebouncedCallback } from "use-debounce";
4+
import { Section, Input, InputNumber, ContextHelp, Checkbox, ControlLabel } from "@/components/visualizations/editor";
5+
import { createNumberFormatter } from "@/lib/value-format";
6+
import { newEval } from "@/lib/utils";
7+
import PlusCircleOutlined from "@ant-design/icons/PlusCircleOutlined";
8+
import MinusCircleOutlined from '@ant-design/icons/MinusCircleOutlined';
9+
import Select from "antd/lib/select";
10+
import Divider from "antd/lib/divider";
11+
import { CONDITIONS, TYPEOPTIONS } from './constant'
12+
import controlType from "./rule-type";
13+
import { values, map, isNull } from 'lodash';
14+
15+
function Editor({ column, onChange }) {
16+
17+
function handleChange(index, filed, value) {
18+
const displayRules = column.displayRules;
19+
displayRules[index][filed] = value
20+
onChange({ displayRules });
21+
}
22+
const [handleChangeDebounced] = useDebouncedCallback(handleChange, 400);
23+
const [onChangeDebounced] = useDebouncedCallback(onChange, 200);
24+
25+
function addRuleItem() {
26+
column.displayRules.push({
27+
op: ">",
28+
opValue: 1.00,
29+
type: '',
30+
typeValue: null
31+
})
32+
return column.displayRules
33+
}
34+
35+
function deleteRuleItem(index) {
36+
column.displayRules.splice(index, 1)
37+
return column.displayRules
38+
}
39+
40+
return (
41+
<React.Fragment>
42+
<Section>
43+
<Input
44+
label={
45+
<React.Fragment>
46+
Number format
47+
<ContextHelp.NumberFormatSpecs />
48+
</React.Fragment>
49+
}
50+
data-test="Table.ColumnEditor.Number.Format"
51+
defaultValue={column.numberFormat}
52+
onChange={event => onChangeDebounced({ numberFormat: event.target.value })}
53+
/>
54+
</Section>
55+
56+
<Section>
57+
<Checkbox
58+
checked={column.displayRuleSwitch}
59+
onChange={event => onChange({ displayRuleSwitch: event.target.checked })}>
60+
Number dispaly rule
61+
</Checkbox>
62+
{column.displayRuleSwitch && <PlusCircleOutlined style={{ cursor: "pointer", float: 'right' }} onClick={() => onChange({ displayRules: addRuleItem() })} />}
63+
</Section>
64+
65+
<React.Fragment>
66+
{column.displayRuleSwitch && map(column.displayRules, (ruleItem, index) => {
67+
return <Section key={index}>
68+
<ControlLabel
69+
label={
70+
<React.Fragment>
71+
{`Condition${index + 1}`}
72+
<MinusCircleOutlined style={{ cursor: "pointer" }} onClick={() => onChange({ displayRules: deleteRuleItem(index) })} />
73+
</React.Fragment>
74+
}>
75+
<div className="image-dimension-selector">
76+
<Select
77+
value={ruleItem.op}
78+
onChange={op => handleChange(index, 'op', op)}
79+
optionLabelProp="label"
80+
dropdownMatchSelectWidth={false}
81+
style={{ width: 100 }}>
82+
<Select.Option value=">" label={CONDITIONS[">"]}>
83+
{CONDITIONS[">"]} greater than
84+
</Select.Option>
85+
<Select.Option value=">=" label={CONDITIONS[">="]}>
86+
{CONDITIONS[">="]} greater than or equals
87+
</Select.Option>
88+
<Select.Option disabled key="dv1">
89+
<Divider className="select-option-divider m-t-10 m-b-5" />
90+
</Select.Option>
91+
<Select.Option value="<" label={CONDITIONS["<"]}>
92+
{CONDITIONS["<"]} less than
93+
</Select.Option>
94+
<Select.Option value="<=" label={CONDITIONS["<="]}>
95+
{CONDITIONS["<="]} less than or equals
96+
</Select.Option>
97+
<Select.Option disabled key="dv2">
98+
<Divider className="select-option-divider m-t-10 m-b-5" />
99+
</Select.Option>
100+
<Select.Option value="==" label={CONDITIONS["=="]}>
101+
{CONDITIONS["=="]} equals
102+
</Select.Option>
103+
<Select.Option value="!=" label={CONDITIONS["!="]}>
104+
{CONDITIONS["!="]} not equal to
105+
</Select.Option>
106+
</Select>
107+
<span className="image-dimension-selector-spacer"></span>
108+
<InputNumber
109+
placeholder="Value"
110+
defaultValue={ruleItem.opValue}
111+
step="0.01"
112+
onChange={opValue => handleChangeDebounced(index, 'opValue', opValue)}
113+
/>
114+
</div>
115+
</ControlLabel>
116+
<ControlLabel
117+
label={
118+
<React.Fragment>
119+
{`Result${index + 1}`}
120+
</React.Fragment>
121+
}>
122+
<div className="image-dimension-selector">
123+
<Select
124+
value={ruleItem.type}
125+
onChange={type => handleChange(index, 'type', type)}
126+
optionLabelProp="label"
127+
dropdownMatchSelectWidth={false}
128+
style={{ width: 100 }}>
129+
{map(TYPEOPTIONS, (item, key) => (
130+
<Select.Option key={item.key} value={item.key}>
131+
{item.label}
132+
</Select.Option>
133+
))}
134+
</Select>
135+
<span className="image-dimension-selector-spacer"></span>
136+
{
137+
controlType({ type: ruleItem.type, field: 'typeValue', index: index, value: ruleItem.typeValue, onChange: handleChange })
138+
}
139+
</div>
140+
</ControlLabel>
141+
</Section>
142+
})}
143+
</React.Fragment>
144+
</React.Fragment>
145+
);
146+
}
147+
148+
Editor.propTypes = {
149+
column: PropTypes.shape({
150+
name: PropTypes.string.isRequired,
151+
numberFormat: PropTypes.string,
152+
displayRuleSwitch: PropTypes.bool,
153+
displayRules: PropTypes.array.isRequired
154+
}).isRequired,
155+
onChange: PropTypes.func.isRequired,
156+
};
157+
158+
export default function initNumberColumn(column) {
159+
160+
const format = createNumberFormatter(column.numberFormat);
161+
162+
function prepareData(row) {
163+
let styleProperty = displayRuleFormatter(column, row)
164+
return {
165+
dom: <div style={styleProperty}>{format(row[column.name])}</div>,
166+
text: format(row[column.name]),
167+
};
168+
}
169+
170+
function isValid(rule) {
171+
let forInValues = values(rule);
172+
if (forInValues.every(item => !isNull(item) && item !== '' && typeof (item) !== 'undefined')) {
173+
return true;
174+
} else {
175+
return false;
176+
}
177+
}
178+
179+
function displayRuleFormatter(column, row) {
180+
let property = new Object({})
181+
if (column.displayRuleSwitch && column.displayRules.length) {
182+
column.displayRules.forEach(item => {
183+
if (isValid(item)) {
184+
if (newEval(`${row[column.name]}${item.op}${item.opValue}`)) {
185+
switch (item.type) {
186+
case 'fontStyle':
187+
switch (item.typeValue) {
188+
case 'bold':
189+
property['fontWeight'] = item.typeValue;
190+
break;
191+
case 'italic':
192+
property['font-style'] = item.typeValue;
193+
break;
194+
}
195+
break;
196+
default:
197+
property[item.type] = item.typeValue;
198+
break;
199+
}
200+
}
201+
}
202+
})
203+
}
204+
return property;
205+
}
206+
207+
function NumberColumn({ row }) {
208+
// eslint-disable-line react/prop-types
209+
const { dom } = prepareData(row);
210+
return dom;
211+
}
212+
213+
NumberColumn.prepareData = prepareData;
214+
215+
return NumberColumn;
216+
}
217+
218+
initNumberColumn.friendlyName = "Number";
219+
initNumberColumn.Editor = Editor;

0 commit comments

Comments
 (0)