第11讲投票创建后端实现

投票创建页面实现

文件选择上传组件 uni-file-picker 扩展组件 安装

https://ext.dcloud.net.cn/plugin?name=uni-file-picker

日期选择器uni-datetime-picker组件 安装

https://ext.dcloud.net.cn/plugin?name=uni-datetime-picker

iconfont小图标

https://www.iconfont.cn/

iconfont.css

c 复制代码
@font-face {
  font-family: 'iconfont';  /* Project id 3888696 */
  src: url('//at.alicdn.com/t/c/font_3888696_rjermxmgmb.woff2?t=1680049466852') format('woff2'),
       url('//at.alicdn.com/t/c/font_3888696_rjermxmgmb.woff?t=1680049466852') format('woff'),
       url('//at.alicdn.com/t/c/font_3888696_rjermxmgmb.ttf?t=1680049466852') format('truetype');
}

.share {
	font-family: iconfont;
	margin-left: 20rpx;
	font-size: 26rpx;
	color: blue;
}

.uploadImg{
	font-family: iconfont;
	font-size: 56rpx;
	color: #acacac;
}

.smallUploadImg{
	font-family: iconfont;
	font-size: 36rpx;
	color: #acacac;
}

.removeOption{
	font-family: iconfont;
	font-size: 38rpx;
	color: red;
	padding-right: 10px;
}

.addOption{
	font-family: iconfont;
	font-size: 38rpx;
	padding-right: 10px;
}

.chooseOption{
	font-family: iconfont;
	font-size: 26rpx;
}

.voteListItem{
	font-family: iconfont;
	font-size: 26rpx;
}

.voteManageItem{
	font-family: iconfont;
	font-size: 46rpx;
	color: blue;
	padding-bottom: 8px;
}

前端代码:

html 复制代码
<template>
	<view class="word_vote">
		<view class="cover_img">
			<view class="title_tip">
				<view class="cover">
					封面图(可以不上传)
				</view>
				<view class="tip">
					( 宽高比:650 × 300 )
				</view>
			</view>
			<view class="upload_img">
				<uni-file-picker 
				@select="selectCoverFileFunc($event)"
				:auto-upload="false" 
				limit="1"
				:del-icon="false" 
				disable-preview 
				file-mediatype="image" 
				:imageStyles="coverImageStyles">
					<view class="upload">
						<text class="uploadImg">&#xe727;</text>
					</view>
				</uni-file-picker>
			</view>
		</view>
		
		<view class="basic_settings">
			<view class="title_tip">
				<view class="title">
					基础设置
				</view>
			</view>
			<view class="settings">
				<view class="title">
					<input type="text"  v-model="title" placeholder="填写投票标题"  placeholder-style="color:#bababa;font-size:16px"/>
				</view>
				<view class="explanation">
					<textarea v-model="explanation" placeholder="投票说明 (非必填)" placeholder-style="color:#bababa;font-size:14px"></textarea>
				</view>
			</view>
		</view>
		
		<view class="vote_options_settings">
			<view class="title_tip">
				<view class="title">
					投票选项设置
				</view>
			</view>
			<view class="option_list">
				<view class="option_item" v-for="(item,index) in options" :key="item.id">
					<text class="removeOption" @click="removeOption(item.id)">&#xe618;</text><input type="text" v-model="item.name" placeholder="输入选项名称" placeholder-style="color:#bababa;font-size:14px">
				</view>
			</view>
			<view class="option_add" @click="addOption()">
				<text class="addOption">&#xe660;</text><text>添加选项</text>
			</view>
		</view>
		
		<view class="vote_rules_settings">
			<view class="title_tip">
				<view class="title">
					投票规则设置
				</view>
			</view>	
			<view class="rule_list">
				<view class="rule_item">
					<text>投票截止时间</text>
					<view >
						<uni-datetime-picker 
							:border="false" 
							:clear-icon="false" 
							v-model="voteEndTime"
							:start="startDate"
							:end="endDate"
							></uni-datetime-picker>
					</view>
				</view>
			</view>
		</view>
	</view>
	
	<view class="vote_btn" >
		<button type="primary" @click="submitVote">发起投票</button>
	</view>
</template>

