教你如何写一个符合自己需求的小程序日历组件

news2025/1/12 23:16:34

1|0 前言

很多时候,我们生活中会有各种打卡的情况,比如 keep 的运动打卡、单词的学习打卡和各种签到打卡或者酒店的入住时间选择,这时候就需要我们书写一个日历组件来处理我们这种需求。

但是更多时候,我们都是网上找一个插件直接套用了,有没有想过自己实现一下呢?如果有,但是感觉不太会的话,接下来跟着我一起实现符合自己需求的日历吧

2|0 准备工作

因为我们是小程序日历嘛,所以必不可少的肯定是微信开发者工具啦。项目目录结构如下:

**

|-- calendar
    |-- app.js
    |-- app.json
    |-- app.wxss
    |-- project.config.json
    |-- sitemap.json
    |-- components
    |   |-- calendar
    |       |-- index.js
    |       |-- index.json
    |       |-- index.wxml
    |       |-- index.wxss
    |-- pages
        |-- index
            |-- index.js
            |-- index.json
            |-- index.wxml
            |-- index.wxss

使用 git 下载空白模板:

**

git clone -b calendar  https://gitee.com/gating/demo.git

ps: 下面步骤有点啰嗦,如果看目录结构就能懂的话就不需要跟着步骤啦

  1. 新建一个名为calendar的空文件夹

  2. 打卡微信开发者工具,新增项目,选中刚刚创建的calendar文件夹,开发模式选中小程序,AppID 使用测试号即可,如图所示:

  3. 创建完后,开发者工具会默认帮我们生成默认的代码,我们在当前文件夹新增components文件家,再在components文件夹中新增calendar文件夹,再从当前文件夹新增名为index的组件,如图:

ps:因为开发者工具会默认生成初始代码,所以直接使用他创建组件比较方便

  1. 删除一些和本次博文无关的代码,比如app.js中的本地缓存能力,具体参考空白模板

3|0 编写代码

接下来编写代码部分我们直接在VSCode编写,因为微信开发者工具实在太。。。- -所以还是使用VSCode编写比较合适

3|1 思考一下

想要实现日历,我们需要知道几个小知识:

  1. 根据常识,我们知道一个月最少有 28 天,最多有 31 天,一周有 7 天,那么就可以有 5 排,但如果有一个月的第一天为星期六且当月有 31 天,那么他就会有 6 排格子才对。比如2020年8月,如图所示:

  1. 我们需要知道,当月的第一天是周几
  2. 我们需要知道,当月有多少天
  3. 最重要的是小程序没有 DOM 操作概念,所以我们不能动态往当月第一天插入格子,所以只能根据第一天是周几循环插入格子

知道以上四点后,我们就可以编写我们的代码啦

首先,第二第三点是最简单的,我先书写第二第三点,怎么获取当前是周几呢?其实jsDate对象直接有现成的方法,我们直接拿来用就好了

**

console.log("今天是星期" + new Date().getDay());

我想有点小难度的是第三点,获取当月有多少天,因为你想,其他的月份的天数是固定的,唯独 2 月,在平年和闰年的不同年份中,2 月的天数也是不同的,那么有没有简单的方法获取当月有多少天呢,其实也是有的,Date实例中的getDate就可以实现我们想要的效果了

**

// 获取一个月有多少天
const getMonthDays = (year, month) => {
  let days = new Date(year, month + 1, 0).getDate();
  return days;
};

我们通过 Date 的第三个参数传 0 就可以获取上个月的最后一天,最后通过 getDate() 获取它的日期就可以对应我们当月的天数,那么就不需要我们自己处理平年和闰年的 2 月有多少天了

是不是又学到了小知识点呢?

解决了 2.3 两个问题,我们就可以往下书写我们的日历了。

众所周知,小程序规定宽度为750rpx(尺寸单位),而我们的一周有 7 天,即 7 格,那么就是每个格子的宽度为107rpx,不建议使用小数,因为 rpx 计算的时候,遇到小数会存在少量偏差。这里我们使用flex布局解决。

