欢迎来到飞鸟慕鱼博客,开始您的技术之旅!
当前位置: 首页知识笔记正文

投票小程序源码 微信,微信小程序投票系统如何制作

墨初 知识笔记 98阅读

欢迎来到我的主页

我是Java方文山一个在分享笔记的博主。

推荐给大家我的专栏《微信小程序开发实战》。

点击这里就可以查看我的主页啦

Java方文山的个人主页

如果感觉还不错的话请给我点赞吧

期待你的加入一起学习一起进步

前言

开会是企业中必不可少的一种沟通方式可以让团队成员之间交流思想协调工作进度解决问题等。但是过于频繁和冗长的会议可能会浪费时间和资源影响工作效率。

在公司中如果每个小事情都要开会那么很容易造成员工的厌烦和不满。这样的会议可能会让人感到无聊、浪费时间甚至失去动力。因此企业应该合理安排会议选择适当的时间和场合避免不必要的会议并确保会议的目的明确、内容简洁、效率高效因此鄙人开发了一个数字化会议OA系统。

这篇文章主讲投票模块的实现其中包括发布投票信息用户选择所属会议的投票标题内容进行投票、投票的数据报表等实现。

一、投票模块页面搭建

【微信小程序】自定义组件布局会议OA其他页面附源码 1.顶部导航栏

我们可以根据自定义组件完成首先在相应的json文件中引用自定义组件。

  usingComponents: {    tabs: /components/tabs/tabs  }

 随后在js文件中定义我们所需要展示的内容。

  tabs: [发起投票, 投票进行中, 已结束投票, 全部投票]

 最后直接在wxml中应用即可。

<tabs tabList{{tabs}} bindtabsItemChangetabsItemChange></tabs>

 效果展示

 2.定义内容切换事件

我们这里与会议管理有些不同我们会议管理是点击不同的菜单显示不同的数据但是这个的内容可就不是数据了而是各不相同的组件所以针对这个事情我们要做一个内容切换事件。

首先在我们的wxml中定义好每个菜单需要展示的内容并写好hidden样式

  <view class{{componentStatus[0] ?  : hidden}}>发起投票</view>  <view class{{componentStatus[1] ?  : hidden}}>投票进行中</view>  <view class{{componentStatus[2] ?  : hidden}}>已结束投票</view>  <view class{{componentStatus[3] ?  : hidden}}>全部投票</view>
.hidden {  display: none;}

