Vue 3 NutUI日歷組件:展開/收縮功能的實(shí)現(xiàn)邏輯詳解

Vue 3 NutUI日歷組件:展開/收縮功能的實(shí)現(xiàn)邏輯詳解

?? 概述

本文詳細(xì)介紹了一個(gè)基于 Vue 3 + TypeScript + Taro + NutUI的智能日歷組件中展開/收縮功能的實(shí)現(xiàn)邏輯。該組件支持兩種顯示模式:展開模式顯示整月,收縮模式只顯示本周,通過(guò)平滑的動(dòng)畫效果提供良好的用戶體驗(yàn)。

? 核心功能

雙模式切換機(jī)制

  • 展開模式: 顯示整月日歷,用戶可以查看和選擇整個(gè)月的日期
  • 收縮模式: 只顯示當(dāng)前周,節(jié)省界面空間,專注于本周安排

?? 技術(shù)實(shí)現(xiàn)

1. 狀態(tài)管理

// 控制日歷展開/收縮狀態(tài)
const isExpanded = ref(false)

// 所有日期元素的總高度(展開模式)
const allDaysHeight = ref(0)

// 單行日期元素的高度(收縮模式)
const oneDayHeight = ref(60)

2. 日期范圍計(jì)算邏輯

獲取本周范圍(收縮模式)

function getWeekRange(): DateRange {
  const today = new Date()
  
  // 計(jì)算本周日的日期(一周的開始)
  // getDay() 返回 0-6,0 表示周日,需要特殊處理
  const dayOfWeek = today.getDay() === 0 ? 7 : today.getDay()
  const firstDay = new Date(today)
  firstDay.setDate(today.getDate() - dayOfWeek) // 回退到本周日
  firstDay.setHours(0, 0, 0, 0) // 設(shè)置為當(dāng)天開始時(shí)間

  // 計(jì)算本周六的日期(一周的結(jié)束)
  const lastDay = new Date(firstDay)
  lastDay.setDate(firstDay.getDate() + 6) // 前進(jìn)6天到周六
  lastDay.setHours(23, 59, 59, 999) // 設(shè)置為當(dāng)天結(jié)束時(shí)間
  
  return { firstDay, lastDay }
}

獲取本月范圍(展開模式)

function getMonthRange(): DateRange {
  const today = new Date()
  const month = today.getMonth()
  const year = today.getFullYear()
  
  // 本月第一天
  const firstDay = new Date(year, month, 1)
  // 本月最后一天(通過(guò)設(shè)置下月第0天實(shí)現(xiàn))
  const lastDay = new Date(year, month + 1, 0)
  
  return { firstDay, lastDay }
}

3. 智能日期禁用邏輯

const disableDay = (day: CalendarCardDay): boolean => {
  // 如果有自定義禁用規(guī)則,優(yōu)先使用
  if (props.customDisableDay) {
    return props.customDisableDay(day)
  }
  
  const { firstDay, lastDay } = currentDateRange.value
  const current = new Date(day.year, day.month - 1, day.date)
  
  return current < firstDay || current > lastDay
}

4. 核心展開/收縮實(shí)現(xiàn)

DOM 操作函數(shù)

const toggleCalendarDays = () => {
  nextTick(() => {
    // 使用緩存的 DOM 查詢結(jié)果
    const calendarDays = safeQuerySelector('.nut-calendarcard-days', 1)
    
    if (!calendarDays) {
      console.warn('Calendar days container not found')
      return
    }
    
    try {
      if (isExpanded.value) {
        // 展開模式:顯示所有日期
        calendarDays.style.height = `${allDaysHeight.value}px`
        calendarDays.style.transition = 'all 0.3s ease'
        
        // 恢復(fù)所有日期的顯示
        const allDays = calendarDays.querySelectorAll('.nut-calendarcard-day')
        allDays.forEach((day) => {
          const dayElement = day as HTMLElement
          dayElement.style.display = ''
        })
      } else {
        // 收縮模式:只顯示當(dāng)前周
        calendarDays.style.height = `${oneDayHeight.value}px`
        calendarDays.style.transition = 'all 0.3s ease'

        // 隱藏非當(dāng)前周的日期(通過(guò)禁用狀態(tài)判斷)
        const allDays = calendarDays.querySelectorAll('.nut-calendarcard-day')
        allDays.forEach((day) => {
          const dayElement = day as HTMLElement
          if (dayElement.classList.contains('disabled')) {
            dayElement.style.display = 'none'
          } else {
            dayElement.style.display = ''
          }
        })
      }
    } catch (error) {
      console.error('Error toggling calendar days:', error)
    }
  })
}

