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

@@ -0,0 +1,275 @@
<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>

View File

@@ -1,250 +1,250 @@
<template>
<!-- 图片组件 -->
<view v-show="!dataConfig.isHide" class="images-component" :style="containerStyle">
<!-- 无链接时使用普通view -->
<view v-if="!linkUrl" class="images-navigator" :style="imgContainerStyle">
<image
v-if="imgUrl"
:src="imgUrl"
:mode="imgMode"
class="images-img"
:style="imgStyle"
:show-menu-by-longpress="isQrCode"
@load="onImgLoad"
@error="onImgError"
></image>
<!-- 占位图 -->
<view v-else class="images-placeholder">
<text class="placeholder-text">暂无图片</text>
</view>
</view>
<!-- 有链接时使用navigator -->
<navigator v-else :url="linkUrl" hover-class="none" class="images-navigator" :style="imgContainerStyle">
<image
v-if="imgUrl"
:src="imgUrl"
:mode="imgMode"
class="images-img"
:style="imgStyle"
:show-menu-by-longpress="isQrCode"
@load="onImgLoad"
@error="onImgError"
></image>
<!-- 占位图 -->
<view v-else class="images-placeholder">
<text class="placeholder-text">暂无图片</text>
</view>
</navigator>
</view>
</template>
<script>
export default {
name: 'home_image',
props: {
dataConfig: {
type: Object,
default: () => {}
}
},
data() {
return {
imgError: false
}
},
mounted() {
const debug = uni.getStorageSync('DEBUG_DIY_RENDER') === true
if (!debug) return
console.log('[DIY][component]', 'home_image', 'mounted', {
id: this.dataConfig?.id,
timestamp: this.dataConfig?.timestamp,
url: this.imgConfig?.url || this.dataConfig?.imgConfig?.url
})
},
computed: {
// 图片配置
imgConfig() {
return this.dataConfig.imgConfig || {}
},
// 链接配置
linkConfig() {
return this.dataConfig.linkConfig || {}
},
// 二维码模式
isQrCode() {
return this.dataConfig.isQrCode?.val === true
},
// 上边距
topConfig() {
return this.dataConfig.topConfig || {}
},
// 下边距
bottomConfig() {
return this.dataConfig.bottomConfig || {}
},
// 左右边距
prConfig() {
return this.dataConfig.prConfig || {}
},
// 页面间距
mbConfig() {
return this.dataConfig.mbConfig || {}
},
// 圆角配置
fillet() {
return this.dataConfig.fillet || {}
},
// 背景颜色
bgColor() {
return this.dataConfig.bgColor || {}
},
// 图片地址
imgUrl() {
const url = this.imgConfig.url || ''
if (!url) return ''
// 处理安全域名
if (url.indexOf('https://') > -1) return url
return url.replace('http://', 'https://')
},
// 链接地址(从 imgConfig.link 获取)
linkUrl() {
// 直接从 imgConfig.link 获取字符串链接
const link = this.imgConfig.link || this.linkConfig?.link || ''
if (!link) return ''
// 支持字符串链接
if (typeof link === 'string') {
return link
}
// 支持对象结构 { url: '', title: '' }
if (typeof link === 'object') {
return link.url || ''
}
// 兼容旧的数组结构
if (Array.isArray(link) && link.length > 0) {
return link[0]?.link || link[0]?.url || ''
}
return ''
},
// 图片显示模式
imgMode() {
return 'widthFix'
},
// 容器样式
containerStyle() {
const style = {}
// 上边距
if (this.topConfig.val) {
style.marginTop = this.topConfig.val + 'rpx'
}
// 页面间距
if (this.mbConfig.val) {
style.marginBottom = this.mbConfig.val + 'rpx'
}
// 下边距
if (this.bottomConfig.val) {
style.paddingBottom = this.bottomConfig.val + 'rpx'
}
// 背景颜色
if (this.bgColor.color && this.bgColor.color[0]?.item) {
style.backgroundColor = this.bgColor.color[0].item
}
return style
},
// 图片容器样式
imgContainerStyle() {
const style = {}
const fillet = this.fillet
// 左右边距
if (this.prConfig.val) {
style.paddingLeft = this.prConfig.val + 'rpx'
style.paddingRight = this.prConfig.val + 'rpx'
}
// 圆角 - 全部圆角模式
if (fillet.type === 0 && fillet.val) {
style.borderRadius = fillet.val + 'rpx'
}
return style
},
// 图片样式
imgStyle() {
const style = {}
const fillet = this.fillet
// 圆角 - 单独设置模式
if (fillet.type === 1 && fillet.valList) {
const [tl, tr, br, bl] = fillet.valList
const radius = `${tl?.val || 0}rpx ${tr?.val || 0}rpx ${br?.val || 0}rpx ${bl?.val || 0}rpx`
style.borderRadius = radius
}
return style
}
},
methods: {
onImgLoad(e) {
const debug = uni.getStorageSync('DEBUG_DIY_RENDER') === true
if (!debug) return
console.log('[DIY][component]', 'home_image', 'img_load', {
id: this.dataConfig?.id,
timestamp: this.dataConfig?.timestamp,
src: this.imgUrl,
width: e?.detail?.width,
height: e?.detail?.height
})
},
// 图片加载失败处理
onImgError(e) {
const debug = uni.getStorageSync('DEBUG_DIY_RENDER') === true
if (debug) {
console.log('[DIY][component]', 'home_image', 'img_error', {
id: this.dataConfig?.id,
timestamp: this.dataConfig?.timestamp,
src: this.imgUrl,
detail: e?.detail
})
}
this.imgError = true
}
}
}
</script>
<style lang="scss" scoped>
.images-component {
width: 100%;
box-sizing: border-box;
overflow: hidden;
.images-navigator {
display: block;
width: 100%;
}
.images-img {
width: 100%;
display: block;
}
.images-placeholder {
width: 100%;
height: 300rpx;
background-color: #f5f5f5;
display: flex;
align-items: center;
justify-content: center;
.placeholder-text {
color: #999;
font-size: 28rpx;
}
}
}
</style>
<template>
<!-- 图片组件 -->
<view v-show="!dataConfig.isHide" class="images-component" :style="containerStyle">
<!-- 无链接时使用普通view -->
<view v-if="!linkUrl" class="images-navigator" :style="imgContainerStyle">
<image
v-if="imgUrl"
:src="imgUrl"
:mode="imgMode"
class="images-img"
:style="imgStyle"
:show-menu-by-longpress="isQrCode"
@load="onImgLoad"
@error="onImgError"
></image>
<!-- 占位图 -->
<view v-else class="images-placeholder">
<text class="placeholder-text">暂无图片</text>
</view>
</view>
<!-- 有链接时使用navigator -->
<navigator v-else :url="linkUrl" hover-class="none" class="images-navigator" :style="imgContainerStyle">
<image
v-if="imgUrl"
:src="imgUrl"
:mode="imgMode"
class="images-img"
:style="imgStyle"
:show-menu-by-longpress="isQrCode"
@load="onImgLoad"
@error="onImgError"
></image>
<!-- 占位图 -->
<view v-else class="images-placeholder">
<text class="placeholder-text">暂无图片</text>
</view>
</navigator>
</view>
</template>
<script>
export default {
name: 'home_image',
props: {
dataConfig: {
type: Object,
default: () => {}
}
},
data() {
return {
imgError: false
}
},
mounted() {
const debug = uni.getStorageSync('DEBUG_DIY_RENDER') === true
if (!debug) return
console.log('[DIY][component]', 'home_image', 'mounted', {
id: this.dataConfig?.id,
timestamp: this.dataConfig?.timestamp,
url: this.imgConfig?.url || this.dataConfig?.imgConfig?.url
})
},
computed: {
// 图片配置
imgConfig() {
return this.dataConfig.imgConfig || {}
},
// 链接配置
linkConfig() {
return this.dataConfig.linkConfig || {}
},
// 二维码模式
isQrCode() {
return this.dataConfig.isQrCode?.val === true
},
// 上边距
topConfig() {
return this.dataConfig.topConfig || {}
},
// 下边距
bottomConfig() {
return this.dataConfig.bottomConfig || {}
},
// 左右边距
prConfig() {
return this.dataConfig.prConfig || {}
},
// 页面间距
mbConfig() {
return this.dataConfig.mbConfig || {}
},
// 圆角配置
fillet() {
return this.dataConfig.fillet || {}
},
// 背景颜色
bgColor() {
return this.dataConfig.bgColor || {}
},
// 图片地址
imgUrl() {
const url = this.imgConfig.url || ''
if (!url) return ''
// 处理安全域名
if (url.indexOf('https://') > -1) return url
return url.replace('http://', 'https://')
},
// 链接地址(从 imgConfig.link 获取)
linkUrl() {
// 直接从 imgConfig.link 获取字符串链接
const link = this.imgConfig.link || this.linkConfig?.link || ''
if (!link) return ''
// 支持字符串链接
if (typeof link === 'string') {
return link
}
// 支持对象结构 { url: '', title: '' }
if (typeof link === 'object') {
return link.url || ''
}
// 兼容旧的数组结构
if (Array.isArray(link) && link.length > 0) {
return link[0]?.link || link[0]?.url || ''
}
return ''
},
// 图片显示模式
imgMode() {
return 'widthFix'
},
// 容器样式
containerStyle() {
const style = {}
// 上边距
if (this.topConfig.val) {
style.marginTop = this.topConfig.val + 'rpx'
}
// 页面间距
if (this.mbConfig.val) {
style.marginBottom = this.mbConfig.val + 'rpx'
}
// 下边距
if (this.bottomConfig.val) {
style.paddingBottom = this.bottomConfig.val + 'rpx'
}
// 背景颜色
if (this.bgColor.color && this.bgColor.color[0]?.item) {
style.backgroundColor = this.bgColor.color[0].item
}
return style
},
// 图片容器样式
imgContainerStyle() {
const style = {}
const fillet = this.fillet
// 左右边距
if (this.prConfig.val) {
style.paddingLeft = this.prConfig.val + 'rpx'
style.paddingRight = this.prConfig.val + 'rpx'
}
// 圆角 - 全部圆角模式
if (fillet.type === 0 && fillet.val) {
style.borderRadius = fillet.val + 'rpx'
}
return style
},
// 图片样式
imgStyle() {
const style = {}
const fillet = this.fillet
// 圆角 - 单独设置模式
if (fillet.type === 1 && fillet.valList) {
const [tl, tr, br, bl] = fillet.valList
const radius = `${tl?.val || 0}rpx ${tr?.val || 0}rpx ${br?.val || 0}rpx ${bl?.val || 0}rpx`
style.borderRadius = radius
}
return style
}
},
methods: {
onImgLoad(e) {
const debug = uni.getStorageSync('DEBUG_DIY_RENDER') === true
if (!debug) return
console.log('[DIY][component]', 'home_image', 'img_load', {
id: this.dataConfig?.id,
timestamp: this.dataConfig?.timestamp,
src: this.imgUrl,
width: e?.detail?.width,
height: e?.detail?.height
})
},
// 图片加载失败处理
onImgError(e) {
const debug = uni.getStorageSync('DEBUG_DIY_RENDER') === true
if (debug) {
console.log('[DIY][component]', 'home_image', 'img_error', {
id: this.dataConfig?.id,
timestamp: this.dataConfig?.timestamp,
src: this.imgUrl,
detail: e?.detail
})
}
this.imgError = true
}
}
}
</script>
<style lang="scss" scoped>
.images-component {
width: 100%;
box-sizing: border-box;
overflow: hidden;
.images-navigator {
display: block;
width: 100%;
}
.images-img {
width: 100%;
display: block;
}
.images-placeholder {
width: 100%;
height: 300rpx;
background-color: #f5f5f5;
display: flex;
align-items: center;
justify-content: center;
.placeholder-text {
color: #999;
font-size: 28rpx;
}
}
}
</style>

