fix: 首页和我的应用排序按 seq 排列

This commit is contained in:
2026-07-29 20:27:36 +08:00
parent 8c9613044c
commit ec6a5dd956
35 changed files with 18361 additions and 17013 deletions

View File

@@ -73,14 +73,14 @@ export default {
</view>
</view>
<!-- 余额、优惠券 -->
<view class="acea-row balance-coupon">
<!-- <view class="acea-row balance-coupon">
<view class="item" v-for="(item, index) in property" @tap="goMenuPage(item.url)" :key="index">
<view class="value num-fa-semi">{{ item.value || 0 }}</view>
<view>{{ item.label }}</view>
</view>
</view>
</view> -->
<!-- 会员中心、积分商城 -->
<view class="acea-row member-points">
<!-- <view class="acea-row member-points">
<view class="acea-row row-middle row-center item" @tap="intoPage(userInfo.level_status == 1 ? '/pages/users/user_vip/index' : '/pages/annex/vip_grade_active/index')">
<view>
<view>会员中心</view>
@@ -101,7 +101,7 @@ export default {
</view>
<image src="@/static/images/user-points.png" class="image"></image>
</view>
</view>
</view> -->
</view>
</template>

View File

@@ -0,0 +1,435 @@
<template>
<view class="my-apps-wrap">
<!-- 展示模式 -->
<template v-if="!editing">
<!-- 顶部:标题 + 编辑按钮 -->
<view class="pt-34 pr-30 pb-20 pl-32 bg--w111-fff rd-t-16rpx mt-20 service ml-20 mr-20">
<view class="flex-row-between">
<view class="fs-30 fw-500 lh-42rpx text--w111-333">我的应用</view>
<view class="edit-btn" @click="enterEdit">
<text class="fs-24 text--w111-1890ff">编辑首页应用</text>
</view>
</view>
</view>
<!-- 应用列表 -->
<view class="pt-10 pr-30 pb-32 pl-32 bg--w111-fff rd-b-16rpx service ml-20 mr-20" v-if="allApps.length">
<view class="grid-column-4 grid-gap-x-76rpx grid-gap-y-54rpx">
<view v-for="(item, index) in allApps" :key="index">
<view class="flex-col flex-center" @click="onAppTap(item)">
<image :src="item.logo" class="app-icon rd-16rpx" v-if="item.logo"></image>
<view class="empty-box" v-else></view>
<text class="fs-26 lh-36rpx text--w111-282828 pt-22">{{ item.name }}</text>
</view>
</view>
</view>
</view>
</template>
<!-- 编辑模式 -->
<template v-else>
<!-- 编辑卡片:整合头栏 + 首页展示栏 + 全部应用 -->
<view class="bg--w111-fff rd-16rpx mt-20 ml-20 mr-20">
<!-- 顶部操作栏 -->
<view class="edit-header">
<view class="edit-header-inner">
<view class="header-btn btn-cancel" @click="cancelEdit">取消</view>
<view class="fs-30 fw-500 text--w111-333">编辑首页展示</view>
<view class="header-btn btn-save" @click="saveEdit">保存</view>
</view>
</view>
<!-- 首页展示栏(横向滚动) -->
<view class="home-bar-wrap" v-if="editSelectedIds.length">
<view class="home-bar-label fs-28 text--w111-333">首页展示栏</view>
<scroll-view class="home-bar-scroll" scroll-x enhanced show-scrollbar="false">
<view class="home-bar-inner">
<view
class="home-bar-item"
v-for="(item, index) in editSelectedList"
:key="item.id"
>
<view class="home-bar-icon-wrap">
<image :src="item.logo" class="home-bar-icon rd-16rpx" v-if="item.logo"></image>
<view class="empty-box-sm" v-else></view>
<!-- 删除标记 -->
<view class="badge badge-remove" @click="removeFromHome(item.id)">✕</view>
</view>
<text class="home-bar-name">{{ item.name }}</text>
</view>
</view>
</scroll-view>
</view>
<!-- 全部应用列表 -->
<view class="all-apps-list">
<view class="fs-28 text--w111-333 mb-20">全部应用</view>
<view class="grid-column-4 grid-gap-x-76rpx grid-gap-y-54rpx">
<view v-for="(item, index) in allApps" :key="index">
<view
class="flex-col flex-center pos-relative"
@click="toggleAppSelect(item)"
>
<view class="app-icon-wrap">
<image :src="item.logo" class="app-icon rd-16rpx" v-if="item.logo"></image>
<view class="empty-box" v-else></view>
<!-- 已选遮罩 -->
<view class="selected-overlay" v-if="isSelected(item.id)">
<text class="selected-check">✓</text>
</view>
</view>
<!-- 添加标记(未选中时) -->
<view
class="badge badge-add"
v-if="!isSelected(item.id) && editSelectedIds.length < 6"
@click.stop="addToHome(item)"
>+</view>
<text
class="fs-26 lh-36rpx pt-22"
:class="isSelected(item.id) ? 'text--w111-ccc' : 'text--w111-282828'"
>{{ item.name }}</text>
</view>
</view>
</view>
</view>
</view>
</template>
</view>
</template>
<script>
const STORAGE_KEY = 'MY_HOME_APPS'
const MAX_HOME_APPS = 6
export default {
name: 'userMyApps',
data() {
return {
allApps: [],
// Storage 数据
homeApps: {
showOnHome: true,
appIds: []
},
editing: false,
// 编辑中的临时副本
editAppIds: []
}
},
computed: {
editSelectedIds() {
return this.editAppIds
},
// 编辑模式中已选应用的完整对象列表(按 allApps 顺序排列)
editSelectedList() {
const idSet = new Set(this.editAppIds)
const filtered = this.allApps.filter(item => idSet.has(item.id))
const indexMap = new Map(this.allApps.map((item, i) => [item.id, i]))
filtered.sort((a, b) => (indexMap.get(a.id) ?? 999) - (indexMap.get(b.id) ?? 999))
return filtered
}
},
mounted() {
this.loadData()
this.fetchApps()
},
// 从首页"更多"跳转过来时刷新数据
onShow() {
this.loadData()
},
methods: {
// ---------- 数据层 ----------
loadData() {
try {
const raw = uni.getStorageSync(STORAGE_KEY)
if (raw) {
this.homeApps = raw
}
} catch (e) {
console.error('[myApps] loadData error:', e)
}
},
saveData() {
try {
uni.setStorageSync(STORAGE_KEY, this.homeApps)
} catch (e) {
console.error('[myApps] saveData error:', e)
}
},
fetchApps() {
uni.request({
url: 'https://mp.yizhezhe.com/api/mp/apps',
data: {
isPublished: 1,
pageSize: 50
},
success: (res) => {
if (res.data?.code === 200 && res.data?.result?.items) {
this.allApps = res.data.result.items
} else if (res.data?.status === 200 && res.data?.data) {
this.allApps = res.data.data
} else {
return
}
this.autoFillHomeApps()
},
fail: () => {
this.allApps = []
}
})
},
// 自动填充首页展示应用(与 appCenter 逻辑一致)
autoFillHomeApps() {
const ids = this.homeApps.appIds || []
const currentCount = ids.length
if (currentCount >= 6) return
const idSet = new Set(ids)
const recommended = this.allApps
.filter(item => item.isRecommended && !idSet.has(item.id))
if (!recommended.length) return
const slots = 6 - currentCount
const toAdd = recommended.slice(0, slots).map(item => item.id)
this.homeApps.appIds = [...ids, ...toAdd]
this.saveData()
},
// ---------- 展示模式 ----------
onAppTap(item) {
if (!item.url) return
this.$util.JumpPath(item.url)
},
// ---------- 编辑模式 ----------
enterEdit() {
// 复制当前 appIds 作为编辑副本
this.editAppIds = [...(this.homeApps.appIds || [])]
this.editing = true
},
cancelEdit() {
this.editing = false
this.editAppIds = []
},
saveEdit() {
this.homeApps.appIds = [...this.editAppIds]
this.saveData()
this.editing = false
},
// 判断应用是否已选
isSelected(id) {
return this.editAppIds.includes(id)
},
// 添加到首页栏(从全部应用点击+)
addToHome(item) {
if (this.editAppIds.length >= MAX_HOME_APPS) {
uni.showToast({ title: `最多选择${MAX_HOME_APPS}个应用`, icon: 'none' })
return
}
if (!this.editAppIds.includes(item.id)) {
this.editAppIds.push(item.id)
}
},
// 从首页栏移除(点击✕)
removeFromHome(id) {
this.editAppIds = this.editAppIds.filter(i => i !== id)
},
// 点击应用图标切换选中状态
toggleAppSelect(item) {
if (this.isSelected(item.id)) {
this.removeFromHome(item.id)
} else {
this.addToHome(item)
}
}
}
}
</script>
<style scoped lang="scss">
.my-apps-wrap {
// 左右布局
.flex-row-between {
display: flex;
justify-content: space-between;
align-items: center;
}
// 应用图标(展示模式 + 编辑模式通用)
.app-icon {
width: 96rpx;
height: 96rpx;
}
.empty-box {
width: 96rpx;
height: 96rpx;
background-color: #f3f9ff;
border-radius: 8rpx;
}
.app-icon-wrap {
position: relative;
width: 96rpx;
height: 96rpx;
}
// 编辑按钮
.edit-btn {
display: inline-flex;
align-items: center;
padding: 6rpx 20rpx;
border: 2rpx solid #1890ff;
border-radius: 10rpx;
flex-shrink: 0;
}
// ---------- 编辑模式 ----------
.edit-header {
padding: 24rpx 30rpx 0;
.edit-header-inner {
display: flex;
align-items: center;
justify-content: space-between;
}
.header-btn {
display: inline-flex;
align-items: center;
justify-content: center;
height: 56rpx;
padding: 0 28rpx;
border-radius: 28rpx;
font-size: 26rpx;
font-weight: 500;
flex-shrink: 0;
}
.btn-cancel {
color: #666;
background: #f5f5f5;
}
.btn-save {
color: #fff;
background: linear-gradient(135deg, #1890ff, #096dd9);
box-shadow: 0 4rpx 12rpx rgba(24, 144, 255, 0.35);
}
}
// 首页展示栏
.home-bar-wrap {
padding: 24rpx 30rpx 0;
margin-top: 20rpx;
.home-bar-label {
margin-bottom: 16rpx;
}
.home-bar-scroll {
white-space: nowrap;
overflow: visible !important;
.home-bar-inner {
display: flex;
gap: 24rpx;
.home-bar-item {
display: flex;
flex-direction: column;
align-items: center;
flex-shrink: 0;
width: 100rpx;
padding-top: 12rpx;
.home-bar-icon-wrap {
position: relative;
width: 90rpx;
height: 90rpx;
overflow: visible;
.home-bar-icon {
width: 100%;
height: 100%;
border-radius: 16rpx;
}
.empty-box-sm {
width: 100%;
height: 100%;
background-color: #f3f9ff;
border-radius: 16rpx;
}
}
.home-bar-name {
font-size: 22rpx;
color: #333;
margin-top: 8rpx;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
max-width: 100rpx;
}
}
}
}
}
// 全部应用列表
.all-apps-list {
padding: 30rpx 30rpx 34rpx;
}
// ---------- 通用徽标 ----------
.badge {
position: absolute;
width: 36rpx;
height: 36rpx;
border-radius: 50%;
font-size: 20rpx;
display: flex;
align-items: center;
justify-content: center;
color: #fff;
z-index: 2;
}
.badge-remove {
top: -8rpx;
right: -8rpx;
background: #ff4d4f;
}
.badge-add {
top: -8rpx;
right: 6rpx;
background: #52c41a;
}
.selected-overlay {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: rgba(0, 0, 0, 0.35);
border-radius: 8rpx;
display: flex;
align-items: center;
justify-content: center;
z-index: 1;
.selected-check {
color: #fff;
font-size: 28rpx;
font-weight: bold;
}
}
.pos-relative {
position: relative;
}
}
</style>