Procházet zdrojové kódy

完善刷题总量

shmily1213 před 1 měsícem
rodič
revize
1adc71d3a1

+ 207 - 0
README.md

@@ -0,0 +1,207 @@
+# IE-plus 项目
+
+## 项目简介
+这是一个基于 uni-app 的学习应用项目,使用 Vite 作为构建工具。
+
+## 最近修改
+
+### 日历组件全面升级 - 新增多项功能
+**修改时间**: 2024年
+**修改内容**: 为 uni-calendar 组件新增了多项实用功能,提升组件的灵活性和可定制性
+
+#### 新增功能
+
+##### 1. 周日位置控制
+- **参数**: `sundayFirst` (Boolean, 默认: false)
+- **功能**: 控制周日显示在第一列还是最后一列
+- **用法**: 
+  ```vue
+  <uni-calendar :sunday-first="true" />  <!-- 周日在前 -->
+  <uni-calendar :sunday-first="false" /> <!-- 周日在后 -->
+  ```
+
+##### 2. 显示模式控制
+- **参数**: `displayMode` (String, 默认: 'month')
+- **可选值**: 'month' | 'week'
+- **功能**: 控制按月显示还是按周显示
+- **特点**: 按周显示时,其他周的日期会显示但置灰,只有指定周正常显示
+- **方法**: 
+  - `preWeek()`: 切换到上一周
+  - `nextWeek()`: 切换到下一周
+  - `setWeekNumber(weekNumber)`: 设置指定周数
+- **事件**: `change-week`: 周数变化时触发
+- **用法**:
+  ```vue
+  <uni-calendar 
+    ref="calendar"
+    display-mode="week" 
+    :week-number="currentWeek"
+    @change-week="onWeekChange" />
+  
+  <!-- 在父组件中调用切换方法 -->
+  <button @click="$refs.calendar.preWeek()">上一周</button>
+  <button @click="$refs.calendar.nextWeek()">下一周</button>
+  <button @click="$refs.calendar.setWeekNumber(3)">第3周</button>
+  ```
+
+##### 3. 今天高亮控制
+- **参数**: `highlightToday` (Boolean, 默认: true)
+- **功能**: 控制是否高亮今天,影响"回到今日"按钮显示和今天日期样式
+- **用法**:
+  ```vue
+  <uni-calendar :highlight-today="false" />  <!-- 不高亮今天 -->
+  ```
+
+##### 4. 工具栏显示控制
+- **参数**: `showToolbar` (Boolean, 默认: true)
+- **功能**: 控制是否显示顶部的月份切换工具栏
+- **用法**:
+  ```vue
+  <uni-calendar :show-toolbar="false" />  <!-- 隐藏工具栏 -->
+  ```
+
+##### 5. 自定义日期样式插槽
+- **插槽**: `calendar-item`
+- **功能**: 允许外部自定义每个日期项的显示样式
+- **特点**: 可以完全自定义整个日期项,包含更多参数
+- **用法**:
+  ```vue
+  <!-- 自定义整个日期项 -->
+  <uni-calendar>
+    <template #calendar-item="{ weeks, calendar, selected, lunar, highlightToday, weekIndex, weeksIndex }">
+      <view class="enhanced-calendar-item" :class="{ 'not-current-month': !weeks.isCurrentMonth }">
+        <text class="date-number">{{ weeks.date }}</text>
+        <view class="date-indicators">
+          <view v-if="weeks.extraInfo" class="indicator-dot"></view>
+          <view v-if="weeks.isDay" class="today-badge">今</view>
+          <view v-if="!weeks.isCurrentMonth" class="month-badge">非本月</view>
+        </view>
+      </view>
+  </template>
+</uni-calendar>
+```
+
+#### 数据属性说明
+每个日期项(weeks)包含以下属性:
+- `fullDate`: 完整日期字符串 (如: "2024-01-15")
+- `year`: 年份
+- `month`: 月份
+- `date`: 日期数字
+- `isDay`: 是否为今天
+- `disable`: 是否禁用
+- `isCurrentMonth`: **是否为当前月份** (true: 本月, false: 上月/下月)
+- `isWeekModeDisabled`: 按周模式下的置灰标识
+- `beforeMultiple`: 多选开始标识
+- `afterMultiple`: 多选结束标识
+- `multiple`: 多选中间标识
+- `lunar`: 农历信息
+- `extraInfo`: 额外信息(打点数据)
+
+#### 修改文件
+1. `src/uni_modules/uni-calendar/components/uni-calendar/uni-calendar.vue`
+   - 新增5个props参数
+   - 添加计算属性 `filteredWeeks` 处理按周显示
+   - 修改模板支持条件渲染和插槽
+   - 优化星期标题显示逻辑
+
+2. `src/uni_modules/uni-calendar/components/uni-calendar/util.js`
+   - 新增 `sundayFirst` 参数支持
+   - 修改 `_getWeek` 方法支持周日位置控制
+   - 优化日期计算逻辑
+
+3. `src/uni_modules/uni-calendar/components/uni-calendar/uni-calendar-item.vue`
+   - 新增 `highlightToday` 参数支持
+   - 添加插槽支持自定义日期样式
+   - 优化今天高亮显示逻辑
+
+#### 完整参数列表
+```vue
+<uni-calendar
+  :sunday-first="false"           <!-- 周日位置控制 -->
+  display-mode="month"            <!-- 显示模式 -->
+  :week-number="1"                <!-- 按周显示时的周数 -->
+  :highlight-today="true"         <!-- 是否高亮今天 -->
+  :show-toolbar="true"            <!-- 是否显示工具栏 -->
+  :insert="true"                  <!-- 插入模式 -->
+  :lunar="false"                  <!-- 显示农历 -->
+  :range="false"                  <!-- 范围选择 -->
+  :selected="[]"                  <!-- 打点信息 -->
+  :start-date=""                  <!-- 开始日期 -->
+  :end-date=""                    <!-- 结束日期 -->
+  :show-month="true"              <!-- 显示月份背景 -->
+  :clear-date="true"              <!-- 清空日期 -->
+  :readonly="false"               <!-- 只读模式 -->
+>
+  <template #calendar-item="{ weeks, calendar, selected, lunar, highlightToday, weekIndex, weeksIndex }">
+    <!-- 自定义整个日期项 -->
+  </template>
+</uni-calendar>
+```
+
+#### 日期范围API
+Calendar 工具类提供两个实用的日期范围获取方法:
+
+##### 1. 获取本月日期范围
+```javascript
+import Calendar from './util.js'
+
+const calendar = new Calendar()
+const monthRange = calendar.getCurrentMonthRange('2024-10-15')
+// 返回: {startDate: '2024-10-01', endDate: '2024-10-31', totalDays: 31, totalWeeks: 5}
+```
+
+##### 2. 获取指定周日期范围
+```javascript
+// 获取当前日期所在的周
+const currentWeek = calendar.getCurrentWeekRange('2024-10-15')
+// 返回: {startDate: '2024-10-13', endDate: '2024-10-19', totalDays: 7, currentWeek: 3, totalWeeks: 5}
+
+// 获取指定周数
+const week2 = calendar.getCurrentWeekRange('2024-10-15', 2)
+// 返回: {startDate: '2024-10-06', endDate: '2024-10-12', totalDays: 7, currentWeek: 2, totalWeeks: 5}
+```
+
+**参数说明:**
+- `date`: 日期字符串,可选,默认为当前日期
+- `weekNumber`: 周数,从1开始,可选,不传时自动计算指定日期所在的周
+
+**返回值说明:**
+- `startDate`: 开始日期
+- `endDate`: 结束日期  
+- `totalDays`: 总天数
+- `totalWeeks`: 总周数(实际包含本月日期的周数)
+- `currentWeek`: 当前周数(仅周范围方法)
+
+#### 技术细节
+- 使用 Vue 3 的 Composition API 和插槽系统
+- 通过计算属性实现按周显示的数据过滤
+- 使用条件渲染控制工具栏和今天高亮显示
+- 支持完全自定义的日期样式渲染
+- 提供完整的周切换API,支持跨月份切换
+- 智能处理边界情况(第一周/最后一周的跨月切换)
+- 提供实用的日期范围获取API,支持外部调用
+
+#### 影响范围
+- 所有使用 uni-calendar 组件的页面都可以使用新功能
+- 向后兼容,现有代码无需修改
+- 新功能为可选参数,默认行为保持不变
+
+## 项目结构
+```
+src/
+├── uni_modules/          # uni-app 模块
+│   └── uni-calendar/     # 日历组件
+├── pagesStudy/           # 学习相关页面
+├── components/           # 公共组件
+└── ...
+```
+
+## 开发说明
+- 使用 Vue 3 + TypeScript
+- 构建工具:Vite
+- UI框架:uni-app
+- 样式:SCSS + Tailwind CSS
+
+## 注意事项
+- 修改日历组件后,请确保所有使用该组件的页面都能正常显示
+- 如果需要在其他项目中复用此修改,请同时复制两个文件的修改内容

+ 368 - 0
src/composables/useCalendar.ts

@@ -0,0 +1,368 @@
+import { ref, computed, watch } from 'vue';
+// @ts-ignore
+import CalendarUtil from '@/uni_modules/uni-calendar/components/uni-calendar/util.js';
+
+export interface PracticeData {
+  date: string;
+  info: number;
+  questionNum: number;
+}
+
+export interface PracticeStatistics {
+  totalQuestions: number; // 刷题总量
+  averageAccuracy: number; // 平均正确率
+  practiceDays: number; // 练习天数
+  totalPracticeDays: number; // 累计刷题天数(所有打点数据的总长度)
+}
+
+export function useCalendar() {
+  const calendarUtil = new CalendarUtil();
+  const selected = ref<PracticeData[]>([]);
+  const statistics = ref<PracticeStatistics>({
+    totalQuestions: 0,
+    averageAccuracy: 0,
+    practiceDays: 0,
+    totalPracticeDays: 0
+  });
+  const currentWeekNumber = ref(1); // 用于外部数据计算
+  const calendarWeekNumber = ref(1); // 专门用于日历组件显示
+  const currentDate = ref(new Date());
+  const displayMode = ref<'year' | 'month' | 'week'>('year');
+  const loading = ref(false);
+
+  // 计算当前周范围
+  const currentWeekRange = computed(() => {
+    return calendarUtil.getCurrentWeekRange(currentDate.value, currentWeekNumber.value);
+  });
+
+  // 计算当前月份范围
+  const currentMonthRange = computed(() => {
+    return calendarUtil.getCurrentMonthRange(currentDate.value);
+  });
+
+  // 计算是否可以切换到上一周/月/年
+  const canGoPrev = computed(() => {
+    const today = new Date();
+    const current = currentDate.value;
+
+    if (displayMode.value === 'year') {
+      return current.getFullYear() > 2020; // 假设最早年份是2020年
+    } else if (displayMode.value === 'week') {
+      return currentWeekNumber.value > 1 || current.getMonth() > 0;
+    } else {
+      // 月份模式:支持跨年,只要不是2020年1月就可以往前切换
+      return !(current.getFullYear() === 2020 && current.getMonth() === 0);
+    }
+  });
+
+  // 计算是否可以切换到下一周/月/年(不能超过当前日期)
+  const canGoNext = computed(() => {
+    const today = new Date();
+    const current = currentDate.value;
+
+    if (displayMode.value === 'year') {
+      return current.getFullYear() < today.getFullYear();
+    } else if (displayMode.value === 'week') {
+      // 检查是否已经到达当前日期
+      const isCurrentMonth = current.getFullYear() === today.getFullYear() &&
+        current.getMonth() === today.getMonth();
+
+      if (isCurrentMonth) {
+        // 当前月份,检查是否超过当前周
+        const firstDayOfMonth = new Date(current.getFullYear(), current.getMonth(), 1);
+        const firstWeekday = firstDayOfMonth.getDay() === 0 ? 7 : firstDayOfMonth.getDay();
+        const currentWeekInMonth = Math.ceil((today.getDate() + firstWeekday - 1) / 7);
+        return currentWeekNumber.value < currentWeekInMonth;
+      } else {
+        // 非当前月份,检查是否超过当前月份
+        return current.getFullYear() < today.getFullYear() ||
+          (current.getFullYear() === today.getFullYear() && current.getMonth() < today.getMonth());
+      }
+    } else {
+      // 月份模式,不能超过当前月份
+      return current.getFullYear() < today.getFullYear() ||
+        (current.getFullYear() === today.getFullYear() && current.getMonth() < today.getMonth());
+    }
+  });
+
+  // 模拟接口请求打点数据
+  // ==================== 真实接口对接说明 ====================
+  // 当对接真实接口时,请替换以下部分:
+  // 1. 删除模拟网络延迟:await new Promise(resolve => setTimeout(resolve, 300));
+  // 2. 替换为真实API调用:
+  //    const response = await api.getPracticeData({ startDate, endDate });
+  //    return response.data;
+  // 3. 确保返回的数据格式符合 PracticeData[] 接口
+  // 4. 统计数据会从返回的练习数据中自动计算,无需额外处理
+  // ========================================================
+  const fetchPracticeData = async (startDate: string, endDate: string): Promise<PracticeData[]> => {
+    console.log('请求数据', startDate, endDate)
+    // ========== 模拟网络延迟 - 真实接口时删除此部分 ==========
+    await new Promise(resolve => setTimeout(resolve, 300));
+
+    // ========== 模拟数据生成逻辑 - 真实接口时替换为API调用 ==========
+    // 生成日期范围内的随机几天数据
+    const start = new Date(startDate);
+    const end = new Date(endDate);
+    const practiceData: PracticeData[] = [];
+
+    // 计算总天数
+    const totalDays = Math.ceil((end.getTime() - start.getTime()) / (1000 * 60 * 60 * 24)) + 1;
+
+    // 根据时间范围决定练习天数
+    let practiceDays: number;
+    const today = new Date();
+    const isCurrentPeriod = start <= today && end >= today;
+    const isYearData = totalDays > 100; // 如果总天数超过100天,认为是年份数据
+
+    if (isYearData) {
+      // 年份数据,生成较多的练习天数
+      practiceDays = Math.floor(Math.random() * 100) + 50; // 50-150天
+    } else if (isCurrentPeriod) {
+      // 当前时间段,练习天数较少
+      practiceDays = Math.floor(Math.random() * 2) + 2; // 2-4天
+    } else if (start < today) {
+      // 过去时间段,练习天数较多
+      practiceDays = Math.floor(Math.random() * 4) + 4; // 4-8天
+    } else {
+      // 未来时间段,没有练习数据
+      return [];
+    }
+
+    // 确保不超过总天数
+    practiceDays = Math.min(practiceDays, totalDays);
+    const practiceDates = new Set<string>();
+
+    // 随机选择练习日期
+    while (practiceDates.size < practiceDays) {
+      const randomDay = Math.floor(Math.random() * totalDays);
+      const practiceDate = new Date(start);
+      practiceDate.setDate(practiceDate.getDate() + randomDay);
+      const dateStr = practiceDate.toISOString().split('T')[0];
+      practiceDates.add(dateStr);
+    }
+
+    // 为每个练习日期生成数据
+    practiceDates.forEach(dateStr => {
+      const questionNum = Math.floor(Math.random() * 20) + 10; // 10-30题
+      const correctRate = Math.random() * 0.4 + 0.6; // 0.6-1.0 的正确率
+
+      practiceData.push({
+        date: dateStr,
+        info: Math.round(correctRate * 100) / 100, // 四舍五入到两位小数
+        questionNum: questionNum
+      });
+    });
+
+    return practiceData.sort((a, b) => a.date.localeCompare(b.date));
+  };
+
+
+  // 更新日历数据 - 统一的数据更新方法
+  const updateCalendarData = async (date?: Date, mode?: 'year' | 'month' | 'week', modeNumber?: number) => {
+    loading.value = true;
+    try {
+      // 使用传入的参数或当前状态
+      const targetDate = date || currentDate.value;
+      const targetMode = mode || displayMode.value;
+      const targetModeNumber = modeNumber !== undefined ? modeNumber : 
+        (targetMode === 'week' ? currentWeekNumber.value : 
+         targetMode === 'month' ? (date ? date.getMonth() + 1 : currentDate.value.getMonth() + 1) : 
+         targetDate.getFullYear());
+
+      // 计算数据范围
+      let startDate: string, endDate: string;
+      
+      if (targetMode === 'year') {
+        // 年份模式:从1月1日到12月31日
+        const year = targetModeNumber;
+        startDate = `${year}-01-01`;
+        endDate = `${year}-12-31`;
+      } else if (targetMode === 'week') {
+        // 周模式:根据指定周数计算范围
+        const range = calendarUtil.getCurrentWeekRange(targetDate, targetModeNumber);
+        startDate = range.startDate;
+        endDate = range.endDate;
+      } else {
+        // 月份模式:根据指定月份计算范围
+        const year = targetDate.getFullYear();
+        const month = targetModeNumber; // 使用传入的月份参数
+        const start = new Date(year, month - 1, 1);
+        const end = new Date(year, month, 0); // 获取该月最后一天
+        startDate = start.getFullYear() + '-' + String(start.getMonth() + 1).padStart(2, '0') + '-' + String(start.getDate()).padStart(2, '0');
+        endDate = end.getFullYear() + '-' + String(end.getMonth() + 1).padStart(2, '0') + '-' + String(end.getDate()).padStart(2, '0');
+      }
+
+      // 请求数据
+      const practiceData = await fetchPracticeData(startDate, endDate);
+
+      // 更新状态
+      currentDate.value = targetDate;
+      displayMode.value = targetMode;
+      
+      if (targetMode === 'week') {
+        currentWeekNumber.value = targetModeNumber;
+        calendarWeekNumber.value = targetModeNumber;
+      }
+      
+      selected.value = practiceData;
+
+      // 计算统计数据
+      const totalQuestions = practiceData.reduce((sum, item) => sum + item.questionNum, 0);
+      const averageAccuracy = practiceData.length > 0
+        ? practiceData.reduce((sum, item) => sum + item.info, 0) / practiceData.length
+        : 0;
+      const practiceDays = practiceData.length;
+      const totalPracticeDays = practiceData.length; // 累计刷题天数就是打点数据的长度
+
+      statistics.value = {
+        totalQuestions,
+        averageAccuracy: Math.round(averageAccuracy * 100) / 100,
+        practiceDays,
+        totalPracticeDays
+      };
+    } finally {
+      loading.value = false;
+    }
+  };
+
+
+  // 切换到上一周
+  const goToPrevWeek = async () => {
+    if (!canGoPrev.value) return;
+
+    if (currentWeekNumber.value > 1) {
+      await updateCalendarData(undefined, 'week', currentWeekNumber.value - 1);
+    } else {
+      // 切换到上个月的最后一周
+      const preDate = new Date(currentDate.value);
+      preDate.setMonth(preDate.getMonth() - 1);
+      calendarUtil.setDate(preDate);
+      const range = calendarUtil.getCurrentWeekRange(preDate);
+      await updateCalendarData(preDate, 'week', range.totalWeeks);
+    }
+  };
+
+  // 切换到下一周
+  const goToNextWeek = async () => {
+    if (!canGoNext.value) return;
+
+    const range = currentWeekRange.value;
+    if (currentWeekNumber.value < range.totalWeeks) {
+      await updateCalendarData(undefined, 'week', currentWeekNumber.value + 1);
+    } else {
+      // 切换到下个月的第一周
+      const nextDate = new Date(currentDate.value);
+      nextDate.setMonth(nextDate.getMonth() + 1);
+      calendarUtil.setDate(nextDate);
+      await updateCalendarData(nextDate, 'week', 1);
+    }
+  };
+
+  // 切换到上一个月
+  const goToPrevMonth = async () => {
+    if (!canGoPrev.value) return;
+
+    const currentMonth = currentDate.value.getMonth();
+    const targetMonth = currentMonth === 0 ? 12 : currentMonth; // 如果当前是1月,目标月份是12月
+    const targetYear = currentMonth === 0 ? currentDate.value.getFullYear() - 1 : currentDate.value.getFullYear();
+    
+    const preDate = new Date(targetYear, targetMonth - 1, 1);
+    await updateCalendarData(preDate, 'month', targetMonth);
+  };
+
+  // 切换到下一个月
+  const goToNextMonth = async () => {
+    if (!canGoNext.value) return;
+
+    const currentMonth = currentDate.value.getMonth();
+    const targetMonth = currentMonth === 11 ? 1 : currentMonth + 2; // 如果当前是12月,目标月份是1月
+    const targetYear = currentMonth === 11 ? currentDate.value.getFullYear() + 1 : currentDate.value.getFullYear();
+    
+    const nextDate = new Date(targetYear, targetMonth - 1, 1);
+    await updateCalendarData(nextDate, 'month', targetMonth);
+  };
+
+  // 设置显示模式
+  const setDisplayMode = async (mode: 'year' | 'month' | 'week') => {
+    await updateCalendarData(undefined, mode);
+  };
+
+  // 根据年份跳转
+  const goToYear = async (year: number) => {
+    const targetDate = new Date(year, 0, 1); // 年初
+    await updateCalendarData(targetDate, 'year', year);
+  };
+
+  // 根据年份和月份跳转
+  const goToYearMonth = async (year: number, month: number) => {
+    const targetDate = new Date(year, month - 1, 1);
+    await updateCalendarData(targetDate, 'month', month);
+  };
+
+  // 根据周数跳转
+  const goToWeek = async (weekNumber: number) => {
+    if (weekNumber >= 1 && weekNumber <= currentWeekRange.value.totalWeeks) {
+      await updateCalendarData(undefined, 'week', weekNumber);
+    }
+  };
+
+  // 获取当前年份
+  const currentYear = computed(() => currentDate.value.getFullYear());
+
+  // 获取当前月份
+  const currentMonth = computed(() => currentDate.value.getMonth() + 1);
+
+  // 初始化下拉菜单数据
+  const initializeFormData = () => {
+    const today = new Date();
+    const currentYearToday = today.getFullYear();
+
+    return {
+      year: currentYearToday.toString(),
+      month: '', // 默认不选择月份
+      week: '' // 默认不选择周
+    };
+  };
+
+  // 初始化数据 - 默认按年份模式初始化
+  const init = async () => {
+    const today = new Date();
+    await updateCalendarData(today, 'year', today.getFullYear());
+  };
+
+  return {
+    // 数据
+    calendarUtil,
+    selected,
+    statistics,
+    currentWeekNumber,
+    calendarWeekNumber,
+    currentDate,
+    displayMode,
+    currentWeekRange,
+    currentMonthRange,
+    
+    // 状态
+    canGoPrev,
+    canGoNext,
+    loading,
+    
+    // 计算属性
+    currentYear,
+    currentMonth,
+    
+    // 方法
+    updateCalendarData,
+    goToPrevWeek,
+    goToNextWeek,
+    goToPrevMonth,
+    goToNextMonth,
+    goToYear,
+    goToYearMonth,
+    goToWeek,
+    setDisplayMode,
+    initializeFormData,
+    init
+  };
+}

