完成部分设置项和游戏的代码

This commit is contained in:
expdsn 2024-10-25 18:53:08 +08:00
parent 199ca92fb3
commit 4f571eed13
16 changed files with 478 additions and 125 deletions

BIN
public/searchIcons/360.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.2 KiB

BIN
public/searchIcons/bing.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

View File

@ -10,14 +10,20 @@ import {
import AdderPageBack from './AdderPageBack'
import useLayoutStore from '../useLayoutStore'
import { OhVueIcon, addIcons } from 'oh-vue-icons'
import { MdKeyboardcommandkey, FaCompass, FaPencilRuler } from 'oh-vue-icons/icons'
import {
MdKeyboardcommandkey,
FaCompass,
FaPencilRuler,
IoGameController
} from 'oh-vue-icons/icons'
import CustomAdder from './CustomAdder'
import clsx from 'clsx'
import ThemeProvider from '@/utils/ThemeProvider'
import WidgetAdder from './WidgetAdder'
import { Form, Input, Select } from 'ant-design-vue'
import HotAdder from './HotAdder'
addIcons(MdKeyboardcommandkey, FaCompass, FaPencilRuler)
import GameAdder from './GameAdder'
addIcons(MdKeyboardcommandkey, FaCompass, FaPencilRuler, IoGameController)
const ItemButton = defineComponent({
props: {
@ -118,6 +124,14 @@ export default defineComponent(() => {
type.value = 2
}}
/>
<ItemButton
name="io-game-controller"
label="快游戏"
active={type.value === 3}
onClick={() => {
type.value = 3
}}
/>
</div>
<div
class={'w-0 h-full flex-grow relative z-10 flex flex-col ' + (isGame.value ? '' : '')}
@ -145,8 +159,10 @@ export default defineComponent(() => {
<WidgetAdder />
) : type.value === 1 ? (
<HotAdder />
) : (
) : type.value === 2 ? (
<CustomAdder />
) : (
<GameAdder />
)}
</Transition>
</div>

View File

@ -0,0 +1,211 @@
import CategoryTab from '@/utils/CategoryTab'
import request from '@/utils/request'
import { computed, defineComponent, inject, onMounted, ref, watch } from 'vue'
import type { BackgroundType } from '../background/BackgroundSwtich'
import clsx from 'clsx'
import useLayoutStore from '../useLayoutStore'
import { AddToToken } from './AdderPage'
import { v4 as uuid } from 'uuid'
import { Button } from 'ant-design-vue'
const URL_ADDRESS = 'http://newfatfox.oss-cn-beijing.aliyuncs.com'
interface GameType {
id: string
name: string
rom: string
playstation: string
hot: number
category: string
despt: string
icon: string
}
export const GameItem = defineComponent({
props: {
content: {
type: Object as () => GameType,
required: true
}
},
setup(props) {
const layout = useLayoutStore()
const isGame = computed(() => layout.state.current === 0)
const addTo = inject(AddToToken)
return () => (
<div
class={clsx(' w-full h-full rounded-lg flex flex-col justify-between shadow p-4', {
'bg-white/20': isGame.value,
'bg-white/80': !isGame.value
})}
key={props.content.name}
>
<div class="flex">
<img
src={props.content.icon.replace('res/game', URL_ADDRESS)}
class="w-[58px] h-[58px] bg-cover rounded-lg shadow-lg"
style={{
background: props.content.icon
}}
/>
<div class="px-2 w-0 flex-grow">
<div
class={clsx('text text-sm', {
'text-white': isGame.value,
'text-black/80': !isGame.value
})}
>
{props.content.name}
</div>
<div
class={clsx('text-[12px] line-clamp-2 text-ellipsis', {
'text-white/80': isGame.value,
'text-black/60': !isGame.value
})}
>
{props.content.despt}
</div>
</div>
</div>
<div class="flex justify-end">
<button
class={clsx(
'rounded-2xl text-[12px] flex justify-center items-center w-[60px] h-[24px]',
{
'bg-[#eeeeee] text-[#333] ': isGame.value,
'bg-[#ffaa4e] text-white': !isGame.value
}
)}
onClick={() => {
layout.addBlock(
{
id: uuid(),
link: '',
name: props.content.name,
label: props.content.name,
icon: '',
text: '',
background: '',
color: '',
w: 1,
h: 1
},
addTo?.value
)
}}
>
</button>
</div>
</div>
)
}
})
export default defineComponent(() => {
const layout = useLayoutStore()
const loading = ref(false)
const isGame = computed(() => layout.state.current === 0)
const appList = ref<GameType[]>([])
const selectType = ref('fc')
watch(
selectType,
(val) => {
loading.value = true
request<GameType[]>('GET', `/api/games?type=${val}`)
.then((res) => {
appList.value = res
})
.finally(() => {
setTimeout(() => {
loading.value = false
}, 200)
})
},
{
immediate: true
}
)
return () => (
<div class={'w-full h-full flex flex-col gap-y-4'}>
<div class={'w-full '}>
<CategoryTab
list={[
{
id: 'fc',
type: '经典红白机',
attr: 0,
oridinal: 0
},
{
id: 'md',
type: '经典世嘉',
attr: 1,
oridinal: 1
},
{
id: 'gma',
type: '经典GBA',
attr: 3,
oridinal: 3
}
]}
selectType={selectType.value}
onUpdate:type={(e) => {
selectType.value = e
}}
v-slots={{
select: (text: string) => (
<button
class={clsx(
'px-[20px] py-1 items-center justify-center duration-75 shrink-0 flex rounded-2xl whitespace-nowrap ',
isGame.value
? 'bg-white/30 text-white bg-gradient-to-b from-[#ffaa4e] to-[#ff6227]'
: 'text-[#000] bg-[#D6D6D6]'
)}
>
{text}
</button>
),
unSelect: (text: string) => (
<button
class={clsx(
'px-[20px] py-1 items-center justify-center duration-75 shrink-0 flex rounded-2xl whitespace-nowrap',
isGame.value
? ' text-[#999] bg-white/10 hover:bg-white/20'
: 'text-[#000] hover:bg-[#f0ecec]'
)}
>
{text}
</button>
)
}}
></CategoryTab>
</div>
<div class={'h-0 flex-1 w-full'}>
<div class={'w-full h-full overflow-y-scroll scrollbar-hide'}>
{!loading.value ? (
<div class={'w-full grid grid-cols-3 gap-4 '} style="grid-auto-rows: 120px">
{appList.value.map((el) => (
<GameItem content={el}></GameItem>
))}
</div>
) : (
<div class={'w-full grid grid-cols-3 gap-4 '} style="grid-auto-rows: 120px">
{Array(12)
.fill(0)
.map((el, idx) => (
<div
class={
' relative cursor-pointer bg-gray-500 group w-full flex-grow-0 rounded-xl overflow-hidden'
}
key={idx}
/>
))}
</div>
)}
</div>
</div>
</div>
)
})

View File

@ -5,6 +5,7 @@ import type { BackgroundType } from '../background/BackgroundSwtich'
import clsx from 'clsx'
import useLayoutStore from '../useLayoutStore'
import { AddToToken } from './AdderPage'
import { v4 as uuid } from 'uuid'
import { Button } from 'ant-design-vue'
interface HotAppCategoryType {
@ -34,14 +35,21 @@ export const LinkItem = defineComponent({
const addTo = inject(AddToToken)
return () => (
<div
class={clsx('bg-white w-full h-full rounded-lg shadow p-4', {
class={clsx(' w-full h-full rounded-lg flex flex-col justify-between shadow p-4', {
'bg-white/20': isGame.value,
'bg-white/80': !isGame.value
})}
key={props.content.name}
>
<div class="flex">
<img src={props.content.icon} class="w-[48px] h-[48px] bg-cover rounded-lg" />
<img
src={props.content.icon}
class="w-[58px] h-[58px] bg-cover rounded-lg shadow-lg"
style={{
background: props.content.background,
}}
/>
<div class="px-2 w-0 flex-grow">
<div
class={clsx('text text-sm', {
@ -62,9 +70,34 @@ export const LinkItem = defineComponent({
</div>
</div>
<div class="flex justify-end">
<Button size="small" type="primary" onClick={() => {}}>
<button
class={clsx(
'rounded-2xl text-[12px] flex justify-center items-center w-[60px] h-[24px]',
{
'bg-[#eeeeee] text-[#333] ': isGame.value,
'bg-[#ffaa4e] text-white': !isGame.value
}
)}
onClick={() => {
layout.addBlock(
{
id: uuid(),
link: '',
name: props.content.name,
label: props.content.name,
icon: '',
text: '',
background: '',
color: '',
w: 1,
h: 1
},
addTo?.value
)
}}
>
</Button>
</button>
</div>
</div>
)
@ -130,7 +163,9 @@ export default defineComponent(() => {
<button
class={clsx(
'px-[20px] py-1 items-center justify-center duration-75 shrink-0 flex rounded-2xl whitespace-nowrap',
isGame.value ? ' text-[#999] bg-white/10 hover:bg-white/20' : 'text-[#000] hover:bg-[#f0ecec]'
isGame.value
? ' text-[#999] bg-white/10 hover:bg-white/20'
: 'text-[#000] hover:bg-[#f0ecec]'
)}
>
{text}
@ -142,19 +177,19 @@ export default defineComponent(() => {
<div class={'h-0 flex-1 w-full'}>
<div class={'w-full h-full overflow-y-scroll scrollbar-hide'}>
{!loading.value ? (
<div class={'w-full grid grid-cols-3 gap-4 '} style="grid-auto-rows: 100px">
<div class={'w-full grid grid-cols-3 gap-4 '} style="grid-auto-rows: 120px">
{appList.value.map((el) => (
<LinkItem content={el}></LinkItem>
))}
</div>
) : (
<div class={'w-full grid grid-cols-3 gap-4 '} style="grid-auto-rows: 100px">
<div class={'w-full grid grid-cols-3 gap-4 '} style="grid-auto-rows: 120px">
{Array(12)
.fill(0)
.map((el, idx) => (
<div
class={
'h-[100px] relative cursor-pointer bg-gray-500 group w-full flex-grow-0 rounded-xl overflow-hidden'
' relative cursor-pointer bg-gray-500 group w-full flex-grow-0 rounded-xl overflow-hidden'
}
key={idx}
/>

View File

@ -26,26 +26,30 @@ export default defineComponent(() => {
label: () => <div></div>
}}
>
<Slider
v-model:value={settings.state.blockRadius}
step={0.1}
tooltipOpen={false}
min={0}
max={1}
/>
<div class={'w-[180px]'}>
<Slider
v-model:value={settings.state.blockRadius}
step={0.01}
tooltipOpen={false}
min={0}
max={1}
/>
</div>
</SettingItem>{' '}
<SettingItem
v-slots={{
label: () => <div></div>
}}
>
<Slider
v-model:value={settings.state.mainWidth}
step={1}
tooltipOpen={false}
min={40}
max={80}
/>
<div class={'w-[180px]'}>
<Slider
v-model:value={settings.state.mainWidth}
step={0.01}
tooltipOpen={false}
min={0}
max={1}
/>
</div>
</SettingItem>
{/* <SettingItem
noRoundedB

View File

@ -0,0 +1,61 @@
import SettingItem from '@/settings/SettingItem'
import useSettingsStore from '@/settings/useSettingsStore'
import { Select, Slider, Switch, type SelectProps } from 'ant-design-vue'
import { defineComponent, ref } from 'vue'
import useSearchConfigStore from '../header/search/useSearchConfigStore'
export default defineComponent({
name: 'SearchSetting',
props: {},
setup() {
const settings = useSettingsStore()
const searchStore = useSearchConfigStore()
const options1 = ref<SelectProps['options']>(
[...searchStore.defaultList, ...searchStore.customList].map((item) => ({
value: item.name,
label: item.name
}))
)
return () => (
<div class="p-4">
<SettingItem
noRoundedB
v-slots={{
label: () => <div></div>
}}
>
<Switch v-model:checked={settings.state.showBlockLabel} />
</SettingItem>
<SettingItem
noRoundedT
v-slots={{
label: () => <div></div>
}}
>
<Switch v-model:checked={settings.state.showHistory} />
</SettingItem>
<SettingItem
v-slots={{
label: () => <div></div>
}}
>
<Switch v-model:checked={settings.state.showHistory} />
</SettingItem>{' '}
<SettingItem
v-slots={{
label: () => <div></div>
}}
>
<Select
value={searchStore.current.name}
onUpdate:value={(e) => {
searchStore.selectSearchByName(e as string)
}}
style={{ width: 100 }}
options={options1.value}
/>
</SettingItem>
</div>
)
}
})

View File

@ -65,7 +65,7 @@ export default defineComponent(() => {
(block, idx) => block?.id && <BlockWrapper key={block.id} idx={idx} block={block} />
)}
{settings.state.showAdder && (
<div class="w-full h-full flex justify-center items-center p-[var(--block-padding)] relative operation-button">
<div class="w-full h-full flex justify-center items-center p-[var(--block-padding)] relative operation-button">
<div
class="w-full h-full overflow-hidden rounded-[calc(var(--block-radius)_*_var(--block-size))] bg-white/60 backdrop-blur flex justify-center items-center cursor-pointer hover:scale-105 transition-all"
onClick={() => {

View File

@ -3,38 +3,41 @@ import { OhVueIcon, addIcons } from 'oh-vue-icons'
import { MdHistory, MdRemove } from 'oh-vue-icons/icons'
import useSearchConfigStore from './useSearchConfigStore'
import jump from '@/utils/jump'
import useSettingsStore from '@/settings/useSettingsStore'
addIcons(MdHistory)
addIcons(MdRemove)
export default defineComponent(() => {
const searchConfig = useSearchConfigStore()
return () => (
<div class="absolute left-0 -bottom-2 translate-y-full w-full rounded-lg bg-white/60 backdrop-blur shadow-lg p-4">
{searchConfig.history.map((item, idx) => (
<div
key={idx}
class="flex justify-between items-center text-black/80 cursor-pointer hover:bg-white/40 py-1 px-2 rounded transition-all"
onMousedown={() => {
jump(searchConfig.current.url + item)
}}
>
<div class="flex items-center w-0 flex-grow">
<OhVueIcon name="md-history" fill="rgba(0,0,0,0.8)" />
<div class="w-0 pl-2 flex-grow text-ellipsis overflow-hidden whitespace-nowrap break-all">
{item}
</div>
</div>
const settings = useSettingsStore()
return () =>
settings.state.showHistory && (
<div class="absolute left-0 -bottom-2 translate-y-full w-full rounded-lg bg-white/60 backdrop-blur shadow-lg p-4">
{searchConfig.history.map((item, idx) => (
<div
class="text-black/40 hover:bg-red-500 hover:text-white px-1 rounded transition-all"
onMousedown={(e) => {
e.preventDefault()
e.stopPropagation()
searchConfig.removeHistory(idx)
key={idx}
class="flex justify-between items-center text-black/80 cursor-pointer hover:bg-white/40 py-1 px-2 rounded transition-all"
onMousedown={() => {
jump(searchConfig.current.url + item)
}}
>
<OhVueIcon name="md-remove" />
<div class="flex items-center w-0 flex-grow">
<OhVueIcon name="md-history" fill="rgba(0,0,0,0.8)" />
<div class="w-0 pl-2 flex-grow text-ellipsis overflow-hidden whitespace-nowrap break-all">
{item}
</div>
</div>
<div
class="text-black/40 hover:bg-red-500 hover:text-white px-1 rounded transition-all"
onMousedown={(e) => {
e.preventDefault()
e.stopPropagation()
searchConfig.removeHistory(idx)
}}
>
<OhVueIcon name="md-remove" />
</div>
</div>
</div>
))}
</div>
)
))}
</div>
)
})

View File

@ -22,7 +22,7 @@ export default defineComponent(() => {
>
<div
class={
'w-full h-11 shadow-content overflow-hidden max-w-[90vw] transition-all flex justify-between items-center gap-4 ' +
'w-full h-11 shadow-content overflow-hidden max-w-[90vw] px-1 transition-all flex justify-between items-center gap-4 ' +
(search.focus ? 'bg-white/60' : 'bg-white/40 hover:bg-white/60')
}
style={{
@ -30,17 +30,18 @@ export default defineComponent(() => {
}}
>
<div
class="w-11 h-11 flex justify-center items-center"
class="w-9 h-11 flex justify-center items-center cursor-pointer"
onClick={() => {
search.showSearchConfig = true
}}
>
<div class="p-2 rounded-full overflow-hidden bg-white/40 cursor-pointer">
<div
class="w-4 h-4 bg-center bg-contain bg-no-repeat"
style={{ backgroundImage: `url('${searchConfig.current.icon}')` }}
/>
</div>
<div
class="w-[26px] h-[26px] bg-center bg-contain bg-no-repeat"
style={{
backgroundImage: `url('${searchConfig.current.icon}')`,
borderRadius: settings.state.searchRadius - 4 + 'px'
}}
/>
</div>
<input
v-model={search.searchStr}

View File

@ -12,78 +12,79 @@ const defaultSearchList: SearchInfo[] = [
{
name: '百度',
url: 'https://www.baidu.com/s?wd=',
icon: 'searchIcons/baidu.svg',
icon: 'searchIcons/baidu.png',
show: true
},
{
name: '必应',
url: 'https://cn.bing.com/search?q=',
icon: 'searchIcons/bing.svg',
icon: 'searchIcons/bing.png',
show: true
},
{
name: '谷歌',
url: 'https://www.google.com/search?q=',
icon: 'searchIcons/google.svg',
icon: 'searchIcons/google.png',
show: true
},
{
name: '360',
url: 'https://www.so.com/s?q=',
icon: 'searchIcons/360.svg',
show: true
},
{
name: '搜狗',
url: 'https://www.sogou.com/sogou?&query=',
icon: 'searchIcons/sougou.svg',
icon: 'searchIcons/360.png',
show: true
}
]
const defaultCustomSearchList: SearchInfo[] = [
{
name: '知乎',
url: 'https://www.zhihu.com/search?type=content&q=',
icon: 'searchIcons/zhihu.svg',
show: true
},
{
name: 'GitHub',
url: 'https://github.com/search?q=',
icon: 'searchIcons/GitHub.svg',
show: true
},
{
name: 'F搜',
url: 'https://fsoufsou.com/search?q=',
icon: 'searchIcons/F.svg',
show: true
},
{
name: '豆瓣',
url: 'https://www.douban.com/search?q=',
icon: 'searchIcons/douban.svg',
show: true
},
{
name: 'Yandex',
url: 'https://yandex.com/search/?text=',
icon: 'searchIcons/yandex.svg',
show: true
},
{
name: '开发者',
url: 'https://kaifa.baidu.com/searchPage?wd=',
icon: 'searchIcons/kaifa.svg',
show: true
},
{
name: 'B站',
url: 'https://search.bilibili.com/all?keyword=',
icon: 'searchIcons/bilibili.svg',
show: true
}
]
// const defaultCustomSearchList: SearchInfo[] = [
// {
// name: '知乎',
// url: 'https://www.zhihu.com/search?type=content&q=',
// icon: 'searchIcons/zhihu.svg',
// show: true
// },
// {
// name: 'GitHub',
// url: 'https://github.com/search?q=',
// icon: 'searchIcons/GitHub.svg',
// show: true
// },
// {
// name: 'F搜',
// url: 'https://fsoufsou.com/search?q=',
// icon: 'searchIcons/F.svg',
// show: true
// },
// {
// name: '豆瓣',
// url: 'https://www.douban.com/search?q=',
// icon: 'searchIcons/douban.svg',
// show: true
// },
// {
// name: 'Yandex',
// url: 'https://yandex.com/search/?text=',
// icon: 'searchIcons/yandex.svg',
// show: true
// },
// {
// name: '开发者',
// url: 'https://kaifa.baidu.com/searchPage?wd=',
// icon: 'searchIcons/kaifa.svg',
// show: true
// },
// {
// name: 'B站',
// url: 'https://search.bilibili.com/all?keyword=',
// icon: 'searchIcons/bilibili.svg',
// show: true
// }
// ]
export default defineStore(
'searchConfig',
() => {
@ -100,13 +101,20 @@ export default defineStore(
const removeHistory = (idx: number) => {
history.value.splice(idx, 1)
}
const selectSearchByName = (name: string) => {
const search = defaultList.value.find((item) => item.name === name)
if (search) {
current.value = search
}
}
return {
current,
customList,
defaultList,
history,
addHistory,
removeHistory
removeHistory,
selectSearchByName
}
},
{ persist: true }

View File

@ -1,23 +1,26 @@
import useRouterStore from '@/useRouterStore'
import { defineComponent, Transition } from 'vue'
import { defineComponent } from 'vue'
import UserPage from '@/user/UserPage'
import BackgroundPage from '@/layout/background/BackgroundPage'
import ThemeProvider from '@/utils/ThemeProvider'
import BlockSettings from '@/layout/grid/BlockSettings'
import SearchSetting from '@/layout/grid/SearchSetting'
export default defineComponent(() => {
const router = useRouterStore()
return () => (
<div class="w-0 h-full flex-grow bg-white/80 backdrop-blur">
<ThemeProvider>
<Transition>
{router.path === 'settings-user' ? (
<UserPage />
) : router.path === 'settings-background' ? (
<BackgroundPage />
) : router.path === 'settings-block' ? (
<BlockSettings />
) : null}
</Transition>
{router.path === 'settings-user' ? (
<UserPage />
) : router.path === 'settings-background' ? (
<BackgroundPage />
) : router.path === 'settings-block' ? (
<BlockSettings />
) : router.path === 'settings-search' ? (
<SearchSetting />
) : null}
</ThemeProvider>
</div>
)

View File

@ -18,6 +18,7 @@ export default defineStore(
showDate: true,
showTime: true,
showAdder: true,
showHistory: true,
// 尺寸
blockSize: 6.7,
blockPadding: 1,
@ -26,7 +27,8 @@ export default defineStore(
showBlockLabel: true,
// 搜索
searchWidth: 30,
searchRadius: 24
searchRadius: 12
})
return { state, blockInner: computed(() => state.blockSize - 2 * state.blockPadding) }
},

View File

@ -15,9 +15,18 @@ export default defineComponent({
theme={{
algorithm: props.dark ? theme.darkAlgorithm : theme.defaultAlgorithm,
token: {
colorPrimary: '#f7a94e',
colorPrimary: '#f88e14',
colorBgBase: props.dark ? '#393a41' : '#fff',
colorBorder: 'transparent'
},
components: {
Slider: {
},
Button: {
colorPrimary: '#f88e14'
}
}
}}
locale={zhCN}