@agile-team/wl-skills-kit 2.9.3 → 2.9.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +12 -0
- package/README.md +1 -1
- package/bin/wl-skills.js +1 -1
- package/files/.github/copilot-instructions.md +9 -14
- package/files/.github/guides/architecture.md +1 -1
- package/files/.github/guides/usage.md +1 -1
- package/files/.github/skills/_best-practices.md +2 -2
- package/files/.github/skills/core/convention-audit/SKILL.md +9 -7
- package/files/.github/skills/core/page-codegen/SKILL.md +1 -0
- package/files/.github/skills/core/page-codegen/USAGE.md +1 -1
- package/files/.github/skills/core/prototype-scan/SKILL.md +3 -3
- package/files/.github/standards/14-layout-containers.md +159 -159
- package/files/demo/sale/demo/add-demo/index.scss +207 -207
- package/files/demo/sale/demo/add-demo/index.vue +171 -171
- package/files/demo/sale/demo/metallurgical-spec/index.scss +264 -264
- package/files/demo/sale/demo/metallurgical-spec/index.vue +313 -313
- package/files/src/components/global/C_Splitter/index.vue +149 -149
- package/files/src/components/local/c_formSections/README.md +496 -496
- package/package.json +2 -2
|
@@ -1,149 +1,149 @@
|
|
|
1
|
-
<template>
|
|
2
|
-
<div
|
|
3
|
-
ref="containerRef"
|
|
4
|
-
class="my-splitter-container"
|
|
5
|
-
:class="[`is-${direction}`]"
|
|
6
|
-
>
|
|
7
|
-
<template v-for="(item, index) in vnodes" :key="index">
|
|
8
|
-
<div class="splitter-item" :style="getItemStyle(index)">
|
|
9
|
-
<component :is="item" />
|
|
10
|
-
</div>
|
|
11
|
-
|
|
12
|
-
<div
|
|
13
|
-
v-if="index < vnodes.length - 1"
|
|
14
|
-
class="splitter-trigger"
|
|
15
|
-
@mousedown="(e) => onMouseDown(e, index)"
|
|
16
|
-
>
|
|
17
|
-
<div class="trigger-line"></div>
|
|
18
|
-
</div>
|
|
19
|
-
</template>
|
|
20
|
-
</div>
|
|
21
|
-
</template>
|
|
22
|
-
|
|
23
|
-
<script setup>
|
|
24
|
-
import { ref, reactive, onMounted, useSlots, onUnmounted } from "vue";
|
|
25
|
-
|
|
26
|
-
// ⚠️ [DEPRECATED] C_Splitter 已废弃,请改用:
|
|
27
|
-
// 左右分割 → <jh-drag-col :leftWidth="260"> #left / #right </jh-drag-col>
|
|
28
|
-
// 上下分栈 → <jh-drag-row :topHeight="200"> #top / #bottom </jh-drag-row>
|
|
29
|
-
// 原因:C_Splitter 在 onMounted 中调用 slots.default() 冻结 vnode 快照,
|
|
30
|
-
// 导致子树响应式绑定(v-if / v-show / 插值)与父组件 ref 完全解耦,
|
|
31
|
-
// ref 赋值不触发重渲染。详见 .github/standards/14-layout-containers.md
|
|
32
|
-
if (typeof window !== "undefined" && !window.__C_SPLITTER_WARNED__) {
|
|
33
|
-
window.__C_SPLITTER_WARNED__ = true;
|
|
34
|
-
// eslint-disable-next-line no-console
|
|
35
|
-
console.warn(
|
|
36
|
-
"[C_Splitter 已废弃] 已检测到 C_Splitter 使用。请改用 jh-drag-col / jh-drag-row。\n" +
|
|
37
|
-
"原因:slots.default() 被冻结为 vnode 快照,导致子树响应式失效。\n" +
|
|
38
|
-
"详见:.github/standards/14-layout-containers.md"
|
|
39
|
-
);
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
const props = defineProps({
|
|
43
|
-
direction: {
|
|
44
|
-
type: String,
|
|
45
|
-
default: "horizontal" // horizontal | vertical
|
|
46
|
-
},
|
|
47
|
-
minSize: {
|
|
48
|
-
type: Number,
|
|
49
|
-
default: 50
|
|
50
|
-
}
|
|
51
|
-
});
|
|
52
|
-
|
|
53
|
-
const slots = useSlots();
|
|
54
|
-
const containerRef = ref(null);
|
|
55
|
-
const vnodes = ref([]); // 存储虚拟节点
|
|
56
|
-
const paneConfigs = ref([]); // 存储每个面板的 minSize 等配置
|
|
57
|
-
const sizes = reactive([]);
|
|
58
|
-
const isDragging = ref(false);
|
|
59
|
-
let currentTriggerIndex = -1;
|
|
60
|
-
|
|
61
|
-
// 初始化:获取插槽并分配初始平均尺寸
|
|
62
|
-
onMounted(() => {
|
|
63
|
-
const defaultSlots = slots.default ? slots.default() : [];
|
|
64
|
-
// 过滤掉注释节点(Symbol(v-cmt))和Fragment,只保留真正的元素
|
|
65
|
-
const children = defaultSlots.filter((v) => {
|
|
66
|
-
// 排除 Symbol 类型(注释节点)和 Fragment
|
|
67
|
-
if (typeof v.type === "symbol") return false;
|
|
68
|
-
// 保留 string(原生HTML元素)和 object(组件)
|
|
69
|
-
return typeof v.type === "string" || typeof v.type === "object";
|
|
70
|
-
});
|
|
71
|
-
vnodes.value = children;
|
|
72
|
-
|
|
73
|
-
const rect = containerRef.value.getBoundingClientRect();
|
|
74
|
-
const totalAvailable =
|
|
75
|
-
props.direction === "horizontal" ? rect.width : rect.height;
|
|
76
|
-
// vertical 模式 trigger 高度是 8px,horizontal 模式宽度是 4px
|
|
77
|
-
const triggerSize = props.direction === "horizontal" ? 4 : 8;
|
|
78
|
-
const triggerTotalSize = (children.length - 1) * triggerSize;
|
|
79
|
-
|
|
80
|
-
let remainingSize = totalAvailable - triggerTotalSize;
|
|
81
|
-
let autoCount = 0;
|
|
82
|
-
|
|
83
|
-
// 1. 第一次遍历:解析 initialSize (支持 200 或 "30%")
|
|
84
|
-
const tempSizes = children.map((vnode) => {
|
|
85
|
-
const initSize = vnode.props?.initialSize;
|
|
86
|
-
if (initSize === undefined || initSize === null) {
|
|
87
|
-
autoCount++;
|
|
88
|
-
return null;
|
|
89
|
-
}
|
|
90
|
-
const sizePx =
|
|
91
|
-
typeof initSize === "string" && initSize.endsWith("%")
|
|
92
|
-
? (parseFloat(initSize) / 100) * (totalAvailable - triggerTotalSize)
|
|
93
|
-
: parseFloat(initSize);
|
|
94
|
-
remainingSize -= sizePx;
|
|
95
|
-
return sizePx;
|
|
96
|
-
});
|
|
97
|
-
|
|
98
|
-
// 2. 第二次遍历:填充 sizes 和 paneConfigs
|
|
99
|
-
tempSizes.forEach((size, idx) => {
|
|
100
|
-
sizes.push(size === null ? remainingSize / autoCount : size);
|
|
101
|
-
paneConfigs.value.push({
|
|
102
|
-
minSize: children[idx].props?.minSize || props.minSize
|
|
103
|
-
});
|
|
104
|
-
});
|
|
105
|
-
});
|
|
106
|
-
|
|
107
|
-
const getItemStyle = (index) => {
|
|
108
|
-
const prop = props.direction === "horizontal" ? "width" : "height";
|
|
109
|
-
return { [prop]: `${sizes[index]}px` };
|
|
110
|
-
};
|
|
111
|
-
|
|
112
|
-
// 拖拽逻辑
|
|
113
|
-
const onMouseDown = (e, index) => {
|
|
114
|
-
isDragging.value = true;
|
|
115
|
-
currentTriggerIndex = index;
|
|
116
|
-
document.body.style.userSelect = "none";
|
|
117
|
-
document.body.style.cursor =
|
|
118
|
-
props.direction === "horizontal" ? "col-resize" : "row-resize";
|
|
119
|
-
|
|
120
|
-
window.addEventListener("mousemove", onMouseMove);
|
|
121
|
-
window.addEventListener("mouseup", onMouseUp);
|
|
122
|
-
};
|
|
123
|
-
|
|
124
|
-
const onMouseMove = (e) => {
|
|
125
|
-
if (!isDragging.value) return;
|
|
126
|
-
const i = currentTriggerIndex;
|
|
127
|
-
const movement = props.direction === "horizontal" ? e.movementX : e.movementY;
|
|
128
|
-
|
|
129
|
-
const minCurr = paneConfigs.value[i].minSize;
|
|
130
|
-
const minNext = paneConfigs.value[i + 1].minSize;
|
|
131
|
-
|
|
132
|
-
if (sizes[i] + movement >= minCurr && sizes[i + 1] - movement >= minNext) {
|
|
133
|
-
sizes[i] += movement;
|
|
134
|
-
sizes[i + 1] -= movement;
|
|
135
|
-
}
|
|
136
|
-
};
|
|
137
|
-
|
|
138
|
-
const onMouseUp = () => {
|
|
139
|
-
isDragging.value = false;
|
|
140
|
-
document.body.style.userSelect = "";
|
|
141
|
-
document.body.style.cursor = "";
|
|
142
|
-
window.removeEventListener("mousemove", onMouseMove);
|
|
143
|
-
window.removeEventListener("mouseup", onMouseUp);
|
|
144
|
-
};
|
|
145
|
-
|
|
146
|
-
onUnmounted(() => onMouseUp())
|
|
147
|
-
</script>
|
|
148
|
-
|
|
149
|
-
<style scoped src="./index.scss"></style>
|
|
1
|
+
<template>
|
|
2
|
+
<div
|
|
3
|
+
ref="containerRef"
|
|
4
|
+
class="my-splitter-container"
|
|
5
|
+
:class="[`is-${direction}`]"
|
|
6
|
+
>
|
|
7
|
+
<template v-for="(item, index) in vnodes" :key="index">
|
|
8
|
+
<div class="splitter-item" :style="getItemStyle(index)">
|
|
9
|
+
<component :is="item" />
|
|
10
|
+
</div>
|
|
11
|
+
|
|
12
|
+
<div
|
|
13
|
+
v-if="index < vnodes.length - 1"
|
|
14
|
+
class="splitter-trigger"
|
|
15
|
+
@mousedown="(e) => onMouseDown(e, index)"
|
|
16
|
+
>
|
|
17
|
+
<div class="trigger-line"></div>
|
|
18
|
+
</div>
|
|
19
|
+
</template>
|
|
20
|
+
</div>
|
|
21
|
+
</template>
|
|
22
|
+
|
|
23
|
+
<script setup>
|
|
24
|
+
import { ref, reactive, onMounted, useSlots, onUnmounted } from "vue";
|
|
25
|
+
|
|
26
|
+
// ⚠️ [DEPRECATED] C_Splitter 已废弃,请改用:
|
|
27
|
+
// 左右分割 → <jh-drag-col :leftWidth="260"> #left / #right </jh-drag-col>
|
|
28
|
+
// 上下分栈 → <jh-drag-row :topHeight="200"> #top / #bottom </jh-drag-row>
|
|
29
|
+
// 原因:C_Splitter 在 onMounted 中调用 slots.default() 冻结 vnode 快照,
|
|
30
|
+
// 导致子树响应式绑定(v-if / v-show / 插值)与父组件 ref 完全解耦,
|
|
31
|
+
// ref 赋值不触发重渲染。详见 .github/standards/14-layout-containers.md
|
|
32
|
+
if (typeof window !== "undefined" && !window.__C_SPLITTER_WARNED__) {
|
|
33
|
+
window.__C_SPLITTER_WARNED__ = true;
|
|
34
|
+
// eslint-disable-next-line no-console
|
|
35
|
+
console.warn(
|
|
36
|
+
"[C_Splitter 已废弃] 已检测到 C_Splitter 使用。请改用 jh-drag-col / jh-drag-row。\n" +
|
|
37
|
+
"原因:slots.default() 被冻结为 vnode 快照,导致子树响应式失效。\n" +
|
|
38
|
+
"详见:.github/standards/14-layout-containers.md"
|
|
39
|
+
);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
const props = defineProps({
|
|
43
|
+
direction: {
|
|
44
|
+
type: String,
|
|
45
|
+
default: "horizontal" // horizontal | vertical
|
|
46
|
+
},
|
|
47
|
+
minSize: {
|
|
48
|
+
type: Number,
|
|
49
|
+
default: 50
|
|
50
|
+
}
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
const slots = useSlots();
|
|
54
|
+
const containerRef = ref(null);
|
|
55
|
+
const vnodes = ref([]); // 存储虚拟节点
|
|
56
|
+
const paneConfigs = ref([]); // 存储每个面板的 minSize 等配置
|
|
57
|
+
const sizes = reactive([]);
|
|
58
|
+
const isDragging = ref(false);
|
|
59
|
+
let currentTriggerIndex = -1;
|
|
60
|
+
|
|
61
|
+
// 初始化:获取插槽并分配初始平均尺寸
|
|
62
|
+
onMounted(() => {
|
|
63
|
+
const defaultSlots = slots.default ? slots.default() : [];
|
|
64
|
+
// 过滤掉注释节点(Symbol(v-cmt))和Fragment,只保留真正的元素
|
|
65
|
+
const children = defaultSlots.filter((v) => {
|
|
66
|
+
// 排除 Symbol 类型(注释节点)和 Fragment
|
|
67
|
+
if (typeof v.type === "symbol") return false;
|
|
68
|
+
// 保留 string(原生HTML元素)和 object(组件)
|
|
69
|
+
return typeof v.type === "string" || typeof v.type === "object";
|
|
70
|
+
});
|
|
71
|
+
vnodes.value = children;
|
|
72
|
+
|
|
73
|
+
const rect = containerRef.value.getBoundingClientRect();
|
|
74
|
+
const totalAvailable =
|
|
75
|
+
props.direction === "horizontal" ? rect.width : rect.height;
|
|
76
|
+
// vertical 模式 trigger 高度是 8px,horizontal 模式宽度是 4px
|
|
77
|
+
const triggerSize = props.direction === "horizontal" ? 4 : 8;
|
|
78
|
+
const triggerTotalSize = (children.length - 1) * triggerSize;
|
|
79
|
+
|
|
80
|
+
let remainingSize = totalAvailable - triggerTotalSize;
|
|
81
|
+
let autoCount = 0;
|
|
82
|
+
|
|
83
|
+
// 1. 第一次遍历:解析 initialSize (支持 200 或 "30%")
|
|
84
|
+
const tempSizes = children.map((vnode) => {
|
|
85
|
+
const initSize = vnode.props?.initialSize;
|
|
86
|
+
if (initSize === undefined || initSize === null) {
|
|
87
|
+
autoCount++;
|
|
88
|
+
return null;
|
|
89
|
+
}
|
|
90
|
+
const sizePx =
|
|
91
|
+
typeof initSize === "string" && initSize.endsWith("%")
|
|
92
|
+
? (parseFloat(initSize) / 100) * (totalAvailable - triggerTotalSize)
|
|
93
|
+
: parseFloat(initSize);
|
|
94
|
+
remainingSize -= sizePx;
|
|
95
|
+
return sizePx;
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
// 2. 第二次遍历:填充 sizes 和 paneConfigs
|
|
99
|
+
tempSizes.forEach((size, idx) => {
|
|
100
|
+
sizes.push(size === null ? remainingSize / autoCount : size);
|
|
101
|
+
paneConfigs.value.push({
|
|
102
|
+
minSize: children[idx].props?.minSize || props.minSize
|
|
103
|
+
});
|
|
104
|
+
});
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
const getItemStyle = (index) => {
|
|
108
|
+
const prop = props.direction === "horizontal" ? "width" : "height";
|
|
109
|
+
return { [prop]: `${sizes[index]}px` };
|
|
110
|
+
};
|
|
111
|
+
|
|
112
|
+
// 拖拽逻辑
|
|
113
|
+
const onMouseDown = (e, index) => {
|
|
114
|
+
isDragging.value = true;
|
|
115
|
+
currentTriggerIndex = index;
|
|
116
|
+
document.body.style.userSelect = "none";
|
|
117
|
+
document.body.style.cursor =
|
|
118
|
+
props.direction === "horizontal" ? "col-resize" : "row-resize";
|
|
119
|
+
|
|
120
|
+
window.addEventListener("mousemove", onMouseMove);
|
|
121
|
+
window.addEventListener("mouseup", onMouseUp);
|
|
122
|
+
};
|
|
123
|
+
|
|
124
|
+
const onMouseMove = (e) => {
|
|
125
|
+
if (!isDragging.value) return;
|
|
126
|
+
const i = currentTriggerIndex;
|
|
127
|
+
const movement = props.direction === "horizontal" ? e.movementX : e.movementY;
|
|
128
|
+
|
|
129
|
+
const minCurr = paneConfigs.value[i].minSize;
|
|
130
|
+
const minNext = paneConfigs.value[i + 1].minSize;
|
|
131
|
+
|
|
132
|
+
if (sizes[i] + movement >= minCurr && sizes[i + 1] - movement >= minNext) {
|
|
133
|
+
sizes[i] += movement;
|
|
134
|
+
sizes[i + 1] -= movement;
|
|
135
|
+
}
|
|
136
|
+
};
|
|
137
|
+
|
|
138
|
+
const onMouseUp = () => {
|
|
139
|
+
isDragging.value = false;
|
|
140
|
+
document.body.style.userSelect = "";
|
|
141
|
+
document.body.style.cursor = "";
|
|
142
|
+
window.removeEventListener("mousemove", onMouseMove);
|
|
143
|
+
window.removeEventListener("mouseup", onMouseUp);
|
|
144
|
+
};
|
|
145
|
+
|
|
146
|
+
onUnmounted(() => onMouseUp())
|
|
147
|
+
</script>
|
|
148
|
+
|
|
149
|
+
<style scoped src="./index.scss"></style>
|