安全 DOM 查詢

const safeQuerySelector = (selector: string, index: number = 0): HTMLElement | null => {
  try {
    const elements = document.querySelectorAll(selector)
    return elements[index] as HTMLElement || null
  } catch (error) {
    console.warn(`Failed to query selector: ${selector}`, error)
    return null
  }
}

5. 用戶交互處理

防抖優(yōu)化

const debounce = <T extends (...args: any[]) => any>(fn: T, delay: number): T => {
  return ((...args: any[]) => {
    if (debounceTimer) {
      clearTimeout(debounceTimer)
    }
    debounceTimer = setTimeout(() => fn(...args), delay)
  }) as T
}

const handleToggleMode = debounce(() => {
  if (isLoading.value) return
  
  isLoading.value = true
  
  try {
    toggleMode()
  } finally {
    // 延遲重置加載狀態(tài),確保動(dòng)畫完成
    setTimeout(() => {
      isLoading.value = false
    }, 300)
  }
}, 100)

切換邏輯

const toggleMode = () => {
  // 切換展開狀態(tài)
  isExpanded.value = !isExpanded.value
  
  // 觸發(fā)事件
  emit('toggle', isExpanded.value)
  
  // 控制日歷日期元素的展開/收縮
  toggleCalendarDays()
}

6. 初始化邏輯

const initializeCalendarSizes = async () => {
  try {
    await nextTick()
    
    // 獲取日期容器元素
    const calendarDays = safeQuerySelector('.nut-calendarcard-days', 1)
    if (!calendarDays) {
      console.warn('Calendar days container not found during initialization')
      return
    }
    
    // 獲取展開模式下的總高度
    const height = calendarDays.getBoundingClientRect().height
    allDaysHeight.value = height

    // 獲取單個(gè)日期元素的高度
    const oneDay = safeQuerySelector('.nut-calendarcard-day', 0)
    if (oneDay) {
      oneDayHeight.value = oneDay.getBoundingClientRect().height
    }
    
    // 初始化日歷的展開/收縮狀態(tài)
    toggleCalendarDays()
  } catch (error) {
    console.error('Error initializing calendar sizes:', error)
  }
}

?? 動(dòng)畫實(shí)現(xiàn)

CSS 過(guò)渡效果

.calendar-wrapper {
  position: relative;
  transition: all 0.3s ease;
  overflow-y: auto;
}

.toggle-icon {
  width: 16px;
  height: 16px;
  transition: transform 0.3s ease;
  
  /* 展開狀態(tài)下的旋轉(zhuǎn)效果 */
  &.rotate {
    transform: rotate(180deg);
  }
}

/* 全局樣式覆蓋 */
:global(.nut-calendarcard-days) {
  transition: max-height 0.3s ease;
}

加載狀態(tài)動(dòng)畫

.loading-spinner {
  width: 24px;
  height: 24px;
  border: 2px solid #f3f3f3;
  border-top: 2px solid #fa6c21;
  border-radius: 50%;
  animation: spin 1s linear infinite;
}

@keyframes spin {
  0% { transform: rotate(0deg); }
  100% { transform: rotate(360deg); }
}

?? 使用示例

<template>
  <Calender 
    :default-expanded="false"
    @change="handleDateChange"
    @toggle="handleToggle"
  />
</template>

<script setup>
const handleDateChange = (date) => {
  console.log('選中日期:', date)
}

const handleToggle = (isExpanded) => {
  console.log('展開狀態(tài):', isExpanded)
}
</script>

?? 擴(kuò)展思路

  1. 多選模式: 支持日期范圍選擇
  2. 事件標(biāo)記: 在日期上顯示事件標(biāo)記
  3. 農(nóng)歷支持: 顯示農(nóng)歷日期
  4. 主題切換: 支持多種視覺主題
  5. 國(guó)際化: 支持多語(yǔ)言和不同日期格式

