1
0
mirror of https://github.com/ialley-workshop-open/uni-halo.git synced 2026-06-12 21:29:31 +08:00

feat: 分类页面支持切换不同的展示模式

This commit is contained in:
小莫唐尼
2025-06-16 19:42:45 +08:00
parent eb69ae4af6
commit 2a25727c0b
6 changed files with 467 additions and 233 deletions
+182 -180
View File
@@ -1,220 +1,222 @@
<template> <template>
<view class="article-min-card" :class="[globalAppSettings.layout.cardType]" @click="fnClickEvent('card')"> <view class="article-min-card" @click="fnClickEvent('card')">
<view class="left"> <view class="left">
<cache-image class="thumbnail" radius="6rpx" :url="$utils.checkThumbnailUrl(article.thumbnail)" :fileMd5="$utils.checkThumbnailUrl(article.thumbnail)" mode="aspectFill"></cache-image> <cache-image class="thumbnail" radius="6rpx" :url="$utils.checkThumbnailUrl(article.spec.cover)" :fileMd5="article.spec.cover"
</view> mode="aspectFill"></cache-image>
<view class="right"> </view>
<view class="title text-overflow">{{ article.title }}</view> <view class="right">
<view class="content text-overflow">{{ article.summary }}</view> <view class="title text-overflow">{{ article.spec.title }}</view>
<view class="foot"> <view class="content text-overflow">{{ article.status.excerpt }}</view>
<view class="create-time"> <view class="foot">
<!-- <text class="icon iconfont icon-clock"></text> --> <view class="create-time">
<text class="time-label">发布时间</text> <text class="time-label">发布时间</text>
{{ { d: article.createTime, f: 'yyyy-MM-dd' } | formatTime }} {{ {d: article.spec.publishTime, f: 'yyyy-MM-dd'} | formatTime }}
</view> </view>
<view class="visits"> <view class="visits">
<!-- <text class="icon iconfont icon-eye"></text> --> 浏览
浏览 <text class="number">{{ article.stats.visit }}</text>
<text class="number">{{ article.visits }}</text>
</view>
</view> </view>
</view> </view>
</view> </view>
</view>
</template> </template>
<script> <script>
export default { export default {
name: 'article-min-card', name: 'article-min-card',
props: { props: {
article: { article: {
type: Object, type: Object,
default: () => {} default: () => {
} }
}, }
methods: { },
fnClickEvent() { methods: {
this.$emit('on-click', this.article); fnClickEvent() {
} this.$emit('on-click', this.article);
} }
}; }
};
</script> </script>
<style scoped lang="scss"> <style scoped lang="scss">
.article-min-card { .article-min-card {
display: flex; display: flex;
box-sizing: border-box; box-sizing: border-box;
border-radius: 12rpx; border-radius: 12rpx;
background-color: #ffff; background-color: #ffff;
overflow: hidden; overflow: hidden;
margin: 12rpx 24rpx; margin: 12rpx 24rpx;
margin-bottom: 24rpx; margin-bottom: 24rpx;
padding: 16rpx; padding: 16rpx;
box-shadow: 0rpx 2rpx 24rpx rgba(0, 0, 0, 0.03); box-shadow: 0rpx 2rpx 24rpx rgba(0, 0, 0, 0.03);
&.lr_image_text {} &.lr_image_text {
}
&.lr_text_image { &.lr_text_image {
.left { .left {
order: 2; order: 2;
padding-left: 30rpx; padding-left: 30rpx;
} }
.right { .right {
order: 1; order: 1;
padding-left: 0; padding-left: 0;
} }
} }
&.tb_image_text { &.tb_image_text {
flex-direction: column; flex-direction: column;
.left { .left {
width: 100%; width: 100%;
height: 220rpx; height: 220rpx;
} }
.right { .right {
padding-left: 0; padding-left: 0;
width: 100%; width: 100%;
.title { .title {
margin-top: 24rpx; margin-top: 24rpx;
} }
.foot { .foot {
justify-content: flex-start; justify-content: flex-start;
.create-time { .create-time {
.time-label { .time-label {
display: inline-block; display: inline-block;
} }
} }
.visits { .visits {
margin-left: 24rpx; margin-left: 24rpx;
} }
} }
} }
} }
&.tb_text_image { &.tb_text_image {
flex-direction: column; flex-direction: column;
.left { .left {
width: 100%; width: 100%;
height: 220rpx; height: 220rpx;
order: 2; order: 2;
margin-top: 20rpx; margin-top: 20rpx;
} }
.right { .right {
padding-left: 0; padding-left: 0;
width: 100%; width: 100%;
order: 1; order: 1;
.foot { .foot {
justify-content: flex-start; justify-content: flex-start;
.create-time { .create-time {
.time-label { .time-label {
display: inline-block; display: inline-block;
} }
} }
.visits { .visits {
margin-left: 24rpx; margin-left: 24rpx;
} }
} }
} }
} }
&.only_text { &.only_text {
.left { .left {
display: none; display: none;
} }
.right { .right {
padding-left: 0; padding-left: 0;
.foot { .foot {
justify-content: flex-start; justify-content: flex-start;
.create-time { .create-time {
.time-label { .time-label {
display: inline-block; display: inline-block;
} }
} }
.visits { .visits {
margin-left: 24rpx; margin-left: 24rpx;
} }
} }
} }
} }
.left { .left {
width: 180rpx; width: 180rpx;
height: 130rpx; height: 130rpx;
.thumbnail { .thumbnail {
width: 100%; width: 100%;
height: 100%; height: 100%;
border-radius: 12rpx; border-radius: 12rpx;
} }
} }
.right { .right {
width: 0; width: 0;
flex-grow: 1; flex-grow: 1;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
padding-left: 20rpx; padding-left: 20rpx;
.title { .title {
font-size: 28rpx; font-size: 28rpx;
font-weight: 600; font-weight: 600;
color: var(--main-text-color); color: var(--main-text-color);
} }
.content { .content {
font-size: 26rpx; font-size: 26rpx;
color: #909399; color: #909399;
margin-top: 14rpx; margin-top: 14rpx;
} }
.foot { .foot {
display: flex; display: flex;
font-size: 24rpx; font-size: 24rpx;
justify-content: space-between; justify-content: space-between;
align-items: center; align-items: center;
color: #909399; color: #909399;
margin-top: 14rpx; margin-top: 14rpx;
.create-time { .create-time {
font-size: 24rpx; font-size: 24rpx;
.time-label { .time-label {
display: none; display: none;
} }
.icon { .icon {
font-size: 24rpx; font-size: 24rpx;
padding-right: 4rpx; padding-right: 4rpx;
} }
} }
.visits { .visits {
.icon { .icon {
font-size: 28rpx; font-size: 28rpx;
} }
.number {
padding: 0 6rpx;
font-size: 24rpx;
}
}
}
}
}
.number {
padding: 0 6rpx;
font-size: 24rpx;
}
}
}
}
}
</style> </style>
+3
View File
@@ -26,6 +26,9 @@ export const DefaultAppConfigs = {
type: "post", type: "post",
list: [] list: []
} }
},
categoryConfig: {
type: "list"
} }
}, },
auditConfig: { auditConfig: {
+3 -8
View File
@@ -1,18 +1,13 @@
import {getAppConfigs} from "@/config/index"; import {getAppConfigs} from "@/config/index";
/** 配置后台管理员token */ /** 配置博客的内容 */
const HaloTokenConfig = Object.freeze({ const UniHaloConfig = Object.freeze({
/** 基础请求域名:你的Halo博客基础域名 */ /** 基础请求域名:你的Halo博客基础域名 */
BASE_API: "", BASE_API: "",
// BASE_API: "https://blog.xiaoxiaomo.cn", // BASE_API: "https://blog.xiaoxiaomo.cn",
/** 管理员token */
systemToken: getAppConfigs()?.basicConfig?.tokenConfig?.personalToken, systemToken: getAppConfigs()?.basicConfig?.tokenConfig?.personalToken,
/** 匿名用户token */
anonymousToken: ``
}) })
export default UniHaloConfig;
export default HaloTokenConfig;
+251 -39
View File
@@ -1,36 +1,69 @@
<template> <template>
<view class="app-page"> <view class="app-page" :style="{
padding:calcShowType==='list-post'? 0 : '24rpx 0'
}">
<view v-if="loading !== 'success'" class="loading-wrap"> <view v-if="loading !== 'success'" class="loading-wrap">
<tm-skeleton model="listAvatr"></tm-skeleton> <tm-skeleton model="listAvatr"></tm-skeleton>
<tm-skeleton model="listAvatr"></tm-skeleton> <tm-skeleton model="listAvatr"></tm-skeleton>
<tm-skeleton model="listAvatr"></tm-skeleton> <tm-skeleton model="listAvatr"></tm-skeleton>
</view> </view>
<!-- 内容区域 --> <!-- 内容区域 -->
<view v-else class="app-page-content"> <view v-else class="app-page-content" :class="[calcShowType==='list-post'?'list-post':'']">
<view v-if="dataList.length === 0" class="content-empty flex flex-center" style="height: 70vh;"> <view v-if="dataList.length === 0" class="content-empty flex flex-center" style="height: 70vh;">
<!-- 空布局 --> <!-- 空布局 -->
<tm-empty icon="icon-shiliangzhinengduixiang-" label="暂无数据"></tm-empty> <tm-empty icon="icon-shiliangzhinengduixiang-" label="暂无数据"></tm-empty>
</view> </view>
<block v-else> <block v-else>
<tm-translate v-for="(item, index) in dataList" :key="index" <block v-if="calcShowType==='list'">
style="box-sizing: border-box;width: 50%;padding: 0 8rpx;" animation-name="fadeUp" <tm-translate v-for="(item, index) in dataList" :key="index"
:wait="calcAniWait(index)"> style="box-sizing: border-box;width: 50%;padding: 0 8rpx;" animation-name="fadeUp"
<view class="catgory-card" :style="{backgroundImage:`url(${item.spec.cover})`}"> :wait="calcAniWait(index)">
<view class="content" @click="handleToCategory(item)"> <view class="catgory-card" :style="{backgroundImage:`url(${item.spec.cover})`}">
<view style="font-size: 32rpx;color: #ffffff;">{{ item.spec.displayName }}</view> <view class="content" @click="handleToCategory(item)">
<view v-if="!calcAuditModeEnabled" style="font-size: 24rpx;color: #ffffff;margin-top: 6rpx;"> <view style="font-size: 32rpx;color: #ffffff;">{{ item.spec.displayName }}</view>
{{ item.postCount }} 篇文章 <view v-if="!calcAuditModeEnabled"
</view> style="font-size: 24rpx;color: #ffffff;margin-top: 6rpx;">
<view v-else style="font-size: 24rpx;color: #ffffff;margin-top: 6rpx;"> {{ item.postCount }} 篇文章
{{ item.postCount }} 篇内容 </view>
<view v-else style="font-size: 24rpx;color: #ffffff;margin-top: 6rpx;">
{{ item.postCount }} 篇内容
</view>
</view> </view>
</view> </view>
</tm-translate>
<view class="load-text">{{ loadMoreText }}</view>
</block>
<view v-else-if="calcShowType==='list-post'" class="fulled flex" style="min-height:100vh">
<view class="bg-white" :style="{height: '100%'}">
<tm-sliderNav :list="categoryList" bg-color="white" color="light-blue" rang-key="displayName"
@change="fnOnCategoryChange"></tm-sliderNav>
</view> </view>
</tm-translate> <scroll-view class="right-content pt-12 pb-12" :scroll-y="true" :scroll-top="scrollTop"
<tm-flotbutton @click="fnToTopPage" size="m" color="light-blue" :scroll-with-animation="true" :refresher-enabled="true" :refresher-triggered="triggered"
icon="icon-angle-up"></tm-flotbutton> :refresher-threshold="60" refresher-background="#fafafa"
<view class="load-text">{{ loadMoreText }}</view> @refresherrefresh="fnGetPostByCategory(true)" @scrolltolower="fnGetPostByCategory(false)"
@scroll="fnOnScroll" @touchmove.stop @touchstart="fnOnTouchStart" @touchend="fnOnTouchEnd"
@touchcancel="fnOnTouchEnd">
<view v-if="postList.length === 0" class="article-empty flex flex-center">
<tm-empty :size="120" icon="icon-shiliangzhinengduixiang-" label="该分类下暂无文章~"></tm-empty>
</view>
<block v-else>
<block v-for="(post, index) in postList" :key="post.spec.publishTime">
<tm-translate animation-name="fadeUp" :wait="calcAniWait(index)">
<article-min-card :article="post" @on-click="fnToArticleDetail"></article-min-card>
</tm-translate>
</block>
<view class="load-text">{{ loadMoreText }}</view>
</block>
</scroll-view>
</view>
<view class="flot-buttons">
<tm-button @click="fnScrollTop" size="m" :fab="true" theme="light-blue"
icon="icon-angle-up"></tm-button>
<tm-button @click="fnChangeShowType" size="m" :fab="true" theme="light-blue"
:icon="calcShowType==='list'?'icon-align-left':'icon-all'"></tm-button>
</view>
</block> </block>
</view> </view>
@@ -39,20 +72,27 @@
<script> <script>
import tmSkeleton from '@/tm-vuetify/components/tm-skeleton/tm-skeleton.vue'; import tmSkeleton from '@/tm-vuetify/components/tm-skeleton/tm-skeleton.vue';
import tmFlotbutton from '@/tm-vuetify/components/tm-flotbutton/tm-flotbutton.vue'; import tmButton from '@/tm-vuetify/components/tm-button/tm-button.vue';
import tmTranslate from '@/tm-vuetify/components/tm-translate/tm-translate.vue'; import tmTranslate from '@/tm-vuetify/components/tm-translate/tm-translate.vue';
import tmEmpty from '@/tm-vuetify/components/tm-empty/tm-empty.vue'; import tmEmpty from '@/tm-vuetify/components/tm-empty/tm-empty.vue';
import MarkdownConfig from '@/common/markdown/markdown.config.js'; import MarkdownConfig from '@/common/markdown/markdown.config.js';
import mpHtml from '@/components/mp-html/components/mp-html/mp-html.vue'; import mpHtml from '@/components/mp-html/components/mp-html/mp-html.vue';
import tmFlowLayout from '@/tm-vuetify/components/tm-flowLayout/tm-flowLayout.vue';
import tmSliderNav from '@/tm-vuetify/components/tm-sliderNav/tm-sliderNav.vue';
import ArticleMinCard from '@/components/article-min-card/article-min-card.vue';
export default { export default {
components: { components: {
tmSkeleton, tmSkeleton,
tmFlotbutton, tmButton,
tmTranslate, tmTranslate,
tmEmpty, tmEmpty,
mpHtml mpHtml,
tmFlowLayout,
tmSliderNav,
ArticleMinCard
}, },
data() { data() {
return { return {
@@ -65,30 +105,58 @@ export default {
}, },
hasNext: false, hasNext: false,
dataList: [], dataList: [],
categoryList: [],
isLoadMore: false, isLoadMore: false,
loadMoreText: '加载中...' loadMoreText: '加载中...',
currentCategoryConfig: {
type: "list"
},
currentCategoryName: "",
postQueryParams: {
size: 10,
page: 0,
},
postList: [],
triggered: false,
scrollTop: 0,
tempScrollTop: 0,
scrollTimeout: null,
}; };
}, },
computed: { computed: {
haloConfigs() { haloConfigs() {
return this.$tm.vx.getters().getConfigs; return this.$tm.vx.getters().getConfigs;
}, },
calcAuditModeEnabled(){ calcAuditModeEnabled() {
return this.haloConfigs.auditConfig.auditModeEnabled return this.haloConfigs.auditConfig.auditModeEnabled
}, },
mockJson() { mockJson() {
return this.$tm.vx.getters().getMockJson; return this.$tm.vx.getters().getMockJson;
},
categoryConfig() {
return this.haloConfigs.pageConfig.categoryConfig
},
calcShowType() {
return this.currentCategoryConfig.type
} }
}, },
onLoad() { watch: {
this.fnGetData(); categoryConfig: {
deep: true,
immediate: true,
handler(newVal) {
console.log("执行了", newVal)
if (!newVal) return;
this.currentCategoryConfig = newVal
this.handleInitPage()
},
}
}, },
onPullDownRefresh() { onPullDownRefresh() {
this.isLoadMore = false; this.isLoadMore = false;
this.queryParams.page = 0; this.queryParams.page = 0;
this.fnGetData(); this.fnGetData();
}, },
onReachBottom(e) { onReachBottom(e) {
if (this.calcAuditModeEnabled) { if (this.calcAuditModeEnabled) {
uni.showToast({ uni.showToast({
@@ -98,7 +166,12 @@ export default {
return return
} }
if (this.hasNext) { if (this.hasNext) {
this.queryParams.page += 1; if (this.calcShowType === 'list') {
this.queryParams.page += 1;
} else {
this.postQueryParams.page += 1;
}
this.isLoadMore = true; this.isLoadMore = true;
this.fnGetData(); this.fnGetData();
} else { } else {
@@ -109,8 +182,37 @@ export default {
} }
}, },
methods: { methods: {
fnChangeShowType() {
if (this.calcShowType === 'list-post') {
this.currentCategoryConfig.type = 'list'
} else {
this.currentCategoryConfig.type = 'list-post'
}
this.handleInitPage();
},
handleResetInit() {
this.postList = []
this.dataList = []
this.categoryList = []
this.queryParams.page = 0;
this.postQueryParams.page = 0;
this.hasNext = false
this.isLoadMore = false
this.loadMoreText = '加载中...'
this.currentCategoryName = ""
this.triggered = false
this.fnResetSetAniWaitIndex()
},
handleInitPage() {
this.handleResetInit()
if (this.calcShowType === 'list-post') {
this.queryParams.size = 99999
}
this.fnGetData();
},
fnGetData() { fnGetData() {
if (this.calcAuditModeEnabled) { if (this.calcAuditModeEnabled) {
this.categoryConfig.type = "list"
this.dataList = this.mockJson.category.list.map((item) => { this.dataList = this.mockJson.category.list.map((item) => {
return { return {
metadata: { metadata: {
@@ -143,21 +245,37 @@ export default {
.then(res => { .then(res => {
console.log('请求结果:'); console.log('请求结果:');
console.log(res); console.log(res);
if (this.calcShowType === 'list') {
this.loading = 'success';
this.loadMoreText = res.hasNext ? '上拉加载更多' : '呜呜,没有更多数据啦~';
// 处理数据
this.hasNext = res.hasNext;
this.loading = 'success'; const tempItems = res.items.map(item => {
this.loadMoreText = res.hasNext ? '上拉加载更多' : '呜呜,没有更多数据啦~'; item.spec.cover = this.$utils.checkThumbnailUrl(item.spec.cover, true)
// 处理数据 return item;
this.hasNext = res.hasNext; })
const tempItems = res.items.map(item => { if (this.isLoadMore) {
item.spec.cover = this.$utils.checkThumbnailUrl(item.spec.cover, true) this.dataList = this.dataList.concat(tempItems);
return item; } else {
}) this.dataList = tempItems;
}
if (this.isLoadMore) {
this.dataList = this.dataList.concat(tempItems);
} else { } else {
this.dataList = tempItems; this.dataList = res.items
this.categoryList = res.items.map(item => {
return {
displayName: item.spec.displayName,
name: item.metadata.name,
...item,
}
})
this.triggered = false;
this.loading = 'success';
if (this.dataList.length !== 0) {
this.currentCategoryName = this.dataList[0].metadata.name;
this.fnGetPostByCategory()
}
} }
}) })
.catch(err => { .catch(err => {
@@ -185,6 +303,84 @@ export default {
uni.navigateTo({ uni.navigateTo({
url: `/pagesA/category-detail/category-detail?name=${data.metadata.name}&title=${data.spec.displayName}` url: `/pagesA/category-detail/category-detail?name=${data.metadata.name}&title=${data.spec.displayName}`
}) })
},
fnGetPostByCategory(isPulldownRefresh = true, triggered = true) {
if (!isPulldownRefresh) {
if (this.hasNext) {
this.postQueryParams.page += 1;
} else {
return uni.showToast({
icon: 'none',
title: '没有更多数据了'
});
}
} else {
this.postQueryParams.page = 0;
if (triggered) {
this.triggered = true;
}
}
this.$httpApi.v2
.getCategoryPostList(this.currentCategoryName, this.postQueryParams)
.then(res => {
this.hasNext = res.hasNext;
if (!isPulldownRefresh) {
this.postList = this.postList.concat(res.items);
} else {
this.postList = res.items;
}
this.loadMoreText = res.hasNext ? '上拉加载更多' : '呜呜,没有更多数据啦~';
})
.catch(err => {
this.loadMoreText = '加载失败!';
})
.finally(() => {
this.triggered = false;
});
},
fnOnCategoryChange(e) {
this.fnResetSetAniWaitIndex();
this.currentCategoryName = this.dataList[e].metadata.name;
this.fnToTopScroll();
this.postList = [];
this.fnGetPostByCategory();
},
fnOnScroll(e) {
this.tempScrollTop = e.detail.scrollTop;
},
fnToTopScroll() {
uni.pageScrollTo({
scrollTop: 0,
duration: 500
});
this.scrollTop = 0;
this.tempScrollTop = 0;
},
fnOnTouchStart() {
clearTimeout(this.scrollTimeout);
},
fnOnTouchEnd() {
this.scrollTimeout = setTimeout(() => {
this.scrollTop = this.tempScrollTop;
}, 500);
},
//跳转文章详情
fnToArticleDetail(post) {
uni.navigateTo({
url: '/pagesA/article-detail/article-detail?name=' + post.metadata.name,
animationType: 'slide-in-right'
});
},
fnScrollTop() {
if (this.calcShowType === 'list') {
this.fnToTopPage()
} else {
this.fnToTopScroll()
}
} }
} }
}; };
@@ -216,8 +412,14 @@ export default {
flex-wrap: wrap; flex-wrap: wrap;
padding: 0 12rpx; padding: 0 12rpx;
gap: 20rpx 0; gap: 20rpx 0;
&.list-post {
padding: 0;
gap: 0;
}
} }
.catgory-card { .catgory-card {
width: 100%; width: 100%;
height: 200rpx; height: 200rpx;
@@ -259,4 +461,14 @@ export default {
width: 100%; width: 100%;
text-align: center; text-align: center;
} }
.flot-buttons {
position: fixed;
bottom: 100rpx;
right: 32rpx;
flex-direction: column;
display: flex;
gap: 6rpx;
z-index: 999;
}
</style> </style>
+4 -5
View File
@@ -5,7 +5,7 @@
* 时间:2024年06月22日 12:00:44 * 时间:2024年06月22日 12:00:44
* 版本:v0.1.0 * 版本:v0.1.0
*/ */
import {DefaultAppConfigs, getAppConfigs, setAppConfigs,setAppMockJson,getAppMockJson} from '@/config/index.js' import {DefaultAppConfigs, getAppConfigs, getAppMockJson, setAppConfigs, setAppMockJson} from '@/config/index.js'
import v2Config from '@/api/v2/all.config.js' import v2Config from '@/api/v2/all.config.js'
import utils from '@/utils/index.js' import utils from '@/utils/index.js'
@@ -21,7 +21,7 @@ export default {
return getAppConfigs() return getAppConfigs()
}, },
getMockJson(state) { getMockJson(state) {
if(state.mockJson) return state.mockJson; if (state.mockJson) return state.mockJson;
return getAppMockJson() return getAppMockJson()
} }
}, },
@@ -36,12 +36,12 @@ export default {
} }
}, },
actions: { actions: {
fetchConfigs({commit, dispatch}) { fetchConfigs({state, commit, dispatch}) {
return new Promise(async (resolve, reject) => { return new Promise(async (resolve, reject) => {
try { try {
const res = await v2Config.getAppConfigs() const res = await v2Config.getAppConfigs()
if (res) { if (res) {
commit('setConfigs', res) commit('setConfigs', utils.deepMerge(DefaultAppConfigs, res))
resolve(res) resolve(res)
} else { } else {
dispatch("setDefaultAppSettings"); dispatch("setDefaultAppSettings");
@@ -65,7 +65,6 @@ export default {
url: mockJsonUrl, url: mockJsonUrl,
method: "GET", method: "GET",
success: (res) => { success: (res) => {
console.log("mockJson", res.data)
commit('setMockJson', res.data) commit('setMockJson', res.data)
resolve({ resolve({
ok: true, ok: true,
+23
View File
@@ -178,7 +178,30 @@ const utils = {
jsonData: {}, jsonData: {},
} }
} }
},
isObject(obj) {
return obj && typeof obj === 'object' && !Array.isArray(obj);
},
deepMerge(target, source) {
let output = Object.assign({}, target);
if (this.isObject(target) && this.isObject(source)) {
Object.keys(source).forEach(key => {
const targetValue = target[key];
const sourceValue = source[key];
if (Array.isArray(targetValue) && Array.isArray(sourceValue)) {
output[key] = targetValue.concat(sourceValue);
} else if (this.isObject(targetValue) && this.isObject(sourceValue)) {
output[key] = this.deepMerge(targetValue, sourceValue);
} else {
output[key] = sourceValue;
}
});
}
return output;
} }
}; };