unicloud初级入门-文章管理带图片上传

这个和前面发的Unicloud初级入门-新闻小案例,是一体的。只不过这个带图片上传功能,更完整些。

我就不啰嗦了,直接把代码全部贴出来。

项目截图

数据库

uniapp代码

artAddNews

'use strict';
const db = uniCloud.database();
exports.main = async (event, context) => {
	let {news,picUrls}  = event;
	let res = await db.collection("article").add({
		posttime: Date.now(),
		...news,
		picUrls
	});
	return res;
};

artDelete

'use strict';
const db = uniCloud.database();
const dbCmd = db.command;
exports.main = async (event, context) => {
	//解析id
	let {id} = event;
	//根据条件删除
	let res = await db.collection("article").where({
		_id : dbCmd.eq(id)
	}).remove();
	//返回
	return res;
};

artGetAll

'use strict';
const db = uniCloud.database();
exports.main = async (event, context) => {
	//翻页需要过滤的的量,默认首次是0
	let {skip = 0 } = event;
	let res = await db.collection("article")
		.limit(8) //一次取6条
		.skip(skip) //过滤多少条,把前面已经展示的内容过滤掉
		.orderBy("posttime","desc") //按时间倒序排序
		.get(); 
	return res;
};

artGetById

'use strict';
const db = uniCloud.database();
const dbCmd = db.command;
exports.main = async (event, context) => {
	let {id} = event;
	//根据id获取新闻详情
	//let res = await db.collection("article").doc(id).get();
	//根据id获取新闻详情
	let res = await db.collection("article").where({
		_id : dbCmd.eq(id)
	}).get();
	
	return res;
};

artUpdateNews

'use strict';
const db = uniCloud.database();
const dbCmd = db.command;
exports.main = async (event, context) => {
	let {news,picUrls} = event;
	let res = await db.collection("article").where({
		_id: dbCmd.eq(news._id)
	}).update({
		title: news.title,
		author: news.author,
		content: news.content,
		picUrls: picUrls
	});
	
	return res;
};

pages add add.vue

<template>
	<view class="add">
		<form @submit="onsubmit">
			<view class="item">
				<input type="text" name="title" placeholder="请输入标题" v-model="formValue.title" />
			</view>
			<view class="item">
				<input type="text" name="author" placeholder="请输入作者" v-model="formValue.author" />
			</view>
			<view class="item">
				<textarea value="" placeholder="请输入内容" name="content" v-model="formValue.content"/>
			</view>
			<view class="item">
				<uni-file-picker 
					v-model="imageValue" 
					file-mediatype="image"
					mode="grid" 
					:limit="3"
					title="可以上传3张图片"
					file-extname="png,jpg"
					@success="uploadSuccess" 
				/>
			</view>
			<view class="item">
				<button type="default" form-type="reset">重置</button>
				<button type="primary" form-type="submit" :disabled="isDisabled(formValue)">提交</button>
			</view>
		</form>
	</view>
</template>

<script>
	export default {
		data() {
			return {
				imageValue:[],
				formValue:{
					title: '',
					author:'',
					content:''
				},
				picUrls:[]
			}
		},
		methods: {
			//图片上传成功
			uploadSuccess(e){
				console.log(e)
				//this.picUrls.push(...e.tempFilePaths);
			},
			//判断按钮是否禁用
			isDisabled(obj){
				let disabled = Object.keys(obj).some((key,value)=>{
					//此项没有填写值时
					return obj[key] == ''
 				})
				
				return disabled;
			},
			onsubmit(e){
				let obj = e.detail.value;
				this.picUrls = this.imageValue.map(item=>{
					return item.url
				});
				uniCloud.callFunction({
					name:"artAddNews",
					data:{
						news: obj,
						picUrls: this.picUrls
					}
				}).then(res=>{
					console.log(res);
					uni.showToast({
						title:"发布成功",
						icon:"success",
						mask:true,
						duration:1000
					});
					setTimeout(()=>{
						uni.reLaunch({
							url:'/pages/index/index'
						})
					},1200);
				})
			}
		}
	}
</script>

