update at 2026-02-08 22:31:25
This commit is contained in:
@@ -1,28 +1,33 @@
|
||||
const { loadFontsManifest } = require('../../utils/mp/font-loader')
|
||||
const { loadAppState, saveAppState, loadFavorites, saveFavorites } = require('../../utils/mp/storage')
|
||||
const { saveSvgToUserPath, shareLocalFile, buildFilename } = require('../../utils/mp/file-export')
|
||||
const { exportSvgToPngByCanvas, savePngToAlbum } = require('../../utils/mp/canvas-export')
|
||||
const { renderSvgByApi } = require('../../utils/mp/render-api')
|
||||
const {
|
||||
shareSvgFromUserTap,
|
||||
} = require('../../utils/mp/file-export')
|
||||
const { savePngToAlbum } = require('../../utils/mp/canvas-export')
|
||||
const { renderSvgByApi, renderPngByApi } = require('../../utils/mp/render-api')
|
||||
// const { ICON_PATHS } = require('../../config/cdn') // CDN 方案暂时注释
|
||||
|
||||
const COLOR_PALETTE = ['#000000', '#1d4ed8', '#047857', '#b45309', '#dc2626', '#7c3aed']
|
||||
|
||||
// 临时使用本地图标
|
||||
// 临时使用本地图标 - 根据Figma annotation配置
|
||||
const LOCAL_ICON_PATHS = {
|
||||
logo: '/assets/icons/webicon.png',
|
||||
fontSizeDecrease: '/assets/icons/font-size-decrease.png',
|
||||
fontSizeIncrease: '/assets/icons/font-size-increase.png',
|
||||
chooseColor: '/assets/icons/choose-color.png',
|
||||
export: '/assets/icons/export.png',
|
||||
exportSvg: '/assets/icons/export-svg.png',
|
||||
exportPng: '/assets/icons/export-png.png',
|
||||
// 字体树图标(参考web项目)
|
||||
fontIcon: 'https://fonts.biboer.cn/assets/icons/icons_idx%20_18.svg', // 字体item图标
|
||||
expandIcon: 'https://fonts.biboer.cn/assets/icons/icons_idx%20_12.svg', // 展开分类图标
|
||||
collapseIcon: 'https://fonts.biboer.cn/assets/icons/zhedie.svg', // 折叠分类图标
|
||||
favoriteIcon: 'https://fonts.biboer.cn/assets/icons/icons_idx%20_19.svg', // 收藏图标
|
||||
checkbox: '/assets/icons/checkbox.png', // 预览checkbox
|
||||
search: 'https://fonts.biboer.cn/assets/icons/search.svg' // 搜索图标
|
||||
fontSizeDecrease: '/assets/icons/font-size-decrease.svg',
|
||||
fontSizeIncrease: '/assets/icons/font-size-increase.svg',
|
||||
chooseColor: '/assets/icons/choose-color.svg',
|
||||
// 导出按钮(根据Figma annotation)
|
||||
exportSvg: '/assets/icons/export-svg-s.svg', // 紫色SVG导出按钮
|
||||
exportPng: '/assets/icons/export-png-s.svg', // 蓝色PNG导出按钮
|
||||
// 输入框图标
|
||||
content: '/assets/icons/content.svg', // 输入框左侧绿色图标
|
||||
// 字体树图标(根据Figma annotation)
|
||||
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', // 复选框(已选中)
|
||||
search: '/assets/icons/search.svg', // 搜索图标
|
||||
}
|
||||
|
||||
function toSvgDataUri(svg) {
|
||||
@@ -38,6 +43,32 @@ function normalizeHexColor(input) {
|
||||
return fallback
|
||||
}
|
||||
|
||||
function extractErrorMessage(error, fallback) {
|
||||
if (!error) {
|
||||
return fallback
|
||||
}
|
||||
const errMsg = error.errMsg || error.message || String(error)
|
||||
return errMsg || fallback
|
||||
}
|
||||
|
||||
function showExportError(title, error, fallback) {
|
||||
const message = extractErrorMessage(error, fallback)
|
||||
wx.showModal({
|
||||
title,
|
||||
content: message,
|
||||
showCancel: false,
|
||||
confirmText: '知道了',
|
||||
})
|
||||
}
|
||||
|
||||
function writePngBufferToTempFile(pngBuffer, fontName) {
|
||||
const safeName = String(fontName || 'font').replace(/[<>:"/\\|?*\x00-\x1F]/g, '_').slice(0, 60) || 'font'
|
||||
const filePath = `${wx.env.USER_DATA_PATH}/${safeName}_${Date.now()}.png`
|
||||
const fs = wx.getFileSystemManager()
|
||||
fs.writeFileSync(filePath, pngBuffer)
|
||||
return filePath
|
||||
}
|
||||
|
||||
Page({
|
||||
data: {
|
||||
inputText: '星程字体转换',
|
||||
@@ -324,10 +355,19 @@ Page({
|
||||
selectedFonts[index].svg = result.svg
|
||||
selectedFonts[index].width = result.width
|
||||
selectedFonts[index].height = result.height
|
||||
selectedFonts[index].previewError = ''
|
||||
this.setData({ selectedFonts })
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('生成预览失败', error)
|
||||
const selectedFonts = this.data.selectedFonts
|
||||
const index = selectedFonts.findIndex(f => f.id === fontId)
|
||||
if (index >= 0) {
|
||||
selectedFonts[index].previewSrc = ''
|
||||
selectedFonts[index].svg = ''
|
||||
selectedFonts[index].previewError = (error && error.message) ? error.message : '预览生成失败'
|
||||
this.setData({ selectedFonts })
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
@@ -471,21 +511,14 @@ Page({
|
||||
return
|
||||
}
|
||||
|
||||
wx.showLoading({ title: '导出 SVG 中', mask: true })
|
||||
wx.showModal({
|
||||
title: '微信限制说明',
|
||||
content: '微信要求 shareFileMessage 必须由单次点击直接触发,暂不支持批量自动分享 SVG,请逐个字体导出。',
|
||||
showCancel: false,
|
||||
confirmText: '知道了',
|
||||
})
|
||||
return
|
||||
|
||||
try {
|
||||
for (const font of selectedFonts) {
|
||||
const filePath = await saveSvgToUserPath(font.svg, font.name, this.data.inputText)
|
||||
const filename = buildFilename(font.name, this.data.inputText, 'svg')
|
||||
await shareLocalFile(filePath, filename)
|
||||
}
|
||||
wx.showToast({ title: 'SVG 导出完成', icon: 'success' })
|
||||
} catch (error) {
|
||||
const message = error && error.errMsg ? error.errMsg : error.message
|
||||
wx.showToast({ title: message || '导出 SVG 失败', icon: 'none', duration: 2400 })
|
||||
} finally {
|
||||
wx.hideLoading()
|
||||
}
|
||||
},
|
||||
|
||||
async exportAllPng() {
|
||||
@@ -500,21 +533,24 @@ Page({
|
||||
|
||||
try {
|
||||
for (const font of selectedFonts) {
|
||||
const width = Math.max(64, Math.round(font.width || 1024))
|
||||
const height = Math.max(64, Math.round(font.height || 1024))
|
||||
|
||||
const pngPath = await exportSvgToPngByCanvas(this, {
|
||||
svgString: font.svg,
|
||||
width,
|
||||
height,
|
||||
const pngBuffer = await renderPngByApi({
|
||||
fontId: font.id,
|
||||
text: this.data.inputText,
|
||||
fontSize: Number(this.data.fontSize),
|
||||
fillColor: normalizeHexColor(this.data.textColor),
|
||||
letterSpacing: Number(this.data.letterSpacingInput || 0),
|
||||
maxCharsPerLine: 45,
|
||||
})
|
||||
const pngPath = writePngBufferToTempFile(pngBuffer, font.name)
|
||||
|
||||
await savePngToAlbum(pngPath)
|
||||
const saveResult = await savePngToAlbum(pngPath)
|
||||
if (!saveResult.success) {
|
||||
throw saveResult.error || new Error('保存 PNG 失败')
|
||||
}
|
||||
}
|
||||
wx.showToast({ title: 'PNG 已保存到相册', icon: 'success' })
|
||||
} catch (error) {
|
||||
const message = error && error.errMsg ? error.errMsg : error.message
|
||||
wx.showToast({ title: message || '导出 PNG 失败', icon: 'none', duration: 2400 })
|
||||
showExportError('导出 PNG 失败', error, '请稍后重试')
|
||||
} finally {
|
||||
wx.hideLoading()
|
||||
}
|
||||
@@ -531,17 +567,11 @@ Page({
|
||||
// 如果只有一个字体,直接导出
|
||||
if (selectedFonts.length === 1) {
|
||||
const font = selectedFonts[0]
|
||||
wx.showLoading({ title: '导出 SVG 中', mask: true })
|
||||
try {
|
||||
const filePath = await saveSvgToUserPath(font.svg, font.name, this.data.inputText)
|
||||
const filename = buildFilename(font.name, this.data.inputText, 'svg')
|
||||
await shareLocalFile(filePath, filename)
|
||||
await shareSvgFromUserTap(font.svg, font.name, this.data.inputText)
|
||||
wx.showToast({ title: 'SVG 已分享', icon: 'success' })
|
||||
} catch (error) {
|
||||
const message = error && error.errMsg ? error.errMsg : error.message
|
||||
wx.showToast({ title: message || '导出 SVG 失败', icon: 'none', duration: 2400 })
|
||||
} finally {
|
||||
wx.hideLoading()
|
||||
showExportError('导出 SVG 失败', error, '请稍后重试')
|
||||
}
|
||||
} else {
|
||||
this.exportAllSvg()
|
||||
@@ -562,24 +592,24 @@ Page({
|
||||
wx.showLoading({ title: '导出 PNG 中', mask: true })
|
||||
|
||||
try {
|
||||
const width = Math.max(64, Math.round(font.width || 1024))
|
||||
const height = Math.max(64, Math.round(font.height || 1024))
|
||||
|
||||
const pngPath = await exportSvgToPngByCanvas(this, {
|
||||
svgString: font.svg,
|
||||
width,
|
||||
height,
|
||||
const pngBuffer = await renderPngByApi({
|
||||
fontId: font.id,
|
||||
text: this.data.inputText,
|
||||
fontSize: Number(this.data.fontSize),
|
||||
fillColor: normalizeHexColor(this.data.textColor),
|
||||
letterSpacing: Number(this.data.letterSpacingInput || 0),
|
||||
maxCharsPerLine: 45,
|
||||
})
|
||||
const pngPath = writePngBufferToTempFile(pngBuffer, font.name)
|
||||
|
||||
const saveResult = await savePngToAlbum(pngPath)
|
||||
if (saveResult.success) {
|
||||
wx.showToast({ title: 'PNG 已保存到相册', icon: 'success' })
|
||||
} else {
|
||||
wx.showToast({ title: '保存失败,请重试', icon: 'none' })
|
||||
showExportError('导出 PNG 失败', saveResult.error, '保存失败,请检查相册权限')
|
||||
}
|
||||
} catch (error) {
|
||||
const message = error && error.errMsg ? error.errMsg : error.message
|
||||
wx.showToast({ title: message || '导出 PNG 失败', icon: 'none', duration: 2400 })
|
||||
showExportError('导出 PNG 失败', error, '请稍后重试')
|
||||
} finally {
|
||||
wx.hideLoading()
|
||||
}
|
||||
@@ -588,6 +618,59 @@ Page({
|
||||
}
|
||||
},
|
||||
|
||||
// 单个字体导出
|
||||
async onExportSingleSvg(e) {
|
||||
const fontId = e.currentTarget.dataset.fontId
|
||||
const font = this.data.selectedFonts.find(f => f.id === fontId)
|
||||
|
||||
if (!font || !font.svg) {
|
||||
wx.showToast({ title: '请先生成预览', icon: 'none' })
|
||||
return
|
||||
}
|
||||
|
||||
try {
|
||||
await shareSvgFromUserTap(font.svg, font.name, this.data.inputText)
|
||||
wx.showToast({ title: 'SVG 已分享', icon: 'success' })
|
||||
} catch (error) {
|
||||
showExportError('导出 SVG 失败', error, '请稍后重试')
|
||||
}
|
||||
},
|
||||
|
||||
async onExportSinglePng(e) {
|
||||
const fontId = e.currentTarget.dataset.fontId
|
||||
const font = this.data.selectedFonts.find(f => f.id === fontId)
|
||||
|
||||
if (!font || !font.svg) {
|
||||
wx.showToast({ title: '请先生成预览', icon: 'none' })
|
||||
return
|
||||
}
|
||||
|
||||
wx.showLoading({ title: '导出 PNG 中', mask: true })
|
||||
|
||||
try {
|
||||
const pngBuffer = await renderPngByApi({
|
||||
fontId: font.id,
|
||||
text: this.data.inputText,
|
||||
fontSize: Number(this.data.fontSize),
|
||||
fillColor: normalizeHexColor(this.data.textColor),
|
||||
letterSpacing: Number(this.data.letterSpacingInput || 0),
|
||||
maxCharsPerLine: 45,
|
||||
})
|
||||
const pngPath = writePngBufferToTempFile(pngBuffer, font.name)
|
||||
|
||||
const saveResult = await savePngToAlbum(pngPath)
|
||||
if (saveResult.success) {
|
||||
wx.showToast({ title: 'PNG 已保存到相册', icon: 'success' })
|
||||
} else {
|
||||
showExportError('导出 PNG 失败', saveResult.error, '保存失败,请检查相册权限')
|
||||
}
|
||||
} catch (error) {
|
||||
showExportError('导出 PNG 失败', error, '请稍后重试')
|
||||
} finally {
|
||||
wx.hideLoading()
|
||||
}
|
||||
},
|
||||
|
||||
// 搜索功能
|
||||
onToggleSearch() {
|
||||
this.setData({ showSearch: !this.data.showSearch })
|
||||
|
||||
Reference in New Issue
Block a user