<script>
	import {getBaseUrl, requestUtil} from "../../utils/requestUtil.js"
	import {isEmpty} from "../../utils/stringUtil.js"
	import {timeFormat} from "../../utils/dateUtil.js"
	export default{
		data(){
			const curDate=new Date();
			const vv=new Date(curDate.getTime()+24*60*60*1000);
			return{
				title:'',
				explanation:'',
				coverImageFileName:'',
				coverImageStyles: {
					width:"700rpx",
					height:"400rpx",
					border:false
				},
				voteEndTime:timeFormat(vv),
				options:[
					{
						id:1,
						name:''
					},
					{
						id:2,
						name:''
					}
				]
			}
		},
		computed:{
			startDate(){
				return new Date();
			},
			endDate(){
				const curDate=new Date();
				const vv=new Date(curDate.getTime()+24*60*60*1000*365);
				return vv;
			}
		},
		methods:{
			addOption:function(){
				var option={
					id:this.options[this.options.length-1].id+1,
					name:''
				}
				this.options.push(option);
			},
			removeOption:function(id){
				const index=this.options.findIndex(v=>v.id===id)
				this.options.splice(index,1);
			},
			selectCoverFileFunc:function(e){
				console.log(e.tempFilePaths[0])
				uni.uploadFile({
					header:{token:uni.getStorageSync("token")},
					url:getBaseUrl()+"/vote/uploadCoverImage",
					filePath:e.tempFilePaths[0],
					name:"coverImage",
					success: (res) => {
						let result=JSON.parse(res.data);
						if(result.code==0){
							this.coverImageFileName=result.coverImageFileName;
						}
					}
				})
			}
		}
	}
</script>

<style lang="scss">
	@import "/common/css/iconfont.css";
	.word_vote{
		padding: 20px;
		padding-bottom: 70px;
		.cover_img{
			.title_tip{
				margin-left: 10rpx;
				font-size: 26rpx;
				color: gray;
				display: flex;
				justify-content: space-between;
			}
			.upload_img{
				border-radius: 5px;
				margin-top: 20rpx;
				width:100%;
				height: 360rpx;
				background-color: white;
				display: flex;
				align-items: center;
				justify-content: center;
				.upload{
					margin: 10rpx;
					background-color: #f4f5f7;
					width:90%;
					height: 80%;
					display: flex;
					align-items: center;
					justify-content: center;
				}
			}
		}
		
		.basic_settings{
			margin-top: 20px;
			.title_tip{
				margin-left: 10rpx;
				font-size: 26rpx;
				color: gray;
				margin-bottom: 10px;
				.title{
					
				}
			}
			.settings{
				
				border-radius: 5px;
				background-color: white;
				.title{
					padding: 10px;
					input{
						font-size: 1.3rem;
						border-bottom: 1px solid #e4e4e4;
						padding-bottom: 15px;
					}
				}
				.explanation{
					padding: 10px;
					textarea{
						height: 100px;
					}
				}
			}
			
		}
		
		.vote_options_settings{
			margin-top: 20px;
			.title_tip{
				margin-left: 10rpx;
				font-size: 26rpx;
				color: gray;
				margin-bottom: 10px;
				.title{
					
				}
			}
			.option_list{
				.option_item{
					margin-top: 10px;
					border-radius: 5px;
					background-color: white;
					padding: 10px;
					display: flex;
					
				}
			}
			.option_add{
				margin-top: 10px;
				border-radius: 5px;
				background-color: white;
				padding: 10px;
				display: flex;
				color:blue;
				font-size:14px
			}
		}
		
		.vote_rules_settings{
			margin-top: 20px;
			.title_tip{
				margin-left: 10rpx;
				font-size: 26rpx;
				color: gray;
				margin-bottom: 10px;
				.title{
					
				}
			}
			.rule_list{
				border-radius: 5px;
				background-color: white;
				.rule_item{
					display: flex;
					justify-content: space-between;
					padding: 12px;
					border-bottom: 1px solid #e4e4e4;
					font-size: 28rpx;
					align-items: center;
					height: 45rpx;
				}
			}
		}
		
	}
	
	.vote_btn{
		height: 120rpx;
		width: 100%;
		background-color: white;
		position: fixed;
		bottom: 0;
		border-top: 1px solid #e4e4e4;
		button{
			margin: 10px;
		}
	}
</style>

后端:

bash 复制代码
coverImagesFilePath: D://uniapp/coverImgs/

封面上传:

bash 复制代码
/**
 * 上传封面图片
 * @param coverImage
 * @return
 * @throws Exception
 */