View File

@@ -1,216 +1,216 @@
<template>
<view v-show="!isHidden" class="page-title-wrap" :style="wrapStyleStr">
<view class="page-title-box px-20" :style="pageTitleBoxStyleStr">
<view class="nav-inner" :style="navInnerStyleStr">
<view class="side" :style="leftSideStyleStr" @tap="onBack">
<text class="iconfont icon-ic_leftarrow" :style="iconStyleStr"></text>
</view>
<view class="title" :style="titleStyleStr">{{ titleText }}</view>
<view class="side" :style="rightSideStyleStr"></view>
</view>
</view>
</view>
</template>
<script>
export default {
name: 'pageTopbar',
props: {
dataConfig: {
type: Object,
default: () => ({})
}
},
data() {
return {
__statusBarRpx: 0,
__statusBarPx: 0,
__rpx2px: 0.5,
__navHeightPx: 44
}
},
created() {
try {
const info = uni.getWindowInfo ? uni.getWindowInfo() : uni.getSystemInfoSync()
const statusBarPx = Number(info?.statusBarHeight || 0)
const windowWidthPx = Number(info?.windowWidth || 375)
this.__statusBarPx = statusBarPx
this.__rpx2px = windowWidthPx ? windowWidthPx / 750 : 0.5
this.__navHeightPx = 80 * this.__rpx2px
this.__statusBarRpx = windowWidthPx ? (statusBarPx * 750) / windowWidthPx : 0
} catch (e) {
this.__statusBarPx = 0
this.__rpx2px = 0.5
this.__navHeightPx = 44
this.__statusBarRpx = 0
}
},
mounted() {
const debug = uni.getStorageSync('DEBUG_DIY_RENDER') === true
if (!debug) return
console.log('[DIY][component]', 'pageTopbar', 'mounted', {
id: this.dataConfig?.id,
timestamp: this.dataConfig?.timestamp,
title: this.dataConfig?.titleConfig?.value,
statusBarRpx: this.__statusBarRpx,
statusBarPx: this.__statusBarPx,
rpx2px: this.__rpx2px,
navHeightPx: this.__navHeightPx,
sideWidthPx: this.sideWidthPx
})
this.$nextTick(() => {
try {
uni.createSelectorQuery()
.in(this)
.select('.page-title-wrap')
.boundingClientRect(rect => {
console.log('[DIY][component]', 'pageTopbar', 'rect', rect)
})
.select('.page-title-box')
.boundingClientRect(rect => {
console.log('[DIY][component]', 'pageTopbar', 'rect_box', rect)
})
.select('.nav-inner')
.boundingClientRect(rect => {
console.log('[DIY][component]', 'pageTopbar', 'rect_inner', rect)
})
.exec()
} catch (e) {}
})
},
computed: {
isHidden() {
return this.dataConfig?.isHide === true || this.dataConfig?.isHide === '1'
},
titleText() {
return this.dataConfig?.titleConfig?.value || ''
},
linkValue() {
return this.dataConfig?.linkConfig?.value || ''
},
themeColorValue() {
return this.dataConfig?.themeColor?.color?.[0]?.item || '#282828'
},
titleColorValue() {
return this.dataConfig?.titleColor?.color?.[0]?.item || '#282828'
},
bgColorValue() {
return this.dataConfig?.bgColor?.color?.[0]?.item || '#ffffff'
},
fontSizeVal() {
return Number(this.dataConfig?.fontSize?.val ?? 16)
},
iconSizeVal() {
return Number(this.dataConfig?.iconSize?.val ?? 18)
},
navHeightRpx() {
return 80
},
topVal() {
return Number(this.dataConfig?.topConfig?.val ?? 0)
},
bottomVal() {
return Number(this.dataConfig?.bottomConfig?.val ?? 0)
},
prVal() {
return Number(this.dataConfig?.prConfig?.val ?? 0)
},
mbVal() {
return Number(this.dataConfig?.mbConfig?.val ?? 0)
},
fillet() {
return this.dataConfig?.fillet || {}
},
wrapStyleStr() {
const top = this.topVal * 2
const padTop = Math.max(0, Number(top))
return `background:#f5f5f5;margin-top:${this.mbVal * 2}rpx;padding-top:${padTop}rpx;padding-bottom:${this.bottomVal * 2}rpx;padding-left:${this.prVal * 2}rpx;padding-right:${this.prVal * 2}rpx;`
},
pageTitleBoxStyleStr() {
const base = `width:100%;display:flex;align-items:center;padding-top:${this.__statusBarPx}px;box-sizing:border-box;`
const bg = `background:${this.bgColorValue};`
let radius = ''
if (this.fillet.type === 0 && this.fillet.val !== undefined) {
radius = `border-radius:${Number(this.fillet.val) * 2}rpx;`
}
if (this.fillet.type === 1 && Array.isArray(this.fillet.valList)) {
const [tl, tr, br, bl] = this.fillet.valList
radius = `border-radius:${Number(tl?.val || 0) * 2}rpx ${Number(tr?.val || 0) * 2}rpx ${Number(br?.val || 0) * 2}rpx ${Number(bl?.val || 0) * 2}rpx;`
}
return base + bg + radius
},
navInnerStyleStr() {
return `width:100%;display:flex;align-items:center;height:${this.__navHeightPx}px;box-sizing:border-box;`
},
leftSideStyleStr() {
return `width:${this.sideWidthPx}px;height:100%;display:flex;align-items:center;justify-content:flex-start;`
},
rightSideStyleStr() {
return `width:${this.sideWidthPx}px;height:100%;display:flex;align-items:center;justify-content:flex-end;`
},
iconStyleStr() {
return `font-size:${this.iconSizeVal * 2}rpx;color:${this.themeColorValue};font-weight:bold;`
},
titleStyleStr() {
return `font-size:${this.fontSizeVal * 2}rpx;color:${this.titleColorValue};flex:1;font-weight:bold;text-align:center;line-height:${this.__navHeightPx}px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;`
},
sideWidthRpx() {
return Math.max(80, this.iconSizeVal * 2 + 28)
},
sideWidth() {
return `${this.sideWidthRpx}rpx`
},
sideWidthPx() {
return this.sideWidthRpx * (this.__rpx2px || 0.5)
}
},
methods: {
onBack() {
const url = this.linkValue
if (!url) return
const pure = url.split('?')[0]
const tabPages = [
'/pages/index/index',
'/pages/goods_cate/goods_cate',
'/pages/discoverIndex/index',
'/pages/order_addcart/order_addcart',
'/pages/user/index'
]
if (tabPages.includes(pure)) {
uni.switchTab({ url })
return
}
uni.navigateTo({ url })
}
}
}
</script>
<style lang="scss" scoped>
.page-title-wrap {
width: 100%;
}
.page-title-box {
width: 100%;
display: flex;
align-items: center;
}
.nav-inner {
width: 100%;
display: flex;
align-items: center;
}
.side {
display: flex;
align-items: center;
justify-content: flex-start;
}
.title {
flex: 1;
font-weight: bold;
text-align: center;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
</style>
<template>
<view v-show="!isHidden" class="page-title-wrap" :style="wrapStyleStr">
<view class="page-title-box px-20" :style="pageTitleBoxStyleStr">
<view class="nav-inner" :style="navInnerStyleStr">
<view class="side" :style="leftSideStyleStr" @tap="onBack">
<text class="iconfont icon-ic_leftarrow" :style="iconStyleStr"></text>
</view>
<view class="title" :style="titleStyleStr">{{ titleText }}</view>
<view class="side" :style="rightSideStyleStr"></view>
</view>
</view>
</view>
</template>
<script>
export default {
name: 'pageTopbar',
props: {
dataConfig: {
type: Object,
default: () => ({})
}
},
data() {
return {
__statusBarRpx: 0,
__statusBarPx: 0,
__rpx2px: 0.5,
__navHeightPx: 44
}
},
created() {
try {
const info = uni.getWindowInfo ? uni.getWindowInfo() : uni.getSystemInfoSync()
const statusBarPx = Number(info?.statusBarHeight || 0)
const windowWidthPx = Number(info?.windowWidth || 375)
this.__statusBarPx = statusBarPx
this.__rpx2px = windowWidthPx ? windowWidthPx / 750 : 0.5
this.__navHeightPx = 80 * this.__rpx2px
this.__statusBarRpx = windowWidthPx ? (statusBarPx * 750) / windowWidthPx : 0
} catch (e) {
this.__statusBarPx = 0
this.__rpx2px = 0.5
this.__navHeightPx = 44
this.__statusBarRpx = 0
}
},
mounted() {
const debug = uni.getStorageSync('DEBUG_DIY_RENDER') === true
if (!debug) return
console.log('[DIY][component]', 'pageTopbar', 'mounted', {
id: this.dataConfig?.id,
timestamp: this.dataConfig?.timestamp,
title: this.dataConfig?.titleConfig?.value,
statusBarRpx: this.__statusBarRpx,
statusBarPx: this.__statusBarPx,
rpx2px: this.__rpx2px,
navHeightPx: this.__navHeightPx,
sideWidthPx: this.sideWidthPx
})
this.$nextTick(() => {
try {
uni.createSelectorQuery()
.in(this)
.select('.page-title-wrap')
.boundingClientRect(rect => {
console.log('[DIY][component]', 'pageTopbar', 'rect', rect)
})
.select('.page-title-box')
.boundingClientRect(rect => {
console.log('[DIY][component]', 'pageTopbar', 'rect_box', rect)
})
.select('.nav-inner')
.boundingClientRect(rect => {
console.log('[DIY][component]', 'pageTopbar', 'rect_inner', rect)
})
.exec()
} catch (e) {}
})
},
computed: {
isHidden() {
return this.dataConfig?.isHide === true || this.dataConfig?.isHide === '1'
},
titleText() {
return this.dataConfig?.titleConfig?.value || ''
},
linkValue() {
return this.dataConfig?.linkConfig?.value || ''
},
themeColorValue() {
return this.dataConfig?.themeColor?.color?.[0]?.item || '#282828'
},
titleColorValue() {
return this.dataConfig?.titleColor?.color?.[0]?.item || '#282828'
},
bgColorValue() {
return this.dataConfig?.bgColor?.color?.[0]?.item || '#ffffff'
},
fontSizeVal() {
return Number(this.dataConfig?.fontSize?.val ?? 16)
},
iconSizeVal() {
return Number(this.dataConfig?.iconSize?.val ?? 18)
},
navHeightRpx() {
return 80
},
topVal() {
return Number(this.dataConfig?.topConfig?.val ?? 0)
},
bottomVal() {
return Number(this.dataConfig?.bottomConfig?.val ?? 0)
},
prVal() {
return Number(this.dataConfig?.prConfig?.val ?? 0)
},
mbVal() {
return Number(this.dataConfig?.mbConfig?.val ?? 0)
},
fillet() {
return this.dataConfig?.fillet || {}
},
wrapStyleStr() {
const top = this.topVal * 2
const padTop = Math.max(0, Number(top))
return `background:#f5f5f5;margin-top:${this.mbVal * 2}rpx;padding-top:${padTop}rpx;padding-bottom:${this.bottomVal * 2}rpx;padding-left:${this.prVal * 2}rpx;padding-right:${this.prVal * 2}rpx;`
},
pageTitleBoxStyleStr() {
const base = `width:100%;display:flex;align-items:center;padding-top:${this.__statusBarPx}px;box-sizing:border-box;`
const bg = `background:${this.bgColorValue};`
let radius = ''
if (this.fillet.type === 0 && this.fillet.val !== undefined) {
radius = `border-radius:${Number(this.fillet.val) * 2}rpx;`
}
if (this.fillet.type === 1 && Array.isArray(this.fillet.valList)) {
const [tl, tr, br, bl] = this.fillet.valList
radius = `border-radius:${Number(tl?.val || 0) * 2}rpx ${Number(tr?.val || 0) * 2}rpx ${Number(br?.val || 0) * 2}rpx ${Number(bl?.val || 0) * 2}rpx;`
}
return base + bg + radius
},
navInnerStyleStr() {
return `width:100%;display:flex;align-items:center;height:${this.__navHeightPx}px;box-sizing:border-box;`
},
leftSideStyleStr() {
return `width:${this.sideWidthPx}px;height:100%;display:flex;align-items:center;justify-content:flex-start;`
},
rightSideStyleStr() {
return `width:${this.sideWidthPx}px;height:100%;display:flex;align-items:center;justify-content:flex-end;`
},
iconStyleStr() {
return `font-size:${this.iconSizeVal * 2}rpx;color:${this.themeColorValue};font-weight:bold;`
},
titleStyleStr() {
return `font-size:${this.fontSizeVal * 2}rpx;color:${this.titleColorValue};flex:1;font-weight:bold;text-align:center;line-height:${this.__navHeightPx}px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;`
},
sideWidthRpx() {
return Math.max(80, this.iconSizeVal * 2 + 28)
},
sideWidth() {
return `${this.sideWidthRpx}rpx`
},
sideWidthPx() {
return this.sideWidthRpx * (this.__rpx2px || 0.5)
}
},
methods: {
onBack() {
const url = this.linkValue
if (!url) return
const pure = url.split('?')[0]
const tabPages = [
'/pages/index/index',
'/pages/goods_cate/goods_cate',
'/pages/discoverIndex/index',
'/pages/order_addcart/order_addcart',
'/pages/user/index'
]
if (tabPages.includes(pure)) {
uni.switchTab({ url })
return
}
uni.navigateTo({ url })
}
}
}
</script>
<style lang="scss" scoped>
.page-title-wrap {
width: 100%;
}
.page-title-box {
width: 100%;
display: flex;
align-items: center;
}
.nav-inner {
width: 100%;
display: flex;
align-items: center;
}
.side {
display: flex;
align-items: center;
justify-content: flex-start;
}
.title {
flex: 1;
font-weight: bold;
text-align: center;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
</style>

View File

@@ -1,102 +1,102 @@
<template>
<!-- 富文本 -->
<view v-if="!dataConfig.isHide" :style="[richTextWrapStyle]">
<view class="richText" v-if="description" :style="[richTextStyle]">
<!-- #ifdef MP-WEIXIN -->
<!-- <rich-text :nodes="description"></rich-text> -->
<jyf-parser :html="description" :tag-style="tagStyle" ref="article"></jyf-parser>
<!-- #endif -->
<!-- #ifdef H5 || APP-PLUS -->
<view v-html="description"></view>
<!-- #endif -->
</view>
</view>
</template>
<script>
import parser from '@/components/jyf-parser/jyf-parser';
export default {
name: 'richText',
props: {
dataConfig: {
type: Object,
default: () => {}
},
isSortType: {
type: String | Number,
default: 0
}
},
components: { 'jyf-parser': parser },
data() {
return {
bgColor: '',
lrConfig: 0,
udConfig: 0,
tagStyle: {
img: 'width:100%;display:block;',
table: 'width:100%',
video: 'width:100%;float:left;'
},
};
},
computed: {
description() {
let description = this.dataConfig.richText.val;
if (description) {
description = description.replace(
/<img/gi,
'<img style="max-width:100%;height:auto;float:left;display:block" '
);
description = description.replace(
/<video/gi,
'<video style="width:100%;height:auto;display:block" '
);
}
return description;
},
richTextStyle() {
let borderRadius = `${this.dataConfig.fillet.val * 2}rpx`;
if (this.dataConfig.fillet.type) {
borderRadius =
`${this.dataConfig.fillet.valList[0].val * 2}rpx ${this.dataConfig.fillet.valList[1].val * 2}rpx ${this.dataConfig.fillet.valList[2].val * 2}rpx ${this.dataConfig.fillet.valList[3].val * 2}rpx`;
}
return {
'border-radius': borderRadius,
'background': this.dataConfig.bgColor.color[0].item,
};
},
richTextWrapStyle() {
return {
'padding': `${this.dataConfig.topConfig.val * 2}rpx ${this.dataConfig.lrConfig.val * 2}rpx ${this.dataConfig.bottomConfig.val * 2}rpx`,
'margin-top': `${this.dataConfig.udConfig.val * 2}rpx`,
'background': this.dataConfig.bottomBgColor.color[0].item,
};
},
},
}
</script>
<style lang="scss">
.richText {
padding: 30rpx;
background-color: #fff;
// margin: 0 20rpx;
// border-radius: 24rpx;
&::after {
content: "";
display: table;
clear: both;
}
}
/deep/uni-video {
width: 100% !important;
}
/deep/video {
width: 100% !important;
}
<template>
<!-- 富文本 -->
<view v-if="!dataConfig.isHide" :style="[richTextWrapStyle]">
<view class="richText" v-if="description" :style="[richTextStyle]">
<!-- #ifdef MP-WEIXIN -->
<!-- <rich-text :nodes="description"></rich-text> -->
<jyf-parser :html="description" :tag-style="tagStyle" ref="article"></jyf-parser>
<!-- #endif -->
<!-- #ifdef H5 || APP-PLUS -->
<view v-html="description"></view>
<!-- #endif -->
</view>
</view>
</template>
<script>
import parser from '@/components/jyf-parser/jyf-parser';
export default {
name: 'richText',
props: {
dataConfig: {
type: Object,
default: () => {}
},
isSortType: {
type: String | Number,
default: 0
}
},
components: { 'jyf-parser': parser },
data() {
return {
bgColor: '',
lrConfig: 0,
udConfig: 0,
tagStyle: {
img: 'width:100%;display:block;',
table: 'width:100%',
video: 'width:100%;float:left;'
},
};
},
computed: {
description() {
let description = this.dataConfig.richText.val;
if (description) {
description = description.replace(
/<img/gi,
'<img style="max-width:100%;height:auto;float:left;display:block" '
);
description = description.replace(
/<video/gi,
'<video style="width:100%;height:auto;display:block" '
);
}
return description;
},
richTextStyle() {
let borderRadius = `${this.dataConfig.fillet.val * 2}rpx`;
if (this.dataConfig.fillet.type) {
borderRadius =
`${this.dataConfig.fillet.valList[0].val * 2}rpx ${this.dataConfig.fillet.valList[1].val * 2}rpx ${this.dataConfig.fillet.valList[2].val * 2}rpx ${this.dataConfig.fillet.valList[3].val * 2}rpx`;
}
return {
'border-radius': borderRadius,
'background': this.dataConfig.bgColor.color[0].item,
};
},
richTextWrapStyle() {
return {
'padding': `${this.dataConfig.topConfig.val * 2}rpx ${this.dataConfig.lrConfig.val * 2}rpx ${this.dataConfig.bottomConfig.val * 2}rpx`,
'margin-top': `${this.dataConfig.udConfig.val * 2}rpx`,
'background': this.dataConfig.bottomBgColor.color[0].item,
};
},
},
}
</script>
<style lang="scss">
.richText {
padding: 30rpx;
background-color: #fff;
// margin: 0 20rpx;
// border-radius: 24rpx;
&::after {
content: "";
display: table;
clear: both;
}
}
/deep/uni-video {
width: 100% !important;
}
/deep/video {
width: 100% !important;
}
</style>

File diff suppressed because it is too large Load Diff