增添分页配置

This commit is contained in:
2026-03-09 09:44:04 +08:00
parent 1673022ea3
commit e5ad13d6f5
6 changed files with 368 additions and 149 deletions

View File

@@ -2069,6 +2069,50 @@ const iconMap: Record<string, string> = {
**解决方案:**
在 `vite.config.js` 中配置 `build.rollupOptions.output.manualChunks`,将 `node_modules` 下的大型库强制拆分为独立文件(如 `vendor-vue`、`vendor-uni`)。这样首屏加载只需下载一次体积恒定的核心库,后续仅下载变化的业务代码。
### 原因三十一:跨域预检失败导致 REST 请求被浏览器拦截 (CORS Preflight)
**现象:**
- 控制台报错:
- `Access to XMLHttpRequest at 'http://192.168.1.61:9122/rest/v1/...' from origin 'http://localhost:5173' has been blocked by CORS policy`
- `Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header`
- `GET ... net::ERR_FAILED`
**原因分析:**
1. H5 调试环境的页面源是 `http://localhost:5173`,请求目标是 `http://192.168.1.61:9122`,属于跨域请求(协议/域名/IP/端口任一不同即跨域)。
2. 业务请求携带 `apikey`、`authorization`、`content-type` 等非简单请求头时,浏览器会先发 `OPTIONS` 预检请求。
3. 目标服务(通常是网关/Kong/Nginx对 `OPTIONS` 响应中缺少 `Access-Control-Allow-Origin`(以及允许方法、允许头),浏览器会在前端直接拦截后续 GET/POST。
4. 配置地址与实际请求地址不一致也会放大问题,例如项目配置可能是 `192.168.1.61:9122`,而实际报错请求是其他主机,两者很可能不是同一服务或同一网关配置。
**解决方案:**
1. **后端网关开启 CORS根本修复**
- 允许来源:`http://localhost:5173`
- 允许方法:`GET, POST, PUT, PATCH, DELETE, OPTIONS`
- 允许请求头:`apikey, authorization, content-type, prefer, x-client-info`
- 确保 `OPTIONS` 返回 `200/204` 且带完整 CORS 响应头
2. **本地开发使用 Vite 代理(前端临时规避)**
- 在 `vite.config.js` 配置 `server.proxy`,将 `/rest/v1`、`/auth/v1` 等路径代理到真实网关,避免浏览器直接跨域。
3. **统一 URL 配置**
- 检查 `ak/config.uts` 中 `SUPA_URL` 与实际发起请求的主机是否一致,避免请求落到未配置 CORS 的地址。
**快速排查命令(后端自检):**
```bash
curl -i -X OPTIONS "http://192.168.1.61:9122/rest/v1/ml_coupon_templates?select=*" \
-H "Origin: http://localhost:5173" \
-H "Access-Control-Request-Method: GET" \
-H "Access-Control-Request-Headers: apikey,authorization,content-type"
```
若响应头中没有 `Access-Control-Allow-Origin`,即可确认是服务端 CORS 配置问题,而非前端语法或请求代码问题。
---
这个指南现在涵盖了 uni-app-x 项目开发中最常见的 30 类问题,为后续开发提供了完整的故障排除和标准化指导。 🚀
这个指南现在涵盖了 uni-app-x 项目开发中最常见的 31 类问题,为后续开发提供了完整的故障排除和标准化指导。 🚀

View File

@@ -122,23 +122,46 @@
</view>
<!-- 分页 -->
<view class="pagination-footer">
<text class="total-txt">共 {{ dataList.length }} 条</text>
<view class="page-select">
<text class="page-val">10 条/页 ▼</text>
</view>
<view class="pagination-footer" v-if="dataList.length > 0 || pageState.loading">
<text class="total-txt">共 {{ pageState.total }} 条</text>
<picker class="page-select" :range="pageSizeOptionLabels" :value="pageSizeIndex" @change="handlePageSizeChange">
<view class="page-select-inner">
<text class="page-val">{{ pageState.pageSize }} 条/页</text>
<text class="arrow-down">▼</text>
</view>
</picker>
<view class="page-btns">
<view class="p-btn disabled"><text></text></view>
<view class="p-btn active"><text>1</text></view>
<view class="p-btn"><text>2</text></view>
<view class="p-btn"><text></text></view>
<view class="p-btn" :class="{ disabled: pageState.currentPage <= 1 }" @click="handlePageChange(pageState.currentPage - 1)">
<text></text>
</view>
<view
v-for="(p, index) in visiblePages"
:key="index"
class="p-btn"
:class="{ active: p === pageState.currentPage, 'ellipsis-btn': p === -1 }"
@click="p !== -1 && handlePageChange(p)">
<text>{{ p === -1 ? '...' : p }}</text>
</view>
<view class="p-btn" :class="{ disabled: pageState.currentPage >= totalPage }" @click="handlePageChange(pageState.currentPage + 1)">
<text></text>
</view>
</view>
<view class="page-jump">
<text class="jump-txt">前往</text>
<input class="jump-input" placeholder="1" />
<input class="jump-input" type="number" v-model="pageState.jumpPageInput" @confirm="handleJumpPage" @blur="handleJumpPage" placeholder="页码" />
<text class="jump-txt">页</text>
</view>
</view>
<!-- 空数据状态 -->
<view class="table-empty" v-if="dataList.length === 0 && !pageState.loading">
<text class="empty-txt">暂无数据</text>
</view>
</view>
</view>
@@ -175,7 +198,7 @@
</template>
<script setup lang="uts">
import { ref, reactive, onMounted } from 'vue'
import { ref, reactive, onMounted, computed } from 'vue'
import supa from '@/components/supadb/aksupainstance.uts'
interface CouponItem {
@@ -220,30 +243,127 @@ const filter = reactive({
const dataList = ref<CouponItem[]>([])
// --- 分页与列表状态管理 ---
const pageState = reactive({
currentPage: 1,
pageSize: 10,
total: 0,
loading: false,
jumpPageInput: ''
})
const pageSizeOptions = [10, 20, 30, 50, 100]
const pageSizeOptionLabels = computed((): string[] => pageSizeOptions.map((size: number): string => `${size} 条/页`))
const pageSizeIndex = computed((): number => {
const index = pageSizeOptions.indexOf(pageState.pageSize)
return index === -1 ? 0 : index
})
const totalPage = computed((): number => {
return Math.ceil(pageState.total / pageState.pageSize)
})
const visiblePages = computed((): number[] => {
const current = pageState.currentPage
const total = totalPage.value
if (total <= 7) {
const pages: number[] = []
for (let i = 1; i <= total; i++) {
pages.push(i)
}
return pages
}
if (current <= 4) {
return [1, 2, 3, 4, 5, -1, total]
}
if (current >= total - 3) {
return [1, -1, total - 4, total - 3, total - 2, total - 1, total]
}
return [1, -1, current - 1, current, current + 1, -1, total]
})
const handlePageSizeChange = (e: any) => {
if (pageState.loading) return
let val = 0
if (typeof e.detail.value === 'string') {
val = parseInt(e.detail.value)
} else {
val = e.detail.value as number
}
pageState.pageSize = pageSizeOptions[val]
pageState.currentPage = 1
fetchCouponTemplates()
}
const handlePageChange = (p: number) => {
if (pageState.loading || p < 1 || p > totalPage.value || p === pageState.currentPage) return
pageState.currentPage = p
pageState.jumpPageInput = ''
fetchCouponTemplates()
}
const handleJumpPage = () => {
if (pageState.loading) return
let jumpTo = parseInt(pageState.jumpPageInput)
if (isNaN(jumpTo)) return
if (jumpTo < 1) jumpTo = 1
if (jumpTo > totalPage.value) jumpTo = totalPage.value
pageState.jumpPageInput = String(jumpTo)
if (jumpTo !== pageState.currentPage) {
pageState.currentPage = jumpTo
fetchCouponTemplates()
}
}
const fetchCouponTemplates = async () => {
pageState.loading = true
uni.showLoading({ title: '加载中...', mask: true })
try {
let query = supa.from('ml_coupon_templates').select('*')
// 兼容使用 { count: 'exact' } 获取精确总数
let query = supa.from('ml_coupon_templates').select('*', { count: 'exact' })
// 如果有名称筛选
if (filter.name.trim() != '') {
query = query.like('name', `%${filter.name}%`)
}
const res = await query.order('created_at', { ascending: false }).execute()
// 计算分页 range
const start = (pageState.currentPage - 1) * pageState.pageSize
const end = pageState.currentPage * pageState.pageSize - 1
const res = await query.order('created_at', { ascending: false }).range(start, end).execute()
if (res.error != null || res.status >= 400) {
const msg = res.error?.message ?? `获取数据失败 (${res.status})`
uni.showToast({ title: msg, icon: 'none' })
return
}
// 尝试安全获取总数。如果有的封装库剥离了 count 属性,则以当前返回的数据长度作为保底
let fCount = 0
if (res.count != null) {
fCount = res.count as number
} else if (res['total'] != null) {
fCount = res['total'] as number
}
if (!Array.isArray(res.data)) {
console.warn('Expected array but got:', res.data)
dataList.value = []
pageState.total = fCount
return
}
const rawData = res.data as Array<UTSJSONObject>
// 如果获取到的总数为 0 但实际有数据,说明接口 count 丢了,用当前拉取的数据量兜底防止分页区坍塌
pageState.total = Math.max(fCount, rawData.length)
dataList.value = rawData.map((item : UTSJSONObject) : CouponItem => {
// 优先获取 cid (自增 ID),如果没有则取 id (UUID) 的后几位或 0
const displayId = (item.get('cid') as number | null) ?? 0
@@ -290,10 +410,14 @@ const fetchCouponTemplates = async () => {
} catch (e) {
console.error('Fetch Coupons Error:', e)
uni.showToast({ title: '访问数据库异常', icon: 'none' })
} finally {
pageState.loading = false
uni.hideLoading()
}
}
const handleQuery = () => {
pageState.currentPage = 1 // 重置到第一页
fetchCouponTemplates()
}
const handleAdd = () => {
@@ -379,9 +503,15 @@ const handleDelete = (item: CouponItem) => {
return
}
// 移除本地列表数据
dataList.value = dataList.value.filter(i => i.id_uuid !== item.id_uuid)
uni.showToast({ title: '删除成功', icon: 'success' })
// 如果当前页只有一条数据,且不是第一页,则退回上一页
if (dataList.value.length === 1 && pageState.currentPage > 1) {
pageState.currentPage--
}
// 重新拉取当前页数据
fetchCouponTemplates()
} catch (e) {
uni.showToast({ title: '操作数据库异常', icon: 'none' })
}
@@ -626,21 +756,80 @@ onMounted(() => {
/* 分页 */
.pagination-footer {
padding: 24px;
padding: 16px 24px;
display: flex;
flex-direction: row;
align-items: center;
justify-content: flex-end;
gap: 12px;
border-top: 1px solid #f0f0f0;
gap: 16px;
border-top: 1px solid #e8eaec;
background-color: #fff;
}
.total-txt { font-size: 14px; color: #606266; }
.page-btns { display: flex; flex-direction: row; gap: 8px; }
.total-txt { font-size: 14px; color: #515a6e; }
.page-select {
border: 1px solid #dcdee2;
border-radius: 4px;
background-color: #fff;
cursor: pointer;
transition: border 0.2s;
}
.page-select:hover { border-color: #2d8cf0; }
.page-select-inner {
display: flex;
flex-direction: row;
align-items: center;
padding: 0 12px;
height: 32px;
gap: 8px;
}
.page-val { font-size: 14px; color: #515a6e; }
.page-btns { display: flex; flex-direction: row; gap: 4px; }
.p-btn {
width: 32px; height: 32px; border: 1px solid #dcdfe6; border-radius: 4px;
display: flex; align-items: center; justify-content: center; font-size: 14px; color: #666;
min-width: 32px; height: 32px; padding: 0 4px; border: 1px solid #dcdee2; border-radius: 4px;
display: flex; align-items: center; justify-content: center; font-size: 14px; color: #515a6e;
background-color: #fff; cursor: pointer; transition: all 0.2s;
}
.p-btn:hover:not(.disabled):not(.active):not(.ellipsis-btn) {
border-color: #2d8cf0; color: #2d8cf0;
}
.p-btn.active { background-color: #2d8cf0; border-color: #2d8cf0; color: #fff; }
.p-btn.disabled { color: #c5c8ce; background-color: #f7f7f7; cursor: not-allowed; border-color: #dcdee2; }
.p-btn.ellipsis-btn { border: none; cursor: default; }
.page-jump {
display: flex;
flex-direction: row;
align-items: center;
gap: 8px;
}
.jump-txt { font-size: 14px; color: #515a6e; }
.jump-input {
width: 50px;
height: 32px;
border: 1px solid #dcdee2;
border-radius: 4px;
text-align: center;
font-size: 14px;
color: #515a6e;
transition: border 0.2s;
}
.jump-input:focus { border-color: #2d8cf0; outline: none; }
/* 空数据状态 */
.table-empty {
padding: 60px 0;
display: flex;
justify-content: center;
align-items: center;
background-color: #fff;
border-top: 1px solid #e8eaec;
}
.empty-txt {
font-size: 14px;
color: #909399;
}
/* 🎭 模态框样式 */
.modal-mask {

View File

@@ -144,9 +144,10 @@
<view class="td col-op overflow-visible no-wrap">
<view class="op-links">
<text class="op-link primary" @click.stop="handleAction('edit', item['sn'] as string)">编辑</text>
<view class="divider-v"></view>
<view class="op-dropdown-container">
<view class="op-link-more" @click.stop="toggleDropdown(item['sn'] as string)">
<text :class="{ 'more-text-active': activeDropdownId === item['sn'] }" class="more-text">更多</text>
<text class="more-text">更多</text>
<view :class="{ 'arrow-up-blue': activeDropdownId === item['sn'], 'arrow-down-blue': activeDropdownId !== item['sn'] }"></view>
</view>
<!-- 浮动菜单 -->
@@ -154,7 +155,7 @@
<view class="dropdown-item" @click.stop="viewDetail(item)">
<text class="item-text">订单详情</text>
</view>
<view class="dropdown-item danger" @click.stop="deleteOrder(item)">
<view class="dropdown-item item-danger" @click.stop="deleteOrder(item)">
<text class="item-text text-red">删除订单</text>
</view>
</view>
@@ -735,11 +736,26 @@ onMounted(() => {
display: flex;
flex-direction: row;
align-items: center;
gap: 12px;
}
.op-link { font-size: 13px; cursor: pointer; }
.primary { color: #1890ff; }
.primary { color: #1890ff; padding-right: 8px; }
.divider-v {
width: 1px;
height: 12px;
background-color: #dcdfe6;
margin: 0 8px;
}
.op-dropdown-container {
position: relative;
display: flex;
flex-direction: row;
align-items: center;
cursor: pointer;
padding-left: 0px;
}
.op-link-more {
display: flex;
@@ -747,15 +763,55 @@ onMounted(() => {
align-items: center;
gap: 4px;
}
.more-text { font-size: 13px; color: #1890ff; transition: color 0.1s; }
.more-text-active { font-weight: bold; }
.more-text { font-size: 13px; color: #1890ff; }
.dropdown-menu {
position: absolute;
top: 100%;
left: 50%;
transform: translateX(-50%);
width: 110px;
background-color: #ffffff;
border-radius: 4px;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
margin-top: 8px;
z-index: 100000;
border: 1px solid #ebeef5;
display: flex;
flex-direction: column;
}
.dropdown-item {
height: 32px;
padding: 0 16px;
display: flex;
flex-direction: row;
align-items: center;
justify-content: center;
transition: background-color 0.2s;
cursor: pointer;
.item-text { font-size: 13px; color: #606266; text-align: center; }
&:hover {
background-color: #f5f7fa;
.item-text { color: #1890ff; }
}
}
.item-danger:hover {
.item-text { color: #ff4d4f !important; }
}
.text-red { color: #ff4d4f !important; }
/* 箭头 */
.arrow-down-blue {
width: 0; height: 0;
border-left: 3px solid transparent;
border-right: 3px solid transparent;
border-top: 4px solid #1890ff;
margin-top: 2px;
margin-top: 1px;
}
.arrow-up-blue {
@@ -763,9 +819,8 @@ onMounted(() => {
border-left: 3px solid transparent;
border-right: 3px solid transparent;
border-bottom: 4px solid #1890ff;
margin-bottom: 3px;
}
/* 分页 */
.pagination-footer {
padding: 16px 20px;
display: flex;

View File

@@ -1,20 +1,6 @@
list.uvue?t=1772068177429&import:1010 [Vue warn]: Property "insertImage" was accessed during render but is not defined on instance.
at <View>
at <View>
at <View>
at <ScrollView>
at <View>
at <View>
at <View>
at <List>
at <View>
at <View>
at <View>
at <View>
at <AdminLayout>
at <View>
at <Index>
at <AsyncComponentWrapper>
GET http://localhost:5173/pages/mall/admin/marketing/coupon/list.uvue?import net::ERR_ABORTED 500 (Internal Server Error)
main.uts:16 [Vue warn]: Unhandled error during execution of async component loader
at <AsyncComponentWrapper>
at <PageBody>
at <Page>
at <Anonymous>
@@ -25,48 +11,24 @@ at <App>
warnHandler @ uni-h5.es.js:19975
callWithErrorHandling @ vue.runtime.esm.js:1381
warn$1 @ vue.runtime.esm.js:1207
get @ vue.runtime.esm.js:4455
(anonymous) @ list.uvue?t=1772068177429&import:1010
renderFnWithContext @ vue.runtime.esm.js:2033
renderSlot @ vue.runtime.esm.js:4254
(anonymous) @ uni-h5.es.js:18666
renderComponentRoot @ vue.runtime.esm.js:2092
componentUpdateFn @ vue.runtime.esm.js:7365
run @ vue.runtime.esm.js:153
instance.update @ vue.runtime.esm.js:7497
setupRenderEffect @ vue.runtime.esm.js:7507
mountComponent @ vue.runtime.esm.js:7274
logError @ vue.runtime.esm.js:1438
errorHandler @ uni-h5.es.js:19600
callWithErrorHandling @ vue.runtime.esm.js:1381
handleError @ vue.runtime.esm.js:1421
onError @ vue.runtime.esm.js:3724
(anonymous) @ vue.runtime.esm.js:3767
Promise.catch
setup @ vue.runtime.esm.js:3766
callWithErrorHandling @ vue.runtime.esm.js:1381
setupStatefulComponent @ vue.runtime.esm.js:8985
setupComponent @ vue.runtime.esm.js:8946
mountComponent @ vue.runtime.esm.js:7262
processComponent @ vue.runtime.esm.js:7228
patch @ vue.runtime.esm.js:6694
mountChildren @ vue.runtime.esm.js:6942
processFragment @ vue.runtime.esm.js:7158
patch @ vue.runtime.esm.js:6668
mountChildren @ vue.runtime.esm.js:6942
mountElement @ vue.runtime.esm.js:6849
processElement @ vue.runtime.esm.js:6814
patch @ vue.runtime.esm.js:6682
componentUpdateFn @ vue.runtime.esm.js:7372
run @ vue.runtime.esm.js:153
instance.update @ vue.runtime.esm.js:7497
setupRenderEffect @ vue.runtime.esm.js:7507
mountComponent @ vue.runtime.esm.js:7274
processComponent @ vue.runtime.esm.js:7228
patch @ vue.runtime.esm.js:6694
mountChildren @ vue.runtime.esm.js:6942
processFragment @ vue.runtime.esm.js:7158
patch @ vue.runtime.esm.js:6668
mountChildren @ vue.runtime.esm.js:6942
mountElement @ vue.runtime.esm.js:6849
processElement @ vue.runtime.esm.js:6814
patch @ vue.runtime.esm.js:6682
componentUpdateFn @ vue.runtime.esm.js:7372
run @ vue.runtime.esm.js:153
instance.update @ vue.runtime.esm.js:7497
setupRenderEffect @ vue.runtime.esm.js:7507
mountComponent @ vue.runtime.esm.js:7274
processComponent @ vue.runtime.esm.js:7228
patch @ vue.runtime.esm.js:6694
mountChildren @ vue.runtime.esm.js:6942
processFragment @ vue.runtime.esm.js:7158
patch @ vue.runtime.esm.js:6668
mountChildren @ vue.runtime.esm.js:6942
@@ -78,23 +40,15 @@ mountElement @ vue.runtime.esm.js:6849
processElement @ vue.runtime.esm.js:6814
patch @ vue.runtime.esm.js:6682
mountChildren @ vue.runtime.esm.js:6942
mountElement @ vue.runtime.esm.js:6849
processElement @ vue.runtime.esm.js:6814
patch @ vue.runtime.esm.js:6682
mountChildren @ vue.runtime.esm.js:6942
mountElement @ vue.runtime.esm.js:6849
processElement @ vue.runtime.esm.js:6814
patch @ vue.runtime.esm.js:6682
componentUpdateFn @ vue.runtime.esm.js:7372
run @ vue.runtime.esm.js:153
instance.update @ vue.runtime.esm.js:7497
setupRenderEffect @ vue.runtime.esm.js:7507
mountComponent @ vue.runtime.esm.js:7274
processComponent @ vue.runtime.esm.js:7228
patch @ vue.runtime.esm.js:6694
mountChildren @ vue.runtime.esm.js:6942
processFragment @ vue.runtime.esm.js:7158
patch @ vue.runtime.esm.js:6668
componentUpdateFn @ vue.runtime.esm.js:7372
run @ vue.runtime.esm.js:153
instance.update @ vue.runtime.esm.js:7497
setupRenderEffect @ vue.runtime.esm.js:7507
mountComponent @ vue.runtime.esm.js:7274
processComponent @ vue.runtime.esm.js:7228
patch @ vue.runtime.esm.js:6694
mountChildren @ vue.runtime.esm.js:6942
mountElement @ vue.runtime.esm.js:6849
processElement @ vue.runtime.esm.js:6814
@@ -106,13 +60,6 @@ setupRenderEffect @ vue.runtime.esm.js:7507
mountComponent @ vue.runtime.esm.js:7274
processComponent @ vue.runtime.esm.js:7228
patch @ vue.runtime.esm.js:6694
mountChildren @ vue.runtime.esm.js:6942
processFragment @ vue.runtime.esm.js:7158
patch @ vue.runtime.esm.js:6668
mountChildren @ vue.runtime.esm.js:6942
mountElement @ vue.runtime.esm.js:6849
processElement @ vue.runtime.esm.js:6814
patch @ vue.runtime.esm.js:6682
componentUpdateFn @ vue.runtime.esm.js:7372
run @ vue.runtime.esm.js:153
instance.update @ vue.runtime.esm.js:7497
@@ -120,14 +67,12 @@ setupRenderEffect @ vue.runtime.esm.js:7507
mountComponent @ vue.runtime.esm.js:7274
processComponent @ vue.runtime.esm.js:7228
patch @ vue.runtime.esm.js:6694
patchBlockChildren @ vue.runtime.esm.js:7084
processFragment @ vue.runtime.esm.js:7176
patch @ vue.runtime.esm.js:6668
patchKeyedChildren @ vue.runtime.esm.js:7650
patchChildren @ vue.runtime.esm.js:7564
patchElement @ vue.runtime.esm.js:6989
processElement @ vue.runtime.esm.js:6825
patch @ vue.runtime.esm.js:6682
componentUpdateFn @ vue.runtime.esm.js:7453
run @ vue.runtime.esm.js:153
instance.update @ vue.runtime.esm.js:7497
updateComponent @ vue.runtime.esm.js:7305
processComponent @ vue.runtime.esm.js:7239
patch @ vue.runtime.esm.js:6694
componentUpdateFn @ vue.runtime.esm.js:7453
run @ vue.runtime.esm.js:153
instance.update @ vue.runtime.esm.js:7497
@@ -136,36 +81,20 @@ flushJobs @ vue.runtime.esm.js:1585
Promise.then
queueFlush @ vue.runtime.esm.js:1494
queueJob @ vue.runtime.esm.js:1488
(anonymous) @ vue.runtime.esm.js:7491
scheduler @ vue.runtime.esm.js:3179
resetScheduling @ vue.runtime.esm.js:236
triggerEffects @ vue.runtime.esm.js:280
triggerRefValue @ vue.runtime.esm.js:1033
set value @ vue.runtime.esm.js:1078
handleAdd @ list.uvue:297
callWithErrorHandling @ vue.runtime.esm.js:1381
callWithAsyncErrorHandling @ vue.runtime.esm.js:1388
invoker @ vue.runtime.esm.js:10253
list.uvue?t=1772068177429&import:1028 [Vue warn]: Property "onEditorReady" was accessed during render but is not defined on instance.
at <View>
at <View>
at <View>
at <ScrollView>
at <View>
at <View>
at <View>
at <List>
at <View>
at <View>
at <View>
at <View>
at <AdminLayout>
at <View>
at <Index>
at <AsyncComponentWrapper>
at <PageBody>
at <Page>
at <Anonymous>
at <KeepAlive>
at <RouterView>
at <Layout>
at <App>
finalizeNavigation @ vue-router.mjs?v=ed041164:2474
(anonymous) @ vue-router.mjs?v=ed041164:2384
Promise.then
pushWithRedirect @ vue-router.mjs?v=ed041164:2352
push @ vue-router.mjs?v=ed041164:2278
install @ vue-router.mjs?v=ed041164:2631
use @ vue.runtime.esm.js:5190
initRouter @ uni-h5.es.js:19886
install @ uni-h5.es.js:19955
use @ vue.runtime.esm.js:5190
(anonymous) @ main.uts:16
main.uts:16 TypeError: Failed to fetch dynamically imported module: http://localhost:5173/pages/mall/admin/homePage/index.uvue?import