小程序之微信登录授权(6)

news2024/11/17 6:27:29

                                                ⭐⭐ 小程序专栏:小程序开发专栏

                                                ⭐⭐ 个人主页:个人主页


        

目录

        

一.了解微信授权登录

小程序登录授权基本原理:

二.微信授权登录演示

三.微信授权与后端的交互

        3.1后台代码:

        3.2 前端代码:

 四.微信退出

 五.微信表情包存储问题

今天的分享就到这啦!!!


一.了解微信授权登录

        微信登录官网:

开放能力 / 用户信息 / 小程序登录 (qq.com)icon-default.png?t=N7T8https://developers.weixin.qq.com/miniprogram/dev/framework/open-ability/login.html        小程序微信登录图解:

        这张图包含了整个微信授权的流程,登录授权是一个涉及前后端协作的过程,前端需要与后端服务器进行数据交互。后端服务器需要处理登录请求、校验凭证、生成用户标识等操作

小程序登录授权基本原理:

        小程序登录授权的基本原理是通过微信用户的微信账号来进行身份验证。用户在小程序中点击登录按钮后,小程序会调用微信提供的登录接口,将登录凭证code发送给开发者的后台服务器。后台服务器通过微信提供的接口,使用code换取用户的唯一标识openid和会话密钥session_key。开发者可以使用openid标识用户的唯一身份,session_key用于解密用户敏感数据。

二.微信授权登录演示

        微信提供了两种方法  wx.login 和  wx.getUserProfile 官方是推荐使用第二种因为第二种是必须要通过用户的授权才可以获取信息,而第一种则是直接获取        

wx.login:

wxLogin: function(e) {
    // debugger
    console.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;
    }
    
  },

​​​​​​​wx.getUserProfile:

 getUserProfile(e) {
    console.log('getUserProfile')
    // 推荐使用 wx.getUserProfile 获取用户信息,开发者每次通过该接口获取用户个人信息均需用户确认
    // 开发者妥善保管用户快速填写的头像昵称,避免重复弹窗
    wx.getUserProfile({
      desc: '用于完善会员资料', // 声明获取用户个人信息后的用途,后续会展示在弹窗中,请谨慎填写
      success: (res) => {
        console.log(res);
        this.setData({
          userInfo: res.userInfo,
          hasUserInfo: true
        })
      }
    })
  },

效果演示:

三.微信授权与后端的交互

        3.1后台代码:

 后端配置appid+appsecret与数据库连接:

微信后台授权:登录的接口

/**
*微信授权
*/
@Slf4j
@RestController
@RequestMapping("/wx/auth")
public class WxAuthController {
    @Autowired
    private WxMaService wxService;
    @Autowired
    private 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及sessionKey
        String 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 id
            openId = 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();
            }
        }
        // token
        UserToken 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);
    }

        3.2 前端代码:

        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>

       login.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) {
        // 推荐使用wx.getUserProfile获取用户信息,开发者每次通过该接口获取用户个人信息均需用户确认
        // 开发者妥善保管用户快速填写的头像昵称,避免重复弹窗
        wx.getUserProfile({
            desc: '用于完善会员资料', // 声明获取用户个人信息后的用途,后续会展示在弹窗中,请谨慎填写
            success: (res) => {
                //console.log(res);
                debugger
                user.checkLogin().catch(() => {
                    user.loginByWeixin(res.userInfo).then(res => {
                      app.globalData.hasLogin = true;
                      debugger
                      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) {
        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('开发中....')
    }

})

config/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',
  AuthLoginByWeixin: WxApiRoot + 'auth/login_by_weixin', //微信登录
  UserIndex: WxApiRoot + 'user/index', //个人页面用户相关信息
  AuthLogout: WxApiRoot + 'auth/logout', //账号登出
  AuthBindPhone: WxApiRoot + 'auth/bindPhone' //绑定微信手机号
};

微信登录工具类utils/util.js:

function formatTime(date) {
  var year = date.getFullYear()
  var month = date.getMonth() + 1
  var day = date.getDate()

  var hour = date.getHours()
  var minute = date.getMinutes()
  var second = date.getSeconds()

  return [year, month, day].map(formatNumber).join('-') + ' ' + [hour, minute, second].map(formatNumber).join(':')
}

function formatNumber(n) {
  n = n.toString()
  return n[1] ? n : '0' + n
}


/**
 * 封封微信的的request
 */
function request(url, data = {}, method = "GET") {
  return new Promise(function (resolve, reject) {
    wx.request({
      url: url,
      data: data,
      method: method,
      timeout:3000,
      header: {
        'Content-Type': 'application/json',
        'X-OA-Token': wx.getStorageSync('token')
      },
      success: function (res) {
        if (res.statusCode == 200) {
          if (res.data.errno == 501) {
            // 清除登录相关内容
            try {
              wx.removeStorageSync('userInfo');
              wx.removeStorageSync('token');
            } catch (e) {
              // Do something when catch error
            }
            // 切换到登录页面
            wx.navigateTo({
              url: '/pages/auth/login/login'
            });
          } else {
            resolve(res.data);
          }
        } else {
          reject(res.errMsg);
        }

      },
      fail: function (err) {
        reject(err)
      }
    })
  });
}

function redirect(url) {
  //判断页面是否需要登录
  if (false) {
    wx.redirectTo({
      url: '/pages/auth/login/login'
    });
    return false;
  } else {
    wx.redirectTo({
      url: url
    });
  }
}

function showErrorToast(msg) {
  wx.showToast({
    title: msg,
    image: '/static/images/icon_error.png'
  })
}

function jhxLoadShow(message) {
  if (wx.showLoading) { // 基础库 1.1.0 微信6.5.6版本开始支持,低版本需做兼容处理
    wx.showLoading({
      title: message,
      mask: true
    });
  } else { // 低版本采用Toast兼容处理并将时间设为20秒以免自动消失
    wx.showToast({
      title: message,
      icon: 'loading',
      mask: true,
      duration: 20000
    });
  }
}

function jhxLoadHide() {
  if (wx.hideLoading) { // 基础库 1.1.0 微信6.5.6版本开始支持,低版本需做兼容处理
    wx.hideLoading();
  } else {
    wx.hideToast();
  }
}

module.exports = {
  formatTime,
  request,
  redirect,
  showErrorToast,
  jhxLoadShow,
  jhxLoadHide
}

判断用户是否登录 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,
};

效果演示:

 四.微信退出

前端代码和上面都是一样的,只需添加一个退出的方法:

 /**
     * 注销登录
     */
    @PostMapping("logout")
    public Object logout(@LoginUser Integer userId) {
        log.info("【请求开始】注销登录,请求参数,userId:{}", userId);
        if (userId == null) {
            return ResponseUtil.unlogin();
        }
        try {
            UserTokenManager.removeToken(userId);
        } catch (Exception e) {
            log.error("注销登录出错:userId:{}", userId);
            e.printStackTrace();
            return ResponseUtil.fail();
        }

        log.info("【请求结束】注销登录成功!");
        return ResponseUtil.ok();
    }
}

前台js代码 user.js:

 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'
        });
      }
    })
  }

