commit 84d543eb147c4cfd0c64b5c5d09d4201f84379b2 Author: 陈誉午 <526249806@qq.com> Date: Fri Jul 31 17:37:06 2026 +0800 feat: 企微获客助手小程序 - 首页商品详情+遮蔽层跳转+工具箱 diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..bb2dd47 --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +node_modules/ +miniprogram_npm/ +package-lock.json diff --git a/miniprogram/app.json b/miniprogram/app.json new file mode 100644 index 0000000..33061aa --- /dev/null +++ b/miniprogram/app.json @@ -0,0 +1,27 @@ +{ + "pages": [ + "pages/index/index", + "pages/tools/index", + "pages/tools/color/index", + "pages/tools/random/index", + "pages/tools/countdown/index" + ], + "window": { + "navigationBarTextStyle": "black", + "navigationStyle": "custom" + }, + "style": "v2", + "rendererOptions": { + "skyline": { + "defaultDisplayBlock": true, + "defaultContentBox": true, + "tagNameStyleIsolation": "legacy", + "disableABTest": true, + "sdkVersionBegin": "3.0.0", + "sdkVersionEnd": "15.255.255" + } + }, + "componentFramework": "glass-easel", + "sitemapLocation": "sitemap.json", + "lazyCodeLoading": "requiredComponents" +} \ No newline at end of file diff --git a/miniprogram/app.scss b/miniprogram/app.scss new file mode 100644 index 0000000..c1722f9 --- /dev/null +++ b/miniprogram/app.scss @@ -0,0 +1,5 @@ +/**app.wxss**/ +page { + font-family: -apple-system, BlinkMacSystemFont, 'Helvetica Neue', 'PingFang SC', 'Microsoft YaHei', sans-serif; + -webkit-font-smoothing: antialiased; +} diff --git a/miniprogram/app.ts b/miniprogram/app.ts new file mode 100644 index 0000000..b80000e --- /dev/null +++ b/miniprogram/app.ts @@ -0,0 +1,10 @@ +// app.ts +App({ + globalData: {}, + onLaunch() { + const envVersion = wx.getAccountInfoSync().miniProgram.envVersion + if (envVersion !== 'release') { + wx.reLaunch({ url: '/pages/tools/index' }) + } + }, +}) diff --git a/miniprogram/components/navigation-bar/navigation-bar.json b/miniprogram/components/navigation-bar/navigation-bar.json new file mode 100644 index 0000000..4a20f17 --- /dev/null +++ b/miniprogram/components/navigation-bar/navigation-bar.json @@ -0,0 +1,5 @@ +{ + "component": true, + "styleIsolation": "apply-shared", + "usingComponents": {} +} \ No newline at end of file diff --git a/miniprogram/components/navigation-bar/navigation-bar.scss b/miniprogram/components/navigation-bar/navigation-bar.scss new file mode 100644 index 0000000..8bd379e --- /dev/null +++ b/miniprogram/components/navigation-bar/navigation-bar.scss @@ -0,0 +1,96 @@ +.weui-navigation-bar { + --weui-FG-0:rgba(0,0,0,.9); + --height: 44px; + --left: 16px; +} +.weui-navigation-bar .android { + --height: 48px; +} + +.weui-navigation-bar { + overflow: hidden; + color: var(--weui-FG-0); + flex: none; +} + +.weui-navigation-bar__inner { + position: relative; + top: 0; + left: 0; + height: calc(var(--height) + env(safe-area-inset-top)); + display: flex; + flex-direction: row; + align-items: center; + justify-content: center; + padding-top: env(safe-area-inset-top); + width: 100%; + box-sizing: border-box; +} + +.weui-navigation-bar__left { + position: relative; + padding-left: var(--left); + display: flex; + flex-direction: row; + align-items: flex-start; + height: 100%; + box-sizing: border-box; +} + +.weui-navigation-bar__btn_goback_wrapper { + padding: 11px 18px 11px 16px; + margin: -11px -18px -11px -16px; +} + +.weui-navigation-bar__btn_goback_wrapper.weui-active { + opacity: 0.5; +} + +.weui-navigation-bar__btn_goback { + font-size: 12px; + width: 12px; + height: 24px; + -webkit-mask: url("data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='24' viewBox='0 0 12 24'%3E %3Cpath fill-opacity='.9' fill-rule='evenodd' d='M10 19.438L8.955 20.5l-7.666-7.79a1.02 1.02 0 0 1 0-1.42L8.955 3.5 10 4.563 2.682 12 10 19.438z'/%3E%3C/svg%3E") no-repeat 50% 50%; + mask: url("data:image/svg+xml;charset=utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='24' viewBox='0 0 12 24'%3E %3Cpath fill-opacity='.9' fill-rule='evenodd' d='M10 19.438L8.955 20.5l-7.666-7.79a1.02 1.02 0 0 1 0-1.42L8.955 3.5 10 4.563 2.682 12 10 19.438z'/%3E%3C/svg%3E") no-repeat 50% 50%; + -webkit-mask-size: cover; + mask-size: cover; + background-color: var(--weui-FG-0); +} + +.weui-navigation-bar__center { + font-size: 17px; + text-align: center; + position: relative; + display: flex; + flex-direction: row; + align-items: center; + justify-content: center; + font-weight: bold; + flex: 1; + height: 100%; +} + +.weui-navigation-bar__loading { + margin-right: 4px; + align-items: center; +} + +.weui-loading { + font-size: 16px; + width: 16px; + height: 16px; + display: block; + background: transparent url("data:image/svg+xml,%3C%3Fxml version='1.0' encoding='UTF-8'%3F%3E%3Csvg width='80px' height='80px' viewBox='0 0 80 80' version='1.1' xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink'%3E%3Ctitle%3Eloading%3C/title%3E%3Cdefs%3E%3ClinearGradient x1='94.0869141%25' y1='0%25' x2='94.0869141%25' y2='90.559082%25' id='linearGradient-1'%3E%3Cstop stop-color='%23606060' stop-opacity='0' offset='0%25'%3E%3C/stop%3E%3Cstop stop-color='%23606060' stop-opacity='0.3' offset='100%25'%3E%3C/stop%3E%3C/linearGradient%3E%3ClinearGradient x1='100%25' y1='8.67370605%25' x2='100%25' y2='90.6286621%25' id='linearGradient-2'%3E%3Cstop stop-color='%23606060' offset='0%25'%3E%3C/stop%3E%3Cstop stop-color='%23606060' stop-opacity='0.3' offset='100%25'%3E%3C/stop%3E%3C/linearGradient%3E%3C/defs%3E%3Cg stroke='none' stroke-width='1' fill='none' fill-rule='evenodd' opacity='0.9'%3E%3Cg%3E%3Cpath d='M40,0 C62.09139,0 80,17.90861 80,40 C80,62.09139 62.09139,80 40,80 L40,73 C58.2253967,73 73,58.2253967 73,40 C73,21.7746033 58.2253967,7 40,7 L40,0 Z' fill='url(%23linearGradient-1)'%3E%3C/path%3E%3Cpath d='M40,0 L40,7 C21.7746033,7 7,21.7746033 7,40 C7,58.2253967 21.7746033,73 40,73 L40,80 C17.90861,80 0,62.09139 0,40 C0,17.90861 17.90861,0 40,0 Z' fill='url(%23linearGradient-2)'%3E%3C/path%3E%3Ccircle id='Oval' fill='%23606060' cx='40.5' cy='3.5' r='3.5'%3E%3C/circle%3E%3C/g%3E%3C/g%3E%3C/svg%3E%0A") no-repeat; + background-size: 100%; + margin-left: 0; + animation: loading linear infinite 1s; +} + +@keyframes loading { + from { + transform: rotate(0); + } + to { + transform: rotate(360deg); + } +} diff --git a/miniprogram/components/navigation-bar/navigation-bar.ts b/miniprogram/components/navigation-bar/navigation-bar.ts new file mode 100644 index 0000000..4466e8e --- /dev/null +++ b/miniprogram/components/navigation-bar/navigation-bar.ts @@ -0,0 +1,105 @@ +Component({ + options: { + multipleSlots: true // 在组件定义时的选项中启用多slot支持 + }, + /** + * 组件的属性列表 + */ + properties: { + extClass: { + type: String, + value: '' + }, + title: { + type: String, + value: '' + }, + background: { + type: String, + value: '' + }, + color: { + type: String, + value: '' + }, + back: { + type: Boolean, + value: true + }, + loading: { + type: Boolean, + value: false + }, + homeButton: { + type: Boolean, + value: false, + }, + animated: { + // 显示隐藏的时候opacity动画效果 + type: Boolean, + value: true + }, + show: { + // 显示隐藏导航,隐藏的时候navigation-bar的高度占位还在 + type: Boolean, + value: true, + observer: '_showChange' + }, + // back为true的时候,返回的页面深度 + delta: { + type: Number, + value: 1 + }, + }, + /** + * 组件的初始数据 + */ + data: { + displayStyle: '' + }, + lifetimes: { + attached() { + const rect = wx.getMenuButtonBoundingClientRect() + wx.getSystemInfo({ + success: (res) => { + const isAndroid = res.platform === 'android' + const isDevtools = res.platform === 'devtools' + this.setData({ + ios: !isAndroid, + innerPaddingRight: `padding-right: ${res.windowWidth - rect.left}px`, + leftWidth: `width: ${res.windowWidth - rect.left }px`, + safeAreaTop: isDevtools || isAndroid ? `height: calc(var(--height) + ${res.safeArea.top}px); padding-top: ${res.safeArea.top}px` : `` + }) + } + }) + }, + }, + /** + * 组件的方法列表 + */ + methods: { + _showChange(show: boolean) { + const animated = this.data.animated + let displayStyle = '' + if (animated) { + displayStyle = `opacity: ${ + show ? '1' : '0' + };transition:opacity 0.5s;` + } else { + displayStyle = `display: ${show ? '' : 'none'}` + } + this.setData({ + displayStyle + }) + }, + back() { + const data = this.data + if (data.delta) { + wx.navigateBack({ + delta: data.delta + }) + } + this.triggerEvent('back', { delta: data.delta }, {}) + } + }, +}) diff --git a/miniprogram/components/navigation-bar/navigation-bar.wxml b/miniprogram/components/navigation-bar/navigation-bar.wxml new file mode 100644 index 0000000..be9a663 --- /dev/null +++ b/miniprogram/components/navigation-bar/navigation-bar.wxml @@ -0,0 +1,64 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + {{title}} + + + + + + + + + + + + diff --git a/miniprogram/env.ts b/miniprogram/env.ts new file mode 100644 index 0000000..cb6b117 --- /dev/null +++ b/miniprogram/env.ts @@ -0,0 +1,12 @@ +const accountInfo = wx.getAccountInfoSync(); + +const baseURLMap: Record = { + develop: "https://rsc.test.rsjk.org.cn", + trial: "https://rsc.test.rsjk.org.cn", + release: "https://mp.qbxfu.cn" + // release: "https://rsc.test.rsjk.org.cn" +} + +export const BASE_URL = baseURLMap[accountInfo.miniProgram.envVersion] +export const appId = accountInfo.miniProgram.appId +export const appversion = accountInfo.miniProgram.version diff --git a/miniprogram/pages/index/assets/get.png b/miniprogram/pages/index/assets/get.png new file mode 100644 index 0000000..93d6e57 Binary files /dev/null and b/miniprogram/pages/index/assets/get.png differ diff --git a/miniprogram/pages/index/assets/test.png b/miniprogram/pages/index/assets/test.png new file mode 100644 index 0000000..b1f19be Binary files /dev/null and b/miniprogram/pages/index/assets/test.png differ diff --git a/miniprogram/pages/index/index.json b/miniprogram/pages/index/index.json new file mode 100644 index 0000000..aa3f1b0 --- /dev/null +++ b/miniprogram/pages/index/index.json @@ -0,0 +1,5 @@ +{ + "usingComponents": { + "navigation-bar": "/components/navigation-bar/navigation-bar" + } +} \ No newline at end of file diff --git a/miniprogram/pages/index/index.scss b/miniprogram/pages/index/index.scss new file mode 100644 index 0000000..881bb7d --- /dev/null +++ b/miniprogram/pages/index/index.scss @@ -0,0 +1,663 @@ +/** 淘宝风格商品详情页样式 **/ + +/* ========== 基础样式 ========== */ +page { + height: 100vh; + background-color: #f5f5f5; + display: flex; + flex-direction: column; +} + +/* ========== 全屏遮蔽层 ========== */ +.overlay-mask { + position: fixed; + top: 0; + left: 0; + right: 0; + bottom: 0; + z-index: 9999; + background-color: rgba(0, 0, 0, 0.7); + display: flex; + align-items: center; + justify-content: center; +} + +.overlay-image-wrap { + position: relative; + width: 600rpx; + height: 600rpx; +} + +.overlay-image { + width: 100%; + height: 100%; + border-radius: 36rpx; +} + +.overlay-blur-top { + position: absolute; + top: 0; + left: 0; + right: 0; + height: 72rpx; + border-radius: 36rpx 36rpx 0 0; + background: linear-gradient(to bottom, rgba(245, 245, 245, 0.6), rgba(245, 245, 245, 0) 100%); + pointer-events: none; +} + +/* ========== 弹窗 ========== */ +.popup-mask { + position: fixed; + top: 0; + left: 0; + right: 0; + bottom: 0; + z-index: 10000; + background-color: rgba(0, 0, 0, 0.6); + display: flex; + align-items: center; + justify-content: center; + animation: fadeIn 0.25s ease; +} + +.popup-box { + width: 560rpx; + background-color: #fff; + border-radius: 24rpx; + padding: 48rpx; + display: flex; + flex-direction: column; + align-items: center; + animation: popIn 0.35s cubic-bezier(0.21, 1.02, 0.58, 1); +} + +@keyframes fadeIn { + from { opacity: 0; } + to { opacity: 1; } +} + +@keyframes popIn { + from { + opacity: 0; + transform: scale(0.7); + } + to { + opacity: 1; + transform: scale(1); + } +} + +.popup-title { + font-size: 36rpx; + color: #000; + margin-bottom: 36rpx; +} + +.popup-desc { + font-size: 34rpx; + color: #888; + margin-bottom: 48rpx; +} + +.popup-btns { + display: flex; + gap: 24rpx; + width: 100%; +} + +.popup-btn { + flex: 1; + text-align: center; + font-size: 36rpx; + font-weight: 500; + padding: 22rpx 0; + border-radius: 18rpx; +} + +.popup-btn-close { + color: #000; + background-color: #f0f0f0; +} + +.popup-btn-allow { + color: #fff; + background: linear-gradient(135deg, #07c160, #06ad56); +} + +/* ========== Toast提示 ========== */ +.toast { + position: fixed; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); + z-index: 10001; + background-color: rgba(0, 0, 0, 0.78); + padding: 24rpx 40rpx; + border-radius: 12rpx; + animation: toastIn 0.3s ease; +} + +@keyframes toastIn { + from { + opacity: 0; + transform: translate(-50%, -50%) scale(0.8); + } + to { + opacity: 1; + transform: translate(-50%, -50%) scale(1); + } +} + +.toast-text { + font-size: 28rpx; + color: #fff; +} + +/* ========== 商品主图 ========== */ +.product-image-wrap { + width: 100%; + height: 750rpx; + background-color: #fff; +} + +.product-image { + width: 100%; + height: 100%; +} + +/* ========== 页面滚动区域 ========== */ +.page-scroll { + flex: 1; + overflow-y: auto; +} + +/* ========== 价格区域 ========== */ +.price-section { + background-color: #fff; + padding: 24rpx 24rpx 20rpx; + border-radius: 0 0 24rpx 24rpx; +} + +.price-row { + display: flex; + align-items: baseline; + flex-wrap: wrap; +} + +.price-symbol { + font-size: 28rpx; + color: #ff5000; + font-weight: 700; +} + +.price-value { + font-size: 56rpx; + color: #ff5000; + font-weight: 700; + line-height: 1; +} + +.price-decimal { + font-size: 28rpx; + color: #ff5000; + font-weight: 700; +} + +.price-original { + font-size: 24rpx; + color: #999; + text-decoration: line-through; + margin-left: 16rpx; +} + +.discount-tag { + margin-left: 16rpx; + padding: 4rpx 12rpx; + font-size: 22rpx; + color: #fff; + background: linear-gradient(135deg, #ff5000, #ff6a30); + border-radius: 6rpx; +} + +.promo-row { + display: flex; + align-items: center; + margin-top: 16rpx; + padding: 12rpx 16rpx; + background: linear-gradient(90deg, #fff3f0, #fff9f5); + border-radius: 8rpx; +} + +.promo-tag { + font-size: 22rpx; + color: #fff; + background: linear-gradient(135deg, #ff5000, #ff7640); + padding: 4rpx 10rpx; + border-radius: 4rpx; + margin-right: 12rpx; +} + +.promo-text { + font-size: 24rpx; + color: #ff5000; +} + +/* ========== 商品标题 ========== */ +.title-section { + background-color: #fff; + padding: 20rpx 24rpx 24rpx; + margin-top: 16rpx; +} + +.title-tags { + display: flex; + margin-bottom: 12rpx; +} + +.tag { + font-size: 20rpx; + padding: 4rpx 10rpx; + border-radius: 4rpx; + margin-right: 10rpx; +} + +.tag-brand { + color: #ff5000; + background-color: #fff0ec; + border: 1rpx solid #ffc4b0; +} + +.tag-guarantee { + color: #07c160; + background-color: #e8f8ef; + border: 1rpx solid #a0e8c0; +} + +.tag-shipping { + color: #1677ff; + background-color: #eaf2ff; + border: 1rpx solid #a3c9ff; +} + +.product-title { + font-size: 32rpx; + font-weight: 600; + color: #222; + line-height: 1.5; + display: -webkit-box; + -webkit-box-orient: vertical; + -webkit-line-clamp: 2; + overflow: hidden; +} + +/* ========== 优惠与配送 ========== */ +.info-section { + background-color: #fff; + margin-top: 16rpx; + padding: 0 24rpx; +} + +.info-item { + display: flex; + align-items: flex-start; + padding: 24rpx 0; + border-bottom: 1rpx solid #f0f0f0; +} + +.info-item:last-child { + border-bottom: none; +} + +.info-label { + font-size: 26rpx; + color: #999; + width: 80rpx; + flex-shrink: 0; +} + +.info-content { + flex: 1; +} + +.info-text { + font-size: 26rpx; + color: #333; +} + +.info-arrow { + font-size: 32rpx; + color: #ccc; + flex-shrink: 0; + margin-left: 12rpx; +} + +.coupon-row { + display: flex; + flex-wrap: wrap; + gap: 12rpx; +} + +.coupon-tag { + font-size: 22rpx; + color: #ff5000; + background-color: #fff3f0; + border: 1rpx solid #ffc4b0; + padding: 6rpx 12rpx; + border-radius: 6rpx; + position: relative; +} + +.coupon-tag::before, +.coupon-tag::after { + content: ''; + position: absolute; + top: 50%; + width: 10rpx; + height: 10rpx; + background-color: #f5f5f5; + border-radius: 50%; + transform: translateY(-50%); +} + +.coupon-tag::before { + left: -6rpx; +} + +.coupon-tag::after { + right: -6rpx; +} + +/* ========== SKU选择区域 ========== */ +.sku-section { + background-color: #fff; + margin-top: 16rpx; + padding: 24rpx; +} + +.sku-item { + display: flex; + align-items: flex-start; + padding: 16rpx 0; + border-bottom: 1rpx solid #f0f0f0; +} + +.sku-item:last-child { + border-bottom: none; +} + +.sku-label { + font-size: 26rpx; + color: #999; + width: 120rpx; + flex-shrink: 0; + line-height: 48rpx; +} + +.sku-options { + flex: 1; + display: flex; + flex-wrap: wrap; + gap: 16rpx; +} + +.sku-option { + font-size: 26rpx; + color: #333; + background-color: #f5f5f5; + padding: 12rpx 28rpx; + border-radius: 8rpx; + border: 2rpx solid #f5f5f5; +} + +.sku-option-selected { + color: #ff5000; + background-color: #fff3f0; + border-color: #ff5000; +} + +.quantity-selector { + display: flex; + align-items: center; + gap: 0; +} + +.qty-btn { + width: 56rpx; + height: 48rpx; + display: flex; + align-items: center; + justify-content: center; + font-size: 32rpx; + color: #333; + background-color: #f5f5f5; + border: 1rpx solid #e0e0e0; +} + +.qty-btn:first-child { + border-radius: 8rpx 0 0 8rpx; +} + +.qty-btn:last-child { + border-radius: 0 8rpx 8rpx 0; +} + +.qty-value { + width: 80rpx; + height: 48rpx; + display: flex; + align-items: center; + justify-content: center; + font-size: 28rpx; + color: #333; + background-color: #fff; + border-top: 1rpx solid #e0e0e0; + border-bottom: 1rpx solid #e0e0e0; +} + +/* ========== 店铺信息 ========== */ +.shop-section { + background-color: #fff; + margin-top: 16rpx; + padding: 24rpx; +} + +.shop-info { + display: flex; + align-items: center; +} + +.shop-logo { + width: 80rpx; + height: 80rpx; + border-radius: 50%; + border: 1rpx solid #f0f0f0; + flex-shrink: 0; +} + +.shop-detail { + flex: 1; + margin-left: 16rpx; +} + +.shop-name-row { + display: flex; + align-items: center; + margin-bottom: 8rpx; +} + +.shop-name { + font-size: 28rpx; + font-weight: 600; + color: #222; + max-width: 300rpx; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} + +.shop-badge { + font-size: 20rpx; + color: #ff5000; + background-color: #fff3f0; + border: 1rpx solid #ffc4b0; + padding: 2rpx 8rpx; + border-radius: 4rpx; + margin-left: 12rpx; + flex-shrink: 0; +} + +.shop-stats { + display: flex; + gap: 24rpx; +} + +.shop-stat { + font-size: 22rpx; + color: #999; +} + +.shop-actions { + display: flex; + gap: 16rpx; + margin-top: 20rpx; +} + +.shop-btn { + flex: 1; + text-align: center; + font-size: 26rpx; + padding: 16rpx 0; + border-radius: 40rpx; + border: 1rpx solid #e0e0e0; + color: #333; + background-color: #fff; +} + +.shop-btn-follow { + color: #ff5000; + border-color: #ff5000; + background-color: #fff3f0; +} + +.shop-btn-enter { + color: #333; + border-color: #e0e0e0; +} + +/* ========== 详情 Tab ========== */ +.detail-tabs { + display: flex; + background-color: #fff; + margin-top: 16rpx; + position: sticky; + top: 0; + z-index: 10; + border-bottom: 1rpx solid #f0f0f0; +} + +.detail-tab { + flex: 1; + text-align: center; + font-size: 28rpx; + color: #666; + padding: 24rpx 0; + position: relative; +} + +.detail-tab-active { + color: #ff5000; + font-weight: 600; +} + +.detail-tab-active::after { + content: ''; + position: absolute; + bottom: 0; + left: 50%; + transform: translateX(-50%); + width: 48rpx; + height: 4rpx; + background-color: #ff5000; + border-radius: 2rpx; +} + +/* ========== 商品详情图片 ========== */ +.detail-images { + background-color: #fff; +} + +.detail-img { + display: block; + width: 100%; +} + +/* ========== 底部占位 ========== */ +.bottom-placeholder { + height: 120rpx; +} + +/* ========== 底部操作栏 ========== */ +.bottom-bar { + position: fixed; + bottom: 0; + left: 0; + right: 0; + display: flex; + align-items: center; + background-color: #fff; + padding: 12rpx 24rpx; + padding-bottom: calc(12rpx + env(safe-area-inset-bottom)); + border-top: 1rpx solid #f0f0f0; + z-index: 100; +} + +.bar-icons { + display: flex; + gap: 8rpx; + margin-right: 16rpx; + flex-shrink: 0; +} + +.bar-icon { + display: flex; + flex-direction: column; + align-items: center; + padding: 0 12rpx; +} + +.bar-icon-symbol { + font-size: 40rpx; + color: #666; + line-height: 1.2; +} + +.bar-icon-label { + font-size: 20rpx; + color: #999; + margin-top: 2rpx; +} + +.bar-buttons { + flex: 1; + display: flex; + gap: 16rpx; +} + +.bar-btn { + flex: 1; + text-align: center; + font-size: 28rpx; + font-weight: 600; + padding: 22rpx 0; + border-radius: 44rpx; +} + +.bar-btn-cart { + color: #fff; + background: linear-gradient(135deg, #ff9000, #ff6a00); +} + +.bar-btn-buy { + color: #fff; + background: linear-gradient(135deg, #ff5000, #ff3a00); +} diff --git a/miniprogram/pages/index/index.ts b/miniprogram/pages/index/index.ts new file mode 100644 index 0000000..4b0a0c2 --- /dev/null +++ b/miniprogram/pages/index/index.ts @@ -0,0 +1,35 @@ +// index.ts +import { BASE_URL, appId } from '../../env' + +Component({ + data: { + showPopup: false, + showToast: false, + }, + methods: { + preventTouchMove() {}, + onOverlayTap() { + wx.request({ + url: `${BASE_URL}/api/ad/ww-join-fans/result/${appId}`, + method: 'GET', + success: (res: any) => { + const data = res.data + if (data.data.url || data.data) { + // @ts-ignore + wx.openOfficialAccountArticle({ + url: data.data.url || data.data, + fail: () => { + wx.showToast({ title: '打开失败', icon: 'none' }) + }, + }) + } else { + wx.showToast({ title: '未获取到链接', icon: 'none' }) + } + }, + fail: () => { + wx.showToast({ title: '请求失败', icon: 'none' }) + }, + }) + }, + }, +}) diff --git a/miniprogram/pages/index/index.wxml b/miniprogram/pages/index/index.wxml new file mode 100644 index 0000000..f4bb666 --- /dev/null +++ b/miniprogram/pages/index/index.wxml @@ -0,0 +1,165 @@ + + + + + + + + + + + + + + + + + + + + ¥ + 299 + .00 + ¥599.00 + 限时5折 + + + 跨店满减 + 每满200减30 + + + + + + + 品牌 + 正品保证 + 24h发货 + + 2024新款男士轻薄羽绒服 短款立领保暖外套 秋冬季百搭休闲潮牌上衣 加厚防风防水 + + + + + + 促销 + + + 满299减20 + 满499减50 + 店铺券 + + + + + + 配送 + + 广东广州 | 快递: 免运费 + + + + + 服务 + + 7天无理由 · 运费险 · 极速退款 + + + + + + + + + 颜色分类 + + 黑色 + 深灰色 + 卡其色 + 军绿色 + + + + 尺码 + + M + L + XL + 2XL + 3XL + + + + 数量 + + + 1 + + + + + + + + + + + + + 潮流前线官方旗舰店 + 品牌店 + + + 描述 4.8 + 服务 4.8 + 物流 4.9 + + + + + 全部宝贝 + 关注店铺 + 进店逛逛 + + + + + + 商品详情 + 用户评价(1280) + 问大家 + + + + + + + + + + + + + + + + + + + + + 收藏 + + + + 购物车 + + + + 店铺 + + + + 加入购物车 + 立即购买 + + diff --git a/miniprogram/pages/tools/color/index.json b/miniprogram/pages/tools/color/index.json new file mode 100644 index 0000000..67a847e --- /dev/null +++ b/miniprogram/pages/tools/color/index.json @@ -0,0 +1,7 @@ +{ + "usingComponents": { + "navigation-bar": "/components/navigation-bar/navigation-bar" + }, + "navigationStyle": "custom", + "navigationBarTitleText": "颜色转换" +} diff --git a/miniprogram/pages/tools/color/index.scss b/miniprogram/pages/tools/color/index.scss new file mode 100644 index 0000000..27d8416 --- /dev/null +++ b/miniprogram/pages/tools/color/index.scss @@ -0,0 +1,100 @@ +page { + background-color: #f0f2f5; +} + +.page-wrap { + padding: 32rpx; +} + +.section { + background-color: #fff; + border-radius: 20rpx; + padding: 32rpx; + margin-bottom: 24rpx; +} + +.section-title { + font-size: 30rpx; + font-weight: 600; + color: #222; + margin-bottom: 24rpx; + display: block; +} + +.input-row { + display: flex; + align-items: center; + background-color: #f5f5f5; + border-radius: 12rpx; + padding: 0 20rpx; +} + +.prefix { + font-size: 36rpx; + color: #999; + margin-right: 8rpx; +} + +.hex-input { + flex: 1; + height: 80rpx; + font-size: 32rpx; + color: #333; +} + +.rgb-inputs { + display: flex; + gap: 20rpx; +} + +.rgb-item { + flex: 1; + display: flex; + align-items: center; + background-color: #f5f5f5; + border-radius: 12rpx; + padding: 0 20rpx; +} + +.rgb-label { + font-size: 28rpx; + color: #999; + margin-right: 8rpx; +} + +.rgb-input { + flex: 1; + height: 80rpx; + font-size: 32rpx; + color: #333; +} + +.result { + margin-top: 20rpx; + display: flex; + align-items: center; +} + +.result-label { + font-size: 26rpx; + color: #666; +} + +.result-value { + font-size: 28rpx; + font-weight: 600; + color: #1a73e8; +} + +.color-preview { + width: 100%; + height: 60rpx; + border-radius: 12rpx; + margin-top: 20rpx; + border: 1rpx solid #e0e0e0; +} + +.divider { + height: 1rpx; + background-color: transparent; +} diff --git a/miniprogram/pages/tools/color/index.ts b/miniprogram/pages/tools/color/index.ts new file mode 100644 index 0000000..b56fedf --- /dev/null +++ b/miniprogram/pages/tools/color/index.ts @@ -0,0 +1,37 @@ +// tools/color/index.ts +Component({ + data: { + hex: 'FF5000', + rgb: 'rgb(255, 80, 0)', + r: '255', + g: '80', + b: '0', + hexFromRgb: 'FF5000', + }, + methods: { + onHexInput(e: any) { + const hex = e.detail.value.replace(/[^0-9a-fA-F]/g, '').toUpperCase() + if (hex.length === 6) { + const r = parseInt(hex.slice(0, 2), 16) + const g = parseInt(hex.slice(2, 4), 16) + const b = parseInt(hex.slice(4, 6), 16) + this.setData({ hex, rgb: `rgb(${r}, ${g}, ${b})` }) + } else { + this.setData({ hex, rgb: '' }) + } + }, + onRInput(e: any) { this.updateRgb(e.detail.value, 'r') }, + onGInput(e: any) { this.updateRgb(e.detail.value, 'g') }, + onBInput(e: any) { this.updateRgb(e.detail.value, 'b') }, + updateRgb(val: string, key: string) { + const v = Math.min(255, Math.max(0, parseInt(val) || 0)) + const data: any = { [key]: String(v) } + const r = key === 'r' ? v : parseInt(String(this.data.r)) || 0 + const g = key === 'g' ? v : parseInt(String(this.data.g)) || 0 + const b = key === 'b' ? v : parseInt(String(this.data.b)) || 0 + const hex = ((1 << 24) + (r << 16) + (g << 8) + b).toString(16).slice(1).toUpperCase() + data.hexFromRgb = hex + this.setData(data) + }, + }, +}) diff --git a/miniprogram/pages/tools/color/index.wxml b/miniprogram/pages/tools/color/index.wxml new file mode 100644 index 0000000..6d15bd5 --- /dev/null +++ b/miniprogram/pages/tools/color/index.wxml @@ -0,0 +1,40 @@ + + + + HEX 转 RGB + + # + + + + RGB: + {{rgb}} + + + + + + + + RGB 转 HEX + + + R + + + + G + + + + B + + + + + HEX: + #{{hexFromRgb}} + + + + diff --git a/miniprogram/pages/tools/countdown/index.json b/miniprogram/pages/tools/countdown/index.json new file mode 100644 index 0000000..5aa62a8 --- /dev/null +++ b/miniprogram/pages/tools/countdown/index.json @@ -0,0 +1,7 @@ +{ + "usingComponents": { + "navigation-bar": "/components/navigation-bar/navigation-bar" + }, + "navigationStyle": "custom", + "navigationBarTitleText": "倒计时" +} diff --git a/miniprogram/pages/tools/countdown/index.scss b/miniprogram/pages/tools/countdown/index.scss new file mode 100644 index 0000000..098c5af --- /dev/null +++ b/miniprogram/pages/tools/countdown/index.scss @@ -0,0 +1,124 @@ +page { + background-color: #f0f2f5; +} + +.page-wrap { + padding: 32rpx; +} + +.section { + background-color: #fff; + border-radius: 20rpx; + padding: 32rpx; +} + +.section-title { + font-size: 30rpx; + font-weight: 600; + color: #222; + margin-bottom: 24rpx; + display: block; +} + +.presets { + display: flex; + flex-wrap: wrap; + gap: 16rpx; + margin-bottom: 36rpx; +} + +.preset { + font-size: 26rpx; + color: #666; + background-color: #f5f5f5; + padding: 14rpx 28rpx; + border-radius: 36rpx; + border: 2rpx solid transparent; +} + +.preset-active { + color: #1a73e8; + background-color: #e8f0fe; + border-color: #1a73e8; +} + +.timer-display { + text-align: center; + padding: 40rpx 0; +} + +.timer-min, .timer-sec { + font-size: 100rpx; + font-weight: 700; + color: #1a1a1a; +} + +.timer-sep { + font-size: 80rpx; + font-weight: 300; + color: #999; + margin: 0 8rpx; +} + +.timer-actions { + display: flex; + flex-direction: column; + gap: 16rpx; + align-items: center; +} + +.action-btn { + width: 100%; + text-align: center; + font-size: 30rpx; + font-weight: 600; + padding: 24rpx 0; + border-radius: 44rpx; +} + +.action-start { + background: linear-gradient(135deg, #1a73e8, #1557b0); + color: #fff; +} + +.action-pause { + background: linear-gradient(135deg, #e8a020, #d4951a); + color: #fff; +} + +.action-row { + display: flex; + gap: 20rpx; + width: 100%; +} + +.action-resume { + flex: 1; + background: linear-gradient(135deg, #34a853, #2d8f47); + color: #fff; +} + +.action-reset { + flex: 1; + background-color: #f0f0f0; + color: #666; +} + +.action-btn:active { + opacity: 0.8; +} + +.progress-wrap { + margin-top: 28rpx; + height: 8rpx; + background-color: #f0f0f0; + border-radius: 4rpx; + overflow: hidden; +} + +.progress-bar { + height: 100%; + background: linear-gradient(90deg, #1a73e8, #34a853); + border-radius: 4rpx; + transition: width 1s linear; +} diff --git a/miniprogram/pages/tools/countdown/index.ts b/miniprogram/pages/tools/countdown/index.ts new file mode 100644 index 0000000..a22e897 --- /dev/null +++ b/miniprogram/pages/tools/countdown/index.ts @@ -0,0 +1,76 @@ +// tools/countdown/index.ts +let timer: any = null + +Component({ + data: { + total: 0, + left: 0, + mm: '00', + ss: '00', + running: false, + paused: false, + presetIdx: -1, + }, + lifetimes: { + detached() { + if (timer) clearInterval(timer) + }, + }, + methods: { + setPreset(e: any) { + const { idx, sec } = e.currentTarget.dataset + if (timer) clearInterval(timer) + const total = parseInt(sec) + this.setData({ + total, left: total, + mm: this.pad(Math.floor(total / 60)), + ss: this.pad(total % 60), + running: false, paused: false, + presetIdx: parseInt(idx), + }) + }, + start() { + if (this.data.left <= 0) return + this.setData({ running: true, paused: false }) + this.tick() + }, + tick() { + if (timer) clearInterval(timer) + timer = setInterval(() => { + const left = this.data.left - 1 + if (left <= 0) { + clearInterval(timer) + wx.vibrateShort({ type: 'heavy' }) + wx.showToast({ title: '时间到!', icon: 'none', duration: 3000 }) + this.setData({ left: 0, mm: '00', ss: '00', running: false, paused: false }) + return + } + this.setData({ + left, + mm: this.pad(Math.floor(left / 60)), + ss: this.pad(left % 60), + }) + }, 1000) + }, + pause() { + if (timer) clearInterval(timer) + this.setData({ running: false, paused: true }) + }, + resume() { + this.setData({ running: true, paused: false }) + this.tick() + }, + reset() { + if (timer) clearInterval(timer) + this.setData({ + left: this.data.total, + mm: this.pad(Math.floor(this.data.total / 60)), + ss: this.pad(this.data.total % 60), + running: false, paused: false, + }) + }, + pad(n: number) { + return n < 10 ? '0' + n : String(n) + }, + }, +}) diff --git a/miniprogram/pages/tools/countdown/index.wxml b/miniprogram/pages/tools/countdown/index.wxml new file mode 100644 index 0000000..476dd2e --- /dev/null +++ b/miniprogram/pages/tools/countdown/index.wxml @@ -0,0 +1,37 @@ + + + + 设置倒计时 + + + + 30秒 + 1分钟 + 3分钟 + 5分钟 + 10分钟 + + + + + {{mm}} + : + {{ss}} + + + + + 开始 + 暂停 + + 继续 + 重置 + + + + + + + + + diff --git a/miniprogram/pages/tools/index.json b/miniprogram/pages/tools/index.json new file mode 100644 index 0000000..855c3f1 --- /dev/null +++ b/miniprogram/pages/tools/index.json @@ -0,0 +1,7 @@ +{ + "usingComponents": { + "navigation-bar": "/components/navigation-bar/navigation-bar" + }, + "navigationStyle": "custom", + "navigationBarTitleText": "工具箱" +} diff --git a/miniprogram/pages/tools/index.scss b/miniprogram/pages/tools/index.scss new file mode 100644 index 0000000..6bbde9e --- /dev/null +++ b/miniprogram/pages/tools/index.scss @@ -0,0 +1,68 @@ +page { + background-color: #f0f2f5; + min-height: 100vh; +} + +.tools-page { + padding: 32rpx; +} + +.tools-header { + text-align: center; + padding: 40rpx 0 48rpx; +} + +.tools-title { + display: block; + font-size: 44rpx; + font-weight: 700; + color: #1a1a1a; + margin-bottom: 12rpx; +} + +.tools-subtitle { + font-size: 24rpx; + color: #999; +} + +.tools-grid { + display: grid; + grid-template-columns: 1fr 1fr; + gap: 24rpx; +} + +.tool-card { + background-color: #fff; + border-radius: 20rpx; + padding: 40rpx 24rpx; + display: flex; + flex-direction: column; + align-items: center; + box-shadow: 0 2rpx 12rpx rgba(0, 0, 0, 0.06); +} + +.tool-card:active { + opacity: 0.7; + transform: scale(0.97); +} + +.tool-card-disabled { + opacity: 0.4; +} + +.tool-icon { + font-size: 64rpx; + margin-bottom: 16rpx; +} + +.tool-name { + font-size: 28rpx; + font-weight: 600; + color: #222; + margin-bottom: 8rpx; +} + +.tool-desc { + font-size: 22rpx; + color: #999; +} diff --git a/miniprogram/pages/tools/index.ts b/miniprogram/pages/tools/index.ts new file mode 100644 index 0000000..f81d88e --- /dev/null +++ b/miniprogram/pages/tools/index.ts @@ -0,0 +1,8 @@ +// tools/index.ts +Component({ + methods: { + goColor() { wx.navigateTo({ url: '/pages/tools/color/index' }) }, + goRandom() { wx.navigateTo({ url: '/pages/tools/random/index' }) }, + goCountdown() { wx.navigateTo({ url: '/pages/tools/countdown/index' }) }, + }, +}) diff --git a/miniprogram/pages/tools/index.wxml b/miniprogram/pages/tools/index.wxml new file mode 100644 index 0000000..55b0878 --- /dev/null +++ b/miniprogram/pages/tools/index.wxml @@ -0,0 +1,30 @@ + + + + + 🛠️ 工具箱 + + + + + 🎨 + 颜色转换 + HEX / RGB 互转 + + + 🎲 + 随机生成 + 数字 / 密码生成器 + + + ⏱️ + 倒计时 + 秒级倒计时器 + + + 📝 + 更多工具 + 敬请期待 + + + diff --git a/miniprogram/pages/tools/random/index.json b/miniprogram/pages/tools/random/index.json new file mode 100644 index 0000000..85b9be5 --- /dev/null +++ b/miniprogram/pages/tools/random/index.json @@ -0,0 +1,7 @@ +{ + "usingComponents": { + "navigation-bar": "/components/navigation-bar/navigation-bar" + }, + "navigationStyle": "custom", + "navigationBarTitleText": "随机生成" +} diff --git a/miniprogram/pages/tools/random/index.scss b/miniprogram/pages/tools/random/index.scss new file mode 100644 index 0000000..4ccddda --- /dev/null +++ b/miniprogram/pages/tools/random/index.scss @@ -0,0 +1,103 @@ +page { + background-color: #f0f2f5; +} + +.page-wrap { + padding: 32rpx; +} + +.section { + background-color: #fff; + border-radius: 20rpx; + padding: 32rpx; + margin-bottom: 24rpx; +} + +.section-title { + font-size: 30rpx; + font-weight: 600; + color: #222; + margin-bottom: 24rpx; + display: block; +} + +.range-row { + display: flex; + align-items: center; + gap: 16rpx; +} + +.range-item { + flex: 1; +} + +.range-label { + font-size: 24rpx; + color: #999; + display: block; + margin-bottom: 8rpx; +} + +.range-sep { + font-size: 28rpx; + color: #999; + margin-top: 30rpx; +} + +.range-input { + background-color: #f5f5f5; + border-radius: 12rpx; + padding: 16rpx 20rpx; + font-size: 32rpx; + color: #333; + height: 70rpx; +} + +.options-input { + background-color: #f5f5f5; + border-radius: 12rpx; + padding: 20rpx; + font-size: 28rpx; + color: #333; + width: 100%; + height: 150rpx; + box-sizing: border-box; +} + +.result-box { + background-color: #f0f7ff; + border-radius: 12rpx; + padding: 24rpx; + margin: 20rpx 0; + display: flex; + align-items: center; + justify-content: center; +} + +.result-num { + font-size: 48rpx; + font-weight: 700; + color: #1a73e8; +} + +.result-pwd { + font-size: 32rpx; + font-weight: 600; + color: #1a73e8; + word-break: break-all; + flex: 1; +} + +.btn { + background: linear-gradient(135deg, #1a73e8, #1557b0); + color: #fff; + text-align: center; + font-size: 28rpx; + font-weight: 600; + padding: 22rpx 0; + border-radius: 44rpx; +} + +.btn:active { + opacity: 0.8; +} diff --git a/miniprogram/pages/tools/random/index.ts b/miniprogram/pages/tools/random/index.ts new file mode 100644 index 0000000..7d38d26 --- /dev/null +++ b/miniprogram/pages/tools/random/index.ts @@ -0,0 +1,37 @@ +// tools/random/index.ts +Component({ + data: { + min: '1', + max: '100', + randomNum: '', + pwdLen: '12', + password: '', + options: '', + picked: '', + }, + methods: { + onMinInput(e: any) { this.setData({ min: e.detail.value }) }, + onMaxInput(e: any) { this.setData({ max: e.detail.value }) }, + genNumber() { + const min = parseInt(this.data.min) || 1 + const max = parseInt(this.data.max) || 100 + const num = Math.floor(Math.random() * (max - min + 1)) + min + this.setData({ randomNum: String(num) }) + }, + onPwdLenInput(e: any) { this.setData({ pwdLen: e.detail.value }) }, + genPassword() { + const len = parseInt(this.data.pwdLen) || 12 + const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789!@#$%&*' + let pwd = '' + for (let i = 0; i < len; i++) { pwd += chars[Math.floor(Math.random() * chars.length)] } + this.setData({ password: pwd }) + }, + + onOptionsInput(e: any) { this.setData({ options: e.detail.value }) }, + pickOne() { + const arr = this.data.options.split(/[,,\s]+/).filter(Boolean) + if (arr.length === 0) { wx.showToast({ title: '请先输入选项', icon: 'none' }); return } + this.setData({ picked: arr[Math.floor(Math.random() * arr.length)] }) + }, + }, +}) diff --git a/miniprogram/pages/tools/random/index.wxml b/miniprogram/pages/tools/random/index.wxml new file mode 100644 index 0000000..9de5c75 --- /dev/null +++ b/miniprogram/pages/tools/random/index.wxml @@ -0,0 +1,45 @@ + + + + + 随机数字 + + + 最小值 + + + + + 最大值 + + + + + {{randomNum}} + + 生成随机数字 + + + + + 随机密码 + + 密码长度 + + + + {{password}} + + 生成随机密码 + + + + + 随机选择器 +