完成全部页面分页组件的抽取2

This commit is contained in:
2026-03-17 11:21:29 +08:00
parent e266482f88
commit 4041933e42
19 changed files with 344 additions and 492 deletions

View File

@@ -1,25 +0,0 @@
<template>
<AdminLayout :currentPage="currentPage">
<view class="page">
<view class="header">
<text class="title">{{ title }}</text>
<text class="sub-title">页面已修复 (UTF-8)</text>
</view>
</view>
</AdminLayout>
</template>
<script setup lang="uts">
import { ref } from 'vue'
import AdminLayout from '@/layouts/admin/AdminLayout.uvue'
const currentPage = ref<string>('category')
const title = ref<string>('category')
</script>
<style scoped lang="scss">
@import '@/uni.scss';
.page { padding: 0; }
.header { padding: $space-lg; border-radius: $radius; background: $background-primary; box-shadow: $shadow-xs; }
.title { font-size: $font-size-lg; font-weight: $font-weight-bold; color: $text-primary; }
.sub-title { margin-top: $space-xs; font-size: $font-size-md; color: $text-secondary; }
</style>

View File

@@ -1,25 +0,0 @@
<template>
<AdminLayout :currentPage="currentPage">
<view class="page">
<view class="header">
<text class="title">{{ title }}</text>
<text class="sub-title">页面已修复 (UTF-8)</text>
</view>
</view>
</AdminLayout>
</template>
<script setup lang="uts">
import { ref } from 'vue'
import AdminLayout from '@/layouts/admin/AdminLayout.uvue'
const currentPage = ref<string>('create')
const title = ref<string>('create')
</script>
<style scoped lang="scss">
@import '@/uni.scss';
.page { padding: 0; }
.header { padding: $space-lg; border-radius: $radius; background: $background-primary; box-shadow: $shadow-xs; }
.title { font-size: $font-size-lg; font-weight: $font-weight-bold; color: $text-primary; }
.sub-title { margin-top: $space-xs; font-size: $font-size-md; color: $text-secondary; }
</style>

View File

@@ -1,25 +0,0 @@
<template>
<AdminLayout :currentPage="currentPage">
<view class="page">
<view class="header">
<text class="title">{{ title }}</text>
<text class="sub-title">页面已修复 (UTF-8)</text>
</view>
</view>
</AdminLayout>
</template>
<script setup lang="uts">
import { ref } from 'vue'
import AdminLayout from '@/layouts/admin/AdminLayout.uvue'
const currentPage = ref<string>('edit')
const title = ref<string>('edit')
</script>
<style scoped lang="scss">
@import '@/uni.scss';
.page { padding: 0; }
.header { padding: $space-lg; border-radius: $radius; background: $background-primary; box-shadow: $shadow-xs; }
.title { font-size: $font-size-lg; font-weight: $font-weight-bold; color: $text-primary; }
.sub-title { margin-top: $space-xs; font-size: $font-size-md; color: $text-secondary; }
</style>

View File

@@ -1,25 +0,0 @@
<template>
<AdminLayout :currentPage="currentPage">
<view class="page">
<view class="header">
<text class="title">{{ title }}</text>
<text class="sub-title">页面已修复 (UTF-8)</text>
</view>
</view>
</AdminLayout>
</template>
<script setup lang="uts">
import { ref } from 'vue'
import AdminLayout from '@/layouts/admin/AdminLayout.uvue'
const currentPage = ref<string>('article-index')
const title = ref<string>('文章管理')
</script>
<style scoped lang="scss">
@import '@/uni.scss';
.page { padding: 0; }
.header { padding: $space-lg; border-radius: $radius; background: $background-primary; box-shadow: $shadow-xs; }
.title { font-size: $font-size-lg; font-weight: $font-weight-bold; color: $text-primary; }
.sub-title { margin-top: $space-xs; font-size: $font-size-md; color: $text-secondary; }
</style>

View File

@@ -1,65 +0,0 @@
<template>
<AdminLayout currentPage="content-list">
<view class="Page">
<view class="Header">
<text class="Title">文章管理</text>
<text class="SubTitle">content/index</text>
</view>
<view class="Card">
<text class="Label">页面参数query</text>
<text class="Mono">{{ params }}</text>
</view>
</view>
</AdminLayout>
</template>
<script setup lang="uts">
import { ref } from 'vue'
import { onLoad } from '@dcloudio/uni-app'
import AdminLayout from '@/layouts/admin/AdminLayout.uvue'
const params = ref('')
onLoad((options) => {
// options: Record<string, any>
params.value = JSON.stringify(options ?? {})
})
</script>
<style>
.Page {
padding: 0;
}
.Header {
padding: 24rpx;
border-radius: 16rpx;
background: #ffffff;
}
.Title {
font-size: 36rpx;
font-weight: 700;
}
.SubTitle {
margin-top: 8rpx;
font-size: 24rpx;
opacity: 0.7;
}
.Card {
margin-top: 24rpx;
padding: 24rpx;
border-radius: 16rpx;
background: #ffffff;
}
.Label {
font-size: 26rpx;
font-weight: 600;
margin-bottom: 12rpx;
}
.Mono {
font-size: 24rpx;
font-family: monospace;
line-height: 36rpx;
word-break: break-all;
}
</style>

View File

@@ -1,25 +0,0 @@
<template>
<AdminLayout :currentPage="currentPage">
<view class="page">
<view class="header">
<text class="title">{{ title }}</text>
<text class="sub-title">页面已修复 (UTF-8)</text>
</view>
</view>
</AdminLayout>
</template>
<script setup lang="uts">
import { ref } from 'vue'
import AdminLayout from '@/layouts/admin/AdminLayout.uvue'
const currentPage = ref<string>('design-category')
const title = ref<string>('分类页装修')
</script>
<style scoped lang="scss">
@import '@/uni.scss';
.page { padding: $space-lg; }
.header { padding: $space-lg; border-radius: $radius; background: $background-primary; box-shadow: $shadow-xs; }
.title { font-size: $font-size-lg; font-weight: $font-weight-bold; color: $text-primary; }
.sub-title { margin-top: $space-xs; font-size: $font-size-md; color: $text-secondary; }
</style>

