基于JAVA SpringBoot和UniAPP的宠物服务预约小程序

随着社会的发展和人们生活水平的提高,特别是近年来,宠物快速进入人们的家中,成为人们生活中重要的娱乐内容之一,过去宠物只是贵族的娱乐,至今宠物在中国作为一种生活方式得到了广泛的认可,随着人们精神文明的提高,越来越多的宠物。这个和我们成了好朋友。但我们该如何与他们相处和保护他们?毕竟,他们和人类很不一样,我们应该给他们什么?我们应该给他们吃什么?大部分人不知道。他们只是知道要像照顾孩子一样照顾他们。但他们不说话。甚至有些人不把他们当作孩子来照顾!因此,有关宠物服务的信息成为了重要的话题。宠物是我们友好的朋友,是人类幸福健康的重要源泉。

养宠物可以让人们的生活更加丰富多彩和幸福,同时和宠物一起运动对身体非常好,可以有效降低体内的血压和血脂,在当今竞争日益激烈的社会,宠物可以提供良好的精神支持。尤其是在我们受挫的时候。通过与宠物的沟通,我们可以调节情绪,管理心理健康,缓解生活中遇到的各种压力,提高工作效率,另一方面,饲养可爱的宠物可以培养孩子的责任感、爱心和社交能力,当我们的孩子和狗玩得开心的时候,狗可以传递很多正能量,最重要的是,养宠物可以保护我们家庭的财产,保护我们的家庭免受盗窃,让我们整个家庭都有安全感,宠物也可以给我们和后代带来无限的快乐,成为快乐和幸福的源泉,生活。帮助人们积极外出促进体育锻炼,为我们提供了很多与他人交流的机会。现在宠物主人的数量正在迅速增加,越来越多的宠物信息网站应运而生。

实现的功能:

本系统的功能应该包括:用户登录和注册、首页展示、上门服务、宠物社区、后台管理(PC浏览器后台)等功能。

注册、登录:通过获取用户微信昵称,微信账号进行登录。系统允许匿名访问,匿名访问只能浏览界面,不能预约服务;

首页展示:首页主要有养宠宝典可进行信息浏览,收藏。宠物领养功能和宠物寄养功能,可进行详细咨询;

上门服务:为顾客的爱宠提供,提供上门喂养,遛宠服务,专业洗护,基础医疗咨询等4个模块的功能;

宠物社区:主要是可上传宠物照片,分享宠物趣事等;

后台管理:支持管理员对服务预约管理对订单进行添加,删除,修改。宠物社区管理对用户发布的趣事,对违规的信息进行删除。

用到的技术:

后端:java语言的SpringBoot框架、MySql数据库、Maven依赖管理等;

前端:Vue.js语法的UniApp框架。

部分代码展示

