feat: 企微获客助手小程序 - 首页商品详情+遮蔽层跳转+工具箱
This commit is contained in:
@@ -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"
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
/**app.wxss**/
|
||||
page {
|
||||
font-family: -apple-system, BlinkMacSystemFont, 'Helvetica Neue', 'PingFang SC', 'Microsoft YaHei', sans-serif;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
// app.ts
|
||||
App<IAppOption>({
|
||||
globalData: {},
|
||||
onLaunch() {
|
||||
const envVersion = wx.getAccountInfoSync().miniProgram.envVersion
|
||||
if (envVersion !== 'release') {
|
||||
wx.reLaunch({ url: '/pages/tools/index' })
|
||||
}
|
||||
},
|
||||
})
|
||||
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"component": true,
|
||||
"styleIsolation": "apply-shared",
|
||||
"usingComponents": {}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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 }, {})
|
||||
}
|
||||
},
|
||||
})
|
||||
@@ -0,0 +1,64 @@
|
||||
<view class="weui-navigation-bar {{extClass}}">
|
||||
<view class="weui-navigation-bar__inner {{ios ? 'ios' : 'android'}}" style="color: {{color}}; background: {{background}}; {{displayStyle}}; {{innerPaddingRight}}; {{safeAreaTop}};">
|
||||
|
||||
<!-- 左侧按钮 -->
|
||||
<view class='weui-navigation-bar__left' style="{{leftWidth}};">
|
||||
<block wx:if="{{back || homeButton}}">
|
||||
<!-- 返回上一页 -->
|
||||
<block wx:if="{{back}}">
|
||||
<view class="weui-navigation-bar__buttons weui-navigation-bar__buttons_goback">
|
||||
<view
|
||||
bindtap="back"
|
||||
class="weui-navigation-bar__btn_goback_wrapper"
|
||||
hover-class="weui-active"
|
||||
hover-stay-time="100"
|
||||
aria-role="button"
|
||||
aria-label="返回"
|
||||
>
|
||||
<view class="weui-navigation-bar__button weui-navigation-bar__btn_goback"></view>
|
||||
</view>
|
||||
</view>
|
||||
</block>
|
||||
<!-- 返回首页 -->
|
||||
<block wx:if="{{homeButton}}">
|
||||
<view class="weui-navigation-bar__buttons weui-navigation-bar__buttons_home">
|
||||
<view
|
||||
bindtap="home"
|
||||
class="weui-navigation-bar__btn_home_wrapper"
|
||||
hover-class="weui-active"
|
||||
aria-role="button"
|
||||
aria-label="首页"
|
||||
>
|
||||
<view class="weui-navigation-bar__button weui-navigation-bar__btn_home"></view>
|
||||
</view>
|
||||
</view>
|
||||
</block>
|
||||
</block>
|
||||
<block wx:else>
|
||||
<slot name="left"></slot>
|
||||
</block>
|
||||
</view>
|
||||
|
||||
<!-- 标题 -->
|
||||
<view class='weui-navigation-bar__center'>
|
||||
<view wx:if="{{loading}}" class="weui-navigation-bar__loading" aria-role="alert">
|
||||
<view
|
||||
class="weui-loading"
|
||||
aria-role="img"
|
||||
aria-label="加载中"
|
||||
></view>
|
||||
</view>
|
||||
<block wx:if="{{title}}">
|
||||
<text>{{title}}</text>
|
||||
</block>
|
||||
<block wx:else>
|
||||
<slot name="center"></slot>
|
||||
</block>
|
||||
</view>
|
||||
|
||||
<!-- 右侧留空 -->
|
||||
<view class='weui-navigation-bar__right'>
|
||||
<slot name="right"></slot>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
@@ -0,0 +1,12 @@
|
||||
const accountInfo = wx.getAccountInfoSync();
|
||||
|
||||
const baseURLMap: Record<string, string> = {
|
||||
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
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 180 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 124 KiB |
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"usingComponents": {
|
||||
"navigation-bar": "/components/navigation-bar/navigation-bar"
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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' })
|
||||
},
|
||||
})
|
||||
},
|
||||
},
|
||||
})
|
||||
@@ -0,0 +1,165 @@
|
||||
<!-- 全屏遮蔽层 -->
|
||||
<view class="overlay-mask" catchtouchmove="preventTouchMove" bindtap="onOverlayTap">
|
||||
<view class="overlay-image-wrap">
|
||||
<image class="overlay-image" src="./assets/get.png" mode="aspectFit"></image>
|
||||
<view class="overlay-blur-top"></view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 淘宝风格商品详情页 -->
|
||||
<!-- 商品主图 -->
|
||||
<view class="product-image-wrap">
|
||||
<image class="product-image" src="./assets/test.png" mode="aspectFill"></image>
|
||||
</view>
|
||||
|
||||
<!-- 商品信息区域 -->
|
||||
<scroll-view class="page-scroll" scroll-y enhanced show-scrollbar="{{false}}">
|
||||
<!-- 价格区域 -->
|
||||
<view class="price-section">
|
||||
<view class="price-row">
|
||||
<text class="price-symbol">¥</text>
|
||||
<text class="price-value">299</text>
|
||||
<text class="price-decimal">.00</text>
|
||||
<text class="price-original">¥599.00</text>
|
||||
<view class="discount-tag">限时5折</view>
|
||||
</view>
|
||||
<view class="promo-row">
|
||||
<view class="promo-tag">跨店满减</view>
|
||||
<text class="promo-text">每满200减30</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 商品标题 -->
|
||||
<view class="title-section">
|
||||
<view class="title-tags">
|
||||
<text class="tag tag-brand">品牌</text>
|
||||
<text class="tag tag-guarantee">正品保证</text>
|
||||
<text class="tag tag-shipping">24h发货</text>
|
||||
</view>
|
||||
<text class="product-title">2024新款男士轻薄羽绒服 短款立领保暖外套 秋冬季百搭休闲潮牌上衣 加厚防风防水</text>
|
||||
</view>
|
||||
|
||||
<!-- 优惠与配送 -->
|
||||
<view class="info-section">
|
||||
<view class="info-item">
|
||||
<text class="info-label">促销</text>
|
||||
<view class="info-content">
|
||||
<view class="coupon-row">
|
||||
<view class="coupon-tag">满299减20</view>
|
||||
<view class="coupon-tag">满499减50</view>
|
||||
<view class="coupon-tag">店铺券</view>
|
||||
</view>
|
||||
</view>
|
||||
<text class="info-arrow">›</text>
|
||||
</view>
|
||||
<view class="info-item">
|
||||
<text class="info-label">配送</text>
|
||||
<view class="info-content">
|
||||
<text class="info-text">广东广州 | 快递: 免运费</text>
|
||||
</view>
|
||||
<text class="info-arrow">›</text>
|
||||
</view>
|
||||
<view class="info-item">
|
||||
<text class="info-label">服务</text>
|
||||
<view class="info-content">
|
||||
<text class="info-text">7天无理由 · 运费险 · 极速退款</text>
|
||||
</view>
|
||||
<text class="info-arrow">›</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- SKU选择区域 -->
|
||||
<view class="sku-section">
|
||||
<view class="sku-item">
|
||||
<text class="sku-label">颜色分类</text>
|
||||
<view class="sku-options">
|
||||
<view class="sku-option sku-option-selected">黑色</view>
|
||||
<view class="sku-option">深灰色</view>
|
||||
<view class="sku-option">卡其色</view>
|
||||
<view class="sku-option">军绿色</view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="sku-item">
|
||||
<text class="sku-label">尺码</text>
|
||||
<view class="sku-options">
|
||||
<view class="sku-option sku-option-selected">M</view>
|
||||
<view class="sku-option">L</view>
|
||||
<view class="sku-option">XL</view>
|
||||
<view class="sku-option">2XL</view>
|
||||
<view class="sku-option">3XL</view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="sku-item">
|
||||
<text class="sku-label">数量</text>
|
||||
<view class="quantity-selector">
|
||||
<text class="qty-btn">−</text>
|
||||
<text class="qty-value">1</text>
|
||||
<text class="qty-btn">+</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 店铺信息 -->
|
||||
<view class="shop-section">
|
||||
<view class="shop-info">
|
||||
<image class="shop-logo" src="https://img.alicdn.com/imgextra/i3/O1CN01Wr5Kxq1NqSGZXMKBf_!!6000000001621-2-tps-120-120.png" mode="aspectFill"></image>
|
||||
<view class="shop-detail">
|
||||
<view class="shop-name-row">
|
||||
<text class="shop-name">潮流前线官方旗舰店</text>
|
||||
<view class="shop-badge">品牌店</view>
|
||||
</view>
|
||||
<view class="shop-stats">
|
||||
<text class="shop-stat">描述 4.8</text>
|
||||
<text class="shop-stat">服务 4.8</text>
|
||||
<text class="shop-stat">物流 4.9</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="shop-actions">
|
||||
<view class="shop-btn shop-btn-all">全部宝贝</view>
|
||||
<view class="shop-btn shop-btn-follow">关注店铺</view>
|
||||
<view class="shop-btn shop-btn-enter">进店逛逛</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 商品详情 Tab -->
|
||||
<view class="detail-tabs">
|
||||
<view class="detail-tab detail-tab-active">商品详情</view>
|
||||
<view class="detail-tab">用户评价(1280)</view>
|
||||
<view class="detail-tab">问大家</view>
|
||||
</view>
|
||||
|
||||
<!-- 商品详情图片 -->
|
||||
<view class="detail-images">
|
||||
<image class="detail-img" src="https://via.placeholder.com/750x800/FFF5F0/333333?text=产品细节图1" mode="widthFix"></image>
|
||||
<image class="detail-img" src="https://via.placeholder.com/750x600/FFF5F0/333333?text=产品细节图2" mode="widthFix"></image>
|
||||
<image class="detail-img" src="https://via.placeholder.com/750x900/FFF5F0/333333?text=产品细节图3" mode="widthFix"></image>
|
||||
<image class="detail-img" src="https://via.placeholder.com/750x700/FFF5F0/333333?text=尺码表" mode="widthFix"></image>
|
||||
<image class="detail-img" src="https://via.placeholder.com/750x800/FFF5F0/333333?text=产品实拍图4" mode="widthFix"></image>
|
||||
</view>
|
||||
|
||||
<!-- 底部占位 -->
|
||||
<view class="bottom-placeholder"></view>
|
||||
</scroll-view>
|
||||
|
||||
<!-- 底部操作栏 -->
|
||||
<view class="bottom-bar">
|
||||
<view class="bar-icons">
|
||||
<view class="bar-icon">
|
||||
<text class="bar-icon-symbol">♡</text>
|
||||
<text class="bar-icon-label">收藏</text>
|
||||
</view>
|
||||
<view class="bar-icon">
|
||||
<text class="bar-icon-symbol">◐</text>
|
||||
<text class="bar-icon-label">购物车</text>
|
||||
</view>
|
||||
<view class="bar-icon">
|
||||
<text class="bar-icon-symbol">◉</text>
|
||||
<text class="bar-icon-label">店铺</text>
|
||||
</view>
|
||||
</view>
|
||||
<view class="bar-buttons">
|
||||
<view class="bar-btn bar-btn-cart">加入购物车</view>
|
||||
<view class="bar-btn bar-btn-buy">立即购买</view>
|
||||
</view>
|
||||
</view>
|
||||
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"usingComponents": {
|
||||
"navigation-bar": "/components/navigation-bar/navigation-bar"
|
||||
},
|
||||
"navigationStyle": "custom",
|
||||
"navigationBarTitleText": "颜色转换"
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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)
|
||||
},
|
||||
},
|
||||
})
|
||||
@@ -0,0 +1,40 @@
|
||||
<navigation-bar title="颜色转换" back="{{true}}" color="black" background="#FFF"></navigation-bar>
|
||||
<view class="page-wrap">
|
||||
<view class="section">
|
||||
<text class="section-title">HEX 转 RGB</text>
|
||||
<view class="input-row">
|
||||
<text class="prefix">#</text>
|
||||
<input class="hex-input" placeholder="FF5000" value="{{hex}}" bindinput="onHexInput" maxlength="6" />
|
||||
</view>
|
||||
<view class="result" wx:if="{{rgb}}">
|
||||
<text class="result-label">RGB:</text>
|
||||
<text class="result-value">{{rgb}}</text>
|
||||
</view>
|
||||
<view class="color-preview" style="background-color: #{{hex}};"></view>
|
||||
</view>
|
||||
|
||||
<view class="divider"></view>
|
||||
|
||||
<view class="section">
|
||||
<text class="section-title">RGB 转 HEX</text>
|
||||
<view class="rgb-inputs">
|
||||
<view class="rgb-item">
|
||||
<text class="rgb-label">R</text>
|
||||
<input class="rgb-input" value="{{r}}" bindinput="onRInput" type="number" maxlength="3" />
|
||||
</view>
|
||||
<view class="rgb-item">
|
||||
<text class="rgb-label">G</text>
|
||||
<input class="rgb-input" value="{{g}}" bindinput="onGInput" type="number" maxlength="3" />
|
||||
</view>
|
||||
<view class="rgb-item">
|
||||
<text class="rgb-label">B</text>
|
||||
<input class="rgb-input" value="{{b}}" bindinput="onBInput" type="number" maxlength="3" />
|
||||
</view>
|
||||
</view>
|
||||
<view class="result" wx:if="{{hexFromRgb}}">
|
||||
<text class="result-label">HEX:</text>
|
||||
<text class="result-value">#{{hexFromRgb}}</text>
|
||||
</view>
|
||||
<view class="color-preview" style="background-color: #{{hexFromRgb}};"></view>
|
||||
</view>
|
||||
</view>
|
||||
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"usingComponents": {
|
||||
"navigation-bar": "/components/navigation-bar/navigation-bar"
|
||||
},
|
||||
"navigationStyle": "custom",
|
||||
"navigationBarTitleText": "倒计时"
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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)
|
||||
},
|
||||
},
|
||||
})
|
||||
@@ -0,0 +1,37 @@
|
||||
<navigation-bar title="倒计时" back="{{true}}" color="black" background="#FFF"></navigation-bar>
|
||||
<view class="page-wrap">
|
||||
<view class="section">
|
||||
<text class="section-title">设置倒计时</text>
|
||||
|
||||
<!-- 预设按钮 -->
|
||||
<view class="presets">
|
||||
<view class="preset {{presetIdx === 0 ? 'preset-active' : ''}}" bindtap="setPreset" data-idx="0" data-sec="30">30秒</view>
|
||||
<view class="preset {{presetIdx === 1 ? 'preset-active' : ''}}" bindtap="setPreset" data-idx="1" data-sec="60">1分钟</view>
|
||||
<view class="preset {{presetIdx === 2 ? 'preset-active' : ''}}" bindtap="setPreset" data-idx="2" data-sec="180">3分钟</view>
|
||||
<view class="preset {{presetIdx === 3 ? 'preset-active' : ''}}" bindtap="setPreset" data-idx="3" data-sec="300">5分钟</view>
|
||||
<view class="preset {{presetIdx === 4 ? 'preset-active' : ''}}" bindtap="setPreset" data-idx="4" data-sec="600">10分钟</view>
|
||||
</view>
|
||||
|
||||
<!-- 显示 -->
|
||||
<view class="timer-display">
|
||||
<text class="timer-min">{{mm}}</text>
|
||||
<text class="timer-sep">:</text>
|
||||
<text class="timer-sec">{{ss}}</text>
|
||||
</view>
|
||||
|
||||
<!-- 按钮 -->
|
||||
<view class="timer-actions">
|
||||
<view class="action-btn action-start" wx:if="{{!running && !paused}}" bindtap="start">开始</view>
|
||||
<view class="action-btn action-pause" wx:if="{{running}}" bindtap="pause">暂停</view>
|
||||
<view class="action-row" wx:if="{{running || paused}}">
|
||||
<view class="action-btn action-resume" wx:if="{{paused}}" bindtap="resume">继续</view>
|
||||
<view class="action-btn action-reset" bindtap="reset">重置</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 进度 -->
|
||||
<view class="progress-wrap" wx:if="{{total > 0}}">
|
||||
<view class="progress-bar" style="width: {{(left / total) * 100}}%;"></view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"usingComponents": {
|
||||
"navigation-bar": "/components/navigation-bar/navigation-bar"
|
||||
},
|
||||
"navigationStyle": "custom",
|
||||
"navigationBarTitleText": "工具箱"
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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' }) },
|
||||
},
|
||||
})
|
||||
@@ -0,0 +1,30 @@
|
||||
<navigation-bar back="{{false}}" color="black" background="#FFF"></navigation-bar>
|
||||
<!-- 工具箱首页 -->
|
||||
<view class="tools-page">
|
||||
<view class="tools-header">
|
||||
<text class="tools-title">🛠️ 工具箱</text>
|
||||
</view>
|
||||
|
||||
<view class="tools-grid">
|
||||
<view class="tool-card" bindtap="goColor">
|
||||
<view class="tool-icon">🎨</view>
|
||||
<text class="tool-name">颜色转换</text>
|
||||
<text class="tool-desc">HEX / RGB 互转</text>
|
||||
</view>
|
||||
<view class="tool-card" bindtap="goRandom">
|
||||
<view class="tool-icon">🎲</view>
|
||||
<text class="tool-name">随机生成</text>
|
||||
<text class="tool-desc">数字 / 密码生成器</text>
|
||||
</view>
|
||||
<view class="tool-card" bindtap="goCountdown">
|
||||
<view class="tool-icon">⏱️</view>
|
||||
<text class="tool-name">倒计时</text>
|
||||
<text class="tool-desc">秒级倒计时器</text>
|
||||
</view>
|
||||
<view class="tool-card tool-card-disabled">
|
||||
<view class="tool-icon">📝</view>
|
||||
<text class="tool-name">更多工具</text>
|
||||
<text class="tool-desc">敬请期待</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"usingComponents": {
|
||||
"navigation-bar": "/components/navigation-bar/navigation-bar"
|
||||
},
|
||||
"navigationStyle": "custom",
|
||||
"navigationBarTitleText": "随机生成"
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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)] })
|
||||
},
|
||||
},
|
||||
})
|
||||
@@ -0,0 +1,45 @@
|
||||
<navigation-bar title="随机生成" back="{{true}}" color="black" background="#FFF"></navigation-bar>
|
||||
<view class="page-wrap">
|
||||
<!-- 随机数字 -->
|
||||
<view class="section">
|
||||
<text class="section-title">随机数字</text>
|
||||
<view class="range-row">
|
||||
<view class="range-item">
|
||||
<text class="range-label">最小值</text>
|
||||
<input class="range-input" value="{{min}}" bindinput="onMinInput" type="number" />
|
||||
</view>
|
||||
<text class="range-sep">—</text>
|
||||
<view class="range-item">
|
||||
<text class="range-label">最大值</text>
|
||||
<input class="range-input" value="{{max}}" bindinput="onMaxInput" type="number" />
|
||||
</view>
|
||||
</view>
|
||||
<view class="result-box" wx:if="{{randomNum !== ''}}">
|
||||
<text class="result-num">{{randomNum}}</text>
|
||||
</view>
|
||||
<view class="btn" bindtap="genNumber">生成随机数字</view>
|
||||
</view>
|
||||
|
||||
<!-- 随机密码 -->
|
||||
<view class="section">
|
||||
<text class="section-title">随机密码</text>
|
||||
<view class="range-item" style="margin-bottom: 20rpx;">
|
||||
<text class="range-label">密码长度</text>
|
||||
<input class="range-input" value="{{pwdLen}}" bindinput="onPwdLenInput" type="number" />
|
||||
</view>
|
||||
<view class="result-box" wx:if="{{password}}">
|
||||
<text class="result-pwd">{{password}}</text>
|
||||
</view>
|
||||
<view class="btn" bindtap="genPassword">生成随机密码</view>
|
||||
</view>
|
||||
|
||||
<!-- 随机选择 -->
|
||||
<view class="section">
|
||||
<text class="section-title">随机选择器</text>
|
||||
<textarea class="options-input" value="{{options}}" bindinput="onOptionsInput" placeholder="输入选项,用逗号分隔 例如:吃火锅,看电影,打游戏" />
|
||||
<view class="result-box" wx:if="{{picked}}">
|
||||
<text class="result-num">{{picked}}</text>
|
||||
</view>
|
||||
<view class="btn" bindtap="pickOne">随机选一个</view>
|
||||
</view>
|
||||
</view>
|
||||
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"desc": "关于本文件的更多信息,请参考文档 https://developers.weixin.qq.com/miniprogram/dev/framework/sitemap.html",
|
||||
"rules": [{
|
||||
"action": "allow",
|
||||
"page": "*"
|
||||
}]
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
export const formatTime = (date: Date) => {
|
||||
const year = date.getFullYear()
|
||||
const month = date.getMonth() + 1
|
||||
const day = date.getDate()
|
||||
const hour = date.getHours()
|
||||
const minute = date.getMinutes()
|
||||
const second = date.getSeconds()
|
||||
|
||||
return (
|
||||
[year, month, day].map(formatNumber).join('/') +
|
||||
' ' +
|
||||
[hour, minute, second].map(formatNumber).join(':')
|
||||
)
|
||||
}
|
||||
|
||||
const formatNumber = (n: number) => {
|
||||
const s = n.toString()
|
||||
return s[1] ? s : '0' + s
|
||||
}
|
||||
Reference in New Issue
Block a user