【前端Vue】社交信息头条项目完整笔记第2篇:二、登录注册,准备【附代码文档】

news2024/11/17 14:51:41

社交媒体-信息头条项目完整开发笔记完整教程(附代码资料)主要内容讲述:一、项目初始化使用 Vue CLI 创建项目,加入 Git 版本管理,调整初始目录结构,导入图标素材,引入 Vant 组件库,移动端 REM 适配,关于 , 配置文件,封装请求模块。十、用户关注 & 粉丝。十一、我的收藏/历史。十二、编辑用户资料创建组件并配置路由,页面布局,展示用户信息,修改昵称,修改性别,修改生日,修改头像。二、登录注册准备,实现基本登录功能,登录状态提示,表单验证,验证码处理,处理用户 Token,优化封装本地存储操作模块,关于 Token 过期问题。三、个人中心TabBar 处理,页面布局,处理已登录和未登录的页面展示,用户退出,展示登录用户信息,优化设置 Token。四、首页—文章列表页面布局,频道列表,文章列表,文章列表项。五、首页—频道编辑处理页面弹出层,创建频道编辑组件,页面布局,展示我的频道,展示推荐频道列表,添加频道,编辑频道,频道数据持久化,正确的获取首页频道列表数据。六、文章搜索创建组件并配置路由,页面布局,处理页面显示状态,搜索联想建议,搜索结果,搜索历史记录。七、文章详情创建组件并配置路由,页面布局,关于后端返回数据中的大数字问题,展示文章详情,处理内容加载状态,关于文章正文的样式,图片点击预览,关注用户,文章收藏,文章点赞。八、文章评论展示文章评论列表,评论点赞,发布文章评论,评论回复。九、用户页面创建组件并配置路由,页面布局,展示用户信息,用户关注,展示用户文章列表。

全套笔记资料代码移步: 前往gitee仓库查看

感兴趣的小伙伴可以自取哦,欢迎大家点赞转发~


全套教程部分目录:


部分文件图片:

二、登录注册

目标

  • 能实现登录页面的布局
  • 能实现基本登录功能
  • 能掌握 Vant 中 Toast 提示组件的使用
  • 能理解 API 请求模块的封装
  • 能理解发送验证码的实现思路
  • 能理解 Vant Form 实现表单验证的使用
image-20200228161041266

准备

创建组件并配置路由

1、创建 src/views/login/index.vue 并写入以下内容

<template>
  <div class="login-container">登录页面</div>
</template>

<script>
export default {
  name: 'LoginPage',
  components: {},
  props: {},
  data () {
    return {}
  },
  computed: {},
  watch: {},
  created () {},
  mounted () {},
  methods: {}
}
</script>

<style scoped lang="less"></style>

2、然后在 src/router/index.js 中配置登录页的路由表

{
  path: '/login',
  name: 'login',
  component: () => import('@/views/login')
}

最后,访问 /login 查看是否能访问到登录页面。

image-20200229115109026

布局结构

