Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
121 changes: 121 additions & 0 deletions app.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
"encoding/json"
"fmt"
"io"
"math/rand"
"net/http"
"os"
"os/exec"
Expand Down Expand Up @@ -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"`
Expand Down Expand Up @@ -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 == "" {
Expand Down
15 changes: 14 additions & 1 deletion frontend/src/components/CalendarControls.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@ type Props = {
// 画笔模式
penMode?: 'manual' | 'auto';
onPenModeChange?: (mode: 'manual' | 'auto') => void;
// 新增:随机刷墙回调
onOpenRandomModal?: () => void;
};

export const CalendarControls: React.FC<Props> = ({
Expand All @@ -56,6 +58,8 @@ export const CalendarControls: React.FC<Props> = ({
// 画笔模式
penMode = 'manual',
onPenModeChange,
// 新增:随机刷墙回调
onOpenRandomModal,
}) => {
const { t } = useTranslations();
const [yearInput, setYearInput] = React.useState<string>(() =>
Expand Down Expand Up @@ -321,7 +325,7 @@ export const CalendarControls: React.FC<Props> = ({
</div>

<div className="flex w-full flex-col gap-2 md:flex-1">
<div className="grid grid-cols-1 gap-2 sm:grid-cols-2">
<div className="grid grid-cols-1 gap-2 sm:grid-cols-3">
<button
type="button"
onClick={onExportContributions}
Expand All @@ -338,6 +342,15 @@ export const CalendarControls: React.FC<Props> = ({
>
{t('buttons.import')}
</button>
{/* 新增:随机刷墙按钮 */}
<button
type="button"
onClick={onOpenRandomModal}
className="w-full rounded-none border border-purple-600 bg-purple-600 px-4 py-2 text-sm font-medium text-white transition-colors duration-200 hover:bg-purple-700"
title="随机生成贡献数据"
>
🎲 随机刷墙
</button>
</div>
</div>
</div>
Expand Down
47 changes: 47 additions & 0 deletions frontend/src/components/ContributionCalendar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down Expand Up @@ -97,6 +98,7 @@ function ContributionCalendar({
const [lastHoveredDate, setLastHoveredDate] = React.useState<string | null>(null);
const [isGeneratingRepo, setIsGeneratingRepo] = React.useState<boolean>(false);
const [isRemoteModalOpen, setIsRemoteModalOpen] = React.useState<boolean>(false);
const [isRandomModalOpen, setIsRandomModalOpen] = React.useState<boolean>(false); // 新增:随机刷墙模态框状态
const [isMaximized, setIsMaximized] = React.useState<boolean>(false);
const containerRef = React.useRef<HTMLDivElement | null>(null);
const [containerVars, setContainerVars] = React.useState<ContainerVars>({});
Expand Down Expand Up @@ -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) => {
Expand Down Expand Up @@ -1121,6 +1158,8 @@ function ContributionCalendar({
return next;
});
}}
// 随机刷墙相关 - 新增
onOpenRandomModal={handleOpenRandomModal}
/>
</aside>
<aside className="workbench__panel">
Expand All @@ -1138,6 +1177,14 @@ function ContributionCalendar({
onSubmit={handleRemoteModalSubmit}
/>
)}
{/* 随机刷墙模态框 - 新增 */}
{isRandomModalOpen && (
<RandomPaintModal
open={isRandomModalOpen}
onClose={handleCloseRandomModal}
onApply={handleApplyRandomContributions}
/>
)}
</div>
);
}
Expand Down
Loading