优化参数表样式与科学计数法支持
This commit is contained in:
1 parent
046aa49814
commit
32f21f08ff
6 files changed
+547
-64
No files matched your search
+145
-26
@@ -100,6 +100,7 @@ import {
|
|||||||
import {
|
import {
|
||||||
evaluateParameterExpression,
|
evaluateParameterExpression,
|
||||||
isParameterExpressionValue,
|
isParameterExpressionValue,
|
||||||
|
isScientificNotationValue,
|
||||||
} from "./parameterExpression";
|
} from "./parameterExpression";
|
||||||
import {
|
import {
|
||||||
WorkspaceViewTabs,
|
WorkspaceViewTabs,
|
||||||
@@ -108,6 +109,10 @@ import {
|
|||||||
|
|
||||||
type ParameterValue = number | string;
|
type ParameterValue = number | string;
|
||||||
type UnitQuantity = string;
|
type UnitQuantity = string;
|
||||||
|
type ParameterScientificNotation = {
|
||||||
|
text: string;
|
||||||
|
unit: string;
|
||||||
|
};
|
||||||
type PortKind = "physical" | "signal";
|
type PortKind = "physical" | "signal";
|
||||||
type PortNominalRole = "inlet" | "outlet" | "bidirectional" | "input" | "output";
|
type PortNominalRole = "inlet" | "outlet" | "bidirectional" | "input" | "output";
|
||||||
type PortSide = "left" | "right";
|
type PortSide = "left" | "right";
|
||||||
@@ -255,6 +260,7 @@ type SimulationNodeData = {
|
|||||||
ports: PortDefinition[];
|
ports: PortDefinition[];
|
||||||
parameters: Record<string, ParameterValue>;
|
parameters: Record<string, ParameterValue>;
|
||||||
parameterUnits: Record<string, string>;
|
parameterUnits: Record<string, string>;
|
||||||
|
parameterScientificNotation: Record<string, ParameterScientificNotation>;
|
||||||
rotation: NodeRotation;
|
rotation: NodeRotation;
|
||||||
mirrored: boolean;
|
mirrored: boolean;
|
||||||
portConnectionStates?: Record<string, PortConnectionState>;
|
portConnectionStates?: Record<string, PortConnectionState>;
|
||||||
@@ -288,8 +294,12 @@ type ProjectNodePayload = {
|
|||||||
x: number;
|
x: number;
|
||||||
y: number;
|
y: number;
|
||||||
};
|
};
|
||||||
data: Omit<SimulationNodeData, "parameterUnits"> & {
|
data: Omit<
|
||||||
|
SimulationNodeData,
|
||||||
|
"parameterUnits" | "parameterScientificNotation"
|
||||||
|
> & {
|
||||||
parameterUnits?: Record<string, string>;
|
parameterUnits?: Record<string, string>;
|
||||||
|
parameterScientificNotation?: Record<string, ParameterScientificNotation>;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -1199,6 +1209,7 @@ function buildNodeData(definition: ComponentDefinition): SimulationNodeData {
|
|||||||
ports: definition.ports.map((port) => ({ ...port })),
|
ports: definition.ports.map((port) => ({ ...port })),
|
||||||
parameters: defaultParameters(definition),
|
parameters: defaultParameters(definition),
|
||||||
parameterUnits: defaultParameterUnits(definition),
|
parameterUnits: defaultParameterUnits(definition),
|
||||||
|
parameterScientificNotation: {},
|
||||||
rotation: 0,
|
rotation: 0,
|
||||||
mirrored: false,
|
mirrored: false,
|
||||||
};
|
};
|
||||||
@@ -2709,6 +2720,7 @@ function DeferredNumberInput({
|
|||||||
<input
|
<input
|
||||||
aria-invalid={invalid}
|
aria-invalid={invalid}
|
||||||
aria-label={ariaLabel}
|
aria-label={ariaLabel}
|
||||||
|
className="parameter-number-input"
|
||||||
inputMode="text"
|
inputMode="text"
|
||||||
onBlur={() => {
|
onBlur={() => {
|
||||||
editingRef.current = false;
|
editingRef.current = false;
|
||||||
@@ -3971,34 +3983,62 @@ function FlowWorkbench() {
|
|||||||
const unit = findUnitOption(definition, selectedUnit);
|
const unit = findUnitOption(definition, selectedUnit);
|
||||||
const normalizedDisplayValue = displayValue.trim();
|
const normalizedDisplayValue = displayValue.trim();
|
||||||
const numericValue = Number(normalizedDisplayValue);
|
const numericValue = Number(normalizedDisplayValue);
|
||||||
const baseValue =
|
const hasFiniteNumericValue =
|
||||||
normalizedDisplayValue === "" || !Number.isFinite(numericValue)
|
normalizedDisplayValue !== "" && Number.isFinite(numericValue);
|
||||||
? normalizedDisplayValue
|
const convertedBaseValue = hasFiniteNumericValue
|
||||||
: unit.toBase(numericValue);
|
? unit.toBase(numericValue)
|
||||||
|
: numericValue;
|
||||||
|
const baseValue = hasFiniteNumericValue
|
||||||
|
? convertedBaseValue
|
||||||
|
: normalizedDisplayValue;
|
||||||
|
const scientificNotation =
|
||||||
|
hasFiniteNumericValue &&
|
||||||
|
Number.isFinite(convertedBaseValue) &&
|
||||||
|
isScientificNotationValue(normalizedDisplayValue)
|
||||||
|
? { text: normalizedDisplayValue, unit: selectedUnit }
|
||||||
|
: null;
|
||||||
|
|
||||||
updateSelectedNode((node) => ({
|
updateSelectedNode((node) => {
|
||||||
...node,
|
const parameterScientificNotation = {
|
||||||
data: {
|
...node.data.parameterScientificNotation,
|
||||||
...node.data,
|
};
|
||||||
parameters: {
|
if (scientificNotation) {
|
||||||
...node.data.parameters,
|
parameterScientificNotation[key] = scientificNotation;
|
||||||
[key]: baseValue,
|
} else {
|
||||||
|
delete parameterScientificNotation[key];
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
...node,
|
||||||
|
data: {
|
||||||
|
...node.data,
|
||||||
|
parameters: {
|
||||||
|
...node.data.parameters,
|
||||||
|
[key]: baseValue,
|
||||||
|
},
|
||||||
|
parameterScientificNotation,
|
||||||
},
|
},
|
||||||
},
|
};
|
||||||
}));
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
const updateSelectedChoice = (key: string, value: number) => {
|
const updateSelectedChoice = (key: string, value: number) => {
|
||||||
updateSelectedNode((node) => ({
|
updateSelectedNode((node) => {
|
||||||
...node,
|
const parameterScientificNotation = {
|
||||||
data: {
|
...node.data.parameterScientificNotation,
|
||||||
...node.data,
|
};
|
||||||
parameters: {
|
delete parameterScientificNotation[key];
|
||||||
...node.data.parameters,
|
return {
|
||||||
[key]: value,
|
...node,
|
||||||
|
data: {
|
||||||
|
...node.data,
|
||||||
|
parameters: {
|
||||||
|
...node.data.parameters,
|
||||||
|
[key]: value,
|
||||||
|
},
|
||||||
|
parameterScientificNotation,
|
||||||
},
|
},
|
||||||
},
|
};
|
||||||
}));
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
const updateSelectedParameterUnit = (key: string, unit: string) => {
|
const updateSelectedParameterUnit = (key: string, unit: string) => {
|
||||||
@@ -4181,7 +4221,9 @@ function FlowWorkbench() {
|
|||||||
key === "method"
|
key === "method"
|
||||||
? value
|
? value
|
||||||
: normalizedValue !== "" && Number.isFinite(numericValue)
|
: normalizedValue !== "" && Number.isFinite(numericValue)
|
||||||
? numericValue
|
? isScientificNotationValue(normalizedValue)
|
||||||
|
? normalizedValue
|
||||||
|
: numericValue
|
||||||
: normalizedValue,
|
: normalizedValue,
|
||||||
}));
|
}));
|
||||||
};
|
};
|
||||||
@@ -4961,6 +5003,7 @@ function FlowWorkbench() {
|
|||||||
baseValue,
|
baseValue,
|
||||||
definition,
|
definition,
|
||||||
selectedUnit,
|
selectedUnit,
|
||||||
|
selectedNode.data.parameterScientificNotation[key],
|
||||||
);
|
);
|
||||||
const mediumDefinitionIndex =
|
const mediumDefinitionIndex =
|
||||||
isAmesimGasMediumDefinition(selectedDefinition) && key === "gi";
|
isAmesimGasMediumDefinition(selectedDefinition) && key === "gi";
|
||||||
@@ -5249,7 +5292,7 @@ function FlowWorkbench() {
|
|||||||
];
|
];
|
||||||
const parameterTableAvailableWidth = Math.max(
|
const parameterTableAvailableWidth = Math.max(
|
||||||
0,
|
0,
|
||||||
modelingPaneLayout.propertiesWidth - 24,
|
modelingPaneLayout.propertiesWidth,
|
||||||
);
|
);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -5900,6 +5943,9 @@ function buildProjectPayload(
|
|||||||
ports: node.data.ports.map((port) => ({ ...port })),
|
ports: node.data.ports.map((port) => ({ ...port })),
|
||||||
parameters: { ...node.data.parameters },
|
parameters: { ...node.data.parameters },
|
||||||
parameterUnits: { ...node.data.parameterUnits },
|
parameterUnits: { ...node.data.parameterUnits },
|
||||||
|
parameterScientificNotation: {
|
||||||
|
...node.data.parameterScientificNotation,
|
||||||
|
},
|
||||||
rotation: normalizeNodeRotation(node.data.rotation),
|
rotation: normalizeNodeRotation(node.data.rotation),
|
||||||
mirrored: Boolean(node.data.mirrored),
|
mirrored: Boolean(node.data.mirrored),
|
||||||
},
|
},
|
||||||
@@ -5915,6 +5961,33 @@ function buildProjectPayload(
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function normalizeParameterScientificNotation(
|
||||||
|
value: unknown,
|
||||||
|
): Record<string, ParameterScientificNotation> {
|
||||||
|
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
const normalized: Record<string, ParameterScientificNotation> = {};
|
||||||
|
Object.entries(value as Record<string, unknown>).forEach(([key, entry]) => {
|
||||||
|
if (!entry || typeof entry !== "object" || Array.isArray(entry)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const candidate = entry as Record<string, unknown>;
|
||||||
|
if (
|
||||||
|
typeof candidate.text !== "string" ||
|
||||||
|
typeof candidate.unit !== "string" ||
|
||||||
|
!isScientificNotationValue(candidate.text)
|
||||||
|
) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
normalized[key] = {
|
||||||
|
text: candidate.text.trim(),
|
||||||
|
unit: candidate.unit,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
return normalized;
|
||||||
|
}
|
||||||
|
|
||||||
function normalizeLoadedNode(
|
function normalizeLoadedNode(
|
||||||
node: ProjectNodePayload,
|
node: ProjectNodePayload,
|
||||||
componentDefinitions: ComponentDefinition[],
|
componentDefinitions: ComponentDefinition[],
|
||||||
@@ -5935,6 +6008,9 @@ function normalizeLoadedNode(
|
|||||||
const parameterUnits = definition
|
const parameterUnits = definition
|
||||||
? { ...defaultParameterUnits(definition), ...(node.data.parameterUnits ?? {}) }
|
? { ...defaultParameterUnits(definition), ...(node.data.parameterUnits ?? {}) }
|
||||||
: { ...(node.data.parameterUnits ?? {}) };
|
: { ...(node.data.parameterUnits ?? {}) };
|
||||||
|
const parameterScientificNotation = normalizeParameterScientificNotation(
|
||||||
|
node.data.parameterScientificNotation,
|
||||||
|
);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
id: node.id,
|
id: node.id,
|
||||||
@@ -5953,6 +6029,7 @@ function normalizeLoadedNode(
|
|||||||
ports: normalizeLoadedPorts(node.data.ports, definition),
|
ports: normalizeLoadedPorts(node.data.ports, definition),
|
||||||
parameters,
|
parameters,
|
||||||
parameterUnits,
|
parameterUnits,
|
||||||
|
parameterScientificNotation,
|
||||||
rotation: normalizeNodeRotation(node.data.rotation),
|
rotation: normalizeNodeRotation(node.data.rotation),
|
||||||
mirrored: Boolean(node.data.mirrored),
|
mirrored: Boolean(node.data.mirrored),
|
||||||
},
|
},
|
||||||
@@ -6190,6 +6267,7 @@ function displayParameterValue(
|
|||||||
baseValue: ParameterValue,
|
baseValue: ParameterValue,
|
||||||
definition: ParameterDefinition,
|
definition: ParameterDefinition,
|
||||||
selectedUnit: string,
|
selectedUnit: string,
|
||||||
|
scientificNotation?: ParameterScientificNotation,
|
||||||
) {
|
) {
|
||||||
if (typeof baseValue === "string" && baseValue.trim() === "") {
|
if (typeof baseValue === "string" && baseValue.trim() === "") {
|
||||||
return "";
|
return "";
|
||||||
@@ -6198,7 +6276,16 @@ function displayParameterValue(
|
|||||||
if (!Number.isFinite(numericValue)) {
|
if (!Number.isFinite(numericValue)) {
|
||||||
return String(baseValue);
|
return String(baseValue);
|
||||||
}
|
}
|
||||||
return formatEditableNumber(findUnitOption(definition, selectedUnit).fromBase(numericValue));
|
const displayValue = findUnitOption(definition, selectedUnit).fromBase(numericValue);
|
||||||
|
if (
|
||||||
|
scientificNotation &&
|
||||||
|
isScientificNotationValue(scientificNotation.text)
|
||||||
|
) {
|
||||||
|
return scientificNotation.unit === selectedUnit
|
||||||
|
? scientificNotation.text.trim()
|
||||||
|
: formatScientificEditableNumber(displayValue, scientificNotation.text);
|
||||||
|
}
|
||||||
|
return formatEditableNumber(displayValue);
|
||||||
}
|
}
|
||||||
|
|
||||||
function formatEditableNumber(value: number) {
|
function formatEditableNumber(value: number) {
|
||||||
@@ -6208,6 +6295,38 @@ function formatEditableNumber(value: number) {
|
|||||||
return Number(value.toPrecision(12)).toString();
|
return Number(value.toPrecision(12)).toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const SCIENTIFIC_NOTATION_STYLE_PATTERN =
|
||||||
|
/^[+-]?(?:\d+(?:\.(\d*))?|\.(\d+))([eE])([+-]?)(\d+)$/;
|
||||||
|
|
||||||
|
function formatScientificEditableNumber(value: number, notationHint: string) {
|
||||||
|
if (!Number.isFinite(value)) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
const style = SCIENTIFIC_NOTATION_STYLE_PATTERN.exec(notationHint.trim());
|
||||||
|
if (!style) {
|
||||||
|
return Number(value.toPrecision(12)).toExponential();
|
||||||
|
}
|
||||||
|
|
||||||
|
const minimumFractionDigits = (style[1] ?? style[2] ?? "").length;
|
||||||
|
const exponentMarker = style[3];
|
||||||
|
const showPositiveExponentSign = style[4] === "+";
|
||||||
|
const roundedDisplayValue = Number(value.toPrecision(15));
|
||||||
|
const [rawMantissa, rawExponent] = roundedDisplayValue.toExponential().split("e");
|
||||||
|
const [wholePart, rawFraction = ""] = rawMantissa.split(".");
|
||||||
|
let fraction = rawFraction;
|
||||||
|
while (fraction.endsWith("0") && fraction.length > minimumFractionDigits) {
|
||||||
|
fraction = fraction.slice(0, -1);
|
||||||
|
}
|
||||||
|
if (fraction.length < minimumFractionDigits) {
|
||||||
|
fraction = fraction.padEnd(minimumFractionDigits, "0");
|
||||||
|
}
|
||||||
|
|
||||||
|
const exponent = Number(rawExponent);
|
||||||
|
const exponentSign = exponent < 0 ? "-" : showPositiveExponentSign ? "+" : "";
|
||||||
|
const exponentDigits = String(Math.abs(exponent));
|
||||||
|
return `${wholePart}${fraction ? `.${fraction}` : ""}${exponentMarker}${exponentSign}${exponentDigits}`;
|
||||||
|
}
|
||||||
|
|
||||||
function parameterMetadataValue(
|
function parameterMetadataValue(
|
||||||
value: ParameterValue,
|
value: ParameterValue,
|
||||||
definition: ParameterDefinition,
|
definition: ParameterDefinition,
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import {
|
|||||||
Fragment,
|
Fragment,
|
||||||
useCallback,
|
useCallback,
|
||||||
useEffect,
|
useEffect,
|
||||||
|
useLayoutEffect,
|
||||||
useRef,
|
useRef,
|
||||||
useState,
|
useState,
|
||||||
} from "react";
|
} from "react";
|
||||||
@@ -173,7 +174,15 @@ export function ParameterTable({
|
|||||||
availableWidth,
|
availableWidth,
|
||||||
onColumnLayoutChange,
|
onColumnLayoutChange,
|
||||||
}: ParameterTableProps) {
|
}: ParameterTableProps) {
|
||||||
const effectiveLayout = effectiveColumnLayout(columnLayout, availableWidth);
|
const tableWrapRef = useRef<HTMLDivElement>(null);
|
||||||
|
const [measuredAvailableWidth, setMeasuredAvailableWidth] = useState<
|
||||||
|
number | null
|
||||||
|
>(null);
|
||||||
|
const resolvedAvailableWidth = measuredAvailableWidth ?? availableWidth;
|
||||||
|
const effectiveLayout = effectiveColumnLayout(
|
||||||
|
columnLayout,
|
||||||
|
resolvedAvailableWidth,
|
||||||
|
);
|
||||||
const columnDragRef = useRef<ColumnDragState | null>(null);
|
const columnDragRef = useRef<ColumnDragState | null>(null);
|
||||||
const helpPreviewTimerRef = useRef<number | null>(null);
|
const helpPreviewTimerRef = useRef<number | null>(null);
|
||||||
const helpPreviewAnchorRef = useRef<HelpPreviewAnchor | null>(null);
|
const helpPreviewAnchorRef = useRef<HelpPreviewAnchor | null>(null);
|
||||||
@@ -181,6 +190,26 @@ export function ParameterTable({
|
|||||||
useState<ColumnName | null>(null);
|
useState<ColumnName | null>(null);
|
||||||
const [helpPreview, setHelpPreview] = useState<HelpPreviewState | null>(null);
|
const [helpPreview, setHelpPreview] = useState<HelpPreviewState | null>(null);
|
||||||
|
|
||||||
|
useLayoutEffect(() => {
|
||||||
|
const wrap = tableWrapRef.current;
|
||||||
|
if (!wrap) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const measureAvailableWidth = () => {
|
||||||
|
const nextWidth = wrap.clientWidth;
|
||||||
|
if (nextWidth <= 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
setMeasuredAvailableWidth((currentWidth) =>
|
||||||
|
currentWidth === nextWidth ? currentWidth : nextWidth,
|
||||||
|
);
|
||||||
|
};
|
||||||
|
measureAvailableWidth();
|
||||||
|
const observer = new ResizeObserver(measureAvailableWidth);
|
||||||
|
observer.observe(wrap);
|
||||||
|
return () => observer.disconnect();
|
||||||
|
}, []);
|
||||||
|
|
||||||
const dismissHelpPreview = useCallback(() => {
|
const dismissHelpPreview = useCallback(() => {
|
||||||
if (helpPreviewTimerRef.current !== null) {
|
if (helpPreviewTimerRef.current !== null) {
|
||||||
window.clearTimeout(helpPreviewTimerRef.current);
|
window.clearTimeout(helpPreviewTimerRef.current);
|
||||||
@@ -258,7 +287,9 @@ export function ParameterTable({
|
|||||||
column === "name"
|
column === "name"
|
||||||
? { ...startLayout, nameWidth: startLayout.nameWidth + boundaryDelta }
|
? { ...startLayout, nameWidth: startLayout.nameWidth + boundaryDelta }
|
||||||
: { ...startLayout, unitWidth: startLayout.unitWidth - boundaryDelta };
|
: { ...startLayout, unitWidth: startLayout.unitWidth - boundaryDelta };
|
||||||
onColumnLayoutChange(effectiveColumnLayout(proposed, availableWidth));
|
onColumnLayoutChange(
|
||||||
|
effectiveColumnLayout(proposed, resolvedAvailableWidth),
|
||||||
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
const startColumnResize = (
|
const startColumnResize = (
|
||||||
@@ -311,11 +342,17 @@ export function ParameterTable({
|
|||||||
|
|
||||||
const nameColumnMaximum = Math.max(
|
const nameColumnMaximum = Math.max(
|
||||||
MIN_NAME_WIDTH,
|
MIN_NAME_WIDTH,
|
||||||
Math.min(MAX_NAME_WIDTH, availableWidth - MIN_VALUE_WIDTH - effectiveLayout.unitWidth),
|
Math.min(
|
||||||
|
MAX_NAME_WIDTH,
|
||||||
|
resolvedAvailableWidth - MIN_VALUE_WIDTH - effectiveLayout.unitWidth,
|
||||||
|
),
|
||||||
);
|
);
|
||||||
const unitColumnMaximum = Math.max(
|
const unitColumnMaximum = Math.max(
|
||||||
MIN_UNIT_WIDTH,
|
MIN_UNIT_WIDTH,
|
||||||
Math.min(MAX_UNIT_WIDTH, availableWidth - MIN_VALUE_WIDTH - effectiveLayout.nameWidth),
|
Math.min(
|
||||||
|
MAX_UNIT_WIDTH,
|
||||||
|
resolvedAvailableWidth - MIN_VALUE_WIDTH - effectiveLayout.nameWidth,
|
||||||
|
),
|
||||||
);
|
);
|
||||||
const tableStyle = {
|
const tableStyle = {
|
||||||
"--parameter-name-width": `${effectiveLayout.nameWidth}px`,
|
"--parameter-name-width": `${effectiveLayout.nameWidth}px`,
|
||||||
@@ -369,6 +406,7 @@ export function ParameterTable({
|
|||||||
<div
|
<div
|
||||||
className={`parameter-table-wrap ${activeColumnResize ? `resizing-${activeColumnResize}` : ""}`}
|
className={`parameter-table-wrap ${activeColumnResize ? `resizing-${activeColumnResize}` : ""}`}
|
||||||
data-resizing-column={activeColumnResize ?? undefined}
|
data-resizing-column={activeColumnResize ?? undefined}
|
||||||
|
ref={tableWrapRef}
|
||||||
>
|
>
|
||||||
<table aria-label={ariaLabel} className="parameter-table" style={tableStyle}>
|
<table aria-label={ariaLabel} className="parameter-table" style={tableStyle}>
|
||||||
<colgroup>
|
<colgroup>
|
||||||
|
|||||||
@@ -13,6 +13,14 @@ const NUMERIC_LITERAL_PATTERN =
|
|||||||
const UNSIGNED_NUMBER_PREFIX =
|
const UNSIGNED_NUMBER_PREFIX =
|
||||||
/^(?:\d+(?:\.\d*)?|\.\d+)(?:[eE][+-]?\d+)?/;
|
/^(?:\d+(?:\.\d*)?|\.\d+)(?:[eE][+-]?\d+)?/;
|
||||||
|
|
||||||
|
export function isScientificNotationValue(value: number | string): value is string {
|
||||||
|
if (typeof value !== "string") {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
const trimmed = value.trim();
|
||||||
|
return NUMERIC_LITERAL_PATTERN.test(trimmed) && /[eE]/.test(trimmed);
|
||||||
|
}
|
||||||
|
|
||||||
type TokenKind =
|
type TokenKind =
|
||||||
| "number"
|
| "number"
|
||||||
| "identifier"
|
| "identifier"
|
||||||
|
|||||||
+92
-34
@@ -447,6 +447,17 @@ textarea {
|
|||||||
font-weight: 700;
|
font-weight: 700;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.properties .panel-title {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
min-height: 32px;
|
||||||
|
padding: 0 10px;
|
||||||
|
border-bottom-color: #c7cdd4;
|
||||||
|
color: #293642;
|
||||||
|
background: #f2f4f6;
|
||||||
|
font-size: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
.palette-list {
|
.palette-list {
|
||||||
display: block;
|
display: block;
|
||||||
padding: 12px;
|
padding: 12px;
|
||||||
@@ -461,13 +472,13 @@ textarea {
|
|||||||
.parameter-table-wrap {
|
.parameter-table-wrap {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
min-width: 0;
|
min-width: 0;
|
||||||
padding: 0 12px 12px;
|
padding: 0;
|
||||||
overflow-x: hidden;
|
overflow-x: clip;
|
||||||
}
|
}
|
||||||
|
|
||||||
.parameter-table-wrap.parameter-form {
|
.parameter-table-wrap.parameter-form {
|
||||||
display: block;
|
display: block;
|
||||||
padding: 0 12px 12px;
|
padding: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.parameter-table {
|
.parameter-table {
|
||||||
@@ -475,27 +486,30 @@ textarea {
|
|||||||
--parameter-unit-width: 72px;
|
--parameter-unit-width: 72px;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
min-width: 0;
|
min-width: 0;
|
||||||
border: 1px solid #cbd5df;
|
border: 0;
|
||||||
border-spacing: 0;
|
border-spacing: 0;
|
||||||
border-radius: 4px;
|
border-radius: 0;
|
||||||
color: #263442;
|
color: #263442;
|
||||||
background: #ffffff;
|
background: #ffffff;
|
||||||
table-layout: fixed;
|
table-layout: fixed;
|
||||||
font-size: 11px;
|
font-size: 12px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.parameter-table-head .parameter-table-cell {
|
.parameter-table-head .parameter-table-cell {
|
||||||
position: relative;
|
position: sticky;
|
||||||
z-index: 1;
|
z-index: 1;
|
||||||
height: 30px;
|
top: 0;
|
||||||
padding: 0 7px;
|
height: 28px;
|
||||||
|
padding: 0 8px;
|
||||||
overflow: visible;
|
overflow: visible;
|
||||||
border-bottom-color: #bfcbd7;
|
border-right-color: #c4cad1;
|
||||||
color: #475569;
|
border-bottom-color: #b9c1c9;
|
||||||
background: #edf1f5;
|
color: #35414c;
|
||||||
font-size: 10px;
|
background: #e3e6e9;
|
||||||
|
box-shadow: 0 1px 0 rgba(51, 65, 85, 0.06);
|
||||||
|
font-size: 11px;
|
||||||
font-weight: 700;
|
font-weight: 700;
|
||||||
line-height: 30px;
|
line-height: 27px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.parameter-table-head .parameter-table-name-cell {
|
.parameter-table-head .parameter-table-name-cell {
|
||||||
@@ -507,16 +521,21 @@ textarea {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.parameter-table-row {
|
.parameter-table-row {
|
||||||
height: 36px;
|
height: 32px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.parameter-table-head-row {
|
||||||
|
height: 28px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.parameter-table-cell {
|
.parameter-table-cell {
|
||||||
min-width: 0;
|
min-width: 0;
|
||||||
height: 36px;
|
height: 32px;
|
||||||
padding: 2px 6px;
|
box-sizing: border-box;
|
||||||
|
padding: 1px 7px;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
border-right: 1px solid #d9e0e7;
|
border-right: 1px solid #d5d9de;
|
||||||
border-bottom: 1px solid #d9e0e7;
|
border-bottom: 1px solid #e1e4e7;
|
||||||
vertical-align: middle;
|
vertical-align: middle;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -524,8 +543,22 @@ textarea {
|
|||||||
border-right: 0;
|
border-right: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.parameter-table
|
||||||
|
> tbody
|
||||||
|
> .parameter-table-row:nth-child(even)
|
||||||
|
> .parameter-table-cell {
|
||||||
|
background: #f3f4f5;
|
||||||
|
}
|
||||||
|
|
||||||
.parameter-table > tbody > .parameter-table-row:hover > .parameter-table-cell {
|
.parameter-table > tbody > .parameter-table-row:hover > .parameter-table-cell {
|
||||||
background: #f8fbfe;
|
background: #eaf3f8;
|
||||||
|
}
|
||||||
|
|
||||||
|
.parameter-table
|
||||||
|
> tbody
|
||||||
|
> .parameter-table-row:focus-within
|
||||||
|
> .parameter-table-cell {
|
||||||
|
background: #deedf5;
|
||||||
}
|
}
|
||||||
|
|
||||||
.parameter-table-row:focus-visible {
|
.parameter-table-row:focus-visible {
|
||||||
@@ -537,16 +570,21 @@ textarea {
|
|||||||
.parameter-table-name-cell {
|
.parameter-table-name-cell {
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
color: #344256;
|
color: #344256;
|
||||||
|
text-align: left;
|
||||||
text-overflow: ellipsis;
|
text-overflow: ellipsis;
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.parameter-table > tbody .parameter-table-name-cell {
|
||||||
|
font-weight: 400;
|
||||||
|
}
|
||||||
|
|
||||||
.parameter-value-cell,
|
.parameter-value-cell,
|
||||||
.parameter-table-value-cell,
|
.parameter-table-value-cell,
|
||||||
.parameter-unit-cell,
|
.parameter-unit-cell,
|
||||||
.parameter-table-unit-cell {
|
.parameter-table-unit-cell {
|
||||||
min-width: 0;
|
min-width: 0;
|
||||||
padding: 2px;
|
padding: 1px 3px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.parameter-unit-cell,
|
.parameter-unit-cell,
|
||||||
@@ -561,30 +599,44 @@ textarea {
|
|||||||
width: 100%;
|
width: 100%;
|
||||||
min-width: 0;
|
min-width: 0;
|
||||||
max-width: 100%;
|
max-width: 100%;
|
||||||
height: 30px;
|
height: 28px;
|
||||||
|
box-sizing: border-box;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
padding: 0 6px;
|
padding: 0 6px;
|
||||||
border: 1px solid #d5dde6;
|
border: 1px solid transparent;
|
||||||
border-radius: 2px;
|
border-radius: 1px;
|
||||||
outline: 0;
|
outline: 0;
|
||||||
color: #1f2933;
|
color: #1f2933;
|
||||||
background: #ffffff;
|
background: transparent;
|
||||||
font-size: 11px;
|
font-size: 12px;
|
||||||
|
font-variant-numeric: tabular-nums;
|
||||||
}
|
}
|
||||||
|
|
||||||
.parameter-table-cell select {
|
.parameter-table-cell select {
|
||||||
padding-right: 4px;
|
padding-right: 4px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.parameter-table > tbody .parameter-table-value-cell,
|
||||||
|
.parameter-table > tbody .parameter-table-value-cell input,
|
||||||
|
.parameter-table > tbody .parameter-table-value-cell select {
|
||||||
|
text-align: right;
|
||||||
|
}
|
||||||
|
|
||||||
|
.parameter-table > tbody .parameter-table-value-cell select {
|
||||||
|
text-align-last: right;
|
||||||
|
}
|
||||||
|
|
||||||
.parameter-table-cell input:hover:not(:disabled),
|
.parameter-table-cell input:hover:not(:disabled),
|
||||||
.parameter-table-cell select:hover:not(:disabled) {
|
.parameter-table-cell select:hover:not(:disabled) {
|
||||||
border-color: #aebdcb;
|
border-color: #aebbc7;
|
||||||
|
background: rgba(255, 255, 255, 0.72);
|
||||||
}
|
}
|
||||||
|
|
||||||
.parameter-table-cell input:focus,
|
.parameter-table-cell input:focus,
|
||||||
.parameter-table-cell select:focus {
|
.parameter-table-cell select:focus {
|
||||||
border-color: #1d6fb8;
|
border-color: #1d6fb8;
|
||||||
outline: 0;
|
outline: 0;
|
||||||
|
background: #ffffff;
|
||||||
box-shadow: inset 0 0 0 1px #1d6fb8;
|
box-shadow: inset 0 0 0 1px #1d6fb8;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -597,24 +649,27 @@ textarea {
|
|||||||
.parameter-table-row.read-only .parameter-value-cell,
|
.parameter-table-row.read-only .parameter-value-cell,
|
||||||
.parameter-table-row.read-only .parameter-table-value-cell,
|
.parameter-table-row.read-only .parameter-table-value-cell,
|
||||||
.parameter-table-cell input:read-only {
|
.parameter-table-cell input:read-only {
|
||||||
background: #f4f6f8;
|
background: transparent;
|
||||||
}
|
}
|
||||||
|
|
||||||
.parameter-read-only-value,
|
.parameter-read-only-value,
|
||||||
.parameter-table-read-only-value {
|
.parameter-table-read-only-value {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
justify-content: flex-end;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
min-width: 0;
|
min-width: 0;
|
||||||
height: 30px;
|
height: 28px;
|
||||||
padding: 0 7px;
|
box-sizing: border-box;
|
||||||
|
padding: 0 6px;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
border: 1px solid #d5dde6;
|
border: 0;
|
||||||
border-radius: 2px;
|
border-radius: 0;
|
||||||
color: #526172;
|
color: #526172;
|
||||||
|
text-align: right;
|
||||||
text-overflow: ellipsis;
|
text-overflow: ellipsis;
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
background: #f4f6f8;
|
background: transparent;
|
||||||
font-variant-numeric: tabular-nums;
|
font-variant-numeric: tabular-nums;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -628,7 +683,10 @@ textarea {
|
|||||||
content: "—";
|
content: "—";
|
||||||
}
|
}
|
||||||
|
|
||||||
.parameter-table-row.invalid > .parameter-table-cell {
|
.parameter-table
|
||||||
|
> tbody
|
||||||
|
> .parameter-table-row.invalid
|
||||||
|
> .parameter-table-cell {
|
||||||
background: #fff8f7;
|
background: #fff8f7;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -146,6 +146,16 @@ export const componentCatalog = {
|
|||||||
minimumExclusive: true,
|
minimumExclusive: true,
|
||||||
description: "用于验证参数表单位换算和 XML 基础单位输出的参考压力。",
|
description: "用于验证参数表单位换算和 XML 基础单位输出的参考压力。",
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: "reference_temperature",
|
||||||
|
label: "参考温度",
|
||||||
|
quantity: "temperature",
|
||||||
|
unit: "K",
|
||||||
|
default: 300,
|
||||||
|
minimum: 0,
|
||||||
|
minimumExclusive: false,
|
||||||
|
description: "用于验证带偏移量单位的数值换算和显示精度。",
|
||||||
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -41,6 +41,7 @@ test("组件参数和仿真设置使用共享的三列表格", async ({ page })
|
|||||||
"节点 ID",
|
"节点 ID",
|
||||||
"介质物性索引",
|
"介质物性索引",
|
||||||
"参考压力",
|
"参考压力",
|
||||||
|
"参考温度",
|
||||||
]);
|
]);
|
||||||
await expect(simulationTable.getByRole("rowheader")).toHaveText([
|
await expect(simulationTable.getByRole("rowheader")).toHaveText([
|
||||||
"起始时间",
|
"起始时间",
|
||||||
@@ -64,6 +65,173 @@ test("组件参数和仿真设置使用共享的三列表格", async ({ page })
|
|||||||
.locator("thead .parameter-table-name-cell")
|
.locator("thead .parameter-table-name-cell")
|
||||||
.evaluate((element) => element.getBoundingClientRect().width);
|
.evaluate((element) => element.getBoundingClientRect().width);
|
||||||
expect(Math.abs(componentNameWidth - simulationNameWidth)).toBeLessThanOrEqual(1);
|
expect(Math.abs(componentNameWidth - simulationNameWidth)).toBeLessThanOrEqual(1);
|
||||||
|
|
||||||
|
const panelTitleHeights = await page
|
||||||
|
.locator(".properties .panel-title")
|
||||||
|
.evaluateAll((elements) =>
|
||||||
|
elements.map((element) => element.getBoundingClientRect().height),
|
||||||
|
);
|
||||||
|
expect(panelTitleHeights).toHaveLength(2);
|
||||||
|
panelTitleHeights.forEach((height) => expect(height).toBeLessThanOrEqual(32));
|
||||||
|
|
||||||
|
const compactTableMetrics = await page
|
||||||
|
.locator(".properties .parameter-table")
|
||||||
|
.evaluateAll((tables) =>
|
||||||
|
tables.map((table) => {
|
||||||
|
const content = table.closest<HTMLElement>(".properties-subpane-content");
|
||||||
|
const headerCell = table.querySelector<HTMLElement>("thead th");
|
||||||
|
const bodyRows = Array.from(
|
||||||
|
table.querySelectorAll<HTMLElement>("tbody .parameter-table-row"),
|
||||||
|
);
|
||||||
|
const control = table.querySelector<HTMLElement>("input, select");
|
||||||
|
if (!content || !headerCell || !control) {
|
||||||
|
throw new Error("参数表缺少布局测量所需的元素");
|
||||||
|
}
|
||||||
|
const tableBounds = table.getBoundingClientRect();
|
||||||
|
const contentBounds = content.getBoundingClientRect();
|
||||||
|
const tableStyle = getComputedStyle(table);
|
||||||
|
const contentLeft = contentBounds.left + content.clientLeft;
|
||||||
|
const contentRight = contentLeft + content.clientWidth;
|
||||||
|
const controlBounds = control.getBoundingClientRect();
|
||||||
|
const controlCell = control.closest<HTMLElement>("td");
|
||||||
|
if (!controlCell) {
|
||||||
|
throw new Error("参数控件不在数据单元格中");
|
||||||
|
}
|
||||||
|
const controlCellBounds = controlCell.getBoundingClientRect();
|
||||||
|
return {
|
||||||
|
leftInset: tableBounds.left - contentLeft,
|
||||||
|
rightInset: contentRight - tableBounds.right,
|
||||||
|
borderRadius: tableStyle.borderRadius,
|
||||||
|
borderTopWidth: tableStyle.borderTopWidth,
|
||||||
|
headerHeight: headerCell.getBoundingClientRect().height,
|
||||||
|
headerPosition: getComputedStyle(headerCell).position,
|
||||||
|
maximumRowHeight: Math.max(
|
||||||
|
...bodyRows.map((row) => row.getBoundingClientRect().height),
|
||||||
|
),
|
||||||
|
controlHeight: controlBounds.height,
|
||||||
|
controlContained:
|
||||||
|
controlBounds.left >= controlCellBounds.left - 0.5 &&
|
||||||
|
controlBounds.right <= controlCellBounds.right + 0.5 &&
|
||||||
|
controlBounds.top >= controlCellBounds.top - 0.5 &&
|
||||||
|
controlBounds.bottom <= controlCellBounds.bottom + 0.5,
|
||||||
|
};
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
expect(compactTableMetrics).toHaveLength(2);
|
||||||
|
compactTableMetrics.forEach((metrics) => {
|
||||||
|
expect(Math.abs(metrics.leftInset)).toBeLessThanOrEqual(1);
|
||||||
|
expect(Math.abs(metrics.rightInset)).toBeLessThanOrEqual(1);
|
||||||
|
expect(metrics.borderRadius).toBe("0px");
|
||||||
|
expect(metrics.borderTopWidth).toBe("0px");
|
||||||
|
expect(metrics.headerHeight).toBeLessThanOrEqual(28);
|
||||||
|
expect(metrics.headerPosition).toBe("sticky");
|
||||||
|
expect(metrics.maximumRowHeight).toBeLessThanOrEqual(32);
|
||||||
|
expect(metrics.controlHeight).toBeGreaterThanOrEqual(22);
|
||||||
|
expect(metrics.controlHeight).toBeLessThanOrEqual(28);
|
||||||
|
expect(metrics.controlContained).toBe(true);
|
||||||
|
});
|
||||||
|
await expect(
|
||||||
|
componentTable.getByRole("rowheader", {
|
||||||
|
name: "组件名称",
|
||||||
|
exact: true,
|
||||||
|
}),
|
||||||
|
).toHaveCSS("text-align", "left");
|
||||||
|
await expect(page.getByLabel("参考压力", { exact: true })).toHaveCSS(
|
||||||
|
"text-align",
|
||||||
|
"right",
|
||||||
|
);
|
||||||
|
const valueColumnAlignment = await page
|
||||||
|
.locator(".properties .parameter-table tbody .parameter-table-value-cell")
|
||||||
|
.evaluateAll((cells) =>
|
||||||
|
cells.every((cell) => {
|
||||||
|
const controls = Array.from(
|
||||||
|
cell.querySelectorAll<HTMLElement>("input, select"),
|
||||||
|
);
|
||||||
|
return (
|
||||||
|
getComputedStyle(cell).textAlign === "right" &&
|
||||||
|
controls.every(
|
||||||
|
(control) => getComputedStyle(control).textAlign === "right",
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
expect(valueColumnAlignment).toBe(true);
|
||||||
|
await expect(
|
||||||
|
componentTable.locator(".parameter-table-read-only-value").first(),
|
||||||
|
).toHaveCSS("justify-content", "flex-end");
|
||||||
|
await expect(page.getByLabel("介质物性索引", { exact: true })).toHaveCSS(
|
||||||
|
"text-align-last",
|
||||||
|
"right",
|
||||||
|
);
|
||||||
|
const solver = page.getByLabel("求解器", { exact: true });
|
||||||
|
await expect(solver).toHaveCSS("text-align", "right");
|
||||||
|
await expect(solver).toHaveCSS("text-align-last", "right");
|
||||||
|
await solver.selectOption("Radau");
|
||||||
|
await expect(solver).toHaveValue("Radau");
|
||||||
|
});
|
||||||
|
|
||||||
|
test("最窄参数面板滚动时表头固定且值列保持可用宽度", async ({ page }) => {
|
||||||
|
await page.goto("/");
|
||||||
|
await addPneumaticTestComponent(page);
|
||||||
|
|
||||||
|
const widthSeparator = page.getByRole("separator", {
|
||||||
|
name: "调整参数面板宽度",
|
||||||
|
exact: true,
|
||||||
|
});
|
||||||
|
await widthSeparator.focus();
|
||||||
|
await page.keyboard.press("ArrowRight");
|
||||||
|
await page.keyboard.press("ArrowRight");
|
||||||
|
await page.keyboard.press("ArrowRight");
|
||||||
|
await expect(widthSeparator).toHaveAttribute("aria-valuenow", "280");
|
||||||
|
|
||||||
|
const heightSeparator = page.getByRole("separator", {
|
||||||
|
name: "调整组件参数和仿真设置区域高度",
|
||||||
|
exact: true,
|
||||||
|
});
|
||||||
|
await heightSeparator.focus();
|
||||||
|
for (let step = 0; step < 11; step += 1) {
|
||||||
|
await page.keyboard.press("ArrowUp");
|
||||||
|
}
|
||||||
|
await expect(heightSeparator).toHaveAttribute("aria-valuenow", "120");
|
||||||
|
|
||||||
|
const componentContent = page
|
||||||
|
.getByRole("region", { name: "组件参数子栏", exact: true })
|
||||||
|
.locator(".properties-subpane-content");
|
||||||
|
await expect
|
||||||
|
.poll(() =>
|
||||||
|
componentContent.evaluate(
|
||||||
|
(element) => element.scrollHeight - element.clientHeight,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
.toBeGreaterThan(0);
|
||||||
|
await componentContent.evaluate((element) => {
|
||||||
|
element.scrollTop = 48;
|
||||||
|
});
|
||||||
|
await expect
|
||||||
|
.poll(() => componentContent.evaluate((element) => element.scrollTop))
|
||||||
|
.toBeGreaterThan(0);
|
||||||
|
|
||||||
|
const narrowLayout = await componentContent.evaluate((content) => {
|
||||||
|
const header = content.querySelector<HTMLElement>(
|
||||||
|
"thead .parameter-table-name-cell",
|
||||||
|
);
|
||||||
|
const valueHeader = content.querySelector<HTMLElement>(
|
||||||
|
"thead .parameter-table-value-cell",
|
||||||
|
);
|
||||||
|
const wrap = content.querySelector<HTMLElement>(".parameter-table-wrap");
|
||||||
|
if (!header || !valueHeader || !wrap) {
|
||||||
|
throw new Error("无法测量最窄参数表布局");
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
stickyTopDelta:
|
||||||
|
header.getBoundingClientRect().top - content.getBoundingClientRect().top,
|
||||||
|
valueWidth: valueHeader.getBoundingClientRect().width,
|
||||||
|
horizontalOverflow: wrap.scrollWidth - wrap.clientWidth,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
expect(Math.abs(narrowLayout.stickyTopDelta)).toBeLessThanOrEqual(1);
|
||||||
|
expect(narrowLayout.valueWidth).toBeGreaterThanOrEqual(95);
|
||||||
|
expect(narrowLayout.horizontalOverflow).toBeLessThanOrEqual(1);
|
||||||
});
|
});
|
||||||
|
|
||||||
test("参数说明延迟显示,单位切换不改变 XML 基础值", async ({ page }) => {
|
test("参数说明延迟显示,单位切换不改变 XML 基础值", async ({ page }) => {
|
||||||
@@ -140,6 +308,63 @@ test("参数说明延迟显示,单位切换不改变 XML 基础值", async ({
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test("科学计数法在提交、单位换算和工程加载后保持", async ({ page }) => {
|
||||||
|
await page.goto("/");
|
||||||
|
await addPneumaticTestComponent(page);
|
||||||
|
|
||||||
|
const pressureInput = page.getByLabel("参考压力", { exact: true });
|
||||||
|
const pressureUnit = page.getByLabel("参考压力单位", { exact: true });
|
||||||
|
|
||||||
|
await pressureInput.fill("1e-5");
|
||||||
|
await pressureInput.press("Enter");
|
||||||
|
await pressureUnit.selectOption("kPa");
|
||||||
|
await expect(pressureInput).toHaveValue("1e-8");
|
||||||
|
await pressureUnit.selectOption("Pa");
|
||||||
|
await expect(pressureInput).toHaveValue("1e-5");
|
||||||
|
|
||||||
|
await pressureUnit.selectOption("kPa");
|
||||||
|
await pressureInput.fill("1.234567890123E+3");
|
||||||
|
await pressureInput.press("Enter");
|
||||||
|
await pressureUnit.selectOption("Pa");
|
||||||
|
await expect(pressureInput).toHaveValue("1.234567890123E+6");
|
||||||
|
await pressureUnit.selectOption("kPa");
|
||||||
|
await expect(pressureInput).toHaveValue("1.234567890123E+3");
|
||||||
|
|
||||||
|
await pressureInput.fill("1.20E+3");
|
||||||
|
await pressureInput.press("Enter");
|
||||||
|
await pressureUnit.selectOption("Pa");
|
||||||
|
await expect(pressureInput).toHaveValue("1.20E+6");
|
||||||
|
await pressureUnit.selectOption("kPa");
|
||||||
|
await expect(pressureInput).toHaveValue("1.20E+3");
|
||||||
|
|
||||||
|
const temperatureInput = page.getByLabel("参考温度", { exact: true });
|
||||||
|
const temperatureUnit = page.getByLabel("参考温度单位", { exact: true });
|
||||||
|
await temperatureUnit.selectOption("degC");
|
||||||
|
await temperatureInput.fill("1e-10");
|
||||||
|
await temperatureInput.press("Enter");
|
||||||
|
await temperatureUnit.selectOption("K");
|
||||||
|
await expect(temperatureInput).toHaveValue("2.731500000001e2");
|
||||||
|
await temperatureUnit.selectOption("degC");
|
||||||
|
await expect(temperatureInput).toHaveValue("1e-10");
|
||||||
|
|
||||||
|
await page.getByRole("button", { name: "保存工程", exact: true }).click();
|
||||||
|
await pressureInput.fill("2");
|
||||||
|
await pressureInput.press("Enter");
|
||||||
|
await page.getByRole("button", { name: "加载工程", exact: true }).click();
|
||||||
|
await page
|
||||||
|
.locator('.flow-canvas .react-flow__node[data-id="amesim_pneumatic_test_1"]')
|
||||||
|
.click();
|
||||||
|
await expect(pressureUnit).toHaveValue("kPa");
|
||||||
|
await expect(pressureInput).toHaveValue("1.20E+3");
|
||||||
|
await expect(temperatureUnit).toHaveValue("degC");
|
||||||
|
await expect(temperatureInput).toHaveValue("1e-10");
|
||||||
|
|
||||||
|
await page.getByRole("button", { name: "生成系统 XML", exact: true }).click();
|
||||||
|
await expect(page.getByLabel("生成的系统 XML")).toContainText(
|
||||||
|
'<Parameter name="reference_pressure" value="1200000"',
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
test("参数表列宽可调整并在刷新后保留", async ({ page }) => {
|
test("参数表列宽可调整并在刷新后保留", async ({ page }) => {
|
||||||
await page.goto("/");
|
await page.goto("/");
|
||||||
|
|
||||||
@@ -388,3 +613,28 @@ test("仿真数值设置同样保留公式,并在执行协议中使用计算
|
|||||||
await expect(xmlBlock).toContainText('tStop="7"');
|
await expect(xmlBlock).toContainText('tStop="7"');
|
||||||
await expect(xmlBlock).not.toContainText(expression);
|
await expect(xmlBlock).not.toContainText(expression);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test("仿真数值设置中的科学计数法在保存加载后保持", async ({ page }) => {
|
||||||
|
await page.goto("/");
|
||||||
|
|
||||||
|
const stepInput = page.getByLabel("采样步长", { exact: true });
|
||||||
|
const maxStepInput = page.getByLabel("最大积分步长", { exact: true });
|
||||||
|
await stepInput.fill("1e-5");
|
||||||
|
await stepInput.press("Enter");
|
||||||
|
await maxStepInput.fill("1.20E-4");
|
||||||
|
await maxStepInput.press("Enter");
|
||||||
|
|
||||||
|
await page.getByRole("button", { name: "保存工程", exact: true }).click();
|
||||||
|
await stepInput.fill("0.1");
|
||||||
|
await stepInput.press("Enter");
|
||||||
|
await maxStepInput.fill("0.01");
|
||||||
|
await maxStepInput.press("Enter");
|
||||||
|
await page.getByRole("button", { name: "加载工程", exact: true }).click();
|
||||||
|
|
||||||
|
await expect(stepInput).toHaveValue("1e-5");
|
||||||
|
await expect(maxStepInput).toHaveValue("1.20E-4");
|
||||||
|
await page.getByRole("button", { name: "生成系统 XML", exact: true }).click();
|
||||||
|
const xmlBlock = page.getByLabel("生成的系统 XML");
|
||||||
|
await expect(xmlBlock).toContainText('step="0.00001"');
|
||||||
|
await expect(xmlBlock).toContainText('maxStep="0.00012"');
|
||||||
|
});
|
||||||
Reference in new issue
Block a user