@4399ywkf/cli 2.0.6 → 2.0.8

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/dist/index.js CHANGED
@@ -480,10 +480,6 @@ async function injectMasterAppPages(pagesDir, appName) {
480
480
  path.join(pagesDir, "micro/layout.tsx"),
481
481
  MASTER_MICRO_LAYOUT
482
482
  );
483
- await writeFile(
484
- path.join(pagesDir, "micro/$.tsx"),
485
- MASTER_MICRO_CATCHALL
486
- );
487
483
  await writeFile(
488
484
  path.join(pagesDir, "page.tsx"),
489
485
  MASTER_HOME_PAGE
@@ -601,14 +597,19 @@ export default function HomePage() {
601
597
  );
602
598
  }
603
599
  `;
604
- const MASTER_MICRO_LAYOUT = `import { Outlet } from "react-router";
600
+ const MASTER_MICRO_LAYOUT = `import { Outlet, useLocation } from "react-router";
605
601
 
606
602
  export default function MicroLayout() {
603
+ const { pathname } = useLocation();
604
+ const isIndex = pathname === "/micro" || pathname === "/micro/";
605
+
607
606
  return (
608
607
  <div>
609
- <Outlet />
610
- {/* Garfish \u5B50\u5E94\u7528\u6302\u8F7D\u5BB9\u5668 */}
611
- <div id="subapp-container" style={{ minHeight: 400 }} />
608
+ {isIndex && <Outlet />}
609
+ <div
610
+ id="subapp-container"
611
+ style={{ minHeight: 400, display: isIndex ? "none" : "block" }}
612
+ />
612
613
  </div>
613
614
  );
614
615
  }
@@ -656,39 +657,6 @@ export default function MicroPage() {
656
657
  );
657
658
  }
658
659
  `;
