From 2c5d5b750576f44cd08ff05aebe343ca49343129 Mon Sep 17 00:00:00 2001 From: Joywayer Date: Mon, 9 Feb 2026 01:15:19 +0800 Subject: [PATCH] Refactor: UI & Feature updates (Calculator, UnitConverter, Privacy, Cleanup) --- .../minipro/calculation/miniprogram/app.json | 32 +- .../miniprogram/assets/tabs/tool-active.png | Bin 0 -> 225 bytes .../miniprogram/assets/tabs/tool.png | Bin 0 -> 222 bytes .../miniprogram/assets/tabs/user-active.png | Bin 0 -> 260 bytes .../miniprogram/assets/tabs/user.png | Bin 0 -> 257 bytes .../privacy-popup/privacy-popup.json | 4 + .../components/privacy-popup/privacy-popup.ts | 61 ++ .../privacy-popup/privacy-popup.wxml | 14 + .../privacy-popup/privacy-popup.wxss | 68 ++ .../calculation/miniprogram/generate_icons.py | 95 +++ .../miniprogram/pages/about/about.json | 4 + .../miniprogram/pages/about/about.ts | 17 + .../miniprogram/pages/about/about.wxml | 27 + .../miniprogram/pages/about/about.wxss | 99 +++ .../pages/calculator/calculator.json | 5 +- .../pages/calculator/calculator.ts | 58 +- .../pages/calculator/calculator.wxml | 29 +- .../pages/calculator/calculator.wxss | 446 ++++++------ .../pages/date-calc/date-calc.json | 6 - .../miniprogram/pages/date-calc/date-calc.ts | 328 --------- .../pages/date-calc/date-calc.wxml | 221 ------ .../pages/date-calc/date-calc.wxss | 664 ------------------ .../miniprogram/pages/discount/discount.json | 4 + .../miniprogram/pages/discount/discount.ts | 53 ++ .../miniprogram/pages/discount/discount.wxml | 28 + .../miniprogram/pages/discount/discount.wxss | 86 +++ .../miniprogram/pages/history/history.json | 4 + .../miniprogram/pages/history/history.ts | 28 + .../miniprogram/pages/history/history.wxml | 22 + .../miniprogram/pages/history/history.wxss | 84 +++ .../miniprogram/pages/index/index.ts | 23 +- .../miniprogram/pages/index/index.wxml | 42 +- .../miniprogram/pages/index/index.wxss | 52 ++ .../miniprogram/pages/privacy/privacy.json | 4 + .../miniprogram/pages/privacy/privacy.ts | 2 + .../miniprogram/pages/privacy/privacy.wxml | 23 + .../miniprogram/pages/privacy/privacy.wxss | 53 ++ .../miniprogram/pages/profile/profile.json | 1 + .../miniprogram/pages/profile/profile.ts | 44 +- .../miniprogram/pages/profile/profile.wxml | 25 +- .../pages/random-draw/random-draw.json | 4 - .../pages/random-draw/random-draw.ts | 84 --- .../pages/random-draw/random-draw.wxml | 39 - .../pages/random-draw/random-draw.wxss | 235 ------- .../pages/scoreboard/scoreboard.json | 6 - .../pages/scoreboard/scoreboard.ts | 186 ----- .../pages/scoreboard/scoreboard.wxml | 69 -- .../pages/scoreboard/scoreboard.wxss | 273 ------- .../pages/unit-converter/unit-converter.ts | 9 + .../pages/unit-converter/unit-converter.wxml | 2 +- .../pages/unit-converter/unit-converter.wxss | 80 ++- .../calculation/miniprogram/utils/lunar.ts | 310 -------- 52 files changed, 1351 insertions(+), 2702 deletions(-) create mode 100644 codes/minipro/calculation/miniprogram/assets/tabs/tool-active.png create mode 100644 codes/minipro/calculation/miniprogram/assets/tabs/tool.png create mode 100644 codes/minipro/calculation/miniprogram/assets/tabs/user-active.png create mode 100644 codes/minipro/calculation/miniprogram/assets/tabs/user.png create mode 100644 codes/minipro/calculation/miniprogram/components/privacy-popup/privacy-popup.json create mode 100644 codes/minipro/calculation/miniprogram/components/privacy-popup/privacy-popup.ts create mode 100644 codes/minipro/calculation/miniprogram/components/privacy-popup/privacy-popup.wxml create mode 100644 codes/minipro/calculation/miniprogram/components/privacy-popup/privacy-popup.wxss create mode 100644 codes/minipro/calculation/miniprogram/generate_icons.py create mode 100644 codes/minipro/calculation/miniprogram/pages/about/about.json create mode 100644 codes/minipro/calculation/miniprogram/pages/about/about.ts create mode 100644 codes/minipro/calculation/miniprogram/pages/about/about.wxml create mode 100644 codes/minipro/calculation/miniprogram/pages/about/about.wxss delete mode 100644 codes/minipro/calculation/miniprogram/pages/date-calc/date-calc.json delete mode 100644 codes/minipro/calculation/miniprogram/pages/date-calc/date-calc.ts delete mode 100644 codes/minipro/calculation/miniprogram/pages/date-calc/date-calc.wxml delete mode 100644 codes/minipro/calculation/miniprogram/pages/date-calc/date-calc.wxss create mode 100644 codes/minipro/calculation/miniprogram/pages/discount/discount.json create mode 100644 codes/minipro/calculation/miniprogram/pages/discount/discount.ts create mode 100644 codes/minipro/calculation/miniprogram/pages/discount/discount.wxml create mode 100644 codes/minipro/calculation/miniprogram/pages/discount/discount.wxss create mode 100644 codes/minipro/calculation/miniprogram/pages/history/history.json create mode 100644 codes/minipro/calculation/miniprogram/pages/history/history.ts create mode 100644 codes/minipro/calculation/miniprogram/pages/history/history.wxml create mode 100644 codes/minipro/calculation/miniprogram/pages/history/history.wxss create mode 100644 codes/minipro/calculation/miniprogram/pages/privacy/privacy.json create mode 100644 codes/minipro/calculation/miniprogram/pages/privacy/privacy.ts create mode 100644 codes/minipro/calculation/miniprogram/pages/privacy/privacy.wxml create mode 100644 codes/minipro/calculation/miniprogram/pages/privacy/privacy.wxss delete mode 100644 codes/minipro/calculation/miniprogram/pages/random-draw/random-draw.json delete mode 100644 codes/minipro/calculation/miniprogram/pages/random-draw/random-draw.ts delete mode 100644 codes/minipro/calculation/miniprogram/pages/random-draw/random-draw.wxml delete mode 100644 codes/minipro/calculation/miniprogram/pages/random-draw/random-draw.wxss delete mode 100644 codes/minipro/calculation/miniprogram/pages/scoreboard/scoreboard.json delete mode 100644 codes/minipro/calculation/miniprogram/pages/scoreboard/scoreboard.ts delete mode 100644 codes/minipro/calculation/miniprogram/pages/scoreboard/scoreboard.wxml delete mode 100644 codes/minipro/calculation/miniprogram/pages/scoreboard/scoreboard.wxss delete mode 100644 codes/minipro/calculation/miniprogram/utils/lunar.ts diff --git a/codes/minipro/calculation/miniprogram/app.json b/codes/minipro/calculation/miniprogram/app.json index e3bb97c..dccfced 100644 --- a/codes/minipro/calculation/miniprogram/app.json +++ b/codes/minipro/calculation/miniprogram/app.json @@ -4,17 +4,39 @@ "pages/profile/profile", "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" + "pages/discount/discount", + "pages/history/history", + "pages/about/about", + "pages/privacy/privacy", + "pages/webview/webview", + "pages/logs/logs" ], + "__usePrivacyCheck__": true, "window": { "navigationBarTextStyle": "black", "navigationBarTitleText": "道棋百宝箱", "navigationBarBackgroundColor": "#EBF4F8" }, + "tabBar": { + "color": "#999999", + "selectedColor": "#11616B", + "backgroundColor": "#ffffff", + "borderStyle": "black", + "list": [ + { + "pagePath": "pages/index/index", + "text": "工具箱", + "iconPath": "assets/tabs/tool.png", + "selectedIconPath": "assets/tabs/tool-active.png" + }, + { + "pagePath": "pages/profile/profile", + "text": "我的", + "iconPath": "assets/tabs/user.png", + "selectedIconPath": "assets/tabs/user-active.png" + } + ] + }, "style": "v2", "componentFramework": "glass-easel", "lazyCodeLoading": "requiredComponents" diff --git a/codes/minipro/calculation/miniprogram/assets/tabs/tool-active.png b/codes/minipro/calculation/miniprogram/assets/tabs/tool-active.png new file mode 100644 index 0000000000000000000000000000000000000000..aec8ed67faef14742c09ec3c505f9b046959b73e GIT binary patch literal 225 zcmeAS@N?(olHy`uVBq!ia0vp^4j|0I1|(Ny7TyC=D?D8sLn`LHov@MffC0ndPfmVs z?)-oLtfh^|iG_2I*8@W(Fff~O{`bu5jItLP-!N{k_^{w?GkdO_O~&U3j?I7<2eh%uU}F5e zBL9{S{bq+_(;XH(4rH*BabK_)NJ+RaNCr|O?hBMz1soU{4H&EfSghh07+C}w`i?NJ zQu65*U!ZX4O9dmBV!}rz5r+l0=XdwL2cl@*e~i4rzWV+bE~fy!#o+1c=d#Wzp$PyL CgJZY= literal 0 HcmV?d00001 diff --git a/codes/minipro/calculation/miniprogram/assets/tabs/user.png b/codes/minipro/calculation/miniprogram/assets/tabs/user.png new file mode 100644 index 0000000000000000000000000000000000000000..e93b8164e422538806a10b7cd693be6a9dcb6338 GIT binary patch literal 257 zcmeAS@N?(olHy`uVBq!ia0vp^4j|0I1|(Ny7TyC=Cp=voLn`LHz3Rx-q$uM0u>QYw z!PZvh#hxOm^Ok>)DR}qOVUOPPImPEJpU+{w7Pb3gdBiTcKkqJ-|2b{PX)pF@_K#$N zx=wp0MX3b0Ki4>zI0SeMjN@8lAOB_%P>@-mZ?#Wf4oKGNSpBQq_;7uh&xbSm`McGf zj!bt{c;eggv&hlBeghETz2 zN4Wz85-ayGv { + console.log('触发隐私协议弹窗', eventInfo); + const needsResolve = eventInfo.referrer === 'api'; // API调用触发需要手动resolve + + _this.resolvePrivacyAuthorization = resolve; // 保存 resolve 函数 + + if (wx.getPrivacySetting) { + wx.getPrivacySetting({ + success: (res) => { + console.log('getPrivacySetting success', res); + // 无论是否需要(needAuthorization), 只要触发了onNeedPrivacyAuthorization就证明被拦截了(或者需要再次确认) + // 通常 res.needAuthorization 为 true + _this.setData({ + innerShow: true, + privacyContractName: res.privacyContractName || _this.data.privacyContractName + }); + } + }) + } + }) + } + }, + }, + methods: { + handleDisagree() { + this.setData({ innerShow: false }); + // 如果有保存的 resolve,调用并报错拒绝(虽然 API 主要是要 resolve({ buttonId: 'agree-btn', event: 'agree' }),拒绝通常不调 resolve 或调 fail) + // 实际上用户拒绝后,该次 API 调用应该失败 + if (this.resolvePrivacyAuthorization) { + // this.resolvePrivacyAuthorization({ event: 'disagree' }); // 某些场景下可能需要 + this.resolvePrivacyAuthorization = null; + } + }, + handleAgree(e: any) { + console.log('用户同意隐私协议'); + // 按钮 open-type="agreePrivacyAuthorization" 会自动触发同意逻辑 + // 这里的 bindagreeprivacyauthorization 回调用于 UI 反馈 + this.setData({ innerShow: false }); + + // 如果是由 API 触发的(如 getClipboardData),可以通过保存的 resolve 告知系统 + // 但通常 open-type 按钮点击后系统会自动重试之前的 API 调用,或者在 button 上绑定 resolve + if (this.resolvePrivacyAuthorization) { + this.resolvePrivacyAuthorization({ buttonId: 'agree-btn', event: 'agree' }); + this.resolvePrivacyAuthorization = null; + } + }, + openPrivacyContract() { + wx.openPrivacyContract({}) + } + } +}) \ No newline at end of file diff --git a/codes/minipro/calculation/miniprogram/components/privacy-popup/privacy-popup.wxml b/codes/minipro/calculation/miniprogram/components/privacy-popup/privacy-popup.wxml new file mode 100644 index 0000000..bd22ee2 --- /dev/null +++ b/codes/minipro/calculation/miniprogram/components/privacy-popup/privacy-popup.wxml @@ -0,0 +1,14 @@ + + + 用户隐私保护提示 + + 感谢您使用本程序,在使用前您需仔细阅读 + {{privacyContractName}} + 。当您点击同意并开始使用服务时,即表示您已理解并同意该条款内容。 + + + + + + + \ No newline at end of file diff --git a/codes/minipro/calculation/miniprogram/components/privacy-popup/privacy-popup.wxss b/codes/minipro/calculation/miniprogram/components/privacy-popup/privacy-popup.wxss new file mode 100644 index 0000000..651c082 --- /dev/null +++ b/codes/minipro/calculation/miniprogram/components/privacy-popup/privacy-popup.wxss @@ -0,0 +1,68 @@ +.privacy-mask { + position: fixed; + top: 0; + left: 0; + right: 0; + bottom: 0; + background: rgba(0, 0, 0, 0.6); + z-index: 9999; + display: flex; + justify-content: center; + align-items: center; +} + +.privacy-dialog { + width: 600rpx; + background: #fff; + border-radius: 24rpx; + padding: 40rpx; + box-sizing: border-box; +} + +.privacy-title { + font-weight: bold; + font-size: 34rpx; + margin-bottom: 30rpx; + text-align: center; + color: #333; +} + +.privacy-content { + font-size: 28rpx; + color: #666; + line-height: 1.6; + margin-bottom: 50rpx; + text-align: justify; +} + +.privacy-link { + color: #11616B; /* Color 01 */ + font-weight: 500; +} + +.privacy-btns { + display: flex; + gap: 30rpx; +} + +.btn-refuse { + flex: 1; + background: #F2F2F2 !important; + color: #666 !important; + font-size: 30rpx !important; + border-radius: 12rpx !important; + height: 80rpx !important; + line-height: 80rpx !important; + border: none !important; +} + +.btn-agree { + flex: 1; + background: #11616B !important; /* Color 01 */ + color: #fff !important; + font-size: 30rpx !important; + border-radius: 12rpx !important; + height: 80rpx !important; + line-height: 80rpx !important; + border: none !important; +} \ No newline at end of file diff --git a/codes/minipro/calculation/miniprogram/generate_icons.py b/codes/minipro/calculation/miniprogram/generate_icons.py new file mode 100644 index 0000000..d713be7 --- /dev/null +++ b/codes/minipro/calculation/miniprogram/generate_icons.py @@ -0,0 +1,95 @@ +import os +import zlib +import struct + +def write_png(buf, width, height, path): + # Reverse the rows for top-down processing if needed, but standard is top-down + # Simple grayscale + alpha: Type 6 (Truecolor with Alpha) is easiest or Type 4 (Grayscale with Alpha) because we want specific colors. + # We will use RGBA (Type 6). + + # Signature + png = b'\x89PNG\r\n\x1a\n' + + # IHDR + ihdr = struct.pack("!I", width) + struct.pack("!I", height) + b'\x08\x06\x00\x00\x00' + png += struct.pack("!I", len(ihdr)) + b'IHDR' + ihdr + struct.pack("!I", zlib.crc32(b'IHDR' + ihdr)) + + # IDAT + # 8-bit depth, RGBA. + # Filter type 0 (None) for each scanline. + raw_data = b'' + for y in range(height): + raw_data += b'\x00' # Filter type 0 + for x in range(width): + # Get pixel from buf + curr = buf[y][x] # (r, g, b, a) + raw_data += struct.pack("BBBB", *curr) + + compressed = zlib.compress(raw_data) + png += struct.pack("!I", len(compressed)) + b'IDAT' + compressed + struct.pack("!I", zlib.crc32(b'IDAT' + compressed)) + + # IEND + png += struct.pack("!I", 0) + b'IEND' + struct.pack("!I", zlib.crc32(b'IEND')) + + with open(path, 'wb') as f: + f.write(png) + +def hex_to_rgba(hex_str, alpha=255): + h = hex_str.lstrip('#') + return tuple(int(h[i:i+2], 16) for i in (0, 2, 4)) + (alpha,) + +def create_icons(): + base_dir = r"g:\Works\YouleGames\codes\minipro\calculation\miniprogram\assets\tabs" + if not os.path.exists(base_dir): + os.makedirs(base_dir) + + size = 64 + + # Colors + c_inactive = hex_to_rgba('#999999') + c_active = hex_to_rgba('#11616B') + c_transparent = (0, 0, 0, 0) + + # Icon 1: Tools (Grid/Square) + # --------------------------- + def draw_tools(color): + grid = [[c_transparent for _ in range(size)] for _ in range(size)] + margin = 12 + for y in range(margin, size-margin): + for x in range(margin, size-margin): + # Border + if x < margin+4 or x > size-margin-5 or y < margin+4 or y > size-margin-5: + grid[y][x] = color + # Inner cross/plus + elif size//2 - 2 <= x <= size//2 + 1 or size//2 - 2 <= y <= size//2 + 1: + grid[y][x] = color + return grid + + # Icon 2: Profile (Circle/Person) + # ------------------------------- + def draw_profile(color): + grid = [[c_transparent for _ in range(size)] for _ in range(size)] + cx, cy = size//2, size//2 + r_head = 10 + r_body = 20 + + for y in range(size): + for x in range(size): + # Head + if (x-cx)**2 + (y-(cy-10))**2 <= r_head**2: + grid[y][x] = color + # Body (arch) + elif (x-cx)**2 + (y-(cy+15))**2 <= r_body**2 and y > cy: + grid[y][x] = color + return grid + + write_png(draw_tools(c_inactive), size, size, os.path.join(base_dir, "tool.png")) + write_png(draw_tools(c_active), size, size, os.path.join(base_dir, "tool-active.png")) + + write_png(draw_profile(c_inactive), size, size, os.path.join(base_dir, "user.png")) + write_png(draw_profile(c_active), size, size, os.path.join(base_dir, "user-active.png")) + + print("Icons created successfully.") + +if __name__ == '__main__': + create_icons() diff --git a/codes/minipro/calculation/miniprogram/pages/about/about.json b/codes/minipro/calculation/miniprogram/pages/about/about.json new file mode 100644 index 0000000..2677d80 --- /dev/null +++ b/codes/minipro/calculation/miniprogram/pages/about/about.json @@ -0,0 +1,4 @@ +{ + "navigationBarTitleText": "关于", + "usingComponents": {} +} \ No newline at end of file diff --git a/codes/minipro/calculation/miniprogram/pages/about/about.ts b/codes/minipro/calculation/miniprogram/pages/about/about.ts new file mode 100644 index 0000000..2c9df82 --- /dev/null +++ b/codes/minipro/calculation/miniprogram/pages/about/about.ts @@ -0,0 +1,17 @@ +Page({ + data: { + version: '1.0.0' + }, + + onShareAppMessage() { + return { + title: '道棋百宝箱 - 您的日常计算好帮手' + } + }, + + goToPrivacy() { + wx.navigateTo({ + url: '/pages/privacy/privacy' + }) + } +}); \ No newline at end of file diff --git a/codes/minipro/calculation/miniprogram/pages/about/about.wxml b/codes/minipro/calculation/miniprogram/pages/about/about.wxml new file mode 100644 index 0000000..eb0dcc7 --- /dev/null +++ b/codes/minipro/calculation/miniprogram/pages/about/about.wxml @@ -0,0 +1,27 @@ + + + 🧮 + 道棋百宝箱 + Version {{version}} + + + + 应用简介 + + 本小程序为实用生活计算工具,提供基础算术、常用单位换算、日期计算、折扣计算等完整功能。 + + 我们致力于为您提供便捷、纯净的日常计算服务,所有数据仅存储于本地,充分保障您的隐私安全。 + + + + + + 隐私政策 + > + + + + + © 2024 道棋百宝箱 + + \ No newline at end of file diff --git a/codes/minipro/calculation/miniprogram/pages/about/about.wxss b/codes/minipro/calculation/miniprogram/pages/about/about.wxss new file mode 100644 index 0000000..9ef0ace --- /dev/null +++ b/codes/minipro/calculation/miniprogram/pages/about/about.wxss @@ -0,0 +1,99 @@ +page { + background-color: #EBF4F8; /* Color 03 */ +} + +.container { + padding: 40rpx; + display: flex; + flex-direction: column; + align-items: center; +} + +.logo-section { + display: flex; + flex-direction: column; + align-items: center; + margin-bottom: 60rpx; + margin-top: 40rpx; +} + +.logo-placeholder { + font-size: 100rpx; + margin-bottom: 20rpx; + background-color: #fff; + width: 160rpx; + height: 160rpx; + line-height: 160rpx; + text-align: center; + border-radius: 40rpx; + box-shadow: 0 4rpx 12rpx rgba(17, 97, 107, 0.1); + color: #11616B; +} + +.app-name { + font-size: 40rpx; + font-weight: bold; + color: #11616B; /* Color 01 */ + margin-bottom: 10rpx; +} + +.version { + font-size: 24rpx; + color: #7BBDB6; /* Color 02 */ +} + +.content-section { + width: 100%; + background-color: #fff; + padding: 40rpx; + border-radius: 20rpx; + margin-bottom: 30rpx; + box-sizing: border-box; + box-shadow: 0 4rpx 12rpx rgba(17, 97, 107, 0.05); +} + +.section-title { + font-size: 30rpx; + font-weight: bold; + margin-bottom: 20rpx; + color: #11616B; /* Color 01 */ +} + +.description { + font-size: 28rpx; + color: #555; /* Neutral Dark */ + line-height: 1.8; +} + +.menu-list { + width: 100%; + background-color: #fff; + border-radius: 20rpx; + overflow: hidden; + box-shadow: 0 4rpx 12rpx rgba(17, 97, 107, 0.05); +} + +.menu-item { + display: flex; + justify-content: space-between; + padding: 30rpx 40rpx; + font-size: 30rpx; + color: #11616B; /* Color 01 */ +} + +.menu-item:active { + background-color: #f0f0f0; +} + +.arrow { + color: #7BBDB6; /* Color 02 */ +} + +.footer { + margin-top: 60rpx; +} + +.copyright { + font-size: 22rpx; + color: #7BBDB6; /* Color 02 */ +} diff --git a/codes/minipro/calculation/miniprogram/pages/calculator/calculator.json b/codes/minipro/calculation/miniprogram/pages/calculator/calculator.json index e2536f8..acaeed9 100644 --- a/codes/minipro/calculation/miniprogram/pages/calculator/calculator.json +++ b/codes/minipro/calculation/miniprogram/pages/calculator/calculator.json @@ -1,5 +1,8 @@ { "navigationBarTitleText": "科学计算器", "navigationBarBackgroundColor": "#EBF4F8", - "navigationBarTextStyle": "black" + "navigationBarTextStyle": "black", + "usingComponents": { + "privacy-popup": "/components/privacy-popup/privacy-popup" + } } diff --git a/codes/minipro/calculation/miniprogram/pages/calculator/calculator.ts b/codes/minipro/calculation/miniprogram/pages/calculator/calculator.ts index ff7bc35..b976a6d 100644 --- a/codes/minipro/calculation/miniprogram/pages/calculator/calculator.ts +++ b/codes/minipro/calculation/miniprogram/pages/calculator/calculator.ts @@ -9,6 +9,7 @@ Page({ isScientific: false, showHistory: false, showHelp: false, + showSettingsMenu: false, historyList: [] as Array<{expression: string, result: string}> }, @@ -26,7 +27,21 @@ Page({ }, toggleHelp() { - this.setData({ showHelp: !this.data.showHelp }); + this.setData({ + showHelp: !this.data.showHelp, + showSettingsMenu: false + }); + }, + + toggleSettings() { + this.vibrate(); + this.setData({ showSettingsMenu: !this.data.showSettingsMenu }); + }, + + closeSettings() { + if (this.data.showSettingsMenu) { + this.setData({ showSettingsMenu: false }); + } }, onLoad() { @@ -209,7 +224,7 @@ Page({ this.setData({ operator: nextOperator, // 更新历史记录中的符号 - history: `${firstOperand} ${nextOperator}` + history: `${firstOperand} ${nextOperator}` }); return; } @@ -279,29 +294,52 @@ Page({ }, toggleHistory() { - this.setData({ showHistory: !this.data.showHistory }); + this.vibrate(); + this.setData({ + showHistory: !this.data.showHistory, + showSettingsMenu: false + }); }, clearHistory() { - this.setData({ historyList: [] }); + this.vibrate(); wx.removeStorageSync('CALC_HISTORY'); + this.setData({ historyList: [] }); }, useHistoryResult(e: any) { + this.vibrate(); const result = e.currentTarget.dataset.result; this.setData({ - displayValue: String(result), - showHistory: false, - waitingForSecondOperand: true + displayValue: result, + waitingForSecondOperand: false, // Treat as a fresh input that can be operated on + showHistory: false }); }, copyResult() { + this.vibrate(); + const { displayValue } = this.data; wx.setClipboardData({ - data: this.data.displayValue, + data: displayValue, success: () => { - wx.showToast({ title: '已复制', icon: 'success', duration: 1000 }); + wx.showToast({ + title: '复制成功', + icon: 'success' + }); + }, + fail: (err) => { + console.error('复制失败:', err); + // 隐私协议未声明或用户拒绝会导致复制失败 (errno 112) + if (err.errno === 112 || (err.errMsg && err.errMsg.indexOf('privacy') > -1)) { + wx.showModal({ + title: '提示', + content: '无法自动复制,请长按显示区域手动复制', + showCancel: false, + confirmText: '知道了' + }); + } } }); } -}) +}); diff --git a/codes/minipro/calculation/miniprogram/pages/calculator/calculator.wxml b/codes/minipro/calculation/miniprogram/pages/calculator/calculator.wxml index cccca49..2f49244 100644 --- a/codes/minipro/calculation/miniprogram/pages/calculator/calculator.wxml +++ b/codes/minipro/calculation/miniprogram/pages/calculator/calculator.wxml @@ -1,5 +1,6 @@ + @@ -70,14 +71,30 @@ 科学 - - 🕐 - - + + 📋 + 复制结果 - - + + + + + ⚙️ + + + + + 🕐 + 历史记录 + + + + 使用帮助 + + + + diff --git a/codes/minipro/calculation/miniprogram/pages/calculator/calculator.wxss b/codes/minipro/calculation/miniprogram/pages/calculator/calculator.wxss index ac59144..9429cbf 100644 --- a/codes/minipro/calculation/miniprogram/pages/calculator/calculator.wxss +++ b/codes/minipro/calculation/miniprogram/pages/calculator/calculator.wxss @@ -24,9 +24,9 @@ page { justify-content: flex-end; align-items: flex-end; padding: 30rpx 40rpx 20rpx; - background: transparent; /* No more dark gradient */ + background: transparent; position: relative; - overflow: hidden; + overflow: visible; /* Limit overflow for dropdown but visible for popup */ } /* 顶部工具栏 */ @@ -38,12 +38,13 @@ page { display: flex; justify-content: space-between; align-items: center; - z-index: 10; + z-index: 100; } .top-actions { display: flex; gap: 20rpx; + align-items: center; } .action-btn { @@ -57,11 +58,92 @@ page { box-shadow: 0 4rpx 12rpx rgba(17, 97, 107, 0.15); /* Teal shadow */ } +.action-btn-hover { + background-color: #F0F0F0 !important; + transform: translateY(2rpx); + box-shadow: 0 2rpx 6rpx rgba(17, 97, 107, 0.1) !important; +} + +/* 复制按钮特殊样式 */ +.action-btn.copy-btn { + width: auto; + padding: 0 20rpx; + border-radius: 36rpx; + gap: 10rpx; +} + +.copy-btn .action-text { + font-size: 24rpx; + color: #11616B; + font-weight: 500; +} + .action-icon { - font-size: 36rpx; + font-size: 34rpx; color: #11616B; /* Color 01 */ } +/* Settings Dropdown Container */ +.settings-container { + position: relative; +} + +/* Dropdown Menu */ +.dropdown-menu { + position: absolute; + top: 80rpx; + right: 0; + width: 240rpx; + background: #ffffff; + border-radius: 12rpx; + box-shadow: 0 8rpx 24rpx rgba(0,0,0,0.15); + padding: 10rpx 0; + opacity: 0; + visibility: hidden; + transform: translateY(-10rpx); + transition: all 0.2s cubic-bezier(0.25, 0.8, 0.25, 1); + z-index: 500; +} + +.dropdown-menu.show { + opacity: 1; + visibility: visible; + transform: translateY(0); +} + +.menu-item { + display: flex; + align-items: center; + padding: 20rpx 30rpx; + gap: 20rpx; + transition: background 0.1s; +} + +.menu-item:active { + background-color: #f5f5f5; +} + +.menu-icon { + font-size: 36rpx; +} + +.menu-text { + font-size: 28rpx; + color: #333; +} + +/* Mask for closing dropdown */ +.settings-mask { + position: fixed; + top: 0; + left: 0; + right: 0; + bottom: 0; + z-index: 400; + background: transparent; +} + + /* 模式切换 */ .mode-indicator { display: flex; @@ -98,165 +180,156 @@ page { font-family: -apple-system, 'SF Pro Display', 'Helvetica Neue', sans-serif; text-align: right; width: 100%; - letter-spacing: 2rpx; + word-break: break-all; } -/* 当前值 */ +/* 当前数值 */ .current-value { - font-size: 130rpx; - color: #11616B; /* Color 01 (Deep Teal) */ + font-size: 96rpx; + font-family: -apple-system, 'SF Pro Display', 'Helvetica Neue', sans-serif; + font-weight: 300; + color: #11616B; /* Color 01 */ line-height: 1.1; - font-weight: 200; word-break: break-all; text-align: right; width: 100%; - font-family: -apple-system, 'SF Pro Display', 'Helvetica Neue', sans-serif; + transition: font-size 0.2s; } -.current-value.shrink { - font-size: 90rpx; -} - -.current-value.shrink-more { - font-size: 62rpx; -} +.current-value.shrink { font-size: 72rpx; } +.current-value.shrink-more { font-size: 56rpx; } /* ========== 键盘区域 ========== */ .keypad-area { - background: #EBF4F8; /* Color 03 */ - padding: 16rpx 24rpx 24rpx; + background-color: #ffffff; + border-radius: 40rpx 40rpx 0 0; + padding: 30rpx 20rpx; + padding-bottom: calc(30rpx + env(safe-area-inset-bottom)); + box-shadow: 0 -4rpx 20rpx rgba(17, 97, 107, 0.05); + transition: transform 0.3s cubic-bezier(0.25, 0.8, 0.25, 1); + z-index: 50; display: flex; flex-direction: column; - gap: 20rpx; + gap: 20rpx; } -.keypad-area.scientific-pad { - gap: 14rpx; - padding-bottom: 20rpx; +/* 科学键盘模式 */ +.scientific-pad { + /* 增加高度或者改变布局逻辑 if needed */ } -/* 行 */ .row { display: flex; justify-content: space-between; gap: 20rpx; -} - -.keypad-area.scientific-pad .row { - gap: 14rpx; + margin-bottom: 0; } .small-row { - justify-content: space-around; - gap: 8rpx; + justify-content: space-between; + gap: 16rpx; } -/* ========== 按钮通用 ========== */ +/* 按钮通用 */ .btn { - width: 152rpx; - height: 152rpx; - border-radius: 50%; + flex: 1; + height: 130rpx; /* Taller buttons */ + border-radius: 30rpx; /* More rounded */ display: flex; - justify-content: center; align-items: center; - font-size: 56rpx; + justify-content: center; + font-size: 48rpx; font-weight: 400; - font-family: -apple-system, 'SF Pro Display', 'Helvetica Neue', sans-serif; - position: relative; - overflow: hidden; - box-shadow: 0 4rpx 10rpx rgba(17, 97, 107, 0.05); /* Very subtle shadow */ -} - -/* 数字键 */ -.digit { - background-color: #ffffff; + background-color: #F8FBFC; /* Light gray-blue */ color: #11616B; /* Color 01 */ + box-shadow: 0 4rpx 0 #E1E8EC; + transition: transform 0.1s, background-color 0.2s; } -/* 功能键 (AC, +/-, %) */ -.func { - background-color: #7BBDB6; /* Color 02 (Medium Teal) */ - color: #ffffff; - font-size: 46rpx; +.btn-hover { + transform: translateY(4rpx); + box-shadow: none !important; +} + +/* 功能键 (AC, Delete, +/-) */ +.btn.func { + color: #DC8B70; /* Color 05 (Highlight) - Use Peach/Orange for clear/action */ font-weight: 500; + background-color: #FFF6F3; /* Light peach bg */ + box-shadow: 0 4rpx 0 #FCEBE6; +} + +.btn.func.btn-hover { + background-color: #FEDCC8 !important; } /* 运算符 */ -.operator { - background-color: #FED9CD; /* Color 04 (Pale Pink/Peach) */ - color: #DC8B70; /* Color 05 (Terracotta) for contrast */ - font-size: 64rpx; - font-weight: 400; +.btn.operator { + background-color: #EBF4F8; /* Color 03 */ + color: #11616B; + font-size: 56rpx; } -/* 等号键:渐变突出 */ -.equal { - background: #11616B; /* Color 01 (Deep Teal) */ +.btn.operator.btn-hover { + background-color: #D3E7ED !important; +} + +.btn.equal { + background-color: #11616B; /* Color 01 (Primary) */ color: #ffffff; - box-shadow: 0 6rpx 20rpx rgba(17, 97, 107, 0.3); + box-shadow: 0 4rpx 0 #0D4A52; } -/* 0键 */ -.zero { - width: 324rpx; - border-radius: 76rpx; - justify-content: flex-start; - padding-left: 62rpx; - box-sizing: border-box; +.btn.equal.btn-hover { + background-color: #167A85 !important; } -/* 点击反馈 */ -.btn-hover { - transform: scale(0.92); - opacity: 0.9; +/* 数字键 */ +.btn.digit { + background-color: #ffffff; + color: #333; + font-weight: 500; + font-size: 52rpx; + box-shadow: 0 4rpx 0 #EAEAEA; } -/* 科学模式缩放 */ -.keypad-area.scientific-pad .btn { - width: 130rpx; - height: 130rpx; - font-size: 48rpx; +.btn.digit.btn-hover { + background-color: #E0E0E0 !important; } -.keypad-area.scientific-pad .zero { - width: 274rpx; - border-radius: 65rpx; +.btn.zero { + flex: 2.2; /* Wider 0 button */ } -.keypad-area.scientific-pad .func { - font-size: 40rpx; -} - -.keypad-area.scientific-pad .operator { - font-size: 54rpx; -} - -/* 科学功能键 */ +/* 科学按钮 */ .sci-btn { - width: 120rpx !important; - height: 120rpx !important; - font-size: 32rpx !important; - background: rgba(17, 97, 107, 0.05) !important; /* Very light teal bg */ - color: #11616B !important; /* Color 01 */ - border-radius: 50% !important; - font-weight: 500 !important; - letter-spacing: 1rpx; + height: 90rpx; + font-size: 32rpx; + border-radius: 20rpx; + background-color: #EBF4F8; /* Color 03 */ + color: #7BBDB6; /* Color 02 */ + box-shadow: 0 3rpx 0 #DEE9EE; } -/* ========== 历史记录面板 ========== */ +.sci-btn.btn-hover { + background-color: #D3E7ED !important; +} + +/* ========== 面板 (历史/帮助) ========== */ .history-panel { position: fixed; top: 0; left: 0; - right: 0; - bottom: 0; - z-index: 100; - pointer-events: none; + width: 100%; + height: 100%; + z-index: 1000; + visibility: hidden; opacity: 0; + transition: all 0.3s; } .history-panel.show { - pointer-events: auto; + visibility: visible; opacity: 1; } @@ -264,171 +337,152 @@ page { position: absolute; top: 0; left: 0; - right: 0; - bottom: 0; - background: rgba(17, 97, 107, 0.4); /* Color 01 dim */ + width: 100%; + height: 100%; + background-color: rgba(0,0,0,0.4); } .history-content { - position: relative; - z-index: 101; - background: #EBF4F8; /* Color 03 */ - border-radius: 0 0 16rpx 16rpx; /* Reduced radius */ - max-height: 70vh; + position: absolute; + bottom: 0; + left: 0; + width: 100%; + height: 70%; + background-color: #ffffff; + border-radius: 40rpx 40rpx 0 0; + transform: translateY(100%); + transition: transform 0.3s cubic-bezier(0.25, 0.8, 0.25, 1); display: flex; flex-direction: column; - box-shadow: 0 16rpx 48rpx rgba(17, 97, 107, 0.2); + padding-bottom: env(safe-area-inset-bottom); } .history-panel.show .history-content { - animation: histSlideDown 0.3s cubic-bezier(0.32, 0.72, 0, 1) forwards; -} - -@keyframes histSlideDown { - from { transform: translateY(-100%); } - to { transform: translateY(0); } + transform: translateY(0); } .history-header { + padding: 30rpx 40rpx; display: flex; justify-content: space-between; align-items: center; - padding: 36rpx 40rpx; - border-bottom: 2rpx solid rgba(17, 97, 107, 0.1); + border-bottom: 1px solid #f0f0f0; } .history-title { - color: #11616B; /* Color 01 */ font-size: 34rpx; - font-weight: 600; - letter-spacing: 1rpx; + font-weight: bold; + color: #11616B; } .history-clear { - color: #DC8B70; /* Color 05 */ font-size: 28rpx; - padding: 8rpx 24rpx; - border-radius: 20rpx; - background: rgba(220, 139, 112, 0.15); /* Color 05 faint */ + color: #999; + padding: 10rpx; } .history-list { - max-height: 58vh; - padding: 10rpx 0; + flex: 1; + padding: 20rpx 40rpx; + box-sizing: border-box; } .history-item { - padding: 28rpx 40rpx; - border-bottom: 1rpx solid rgba(17, 97, 107, 0.08); /* Darker border for light bg */ + padding: 20rpx 0; + border-bottom: 1px solid #f8f8f8; display: flex; flex-direction: column; align-items: flex-end; } -.history-item:active { - background: rgba(17, 97, 107, 0.05); /* Dark interaction state */ +.history-item-hover { + background-color: #f9f9f9; } .history-expr { - color: rgba(17, 97, 107, 0.6); /* Color 01 faded - Visible on light bg */ font-size: 26rpx; - margin-bottom: 8rpx; - font-family: -apple-system, 'SF Pro Display', monospace; + color: #999; + margin-bottom: 6rpx; } .history-result { - color: #11616B; /* Color 01 - Visible High Contrast */ - font-size: 42rpx; + font-size: 36rpx; + color: #11616B; font-weight: 500; - font-family: -apple-system, 'SF Pro Display', 'Helvetica Neue', sans-serif; } .history-empty { - text-align: center; - padding: 100rpx 0; - color: rgba(17, 97, 107, 0.4); - font-size: 28rpx; + height: 400rpx; + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + color: #ccc; + gap: 20rpx; } .history-empty-icon { - font-size: 64rpx; - display: block; - margin-bottom: 16rpx; + font-size: 80rpx; opacity: 0.5; } -/* Help Panel Styles */ +/* 帮助样式 */ +.help-content { + height: 80%; /* Slightly taller */ +} + .help-list { - padding: 0; /* padding moved to inner section to avoid scroll-view width issues */ + padding: 10rpx 40rpx; } .help-section { - display: flex; - flex-direction: column; - gap: 40rpx; - padding: 30rpx 40rpx 60rpx; /* Apply padding here */ - box-sizing: border-box; + padding-bottom: 60rpx; } .help-item { - display: flex; - gap: 24rpx; - align-items: flex-start; + display: flex; + gap: 24rpx; + margin-bottom: 40rpx; + align-items: flex-start; } .help-icon { - font-size: 44rpx; - width: 60rpx; - text-align: center; - padding-top: 4rpx; + font-size: 40rpx; + background: #EBF4F8; + width: 80rpx; + height: 80rpx; + display: flex; + align-items: center; + justify-content: center; + border-radius: 20rpx; + color: #11616B; } .help-text { - flex: 1; - display: flex; - flex-direction: column; + flex: 1; } .help-h1 { - font-size: 30rpx; - font-weight: bold; - color: #11616B; /* Color 01 */ - margin-bottom: 12rpx; + display: block; + font-size: 30rpx; + font-weight: bold; + color: #11616B; + margin-bottom: 8rpx; } .help-p { - font-size: 26rpx; - color: #2d3436; - line-height: 1.6; - opacity: 0.8; + display: block; + font-size: 26rpx; + color: #666; + line-height: 1.5; + text-align: justify; } .share-btn-large { - background-color: #11616B !important; /* Color 01 */ - color: white !important; - border-radius: 16rpx !important; /* Unified radius */ - font-size: 30rpx !important; - font-weight: 500 !important; - margin-top: 20rpx; - width: 100% !important; - box-shadow: 0 8rpx 20rpx rgba(17, 97, 107, 0.25); - display: flex; - align-items: center; - justify-content: center; - padding: 24rpx 0; -} - -/* ========== 小屏适配 ========== */ -@media (max-width: 360px) { - .btn { - width: 140rpx; - height: 140rpx; - font-size: 50rpx; - } - .zero { - width: 300rpx; - } - .current-value { - font-size: 110rpx; - } + background-color: #11616B; + color: #fff; + border-radius: 50rpx; + font-weight: bold; + margin-top: 40rpx; + box-shadow: 0 4rpx 12rpx rgba(17, 97, 107, 0.3); } diff --git a/codes/minipro/calculation/miniprogram/pages/date-calc/date-calc.json b/codes/minipro/calculation/miniprogram/pages/date-calc/date-calc.json deleted file mode 100644 index 93db4f7..0000000 --- a/codes/minipro/calculation/miniprogram/pages/date-calc/date-calc.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "usingComponents": {}, - "navigationBarTitleText": "日期计算与农历转换", - "navigationBarBackgroundColor": "#EBF4F8", - "navigationBarTextStyle": "black" -} diff --git a/codes/minipro/calculation/miniprogram/pages/date-calc/date-calc.ts b/codes/minipro/calculation/miniprogram/pages/date-calc/date-calc.ts deleted file mode 100644 index 08c8454..0000000 --- a/codes/minipro/calculation/miniprogram/pages/date-calc/date-calc.ts +++ /dev/null @@ -1,328 +0,0 @@ -// pages/date-calc/date-calc.ts -import { solarToLunar, lunarToSolar, getSolarTerm, getFestival, getLunarFestival } from '../../utils/lunar'; - -Page({ - data: { - currentTab: 0, - startDate: '', - startWeekday: '', - endDate: '', - days: 0, - resultDate: '', - resultWeekday: '', - resultLunar: '', - intervalDays: 0, - intervalWeeks: 0, - intervalRemainingDays: 0, - intervalMonths: 0, - intervalExtraDays: 0, - // 农历转换相关 - lunarDirection: 'toL', // 'toL' 公历→农历, 'toS' 农历→公历 - lunarSolarDate: '', - lunarResult: null as any, - lunarFestivalText: '', - solarTermText: '', - solarFestivalText: '', - // 农历→公历 - lunarPickerRange: [[] as string[], [] as string[], [] as string[]], - lunarPickerValue: [0, 0, 0], - lunarPickerDisplay: '', - lunarToSolarResult: '', - lunarToSolarWeekday: '', - // 今日信息 - todaySolar: '', - todayLunar: '', - todayGanZhi: '', - todayAnimal: '', - todayTerm: '', - showHelp: false - }, - - onShareAppMessage() { - return { - title: '实用的日期推算与农历转换工具', - path: '/pages/date-calc/date-calc' - } - }, - - onShareTimeline() { - return { - title: '实用的日期推算与农历转换工具' - } - }, - - toggleHelp() { - this.setData({ - showHelp: !this.data.showHelp - }); - }, - - 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), - lunarSolarDate: dateStr - }); - - // 初始化农历相关 - this.initLunarPicker(); - this.updateTodayInfo(); - this.convertSolarToLunar(dateStr); - this.updateResultLunar(today); - }, - - switchTab(e: any) { - wx.vibrateShort({ type: 'light' }).catch(() => {}); - 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.replace(/-/g, '/')); - 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) { - wx.vibrateShort({ type: 'light' }).catch(() => {}); - const days = parseInt(e.currentTarget.dataset.days); - this.setData({ days: days }); - this.calculate(); - }, - - calculate() { - if (this.data.currentTab === 0) { - const start = new Date(this.data.startDate.replace(/-/g, '/')); - 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) - }); - this.updateResultLunar(target); - } else if (this.data.currentTab === 1) { - 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; - const months = Math.floor(diffDays / 30); - const extraDays = diffDays % 30; - - this.setData({ - intervalDays: diffDays, - intervalWeeks: weeks, - intervalRemainingDays: remainingDays, - intervalMonths: months, - intervalExtraDays: extraDays - }); - } - }, - - // 更新推算结果的农历信息 - updateResultLunar(date: Date) { - try { - const lunar = solarToLunar(date.getFullYear(), date.getMonth() + 1, date.getDate()); - this.setData({ - resultLunar: lunar.monthName + lunar.dayName - }); - } catch (e) { - this.setData({ resultLunar: '' }); - } - }, - - // === 农历转换相关 === - - setLunarDirection(e: any) { - wx.vibrateShort({ type: 'light' }).catch(() => {}); - this.setData({ lunarDirection: e.currentTarget.dataset.dir }); - }, - - toggleLunarDirection() { - wx.vibrateShort({ type: 'light' }).catch(() => {}); - this.setData({ - lunarDirection: this.data.lunarDirection === 'toL' ? 'toS' : 'toL' - }); - }, - - onLunarSolarDateChange(e: any) { - const dateStr = e.detail.value; - this.setData({ lunarSolarDate: dateStr }); - this.convertSolarToLunar(dateStr); - }, - - convertSolarToLunar(dateStr: string) { - try { - const parts = dateStr.split('-'); - const year = parseInt(parts[0]); - const month = parseInt(parts[1]); - const day = parseInt(parts[2]); - - const result = solarToLunar(year, month, day); - const term = getSolarTerm(year, month, day); - const solarFest = getFestival(month, day); - const lunarFest = getLunarFestival(result.month, result.day); - - this.setData({ - lunarResult: result, - solarTermText: term || '', - solarFestivalText: solarFest || '', - lunarFestivalText: lunarFest || '' - }); - } catch (e) { - console.error('农历转换出错:', e); - } - }, - - // 初始化农历选择器 - initLunarPicker() { - const years: string[] = []; - for (let y = 1901; y <= 2099; y++) { - years.push(y + '年'); - } - const months: string[] = []; - for (let m = 1; m <= 12; m++) { - months.push(this.getLunarMonthLabel(m)); - } - const days: string[] = []; - for (let d = 1; d <= 30; d++) { - days.push(this.getLunarDayLabel(d)); - } - - const today = new Date(); - - // Calculate today's lunar date to set default picker value - const result = solarToLunar(today.getFullYear(), today.getMonth() + 1, today.getDate()); - const yearIdx = result.year - 1901; - const monthIdx = result.month - 1; - const dayIdx = result.day - 1; - - const display = `${result.year}年 ${result.monthName} ${result.dayName}`; - - this.setData({ - lunarPickerRange: [years, months, days], - lunarPickerValue: [yearIdx >= 0 ? yearIdx : 0, monthIdx, dayIdx], - lunarPickerDisplay: display - }); - - // Also init the reverse calculation result - this.calcLunarToSolar(result.year, result.month, result.day); - }, - - getLunarMonthLabel(m: number): string { - const names = ['正月', '二月', '三月', '四月', '五月', '六月', '七月', '八月', '九月', '十月', '冬月', '腊月']; - return names[m - 1]; - }, - - getLunarDayLabel(d: number): string { - const names = [ - '初一', '初二', '初三', '初四', '初五', '初六', '初七', '初八', '初九', '初十', - '十一', '十二', '十三', '十四', '十五', '十六', '十七', '十八', '十九', '二十', - '廿一', '廿二', '廿三', '廿四', '廿五', '廿六', '廿七', '廿八', '廿九', '三十' - ]; - return names[d - 1]; - }, - - onLunarColumnChange(e: any) { - // 列变化时可以联动更新(简化处理) - }, - - onLunarPickerChange(e: any) { - const val = e.detail.value; - const year = 1901 + val[0]; - const month = val[1] + 1; - const day = val[2] + 1; - - const display = `${year}年 ${this.getLunarMonthLabel(month)} ${this.getLunarDayLabel(day)}`; - - this.setData({ - lunarPickerValue: val, - lunarPickerDisplay: display, - }); - - this.calcLunarToSolar(year, month, day); - }, - - calcLunarToSolar(year: number, month: number, day: number) { - try { - const result = lunarToSolar(year, month, day, false); - const solarDate = new Date(result.year, result.month - 1, result.day); - const solarStr = this.formatDate(solarDate); - - this.setData({ - lunarToSolarResult: solarStr, - lunarToSolarWeekday: this.getWeekday(solarDate) - }); - } catch (e) { - this.setData({ - lunarToSolarResult: '无效日期', - lunarToSolarWeekday: '' - }); - } - }, - - // 今日信息 - updateTodayInfo() { - const today = new Date(); - const y = today.getFullYear(); - const m = today.getMonth() + 1; - const d = today.getDate(); - - try { - const lunar = solarToLunar(y, m, d); - const term = getSolarTerm(y, m, d); - - this.setData({ - todaySolar: `${y}年${m}月${d}日 ${this.getWeekday(today)}`, - todayLunar: lunar.monthName + lunar.dayName, - todayGanZhi: lunar.ganZhi + '年', - todayAnimal: lunar.animal, - todayTerm: term || '' - }); - } catch (e) { - console.error('获取今日信息出错:', e); - } - }, - - 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 deleted file mode 100644 index 94e3727..0000000 --- a/codes/minipro/calculation/miniprogram/pages/date-calc/date-calc.wxml +++ /dev/null @@ -1,221 +0,0 @@ - - - - - - - - - 功能说明 - 关闭 - - - - - 📅 - - 日期推算 - 设定一个开始日期,输入往后(+)或往前(-)推算的天数,即可计算出目标日期。底部提供常用快捷按钮。 - - - - - - 间隔计算 - 选择两个日期,计算它们之间相差多少天,并自动换算为周数和月数。 - - - - 🌙 - - 农历转换 - 支持公历(阳历)与农历(阴历)互转,并显示干支纪年、生肖、节气等传统信息。 - - - - - - - - - - - 日期推算 - 间隔计算 - 农历转换 - - - - - - - - 开始日期 - - - - {{startDate || '请选择'}} - {{startWeekday}} - - - - - - - - - 推算天数 - - - - - - +1周 - +1月 - +百日 - +1年 - -1天 - -1周 - - - - - - 推算结果 - {{resultDate}} - {{resultWeekday}} - 🌙 农历 {{resultLunar}} - - - - - - - - - 起始日期 - - - {{startDate}} - - - - - - - - - - - - 结束日期 - - - {{endDate}} - - - - - - - - 时间跨度 - - {{intervalDays}} - - - - ≈ {{intervalWeeks}} 周{{intervalRemainingDays > 0 ? ' ' + intervalRemainingDays + ' 天' : ''}} - | - ≈ {{intervalMonths}} 个月{{intervalExtraDays > 0 ? ' ' + intervalExtraDays + ' 天' : ''}} - - - - - - - - - - - {{lunarDirection === 'toL' ? '选择公历日期' : '选择农历日期'}} - - 切换为 {{lunarDirection === 'toL' ? '农历→公历' : '公历→农历'}} - - - - - - - - - {{lunarSolarDate || '请选择'}} - - - - - - - - - - {{lunarPickerDisplay || '请选择'}} - - - - - - - - - - - - 对应农历 - {{lunarResult.monthName}}{{lunarResult.dayName}} - {{lunarResult.ganZhi}}年 · {{lunarResult.animal}}年 - - 🎊 {{lunarFestivalText}} - 🌿 {{solarTermText}} - 🎉 {{solarFestivalText}} - - - - - - 对应公历 - {{lunarToSolarResult}} - {{lunarToSolarWeekday}} - - - - - - - 📌 今日 - - - 公历 - {{todaySolar}} - - - 农历 - {{todayLunar}} - - - 干支 - {{todayGanZhi}} - - - 生肖 - {{todayAnimal}} - - - - 🌿 - 今日节气:{{todayTerm}} - - - - - diff --git a/codes/minipro/calculation/miniprogram/pages/date-calc/date-calc.wxss b/codes/minipro/calculation/miniprogram/pages/date-calc/date-calc.wxss deleted file mode 100644 index 0c8cc93..0000000 --- a/codes/minipro/calculation/miniprogram/pages/date-calc/date-calc.wxss +++ /dev/null @@ -1,664 +0,0 @@ -/* pages/date-calc/date-calc.wxss */ - -/* ========== 全局变量 ========== */ -page { - background-color: #EBF4F8; /* Color 03: Pale Blue */ - color: #2d3436; - - /* Color Palette based on '2024 COLOR MATCHING' */ - --primary: #11616B; /* Color 01: Deep Teal */ - --primary-light: #7BBDB6; /* Color 02: Medium Teal */ - --primary-bg: #dbecef; /* Lighter version of Color 02 for backgrounds */ - --card-shadow: 0 4rpx 16rpx rgba(17, 97, 107, 0.08); - --card-radius: 24rpx; - - --accent-color: #11616B; /* Color 01 */ -} - -/* ========== 容器 ========== */ -.container { - padding: 30rpx 32rpx; - padding-bottom: 80rpx; - min-height: 100vh; - box-sizing: border-box; -} - -/* ========== Tab 切换 ========== */ -.tab-header { - display: flex; - background: #ffffff; /* Changed to white */ - padding: 6rpx; - border-radius: 16rpx; /* Reduced radius */ - margin-bottom: 40rpx; - align-items: center; - gap: 4rpx; /* Reduced gap */ - box-shadow: 0 4rpx 12rpx rgba(17, 97, 107, 0.1); -} - -.tab-pill { - flex: 1; - text-align: center; - padding: 16rpx 8rpx; /* Added horizontal padding */ - border-radius: 12rpx; /* Reduced radius */ - font-size: 24rpx; /* Reduced font size to create more space */ - color: #7BBDB6; /* Color 02 */ - font-weight: 500; - white-space: nowrap; - transition: all 0.2s ease; - position: relative; - z-index: 1; -} - -.help-btn { - width: 64rpx; /* Reduced width */ - height: 64rpx; /* Reduced height */ - display: flex; - align-items: center; - justify-content: center; - font-size: 36rpx; - color: #11616B; -} - -/* Modal Styles Copied & Adapted */ -.history-panel { - position: fixed; - top: 0; - left: 0; - right: 0; - bottom: 0; - z-index: -1; - opacity: 0; - transition: all 0.3s cubic-bezier(0.32, 0.72, 0, 1); - pointer-events: none; - visibility: hidden; -} - -.history-panel.show { - z-index: 1000; - opacity: 1; - pointer-events: auto; - visibility: visible; -} - -.history-mask { - position: absolute; - top: 0; - left: 0; - width: 100%; - height: 100%; - background: rgba(0, 0, 0, 0.4); - backdrop-filter: blur(4px); -} - -.history-content { - position: absolute; - bottom: 0; - left: 0; - right: 0; - background: #EBF4F8; /* Color 03 */ - border-radius: 36rpx 36rpx 0 0; - max-height: 80vh; - display: flex; - flex-direction: column; - box-shadow: 0 -8rpx 30rpx rgba(17, 97, 107, 0.15); - transform: translateY(100%); - transition: transform 0.3s cubic-bezier(0.32, 0.72, 0, 1); -} - -.history-panel.show .history-content { - transform: translateY(0); -} - -.history-header { - display: flex; - justify-content: space-between; - align-items: center; - padding: 36rpx 40rpx; - border-bottom: 2rpx solid rgba(17, 97, 107, 0.1); -} - -.history-title { - color: #11616B; /* Color 01 */ - font-size: 34rpx; - font-weight: 600; - letter-spacing: 1rpx; -} - -.history-clear { - color: #DC8B70; /* Color 05 */ - font-size: 28rpx; - padding: 8rpx 24rpx; - border-radius: 20rpx; - background: rgba(220, 139, 112, 0.15); /* Color 05 faint */ -} - -/* Help specific */ -.help-list { - padding: 0; -} - -.help-section { - display: flex; - flex-direction: column; - gap: 40rpx; - padding: 30rpx 40rpx 60rpx; - box-sizing: border-box; -} - -.help-item { - display: flex; - gap: 24rpx; - align-items: flex-start; -} - -.help-icon { - font-size: 44rpx; - width: 60rpx; - text-align: center; - padding-top: 4rpx; -} - -.help-text { - flex: 1; - display: flex; - flex-direction: column; -} - -.help-h1 { - font-size: 30rpx; - font-weight: bold; - color: #11616B; /* Color 01 */ - margin-bottom: 12rpx; -} - -.help-p { - font-size: 26rpx; - color: #2d3436; - line-height: 1.6; - opacity: 0.8; -} - -.share-btn-large { - background-color: #11616B !important; /* Color 01 */ - color: white !important; - border-radius: 16rpx !important; - font-size: 30rpx !important; - font-weight: 500 !important; - margin-top: 40rpx; - width: 100% !important; - box-shadow: 0 8rpx 20rpx rgba(17, 97, 107, 0.25); - display: flex; - align-items: center; - justify-content: center; - padding: 24rpx 0; -} - -.tab-pill.active { - background: #11616B; /* Color 01 */ - color: #ffffff; - font-weight: 600; - box-shadow: 0 2rpx 8rpx rgba(17, 97, 107, 0.2); - transform: none; -} - -.tab-hover { - background: rgba(0, 0, 0, 0.05); /* 简单的灰色点击态 */ -} - -.tab-pill.active.tab-hover { - opacity: 0.9; - background: var(--accent-gradient); /* 保持渐变 */ -} - -/* ========== 通用卡片 ========== */ -.card { - background: #fff; - border-radius: var(--card-radius); - padding: 32rpx; - margin-bottom: 24rpx; - box-shadow: 0 8rpx 24rpx rgba(0, 0, 0, 0.04); /* 更柔和的弥散阴影 */ - position: relative; - transition: transform 0.2s; -} - -/* 移除旧的顶部边框,使用更现代的左侧强调条 */ -.input-card { - border-left: 8rpx solid var(--primary); -} - -.card-label { - font-size: 23rpx; - color: #b2bec3; - margin-bottom: 14rpx; - letter-spacing: 1rpx; - font-weight: 600; -} - -.card-divider { - height: 1rpx; - background: #f0f0f5; - margin: 20rpx 0; -} - -/* ========== 日期选择栏(通用) ========== */ -.date-picker-bar { - display: flex; - align-items: center; - justify-content: space-between; - background: #f8f9ff; - border-radius: 16rpx; - padding: 20rpx 24rpx; - border: 1rpx solid #eef0f8; -} - -.picker-bar-hover { - background: var(--primary-bg); - border-color: var(--primary-light); -} - -.picker-left { - display: flex; - align-items: baseline; - gap: 14rpx; -} - -.picker-date { - font-size: 36rpx; - font-weight: 700; - color: #2d3436; -} - -.picker-weekday { - font-size: 24rpx; - color: var(--primary); - background: var(--primary-bg); - padding: 4rpx 14rpx; - border-radius: 10rpx; - font-weight: 600; -} - -.picker-arrow { - color: var(--primary-light); - font-size: 30rpx; - width: 48rpx; - height: 48rpx; - display: flex; - align-items: center; - justify-content: center; - background: var(--primary-bg); - border-radius: 50%; - flex-shrink: 0; -} - -/* ========== 天数输入(模式1) ========== */ -.days-input-group { - display: flex; - align-items: center; - background: #f8f9ff; - border-radius: 16rpx; - padding: 8rpx 24rpx; - margin-bottom: 20rpx; - border: 1rpx solid #eef0f8; -} - -.days-input { - flex: 1; - font-size: 42rpx; - font-weight: 700; - color: var(--primary); - height: 72rpx; - font-family: -apple-system, 'SF Pro Display', 'Helvetica Neue', sans-serif; -} - -.days-suffix { - font-size: 26rpx; - color: #b2bec3; - font-weight: 500; - flex-shrink: 0; -} - -/* ========== 快捷标签 ========== */ -.quick-tags { - display: flex; - flex-wrap: wrap; - gap: 14rpx; -} - -.tag { - padding: 12rpx 26rpx; - border-radius: 28rpx; - font-size: 24rpx; - font-weight: 600; - border: 1rpx solid transparent; -} - -.tag-plus { - background: #EBF4F8; /* Color 03 */ - color: #11616B; /* Color 01 */ - border-color: #7BBDB6; /* Color 02 */ -} - -.tag-minus { - background: #FED9CD; /* Color 04 */ - color: #DC8B70; /* Color 05 */ - border-color: #fceceb; -} - -.tag-hover { - background: #7BBDB6 !important; /* Color 02 */ - color: #ffffff !important; - border-color: #11616B !important; - transform: scale(0.95); -} - -.tag-minus-hover { - background: #DC8B70 !important; /* Color 05 */ - color: #ffffff !important; - transform: scale(0.95); -} - -/* ========== 结果卡片(通用) ========== */ -.result-card { - background: var(--accent-color); /* Color 01 */ - color: #fff; - text-align: center; - padding: 40rpx 32rpx; - border-radius: var(--card-radius); - margin-bottom: 24rpx; - box-shadow: 0 8rpx 24rpx rgba(17, 97, 107, 0.25); /* Teal shadow */ - position: relative; - overflow: hidden; -} - -.result-card::after { - content: ''; - position: absolute; - top: -50rpx; - right: -50rpx; - width: 160rpx; - height: 160rpx; - border-radius: 50%; - background: rgba(255,255,255,0.1); -} - -.result-label { - font-size: 21rpx; - opacity: 0.7; - margin-bottom: 14rpx; - letter-spacing: 2rpx; - font-weight: 600; -} - -.result-main { - font-size: 48rpx; - font-weight: 700; - margin-bottom: 6rpx; - letter-spacing: 1rpx; -} - -.result-extra { - font-size: 28rpx; - opacity: 0.85; - font-weight: 500; -} - -.result-tag { - font-size: 24rpx; - opacity: 0.8; - margin-top: 12rpx; - background: rgba(255,255,255,0.12); - display: inline-block; - padding: 6rpx 18rpx; - border-radius: 14rpx; -} - -/* ========== 模式2:日期间隔 (Compact) ========== */ -.interval-card-compact { - display: flex; - align-items: center; - justify-content: space-between; - padding: 40rpx 24rpx; /* Increase vertical padding */ -} - -.iv-col { - flex: 1; - display: flex; - flex-direction: column; - align-items: center; -} - -.iv-small-label { - font-size: 24rpx; - color: #b2bec3; - margin-bottom: 16rpx; - font-weight: 500; -} - -.iv-pill { - background: #f8f9ff; - padding: 16rpx 32rpx; /* Wider padding */ - border-radius: 20rpx; - font-size: 34rpx; - font-weight: 600; - color: #2d3436; - border: 1rpx solid #eef0f8; - white-space: nowrap; - box-shadow: 0 2rpx 6rpx rgba(0,0,0,0.02); - transition: all 0.2s; -} - -.iv-pill-hover { - background: var(--primary-bg); - border-color: var(--primary-light); - color: var(--primary); - transform: translateY(-2rpx); -} - -.iv-connector { - width: 80rpx; - display: flex; - justify-content: center; - align-items: center; - padding-top: 40rpx; /* Align with text */ -} - -.iv-arrow-icon { - font-size: 32rpx; - color: #b2bec3; - font-weight: 700; -} - -/* 间隔结果 */ -.interval-num-row { - display: flex; - align-items: baseline; - justify-content: center; - margin-bottom: 8rpx; -} - -.interval-big-num { - font-size: 72rpx; - font-weight: 700; - letter-spacing: 2rpx; -} - -.interval-big-unit { - font-size: 28rpx; - font-weight: 400; - margin-left: 8rpx; - opacity: 0.75; -} - -.result-detail-row { - display: flex; - align-items: center; - justify-content: center; - gap: 16rpx; - margin-top: 8rpx; - opacity: 0.8; - font-size: 24rpx; -} - -.result-detail { - font-size: 24rpx; -} - -.result-detail-sep { - opacity: 0.4; - font-size: 20rpx; -} - -/* ========== 模式3:农历转换 ========== */ - -/* 方向切换(行内文字链接样式 -> 按钮样式) */ -.lunar-header-row { - display: flex; - align-items: center; - justify-content: space-between; - margin-bottom: 24rpx; -} - -.dir-switch { - display: flex; - align-items: center; - gap: 8rpx; - padding: 12rpx 24rpx; - border-radius: 12rpx; /* Reduced radius */ - background: var(--primary-bg); - transition: all 0.2s; - border: 1rpx solid transparent; /* 防止抖动 */ -} - -.dir-switch-hover { - background: #e2e0ff; /* slightly darker primary-bg */ - transform: scale(0.98); -} - -.dir-switch-text { - font-size: 24rpx; - color: var(--primary); - font-weight: 700; -} - -.dir-switch-icon { - font-size: 24rpx; - color: var(--primary); - font-weight: 700; -} - -/* 农历结果卡片 */ -.lunar-result-card { - text-align: center; -} - -.lunar-hero { - font-size: 48rpx; - font-weight: 700; - margin-bottom: 6rpx; - letter-spacing: 3rpx; -} - -.lunar-sub { - font-size: 26rpx; - opacity: 0.8; - margin-bottom: 16rpx; - font-weight: 500; -} - -.lunar-tags { - display: flex; - flex-wrap: wrap; - gap: 10rpx; - justify-content: center; - border-top: 1rpx solid rgba(255,255,255,0.12); - padding-top: 16rpx; -} - -.lunar-tag { - background: rgba(255,255,255,0.12); - border-radius: 12rpx; - padding: 8rpx 20rpx; - font-size: 24rpx; - font-weight: 600; -} - -.lunar-tag.festival { - color: #ffeaa7; -} - -.lunar-tag.term { - color: #55efc4; -} - -.lunar-tag.solar { - color: #fff; -} - -/* ========== 今日信息 ========== */ -.today-card { - background: #fff; - border-radius: var(--card-radius); - padding: 28rpx 32rpx; - margin-bottom: 20rpx; - box-shadow: var(--card-shadow); - border-left: 5rpx solid var(--primary); -} - -.today-title { - font-size: 28rpx; - font-weight: 700; - color: #2d3436; - margin-bottom: 16rpx; -} - -.today-grid { - display: grid; - grid-template-columns: 1fr 1fr; - gap: 14rpx; - margin-bottom: 4rpx; -} - -.today-cell { - background: #f8f9ff; - border-radius: 14rpx; - padding: 16rpx 20rpx; - display: flex; - flex-direction: column; - gap: 6rpx; -} - -.today-cell-label { - font-size: 21rpx; - color: #b2bec3; - font-weight: 600; -} - -.today-cell-value { - font-size: 28rpx; - color: #2d3436; - font-weight: 700; -} - -.today-cell-value.accent { - color: var(--primary); -} - -.today-term-bar { - display: flex; - align-items: center; - gap: 8rpx; - margin-top: 14rpx; - background: var(--primary-bg); - padding: 14rpx 20rpx; - border-radius: 14rpx; -} - -.term-icon { - font-size: 24rpx; -} - -.term-text { - font-size: 25rpx; - color: var(--primary); - font-weight: 600; -} diff --git a/codes/minipro/calculation/miniprogram/pages/discount/discount.json b/codes/minipro/calculation/miniprogram/pages/discount/discount.json new file mode 100644 index 0000000..9cc74b3 --- /dev/null +++ b/codes/minipro/calculation/miniprogram/pages/discount/discount.json @@ -0,0 +1,4 @@ +{ + "navigationBarTitleText": "折扣计算", + "usingComponents": {} +} \ No newline at end of file diff --git a/codes/minipro/calculation/miniprogram/pages/discount/discount.ts b/codes/minipro/calculation/miniprogram/pages/discount/discount.ts new file mode 100644 index 0000000..cb39102 --- /dev/null +++ b/codes/minipro/calculation/miniprogram/pages/discount/discount.ts @@ -0,0 +1,53 @@ +Page({ + data: { + originalPrice: '', + discount: '', + finalPrice: '0.00', + savedMoney: '0.00' + }, + + bindOriginalInput(e: any) { + this.setData({ + originalPrice: e.detail.value + }); + this.calculate(); + }, + + bindDiscountInput(e: any) { + this.setData({ + discount: e.detail.value + }); + this.calculate(); + }, + + calculate() { + const original = parseFloat(this.data.originalPrice); + const discount = parseFloat(this.data.discount); // Assume local style 8.8折 or international %? Let's assume N折 (0-10) + + if (isNaN(original) || isNaN(discount)) { + this.setData({ + finalPrice: '0.00', + savedMoney: '0.00' + }); + return; + } + + // Logic: Input 9 = 9折 = 90% of price. Input 8.5 = 85%. + // If input > 10, maybe it means percentage? e.g. 80 = 80%? + // Let's stick to N折 standard in China. + + let rate = discount; + if (rate > 10) { + rate = rate / 10.0; + } + + // rate is now 0-10. e.g. 8.8 + const final = original * (rate / 10.0); + const saved = original - final; + + this.setData({ + finalPrice: final.toFixed(2), + savedMoney: saved.toFixed(2) + }); + } +}); \ No newline at end of file diff --git a/codes/minipro/calculation/miniprogram/pages/discount/discount.wxml b/codes/minipro/calculation/miniprogram/pages/discount/discount.wxml new file mode 100644 index 0000000..39faec7 --- /dev/null +++ b/codes/minipro/calculation/miniprogram/pages/discount/discount.wxml @@ -0,0 +1,28 @@ + + + + 原价 (元) + + + + + 折扣 (折) + + + + + + + 折后价 + ¥ {{finalPrice}} + + + 为您节省 + ¥ {{savedMoney}} + + + + + 输入提示:输入 9 代表 9 折,8.5 代表 85 折 + + \ No newline at end of file diff --git a/codes/minipro/calculation/miniprogram/pages/discount/discount.wxss b/codes/minipro/calculation/miniprogram/pages/discount/discount.wxss new file mode 100644 index 0000000..7939665 --- /dev/null +++ b/codes/minipro/calculation/miniprogram/pages/discount/discount.wxss @@ -0,0 +1,86 @@ +page { + background-color: #EBF4F8; /* Color 03 */ + padding: 30rpx; + box-sizing: border-box; +} + +.card { + background-color: #fff; + border-radius: 20rpx; + padding: 0 30rpx; + margin-bottom: 30rpx; + box-shadow: 0 4rpx 12rpx rgba(17, 97, 107, 0.08); +} + +.input-group { + padding: 30rpx 0; + display: flex; + align-items: center; + justify-content: space-between; +} + +.label { + font-size: 32rpx; + color: #11616B; /* Color 01 */ + width: 180rpx; + font-weight: 500; +} + +input { + flex: 1; + text-align: right; + font-size: 36rpx; + color: #11616B; /* Color 01 */ + font-weight: 500; +} + +.divider { + height: 1rpx; + background-color: rgba(17, 97, 107, 0.1); +} + +.result-card { + background: linear-gradient(135deg, #11616B 0%, #0D4E56 100%); /* Color 01 based gradient */ + border-radius: 20rpx; + padding: 40rpx; + color: #fff; + box-shadow: 0 6rpx 16rpx rgba(17, 97, 107, 0.3); +} + +.result-row { + display: flex; + justify-content: space-between; + align-items: center; + margin-bottom: 20rpx; +} + +.result-label { + font-size: 30rpx; + opacity: 0.9; + color: #EBF4F8; /* Color 03 */ +} + +.result-value { + font-size: 56rpx; + font-weight: bold; + color: #ffffff; +} + +.sub-result { + margin-top: 30rpx; + margin-bottom: 0; + padding-top: 20rpx; + border-top: 1rpx solid rgba(235, 244, 248, 0.2); +} + +.sub-result .result-value { + font-size: 36rpx; + color: #FED9CD; /* Color 04 for highlight */ +} + +.tips { + margin-top: 40rpx; + text-align: center; + font-size: 24rpx; + color: #7BBDB6; /* Color 02 */ +} diff --git a/codes/minipro/calculation/miniprogram/pages/history/history.json b/codes/minipro/calculation/miniprogram/pages/history/history.json new file mode 100644 index 0000000..2ccdc60 --- /dev/null +++ b/codes/minipro/calculation/miniprogram/pages/history/history.json @@ -0,0 +1,4 @@ +{ + "navigationBarTitleText": "计算历史", + "usingComponents": {} +} \ No newline at end of file diff --git a/codes/minipro/calculation/miniprogram/pages/history/history.ts b/codes/minipro/calculation/miniprogram/pages/history/history.ts new file mode 100644 index 0000000..6665ae7 --- /dev/null +++ b/codes/minipro/calculation/miniprogram/pages/history/history.ts @@ -0,0 +1,28 @@ + +Page({ + data: { + historyList: [] + }, + + onShow() { + const list = wx.getStorageSync('CALC_HISTORY') || []; + this.setData({ + historyList: list + }); + }, + + clearHistory() { + wx.showModal({ + title: '提示', + content: '确定要清空所有记录吗?', + success: (res) => { + if (res.confirm) { + wx.removeStorageSync('CALC_HISTORY'); + this.setData({ + historyList: [] + }); + } + } + }); + } +}); \ No newline at end of file diff --git a/codes/minipro/calculation/miniprogram/pages/history/history.wxml b/codes/minipro/calculation/miniprogram/pages/history/history.wxml new file mode 100644 index 0000000..84b6a23 --- /dev/null +++ b/codes/minipro/calculation/miniprogram/pages/history/history.wxml @@ -0,0 +1,22 @@ + + + + + {{item.expression}} + = {{item.result}} + + + + + + + + + + + 📭 + 暂无历史记录 + 快去使用计算器吧 + + + \ No newline at end of file diff --git a/codes/minipro/calculation/miniprogram/pages/history/history.wxss b/codes/minipro/calculation/miniprogram/pages/history/history.wxss new file mode 100644 index 0000000..153cfdb --- /dev/null +++ b/codes/minipro/calculation/miniprogram/pages/history/history.wxss @@ -0,0 +1,84 @@ +page { + background-color: #EBF4F8; /* Color 03 */ +} + +.container { + padding: 30rpx; + padding-bottom: 120rpx; +} + +.history-list { + width: 100%; + display: flex; + flex-direction: column; + gap: 20rpx; +} + +.history-item { + background-color: #fff; + padding: 30rpx; + border-radius: 20rpx; + box-shadow: 0 4rpx 12rpx rgba(17, 97, 107, 0.08); +} + +.expression { + font-size: 28rpx; + color: #7BBDB6; /* Color 02 */ + margin-bottom: 10rpx; + text-align: right; +} + +.result { + font-size: 40rpx; + color: #11616B; /* Color 01 */ + font-weight: bold; + text-align: right; +} + +.footer-btn { + position: fixed; + bottom: 40rpx; + left: 0; + width: 100%; + padding: 0 40rpx; + box-sizing: border-box; +} + +.clear-btn { + background-color: #DC8B70; /* Color 05 - Accent */ + color: #fff; + border-radius: 40rpx; + font-size: 30rpx; + box-shadow: 0 8rpx 20rpx rgba(220, 139, 112, 0.4); +} + +.btn-hover { + opacity: 0.9; + transform: scale(0.98); +} + +.empty-state { + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + margin-top: 200rpx; +} + +.empty-icon { + font-size: 100rpx; + margin-bottom: 30rpx; + color: #7BBDB6; /* Color 02 */ +} + +.empty-text { + font-size: 34rpx; + color: #11616B; /* Color 01 */ + margin-bottom: 10rpx; + font-weight: bold; +} + +.empty-sub { + font-size: 26rpx; + color: #7BBDB6; /* Color 02 */ +} diff --git a/codes/minipro/calculation/miniprogram/pages/index/index.ts b/codes/minipro/calculation/miniprogram/pages/index/index.ts index cc1e836..9d41eb3 100644 --- a/codes/minipro/calculation/miniprogram/pages/index/index.ts +++ b/codes/minipro/calculation/miniprogram/pages/index/index.ts @@ -47,7 +47,7 @@ Component({ }, goToProfile() { - wx.navigateTo({ + wx.switchTab({ url: '/pages/profile/profile' }); }, @@ -64,21 +64,28 @@ Component({ }); }, - goToRandomDraw() { + goToDiscount() { wx.navigateTo({ - url: '/pages/random-draw/random-draw' + url: '/pages/discount/discount', }); }, - goToScoreboard() { + + goToHistory() { wx.navigateTo({ - url: '/pages/scoreboard/scoreboard' + url: '/pages/history/history', }); }, - - goToDateCalc() { + + goToAbout() { wx.navigateTo({ - url: '/pages/date-calc/date-calc' + url: '/pages/about/about', + }); + }, + + goToPrivacy() { + wx.navigateTo({ + url: '/pages/privacy/privacy', }); } } diff --git a/codes/minipro/calculation/miniprogram/pages/index/index.wxml b/codes/minipro/calculation/miniprogram/pages/index/index.wxml index d7a120e..332cdaa 100644 --- a/codes/minipro/calculation/miniprogram/pages/index/index.wxml +++ b/codes/minipro/calculation/miniprogram/pages/index/index.wxml @@ -33,17 +33,47 @@ - - - - 📅 + + + + 📏 - 日期计算器 - 日期推算 · 间隔计算 · 农历转换 + 单位换算 + 长度 · 重量 · 面积 · 温度 + + + + + 🏷️ + + + 折扣计算 + 快速计算折后价与节省金额 + + + + + + + 系统 + + + + + ℹ️ + + 关于与帮助 + + + + + + + 隐私政策 diff --git a/codes/minipro/calculation/miniprogram/pages/index/index.wxss b/codes/minipro/calculation/miniprogram/pages/index/index.wxss index dcdc528..6a94509 100644 --- a/codes/minipro/calculation/miniprogram/pages/index/index.wxss +++ b/codes/minipro/calculation/miniprogram/pages/index/index.wxss @@ -258,3 +258,55 @@ page { font-size: 14rpx; color: #6c5ce7; } + +/* 列表容器 */ +.list-container { + background-color: #ffffff; + border-radius: 20rpx; + overflow: hidden; + box-shadow: 0 4rpx 16rpx rgba(17, 97, 107, 0.08); +} + +.list-item { + display: flex; + align-items: center; + padding: 30rpx; + background-color: #ffffff; + transition: background-color 0.1s; +} + +.list-item-hover { + background-color: #f9f9f9; +} + +.list-icon-box { + width: 56rpx; + height: 56rpx; + background-color: #EBF4F8; + border-radius: 16rpx; + display: flex; + align-items: center; + justify-content: center; + margin-right: 24rpx; +} + +.icon-small { + font-size: 32rpx; +} + +.list-label { + flex: 1; + font-size: 30rpx; + color: #333; + font-weight: 500; +} + +.section-title { + display: block !important; /* Ensure visibility */ + font-size: 28rpx; + color: #999; + margin-bottom: 20rpx; + margin-left: 10rpx; + font-weight: 500; +} + diff --git a/codes/minipro/calculation/miniprogram/pages/privacy/privacy.json b/codes/minipro/calculation/miniprogram/pages/privacy/privacy.json new file mode 100644 index 0000000..74279fa --- /dev/null +++ b/codes/minipro/calculation/miniprogram/pages/privacy/privacy.json @@ -0,0 +1,4 @@ +{ + "navigationBarTitleText": "隐私政策", + "usingComponents": {} +} \ No newline at end of file diff --git a/codes/minipro/calculation/miniprogram/pages/privacy/privacy.ts b/codes/minipro/calculation/miniprogram/pages/privacy/privacy.ts new file mode 100644 index 0000000..e778e37 --- /dev/null +++ b/codes/minipro/calculation/miniprogram/pages/privacy/privacy.ts @@ -0,0 +1,2 @@ +Page({ +}); \ No newline at end of file diff --git a/codes/minipro/calculation/miniprogram/pages/privacy/privacy.wxml b/codes/minipro/calculation/miniprogram/pages/privacy/privacy.wxml new file mode 100644 index 0000000..5357a76 --- /dev/null +++ b/codes/minipro/calculation/miniprogram/pages/privacy/privacy.wxml @@ -0,0 +1,23 @@ + + + 隐私政策 + 生效日期:2024年1月1日 + + 1. 引言 + “道棋百宝箱”(以下称为“本小程序”)非常重视您的隐私。本隐私政策旨在说明我们在您使用本小程序提供的服务时,如何处理您的信息。 + + 2. 信息收集 + 本小程序作为一款实用工具类产品,不收集、不存储、不上传任何您的个人身份信息(如姓名、电话、地址等)。 + 您在使用计算器、单位换算等功能时产生的数据(如计算历史、设置偏好),仅存储在您设备的本地缓存中,此时不会上传至任何服务器。 + + 3. 权限使用 + 为了提供完整的服务,本小程序可能会申请以下权限,您可以拒绝授权,拒绝后不影响基本功能的使用: + • 获取用户信息(头像/昵称):仅用于在个人中心页展示您的头像和昵称,增强用户体验。您可以选择不授权。 + + 4. 数据安全 + 由于我们不收集您的个人信息,因此不存在信息泄露的风险。您的本地数据受微信客户端的安全机制保护。 + + 5. 联系我们 + 如果您对本隐私政策有任何疑问,请通过小程序“关于”或者意见反馈功能联系我们。 + + \ No newline at end of file diff --git a/codes/minipro/calculation/miniprogram/pages/privacy/privacy.wxss b/codes/minipro/calculation/miniprogram/pages/privacy/privacy.wxss new file mode 100644 index 0000000..4efeee5 --- /dev/null +++ b/codes/minipro/calculation/miniprogram/pages/privacy/privacy.wxss @@ -0,0 +1,53 @@ +page { + background-color: #EBF4F8; /* Color 03 */ + color: #333; +} + +.container { + padding: 40rpx; +} + +.content { + width: 100%; + background-color: #fff; + padding: 40rpx; + border-radius: 20rpx; + box-shadow: 0 4rpx 12rpx rgba(17, 97, 107, 0.05); +} + +.h1 { + font-size: 44rpx; + font-weight: bold; + margin-bottom: 40rpx; + text-align: center; + color: #11616B; /* Color 01 */ +} + +.h2 { + font-size: 32rpx; + font-weight: bold; + margin-top: 40rpx; + margin-bottom: 20rpx; + color: #11616B; /* Color 01 */ +} + +.p { + font-size: 28rpx; + color: #555; + line-height: 1.8; + margin-bottom: 20rpx; + text-align: justify; +} + +.list-item { + font-size: 28rpx; + color: #555; + line-height: 1.8; + margin-bottom: 10rpx; + padding-left: 20rpx; +} + +.bold { + font-weight: bold; + color: #11616B; /* Color 01 */ +} diff --git a/codes/minipro/calculation/miniprogram/pages/profile/profile.json b/codes/minipro/calculation/miniprogram/pages/profile/profile.json index c77dc7a..f10a42b 100644 --- a/codes/minipro/calculation/miniprogram/pages/profile/profile.json +++ b/codes/minipro/calculation/miniprogram/pages/profile/profile.json @@ -1,4 +1,5 @@ { "usingComponents": { + "privacy-popup": "/components/privacy-popup/privacy-popup" } } diff --git a/codes/minipro/calculation/miniprogram/pages/profile/profile.ts b/codes/minipro/calculation/miniprogram/pages/profile/profile.ts index 9a37ebe..2602a9e 100644 --- a/codes/minipro/calculation/miniprogram/pages/profile/profile.ts +++ b/codes/minipro/calculation/miniprogram/pages/profile/profile.ts @@ -96,6 +96,19 @@ Component({ } }, + // 页面跳转方法 + goToHistory() { + wx.navigateTo({ url: '/pages/history/history' }); + }, + + goToPrivacy() { + wx.navigateTo({ url: '/pages/privacy/privacy' }); + }, + + goToAbout() { + wx.navigateTo({ url: '/pages/about/about' }); + }, + // 弹出设置菜单 onSettingsTap() { wx.showActionSheet({ @@ -449,13 +462,38 @@ Component({ // 复制验证码 copyVerificationCode() { + console.log('点击复制验证码'); const { verificationCode } = this.data; - if (!verificationCode) return; + console.log('当前验证码:', verificationCode); + + if (!verificationCode) { + console.log('无验证码,无法复制'); + return; + } + const dataStr = String(verificationCode); wx.setClipboardData({ - data: String(verificationCode), + data: dataStr, success: () => { - wx.showToast({ title: '复制成功', icon: 'success' }); + console.log('复制成功回调'); + // 延迟一下显示 Toast,防止被系统 Toast 覆盖(如果有) + setTimeout(() => { + wx.showToast({ title: '复制成功', icon: 'success' }); + }, 100); + }, + fail: (err) => { + console.error('复制失败:', err); + // 隐私协议未声明或用户拒绝会导致复制失败 (errno 112) + if (err.errno === 112 || (err.errMsg && err.errMsg.indexOf('privacy') > -1)) { + wx.showModal({ + title: '提示', + content: '无法自动复制,请长按数字进行手动复制', + showCancel: false, + confirmText: '知道了' + }); + } else { + wx.showToast({ title: '复制失败', icon: 'none' }); + } } }); }, diff --git a/codes/minipro/calculation/miniprogram/pages/profile/profile.wxml b/codes/minipro/calculation/miniprogram/pages/profile/profile.wxml index 008e252..37713d8 100644 --- a/codes/minipro/calculation/miniprogram/pages/profile/profile.wxml +++ b/codes/minipro/calculation/miniprogram/pages/profile/profile.wxml @@ -70,7 +70,7 @@ - {{verificationCode}} + {{verificationCode}} 复制 @@ -80,6 +80,27 @@ - + + + + + 计算历史 + > + + + 隐私政策 + > + + + 关于我们 + > + + + + + diff --git a/codes/minipro/calculation/miniprogram/pages/random-draw/random-draw.json b/codes/minipro/calculation/miniprogram/pages/random-draw/random-draw.json deleted file mode 100644 index 7955a64..0000000 --- a/codes/minipro/calculation/miniprogram/pages/random-draw/random-draw.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "usingComponents": {}, - "navigationBarTitleText": "谁去拿外卖" -} \ No newline at end of file diff --git a/codes/minipro/calculation/miniprogram/pages/random-draw/random-draw.ts b/codes/minipro/calculation/miniprogram/pages/random-draw/random-draw.ts deleted file mode 100644 index 494ab16..0000000 --- a/codes/minipro/calculation/miniprogram/pages/random-draw/random-draw.ts +++ /dev/null @@ -1,84 +0,0 @@ - -Page({ - data: { - names: [] as string[], - newName: '', - result: '', - isRolling: false - }, - - onLoad() { - // Initialize with some default placeholder data optionally, or keep empty - // this.setData({ names: ['张三', '李四', '王五'] }); - }, - - onInput(e: any) { - this.setData({ newName: e.detail.value }); - }, - - addName() { - const name = this.data.newName.trim(); - if (!name) { - wx.showToast({ title: '请输入名字', icon: 'none' }); - return; - } - - // Check duplication - if (this.data.names.includes(name)) { - wx.showToast({ title: '名字已存在', icon: 'none' }); - return; - } - - const names = [...this.data.names, name]; - this.setData({ names, newName: '' }); - }, - - removeName(e: any) { - if (this.data.isRolling) return; - - const index = e.currentTarget.dataset.index; - const names = [...this.data.names]; - names.splice(index, 1); - this.setData({ names }); - - // If deleted the current result, clear result - if (this.data.names.indexOf(this.data.result) === -1) { - // actually result is a string copy, but if logic requires reset: - // this.setData({ result: '' }); - } - }, - - startDraw() { - if (this.data.isRolling) return; - - const names = this.data.names; - if (names.length < 2) { - wx.showToast({ title: '至少需要两个人才能抽取哦', icon: 'none' }); - return; - } - - this.setData({ isRolling: true, result: '' }); - - let count = 0; - // Speed up first then slow down? Or simple uniform interval. - // Let's do a simple one first. - let baseInterval = 50; - let totalRolls = 30; - - const roll = () => { - const randomIndex = Math.floor(Math.random() * names.length); - this.setData({ result: names[randomIndex] }); - - count++; - if (count < totalRolls) { - // dynamic interval could be fun, but keeping it simple for now - setTimeout(roll, baseInterval + (count * 5)); // slowing down - } else { - this.setData({ isRolling: false }); - wx.vibrateShort({ type: 'heavy' }); - } - }; - - roll(); - } -}); diff --git a/codes/minipro/calculation/miniprogram/pages/random-draw/random-draw.wxml b/codes/minipro/calculation/miniprogram/pages/random-draw/random-draw.wxml deleted file mode 100644 index e7b12ef..0000000 --- a/codes/minipro/calculation/miniprogram/pages/random-draw/random-draw.wxml +++ /dev/null @@ -1,39 +0,0 @@ - - - 🎲 天选打工人 - 输入名字,看看今天谁去拿外卖 - - - - - 添加 - - - - 候选名单 ({{names.length}}) - - - - - {{item}} - × - - - - 🍃 - 还没有候选人,快去添加吧~ - - - - - - - - 🎉 天选之人 - {{result || '?'}} - - - - diff --git a/codes/minipro/calculation/miniprogram/pages/random-draw/random-draw.wxss b/codes/minipro/calculation/miniprogram/pages/random-draw/random-draw.wxss deleted file mode 100644 index 6308054..0000000 --- a/codes/minipro/calculation/miniprogram/pages/random-draw/random-draw.wxss +++ /dev/null @@ -1,235 +0,0 @@ -page { - background: linear-gradient(180deg, #fce4ec 0%, #f3e5f5 100%); - height: 100%; - font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif; -} - -.container { - display: flex; - flex-direction: column; - height: 100vh; - padding: 20px; - box-sizing: border-box; -} - -.header-tip { - text-align: center; - margin-bottom: 24px; -} - -.title { - font-size: 24px; - font-weight: bold; - color: #880e4f; - margin-bottom: 4px; -} - -.subtitle { - font-size: 14px; - color: #ad1457; - opacity: 0.7; -} - -.input-area { - display: flex; - margin-bottom: 20px; - background: #fff; - border-radius: 50px; - padding: 6px; - box-shadow: 0 4px 12px rgba(173, 20, 87, 0.1); - border: 2px solid rgba(255,255,255,0.5); -} - -.input { - flex: 1; - height: 44px; - padding: 0 20px; - font-size: 16px; - color: #333; -} - -.btn-add { - min-width: 80px; - height: 44px; - line-height: 44px; - text-align: center; - background: linear-gradient(135deg, #ec407a, #d81b60); - color: #fff; - border-radius: 40px; - font-size: 15px; - font-weight: bold; - box-shadow: 0 4px 8px rgba(216, 27, 96, 0.3); - transition: opacity 0.2s; -} - -.btn-add:active { - opacity: 0.8; -} - -.list-container { - flex: 1; - display: flex; - flex-direction: column; - background: rgba(255, 255, 255, 0.6); - border-radius: 20px; - padding: 15px 15px 5px 15px; /* bottom padding smaller because scrollview */ - margin-bottom: 20px; - backdrop-filter: blur(10px); - min-height: 0; /* Important for flex child to scroll */ -} - -.list-header { - font-size: 14px; - color: #888; - margin-bottom: 10px; - padding-left: 5px; -} - -.list-area { - flex: 1; - overflow: hidden; -} - -.name-list { - display: flex; - flex-wrap: wrap; - align-content: flex-start; /* 防止换行后拉伸 */ - gap: 10px; - padding-bottom: 10px; -} - -.name-item { - padding: 8px 16px 8px 12px; - border-radius: 20px; - font-size: 14px; - display: flex; - align-items: center; - position: relative; - font-weight: 500; - box-shadow: 0 2px 5px rgba(0,0,0,0.05); - animation: popIn 0.3s cubic-bezier(0.175, 0.885, 0.32, 1.275); -} - -@keyframes popIn { - from { transform: scale(0.8); opacity: 0; } - to { transform: scale(1); opacity: 1; } -} - -/* 伪随机颜色,根据 .color-0 到 .color-4 */ -.color-0 { background: #e3f2fd; color: #1565c0; } -.color-1 { background: #e8f5e9; color: #2e7d32; } -.color-2 { background: #fff3e0; color: #ef6c00; } -.color-3 { background: #f3e5f5; color: #7b1fa2; } -.color-4 { background: #ffebee; color: #c62828; } - -.name-text { - margin-right: 18px; /* 给关闭按钮留空间 */ -} - -.btn-delete { - position: absolute; - right: 6px; - top: 50%; - transform: translateY(-50%); - width: 20px; - height: 20px; - line-height: 18px; - text-align: center; - border-radius: 50%; - background: rgba(0,0,0,0.08); - font-size: 16px; - font-weight: normal; -} -.btn-delete:active { - background: rgba(0,0,0,0.2); -} - -.color-0 .btn-delete { color: #1565c0; } -.color-1 .btn-delete { color: #2e7d32; } -.color-2 .btn-delete { color: #ef6c00; } -.color-3 .btn-delete { color: #7b1fa2; } -.color-4 .btn-delete { color: #c62828; } - -.empty-tip { - width: 100%; - display: flex; - flex-direction: column; - align-items: center; - color: #999; - padding-top: 40px; -} - -.empty-icon { - font-size: 40px; - margin-bottom: 10px; - opacity: 0.5; -} - -.result-area { - display: flex; - flex-direction: column; - align-items: center; - padding-bottom: 20px; -} - -.result-box { - width: 100%; - background: #fff; - border-radius: 20px; - padding: 20px; - text-align: center; - margin-bottom: 20px; - box-shadow: 0 10px 30px rgba(0,0,0,0.1); - display: flex; - flex-direction: column; - justify-content: center; - min-height: 120px; - transition: transform 0.1s; -} - -.result-box.rolling { - transform: scale(0.98); - background: #fff8e1; -} - -.result-label { - font-size: 13px; - color: #999; - margin-bottom: 5px; - text-transform: uppercase; - letter-spacing: 1px; -} - -.result-text { - font-size: 32px; - font-weight: 800; - color: #333; - /* Prevent long names from overflowing */ - overflow: hidden; - white-space: nowrap; - text-overflow: ellipsis; -} - -.btn-start { - width: 100% !important; - background: linear-gradient(135deg, #7b1fa2 0%, #4a148c 100%); - color: #fff; - border-radius: 50px; - padding: 14px 0; - font-size: 18px; - font-weight: bold; - box-shadow: 0 8px 20px rgba(74, 20, 140, 0.4); - transition: all 0.2s; -} - -.btn-start-hover { - transform: translateY(2px); - box-shadow: 0 4px 10px rgba(74, 20, 140, 0.3); -} - -.btn-start[disabled] { - opacity: 0.6; - background: #9e9e9e; - box-shadow: none; - transform: none; -} diff --git a/codes/minipro/calculation/miniprogram/pages/scoreboard/scoreboard.json b/codes/minipro/calculation/miniprogram/pages/scoreboard/scoreboard.json deleted file mode 100644 index 9d6608e..0000000 --- a/codes/minipro/calculation/miniprogram/pages/scoreboard/scoreboard.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "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 deleted file mode 100644 index 90e3601..0000000 --- a/codes/minipro/calculation/miniprogram/pages/scoreboard/scoreboard.ts +++ /dev/null @@ -1,186 +0,0 @@ -// 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 deleted file mode 100644 index b7347d6..0000000 --- a/codes/minipro/calculation/miniprogram/pages/scoreboard/scoreboard.wxml +++ /dev/null @@ -1,69 +0,0 @@ - - - - - - - 第 {{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 deleted file mode 100644 index 33b5501..0000000 --- a/codes/minipro/calculation/miniprogram/pages/scoreboard/scoreboard.wxss +++ /dev/null @@ -1,273 +0,0 @@ -/* 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; -} diff --git a/codes/minipro/calculation/miniprogram/pages/unit-converter/unit-converter.ts b/codes/minipro/calculation/miniprogram/pages/unit-converter/unit-converter.ts index a3b7413..e5ba1a4 100644 --- a/codes/minipro/calculation/miniprogram/pages/unit-converter/unit-converter.ts +++ b/codes/minipro/calculation/miniprogram/pages/unit-converter/unit-converter.ts @@ -162,6 +162,15 @@ Page({ }); }, + swapUnits() { + const { fromIndex, toIndex } = this.data; + this.setData({ + fromIndex: toIndex, + toIndex: fromIndex + }); + this.calculate(); + }, + formatResult(val: number): string { if (Math.abs(val) < 0.000001 || Math.abs(val) > 10000000) { return val.toExponential(4); diff --git a/codes/minipro/calculation/miniprogram/pages/unit-converter/unit-converter.wxml b/codes/minipro/calculation/miniprogram/pages/unit-converter/unit-converter.wxml index 3c705b5..32e848e 100644 --- a/codes/minipro/calculation/miniprogram/pages/unit-converter/unit-converter.wxml +++ b/codes/minipro/calculation/miniprogram/pages/unit-converter/unit-converter.wxml @@ -25,7 +25,7 @@ - + diff --git a/codes/minipro/calculation/miniprogram/pages/unit-converter/unit-converter.wxss b/codes/minipro/calculation/miniprogram/pages/unit-converter/unit-converter.wxss index b5ab7d5..78eaaff 100644 --- a/codes/minipro/calculation/miniprogram/pages/unit-converter/unit-converter.wxss +++ b/codes/minipro/calculation/miniprogram/pages/unit-converter/unit-converter.wxss @@ -1,5 +1,5 @@ page { - background-color: #f7f8fa; + background-color: #EBF4F8; /* Color 03 */ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif; } @@ -20,27 +20,27 @@ page { background: #ffffff; padding: 10px 24px; border-radius: 50px; - box-shadow: 0 4px 12px rgba(0, 0, 0, 0.05); + box-shadow: 0 4px 12px rgba(17, 97, 107, 0.1); display: flex; align-items: center; gap: 10px; - color: #333; + color: #11616B; /* Color 01 */ } .category-picker .label { font-size: 14px; - color: #888; + color: #7BBDB6; /* Color 02 */ } .category-picker .value { font-size: 18px; font-weight: 600; - color: #007aff; + color: #11616B; /* Color 01 */ } .category-picker .arrow { font-size: 12px; - color: #ccc; + color: #7BBDB6; /* Color 02 */ } /* 核心转换卡片 */ @@ -48,7 +48,7 @@ page { background: #ffffff; border-radius: 20px; padding: 10px; - box-shadow: 0 8px 24px rgba(0, 0, 0, 0.06); + box-shadow: 0 8px 24px rgba(17, 97, 107, 0.08); /* Teal shadow */ display: flex; flex-direction: column; } @@ -64,13 +64,13 @@ page { } .output-row { - background-color: #f9fbfd; /* 稍微不同的背景色区分输入输出 */ + background-color: #EBF4F8; /* Color 03 */ border-radius: 4px 4px 16px 16px; } .row-label { font-size: 12px; - color: #999; + color: #7BBDB6; /* Color 02 */ margin-bottom: 8px; text-transform: uppercase; letter-spacing: 1px; @@ -86,66 +86,68 @@ page { flex: 1; font-size: 32px; font-weight: 500; - color: #333; + color: #11616B; /* Color 01 */ height: 48px; line-height: 48px; - min-width: 0; /* 防止flex子项溢出 */ + min-width: 0; } .value-display { - color: #007aff; /* 结果颜色高亮 */ + color: #DC8B70; /* Color 05 - Accent for Result */ } /* 单位选择器 */ .unit-selector { - margin-left: 15px; + display: flex; + align-items: center; } .unit-text { + font-size: 16px; + color: #11616B; /* Color 01 */ + font-weight: 500; + background-color: rgba(123, 189, 182, 0.2); /* Color 02 Low Opacity */ + padding: 6px 12px; + border-radius: 8px; display: flex; align-items: center; - gap: 6px; - padding: 8px 16px; - background-color: #f0f2f5; - border-radius: 12px; - font-size: 16px; - color: #444; - font-weight: 500; - transition: background-color 0.2s; -} - -.unit-text:active { - background-color: #e1e4e8; + gap: 4px; } .unit-arrow { font-size: 10px; - color: #888; + opacity: 0.6; } -/* 分割线和图标 */ +/* 分割线/转换图标 */ .divider { - height: 1px; - background-color: #eee; - margin: 0 20px; position: relative; + height: 1px; + background-color: rgba(17, 97, 107, 0.1); + margin: 0 20px; display: flex; - justify-content: center; align-items: center; + justify-content: center; + z-index: 10; } .icon-transfer { - position: absolute; - background: #fff; - color: #ccc; - font-size: 20px; - padding: 0 10px; - top: -14px; + width: 32px; + height: 32px; + background-color: #fff; + border-radius: 50%; + border: 1px solid rgba(17, 97, 107, 0.1); + color: #11616B; /* Color 01 */ + display: flex; + align-items: center; + justify-content: center; + font-size: 18px; + box-shadow: 0 2px 8px rgba(0,0,0,0.05); } .tips { - text-align: center; margin-top: 30px; + text-align: center; font-size: 13px; - color: #ccc; + color: #999; } diff --git a/codes/minipro/calculation/miniprogram/utils/lunar.ts b/codes/minipro/calculation/miniprogram/utils/lunar.ts deleted file mode 100644 index db58ba0..0000000 --- a/codes/minipro/calculation/miniprogram/utils/lunar.ts +++ /dev/null @@ -1,310 +0,0 @@ -/** - * 农历(阴历)工具库 - * 支持 1900-2100 年的公历/农历互转 - */ - -// 农历数据表 (1900-2100) -// 每个元素编码该年的农历信息: -// - 低 12 bit: 各月大小 (1=30天, 0=29天) -// - 第 13-16 bit: 闰月月份 (0=无闰月) -// - 第 17-20 bit: 闰月大小 (0=29天, 1=30天) -const lunarInfo: number[] = [ - 0x04bd8, 0x04ae0, 0x0a570, 0x054d5, 0x0d260, 0x0d950, 0x16554, 0x056a0, 0x09ad0, 0x055d2, - 0x04ae0, 0x0a5b6, 0x0a4d0, 0x0d250, 0x1d255, 0x0b540, 0x0d6a0, 0x0ada2, 0x095b0, 0x14977, - 0x04970, 0x0a4b0, 0x0b4b5, 0x06a50, 0x06d40, 0x1ab54, 0x02b60, 0x09570, 0x052f2, 0x04970, - 0x06566, 0x0d4a0, 0x0ea50, 0x06e95, 0x05ad0, 0x02b60, 0x186e3, 0x092e0, 0x1c8d7, 0x0c950, - 0x0d4a0, 0x1d8a6, 0x0b550, 0x056a0, 0x1a5b4, 0x025d0, 0x092d0, 0x0d2b2, 0x0a950, 0x0b557, - 0x06ca0, 0x0b550, 0x15355, 0x04da0, 0x0a5b0, 0x14573, 0x052b0, 0x0a9a8, 0x0e950, 0x06aa0, - 0x0aea6, 0x0ab50, 0x04b60, 0x0aae4, 0x0a570, 0x05260, 0x0f263, 0x0d950, 0x05b57, 0x056a0, - 0x096d0, 0x04dd5, 0x04ad0, 0x0a4d0, 0x0d4d4, 0x0d250, 0x0d558, 0x0b540, 0x0b6a0, 0x195a6, - 0x095b0, 0x049b0, 0x0a974, 0x0a4b0, 0x0b27a, 0x06a50, 0x06d40, 0x0af46, 0x0ab60, 0x09570, - 0x04af5, 0x04970, 0x064b0, 0x074a3, 0x0ea50, 0x06b58, 0x05ac0, 0x0ab60, 0x096d5, 0x092e0, - 0x0c960, 0x0d954, 0x0d4a0, 0x0da50, 0x07552, 0x056a0, 0x0abb7, 0x025d0, 0x092d0, 0x0cab5, - 0x0a950, 0x0b4a0, 0x0baa4, 0x0ad50, 0x055d9, 0x04ba0, 0x0a5b0, 0x15176, 0x052b0, 0x0a930, - 0x07954, 0x06aa0, 0x0ad50, 0x05b52, 0x04b60, 0x0a6e6, 0x0a4e0, 0x0d260, 0x0ea65, 0x0d530, - 0x05aa0, 0x076a3, 0x096d0, 0x04afb, 0x04ad0, 0x0a4d0, 0x1d0b6, 0x0d250, 0x0d520, 0x0dd45, - 0x0b5a0, 0x056d0, 0x055b2, 0x049b0, 0x0a577, 0x0a4b0, 0x0aa50, 0x1b255, 0x06d20, 0x0ada0, - 0x14b63, 0x09370, 0x049f8, 0x04970, 0x064b0, 0x168a6, 0x0ea50, 0x06b20, 0x1a6c4, 0x0aae0, - 0x092e0, 0x0d2e3, 0x0c960, 0x0d557, 0x0d4a0, 0x0da50, 0x05d55, 0x056a0, 0x0a6d0, 0x055d4, - 0x052d0, 0x0a9b8, 0x0a950, 0x0b4a0, 0x0b6a6, 0x0ad50, 0x055a0, 0x0aba4, 0x0a5b0, 0x052b0, - 0x0b273, 0x06930, 0x07337, 0x06aa0, 0x0ad50, 0x14b55, 0x04b60, 0x0a570, 0x054e4, 0x0d160, - 0x0e968, 0x0d520, 0x0daa0, 0x16aa6, 0x056d0, 0x04ae0, 0x0a9d4, 0x0a4d0, 0x0d150, 0x0f252, - 0x0d520 -]; - -// 天干 -const tianGan = ['甲', '乙', '丙', '丁', '戊', '己', '庚', '辛', '壬', '癸']; -// 地支 -const diZhi = ['子', '丑', '寅', '卯', '辰', '巳', '午', '未', '申', '酉', '戌', '亥']; -// 生肖 -const shengXiao = ['鼠', '牛', '虎', '兔', '龙', '蛇', '马', '羊', '猴', '鸡', '狗', '猪']; -// 农历月份名 -const lunarMonthNames = ['正', '二', '三', '四', '五', '六', '七', '八', '九', '十', '冬', '腊']; -// 农历日名 -const lunarDayNames = [ - '初一', '初二', '初三', '初四', '初五', '初六', '初七', '初八', '初九', '初十', - '十一', '十二', '十三', '十四', '十五', '十六', '十七', '十八', '十九', '二十', - '廿一', '廿二', '廿三', '廿四', '廿五', '廿六', '廿七', '廿八', '廿九', '三十' -]; - -// 24节气 -const solarTermNames = [ - '小寒', '大寒', '立春', '雨水', '惊蛰', '春分', - '清明', '谷雨', '立夏', '小满', '芒种', '夏至', - '小暑', '大暑', '立秋', '处暑', '白露', '秋分', - '寒露', '霜降', '立冬', '小雪', '大雪', '冬至' -]; - -// 节气数据 (1900-2100) 用 delta 编码 -const sTermInfo = [ - 0, 21208, 42467, 63836, 85337, 107014, 128867, 150921, 173149, 195551, - 218072, 240693, 263343, 285989, 308563, 331033, 353350, 375494, 397447, - 419210, 440795, 462224, 483532, 504758 -]; - -/** - * 返回农历 year 年的总天数 - */ -function lunarYearDays(year: number): number { - let sum = 348; // 12 * 29 - for (let i = 0x8000; i > 0x8; i >>= 1) { - sum += (lunarInfo[year - 1900] & i) ? 1 : 0; - } - return sum + leapDays(year); -} - -/** - * 返回农历 year 年闰月的天数,无闰月返回 0 - */ -function leapDays(year: number): number { - if (leapMonth(year)) { - return (lunarInfo[year - 1900] & 0x10000) ? 30 : 29; - } - return 0; -} - -/** - * 返回农历 year 年闰月月份,无闰月返回 0 - */ -function leapMonth(year: number): number { - return lunarInfo[year - 1900] & 0xf; -} - -/** - * 返回农历 year 年 month 月的天数 - */ -function monthDays(year: number, month: number): number { - return (lunarInfo[year - 1900] & (0x10000 >> month)) ? 30 : 29; -} - -/** - * 公历转农历 - */ -export function solarToLunar(year: number, month: number, day: number) { - // 基准日期: 1900年1月31日 = 农历1900年正月初一 - const baseDate = new Date(1900, 0, 31); - const targetDate = new Date(year, month - 1, day); - let offset = Math.floor((targetDate.getTime() - baseDate.getTime()) / 86400000); - - // 计算农历年 - let lunarYear = 1900; - let temp = 0; - for (lunarYear = 1900; lunarYear < 2101 && offset > 0; lunarYear++) { - temp = lunarYearDays(lunarYear); - offset -= temp; - } - if (offset < 0) { - offset += temp; - lunarYear--; - } - - // 闰月 - const leap = leapMonth(lunarYear); - let isLeap = false; - let lunarMonth = 1; - - for (let i = 1; i < 13 && offset > 0; i++) { - // 闰月 - if (leap > 0 && i === (leap + 1) && !isLeap) { - --i; - isLeap = true; - temp = leapDays(lunarYear); - } else { - temp = monthDays(lunarYear, i); - } - - if (isLeap && i === (leap + 1)) { - isLeap = false; - } - - offset -= temp; - if (!isLeap) { - lunarMonth = i; - } - } - - if (offset === 0 && leap > 0 && lunarMonth === leap + 1) { - if (isLeap) { - isLeap = false; - } else { - isLeap = true; - --lunarMonth; - } - } - - if (offset < 0) { - offset += temp; - --lunarMonth; - if (lunarMonth <= 0) { - lunarMonth = 12; - lunarYear--; - } - } - - const lunarDay = offset + 1; - - // 天干地支 - const ganIndex = (lunarYear - 4) % 10; - const zhiIndex = (lunarYear - 4) % 12; - const ganZhi = tianGan[ganIndex] + diZhi[zhiIndex]; - const animal = shengXiao[zhiIndex]; - - return { - year: lunarYear, - month: lunarMonth, - day: lunarDay, - isLeap: isLeap, - monthName: (isLeap ? '闰' : '') + lunarMonthNames[lunarMonth - 1] + '月', - dayName: lunarDayNames[lunarDay - 1], - ganZhi: ganZhi, - animal: animal, - // 完整中文表示 - fullText: `${ganZhi}年(${animal}年)${isLeap ? '闰' : ''}${lunarMonthNames[lunarMonth - 1]}月${lunarDayNames[lunarDay - 1]}` - }; -} - -/** - * 农历转公历 - */ -export function lunarToSolar(lunarYear: number, lunarMonth: number, lunarDay: number, isLeapMonth: boolean = false) { - // 检查闰月是否有效 - const leap = leapMonth(lunarYear); - if (isLeapMonth && leap !== lunarMonth) { - // 该年该月不是闰月,回退为非闰 - isLeapMonth = false; - } - - // 计算从基准日偏移天数 - let offset = 0; - for (let y = 1900; y < lunarYear; y++) { - offset += lunarYearDays(y); - } - - // 加上本年各月天数 - let leapM = leapMonth(lunarYear); - let isAfterLeap = false; - for (let m = 1; m < lunarMonth; m++) { - // 如果有闰月且在目标月之前 - if (leapM > 0 && m === leapM && !isAfterLeap) { - offset += leapDays(lunarYear); - isAfterLeap = true; - } - offset += monthDays(lunarYear, m); - } - // 如果目标就是闰月 - if (isLeapMonth) { - offset += monthDays(lunarYear, lunarMonth); - } - // 如果闰月在目标月之前或等于目标月且不是闰月 - if (!isLeapMonth && leapM > 0 && leapM === lunarMonth && !isAfterLeap) { - // 不需要额外操作 - } - - offset += lunarDay - 1; - - // 基准日期: 1900年1月31日 - const baseDate = new Date(1900, 0, 31); - const targetDate = new Date(baseDate.getTime() + offset * 86400000); - - return { - year: targetDate.getFullYear(), - month: targetDate.getMonth() + 1, - day: targetDate.getDate() - }; -} - -/** - * 获取某年某月某日的节气(如果有的话) - */ -export function getSolarTerm(year: number, month: number, day: number): string | null { - // 每个月有两个节气 - const termIndex1 = (month - 1) * 2; // 本月第一个节气 - const termIndex2 = termIndex1 + 1; // 本月第二个节气 - - const d1 = getSolarTermDate(year, termIndex1); - const d2 = getSolarTermDate(year, termIndex2); - - if (day === d1) return solarTermNames[termIndex1]; - if (day === d2) return solarTermNames[termIndex2]; - return null; -} - -/** - * 计算某年第 n 个节气的日期(返回日) - */ -function getSolarTermDate(year: number, n: number): number { - const offDate = new Date((31556925974.7 * (year - 1900) + sTermInfo[n] * 60000) + Date.UTC(1900, 0, 6, 2, 5)); - return offDate.getUTCDate(); -} - -/** - * 获取农历年份的天干地支和生肖 - */ -export function getYearInfo(year: number) { - const ganIndex = (year - 4) % 10; - const zhiIndex = (year - 4) % 12; - return { - ganZhi: tianGan[ganIndex] + diZhi[zhiIndex], - animal: shengXiao[zhiIndex] - }; -} - -/** - * 获取农历月份名 - */ -export function getLunarMonthName(month: number, isLeap: boolean = false): string { - return (isLeap ? '闰' : '') + lunarMonthNames[month - 1] + '月'; -} - -/** - * 获取农历日名 - */ -export function getLunarDayName(day: number): string { - return lunarDayNames[day - 1]; -} - -/** - * 获取常见节日 - */ -export function getFestival(month: number, day: number): string | null { - const solarFestivals: Record = { - '1-1': '元旦', '2-14': '情人节', '3-8': '妇女节', '3-12': '植树节', - '4-1': '愚人节', '5-1': '劳动节', '5-4': '青年节', '6-1': '儿童节', - '7-1': '建党节', '8-1': '建军节', '9-10': '教师节', '10-1': '国庆节', - '10-31': '万圣节', '12-24': '平安夜', '12-25': '圣诞节' - }; - return solarFestivals[`${month}-${day}`] || null; -} - -/** - * 获取农历节日 - */ -export function getLunarFestival(month: number, day: number): string | null { - const lunarFestivals: Record = { - '1-1': '春节', '1-15': '元宵节', '2-2': '龙抬头', - '5-5': '端午节', '7-7': '七夕', '7-15': '中元节', - '8-15': '中秋节', '9-9': '重阳节', '12-8': '腊八节', - '12-23': '小年', '12-30': '除夕' - }; - return lunarFestivals[`${month}-${day}`] || null; -}