完成了大家都在用小组件的开发
This commit is contained in:
parent
9cd6da6b82
commit
ebb4bc8421
Binary file not shown.
After Width: | Height: | Size: 21 KiB |
Binary file not shown.
After Width: | Height: | Size: 16 KiB |
Binary file not shown.
After Width: | Height: | Size: 63 KiB |
|
@ -26,7 +26,7 @@ export const WidgetItem = defineComponent({
|
||||||
key={props.content.name}
|
key={props.content.name}
|
||||||
>
|
>
|
||||||
<div class="flex">
|
<div class="flex">
|
||||||
<img src={props.content.icon} class="w-[48px] h-[48px] bg-cover" />
|
<img src={props.content.icon} class="w-[48px] h-[48px] bg-cover rounded-lg" />
|
||||||
<div class="px-2 w-0 flex-grow">
|
<div class="px-2 w-0 flex-grow">
|
||||||
<div
|
<div
|
||||||
class={clsx('text text-sm', {
|
class={clsx('text text-sm', {
|
||||||
|
|
|
@ -26,13 +26,13 @@ export default defineComponent({
|
||||||
const hover = ref(false)
|
const hover = ref(false)
|
||||||
return () => (
|
return () => (
|
||||||
<div
|
<div
|
||||||
class="w-full h-full p-[var(--block-padding)] relative rounded-lg"
|
class="w-full h-full p-[var(--block-padding)] relative rounded-lg hover-move"
|
||||||
key={props.block.id}
|
key={props.block.id}
|
||||||
id={props.block.id}
|
id={props.block.id}
|
||||||
style={{
|
style={{
|
||||||
gridColumn: `span ${props.block.w}`,
|
gridColumn: `span ${props.block.w}`,
|
||||||
gridRow: `span ${props.block.h}`,
|
gridRow: `span ${props.block.h}`,
|
||||||
transition: 'border .3s',
|
transition: 'border .3s, transform .2s',
|
||||||
border: hover.value ? '2px solid rgba(255,255,255,.5)' : '2px solid rgba(255,255,255,0)'
|
border: hover.value ? '2px solid rgba(255,255,255,.5)' : '2px solid rgba(255,255,255,0)'
|
||||||
}}
|
}}
|
||||||
data-transportable={props.block.link && !props.block.link.startsWith('id:') ? '1' : ''}
|
data-transportable={props.block.link && !props.block.link.startsWith('id:') ? '1' : ''}
|
||||||
|
|
|
@ -32,6 +32,7 @@ export default defineComponent({
|
||||||
menu.open(props.block)
|
menu.open(props.block)
|
||||||
}}
|
}}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
|
if (!selected.modal) return
|
||||||
router.go(`widget-${props.block.name}`)
|
router.go(`widget-${props.block.name}`)
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
|
|
|
@ -165,6 +165,10 @@ body {
|
||||||
scrollbar-width: none;
|
scrollbar-width: none;
|
||||||
-ms-overflow-style: none;
|
-ms-overflow-style: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.hover-move:hover {
|
||||||
|
transform: translateY(-4px);
|
||||||
|
}
|
||||||
@layer utilities {
|
@layer utilities {
|
||||||
.scrollbar-transparent::-webkit-scrollbar {
|
.scrollbar-transparent::-webkit-scrollbar {
|
||||||
width: 0;
|
width: 0;
|
||||||
|
|
|
@ -1,12 +1,13 @@
|
||||||
import type { Component } from 'vue'
|
import type { Component } from 'vue'
|
||||||
import calendar from './calendar'
|
import calendar from './calendar'
|
||||||
import weather from './weather'
|
import weather from './weather'
|
||||||
|
import weApply from './weApply'
|
||||||
export interface Widget {
|
export interface Widget {
|
||||||
name: string // 小组件类型唯一标识
|
name: string // 小组件类型唯一标识
|
||||||
label: string // 小组件名称
|
label: string // 小组件名称
|
||||||
description: string // 小组件描述
|
description: string // 小组件描述
|
||||||
icon: string // 小组件图标
|
icon: string // 小组件图标
|
||||||
modal: Component // 弹框组件
|
modal: Component | null // 弹框组件
|
||||||
list: {
|
list: {
|
||||||
w: number
|
w: number
|
||||||
h: number
|
h: number
|
||||||
|
@ -15,4 +16,4 @@ export interface Widget {
|
||||||
}[] // 不同尺寸小组件块
|
}[] // 不同尺寸小组件块
|
||||||
}
|
}
|
||||||
|
|
||||||
export default [calendar, weather] as Widget[]
|
export default [calendar, weather, weApply] as Widget[]
|
||||||
|
|
|
@ -0,0 +1,171 @@
|
||||||
|
import { defineComponent, ref, watch, type VNodeRef } from 'vue'
|
||||||
|
import { useWeApplyStore } from './useWeApplyStore'
|
||||||
|
import { addIcons, OhVueIcon } from 'oh-vue-icons'
|
||||||
|
import { HiChevronDown } from 'oh-vue-icons/icons'
|
||||||
|
import clsx from 'clsx'
|
||||||
|
addIcons(HiChevronDown)
|
||||||
|
export default defineComponent(() => {
|
||||||
|
const store = useWeApplyStore()
|
||||||
|
const gameRef = ref<VNodeRef | null>(null)
|
||||||
|
const isGameBottom = ref(false)
|
||||||
|
const isWorkBottom = ref(false)
|
||||||
|
const workRef = ref<VNodeRef | null>(null)
|
||||||
|
const computIsBottom = (ref: any) => {
|
||||||
|
if (ref) {
|
||||||
|
const { scrollTop, clientHeight, scrollHeight } = ref
|
||||||
|
return scrollTop + clientHeight >= scrollHeight
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const handleGameScroll = () => {
|
||||||
|
const isAtBottom = computIsBottom(gameRef.value)
|
||||||
|
console.log(isAtBottom ? '已滚动到底部!' : '未滚动到底部。')
|
||||||
|
if (isAtBottom) {
|
||||||
|
isGameBottom.value = true
|
||||||
|
} else {
|
||||||
|
isGameBottom.value = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const handleWorkScroll = () => {
|
||||||
|
const isAtBottom = computIsBottom(workRef.value)
|
||||||
|
console.log(isAtBottom ? '已滚动到底部!' : '未滚动到底部。')
|
||||||
|
if (isAtBottom) {
|
||||||
|
isWorkBottom.value = true
|
||||||
|
} else {
|
||||||
|
isWorkBottom.value = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
watch(gameRef, (val, _, onCleanup) => {
|
||||||
|
console.log(val)
|
||||||
|
if (!val) return
|
||||||
|
val.addEventListener('scroll', handleGameScroll)
|
||||||
|
// if (val + gameRef.value.clientHeight >= gameRef.value.scrollHeight) {
|
||||||
|
// isGameBottom.value = true
|
||||||
|
// } else {
|
||||||
|
// isGameBottom.value = false
|
||||||
|
// }
|
||||||
|
onCleanup(() => {
|
||||||
|
val.removeEventListener('scroll', handleGameScroll)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
watch(workRef, (val, _, onCleanup) => {
|
||||||
|
console.log(val)
|
||||||
|
if (!val) return
|
||||||
|
|
||||||
|
val.addEventListener('scroll', handleWorkScroll)
|
||||||
|
// if (val + gameRef.value.clientHeight >= gameRef.value.scrollHeight) {
|
||||||
|
// isGameBottom.value = true
|
||||||
|
// } else {
|
||||||
|
// isGameBottom.value = false
|
||||||
|
// }
|
||||||
|
onCleanup(() => {
|
||||||
|
val.removeEventListener('scroll', handleWorkScroll)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
return () => (
|
||||||
|
<div
|
||||||
|
class={'w-full h-full flex p-2 justify-between'}
|
||||||
|
style={{
|
||||||
|
background: 'rgba(23,33,45,.6)'
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class={'w-[49.5%] h-full rounded-xl p-2 relative overflow-hidden'}
|
||||||
|
style={{
|
||||||
|
background: 'rgba(23,33,46,.8)'
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{!isGameBottom.value && (
|
||||||
|
<div
|
||||||
|
class={
|
||||||
|
'w-full absolute bottom-0 left-0 flex justify-center bg-[#17212e] h-[20px] items-center'
|
||||||
|
}
|
||||||
|
onClick={(e) => {
|
||||||
|
e.stopPropagation()
|
||||||
|
e.preventDefault()
|
||||||
|
if (gameRef.value) {
|
||||||
|
gameRef.value.scrollTop += 40
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<OhVueIcon name={HiChevronDown.name} fill="#ddd"></OhVueIcon>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
<div
|
||||||
|
ref={gameRef}
|
||||||
|
class={clsx('w-full h-full flex flex-col overflow-y-scroll gap-y-2 scrollbar-hide ')}
|
||||||
|
onWheel={() => {
|
||||||
|
console.log('wheel')
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{store.state.list
|
||||||
|
.filter((val) => val.type === 'game')
|
||||||
|
.map((item) => (
|
||||||
|
<div class={'flex gap-x-2 items-center'}>
|
||||||
|
<img src={item.icon} alt="game icon" class={'w-[37px] h-[37px] rounded'}></img>
|
||||||
|
<div class={'flex-1 flex flex-col overflow-hidden'}>
|
||||||
|
<span class={'text-white text-[14px]'}>{item.name}</span>
|
||||||
|
<span
|
||||||
|
class={
|
||||||
|
'text-[#fff9] text-[12px] whitespace-nowrap text-ellipsis overflow-hidden'
|
||||||
|
}
|
||||||
|
>
|
||||||
|
{item.des}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
class={'w-[49.5%] h-full rounded-xl p-2 relative overflow-hidden'}
|
||||||
|
style={{
|
||||||
|
background: 'rgba(23,33,46,.8)'
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{!isWorkBottom.value && (
|
||||||
|
<div
|
||||||
|
class={
|
||||||
|
'w-full absolute bottom-0 left-0 flex justify-center bg-[#17212e] h-[20px] items-center'
|
||||||
|
}
|
||||||
|
onClick={(e) => {
|
||||||
|
e.stopPropagation()
|
||||||
|
e.preventDefault()
|
||||||
|
if (workRef.value) {
|
||||||
|
workRef.value.scrollTop += 20
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<OhVueIcon name={HiChevronDown.name} fill="#ddd"></OhVueIcon>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
<div
|
||||||
|
ref={workRef}
|
||||||
|
class={clsx('w-full h-full flex flex-col overflow-y-scroll gap-y-2 scrollbar-hide ')}
|
||||||
|
onWheel={() => {
|
||||||
|
console.log('wheel')
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{store.state.list
|
||||||
|
.filter((val) => val.type === 'work')
|
||||||
|
.map((item) => (
|
||||||
|
<div class={'flex gap-x-2 items-center'}>
|
||||||
|
<img src={item.icon} alt="game icon" class={'w-[37px] h-[37px] rounded'}></img>
|
||||||
|
<div class={'flex-1 flex flex-col overflow-hidden'}>
|
||||||
|
<span class={'text-white text-[14px]'}>{item.name}</span>
|
||||||
|
<span
|
||||||
|
class={
|
||||||
|
'text-[#fff9] text-[12px] whitespace-nowrap text-ellipsis overflow-hidden'
|
||||||
|
}
|
||||||
|
>
|
||||||
|
{item.des}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
})
|
|
@ -0,0 +1,18 @@
|
||||||
|
import asyncLoader from '@/utils/asyncLoader'
|
||||||
|
import type { Widget } from '..'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'weApply',
|
||||||
|
label: '大家都在用',
|
||||||
|
description: '大家都在用',
|
||||||
|
icon: '/icons/recommendedIcon.png',
|
||||||
|
modal: null,
|
||||||
|
list: [
|
||||||
|
{
|
||||||
|
w: 4,
|
||||||
|
h: 2,
|
||||||
|
label: '大',
|
||||||
|
component: asyncLoader(() => import('./Large'))
|
||||||
|
}
|
||||||
|
]
|
||||||
|
} as Widget
|
|
@ -0,0 +1,29 @@
|
||||||
|
import request from "@/utils/request"
|
||||||
|
import { defineStore } from "pinia"
|
||||||
|
import { reactive } from "vue"
|
||||||
|
type WeApplyType = {
|
||||||
|
id: string;
|
||||||
|
name: string;
|
||||||
|
url: string;
|
||||||
|
icon: string;
|
||||||
|
des: string;
|
||||||
|
type: string;
|
||||||
|
}
|
||||||
|
export const useWeApplyStore = defineStore('weApply', () => {
|
||||||
|
const state = reactive({
|
||||||
|
list: [] as WeApplyType[]
|
||||||
|
})
|
||||||
|
const getWeApplyList = async () => {
|
||||||
|
return request<WeApplyType[]>('GET', '/api/app/weApplys').then(res => {
|
||||||
|
return res
|
||||||
|
})
|
||||||
|
}
|
||||||
|
const getList = async () => {
|
||||||
|
state.list = await getWeApplyList()
|
||||||
|
}
|
||||||
|
getList()
|
||||||
|
return {
|
||||||
|
state,
|
||||||
|
getList
|
||||||
|
}
|
||||||
|
})
|
Loading…
Reference in New Issue