uniapp——项目day05

news2024/11/16 11:25:10

购物车页面

结算区域

把结算区域封装为组件

1. 在 components 目录中,新建 my-settle 结算组件:

2. 初始化 my-settle 组件的基本结构和样式:

<template>
  <view class="my-settle-container">
    结算组件
  </view>
</template>

<script>
  export default {
    name: "my-settle",
    data() {
      return {

      };
    }
  }
</script>

<style lang="scss">
  .my-settle-container {
    /* 底部固定定位 */
    position: fixed;
    bottom: 0;
    left: 0;
    /* 设置宽高和背景色 */
    width: 100%;
    height: 50px;
    background-color: cyan;
  }
</style>

3. 在 cart.vue 页面中使用自定义的 my-settle 组件,并美化页面样式,防止页面底部被覆 盖:

<template>
<view class="cart-container">
<!-- 使用自定义的 address 组件 -->
<!-- 购物车商品列表的标题区域 -->
<!-- 商品列表区域 -->
<!-- 结算区域 -->
<my-settle></my-settle>
</view>
</template>
<style lang="scss">
.cart-container {
padding-bottom: 50px;
}
</style>

渲染结算区域的结构和样式

1. 定义如下的 UI 结构:

  <!-- 最外层的容器 -->
  <view class="my-settle-container">
    <!-- 全选区域 -->
    <label class="radio">
      <radio color="#C00000" :checked="true" /><text>全选</text>
    </label>
    <!-- 合计区域 -->
    <view class="amount-box">
      合计:<text class="amount">¥1234.00</text>
    </view>
    <!-- 结算按钮 -->
    <view class="btn-settle">结算(0)</view>
  </view>

2.美化样式

  .my-settle-container {
    position: fixed;
    bottom: 0;
    left: 0;
    width: 100%;
    height: 50px;
    // 将背景色从 cyan 改为 white
    background-color: white;
    display: flex;
    justify-content: space-between;
    align-items: center;
    padding-left: 5px;
    font-size: 14px;

    .radio {
      display: flex;
      align-items: center;
    }

    .amount {
      color: #c00000;
    }

    .btn-settle {
      height: 50px;
      min-width: 100px;
      background-color: #c00000;
      color: white;
      line-height: 50px;
      text-align: center;
      padding: 0 10px;
    }
  }

动态渲染已勾选商品的总数量

1. 在 store/cart.js 模块中,定义一个名称为 checkedCount 的 getters,用来统计已勾选商 品的总数量:

    // 勾选的商品的总数量
    checkedCount(state) {
      // 先使用 filter 方法,从购物车中过滤器已勾选的商品
      // 再使用 reduce 方法,将已勾选的商品总数量进行累加
      // reduce() 的返回值就是已勾选的商品的总数量
      return state.cart.filter(x => x.goods_state).reduce((total, item) =>
        total += item.goods_count, 0)
    }

2. 在 my-settle 组件中,通过 mapGetters 辅助函数,将需要的 getters 映射到当前组件中使 用:

  import {
    mapGetters
  } from 'vuex'
  export default {
    name: "my-settle",
    computed: {
      ...mapGetters('m_cart', ['checkedCount']),
    },
    data() {
      return {
      };
    }
  }

3. 将 checkedCount 的值渲染到页面中: 

    <!-- 结算按钮 -->
    <view class="btn-settle">结算({{checkedCount}})</view>

动态渲染全选按钮的选中状态

1. 使用 mapGetters 辅助函数,将商品的总数量映射到当前组件中使用,并定义一个叫做 isFullCheck 的计算属性:

    computed: {
      // 1. 将 total 映射到当前组件中
      ...mapGetters('m_cart', ['checkedCount', 'total']),
      // 2. 是否全选
      isFullCheck() {
        return this.total === this.checkedCount
      },
    },

2. 为 radio 组件动态绑定 checked 属性的值:

    <!-- 全选区域 -->
    <label class="radio">
      <radio color="#C00000" :checked="isFullCheck" /><text>全选</text>
    </label>

实现商品的全选/反选功能

1. 在 store/cart.js 模块中,定义一个叫做 updateAllGoodsState 的 mutations 方法,用来 修改所有商品的勾选状态:

// 更新所有商品的勾选状态
    updateAllGoodsState(state, newState) {
      // 循环更新购物车中每件商品的勾选状态
      state.cart.forEach(x => x.goods_state = newState)
      // 持久化存储到本地
      this.commit('m_cart/saveToStorage')
    }

2. 在 my-settle 组件中,通过 mapMutations 辅助函数,将需要的 mutations 方法映射到当前 组件中使用:

// 1. 按需导入 mapMutations 辅助函数
import { mapGetters, mapMutations } from 'vuex'
export default {
// 省略其它代码
methods: {
// 2. 使用 mapMutations 辅助函数,把 m_cart 模块提供的
updateAllGoodsState 方法映射到当前组件中使用
...mapMutations('m_cart', ['updateAllGoodsState']),
},
}

3. 为 UI 中的 label 组件绑定 click 事件处理函数:

    <!-- 全选区域 -->
    <label class="radio" @click="changeAllState">
      <radio color="#C00000" :checked="isFullCheck" /><text>全选</text>
    </label>

4. 在 my-settle 组件的 methods 节点中,声明 changeAllState 事件处理函数:

    methods: {
      // 2. 使用 mapMutations 辅助函数,把 m_cart 模块提供的updateAllGoodsState 方法映射到当前组件中使用
      ...mapMutations('m_cart', ['updateAllGoodsState']),
      // label 的点击事件处理函数
      changeAllState() {
        // 修改购物车中所有商品的选中状态
        // !this.isFullCheck 表示:当前全选按钮的状态取反之后,就是最新的勾选状态
        this.updateAllGoodsState(!this.isFullCheck)
      }
    },

动态渲染已勾选商品的总价格

1. 在 store/cart.js 模块中,定义一个叫做 checkedGoodsAmount 的 getters,用来统计已勾 选商品的总价格:

    // 已勾选的商品的总价
    checkedGoodsAmount(state) {
      // 先使用 filter 方法,从购物车中过滤器已勾选的商品
      // 再使用 reduce 方法,将已勾选的商品数量 * 单价之后,进行累加
      // reduce() 的返回值就是已勾选的商品的总价
      // 最后调用 toFixed(2) 方法,保留两位小数
      return state.cart.filter(x => x.goods_state)
        .reduce((total, item) => total += item.goods_count *
          item.goods_price, 0)
        .toFixed(2)
    }

2. 在 my-settle 组件中,使用 mapGetters 辅助函数,把需要的 checkedGoodsAmount 映射 到当前组件中使用:

...mapGetters('m_cart', ['total', 'checkedCount', 'checkedGoodsAmount']),

3. 在组件的 UI 结构中,渲染已勾选的商品的总价: 

<!-- 合计区域 -->
<view class="amount-box">
合计:<text class="amount">¥{{checkedGoodsAmount}}</text>
</view>

动态计算购物车徽标的数值

  watch: {
    // 监听 total 值的变化
    total() {
      // 调用 methods 中的 setBadge 方法,重新为 tabBar 的数字徽章赋值
      this.setBadge()
    },
  },

