@liujitcn/kratos-uni-app-core 0.0.1

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 (66) hide show
  1. package/README.md +46 -0
  2. package/dist/module.mjs +84 -0
  3. package/dist/vite.mjs +405 -0
  4. package/package.json +58 -0
  5. package/src/api/base/config.ts +20 -0
  6. package/src/api/base/file.ts +79 -0
  7. package/src/api/base/login.ts +87 -0
  8. package/src/api/base/menu.ts +17 -0
  9. package/src/api/base/oauth.ts +149 -0
  10. package/src/api/system/auth.ts +54 -0
  11. package/src/api/system/base_area.ts +34 -0
  12. package/src/api/system/base_dict.ts +24 -0
  13. package/src/bootstrap.ts +23 -0
  14. package/src/components/BootstrapStatus.vue +39 -0
  15. package/src/components/KratosTabBar.vue +73 -0
  16. package/src/index.ts +6 -0
  17. package/src/module.ts +85 -0
  18. package/src/navigation-pattern.d.mts +2 -0
  19. package/src/navigation-pattern.mjs +29 -0
  20. package/src/navigation-tree.mjs +45 -0
  21. package/src/navigation.ts +330 -0
  22. package/src/pages.ts +26 -0
  23. package/src/rpc/base/v1/config.ts +34 -0
  24. package/src/rpc/base/v1/file.ts +66 -0
  25. package/src/rpc/base/v1/login.ts +128 -0
  26. package/src/rpc/base/v1/mcp.ts +20 -0
  27. package/src/rpc/base/v1/oauth.ts +199 -0
  28. package/src/rpc/base/v1/sse.ts +22 -0
  29. package/src/rpc/common/v1/common.ts +71 -0
  30. package/src/rpc/common/v1/enum.ts +71 -0
  31. package/src/rpc/common/v1/types.ts +77 -0
  32. package/src/rpc/google/protobuf/empty.ts +19 -0
  33. package/src/rpc/google/protobuf/wrappers.ts +124 -0
  34. package/src/rpc/system/app/v1/auth.ts +54 -0
  35. package/src/rpc/system/app/v1/base_area.ts +24 -0
  36. package/src/rpc/system/app/v1/base_dict.ts +37 -0
  37. package/src/rpc/system/app/v1/base_menu.ts +61 -0
  38. package/src/rpc/system/common/v1/enum.ts +80 -0
  39. package/src/static/images/avatar.png +0 -0
  40. package/src/static/images/center_bg.png +0 -0
  41. package/src/static/images/logo_icon.png +0 -0
  42. package/src/static/images/navigator_bg.png +0 -0
  43. package/src/static/tabs/home_default.png +0 -0
  44. package/src/static/tabs/home_selected.png +0 -0
  45. package/src/static/tabs/user_default.png +0 -0
  46. package/src/static/tabs/user_selected.png +0 -0
  47. package/src/stores/index.ts +14 -0
  48. package/src/stores/modules/setting.ts +56 -0
  49. package/src/stores/modules/user.ts +266 -0
  50. package/src/styles/base.scss +42 -0
  51. package/src/types/env.d.ts +24 -0
  52. package/src/types/go-captcha-uni.d.ts +7 -0
  53. package/src/types/uni-helper.d.ts +54 -0
  54. package/src/uni.scss +76 -0
  55. package/src/utils/auth.ts +67 -0
  56. package/src/utils/file.ts +95 -0
  57. package/src/utils/http.ts +430 -0
  58. package/src/utils/index.ts +79 -0
  59. package/src/utils/navigation.ts +81 -0
  60. package/src/utils/passwordCrypto.ts +275 -0
  61. package/src/views/pages/index/index.vue +227 -0
  62. package/src/views/pages/login/login.vue +1133 -0
  63. package/src/views/pages/login/protocal.vue +176 -0
  64. package/src/views/pages/status/index.vue +36 -0
  65. package/src/views/pages/webview/webview.vue +136 -0
  66. package/src/vite.ts +474 -0
