ant Disign Vue 日历组件

本文详细介绍了如何使用antDesignVue库中的a-calendar组件进行封装,包括自定义头部、日期单元格和提示框,以及处理日期面板变化和选择日期的事件逻辑。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

基于ant Design Vue日历组件a-calendar封装的日历组件

示例

 结构

<month-calendar :date="dateStr" :data="workData" @panelChange="onPanelChange">
  <template v-slot:header="{ date }">
    <div class="calendar-header">
      <a-month-picker :value="date" :allow-clear="false" @change="onPanelChange"></a-month-picker>
      <div>其他内容呀</div>
    </div>
  </template>
  <template v-slot:dateCell="{ item }">
    <div class="calendar-data">
      <div v-if="item.todaySummary" class="calendar-data__text">{{ item.todaySummary }}</div>
    </div>
  </template>
  <template v-slot:dateTooltip="{ item }">
    <div v-if="item.todaySummary" class="popover-content">{{ item.todaySummary }}</div>
  </template>
</month-calendar>

逻辑

import MonthCalendar from './modules/monthCalendar.vue';
import moment from 'moment';
export default {
  name: 'Test',
  components: {
    MonthCalendar
  },
  data() {
    return {
      date: moment(),
      workData: {}
    }
  },
  computed: {
    dateStr() {
      return this.date.format('YYYY-MM-DD');
    }
  },
  created() {
    this.queryMonth();
  },
  methods: {
    queryMonth() {
      const month = this.date.format('YYYY-MM');
      const workData = {
        [`${month}-12`]: {tooltip: true, todaySummary: '123'},
        [`${month}-13`]: {tooltip: true, todaySummary: '工作内容'},
        [`${month}-15`]: {tooltip: true, todaySummary: '广袤星河里亿万生命,何其有幸,与你一同在场,就在,这里。'},
        [`${month}-16`]: {tooltip: true, todaySummary: '从每一个寻寻觅觅的日子里,穿行而过,看似捕手的,可能也是猎物,看似平常的,可能正是宝藏。灵魂触角,敏感或是迟钝,都有权尽情舞蹈,大千世界啊,风马牛秘密联络,肉眼所得,仅供参考。'},
        [`${month}-18`]: {tooltip: true, todaySummary: '过去,当下,未来,未必真有界限,时间,只是行一个方便的幻觉。所以,无忧无惧,专心游戏,无牵无挂,寻宝之旅。'},
        [`${month}-21`]: {tooltip: true, todaySummary: '常见的一个定义是:“材料是提供文章内容和表达主题的事物和观念。”严格地说,“事物”并不是材料:尚未反映到头脑中的“事物”不会是材料;已经反映到头脑中(或写入文章中)的“事物”,已是一种观念,一种关于“事物”的感性或理性的认识。这是唯物论的常识。与此相一致,人们有“文章是客观事物的反映”的正确命题:“反映”二字,不独指文章的观点,也指其中的材料。也正因为如此,人们评价文中材料时才有“真与假”、“片面与全面”等标准。如果材料的外延包含与“观念”相对的“事物”本身,那材料(事物)就没有“真假”、“偏全”等区别了。所以,材料是“事物”的说法不能成立'},
      };
      this.workData = Object.freeze(workData);
      console.log(workData);
    },
    onPanelChange(value) {
      // 日期变化
      this.date = value;
      this.queryMonth();
    }
  }
}

样式

@font-df: 0.14rem;
.calendar-header {
  display: flex;
  justify-content: space-between;
  align-items: center;
}
.calendar-data {
  min-height: calc(@font-df * 1.6 * 4 + 0.2rem);  // 4行 + padding-bottom
  padding: 0 0.2rem 0.4rem;
  &__text {
    display: -webkit-box !important;
    overflow: hidden;
    text-overflow: ellipsis;
    -webkit-box-orient: vertical !important;
    word-break: break-all;
    -webkit-line-clamp: 3;	/* 两行 */
    line-height: 1.4em;			/* 行高 */
    max-height: 4.2em;  		/* 1.4 * 2 */
  }
}
.popover-content {
  max-width: 500px;
  white-space: pre-wrap;
}

API

attributes

events

事件名说明参数

panelChange

日期面板变化回调

data:moment, mode: string

select

点击选择日期回调

data:moment

scoped slot

插槽名说明

header

自定义头部,参数:{date}
dateCell自定义日期单元格,参数:{item}
dateTooltip自定义tooptip,参数:{item}

组件代码

结构、逻辑

