update at 2026-02-09 09:48:44
This commit is contained in:
@@ -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日(远端渲染改造)
|
||||
|
||||
|
||||
@@ -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 || '')
|
||||
|
||||
@@ -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="section-title">已收藏</view>
|
||||
<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">
|
||||
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user