feat(页面组件): 新增图片组件和页面顶部导航组件

- 新增 images.vue 图片组件,支持图片展示、链接跳转和二维码长按识别
- 新增 pageTopbar.vue 页面顶部导航组件,支持自定义标题、返回按钮和样式配置
- 在首页和专题页中注册并使用新增组件
- 优化 homeComb.vue 组件,添加选项卡轮播显示控制逻辑
- 添加 DIY 组件调试日志功能,便于开发调试
- 修复缓存读取空值时的默认值处理
This commit is contained in:
2026-04-16 19:51:08 +08:00
parent a5c5d753b6
commit 797724c01d
6 changed files with 545 additions and 7 deletions

View File

@@ -116,6 +116,8 @@
})
}
// #endif
uni.setStorageSync('DEBUG_DIY_RENDER', true)
},
methods: {
remoteRegister(remote_token) {

View File

@@ -11,6 +11,7 @@
<userInfor v-if="item.name == 'userInfor'" :dataConfig="item" @changeLogin="changeLogin">
</userInfor>
<newVip v-if="item.name == 'newVip'" :dataConfig="item"></newVip>
<pageTopbar v-if="item.name == 'pageTopbar'" :dataConfig="item"></pageTopbar>
<!-- 文章列表 -->
<articleList v-if="item.name == 'articleList'" :dataConfig="item"></articleList>
<bargain v-if="item.name == 'bargain'" :dataConfig="item" @changeBarg="changeBarg"></bargain>
@@ -35,6 +36,8 @@
<!-- 图片库 -->
<pictureCube v-if="item.name == 'pictureCube'" :dataConfig="item">
</pictureCube>
<!-- 图片组件 -->
<images v-if="item.name == 'images'" :dataConfig="item"></images>
<!-- 促销列表 -->
<promotionList v-if="item.name == 'promotionList'" :dataConfig="item" @detail="goDetail"
:productVideoStatus='product_video_status'>
@@ -105,6 +108,7 @@
import userInfor from '@/pages/index/components/userInfor';
import homeComb from '@/pages/index/components/homeComb';
import newVip from '@/pages/index/components/newVip';
import pageTopbar from '@/pages/index/components/pageTopbar';
import headerSerch from '@/pages/index/components/headerSerch';
import swipers from '@/pages/index/components/swipers';
import coupon from '@/pages/index/components/coupon';
@@ -119,6 +123,7 @@
import menus from '@/pages/index/components/menus';
import news from '@/pages/index/components/news';
import pictureCube from '@/pages/index/components/pictureCube';
import images from '@/pages/index/components/images';
import promotionList from '@/pages/index/components/promotionList';
import richText from '@/pages/index/components/richText';
import seckill from '@/pages/index/components/seckill';
@@ -171,6 +176,7 @@
couponWindow,
homeComb,
newVip,
pageTopbar,
userInfor,
headerSerch,
swipers,
@@ -185,6 +191,7 @@
liveBroadcast,
menus,
pictureCube,
images,
news,
promotionList,
richText,
@@ -205,6 +212,7 @@
},
data() {
return {
__debugDiyRender: false,
styleConfig: [],
loading: false,
loadend: false,
@@ -246,6 +254,7 @@
},
onLoad(options) {
let that = this
this.__debugDiyRender = uni.getStorageSync('DEBUG_DIY_RENDER') === true
this.$nextTick(function() {
uni.getSystemInfo({
success: function(res) {
@@ -323,6 +332,11 @@
},
mounted() {},
methods: {
__logDiy(step, payload) {
if (!this.__debugDiyRender) return;
const safe = payload === undefined ? '' : payload
console.log('[DIY][special]', step, safe)
},
// 分类点击
changeSort(item, index) {
if (this.curSort == index) return;
@@ -355,6 +369,7 @@
*/
getMicroPage(id, type) {
let that = this;
that.__logDiy('micro_fetch_start', { id })
that.styleConfig = []
uni.showLoading({
title: '加载中...'
@@ -363,11 +378,15 @@
uni.hideLoading();
let data = res.data;
that.styleConfig = that.objToArr(res.data.value);
that.__logDiy('micro_fetch_ok', { title: data?.title, count: that.styleConfig.length, names: that.styleConfig.map(v => v?.name) })
that.styleConfig.forEach((item, index) => {
if (['headerSerch', 'homeComb'].includes(item.name)) {
that.styleConfig.splice(index, 1);
}
});
that.$nextTick(() => {
that.__logDiy('micro_render_nextTick', { count: that.styleConfig.length, names: that.styleConfig.map(v => v?.name) })
})
}).catch(err => {
return that.$util.Tips({
title: err
@@ -504,10 +523,12 @@
return m;
},
getDiyData() {
this.__logDiy('special_fetch_start', { id: this.pageId })
getDiy(this.pageId).then(res => {
// uni.setStorageSync('specialDiyData', JSON.stringify(res.data));
// this.setDiyData(res.data);
let data = res.data;
this.__logDiy('special_fetch_ok', { title: data?.title, keys: Object.keys(data?.value || {}).length })
if (data.is_bg_color) {
this.bgColor = data.color_picker
}
@@ -521,6 +542,7 @@
})
let temp = []
let lastArr = this.objToArr(data.value)
this.__logDiy('special_value_arr', { count: lastArr.length, names: lastArr.map(v => v?.name) })
lastArr.forEach((item, index, arr) => {
if (item.isHide !== '1') {
temp.push(item);
@@ -530,6 +552,10 @@
}
});
this.styleConfig = temp;
this.__logDiy('special_styleConfig_set', { count: this.styleConfig.length, names: this.styleConfig.map(v => v?.name) })
this.$nextTick(() => {
this.__logDiy('special_render_nextTick', { count: this.styleConfig.length, names: this.styleConfig.map(v => v?.name) })
})
});
},
changeBarg(item) {

View File

@@ -62,7 +62,7 @@
</view>
</view>
</view>
<view class="swiperBg pb-20">
<view class="swiperBg pb-20" v-if="showTabCarousel">
<!-- :style="{ paddingBottom: isMenu ? '30rpx' : '30rpx'}" -->
<view class="swiper page_swiper" v-if="imgUrls.length">
<swiper
@@ -179,6 +179,13 @@
swiperMargin() {
return this.dataConfig.styleConfig.tabVal ? '50rpx' : '10rpx';
},
tabCarouselConfig() {
return this.dataConfig.tabCarouselConfig || { tabVal: 0 };
},
showTabCarousel() {
// 如果选项卡轮播设置为显示,则切换选项卡后也显示,如果选项卡轮播设置为隐藏,则只在首页时显示
return this.tabCarouselConfig.tabVal === 0 ? true : this.tabClick === 0 ? true : false;
},
bgGradientStyle(){
return {
'background-image': `linear-gradient(to bottom, rgba(245,245,245,0) 0%, rgba(245,245,245,0) 50%, ${this.dataConfig.gradientColor ? this.dataConfig.gradientColor.color[0].item : '#f5f5f5'} 100%)`
@@ -234,7 +241,7 @@
};
},
tabListConfig() {
let tabList = this.dataConfig.tabListConfig.list;
let tabList = [...(this.dataConfig.tabListConfig?.list || [])];
tabList.unshift({
classPage: {
id: 0
@@ -291,9 +298,20 @@
this.hotWordShow = true;
}
})
console.log('tabCarouselConfig', that.tabCarouselConfig.tabVal)
that.computedHeight();
uni.setStorageSync('hotList', that.hotWords);
},
watch: {
tabClick(nVal) {
if (nVal === 0 && this.showTabCarousel && this.imgUrls.length) {
this.$nextTick(() => {
this.computedHeight();
})
}
}
},
methods: {
goDetail(url) {
let urls = url.info[1].value

View File

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

View File

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

View File

@@ -19,6 +19,7 @@
@changeLogin="changeLogin">
</userInfor>
<newVip v-if="item.name == 'newVip'" :dataConfig="item"></newVip>
<pageTopbar v-if="item.name == 'pageTopbar'" :dataConfig="item"></pageTopbar>
<!-- 文章列表 -->
<articleList v-if="item.name == 'articleList'" :dataConfig="item">
</articleList>
@@ -46,6 +47,8 @@
<news v-if="item.name == 'news'" :dataConfig="item"></news>
<!-- 图片库 -->
<pictureCube v-if="item.name == 'pictureCube'" :dataConfig="item"></pictureCube>
<!-- 图片组件 -->
<images v-if="item.name == 'images'" :dataConfig="item"></images>
<!-- 促销列表 -->
<promotionList v-if="item.name == 'promotionList'" :dataConfig="item"
:productVideoStatus='product_video_status' :showHomeComb="showHomeComb" :isHeaderSerch="isHeaderSerch">
@@ -146,6 +149,7 @@
import userInfor from './components/userInfor';
import homeComb from './components/homeComb';
import newVip from './components/newVip';
import pageTopbar from './components/pageTopbar';
// import shortVideo from './components/shortVideo';
import community from './components/community'
import headerSerch from './components/headerSerch';
@@ -162,6 +166,7 @@
import menus from './components/menus';
import news from './components/news';
import pictureCube from './components/pictureCube';
import images from './components/images';
import promotionList from './components/promotionList';
import richText from './components/richText';
import seckill from './components/seckill';
@@ -221,6 +226,7 @@
couponWindow,
homeComb,
newVip,
pageTopbar,
userInfor,
community,
headerSerch,
@@ -235,6 +241,7 @@
guide,
menus,
pictureCube,
images,
news,
promotionList,
richText,
@@ -263,6 +270,7 @@
data() {
return {
__debugDiyRender: false,
styleConfig: [],
loading: false,
loadend: false,
@@ -309,11 +317,12 @@
confirm_video_status: false,
isFooter: false,
pullReset: 0,
configData: Cache.get('BASIC_CONFIG'),
configData: Cache.get('BASIC_CONFIG') || {},
};
},
onLoad(options) {
let that = this;
this.__debugDiyRender = uni.getStorageSync('DEBUG_DIY_RENDER') === true
that.getOptions(options);
this.$nextTick(function() {
uni.getSystemInfo({
@@ -360,6 +369,11 @@
uni.removeStorageSync('form_type_cart')
},
methods: {
__logDiy(step, payload) {
if (!this.__debugDiyRender) return;
const safe = payload === undefined ? '' : payload
console.log('[DIY][home]', step, safe)
},
...mapMutations(['SET_AUTOPLAY']),
getOptions(options) {
let that = this;
@@ -431,6 +445,7 @@
*/
getMicroPage(id, type) {
let that = this;
that.__logDiy('micro_fetch_start', { id })
that.styleConfig = [];
that.goodList = [];
uni.showLoading({
@@ -440,6 +455,7 @@
uni.hideLoading();
let data = res.data;
let diyArr = that.objToArr(res.data.value);
that.__logDiy('micro_fetch_ok', { title: data?.title, count: diyArr.length, names: diyArr.map(v => v?.name) })
diyArr = diyArr.filter(item => item.isHide !== '1');
diyArr.forEach((item,index) => {
if(['headerSerch','homeComb'].includes(item.name)){
@@ -447,6 +463,9 @@
}
});
this.styleConfig = diyArr;
this.$nextTick(() => {
this.__logDiy('micro_render_nextTick', { count: this.styleConfig.length, names: this.styleConfig.map(v => v?.name) })
})
}).catch(err => {
return that.$util.Tips({
title: err
@@ -525,6 +544,7 @@
return m;
},
setDiyData(data) {
this.__logDiy('home_setDiyData_start', { title: data?.title })
this.errorNetwork = false
if (data.is_bg_color) {
this.bgColor = data.color_picker
@@ -563,9 +583,15 @@
}
temp.sort(sortNumber)
this.styleConfig = temp;
this.__logDiy('home_setDiyData_done', { count: this.styleConfig.length, names: this.styleConfig.map(v => v?.name) })
this.$nextTick(() => {
this.__logDiy('home_render_nextTick', { count: this.styleConfig.length, names: this.styleConfig.map(v => v?.name) })
})
},
getDiyData() {
this.__logDiy('home_fetch_start', { id: 0 })
getDiy(0).then(res => {
this.__logDiy('home_fetch_ok', { title: res?.data?.title, keys: Object.keys(res?.data?.value || {}).length })
uni.setStorageSync('diyData', JSON.stringify(res.data));
this.setDiyData(res.data);
}).catch(error => {