+ 417 - 59
src/pagesStudy/components/practice-table.vue

@@ -22,13 +22,13 @@
           <ie-picker ref="pickerRef" v-model="form.week" :list="weekList" placeholder="请选择" title="选择周" :fontSize="26"
             icon="arrow-down" key-label="label" key-value="value" @change="handleWeekChange">
             <template #default="{ label }">
-              <text>{{ label }}</text>
+              <text>{{ label }}</text>
             </template>
           </ie-picker>
         </view>
-        <view class="btn-wrap" @click="handleOpenCalendar">
+        <view :class="calendarButtonClass" @click="canOpenCalendar ? handleOpenCalendar() : null">
           <text>刷题日历</text>
-          <uv-icon name="search" size="16" color="white" />
+          <uv-icon name="search" size="16" :color="canOpenCalendar ? 'white' : '#CCCCCC'" />
         </view>
       </view>
     </view>
@@ -42,10 +42,10 @@
     </view>
     <view class="mt-30 flex items-center">
       <text>已累计刷题</text>
-      <text class="text-32 text-primary">24</text>
+      <text class="text-32 text-primary">{{ totalPracticeDays }}</text>
       <text>天~</text>
     </view>
-    <view class="mt-30">
+    <view v-if="(form.month || form.week) && displayMode !== 'year'" class="mt-30">
       <ie-table :tableColumns="tableColumns" :data="tableDate">
         <template #date="{ item }">
           <text class="font-bold">{{ item.date }}</text>
@@ -59,47 +59,215 @@
       </ie-table>
     </view>
     <root-portal>
-      <uv-popup ref="calendarPopupRef" mode="bottom" :round="16">
-        <view class="min-h-200">
-          <uni-calendar :insert="true" :lunar="false" :readonly="true" :showMonth="false" :selected="selected"
-            :endDate="endDate" @change-month="handleCalendarChange" />
+      <uv-popup ref="calendarPopupRef" mode="bottom" :round="16" v-if="canOpenCalendar">
+        <view class="h-[480px]">
+          <view class="h-108 flex items-center justify-center border-0 border-b border-solid border-border">
+            <view :class="prevButtonClass" @click="canGoPrev && !loading ? handlePrev() : null">
+              <uv-icon name="arrow-left" size="10" :color="canGoPrev && !loading ? '#808080' : '#CCCCCC'" />
+            </view>
+            <view class="mx-40 text-30 text-fore-title font-bold">
+              <text>{{ calendarTitle }}</text>
+            </view>
+            <view :class="nextButtonClass" @click="canGoNext && !loading ? handleNext() : null">
+              <uv-icon name="arrow-right" size="10" :color="canGoNext && !loading ? '#808080' : '#CCCCCC'" />
+            </view>
+          </view>
+          <view class="relative">
+            <view class="px-40 py-20 flex items-center justify-between">
+              <view class=" text-28">
+                <text>{{ calendarSubTitle }}</text>
+                <text class="text-32 text-primary font-bold">{{ practiceDate }}</text>
+                <text>天~</text>
+              </view>
+              <uv-icon name="question-circle" size="18" color="#31a0fc" />
+            </view>
+
+            <uni-calendar ref="calendarRef" :insert="true" :lunar="false" :readonly="true" :showMonth="false"
+              :sundayFirst="false" :highlightToday="false" :showToolbar="false" :displayMode="displayMode"
+              :weekNumber="calendarWeekNumber" :selected="selected" :date="currentDate"
+              @change-week="handleCalendarWeekChange" @monthSwitch="handleCalendarMonthSwitch">
+              <template #calendar-item="{ weeks }">
+                <view class="calendar-item" :class="{
+                  'calendar-item--week-mode-disabled': weeks.isWeekModeDisabled,
+                  'uni-calendar-item--disable': !weeks.isCurrentMonth,
+                  'calendar-item--valid': weeks.extraInfo && weeks.extraInfo.info >= 0.7,
+                  'calendar-item--invalid': weeks.extraInfo && weeks.extraInfo.info < 0.7
+                }">
+                  <view class="date">{{ weeks.date }}</view>
+                  <view class="info">
+                    <text v-if="weeks.extraInfo && weeks.extraInfo.info">{{ weeks.extraInfo.info * 100 }}%</text>
+                  </view>
+                </view>
+              </template>
+            </uni-calendar>
+            <!-- Loading 覆盖层 -->
+            <view v-if="loading" class="calendar-loading-overlay">
+              <uv-loading-icon mode="circle" size="32" color="#31a0fc" />
+            </view>
+          </view>
         </view>
       </uv-popup>
     </root-portal>
   </view>
 </template>
 <script lang="ts" setup>
+import { nextTick } from 'vue';
 import { TableColumnConfig } from '@/types';
 import ieEchart from './ie-echart/ie-echart.vue';
-import { CSSProperties } from 'vue';
+import { useCalendar } from '@/composables/useCalendar';
+
+// 使用 useCalendar composable
+const {
+  selected,
+  statistics,
+  currentWeekNumber,
+  calendarWeekNumber,
+  currentDate,
+  displayMode,
+  currentWeekRange,
+  currentMonthRange,
+  canGoPrev,
+  canGoNext,
+  loading,
+  currentYear,
+  currentMonth,
+  goToPrevWeek,
+  goToNextWeek,
+  goToPrevMonth,
+  goToNextMonth,
+  goToYear,
+  goToYearMonth,
+  goToWeek,
+  setDisplayMode,
+  updateCalendarData,
+  initializeFormData,
+  init: initCalendar
+} = useCalendar();
+
+// 表单数据 - 与日历同步
 const form = ref({
   year: '',
   month: '',
   week: ''
 })
