From c34ab2be1d7db8200ffffc3741d57907cdbd25f1 Mon Sep 17 00:00:00 2001 From: ZzCreative <1009486370@qq.com> Date: Wed, 10 Dec 2025 13:47:06 +0800 Subject: [PATCH] feat: complete random wall brushing feature - Add frontend RandomPaintModal component - Integrate random paint button in CalendarControls - Connect frontend with backend GenerateRandomContributions API - Support density, time range, and weekend exclusion parameters --- app.go | 121 ++++++++ frontend/src/components/CalendarControls.tsx | 15 +- .../src/components/ContributionCalendar.tsx | 47 ++++ frontend/src/components/RandomPaintModal.tsx | 265 ++++++++++++++++++ frontend/wailsjs/go/main/App.d.ts | 2 + frontend/wailsjs/go/main/App.js | 4 + frontend/wailsjs/go/models.ts | 60 ++++ 7 files changed, 513 insertions(+), 1 deletion(-) create mode 100644 frontend/src/components/RandomPaintModal.tsx diff --git a/app.go b/app.go index e7a613e..5b70a67 100644 --- a/app.go +++ b/app.go @@ -6,6 +6,7 @@ import ( "encoding/json" "fmt" "io" + "math/rand" "net/http" "os" "os/exec" @@ -57,6 +58,25 @@ type GenerateRepoRequest struct { RemoteRepo *RemoteRepoOptions `json:"remoteRepo,omitempty"` } +// RandomPaintRequest 随机刷墙请求参数 +type RandomPaintRequest struct { + StartDate string `json:"startDate"` // 格式: "2024-01-01" + EndDate string `json:"endDate"` // 格式: "2024-12-31" + Density float64 `json:"density"` // 0.0-1.0,活跃天数比例 + MinPerDay int `json:"minPerDay"` // 每天最少提交数 + MaxPerDay int `json:"maxPerDay"` // 每天最多提交数 + ExcludeWeekend bool `json:"excludeWeekend"` // 是否排除周末 + RandomSeed int64 `json:"randomSeed"` // 随机种子,0表示使用时间戳 +} + +// RandomPaintResponse 随机刷墙响应 +type RandomPaintResponse struct { + Contributions []ContributionDay `json:"contributions"` + TotalDays int `json:"totalDays"` // 总天数 + ActiveDays int `json:"activeDays"` // 活跃天数 + TotalCommits int `json:"totalCommits"` // 总提交数 +} + type GenerateRepoResponse struct { RepoPath string `json:"repoPath"` CommitCount int `json:"commitCount"` @@ -477,6 +497,107 @@ func (a *App) ImportContributions() (*ImportContributionsResponse, error) { return &ImportContributionsResponse{Contributions: contributions}, nil } +// 在 ExportContributions 或 ImportContributions 函数之后添加 + +// GenerateRandomContributions 生成随机贡献数据 +func (a *App) GenerateRandomContributions(req RandomPaintRequest) (*RandomPaintResponse, error) { + // 参数验证 + startDate, err := time.Parse("2006-01-02", req.StartDate) + if err != nil { + return nil, fmt.Errorf("invalid start date: %w", err) + } + + endDate, err := time.Parse("2006-01-02", req.EndDate) + if err != nil { + return nil, fmt.Errorf("invalid end date: %w", err) + } + + if startDate.After(endDate) { + return nil, fmt.Errorf("start date must be before end date") + } + + if req.Density < 0 || req.Density > 1 { + return nil, fmt.Errorf("density must be between 0 and 1") + } + + if req.MinPerDay < 0 { + return nil, fmt.Errorf("minPerDay must be positive") + } + + if req.MaxPerDay < req.MinPerDay { + return nil, fmt.Errorf("maxPerDay must be greater than or equal to minPerDay") + } + + // 初始化随机数生成器 + var seed int64 + if req.RandomSeed == 0 { + seed = time.Now().UnixNano() + } else { + seed = req.RandomSeed + } + rng := rand.New(rand.NewSource(seed)) + + // 生成贡献数据 + contributions := []ContributionDay{} + totalCommits := 0 + activeDays := 0 + + currentDate := startDate + for !currentDate.After(endDate) { + // 检查是否排除周末 + if req.ExcludeWeekend { + weekday := currentDate.Weekday() + if weekday == time.Saturday || weekday == time.Sunday { + currentDate = currentDate.AddDate(0, 0, 1) + continue + } + } + + dateStr := currentDate.Format("2006-01-02") + + // 根据密度决定今天是否有贡献 + if rng.Float64() <= req.Density { + // 生成当天的提交次数 + var commitsToday int + if req.MaxPerDay == req.MinPerDay { + commitsToday = req.MinPerDay + } else { + commitsToday = req.MinPerDay + rng.Intn(req.MaxPerDay-req.MinPerDay+1) + } + + // 确保至少有一次提交 + if commitsToday < 1 { + commitsToday = 1 + } + + contributions = append(contributions, ContributionDay{ + Date: dateStr, + Count: commitsToday, + }) + + totalCommits += commitsToday + activeDays++ + } + + currentDate = currentDate.AddDate(0, 0, 1) + } + + // 按日期排序 + sort.Slice(contributions, func(i, j int) bool { + return contributions[i].Date < contributions[j].Date + }) + + // 计算总天数 + totalDays := int(endDate.Sub(startDate).Hours()/24) + 1 + + return &RandomPaintResponse{ + Contributions: contributions, + TotalDays: totalDays, + ActiveDays: activeDays, + TotalCommits: totalCommits, + }, nil +} + func sanitiseRepoName(input string) string { input = strings.TrimSpace(input) if input == "" { diff --git a/frontend/src/components/CalendarControls.tsx b/frontend/src/components/CalendarControls.tsx index bdf2ce7..83662a9 100644 --- a/frontend/src/components/CalendarControls.tsx +++ b/frontend/src/components/CalendarControls.tsx @@ -30,6 +30,8 @@ type Props = { // 画笔模式 penMode?: 'manual' | 'auto'; onPenModeChange?: (mode: 'manual' | 'auto') => void; + // 新增:随机刷墙回调 + onOpenRandomModal?: () => void; }; export const CalendarControls: React.FC = ({ @@ -56,6 +58,8 @@ export const CalendarControls: React.FC = ({ // 画笔模式 penMode = 'manual', onPenModeChange, + // 新增:随机刷墙回调 + onOpenRandomModal, }) => { const { t } = useTranslations(); const [yearInput, setYearInput] = React.useState(() => @@ -321,7 +325,7 @@ export const CalendarControls: React.FC = ({
-
+
+ {/* 新增:随机刷墙按钮 */} +
diff --git a/frontend/src/components/ContributionCalendar.tsx b/frontend/src/components/ContributionCalendar.tsx index 736f372..778de85 100644 --- a/frontend/src/components/ContributionCalendar.tsx +++ b/frontend/src/components/ContributionCalendar.tsx @@ -3,6 +3,7 @@ import clsx from 'clsx'; import styles from './ContributionCalendar.module.scss'; import { CalendarControls } from './CalendarControls'; import RemoteRepoModal, { RemoteRepoPayload } from './RemoteRepoModal'; +import RandomPaintModal from './RandomPaintModal'; // 新增导入 import { GenerateRepo, ExportContributions, ImportContributions } from '../../wailsjs/go/main/App'; import { main } from '../../wailsjs/go/models'; import { useTranslations } from '../i18n'; @@ -97,6 +98,7 @@ function ContributionCalendar({ const [lastHoveredDate, setLastHoveredDate] = React.useState(null); const [isGeneratingRepo, setIsGeneratingRepo] = React.useState(false); const [isRemoteModalOpen, setIsRemoteModalOpen] = React.useState(false); + const [isRandomModalOpen, setIsRandomModalOpen] = React.useState(false); // 新增:随机刷墙模态框状态 const [isMaximized, setIsMaximized] = React.useState(false); const containerRef = React.useRef(null); const [containerVars, setContainerVars] = React.useState({}); @@ -259,6 +261,41 @@ function ContributionCalendar({ setPastePreviewDates(new Set()); }, []); + // 打开随机刷墙模态框 + const handleOpenRandomModal = React.useCallback(() => { + setIsRandomModalOpen(true); + }, []); + + // 关闭随机刷墙模态框 + const handleCloseRandomModal = React.useCallback(() => { + setIsRandomModalOpen(false); + }, []); + + // 应用随机数据 + const handleApplyRandomContributions = React.useCallback( + (newContributions: { date: string; count: number }[]) => { + console.log('收到随机数据:', newContributions.length); + + if (!newContributions || newContributions.length === 0) { + alert('没有收到随机数据'); + return; + } + + const updatedMap = new Map(userContributions); + + newContributions.forEach((day) => { + if (day.date && day.count > 0) { + updatedMap.set(day.date, day.count); + } + }); + + setUserContributions(updatedMap); + alert('成功应用 ' + newContributions.length + ' 天随机数据'); + setIsRandomModalOpen(false); + }, + [userContributions] + ); + // helper: convert date -> {row, col} const getDateCoord = React.useCallback( (dateStr: string) => { @@ -1121,6 +1158,8 @@ function ContributionCalendar({ return next; }); }} + // 随机刷墙相关 - 新增 + onOpenRandomModal={handleOpenRandomModal} />