@manohub/app-kit 0.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.
Files changed (103) hide show
  1. package/CONTRACT.md +592 -0
  2. package/README.md +27 -0
  3. package/dist/atoms/app-button.d.ts +90 -0
  4. package/dist/atoms/app-button.js +74 -0
  5. package/dist/atoms/app-checkbox.d.ts +86 -0
  6. package/dist/atoms/app-checkbox.js +78 -0
  7. package/dist/atoms/app-icon-button.d.ts +79 -0
  8. package/dist/atoms/app-icon-button.js +54 -0
  9. package/dist/atoms/app-input.d.ts +118 -0
  10. package/dist/atoms/app-input.js +83 -0
  11. package/dist/atoms/app-layout.d.ts +348 -0
  12. package/dist/atoms/app-layout.js +102 -0
  13. package/dist/atoms/app-notice.d.ts +90 -0
  14. package/dist/atoms/app-notice.js +59 -0
  15. package/dist/atoms/app-query-state.d.ts +191 -0
  16. package/dist/atoms/app-query-state.js +161 -0
  17. package/dist/atoms/app-radio-card.d.ts +80 -0
  18. package/dist/atoms/app-radio-card.js +72 -0
  19. package/dist/atoms/app-search-box.d.ts +80 -0
  20. package/dist/atoms/app-search-box.js +64 -0
  21. package/dist/atoms/app-select.d.ts +106 -0
  22. package/dist/atoms/app-select.js +74 -0
  23. package/dist/atoms/app-switch.d.ts +72 -0
  24. package/dist/atoms/app-switch.js +66 -0
  25. package/dist/atoms/app-textarea.d.ts +89 -0
  26. package/dist/atoms/app-textarea.js +61 -0
  27. package/dist/atoms/app-tooltip.d.ts +70 -0
  28. package/dist/atoms/app-tooltip.js +48 -0
  29. package/dist/atoms/atoms.css +679 -0
  30. package/dist/atoms/state-illustration.d.ts +21 -0
  31. package/dist/atoms/state-illustration.js +101 -0
  32. package/dist/components/app-badge.d.ts +72 -0
  33. package/dist/components/app-badge.js +40 -0
  34. package/dist/components/app-dialog.d.ts +164 -0
  35. package/dist/components/app-dialog.js +121 -0
  36. package/dist/components/app-filter.d.ts +173 -0
  37. package/dist/components/app-filter.js +200 -0
  38. package/dist/components/app-form.d.ts +446 -0
  39. package/dist/components/app-form.js +159 -0
  40. package/dist/components/app-pagination.d.ts +78 -0
  41. package/dist/components/app-pagination.js +57 -0
  42. package/dist/components/app-panel.d.ts +399 -0
  43. package/dist/components/app-panel.js +213 -0
  44. package/dist/components/app-section.d.ts +68 -0
  45. package/dist/components/app-section.js +28 -0
  46. package/dist/components/app-steps.d.ts +115 -0
  47. package/dist/components/app-steps.js +108 -0
  48. package/dist/components/app-table.d.ts +415 -0
  49. package/dist/components/app-table.js +479 -0
  50. package/dist/components/app-tabs.d.ts +58 -0
  51. package/dist/components/app-tabs.js +54 -0
  52. package/dist/components/app-tree.d.ts +273 -0
  53. package/dist/components/app-tree.js +222 -0
  54. package/dist/components/components.css +597 -0
  55. package/dist/composables/use-client-pagination.d.ts +39 -0
  56. package/dist/composables/use-client-pagination.js +29 -0
  57. package/dist/entry/create-query-client.d.ts +8 -0
  58. package/dist/entry/create-query-client.js +14 -0
  59. package/dist/entry/create-sub-app.d.ts +45 -0
  60. package/dist/entry/create-sub-app.js +110 -0
  61. package/dist/entry/index.d.ts +4 -0
  62. package/dist/entry/index.js +12 -0
  63. package/dist/entry/initial-guard.d.ts +12 -0
  64. package/dist/entry/initial-guard.js +23 -0
  65. package/dist/index.d.ts +37 -0
  66. package/dist/index.js +67 -0
  67. package/dist/providers/setup-i18n.d.ts +19 -0
  68. package/dist/providers/setup-i18n.js +24 -0
  69. package/dist/services/app-container.d.ts +15 -0
  70. package/dist/services/app-container.js +7 -0
  71. package/dist/services/app-context.d.ts +4 -0
  72. package/dist/services/app-context.js +17 -0
  73. package/dist/services/index.d.ts +5 -0
  74. package/dist/services/loading.d.ts +16 -0
  75. package/dist/services/loading.js +27 -0
  76. package/dist/services/message-box.d.ts +84 -0
  77. package/dist/services/message-box.js +65 -0
  78. package/dist/services/modal.d.ts +51 -0
  79. package/dist/services/modal.js +51 -0
  80. package/dist/services/notify.d.ts +24 -0
  81. package/dist/services/notify.js +22 -0
  82. package/dist/shell/AppShell.d.ts +139 -0
  83. package/dist/shell/AppShell.js +171 -0
  84. package/dist/shell/shell.css +153 -0
  85. package/dist/styles/farris-bridge.css +129 -0
  86. package/dist/styles/index.css +16 -0
  87. package/dist/styles/markdown.css +85 -0
  88. package/dist/styles/reset.css +74 -0
  89. package/dist/styles/tokens.css +111 -0
  90. package/lint/__tests__/fixtures/clean-src/styles.css +14 -0
  91. package/lint/__tests__/fixtures/clean-src/views/good-page.tsx +15 -0
  92. package/lint/__tests__/fixtures/violations-src/styles.css +18 -0
  93. package/lint/__tests__/fixtures/violations-src/views/bad-page.tsx +38 -0
  94. package/lint/__tests__/fixtures/violations-src/views/no-shell-page.tsx +4 -0
  95. package/lint/__tests__/guardrails.spec.mjs +144 -0
  96. package/lint/component-audit.mjs +131 -0
  97. package/lint/guardrails.config.schema.json +74 -0
  98. package/lint/pre-commit.sample +26 -0
  99. package/lint/run-all.mjs +59 -0
  100. package/lint/shared.mjs +347 -0
  101. package/lint/structure-audit.mjs +254 -0
  102. package/lint/style-audit.mjs +200 -0
  103. package/package.json +78 -0
