@liujitcn/kratos-uni-app-system 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.
package/README.md ADDED
@@ -0,0 +1,31 @@
1
+ # @liujitcn/kratos-uni-app-system
2
+
3
+ `@liujitcn/kratos-uni-app-system` 是内置 system 业务模块。个人中心、个人资料、设置和 AI 能力都归属本模块,并沿用 core 的登录态、请求、导航和页面装配协议。
4
+
5
+ ## 目录结构
6
+
7
+ ```text
8
+ packages/modules/system
9
+ ├── src
10
+ │ ├── api/base # AI 消息、会话和工具接口
11
+ │ ├── rpc # system 相关 Buf 生成类型
12
+ │ ├── views
13
+ │ │ ├── pages/my # 个人中心主页
14
+ │ │ └── pagesMember # AI、个人资料和设置页面
15
+ │ ├── index.ts # systemModule 定义和公开入口
16
+ │ └── pages.ts # 页面样式与分包声明
17
+ ├── tsconfig.json
18
+ └── package.json
19
+ ```
20
+
21
+ `src/rpc` 是生成产物,只能通过 workspace 根目录的 `pnpm generate:rpc` 更新;该命令委托 backend 的 `make ts-app` 和 `backend/api/buf.app.core.typescript.gen.yaml` 生成。
22
+
23
+ ## 功能
24
+
25
+ - 注册 `PROFILE_HOME`、`PROFILE`、`SETTINGS` 和 `AI` 四个稳定 `viewKey`。
26
+ - 提供个人中心、资料维护、应用设置和 AI 助手页面。
27
+ - 通过 core 的公开 service、store 和工具复用底座能力。
28
+ - 允许后续模块按相同物理路由或 `viewKey` 覆盖 system 静态视图。
29
+ - npm 根入口使用 `dist/index.mjs`;workspace 的 Turbo 任务和 `prepare:modules` 会先构建 system 及其依赖的 core Node 入口,干净安装后不依赖历史 dist。
30
+
31
+ 模块装配与动态导航约定见 [workspace 文档](../../../README.md)。
package/dist/index.mjs ADDED
@@ -0,0 +1,93 @@
1
+ // ../../core/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
+ // ../../core/src/module.ts
27
+ function defineKratosAppModule(module) {
28
+ return module;
29
+ }
30
+ var coreModule = defineKratosAppModule({
31
+ name: "@liujitcn/kratos-uni-app-core",
32
+ pages: corePages,
33
+ views: {
34
+ HOME: "pages/index/index",
35
+ LOGIN: "pages/login/login",
36
+ PROTOCOL: "pages/login/protocal",
37
+ WEBVIEW: "pages/webview/webview",
38
+ BOOTSTRAP_LOADING: "pages/status/index",
39
+ FORBIDDEN: "pages/status/index",
40
+ NOT_FOUND: "pages/status/index",
41
+ OFFLINE: "pages/status/index",
42
+ CONFIG_ERROR: "pages/status/index",
43
+ PAGE_UNAVAILABLE: "pages/status/index"
44
+ },
45
+ icons: {
46
+ HOME_DEFAULT: "static/tabs/home_default.png",
47
+ HOME_SELECTED: "static/tabs/home_selected.png",
48
+ USER_DEFAULT: "static/tabs/user_default.png",
49
+ USER_SELECTED: "static/tabs/user_selected.png"
50
+ }
51
+ });
52
+
53
+ // src/pages.ts
54
+ var systemPages = {
55
+ "pages/my/my": {
56
+ style: {
57
+ navigationStyle: "custom",
58
+ navigationBarTextStyle: "white",
59
+ navigationBarTitleText: "\u6211\u7684"
60
+ }
61
+ },
62
+ "pagesMember/settings/settings": { style: { navigationBarTitleText: "\u8BBE\u7F6E" } },
63
+ "pagesMember/ai/index": {
64
+ style: {
65
+ navigationStyle: "custom",
66
+ navigationBarTitleText: "AI \u52A9\u624B"
67
+ }
68
+ },
69
+ "pagesMember/profile/profile": {
70
+ style: {
71
+ navigationStyle: "custom",
72
+ navigationBarTextStyle: "white",
73
+ navigationBarTitleText: "\u4E2A\u4EBA\u4FE1\u606F"
74
+ }
75
+ }
76
+ };
77
+
78
+ // src/index.ts
79
+ var systemModule = defineKratosAppModule({
80
+ name: "@liujitcn/kratos-uni-app-system",
81
+ pages: systemPages,
82
+ views: {
83
+ PROFILE_HOME: "pages/my/my",
84
+ PROFILE: "pagesMember/profile/profile",
85
+ SETTINGS: "pagesMember/settings/settings",
86
+ AI: "pagesMember/ai/index"
87
+ }
88
+ });
89
+ var src_default = systemModule;
90
+ export {
91
+ src_default as default,
92
+ systemModule
93
+ };
package/package.json ADDED
@@ -0,0 +1,38 @@
1
+ {
2
+ "name": "@liujitcn/kratos-uni-app-system",
3
+ "description": "Kratos uni-app system module with profile, settings, and AI views.",
4
+ "version": "0.0.1",
5
+ "type": "module",
6
+ "files": [
7
+ "dist",
8
+ "src",
9
+ "README.md"
10
+ ],
11
+ "exports": {
12
+ ".": {
13
+ "types": "./src/index.ts",
14
+ "import": "./dist/index.mjs",
15
+ "default": "./dist/index.mjs"
16
+ },
17
+ "./views/*": "./src/views/*",
18
+ "./package.json": "./package.json"
19
+ },
20
+ "publishConfig": {
21
+ "access": "public"
22
+ },
23
+ "dependencies": {
24
+ "@dcloudio/uni-app": "3.0.0-5010520260709002",
25
+ "@dcloudio/uni-ui": "^1.4.28",
26
+ "vue": "^3.4.21",
27
+ "@liujitcn/kratos-uni-app-core": "^0.0.1"
28
+ },
29
+ "devDependencies": {
30
+ "esbuild": "^0.20.2",
31
+ "vue-tsc": "^1.8.8"
32
+ },
33
+ "scripts": {
34
+ "build": "pnpm run build:entries && pnpm pack --pack-destination ../../../dist/npm",
35
+ "build:entries": "esbuild src/index.ts --bundle --platform=node --format=esm --packages=external --outfile=dist/index.mjs",
36
+ "tsc": "vue-tsc --noEmit -p tsconfig.json"
37
+ }
38
+ }
@@ -0,0 +1,373 @@
1
+ import {
2
+ getRequestAccessToken,
3
+ handleAuthExpired,
4
+ http,
5
+ requestBaseURL,
6
+ } from '@liujitcn/kratos-uni-app-core/utils/http'
7
+ import type { ListAiMessageRequest, ListAiMessageResponse } from '../../rpc/base/v1/ai_session'
8
+ import type {
9
+ AiMessageService,
10
+ DeleteAiMessageRequest,
11
+ DeleteAiMessageResponse,
12
+ RegenerateAiMessageRequest,
13
+ RetryAiUserMessageRequest,
14
+ SendAiMessageRequest,
15
+ SendAiMessageResponse,
16
+ UpdateAiMessageRequest,
17
+ } from '../../rpc/base/v1/ai_message'
18
+
19
+ const AI_SESSION_URL = '/v1/base/ai/session'
20
+
21
+ /** 微信小程序 chunked stream 分片处理选项。 */
22
+ export type AiMessageChunkedStreamOptions = {
23
+ onChunk: (chunkText: string) => void
24
+ }
25
+
26
+ /** 微信小程序 chunked stream 请求任务。 */
27
+ export type AiMessageChunkedStreamTask = {
28
+ promise: Promise<void>
29
+ abort: () => void
30
+ }
31
+
32
+ /** direct stream 请求控制选项。 */
33
+ export type AiMessageStreamOptions = {
34
+ /** 外部取消信号,用于页面卸载或会话删除时终止流式请求。 */
35
+ signal?: AbortSignal
36
+ }
37
+
38
+ type ChunkReceivedResult = {
39
+ data?: ArrayBuffer
40
+ }
41
+
42
+ type ChunkedRequestTask = UniNamespace.RequestTask & {
43
+ onChunkReceived?: (listener: (result: ChunkReceivedResult) => void) => number
44
+ }
45
+
46
+ /** 从 direct stream 错误响应中提取后端业务提示。 */
47
+ async function resolveStreamErrorMessage(response: Response): Promise<string> {
48
+ const fallbackMessage = `AI 助手请求失败(${response.status})`
49
+ const contentType = response.headers.get('Content-Type') ?? ''
50
+ if (contentType.includes('application/json')) {
51
+ try {
52
+ const payload = await response.json()
53
+ return String(payload?.message || payload?.error || fallbackMessage)
54
+ } catch {
55
+ return fallbackMessage
56
+ }
57
+ }
58
+
59
+ try {
60
+ const text = (await response.text()).trim()
61
+ return text || fallbackMessage
62
+ } catch {
63
+ return fallbackMessage
64
+ }
65
+ }
66
+
67
+ /** 使用 direct stream 发送 AI 助手消息,并返回原始 Fetch Response 供调用方消费。 */
68
+ export async function SendAiMessageStream(
69
+ request: SendAiMessageRequest,
70
+ options?: AiMessageStreamOptions,
71
+ ): Promise<Response> {
72
+ const accessToken = await getRequestAccessToken()
73
+ const headers: Record<string, string> = {
74
+ Accept: 'text/event-stream',
75
+ 'Content-Type': 'application/json;charset=utf-8',
76
+ 'source-client': 'miniapp',
77
+ }
78
+ if (accessToken) {
79
+ headers.Authorization = accessToken
80
+ }
81
+
82
+ const response = await fetch(`${requestBaseURL}${AI_SESSION_URL}/${request.session_id}/message`, {
83
+ method: 'POST',
84
+ headers,
85
+ body: JSON.stringify(request),
86
+ signal: options?.signal,
87
+ })
88
+
89
+ // direct stream 不经过 uni.request 拦截器,需要在这里补齐登录失效处理。
90
+ if (response.status === 401 || response.status === 403) {
91
+ handleAuthExpired('required')
92
+ throw new Error('登录状态已失效,请重新登录')
93
+ }
94
+ if (!response.ok) {
95
+ throw new Error(await resolveStreamErrorMessage(response))
96
+ }
97
+
98
+ return response
99
+ }
100
+
101
+ /** 使用微信小程序 chunked request 发送 AI 助手消息并增量返回 SSE 文本。 */
102
+ export function StreamAiMessageByChunkedRequest(
103
+ request: SendAiMessageRequest,
104
+ options: AiMessageChunkedStreamOptions,
105
+ ): AiMessageChunkedStreamTask {
106
+ let requestTask: ChunkedRequestTask | undefined
107
+ let aborted = false
108
+ let receivedChunk = false
109
+ const decoder = createChunkTextDecoder()
110
+
111
+ const promise = (async () => {
112
+ const accessToken = await getRequestAccessToken()
113
+ if (aborted) {
114
+ return
115
+ }
116
+
117
+ await new Promise<void>((resolve, reject) => {
118
+ requestTask = uni.request({
119
+ url: `${requestBaseURL}${AI_SESSION_URL}/${request.session_id}/message`,
120
+ method: 'POST',
121
+ data: request,
122
+ dataType: 'text',
123
+ responseType: 'arraybuffer',
124
+ enableChunked: true,
125
+ timeout: 120000,
126
+ header: {
127
+ Accept: 'text/event-stream',
128
+ 'Content-Type': 'application/json;charset=utf-8',
129
+ 'source-client': 'miniapp',
130
+ ...(accessToken ? { Authorization: accessToken } : {}),
131
+ },
132
+ success(res) {
133
+ if (aborted) {
134
+ resolve()
135
+ return
136
+ }
137
+ if (res.statusCode === 401 || res.statusCode === 403) {
138
+ handleAuthExpired('required')
139
+ reject(new Error('登录状态已失效,请重新登录'))
140
+ return
141
+ }
142
+ if (res.statusCode < 200 || res.statusCode >= 300) {
143
+ reject(new Error(resolveChunkedStreamErrorMessage(res)))
144
+ return
145
+ }
146
+
147
+ const tailText = decoder.flush()
148
+ if (tailText) {
149
+ options.onChunk(tailText)
150
+ }
151
+ if (!receivedChunk) {
152
+ const fallbackText = decodeChunkedResponseData(res.data)
153
+ if (fallbackText) {
154
+ options.onChunk(fallbackText)
155
+ }
156
+ }
157
+ resolve()
158
+ },
159
+ fail(error) {
160
+ if (aborted) {
161
+ resolve()
162
+ return
163
+ }
164
+ reject(error)
165
+ },
166
+ }) as ChunkedRequestTask
167
+
168
+ if (typeof requestTask.onChunkReceived !== 'function') {
169
+ requestTask.abort()
170
+ reject(new Error('当前端不支持流式回复'))
171
+ return
172
+ }
173
+
174
+ requestTask.onChunkReceived((result) => {
175
+ if (aborted || !result.data) {
176
+ return
177
+ }
178
+ receivedChunk = true
179
+ const chunkText = decoder.decode(result.data)
180
+ if (chunkText) {
181
+ options.onChunk(chunkText)
182
+ }
183
+ })
184
+ })
185
+ })()
186
+
187
+ return {
188
+ promise,
189
+ abort() {
190
+ aborted = true
191
+ requestTask?.abort()
192
+ },
193
+ }
194
+ }
195
+
196
+ /** AI 助手消息服务。 */
197
+ export class AiMessageServiceImpl implements AiMessageService {
198
+ /** 查询 AI 助手消息列表。 */
199
+ ListAiMessage(request: ListAiMessageRequest): Promise<ListAiMessageResponse> {
200
+ return http<ListAiMessageResponse>({
201
+ url: `${AI_SESSION_URL}/${request.session_id}/message`,
202
+ method: 'GET',
203
+ authMode: 'required',
204
+ data: request,
205
+ })
206
+ }
207
+
208
+ /** 发送 AI 助手消息并等待完整响应。 */
209
+ SendAiMessage(request: SendAiMessageRequest): Promise<SendAiMessageResponse> {
210
+ return http<SendAiMessageResponse>({
211
+ url: `${AI_SESSION_URL}/${request.session_id}/message`,
212
+ method: 'POST',
213
+ authMode: 'required',
214
+ data: request,
215
+ })
216
+ }
217
+
218
+ /** 删除 AI 助手消息。 */
219
+ DeleteAiMessage(request: DeleteAiMessageRequest): Promise<DeleteAiMessageResponse> {
220
+ return http<DeleteAiMessageResponse>({
221
+ url: `${AI_SESSION_URL}/${request.session_id}/message/${request.message_id}`,
222
+ method: 'DELETE',
223
+ authMode: 'required',
224
+ data: request,
225
+ })
226
+ }
227
+
228
+ /** 更新 AI 助手消息文本并重新生成输出。 */
229
+ UpdateAiMessage(request: UpdateAiMessageRequest): Promise<SendAiMessageResponse> {
230
+ return http<SendAiMessageResponse>({
231
+ url: `${AI_SESSION_URL}/${request.session_id}/message/${request.message_id}`,
232
+ method: 'PUT',
233
+ authMode: 'required',
234
+ data: request,
235
+ })
236
+ }
237
+
238
+ /** 重试失败的 AI 助手消息。 */
239
+ RetryAiUserMessage(request: RetryAiUserMessageRequest): Promise<SendAiMessageResponse> {
240
+ return http<SendAiMessageResponse>({
241
+ url: `${AI_SESSION_URL}/${request.session_id}/message/${request.message_id}/retry`,
242
+ method: 'POST',
243
+ authMode: 'required',
244
+ data: request,
245
+ })
246
+ }
247
+
248
+ /** 重新生成 AI 助手输出。 */
249
+ RegenerateAiMessage(request: RegenerateAiMessageRequest): Promise<SendAiMessageResponse> {
250
+ return http<SendAiMessageResponse>({
251
+ url: `${AI_SESSION_URL}/${request.session_id}/message/${request.message_id}/regeneration`,
252
+ method: 'POST',
253
+ authMode: 'required',
254
+ data: request,
255
+ })
256
+ }
257
+
258
+ /** 使用 direct stream 发送 AI 助手消息,并返回原始 Fetch Response 供调用方消费。 */
259
+ StreamAiMessage(
260
+ request: SendAiMessageRequest,
261
+ options?: AiMessageStreamOptions,
262
+ ): Promise<Response> {
263
+ return SendAiMessageStream(request, options)
264
+ }
265
+ }
266
+
267
+ export const defAiMessageService = new AiMessageServiceImpl()
268
+
269
+ function resolveChunkedStreamErrorMessage(response: UniApp.RequestSuccessCallbackResult) {
270
+ const fallbackMessage = `AI 助手请求失败(${response.statusCode})`
271
+ const text = decodeChunkedResponseData(response.data).trim()
272
+ if (!text) {
273
+ return fallbackMessage
274
+ }
275
+
276
+ try {
277
+ const payload = JSON.parse(text) as { message?: string; error?: string }
278
+ return payload.message || payload.error || fallbackMessage
279
+ } catch {
280
+ return text || fallbackMessage
281
+ }
282
+ }
283
+
284
+ function decodeChunkedResponseData(data: unknown) {
285
+ if (typeof data === 'string') {
286
+ return data
287
+ }
288
+ if (data instanceof ArrayBuffer) {
289
+ return createChunkTextDecoder().decode(data)
290
+ }
291
+ if (data && typeof data === 'object') {
292
+ return JSON.stringify(data)
293
+ }
294
+ return ''
295
+ }
296
+
297
+ function createChunkTextDecoder() {
298
+ if (typeof TextDecoder !== 'undefined') {
299
+ const decoder = new TextDecoder('utf-8')
300
+ return {
301
+ decode(data: ArrayBuffer) {
302
+ return decoder.decode(data, { stream: true })
303
+ },
304
+ flush() {
305
+ return decoder.decode()
306
+ },
307
+ }
308
+ }
309
+
310
+ let pendingBytes: number[] = []
311
+ return {
312
+ decode(data: ArrayBuffer) {
313
+ const bytes = [...pendingBytes, ...Array.from(new Uint8Array(data))]
314
+ const boundary = resolveUtf8Boundary(bytes)
315
+ pendingBytes = bytes.slice(boundary)
316
+ return decodeUtf8Bytes(bytes.slice(0, boundary))
317
+ },
318
+ flush() {
319
+ const text = decodeUtf8Bytes(pendingBytes)
320
+ pendingBytes = []
321
+ return text
322
+ },
323
+ }
324
+ }
325
+
326
+ function resolveUtf8Boundary(bytes: number[]) {
327
+ let index = bytes.length - 1
328
+ while (index >= 0 && (bytes[index] & 0xc0) === 0x80) {
329
+ index--
330
+ }
331
+ if (index < 0) {
332
+ return 0
333
+ }
334
+
335
+ const length = resolveUtf8SequenceLength(bytes[index])
336
+ if (length > 1 && bytes.length - index < length) {
337
+ return index
338
+ }
339
+ return bytes.length
340
+ }
341
+
342
+ function resolveUtf8SequenceLength(lead: number) {
343
+ if ((lead & 0x80) === 0) {
344
+ return 1
345
+ }
346
+ if ((lead & 0xe0) === 0xc0) {
347
+ return 2
348
+ }
349
+ if ((lead & 0xf0) === 0xe0) {
350
+ return 3
351
+ }
352
+ if ((lead & 0xf8) === 0xf0) {
353
+ return 4
354
+ }
355
+ return 1
356
+ }
357
+
358
+ function decodeUtf8Bytes(bytes: number[]) {
359
+ if (!bytes.length) {
360
+ return ''
361
+ }
362
+
363
+ let encoded = ''
364
+ for (const byte of bytes) {
365
+ encoded += `%${byte.toString(16).padStart(2, '0')}`
366
+ }
367
+
368
+ try {
369
+ return decodeURIComponent(encoded)
370
+ } catch {
371
+ return String.fromCharCode(...bytes)
372
+ }
373
+ }
@@ -0,0 +1,85 @@
1
+ import { http } from '@liujitcn/kratos-uni-app-core/utils/http'
2
+ import type {
3
+ AiSessionService,
4
+ CreateAiSessionBranchRequest,
5
+ CreateAiSessionBranchResponse,
6
+ CreateAiSessionRequest,
7
+ CreateAiSessionResponse,
8
+ DeleteAiSessionRequest,
9
+ DeleteAiSessionResponse,
10
+ ListAiMessageRequest,
11
+ ListAiMessageResponse,
12
+ ListAiSessionRequest,
13
+ ListAiSessionResponse,
14
+ UpdateAiSessionRequest,
15
+ UpdateAiSessionResponse,
16
+ } from '../../rpc/base/v1/ai_session'
17
+
18
+ const AI_SESSION_URL = '/v1/base/ai/session'
19
+
20
+ /** AI 助手会话服务。 */
21
+ export class AiSessionServiceImpl implements AiSessionService {
22
+ /** 查询 AI 助手会话列表。 */
23
+ ListAiSession(request: ListAiSessionRequest): Promise<ListAiSessionResponse> {
24
+ return http<ListAiSessionResponse>({
25
+ url: AI_SESSION_URL,
26
+ method: 'GET',
27
+ authMode: 'required',
28
+ data: request,
29
+ })
30
+ }
31
+
32
+ /** 创建 AI 助手会话。 */
33
+ CreateAiSession(request: CreateAiSessionRequest): Promise<CreateAiSessionResponse> {
34
+ return http<CreateAiSessionResponse>({
35
+ url: AI_SESSION_URL,
36
+ method: 'POST',
37
+ authMode: 'required',
38
+ data: request,
39
+ })
40
+ }
41
+
42
+ /** 更新 AI 助手会话。 */
43
+ UpdateAiSession(request: UpdateAiSessionRequest): Promise<UpdateAiSessionResponse> {
44
+ return http<UpdateAiSessionResponse>({
45
+ url: `${AI_SESSION_URL}/${request.id}`,
46
+ method: 'PUT',
47
+ authMode: 'required',
48
+ data: request,
49
+ })
50
+ }
51
+
52
+ /** 删除 AI 助手会话。 */
53
+ DeleteAiSession(request: DeleteAiSessionRequest): Promise<DeleteAiSessionResponse> {
54
+ return http<DeleteAiSessionResponse>({
55
+ url: `${AI_SESSION_URL}/${request.id}`,
56
+ method: 'DELETE',
57
+ authMode: 'required',
58
+ data: request,
59
+ })
60
+ }
61
+
62
+ /** 查询 AI 助手消息列表。 */
63
+ ListAiMessage(request: ListAiMessageRequest): Promise<ListAiMessageResponse> {
64
+ return http<ListAiMessageResponse>({
65
+ url: `${AI_SESSION_URL}/${request.session_id}/message`,
66
+ method: 'GET',
67
+ authMode: 'required',
68
+ data: request,
69
+ })
70
+ }
71
+
72
+ /** 从指定消息创建 AI 助手分支会话。 */
73
+ CreateAiSessionBranch(
74
+ request: CreateAiSessionBranchRequest,
75
+ ): Promise<CreateAiSessionBranchResponse> {
76
+ return http<CreateAiSessionBranchResponse>({
77
+ url: `${AI_SESSION_URL}/${request.source_session_id}/branch`,
78
+ method: 'POST',
79
+ authMode: 'required',
80
+ data: request,
81
+ })
82
+ }
83
+ }
84
+
85
+ export const defAiSessionService = new AiSessionServiceImpl()
@@ -0,0 +1,23 @@
1
+ import { http } from '@liujitcn/kratos-uni-app-core/utils/http'
2
+ import type {
3
+ AiToolService,
4
+ ListAiShortcutRequest,
5
+ ListAiShortcutResponse,
6
+ } from '../../rpc/base/v1/ai_tool'
7
+
8
+ const AI_SHORTCUT_URL = '/v1/base/ai/shortcut'
9
+
10
+ /** AI 助手工具服务。 */
11
+ export class AiToolServiceImpl implements AiToolService {
12
+ /** 查询 AI 助手快捷入口列表。 */
13
+ ListAiShortcut(request: ListAiShortcutRequest): Promise<ListAiShortcutResponse> {
14
+ return http<ListAiShortcutResponse>({
15
+ url: AI_SHORTCUT_URL,
16
+ method: 'GET',
17
+ authMode: 'required',
18
+ data: request,
19
+ })
20
+ }
21
+ }
22
+
23
+ export const defAiToolService = new AiToolServiceImpl()
package/src/index.ts ADDED
@@ -0,0 +1,16 @@
1
+ import { defineKratosAppModule } from '@liujitcn/kratos-uni-app-core/module'
2
+ import { systemPages } from './pages'
3
+
4
+ /** system 内置模块,AI 能力也归属 system。 */
5
+ export const systemModule = defineKratosAppModule({
6
+ name: '@liujitcn/kratos-uni-app-system',
7
+ pages: systemPages,
8
+ views: {
9
+ PROFILE_HOME: 'pages/my/my',
10
+ PROFILE: 'pagesMember/profile/profile',
11
+ SETTINGS: 'pagesMember/settings/settings',
12
+ AI: 'pagesMember/ai/index',
13
+ },
14
+ })
15
+
16
+ export default systemModule
package/src/pages.ts ADDED
@@ -0,0 +1,26 @@
1
+ import type { KratosAppPageConfig } from '@liujitcn/kratos-uni-app-core/module'
2
+
3
+ /** system 页面编译配置。 */
4
+ export const systemPages: Record<string, KratosAppPageConfig> = {
5
+ 'pages/my/my': {
6
+ style: {
7
+ navigationStyle: 'custom',
8
+ navigationBarTextStyle: 'white',
9
+ navigationBarTitleText: '我的',
10
+ },
11
+ },
12
+ 'pagesMember/settings/settings': { style: { navigationBarTitleText: '设置' } },
13
+ 'pagesMember/ai/index': {
14
+ style: {
15
+ navigationStyle: 'custom',
16
+ navigationBarTitleText: 'AI 助手',
17
+ },
18
+ },
19
+ 'pagesMember/profile/profile': {
20
+ style: {
21
+ navigationStyle: 'custom',
22
+ navigationBarTextStyle: 'white',
23
+ navigationBarTitleText: '个人信息',
24
+ },
25
+ },
26
+ }