Skip to content

Commit 40a35f0

Browse files
committed
feat:切换为前端接口
1 parent 17508a7 commit 40a35f0

25 files changed

+5689
-11802
lines changed

template/lowcode-designer/package.json

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,12 @@
2222
"@opentiny/vue-locale": "~3.20.0",
2323
"@opentiny/vue-renderless": "~3.20.0",
2424
"@opentiny/vue-theme": "~3.20.0",
25+
"@opentiny/vue-theme-mobile": "~3.20.0",
2526
"@vueuse/core": "^9.6.0",
27+
"dexie": "^4.2.2",
28+
"monaco-editor": "0.51.0",
29+
"axios": "^1.12.0",
30+
"axios-mock-adapter": "^2.1.0",
2631
"vue": "^3.4.21"
2732
},
2833
"devDependencies": {

template/lowcode-designer/pnpm-lock.yaml

Lines changed: 0 additions & 11668 deletions
This file was deleted.
Lines changed: 235 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,235 @@
1+
import { defineService, META_SERVICE } from '@opentiny/tiny-engine';
2+
import axios from 'axios';
3+
import AxiosMockAdapter from 'axios-mock-adapter';
4+
5+
let http = null;
6+
let mock = null;
7+
8+
const createInterceptorHandler =
9+
(http) =>
10+
({ data, type }) => {
11+
if (typeof data === 'function') {
12+
http.interceptors[type].use(data);
13+
14+
return;
15+
}
16+
17+
if (Array.isArray(data)) {
18+
data.forEach((item) => {
19+
if (!item) return;
20+
21+
if (Array.isArray(item)) {
22+
http.interceptors[type].use(...item);
23+
24+
return;
25+
}
26+
27+
if (typeof item === 'function') {
28+
http.interceptors[type].use(item);
29+
}
30+
});
31+
}
32+
};
33+
34+
export default defineService({
35+
id: META_SERVICE.Http,
36+
type: 'MetaService',
37+
options: {
38+
axiosConfig: {
39+
// axios 配置
40+
baseURL: '',
41+
withCredentials: false, // 跨域请求时是否需要使用凭证
42+
headers: {}, // 请求头
43+
},
44+
interceptors: {
45+
// 拦截器
46+
request: [], // 支持配置多个请求拦截器,先注册后执行
47+
response: [], // 支持配置多个响应拦截器,先注册先执行
48+
},
49+
mockConfig: [],
50+
},
51+
init: ({ options = {} }) => {
52+
const { axiosConfig = {}, interceptors = {}, enableMock } = options;
53+
const { request = [], response = [] } = interceptors;
54+
55+
http = axios.create(axiosConfig);
56+
if (enableMock) {
57+
mock = new AxiosMockAdapter(http);
58+
mock.onGet(/\/mock\/bundle\.json$/).passThrough();
59+
mock.onPost(/\/app-center\/api\/ai\/chat/).passThrough();
60+
61+
http.interceptors.request.use((config) => {
62+
const AI_PATH = '/app-center/api/ai/chat';
63+
64+
if (config.url === AI_PATH) {
65+
config.url = `/tiny-engine${AI_PATH}`; // 修改路径
66+
}
67+
return config;
68+
});
69+
70+
mock.onAny().reply(async (config) => {
71+
const { mockConfig = [] } = options;
72+
// 构建完整 URL(包含 baseURL)
73+
const fullUrl = (config.baseURL || '') + (config.url || '');
74+
const method = (config.method || 'GET').toUpperCase();
75+
76+
const mockItem = mockConfig.find((item) => {
77+
// 方法匹配
78+
if (item.method && method !== item.method.toUpperCase()) {
79+
return false;
80+
}
81+
82+
// URL 匹配 - 同时检查 config.url 和 fullUrl
83+
if (typeof item.url === 'string') {
84+
return (
85+
item.url === config.url ||
86+
item.url === fullUrl ||
87+
config.url?.includes(item.url) ||
88+
fullUrl.includes(item.url)
89+
);
90+
}
91+
92+
if (item.url instanceof RegExp) {
93+
return item.url.test(config.url) || item.url.test(fullUrl);
94+
}
95+
96+
return false;
97+
});
98+
99+
if (mockItem) {
100+
if (typeof mockItem.response === 'function') {
101+
return mockItem.response(config);
102+
}
103+
return mockItem.response;
104+
}
105+
106+
// 如果没有匹配到,输出调试信息
107+
console.warn(
108+
`[Mock] 未匹配到接口: ${method} ${fullUrl || config.url}`,
109+
'可用路由:',
110+
mockConfig.map((item) => `${item.method || 'ANY'} ${item.url}`)
111+
);
112+
113+
return [
114+
200,
115+
{
116+
code: 200,
117+
errMsg:
118+
'当前 demo 暂未支持该接口,请前往GitHub 或者 Gitee 克隆项目完整体验',
119+
error:
120+
'当前 demo 暂未支持该接口,请前往GitHub 或者 Gitee 克隆项目完整体验',
121+
},
122+
];
123+
});
124+
}
125+
126+
const addInterceptors = createInterceptorHandler(http);
127+
addInterceptors({ data: request, type: 'request' });
128+
addInterceptors({ data: response, type: 'response' });
129+
},
130+
apis: () => ({
131+
getHttp: () => http,
132+
getMock: () => mock,
133+
get: (...args) => http?.get(...args),
134+
post: (...args) => http?.post(...args),
135+
request: (...args) => http?.request(...args),
136+
put: (...args) => http?.put(...args),
137+
delete: (...args) => http?.delete(...args),
138+
setOptions: (options) => {
139+
// 支持动态设置选项
140+
if (options.axiosConfig) {
141+
Object.assign(http.defaults, options.axiosConfig);
142+
}
143+
if (options.interceptors) {
144+
const addInterceptors = createInterceptorHandler(http);
145+
if (options.interceptors.request) {
146+
addInterceptors({
147+
data: options.interceptors.request,
148+
type: 'request',
149+
});
150+
}
151+
if (options.interceptors.response) {
152+
addInterceptors({
153+
data: options.interceptors.response,
154+
type: 'response',
155+
});
156+
}
157+
}
158+
// 重新初始化 mock(如果启用)
159+
if (options.enableMock && options.mockConfig) {
160+
// 如果 mock 已存在,先恢复
161+
if (mock) {
162+
mock.restore();
163+
}
164+
mock = new AxiosMockAdapter(http);
165+
mock.onGet(/\/mock\/bundle\.json$/).passThrough();
166+
mock.onPost(/\/app-center\/api\/ai\/chat/).passThrough();
167+
168+
http.interceptors.request.use((config) => {
169+
const AI_PATH = '/app-center/api/ai/chat';
170+
171+
if (config.url === AI_PATH) {
172+
config.url = `/tiny-engine${AI_PATH}`;
173+
}
174+
return config;
175+
});
176+
177+
mock.onAny().reply(async (config) => {
178+
// 构建完整 URL(包含 baseURL)
179+
const fullUrl = (config.baseURL || '') + (config.url || '');
180+
const method = (config.method || 'GET').toUpperCase();
181+
182+
const mockItem = options.mockConfig.find((item) => {
183+
// 方法匹配
184+
if (item.method && method !== item.method.toUpperCase()) {
185+
return false;
186+
}
187+
188+
// URL 匹配 - 同时检查 config.url 和 fullUrl
189+
if (typeof item.url === 'string') {
190+
return (
191+
item.url === config.url ||
192+
item.url === fullUrl ||
193+
config.url?.includes(item.url) ||
194+
fullUrl.includes(item.url)
195+
);
196+
}
197+
198+
if (item.url instanceof RegExp) {
199+
return item.url.test(config.url) || item.url.test(fullUrl);
200+
}
201+
202+
return false;
203+
});
204+
205+
if (mockItem) {
206+
if (typeof mockItem.response === 'function') {
207+
return mockItem.response(config);
208+
}
209+
return mockItem.response;
210+
}
211+
212+
// 如果没有匹配到,输出调试信息
213+
console.warn(
214+
`[Mock] 未匹配到接口: ${method} ${fullUrl || config.url}`,
215+
'可用路由:',
216+
options.mockConfig.map(
217+
(item) => `${item.method || 'ANY'} ${item.url}`
218+
)
219+
);
220+
221+
return [
222+
200,
223+
{
224+
code: 200,
225+
errMsg:
226+
'当前 demo 暂未支持该接口,请前往GitHub 或者 Gitee 克隆项目完整体验',
227+
error:
228+
'当前 demo 暂未支持该接口,请前往GitHub 或者 Gitee 克隆项目完整体验',
229+
},
230+
];
231+
});
232+
}
233+
},
234+
}),
235+
});

template/lowcode-designer/src/composable/http/index.js

Lines changed: 3 additions & 69 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
import { createApp } from 'vue'
2-
import { HttpService } from '@opentiny/tiny-engine'
32
import { useBroadcastChannel } from '@vueuse/core'
43
import { constants } from '@opentiny/tiny-engine-utils'
4+
import HttpService from './httpServices'
55
import Login from './Login.vue'
6-
import mockConfig from './mockConfig'
6+
import mockConfig from '../../routes'
77

88
const LOGIN_EXPIRED_CODE = 401
99
const { BROADCAST_CHANNEL } = constants
@@ -37,77 +37,12 @@ const preRequest = (config) => {
3737
config.baseURL = ''
3838
}
3939

40-
// 检查是否需要 mock - 纯前端项目,拦截 /api/user/me 接口
41-
const fullUrl = (config.baseURL || '') + (config.url || '')
42-
const urlToCheck = config.url || ''
43-
44-
// 检查是否是 /api/user/me 或 /user/me 请求
45-
const isMeRequest = /\/api\/user\/me$|\/user\/me$/.test(fullUrl) ||
46-
/\/api\/user\/me$|\/user\/me$/.test(urlToCheck)
47-
48-
if (isMeRequest && (config.method || 'GET').toUpperCase() === 'GET') {
49-
50-
// 找到对应的 mock 配置
51-
const mockRoute = mockConfig.find((route) => {
52-
const urlMatch = typeof route.url === 'string'
53-
? fullUrl.includes(route.url) || urlToCheck.includes(route.url)
54-
: route.url.test(fullUrl) || route.url.test(urlToCheck)
55-
const methodMatch = !route.method ||
56-
route.method.toUpperCase() === (config.method || 'GET').toUpperCase()
57-
return urlMatch && methodMatch
58-
})
59-
60-
if (mockRoute) {
61-
// 直接调用 mock 的 response 函数
62-
const mockResult = mockRoute.response(config)
63-
64-
// 如果返回的是 Promise,等待 resolve
65-
if (mockResult && typeof mockResult.then === 'function') {
66-
return mockResult.then(([status, data]) => {
67-
// 返回一个模拟的响应对象
68-
// 注意:axios 请求拦截器返回的应该是 config,但我们可以通过返回一个特殊对象来阻止请求
69-
// 实际上,我们需要修改 config,让它不会发送真实请求
70-
config.adapter = () => {
71-
// 返回一个 Promise,resolve 一个模拟的响应
72-
return Promise.resolve({
73-
data: {
74-
data,
75-
error: null
76-
},
77-
status,
78-
statusText: 'OK',
79-
headers: {},
80-
config
81-
})
82-
}
83-
return config
84-
})
85-
} else {
86-
// 如果 response 不是异步的,直接处理
87-
const [status, data] = mockResult
88-
config.adapter = () => {
89-
return Promise.resolve({
90-
data: {
91-
data,
92-
error: null
93-
},
94-
status,
95-
statusText: 'OK',
96-
headers: {},
97-
config
98-
})
99-
}
100-
return config
101-
}
102-
}
103-
}
104-
10540
return config
10641
}
10742

10843
const preResponse = (res) => {
10944
if (res.data?.error) {
110-
showError(res.config?.url, res?.data?.error?.message)
45+
showError(res.config?.url, res?.data?.error?.message || res?.data?.error)
11146

11247
return Promise.reject(res.data.error)
11348
}
@@ -147,7 +82,6 @@ const openLogin = () => {
14782
}
14883

14984
const errorResponse = (error) => {
150-
// 用户信息失效时,弹窗提示登录
15185
const { response } = error
15286

15387
if (response?.status === LOGIN_EXPIRED_CODE) {

template/lowcode-designer/src/composable/http/mockConfig.js

Lines changed: 0 additions & 45 deletions
This file was deleted.

0 commit comments

Comments
 (0)