@RequestMapping("/uploadCoverImage")
public Map<String,Object> uploadCoverImage(MultipartFile coverImage)throws Exception{
    System.out.println("filename:"+coverImage.getName());
    Map<String,Object> resultMap=new HashMap<>();
    if(!coverImage.isEmpty()){
        // 获取文件名
        String originalFilename = coverImage.getOriginalFilename();
        String suffixName=originalFilename.substring(originalFilename.lastIndexOf("."));
        String newFileName= DateUtil.getCurrentDateStr()+suffixName;
        FileUtils.copyInputStreamToFile(coverImage.getInputStream(),new File(coverImagesFilePath+newFileName));
        resultMap.put("code",0);
        resultMap.put("msg","上传成功");
        resultMap.put("coverImageFileName",newFileName);
    }
    return resultMap;
}

vote

bash 复制代码
package com.java1234.entity;

import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableName;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import lombok.Data;

import java.util.Date;
import java.util.List;

/**
 * 投票实体
 * @author java1234_小锋 (公众号:java1234)
 * @site www.java1234.vip
 * @company 南通小锋网络科技有限公司
 */
@TableName("t_vote")
@Data
public class Vote {

    private Integer id; // 编号

    private String title; // 标题

    private String explanation; // 投票说明

    private String coverImage; // 封面图片

    @JsonSerialize(using=CustomDateTimeSerializer.class)
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
    private Date voteEndTime; // 投票结束时间

    private String openid; // 投票发起人openid

    @TableField(select=false,exist = false)
    private List<VoteItem> voteItemList;

    @TableField(select=false,exist = false)
    private WxUserInfo wxUserInfo;

    private Integer type=1; // 1 文字投票  2 图片投票

}

VoteMapper

bash 复制代码
package com.java1234.mapper;

import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.java1234.entity.Vote;

/**
 * 投票Mapper接口
 */
public interface VoteMapper extends BaseMapper<Vote> {
}

VoteItemMapper

bash 复制代码
package com.java1234.mapper;

import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.java1234.entity.VoteItem;

/**
 * 投票选项Mapper接口
 */
public interface VoteItemMapper extends BaseMapper<VoteItem> {
}

IVoteItemService

bash 复制代码
package com.java1234.service;

import com.baomidou.mybatisplus.extension.service.IService;
import com.java1234.entity.VoteItem;

/**
 * 投票选项Service接口
 */
public interface IVoteItemService extends IService<VoteItem> {
}

IVoteService

bash 复制代码
package com.java1234.service;

import com.baomidou.mybatisplus.extension.service.IService;
import com.java1234.entity.Vote;

/**
 * 投票Service接口
 */
public interface IVoteService extends IService<Vote> {
}

IVoteItemServiceImpl

bash 复制代码
package com.java1234.service.impl;

import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.java1234.entity.VoteItem;
import com.java1234.mapper.VoteItemMapper;
import com.java1234.service.IVoteItemService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;


/**
 * 投票选项Service实现类
 * @author java1234_小锋 (公众号:java1234)
 * @site www.java1234.vip
 * @company 南通小锋网络科技有限公司
 */
@Service("voteItemService")
public class IVoteItemServiceImpl extends ServiceImpl<VoteItemMapper,VoteItem> implements IVoteItemService {

    @Autowired
    private VoteItemMapper voteItemMapper;

}

IVoteServiceImpl

bash 复制代码
package com.java1234.service.impl;

import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.java1234.entity.Vote;
import com.java1234.mapper.VoteMapper;
import com.java1234.service.IVoteService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

/**
 * 投票Service实现类
 * @author java1234_小锋 (公众号:java1234)
 * @site www.java1234.vip
 * @company 南通小锋网络科技有限公司
 */
@Service("voteService")
public class IVoteServiceImpl extends ServiceImpl<VoteMapper, Vote> implements IVoteService {

    @Autowired
    private VoteMapper voteMapper;
}

VoteController

bash 复制代码
package com.java1234.controller;

import com.java1234.entity.R;
import com.java1234.entity.Vote;
import com.java1234.entity.VoteItem;
import com.java1234.service.IVoteItemService;
import com.java1234.service.IVoteService;
import com.java1234.util.DateUtil;
import com.java1234.util.JwtUtils;
import io.jsonwebtoken.Claims;
import org.apache.commons.io.FileUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestHeader;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;

import java.io.File;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * 投票Controller控制器
 * @author java1234_小锋 (公众号:java1234)
 * @site www.java1234.vip
 * @company 南通小锋网络科技有限公司
 */