?? 總結(jié)

這個(gè)展開/收縮功能的實(shí)現(xiàn)核心在于:

  1. 狀態(tài)管理: 通過(guò) isExpanded 控制顯示模式
  2. DOM 操作: 直接操作容器高度和元素顯示狀態(tài)
  3. 日期計(jì)算: 精確計(jì)算當(dāng)前周和當(dāng)前月的日期范圍
  4. 動(dòng)畫效果: 使用 CSS transition 實(shí)現(xiàn)平滑過(guò)渡
  5. 性能優(yōu)化: 防抖處理和緩存機(jī)制

通過(guò)這種實(shí)現(xiàn)方式,既保證了功能的完整性,又確保了良好的用戶體驗(yàn)和性能表現(xiàn)。這種設(shè)計(jì)模式可以應(yīng)用到其他需要展開/收縮功能的組件中,具有很好的復(fù)用價(jià)值。

全部代碼

<template>
  <!-- 日歷容器:使用 SkyCard 組件提供統(tǒng)一的卡片樣式 -->
  <SkyCard class="calendar-container">
    <!-- 日歷主體區(qū)域:包含展開/收縮狀態(tài)控制 -->
    <view class="calendar-wrapper" :class="{ expanded: isExpanded }">
      <!-- 加載狀態(tài)指示器 -->
      <view v-if="isLoading" class="loading-overlay">
        <view class="loading-spinner"></view>
      </view>

      <!-- NutUI 日歷卡片組件:提供基礎(chǔ)的日歷功能 -->
      <nut-calendar-card
        ref="calendarRef"
        v-model="value"
        :disable-day="disableDay"
        @change="onChange"
      ></nut-calendar-card>
    </view>

    <!-- 展開/收縮切換按鈕:用戶交互入口 -->
    <view
      class="toggle-button"
      :class="{ 'toggle-button--loading': isLoading }"
      role="button"
      :aria-label="isExpanded ? '收起日歷' : '展開日歷'"
      :aria-expanded="isExpanded"
      @click="handleToggleMode"
    >
      <!-- 動(dòng)態(tài)圖標(biāo):根據(jù)展開狀態(tài)顯示不同方向的箭頭 -->
      <image
        :src="images[IconImageName.Down]"
        class="toggle-icon"
        :class="{ rotate: isExpanded }"
        :alt="isExpanded ? '收起' : '展開'"
      />
    </view>
  </SkyCard>
</template>

<script setup lang="ts">
import { onMounted, ref, computed, nextTick, watch, onUnmounted } from 'vue'
import { CalendarCardDay } from '@nutui/nutui-taro'
import { useReady } from '@tarojs/taro'
import images from '~/assets/icon-image/images'
import IconImageName from '~/assets/icon-image/const'
import { SkyCard } from '~/components'

// ==================== 類型定義 ====================

/** 日期范圍接口 */
interface DateRange {
  firstDay: Date
  lastDay: Date
}

/** 組件 Props 接口 */
interface Props {
  /** 默認(rèn)展開狀態(tài) */
  defaultExpanded?: boolean
  /** 是否顯示加載狀態(tài) */
  showLoading?: boolean
  /** 自定義禁用規(guī)則 */
  customDisableDay?: (day: CalendarCardDay) => boolean
}

/** 組件 Emits 接口 */
interface Emits {
  /** 日期變化事件 */
  (e: 'change', date: Date): void
  /** 展開狀態(tài)變化事件 */
  (e: 'toggle', isExpanded: boolean): void
}

// ==================== Props & Emits ====================

const props = withDefaults(defineProps<Props>(), {
  defaultExpanded: false,
  showLoading: false,
})

const emit = defineEmits<Emits>()

// ==================== 響應(yīng)式數(shù)據(jù)定義 ====================

/** 當(dāng)前選中的日期值 */
const value = ref<Date | null>(null)

/** 控制日歷展開/收縮狀態(tài) */
const isExpanded = ref(props.defaultExpanded)

/** 所有日期元素的總高度(展開模式) */
const allDaysHeight = ref(0)

