Skip to content

Commit a479e2f

Browse files
committed
图像设置数据结构
1 parent 3f08bda commit a479e2f

File tree

6 files changed

+161
-55
lines changed

6 files changed

+161
-55
lines changed

src/consts.ts

Lines changed: 76 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,8 @@
1-
import type { FunctionPlotAnnotation, FunctionPlotDatum } from "function-plot";
1+
import type {
2+
FunctionPlotAnnotation,
3+
FunctionPlotDatum,
4+
FunctionPlotOptions,
5+
} from "function-plot";
26
import { cloneDeep } from "lodash-es";
37

48
export type ValueLabel = { value: string; label: string; default?: boolean };
@@ -422,3 +426,74 @@ export function toInternalAnnotation(items: FunctionPlotAnnotation[]) {
422426
});
423427
return cloned;
424428
}
429+
430+
export type InternalGraphOptions = {
431+
xAxis: {
432+
invert: boolean;
433+
label: string;
434+
type: "linear" | "log";
435+
};
436+
yAxis: {
437+
invert: boolean;
438+
label: string;
439+
type: "linear" | "log";
440+
};
441+
grid: boolean;
442+
title: string;
443+
};
444+
445+
export function toInternalGraphOptions(
446+
original: Partial<FunctionPlotOptions>
447+
): InternalGraphOptions {
448+
const { xAxis, yAxis, title, grid } = original;
449+
return {
450+
xAxis: {
451+
invert: xAxis?.invert ?? false,
452+
label: xAxis?.label ?? "",
453+
type: xAxis?.type ?? "linear",
454+
},
455+
yAxis: {
456+
invert: yAxis?.invert ?? false,
457+
label: yAxis?.label ?? "",
458+
type: yAxis?.type ?? "linear",
459+
},
460+
title: title ?? "",
461+
grid: grid ?? false,
462+
};
463+
}
464+
465+
function removeUndefined(obj: object) {
466+
Object.entries(obj).forEach(([key, value]) => {
467+
if (value === undefined) {
468+
delete (<any>obj)[key];
469+
} else if (typeof value === "object" && value !== null) {
470+
removeUndefined(value);
471+
}
472+
});
473+
}
474+
475+
export function toOriginalGraphOptions(
476+
internal: InternalGraphOptions
477+
): Partial<FunctionPlotOptions> {
478+
const { xAxis, yAxis, title, grid } = internal;
479+
const checkIfDefault = (value: any, defaultValue: any) =>
480+
value === defaultValue ? undefined : value;
481+
const cloned: Partial<FunctionPlotOptions> = {
482+
xAxis: {
483+
invert: checkIfDefault(xAxis.invert, false),
484+
label: checkIfDefault(xAxis.label, ""),
485+
type: checkIfDefault(xAxis.type, "linear"),
486+
},
487+
yAxis: {
488+
invert: checkIfDefault(yAxis.invert, false),
489+
label: checkIfDefault(yAxis.label, ""),
490+
type: checkIfDefault(yAxis.type, "linear"),
491+
},
492+
title: checkIfDefault(title, ""),
493+
grid: checkIfDefault(grid, false),
494+
};
495+
removeUndefined(cloned);
496+
if (Object.keys(cloned.xAxis ?? {}).length === 0) delete cloned.xAxis;
497+
if (Object.keys(cloned.yAxis ?? {}).length === 0) delete cloned.yAxis;
498+
return cloned;
499+
}

src/editor/options.vue

Lines changed: 34 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -5,57 +5,70 @@
55

66
<div class="option">
77
<span class="label">{{ t("inputs.grid") }}</span>
8-
<s-switch></s-switch>
8+
<s-switch
9+
v-model.lazy="profile.options.grid"
10+
type="checkbox"
11+
></s-switch>
912
</div>
1013
<div class="option">
1114
<span class="label">{{ t("inputs.caption") }}</span>
12-
<s-text-field :label="t('inputs.caption')"></s-text-field>
15+
<s-text-field
16+
:label="t('inputs.caption')"
17+
v-model="profile.options.title"
18+
></s-text-field>
1319
</div>
1420