@@ -0,0 +1,191 @@
1
+ import { type PropType, type SlotsType, type VNodeChild } from 'vue';
2
+ /** 三态种类(插画 / 操作区插槽会收到它,便于按状态给不同内容) */
3
+ export type AppQueryStateKind = 'empty' | 'error';
4
+ export interface AppQueryStateProps {
5
+ /** 加载中 → 骨架屏 */
6
+ loading?: boolean;
7
+ /** 错误态(truthy 即触发) */
8
+ error?: unknown;
9
+ /** 空态:true 用默认文案,string 作为空态标题 */
10
+ empty?: boolean | string;
11
+ /** 空态副文案 */
12
+ emptyDescription?: string;
13
+ /** 错误态标题(默认「加载失败」) */
14
+ errorTitle?: string;
15
+ /** 错误态副文案 */
16
+ errorDescription?: string;
17
+ /** 自定义插画资源 URL(不传用内建插画;整幅自定义图形请用 `illustration` 插槽) */
18
+ illustration?: string;
19
+ /** 空态操作按钮(如「新建」);与 `onEmptyAction` 同时传才渲染 */
20
+ emptyActionText?: string;
21
+ onEmptyAction?: () => void;
22
+ /** 错误态操作按钮(如「重试」);与 `onErrorAction` 同时传才渲染 */
23
+ errorActionText?: string;
24
+ onErrorAction?: () => void;
25
+ /** 骨架屏条数 */
26
+ skeletonRows?: number;
27
+ /** 骨架屏条高(px) */
28
+ skeletonRowHeight?: number;
29
+ }
30
+ /**
31
+ * loading / empty / error 三态容器(自建,替代各 app 手拼的 empty-state + skeleton)。
32
+ *
33
+ * 三态互斥,优先级 loading > error > empty > default 插槽。
34
+ *
35
+ * ## 视觉构成(三态非加载态一致)
36
+ *
37
+ * **插画 → 主文案 → 副文案 → 操作区**,整块垂直居中、留白由令牌给(`.ak-state`)。
38
+ * 插画默认取内建件(空态「+」/ 错误态「!」同族箱体),可用 `illustration` 属性换成图片资源、
39
+ * 或用 `illustration` 插槽整幅自定义;操作区同理有 `actions` 插槽。
40
+ *
41
+ * 文案 props 可覆盖(i18n 由消费方传 t() 结果;app-kit 统一 i18n 内置文案后续批次接入)。
42
+ *
43
+ * @example 空态带 CTA、错误态带重试
44
+ * <AppQueryState
45
+ * loading={isLoading}
46
+ * error={loadError.value}
47
+ * errorActionText={t('common.retry')}
48
+ * onErrorAction={() => reload()}
49
+ * empty={topics.length === 0}
50
+ * empty={t('topic.empty')}
51
+ * emptyDescription={t('topic.emptyDescription')}
52
+ * emptyActionText={t('topic.create')}
53
+ * onEmptyAction={() => openCreate()}>
54
+ * <TopicGrid topics={topics} />
55
+ * </AppQueryState>
56
+ */
57
+ export declare const AppQueryState: import("vue").DefineComponent<import("vue").ExtractPropTypes<{
58
+ loading: {
59
+ type: BooleanConstructor;
60
+ default: boolean;
61
+ };
62
+ error: {
63
+ type: PropType<any>;
64
+ default: undefined;
65
+ };
66
+ empty: {
67
+ type: PropType<boolean | string>;
68
+ default: boolean;
69
+ };
70
+ emptyDescription: {
71
+ type: StringConstructor;
72
+ default: undefined;
73
+ };
74
+ errorTitle: {
75
+ type: StringConstructor;
76
+ default: string;
77
+ };
78
+ errorDescription: {
79
+ type: StringConstructor;
80
+ default: undefined;
81
+ };
82
+ illustration: {
83
+ type: StringConstructor;
84
+ default: undefined;
85
+ };
86
+ emptyActionText: {
87
+ type: StringConstructor;
88
+ default: undefined;
89
+ };
90
+ onEmptyAction: {
91
+ type: PropType<() => void>;
92
+ default: undefined;
93
+ };
94
+ errorActionText: {
95
+ type: StringConstructor;
96
+ default: undefined;
97
+ };
98
+ onErrorAction: {
99
+ type: PropType<() => void>;
100
+ default: undefined;
101
+ };
102
+ skeletonRows: {
103
+ type: NumberConstructor;
104
+ default: number;
105
+ };
106
+ skeletonRowHeight: {
107
+ type: NumberConstructor;
108
+ default: number;
109
+ };
110
+ }>, () => string | number | boolean | import("vue").VNode<import("vue").RendererNode, import("vue").RendererElement, {
111
+ [key: string]: any;
112
+ }> | import("vue").VNodeArrayChildren | null, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<import("vue").ExtractPropTypes<{
113
+ loading: {
114
+ type: BooleanConstructor;
115
+ default: boolean;
116
+ };
117
+ error: {
118
+ type: PropType<any>;
119
+ default: undefined;
120
+ };
121
+ empty: {
122
+ type: PropType<boolean | string>;
123
+ default: boolean;
124
+ };
125
+ emptyDescription: {
126
+ type: StringConstructor;
127
+ default: undefined;
128
+ };
129
+ errorTitle: {
130
+ type: StringConstructor;
131
+ default: string;
132
+ };
133
+ errorDescription: {
134
+ type: StringConstructor;
135
+ default: undefined;
136
+ };
137
+ illustration: {
138
+ type: StringConstructor;
139
+ default: undefined;
140
+ };
141
+ emptyActionText: {
142
+ type: StringConstructor;
143
+ default: undefined;
144
+ };
145
+ onEmptyAction: {
146
+ type: PropType<() => void>;
147
+ default: undefined;
148
+ };
149
+ errorActionText: {
150
+ type: StringConstructor;
151
+ default: undefined;
152
+ };
153
+ onErrorAction: {
154
+ type: PropType<() => void>;
155
+ default: undefined;
156
+ };
157
+ skeletonRows: {
158
+ type: NumberConstructor;
159
+ default: number;
160
+ };
161
+ skeletonRowHeight: {
162
+ type: NumberConstructor;
163
+ default: number;
164
+ };
165
+ }>> & Readonly<{}>, {
166
+ loading: boolean;
167
+ error: any;
168
+ empty: string | boolean;
169
+ emptyDescription: string;
170
+ errorTitle: string;
171
+ errorDescription: string;
172
+ illustration: string;
173
+ emptyActionText: string;
174
+ onEmptyAction: () => void;
175
+ errorActionText: string;
176
+ onErrorAction: () => void;
177
+ skeletonRows: number;
178
+ skeletonRowHeight: number;
179
+ }, SlotsType<{
180
+ default?: () => VNodeChild;
181
+ empty?: () => VNodeChild;
182
+ error?: () => VNodeChild;
183
+ /** 自定义插画;入参为当前状态种类 */
184
+ illustration?: (ctx: {
185
+ kind: AppQueryStateKind;
186
+ }) => VNodeChild;
187
+ /** 自定义操作区(按钮组);不传则按 *ActionText / on*Action 渲染单个主按钮 */
188
+ actions?: (ctx: {
189
+ kind: AppQueryStateKind;
190
+ }) => VNodeChild;
191
+ }>, {}, {}, string, import("vue").ComponentProvideOptions, true, {}, any>;
@@ -0,0 +1,161 @@
1
+ import { defineComponent, createVNode, Fragment, isVNode } from "vue";
2
+ import { AppButton } from "./app-button.js";
3
+ import { AppStateIllustrationError, AppStateIllustrationEmpty } from "./state-illustration.js";
4
+ function _isSlot(s) {
5
+ return typeof s === "function" || Object.prototype.toString.call(s) === "[object Object]" && !isVNode(s);
6
+ }
7
+ const AppQueryState = /* @__PURE__ */ defineComponent({
8
+ name: "AppQueryState",
9
+ inheritAttrs: false,
10
+ props: {
11
+ loading: {
12
+ type: Boolean,
13
+ default: false
14
+ },
15
+ /*
16
+ * truthy 即触发错误态;type 用构造器数组承载 unknown(`type: null` 在 vue-tsc 下被推断为 undefined)。
17
+ * **`String` 必须排在 `Boolean` 前**:否则 Vue 的布尔转换会把空字符串 `''` 转成 `true`,
18
+ * 成功态(`error={''}`)也会被判成错误态。
19
+ */
20
+ error: {
21
+ type: [String, Boolean, Object, Number, Array],
22
+ default: void 0
23
+ },
24
+ empty: {
25
+ type: [Boolean, String],
26
+ default: false
27
+ },
28
+ emptyDescription: {
29
+ type: String,
30
+ default: void 0
31
+ },
32
+ errorTitle: {
33
+ type: String,
34
+ default: "加载失败"
35
+ },
36
+ errorDescription: {
37
+ type: String,
38
+ default: void 0
39
+ },
40
+ illustration: {
41
+ type: String,
42
+ default: void 0
43
+ },
44
+ emptyActionText: {
45
+ type: String,
46
+ default: void 0
47
+ },
48
+ onEmptyAction: {
49
+ type: Function,
50
+ default: void 0
51
+ },
52
+ errorActionText: {
53
+ type: String,
54
+ default: void 0
55
+ },
56
+ onErrorAction: {
57
+ type: Function,
58
+ default: void 0
59
+ },
60
+ skeletonRows: {
61
+ type: Number,
62
+ default: 6
63
+ },
64
+ skeletonRowHeight: {
65
+ type: Number,
66
+ default: 40
67
+ }
68
+ },
69
+ slots: Object,
70
+ setup(props, {
71
+ slots,
72
+ attrs
73
+ }) {
74
+ const renderIllustration = (kind) => {
75
+ if (slots.illustration) {
76
+ return slots.illustration({
77
+ kind
78
+ });
79
+ }
80
+ if (props.illustration) {
81
+ return createVNode("img", {
82
+ "class": "ak-state-illustration",
83
+ "src": props.illustration,
84
+ "alt": ""
85
+ }, null);
86
+ }
87
+ return kind === "error" ? createVNode(AppStateIllustrationError, null, null) : createVNode(AppStateIllustrationEmpty, null, null);
88
+ };
89
+ const renderActions = (kind, actionText, onAction) => {
90
+ if (slots.actions) {
91
+ return slots.actions({
92
+ kind
93
+ });
94
+ }
95
+ if (!actionText || !onAction) {
96
+ return null;
97
+ }
98
+ return createVNode(AppButton, {
99
+ "tone": "primary",
100
+ "onClick": onAction
101
+ }, _isSlot(actionText) ? actionText : {
102
+ default: () => [actionText]
103
+ });
104
+ };
105
+ const renderState = (config) => {
106
+ const actions = renderActions(config.kind, config.actionText, config.onAction);
107
+ return createVNode("div", {
108
+ "class": "ak-state",
109
+ "style": attrs.style
110
+ }, [config.slotFn ? config.slotFn() : createVNode(Fragment, null, [renderIllustration(config.kind), createVNode("p", {
111
+ "class": "ak-state-title"
112
+ }, [config.title]), config.description ? createVNode("p", {
113
+ "class": "ak-state-description"
114
+ }, [config.description]) : null, actions ? createVNode("div", {
115
+ "class": "ak-state-actions"
116
+ }, [actions]) : null])]);
117
+ };
118
+ return () => {
119
+ if (props.loading) {
120
+ return createVNode("div", {
121
+ "class": "ak-state"
122
+ }, [createVNode("div", {
123
+ "class": "ak-state-skeletons"
124
+ }, [Array.from({
125
+ length: props.skeletonRows
126
+ }).map((_, i) => createVNode("div", {
127
+ "key": i,
128
+ "class": "ak-skeleton-block",
129
+ "style": {
130
+ height: `${props.skeletonRowHeight}px`
131
+ }
132
+ }, null))])]);
133
+ }
134
+ if (props.error) {
135
+ return renderState({
136
+ kind: "error",
137
+ title: props.errorTitle,
138
+ description: props.errorDescription,
139
+ slotFn: slots.error,
140
+ actionText: props.errorActionText,
141
+ onAction: props.onErrorAction
142
+ });
143
+ }
144
+ if (props.empty) {
145
+ const title = typeof props.empty === "string" ? props.empty : "暂无数据";
146
+ return renderState({
147
+ kind: "empty",
148
+ title,
149
+ description: props.emptyDescription,
150
+ slotFn: slots.empty,
151
+ actionText: props.emptyActionText,
152
+ onAction: props.onEmptyAction
153
+ });
154
+ }
155
+ return slots.default?.() ?? null;
156
+ };
157
+ }
158
+ });
159
+ export {
160
+ AppQueryState
161
+ };
@@ -0,0 +1,80 @@
1
+ import { type PropType, type SlotsType, type VNodeChild } from 'vue';
2
+ export type AppRadioCardValue = string | number;
3
+ export interface AppRadioCardItem {
4
+ /** 选项值(业务值,与 `modelValue` 同域) */
5
+ value: AppRadioCardValue;
6
+ /** 选项名(卡片标题) */
7
+ label: string;
8
+ /** 选项说明(卡片第二段文字,可缺省) */
9
+ description?: string;
10
+ /** 单项禁用 */
11
+ disabled?: boolean;
12
+ }
13
+ /**
14
+ * 卡片式单选组(**自建**,不封装 farris):一个选项一张卡片,整卡可点。
15
+ *
16
+ * 为什么不封装:farris 没有卡片形态的单选件(`f-radio-button` 是按钮组、`custom-radio` 是原生圆点),
17
+ * 页面上原先各自自绘 `.xxx-card-option`(只有布局没有视觉,护栏收走视觉后就「没生效」),
18
+ * 故按「缺件处置流程」自建。
19
+ *
20
+ * 实现要点:
21
+ * - 真实 `<input type="radio">` 负责可访问性与键盘(同 `name` 成组 ⇒ 方向键即换选),视觉自绘;
22
+ * - 状态走 `data-state`(`checked` / `unchecked`)协议,与 `AppCheckbox` / `AppTable` 自绘选择列一致;
23
+ * - `name` 每实例唯一(手写固定名会让同页两组卡片互相串选)。
24
+ *
25
+ * @example
26
+ * <AppRadioCard modelValue={form.type}
27
+ * items={[{ value: 'A', label: '值映射设置', description: '手工维护来源/目标向量' },
28
+ * { value: 'B', label: '数据库表', description: '绑定 msu 与数据对象' }]}
29
+ * onChange={(v) => (form.type = v)} />
30
+ */
31
+ export declare const AppRadioCard: import("vue").DefineComponent<import("vue").ExtractPropTypes<{
32
+ /** 受控值(与 `items[].value` 同域) */
33
+ modelValue: {
34
+ type: PropType<AppRadioCardValue | null>;
35
+ default: null;
36
+ };
37
+ items: {
38
+ type: PropType<AppRadioCardItem[]>;
39
+ default: () => never[];
40
+ };
41
+ /** 整组禁用(与单项 `disabled` 叠加) */
42
+ disabled: {
43
+ type: BooleanConstructor;
44
+ default: boolean;
45
+ };
46
+ onChange: {
47
+ type: PropType<(value: AppRadioCardValue) => void>;
48
+ default: undefined;
49
+ };
50
+ }>, () => import("vue/jsx-runtime").JSX.Element, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
51
+ 'update:modelValue': (value: AppRadioCardValue) => true;
52
+ }, string, import("vue").PublicProps, Readonly<import("vue").ExtractPropTypes<{
53
+ /** 受控值(与 `items[].value` 同域) */
54
+ modelValue: {
55
+ type: PropType<AppRadioCardValue | null>;
56
+ default: null;
57
+ };
58
+ items: {
59
+ type: PropType<AppRadioCardItem[]>;
60
+ default: () => never[];
61
+ };
62
+ /** 整组禁用(与单项 `disabled` 叠加) */
63
+ disabled: {
64
+ type: BooleanConstructor;
65
+ default: boolean;
66
+ };
67
+ onChange: {
68
+ type: PropType<(value: AppRadioCardValue) => void>;
69
+ default: undefined;
70
+ };
71
+ }>> & Readonly<{
72
+ "onUpdate:modelValue"?: ((value: AppRadioCardValue) => any) | undefined;
73
+ }>, {
74
+ onChange: (value: AppRadioCardValue) => void;
75
+ disabled: boolean;
76
+ modelValue: AppRadioCardValue | null;
77
+ items: AppRadioCardItem[];
78
+ }, SlotsType<{
79
+ default?: () => VNodeChild;
80
+ }>, {}, {}, string, import("vue").ComponentProvideOptions, true, {}, any>;
@@ -0,0 +1,72 @@
1
+ import { defineComponent, createVNode } from "vue";
2
+ const AppRadioCard = /* @__PURE__ */ defineComponent({
3
+ name: "AppRadioCard",
4
+ inheritAttrs: false,
5
+ props: {
6
+ /** 受控值(与 `items[].value` 同域) */
7
+ modelValue: {
8
+ type: [String, Number],
9
+ default: null
10
+ },
11
+ items: {
12
+ type: Array,
13
+ default: () => []
14
+ },
15
+ /** 整组禁用(与单项 `disabled` 叠加) */
16
+ disabled: {
17
+ type: Boolean,
18
+ default: false
19
+ },
20
+ onChange: {
21
+ type: Function,
22
+ default: void 0
23
+ }
24
+ },
25
+ emits: {
26
+ "update:modelValue": (value) => true
27
+ },
28
+ slots: Object,
29
+ setup(props, {
30
+ emit,
31
+ attrs
32
+ }) {
33
+ const groupName = `ak-radio-card-${Math.random().toString(36).slice(2, 10)}`;
34
+ const handleChange = (value) => {
35
+ emit("update:modelValue", value);
36
+ props.onChange?.(value);
37
+ };
38
+ return () => createVNode("div", {
39
+ "class": ["ak-radio-card", attrs.class].filter(Boolean).join(" "),
40
+ "role": "radiogroup"
41
+ }, [props.items.map((item) => {
42
+ const isDisabled = props.disabled || !!item.disabled;
43
+ const isChecked = props.modelValue === item.value;
44
+ return createVNode("label", {
45
+ "class": "ak-radio-card__item",
46
+ "key": String(item.value),
47
+ "data-state": isChecked ? "checked" : "unchecked",
48
+ "data-disabled": isDisabled ? "true" : "false"
49
+ }, [createVNode("input", {
50
+ "class": "ak-radio-card__input",
51
+ "type": "radio",
52
+ "name": groupName,
53
+ "value": String(item.value),
54
+ "checked": isChecked,
55
+ "disabled": isDisabled,
56
+ "onChange": () => handleChange(item.value)
57
+ }, null), createVNode("span", {
58
+ "class": "ak-radio-card__radio",
59
+ "aria-hidden": "true"
60
+ }, null), createVNode("span", {
61
+ "class": "ak-radio-card__body"
62
+ }, [createVNode("span", {
63
+ "class": "ak-radio-card__label"
64
+ }, [item.label]), item.description ? createVNode("span", {
65
+ "class": "ak-radio-card__desc"
66
+ }, [item.description]) : null])]);
67
+ })]);
68
+ }
69
+ });
70
+ export {
71
+ AppRadioCard
72
+ };
@@ -0,0 +1,80 @@
1
+ import { type PropType } from 'vue';
2
+ /**
3
+ * 搜索框(封装 farris FSearchBox)。
4
+ *
5
+ * 吃掉两个 farris 坑:
6
+ * 1. **清空图标只改 `modelValue`、不触发 `clickButton`** —— 本仓 repository 原为此在应用侧
7
+ * 加 watch 兜底;此处内部识别「由非空变空」并补发 `onClear`,应用侧不再需要该 watch。
8
+ * 2. 回车不会触发搜索(`clickButton` 只绑按钮)—— 内部在容器上监听 Enter 并补发 `onSearch`
9
+ * (事件从内部 input 冒泡,原 repository 也是这么绕的)。
10
+ *
11
+ * @example
12
+ * <AppSearchBox modelValue={keyword} placeholder="搜索名称"
13
+ * onChange={(v) => (keyword = v)}
14
+ * onSearch={(v) => applyFilter(v)}
15
+ * onClear={() => applyFilter('')} />
16
+ */
17
+ export declare const AppSearchBox: import("vue").DefineComponent<import("vue").ExtractPropTypes<{
18
+ modelValue: {
19
+ type: StringConstructor;
20
+ default: string;
21
+ };
22
+ placeholder: {
23
+ type: StringConstructor;
24
+ default: string;
25
+ };
26
+ disabled: {
27
+ type: BooleanConstructor;
28
+ default: boolean;
29
+ };
30
+ /** 值变化(含清空前的中间态);实时上抛 */
31
+ onChange: {
32
+ type: PropType<(value: string) => void>;
33
+ default: undefined;
34
+ };
35
+ /** 搜索触发(点搜索按钮 / 回车) */
36
+ onSearch: {
37
+ type: PropType<(value: string) => void>;
38
+ default: undefined;
39
+ };
40
+ /** 清空(由非空变空时补发,替代应用侧的 watch 兜底) */
41
+ onClear: {
42
+ type: PropType<() => void>;
43
+ default: undefined;
44
+ };
45
+ }>, () => import("vue/jsx-runtime").JSX.Element, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<import("vue").ExtractPropTypes<{
46
+ modelValue: {
47
+ type: StringConstructor;
48
+ default: string;
49
+ };
50
+ placeholder: {
51
+ type: StringConstructor;
52
+ default: string;
53
+ };
54
+ disabled: {
55
+ type: BooleanConstructor;
56
+ default: boolean;
57
+ };
58
+ /** 值变化(含清空前的中间态);实时上抛 */
59
+ onChange: {
60
+ type: PropType<(value: string) => void>;
61
+ default: undefined;
62
+ };
63
+ /** 搜索触发(点搜索按钮 / 回车) */
64
+ onSearch: {
65
+ type: PropType<(value: string) => void>;
66
+ default: undefined;
67
+ };
68
+ /** 清空(由非空变空时补发,替代应用侧的 watch 兜底) */
69
+ onClear: {
70
+ type: PropType<() => void>;
71
+ default: undefined;
72
+ };
73
+ }>> & Readonly<{}>, {
74
+ onChange: (value: string) => void;
75
+ disabled: boolean;
76
+ modelValue: string;
77
+ placeholder: string;
78
+ onSearch: (value: string) => void;
79
+ onClear: () => void;
80
+ }, {}, {}, {}, string, import("vue").ComponentProvideOptions, true, {}, any>;
@@ -0,0 +1,64 @@
1
+ import { defineComponent, createVNode } from "vue";
2
+ import { FSearchBox } from "@farris/ui-vue";
3
+ const AppSearchBox = /* @__PURE__ */ defineComponent({
4
+ name: "AppSearchBox",
5
+ inheritAttrs: false,
6
+ props: {
7
+ modelValue: {
8
+ type: String,
9
+ default: ""
10
+ },
11
+ placeholder: {
12
+ type: String,
13
+ default: ""
14
+ },
15
+ disabled: {
16
+ type: Boolean,
17
+ default: false
18
+ },
19
+ /** 值变化(含清空前的中间态);实时上抛 */
20
+ onChange: {
21
+ type: Function,
22
+ default: void 0
23
+ },
24
+ /** 搜索触发(点搜索按钮 / 回车) */
25
+ onSearch: {
26
+ type: Function,
27
+ default: void 0
28
+ },
29
+ /** 清空(由非空变空时补发,替代应用侧的 watch 兜底) */
30
+ onClear: {
31
+ type: Function,
32
+ default: void 0
33
+ }
34
+ },
35
+ setup(props, {
36
+ attrs
37
+ }) {
38
+ const handleUpdate = (value) => {
39
+ const next = value ?? "";
40
+ const cleared = next === "" && props.modelValue !== "";
41
+ props.onChange?.(next);
42
+ if (cleared) props.onClear?.();
43
+ };
44
+ return () => createVNode("div", {
45
+ "class": ["ak-searchbox", attrs.class].filter(Boolean).join(" "),
46
+ "onKeydown": (event) => {
47
+ if (event.key === "Enter") {
48
+ event.preventDefault();
49
+ props.onSearch?.(props.modelValue);
50
+ }
51
+ }
52
+ }, [createVNode(FSearchBox, {
53
+ "modelValue": props.modelValue,
54
+ "placeholder": props.placeholder,
55
+ "disabled": props.disabled,
56
+ "showDropdown": false,
57
+ "onUpdate:modelValue": handleUpdate,
58
+ "onClickButton": () => props.onSearch?.(props.modelValue)
59
+ }, null)]);
60
+ }
61
+ });
62
+ export {
63
+ AppSearchBox
64
+ };