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
+ x²
+ √
+ %
+
+
+
+
+
+ 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)
+
+
+
+
+
+
+
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;
+}