<style lang="scss" scoped>
.add{
	padding: 30rpx;
	.item{
		padding-bottom: 20rpx;
		input,textarea{
			border: 1px solid #eee;
			height: 80rpx;
			padding: 0 20rpx;
		}
		
		textarea{
			height: 200rpx;
			width: 100%;
			box-sizing: border-box;
		}
		
		button{
			margin-bottom: 20rpx;
		}
	}
}
</style>

pages detail detail.vue

<template>
	<view class="detail">
		<view v-if="loading">
			<view class="title">
				{{detailData.title}}
			</view>
			<view class="info">
				<text class="txt">作者:{{detailData.author}}</text>
				<text>时间:</text>
				<uni-dateformat 
					:date="detailData.posttime" 
					format="yyyy年MM月dd hh:mm:ss"
					class="txt"/>
			</view>
			<view class="content">
				<rich-text :nodes="detailData.content"></rich-text>
			</view>
			
			<view class="picUrls" v-if="detailData.picUrls && detailData.picUrls.length">
				<image :src="item" mode="widthFix" v-for="(item,index) in detailData.picUrls" :key="index"></image>
			</view>
			
			<view class="btnGroup">
				<button type="default" size="mini" class="btn" @tap="onUpdate">
					<uni-icons type="compose" size="15"></uni-icons>
					修改
				</button>
				<button type="warn" size="mini" class="btn" @tap="onDelete">
					<uni-icons type="trash" size="15" color="#fff"></uni-icons>
					删除
				</button>
			</view>
		</view>
		<view v-else>
			<uni-load-more status="loading"></uni-load-more>
		</view>
	</view>
</template>

<script>
	export default {
		data() {
			return {
				id:null,
				detailData:{},
				loading: false
			};
		},
		onLoad(options) {
			this.id = options.id;
		},
		onShow() {
			this.getData();
		},
		methods:{
			//
			getData(){
				uniCloud.callFunction({
					name:"artGetById",
					data:{
						id: this.id
					}
				}).then(res=>{
					console.log(res)
					this.detailData = res.result.data[0];
					//加载完成,关闭提示
					this.loading = true;
					//修改导航栏的标题
					uni.setNavigationBarTitle({
						title: this.detailData.title
					});
				}).catch(()=>{
					uni.showToast({
						title:'参数有误',
						icon:'none'
					});
					setTimeout(()=>{
						uni.reLaunch({
							url:'/pages/index/index'
						})
					},800);
					
				})
			},
			//修改
			onUpdate(){
				uni.navigateTo({
					url:'/pages/edit/edit?id=' + this.id
				});
			},
			//调用云函数
			remove(){
				uniCloud.callFunction({
					name:"artDelete",
					data:{
						id: this.id
					}
				}).then(res=>{
					console.log(res);
					//
					uni.showToast({
						title:'删除成功',
						icon:'success',
						mask:true,
						duration:1000
					});
					//等待图标提示完毕,再跳转
					setTimeout(()=>{
						uni.reLaunch({
							url:'/pages/index/index'
						});
					},1500)
				})
			},
			//删除
			onDelete(){
				uni.showModal({
					title:'删除提示',
					content:'您确定要删除此篇文章吗?',
					confirmText:'删除',
					success: (res) => {
						//确认删除
						if(res.confirm){
							this.remove();
						}else{
							//不删除
							uni.showToast({
								title:'已取消删除',
								icon:'none'
							});
						}
					}
				})
			}
		}
	}
</script>

<style lang="scss" scoped>
	.detail{
		padding: 30rpx;
		.title{
			font-size: 50rpx;
			color: #333;
			text-align: justify;
			line-height: 1.4em;
		}
		
		.info{
			font-size: 30rpx;
			color: #666;
			padding: 30rpx 0 60rpx;
			.txt{
				padding-right: 30rpx;
			}
		}
		
		.content{
			font-size: 36rpx;
			line-height: 1.7em;
			color: #333;
		}
		
		.picUrls{
			padding-top: 50rpx;
			image{
				width: 100%;
				display: block;
				margin-bottom: 30rpx;
			}
		}
		
		.btnGroup{
			margin-top: 50rpx;
			padding: 50rpx 0;
			border-top: 1px solid #ccc;
			.btn{
				margin-right: 30rpx;
			}
		}
	}