渲染购物车为空时的页面结构

<template>
<view class="cart-container" v-if="cart.length !== 0">
<!-- 使用自定义的 address 组件 -->
<!-- 购物车商品列表的标题区域 -->
<!-- 商品列表区域 -->
<!-- 结算区域 -->
</view>
<!-- 空白购物车区域 -->
<view class="empty-cart" v-else>
<image src="/static/cart_empty@2x.png" class="empty-img"></image>
<text class="tip-text">空空如也~</text>
</view>
</template>

3. 美化空白购物车区域的样式: 

  //空白区域购物车样式
  .empty-cart {
    display: flex;
    flex-direction: column;
    align-items: center;
    padding-top: 150px;

    .empty-img {
      width: 90px;
      height: 90px;
    }

    .tip-text {
      font-size: 12px;
      color: gray;
      margin-top: 15px;
    }
  }

分支提交

登录与支付

 

创建 settle 分支

点击结算按钮进行条件判断

说明:用户点击了结算按钮之后,需要先后判断是否勾选了要结算的商品、是否选择了收货地 址、是否登录。

1. 在 my-settle 组件中,为结算按钮绑定点击事件处理函数:

<!-- 结算按钮 -->
<view class="btn-settle" @click="settlement">结算({{checkedCount}})</view>

2. 在 my-settle 组件的 methods 节点中声明 settlement 事件处理函数如下: 

// 点击了结算按钮
      settlement() {
        // 1. 先判断是否勾选了要结算的商品
        if (!this.checkedCount) return uni.$showMsg('请选择要结算的商品!')
        // 2. 再判断用户是否选择了收货地址
        if (!this.addstr) return uni.$showMsg('请选择收货地址!')
        // 3. 最后判断用户是否登录了
        if (!this.token) return uni.$showMsg('请先登录!')
      }

3. 在 my-settle 组件中,使用 mapGetters 辅助函数,从 m_user 模块中将 addstr 映射 到当前组件中使用:

    computed: {
      // 1. 将 total 映射到当前组件中
      ...mapGetters('m_cart', ['total', 'checkedCount', 'checkedGoodsAmount']),
      // addstr 是详细的收货地址
      ...mapGetters('m_user', ['addstr']),
      // 2. 是否全选
      isFullCheck() {
        return this.total === this.checkedCount
      },
    },

4. 在 store/user.js 模块的 state 节点中,声明 token 字符串:

  // state 数据
  state: () => ({
    // 3. 读取本地的收货地址数据,初始化 address 对象
    address: JSON.parse(uni.getStorageSync('address') || '{}'),
    // 登录成功之后的 token 字符串
    token: '',
  }),

5. 在 my-settle 组件中,使用 mapState 辅助函数,从 m_user 模块中将 token 映射到当 前组件中使用:

      // token 是用户登录成功之后的 token 字符串
      ...mapState('m_user', ['token']),

登录

创建登录组件和用户信息组件

1. 在 components 目录中新建登录组件和用户信息组件:

2. 在 my.vue 页面中,通过 mapState 辅助函数,导入需要的 token 字符串:

import badgeMix from '@/mixins/tabbar-badge.js'
// 1. 从 vuex 中按需导入 mapState 辅助函数
import { mapState } from 'vuex'
export default {
mixins: [badgeMix],
computed: {
// 2. 从 m_user 模块中导入需要的 token 字符串
...mapState('m_user', ['token']),
},
data() {
return {}
},
}

3. 在 my.vue 页面中,实现登录组件和用户信息组件的按需展示:

<template>
  <view>
<!--    用户未登录时,显示登录组件 -->
   <my-login v-if="!token"></my-login>
    <!-- 用户登录后,显示用户信息组件 -->
    <my-userinfo v-else></my-userinfo>
  </view>
</template>

实现登录组件的基本布局

1. 为 my-login 组件定义如下的 UI 结构:

2. 美化登录组件的样式:

 .login-container {
    // 登录盒子的样式
    height: 750rpx;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    background-color: #f8f8f8;
    position: relative;
    overflow: hidden;

    // 绘制登录盒子底部的半椭圆造型
    &::after {
      content: ' ';
      display: block;
      position: absolute;
      width: 100%;
      height: 40px;
      left: 0;
      bottom: 0;
      background-color: white;
      border-radius: 100%;
      transform: translateY(50%);
    }

    // 登录按钮的样式
    .btn-login {
      width: 90%;
      border-radius: 100px;
      margin: 15px 0;
      background-color: #c00000;
    }

    // 按钮下方提示消息的样式
    .tips-text {
      font-size: 12px;
      color: gray;
    }
  }

点击登录按钮获取微信用户的基本信息

需求描述:需要获取微信用户的头像、昵称等基本信息。

食大便了,现在要用新东西了。tnnd,现在不能自动获取用户的头像和昵称那些东西了,必须要用户自己填写。看下面这个

头像昵称填写 | 微信开放文档

下面代码全是我改过之后的,需要用户自己上传头像和设置昵称,不然会有默认的设置,用户信息获取到之后会保存到userInfo里面。

<template>
  <view class="login-container">
    <view class="login-container2">
      <button class="avatar-wrapper" open-type="chooseAvatar" @chooseavatar="onChooseAvatar">
        <image class="avatar" :src="avatarUrl"></image>
      </button>
      <input type="nickname" class="weui-input" placeholder="请输入昵称" @input="onInputNickname" />
    </view>
    <!-- 登录按钮 -->
    <!-- 可以从 @getuserinfo 事件处理函数的形参中,获取到用户的基本信息 -->
    <button type="primary" class="btn-login" @click="getUserProfile">一键登录</button>
    <!-- 登录提示 -->
    <view class="tips-text">登录后尽享更多权益</view>
  </view>

</template>

<script>
  export default {
    name: "my-login",
    data() {
      return {
        userInfo: {},
        hasUserInfo: false,
        avatarUrl: 'https://mmbiz.qpic.cn/mmbiz/icTdbqWNOwNRna42FI242Lcia07jQodd2FJGIYQfG0LAJGFxM4FbnQP6yfMxBgJ0F3YRqJCJ1aPAK2dQagdusBZg/0',
        nickname: '默认用户'
      };
    },
    methods: {
      // 获取微信用户的基本信息
      getUserProfile(e) {
        uni.getUserProfile({
          desc: '用于完善会员资料', // 声明获取用户个人信息后的用途,后续会展示在弹窗中,请谨慎填写
          success: (res) => {
            this.userInfo = res.userInfo;
            this.hasUserInfo = true;
            this.userInfo.avatarUrl=this.avatarUrl;
            this.userInfo.nickName=this.nickname
          },
          fail: (res) => {
            console.log(res)
          }
        })
        // 判断是否获取用户信息成功
        // if (e.detail.errMsg === 'getUserProfile:fail auth deny') return
        // uni.$showMsg('您取消了登录授权!')
        // 获取用户信息成功, e.detail.userInfo 就是用户的基本信息
        // console.log(e.detail.userInfo)
      },
      onChooseAvatar(e) {
        const {
          avatarUrl
        } = e.detail
        this.avatarUrl = avatarUrl
      },
      onInputNickname(e) {
        const {
          value
        } = e.target;
        this.nickname = value;
      }
    }
  }
