feat: 字母描红页样式开发基本完成

This commit is contained in:
R524809
2026-04-09 17:43:22 +08:00
parent 653737c481
commit a911da6181
59 changed files with 1043 additions and 206 deletions
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+14
View File
@@ -5,6 +5,20 @@
"css_prefix_text": "icon-",
"description": "",
"glyphs": [
{
"icon_id": "47291112",
"name": "home-o",
"font_class": "home-o",
"unicode": "e621",
"unicode_decimal": 58913
},
{
"icon_id": "47291111",
"name": "home",
"font_class": "home",
"unicode": "e622",
"unicode_decimal": 58914
},
{
"icon_id": "47282109",
"name": "setting",
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
+1 -1
View File
@@ -1,6 +1,6 @@
import { PAPER_SIZE } from '../constants/colors';
import { downloadPrint } from '../utils/downloadPrint';
import { BaseDrawService } from '../service/baseDraw';
import { BaseDrawService } from '../core/draw/baseDraw';
import tracker from '../utils/tracker';
import { defaultShareConfig } from '../config/config';
@@ -32,6 +32,10 @@
flex-shrink: 0;
}
.nav-bar__back--hidden {
visibility: hidden;
}
.nav-bar__back-icon {
font-size: 40rpx;
color: @color-nav-title;
@@ -57,4 +61,4 @@
.nav-bar__placeholder {
width: 100%;
flex-shrink: 0;
}
}
+44 -9
View File
@@ -1,5 +1,13 @@
import { NAV_INNER_PX } from '../../utils/navMetrics';
const HOME_TAB_URL = '/pages/home/home';
const TAB_BAR_ROUTES = new Set([
'pages/home/home',
'pages/age/age',
'pages/favorites/favorites',
'pages/profile/profile',
]);
Component({
options: { multipleSlots: true },
properties: {
@@ -9,26 +17,53 @@ Component({
data: {
statusBarHeight: 0,
navBlockHeight: 0,
showBackButton: false,
leftActionType: 'none',
},
lifetimes: {
attached() {
const { statusBarHeight } = wx.getWindowInfo();
const pages = getCurrentPages();
this.setData({
statusBarHeight,
navBlockHeight: statusBarHeight + NAV_INNER_PX,
showBackButton: pages.length > 1,
});
this.syncLeftAction();
},
},
pageLifetimes: {
show() {
this.syncLeftAction();
},
},
methods: {
handleBack() {
wx.navigateBack({
fail: () => {
wx.switchTab({ url: '/pages/home/home' });
},
});
syncLeftAction() {
const pages = getCurrentPages();
const currentRoute = pages[pages.length - 1]?.route || '';
const isTabBarPage = TAB_BAR_ROUTES.has(currentRoute);
let leftActionType: 'none' | 'back' | 'home' = 'none';
if (pages.length > 1) {
leftActionType = 'back';
} else if (!isTabBarPage) {
leftActionType = 'home';
}
this.setData({ leftActionType });
},
handleLeftAction() {
if (this.data.leftActionType === 'back') {
wx.navigateBack({
fail: () => {
wx.switchTab({ url: HOME_TAB_URL });
},
});
return;
}
if (this.data.leftActionType === 'home') {
wx.switchTab({ url: HOME_TAB_URL });
}
},
},
});
+11 -5
View File
@@ -1,10 +1,16 @@
<view class="nav-bar" style="padding-top: {{statusBarHeight}}px; {{background ? 'background:' + background + ';' : ''}}">
<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>
class="nav-bar__back {{leftActionType === 'none' ? 'nav-bar__back--hidden' : ''}}"
bindtap="handleLeftAction">
<text
wx:if="{{leftActionType === 'back'}}"
class="toy-icon toy-icon-arrow-left nav-bar__back-icon"></text>
<text
wx:elif="{{leftActionType === 'home'}}"
class="toy-icon toy-icon-home-o nav-bar__back-icon"></text>
</view>
<slot name="left"></slot>
<text class="nav-bar__title">{{title}}</text>
@@ -6,7 +6,8 @@
bottom: 0;
width: 100%;
padding: 0 24rpx;
padding-bottom: calc(24rpx + env(safe-area-inset-bottom));
// padding-bottom: calc(24rpx + env(safe-area-inset-bottom));
padding-bottom: env(safe-area-inset-bottom);
box-sizing: border-box;
z-index: 60;
@@ -22,23 +23,27 @@
gap: 16rpx;
}
/* flex 子项:占宽比例;min-width:0 避免子项溢出时把 flex 算崩 */
&__secondary {
flex: 4;
flex: 3.92;
min-width: 0;
}
&__primary {
flex: 6;
flex: 6.18;
min-width: 0;
}
&__secondary .toy-btn,
&__primary .toy-btn {
width: 100%;
height: 88rpx;
/*
* 以下类通过 toy-button 的 custom-class 作用在内部原生 button 上。
* 不能写 &__primary .toy-btn:小程序样式无法穿透进子自定义组件。
*/
.preview-footer-actions__toy-btn {
border-radius: 28rpx;
font-size: 30rpx;
}
&__primary .toy-btn--primary {
.preview-footer-actions__toy-btn--primary {
box-shadow: 0 4rpx 12rpx rgba(255, 215, 0, 0.3);
}
}
@@ -9,12 +9,21 @@ Component({
properties: {
primaryText: {
type: String,
value: '保存到作品库',
value: '保存到相册',
},
primaryIcon: {
type: String,
value: 'download',
},
secondaryText: {
type: String,
value: '分享',
},
/** 须对应 iconfont 中 toy-icon-{name};历史上常用 wechat 已映射为 wechat-o */
secondaryIcon: {
type: String,
value: 'line-share',
},
disabled: {
type: Boolean,
value: false,
@@ -28,7 +37,31 @@ Component({
value: 'share',
},
},
data: {
/** 经归一化后的图标名,供 wxml 绑定(避免父级传 wechat 等 iconfont 中不存在的 name */
resolvedSecondaryIcon: 'line-share',
},
observers: {
secondaryIcon() {
this.syncSecondaryIconGlyph();
},
},
lifetimes: {
attached() {
this.syncSecondaryIconGlyph();
},
},
methods: {
syncSecondaryIconGlyph() {
const raw = (this.data.secondaryIcon ?? '').trim();
const aliases: Record<string, string> = {
wechat: 'wechat-o',
};
const name = aliases[raw] || raw;
this.setData({
resolvedSecondaryIcon: name || 'line-share',
});
},
onPrimaryTap() {
if (this.data.disabled || this.data.loading) return;
this.triggerEvent('primary');
@@ -1,23 +1,33 @@
<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 class="preview-footer-actions__secondary">
<toy-button
type="secondary"
icon="{{ resolvedSecondaryIcon }}"
icon-size="40rpx"
block
width="100%"
height="88rpx"
open-type="{{ shareOpenType }}"
custom-class="preview-footer-actions__toy-btn"
bind:click="onSecondaryTap">
{{ secondaryText }}
</toy-button>
</view>
<view class="preview-footer-actions__primary">
<toy-button
type="primary"
icon="{{ primaryIcon }}"
icon-size="40rpx"
block
width="100%"
height="88rpx"
disabled="{{ disabled }}"
loading="{{ loading }}"
custom-class="preview-footer-actions__toy-btn preview-footer-actions__toy-btn--primary"
bind:click="onPrimaryTap">
{{ primaryText }}
</toy-button>
</view>
</view>
</view>
@@ -1,4 +1,4 @@
import { PAPER_SIZE } from '../constants/colors';
import { PAPER_SIZE } from '../../constants/colors';
import { drawBaseHeader, drawBaseMiniHeader } from './baseHeaderDraw';
/**
@@ -1,4 +1,4 @@
import { getMiniCodeImage, getImage } from '../utils/index';
import { getMiniCodeImage, getImage } from '../../utils/index';
/**
* 3
@@ -10,7 +10,6 @@
"share-guide-popup": "../../components/share-guide-popup/share-guide-popup",
"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"
}
}
@@ -15,19 +15,20 @@ page {
padding-top: 24rpx;
display: flex;
flex-direction: column;
gap: @section-gap;
/* space-y-8 = 32px → 64rpx */
gap: 64rpx;
}
/* ===== 预览卡 ===== */
/* ===== 预览卡 (rounded-xl shadow-sm p-8 border) ===== */
.lt-preview-wrap {
position: relative;
}
.lt-preview-card {
background: @bg-white;
border-radius: @radius;
border-radius: @radius-xl;
box-shadow: @shadow;
padding: 48rpx @page-padding-inner-x 32rpx;
padding: 48rpx;
border: @border;
box-sizing: border-box;
display: flex;
@@ -38,12 +39,14 @@ page {
.lt-preview-kicker {
display: block;
text-align: center;
font-size: 22rpx;
font-weight: 700;
color: @text-gray;
/* text-xs = 12px → 24rpx */
font-size: @fs-section-head-title;
font-weight: bold;
color: @text-secondary;
// 设置字母间距,使文本字符之间有更宽的间隔,提升可读性和美观性
letter-spacing: 0.12em;
text-transform: uppercase;
margin-bottom: 24rpx;
margin-bottom: 40rpx;
}
.lt-canvas-wrap {
@@ -63,118 +66,170 @@ page {
box-shadow: @shadow;
}
/* ===== 参数面板 ===== */
/* ===== 参数面板 (bg-surface-container rounded-xl p-6 space-y-6) ===== */
.lt-params {
background: @bg-header;
border-radius: @radius;
padding: 40rpx @page-padding-inner-x;
/* surface-container = #f0e7d6 */
background: @bg-card;
border-radius: @radius-xl;
padding: 48rpx;
box-sizing: border-box;
display: flex;
flex-direction: column;
gap: 40rpx;
border: @border;
gap: 48rpx;
box-shadow: @shadow;
}
/* 标题行 (gap-2 = 8px → 16rpx) */
.lt-params-head {
display: flex;
flex-direction: row;
align-items: center;
gap: 12rpx;
gap: 16rpx;
}
.lt-params-emoji {
font-size: 36rpx;
/* text-xl = 20px → 40rpx */
font-size: 40rpx;
line-height: 1;
}
.lt-params-title {
/* text-lg = 18px → 36rpx */
font-size: @fs-section-head-title;
font-weight: 700;
color: @text-title;
letter-spacing: @ls-nav-title;
}
/* 参数区块内间距 (space-y-3 = 12px → 24rpx) */
.param-block {
display: flex;
flex-direction: column;
/* space-y-3 = 12px → 24rpx */
gap: 24rpx;
}
/* 字段标签 (text-sm font-bold text-on-surface-variant ml-1) */
.lt-field-label {
display: block;
font-size: 26rpx;
/* text-sm = 14px → 28rpx */
font-size: 28rpx;
font-weight: 700;
/* on-surface-variant = #605b50 */
color: @text-secondary;
margin-bottom: 16rpx;
margin-left: 4rpx;
/* ml-1 = 4px → 8rpx */
margin-left: 8rpx;
}
/* ===== 练习类型2×2 网格选择器 ===== */
/* ===== 练习类型 Tab (grid-cols-2 gap-2 bg-surface-container-high p-1.5 rounded-lg) ===== */
.lt-mode-seg {
display: grid;
grid-template-columns: 1fr 1fr;
/* gap-2 = 8px → 16rpx */
gap: 16rpx;
/* p-1.5 = 6px → 12rpx */
padding: 12rpx;
/* surface-container-high = #eae2d0 */
background: #eae2d0;
border-radius: 32rpx;
box-sizing: border-box;
}
/* 未选中 item (py-2.5 px-3 text-sm font-medium rounded-lg text-on-surface-variant) */
.lt-mode-item {
display: flex;
align-items: center;
justify-content: center;
padding: 24rpx 16rpx;
border-radius: @radius-sm;
background: @bg-white;
border: 2rpx solid rgba(50, 46, 37, 0.1);
box-shadow: 0 2rpx 8rpx rgba(50, 46, 37, 0.04);
transition: all 0.2s ease;
/* py-2.5 = 10px → 20rpx; px-3 = 12px → 24rpx */
padding: 20rpx 24rpx;
/* rounded-lg = 8px → 16rpx */
border-radius: 32rpx;
background: transparent;
transition: all 0.2s cubic-bezier(0.4, 0, 0.2, 1);
// transition: all 1s ease;
box-sizing: border-box;
}
/* 选中 item (bg-primary-container text-on-primary-container font-bold shadow-sm) */
.lt-mode-item--active {
/* primary-container = #ffd709 */
background: @brand;
border-color: rgba(255, 215, 9, 0.55);
box-shadow: @shadow;
box-shadow: 0 2rpx 8rpx rgba(50, 46, 37, 0.08);
}
.lt-mode-item__text {
font-size: 26rpx;
font-weight: 600;
color: @text-secondary;
line-height: 1.35;
/* text-sm = 14px → 28rpx */
font-size: 28rpx;
/* 未选中 font-medium = 500 */
font-weight: 500;
/* on-surface-variant = #605b50 */
color: @text-secondary; //#5b4b00
line-height: 40rpx;
text-align: center;
}
/* 选中态字色 (on-primary-container = #5b4b00, font-bold = 700) */
.lt-mode-item--active .lt-mode-item__text {
/* on-primary-container = #5b4b00 */
color: @text-selected-btn;
font-weight: 700;
}
/* ===== 字母选择网格7 列 ===== */
/* ===== 字母选择网格 (grid-cols-6 gap-2) ===== */
.lt-letter-grid {
display: grid;
grid-template-columns: repeat(7, 1fr);
gap: 12rpx;
/* 稿 grid-cols-6 */
grid-template-columns: repeat(6, 1fr);
/* gap-2 = 8px → 16rpx */
gap: 16rpx;
}
/* 字母格子 (aspect-square rounded-lg bg-surface-container-lowest) */
.lt-letter-cell {
aspect-ratio: 1;
display: flex;
align-items: center;
justify-content: center;
border-radius: @radius-sm;
border-radius: 32rpx;
/* surface-container-lowest = #ffffff */
background: @bg-white;
border: 2rpx solid rgba(50, 46, 37, 0.1);
box-shadow: 0 2rpx 8rpx rgba(50, 46, 37, 0.04);
transition: all 0.15s ease;
box-sizing: border-box;
/* 不用 :active:小程序 view 上背景色常在松手后才刷新;用 hover-class 才能按下即变色,并做过渡 */
transition:
background-color 0.2s ease,
transform 0.2s ease,
box-shadow 0.2s ease;
}
/* 按下态(hover-class):与选中态同色,松手后由 --active 保持 */
.lt-letter-cell--pressed {
transform: scale(0.95);
background: @brand;
box-shadow: 0 2rpx 8rpx rgba(50, 46, 37, 0.08);
}
/* 选中字母 (bg-[#FFD700] text-[#6c5a00] shadow-sm) */
.lt-letter-cell--active {
background: @brand;
border-color: rgba(255, 215, 9, 0.55);
box-shadow: @shadow;
transform: scale(1.05);
box-shadow: 0 2rpx 8rpx rgba(50, 46, 37, 0.08);
// box-shadow: @btn-shadow-pressed;
}
.lt-letter-text {
font-size: 32rpx;
/* text-lg = 18px → 36rpx */
font-size: 36rpx;
font-weight: 700;
/* on-surface-variant = #605b50 */
color: @text-secondary;
line-height: 1;
transition: color 0.2s ease;
}
.lt-letter-cell--active .lt-letter-text {
/* #6c5a00 */
color: @text-selected-btn;
}
.lt-letter-cell--pressed .lt-letter-text {
color: @text-selected-btn;
}
@@ -182,36 +237,90 @@ page {
.lt-chip-row {
display: flex;
flex-direction: row;
/* gap-2 = 8px → 16rpx */
gap: 16rpx;
}
/* 未选中 chip (同练习类型 item 风格) */
.lt-chip {
flex: 1;
text-align: center;
padding: 24rpx 20rpx;
/* py-2.5 px-3 */
padding: 20rpx 24rpx;
/* text-sm = 14px → 28rpx */
font-size: 28rpx;
font-weight: 600;
font-weight: 500;
line-height: 40rpx;
color: @text-secondary;
background: @bg-white;
border-radius: @radius-sm;
border: 2rpx solid rgba(50, 46, 37, 0.1);
box-shadow: 0 2rpx 8rpx rgba(50, 46, 37, 0.04);
background: #fff;
/* rounded-lg */
border-radius: 32rpx;
transition:
background-color 0.2s cubic-bezier(0.4, 0, 0.2, 1),
transform 0.2s cubic-bezier(0.4, 0, 0.2, 1),
color 0.2s cubic-bezier(0.4, 0, 0.2, 1),
box-shadow 0.2s cubic-bezier(0.4, 0, 0.2, 1);
}
/* 按下态(hover-class),未选与已选 chip 共用 */
.lt-chip--pressed {
transform: scale(0.96);
background: @brand;
color: @text-selected-btn;
font-weight: 700;
box-shadow: 0 2rpx 8rpx rgba(50, 46, 37, 0.08);
}
/* 选中 chip (同练习类型选中态) */
.lt-chip--active {
background: @brand;
color: @text-selected-btn;
font-weight: 700;
border-color: rgba(255, 215, 9, 0.55);
box-shadow: @shadow;
box-shadow: 0 2rpx 8rpx rgba(50, 46, 37, 0.08);
}
/* ===== 换一批(toy-button outlined 覆盖) ===== */
/* 换一批:自定义描边按钮(对齐 stitch 粗边框 + 图标文案横排,避免 toy-button 覆盖成本) */
.lt-shuffle-btn {
border-width: 6rpx !important;
border-color: @bg-header !important;
height: 96rpx !important;
font-size: 32rpx !important;
box-sizing: border-box;
width: 100%;
height: 108rpx;
padding: 0 32rpx;
border-radius: 32rpx;
border: 8rpx solid @bg-card;
background: transparent;
display: flex;
flex-direction: row;
align-items: center;
justify-content: center;
gap: 12rpx;
/* 与字母格/chip 一致:view 上勿依赖 :active 做背景或稳定缩放,用 hover-class */
transition:
transform 0.2s cubic-bezier(0.4, 0, 0.2, 1),
opacity 0.2s cubic-bezier(0.4, 0, 0.2, 1),
background-color 0.2s cubic-bezier(0.4, 0, 0.2, 1);
}
/* 稿 active:scale-[0.98],按下时略铺底便于看出过渡 */
.lt-shuffle-btn--hover {
transform: scale(0.98);
opacity: 0.92;
background-color: rgba(234, 226, 208, 0.55);
}
/* toy-icon 自定义组件宿主节点默认 display:inline,须强制为 inline-flex 方可参与 flex 对齐 */
.lt-shuffle-btn toy-icon {
display: inline-flex;
align-items: center;
justify-content: center;
flex-shrink: 0;
}
.lt-shuffle-btn__text {
font-size: 36rpx;
font-weight: 700;
line-height: 1;
color: @text-secondary;
transition: color 0.2s cubic-bezier(0.4, 0, 0.2, 1);
}
.lt-spacer-bottom {
@@ -1,4 +1,4 @@
import LetterTracingDraw from '../shared/service/letterTracingDraw';
import LetterTracingDraw from '../shared/draw/letterTracingDraw';
import { createPage, type CanvasDataState } from '../../base/pageMixin';
import { defaultShareConfig } from '../../config/config';
import { getWorksheetById } from '../../core/data/worksheets';
@@ -81,36 +81,15 @@ createPage(
options.id && PAGE_META[options.id]
? options.id
: 'letter-tracing-single';
const def = getWorksheetById(worksheetId);
const base = (def?.generatorConfig ??
{}) as Partial<LetterTracingGeneratorConfig>;
const merged = mergeLetterTracingConfig(base, {});
const traceMode = merged.mode;
const showLetterPicker = traceMode === 'single-letter';
const showCaseToggle =
traceMode === 'full-alphabet' || traceMode === 'two-column';
const showNextLetter = traceMode === 'single-letter';
this.setData({
worksheetId,
functionId: worksheetId,
traceMode,
selectedLetter: normalizePick(merged.selectedLetter),
letterCaseLower: merged.letterCase === 'lower',
showLetterPicker,
showCaseToggle,
showNextLetter,
});
this.initPageInfo(worksheetId, PAGE_META[worksheetId]?.title ?? '字母描红');
this.applyWorksheet(worksheetId);
},
onSelectMode(e: WechatMiniprogram.TouchEvent) {
const id = e.currentTarget.dataset.id as string | undefined;
if (!id || id === this.data.worksheetId) return;
wx.redirectTo({
url: `/englishPages/letterTracing/letterTracing?id=${id}`,
this.applyWorksheet(id, {
preserveLetter: true,
redraw: true,
});
},
@@ -184,6 +163,61 @@ createPage(
this.setData({ selectedLetter: next });
this.drawCanvas();
},
applyWorksheet(
worksheetId: string,
options?: {
preserveLetter?: boolean;
redraw?: boolean;
},
) {
const def = getWorksheetById(worksheetId);
const base = (def?.generatorConfig ??
{}) as Partial<LetterTracingGeneratorConfig>;
const merged = mergeLetterTracingConfig(base, {});
const traceMode = merged.mode;
const showLetterPicker = traceMode === 'single-letter';
const showCaseToggle =
traceMode === 'full-alphabet' || traceMode === 'two-column';
const showNextLetter = traceMode === 'single-letter';
const selectedLetter = options?.preserveLetter
? this.data.selectedLetter
: normalizePick(merged.selectedLetter);
const letterCaseLower =
traceMode === 'single-letter'
? merged.letterCase === 'lower'
: this.data.letterCaseLower;
this.setData(
{
worksheetId,
functionId: worksheetId,
traceMode,
selectedLetter,
letterCaseLower,
showLetterPicker,
showCaseToggle,
showNextLetter,
},
() => {
this.initPageInfo(
worksheetId,
PAGE_META[worksheetId]?.title ?? '字母描红',
);
if (this.drawService) {
this.drawService.options.title = this.data.pageTitle;
this.drawService.options.subTitle =
this.data.subTitle || '';
}
if (options?.redraw) {
this.drawCanvas();
}
},
);
},
},
{
shareConfig: defaultShareConfig,
@@ -43,6 +43,9 @@
wx:key="*this"
class="lt-letter-cell {{selectedLetter === item ? 'lt-letter-cell--active' : ''}}"
data-letter="{{item}}"
hover-class="lt-letter-cell--pressed"
hover-start-time="0"
hover-stay-time="70"
bind:tap="onSelectLetter">
<text class="lt-letter-text">{{item}}</text>
</view>
@@ -54,11 +57,17 @@
<view class="lt-chip-row">
<view
class="lt-chip {{!letterCaseLower ? 'lt-chip--active' : ''}}"
hover-class="lt-chip--pressed"
hover-start-time="0"
hover-stay-time="70"
bind:tap="onSelectCaseUpper">
大写 A–Z
</view>
<view
class="lt-chip {{letterCaseLower ? 'lt-chip--active' : ''}}"
hover-class="lt-chip--pressed"
hover-start-time="0"
hover-stay-time="70"
bind:tap="onSelectCaseLower">
小写 a–z
</view>
@@ -66,27 +75,25 @@
</view>
</view>
<toy-button
<view
wx:if="{{showNextLetter}}"
type="default"
variant="outlined"
block
icon="refresh"
custom-class="lt-shuffle-btn"
bind:click="onNextLetter">
换一批
</toy-button>
class="lt-shuffle-btn"
hover-class="lt-shuffle-btn--hover"
hover-start-time="0"
hover-stay-time="70"
bindtap="onNextLetter">
<toy-icon
name="refresh"
size="40rpx"
color="#605b50"
custom-class="lt-shuffle-btn__icon" />
<text class="lt-shuffle-btn__text">换一批</text>
</view>
<draw-ad type="mathDraw"></draw-ad>
<view class="lt-spacer-bottom"></view>
</view>
</view>
<preview-footer-actions
primary-text="保存到相册"
primary-icon="download"
secondary-text="分享"
secondary-icon="wechat"
disabled="{{!hasContent}}"
bind:primary="exportToPrint"
bind:secondary="onShare" />
@@ -1,4 +1,4 @@
import { BaseDrawService } from '../../../service/baseDraw';
import { BaseDrawService } from '../../../core/draw/baseDraw';
import type {
LetterTracingData,
TracingRow,
@@ -169,7 +169,13 @@ export default class LetterTracingDraw extends BaseDrawService {
const rowH = Math.max(36, (rest - 8) / rows.length);
const rowWidth = cw - M * 2;
for (let r = 0; r < rows.length; r++) {
this.drawTracingRow(rows[r], M, y + r * (rowH + 10), rowH, rowWidth);
this.drawTracingRow(
rows[r],
M,
y + r * (rowH + 10),
rowH,
rowWidth,
);
}
}
@@ -212,7 +218,15 @@ export default class LetterTracingDraw extends BaseDrawService {
if (idx >= chars.length) break;
const cx = M + c * (cellW + 4);
this.drawFourLines(cx, y, cellW, cellH);
this.drawCharInCell(chars[idx], cx, y, cellW, cellH, 0.85, true);
this.drawCharInCell(
chars[idx],
cx,
y,
cellW,
cellH,
0.85,
true,
);
}
y += cellH + 6;
}
@@ -2,7 +2,7 @@
* 译码连线绘制服务
*/
import { BaseDrawService } from '../../../service/baseDraw';
import { BaseDrawService } from '../../../core/draw/baseDraw';
/**
* 译码连线数据
@@ -2,7 +2,7 @@
* 颜色找规律绘制服务
*/
import { BaseDrawService } from '../../../service/baseDraw';
import { BaseDrawService } from '../../../core/draw/baseDraw';
import {
drawShapeSymbolShape,
DrawShapeOptions,
@@ -2,7 +2,7 @@
* 根据颜色画图形绘制服务
*/
import { BaseDrawService } from '../../../service/baseDraw';
import { BaseDrawService } from '../../../core/draw/baseDraw';
import { drawShapeSymbolShape } from '../shapes/shapeSymbolShapes';
/**
@@ -2,7 +2,7 @@
* 数字点连线绘制服务
*/
import { BaseDrawService } from '../../../service/baseDraw';
import { BaseDrawService } from '../../../core/draw/baseDraw';
/**
* 数字点连线数据
@@ -3,7 +3,7 @@
* 合并了基础绘制和内容绘制逻辑
*/
import { BaseDrawService } from '../../../service/baseDraw';
import { BaseDrawService } from '../../../core/draw/baseDraw';
import { GridCell, GridConfig } from '../types/gridTypes';
/**
@@ -2,7 +2,7 @@
* 方格推理绘制服务
*/
import { BaseDrawService } from '../../../service/baseDraw';
import { BaseDrawService } from '../../../core/draw/baseDraw';
/**
* 3x3网格中的位置坐标
@@ -2,7 +2,7 @@
* 线条识别绘制服务
*/
import { BaseDrawService } from '../../../service/baseDraw';
import { BaseDrawService } from '../../../core/draw/baseDraw';
import { getImage } from '../../../utils/index';
/**
@@ -2,7 +2,7 @@
* 连连看绘制服务
*/
import { BaseDrawService } from '../../../service/baseDraw';
import { BaseDrawService } from '../../../core/draw/baseDraw';
import { getImage } from '../../../utils/index';
/**
@@ -2,7 +2,7 @@
* 方位涂涂乐绘制服务
*/
import { BaseDrawService } from '../../../service/baseDraw';
import { BaseDrawService } from '../../../core/draw/baseDraw';
import { getImage } from '../../../utils/index';
/**
@@ -2,7 +2,7 @@
* 图形符号配对绘制服务
*/
import { BaseDrawService } from '../../../service/baseDraw';
import { BaseDrawService } from '../../../core/draw/baseDraw';
import { drawShapeSymbolShape } from '../shapes/shapeSymbolShapes';
/**
@@ -2,7 +2,7 @@
* 加减法计算绘制服务
*/
import { BaseDrawService } from '../../../service/baseDraw';
import { BaseDrawService } from '../../../core/draw/baseDraw';
import { getImage } from '../../../utils/index';
/**
@@ -2,7 +2,7 @@
* 破十法练习绘制服务
*/
import { BaseDrawService } from '../../../service/baseDraw';
import { BaseDrawService } from '../../../core/draw/baseDraw';
/**
* 破十法数据
@@ -2,7 +2,7 @@
* 计算练习题绘制服务
*/
import { BaseDrawService } from '../../../service/baseDraw';
import { BaseDrawService } from '../../../core/draw/baseDraw';
/**
* 计算练习题数据
@@ -2,7 +2,7 @@
* 数一数比大小绘制服务
*/
import { BaseDrawService } from '../../../service/baseDraw';
import { BaseDrawService } from '../../../core/draw/baseDraw';
import { getImage } from '../../../utils/index';
/**
@@ -2,7 +2,7 @@
* 数一数连一连绘制服务
*/
import { BaseDrawService } from '../../../service/baseDraw';
import { BaseDrawService } from '../../../core/draw/baseDraw';
import { getImage } from '../../../utils/index';
import { getRandomNumberColor } from '../../../constants/colors';
@@ -3,7 +3,7 @@
* 支持两种模式:'select'(选一选)和 'fill'(填一填)
*/
import { BaseDrawService } from '../../../service/baseDraw';
import { BaseDrawService } from '../../../core/draw/baseDraw';
import { getImage } from '../../../utils/index';
import { getRandomNumberColor } from '../../../constants/colors';
@@ -2,7 +2,7 @@
* 平十法练习绘制服务
*/
import { BaseDrawService } from '../../../service/baseDraw';
import { BaseDrawService } from '../../../core/draw/baseDraw';
/**
* 平十法数据
@@ -2,7 +2,7 @@
* 凑十法练习绘制服务
*/
import { BaseDrawService } from '../../../service/baseDraw';
import { BaseDrawService } from '../../../core/draw/baseDraw';
/**
* 凑十法数据
@@ -2,7 +2,7 @@
* 填上缺少的数字绘制服务
*/
import { BaseDrawService } from '../../../service/baseDraw';
import { BaseDrawService } from '../../../core/draw/baseDraw';
/**
* 填上缺少的数字数据
@@ -2,7 +2,7 @@
* 九九乘法表绘制服务
*/
import { BaseDrawService } from '../../../service/baseDraw';
import { BaseDrawService } from '../../../core/draw/baseDraw';
import { getRandomUniqueNumberColors } from '../../../constants/colors';
/**
@@ -2,7 +2,7 @@
* 按数字涂颜色绘制服务
*/
import { BaseDrawService } from '../../../service/baseDraw';
import { BaseDrawService } from '../../../core/draw/baseDraw';
import { getNumberColors } from '../../../constants/colors';
/**
@@ -2,7 +2,7 @@
* 10以内数的分与合绘制服务
*/
import { BaseDrawService } from '../../../service/baseDraw';
import { BaseDrawService } from '../../../core/draw/baseDraw';
import { getImage } from '../../../utils/index';
import { getRandomNumberColor } from '../../../constants/colors';
@@ -1,4 +1,4 @@
import { BaseDrawService } from '../../../service/baseDraw';
import { BaseDrawService } from '../../../core/draw/baseDraw';
import { drawNumberPreview } from './numberPreviewDraw';
import { drawNumberWriteContent } from './numberWriteDraw';
@@ -2,7 +2,7 @@
* 数物对应绘制服务
*/
import { BaseDrawService } from '../../../service/baseDraw';
import { BaseDrawService } from '../../../core/draw/baseDraw';
import { getImage } from '../../../utils/index';
/**
@@ -2,7 +2,7 @@
* 数字排序绘制服务
*/
import { BaseDrawService } from '../../../service/baseDraw';
import { BaseDrawService } from '../../../core/draw/baseDraw';
/**
* 数字排序数据
@@ -2,7 +2,7 @@
* 一位数加法绘制服务
*/
import { BaseDrawService } from '../../../service/baseDraw';
import { BaseDrawService } from '../../../core/draw/baseDraw';
import { getRandomNumberColor } from '../../../constants/colors';
/**
+1 -1
View File
@@ -1,4 +1,4 @@
import { BaseDrawService } from './baseDraw';
import { BaseDrawService } from '../core/draw/baseDraw';
import { POSITION_TEMPLATES } from './findWordTemplate';
// ==================== Debug 开关 ====================
+1 -1
View File
@@ -1,4 +1,4 @@
import { BaseDrawService } from './baseDraw';
import { BaseDrawService } from '../core/draw/baseDraw';
import { ShapeCard } from '../constants/shapes';
import { drawShape } from './drawShape';
+1 -1
View File
@@ -1,4 +1,4 @@
import { BaseDrawService } from './baseDraw';
import { BaseDrawService } from '../core/draw/baseDraw';
/**
* 计算示例区域圆的中心点
* @param canvasWidth 画布宽度(逻辑像素)
+1 -1
View File
@@ -1,4 +1,4 @@
import { BaseDrawService } from './baseDraw';
import { BaseDrawService } from '../core/draw/baseDraw';
import { CharacterItem } from '../types/characterType';
/**
+1
View File
@@ -24,6 +24,7 @@
@bg-gray: #f5edd8;
@bg-muted: #f5f5f5; // 分龄 Tab 未选中等弱对比底(Stitch)
@bg-week-head: #fafafa; // 分龄周卡头部(Stitch stone-50
@bg-card: #f0e7d6;
// ========== 文本色 ==========
@text-title: #322e25;
+35 -17
View File
@@ -3,21 +3,30 @@
/* ── 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-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-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 */
@btn-error-bg: #f95630;
/* error-container */
@btn-error-text: #520c00;
/* on-error-container */
/* surface 参考 */
@btn-surface-variant: #e5dcc9;
@@ -45,8 +54,8 @@
@btn-disabled-bg: @bg-gray;
@btn-disabled-color: @text-disable;
/* ── reset native button chrome ── */
wx-button:not([size=mini]) {
/* ── reset native button chrome(全宽块级按钮由 .toy-btn--block 自行控制宽度) ── */
wx-button.toy-btn:not([size=mini]):not(.toy-btn--block) {
margin-left: auto;
margin-right: auto;
width: auto;
@@ -252,17 +261,21 @@ wx-button:not([size=mini]) {
}
}
/* ═══════ block ═══════ */
/* ═══════ block(铺满父级 flex 槽位,避免被 wx-button 默认 width:auto 限制) ═══════ */
&--block {
display: flex;
width: 100%;
max-width: 100%;
margin-left: 0;
margin-right: 0;
box-sizing: border-box;
}
/* ═══════ states ═══════ */
/* 通用按下态:缩放 0.95 */
/* 通用按下态:缩放 0.9 */
&--active {
transform: scale(0.95);
transform: scale(0.9);
box-shadow: @btn-shadow-pressed;
}
@@ -312,6 +325,11 @@ wx-button:not([size=mini]) {
}
@keyframes toy-btn-spin {
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
}
0% {
transform: rotate(0deg);
}
100% {
transform: rotate(360deg);
}
}
+7
View File
@@ -171,6 +171,7 @@ Component({
visualType,
variant,
disabled,
pressed,
);
const resolvedIconSize =
this.normalizeUnit(iconSize) ||
@@ -214,10 +215,16 @@ Component({
type: string,
variant: string,
disabled: boolean,
pressed: boolean,
) {
if (disabled) return '#b8b2a6';
if (iconColor) return iconColor;
/* 与 .toy-btn--primary.toy-btn--active 的 color@btn-primary-pressed-text)一致 */
if (pressed && variant === 'filled' && type === 'primary') {
return '#fff2cd';
}
if (variant === 'outlined' || variant === 'ghost') {
const outlinedMap: Record<string, string> = {
primary: '#6c5a00',
+6 -2
View File
@@ -24,9 +24,13 @@
display: block;
}
/* 内层 text 显示 iconfont 字形 */
/* 内层 text 承载 ::before 字形,固定 1em 方盒便于与 flex 行内文案垂直对齐 */
> text {
display: inline-block;
display: inline-flex;
align-items: center;
justify-content: center;
width: 1em;
height: 1em;
line-height: 1;
font-style: normal;
}
+2 -4
View File
@@ -66,10 +66,8 @@ Component({
const styleSegments = [style];
if (normalizedSize) {
styleSegments.push(`font-size:${normalizedSize}`);
if (isImage) {
styleSegments.push(`width:${normalizedSize}`);
styleSegments.push(`height:${normalizedSize}`);
}
styleSegments.push(`width:${normalizedSize}`);
styleSegments.push(`height:${normalizedSize}`);
}
if (color && !isImage) {
styleSegments.push(`color:${color}`);