所以接下来就可以写我们的布局和生成我们的数据啦,从上面我们分析了,我们有 6 排格子,一排有 7 个,也就是一共 42 个格子。即需要遍历 42 次

先定义一下我们所需要的数据,便于我们后续操作:

**

[
  {
    "dataStr": "2020/06/08",
    "day": "08",
    "month": "08",
    "otherMonth": false,
    "today": true,
    "year": 2020
  }
]

这里我只定义个几个简单的基本数据,如果有不同的业务场景可以自行添加基础数据

小 tips

IOS 端的日期格式必须为/才可以转化为日期格式,比如2018/07/08,而2018-07-08则返回Invalid Date,所以我们需要把-都替换为/

不单单是小程序,微信公众号,safari 都是一样的。

3|2 正式开始编写代码

那么就可以写我们的 js 代码了,在 components -> calendar目录下新建utils.js文件,书写我们创建数据的基础方法:

**

/**
 * 获取当月有多少天
 * @param {String | Number} year => 年
 * @param {String | Number} month => 月
 */
const getMonthDays = (year, month) => {
  let days = new Date(year, month + 1, 0).getDate();
  return days;
};
/**
 * 补0
 * @param {String | Number} num
 */
const toDou = (num) => {
  return num > 9 ? num : "0" + num;
};

/**
 * 转换为日期格式
 * @param {*} date
 */
const transformDate = (date) => {
  if (!(date instanceof Date)) {
    date = new Date(date);
  }
  return date;
};
/**
 * 获取当前日期的年月日
 * @param {any} date => 日期对象
 */
const getDateObj = (date) => {
  date = transformDate(date);
  var year = date.getFullYear();
  var month = date.getMonth() + 1;
  var day = date.getDate();
  return {
    year,
    month,
    day,
    dataStr: `${year}/${toDou(month)}/${toDou(day)}`,
  };
};

/**
 * 获取当月1号的时间戳
 * @param {Date} date => 日期对象
 */
const startOfMonth = (date) => {
  return date.setDate(1);
};

// 获取今天,导出供组件作为默认值使用
const { dataStr } = getDateObj(new Date());

/**
 * 生成日历数据
 * @param {Date} date => 日期对象
 */
const getDate = (date) => {
  date = transformDate(date);
  // 计算需要补的格子
  let dist;
  const { year, month } = getDateObj(date);
  // 获取当月有多少天
  const days = getMonthDays(year, month - 1);
  // 获取当前日期是星期几
  let currentDate = new Date(startOfMonth(date)).getDay();
  // 众所周知的原因,一周的第一天时星期天,而我们做的日历星期天是放在最后的,所以我们这里需要改一下值
  if (currentDate == 0) {
    currentDate = 7;
  }
  dist = currentDate - 1;
  currentDate -= 2;
  const res = [];
  for (let i = 0; i < 42; i++) {
    // 是否不是当前月
    const otherMonth = i >= dist + days || i <= currentDate;
    const date = new Date(year, month - 1, -currentDate + i);
    const dateObj = getDateObj(date);
    res.push({
      ...dateObj,
      today: dataStr === dateObj.dataStr,
      otherMonth,
    });
  }
  return res;
};

module.exports = {
  getMonthDays,
  toDou,
  getDateObj,
  startOfMonth,
  getDate,
  dataStr,
  transformDate,
};

这里代码都比较简单,注释也有写,所以就不详细解释了,如有问题就评论,我看到会第一时间回复的。。。。

在 components -> calendar -> index.js 引入一下 utils.js 文件,然后在created这个生命周期打印一下我们的基础数据,看是否符合预期:

 如果你打印的和我打印的一致,那么就可以愉快的写我们组件的界面啦 😄

因为布局大多数都是样式方面的问题,这里就不多讲解啦,我想大家应该都会的,所以这里直接粘贴代码啦,主要部分我就讲解一下

index.wxml代码如下:

**