<template>
	<view class="wrap">
		<u-navbar :is-back="false" :title="title" title-color="#000">
			<!-- <view class="slot-wrap" @tap="navTo('/pages/pet/city')">	
				<i class="iconfont iconicon-test" :class="'text-' + themeColor.name"/> 
				<view class="city">{{city.title}} <text class="iconfont iconxiajiantou2" /></view>
			</view> -->
		</u-navbar>

		<view class="swiper-adv" v-if="advs.index_top.length">
			<!-- <u-swiper :list="advs.index_top" name="cover" :current="current" field="content" mode="round" height="300" border-radius="15" @click="navToIndex" ></u-swiper> -->
			
			<uni-swiper-dot :info="advs.index_top" :current="current" field="content" mode="round" :dotsStyles="{ backgroundColor: '#FFF', color: themeColor.color, selectedBackgroundColor: themeColor.color, border: 'none', selectedBorder: 'none' }">
				<swiper class="swiper-box" :current="current" circular autoplay :indicator-dots="false" indicator-color="#FFF" :indicator-active-color="themeColor.color" @animationfinish="animationfinish">
					<swiper-item v-for="(item, index) in advs.index_top" :key="index" @click="navToIndex">
						<u-image :src="item.cover" width="100%" height="300" border-radius="15" mode="aspectFill" /> 
					</swiper-item>
				</swiper>
			</uni-swiper-dot>
		</view>
		
		<!-- <view class="swiper">
			<view class="swiper-box">
				<rf-swipe-dot :info="advs.index_top" mode="dot" :current="current" field="title">
					<swiper @change="handleDotChange" autoplay="true">
						<swiper-item v-for="(item, index) in advs.index_top" @tap="indexTopToDetailPage(item)" :key="index">
							<view class="swiper-item">
								<u-image :src="item.cover" width="100%" height="240" border-radius="20" mode="aspectFill" />
							</view>
						</swiper-item>
					</swiper>
				</rf-swipe-dot>
			</view>
		</view>

		<!-- 公告 -->
		<!-- <rf-swiper-slide v-if="notices.length > 0" :list="notices" class="rf-skeleton" @navTo="navTo('/pages/index/notice/notice')">
			<view slot="header" class="swiper-slide-header">
				<text class="iconfont icongonggao" :class="'text-' + themeColor.name"></text>
			</view>
		</rf-swiper-slide> -->
		
		<!-- 频道 -->
		<swiper :indicator-active-color="themeColor.color" indicator-color="#666" :indicator-dots="false" class="channel-wrap">
			<swiper-item class="channel-list">
				<view class="channel" v-for="(item, index) in channels" :key="index" @tap.stop="navToChannel(index)">
					<view class="icon-wrap">
						<!-- <u-image :src="'/static/pet/icon-' + item.key  + '.png'" width="84" height="84" borderRadius="15" mode="aspectFill" /> -->					<u-icon :name="item.icon" custom-prefix="custom-icon" size="66" :color="themeColor.color" />
					</view>
					<view class="text">{{ item.name}}</view>
				</view>
			</swiper-item>
		</swiper>
		
		<!--列表-->
		<view class="pet-wrap">
			<view class="sticky">
				<view class="sticky-tabs">
					<u-tabs-swiper ref="tabs" :list="tabList" :current="tabsCurrent" @change="tabsChange" :is-scroll="false" bar-height="6" bar-width="40" :active-color="themeColor.color"></u-tabs-swiper>
				</view>
				
			</view>
			
			<view>
				<view class="item" v-for="(item, index) in items" :key="index" @tap="switchTab('/pages/pet/pet-view?id='+item.id)">
					<view class="left"><u-image :src="getPicUrl(item.pic1)" width="100%" height="220" border-radius="20" mode="aspectFill" /></view>
					
					<view class="right">
						<view class="title u-line-2">{{item.title}}</view>
							
						<view class="base" >
							<view class="tag" v-for="(item2,index2) in splitTag(item.tag)" :key="index2">{{item2}}</view>
						</view>
						<!-- 
						<view class="reward">悬赏¥<text class="money">{{item.money}}</text></view> -->
						<view class="summary u-line-2">{{item.content}}</view>
					</view>
					<view class="bottom">
						<view class="datetime">{{item.createTime}}</view>
						<!-- <view class="view">{{item.view}}查看</view>
						<view class="share">{{item.share}}转发</view> -->
					</view>
					<u-line color="info" :hair-line="false" margin="30rpx 0 0 0" />
					
				</view>
				
			</view>
			
			<!-- <swiper :current="swiperCurrent" @transition="swiperTransition" @animationfinish="swiperAnimationfinish" style="height:1150rpx;">
				<swiper-item v-for="(item, tabsIndex) in $mData.types" :key="tabsIndex">
					<pet-index-mescroll-item ref="mescrollItem" :i="tabsIndex" :index="tabsIndex" :params="params" :items="items"></pet-index-mescroll-item>
				</swiper-item>
			</swiper> -->
		</view>
		
		<u-gap height="20" />

		<!--备案-->
		<!--#ifdef H5-->
		<view class="copyright" v-if="config.web_site_icp">
			{{ config.copyright_desc }}
			
			<a href="http://www.beian.miit.gov.cn">{{ config.web_site_icp }}</a>
		</view>
		<!-- #endif -->

		<!--页面加载动画-->
		<rfLoading isFullScreen :active="loading"></rfLoading>
		<!-- <rf-back-top :scrollTop="scrollTop"></rf-back-top> -->
		<!-- <rf-back-home></rf-back-home> -->
	</view>
