@icc-grow/web-components 1.0.0 → 1.1.0
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/README.md +15 -0
- package/dist/es/components/i-avatar-group/src/avatar-group.mjs +1 -1
- package/dist/es/components/i-form-item/src/form-item.mjs +1 -1
- package/dist/es/components/i-form-item/src/form-item.vue_vue_type_script_setup_true_lang.mjs +2 -1
- package/dist/es/components/i-image/src/image.vue_vue_type_script_setup_true_lang.mjs +20 -20
- package/dist/es/components/i-tooltip/src/tooltip-content.mjs +1 -1
- package/dist/es/style.css +1 -1
- package/dist/es/utils/use-intersection-observer.mjs +31 -0
- package/dist/lib/components/i-avatar-group/src/avatar-group.cjs +1 -1
- package/dist/lib/components/i-form-item/src/form-item.cjs +1 -1
- package/dist/lib/components/i-form-item/src/form-item.vue_vue_type_script_setup_true_lang.cjs +1 -1
- package/dist/lib/components/i-image/src/image.vue_vue_type_script_setup_true_lang.cjs +1 -1
- package/dist/lib/components/i-tooltip/src/tooltip-content.cjs +1 -1
- package/dist/lib/style.css +1 -1
- package/dist/lib/utils/use-intersection-observer.cjs +1 -0
- package/dist/types/components/i-form/index.d.ts +3 -3
- package/dist/types/components/i-form/index.d.ts.map +1 -1
- package/dist/types/components/i-form/src/form.vue.d.ts +1 -1
- package/dist/types/components/i-form/src/form.vue.d.ts.map +1 -1
- package/dist/types/components/i-form/src/types.d.ts +16 -4
- package/dist/types/components/i-form/src/types.d.ts.map +1 -1
- package/dist/types/components/i-form-item/index.d.ts +3 -3
- package/dist/types/components/i-form-item/index.d.ts.map +1 -1
- package/dist/types/components/i-form-item/src/form-item.vue.d.ts +2 -2
- package/dist/types/components/i-form-item/src/form-item.vue.d.ts.map +1 -1
- package/dist/types/components/i-form-item/src/types.d.ts +2 -2
- package/dist/types/components/i-form-item/src/types.d.ts.map +1 -1
- package/dist/types/components/i-tooltip/index.d.ts +9 -2
- package/dist/types/components/i-tooltip/index.d.ts.map +1 -1
- package/dist/types/components/i-tooltip/src/tooltip-content.vue.d.ts +15 -2
- package/dist/types/components/i-tooltip/src/tooltip-content.vue.d.ts.map +1 -1
- package/dist/types/components/i-tooltip/src/tooltip.vue.d.ts +18 -4
- package/dist/types/components/i-tooltip/src/tooltip.vue.d.ts.map +1 -1
- package/dist/types/utils/use-intersection-observer.d.ts +19 -0
- package/dist/types/utils/use-intersection-observer.d.ts.map +1 -0
- package/package.json +2 -3
- package/skills/icc-web-components/SKILL.md +0 -191
- package/skills/icc-web-components/patterns/crud-list.md +0 -195
- package/skills/icc-web-components/patterns/dialog-form.md +0 -180
- package/skills/icc-web-components/patterns/form-page.md +0 -139
- package/skills/icc-web-components/patterns/tabs-page.md +0 -164
- package/skills/icc-web-components/patterns/tree-manage.md +0 -169
- package/skills/icc-web-components/references/i-avatar.md +0 -29
- package/skills/icc-web-components/references/i-badge.md +0 -19
- package/skills/icc-web-components/references/i-card.md +0 -12
- package/skills/icc-web-components/references/i-color-picker.md +0 -17
- package/skills/icc-web-components/references/i-crud-page.md +0 -204
- package/skills/icc-web-components/references/i-dialog.md +0 -36
- package/skills/icc-web-components/references/i-draggable.md +0 -46
- package/skills/icc-web-components/references/i-dropdown.md +0 -21
- package/skills/icc-web-components/references/i-empty.md +0 -15
- package/skills/icc-web-components/references/i-feedback.md +0 -50
- package/skills/icc-web-components/references/i-form.md +0 -223
- package/skills/icc-web-components/references/i-icon.md +0 -21
- package/skills/icc-web-components/references/i-image.md +0 -34
- package/skills/icc-web-components/references/i-loading.md +0 -25
- package/skills/icc-web-components/references/i-menu.md +0 -22
- package/skills/icc-web-components/references/i-popover.md +0 -51
- package/skills/icc-web-components/references/i-progress.md +0 -14
- package/skills/icc-web-components/references/i-scrollbar.md +0 -20
- package/skills/icc-web-components/references/i-space.md +0 -15
- package/skills/icc-web-components/references/i-steps.md +0 -12
- package/skills/icc-web-components/references/i-tabs.md +0 -16
- package/skills/icc-web-components/references/i-tag.md +0 -35
- package/skills/icc-web-components/references/i-time-picker.md +0 -19
- package/skills/icc-web-components/references/i-transition.md +0 -17
- package/skills/icc-web-components/references/i-tree-select.md +0 -26
- package/skills/icc-web-components/references/i-tree.md +0 -34
- package/skills/icc-web-components/references/i-upload.md +0 -25
- package/skills/icc-web-components/references/i-virtual-list.md +0 -24
- package/skills/icc-web-components/references/i-virtual-table.md +0 -20
- package/skills/icc-web-components/rules/composition.md +0 -28
- package/skills/icc-web-components/rules/forms.md +0 -28
- package/skills/icc-web-components/rules/styling.md +0 -45
|
@@ -37,7 +37,12 @@ export declare const ITooltip: {
|
|
|
37
37
|
readonly onMouseleave?: (() => any) | undefined;
|
|
38
38
|
} & import('vue').VNodeProps & import('vue').AllowedComponentProps & import('vue').ComponentCustomProps;
|
|
39
39
|
$attrs: import('vue').Attrs;
|
|
40
|
-
$refs:
|
|
40
|
+
$refs: {
|
|
41
|
+
[x: string]: unknown;
|
|
42
|
+
} & {
|
|
43
|
+
floatingRef: HTMLDivElement;
|
|
44
|
+
arrowRef: HTMLDivElement;
|
|
45
|
+
};
|
|
41
46
|
$slots: Readonly<{
|
|
42
47
|
[name: string]: import('vue').Slot<any> | undefined;
|
|
43
48
|
}>;
|
|
@@ -108,7 +113,9 @@ export declare const ITooltip: {
|
|
|
108
113
|
}>, "floatingRef" | ("content" | "zIndex" | "offset" | "placement" | "visible" | "reference" | "title" | "showArrow" | "interactive" | "theme")> & import('vue').ShallowUnwrapRef<{
|
|
109
114
|
floatingRef: import('vue').Ref<HTMLElement | null, HTMLElement | null>;
|
|
110
115
|
}> & {} & import('vue').ComponentCustomProperties & {} & {
|
|
111
|
-
$slots:
|
|
116
|
+
$slots: {
|
|
117
|
+
default?(_: {}): any;
|
|
118
|
+
};
|
|
112
119
|
}) | null;
|
|
113
120
|
}, any, import('vue').ComponentProvideOptions, {
|
|
114
121
|
P: {};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/components/i-tooltip/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAO,SAAS,EAAE,MAAM,EAAE,MAAM,KAAK,CAAC;AAElD,OAAO,OAAO,MAAM,mBAAmB,CAAC;AAGxC,cAAc,aAAa,CAAC;AAC5B,MAAM,MAAM,gBAAgB,GAAG,YAAY,CAAC,OAAO,OAAO,CAAC,CAAC;AAE5D,eAAO,MAAM,QAAQ
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/components/i-tooltip/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAO,SAAS,EAAE,MAAM,EAAE,MAAM,KAAK,CAAC;AAElD,OAAO,OAAO,MAAM,mBAAmB,CAAC;AAGxC,cAAc,aAAa,CAAC;AAC5B,MAAM,MAAM,gBAAgB,GAAG,YAAY,CAAC,OAAO,OAAO,CAAC,CAAC;AAE5D,eAAO,MAAM,QAAQ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;wCAS8+yB,GAAG,8CAA8C,GAAG,yBAAyB,GAAG,6DAAmC,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;WAT7kzB,CAAC;AAC7C,eAAO,MAAM,iBAAiB,EAAe,SAAS,GAAG,MAAM,CAAC;AAMhE,eAAO,MAAM,iBAAiB,oBAAoB,CAAC;AACnD,eAAe,QAAQ,CAAC"}
|
|
@@ -1,5 +1,15 @@
|
|
|
1
1
|
import { TooltipContentProps } from './types';
|
|
2
|
-
declare function __VLS_template():
|
|
2
|
+
declare function __VLS_template(): {
|
|
3
|
+
attrs: Partial<{}>;
|
|
4
|
+
slots: {
|
|
5
|
+
default?(_: {}): any;
|
|
6
|
+
};
|
|
7
|
+
refs: {
|
|
8
|
+
floatingRef: HTMLDivElement;
|
|
9
|
+
arrowRef: HTMLDivElement;
|
|
10
|
+
};
|
|
11
|
+
rootEl: any;
|
|
12
|
+
};
|
|
3
13
|
type __VLS_TemplateResult = ReturnType<typeof __VLS_template>;
|
|
4
14
|
declare const __VLS_component: import('vue').DefineComponent<TooltipContentProps, {
|
|
5
15
|
floatingRef: import('vue').Ref<HTMLElement | null, HTMLElement | null>;
|
|
@@ -24,7 +34,10 @@ declare const __VLS_component: import('vue').DefineComponent<TooltipContentProps
|
|
|
24
34
|
showArrow: boolean;
|
|
25
35
|
interactive: boolean;
|
|
26
36
|
theme: import('./types').TooltipTheme;
|
|
27
|
-
}, {}, {}, {}, string, import('vue').ComponentProvideOptions, false,
|
|
37
|
+
}, {}, {}, {}, string, import('vue').ComponentProvideOptions, false, {
|
|
38
|
+
floatingRef: HTMLDivElement;
|
|
39
|
+
arrowRef: HTMLDivElement;
|
|
40
|
+
}, any>;
|
|
28
41
|
declare const _default: __VLS_WithTemplateSlots<typeof __VLS_component, __VLS_TemplateResult["slots"]>;
|
|
29
42
|
export default _default;
|
|
30
43
|
type __VLS_WithTemplateSlots<T, S> = T & {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tooltip-content.vue.d.ts","sourceRoot":"","sources":["../../../../../src/components/i-tooltip/src/tooltip-content.vue"],"names":[],"mappings":"AAkCA;AA8IA,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,SAAS,CAAC;AA+FnD,iBAAS,cAAc,
|
|
1
|
+
{"version":3,"file":"tooltip-content.vue.d.ts","sourceRoot":"","sources":["../../../../../src/components/i-tooltip/src/tooltip-content.vue"],"names":[],"mappings":"AAkCA;AA8IA,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,SAAS,CAAC;AA+FnD,iBAAS,cAAc;WA4FT,OAAO,IAA6B;;yBAZrB,GAAG;;;;;;;EAiB/B;AAkBD,KAAK,oBAAoB,GAAG,UAAU,CAAC,OAAO,cAAc,CAAC,CAAC;AAC9D,QAAA,MAAM,eAAe;;;;;;;;;;;;;;;;;;;;;;;;;;OAWnB,CAAC;wBACkB,uBAAuB,CAAC,OAAO,eAAe,EAAE,oBAAoB,CAAC,OAAO,CAAC,CAAC;AAAnG,wBAAoG;AAapG,KAAK,uBAAuB,CAAC,CAAC,EAAE,CAAC,IAAI,CAAC,GAAG;IACxC,QAAO;QACN,MAAM,EAAE,CAAC,CAAC;KAEV,CAAA;CACD,CAAC"}
|
|
@@ -27,7 +27,12 @@ declare function __VLS_template(): {
|
|
|
27
27
|
readonly onMouseleave?: (() => any) | undefined;
|
|
28
28
|
} & import('vue').VNodeProps & import('vue').AllowedComponentProps & import('vue').ComponentCustomProps;
|
|
29
29
|
$attrs: import('vue').Attrs;
|
|
30
|
-
$refs:
|
|
30
|
+
$refs: {
|
|
31
|
+
[x: string]: unknown;
|
|
32
|
+
} & {
|
|
33
|
+
floatingRef: HTMLDivElement;
|
|
34
|
+
arrowRef: HTMLDivElement;
|
|
35
|
+
};
|
|
31
36
|
$slots: Readonly<{
|
|
32
37
|
[name: string]: import('vue').Slot<any> | undefined;
|
|
33
38
|
}>;
|
|
@@ -98,7 +103,9 @@ declare function __VLS_template(): {
|
|
|
98
103
|
}>, "floatingRef" | ("content" | "zIndex" | "offset" | "placement" | "visible" | "reference" | "title" | "showArrow" | "interactive" | "theme")> & import('vue').ShallowUnwrapRef<{
|
|
99
104
|
floatingRef: import('vue').Ref<HTMLElement | null, HTMLElement | null>;
|
|
100
105
|
}> & {} & import('vue').ComponentCustomProperties & {} & {
|
|
101
|
-
$slots:
|
|
106
|
+
$slots: {
|
|
107
|
+
default?(_: {}): any;
|
|
108
|
+
};
|
|
102
109
|
}) | null;
|
|
103
110
|
};
|
|
104
111
|
rootEl: any;
|
|
@@ -138,7 +145,12 @@ declare const __VLS_component: import('vue').DefineComponent<TooltipProps, {
|
|
|
138
145
|
readonly onMouseleave?: (() => any) | undefined;
|
|
139
146
|
} & import('vue').VNodeProps & import('vue').AllowedComponentProps & import('vue').ComponentCustomProps;
|
|
140
147
|
$attrs: import('vue').Attrs;
|
|
141
|
-
$refs:
|
|
148
|
+
$refs: {
|
|
149
|
+
[x: string]: unknown;
|
|
150
|
+
} & {
|
|
151
|
+
floatingRef: HTMLDivElement;
|
|
152
|
+
arrowRef: HTMLDivElement;
|
|
153
|
+
};
|
|
142
154
|
$slots: Readonly<{
|
|
143
155
|
[name: string]: import('vue').Slot<any> | undefined;
|
|
144
156
|
}>;
|
|
@@ -209,7 +221,9 @@ declare const __VLS_component: import('vue').DefineComponent<TooltipProps, {
|
|
|
209
221
|
}>, "floatingRef" | ("content" | "zIndex" | "offset" | "placement" | "visible" | "reference" | "title" | "showArrow" | "interactive" | "theme")> & import('vue').ShallowUnwrapRef<{
|
|
210
222
|
floatingRef: import('vue').Ref<HTMLElement | null, HTMLElement | null>;
|
|
211
223
|
}> & {} & import('vue').ComponentCustomProperties & {} & {
|
|
212
|
-
$slots:
|
|
224
|
+
$slots: {
|
|
225
|
+
default?(_: {}): any;
|
|
226
|
+
};
|
|
213
227
|
}) | null;
|
|
214
228
|
}, any>;
|
|
215
229
|
declare const _default: __VLS_WithTemplateSlots<typeof __VLS_component, __VLS_TemplateResult["slots"]>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tooltip.vue.d.ts","sourceRoot":"","sources":["../../../../../src/components/i-tooltip/src/tooltip.vue"],"names":[],"mappings":"AAqCA;AAoTA,OAAO,KAAK,EAAuC,YAAY,EAAE,MAAM,SAAS,CAAC;AAyRjF,iBAAS,cAAc;WAuGT,OAAO,IAA6B;;yBAbrB,GAAG;yBACF,GAAG
|
|
1
|
+
{"version":3,"file":"tooltip.vue.d.ts","sourceRoot":"","sources":["../../../../../src/components/i-tooltip/src/tooltip.vue"],"names":[],"mappings":"AAqCA;AAoTA,OAAO,KAAK,EAAuC,YAAY,EAAE,MAAM,SAAS,CAAC;AAyRjF,iBAAS,cAAc;WAuGT,OAAO,IAA6B;;yBAbrB,GAAG;yBACF,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;wCAwEu+N,GAAG,8CAA8C,GAAG,yBAAyB,GAAG,6DAAmC,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;EAvD7nO;AAwBD,KAAK,oBAAoB,GAAG,UAAU,CAAC,OAAO,cAAc,CAAC,CAAC;AAC9D,QAAA,MAAM,eAAe;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;oCA8Bm/N,GAAG,8CAA8C,GAAG,yBAAyB,GAAG,6DAAmC,GAAG;;;;;;;;;;;;;;;;;;;;;;;;OArB5nO,CAAC;wBACkB,uBAAuB,CAAC,OAAO,eAAe,EAAE,oBAAoB,CAAC,OAAO,CAAC,CAAC;AAAnG,wBAAoG;AAapG,KAAK,uBAAuB,CAAC,CAAC,EAAE,CAAC,IAAI,CAAC,GAAG;IACxC,QAAO;QACN,MAAM,EAAE,CAAC,CAAC;KAEV,CAAA;CACD,CAAC"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { Ref } from 'vue';
|
|
2
|
+
/** IntersectionObserver 回调中每个条目的简化类型 */
|
|
3
|
+
export interface IntersectionEntry {
|
|
4
|
+
isIntersecting: boolean;
|
|
5
|
+
target: Element;
|
|
6
|
+
}
|
|
7
|
+
/**
|
|
8
|
+
* 轻量级 IntersectionObserver 封装
|
|
9
|
+
* 替代 @vueuse/core 的 useIntersectionObserver,仅实现当前组件库所需的最小接口。
|
|
10
|
+
*
|
|
11
|
+
* @param targetRef - 目标元素的 Vue ref
|
|
12
|
+
* @param callback - 每次交叉状态变更时触发的回调,参数与原生 API 保持一致
|
|
13
|
+
* @param options - 原生 IntersectionObserver 配置项
|
|
14
|
+
* @returns 包含 stop 方法的对象,调用后停止监听
|
|
15
|
+
*/
|
|
16
|
+
export declare function useIntersectionObserver(targetRef: Ref<HTMLElement | undefined>, callback: (entries: IntersectionEntry[]) => void, options?: IntersectionObserverInit): {
|
|
17
|
+
stop: () => void;
|
|
18
|
+
};
|
|
19
|
+
//# sourceMappingURL=use-intersection-observer.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"use-intersection-observer.d.ts","sourceRoot":"","sources":["../../../src/utils/use-intersection-observer.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,KAAK,CAAC;AAG/B,wCAAwC;AACxC,MAAM,WAAW,iBAAiB;IAChC,cAAc,EAAE,OAAO,CAAC;IACxB,MAAM,EAAE,OAAO,CAAC;CACjB;AAED;;;;;;;;GAQG;AACH,wBAAgB,uBAAuB,CACrC,SAAS,EAAE,GAAG,CAAC,WAAW,GAAG,SAAS,CAAC,EACvC,QAAQ,EAAE,CAAC,OAAO,EAAE,iBAAiB,EAAE,KAAK,IAAI,EAChD,OAAO,CAAC,EAAE,wBAAwB,GACjC;IAAE,IAAI,EAAE,MAAM,IAAI,CAAA;CAAE,CA6CtB"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@icc-grow/web-components",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.1.0",
|
|
4
4
|
"description": "ICC Grow 内部 Web 组件库",
|
|
5
5
|
"author": "ICC Grow",
|
|
6
6
|
"license": "MIT",
|
|
@@ -24,8 +24,7 @@
|
|
|
24
24
|
"files": [
|
|
25
25
|
"dist",
|
|
26
26
|
"tailwind-preset.ts",
|
|
27
|
-
"src/styles/themes/"
|
|
28
|
-
"skills/"
|
|
27
|
+
"src/styles/themes/"
|
|
29
28
|
],
|
|
30
29
|
"sideEffects": [
|
|
31
30
|
"./dist/es/style.css"
|
|
@@ -1,191 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: icc-web-components
|
|
3
|
-
description: >
|
|
4
|
-
ICC Web Components (Vue 3) 组件库使用指南。当用户需要用 Vue 3 构建中后台管理页面、
|
|
5
|
-
从 Figma 设计稿或截图生成页面代码时使用此 skill。当项目依赖了 @icc-grow/web-components,
|
|
6
|
-
或代码中出现 i-button、i-table 等 i- 前缀组件时,必须使用此 skill 确保正确的 API 用法。
|
|
7
|
-
适用于 CRUD 列表、表单详情、仪表盘等中后台业务页面的代码生成。
|
|
8
|
-
即使用户只说"做个用户管理页面"或"把这个截图转成代码"也应触发。
|
|
9
|
-
---
|
|
10
|
-
|
|
11
|
-
# ICC Web Components
|
|
12
|
-
|
|
13
|
-
`@icc-grow/web-components` — Vue 3 企业级中后台组件库。
|
|
14
|
-
|
|
15
|
-
## 技术栈
|
|
16
|
-
|
|
17
|
-
- **框架**: Vue 3 Composition API (`<script setup lang="ts">`)
|
|
18
|
-
- **样式**: Tailwind CSS v3,CSS 变量前缀 `--i-color-`
|
|
19
|
-
- **组件前缀**: `i-`,BEM 类名 `.i-button`、`.i-button__icon`、`.is-disabled`
|
|
20
|
-
- **包名**: `@icc-grow/web-components`
|
|
21
|
-
- **导入方式**: `import { IButton, ITable } from '@icc-grow/web-components'`
|
|
22
|
-
- **样式引入**: `import '@icc-grow/web-components/style.css'`
|
|
23
|
-
- **Tailwind Preset**: 消费方需在 `tailwind.config.ts` 中使用 `presets: [iccPreset]`
|
|
24
|
-
|
|
25
|
-
## 核心原则
|
|
26
|
-
|
|
27
|
-
1. **优先使用组件** — 看到按钮用 `IButton`,不要写 `<button>`;看到空状态用 `IEmpty`,不要写自定义 div
|
|
28
|
-
2. **使用语义 token** — 用 `bg-primary` / `text-danger` 等 Tailwind 主题色,禁止 `bg-blue-500` 等原生色
|
|
29
|
-
3. **Pattern-First** — 先识别页面属于哪种模式(CRUD / 表单 / 弹窗),读模式骨架后修改生成
|
|
30
|
-
4. **不随意新增颜色** — 所有颜色由 CSS 变量体系管理,遇到未定义颜色时请标记出来,不要自己新增颜色
|
|
31
|
-
5. **间距用组件** — 使用 `ISpace` 组件处理间距,而不是手动添加 margin/padding
|
|
32
|
-
|
|
33
|
-
## 页面模式选型
|
|
34
|
-
|
|
35
|
-
在生成页面代码之前,**先识别设计稿属于哪种页面模式**,然后读取对应的 pattern 文件获取完整代码骨架。
|
|
36
|
-
|
|
37
|
-
| 设计稿特征 | 模式 | 读取文件 |
|
|
38
|
-
|-----------|------|---------|
|
|
39
|
-
| 搜索条件区 + 数据表格 + 分页器 | CRUD 列表页 | → `patterns/crud-list.md` |
|
|
40
|
-
| 整页表单 + 提交/返回按钮 | 表单页 | → `patterns/form-page.md` |
|
|
41
|
-
| 弹窗或抽屉中包含表单 | 弹窗表单 | → `patterns/dialog-form.md` |
|
|
42
|
-
| 左侧树形 + 右侧内容区 | 树形管理 | → `patterns/tree-manage.md` |
|
|
43
|
-
| 顶部 Tabs 切换多个子页面 | 标签页 | → `patterns/tabs-page.md` |
|
|
44
|
-
|
|
45
|
-
> 如果一个页面包含多种模式(如 tabs-page 里的每个 tab 是 crud-list),组合使用。
|
|
46
|
-
|
|
47
|
-
## 组件选型速查
|
|
48
|
-
|
|
49
|
-
看到设计稿中的 UI 元素时,用此表找到对应组件和 API 文件。
|
|
50
|
-
|
|
51
|
-
> **"何时选"**列描述选型判断;API 细节在对应 `references/` 文件中。
|
|
52
|
-
> 两个高频组合文件覆盖 80% 场景:`i-crud-page.md`(列表页)、`i-form.md`(表单页)。
|
|
53
|
-
|
|
54
|
-
### CRUD 列表页组合 → `references/i-crud-page.md`
|
|
55
|
-
|
|
56
|
-
| UI 元素 | 组件 | 何时选 |
|
|
57
|
-
|--------|------|-------|
|
|
58
|
-
| 页面骨架 | `IPageLayout` | **每个页面必用**。标题/搜索/主体/底部四区域 |
|
|
59
|
-
| 搜索区 | `IPageSearch` | 搭配 IPageLayout 的 #search 插槽 |
|
|
60
|
-
| 按钮 | `IButton` | 所有可点击操作。实心/描边/文字三种形态 |
|
|
61
|
-
| 输入框 | `IInput` | 单行文本/密码输入 |
|
|
62
|
-
| 下拉选择 | `ISelect` | 从固定选项中选一个或多个 |
|
|
63
|
-
| 日期选择 | `IDatePicker` | 日期/周/月/年选择,可范围/多选 |
|
|
64
|
-
| 表格 | `ITable` | 数据量 <500 行的常规表格 |
|
|
65
|
-
| 分页 | `IPagination` | 表格/列表底部分页器 |
|
|
66
|
-
|
|
67
|
-
### 表单组合 → `references/i-form.md`
|
|
68
|
-
|
|
69
|
-
| UI 元素 | 组件 | 何时选 |
|
|
70
|
-
|--------|------|-------|
|
|
71
|
-
| 表单容器 | `IForm` | 包裹所有表单控件,提供校验/重置 |
|
|
72
|
-
| 表单项 | `IFormItem` | 每个字段的 label + 校验展示容器 |
|
|
73
|
-
| 输入框 | `IInput` | 单行文本/密码输入 |
|
|
74
|
-
| 数字输入 | `IInputNumber` | 纯数字,步进/边界/精度 |
|
|
75
|
-
| 多行文本 | `ITextarea` | 备注、描述输入 |
|
|
76
|
-
| 下拉选择 | `ISelect` | 从固定选项中选一个或多个 |
|
|
77
|
-
| 日期选择 | `IDatePicker` | 日期/周/月/年选择 |
|
|
78
|
-
| 开关 | `ISwitch` | 二态切换(开/关) |
|
|
79
|
-
| 复选框 | `ICheckbox` | 多选场景,单独或 Group 使用 |
|
|
80
|
-
| 单选框 | `IRadio` | 单选,选项 ≤5 个时优先用 Radio |
|
|
81
|
-
|
|
82
|
-
### 其他组件(按需读取对应文件)
|
|
83
|
-
|
|
84
|
-
| UI 元素 | 组件 | 何时选 | API 文件 |
|
|
85
|
-
|--------|------|-------|---------|
|
|
86
|
-
| 图标 | `IIcon` | iconfont 或自定义 SVG | `i-icon.md` |
|
|
87
|
-
| 标签 | `ITag` / `ITagGroup` | 状态标签、分类标签 | `i-tag.md` |
|
|
88
|
-
| 角标 | `IBadge` | 右上角数字/红点 | `i-badge.md` |
|
|
89
|
-
| 头像 | `IAvatar` / `IAvatarGroup` | 用户头像,可重叠 | `i-avatar.md` |
|
|
90
|
-
| 过渡动画 | `ITransition` | v-if/v-show 过渡 | `i-transition.md` |
|
|
91
|
-
| 树形选择 | `ITreeSelect` | 树形层级数据选择 | `i-tree-select.md` |
|
|
92
|
-
| 时间选择 | `ITimePicker` | 时:分:秒 滚轮 | `i-time-picker.md` |
|
|
93
|
-
| 颜色选择 | `IColorPicker` | 颜色值输入 | `i-color-picker.md` |
|
|
94
|
-
| 文件上传 | `IUpload` | 文件/图片上传 | `i-upload.md` |
|
|
95
|
-
| 大数据表格 | `IVirtualTable` | >500 行,虚拟滚动 | `i-virtual-table.md` |
|
|
96
|
-
| 树形控件 | `ITree` | 层级数据展示/选择 | `i-tree.md` |
|
|
97
|
-
| 卡片 | `ICard` | 内容分区容器 | `i-card.md` |
|
|
98
|
-
| 图片 | `IImage` / `IImageGroup` | 图片展示 + 预览 | `i-image.md` |
|
|
99
|
-
| 空状态 | `IEmpty` | 无数据占位。⚠️ 不要自己写 div | `i-empty.md` |
|
|
100
|
-
| 进度条 | `IProgress` | 线性进度展示 | `i-progress.md` |
|
|
101
|
-
| 对话框 | `IDialog` / `IDrawer` | 模态弹窗 / 侧边面板 | `i-dialog.md` |
|
|
102
|
-
| 反馈服务 | `IConfirm` / `IMessage` / `INotification` | ⚠️ 服务调用,不是组件 | `i-feedback.md` |
|
|
103
|
-
| 浮层提示 | `IPopover` / `ITooltip` | 弹出气泡 / 文字提示 | `i-popover.md` |
|
|
104
|
-
| 加载中 | `ILoading` | v-loading 指令或服务 | `i-loading.md` |
|
|
105
|
-
| 标签页 | `ITabs` | Tab 切换,数据驱动 | `i-tabs.md` |
|
|
106
|
-
| 下拉菜单 | `IDropdown` | 按钮触发的操作菜单 | `i-dropdown.md` |
|
|
107
|
-
| 步骤条 | `ISteps` | 多步骤流程引导 | `i-steps.md` |
|
|
108
|
-
| 侧边菜单 | `IMenu` | 侧边栏导航 | `i-menu.md` |
|
|
109
|
-
| 间距 | `ISpace` | 间距排列。⚠️ 不要手动 gap/margin | `i-space.md` |
|
|
110
|
-
| 滚动条 | `IScrollbar` | 自定义美化滚动条 | `i-scrollbar.md` |
|
|
111
|
-
| 虚拟列表 | `IVirtualList` | 海量列表渲染 | `i-virtual-list.md` |
|
|
112
|
-
| 拖拽 | `IDraggable` / `ISortable` | 拖拽移动 / 排序 | `i-draggable.md` |
|
|
113
|
-
|
|
114
|
-
## Critical Rules
|
|
115
|
-
|
|
116
|
-
> 生成代码前必须过一遍此清单。详细的 Incorrect/Correct 代码对见各 rules/ 文件。
|
|
117
|
-
|
|
118
|
-
### 样式红线 → `rules/styling.md`
|
|
119
|
-
|
|
120
|
-
- ❌ 禁止硬编码颜色(`#xxx`、`rgb()`)和 Tailwind 原生色(`bg-blue-500`)→ 用 `bg-primary` 等语义 token
|
|
121
|
-
- ❌ 禁止手动 `gap-*` / `mr-*` 做间距 → 用 `ISpace` 组件
|
|
122
|
-
- ❌ 禁止自行新增 CSS 变量 → 遇到未定义颜色标记出来,不要自己新增
|
|
123
|
-
- ❌ 禁止 `style` 内联写颜色
|
|
124
|
-
|
|
125
|
-
### 表单红线 → `rules/forms.md`
|
|
126
|
-
|
|
127
|
-
- ❌ 禁止裸用 `<input>` / `<select>` 等原生元素 → 用 `IInput` / `ISelect` 等组件
|
|
128
|
-
- ❌ 禁止不包裹 `IForm` 直接用表单控件 → 整个表单区域用 `IForm` + `IFormItem`
|
|
129
|
-
- ❌ 禁止手写搜索区布局 → 用 `IPageSearch` 组件
|
|
130
|
-
- ❌ 表单输入框必须限制宽度(`w-[400px]` 等),不要全宽
|
|
131
|
-
|
|
132
|
-
### 组合红线 → `rules/composition.md`
|
|
133
|
-
|
|
134
|
-
- ❌ 禁止用 `IDialog` 做简单确认弹窗 → 用 `IConfirm` 服务调用
|
|
135
|
-
- ❌ 禁止自己写 toast / message → 用 `IMessage.success()` 等
|
|
136
|
-
- ❌ 禁止自己写空状态 div → 用 `IEmpty` 组件
|
|
137
|
-
- ❌ 禁止自己写 loading 遮罩 → 用 `v-loading` 指令
|
|
138
|
-
- ❌ 弹窗表单必须抽离为独立子组件,不要混在列表页中
|
|
139
|
-
|
|
140
|
-
## 代码约定
|
|
141
|
-
|
|
142
|
-
### 基本格式
|
|
143
|
-
|
|
144
|
-
```vue
|
|
145
|
-
<template>
|
|
146
|
-
<!-- 模板内容 -->
|
|
147
|
-
</template>
|
|
148
|
-
|
|
149
|
-
<script setup lang="ts">
|
|
150
|
-
import { ref, reactive } from 'vue'
|
|
151
|
-
import { IButton, ITable, IMessage } from '@icc-grow/web-components'
|
|
152
|
-
import type { TableColumn } from '@icc-grow/web-components'
|
|
153
|
-
|
|
154
|
-
// 类型定义
|
|
155
|
-
interface UserItem {
|
|
156
|
-
id: number
|
|
157
|
-
name: string
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
// 响应式数据
|
|
161
|
-
const loading = ref(false)
|
|
162
|
-
const tableData = ref<UserItem[]>([])
|
|
163
|
-
|
|
164
|
-
// 方法
|
|
165
|
-
function handleSearch() {
|
|
166
|
-
// ...
|
|
167
|
-
}
|
|
168
|
-
</script>
|
|
169
|
-
```
|
|
170
|
-
|
|
171
|
-
### 关键约定
|
|
172
|
-
|
|
173
|
-
- **组件全部按需导入**,不要整体导入
|
|
174
|
-
- **类型使用 `import type`** 区分值/类型导入
|
|
175
|
-
- **Props 类型从组件库导入**:`import type { TableColumn } from '@icc-grow/web-components'`
|
|
176
|
-
- **服务型组件直接调用**:`IMessage.success('操作成功')`、`IConfirm({...})`
|
|
177
|
-
- **v-model 双向绑定**:`v-model:visible`、`v-model:currentPage`
|
|
178
|
-
- **表格列用 `columns` 数组配置**,不用子组件
|
|
179
|
-
|
|
180
|
-
## 生成页面工作流
|
|
181
|
-
|
|
182
|
-
> ⚠️ **不要一上来就读所有 references/ 文件**。patterns/ 骨架已包含最常见的组件用法,references/ 只在骨架不够时按需查阅。
|
|
183
|
-
|
|
184
|
-
```
|
|
185
|
-
1. 识别页面模式 → 查【页面模式选型】→ 读对应 patterns/ 骨架(主力文档,覆盖 80% 用法)
|
|
186
|
-
2. 对照【组件选型速查】→ 确认骨架中没覆盖到的组件
|
|
187
|
-
3. 仅当遇到骨架未涉及的组件或不常见的 Prop → 读 references/ 中对应分类文件(不要全读)
|
|
188
|
-
4. 过一遍【Critical Rules】清单 → 有疑问时读 rules/ 详细正反例
|
|
189
|
-
5. 生成 <script setup lang="ts"> 格式的 .vue 文件
|
|
190
|
-
```
|
|
191
|
-
|
|
@@ -1,195 +0,0 @@
|
|
|
1
|
-
# Pattern: CRUD 列表页
|
|
2
|
-
|
|
3
|
-
> 搜索条件区 + 数据表格 + 分页器
|
|
4
|
-
|
|
5
|
-
## ⚠️ 类型文件要求
|
|
6
|
-
|
|
7
|
-
**所有 TS 类型(`interface`、`type`、常量映射)必须新建 `types.ts` 文件**,与 `.vue` 文件同目录放置。禁止在 `.vue` 文件中内联定义业务类型。
|
|
8
|
-
|
|
9
|
-
```
|
|
10
|
-
views/
|
|
11
|
-
your-page/
|
|
12
|
-
index.vue ← 页面组件
|
|
13
|
-
types.ts ← 业务类型定义
|
|
14
|
-
```
|
|
15
|
-
|
|
16
|
-
### types.ts 骨架
|
|
17
|
-
|
|
18
|
-
```typescript
|
|
19
|
-
// ---- 搜索查询参数 ----
|
|
20
|
-
export interface XxxListQuery {
|
|
21
|
-
pageIndex: number;
|
|
22
|
-
pageSize: number;
|
|
23
|
-
name?: string;
|
|
24
|
-
status?: number;
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
// ---- 列表行数据 ----
|
|
28
|
-
export interface XxxListVO {
|
|
29
|
-
id: number;
|
|
30
|
-
name: string;
|
|
31
|
-
status: number;
|
|
32
|
-
createdAt: string;
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
// ---- 常量映射(使用 as const 保证类型安全) ----
|
|
36
|
-
export const STATUS_MAP = {
|
|
37
|
-
1: '启用',
|
|
38
|
-
0: '禁用',
|
|
39
|
-
} as const;
|
|
40
|
-
```
|
|
41
|
-
|
|
42
|
-
---
|
|
43
|
-
|
|
44
|
-
## 完整骨架(index.vue)
|
|
45
|
-
|
|
46
|
-
```vue
|
|
47
|
-
<template>
|
|
48
|
-
<IPageLayout title="Xxx管理">
|
|
49
|
-
<!-- 标题栏右侧操作 -->
|
|
50
|
-
<template #header-action>
|
|
51
|
-
<IButton icon="icon-add" @click="handleAdd">新增</IButton>
|
|
52
|
-
</template>
|
|
53
|
-
|
|
54
|
-
<!-- 搜索区 -->
|
|
55
|
-
<template #search>
|
|
56
|
-
<IInput v-model="searchQuery.name" placeholder="名称" clearable />
|
|
57
|
-
<ISelect v-model="searchQuery.status" :options="statusOptions" placeholder="状态" clearable />
|
|
58
|
-
<IButton @click="handleSearch">查询</IButton>
|
|
59
|
-
<IButton type="black" plain @click="handleReset">重置</IButton>
|
|
60
|
-
</template>
|
|
61
|
-
|
|
62
|
-
<!-- 表格 -->
|
|
63
|
-
<ITable v-loading="loading" :data="tableData" :columns="tableColumns" row-key="id">
|
|
64
|
-
<!-- 操作列 -->
|
|
65
|
-
<template #action="{ row }">
|
|
66
|
-
<ISpace :size="4">
|
|
67
|
-
<IButton text @click="handleEdit(row)">编辑</IButton>
|
|
68
|
-
<IButton text type="danger" @click="handleDelete(row)">删除</IButton>
|
|
69
|
-
</ISpace>
|
|
70
|
-
</template>
|
|
71
|
-
</ITable>
|
|
72
|
-
|
|
73
|
-
<!-- 分页 -->
|
|
74
|
-
<template #footer>
|
|
75
|
-
<IPagination
|
|
76
|
-
v-model:current-page="searchQuery.pageIndex"
|
|
77
|
-
v-model:page-size="searchQuery.pageSize"
|
|
78
|
-
:total="pageTotal"
|
|
79
|
-
@change="fetchList"
|
|
80
|
-
/>
|
|
81
|
-
</template>
|
|
82
|
-
</IPageLayout>
|
|
83
|
-
</template>
|
|
84
|
-
|
|
85
|
-
<script setup lang="ts">
|
|
86
|
-
import { ref, reactive, onMounted } from 'vue';
|
|
87
|
-
import { IPageLayout, ITable, IPagination, IButton, IInput, ISelect, ISpace, IConfirm, IMessage } from '@icc-grow/web-components';
|
|
88
|
-
import type { ConfirmResult, TableColumn } from '@icc-grow/web-components';
|
|
89
|
-
import type { XxxListQuery, XxxListVO } from './types';
|
|
90
|
-
import { STATUS_MAP } from './types';
|
|
91
|
-
|
|
92
|
-
// ---- 选项数据(从映射对象自动生成) ----
|
|
93
|
-
const statusOptions = Object.entries(STATUS_MAP).map(([value, label]) => ({
|
|
94
|
-
label,
|
|
95
|
-
value: Number(value),
|
|
96
|
-
}));
|
|
97
|
-
|
|
98
|
-
// ---- 总数 ----
|
|
99
|
-
const pageTotal = ref(0);
|
|
100
|
-
|
|
101
|
-
// ---- 搜索条件(使用泛型约束) ----
|
|
102
|
-
const searchQuery = reactive<XxxListQuery>({
|
|
103
|
-
pageIndex: 1,
|
|
104
|
-
pageSize: 20,
|
|
105
|
-
name: '',
|
|
106
|
-
status: undefined,
|
|
107
|
-
});
|
|
108
|
-
|
|
109
|
-
// ---- 表格 ----
|
|
110
|
-
const loading = ref(false);
|
|
111
|
-
const tableData = ref<XxxListVO[]>([]);
|
|
112
|
-
|
|
113
|
-
const tableColumns: TableColumn<XxxListVO>[] = [
|
|
114
|
-
{ label: '名称', prop: 'name' },
|
|
115
|
-
{
|
|
116
|
-
label: '状态',
|
|
117
|
-
prop: 'status',
|
|
118
|
-
formatter: (_row, _col, cellValue) => STATUS_MAP[cellValue as keyof typeof STATUS_MAP] || '未知',
|
|
119
|
-
},
|
|
120
|
-
{ label: '创建时间', prop: 'createdAt' },
|
|
121
|
-
{ label: '操作', prop: 'action', fixed: 'right' },
|
|
122
|
-
];
|
|
123
|
-
|
|
124
|
-
// ---- 方法 ----
|
|
125
|
-
/** 获取列表数据 */
|
|
126
|
-
async function fetchList() {
|
|
127
|
-
loading.value = true;
|
|
128
|
-
try {
|
|
129
|
-
// TODO: 替换为真实 API 调用
|
|
130
|
-
const res = await api.getList(searchQuery);
|
|
131
|
-
tableData.value = res.list;
|
|
132
|
-
pageTotal.value = res.total;
|
|
133
|
-
} catch (error) {
|
|
134
|
-
console.error(`获取列表失败: ${error}`);
|
|
135
|
-
} finally {
|
|
136
|
-
loading.value = false;
|
|
137
|
-
}
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
/** 搜索(重置页码为 1) */
|
|
141
|
-
function handleSearch() {
|
|
142
|
-
searchQuery.pageIndex = 1;
|
|
143
|
-
fetchList();
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
/** 重置搜索条件 */
|
|
147
|
-
function handleReset() {
|
|
148
|
-
Object.assign(searchQuery, {
|
|
149
|
-
name: '',
|
|
150
|
-
status: undefined,
|
|
151
|
-
});
|
|
152
|
-
handleSearch();
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
function handleAdd() {
|
|
156
|
-
// TODO: 打开新增弹窗
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
function handleEdit(row: XxxListVO) {
|
|
160
|
-
// TODO: 打开编辑弹窗
|
|
161
|
-
}
|
|
162
|
-
|
|
163
|
-
function handleDelete(row: XxxListVO) {
|
|
164
|
-
IConfirm.error({
|
|
165
|
-
title: '确认删除',
|
|
166
|
-
content: `确定删除「${row.name}」吗?删除后不可恢复。`,
|
|
167
|
-
})
|
|
168
|
-
.then(async (value: ConfirmResult) => {
|
|
169
|
-
if (value === 'confirm') {
|
|
170
|
-
// TODO: 替换为真实 API 调用
|
|
171
|
-
await api.delete(row.id);
|
|
172
|
-
IMessage.success('删除成功');
|
|
173
|
-
fetchList();
|
|
174
|
-
}else{
|
|
175
|
-
IMessage.error('删除失败');
|
|
176
|
-
}
|
|
177
|
-
})
|
|
178
|
-
}
|
|
179
|
-
|
|
180
|
-
onMounted(() => fetchList());
|
|
181
|
-
</script>
|
|
182
|
-
```
|
|
183
|
-
|
|
184
|
-
## 关键要点
|
|
185
|
-
|
|
186
|
-
1. **类型拆分**:所有 `interface`、`type`、常量映射必须放在同目录的 `types.ts` 中,`.vue` 文件通过 `import type` 引入
|
|
187
|
-
2. **IPageLayout** 提供插槽:`#header-action` / `#search` / `default` / `#footer`
|
|
188
|
-
3. **常量映射 → 选项**:使用 `as const` 定义映射对象,通过 `Object.entries()` 自动生成下拉选项,避免手动维护两份数据
|
|
189
|
-
4. **列格式化**:枚举值展示优先使用 `TableColumn.formatter`,避免为简单映射单独写 `<template #slot>` 插槽;仅当需要渲染组件(如 `ITag`、`IButton`)时才使用插槽
|
|
190
|
-
5. **搜索条件**:`searchQuery` 使用 `reactive<QueryType>()` 泛型约束,确保类型安全
|
|
191
|
-
6. **分页属性**:使用 `v-model:current-page` / `v-model:page-size`(kebab-case)
|
|
192
|
-
7. **表格操作列**:使用 `text` 按钮 + `ISpace :size="4"` 间隔
|
|
193
|
-
8. **删除确认**:使用 `IConfirm.error()` + `.then(value => if(value === 'confirm'))` 模式,搭配 `.catch()` 处理取消
|
|
194
|
-
9. **重置按钮**:使用 `type="black" plain` 样式,重置时将 `pageIndex` 重置为 1
|
|
195
|
-
10. **API 占位**:使用 `// TODO: 替换为真实 API 调用` 标注,禁止在模板中编写 mockApi
|