[uni-app]小兔鲜-04推荐+分类+详情

news2024/9/30 9:38:34

热门推荐

新建热门推荐组件, 动态设置组件的标题

<template>
  <!-- 推荐专区 -->
  <view class="panel hot">
    <view class="item" v-for="item in list" :key="item.id">
      ... ...
      <navigator hover-class="none" :url="`/pages/hot/hot?type=${item.type}`" class="cards">
        <image
          v-for="src in item.pictures"
          :key="src"
          class="image"
          mode="aspectFit"
          :src="src"
        ></image>
      </navigator>
      
    </view>
  </view>
</template>
<script setup lang="ts">
// 热门推荐页 标题和url
const hotMap = [
  { type: '1', title: '特惠推荐', url: '/hot/preference' },
  { type: '2', title: '爆款推荐', url: '/hot/inVogue' },
  { type: '3', title: '一站买全', url: '/hot/oneStop' },
  { type: '4', title: '新鲜好物', url: '/hot/new' },
]

// uniapp 获取页面参数
const query = defineProps<{
  type: string
}>()
const currHot = hotMap.find((v) => v.type === query.type)
// 动态设置标题
uni.setNavigationBarTitle({ title: currHot!.title })

</script>

<template>
  ... ...
</template>
  1. !类型断言: 页面标题是基于query参数和hotMap数组计算出来的, 而query是页面参数, TS认为这个参数有可能是undefiend, 所以计算出来的currHot对象可能是undefined, undefined是没有title属性的, 所以TS会进行语法警告, 但是我们明确知道 query 参数不会为undefiend, 所以这里只用类型断言, 告诉TS, 这个参数不会出现问题, 让程序顺利执行

定义数据类型

/** 通用分页结果类型 */
export type PageResult<T> = {
    /** 列表数据 */
    items: T[]
    /** 总条数 */
    counts: number
    /** 当前页数 */
    page: number
    /** 总页数 */
    pages: number
    /** 每页条数 */
    pageSize: number
}

/** 通用分页参数类型 */
export type PageParams = {
    /** 页码:默认值为 1 */
    page?: number
    /** 页大小:默认值为 10 */
    pageSize?: number
}

/** 通用商品类型 */
export type GoodsItem = {
    /** 商品描述 */
    desc: string
    /** 商品折扣 */
    discount: number
    /** id */
    id: string
    /** 商品名称 */
    name: string
    /** 商品已下单数量 */
    orderNum: number
    /** 商品图片 */
    picture: string
    /** 商品价格 */
    price: number
}
import type { PageResult, GoodsItem } from './global'

/** 热门推荐 */
export type HotResult = {
    /** id信息 */
    id: string
    /** 活动图片 */
    bannerPicture: string
    /** 活动标题 */
    title: string
    /** 子类选项 */
    subTypes: SubTypeItem[]
}

/** 热门推荐-子类选项 */
export type SubTypeItem = {
    /** 子类id */
    id: string
    /** 子类标题 */
    title: string
    /** 子类对应的商品集合 */
    goodsItems: PageResult<GoodsItem>
}

请求接口封装

import type { PageParams } from '@/types/global'
import type { HotResult } from '@/types/hot'
import { http } from '@/utils/http'

// 通过联合类型,复用之前的类型
type HotParams = PageParams & { subType?: string }
// 通用热门推荐类型
export const getHotRcommendAPI = (url: string, data?: HotParams) => {
    return http<HotResult>({
        method: 'GET',
        url,
        data,
    })
}
  1. 联合类型: 通过组合, 产生新的类型, 完成复用

页面渲染和Tab交互

<script setup lang="ts">
import { ref } from 'vue'
import { getHotRcommendAPI } from '@/services/hot'
import { onLoad } from '@dcloudio/uni-app'
import type { SubTypeItem } from '@/types/hot'