<view class="calendar-wrapper">
  <view class="calendar-controller">
    <view class="calendar-picker">
      <text class="arrow left" bindtap="prevMonth"></text>
      <picker
        mode='date'
        fields='month'
        end="2999-12-31"
        start="1970-01-01"
        value="{{monthFormat}}"
        bindchange="dateChange"
      >
        <text class="month-format">{{monthFormat}}</text>
      </picker>
      <text class="arrow right" bindtap="nextMonth"></text>
    </view>
  </view>
  <view class="calendar-header">
    <view class="item" wx:for="{{week}}" wx:key="*this">{{item}}</view>
  </view>
  <view class="calendar-container">
    <view class="item {{item.today?'today':''}} {{item.otherMonth?'other-month':''}}" wx:for="{{calendar}}" wx:key="dataStr">
      <text>{{item.day}}</text>
    </view>
  </view>
</view>

index.wxss代码如下:

**

.calendar-container,
.calendar-controller,
.calendar-header,
.calendar-picker,
.calendar-container .item,
.calendar-header .item {
  display: flex;
  align-items: center;
  line-height: normal;
}
.calendar-container,
.calendar-controller,
.calendar-header {
  justify-content: space-around;
  flex-wrap: wrap;
}
.calendar-container .item,
.calendar-header .item {
  justify-content: center;
  width: 107rpx;
  font-size: 28rpx;
  height: 80rpx;
}
.calendar-header .item {
  color: #666;
}
.calendar-container .item {
  color: #111;
}
.calendar-container .item.other-month {
  color: #999;
}
.calendar-container .item.today {
  color: #6190e8;
  font-weight: 600;
}
.calendar-picker {
  font-size: 30rpx;
  color: #111;
  padding: 20rpx 0;
}
.month-format {
  margin: 0 30rpx;
}
.arrow {
  display: flex;
  padding: 10rpx 15rpx;
  background: #f7f8fc;
}
.arrow::after {
  content: "";
  width: 14rpx;
  height: 14rpx;
  border-top: 4rpx solid #ccc;
  border-left: 4rpx solid #ccc;
}
.arrow.left::after {
  transform: rotateY(-45deg) rotate(-47deg) skew(5deg);
}
.arrow.right::after {
  transform: rotateY(-135deg) rotate(-47deg) skew(5deg);
}

index.js代码如下:

**

// components/calendar/index.js
const { getDate, dataStr, getDateObj } = require("./utils");

const getDateStr = (dataStr) => dataStr.slice(0, -3).replace("/", "-");
Component({
  /**
   * 组件的属性列表
   */
  properties: {},

  /**
   * 组件的初始数据
   */
  data: {
    week: ["一", "二", "三", "四", "五", "六", "日"],
    calendar: getDate(new Date()),
    monthFormat: getDateStr(dataStr),
  },

  /**
   * 组件的方法列表
   */
  methods: {
    dateChange(e) {
      const monthFormat = e.detail.value;
      this.setData({
        monthFormat,
      });
    },
    // 上个月日期
    prevMonth() {
      const [year, month] = this.data.monthFormat.split("-");
      const { dataStr } = getDateObj(
        new Date(year, month, 1).setMonth(month - 2)
      );
      this.setData({
        monthFormat: getDateStr(dataStr),
        calendar: getDate(new Date(dataStr)),
      });
    },
    // 下个月日期
    nextMonth() {
      const [year, month] = this.data.monthFormat.split("-");
      const { dataStr } = getDateObj(new Date(year, month, 1));
      this.setData({
        monthFormat: getDateStr(dataStr),
        calendar: getDate(new Date(dataStr)),
      });
    },
  },
  created() {},
});

这里的主要迷惑点就是月份,因为我们得到的月份是转换后的(即月份+1),而js中的月份是从 0 开始的,所以我们获取上个月的时候月份就需要-2才能实现我们要的效果,而获取下个月的时候,因为本身我们月份本身就+1了,所以不需要进行操作。

书写完成布局后,大概会得出下面这个日历:

 写到这里,其实整个日历的雏形已经出来了,我们可以通过picker换,可以通过点击切换,也算一个相对可以使用的日历组件啦 😝

但是其实还是远远不够的,毕竟,我们连手势左右滑动切换日历这个功能都没有,所以接下来就完善我们这个日历吧