/** 單行日期元素的高度(收縮模式) */
const oneDayHeight = ref(60)

/** 加載狀態(tài) */
const isLoading = ref(false)

/** 日歷組件引用 */
const calendarRef = ref()

/** 防抖定時(shí)器 */
let debounceTimer: NodeJS.Timeout | null = null

// ==================== 計(jì)算屬性 ====================

/** 當(dāng)前日期范圍(緩存優(yōu)化) */
const currentDateRange = computed<DateRange>(() => {
  return isExpanded.value ? getMonthRange() : getWeekRange()
})

/** 切換按鈕的 ARIA 標(biāo)簽 */
const toggleButtonAriaLabel = computed(() => {
  return isExpanded.value ? '收起日歷視圖' : '展開日歷視圖'
})

// ==================== 工具函數(shù) ====================

/**
 * 防抖函數(shù) - 優(yōu)化頻繁操作
 * @param fn 要執(zhí)行的函數(shù)
 * @param delay 延遲時(shí)間
 */
const debounce = <T extends (...args: any[]) => any>(
  fn: T,
  delay: number
): T => {
  return ((...args: any[]) => {
    if (debounceTimer) {
      clearTimeout(debounceTimer)
    }
    debounceTimer = setTimeout(() => fn(...args), delay)
  }) as T
}

/**
 * 安全獲取 DOM 元素
 * @param selector 選擇器
 * @param index 索引
 * @returns DOM 元素或 null
 */
const safeQuerySelector = (
  selector: string,
  index: number = 0
): HTMLElement | null => {
  try {
    const elements = document.querySelectorAll(selector)
    return (elements[index] as HTMLElement) || null
  } catch (error) {
    console.warn(`Failed to query selector: ${selector}`, error)
    return null
  }
}

/**
 * 獲取本周的日期范圍(周日到周六)
 * 用于收縮模式下的日期范圍控制
 *
 * @returns 包含本周開始和結(jié)束日期的對(duì)象
 */
function getWeekRange(): DateRange {
  const today = new Date()

  // 計(jì)算本周日的日期(一周的開始)
  // getDay() 返回 0-6,0 表示周日,需要特殊處理
  const dayOfWeek = today.getDay() === 0 ? 7 : today.getDay()
  const firstDay = new Date(today)
  firstDay.setDate(today.getDate() - dayOfWeek) // 回退到本周日
  firstDay.setHours(0, 0, 0, 0) // 設(shè)置為當(dāng)天開始時(shí)間

  // 計(jì)算本周六的日期(一周的結(jié)束)
  const lastDay = new Date(firstDay)
  lastDay.setDate(firstDay.getDate() + 6) // 前進(jìn)6天到周六
  lastDay.setHours(23, 59, 59, 999) // 設(shè)置為當(dāng)天結(jié)束時(shí)間

  return { firstDay, lastDay }
}

/**
 * 獲取本月的日期范圍
 * 用于展開模式下的日期范圍控制
 *
 * @returns 包含本月開始和結(jié)束日期的對(duì)象
 */
function getMonthRange(): DateRange {
  const today = new Date()
  const month = today.getMonth()
  const year = today.getFullYear()

  // 本月第一天
  const firstDay = new Date(year, month, 1)
  // 本月最后一天(通過(guò)設(shè)置下月第0天實(shí)現(xiàn))
  const lastDay = new Date(year, month + 1, 0)

  return { firstDay, lastDay }
}

// ==================== 事件處理函數(shù) ====================

/**
 * 日期選擇變化回調(diào)
 * @param val 選中的日期對(duì)象
 */
const onChange = (val: Date) => {
  console.log('日期選擇變化:', val)
  emit('change', val)
}

/**
 * 切換模式處理函數(shù)(帶防抖)
 */
const handleToggleMode = debounce(() => {
  if (isLoading.value) return

  isLoading.value = true

  try {
    toggleMode()
  } finally {
    // 延遲重置加載狀態(tài),確保動(dòng)畫完成
    setTimeout(() => {
      isLoading.value = false
    }, 300)
  }
}, 100)

// ==================== 核心業(yè)務(wù)邏輯 ====================