</script>

<style lang="scss">
  .login-container {
    // 登录盒子的样式
    height: 750rpx;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    background-color: #f8f8f8;
    position: relative;
    overflow: hidden;

    // 绘制登录盒子底部的半椭圆造型
    &::after {
      content: ' ';
      display: block;
      position: absolute;
      width: 100%;
      height: 40px;
      left: 0;
      bottom: 0;
      background-color: white;
      border-radius: 100%;
      transform: translateY(50%);
    }

    // 登录按钮的样式
    .btn-login {
      width: 90%;
      border-radius: 100px;
      margin: 15px 0;
      background-color: #c00000;
    }

    // 按钮下方提示消息的样式
    .tips-text {
      font-size: 12px;
      color: gray;
    }
  }

  .login-container2 {
    display: flex;
    flex-direction: column;
    align-items: center;

    .avatar-wrapper {
      margin-bottom: 10px;
      /* 调整头像框与昵称输入框之间的距离 */
      width: 80px;
      /* 调整头像框的宽度 */
      height: 80px;
      /* 调整头像框的高度 */
      border-radius: 50%;
      /* 使头像框呈圆形 */
      overflow: hidden;

      .avatar {
        width: 100%;
        height: 100%;
        object-fit: cover;
        /* 保持图片比例并填充整个容器 */
      }
    }

    .weui-input {
      width: 100%;
      /* 让昵称输入框充满父容器的宽度 */
      max-width: 200px;
      /* 设置昵称输入框的最大宽度,避免过宽 */
      margin-top: 5px;
      /* 调整昵称输入框与头像框之间的距离 */
      padding: 10px;
      /* 添加内边距,增强样式 */
      border: 1px solid #ccc;
      /* 添加边框 */
      border-radius: 5px;
      /* 圆角边框 */
      box-sizing: border-box;
      /* 让边框宽度包含在盒子尺寸内 */
      font-size: 14px;
      /* 调整字体大小 */
    }
  }
</style>

将用户的基本信息存储到 vuex

1. 在 store/user.js 模块的 state 节点中,声明 userinfo 的信息对象如下:

  state: () => ({
    // 3. 读取本地的收货地址数据,初始化 address 对象
    address: JSON.parse(uni.getStorageSync('address') || '{}'),
    // 登录成功之后的 token 字符串
    token: '',
    // 用户的基本信息
    userinfo: JSON.parse(uni.getStorageSync('userinfo') || '{}')
  }),

2. 在 store/user.js 模块的 mutations 节点中,声明如下的两个方法:

// 更新用户的基本信息
    updateUserInfo(state, userinfo) {
      state.userinfo = userinfo
      // 通过 this.commit() 方法,调用 m_user 模块下的 saveUserInfoToStorage 方
      法, 将 userinfo 对象持久化存储到本地
      this.commit('m_user/saveUserInfoToStorage')
    },
    // 将 userinfo 持久化存储到本地
    saveUserInfoToStorage(state) {
      uni.setStorageSync('userinfo', JSON.stringify(state.userinfo))
    }

3. 使用 mapMutations 辅助函数,将需要的方法映射到 my-login 组件中使用:

// 1. 按需导入 mapMutations 辅助函数
import { mapMutations } from 'vuex'   
    methods: {
      // 2. 调用 mapMutations 辅助方法,把 m_user 模块中的 updateUserInfo 映射到当前组件中使用
      ...mapMutations('m_user', ['updateUserInfo']),
      // 获取微信用户的基本信息
      getUserProfile(e) {
        let userInfo={}
        uni.getUserProfile({
          desc: '用于完善会员资料', // 声明获取用户个人信息后的用途,后续会展示在弹窗中,请谨慎填写
          success: (res) => {
            userInfo = res.userInfo;
            this.hasUserInfo = true;
            userInfo.avatarUrl = this.avatarUrl;
            userInfo.nickName = this.nickname
            // 3. 将用户的基本信息存储到 vuex 中
            this.updateUserInfo(userInfo)
          },
          fail: (res) => {
            console.log(res)
          }
        })
        // 判断是否获取用户信息成功
        // if (e.detail.errMsg === 'getUserProfile:fail auth deny') return
        // uni.$showMsg('您取消了登录授权!')
        // 获取用户信息成功, e.detail.userInfo 就是用户的基本信息
        // console.log(e.detail.userInfo)
      },}

登录获取 Token 字符串

需求说明:当获取到了微信用户的基本信息之后,还需要进一步调用登录相关的接口,从而换取 登录成功之后的 Token 字符串。

 1. 在 getUserProfile方法中,预调用 this.getToken() 方法,同时把获取到的用户信息传递进 去:

 getUserProfile(e) {
        let userInfo={}
        uni.getUserProfile({
          desc: '用于完善会员资料', // 声明获取用户个人信息后的用途,后续会展示在弹窗中,请谨慎填写
          success: (res) => {
            userInfo = res.userInfo;
            this.hasUserInfo = true;
            userInfo.avatarUrl = this.avatarUrl;
            userInfo.nickName = this.nickname
            // 3. 将用户的基本信息存储到 vuex 中
            this.updateUserInfo(userInfo)
            // 获取登录成功后的 Token 字符串
            this.getToken(e.detail)
          },
          fail: (res) => {
            console.log(res)
          }
        })
        // 判断是否获取用户信息成功
        // if (e.detail.errMsg === 'getUserProfile:fail auth deny') return
        // uni.$showMsg('您取消了登录授权!')
        // 获取用户信息成功, e.detail.userInfo 就是用户的基本信息
        // console.log(e.detail.userInfo)
      },

2. 在 methods 中定义 getToken 方法,调用登录相关的 API,实现登录的功能:

// 调用登录接口,换取永久的 token
      async getToken(info) {
        // 调用微信登录接口
        let err=null;
         let res=null;
        try {
          const loginResult = await uni.login();
          res = loginResult
        } catch (error) {
          err = error;
        }
        // 然后你可以检查 err 或 res 来处理成功或失败的情况
        if (err) {
          // 处理错误情况
          console.error(err);
        } else {
          // 处理成功情况
          console.log(res);
          // 其他代码...
        }
        // 判断是否 wx.login() 调用失败
        if (err || res.errMsg !== 'login:ok') return uni.$showError('登录失败!')
        // 准备参数对象
        const query = {
          code: res.code,
          encryptedData: info.encryptedData,
          iv: info.iv,
          rawData: info.rawData,
          signature: info.signature
        }
        console.log(query)
        // 换取 token
        const {
          data: loginResult
        } = await
        uni.$http.post('/api/public/v1/users/wxlogin', query)
        console.log(loginResult)
        if (loginResult.meta.status !== 200) return uni.$showMsg('登录失败!')
        uni.$showMsg('登录成功')
      }

将 Token 存储到 vuex

1. 在 store/user.js 模块的 mutations 节点中,声明如下的两个方法:

// 更新 token 字符串
    updateToken(state, token) {
      state.token = token
      // 通过 this.commit() 方法,调用 m_user 模块下的 saveTokenToStorage 方法,将 token 字符串持久化存储到本地
      this.commit('m_user/saveTokenToStorage')
    },
    // 将 token 字符串持久化存储到本地
    saveTokenToStorage(state) {
      uni.setStorageSync('token', state.token)
    }

2. 修改 store/user.js 模块的 state 节点如下:

// 登录成功之后的 token 字符串
token: uni.getStorageSync('token') || '',

3. 在 my-login 组件中,把 vuex 中的 updateToken 方法映射到当前组件中使用:

...mapMutations('m_user', ['updateUserInfo', 'updateToken'])

//在getToken方法里面更新state里的token
//这里不管statu多少都显示成功登录
        // if (loginResult.meta.status !== 200) return uni.$showMsg('登录失败!')
        uni.$showMsg('登录成功')
        
        // 2. 更新 vuex 中的 token
        // this.updateToken(loginResult.message.token)
        this.updateToken("testToken")

这里有个问题,黑马的这个接口已经不能正常返回token了,所以上面假装登录成功然后返回一个自定义的token.

最后整个my-login组件的代码如下

<template>
  <view class="login-container">
    <view class="login-container2">
      <button class="avatar-wrapper" open-type="chooseAvatar" @chooseavatar="onChooseAvatar">
        <image class="avatar" :src="avatarUrl"></image>
      </button>
      <input type="nickname" class="weui-input" placeholder="请输入昵称" @input="onInputNickname" />
    </view>
    <!-- 登录按钮 -->
    <!-- 可以从 @getuserinfo 事件处理函数的形参中,获取到用户的基本信息 -->
    <button type="primary" class="btn-login" @click="getUserProfile">一键登录</button>
    <!-- 登录提示 -->
    <view class="tips-text">登录后尽享更多权益</view>
  </view>

</template>

<script>
  // 1. 按需导入 mapMutations 辅助函数
  import {
    resolveDirective
  } from 'vue';
  import {
    mapMutations
  } from 'vuex'
  export default {
    name: "my-login",
    data() {
      return {
        hasUserInfo: false,
        avatarUrl: 'https://mmbiz.qpic.cn/mmbiz/icTdbqWNOwNRna42FI242Lcia07jQodd2FJGIYQfG0LAJGFxM4FbnQP6yfMxBgJ0F3YRqJCJ1aPAK2dQagdusBZg/0',
        nickname: '默认用户'
      };
    },
    methods: {
      // 2. 调用 mapMutations 辅助方法,把 m_user 模块中的 updateUserInfo 映射到当前组件中使用
...mapMutations('m_user', ['updateUserInfo', 'updateToken']),
      // 获取微信用户的基本信息
      getUserProfile(e) {
        let userInfo = {}
        uni.getUserProfile({
          desc: '用于完善会员资料', // 声明获取用户个人信息后的用途,后续会展示在弹窗中,请谨慎填写
          success: (res) => {
            userInfo = res.userInfo;
            this.hasUserInfo = true;
            userInfo.avatarUrl = this.avatarUrl;
            userInfo.nickName = this.nickname
            // 3. 将用户的基本信息存储到 vuex 中
            this.updateUserInfo(userInfo)
            // 获取登录成功后的 Token 字符串
            this.getToken(res)
          },
          fail: (res) => {
            console.log(res)
          }
        })
        // 判断是否获取用户信息成功
        // if (e.detail.errMsg === 'getUserProfile:fail auth deny') return
        // uni.$showMsg('您取消了登录授权!')
        // 获取用户信息成功, e.detail.userInfo 就是用户的基本信息
        // console.log(e.detail.userInfo)
      }, // 调用登录接口,换取永久的 token
      async getToken(info) {
        // 调用微信登录接口
        let err=null;
         let res=null;
        try {
          const loginResult = await uni.login();
          res = loginResult
        } catch (error) {
          err = error;
        }
        // 然后你可以检查 err 或 res 来处理成功或失败的情况
        if (err) {
          // 处理错误情况
          console.error(err);
        } else {
          // 处理成功情况
          console.log(res);
          // 其他代码...
        }
        // 判断是否 wx.login() 调用失败
        if (err || res.errMsg !== 'login:ok') return uni.$showError('登录失败!')
        // 准备参数对象
        const query = {
          code: res.code,
          encryptedData: info.encryptedData,
          iv: info.iv,
          rawData: info.rawData,
          signature: info.signature
        }
        // 换取 token
        const {
          data: loginResult
        } = await
        uni.$http.post('/api/public/v1/users/wxlogin', query)
        // if (loginResult.meta.status !== 200) return uni.$showMsg('登录失败!')
        uni.$showMsg('登录成功')
        
        // 2. 更新 vuex 中的 token
        // this.updateToken(loginResult.message.token)
        this.updateToken("testToken")
      },
      onChooseAvatar(e) {
        const {
          avatarUrl
        } = e.detail
        this.avatarUrl = avatarUrl
      },
      onInputNickname(e) {
        const {
          value
        } = e.target;
        this.nickname = value;
      }
    }
  }
</script>

<style lang="scss">
  .login-container {
    // 登录盒子的样式
    height: 750rpx;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    background-color: #f8f8f8;
    position: relative;
    overflow: hidden;

    // 绘制登录盒子底部的半椭圆造型
    &::after {
      content: ' ';
      display: block;
      position: absolute;
      width: 100%;
      height: 40px;
      left: 0;
      bottom: 0;
      background-color: white;
      border-radius: 100%;
      transform: translateY(50%);
    }

    // 登录按钮的样式
    .btn-login {
      width: 90%;
      border-radius: 100px;
      margin: 15px 0;
      background-color: #c00000;
    }

    // 按钮下方提示消息的样式
    .tips-text {
      font-size: 12px;
      color: gray;
    }
  }

  .login-container2 {
    display: flex;
    flex-direction: column;
    align-items: center;

    .avatar-wrapper {
      margin-bottom: 10px;
      /* 调整头像框与昵称输入框之间的距离 */
      width: 80px;
      /* 调整头像框的宽度 */
      height: 80px;
      /* 调整头像框的高度 */
      border-radius: 50%;
      /* 使头像框呈圆形 */
      overflow: hidden;

      .avatar {
        width: 100%;
        height: 100%;
        object-fit: cover;
        /* 保持图片比例并填充整个容器 */
      }
    }

    .weui-input {
      width: 100%;
      /* 让昵称输入框充满父容器的宽度 */
      max-width: 200px;
      /* 设置昵称输入框的最大宽度,避免过宽 */
      margin-top: 5px;
      /* 调整昵称输入框与头像框之间的距离 */
      padding: 10px;
      /* 添加内边距,增强样式 */
      border: 1px solid #ccc;
      /* 添加边框 */
      border-radius: 5px;
      /* 圆角边框 */
      box-sizing: border-box;
      /* 让边框宽度包含在盒子尺寸内 */
      font-size: 14px;
      /* 调整字体大小 */
    }
  }
</style>

用户信息

实现用户头像昵称区域的基本布局

1. 在 my-userinfo 组件中,定义如下的 UI 结构:

<template>
  <view class="my-userinfo-container">
    <!-- 头像昵称区域 -->
    <view class="top-box">
      <image src="" class="avatar"></image>
      <view class="nickname">xxx</view>
    </view>
  </view>
</template>

2. 美化当前组件的样式:

 .my-userinfo-container {
    height: 100%;
    // 为整个组件的结构添加浅灰色的背景
    background-color: #f4f4f4;

    .top-box {
      height: 400rpx;
      background-color: #c00000;
      display: flex;
      flex-direction: column;
      align-items: center;
      justify-content: center;

      .avatar {
        display: block;
        width: 90px;
        height: 90px;
        border-radius: 45px;
        border: 2px solid white;
        box-shadow: 0 1px 5px black;
      }

      .nickname {
        color: white;
        font-weight: bold;
        font-size: 16px;
        margin-top: 10px;
      }
    }
  }

3. 在 my.vue 页面中,为最外层包裹性质的 view 容器,添加 class="my-container" 的类 名,并美化样式如下:

page,
.my-container {
height: 100%;
}

渲染用户的头像和昵称

1. 在 my-userinfo 组件中,通过 mapState 辅助函数,将需要的成员映射到当前组件中使用:

// 按需导入 mapState 辅助函数
import { mapState } from 'vuex'
export default {
computed: {
// 将 m_user 模块中的 userinfo 映射到当前页面中使用
...mapState('m_user', ['userinfo']),
},
data() {
return {}
},
}

 2. 将用户的头像和昵称渲染到页面中:

    <!-- 头像昵称区域 -->
    <view class="top-box">
      <image :src="userinfo.avatarUrl" class="avatar"></image>
      <view class="nickname">{{userinfo.nickName}}</view>
    </view>

渲染第一个面板区域

我一直想要的效果,一个区域叠放在另一个区域上面,原来是通过top=-10px实现的

1. 在 my-userinfo 组件中,定义如下的 UI 结构:

 <!-- 面板的列表区域 -->
    <view class="panel-list">
      <!-- 第一个面板 -->
      <view class="panel">
        <!-- panel 的主体区域 -->
        <view class="panel-body">
          <!-- panel 的 item 项 -->
          <view class="panel-item">
            <text>8</text>
            <text>收藏的店铺</text>
          </view>
          <view class="panel-item">
            <text>14</text>
            <text>收藏的商品</text>
          </view>
          <view class="panel-item">
            <text>18</text>
            <text>关注的商品</text>
          </view>
          <view class="panel-item">
            <text>84</text>
            <text>足迹</text>
          </view>
        </view>
      </view>
      <!-- 第二个面板 -->
      <!-- 第三个面板 -->
    </view>

2. 美化第一个面板的样式:

.panel-list {
    padding: 0 10px;
    position: relative;
    top: -10px;

    .panel {
      background-color: white;
      border-radius: 3px;
      margin-bottom: 8px;

      .panel-body {
        display: flex;
        justify-content: space-around;

        .panel-item {
          display: flex;
          flex-direction: column;
          align-items: center;
          justify-content: space-around;
          font-size: 13px;
          padding: 10px 0;
        }
      }
    }
  }

渲染第二个面板区域

1. 定义第二个面板区域的 UI 结构:

      <!-- 第二个面板 -->
      <view class="panel">
        <!-- 面板的标题 -->
        <view class="panel-title">我的订单</view>
        <!-- 面板的主体 -->
        <view class="panel-body">
          <!-- 面板主体中的 item 项 -->
          <view class="panel-item">
            <image src="/static/my-icons/icon1.png" class="icon"></image>
            <text>待付款</text>
          </view>
          <view class="panel-item">
            <image src="/static/my-icons/icon2.png" class="icon"></image>
            <text>待收货</text>
          </view>
          <view class="panel-item">
            <image src="/static/my-icons/icon3.png" class="icon"></image>
            <text>退款/退货</text>
          </view>
          <view class="panel-item">
            <image src="/static/my-icons/icon4.png" class="icon"></image>
            <text>全部订单</text>
          </view>
        </view>
      </view>

2. 对之前的 SCSS 样式进行改造,从而美化第二个面板的样式:

    .panel {
      background-color: white;
      border-radius: 3px;
      margin-bottom: 8px;

      .panel-title {
        line-height: 45px;
        padding-left: 10px;
        font-size: 15px;
        border-bottom: 1px solid #f4f4f4;
      }

      .panel-body {
        display: flex;
        justify-content: space-around;

        .panel-item {
          display: flex;
          flex-direction: column;
          align-items: center;
          justify-content: space-around;
          font-size: 13px;
          padding: 10px 0;

          .icon {
            width: 35px;
            height: 35px;
          }
        }
      }
    }
  }

渲染第三个面板区域

1. 定义第三个面板区域的 UI 结构:

      <!-- 第三个面板 -->
      <view class="panel">
        <view class="panel-list-item">
          <text>收货地址</text>
          <uni-icons type="arrowright" size="15"></uni-icons>
        </view>
        <view class="panel-list-item">
          <text>联系客服</text>
          <uni-icons type="arrowright" size="15"></uni-icons>
        </view>
        <view class="panel-list-item">
          <text>退出登录</text>
          <uni-icons type="arrowright" size="15"></uni-icons>
        </view>
      </view>

2. 美化第三个面板区域的样式:

.panel-list-item {
height: 45px;
display: flex;
justify-content: space-between;
align-items: center;
font-size: 15px;
padding: 0 10px;
}

实现退出登录的功能

1. 为第三个面板区域中的 退出登录 项绑定 click 点击事件处理函数:

        <view class="panel-list-item" @click="logout">
          <text>退出登录</text>
          <uni-icons type="arrowright" size="15"></uni-icons>
        </view>

2. 在 my-userinfo 组件的 methods 节点中定义 logout 事件处理函数:

这里又进行了一次修改

      // 退出登录
      async logout() {
        // 询问用户是否退出登录
        let err=null
        let succ=null
        try{
          const res = await uni.showModal({
            title: '提示',
            content: '确认退出登录吗?'
          })
          succ=res
        } catch (error) {
          err=error
        }
        
        if (succ && succ.confirm) {
          // 用户确认了退出登录的操作
          // 需要清空 vuex 中的 userinfo、token 和 address
          this.updateUserInfo({})
          this.updateToken('')
          this.updateAddress({})
        }
      }

3. 使用 mapMutations 辅助方法,将需要用到的 mutations 方法映射到当前组件中:

// 按需导入辅助函数
import { mapState, mapMutations } from 'vuex'     

 ...mapMutations('m_user', ['updateUserInfo', 'updateToken',
      'updateAddress']),

三秒后自动跳转

需求描述:在购物车页面,当用户点击 “结算” 按钮时,如果用户没有登录,则 3 秒后自动跳转到 登录页面

 1. 在 my-settle 组件的 methods 节点中,声明一个叫做 showTips 的方法,专门用来展示倒 计时的提示消息:

// 展示倒计时的提示消息
      showTips(n) {
        // 调用 uni.showToast() 方法,展示提示消息
        uni.showToast({
          // 不展示任何图标
          icon: 'none', // 提示的消息
          title: '请登录后再结算!' + n + ' 秒后自动跳转到登录页',
          // 为页面添加透明遮罩,防止点击穿透
          mask: true,
          // 1.5 秒后自动消失
          duration: 1500
        })
      }