3|3 无缝滑动思考

你想,既然要做左右滑动切换了,肯定得无缝吧?既然得无缝,肯定不能生成多份吧?那么怎么才能用最少的 DOM 做到无缝呢?答案是我们只需要在我们可视范围内生成 DOM 结构即可,即我们的可视范围就是三份,如图所示:

既然说到了左右滑动,肯定少不了我们强大的swiper组件啦,我们这次的日历组件就是建立在swiper组件下实现的,既然用到了swiper,那么我们的布局肯定需要进行小改,数据结构也是,需要进行小改动。

刚才说了,我们的可是范围是三份,所以我们的数据结构就变成了长度为三的数组,即:

**

{
  "calendarArr": [calendar, calendar, calendar]
}

界面也是,我们新增一个swiper组件,然后遍历calendarArr这个数据,

**

<view class="calendar-wrapper">
    <view class="calendar-controller">
      <view class="calendar-picker">
        <text class="arrow left" bindtap="prevMonth"></text>
        <picker
          mode='date'
          fields='month'
          end="2999-12-31"
          start="1970-01-01"
          value="{{monthFormat}}"
          bindchange="dateChange"
        >
          <text class="month-format">{{monthFormat}}</text>
        </picker>
        <text class="arrow right" bindtap="nextMonth"></text>
      </view>
    </view>
    <view class="calendar-header">
      <view class="item" wx:for="{{week}}" wx:key="*this">{{item}}</view>
    </view>
    <swiper
      circular
      class="calendar-swiper"
      current="{{current}}"
      duration="{{duration}}"
      vertical="{{isVertical}}"
      skip-hidden-item-layout
      bindchange="swiperChange"
      bindanimationfinish="swiperAnimateFinish"
      bindtouchstart="swipeTouchStart"
      bindtouchend="swipeTouchEnd"
    >
      <block wx:for="{{calendarArr}}" wx:for-item="calendar" wx:key="index">
        <swiper-item>
          <view class="calendar-container">
            <view class="item {{item.today?'today':''}} {{item.otherMonth?'other-month':''}}" wx:for="{{calendar}}" wx:key="dataStr">
              <text>{{item.day}}</text>
            </view>
          </view>
        </swiper-item>
      </block>
    </swiper>
</view>

样式的话,因为swiper组件有默认样式,高度是150px,而我们这里6 * 80rpx,所以我们需要修改下它的默认样式,即添加下面的 css 即可:

**

.calendar-swiper {
  height: 480rpx;
}

之后就是书写我们的逻辑啦,从布局可以看到我们用了touchstarttouchend,本意其实就是判断我们是向左滑还是向右滑(向上划还是向下滑),来切换我们的月份

3|4 如何区分左滑右滑(上滑下滑)

  1. 需要定两个变量供我们区分是滑动的方向,一个是swipeStartPoint,一个是isPrevMonth
  2. 既然我们说到了无缝,那么肯定用户就会滑动多次,那么我们也需要一个值来计算用户滑动的次数,我们定义为changeCount
  3. 这点也是最重要的一点,我们需用通过当前我们滑动到第几个swiper-item,来修改我们的上个月和下个月的数据,因为我们知道,当前的swiper-item肯定是中间的那个月份,所以我们也需要一个变量来标记我们当前的是第几个,我们定义为currentSwiperIndex,针对于这里的逻辑,我们举个例子:

**

// 假设我们现在是6月,那么数据就是
let calendar = [5, 6, 7];
// 那么我们的 currentSwiperIndex 这时是等于1的
// 假设我滑动了五月,currentSwiperIndex 这时变成0了,我们的月份还是不变
// 但是我们的逻辑就发生改变了
// 这时候的上个月变成了7,下个月变成6,我们需要通过 currentSwiperIndex 的值来动态修改他,即
calendar = [5, 6, 7];
// 半伪代码
const calendarArr = [];
const now = getDate(currentDate);
const prev = getDate(this.getPrevMonth(dataStr));
const next = getDate(this.getNextMonth(dataStr));
const prevIndex = currentSwiperIndex === 0 ? 2 : currentSwiperIndex - 1;
const nextIndex = currentSwiperIndex === 2 ? 0 : currentSwiperIndex + 1;
calendarArr[prevIndex] = prev;
calendarArr[nextIndex] = next;
calendarArr[currentSwiperIndex] = now;