/**
 * 智能日期禁用規(guī)則
 * 根據(jù)當(dāng)前展開狀態(tài)決定哪些日期可以被選擇
 *
 * @param day 日歷日期對(duì)象
 * @returns true表示禁用,false表示可選
 */
const disableDay = (day: CalendarCardDay): boolean => {
  // 如果有自定義禁用規(guī)則,優(yōu)先使用
  if (props.customDisableDay) {
    return props.customDisableDay(day)
  }

  const { firstDay, lastDay } = currentDateRange.value
  const current = new Date(day.year, day.month - 1, day.date)

  return current < firstDay || current > lastDay
}

// ==================== 用戶交互處理 ====================

/**
 * 切換展開/收縮模式
 * 核心功能:在兩種顯示模式間切換
 */
const toggleMode = () => {
  // 切換展開狀態(tài)
  isExpanded.value = !isExpanded.value

  // 觸發(fā)事件
  emit('toggle', isExpanded.value)

  // 控制日歷日期元素的展開/收縮
  toggleCalendarDays()
}

/**
 * 控制日歷日期元素的展開/收縮
 * 通過(guò)直接操作 DOM 實(shí)現(xiàn)平滑的視覺效果
 */
const toggleCalendarDays = () => {
  nextTick(() => {
    // 使用緩存的 DOM 查詢結(jié)果
    const calendarDays = safeQuerySelector('.nut-calendarcard-days', 1)

    if (!calendarDays) {
      console.warn('Calendar days container not found')
      return
    }

    try {
      if (isExpanded.value) {
        // 展開模式:顯示所有日期
        calendarDays.style.height = `${allDaysHeight.value}px`
        calendarDays.style.transition = 'all 0.3s ease'

        // 恢復(fù)所有日期的顯示
        const allDays = calendarDays.querySelectorAll('.nut-calendarcard-day')
        allDays.forEach((day) => {
          const dayElement = day as HTMLElement
          dayElement.style.display = ''
        })
      } else {
        // 收縮模式:只顯示當(dāng)前周
        calendarDays.style.height = `${oneDayHeight.value}px`
        calendarDays.style.transition = 'all 0.3s ease'

        // 隱藏非當(dāng)前周的日期(通過(guò)禁用狀態(tài)判斷)
        const allDays = calendarDays.querySelectorAll('.nut-calendarcard-day')
        allDays.forEach((day) => {
          const dayElement = day as HTMLElement
          if (dayElement.classList.contains('disabled')) {
            dayElement.style.display = 'none'
          } else {
            dayElement.style.display = ''
          }
        })
      }
    } catch (error) {
      console.error('Error toggling calendar days:', error)
    }
  })
}

/**
 * 初始化日歷尺寸信息
 */
const initializeCalendarSizes = async () => {
  try {
    await nextTick()

    // 獲取日期容器元素
    const calendarDays = safeQuerySelector('.nut-calendarcard-days', 1)
    if (!calendarDays) {
      console.warn('Calendar days container not found during initialization')
      return
    }

    // 獲取展開模式下的總高度
    const height = calendarDays.getBoundingClientRect().height
    allDaysHeight.value = height

    // 獲取單個(gè)日期元素的高度
    const oneDay = safeQuerySelector('.nut-calendarcard-day', 0)
    if (oneDay) {
      oneDayHeight.value = oneDay.getBoundingClientRect().height
    }

    // 初始化日歷的展開/收縮狀態(tài)
    toggleCalendarDays()
  } catch (error) {
    console.error('Error initializing calendar sizes:', error)
  }
}

// ==================== 生命周期管理 ====================

/**
 * 組件掛載時(shí)的初始化
 */
onMounted(() => {
  // 設(shè)置當(dāng)前日期為默認(rèn)選中值
  value.value = new Date()
})

/**
 * 頁(yè)面準(zhǔn)備就緒時(shí)的初始化
 * 獲取必要的 DOM 尺寸信息并初始化展開/收縮狀態(tài)
 */
useReady(() => {
  // 延遲初始化,確保 DOM 完全渲染
  setTimeout(() => {
    initializeCalendarSizes()
  }, 100)
})

/**
 * 組件卸載時(shí)的清理
 */