@RestController
@RequestMapping("/vote")
public class VoteController {

    @Value("${coverImagesFilePath}")
    private String coverImagesFilePath;

    @Autowired
    private IVoteService voteService;

    @Autowired
    private IVoteItemService voteItemService;

    /**
     * 上传封面图片
     * @param coverImage
     * @return
     * @throws Exception
     */
    @RequestMapping("/uploadCoverImage")
    public Map<String,Object> uploadCoverImage(MultipartFile coverImage)throws Exception{
        System.out.println("filename:"+coverImage.getName());
        Map<String,Object> resultMap=new HashMap<>();
        if(!coverImage.isEmpty()){
            // 获取文件名
            String originalFilename = coverImage.getOriginalFilename();
            String suffixName=originalFilename.substring(originalFilename.lastIndexOf("."));
            String newFileName= DateUtil.getCurrentDateStr()+suffixName;
            FileUtils.copyInputStreamToFile(coverImage.getInputStream(),new File(coverImagesFilePath+newFileName));
            resultMap.put("code",0);
            resultMap.put("msg","上传成功");
            resultMap.put("coverImageFileName",newFileName);
        }
        return resultMap;
    }

    /**
     * 添加投票
     * @param vote
     * @param token
     * @return
     */
    @RequestMapping("/add")
    @Transactional
    public R add(@RequestBody Vote vote, @RequestHeader String token){
        Claims claims = JwtUtils.validateJWT(token).getClaims();
        vote.setOpenid(claims.getId());
        voteService.save(vote);
        List<VoteItem> voteItemList = vote.getVoteItemList();
        for(VoteItem voteItem:voteItemList){
            voteItem.setVoteId(vote.getId());
            voteItem.setNumber(0);
            voteItemService.save(voteItem);
        }
        return R.ok();
    }
}

前端验证以及提交:

javascript 复制代码
submitVote:async function(e){
    // 验证
    if(isEmpty(this.title)){
        uni.showToast({
            icon:"error",
            title:"请填写投票标题"
        })
        return;
    }
    // 投票选项片段 至少2个选项
    let resultOptions=this.options.filter(function(value,index,self){  // 过滤掉名称为空的投票选项
        console.log("value="+value.name)
        return !isEmpty(value.name)
    })
    console.log("xx"+JSON.stringify(resultOptions));
    console.log("length="+resultOptions.length)
    if(resultOptions.length<2){
        uni.showToast({
            icon:"error",
            title:"请至少填写两个投票选项"
        })
        return;
    }
    // 提交表单
    let form={
        title:this.title,
        coverImage:this.coverImageFileName,
        explanation:this.explanation,
        voteEndTime:this.voteEndTime,
        voteItemList:resultOptions,
        type:1
    }
    const result=await requestUtil({url:"/vote/add",data:form,method:"post"});
    if(result.code==0){
        console.log("发布成功")
        uni.showToast({
            icon:"success",
            title:"投票发起成功!"
        })
    }
}

dateUtil.js

bash 复制代码
// 判断传入日期是否和当前日期比较 
 const judgeDate=(toDate)=>{
	return new Date().getTime()-new Date(toDate).getTime();
}


var timeFormat = function (msTime) {
    let time = new Date(msTime);
    let yy = time.getFullYear();
    let MM = time.getMonth() + 1;
    let dd = time.getDate();
    let hh = time.getHours() < 10 ? "0" + time.getHours() : time.getHours();
    let min =
        time.getMinutes() < 10 ? "0" + time.getMinutes() : time.getMinutes();
    let sec =
        time.getSeconds() < 10 ? "0" + time.getSeconds() : time.getSeconds();
    return yy + "-" + MM + "-" + dd + " " + hh + ":" + min + ":" + sec;
}

export {timeFormat,judgeDate}

数据库

bash 复制代码
/*
SQLyog Ultimate v11.33 (64 bit)
MySQL - 5.7.18-log : Database - db_vote3
*********************************************************************
*/


/*!40101 SET NAMES utf8 */;

/*!40101 SET SQL_MODE=''*/;

/*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */;
/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */;
/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */;
/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */;
CREATE DATABASE /*!32312 IF NOT EXISTS*/`db_vote3` /*!40100 DEFAULT CHARACTER SET utf8 */;

USE `db_vote3`;

/*Table structure for table `t_vote` */

DROP TABLE IF EXISTS `t_vote`;

