276 lines
6.6 KiB
Vue
276 lines
6.6 KiB
Vue
<template>
|
||
<view v-if="showApps" :style="[boxStyle]">
|
||
<view :style="[boxContentStyle]">
|
||
<view class="list-menu" :style="[gridColumns]">
|
||
<view
|
||
class="item flex-col flex-center"
|
||
v-for="(item, index) in displayList"
|
||
:key="index"
|
||
@click="onAppTap(item)"
|
||
>
|
||
<!-- 更多按钮 -->
|
||
<template v-if="item._isMore">
|
||
<view class="img-box skeleton-radius" v-if="menuStyleConfig !== 2">
|
||
<view class="more-icon-inner flex-col flex-center">
|
||
<view class="more-dots">
|
||
<view class="more-dot"></view>
|
||
<view class="more-dot"></view>
|
||
<view class="more-dot"></view>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
<text
|
||
class="fs-24"
|
||
:class="{ 'mt-14': menuStyleConfig == 0 }"
|
||
:style="[nameColorStyle]"
|
||
v-if="menuStyleConfig !== 1"
|
||
>更多</text>
|
||
</template>
|
||
<!-- 普通应用 -->
|
||
<template v-else>
|
||
<view class="img-box skeleton-radius" v-if="menuStyleConfig !== 2">
|
||
<image
|
||
:src="item.logo"
|
||
mode="aspectFill"
|
||
:style="{ borderRadius: (imgStyle * 2) + 'rpx' }"
|
||
v-if="item.logo"
|
||
></image>
|
||
<view class="empty-box" v-else></view>
|
||
</view>
|
||
<text
|
||
class="fs-24"
|
||
:class="{ 'mt-14': menuStyleConfig == 0 }"
|
||
:style="[nameColorStyle]"
|
||
v-if="menuStyleConfig !== 1"
|
||
>{{ item.name }}</text>
|
||
</template>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
</template>
|
||
|
||
<script>
|
||
const STORAGE_KEY = 'MY_HOME_APPS'
|
||
const MAX_HOME_APPS = 6
|
||
|
||
export default {
|
||
name: 'appCenter',
|
||
props: {
|
||
dataConfig: {
|
||
type: Object,
|
||
default: () => ({})
|
||
}
|
||
},
|
||
computed: {
|
||
menuStyleConfig() {
|
||
return this.dataConfig.menuStyleConfig?.tabVal ?? 0
|
||
},
|
||
number() {
|
||
return this.dataConfig.number?.tabVal ?? 0
|
||
},
|
||
boxStyle() {
|
||
return {
|
||
padding: `${(this.dataConfig.topConfig?.val ?? 0) * 2}rpx ${(this.dataConfig.prConfig?.val ?? 0) * 2}rpx ${(this.dataConfig.bottomConfig?.val ?? 0) * 2}rpx`,
|
||
marginTop: `${(this.dataConfig.mbConfig?.val ?? 0) * 2}rpx`,
|
||
background: this.dataConfig.bottomBgColor?.color?.[0]?.item ?? 'transparent'
|
||
}
|
||
},
|
||
boxContentStyle() {
|
||
let borderRadius = `${(this.dataConfig.fillet?.val ?? 0) * 2}rpx`
|
||
if (this.dataConfig.fillet?.type) {
|
||
const vl = this.dataConfig.fillet.valList || []
|
||
borderRadius = `${(vl[0]?.val ?? 0) * 2}rpx ${(vl[1]?.val ?? 0) * 2}rpx ${(vl[3]?.val ?? 0) * 2}rpx ${(vl[2]?.val ?? 0) * 2}rpx`
|
||
}
|
||
return {
|
||
borderRadius,
|
||
background: `linear-gradient(90deg, ${this.dataConfig.bgColor?.color?.[0]?.item ?? '#fff'} 0%, ${this.dataConfig.bgColor?.color?.[1]?.item ?? '#fff'} 100%)`
|
||
}
|
||
},
|
||
imgStyle() {
|
||
return this.dataConfig.filletImg?.val ?? 0
|
||
},
|
||
gridColumns() {
|
||
// 应用中心用 4 列固定网格
|
||
return {
|
||
gridTemplateColumns: `repeat(4, 1fr)`
|
||
}
|
||
},
|
||
nameColorStyle() {
|
||
return {
|
||
color: this.dataConfig.nameColor?.color?.[0]?.item ?? '#333333'
|
||
}
|
||
},
|
||
// 是否在首页展示
|
||
showApps() {
|
||
return this.homeApps.showOnHome && this.displayList.length > 0
|
||
},
|
||
// 已选应用的 id 集合(快速查找)
|
||
selectedIds() {
|
||
return new Set(this.homeApps.appIds || [])
|
||
},
|
||
// 最终展示列表(过滤 + 按 seq 排序 + 截取 + 追加"更多")
|
||
displayList() {
|
||
const ids = this.homeApps.appIds || []
|
||
if (!ids.length) return []
|
||
|
||
const idSet = new Set(ids)
|
||
// 按 allApps API 返回顺序排列(即后台设置的 seq 顺序)
|
||
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))
|
||
const sliced = filtered.slice(0, MAX_HOME_APPS)
|
||
|
||
// 追加"更多"条目
|
||
sliced.push({
|
||
_isMore: true,
|
||
name: '更多'
|
||
})
|
||
|
||
return sliced
|
||
}
|
||
},
|
||
data() {
|
||
return {
|
||
allApps: [],
|
||
homeApps: {
|
||
showOnHome: true,
|
||
appIds: []
|
||
}
|
||
}
|
||
},
|
||
mounted() {
|
||
this.loadHomeApps()
|
||
this.fetchApps()
|
||
},
|
||
// 从个人中心编辑保存后跳回首页时,重新读取 storage
|
||
onShow() {
|
||
this.loadHomeApps()
|
||
},
|
||
methods: {
|
||
// 读取本地存储的首页应用配置
|
||
loadHomeApps() {
|
||
try {
|
||
const raw = uni.getStorageSync(STORAGE_KEY)
|
||
if (raw) {
|
||
this.homeApps = raw
|
||
}
|
||
} catch (e) {
|
||
console.error('[appCenter] loadHomeApps 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 = []
|
||
}
|
||
})
|
||
},
|
||
// 自动填充首页展示应用
|
||
autoFillHomeApps() {
|
||
const ids = this.homeApps.appIds || []
|
||
const currentCount = ids.length
|
||
if (currentCount >= MAX_HOME_APPS) return
|
||
|
||
const idSet = new Set(ids)
|
||
// 从推荐应用中筛选尚未选中的
|
||
const recommended = this.allApps
|
||
.filter(item => item.isRecommended && !idSet.has(item.id))
|
||
|
||
if (!recommended.length) return
|
||
|
||
const slots = MAX_HOME_APPS - currentCount
|
||
const toAdd = recommended.slice(0, slots).map(item => item.id)
|
||
|
||
this.homeApps.appIds = [...ids, ...toAdd]
|
||
try {
|
||
uni.setStorageSync(STORAGE_KEY, this.homeApps)
|
||
} catch (e) {
|
||
console.error('[appCenter] autoFill save error:', e)
|
||
}
|
||
},
|
||
onAppTap(item) {
|
||
if (item._isMore) {
|
||
// 点击"更多"跳转到我的页面
|
||
uni.switchTab({
|
||
url: '/pages/user/index'
|
||
})
|
||
return
|
||
}
|
||
if (!item.url) return
|
||
this.$util.JumpPath(item.url)
|
||
}
|
||
}
|
||
}
|
||
</script>
|
||
|
||
<style lang="scss" scoped>
|
||
.list-menu {
|
||
display: grid;
|
||
grid-template-rows: auto;
|
||
grid-row-gap: 24rpx;
|
||
width: 100%;
|
||
padding: 20rpx 0;
|
||
box-sizing: border-box;
|
||
|
||
.item {
|
||
text-align: center;
|
||
|
||
.img-box {
|
||
width: 90rpx;
|
||
height: 90rpx;
|
||
margin: 0 auto 6rpx;
|
||
|
||
image {
|
||
width: 100%;
|
||
height: 100%;
|
||
display: block;
|
||
}
|
||
|
||
.empty-box {
|
||
width: 100%;
|
||
height: 100%;
|
||
background-color: #f3f9ff;
|
||
}
|
||
}
|
||
|
||
// 更多图标
|
||
.more-icon-inner {
|
||
width: 100%;
|
||
height: 100%;
|
||
background: #f5f5f5;
|
||
border-radius: 16rpx;
|
||
|
||
.more-dots {
|
||
display: flex;
|
||
flex-direction: column;
|
||
align-items: center;
|
||
gap: 6rpx;
|
||
|
||
.more-dot {
|
||
width: 8rpx;
|
||
height: 8rpx;
|
||
border-radius: 50%;
|
||
background: #bbb;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
</style>
|