</style>

pages edit edit.vue

<template>
	<view class="add">
		<form @submit="onsubmit">
			<view class="item">
				<input type="text" name="title" placeholder="请输入标题" v-model="formValue.title" />
			</view>
			<view class="item">
				<input type="text" name="author" placeholder="请输入作者" v-model="formValue.author" />
			</view>
			<view class="item">
				<textarea value="" placeholder="请输入内容" name="content" v-model="formValue.content"/>
			</view>
			<view class="item">
				<uni-file-picker 
					v-model="imageValue" 
					file-mediatype="image"
					mode="grid" 
					:limit="3"
					title="可以上传3张图片"
					file-extname="png,jpg"
					@success="uploadSuccess" 
				/>
			</view>
			<view class="item">
				<button type="default" form-type="reset">重置</button>
				<button type="primary" form-type="submit" :disabled="isDisabled(formValue)">提交</button>
			</view>
		</form>
	</view>
</template>

<script>
	export default {
		data() {
			return {
				id:null,
				formValue:{
					title: '',
					author:'',
					content:''
				},
				imageValue:[],
				picUrls:[]
			}
		},
		onLoad(options) {
			this.id = options.id;
			this.getDetail();
		},
		methods: {
			//图片上传成功
			uploadSuccess(e){
				console.log(e)
				//this.picUrls.push(...e.tempFilePaths);
			},
			getDetail(){
				uniCloud.callFunction({
					name:"artGetById",
					data:{
						id: this.id
					}
				}).then(res=>{
					console.log(res)
					this.formValue = res.result.data[0];
					//如果本就没有图片,不做回显
					if(!this.formValue.picUrls) return;
					this.picUrls = this.formValue.picUrls;
					let urls = this.formValue.picUrls.map(item =>{
						return {
							url: item
						}
					})
					console.log("urls",urls)
					this.imageValue = urls;
				});
			},
			//判断按钮是否禁用
			isDisabled(obj){
				let disabled = Object.keys(obj).some((key,value)=>{
					//此项没有填写值时
					return obj[key] == ''
				})
				
				return disabled;
			},
			onsubmit(){
				//从控件中取,
				this.picUrls = this.imageValue.map(item=>{
					return item.url
				});
				uniCloud.callFunction({
					name:"artUpdateNews",
					data:{
						news: this.formValue,
						picUrls: this.picUrls
					}
				}).then(res=>{
					console.log(res);
					uni.showToast({
						title:"更新成功",
						icon:"success",
						mask:true,
						duration:1000
					});
					setTimeout(()=>{
						uni.navigateBack({
							delta:1
						});
					},1200);
				})
			}
		}
	}
</script>

<style lang="scss" scoped>
.add{
	padding: 30rpx;
	.item{
		padding-bottom: 20rpx;
		input,textarea{
			border: 1px solid #eee;
			height: 80rpx;
			padding: 0 20rpx;
		}
		
		textarea{
			height: 200rpx;
			width: 100%;
			box-sizing: border-box;
		}
		
		button{
			margin-bottom: 20rpx;
		}
	}
}
</style>

pages index index.vue

<template>
	<view class="home">
		<view class="content">
			<view class="item" v-for="item in newsList" :key="item._id" @tap="goDetail(item)">
				<view class="txt">
					<view class="title">
						{{item.title}}
					</view>
					<view class="info">
						<text class="t">{{item.author}}</text>
						<uni-dateformat
							:date="item.posttime"
							:threshold="[60000, 7200000]"
							format="MM-dd">
							</uni-dateformat>
					</view>
				</view>
				<view class="pic">
					<image :src="item.picUrls[0]" mode="aspectFit" v-if="item.picUrls && item.picUrls.length"></image>
					<image src="../../static/logo.png" mode="aspectFit" v-else></image>
				</view>
			</view>
		</view>
		
		<view class="goAdd" @tap="goAdd">
			<uni-icons type="plusempty" size="30" color="#fff"></uni-icons>
		</view>
		
		<view v-if="isEnd">
			<uni-load-more status="noMore"></uni-load-more>
		</view>
	</view>