CREATE TABLE `t_vote` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `title` varchar(600) DEFAULT NULL,
  `explanation` varchar(3000) DEFAULT NULL,
  `cover_image` varchar(600) DEFAULT NULL,
  `vote_end_time` datetime DEFAULT NULL,
  `openid` varchar(600) DEFAULT NULL,
  `type` int(11) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8;

/*Data for the table `t_vote` */

insert  into `t_vote`(`id`,`title`,`explanation`,`cover_image`,`vote_end_time`,`openid`,`type`) values (1,'2','2','20230516092542000000157.jpg','2023-05-17 09:25:33','o30ur5JpAsAUyGBkR0uW4IxvahR8',1);

/*Table structure for table `t_vote_item` */

DROP TABLE IF EXISTS `t_vote_item`;

CREATE TABLE `t_vote_item` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `vote_id` int(11) DEFAULT NULL,
  `name` varchar(600) DEFAULT NULL,
  `image` varchar(600) DEFAULT NULL,
  `number` int(11) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8;

/*Data for the table `t_vote_item` */

insert  into `t_vote_item`(`id`,`vote_id`,`name`,`image`,`number`) values (1,1,'1',NULL,0),(2,1,'2',NULL,0);

/*Table structure for table `t_wxuserinfo` */

DROP TABLE IF EXISTS `t_wxuserinfo`;

CREATE TABLE `t_wxuserinfo` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `openid` varchar(90) DEFAULT NULL,
  `nick_name` varchar(150) DEFAULT NULL,
  `avatar_url` varchar(600) DEFAULT NULL,
  `register_date` datetime DEFAULT NULL,
  `last_login_date` datetime DEFAULT NULL,
  `status` char(3) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=20 DEFAULT CHARSET=utf8;

/*Data for the table `t_wxuserinfo` */

insert  into `t_wxuserinfo`(`id`,`openid`,`nick_name`,`avatar_url`,`register_date`,`last_login_date`,`status`) values (7,'o30ur5PiPOr52bBsetXcIV93NL-U','小锋四号@java1234','20230410102248000000487.jpg','2023-04-10 10:21:30','2023-05-04 21:20:38','0'),(9,'o30ur5JpAsAUyGBkR0uW4IxvahR0','微信用户','default.png','2023-04-30 07:42:19','2023-04-30 07:42:19','1'),(10,'1',NULL,NULL,NULL,NULL,'1'),(11,'2',NULL,NULL,NULL,NULL,'1'),(12,'3',NULL,NULL,NULL,NULL,'1'),(13,'4',NULL,NULL,NULL,NULL,'1'),(14,'5',NULL,NULL,NULL,NULL,'1'),(15,'6',NULL,NULL,NULL,NULL,'1'),(16,'7',NULL,NULL,NULL,NULL,'1'),(17,'8',NULL,NULL,NULL,NULL,'1'),(19,'o30ur5JpAsAUyGBkR0uW4IxvahR8','小锋111@java1234','20230514112056000000757.jpeg','2023-05-11 08:44:11','2023-05-16 09:25:31','0');

/*!40101 SET SQL_MODE=@OLD_SQL_MODE */;
/*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */;
/*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */;
/*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */;
相关推荐
计算机-秋大田7 小时前
基于微信小程序的平安驾校预约平台的设计与实现(源码+LW++远程调试+代码讲解等)
java·spring boot·微信小程序·小程序·vue·课程设计
虞书欣的68 小时前
Python小游戏24——小恐龙躲避游戏
开发语言·python·游戏·小程序·pygame
HUODUNYUN10 小时前
小程序免备案
网络·web安全·小程序·1024程序员节
guanpinkeji10 小时前
家政服务小程序,家政行业数字化发展下的优势
小程序·团队开发·软件开发·小程序开发·家政·家政服务小程序
hyswl66611 小时前
租赁回收系统小程序
小程序
DK七七11 小时前
多端校园圈子论坛小程序,多个学校同时代理,校园小程序分展示后台管理源码
开发语言·前端·微信小程序·小程序·php
王大爷~11 小时前
小程序服务商常见问题
小程序
sagima_sdu11 小时前
Py2Neo 库将 Json 文件导入 Neo4J
oracle·json·neo4j
集成显卡13 小时前
axios平替!用浏览器自带的fetch处理AJAX(兼容表单/JSON/文件上传)
前端·ajax·json
晓风伴月14 小时前
uniapp: IOS微信小程序输入框部分被软键盘遮挡问题
微信小程序·小程序·uni-app