完成经典即玩小游戏

This commit is contained in:
expdsn 2024-11-05 17:56:40 +08:00
parent 775ba48259
commit d0ed386313
13 changed files with 229 additions and 111 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 52 KiB

View File

@ -34,7 +34,7 @@ const layout = useLayoutStore()
<SettingsOverlay /> <SettingsOverlay />
<SettingsButton /> <SettingsButton />
<Sider /> <Sider />
<LoginModal v-if="router.path !== 'global-login'"/> <LoginModal v-if="router.path === 'global-login'"/>
<Grid v-if="layout.ready" /> <Grid v-if="layout.ready" />
<Dock /> <Dock />
<div class="fixed z-40 right-[14%] top-8"> <div class="fixed z-40 right-[14%] top-8">

View File

@ -1,6 +1,6 @@
import CategoryTab from '@/utils/CategoryTab' import CategoryTab from '@/utils/CategoryTab'
import request from '@/utils/request' import request from '@/utils/request'
import { computed, defineComponent, inject, ref, watch } from 'vue' import { computed, defineComponent, inject, ref, watch } from 'vue'
import clsx from 'clsx' import clsx from 'clsx'
import useLayoutStore from '../useLayoutStore' import useLayoutStore from '../useLayoutStore'
import { AddToToken } from './AdderPage' import { AddToToken } from './AdderPage'
@ -9,9 +9,9 @@ import { frontAddress, ossBase } from '@/config'
import dayjs from 'dayjs' import dayjs from 'dayjs'
import { generateRandomString } from '@/utils/tool' import { generateRandomString } from '@/utils/tool'
import MD5 from 'crypto-js/md5' import MD5 from 'crypto-js/md5'
const SECRET = 'A1Cv12olxT12dOE3xA1vPA==' export const SECRET = 'A1Cv12olxT12dOE3xA1vPA=='
const URL_ADDRESS = 'http://newfatfox.oss-cn-beijing.aliyuncs.com' export const URL_ADDRESS = 'http://newfatfox.oss-cn-beijing.aliyuncs.com'
interface GameType { export interface GameType {
id: string id: string
name: string name: string
rom: string rom: string
@ -21,7 +21,7 @@ interface GameType {
despt: string despt: string
icon: string icon: string
} }
interface OtherGame { export interface OtherGame {
id: number // 游戏ID id: number // 游戏ID
category_ids: number[] // 分类ID数组 category_ids: number[] // 分类ID数组
rank: number // 排名 rank: number // 排名
@ -33,6 +33,32 @@ interface OtherGame {
cover_url: string // 封面URL cover_url: string // 封面URL
corner_mark: number // 角标标识 corner_mark: number // 角标标识
} }
export const DefautGameTypeList = [
{
id: 'fc',
type: '经典红白机',
attr: 0,
oridinal: 0
},
{
id: 'md',
type: '经典世嘉',
attr: 1,
oridinal: 1
},
{
id: 'yiqiyoo',
type: '休闲游戏',
attr: 1,
oridinal: 2
},
{
id: 'gba',
type: '经典GBA',
attr: 3,
oridinal: 3
}
]
export const GameItem = defineComponent({ export const GameItem = defineComponent({
props: { props: {
content: { content: {
@ -100,9 +126,9 @@ export const GameItem = defineComponent({
id: uuid(), id: uuid(),
link: !props.content.rom.startsWith('http') link: !props.content.rom.startsWith('http')
? `${frontAddress}/emu/#/home?params=${JSON.stringify({ ? `${frontAddress}/emu/#/home?params=${JSON.stringify({
...props.content, ...props.content,
rom: ossBase + '/' + props.content.rom rom: ossBase + '/' + props.content.rom
})}` })}`
: props.content.rom, : props.content.rom,
name: '', name: '',
label: props.content.name, label: props.content.name,
@ -188,32 +214,7 @@ export default defineComponent(() => {
<div class={'w-full h-full flex flex-col gap-y-4'}> <div class={'w-full h-full flex flex-col gap-y-4'}>
<div class={'w-full '}> <div class={'w-full '}>
<CategoryTab <CategoryTab
list={[ list={DefautGameTypeList}
{
id: 'fc',
type: '经典红白机',
attr: 0,
oridinal: 0
},
{
id: 'md',
type: '经典世嘉',
attr: 1,
oridinal: 1
},
{
id: 'yiqiyoo',
type: '休闲游戏',
attr: 1,
oridinal: 2
},
{
id: 'gba',
type: '经典GBA',
attr: 3,
oridinal: 3
}
]}
selectType={selectType.value} selectType={selectType.value}
onUpdate:type={(e) => { onUpdate:type={(e) => {
selectType.value = e selectType.value = e

View File

@ -10,6 +10,7 @@ import useTomatoStore, { musicList } from '@/widgets/work/useTomatoStore'
import Search from '../header/search' import Search from '../header/search'
import { Modal, Tooltip } from 'ant-design-vue' import { Modal, Tooltip } from 'ant-design-vue'
import { formatSeconds } from '@/utils/tool' import { formatSeconds } from '@/utils/tool'
import clsx from 'clsx'
export const DefaultPageSetting = [ export const DefaultPageSetting = [
{ {
name: '游戏', name: '游戏',
@ -55,7 +56,6 @@ export default defineComponent(() => {
watch(() => watch(() =>
store.remainingTime store.remainingTime
, (val) => { , (val) => {
console.log(val);
if (val <= 0) { if (val <= 0) {
store.stopTomatoTime() store.stopTomatoTime()
@ -123,28 +123,33 @@ export default defineComponent(() => {
}}></button> }}></button>
</div> </div>
</Modal> </Modal>
{ <div class={"w-full h-full absolute z-0 rotate-90"}>
Array.from({ length: 60 }).map((_, idx) => ( {
<div class={"bg-white w-[30px] h-[5px] absolute mt-[-2.5px] top-1/2"} style={{ Array.from({ length: 60 }).map((_, idx) => (
transformOrigin: '250px', <div class={clsx(" w-[30px] h-[5px] absolute mt-[-2.5px] top-1/2",
borderRadius: '3px', (((60 * 15 - store.remainingTime) / 15) >= idx) ? "bg-white" : "bg-white/50"
transform: `rotateZ(${idx * 6}deg)` )} style={{
transformOrigin: '250px',
borderRadius: '3px',
transform: `rotateZ(${idx * 6}deg)`
}}> }}>
</div>
))
}
</div>
</div>
))
}
<div class={"w-[500px] flex justify-center flex-col items-center gap-y-3 h-full text-white "}> <div class={"w-[500px] flex justify-center flex-col items-center gap-y-3 h-full text-white "}>
<span class={"text-[24px] leading-[36px]"}></span> <span class={"text-[24px] leading-[36px]"}></span>
<span class={"font-din text-[82px] font-bold leading-[115px]"}>{!store.state.isStart ? '15:00' : formatSeconds(store.remainingTime)}</span> <span class={"font-din text-[82px] font-bold leading-[115px]"}>{!store.state.isStart ? '15:00' : formatSeconds(store.remainingTime)}</span>
<div class={"relative"}> <div class={"relative"}>
<div class={"aboslute w-[370px]"}> <div class={"aboslute w-[370px] z-10"}>
<Search isMini></Search> <Search isMini></Search>
</div> </div>
</div> </div>
<div class={"w-full flex gap-x-4 justify-center mt-5"}> <div class={"w-full flex gap-x-4 justify-center z-[1] mt-5"}>
<Tooltip title={"返回工作模式"}> <Tooltip title={"返回工作模式"}>
<div <div
onClick={() => { onClick={() => {

View File

@ -14,7 +14,7 @@ export default defineComponent({
const router = useRouterStore() const router = useRouterStore()
return () => ( return () => (
<div <div
class="absolute left-0 -bottom-2 translate-y-full w-full rounded-lg bg-white/60 backdrop-blur shadow-lg p-4 flex flex-wrap gap-4" class="absolute left-0 -bottom-2 z-10 translate-y-full w-full rounded-lg bg-white/60 backdrop-blur shadow-lg p-4 flex flex-wrap gap-4"
v-outside-click={() => { v-outside-click={() => {
search.showSearchConfig = false search.showSearchConfig = false
}} }}

View File

@ -11,7 +11,7 @@ export default defineComponent(() => {
const settings = useSettingsStore() const settings = useSettingsStore()
return () => return () =>
settings.state.showHistory && ( 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"> <div class="absolute left-0 -bottom-2 translate-y-full w-full rounded-lg z-10 bg-white/60 backdrop-blur shadow-lg p-4">
{searchConfig.history.map((item, idx) => ( {searchConfig.history.map((item, idx) => (
<div <div
key={idx} key={idx}

View File

@ -1,9 +1,167 @@
import { frontAddress, ossBase } from '@/config'
import { DefautGameTypeList, SECRET, type GameType, type OtherGame } from '@/layout/adder/GameAdder'
import request from '@/utils/request'
import { generateRandomString } from '@/utils/tool'
import clsx from 'clsx'
import { MD5 } from 'crypto-js'
import dayjs from 'dayjs'
import { ref, watch } from 'vue'
import { defineComponent } from 'vue' import { defineComponent } from 'vue'
export default defineComponent(() => { export default defineComponent(() => {
const selectType = ref('fc')
const appList = ref<GameType[]>([])
const hoverId = ref(0)
const loading = ref(false)
const fetchGame = async (page: number) => {
const parems = `nonce=${generateRandomString(8)}&pid=PIDc8uT24mpo&timestamp=${dayjs().unix()}`
const sign = MD5(parems + SECRET).toString()
const response = await fetch(
`https://ge.yiqiyoo.com/game/v2/third-part/games?${parems}&sign=${sign}&paginate.limit=99&paginate.page=${page}`
)
const res = await response.json()
return res.data.items
}
watch(
selectType,
(val) => {
console.log(val);
appList.value = []
loading.value = true
if (val !== 'yiqiyoo') {
request<GameType[]>('GET', `/api/games?type=${val}`)
.then((res) => {
appList.value = res
})
.finally(() => {
loading.value = false
})
} else {
try {
Promise.all([fetchGame(1)]).then((res) => {
const resData = res.flat() as OtherGame[]
appList.value = resData.map((el) => ({
id: el.id.toString(),
name: el.name,
despt: el.short_description,
icon: el.icon,
rom: el.url,
playstation: el.url,
hot: el.rank,
category: el.category_ids[0].toString()
}))
})
} catch (err) {
console.error(err)
} finally {
loading.value = false
}
}
},
{
immediate: true
}
)
return () => ( return () => (
<div class="w-full h-full bg-[#ecfbff] flex flex-col"> <div class="w-full h-full bg-[#ecfbff] flex flex-col backdrop-blur-sm p-[8px]" style={{
large background: 'rgba(23,33,45,.6)'
}}
onClick={() => {
}}
>
<div class={"w-full h-full rounded-2xl backdrop-blur-lg px-4 flex flex-col"} style={{
background: 'rgba(23,33,46,.8)'
}}>
<div class={"w-full flex justify-between items-center"}>
<div class={"flex gap-x-2 h-[36px]"}>
{
DefautGameTypeList.map(item => (
<div
onClick={() => {
selectType.value = item.id
}}
class={clsx("flex items-center jusitfy-center relative py-4 text-[13px]",
selectType.value === item.id ? "text-[#589fff]" : " text-[#589fffcc]"
)} >
{item.type}
{
selectType.value === item.id &&
<div class={"bg-[#589fff] w-[22px] h-[4px] absolute top-0 left-1/2 -translate-x-1/2"} style={{
borderRadius: '0 0 12px 12px',
}}></div>
}
</div>
))
}
</div>
<span class={"text-[#ddd]/70 text-[14px]"}></span>
</div>
{
!loading.value &&
<div class={"flex-1 h-0 w-full overflow-y-auto scrollbar-hide"}>
<div class={"w-full flex flex-col"}>
{
appList.value.filter((_, index) => index < 10).map((item, idx) => (
<div class={"flex items-center gap-x-2 pl-[8px] py-[4px] rounded-lg"}
onMouseenter={() => {
hoverId.value = idx
}}
style={{
background: hoverId.value === idx ? "rgba(61,80,105,.8)" : "transparent"
}}>
<div class={clsx("w-[22px] h-[22px] bg-white/10 flex items-center justify-center rounded",
idx === 0 ? "text-red-500" : idx === 1 ? "text-orange-500" : idx === 2 ? "text-yellow-400" : "text-white"
)}>{idx + 1}</div>
<div class={"flex flex-1"}>
{
hoverId.value === idx ?
<div class={"w-full h-full flex justify-between pr-4 items-center"}>
<div class={"flex gap-x-3"}>
<img
class={"w-[36px] h-[36px] rounded "}
src={item.icon.startsWith('http')
? item.icon
: ossBase + '/' + item.icon}></img>
<div class={"flex flex-col"}>
<span class={"w-[150px] text-[13px] text-white text-ellipsis whitespace-nowrap overflow-hidden"}>{item.name}</span>
<span class={"w-[150px] text-[13px] text-white/50 text-ellipsis whitespace-nowrap overflow-hidden"}>{item.despt}</span>
</div>
</div>
<button
onClick={() => {
window.open(
!item.rom.startsWith('http')
? `${frontAddress}/emu/#/home?params=${JSON.stringify({
...item,
rom: ossBase + '/' + item.rom
})}`
: item.rom,
)
}}
class={"bg-[#317aff] w-[76px] h-[26px] rounded text-white text-[12px]"}></button>
</div> :
<div class={"w-full h-full flex gap-x-4"}>
<span class={"w-[80px] text-[13px] text-white text-ellipsis whitespace-nowrap overflow-hidden"}>{item.name}</span>
<span class={"w-[180px] text-[13px] text-white/50 text-ellipsis whitespace-nowrap overflow-hidden"}>{item.despt}</span>
</div>
}
</div>
</div>
))
}
</div>
</div>
}
</div>
</div> </div>
) )
}) })

View File

@ -1,10 +0,0 @@
import { defineComponent } from 'vue'
export default defineComponent(() => {
return () => (
<div class="w-full h-full bg-[#ecfbff] flex flex-col">
middle
</div>
)
})

View File

@ -1,12 +0,0 @@
import { defineComponent } from 'vue'
export default defineComponent(() => {
return () => (
<div
class="w-full h-full bg-red-50 flex "
style={{
background: 'linear-gradient(180deg,#dcefff 0%,#e7ecff 100%)'
}}
></div>
)
})

View File

@ -1,14 +0,0 @@
import { defineComponent } from 'vue'
export default defineComponent(() => {
return () => (
<div
class="w-full h-full items-center pl-3 gap-x-2 flex py-3 text-white"
style={{
background: 'linear-gradient(135deg,#5996ff 0%,#4862ff 100%)'
}}
>
</div>
)
})

View File

@ -3,23 +3,11 @@ import type { Widget } from '..'
export default { export default {
name: 'gameNews', name: 'gameNews',
label: '游戏资讯', label: '经典即玩游戏',
description: '游戏资讯', description: '经典即玩游戏',
icon: '/tab/icons/game_news_icon.png', icon: '/tab/icons/classicPlay.png',
modal: asyncLoader(() => import('./Modal')), modal: null,
list: [ list: [
{
w: 2,
h: 1,
label: '小',
component: asyncLoader(() => import('./Small'))
},
{
w: 2,
h: 2,
label: '中',
component: asyncLoader(() => import('./Middle'))
},
{ {
w: 4, w: 4,
h: 2, h: 2,

View File

@ -9,6 +9,7 @@ import hotspot from './hotspot'
import constellation from './constellation' import constellation from './constellation'
import gameVideo from './gameVideo' import gameVideo from './gameVideo'
import work from './work' import work from './work'
import game from './game'
export interface Widget { export interface Widget {
name: string // 小组件类型唯一标识 name: string // 小组件类型唯一标识
label: string // 小组件名称 label: string // 小组件名称
@ -23,4 +24,4 @@ export interface Widget {
}[] // 不同尺寸小组件块 }[] // 不同尺寸小组件块
} }
export default [calendar, weather, weApply, gameNews, eat, discount, hotspot, constellation, gameVideo, work] as Widget[] export default [game, calendar, weather, weApply, gameNews, eat, discount, hotspot, constellation, gameVideo, work] as Widget[]

View File

@ -8,17 +8,18 @@ export default {
icon: '/tab/icons/work/tomato_work_icon.png', icon: '/tab/icons/work/tomato_work_icon.png',
modal: asyncLoader(() => import('./Modal')), modal: asyncLoader(() => import('./Modal')),
list: [ list: [
{
w: 4,
h: 2,
label: '大',
component: asyncLoader(() => import('./Large'))
},
{ {
w: 2, w: 2,
h: 1, h: 1,
label: '小', label: '小',
component: asyncLoader(() => import('./Small')) component: asyncLoader(() => import('./Small'))
}, },
{
w: 4,
h: 2,
label: '大',
component: asyncLoader(() => import('./Large'))
}
] ]
} as Widget } as Widget