View File

@@ -1,25 +0,0 @@
<template>
<AdminLayout :currentPage="currentPage">
<view class="page">
<view class="header">
<text class="title">{{ title }}</text>
<text class="sub-title">页面已修复 (UTF-8)</text>
</view>
</view>
</AdminLayout>
</template>
<script setup lang="uts">
import { ref } from 'vue'
import AdminLayout from '@/layouts/admin/AdminLayout.uvue'
const currentPage = ref<string>('category')
const title = ref<string>('category')
</script>
<style scoped lang="scss">
@import '@/uni.scss';
.page { padding: $space-lg; }
.header { padding: $space-lg; border-radius: $radius; background: $background-primary; box-shadow: $shadow-xs; }
.title { font-size: $font-size-lg; font-weight: $font-weight-bold; color: $text-primary; }
.sub-title { margin-top: $space-xs; font-size: $font-size-md; color: $text-secondary; }
</style>

View File

@@ -1,25 +0,0 @@
<template>
<AdminLayout :currentPage="currentPage">
<view class="page">
<view class="header">
<text class="title">{{ title }}</text>
<text class="sub-title">页面已修复 (UTF-8)</text>
</view>
</view>
</AdminLayout>
</template>
<script setup lang="uts">
import { ref } from 'vue'
import AdminLayout from '@/layouts/admin/AdminLayout.uvue'
const currentPage = ref<string>('custom')
const title = ref<string>('custom')
</script>
<style scoped lang="scss">
@import '@/uni.scss';
.page { padding: $space-lg; }
.header { padding: $space-lg; border-radius: $radius; background: $background-primary; box-shadow: $shadow-xs; }
.title { font-size: $font-size-lg; font-weight: $font-weight-bold; color: $text-primary; }
.sub-title { margin-top: $space-xs; font-size: $font-size-md; color: $text-secondary; }
</style>

View File

@@ -1,38 +0,0 @@
<template>
<AdminLayout current-page="design-data">
<view class="admin-main">
<view class="header">
<text class="title">数据配置</text>
</view>
<view class="content">
<text>商城数据配置(建设中)</text>
</view>
</view>
</AdminLayout>
</template>
<script setup lang="uts">
import AdminLayout from '@/layouts/admin/AdminLayout.uvue'
</script>
<style scoped>
.admin-main {
padding: 20px;
}
.header {
margin-bottom: 20px;
}
.title {
font-size: 20px;
font-weight: bold;
}
.content {
background-color: #fff;
padding: 20px;
border-radius: 4px;
min-height: 400px;
display: flex;
justify-content: center;
align-items: center;
}
</style>

View File

@@ -1,25 +0,0 @@
<template>
<AdminLayout :currentPage="currentPage">
<view class="page">
<view class="header">
<text class="title">{{ title }}</text>
<text class="sub-title">页面已修复 (UTF-8)</text>
</view>
</view>
</AdminLayout>
</template>
<script setup lang="uts">
import { ref } from 'vue'
import AdminLayout from '@/layouts/admin/AdminLayout.uvue'
const currentPage = ref<string>('design-homepage')
const title = ref<string>('首页装修')
</script>
<style scoped lang="scss">
@import '@/uni.scss';
.page { padding: $space-lg; }
.header { padding: $space-lg; border-radius: $radius; background: $background-primary; box-shadow: $shadow-xs; }
.title { font-size: $font-size-lg; font-weight: $font-weight-bold; color: $text-primary; }
.sub-title { margin-top: $space-xs; font-size: $font-size-md; color: $text-secondary; }
</style>

View File

@@ -1,65 +0,0 @@
<template>
<AdminLayout currentPage="design-home">
<view class="Page">
<view class="Header">
<text class="Title">页面装修</text>
<text class="SubTitle">design/index</text>
</view>
<view class="Card">
<text class="Label">页面参数query</text>
<text class="Mono">{{ params }}</text>
</view>
</view>
</AdminLayout>
</template>
<script setup lang="uts">
import { ref } from 'vue'
import { onLoad } from '@dcloudio/uni-app'
import AdminLayout from '@/layouts/admin/AdminLayout.uvue'
const params = ref('')
onLoad((options) => {
// options: Record<string, any>
params.value = JSON.stringify(options ?? {})
})
</script>
<style>
.Page {
padding: 24rpx;
}
.Header {
padding: 24rpx;
border-radius: 16rpx;
background: #ffffff;
}
.Title {
font-size: 36rpx;
font-weight: 700;
}
.SubTitle {
margin-top: 8rpx;
font-size: 24rpx;
opacity: 0.7;
}
.Card {
margin-top: 24rpx;
padding: 24rpx;
border-radius: 16rpx;
background: #ffffff;
}
.Label {
font-size: 26rpx;
font-weight: 600;
margin-bottom: 12rpx;
}
.Mono {
font-size: 24rpx;
font-family: monospace;
line-height: 36rpx;
word-break: break-all;
}
</style>

View File

@@ -1,25 +0,0 @@
<template>
<AdminLayout :currentPage="currentPage">
<view class="page">
<view class="header">
<text class="title">{{ title }}</text>
<text class="sub-title">页面已修复 (UTF-8)</text>
</view>
</view>
</AdminLayout>
</template>
<script setup lang="uts">
import { ref } from 'vue'
import AdminLayout from '@/layouts/admin/AdminLayout.uvue'
const currentPage = ref<string>('product')
const title = ref<string>('product')
</script>
<style scoped lang="scss">
@import '@/uni.scss';
.page { padding: $space-lg; }
.header { padding: $space-lg; border-radius: $radius; background: $background-primary; box-shadow: $shadow-xs; }
.title { font-size: $font-size-lg; font-weight: $font-weight-bold; color: $text-primary; }
.sub-title { margin-top: $space-xs; font-size: $font-size-md; color: $text-secondary; }
</style>

View File