-const year = ref('');
-const yearList = ref([
-  {
-    label: '2025',
-    value: '2025'
+
+// 年份列表(当前年份前后各2年,但不能超过当前年份)
+const yearList = computed(() => {
+  const currentYearValue = currentYear.value;
+  const today = new Date();
+  const currentYearToday = today.getFullYear();
+  const years = [];
+
+  for (let i = currentYearValue - 2; i <= currentYearToday; i++) {
+    years.push({
+      label: i.toString(),
+      value: i.toString()
+    });
   }
-]);
-const monthList = ref([
-  {
-    label: '10月',
-    value: '1'
+  return years;
+});
+
+// 月份列表(不能超过当前月份)
+const monthList = computed(() => {
+  const today = new Date();
+  const currentYearToday = today.getFullYear();
+  const currentMonthToday = today.getMonth() + 1;
+  const selectedYear = parseInt(form.value.year) || currentYear.value;
+
+  const months = [];
+  const maxMonth = selectedYear === currentYearToday ? currentMonthToday : 12;
+
+  for (let i = 1; i <= maxMonth; i++) {
+    months.push({
+      label: `${i}月`,
+      value: i.toString()
+    });
   }
-]);
-const weekList = ref([
-  {
-    label: '1周',
-    value: '1'
+  return months;
+});
+
+// 周列表 - 根据当前月份动态生成,不显示未来周数
+const weekList = computed(() => {
+  const weeks = [];
+  const totalWeeks = currentWeekRange.value?.totalWeeks || 1;
+
+  // 计算当前周数
+  const today = new Date();
+  const current = new Date(currentYear.value, currentMonth.value - 1, 1);
+  const isCurrentMonth = current.getFullYear() === today.getFullYear() &&
+    current.getMonth() === today.getMonth();
+
+  let maxWeek = totalWeeks;
+  if (isCurrentMonth) {
+    // 当前月份,只显示到当前周
+    const firstDayOfMonth = new Date(current.getFullYear(), current.getMonth(), 1);
+    const firstWeekday = firstDayOfMonth.getDay() === 0 ? 7 : firstDayOfMonth.getDay();
+    const currentWeekInMonth = Math.ceil((today.getDate() + firstWeekday - 1) / 7);
+    maxWeek = Math.min(totalWeeks, currentWeekInMonth);
   }
-]);
+
+  for (let i = 1; i <= maxWeek; i++) {
+    weeks.push({
+      label: `第${i}周`,
+      value: i.toString()
+    });
+  }
+  return weeks;
+});
+
+// 检查是否可以选择日历
+const canOpenCalendar = computed(() => {
+  // 必须选择年份
+  if (!form.value.year) {
+    return false;
+  }
+
+  // 如果是week模式,必须选择月份和周
+  if (displayMode.value === 'week') {
+    return !!(form.value.month && form.value.week);
+  }
+
+  // 如果是month模式,必须选择月份
+  if (displayMode.value === 'month') {
+    return !!form.value.month;
+  }
+
+  // 如果是year模式,不能打开日历(年份模式不显示记录表)
+  return false;
+});
+
+// 导航按钮样式计算属性
+const prevButtonClass = computed(() => {
+  return {
+    'w-34 h-34 rounded-full flex items-center justify-center transition-all duration-200': true,
+    'bg-[#EEF4FA] cursor-pointer': canGoPrev.value && !loading.value,
+    'bg-[#F5F5F5] cursor-not-allowed': !canGoPrev.value || loading.value,
+    'opacity-50': loading.value
+  };
+});
+
+const nextButtonClass = computed(() => {
+  return {
+    'w-34 h-34 rounded-full flex items-center justify-center transition-all duration-200': true,
+    'bg-[#EEF4FA] cursor-pointer': canGoNext.value && !loading.value,
+    'bg-[#F5F5F5] cursor-not-allowed': !canGoNext.value || loading.value,
+    'opacity-50': loading.value
+  };
+});
+
+const calendarButtonClass = computed(() => {
+  return {
+    'btn-wrap transition-all duration-200': true,
+    'opacity-50 cursor-not-allowed': !canOpenCalendar.value,
+    'cursor-pointer': canOpenCalendar.value
+  };
+});
+
+// 图表配置
 const options1 = computed(() => {
   return {
     title: {
-      text: '680',
+      text: statistics.value.totalQuestions.toString(),
       subtext: '{a|刷题总量}',
       left: 'center',
       top: '34%',
@@ -152,10 +320,13 @@ const options1 = computed(() => {
     ]
   };
 });
+
 const options2 = computed(() => {
+  const accuracy = Math.round(statistics.value.averageAccuracy * 100);
+
   return {
     title: {
-      text: '75%',
+      text: `${accuracy}%`,
       subtext: '{a|正确率}',
       left: 'center',
       top: '34%',
@@ -208,6 +379,8 @@ const options2 = computed(() => {
     ]
   };
 });
+
+// 表格配置
 const tableColumns = ref<TableColumnConfig[]>([
   {
     prop: 'date',
@@ -228,43 +401,169 @@ const tableColumns = ref<TableColumnConfig[]>([
     slot: 'correctNum'
   }
 ]);
-const cellStyle: CSSProperties = {
-  padding: '30rpx 20rpx'
-}
-const tableDate = ref([
-  {
-    date: '2025.09.11',
-    questionNum: 10,
-    correctNum: 10
-  },
-  {
-    date: '2025.09.12',
-    questionNum: 10,
-    correctNum: 10
+
+// 表格数据(从 selected 数据转换)
+const tableDate = computed(() => {
+  return selected.value.map(item => ({
+    date: item.date.replace(/-/g, '.'),
+    questionNum: item.questionNum, // 使用 composable 中的数据
+    correctNum: Math.round(item.info * 100) + '%' // 四舍五入到整数
+  }));
+});
+
+// 练习天数统计
+const practiceDate = computed(() => statistics.value.practiceDays);
+
+// 累计刷题天数统计
+const totalPracticeDays = computed(() => {
+  return statistics.value.totalPracticeDays;
+});
+
+// 日历标题
+const calendarTitle = computed(() => {
+  if (displayMode.value === 'week') {
+    const year = currentWeekRange.value?.startDate?.split('-')[0] || new Date().getFullYear();
+    const month = currentWeekRange.value?.startDate?.split('-')[1] || new Date().getMonth() + 1;
+    return `${year}年-第${month}月-第${currentWeekNumber.value}周`;
+  } else {
+    const year = currentMonthRange.value?.startDate?.split('-')[0] || new Date().getFullYear();
+    const month = currentMonthRange.value?.startDate?.split('-')[1] || new Date().getMonth() + 1;
+    return `${year}年-第${month}月`;
+  }
+});
+
+// 日历副标题
+const calendarSubTitle = computed(() => {
+  if (displayMode.value === 'week') {
+    const startDate = currentWeekRange.value?.startDate?.slice(5).replace('-', '月') + '日' || '';
+    const endDate = currentWeekRange.value?.endDate?.slice(5).replace('-', '月') + '日' || '';
+    return `本周(${startDate}-${endDate})已累计刷题`;
+  } else {
+    const month = currentMonthRange.value?.startDate?.split('-')[1] || new Date().getMonth() + 1;
+    return `本月(${month}月)已累计刷题`;
+  }
+});
+
+// 导航方法
+const handlePrev = async () => {
+  if (loading.value) return; // 加载中时禁止操作
+  uni.$ie.showLoading();
+  if (displayMode.value === 'week') {
+    await goToPrevWeek();
+  } else {
+    await goToPrevMonth();
+  }
+  uni.$ie.hideLoading();
+};
+
+const handleNext = async () => {
+  if (loading.value) return; // 加载中时禁止操作
+  uni.$ie.showLoading();
+  if (displayMode.value === 'week') {
+    await goToNextWeek();
+  } else {
+    await goToNextMonth();
   }
-])
-const handleYearChange = () => {
+  uni.$ie.hideLoading();
+};
+
+// 表单处理 - 与日历同步
+const handleYearChange = async () => {
+  // 年份切换时清空月份和周
   form.value.month = '';
   form.value.week = '';
-}
-const handleMonthChange = () => {
-  form.value.week = '';
-}
-const handleWeekChange = () => {
-  loadData();
-}
-const endDate = ref('');
-const selected = ref([]);
+
+  // 年份切换时请求年度数据,但不显示记录表
+  if (form.value.year) {
+    const year = parseInt(form.value.year);
+    // 请求年度数据来更新统计信息
+    await goToYear(year);
+  }
+};
+
+const handleMonthChange = async () => {
+  if (form.value.year && form.value.month) {
+    // 月份切换时清空周,不自动选中
+    form.value.week = '';
+
+    // 切换到月份模式并跳转到指定年月
+    await goToYearMonth(parseInt(form.value.year), parseInt(form.value.month));
+  }
+};
+
+const handleWeekChange = async () => {
+  if (form.value.week) {
+    // 切换到week模式并跳转到指定周(合并两个操作,避免重复请求)
+    await goToWeek(parseInt(form.value.week));
+  }
+};
+
+// 监听日历状态变化,同步到表单
+watch([currentYear, currentMonth, currentWeekNumber, displayMode], ([year, month, week, mode]) => {
+  form.value.year = year.toString();
+
+  // 只有在非年份模式下才同步月份,年份模式下保持用户选择
+  if (mode !== 'year') {
+    form.value.month = month.toString();
+  }
+
+  // 只有在week模式下才同步周数,避免月份模式下被清空
+  if (mode === 'week') {
+    form.value.week = week.toString();
+  } else {
+    form.value.week = '';
+  }
+}, { immediate: true });
+
+// 初始化默认选中当前时间
+const initializeDefaultSelection = () => {
+  const formData = initializeFormData();
+  form.value.year = formData.year;
+  form.value.month = formData.month;
+  form.value.week = formData.week;
+};
+
+// 监听日历组件内部事件,确保数据同步
+const handleCalendarWeekChange = (event: any) => {
+  // console.log('周变化:', event);
+  // 更新外部状态以保持同步
+  if (event.weekNumber) {
+    currentWeekNumber.value = event.weekNumber;
+  }
+};
+
+const handleCalendarMonthSwitch = (event: any) => {
+  // console.log('月份切换:', event);
+  // 更新外部状态以保持同步
+  if (event.year && event.month) {
+    // 通过 goToYearMonth 方法来更新状态
+    goToYearMonth(event.year, event.month);
+  }
+};
+
+const calendarRef = ref();
 const calendarPopupRef = ref();
+
 const handleOpenCalendar = () => {
-  calendarPopupRef.value.open();
-}
-const handleCalendarChange = (e: any) => {
-  console.log(e)
-}
-const loadData = () => {
+  if (canOpenCalendar.value && calendarPopupRef.value) {
+    calendarPopupRef.value.open();
+  }
+};
 
-}
+// 初始化
+onMounted(async () => {
+  // 先初始化默认选中
+  initializeDefaultSelection();
+  // 显示全屏loading
+  uni.$ie.showLoading();
+  try {
+    // 初始化日历
+    await initCalendar();
+  } finally {
+    // 隐藏loading
+    uni.$ie.hideLoading();
+  }
+});
 </script>
 <style lang="scss" scoped>
 .picker-wrap {
@@ -274,4 +573,63 @@ const loadData = () => {
 .btn-wrap {
   @apply flex items-center gap-x-10 bg-primary text-white text-26 px-10 h-56 rounded-4;
 }
+
+.calendar-item {
+  @apply rounded-5 text-center w-64 h-56 mx-auto py-8;
+
+  .date {
+    color: #222;
+    font-size: 30rpx;
+    line-height: 30rpx;
+  }
+
+  .info {
+    font-size: 20rpx;
+    line-height: 20rpx;
+    margin-top: 4rpx;
+  }
+}
+
+.uni-calendar-item--disable {
+  opacity: 0;
+}
+
+.calendar-item--week-mode-disabled {
+  .date {
+    color: #cccccc;
+  }
+}
+
+.calendar-item--valid {
+  --valid-color: #22C55E;
+  background-color: #F0FDF4;
+  border: 1px solid var(--valid-color);
+
+  .info {
+    color: var(--valid-color);
+  }
+}
+
+.calendar-item--invalid {
+  --invalid-color: #FF5B5C;
+  background-color: #FEEDE9;
+  border: 1px solid var(--invalid-color);
+
+  .info {
+    color: var(--invalid-color);
+  }
+}
+
+.calendar-loading-overlay {
+  position: absolute;
+  top: 0;
+  left: 0;
+  right: 0;
+  bottom: 0;
+  background-color: rgba(255, 255, 255, 0.8);
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  z-index: 10;
+}
 </style>

+ 544 - 0
src/uni_modules/uni-calendar/components/uni-calendar copy/calendar.js

@@ -0,0 +1,544 @@
+/**
+* @1900-2100区间内的公历、农历互转
+* @charset UTF-8
+* @github  https://github.com/jjonline/calendar.js
+* @Author  Jea杨(JJonline@JJonline.Cn)
+* @Time    2014-7-21
+* @Time    2016-8-13 Fixed 2033hex、Attribution Annals
+* @Time    2016-9-25 Fixed lunar LeapMonth Param Bug
+* @Time    2017-7-24 Fixed use getTerm Func Param Error.use solar year,NOT lunar year
+* @Version 1.0.3
+* @公历转农历:calendar.solar2lunar(1987,11,01); //[you can ignore params of prefix 0]
+* @农历转公历:calendar.lunar2solar(1987,09,10); //[you can ignore params of prefix 0]
+*/
+/* eslint-disable */
+var calendar = {
+
+  /**
+      * 农历1900-2100的润大小信息表
+      * @Array Of Property
+      * @return Hex
+      */
+  lunarInfo: [0x04bd8, 0x04ae0, 0x0a570, 0x054d5, 0x0d260, 0x0d950, 0x16554, 0x056a0, 0x09ad0, 0x055d2, // 1900-1909
+    0x04ae0, 0x0a5b6, 0x0a4d0, 0x0d250, 0x1d255, 0x0b540, 0x0d6a0, 0x0ada2, 0x095b0, 0x14977, // 1910-1919
+    0x04970, 0x0a4b0, 0x0b4b5, 0x06a50, 0x06d40, 0x1ab54, 0x02b60, 0x09570, 0x052f2, 0x04970, // 1920-1929
+    0x06566, 0x0d4a0, 0x0ea50, 0x06e95, 0x05ad0, 0x02b60, 0x186e3, 0x092e0, 0x1c8d7, 0x0c950, // 1930-1939
+    0x0d4a0, 0x1d8a6, 0x0b550, 0x056a0, 0x1a5b4, 0x025d0, 0x092d0, 0x0d2b2, 0x0a950, 0x0b557, // 1940-1949
+    0x06ca0, 0x0b550, 0x15355, 0x04da0, 0x0a5b0, 0x14573, 0x052b0, 0x0a9a8, 0x0e950, 0x06aa0, // 1950-1959
+    0x0aea6, 0x0ab50, 0x04b60, 0x0aae4, 0x0a570, 0x05260, 0x0f263, 0x0d950, 0x05b57, 0x056a0, // 1960-1969
+    0x096d0, 0x04dd5, 0x04ad0, 0x0a4d0, 0x0d4d4, 0x0d250, 0x0d558, 0x0b540, 0x0b6a0, 0x195a6, // 1970-1979
+    0x095b0, 0x049b0, 0x0a974, 0x0a4b0, 0x0b27a, 0x06a50, 0x06d40, 0x0af46, 0x0ab60, 0x09570, // 1980-1989
+    0x04af5, 0x04970, 0x064b0, 0x074a3, 0x0ea50, 0x06b58, 0x05ac0, 0x0ab60, 0x096d5, 0x092e0, // 1990-1999
+    0x0c960, 0x0d954, 0x0d4a0, 0x0da50, 0x07552, 0x056a0, 0x0abb7, 0x025d0, 0x092d0, 0x0cab5, // 2000-2009
+    0x0a950, 0x0b4a0, 0x0baa4, 0x0ad50, 0x055d9, 0x04ba0, 0x0a5b0, 0x15176, 0x052b0, 0x0a930, // 2010-2019
+    0x07954, 0x06aa0, 0x0ad50, 0x05b52, 0x04b60, 0x0a6e6, 0x0a4e0, 0x0d260, 0x0ea65, 0x0d530, // 2020-2029
+    0x05aa0, 0x076a3, 0x096d0, 0x04afb, 0x04ad0, 0x0a4d0, 0x1d0b6, 0x0d250, 0x0d520, 0x0dd45, // 2030-2039
+    0x0b5a0, 0x056d0, 0x055b2, 0x049b0, 0x0a577, 0x0a4b0, 0x0aa50, 0x1b255, 0x06d20, 0x0ada0, // 2040-2049
+    /** Add By JJonline@JJonline.Cn**/
+    0x14b63, 0x09370, 0x049f8, 0x04970, 0x064b0, 0x168a6, 0x0ea50, 0x06b20, 0x1a6c4, 0x0aae0, // 2050-2059
+    0x0a2e0, 0x0d2e3, 0x0c960, 0x0d557, 0x0d4a0, 0x0da50, 0x05d55, 0x056a0, 0x0a6d0, 0x055d4, // 2060-2069
+    0x052d0, 0x0a9b8, 0x0a950, 0x0b4a0, 0x0b6a6, 0x0ad50, 0x055a0, 0x0aba4, 0x0a5b0, 0x052b0, // 2070-2079
+    0x0b273, 0x06930, 0x07337, 0x06aa0, 0x0ad50, 0x14b55, 0x04b60, 0x0a570, 0x054e4, 0x0d160, // 2080-2089
+    0x0e968, 0x0d520, 0x0daa0, 0x16aa6, 0x056d0, 0x04ae0, 0x0a9d4, 0x0a2d0, 0x0d150, 0x0f252, // 2090-2099
+    0x0d520], // 2100
+
+  /**
+      * 公历每个月份的天数普通表
+      * @Array Of Property
+      * @return Number
+      */
+  solarMonth: [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31],
+
+  /**
+      * 天干地支之天干速查表
+      * @Array Of Property trans["甲","乙","丙","丁","戊","己","庚","辛","壬","癸"]
+      * @return Cn string
+      */
+  Gan: ['\u7532', '\u4e59', '\u4e19', '\u4e01', '\u620a', '\u5df1', '\u5e9a', '\u8f9b', '\u58ec', '\u7678'],
+
+  /**
+      * 天干地支之地支速查表
+      * @Array Of Property
+      * @trans["子","丑","寅","卯","辰","巳","午","未","申","酉","戌","亥"]
+      * @return Cn string
+      */
+  Zhi: ['\u5b50', '\u4e11', '\u5bc5', '\u536f', '\u8fb0', '\u5df3', '\u5348', '\u672a', '\u7533', '\u9149', '\u620c', '\u4ea5'],
+
+  /**
+      * 天干地支之地支速查表<=>生肖
+      * @Array Of Property
+      * @trans["鼠","牛","虎","兔","龙","蛇","马","羊","猴","鸡","狗","猪"]
+      * @return Cn string
+      */
+  Animals: ['\u9f20', '\u725b', '\u864e', '\u5154', '\u9f99', '\u86c7', '\u9a6c', '\u7f8a', '\u7334', '\u9e21', '\u72d7', '\u732a'],
+
+  /**
+      * 24节气速查表
+      * @Array Of Property
+      * @trans["小寒","大寒","立春","雨水","惊蛰","春分","清明","谷雨","立夏","小满","芒种","夏至","小暑","大暑","立秋","处暑","白露","秋分","寒露","霜降","立冬","小雪","大雪","冬至"]
+      * @return Cn string
+      */
+  solarTerm: ['\u5c0f\u5bd2', '\u5927\u5bd2', '\u7acb\u6625', '\u96e8\u6c34', '\u60ca\u86f0', '\u6625\u5206', '\u6e05\u660e', '\u8c37\u96e8', '\u7acb\u590f', '\u5c0f\u6ee1', '\u8292\u79cd', '\u590f\u81f3', '\u5c0f\u6691', '\u5927\u6691', '\u7acb\u79cb', '\u5904\u6691', '\u767d\u9732', '\u79cb\u5206', '\u5bd2\u9732', '\u971c\u964d', '\u7acb\u51ac', '\u5c0f\u96ea', '\u5927\u96ea', '\u51ac\u81f3'],
+
+  /**
+      * 1900-2100各年的24节气日期速查表
+      * @Array Of Property
+      * @return 0x string For splice
+      */
+  sTermInfo: ['9778397bd097c36b0b6fc9274c91aa', '97b6b97bd19801ec9210c965cc920e', '97bcf97c3598082c95f8c965cc920f',
+    '97bd0b06bdb0722c965ce1cfcc920f', 'b027097bd097c36b0b6fc9274c91aa', '97b6b97bd19801ec9210c965cc920e',
+    '97bcf97c359801ec95f8c965cc920f', '97bd0b06bdb0722c965ce1cfcc920f', 'b027097bd097c36b0b6fc9274c91aa',
+    '97b6b97bd19801ec9210c965cc920e', '97bcf97c359801ec95f8c965cc920f', '97bd0b06bdb0722c965ce1cfcc920f',
+    'b027097bd097c36b0b6fc9274c91aa', '9778397bd19801ec9210c965cc920e', '97b6b97bd19801ec95f8c965cc920f',
+    '97bd09801d98082c95f8e1cfcc920f', '97bd097bd097c36b0b6fc9210c8dc2', '9778397bd197c36c9210c9274c91aa',
+    '97b6b97bd19801ec95f8c965cc920e', '97bd09801d98082c95f8e1cfcc920f', '97bd097bd097c36b0b6fc9210c8dc2',
+    '9778397bd097c36c9210c9274c91aa', '97b6b97bd19801ec95f8c965cc920e', '97bcf97c3598082c95f8e1cfcc920f',
+    '97bd097bd097c36b0b6fc9210c8dc2', '9778397bd097c36c9210c9274c91aa', '97b6b97bd19801ec9210c965cc920e',
+    '97bcf97c3598082c95f8c965cc920f', '97bd097bd097c35b0b6fc920fb0722', '9778397bd097c36b0b6fc9274c91aa',
+    '97b6b97bd19801ec9210c965cc920e', '97bcf97c3598082c95f8c965cc920f', '97bd097bd097c35b0b6fc920fb0722',
+    '9778397bd097c36b0b6fc9274c91aa', '97b6b97bd19801ec9210c965cc920e', '97bcf97c359801ec95f8c965cc920f',
+    '97bd097bd097c35b0b6fc920fb0722', '9778397bd097c36b0b6fc9274c91aa', '97b6b97bd19801ec9210c965cc920e',
+    '97bcf97c359801ec95f8c965cc920f', '97bd097bd097c35b0b6fc920fb0722', '9778397bd097c36b0b6fc9274c91aa',
+    '97b6b97bd19801ec9210c965cc920e', '97bcf97c359801ec95f8c965cc920f', '97bd097bd07f595b0b6fc920fb0722',
+    '9778397bd097c36b0b6fc9210c8dc2', '9778397bd19801ec9210c9274c920e', '97b6b97bd19801ec95f8c965cc920f',
+    '97bd07f5307f595b0b0bc920fb0722', '7f0e397bd097c36b0b6fc9210c8dc2', '9778397bd097c36c9210c9274c920e',
+    '97b6b97bd19801ec95f8c965cc920f', '97bd07f5307f595b0b0bc920fb0722', '7f0e397bd097c36b0b6fc9210c8dc2',
+    '9778397bd097c36c9210c9274c91aa', '97b6b97bd19801ec9210c965cc920e', '97bd07f1487f595b0b0bc920fb0722',
+    '7f0e397bd097c36b0b6fc9210c8dc2', '9778397bd097c36b0b6fc9274c91aa', '97b6b97bd19801ec9210c965cc920e',
+    '97bcf7f1487f595b0b0bb0b6fb0722', '7f0e397bd097c35b0b6fc920fb0722', '9778397bd097c36b0b6fc9274c91aa',
+    '97b6b97bd19801ec9210c965cc920e', '97bcf7f1487f595b0b0bb0b6fb0722', '7f0e397bd097c35b0b6fc920fb0722',
+    '9778397bd097c36b0b6fc9274c91aa', '97b6b97bd19801ec9210c965cc920e', '97bcf7f1487f531b0b0bb0b6fb0722',
+    '7f0e397bd097c35b0b6fc920fb0722', '9778397bd097c36b0b6fc9274c91aa', '97b6b97bd19801ec9210c965cc920e',
+    '97bcf7f1487f531b0b0bb0b6fb0722', '7f0e397bd07f595b0b6fc920fb0722', '9778397bd097c36b0b6fc9274c91aa',
+    '97b6b97bd19801ec9210c9274c920e', '97bcf7f0e47f531b0b0bb0b6fb0722', '7f0e397bd07f595b0b0bc920fb0722',
+    '9778397bd097c36b0b6fc9210c91aa', '97b6b97bd197c36c9210c9274c920e', '97bcf7f0e47f531b0b0bb0b6fb0722',
+    '7f0e397bd07f595b0b0bc920fb0722', '9778397bd097c36b0b6fc9210c8dc2', '9778397bd097c36c9210c9274c920e',
+    '97b6b7f0e47f531b0723b0b6fb0722', '7f0e37f5307f595b0b0bc920fb0722', '7f0e397bd097c36b0b6fc9210c8dc2',
+    '9778397bd097c36b0b70c9274c91aa', '97b6b7f0e47f531b0723b0b6fb0721', '7f0e37f1487f595b0b0bb0b6fb0722',
+    '7f0e397bd097c35b0b6fc9210c8dc2', '9778397bd097c36b0b6fc9274c91aa', '97b6b7f0e47f531b0723b0b6fb0721',
+    '7f0e27f1487f595b0b0bb0b6fb0722', '7f0e397bd097c35b0b6fc920fb0722', '9778397bd097c36b0b6fc9274c91aa',
+    '97b6b7f0e47f531b0723b0b6fb0721', '7f0e27f1487f531b0b0bb0b6fb0722', '7f0e397bd097c35b0b6fc920fb0722',
+    '9778397bd097c36b0b6fc9274c91aa', '97b6b7f0e47f531b0723b0b6fb0721', '7f0e27f1487f531b0b0bb0b6fb0722',
+    '7f0e397bd097c35b0b6fc920fb0722', '9778397bd097c36b0b6fc9274c91aa', '97b6b7f0e47f531b0723b0b6fb0721',
+    '7f0e27f1487f531b0b0bb0b6fb0722', '7f0e397bd07f595b0b0bc920fb0722', '9778397bd097c36b0b6fc9274c91aa',
+    '97b6b7f0e47f531b0723b0787b0721', '7f0e27f0e47f531b0b0bb0b6fb0722', '7f0e397bd07f595b0b0bc920fb0722',
+    '9778397bd097c36b0b6fc9210c91aa', '97b6b7f0e47f149b0723b0787b0721', '7f0e27f0e47f531b0723b0b6fb0722',
+    '7f0e397bd07f595b0b0bc920fb0722', '9778397bd097c36b0b6fc9210c8dc2', '977837f0e37f149b0723b0787b0721',
+    '7f07e7f0e47f531b0723b0b6fb0722', '7f0e37f5307f595b0b0bc920fb0722', '7f0e397bd097c35b0b6fc9210c8dc2',
+    '977837f0e37f14998082b0787b0721', '7f07e7f0e47f531b0723b0b6fb0721', '7f0e37f1487f595b0b0bb0b6fb0722',
+    '7f0e397bd097c35b0b6fc9210c8dc2', '977837f0e37f14998082b0787b06bd', '7f07e7f0e47f531b0723b0b6fb0721',
+    '7f0e27f1487f531b0b0bb0b6fb0722', '7f0e397bd097c35b0b6fc920fb0722', '977837f0e37f14998082b0787b06bd',
+    '7f07e7f0e47f531b0723b0b6fb0721', '7f0e27f1487f531b0b0bb0b6fb0722', '7f0e397bd097c35b0b6fc920fb0722',
+    '977837f0e37f14998082b0787b06bd', '7f07e7f0e47f531b0723b0b6fb0721', '7f0e27f1487f531b0b0bb0b6fb0722',
+    '7f0e397bd07f595b0b0bc920fb0722', '977837f0e37f14998082b0787b06bd', '7f07e7f0e47f531b0723b0b6fb0721',
+    '7f0e27f1487f531b0b0bb0b6fb0722', '7f0e397bd07f595b0b0bc920fb0722', '977837f0e37f14998082b0787b06bd',
+    '7f07e7f0e47f149b0723b0787b0721', '7f0e27f0e47f531b0b0bb0b6fb0722', '7f0e397bd07f595b0b0bc920fb0722',
+    '977837f0e37f14998082b0723b06bd', '7f07e7f0e37f149b0723b0787b0721', '7f0e27f0e47f531b0723b0b6fb0722',
+    '7f0e397bd07f595b0b0bc920fb0722', '977837f0e37f14898082b0723b02d5', '7ec967f0e37f14998082b0787b0721',
+    '7f07e7f0e47f531b0723b0b6fb0722', '7f0e37f1487f595b0b0bb0b6fb0722', '7f0e37f0e37f14898082b0723b02d5',
+    '7ec967f0e37f14998082b0787b0721', '7f07e7f0e47f531b0723b0b6fb0722', '7f0e37f1487f531b0b0bb0b6fb0722',
+    '7f0e37f0e37f14898082b0723b02d5', '7ec967f0e37f14998082b0787b06bd', '7f07e7f0e47f531b0723b0b6fb0721',
+    '7f0e37f1487f531b0b0bb0b6fb0722', '7f0e37f0e37f14898082b072297c35', '7ec967f0e37f14998082b0787b06bd',
+    '7f07e7f0e47f531b0723b0b6fb0721', '7f0e27f1487f531b0b0bb0b6fb0722', '7f0e37f0e37f14898082b072297c35',
+    '7ec967f0e37f14998082b0787b06bd', '7f07e7f0e47f531b0723b0b6fb0721', '7f0e27f1487f531b0b0bb0b6fb0722',
+    '7f0e37f0e366aa89801eb072297c35', '7ec967f0e37f14998082b0787b06bd', '7f07e7f0e47f149b0723b0787b0721',
+    '7f0e27f1487f531b0b0bb0b6fb0722', '7f0e37f0e366aa89801eb072297c35', '7ec967f0e37f14998082b0723b06bd',
+    '7f07e7f0e47f149b0723b0787b0721', '7f0e27f0e47f531b0723b0b6fb0722', '7f0e37f0e366aa89801eb072297c35',
+    '7ec967f0e37f14998082b0723b06bd', '7f07e7f0e37f14998083b0787b0721', '7f0e27f0e47f531b0723b0b6fb0722',
+    '7f0e37f0e366aa89801eb072297c35', '7ec967f0e37f14898082b0723b02d5', '7f07e7f0e37f14998082b0787b0721',
+    '7f07e7f0e47f531b0723b0b6fb0722', '7f0e36665b66aa89801e9808297c35', '665f67f0e37f14898082b0723b02d5',
+    '7ec967f0e37f14998082b0787b0721', '7f07e7f0e47f531b0723b0b6fb0722', '7f0e36665b66a449801e9808297c35',
+    '665f67f0e37f14898082b0723b02d5', '7ec967f0e37f14998082b0787b06bd', '7f07e7f0e47f531b0723b0b6fb0721',
+    '7f0e36665b66a449801e9808297c35', '665f67f0e37f14898082b072297c35', '7ec967f0e37f14998082b0787b06bd',
+    '7f07e7f0e47f531b0723b0b6fb0721', '7f0e26665b66a449801e9808297c35', '665f67f0e37f1489801eb072297c35',
+    '7ec967f0e37f14998082b0787b06bd', '7f07e7f0e47f531b0723b0b6fb0721', '7f0e27f1487f531b0b0bb0b6fb0722'],
+
+  /**
+      * 数字转中文速查表
+      * @Array Of Property
+      * @trans ['日','一','二','三','四','五','六','七','八','九','十']
+      * @return Cn string
+      */
+  nStr1: ['\u65e5', '\u4e00', '\u4e8c', '\u4e09', '\u56db', '\u4e94', '\u516d', '\u4e03', '\u516b', '\u4e5d', '\u5341'],
+
+  /**
+      * 日期转农历称呼速查表
+      * @Array Of Property
+      * @trans ['初','十','廿','卅']
+      * @return Cn string
+      */
+  nStr2: ['\u521d', '\u5341', '\u5eff', '\u5345'],
+
+  /**
+      * 月份转农历称呼速查表
+      * @Array Of Property
+      * @trans ['正','一','二','三','四','五','六','七','八','九','十','冬','腊']
+      * @return Cn string
+      */
+  nStr3: ['\u6b63', '\u4e8c', '\u4e09', '\u56db', '\u4e94', '\u516d', '\u4e03', '\u516b', '\u4e5d', '\u5341', '\u51ac', '\u814a'],
+
+  /**
+      * 返回农历y年一整年的总天数
+      * @param lunar Year
+      * @return Number
+      * @eg:var count = calendar.lYearDays(1987) ;//count=387
+      */
+  lYearDays: function (y) {
+    var i; var sum = 348
+    for (i = 0x8000; i > 0x8; i >>= 1) { sum += (this.lunarInfo[y - 1900] & i) ? 1 : 0 }
+    return (sum + this.leapDays(y))
+  },
+
+  /**
+      * 返回农历y年闰月是哪个月;若y年没有闰月 则返回0
+      * @param lunar Year
+      * @return Number (0-12)
+      * @eg:var leapMonth = calendar.leapMonth(1987) ;//leapMonth=6
+      */
+  leapMonth: function (y) { // 闰字编码 \u95f0
+    return (this.lunarInfo[y - 1900] & 0xf)
+  },
+
+  /**
+      * 返回农历y年闰月的天数 若该年没有闰月则返回0
+      * @param lunar Year
+      * @return Number (0、29、30)
+      * @eg:var leapMonthDay = calendar.leapDays(1987) ;//leapMonthDay=29
+      */
+  leapDays: function (y) {
+    if (this.leapMonth(y)) {
+      return ((this.lunarInfo[y - 1900] & 0x10000) ? 30 : 29)
+    }
+    return (0)
+  },
+
+  /**
+      * 返回农历y年m月(非闰月)的总天数,计算m为闰月时的天数请使用leapDays方法
+      * @param lunar Year
+      * @return Number (-1、29、30)
+      * @eg:var MonthDay = calendar.monthDays(1987,9) ;//MonthDay=29
+      */
+  monthDays: function (y, m) {
+    if (m > 12 || m < 1) { return -1 }// 月份参数从1至12,参数错误返回-1
+    return ((this.lunarInfo[y - 1900] & (0x10000 >> m)) ? 30 : 29)
+  },
+
+  /**
+      * 返回公历(!)y年m月的天数
+      * @param solar Year
+      * @return Number (-1、28、29、30、31)
+      * @eg:var solarMonthDay = calendar.leapDays(1987) ;//solarMonthDay=30
+      */
+  solarDays: function (y, m) {
+    if (m > 12 || m < 1) { return -1 } // 若参数错误 返回-1
+    var ms = m - 1
+    if (ms == 1) { // 2月份的闰平规律测算后确认返回28或29
+      return (((y % 4 == 0) && (y % 100 != 0) || (y % 400 == 0)) ? 29 : 28)
+    } else {
+      return (this.solarMonth[ms])
+    }
+  },
+
+  /**
+     * 农历年份转换为干支纪年
+     * @param  lYear 农历年的年份数
+     * @return Cn string
+     */
+  toGanZhiYear: function (lYear) {
+    var ganKey = (lYear - 3) % 10
+    var zhiKey = (lYear - 3) % 12
+    if (ganKey == 0) ganKey = 10// 如果余数为0则为最后一个天干
+    if (zhiKey == 0) zhiKey = 12// 如果余数为0则为最后一个地支
+    return this.Gan[ganKey - 1] + this.Zhi[zhiKey - 1]
+  },
+
+  /**
+     * 公历月、日判断所属星座
+     * @param  cMonth [description]
+     * @param  cDay [description]
+     * @return Cn string
+     */
+  toAstro: function (cMonth, cDay) {
+    var s = '\u9b54\u7faf\u6c34\u74f6\u53cc\u9c7c\u767d\u7f8a\u91d1\u725b\u53cc\u5b50\u5de8\u87f9\u72ee\u5b50\u5904\u5973\u5929\u79e4\u5929\u874e\u5c04\u624b\u9b54\u7faf'
+    var arr = [20, 19, 21, 21, 21, 22, 23, 23, 23, 23, 22, 22]
+    return s.substr(cMonth * 2 - (cDay < arr[cMonth - 1] ? 2 : 0), 2) + '\u5ea7'// 座
+  },
+
+  /**
+      * 传入offset偏移量返回干支
+      * @param offset 相对甲子的偏移量
+      * @return Cn string
+      */
+  toGanZhi: function (offset) {
+    return this.Gan[offset % 10] + this.Zhi[offset % 12]
+  },
+
+  /**
+      * 传入公历(!)y年获得该年第n个节气的公历日期
+      * @param y公历年(1900-2100);n二十四节气中的第几个节气(1~24);从n=1(小寒)算起
+      * @return day Number
+      * @eg:var _24 = calendar.getTerm(1987,3) ;//_24=4;意即1987年2月4日立春
+      */
+  getTerm: function (y, n) {
+    if (y < 1900 || y > 2100) { return -1 }
+    if (n < 1 || n > 24) { return -1 }
+    var _table = this.sTermInfo[y - 1900]
+    var _info = [
+      parseInt('0x' + _table.substr(0, 5)).toString(),
+      parseInt('0x' + _table.substr(5, 5)).toString(),
+      parseInt('0x' + _table.substr(10, 5)).toString(),
+      parseInt('0x' + _table.substr(15, 5)).toString(),
+      parseInt('0x' + _table.substr(20, 5)).toString(),
+      parseInt('0x' + _table.substr(25, 5)).toString()
+    ]
+    var _calday = [
+      _info[0].substr(0, 1),
+      _info[0].substr(1, 2),
+      _info[0].substr(3, 1),
+      _info[0].substr(4, 2),
+
+      _info[1].substr(0, 1),
+      _info[1].substr(1, 2),
+      _info[1].substr(3, 1),
+      _info[1].substr(4, 2),
+
+      _info[2].substr(0, 1),
+      _info[2].substr(1, 2),
+      _info[2].substr(3, 1),
+      _info[2].substr(4, 2),
+
+      _info[3].substr(0, 1),
+      _info[3].substr(1, 2),
+      _info[3].substr(3, 1),
+      _info[3].substr(4, 2),
+
+      _info[4].substr(0, 1),
+      _info[4].substr(1, 2),
+      _info[4].substr(3, 1),
+      _info[4].substr(4, 2),
+
+      _info[5].substr(0, 1),
+      _info[5].substr(1, 2),
+      _info[5].substr(3, 1),
+      _info[5].substr(4, 2)
+    ]
+    return parseInt(_calday[n - 1])
+  },
+
+  /**
+      * 传入农历数字月份返回汉语通俗表示法
+      * @param lunar month
+      * @return Cn string
+      * @eg:var cnMonth = calendar.toChinaMonth(12) ;//cnMonth='腊月'
+      */
+  toChinaMonth: function (m) { // 月 => \u6708
+    if (m > 12 || m < 1) { return -1 } // 若参数错误 返回-1
+    var s = this.nStr3[m - 1]
+    s += '\u6708'// 加上月字
+    return s
+  },
+
+  /**
+      * 传入农历日期数字返回汉字表示法
+      * @param lunar day
+      * @return Cn string
+      * @eg:var cnDay = calendar.toChinaDay(21) ;//cnMonth='廿一'
+      */
+  toChinaDay: function (d) { // 日 => \u65e5
+    var s
+    switch (d) {
+      case 10:
+        s = '\u521d\u5341'; break
+      case 20:
+        s = '\u4e8c\u5341'; break
+      case 30:
+        s = '\u4e09\u5341'; break
+      default :
+        s = this.nStr2[Math.floor(d / 10)]
+        s += this.nStr1[d % 10]
+    }
+    return (s)
+  },
+
+  /**
+      * 年份转生肖[!仅能大致转换] => 精确划分生肖分界线是“立春”
+      * @param y year
+      * @return Cn string
+      * @eg:var animal = calendar.getAnimal(1987) ;//animal='兔'
+      */
+  getAnimal: function (y) {
+    return this.Animals[(y - 4) % 12]
+  },
+
+  /**
+      * 传入阳历年月日获得详细的公历、农历object信息 <=>JSON
+      * @param y  solar year
+      * @param m  solar month
+      * @param d  solar day
+      * @return JSON object
+      * @eg:console.log(calendar.solar2lunar(1987,11,01));
+      */
+  solar2lunar: function (y, m, d) { // 参数区间1900.1.31~2100.12.31
+    // 年份限定、上限
+    if (y < 1900 || y > 2100) {
+      return -1// undefined转换为数字变为NaN
+    }
+    // 公历传参最下限
+    if (y == 1900 && m == 1 && d < 31) {
+      return -1
+    }
+    // 未传参  获得当天
+    if (!y) {
+      var objDate = new Date()
+    } else {
+      var objDate = new Date(y, parseInt(m) - 1, d)
+    }
+    var i; var leap = 0; var temp = 0
+    // 修正ymd参数
+    var y = objDate.getFullYear()
+    var m = objDate.getMonth() + 1
+    var d = objDate.getDate()
+    var offset = (Date.UTC(objDate.getFullYear(), objDate.getMonth(), objDate.getDate()) - Date.UTC(1900, 0, 31)) / 86400000
+    for (i = 1900; i < 2101 && offset > 0; i++) {
+      temp = this.lYearDays(i)
+      offset -= temp
+    }
+    if (offset < 0) {
+      offset += temp; i--
+    }
+
+    // 是否今天
+    var isTodayObj = new Date()
+    var isToday = false
+    if (isTodayObj.getFullYear() == y && isTodayObj.getMonth() + 1 == m && isTodayObj.getDate() == d) {
+      isToday = true
+    }
+    // 星期几
+    var nWeek = objDate.getDay()
+    var cWeek = this.nStr1[nWeek]
+    // 数字表示周几顺应天朝周一开始的惯例
+    if (nWeek == 0) {
+      nWeek = 7
+    }
+    // 农历年
+    var year = i
+    var leap = this.leapMonth(i) // 闰哪个月
+    var isLeap = false
+
+    // 效验闰月
+    for (i = 1; i < 13 && offset > 0; i++) {
+      // 闰月
+      if (leap > 0 && i == (leap + 1) && isLeap == false) {
+        --i
+        isLeap = true; temp = this.leapDays(year) // 计算农历闰月天数
+      } else {
+        temp = this.monthDays(year, i)// 计算农历普通月天数
+      }
+      // 解除闰月
+      if (isLeap == true && i == (leap + 1)) { isLeap = false }
+      offset -= temp
+    }
+    // 闰月导致数组下标重叠取反
+    if (offset == 0 && leap > 0 && i == leap + 1) {
+      if (isLeap) {
+        isLeap = false
+      } else {
+        isLeap = true; --i
+      }
+    }
+    if (offset < 0) {
+      offset += temp; --i
+    }
+    // 农历月
+    var month = i
+    // 农历日
+    var day = offset + 1
+    // 天干地支处理
+    var sm = m - 1
+    var gzY = this.toGanZhiYear(year)
+
+    // 当月的两个节气
+    // bugfix-2017-7-24 11:03:38 use lunar Year Param `y` Not `year`
+    var firstNode = this.getTerm(y, (m * 2 - 1))// 返回当月「节」为几日开始
+    var secondNode = this.getTerm(y, (m * 2))// 返回当月「节」为几日开始
+
+    // 依据12节气修正干支月
+    var gzM = this.toGanZhi((y - 1900) * 12 + m + 11)
+    if (d >= firstNode) {
+      gzM = this.toGanZhi((y - 1900) * 12 + m + 12)
+    }
+
+    // 传入的日期的节气与否
+    var isTerm = false
+    var Term = null
+    if (firstNode == d) {
+      isTerm = true
+      Term = this.solarTerm[m * 2 - 2]
+    }
+    if (secondNode == d) {
+      isTerm = true
+      Term = this.solarTerm[m * 2 - 1]
+    }
+    // 日柱 当月一日与 1900/1/1 相差天数
+    var dayCyclical = Date.UTC(y, sm, 1, 0, 0, 0, 0) / 86400000 + 25567 + 10
+    var gzD = this.toGanZhi(dayCyclical + d - 1)
+    // 该日期所属的星座
+    var astro = this.toAstro(m, d)
+
+    return { 'lYear': year, 'lMonth': month, 'lDay': day, 'Animal': this.getAnimal(year), 'IMonthCn': (isLeap ? '\u95f0' : '') + this.toChinaMonth(month), 'IDayCn': this.toChinaDay(day), 'cYear': y, 'cMonth': m, 'cDay': d, 'gzYear': gzY, 'gzMonth': gzM, 'gzDay': gzD, 'isToday': isToday, 'isLeap': isLeap, 'nWeek': nWeek, 'ncWeek': '\u661f\u671f' + cWeek, 'isTerm': isTerm, 'Term': Term, 'astro': astro }
+  },
+
+  /**
+      * 传入农历年月日以及传入的月份是否闰月获得详细的公历、农历object信息 <=>JSON
+      * @param y  lunar year
+      * @param m  lunar month
+      * @param d  lunar day
+      * @param isLeapMonth  lunar month is leap or not.[如果是农历闰月第四个参数赋值true即可]
+      * @return JSON object
+      * @eg:console.log(calendar.lunar2solar(1987,9,10));
+      */
+  lunar2solar: function (y, m, d, isLeapMonth) { // 参数区间1900.1.31~2100.12.1
+    var isLeapMonth = !!isLeapMonth
+    var leapOffset = 0
+    var leapMonth = this.leapMonth(y)
+    var leapDay = this.leapDays(y)
+    if (isLeapMonth && (leapMonth != m)) { return -1 }// 传参要求计算该闰月公历 但该年得出的闰月与传参的月份并不同
+    if (y == 2100 && m == 12 && d > 1 || y == 1900 && m == 1 && d < 31) { return -1 }// 超出了最大极限值
+    var day = this.monthDays(y, m)
+    var _day = day
+    // bugFix 2016-9-25
+    // if month is leap, _day use leapDays method
+    if (isLeapMonth) {
+      _day = this.leapDays(y, m)
+    }
+    if (y < 1900 || y > 2100 || d > _day) { return -1 }// 参数合法性效验
+
+    // 计算农历的时间差
+    var offset = 0
+    for (var i = 1900; i < y; i++) {
+      offset += this.lYearDays(i)
+    }
+    var leap = 0; var isAdd = false
+    for (var i = 1; i < m; i++) {
+      leap = this.leapMonth(y)
+      if (!isAdd) { // 处理闰月
+        if (leap <= i && leap > 0) {
+          offset += this.leapDays(y); isAdd = true
+        }
+      }
+      offset += this.monthDays(y, i)
+    }
+    // 转换闰月农历 需补充该年闰月的前一个月的时差
+    if (isLeapMonth) { offset += day }
+    // 1900年农历正月一日的公历时间为1900年1月30日0时0分0秒(该时间也是本农历的最开始起始点)
+    var stmap = Date.UTC(1900, 1, 30, 0, 0, 0)
+    var calObj = new Date((offset + d - 31) * 86400000 + stmap)
+    var cY = calObj.getUTCFullYear()
+    var cM = calObj.getUTCMonth() + 1
+    var cD = calObj.getUTCDate()
+
+    return this.solar2lunar(cY, cM, cD)
+  }
+}
+
+export default calendar

+ 12 - 0
src/uni_modules/uni-calendar/components/uni-calendar copy/i18n/en.json

@@ -0,0 +1,12 @@
+{
+	"uni-calender.ok": "ok",
+	"uni-calender.cancel": "cancel",
+	"uni-calender.today": "today",
+	"uni-calender.MON": "MON",
+	"uni-calender.TUE": "TUE",
+	"uni-calender.WED": "WED",
+	"uni-calender.THU": "THU",
+	"uni-calender.FRI": "FRI",
+	"uni-calender.SAT": "SAT",
+	"uni-calender.SUN": "SUN"
+}

+ 8 - 0
src/uni_modules/uni-calendar/components/uni-calendar copy/i18n/index.js

@@ -0,0 +1,8 @@
+import en from './en.json'
+import zhHans from './zh-Hans.json'
+import zhHant from './zh-Hant.json'
+export default {
+	en,
+	'zh-Hans': zhHans,
+	'zh-Hant': zhHant
+}

+ 12 - 0
src/uni_modules/uni-calendar/components/uni-calendar copy/i18n/zh-Hans.json

@@ -0,0 +1,12 @@
+{
+	"uni-calender.ok": "确定",
+	"uni-calender.cancel": "取消",
+	"uni-calender.today": "今日",
+	"uni-calender.SUN": "日",
+	"uni-calender.MON": "一",
+	"uni-calender.TUE": "二",
+	"uni-calender.WED": "三",
+	"uni-calender.THU": "四",
+	"uni-calender.FRI": "五",
+	"uni-calender.SAT": "六"
+}

+ 12 - 0
src/uni_modules/uni-calendar/components/uni-calendar copy/i18n/zh-Hant.json

@@ -0,0 +1,12 @@
+{
+	"uni-calender.ok": "確定",
+	"uni-calender.cancel": "取消",
+	"uni-calender.today": "今日",
+	"uni-calender.SUN": "日",
+	"uni-calender.MON": "一",
+	"uni-calender.TUE": "二",
+	"uni-calender.WED": "三",
+	"uni-calender.THU": "四",
+	"uni-calender.FRI": "五",
+	"uni-calender.SAT": "六"
+}

+ 233 - 0
src/uni_modules/uni-calendar/components/uni-calendar copy/uni-calendar-item.vue

@@ -0,0 +1,233 @@
+<template>
+  <view class="uni-calendar-item__weeks-box" :class="{
+    'uni-calendar-item--disable': weeks.disable,
+    'uni-calendar-item--isDay': calendar.fullDate === weeks.fullDate && weeks.isDay,
+    'uni-calendar-item--checked': (calendar.fullDate === weeks.fullDate && !weeks.isDay),
+    'uni-calendar-item--before-checked': weeks.beforeMultiple,
+    'uni-calendar-item--multiple': weeks.multiple,
+    'uni-calendar-item--after-checked': weeks.afterMultiple,
+  }" @click="choiceDate(weeks)">
+    <view class="uni-calendar-item__weeks-box-item"
+     :class="[{'uni-calendar-item--disable-after': weeks.disableAfter != false && weeks.disableAfter != true}]">
+      <!-- <text v-if="selected&&weeks.extraInfo" class="uni-calendar-item__weeks-box-circle"></text> -->
+      <text class="uni-calendar-item__weeks-box-text" :class="{
+        'uni-calendar-item--isDay-text': weeks.isDay,
+        'uni-calendar-item--isDay': calendar.fullDate === weeks.fullDate && weeks.isDay,
+        'uni-calendar-item--checked': calendar.fullDate === weeks.fullDate && !weeks.isDay,
+        'uni-calendar-item--before-checked': weeks.beforeMultiple,
+        'uni-calendar-item--multiple': weeks.multiple,
+        'uni-calendar-item--after-checked': weeks.afterMultiple,
+        'uni-calendar-item--disable': weeks.disable,
+      }">{{ weeks.date }}</text>
+      <!-- <text v-if="!lunar&&!weeks.extraInfo && weeks.isDay" class="uni-calendar-item__weeks-lunar-text" :class="{
+				'uni-calendar-item--isDay-text':weeks.isDay,
+				'uni-calendar-item--isDay':calendar.fullDate === weeks.fullDate && weeks.isDay,
+				'uni-calendar-item--checked':calendar.fullDate === weeks.fullDate && !weeks.isDay,
+				'uni-calendar-item--before-checked':weeks.beforeMultiple,
+				'uni-calendar-item--multiple': weeks.multiple,
+				'uni-calendar-item--after-checked':weeks.afterMultiple,
+				}">{{todayText}}</text> -->
+      <!-- <text v-if="lunar&&!weeks.extraInfo" class="uni-calendar-item__weeks-lunar-text" :class="{
+				'uni-calendar-item--isDay-text':weeks.isDay,
+				'uni-calendar-item--isDay':calendar.fullDate === weeks.fullDate && weeks.isDay,
+				'uni-calendar-item--checked':calendar.fullDate === weeks.fullDate && !weeks.isDay,
+				'uni-calendar-item--before-checked':weeks.beforeMultiple,
+				'uni-calendar-item--multiple': weeks.multiple,
+				'uni-calendar-item--after-checked':weeks.afterMultiple,
+				'uni-calendar-item--disable':weeks.disable,
+				}">{{weeks.isDay ? todayText : (weeks.lunar.IDayCn === '初一'?weeks.lunar.IMonthCn:weeks.lunar.IDayCn)}}</text> -->
+      <!-- <text v-if="weeks.extraInfo&&weeks.extraInfo.info" class="uni-calendar-item__weeks-lunar-text" :class="{
+				'uni-calendar-item--extra':weeks.extraInfo.info,
+				'uni-calendar-item--isDay-text':weeks.isDay,
+				'uni-calendar-item--isDay':calendar.fullDate === weeks.fullDate && weeks.isDay,
+				'uni-calendar-item--checked':calendar.fullDate === weeks.fullDate && !weeks.isDay,
+				'uni-calendar-item--before-checked':weeks.beforeMultiple,
+				'uni-calendar-item--multiple': weeks.multiple,
+				'uni-calendar-item--after-checked':weeks.afterMultiple,
+				'uni-calendar-item--disable':weeks.disable,
+				}">{{weeks.extraInfo.info}}</text> -->
+      <view class="uni-calendar-item__weeks-info-text-box">
+        <!-- <text class="uni-calendar-item__weeks-lunar-text" :class="{
+          'uni-calendar-item--extra':weeks.extraInfo.info,				
+          }">{{weeks.extraInfo.info}}</text> -->
+        <view v-if="weeks.extraInfo && weeks.extraInfo.info" class="uni-calendar-item__weeks-info-text-box-content"
+          :class="[weeks.extraInfo.info > 2 ? 'uni-calendar-item__weeks-info-text-box--active' : 'uni-calendar-item__weeks-info-text-box--inactive']">
+
+        </view>
+      </view>
+    </view>
+  </view>
+</template>
+
+<script>
+import { initVueI18n } from '@dcloudio/uni-i18n'
+import i18nMessages from './i18n/index.js'
+const { t } = initVueI18n(i18nMessages)
+
+export default {
+  emits: ['change'],
+  props: {
+    weeks: {
+      type: Object,
+      default() {
+        return {}
+      }
+    },
+    calendar: {
+      type: Object,
+      default: () => {
+        return {}
+      }
+    },
+    selected: {
+      type: Array,
+      default: () => {
+        return []
+      }
+    },
+    lunar: {
+      type: Boolean,
+      default: false
+    }
+  },
+  computed: {
+    todayText() {
+      return t("uni-calender.today")
+    },
+  },
+  methods: {
+    choiceDate(weeks) {
+      this.$emit('change', weeks)
+    }
+  }
+}
+</script>
+
+<style lang="scss" scoped>
+$uni-font-size-base: 14px;
+$uni-text-color: #333;
+$uni-font-size-sm: 12px;
+$uni-color-error: #e43d33;
+$uni-opacity-disabled: 0.3;
+$uni-text-color-disable: #c0c0c0;
+$uni-primary: #2979ff !default;
+
+.uni-calendar-item__weeks-box {
+  // flex: 1;
+  width: fit-content;
+  /* #ifndef APP-NVUE */
+  display: flex;
+  /* #endif */
+  flex-direction: column;
+  justify-content: center;
+  align-items: center;
+  border-radius: 50%;
+  margin: 0 auto;
+  background-color: #F6F8FA;
+}
+
+.uni-calendar-item__weeks-box-text {
+  // font-size: $uni-font-size-base;
+  color: $uni-text-color;
+  font-size: 30rpx;
+  font-weight: bold;
+}
+
+.uni-calendar-item__weeks-lunar-text {
+  font-size: $uni-font-size-sm;
+  color: $uni-text-color;
+}
+
+.uni-calendar-item__weeks-box-item {
+  position: relative;
+  /* #ifndef APP-NVUE */
+  display: flex;
+  /* #endif */
+  flex-direction: column;
+  justify-content: center;
+  align-items: center;
+  width: 76rpx;
+  height: 76rpx;
+}
+
+.uni-calendar-item__weeks-info-text-box {
+  width: 5px;
+  height: 5px;
+  border-radius: 50%;
+}
+
+.uni-calendar-item__weeks-info-text-box-content {
+  height: 100%;
+  border-radius: 50%;
+}
+
+.uni-calendar-item__weeks-info-text-box--active {
+  background-color: #22C55E;
+}
+
+.uni-calendar-item__weeks-info-text-box--inactive {
+  background-color: #FF5A5F;
+}
+
+.uni-calendar-item__weeks-box-circle {
+  position: absolute;
+  top: 5px;
+  right: 5px;
+  width: 8px;
+  height: 8px;
+  border-radius: 8px;
+  background-color: $uni-color-error;
+
+}
+
+.uni-calendar-item--disable {
+  background-color: rgba(249, 249, 249, $uni-opacity-disabled);
+  color: $uni-text-color-disable;
+  // opacity: 0;
+  // display: none;
+}
+
+.uni-calendar-item--disable-after {
+  background-color: rgba(249, 249, 249, $uni-opacity-disabled);
+  color: $uni-text-color-disable;
+  opacity: 0;
+  display: none;
+}
+
+.uni-calendar-item--isDay-text {
+  color: $uni-primary;
+}
+
+.uni-calendar-item--isDay {
+  background-color: $uni-primary;
+  opacity: 0.8;
+  color: #fff;
+}
+
+.uni-calendar-item--extra {
+  color: $uni-color-error;
+  opacity: 0.8;
+}
+
+.uni-calendar-item--checked {
+  background-color: $uni-primary;
+  color: #fff;
+  opacity: 0.8;
+}
+
+.uni-calendar-item--multiple {
+  background-color: $uni-primary;
+  color: #fff;
+  opacity: 0.8;
+}
+
+.uni-calendar-item--before-checked {
+  background-color: #ff5a5f;
+  color: #fff;
+}
+
+.uni-calendar-item--after-checked {
+  background-color: #ff5a5f;
+  color: #fff;
+}
+</style>

+ 615 - 0
src/uni_modules/uni-calendar/components/uni-calendar copy/uni-calendar.vue

@@ -0,0 +1,615 @@
+<template>
+	<view class="uni-calendar">
+		<view v-if="!insert&&show" class="uni-calendar__mask" :class="{'uni-calendar--mask-show':aniMaskShow}" @click="clean"></view>
+		<view v-if="insert || show" class="uni-calendar__content" :class="{'uni-calendar--fixed':!insert,'uni-calendar--ani-show':aniMaskShow}">
+			<view v-if="!insert" class="uni-calendar__header uni-calendar--fixed-top">
+				<view class="uni-calendar__header-btn-box" @click="close">
+					<text class="uni-calendar__header-text uni-calendar--fixed-width">{{cancelText}}</text>
+				</view>
+				<view class="uni-calendar__header-btn-box" @click="confirm">
+					<text class="uni-calendar__header-text uni-calendar--fixed-width">{{okText}}</text>
+				</view>
+			</view>
+			<view class="uni-calendar__header">
+				<view class="uni-calendar__header-btn-box" @click.stop="pre">
+					<!-- <view class="uni-calendar__header-btn uni-calendar--left"></view> -->
+           <uv-icon name="play-left-fill" size="12" :color="canPre ? '#333' : '#ccc'" />
+				</view>
+				<!-- <picker mode="date" :value="date" fields="month" @change="bindDateChange">
+					<text class="uni-calendar__header-text">{{ (nowDate.year||'') +'年'+( nowDate.month||'')+'月'}}</text>
+				</picker> -->
+        <view>
+          <text class="uni-calendar__header-text">{{ (nowDate.year||'') +'年'+( nowDate.month||'')+'月'}}</text>
+        </view>
+				<view class="uni-calendar__header-btn-box" @click.stop="next">
+          <uv-icon name="play-right-fill" size="12" :color="canNext ? '#333' : '#ccc'" />
+					<!-- <view class="uni-calendar__header-btn uni-calendar--right"></view> -->
+				</view>
+				<text class="uni-calendar__backtoday" @click="backToday">{{todayText}}</text>
+
+			</view>
+			<view class="uni-calendar__box">
+				<view v-if="showMonth" class="uni-calendar__box-bg">
+					<text class="uni-calendar__box-bg-text">{{nowDate.month}}</text>
+				</view>
+				<view class="uni-calendar__weeks uni-calendar__weeks-head">
+					<view class="uni-calendar__weeks-day">
+						<text class="uni-calendar__weeks-day-text">{{SUNText}}</text>
+					</view>
+					<view class="uni-calendar__weeks-day">
+						<text class="uni-calendar__weeks-day-text">{{monText}}</text>
+					</view>
+					<view class="uni-calendar__weeks-day">
+						<text class="uni-calendar__weeks-day-text">{{TUEText}}</text>
+					</view>
+					<view class="uni-calendar__weeks-day">
+						<text class="uni-calendar__weeks-day-text">{{WEDText}}</text>
+					</view>
+					<view class="uni-calendar__weeks-day">
+						<text class="uni-calendar__weeks-day-text">{{THUText}}</text>
+					</view>
+					<view class="uni-calendar__weeks-day">
+						<text class="uni-calendar__weeks-day-text">{{FRIText}}</text>
+					</view>
+					<view class="uni-calendar__weeks-day">
+						<text class="uni-calendar__weeks-day-text">{{SATText}}</text>
+					</view>
+				</view>
+				<view class="uni-calendar__weeks" v-for="(item,weekIndex) in weeks" :key="weekIndex">
+					<view class="uni-calendar__weeks-item" v-for="(weeks,weeksIndex) in item" :key="weeksIndex">
+						<calendar-item class="uni-calendar-item--hook" :weeks="weeks" :calendar="calendar" :selected="selected" :lunar="lunar" @change="choiceDate"></calendar-item>
+					</view>
+				</view>
+			</view>
+		</view>
+	</view>
+</template>
+
+<script>
+	import Calendar from './util.js';
+	import CalendarItem from './uni-calendar-item.vue'
+
+	import { initVueI18n } from '@dcloudio/uni-i18n'
+	import i18nMessages from './i18n/index.js'
+	const {	t	} = initVueI18n(i18nMessages)
+
+	/**
+	 * Calendar 日历
+	 * @description 日历组件可以查看日期,选择任意范围内的日期,打点操作。常用场景如:酒店日期预订、火车机票选择购买日期、上下班打卡等
+	 * @tutorial https://ext.dcloud.net.cn/plugin?id=56
+	 * @property {String} date 自定义当前时间,默认为今天
+	 * @property {Boolean} lunar 显示农历
+	 * @property {String} startDate 日期选择范围-开始日期
+	 * @property {String} endDate 日期选择范围-结束日期
+	 * @property {Boolean} range 范围选择
+	 * @property {Boolean} insert = [true|false] 插入模式,默认为false
+	 * 	@value true 弹窗模式
+	 * 	@value false 插入模式
+	 * @property {Boolean} clearDate = [true|false] 弹窗模式是否清空上次选择内容
+	 * @property {Array} selected 打点,期待格式[{date: '2019-06-27', info: '签到', data: { custom: '自定义信息', name: '自定义消息头',xxx:xxx... }}]
+	 * @property {Boolean} showMonth 是否选择月份为背景
+	 * @event {Function} change 日期改变,`insert :ture` 时生效
+	 * @event {Function} confirm 确认选择`insert :false` 时生效
+	 * @event {Function} monthSwitch 切换月份时触发
+	 * @example <uni-calendar :insert="true":lunar="true" :start-date="'2019-3-2'":end-date="'2019-5-20'"@change="change" />
+	 */
+	export default {
+		components: {
+			CalendarItem
+		},
+		emits:['close','confirm','change','monthSwitch'],
+		props: {
+			date: {
+				type: String,
+				default: ''
+			},
+			selected: {
+				type: Array,
+				default () {
+					return []
+				}
+			},
+			lunar: {
+				type: Boolean,
+				default: false
+			},
+			startDate: {
+				type: String,
+				default: ''
+			},
+			endDate: {
+				type: String,
+				default: ''
+			},
+			range: {
+				type: Boolean,
+				default: false
+			},
+			insert: {
+				type: Boolean,
+				default: true
+			},
+			showMonth: {
+				type: Boolean,
+				default: true
+			},
+			clearDate: {
+				type: Boolean,
+				default: true
+			},
+      readonly: {
+        type: Boolean,
+        default: false
+      }
+		},
+		data() {
+			return {
+				show: false,
+				weeks: [],
+				calendar: {},
+				nowDate: '',
+				aniMaskShow: false
+			}
+		},
+		computed:{
+			/**
+			 * for i18n
+			 */
+
+			okText() {
+				return t("uni-calender.ok")
+			},
+			cancelText() {
+				return t("uni-calender.cancel")
+			},
+			todayText() {
+				return t("uni-calender.today")
+			},
+			monText() {
+				return t("uni-calender.MON")
+			},
+			TUEText() {
+				return t("uni-calender.TUE")
+			},
+			WEDText() {
+				return t("uni-calender.WED")
+			},
+			THUText() {
+				return t("uni-calender.THU")
+			},
+			FRIText() {
+				return t("uni-calender.FRI")
+			},
+			SATText() {
+				return t("uni-calender.SAT")
+			},
+			SUNText() {
+				return t("uni-calender.SUN")
+			},
+      canNext() {
+        if (!this.endDate) return true
+        const currentMonth = this.nowDate.month
+        const endMonth = new Date(this.endDate).getMonth() + 1
+        return currentMonth !== endMonth
+      },
+      canPre() {
+        if (!this.startDate) return true
+        const currentMonth = this.nowDate.month
+        const startMonth = new Date(this.startDate).getMonth() + 1
+        return currentMonth !== startMonth
+      }
+		},
+		watch: {
+			date(newVal) {
+				// this.cale.setDate(newVal)
+				this.init(newVal)
+			},
+			startDate(val){
+				this.cale.resetSatrtDate(val)
+				this.cale.setDate(this.nowDate.fullDate)
+				this.weeks = this.cale.weeks
+			},
+			endDate(val){
+				this.cale.resetEndDate(val)
+				this.cale.setDate(this.nowDate.fullDate)
+				this.weeks = this.cale.weeks
+			},
+			selected(newVal) {
+				this.cale.setSelectInfo(this.nowDate.fullDate, newVal)
+				this.weeks = this.cale.weeks
+			}
+		},
+		created() {
+			this.cale = new Calendar({
+				selected: this.selected,
+				startDate: this.startDate,
+				endDate: this.endDate,
+				range: this.range,
+			})
+			this.init(this.date)
+		},
+		methods: {
+			// 取消穿透
+			clean() {},
+			bindDateChange(e) {
+				const value = e.detail.value + '-1'
+				this.setDate(value)
+
+				const { year,month } = this.cale.getDate(value)
+        this.$emit('monthSwitch', {
+            year,
+            month
+        })
+			},
+			/**
+			 * 初始化日期显示
+			 * @param {Object} date
+			 */
+			init(date) {
+				this.cale.setDate(date)
+				this.weeks = this.cale.weeks
+				this.nowDate = this.calendar = this.cale.getInfo(date)
+			},
+			/**
+			 * 打开日历弹窗
+			 */
+			open() {
+				// 弹窗模式并且清理数据
+				if (this.clearDate && !this.insert) {
+					this.cale.cleanMultipleStatus()
+					// this.cale.setDate(this.date)
+					this.init(this.date)
+				}
+				this.show = true
+				this.$nextTick(() => {
+					setTimeout(() => {
+						this.aniMaskShow = true
+					}, 50)
+				})
+			},
+			/**
+			 * 关闭日历弹窗
+			 */
+			close() {
+				this.aniMaskShow = false
+				this.$nextTick(() => {
+					setTimeout(() => {
+						this.show = false
+						this.$emit('close')
+					}, 300)
+				})
+			},
+			/**
+			 * 确认按钮
+			 */
+			confirm() {
+				this.setEmit('confirm')
+				this.close()
+			},
+			/**
+			 * 变化触发
+			 */
+			change() {
+				if (!this.insert) return
+				this.setEmit('change')
+			},
+			/**
+			 * 选择月份触发
+			 */
+			monthSwitch() {
+				let {
+					year,
+					month
+				} = this.nowDate
+				this.$emit('monthSwitch', {
+					year,
+					month: Number(month)
+				})
+			},
+			/**
+			 * 派发事件
+			 * @param {Object} name
+			 */
+			setEmit(name) {
+				let {
+					year,
+					month,
+					date,
+					fullDate,
+					lunar,
+					extraInfo
+				} = this.calendar
+				this.$emit(name, {
+					range: this.cale.multipleStatus,
+					year,
+					month,
+					date,
+					fulldate: fullDate,
+					lunar,
+					extraInfo: extraInfo || {}
+				})
+			},
+			/**
+			 * 选择天触发
+			 * @param {Object} weeks
+			 */
+			choiceDate(weeks) {
+        if (this.readonly) return
+				if (weeks.disable) return
+				this.calendar = weeks
+				// 设置多选
+				this.cale.setMultiple(this.calendar.fullDate)
+				this.weeks = this.cale.weeks
+				this.change()
+			},
+			/**
+			 * 回到今天
+			 */
+			backToday() {
+				const nowYearMonth = `${this.nowDate.year}-${this.nowDate.month}`
+				const date = this.cale.getDate(new Date())
+        const todayYearMonth = `${date.year}-${date.month}`
+
+				this.init(date.fullDate)
+
+        if(nowYearMonth !== todayYearMonth) {
+          this.monthSwitch()
+        }
+
+				this.change()
+        this.$emit('change-month', {
+          date: date.fullDate,
+          month: Number(this.nowDate.month)
+        })
+			},
+			/**
+			 * 上个月
+			 */
+			pre() {
+        if (!this.canPre) return
+				const preDate = this.cale.getDate(this.nowDate.fullDate, -1, 'month').fullDate
+				this.setDate(preDate)
+				this.monthSwitch()
+        this.$emit('change-month', {
+          date: preDate,
+          month: Number(this.nowDate.month)
+        })
+			},
+			/**
+			 * 下个月
+			 */
+			next() {
+        if (!this.canNext) return
+				const nextDate = this.cale.getDate(this.nowDate.fullDate, +1, 'month').fullDate
+				this.setDate(nextDate)
+				this.monthSwitch()
+        this.$emit('change-month', {
+          date: nextDate,
+          month: Number(this.nowDate.month)
+        })
+			},
+			/**
+			 * 设置日期
+			 * @param {Object} date
+			 */
+			setDate(date) {
+				this.cale.setDate(date)
+				this.weeks = this.cale.weeks
+				this.nowDate = this.cale.getInfo(date)
+			}
+		}
+	}
+</script>
+
+<style lang="scss" scoped>
+	$uni-bg-color-mask: rgba($color: #000000, $alpha: 0.4);
+	$uni-border-color: #EDEDED;
+	$uni-text-color: #333;
+	$uni-bg-color-hover:#f1f1f1;
+	$uni-font-size-base:14px;
+	$uni-text-color-placeholder: #808080;
+	$uni-color-subtitle: #555555;
+	$uni-text-color-grey:#999;
+	.uni-calendar {
+		/* #ifndef APP-NVUE */
+		display: flex;
+		/* #endif */
+		flex-direction: column;
+	}
+
+	.uni-calendar__mask {
+		position: fixed;
+		bottom: 0;
+		top: 0;
+		left: 0;
+		right: 0;
+		background-color: $uni-bg-color-mask;
+		transition-property: opacity;
+		transition-duration: 0.3s;
+		opacity: 0;
+		/* #ifndef APP-NVUE */
+		z-index: 99;
+		/* #endif */
+	}
+
+	.uni-calendar--mask-show {
+		opacity: 1
+	}
+
+	.uni-calendar--fixed {
+		position: fixed;
+		/* #ifdef APP-NVUE */
+		bottom: 0;
+		/* #endif */
+		left: 0;
+		right: 0;
+		transition-property: transform;
+		transition-duration: 0.3s;
+		transform: translateY(460px);
+		/* #ifndef APP-NVUE */
+		bottom: calc(var(--window-bottom));
+		z-index: 99;
+		/* #endif */
+	}
+
+	.uni-calendar--ani-show {
+		transform: translateY(0);
+	}
+
+	.uni-calendar__content {
+		background-color: #fff;
+	}
+
+	.uni-calendar__header {
+		position: relative;
+		/* #ifndef APP-NVUE */
+		display: flex;
+		/* #endif */
+		flex-direction: row;
+		justify-content: center;
+		align-items: center;
+		height: 50px;
+		// border-bottom-color: $uni-border-color;
+		// border-bottom-style: solid;
+		// border-bottom-width: 1px;
+	}
+
+	.uni-calendar--fixed-top {
+		/* #ifndef APP-NVUE */
+		display: flex;
+		/* #endif */
+		flex-direction: row;
+		justify-content: space-between;
+		border-top-color: $uni-border-color;
+		border-top-style: solid;
+		border-top-width: 1px;
+	}
+
+	.uni-calendar--fixed-width {
+		width: 50px;
+	}
+
+	.uni-calendar__backtoday {
+		position: absolute;
+		right: 0;
+		top: 25rpx;
+		padding: 0 5px;
+		padding-left: 10px;
+		height: 25px;
+		line-height: 25px;
+		font-size: 12px;
+		border-top-left-radius: 25px;
+		border-bottom-left-radius: 25px;
+		color: $uni-text-color;
+		background-color: $uni-bg-color-hover;
+	}
+
+	.uni-calendar__header-text {
+    display: block;
+		text-align: center;
+		width: 116px;
+		// font-size: $uni-font-size-base;
+		color: $uni-text-color;
+    font-size: 30rpx; 
+    font-weight: bold;
+	}
+
+	.uni-calendar__header-btn-box {
+		/* #ifndef APP-NVUE */
+		display: flex;
+		/* #endif */
+		flex-direction: row;
+		align-items: center;
+		justify-content: center;
+		width: 50px;
+		height: 50px;
+	}
+
+	.uni-calendar__header-btn {
+		width: 6px;
+		height: 6px;
+		border-left-color: $uni-text-color-placeholder;
+		border-left-style: solid;
+		border-left-width: 2px;
+		border-top-color: $uni-color-subtitle;
+		border-top-style: solid;
+		border-top-width: 2px;
+	}
+
+	.uni-calendar--left {
+		transform: rotate(-45deg);
+	}
+
+	.uni-calendar--right {
+		transform: rotate(135deg);
+	}
+
+
+	.uni-calendar__weeks {
+		position: relative;
+		/* #ifndef APP-NVUE */
+		display: flex;
+		/* #endif */
+		flex-direction: row;
+	}
+
+  .uni-calendar__weeks-head {
+    color: #B3B3B3;
+    font-size: 30rpx;
+  }
+
+  .uni-calendar__weeks + .uni-calendar__weeks {
+    margin-top: 26rpx;
+  }
+
+	.uni-calendar__weeks-item {
+		flex: 1;
+	}
+
+	.uni-calendar__weeks-day {
+		flex: 1;
+		/* #ifndef APP-NVUE */
+		display: flex;
+		/* #endif */
+		flex-direction: column;
+		justify-content: center;
+		align-items: center;
+		height: 45px;
+		// border-bottom-color: #F5F5F5;
+		// border-bottom-style: solid;
+		// border-bottom-width: 1px;
+	}
+
+	.uni-calendar__weeks-day-text {
+		font-size: 14px;
+	}
+
+	.uni-calendar__box {
+		position: relative;
+    padding: 0 40rpx;
+	}
+
+	.uni-calendar__box-bg {
+		/* #ifndef APP-NVUE */
+		display: flex;
+		/* #endif */
+		justify-content: center;
+		align-items: center;
+		position: absolute;
+		top: 0;
+		left: 0;
+		right: 0;
+		bottom: 0;
+	}
+
+	.uni-calendar__box-bg-text {
+		font-size: 200px;
+		font-weight: bold;
+		color: $uni-text-color-grey;
+		opacity: 0.1;
+		text-align: center;
+		/* #ifndef APP-NVUE */
+		line-height: 1;
+		/* #endif */
+	}
+</style>

+ 362 - 0
src/uni_modules/uni-calendar/components/uni-calendar copy/util.js

@@ -0,0 +1,362 @@
+import CALENDAR from './calendar.js'
+
+class Calendar {
+	constructor({
+		date,
+		selected,
+		startDate,
+		endDate,
+		range
+	} = {}) {
+		// 当前日期
+		this.date = this.getDate(new Date()) // 当前初入日期
+		// 打点信息
+		this.selected = selected || [];
+		// 范围开始
+		this.startDate = startDate
+		// 范围结束
+		this.endDate = endDate
+		this.range = range
+		// 多选状态
+		this.cleanMultipleStatus()
+		// 每周日期
+		this.weeks = {}
+		// this._getWeek(this.date.fullDate)
+	}
+	/**
+	 * 设置日期
+	 * @param {Object} date
+	 */
+	setDate(date) {
+		this.selectDate = this.getDate(date)
+		this._getWeek(this.selectDate.fullDate)
+	}
+
+	/**
+	 * 清理多选状态
+	 */
+	cleanMultipleStatus() {
+		this.multipleStatus = {
+			before: '',
+			after: '',
+			data: []
+		}
+	}
+
+	/**
+	 * 重置开始日期
+	 */
+	resetSatrtDate(startDate) {
+		// 范围开始
+		this.startDate = startDate
+
+	}
+
+	/**
+	 * 重置结束日期
+	 */
+	resetEndDate(endDate) {
+		// 范围结束
+		this.endDate = endDate
+	}
+
+	/**
+	 * 获取任意时间
+	 */
+	getDate(date, AddDayCount = 0, str = 'day') {
+		if (!date) {
+			date = new Date()
+		}
+		if (typeof date !== 'object') {
+			date = date.replace(/-/g, '/')
+		}
+		const dd = new Date(date)
+		switch (str) {
+			case 'day':
+				dd.setDate(dd.getDate() + AddDayCount) // 获取AddDayCount天后的日期
+				break
+			case 'month':
+				if (dd.getDate() === 31 && AddDayCount>0) {
+					dd.setDate(dd.getDate() + AddDayCount)
+				} else {
+					const preMonth = dd.getMonth()
+					dd.setMonth(preMonth + AddDayCount) // 获取AddDayCount天后的日期
+					const nextMonth = dd.getMonth()
+					// 处理 pre 切换月份目标月份为2月没有当前日(30 31) 切换错误问题
+					if(AddDayCount<0 && preMonth!==0 && nextMonth-preMonth>AddDayCount){
+						dd.setMonth(nextMonth+(nextMonth-preMonth+AddDayCount))
+					}
+					// 处理 next 切换月份目标月份为2月没有当前日(30 31) 切换错误问题
+					if(AddDayCount>0 && nextMonth-preMonth>AddDayCount){
+						dd.setMonth(nextMonth-(nextMonth-preMonth-AddDayCount))
+					}
+				}
+				break
+			case 'year':
+				dd.setFullYear(dd.getFullYear() + AddDayCount) // 获取AddDayCount天后的日期
+				break
+		}
+		const y = dd.getFullYear()
+		const m = dd.getMonth() + 1 < 10 ? '0' + (dd.getMonth() + 1) : dd.getMonth() + 1 // 获取当前月份的日期,不足10补0
+		const d = dd.getDate() < 10 ? '0' + dd.getDate() : dd.getDate() // 获取当前几号,不足10补0
+		return {
+			fullDate: y + '-' + m + '-' + d,
+			year: y,
+			month: m,
+			date: d,
+			day: dd.getDay()
+		}
+	}
+
+
+	/**
+	 * 获取上月剩余天数
+	 */
+	_getLastMonthDays(firstDay, full) {
+		let dateArr = []
+		for (let i = firstDay; i > 0; i--) {
+			const beforeDate = new Date(full.year, full.month - 1, -i + 1).getDate()
+			dateArr.push({
+				date: beforeDate,
+				month: full.month - 1,
+				lunar: this.getlunar(full.year, full.month - 1, beforeDate),
+				disable: true
+			})
+		}
+		return dateArr
+	}
+	/**
+	 * 获取本月天数
+	 */
+	_currentMonthDys(dateData, full) {
+		let dateArr = []
+		let fullDate = this.date.fullDate
+		for (let i = 1; i <= dateData; i++) {
+			let nowDate = full.year + '-' + (full.month < 10 ?
+				full.month : full.month) + '-' + (i < 10 ?
+				'0' + i : i)
+			// 是否今天
+			let isDay = fullDate === nowDate
+			// 获取打点信息
+			let info = this.selected && this.selected.find((item) => {
+				if (this.dateEqual(nowDate, item.date)) {
+					return item
+				}
+			})
+
+			// 日期禁用
+			let disableBefore = true
+			let disableAfter = true
+			if (this.startDate) {
+				// let dateCompBefore = this.dateCompare(this.startDate, fullDate)
+				// disableBefore = this.dateCompare(dateCompBefore ? this.startDate : fullDate, nowDate)
+				disableBefore = this.dateCompare(this.startDate, nowDate)
+			}
+
+			if (this.endDate) {
+				// let dateCompAfter = this.dateCompare(fullDate, this.endDate)
+				// disableAfter = this.dateCompare(nowDate, dateCompAfter ? this.endDate : fullDate)
+				disableAfter = this.dateCompare(nowDate, this.endDate)
+			}
+			let multiples = this.multipleStatus.data
+			let checked = false
+			let multiplesStatus = -1
+			if (this.range) {
+				if (multiples) {
+					multiplesStatus = multiples.findIndex((item) => {
+						return this.dateEqual(item, nowDate)
+					})
+				}
+				if (multiplesStatus !== -1) {
+					checked = true
+				}
+			}
+			let data = {
+				fullDate: nowDate,
+				year: full.year,
+				date: i,
+				multiple: this.range ? checked : false,
+				beforeMultiple: this.dateEqual(this.multipleStatus.before, nowDate),
+				afterMultiple: this.dateEqual(this.multipleStatus.after, nowDate),
+				month: full.month,
+				lunar: this.getlunar(full.year, full.month, i),
+				disable: !(disableBefore && disableAfter),
+        disableAfter: !disableAfter,
+        disableBefore,
+				isDay
+			}
+			if (info) {
+				data.extraInfo = info
+			}
+
+			dateArr.push(data)
+		}
+		return dateArr
+	}
+	/**
+	 * 获取下月天数
+	 */
+	_getNextMonthDays(surplus, full) {
+		let dateArr = []
+		for (let i = 1; i < surplus + 1; i++) {
+			dateArr.push({
+				date: i,
+				month: Number(full.month) + 1,
+				lunar: this.getlunar(full.year, Number(full.month) + 1, i),
+				disable: true
+			})
+		}
+		return dateArr
+	}
+
+	/**
+	 * 获取当前日期详情
+	 * @param {Object} date
+	 */
+	getInfo(date) {
+		if (!date) {
+			date = new Date()
+		}
+		const dateInfo = this.canlender.find(item => item.fullDate === this.getDate(date).fullDate)
+		return dateInfo
+	}
+
+	/**
+	 * 比较时间大小
+	 */
+	dateCompare(startDate, endDate) {
+		// 计算截止时间
+		startDate = new Date(startDate.replace('-', '/').replace('-', '/'))
+		// 计算详细项的截止时间
+		endDate = new Date(endDate.replace('-', '/').replace('-', '/'))
+		if (startDate <= endDate) {
+			return true
+		} else {
+			return false
+		}
+	}
+
+	/**
+	 * 比较时间是否相等
+	 */
+	dateEqual(before, after) {
+		// 计算截止时间
+		before = new Date(before.replace('-', '/').replace('-', '/'))
+		// 计算详细项的截止时间
+		after = new Date(after.replace('-', '/').replace('-', '/'))
+		if (before.getTime() - after.getTime() === 0) {
+			return true
+		} else {
+			return false
+		}
+	}
+
+
+	/**
+	 * 获取日期范围内所有日期
+	 * @param {Object} begin
+	 * @param {Object} end
+	 */
+	geDateAll(begin, end) {
+		var arr = []
+		var ab = begin.split('-')
+		var ae = end.split('-')
+		var db = new Date()
+		db.setFullYear(ab[0], ab[1] - 1, ab[2])
+		var de = new Date()
+		de.setFullYear(ae[0], ae[1] - 1, ae[2])
+		var unixDb = db.getTime() - 24 * 60 * 60 * 1000
+		var unixDe = de.getTime() - 24 * 60 * 60 * 1000
+		for (var k = unixDb; k <= unixDe;) {
+			k = k + 24 * 60 * 60 * 1000
+			arr.push(this.getDate(new Date(parseInt(k))).fullDate)
+		}
+		return arr
+	}
+	/**
+	 * 计算阴历日期显示
+	 */
+	getlunar(year, month, date) {
+		return CALENDAR.solar2lunar(year, month, date)
+	}
+	/**
+	 * 设置打点
+	 */
+	setSelectInfo(data, value) {
+		this.selected = value
+		this._getWeek(data)
+	}
+
+	/**
+	 *  获取多选状态
+	 */
+	setMultiple(fullDate) {
+		let {
+			before,
+			after
+		} = this.multipleStatus
+
+		if (!this.range) return
+		if (before && after) {
+			this.multipleStatus.before = fullDate
+			this.multipleStatus.after = ''
+			this.multipleStatus.data = []
+		} else {
+			if (!before) {
+				this.multipleStatus.before = fullDate
+			} else {
+				this.multipleStatus.after = fullDate
+				if (this.dateCompare(this.multipleStatus.before, this.multipleStatus.after)) {
+					this.multipleStatus.data = this.geDateAll(this.multipleStatus.before, this.multipleStatus.after);
+				} else {
+					this.multipleStatus.data = this.geDateAll(this.multipleStatus.after, this.multipleStatus.before);
+				}
+			}
+		}
+		this._getWeek(fullDate)
+	}
+
+	/**
+	 * 获取每周数据
+	 * @param {Object} dateData
+	 */
+	_getWeek(dateData) {
+		const {
+			year,
+			month
+		} = this.getDate(dateData)
+		let firstDay = new Date(year, month - 1, 1).getDay()
+		let currentDay = new Date(year, month, 0).getDate()
+		let dates = {
+			lastMonthDays: this._getLastMonthDays(firstDay, this.getDate(dateData)), // 上个月末尾几天
+			currentMonthDys: this._currentMonthDys(currentDay, this.getDate(dateData)), // 本月天数
+			nextMonthDays: [], // 下个月开始几天
+			weeks: []
+		}
+		let canlender = []
+		const surplus = 42 - (dates.lastMonthDays.length + dates.currentMonthDys.length)
+		dates.nextMonthDays = this._getNextMonthDays(surplus, this.getDate(dateData))
+		canlender = canlender.concat(dates.lastMonthDays, dates.currentMonthDys, dates.nextMonthDays)
+		let weeks = {}
+		// 拼接数组  上个月开始几天 + 本月天数+ 下个月开始几天
+		for (let i = 0; i < canlender.length; i++) {
+			if (i % 7 === 0) {
+				weeks[parseInt(i / 7)] = new Array(7)
+			}
+			weeks[parseInt(i / 7)][i % 7] = canlender[i]
+		}
+		this.canlender = canlender
+		this.weeks = weeks
+	}
+
+	//静态方法
+	// static init(date) {
+	// 	if (!this.instance) {
+	// 		this.instance = new Calendar(date);
+	// 	}
+	// 	return this.instance;
+	// }
+}
+
+
+export default Calendar

+ 22 - 36
src/uni_modules/uni-calendar/components/uni-calendar/uni-calendar-item.vue

@@ -1,58 +1,29 @@
 <template>
   <view class="uni-calendar-item__weeks-box" :class="{
     'uni-calendar-item--disable': weeks.disable,
-    'uni-calendar-item--isDay': calendar.fullDate === weeks.fullDate && weeks.isDay,
+    'uni-calendar-item--isDay': calendar.fullDate === weeks.fullDate && weeks.isDay && highlightToday,
     'uni-calendar-item--checked': (calendar.fullDate === weeks.fullDate && !weeks.isDay),
     'uni-calendar-item--before-checked': weeks.beforeMultiple,
     'uni-calendar-item--multiple': weeks.multiple,
     'uni-calendar-item--after-checked': weeks.afterMultiple,
+    'uni-calendar-item--week-mode-disabled': weeks.isWeekModeDisabled,
   }" @click="choiceDate(weeks)">
     <view class="uni-calendar-item__weeks-box-item"
-     :class="[{'uni-calendar-item--disable-after': weeks.disableAfter != false && weeks.disableAfter != true}]">
-      <!-- <text v-if="selected&&weeks.extraInfo" class="uni-calendar-item__weeks-box-circle"></text> -->
+      :class="[{ 'uni-calendar-item--disable-after': weeks.disableAfter != false && weeks.disableAfter != true }]">
       <text class="uni-calendar-item__weeks-box-text" :class="{
-        'uni-calendar-item--isDay-text': weeks.isDay,
-        'uni-calendar-item--isDay': calendar.fullDate === weeks.fullDate && weeks.isDay,
+        'uni-calendar-item--isDay-text': weeks.isDay && highlightToday,
+        'uni-calendar-item--isDay': calendar.fullDate === weeks.fullDate && weeks.isDay && highlightToday,
         'uni-calendar-item--checked': calendar.fullDate === weeks.fullDate && !weeks.isDay,
         'uni-calendar-item--before-checked': weeks.beforeMultiple,
         'uni-calendar-item--multiple': weeks.multiple,
         'uni-calendar-item--after-checked': weeks.afterMultiple,
         'uni-calendar-item--disable': weeks.disable,
+        'uni-calendar-item--week-mode-disabled': weeks.isWeekModeDisabled,
+        'uni-calendar-item--not-current-month': !weeks.isCurrentMonth,
       }">{{ weeks.date }}</text>
-      <!-- <text v-if="!lunar&&!weeks.extraInfo && weeks.isDay" class="uni-calendar-item__weeks-lunar-text" :class="{
-				'uni-calendar-item--isDay-text':weeks.isDay,
-				'uni-calendar-item--isDay':calendar.fullDate === weeks.fullDate && weeks.isDay,
-				'uni-calendar-item--checked':calendar.fullDate === weeks.fullDate && !weeks.isDay,
-				'uni-calendar-item--before-checked':weeks.beforeMultiple,
-				'uni-calendar-item--multiple': weeks.multiple,
-				'uni-calendar-item--after-checked':weeks.afterMultiple,
-				}">{{todayText}}</text> -->
-      <!-- <text v-if="lunar&&!weeks.extraInfo" class="uni-calendar-item__weeks-lunar-text" :class="{
-				'uni-calendar-item--isDay-text':weeks.isDay,
-				'uni-calendar-item--isDay':calendar.fullDate === weeks.fullDate && weeks.isDay,
-				'uni-calendar-item--checked':calendar.fullDate === weeks.fullDate && !weeks.isDay,
-				'uni-calendar-item--before-checked':weeks.beforeMultiple,
-				'uni-calendar-item--multiple': weeks.multiple,
-				'uni-calendar-item--after-checked':weeks.afterMultiple,
-				'uni-calendar-item--disable':weeks.disable,
-				}">{{weeks.isDay ? todayText : (weeks.lunar.IDayCn === '初一'?weeks.lunar.IMonthCn:weeks.lunar.IDayCn)}}</text> -->
-      <!-- <text v-if="weeks.extraInfo&&weeks.extraInfo.info" class="uni-calendar-item__weeks-lunar-text" :class="{
-				'uni-calendar-item--extra':weeks.extraInfo.info,
-				'uni-calendar-item--isDay-text':weeks.isDay,
-				'uni-calendar-item--isDay':calendar.fullDate === weeks.fullDate && weeks.isDay,
-				'uni-calendar-item--checked':calendar.fullDate === weeks.fullDate && !weeks.isDay,
-				'uni-calendar-item--before-checked':weeks.beforeMultiple,
-				'uni-calendar-item--multiple': weeks.multiple,
-				'uni-calendar-item--after-checked':weeks.afterMultiple,
-				'uni-calendar-item--disable':weeks.disable,
-				}">{{weeks.extraInfo.info}}</text> -->
       <view class="uni-calendar-item__weeks-info-text-box">
-        <!-- <text class="uni-calendar-item__weeks-lunar-text" :class="{
-          'uni-calendar-item--extra':weeks.extraInfo.info,				
-          }">{{weeks.extraInfo.info}}</text> -->
         <view v-if="weeks.extraInfo && weeks.extraInfo.info" class="uni-calendar-item__weeks-info-text-box-content"
           :class="[weeks.extraInfo.info > 2 ? 'uni-calendar-item__weeks-info-text-box--active' : 'uni-calendar-item__weeks-info-text-box--inactive']">