// 推荐封面图
const bannerPicture = ref('')
// 推荐选项
const subTypes = ref<SubTypeItem[]>([])
// tab高亮索引
const activeIndex = ref(0)
// 获取热门推荐数据
const getHotRecommendData = async () => {
  const res = await getHotRcommendAPI(currHot!.url)
  bannerPicture.value = res.result.bannerPicture
  subTypes.value = res.result.subTypes
}

// 页面加载
onLoad(async () => {
  await getHotRecommendData()
})
</script>

<template>
  <view class="viewport">
    <!-- 推荐封面图 -->
    <view class="cover">
      <image :src="bannerPicture"></image>
    </view>
    <!-- 推荐选项 -->
    <view class="tabs">
      <text
        v-for="(item, index) in subTypes"
        :key="item.id"
        :class="{ active: index === activeIndex }"
        @tap="activeIndex = index"
        class="text"
        >抢先尝鲜</text
      >
    </view>
    <!-- 推荐列表 -->
    <scroll-view
      v-for="(item, index) in subTypes"
      :key="item.id"
      v-show="activeIndex === index"
      scroll-y
      class="scroll-view"
    >
      <view class="goods">
        <navigator
          hover-class="none"
          class="navigator"
          v-for="goods in item.goodsItems.items"
          :key="goods.id"
          :url="`/pages/goods/goods?id=${goods.id}`"
        >
          <image class="thumb" :src="goods.picture"></image>
          <view class="name ellipsis">{{ goods.name }}</view>
          <view class="price">
            <text class="symbol">¥</text>
            <text class="number">{{ goods.price }}</text>
          </view>
        </navigator>
      </view>
    </scroll-view>
  </view>
</template>

分页加载

<script setup lang="ts">
// 推荐封面图
const bannerPicture = ref('')
// 推荐选项
// (SubTypeItem & { finish?: boolean }) 在subTypeItem中添加finish属性,用于判断是否数据枯竭
const subTypes = ref<(SubTypeItem & { finish?: boolean })[]>([])
// tab高亮索引
const activeIndex = ref(0)
// 获取热门推荐数据
const getHotRecommendData = async () => {
  const res = await getHotRcommendAPI(currHot!.url, {
    // 技巧: 使用环境变量,开发环境用30页请求, 生产环境用1页请求
    page: import.meta.env.MODE ? 30 : 1,
    pageSize: 10,
  })
  bannerPicture.value = res.result.bannerPicture
  subTypes.value = res.result.subTypes
}

// 触底加载
const onScrolltolower = async () => {
  // 获取当前选项
  const currsubType = subTypes.value[activeIndex.value]
  // 分页条件
  if (currsubType.goodsItems.page < currsubType.goodsItems.pages) {
    // 当前页码累加
    currsubType.goodsItems.page++
  } else {
    // 标记已结束
    currsubType.finish = true
    // 标记数据枯
    return uni.showToast({
      title: '没有更多数据了',
      icon: 'none',
    })
  }

  // 获取分页后的数据
  const res = await getHotRcommendAPI(currHot!.url, {
    subType: currsubType.id,
    page: currsubType.goodsItems.page,
    pageSize: currsubType.goodsItems.pageSize,
  })
  // 新的列表数据
  const newsubType = res.result.subTypes[activeIndex.value]
  // 数组追加
  currsubType.goodsItems.items.push(...newsubType.goodsItems.items)
}
</script>

<template>
  <view class="viewport">
    ... ...
  
    <!-- 推荐列表 -->
    <scroll-view
      v-for="(item, index) in subTypes"
      :key="item.id"
      v-show="activeIndex === index"
      scroll-y
      class="scroll-view"
      @scrolltolower="onScrolltolower"
    >
      <view class="goods">
       ... ...
      </view>
      <view class="loading-text">
        {{ item.finish ? '没有更多数据了' : '正在加载...' }}
      </view>
    </scroll-view>
  </view>
</template>
  1. 类型属性扩展: 在TS中, 对象中未定义的属性是不能使用的, 可以使用加超过类型& 给对象扩展属性, 扩展后返回一个新类型, 如果直接使用还需要使用联合类型(), 作为整体使用
  2. 环境变量: 在viet项目中, 可以使用固定语法 import.meta.env.DEV 获取当前项目所运行的环境

