完成经典即玩小游戏

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 />
<SettingsButton />
<Sider />
<LoginModal v-if="router.path !== 'global-login'"/>
<LoginModal v-if="router.path === 'global-login'"/>
<Grid v-if="layout.ready" />
<Dock />
<div class="fixed z-40 right-[14%] top-8">

View File

@ -1,6 +1,6 @@
import CategoryTab from '@/utils/CategoryTab'
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 useLayoutStore from '../useLayoutStore'
import { AddToToken } from './AdderPage'
@ -9,9 +9,9 @@ import { frontAddress, ossBase } from '@/config'
import dayjs from 'dayjs'
import { generateRandomString } from '@/utils/tool'
import MD5 from 'crypto-js/md5'
const SECRET = 'A1Cv12olxT12dOE3xA1vPA=='
const URL_ADDRESS = 'http://newfatfox.oss-cn-beijing.aliyuncs.com'
interface GameType {
export const SECRET = 'A1Cv12olxT12dOE3xA1vPA=='
export const URL_ADDRESS = 'http://newfatfox.oss-cn-beijing.aliyuncs.com'
export interface GameType {
id: string
name: string
rom: string
@ -21,7 +21,7 @@ interface GameType {
despt: string
icon: string
}
interface OtherGame {
export interface OtherGame {
id: number // 游戏ID
category_ids: number[] // 分类ID数组
rank: number // 排名
@ -33,6 +33,32 @@ interface OtherGame {
cover_url: string // 封面URL
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({
props: {
content: {
@ -100,9 +126,9 @@ export const GameItem = defineComponent({
id: uuid(),
link: !props.content.rom.startsWith('http')
? `${frontAddress}/emu/#/home?params=${JSON.stringify({
...props.content,
rom: ossBase + '/' + props.content.rom
})}`
...props.content,
rom: ossBase + '/' + props.content.rom
})}`
: props.content.rom,
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 '}>
<CategoryTab
list={[
{
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
}
]}
list={DefautGameTypeList}
selectType={selectType.value}
onUpdate:type={(e) => {
selectType.value = e

View File

@ -10,6 +10,7 @@ import useTomatoStore, { musicList } from '@/widgets/work/useTomatoStore'
import Search from '../header/search'
import { Modal, Tooltip } from 'ant-design-vue'
import { formatSeconds } from '@/utils/tool'
import clsx from 'clsx'
export const DefaultPageSetting = [
{
name: '游戏',
@ -55,7 +56,6 @@ export default defineComponent(() => {
watch(() =>
store.remainingTime
, (val) => {
console.log(val);
if (val <= 0) {
store.stopTomatoTime()
@ -123,28 +123,33 @@ export default defineComponent(() => {
}}></button>
</div>
</Modal>
{
Array.from({ length: 60 }).map((_, idx) => (
<div class={"bg-white w-[30px] h-[5px] absolute mt-[-2.5px] top-1/2"} style={{
transformOrigin: '250px',
borderRadius: '3px',
transform: `rotateZ(${idx * 6}deg)`
<div class={"w-full h-full absolute z-0 rotate-90"}>
{
Array.from({ length: 60 }).map((_, idx) => (
<div class={clsx(" w-[30px] h-[5px] absolute mt-[-2.5px] top-1/2",
(((60 * 15 - store.remainingTime) / 15) >= idx) ? "bg-white" : "bg-white/50"
)} 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 "}>
<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>
<div class={"relative"}>
<div class={"aboslute w-[370px]"}>
<div class={"aboslute w-[370px] z-10"}>
<Search isMini></Search>
</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={"返回工作模式"}>
<div
onClick={() => {

View File

@ -14,7 +14,7 @@ export default defineComponent({
const router = useRouterStore()
return () => (
<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={() => {
search.showSearchConfig = false
}}

View File

@ -11,7 +11,7 @@ export default defineComponent(() => {
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">
<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) => (
<div
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'
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 () => (
<div class="w-full h-full bg-[#ecfbff] flex flex-col">
large
<div class="w-full h-full bg-[#ecfbff] flex flex-col backdrop-blur-sm p-[8px]" style={{
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>
)
})

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 {
name: 'gameNews',
label: '游戏资讯',
description: '游戏资讯',
icon: '/tab/icons/game_news_icon.png',
modal: asyncLoader(() => import('./Modal')),
label: '经典即玩游戏',
description: '经典即玩游戏',
icon: '/tab/icons/classicPlay.png',
modal: null,
list: [
{
w: 2,
h: 1,
label: '小',
component: asyncLoader(() => import('./Small'))
},
{
w: 2,
h: 2,
label: '中',
component: asyncLoader(() => import('./Middle'))
},
{
w: 4,
h: 2,

View File

@ -9,6 +9,7 @@ import hotspot from './hotspot'
import constellation from './constellation'
import gameVideo from './gameVideo'
import work from './work'
import game from './game'
export interface Widget {
name: 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',
modal: asyncLoader(() => import('./Modal')),
list: [
{
w: 4,
h: 2,
label: '大',
component: asyncLoader(() => import('./Large'))
},
{
w: 2,
h: 1,
label: '小',
component: asyncLoader(() => import('./Small'))
},
{
w: 4,
h: 2,
label: '大',
component: asyncLoader(() => import('./Large'))
}
]
} as Widget