import moment from 'moment';
export default {
  name: 'MonthCalendar',
  props: {
    date: {
      // 查询日期
      type: String,
      default: ''
    },
    format: {
      // 日期格式
      type: String,
      default: 'YYYY-MM-DD'
    },
    data: {
      type: Object,
      default: () => ({})
    },
    defaultProps: {
      type: Object,
      default: () => ({ tooltip: 'tooltip' }),
    }
  },
  computed: {
    dutyDate() {
      if (this.date) {
        const date = moment(this.date, this.format);
        if (date.isValid()) {
          return date;
        }
      }
      // date 没传日期或传递日期格式错误 默认今天
      const today = moment();
      return today;
    },
    validRange() {
      // 可以显示的日期 对应月份
      let date = moment(this.dutyDate);
      const start = date.startOf('month'); // 会修改原始值
      date = moment(this.dutyDate);
      const end = date.endOf('month');
      return [start, end];
    },
    todayDate() {
      // 今天
      return moment(moment().format('YYYY-MM-DD'));
    },
    tooltipKey() {
      return this.defaultProps.tooltip || 'tooltip';
    }
  },
  methods: {
    headerRender() {
      // 自定义头部内容
      if (this.$scopedSlots.header) {
        // 处理作用域插槽 header
        return (this.$scopedSlots.header({ date: this.dutyDate }));
      }
      return (<div class="fullcalendar-header">
        <a-month-picker
          value={this.dutyDate}
          allowClear={false}
          onChange={this.onChangeMonth}
        ></a-month-picker>
      </div>);
    },
    onChangeMonth(date, dateString) {
      // 选择日期发生变化
      this.$emit('panelChange', date, 'month');
    },
    onPanelChange(value, mode) {
      // 日期面板变化回调
      this.$emit('panelChange', value, mode);
    },
    onSelect(date) {
      // 点击选择日期回调
      this.$emit('select', date);
    }
  },
  render(h) {
    let lastDay = this.validRange[1].day(); // 星期几(星期日为 0, 星期六为 6)
    if (lastDay === 0) lastDay = 7;         // 当月最后一天是星期几 1-7
    const last = moment(this.validRange[1]).add((7 - lastDay), 'days'); // 显示的最后一天 (显示从星期一开始)
    const dateFullCellRender = (value) => {
      // 作用域插槽 dateFullCellRender
      if (value.isAfter(last)) {
        // 最后一行 不渲染
        return null;
      }
      const day = value.date();
      const date = value.format(this.format);
      let item = {}, itemHas = false;
      if (this.data[date]) {
        item = this.data[date];
        itemHas = true;
      }
      const isSameMonth = (value.isAfter(this.validRange[0]) || value.isSame(this.validRange[0])) && value.isBefore(this.validRange[1]);  // 本月
      const isSameDay = this.todayDate.isSame(value);  // 当天
      const dateFullCell = (<div class='fullcalendar-date'>
        <div class='fullcalendar-value'>
          <span class={[isSameMonth ? 'is-same-month' : '', isSameDay ? 'is-same-day' : '']}>{day}</span>
        </div>
        {/* 处理作用域插槽 dateCell */}
        {this.$scopedSlots.dateCell
          ?
          this.$scopedSlots.dateCell({ item: item })
          :
          <div class="fullcalendar-content">
            {itemHas ? (<a-icon type="calendar" theme="twoTone" style="font-size: 36px;" />) : ''}
          </div>
        }
      </div>);
      return (<div class="fullcalendar-date-wrap">
        {/* 处理作用域插槽 dateTooltip */}
        {(this.$scopedSlots.dateTooltip && !!item[this.tooltipKey])
          ?
          (<a-popover title={date}>
            <template slot="content">{this.$scopedSlots.dateTooltip({ item: item })}</template>
            {dateFullCell}
          </a-popover>)
          :
          dateFullCell
        }
      </div>);
    }
    return (
      // validRange={this.validRange}
      <a-calendar
        value={this.dutyDate}
        headerRender={this.headerRender}
        class="month-calendar"
        scopedSlots={{ dateFullCellRender }}
        onPanelChange={this.onPanelChange}
        onSelect={this.onSelect}
      >
      </a-calendar>
    );
  }
}

样式

@primaryColor: #1890FF;
@primaryLight: #F2F8FD;
@tableBorderColor: #EBEEEF;
@white: #FFFFFF;

@font-lg: 0.16rem;
@font-df: 0.14rem;

@fontColor: #333333;
@fontLight: #999999;
@fontDisabled: #DDDDDD;

.month-calendar {
  /deep/ .ant-fullcalendar-calendar-body {
    padding-left: 0;
    padding-right: 0;
  }
  /deep/ table {
    border-collapse: collapse;
    thead {
      font-size: @font-lg;
      color: @fontLight;
      font-weight: 400;
      th {
        padding-right: 12px;
        padding-bottom: 13px;
      }
    }
    tbody {
      border-top: 1px solid @tableBorderColor;
      border-left: 1px solid @tableBorderColor;
    }
    td {
      border-bottom: 1px solid @tableBorderColor;
      border-right: 1px solid @tableBorderColor;
      vertical-align: top;
      &:empty {
        // 最后一行 不渲染 (空元素 隐藏边框)
        border: unset;
      }
    }
  }
  .fullcalendar-date-wrap {
    height: 100%;
  }
  .fullcalendar-date {
    height: 100%;
    &:hover {
      background: @primaryLight;
      // cursor: pointer;
    }
    .fullcalendar-value {
      padding: 0.08rem;
      text-align: right;
      & > span {
        width: 0.32rem;
        height: 0.32rem;
        line-height: 0.32rem;
        text-align: center;
        display: inline-block;
        font-size: @font-lg;
        font-weight: bold;
        color: @fontDisabled;
        &.is-same-month {
          color: @fontColor;
        }
        &.is-same-day {
          background-color: @primaryColor;
          color: @white;
          border-radius: 50%;
        }
      }
    }
    .fullcalendar-content {
      min-height: calc(@font-df * 1.6 * 4 + 0.2rem);  // 4行 + padding-bottom
      padding: 0 0.2rem 0.4rem;
      display: flex;
      justify-content: center;
      align-items: center;
    }
  }
  .fullcalendar-header {
    padding: 11px 16px 11px 0;
    display: flex;
    justify-content: flex-end;
  }
}