商品分类

复用轮播图组件

<script setup lang="ts">
import { getHomeBannerAPI } from '@/services/home'
import type { BannerItem } from '@/types/home'
import type { CategoryTopItem } from '@/types/category'
import { onLoad } from '@dcloudio/uni-app'
import { ref, computed } from 'vue'

// 获取轮播图数据
const bannerList = ref<BannerItem[]>([])
const getBannerData = async () => {
  const res = await getHomeBannerAPI(2)
  bannerList.value = res.result
}

// 页面加载
onLoad(() => {
  getBannerData()
})
</script>

<template>
  <view class="viewport">
    <!-- 分类 -->
    <view class="categories">
      <!-- 左侧:一级分类 -->
      <scroll-view class="primary" scroll-y>
        ... ...
      </scroll-view>
      <!-- 右侧:二级分类 -->
      <scroll-view class="secondary" scroll-y>
        <!-- 焦点图 -->
        <XtxSwiper class="banner" :list="bannerList" />
        <!-- 内容区域 -->
        <view class="panel" v-for="item in subCategoryList" :key="item.id">
         ... ...
        </view>
      </scroll-view>
    </view>
  </view>
</template>

渲染一级分类和Tab交互

<script setup lang="ts">
import { getCategoryListAPI } from '@/services/category'
import type { CategoryTopItem } from '@/types/category'
import { onLoad } from '@dcloudio/uni-app'
import { ref, computed } from 'vue'

// 获取分类列表数据
const categoryList = ref<CategoryTopItem[]>([])
const activeIndex = ref(0)
const getCategoryTopDate = async () => {
  const res = await getCategoryListAPI()
  categoryList.value = res.result
}

// 页面加载
onLoad(() => {
  getBannerData()
  getCategoryTopDate()
})
</script>

<template>
  <view class="viewport">
    <!-- 搜索框 -->
    <view class="search">
      <view class="input">
        <text class="icon-search">女靴</text>
      </view>
    </view>
    <!-- 分类 -->
    <view class="categories">
      <!-- 左侧:一级分类 -->
      <scroll-view class="primary" scroll-y>
        <view
          v-for="(item, index) in categoryList"
          :key="item.id"
          class="item"
          :class="{ active: index === activeIndex }"
          @tap="activeIndex = index"
        >
          <text class="name"> {{ item.name }} </text>
        </view>
      </scroll-view>
      <!-- 右侧:二级分类 -->
      <scroll-view class="secondary" scroll-y>
        <!-- 焦点图 -->
        <XtxSwiper class="banner" :list="bannerList" />
        <!-- 内容区域 -->
        <view class="panel" v-for="item in subCategoryList" :key="item.id">
           ... ...
        </view>
      </scroll-view>
    </view>
  </view>
</template>
import type { GoodsItem } from './global'

/** 一级分类项 */
export type CategoryTopItem = {
    /** 二级分类集合[ 二级分类项 ] */
    children: CategoryChildItem[]
    /** 一级分类id */
    id: string
    /** 一级分类图片集[ 一级分类图片项 ] */
    imageBanners: string[]
    /** 一级分类名称 */
    name: string
    /** 一级分类图片 */
    picture: string
}

/** 二级分类项 */
export type CategoryChildItem = {
    /** 商品集合[ 商品项 ] */
    goods: GoodsItem[]
    /** 二级分类id */
    id: string
    /** 二级分类名称 */
    name: string
    /** 二级分类图片 */
    picture: string
}
import { http } from '@/utils/http'
import type { CategoryTopItem } from '@/types/category'

// 分类列表
export const getCategoryListAPI = () => {
    return http<CategoryTopItem[]>({
        method: 'GET',
        url: '/category/top',
    })
}
  1. 通过添加编译模式, 可以快速打开需要调试的页面, 提高开发效率