2. 在 data 节点中声明倒计时的秒数:

data() {
return {
// 倒计时的秒数
seconds: 3
}
}

3. 改造 结算 按钮的 click 事件处理函数,如果用户没有登录,则预调用一个叫做 delayNavigate 的方法,进行倒计时的导航跳转:

// 点击了结算按钮
      settlement() {
        // 1. 先判断是否勾选了要结算的商品
        if (!this.checkedCount) return uni.$showMsg('请选择要结算的商品!')
        // 2. 再判断用户是否选择了收货地址
        if (!this.addstr) return uni.$showMsg('请选择收货地址!')
        // 3. 最后判断用户是否登录了,如果没有登录,则调用 delayNavigate() 进行倒计时的导
        航跳转
        // if (!this.token) return uni.$showMsg('请先登录!')
        if (!this.token) return this.delayNavigate()
      }

4. 定义 delayNavigate 方法,初步实现倒计时的提示功能:

 // 延迟导航到 my 页面
      delayNavigate() {
        // 1. 展示提示消息,此时 seconds 的值等于 3
        this.showTips(this.seconds)
        // 2. 创建定时器,每隔 1 秒执行一次
        setInterval(() => {
          // 2.1 先让秒数自减 1
          this.seconds--
          // 2.2 再根据最新的秒数,进行消息提示
          this.showTips(this.seconds)
        }, 1000)
      }

上述代码的问题:定时器不会自动停止,此时秒数会出现等于 0 或小于 0 的情况!

5. 在 data 节点中声明定时器的 Id 如下: 

data() {
return {
// 倒计时的秒数
seconds: 3,
// 定时器的 Id
timer: null
}
}

6. 改造 delayNavigate 方法如下:

 // 延迟导航到 my 页面
      delayNavigate() {
        this.showTips(this.seconds)
        // 1. 将定时器的 Id 存储到 timer 中
        this.timer = setInterval(() => {
          this.seconds--
          // 2. 判断秒数是否 <= 0
          if (this.seconds <= 0) {
            // 2.1 清除定时器
            clearInterval(this.timer)
            // 2.2 跳转到 my 页面
            uni.switchTab({
              url: '/pages/my/my'
            })
            // 2.3 终止后续代码的运行(当秒数为 0 时,不再展示 toast 提示消息)
            return
          }
          this.showTips(this.seconds)
        }, 1000)
      },

上述代码的问题:seconds 秒数不会被重置,导致第 2 次,3 次,n 次 的倒计时跳转功能无 法正常工作

7. 进一步改造 delayNavigate 方法,在执行此方法时,立即将 seconds 秒数重置为 3 即 可:

// 延迟导航到 my 页面
      delayNavigate() {
        // 把 data 中的秒数重置成 3 秒
        this.seconds = 3
        this.showTips(this.seconds)
        this.timer = setInterval(() => {
          this.seconds--
          if (this.seconds <= 0) {
            clearInterval(this.timer)
            uni.switchTab({
              url: '/pages/my/my'
            })
            return
          }
          this.showTips(this.seconds)
        }, 1000)
      }

 登录成功之后再返回之前的页面

核心实现思路:在自动跳转到登录页面成功之后,把返回页面的信息存储到 vuex 中,从而方便登 录成功之后,根据返回页面的信息重新跳转回去。

返回页面的信息对象,主要包含 { openType, from } 两个属性,其中 openType 表示以哪种方式导 航回之前的页面;from 表示之前页面的 url 地址;

 1. 在 store/user.js 模块的 state 节点中,声明一个叫做 redirectInfo 的对象如下:

    // 重定向的 object 对象 { openType, from }
    redirectInfo: null,

2. 在 store/user.js 模块的 mutations 节点中,声明一个叫做 updateRedirectInfo 的方 法:

 // 更新重定向的信息对象
    updateRedirectInfo(state, info) {
      state.redirectInfo = info
    }

3. 在 my-settle 组件中,通过 mapMutations 辅助方法,把 m_user 模块中的 updateRedirectInfo 方法映射到当前页面中使用:

// 把 m_user 模块中的 updateRedirectInfo 方法映射到当前页面中使用
...mapMutations('m_user', ['updateRedirectInfo']),

4. 改造 my-settle 组件 methods 节点中的 delayNavigate 方法,当成功跳转到 my 页面 之 后,将重定向的信息对象存储到 vuex 中:

 // 延迟导航到 my 页面
      delayNavigate() {
        // 把 data 中的秒数重置成 3 秒
        this.seconds = 3
        this.showTips(this.seconds)
        this.timer = setInterval(() => {
          this.seconds--
          if (this.seconds <= 0) {
            clearInterval(this.timer)
            uni.switchTab({
              url: '/pages/my/my',
              // 页面跳转成功之后的回调函数
              success: () => {
                // 调用 vuex 的 updateRedirectInfo 方法,把跳转信息存储到 Store 中
                this.updateRedirectInfo({
                  // 跳转的方式
                  openType: 'switchTab',
                  // 从哪个页面跳转过去的
                  from: '/pages/cart/cart'
                })
              }
            })
            return
          }
          this.showTips(this.seconds)
        }, 1000)
      }

5. 在 my-login 组件中,通过 mapState 和 mapMutations 辅助方法,将 vuex 中需要的数据 和方法,映射到当前页面中使用:

  // 按需导入辅助函数
  import {
    mapMutations,
    mapState
  } from 'vuex'
  export default {
    computed: {
      // 调用 mapState 辅助方法,把 m_user 模块中的数据映射到当前用组件中使用
      ...mapState('m_user', ['redirectInfo']),
    },
    methods: {
      // 调用 mapMutations 辅助方法,把 m_user 模块中的方法映射到当前组件中使用
      ...mapMutations('m_user', ['updateUserInfo', 'updateToken',
        'updateRedirectInfo'
      ]),
    },
  }

6. 改造 my-login 组件中的 getToken 方法,当登录成功之后,预调用 this.navigateBack() 方法返回登录之前的页面:

// 调用登录接口,换取永久的 token
async getToken(info) {
// 省略其它代码...
// 判断 vuex 中的 redirectInfo 是否为 null
// 如果不为 null,则登录成功之后,需要重新导航到对应的页面
this.navigateBack()
}

7. 在 my-login 组件中,声明 navigateBack 方法如下: 

 // 返回登录之前的页面
      navigateBack() {
        // redirectInfo 不为 null,并且导航方式为 switchTab
        if (this.redirectInfo && this.redirectInfo.openType === 'switchTab') {
          // 调用小程序提供的 uni.switchTab() API 进行页面的导航
          uni.switchTab({
            // 要导航到的页面地址
            url: this.redirectInfo.from,
            // 导航成功之后,把 vuex 中的 redirectInfo 对象重置为 null
            complete: () => {
              this.updateRedirectInfo(null)
            }
          })
        }
      },

微信支付

在请求头中添加 Token 身份认证的字段