user.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='phone' 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>

效果:

 五.微信表情包存储问题

在我们建立mysql的包下有一个my.ini的文件,将utf8改为utf8mb4,即可

今天的分享就到这啦!!!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1121407.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

DELM深度极限学习机回归预测研究(Matlab代码实现)

&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5; &#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜密&#xff0c;逻辑清晰&#xff0c;为了方便读者。 ⛳️座右铭&a…

自然语言处理---Self Attention自注意力机制

Self-attention介绍 Self-attention是一种特殊的attention&#xff0c;是应用在transformer中最重要的结构之一。attention机制&#xff0c;它能够帮助找到子序列和全局的attention的关系&#xff0c;也就是找到权重值wi。Self-attention相对于attention的变化&#xff0c;其实…

项目总结-商品购买流程

&#xff08;1&#xff09;添加购物车 Controller&#xff1a; CartService&#xff1a; 实现类&#xff1a; CartDetail detaildao.queryByCdid(cid,gds.getId()); CartDao&#xff1a; //获取详情对象Select("select * from t_cartdetail where cid#{cid} and gid#{gid…

buu第五页 wp

[RootersCTF2019]babyWeb 预期解 一眼就是sql注入&#xff0c;发现过滤了 UNION SLEEP " OR - BENCHMARK盲注没法用了&#xff0c;因为union被过滤&#xff0c;堆叠注入也不考虑&#xff0c;发现报错有回显&#xff0c;尝试报错注入。 尝试&#xff1a; 1||(updatex…

ubuntu20.04下安装nc

前言 nc在网络渗透测试中非常好用&#xff0c;这里的主要记一下Ubuntu20.04中nc的安装 编译安装 第一种方式是自己编译安装&#xff0c;先下载安装包 nc.zip wget http://sourceforge.net/projects/netcat/files/netcat/0.7.1/netcat-0.7.1.tar.gz/download -O netcat-0.7.…

anyproxy 的安装和抓包使用

简介 AnyProxy是阿里开发的开源的代理服务器&#xff0c;主要特性包括&#xff1a; 基于Node.js&#xff0c;开放二次开发能力&#xff0c;允许自定义请求处理逻辑支持Https的解析提供GUI界面&#xff0c;用以观察请求 安装运行Anyproxy 首先需要电脑由安装 node&#xff0…

H3C SecParh堡垒机 data_provider.php 远程命令执行漏洞

构造poc执行远程命令&#xff1a; /audit/data_provider.php?ds_y2019&ds_m04&ds_d02&ds_hour09&ds_min40&server_cond&service$(id)&identity_cond&query_typeall&formatjson&browsetrue漏洞证明&#xff1a; 文笔生疏&#xff0c…

【大模型应用开发教程】02_LangChain介绍

LangChain介绍 什么是 LangChain1. 模型输入/输出2. 数据连接3. 链&#xff08;Chain&#xff09;4. 记忆&#xff08;Meomory&#xff09;5. 代理&#xff08;Agents&#xff09;6.回调&#xff08;Callback&#xff09;在哪里传入回调 ?你想在什么时候使用这些东西呢&#x…