-
         </view>
       </view>
     </view>
@@ -88,6 +59,10 @@ export default {
     lunar: {
       type: Boolean,
       default: false
+    },
+    highlightToday: {
+      type: Boolean,
+      default: true
     }
   },
   computed: {
@@ -230,4 +205,15 @@ $uni-primary: #2979ff !default;
   background-color: #ff5a5f;
   color: #fff;
 }
+
+.uni-calendar-item--week-mode-disabled {
+  background-color: rgba(249, 249, 249, $uni-opacity-disabled);
+  color: $uni-text-color-disable;
+  // color: #ccc;
+}
+
+.uni-calendar-item--not-current-month {
+  opacity: 0.5;
+  color: #999;
+}
 </style>

+ 752 - 599
src/uni_modules/uni-calendar/components/uni-calendar/uni-calendar.vue

@@ -1,615 +1,768 @@
 <template>
-	<view class="uni-calendar">
-		<view v-if="!insert&&show" class="uni-calendar__mask" :class="{'uni-calendar--mask-show':aniMaskShow}" @click="clean"></view>
-		<view v-if="insert || show" class="uni-calendar__content" :class="{'uni-calendar--fixed':!insert,'uni-calendar--ani-show':aniMaskShow}">
-			<view v-if="!insert" class="uni-calendar__header uni-calendar--fixed-top">
-				<view class="uni-calendar__header-btn-box" @click="close">
-					<text class="uni-calendar__header-text uni-calendar--fixed-width">{{cancelText}}</text>
-				</view>
-				<view class="uni-calendar__header-btn-box" @click="confirm">
-					<text class="uni-calendar__header-text uni-calendar--fixed-width">{{okText}}</text>
-				</view>
-			</view>
-			<view class="uni-calendar__header">
-				<view class="uni-calendar__header-btn-box" @click.stop="pre">
-					<!-- <view class="uni-calendar__header-btn uni-calendar--left"></view> -->
-           <uv-icon name="play-left-fill" size="12" :color="canPre ? '#333' : '#ccc'" />
-				</view>
-				<!-- <picker mode="date" :value="date" fields="month" @change="bindDateChange">
+  <view class="uni-calendar">
+    <view v-if="!insert && show" class="uni-calendar__mask" :class="{ 'uni-calendar--mask-show': aniMaskShow }"
+      @click="clean"></view>
+    <view v-if="insert || show" class="uni-calendar__content"
+      :class="{ 'uni-calendar--fixed': !insert, 'uni-calendar--ani-show': aniMaskShow }">
+      <view v-if="!insert" class="uni-calendar__header uni-calendar--fixed-top">
+        <view class="uni-calendar__header-btn-box" @click="close">
+          <text class="uni-calendar__header-text uni-calendar--fixed-width">{{ cancelText }}</text>
+        </view>
+        <view class="uni-calendar__header-btn-box" @click="confirm">
+          <text class="uni-calendar__header-text uni-calendar--fixed-width">{{ okText }}</text>
+        </view>
+      </view>
+      <view v-if="showToolbar" class="uni-calendar__header">
+        <view class="uni-calendar__header-btn-box" @click.stop="pre">
+          <!-- <view class="uni-calendar__header-btn uni-calendar--left"></view> -->
+          <uv-icon name="play-left-fill" size="12" :color="canPre ? '#333' : '#ccc'" />
+        </view>
+        <!-- <picker mode="date" :value="date" fields="month" @change="bindDateChange">
 					<text class="uni-calendar__header-text">{{ (nowDate.year||'') +'年'+( nowDate.month||'')+'月'}}</text>
 				</picker> -->
         <view>
