Files
crmeb-mp/pages/index/components/appCenter.vue

276 lines
6.6 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<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>