</template>

<script>
	export default {
		data() {
			return {
				newsList:[],
				isEnd: false
			}
		},
		onLoad() {
			//重置
			this.isEnd = false;
			//获取数据
			this.getData();
		},
		//触底加载
		onReachBottom() {
			this.getData();
		},
		//下拉刷新
		onPullDownRefresh(){
			//重置状态
			this.isEnd = false;
			this.newsList = [];
			this.getData();
		},
		methods: {
			//前往详情页
			goDetail(obj){
				//
				uni.navigateTo({
					url:"/pages/detail/detail?id=" + obj._id
				});
			},
			getData(){
				uniCloud.callFunction({
					name:"artGetAll",
					data:{
						//过滤的量就是,已经获取的数组长度
						skip: this.newsList.length
					}
				}).then(res=>{
					console.log(res)
					//新获取的数据,解析后,追加到数组中
					this.newsList.push(...res.result.data);
					//下拉刷新停止
					uni.stopPullDownRefresh();
					//判断是否到底了
					if(res.result.affectedDocs <= 0){
						this.isEnd = true;
					}
				})
				
			},
			goAdd(){
				uni.navigateTo({
					url:'/pages/add/add'
				});
			}
		}
	}
</script>

<style lang="scss" scoped>
	.home{
		.content{
			padding: 30rpx;
			
			.item{
				display: flex;
				justify-content: space-between;
				padding: 20rpx 0;
				border-bottom: 1rpx solid #eee;
				.txt{
					flex: 1;
					display: flex;
					flex-direction: column;
					justify-content: space-between;
					padding-right: 20rpx;
					.title{
						font-size: 40rpx;
						color: #333;
						text-align: justify;
						overflow: hidden;
						text-overflow: ellipsis;
						display: -webkit-box;
						-webkit-line-clamp: 2;
						-webkit-box-orient: vertical;
					}
					
					.info{
						font-size: 28rpx;
						color: #888;
						.t{
							padding-right: 20rpx;
						}
					}
				}
				
				.pic{
					width: 260rpx;
					height: 160rpx;
					image{
						width: 100%;
						height: 100%;
					}
				}
			}
		}
		
		.goAdd{
			width: 120rpx;
			height: 120rpx;
			background-color: #5B89FE;
			color: #fff;
			display: flex;
			justify-content: center;
			align-items: center;
			border-radius: 50%;
			position: fixed;
			right: 30rpx;
			bottom: 60rpx;
		}
	}
</style>

app.vue

<script>
	export default {
		onLaunch: function() {
			console.log('App Launch')
		},
		onShow: function() {
			console.log('App Show')
		},
		onHide: function() {
			console.log('App Hide')
		}
	}
</script>

<style>
	/*每个页面公共css */
	:root{
		box-sizing: border-box;
	}
</style>

pages.json

{
	"pages": [ //pages数组中第一项表示应用启动页,参考:https://uniapp.dcloud.io/collocation/pages
		{
			"path": "pages/index/index",
			"style": {
				"navigationBarTitleText": "文章列表",
				"enablePullDownRefresh": true
			}
		},
		{
			"path" : "pages/add/add",
			"style" : 
			{
				"navigationBarTitleText" : "新增文章",
				"enablePullDownRefresh" : false
			}
		},
		{
			"path" : "pages/edit/edit",
			"style" : 
			{
				"navigationBarTitleText" : "修改文章",
				"enablePullDownRefresh" : false
			}
		},
		{
			"path" : "pages/detail/detail",
			"style" : 
			{
				"navigationBarTitleText" : "新闻详情",
				"enablePullDownRefresh" : false
			}
		}
	],
	"globalStyle": {
		"navigationBarTextStyle": "white",
		"navigationBarTitleText": "文章管理系统",
		"navigationBarBackgroundColor": "#2B9939",
		"backgroundColor": "#F8F8F8"
	},
	"uniIdRouter": {}
}

图示

首页

 详情页

 修改

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

虾米大王

有你的支持,我会更有动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值