-          <text class="uni-calendar__header-text">{{ (nowDate.year||'') +'年'+( nowDate.month||'')+'月'}}</text>
+          <text class="uni-calendar__header-text">{{ (nowDate.year || '') + '年' + (nowDate.month || '') + '月' }}</text>
         </view>
-				<view class="uni-calendar__header-btn-box" @click.stop="next">
+        <view class="uni-calendar__header-btn-box" @click.stop="next">
           <uv-icon name="play-right-fill" size="12" :color="canNext ? '#333' : '#ccc'" />
-					<!-- <view class="uni-calendar__header-btn uni-calendar--right"></view> -->
-				</view>
-				<text class="uni-calendar__backtoday" @click="backToday">{{todayText}}</text>
-
-			</view>
-			<view class="uni-calendar__box">
-				<view v-if="showMonth" class="uni-calendar__box-bg">
-					<text class="uni-calendar__box-bg-text">{{nowDate.month}}</text>
-				</view>
-				<view class="uni-calendar__weeks uni-calendar__weeks-head">
-					<view class="uni-calendar__weeks-day">
-						<text class="uni-calendar__weeks-day-text">{{SUNText}}</text>
-					</view>
-					<view class="uni-calendar__weeks-day">
-						<text class="uni-calendar__weeks-day-text">{{monText}}</text>
-					</view>
-					<view class="uni-calendar__weeks-day">
-						<text class="uni-calendar__weeks-day-text">{{TUEText}}</text>
-					</view>
-					<view class="uni-calendar__weeks-day">
-						<text class="uni-calendar__weeks-day-text">{{WEDText}}</text>
-					</view>
-					<view class="uni-calendar__weeks-day">
-						<text class="uni-calendar__weeks-day-text">{{THUText}}</text>
-					</view>
-					<view class="uni-calendar__weeks-day">
-						<text class="uni-calendar__weeks-day-text">{{FRIText}}</text>
-					</view>
-					<view class="uni-calendar__weeks-day">
-						<text class="uni-calendar__weeks-day-text">{{SATText}}</text>
-					</view>
-				</view>
-				<view class="uni-calendar__weeks" v-for="(item,weekIndex) in weeks" :key="weekIndex">
-					<view class="uni-calendar__weeks-item" v-for="(weeks,weeksIndex) in item" :key="weeksIndex">
-						<calendar-item class="uni-calendar-item--hook" :weeks="weeks" :calendar="calendar" :selected="selected" :lunar="lunar" @change="choiceDate"></calendar-item>
-					</view>
-				</view>
-			</view>
-		</view>
-	</view>
+          <!-- <view class="uni-calendar__header-btn uni-calendar--right"></view> -->
+        </view>
+        <text v-if="highlightToday" class="uni-calendar__backtoday" @click="backToday">{{ todayText }}</text>
+
+      </view>
+      <view class="uni-calendar__box">
+        <view v-if="showMonth" class="uni-calendar__box-bg">
+          <text class="uni-calendar__box-bg-text">{{ nowDate.month }}</text>
+        </view>
+        <view class="uni-calendar__weeks uni-calendar__weeks-head">
+          <template v-if="sundayFirst">
+            <view class="uni-calendar__weeks-day">
+              <text class="uni-calendar__weeks-day-text">{{ SUNText }}</text>
+            </view>
+            <view class="uni-calendar__weeks-day">
+              <text class="uni-calendar__weeks-day-text">{{ monText }}</text>
+            </view>
+            <view class="uni-calendar__weeks-day">
+              <text class="uni-calendar__weeks-day-text">{{ TUEText }}</text>
+            </view>
+            <view class="uni-calendar__weeks-day">
+              <text class="uni-calendar__weeks-day-text">{{ WEDText }}</text>
+            </view>
+            <view class="uni-calendar__weeks-day">
+              <text class="uni-calendar__weeks-day-text">{{ THUText }}</text>
+            </view>
+            <view class="uni-calendar__weeks-day">
+              <text class="uni-calendar__weeks-day-text">{{ FRIText }}</text>
+            </view>
+            <view class="uni-calendar__weeks-day">
+              <text class="uni-calendar__weeks-day-text">{{ SATText }}</text>
+            </view>
+          </template>
+          <template v-else>
+            <view class="uni-calendar__weeks-day">
+              <text class="uni-calendar__weeks-day-text">{{ monText }}</text>
+            </view>
+            <view class="uni-calendar__weeks-day">
+              <text class="uni-calendar__weeks-day-text">{{ TUEText }}</text>
+            </view>
+            <view class="uni-calendar__weeks-day">
+              <text class="uni-calendar__weeks-day-text">{{ WEDText }}</text>
+            </view>
+            <view class="uni-calendar__weeks-day">
+              <text class="uni-calendar__weeks-day-text">{{ THUText }}</text>
+            </view>
+            <view class="uni-calendar__weeks-day">
+              <text class="uni-calendar__weeks-day-text">{{ FRIText }}</text>
+            </view>
+            <view class="uni-calendar__weeks-day">
+              <text class="uni-calendar__weeks-day-text">{{ SATText }}</text>
+            </view>
+            <view class="uni-calendar__weeks-day">
+              <text class="uni-calendar__weeks-day-text">{{ SUNText }}</text>
+            </view>
+          </template>
+        </view>
+        <view class="uni-calendar__weeks" v-for="(item, weekIndex) in filteredWeeks" :key="weekIndex">
+          <view class="uni-calendar__weeks-item" v-for="(weeks, weeksIndex) in item" :key="weeksIndex">
+            <slot name="calendar-item" :weeks="weeks" :calendar="calendar" :selected="selected" :lunar="lunar"
+              :highlight-today="highlightToday" :week-index="weekIndex" :weeks-index="weeksIndex">
+              <calendar-item class="uni-calendar-item--hook" :weeks="weeks" :calendar="calendar" :selected="selected"
+                :lunar="lunar" :highlight-today="highlightToday" @change="choiceDate" />
+            </slot>
+          </view>
+        </view>
+      </view>
+    </view>
+  </view>
 </template>
 
 <script>