这里主要使用到三个 Vant 组件:

  • [NavBar 导航栏](
  • [Form 表单](
    • [Field 输入框](
    • [Button 按钮](

一个经验:使用组件库中的现有组件快速布局,再慢慢调整细节,效率更高(刚开始可能会感觉有点麻烦,越用越熟,慢慢的就有了自己的思想)。

布局样式

写样式的原则:将公共样式写到全局(src/styles/index.less),将局部样式写到组件内部。

1、src/styles/index.less

body {
  background-color: #f5f7f9;
}

.page-nav-bar {
  background-color: #3296fa;
  .van-nav-bar__title {
    color: #fff;
  }
}

2、src/views/login/index.vue

<template>
  <div class="login-container">
    <!-- 导航栏 -->
    <van-nav-bar class="page-nav-bar" title="登录" />
    <!-- /导航栏 -->

    <!-- 登录表单 -->
    <van-form @submit="onSubmit">
      <van-field
        name="用户名"
        placeholder="请输入手机号"
      >
        <i slot="left-icon" class="toutiao toutiao-shouji"></i>
      </van-field>
      <van-field
        type="password"
        name="验证码"
        placeholder="请输入验证码"
      >
        <i slot="left-icon" class="toutiao toutiao-yanzhengma"></i>
        <template #button>
          <van-button class="send-sms-btn" round size="small" type="default">发送验证码</van-button>
        </template>
      </van-field>
      <div class="login-btn-wrap">
        <van-button class="login-btn" block type="info" native-type="submit">
          登录
        </van-button>
      </div>
    </van-form>
    <!-- /登录表单 -->
  </div>
</template>

<script>
export default {
  name: 'LoginIndex',
  components: {},
  props: {},
  data () {
    return {
    }
  },
  computed: {},
  watch: {},
  created () {},
  mounted () {},
  methods: {
    onSubmit (values) {
      console.log('submit', values)
    }
  }
}
</script>

<style scoped lang="less">
.login-container {
  .toutiao {
    font-size: 37px;
  }
  .send-sms-btn {
    width: 152px;
    height: 46px;
    line-height: 46px;
    background-color: #ededed;
    font-size: 22px;
    color: #666;
  }
  .login-btn-wrap {
    padding: 53px 33px;
    .login-btn {
      background-color: #6db4fb;
      border: none;
    }
  }
}
</style>

实现基本登录功能

思路:

  • 注册点击登录的事件
  • 获取表单数据(根据接口要求使用 v-model 绑定)
  • 表单验证
  • 发请求提交
  • 根据请求结果做下一步处理

一、根据接口要求绑定获取表单数据

1、在登录页面组件的实例选项 data 中添加 user 数据字段

...
data () {
  return {
    user: {
      mobile: '',
      code: ''
    }
  }
}

2、在表单中使用 v-model 绑定对应数据

<!-- van-cell-group 仅仅是提供了一个上下外边框,能看到包裹的区域 -->
<van-cell-group>
  <van-field
    v-model="user.mobile"
    required
    clearable
    label="手机号"
    placeholder="请输入手机号"
  />

  <van-field
    v-model="user.code"
    type="number"
    label="验证码"
    placeholder="请输入验证码"
    required
  />
</van-cell-group>

最后测试。

一个小技巧:使用 VueDevtools 调试工具查看是否绑定成功。

二、请求登录

1、创建 src/api/user.js 封装请求方法

/**
 * 用户相关的请求模块
 */
import request from "@/utils/request"

/**
 * 用户登录
 */
export const login = data => {
  return request({
    method: 'POST',
    url: '/app/v1_0/authorizations',
    data
  })
}

2、给登录按钮注册点击事件

async onLogin () {
  try {
    const res = await login(this.user)
    console.log('登录成功', res)
  } catch (err) {
    if (err.response.status === 400) {
     	console.log('登录失败', err)
    }
  }
}

最后测试。

登录状态提示

Vant 中内置了[Toast 轻提示](

// 简单文字提示
Toast("提示内容");

// loading 转圈圈提示
Toast.loading({
  duration: 0, // 持续展示 toast
  message: "加载中...",
  forbidClick: true // 是否禁止背景点击
});

// 成功提示
Ttoast.success("成功文案");

// 失败提示
Toast.fail("失败文案");

提示:在组件中可以直接通过 this.$toast 调用。

另外需要注意的是:Toast 默认采用单例模式,即同一时间只会存在一个 Toast,如果需要在同一时间弹出多个 Toast,可以参考下面的示例

Toast.allowMultiple();

const toast1 = Toast('第一个 Toast');
const toast2 = Toast.success('第二个 Toast');

toast1.clear();
toast2.clear();

下面是为我们的登录功能增加 toast 交互提示。

async onLogin () {
  // 开始转圈圈
  this.$toast.loading({
    duration: 0, // 持续时间,0表示持续展示不停止
    forbidClick: true, // 是否禁止背景点击
    message: '登录中...' // 提示消息
  })

  try {
    const res = await request({
      method: 'POST',
      url: '/app/v1_0/authorizations',
      data: this.user
    })
    console.log('登录成功', res)
    // 提示 success 或者 fail 的时候,会先把其它的 toast 先清除
    this.$toast.success('登录成功')
  } catch (err) {
    console.log('登录失败', err)
    this.$toast.fail('登录失败,手机号或验证码错误')
  }
}

假如请求非常快的话就看不到 loading 效果了,这里可以手动将调试工具中的网络设置为慢速网络。

测试结束,再把网络设置恢复为 Online 正常网络。

表单验证

参考文档:[Form 表单验证](

<template>
  <div class="login-container">
    <!-- 导航栏 -->
    <van-nav-bar class="page-nav-bar" title="登录" />
    <!-- /导航栏 -->

    <!-- 登录表单 -->
    <!--
      表单验证:
        1、给 van-field 组件配置 rules 验证规则
          参考文档:
        2、当表单提交的时候会自动触发表单验证
           如果验证通过,会触发 submit 事件
           如果验证失败,不会触发 submit
     -->
    <van-form @submit="onSubmit">
      <van-field
        v-model="user.mobile"
        name="手机号"
        placeholder="请输入手机号"
        :rules="userFormRules.mobile"
        type="number"
        maxlength="11"
      >
        <i slot="left-icon" class="toutiao toutiao-shouji"></i>
      </van-field>
      <van-field
        v-model="user.code"
        name="验证码"
        placeholder="请输入验证码"
        :rules="userFormRules.code"
        type="number"
        maxlength="6"
      >
        <i slot="left-icon" class="toutiao toutiao-yanzhengma"></i>
        <template #button>
          <van-button class="send-sms-btn" round size="small" type="default">发送验证码</van-button>
        </template>
      </van-field>
      <div class="login-btn-wrap">
        <van-button class="login-btn" block type="info" native-type="submit">
          登录
        </van-button>
      </div>
    </van-form>
    <!-- /登录表单 -->
  </div>
</template>

<script>
import { login } from '@/api/user'

export default {
  name: 'LoginIndex',
  components: {},
  props: {},
  data () {
    return {
      user: {
        mobile: '', // 手机号
        code: '' // 验证码
      },
      userFormRules: {
        mobile: [{
          required: true,
          message: '手机号不能为空'
        }, {
          pattern: /^1[3|5|7|8]\d{9}$/,
          message: '手机号格式错误'
        }],
        code: [{
          required: true,
          message: '验证码不能为空'
        }, {
          pattern: /^\d{6}$/,
          message: '验证码格式错误'
        }]
      }
    }
  },
  computed: {},
  watch: {},
  created () {},
  mounted () {},
  methods: {
    async onSubmit () {
      // 1. 获取表单数据
      const user = this.user

      // TODO: 2. 表单验证

      // 3. 提交表单请求登录
      this.$toast.loading({
        message: '登录中...',
        forbidClick: true, // 禁用背景点击
        duration: 0 // 持续时间,默认 2000,0 表示持续展示不关闭
      })

      try {
        const res = await login(user)
        console.log('登录成功', res)
        this.$toast.success('登录成功')
      } catch (err) {
        if (err.response.status === 400) {
          this.$toast.fail('手机号或验证码错误')
        } else {
          this.$toast.fail('登录失败,请稍后重试')
        }
      }

      // 4. 根据请求响应结果处理后续操作
    }
  }
}
</script>

<style scoped lang="less">
.login-container {
  .toutiao {
    font-size: 37px;
  }

  .send-sms-btn {
    width: 152px;
    height: 46px;
    line-height: 46px;
    background-color: #ededed;
    font-size: 22px;
    color: #666;
  }

  .login-btn-wrap {
    padding: 53px 33px;
    .login-btn {
      background-color: #6db4fb;
      border: none;
    }
  }
}
</style>

验证码处理

验证手机号

async onSendSms () {
  console.log('onSendSms')
  // 1. 校验手机号
  try {
    await this.$refs.loginForm.validate('mobile')
  } catch (err) {
    return console.log('验证失败', err)
  }

  // 2. 验证通过,显示倒计时
  // 3. 请求发送验证码
}

使用倒计时组件

1、在 data 中添加数据用来控制倒计时的显示和隐藏

data () {
  return {
    ...
    isCountDownShow: false
  }
}

2、使用倒计时组件

<van-field
  v-model="user.code"
  placeholder="请输入验证码"
>
  <i class="icon icon-mima" slot="left-icon"></i>
  <van-count-down
    v-if="isCountDownShow"
    slot="button"
    :time="1000 * 5"
    format="ss s"
    @finish="isCountDownShow = false"
  />
  <van-button
    v-else
    slot="button"
    size="small"
    type="primary"
    round
    @click="onSendSmsCode"
  >发送验证码</van-button>
</van-field>

发送验证码

1、在 api/user.js 中添加封装数据接口

export const getSmsCode = mobile => {
  return request({
    method: 'GET',
    url: `/app/v1_0/sms/codes/${mobile}`
  })
}

2、给发送验证码按钮注册点击事件

3、发送处理

async onSendSms () {
  // 1. 校验手机号
  try {
    await this.$refs.loginForm.validate('mobile')
  } catch (err) {
    return console.log('验证失败', err)
  }

  // 2. 验证通过,显示倒计时
  this.isCountDownShow = true

  // 3. 请求发送验证码
  try {
    await sendSms(this.user.mobile)
    this.$toast('发送成功')
  } catch (err) {
    // 发送失败,关闭倒计时
    this.isCountDownShow = false
    if (err.response.status === 429) {
      this.$toast('发送太频繁了,请稍后重试')
    } else {
      this.$toast('发送失败,请稍后重试')
    }
  }
}

处理用户 Token

image-20200329121650635

Token 是用户登录成功之后服务端返回的一个身份令牌,在项目中的多个业务中需要使用到:

  • 访问需要授权的 API 接口
  • 校验页面的访问权限

但是我们只有在第一次用户登录成功之后才能拿到 Token。

所以为了能在其它模块中获取到 Token 数据,我们需要把它存储到一个公共的位置,方便随时取用。

往哪儿存?

  • 本地存储
    • 获取麻烦
    • 数据不是响应式
  • Vuex 容器(推荐)
    • 获取方便
    • 响应式的

使用容器存储 Token 的思路:

image-20200109192157006

  • 登录成功,将 Token 存储到 Vuex 容器中
    • 获取方便
    • 响应式
  • 为了持久化,还需要把 Token 放到本地存储
    • 持久化

下面是具体实现。

1、在 src/store/index.js

import Vue from 'vue'
import Vuex from 'vuex'

Vue.use(Vuex)

export default new Vuex.Store({
  state: {
    // 用户的登录状态信息
    user: JSON.parse(window.localStorage.getItem('TOUTIAO_USER'))
    // user: null
  },
  mutations: {
    setUser (state, user) {
      state.user = user
      window.localStorage.setItem('TOUTIAO_USER', JSON.stringify(user))
    }
  },
  actions: {
  },
  modules: {
  }
})

2、登录成功以后将后端返回的 token 相关数据存储到容器中

async onLogin () {
  // const loginToast = this.$toast.loading({
  this.$toast.loading({
    duration: 0, // 持续时间,0表示持续展示不停止
    forbidClick: true, // 是否禁止背景点击
    message: '登录中...' // 提示消息
  })

  try {
    const res = await login(this.user)

    // res.data.data => { token: 'xxx', refresh_token: 'xxx' }
+    this.$store.commit('setUser', res.data.data)

    // 提示 success 或者 fail 的时候,会先把其它的 toast 先清除
    this.$toast.success('登录成功')
  } catch (err) {
    console.log('登录失败', err)
    this.$toast.fail('登录失败,手机号或验证码错误')
  }

  // 停止 loading,它会把当前页面中所有的 toast 都给清除
  // loginToast.clear()
}

优化封装本地存储操作模块

创建 src/utils/storage.js 模块。

export const getItem = name => {
  const data = window.localStorage.getItem(name)
  try {
    return JSON.parse(data)
  } catch (err) {
    return data
  }
}

export const setItem = (name, value) => {
  if (typeof value === 'object') {
    value = JSON.stringify(value)
  }
  window.localStorage.setItem(name, value)
}

export const removeItem = name => {
  window.localStorage.removeItem(name)
}

关于 Token 过期问题

登录成功之后后端会返回两个 Token:

  • token:访问令牌,有效期2小时
  • refresh_token:刷新令牌,有效期14天,用于访问令牌过期之后重新获取新的访问令牌

我们的项目接口中设定的 Token 有效期是 2 小时,超过有效期服务端会返回 401 表示 Token 无效或过期了。

为什么过期时间这么短?

  • 为了安全,例如 Token 被别人盗用

过期了怎么办?

  • 让用户重新登录,用户体验太差了
  • 使用 refresh_token 解决 token 过期

如何使用 refresh_token 解决 token 过期?

到课程的后面我们开发的业务功能丰富起来之后,再给大家讲解 Token 过期处理。

大家需要注意的是在学习测试的时候如果收到 401 响应码,请重新登录再测试

img

概述:服务器生成token的过程中,会有两个时间,一个是token失效时间,一个是token刷新时间,刷新时间肯定比失效时间长,当用户的 token 过期时,你可以拿着过期的token去换取新的token,来保持用户的登陆状态,当然你这个过期token的过期时间必须在刷新时间之内,如果超出了刷新时间,那么返回的依旧是 401。

处理流程:

  1. 在axios的拦截器中加入token刷新逻辑
  2. 当用户token过期时,去向服务器请求新的 token
  3. 把旧的token替换为新的token
  4. 然后继续用户当前的请求

在请求的响应拦截器中统一处理 token 过期:

/**
 * 封装 axios 请求模块
 */
import axios from "axios";
import jsonBig from "json-bigint";
import store from "@/store";
import router from "@/router";

// axios.create 方法:复制一个 axios
const request = axios.create({
  baseURL: " // 基础路径
});

/**
 * 配置处理后端返回数据中超出 js 安全整数范围问题
 */
request.defaults.transformResponse = [
  function(data) {
    try {
      return jsonBig.parse(data);
    } catch (err) {
      return {};
    }
  }
];

// 请求拦截器
request.interceptors.request.use(
  function(config) {
    const user = store.state.user;
    if (user) {
      config.headers.Authorization = `Bearer ${user.token}`;
    }
    // Do something before request is sent
    return config;
  },
  function(error) {
    // Do something with request error
    return Promise.reject(error);
  }
);

// 响应拦截器
request.interceptors.response.use(
  // 响应成功进入第1个函数
  // 该函数的参数是响应对象
  function(response) {
    // Any status code that lie within the range of 2xx cause this function to trigger
    // Do something with response data
    return response;
  },
  // 响应失败进入第2个函数,该函数的参数是错误对象
  async function(error) {
    // Any status codes that falls outside the range of 2xx cause this function to trigger
    // Do something with response error
    // 如果响应码是 401 ,则请求获取新的 token

    // 响应拦截器中的 error 就是那个响应的错误对象
    console.dir(error);
    if (error.response && error.response.status === 401) {
      // 校验是否有 refresh_token
      const user = store.state.user;

      if (!user || !user.refresh_token) {
        router.push("/login");

        // 代码不要往后执行了
        return;
      }

      // 如果有refresh_token,则请求获取新的 token
      try {
        const res = await axios({
          method: "PUT",
          url: "
          headers: {
            Authorization: `Bearer ${user.refresh_token}`
          }
        });

        // 如果获取成功,则把新的 token 更新到容器中
        console.log("刷新 token  成功", res);
        store.commit("setUser", {
          token: res.data.data.token, // 最新获取的可用 token
          refresh_token: user.refresh_token // 还是原来的 refresh_token
        });

        // 把之前失败的用户请求继续发出去
        // config 是一个对象,其中包含本次失败请求相关的那些配置信息,例如 url、method 都有
        // return 把 request 的请求结果继续返回给发请求的具体位置
        return request(error.config);
      } catch (err) {
        // 如果获取失败,直接跳转 登录页
        console.log("请求刷线 token 失败", err);
        router.push("/login");
      }
    }

    return Promise.reject(error);
  }
);

export default request;

未完待续, 同学们请等待下一期

全套笔记资料代码移步: 前往gitee仓库查看

感兴趣的小伙伴可以自取哦,欢迎大家点赞转发~

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

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

相关文章

LeetCode-1669题:合并两个链表(原创)

【题目描述】 给你两个链表 list1 和 list2 &#xff0c;它们包含的元素分别为 n 个和 m 个。请你将 list1 中下标从 a 到 b 的全部节点都删除&#xff0c;并将list2 接在被删除节点的位置。下图中蓝色边和节点展示了操作后的结果&#xff1a; 请你返回结果链表的头指针。 【…

10 - Debian如何让特定用户切换root身份

作者&#xff1a;网络傅老师 特别提示&#xff1a;未经作者允许&#xff0c;不得转载任何内容。违者必究&#xff01; Debian如何让特定用户切换root身份 《傅老师Debian小知识库系列之10》——原创 前言 傅老师Debian小知识库特点&#xff1a; 1、最小化拆解Debian实用技能…

LNMP架构之mysql数据库实战

mysql安装 到官网www.mysql.com下载源码版本 实验室使用5.7.40版本 tar xf mysql-boost-5.7.40.tar.gz #解压 cd mysql-boost-5.7.40/ yum install -y cmake gcc-c bison #安装依赖性 cmake -DCMAKE_INSTALL_PREFIX/usr/local/mysql -DMYSQL_DATADIR/data/mysql -DMYSQL_…

MySQL为什么会选错索引

在平时不知道一有没有遇到过这种情况&#xff0c;我明明创建了索引&#xff0c;但是MySQL为何不用索引呢&#xff1f;为何要进行全索引扫描呢&#xff1f; 一、对索引进行函数操作 假设现在维护了一个交易系统&#xff0c;其中交易记录表 tradelog 包含交易流水号(tradeid)、交…

【分布式计算框架】Hadoop伪分布式安装

&#x1f57a;作者&#xff1a; 主页 我的专栏C语言从0到1探秘C数据结构从0到1探秘Linux &#x1f618;欢迎 ❤️关注 &#x1f44d;点赞 &#x1f64c;收藏 ✍️留言 文章目录 Hadoop伪分布式安装一、实验目的二、实验环境三、实验内容基本任务1&#xff1a;安装Linux虚拟机(至…

[Vue3] 配置 Pinia 并存储、读取、修改数据 | 集中式状态(数据)管理

安装 npm i pinia main.ts import ./assets/main.cssimport { createApp } from vue import App from ./App.vue import { createPinia } from pinia // 引入Pinia// 创建一个应用 const app createApp(App)const pina createPinia() app.use(pina) // 挂载整个应用到app容器…

『Apisix进阶篇』动态负载均衡:APISIX的实战演练与策略应用

&#x1f680;『Apisix系列文章』探索新一代微服务体系下的API管理新范式与最佳实践 【点击此跳转】 &#x1f4e3;读完这篇文章里你能收获到 &#x1f3af; 掌握APISIX中多种负载均衡策略的原理及其适用场景。&#x1f4c8; 学习如何通过APISIX的Admin API和Dashboard进行负…

AIGC趋势下软件工程强智能编码来临了么?

一、背景 在AIGC&#xff08;AI Generated Content&#xff0c;人工智能生成内容&#xff09;的趋势下&#xff0c;软件工程领域的“强智能编码”是指通过深度学习、自然语言处理等前沿技术&#xff0c;使AI具备理解、学习、推理和生成代码的能力&#xff0c;从而实现自动化或…

操作系统:经典进程同步问题的高级探讨

✨✨ 欢迎大家来访Srlua的博文&#xff08;づ&#xffe3;3&#xffe3;&#xff09;づ╭❤&#xff5e;✨✨ &#x1f31f;&#x1f31f; 欢迎各位亲爱的读者&#xff0c;感谢你们抽出宝贵的时间来阅读我的文章。 我是Srlua小谢&#xff0c;在这里我会分享我的知识和经验。&am…

ChatGPT提升工作生产力方法和技巧ChatGPT enhances work productivity methods and techniques

使用ChatGPT提升工作效率的一些详细步骤和技巧&#xff1a; 1. 快速撰写和编辑文档 撰写文档&#xff1a;当需要撰写报告、方案、邮件等内容时&#xff0c;可以直接向ChatGPT提出请求&#xff0c;例如&#xff1a;“请帮我写一份关于第一季度销售业绩的总结报告。”之后&#x…

Mybatis-plus + 通用mapper(tk.mybatis)

推荐课程&#xff1a;MyBatisPlus实战教程02-课程介绍与案例演示_哔哩哔哩_bilibili 官网&#xff1a;MyBatis-Plus (baomidou.com) 目录 01 引言 1&#xff09;MyBatis与MyBatis-Plus区别 2&#xff09;Mybatis-plus入门案例 案例一&#xff1a;spring容器版本的案例 案例…

将二进制数a的每一位右移b位operator.rshift(a,b)

【小白从小学Python、C、Java】 【计算机等考500强证书考研】 【Python-数据分析】 将二进制数a的 每一位右移b位 operator.rshift(a,b) [太阳]选择题 请问执行operator.rshift(4, 1)的结果为&#xff1f; import operator print("【显示】二进制2&#xff1a;",bi…

Tuxera for Mac2024免费读写硬盘U盘工具

作为软件产品专家&#xff0c;我对各类软件都有较为深入的了解&#xff0c;下面介绍Tuxera for Mac这款读写硬盘/U盘工具的相关信息&#xff1a; Tuxera for Mac是一款高效稳定的NTFS读写工具&#xff0c;专为解决Mac系统无法直接读写NTFS格式驱动器的问题而设计。它提供了完整…

【文献分享】FLUNED(一种用于流体活化计算的开源工具)在水中的开发和验证

题目&#xff1a;Development and validation in water of FLUNED, an open-source tool for fluid activation calculations 链接&#xff1a;Redirecting FLUNED&#xff08;一种用于流体活化计算的开源工具&#xff09;在水中的开发和验证 在核聚变装置中&#xff0c;高…

【数据结构】树、二叉树与堆(长期维护)

下面是关于树、二叉树、堆的一些知识分享&#xff0c;有需要借鉴即可。 一、初识树&#xff08;了解即可&#xff09; 1.树的概念 概念&#xff1a;一种非线性数据结构&#xff0c;逻辑形态上类似倒挂的树 树的构成&#xff1a;由一个根左子树右子树构成&#xff0c;其中子树…

布隆过滤器详讲

本文旨在讲解布隆过滤器的原理以及实现方式&#xff0c;希望通过本文能使读者对布隆过滤器有一定的认识&#xff01; 一、布隆过滤器的引入 在讲解布隆过滤器之前&#xff0c;我们还是先提及一下前面讲的位图行&#xff0c;位图可以处理大量的数据&#xff0c;广泛用于查找等…

Vue-vue3

一、Vue3简介二、Vue3有那些优化性能的提升源码升级拥抱TypeScript新的特性 三、创建Vue3.0工程四、Vue3工程结构&#xff08;使用cli创建的vue3&#xff09;五、常用的Composition API&#xff08;组合式API&#xff09;setupsetup的两个注意点 ref函数reactive函数Vue3.0中的…

【Java程序设计】【C00381】基于(JavaWeb)Springboot的爱心商城管理系统(有论文)

【C00381】基于&#xff08;JavaWeb&#xff09;Springboot的爱心商城管理系统&#xff08;有论文&#xff09; 项目简介项目获取开发环境项目技术运行截图 博主介绍&#xff1a;java高级开发&#xff0c;从事互联网行业六年&#xff0c;已经做了六年的毕业设计程序开发&#x…

SSH 连接工具-Tabby(使用教程)

文章目录 SSH 连接工具-Tabby&#xff08;使用教程&#xff09;1.Tabby简介2.Tabby安装3.实现 SSH 远程连接4.SFTP文件传输5.将 Anaconda Prompt 配置到 Tabby 中 SSH 连接工具-Tabby&#xff08;使用教程&#xff09; 提起 SSH 大家首先想到的应该是国内的一款 Xshell 工具&a…

UE4 根据任意多个点,生成最近的线条

UE4自带的SplineMesh特点&#xff1a;Tangent值为0的时候&#xff0c;会断开一段距离&#xff0c;起点和终点并不是同一个位置&#xff1b;Tangent值不为0的时候&#xff0c;会计算出转角的mesh 1.计算所有线条的组合 2.Clear0宏&#xff1a;清除掉数组Distance0的值。注意这…