@lark-apaas/coding-template-react-standard-webapp 0.1.0-alpha.20260827082008
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/LICENSE +13 -0
- package/README.md +25 -0
- package/hooks/after-install.mjs +3 -0
- package/hooks/after-render.mjs +3 -0
- package/hooks/before-render.mjs +3 -0
- package/package.json +35 -0
- package/template/README.md +105 -0
- package/template/_gitignore +64 -0
- package/template/_npmrc +1 -0
- package/template/components.json +21 -0
- package/template/eslint.config.mjs +27 -0
- package/template/index.html +13 -0
- package/template/miaoda.json +11 -0
- package/template/package-lock.json +7685 -0
- package/template/package.json +89 -0
- package/template/public/favicon.svg +1 -0
- package/template/public/icons.svg +24 -0
- package/template/src/app.tsx +15 -0
- package/template/src/components/ErrorFallback.tsx +19 -0
- package/template/src/components/Layout.tsx +7 -0
- package/template/src/components/ui/accordion.tsx +66 -0
- package/template/src/components/ui/alert-dialog.tsx +157 -0
- package/template/src/components/ui/alert.tsx +71 -0
- package/template/src/components/ui/aspect-ratio.tsx +11 -0
- package/template/src/components/ui/avatar.tsx +53 -0
- package/template/src/components/ui/badge.tsx +42 -0
- package/template/src/components/ui/breadcrumb.tsx +109 -0
- package/template/src/components/ui/button-group.tsx +83 -0
- package/template/src/components/ui/button.tsx +69 -0
- package/template/src/components/ui/calendar.tsx +213 -0
- package/template/src/components/ui/card.tsx +82 -0
- package/template/src/components/ui/carousel.tsx +241 -0
- package/template/src/components/ui/chart.tsx +357 -0
- package/template/src/components/ui/checkbox.tsx +32 -0
- package/template/src/components/ui/collapsible.tsx +33 -0
- package/template/src/components/ui/command.tsx +208 -0
- package/template/src/components/ui/context-menu.tsx +324 -0
- package/template/src/components/ui/dialog.tsx +143 -0
- package/template/src/components/ui/drawer.tsx +135 -0
- package/template/src/components/ui/dropdown-menu.tsx +329 -0
- package/template/src/components/ui/empty.tsx +104 -0
- package/template/src/components/ui/field.tsx +248 -0
- package/template/src/components/ui/form.tsx +167 -0
- package/template/src/components/ui/hover-card.tsx +44 -0
- package/template/src/components/ui/image.tsx +183 -0
- package/template/src/components/ui/input-group.tsx +166 -0
- package/template/src/components/ui/input-otp.tsx +77 -0
- package/template/src/components/ui/input.tsx +21 -0
- package/template/src/components/ui/item.tsx +193 -0
- package/template/src/components/ui/kbd.tsx +28 -0
- package/template/src/components/ui/label.tsx +24 -0
- package/template/src/components/ui/menubar.tsx +348 -0
- package/template/src/components/ui/native-select.tsx +48 -0
- package/template/src/components/ui/navigation-menu.tsx +168 -0
- package/template/src/components/ui/pagination.tsx +127 -0
- package/template/src/components/ui/popover.tsx +48 -0
- package/template/src/components/ui/progress.tsx +31 -0
- package/template/src/components/ui/radio-group.tsx +45 -0
- package/template/src/components/ui/resizable.tsx +56 -0
- package/template/src/components/ui/scroll-area.tsx +58 -0
- package/template/src/components/ui/select.tsx +243 -0
- package/template/src/components/ui/separator.tsx +28 -0
- package/template/src/components/ui/sheet.tsx +139 -0
- package/template/src/components/ui/sidebar.tsx +727 -0
- package/template/src/components/ui/skeleton.tsx +13 -0
- package/template/src/components/ui/slider.tsx +87 -0
- package/template/src/components/ui/sonner.tsx +67 -0
- package/template/src/components/ui/spinner.tsx +16 -0
- package/template/src/components/ui/switch.tsx +31 -0
- package/template/src/components/ui/table.tsx +116 -0
- package/template/src/components/ui/tabs.tsx +66 -0
- package/template/src/components/ui/textarea.tsx +18 -0
- package/template/src/components/ui/toggle-group.tsx +83 -0
- package/template/src/components/ui/toggle.tsx +47 -0
- package/template/src/components/ui/tooltip.tsx +61 -0
- package/template/src/hooks/use-mobile.ts +20 -0
- package/template/src/index.css +4 -0
- package/template/src/index.tsx +17 -0
- package/template/src/lib/utils.ts +6 -0
- package/template/src/pages/ExamplePage/ExamplePage.tsx +37 -0
- package/template/src/pages/HomePage/HomePage.tsx +10 -0
- package/template/src/pages/NotFoundPage/NotFoundPage.tsx +11 -0
- package/template/src/tailwind-theme.css +258 -0
- package/template/src/typography.css +41 -0
- package/template/src/vite-env.d.ts +5 -0
- package/template/tsconfig.app.json +29 -0
- package/template/tsconfig.json +10 -0
- package/template/tsconfig.node.json +26 -0
- package/template/vite.config.ts +97 -0
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
// import { useState } from 'react';
|
|
2
|
+
// import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
|
|
3
|
+
// import { Button } from '@/components/ui/button';
|
|
4
|
+
// import { RefreshCw, TrendingUp, PieChart, Clock } from 'lucide-react';
|
|
5
|
+
// import { Link } from 'react-router-dom';
|
|
6
|
+
// import { useRecordData } from '@/hooks/use-example';
|
|
7
|
+
|
|
8
|
+
// export default function ExamplePage() {
|
|
9
|
+
// const { record, loading, error, refetch } = useRecordData();
|
|
10
|
+
// const [dimension, setDimension] = useState<'month' | 'quarter' | 'year'>('month');
|
|
11
|
+
|
|
12
|
+
// if (loading) {
|
|
13
|
+
// return (
|
|
14
|
+
// <div className="flex items-center justify-center h-64">
|
|
15
|
+
// <div className="text-muted-foreground">加载中...</div>
|
|
16
|
+
// </div>
|
|
17
|
+
// );
|
|
18
|
+
// }
|
|
19
|
+
|
|
20
|
+
// if (error) {
|
|
21
|
+
// return (
|
|
22
|
+
// <div className="flex flex-col items-center justify-center h-64 space-y-4">
|
|
23
|
+
// <div className="text-destructive">{error}</div>
|
|
24
|
+
// <Button onClick={refetch} variant="outline" size="sm">
|
|
25
|
+
// <RefreshCw className="h-4 w-4 mr-2" />
|
|
26
|
+
// 重试
|
|
27
|
+
// </Button>
|
|
28
|
+
// </div>
|
|
29
|
+
// );
|
|
30
|
+
// }
|
|
31
|
+
|
|
32
|
+
// return (
|
|
33
|
+
// <div className="space-y-6">
|
|
34
|
+
// {/* 页面内容 */}
|
|
35
|
+
// </div>
|
|
36
|
+
// );
|
|
37
|
+
// }
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export default function HomePage() {
|
|
2
|
+
return (
|
|
3
|
+
<main className="flex min-h-screen flex-col items-center justify-center gap-3 p-8 text-center">
|
|
4
|
+
<h1 className="text-2xl font-semibold">应用已就绪</h1>
|
|
5
|
+
<p className="text-muted-foreground">
|
|
6
|
+
编辑 <code className="rounded bg-muted px-1.5 py-0.5 text-sm">src/app.tsx</code> 开始开发
|
|
7
|
+
</p>
|
|
8
|
+
</main>
|
|
9
|
+
);
|
|
10
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { Link } from "react-router-dom";
|
|
2
|
+
|
|
3
|
+
export default function NotFoundPage() {
|
|
4
|
+
return (
|
|
5
|
+
<div className="flex flex-col items-center justify-center py-24">
|
|
6
|
+
<h1 className="text-6xl font-bold mb-4">404</h1>
|
|
7
|
+
<p className="text-lg text-muted-foreground mb-8">页面不存在</p>
|
|
8
|
+
<Link to="/" className="text-primary hover:underline">返回首页</Link>
|
|
9
|
+
</div>
|
|
10
|
+
);
|
|
11
|
+
}
|
|
@@ -0,0 +1,258 @@
|
|
|
1
|
+
:root {
|
|
2
|
+
--button-outline: rgba(0,0,0, .10);
|
|
3
|
+
--badge-outline: rgba(0,0,0, .05);
|
|
4
|
+
|
|
5
|
+
/* Automatic computation of border around primary / danger buttons */
|
|
6
|
+
--opaque-button-border-intensity: -8; /* In terms of percentages */
|
|
7
|
+
|
|
8
|
+
/* Backgrounds applied on top of other backgrounds when hovered/active */
|
|
9
|
+
--elevate-1: rgba(0,0,0, .03);
|
|
10
|
+
--elevate-2: rgba(0,0,0, .08);
|
|
11
|
+
|
|
12
|
+
--background: hsl(0 0% 100%);
|
|
13
|
+
--foreground: hsl(220 14% 14%);
|
|
14
|
+
--card: hsl(0 0% 100%);
|
|
15
|
+
--card-foreground: hsl(220 14% 14%);
|
|
16
|
+
--popover: hsl(0 0% 100%);
|
|
17
|
+
--popover-foreground: hsl(220 14% 14%);
|
|
18
|
+
--primary: hsl(221 83% 53%);
|
|
19
|
+
--primary-foreground: hsl(220 14% 98%);
|
|
20
|
+
--secondary: hsl(220 14% 96%);
|
|
21
|
+
--secondary-foreground: hsl(220 14% 14%);
|
|
22
|
+
--muted: hsl(220 14% 96%);
|
|
23
|
+
--muted-foreground: hsl(218 7% 59%);
|
|
24
|
+
--accent: hsl(220 14% 96%);
|
|
25
|
+
--accent-foreground: hsl(220 14% 14%);
|
|
26
|
+
--info: hsl(221 83% 53%);
|
|
27
|
+
--info-foreground: hsl(230 100% 98%);
|
|
28
|
+
--destructive: hsl(2 84% 62%);
|
|
29
|
+
--destructive-foreground: hsl(0 0% 100%);
|
|
30
|
+
--success: hsl(130 54% 42%);
|
|
31
|
+
--success-foreground: hsl(115 77% 97%);
|
|
32
|
+
--warning: hsl(26 90% 49%);
|
|
33
|
+
--warning-foreground: hsl(33 100% 96%);
|
|
34
|
+
--border: hsl(220 14% 89%);
|
|
35
|
+
--input: hsl(220 14% 89%);
|
|
36
|
+
--ring: hsl(221 83% 53%);
|
|
37
|
+
--chart-1: hsl(222 82% 56%);
|
|
38
|
+
--chart-2: hsl(174 77% 46%);
|
|
39
|
+
--chart-3: hsl(46 100% 52%);
|
|
40
|
+
--chart-4: hsl(27 92% 49%);
|
|
41
|
+
--chart-5: hsl(292 54% 76%);
|
|
42
|
+
--sidebar: hsl(220 14% 96%);
|
|
43
|
+
--sidebar-foreground: hsl(220 14% 14%);
|
|
44
|
+
--sidebar-primary: hsl(221 83% 53%);
|
|
45
|
+
--sidebar-primary-foreground: hsl(220 14% 98%);
|
|
46
|
+
--sidebar-accent: hsl(222 100% 94%);
|
|
47
|
+
--sidebar-accent-foreground: hsl(221 83% 53%);
|
|
48
|
+
--sidebar-border: hsl(220 14% 89%);
|
|
49
|
+
--sidebar-ring: hsl(221 83% 53%);
|
|
50
|
+
|
|
51
|
+
/* Computed borders using hsl relative color syntax */
|
|
52
|
+
--primary-border: hsl(from hsl(221 83% 53%) h s calc(l + var(--opaque-button-border-intensity)));
|
|
53
|
+
--secondary-border: hsl(from hsl(220 14% 96%) h s calc(l + var(--opaque-button-border-intensity)));
|
|
54
|
+
--destructive-border: hsl(from hsl(2 84% 62%) h s calc(l + var(--opaque-button-border-intensity)));
|
|
55
|
+
--accent-border: hsl(from hsl(220 14% 96%) h s calc(l + var(--opaque-button-border-intensity)));
|
|
56
|
+
--muted-border: hsl(from hsl(220 14% 96%) h s calc(l + var(--opaque-button-border-intensity)));
|
|
57
|
+
--font-sans:
|
|
58
|
+
'LarkHackSafariFont', 'LarkEmojiFont', 'LarkChineseQuote', '-apple-system',
|
|
59
|
+
'BlinkMacSystemFont', 'Helvetica Neue', 'Tahoma', 'PingFang SC',
|
|
60
|
+
'Microsoft Yahei', 'Arial', 'Hiragino Sans GB', 'sans-serif',
|
|
61
|
+
'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol', 'Noto Color Emoji';
|
|
62
|
+
--font-serif:
|
|
63
|
+
'Geist', 'Geist Fallback', ui-serif, Georgia, Cambria, 'Times New Roman',
|
|
64
|
+
Times, serif;
|
|
65
|
+
--font-mono: Source Code Pro, monospace;
|
|
66
|
+
--radius: 0.5rem;
|
|
67
|
+
--shadow-x: 0px;
|
|
68
|
+
--shadow-y: 2px;
|
|
69
|
+
--shadow-blur: 12px;
|
|
70
|
+
--shadow-spread: 0px;
|
|
71
|
+
--shadow-opacity: 0.02;
|
|
72
|
+
--shadow-color: #000000;
|
|
73
|
+
--shadow-2xs: 0px 2px 12px 0px hsl(0 0% 0% / 0.01);
|
|
74
|
+
--shadow-xs: 0px 2px 12px 0px hsl(0 0% 0% / 0.01);
|
|
75
|
+
--shadow-sm:
|
|
76
|
+
0px 2px 12px 0px hsl(0 0% 0% / 0.02), 0px 1px 2px -1px hsl(0 0% 0% / 0.02);
|
|
77
|
+
--shadow:
|
|
78
|
+
0px 2px 12px 0px hsl(0 0% 0% / 0.02), 0px 1px 2px -1px hsl(0 0% 0% / 0.02);
|
|
79
|
+
--shadow-md:
|
|
80
|
+
0px 2px 12px 0px hsl(0 0% 0% / 0.02), 0px 2px 4px -1px hsl(0 0% 0% / 0.02);
|
|
81
|
+
--shadow-lg:
|
|
82
|
+
0px 2px 12px 0px hsl(0 0% 0% / 0.02), 0px 4px 6px -1px hsl(0 0% 0% / 0.02);
|
|
83
|
+
--shadow-xl:
|
|
84
|
+
0px 2px 12px 0px hsl(0 0% 0% / 0.02), 0px 8px 10px -1px hsl(0 0% 0% / 0.02);
|
|
85
|
+
--shadow-2xl: 0px 2px 12px 0px hsl(0 0% 0% / 0.05);
|
|
86
|
+
--tracking-normal: 0em;
|
|
87
|
+
--spacing: 0.25rem;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
@theme inline {
|
|
91
|
+
--color-background: var(--background);
|
|
92
|
+
--color-foreground: var(--foreground);
|
|
93
|
+
--color-card: var(--card);
|
|
94
|
+
--color-card-foreground: var(--card-foreground);
|
|
95
|
+
--color-popover: var(--popover);
|
|
96
|
+
--color-popover-foreground: var(--popover-foreground);
|
|
97
|
+
--color-primary: var(--primary);
|
|
98
|
+
--color-primary-foreground: var(--primary-foreground);
|
|
99
|
+
--color-secondary: var(--secondary);
|
|
100
|
+
--color-secondary-foreground: var(--secondary-foreground);
|
|
101
|
+
--color-muted: var(--muted);
|
|
102
|
+
--color-muted-foreground: var(--muted-foreground);
|
|
103
|
+
--color-accent: var(--accent);
|
|
104
|
+
--color-accent-foreground: var(--accent-foreground);
|
|
105
|
+
--color-info: var(--info);
|
|
106
|
+
--color-info-foreground: var(--info-foreground);
|
|
107
|
+
--color-destructive: var(--destructive);
|
|
108
|
+
--color-destructive-foreground: var(--destructive-foreground);
|
|
109
|
+
--color-success: var(--success);
|
|
110
|
+
--color-success-foreground: var(--success-foreground);
|
|
111
|
+
--color-warning: var(--warning);
|
|
112
|
+
--color-warning-foreground: var(--warning-foreground);
|
|
113
|
+
--color-border: var(--border);
|
|
114
|
+
--color-input: var(--input);
|
|
115
|
+
--color-ring: var(--ring);
|
|
116
|
+
--color-chart-1: var(--chart-1);
|
|
117
|
+
--color-chart-2: var(--chart-2);
|
|
118
|
+
--color-chart-3: var(--chart-3);
|
|
119
|
+
--color-chart-4: var(--chart-4);
|
|
120
|
+
--color-chart-5: var(--chart-5);
|
|
121
|
+
--color-sidebar: var(--sidebar);
|
|
122
|
+
--color-sidebar-foreground: var(--sidebar-foreground);
|
|
123
|
+
--color-sidebar-primary: var(--sidebar-primary);
|
|
124
|
+
--color-sidebar-primary-foreground: var(--sidebar-primary-foreground);
|
|
125
|
+
--color-sidebar-accent: var(--sidebar-accent);
|
|
126
|
+
--color-sidebar-accent-foreground: var(--sidebar-accent-foreground);
|
|
127
|
+
--color-sidebar-border: var(--sidebar-border);
|
|
128
|
+
--color-sidebar-ring: var(--sidebar-ring);
|
|
129
|
+
|
|
130
|
+
--font-sans: var(--font-sans);
|
|
131
|
+
--font-mono: var(--font-mono);
|
|
132
|
+
--font-serif: var(--font-serif);
|
|
133
|
+
|
|
134
|
+
--radius-sm: calc(var(--radius) - 4px);
|
|
135
|
+
--radius-md: calc(var(--radius) - 2px);
|
|
136
|
+
--radius-lg: var(--radius);
|
|
137
|
+
--radius-xl: calc(var(--radius) + 4px);
|
|
138
|
+
|
|
139
|
+
--shadow-2xs: var(--shadow-2xs);
|
|
140
|
+
--shadow-xs: var(--shadow-xs);
|
|
141
|
+
--shadow-sm: var(--shadow-sm);
|
|
142
|
+
--shadow: var(--shadow);
|
|
143
|
+
--shadow-md: var(--shadow-md);
|
|
144
|
+
--shadow-lg: var(--shadow-lg);
|
|
145
|
+
--shadow-xl: var(--shadow-xl);
|
|
146
|
+
--shadow-2xl: var(--shadow-2xl);
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
/**
|
|
150
|
+
* Using the elevate system.
|
|
151
|
+
* Automatic contrast adjustment.
|
|
152
|
+
*
|
|
153
|
+
* <element className="hover-elevate" />
|
|
154
|
+
* <element className="active-elevate-2" />
|
|
155
|
+
*
|
|
156
|
+
* // Using the tailwind utility when a data attribute is "on"
|
|
157
|
+
* <element className="toggle-elevate data-[state=on]:toggle-elevated" />
|
|
158
|
+
* // Or manually controlling the toggle state
|
|
159
|
+
* <element className="toggle-elevate toggle-elevated" />
|
|
160
|
+
*
|
|
161
|
+
* Elevation systems have to handle many states.
|
|
162
|
+
* - not-hovered, vs. hovered vs. active (three mutually exclusive states)
|
|
163
|
+
* - toggled or not
|
|
164
|
+
* - focused or not (this is not handled with these utilities)
|
|
165
|
+
*
|
|
166
|
+
* Even without handling focused or not, this is six possible combinations that
|
|
167
|
+
* need to be distinguished from eachother visually.
|
|
168
|
+
*/
|
|
169
|
+
@layer utilities {
|
|
170
|
+
/* Hide ugly search cancel button in Chrome until we can style it properly */
|
|
171
|
+
input[type="search"]::-webkit-search-cancel-button {
|
|
172
|
+
@apply hidden;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
/* Placeholder styling for contentEditable div */
|
|
176
|
+
[contenteditable][data-placeholder]:empty::before {
|
|
177
|
+
content: attr(data-placeholder);
|
|
178
|
+
color: var(--muted-foreground);
|
|
179
|
+
pointer-events: none;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
/* .no-default-hover-elevate/no-default-active-elevate is an escape hatch so consumers of
|
|
183
|
+
* buttons/badges can remove the automatic brightness adjustment on interactions
|
|
184
|
+
* and program their own. */
|
|
185
|
+
.no-default-hover-elevate {}
|
|
186
|
+
.no-default-active-elevate {}
|
|
187
|
+
|
|
188
|
+
|
|
189
|
+
/**
|
|
190
|
+
* Toggleable backgrounds go behind the content. Hoverable/active goes on top.
|
|
191
|
+
* This way they can stack/compound. Both will overlap the parent's borders!
|
|
192
|
+
* So borders will be automatically adjusted both on toggle, and hover/active,
|
|
193
|
+
* and they will be compounded.
|
|
194
|
+
*/
|
|
195
|
+
.toggle-elevate::before,
|
|
196
|
+
.toggle-elevate-2::before {
|
|
197
|
+
content: "";
|
|
198
|
+
pointer-events: none;
|
|
199
|
+
position: absolute;
|
|
200
|
+
inset: 0px;
|
|
201
|
+
/*border-radius: inherit; match rounded corners */
|
|
202
|
+
border-radius: inherit;
|
|
203
|
+
z-index: -1;
|
|
204
|
+
/* sits behind content but above backdrop */
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
.toggle-elevate.toggle-elevated::before {
|
|
208
|
+
background-color: var(--elevate-2);
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
/* If there's a 1px border, adjust the inset so that it covers that parent's border */
|
|
212
|
+
.border.toggle-elevate::before {
|
|
213
|
+
inset: -1px;
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
/* Does not work on elements with overflow:hidden!
|
|
217
|
+
* Wrap in :where() to zero out specificity so Tailwind position utilities (absolute/fixed/sticky) can override */
|
|
218
|
+
:where(.hover-elevate:not(.no-default-hover-elevate)),
|
|
219
|
+
:where(.active-elevate:not(.no-default-active-elevate)),
|
|
220
|
+
:where(.hover-elevate-2:not(.no-default-hover-elevate)),
|
|
221
|
+
:where(.active-elevate-2:not(.no-default-active-elevate)) {
|
|
222
|
+
position: relative;
|
|
223
|
+
z-index: 0;
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
.hover-elevate:not(.no-default-hover-elevate)::after,
|
|
227
|
+
.active-elevate:not(.no-default-active-elevate)::after,
|
|
228
|
+
.hover-elevate-2:not(.no-default-hover-elevate)::after,
|
|
229
|
+
.active-elevate-2:not(.no-default-active-elevate)::after {
|
|
230
|
+
content: "";
|
|
231
|
+
pointer-events: none;
|
|
232
|
+
position: absolute;
|
|
233
|
+
inset: 0px;
|
|
234
|
+
/*border-radius: inherit; match rounded corners */
|
|
235
|
+
border-radius: inherit;
|
|
236
|
+
z-index: 999;
|
|
237
|
+
/* sits in front of content */
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
.hover-elevate:hover:not(.no-default-hover-elevate)::after,
|
|
241
|
+
.active-elevate:active:not(.no-default-active-elevate)::after {
|
|
242
|
+
background-color: var(--elevate-1);
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
.hover-elevate-2:hover:not(.no-default-hover-elevate)::after,
|
|
246
|
+
.active-elevate-2:active:not(.no-default-active-elevate)::after {
|
|
247
|
+
background-color: var(--elevate-2);
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
/* If there's a 1px border, adjust the inset so that it covers that parent's border */
|
|
251
|
+
.border.hover-elevate:not(.no-hover-interaction-elevate)::after,
|
|
252
|
+
.border.active-elevate:not(.no-active-interaction-elevate)::after,
|
|
253
|
+
.border.hover-elevate-2:not(.no-hover-interaction-elevate)::after,
|
|
254
|
+
.border.active-elevate-2:not(.no-active-interaction-elevate)::after,
|
|
255
|
+
.border.hover-elevate:not(.no-hover-interaction-elevate)::after {
|
|
256
|
+
inset: -1px;
|
|
257
|
+
}
|
|
258
|
+
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
@plugin "@tailwindcss/typography";
|
|
2
|
+
|
|
3
|
+
@utility prose {
|
|
4
|
+
--tw-prose-body: var(--foreground);
|
|
5
|
+
--tw-prose-headings: var(--foreground);
|
|
6
|
+
--tw-prose-lead: var(--muted-foreground);
|
|
7
|
+
--tw-prose-links: var(--primary);
|
|
8
|
+
--tw-prose-bold: var(--foreground);
|
|
9
|
+
--tw-prose-counters: var(--muted-foreground);
|
|
10
|
+
--tw-prose-bullets: var(--primary);
|
|
11
|
+
--tw-prose-hr: var(--border);
|
|
12
|
+
--tw-prose-quotes: var(--foreground);
|
|
13
|
+
--tw-prose-quote-borders: var(--border);
|
|
14
|
+
--tw-prose-captions: var(--muted-foreground);
|
|
15
|
+
--tw-prose-kbd: var(--foreground);
|
|
16
|
+
--tw-prose-kbd-shadows: --alpha(var(--foreground) / 90%);
|
|
17
|
+
--tw-prose-code: var(--foreground);
|
|
18
|
+
--tw-prose-pre-code: var(--accent-foreground);
|
|
19
|
+
--tw-prose-pre-bg: var(--accent);
|
|
20
|
+
--tw-prose-th-borders: var(--border);
|
|
21
|
+
--tw-prose-td-borders: var(--border);
|
|
22
|
+
|
|
23
|
+
--tw-prose-invert-body: var(--foreground);
|
|
24
|
+
--tw-prose-invert-headings: var(--foreground);
|
|
25
|
+
--tw-prose-invert-lead: var(--muted-foreground);
|
|
26
|
+
--tw-prose-invert-links: var(--primary);
|
|
27
|
+
--tw-prose-invert-bold: var(--foreground);
|
|
28
|
+
--tw-prose-invert-counters: var(--muted-foreground);
|
|
29
|
+
--tw-prose-invert-bullets: var(--primary);
|
|
30
|
+
--tw-prose-invert-hr: var(--border);
|
|
31
|
+
--tw-prose-invert-quotes: var(--foreground);
|
|
32
|
+
--tw-prose-invert-quote-borders: var(--border);
|
|
33
|
+
--tw-prose-invert-captions: var(--muted-foreground);
|
|
34
|
+
--tw-prose-invert-kbd: var(--foreground);
|
|
35
|
+
--tw-prose-invert-kbd-shadows: --alpha(var(--foreground) / 90%);
|
|
36
|
+
--tw-prose-invert-code: var(--foreground);
|
|
37
|
+
--tw-prose-invert-pre-code: var(--accent-foreground);
|
|
38
|
+
--tw-prose-invert-pre-bg: var(--accent);
|
|
39
|
+
--tw-prose-invert-th-borders: var(--border);
|
|
40
|
+
--tw-prose-invert-td-borders: var(--border);
|
|
41
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"target": "ES2023",
|
|
4
|
+
"useDefineForClassFields": true,
|
|
5
|
+
"lib": ["ES2023", "DOM", "DOM.Iterable"],
|
|
6
|
+
"module": "ESNext",
|
|
7
|
+
"skipLibCheck": true,
|
|
8
|
+
|
|
9
|
+
"moduleResolution": "bundler",
|
|
10
|
+
"allowImportingTsExtensions": true,
|
|
11
|
+
"verbatimModuleSyntax": false,
|
|
12
|
+
"moduleDetection": "force",
|
|
13
|
+
"noEmit": true,
|
|
14
|
+
"jsx": "react-jsx",
|
|
15
|
+
|
|
16
|
+
"strict": false,
|
|
17
|
+
"noUnusedLocals": false,
|
|
18
|
+
"noUnusedParameters": false,
|
|
19
|
+
"noImplicitAny": false,
|
|
20
|
+
"noFallthroughCasesInSwitch": false,
|
|
21
|
+
|
|
22
|
+
"tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo",
|
|
23
|
+
"baseUrl": ".",
|
|
24
|
+
"paths": {
|
|
25
|
+
"@/*": ["./src/*"]
|
|
26
|
+
}
|
|
27
|
+
},
|
|
28
|
+
"include": ["src"]
|
|
29
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"tsBuildInfoFile": "./node_modules/.tmp/tsconfig.node.tsbuildinfo",
|
|
4
|
+
"target": "ES2023",
|
|
5
|
+
"lib": ["ES2023"],
|
|
6
|
+
"module": "ESNext",
|
|
7
|
+
"types": ["node"],
|
|
8
|
+
"skipLibCheck": true,
|
|
9
|
+
|
|
10
|
+
/* Bundler mode */
|
|
11
|
+
"moduleResolution": "bundler",
|
|
12
|
+
"allowImportingTsExtensions": true,
|
|
13
|
+
"verbatimModuleSyntax": true,
|
|
14
|
+
"moduleDetection": "force",
|
|
15
|
+
"noEmit": true,
|
|
16
|
+
|
|
17
|
+
/* Linting */
|
|
18
|
+
"strict": true,
|
|
19
|
+
"noUnusedLocals": true,
|
|
20
|
+
"noUnusedParameters": true,
|
|
21
|
+
"erasableSyntaxOnly": true,
|
|
22
|
+
"noFallthroughCasesInSwitch": true,
|
|
23
|
+
"noUncheckedSideEffectImports": true
|
|
24
|
+
},
|
|
25
|
+
"include": ["vite.config.ts"]
|
|
26
|
+
}
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import fs from 'node:fs';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import { defineConfig, type Plugin } from 'vite';
|
|
4
|
+
import react from '@vitejs/plugin-react';
|
|
5
|
+
import tailwindcss from '@tailwindcss/vite';
|
|
6
|
+
|
|
7
|
+
// ── 妙搭部署协议(参考《妙搭应用构建产物规范》)─────────────────────────
|
|
8
|
+
// 构建期环境变量(由 lark-cli apps +app-dev-get-build-env 提供;本地开发不需要,缺省回退):
|
|
9
|
+
// MIAODA_CLIENT_BASE_PATH 应用路由根目录,如 /app/app_xxx
|
|
10
|
+
// MIAODA_RESOURCE_CDN_PREFIX JS/CSS 静态资源 CDN 前缀
|
|
11
|
+
const basePath = process.env.MIAODA_CLIENT_BASE_PATH || '/';
|
|
12
|
+
const cdnPrefix = process.env.MIAODA_RESOURCE_CDN_PREFIX;
|
|
13
|
+
|
|
14
|
+
// 产物分层:vite 原生产物(dist/client,保留,可手动 `npx vite preview` 验证)
|
|
15
|
+
// 复制出妙搭托管产物结构:
|
|
16
|
+
// dist/output/ index.html + public 同源资源 + routes.json(走应用权限校验)
|
|
17
|
+
// dist/output_resource/ assets JS/CSS(推 CDN,公开)
|
|
18
|
+
function miaodaOutputPlugin(): Plugin {
|
|
19
|
+
return {
|
|
20
|
+
name: 'miaoda-output',
|
|
21
|
+
apply: 'build',
|
|
22
|
+
writeBundle() {
|
|
23
|
+
const dist = path.resolve(import.meta.dirname, 'dist');
|
|
24
|
+
const client = path.join(dist, 'client');
|
|
25
|
+
const output = path.join(dist, 'output');
|
|
26
|
+
const outputResource = path.join(dist, 'output_resource');
|
|
27
|
+
|
|
28
|
+
fs.rmSync(output, { recursive: true, force: true });
|
|
29
|
+
fs.rmSync(outputResource, { recursive: true, force: true });
|
|
30
|
+
fs.mkdirSync(output, { recursive: true });
|
|
31
|
+
|
|
32
|
+
// assets 之外的所有产物(index.html + public/ 平铺文件)→ 同源 output/
|
|
33
|
+
for (const entry of fs.readdirSync(client)) {
|
|
34
|
+
if (entry === 'assets') continue;
|
|
35
|
+
fs.cpSync(path.join(client, entry), path.join(output, entry), {
|
|
36
|
+
recursive: true,
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
// assets → CDN 桶
|
|
40
|
+
const assets = path.join(client, 'assets');
|
|
41
|
+
if (fs.existsSync(assets)) {
|
|
42
|
+
fs.cpSync(assets, path.join(outputResource, 'assets'), {
|
|
43
|
+
recursive: true,
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
// routes.json:扫描 src 内 <Route path> 生成路由枚举(TNS 按此遍历送审);
|
|
47
|
+
// SPA 场景各路由均由 index.html 服务
|
|
48
|
+
const routes = collectRoutePaths(
|
|
49
|
+
path.resolve(import.meta.dirname, 'src'),
|
|
50
|
+
).map((p) => ({ path: p, file: 'index.html' }));
|
|
51
|
+
fs.writeFileSync(
|
|
52
|
+
path.join(output, 'routes.json'),
|
|
53
|
+
JSON.stringify(routes, null, 2) + '\n',
|
|
54
|
+
);
|
|
55
|
+
},
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
// 收集 <Route path="..."> 声明的路由;index 路由计为 "/",通配 "*" 不进枚举
|
|
60
|
+
function collectRoutePaths(srcDir: string): string[] {
|
|
61
|
+
const paths = new Set<string>(['/']);
|
|
62
|
+
const walk = (dir: string) => {
|
|
63
|
+
for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
|
|
64
|
+
const p = path.join(dir, entry.name);
|
|
65
|
+
if (entry.isDirectory()) {
|
|
66
|
+
walk(p);
|
|
67
|
+
} else if (/\.(tsx|jsx|ts|js)$/.test(entry.name)) {
|
|
68
|
+
const code = fs.readFileSync(p, 'utf-8');
|
|
69
|
+
for (const m of code.matchAll(/<Route[^>]*\bpath=["']([^"']+)["']/g)) {
|
|
70
|
+
const route = m[1];
|
|
71
|
+
if (route.includes('*')) continue;
|
|
72
|
+
paths.add(route.startsWith('/') ? route : `/${route}`);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
};
|
|
77
|
+
walk(srcDir);
|
|
78
|
+
return [...paths];
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
export default defineConfig(({ command }) => ({
|
|
82
|
+
plugins: [react(), tailwindcss(), miaodaOutputPlugin()],
|
|
83
|
+
// 生产构建:JS/CSS 引用带 CDN 前缀(无 CDN 时退回 base path);dev 恒为 /
|
|
84
|
+
base: command === 'build' ? cdnPrefix || basePath : '/',
|
|
85
|
+
define: {
|
|
86
|
+
// 路由 basename 与资源前缀解耦,单独注入
|
|
87
|
+
'import.meta.env.MIAODA_CLIENT_BASE_PATH': JSON.stringify(basePath),
|
|
88
|
+
},
|
|
89
|
+
resolve: {
|
|
90
|
+
alias: {
|
|
91
|
+
'@': path.resolve(import.meta.dirname, 'src'),
|
|
92
|
+
},
|
|
93
|
+
},
|
|
94
|
+
build: {
|
|
95
|
+
outDir: 'dist/client',
|
|
96
|
+
},
|
|
97
|
+
}));
|