当前位置: 首页 > news >正文

【微信小程序开发】小程序微信用户授权登录(用户信息手机号)

🥳🥳Welcome Huihui's Code World ! !🥳🥳

接下来看看由辉辉所写的关于小程序的相关操作吧

目录

🥳🥳Welcome Huihui's Code World ! !🥳🥳

授权流程讲解

一.用户信息授权登录

1.wx.login

2.wx.getUserProfile

3.代码

WXML

JS

二.用户信息授权登录之后台交互

前期准备

①准备数据接口

②密钥以及appid

后端代码

WXML

JS

utils/user.js【封装的代码块】

三.手机号授权登录之后台交互

后端代码

WXML

JS

四.投票界面与后台交互

1.效果预览

2.代码演示

vote/list.wxml

 vote/list.wxss

vote/list.js

vote/addvote.wxml

 vote/addvote.wxss

vote/addvote.js

vote/many.wxml

 vote/many.wxss

vote/many.js


授权流程讲解

我们在使用微信中的小程序时,都会要我们进行授权,例如下面这样

那么这样的用户授权时怎么实现的呢,这一篇主要就是讲解用户授权登录的流程!!!

图片说明:

1.客户端调用 wx.login() 获取 临时登录凭证code,通过 wx.request() 发起网络请求,将 code 传给服务端
2、服务端使用 code + appid + appsecret 向微信换取 (调用 auth.code2Session 接口)用户唯一标识openid 会话密钥session_key
3、服务端自定义 登录状态token(与openid、session_key关联)返回客户端
4、客户端将 登录状态token 存入 缓存storage(推荐使用 wx.setStorageSync(‘key’, ‘value’) 同步存储)
5、客户端wx.request() 发起请求时,携带登录状态token (推荐使用 wx.getStorageSync(‘key’) 同步获取)
6、服务端通过 登录状态token 查询到对应 openid 和 session_key
7、验证成功后返回业务数据给客户端

一.用户信息授权登录

其中有两种方法,第一种方法是点击登录之后便直接获取了用户的个人信息,而第二种会询问用户是否同意授权,这样的话,会更具安全性

1.wx.login

这个方法主要用于获取用户的登录凭证(code)。在用户进入小程序时,前端会调用wx.login来获取这个code,然后将这个code发送给后台服务器。后台服务器再向微信发送请求,通过这个code来获取用户的唯一标识(openid)以及本次登录的会话密钥(session_key)。之后,后台服务器将这两个信息传回前台,用于自定义登录状态和用户唯一标识

2.wx.getUserProfile

这个方法主要用于获取用户的更多详细信息,如昵称、头像等。在使用这个方法之前,需要先调用wx.authorize接口来发起授权请求,请求用户授权提供这些信息。如果用户同意授权,就可以通过调用wx.getUserProfile方法来获取这些详细信息

3.代码

WXML

<!--pages/index/index.wxml-->
<view><button wx:if="{{canIUseGetUserProfile}}" type="primary" class="wx-login-btn" bindtap="getUserProfile">微信直接登录1</button><button wx:else open-type="getUserInfo" type="primary" class="wx-login-btn" bindgetuserinfo="wxLogin">微信直接登录2</button><image mode="scaleToFill" src="{{userInfo.avatarUrl}}" /><text>昵称:{{userInfo.nickName}}</text>
</view>

JS