@@ -1,38 +0,0 @@
<template>
<AdminLayout current-page="design-user">
<view class="admin-main">
<view class="header">
<text class="title">个人中心装修</text>
</view>
<view class="content">
<text>个人中心页面装修(建设中)</text>
</view>
</view>
</AdminLayout>
</template>
<script setup lang="uts">
import AdminLayout from '@/layouts/admin/AdminLayout.uvue'
</script>
<style scoped>
.admin-main {
padding: 20px;
}
.header {
margin-bottom: 20px;
}
.title {
font-size: 20px;
font-weight: bold;
}
.content {
background-color: #fff;
padding: 20px;
border-radius: 4px;
min-height: 400px;
display: flex;
justify-content: center;
align-items: center;
}
</style>

View File

@@ -28,7 +28,7 @@
<view v-if="list.length === 0" class="empty-box"> <view v-if="list.length === 0" class="empty-box">
<text class="empty-text">暂无数据</text> <text class="empty-text">暂无数据</text>
</view> </view>
<view v-for="(item, index) in list" :key="index" class="table-row"> <view v-for="(item, index) in pagedList" :key="index" class="table-row">
<text class="td-cell flex-1 color-9">{{ item.id }}</text> <text class="td-cell flex-1 color-9">{{ item.id }}</text>
<text class="td-cell flex-3">{{ item.name }}</text> <text class="td-cell flex-3">{{ item.name }}</text>
<view class="td-cell flex-5"> <view class="td-cell flex-5">
@@ -41,6 +41,23 @@
</view> </view>
</view> </view>
</view> </view>
<CommonPagination
v-if="true"
:total="total"
:loading="false"
:currentPage="currentPage"
:pageSize="pageSize"
:pageSizeOptionLabels="pageSizeOptionLabels"
:pageSizeIndex="pageSizeIndex"
:visiblePages="visiblePages"
:totalPage="totalPage"
:jumpPageInput="jumpPageInput"
@page-size-change="handlePageSizeChange"
@page-change="handlePageChange"
@update:jumpPageInput="(val: string) => { jumpPageInput.value = val }"
@jump-page="handleJumpPage"
/>
</view> </view>
<!-- 添加/编辑参数抽屉 (右侧 50%) --> <!-- 添加/编辑参数抽屉 (右侧 50%) -->
@@ -101,7 +118,8 @@
</template> </template>
<script setup lang="uts"> <script setup lang="uts">
import { ref, reactive } from 'vue' import { ref, reactive, computed } from 'vue'
import CommonPagination from '@/components/CommonPagination/CommonPagination.uvue'
interface ParamKV { interface ParamKV {
label: string; label: string;
@@ -115,10 +133,55 @@ sort: number;
params: ParamKV[]; params: ParamKV[];
} }
// ========== MOCK DATA START ==========
// TODO: 接真实接口时替换此处 list 为 fetchParamList() 调用
const list = reactive<ParamItem[]>([ const list = reactive<ParamItem[]>([
{ id: 1, name: '手机数码', sort: 1, params: [{label: '品牌', value: '华为'}, {label: '型号', value: 'Mate 60'}] as ParamKV[] }, { id: 1, name: '手机数码', sort: 1, params: [{label: '品牌', value: '华为'}, {label: '型号', value: 'Mate 60'}] as ParamKV[] },
{ id: 2, name: '家用电器', sort: 2, params: [{label: '能效等级', value: '一级'}, {label: '产地', value: '中国'}] as ParamKV[] } { id: 2, name: '家用电器', sort: 2, params: [{label: '能效等级', value: '一级'}, {label: '产地', value: '中国'}] as ParamKV[] },
{ id: 3, name: '服装鞋履', sort: 3, params: [{label: '面料', value: '纯棉'}, {label: '适用季节', value: '春夏'}] as ParamKV[] },
{ id: 4, name: '食品饮料', sort: 4, params: [{label: '保质期', value: '12个月'}, {label: '常温存储', value: '是'}] as ParamKV[] },
{ id: 5, name: '家具家居', sort: 5, params: [{label: '材质', value: '实木'}, {label: '风格', value: '新中式'}] as ParamKV[] },
{ id: 6, name: '美妆护肤', sort: 6, params: [{label: '肉质', value: '混合背'}, {label: '容量', value: '50ml'}] as ParamKV[] },
{ id: 7, name: '图书文具', sort: 7, params: [{label: '出版社', value: '人民兰山'}, {label: '平装/精装', value: '精装'}] as ParamKV[] },
{ id: 8, name: '运动户外', sort: 8, params: [{label: '适用季节', value: '冬季'}, {label: '防水等级', value: 'IPX5'}] as ParamKV[] },
{ id: 9, name: '母婴童装', sort: 9, params: [{label: '适用年龄', value: '0-3岁'}, {label: '安全认证', value: 'CCC'}] as ParamKV[] },
{ id: 10, name: '創业特惠', sort: 10, params: [{label: '折扣力度', value: '9折'}, {label: '限时时间', value: '7天'}] as ParamKV[] },
{ id: 11, name: '山地车辆', sort: 11, params: [{label: '厂家', value: '丰田'}, {label: '排量', value: '2.0T'}] as ParamKV[] },
{ id: 12, name: '唨具家电', sort: 12, params: [{label: '功率', value: '1500W'}, {label: '容量', value: '5L'}] as ParamKV[] }
]) ])
// ========== MOCK DATA END ==========
// ========== PAGINATION STATE ==========
const currentPage = ref(1)
const pageSize = ref(10)
const jumpPageInput = ref('')
const pageSizeOptions = [10, 15, 20, 30, 50]
const pageSizeOptionLabels = computed(() => pageSizeOptions.map((n: number) => `${n}条/页`))
const pageSizeIndex = computed(() => { const idx = pageSizeOptions.indexOf(pageSize.value); return idx >= 0 ? idx : 0 })
const total = computed(() => list.length)
const totalPage = computed(() => Math.max(1, Math.ceil(total.value / pageSize.value)))
const pagedList = computed(() => {
const start = (currentPage.value - 1) * pageSize.value
return list.slice(start, start + pageSize.value)
})
const visiblePages = computed((): number[] => {
const t = totalPage.value; const cur = currentPage.value
if (t <= 7) return Array.from({ length: t }, (_: any, i: number) => i + 1)
if (cur <= 4) return [1, 2, 3, 4, 5, -1, t]
if (cur >= t - 3) return [1, -1, t - 4, t - 3, t - 2, t - 1, t]
return [1, -1, cur - 1, cur, cur + 1, -1, t]
})
const handlePageChange = (p: number) => { currentPage.value = p }
const handlePageSizeChange = (e: any) => {
const idx = Number(e.detail.value)
pageSize.value = pageSizeOptions[idx] ?? pageSizeOptions[0]
currentPage.value = 1
}
const handleJumpPage = () => {
const p = parseInt(jumpPageInput.value)
if (!isNaN(p) && p >= 1 && p <= totalPage.value) currentPage.value = p
}
// ========== END PAGINATION STATE ==========
const showDrawerMask = ref(false) const showDrawerMask = ref(false)
const showDrawer = ref(false) const showDrawer = ref(false)