1521
<s-divider>{{ t("title.horizontalAxis") }}</s-divider>
22+
<div class="option">
23+
<span class="label">{{ t("inputs.reverse") }}</span>
24+
<s-switch></s-switch>
25+
</div>
1626
<div class="option">
1727
<span class="label">{{ t("inputs.grow") }}</span>
18-
<s-picker :label="t('inputs.grow')" :key="locale">
19-
<s-picker-item value="linear" selected>{{
28+
<s-segmented-button>
29+
<s-segmented-button-item value="y">{{
2030
t("inputs.linear")
21-
}}</s-picker-item>
22-
<s-picker-item value="log">{{ t("inputs.log") }}</s-picker-item>
23-
</s-picker>
31+
}}</s-segmented-button-item>
32+
<s-segmented-button-item value="x">
33+
{{ t("inputs.log") }}
34+
</s-segmented-button-item>
35+
</s-segmented-button>
2436
</div>
2537
<div class="option">
2638
<span class="label">{{ t("inputs.caption") }}</span>
2739
<s-text-field :label="t('inputs.caption')"></s-text-field>
2840
</div>
29-
<div class="option">
30-
<span class="label">{{ t("inputs.range") }}</span>
31-
<s-text-field :label="t('inputs.caption')"></s-text-field>
32-
</div>
3341

3442
<s-divider>{{ t("title.verticalAxis") }}</s-divider>
43+
<div class="option">
44+
<span class="label">{{ t("inputs.reverse") }}</span>
45+
<s-switch></s-switch>
46+
</div>
3547
<div class="option">
3648
<span class="label">{{ t("inputs.grow") }}</span>
37-
<s-picker :label="t('inputs.grow')" :key="locale">
38-
<s-picker-item value="linear" selected>{{
49+
<s-segmented-button>
50+
<s-segmented-button-item value="y">{{
3951
t("inputs.linear")
40-
}}</s-picker-item>
41-
<s-picker-item value="log">{{ t("inputs.log") }}</s-picker-item>
42-
</s-picker>
52+
}}</s-segmented-button-item>
53+
<s-segmented-button-item value="x">
54+
{{ t("inputs.log") }}
55+
</s-segmented-button-item>
56+
</s-segmented-button>
4357
</div>
4458
<div class="option">
4559
<span class="label">{{ t("inputs.caption") }}</span>
4660
<s-text-field :label="t('inputs.caption')"></s-text-field>
4761
</div>
48-
<div class="option">
49-
<span class="label">{{ t("inputs.range") }}</span>
50-
<s-text-field :label="t('inputs.caption')"></s-text-field>
51-
</div>
5262
</div>
5363
</s-scroll-view>
5464
</template>
5565

5666
<script setup lang="ts">
5767
import { useI18n } from "vue-i18n";
58-
const { t, locale } = useI18n();
68+
const { t } = useI18n();
69+
70+
import { useProfile } from "@/states";
71+
const profile = useProfile();
5972
</script>
6073

6174
<style>

