Sfoglia il codice sorgente

新增:新增恋爱日记功能

小莫唐尼 3 anni fa
parent
commit
b7e2025ebb

+ 3 - 2
components/article-card/article-card.vue

@@ -157,7 +157,7 @@ export default {
 	}
 	&.tb_image_text {
 		flex-direction: column;
-		padding: 0;
+		padding: 24rpx;
 		.left {
 			width: 100%;
 			height: 340rpx;
@@ -169,7 +169,8 @@ export default {
 		}
 		.right {
 			padding-left: 0;
-			padding: 24rpx;
+			padding: 24rpx 0;
+			padding-bottom: 0;
 			width: 100%;
 			.foot {
 				justify-content: flex-start;

+ 2 - 2
components/cache-image/cache-image.vue

@@ -1,5 +1,5 @@
 <template>
-	<view class="cache-image">
+	<view class="cache-image-wrap">
 		<view v-if="loadStatus == 'loading'" class="img-loading" :style="[imgStyle, loadStyle]">
 			<!-- <text class="img-load-icon iconfont icon-loading"></text>
 			<text class="img-load-text">{{ loadText }}</text> -->
@@ -145,7 +145,7 @@ export default {
 </script>
 
 <style scoped lang="scss">
-.cache-image {
+.cache-image-wrap {
 	width: 100%;
 	height: 100%;
 }

+ 7 - 3
components/category-mini-card/category-mini-card.vue

@@ -1,6 +1,5 @@
 <template>
 	<view class="category-mini-card">
-		<!-- 	<image class="img" lazy-load :src="$utils.checkThumbnailUrl(category.thumbnail)" mode="aspectFill"></image> -->
 		<cache-image
 			class="img"
 			height="120rpx"
@@ -35,12 +34,13 @@ export default {
 	border-radius: 12rpx;
 	background-color: #fff;
 	overflow: hidden;
-	// border: 2rpx solid #f7f7f7;
 	box-shadow: 0rpx 2rpx 24rpx rgba(0, 0, 0, 0.03);
+	position: relative;
 	.img {
 		width: 100%;
 		height: 120rpx;
 		border: 6rpx 6rpx 0 0;
+		overflow: hidden;
 	}
 	.label {
 		position: absolute;
@@ -55,9 +55,13 @@ export default {
 		padding-right: 24rpx;
 	}
 	.name {
+		position: absolute;
+		bottom: 16rpx;
+		left: 0;
+		right: 0;
 		font-size: 24rpx;
 		text-align: center;
-		color: var(--main-text-color);
+		color: #333;
 	}
 }
 </style>

File diff suppressed because it is too large
+ 23 - 0
config/love.config.js


+ 39 - 0
config/love.config.template.js

@@ -0,0 +1,39 @@
+/**
+ * 恋爱日记配置
+ */
+export default {
+	photoKeyName: '情侣相册', // 对应后台的图库分组名称
+	waveImageUrl: 'https://b.925i.cn/uni_halo/uni_halo_about_wave.gif', // 波浪图片地址
+	bgImageUrl: 'https://b.925i.cn/uni_halo_love/love_bg1.png', // 背景图片
+	loveImageUrl: 'https://b.925i.cn/uni_halo_love/like.png', // 爱心图片
+
+	boy: {
+		name: '未知男主',
+		avatar: 'https://b.925i.cn/uni_halo_love/love_boy.png',
+		birthday: '2022-09-25', // 生日
+	},
+	girl: {
+		name: '未知女主',
+		avatar: 'https://b.925i.cn/uni_halo_love/love_girl.png',
+		birthday: '2022-07-25', // 生日
+	},
+	loveStartDate: '2022-05-20 13:14:20', // 恋爱开始时间 
+	timeTitle: '这是我们一起走过的',
+	// 恋爱清单
+	loveList: [{
+			index: 1, // 序号
+			finish: true, // 是否已完成
+			title: '陪对方过生日', // 事件名称
+			desc: '陪对方一起过生日', // 事件描述
+			moment: '很难忘的一次生日,他陪我我陪他反正就是很难忘', // 完成瞬间的记录
+		},
+		{
+			index: 2,
+			finish: false,
+			title: '一起去旅行',
+			desc: '一起来一场说走就走的旅行,希望去的是大理',
+			moment: '',
+		},
+		// 更多...
+	],
+}

+ 21 - 7
package.json

@@ -1,9 +1,23 @@
 {
-  "devDependencies": {
-    "vue-i18n": "^9.1.10"
-  },
-  "dependencies": {
-    "uni-read-pages": "^1.0.5",
-    "uni-simple-router": "^2.0.8-beta.4"
-  }
+	"name": "uni-halo",
+	"version": "1.0.0",
+	"description": "<p align=\"center\">\r     <img alt=\"logo\" src=\"https://b.925i.cn/uni_halo/uni_halo_logo.png\" width=\"120\" height=\"120\" style=\"margin-bottom: 10px;\">\r </p>\r <h3 align=\"center\" style=\"margin: 30px 0 30px;font-weight: bold;font-size:40px;\">uni-halo</h3>\r <h3 align=\"center\">uni-halo 基于Halo一款现代化的开源博客/CMS系统API开发的可多端编译应用</h3>",
+	"repository": {
+		"type": "git",
+		"url": "git+https://gitee.com/ialley-workshop-open/uni-halo.git"
+	},
+	"keywords": ["uni-halo", "小莫唐尼", "巷子工坊"],
+	"author": "巷子工坊丨小莫唐尼",
+	"license": "AGPL-3.0",
+	"bugs": {
+		"url": "https://gitee.com/ialley-workshop-open/uni-halo/issues"
+	},
+	"homepage": "https://uni-halo.925i.cn",
+	"devDependencies": {
+		"vue-i18n": "^9.1.10"
+	},
+	"dependencies": {
+		"uni-read-pages": "^1.0.5",
+		"uni-simple-router": "^2.0.8-beta.4"
+	}
 }

+ 29 - 2
pages.json

@@ -110,8 +110,9 @@
 			}, {
 				"path": "love/love",
 				"style": {
-					"navigationBarTitleText": "",
-					"enablePullDownRefresh": true,
+					"navigationBarTitleText": "恋爱日记",
+					"navigationStyle": "custom",
+					"enablePullDownRefresh": false,
 					"app-plus": {
 						"pullToRefresh": {
 							"color": "#03a9f4",
@@ -119,7 +120,33 @@
 						}
 					}
 				}
+			}, {
+				"path": "love/list",
+				"style": {
+					"navigationBarTitleText": "恋爱清单",
+					"enablePullDownRefresh": false,
+					"navigationStyle": "custom"
+				}
 
+			}, {
+				"path": "love/album",
+				"style": {
+					"navigationBarTitleText": "恋爱相册",
+					"enablePullDownRefresh": true,
+					"app-plus": {
+						"pullToRefresh": {
+							"color": "#03a9f4",
+							"style": "circle"
+						}
+					}
+				}
+			}, {
+				"path": "love/journey",
+				"style": {
+					"navigationBarTitleText": "我们的故事",
+					"navigationStyle": "custom",
+					"enablePullDownRefresh": false
+				}
 			}, {
 				"path": "archives/archives",
 				"style": {

+ 4 - 6
pages/tabbar/gallery/gallery.vue

@@ -78,7 +78,7 @@
 					<block v-for="(item, index) in dataList" :key="index">
 						<tm-translate animation-name="fadeUp" :wait="(index + 1) * 50">
 							<view class="round-3 shadow-2 overflow white mb-24">
-								<tm-images :previmage="false" :src="item.image" @click="fnPreview(item, index)"></tm-images>
+								<tm-images :previmage="false" :src="item.image" @click="fnPreview(item)"></tm-images>
 								<view class="pa-24 text-size-m">
 									<view class="text-overflow-2">
 										<tm-tags :shadow="0" :dense="true" color="bg-gradient-amber-accent" size="s" model="fill">{{ item.team }}</tm-tags>
@@ -281,13 +281,11 @@ export default {
 			console.log('点击数据', data);
 		},
 		// 预览
-		fnPreview(item, index) {
-			if (!index) {
-				index = this.dataList.findIndex(x => x.id == item.id);
-			}
+		fnPreview(item) {
+			const index = this.cache.dataList.findIndex(x => x.id == item.id);
 			uni.previewImage({
 				current: index,
-				urls: this.dataList.map(x => x.image),
+				urls: this.cache.dataList.map(x => x.image),
 				indicator: 'number',
 				loop: true
 			});

+ 317 - 0
pagesA/love/album.vue

@@ -0,0 +1,317 @@
+<template>
+	<view class="app-page">
+		<view v-if="loading != 'success'" class="loading-wrap">
+			<tm-skeleton model="card"></tm-skeleton>
+			<tm-skeleton model="card"></tm-skeleton>
+			<tm-skeleton model="card"></tm-skeleton>
+			<tm-skeleton model="card"></tm-skeleton>
+		</view>
+		<!-- 内容区域 -->
+		<view v-else class="app-page-content">
+			<view v-if="dataList.length == 0" class="content-empty flex flex-center"><tm-empty icon="icon-shiliangzhinengduixiang-" label="相册暂时还没有数据~"></tm-empty></view>
+			<block v-else>
+				<swiper
+					class="swiper-album"
+					:current="swiperIndex"
+					:acceleration="true"
+					:circular="true"
+					:vertical="false"
+					:indicator-dots="false"
+					:autoplay="false"
+					@change="fnOnChange"
+				>
+					<block v-for="(item, index) in dataList" :key="index">
+						<swiper-item class="swiper-album-item">
+							<view class="scroll-wrap">
+								<view class="card">
+									<cache-image
+										class="card-image"
+										width="100%"
+										height="46vh"
+										radius="12rpx"
+										:url="item.image"
+										:fileMd5="item.image"
+										mode="aspectFill"
+										@on-click="fnOnPreview(item)"
+									></cache-image>
+									<view v-if="item.description" class="card-desc">{{ item.description }}</view>
+									<view v-else class="card-desc is-empty flex flex-col">
+										<view class="text-grey-darken-1">该照片没有记录任何信息</view>
+										<view class="text-size-m mt-24 text-grey-darken-1">记录一下拍照的瞬间,会更精彩哟</view>
+									</view>
+								</view>
+							</view>
+						</swiper-item>
+					</block>
+				</swiper>
+				<view class="tabbar">
+					<view class="pre" @click="fnChange(false)">
+						<text class="icon"><text class="iconfont icon-arrow-left"></text></text>
+						<text class="text">上一张</text>
+					</view>
+					<view class="refresh" @click="fnRefresh()">刷新</view>
+					<view class="next" @click="fnChange(true)">
+						<text class="text">下一张</text>
+						<text class="icon"><text class="iconfont icon-arrow-right"></text></text>
+					</view>
+				</view>
+			</block>
+		</view>
+	</view>
+</template>
+
+<script>
+import LoveConfig from '@/config/love.config.js';
+import tmSkeleton from '@/tm-vuetify/components/tm-skeleton/tm-skeleton.vue';
+import tmFlotbutton from '@/tm-vuetify/components/tm-flotbutton/tm-flotbutton.vue';
+import tmTranslate from '@/tm-vuetify/components/tm-translate/tm-translate.vue';
+import tmEmpty from '@/tm-vuetify/components/tm-empty/tm-empty.vue';
+
+export default {
+	components: {
+		tmSkeleton,
+		tmFlotbutton,
+		tmTranslate,
+		tmEmpty
+	},
+	data() {
+		return {
+			loading: 'loading',
+			loveConfig: LoveConfig,
+			queryParams: {
+				size: 99,
+				page: 0,
+				sort: 'takeTime',
+				team: LoveConfig.photoKeyName
+			},
+			result: {},
+			dataList: [],
+			cache: {
+				dataList: [],
+				total: 0
+			},
+			swiperIndex: 0,
+			tabbar: {
+				list: []
+			}
+		};
+	},
+
+	onLoad() {
+		this.fnSetPageTitle('恋爱相册');
+	},
+	created() {
+		this.fnGetData();
+	},
+	onPullDownRefresh() {
+		this.fnRefresh();
+	},
+
+	methods: {
+		fnRefresh() {
+			this.queryParams.page = 0;
+			this.fnGetData();
+		},
+		fnGetData() {
+			uni.showLoading({
+				mask: true,
+				title: '加载中...'
+			});
+
+			this.loading = 'loading';
+			this.$httpApi
+				.getPhotoListByPage(this.queryParams)
+				.then(res => {
+					if (res.status == 200) {
+						this.loading = 'success';
+						if (res.data.content.length != 0) {
+							const _list = res.data.content.map((item, index) => {
+								item['image'] = this.$utils.checkImageUrl(item.thumbnail);
+								item['takeTime'] = this.$tm.dayjs(item['takeTime']).format('DD/MM/YYYY');
+								return item;
+							});
+							this.dataList = _list;
+							// this.fnCacheDataList(_list);
+							// this.dataList = this.dataList.concat(_list);
+
+							this.swiperIndex = 0;
+							uni.hideLoading();
+						}
+					} else {
+						this.loading = 'error';
+						uni.$tm.toast('加载失败,请下拉刷新重试!');
+					}
+				})
+				.catch(err => {
+					console.error(err);
+					this.loading = 'error';
+					uni.$tm.toast('加载失败,请下拉刷新重试!');
+				})
+				.finally(() => {
+					setTimeout(() => {
+						uni.stopPullDownRefresh();
+					}, 200);
+				});
+		},
+		// 缓存数据
+		fnCacheDataList(dataList) {
+			if (this.queryParams.page == 0) {
+				this.cache.dataList = dataList;
+			} else {
+				this.cache.dataList = [...this.cache.dataList, ...dataList];
+			}
+		},
+		fnOnPreview(item) {
+			uni.previewImage({
+				current: item.image,
+				urls: this.dataList.map(x => x.image)
+			});
+		},
+		fnOnChange(e) {
+			console.log('e', e);
+			this.swiperIndex = e.detail.current;
+		},
+		fnChange(isNext) {
+			if (isNext) {
+				if (this.swiperIndex == this.dataList.length - 1) {
+					this.swiperIndex = 0;
+				} else {
+					this.swiperIndex += 1;
+				}
+			} else {
+				if (this.swiperIndex == 0) {
+					this.swiperIndex = this.dataList.length - 1;
+				} else {
+					this.swiperIndex -= 1;
+				}
+			}
+		}
+	}
+};
+</script>
+
+<style lang="scss" scoped>
+.app-page {
+	width: 100vw;
+	min-height: 100vh;
+	display: flex;
+	flex-direction: column;
+	box-sizing: border-box;
+	padding: 24rpx 0;
+	padding-bottom: 144rpx;
+	background: linear-gradient(
+		-135deg,
+		rgba(247, 149, 51, 0.1),
+		rgba(243, 112, 85, 0.1) 15%,
+		rgba(239, 78, 123, 0.1) 30%,
+		rgba(161, 102, 171, 0.1) 44%,
+		rgba(80, 115, 184, 0.1) 58%,
+		rgba(16, 152, 173, 0.1) 72%,
+		rgba(7, 179, 155, 0.1) 86%,
+		rgba(109, 186, 130, 0.1)
+	);
+	color: #55423b;
+}
+.app-page-content {
+}
+.content-empty {
+	width: 100%;
+	height: 60vh;
+}
+.loading-wrap {
+	box-sizing: border-box;
+	padding: 0 24rpx;
+}
+.swiper-album {
+	width: 100vw;
+	height: calc(100vh - 24rpx - 144rpx);
+}
+.swiper-album-item {
+	height: 100%;
+	display: flex;
+	align-items: center;
+	justify-content: center;
+	box-sizing: border-box;
+	padding: 36rpx;
+	/* #ifdef H5 */
+	padding-bottom: 110rpx;
+	/* #endif */
+	/* #ifndef H5 */
+	padding-bottom: 60rpx;
+	/* #endif */
+}
+.scroll-wrap {
+	width: 100%;
+	height: 100%;
+	box-sizing: border-box;
+	padding: 36rpx;
+	background-color: #ffffff;
+	border-radius: 12rpx;
+	box-shadow: 0rpx 4rpx 24rpx rgba(0, 0, 0, 0.03);
+}
+.card {
+	display: flex;
+	flex-direction: column;
+	width: 100%;
+	// height: 65vh;
+	height: 100%;
+	max-height: 100%;
+	border-radius: 12rpx;
+	background-color: #ffffff;
+	box-sizing: border-box;
+	// box-shadow: 0rpx 4rpx 24rpx rgba(0, 0, 0, 0.03);
+	overflow: hidden;
+	overflow-y: auto;
+	::v-deep {
+		.cache-image {
+			height: initial !important;
+		}
+	}
+	&-image {
+		width: 100%;
+		height: initial !important;
+		border-radius: 12rpx;
+	}
+	&-desc {
+		margin-top: 24rpx;
+		line-height: 1.6;
+		font-size: 28rpx;
+		&.is-empty {
+			display: flex;
+			align-items: center;
+			justify-content: center;
+			flex-grow: 1;
+			font-size: 32rpx;
+		}
+	}
+}
+.tabbar {
+	width: 80vw;
+	position: fixed;
+	left: 50%;
+	bottom: 40rpx;
+	transform: translateX(-50%);
+	border-radius: 25rpx;
+	box-sizing: border-box;
+	padding: 24rpx;
+	display: flex;
+	align-items: center;
+	justify-content: space-between;
+	// background-color: rgba(0, 0, 0, 0.5);
+	background-color: #ffffff;
+	color: #333;
+	box-shadow: 0rpx 4rpx 24rpx rgba(0, 0, 0, 0.07);
+	.pre {
+		color: #56bbf9;
+		.text {
+			padding-left: 12rpx;
+		}
+	}
+	.next {
+		color: #f88ca2;
+		.text {
+			padding-right: 12rpx;
+		}
+	}
+}
+</style>

+ 83 - 0
pagesA/love/journey.vue

@@ -0,0 +1,83 @@
+<template>
+	<view class="app-page">
+		<view class="page-title">我们的故事</view>
+		<view class="html-typed" v-html="html">内容渲染</view>
+	</view>
+</template>
+
+<script>
+import LoveConfig from '@/config/love.config.js';
+export default {
+	data() {
+		return {
+			html: '',
+			timer: null
+		};
+	},
+	created() {
+		this.fnInit();
+	},
+	onBackPress() {
+		clearTimeout(this.timer);
+	},
+	methods: {
+		fnInit() {
+			clearTimeout(this.timer);
+			const _html = LoveConfig.story;
+			let _index = 0;
+			const _typing = () => {
+				this.timer = setTimeout(() => {
+					if (_index >= _html.length) {
+						clearTimeout(this.timer);
+					} else {
+						this.html += _html.substring(_index, _index + 1);
+						_index += 1;
+						_typing();
+					}
+				}, 100);
+			};
+			_typing();
+		}
+	}
+};
+</script>
+
+<style scoped lang="scss">
+.app-page {
+	width: 100vw;
+	min-height: 100vh;
+	box-sizing: border-box;
+	padding: 36rpx;
+	/* #ifdef APP-PLUS */
+	padding-top: 100rpx;
+	/* #endif */
+	/* #ifdef H5 */
+	padding-top: 80rpx;
+	/* #endif */
+	/* #ifdef MP-WEIXIN */
+	padding-top: 120rpx;
+	/* #endif */
+	background: linear-gradient(
+		-45deg,
+		rgba(247, 149, 51, 0.1),
+		rgba(243, 112, 85, 0.1) 15%,
+		rgba(239, 78, 123, 0.1) 30%,
+		rgba(161, 102, 171, 0.1) 44%,
+		rgba(80, 115, 184, 0.1) 58%,
+		rgba(16, 152, 173, 0.1) 72%,
+		rgba(7, 179, 155, 0.1) 86%,
+		rgba(109, 186, 130, 0.1)
+	);
+}
+.page-title {
+	font-size: 42rpx;
+	font-weight: bold;
+	text-align: center;
+	text-shadow: 0rpx 4rpx 24rpx #bfe9ef;
+}
+.html-typed {
+	margin-top: 52rpx;
+	line-height: 1.8;
+	font-size: 30rpx;
+}
+</style>

+ 369 - 0
pagesA/love/list.vue

@@ -0,0 +1,369 @@
+<template>
+	<view class="app-page">
+		<view class="love-card">
+			<view class="head">
+				<image class="avatar" :src="loveConfig.boy.avatar" mode="scaleToFill"></image>
+				<view class="love-days">
+					<view class="tip-text">相恋</view>
+					<view class="number">
+						<text class="boy">-</text>
+						<text class="days">{{ calcLoveDays }}</text>
+						<text class="girl">-</text>
+					</view>
+					<view class="tip-text">天</view>
+				</view>
+				<image class="avatar" :src="loveConfig.girl.avatar" mode="scaleToFill"></image>
+			</view>
+			<view class="foot">
+				<view class="text" v-if="false">
+					我们已经相恋
+					<text class="number">- {{ calcLoveDays }} -</text>
+					天啦
+				</view>
+				看看我们的恋爱清单都完成了哪些吧
+			</view>
+		</view>
+		<view v-if="list.length == 0" class="list empty">
+			<view class="card">
+				<image class="empty-image" :src="loveConfig.loveImageUrl" mode="scaleToFill"></image>
+				<view class="empty-text">暂时还没有恋爱清单,快去制定你们的恋爱清单吧~</view>
+			</view>
+		</view>
+		<view v-else class="list">
+			<block v-for="(item, index) in list" :key="index">
+				<view class="card" :style="{ '--delay': calcCardDelay(index) }">
+					<view class="head">
+						<view class="status">
+							<view v-if="!item.finish" class="text">进行中</view>
+							<view v-else class="text finish">已完成</view>
+						</view>
+						<view class="title">
+							<view class="title-name">{{ item.title }}</view>
+							<view class="title-desc">{{ item.desc }}</view>
+						</view>
+						<view class="actions" @click="fnOnItemOpen(item)">
+							<text class="icon">{{ item.open ? '-' : '+' }}</text>
+						</view>
+					</view>
+					<view v-if="item.open" class="body">
+						<view class="desc">
+							<view class="desc-label">开始时间:</view>
+							<view class="desc-value">{{ item.detail.start || '暂无计划' }}</view>
+						</view>
+						<view class="desc" v-if="item.detail.desc">
+							<view class="desc-label">事件描述:</view>
+							<view class="desc-value">{{ item.detail.desc }}</view>
+						</view>
+						<view class="desc">
+							<view class="desc-label">完成时间:</view>
+							<view class="desc-value">{{ item.detail.end || '未开始或正在进行中...' }}</view>
+						</view>
+						<view class="desc">
+							<view class="desc-label">完成打卡:</view>
+							<view class="desc-value">{{ item.detail.moment || '未开始或正在进行中...' }}</view>
+						</view>
+						<view class="desc" v-if="item.detail.other">
+							<view class="desc-label">爱心备注:</view>
+							<view class="desc-value">{{ item.detail.other }}</view>
+						</view>
+					</view>
+				</view>
+			</block>
+		</view>
+	</view>
+</template>
+
+<script>
+import LoveConfig from '@/config/love.config.js';
+export default {
+	data() {
+		return {
+			loveConfig: LoveConfig,
+			list: []
+		};
+	},
+	computed: {
+		calcLoveDays() {
+			const start = new Date(this.loveConfig.loveStartDate),
+				now = new Date();
+			const T = now.getTime() - start.getTime();
+			const i = 24 * 60 * 60 * 1000;
+			const d = T / i;
+			const D = Math.floor(d);
+			return D;
+		},
+		calcCardDelay() {
+			return index => {
+				return Math.random() * index + 1 + 's';
+			};
+		}
+	},
+	created() {
+		this.fnFormatList();
+	},
+	methods: {
+		fnFormatList() {
+			this.list = LoveConfig.loveList.map(item => {
+				item['open'] = false;
+				return item;
+			});
+		},
+		fnOnItemOpen(item) {
+			item.open = !item.open;
+			this.$forceUpdate();
+		}
+	}
+};
+</script>
+
+<style scoped lang="scss">
+.app-page {
+	width: 100vw;
+	min-height: 100vh;
+	box-sizing: border-box;
+	padding: 36rpx;
+	/* #ifdef H5 */
+	padding-top: 60rpx;
+	/* #endif */
+	/* #ifndef H5 */
+	padding-top: 180rpx;
+	/* #endif */
+
+	background: linear-gradient(
+		135deg,
+		rgba(247, 149, 51, 0.1),
+		rgba(243, 112, 85, 0.1) 15%,
+		rgba(239, 78, 123, 0.1) 30%,
+		rgba(161, 102, 171, 0.1) 44%,
+		rgba(80, 115, 184, 0.1) 58%,
+		rgba(16, 152, 173, 0.1) 72%,
+		rgba(7, 179, 155, 0.1) 86%,
+		rgba(109, 186, 130, 0.1)
+	);
+	// background: rgba(247, 246, 242, 1);
+	color: #55423b;
+}
+.love-card {
+	width: 100%;
+	display: flex;
+	flex-direction: column;
+	align-items: center;
+	box-sizing: border-box;
+	padding: 0 24rpx;
+	padding-top: 66rpx;
+	padding-bottom: 52rpx;
+	border-radius: 50rpx;
+	border: 4rpx solid rgba(96, 77, 68, 0.9);
+	border-color: #faf8eb;
+	background-color: rgba(255, 199, 184, 0.9);
+	margin-bottom: 52rpx;
+	box-shadow: 0rpx 4rpx 24rpx rgba(0, 0, 0, 0.1);
+	transition: transform 0.5s ease-in-out;
+	&:hover {
+		transform: translateY(-6rpx);
+	}
+	.head {
+		display: flex;
+		.avatar {
+			width: 150rpx;
+			height: 150rpx;
+			box-sizing: border-box;
+			border-radius: 50%;
+			border: 6rpx solid rgba(255, 255, 255, 0.7);
+
+			&.boy {
+				border-color: #56bbf9;
+			}
+			&.girl {
+				border-color: #f88ca2;
+			}
+		}
+		.love-days {
+			margin: 0 12rpx;
+			display: flex;
+			flex-direction: column;
+			align-items: center;
+			justify-content: center;
+			font-size: 26rpx;
+			.tip-text {
+				color: #333;
+			}
+			.number {
+				font-size: 46rpx;
+				padding: 12rpx 0;
+				> .boy {
+					color: #56bbf9;
+					margin-right: 12rpx;
+				}
+				> .girl {
+					color: #f88ca2;
+					margin-left: 12rpx;
+				}
+			}
+			.days {
+				animation: daysAni 6s ease-in-out infinite;
+				font-weight: bold;
+			}
+		}
+	}
+	.foot {
+		display: none;
+		margin-top: 36rpx;
+		font-size: 24rpx;
+	}
+}
+@keyframes daysAni {
+	0% {
+		color: #f88ca2;
+	}
+	50% {
+		color: #56bbf9;
+	}
+	100% {
+		color: #f88ca2;
+	}
+}
+.list {
+	display: flex;
+	flex-direction: column;
+	align-items: center;
+	justify-content: center;
+	box-sizing: border-box;
+}
+.empty {
+	height: calc(100vh - 180rpx - 280rpx - 36rpx);
+	.card {
+		height: 100%;
+		padding: 100rpx;
+		margin-bottom: 0;
+		text-align: center;
+		justify-content: center;
+		color: rgba(96, 77, 68, 0.9);
+	}
+	&-image {
+		width: 300rpx;
+		height: 300rpx;
+	}
+	&-text {
+		margin-top: 36rpx;
+		line-height: 50rpx;
+	}
+}
+.card {
+	width: 100%;
+	display: flex;
+	flex-direction: column;
+	align-items: center;
+	box-sizing: border-box;
+	padding: 24rpx;
+	border-radius: 50rpx;
+	border: 4rpx solid rgba(96, 77, 68, 0.9);
+	border-color: #fff;
+	// background-color: #faf8eb;
+	background-color: #ffffff;
+	margin-bottom: 36rpx;
+	box-shadow: 0rpx 4rpx 24rpx rgba(0, 0, 0, 0.1);
+	transition: transform 0.3s ease-in-out;
+	animation: cardAni 3s ease-in-out infinite;
+	animation-delay: var(--delay);
+	&:hover {
+		transform: translateY(-8rpx);
+	}
+	.head {
+		width: 100%;
+		display: flex;
+		align-items: center;
+		box-sizing: border-box;
+		.status {
+			width: 100rpx;
+			display: flex;
+			.text {
+				width: 100rpx;
+				height: 100rpx;
+				border-radius: 50%;
+				background-color: #ffc6ba;
+				font-size: 24rpx;
+				line-height: 100rpx;
+				text-align: center;
+				&.finish {
+					background-color: #bfe9ef;
+				}
+			}
+		}
+		.title {
+			width: 0;
+			flex-grow: 1;
+			box-sizing: border-box;
+			padding-left: 30rpx;
+			padding-right: 24rpx;
+			&-name {
+				font-weight: bold;
+				font-size: 30rpx;
+				color: #333;
+			}
+			&-desc {
+				margin-top: 8rpx;
+				font-size: 26rpx;
+				color: rgba(96, 77, 68, 1);
+				text-overflow: ellipsis;
+				overflow: hidden;
+				white-space: nowrap;
+			}
+		}
+		.actions {
+			width: 50rpx;
+			.icon {
+				display: inline-block;
+				width: 45rpx;
+				height: 45rpx;
+				background-color: rgba(96, 77, 68, 0.2);
+				border-radius: 50%;
+				text-align: center;
+				line-height: 45rpx;
+				font-size: 32rpx;
+				font-weight: bold;
+			}
+		}
+	}
+
+	.body {
+		margin-top: 24rpx;
+		width: 100%;
+		background-color: #ffffff;
+		// background-color: #faf8eb;
+		background-color: rgba(96, 77, 68, 0.05);
+		border-radius: 24rpx;
+		box-sizing: border-box;
+		padding: 24rpx;
+		padding-bottom: 12rpx;
+		font-size: 26rpx;
+	}
+}
+@keyframes cardAni {
+	0% {
+		transform: translateY(0rpx);
+	}
+
+	50% {
+		transform: translateY(-10rpx);
+	}
+
+	100% {
+		transform: translateY(0rpx);
+	}
+}
+.desc {
+	display: flex;
+	margin-bottom: 12rpx;
+	&-label {
+		color: #333;
+		width: 140rpx;
+		// font-weight: bold;
+	}
+	&-value {
+		width: 0;
+		flex-grow: 1;
+		line-height: 1.5;
+	}
+}
+</style>

+ 350 - 4
pagesA/love/love.vue

@@ -1,22 +1,368 @@
 <template>
-	<view class="app-page bg-white flex flex-center"><text class="text-bg-gradient-red-accent-b">祝有情人👩‍❤‍👨终成眷属</text></view>
+	<view class="app-page bg-white">
+		<!-- 情侣信息 -->
+		<view class="lover-wrap" :style="[loveWrapStyle]">
+			<view class="lover-card">
+				<view class="boy">
+					<image class="avatar" :src="loveConfig.boy.avatar" mode="aspectFit"></image>
+					<view class="name">{{ loveConfig.boy.name }}</view>
+				</view>
+				<image class="like" :src="loveConfig.loveImageUrl" mode="scaleToFill"></image>
+				<view class="girl">
+					<image class="avatar" :src="loveConfig.girl.avatar" mode="aspectFit"></image>
+					<view class="name">{{ loveConfig.girl.name }}</view>
+				</view>
+			</view>
+			<image class="wave-image" :src="loveConfig.waveImageUrl" mode="scaleToFill"></image>
+		</view>
+		<!-- 恋爱记时 -->
+		<view class="love-time-wrap">
+			<view class="title">{{ loveConfig.timeTitle }}</view>
+			<view class="content">
+				<text class="text">
+					第
+					<text class="number">{{ loveDayCount.d }}</text>
+					天
+				</text>
+				<text class="text">
+					<text class="number">{{ loveDayCount.h }}</text>
+					小时
+				</text>
+				<text class="text">
+					<text class="number">{{ loveDayCount.m }}</text>
+					分钟
+				</text>
+				<text class="text">
+					<text class="number">{{ loveDayCount.s }}</text>
+					秒
+				</text>
+			</view>
+		</view>
+		<!-- 功能导航 -->
+		<view class="list-wrap">
+			<view class="list-item" @click="fnToPage('journey')">
+				<view class="left"><image class="icon" src="https://b.925i.cn/uni_halo_love/diandian.png" mode="aspectFit"></image></view>
+				<view class="right">
+					<view class="name">关于我们</view>
+					<view class="desc">我们一起度过的那些经历</view>
+				</view>
+			</view>
+			<view class="list-item" @click="fnToPage('album')">
+				<view class="left"><image class="icon" src="https://b.925i.cn/uni_halo_love/diandian.png" mode="aspectFit"></image></view>
+				<view class="right">
+					<view class="name">恋爱相册</view>
+					<view class="desc">定格了我们的那些小美好</view>
+				</view>
+			</view>
+			<view class="list-item" @click="fnToPage('list')">
+				<view class="left"><image class="icon" src="https://b.925i.cn/uni_halo_love/diandian.png" mode="aspectFit"></image></view>
+				<view class="right">
+					<view class="name">恋爱清单</view>
+					<view class="desc">你我之间的约定我们都在努力实现</view>
+				</view>
+			</view>
+		</view>
+	</view>
 </template>
 
 <script>
+import LoveConfig from '@/config/love.config.js';
 export default {
 	data() {
-		return {};
+		return {
+			loveConfig: LoveConfig,
+			loveDayTimer: null,
+			loveDayCount: {
+				d: 0,
+				h: 0,
+				m: 0,
+				s: 0
+			}
+		};
+	},
+	computed: {
+		loveWrapStyle() {
+			return {
+				backgroundImage: `url(${this.loveConfig.bgImageUrl})`
+			};
+		}
 	},
 	onLoad() {
 		this.fnSetPageTitle('恋爱日记');
+		this.fnInitLoveDayCount();
 	},
-	methods: {}
+	methods: {
+		fnInitLoveDayCount() {
+			clearTimeout(this.loveDayTimer);
+			const _countDownFn = () => {
+				this.loveDayTimer = setTimeout(_countDownFn, 1000);
+				const start = new Date(this.loveConfig.loveStartDate),
+					now = new Date();
+				const T = now.getTime() - start.getTime();
+				const i = 24 * 60 * 60 * 1000;
+				const d = T / i;
+				const D = Math.floor(d);
+				const h = (d - D) * 24;
+				const H = Math.floor(h);
+				const m = (h - H) * 60;
+				const M = Math.floor(m);
+				const s = (m - M) * 60;
+				const S = Math.floor(s);
+				this.loveDayCount = {
+					d: D,
+					h: H,
+					m: M,
+					s: S
+				};
+			};
+			_countDownFn();
+		},
+		fnToPage(pageName) {
+			uni.navigateTo({
+				url: `/pagesA/love/${pageName}`
+			});
+		}
+	}
 };
 </script>
 
 <style scoped lang="scss">
 .app-page {
 	width: 100vw;
-	height: 100vh;
+	min-height: 100vh;
+	background: linear-gradient(
+		-45deg,
+		rgba(247, 149, 51, 0.1),
+		rgba(243, 112, 85, 0.1) 15%,
+		rgba(239, 78, 123, 0.1) 30%,
+		rgba(161, 102, 171, 0.1) 44%,
+		rgba(80, 115, 184, 0.1) 58%,
+		rgba(16, 152, 173, 0.1) 72%,
+		rgba(7, 179, 155, 0.1) 86%,
+		rgba(109, 186, 130, 0.1)
+	);
+}
+
+.lover-wrap {
+	position: relative;
+	width: 100vw;
+	height: 50vh;
+	display: flex;
+	align-items: center;
+	justify-content: center;
+	background-size: cover;
+	background-repeat: no-repeat;
+	background-position: 50% 50%;
+	&::before {
+		position: absolute;
+		left: 0;
+		top: 0;
+		right: 0;
+		bottom: 0;
+		content: '';
+		background-color: rgba(0, 0, 0, 0.07);
+		background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAQAAAAECAYAAACp8Z5+AAAABmJLR0QA/wD/AP+gvaeTAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAKUlEQVQImU3IMREAIAgAwJfNkQCEsH8cijjpMf6vnXlQaIiJFx+omEBfmqIEZLe2jzcAAAAASUVORK5CYII=);
+		z-index: 0;
+		backdrop-filter: blur(4rpx);
+		overflow: hidden;
+	}
+	&::after {
+		content: '';
+		position: absolute;
+		left: 0;
+		bottom: -60rpx;
+		width: 100vw;
+		height: 60rpx;
+		background-image: linear-gradient(to bottom, rgba(255, 255, 255, 1), rgba(255, 255, 255, 0));
+	}
+	.lover-card {
+		position: absolute;
+		left: 50%;
+		top: 58%;
+		transform: translate(-50%, -50%);
+		width: 90vw;
+		display: flex;
+		align-items: center;
+		justify-content: space-around;
+		border-radius: 12rpx;
+		z-index: 2;
+		.avatar {
+			width: 180rpx;
+			height: 180rpx;
+			border-radius: 50%;
+			box-sizing: border-box;
+			// border: 8rpx solid transparent;
+			// border: 8rpx solid rgba(255, 255, 255, 0.7) !important;
+			border: 8rpx solid rgba(255, 255, 255, 1) !important;
+		}
+		.name {
+			font-size: 32rpx;
+			font-weight: bold;
+			color: #ffffff;
+			text-align: center;
+			letter-spacing: 2rpx;
+		}
+		.boy {
+			color: #3ab8e4;
+			.avatar {
+				border-color: rgba(58, 184, 228, 0.7);
+			}
+		}
+		.girl {
+			color: #f57ab3;
+			.avatar {
+				border-color: rgba(245, 122, 179, 0.7);
+			}
+		}
+
+		.like {
+			width: 120rpx;
+			height: 120rpx;
+			animation: likeani 1s ease-in-out infinite;
+		}
+	}
+	.wave-image {
+		width: 100%;
+		height: 120rpx;
+		position: absolute;
+		left: 0;
+		bottom: 0;
+		mix-blend-mode: screen;
+	}
+}
+
+.love-time-wrap {
+	margin-top: 80rpx;
+	width: 100vw;
+	display: flex;
+	flex-direction: column;
+	align-items: center;
+	justify-content: center;
+
+	.title {
+		font-size: 46rpx;
+		letter-spacing: 4rpx;
+		// background-image: linear-gradient(270deg, #ff4500, #ffa500, #ffd700, #90ee90, #00ffff, #1e90ff, #9370db, #ff69b4, #ff4500);
+		// -webkit-background-clip: text;
+		// color: #000;
+		color: #333;
+		font-size: 42rpx;
+		font-weight: bold;
+		// animation: loveTimeTitleAni 80s linear infinite;
+	}
+	.content {
+		margin-top: 24rpx;
+		display: flex;
+		align-items: center;
+		justify-content: center;
+
+		.text {
+			font-size: 28rpx;
+		}
+		.number {
+			margin: 0 8rpx;
+			font-size: 46rpx;
+			// color: #ff69b4;
+			color: #f83856;
+			font-weight: bold;
+		}
+	}
+}
+
+.list-wrap {
+	margin-top: 75rpx;
+	display: flex;
+	flex-direction: column;
+	align-items: center;
+	justify-content: center;
+	box-sizing: border-box;
+	padding: 0 36rpx;
+
+	.list-item {
+		width: 100%;
+		display: flex;
+		align-items: center;
+		justify-content: space-around;
+		box-sizing: border-box;
+		padding: 28rpx 32rpx;
+		background-color: #ffffff;
+		border-radius: 50rpx;
+		margin-bottom: 32rpx;
+		box-shadow: 0rpx 4rpx 24rpx rgba(0, 0, 0, 0.03);
+
+		&:nth-child(1) {
+			animation: listItemAni1 3s ease-in-out infinite;
+		}
+		&:nth-child(2) {
+			animation: listItemAni1 3s ease-in-out infinite;
+			animation-delay: 1.5s;
+		}
+		&:nth-child(3) {
+			animation: listItemAni1 3s ease-in-out infinite;
+			animation-delay: 2s;
+		}
+		.left {
+			width: 120rpx;
+			height: 120rpx;
+			.icon {
+				width: 100%;
+				height: 100%;
+			}
+		}
+		.right {
+			flex-grow: 1;
+			display: flex;
+			flex-direction: column;
+			justify-content: center;
+			box-sizing: border-box;
+			padding-left: 40rpx;
+			.name {
+				font-size: 32rpx;
+				font-weight: bold;
+				color: #333333;
+			}
+			.desc {
+				margin-top: 8px;
+				font-size: 26rpx;
+				color: #777777;
+			}
+		}
+	}
+}
+
+@keyframes likeani {
+	0% {
+		transform: scale(1);
+	}
+	25% {
+		transform: scale(1.2);
+	}
+	50% {
+		transform: scale(1.1);
+	}
+	75% {
+		transform: scale(1.3);
+	}
+	100% {
+		transform: scale(1);
+	}
+}
+@keyframes loveTimeTitleAni {
+	to {
+		background-position: -200rem;
+	}
+}
+@keyframes listItemAni1 {
+	0% {
+		transform: translateY(0rpx);
+	}
+
+	50% {
+		transform: translateY(-10rpx);
+	}
+
+	100% {
+		transform: translateY(0rpx);
+	}
 }
 </style>

Some files were not shown because too many files changed in this diff