onUnmounted(() => {
  // 清理防抖定時(shí)器
  if (debounceTimer) {
    clearTimeout(debounceTimer)
    debounceTimer = null
  }
})

// ==================== 監(jiān)聽器 ====================

/**
 * 監(jiān)聽展開狀態(tài)變化,更新 ARIA 屬性
 */
watch(isExpanded, (newValue) => {
  // 可以在這里添加額外的狀態(tài)變化處理邏輯
  console.log('Calendar expanded state changed:', newValue)
})

/**
 * 監(jiān)聽加載狀態(tài)變化
 */
watch(isLoading, (newValue) => {
  if (newValue) {
    console.log('Calendar is loading...')
  }
})
</script>

<style lang="less" scoped>
/* ==================== 容器樣式 ==================== */

.calendar-container {
  position: relative;
}

/* ==================== 日歷包裝器樣式 ==================== */

.calendar-wrapper {
  position: relative;
  transition: all 0.3s ease;
  overflow-y: auto; /* 添加垂直滾動(dòng)支持 */

  /* 展開狀態(tài)樣式 */
  &.expanded {
    /* 可以添加展開狀態(tài)的特殊樣式 */
  }
}

/* ==================== 加載狀態(tài)樣式 ==================== */

.loading-overlay {
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  bottom: 0;
  background: rgba(255, 255, 255, 0.8);
  display: flex;
  align-items: center;
  justify-content: center;
  z-index: 10;
}

.loading-spinner {
  width: 24px;
  height: 24px;
  border: 2px solid #f3f3f3;
  border-top: 2px solid #fa6c21;
  border-radius: 50%;
  animation: spin 1s linear infinite;
}

@keyframes spin {
  0% {
    transform: rotate(0deg);
  }
  100% {
    transform: rotate(360deg);
  }
}

/* ==================== 切換按鈕樣式 ==================== */

.toggle-button {
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 12px 16px;
  cursor: pointer;
  transition: all 0.3s ease;
  user-select: none; /* 防止文本選擇 */

  /* 加載狀態(tài)樣式 */
  &--loading {
    opacity: 0.6;
    cursor: not-allowed;
    pointer-events: none;
  }

  /* 點(diǎn)擊反饋效果 */
  &:active:not(&--loading) {
    opacity: 0.8;
    transform: scale(0.95);
  }

  /* 焦點(diǎn)狀態(tài)(無(wú)障礙訪問(wèn)) */
  &:focus-visible {
    outline: 2px solid #3498db;
    outline-offset: 2px;
  }

  /* 箭頭圖標(biāo)樣式 */
  .toggle-icon {
    width: 16px;
    height: 16px;
    transition: transform 0.3s ease;
    pointer-events: none; /* 防止圖標(biāo)干擾點(diǎn)擊事件 */

    /* 展開狀態(tài)下的旋轉(zhuǎn)效果 */
    &.rotate {
      transform: rotate(180deg);
    }
  }
}

/* ==================== 全局樣式覆蓋 ==================== */

/* 控制日歷日期元素的展開/收縮過(guò)渡效果 */
:global(.nut-calendarcard-days) {
  transition: max-height 0.3s ease;
}

/* ==================== 響應(yīng)式設(shè)計(jì) ==================== */

@media (max-width: 768px) {
  .toggle-button {
    padding: 10px 12px;

    .toggle-icon {
      width: 14px;
      height: 14px;
    }
  }

  .loading-spinner {
    width: 20px;
    height: 20px;
  }
}

/* ==================== 高對(duì)比度模式支持 ==================== */

@media (prefers-contrast: high) {
  .toggle-button {
    border: 1px solid currentColor;

    &:focus-visible {
      outline: 3px solid currentColor;
    }
  }

  .loading-overlay {
    background: rgba(0, 0, 0, 0.8);
  }
}

/* ==================== 減少動(dòng)畫模式支持 ==================== */

@media (prefers-reduced-motion: reduce) {
  .calendar-wrapper,
  .toggle-button,
  .toggle-icon,
  :global(.nut-calendarcard-days) {
    transition: none !important;
  }

  .loading-spinner {
    animation: none;
  }
}
</style>


本文檔持續(xù)更新中,如有問(wèn)題或建議,歡迎反饋。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容