feat:重构按钮和icon组件

This commit is contained in:
R524809
2026-04-07 17:23:05 +08:00
parent 672d457a35
commit 653737c481
95 changed files with 3261 additions and 1342 deletions
+33 -36
View File
@@ -1,15 +1,15 @@
/* Logo 字体 */
@font-face {
font-family: 'iconfont logo';
/* src: url('https://at.alicdn.com/t/font_985780_km7mi63cihi.eot?t=1545807318834');
font-family: "iconfont logo";
src: url('https://at.alicdn.com/t/font_985780_km7mi63cihi.eot?t=1545807318834');
src: url('https://at.alicdn.com/t/font_985780_km7mi63cihi.eot?t=1545807318834#iefix') format('embedded-opentype'),
url('https://at.alicdn.com/t/font_985780_km7mi63cihi.woff?t=1545807318834') format('woff'),
url('https://at.alicdn.com/t/font_985780_km7mi63cihi.ttf?t=1545807318834') format('truetype'),
url('https://at.alicdn.com/t/font_985780_km7mi63cihi.svg?t=1545807318834#iconfont') format('svg'); */
url('https://at.alicdn.com/t/font_985780_km7mi63cihi.svg?t=1545807318834#iconfont') format('svg');
}
.logo {
font-family: 'iconfont logo';
font-family: "iconfont logo";
font-size: 160px;
font-style: normal;
-webkit-font-smoothing: antialiased;
@@ -48,6 +48,7 @@
color: #666;
}
#tabs .active {
border-bottom-color: #f00;
color: #222;
@@ -118,15 +119,9 @@
font-size: 42px;
margin: 10px auto;
color: #333;
-webkit-transition:
font-size 0.25s linear,
width 0.25s linear;
-moz-transition:
font-size 0.25s linear,
width 0.25s linear;
transition:
font-size 0.25s linear,
width 0.25s linear;
-webkit-transition: font-size 0.25s linear, width 0.25s linear;
-moz-transition: font-size 0.25s linear, width 0.25s linear;
transition: font-size 0.25s linear, width 0.25s linear;
}
.icon_lists .icon:hover {
@@ -288,7 +283,7 @@
}
.markdown>table th {
background: #f7f7f7;
background: #F7F7F7;
}
.markdown blockquote {
@@ -328,6 +323,7 @@
clear: both;
}
.hljs {
display: block;
background: white;
@@ -403,8 +399,8 @@ https://prismjs.com/download.html#themes=prism&languages=markup+css+clike+javasc
* Based on dabblet (http://dabblet.com)
* @author Lea Verou
*/
code[class*='language-'],
pre[class*='language-'] {
code[class*="language-"],
pre[class*="language-"] {
color: black;
background: none;
text-shadow: 0 1px white;
@@ -426,45 +422,46 @@ pre[class*='language-'] {
hyphens: none;
}
pre[class*='language-']::-moz-selection,
pre[class*='language-'] ::-moz-selection,
code[class*='language-']::-moz-selection,
code[class*='language-'] ::-moz-selection {
pre[class*="language-"]::-moz-selection,
pre[class*="language-"] ::-moz-selection,
code[class*="language-"]::-moz-selection,
code[class*="language-"] ::-moz-selection {
text-shadow: none;
background: #b3d4fc;
}
pre[class*='language-']::selection,
pre[class*='language-'] ::selection,
code[class*='language-']::selection,
code[class*='language-'] ::selection {
pre[class*="language-"]::selection,
pre[class*="language-"] ::selection,
code[class*="language-"]::selection,
code[class*="language-"] ::selection {
text-shadow: none;
background: #b3d4fc;
}
@media print {
code[class*='language-'],
pre[class*='language-'] {
code[class*="language-"],
pre[class*="language-"] {
text-shadow: none;
}
}
/* Code blocks */
pre[class*='language-'] {
pre[class*="language-"] {
padding: 1em;
margin: 0.5em 0;
margin: .5em 0;
overflow: auto;
}
:not(pre) > code[class*='language-'],
pre[class*='language-'] {
:not(pre)>code[class*="language-"],
pre[class*="language-"] {
background: #f5f2f0;
}
/* Inline code */
:not(pre) > code[class*='language-'] {
padding: 0.1em;
border-radius: 0.3em;
:not(pre)>code[class*="language-"] {
padding: .1em;
border-radius: .3em;
white-space: normal;
}
@@ -480,7 +477,7 @@ pre[class*='language-'] {
}
.namespace {
opacity: 0.7;
opacity: .7;
}
.token.property,
@@ -508,7 +505,7 @@ pre[class*='language-'] {
.language-css .token.string,
.style .token.string {
color: #9a6e3a;
background: hsla(0, 0%, 100%, 0.5);
background: hsla(0, 0%, 100%, .5);
}
.token.atrule,
@@ -519,7 +516,7 @@ pre[class*='language-'] {
.token.function,
.token.class-name {
color: #dd4a68;
color: #DD4A68;
}
.token.regex,
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+182
View File
@@ -5,6 +5,188 @@
"css_prefix_text": "icon-",
"description": "",
"glyphs": [
{
"icon_id": "47282109",
"name": "setting",
"font_class": "setting",
"unicode": "e61e",
"unicode_decimal": 58910
},
{
"icon_id": "47282098",
"name": "round-star-o",
"font_class": "round-star-o",
"unicode": "e620",
"unicode_decimal": 58912
},
{
"icon_id": "47281642",
"name": "plus",
"font_class": "plus",
"unicode": "e61f",
"unicode_decimal": 58911
},
{
"icon_id": "47281605",
"name": "star",
"font_class": "star",
"unicode": "e60d",
"unicode_decimal": 58893
},
{
"icon_id": "47281606",
"name": "minus",
"font_class": "minus",
"unicode": "e60e",
"unicode_decimal": 58894
},
{
"icon_id": "47281607",
"name": "round-star",
"font_class": "round-star",
"unicode": "e60f",
"unicode_decimal": 58895
},
{
"icon_id": "47281608",
"name": "star-o",
"font_class": "star-o",
"unicode": "e610",
"unicode_decimal": 58896
},
{
"icon_id": "47281609",
"name": "about-o",
"font_class": "about-o",
"unicode": "e611",
"unicode_decimal": 58897
},
{
"icon_id": "47281610",
"name": "feedback-o",
"font_class": "feedback-o",
"unicode": "e612",
"unicode_decimal": 58898
},
{
"icon_id": "47281611",
"name": "feedback",
"font_class": "feedback",
"unicode": "e613",
"unicode_decimal": 58899
},
{
"icon_id": "47281612",
"name": "about",
"font_class": "about",
"unicode": "e614",
"unicode_decimal": 58900
},
{
"icon_id": "47281613",
"name": "heart-o",
"font_class": "heart-o",
"unicode": "e615",
"unicode_decimal": 58901
},
{
"icon_id": "47281614",
"name": "heart",
"font_class": "heart",
"unicode": "e616",
"unicode_decimal": 58902
},
{
"icon_id": "47281615",
"name": "print",
"font_class": "print",
"unicode": "e617",
"unicode_decimal": 58903
},
{
"icon_id": "47281616",
"name": "family",
"font_class": "family",
"unicode": "e618",
"unicode_decimal": 58904
},
{
"icon_id": "47281617",
"name": "search-plus",
"font_class": "search-plus",
"unicode": "e619",
"unicode_decimal": 58905
},
{
"icon_id": "47281618",
"name": "search",
"font_class": "search",
"unicode": "e61a",
"unicode_decimal": 58906
},
{
"icon_id": "47281619",
"name": "clock-o",
"font_class": "clock-o",
"unicode": "e61b",
"unicode_decimal": 58907
},
{
"icon_id": "47281620",
"name": "clock",
"font_class": "clock",
"unicode": "e61c",
"unicode_decimal": 58908
},
{
"icon_id": "47281621",
"name": "search-minus",
"font_class": "search-minus",
"unicode": "e61d",
"unicode_decimal": 58909
},
{
"icon_id": "47279508",
"name": "setting-o",
"font_class": "setting-o",
"unicode": "e60c",
"unicode_decimal": 58892
},
{
"icon_id": "47278437",
"name": "line-share",
"font_class": "line-share",
"unicode": "e60b",
"unicode_decimal": 58891
},
{
"icon_id": "39768497",
"name": "arrow-up",
"font_class": "arrow-up",
"unicode": "e602",
"unicode_decimal": 58882
},
{
"icon_id": "39768498",
"name": "arrow-right",
"font_class": "arrow-right",
"unicode": "e603",
"unicode_decimal": 58883
},
{
"icon_id": "39768499",
"name": "arrow-down",
"font_class": "arrow-down",
"unicode": "e604",
"unicode_decimal": 58884
},
{
"icon_id": "39768501",
"name": "arrow-left",
"font_class": "arrow-left",
"unicode": "e605",
"unicode_decimal": 58885
},
{
"icon_id": "782338",
"name": "success",
File diff suppressed because one or more lines are too long
Binary file not shown.
Binary file not shown.
File diff suppressed because one or more lines are too long
@@ -3,6 +3,6 @@
"styleIsolation": "isolated",
"usingComponents": {
"van-popup": "@vant/weapp/popup/index",
"toy-button": "/ui/button/button"
"toy-button": "/toy/button-v2/button"
}
}
@@ -3,7 +3,7 @@
"styleIsolation": "isolated",
"usingComponents": {
"van-popup": "@vant/weapp/popup/index",
"van-icon": "@vant/weapp/icon/index",
"toy-button": "/ui/button/button"
"toy-icon": "/toy/icon/icon",
"toy-button": "/toy/button-v2/button"
}
}
@@ -11,7 +11,7 @@
</toy-button>
</view>
<view class="step-box {{currentStep}}-step {{platform}}">
<van-icon
<toy-icon
class-prefix="toy-icon"
name="guide"
color="#FFEB3B"
@@ -1,6 +1,6 @@
{
"component": true,
"usingComponents": {
"toy-button": "../../ui/button/button"
"toy-button": "../../toy/button-v2/button"
}
}
@@ -1,7 +1,7 @@
{
"component": true,
"usingComponents": {
"toy-button": "../../ui/button/button",
"toy-button": "../../toy/button-v2/button",
"van-action-sheet": "../../miniprogram_npm/@vant/weapp/action-sheet/index"
}
}
@@ -2,6 +2,6 @@
"component": true,
"styleIsolation": "apply-shared",
"usingComponents": {
"van-icon": "@vant/weapp/icon/index"
"toy-icon": "/toy/icon/icon"
}
}
@@ -11,7 +11,7 @@
bind:tap="onShapeTap" />
</view>
<view class="delete-icon" bind:tap="onDelete">
<van-icon
<toy-icon
class-prefix="toy-icon"
name="dustbin-o"
custom-class="delete-icon-inner"
@@ -3,6 +3,6 @@
"styleIsolation": "isolated",
"usingComponents": {
"van-popup": "@vant/weapp/popup/index",
"toy-button": "/ui/button/button"
"toy-button": "/toy/button-v2/button"
}
}
@@ -2,6 +2,6 @@
"component": true,
"styleIsolation": "apply-shared",
"usingComponents": {
"van-icon": "@vant/weapp/icon/index"
"toy-icon": "/toy/icon/icon"
}
}
@@ -8,7 +8,7 @@
<text class="word-content">{{word}}</text>
</view>
<view class="delete-icon" bindtap="onDelete">
<van-icon
<toy-icon
class-prefix="toy-icon"
name="dustbin-o"
custom-class="delete-icon-inner"
@@ -2,7 +2,7 @@
"component": true,
"styleIsolation": "apply-shared",
"usingComponents": {
"van-icon": "@vant/weapp/icon/index",
"toy-button": "/ui/button/button"
"toy-icon": "/toy/icon/icon",
"toy-button": "/toy/button-v2/button"
}
}
@@ -3,7 +3,7 @@
<view
class="word-input-container"
style="{{width ? 'width:' + width + ';' : ''}}">
<van-icon
<toy-icon
class-prefix="toy-icon"
name="translate"
custom-class="word-input-icon"
@@ -3,7 +3,7 @@
"styleIsolation": "isolated",
"usingComponents": {
"van-popup": "@vant/weapp/popup/index",
"toy-button": "/ui/button/button",
"toy-button": "/toy/button-v2/button",
"van-tab": "@vant/weapp/tab/index",
"van-tabs": "@vant/weapp/tabs/index"
}
@@ -1,3 +1,5 @@
{
"component": true
"component": true,
"styleIsolation": "apply-shared",
"addGlobalClass": true
}
+17 -1
View File
@@ -1,4 +1,5 @@
@import '../../style/theme3.less';
@import '../../style/theme.less';
@import '../../assets/fonts/iconfont.less';
.nav-bar {
position: fixed;
@@ -22,6 +23,21 @@
box-sizing: border-box;
}
.nav-bar__back {
width: 64rpx;
height: 64rpx;
display: flex;
align-items: center;
justify-content: flex-start;
flex-shrink: 0;
}
.nav-bar__back-icon {
font-size: 40rpx;
color: @color-nav-title;
line-height: 1;
}
.nav-bar__title {
font-size: @fs-nav-title;
font-weight: @fw-nav-title;
@@ -9,13 +9,25 @@ Component({
data: {
statusBarHeight: 0,
navBlockHeight: 0,
showBackButton: false,
},
lifetimes: {
attached() {
const { statusBarHeight } = wx.getWindowInfo();
const pages = getCurrentPages();
this.setData({
statusBarHeight,
navBlockHeight: statusBarHeight + NAV_INNER_PX,
showBackButton: pages.length > 1,
});
},
},
methods: {
handleBack() {
wx.navigateBack({
fail: () => {
wx.switchTab({ url: '/pages/home/home' });
},
});
},
},
@@ -1,5 +1,11 @@
<view class="nav-bar" style="padding-top: {{statusBarHeight}}px; {{background ? 'background:' + background + ';' : ''}}">
<view class="nav-bar__inner">
<view
wx:if="{{showBackButton}}"
class="nav-bar__back"
bindtap="handleBack">
<text class="toy-icon toy-icon-arrow-left nav-bar__back-icon"></text>
</view>
<slot name="left"></slot>
<text class="nav-bar__title">{{title}}</text>
<view class="nav-bar__right">
@@ -0,0 +1,8 @@
{
"component": true,
"usingComponents": {
"toy-button": "/toy/button/button"
},
"styleIsolation": "apply-shared",
"addGlobalClass": true
}
@@ -0,0 +1,44 @@
@import '../../style/theme.less';
.preview-footer-actions {
position: fixed;
left: 0;
bottom: 0;
width: 100%;
padding: 0 24rpx;
padding-bottom: calc(24rpx + env(safe-area-inset-bottom));
box-sizing: border-box;
z-index: 60;
&__bar {
max-width: 686rpx;
margin: 0 auto;
background: @bg-white;
border-radius: 48rpx;
padding: 20rpx;
box-shadow: 0 12rpx 40rpx rgba(0, 0, 0, 0.12);
border: 1rpx solid #f0e7d6;
display: flex;
gap: 16rpx;
}
&__secondary {
flex: 4;
}
&__primary {
flex: 6;
}
&__secondary .toy-btn,
&__primary .toy-btn {
width: 100%;
height: 88rpx;
border-radius: 28rpx;
font-size: 30rpx;
}
&__primary .toy-btn--primary {
box-shadow: 0 4rpx 12rpx rgba(255, 215, 0, 0.3);
}
}
@@ -0,0 +1,40 @@
/**
* 预览页底部悬浮操作条:左为次级操作(分享,toy-button type=secondary),右为主操作(保存等,primary)。
*/
Component({
options: {
styleIsolation: 'apply-shared',
addGlobalClass: true,
},
properties: {
primaryText: {
type: String,
value: '保存到作品库',
},
secondaryText: {
type: String,
value: '分享',
},
disabled: {
type: Boolean,
value: false,
},
loading: {
type: Boolean,
value: false,
},
shareOpenType: {
type: String,
value: 'share',
},
},
methods: {
onPrimaryTap() {
if (this.data.disabled || this.data.loading) return;
this.triggerEvent('primary');
},
onSecondaryTap() {
this.triggerEvent('secondary');
},
},
});
@@ -0,0 +1,23 @@
<view class="preview-footer-actions">
<view class="preview-footer-actions__bar">
<toy-button
class="preview-footer-actions__secondary"
type="secondary"
icon="line-share"
icon-size="40rpx"
open-type="{{ shareOpenType }}"
bind:click="onSecondaryTap">
{{ secondaryText }}
</toy-button>
<toy-button
class="preview-footer-actions__primary"
type="primary"
icon="download"
icon-size="40rpx"
disabled="{{ disabled }}"
loading="{{ loading }}"
bind:click="onPrimaryTap">
{{ primaryText }}
</toy-button>
</view>
</view>
@@ -8,6 +8,9 @@
"usingComponents": {
"nav-bar": "../../components3.0/nav-bar/nav-bar",
"share-guide-popup": "../../components/share-guide-popup/share-guide-popup",
"draw-ad": "../../components/draw-ad/draw-ad"
"draw-ad": "../../components/draw-ad/draw-ad",
"preview-footer-actions": "../../components3.0/preview-footer-actions/preview-footer-actions",
"toy-button": "../../toy/button/button",
"toy-icon": "../../toy/icon/icon"
}
}
@@ -1,6 +1,4 @@
@import '../../style/theme3.less';
@lt-share-green: #9cd343;
@import '../../style/theme.less';
page {
background-color: @bg-page;
@@ -208,98 +206,12 @@ page {
box-shadow: @shadow;
}
/* ===== 换一批 ===== */
/* ===== 换一批toy-button outlined 覆盖) ===== */
.lt-shuffle-btn {
width: 100%;
padding: 28rpx 24rpx;
margin: 0;
border-radius: @radius;
border: 6rpx solid @bg-header;
background: transparent;
font-size: 32rpx;
font-weight: 700;
color: @text-secondary;
display: flex;
align-items: center;
justify-content: center;
gap: 12rpx;
box-sizing: border-box;
&::after {
border: none;
}
}
.lt-shuffle-btn:active {
background: @bg-header;
transform: scale(0.98);
}
/* ===== 底栏 ===== */
.lt-bottom-bar {
position: fixed;
left: 0;
right: 0;
bottom: 0;
z-index: 100;
display: flex;
flex-direction: row;
align-items: center;
gap: 20rpx;
padding: 24rpx @page-padding-x;
padding-bottom: calc(24rpx + env(safe-area-inset-bottom));
background: rgba(255, 255, 255, 0.92);
backdrop-filter: blur(20px);
-webkit-backdrop-filter: blur(20px);
border-top: @border;
box-shadow: 0 -8rpx 32rpx rgba(50, 46, 37, 0.06);
box-sizing: border-box;
}
.lt-bottom-share,
.lt-bottom-save {
margin: 0;
height: 96rpx;
line-height: 96rpx;
border-radius: @radius-sm;
font-size: 30rpx;
font-weight: 700;
display: flex;
align-items: center;
justify-content: center;
gap: 8rpx;
padding: 0;
box-sizing: border-box;
&::after {
border: none;
}
}
.lt-bottom-share {
flex: 0 0 35%;
width: 35%;
background: @lt-share-green;
color: @text-white;
box-shadow: @shadow;
}
.lt-bottom-save {
flex: 1;
min-width: 0;
background: @brand;
color: @text-selected-btn;
font-weight: 800;
box-shadow: @shadow-lg;
}
.lt-bottom-save[disabled] {
opacity: 0.45;
}
.lt-bottom-icon {
font-size: 36rpx;
line-height: 1;
border-width: 6rpx !important;
border-color: @bg-header !important;
height: 96rpx !important;
font-size: 32rpx !important;
}
.lt-spacer-bottom {
@@ -66,33 +66,30 @@
</view>
</view>
<button
<toy-button
wx:if="{{showNextLetter}}"
hover-class="none"
class="lt-shuffle-btn"
bindtap="onNextLetter">
🔄 换一批
</button>
type="default"
variant="outlined"
block
icon="refresh"
custom-class="lt-shuffle-btn"
bind:click="onNextLetter">
换一批
</toy-button>
<draw-ad type="mathDraw"></draw-ad>
<view class="lt-spacer-bottom"></view>
</view>
</view>
<view class="lt-bottom-bar">
<button class="lt-bottom-share" hover-class="none" open-type="share">
<text class="lt-bottom-icon">↗</text>
分享
</button>
<button
class="lt-bottom-save"
hover-class="none"
<preview-footer-actions
primary-text="保存到相册"
primary-icon="download"
secondary-text="分享"
secondary-icon="wechat"
disabled="{{!hasContent}}"
bindtap="exportToPrint">
<text class="lt-bottom-icon">⬇</text>
保存到相册
</button>
</view>
bind:primary="exportToPrint"
bind:secondary="onShare" />
<share-guide-popup
show="{{showShareDialog}}"
@@ -5,7 +5,7 @@
"backgroundColor": "#F6F6F6",
"enablePullDownRefresh": false,
"usingComponents": {
"toy-button": "../../ui/button/button",
"toy-button": "../../toy/button-v2/button",
"share-guide-popup": "../../components/share-guide-popup/share-guide-popup",
"math-bottom-buttons": "../../components/math-bottom-buttons/math-bottom-buttons",
"draw-ad": "../../components/draw-ad/draw-ad"
@@ -5,7 +5,7 @@
"backgroundColor": "#F6F6F6",
"enablePullDownRefresh": false,
"usingComponents": {
"toy-button": "../../ui/button/button",
"toy-button": "../../toy/button-v2/button",
"share-guide-popup": "../../components/share-guide-popup/share-guide-popup",
"math-bottom-buttons": "../../components/math-bottom-buttons/math-bottom-buttons",
"draw-ad": "../../components/draw-ad/draw-ad"
@@ -5,7 +5,7 @@
"backgroundColor": "#F6F6F6",
"enablePullDownRefresh": false,
"usingComponents": {
"toy-button": "../../ui/button/button",
"toy-button": "../../toy/button-v2/button",
"share-guide-popup": "../../components/share-guide-popup/share-guide-popup",
"math-bottom-buttons": "../../components/math-bottom-buttons/math-bottom-buttons",
"draw-ad": "../../components/draw-ad/draw-ad"
@@ -5,7 +5,7 @@
"backgroundColor": "#F6F6F6",
"enablePullDownRefresh": false,
"usingComponents": {
"toy-button": "../../ui/button/button",
"toy-button": "../../toy/button-v2/button",
"share-guide-popup": "../../components/share-guide-popup/share-guide-popup",
"math-bottom-buttons": "../../components/math-bottom-buttons/math-bottom-buttons",
"draw-ad": "../../components/draw-ad/draw-ad"
@@ -5,7 +5,7 @@
"backgroundColor": "#F6F6F6",
"enablePullDownRefresh": false,
"usingComponents": {
"toy-button": "../../ui/button/button",
"toy-button": "../../toy/button-v2/button",
"share-guide-popup": "../../components/share-guide-popup/share-guide-popup",
"math-bottom-buttons": "../../components/math-bottom-buttons/math-bottom-buttons",
"draw-ad": "../../components/draw-ad/draw-ad"
@@ -5,7 +5,7 @@
"backgroundColor": "#F6F6F6",
"enablePullDownRefresh": false,
"usingComponents": {
"toy-button": "../../ui/button/button",
"toy-button": "../../toy/button-v2/button",
"share-guide-popup": "../../components/share-guide-popup/share-guide-popup",
"math-bottom-buttons": "../../components/math-bottom-buttons/math-bottom-buttons",
"draw-ad": "../../components/draw-ad/draw-ad"
@@ -5,7 +5,7 @@
"backgroundColor": "#F6F6F6",
"enablePullDownRefresh": false,
"usingComponents": {
"toy-button": "../../ui/button/button",
"toy-button": "../../toy/button-v2/button",
"share-guide-popup": "../../components/share-guide-popup/share-guide-popup",
"math-bottom-buttons": "../../components/math-bottom-buttons/math-bottom-buttons",
"draw-ad": "../../components/draw-ad/draw-ad"
+1 -1
View File
@@ -9,7 +9,7 @@
"van-icon": "@vant/weapp/icon/index",
"van-popup": "@vant/weapp/popup/index",
"van-dialog": "@vant/weapp/dialog/index",
"toy-button": "../../ui/button/button",
"toy-button": "../../toy/button-v2/button",
"shape-card": "../../components/shape-card/shape-card",
"shape-picker": "../../components/shape-picker/shape-picker",
"color-picker": "../../components/color-picker/color-picker",
@@ -5,7 +5,7 @@
"backgroundColor": "#F6F6F6",
"enablePullDownRefresh": false,
"usingComponents": {
"toy-button": "../../ui/button/button",
"toy-button": "../../toy/button-v2/button",
"share-guide-popup": "../../components/share-guide-popup/share-guide-popup",
"math-bottom-buttons": "../../components/math-bottom-buttons/math-bottom-buttons",
"draw-ad": "../../components/draw-ad/draw-ad"
+1 -1
View File
@@ -5,7 +5,7 @@
"backgroundColor": "#F6F6F6",
"enablePullDownRefresh": false,
"usingComponents": {
"toy-button": "../../ui/button/button",
"toy-button": "../../toy/button-v2/button",
"share-guide-popup": "../../components/share-guide-popup/share-guide-popup",
"math-type-selector": "../../components/math-type-selector/math-type-selector",
"math-bottom-buttons": "../../components/math-bottom-buttons/math-bottom-buttons",
@@ -5,7 +5,7 @@
"backgroundColor": "#F6F6F6",
"enablePullDownRefresh": false,
"usingComponents": {
"toy-button": "../../ui/button/button",
"toy-button": "../../toy/button-v2/button",
"share-guide-popup": "../../components/share-guide-popup/share-guide-popup",
"math-bottom-buttons": "../../components/math-bottom-buttons/math-bottom-buttons",
"math-type-selector": "../../components/math-type-selector/math-type-selector",
+1 -1
View File
@@ -5,7 +5,7 @@
"backgroundColor": "#F6F6F6",
"enablePullDownRefresh": false,
"usingComponents": {
"toy-button": "../../ui/button/button",
"toy-button": "../../toy/button-v2/button",
"share-guide-popup": "../../components/share-guide-popup/share-guide-popup",
"math-bottom-buttons": "../../components/math-bottom-buttons/math-bottom-buttons",
"draw-ad": "../../components/draw-ad/draw-ad"
@@ -5,7 +5,7 @@
"backgroundColor": "#F6F6F6",
"enablePullDownRefresh": false,
"usingComponents": {
"toy-button": "../../ui/button/button",
"toy-button": "../../toy/button-v2/button",
"share-guide-popup": "../../components/share-guide-popup/share-guide-popup",
"math-bottom-buttons": "../../components/math-bottom-buttons/math-bottom-buttons",
"draw-ad": "../../components/draw-ad/draw-ad"
+1 -1
View File
@@ -6,7 +6,7 @@
"enablePullDownRefresh": false,
"usingComponents": {
"share-guide-popup": "../../components/share-guide-popup/share-guide-popup",
"toy-button": "../../ui/button/button",
"toy-button": "../../toy/button-v2/button",
"math-bottom-buttons": "../../components/math-bottom-buttons/math-bottom-buttons",
"draw-ad": "../../components/draw-ad/draw-ad"
}
+1 -1
View File
@@ -5,7 +5,7 @@
"backgroundColor": "#F6F6F6",
"enablePullDownRefresh": false,
"usingComponents": {
"toy-button": "../../ui/button/button",
"toy-button": "../../toy/button-v2/button",
"share-guide-popup": "../../components/share-guide-popup/share-guide-popup",
"math-bottom-buttons": "../../components/math-bottom-buttons/math-bottom-buttons",
"draw-ad": "../../components/draw-ad/draw-ad"
@@ -5,7 +5,7 @@
"backgroundColor": "#F6F6F6",
"enablePullDownRefresh": false,
"usingComponents": {
"toy-button": "../../ui/button/button",
"toy-button": "../../toy/button-v2/button",
"share-guide-popup": "../../components/share-guide-popup/share-guide-popup",
"math-bottom-buttons": "../../components/math-bottom-buttons/math-bottom-buttons",
"draw-ad": "../../components/draw-ad/draw-ad"
@@ -5,7 +5,7 @@
"backgroundColor": "#F6F6F6",
"enablePullDownRefresh": false,
"usingComponents": {
"toy-button": "../../ui/button/button",
"toy-button": "../../toy/button-v2/button",
"share-guide-popup": "../../components/share-guide-popup/share-guide-popup",
"math-bottom-buttons": "../../components/math-bottom-buttons/math-bottom-buttons",
"draw-ad": "../../components/draw-ad/draw-ad"
@@ -5,7 +5,7 @@
"backgroundColor": "#F6F6F6",
"enablePullDownRefresh": false,
"usingComponents": {
"toy-button": "../../ui/button/button",
"toy-button": "../../toy/button-v2/button",
"share-guide-popup": "../../components/share-guide-popup/share-guide-popup",
"math-bottom-buttons": "../../components/math-bottom-buttons/math-bottom-buttons",
"draw-ad": "../../components/draw-ad/draw-ad",
@@ -5,7 +5,7 @@
"backgroundColor": "#F6F6F6",
"enablePullDownRefresh": false,
"usingComponents": {
"toy-button": "../../ui/button/button",
"toy-button": "../../toy/button-v2/button",
"share-guide-popup": "../../components/share-guide-popup/share-guide-popup",
"math-bottom-buttons": "../../components/math-bottom-buttons/math-bottom-buttons",
"draw-ad": "../../components/draw-ad/draw-ad"
@@ -5,7 +5,7 @@
"backgroundColor": "#F6F6F6",
"enablePullDownRefresh": false,
"usingComponents": {
"toy-button": "../../ui/button/button",
"toy-button": "../../toy/button-v2/button",
"share-guide-popup": "../../components/share-guide-popup/share-guide-popup",
"math-bottom-buttons": "../../components/math-bottom-buttons/math-bottom-buttons",
"draw-ad": "../../components/draw-ad/draw-ad"
+1 -1
View File
@@ -1,4 +1,4 @@
@import '../../style/theme3.less';
@import '../../style/theme.less';
@age-tab-gap: 32rpx;
@age-tab-w: 226rpx;
+1 -2
View File
@@ -6,8 +6,7 @@
"backgroundColor": "#F6F6F6",
"enablePullDownRefresh": false,
"usingComponents": {
"toy-button": "/ui/button/button",
"van-icon": "@vant/weapp/icon/index",
"toy-button": "/toy/button-v2/button",
"van-dialog": "@vant/weapp/dialog/index",
"word-input": "/components/word-input/word-input",
"word-card": "/components/word-card/word-card",
+1 -1
View File
@@ -12,7 +12,7 @@
height="80rpx"
width="410rpx"
icon="success"
icon-class-prefix="van-icon">
icon-class-prefix="toy-icon">
选择文字
</toy-button>
<toy-button
+1 -1
View File
@@ -1,4 +1,4 @@
@import '../../style/theme3.less';
@import '../../style/theme.less';
@fav-seg-track: #f0e7d6;
@fav-thumb-placeholder: #e5dcc9;
+1 -1
View File
@@ -1,4 +1,4 @@
@import '../../style/theme3.less';
@import '../../style/theme.less';
@home-track-gap: 16rpx;
+1 -1
View File
@@ -16,7 +16,7 @@
"introduction-popup": "../../components/introduction-popup/introduction-popup",
"share-guide-popup": "../../components/share-guide-popup/share-guide-popup",
"add-to-miniprogram-tip": "../../components/add-to-miniprogram-tip/add-to-miniprogram-tip",
"toy-button": "../../ui/button/button",
"toy-button": "../../toy/button-v2/button",
"word-picker": "../../components/word-picker/word-picker",
"empty-state": "../../components/empty-state/empty-state"
}
+1 -1
View File
@@ -1,4 +1,4 @@
@import '../../style/theme3.less';
@import '../../style/theme.less';
@profile-premium-bg: #2d2a26;
+66 -27
View File
@@ -1,40 +1,79 @@
// v3.0 品牌色系
@brand: #f7ee47;
@brand-dark: #e6d520;
@brand-light: #fffef0;
/**
* v3.0 主题令牌(Less 变量)
*
* 使用:在页面/组件 less 顶部 @import '../../style/theme.less',再用 @brand、@page-padding-x、@fs-nav-title 等
*
* 色谱速览:主题 #FFD709 · 页背景 #FEF6E7 · Header #F8F0E0
* · 主标题 #322E25 · 选中/强调字 #6C5A00
*/
// 主题色(保持向后兼容)
@primary-color: #798154;
@select-color: #f7ee47;
// ========== 品牌 / 主题色 ==========
@brand: #ffd709;
@brand-dark: #e5c200; // 按下、加深态
@brand-light: #fff4c4; // 浅底、高亮背景
// 与 theme.less 对齐的别名
@primary-color: @brand;
@select-color: @brand;
// ========== 页面结构背景 ==========
@bg-page: #fef6e7;
@bg-header: #f8f0e0;
@bg-default: @bg-page;
@bg-white: #ffffff;
@bg-gray: #f5edd8;
@bg-muted: #f5f5f5; // 分龄 Tab 未选中等弱对比底(Stitch)
@bg-week-head: #fafafa; // 分龄周卡头部(Stitch stone-50
// ========== 文本色 ==========
@text-title: #322e25;
@text-primary: @text-title;
@text-default: @text-title;
@text-selected-btn: #6c5a00; // 黄底按钮上的字、强调整示
@text-secondary: #605b50;
@text-light: rgba(50, 46, 37, 0.45);
@text-gray: #8a8478;
@text-white: #ffffff;
@text-disable: rgba(50, 46, 37, 0.35);
// ========== 链接与外链 ==========
@link-color: #2794f4;
@wechat-color: #1fe000;
// 文本颜色
@text-default: #333333;
@text-primary: #333333;
@text-secondary: #999999;
@text-light: #cccccc;
@text-gray: #9da0a3;
@text-white: #fff;
@text-disable: #ccc;
// ========== 边框 ==========
@border: 1rpx solid rgba(50, 46, 37, 0.08);
@border-color: rgba(255, 215, 9, 0.35);
// 边框
@border: 1rpx solid rgba(221, 221, 221, 0.5);
@border-color: #eeeeee;
// ========== 页面水平内边距 ==========
// 主内容区与屏幕左右对齐;卡片内略小,避免双重视觉边距
@page-padding-x: 40rpx;
@page-padding-inner-x: 26rpx;
// 背景色
@bg-default: #f5f5f5;
@bg-white: #ffffff;
@bg-gray: #fef6db;
// ========== 区块纵向间距 ==========
@section-gap: 60rpx; // 主区块之间(按年龄挑选 ↔ 热门推荐 等)
@section-gap-lg: 48rpx; // 主区块之间(按年龄挑选 ↔ 热门推荐 等)
@section-gap-sm: 32rpx; // 区块内:标题与下方列表 / 横向列表顶距
@section-gap-stack: 28rpx; // 同级卡片堆叠(周卡、热门列表卡片等)
@section-gap-xs: 24rpx; // 更小块间(搜索区顶、Tab 区等)
// 圆角
// ========== 圆角 ==========
@radius-sm: 16rpx;
@radius: 24rpx;
@radius-lg: 32rpx;
@radius-xl: 48rpx; // 大卡片(如个人中心主卡,对齐 Stitch 1.5rem
// 阴影
@shadow: 0 4rpx 16rpx rgba(0, 0, 0, 0.06);
@shadow-lg: 0 8rpx 32rpx rgba(0, 0, 0, 0.1);
// ========== 阴影 ==========
@shadow: 0 4rpx 16rpx rgba(50, 46, 37, 0.06);
@shadow-lg: 0 8rpx 32rpx rgba(50, 46, 37, 0.1);
// icon
// ========== 字号 ==========
@font-size-icon: 40rpx;
@fs-section-head-title: 36rpx; // 板块主标题(如首页区块头、分龄路径头等)
// ========== 顶栏(Tab 页固定导航标题)==========
@nav-inner-height: 44px;
@fs-nav-title: 34rpx;
@fw-nav-title: 700;
@color-nav-title: @text-title;
@ls-nav-title: -0.02em;
+40
View File
@@ -0,0 +1,40 @@
// v3.0 品牌色系
@brand: #f7ee47;
@brand-dark: #e6d520;
@brand-light: #fffef0;
// 主题色(保持向后兼容)
@primary-color: #798154;
@select-color: #f7ee47;
@link-color: #2794f4;
@wechat-color: #1fe000;
// 文本颜色
@text-default: #333333;
@text-primary: #333333;
@text-secondary: #999999;
@text-light: #cccccc;
@text-gray: #9da0a3;
@text-white: #fff;
@text-disable: #ccc;
// 边框
@border: 1rpx solid rgba(221, 221, 221, 0.5);
@border-color: #eeeeee;
// 背景色
@bg-default: #f5f5f5;
@bg-white: #ffffff;
@bg-gray: #fef6db;
// 圆角
@radius-sm: 16rpx;
@radius: 24rpx;
@radius-lg: 32rpx;
// 阴影
@shadow: 0 4rpx 16rpx rgba(0, 0, 0, 0.06);
@shadow-lg: 0 8rpx 32rpx rgba(0, 0, 0, 0.1);
// icon
@font-size-icon: 40rpx;
-79
View File
@@ -1,79 +0,0 @@
/**
* v3.0 主题令牌(Less 变量)
*
* 使用:在页面/组件 less 顶部 @import '../../style/theme3.less',再用 @brand、@page-padding-x、@fs-nav-title 等
*
* 色谱速览:主题 #FFD709 · 页背景 #FEF6E7 · Header #F8F0E0
* · 主标题 #322E25 · 选中/强调字 #6C5A00
*/
// ========== 品牌 / 主题色 ==========
@brand: #ffd709;
@brand-dark: #e5c200; // 按下、加深态
@brand-light: #fff4c4; // 浅底、高亮背景
// 与 theme.less 对齐的别名
@primary-color: @brand;
@select-color: @brand;
// ========== 页面结构背景 ==========
@bg-page: #fef6e7;
@bg-header: #f8f0e0;
@bg-default: @bg-page;
@bg-white: #ffffff;
@bg-gray: #f5edd8;
@bg-muted: #f5f5f5; // 分龄 Tab 未选中等弱对比底(Stitch)
@bg-week-head: #fafafa; // 分龄周卡头部(Stitch stone-50
// ========== 文本色 ==========
@text-title: #322e25;
@text-primary: @text-title;
@text-default: @text-title;
@text-selected-btn: #6c5a00; // 黄底按钮上的字、强调整示
@text-secondary: #605b50;
@text-light: rgba(50, 46, 37, 0.45);
@text-gray: #8a8478;
@text-white: #ffffff;
@text-disable: rgba(50, 46, 37, 0.35);
// ========== 链接与外链 ==========
@link-color: #2794f4;
@wechat-color: #1fe000;
// ========== 边框 ==========
@border: 1rpx solid rgba(50, 46, 37, 0.08);
@border-color: rgba(255, 215, 9, 0.35);
// ========== 页面水平内边距 ==========
// 主内容区与屏幕左右对齐;卡片内略小,避免双重视觉边距
@page-padding-x: 40rpx;
@page-padding-inner-x: 26rpx;
// ========== 区块纵向间距 ==========
@section-gap: 60rpx; // 主区块之间(按年龄挑选 ↔ 热门推荐 等)
@section-gap-lg: 48rpx; // 主区块之间(按年龄挑选 ↔ 热门推荐 等)
@section-gap-sm: 32rpx; // 区块内:标题与下方列表 / 横向列表顶距
@section-gap-stack: 28rpx; // 同级卡片堆叠(周卡、热门列表卡片等)
@section-gap-xs: 24rpx; // 更小块间(搜索区顶、Tab 区等)
// ========== 圆角 ==========
@radius-sm: 16rpx;
@radius: 24rpx;
@radius-lg: 32rpx;
@radius-xl: 48rpx; // 大卡片(如个人中心主卡,对齐 Stitch 1.5rem
// ========== 阴影 ==========
@shadow: 0 4rpx 16rpx rgba(50, 46, 37, 0.06);
@shadow-lg: 0 8rpx 32rpx rgba(50, 46, 37, 0.1);
// ========== 字号 ==========
@font-size-icon: 40rpx;
@fs-section-head-title: 36rpx; // 板块主标题(如首页区块头、分龄路径头等)
// ========== 顶栏(Tab 页固定导航标题)==========
@nav-inner-height: 44px;
@fs-nav-title: 34rpx;
@fw-nav-title: 700;
@color-nav-title: @text-title;
@ls-nav-title: -0.02em;
+1 -1
View File
@@ -5,7 +5,7 @@
"backgroundColor": "#F6F6F6",
"enablePullDownRefresh": false,
"usingComponents": {
"toy-button": "../../ui/button/button",
"toy-button": "../../toy/button-v2/button",
"van-radio": "@vant/weapp/radio/index",
"van-radio-group": "@vant/weapp/radio-group/index",
"van-cell": "@vant/weapp/cell/index",
@@ -1,6 +1,6 @@
# Toy Button 组件
基于涂鸦丫设计规范的按钮组件,与 doodle-ui 中的按钮样式保持一致
基于涂鸦丫设计规范的按钮组件,当前视觉参考 `stitch-ui/letterTracing/code.html` 中底部“分享 / 保存到相册”按钮,内部不依赖 Vant
## 功能特性
@@ -9,7 +9,7 @@
- ✅ 扁平化模式支持
- ✅ 图标支持
- ✅ 自定义尺寸
- ✅ 流畅的点击动效
- ✅ 参考设计稿的缩放点击动效
- ✅ 与 doodle-ui 样式完全一致
## 使用方法
@@ -89,16 +89,16 @@
### 按钮类型
- **primary**: 黄色背景 (#FFD719),深色文字 (#141414)
- **secondary**: 绿色背景 (#93D333),白色文字
- **green**: 绿色背景 (#93D333),白色文字(与secondary相同)
- **white**: 白背景,深色文字,边框
- **primary**: 黄色背景 (#FFD700),深色文字 (#6c5a00)
- **secondary**: 绿色背景 (#9CD343),白色文字
- **green**: 绿色背景 (#9CD343),白色文字(与secondary相同)
- **white**: 白背景,深色文字,边框
### 点击动效
- 所有按钮都支持 `scale(0.95)` 的点击缩放效果
- 扁平化模式下有背景色变化效果
- 动效持续时间为 150ms
- `flat` 模式下更接近底部操作栏按钮视觉
- 动效持续时间为 180ms
### 禁用状态
+8
View File
@@ -0,0 +1,8 @@
{
"component": true,
"usingComponents": {
"toy-icon": "/toy/icon/icon"
},
"styleIsolation": "apply-shared",
"addGlobalClass": true
}
+194
View File
@@ -0,0 +1,194 @@
@button-default-height: 92rpx;
@button-border-radius: 32rpx;
@button-default-font-size: 30rpx;
@button-default-color: #fff;
@button-transition: 0.18s ease;
@button-shadow-primary: 0 8rpx 20rpx rgba(255, 215, 0, 0.22);
@button-shadow-green: 0 8rpx 20rpx rgba(156, 211, 67, 0.22);
@button-shadow-white: 0 6rpx 16rpx rgba(50, 46, 37, 0.08);
@button-shadow-pressed: 0 4rpx 10rpx rgba(50, 46, 37, 0.1);
@button-disabled-bg: #ece6da;
@button-disabled-color: #b8b2a6;
wx-button:not([size=mini]) {
margin-left: auto;
margin-right: auto;
width: auto;
}
.toy-button {
--button-bg: #f0e7d6;
--button-color: @button-default-color;
--button-border-color: transparent;
--button-shadow: none;
box-sizing: border-box;
display: inline-flex;
align-items: center;
justify-content: center;
min-width: 180rpx;
height: @button-default-height;
line-height: 1;
padding: 0 28rpx;
border-radius: @button-border-radius;
border: 2rpx solid var(--button-border-color);
background: var(--button-bg);
color: var(--button-color);
box-shadow: var(--button-shadow);
font-size: @button-default-font-size;
font-weight: 700;
letter-spacing: 0.5rpx;
text-align: center;
cursor: pointer;
transition:
transform @button-transition,
box-shadow @button-transition,
background-color @button-transition,
border-color @button-transition,
color @button-transition,
opacity @button-transition;
&::after {
border: none;
}
&--default {
--button-bg: #f0e7d6;
--button-color: #322e25;
--button-border-color: #e5dcc9;
--button-shadow: @button-shadow-white;
}
&--large {
width: 100%;
}
&--small {
min-width: 136rpx;
height: 72rpx;
padding: 0 20rpx;
border-radius: 20rpx;
font-size: 26rpx;
}
&--mini {
min-width: 96rpx;
height: 56rpx;
padding: 0 16rpx;
border-radius: 18rpx;
font-size: 24rpx;
}
&--primary {
--button-bg: #ffd700;
--button-color: #6c5a00;
--button-shadow: @button-shadow-primary;
}
&--green,
&--secondary {
--button-bg: #9cd343;
--button-color: #ffffff;
--button-shadow: @button-shadow-green;
}
&--white {
--button-bg: rgba(255, 255, 255, 0.96);
--button-color: #322e25;
--button-border-color: #ebe3d2;
--button-shadow: @button-shadow-white;
}
&.plain.toy-button--white {
--button-bg: transparent;
--button-color: #ffffff;
--button-border-color: rgba(255, 255, 255, 0.92);
--button-shadow: none;
}
&.disabled {
--button-bg: @button-disabled-bg !important;
--button-color: @button-disabled-color !important;
--button-border-color: transparent !important;
--button-shadow: none !important;
opacity: 1;
}
&.loading {
opacity: 0.92;
}
&.active {
transform: scale(0.95);
box-shadow: @button-shadow-pressed;
}
&--flat {
min-width: 0;
padding: 0 24rpx;
border-width: 0;
border-radius: 32rpx;
&.toy-button--green,
&.toy-button--secondary {
--button-bg: #9cd343;
--button-color: #ffffff;
--button-shadow: 0 4rpx 12rpx rgba(50, 46, 37, 0.08);
font-weight: 700;
}
&.toy-button--primary {
--button-bg: #ffd700;
--button-color: #6c5a00;
--button-shadow: 0 8rpx 18rpx rgba(108, 90, 0, 0.12);
font-weight: 800;
}
&.toy-button--white {
--button-bg: rgba(255, 255, 255, 0.96);
--button-color: #322e25;
--button-shadow: 0 4rpx 12rpx rgba(50, 46, 37, 0.08);
}
&.toy-button--white.plain {
--button-bg: transparent;
--button-color: #ffffff;
--button-shadow: none;
border-width: 2rpx;
}
&.active {
transform: scale(0.95);
}
&.disabled {
--button-bg: #e6e0d4 !important;
--button-color: #b8b2a6 !important;
--button-shadow: none !important;
}
}
.loading-class,
&__icon {
margin-right: 12rpx;
flex-shrink: 0;
}
&__spinner {
box-sizing: border-box;
border-radius: 50%;
border-width: 4rpx;
border-style: solid;
animation: toy-button-spin 0.8s linear infinite;
}
}
@keyframes toy-button-spin {
0% {
transform: rotate(0deg);
}
100% {
transform: rotate(360deg);
}
}
+260
View File
@@ -0,0 +1,260 @@
Component({
options: {
styleIsolation: 'apply-shared',
addGlobalClass: true,
},
properties: {
type: {
type: String,
value: 'default',
},
openType: {
type: String,
value: '',
},
plain: {
type: Boolean,
value: false,
},
size: {
type: String,
value: 'normal',
},
disabled: {
type: Boolean,
value: false,
},
flat: {
type: Boolean,
value: false,
},
loading: {
type: Boolean,
value: false,
},
style: {
type: String,
value: '',
},
width: {
type: String,
value: '',
},
height: {
type: String,
value: '',
},
loadingType: {
type: String,
value: 'circular',
},
loadingSize: {
type: String,
value: '20px',
},
loadingColor: {
type: String,
value: '#ffffff',
},
icon: {
type: String,
value: '',
},
iconClassPrefix: {
type: String,
value: 'toy-icon',
},
iconSize: {
type: String,
value: '44rpx',
},
iconColor: {
type: String,
value: '#ffffff',
},
sessionFrom: {
type: String,
value: '',
},
sendMessageTitle: {
type: String,
value: '',
},
sendMessagePath: {
type: String,
value: '',
},
sendMessageImg: {
type: String,
value: '',
},
showMessageCard: {
type: Boolean,
value: false,
},
appParameter: {
type: String,
value: '',
},
ariaLabel: {
type: String,
value: '',
},
},
data: {
pressed: false,
rootClassName: '',
rootStyle: '',
resolvedIconColor: '#ffffff',
resolvedLoadingColor: '#ffffff',
resolvedIconSize: '44rpx',
resolvedLoadingSize: '20px',
},
observers: {
'type,plain,size,disabled,flat,loading,style,width,height,iconColor,iconSize,loadingColor,loadingSize,pressed':
function () {
this.syncViewState();
},
},
lifetimes: {
attached() {
this.syncViewState();
},
},
methods: {
syncViewState() {
const {
type,
plain,
size,
disabled,
flat,
loading,
style,
width,
height,
iconColor,
iconSize,
loadingColor,
loadingSize,
pressed,
} = this.data;
const classNames = ['toy-button', `toy-button--${type}`, `toy-button--${size}`];
if (flat) {
classNames.push('toy-button--flat');
}
if (plain) {
classNames.push('plain');
}
if (disabled) {
classNames.push('disabled');
}
if (loading) {
classNames.push('loading');
}
if (pressed && !disabled && !loading) {
classNames.push('active');
}
const styleSegments = [style];
const normalizedWidth = this.normalizeUnit(width);
const normalizedHeight = this.normalizeUnit(height);
if (normalizedWidth) {
styleSegments.push(`width:${normalizedWidth}`);
}
if (normalizedHeight) {
styleSegments.push(`height:${normalizedHeight}`);
}
const resolvedIconColor = this.getResolvedIconColor(
iconColor,
type,
plain,
disabled,
);
const resolvedLoadingColor = loadingColor || resolvedIconColor;
this.setData({
rootClassName: classNames.join(' '),
rootStyle: styleSegments.filter(Boolean).join(';'),
resolvedIconColor,
resolvedLoadingColor,
resolvedIconSize: this.normalizeUnit(iconSize) || '44rpx',
resolvedLoadingSize: this.normalizeUnit(loadingSize) || '20px',
});
},
normalizeUnit(value: string) {
if (!value) {
return '';
}
return /^\d+(\.\d+)?$/.test(value) ? `${value}rpx` : value;
},
getResolvedIconColor(
iconColor: string,
type: string,
plain: boolean,
disabled: boolean,
) {
if (disabled) {
return '#b8b2a6';
}
if (type === 'primary') {
return '#6c5a00';
}
if (type === 'white' && !plain) {
return '#322e25';
}
return iconColor || '#ffffff';
},
onTouchStart() {
if (this.data.disabled || this.data.loading) {
return;
}
this.setData({ pressed: true });
},
onTouchEnd() {
if (!this.data.pressed) {
return;
}
this.setData({ pressed: false });
},
onTouchCancel() {
if (!this.data.pressed) {
return;
}
this.setData({ pressed: false });
},
onClick(e: WechatMiniprogram.TouchEvent): void {
this.triggerEvent('click', e);
},
onGetUserInfo(e: WechatMiniprogram.CustomEvent) {
this.triggerEvent('getuserinfo', e.detail);
},
onContact(e: WechatMiniprogram.CustomEvent) {
this.triggerEvent('contact', e.detail);
},
onGetPhoneNumber(e: WechatMiniprogram.CustomEvent) {
this.triggerEvent('getphonenumber', e.detail);
},
onGetRealTimePhoneNumber(e: WechatMiniprogram.CustomEvent) {
this.triggerEvent('getrealtimephonenumber', e.detail);
},
onAgreePrivacyAuthorization(e: WechatMiniprogram.CustomEvent) {
this.triggerEvent('agreeprivacyauthorization', e.detail);
},
onError(e: WechatMiniprogram.CustomEvent) {
this.triggerEvent('error', e.detail);
},
onLaunchApp(e: WechatMiniprogram.CustomEvent) {
this.triggerEvent('launchapp', e.detail);
},
onOpenSetting(e: WechatMiniprogram.CustomEvent) {
this.triggerEvent('opensetting', e.detail);
},
onChooseAvatar(e: WechatMiniprogram.CustomEvent) {
this.triggerEvent('chooseavatar', e.detail);
},
},
externalClasses: ['custom-class'],
});
@@ -1,13 +1,7 @@
<wxs src="../wxs/base.wxs" module="Utils" />
<wxs module="wxs" src="./button.wxs"></wxs>
<!-- <view class="keyboard" bind:touchstart="{{wxs.keypadTouchStart}}" bind:touchend="{{wxs.keypadTouchEnd}}"> -->
<button
data-id="test-toy-button-id"
class="toy-button toy-button--{{type}} {{flat ? 'toy-button--flat' : ''}} {{Utils.classNames({disabled: disabled, loading: loading, plain: plain })}} custom-class"
data-disabled="{{disabled}}"
data-loading="{{loading}}"
style="{{style}};{{width ? 'width:' + width + ';' : ''}};{{height ? 'height:' + height + ';' : ''}}"
class="{{rootClassName}} custom-class"
style="{{rootStyle}}"
session-from="{{ sessionFrom }}"
open-type="{{ disabled || loading ? '' : openType }}"
send-message-title="{{ sendMessageTitle }}"
@@ -16,8 +10,9 @@
show-message-card="{{ showMessageCard }}"
app-parameter="{{ appParameter }}"
aria-label="{{ ariaLabel }}"
bind:touchstart="{{wxs.touchStart}}"
bind:touchend="{{wxs.touchEnd}}"
bindtouchstart="onTouchStart"
bindtouchend="onTouchEnd"
bindtouchcancel="onTouchCancel"
bind:tap="{{ disabled || loading ? '' : 'onClick'}}"
bindgetuserinfo="onGetUserInfo"
bindcontact="onContact"
@@ -29,20 +24,18 @@
bindopensetting="onOpenSetting"
bindchooseavatar="onChooseAvatar">
<block wx:if="{{ loading }}">
<van-loading
custom-class="loading-class"
size="{{ loadingSize || iconSize }}"
type="{{ loadingType }}"
color="{{ loadingColor }}" />
<view
class="loading-class toy-button__spinner toy-button__spinner--{{loadingType}}"
style="width:{{resolvedLoadingSize}};height:{{resolvedLoadingSize}};border-color:transparent;border-top-color:{{resolvedLoadingColor}};border-right-color:{{resolvedLoadingColor}};"></view>
</block>
<block wx:else>
<van-icon
<toy-icon
wx:if="{{ icon }}"
name="{{ icon }}"
size="{{ iconSize }}"
color="{{ wxs.getIconColor(iconColor, type, plain, disabled) }}"
size="{{ resolvedIconSize }}"
color="{{ resolvedIconColor }}"
class-prefix="{{ iconClassPrefix }}"
class="toy-button__icon" />
custom-class="toy-button__icon" />
</block>
<slot></slot>
</button>
+8
View File
@@ -0,0 +1,8 @@
{
"component": true,
"usingComponents": {
"toy-icon": "/toy/icon/icon"
},
"styleIsolation": "apply-shared",
"addGlobalClass": true
}
+317
View File
@@ -0,0 +1,317 @@
@import '../../style/theme.less';
/* ── design tokens(严格对齐 stitch-ui/buttonUi/code.html 色谱) ── */
/* primary 色系 */
@btn-primary-solid: #ffd709; /* primary-container / primary-fixed */
@btn-primary-gradient-from: #ffd709; /* primary-fixed */
@btn-primary-gradient-to: #efc900; /* primary-fixed-dim */
@btn-primary-text: #5b4b00; /* on-primary-container */
@btn-primary-pressed-bg: #5e4e00; /* primary-dim */
@btn-primary-pressed-text: #fff2cd; /* on-primary */
@btn-primary-ring: rgba(108, 90, 0, 0.10);
/* secondary 色系 */
@btn-secondary-bg: #9cd343; /* tertiary-container / tertiary-fixed */
@btn-secondary-text-on-tint: #5a8c1a;
/* error 色系 */
@btn-error-bg: #f95630; /* error-container */
@btn-error-text: #520c00; /* on-error-container */
/* surface 参考 */
@btn-surface-variant: #e5dcc9;
@btn-surface-container-high: #eae2d0;
@btn-surface-container-highest: #e5dcc9;
@btn-outline-variant-20: rgba(179, 172, 159, 0.20);
/* 阴影 */
@btn-shadow-primary: 0 8rpx 24rpx rgba(108, 90, 0, 0.15);
@btn-shadow-primary-lg: 0 8rpx 24rpx rgba(255, 215, 0, 0.3);
@btn-shadow-secondary: 0 8rpx 24rpx rgba(156, 211, 67, 0.18);
@btn-shadow-error: 0 8rpx 24rpx rgba(249, 86, 48, 0.18);
@btn-shadow-default: @shadow;
@btn-shadow-pressed: none;
/* 圆角 */
@btn-radius: @radius-lg;
@btn-radius-sm: 20rpx;
@btn-radius-mini: 18rpx;
/* 过渡 */
@btn-transition: 0.18s ease;
/* 禁用 */
@btn-disabled-bg: @bg-gray;
@btn-disabled-color: @text-disable;
/* ── reset native button chrome ── */
wx-button:not([size=mini]) {
margin-left: auto;
margin-right: auto;
width: auto;
}
/* ── base ── */
.toy-btn {
box-sizing: border-box;
display: inline-flex;
align-items: center;
justify-content: center;
gap: 10rpx;
min-width: 180rpx;
height: 92rpx;
padding: 0 32rpx;
border-radius: @btn-radius;
border: none;
background: @bg-gray;
color: @text-title;
box-shadow: @btn-shadow-default;
font-size: 30rpx;
font-weight: 700;
letter-spacing: 0.5rpx;
text-align: center;
line-height: 1;
transition:
transform @btn-transition,
box-shadow @btn-transition,
background @btn-transition,
color @btn-transition,
opacity @btn-transition;
&::after {
border: none;
}
/* ═══════ type variants ═══════ */
/* 默认:surface-container 底 + on-surface 字 */
&--default {
background: @bg-gray;
color: @text-title;
box-shadow: @btn-shadow-default;
}
/*
* 主色:中号/小号/mini 用纯色 primary-container (#ffd709)
* 大号单独覆盖为渐变(见 &--large 内)
*/
&--primary {
background: @btn-primary-solid;
color: @btn-primary-text;
box-shadow: @btn-shadow-primary;
}
/* 次级(语义名;视觉上目前为绿色) */
&--secondary {
background: @btn-secondary-bg;
color: @text-white;
box-shadow: @btn-shadow-secondary;
}
/* @deprecated 向后兼容,等同 secondary */
&--green {
background: @btn-secondary-bg;
color: @text-white;
box-shadow: @btn-shadow-secondary;
}
/* 危险操作:error-container 底 + on-error-container 深字 */
&--error {
background: @btn-error-bg;
color: @btn-error-text;
box-shadow: @btn-shadow-error;
}
/* 白色 / 浅底 */
&--white {
background: @bg-white;
color: @text-title;
box-shadow: @btn-shadow-default;
}
/* ═══════ size variants ═══════ */
/* 大号 / 全宽:primary 此时启用渐变 + 更强阴影 */
&--large {
width: 100%;
height: 100rpx;
padding: 0 40rpx;
font-size: 34rpx;
&.toy-btn--primary {
background: linear-gradient(135deg, @btn-primary-gradient-from, @btn-primary-gradient-to);
box-shadow: @btn-shadow-primary-lg;
}
}
&--normal {
min-width: 180rpx;
}
&--small {
min-width: 136rpx;
height: 72rpx;
padding: 0 24rpx;
border-radius: @btn-radius-sm;
font-size: 26rpx;
}
&--mini {
min-width: 96rpx;
height: 56rpx;
padding: 0 16rpx;
border-radius: @btn-radius-mini;
font-size: 24rpx;
}
/* ═══════ variant: outlined (描边) ═══════ */
&--outlined {
background: transparent !important;
box-shadow: none !important;
border: 4rpx solid @btn-surface-variant;
&.toy-btn--primary {
border-color: @btn-primary-solid;
color: @btn-primary-text;
}
&.toy-btn--secondary,
&.toy-btn--green {
border-color: @btn-secondary-bg;
color: @btn-secondary-text-on-tint;
}
&.toy-btn--error {
border-color: @btn-error-bg;
color: #b02500;
}
&.toy-btn--default {
border-color: @btn-surface-variant;
color: @text-secondary;
}
}
/* ═══════ variant: ghost (幽灵) ═══════ */
&--ghost {
background: transparent !important;
border: none !important;
box-shadow: none !important;
&.toy-btn--primary {
color: #6c5a00;
}
&.toy-btn--secondary,
&.toy-btn--green {
color: @btn-secondary-text-on-tint;
}
&.toy-btn--error {
color: #b02500;
}
&.toy-btn--default {
color: @text-secondary;
}
&.toy-btn--active {
background: @btn-surface-container-high !important;
}
}
/* ═══════ icon-only(纯图标按钮) ═══════ */
&--icon-only {
min-width: 0;
padding: 0;
gap: 0;
&.toy-btn--large {
width: 128rpx;
height: 128rpx;
border-radius: @btn-radius;
}
&.toy-btn--normal {
width: 112rpx;
height: 112rpx;
border-radius: @btn-radius;
}
&.toy-btn--small {
width: 88rpx;
height: 88rpx;
border-radius: @btn-radius-sm;
}
&.toy-btn--mini {
width: 72rpx;
height: 72rpx;
border-radius: @btn-radius-mini;
}
}
/* ═══════ block ═══════ */
&--block {
display: flex;
width: 100%;
}
/* ═══════ states ═══════ */
/* 通用按下态:缩放 0.95 */
&--active {
transform: scale(0.95);
box-shadow: @btn-shadow-pressed;
}
/* 图标按钮按下态:缩放 0.90 */
&--icon-only&--active {
transform: scale(0.90);
}
/* primary 按下态:切到 primary-dim 底色 + on-primary 字 + ring */
&--primary&--active {
background: @btn-primary-pressed-bg !important;
color: @btn-primary-pressed-text;
box-shadow: 0 0 0 8rpx @btn-primary-ring;
}
&--disabled {
background: @btn-disabled-bg !important;
color: @btn-disabled-color !important;
border-color: transparent !important;
box-shadow: none !important;
opacity: 1;
}
&--loading {
opacity: 0.88;
}
/* ═══════ inner elements ═══════ */
&__icon {
flex-shrink: 0;
}
&__label {
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
&__spinner {
box-sizing: border-box;
border-radius: 50%;
border: 4rpx solid transparent;
border-style: solid;
animation: toy-btn-spin 0.8s linear infinite;
flex-shrink: 0;
}
}
@keyframes toy-btn-spin {
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
}
+284
View File
@@ -0,0 +1,284 @@
Component({
options: {
styleIsolation: 'apply-shared',
addGlobalClass: true,
},
properties: {
type: {
type: String,
value: 'default',
},
size: {
type: String,
value: 'normal',
},
variant: {
type: String,
value: 'filled',
},
block: {
type: Boolean,
value: false,
},
disabled: {
type: Boolean,
value: false,
},
loading: {
type: Boolean,
value: false,
},
icon: {
type: String,
value: '',
},
iconOnly: {
type: Boolean,
value: false,
},
iconClassPrefix: {
type: String,
value: 'toy-icon',
},
iconSize: {
type: String,
value: '',
},
iconColor: {
type: String,
value: '',
},
openType: {
type: String,
value: '',
},
style: {
type: String,
value: '',
},
width: {
type: String,
value: '',
},
height: {
type: String,
value: '',
},
loadingSize: {
type: String,
value: '20px',
},
sessionFrom: {
type: String,
value: '',
},
sendMessageTitle: {
type: String,
value: '',
},
sendMessagePath: {
type: String,
value: '',
},
sendMessageImg: {
type: String,
value: '',
},
showMessageCard: {
type: Boolean,
value: false,
},
appParameter: {
type: String,
value: '',
},
ariaLabel: {
type: String,
value: '',
},
},
externalClasses: ['custom-class'],
data: {
pressed: false,
rootClassName: '',
rootStyle: '',
resolvedIconColor: '',
resolvedIconSize: '44rpx',
resolvedLoadingColor: '#ffffff',
},
observers: {
'type,size,variant,block,disabled,loading,iconOnly,style,width,height,iconColor,iconSize,pressed':
function () {
this.syncViewState();
},
},
lifetimes: {
attached() {
this.syncViewState();
},
},
methods: {
syncViewState() {
const {
type,
size,
variant,
block,
disabled,
loading,
iconOnly,
style,
width,
height,
iconColor,
iconSize,
pressed,
} = this.data;
const visualType = type === 'green' ? 'secondary' : type;
const cls = ['toy-btn'];
cls.push(`toy-btn--${visualType}`);
cls.push(`toy-btn--${size}`);
if (variant !== 'filled') {
cls.push(`toy-btn--${variant}`);
}
if (block) {
cls.push('toy-btn--block');
}
if (iconOnly) {
cls.push('toy-btn--icon-only');
}
if (disabled) {
cls.push('toy-btn--disabled');
}
if (loading) {
cls.push('toy-btn--loading');
}
if (pressed && !disabled && !loading) {
cls.push('toy-btn--active');
}
const styleParts = [style];
const w = this.normalizeUnit(width);
const h = this.normalizeUnit(height);
if (w) styleParts.push(`width:${w}`);
if (h) styleParts.push(`height:${h}`);
const resolvedIconColor = this.resolveIconColor(
iconColor,
visualType,
variant,
disabled,
);
const resolvedIconSize =
this.normalizeUnit(iconSize) ||
this.getDefaultIconSize(size, iconOnly);
this.setData({
rootClassName: cls.join(' '),
rootStyle: styleParts.filter(Boolean).join(';'),
resolvedIconColor,
resolvedIconSize,
resolvedLoadingColor: resolvedIconColor || '#ffffff',
});
},
normalizeUnit(value: string) {
if (!value) return '';
return /^\d+(\.\d+)?$/.test(value) ? `${value}rpx` : value;
},
getDefaultIconSize(size: string, iconOnly: boolean) {
if (iconOnly) {
const map: Record<string, string> = {
large: '56rpx',
normal: '48rpx',
small: '44rpx',
mini: '40rpx',
};
return map[size] || '48rpx';
}
const map: Record<string, string> = {
large: '44rpx',
normal: '40rpx',
small: '36rpx',
mini: '32rpx',
};
return map[size] || '40rpx';
},
resolveIconColor(
iconColor: string,
type: string,
variant: string,
disabled: boolean,
) {
if (disabled) return '#b8b2a6';
if (iconColor) return iconColor;
if (variant === 'outlined' || variant === 'ghost') {
const outlinedMap: Record<string, string> = {
primary: '#6c5a00',
secondary: '#5a8c1a',
error: '#b02500',
default: '#605b50',
};
return outlinedMap[type] || '#605b50';
}
const filledMap: Record<string, string> = {
primary: '#5b4b00',
error: '#520c00',
default: '#322e25',
white: '#322e25',
};
return filledMap[type] || '#ffffff';
},
onTouchStart() {
if (this.data.disabled || this.data.loading) return;
this.setData({ pressed: true });
},
onTouchEnd() {
if (!this.data.pressed) return;
this.setData({ pressed: false });
},
onTouchCancel() {
if (!this.data.pressed) return;
this.setData({ pressed: false });
},
onClick(e: WechatMiniprogram.TouchEvent) {
if (this.data.disabled || this.data.loading) return;
this.triggerEvent('click', e);
},
onGetUserInfo(e: WechatMiniprogram.CustomEvent) {
this.triggerEvent('getuserinfo', e.detail);
},
onContact(e: WechatMiniprogram.CustomEvent) {
this.triggerEvent('contact', e.detail);
},
onGetPhoneNumber(e: WechatMiniprogram.CustomEvent) {
this.triggerEvent('getphonenumber', e.detail);
},
onGetRealTimePhoneNumber(e: WechatMiniprogram.CustomEvent) {
this.triggerEvent('getrealtimephonenumber', e.detail);
},
onAgreePrivacyAuthorization(e: WechatMiniprogram.CustomEvent) {
this.triggerEvent('agreeprivacyauthorization', e.detail);
},
onError(e: WechatMiniprogram.CustomEvent) {
this.triggerEvent('error', e.detail);
},
onLaunchApp(e: WechatMiniprogram.CustomEvent) {
this.triggerEvent('launchapp', e.detail);
},
onOpenSetting(e: WechatMiniprogram.CustomEvent) {
this.triggerEvent('opensetting', e.detail);
},
onChooseAvatar(e: WechatMiniprogram.CustomEvent) {
this.triggerEvent('chooseavatar', e.detail);
},
},
});
+40
View File
@@ -0,0 +1,40 @@
<button
class="{{rootClassName}} custom-class"
style="{{rootStyle}}"
session-from="{{ sessionFrom }}"
open-type="{{ disabled || loading ? '' : openType }}"
send-message-title="{{ sendMessageTitle }}"
send-message-path="{{ sendMessagePath }}"
send-message-img="{{ sendMessageImg }}"
show-message-card="{{ showMessageCard }}"
app-parameter="{{ appParameter }}"
aria-label="{{ ariaLabel }}"
bindtouchstart="onTouchStart"
bindtouchend="onTouchEnd"
bindtouchcancel="onTouchCancel"
bind:tap="onClick"
bindgetuserinfo="onGetUserInfo"
bindcontact="onContact"
bindgetphonenumber="onGetPhoneNumber"
bindgetrealtimephonenumber="onGetRealTimePhoneNumber"
bindagreeprivacyauthorization="onAgreePrivacyAuthorization"
binderror="onError"
bindlaunchapp="onLaunchApp"
bindopensetting="onOpenSetting"
bindchooseavatar="onChooseAvatar">
<view wx:if="{{ loading }}" class="toy-btn__spinner"
style="width:{{loadingSize}};height:{{loadingSize}};border-top-color:{{resolvedLoadingColor}};border-right-color:{{resolvedLoadingColor}};"></view>
<toy-icon
wx:elif="{{ icon }}"
name="{{ icon }}"
size="{{ resolvedIconSize }}"
color="{{ resolvedIconColor }}"
class-prefix="{{ iconClassPrefix }}"
custom-class="toy-btn__icon" />
<view wx:if="{{ !iconOnly }}" class="toy-btn__label">
<slot></slot>
</view>
</button>
+6
View File
@@ -0,0 +1,6 @@
{
"usingComponents": {},
"component": true,
"styleIsolation": "apply-shared",
"addGlobalClass": true
}
+33
View File
@@ -0,0 +1,33 @@
@import '../../style/theme.less';
@import '../../assets/fonts/iconfont.less';
.toy-icon-root {
margin: 0;
padding: 0;
display: inline-flex;
align-items: center;
justify-content: center;
vertical-align: middle;
font-size: inherit;
color: inherit;
line-height: 1;
box-sizing: border-box;
&--image {
width: @font-size-icon;
height: @font-size-icon;
}
&__image {
width: 100%;
height: 100%;
display: block;
}
/* 内层 text 显示 iconfont 字形 */
> text {
display: inline-block;
line-height: 1;
font-style: normal;
}
}
+95
View File
@@ -0,0 +1,95 @@
Component({
options: {
styleIsolation: 'apply-shared',
addGlobalClass: true,
},
properties: {
name: {
type: String,
value: '',
},
color: {
type: String,
value: '',
},
size: {
type: String,
value: '',
},
classPrefix: {
type: String,
value: 'toy-icon',
},
style: {
type: String,
value: '',
},
},
externalClasses: ['custom-class', 'ext-class'],
data: {
isImage: false,
rootClassName: '',
rootStyle: '',
fontGlyphClass: '',
},
observers: {
'name,color,size,classPrefix,style': function () {
this.syncViewState();
},
},
lifetimes: {
attached() {
this.syncViewState();
},
},
methods: {
syncViewState() {
const { name, color, size, classPrefix, style } = this.data;
const isImage = this.isImageSource(name);
const normalizedSize = this.normalizeUnit(size);
const classNames = ['toy-icon-root'];
let fontGlyphClass = '';
if (isImage) {
classNames.push('toy-icon-root--image');
} else {
const parts: string[] = [];
if (classPrefix) {
parts.push(classPrefix);
}
if (name) {
parts.push(`${classPrefix}-${name}`);
}
fontGlyphClass = parts.join(' ');
}
const styleSegments = [style];
if (normalizedSize) {
styleSegments.push(`font-size:${normalizedSize}`);
if (isImage) {
styleSegments.push(`width:${normalizedSize}`);
styleSegments.push(`height:${normalizedSize}`);
}
}
if (color && !isImage) {
styleSegments.push(`color:${color}`);
}
this.setData({
isImage,
rootClassName: classNames.join(' '),
rootStyle: styleSegments.filter(Boolean).join(';'),
fontGlyphClass,
});
},
isImageSource(name: string) {
return /^(https?:\/\/|data:|\/|\.{1,2}\/)/.test(name);
},
normalizeUnit(value: string) {
if (!value) {
return '';
}
return /^\d+(\.\d+)?$/.test(value) ? `${value}rpx` : value;
},
},
});
+10
View File
@@ -0,0 +1,10 @@
<view
wx:if="{{isImage}}"
class="{{rootClassName}} custom-class ext-class"
style="{{rootStyle}}">
<image src="{{name}}" mode="aspectFit" class="toy-icon-root__image" />
</view>
<!-- 字体图标:内层 text 承载 iconfont::before),外层 view 负责布局与 externalClass -->
<view wx:else class="{{rootClassName}} custom-class ext-class" style="{{rootStyle}}">
<text class="{{fontGlyphClass}}"></text>
</view>
-7
View File
@@ -1,7 +0,0 @@
{
"component": true,
"usingComponents": {
"van-icon": "@vant/weapp/icon/index",
"van-loading": "@vant/weapp/loading/index"
}
}
-184
View File
@@ -1,184 +0,0 @@
@button-default-height: 92rpx;
@button-border-radius: 16rpx;
@button-default-font-size: 32rpx;
@button-default-color: #fff;
wx-button:not([size=mini]) {
margin-left: auto;
margin-right: auto;
width: auto;
}
.toy-button {
box-sizing: border-box;
display: inline-flex;
align-items: center;
justify-content: center;
height: @button-default-height;
line-height: @button-default-height;
border-radius: @button-border-radius;
font-size: @button-default-font-size;
color: @button-default-color;
padding: 0 24rpx;
font-weight: 600;
text-align: center;
cursor: pointer;
transition: all 0.15s ease-in-out;
border: none;
&--primary {
background: #FFD719;
box-shadow: 0 4rpx 12rpx rgba(255, 215, 25, 0.3);
color: #141414;
&.disabled {
background: #F5F5F5;
color: #CCCCCC;
box-shadow: none;
}
&.active {
transform: scale(0.92);
box-shadow: 0 2rpx 6rpx rgba(255, 215, 25, 0.2);
animation: buttonPress 0.15s ease-in-out;
}
}
&--green {
background: #93D333;
box-shadow: 0 4rpx 12rpx rgba(147, 211, 51, 0.3);
color: #FFFFFF;
&.disabled {
background: #F5F5F5;
color: #CCCCCC;
box-shadow: none;
}
&.active {
transform: scale(0.92);
box-shadow: 0 2rpx 6rpx rgba(147, 211, 51, 0.2);
animation: buttonPress 0.15s ease-in-out;
}
}
&--secondary {
background: #93D333;
box-shadow: 0 4rpx 12rpx rgba(147, 211, 51, 0.3);
color: #FFFFFF;
&.disabled {
background: #F5F5F5;
color: #CCCCCC;
box-shadow: none;
}
&.active {
transform: scale(0.92);
box-shadow: 0 2rpx 6rpx rgba(147, 211, 51, 0.2);
animation: buttonPress 0.15s ease-in-out;
}
}
&--white {
background: #FFFFFF;
border: 1rpx solid #E8E8E8;
color: #141414;
&.disabled {
background: #F5F5F5;
color: #CCCCCC;
border-color: #E8E8E8;
}
&.plain {
color: #FFFFFF;
background: transparent;
border: 1rpx solid #FFFFFF;
}
&.active {
transform: scale(0.92);
background: #F8F8F8;
animation: buttonPress 0.15s ease-in-out;
}
}
&--flat {
box-shadow: none !important;
border: none !important;
transition: all 0.15s ease-in-out;
// 覆盖所有颜色变体
&.toy-button--primary,
&.toy-button--green,
&.toy-button--secondary,
&.toy-button--white {
box-shadow: none !important;
border: none !important;
&.active {
transform: scale(0.92) !important;
animation: buttonPress 0.15s ease-in-out !important;
}
}
// 特殊处理白色plain模式
&.toy-button--white.plain {
background: transparent !important;
border: none !important;
box-shadow: none !important;
}
// 彩色按钮active背景变化
&.toy-button--primary.active {
background: rgba(255, 215, 25, 0.8);
}
&.toy-button--green.active {
background: rgba(147, 211, 51, 0.8);
}
&.toy-button--secondary.active {
background: rgba(147, 211, 51, 0.8);
}
&.toy-button--white.active {
background: rgba(255, 255, 255, 0.8);
}
}
.loading-class,
&__icon {
margin-right: 12rpx;
}
// 添加按钮点击动画类
&.button-press {
animation: buttonPress 0.15s ease-in-out;
}
}
// 按钮点击动画
@keyframes buttonPress {
0% {
transform: scale(1);
}
25% {
transform: scale(0.92);
}
50% {
transform: scale(0.98);
}
75% {
transform: scale(0.92);
}
100% {
transform: scale(1);
}
}
-108
View File
@@ -1,108 +0,0 @@
Component({
options: {
// multipleSlots: true, // 在组件定义时的选项中启用多slot支持
},
/**
* 组件的属性列表
*/
properties: {
type: {
type: String,
value: 'default', // primary, green, secondary, white
},
openType: {
type: String,
value: '',
},
plain: {
type: Boolean,
value: false, // 是否朴素按钮
},
size: {
type: String,
value: 'normal', // large, normal, small, mini
},
disabled: {
type: Boolean,
value: false, // 是否禁用
},
flat: {
type: Boolean,
value: false, // 是否扁平化
},
loading: {
type: Boolean,
value: false, // 是否加载中
},
style: {
type: String,
value: '', // 自定义样式
},
// className: {
// type: String,
// value: '', // 自定义类名
// },
width: {
type: String,
value: '', // 按钮宽度
},
height: {
type: String,
value: '',
},
loadingType: {
type: String,
value: 'circular',
},
loadingSize: {
type: String,
value: '20px',
},
loadingColor: {
type: String,
value: '#ffffff',
},
// icon
icon: {
type: String,
value: '',
},
iconClassPrefix: {
type: String,
value: 'toy-icon',
},
iconSize: {
type: String,
value: '44rpx',
},
iconColor: {
type: String,
value: '#ffffff',
},
},
/**
* 组件的初始数据
*/
data: {},
lifetimes: {
attached() {
const { type, plain, disabled } = this.data;
if (type === 'white' && !plain) {
const iconColor = disabled ? 'rgba(34, 36, 37, 0.5)' : 'rgba(34, 36, 37, 0.9)';
this.setData({
iconColor,
});
}
},
},
/**
* 组件的方法列表
*/
methods: {
onClick(e: WechatMiniprogram.TouchEvent): void {
this.triggerEvent('click', e);
},
},
externalClasses: ['custom-class'],
});
-33
View File
@@ -1,33 +0,0 @@
module.exports = {
touchStart: function (event, ownerInstance) {
var instance = event.instance;
var dataSet = instance.getDataset();
var disabled = dataSet.disabled;
var loading = dataSet.loading;
if (instance && !disabled && !loading) {
instance.addClass('active');
}
},
touchEnd: function (event, ownerInstance) {
var instance = event.instance;
var dataSet = instance.getDataset();
var disabled = dataSet.disabled;
var loading = dataSet.loading;
if (instance && !disabled && !loading) {
instance.removeClass('active');
}
},
getIconColor: function (iconColor, type, plain, disabled) {
if (type === 'primary') {
return disabled ? '#CCCCCC' : '#141414';
}
if (type === 'white' && !plain) {
return disabled ? 'rgba(34, 36, 37, 0.5)' : 'rgba(34, 36, 37, 0.9)';
}
return iconColor;
},
};
-4
View File
@@ -1,4 +0,0 @@
{
"usingComponents": {},
"component": true
}
-21
View File
@@ -1,21 +0,0 @@
@import '../../style/theme.less';
@import '../../assets/fonts/iconfont.less';
.icon {
margin: 0;
padding: 0;
display: inline-block;
vertical-align: middle;
font-size: inherit;
color: inherit;
&.icon_image {
width: @font-size-icon;
height: @font-size-icon;
}
&__image {
width: 100%;
height: 100%;
}
}
-35
View File
@@ -1,35 +0,0 @@
Component({
options: {
//@ts-ignore
styleIsolation: 'apply-shared',
},
properties: {
// icon name
name: {
type: String,
value: '',
optionalTypes: [String],
},
// icon color
color: {
type: String,
value: '',
optionalTypes: [String],
},
// icon size
size: {
type: String,
value: '',
optionalTypes: [String],
},
// icon classPrefix
classPrefix: {
type: String,
value: 'icon',
optionalTypes: [String],
},
},
externalClasses: ['ext-class'],
data: {},
methods: {},
});
-4
View File
@@ -1,4 +0,0 @@
<wxs src="../wxs/base.wxs" module="Utils" />
<view class="{{ Utils.classNames('icon iconfont ' + classPrefix, { icon_image: Utils.isUrl(name)}) }} {{ !Utils.isUrl(name) && classPrefix + '-' + name }} ext-class" style="{{size ? 'font-size: ' + size + 'rpx;' : ''}}{{color ? 'color: ' + color + ';' : ''}}{{Utils.isUrl(name) && size ? 'width: ' + size + ';height: ' + size + ';' : ''}}">
<image wx:if="{{ Utils.isUrl(name) }}" src="{{ name }}" mode='aspectFit' class="icon__image" />
</view>
+1 -1
View File
@@ -1,2 +1,2 @@
/** 与 style/theme3.less 中 @nav-inner-height 对齐(px */
/** 与 style/theme.less 中 @nav-inner-height 对齐(px */
export const NAV_INNER_PX = 44;
+3 -234
View File
@@ -24,242 +24,11 @@
"miniprogram": {
"list": [
{
"name": "pages/age/age",
"pathName": "pages/age/age",
"query": "",
"name": "englishPages/letterTracing/letterTracing",
"pathName": "englishPages/letterTracing/letterTracing",
"query": "id=letter-tracing-single",
"scene": null,
"launchMode": "default"
},
{
"name": "pages/copyBook/copyBook",
"pathName": "pages/copyBook/copyBook",
"query": "",
"launchMode": "default",
"scene": null
},
{
"name": "mathPages/multiplicationTable/multiplicationTable",
"pathName": "mathPages/multiplicationTable/multiplicationTable",
"query": "id=multiplication-table",
"launchMode": "default",
"scene": null
},
{
"name": "mathPages/calculationPractice/calculationPractice",
"pathName": "mathPages/calculationPractice/calculationPractice",
"query": "id=practice-addition",
"launchMode": "default",
"scene": null
},
{
"name": "mathPages/oneDigitAddition/oneDigitAddition",
"pathName": "mathPages/oneDigitAddition/oneDigitAddition",
"query": "id=one-digit-addition",
"launchMode": "default",
"scene": null
},
{
"name": "mathPages/borrowTen/borrowTen",
"pathName": "mathPages/borrowTen/borrowTen",
"query": "id=borrow-ten",
"launchMode": "default",
"scene": null
},
{
"name": "mathPages/flatTen/flatTen",
"pathName": "mathPages/flatTen/flatTen",
"query": "id=flat-ten",
"launchMode": "default",
"scene": null
},
{
"name": "mathPages/breakTen/breakTen",
"pathName": "mathPages/breakTen/breakTen",
"query": "id=break-ten",
"launchMode": "default",
"scene": null
},
{
"name": "mathPages/makeTen/makeTen",
"pathName": "mathPages/makeTen/makeTen",
"query": "id=make-ten",
"launchMode": "default",
"scene": null
},
{
"name": "focusPages/dotConnect/dotConnect",
"pathName": "focusPages/dotConnect/dotConnect",
"query": "id=dot-connect",
"launchMode": "default",
"scene": null
},
{
"name": "mathPages/numberObjectMatch/numberObjectMatch",
"pathName": "mathPages/numberObjectMatch/numberObjectMatch",
"query": "id=number-object-match",
"launchMode": "default",
"scene": null
},
{
"name": "focusPages/colorShapeMatch/colorShapeMatch",
"pathName": "focusPages/colorShapeMatch/colorShapeMatch",
"query": "id=color-shape-match",
"launchMode": "default",
"scene": null
},
{
"name": "mathPages/numberSort/numberSort",
"pathName": "mathPages/numberSort/numberSort",
"query": "id=number-sort",
"launchMode": "default",
"scene": null
},
{
"name": "focusPages/codeConnect/codeConnect",
"pathName": "focusPages/codeConnect/codeConnect",
"query": "id=code-connect",
"launchMode": "default",
"scene": null
},
{
"name": "focusPages/gridReasoning/gridReasoning",
"pathName": "focusPages/gridReasoning/gridReasoning",
"query": "id=grid-reasoning",
"launchMode": "default",
"scene": null
},
{
"name": "focusPages/lineRecognition/lineRecognition",
"pathName": "focusPages/lineRecognition/lineRecognition",
"query": "id=line-recognition",
"launchMode": "default",
"scene": null
},
{
"name": "focusPages/matchConnect/matchConnect",
"pathName": "focusPages/matchConnect/matchConnect",
"query": "id=match-connect",
"launchMode": "default",
"scene": null
},
{
"name": "focusPages/colorPattern/colorPattern",
"pathName": "focusPages/colorPattern/colorPattern",
"query": "id=color-pattern",
"launchMode": "default",
"scene": null
},
{
"name": "focusPages/shapeSymbol/shapeSymbol",
"pathName": "focusPages/shapeSymbol/shapeSymbol",
"query": "id=shape-symbol",
"launchMode": "default",
"scene": null
},
{
"name": "focusPages/positionColoring/positionColoring",
"pathName": "focusPages/positionColoring/positionColoring",
"query": "id=position-coloring",
"launchMode": "default",
"scene": null
},
{
"name": "pages/focusIndex/focusIndex",
"pathName": "pages/focusIndex/focusIndex",
"query": "",
"launchMode": "default",
"scene": null
},
{
"name": "focusPages/gridDrawing/gridDrawing",
"pathName": "focusPages/gridDrawing/gridDrawing",
"query": "id=grid-drawing",
"launchMode": "default",
"scene": null
},
{
"name": "focusPages/shape/shape",
"pathName": "focusPages/shape/shape",
"query": "",
"launchMode": "default",
"scene": null
},
{
"name": "pages/index/index",
"pathName": "pages/index/index",
"query": "",
"launchMode": "default",
"scene": null
},
{
"name": "mathPages/numberDecompose/numberDecompose",
"pathName": "mathPages/numberDecompose/numberDecompose",
"query": "id=number-decompose&mode=with-image",
"launchMode": "default",
"scene": null
},
{
"name": "mathPages/countingSelect/countingSelect",
"pathName": "mathPages/countingSelect/countingSelect",
"query": "id=counting-fill",
"launchMode": "default",
"scene": null
},
{
"name": "mathPages/compare/compare",
"pathName": "mathPages/compare/compare",
"query": "id=compare",
"launchMode": "default",
"scene": null
},
{
"name": "mathPages/missingNumber/missingNumber",
"pathName": "mathPages/missingNumber/missingNumber",
"query": "id=missing-number",
"launchMode": "default",
"scene": null
},
{
"name": "mathPages/addition/addition",
"pathName": "mathPages/addition/addition",
"query": "id=addition-5",
"launchMode": "default",
"scene": null
},
{
"name": "mathPages/countMatch/countMatch",
"pathName": "mathPages/countMatch/countMatch",
"query": "id=number-coloring",
"launchMode": "default",
"scene": null
},
{
"name": "pages/mathIndex/mathIndex",
"pathName": "pages/mathIndex/mathIndex",
"query": "",
"launchMode": "default",
"scene": null
},
{
"name": "mathPages/countMatch/countMatch",
"pathName": "mathPages/countMatch/countMatch",
"query": "id=counting-matching",
"launchMode": "default",
"scene": null
},
{
"name": "mathPages/numberFind/numberFind",
"pathName": "mathPages/numberFind/numberFind",
"query": "id=number-write",
"launchMode": "default",
"scene": null
},
{
"name": "pages/mathIndex/mathIndex",
"pathName": "pages/mathIndex/mathIndex",
"query": "",
"launchMode": "default",
"scene": null
}
]
}
+54
View File
@@ -0,0 +1,54 @@
# Design System Strategy: Doodle Mini
## 1. Overview & Creative North Star: "The Sun-Drenched Studio"
The creative direction for this design system is **"The Sun-Drenched Studio."** We are moving away from the cold, clinical "educational app" look and toward a space that feels like a physical, tactile craft room bathed in morning light.
Instead of a rigid, boxed-in grid, we embrace **Organic Asymmetry**. Elements should feel like they were placed on a table by hand, not snapped to a pixel-perfect wireframe. We break the "template" look by using exaggerated corner radii, overlapping "paper" layers, and a hierarchy that prioritizes warmth and play over traditional data density.
## 2. Colors: Tonal Radiance
The palette is anchored by `primary` (#6c5a00) and `primary_container` (#ffd709), creating a "Yaya Yellow" experience that is vibrant yet sophisticated.
* **The "No-Line" Rule:** Under no circumstances should 1px solid borders be used to separate content. We define boundaries exclusively through background shifts. Use `surface_container_low` (#f8f0e0) for main content areas and `surface` (#fef6e7) for the global backdrop.
* **Surface Hierarchy & Nesting:** Treat the UI as stacked sheets of heavy-weight construction paper.
* **Level 0:** `background` (#fef6e7) The desk surface.
* **Level 1:** `surface_container` (#f0e7d6) The worksheet base.
* **Level 2:** `surface_container_lowest` (#ffffff) Active drawing cards or "Paper" modals.
* **The Glass & Gradient Rule:** For navigation bars or floating tool palettes, use Glassmorphism. Apply `surface_container_low` at 80% opacity with a `20px` backdrop blur. This ensures the "sunny" background bleeds through, maintaining the "Sun-Drenched" atmosphere.
* **Signature Textures:** Main CTAs should not be flat. Apply a subtle linear gradient from `primary_fixed` (#ffd709) to `primary_fixed_dim` (#efc900) at a 145-degree angle to give buttons a "pillowy" 3D volume.
## 3. Typography: The Friendly Guide
We use **Plus Jakarta Sans** exclusively. Its geometric yet soft terminals provide a modern, legible, and approachable voice.
* **Display & Headlines:** Use `display-lg` (3.5rem) for "Doodle Mini" brand moments. Headlines (`headline-md`) should use a tighter letter-spacing (-0.02em) to feel "hugged" and friendly.
* **Body & Labels:** Use `body-lg` for all instructional text. In a children's context, readability is paramount; ensure a line height of at least 1.6x the font size.
* **Hierarchy as Personality:** Use `on_secondary_container` (#6d3b00) for sub-headers to provide a warm, "chocolatey" contrast against the yellow, moving away from harsh blacks.
## 4. Elevation & Depth: Tonal Layering
Depth in this system is a result of light and shadow, not lines and strokes.
* **The Layering Principle:** To lift a component, place a `surface_container_highest` (#e5dcc9) element inside a `surface_container_low` (#f8f0e0) environment. This creates a "recessed" or "elevated" feel through color value alone.
* **Ambient Shadows:** Use shadows sparingly. When an object "floats" (like a floating action button), use a shadow color derived from `on_surface` (#322e25) at 6% opacity, with a blur of `32px` and a Y-offset of `8px`. It should look like a soft glow, not a drop shadow.
* **The "Ghost Border" Fallback:** If a separator is required for accessibility in input fields, use `outline_variant` (#b3ac9f) at **15% opacity**. It should be felt, not seen.
## 5. Components: Tactile & Safe
Every component must feel "squishy" and safe to touch.
* **Buttons:**
* **Primary:** Uses `primary_container` (#ffd709) with a `full` (9999px) radius.
* **States:** On press, scale the button down to 96% size to simulate a physical "click" into the screen.
* **Cards & Lists:** Forbid divider lines. Separate list items using a `spacing-3` (1rem) gap and alternating background colors between `surface_container_low` and `surface_container`.
* **Input Fields:** Use `surface_container_highest` for the input track. Corners must be `xl` (3rem). Labels sit *above* the field in `title-sm` to ensure children can always see the context.
* **Selection Chips:** Use `secondary_container` (#ffc697) for selected states. The roundedness must be `full` to mimic smooth river stones.
* **The "Joy" Progress Bar:** A custom component for Doodle Mini. Use a thick track (`1.5rem` height) in `surface_container_highest` with the progress indicator using a gradient of `tertiary` (#006b1b) to `tertiary_fixed` (#91f78e).
## 6. Dos and Donts
### Do:
* **Do** use `xl` and `full` corner radii for almost everything. Sharp corners are "scary."
* **Do** lean into white space. Use `spacing-12` (4rem) between major sections to let the design breathe.
* **Do** use `on_tertiary_container` (dark green) for success states; it feels organic and "earthy" against the yellow.
### Dont:
* **Dont** use pure black (#000000). Use `on_surface` (#322e25) for all dark text to maintain the warm, sunny tone.
* **Dont** use 1px dividers. If you feel you need one, use a `surface_variant` (#e5dcc9) color block that is `4px` thick with `full` rounded caps.
* **Dont** crowd the interface. If a screen feels busy, increase the background "paper" size and use a scrollable layout instead of shrinking elements.
+306
View File
@@ -0,0 +1,306 @@
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="utf-8" />
<meta content="width=device-width, initial-scale=1.0" name="viewport" />
<title>按钮组件库 (Button Library) - 优化版</title>
<script src="https://cdn.tailwindcss.com?plugins=forms,container-queries"></script>
<link href="https://fonts.googleapis.com/css2?family=Plus+Jakarta+Sans:wght@400;500;600;700;800&amp;display=swap"
rel="stylesheet" />
<link
href="https://fonts.googleapis.com/css2?family=Material+Symbols+Outlined:wght,FILL@100..700,0..1&amp;display=swap"
rel="stylesheet" />
<script id="tailwind-config">
tailwind.config = {
darkMode: "class",
theme: {
extend: {
"colors": {
"surface-container-lowest": "#ffffff",
"tertiary-container": "#9cd343", // Updated to #9cd343
"secondary": "#8a4c00",
"surface-dim": "#dcd4c0",
"error-container": "#f95630",
"background": "#fef6e7",
"surface-container-high": "#eae2d0",
"on-surface": "#322e25",
"surface-tint": "#6c5a00",
"inverse-surface": "#110e06",
"tertiary": "#006b1b",
"on-tertiary-fixed-variant": "#00691a",
"on-tertiary-container": "#005e17",
"inverse-on-surface": "#a29c8f",
"on-secondary-container": "#6d3b00",
"secondary-container": "#ffc697",
"on-error-container": "#520c00",
"surface-container-low": "#f8f0e0",
"on-background": "#322e25",
"surface": "#fef6e7",
"primary-container": "#ffd709",
"secondary-fixed-dim": "#ffb470",
"primary-fixed": "#ffd709",
"tertiary-fixed": "#9cd343", // Updated
"on-primary": "#fff2cd",
"error": "#b02500",
"surface-bright": "#fef6e7",
"tertiary-dim": "#005d16",
"surface-variant": "#e5dcc9",
"outline-variant": "#b3ac9f",
"primary-dim": "#5e4e00",
"on-secondary": "#fff0e6",
"on-error": "#ffefec",
"on-secondary-fixed": "#512a00",
"on-secondary-fixed-variant": "#7a4300",
"primary": "#6c5a00",
"surface-container-highest": "#e5dcc9",
"on-primary-fixed": "#453900",
"tertiary-fixed-dim": "#83e881",
"on-primary-container": "#5b4b00",
"inverse-primary": "#ffd709",
"secondary-fixed": "#ffc697",
"on-surface-variant": "#605b50",
"on-tertiary": "#d1ffc8",
"on-tertiary-fixed": "#00480f",
"error-dim": "#b92902",
"on-primary-fixed-variant": "#665500",
"secondary-dim": "#794200",
"primary-fixed-dim": "#efc900",
"outline": "#7c766a",
"surface-container": "#f0e7d6"
},
"borderRadius": {
"DEFAULT": "1rem",
"lg": "1.5rem", // Changed from 2rem to reflect moderate rounding request
"xl": "2rem",
"2xl": "1.5rem", // Standard 24px-ish for buttons
"full": "9999px"
},
"fontFamily": {
"headline": ["Plus Jakarta Sans", "Noto Sans SC"],
"body": ["Plus Jakarta Sans", "Noto Sans SC"],
"label": ["Plus Jakarta Sans", "Noto Sans SC"]
}
},
},
}
</script>
<style>
body {
font-family: 'Plus Jakarta Sans', 'Noto Sans SC', sans-serif;
}
.material-symbols-outlined {
font-variation-settings: 'FILL' 0, 'wght' 400, 'GRAD' 0, 'opsz' 24;
}
.btn-press-simulate {
transform: scale(0.96);
}
</style>
<style>
body {
min-height: max(884px, 100dvh);
}
</style>
</head>
<body class="bg-background text-on-surface min-h-screen pb-48"> <!-- Increased padding for the double bottom bar -->
<!-- TopAppBar Navigation Shell -->
<header
class="sticky top-0 z-50 bg-[#f8f0e0]/80 backdrop-blur-xl shadow-[0_8px_32px_rgba(50,46,37,0.06)] rounded-b-2xl">
<div class="flex justify-between items-center w-full px-6 py-4 max-w-5xl mx-auto">
<div class="flex items-center gap-4">
<button
class="material-symbols-outlined text-[#6c5a00] active:scale-95 transition-transform duration-200">menu</button>
<h1 class="text-xl font-black text-[#6d3b00] tracking-[-0.02em]">按钮组件库 - 优化版</h1>
</div>
<button
class="material-symbols-outlined text-[#6c5a00] active:scale-95 transition-transform duration-200">print</button>
</div>
</header>
<main class="max-w-5xl mx-auto px-6 py-8">
<!-- Intro Hero Section -->
<section class="mb-12">
<div
class="bg-surface-container-low rounded-2xl p-8 flex flex-col md:flex-row items-center gap-8 border-4 border-white/50">
<div class="flex-1 text-center md:text-left">
<h2 class="text-3xl font-bold text-on-secondary-container mb-4 tracking-tight">阳光色彩调色盘</h2>
<p class="text-on-surface-variant text-base leading-relaxed">
触感舒适、灵动活泼,专为儿童和家长设计的按钮系统。抛弃尖锐棱角,拥抱温暖与游戏感。</p>
</div>
<div class="w-full md:w-64 h-40 bg-primary-container rounded-2xl shadow-inner overflow-hidden relative">
<div class="absolute inset-0 bg-gradient-to-br from-primary-fixed to-primary-fixed-dim opacity-50">
</div>
<div class="absolute inset-0 flex items-center justify-center">
<span class="material-symbols-outlined text-on-primary-container text-6xl"
style="font-variation-settings: 'FILL' 1;">smart_toy</span>
</div>
</div>
</div>
</section>
<!-- Bento Grid for Categories -->
<div class="grid grid-cols-1 md:grid-cols-2 gap-6">
<!-- Primary Buttons -->
<div class="bg-surface-container rounded-2xl p-8 flex flex-col gap-6">
<div class="flex items-center gap-3">
<span class="material-symbols-outlined text-primary"
style="font-variation-settings: 'FILL' 1;">star</span>
<h3 class="text-xl font-bold text-on-surface">主要按钮 (Primary)</h3>
</div>
<div class="space-y-6">
<div>
<p class="text-xs font-bold text-outline uppercase tracking-widest mb-3">大号 / 全宽</p>
<button
class="w-full bg-gradient-to-br from-primary-fixed to-primary-fixed-dim text-on-primary-container font-bold py-5 px-8 rounded-2xl shadow-[0_8px_24px_rgba(108,90,0,0.15)] active:scale-95 transition-all duration-200 text-lg">开始创作</button>
</div>
<div class="flex items-end gap-4">
<div class="flex-1">
<p class="text-xs font-bold text-outline uppercase tracking-widest mb-3">中号</p>
<button
class="w-full bg-primary-container text-on-primary-container font-bold py-3 px-8 rounded-2xl active:scale-95 transition-all duration-200">继续</button>
</div>
<div>
<p class="text-xs font-bold text-outline uppercase tracking-widest mb-3">小号</p>
<button
class="bg-primary-container text-on-primary-container font-bold py-2 px-6 rounded-2xl text-sm active:scale-95 transition-all duration-200">下一步</button>
</div>
</div>
</div>
</div>
<!-- Interaction States -->
<div class="bg-surface-container-highest rounded-2xl p-8 flex flex-col gap-6">
<div class="flex items-center gap-3">
<span class="material-symbols-outlined text-secondary"
style="font-variation-settings: 'FILL' 1;">ads_click</span>
<h3 class="text-xl font-bold text-on-surface">交互状态 (States)</h3>
</div>
<div class="grid grid-cols-2 gap-4 h-full items-center">
<div class="flex flex-col items-center gap-4">
<button
class="bg-primary-container text-on-primary-container font-bold py-3 px-8 rounded-2xl shadow-sm w-full">常规状态</button>
<span class="text-xs font-medium text-outline">静止态</span>
</div>
<div class="flex flex-col items-center gap-4">
<button
class="bg-primary-dim text-on-primary font-bold py-3 px-8 rounded-2xl scale-95 shadow-none ring-4 ring-primary/10 w-full">点击状态</button>
<span class="text-xs font-medium text-outline">激活态</span>
</div>
</div>
</div>
<!-- Functional & Secondary -->
<div class="bg-surface-container rounded-2xl p-8 flex flex-col gap-6">
<div class="flex items-center gap-3">
<span class="material-symbols-outlined text-tertiary"
style="font-variation-settings: 'FILL' 1;">category</span>
<h3 class="text-xl font-bold text-on-surface">功能与次要按钮</h3>
</div>
<div class="space-y-6">
<div class="flex gap-4">
<button
class="flex-1 bg-[#9cd343] text-white font-bold py-3 px-6 rounded-2xl flex items-center justify-center gap-2 active:scale-95 transition-all">
<span class="material-symbols-outlined text-xl">share</span> 分享
</button>
<button
class="flex-1 bg-error-container text-on-error-container font-bold py-3 px-6 rounded-2xl flex items-center justify-center gap-2 active:scale-95 transition-all">
<span class="material-symbols-outlined text-xl">delete</span> 删除
</button>
</div>
<div class="flex gap-4">
<button
class="flex-1 border-4 border-surface-variant text-on-surface-variant font-bold py-3 px-6 rounded-2xl active:scale-95 transition-all">描边样式</button>
<button
class="flex-1 text-primary font-bold py-3 px-6 rounded-2xl active:bg-surface-container-high transition-all">幽灵按钮</button>
</div>
</div>
</div>
<!-- Icon Buttons -->
<div class="bg-surface-container-low rounded-2xl p-8 flex flex-col gap-6 border-4 border-white/30">
<div class="flex items-center gap-3">
<span class="material-symbols-outlined text-on-secondary-container"
style="font-variation-settings: 'FILL' 1;">grid_view</span>
<h3 class="text-xl font-bold text-on-surface">图标变体</h3>
</div>
<div class="flex flex-wrap gap-6 items-center justify-center py-4">
<button
class="w-16 h-16 rounded-2xl bg-secondary-container text-on-secondary-container flex items-center justify-center shadow-lg active:scale-90 transition-all">
<span class="material-symbols-outlined text-3xl"
style="font-variation-settings: 'FILL' 1;">brush</span>
</button>
<button
class="w-14 h-14 rounded-2xl bg-surface-container-highest text-on-surface-variant flex items-center justify-center active:scale-90 transition-all">
<span class="material-symbols-outlined text-2xl">undo</span>
</button>
<button
class="w-14 h-14 rounded-2xl bg-primary-container text-on-primary-container flex items-center justify-center shadow-sm active:scale-90 transition-all">
<span class="material-symbols-outlined text-2xl"
style="font-variation-settings: 'FILL' 1;">palette</span>
</button>
<button
class="w-12 h-12 rounded-xl border-4 border-outline-variant/20 text-on-surface flex items-center justify-center active:scale-90 transition-all">
<span class="material-symbols-outlined text-xl">close</span>
</button>
</div>
<p class="text-center text-sm text-outline px-4 italic">所有图标按钮均采用温润的方圆形状,强调触控感。</p>
</div>
</div>
<!-- Full Layout Example Section -->
<section
class="mt-12 mb-8 bg-surface-container-lowest rounded-2xl p-12 shadow-[0_8px_32px_rgba(50,46,37,0.06)] border-4 border-surface-container">
<div class="max-w-md mx-auto text-center space-y-8">
<div
class="bg-surface-container-low w-24 h-24 rounded-2xl mx-auto flex items-center justify-center mb-4">
<span class="material-symbols-outlined text-5xl text-primary"
style="font-variation-settings: 'FILL' 1;">draw</span>
</div>
<h3 class="text-3xl font-extrabold text-on-surface">准备好开始了吗?</h3>
<p class="text-on-surface-variant">使用我们的“暖阳画室”工具包创造你的第一件杰作。</p>
<div class="flex flex-col gap-4">
<button
class="w-full bg-gradient-to-br from-primary-fixed to-primary-fixed-dim text-on-primary-container font-bold py-5 px-8 rounded-2xl shadow-lg active:scale-95 transition-all text-xl">立即出发!</button>
<button
class="w-full text-secondary font-bold py-3 px-8 rounded-2xl active:bg-surface-container-low transition-all">稍后再说</button>
</div>
</div>
</section>
</main>
<!-- Floating Action Bar (Requested Component) -->
<div class="fixed bottom-24 left-0 w-full px-4 z-[60]">
<div
class="max-w-md mx-auto bg-white rounded-3xl p-4 shadow-[0_12px_40px_rgba(0,0,0,0.12)] border border-surface-container flex gap-3">
<button
class="flex-[4] bg-[#9cd343] text-white font-bold py-4 rounded-2xl flex items-center justify-center gap-2 active:scale-95 transition-all">
<span class="material-symbols-outlined text-xl"
style="font-variation-settings: 'FILL' 1;">bubble_chart</span>
<span class="text-lg">分享</span>
</button>
<button
class="flex-[6] bg-[#FFD700] text-on-primary-container font-bold py-4 rounded-2xl flex items-center justify-center gap-2 shadow-[0_4px_12px_rgba(255,215,0,0.3)] active:scale-95 transition-all">
<span class="material-symbols-outlined text-xl">download</span>
<span class="text-lg">保存到作品库</span>
</button>
</div>
</div>
<!-- BottomNavBar -->
<nav
class="fixed bottom-0 left-0 w-full flex justify-around items-center px-4 pb-8 pt-4 bg-[#f8f0e0]/90 backdrop-blur-2xl z-50 rounded-t-3xl shadow-[0_-8px_32px_rgba(50,46,37,0.06)] border-t border-white/20">
<a class="flex flex-col items-center justify-center bg-[#ffd709] text-[#6d3b00] rounded-2xl px-6 py-2 transition-all duration-200"
href="#">
<span class="material-symbols-outlined" style="font-variation-settings: 'FILL' 1;">edit_note</span>
<span class="text-xs font-bold mt-1">画室</span>
</a>
<a class="flex flex-col items-center justify-center text-[#b3ac9f] px-6 py-2 hover:opacity-80 transition-opacity"
href="#">
<span class="material-symbols-outlined">folder_open</span>
<span class="text-xs font-medium mt-1">作品集</span>
</a>
<a class="flex flex-col items-center justify-center text-[#b3ac9f] px-6 py-2 hover:opacity-80 transition-opacity"
href="#">
<span class="material-symbols-outlined">settings</span>
<span class="text-xs font-medium mt-1">设置</span>
</a>
</nav>
</body>
</html>
Binary file not shown.

After

Width:  |  Height:  |  Size: 107 KiB

+3 -4
View File
@@ -117,9 +117,6 @@
</button>
<h1 class="font-bold tracking-tight text-[#6c5a00] dark:text-[#ffd709] text-xl">英语字母描红</h1>
</div>
<button class="text-[#6c5a00] dark:text-[#ffd709] hover:bg-[#f0e7d6] dark:hover:bg-[#4a453a] rounded-full p-2 transition-colors active:scale-95">
<span class="material-symbols-outlined">favorite</span>
</button>
</header>
<main class="pt-24 px-6 max-w-2xl mx-auto space-y-8">
<!-- A4 Preview Area -->
@@ -155,7 +152,9 @@
<div class="absolute bottom-4 right-4 bg-primary-container text-on-primary-container p-3 rounded-full shadow-md">
<span class="material-symbols-outlined">zoom_in</span>
</div>
</section>
<button class="absolute top-4 right-4 bg-white/90 backdrop-blur-sm text-red-500 p-2 rounded-full shadow-lg flex items-center justify-center transition-transform active:scale-95 z-10">
<span class="material-symbols-outlined" style="font-variation-settings: 'FILL' 1;">favorite</span>
</button></section>
<!-- Parameters Setting Panel -->
<section class="bg-surface-container rounded-xl p-6 space-y-6">
<div class="flex items-center gap-2">
Binary file not shown.

Before

Width:  |  Height:  |  Size: 123 KiB

After

Width:  |  Height:  |  Size: 124 KiB