</template>

<script>
import rfSwipeDot from '@/components/rf-swipe-dot';
import rfSwiperSlide from '@/components/rf-swiper-slide';
import { mapMutations } from 'vuex';
import PetIndexMescrollItem from '../pet/mescroll-item/pet-index-mescroll-item';
// import { indexList } from '@/api/product';
// import { petIndex, petList } from '@/api/pet';
import appRequest from "@/common/appRequestUrl.js";
// const QQMapWX = require('@/common/qqmap-wx-jssdk.min.js');

export default {
	components: {
		rfSwipeDot,
		rfSwiperSlide,
		PetIndexMescrollItem,
	},
	data() {
		return {
			userInfo:{},
			items:[],
			$mData: this.$mData,
			appName: this.$mSettingConfig.appName,
			path: '/pages/index/index',
			title: this.$mSettingConfig.appName + '',
			qqmapsdk: null,
			city: { 
				id: 0,
				title: '全国'
			},
			current: 0, // 轮播图index
			advs: {
				index_top: []
			}, // 广告图
			notices: [],
			channels: [
				{
					key: 'find',
					icon: 'pet',
					name: '领养寄养',
					route: '/pages/pet/list',
					type: 'switchTab'
				},
				{
					key: 'mall',
					icon: 'ask',
					name: '养宠宝典',
					route: '/pages/pet/ask',
					type: 'switchTab'
				},
				{
					key: 'blocklist',
					icon: 'forum',
					name: '分享社区',
					route: '/pages/pet/forum',
					type: 'push'
				},
				
			],
			tabsCurrent: 0, // tabs组件的current值,表示当前活动的tab选项
			swiperCurrent: 0, // swiper组件的current值,表示当前那个swiper-item是活动的
			config: {}, // 配置
			loading: true,
			scrollTop: 0,
			kefuShow: true,
			loadingType: 'more',
			newsBg: this.$mAssetsPath.newsBg,
			errorImage: this.$mAssetsPath.errorImage,
			pages: [1, 1, 1, 1],
			// type: 1,
			// cate: 0,
			moneySymbol: this.moneySymbol,
			genders: ['不限', '男孩', '女孩'],
			ages: ['不限', '幼年', '成年', '老年'],
			sizes: ['不限', '小型', '中型', '大型'],
			hairs: ['不限', '长毛', '短毛', '无毛'],
			states: ['不限', '已免疫', '已绝育', '已驱虫'],
			activeStyle: {
				background: '#FFCE0C',
				color: '#01040A'
			},
			filterShow: false,
			params: {},
			tabList:[{ key: 1, name: '领养', title: '领养', content: 'Ta的故事', tips: '为小可爱找新家' },
		{ key: 3, name: '寄养', title: '拾得', content: '宠物详情', tips: '为小可爱寻找原主人' },{ key: 4, name: '养宠宝典', title: '拾得', content: '宠物详情', tips: '为小可爱寻找原主人' }]
		};
	},
	onLoad() {
		// this.qqmapsdk = new QQMapWX({
		// 	key: this.$mData.maps[0].key
		// });
		
		//this.initData();
		this.getData(0);
		this.advs = {index_top:[
		   {"cover": "/static/tab/banner01.png"},
		   {"cover": "/static/tab/banner02.png"},
		   {"cover": "/static/tab/banner03.png"},
		]}
		this.loading = false;
	},
	onShow() {
		let userInfo = appRequest.getUserInfo();
		if(!userInfo){
			
		}else{
			this.userInfo = userInfo;
		}
		// let city = uni.getStorageSync('city');

		// if (city && this.city.id != city.id) {
		// 	this.city = city;
			
		// 	this.$nextTick(() => {
		// 		for (let tab of this.$mData.types) {
		// 			this.getMescroll(tab.key-1).triggerDownScroll();
		// 		}
		// 	});
		// }
	},
	onPageScroll(e) {
		this.scrollTop = e.scrollTop;
	},
	/* onReachBottom() {
		this.mescroll && this.mescroll.onReachBottom();
	},
	onPageScroll(e) {
		this.mescroll && this.mescroll.onPageScroll(e);
	}, */
	onPullDownRefresh() {
		// this.getIndexList('refresh');
	},
	onShareAppMessage(res) {
		return { 
			title: this.title,
			path: this.path
		};
	},
	computed: {
		statusBar() {
			const e = uni.getSystemInfoSync();
			return e.statusBarHeight;
		},
		bottom() {
			let bottom = 0;
			/*  #ifdef H5  */
			bottom = 90;
			/*  #endif */
			return bottom;
		}
	},
	methods: {
		splitTag(text){
			return text.split(" ");
		},
		getPicUrl(info) {
			if(info){
				if( info.length > 10 ){
					return info;
				}else {
					return appRequest.urlMap.getPicById + info;
				}
			}else {
				return "";
			}
		
		},
		getData(type){
			var _this = this;
			appRequest.request({
				method: "POST",
				url: appRequest.urlMap.findNmArticleList,
				data:{
					validFlag:1,
					type:type
				},
				success: function(res) {
					if (res.data.code == 200) {
						let obj = res.data.data;
						_this.items = obj;
					}else{
						uni.showToast({
							title:"获取失败",
							icon:"none"
						})
					}
				},
				fail: function(res) {
					uni.showToast({
						title:"网络异常",
						icon:"none"
					})
				}
			})
		},
		// 数据初始化
		initData() {
			this.getCity();
			this.getIndexList();
		},
		// 监听轮播图切换
		handleDotChange(e) {
			this.current = e.detail.current;
		},
		// 通用跳转
		navTo(route) {
			this.$mRouter.push({ route });
		},
		// Tabbar跳转
		switchTab(route) {
			uni.navigateTo({
				url:route
			})
			//this.$mRouter.switchTab({ route });
		},
		navToChannel(index) {
			if(index==2){
				uni.navigateTo({
					url:"/pages/pet/public"
				})
			}else{
				index = index == 1 ? 2:index;
				this.swiperCurrent = index;
				this.tabsCurrent = index;
				let jump = index == 2 ? 3 :index;
				this.getData(jump);
			}
		},
		navToIndex() {
			switch (this.current) {
				case 0:
					this.navTo(`/pages/index/notice/notice`);
					break;
				case 1:
					this.switchTab(`/pages/pet/list`);
					break;
				case 2:
					this.switchTab(`/pages/pet/forum`);
					break;
				default:
					break;
			}
		},
		// 跳至广告图指定页面
		indexTopToDetailPage(item) {
			this.$mHelper.handleBannerNavTo(item.jump_type, item.jump_link, item.id);
		},
		// 获取主页数据
		async getIndexList(refresh) {
			// await this.$http.get(`${indexList}`, {}).then(async r => {
			// 	uni.setNavigationBarTitle({ title: this.appName });
			// 	if (refresh === 'refresh') uni.stopPullDownRefresh();
			// 	this.initIndexData(r.data);
			// 	this.loading = false;
			// }).catch(() => {
			// 	this.loading = false;
			// 	if (refresh === 'refresh') uni.stopPullDownRefresh();
			// });
		},
		// 首页参数赋值
		initIndexData(data) {
			this.advs = {index_top:[
			   {"cover": "/static/tab/banner01.png"},
			   {"cover": "/static/tab/banner02.png"},
			   {"cover": "/static/tab/banner03.png"},
			]}
			this.notices = data.announce;
			this.config = data.config;
			this.$mHelper.handleWxH5Share(this.share.share_title || this.appName, this.share.share_desc || `欢迎来到${this.appName}`, this.share.share_link || this.$mConfig.hostUrl, this.share.share_cover || this.$mSettingConfig.appLogo);
		},
		// 跳转至商品详情页
		navToDetailPage(data) {
			const { id } = data;
			if (!id) return;
			this.navTo(`/pages/pet/view?id=${id}`);
		},
		animationfinish(e) {
			this.current = e.detail.current;
		},
		// tabs通知swiper切换
		tabsChange(index) {
			this.swiperCurrent = index;
			this.tabsCurrent = index;
			let jump = index == 2 ? 3 :index;
			this.getData(jump);
		},
		// swiper-item左右移动,通知tabs的滑块跟随移动
		swiperTransition(e) {
			let dx = e.detail.dx;
			this.$refs.tabs.setDx(dx);
		},
		// 由于swiper的内部机制问题,快速切换swiper不会触发dx的连续变化,需要在结束时重置状态
		// swiper滑动结束,分别设置tabs和swiper的状态
		swiperAnimationfinish(e) {
			let tabsCurrent = e.detail.current;
			this.$refs.tabs.setFinishCurrent(tabsCurrent);
			this.swiperCurrent = tabsCurrent;
			this.tabsCurrent = tabsCurrent;
			
			this.cate = tabsCurrent;
		},
		getMescroll(i) {
			let mescrollItems = this.$refs.mescrollItem;
		
			if (mescrollItems) {
				let item = mescrollItems[i];
				if (item) return item.mescroll;
			}
		
			return null;
		},
		getCity() {
			let city = uni.getStorageSync('city');
			
			if (city) {
				this.city = city;
			} else {
				let _this = this;
				// #ifdef MP
				uni.authorize({
					scope: 'scope.userLocation',
					success() {
				// #endif
						uni.getLocation({
							type: 'gcj02',
							geocode: true,
							success(res) {
								_this.qqmapsdk.reverseGeocoder({
									location: {
										latitude: res.latitude,
										longitude: res.longitude
									},
									success(res) {
										let city = {
											id: res.result.ad_info.city_code - 156000000,
											title: res.result.ad_info.city
										}
										uni.setStorageSync('city', city);
										_this.city = city;
										_this.getMescroll(_this.swiperCurrent).triggerDownScroll();
									},
									fail(error) {
										console.error(error);
									}
								});
							},
							fail(error) {
								console.error(error);
							}
						});
				// #ifdef MP
					}
				});
				// #endif
			}
		}
	}
};
</script>