二级分类和商品渲染

<script setup lang="ts">
import { getCategoryListAPI } from '@/services/category'
import type { CategoryTopItem } from '@/types/category'
import { onLoad } from '@dcloudio/uni-app'
import { ref, computed } from 'vue'

// 获取分类列表数据
const categoryList = ref<CategoryTopItem[]>([])
const activeIndex = ref(0)
const getCategoryTopDate = async () => {
  const res = await getCategoryListAPI()
  categoryList.value = res.result
}

//计算当前二级分类数据
const subCategoryList = computed(() => {
  // categoryList.value[activeIndex.value] 可能是undefind
  return categoryList.value[activeIndex.value]?.children || []
})

// 页面加载
onLoad(async () => {
  getBannerData(), 
  getCategoryTopDate()
})
</script>

<template>
  <view class="viewport">
    <!-- 搜索框 -->
    <view class="search">
      <view class="input">
        <text class="icon-search">女靴</text>
      </view>
    </view>
    <!-- 分类 -->
    <view class="categories">
      <!-- 左侧:一级分类 -->
      <scroll-view class="primary" scroll-y>
        <view
          v-for="(item, index) in categoryList"
          :key="item.id"
          class="item"
          :class="{ active: index === activeIndex }"
          @tap="activeIndex = index"
        >
          <text class="name"> {{ item.name }} </text>
        </view>
      </scroll-view>
      <!-- 右侧:二级分类 -->
      <scroll-view class="secondary" scroll-y>
        <!-- 焦点图 -->
        <XtxSwiper class="banner" :list="bannerList" />
        <!-- 内容区域 -->
        <view class="panel" v-for="item in subCategoryList" :key="item.id">
          <view class="title">
            <text class="name">{{ item.name }}</text>
            <navigator class="more" hover-class="none">全部</navigator>
          </view>
          <view class="section">
            <navigator
              v-for="goods in item.goods"
              :key="goods.id"
              class="goods"
              hover-class="none"
              :url="`/pages/goods/goods?id=${goods.id}`"
            >
              <image class="image" :src="goods.picture"></image>
              <view class="name ellipsis">{{ goods.name }}</view>
              <view class="price">
                <text class="symbol">¥</text>
                <text class="number">{{ goods.price }}</text>
              </view>
            </navigator>
          </view>
        </view>
      </scroll-view>
    </view>
  </view>
</template>
  1. 代码优化: 当请求的分类数据回来之前是一个空数组, 空数组访问访问元素会返回undefiend, undefiend在取属性会报错, 所以使用安全访问符? 进行代码优化, 并且结果是undefiend时返回空数组, 提高代码健壮性

骨架屏

<script setup lang="ts">
// 数据请求完成
const isShow = ref(false)
// 页面加载
onLoad(async () => {
  await Promise.all([getBannerData(), getCategoryTopDate()])
  isShow.value = true
})
</script>

<template>
  <view class="viewport" v-if="isShow">
     ... ...
  </view>
  <PageSkeleton v-else />
</template>
<template name="skeleton">
  <view class="sk-container">
    <view class="viewport">
      <view class="search">
        <view class="input">
          <text
            class="icon-search sk-transparent sk-text-14-2857-225 sk-text sk-pseudo sk-pseudo-circle"
            >女靴</text
          >
        </view>
      </view>
      ....
    </view>
  </view>
</template>

效果展示

商品详情

创建商品详情页面, 请求数据, 渲染数据

<template>
      <!-- 右侧:二级分类 -->
      <scroll-view class="secondary" scroll-y>
        <!-- 内容区域 -->
        <view class="panel">
          ...
          <view class="section">
            <navigator
              v-for="goods in item.goods"
              :key="goods.id"
              class="goods"
              hover-class="none"
              :url="`/pages/goods/goods?id=${goods.id}`"
            >
              ... ...
            </navigator>
          </view>
        </view>
      </scroll-view>
