目录
一、小程序UI
1.讲述
小程序UI(User Interface)是指小程序的用户界面,包括小程序的整体布局、样式、交互等方面的设计。小程序UI的设计目的是为了提供用户友好的界面,使用户能够方便地使用小程序的功能。
小程序UI的设计原则通常包括以下几点:
1. 简洁明了:小程序的界面设计应该简洁明了,避免过多的视觉干扰和复杂的布局。用户可以通过一目了然的界面结构快速找到所需的功能。
2. 一致性:小程序的不同页面之间应该保持一致的界面设计,包括颜色、字体、按钮等方面的统一。这样可以提高用户的学习和使用效率,减少用户的困惑。
3. 易用性:小程序的界面设计应该考虑用户的使用习惯和心理需求,尽量减少用户的操作步骤和思考负担。例如,常用的功能应该放在显眼的位置,操作按钮应该易于点击等。
4. 可访问性:小程序的界面设计应该考虑到不同用户的特殊需求,例如视力障碍者、听力障碍者等。设计师应该通过合适的颜色对比度、字体大小等方式来提高小程序的可访问性。
5. 反馈机制:小程序的界面设计应该提供即时的反馈机制,告知用户他们的操作是否成功或失败。例如,可以通过动画、弹窗等方式来提示用户操作的结果。
小程序UI的设计通常由UI设计师负责,他们需要根据小程序的功能和定位进行界面设计,包括页面的布局、颜色的选择、图标的设计等。同时,他们还需要与开发人员密切合作,确保设计的界面能够被准确地实现。
2. 介绍vantWeapp
vantWeapp是一个基于微信小程序的组件库,是Vant组件库的小程序版本。它包括了常用的UI组件、业务组件和功能组件,可以帮助开发者快速构建出优秀的小程序页面。
vantWeapp的主要作用包括:
1. 提供常用UI组件:vantWeapp提供了丰富的UI组件,包括按钮、表单、列表、卡片、标签、导航、弹窗等,可以帮助开发者快速构建出美观、易用的小程序页面。
2. 提高开发效率:vantWeapp的组件具有可复用性,可以减少开发者的重复工作,提高开发效率。
3. 提供业务组件:vantWeapp还提供了一些常用的业务组件,例如地址选择器、城市选择器、日期选择器等,可以帮助开发者快速实现一些常用的业务需求。
4. 提供功能组件:vantWeapp还提供了一些功能组件,例如图片预览、下拉刷新、上拉加载等,可以帮助开发者实现一些常用的功能需求。
总之,vantWeapp是一个非常实用的小程序组件库,可以帮助开发者快速构建出美观、易用、功能丰富的小程序页面,提高开发效率,降低开发成本。
3. 使用vantWeapp
我们进入 vantWeapp 的官网进行快速上手 : https://vant-contrib.gitee.io/vant-weapp/0.x/#/quickstarthttps://vant-contrib.gitee.io/vant-weapp/0.x/#/quickstart
安装
在前端项目的跟路径中,打开CMD窗口,输入以下命令安装npm。
npm i vant-weapp -S --production
如图 :
构建
打开微信开发者工具,点击 工具 -> 构建 npm,并勾选 使用 npm 模块 选项,构建完成后,即可引入组件
如图 :
注 : 有些版本的开发工具是不需要勾选使用 npm 模块 选项的
依赖
在前端项目的跟路径中,打开CMD窗口,输入以下命令安装项目依赖。
npm install
如图 :
引用
以 Button 组件为例,只需要在
app.json
或index.json
中配置 Button 对应的路径即可。如果你是通过下载源代码的方式使用 vant-weapp,请将路径修改为项目中 vant-weapp 所在的目录。
例如 :
{
"navigationBarTitleText": "投票管理",
"usingComponents": {
"tabs":"/components/tabs/tabs",
"van-button": "vant-weapp/button",
}
}
引入组件后,可以在 wxml 中直接使用组件,如以下代码 :
<van-button type="default">默认按钮</van-button>
<van-button type="primary">主要按钮</van-button>
<van-button type="info">信息按钮</van-button>
<van-button type="warning">警告按钮</van-button>
<van-button type="danger">危险按钮</van-button>
效果图 :
二、后端
以下后端代码都是使用SpringMVC及mybatis的技术学习,还有前后端分离的技术应用,在我博客中也信息的学习及技术,通过这些来完成后端的代码及功能的实现。如有不懂可以在我博客中自行学习,你肯定是技术大牛。
1. 后端实体对象
Vote : 投票
package com.CloudJun.ssm.model;
import lombok.Data;
@Data
public class Vote {
private Integer id;
private Integer meetingId;
private String name;
private String remark;
private Integer state;
private String images;
public Vote(Integer id, Integer meetingId, String name, String remark, Integer state, String images) {
this.id = id;
this.meetingId = meetingId;
this.name = name;
this.remark = remark;
this.state = state;
this.images = images;
}
@Override
public String toString() {
return "Vote{" +
"id=" + id +
", meetingId=" + meetingId +
", name='" + name + '\'' +
", remark='" + remark + '\'' +
", state=" + state +
", images='" + images + '\'' +
'}';
}
}
2. 后端接口
VoteMapper : 自动生成的对象接口
package com.CloudJun.ssm.mapper;
import com.CloudJun.ssm.model.Vote;
import java.util.List;
public interface VoteMapper {
int deleteByPrimaryKey(Integer id);
int insert(Vote record);
int insertSelective(Vote record);
Vote selectByPrimaryKey(Integer id);
int updateByPrimaryKeySelective(Vote record);
int updateByPrimaryKey(Vote record);
List<Vote> selectByList(Integer state);
}
VoteService : 自己为了实现方法创建的接口
package com.CloudJun.ssm.service;
import com.CloudJun.ssm.model.Vote;
import java.util.List;
/**
* @author CloudJun
* @create 2023-10-24 14:41
*/
public interface VoteService {
int deleteByPrimaryKey(Integer id);
int insert(Vote record);
int insertSelective(Vote record);
Vote selectByPrimaryKey(Integer id);
int updateByPrimaryKeySelective(Vote record);
int updateByPrimaryKey(Vote record);
List<Vote> selectByList(Integer state);
}
3. 实现类
VoteServiceimpl : 实现调用方法功能而创建的实现类
package com.CloudJun.ssm.service.impl;
import com.CloudJun.ssm.mapper.VoteMapper;
import com.CloudJun.ssm.model.Vote;
import com.CloudJun.ssm.service.VoteService;
import org.springframework.beans.factory.annotation.Autowired;
import java.util.List;
/**
* @author CloudJun
* @create 2023-10-24 14:42
*/
public class VoteServiceimpl implements VoteService {
@Autowired
private VoteMapper voteMapper;
@Override
public int deleteByPrimaryKey(Integer id) {
return voteMapper.deleteByPrimaryKey(id);
}
@Override
public int insert(Vote record) {
return voteMapper.insert(record);
}
@Override
public int insertSelective(Vote record) {
return voteMapper.insertSelective(record);
}
@Override
public Vote selectByPrimaryKey(Integer id) {
return voteMapper.selectByPrimaryKey(id);
}
@Override
public int updateByPrimaryKeySelective(Vote record) {
return 0;
}
@Override
public int updateByPrimaryKey(Vote record) {
return 0;
}
@Override
public List<Vote> selectByList(Integer state) {
return voteMapper.selectByList(state);
}
}
4. 请求处理类
VoteController : 处理前端发送的请求及处理数据,并且给予回馈数据到前端
package com.CloudJun.ssm.wxcontroller;
import com.CloudJun.ssm.mapper.VoteMapper;
import com.CloudJun.ssm.model.Info;
import com.CloudJun.ssm.model.Vote;
import com.CloudJun.ssm.util.ResponseUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* @author CloudJun
* @create 2023-10-24 14:24
*/
@RestController
@RequestMapping("/wx/vote")
public class VoteController {
@Autowired
private VoteMapper voteMapper;
@RequestMapping("/add")
public Object Add (Vote vote){
int i = voteMapper.insertSelective(vote);
return ResponseUtil.ok();
}
@RequestMapping("/list")
public Object list (Integer state){
List<Vote> votes = voteMapper.selectByList(state);
return ResponseUtil.ok(votes);
}
@RequestMapping("/update")
public Object update (Vote vote){
int i = voteMapper.updateByPrimaryKey(vote);
return ResponseUtil.ok();
}
}
三、前端
以下的前端代码基于我博客中的进行扩写 : 微信小程序之微信授权登入及授权的流程讲解https://blog.csdn.net/SAME_LOVE/article/details/133963879?spm=1001.2014.3001.5501
1 . 定义路径
在项目中的 api.js 文件里,配置后端请求数据的请求地址。
// 以下是业务服务器API地址
// 本机开发API地址
var WxApiRoot = 'http://localhost:8080/oapro/wx/';
// 测试环境部署api地址
// var WxApiRoot = 'http://192.168.191.1:8080/oapro/wx/';
// 线上平台api地址
//var WxApiRoot = 'https://www.oa-mini.com/demo/wx/';
module.exports = {
IndexUrl: WxApiRoot + 'home/index', //首页数据接口
SwiperImgs: WxApiRoot+'swiperImgs',
MettingInfos: WxApiRoot+'meeting/list',
MettingVote : WxApiRoot+'info/list',
MettingVoteAdd : WxApiRoot+'vote/add',//增加投票
MettingVoteList : WxApiRoot+'vote/list',//获取投票信息
MettingVoteupdate : WxApiRoot+'vote/update',//确认投票
AuthLoginByWeixin: WxApiRoot + 'auth/login_by_weixin', //微信登录
UserIndex: WxApiRoot + 'user/index', //个人页面用户相关信息
AuthLogout: WxApiRoot + 'auth/logout', //账号登出
AuthBindPhone: WxApiRoot + 'auth/bindPhone' //绑定微信手机号
};
2. 页面引用
在项目的投票管理页面中配置引用的组件及自定义组件,在.JSON文件中编写 :
list.json
{
"navigationBarTitleText": "投票管理",
"usingComponents": {
"tabs":"/components/tabs/tabs",
"van-button": "vant-weapp/button",
"van-notice-bar": "vant-weapp/notice-bar/index",
"van-toast": "vant-weapp/toast/index"
}
}
3. 页面
在项目的投票管理页面中,进行编写页面标签,对数据进行显示,在.wxml文件中
list.wxml
<!--pages/vote/list/list.wxml-->
<tabs tabList="{{tabs}}" bindtabsItemChange="tabsItemChange">
</tabs>
<view style="height: 100rpx;"></view>
<view class="{{componentStatus[0] ? '' : 'hidden'}}">
<!-- 发起投票版块 -->
<view class="publish">
<form bindsubmit='votesubmit'>
<view class="img">
<view class="img_left" >
<image class="imgs" src="{{imageUrl=='' ? '/static/persons/15.gif':imageUrl }}" mode="aspectFit" bindtap="handleUploadImage"></image>
<input hidden="true" type="text" name="images" value="{{imageUrl}}" />
</view>
</view>
<view class="meeting_id">
<view class="meeting_title">所属会议 : </view>
<picker bindchange="meetingChange" name="meetingId" value="{{array[meeting_id].id}}" range-key="title" range="{{array}}">
<view class="meeting_text" >{{array[meeting_id].title==null?'请选择会议':array[meeting_id].title}}</view>
</picker>
</view>
<view class="meeting_id" id="meeting_id">
<view class="meeting_title">投票标题 : </view>
<input class="vote_text" placeholder="请输入标题" type="text" name="name" />
</view>
<view class="textarea" id="vote_text">
<view class="meeting_textarea">投票说明 : </view>
<textarea class="vote_textarea" name="remark" type="text" ></textarea>
</view>
<view class="vote_button">
<button class="vote_button_empty" type="default" formType="reset" plain="true">清空内容</button>
<view style="width: 70rpx;"></view>
<button class="vote_button_submit" type="primary" formType="submit" plain="true">确认发起</button>
</view>
</form>
</view>
</view>
<view class="{{componentStatus[1] ? '' : 'hidden'}}">
<van-notice-bar
left-icon="flower-o"
mode="closeable"
color="#6d9d9e"
delay="100"
backgroundColor="#e4ecec"
text="无论我们能活多久,我们能够享受的只有无法分割的此刻,不会回头,离弦的箭、逝去的生活和失去的机会。此外别无其他。"
/>
<!-- 已参与投票 -->
<block wx:for-items="{{engage}}" wx:for-item="item" wx:key="item.id">
<view class="vote_carryout">
<image class="vote_carryout_img" src="{{item.images}}"></image>
<view class="vote_carryout_text">
<view style="width: 450rpx;display: flex;flex-direction:column;">
<text class="vote_carryout_text_f" >{{item.name}}</text>
<text class="vote_carryout_text_t">{{item.remark}}</text>
</view>
<button class="vote_carryout_text_btn" bindtap="voteparticipate" data-item="{{item.id}}" size="mini">参与</button>
</view>
</view>
</block>
</view>
<view class="{{componentStatus[2] ? '' : 'hidden'}}">
<van-notice-bar
left-icon="flower-o"
mode="closeable"
color="#6d9d9e"
delay="100"
backgroundColor="#e4ecec"
text="无论我们能活多久,我们能够享受的只有无法分割的此刻,不会回头,离弦的箭、逝去的生活和失去的机会。此外别无其他。"
/>
<!-- 未参与投票 -->
<block wx:for-items="{{not}}" wx:for-item="item" wx:key="item.id">
<view class="vote_carryout">
<image class="vote_carryout_img" src="{{item.images==null?'/static/persons/15.gif':item.images}}"></image>
<view class="vote_carryout_text">
<view style="width: 450rpx;display: flex;flex-direction:column;">
<text class="vote_carryout_text_f" >{{item.name}}</text>
<text class="vote_carryout_text_t">{{item.remark}}</text>
</view>
<button class="vote_carryout_text_btn" bindtap="Votenotbtn" size="mini">已参与</button>
</view>
</view>
</block>
</view>
<view class="{{componentStatus[3] ? '' : 'hidden'}}">
<van-notice-bar
left-icon="flower-o"
mode="closeable"
color="#6d9d9e"
delay="100"
backgroundColor="#e4ecec"
text="无论我们能活多久,我们能够享受的只有无法分割的此刻,不会回头,离弦的箭、逝去的生活和失去的机会。此外别无其他。"
/>
<!-- 未参与投票 -->
<block wx:for-items="{{lists}}" wx:for-item="item" wx:key="item.id">
<view class="vote_carryout">
<image class="vote_carryout_img" src="{{item.images==null?'/static/persons/15.gif':item.images}}"></image>
<view class="vote_carryout_text">
<view style="width: 450rpx;display: flex;flex-direction:column;">
<text class="vote_carryout_text_f" >{{item.name}}</text>
<text class="vote_carryout_text_t">{{item.remark}}</text>
</view>
<button class="vote_carryout_text_btn" size="mini">{{item.state==0?'未参与':'已参与'}}</button>
</view>
</view>
</block>
</view>
4. 页面美化
在项目的投票管理页面中,编写标签样式,对页面的美化,在.wxss文件中编写
list.wxss
/* pages/vote/list/list.wxss */
.hidden {
display: none;
}
.img {
height: 450rpx;
/* display: flex; */
/* border-radius: 6px; */
/* align-items: center; */
}
.imgs {
height: 420rpx;
width: 419rpx;
margin-left: 105rpx;
box-shadow: 0 0 20px rgb(117, 241, 241);
border-radius: 10rpx;
}
.img_left {
margin-top: 28rpx;
height: 450rpx;
width: 600rpx;
margin-left: 57rpx;
/* border-radius: 23rpx; */
/* background-color: deeppink; */
}
.publish {
width: 100%;
}
.meeting_id {
height: 100rpx;
background-color: rgb(230, 243, 243);
display: flex;
/* border-radius: 6px; */
align-items: center;
}
.meeting_title {
margin-left: 20rpx;
}
.meeting_text {
/* background-color: aqua; */
margin-left: 20rpx;
margin-top: 10rpx;
padding-left: 20rpx;
font-size: 14px;
height: 50rpx;
width: 520rpx;
}
#meeting_id {
border-top: 2px solid #fafafa;
}
#vote_text {
display: flex;
align-items: center;
}
.vote_text {
margin-left: 35rpx;
}
.meeting_textarea {
margin-left: 20rpx;
width: 440rpx;
}
.vote_textarea {
height: 160rpx;
width: 490px;
padding-right: 10rpx;
padding-top: 10rpx;
padding-left: 10rpx;
border: 2px solid #c0f0f1;
border-radius: 20rpx;
}
.textarea {
border-top: 2px solid #fafafa;
background-color: rgb(238, 247, 247);
}
.vote_button {
border-top: 4px solid #fafafa;
display: flex;
width: 80%;
align-items: center;
margin-left: 73rpx;
}
.vote_carryout{
background-color: rgba(150, 250, 250, 0.315);
box-shadow: 0 0 20px rgb(117, 241, 241);
margin: 20rpx 30rpx 0rpx 70rpx;
border-radius: 20rpx;
height: 460rpx;
width: 620rpx;
}
.vote_carryout_img{
height: 350rpx;
width: 620rpx;
border-radius: 20rpx;
}
.vote_carryout_text{
display: flex;
height: 100rpx;
border-radius: 20rpx;
}
.vote_carryout_text_f{
margin-left: 50rpx;
height: 40rpx;
font-size: 12px;
}
.vote_carryout_text_t{
margin-left: 50rpx;
height: 40rpx;
color: rgba(116, 114, 114, 0.89);
font-size: 12px;
}
.vote_carryout_text_btn{
margin-top: 10rpx;
height: 80rpx;
font-size: 12px;
size: 12px;
background-color: rgb(174, 249, 252);
}
5. 数据
在项目的投票管理页面中,编写后台请求的数据进行回应到前端,在.js文件中
list.js
// pages/vote/list/list.js
var app = getApp();
const api = require('../../../config/api');
const util = require('../../../utils/util.js');
Page({
/**
* 页面的初始数据
*/
data: {
tabs: ['发起投票', '未参与', '已参与', '全部投票'],//顶部导航栏
componentStatus: [true, false, false, false],//用于处理内容显示
array: ['美国', '中国', '巴西', '日本'],
engage:[],//未参与的投票
not:[],//已参与的投票
lists:[],//全部投票信息
meeting_id: '请选择会议',
imageUrl: '',//图片路径
votename: '123'
},
//选择所属会议的事件
meetingChange(e) {
// console.log(e.detail.value)
this.setData({
meeting_id: e.detail.value
})
},
//初始会议信息
meetingini() {
util.request(api.MettingVote).then(res => {
this.setData({
array: res.data.infoList
})
}).catch(res => {
console.log('服器没有开启,使用模拟数据!')
})
},
//初始化未参与的投票
Voteiniengage() {
util.request(api.MettingVoteList,{state:0}).then(res => {
// console.log(res);
this.setData({
engage: res.data
})
}).catch(res => {
console.log('服器没有开启,使用模拟数据!')
})
},
//初始化已参与的投票
Voteininot() {
util.request(api.MettingVoteList,{state:1}).then(res => {
// console.log(res);
this.setData({
not: res.data
})
}).catch(res => {
console.log('服器没有开启,使用模拟数据!')
})
},
//初始化全部投票信息
VoteiniList() {
util.request(api.MettingVoteList).then(res => {
// console.log(res);
this.setData({
lists: res.data
})
}).catch(res => {
console.log('服器没有开启,使用模拟数据!')
})
},
//参与投票的点击事件
voteparticipate(id){
// console.log(id.target.dataset.item)
wx.showModal({
title: '提示',
content: '你是否要贡献出你宝贵的一票?',
success: function (res) {
if (res.confirm) {
//这里是点击了确定以后
util.request(api.MettingVoteupdate, {id:id.target.dataset.item}).then(res => {
// console.log(res)
if (res.errno == 0) {
wx.showToast({
title: '成功投票!!',
icon: 'none',
duration: 1000//持续的时间
})
}
}).catch(res => {
console.log('服器没有开启,发布失败')
})
} else {//这里是点击了取消以后
console.log('用户点击取消')
}
}
})
},
//发起投票的事件
votesubmit(data) {
console.log(data.detail.value)
wx.showModal({
title: '提示',
content: '确定发起该投票吗?',
success: function (res) {
if (res.confirm) {
//这里是点击了确定以后
util.request(api.MettingVoteAdd, data.detail.value).then(res => {
// console.log(res)
if (res.errno == 0) {
wx.showToast({
title: '发布投票成功',
icon: 'none',
duration: 1500//持续的时间
})
}
}).catch(res => {
console.log('服器没有开启,发布失败')
})
} else {//这里是点击了取消以后
console.log('用户点击取消')
}
}
})
},
//已参与按钮的点击事件
Votenotbtn() {
wx.showToast({
title: '已进行投票,不可再投',
icon: 'none',
duration: 1000//持续的时间
})
},
//图片选择器
handleUploadImage() {
wx.chooseImage({
count: 1,
success: (res) => {
const imagePath = res.tempFilePaths[0];
// 处理选择的图片路径
// console.log('选择的图片路径:', imagePath);
this.setData({
imageUrl: imagePath // 更新图片路径,触发重新渲染
});
}
});
},
/**
* 生命周期函数--监听页面加载
*/
onLoad(options) {
this.meetingini();
this.Voteiniengage();
this.Voteininot();
this.VoteiniList();
},
/**
* 生命周期函数--监听页面初次渲染完成
*/
onReady() {
},
/**
* 生命周期函数--监听页面显示
*/
onShow() {
},
/**
* 生命周期函数--监听页面隐藏
*/
onHide() {
},
/**
* 生命周期函数--监听页面卸载
*/
onUnload() {
},
/**
* 页面相关事件处理函数--监听用户下拉动作
*/
onPullDownRefresh() {
},
/**
* 页面上拉触底事件的处理函数
*/
onReachBottom() {
},
/**
* 用户点击右上角分享
*/
onShareAppMessage() {
},
//点击导航栏进行切换下方内容
tabsItemChange(e) {
let index = e.detail.index;
//全部的组件赋值为false
const lists = [false, false, false, false];
//将所点击的组件赋值为true
lists[index] = true;
this.setData({
componentStatus: lists // 更新 data 中的 componentStatus 属性值
});
},
// tabsItemChange(e){
// let index = e.detail.index;
// console.log('vote.index='+index)
// if(index==1 || index==2){
// if (app.globalData.hasLogin) {
// }else{
// wx.navigateTo({
// url: '/pages/auth/login/login',
// })
// }
// }
// }
})
6. 效果展示
发起投票
参与投票
查看所有投票