理清楚上面所有的,基本上我们就可以开始重构我们的代码了

3|5 正式书写我们可滑动的日历组件

先定义我们之前的所说的变量,和处理这些变量的方法

**

// 当前的索引值,必须从第一个开始,因为这样我们才能实现视野内的无缝
let currentSwiperIndex = 1,
  generateDate = dataStr, // 当前时间
  swipeStartPoint = 0, // 滑动的坐标
  isPrevMonth = false, // 是否向右滑动
  changeCount = 0; // 滑动的次数

Component({
  // ...
  methods: {
    // 设置当前的索引值
    swiperChange(e) {
      const { current, source } = e.detail;
      if (source === "touch") {
        currentSwiperIndex = current;
        changeCount += 1;
      }
    },
    // 获取手指刚按下的坐标
    swipeTouchStart(e) {
      const { clientY, clientX } = e.changedTouches[0];
      swipeStartPoint = this.data.isVertical ? clientY : clientX;
    },
    // 获取手指松开时的坐标
    swipeTouchEnd(e) {
      const { clientY, clientX } = e.changedTouches[0];
      isPrevMonth = this.data.isVertical
        ? clientY - swipeStartPoint > 0
        : clientX - swipeStartPoint > 0;
    },
  },
  // ...
});

然后定义一个处理我们日历数据的方法,因为我们日历方法是每个时间都需要使用的,所以我们定义个公用的方法,

**