</template>
import type { GoodsItem } from './global'
import type { AddressItem } from '@/types/address'

/** 商品信息 */
export type GoodsResult = {
    /** id */
    id: string
    /** 商品名称 */
    name: string
    /** 商品描述 */
    desc: string
    /** 当前价格 */
    price: number
    /** 原价 */
    oldPrice: number
    /** 商品详情: 包含详情属性 + 详情图片 */
    details: Details
    /** 主图图片集合[ 主图图片链接 ] */
    mainPictures: string[]
    /** 同类商品[ 商品信息 ] */
    similarProducts: GoodsItem[]
    /** sku集合[ sku信息 ] */
    skus: SkuItem[]
    /** 可选规格集合备注[ 可选规格信息 ] */
    specs: SpecItem[]
    /** 用户地址列表[ 地址信息 ] */
    userAddresses: AddressItem[]
}

/** 商品详情: 包含详情属性 + 详情图片 */
export type Details = {
    /** 商品属性集合[ 属性信息 ] */
    properties: DetailsPropertyItem[]
    /** 商品详情图片集合[ 图片链接 ] */
    pictures: string[]
}

/** 属性信息 */
export type DetailsPropertyItem = {
    /** 属性名称 */
    name: string
    /** 属性值 */
    value: string
}

/** sku信息 */
export type SkuItem = {
    /** id */
    id: string
    /** 库存 */
    inventory: number
    /** 原价 */
    oldPrice: number
    /** sku图片 */
    picture: string
    /** 当前价格 */
    price: number
    /** sku编码 */
    skuCode: string
    /** 规格集合[ 规格信息 ] */
    specs: SkuSpecItem[]
}

/** 规格信息 */
export type SkuSpecItem = {
    /** 规格名称 */
    name: string
    /** 可选值名称 */
    valueName: string
}

/** 可选规格信息 */
export type SpecItem = {
    /** 规格名称 */
    name: string
    /** 可选值集合[ 可选值信息 ] */
    values: SpecValueItem[]
}

/** 可选值信息 */
export type SpecValueItem = {
    /** 是否可售 */
    available: boolean
    /** 可选值备注 */
    desc: string
    /** 可选值名称 */
    name: string
    /** 可选值图片链接 */
    picture: string
}
import type { GoodsResult } from '@/types/goods'
import { http } from '@/utils/http'

// 商品详情
export const getGoodsByIdApi = (id: string) => {
    return http<GoodsResult>({
        url: '/goods',
        method: 'GET',
        data: {
            id,
        },
    })
}

轮播图交互和大图预览

<script setup lang="ts">
// 轮播图变化
const currentIndex = ref(0)
const onChange: UniHelper.SwiperOnChange = (e) => {
  currentIndex.value = e.detail.current
}

// 图片点击事件
const onTapImage = (url: string) => {
  // 大图预览
  uni.previewImage({
    current: url,
    urls: goods.value?.mainPictures,
  })
}
</script>

<template>
  <scroll-view scroll-y class="viewport">
    <!-- 基本信息 -->
    <view class="goods">
      <!-- 商品主图 -->
      <view class="preview">
        <swiper circular @change="onChange">
          <swiper-item v-for="item in goods?.mainPictures" :key="item">
            <image mode="aspectFill" :src="item" @tap="onTapImage(item)" />
          </swiper-item>
        </swiper>
        <view class="indicator">
          <text class="current">{{ currentIndex + 1 }}</text>
          <text class="split">/</text>
          <text class="total">{{ goods?.mainPictures.length }}</text>
        </view>
      </view>

      ... ...
    </view>
     ...
  </scroll-view>
</template>
  1. 在TS中事件对象也要有类型, 我们使用uniHelper提供的类型对象即可, 固定写法UniHelper.组件名On事件名

弹出层交互

// 服务说明组件

<script setup lang="ts">
// 子调父
const emit = defineEmits<{
  (event: 'close'): void
}>()
</script>