-	import Calendar from './util.js';
-	import CalendarItem from './uni-calendar-item.vue'
-
-	import { initVueI18n } from '@dcloudio/uni-i18n'
-	import i18nMessages from './i18n/index.js'
-	const {	t	} = initVueI18n(i18nMessages)
-
-	/**
-	 * Calendar 日历
-	 * @description 日历组件可以查看日期,选择任意范围内的日期,打点操作。常用场景如:酒店日期预订、火车机票选择购买日期、上下班打卡等
-	 * @tutorial https://ext.dcloud.net.cn/plugin?id=56
-	 * @property {String} date 自定义当前时间,默认为今天
-	 * @property {Boolean} lunar 显示农历
-	 * @property {String} startDate 日期选择范围-开始日期
-	 * @property {String} endDate 日期选择范围-结束日期
-	 * @property {Boolean} range 范围选择
-	 * @property {Boolean} insert = [true|false] 插入模式,默认为false
-	 * 	@value true 弹窗模式
-	 * 	@value false 插入模式
-	 * @property {Boolean} clearDate = [true|false] 弹窗模式是否清空上次选择内容
-	 * @property {Array} selected 打点,期待格式[{date: '2019-06-27', info: '签到', data: { custom: '自定义信息', name: '自定义消息头',xxx:xxx... }}]
-	 * @property {Boolean} showMonth 是否选择月份为背景
-	 * @event {Function} change 日期改变,`insert :ture` 时生效
-	 * @event {Function} confirm 确认选择`insert :false` 时生效
-	 * @event {Function} monthSwitch 切换月份时触发
-	 * @example <uni-calendar :insert="true":lunar="true" :start-date="'2019-3-2'":end-date="'2019-5-20'"@change="change" />
-	 */
-	export default {
-		components: {
-			CalendarItem
-		},
-		emits:['close','confirm','change','monthSwitch'],
-		props: {
-			date: {
-				type: String,
-				default: ''
-			},
-			selected: {
-				type: Array,
-				default () {
-					return []
-				}
-			},
-			lunar: {
-				type: Boolean,
-				default: false
-			},
-			startDate: {
-				type: String,
-				default: ''
-			},
-			endDate: {
-				type: String,
-				default: ''
-			},
-			range: {
-				type: Boolean,
-				default: false
-			},
-			insert: {
-				type: Boolean,
-				default: true
-			},
-			showMonth: {
-				type: Boolean,
-				default: true
-			},
-			clearDate: {
-				type: Boolean,
-				default: true
-			},
-      readonly: {
-        type: Boolean,
-        default: false
-      }
-		},
-		data() {
-			return {
-				show: false,
-				weeks: [],
-				calendar: {},
-				nowDate: '',
-				aniMaskShow: false
-			}
-		},
-		computed:{
-			/**
-			 * for i18n
-			 */
-
-			okText() {
-				return t("uni-calender.ok")
-			},
-			cancelText() {
-				return t("uni-calender.cancel")
-			},
-			todayText() {
-				return t("uni-calender.today")
-			},
-			monText() {
-				return t("uni-calender.MON")
-			},
-			TUEText() {
-				return t("uni-calender.TUE")
-			},
-			WEDText() {
-				return t("uni-calender.WED")
-			},
-			THUText() {
-				return t("uni-calender.THU")
-			},
-			FRIText() {
-				return t("uni-calender.FRI")
-			},
-			SATText() {
-				return t("uni-calender.SAT")
-			},
-			SUNText() {
-				return t("uni-calender.SUN")
-			},
-      canNext() {
-        if (!this.endDate) return true
-        const currentMonth = this.nowDate.month
-        const endMonth = new Date(this.endDate).getMonth() + 1
-        return currentMonth !== endMonth
-      },
-      canPre() {
-        if (!this.startDate) return true
-        const currentMonth = this.nowDate.month
-        const startMonth = new Date(this.startDate).getMonth() + 1
-        return currentMonth !== startMonth
+import Calendar from './util.js';
+import CalendarItem from './uni-calendar-item.vue'
+
+import { initVueI18n } from '@dcloudio/uni-i18n'
+import i18nMessages from './i18n/index.js'
+const { t } = initVueI18n(i18nMessages)
+
+/**
+ * Calendar 日历
+ * @description 日历组件可以查看日期,选择任意范围内的日期,打点操作。常用场景如:酒店日期预订、火车机票选择购买日期、上下班打卡等
+ * @tutorial https://ext.dcloud.net.cn/plugin?id=56
+ * @property {String} date 自定义当前时间,默认为今天
+ * @property {Boolean} lunar 显示农历
+ * @property {String} startDate 日期选择范围-开始日期
+ * @property {String} endDate 日期选择范围-结束日期
+ * @property {Boolean} range 范围选择
+ * @property {Boolean} insert = [true|false] 插入模式,默认为false
+ * 	@value true 弹窗模式
+ * 	@value false 插入模式
+ * @property {Boolean} clearDate = [true|false] 弹窗模式是否清空上次选择内容
+ * @property {Array} selected 打点,期待格式[{date: '2019-06-27', info: '签到', data: { custom: '自定义信息', name: '自定义消息头',xxx:xxx... }}]
+ * @property {Boolean} showMonth 是否选择月份为背景
+ * @event {Function} change 日期改变,`insert :ture` 时生效
+ * @event {Function} confirm 确认选择`insert :false` 时生效
+ * @event {Function} monthSwitch 切换月份时触发
+ * @example <uni-calendar :insert="true":lunar="true" :start-date="'2019-3-2'":end-date="'2019-5-20'"@change="change" />
+ */
+export default {
+  components: {
+    CalendarItem
+  },
+  emits: ['close', 'confirm', 'change', 'monthSwitch', 'change-week'],
+  props: {
+    date: {
+      type: String,
+      default: ''
+    },
+    selected: {
+      type: Array,
+      default() {
+        return []
       }
-		},
-		watch: {
-			date(newVal) {
-				// this.cale.setDate(newVal)
-				this.init(newVal)
-			},
-			startDate(val){
-				this.cale.resetSatrtDate(val)
-				this.cale.setDate(this.nowDate.fullDate)
-				this.weeks = this.cale.weeks
-			},
-			endDate(val){
-				this.cale.resetEndDate(val)
-				this.cale.setDate(this.nowDate.fullDate)
-				this.weeks = this.cale.weeks
-			},
-			selected(newVal) {
-				this.cale.setSelectInfo(this.nowDate.fullDate, newVal)
-				this.weeks = this.cale.weeks
-			}
-		},
-		created() {
-			this.cale = new Calendar({
-				selected: this.selected,
-				startDate: this.startDate,
-				endDate: this.endDate,
-				range: this.range,
-			})
-			this.init(this.date)
-		},
-		methods: {
-			// 取消穿透
-			clean() {},
-			bindDateChange(e) {
-				const value = e.detail.value + '-1'
-				this.setDate(value)
-
-				const { year,month } = this.cale.getDate(value)
-        this.$emit('monthSwitch', {
-            year,
-            month
-        })
-			},
-			/**
-			 * 初始化日期显示
-			 * @param {Object} date
-			 */
-			init(date) {
-				this.cale.setDate(date)
-				this.weeks = this.cale.weeks
-				this.nowDate = this.calendar = this.cale.getInfo(date)
-			},
-			/**
-			 * 打开日历弹窗
-			 */
-			open() {
-				// 弹窗模式并且清理数据
-				if (this.clearDate && !this.insert) {
-					this.cale.cleanMultipleStatus()
-					// this.cale.setDate(this.date)
-					this.init(this.date)
-				}
-				this.show = true
-				this.$nextTick(() => {
-					setTimeout(() => {
-						this.aniMaskShow = true
-					}, 50)
-				})
-			},
-			/**
-			 * 关闭日历弹窗
-			 */
-			close() {
-				this.aniMaskShow = false
-				this.$nextTick(() => {
-					setTimeout(() => {
-						this.show = false
-						this.$emit('close')
-					}, 300)
-				})
-			},
-			/**
-			 * 确认按钮
-			 */
-			confirm() {
-				this.setEmit('confirm')
-				this.close()
-			},
-			/**
-			 * 变化触发
-			 */
-			change() {
-				if (!this.insert) return
-				this.setEmit('change')
-			},
-			/**
-			 * 选择月份触发
-			 */
-			monthSwitch() {
-				let {
-					year,
-					month
-				} = this.nowDate
-				this.$emit('monthSwitch', {
-					year,
-					month: Number(month)
-				})
-			},
-			/**
-			 * 派发事件
-			 * @param {Object} name
-			 */
-			setEmit(name) {
-				let {
-					year,
-					month,
-					date,
-					fullDate,
-					lunar,
-					extraInfo
-				} = this.calendar
-				this.$emit(name, {
-					range: this.cale.multipleStatus,
-					year,
-					month,
-					date,
-					fulldate: fullDate,
-					lunar,
-					extraInfo: extraInfo || {}
-				})
-			},
-			/**
-			 * 选择天触发
-			 * @param {Object} weeks
-			 */
-			choiceDate(weeks) {
-        if (this.readonly) return
-				if (weeks.disable) return
-				this.calendar = weeks
-				// 设置多选
-				this.cale.setMultiple(this.calendar.fullDate)
-				this.weeks = this.cale.weeks
-				this.change()
-			},
-			/**
-			 * 回到今天
-			 */
-			backToday() {
-				const nowYearMonth = `${this.nowDate.year}-${this.nowDate.month}`
-				const date = this.cale.getDate(new Date())
-        const todayYearMonth = `${date.year}-${date.month}`
-
-				this.init(date.fullDate)
-
-        if(nowYearMonth !== todayYearMonth) {
-          this.monthSwitch()
-        }
-
-				this.change()
-        this.$emit('change-month', {
-          date: date.fullDate,
-          month: Number(this.nowDate.month)
-        })
-			},
-			/**
-			 * 上个月
-			 */
-			pre() {
-        if (!this.canPre) return
-				const preDate = this.cale.getDate(this.nowDate.fullDate, -1, 'month').fullDate
-				this.setDate(preDate)
-				this.monthSwitch()
-        this.$emit('change-month', {
-          date: preDate,
-          month: Number(this.nowDate.month)
-        })
-			},
-			/**
-			 * 下个月
-			 */
-			next() {
-        if (!this.canNext) return
-				const nextDate = this.cale.getDate(this.nowDate.fullDate, +1, 'month').fullDate
-				this.setDate(nextDate)
-				this.monthSwitch()
-        this.$emit('change-month', {
-          date: nextDate,
-          month: Number(this.nowDate.month)
+    },
+    lunar: {
+      type: Boolean,
+      default: false
+    },
+    startDate: {
+      type: String,
+      default: ''
+    },
+    endDate: {
+      type: String,
+      default: ''
+    },
+    range: {
+      type: Boolean,
+      default: false
+    },
+    insert: {
+      type: Boolean,
+      default: true
+    },
+    showMonth: {
+      type: Boolean,
+      default: true
+    },
+    clearDate: {
+      type: Boolean,
+      default: true
+    },
+    readonly: {
+      type: Boolean,
+      default: false
+    },
+    // 新增参数:控制周日位置
+    sundayFirst: {
+      type: Boolean,
+      default: false
+    },
+    // 新增参数:控制显示模式
+    displayMode: {
+      type: String,
+      default: 'month', // 'month' | 'week'
+      validator: (value) => ['month', 'week'].includes(value)
+    },
+    // 新增参数:按周显示时的周数
+    weekNumber: {
+      type: Number,
+      default: 1
+    },
+    // 新增参数:控制是否高亮今天
+    highlightToday: {
+      type: Boolean,
+      default: true
+    },
+    // 新增参数:控制是否显示toolbar
+    showToolbar: {
+      type: Boolean,
+      default: true
+    }
+  },
+  data() {
+    return {
+      show: false,
+      weeks: [],
+      calendar: {},
+      nowDate: '',
+      aniMaskShow: false
+    }
+  },
+  computed: {
+    /**
+     * for i18n
+     */
+
+    okText() {
+      return t("uni-calender.ok")
+    },
+    cancelText() {
+      return t("uni-calender.cancel")
+    },
+    todayText() {
+      return t("uni-calender.today")
+    },
+    monText() {
+      return t("uni-calender.MON")
+    },
+    TUEText() {
+      return t("uni-calender.TUE")
+    },
+    WEDText() {
+      return t("uni-calender.WED")
+    },
+    THUText() {
+      return t("uni-calender.THU")
+    },
+    FRIText() {
+      return t("uni-calender.FRI")
+    },
+    SATText() {
+      return t("uni-calender.SAT")
+    },
+    SUNText() {
+      return t("uni-calender.SUN")
+    },
+    canNext() {
+      if (!this.endDate) return true
+      const currentMonth = this.nowDate.month
+      const endMonth = new Date(this.endDate).getMonth() + 1
+      return currentMonth !== endMonth
+    },
+    canPre() {
+      if (!this.startDate) return true
+      const currentMonth = this.nowDate.month
+      const startMonth = new Date(this.startDate).getMonth() + 1
+      return currentMonth !== startMonth
+    },
+    // 新增计算属性:过滤后的周数据
+    filteredWeeks() {
+      if (this.displayMode === 'week') {
+        // 按周显示,显示所有周但只有指定周正常显示,其他周置灰
+        const weekIndex = this.weekNumber - 1
+        const filteredWeeks = {}
+
+        // 遍历weeks对象
+        Object.keys(this.weeks).forEach(key => {
+          const index = parseInt(key)
+          const week = this.weeks[key]
+
+          if (index === weekIndex) {
+            // 指定周正常显示
+            filteredWeeks[key] = week
+          } else {
+            // 其他周置灰显示
+            filteredWeeks[key] = week.map(date => ({
+              ...date,
+              disable: true,
+              isWeekModeDisabled: true // 新增标识,用于样式区分
+            }))
+          }
         })
-			},
-			/**
-			 * 设置日期
-			 * @param {Object} date
-			 */
-			setDate(date) {
-				this.cale.setDate(date)
-				this.weeks = this.cale.weeks
-				this.nowDate = this.cale.getInfo(date)
-			}
-		}
-	}
-</script>
 
-<style lang="scss" scoped>
-	$uni-bg-color-mask: rgba($color: #000000, $alpha: 0.4);
-	$uni-border-color: #EDEDED;
-	$uni-text-color: #333;
-	$uni-bg-color-hover:#f1f1f1;
-	$uni-font-size-base:14px;
-	$uni-text-color-placeholder: #808080;
-	$uni-color-subtitle: #555555;
-	$uni-text-color-grey:#999;
-	.uni-calendar {
-		/* #ifndef APP-NVUE */
-		display: flex;
-		/* #endif */
-		flex-direction: column;
-	}
-
-	.uni-calendar__mask {
-		position: fixed;
-		bottom: 0;
-		top: 0;
-		left: 0;
-		right: 0;
-		background-color: $uni-bg-color-mask;
-		transition-property: opacity;
-		transition-duration: 0.3s;
-		opacity: 0;
-		/* #ifndef APP-NVUE */
-		z-index: 99;
-		/* #endif */
-	}
-
-	.uni-calendar--mask-show {
-		opacity: 1
-	}
-
-	.uni-calendar--fixed {
-		position: fixed;
-		/* #ifdef APP-NVUE */
-		bottom: 0;
-		/* #endif */
-		left: 0;
-		right: 0;
-		transition-property: transform;
-		transition-duration: 0.3s;
-		transform: translateY(460px);
-		/* #ifndef APP-NVUE */
-		bottom: calc(var(--window-bottom));
-		z-index: 99;
-		/* #endif */
-	}
-
-	.uni-calendar--ani-show {
-		transform: translateY(0);
-	}
-
-	.uni-calendar__content {
-		background-color: #fff;
-	}
-
-	.uni-calendar__header {
-		position: relative;
-		/* #ifndef APP-NVUE */
-		display: flex;
-		/* #endif */
-		flex-direction: row;
-		justify-content: center;
-		align-items: center;
-		height: 50px;
-		// border-bottom-color: $uni-border-color;
-		// border-bottom-style: solid;
-		// border-bottom-width: 1px;
-	}
-
-	.uni-calendar--fixed-top {
-		/* #ifndef APP-NVUE */
-		display: flex;
-		/* #endif */
-		flex-direction: row;
-		justify-content: space-between;
-		border-top-color: $uni-border-color;
-		border-top-style: solid;
-		border-top-width: 1px;
-	}
-
-	.uni-calendar--fixed-width {
-		width: 50px;
-	}
-
-	.uni-calendar__backtoday {
-		position: absolute;
-		right: 0;
-		top: 25rpx;
-		padding: 0 5px;
-		padding-left: 10px;
-		height: 25px;
-		line-height: 25px;
-		font-size: 12px;
-		border-top-left-radius: 25px;
-		border-bottom-left-radius: 25px;
-		color: $uni-text-color;
-		background-color: $uni-bg-color-hover;
-	}
-
-	.uni-calendar__header-text {
-    display: block;
-		text-align: center;
-		width: 116px;
-		// font-size: $uni-font-size-base;
-		color: $uni-text-color;
-    font-size: 30rpx; 
-    font-weight: bold;
-	}
-
-	.uni-calendar__header-btn-box {
-		/* #ifndef APP-NVUE */
-		display: flex;
-		/* #endif */
-		flex-direction: row;
-		align-items: center;
-		justify-content: center;
-		width: 50px;
-		height: 50px;
-	}
-
-	.uni-calendar__header-btn {
-		width: 6px;
-		height: 6px;
-		border-left-color: $uni-text-color-placeholder;
-		border-left-style: solid;
-		border-left-width: 2px;
-		border-top-color: $uni-color-subtitle;
-		border-top-style: solid;
-		border-top-width: 2px;
-	}
-
-	.uni-calendar--left {
-		transform: rotate(-45deg);
-	}
-
-	.uni-calendar--right {
-		transform: rotate(135deg);
-	}
-
-
-	.uni-calendar__weeks {
-		position: relative;
-		/* #ifndef APP-NVUE */
-		display: flex;
-		/* #endif */
-		flex-direction: row;
-	}
-
-  .uni-calendar__weeks-head {
-    color: #B3B3B3;
-    font-size: 30rpx;
-  }
+        return filteredWeeks
+      }
+      // 按月显示,返回所有周数据
+      return this.weeks
+    }
+  },
+  watch: {
+    date(newVal) {
+      // this.cale.setDate(newVal)
+      this.init(newVal)
+    },
+    startDate(val) {
+      this.cale.resetSatrtDate(val)
+      this.cale.setDate(this.nowDate.fullDate)
+      this.weeks = this.cale.weeks
+    },
+    endDate(val) {
+      this.cale.resetEndDate(val)
+      this.cale.setDate(this.nowDate.fullDate)
+      this.weeks = this.cale.weeks
+    },
+    selected(newVal) {
+      this.cale.setSelectInfo(this.nowDate.fullDate, newVal)
+      this.weeks = this.cale.weeks
+    },
+    displayMode(newVal) {
+      // 显示模式变化时重新计算weeks
+      this.weeks = this.cale.weeks
+    },
+    weekNumber(newVal) {
+      // 周数变化时重新计算weeks
+      this.weeks = this.cale.weeks
+    }
+  },
+  created() {
+    this.cale = new Calendar({
+      selected: this.selected,
+      startDate: this.startDate,
+      endDate: this.endDate,
+      range: this.range,
+      sundayFirst: this.sundayFirst,
+    })
+    this.init(this.date)
+  },
+  methods: {
+    // 取消穿透
+    clean() { },
+    bindDateChange(e) {
+      const value = e.detail.value + '-1'
+      this.setDate(value)
+
+      const { year, month } = this.cale.getDate(value)
+      this.$emit('monthSwitch', {
+        year,
+        month
+      })
+    },
+    /**
+     * 初始化日期显示
+     * @param {Object} date
+     */
+    init(date) {
+      this.cale.setDate(date)
+      this.weeks = this.cale.weeks
+      this.nowDate = this.calendar = this.cale.getInfo(date)
+    },
+    /**
+     * 打开日历弹窗
+     */
+    open() {
+      // 弹窗模式并且清理数据
+      if (this.clearDate && !this.insert) {
+        this.cale.cleanMultipleStatus()
+        // this.cale.setDate(this.date)
+        this.init(this.date)
+      }
+      this.show = true
+      this.$nextTick(() => {
+        setTimeout(() => {
+          this.aniMaskShow = true
+        }, 50)
+      })
+    },
+    /**
+     * 关闭日历弹窗
+     */
+    close() {
+      this.aniMaskShow = false
+      this.$nextTick(() => {
+        setTimeout(() => {
+          this.show = false
+          this.$emit('close')
+        }, 300)
+      })
+    },
+    /**
+     * 确认按钮
+     */
+    confirm() {
+      this.setEmit('confirm')
+      this.close()
+    },
+    /**
+     * 变化触发
+     */
+    change() {
+      if (!this.insert) return
+      this.setEmit('change')
+    },
+    /**
+     * 选择月份触发
+     */
+    monthSwitch() {
+      let {
+        year,
+        month
+      } = this.nowDate
+      this.$emit('monthSwitch', {
+        year,
+        month: Number(month)
+      })
+    },
+    /**
+     * 派发事件
+     * @param {Object} name
+     */
+    setEmit(name) {
+      let {
+        year,
+        month,
+        date,
+        fullDate,
+        lunar,
+        extraInfo
+      } = this.calendar
+      this.$emit(name, {
+        range: this.cale.multipleStatus,
+        year,
+        month,
+        date,
+        fulldate: fullDate,
+        lunar,
+        extraInfo: extraInfo || {}
+      })
+    },
+    /**
+     * 选择天触发
+     * @param {Object} weeks
+     */
+    choiceDate(weeks) {
+      if (this.readonly) return
+      if (weeks.disable) return
+      this.calendar = weeks
+      // 设置多选
+      this.cale.setMultiple(this.calendar.fullDate)
+      this.weeks = this.cale.weeks
+      this.change()
+    },
+    /**
+     * 回到今天
+     */
+    backToday() {
+      const nowYearMonth = `${this.nowDate.year}-${this.nowDate.month}`
+      const date = this.cale.getDate(new Date())
+      const todayYearMonth = `${date.year}-${date.month}`
+
+      this.init(date.fullDate)
+
+      if (nowYearMonth !== todayYearMonth) {
+        this.monthSwitch()
+      }
 
-  .uni-calendar__weeks + .uni-calendar__weeks {
-    margin-top: 26rpx;
+      this.change()
+      this.$emit('change-month', {
+        date: date.fullDate,
+        month: Number(this.nowDate.month)
+      })
+    },
+    /**
+     * 上个月
+     */
+    pre() {
+      if (!this.canPre) return
+      const preDate = this.cale.getDate(this.nowDate.fullDate, -1, 'month').fullDate
+      this.setDate(preDate)
+      this.monthSwitch()
+      this.$emit('change-month', {
+        date: preDate,
+        month: Number(this.nowDate.month)
+      })
+    },
+    /**
+     * 下个月
+     */
+    next() {
+      if (!this.canNext) return
+      const nextDate = this.cale.getDate(this.nowDate.fullDate, +1, 'month').fullDate
+      this.setDate(nextDate)
+      this.monthSwitch()
+      this.$emit('change-month', {
+        date: nextDate,
+        month: Number(this.nowDate.month)
+      })
+    },
+    /**
+     * 设置日期
+     * @param {Object} date
+     */
+    setDate(date) {
+      this.cale.setDate(date)
+      this.weeks = this.cale.weeks
+      this.nowDate = this.cale.getInfo(date)
+    },
+    /**
+     * 上一周
+     */
+    preWeek() {
+      if (this.displayMode !== 'week') return
+      const totalWeeks = Object.keys(this.weeks).length
+      if (this.weekNumber > 1) {
+        this.weekNumber--
+      } else {
+        // 如果已经是第一周,切换到上个月的最后一周
+        const preDate = this.cale.getDate(this.nowDate.fullDate, -1, 'month').fullDate
+        this.setDate(preDate)
+        this.weekNumber = Object.keys(this.weeks).length
+      }
+      this.$emit('change-week', {
+        weekNumber: this.weekNumber,
+        totalWeeks: Object.keys(this.weeks).length,
+        date: this.nowDate.fullDate
+      })
+    },
+    /**
+     * 下一周
+     */
+    nextWeek() {
+      if (this.displayMode !== 'week') return
+      const totalWeeks = Object.keys(this.weeks).length
+      if (this.weekNumber < totalWeeks) {
+        this.weekNumber++
+      } else {
+        // 如果已经是最后一周,切换到下个月的第一周
+        const nextDate = this.cale.getDate(this.nowDate.fullDate, +1, 'month').fullDate
+        this.setDate(nextDate)
+        this.weekNumber = 1
+      }
+      this.$emit('change-week', {
+        weekNumber: this.weekNumber,
+        totalWeeks: Object.keys(this.weeks).length,
+        date: this.nowDate.fullDate
+      })
+    },
+    /**
+     * 设置周数
+     * @param {Number} weekNumber
+     */
+    setWeekNumber(weekNumber) {
+      if (this.displayMode !== 'week') return
+      const totalWeeks = Object.keys(this.weeks).length
+      if (weekNumber >= 1 && weekNumber <= totalWeeks) {
+        this.weekNumber = weekNumber
+        this.$emit('change-week', {
+          weekNumber: this.weekNumber,
+          totalWeeks: totalWeeks,
+          date: this.nowDate.fullDate
+        })
+      }
+    }
   }
+}
+</script>
 
-	.uni-calendar__weeks-item {
-		flex: 1;
-	}
-
-	.uni-calendar__weeks-day {
-		flex: 1;
-		/* #ifndef APP-NVUE */
-		display: flex;
-		/* #endif */
-		flex-direction: column;
-		justify-content: center;
-		align-items: center;
-		height: 45px;
-		// border-bottom-color: #F5F5F5;
-		// border-bottom-style: solid;
-		// border-bottom-width: 1px;
-	}
-
-	.uni-calendar__weeks-day-text {
-		font-size: 14px;
-	}
-
-	.uni-calendar__box {
-		position: relative;
-    padding: 0 40rpx;
-	}
-
-	.uni-calendar__box-bg {
-		/* #ifndef APP-NVUE */
-		display: flex;
-		/* #endif */
-		justify-content: center;
-		align-items: center;
-		position: absolute;
-		top: 0;
-		left: 0;
-		right: 0;
-		bottom: 0;
-	}
-
-	.uni-calendar__box-bg-text {
-		font-size: 200px;
-		font-weight: bold;
-		color: $uni-text-color-grey;
-		opacity: 0.1;
-		text-align: center;
-		/* #ifndef APP-NVUE */
-		line-height: 1;
-		/* #endif */
-	}
+<style lang="scss" scoped>
+$uni-bg-color-mask: rgba($color: #000000, $alpha: 0.4);
+$uni-border-color: #EDEDED;
+$uni-text-color: #333;
+$uni-bg-color-hover: #f1f1f1;
+$uni-font-size-base: 14px;
+$uni-text-color-placeholder: #808080;
+$uni-color-subtitle: #555555;
+$uni-text-color-grey: #999;
+
+.uni-calendar {
+  /* #ifndef APP-NVUE */
+  display: flex;
+  /* #endif */
+  flex-direction: column;
+}
+
+.uni-calendar__mask {
+  position: fixed;
+  bottom: 0;
+  top: 0;
+  left: 0;
+  right: 0;
+  background-color: $uni-bg-color-mask;
+  transition-property: opacity;
+  transition-duration: 0.3s;
+  opacity: 0;
+  /* #ifndef APP-NVUE */
+  z-index: 99;
+  /* #endif */
+}
+
+.uni-calendar--mask-show {
+  opacity: 1
+}
+
+.uni-calendar--fixed {
+  position: fixed;
+  /* #ifdef APP-NVUE */
+  bottom: 0;
+  /* #endif */
+  left: 0;
+  right: 0;
+  transition-property: transform;
+  transition-duration: 0.3s;
+  transform: translateY(460px);
+  /* #ifndef APP-NVUE */
+  bottom: calc(var(--window-bottom));
+  z-index: 99;
+  /* #endif */
+}
+
+.uni-calendar--ani-show {
+  transform: translateY(0);
+}
+
+.uni-calendar__content {
+  background-color: #fff;
+}
+
+.uni-calendar__header {
+  position: relative;
+  /* #ifndef APP-NVUE */
+  display: flex;
+  /* #endif */
+  flex-direction: row;
+  justify-content: center;
+  align-items: center;
+  height: 50px;
+  // border-bottom-color: $uni-border-color;
+  // border-bottom-style: solid;
+  // border-bottom-width: 1px;
+}
+
+.uni-calendar--fixed-top {
+  /* #ifndef APP-NVUE */
+  display: flex;
+  /* #endif */
+  flex-direction: row;
+  justify-content: space-between;
+  border-top-color: $uni-border-color;
+  border-top-style: solid;
+  border-top-width: 1px;
+}
+
+.uni-calendar--fixed-width {
+  width: 50px;
+}
+
+.uni-calendar__backtoday {
+  position: absolute;
+  right: 0;
+  top: 25rpx;
+  padding: 0 5px;
+  padding-left: 10px;
+  height: 25px;
+  line-height: 25px;
+  font-size: 12px;
+  border-top-left-radius: 25px;
+  border-bottom-left-radius: 25px;
+  color: $uni-text-color;
+  background-color: $uni-bg-color-hover;
+}
+
+.uni-calendar__header-text {
+  display: block;
+  text-align: center;
+  width: 116px;
+  // font-size: $uni-font-size-base;
+  color: $uni-text-color;
+  font-size: 30rpx;
+  font-weight: bold;
+}
+
+.uni-calendar__header-btn-box {
+  /* #ifndef APP-NVUE */
+  display: flex;
+  /* #endif */
+  flex-direction: row;
+  align-items: center;
+  justify-content: center;
+  width: 50px;
+  height: 50px;
+}
+
+.uni-calendar__header-btn {
+  width: 6px;
+  height: 6px;
+  border-left-color: $uni-text-color-placeholder;
+  border-left-style: solid;
+  border-left-width: 2px;
+  border-top-color: $uni-color-subtitle;
+  border-top-style: solid;
+  border-top-width: 2px;
+}
+
+.uni-calendar--left {
+  transform: rotate(-45deg);
+}
+
+.uni-calendar--right {
+  transform: rotate(135deg);
+}
+
+
+.uni-calendar__weeks {
+  position: relative;
+  /* #ifndef APP-NVUE */
+  display: flex;
+  /* #endif */
+  flex-direction: row;
+}
+
+.uni-calendar__weeks-head {
+  color: #B3B3B3;
+  font-size: 30rpx;
+}
+
+.uni-calendar__weeks+.uni-calendar__weeks {
+  margin-top: 26rpx;
+}
+
+.uni-calendar__weeks-item {
+  flex: 1;
+}
+
+.uni-calendar__weeks-day {
+  flex: 1;
+  /* #ifndef APP-NVUE */
+  display: flex;
+  /* #endif */
+  flex-direction: column;
+  justify-content: center;
+  align-items: center;
+  height: 45px;
+  // border-bottom-color: #F5F5F5;
+  // border-bottom-style: solid;
+  // border-bottom-width: 1px;
+}
+
+.uni-calendar__weeks-day-text {
+  font-size: 14px;
+}
+
+.uni-calendar__box {
+  position: relative;
+  padding: 0 40rpx;
+}
+
+.uni-calendar__box-bg {
+  /* #ifndef APP-NVUE */
+  display: flex;
+  /* #endif */
+  justify-content: center;
+  align-items: center;
+  position: absolute;
+  top: 0;
+  left: 0;
+  right: 0;
+  bottom: 0;
+}
+
+.uni-calendar__box-bg-text {
+  font-size: 200px;
+  font-weight: bold;
+  color: $uni-text-color-grey;
+  opacity: 0.1;
+  text-align: center;
+  /* #ifndef APP-NVUE */
+  line-height: 1;
+  /* #endif */
+}
 </style>

+ 413 - 0
src/uni_modules/uni-calendar/components/uni-calendar/usage-examples.vue

@@ -0,0 +1,413 @@
+<template>
+  <view class="calendar-examples">
+    <view class="example-section">
+      <text class="section-title">基础用法</text>
+      <uni-calendar />
+    </view>
+
+    <view class="example-section">
+      <text class="section-title">周日在前</text>
+      <uni-calendar :sunday-first="true" />
+    </view>
+
+    <view class="example-section">
+      <text class="section-title">按周显示(第2周)- 其他周置灰</text>
+      <uni-calendar 
+        ref="weekCalendar"
+        display-mode="week" 
+        :week-number="currentWeek" 
+        @change-week="onWeekChange" />
+      <view class="week-controls">
+        <button @click="preWeek" class="week-btn">上一周</button>
+        <text class="week-info">第{{ currentWeek }}周 / 共{{ totalWeeks }}周</text>
+        <button @click="nextWeek" class="week-btn">下一周</button>
+      </view>
+    </view>
+
+    <view class="example-section">
+      <text class="section-title">不高亮今天</text>
+      <uni-calendar :highlight-today="false" />
+    </view>
+
+    <view class="example-section">
+      <text class="section-title">隐藏工具栏</text>
+      <uni-calendar :show-toolbar="false" />
+    </view>
+
+    <view class="example-section">
+      <text class="section-title">自定义日期样式</text>
+      <uni-calendar>
+        <template #calendar-item="{ weeks, calendar, selected, lunar, highlightToday, weekIndex, weeksIndex }">
+          <view class="custom-date" :class="{
+            'custom-today': weeks.isDay,
+            'custom-selected': calendar.fullDate === weeks.fullDate,
+            'custom-disabled': weeks.disable
+          }">
+            <text class="date-number">{{ weeks.date }}</text>
+            <view v-if="weeks.extraInfo" class="custom-indicator"></view>
+          </view>
+        </template>
+      </uni-calendar>
+    </view>
+
+    <view class="example-section">
+      <text class="section-title">增强插槽 - 自定义整个日期项</text>
+      <uni-calendar>
+        <template #calendar-item="{ weeks, calendar, selected, lunar, highlightToday, weekIndex, weeksIndex }">
+          <view class="enhanced-calendar-item" :class="{
+            'enhanced-today': weeks.isDay,
+            'enhanced-selected': calendar.fullDate === weeks.fullDate,
+            'enhanced-disabled': weeks.disable,
+            'enhanced-week-disabled': weeks.isWeekModeDisabled,
+            'enhanced-not-current-month': !weeks.isCurrentMonth
+          }">
+            <view class="date-content">
+              <text class="date-number">{{ weeks.date }}</text>
+              <text class="date-lunar" v-if="lunar && weeks.lunar">{{ weeks.lunar }}</text>
+            </view>
+            <view class="date-indicators">
+              <view v-if="weeks.extraInfo" class="indicator-dot"></view>
+              <view v-if="weeks.isDay" class="today-badge">今</view>
+              <view v-if="!weeks.isCurrentMonth" class="month-badge">非本月</view>
+            </view>
+          </view>
+        </template>
+      </uni-calendar>
+    </view>
+
+    <view class="example-section">
+      <text class="section-title">组合使用</text>
+      <uni-calendar
+        :sunday-first="true"
+        display-mode="week"
+        :week-number="1"
+        :highlight-today="false"
+        :show-toolbar="false"
+      >
+        <template #calendar-item="{ weeks, calendar, selected, lunar, highlightToday, weekIndex, weeksIndex }">
+          <view class="minimal-date">
+            <text>{{ weeks.date }}</text>
+          </view>
+        </template>
+      </uni-calendar>
+    </view>
+
+    <view class="example-section">
+      <text class="section-title">日期范围API示例</text>
+      <view class="api-demo">
+        <button @click="getMonthRange" class="api-btn">获取本月范围</button>
+        <button @click="getWeekRange" class="api-btn">获取当前周范围</button>
+        <view v-if="monthRange" class="api-result">
+          <text class="api-title">本月范围:</text>
+          <text>{{ monthRange.startDate }} 至 {{ monthRange.endDate }}</text>
+          <text>共 {{ monthRange.totalDays }} 天,{{ monthRange.totalWeeks }} 周</text>
+        </view>
+        <view v-if="weekRange" class="api-result">
+          <text class="api-title">当前周范围:</text>
+          <text>{{ weekRange.startDate }} 至 {{ weekRange.endDate }}</text>
+          <text>共 {{ weekRange.totalDays }} 天 (第{{ weekRange.currentWeek }}周/共{{ weekRange.totalWeeks }}周)</text>
+        </view>
+      </view>
+    </view>
+  </view>
+</template>
+
+<script>
+import Calendar from './util.js'
+
+export default {
+  name: 'CalendarExamples',
+  data() {
+    return {
+      calendar: {},
+      currentWeek: 2,
+      totalWeeks: 6,
+      monthRange: null,
+      weekRange: null,
+      calendarUtil: null
+    }
+  },
+  created() {
+    // 初始化日历工具类
+    this.calendarUtil = new Calendar()
+  },
+  methods: {
+    onWeekChange(event) {
+      this.currentWeek = event.weekNumber
+      this.totalWeeks = event.totalWeeks
+      console.log('周数变化:', event)
+    },
+    preWeek() {
+      if (this.$refs.weekCalendar) {
+        this.$refs.weekCalendar.preWeek()
+      }
+    },
+    nextWeek() {
+      if (this.$refs.weekCalendar) {
+        this.$refs.weekCalendar.nextWeek()
+      }
+    },
+    // 获取本月范围
+    getMonthRange() {
+      try {
+        this.monthRange = this.calendarUtil.getCurrentMonthRange()
+        console.log('本月范围:', this.monthRange)
+      } catch (error) {
+        console.error('获取本月范围失败:', error)
+        uni.showToast({
+          title: '获取失败',
+          icon: 'error'
+        })
+      }
+    },
+    // 获取指定周范围
+    getWeekRange() {
+      try {
+        // 获取当前日期所在的周
+        this.weekRange = this.calendarUtil.getCurrentWeekRange()
+        console.log('当前周范围:', this.weekRange)
+      } catch (error) {
+        console.error('获取周范围失败:', error)
+        uni.showToast({
+          title: '获取失败',
+          icon: 'error'
+        })
+      }
+    }
+  }
+}
+</script>
+
+<style lang="scss" scoped>
+.calendar-examples {
+  padding: 20rpx;
+}
+
+.example-section {
+  margin-bottom: 40rpx;
+  padding: 20rpx;
+  border: 1px solid #eee;
+  border-radius: 8rpx;
+}
+
+.section-title {
+  display: block;
+  font-size: 32rpx;
+  font-weight: bold;
+  margin-bottom: 20rpx;
+  color: #333;
+}
+
+.custom-date {
+  width: 100%;
+  height: 100%;
+  display: flex;
+  flex-direction: column;
+  align-items: center;
+  justify-content: center;
+  border-radius: 50%;
+  position: relative;
+}
+
+.custom-today {
+  background-color: #007aff;
+  color: white;
+}
+
+.custom-selected {
+  background-color: #ff3b30;
+  color: white;
+}
+
+.custom-disabled {
+  opacity: 0.3;
+  color: #ccc;
+}
+
+.date-number {
+  font-size: 28rpx;
+  font-weight: bold;
+}
+
+.custom-indicator {
+  position: absolute;
+  bottom: 4rpx;
+  width: 8rpx;
+  height: 8rpx;
+  background-color: #ff9500;
+  border-radius: 50%;
+}
+
+.minimal-date {
+  width: 100%;
+  height: 100%;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  font-size: 24rpx;
+  color: #666;
+}
+
+.enhanced-calendar-item {
+  width: 100%;
+  height: 100%;
+  display: flex;
+  flex-direction: column;
+  align-items: center;
+  justify-content: center;
+  border-radius: 50%;
+  position: relative;
+  background-color: #F6F8FA;
+  transition: all 0.3s ease;
+}
+
+.enhanced-today {
+  background-color: #007aff;
+  color: white;
+}
+
+.enhanced-selected {
+  background-color: #ff3b30;
+  color: white;
+}
+
+.enhanced-disabled {
+  opacity: 0.3;
+  color: #ccc;
+}
+
+.enhanced-week-disabled {
+  opacity: 0.3;
+  color: #ccc;
+}
+
+.enhanced-not-current-month {
+  opacity: 0.5;
+  color: #999;
+}
+
+.date-content {
+  display: flex;
+  flex-direction: column;
+  align-items: center;
+  justify-content: center;
+}
+
+.date-number {
+  font-size: 28rpx;
+  font-weight: bold;
+  line-height: 1;
+}
+
+.date-lunar {
+  font-size: 20rpx;
+  margin-top: 2rpx;
+  opacity: 0.8;
+}
+
+.date-indicators {
+  position: absolute;
+  top: 4rpx;
+  right: 4rpx;
+  display: flex;
+  flex-direction: column;
+  align-items: center;
+  gap: 2rpx;
+}
+
+.indicator-dot {
+  width: 8rpx;
+  height: 8rpx;
+  background-color: #ff9500;
+  border-radius: 50%;
+}
+
+.today-badge {
+  width: 16rpx;
+  height: 16rpx;
+  background-color: #ff3b30;
+  color: white;
+  border-radius: 50%;
+  font-size: 16rpx;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  line-height: 1;
+}
+
+.month-badge {
+  width: 20rpx;
+  height: 20rpx;
+  background-color: #ff9500;
+  color: white;
+  border-radius: 50%;
+  font-size: 12rpx;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  line-height: 1;
+}
+
+.week-controls {
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  margin-top: 20rpx;
+  gap: 20rpx;
+}
+
+.week-btn {
+  padding: 10rpx 20rpx;
+  background-color: #007aff;
+  color: white;
+  border: none;
+  border-radius: 8rpx;
+  font-size: 24rpx;
+}
+
+.week-info {
+  font-size: 28rpx;
+  color: #333;
+  font-weight: bold;
+}
+
+.api-demo {
+  margin-top: 20rpx;
+  padding: 20rpx;
+  background-color: #f8f9fa;
+  border-radius: 8rpx;
+}
+
+.api-btn {
+  margin: 10rpx;
+  padding: 15rpx 30rpx;
+  background-color: #007aff;
+  color: white;
+  border: none;
+  border-radius: 8rpx;
+  font-size: 24rpx;
+}
+
+.api-result {
+  margin-top: 20rpx;
+  padding: 15rpx;
+  background-color: white;
+  border-radius: 8rpx;
+  border-left: 4rpx solid #007aff;
+}
+
+.api-title {
+  display: block;
+  font-size: 28rpx;
+  font-weight: bold;
+  color: #333;
+  margin-bottom: 10rpx;
+}
+
+.api-result text {
+  display: block;
+  font-size: 24rpx;
+  color: #666;
+  margin-bottom: 5rpx;
+}
+</style>

+ 148 - 4
src/uni_modules/uni-calendar/components/uni-calendar/util.js

@@ -6,7 +6,8 @@ class Calendar {
 		selected,
 		startDate,
 		endDate,
-		range
+		range,
+		sundayFirst = false
 	} = {}) {
 		// 当前日期
 		this.date = this.getDate(new Date()) // 当前初入日期
@@ -17,10 +18,14 @@ class Calendar {
 		// 范围结束
 		this.endDate = endDate
 		this.range = range
+		// 周日位置控制
+		this.sundayFirst = sundayFirst
 		// 多选状态
 		this.cleanMultipleStatus()
 		// 每周日期
 		this.weeks = {}
+    this.week = this.getCurrentWeekRange(new Date())
+    this.month = this.getCurrentMonthRange(new Date())
 		// this._getWeek(this.date.fullDate)
 	}
 	/**
@@ -120,7 +125,8 @@ class Calendar {
 				date: beforeDate,
 				month: full.month - 1,
 				lunar: this.getlunar(full.year, full.month - 1, beforeDate),
-				disable: true
+				disable: true,
+				isCurrentMonth: false // 上月日期为false
 			})
 		}
 		return dateArr
@@ -183,7 +189,8 @@ class Calendar {
 				disable: !(disableBefore && disableAfter),
         disableAfter: !disableAfter,
         disableBefore,
-				isDay
+				isDay,
+				isCurrentMonth: true // 本月内的日期为true
 			}
 			if (info) {
 				data.extraInfo = info
@@ -203,7 +210,8 @@ class Calendar {
 				date: i,
 				month: Number(full.month) + 1,
 				lunar: this.getlunar(full.year, Number(full.month) + 1, i),
-				disable: true
+				disable: true,
+				isCurrentMonth: false // 下月日期为false
 			})
 		}
 		return dateArr
@@ -287,6 +295,133 @@ class Calendar {
 		this._getWeek(data)
 	}
 
+	/**
+	 * 获取本月的日期范围
+	 * @param {String} date 日期字符串,默认为当前日期
+	 * @returns {Object} {startDate, endDate, totalDays, totalWeeks}
+	 */
+	getCurrentMonthRange(date) {
+		if (!date) {
+			date = this.getDate(new Date()).fullDate
+		}
+		const dateInfo = this.getDate(date)
+		const year = dateInfo.year
+		const month = dateInfo.month
+		
+		// 获取本月第一天
+		const startDate = `${year}-${month.toString().padStart(2, '0')}-01`
+		
+		// 获取本月最后一天
+		const lastDay = new Date(year, month, 0).getDate()
+		const endDate = `${year}-${month.toString().padStart(2, '0')}-${lastDay.toString().padStart(2, '0')}`
+		
+		// 计算实际包含本月日期的周数
+		this._getWeek(date)
+		const weeks = this.weeks
+		const weekKeys = Object.keys(weeks)
+		
+		let actualWeeks = 0
+		weekKeys.forEach(key => {
+			const weekData = weeks[key]
+			const hasCurrentMonth = weekData.some(dateItem => dateItem.isCurrentMonth)
+			if (hasCurrentMonth) {
+				actualWeeks++
+			}
+		})
+		
+		return {
+			startDate,
+			endDate,
+			totalDays: lastDay,
+			totalWeeks: actualWeeks
+		}
+	}
+
+	/**
+	 * 获取本月指定周的日期范围
+	 * @param {String} date 日期字符串,默认为当前日期
+	 * @param {Number} weekNumber 周数,从1开始
+	 * @returns {Object} {startDate, endDate, totalDays, current, totalWeeks}
+	 */
+	getCurrentWeekRange(date, weekNumber = null) {
+		if (!date) {
+			date = this.getDate(new Date()).fullDate
+		}
+		
+		// 重新生成当前月份的周数据
+		this._getWeek(date)
+		const weeks = this.weeks
+		const weekKeys = Object.keys(weeks)
+		
+		// 计算实际包含本月日期的周数
+		let actualWeeks = []
+		weekKeys.forEach(key => {
+			const weekData = weeks[key]
+			const hasCurrentMonth = weekData.some(dateItem => dateItem.isCurrentMonth)
+			if (hasCurrentMonth) {
+				actualWeeks.push({
+					originalIndex: parseInt(key),
+					data: weekData
+				})
+			}
+		})
+		
+		const totalWeeks = actualWeeks.length
+		
+		// 如果没有指定周数,自动计算当前日期所在的周
+		if (weekNumber === null) {
+			const targetDate = this.getDate(date).fullDate
+			let foundWeek = null
+			
+			actualWeeks.forEach((week, index) => {
+				const weekData = week.data
+				const hasTargetDate = weekData.some(dateItem => dateItem.fullDate === targetDate)
+				if (hasTargetDate) {
+					foundWeek = index + 1 // 周数从1开始
+				}
+			})
+			
+			if (foundWeek === null) {
+				throw new Error('无法确定指定日期所在的周')
+			}
+			
+			weekNumber = foundWeek
+		}
+		
+		// 验证周数是否有效
+		if (weekNumber < 1 || weekNumber > totalWeeks) {
+			throw new Error(`周数必须在 1-${totalWeeks} 之间`)
+		}
+		
+		const weekIndex = weekNumber - 1
+		const weekData = actualWeeks[weekIndex].data
+		
+		if (!weekData || weekData.length === 0) {
+			throw new Error('指定的周数据不存在')
+		}
+		
+		// 过滤出本月的日期
+		const currentMonthDates = weekData.filter(dateItem => dateItem.isCurrentMonth)
+		
+		if (currentMonthDates.length === 0) {
+			throw new Error('指定周内没有本月的日期')
+		}
+		
+		// 按日期排序
+		currentMonthDates.sort((a, b) => new Date(a.fullDate) - new Date(b.fullDate))
+		
+		const startDate = currentMonthDates[0].fullDate
+		const endDate = currentMonthDates[currentMonthDates.length - 1].fullDate
+		
+		return {
+			startDate,
+			endDate,
+			totalDays: currentMonthDates.length,
+			current: weekNumber,
+			totalWeeks: totalWeeks
+		}
+	}
+
 	/**
 	 *  获取多选状态
 	 */
@@ -326,6 +461,15 @@ class Calendar {
 			month
 		} = this.getDate(dateData)
 		let firstDay = new Date(year, month - 1, 1).getDay()
+		
+		// 根据 sundayFirst 参数调整日期计算
+		if (this.sundayFirst) {
+			// 周日在前,不需要调整
+		} else {
+			// 周日在后,将周日从0调整为6
+			firstDay = firstDay === 0 ? 6 : firstDay - 1
+		}
+		
 		let currentDay = new Date(year, month, 0).getDate()
 		let dates = {
 			lastMonthDays: this._getLastMonthDays(firstDay, this.getDate(dateData)), // 上个月末尾几天