659
- const MASTER_MICRO_CATCHALL = `import { Typography, Spin, Alert } from "antd";
660
- import { useLocation } from "react-router";
661
-
662
- const { Paragraph } = Typography;
663
-
664
- export default function MicroCatchAll() {
665
- const { pathname } = useLocation();
666
-
667
- return (
668
- <div style={{ maxWidth: 720, margin: "0 auto", padding: "40px 0" }}>
669
- <Alert
670
- type="warning"
671
- showIcon
672
- message="\u5B50\u5E94\u7528\u8DEF\u7531\u533A\u57DF"
673
- description={
674
- <>
675
- <Paragraph>
676
- \u5F53\u524D\u8DEF\u5F84 <code>{pathname}</code> \u9884\u671F\u7531 Garfish \u5B50\u5E94\u7528\u63A5\u7BA1\u3002
677
- </Paragraph>
678
- <Paragraph type="secondary">
679
- \u5982\u679C\u4F60\u770B\u5230\u6B64\u9875\u9762\uFF0C\u8BF4\u660E\u5BF9\u5E94\u7684\u5B50\u5E94\u7528\u5C1A\u672A\u6CE8\u518C\u6216\u672A\u542F\u52A8\u3002
680
- \u8BF7\u68C0\u67E5 <code>ywkf.config.ts</code> \u4E2D <code>garfishPlugin</code> \u7684 apps \u914D\u7F6E\u3002
681
- </Paragraph>
682
- </>
683
- }
684
- />
685
- <div style={{ textAlign: "center", marginTop: 24 }}>
686
- <Spin tip="\u7B49\u5F85\u5B50\u5E94\u7528\u52A0\u8F7D..." />
687
- </div>
688
- </div>
689
- );
690
- }
691
- `;
692
660
  const SUB_LAYOUT = `import { Outlet, Link, useLocation } from "react-router";
693
661
  import { Layout, Menu, Tag } from "antd";
694
662
  import { useEffect, useState } from "react";
@@ -1 +1,26 @@
1
- /* 基础样式 - Tailwind CSS 由 tailwindPlugin 自动注入到 .ywkf/tailwind.css */
1
+ *,
2
+ *::before,
3
+ *::after {
4
+ box-sizing: border-box;
5
+ margin: 0;
6
+ }
7
+
8
+ body {
9
+ font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto,
10
+ "Helvetica Neue", Arial, sans-serif;
11
+ -webkit-font-smoothing: antialiased;
12
+ -moz-osx-font-smoothing: grayscale;
13
+ background: #fafafa;
14
+ color: #1a1a1a;
15
+ }
16
+
17
+ a:hover {
18
+ opacity: 0.85;
19
+ }
20
+
21
+ /* Card hover effect */
22
+ a[style*="border-radius: 12px"]:hover {
23
+ border-color: #3b82f6 !important;
24
+ box-shadow: 0 4px 12px rgba(59, 130, 246, 0.1);
25
+ transform: translateY(-2px);
26
+ }
@@ -1,36 +1,105 @@
1
1
  import { Outlet, Link, useLocation } from "react-router";
2
- import { Layout, Menu } from "antd";
3
-
4
- const { Header, Content, Footer } = Layout;
5
2
 
6
3
  const NAV_ITEMS = [
7
- { key: "/", label: <Link to="/">首页</Link> },
8
- { key: "/dashboard", label: <Link to="/dashboard">工作台</Link> },
9
- { key: "/user", label: <Link to="/user">用户管理</Link> },
4
+ { key: "/", label: "首页" },
5
+ { key: "/dashboard", label: "工作台" },
6
+ { key: "/user", label: "用户管理" },
10
7
  ];
11
8
 
12
9
  export default function RootLayout() {
13
10
  const { pathname } = useLocation();
14
- const activeKey = NAV_ITEMS.find((item) => pathname.startsWith(item.key) && item.key !== "/")?.key
15
- || (pathname === "/" ? "/" : "");
16
11
 
17
12
  return (
18
- <Layout style={{ minHeight: "100vh" }}>
19
- <Header style={{ display: "flex", alignItems: "center" }}>
20
- <Menu
21
- theme="dark"
22
- mode="horizontal"
23
- selectedKeys={[activeKey]}
24
- style={{ flex: 1 }}
25
- items={NAV_ITEMS}
26
- />
27
- </Header>
28
- <Content style={{ padding: "24px 48px" }}>
13
+ <div style={styles.container}>
14
+ <header style={styles.header}>
15
+ <Link to="/" style={styles.logo}>
16
+ YWKF
17
+ </Link>
18
+ <nav style={styles.nav}>
19
+ {NAV_ITEMS.map((item) => {
20
+ const active =
21
+ item.key === "/"
22
+ ? pathname === "/"
23
+ : pathname.startsWith(item.key);
24
+ return (
25
+ <Link
26
+ key={item.key}
27
+ to={item.key}
28
+ style={{
29
+ ...styles.navLink,
30
+ ...(active ? styles.navLinkActive : {}),
31
+ }}
32
+ >
33
+ {item.label}
34
+ </Link>
35
+ );
36
+ })}
37
+ </nav>
38
+ </header>
39
+
40
+ <main style={styles.main}>
29
41
  <Outlet />
30
- </Content>
31
- <Footer style={{ textAlign: "center" }}>
42
+ </main>
43
+
44
+ <footer style={styles.footer}>
32
45
  YWKF Framework ©{new Date().getFullYear()}
33
- </Footer>
34
- </Layout>
46
+ </footer>
47
+ </div>
35
48
  );
36
49
  }
50
+
51
+ const styles: Record<string, React.CSSProperties> = {
52
+ container: {
53
+ minHeight: "100vh",
54
+ display: "flex",
55
+ flexDirection: "column",
56
+ background: "#fafafa",
57
+ },
58
+ header: {
59
+ display: "flex",
60
+ alignItems: "center",
61
+ padding: "0 32px",
62
+ height: 56,
63
+ background: "#fff",
64
+ borderBottom: "1px solid #eee",
65
+ position: "sticky",
66
+ top: 0,
67
+ zIndex: 100,
68
+ },
69
+ logo: {
70
+ fontSize: 18,
71
+ fontWeight: 800,
72
+ color: "#3b82f6",
73
+ textDecoration: "none",
74
+ marginRight: 40,
75
+ letterSpacing: "-0.02em",
76
+ },
77
+ nav: {
78
+ display: "flex",
79
+ gap: 4,
80
+ },
81
+ navLink: {
82
+ padding: "6px 16px",
83
+ borderRadius: 6,
84
+ fontSize: 14,
85
+ color: "#666",
86
+ textDecoration: "none",
87
+ transition: "all 0.15s ease",
88
+ },
89
+ navLinkActive: {
90
+ color: "#3b82f6",
91
+ background: "rgba(59, 130, 246, 0.08)",
92
+ fontWeight: 500,
93
+ },
94
+ main: {
95
+ flex: 1,
96
+ padding: "24px 32px",
97
+ },
98
+ footer: {
99
+ textAlign: "center",
100
+ padding: "16px 0",
101
+ fontSize: 13,
102
+ color: "#aaa",
103
+ borderTop: "1px solid #eee",
104
+ },
105
+ };
@@ -1,47 +1,175 @@
1
- import { Typography, Space, Card } from "antd";
2
1
  import { Link } from "react-router";
3
2
 
4
- const { Title, Paragraph } = Typography;
3
+ const CARDS = [
4
+ {
5
+ title: "快速上手 →",
6
+ desc: "了解框架核心概念,5 分钟创建你的第一个页面。",
7
+ to: "/dashboard",
8
+ },
9
+ {
10
+ title: "路由约定 →",
11
+ desc: "基于文件系统的约定式路由,零配置即可使用。",
12
+ to: "/user",
13
+ },
14
+ {
15
+ title: "配置文档 →",
16
+ desc: "通过 ywkf.config.ts 灵活定制构建、代理等行为。",
17
+ href: "https://github.com/nicepkg/ywkf-cli",
18
+ },
19
+ {
20
+ title: "插件生态 →",
21
+ desc: "React Query、Zustand、Tailwind、Garfish 等开箱即用。",
22
+ href: "https://github.com/nicepkg/ywkf-cli",
23
+ },
24
+ ];
5
25
 
6
- export default function HomePage() {
26
+ export default function WelcomePage() {
7
27
  return (
8
- <div style={{ maxWidth: 720, margin: "0 auto" }}>
9
- <Title>Welcome to YWKF Framework</Title>
10
- <Paragraph>
11
- 应用由 <code>@4399ywkf/core</code> 驱动,采用 Modern.js 风格的文件约定路由。
12
- </Paragraph>
28
+ <div style={styles.wrapper}>
29
+ {/* Hero */}
30
+ <div style={styles.hero}>
31
+ <div style={styles.gradient} />
32
+ <div style={styles.heroContent}>
33
+ <h1 style={styles.heading}>
34
+ <span style={styles.headingLight}>欢迎使用</span>
35
+ <br />
36
+ <span style={styles.brand}>YWKF Framework</span>
37
+ </h1>
38
+ <p style={styles.hint}>
39
+ 开始编辑{" "}
40
+ <code style={styles.code}>src/pages/page.tsx</code>
41
+ </p>
42
+ </div>
43
+ </div>
13
44
 
14
- <Space direction="vertical" size="middle" style={{ width: "100%" }}>
15
- <Card title="路由文件约定">
16
- <ul>
17
- <li><code>page.tsx</code> 页面内容</li>
18
- <li><code>layout.tsx</code> — 布局组件(使用 Outlet)</li>
19
- <li><code>loading.tsx</code> — 加载状态(Suspense fallback)</li>
20
- <li><code>error.tsx</code> — 错误边界</li>
21
- <li><code>$.tsx</code> — 通配 / 404 路由</li>
22
- </ul>
23
- </Card>
45
+ {/* Cards */}
46
+ <div style={styles.cardGrid}>
47
+ {CARDS.map((card) => {
48
+ const inner = (
49
+ <>
50
+ <h3 style={styles.cardTitle}>{card.title}</h3>
51
+ <p style={styles.cardDesc}>{card.desc}</p>
52
+ </>
53
+ );
24
54
 
25
- <Card title="目录约定">
26
- <ul>
27
- <li><code>[id]/</code> — 动态路由(<code>/:id</code>)</li>
28
- <li><code>[id$]/</code> — 可选动态路由(<code>/:id?</code>)</li>
29
- <li><code>[...slug]/</code> — 全匹配路由(<code>/*</code>)</li>
30
- <li><code>__auth/</code> — 无路径布局(路由分组,不生成 URL 片段)</li>
31
- <li><code>user.profile/</code> — 扁平路由(<code>/user/profile</code>)</li>
32
- </ul>
33
- </Card>
34
-
35
- <Card title="本模板包含的示例路由">
36
- <ul>
37
- <li><Link to="/">/</Link> — 首页(当前页面)</li>
38
- <li><Link to="/dashboard">/dashboard</Link> — 工作台(嵌套布局 + 子路由)</li>
39
- <li><Link to="/user">/user</Link> — 用户列表</li>
40
- <li><Link to="/user/42">/user/42</Link> — 动态路由示例</li>
41
- <li><Link to="/not-exist">/not-exist</Link> — 404 通配路由</li>
42
- </ul>
43
- </Card>
44
- </Space>
55
+ return card.href ? (
56
+ <a
57
+ key={card.title}
58
+ href={card.href}
59
+ target="_blank"
60
+ rel="noopener noreferrer"
61
+ style={styles.card}
62
+ >
63
+ {inner}
64
+ </a>
65
+ ) : (
66
+ <Link key={card.title} to={card.to!} style={styles.card}>
67
+ {inner}
68
+ </Link>
69
+ );
70
+ })}
71
+ </div>
45
72
  </div>
46
73
  );
47
74
  }
75
+
76
+ const styles: Record<string, React.CSSProperties> = {
77
+ wrapper: {
78
+ display: "flex",
79
+ flexDirection: "column",
80
+ alignItems: "center",
81
+ minHeight: "calc(100vh - 128px)",
82
+ fontFamily:
83
+ '-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif',
84
+ },
85
+ hero: {
86
+ position: "relative",
87
+ width: "100%",
88
+ display: "flex",
89
+ justifyContent: "center",
90
+ paddingTop: 80,
91
+ paddingBottom: 60,
92
+ overflow: "hidden",
93
+ },
94
+ gradient: {
95
+ position: "absolute",
96
+ top: -120,
97
+ left: "50%",
98
+ transform: "translateX(-50%)",
99
+ width: 800,
100
+ height: 400,
101
+ borderRadius: "50%",
102
+ background:
103
+ "radial-gradient(ellipse at center, rgba(56, 189, 248, 0.15) 0%, rgba(59, 130, 246, 0.08) 40%, transparent 70%)",
104
+ pointerEvents: "none",
105
+ },
106
+ heroContent: {
107
+ position: "relative",
108
+ textAlign: "center",
109
+ zIndex: 1,
110
+ },
111
+ heading: {
112
+ margin: 0,
113
+ lineHeight: 1.2,
114
+ fontWeight: 800,
115
+ letterSpacing: "-0.02em",
116
+ },
117
+ headingLight: {
118
+ fontSize: 20,
119
+ fontWeight: 400,
120
+ color: "#666",
121
+ letterSpacing: "0.05em",
122
+ },
123
+ brand: {
124
+ fontSize: 48,
125
+ background: "linear-gradient(135deg, #3b82f6, #06b6d4)",
126
+ WebkitBackgroundClip: "text",
127
+ WebkitTextFillColor: "transparent",
128
+ },
129
+ hint: {
130
+ marginTop: 24,
131
+ fontSize: 16,
132
+ color: "#888",
133
+ },
134
+ code: {
135
+ padding: "4px 12px",
136
+ borderRadius: 6,
137
+ background: "#f1f5f9",
138
+ border: "1px solid #e2e8f0",
139
+ fontFamily: '"SF Mono", "Fira Code", "Fira Mono", Menlo, monospace',
140
+ fontSize: 14,
141
+ color: "#334155",
142
+ },
143
+ cardGrid: {
144
+ display: "grid",
145
+ gridTemplateColumns: "repeat(4, 1fr)",
146
+ gap: 20,
147
+ maxWidth: 960,
148
+ width: "100%",
149
+ padding: "0 24px",
150
+ marginTop: 20,
151
+ },
152
+ card: {
153
+ display: "block",
154
+ padding: "24px 20px",
155
+ borderRadius: 12,
156
+ border: "1px solid #e5e7eb",
157
+ background: "#fff",
158
+ textDecoration: "none",
159
+ color: "inherit",
160
+ transition: "all 0.2s ease",
161
+ cursor: "pointer",
162
+ },
163
+ cardTitle: {
164
+ margin: "0 0 8px 0",
165
+ fontSize: 17,
166
+ fontWeight: 600,
167
+ color: "#1a1a1a",
168
+ },
169
+ cardDesc: {
170
+ margin: 0,
171
+ fontSize: 14,
172
+ lineHeight: 1.6,
173
+ color: "#666",
174
+ },
175
+ };
@@ -0,0 +1,79 @@
1
+ /**
2
+ * HTTP 请求配置
3
+ *
4
+ * 框架会在 .ywkf/request.ts 中读取此配置并应用到 Axios 实例。
5
+ * 你可以自由修改此文件来定制请求行为。
6
+ *
7
+ * 使用方式:
8
+ * import request from "@ywkf/request";
9
+ * const res = await request.get<Result<User[]>>("/api/users");
10
+ */
11
+ import type { RequestConfig, Result } from "@4399ywkf/core/runtime";
12
+ import { message } from "antd";
13
+
14
+ const requestConfig: RequestConfig = {
15
+ // API 基础路径,优先从环境变量读取
16
+ // baseURL: import.meta.env.VITE_API_BASE_URL || "/api",
17
+
18
+ timeout: 15000,
19
+
20
+ getToken() {
21
+ return localStorage.getItem("token");
22
+ },
23
+
24
+ // Token 前缀,默认 "Bearer",设为 "" 则不添加前缀
25
+ // tokenPrefix: "Bearer",
26
+
27
+ // 请求拦截器 — 在 Token 注入之后执行
28
+ // requestInterceptor(config) {
29
+ // // 可在此添加签名、自定义 header 等
30
+ // return config;
31
+ // },
32
+
33
+ responseInterceptor(response: any) {
34
+ const { data } = response;
35
+
36
+ if (data && typeof data.code === "number") {
37
+ const { code, message: msg } = data as Result;
38
+
39
+ if (code === 0) {
40
+ return data.data;
41
+ }
42
+
43
+ if (code === 401 || code === 999) {
44
+ message.error("登录过期,请重新登录");
45
+ // window.location.href = "/login";
46
+ return Promise.reject(data);
47
+ }
48
+
49
+ message.error(msg || `请求失败 (${code})`);
50
+ return Promise.reject(data);
51
+ }
52
+
53
+ return data;
54
+ },
55
+
56
+ errorHandler(error: any) {
57
+ const status = error?.response?.status;
58
+
59
+ switch (status) {
60
+ case 401:
61
+ message.error("未授权,请重新登录");
62
+ break;
63
+ case 403:
64
+ message.error("无访问权限");
65
+ break;
66
+ case 500:
67
+ message.error("服务器内部错误");
68
+ break;
69
+ default:
70
+ if (!error?.response) {
71
+ message.error("网络异常,请检查网络连接");
72
+ }
73
+ }
74
+
75
+ return Promise.reject(error);
76
+ },
77
+ };
78
+
79
+ export default requestConfig;
@@ -35,6 +35,18 @@ export default defineConfig({
35
35
  dropConsole: process.env.NODE_ENV === "production",
36
36
  },
37
37
 
38
+ // 自定义 Rspack 配置(按需启用)
39
+ // tools: {
40
+ // rspack(config, { isDev }) {
41
+ // // 添加自定义 module.rules
42
+ // config.module?.rules?.push({
43
+ // test: /\.yaml$/,
44
+ // type: "json",
45
+ // });
46
+ // return config;
47
+ // },
48
+ // },
49
+
38
50
  plugins: [
39
51
  reactQueryPlugin(),
40
52
  zustandPlugin(),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@4399ywkf/cli",
3
- "version": "2.0.6",
3
+ "version": "2.0.8",
4
4
  "description": "运维开发部脚手架",
5
5
  "main": "dist/index.js",
6
6
  "bin": {
@@ -20,9 +20,12 @@
20
20
  "build": "rollup -c && node scripts/postbuild.js",
21
21
  "build:watch": "rollup -c -w",
22
22
  "prepublishOnly": "npm run build",
23
- "release:core": "pnpm --filter @4399ywkf/core release",
24
- "release:core:minor": "pnpm --filter @4399ywkf/core release:minor",
25
- "release:core:major": "pnpm --filter @4399ywkf/core release:major",
23
+ "publish:all": "bash scripts/publish.sh",
24
+ "publish:core": "bash scripts/publish.sh -t core",
25
+ "publish:cli": "bash scripts/publish.sh -t cli",
26
+ "publish:minor": "bash scripts/publish.sh -b minor",
27
+ "publish:major": "bash scripts/publish.sh -b major",
28
+ "publish:dry": "bash scripts/publish.sh -d",
26
29
  "link": "npm link",
27
30
  "unlink": "npm unlink -g",
28
31
  "changeset": "changeset",