<template>
  <view class="service-panel">
    <!-- 关闭按钮 -->
    <text class="close icon-close" @tap="emit('close')"></text>
    <!-- 标题 -->
    <view class="title">服务说明</view>
    <!-- 内容 -->
    <view class="content">
      <view class="item">
        <view class="dt">无忧退货</view>
        <view class="dd">
          自收到商品之日起30天内,可在线申请无忧退货服务(食品等特殊商品除外)
        </view>
      </view>
      <view class="item">
        <view class="dt">快速退款</view>
        <view class="dd">
          收到退货包裹并确认无误后,将在48小时内办理退款,
          退款将原路返回,不同银行处理时间不同,预计1-5个工作日到账
        </view>
      </view>
      <view class="item">
        <view class="dt">满88元免邮费</view>
        <view class="dd">
          单笔订单金额(不含运费)满88元可免邮费,不满88元, 单笔订单收取10元邮费
        </view>
      </view>
    </view>
  </view>
</template>
// 收货地址组件

<script setup lang="ts">
// 子调父
const emit = defineEmits<{
  (event: 'close'): void
}>()
</script>

<template>
  <view class="address-panel">
    <!-- 关闭按钮 -->
    <text class="close icon-close" @tap="emit('close')"></text>
    <!-- 标题 -->
    <view class="title">配送至</view>
    <!-- 内容 -->
    <view class="content">
      <view class="item">
        <view class="user">李明 13824686868</view>
        <view class="address">北京市顺义区后沙峪地区安平北街6号院</view>
        <text class="icon icon-checked"></text>
      </view>
      <view class="item">
        <view class="user">王东 13824686868</view>
        <view class="address">北京市顺义区后沙峪地区安平北街6号院</view>
        <text class="icon icon-ring"></text>
      </view>
      <view class="item">
        <view class="user">张三 13824686868</view>
        <view class="address">北京市朝阳区孙河安平北街6号院</view>
        <text class="icon icon-ring"></text>
      </view>
    </view>
    <view class="footer">
      <view class="button primary"> 新建地址 </view>
      <view v-if="false" class="button primary">确定</view>
    </view>
  </view>
</template>
<script setup lang="ts">
// 弹出层
const popup = ref<{
  open: (type?: UniHelper.UniPopupType) => void
  close: () => void
}>()

// 条件渲染弹出层
const popupName = ref<'address' | 'service'>()
const openPopop = (name: typeof popupName.value) => {
  // 修改弹出层名称
  popupName.value = name
  popup.value?.open()
}

</script>

<template>
  <scroll-view scroll-y class="viewport">
    <!-- 基本信息 -->
    <view class="goods">

      <!-- 操作面板 -->
      <view class="action">
        <view class="item arrow" @tap="openSkuPopup(1)">
          <text class="label">选择</text>
          <text class="text ellipsis"> {{ selectArrText }} </text>
        </view>
        <view class="item arrow" @tap="openPopop('address')">
          <text class="label">送至</text>
          <text class="text ellipsis"> 请选择收获地址 </text>
        </view>
        <view class="item arrow" @tap="openPopop('service')">
          <text class="label">服务</text>
          <text class="text ellipsis"> 无忧退 快速退款 免费包邮 </text>
        </view>
      </view>
      
    </view>
  </scroll-view>

  <!-- uni-ui 弹出层 -->
  <uni-popup ref="popup" type="bottom" background-color="#fff">
    <AddressPanel v-if="popupName === 'address'" @close="popup?.close()" />
    <ServicePanel v-if="popupName === 'service'" @close="popup?.close()" />
  </uni-popup>
</template>
  1. 在TS中通过 typeof关键字 可以把对象的属性提取出来, 作为类型使用

效果展示

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

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

相关文章

在 C++ std::set 中如何利用不同类型的值进行搜索?

在 C 集合中如何利用不同类型的值进行搜索 一、背景二、初衷三、is_transparent四、总结 一、背景 C14 引入了一项引人注目的功能&#xff0c;为关联容器弥补了某些案例中长久以来的不足之处&#xff1a;即允许使用在语义上被视为键&#xff0c;但在技术上并非真正键的值对关联…

