-
Notifications
You must be signed in to change notification settings - Fork 22
feat: intercepts the mock returned by the me interface. #171
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: dev
Are you sure you want to change the base?
Conversation
WalkthroughAdds a mock-enabled HTTP client, a Dexie-backed IndexedDB (schema + seeding), many mock route handlers and fixture files, runtime dependency additions, and entry-point updates to initialize the DB and wire the registry for the lowcode-designer template. Changes
Sequence Diagram(s)sequenceDiagram
participant App as Application
participant DB as Dexie (IndexedDB)
participant HTTP as HTTP Service (Axios / MetaService)
participant Mock as AxiosMockAdapter
participant Routes as Mock Route Handlers
App->>DB: await initIndexDB()
DB->>DB: createDB() and seed from mock JSON
App->>HTTP: initialize HTTP service (create axios instance)
HTTP->>Mock: attach AxiosMockAdapter when enableMock=true
HTTP->>Routes: Mock onAny() -> route matcher calls response handler
Routes->>DB: query/update tables (Dexie)
Routes-->>HTTP: return mocked response
HTTP-->>App: resolve request promise
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes
🚥 Pre-merge checks | ✅ 2 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing touches
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 4
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
template/lowcode-designer/src/composable/http/index.js (1)
188-202: Remove unsupported mockConfig and enableMock options from HttpService configuration.Lines 195-196 pass
mockConfigandenableMocktoHttpService.apis.setOptions(), but@opentiny/tiny-engine's HttpService does not support these options. The mock interception logic inpreRequestdirectly uses the importedmockConfigmodule and never reads from the options object. These options are silently ignored and should be removed.Original code snippet
const customizeHttpService = () => { const options = { axiosConfig: getConfig(), interceptors: { request: [preRequest], response: [[preResponse, errorResponse]] }, mockConfig, enableMock: true } HttpService.apis.setOptions(options) return HttpService }
🤖 Fix all issues with AI agents
In @template/lowcode-designer/src/composable/http/index.js:
- Around line 60-85: Remove the leftover debug console.info in the mock handling
path and stop double-wrapping the returned mock payload: when handling
mockRoute.response(config) (the mockResult.then callback) use the resolved data
payload directly instead of nesting it under another "data" key; update the
config.adapter to resolve with the response object whose data is the mock
payload (and preserve status/statusText/headers/config) so the adapter returns
the same shape axios expects without extra wrapping.
- Around line 86-102: The synchronous branch in config.adapter is wrapping the
payload one level too deep (creating a double-nested data structure) — make the
adapter return the same shape as the fixed async path by removing the extra data
wrapper: set the resolved response's data to the object { data: <mock payload>,
error: null } (use the existing mockResult/mock data variable) and keep
status/statusText/headers/config unchanged so the sync and async paths produce
identical response shapes; update the code in the config.adapter closure that
currently uses "data" to follow the async branch structure.
In @template/lowcode-designer/src/composable/http/mockConfig.js:
- Around line 36-46: The mock response is double-wrapped because index.js
already wraps returned payloads, so update the response function in the mock
(the response async arrow function that currently returns [200,
getResponseData(mockUserData)]) to return the raw data instead (e.g., [200,
mockUserData]) so you don't return an already-wrapped structure; you can also
alternatively remove getResponseData usage here if you prefer, but ensure the
response no longer contains the outer { data, error } shape that index.js adds.
- Around line 7-26: The mockUserData object defines both blocked and block
properties which are unused and duplicated; inspect the backend/API schema to
determine the correct field name (likely blocked) and then remove the incorrect
duplicate from mockUserData (or update to match the API if neither is correct),
ensuring only the canonical property remains and updating any tests or consumers
if necessary.
🧹 Nitpick comments (1)
template/lowcode-designer/src/composable/http/index.js (1)
40-47: Consider simplifying the URL matching logic.The code checks the same regex pattern against both
fullUrlandurlToCheck. While this works, it's somewhat redundant. Consider checkingfullUrlfirst as it's the complete URL, and only fall back tourlToCheckif needed, or consolidate the logic.♻️ Potential simplification
- // 检查是否是 /api/user/me 或 /user/me 请求 - const isMeRequest = /\/api\/user\/me$|\/user\/me$/.test(fullUrl) || - /\/api\/user\/me$|\/user\/me$/.test(urlToCheck) + // 检查是否是 /api/user/me 或 /user/me 请求 + const isMeRequest = /\/api\/user\/me$|\/user\/me$/.test(fullUrl || urlToCheck)
📜 Review details
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (3)
package.jsontemplate/lowcode-designer/src/composable/http/index.jstemplate/lowcode-designer/src/composable/http/mockConfig.js
🧰 Additional context used
🪛 GitHub Actions: BackEnd UnitTest
package.json
[warning] 1-1: Field 'resolutions' found in template/tinyvue/package.json will not take effect. Move to the root workspace configuration.
🪛 GitHub Actions: Playwright E2E Tests
package.json
[warning] 1-1: The field "resolutions" was found in package.json. This will not take effect for a mono-repo. Move or configure "resolutions" at the workspace root.
🔇 Additional comments (2)
package.json (1)
49-49: LGTM!The new development script follows the existing pattern and provides a convenient way to run the lowcode-designer template locally.
template/lowcode-designer/src/composable/http/index.js (1)
6-6: LGTM!The import statement correctly brings in the mock configuration.
| export default [ | ||
| // 获取用户信息 | ||
| { | ||
| url: /\/api\/user\/me$|\/user\/me$/, // 使用正则表达式匹配 | ||
| method: 'GET', | ||
| response: async () => { | ||
| console.log('[Lowcode Designer] Mock /api/user/me 接口,返回 mock 数据'); | ||
| return [200, getResponseData(mockUserData)]; | ||
| }, | ||
| }, | ||
| ]; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fix data structure double-nesting issue.
The response function returns [200, getResponseData(mockUserData)], but in index.js (lines 74-76, 91-93), the data is wrapped again in the same structure { data, error: null }. This creates incorrect double nesting:
{
data: {
data: { /* mockUserData */ },
error: null
},
error: null
}The response function should return the raw data without wrapping, as the wrapping is already handled in index.js.
🐛 Proposed fix
export default [
// 获取用户信息
{
url: /\/api\/user\/me$|\/user\/me$/, // 使用正则表达式匹配
method: 'GET',
response: async () => {
console.log('[Lowcode Designer] Mock /api/user/me 接口,返回 mock 数据');
- return [200, getResponseData(mockUserData)];
+ return [200, mockUserData];
},
},
];Alternatively, if you want to keep getResponseData, remove the wrapping in index.js or adjust the structure to avoid double nesting.
🤖 Prompt for AI Agents
In @template/lowcode-designer/src/composable/http/mockConfig.js around lines 36
- 46, The mock response is double-wrapped because index.js already wraps
returned payloads, so update the response function in the mock (the response
async arrow function that currently returns [200,
getResponseData(mockUserData)]) to return the raw data instead (e.g., [200,
mockUserData]) so you don't return an already-wrapped structure; you can also
alternatively remove getResponseData usage here if you prefer, but ensure the
response no longer contains the outer { data, error } shape that index.js adds.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 2
🤖 Fix all issues with AI agents
In @template/lowcode-designer/src/composable/http/mockConfig.js:
- Around line 7-26: The mockUserData.data object contains both blocked and block
properties which are likely redundant; either remove the duplicate (keep
blocked) or, if both are required by the API, add a clarifying comment next to
mockUserData.data explaining the semantic difference and why both blocked and
block are present so future readers understand the intent.
- Around line 36-45: The enableMock flag is currently hardcoded to true in
index.js; change the enableMock assignment (symbol: enableMock in index.js) to
be gated by environment by using getConfig().enableMock with a fallback to the
dev check (import.meta.env.MODE?.includes('dev')) or compute an isDev boolean
and use that instead so mocks only run in development; update the enableMock
assignment accordingly to reference getConfig() or the isDev check rather than
the literal true.
📜 Review details
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (3)
package.jsontemplate/lowcode-designer/src/composable/http/index.jstemplate/lowcode-designer/src/composable/http/mockConfig.js
🚧 Files skipped from review as they are similar to previous changes (2)
- template/lowcode-designer/src/composable/http/index.js
- package.json
🔇 Additional comments (1)
template/lowcode-designer/src/composable/http/mockConfig.js (1)
28-34: LGTM!The response wrapper function is clean and follows a standard pattern for API response formatting.
| export default [ | ||
| // 获取用户信息 | ||
| { | ||
| url: /\/api\/user\/me$|\/user\/me$/, // 使用正则表达式匹配 | ||
| method: 'GET', | ||
| response: async () => { | ||
| return [200, getResponseData(mockUserData)]; | ||
| }, | ||
| }, | ||
| ]; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧩 Analysis chain
🏁 Script executed:
# Search for enableMock configuration and environment checks
rg -n 'enableMock' --type=js --type=json -g '!node_modules'Repository: opentiny/tiny-pro
Length of output: 140
🏁 Script executed:
# Search for environment-based conditionals related to mocking
rg -n -B3 -A3 'NODE_ENV|VITE_|VUE_APP_' template/lowcode-designer/src/composable/http/ --type=jsRepository: opentiny/tiny-pro
Length of output: 642
🏁 Script executed:
# Find where mockConfig.js is imported
rg -n 'mockConfig|from.*mockConfig' template/lowcode-designer/src/composable/http/ --type=jsRepository: opentiny/tiny-pro
Length of output: 339
🏁 Script executed:
# Look at the index.js file in the same directory
cat -n template/lowcode-designer/src/composable/http/index.js | head -50Repository: opentiny/tiny-pro
Length of output: 1743
🏁 Script executed:
# Get context around line 195 where enableMock is set
cat -n template/lowcode-designer/src/composable/http/index.js | sed -n '180,210p'Repository: opentiny/tiny-pro
Length of output: 676
🏁 Script executed:
# Also check if there's any conditional logic around where the config is used/applied
cat -n template/lowcode-designer/src/composable/http/index.js | sed -n '40,70p'Repository: opentiny/tiny-pro
Length of output: 1508
🏁 Script executed:
# Search for how enableMock is used in the codebase
rg -n 'enableMock' --type=js -B5 -A5Repository: opentiny/tiny-pro
Length of output: 915
🏁 Script executed:
# Check if there's a build-time check or other safeguard for mocking in production
rg -n -i 'production|prod.*mock|mock.*prod' template/lowcode-designer/src/composable/http/ --type=jsRepository: opentiny/tiny-pro
Length of output: 43
🏁 Script executed:
# Check the getConfig function more completely to see full environment logic
cat -n template/lowcode-designer/src/composable/http/index.js | sed -n '170,186p'Repository: opentiny/tiny-pro
Length of output: 587
Gate enableMock by development environment.
The enableMock: true setting at line 195 of index.js is hardcoded without environment checks. While getConfig() properly gates development-only features using env.MODE?.includes('dev') (line 173), the mock configuration is unconditionally enabled. Change line 195 to:
enableMock: getConfig().enableMock ?? import.meta.env.MODE?.includes('dev')Or pass the environment check explicitly:
const isDev = import.meta.env.MODE?.includes('dev')
// ...
enableMock: isDevThis ensures mocks never run in production builds.
🤖 Prompt for AI Agents
In @template/lowcode-designer/src/composable/http/mockConfig.js around lines 36
- 45, The enableMock flag is currently hardcoded to true in index.js; change the
enableMock assignment (symbol: enableMock in index.js) to be gated by
environment by using getConfig().enableMock with a fallback to the dev check
(import.meta.env.MODE?.includes('dev')) or compute an isDev boolean and use that
instead so mocks only run in development; update the enableMock assignment
accordingly to reference getConfig() or the isDev check rather than the literal
true.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 6
Note
Due to the large number of review comments, Critical severity comments were prioritized as inline comments.
🤖 Fix all issues with AI agents
In @template/lowcode-designer/src/db/index.js:
- Around line 14-19: The schema registration must happen once at DB construction
to avoid "Cannot add version when database is open"; move the call to
db.version(1).stores(...) out of createDB() and into the Dexie instance setup so
it runs at module initialization (i.e., declare export const db = new
Dexie(...); then call db.version(1).stores({...schema})). Then change createDB()
to simply return the existing db (or remove it) and update initIndexDB() to call
await db.open() (and keep the existing userCount guard for data seeding only),
ensuring no further db.version(...) calls occur after db.open().
In @template/lowcode-designer/src/routes/app.js:
- Around line 80-99: The response handler for the POST update route returns a
Promise because db.appDetail.get(1) is not awaited; update the async response
function to await the call (i.e., change const appDetail = db.appDetail.get(1)
to use await) so getResponseData receives the resolved appDetail object, keeping
the rest of the flow (updates to db.appDetail.update and db.appSchema.update and
the response return) unchanged.
In @template/lowcode-designer/src/routes/block.js:
- Around line 96-144: The list route's regex url:
/\/material-center\/api\/block-groups/ currently also matches requests with
query strings (e.g., ?id=...), causing the detail handler
(/\/material-center\/api\/block-groups\?id=\d+/) to be unreachable; fix by
either tightening the list route to only match the exact path (e.g., require
end-anchor so the list pattern only matches the exact
/material-center/api/block-groups) or by moving the detail handler block (the
GET handler that reads id via URLSearchParams and calls db.blockGroup.get) above
the list handler so the id-specific route is evaluated first.
In @template/lowcode-designer/src/routes/utils.js:
- Around line 7-19: The route handler's response function may return undefined
when config.url has no query string or when category !== 'utils', violating
axios-mock-adapter's reply contract; update the response lambdas (the response:
async (config) => { ... } bodies) to safely parse the query (e.g., use const
query = new URLSearchParams((config.url || '').split('?')[1] || '') or new
URL(config.url, 'http://localhost') fallback) and always return a reply tuple;
for the utils branch return [200, getResponseData(list)] and for other/unknown
categories return a default mock like [200, getResponseData([])] (or a
consistent error tuple), and apply the same pattern to the other similar
handlers that use config.url.split('?')[1], db.utils, and getResponseData.
- Around line 46-67: originData from db.utils.get(id) can be undefined so
spreading it into the update payload will throw; update the response handler in
the POST route (the async response function) to check if originData exists
before calling db.utils.update, and if missing return an appropriate error
response (e.g., [404, getResponseData({ message: 'Record not found' })]) or
reject, otherwise call db.utils.update with a safe merge (e.g., {...(originData
|| {}), ...data, updated_at: time}) and continue returning the updated record
from db.utils.get(id).
🟠 Major comments (15)
template/lowcode-designer/src/mock/blockHistories.json-1-258 (1)
1-258: Align mock history field names with what code writes/reads (e.g.,platformIdvsplatform_id).If consumers expect
platformId(camelCase, as in the schema/routes), seedingplatform_idmay cause missing/undefined values downstream. Consider normalizing the mock to camelCase (or duplicating both keys if you need backward compatibility during transition).template/lowcode-designer/src/preview.js-17-44 (1)
17-44: Handle startup failures:startApp()is async but errors are currently unhandled.Proposed fix
@@ -startApp(); +void startApp().catch((err) => { + // Avoid silent white-screen failures (Dexie open/init, dynamic imports, etc.) + console.error('[designer-demo] failed to start preview app', err); +});template/lowcode-designer/src/routes/page.js-46-135 (1)
46-135: Normalize ID field types in created records (string vs number).In the two create branches,
createdBy/tenantId/siteId/occupierByare numbers in one branch (Line 57-71) and strings in the other (Line 103-117). This can create hard-to-debug mismatches later.template/lowcode-designer/src/db/index.js-21-32 (1)
21-32: Prefer idempotent seeding (put/bulkPut) overaddto avoid ConstraintError.
db.user.add(user)/db.appDetail.add(appDetail)/db.appSchema.add(appSchema)(Line 24-26) will throw if seeding partially happened or if data already exists for any reason. Usingput/bulkPutmakesinitData()safe to rerun (also helpful afterresetDataBase()if anything fails mid-way).Proposed fix
const initData = async () => { // 添加用户 - await db.user.add(user) - await db.appDetail.add(appDetail) - await db.appSchema.add(appSchema) + await db.user.put(user) + await db.appDetail.put(appDetail) + await db.appSchema.put(appSchema) await db.page.bulkPut(pageList) await db.block.bulkPut(blockList) await db.blockGroup.bulkPut(blockGroup) await db.blockHistories.bulkPut(blockHistories) await db.utils.bulkPut(utilsList) }Also applies to: 38-46, 48-55
template/lowcode-designer/src/routes/i18n.js-97-135 (1)
97-135: Validatedata.entriesbefore iterating.
data.entries.forEach(...)(Line 113-116) will throw ifentriesis missing.template/lowcode-designer/src/routes/page.js-26-37 (1)
26-37: Return a consistent response envelope (getResponseData) for update/delete.Update returns
[200, page](Line 35) and delete returns[200, page](Line 146), while other endpoints returngetResponseData(...). This inconsistency will likely break consumers expecting{ code: 200, data: ... }.Proposed fix
response: async (config) => { const id = config.url.split('/').at(-1) await db.page.update(Number(id), JSON.parse(config.data)) const page = await db.page.get(Number(id)) - return [200, page] + return [200, getResponseData(page)] } }, @@ response: async (config) => { const id = Number(config.url.split('/').at(-1)) const page = await db.page.get(id) await db.page.delete(id) - return [200, page] + return [200, getResponseData(page)] } },Also applies to: 136-147
template/lowcode-designer/src/routes/i18n.js-75-95 (1)
75-95: Validatekey_inis an array before usingincludes.
key_in.includes(key)(Line 83-84) will throw ifkey_inis missing/not an array.template/lowcode-designer/src/routes/utils.js-68-79 (1)
68-79: Validateidand missing record on delete.
idcan becomeNaN(Line 73) andrescan beundefined(Line 74). Consider returning a structured error viagetResponseData.template/lowcode-designer/src/routes/block.js-210-242 (1)
210-242: Guard optional arrays (data.groups,groupItem.blocks) to avoid runtime throws.
group.includes(...)(Line 222) will throw ifdata.groupsis missing, andgroupItem.blocks.includes(...)(Line 224/230) will throw ifblocksisn’t initialized. Add defaults likeconst group = data.groups || []andgroupItem.blocks ||= [].template/lowcode-designer/src/routes/block.js-336-356 (1)
336-356: Await block-group updates on delete (currently fire-and-forget).
blockGroup.map(...)(Line 345-350) triggers asyncdb.blockGroup.update(...)withoutawait, so group cleanup can race with subsequent reads.template/lowcode-designer/src/routes/block.js-60-95 (1)
60-95: Don’t useforEach(async ...)without awaiting; updates can be dropped.In group update,
blocks.forEach(async ...)(Line 77-82) isn’t awaited, so block version updates may not complete before the response is returned.Proposed fix
if (blocks) { Object.assign(groupItem, { materialBlocks: blocks }) - - blocks.forEach(async (blockItem) => { - const id = Number(blockItem.id) - const blockDetail = await db.block.get(id) - blockDetail.current_version = blockItem.version - await db.block.update(id, blockDetail) - }) + await Promise.all( + blocks.map(async (blockItem) => { + const id = Number(blockItem.id) + const blockDetail = await db.block.get(id) + if (!blockDetail) return + blockDetail.current_version = blockItem.version + await db.block.update(id, blockDetail) + }) + ) }template/lowcode-designer/src/routes/i18n.js-27-74 (1)
27-74: GuardappSchema.i18nand avoid writingundefinedvalues.
const i18n = appSchema.i18n(Line 33 / 57) and theni18n.en_US[key] = ...(Line 41-42 / 65-66) will throw ifi18nor locale maps are missing. Also, you currently overwrite both locales even if only one was provided, potentially settingundefined.Proposed fix
const appSchema = await db.appSchema.get(1) - const i18n = appSchema.i18n + const i18n = { + en_US: { ...(appSchema?.i18n?.en_US || {}) }, + zh_CN: { ...(appSchema?.i18n?.zh_CN || {}) } + } const { key, contents } = data const { en_US, zh_CN } = contents || {} @@ - i18n.en_US[key] = contents.en_US - i18n.zh_CN[key] = contents.zh_CN + if (en_US !== undefined) i18n.en_US[key] = en_US + if (zh_CN !== undefined) i18n.zh_CN[key] = zh_CNtemplate/lowcode-designer/src/db/index.js-48-55 (1)
48-55: EnsureresetDataBase()opens the DB and uses transactions for atomicity.
resetDataBase()accessesdb.tableswithout ensuring the database is open. Additionally, according to Dexie best practices, table clearing operations should be wrapped in a transaction for atomicity and error safety. Use this pattern instead:export const resetDataBase = async () => { if (!db.isOpen()) await db.open() await db.transaction('rw', ...db.tables, async () => { await Promise.all(db.tables.map(table => table.clear())) }) await initData() }This ensures the DB is open, performs the clear operation atomically within a transaction, and rolls back on error.
template/lowcode-designer/src/composable/http/httpServices.js-56-124 (1)
56-124: Track and eject request interceptor before re-adding insetOptions()when reconfiguring mock.When
setOptions()is called withenableMockenabled, a new request interceptor is registered (lines 168–175) without ejecting the previously added interceptor (frominit()or priorsetOptions()calls). Repeated calls will stack duplicate interceptors, each executing the same AI path rewriting logic. Store the interceptor ID returned byhttp.interceptors.request.use()and callhttp.interceptors.request.eject(id)before adding a new one. Alternatively, avoid re-registering the interceptor insetOptions()if it was already added duringinit().template/lowcode-designer/src/composable/http/httpServices.js-70-123 (1)
70-123: Add defensive return-type validation for mock route responses.Route handlers in
src/routes/utils.js(and potentially others) returnundefinedwhen conditional guards fail (e.g., whencategory !== 'utils'). ThemockItem.response(config)call at lines 101 and 207 directly returns this result without validation. Per axios-mock-adapter documentation, reply callbacks must return[status, data, headers]or a Promise resolving to it—returningundefinedcauses runtime errors. Add validation and a fallback:if (mockItem) { if (typeof mockItem.response === 'function') { const response = await mockItem.response(config); // Validate response is [status, data] tuple if (Array.isArray(response)) { return response; } } else if (Array.isArray(mockItem.response)) { return mockItem.response; } } // Fallback to avoid undefined return return [200, { code: 200, message: 'Mock response incomplete' }];Also fix route handlers to always return a valid response tuple, or move guard logic earlier to prevent undefined returns.
🟡 Minor comments (3)
template/lowcode-designer/src/mock/pageList.json-375-380 (1)
375-380: Duplicate button labels with different values.Lines 375-380 show two buttons with identical text "内存优化型" but different values ("3" and "4"). This is likely a copy-paste error.
Proposed fix
{ "text": "内存优化型", "value": "3" }, { - "text": "内存优化型", + "text": "高性能计算型", "value": "4" },template/lowcode-designer/src/mock/pageList.json-148-153 (1)
148-153: Invalid style value:background-color: [object Event].Line 151 contains an invalid background-color value
"background-color: [object Event]". This appears to be a serialization error where an Event object was accidentally stringified.Proposed fix
{ "componentName": "Text", "props": { "text": "温馨提示:页面左上角切换区域", - "style": "background-color: [object Event]; color: #8a8e99; font-size: 12px;" + "style": "color: #8a8e99; font-size: 12px;" }, "id": "20923497" },template/lowcode-designer/src/mock/utilsList.json-11-18 (1)
11-18: Update axios version to be consistent and current.The
versionfield is empty whilecdnLinkpoints to axios1.0.0, an outdated release from October 2021. These should be consistent with the latest stable version.Suggested fix
"content": { "package": "axios", - "version": "", + "version": "1.13.2", "exportName": "axios", "subName": "", "destructuring": false, "main": "", - "cdnLink": "https://registry.npmmirror.com/axios/1.0.0/files/dist/esm/axios.js" + "cdnLink": "https://registry.npmmirror.com/axios/1.13.2/files/dist/esm/axios.js" },
🧹 Nitpick comments (6)
template/lowcode-designer/src/routes/commonUtils.js (1)
13-26: Consider usingtoLocaleStringor a date library for simpler date formatting.The manual date formatting works correctly, but you could simplify this using built-in methods or a well-established date library.
Alternative implementations
Option 1: Using toLocaleString (zero dependencies):
-export function formatDateTime() { - const date = new Date() - - // 获取各时间组件 - const year = date.getFullYear() - const month = String(date.getMonth() + 1).padStart(2, '0') // 月份从0开始要+1 - const day = String(date.getDate()).padStart(2, '0') - const hours = String(date.getHours()).padStart(2, '0') - const minutes = String(date.getMinutes()).padStart(2, '0') - const seconds = String(date.getSeconds()).padStart(2, '0') - - // 组合成目标格式 - return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`; -} +export function formatDateTime() { + const date = new Date(); + return date.toLocaleString('zh-CN', { + year: 'numeric', + month: '2-digit', + day: '2-digit', + hour: '2-digit', + minute: '2-digit', + second: '2-digit', + hour12: false + }).replace(/\//g, '-'); +}Option 2: Using a date library like date-fns (if already a dependency):
import { format } from 'date-fns'; export function formatDateTime() { return format(new Date(), 'yyyy-MM-dd HH:mm:ss'); }template/lowcode-designer/src/db/schema.js (1)
1-205: Dexie schema significantly over-indexes; trim to only queried fields and verify unused multiEntry markers.The codebase executes only 4 distinct
.where()queries across all database operations:
blockGroup.where('name')blockHistories.where('block_id')blockHistories.where('[block_id+version]')block.where('label')Yet the schema defines 40+ indexed fields per table. Remove all unused indexes to reduce IndexedDB size and improve write performance.
For multiEntry fields:
public_scope_tenants,componentsTree, andutilsare confirmed as arrays in actual usage, so their*markers are correct. However,setTemplateTime,extendConfig, andcomponentsMapappear unused in the codebase. Confirm whether these fields should be kept in the schema at all, or if they're stale definitions.template/lowcode-designer/src/routes/page.js (1)
26-37: Consider updatingupdated_aton update to match other modules.
/pages/update/:iddoesn’t bumpupdated_at(Line 33-34). If UI relies on it, you’ll want to add it.template/lowcode-designer/src/routes/i18n.js (1)
37-39: UsegetResponseData(..., error)consistently for errors.Returning
[200, { error: '缺少必要参数' }](Line 38 / 62) diverges from the response contract used elsewhere.Also applies to: 61-63
template/lowcode-designer/src/routes/app.js (1)
39-57: Avoid mutatingappSchemadirectly before returning (clone instead).
appSchema.componentsTree = ...andappSchema.utils = ...(Line 52-54) mutates the object returned from Dexie. Cloning reduces surprises if the same object reference is reused by callers.template/lowcode-designer/src/composable/http/httpServices.js (1)
76-97: URL matching viaincludes()is likely too permissive.The string match logic (Line 83-89 / 189-195) can cause accidental matches (e.g.,
/api/apps/detail/1matching a shorter substring route) depending on route ordering. Prefer exact match, prefix match with path boundary, or RegExp-only to avoid surprising collisions.Also applies to: 182-203
📜 Review details
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (1)
template/lowcode-designer/pnpm-lock.yamlis excluded by!**/pnpm-lock.yaml
📒 Files selected for processing (23)
template/lowcode-designer/package.jsontemplate/lowcode-designer/src/composable/http/httpServices.jstemplate/lowcode-designer/src/composable/http/index.jstemplate/lowcode-designer/src/db/index.jstemplate/lowcode-designer/src/db/schema.jstemplate/lowcode-designer/src/defineEntry.jstemplate/lowcode-designer/src/main.jstemplate/lowcode-designer/src/mock/appDetail.jsontemplate/lowcode-designer/src/mock/appSchema.jsontemplate/lowcode-designer/src/mock/block-groups.jsontemplate/lowcode-designer/src/mock/blockHistories.jsontemplate/lowcode-designer/src/mock/blocks.jsontemplate/lowcode-designer/src/mock/pageList.jsontemplate/lowcode-designer/src/mock/user.jsontemplate/lowcode-designer/src/mock/utilsList.jsontemplate/lowcode-designer/src/preview.jstemplate/lowcode-designer/src/routes/app.jstemplate/lowcode-designer/src/routes/block.jstemplate/lowcode-designer/src/routes/commonUtils.jstemplate/lowcode-designer/src/routes/i18n.jstemplate/lowcode-designer/src/routes/index.jstemplate/lowcode-designer/src/routes/page.jstemplate/lowcode-designer/src/routes/utils.js
✅ Files skipped from review due to trivial changes (5)
- template/lowcode-designer/src/mock/user.json
- template/lowcode-designer/src/mock/block-groups.json
- template/lowcode-designer/src/mock/appDetail.json
- template/lowcode-designer/src/mock/blocks.json
- template/lowcode-designer/src/mock/appSchema.json
🚧 Files skipped from review as they are similar to previous changes (1)
- template/lowcode-designer/src/composable/http/index.js
🧰 Additional context used
🧬 Code graph analysis (8)
template/lowcode-designer/src/routes/utils.js (2)
template/lowcode-designer/src/db/index.js (2)
db(12-12)db(12-12)template/lowcode-designer/src/routes/commonUtils.js (4)
getResponseData(29-44)getResponseData(29-44)formatDateTime(13-26)res(30-33)
template/lowcode-designer/src/preview.js (1)
template/lowcode-designer/src/db/index.js (2)
initIndexDB(34-46)initIndexDB(34-46)
template/lowcode-designer/src/routes/i18n.js (3)
template/lowcode-designer/src/routes/app.js (3)
appSchema(43-43)appSchema(64-64)appSchema(86-86)template/lowcode-designer/src/db/index.js (2)
db(12-12)db(12-12)template/lowcode-designer/src/routes/commonUtils.js (2)
getResponseData(29-44)getResponseData(29-44)
template/lowcode-designer/src/db/index.js (1)
template/lowcode-designer/src/db/schema.js (2)
schema(2-205)schema(2-205)
template/lowcode-designer/src/defineEntry.js (1)
template/lowcode-designer/src/preview.js (1)
registry(29-32)
template/lowcode-designer/src/routes/index.js (5)
template/lowcode-designer/src/routes/app.js (2)
appRoutes(16-100)appRoutes(16-100)template/lowcode-designer/src/routes/utils.js (2)
utilsRoutes(4-80)utilsRoutes(4-80)template/lowcode-designer/src/routes/i18n.js (2)
i18nRoutes(4-136)i18nRoutes(4-136)template/lowcode-designer/src/routes/page.js (2)
pageRoutes(4-150)pageRoutes(4-150)template/lowcode-designer/src/routes/block.js (2)
blockRoutes(16-438)blockRoutes(16-438)
template/lowcode-designer/src/composable/http/httpServices.js (1)
template/lowcode-designer/src/composable/http/index.js (1)
options(122-130)
template/lowcode-designer/src/main.js (2)
template/lowcode-designer/src/db/index.js (2)
initIndexDB(34-46)initIndexDB(34-46)template/lowcode-designer/src/preview.js (1)
registry(29-32)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
- GitHub Check: build
🔇 Additional comments (8)
template/lowcode-designer/src/routes/commonUtils.js (1)
29-44: LGTM!The response data structure builder is clean and handles both success and error cases appropriately.
template/lowcode-designer/src/main.js (3)
15-16: LGTM! Static imports improve clarity.The change from dynamic imports to static imports makes the dependency graph clearer and ensures
initIndexDBandregistryare available synchronously at module load time.
20-20: Good sequencing: IndexedDB initialized before engine.Calling
await initIndexDB()beforeinit()ensures the database is ready before the Tiny Engine initializes, preventing potential race conditions.
23-23: Registry array wrapping is correct per API contract.The array wrapping
[registry]matches the@opentiny/tiny-engineinit()method's expected format. The method accepts both single registry objects and arrays of registry objects, with array format supporting merging of multiple registry sources. The imported registry object fromregistry.jsis properly structured with the required fields (META_SERVICE.Http, engine.config, META_APP entries, etc.).template/lowcode-designer/src/defineEntry.js (1)
13-18: Registry import path is correct.The file
registry.jsexists attemplate/lowcode-designer/registry.js, confirming the import path'../registry.js'indefineEntry.jscorrectly resolves to the parent directory.template/lowcode-designer/src/routes/index.js (2)
1-7: LGTM: simple, readable registry aggregation.
1-7: Route order does not affect behavior—patterns are mutually exclusive.The concatenation order is immaterial here. No overlapping URL patterns exist across the route files:
appRoutesuses/platform-centerand/app-center/apps,utilsRoutesuses/app-center/apps/extension,pageRoutesuses/app-center/pages,i18nRoutesuses/app-center/i18n, andblockRoutesuses/material-center. Each route has a distinct path hierarchy, so the "first match wins" behavior of the mock adapter does not create any risk.template/lowcode-designer/src/preview.js (1)
12-41: Good: initializing IndexedDB before wiring HttpService/routes avoids “empty DB” races.
| "@opentiny/vue-theme-mobile": "~3.20.0", | ||
| "@vueuse/core": "^9.6.0", | ||
| "dexie": "^4.2.2", | ||
| "monaco-editor": "0.51.0", | ||
| "axios": "^1.12.0", | ||
| "axios-mock-adapter": "^2.1.0", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧩 Analysis chain
🌐 Web query:
axios-mock-adapter 2.1.0 supported axios versions compatibility
💡 Result:
axios-mock-adapter 2.1.0 is intended to work with axios v0.17.0 and above (per the package README). However, there are known TypeScript compatibility issues when used with axios ≥ 1.6.0 — users report errors and note axios 1.5.1 is the last version that did not trigger the type error. [1][2]
Sources:
- npm package README (axios-mock-adapter: "works with axios v0.17.0 and above"). [1]
- GitHub issue reporting TypeScript errors with axios >= 1.6.0 (recommends axios 1.5.1 to avoid the error). [2]
🌐 Web query:
monaco-editor 0.51.0 Vite worker configuration requirements
💡 Result:
Summary — what you must do to run monaco-editor 0.51.0 with Vite
-
Use the ESM integration pattern: set self.MonacoEnvironment.getWorker (Vite expects getWorker, not getWorkerUrl) and return a Worker constructed from the Vite-built worker module (type: 'module'). Example pattern and label → file mapping shown in the Monaco ESM integration docs. [1][2]
-
Worker label → module mapping (use these labels in getWorker):
- json → vs/language/json/json.worker
- css / scss / less → vs/language/css/css.worker
- html / handlebars / razor → vs/language/html/html.worker
- typescript / javascript → vs/language/typescript/ts.worker
- default → vs/editor/editor.worker. [1][2]
-
Construct the Worker using the Vite worker entry URL (append ?worker or use Vite’s worker API) and set { type: 'module', name: label } so the worker is an ES module. Example: new Worker('/monaco-editor/esm/vs/language/typescript/ts.worker?worker', { type: 'module', name: 'typescript' }). [1][3]
-
If you prefer convenience, use a Vite plugin (vite-plugin-monaco-editor) which bundles/outputs worker scripts and exposes options (languageWorkers, customWorkers, publicPath, globalAPI). The plugin also enforces including editorWorkerService when using other language workers. [3][4]
-
Be aware of the 0.51.0-specific worker issues reported by users: some users saw importScripts / invalid-URL errors when upgrading to 0.51.0 (fallback to main-thread workers). If you hit worker-creation errors after upgrading, check your getWorker implementation / public base path and consider using the plugin or a package that bundles Monaco workers for ESM (monaco-esm / monaco-editor-workers). [5][6]
Sources
- Monaco ESM integration + Vite example (getWorker implementation and label mapping). [1]
- Guide showing Vite usage and worker URLs (example using ?worker and type:'module'). [2]
- vite-plugin-monaco-editor README/options (languageWorkers, customWorkers, publicPath, globalAPI). [3]
- vite-plugin-monaco-editor GitHub repo. [4]
- Issue reporting worker creation errors after upgrading to 0.51.0. [5]
- monaco-esm / monaco-editor-workers community packages (bundled workers alternative). [6]
References
[1] Integrating the ESM version of the Monaco Editor — Vite example.
[2] Integrate-ESM docs / examples (getWorker + ?worker usage).
[3] vite-plugin-monaco-editor (npm / README).
[4] vite-plugin-monaco-editor (GitHub).
[5] microsoft/monaco-editor Issue: "0.51.0 only: Could not create web worker".
[6] monaco-esm / monaco-editor-workers (bundled-workers packages).
🌐 Web query:
axios 1.12.0 axios-mock-adapter compatibility known issues
💡 Result:
Short summary — known compatibility problems between axios 1.12.0 (and recent 1.x releases) and axios-mock-adapter:
-
TypeScript type incompatibility: axios introduced ESM/typing changes (starting around 1.6.0) that cause TypeScript errors when using axios-mock-adapter’s types (errors reported when the axios import uses the newer ESM types). This is a long-running issue on the axios-mock-adapter repo (see issue #400). [1]
-
Axios 1.12.0-specific type/build problems: people reported TypeScript build errors after upgrading to axios 1.12.0 (missing global DOM types like RequestInfo/BodyInit in some Node setups), which can surface when using libraries that depend on axios types. See axios issue #7016. [2]
-
Adapter/behavior changes in axios 1.12.0: the fetch adapter behavior in recent axios releases changed and has caused compatibility problems with some mocking/interception tools (e.g., MSW) — reported in axios issue #7015. While this is not axios-mock-adapter directly, it can affect test setups that rely on different adapters. [3]
Recommendations
- If you hit TS errors with axios-mock-adapter, either:
- use an axios version known to avoid the ESM-typing mismatch (users reported 1.5.1 as the last version without the ESM-type issue), or
- import axios in a way that yields compatible types (CJS import) or update/patch axios-mock-adapter types if feasible. See the axios-mock-adapter issue for discussion. [1][4]
- If you rely on fetch-adapter behavior (or MSW), prefer forcing the XHR adapter or pinning axios until the adapter/compatibility issue is resolved. [3]
- Watch the upstream issue trackers for fixes/PRs in both repos and test after upgrades.
Sources:
[1] axios-mock-adapter issue discussing TS errors with axios >=1.6.0: ctimmerm/axios-mock-adapter#400
[2] axios issue: TypeScript build errors reported for 1.12.0: axios/axios#7016
[3] axios issue: fetch adapter breaks compatibility with MSW in 1.12.0: axios/axios#7015
[4] (context) axios-mock-adapter repo / discussions about compatibility with newer axios typings. https://github.com/ctimmerm/axios-mock-adapter
If you want, I can search your project’s error message and suggest a concrete fix (pin version, tsconfig change, or import workaround).
🏁 Script executed:
cd template/lowcode-designer && find . -type f \( -name "*.ts" -o -name "*.tsx" -o -name "*.js" -o -name "*.vue" \) | head -20Repository: opentiny/tiny-pro
Length of output: 538
🏁 Script executed:
cd template/lowcode-designer && rg -i "MonacoEnvironment|getWorker" --type ts --type js --type vueRepository: opentiny/tiny-pro
Length of output: 88
🏁 Script executed:
cd template/lowcode-designer && rg -i "axios-mock-adapter|createMockAdapter" --type ts --type js --type vueRepository: opentiny/tiny-pro
Length of output: 88
🏁 Script executed:
cd template/lowcode-designer && cat -n vite.config.ts 2>/dev/null || cat -n vite.config.js 2>/dev/null || echo "No vite config found"Repository: opentiny/tiny-pro
Length of output: 1643
🏁 Script executed:
cd template/lowcode-designer && rg -i "MonacoEnvironment|getWorker" --type-list | grep -E "js|ts" | head -5Repository: opentiny/tiny-pro
Length of output: 450
🏁 Script executed:
cd template/lowcode-designer && rg "MonacoEnvironment|getWorker"Repository: opentiny/tiny-pro
Length of output: 43
🏁 Script executed:
cd template/lowcode-designer && rg "axios-mock-adapter|MockAdapter"Repository: opentiny/tiny-pro
Length of output: 352
🏁 Script executed:
cd template/lowcode-designer && rg "import.*axios|require.*axios" | head -10Repository: opentiny/tiny-pro
Length of output: 210
🏁 Script executed:
cd template/lowcode-designer && cat -n src/main.js | head -50Repository: opentiny/tiny-pro
Length of output: 1202
🏁 Script executed:
cd template/lowcode-designer && cat -n tsconfig.json 2>/dev/null || echo "No tsconfig found"Repository: opentiny/tiny-pro
Length of output: 78
🏁 Script executed:
cd template/lowcode-designer && find . -name "tsconfig.json" -o -name "jsconfig.json" | head -5Repository: opentiny/tiny-pro
Length of output: 43
🏁 Script executed:
cd template && cat -n tsconfig.json 2>/dev/null | head -30Repository: opentiny/tiny-pro
Length of output: 43
🏁 Script executed:
cd template/lowcode-designer && rg "monaco-editor|monaco"Repository: opentiny/tiny-pro
Length of output: 104
🏁 Script executed:
cd template/lowcode-designer && cat -n src/composable/http/httpServices.js | head -40Repository: opentiny/tiny-pro
Length of output: 1163
Resolve axios/axios-mock-adapter version conflict; verify Monaco worker configuration is handled by base config.
axios-mock-adapter 2.1.0 has confirmed TypeScript compatibility issues with axios ≥ 1.6.0 (see axios-mock-adapter issue #400). axios 1.5.1 is the last version without the ESM-type mismatch, and the project actively uses axios-mock-adapter in src/composable/http/httpServices.js. Either downgrade axios to 1.5.1 or use a different testing approach.
For monaco-editor 0.51.0: the dependency requires explicit Vite worker configuration (getWorker implementation and worker URL setup). Verify that useTinyEngineBaseConfig from @opentiny/tiny-engine-vite-config already handles Monaco's ESM worker requirements; if not, you'll see runtime worker errors when the editor loads.
| // 声明数据库表 | ||
| export const createDB = async () => { | ||
| return db.version(1).stores({ | ||
| ...schema | ||
| }) | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧩 Analysis chain
🏁 Script executed:
cd template/lowcode-designer && find . -name "index.js" -path "*/db/*" | head -20Repository: opentiny/tiny-pro
Length of output: 78
🏁 Script executed:
cat -n template/lowcode-designer/src/db/index.jsRepository: opentiny/tiny-pro
Length of output: 1751
🌐 Web query:
Dexie 4.x idempotent initialization db.version stores repeated calls
💡 Result:
Short answer: Dexie’s version(...).stores(...) must be declared before opening the DB (and you cannot repeatedly add the same version while the DB is open). To make initialization idempotent either (A) ensure you only declare the schema once (e.g. from a single DB singleton/constructor), or (B) guard/skip calls when the DB is already opened or already at the desired version. Documentation: version/stores must be declared before open and Dexie sorts/uses the highest declared version. [1][2]
Practical options:
-
Preferred (singleton): declare db.version(...).stores(...) once in your Dexie subclass or single module that is imported by the app — don’t call it on every render or repeated init. Example pattern is shown in the docs. [1][3]
-
Guard by open/version:
- If you want a runtime guard, skip calling stores when db.isOpen() (or when db.verno >= desiredVersion). Example:
const db = new Dexie('MyDB');
if (!db.isOpen() && (db.verno < 1)) {
db.version(1).stores({ friends: '++id,name' });
}
await db.open(); - Or catch the “Cannot add version when database is open” error and ignore or close/re-open only when appropriate (older advice shown in community answers). [2]
- If you want a runtime guard, skip calling stores when db.isOpen() (or when db.verno >= desiredVersion). Example:
References:
- Dexie API / declaring versions and stores. [1]
- StackOverflow discussion: “Cannot add version when database is open” (try/catch / close workaround). [2]
- Version.stores docs (behavior & upgrade notes). [3]
Make DB initialization idempotent—createDB() will fail on repeated initIndexDB() calls.
Calling db.version(1).stores() inside initIndexDB() (line 35) violates Dexie's requirement that schema be declared before the database is opened. On the first call, createDB() declares the schema and db.open() succeeds; on the second call, createDB() will throw "Cannot add version when database is open" because the DB is already open from the first call. The userCount guard (line 41) only prevents data re-initialization, not schema re-declaration.
Recommended fix: Move schema declaration to the Dexie instance creation (lines 12–19), so it runs once at app startup:
export const db = new Dexie('tiny-engine-demo-indexdb')
db.version(1).stores({
...schema
})
export const initIndexDB = async () => {
await db.open()
// ... rest of function
}Also applies to: 34–46
🤖 Prompt for AI Agents
In @template/lowcode-designer/src/db/index.js around lines 14 - 19, The schema
registration must happen once at DB construction to avoid "Cannot add version
when database is open"; move the call to db.version(1).stores(...) out of
createDB() and into the Dexie instance setup so it runs at module initialization
(i.e., declare export const db = new Dexie(...); then call
db.version(1).stores({...schema})). Then change createDB() to simply return the
existing db (or remove it) and update initIndexDB() to call await db.open() (and
keep the existing userCount guard for data seeding only), ensuring no further
db.version(...) calls occur after db.open().
| // 更新 app | ||
| { | ||
| url: /\/app-center\/api\/apps\/update\/\d+/, | ||
| method: 'POST', | ||
| response: async (config) => { | ||
| const newAppData = JSON.parse(config.data) | ||
| const appSchema = await db.appSchema.get(1) | ||
|
|
||
| if (newAppData.global_state) { | ||
| await db.appDetail.update(1, { global_state: newAppData.global_state }) | ||
| await db.appSchema.update(1, { meta: { ...appSchema.meta, globalState: newAppData.global_state } }) | ||
| } | ||
|
|
||
| // TODO: 支持更新数据源 | ||
|
|
||
| const appDetail = db.appDetail.get(1) | ||
|
|
||
| return [200, getResponseData(appDetail)] | ||
| } | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fix missing await (currently returns a Promise in response data).
Line 95 assigns const appDetail = db.appDetail.get(1) without await, so Line 97 returns a Promise inside getResponseData.
Proposed fix
- const appDetail = db.appDetail.get(1)
+ const appDetail = await db.appDetail.get(1)
return [200, getResponseData(appDetail)]🤖 Prompt for AI Agents
In @template/lowcode-designer/src/routes/app.js around lines 80 - 99, The
response handler for the POST update route returns a Promise because
db.appDetail.get(1) is not awaited; update the async response function to await
the call (i.e., change const appDetail = db.appDetail.get(1) to use await) so
getResponseData receives the resolved appDetail object, keeping the rest of the
flow (updates to db.appDetail.update and db.appSchema.update and the response
return) unchanged.
| // 获取指定应用下所有区块分组 | ||
| { | ||
| url: /\/material-center\/api\/block-groups/, | ||
| method: 'GET', | ||
| response: async (config) => { | ||
| const blockGroups = await db.blockGroup.toArray() | ||
| const query = config.params || {} | ||
| const source = query.from | ||
|
|
||
| const promises = blockGroups.map(async(item) => { | ||
| let blocks = [] | ||
| if (source !== 'block') { | ||
| const idVersionMap = {} | ||
| const ids = (item.materialBlocks || []).map((item) => { | ||
| idVersionMap[item.id] = item.version | ||
|
|
||
| return item.id | ||
| }) | ||
| blocks = await db.blockHistories.where('block_id').anyOf(ids).toArray() | ||
|
|
||
| blocks = blocks | ||
| .filter(item => item.version === idVersionMap[item.block_id]) | ||
| .map(item => ({ ...item, id: item.block_id })) | ||
| } else { | ||
| const ids = item.blocks || [] | ||
| blocks = await db.block.bulkGet(ids) | ||
|
|
||
| } | ||
| item.blocks = blocks | ||
| return item | ||
| }) | ||
|
|
||
| const allBlockGroups = await Promise.all(promises) | ||
|
|
||
| return [200, getResponseData(allBlockGroups)] | ||
| } | ||
| }, | ||
| // 获取指定区块分组详情 | ||
| { | ||
| url: /\/material-center\/api\/block-groups\?id=\d+/, | ||
| method: 'GET', | ||
| response: async (config) => { | ||
| const urlSearch = new URLSearchParams(config.url.split('?')[1]) | ||
| const id = urlSearch.get('id') | ||
| const blockGroupDetail = await db.blockGroup.get(Number(id)) | ||
|
|
||
| return [200, getResponseData([blockGroupDetail])] | ||
| } | ||
| }, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fix route collision: list handler regex will swallow ?id= detail requests.
The list route url: /\/material-center\/api\/block-groups/ (Line 98) matches /material-center/api/block-groups?id=..., so the detail handler (Line 135-143) is likely never reached. Tighten the list regex (e.g., end-anchor) or place the ?id= handler before the list handler.
Proposed fix
{
- url: /\/material-center\/api\/block-groups/,
+ url: /\/material-center\/api\/block-groups$/,
method: 'GET',
response: async (config) => {
@@
{
- url: /\/material-center\/api\/block-groups\?id=\d+/,
+ url: /\/material-center\/api\/block-groups\?id=\d+$/,
method: 'GET',
response: async (config) => {📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| // 获取指定应用下所有区块分组 | |
| { | |
| url: /\/material-center\/api\/block-groups/, | |
| method: 'GET', | |
| response: async (config) => { | |
| const blockGroups = await db.blockGroup.toArray() | |
| const query = config.params || {} | |
| const source = query.from | |
| const promises = blockGroups.map(async(item) => { | |
| let blocks = [] | |
| if (source !== 'block') { | |
| const idVersionMap = {} | |
| const ids = (item.materialBlocks || []).map((item) => { | |
| idVersionMap[item.id] = item.version | |
| return item.id | |
| }) | |
| blocks = await db.blockHistories.where('block_id').anyOf(ids).toArray() | |
| blocks = blocks | |
| .filter(item => item.version === idVersionMap[item.block_id]) | |
| .map(item => ({ ...item, id: item.block_id })) | |
| } else { | |
| const ids = item.blocks || [] | |
| blocks = await db.block.bulkGet(ids) | |
| } | |
| item.blocks = blocks | |
| return item | |
| }) | |
| const allBlockGroups = await Promise.all(promises) | |
| return [200, getResponseData(allBlockGroups)] | |
| } | |
| }, | |
| // 获取指定区块分组详情 | |
| { | |
| url: /\/material-center\/api\/block-groups\?id=\d+/, | |
| method: 'GET', | |
| response: async (config) => { | |
| const urlSearch = new URLSearchParams(config.url.split('?')[1]) | |
| const id = urlSearch.get('id') | |
| const blockGroupDetail = await db.blockGroup.get(Number(id)) | |
| return [200, getResponseData([blockGroupDetail])] | |
| } | |
| }, | |
| // 获取指定应用下所有区块分组 | |
| { | |
| url: /\/material-center\/api\/block-groups$/, | |
| method: 'GET', | |
| response: async (config) => { | |
| const blockGroups = await db.blockGroup.toArray() | |
| const query = config.params || {} | |
| const source = query.from | |
| const promises = blockGroups.map(async(item) => { | |
| let blocks = [] | |
| if (source !== 'block') { | |
| const idVersionMap = {} | |
| const ids = (item.materialBlocks || []).map((item) => { | |
| idVersionMap[item.id] = item.version | |
| return item.id | |
| }) | |
| blocks = await db.blockHistories.where('block_id').anyOf(ids).toArray() | |
| blocks = blocks | |
| .filter(item => item.version === idVersionMap[item.block_id]) | |
| .map(item => ({ ...item, id: item.block_id })) | |
| } else { | |
| const ids = item.blocks || [] | |
| blocks = await db.block.bulkGet(ids) | |
| } | |
| item.blocks = blocks | |
| return item | |
| }) | |
| const allBlockGroups = await Promise.all(promises) | |
| return [200, getResponseData(allBlockGroups)] | |
| } | |
| }, | |
| // 获取指定区块分组详情 | |
| { | |
| url: /\/material-center\/api\/block-groups\?id=\d+$/, | |
| method: 'GET', | |
| response: async (config) => { | |
| const urlSearch = new URLSearchParams(config.url.split('?')[1]) | |
| const id = urlSearch.get('id') | |
| const blockGroupDetail = await db.blockGroup.get(Number(id)) | |
| return [200, getResponseData([blockGroupDetail])] | |
| } | |
| }, |
🤖 Prompt for AI Agents
In @template/lowcode-designer/src/routes/block.js around lines 96 - 144, The
list route's regex url: /\/material-center\/api\/block-groups/ currently also
matches requests with query strings (e.g., ?id=...), causing the detail handler
(/\/material-center\/api\/block-groups\?id=\d+/) to be unreachable; fix by
either tightening the list route to only match the exact path (e.g., require
end-anchor so the list pattern only matches the exact
/material-center/api/block-groups) or by moving the detail handler block (the
GET handler that reads id via URLSearchParams and calls db.blockGroup.get) above
the list handler so the id-specific route is evaluated first.
| { | ||
| url: /\/app-center\/api\/apps\/extension\/list/, | ||
| method: 'GET', | ||
| response: async (config) => { | ||
| const query = new URLSearchParams(config.url.split('?')[1]) | ||
| const category = query.get('category') | ||
|
|
||
| if (category === 'utils') { | ||
| const list = await db.utils.toArray() | ||
| return [200, getResponseData(list)] | ||
| } | ||
| } | ||
| }, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Always return a mock reply tuple (avoid implicit undefined paths).
On Line 14-18 / Line 28-44 / Line 53-66, when category !== 'utils' the handler returns nothing, which can break axios-mock-adapter’s reply() contract. Also config.url.split('?')[1] can be undefined (Line 11 / Line 72).
Proposed fix
export const utilsRoutes = [
@@
{
@@
response: async (config) => {
- const query = new URLSearchParams(config.url.split('?')[1])
+ const qs = (config.url || '').split('?')[1] || ''
+ const query = new URLSearchParams(qs)
const category = query.get('category')
if (category === 'utils') {
const list = await db.utils.toArray()
return [200, getResponseData(list)]
}
+ return [200, getResponseData([], { code: 'INVALID_CATEGORY', message: 'category must be utils' })]
}
},
@@
response: async (config) => {
- const data = JSON.parse(config.data)
+ let data
+ try {
+ data = JSON.parse(config.data)
+ } catch {
+ return [200, getResponseData(null, { code: 'BAD_JSON', message: 'Invalid JSON body' })]
+ }
const time = formatDateTime()
@@
if (data?.category === 'utils') {
@@
return [200, getResponseData(resData)]
}
+ return [200, getResponseData(null, { code: 'INVALID_CATEGORY', message: 'category must be utils' })]
}
},
@@
response: async (config) => {
- const data = JSON.parse(config.data)
+ let data
+ try {
+ data = JSON.parse(config.data)
+ } catch {
+ return [200, getResponseData(null, { code: 'BAD_JSON', message: 'Invalid JSON body' })]
+ }
const time = formatDateTime()
@@
if (data?.category === 'utils') {
@@
return [200, getResponseData(resData)]
}
+ return [200, getResponseData(null, { code: 'INVALID_CATEGORY', message: 'category must be utils' })]
}
},Also applies to: 21-45, 46-67
🤖 Prompt for AI Agents
In @template/lowcode-designer/src/routes/utils.js around lines 7 - 19, The route
handler's response function may return undefined when config.url has no query
string or when category !== 'utils', violating axios-mock-adapter's reply
contract; update the response lambdas (the response: async (config) => { ... }
bodies) to safely parse the query (e.g., use const query = new
URLSearchParams((config.url || '').split('?')[1] || '') or new URL(config.url,
'http://localhost') fallback) and always return a reply tuple; for the utils
branch return [200, getResponseData(list)] and for other/unknown categories
return a default mock like [200, getResponseData([])] (or a consistent error
tuple), and apply the same pattern to the other similar handlers that use
config.url.split('?')[1], db.utils, and getResponseData.
| { | ||
| url: "/app-center/api/apps/extension/update", | ||
| method: 'POST', | ||
| response: async (config) => { | ||
| const data = JSON.parse(config.data) | ||
| const time = formatDateTime() | ||
|
|
||
| if (data?.category === 'utils') { | ||
| const id = Number(data.id) | ||
| const originData = await db.utils.get(id) | ||
| await db.utils.update(id, { | ||
| ...originData, | ||
| ...data, | ||
| updated_at: time | ||
| }) | ||
|
|
||
| const resData = await db.utils.get(id) | ||
|
|
||
| return [200, getResponseData(resData)] | ||
| } | ||
| } | ||
| }, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Handle missing record on update (avoid spreading undefined).
originData can be undefined (Line 55). Spreading it (Line 57) will throw.
Proposed fix
if (data?.category === 'utils') {
const id = Number(data.id)
const originData = await db.utils.get(id)
+ if (!originData) {
+ return [200, getResponseData(null, { code: 'NOT_FOUND', message: `utils ${id} not found` })]
+ }
await db.utils.update(id, {
...originData,
...data,
updated_at: time
})🤖 Prompt for AI Agents
In @template/lowcode-designer/src/routes/utils.js around lines 46 - 67,
originData from db.utils.get(id) can be undefined so spreading it into the
update payload will throw; update the response handler in the POST route (the
async response function) to check if originData exists before calling
db.utils.update, and if missing return an appropriate error response (e.g.,
[404, getResponseData({ message: 'Record not found' })]) or reject, otherwise
call db.utils.update with a safe merge (e.g., {...(originData || {}), ...data,
updated_at: time}) and continue returning the updated record from
db.utils.get(id).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 3
🤖 Fix all issues with AI agents
In @template/lowcode-designer/src/composable/http/httpServices.js:
- Around line 76-129: The mock reply logic duplicated between init and
setOptions should be extracted into a single helper (e.g.,
createMockReplyHandler or getMockResponder) that accepts the options/mockConfig
and returns the function passed to mock.onAny().reply; move the URL/method
matching, RegExp handling, response invocation (call when response is a
function) and the default 200 fallback into that helper, then replace both
inline handlers in init and setOptions with
mock.onAny().reply(createMockReplyHandler(options)); ensure the helper
references config.baseURL/config.url and preserves current log message and
returned payload semantics.
- Around line 67-74: The request interceptor that rewrites AI_PATH is being
added unconditionally (in init and setOptions), causing duplicates on repeated
setOptions; update the httpServices module to store the interceptor id (the
value returned by http.interceptors.request.use) or a boolean flag (e.g.,
aiPathInterceptorId or aiPathInterceptorRegistered) and before adding the
interceptor in init/setOptions either check the flag to avoid re-adding or call
http.interceptors.request.eject(interceptorId) to remove the old one, then
register and save the new id so the AI_PATH rewrite is only applied once.
- Around line 171-174: setOptions reinitializes the mock but only restores two
passthroughs; update the mock reinit logic (where setOptions recreates or calls
init) to register the same passthrough routes that init uses: add
mock.onGet(/\/api\/i18n/).passThrough(),
mock.onGet(/\/api\/menu/).passThrough(),
mock.onAny(/\/api\/role$/).passThrough() and
mock.onAny(/\/api\/role\/detail/).passThrough() (or the equivalent patterns used
in init) so the mock instance in setOptions matches init's passthrough list.
🧹 Nitpick comments (1)
template/lowcode-designer/src/composable/http/httpServices.js (1)
88-96: Loose URL matching may cause unintended matches.Using
includes(lines 93-94) can match URLs unintentionally. For example, a mock config for/api/userwould also match/api/users,/api/user-details, etc.Consider using stricter matching if precision is required (e.g., exact match or path-segment-aware comparison).
📜 Review details
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (1)
template/lowcode-designer/src/composable/http/httpServices.js
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
- GitHub Check: build
🔇 Additional comments (2)
template/lowcode-designer/src/composable/http/httpServices.js (2)
1-6: LGTM!Imports and module-scoped singleton pattern are appropriate for an HTTP service module.
8-32: LGTM!The interceptor handler cleanly supports multiple registration formats (single function, array of functions, and arrays with
[onFulfilled, onRejected]pairs).
| http.interceptors.request.use((config) => { | ||
| const AI_PATH = '/app-center/api/ai/chat'; | ||
|
|
||
| if (config.url === AI_PATH) { | ||
| config.url = `/tiny-engine${AI_PATH}`; // 修改路径 | ||
| } | ||
| return config; | ||
| }); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
AI_PATH interceptor accumulates on repeated setOptions calls.
This request interceptor is added unconditionally in both init and setOptions. If setOptions is called multiple times with enableMock, this interceptor will be registered repeatedly, potentially causing unexpected URL rewrites.
Consider tracking whether this interceptor has been added, or eject it before re-adding.
Proposed fix: track interceptor registration
let http = null;
let mock = null;
+let aiPathInterceptorId = null;
// In init and setOptions, before adding the interceptor:
+if (aiPathInterceptorId !== null) {
+ http.interceptors.request.eject(aiPathInterceptorId);
+}
-http.interceptors.request.use((config) => {
+aiPathInterceptorId = http.interceptors.request.use((config) => {
const AI_PATH = '/app-center/api/ai/chat';
if (config.url === AI_PATH) {
config.url = `/tiny-engine${AI_PATH}`;
}
return config;
});Committable suggestion skipped: line range outside the PR's diff.
🤖 Prompt for AI Agents
In @template/lowcode-designer/src/composable/http/httpServices.js around lines
67 - 74, The request interceptor that rewrites AI_PATH is being added
unconditionally (in init and setOptions), causing duplicates on repeated
setOptions; update the httpServices module to store the interceptor id (the
value returned by http.interceptors.request.use) or a boolean flag (e.g.,
aiPathInterceptorId or aiPathInterceptorRegistered) and before adding the
interceptor in init/setOptions either check the flag to avoid re-adding or call
http.interceptors.request.eject(interceptorId) to remove the old one, then
register and save the new id so the AI_PATH rewrite is only applied once.
| mock.onAny().reply(async (config) => { | ||
| const { mockConfig = [] } = options; | ||
| // 构建完整 URL(包含 baseURL) | ||
| const fullUrl = (config.baseURL || '') + (config.url || ''); | ||
| const method = (config.method || 'GET').toUpperCase(); | ||
|
|
||
| const mockItem = mockConfig.find((item) => { | ||
| // 方法匹配 | ||
| if (item.method && method !== item.method.toUpperCase()) { | ||
| return false; | ||
| } | ||
|
|
||
| // URL 匹配 - 同时检查 config.url 和 fullUrl | ||
| if (typeof item.url === 'string') { | ||
| return ( | ||
| item.url === config.url || | ||
| item.url === fullUrl || | ||
| config.url?.includes(item.url) || | ||
| fullUrl.includes(item.url) | ||
| ); | ||
| } | ||
|
|
||
| if (item.url instanceof RegExp) { | ||
| return item.url.test(config.url) || item.url.test(fullUrl); | ||
| } | ||
|
|
||
| return false; | ||
| }); | ||
|
|
||
| if (mockItem) { | ||
| if (typeof mockItem.response === 'function') { | ||
| return mockItem.response(config); | ||
| } | ||
| return mockItem.response; | ||
| } | ||
|
|
||
| // 如果没有匹配到,输出调试信息 | ||
| console.warn( | ||
| `[Mock] 未匹配到接口: ${method} ${fullUrl || config.url}`, | ||
| '可用路由:', | ||
| mockConfig.map((item) => `${item.method || 'ANY'} ${item.url}`) | ||
| ); | ||
|
|
||
| return [ | ||
| 200, | ||
| { | ||
| code: 200, | ||
| errMsg: | ||
| '当前 demo 暂未支持该接口,请前往GitHub 或者 Gitee 克隆项目完整体验', | ||
| error: | ||
| '当前 demo 暂未支持该接口,请前往GitHub 或者 Gitee 克隆项目完整体验', | ||
| }, | ||
| ]; | ||
| }); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion | 🟠 Major
Significant code duplication between init and setOptions.
The mock handler logic (lines 76-129) is nearly identical to lines 185-239 in setOptions. This ~50-line duplication violates DRY and increases maintenance burden.
Proposed refactor: extract a shared helper
+const createMockHandler = (mockConfig) => async (config) => {
+ const fullUrl = (config.baseURL || '') + (config.url || '');
+ const method = (config.method || 'GET').toUpperCase();
+
+ const mockItem = mockConfig.find((item) => {
+ if (item.method && method !== item.method.toUpperCase()) {
+ return false;
+ }
+ if (typeof item.url === 'string') {
+ return (
+ item.url === config.url ||
+ item.url === fullUrl ||
+ config.url?.includes(item.url) ||
+ fullUrl.includes(item.url)
+ );
+ }
+ if (item.url instanceof RegExp) {
+ return item.url.test(config.url) || item.url.test(fullUrl);
+ }
+ return false;
+ });
+
+ if (mockItem) {
+ return typeof mockItem.response === 'function'
+ ? mockItem.response(config)
+ : mockItem.response;
+ }
+
+ console.warn(
+ `[Mock] 未匹配到接口: ${method} ${fullUrl || config.url}`,
+ '可用路由:',
+ mockConfig.map((item) => `${item.method || 'ANY'} ${item.url}`)
+ );
+
+ return [200, { code: 200, errMsg: '...', error: '...' }];
+};
// Then in init and setOptions:
-mock.onAny().reply(async (config) => { /* 50 lines */ });
+mock.onAny().reply(createMockHandler(options.mockConfig));Committable suggestion skipped: line range outside the PR's diff.
🤖 Prompt for AI Agents
In @template/lowcode-designer/src/composable/http/httpServices.js around lines
76 - 129, The mock reply logic duplicated between init and setOptions should be
extracted into a single helper (e.g., createMockReplyHandler or
getMockResponder) that accepts the options/mockConfig and returns the function
passed to mock.onAny().reply; move the URL/method matching, RegExp handling,
response invocation (call when response is a function) and the default 200
fallback into that helper, then replace both inline handlers in init and
setOptions with mock.onAny().reply(createMockReplyHandler(options)); ensure the
helper references config.baseURL/config.url and preserves current log message
and returned payload semantics.
| mock.onGet(/\/mock\/bundle\.json$/).passThrough(); | ||
| mock.onPost(/\/app-center\/api\/ai\/chat/).passThrough(); | ||
| // 允许 /api/auth/api-token 接口通过,不被 mock 拦截 | ||
| mock.onAny(/\/api\/auth\/api-token/).passThrough(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Passthrough routes are inconsistent with init.
When setOptions reinitializes the mock, it only restores 2 of the 6 passthrough routes defined in init. Missing routes:
/api/i18n/api/menu/api/role/api/role/detail
This will cause these endpoints to be unexpectedly mocked after calling setOptions.
Proposed fix: add missing passthrough routes
mock = new AxiosMockAdapter(http);
mock.onGet(/\/mock\/bundle\.json$/).passThrough();
mock.onPost(/\/app-center\/api\/ai\/chat/).passThrough();
-// 允许 /api/auth/api-token 接口通过,不被 mock 拦截
+// 允许 tiny-pro 的接口通过,不被 mock 拦截
mock.onAny(/\/api\/auth\/api-token/).passThrough();
+mock.onAny(/\/api\/i18n/).passThrough();
+mock.onAny(/\/api\/menu/).passThrough();
+mock.onAny(/\/api\/role/).passThrough();
+mock.onAny(/\/api\/role\/detail/).passThrough();📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| mock.onGet(/\/mock\/bundle\.json$/).passThrough(); | |
| mock.onPost(/\/app-center\/api\/ai\/chat/).passThrough(); | |
| // 允许 /api/auth/api-token 接口通过,不被 mock 拦截 | |
| mock.onAny(/\/api\/auth\/api-token/).passThrough(); | |
| mock.onGet(/\/mock\/bundle\.json$/).passThrough(); | |
| mock.onPost(/\/app-center\/api\/ai\/chat/).passThrough(); | |
| // 允许 tiny-pro 的接口通过,不被 mock 拦截 | |
| mock.onAny(/\/api\/auth\/api-token/).passThrough(); | |
| mock.onAny(/\/api\/i18n/).passThrough(); | |
| mock.onAny(/\/api\/menu/).passThrough(); | |
| mock.onAny(/\/api\/role/).passThrough(); | |
| mock.onAny(/\/api\/role\/detail/).passThrough(); |
🤖 Prompt for AI Agents
In @template/lowcode-designer/src/composable/http/httpServices.js around lines
171 - 174, setOptions reinitializes the mock but only restores two passthroughs;
update the mock reinit logic (where setOptions recreates or calls init) to
register the same passthrough routes that init uses: add
mock.onGet(/\/api\/i18n/).passThrough(),
mock.onGet(/\/api\/menu/).passThrough(),
mock.onAny(/\/api\/role$/).passThrough() and
mock.onAny(/\/api\/role\/detail/).passThrough() (or the equivalent patterns used
in init) so the mock instance in setOptions matches init's passthrough list.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 1
🤖 Fix all issues with AI agents
In @template/lowcode-designer/engine.config.js:
- Line 4: Update the "material" entry in the config so its relative path points
to the existing bundle.json; replace the incorrect './mock/bundle.json' value
with './public/mock/bundle.json' for the material array entry to resolve the
path resolution error.
| id: 'engine.config', | ||
| theme: 'light', | ||
| material: ['/mock/bundle.json'], | ||
| material: ['./mock/bundle.json'], |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Find bundle.json file in the template/lowcode-designer directory
echo "=== Checking for bundle.json in mock directory ==="
find template/lowcode-designer -name "bundle.json" -type f 2>/dev/null
echo ""
echo "=== Listing files in template/lowcode-designer/mock/ ==="
ls -la template/lowcode-designer/mock/ 2>/dev/null || echo "Directory does not exist or is empty"
echo ""
echo "=== Checking engine.config.js content ==="
cat -n template/lowcode-designer/engine.config.js
echo ""
echo "=== Searching for old absolute path pattern with absolute path /mock/bundle.json ==="
rg "/mock/bundle\.json" template/lowcode-designer/ --type js -nRepository: opentiny/tiny-pro
Length of output: 639
Path resolution error: relative path points to non-existent directory.
The file bundle.json actually exists at template/lowcode-designer/public/mock/bundle.json, but the relative path ./mock/bundle.json resolves to a non-existent directory. The correct relative path should be ./public/mock/bundle.json.
🤖 Prompt for AI Agents
In @template/lowcode-designer/engine.config.js at line 4, Update the "material"
entry in the config so its relative path points to the existing bundle.json;
replace the incorrect './mock/bundle.json' value with
'./public/mock/bundle.json' for the material array entry to resolve the path
resolution error.
PR
feat:拦截me接口返回mock
PR Checklist
Please check if your PR fulfills the following requirements:
PR Type
What kind of change does this PR introduce?
What is the current behavior?
Issue Number: N/A
What is the new behavior?
Does this PR introduce a breaking change?
Other information
Summary by CodeRabbit
New Features
dev:designerscript to run the low-code designer locally.Chores
✏️ Tip: You can customize this high-level summary in your review settings.