<think>我们正在使用Ant Design Vue的a-card组件,并希望在标题前添加一个图标。 根据Ant Design Vue的文档,a-card组件的标题可以通过插槽(slot)或属性(prop)来设置。 我们可以使用插槽方式来自定义标题,这样可以在标题前添加图标。 具体步骤如下: 1. 使用`title`插槽来自定义标题区域。 2. 在插槽内,我们可以使用`a-icon`组件来添加图标,然后添加标题文本。 示例代码如下: ```vue <template> <a-card> <!-- 使用template v-slot:title 来定义标题插槽 --> <template v-slot:title> <!-- 在标题前添加图标,例如type="user"的图标,然后加上标题文本 --> <a-icon type="user" /> 用户信息 </template> <!-- 卡片内容 --> <p>这里是卡片的内容</p> </a-card> </template> ``` 或者,如果你使用的是Ant Design Vue 2.x(基于Vue 3),图标的使用方式可能有所不同(因为Ant Design Vue 2.x使用了图标组件单独引入的方式): 在Ant Design Vue 2.x中,我们通常这样使用: ```vue <template> <a-card> <template #title> <!-- 使用UserOutlined图标(需要从@ant-design/icons-vue中引入) --> <UserOutlined /> 用户信息 </template> <p>卡片内容</p> </a-card> </template> <script> import { UserOutlined } from '@ant-design/icons-vue'; export default { components: { UserOutlined } }; </script> ``` 另外,也可以通过`title`属性配合插槽的方式,但使用插槽更灵活。 注意:在Ant Design Vue 1.x(基于Vue 2)中,图标是通过`a-icon`组件的`type`属性来使用的,而在2.x中,图标是作为组件引入的。 因此,请根据你使用的Ant Design Vue版本选择合适的方式。 引用参考:在Ant Design Vue官方文档中,关于Card组件的使用有自定义标题的示例[^1]。 如果你需要调整图标和标题文本之间的间距,可以添加样式,例如: ```vue <template #title> <span style="display: inline-flex; align-items: center;"> <UserOutlined style="margin-right: 8px;" /> <span>用户信息</span> </span> </template> ``` 这样,图标和文字之间就会有8px的间距。 §§相关问题§§ 1. 如何在Ant Design Vue的a-card组件中添加多个图标? 2. Ant Design Vue的a-card组件支持哪些其他自定义插槽? 3. 如何调整a-card组件标题区域的整体样式?</think>在 Ant Design Vue 的 a-card 组件标题前添加图标,可以通过以下两种方法实现: ### 方法 1:使用 `title` 插槽(推荐) ```vue <template> <a-card> <!-- 自定义标题插槽 --> <template #title> <span> <user-outlined /> <!-- 图标组件 --> 用户信息 <!-- 标题文本 --> </span> </template> <!-- 卡片内容 --> <p>卡片内容区域...</p> </a-card> </template> <script> import { UserOutlined } from '@ant-design/icons-vue'; // 引入图标 export default { components: { UserOutlined } } </script> ``` ### 方法 2:使用 `title` 属性配合 JSX ```vue <template> <a-card :title="titleWithIcon"> <!-- 卡片内容 --> <p>卡片内容区域...</p> </a-card> </template> <script> import { SettingOutlined } from '@ant-design/icons-vue'; export default { components: { SettingOutlined }, computed: { titleWithIcon() { return ( <span> <SettingOutlined style="margin-right: 8px" /> 系统设置 </span> ) } } } </script> ``` ### 关键说明: 1. **图标引入**:需从 `@ant-design/icons-vue` 导入所需图标组件 2. **样式调整**: - 使用 `style="margin-right: 8px"` 添加图标与文本间距 - 添加 `class` 可进一步自定义样式: ```css .custom-title { display: flex; align-items: center; font-weight: bold; } ``` 3. **图标类型**:Ant Design Vue 提供多种图标(如 `HomeOutlined`, `SearchOutlined` 等),完整列表参考[官方图标文档](https://www.antdv.com/components/icon-cn)[^1] ### 效果演示: ```vue <template> <a-card> <template #title> <span class="custom-title"> <notification-outlined :style="{ color: '#1890ff' }" /> 通知中心 </span> </template> <p>您有 5 条未读消息</p> </a-card> </template> ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值