package/README.md ADDED
@@ -0,0 +1,46 @@
1
+ # @liujitcn/kratos-uni-app-core
2
+
3
+ `@liujitcn/kratos-uni-app-core` 是应用底座包,提供模块协议、启动流程、动态导航、认证与请求、共享状态、基础页面和构建期页面装配能力。业务模块只能通过本包公开 exports 使用这些能力。
4
+
5
+ ## 目录结构
6
+
7
+ ```text
8
+ packages/core
9
+ ├── src
10
+ │ ├── api # base、system 基础接口 service
11
+ │ ├── components # 启动状态和自绘 tabBar
12
+ │ ├── rpc # Buf 生成的 TypeScript 接口类型
13
+ │ ├── static # 底座图片和 tab 图标
14
+ │ ├── stores # Pinia 设置与用户状态
15
+ │ ├── styles # 全局基础样式
16
+ │ ├── types # uni-app 与第三方类型补充
17
+ │ ├── utils # HTTP、认证、文件和导航工具
18
+ │ ├── views # 首页、登录、状态页和 WebView
19
+ │ ├── bootstrap.ts # 应用初始化
20
+ │ ├── module.ts # 模块定义与注册表
21
+ │ ├── navigation.ts # base-menu 导航解析与缓存
22
+ │ ├── pages.ts # core 页面声明
23
+ │ └── vite.ts # 自动页面装配插件
24
+ ├── test # 导航匹配和 Vite 装配测试
25
+ ├── tsconfig.json
26
+ └── package.json
27
+ ```
28
+
29
+ `src/rpc` 是生成产物,只能通过 workspace 根目录的 `pnpm generate:rpc` 更新;该命令委托 backend 的 `make ts-app` 和 `backend/api/buf.app.typescript.gen.yaml` 生成。
30
+
31
+ ## 功能
32
+
33
+ - 定义 `KratosAppModule`、`viewKey`、静态页面覆盖和模块图标协议。
34
+ - 从 `/v1/app/base/menu` 加载 `999` 下的扁平菜单,按 `parent_id` 构建树,以二级页面生成 tabBar,并原子更新动态路由和层级导航。
35
+ - 按匿名态、登录态缓存导航,并在远端不可用时回退到最近缓存或默认菜单。
36
+ - 统一认证、token、HTTP、上传、配置、Pinia 和基础页面能力。
37
+ - 构建时扫描模块 `src/views/**/*.vue`,逐文件合并模块 static,生成宿主页并在退出时按事务精确恢复。
38
+
39
+ ## 公开入口
40
+
41
+ - `@liujitcn/kratos-uni-app-core`:运行时、导航、store 和组件。
42
+ - `@liujitcn/kratos-uni-app-core/module`:模块协议和 core 模块。
43
+ - `@liujitcn/kratos-uni-app-core/vite`:Vite 配置与页面装配插件。
44
+ - `@liujitcn/kratos-uni-app-core/api/*`、`components/*.vue`、`utils/*`、`views/*`:白名单子路径。
45
+
46
+ 构建与验证命令见 [workspace 文档](../../README.md)。
@@ -0,0 +1,84 @@
1
+ // src/pages.ts
2
+ var corePages = {
3
+ "pages/index/index": {
4
+ style: {
5
+ navigationStyle: "custom",
6
+ navigationBarTextStyle: "white",
7
+ navigationBarTitleText: "\u9996\u9875"
8
+ }
9
+ },
10
+ "pages/login/login": { style: { navigationBarTitleText: "\u767B\u5F55" } },
11
+ "pages/login/protocal": { style: { navigationBarTitleText: "\u534F\u8BAE\u8BE6\u60C5" } },
12
+ "pages/webview/webview": {
13
+ style: {
14
+ navigationBarTitleText: "",
15
+ "app-plus": { titleNView: false }
16
+ }
17
+ },
18
+ "pages/status/index": {
19
+ style: {
20
+ navigationStyle: "custom",
21
+ navigationBarTitleText: ""
22
+ }
23
+ }
24
+ };
25
+
26
+ // src/module.ts
27
+ function defineKratosAppModule(module) {
28
+ return module;
29
+ }
30
+ var registeredModules = [];
31
+ function registerKratosAppModules(modules) {
32
+ registeredModules = [...modules];
33
+ }
34
+ function getRegisteredKratosAppModules() {
35
+ return [...registeredModules];
36
+ }
37
+ function resolveStaticView(viewKey) {
38
+ for (let index = registeredModules.length - 1; index >= 0; index -= 1) {
39
+ const route = registeredModules[index].views[viewKey];
40
+ if (route)
41
+ return route;
42
+ }
43
+ }
44
+ function resolveModuleIcon(icon) {
45
+ if (!icon || /^https:\/\//.test(icon))
46
+ return icon;
47
+ for (let index = registeredModules.length - 1; index >= 0; index -= 1) {
48
+ const source = registeredModules[index].icons?.[icon];
49
+ if (source) {
50
+ const base = import.meta.env.BASE_URL || "/";
51
+ return `${base.replace(/\/?$/, "/")}${source.replace(/^\/+/, "")}`;
52
+ }
53
+ }
54
+ }
55
+ var coreModule = defineKratosAppModule({
56
+ name: "@liujitcn/kratos-uni-app-core",
57
+ pages: corePages,
58
+ views: {
59
+ HOME: "pages/index/index",
60
+ LOGIN: "pages/login/login",
61
+ PROTOCOL: "pages/login/protocal",
62
+ WEBVIEW: "pages/webview/webview",
63
+ BOOTSTRAP_LOADING: "pages/status/index",
64
+ FORBIDDEN: "pages/status/index",
65
+ NOT_FOUND: "pages/status/index",
66
+ OFFLINE: "pages/status/index",
67
+ CONFIG_ERROR: "pages/status/index",
68
+ PAGE_UNAVAILABLE: "pages/status/index"
69
+ },
70
+ icons: {
71
+ HOME_DEFAULT: "static/tabs/home_default.png",
72
+ HOME_SELECTED: "static/tabs/home_selected.png",
73
+ USER_DEFAULT: "static/tabs/user_default.png",
74
+ USER_SELECTED: "static/tabs/user_selected.png"
75
+ }
76
+ });
77
+ export {
78
+ coreModule,
79
+ defineKratosAppModule,
80
+ getRegisteredKratosAppModules,
81
+ registerKratosAppModules,
82
+ resolveModuleIcon,
83
+ resolveStaticView
84
+ };
package/dist/vite.mjs ADDED
@@ -0,0 +1,405 @@
1
+ // src/vite.ts
2
+ import {
3
+ copyFileSync,
4
+ existsSync,
5
+ mkdirSync,
6
+ readFileSync,
7
+ readdirSync,
8
+ rmdirSync,
9
+ rmSync,
10
+ statSync,
11
+ unlinkSync,
12
+ writeFileSync
13
+ } from "node:fs";
14
+ import { createRequire } from "node:module";
15
+ import { dirname, isAbsolute, relative, resolve } from "node:path";
16
+ import uniPlugin from "@dcloudio/vite-plugin-uni";
17
+ import { defineConfig, loadEnv } from "vite";
18
+ var sourceVersionCounter = 0;
19
+ function createKratosUniPlugin() {
20
+ const createPlugin = typeof uniPlugin === "function" ? uniPlugin : uniPlugin.default;
21
+ return createPlugin();
22
+ }
23
+ function kratosApp(options) {
24
+ const sourceVersion = `${process.pid}-${Date.now()}-${++sourceVersionCounter}`;
25
+ const state = {
26
+ generatedFiles: [],
27
+ generatedStaticFiles: [],
28
+ cleaned: false,
29
+ persistent: false,
30
+ moduleRoots: /* @__PURE__ */ new Map()
31
+ };
32
+ const cleanup = () => {
33
+ if (state.cleaned)
34
+ return;
35
+ state.cleaned = true;
36
+ process.removeListener("exit", cleanup);
37
+ process.removeListener("SIGINT", handleSigint);
38
+ process.removeListener("SIGTERM", handleSigterm);
39
+ for (const file of [...state.generatedFiles].reverse()) {
40
+ if (existsSync(file))
41
+ unlinkSync(file);
42
+ removeEmptyParents(dirname(file), state.inputDir ?? "");
43
+ }
44
+ if (state.inputDir) {
45
+ removeGeneratedStaticFiles(state.generatedStaticFiles, resolve(state.inputDir, "static"));
46
+ }
47
+ if (state.pagesFile && state.originalPages !== void 0) {
48
+ writeFileSync(state.pagesFile, state.originalPages);
49
+ }
50
+ if (state.transactionFile && existsSync(state.transactionFile)) {
51
+ unlinkSync(state.transactionFile);
52
+ }
53
+ };
54
+ const handleSigint = () => {
55
+ cleanup();
56
+ process.exit(130);
57
+ };
58
+ const handleSigterm = () => {
59
+ cleanup();
60
+ process.exit(143);
61
+ };
62
+ return {
63
+ name: "kratos-uni-app-pages",
64
+ enforce: "pre",
65
+ configResolved(config) {
66
+ state.persistent = config.command === "serve" || Boolean(config.build.watch);
67
+ },
68
+ resolveId(source, importer) {
69
+ const [sourcePath, query = ""] = source.split("?", 2);
70
+ let target;
71
+ if (sourcePath === "@liujitcn/kratos-uni-app-core" || sourcePath.startsWith("@liujitcn/kratos-uni-app-core/")) {
72
+ const coreRoot = state.moduleRoots.get("@liujitcn/kratos-uni-app-core");
73
+ if (coreRoot) {
74
+ target = resolveWorkspacePackageSource(
75
+ coreRoot,
76
+ sourcePath.slice("@liujitcn/kratos-uni-app-core".length).replace(/^\/+/, "")
77
+ );
78
+ }
79
+ } else if (sourcePath === "@liujitcn/kratos-uni-app-system" || sourcePath.startsWith("@liujitcn/kratos-uni-app-system/")) {
80
+ const systemRoot = state.moduleRoots.get("@liujitcn/kratos-uni-app-system");
81
+ if (systemRoot) {
82
+ target = resolveWorkspacePackageSource(
83
+ systemRoot,
84
+ sourcePath.slice("@liujitcn/kratos-uni-app-system".length).replace(/^\/+/, "")
85
+ );
86
+ }
87
+ } else if (sourcePath.startsWith("@kratos-uni-app-system-source/")) {
88
+ const systemRoot = state.moduleRoots.get("@liujitcn/kratos-uni-app-system");
89
+ if (systemRoot) {
90
+ target = resolve(
91
+ systemRoot,
92
+ "src",
93
+ sourcePath.slice("@kratos-uni-app-system-source/".length)
94
+ );
95
+ }
96
+ } else if (sourcePath.startsWith("@kratos-uni-app-core-source/")) {
97
+ const coreRoot = state.moduleRoots.get("@liujitcn/kratos-uni-app-core");
98
+ if (coreRoot) {
99
+ target = resolve(coreRoot, "src", sourcePath.slice("@kratos-uni-app-core-source/".length));
100
+ }
101
+ } else if (sourcePath.startsWith(".") && importer) {
102
+ const importerPath = importer.split("?", 1)[0];
103
+ const moduleRoot = [...state.moduleRoots.values()].find(
104
+ (root) => isWithinRoot(resolve(root, "src"), importerPath)
105
+ );
106
+ if (moduleRoot)
107
+ target = resolve(dirname(importerPath), sourcePath);
108
+ }
109
+ if (!target)
110
+ return;
111
+ const resolved = resolveSourceFile(target);
112
+ const importQuery = query || dependencyVersionQuery(importer, sourceVersion);
113
+ return `${resolved}${importQuery ? `?${importQuery}` : ""}`;
114
+ },
115
+ transform(code, id) {
116
+ const sourceFile = id.split("?", 1)[0];
117
+ const isModuleSource = [...state.moduleRoots.values()].some(
118
+ (root) => isWithinRoot(resolve(root, "src"), sourceFile)
119
+ );
120
+ const isLinkedModuleSource = sourceFile.includes("/@liujitcn/kratos-uni-app-core/") || sourceFile.includes("/@liujitcn/kratos-uni-app-system/");
121
+ if (!isModuleSource && !isLinkedModuleSource)
122
+ return;
123
+ const transformed = code.replace(/@system\//g, "@kratos-uni-app-system-source/").replace(/@\//g, "@kratos-uni-app-core-source/");
124
+ if (transformed === code)
125
+ return;
126
+ return { code: transformed, map: null };
127
+ },
128
+ config() {
129
+ const inputDir = process.env.UNI_INPUT_DIR || resolve(process.cwd(), "src");
130
+ const pagesFile = resolve(inputDir, "pages.json");
131
+ const transactionFile = resolve(inputDir, "../.kratos-uni-app-pages-state.json");
132
+ recoverBuildTransaction(transactionFile, inputDir, pagesFile);
133
+ const hostRequire = createRequire(resolve(inputDir, "../package.json"));
134
+ const originalPages = readFileSync(pagesFile, "utf8");
135
+ const manifest = JSON.parse(originalPages);
136
+ manifest.pages ??= [];
137
+ const pageMap = /* @__PURE__ */ new Map();
138
+ const moduleRoots = options.modules.map((module) => {
139
+ const linkedRoot = resolve(inputDir, "../node_modules", ...module.name.split("/"));
140
+ return {
141
+ module,
142
+ root: existsSync(resolve(linkedRoot, "package.json")) ? linkedRoot : dirname(hostRequire.resolve(`${module.name}/package.json`))
143
+ };
144
+ });
145
+ state.moduleRoots = new Map(moduleRoots.map(({ module, root }) => [module.name, root]));
146
+ moduleRoots.forEach(({ module, root }) => {
147
+ scanViews(resolve(root, "src/views")).forEach((page) => {
148
+ const config = module.pages[page.route] ?? {};
149
+ const route = normalizeRoute(config.route ?? page.route);
150
+ pageMap.set(route, {
151
+ route,
152
+ source: `${module.name}/views/${page.route}.vue`,
153
+ style: config.style
154
+ });
155
+ });
156
+ });
157
+ state.inputDir = inputDir;
158
+ state.pagesFile = pagesFile;
159
+ state.originalPages = originalPages;
160
+ state.transactionFile = transactionFile;
161
+ state.generatedFiles = [...pageMap.values()].map((page) => resolve(inputDir, `${page.route}.vue`)).filter((target) => !existsSync(target));
162
+ const staticTarget = resolve(inputDir, "static");
163
+ const staticFiles = collectModuleStaticFiles(
164
+ moduleRoots.map(({ root }) => resolve(root, "src/static")),
165
+ staticTarget
166
+ );
167
+ state.generatedStaticFiles = staticFiles.map(({ target }) => target);
168
+ writeFileSync(
169
+ transactionFile,
170
+ `${JSON.stringify(
171
+ {
172
+ inputDir,
173
+ pagesFile,
174
+ originalPages,
175
+ generatedFiles: state.generatedFiles,
176
+ generatedStaticFiles: state.generatedStaticFiles,
177
+ ownerPid: process.pid
178
+ },
179
+ null,
180
+ 2
181
+ )}
182
+ `,
183
+ { flag: "wx" }
184
+ );
185
+ pageMap.forEach((page) => {
186
+ const target = resolve(inputDir, `${page.route}.vue`);
187
+ if (existsSync(target))
188
+ return;
189
+ mkdirSync(dirname(target), { recursive: true });
190
+ writeFileSync(target, createPageWrapper(page));
191
+ appendPage(manifest, page);
192
+ });
193
+ staticFiles.forEach(({ source, target }) => {
194
+ mkdirSync(dirname(target), { recursive: true });
195
+ copyFileSync(source, target);
196
+ });
197
+ writeFileSync(pagesFile, `${JSON.stringify(manifest, null, 2)}
198
+ `);
199
+ process.once("exit", cleanup);
200
+ process.prependOnceListener("SIGINT", handleSigint);
201
+ process.prependOnceListener("SIGTERM", handleSigterm);
202
+ return {
203
+ resolve: {
204
+ alias: [
205
+ {
206
+ find: "@kratos-uni-app-core-source",
207
+ replacement: resolve(
208
+ state.moduleRoots.get("@liujitcn/kratos-uni-app-core") ?? "",
209
+ "src"
210
+ )
211
+ },
212
+ {
213
+ find: "@kratos-uni-app-system-source",
214
+ replacement: resolve(
215
+ state.moduleRoots.get("@liujitcn/kratos-uni-app-system") ?? "",
216
+ "src"
217
+ )
218
+ }
219
+ ]
220
+ },
221
+ optimizeDeps: {
222
+ exclude: options.modules.map((module) => module.name)
223
+ }
224
+ };
225
+ },
226
+ configureServer(server) {
227
+ server.httpServer?.once("close", cleanup);
228
+ },
229
+ closeBundle() {
230
+ if (!state.persistent)
231
+ cleanup();
232
+ },
233
+ closeWatcher: cleanup
234
+ };
235
+ }
236
+ function recoverBuildTransaction(transactionFile, inputDir, pagesFile) {
237
+ if (!existsSync(transactionFile))
238
+ return;
239
+ const transaction = JSON.parse(readFileSync(transactionFile, "utf8"));
240
+ if (transaction.inputDir !== inputDir || transaction.pagesFile !== pagesFile) {
241
+ throw new Error(`uni-app \u6784\u5EFA\u4E8B\u52A1\u76EE\u5F55\u4E0D\u5339\u914D\uFF1A${transactionFile}`);
242
+ }
243
+ if (transaction.ownerPid !== void 0 && transaction.ownerPid !== process.pid && isProcessRunning(transaction.ownerPid)) {
244
+ throw new Error(
245
+ `\u9875\u9762\u88C5\u914D\u5DF2\u7531\u8FDB\u7A0B ${transaction.ownerPid} \u4F7F\u7528\uFF0C\u8BF7\u5148\u505C\u6B62\u53E6\u4E00\u4E2A H5 \u6216\u5C0F\u7A0B\u5E8F\u5F00\u53D1\u8FDB\u7A0B`
246
+ );
247
+ }
248
+ for (const file of [...transaction.generatedFiles].reverse()) {
249
+ if (existsSync(file))
250
+ unlinkSync(file);
251
+ removeEmptyParents(dirname(file), inputDir);
252
+ }
253
+ removeGeneratedStaticFiles(transaction.generatedStaticFiles ?? [], resolve(inputDir, "static"));
254
+ if (transaction.generatedStatic && transaction.generatedStaticFiles === void 0) {
255
+ rmSync(resolve(inputDir, "static"), { recursive: true, force: true });
256
+ }
257
+ writeFileSync(pagesFile, transaction.originalPages);
258
+ unlinkSync(transactionFile);
259
+ }
260
+ function isProcessRunning(pid) {
261
+ try {
262
+ process.kill(pid, 0);
263
+ return true;
264
+ } catch (error) {
265
+ return error.code === "EPERM";
266
+ }
267
+ }
268
+ function collectModuleStaticFiles(sourceRoots, targetRoot) {
269
+ const fileMap = /* @__PURE__ */ new Map();
270
+ sourceRoots.forEach((sourceRoot) => {
271
+ scanFiles(sourceRoot).forEach((source) => {
272
+ const target = resolve(targetRoot, relative(sourceRoot, source));
273
+ if (!existsSync(target))
274
+ fileMap.set(target, { source, target });
275
+ });
276
+ });
277
+ return [...fileMap.values()];
278
+ }
279
+ function scanFiles(root) {
280
+ if (!existsSync(root))
281
+ return [];
282
+ const files = [];
283
+ const visit = (directory) => {
284
+ readdirSync(directory).forEach((entry) => {
285
+ const path = resolve(directory, entry);
286
+ if (statSync(path).isDirectory())
287
+ visit(path);
288
+ else
289
+ files.push(path);
290
+ });
291
+ };
292
+ visit(root);
293
+ return files;
294
+ }
295
+ function removeGeneratedStaticFiles(files, staticRoot) {
296
+ for (const file of [...files].reverse()) {
297
+ if (existsSync(file))
298
+ unlinkSync(file);
299
+ removeEmptyParents(dirname(file), staticRoot);
300
+ }
301
+ }
302
+ function scanViews(root) {
303
+ if (!existsSync(root))
304
+ return [];
305
+ const files = [];
306
+ const visit = (directory) => {
307
+ readdirSync(directory).forEach((entry) => {
308
+ const path = resolve(directory, entry);
309
+ if (statSync(path).isDirectory()) {
310
+ if (entry !== "components")
311
+ visit(path);
312
+ } else if (entry.endsWith(".vue")) {
313
+ files.push(path);
314
+ }
315
+ });
316
+ };
317
+ visit(root);
318
+ return files.map((source) => ({
319
+ route: normalizeRoute(relative(root, source)),
320
+ source
321
+ }));
322
+ }
323
+ function appendPage(manifest, page) {
324
+ const [first, ...rest] = page.route.split("/");
325
+ if (!first.startsWith("pages") || first === "pages") {
326
+ manifest.pages.push({ path: page.route, style: page.style });
327
+ return;
328
+ }
329
+ manifest.subPackages ??= [];
330
+ let subPackage = manifest.subPackages.find((item) => item.root === first);
331
+ if (!subPackage) {
332
+ subPackage = { root: first, pages: [] };
333
+ manifest.subPackages.push(subPackage);
334
+ }
335
+ subPackage.pages.push({ path: rest.join("/"), style: page.style });
336
+ }
337
+ function createPageWrapper(page) {
338
+ const source = page.source.replace(/\\/g, "/");
339
+ return `<script setup lang="ts">
340
+ import KratosPage from ${JSON.stringify(source)}
341
+ import KratosTabBar from '@liujitcn/kratos-uni-app-core/components/KratosTabBar.vue'
342
+
343
+ defineOptions({ inheritAttrs: false })
344
+ </script>
345
+
346
+ <template>
347
+ <KratosPage />
348
+ <KratosTabBar route=${JSON.stringify(page.route)} />
349
+ </template>
350
+ `;
351
+ }
352
+ function normalizeRoute(route) {
353
+ return route.replace(/\\/g, "/").replace(/^\/+/, "").replace(/\.vue$/, "");
354
+ }
355
+ function removeEmptyParents(directory, boundary) {
356
+ let current = directory;
357
+ while (current !== boundary && current.startsWith(boundary) && existsSync(current)) {
358
+ if (readdirSync(current).length)
359
+ return;
360
+ rmdirSync(current);
361
+ current = dirname(current);
362
+ }
363
+ }
364
+ function resolveSourceFile(target) {
365
+ const candidates = [
366
+ target,
367
+ `${target}.ts`,
368
+ `${target}.js`,
369
+ `${target}.vue`,
370
+ resolve(target, "index.ts"),
371
+ resolve(target, "index.js")
372
+ ];
373
+ const resolved = candidates.find((candidate) => existsSync(candidate) && statSync(candidate).isFile()) ?? target;
374
+ if (!isAbsolute(resolved))
375
+ throw new Error(`\u6A21\u5757\u6E90\u7801\u8DEF\u5F84\u4E0D\u662F\u7EDD\u5BF9\u8DEF\u5F84\uFF1A${resolved}`);
376
+ return resolved;
377
+ }
378
+ function dependencyVersionQuery(importer, sourceVersion) {
379
+ const query = importer?.split("?", 2)[1] ?? "";
380
+ const version = new URLSearchParams(query).get("v");
381
+ const params = new URLSearchParams();
382
+ if (version)
383
+ params.set("v", version);
384
+ params.set("kratos", sourceVersion);
385
+ return params.toString();
386
+ }
387
+ function resolveWorkspacePackageSource(packageRoot, subpath) {
388
+ if (!subpath)
389
+ return resolve(packageRoot, "src/index.ts");
390
+ if (subpath === "module")
391
+ return resolve(packageRoot, "src/module.ts");
392
+ if (subpath === "stores")
393
+ return resolve(packageRoot, "src/stores/index.ts");
394
+ return resolve(packageRoot, "src", subpath);
395
+ }
396
+ function isWithinRoot(root, target) {
397
+ const relativePath = relative(root, target);
398
+ return relativePath === "" || !relativePath.startsWith("..") && !isAbsolute(relativePath);
399
+ }
400
+ export {
401
+ createKratosUniPlugin,
402
+ defineConfig,
403
+ kratosApp,
404
+ loadEnv
405
+ };
package/package.json ADDED
@@ -0,0 +1,58 @@
1
+ {
2
+ "name": "@liujitcn/kratos-uni-app-core",
3
+ "description": "Runtime, navigation, authentication, and build foundation for Kratos apps.",
4
+ "version": "0.0.1",
5
+ "type": "module",
6
+ "files": [
7
+ "dist",
8
+ "src",
9
+ "README.md"
10
+ ],
11
+ "exports": {
12
+ ".": "./src/index.ts",
13
+ "./module": {
14
+ "types": "./src/module.ts",
15
+ "import": "./dist/module.mjs",
16
+ "default": "./dist/module.mjs"
17
+ },
18
+ "./api/*": "./src/api/*.ts",
19
+ "./components/*.vue": "./src/components/*.vue",
20
+ "./rpc/*": "./src/rpc/*",
21
+ "./stores": "./src/stores/index.ts",
22
+ "./static/*": "./src/static/*",
23
+ "./styles/base.scss": "./src/styles/base.scss",
24
+ "./uni.scss": "./src/uni.scss",
25
+ "./utils/*": "./src/utils/*.ts",
26
+ "./views/*": "./src/views/*",
27
+ "./vite": {
28
+ "types": "./src/vite.ts",
29
+ "import": "./dist/vite.mjs",
30
+ "default": "./dist/vite.mjs"
31
+ },
32
+ "./package.json": "./package.json"
33
+ },
34
+ "publishConfig": {
35
+ "access": "public"
36
+ },
37
+ "dependencies": {
38
+ "@dcloudio/uni-app": "3.0.0-5010520260709002",
39
+ "@dcloudio/uni-ui": "^1.4.28",
40
+ "@dcloudio/vite-plugin-uni": "3.0.0-5010520260709002",
41
+ "asmcrypto.js": "^2.3.2",
42
+ "go-captcha-uni": "1.0.6",
43
+ "pinia": "^2.0.27",
44
+ "pinia-plugin-persistedstate": "^3.2.0",
45
+ "vite": "5.2.8",
46
+ "vue": "^3.4.21"
47
+ },
48
+ "devDependencies": {
49
+ "@types/node": "^18.11.9",
50
+ "esbuild": "^0.20.2",
51
+ "vue-tsc": "^1.8.8"
52
+ },
53
+ "scripts": {
54
+ "build": "pnpm run build:entries && pnpm pack --pack-destination ../../dist/npm",
55
+ "build:entries": "esbuild src/module.ts --bundle --platform=node --format=esm --packages=external --outfile=dist/module.mjs && esbuild src/vite.ts --bundle --platform=node --format=esm --packages=external --outfile=dist/vite.mjs",
56
+ "tsc": "vue-tsc --noEmit -p tsconfig.json"
57
+ }
58
+ }
@@ -0,0 +1,20 @@
1
+ import { http } from '../../utils/http'
2
+ import type { ConfigService, GetConfigRequest, GetConfigResponse } from '../../rpc/base/v1/config'
3
+
4
+ const CONFIG_URL = '/v1/base/config'
5
+
6
+ /** 系统配置公共服务 */
7
+ export class ConfigServiceImpl implements ConfigService {
8
+ /** 获取系统配置 */
9
+ GetConfig(request: GetConfigRequest): Promise<GetConfigResponse> {
10
+ return http<GetConfigResponse>({
11
+ url: `${CONFIG_URL}`,
12
+ method: 'GET',
13
+ authMode: 'none',
14
+ data: request,
15
+ header: { Authorization: 'no-auth' },
16
+ })
17
+ }
18
+ }
19
+
20
+ export const defConfigService = new ConfigServiceImpl()
@@ -0,0 +1,79 @@
1
+ import { http } from '../../utils/http'
2
+ import type { DownloadFileRequest, MultiUploadFileResponse, FileInfo } from '../../rpc/base/v1/file'
3
+
4
+ const FILE_URL = '/v1/base/file'
5
+
6
+ /** 文件服务 */
7
+ export class FileServiceImpl {
8
+ /** 多个文件上传 */
9
+ MultiUploadFile(files: File[], fileType: string): Promise<MultiUploadFileResponse> {
10
+ const formData = new FormData()
11
+ files.map((file) => {
12
+ formData.append(file.name, file)
13
+ })
14
+ formData.append('fileType', fileType)
15
+ return http<MultiUploadFileResponse>({
16
+ url: `${FILE_URL}/multi`,
17
+ method: 'POST',
18
+ authMode: 'required',
19
+ data: formData,
20
+ header: {
21
+ 'Content-Type': 'multipart/form-data',
22
+ },
23
+ })
24
+ }
25
+ /** 单个文件上传 */
26
+ UploadFile(file: File, fileType: string): Promise<FileInfo> {
27
+ const formData = new FormData()
28
+ formData.append('file', file)
29
+ formData.append('fileType', fileType)
30
+ return http<FileInfo>({
31
+ url: `${FILE_URL}`,
32
+ method: 'POST',
33
+ authMode: 'required',
34
+ data: formData,
35
+ header: {
36
+ 'Content-Type': 'multipart/form-data',
37
+ },
38
+ })
39
+ }
40
+ /** 下载文件 */
41
+
42
+ async DownloadFile(file: string, fileName: string) {
43
+ try {
44
+ const response = await http<any>({
45
+ url: `${FILE_URL}`,
46
+ method: 'GET',
47
+ authMode: 'required',
48
+ data: {
49
+ name: fileName,
50
+ path: file,
51
+ } as DownloadFileRequest,
52
+ responseType: 'blob', // 明确要求返回 Blob 类型
53
+ })
54
+ // 获取文件名
55
+ const contentDisposition = response.headers['content-disposition']
56
+ let downloadFilename = fileName
57
+ if (contentDisposition) {
58
+ const match = contentDisposition.match(/filename="?(.+?)"?$/)
59
+ if (match && match[1]) {
60
+ downloadFilename = match[1]
61
+ }
62
+ }
63
+
64
+ // 创建下载链接
65
+ const url = window.URL.createObjectURL(new Blob([response.data]))
66
+ const a = document.createElement('a')
67
+ a.href = url
68
+ a.download = downloadFilename
69
+ document.body.appendChild(a)
70
+ a.click()
71
+ document.body.removeChild(a)
72
+ window.URL.revokeObjectURL(url)
73
+ } catch (error) {
74
+ console.error('下载错误:', error)
75
+ }
76
+ }
77
+ }
78
+
79
+ export const defFileService = new FileServiceImpl()