From 10304c4abf04788341f3f95bbce41978e4655048 Mon Sep 17 00:00:00 2001 From: Joywayer Date: Thu, 5 Feb 2026 16:04:09 +0800 Subject: [PATCH] feat: Add Scoreboard and Date Calc pages, update Calculator with scientific mode and new UI --- .../minipro/calculation/miniprogram/app.json | 2 + .../pages/calculator/calculator.ts | 141 +++++++-- .../pages/calculator/calculator.wxml | 84 ++++-- .../pages/calculator/calculator.wxss | 186 +++++++++--- .../pages/date-calc/date-calc.json | 4 + .../miniprogram/pages/date-calc/date-calc.ts | 112 +++++++ .../pages/date-calc/date-calc.wxml | 96 ++++++ .../pages/date-calc/date-calc.wxss | 276 ++++++++++++++++++ .../miniprogram/pages/index/index.ts | 12 + .../miniprogram/pages/index/index.wxml | 22 ++ .../miniprogram/pages/index/index.wxss | 1 + .../pages/scoreboard/scoreboard.json | 6 + .../pages/scoreboard/scoreboard.ts | 186 ++++++++++++ .../pages/scoreboard/scoreboard.wxml | 69 +++++ .../pages/scoreboard/scoreboard.wxss | 273 +++++++++++++++++ 15 files changed, 1390 insertions(+), 80 deletions(-) create mode 100644 codes/minipro/calculation/miniprogram/pages/date-calc/date-calc.json create mode 100644 codes/minipro/calculation/miniprogram/pages/date-calc/date-calc.ts create mode 100644 codes/minipro/calculation/miniprogram/pages/date-calc/date-calc.wxml create mode 100644 codes/minipro/calculation/miniprogram/pages/date-calc/date-calc.wxss create mode 100644 codes/minipro/calculation/miniprogram/pages/scoreboard/scoreboard.json create mode 100644 codes/minipro/calculation/miniprogram/pages/scoreboard/scoreboard.ts create mode 100644 codes/minipro/calculation/miniprogram/pages/scoreboard/scoreboard.wxml create mode 100644 codes/minipro/calculation/miniprogram/pages/scoreboard/scoreboard.wxss diff --git a/codes/minipro/calculation/miniprogram/app.json b/codes/minipro/calculation/miniprogram/app.json index b6629ec..59d4701 100644 --- a/codes/minipro/calculation/miniprogram/app.json +++ b/codes/minipro/calculation/miniprogram/app.json @@ -5,6 +5,8 @@ "pages/calculator/calculator", "pages/unit-converter/unit-converter", "pages/random-draw/random-draw", + "pages/scoreboard/scoreboard", + "pages/date-calc/date-calc", "pages/logs/logs", "pages/webview/webview" ], diff --git a/codes/minipro/calculation/miniprogram/pages/calculator/calculator.ts b/codes/minipro/calculation/miniprogram/pages/calculator/calculator.ts index bee69ef..c2a53f1 100644 --- a/codes/minipro/calculation/miniprogram/pages/calculator/calculator.ts +++ b/codes/minipro/calculation/miniprogram/pages/calculator/calculator.ts @@ -6,15 +6,34 @@ Page({ operator: null as string | null, firstOperand: null as number | null, waitingForSecondOperand: false, + isScientific: false }, onLoad() { }, + vibrate() { + // wx.vibrateShort({ type: 'light' }); + }, + + toggleMode() { + this.vibrate(); + this.setData({ + isScientific: !this.data.isScientific + }); + }, + onDigit(e: any) { + this.vibrate(); const digit = e.currentTarget.dataset.digit; const { displayValue, waitingForSecondOperand } = this.data; + // 科学模式允许更多数字 + const limit = 15; + if (!waitingForSecondOperand && displayValue.length >= limit) { + return; + } + if (waitingForSecondOperand) { this.setData({ displayValue: digit, @@ -28,6 +47,7 @@ Page({ }, onDot() { + this.vibrate(); const { displayValue, waitingForSecondOperand } = this.data; if (waitingForSecondOperand) { this.setData({ @@ -42,6 +62,7 @@ Page({ }, onClear() { + this.vibrate(); this.setData({ displayValue: '0', history: '', @@ -52,13 +73,94 @@ Page({ }, onDelete() { + this.vibrate(); const { displayValue } = this.data; + if (displayValue.length > 1) { + this.setData({ + displayValue: displayValue.slice(0, -1) + }); + } else { + this.setData({ displayValue: '0' }); + } + }, + + onToggleSign() { + this.vibrate(); + const { displayValue } = this.data; + const newValue = parseFloat(displayValue) * -1; + this.setData({ + displayValue: String(newValue) + }); + }, + + onConstant(e: any) { + this.vibrate(); + const type = e.currentTarget.dataset.const; + let val = 0; + if (type === 'pi') val = Math.PI; + if (type === 'e') val = Math.E; + this.setData({ - displayValue: displayValue.length > 1 ? displayValue.slice(0, -1) : '0' + displayValue: String(val).slice(0, 10), + waitingForSecondOperand: true // 下次输入将开启新数字,或者作为操作数 + }); + }, + + onMathFunc(e: any) { // 单操作数立即计算 + this.vibrate(); + const func = e.currentTarget.dataset.func; + let val = parseFloat(this.data.displayValue); + let result = 0; + let desc = ''; + + switch(func) { + case 'sin': + // 默认转为弧度计算 (度数 * PI / 180) + result = Math.sin(val * Math.PI / 180); + desc = `sin(${val})`; + break; + case 'cos': + result = Math.cos(val * Math.PI / 180); + desc = `cos(${val})`; + break; + case 'tan': + result = Math.tan(val * Math.PI / 180); + desc = `tan(${val})`; + break; + case 'log': + result = Math.log10(val); + desc = `log(${val})`; + break; + case 'ln': + result = Math.log(val); + desc = `ln(${val})`; + break; + case 'sqrt': + result = Math.sqrt(val); + desc = `√(${val})`; + break; + case 'pow2': + result = Math.pow(val, 2); + desc = `sqr(${val})`; + break; + case 'percent': + result = val / 100; + desc = `${val}%`; + break; + } + + // 格式化结果 + const formatted = parseFloat(result.toPrecision(12)); + + this.setData({ + displayValue: String(formatted), + history: desc, + waitingForSecondOperand: true // 计算完后,下次输入数字是新数字 }); }, onOperator(e: any) { + this.vibrate(); const nextOperator = e.currentTarget.dataset.op; const { displayValue, operator, firstOperand } = this.data; const inputValue = parseFloat(displayValue); @@ -66,7 +168,8 @@ Page({ if (operator && this.data.waitingForSecondOperand) { this.setData({ operator: nextOperator, - history: `${firstOperand} ${nextOperator}` + // 更新历史记录中的符号 + history: `${firstOperand} ${nextOperator}` }); return; } @@ -79,7 +182,7 @@ Page({ const result = this.performCalculation(operator, firstOperand, inputValue); newFirstOperand = result; this.setData({ - displayValue: String(result), + displayValue: String(result), }); } @@ -92,35 +195,33 @@ Page({ }, onEqual() { + this.vibrate(); const { displayValue, operator, firstOperand } = this.data; const inputValue = parseFloat(displayValue); if (operator && firstOperand != null) { const result = this.performCalculation(operator, firstOperand, inputValue); + this.setData({ displayValue: String(result), firstOperand: null, operator: null, waitingForSecondOperand: true, - history: '' + history: `${firstOperand} ${operator} ${inputValue} =` }); } }, - performCalculation(operator: string, firstOperand: number, secondOperand: number) { - switch (operator) { - case '+': - return firstOperand + secondOperand; - case '-': - return firstOperand - secondOperand; - case '*': - return firstOperand * secondOperand; - case '/': - return firstOperand / secondOperand; - case '%': - return firstOperand % secondOperand; - default: - return secondOperand; - } + performCalculation(op: string, first: number, second: number): number { + let result = 0; + switch(op) { + case '+': result = first + second; break; + case '-': result = first - second; break; + case '×': result = first * second; break; + case '÷': result = first / second; break; + default: return second; + } + const precision = 10000000000; + return Math.round(result * precision) / precision; } -}); +}) diff --git a/codes/minipro/calculation/miniprogram/pages/calculator/calculator.wxml b/codes/minipro/calculation/miniprogram/pages/calculator/calculator.wxml index ca1745c..0f65aaa 100644 --- a/codes/minipro/calculation/miniprogram/pages/calculator/calculator.wxml +++ b/codes/minipro/calculation/miniprogram/pages/calculator/calculator.wxml @@ -1,32 +1,68 @@ - - - {{history}} - {{displayValue}} + + + + + ⚗️ 科学模式 + + {{history}} + {{displayValue}} - - C - DEL - % - ÷ + + + - 7 - 8 - 9 - × + + + + sin + cos + tan + log + ln + + + π + e + + + % + + + + + + AC + + +/- + ÷ + - 4 - 5 - 6 - - + + 7 + 8 + 9 + × + - 1 - 2 - 3 - + + + 4 + 5 + 6 + + - 0 - . - = + + 1 + 2 + 3 + + + + + + 0 + . + = + diff --git a/codes/minipro/calculation/miniprogram/pages/calculator/calculator.wxss b/codes/minipro/calculation/miniprogram/pages/calculator/calculator.wxss index 3f54aae..ca3470b 100644 --- a/codes/minipro/calculation/miniprogram/pages/calculator/calculator.wxss +++ b/codes/minipro/calculation/miniprogram/pages/calculator/calculator.wxss @@ -1,74 +1,188 @@ /* pages/calculator/calculator.wxss */ page { height: 100%; - background-color: #f5f5f5; + background-color: #000000; + display: flex; + flex-direction: column; } -.calculator { +.calculator-container { display: flex; flex-direction: column; height: 100%; + padding-bottom: constant(safe-area-inset-bottom); + padding-bottom: env(safe-area-inset-bottom); } -.screen { +/* 显示区域 */ +.display-area { flex: 1; - background-color: #333; - color: white; display: flex; flex-direction: column; justify-content: flex-end; align-items: flex-end; - padding: 20rpx; + padding: 40rpx; + background-color: #000; + position: relative; +} + +.mode-indicator { + position: absolute; + top: 40rpx; + left: 40rpx; +} + +.mode-indicator text { + color: #444; + font-size: 24rpx; + border: 1px solid #444; + padding: 10rpx 20rpx; + border-radius: 30rpx; +} + +.mode-indicator text.active { + color: #ff9f0a; + border-color: #ff9f0a; + background: rgba(255, 159, 10, 0.1); +} + + +.history-text { + font-size: 36rpx; + color: #666; + min-height: 50rpx; + margin-bottom: 20rpx; + font-family: monospace; + text-align: right; + width: 100%; +} + +.current-value { + font-size: 160rpx; + color: #fff; + line-height: 1; + font-weight: 200; word-break: break-all; + text-align: right; + width: 100%; } -.history { - font-size: 40rpx; - color: #aaa; - margin-bottom: 10rpx; +.current-value.shrink { + font-size: 100rpx; +} +.current-value.shrink-more { + font-size: 70rpx; } -.result { - font-size: 80rpx; - font-weight: bold; +/* 键盘区域 */ +.keypad-area { + background-color: #000; + padding: 0 30rpx 30rpx 30rpx; + display: flex; + flex-direction: column; + gap: 30rpx; } -.keypad { - background-color: #fff; - display: grid; - grid-template-columns: repeat(4, 1fr); - grid-gap: 1px; - background-color: #ccc; /* Gap color */ +/* 科学模式适配 */ +.keypad-area.scientific-pad { + gap: 20rpx; + padding-bottom: 20rpx; } +/* 统一圆形按钮 */ +.keypad-area.scientific-pad .btn { + width: 130rpx; + height: 130rpx; + font-size: 50rpx; + border-radius: 50%; /* 保持圆形 */ +} + +.keypad-area.scientific-pad .zero { + width: 280rpx; /* 适配新的宽度 */ + border-radius: 65rpx; /* 圆角为高度一半 */ +} + +/* 科学功能键行 */ +.keypad-area.scientific-pad .sci-btn { + width: 120rpx; /* 稍微大一点,填满宽度 */ + height: 120rpx; /* 正圆 */ + font-size: 36rpx; + background: #222; + color: #fff; + border-radius: 50%; +} + +.small-row { + justify-content: space-around; /* 均匀分布 */ + gap: 10rpx; +} + +.row { + display: flex; + justify-content: space-between; + gap: 30rpx; +} +.keypad-area.scientific-pad .row { + gap: 20rpx; +} + + .btn { - background-color: #fff; + width: 150rpx; height: 150rpx; + border-radius: 75rpx; /* 圆形按钮 */ display: flex; justify-content: center; align-items: center; - font-size: 40rpx; - active-color: #eee; + font-size: 60rpx; + font-weight: 500; } -.btn:active { - background-color: #eee; +/* 数字键 */ +.digit { + background-color: #333333; + color: #fff; } +/* 功能键 (AC, Del, +/-) */ +.func { + background-color: #a5a5a5; + color: #000; + font-size: 50rpx; + font-weight: 600; +} + +/* 运算符 */ .operator { - color: #ff9500; - font-weight: bold; -} - -.equal { - background-color: #ff9500; - color: white; -} - -.equal:active { - background-color: #e08900; + background-color: #ff9f0a; + color: #fff; + font-size: 70rpx; + padding-bottom: 10rpx; + padding: 0; } +/* 0键特殊处理 */ .zero { - grid-column: span 2; + width: 330rpx; + border-radius: 75rpx; + justify-content: flex-start; + padding-left: 60rpx; + box-sizing: border-box; +} + +/* 点击反馈 */ +.btn-hover { + opacity: 0.7; +} + +/* 平板适配或小屏适配 */ +@media (max-width: 360px) { + .btn { + width: 140rpx; + height: 140rpx; + font-size: 50rpx; + } + .zero { + width: 310rpx; + } } diff --git a/codes/minipro/calculation/miniprogram/pages/date-calc/date-calc.json b/codes/minipro/calculation/miniprogram/pages/date-calc/date-calc.json new file mode 100644 index 0000000..1e5e7b9 --- /dev/null +++ b/codes/minipro/calculation/miniprogram/pages/date-calc/date-calc.json @@ -0,0 +1,4 @@ +{ + "usingComponents": {}, + "navigationBarTitleText": "日期计算器" +} diff --git a/codes/minipro/calculation/miniprogram/pages/date-calc/date-calc.ts b/codes/minipro/calculation/miniprogram/pages/date-calc/date-calc.ts new file mode 100644 index 0000000..37294ed --- /dev/null +++ b/codes/minipro/calculation/miniprogram/pages/date-calc/date-calc.ts @@ -0,0 +1,112 @@ +// pages/date-calc/date-calc.ts +Page({ + data: { + currentTab: 0, + startDate: '', + startWeekday: '', // 新增:开始日期的星期 + endDate: '', + days: 0, // 可以为空字符串以便清空输入框,但 input type number 会处理 + resultDate: '', + resultWeekday: '', + intervalDays: 0, + intervalWeeks: 0, + intervalRemainingDays: 0 + }, + + onLoad() { + const today = new Date(); + const dateStr = this.formatDate(today); + this.setData({ + startDate: dateStr, + endDate: dateStr, + resultDate: dateStr, + resultWeekday: this.getWeekday(today), + startWeekday: this.getWeekday(today) + }); + }, + + switchTab(e: any) { + this.setData({ + currentTab: parseFloat(e.currentTarget.dataset.index) + }); + this.calculate(); // 切换时重新计算 + }, + + bindDateChange(e: any) { + const field = e.currentTarget.dataset.field; + this.setData({ + [field]: e.detail.value + }); + + // 如果修改的是开始日期,同步更新星期显示 + if (field === 'startDate') { + const d = new Date(e.detail.value); + this.setData({ startWeekday: this.getWeekday(d) }); + } + + this.calculate(); + }, + + bindDaysInput(e: any) { + const val = e.detail.value; + // 允许输入负号 + if (val === '-' || val === '') { + this.setData({ days: val }); + return; + } + this.setData({ + days: parseInt(val) + }); + this.calculate(); + }, + + setDays(e: any) { + const days = parseInt(e.currentTarget.dataset.days); + this.setData({ days: days }); + this.calculate(); + }, + + calculate() { + if (this.data.currentTab === 0) { + // 日期推算 + // 注意:直接使用 new Date('2023-10-01') 在 iOS 上可能不兼容,应替换为 '/' + const start = new Date(this.data.startDate.replace(/-/g, '/')); + // days 可能是字符串或数字 + const dayOffset = typeof(this.data.days) === 'number' ? this.data.days : parseInt(this.data.days || '0'); + + const target = new Date(start.getTime() + dayOffset * 24 * 60 * 60 * 1000); + + this.setData({ + resultDate: this.formatDate(target), + resultWeekday: this.getWeekday(target) + }); + } else { + // 日期通过 (Interval) + const start = new Date(this.data.startDate.replace(/-/g, '/')); + const end = new Date(this.data.endDate.replace(/-/g, '/')); + const diffTime = Math.abs(end.getTime() - start.getTime()); + const diffDays = Math.ceil(diffTime / (1000 * 60 * 60 * 24)); + + const weeks = Math.floor(diffDays / 7); + const remainingDays = diffDays % 7; + + this.setData({ + intervalDays: diffDays, + intervalWeeks: weeks, + intervalRemainingDays: remainingDays + }); + } + }, + + formatDate(date: Date): string { + const y = date.getFullYear(); + const m = (date.getMonth() + 1).toString().padStart(2, '0'); + const d = date.getDate().toString().padStart(2, '0'); + return `${y}-${m}-${d}`; + }, + + getWeekday(date: Date): string { + const days = ['周日', '周一', '周二', '周三', '周四', '周五', '周六']; + return days[date.getDay()]; + } +}) diff --git a/codes/minipro/calculation/miniprogram/pages/date-calc/date-calc.wxml b/codes/minipro/calculation/miniprogram/pages/date-calc/date-calc.wxml new file mode 100644 index 0000000..40283ac --- /dev/null +++ b/codes/minipro/calculation/miniprogram/pages/date-calc/date-calc.wxml @@ -0,0 +1,96 @@ + + + + + + + 📅 日期推算 + + + 间隔计算 + + + + + + + + + + 开始日期 + + + {{startDate || '请选择'}} + {{startWeekday}} + + + + + + + 推算天数 + + + 天后 + + + + + +1周 + +1月 + +百日 + -1天 + + + + + 计算结果 + {{resultDate}} + {{resultWeekday}} + + + + + + + + + 开始 + + + {{startDate}} + + + + + + + 相隔 + + + + + + 结束 + + + {{endDate}} + + + + + + + 时间跨度 + + {{intervalDays}} + + + + 约合 {{intervalWeeks}} 周 {{intervalRemainingDays > 0 ? '+ ' + intervalRemainingDays + ' 天' : ''}} + + + + + + diff --git a/codes/minipro/calculation/miniprogram/pages/date-calc/date-calc.wxss b/codes/minipro/calculation/miniprogram/pages/date-calc/date-calc.wxss new file mode 100644 index 0000000..172a146 --- /dev/null +++ b/codes/minipro/calculation/miniprogram/pages/date-calc/date-calc.wxss @@ -0,0 +1,276 @@ +/* pages/date-calc/date-calc.wxss */ +page { + background-color: #f0f2f5; + color: #333; + --primary-color: #5b73e8; + --primary-light: #e0e6fc; +} + +.container { + padding: 30rpx; + min-height: 100vh; + box-sizing: border-box; +} + +/* Tab 切换 */ +.tab-header { + display: flex; + background: #ffffff; + padding: 8rpx; + border-radius: 20rpx; + margin-bottom: 30rpx; + box-shadow: 0 4rpx 12rpx rgba(0,0,0,0.03); +} + +.tab-pill { + flex: 1; + text-align: center; + padding: 20rpx 0; + border-radius: 16rpx; + font-size: 28rpx; + color: #666; + font-weight: 500; + transition: all 0.3s ease; + display: flex; + align-items: center; + justify-content: center; + gap: 10rpx; + white-space: nowrap; +} + +.tab-pill.active { + background-color: var(--primary-light); + color: var(--primary-color); + font-weight: bold; +} + +.tab-icon { + font-size: 32rpx; +} + +/* 卡片通用样式 */ +.card { + background: #ffffff; + border-radius: 24rpx; + padding: 40rpx; + margin-bottom: 30rpx; + box-shadow: 0 4rpx 20rpx rgba(0,0,0,0.04); +} + +.card-title { + font-size: 24rpx; + color: #999; + margin-bottom: 20rpx; + text-transform: uppercase; + letter-spacing: 1rpx; +} + +/* 模式1:日期推算 */ +.date-display { + display: flex; + align-items: baseline; + padding-bottom: 20rpx; +} + +.date-text { + font-size: 48rpx; + font-weight: bold; + color: #333; + margin-right: 20rpx; +} + +.date-week { + font-size: 28rpx; + color: #666; +} + +.edit-icon { + margin-left: auto; + color: var(--primary-color); + font-size: 36rpx; + opacity: 0.8; +} + +.divider { + height: 1px; + background: #f0f0f0; + margin: 30rpx 0; +} + +.input-row { + display: flex; + align-items: center; + border-bottom: 2px solid var(--primary-light); + padding-bottom: 10rpx; + margin-bottom: 30rpx; +} + +.days-input { + flex: 1; + font-size: 56rpx; + font-weight: bold; + color: var(--primary-color); + height: 80rpx; +} + +.input-unit { + font-size: 32rpx; + color: #999; +} + +.tags-row { + display: flex; + gap: 20rpx; +} + +.tag { + background: #f5f7fa; + color: #666; + padding: 12rpx 24rpx; + border-radius: 30rpx; + font-size: 24rpx; + font-weight: 500; +} + +.tag-red { + background: #fff0f0; + color: #ff4d4f; +} + +/* 结果卡片 */ +.result-card { + background: var(--primary-color); /* 渐变色更好吗?先用纯色突出 */ + background: linear-gradient(135deg, #5b73e8 0%, #455ac0 100%); + color: #fff; + text-align: center; + padding: 50rpx; +} + +.result-header { + font-size: 24rpx; + opacity: 0.8; + margin-bottom: 20rpx; +} + +.result-date { + font-size: 64rpx; + font-weight: bold; + margin-bottom: 10rpx; +} + +.result-week { + font-size: 32rpx; + opacity: 0.9; +} + +/* 模式2:日期间隔 */ +.date-row { + display: flex; + justify-content: space-between; + align-items: center; +} + +.row-label { + font-size: 30rpx; + color: #666; + font-weight: bold; +} + +.date-pill { + background: #f5f7fa; + padding: 16rpx 30rpx; + border-radius: 12rpx; + font-size: 32rpx; + color: #333; + font-weight: 500; + display: flex; + align-items: center; + gap: 10rpx; +} + +.pill-icon { + font-size: 20rpx; + color: #999; +} + +.timeline-connect { + display: flex; + align-items: center; /* 居中对齐 */ + margin: 30rpx 0; + padding-left: 20rpx; /* 微调对齐 */ +} + +.dots { + width: 2px; + height: 40rpx; + border-left: 2px dashed #ddd; /* 使用左边框模拟竖线 */ + margin: 0 auto; /* 水平居中 */ +} + +/* 实际上我想做竖向的时间轴,但在 input-card 里横向可能更好看,但这设计是竖向列表? + 重新设计 timeline 为竖向的连接线 +*/ +.input-card { + position: relative; +} + +/* 覆盖上面的 timeline-connect 为竖向视觉连接 */ +.timeline-connect { + display: flex; + flex-direction: column; /* 竖向 */ + align-items: flex-start; /* 靠左对齐,或者根据布局 */ + margin: 30rpx 0; + padding-left: 0; + position: relative; + height: 60rpx; /* 高度 */ + justify-content: center; +} + +.dots { + /* 复用为无形占位或改用伪元素连线 */ + display: none; +} + +.timeline-connect::before { + content: ''; + position: absolute; + left: 40rpx; /* 连接线的位置,约等于 row-label 的中心 */ + top: -20rpx; + bottom: -20rpx; + width: 2rpx; + background: #e0e0e0; + z-index: 0; +} + +.duration-badge { + position: relative; + z-index: 1; + background: #e6f7ff; + color: #1890ff; + font-size: 22rpx; + padding: 6rpx 20rpx; + border-radius: 20rpx; + margin-left: 20rpx; /* 避开左侧文字 */ + align-self: center; /* 居中 */ +} + +/* 间隔结果 */ +.interval-result .result-huge { + font-size: 80rpx; + font-weight: bold; + margin-bottom: 10rpx; +} + +.interval-result .unit { + font-size: 32rpx; + font-weight: normal; + margin-left: 10rpx; +} + +.interval-result .result-sub { + font-size: 28rpx; + opacity: 0.9; + border-top: 1px solid rgba(255,255,255,0.2); + padding-top: 20rpx; + display: inline-block; +} diff --git a/codes/minipro/calculation/miniprogram/pages/index/index.ts b/codes/minipro/calculation/miniprogram/pages/index/index.ts index 2024a3a..cc1e836 100644 --- a/codes/minipro/calculation/miniprogram/pages/index/index.ts +++ b/codes/minipro/calculation/miniprogram/pages/index/index.ts @@ -68,6 +68,18 @@ Component({ wx.navigateTo({ url: '/pages/random-draw/random-draw' }); + }, + + goToScoreboard() { + wx.navigateTo({ + url: '/pages/scoreboard/scoreboard' + }); + }, + + goToDateCalc() { + wx.navigateTo({ + url: '/pages/date-calc/date-calc' + }); } } }) diff --git a/codes/minipro/calculation/miniprogram/pages/index/index.wxml b/codes/minipro/calculation/miniprogram/pages/index/index.wxml index 63e0bb9..c3b8ebe 100644 --- a/codes/minipro/calculation/miniprogram/pages/index/index.wxml +++ b/codes/minipro/calculation/miniprogram/pages/index/index.wxml @@ -53,6 +53,28 @@ 随机抽取幸运儿 + + + + + 📝 + + + 万能记分板 + 桌游/比赛记分 + + + + + + + 📅 + + + 日期计算 + 天数推算 + + diff --git a/codes/minipro/calculation/miniprogram/pages/index/index.wxss b/codes/minipro/calculation/miniprogram/pages/index/index.wxss index 8be212a..cfd87de 100644 --- a/codes/minipro/calculation/miniprogram/pages/index/index.wxss +++ b/codes/minipro/calculation/miniprogram/pages/index/index.wxss @@ -140,6 +140,7 @@ page { .bg-gradient-green { background: #e8f5e9; color: #4caf50; } .bg-gradient-orange { background: #fff3e0; color: #ff9800; } .bg-gradient-purple { background: #f3e5f5; color: #9c27b0; } +.bg-gradient-pink { background: #ffebee; color: #e91e63; } .text-box { display: flex; diff --git a/codes/minipro/calculation/miniprogram/pages/scoreboard/scoreboard.json b/codes/minipro/calculation/miniprogram/pages/scoreboard/scoreboard.json new file mode 100644 index 0000000..9d6608e --- /dev/null +++ b/codes/minipro/calculation/miniprogram/pages/scoreboard/scoreboard.json @@ -0,0 +1,6 @@ +{ + "usingComponents": {}, + "navigationBarTitleText": "比赛计分板", + "navigationBarBackgroundColor": "#1a1b1f", + "navigationBarTextStyle": "white" +} diff --git a/codes/minipro/calculation/miniprogram/pages/scoreboard/scoreboard.ts b/codes/minipro/calculation/miniprogram/pages/scoreboard/scoreboard.ts new file mode 100644 index 0000000..90e3601 --- /dev/null +++ b/codes/minipro/calculation/miniprogram/pages/scoreboard/scoreboard.ts @@ -0,0 +1,186 @@ +// pages/scoreboard/scoreboard.ts +Page({ + data: { + // 队伍数据 + homeName: '主队', + guestName: '客队', + homeScore: 0, + guestScore: 0, + + // 动画状态 + homeScoreAnim: false, + guestScoreAnim: false, + + // 计时器数据 + period: 1, + timeLeft: 12 * 60, // 默认12分钟 (秒) + timeStr: '12:00', + isTimerRunning: false, + timerId: null as number | null, + + // 配置 + quarterLength: 12 // 节长(分钟) + }, + + onLoad() { + this.updateTimeStr(); + }, + + onUnload() { + this.stopTimer(); + }, + + // === 分数控制 === + + addScore(e: any) { + const { team, val } = e.currentTarget.dataset; + const value = parseInt(val); + + if (team === 'home') { + let newScore = this.data.homeScore + value; + if (newScore < 0) newScore = 0; + + this.setData({ + homeScore: newScore, + homeScoreAnim: true // 触发动画 + }); + // 动画自动复位 + setTimeout(() => this.setData({ homeScoreAnim: false }), 300); + + } else { + let newScore = this.data.guestScore + value; + if (newScore < 0) newScore = 0; + + this.setData({ + guestScore: newScore, + guestScoreAnim: true + }); + setTimeout(() => this.setData({ guestScoreAnim: false }), 300); + } + + // 震动反馈 (提升手感) + if (value > 0) { + wx.vibrateShort({ type: 'light' }); + } + }, + + onTeamNameChange(e: any) { + const { team } = e.currentTarget.dataset; + const name = e.detail.value; + if (team === 'home') { + this.setData({ homeName: name }); + } else { + this.setData({ guestName: name }); + } + }, + + exchangeTeams() { + // 交换分数和名字 + this.setData({ + homeName: this.data.guestName, + guestName: this.data.homeName, + homeScore: this.data.guestScore, + guestScore: this.data.homeScore + }); + wx.showToast({ title: '场地已交换', icon: 'none' }); + }, + + resetGame() { + wx.showModal({ + title: '重置比赛', + content: '确定要清空比分并重置时间吗?', + success: (res) => { + if (res.confirm) { + this.stopTimer(); + this.setData({ + homeScore: 0, + guestScore: 0, + period: 1, + timeLeft: this.data.quarterLength * 60, + isTimerRunning: false + }); + this.updateTimeStr(); + } + } + }); + }, + + // === 计时器逻辑 === + + toggleTimer() { + if (this.data.isTimerRunning) { + this.stopTimer(); + } else { + this.startTimer(); + } + // 触感反馈 + wx.vibrateShort({ type: 'medium' }); + }, + + startTimer() { + if (this.data.timeLeft <= 0) return; + + this.setData({ isTimerRunning: true }); + + this.data.timerId = setInterval(() => { + if (this.data.timeLeft > 0) { + this.data.timeLeft--; + this.updateTimeStr(); + } else { + this.stopTimer(); + wx.vibrateLong(); // 时间到震动提示 + wx.showToast({ title: '本节结束', icon: 'none' }); + } + }, 1000); + }, + + stopTimer() { + if (this.data.timerId) { + clearInterval(this.data.timerId); + this.data.timerId = null; + } + this.setData({ isTimerRunning: false }); + }, + + resetTimer() { + // 如果正在运行,先暂停 + if (this.data.isTimerRunning) { + this.stopTimer(); + } + + // 弹出选项:重置当前节还是自定义 + wx.showActionSheet({ + itemList: ['重置回 12:00', '重置回 10:00', '重置回 20:00'], + success: (res) => { + let minutes = 12; + if (res.tapIndex === 1) minutes = 10; + if (res.tapIndex === 2) minutes = 20; + + this.setData({ + timeLeft: minutes * 60, + quarterLength: minutes + }); + this.updateTimeStr(); + } + }); + }, + + changePeriod() { + const nextPeriod = this.data.period >= 4 ? 1 : this.data.period + 1; + // 自动重置时间 + this.stopTimer(); + this.setData({ + period: nextPeriod, + timeLeft: this.data.quarterLength * 60 + }); + this.updateTimeStr(); + wx.showToast({ title: `第 ${nextPeriod} 节`, icon: 'none' }); + }, + + updateTimeStr() { + const m = Math.floor(this.data.timeLeft / 60); + const s = this.data.timeLeft % 60; + const str = `${m.toString().padStart(2, '0')}:${s.toString().padStart(2, '0')}`; + this.setData({ timeStr: str }); + } +}) diff --git a/codes/minipro/calculation/miniprogram/pages/scoreboard/scoreboard.wxml b/codes/minipro/calculation/miniprogram/pages/scoreboard/scoreboard.wxml new file mode 100644 index 0000000..b7347d6 --- /dev/null +++ b/codes/minipro/calculation/miniprogram/pages/scoreboard/scoreboard.wxml @@ -0,0 +1,69 @@ + + + + + + + 第 {{period}} 节 + + + + {{timeStr}} + {{isTimerRunning ? '进行中' : '已暂停'}} + + + + + + + + + + + + + {{homeScore}} + + + +1 + +2 + +3 + + 分数修正 (-1) + + + + + + VS + + + + 交换 + + + + + + + + {{guestScore}} + + + +1 + +2 + +3 + + 分数修正 (-1) + + + + + + 重置比赛 + + 点击比分可直接+1 + + + + diff --git a/codes/minipro/calculation/miniprogram/pages/scoreboard/scoreboard.wxss b/codes/minipro/calculation/miniprogram/pages/scoreboard/scoreboard.wxss new file mode 100644 index 0000000..33b5501 --- /dev/null +++ b/codes/minipro/calculation/miniprogram/pages/scoreboard/scoreboard.wxss @@ -0,0 +1,273 @@ +/* pages/scoreboard/scoreboard.wxss */ +page { + background-color: #1a1b1f; /* 深色背景 */ + color: #fff; + height: 100%; + overflow: hidden; /* 禁止整页滚动,保持应用感 */ +} + +.container { + display: flex; + flex-direction: column; + height: 100vh; + padding: 0; + box-sizing: border-box; + overflow: hidden; +} + +/* 顶部计时区 */ +.timer-section { + flex-shrink: 0; /* 防止被压缩 */ + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + padding: 20rpx 0; /* 减小内边距 */ + background: #25262b; + box-shadow: 0 4rpx 20rpx rgba(0,0,0,0.2); + position: relative; + z-index: 10; +} + +.period-badge { + background: rgba(255, 255, 255, 0.1); + padding: 6rpx 20rpx; + border-radius: 30rpx; + font-size: 24rpx; + color: #aaa; + margin-bottom: 10rpx; + display: flex; + align-items: center; +} + +.tip-icon { font-size: 16rpx; margin-left: 8rpx; } + +.timer-display { + font-family: 'Courier New', Courier, monospace; + font-size: 72rpx; /* 稍微缩小字体 */ + font-weight: bold; + letter-spacing: 4rpx; + color: #fff; + line-height: 1; + position: relative; + text-shadow: 0 0 10rpx rgba(255, 255, 255, 0.3); + transition: color 0.3s; + padding: 10rpx 0; +} + +.timer-display.running { + color: #00e676; + text-shadow: 0 0 16rpx rgba(0, 230, 118, 0.4); +} + +.timer-status { + position: absolute; + bottom: -24rpx; /* 调整位置 */ + left: 50%; + transform: translateX(-50%); + font-size: 18rpx; + color: #666; + white-space: nowrap; +} + +.timer-controls { + position: absolute; + right: 30rpx; + top: 50%; + transform: translateY(-50%); +} + +.icon-btn { + width: 50rpx; + height: 50rpx; + border-radius: 50%; + background: rgba(255,255,255,0.1); + display: flex; + align-items: center; + justify-content: center; + font-size: 26rpx; + color: #aaa; +} + +/* 计分核心区 */ +.scoreboard-card { + flex: 1; + display: flex; + padding: 20rpx; + gap: 16rpx; + align-items: stretch; /* 拉伸填充高度 */ + justify-content: center; + overflow: hidden; /* 内部溢出隐藏 */ +} + +.team-panel { + flex: 1; + display: flex; + flex-direction: column; + align-items: center; + background: #25262b; + border-radius: 20rpx; + padding: 20rpx 10rpx; + /* height: 90%; 删除固定高度 */ + height: auto; + justify-content: space-between; /* 上下分布对齐 */ + border: 1px solid rgba(255,255,255,0.05); + position: relative; + /* overflow: hidden; 删除溢出隐藏,避免裁剪内容 */ +} + +/* 队伍顶部的装饰条 */ +.team-panel::before { + content: ''; + position: absolute; + top: 0; + left: 0; + right: 0; + height: 6rpx; + border-top-left-radius: 20rpx; + border-top-right-radius: 20rpx; +} +.team-home::before { background: #ff4d4f; } +.team-guest::before { background: #1890ff; } + +.team-name { + text-align: center; + font-size: 32rpx; + color: #ddd; + font-weight: bold; + margin-top: 10rpx; + flex-shrink: 0; +} + +.score-display { + font-size: 120rpx; /* 适配小屏幕,稍微缩小 */ + font-weight: 900; + line-height: 1; + margin: 20rpx 0; + position: relative; + z-index: 1; /* 确保在按钮上方(如果重叠) */ + flex-grow: 1; /* 占据剩余空间 */ + display: flex; + align-items: center; justify-content: center; + transition: transform 0.1s ease-in-out; +} + +/* 弹跳动画 */ +.bounce-anim { + animation: bounce 0.3s cubic-bezier(0.175, 0.885, 0.32, 1.275); +} + +@keyframes bounce { + 0% { transform: scale(1); } + 50% { transform: scale(1.2); } + 100% { transform: scale(1); } +} + +.team-home .score-display { color: #ff4d4f; } +.team-guest .score-display { color: #1890ff; } + +.action-buttons { + display: flex; + flex-direction: column; + gap: 16rpx; + width: 85%; + flex-shrink: 0; + margin-bottom: 10rpx; +} + +.score-btn { + width: 100%; + padding: 18rpx 0; /* 减小高度 */ + text-align: center; + border-radius: 12rpx; + font-size: 30rpx; + font-weight: bold; + transition: opacity 0.2s; +} + +.score-btn:active { + opacity: 0.7; + transform: scale(0.98); +} + +.btn-red { background: rgba(255, 77, 79, 0.15); color: #ff4d4f; border: 1px solid rgba(255, 77, 79, 0.3); } +.btn-blue { background: rgba(24, 144, 255, 0.15); color: #1890ff; border: 1px solid rgba(24, 144, 255, 0.3); } + +.minus-btn { + margin-top: 10rpx; + font-size: 22rpx; + color: #888; + padding: 10rpx 24rpx; + border-radius: 30rpx; + background: rgba(255,255,255,0.08); + border: 1px solid rgba(255,255,255,0.1); + flex-shrink: 0; +} + +/* 中间分割与VS */ +.vs-divider { + width: 40rpx; /* 变窄 */ + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + height: 100%; +} + +.line { + width: 2rpx; + background: rgba(255,255,255,0.1); + flex: 1; +} + +.vs-text { + font-size: 28rpx; + font-weight: bold; + color: #555; + margin: 10rpx 0; + font-style: italic; +} + +.exchange-btn { + display: flex; + flex-direction: column; + align-items: center; + margin: 20rpx 0; +} + +.exchange-icon { + font-size: 36rpx; + color: #aaa; + margin-bottom: 2rpx; +} + +.exchange-btn text:last-child { + font-size: 18rpx; + color: #666; +} + +/* 底部功能 */ +.footer-tools { + flex-shrink: 0; + padding: 20rpx 30rpx; /* 减小内边距 */ + display: flex; + justify-content: space-between; + align-items: center; + background: #1a1b1f; + border-top: 1px solid rgba(255,255,255,0.05); /* 增加分割线防止混淆 */ + margin-bottom: constant(safe-area-inset-bottom); /* 适配全面屏底部 */ + margin-bottom: env(safe-area-inset-bottom); +} + +.tool-btn { + background: rgba(255,255,255,0.08); + padding: 14rpx 36rpx; + border-radius: 40rpx; + font-size: 26rpx; + color: #ccc; +} + +.hint { + font-size: 22rpx; + color: #444; +}