Component({
  // ...
  methods: {
    // 设置上个月的时间
    getPrevMonth(monthFormat) {
      const [year, month] = monthFormat.split(/-|//);
      const { dataStr } = getDateObj(
        new Date(year, month, 1).setMonth(month - 2)
      );
      return dataStr;
    },
    // 设置下个月的时间
    getNextMonth(monthFormat) {
      const [year, month] = monthFormat.split(/-|//);
      const { dataStr } = getDateObj(new Date(year, month, 1));
      return dataStr;
    },
    // 生成日历数组
    generatorCalendar(date) {
      const calendarArr = [];
      // 转换为 Date 实例
      const currentDate = transformDate(date);
      // 获取当前时间的日历数据
      const now = getDate(currentDate);
      // 获取当前时间的字符串
      const { dataStr } = getDateObj(currentDate);
      // 获取上个月的日历数据
      const prev = getDate(this.getPrevMonth(dataStr));
      // 获取下个月的日历数据
      const next = getDate(this.getNextMonth(dataStr));
      // 设置日历数据
      const prevIndex = currentSwiperIndex === 0 ? 2 : currentSwiperIndex - 1;
      const nextIndex = currentSwiperIndex === 2 ? 0 : currentSwiperIndex + 1;
      calendarArr[prevIndex] = prev;
      calendarArr[nextIndex] = next;
      calendarArr[currentSwiperIndex] = now;
      this.setData({
        calendarArr,
        monthFormat: getDateStr(dataStr),
      });
    },
  },
  // ...
});

ps: 因为这里上下月份也可以公用,所以单独提取出来供其他方法使用

最后,我们只需要在动画结束的时候设置日历数据即可,即:

**

Component({
  // ...
  methods: {
    // 动画结束后让滑动的次数置0
    swiperAnimateFinish() {
      const { year, month } = getDateObj(generateDate);
      const monthDist = isPrevMonth ? -changeCount : changeCount;
      generateDate = new Date(year, month + monthDist - 1);
      // 清空滑动次数
      changeCount = 0;
      this.generatorCalendar(generateDate);
    },
  },
  // ...
});

整合起来就是:

**

// components/calendar/index.js
const { getDate, dataStr, getDateObj, transformDate } = require("./utils");
const getDateStr = (dataStr) => dataStr.slice(0, 7).replace("/", "-");
// 当前的索引值,必须从第一个开始,因为这样我们才能实现视野内的无缝
let currentSwiperIndex = 1,
  generateDate = dataStr, // 当前时间
  swipeStartPoint = 0, // 滑动的坐标
  isPrevMonth = false, // 是否向右滑动
  changeCount = 0; // 滑动的次数
Component({
  /**
   * 组件的属性列表
   */
  properties: {
    duration: {
      type: String,
      value: 500,
    },
    isVertical: {
      type: Boolean,
      value: false,
    },
  },

  /**
   * 组件的初始数据
   */
  data: {
    week: ["一", "二", "三", "四", "五", "六", "日"],
    current: 1,
    calendarArr: [],
    monthFormat: getDateStr(dataStr),
  },

  /**
   * 组件的方法列表
   */
  methods: {
    // 设置上个月的时间
    getPrevMonth(monthFormat) {
      const [year, month] = monthFormat.split(/-|//);
      const { dataStr } = getDateObj(
        new Date(year, month, 1).setMonth(month - 2)
      );
      return dataStr;
    },
    // 设置下个月的时间
    getNextMonth(monthFormat) {
      const [year, month] = monthFormat.split(/-|//);
      const { dataStr } = getDateObj(new Date(year, month, 1));
      return dataStr;
    },
    // 生成日历数组
    generatorCalendar(date) {
      const calendarArr = [];
      // 转换为 Date 实例
      const currentDate = transformDate(date);
      // 获取当前时间的日历数据
      const now = getDate(currentDate);
      // 获取当前时间的字符串
      const { dataStr } = getDateObj(currentDate);
      // 获取上个月的日历数据
      const prev = getDate(this.getPrevMonth(dataStr));
      // 获取下个月的日历数据
      const next = getDate(this.getNextMonth(dataStr));
      // 设置日历数据
      const prevIndex = currentSwiperIndex === 0 ? 2 : currentSwiperIndex - 1;
      const nextIndex = currentSwiperIndex === 2 ? 0 : currentSwiperIndex + 1;
      calendarArr[prevIndex] = prev;
      calendarArr[nextIndex] = next;
      calendarArr[currentSwiperIndex] = now;
      this.setData({
        calendarArr,
        monthFormat: getDateStr(dataStr),
      });
      // 通知父组件
      this.triggerEvent("change", this.data.monthFormat);
    },
    // 设置当前的索引值
    swiperChange(e) {
      const { current, source } = e.detail;
      if (source === "touch") {
        currentSwiperIndex = current;
        changeCount += 1;
      }
    },
    // 动画结束后让滑动的次数置0
    swiperAnimateFinish() {
      const { year, month } = getDateObj(generateDate);
      const monthDist = isPrevMonth ? -changeCount : changeCount;
      generateDate = new Date(year, month + monthDist - 1);
      // 清空滑动次数
      changeCount = 0;
      this.generatorCalendar(generateDate);
    },
    // 获取手指刚按下的坐标
    swipeTouchStart(e) {
      const { clientY, clientX } = e.changedTouches[0];
      swipeStartPoint = this.data.isVertical ? clientY : clientX;
    },
    // 获取手指松开时的坐标
    swipeTouchEnd(e) {
      const { clientY, clientX } = e.changedTouches[0];
      isPrevMonth = this.data.isVertical
        ? clientY - swipeStartPoint > 0
        : clientX - swipeStartPoint > 0;
    },

    dateChange(e) {
      const monthFormat = e.detail.value;
      this.setData({
        monthFormat,
      });
      generateDate = getDateStr(monthFormat);
      this.generatorCalendar(generateDate);
    },

    // 上个月日期
    prevMonth() {
      this.setData({
        monthFormat: this.getPrevMonth(this.data.monthFormat),
      });
      this.generatorCalendar(this.data.monthFormat);
    },
    // 下个月日期
    nextMonth() {
      this.setData({
        monthFormat: this.getNextMonth(this.data.monthFormat),
      });
      this.generatorCalendar(this.data.monthFormat);
    },
  },
  ready() {
    this.generatorCalendar(generateDate);
  },
});

页面中使用,

**

<calendar bindchange="calendarChange"></calendar>

**

Page({
  calendarChange(e) {
    console.log(e.detail);
  },
});

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

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

相关文章

【HBase】【一】windows搭建源码开发环境

目录环境配置1. Windows安装Cygwin2. 安装ProtocolBuffers3. 启动zookeeper4. 搭建Hadoop环境5. 编译Hbase源码6. 启动HRegionServer7. 启动HMaster8. 启动HShell客户端环境配置 系统&#xff1a;windows10 IDE: Eclipse hadoop: 3.3.4 hbase: 2.4.15 java: 17 1. Window…

pytest学习——pytest插件的7种用法

1.pytest-repeat 重复跑 安装包 pip install pytest-repeat第一种用法&#xff1a; 装饰器 pytest.mark.repeat(次数) 示例代码 import pytest pytest.mark.repeat(5) def test_001(): assert 12 if __name__ __main__: pytest.main([-sv,__file__])第二种用法&#xff1a…

[附源码]Python计算机毕业设计SSM基于数据挖掘的毕业生离校信息系统(程序+LW)

项目运行 环境配置&#xff1a; Jdk1.8 Tomcat7.0 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&#xff1a; SSM mybatis Maven Vue 等等组成&#xff0c;B/S模式 Ma…

基于牛顿方法在直流微电网潮流研究(Matlab代码实现)

&#x1f4a5;&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️❤️&#x1f4a5;&#x1f4a5;&#x1f4a5; &#x1f389;作者研究&#xff1a;&#x1f3c5;&#x1f3c5;&#x1f3c5;本科计算机专业&#xff0c;研究生电气学硕…

拆解理想汽车Q3财报:收入增速继续下滑,年内两次更换首席技术官

12月9日&#xff0c;理想汽车&#xff08;NASDAQ:LI、HK:02015&#xff09;发布截至2022年9月30日止季度&#xff08;即2022年第三季度&#xff09;的未经审计财务业绩。财报显示&#xff0c;理想汽车2022年第三季度的收入为93.42亿元&#xff0c;同比增加20.2%&#xff0c;低于…

(九)Vue之侦听/监听/监视属性

文章目录普通实现监视属性实现Vue里配置监视属性Vue外配置监视属性配置属性immediate配置deep&#xff08;深度监视&#xff09;配置普通监视监视多级结构中某个属性的变化监视多级结构中所有属性的变化监视属性简写watch配置简写$watch配置简写监视属性vs计算属性Vue学习目录上…

2023最新SSM计算机毕业设计选题大全(附源码+LW)之java农产品推广平台98966

对于计算机专业的学生最头疼的就是临近毕业时的毕业设计,对于如何选题,技术选型等这些问题,难道了大部分人,确实,还没毕业的学生对于这些问题还比较陌生,只有学习的理论知识,没有实战经验怎么能独自完成毕业设计这一系列的流程,今天我们就聊聊如何快速应对这一难题. 比较容易的…

ITK 形态学中的开运算和闭运算 腐蚀 膨胀

一. 图像形态学处理 —— 膨胀和腐蚀 腐蚀在二值图像的基础上做“收缩”或“细化”操作; 膨胀在二值图像的基础上做“加长”或“变粗”的操作。 什么是二值图像呢&#xff1f;把一幅图片看做成一个二维的数组&#xff0c;那么二值图像是一个只有0和1的逻辑数组&#xff0c;我们…

vertical-align属性

vertical-align属性 CSS的vertical-align属性使用场景&#xff0c;经常用于设置图片或者表单(行内块元素)和文字垂直对齐 用于设置一个元素的垂直对齐方式&#xff0c;但是它只针对于行内元素或者行内块元素有效 源代码 语法&#xff1a; vertical-align { baseline | top | …

序——在linux下学习C语言

目录 在Linux下学习C语言的前提。。。 一、Linux的一些常见命令 二、Linux中VI和VIM的一些命令操作 1、在VIM中控制光标 2、vim中的插入模式 3、退出插入模式的方法 4、在VIM模式中的删除命令 5、撤销命令 6、 粘贴和拷贝命令 7、查看文件信息和寻找另一半括号 8、缩…

十万部冷知识:日本国歌为什么像哀乐?

大家在世界杯上看日本队比赛的时候&#xff0c;有没有感觉他们的国歌跟哀乐似的&#xff0c;听着就跟在办葬礼一样。其实&#xff0c;这还真不是像与不像的问题&#xff0c;而是因为它确实是一首挽歌。 这首歌叫《君之代》&#xff0c;出自于《古今和歌集》&#xff0c;是在天皇…

关于Servlet编程(1)

1.Servlet编程中常见网页错误 404错误 : 访问不存在 一般都是路径出错. 405错误 : 请求方法不允许 使用访问的方法有误 只书写了接受Get方法的代码.却使用POST方法访问. 代码中忘记注释super()方法也会返回405 因为源码是直接返回405的 这里展示的两段代码都会引发上图的40…

[附源码]计算机毕业设计健康医疗体检Springboot程序

项目运行 环境配置&#xff1a; Jdk1.8 Tomcat7.0 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&#xff1a; SSM mybatis Maven Vue 等等组成&#xff0c;B/S模式 M…

powershell实现发送win10系统通知

powershell实现发送win10系统通知 一、实现方式是使用系统自带的类NotifyIcon 关于Notify类的属性和使用方法可以查阅NotifyIcon官方文档&#xff0c;这主要讲一下用到的几个变量和方法。 1. Icon Icon只支持 .ico格式的图标&#xff0c;属于自定义图标&#xff0c;只有当 Ba…

PLC、运动控制卡、运动控制器,傻傻分不清

前言 最近有不少做PLC开发的小伙伴&#xff0c;在一些咨询运动控制卡、运动控制器相关的问题&#xff0c;很多人不清楚它们之间有什么区别和联系&#xff0c;今天跟大家就PLC、运动控制卡、运动控制器区别及选型做个分享。 背景 随着工业技术的发展&#xff0c;工业机器人应…

怎么调整视频画布?这些技巧你知道吗?

小伙伴们平时在制作视频时是否有遇到过这种情况&#xff1a;从网站下载的视频素材画面过大&#xff0c;两边还带有黑边&#xff0c;插入到自己的视频后&#xff0c;显得视频整体上很突兀&#xff0c;美观度也受到影响&#xff0c;没有达到自己预期的效果。 如果你们也有这样的烦…

什么是CodeArts?

什么是CodeArts 软件开发生产线&#xff08;CodeArts&#xff09;原名“软件开发平台&#xff08;DevCloud&#xff09;”是集华为近30年研发实践、前沿研发理念、先进研发工具为一体的一站式云端DevOps平台&#xff0c;面向开发者提供的云服务&#xff0c;即开即用&#xff0…

从开环到闭环的旅程-CoCube

差动驱动机器人轨迹-CoCube 迷宫逃离的问题-CoCube 自由运动和环境限制-CoCube 001&#xff0c;自由运动 002&#xff0c;引出环境 003&#xff0c;对比差异 ROS机器人从起点到终点&#xff08;四&#xff09;蓝桥云实践复现 cocube自由运动机器人也需要一个目标&#xff…

JSON数据交互方式

目录 一、JSON的特点 二、前后端开发推荐使用工具ApiPost 扩展&#xff1a;xml与json的区别 三、JSON语法格式 语法注意点&#xff1a; 四、在html中定义json数据类型 1.单个实体——>JSON数据 2.数组实体——>JSON数据 3.集合实体——>JSON数据 五、JSON数据…

Redis的缓存穿透

文章目录1. 缓存穿透的理解2. 常见的解决方案有两种&#xff1a;3. 布隆过滤&#xff1a;4. 编码解决查询的缓存穿透问题&#xff1a;1. 缓存穿透的理解 缓存穿透是指客户端请求的数据在缓存中和数据库中都不存在&#xff0c;这样缓存永远不会生效&#xff0c;这些请求都会打到…