用 bun fmt 格式化全部代码和文档
- bunfig.toml 的 [fmt]:不写分号、单引号、一行最长 160,和原来的写法一致 - .prettierignore:docs/教材摘要/ 是书上的摘录,不格式化 - 模板里一行写两条语句的事件处理(@keydown.delete、@dblclick)改成调用方法:去掉分号后拆成两行,Vue 编译不过 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
d4b8bf9241
commit
de277d3e9d
102 files changed
+1971
-1059
No files matched your search
@@ -2,14 +2,46 @@
|
||||
// 图表(WinForms 的 Chart,教材项目 8 的 7 天温度折线图):运行器发来各个系列的点,用 Chart.js 画。
|
||||
// 图表类型按 WinForms 的 SeriesChartType 对应;配色是 WinForms 默认的 BrightPastel;图例默认在右边;不要动画。
|
||||
import {
|
||||
ArcElement, BarController, BarElement, CategoryScale, Chart, DoughnutController, Filler, Legend, LinearScale, LineController,
|
||||
LineElement, PieController, PointElement, RadarController, RadialLinearScale, Title, Tooltip, type ChartConfiguration,
|
||||
ArcElement,
|
||||
BarController,
|
||||
BarElement,
|
||||
CategoryScale,
|
||||
Chart,
|
||||
DoughnutController,
|
||||
Filler,
|
||||
Legend,
|
||||
LinearScale,
|
||||
LineController,
|
||||
LineElement,
|
||||
PieController,
|
||||
PointElement,
|
||||
RadarController,
|
||||
RadialLinearScale,
|
||||
Title,
|
||||
Tooltip,
|
||||
type ChartConfiguration,
|
||||
} from 'chart.js'
|
||||
import { computed, onBeforeUnmount, onMounted, ref, watch, type CSSProperties } from 'vue'
|
||||
import type { ControlNode } from '@iot/shared'
|
||||
|
||||
Chart.register(ArcElement, BarController, BarElement, CategoryScale, DoughnutController, Filler, Legend, LinearScale, LineController,
|
||||
LineElement, PieController, PointElement, RadarController, RadialLinearScale, Title, Tooltip)
|
||||
Chart.register(
|
||||
ArcElement,
|
||||
BarController,
|
||||
BarElement,
|
||||
CategoryScale,
|
||||
DoughnutController,
|
||||
Filler,
|
||||
Legend,
|
||||
LinearScale,
|
||||
LineController,
|
||||
LineElement,
|
||||
PieController,
|
||||
PointElement,
|
||||
RadarController,
|
||||
RadialLinearScale,
|
||||
Title,
|
||||
Tooltip,
|
||||
)
|
||||
|
||||
interface ChartSeries {
|
||||
name: string
|
||||
@@ -33,7 +65,23 @@ interface ChartData {
|
||||
const props = defineProps<{ node: ControlNode; style: CSSProperties }>()
|
||||
|
||||
/** WinForms 图表默认的 BrightPastel 配色 */
|
||||
const PALETTE = ['#418CF0', '#FCB441', '#E0400A', '#056492', '#BFBFBF', '#1A3B69', '#FFE382', '#129CDD', '#CA6B4B', '#005CDB', '#F3D288', '#506381', '#F1B9A8', '#E0830A', '#7893BE']
|
||||
const PALETTE = [
|
||||
'#418CF0',
|
||||
'#FCB441',
|
||||
'#E0400A',
|
||||
'#056492',
|
||||
'#BFBFBF',
|
||||
'#1A3B69',
|
||||
'#FFE382',
|
||||
'#129CDD',
|
||||
'#CA6B4B',
|
||||
'#005CDB',
|
||||
'#F3D288',
|
||||
'#506381',
|
||||
'#F1B9A8',
|
||||
'#E0830A',
|
||||
'#7893BE',
|
||||
]
|
||||
|
||||
const data = computed(() => props.node.props.Chart as ChartData | undefined)
|
||||
const canvas = ref<HTMLCanvasElement>()
|
||||
@@ -84,12 +132,13 @@ function config(d: ChartData): ChartConfiguration {
|
||||
legend: { display: d.series.some((s) => s.inLegend), position, labels: { filter: (item) => d.series[item.datasetIndex ?? 0]?.inLegend ?? true } },
|
||||
title: { display: d.titles.length > 0, text: d.titles },
|
||||
},
|
||||
scales: pie || chartKind === 'radar'
|
||||
? {}
|
||||
: {
|
||||
x: { stacked, grid: { color: '#d9d9d9' } },
|
||||
y: { stacked, min: d.yMin ?? undefined, max: d.yMax ?? undefined, beginAtZero: true, grid: { color: '#d9d9d9' } },
|
||||
},
|
||||
scales:
|
||||
pie || chartKind === 'radar'
|
||||
? {}
|
||||
: {
|
||||
x: { stacked, grid: { color: '#d9d9d9' } },
|
||||
y: { stacked, min: d.yMin ?? undefined, max: d.yMax ?? undefined, beginAtZero: true, grid: { color: '#d9d9d9' } },
|
||||
},
|
||||
},
|
||||
} as ChartConfiguration
|
||||
}
|
||||
|
||||
@@ -95,13 +95,7 @@ const itemHeight = computed(() => `${(p.value.ItemHeight as number | undefined)
|
||||
|
||||
<template>
|
||||
<!-- Button -->
|
||||
<button
|
||||
v-if="node.type === 'Button'"
|
||||
class="wf-control wf-button"
|
||||
:style="style"
|
||||
:disabled="!enabled"
|
||||
@click="act({ action: 'click' })"
|
||||
>
|
||||
<button v-if="node.type === 'Button'" class="wf-control wf-button" :style="style" :disabled="!enabled" @click="act({ action: 'click' })">
|
||||
{{ displayText(p.Text) }}
|
||||
</button>
|
||||
|
||||
@@ -230,11 +224,7 @@ const itemHeight = computed(() => `${(p.value.ItemHeight as number | undefined)
|
||||
</div>
|
||||
|
||||
<!-- 教材控件库:LED 显示屏 -->
|
||||
<LedControl
|
||||
v-else-if="node.type === 'WinFormsDeviceControlLibrary.LED'"
|
||||
:node="node"
|
||||
:style="style"
|
||||
/>
|
||||
<LedControl v-else-if="node.type === 'WinFormsDeviceControlLibrary.LED'" :node="node" :style="style" />
|
||||
|
||||
<!-- 其他容器(Panel、UserControl 等):按普通容器渲染 -->
|
||||
<div v-else class="wf-control wf-panel" :style="style" :title="node.type">
|
||||
|
||||
@@ -53,6 +53,11 @@ function click(row: number, column: number) {
|
||||
if (col?.check && !col.readOnly && p.value.ReadOnly !== true) props.act({ action: 'cellEdit', row, column, value: cell === true ? 'False' : 'True' })
|
||||
}
|
||||
|
||||
function doubleClick(row: number, column: number) {
|
||||
props.act({ action: 'cellDoubleClick', row, column })
|
||||
startEdit(row, column)
|
||||
}
|
||||
|
||||
function startEdit(row: number, column: number, initial?: string) {
|
||||
const col = columns.value.find((c) => c.index === column)
|
||||
if (!props.enabled || !col || col.readOnly || col.check || p.value.ReadOnly === true) return
|
||||
@@ -137,12 +142,7 @@ function columnStyle(column: Column): CSSProperties {
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr
|
||||
v-for="(row, r) in rows.items"
|
||||
:key="r"
|
||||
:class="{ 'wf-dgv-selected': selectedRows.has(r) }"
|
||||
:style="{ height: `${rows.height}px` }"
|
||||
>
|
||||
<tr v-for="(row, r) in rows.items" :key="r" :class="{ 'wf-dgv-selected': selectedRows.has(r) }" :style="{ height: `${rows.height}px` }">
|
||||
<th v-if="rowHeaders" class="wf-dgv-rowheader" @click="click(r, -1)">
|
||||
{{ current?.[0] === r ? '▶' : row.isNew ? '*' : '' }}
|
||||
</th>
|
||||
@@ -152,7 +152,7 @@ function columnStyle(column: Column): CSSProperties {
|
||||
:class="{ 'wf-dgv-current': isCurrent(r, column.index) }"
|
||||
:style="columnStyle(column)"
|
||||
@click="click(r, column.index)"
|
||||
@dblclick="act({ action: 'cellDoubleClick', row: r, column: column.index }); startEdit(r, column.index)"
|
||||
@dblclick="doubleClick(r, column.index)"
|
||||
>
|
||||
<input
|
||||
v-if="editing && editing.row === r && editing.column === column.index"
|
||||
|
||||
@@ -33,8 +33,7 @@ function pick(event: Event) {
|
||||
if (!picked) return
|
||||
const [date, time] = value.value.split('T')
|
||||
const withSeconds = (t: string) => (t.length === 5 ? `${t}:00` : t)
|
||||
const next =
|
||||
inputType.value === 'date' ? `${picked}T${time}` : inputType.value === 'time' ? `${date}T${withSeconds(picked)}` : withSeconds(picked)
|
||||
const next = inputType.value === 'date' ? `${picked}T${time}` : inputType.value === 'time' ? `${date}T${withSeconds(picked)}` : withSeconds(picked)
|
||||
props.act({ action: 'pick', value: next })
|
||||
}
|
||||
|
||||
|
||||
@@ -26,7 +26,10 @@ const props = defineProps<{
|
||||
|
||||
const p = computed(() => props.form.props)
|
||||
const runImages = inject(RUN_IMAGES, new Map<string, string>())
|
||||
provide(AMBIENT_BACK_COLOR, computed(() => (p.value.BackColor as string | null) ?? DEFAULT_BACK_COLOR))
|
||||
provide(
|
||||
AMBIENT_BACK_COLOR,
|
||||
computed(() => (p.value.BackColor as string | null) ?? DEFAULT_BACK_COLOR),
|
||||
)
|
||||
const borderless = computed(() => p.value.FormBorderStyle === 'None')
|
||||
// WinForms 中先加入 Controls 的控件在最上层,所以倒序渲染
|
||||
const childrenTopLast = computed(() => [...props.form.children].reverse())
|
||||
|
||||
@@ -8,13 +8,39 @@ const emit = defineEmits<{ reply: [result: string] }>()
|
||||
|
||||
const BUTTONS: Record<string, [string, string][]> = {
|
||||
OK: [['OK', '确定']],
|
||||
OKCancel: [['OK', '确定'], ['Cancel', '取消']],
|
||||
YesNo: [['Yes', '是(Y)'], ['No', '否(N)']],
|
||||
YesNoCancel: [['Yes', '是(Y)'], ['No', '否(N)'], ['Cancel', '取消']],
|
||||
RetryCancel: [['Retry', '重试(R)'], ['Cancel', '取消']],
|
||||
AbortRetryIgnore: [['Abort', '中止(A)'], ['Retry', '重试(R)'], ['Ignore', '忽略(I)']],
|
||||
OKCancel: [
|
||||
['OK', '确定'],
|
||||
['Cancel', '取消'],
|
||||
],
|
||||
YesNo: [
|
||||
['Yes', '是(Y)'],
|
||||
['No', '否(N)'],
|
||||
],
|
||||
YesNoCancel: [
|
||||
['Yes', '是(Y)'],
|
||||
['No', '否(N)'],
|
||||
['Cancel', '取消'],
|
||||
],
|
||||
RetryCancel: [
|
||||
['Retry', '重试(R)'],
|
||||
['Cancel', '取消'],
|
||||
],
|
||||
AbortRetryIgnore: [
|
||||
['Abort', '中止(A)'],
|
||||
['Retry', '重试(R)'],
|
||||
['Ignore', '忽略(I)'],
|
||||
],
|
||||
}
|
||||
const ICONS: Record<string, string> = {
|
||||
Error: '⛔',
|
||||
Hand: '⛔',
|
||||
Stop: '⛔',
|
||||
Warning: '⚠️',
|
||||
Exclamation: '⚠️',
|
||||
Information: 'ℹ️',
|
||||
Asterisk: 'ℹ️',
|
||||
Question: '❓',
|
||||
}
|
||||
const ICONS: Record<string, string> = { Error: '⛔', Hand: '⛔', Stop: '⛔', Warning: '⚠️', Exclamation: '⚠️', Information: 'ℹ️', Asterisk: 'ℹ️', Question: '❓' }
|
||||
|
||||
const buttons = computed(() => BUTTONS[props.box.buttons] ?? BUTTONS.OK)
|
||||
const icon = computed(() => ICONS[props.box.icon])
|
||||
|
||||
@@ -22,7 +22,7 @@ const vertical = computed(() => p.value.Orientation === 'Vertical')
|
||||
const tickStyle = computed(() => String(p.value.TickStyle ?? 'BottomRight'))
|
||||
const size = computed(() => p.value.Size as { width: number; height: number } | undefined)
|
||||
/** 轨道方向上的长度 */
|
||||
const length = computed(() => (vertical.value ? size.value?.height ?? 104 : size.value?.width ?? 104))
|
||||
const length = computed(() => (vertical.value ? (size.value?.height ?? 104) : (size.value?.width ?? 104)))
|
||||
|
||||
/** 值 → 沿轨道的位置(像素);竖向时最小值在下面 */
|
||||
function position(v: number) {
|
||||
|
||||
@@ -28,8 +28,7 @@ export function imageSources(value: unknown, images: ReadonlyMap<string, string>
|
||||
}
|
||||
|
||||
/** <img> 的 srcset:有两倍图时浏览器按屏幕分辨率选 */
|
||||
export const srcset = (sources: { src: string; hidpi?: string } | undefined) =>
|
||||
sources?.hidpi ? `${sources.src} 1x, ${sources.hidpi} 2x` : undefined
|
||||
export const srcset = (sources: { src: string; hidpi?: string } | undefined) => (sources?.hidpi ? `${sources.src} 1x, ${sources.hidpi} 2x` : undefined)
|
||||
|
||||
/** 背景图片的 CSS:BackgroundImageLayout 为 Tile(默认,平铺)、None(左上角一张)、Center、Stretch、Zoom */
|
||||
export function backgroundCss(value: unknown, layout: unknown, images: ReadonlyMap<string, string>): CSSProperties {
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
background: #f0f0f0;
|
||||
border: 1px solid #1883d7;
|
||||
box-shadow: 0 6px 24px rgb(0 0 0 / 0.25);
|
||||
font-family: "SimSun", "Songti SC", "Noto Serif CJK SC", "Noto Serif SC", serif;
|
||||
font-family: 'SimSun', 'Songti SC', 'Noto Serif CJK SC', 'Noto Serif SC', serif;
|
||||
font-size: 12px;
|
||||
color: #000;
|
||||
user-select: none;
|
||||
@@ -26,7 +26,7 @@
|
||||
.wf-title {
|
||||
flex: 1;
|
||||
padding-left: 8px;
|
||||
font-family: "Microsoft YaHei", "PingFang SC", "Noto Sans CJK SC", sans-serif;
|
||||
font-family: 'Microsoft YaHei', 'PingFang SC', 'Noto Sans CJK SC', sans-serif;
|
||||
font-size: 12px;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
@@ -373,7 +373,7 @@
|
||||
flex-direction: column;
|
||||
background: #000;
|
||||
color: #9aa4b2;
|
||||
font-family: "Microsoft YaHei", "PingFang SC", sans-serif;
|
||||
font-family: 'Microsoft YaHei', 'PingFang SC', sans-serif;
|
||||
font-size: 11px;
|
||||
overflow: hidden;
|
||||
}
|
||||
@@ -456,15 +456,15 @@
|
||||
border-radius: 50%;
|
||||
}
|
||||
|
||||
.wf-checkbox-box[data-checked="true"]::after {
|
||||
content: "✓";
|
||||
.wf-checkbox-box[data-checked='true']::after {
|
||||
content: '✓';
|
||||
display: block;
|
||||
font: bold 11px/11px sans-serif;
|
||||
text-align: center;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.wf-radio-dot[data-checked="true"] {
|
||||
.wf-radio-dot[data-checked='true'] {
|
||||
background: radial-gradient(circle, #333 0 3px, #fff 3.5px);
|
||||
}
|
||||
|
||||
@@ -551,7 +551,7 @@
|
||||
align-items: center;
|
||||
padding: 24px 28px;
|
||||
background: #fff;
|
||||
font-family: "Microsoft YaHei", "PingFang SC", "Noto Sans CJK SC", sans-serif;
|
||||
font-family: 'Microsoft YaHei', 'PingFang SC', 'Noto Sans CJK SC', sans-serif;
|
||||
}
|
||||
|
||||
.wf-messagebox-icon {
|
||||
|
||||
Reference in new issue
Block a user