VS code Jupyter notebook 导入文件目录问题

VS code Jupyter notebook 导入文件目录问题 引言正文引言 这几天被 VS code 中 Jupyter Notebook 中的文件导入折磨的死去活来。这里特来说明一下放置于不同文件夹下的模块该如何被导入。 正文 首先,我们需要按下 Ctrl + , 键打开设置,然后搜索 notebook file root。在如…

window java17改成java 8

window上装了两个版本的Java&#xff0c;目前全局生效的是Java17&#xff0c;想切换成java8的全局。但是在修改环境变量的Path之后&#xff0c;java -version 还是java 17 但是自己的JAVA_HOME 和Path 都没配错啊… 怕是%JAVA_HOME%\bin\ 有问题&#xff0c;我还特意重写了bin…

【GEE学习第二期】GEE数据类型、语法

【GEE学习第二期】GEE数据类型、语法 GEE基本数据类型基本语法循环条件判断累加 可视化影像与波段影像集数据导出数值与绘图保存影像 参考 GEE基本数据类型 GEE 中使用的主要数据类型是栅格&#xff0c;涵盖从本地到全球范围的图像&#xff0c;可从数百个卫星和航空资源获得图…

网盘能否作为FTP替代产品?企业该如何进行FTP国产化替代?

近年来&#xff0c;信创的概念引入和高效实践落地让更多的行业企业自发性地进行国产化替代&#xff0c;目前信创国产化替代还多发生在操作系统和应用层面&#xff0c;软件工具等目前还在下一阶段规划&#xff0c;但很多企业未雨绸缪&#xff0c;已经在做调研和尝试。 FTP作为世…

数据结构:并查集

数据结构&#xff1a;并查集 并查集原理实现框架初始化合并查询获取成员路径压缩其它 总代码 并查集 在生活中&#xff0c;经常会出现分组问题。比如一个班级分为多个小组&#xff0c;打篮球分为两方等等。在同一个组中的所有成员&#xff0c;就构成一个集合。对这种一个群体分…

基于SSM的会员管理系统【附源码】

基于SSM的会员管理系统&#xff08;源码L文说明文档&#xff09; 目录 4 系统设计 4.1 系统概述 4.2 数据库设计原则 4.3 数据表 第五章 系统实现 5.1用户功能模块 5.2管理员功能模块 5.3前台首页功能模块 4 系统…

可视化是工业互联网的核心技术之一,都有哪些应用场景?

一、工业互联网是什么&#xff0c;发展的来胧去脉 工业互联网是指利用互联网技术和物联网技术&#xff0c;将工业生产中的各种设备、机器、传感器等进行互联互通&#xff0c;实现信息的实时采集、传输和分析&#xff0c;从而实现生产过程的智能化、自动化和高效化。 工业互联网…

微信网页 上传图片压缩

微信网页上传图片时的压缩问题可以通过多种方法解决。以下是一些有效的方案和相关API的使用说明。 主要解决方案 1. 使用Canvas进行自定义压缩: 对于需要适配多种设备和格式的情况,可以利用Canvas API进行图片重绘和压缩。通过获取图片信息、设置Canvas尺寸、绘制图片并…

地图资源下载工具(geodatatool)下载 亚洲 8 米 DEM数据

本数据集提供的 DEM 镶嵌图是由 DigitalGlobe 卫星的超高分辨率 (VHR) 沿轨和跨轨立体图像生成的。为了生成 DEM 镶嵌图块&#xff0c;超过 4000 个 DEM 条带与加权平均 镶嵌程序合并&#xff0c;以减少错误并消除接缝。镶嵌图块为 100 公里 x 100 公里&#xff0c;8 米处为 …

【easypoi 一对多导入解决方案】