// pages/index/index.js
Page({data: {userInfo: {},canIUseGetUserProfile: false,},onLoad() {// if (wx.getUserProfile) {//   this.setData({//     canIUseGetUserProfile: true//   })// }},getUserProfile(e) {console.log('getUserProfile')// 推荐使用 wx.getUserProfile 获取用户信息,开发者每次通过该接口获取用户个人信息均需用户确认// 开发者妥善保管用户快速填写的头像昵称,避免重复弹窗wx.getUserProfile({desc: '用于完善会员资料', // 声明获取用户个人信息后的用途,后续会展示在弹窗中,请谨慎填写success: (res) => {console.log(res);this.setData({userInfo: res.userInfo,hasUserInfo: true})}})},wxLogin: function(e) {debuggerconsole.log('wxLogin')console.log(e.detail.userInfo);this.setData({userInfo: e.detail.userInfo})if (e.detail.userInfo == undefined) {app.globalData.hasLogin = false;util.showErrorToast('微信登录失败');return;}},/*** 生命周期函数--监听页面初次渲染完成*/onReady() {},/*** 生命周期函数--监听页面显示*/onShow() {},/*** 生命周期函数--监听页面隐藏*/onHide() {},/*** 生命周期函数--监听页面卸载*/onUnload() {},/*** 页面相关事件处理函数--监听用户下拉动作*/onPullDownRefresh() {},/*** 页面上拉触底事件的处理函数*/onReachBottom() {},/*** 用户点击右上角分享*/onShareAppMessage() {}
})

用户授权登录后,后台便会保存用户的信息

二.用户信息授权登录之后台交互

前期准备

①准备数据接口

②密钥以及appid

后端代码

package com.zking.ssm.wxcontroller;/*** @Autho donkee* @Since 2022/6/27*/import cn.binarywang.wx.miniapp.bean.WxMaPhoneNumberInfo;
import com.alibaba.fastjson.JSONObject;
import com.zking.ssm.annotation.LoginUser;
import com.zking.ssm.model.UserInfo;
import com.zking.ssm.model.WxLoginInfo;
import com.zking.ssm.model.WxUser;
import com.zking.ssm.service.UserToken;
import com.zking.ssm.service.UserTokenManager;
import com.zking.ssm.service.WxUserService;
import com.zking.ssm.util.JacksonUtil;
import com.zking.ssm.util.ResponseUtil;
import com.zking.ssm.util.UserTypeEnum;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;import cn.binarywang.wx.miniapp.api.WxMaService;
import cn.binarywang.wx.miniapp.bean.WxMaJscode2SessionResult;
import javax.servlet.http.HttpServletRequest;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;/*** 鉴权服务*/
@Slf4j
@RestController
@RequestMapping("/wx/auth")
public class WxAuthController {@Autowiredprivate WxMaService wxService;@Autowiredprivate WxUserService userService;/*** 微信登录** @param wxLoginInfo*            请求内容,{ code: xxx, userInfo: xxx }* @param request*            请求对象* @return 登录结果*/@PostMapping("login_by_weixin")public Object loginByWeixin(@RequestBody WxLoginInfo wxLoginInfo, HttpServletRequest request) {//客户端需携带code与userInfo信息String code = wxLoginInfo.getCode();UserInfo userInfo = wxLoginInfo.getUserInfo();if (code == null || userInfo == null) {return ResponseUtil.badArgument();}//调用微信sdk获取openId及sessionKeyString sessionKey = null;String openId = null;try {long beginTime = System.currentTimeMillis();//WxMaJscode2SessionResult result = this.wxService.getUserService().getSessionInfo(code);
//            Thread.sleep(6000);long endTime = System.currentTimeMillis();log.info("响应时间:{}",(endTime-beginTime));sessionKey = result.getSessionKey();//session idopenId = result.getOpenid();//用户唯一标识 OpenID} catch (Exception e) {e.printStackTrace();}if (sessionKey == null || openId == null) {log.error("微信登录,调用官方接口失败:{}", code);return ResponseUtil.fail();}else{log.info("openId={},sessionKey={}",openId,sessionKey);}//根据openId查询wx_user表//如果不存在,初始化wx_user,并保存到数据库中//如果存在,更新最后登录时间WxUser user = userService.queryByOid(openId);if (user == null) {user = new WxUser();user.setUsername(openId);user.setPassword(openId);user.setWeixinOpenid(openId);user.setAvatar(userInfo.getAvatarUrl());user.setNickname(userInfo.getNickName());user.setGender(userInfo.getGender());user.setUserLevel((byte) 0);user.setStatus((byte) 0);user.setLastLoginTime(new Date());user.setLastLoginIp(IpUtil.client(request));user.setShareUserId(1);userService.add(user);} else {user.setLastLoginTime(new Date());user.setLastLoginIp(IpUtil.client(request));if (userService.updateById(user) == 0) {log.error("修改失败:{}", user);return ResponseUtil.updatedDataFailed();}}// tokenUserToken userToken = null;try {userToken = UserTokenManager.generateToken(user.getId());} catch (Exception e) {log.error("微信登录失败,生成token失败:{}", user.getId());e.printStackTrace();return ResponseUtil.fail();}userToken.setSessionKey(sessionKey);log.info("SessionKey={}",UserTokenManager.getSessionKey(user.getId()));Map<Object, Object> result = new HashMap<Object, Object>();result.put("token", userToken.getToken());result.put("tokenExpire", userToken.getExpireTime().toString());userInfo.setUserId(user.getId());if (!StringUtils.isEmpty(user.getMobile())) {// 手机号存在则设置userInfo.setPhone(user.getMobile());}try {DateFormat df = new SimpleDateFormat("yyyy-MM-dd");String registerDate = df.format(user.getAddTime() != null ? user.getAddTime() : new Date());userInfo.setRegisterDate(registerDate);userInfo.setStatus(user.getStatus());userInfo.setUserLevel(user.getUserLevel());// 用户层级userInfo.setUserLevelDesc(UserTypeEnum.getInstance(user.getUserLevel()).getDesc());// 用户层级描述} catch (Exception e) {log.error("微信登录:设置用户指定信息出错:"+e.getMessage());e.printStackTrace();}result.put("userInfo", userInfo);log.info("【请求结束】微信登录,响应结果:{}", JSONObject.toJSONString(result));return ResponseUtil.ok(result);}}
}

WXML

<!--pages/auth/login/login.wxml-->
<view class="container"><view class="login-box"><button wx:if="{{canIUseGetUserProfile}}" type="primary" class="wx-login-btn" bindtap="getUserProfile">微信直接登录</button><button wx:else open-type="getUserInfo" type="primary" class="wx-login-btn" bindgetuserinfo="wxLogin">微信直接登录</button><button type="primary" class="account-login-btn" bindtap="accountLogin">账号登录</button></view>
</view>

JS

// pages/auth/login/login.js
var util = require('../../../utils/util.js');
var user = require('../../../utils/user.js');
const app = getApp();
Page({/*** 页面的初始数据*/data: {canIUseGetUserProfile: false, // 用于向前兼容lock:false},onLoad: function(options) {// 页面初始化 options为页面跳转所带来的参数// 页面渲染完成if (wx.getUserProfile) {this.setData({canIUseGetUserProfile: true})}//console.log('login.onLoad.canIUseGetUserProfile='+this.data.canIUseGetUserProfile)},/*** 生命周期函数--监听页面初次渲染完成*/onReady() {},/*** 生命周期函数--监听页面显示*/onShow() {},getUserProfile(e) {console.log('getUserProfile');// 推荐使用wx.getUserProfile获取用户信息,开发者每次通过该接口获取用户个人信息均需用户确认// 开发者妥善保管用户快速填写的头像昵称,避免重复弹窗wx.getUserProfile({desc: '用于完善会员资料', // 声明获取用户个人信息后的用途,后续会展示在弹窗中,请谨慎填写success: (res) => {//console.log(res);user.checkLogin().catch(() => {user.loginByWeixin(res.userInfo).then(res => {app.globalData.hasLogin = true;wx.navigateBack({delta: 1})}).catch((err) => {app.globalData.hasLogin = false;if(err.errMsg=="request:fail timeout"){util.showErrorToast('微信登录超时');}else{util.showErrorToast('微信登录失败');}this.setData({lock:false})});});},fail: (res) => {app.globalData.hasLogin = false;console.log(res);util.showErrorToast('微信登录失败');}});},wxLogin: function(e) {console.log('wxLogin');if (e.detail.userInfo == undefined) {app.globalData.hasLogin = false;util.showErrorToast('微信登录失败');return;}user.checkLogin().catch(() => {user.loginByWeixin(e.detail.userInfo).then(res => {app.globalData.hasLogin = true;wx.navigateBack({delta: 1})}).catch((err) => {app.globalData.hasLogin = false;if(err.errMsg=="request:fail timeout"){util.showErrorToast('微信登录超时');}else{util.showErrorToast('微信登录失败');}});});},accountLogin() {console.log('开发中....')}})

utils/user.js【封装的代码块】

/*** 用户相关服务*/
const util = require('../utils/util.js');
const api = require('../config/api.js');/*** Promise封装wx.checkSession*/
function checkSession() {return new Promise(function(resolve, reject) {wx.checkSession({success: function() {resolve(true);},fail: function() {reject(false);}})});
}
/*** Promise封装wx.login*/
function login() {return new Promise(function(resolve, reject) {wx.login({success: function(res) {if (res.code) {resolve(res);} else {reject(res);}},fail: function(err) {reject(err);}});});
}
/*** 调用微信登录*/
function loginByWeixin(userInfo) {return new Promise(function(resolve, reject) {return login().then((res) => {//登录远程服务器util.request(api.AuthLoginByWeixin, {code: res.code,userInfo: userInfo}, 'POST').then(res => {if (res.errno === 0) {//存储用户信息wx.setStorageSync('userInfo', res.data.userInfo);wx.setStorageSync('token', res.data.token);resolve(res);} else {reject(res);}}).catch((err) => {reject(err);});}).catch((err) => {reject(err);})});
}/*** 判断用户是否登录*/
function checkLogin() {return new Promise(function(resolve, reject) {if (wx.getStorageSync('userInfo') && wx.getStorageSync('token')) {checkSession().then(() => {resolve(true);}).catch(() => {reject(false);});} else {reject(false);}});
}module.exports = {loginByWeixin,checkLogin,
};

三.手机号授权登录之后台交互

手机号授权登录的流程与用户信息授权登录流程是一样的,只不过向微信调用的接口有所不同

后端代码

package com.zking.ssm.wxcontroller;/*** @Autho donkee* @Since 2022/6/27*/import cn.binarywang.wx.miniapp.bean.WxMaPhoneNumberInfo;
import com.alibaba.fastjson.JSONObject;
import com.zking.ssm.annotation.LoginUser;
import com.zking.ssm.model.UserInfo;
import com.zking.ssm.model.WxLoginInfo;
import com.zking.ssm.model.WxUser;
import com.zking.ssm.service.UserToken;
import com.zking.ssm.service.UserTokenManager;
import com.zking.ssm.service.WxUserService;
import com.zking.ssm.util.JacksonUtil;
import com.zking.ssm.util.ResponseUtil;
import com.zking.ssm.util.UserTypeEnum;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;import cn.binarywang.wx.miniapp.api.WxMaService;
import cn.binarywang.wx.miniapp.bean.WxMaJscode2SessionResult;
import javax.servlet.http.HttpServletRequest;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;/*** 鉴权服务*/
@Slf4j
@RestController
@RequestMapping("/wx/auth")
public class WxAuthController {/*** 绑定手机号码** @param userId* @param body* @return*/@PostMapping("bindPhone")public Object bindPhone(@LoginUser Integer userId, @RequestBody String body) {log.info("【请求开始】绑定手机号码,请求参数,body:{}", body);String sessionKey = UserTokenManager.getSessionKey(userId);String encryptedData = JacksonUtil.parseString(body, "encryptedData");String iv = JacksonUtil.parseString(body, "iv");WxMaPhoneNumberInfo phoneNumberInfo = null;try {phoneNumberInfo = this.wxService.getUserService().getPhoneNoInfo(sessionKey, encryptedData, iv);} catch (Exception e) {log.error("绑定手机号码失败,获取微信绑定的手机号码出错:{}", body);e.printStackTrace();return ResponseUtil.fail();}String phone = phoneNumberInfo.getPhoneNumber();WxUser user = userService.selectByPrimaryKey(userId);user.setMobile(phone);if (userService.updateById(user) == 0) {log.error("绑定手机号码,更新用户信息出错,id:{}", user.getId());return ResponseUtil.updatedDataFailed();}Map<Object, Object> data = new HashMap<Object, Object>();data.put("phone", phone);log.info("【请求结束】绑定手机号码,响应结果:{}", JSONObject.toJSONString(data));return ResponseUtil.ok(data);}}
}

WXML

<!--pages/ucenter/user/user.wxml-->
<form bindsubmit="formSubmit"><view class='personal-data'><view class='list'><view class='item acea-row row-between-wrapper'><view>头像</view><view class='pictrue'><image src='{{userInfo.avatarUrl}}'></image></view></view><view class='item acea-row row-between-wrapper'><view>名字</view><view class='input'><input type='text' disabled='true' name='nickname' value='{{userInfo.nickName}}'></input></view></view><view class='item acea-row row-between-wrapper'><view>手机号码</view><button name='phone' class='phoneW' value='{{userInfo.phone}}' wx:if="{{!userInfo.phone}}" bindgetphonenumber="getPhoneNumber" hover-class='none' open-type='getPhoneNumber'>点击获取</button><view class='input acea-row row-between-wrapper' wx:else><input type='text' disabled='true' name='phone' value='{{userInfo.phone}}' class='id'></input><text class='iconfont icon-suozi'></text></view></view><view class='item acea-row row-between-wrapper'><view>ID号</view><view class='input acea-row row-between-wrapper'><input type='text' value='1000{{userInfo.userId}}' disabled='true' class='id'></input><text class='iconfont icon-suozi'></text></view></view></view><button class='modifyBnt' bindtap="exitLogin">退 出</button></view>
</form>

JS

var util = require('../../../utils/util.js');
var api = require('../../../config/api.js');
var user = require('../../../utils/user.js');
var app = getApp();
Page({/*** 页面的初始数据*/data: {userInfo: {},hasLogin: false,userSharedUrl: ''},/*** 生命周期函数--监听页面加载*/onLoad: function (options) {},onShow: function () {let that = this;//获取用户的登录信息let userInfo = wx.getStorageSync('userInfo');this.setData({userInfo: userInfo,hasLogin: true});},getPhoneNumber: function (e) {console.log(e);let that = this;if (e.detail.errMsg !== "getPhoneNumber:ok") {// 拒绝授权return;}if (!this.data.hasLogin) {wx.showToast({title: '绑定失败:请先登录',icon: 'none',duration: 2000});return;}util.request(api.AuthBindPhone, {iv: e.detail.iv,encryptedData: e.detail.encryptedData}, 'POST').then(function (res) {if (res.errno === 0) {let userInfo = wx.getStorageSync('userInfo');userInfo.phone = res.data.phone;//设置手机号码wx.setStorageSync('userInfo', userInfo);that.setData({userInfo: userInfo,hasLogin: true});wx.showToast({title: '绑定手机号码成功',icon: 'success',duration: 2000});}});},exitLogin: function () {wx.showModal({title: '',confirmColor: '#b4282d',content: '退出登录?',success: function (res) {if (!res.confirm) {return;}util.request(api.AuthLogout, {}, 'POST');app.globalData.hasLogin = false;wx.removeStorageSync('token');wx.removeStorageSync('userInfo');wx.reLaunch({url: '/pages/index/index'});}})}
})

四.投票界面与后台交互

1.效果预览

2.代码演示

vote/list.wxml

<!--pages/vote/list/list.wxml-->
<!-- <view class="food-button" bindtap="jumpToFoodPage"><image class="food-icon" src="../../../static/vote/totaldata-active.png"></image><text class="food-label">投票统计</text>
</view> --><view class="info-title"><image class="img-title"  src="../../../static/vote/sousuo.png"></image><input  type="text" placeholder="选择所属会议" bindtap="showModal" />
</view>
<view wx:if="{{modalVisible}}">你好,我是模态框</view><view class="vote-button" bindtap="jumpToFoodPage"><block wx:for="{{myvotelist}}" wx:key="index"><view class="myvotes"><view class="myimg"><image class="vote-icon" src="{{item.img}}"></image></view><view class="myview"><text class="vote-label">{{item.name}}</text></view></view></block><image class="vote-add" src="../../../static/vote/addvote.png" bindtap="addvote"></image>
</view>

 vote/list.wxss

/* pages/vote/list/list.wxss */
.vote-button {display: flex;flex-direction: row;flex-wrap: wrap;/* 使元素在一行中超出容器宽度时自动换行 */
}.myvotes {padding-top: 50px;padding-left: 5px;height: 430rpx;width: 360rpx;display: flex;flex-direction: column;
}
.myimg{display: flex;justify-content: center;
}
.myview{}
.vote-icon {margin: 5px;width: 320rpx;height: 300rpx;border-radius:25px;border: 10px solid rgb(247, 198, 66);
}
.img-title{width: 32px;height: 32px;
}.vote-label {font-weight: 800;font-family: YouYuan;width: 350rpx;padding-left: 65px;/* 将文字放在图标的正下方 */}
.info-title{display: flex;align-items: center;margin-left:65px ;width: 300px;border-radius:25px;border: 2px solid rgb(247, 198, 66);
}.vote-add {margin-left: 340px;margin-top: 35px;width: 120rpx;height: 120rpx;
}/* --------------------- */
/* 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;
}

vote/list.js

// pages/vote/list/list.js
Page({addvote: function () {console.log(111)wx.navigateTo ({url: '/pages/vote/addvote/addvote' // 跳转到目标页面的路径})},/*** 页面的初始数据*/data: {myvotelist:[{index:1, name:'投票统计',img:'../../../static/vote/totaldata-active.png'},{index:3, name:'历史投票',img:'../../../static/vote/voterecords-active.png'},{index:2, name:'赞成人数',img:'../../../static/vote/yes-active.png'},{index:3, name:'反对人数',img:'../../../static/vote/no-active.png'},], modalVisible: false,   // 模态框是否可见},// 点击事件,显示模态框showModal(e) {this.setData({modalVisible: true});},/*** 生命周期函数--监听页面加载*/onLoad(options) {},/*** 生命周期函数--监听页面初次渲染完成*/onReady() {},/*** 生命周期函数--监听页面显示*/onShow() {},/*** 生命周期函数--监听页面隐藏*/onHide() {},/*** 生命周期函数--监听页面卸载*/onUnload() {},/*** 页面相关事件处理函数--监听用户下拉动作*/onPullDownRefresh() {},/*** 页面上拉触底事件的处理函数*/onReachBottom() {},/*** 用户点击右上角分享*/onShareAppMessage() {}
})

vote/addvote.wxml

<view class="container"><button class="title" bindtap="single">单选投票</button><button class="title" bindtap="many">多选投票</button></view>

 vote/addvote.wxss

.container {padding: 20rpx;
}button {margin-top: 60px;background-color: rgb(247, 198, 66);color: #fff;padding: 10rpx 20rpx;border-radius: 4rpx;text-align: center;} .title {font-size: 30rpx;font-weight: bold;margin-bottom: 10rpx;
}

vote/addvote.js

Page({many: function () {console.log(111)wx.navigateTo ({url: '/pages/vote/many/many' // 跳转到目标页面的路径})},data: {radioOptions: ['选项1', '选项2', '选项3'],checkboxOptions: ['选项A', '选项B', '选项C'],voteTitle: '',voteDescription: '',selectedRadioIndex: -1,selectedCheckboxIndexes: []},onTitleInput(e) {this.setData({voteTitle: e.detail.value});},onDescriptionInput(e) {this.setData({voteDescription: e.detail.value});},onRadioChange(e) {this.setData({selectedRadioIndex: e.detail.value});},onCheckboxChange(e) {this.setData({selectedCheckboxIndexes: e.detail.value});},submitVote() {// 获取投票的标题、描述以及选择的选项const { voteTitle, voteDescription, selectedRadioIndex, selectedCheckboxIndexes } = this.data;// TODO: 处理提交投票逻辑,可以发送请求给服务器等// 打印投票结果console.log('投票标题:', voteTitle);console.log('投票描述:', voteDescription);console.log('单选投票选项:', selectedRadioIndex);console.log('多选投票选项:', selectedCheckboxIndexes);},/*** 页面的初始数据*/data: {},/*** 生命周期函数--监听页面加载*/onLoad(options) {},/*** 生命周期函数--监听页面初次渲染完成*/onReady() {},/*** 生命周期函数--监听页面显示*/onShow() {},/*** 生命周期函数--监听页面隐藏*/onHide() {},/*** 生命周期函数--监听页面卸载*/onUnload() {},/*** 页面相关事件处理函数--监听用户下拉动作*/onPullDownRefresh() {},/*** 页面上拉触底事件的处理函数*/onReachBottom() {},/*** 用户点击右上角分享*/onShareAppMessage() {}
})

vote/many.wxml

<!--pages/vote/many/many.wxml--><view class="container" >  <input class="title" placeholder="会议标题" /><!-- <input class="title" value="{{title}}" disabled /> --><view class="line"></view><input class="info" placeholder="投票标题" bindinput="titleChange"/><view class="line"></view><view id="scrollContainer"><view wx:for="{{inputList}}" wx:key="index" class="select"><image class="select-add" src="../../../static/vote/减.png" bindtap="hideInput"data-index="{{index}}"></image><input class="vote-content" placeholder="请输入内容" data-index="{{index}}"value="{{item.value}}" bindinput="inputChange"></input></view><view class="select" style="margin-left: 4px;"><image class="select-add" src="../../../static/vote/添加_填充.png" bindtap="showInput"></image><view class="select-content" bindtap="showInput"> 添加选项</view></view><button class="submit-button" style="top: {{buttonTop}}rpx;" bindtap="submitVote">提交</button>
</view></view>

 vote/many.wxss

/* pages/vote/many/many.wxss */
.container {display: flex;flex-direction: column;align-items: center;
} .title{height: 200px;font-weight: bolder;font-size: 70rpx;margin-left: 20px;margin-top: -160px;margin-bottom: -50px;
}
.line {width: 100%;height: 1px;background-color: #ccc;
}
.info{
height: 70px;
width:380px;
}
.select{display: flex;
flex-direction: row;
align-self: start;
}
.select-add {
margin-left: 15px;margin-top: 15px;width: 50rpx;height: 50rpx;
}
.select-content{margin-top: 10px;margin-left: 7px;display: flex;align-items: center;
}.vote-content {width: 200rpx;height: 30rpx;margin-top: 17px;margin-left: 7px;display: flex;align-items: center;
}
.scroll-container {height: 500rpx; /* 设置合适的高度,使其可以滚动 */
}.submit-button {
margin-top: 20px;width: 200rpx;height: 60rpx;background-color: rgb(241, 189, 46);
}

vote/many.js

const app = getApp()
const api = require("../../../config/api.js")
const util = require("../../../utils/util")Page({data: {inputList: [],voteTitle: ''},// 显示输入框showInput: function () {const length = this.data.inputList.length;const newInputList = [...this.data.inputList];newInputList.push({value: ''});this.setData({inputList: newInputList});},// 输入框内容改变inputChange: function (event) {const index = event.currentTarget.dataset.index;const value = event.detail.value;const newInputList = [...this.data.inputList];newInputList[index].value = value;this.setData({inputList: newInputList});},// 删除输入框hideInput: function (event) {const index = event.currentTarget.dataset.index;const newInputList = [...this.data.inputList];newInputList.splice(index, 1);this.setData({inputList: newInputList});},// 投票标题改变titleChange: function (event) {const value = event.detail.value;this.setData({voteTitle: value});},// 提交投票submitVote: function () {// 获取投票标题和选项内容const title = this.data.voteTitle;const options = this.data.inputList.map(item => item.value);// 打印标题和选项console.log('投票标题:', title);console.log('投票选项:', options);let data = {title: title,options: options,};util.request(api.VoteADD, data, 'POST').then(res => {console.log(res)// 清空投票标题和选项内容this.setData({voteTitle: '',inputList: []});})},/*** 生命周期函数--监听页面加载*/onLoad(options) {},/*** 生命周期函数--监听页面初次渲染完成*/onReady() {},/*** 生命周期函数--监听页面显示*/onShow() {},/*** 生命周期函数--监听页面隐藏*/onHide() {},/*** 生命周期函数--监听页面卸载*/onUnload() {},/*** 页面相关事件处理函数--监听用户下拉动作*/onPullDownRefresh() {},/*** 页面上拉触底事件的处理函数*/onReachBottom() {},/*** 用户点击右上角分享*/onShareAppMessage() {}
})

好啦,今天的分享就到这了,希望能够帮到你呢!😊😊  

相关文章:

【微信小程序开发】小程序微信用户授权登录(用户信息手机号)

&#x1f973;&#x1f973;Welcome Huihuis Code World ! !&#x1f973;&#x1f973; 接下来看看由辉辉所写的关于小程序的相关操作吧 目录 &#x1f973;&#x1f973;Welcome Huihuis Code World ! !&#x1f973;&#x1f973; 授权流程讲解 一.用户信息授权登录 1.w…...

VSCode 自动格式化

1.打开应用商店&#xff0c;搜索 prettier code formatter &#xff0c;选择第一个&#xff0c;点击安装。 2.安装完成后&#xff0c;点击文件&#xff0c;选择首选项&#xff0c;选择设置。 3.在搜索框内输入 save &#xff0c;勾选在保存时格式化文件。 4.随便打开一个文件&a…...

数据库、数据仓库相关

1. 数据库与数据仓库的区别 数据库 Database (Oracle, Mysql, PostgreSQL)主要用于事务处理。数据仓库 Datawarehouse (Amazon Redshift, Hive)主要用于数据分析。 数据库和数据仓库是两种不同的数据存储方式&#xff0c;它们的设计目的和使用场景也有所不同。数据库通常用于…...

【STM32】RCC时钟模块(使用HAL库)

https://gitee.com/linhir-linhir/stm32-f103-c8/blob/master/STM32%E6%9C%80%E6%96%B0%E5%9B%BA%E4%BB%B6%E5%BA%93v3.5/Libraries/STM32F10x_StdPeriph_Driver/inc/stm32f10x_rcc.h STM32最新固件库v3.5/Libraries/CMSIS/CM3/DeviceSupport/ST/STM32F10x/system_stm32f10x.c…...

WPF中的绑定知识详解(含案例源码分享)

✅作者简介&#xff1a;2022年博客新星 第八。热爱国学的Java后端开发者&#xff0c;修心和技术同步精进。 &#x1f34e;个人主页&#xff1a;Java Fans的博客 &#x1f34a;个人信条&#xff1a;不迁怒&#xff0c;不贰过。小知识&#xff0c;大智慧。 &#x1f49e;当前专栏…...

【JVM】类的生命周期

【JVM】类的生命周期 文章目录 【JVM】类的生命周期1. 生命周期概述2. 加载阶段3. 连接阶段3.1 验证3.2 准备3.3 解析 4. 初始化阶段4.1 触发初始化的方式4.2 clinit不存在的情况4.3 多个类的初始化 5. 总结 1. 生命周期概述 类的生命周期分为5/7个阶段&#xff1a; 加载(Loa…...

asp.net网上商城系统VS开发sqlserver数据库web结构c#编程Microsoft Visual Studio协同过滤设计

一、源码特点 asp.net网上商城系统是一套完善的web设计管理系统系统采用协同过滤算法进行商品推荐&#xff0c;系统具有完整的源代码和数据库&#xff0c;系统主要采用B/S模式开发。开发环境为vs2010&#xff0c;数据库 为sqlserver2008&#xff0c;使用c#语言开发 ASP…...

APUS入驻百度灵境矩阵,普惠AI大模型插件能力

10月17日&#xff0c;APUS出席百度世界大会2023。会上&#xff0c;百度公布了灵境矩阵业务进展&#xff0c;APUS作为灵境矩阵首批合作伙伴正与百度携手拓展大模型能力边界、构建大模型应用生态。 百度认为&#xff0c;大模型将繁荣AI应用生态&#xff0c;在生态搭建过程中&…...

通过C++调用Com接口

头文件 #include <iostream> #include <Windows.h> #include <comdef.h> #include <rpcdce.h> using namespace std; #pragma comment(lib, "Rpcrt4.lib")72C24DD5-D70A-438B-8A42-98424B88AFB8 通过Wscript.Shell来创建进程: void Wscri…...

完全背包问题

目录 1. 朴素解法 2. 优化 原题链接&#xff1a; 3. 完全背包问题 - AcWing题库 题目描述&#xff1a; 有 N 种物品和一个容量是 V 的背包&#xff0c;每种物品都有无限件可用。 第 i 种物品的体积是 vi&#xff0c;价值是 wi。 求解将哪些物品装入背包&#xff0c;可使这些…...

J2EE的N层体系结构

J2EE平台采用了多层分布式应用程序模型&#xff0c;实现不同逻辑功能的应用程序被封装到不同的构件中&#xff0c;处于不同层次的构件可被分别部署到不同的机器中。 RMI/IIOP&#xff1a;RMI&#xff08;Remote Method Invocation&#xff0c;远程方法调用&#xff09;是Java的…...

Quirks(怪癖)模式是什么?它和 Standards(标准)模式有什么区别?

目录 前言: 用法: 代码: Quirks模式示例: Standards模式示例: 理解: Quirks模式&#xff1a; Standards模式&#xff1a; 高质量讨论: 前言: "Quirks模式"和"Standards模式"是与HTML文档渲染模式相关的两种模式。它们影响着浏览器如何解释和渲染HT…...

自然语言处理---Transformer模型

Transformer概述 相比LSTM和GRU模型&#xff0c;Transformer模型有两个显著的优势&#xff1a; Transformer能够利用分布式GPU进行并行训练&#xff0c;提升模型训练效率。 在分析预测更长的文本时&#xff0c;捕捉间隔较长的语义关联效果更好。 Transformer模型的作用 基于seq…...

动画系统的前世今生(一)

掐指一算&#xff0c;五年没更新过我的CSDN账号啦&#xff0c;方向也从人工智能变成了计算机图形学&#xff0c;当然也依旧会关注AI的发展&#xff0c;之前在知乎上写了一些文章[传送门]&#xff0c;后续也会逐渐同步到CSDN上&#xff5e; 这个系列将包含五篇文章&#xff0c;内…...

11 结构型模式- 代理模式

结构性模式一共包括七种&#xff1a; 代理模式、桥接模式、装饰者模式、适配器模式、门面(外观)模式、组合模式、和享元模式。 1 代理模式介绍 软件开发中的代理&#xff1a; 代理模式中引入了一个新的代理对象,代理对象在客户端对象和目标对象之间起到了中介的作用,它去掉客…...

Unity--用户界面

目录 “使用工具栏”&#xff1a; “层次结构”窗口&#xff1a; 层次结构窗口 制作子GameObject “游戏”视图&#xff1a; “场景视图“&#xff1a; ”项目窗口“&#xff1a; 项目窗口工具栏&#xff1a; "Inspector" 窗口&#xff1a; Inspector 游戏…...

BUUCTF 乌镇峰会种图 1

BUUCTF:https://buuoj.cn/challenges 题目描述&#xff1a; 乌镇互联网大会召开了&#xff0c;各国巨头汇聚一堂&#xff0c;他们的照片里隐藏着什么信息呢&#xff1f;&#xff08;答案格式&#xff1a;flag&#xff5b;答案&#xff5d;&#xff0c;只需提交答案&#xff0…...

Runner GoUI自动化测试发布

构建自动化测试体系是当下每个项目团队愿意去做的&#xff0c;自动化测试减少重复操作节省人力成本。 RunnerGo UI自动化平台 RunnerGo提供从API管理到API性能再到可视化的API自动化、UI自动化测试功能模块&#xff0c;覆盖了整个产品测试周期。 RunnerGo UI自动化基于Selen…...

【Gensim概念】03/3 NLP玩转 word2vec

第三部分 对象函数 八 word2vec对象函数 该对象本质上包含单词和嵌入之间的映射。训练后&#xff0c;可以直接使用它以各种方式查询这些嵌入。有关示例&#xff0c;请参阅模块级别文档字符串。 类型 KeyedVectors 1&#xff09; add_lifecycle_event(event_name, log_level2…...

【网络协议】聊聊网络路由相关算法

如何配置路由 路由器是一台网络设备&#xff0c;多张网卡&#xff0c;当一个入口的网络包到达路由器时&#xff0c;会根据本地的信息库决定如何正确的转发流量&#xff0c;通常称为路由表 路由表主要包含如下 核心思想是根据目的 IP 地址来配置路由 目的网络&#xff1a;要去…...

大话软工笔记—需求分析概述

需求分析&#xff0c;就是要对需求调研收集到的资料信息逐个地进行拆分、研究&#xff0c;从大量的不确定“需求”中确定出哪些需求最终要转换为确定的“功能需求”。 需求分析的作用非常重要&#xff0c;后续设计的依据主要来自于需求分析的成果&#xff0c;包括: 项目的目的…...

JavaScript 中的 ES|QL:利用 Apache Arrow 工具

作者&#xff1a;来自 Elastic Jeffrey Rengifo 学习如何将 ES|QL 与 JavaScript 的 Apache Arrow 客户端工具一起使用。 想获得 Elastic 认证吗&#xff1f;了解下一期 Elasticsearch Engineer 培训的时间吧&#xff01; Elasticsearch 拥有众多新功能&#xff0c;助你为自己…...

蓝桥杯 2024 15届国赛 A组 儿童节快乐

P10576 [蓝桥杯 2024 国 A] 儿童节快乐 题目描述 五彩斑斓的气球在蓝天下悠然飘荡&#xff0c;轻快的音乐在耳边持续回荡&#xff0c;小朋友们手牵着手一同畅快欢笑。在这样一片安乐祥和的氛围下&#xff0c;六一来了。 今天是六一儿童节&#xff0c;小蓝老师为了让大家在节…...

高等数学(下)题型笔记(八)空间解析几何与向量代数

目录 0 前言 1 向量的点乘 1.1 基本公式 1.2 例题 2 向量的叉乘 2.1 基础知识 2.2 例题 3 空间平面方程 3.1 基础知识 3.2 例题 4 空间直线方程 4.1 基础知识 4.2 例题 5 旋转曲面及其方程 5.1 基础知识 5.2 例题 6 空间曲面的法线与切平面 6.1 基础知识 6.2…...

零基础设计模式——行为型模式 - 责任链模式

第四部分&#xff1a;行为型模式 - 责任链模式 (Chain of Responsibility Pattern) 欢迎来到行为型模式的学习&#xff01;行为型模式关注对象之间的职责分配、算法封装和对象间的交互。我们将学习的第一个行为型模式是责任链模式。 核心思想&#xff1a;使多个对象都有机会处…...

群晖NAS如何在虚拟机创建飞牛NAS

套件中心下载安装Virtual Machine Manager 创建虚拟机 配置虚拟机 飞牛官网下载 https://iso.liveupdate.fnnas.com/x86_64/trim/fnos-0.9.2-863.iso 群晖NAS如何在虚拟机创建飞牛NAS - 个人信息分享...

毫米波雷达基础理论(3D+4D)

3D、4D毫米波雷达基础知识及厂商选型 PreView : https://mp.weixin.qq.com/s/bQkju4r6med7I3TBGJI_bQ 1. FMCW毫米波雷达基础知识 主要参考博文&#xff1a; 一文入门汽车毫米波雷达基本原理 &#xff1a;https://mp.weixin.qq.com/s/_EN7A5lKcz2Eh8dLnjE19w 毫米波雷达基础…...

【FTP】ftp文件传输会丢包吗?批量几百个文件传输,有一些文件没有传输完整,如何解决?

FTP&#xff08;File Transfer Protocol&#xff09;本身是一个基于 TCP 的协议&#xff0c;理论上不会丢包。但 FTP 文件传输过程中仍可能出现文件不完整、丢失或损坏的情况&#xff0c;主要原因包括&#xff1a; ✅ 一、FTP传输可能“丢包”或文件不完整的原因 原因描述网络…...

FOPLP vs CoWoS

以下是 FOPLP&#xff08;Fan-out panel-level packaging 扇出型面板级封装&#xff09;与 CoWoS&#xff08;Chip on Wafer on Substrate&#xff09;两种先进封装技术的详细对比分析&#xff0c;涵盖技术原理、性能、成本、应用场景及市场趋势等维度&#xff1a; 一、技术原…...

高抗扰度汽车光耦合器的特性

晶台光电推出的125℃光耦合器系列产品&#xff08;包括KL357NU、KL3H7U和KL817U&#xff09;&#xff0c;专为高温环境下的汽车应用设计&#xff0c;具备以下核心优势和技术特点&#xff1a; 一、技术特性分析 高温稳定性 采用先进的LED技术和优化的IC设计&#xff0c;确保在…...