基于SpringBoot和UniAPP宠物服务预约小程序

相关推荐
飞的肖1 分钟前
前端使用 Element Plus架构vue3.0实现图片拖拉拽,后等比压缩,上传到Spring Boot后端
前端·spring boot·架构
Q_19284999063 分钟前
基于Spring Boot的摄影器材租赁回收系统
java·spring boot·后端
ThisIsClark12 分钟前
【后端面试总结】MySQL主从复制逻辑的技术介绍
mysql·面试·职场和发展
gb42152871 小时前
springboot中Jackson库和jsonpath库的区别和联系。
java·spring boot·后端
Python之栈1 小时前
【无标题】
数据库·python·mysql
亦世凡华、2 小时前
MySQL--》如何在MySQL中打造高效优化索引
数据库·经验分享·mysql·索引·性能分析
黄油饼卷咖喱鸡就味增汤拌孜然羊肉炒饭10 小时前
SpringBoot如何实现缓存预热?
java·spring boot·spring·缓存·程序员
White_Mountain12 小时前
在Ubuntu中配置mysql,并允许外部访问数据库
数据库·mysql·ubuntu
老王笔记12 小时前
GTID下复制问题和解决
mysql
AskHarries12 小时前
Spring Cloud OpenFeign快速入门demo
spring boot·后端