在js中定义好属性与事件

 componentStatus: [true,false, false, false]
  tabsItemChange(e) {    let index  e.detail.index;    //全部的组件赋值为false    const lists  [false, false, false, false];    //将所点击的组件赋值为true    lists[index]  true;    this.setData({      componentStatus: lists // 更新 data 中的 componentStatus 属性值    });  }

 效果展示

 温馨提示

因为在写这篇博客之前我就已经完成了功能并且模块中的样式没有进行相应的标记所以这里就放上我投票模块的所有样式,避免大家在进行编写的时候因为样式问题而导致效果出不来的情况。

wxss

/* pages/vote/list/list.wxss */.hidden {  display: none;}.title-view {  background-color: beige;  font-weight: 700;  padding-left: 7px;}.info-title {  padding: 5px 5px 10px 5px;  border-top: 1px solid rgb(129, 129, 127);}.info-text {  height: 100px;  padding: 5px 5px 10px 5px;  border-top: 1px solid rgb(129, 129, 127);}.image {  padding-left: 55px;  display: flex;  align-items: center;}.time {  border-top: 1px solid rgb(129, 129, 127);  padding: 5px 0px 5px 0px;  display: flex;  align-items: center;}.image-container {  padding-left: 60px;}.info-sousuo {  margin-left: 85px;  padding-left: 20px;  border-radius: 25px;  border: 4px solid rgb(214, 214, 98);  width: 250px;}/* pages/meeting/list/list.wxss */.section{  color: #aaa;  display: flex;  justify-content: center;}.list-info {  margin-top: 10px;  color: #aaa;}.list-num {  color: #e40909;  font-weight: 700;}.join {  padding: 0px 0px 0px 10px;  color: #aaa;}.state {  margin: 0px 6px 0px 6px;  border: 1px solid #93b9ff;  color: #93b9ff;}.list-tag {  padding: 3px 0px 10px 0px;  display: flex;  align-items: center;}.list-title {  display: flex;  justify-content: space-between;  color: #333;  font-weight: 900;}.yyy{  display: flex;  align-items: center;}.list-img{  height: 300rpx;  width: 90%;  border-radius: 50rpx;  margin: 5px 5px 5px 20px;}.centered {  display: flex;  /* 设置为弹性容器 */  justify-content: center;  /* 子元素水平方向居中对齐 */}.video-img {  width: 100px;  height: 100px;}.list {  border-bottom: 1px solid #6b6e74;  padding: 10px;}.mobi-text {  font-weight: 700;  padding: 15px;}.mobi-icon {  border-left: 5px solid #e40909;}.mobi-title {  background-color: rgba(158, 158, 142, 0.678);  margin: 10px 0px 10px 0px;}.swiper-item {  height: 300rpx;  width: 100%;  border-radius: 10rpx;}.userinfo {  display: flex;  flex-direction: column;  align-items: center;  color: #aaa;}.userinfo-avatar {  overflow: hidden;  width: 128rpx;  height: 128rpx;  margin: 20rpx;  border-radius: 50%;}.usermotto {  margin-top: 200px;}.filx{  display: flex;  align-items: center;}.container {  padding: 20px;}.modal-container {  position: fixed;  top: 50%;  left: 50%;  transform: translate(-50%, -50%);  background-color: #fff;  width: 80%;  max-height: 80%;  overflow-y: auto;  padding: 20px;}.mask {  position: fixed;  top: 0;  left: 0;  width: 100%;  height: 100%;  background-color: rgba(0, 0, 0, 0.5);}button {  display: block;  margin-top: 10px;}.content {  margin-top: 10px;  border: 1px solid #ccc;  padding: 10px;}.right{  display: flex;  align-items: center;}

二、发起投票 1.初始数据

首先最基本的就是要拿到数据但是这个数据可不是乱拿的我们先来看一下表数据。

我们要拿到state值为5的数据只有该数据才是通过了审核并且还未结束的会议所以能进行投票明确了目标现在开始敲代码吧。

InfoMapper.xml
  <select idvotelist resultMapBaseResultMap  >    select    <include refidBase_Column_List />    from t_oa_meeting_info    where state5  </select>
Controller代码
//创建投票的会议信息    RequestMapping(/vote)    public Object votelist(Info info) {        List<Info> infoList  infoMapper.votelist(info);        Map<Object, Object> data  new HashMap<Object, Object>();        data.put(infoList,infoList);        return ResponseUtil.ok(data);    }

 在config/api.js配置后端接口

// 以下是业务服务器API地址 // 本机开发API地址 var WxApiRoot   // 测试环境部署api地址 // var WxApiRoot   // 线上平台api地址 //var WxApiRoot    module.exports  {   IndexUrl: WxApiRoot  home/index, //首页数据接口   SwiperImgs: WxApiRootswiperImgs, //轮播图   MettingInfos: WxApiRootmeeting/list, //会议信息   VoteInfos:WxApiRoothome/vote,//可发起投票会议信息   OptionInfos:WxApiRoothome/add,//发布投票信息   SelectOption:WxApiRoothome/selectOptions//查看可进行投票信息 };
 JS代码

引入后端接口配置文件 

const api  require(../../config/api.js)

 可发起投票会议信息的ajax

  loadvoteInfos() {    util.request(api.VoteInfos).then(res > {      // console.log(res.data.infoList)      this.setData({        lists: res.data.infoList      })    })  }

 在onLoad函数中调用该方法

WXML代码
<!-- 我创建的投票 --><view class{{componentStatus[0] ?  : hidden}}>  <block wx:for-items{{lists}} wx:for-itemitem wx:keyitem.id>    <view>      <image bindtapshowModal data-id{{item}} classlist-img modescaleToFill src{{item.seatpic !null? item.seatpic : /static/persons/6.png}}></image>    </view>    <view classcentered>      <view classlist data-id{{item.id}}>        <view classyyy>          <view classlist-title><text>{{item.title}}</text></view>          <view classjoin><text classlist-num>{{tools.getNumber(item.canyuze,item.liexize,item.item.zhuchiren)}}</text>人报名</view>        </view>        <view classlist-info><text>{{item.location}}</text>|<text>{{tools.formatDate(item.starttime)}}</text></view>      </view>    </view>  </block></view>

 注意将代码放入所需要展示内容的view中。

效果展示

2.模态框实现发布 

这里我给大家捋一下我的思路当我点击会议信息的时候就会弹出一个模态框模态框的内容有显示该会议的标题、输入投票的标题、新增选项、删除选项、发布投票、取消发布等。我需要将我所点击的会议信息的标题传入模态框中并且可以获取投票标题和选项内容的文本内容并进行保存等我点击发布投票的时候就会将保存的内容传值给后端。

数据库展示

OptionMapper.xml
  <insert idinsertSelective parameterTypecom.zking.minoa.model.Option>    INSERT INTO t_oa_meeting_option    <trim prefix( suffix) suffixOverrides,>      <if testid ! null>        id,      </if>      <if testmeetingId ! null>        meetingId,      </if>      <if testoptionValue ! null>        optionValue,      </if>      <if testoptionText ! null>        optionText,      </if>    </trim>    <trim prefixVALUES ( suffix) suffixOverrides,>      <if testid ! null>        #{id, jdbcTypeINTEGER},      </if>      <if testmeetingId ! null>        #{meetingId, jdbcTypeINTEGER},      </if>      <if testoptionValue ! null>        #{optionValue, jdbcTypeVARCHAR},      </if>      <if testoptionText ! null>        #{optionText, jdbcTypeVARCHAR},      </if>    </trim>  </insert>
Controller代码
//发布投票    RequestMapping(/add)    public Object add(RequestBody Map<String, Object> obj) {        System.out.println(obj);        //获取选项数组的长度来控制新增的次数        int optionsLength  ((List)obj.get(options)).size();        //循环添加投票数据        for (int i  0; i < optionsLength; i) {            //初始化option            Option option  new Option();            List<String> options  (List<String>) obj.get(options);            option.setmeetingId((Integer) obj.get(modalConId));//会议id            String firstOption  options.get(i);            option.setoptionValue((String)obj.get(title));//投票标题            option.setoptionText(firstOption);//选项内容            //调取添加的方法            int insert  optionMapper.insertSelective(option);        }        return ResponseUtil.ok();    }

这里需要注意是一个投票选项就是一条数据所以我们要先拿到前端的选择数组的长度从而进行遍历添加数据同一批请求的投票标题与会议id肯定是一致的所以不用变动。

 在config/api.js配置后端接口

 OptionInfos:WxApiRoothome/add,//发布投票信息
JS代码
//投票标题的监听事件  bindTitle: function (e) {    this.setData({      title: e.detail.value    })  },  // 输入框输入事件保存输入的内容到相应的选项  bindInput(e) {    console.log(e)    const { index }  e.currentTarget.dataset; // 获取当前选项的索引    const { value }  e.detail;    const { options }  this.data;    options[index]  value;    this.setData({      options: options    });  },  // 添加选项  addOption() {    const { options, optionCount }  this.data;    options.push();    this.setData({      options: options,      optionCount: optionCount  1    });  },  // 删除选项  deleteOption() {    const { options, optionCount }  this.data;    options.pop();    this.setData({      options: options,      optionCount: optionCount - 1    });  },   // 确认按钮点击事件  confirm() {    let modalConId  this.data.modalConId;//会议id    let title  this.data.title;//投票标题    let options  this.data.options;//选项内容    let data  {      modalConId: modalConId,      options: options,      title: title    };    // console.log(data)    util.request(api.OptionInfos, data, POST).then(res > {      // console.log(res)      this.setData({        modalVisible: false,        optionCount: 1      });    })  },  // 取消按钮点击事件  cancel() {    this.setData({      modalVisible: false,      optionCount: 1    });  }

 事件中提到的变量在data中定义即可这里就不做展示了

WXML代码
  <!-- 模态框 -->  <view wx:if{{modalVisible}}>    <view classmask></view>    <view classmodal-container>      <h1>{{modalContent}}</h1>      <view stylemargin-top: 20px; classfilx>        <l-button typedefault bindtapaddOption stylepadding: 0px 10px 0px 0px;>添加选项</l-button>        <l-button typewarning bindtapdeleteOption wx:if{{optionCount > 1}}>删除选项</l-button>      </view>        <span stylemargin-right: 30px;>投票标题</span>      <input typetext placeholder请输入投票标题 bindinputbindTitle stylewidth: 300px;border: 3px solid rgb(7, 7, 7); />      <view wx:for{{options}} wx:key{{index}}>        <span stylemargin-right: 30px;>选项{{index1}}</span>        <input typetext value{{item}} bindinputbindInput data-index{{index}} stylewidth: 300px;border: 3px solid rgb(7, 7, 7); />      </view>      <view classfilx stylemargin-top: 20px;>        <l-button bindtapconfirm typesuccess stylepadding: 0px 10px 0px 0px;>发布投票</l-button>        <l-button bindtapcancel typeerror>取消</l-button>      </view>    </view>  </view>

 我这里还引用了小程序框架LinUI可以丰富我的页面不懂的可以看官网哦

结合我们刚刚的页面数据再看看效果

三、用户投票 1.初始数据 OptionMapper.xml

 <select idselecttoupiao resultTypejava.util.Map parameterTypejava.lang.String>    SELECT    id,    optionValue,    optionText,    title,    seatPic    FROM (    SELECT    t_oa_meeting_option.id,    t_oa_meeting_option.optionValue,    t_oa_meeting_option.optionText,    t_oa_meeting_info.title,    t_oa_meeting_info.seatPic,    ROW_NUMBER() OVER (PARTITION BY t_oa_meeting_option.optionValue ORDER BY t_oa_meeting_option.id) AS row_num    FROM    t_oa_meeting_option    JOIN t_oa_meeting_info ON t_oa_meeting_option.meetingId  t_oa_meeting_info.id    WHERE    t_oa_meeting_info.state  5    ) subquery    WHERE row_num  1    <if testtitle ! null and title ! >      AND title LIKE CONCAT(%, #{title}, %)    </if>  </select>
Controller代码
  //带模糊查询的会议信息    RequestMapping(/selectOptions)    public Object selectOptions(String title) {        System.out.println(title);        List<Map> selecttoupiao  optionMapper.selecttoupiao(title);        Map<Object, Object> data  new HashMap<Object, Object>();        data.put(selecttoupiao, selecttoupiao);        return ResponseUtil.ok(data);    }

 在config/api.js配置后端接口

   SelectOption:WxApiRoothome/selectOptions//查看可进行投票信息

在投票页面我使用了LinUI的滑动菜单和卡片布局。

 利用好框架我们写起来就会简单许多只需要将数据进行绑定即可但为了让用户有更好的体验所以我加了一个搜索框用户可进行模糊查询自己所需要投票的会议。

JS代码
   //加载投票会议信息的ajax   loadoptinos() {    util.request(api.SelectOption).then(res > {      // console.log(res.data.selecttoupiao)      this.setData({        Options: res.data.selecttoupiao      })    })  }, // 监听搜索事件   onSearch: function(event) {    var title  event.detail.value;    let data  {      title: title    };    util.request(api.SelectOption,data).then(res > {      this.setData({        Options: res.data.selecttoupiao      })    })  }    
 WXML代码
<view styleheight: 20px;></view>  <view classview-suo>   <input classinfo-sousuo typetext placeholder请输入会议标题进行搜索 bindconfirmonSearch/>  </view>  <block wx:for-items{{Options}} wx:for-itemitem wx:keyitem.id>  <l-card typecover image{{item.seatPic !null? item.seatPic : /static/persons/6.png}} title{{item.title}} stylemargin-top: 10px;>    <l-slide-view height120 width650 slide-width340>      <view slotleft classleft>        <view classleft-con stylemargin-top: 30px;>          <text classleft-title>投票标题《{{item.optionValue}}》</text>        </view>      </view>      <view slotright classright>        <view stylebackground-color:#9eb0fb;width: 100px;height: 100px;color: white; >        <view stylemargin: 20px 5px 5px 14px;>投票</view>        </view>        <view stylebackground-color: #728cf8;width: 100px;height: 100px;color: white;>          <view stylemargin: 20px 5px 5px 14px;>报表</view>        </view>        <view stylebackground-color: #e45c6c;width: 100px;height: 100px;color: white;>          <view stylemargin: 10px 5px 5px 14px;>结束会议</view>        </view>      </view>    </l-slide-view>  </l-card></block>

 效果展示

2.模态框显示选项

投票数据表的字段非常简单只需要拿到所选投票的id与投票人员的id即可

首先我们需要一个模态框作为载体来加载投票标题的投票选项

OptionMapper.xml
  <!--根据投票标题查询选项  -->  <select idvoting resultTypejava.util.Map parameterTypejava.lang.String >    select *from t_oa_meeting_option where 11    <if testoptionValue ! null and optionValue !  >      AND optionValue LIKE CONCAT(%, #{optionValue}, %)    </if>  </select>
 Controller代码
   //根据投票标题查询选项    RequestMapping(/voting)    public Object voting(String optionValue) {        List<Map> voting  optionMapper.voting(optionValue);        Map<Object, Object> data  new HashMap<Object, Object>();        data.put(voting, voting);        return ResponseUtil.ok(data);    }

 在config/api.js配置后端接口

  voting:WxApiRoothome/voting,//根据投票标题查询选项
JS代码
 //打开投票模态框并向后端拿值  openModal: function (e) {    let optionValue  e.currentTarget.dataset.value;    // console.log(传递的值, optionValue);    let data  {      optionValue: optionValue    };    util.request(api.voting, data).then(res > {      this.setData({        votes: res.data.voting,        showModal: true      })    })  },  //关闭投票模态框  closeModel(){    this.setData({      selectedOption:,      showModal: false    })  },//保存用户所选择的选项  radioChange: function (e) {    const value  e.detail.value;    this.setData({      selectedOption: value    });  }
WXML代码
<!-- 模态框 --><view wx:if{{showModal}} classmodal>  <view classmodal-content>    <!-- 单选框 -->    <radio-group bindchangeradioChange>      <label wx:for{{votes}} wx:key{{index}} classradio-label>        <radio value{{item.id}} checked>{{item.optionText}}</radio>      </label>    </radio-group>    <!-- 按钮 -->    <view>      <button bindtapconfirmVote>确定</button>      <button bindtapcloseModel>取消</button>    </view>  </view></view>

这里有个小地方需要注意我在投票的view上绑定了data-value{{item.optionValue}}也就是投票标题当打开模态框的时候就会带着这个投票标题找到相应的选项出来。

效果演示

3.确认投票

 到这一步就可以说是非常简单了我们只需要保存用户所选的是什么选项并将值传到后端进行保存即可。

VoteMapper.xml
<insert idinsertSelective parameterTypecom.zking.minoa.model.Vote >    insert into t_oa_meeting_vote    <trim prefix( suffix) suffixOverrides, >      <if testid ! null >        id,      </if>      <if testoptionid ! null >        optionId,      </if>      <if testpersonid ! null >        personId,      </if>    </trim>    <trim prefixvalues ( suffix) suffixOverrides, >      <if testid ! null >        #{id,jdbcTypeVARCHAR},      </if>      <if testoptionid ! null >        #{optionid,jdbcTypeVARCHAR},      </if>      <if testpersonid ! null >        #{personid,jdbcTypeBIGINT},      </if>    </trim>  </insert>
 Controller代码
   //投票    RequestMapping(/option)    public Object add(int optionId) {        System.out.println(optionId);        Vote votenew Vote();        vote.setOptionid(optionId);        vote.setPersonid(7);        int i  voteMapper.insertSelective(vote);        return ResponseUtil.ok();    }

这里没有进行用户登录的操作所以人的id就先定死 。

 在config/api.js配置后端接口

   option:WxApiRoothome/option,//用户投票
JS代码
//用户投票  confirmVote: function () {    const optionId  this.data.selectedOption;    let data  {      optionId: optionId    };    util.request(api.option, data).then(res > {      console.log(res)      this.setData({        showModal: false      })    })  }

效果展示

四、整体效果演示

 ​

到这里我的分享就结束了欢迎到评论区探讨交流

如果觉得有用的话还请点个赞吧

标签:
声明:无特别说明,转载请标明本文来源!