src/editor/output.vue

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ watch(
5151
...(profile.annotations.length
5252
? { annotations: profile.getOriginalAnnotaion() }
5353
: {}),
54+
...profile.getOriginalOptions(),
5455
});
5556
const url =
5657
window.location.href.match(/https?:\/\/[^/]+\//) +

src/graph/graph.vue

Lines changed: 38 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ const { t } = useI18n();
2626
import SIconRefresh from "@/ui/icons/refresh.vue";
2727
import SIconWarn from "@/ui/icons/warn.vue";
2828
29-
import { onMounted, ref, watch } from "vue";
29+
import { onMounted, onUnmounted, ref, watch, WatchHandle } from "vue";
3030
import { throttle } from "lodash-es";
3131
import type { FunctionPlotDatum } from "function-plot";
3232
import { getFnType } from "../consts";
@@ -54,40 +54,45 @@ function findError(graphData: FunctionPlotDatum[]) {
5454
return 0;
5555
}
5656
57+
let unwatchHandler: WatchHandle | null = null;
58+
5759
onMounted(async () => {
5860
const functionPlot = (await import("function-plot")).default;
59-
watch(
60-
[() => width, () => height, profile],
61-
throttle(() => {
62-
if (import.meta.env.DEV) console.log("graph update");
63-
const graphData = profile.getOriginalData();
64-
const flag = findError(graphData);
65-
if (flag) {
66-
errorMsg.value = `Invalid input in function ${flag + 1}`;
67-
return;
68-
}
69-
try {
70-
plotRef.value &&
71-
functionPlot({
72-
target: plotRef.value,
73-
data: <FunctionPlotDatum[]>graphData,
74-
width: width - 20,
75-
height: height - 20,
76-
annotations: profile.getOriginalAnnotaion(),
77-
});
78-
if (fullUpdateState.value) {
79-
fullUpdateState.value = false;
80-
emitter.emit("require-post-update", "once after error");
81-
} else errorMsg.value = undefined;
82-
} catch (e) {
83-
// console.log(e);
84-
if (!fullUpdateState.value)
85-
emitter.emit("require-full-update", "error");
86-
errorMsg.value = (e as Error).message;
87-
}
88-
}, 200),
89-
{ immediate: true }
90-
);
61+
const handleUpdate = throttle(() => {
62+
if (import.meta.env.DEV) console.log("graph update");
63+
const graphData = profile.getOriginalData();
64+
const flag = findError(graphData);
65+
if (flag) {
66+
errorMsg.value = `Invalid input in function ${flag + 1}`;
67+
return;
68+
}
69+
try {
70+
plotRef.value &&
71+
functionPlot({
72+
target: plotRef.value,
73+
data: <FunctionPlotDatum[]>graphData,
74+
width: width - 20,
75+
height: height - 20,
76+
annotations: profile.getOriginalAnnotaion(),
77+
...profile.getOriginalOptions(),
78+
});
79+
if (fullUpdateState.value) {
80+
fullUpdateState.value = false;
81+
emitter.emit("require-post-update", "once after error");
82+
} else errorMsg.value = undefined;
83+
} catch (e) {
84+
// console.log(e);
85+
if (!fullUpdateState.value) emitter.emit("require-full-update", "error");
86+
errorMsg.value = (e as Error).message;
87+
}
88+
}, 200);
89+
unwatchHandler = watch([() => width, () => height, profile], handleUpdate, {
90+
immediate: true,
91+
});
92+
});
93+
94+
onUnmounted(() => {
95+
if (unwatchHandler) unwatchHandler();
9196
});
9297
</script>
9398

src/i18n.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,7 @@ export default {
6969
log: "对数",
7070
caption: "标题",
7171
grid: "网格线",
72+
reverse: "反向绘制",
7273
},
7374
title: {
7475
output: "输出代码",
@@ -144,6 +145,7 @@ export default {
144145
log: "Logarithmic",
145146
caption: "Caption",
146147
grid: "Grid",
148+
reverse: "Reversed direction",
147149
},
148150
title: {
149151
output: "Output code",

src/states.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,13 @@ import {
77
getNewDatum,
88
InternalAnnotation,
99
InternalDatum,
10+
InternalGraphOptions,
1011
toInternalAnnotation,
1112
toInternalDatum,
13+
toInternalGraphOptions,
1214
toOriginalAnnotation,
1315
toOriginalDatum,
16+
toOriginalGraphOptions,
1417
} from "./consts";
1518
import { FunctionPlotOptions } from "function-plot";
1619

@@ -51,13 +54,20 @@ export const useProfile = defineStore("profile", () => {
5154
key: Math.random(),
5255
});
5356

57+
const options = ref<InternalGraphOptions>(
58+
toInternalGraphOptions(importedProfile ?? {})
59+
);
60+
const getOriginalOptions = () => toOriginalGraphOptions(options.value);
61+
5462
return {
5563
data,
5664
getOriginalData,
5765
addData,
5866
annotations,
5967
getOriginalAnnotaion,
6068
addAnnotation,
69+
options,
70+
getOriginalOptions,
6171
};
6272
});
6373

0 commit comments

Comments
 (0)