1024常玩到的漏洞(第十六课)

1024常玩到的两个漏洞(第十六课) 漏洞扫描工具 1024渗透OpenVas扫描工具使用(第十四课)-CSDN博客 流程 一 ms12-020漏洞分析 MS12-020漏洞是一种远程桌面协议(RDP)漏洞。在攻击者利用该漏洞之前,它需要将攻击者的计算机连接到受害者的计算机上。攻击者可以通过向受害者计算…

跟着NatureMetabolism学作图:R语言ggplot2转录组差异表达火山图

论文 Independent phenotypic plasticity axes define distinct obesity sub-types https://www.nature.com/articles/s42255-022-00629-2#Sec15 s42255-022-00629-2.pdf 论文中没有公开代码&#xff0c;但是所有作图数据都公开了&#xff0c;我们可以试着用论文中提供的数据…

Linux进程与线程的内核实现

进程描述符task_struct 进程描述符&#xff08;struct task_struct&#xff09;pid与tgid进程id编号分配规则内存管理mm_struct进程与文件,文件系统 进程,线程创建的本质 clone函数原型线程创建的实现进程创建的实现 总结 进程描述符task_struct 进程描述符&#xff08;st…

自动驾驶的商业应用和市场前景

自动驾驶技术已经成为了交通运输领域的一项重要创新。它不仅在改善交通安全性和效率方面具有巨大潜力&#xff0c;还为各种商业应用提供了新的机会。本文将探讨自动驾驶在交通运输中的潜力&#xff0c;自动驾驶汽车的制造商和技术公司&#xff0c;以及自动驾驶的商业模式和市场…

Git GUI工具:SourceTree代码管理

Git GUI工具&#xff1a;SourceTree SourceTreeSourceTree的安装SourceTree的使用 总结 SourceTree 当我们对Git的提交、分支已经非常熟悉&#xff0c;可以熟练使用命令操作Git后&#xff0c;再使用GUI工具&#xff0c;就可以更高效。 Git有很多图形界面工具&#xff0c;这里…

JAVA高级教程Java 泛型(10)

目录 三、泛型的使用泛型类泛型接口泛型方法 四、泛型在集合中的使用1、使用泛型来创建set2、使用泛型来创建HashSet3、使用equals&#xff0c;hashCode的使用Person 类HashSet的使用泛型TreeSet的使用泛型comparator实现定制比较(比较器) 三、泛型的使用 泛型类语法 类名T表示…

散列表:如何打造一个工业级水平的散列表?

文章来源于极客时间前google工程师−王争专栏。 散列表的查询效率并不能笼统地说成是O(1)。它跟散列函数、装载因子、散列冲突等都有关系。如果散列函数设计得不好&#xff0c;或者装载因子过高&#xff0c;都可能导致散列冲突发生的概率升高&#xff0c;查询效率下降。 极端情…

在ESP32上使用Arduino(Arduino as an ESP-IDF component)

目录 前言 原理说明 操作步骤 下载esp-arduino 安装esp-arduino 工程里配置arduino 1、勾选该选项&#xff0c;工程将作为一个标准的arduino程序工作 2、不勾选该选型&#xff0c;工程将作为一个传统的嵌入式项目开发&#xff0c; 前言 Arduino拥有丰富的各类库&#…

一款WPF开发的网易云音乐客户端 - DMSkin-CloudMusic

前言 今天推荐一款基于DMSkin框架开发的网易云音乐播放器&#xff1a;DMSkin-CloudMusic。 DMSkin 框架介绍 DMSkin是一个开源的WPF样式UI框架&#xff0c;可以帮助开发者快速创建漂亮的用户界面。 下载体验 下载地址&#xff1a;https://github.com/944095635/DMSkin-Clou…

如何使用vim粘贴鼠标复制的内容

文章目录 一、使用步骤1.找到要编辑的配置文件2.找到目标文件3.再回到vim编辑器 一、使用步骤 1.找到要编辑的配置文件 用sudo vim /etc/apt/sources.list编辑软件源配置文件 sudo vim /etc/apt/sources.listvim 在默认的情况下当鼠标选中的时候进入的 Visual 模式&#xff…

加法器:如何像搭乐高一样搭电路(上)?

目录 背景 异或门和半加器 全加器 小结 补充阅读 背景 上一讲&#xff0c;我们看到了如何通过电路&#xff0c;在计算机硬件层面设计最基本的单元&#xff0c;门电路。我给你看的门电路非常简单&#xff0c;只能做简单的 “与&#xff08;AND&#xff09;”“或&#xff…

UG\NX二次开发 获取用户默认设置中的绘图信息 UF_PLOT_ask_session_job_options

文章作者:里海 来源网站:《里海NX二次开发3000例专栏》 感谢粉丝订阅 感谢 m0_58724732 订阅本专栏,非常感谢。 简介 UG\NX二次开发 获取用户默认设置中的绘图信息 UF_PLOT_ask_session_job_options 效果 代码 #include "me.hp