feat(font): 添加本地字体选择功能(not done) #96
This commit is contained in:
51
src/hooks/useLocalFonts.ts
Normal file
51
src/hooks/useLocalFonts.ts
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
import { onMounted, ref } from 'vue'
|
||||||
|
|
||||||
|
export function useLocalFonts() {
|
||||||
|
const fonts = ref<Map<string, { name: string, value: string }[]>>(new Map())
|
||||||
|
const disabled = ref(false)
|
||||||
|
|
||||||
|
const formatFonts = (list: FontData[]): Map<string, { name: string, value: string }[]> => {
|
||||||
|
const res: Map<string, { name: string, value: string }[]> = new Map()
|
||||||
|
for (const item of list) {
|
||||||
|
if (!res.has(item.family)) {
|
||||||
|
res.set(item.family, [])
|
||||||
|
}
|
||||||
|
|
||||||
|
const fontArray = res.get(item.family)
|
||||||
|
if (!Array.isArray(fontArray)) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if (item.family === item.fullName) {
|
||||||
|
fontArray.push({ name: item.style, value: item.fullName })
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
const name = item.fullName.replace(item.family, '').trim()
|
||||||
|
const value = item.fullName
|
||||||
|
fontArray.push({ name, value })
|
||||||
|
}
|
||||||
|
return res
|
||||||
|
}
|
||||||
|
const getFonts = async () => {
|
||||||
|
if (!window.queryLocalFonts) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
const list = await window.queryLocalFonts()
|
||||||
|
console.log('list', list)
|
||||||
|
const res = formatFonts(list)
|
||||||
|
console.log('fontlist', res)
|
||||||
|
fonts.value = res
|
||||||
|
// 对比family,相同则移入第一个元素的children里面
|
||||||
|
}
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
if (!window.queryLocalFonts) {
|
||||||
|
disabled.value = true
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
return {
|
||||||
|
disabled,
|
||||||
|
fonts,
|
||||||
|
getFonts,
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -24,6 +24,7 @@ export const useGlobalConfig = defineStore('global', {
|
|||||||
patternColor: '#1b66c9',
|
patternColor: '#1b66c9',
|
||||||
patternList: defaultPatternList as number[],
|
patternList: defaultPatternList as number[],
|
||||||
background: {}, // 背景颜色或图片
|
background: {}, // 背景颜色或图片
|
||||||
|
font: '',
|
||||||
},
|
},
|
||||||
musicList: defaultMusicList as IMusic[],
|
musicList: defaultMusicList as IMusic[],
|
||||||
imageList: defaultImageList as IImage[],
|
imageList: defaultImageList as IImage[],
|
||||||
@@ -106,6 +107,10 @@ export const useGlobalConfig = defineStore('global', {
|
|||||||
getBackground(state) {
|
getBackground(state) {
|
||||||
return state.globalConfig.theme.background
|
return state.globalConfig.theme.background
|
||||||
},
|
},
|
||||||
|
// 获取字体
|
||||||
|
getFont(state) {
|
||||||
|
return state.globalConfig.theme.font
|
||||||
|
},
|
||||||
// 获取是否显示头像
|
// 获取是否显示头像
|
||||||
getIsShowAvatar(state) {
|
getIsShowAvatar(state) {
|
||||||
return state.globalConfig.isShowAvatar
|
return state.globalConfig.isShowAvatar
|
||||||
@@ -232,6 +237,10 @@ export const useGlobalConfig = defineStore('global', {
|
|||||||
setBackground(background: any) {
|
setBackground(background: any) {
|
||||||
this.globalConfig.theme.background = background
|
this.globalConfig.theme.background = background
|
||||||
},
|
},
|
||||||
|
// 设置字体
|
||||||
|
setFont(font: any) {
|
||||||
|
this.globalConfig.theme.font = font
|
||||||
|
},
|
||||||
// 设置是否显示头像
|
// 设置是否显示头像
|
||||||
setIsShowAvatar(isShowAvatar: boolean) {
|
setIsShowAvatar(isShowAvatar: boolean) {
|
||||||
this.globalConfig.isShowAvatar = isShowAvatar
|
this.globalConfig.isShowAvatar = isShowAvatar
|
||||||
@@ -256,6 +265,7 @@ export const useGlobalConfig = defineStore('global', {
|
|||||||
patternColor: '#1b66c9',
|
patternColor: '#1b66c9',
|
||||||
patternList: defaultPatternList as number[],
|
patternList: defaultPatternList as number[],
|
||||||
background: {}, // 背景颜色或图片
|
background: {}, // 背景颜色或图片
|
||||||
|
font: '',
|
||||||
},
|
},
|
||||||
musicList: defaultMusicList as IMusic[],
|
musicList: defaultMusicList as IMusic[],
|
||||||
imageList: defaultImageList as IImage[],
|
imageList: defaultImageList as IImage[],
|
||||||
|
|||||||
3
src/utils/format/tree.ts
Normal file
3
src/utils/format/tree.ts
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
/**
|
||||||
|
* @description 用于将平铺的数组组合到树形数组中
|
||||||
|
*/
|
||||||
@@ -6,6 +6,7 @@ import { useI18n } from 'vue-i18n'
|
|||||||
import { useRouter } from 'vue-router'
|
import { useRouter } from 'vue-router'
|
||||||
import { z as zod } from 'zod'
|
import { z as zod } from 'zod'
|
||||||
import { daisyuiThemes } from '@/constant/theme'
|
import { daisyuiThemes } from '@/constant/theme'
|
||||||
|
import { useLocalFonts } from '@/hooks/useLocalFonts'
|
||||||
import i18n, { languageList } from '@/locales/i18n'
|
import i18n, { languageList } from '@/locales/i18n'
|
||||||
import useStore from '@/store'
|
import useStore from '@/store'
|
||||||
import { themeChange } from '@/utils'
|
import { themeChange } from '@/utils'
|
||||||
@@ -18,9 +19,10 @@ const { t } = useI18n()
|
|||||||
const globalConfig = useStore().globalConfig
|
const globalConfig = useStore().globalConfig
|
||||||
const personConfig = useStore().personConfig
|
const personConfig = useStore().personConfig
|
||||||
const prizeConfig = useStore().prizeConfig
|
const prizeConfig = useStore().prizeConfig
|
||||||
const { getTopTitle: topTitle, getTheme: localTheme, getPatterColor: patternColor, getPatternList: patternList, getCardColor: cardColor, getLuckyColor: luckyCardColor, getTextColor: textColor, getCardSize: cardSize, getTextSize: textSize, getRowCount: rowCount, getIsShowPrizeList: isShowPrizeList, getLanguage: userLanguage, getBackground: backgroundImage, getImageList: imageList, getIsShowAvatar: isShowAvatar,
|
const { getTopTitle: topTitle, getTheme: localTheme, getPatterColor: patternColor, getPatternList: patternList, getCardColor: cardColor, getLuckyColor: luckyCardColor, getTextColor: textColor, getCardSize: cardSize, getTextSize: textSize, getRowCount: rowCount, getIsShowPrizeList: isShowPrizeList, getLanguage: userLanguage, getBackground: backgroundImage, getFont: currentFont, getImageList: imageList, getIsShowAvatar: isShowAvatar,
|
||||||
} = storeToRefs(globalConfig)
|
} = storeToRefs(globalConfig)
|
||||||
const { getAlreadyPersonList: alreadyPersonList, getNotPersonList: notPersonList } = storeToRefs(personConfig)
|
const { getAlreadyPersonList: alreadyPersonList, getNotPersonList: notPersonList } = storeToRefs(personConfig)
|
||||||
|
const { getFonts, disabled: disabledFonts, fonts } = useLocalFonts()
|
||||||
const colorPickerRef = ref()
|
const colorPickerRef = ref()
|
||||||
const resetDataDialogRef = ref()
|
const resetDataDialogRef = ref()
|
||||||
interface ThemeDaType {
|
interface ThemeDaType {
|
||||||
@@ -42,6 +44,7 @@ const patternColorValue = ref(structuredClone(patternColor.value))
|
|||||||
const themeList = ref(daisyuiThemes)
|
const themeList = ref(daisyuiThemes)
|
||||||
const daisyuiThemeList = ref<ThemeDaType>(daisyuiThemes)
|
const daisyuiThemeList = ref<ThemeDaType>(daisyuiThemes)
|
||||||
const backgroundImageValue = ref(backgroundImage.value)
|
const backgroundImageValue = ref(backgroundImage.value)
|
||||||
|
const currentFontValue = ref(currentFont.value)
|
||||||
const formData = ref({
|
const formData = ref({
|
||||||
rowCount: rowCountValue,
|
rowCount: rowCountValue,
|
||||||
})
|
})
|
||||||
@@ -150,6 +153,10 @@ watch(isShowPrizeListValue, () => {
|
|||||||
watch(backgroundImageValue, (val) => {
|
watch(backgroundImageValue, (val) => {
|
||||||
globalConfig.setBackground(val)
|
globalConfig.setBackground(val)
|
||||||
})
|
})
|
||||||
|
watch(currentFontValue, (val) => {
|
||||||
|
console.log('currentFontValue', val)
|
||||||
|
globalConfig.setFont(val)
|
||||||
|
})
|
||||||
watch(languageValue, (val: string) => {
|
watch(languageValue, (val: string) => {
|
||||||
globalConfig.setLanguage(val)
|
globalConfig.setLanguage(val)
|
||||||
})
|
})
|
||||||
@@ -225,6 +232,16 @@ onMounted(() => {
|
|||||||
class="w-full max-w-xs input input-bordered"
|
class="w-full max-w-xs input input-bordered"
|
||||||
>
|
>
|
||||||
</label>
|
</label>
|
||||||
|
|
||||||
|
<label class="w-full max-w-xs form-control">
|
||||||
|
<div class="label">
|
||||||
|
<span class="label-text">字体</span>
|
||||||
|
</div>
|
||||||
|
<select v-model="currentFontValue" class="w-full max-w-xs border-solid select border-1" @click="getFonts">
|
||||||
|
<option disabled selected>选择字体</option>
|
||||||
|
<option v-for="item in fonts" :key="item.fullName" :style="{ fontFamily: `${item.fullName}`, fontSize: 'normal', fontWeight: 'normal' }" :value="item.fullName">{{ item.fullName }}</option>
|
||||||
|
</select>
|
||||||
|
</label>
|
||||||
</fieldset>
|
</fieldset>
|
||||||
<!-- 布局设置(列数、卡片宽度、卡片高度 -->
|
<!-- 布局设置(列数、卡片宽度、卡片高度 -->
|
||||||
<fieldset class="p-4 border text-setting fieldset bg-base-200 border-base-300 rounded-box w-xs">
|
<fieldset class="p-4 border text-setting fieldset bg-base-200 border-base-300 rounded-box w-xs">
|
||||||
|
|||||||
@@ -1,51 +1,76 @@
|
|||||||
<script setup lang='ts'>
|
<script setup lang='ts'>
|
||||||
import { Grip } from 'lucide-vue-next'
|
import { ChevronDown, ChevronUp } from 'lucide-vue-next'
|
||||||
import { onMounted, ref, watch } from 'vue'
|
import { ref } from 'vue'
|
||||||
import { VueDraggable } from 'vue-draggable-plus'
|
import {
|
||||||
|
DropdownMenu,
|
||||||
|
DropdownMenuContent,
|
||||||
|
DropdownMenuItem,
|
||||||
|
DropdownMenuLabel,
|
||||||
|
DropdownMenuSeparator,
|
||||||
|
DropdownMenuTrigger,
|
||||||
|
} from '@/components/ui/dropdown-menu'
|
||||||
|
import { useLocalFonts } from '@/hooks/useLocalFonts'
|
||||||
|
|
||||||
const list = ref([
|
const inputRef = ref()
|
||||||
{
|
|
||||||
name: 'Joao',
|
|
||||||
id: '1',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'Jean',
|
|
||||||
id: '2',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'Johanna',
|
|
||||||
id: '3',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'Juan',
|
|
||||||
id: '4',
|
|
||||||
},
|
|
||||||
])
|
|
||||||
onMounted(() => {
|
|
||||||
|
|
||||||
})
|
const { getFonts, disabled, fonts } = useLocalFonts()
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div>
|
<div class="flex flex-col gap-4">
|
||||||
<button class="btn btn-error">
|
<div class="text-2xl">
|
||||||
打印
|
code
|
||||||
</button>
|
|
||||||
<VueDraggable
|
|
||||||
v-model="list"
|
|
||||||
:animation="150"
|
|
||||||
handle=".handle"
|
|
||||||
class="flex flex-col gap-2 p-4 w-300px bg-gray-500/5 rounded"
|
|
||||||
>
|
|
||||||
<div
|
|
||||||
v-for="(item, index) in list"
|
|
||||||
:key="item.id"
|
|
||||||
class="h-50px bg-gray-500/5 px-2 rounded flex items-center justify-between"
|
|
||||||
>
|
|
||||||
<Grip class="handle cursor-move" />
|
|
||||||
<input v-model="item.name" type="text">
|
|
||||||
</div>
|
</div>
|
||||||
</VueDraggable>
|
<div class="text-2xl" style="font-family: 'Consolas Bold Italic';">
|
||||||
|
code
|
||||||
|
</div>
|
||||||
|
<font-select id="font-select-1" value="" style="display: initial;" />
|
||||||
|
</div>
|
||||||
|
<div class="w-full h-full flex justify-center items-center">
|
||||||
|
<DropdownMenu>
|
||||||
|
<DropdownMenuTrigger as-child>
|
||||||
|
<label class="input cursor-default!" @click="getFonts">
|
||||||
|
<input ref="inputRef" type="search" class="grow" placeholder="Search">
|
||||||
|
<ChevronDown class="cursor-default" @click="inputRef.focus()" />
|
||||||
|
<ChevronUp class="cursor-default" @click="inputRef.focus()" />
|
||||||
|
</label>
|
||||||
|
</DropdownMenuTrigger>
|
||||||
|
<DropdownMenuContent class="w-56" align="start">
|
||||||
|
<DropdownMenuLabel>My Account</DropdownMenuLabel>
|
||||||
|
<DropdownMenuGroup>
|
||||||
|
<!-- <DropdownMenuItem v-for="[key, value] in Array.from(fonts)" :key="key" :style="{ fontFamily: `${key}` }">
|
||||||
|
{{ key }}
|
||||||
|
</DropdownMenuItem> -->
|
||||||
|
<DropdownMenuSub v-for="[key, children] in Array.from(fonts)" :key="key">
|
||||||
|
<DropdownMenuSubTrigger>
|
||||||
|
<span :style="{ fontFamily: `${key}` }">{{ key }}</span>
|
||||||
|
</DropdownMenuSubTrigger>
|
||||||
|
<DropdownMenuPortal>
|
||||||
|
<DropdownMenuSubContent>
|
||||||
|
<DropdownMenuItem v-for="child in children" :key="child.value">
|
||||||
|
<span :style="{ fontFamily: `${child.value}` }">{{ child.name }}</span>
|
||||||
|
</DropdownMenuItem>
|
||||||
|
</DropdownMenuSubContent>
|
||||||
|
</DropdownMenuPortal>
|
||||||
|
</DropdownMenuSub>
|
||||||
|
<DropdownMenuItem>
|
||||||
|
New Team
|
||||||
|
<DropdownMenuShortcut>⌘+T</DropdownMenuShortcut>
|
||||||
|
</DropdownMenuItem>
|
||||||
|
</DropdownMenuGroup>
|
||||||
|
<DropdownMenuSeparator />
|
||||||
|
<DropdownMenuItem>GitHub</DropdownMenuItem>
|
||||||
|
<DropdownMenuItem>Support</DropdownMenuItem>
|
||||||
|
<DropdownMenuItem disabled>
|
||||||
|
API
|
||||||
|
</DropdownMenuItem>
|
||||||
|
<DropdownMenuSeparator />
|
||||||
|
<DropdownMenuItem>
|
||||||
|
Log out
|
||||||
|
<DropdownMenuShortcut>⇧⌘Q</DropdownMenuShortcut>
|
||||||
|
</DropdownMenuItem>
|
||||||
|
</DropdownMenuContent>
|
||||||
|
</DropdownMenu>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
|||||||
12
src/window.d.ts
vendored
Normal file
12
src/window.d.ts
vendored
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
// src/types/window.d.ts
|
||||||
|
interface FontData {
|
||||||
|
family: string
|
||||||
|
fullName: string
|
||||||
|
postscriptName: string
|
||||||
|
style: string
|
||||||
|
blob: () => Promise<Blob>
|
||||||
|
}
|
||||||
|
|
||||||
|
interface Window {
|
||||||
|
queryLocalFonts?: (options?) => Promise<FontData[]>
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user