easypoi 一对多导入解决方案 1.需求2.复现问题2.1校验时获取不到一对多中多的完整数据2.2控制台报错 Cannot add merged region B5:B7 to sheet because it overlaps with an existing merged region (B3:B5). 3.如何解决第二个问题处理&#xff1a; Cannot add merged region …

tr命令:替换文本中的字符

一、命令简介 ​tr​ 命令用于转换或删除文件中的字符。它可以从标准输入中读取数据&#xff0c;对数据进行字符替换、删除或压缩&#xff0c;并将结果输出到标准输出。 ‍ 二、命令参数 格式 tr [选项] [集合1] [集合2]选项和参数 ​ ​-c​​: 指定 集合 1 的补集。​ …

Vulhub zico 2靶机详解

项目地址 https://download.vulnhub.com/zico/zico2.ova实验过程 将下载好的靶机导入到VMware中&#xff0c;设置网络模式为NAT模式&#xff0c;然后开启靶机虚拟机 使用nmap进行主机发现&#xff0c;获取靶机IP地址 nmap 192.168.47.1-254根据对比可知Zico 2的一个ip地址为…

以太网交换安全:MAC地址表安全

一、MAC地址表安全 MAC地址表安全是网络安全中的一个重要方面&#xff0c;它涉及到网络设备的MAC地址表的管理和保护。以下是对MAC地址表安全的详细介绍&#xff1a; &#xff08;1&#xff09;基本概念 定义&#xff1a;MAC地址表是网络设备&#xff08;如交换机&#xff0…

群晖安装Audiobookshelf(有声书)

一、Audiobookshelf是什么&#xff1f; Audiobookshelf是一款自托管的有声读物和播客服务器&#xff0c;用于管理和播放您的有声读物。为用户提供便捷、个性化的音频书籍管理与播放体验 支持网页端、安卓端、IOS端三端同步,支持对有声书进行不同分类。 二、安装教程 通过群晖…

【C语言进阶】一次解决字符串输入问题——为什么输入这么容易奔溃?

文章一览 写在前面一、scanf、getchar与gets函数的爱恨情仇1.1 scanf函数1.1.1 %c输入单个字符2. %s 输入字符串1.1.3 %d输入数字 1.2 getchar函数1.3 gets函数 二、不同输入情况下的使用三、回顾C语言的输入函数总结 写在前面 在之前的文章中&#xff0c;笔者详细介绍了scanf函…

基于Springboot+Vue的课程教学平台的设计与实现系统(含源码数据库)

1.开发环境 开发系统:Windows10/11 架构模式:MVC/前后端分离 JDK版本: Java JDK1.8 开发工具:IDEA 数据库版本: mysql5.7或8.0 数据库可视化工具: navicat 服务器: SpringBoot自带 apache tomcat 主要技术: Java,Springboot,mybatis,mysql,vue 2.视频演示地址 3.功能 这个系…

Arm Linux 交叉编译openssl 1.1.1

一、openssl 源码下载 OpenSSL的官方网站或源代码仓库下载最新或指定版本的OpenSSL源代码。 官网地址&#xff1a; https://openssl-library.org/source/index.html 旧版本下载&#xff1a; https://openssl-library.org/source/old/index.html 这里以 1.1.1 为例 国内git…

网线最短不能短于1米?

大家都知道网线最长不能长于100米&#xff0c;但你有没有听说过“网线最短不能短于1米”这个说法呢&#xff1f;也有的朋友说不能低于0.6米。 有的网友说“‌‌网线最短1米的说法是真的。‌ 短于1米的网线电阻几乎为零&#xff0c;设备可能无法识别&#xff0c;因此在实际应用中…

Android 安卓内存安全漏洞数量大幅下降的原因

谷歌决定使用内存安全的编程语言 Rust 向 Android 代码库中写入新代码&#xff0c;尽管旧代码&#xff08;用 C/C 编写&#xff09;没有被重写&#xff0c;但内存安全漏洞却大幅减少。 Android 代码库中每年发现的内存安全漏洞数量&#xff08;来源&#xff1a;谷歌&#xff09…