update at 2026-02-09 09:48:44

This commit is contained in:
douboer
2026-02-09 09:48:44 +08:00
parent 77a0c7b741
commit 49c70efed0
4 changed files with 244 additions and 38 deletions

View File

@@ -1,5 +1,81 @@
# 小程序 UI 更新日志
## 更新时间
2026年2月9日
## 修复:"选择"与搜索框垂直对齐问题
### 问题描述
"选择"文字与右侧搜索框无法垂直居中对齐,"选择"看起来偏上。
### 根本原因
1. **全局样式污染**`app.wxss``index.wxss` 中的全局 `.section-title` 样式设置了 `padding: 12rpx 0``margin-bottom: 16rpx`,导致"选择"文字上下有额外间距
2. **小程序 input 组件最小高度**:微信小程序的 `<input>` 组件有默认最小高度(约 48rpx无法通过 CSS 设置更小的高度,导致搜索框实际高度大于预期
### 解决方案
1. **统一高度为 48rpx**:适配 input 组件的最小高度限制
2. **覆盖全局样式**:在 `.selection-header .section-title` 中显式设置 `padding: 0; margin: 0`
3. **强制 flexbox 居中**
- 父容器 `.selection-header` 使用 `display: flex; align-items: center`
- `.section-title` 使用 `display: flex; align-items: center; height: 48rpx`
- `.search-container` 使用 `height: 48rpx; overflow: hidden`
### 关键代码
```css
.selection-header {
display: flex;
align-items: center;
gap: 8rpx;
padding: 0;
}
.selection-header .section-title {
padding: 0;
margin: 0;
font-size: 28rpx;
font-weight: 400;
flex-shrink: 0;
display: flex;
align-items: center;
height: 48rpx;
}
.search-container {
flex: 1;
display: flex;
align-items: center;
background: #FEFDFE;
border-radius: 24rpx;
padding: 0 12rpx;
height: 48rpx;
min-width: 0;
overflow: hidden;
}
.search-input {
flex: 1;
font-size: 24rpx;
color: #4E5969;
height: 48rpx;
line-height: 48rpx;
min-height: 0;
padding: 0;
margin: 0;
background: transparent;
}
```
### 经验教训
1. **检查全局样式**:修改特定组件样式前,先检查是否有全局样式影响
2. **小程序组件限制**:微信小程序原生组件(如 input、textarea有内置最小尺寸需要适配而非强制覆盖
3. **调试技巧**:当 flexbox `align-items: center` 不生效时,优先检查子元素的 padding/margin/line-height
### 其他修复
- **搜索框初始状态**:将 `showSearch` 初始值从 `false` 改为 `true`,搜索框默认完整显示(符合 Figma 设计)
- **字体选中状态同步**:在 `bootstrap()` 中恢复 `selectedFonts` 后调用 `updateFontTrees()`,确保预览区的字体在字体树中正确显示为已选中
---
## 更新时间
2026年2月8日远端渲染改造

View File

@@ -30,10 +30,13 @@ const LOCAL_ICON_PATHS = {
fontIcon: '/assets/icons/font-icon.svg', // 字体item图标
expandIcon: '/assets/icons/expand.svg', // 展开分类图标
collapseIcon: '/assets/icons/expand.svg', // 折叠使用同一图标,通过旋转实现
favoriteIcon: '/assets/icons/favorite.svg', // 收藏图标(未收藏白色底,收藏红色底
checkbox: '/assets/icons/checkbox.svg', // 复选框(未选中
checkboxChecked: '/assets/icons/checkbox-no.svg', // 复选框(选中)
favoriteIcon: '/assets/icons/favorite.svg', // 收藏图标(未收藏)
favoriteRedIcon: '/assets/icons/favorite-red.svg', // 已收藏图标(红色
checkbox: '/assets/icons/checkbox-no.svg', // 复选框(选中)
checkboxChecked: '/assets/icons/checkbox.svg', // 复选框(已选中)
search: '/assets/icons/search.svg', // 搜索图标
selectAll: '/assets/icons/selectall.svg', // 全选图标
unselectAll: '/assets/icons/unselectall.svg', // 取消全选图标
}
function toSvgDataUri(svg) {
@@ -156,7 +159,7 @@ Page({
icons: LOCAL_ICON_PATHS,
// 搜索功能
searchKeyword: '',
showSearch: false,
showSearch: true,
},
async onLoad() {
@@ -233,6 +236,8 @@ Page({
}))
this.setData({ selectedFonts })
// 更新字体树以反映选中状态
this.updateFontTrees()
await this.generateAllPreviews()
}
} catch (error) {
@@ -284,6 +289,7 @@ Page({
? true
: (typeof expandedFromState === 'boolean' ? expandedFromState : false),
fonts: [],
allSelected: false,
})
}
categoryMap.get(category).fonts.push({
@@ -303,6 +309,15 @@ Page({
})
const fontCategories = Array.from(categoryMap.values()).sort((a, b) => a.category.localeCompare(b.category))
// 计算每个分类的allSelected状态
fontCategories.forEach(cat => {
if (cat.fonts.length > 0) {
cat.allSelected = cat.fonts.every(f => f.selected)
} else {
cat.allSelected = false
}
})
favoriteFonts.sort((a, b) => {
const categoryCompare = String(a.category || '').localeCompare(String(b.category || ''))
if (categoryCompare !== 0) return categoryCompare
@@ -414,6 +429,56 @@ Page({
}
},
// 切换分类全选/取消全选
onToggleSelectAllInCategory(e) {
const category = e.currentTarget.dataset.category
if (!category) return
const categoryFonts = this.data.fontCategories.find(c => c.category === category)
if (!categoryFonts || categoryFonts.fonts.length === 0) return
const allSelected = categoryFonts.allSelected
const selectedFonts = [...this.data.selectedFonts]
const selectedIdSet = new Set(selectedFonts.map(f => f.id))
if (allSelected) {
// 取消全选:移除该分类下的所有字体
categoryFonts.fonts.forEach(font => {
selectedIdSet.delete(font.id)
})
} else {
// 全选:添加该分类下的所有字体
categoryFonts.fonts.forEach(font => {
selectedIdSet.add(font.id)
})
}
const newSelectedFonts = []
this.fontMap.forEach(font => {
if (selectedIdSet.has(font.id)) {
newSelectedFonts.push({
id: font.id,
name: font.name,
category: font.category,
showInPreview: true,
previewSrc: '',
})
}
})
this.setData({ selectedFonts: newSelectedFonts })
this.updateFontTrees()
this.scheduleGenerate()
saveAppState({
inputText: this.data.inputText,
selectedFontIds: newSelectedFonts.map(f => f.id),
fontSize: Number(this.data.fontSize),
letterSpacing: Number(this.data.letterSpacingInput || 0),
textColor: this.data.textColor,
})
},
// 生成单个字体的预览
async generatePreviewForFont(fontId) {
const text = String(this.data.inputText || '')

View File

@@ -119,7 +119,10 @@
src="{{icons.expandIcon}}"
style="transform: rotate({{item.expanded ? '90deg' : '0deg'}})"
/>
<view class="category-name">{{item.category}}{{item.fonts.length}}</view>
<view class="category-name">{{item.category}}</view>
<view class="category-select-all" catchtap="onToggleSelectAllInCategory" data-category="{{item.category}}">
<image class="select-all-icon" src="{{item.allSelected ? icons.unselectAll : icons.selectAll}}" />
</view>
</view>
<view wx:if="{{item.expanded && item.fonts.length > 0}}" class="tree-vertical-line" />
<view wx:if="{{item.expanded}}" class="font-list select-font-list">
@@ -134,15 +137,12 @@
<view class="font-item-name">{{font.name}}</view>
<view class="font-item-actions">
<view class="font-checkbox" bindtap="onToggleFont" data-font-id="{{font.id}}">
<view class="checkbox-wrapper {{font.selected ? 'checked' : ''}}">
<image wx:if="{{font.selected}}" class="checkbox-icon-sm" src="{{icons.checkboxChecked}}" />
</view>
<image class="checkbox-icon-sm" src="{{font.selected ? icons.checkboxChecked : icons.checkbox}}" />
</view>
<view class="favorite-btn" bindtap="onToggleFavorite" data-font-id="{{font.id}}">
<image
class="favorite-icon"
src="{{icons.favoriteIcon}}"
style="opacity: {{font.isFavorite ? '1' : '0.3'}}"
src="{{font.isFavorite ? icons.favoriteRedIcon : icons.favoriteIcon}}"
/>
</view>
</view>
@@ -154,19 +154,19 @@
<!-- 已收藏字体 -->
<view class="favorite-selection">
<view class="favorite-header">
<view class="section-title">已收藏</view>
</view>
<scroll-view class="font-tree favorite-list" scroll-y>
<view wx:for="{{favoriteFonts}}" wx:key="id" class="font-item favorite-font-item">
<image class="font-item-icon" src="{{icons.fontIcon}}" />
<view class="font-item-name">{{item.name}}</view>
<view class="font-item-actions">
<view class="font-checkbox" bindtap="onToggleFont" data-font-id="{{item.id}}">
<view class="checkbox-wrapper {{item.selected ? 'checked' : ''}}">
<image wx:if="{{item.selected}}" class="checkbox-icon-sm" src="{{icons.checkboxChecked}}" />
</view>
<image class="checkbox-icon-sm" src="{{item.selected ? icons.checkboxChecked : icons.checkbox}}" />
</view>
<view class="favorite-btn" bindtap="onToggleFavorite" data-font-id="{{item.id}}">
<image class="favorite-icon" src="{{icons.favoriteIcon}}" />
<image class="favorite-icon" src="{{icons.favoriteRedIcon}}" />
</view>
</view>
</view>
@@ -175,6 +175,11 @@
</view>
</view>
<!-- 版权说明 -->
<view class="copyright-footer">
@版权说明仅SVG和PNG分享无TTF下载如侵权反馈douboer@gmail.com
</view>
<!-- 颜色选择器弹窗 -->
<view wx:if="{{showColorPicker}}" class="color-picker-modal" bindtap="onHideColorPicker">
<view class="color-picker-content" catchtap="onStopPropagation">

View File

@@ -122,8 +122,8 @@
display: flex;
flex-direction: column;
margin-top: 16rpx;
padding: 0 16rpx;
border: 1rpx solid #f7e0e0;
padding: 8rpx;
border: 1rpx solid #3EE4C3;
border-radius: 12rpx;
background: #fff;
overflow: hidden;
@@ -266,10 +266,11 @@
/* 字体选择和已收藏字体 */
.bottom-section {
display: flex;
flex: 1;
gap: 16rpx;
height: 600rpx;
min-height: 0;
margin-top: 16rpx;
padding: 0 16rpx;
padding: 0;
}
.font-selection,
@@ -277,11 +278,12 @@
flex: 1;
display: flex;
flex-direction: column;
border: 1rpx solid #f7e0e0;
border: 1rpx solid #3EE4C3;
border-radius: 16rpx;
background: #fff;
padding: 9rpx;
padding: 8rpx 8rpx;
overflow: hidden;
min-width: 0;
}
/* 搜索相关样式 */
@@ -290,58 +292,90 @@
align-items: center;
gap: 8rpx;
padding: 0;
height: 40rpx;
margin-bottom: 8rpx;
}
.selection-header .section-title {
padding: 0;
margin: 0;
font-size: 28rpx;
line-height: 40rpx;
font-weight: 400;
flex-shrink: 0;
display: flex;
align-items: center;
flex-shrink: 0;
height: 48rpx;
width: 66rpx;
}
.favorite-header {
display: flex;
align-items: center;
padding: 0;
margin-bottom: 8rpx;
}
.favorite-header .section-title {
padding: 0;
margin: 0;
font-size: 28rpx;
font-weight: 400;
display: flex;
align-items: center;
height: 48rpx;
}
.search-container {
flex: 1;
display: flex;
align-items: center;
gap: 6rpx;
background: #F7F8FA;
border-radius: 8rpx;
padding: 4rpx 12rpx;
height: 40rpx;
background: #FEFDFE;
border-radius: 24rpx;
padding: 0 12rpx;
height: 48rpx;
min-width: 0;
overflow: hidden;
}
.search-icon {
width: 28rpx;
height: 28rpx;
width: 24rpx;
height: 24rpx;
flex-shrink: 0;
opacity: 0.5;
margin-right: 8rpx;
}
.search-input {
flex: 1;
font-size: 22rpx;
font-size: 24rpx;
color: #4E5969;
height: 100%;
height: 48rpx;
line-height: 48rpx;
min-height: 0;
padding: 0;
margin: 0;
background: transparent;
}
.search-toggle {
width: 40rpx;
height: 40rpx;
width: 32rpx;
height: 32rpx;
display: flex;
align-items: center;
justify-content: center;
flex-shrink: 0;
background: #F7F8FA;
border-radius: 8rpx;
background: #8552A1;
border-radius: 20rpx;
}
.search-toggle .search-icon {
width: 18rpx;
height: 18rpx;
opacity: 1;
}
.font-tree {
flex: 1;
min-height: 0;
overflow-y: auto;
}
.font-category {
@@ -364,11 +398,28 @@
}
.category-name {
flex: 1;
font-size: 21rpx;
font-weight: 500;
color: #000;
}
.category-select-all {
width: 24rpx;
height: 24rpx;
display: flex;
align-items: center;
justify-content: center;
flex-shrink: 0;
padding: 4rpx;
}
.select-all-icon {
width: 100%;
height: 100%;
display: block;
}
.font-list {
display: flex;
flex-direction: column;
@@ -528,6 +579,15 @@
font-size: 28rpx;
}
/* 版权说明 */
.copyright-footer {
text-align: center;
font-size: 20rpx;
color: #86909C;
padding: 16rpx 0;
flex-shrink: 0;
}
/* 画布 */
.hidden-canvas {
position: fixed;