1. 原因说明:只有在登录之后才允许调用支付相关的接口,所以必须为有权限的接口添加身份认证 的请求头字段

2. 打开项目根目录下的 main.js ,改造 $http.beforeRequest 请求拦截器中的代码如下:

//请求拦截器
$http.beforeRequest = function(options) {
  uni.showLoading({
    title: '数据加载中...'
  })
  // 判断请求的是否为有权限的 API 接口
  if (options.url.indexOf('/my/') !== -1) {
  // 为请求头添加身份认证字段
  options.header = {
  // 字段的值可以直接从 vuex 中进行获取
  Authorization: store.state.m_user.token,
  }
  }
}

微信支付的流程

1. 创建订单

  • 请求创建订单的 API 接口:把(订单金额、收货地址、订单中包含的商品信息)发送到服务 器
  • 服务器响应的结果:订单编号

2. 订单预支付

  •  请求订单预支付的 API 接口:把(订单编号)发送到服务器
  • 服务器响应的结果:订单预支付的参数对象 ,里面包含了订单支付相关的必要参数

3. 发起微信支付 

  • 调用 uni.requestPayment() 这个 API,发起微信支付;把步骤 2 得到的 “订单预支付对 象” 作为参数传递给 uni.requestPayment() 方法
  • 监听 uni.requestPayment() 这个 API 的 success , fail , complete 回调函数

创建订单

1. 改造 my-settle 组件中的 settlement 方法,当前三个判断条件通过之后,调用实现微信支 付的方法:

// 点击了结算按钮
      settlement() {
        // 1. 先判断是否勾选了要结算的商品
        if (!this.checkedCount) return uni.$showMsg('请选择要结算的商品!')
        // 2. 再判断用户是否选择了收货地址
        if (!this.addstr) return uni.$showMsg('请选择收货地址!')
        // 3. 最后判断用户是否登录了,如果没有登录,则调用 delayNavigate() 进行倒计时的导航跳转
        // if (!this.token) return uni.$showMsg('请先登录!')
        if (!this.token) return this.delayNavigate()
        // 4. 实现微信支付功能
        this.payOrder()
      }

2. 在 my-settle 组件中定义 payOrder 方法如下,先实现创建订单的功能:

// 微信支付
      async payOrder() {
        // 1. 创建订单
        // 1.1 组织订单的信息对象
        const orderInfo = {
          // 开发期间,注释掉真实的订单价格,
          // order_price: this.checkedGoodsAmount,
          // 写死订单总价为 1 分钱
          order_price: 0.01,
          consignee_addr: this.addstr,
          goods: this.cart.filter(x => x.goods_state).map(x => ({
            goods_id: x.goods_id,
            goods_number: x.goods_count,
            goods_price: x.goods_price
          }))
        }
        // 1.2 发起请求创建订单
        const {
          data: res
        } = await
        uni.$http.post('/api/public/v1/my/orders/create', orderInfo)
        if (res.meta.status !== 200) return uni.$showMsg('创建订单失败!')
        // 1.3 得到服务器响应的“订单编号”
        const orderNumber = res.message.order_number
        // 2. 订单预支付
        // 3. 发起微信支付
      }

订单预支付

1. 改造 my-settle 组件的 payOrder 方法,实现订单预支付功能:

        // 2. 订单预支付
        // 2.1 发起请求获取订单的支付信息
        const {
          data: res2
        } = await
        uni.$http.post('/api/public/v1/my/orders/req_unifiedorder', {
          order_number: orderNumber
        })
        // 2.2 预付订单生成失败
        if (res2.meta.status !== 200) return uni.$showError('预付订单生成失败!')
        // 2.3 得到订单支付相关的必要参数
        const payInfo = res2.message.pay

发起微信支付

1. 改造 my-settle 组件的 payOrder 方法,实现微信支付的功能:

// 3. 发起微信支付
        // 3.1 调用 uni.requestPayment() 发起微信支付
        const [err, succ] = await uni.requestPayment(payInfo)
        // 3.2 未完成支付
        if (err) return uni.$showMsg('订单未支付!')
        // 3.3 完成了支付,进一步查询支付的结果
        const {
          data: res3
        } = await
        uni.$http.post('/api/public/v1/my/orders/chkOrder', {
          order_number: orderNumber
        })
        // 3.4 检测到订单未支付
        if (res3.meta.status !== 200) return uni.$showMsg('订单未支付!')
        // 3.5 检测到订单支付完成
        uni.showToast({
          title: '支付完成!',
          icon: 'success'
        })

分支的合并与提交

提交了个未完成品.除了点击结算之后的订单和支付页面都没有以外其他都还是阔以的。

 发布小程序

为什么要发布

1. 小程序只有发布之后,才能被用户搜索并使用 2. 开发期间的小程序为了便于调试,含有 sourcemap 相关的文件,并且代码没有被压缩,因此体积 较大,不适合直接当作线上版本进行发布 3. 通过执行 “小程序发布”,能够优化小程序的体积,提高小程序的运行性能

发布小程序的流程

1. 点击 HBuilderX 菜单栏上的 发行 -> 小程序-微信(仅适用于uni-app) :

2. 在弹出框中填写要发布的小程序的名称和AppId之后,点击发行按钮:

3. 在 HBuilderX 的控制台中查看小程序发布编译的进度:

4. 发布编译完成之后,会自动打开一个新的微信开发者工具界面,此时,点击工具栏上的上传按 钮:

5. 填写版本号和项目备注之后,点击上传按钮: 

 发布为 Android App 的流程

3. 打开项目根目录中的 manifest.json 配置文件,在基础配置面板中,获取uni-app 应用标 识,并填写应用名称:

4. 切换到 App 图标配置面板,点击浏览按钮,选择合适的图片之后,再点击自动生成所有图标并 替换即可: 

 5. 点击菜单栏上的 发行 -> 原生 App-云打包:

6. 勾选打包配置如下: 

7. 在控制台中查看打包的进度信息:

8. 点击链接下载 apk 的安装包,并安装到 Android 手机中查看打包的效果。

 

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

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

相关文章

代码随想录算法训练营第21天|530.二叉搜索树的最小绝对差 501.二叉搜索树中的众数 236. 二叉树的最近公共祖先

JAVA代码编写 530.二叉搜索树的最小绝对差 给你一个二叉搜索树的根节点 root &#xff0c;返回 树中任意两不同节点值之间的最小差值 。 差值是一个正数&#xff0c;其数值等于两值之差的绝对值。 示例 1&#xff1a; 输入&#xff1a;root [4,2,6,1,3] 输出&#xff1a;1…

内网穿透工具NPS(保姆级教程)

前言&#xff1a; 有时候我们受限于硬件设备和网络的的问题&#xff0c;无法将内网的大容量、高性能存储设备或计算设备对外访问。这个时候就会变的特别苦恼&#xff0c;上云呢成本太大&#xff0c;不用云呢公网又无法直接访问&#xff0c;这个时候怎么办呢&#xff0c;NPS它来…

华为云优惠券介绍、领取入口及使用教程

