update at 2026-02-08 18:28:39

This commit is contained in:
douboer
2026-02-08 18:28:39 +08:00
parent e2a46e413a
commit 0f5a7f0d85
97 changed files with 22029 additions and 59 deletions

View File

@@ -0,0 +1,144 @@
const { loadFontsManifest, listCategories } = require('../../utils/mp/font-loader')
const { loadFavorites, saveFavorites } = require('../../utils/mp/storage')
Page({
data: {
fonts: [],
filteredFonts: [],
categories: ['全部'],
categoryIndex: 0,
favoriteOnly: false,
favorites: [],
searchText: '',
selectedFontId: '',
},
async onLoad(options) {
const selectedFontId = options && options.selected ? decodeURIComponent(options.selected) : ''
this.setData({ selectedFontId })
wx.showLoading({ title: '加载字体中', mask: true })
try {
const favorites = loadFavorites()
const fonts = await loadFontsManifest()
const categories = listCategories(fonts)
this.fontMap = new Map(fonts.map((font) => [font.id, font]))
this.setData({
fonts,
categories,
favorites,
})
this.applyFilter()
} catch (error) {
wx.showToast({ title: '字体加载失败', icon: 'none' })
} finally {
wx.hideLoading()
}
},
applyFilter() {
const {
fonts,
favorites,
searchText,
categories,
categoryIndex,
favoriteOnly,
} = this.data
const keyword = String(searchText || '').trim().toLowerCase()
const selectedCategory = categories[categoryIndex] || '全部'
const favoriteSet = new Set(favorites)
const filteredFonts = fonts
.filter((font) => {
if (favoriteOnly && !favoriteSet.has(font.id)) {
return false
}
if (selectedCategory !== '全部' && selectedCategory !== '收藏' && font.category !== selectedCategory) {
return false
}
if (selectedCategory === '收藏' && !favoriteSet.has(font.id)) {
return false
}
if (!keyword) {
return true
}
return (
String(font.name || '').toLowerCase().includes(keyword) ||
String(font.category || '').toLowerCase().includes(keyword)
)
})
.map((font) => ({
...font,
isFavorite: favoriteSet.has(font.id),
}))
this.setData({ filteredFonts })
},
onSearchInput(event) {
this.setData({ searchText: event.detail.value || '' })
this.applyFilter()
},
onCategoryChange(event) {
this.setData({ categoryIndex: Number(event.detail.value) || 0 })
this.applyFilter()
},
onToggleFavoriteOnly() {
this.setData({ favoriteOnly: !this.data.favoriteOnly })
this.applyFilter()
},
onToggleFavorite(event) {
const fontId = event.currentTarget.dataset.fontId
if (!fontId) {
return
}
const next = new Set(this.data.favorites)
if (next.has(fontId)) {
next.delete(fontId)
} else {
next.add(fontId)
}
const favorites = saveFavorites(Array.from(next))
this.setData({ favorites })
this.applyFilter()
},
onSelectFont(event) {
const fontId = event.currentTarget.dataset.fontId
if (!fontId) {
return
}
this.setData({ selectedFontId: fontId })
},
onCancel() {
wx.navigateBack()
},
onConfirm() {
const { selectedFontId } = this.data
if (!selectedFontId) {
wx.showToast({ title: '请选择字体', icon: 'none' })
return
}
const font = this.fontMap ? this.fontMap.get(selectedFontId) : null
const eventChannel = this.getOpenerEventChannel()
eventChannel.emit('fontSelected', {
fontId: selectedFontId,
font,
})
wx.navigateBack()
},
})

View File

@@ -0,0 +1,3 @@
{
"navigationBarTitleText": "选择字体"
}

View File

@@ -0,0 +1,53 @@
<view class="container">
<view class="card">
<input
class="search-input"
placeholder="搜索字体名称"
value="{{searchText}}"
bindinput="onSearchInput"
/>
<view class="toolbar row space-between">
<picker mode="selector" range="{{categories}}" value="{{categoryIndex}}" bindchange="onCategoryChange">
<view class="picker-btn">分类:{{categories[categoryIndex]}}</view>
</picker>
<button class="mini-btn" size="mini" bindtap="onToggleFavoriteOnly">
{{favoriteOnly ? '仅收藏中' : '全部字体'}}
</button>
</view>
</view>
<view class="card list-card">
<view class="summary">共 {{filteredFonts.length}} 个字体</view>
<scroll-view class="font-list" scroll-y>
<view
wx:for="{{filteredFonts}}"
wx:key="id"
class="font-item {{item.id === selectedFontId ? 'selected' : ''}}"
bindtap="onSelectFont"
data-font-id="{{item.id}}"
>
<view class="font-info">
<view class="font-name">{{item.name}}</view>
<view class="font-meta">{{item.category}}</view>
</view>
<view class="actions row">
<view
class="star"
catchtap="onToggleFavorite"
data-font-id="{{item.id}}"
>
{{item.isFavorite ? '★' : '☆'}}
</view>
<view wx:if="{{item.id === selectedFontId}}" class="selected-tag">已选</view>
</view>
</view>
<view wx:if="{{!filteredFonts.length}}" class="empty">没有匹配字体</view>
</scroll-view>
</view>
<view class="footer row space-between">
<button class="btn-secondary" bindtap="onCancel">取消</button>
<button class="btn-primary" bindtap="onConfirm">使用该字体</button>
</view>
</view>

View File

@@ -0,0 +1,102 @@
.search-input {
background: #f6f8fc;
border-radius: 12rpx;
padding: 18rpx 20rpx;
font-size: 28rpx;
}
.toolbar {
margin-top: 16rpx;
}
.picker-btn {
background: #f6f8fc;
border-radius: 12rpx;
padding: 14rpx 16rpx;
font-size: 26rpx;
}
.mini-btn {
margin: 0;
height: 56rpx;
line-height: 56rpx;
background: #edf2ff;
color: #274c95;
}
.list-card {
padding: 0;
}
.summary {
padding: 18rpx 22rpx;
color: #6b7280;
font-size: 24rpx;
border-bottom: 1rpx solid #f0f2f7;
}
.font-list {
height: calc(100vh - 410rpx);
}
.font-item {
display: flex;
align-items: center;
justify-content: space-between;
padding: 18rpx 22rpx;
border-bottom: 1rpx solid #f2f3f8;
}
.font-item.selected {
background: #eef5ff;
}
.font-name {
font-size: 30rpx;
font-weight: 500;
}
.font-meta {
margin-top: 6rpx;
font-size: 22rpx;
color: #6b7280;
}
.actions {
align-items: center;
}
.star {
font-size: 36rpx;
color: #f59e0b;
width: 56rpx;
text-align: center;
}
.selected-tag {
background: #1677ff;
color: #fff;
border-radius: 999rpx;
font-size: 20rpx;
padding: 6rpx 14rpx;
}
.empty {
color: #9ca3af;
text-align: center;
padding: 60rpx 0;
}
.footer {
position: fixed;
left: 24rpx;
right: 24rpx;
bottom: 24rpx;
gap: 16rpx;
}
.footer button {
flex: 1;
height: 84rpx;
line-height: 84rpx;
}