View File

@@ -24,7 +24,7 @@
<view v-if="list.length === 0" class="empty-box"> <view v-if="list.length === 0" class="empty-box">
<text class="empty-text">暂无数据</text> <text class="empty-text">暂无数据</text>
</view> </view>
<view v-for="(item, index) in list" :key="index" class="table-row-item"> <view v-for="(item, index) in pagedList" :key="index" class="table-row-item">
<text class="td flex-1 color-9">{{ item.id }}</text> <text class="td flex-1 color-9">{{ item.id }}</text>
<view class="td flex-2"> <view class="td flex-2">
<image class="protection-icon-img" :src="item.icon" mode="aspectFit"></image> <image class="protection-icon-img" :src="item.icon" mode="aspectFit"></image>
@@ -42,6 +42,22 @@
</view> </view>
</view> </view>
</view> </view>
<CommonPagination
v-if="true"
:total="total"
:loading="false"
:currentPage="currentPage"
:pageSize="pageSize"
:pageSizeOptionLabels="pageSizeOptionLabels"
:pageSizeIndex="pageSizeIndex"
:visiblePages="visiblePages"
:totalPage="totalPage"
:jumpPageInput="jumpPageInput"
@page-size-change="handlePageSizeChange"
@page-change="handlePageChange"
@update:jumpPageInput="(val: string) => { jumpPageInput.value = val }"
@jump-page="handleJumpPage"
/>
<!-- 添加/编辑弹窗 (居中 Modal) --> <!-- 添加/编辑弹窗 (居中 Modal) -->
<view class="modal-overlay" v-if="showModal" @click="closeModal"> <view class="modal-overlay" v-if="showModal" @click="closeModal">
@@ -102,8 +118,9 @@
</template> </template>
<script setup lang="uts"> <script setup lang="uts">
import { ref, reactive } from 'vue' import { ref, reactive, computed } from 'vue'
import StatusSwitch from '@/components/StatusSwitch.uvue' import StatusSwitch from '@/components/StatusSwitch.uvue'
import CommonPagination from '@/components/CommonPagination/CommonPagination.uvue'
interface ProtectionItem { interface ProtectionItem {
id: number; id: number;
@@ -114,10 +131,53 @@ interface ProtectionItem {
sort: number; sort: number;
} }
// ========== MOCK DATA START ==========
// TODO: 接真实接口时替换此处 list 为 fetchProtectionList() 调用
const list = reactive<ProtectionItem[]>([ const list = reactive<ProtectionItem[]>([
{ id: 1, name: '正品保障', icon: '/static/logo.png', desc: '该商品由平台认证,保证百分百正品。', status: true, sort: 0 }, { id: 1, name: '正品保障', icon: '/static/logo.png', desc: '该商品由平台认证,保证百分百正品。', status: true, sort: 0 },
{ id: 2, name: '七天无理由', icon: '/static/logo.png', desc: '商品在不影响二次销售的情况下,支持7天无理由退换。', status: true, sort: 0 } { id: 2, name: '七天无理由', icon: '/static/logo.png', desc: '商品在不影响二次销售的情况下,支持 7 天无理由退换。', status: true, sort: 0 },
{ id: 3, name: '价格保指', icon: '/static/logo.png', desc: '购买后 30 天内如遇同款低价,即可申请价差补偿。', status: true, sort: 1 },
{ id: 4, name: '全程颜料隐形', icon: '/static/logo.png', desc: '顺丰乐丰包装,不露商品信息,注重隐次保护。', status: true, sort: 2 },
{ id: 5, name: '隐私保护', icon: '/static/logo.png', desc: '尥尺保护您的个人信息,不向任何第三方泄露。', status: true, sort: 3 },
{ id: 6, name: '即时客服', icon: '/static/logo.png', desc: '7×24小时在线客服随时解决您的问题。', status: true, sort: 4 },
{ id: 7, name: '准时发货', icon: '/static/logo.png', desc: '下单后 48 小时内发货,快递全程跟踪。', status: false, sort: 5 },
{ id: 8, name: '免费退返运', icon: '/static/logo.png', desc: '指定品类商品支持免费退返运。', status: true, sort: 6 },
{ id: 9, name: '官方维修', icon: '/static/logo.png', desc: '各地维修中心 500+,提供上门维修服务。', status: true, sort: 7 },
{ id: 10, name: '分期免息', icon: '/static/logo.png', desc: '支持花唉/支付分期,指定商品免息付款。', status: false, sort: 8 }
]) ])
// ========== MOCK DATA END ==========
// ========== PAGINATION STATE ==========
const currentPage = ref(1)
const pageSize = ref(10)
const jumpPageInput = ref('')
const pageSizeOptions = [10, 15, 20, 30, 50]
const pageSizeOptionLabels = computed(() => pageSizeOptions.map((n: number) => `${n}条/页`))
const pageSizeIndex = computed(() => { const idx = pageSizeOptions.indexOf(pageSize.value); return idx >= 0 ? idx : 0 })
const total = computed(() => list.length)
const totalPage = computed(() => Math.max(1, Math.ceil(total.value / pageSize.value)))
const pagedList = computed(() => {
const start = (currentPage.value - 1) * pageSize.value
return list.slice(start, start + pageSize.value)
})
const visiblePages = computed((): number[] => {
const t = totalPage.value; const cur = currentPage.value
if (t <= 7) return Array.from({ length: t }, (_: any, i: number) => i + 1)
if (cur <= 4) return [1, 2, 3, 4, 5, -1, t]
if (cur >= t - 3) return [1, -1, t - 4, t - 3, t - 2, t - 1, t]
return [1, -1, cur - 1, cur, cur + 1, -1, t]
})
const handlePageChange = (p: number) => { currentPage.value = p }
const handlePageSizeChange = (e: any) => {
const idx = Number(e.detail.value)
pageSize.value = pageSizeOptions[idx] ?? pageSizeOptions[0]
currentPage.value = 1
}
const handleJumpPage = () => {
const p = parseInt(jumpPageInput.value)
if (!isNaN(p) && p >= 1 && p <= totalPage.value) currentPage.value = p
}
// ========== END PAGINATION STATE ==========
const showModal = ref(false) const showModal = ref(false)
const isEdit = ref(false) const isEdit = ref(false)

View File

@@ -55,7 +55,7 @@
<view class="th col-op"><text>操作</text></view> <view class="th col-op"><text>操作</text></view>
</view> </view>
<view v-for="item in replyList" :key="item.id" class="tr-row"> <view v-for="item in pagedList" :key="item.id" class="tr-row">
<view class="td col-check"><text>□</text></view> <view class="td col-check"><text>□</text></view>
<view class="td col-id"><text>{{ item.id }}</text></view> <view class="td col-id"><text>{{ item.id }}</text></view>
<view class="td col-product"> <view class="td col-product">
@@ -84,8 +84,8 @@
<!-- 分页 --> <!-- 分页 -->
<CommonPagination <CommonPagination
v-if="replyList.length > 0" v-if="true"
:total="replyList.length" :total="total"
:loading="false" :loading="false"
:currentPage="currentPage" :currentPage="currentPage"
:pageSize="pageSize" :pageSize="pageSize"
@@ -146,24 +146,25 @@ const replyList = ref([
} }
]) ])
// 分页适配状态 // ========== PAGINATION STATE ==========
const currentPage = ref(1) const currentPage = ref(1)
const pageSize = ref(10) const pageSize = ref(10)
let jumpPageInput = '' const jumpPageInput = ref('')
const pageSizeOptions = [10, 20, 30, 50] const pageSizeOptions = [10, 20, 30, 50]
const pageSizeOptionLabels = computed(() => pageSizeOptions.map((n: number) => `${n}条/页`)) const pageSizeOptionLabels = computed(() => pageSizeOptions.map((n: number) => `${n}条/页`))
const pageSizeIndex = computed(() => { const pageSizeIndex = computed(() => { const idx = pageSizeOptions.indexOf(pageSize.value); return idx >= 0 ? idx : 0 })
const idx = pageSizeOptions.indexOf(pageSize.value) const total = computed(() => replyList.value.length)
return idx >= 0 ? idx : 0 const totalPage = computed(() => Math.max(1, Math.ceil(total.value / pageSize.value)))
const pagedList = computed(() => {
const start = (currentPage.value - 1) * pageSize.value
return replyList.value.slice(start, start + pageSize.value)
}) })
const totalPage = computed(() => Math.max(1, Math.ceil(replyList.value.length / pageSize.value))) const visiblePages = computed((): number[] => {
const visiblePages = computed(() => { const t = totalPage.value; const cur = currentPage.value
const total = totalPage.value if (t <= 7) return Array.from({ length: t }, (_: any, i: number) => i + 1)
const cur = currentPage.value if (cur <= 4) return [1, 2, 3, 4, 5, -1, t]
if (total <= 7) return Array.from({ length: total }, (_: any, i: number) => i + 1) if (cur >= t - 3) return [1, -1, t - 4, t - 3, t - 2, t - 1, t]
if (cur <= 4) return [1, 2, 3, 4, 5, -1, total] return [1, -1, cur - 1, cur, cur + 1, -1, t]
if (cur >= total - 3) return [1, -1, total - 4, total - 3, total - 2, total - 1, total]
return [1, -1, cur - 1, cur, cur + 1, -1, total]
}) })
const handlePageChange = (p: number) => { currentPage.value = p } const handlePageChange = (p: number) => { currentPage.value = p }
const handlePageSizeChange = (e: any) => { const handlePageSizeChange = (e: any) => {
@@ -172,9 +173,10 @@ const handlePageSizeChange = (e: any) => {
currentPage.value = 1 currentPage.value = 1
} }
const handleJumpPage = () => { const handleJumpPage = () => {
const p = parseInt(jumpPageInput) const p = parseInt(jumpPageInput.value)
if (!isNaN(p) && p >= 1 && p <= totalPage.value) currentPage.value = p if (!isNaN(p) && p >= 1 && p <= totalPage.value) currentPage.value = p
} }
// ========== END PAGINATION STATE ==========
</script> </script>
<style scoped lang="scss"> <style scoped lang="scss">
@@ -274,7 +276,7 @@ const handleJumpPage = () => {
.col-reply { flex: 1; } .col-reply { flex: 1; }
.col-status { width: 100px; } .col-status { width: 100px; }
.col-time { width: 160px; } .col-time { width: 160px; }
.col-op { width: 180px; } .col-op { width: 180px; display: flex; flex-direction: row; }
.p-img { width: 40px; height: 40px; border-radius: 4px; } .p-img { width: 40px; height: 40px; border-radius: 4px; }
.p-name-txt { font-size: 13px; line-height: 1.4; color: #1890ff; } .p-name-txt { font-size: 13px; line-height: 1.4; color: #1890ff; }

View File

@@ -33,7 +33,7 @@
<view v-if="list.length === 0" class="empty-box"> <view v-if="list.length === 0" class="empty-box">
<text class="empty-text">暂无数据</text> <text class="empty-text">暂无数据</text>
</view> </view>
<view v-for="(item, index) in list" :key="index" class="table-row"> <view v-for="(item, index) in pagedList" :key="index" class="table-row">
<view class="td-cell flex-1 row-center"> <view class="td-cell flex-1 row-center">
<view class="checkbox-mock" :class="item.selected ? 'checked' : ''" @click="item.selected = !item.selected"> <view class="checkbox-mock" :class="item.selected ? 'checked' : ''" @click="item.selected = !item.selected">
<text v-if="item.selected" class="check-mark">✓</text> <text v-if="item.selected" class="check-mark">✓</text>
@@ -50,6 +50,22 @@
</view> </view>
</view> </view>
</view> </view>
<CommonPagination
v-if="true"
:total="total"
:loading="false"
:currentPage="currentPage"
:pageSize="pageSize"
:pageSizeOptionLabels="pageSizeOptionLabels"
:pageSizeIndex="pageSizeIndex"
:visiblePages="visiblePages"
:totalPage="totalPage"
:jumpPageInput="jumpPageInput"
@page-size-change="handlePageSizeChange"
@page-change="handlePageChange"
@update:jumpPageInput="(val: string) => { jumpPageInput.value = val }"
@jump-page="handleJumpPage"
/>
</view> </view>
<!-- 添加规格弹窗 --> <!-- 添加规格弹窗 -->
@@ -91,7 +107,8 @@
</template> </template>
<script setup lang="uts"> <script setup lang="uts">
import { ref, reactive } from 'vue' import { ref, reactive, computed } from 'vue'
import CommonPagination from '@/components/CommonPagination/CommonPagination.uvue'
interface AttrItem { interface AttrItem {
id: number; id: number;
@@ -101,13 +118,58 @@ interface AttrItem {
selected: boolean; selected: boolean;
} }
// ========== MOCK DATA START ==========
// TODO: 接真实接口时替换此处 list 为 fetchSpecList() 调用
const list = reactive<AttrItem[]>([ const list = reactive<AttrItem[]>([
{ id: 104, name: '颜色', specs: '红色,蓝色,黑色,白色', attrs: '颜色属性', selected: false }, { id: 104, name: '颜色', specs: '红色,蓝色,黑色,白色', attrs: '颜色属性', selected: false },
{ id: 105, name: '尺寸', specs: 'S,M,L,XL,XXL', attrs: '服装尺寸', selected: false }, { id: 105, name: '尺寸', specs: 'S,M,L,XL,XXL', attrs: '服装尺寸', selected: false },
{ id: 106, name: '材质', specs: '纯棉,纶,真丝', attrs: '面料材质', selected: false }, { id: 106, name: '材质', specs: '纯棉,纶,真丝', attrs: '面料材质', selected: false },
{ id: 107, name: '内存', specs: '8G,16G,32G', attrs: '硬件参数', selected: false }, { id: 107, name: '内存', specs: '8G,16G,32G', attrs: '硬件参数', selected: false },
{ id: 108, name: '存储', specs: '128G,256G,512G', attrs: '容量', selected: false } { id: 108, name: '存储', specs: '128G,256G,512G', attrs: '容量', selected: false },
{ id: 109, name: '重量', specs: '100g,200g,500g,1kg', attrs: '包装规格', selected: false },
{ id: 110, name: '口味', specs: '原味,辣味,甜味,咋味', attrs: '食品口味', selected: false },
{ id: 111, name: '风格', specs: '日系,韩系,欧美,新中式', attrs: '服装风格', selected: false },
{ id: 112, name: '屏幕尺寸', specs: '6.1小时,6.7小时,6.9小时', attrs: '手机屏幕', selected: false },
{ id: 113, name: '套餐选择', specs: '套餐A,套餐B,套餐C', attrs: '餐飲套餐', selected: false },
{ id: 114, name: '独立包装', specs: '独立包装,组合装', attrs: '包装方式', selected: false },
{ id: 115, name: '靘刀', specs: '靘刀7天,靘刀14天,靘刀30天', attrs: '服装保洁', selected: false },
{ id: 116, name: '宣传图', specs: '带宣传图,不带宣传图', attrs: '商品配送', selected: false },
{ id: 117, name: '主题色', specs: '科技蓝,记忆红,森林绿,太空黑', attrs: '设备配色', selected: false },
{ id: 118, name: '等级', specs: '普通版,标准版,旗舰版', attrs: '商品等级', selected: false }
]) ])
// ========== MOCK DATA END ==========
// ========== PAGINATION STATE ==========
const currentPage = ref(1)
const pageSize = ref(10)
const jumpPageInput = ref('')
const pageSizeOptions = [10, 15, 20, 30, 50]
const pageSizeOptionLabels = computed(() => pageSizeOptions.map((n: number) => `${n}条/页`))
const pageSizeIndex = computed(() => { const idx = pageSizeOptions.indexOf(pageSize.value); return idx >= 0 ? idx : 0 })
const total = computed(() => list.length)
const totalPage = computed(() => Math.max(1, Math.ceil(total.value / pageSize.value)))
const pagedList = computed(() => {
const start = (currentPage.value - 1) * pageSize.value
return list.slice(start, start + pageSize.value)
})
const visiblePages = computed((): number[] => {
const t = totalPage.value; const cur = currentPage.value
if (t <= 7) return Array.from({ length: t }, (_: any, i: number) => i + 1)
if (cur <= 4) return [1, 2, 3, 4, 5, -1, t]
if (cur >= t - 3) return [1, -1, t - 4, t - 3, t - 2, t - 1, t]
return [1, -1, cur - 1, cur, cur + 1, -1, t]
})
const handlePageChange = (p: number) => { currentPage.value = p }
const handlePageSizeChange = (e: any) => {
const idx = Number(e.detail.value)
pageSize.value = pageSizeOptions[idx] ?? pageSizeOptions[0]
currentPage.value = 1
}
const handleJumpPage = () => {
const p = parseInt(jumpPageInput.value)
if (!isNaN(p) && p >= 1 && p <= totalPage.value) currentPage.value = p
}
// ========== END PAGINATION STATE ==========
const showModal = ref(false) const showModal = ref(false)
const form = reactive({ const form = reactive({

View File

@@ -88,7 +88,7 @@
<!-- 表格内容 --> <!-- 表格内容 -->
<view class="table-body"> <view class="table-body">
<view v-for="user in userList" :key="user.id" class="table-row" <view v-for="user in pagedList" :key="user.id" class="table-row"
:style="{ zIndex: activeDropdownId === user.id ? 1000 : 1 }" :style="{ zIndex: activeDropdownId === user.id ? 1000 : 1 }"
> >
<view class="col col-check"><checkbox :checked="user.checked" /></view> <view class="col col-check"><checkbox :checked="user.checked" /></view>
@@ -140,7 +140,7 @@
<!-- 分页 --> <!-- 分页 -->
<CommonPagination <CommonPagination
v-if="total > 0" v-if="true"
:total="total" :total="total"
:loading="false" :loading="false"
:currentPage="currentPage" :currentPage="currentPage"
@@ -152,7 +152,7 @@
:jumpPageInput="jumpPageInput" :jumpPageInput="jumpPageInput"
@page-size-change="handlePageSizeChange" @page-size-change="handlePageSizeChange"
@page-change="handlePageChange" @page-change="handlePageChange"
@update:jumpPageInput="(val : string) => { jumpPageInput = val }" @update:jumpPageInput="(val: string) => { jumpPageInput.value = val }"
@jump-page="handleJumpPage" @jump-page="handleJumpPage"
/> />
</view> </view>
@@ -165,6 +165,7 @@ import { ref, computed } from 'vue'
import CommonPagination from '@/components/CommonPagination/CommonPagination.uvue' import CommonPagination from '@/components/CommonPagination/CommonPagination.uvue'
const activeTab = ref(0) const activeTab = ref(0)
// ========== MOCK DATA START ==========
const tabs = ['全部', '微信公众号', '微信小程序', 'H5', 'PC', 'APP'] const tabs = ['全部', '微信公众号', '微信小程序', 'H5', 'PC', 'APP']
const isAllChecked = ref(false) const isAllChecked = ref(false)
const activeDropdownId = ref<string | null>(null) const activeDropdownId = ref<string | null>(null)
@@ -178,8 +179,51 @@ const userList = ref([
{ id: '75289', avatar: '/static/logo.png', nickname: '小二上酒', isMember: '否', level: '无', group: 'A类客户', spreadLevel: '', phone: '', userType: '公众号', balance: '100000.00', checked: false }, { id: '75289', avatar: '/static/logo.png', nickname: '小二上酒', isMember: '否', level: '无', group: 'A类客户', spreadLevel: '', phone: '', userType: '公众号', balance: '100000.00', checked: false },
{ id: '75257', avatar: '/static/logo.png', nickname: '5+7', isMember: '是', level: '无', group: 'A类客户', spreadLevel: '', phone: '', userType: '公众号', balance: '100000.00', checked: false }, { id: '75257', avatar: '/static/logo.png', nickname: '5+7', isMember: '是', level: '无', group: 'A类客户', spreadLevel: '', phone: '', userType: '公众号', balance: '100000.00', checked: false },
{ id: '75226', avatar: '/static/logo.png', nickname: '慢步前行', isMember: '是', level: '无', group: 'A类客户', spreadLevel: '', phone: '', userType: '公众号', balance: '100000.00', checked: false }, { id: '75226', avatar: '/static/logo.png', nickname: '慢步前行', isMember: '是', level: '无', group: 'A类客户', spreadLevel: '', phone: '', userType: '公众号', balance: '100000.00', checked: false },
{ id: '75211', avatar: '/static/logo.png', nickname: '难得糊涂', isMember: '否', level: '无', group: 'A类客户', spreadLevel: '', phone: '', userType: '公众号', balance: '100000.00', checked: false } { id: '75211', avatar: '/static/logo.png', nickname: '难得糊涂', isMember: '否', level: '无', group: 'A类客户', spreadLevel: '', phone: '', userType: '公众号', balance: '100000.00', checked: false },
{ id: '75100', avatar: '/static/logo.png', nickname: '山河远阔', isMember: '否', level: '无', group: '无', spreadLevel: '', phone: '138****5566', userType: '小程序', balance: '9800.00', checked: false },
{ id: '74988', avatar: '/static/logo.png', nickname: '野草菓芙', isMember: '是', level: '金等', group: 'B类客户', spreadLevel: '一级', phone: '155****7788', userType: '公众号', balance: '25600.00', checked: false },
{ id: '74833', avatar: '/static/logo.png', nickname: '星花雨月', isMember: '否', level: '无', group: '无', spreadLevel: '', phone: '', userType: 'H5', balance: '320.00', checked: false },
{ id: '74701', avatar: '/static/logo.png', nickname: '天道酬勤', isMember: '是', level: '银等', group: 'A类客户', spreadLevel: '二级', phone: '186****3344', userType: '小程序', balance: '7700.00', checked: false },
{ id: '74590', avatar: '/static/logo.png', nickname: '南风知劲草', isMember: '否', level: '无', group: '无', spreadLevel: '', phone: '177****1122', userType: 'APP', balance: '150.00', checked: false },
{ id: '74422', avatar: '/static/logo.png', nickname: '大漠孤烟', isMember: '是', level: '金等', group: 'A类客户', spreadLevel: '一级', phone: '139****9900', userType: '公众号', balance: '88800.00', checked: false },
{ id: '74310', avatar: '/static/logo.png', nickname: '小桥流水', isMember: '否', level: '无', group: 'B类客户', spreadLevel: '', phone: '', userType: 'PC', balance: '600.00', checked: false },
{ id: '74198', avatar: '/static/logo.png', nickname: '日暗香残', isMember: '是', level: '银等', group: '无', spreadLevel: '一级', phone: '135****6677', userType: '小程序', balance: '4300.00', checked: false },
{ id: '74056', avatar: '/static/logo.png', nickname: '梦里花开', isMember: '否', level: '无', group: '无', spreadLevel: '', phone: '', userType: '公众号', balance: '200.00', checked: false },
{ id: '73900', avatar: '/static/logo.png', nickname: '春风十里', isMember: '是', level: '金等', group: 'A类客户', spreadLevel: '二级', phone: '151****4455', userType: 'APP', balance: '56000.00', checked: false }
]) ])
// ========== MOCK DATA END ==========
// ========== PAGINATION STATE ==========
const currentPage = ref(1)
const pageSize = ref(15)
const jumpPageInput = ref('')
const pageSizeOptions = [10, 15, 20, 30, 50]
const pageSizeOptionLabels = computed(() => pageSizeOptions.map((n: number) => `${n}条/页`))
const pageSizeIndex = computed(() => { const idx = pageSizeOptions.indexOf(pageSize.value); return idx >= 0 ? idx : 0 })
const total = computed(() => userList.value.length)
const totalPage = computed(() => Math.max(1, Math.ceil(total.value / pageSize.value)))
const pagedList = computed(() => {
const start = (currentPage.value - 1) * pageSize.value
return userList.value.slice(start, start + pageSize.value)
})
const visiblePages = computed((): number[] => {
const t = totalPage.value; const cur = currentPage.value
if (t <= 7) return Array.from({ length: t }, (_: any, i: number) => i + 1)
if (cur <= 4) return [1, 2, 3, 4, 5, -1, t]
if (cur >= t - 3) return [1, -1, t - 4, t - 3, t - 2, t - 1, t]
return [1, -1, cur - 1, cur, cur + 1, -1, t]
})
const handlePageChange = (p: number) => { currentPage.value = p }
const handlePageSizeChange = (e: any) => {
const idx = Number(e.detail.value)
pageSize.value = pageSizeOptions[idx] ?? pageSizeOptions[0]
currentPage.value = 1
}
const handleJumpPage = () => {
const p = parseInt(jumpPageInput.value)
if (!isNaN(p) && p >= 1 && p <= totalPage.value) currentPage.value = p
}
// ========== END PAGINATION STATE ==========
function onSearch() { function onSearch() {
uni.showToast({ title: '搜索中...', icon: 'none' }) uni.showToast({ title: '搜索中...', icon: 'none' })

View File

@@ -1,5 +1,5 @@
withdrawal.uvue:1 Failed to load resource: the server responded with a status of 500 (Internal Server Error) adminComponentMap.uts:36 GET http://localhost:5173/pages/mall/admin/product/parameters/index.uvue?t=1773717301644&import net::ERR_ABORTED 500 (Internal Server Error)
uni-h5.es.js:19975 [Vue warn]: Unhandled error during execution of async component loader main.uts:16 [Vue warn]: Unhandled error during execution of async component loader
at <AsyncComponentWrapper> at <AsyncComponentWrapper>
at <PageBody> at <PageBody>
at <Page> at <Page>
@@ -17,32 +17,84 @@ callWithErrorHandling @ vue.runtime.esm.js:1381
handleError @ vue.runtime.esm.js:1421 handleError @ vue.runtime.esm.js:1421
onError @ vue.runtime.esm.js:3724 onError @ vue.runtime.esm.js:3724
(anonymous) @ vue.runtime.esm.js:3767 (anonymous) @ vue.runtime.esm.js:3767
vue.runtime.esm.js:1443 TypeError: Failed to fetch dynamically imported module: http://localhost:5173/pages/mall/admin/homePage/index.uvue?t=1773707553790&import Promise.catch
logError @ vue.runtime.esm.js:1443 setup @ vue.runtime.esm.js:3766
errorHandler @ uni-h5.es.js:19600
callWithErrorHandling @ vue.runtime.esm.js:1381 callWithErrorHandling @ vue.runtime.esm.js:1381
handleError @ vue.runtime.esm.js:1421 setupStatefulComponent @ vue.runtime.esm.js:8985
onError @ vue.runtime.esm.js:3724 setupComponent @ vue.runtime.esm.js:8946
(anonymous) @ vue.runtime.esm.js:3767 mountComponent @ vue.runtime.esm.js:7262
ak-req.uts:164 GET http://119.146.131.237:9126/auth/v1/user 403 (Forbidden) processComponent @ vue.runtime.esm.js:7228
(anonymous) @ uni-h5.es.js:23666 patch @ vue.runtime.esm.js:6694
(anonymous) @ uni-h5.es.js:4125 mountChildren @ vue.runtime.esm.js:6942
invokeApi @ uni-h5.es.js:3971 processFragment @ vue.runtime.esm.js:7158
(anonymous) @ uni-h5.es.js:3989 patch @ vue.runtime.esm.js:6668
(anonymous) @ ak-req.uts:164 mountChildren @ vue.runtime.esm.js:6942
doOnce @ ak-req.uts:163 processFragment @ vue.runtime.esm.js:7158
(anonymous) @ ak-req.uts:233 patch @ vue.runtime.esm.js:6668
fulfilled @ tslib.es6.js:73 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
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
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
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
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
callWithErrorHandling @ vue.runtime.esm.js:1381
flushJobs @ vue.runtime.esm.js:1585
Promise.then Promise.then
step @ tslib.es6.js:75 queueFlush @ vue.runtime.esm.js:1494
(anonymous) @ tslib.es6.js:76 queueJob @ vue.runtime.esm.js:1488
__awaiter @ tslib.es6.js:72 scheduler @ vue.runtime.esm.js:3179
request @ ak-req.uts:127 resetScheduling @ vue.runtime.esm.js:236
(anonymous) @ aksupa.uts:733 triggerEffects @ vue.runtime.esm.js:280
(anonymous) @ tslib.es6.js:76 triggerRefValue @ vue.runtime.esm.js:1033
__awaiter @ tslib.es6.js:72 set value @ vue.runtime.esm.js:1078
hydrateSessionFromStorage @ aksupa.uts:729 finalizeNavigation @ vue-router.mjs?v=ed041164:2474
AkSupa @ aksupa.uts:655 (anonymous) @ vue-router.mjs?v=ed041164:2384
createClient @ aksupa.uts:1254 Promise.then
(anonymous) @ aksupainstance.uts:7 pushWithRedirect @ vue-router.mjs?v=ed041164:2352
ak-req.uts:164 GET http://119.146.131.237:9126/auth/v1/user 403 (Forbidden) 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?t=1773717331353&import