华为云是华为的云服务品牌&#xff0c;致力于为用户提供一站式云计算基础设施服务。为了吸引用户&#xff0c;华为云经常推出各种优惠活动&#xff0c;其中就包括优惠券的发放&#xff0c;下面将为大家详细介绍华为云优惠券的作用、领取入口以及使用教程。 一、华为云优惠券介绍…

SDL2 播放视频文件(MP4)

1.简介 这里引入FFmpeg库&#xff0c;获取视频流数据&#xff0c;然后通过FFmpeg将视频流解码成YUV原始数据&#xff0c;再将YUV数据送入到SDL库中实现视频播放。 2.FFmpeg的操作流程 注册API&#xff1a;av_register_all()构建输入AVFormatContext上下文&#xff1a;avform…

如何检查 Docker 和 Kubernetes 是否可以访问外部网络,特别是用于拉取镜像的仓库?

要检查 Docker 和 Kubernetes 是否可以访问外部网络&#xff0c;尤其是用于拉取容器镜像的仓库&#xff0c;您可以按照以下步骤进行&#xff1a; 1. 检查节点的网络连接 首先&#xff0c;您需要确保 Kubernetes 节点能够访问外部网络。这可以通过在节点上执行 ping 命令来测试…

11月第2周榜单丨飞瓜数据B站UP主排行榜榜单(B站平台)发布!

飞瓜轻数发布2023年11月6日-11月12日飞瓜数据UP主排行榜&#xff08;B站平台&#xff09;&#xff0c;通过充电数、涨粉数、成长指数、带货数据等维度来体现UP主账号成长的情况&#xff0c;为用户提供B站号综合价值的数据参考&#xff0c;根据UP主成长情况用户能够快速找到运营…

Linux安装jdk1.8教程(服务器可以访问网络)

文章目录 前言创建安装目录查看是否安装过下载解压配置环境变量查看是否安装成功 前言 本教程介绍了一种快捷的jdk1.8安装方法。 创建安装目录 mkdir -p /opt/software // 这是我自己的安装目录&#xff0c;根据自己的习惯确定查看是否安装过 rpm -qa | grep -i jdk需要注意…

2023年人工智能还好找工作吗?

人工智能的就业形势并不严峻&#xff0c;相反&#xff0c;很多岗位都是供不应求的状态&#xff0c;可以看一下下面的官方数据。 脉脉高聘人才智库发布《2023泛人工智能人才洞察》&#xff0c;对23年1-8月的人工智能行业现状进行了分析总结。 人工智能相关岗位数据&#xff1a…

探索游戏公司跨部门合作的项目管理工具选择

为了实现出色的用户体验&#xff0c;游戏公司需要强大的研发能力和发行运营经验。通常情况下&#xff0c;游戏公司内部有多个独立工作的研发部门和发行部门&#xff0c;它们需要跨部门协作。随着公司快速发展和游戏项目增加&#xff0c;游戏公司迫切需要一套适用于特殊协作流程…

vue中ref的用法

vue中ref的用法 在项目中使用ref时有时候直接取值,有时候返回的却是一个数组,不知其中缘由,后查了一下ref用法,所以总结一下. 1.绑定在dom元素上时&#xff0c;用起来与id差不多&#xff0c;通过this.$refs来调用: <div id"passCarEchart" ref"passCarEch…

浙江大学数据结构陈越 第一讲 数据结构和算法

数据结构 数据结构是计算机科学中用来组织和存储数据的方式。它可以理解为一种组织数据的方式&#xff0c;能够有效地管理和操作数据&#xff0c;以及提供对数据进行存储、检索、更新和删除等操作的方法。常见的数据结构包括数组、链表、栈、队列、树和图等&#xff0c;它们各自…

竞赛选题 深度学习的视频多目标跟踪实现

文章目录 1 前言2 先上成果3 多目标跟踪的两种方法3.1 方法13.2 方法2 4 Tracking By Detecting的跟踪过程4.1 存在的问题4.2 基于轨迹预测的跟踪方式 5 训练代码6 最后 1 前言 &#x1f525; 优质竞赛项目系列&#xff0c;今天要分享的是 基于深度学习的视频多目标跟踪实现 …

vue项目使用electron打包exe桌面程序

首先在vue项目中cmd进入终端&#xff0c;然后第一次下载electron依赖需要切换镜像&#xff0c;否则下载得很慢很慢&#xff0c;在终端中输入以下两个命令 将 Electron Builder Binaries 的镜像源设置为淘宝的 npm 镜像源。这同样用于提升 Electron Builder 相关包的下载速度。 …

Android 摇一摇功能实现,重力加速度大于15

最近接到需求实现摇一摇需求&#xff0c;不过这个法律限制的很严格&#xff0c;属于敏感地带&#xff0c;实现后又被叫停了。 法律要求&#xff1a; 如果按照规定&#xff0c;操作时间不少于3s就基本没什么跳转了。 实现的话&#xff0c;只考虑了第一条&#xff0c;即&#…

你绝对需要的Facebook养号攻略,教你如何养成耐用号

Facebook 可谓是大家的“老熟人”了&#xff0c;作为全球热门的社交媒体平台&#xff0c;Facebook 一直以来都是社媒营销、跨境电商的重要阵地&#xff0c;但是很多小伙伴们在注册新账号后往往忽略了一个重要的步骤&#xff0c;也是必不可少的一步&#xff0c;那就是养号&#…

Java 轻松删除PDF指定页、空白页 (免费工具分享)

对PDF页面的增删通常需要借助专门的工具&#xff0c;而这些工具一般需要付费才能使用。那么我们可以通过Java代码免费实现这一功能吗&#xff1f;答案是肯定的。这篇文章就教大家如何使用一个免费的国产Java库来删除PDF中的指定页面或者删除PDF中的空白页。 使用Java快速删除PD…

git push 报错 The requested URL returned error: 500

今天gitpush时报错The requested URL returned error: 500 看报错应该是本地和gitlab服务器之间通信的问题&#xff0c;登录gitlab网站查看 登录时报错无法通过ldapadmin认证&#xff0c;ldap服务器连接失败。 首先&#xff0c;登录ldap服务器&#xff0c;查看是否是ldap服务…

【星海出品】SDN neutron (五) openvswitch

1、ovs-vswitchd组件是交换机的主要模块&#xff0c;运行在用户态&#xff0c;其主要负责基本的转发逻辑、地址学习、外部物理端口绑定等。还可以运用OVS自带的ovs-ofctl工具采用openflow协议对交换机进行远程配置和管理。 2、ovsdb-server组件是存储OVS的网桥等配置、日志以及…

(论文阅读34-39)理解CNN

34.文献阅读笔记 简介 题目 Understanding image representations by measuring their equivariance and equivalence 作者 Karel Lenc, Andrea Vedaldi, CVPR, 2015. 原文链接 http://www.cv-foundation.org/openaccess/content_cvpr_2015/papers/Lenc_Understanding_I…

NumLevels

NumLevels&#xff1a;输入参数&#xff0c;最大的金字塔层数。默认auto&#xff0c;范围【0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, auto】。 AngleStart&#xff1a;输入参数&#xff0c;输入匹配时的起始角度。默认-0.39&#xff0c;建议值【 -3.14, -1.57, -0.79, -0.39, -0.20,…