Merge remote-tracking branch 'origin/tomato'
This commit is contained in:
commit
ddb834f0f7
Binary file not shown.
After Width: | Height: | Size: 16 KiB |
Binary file not shown.
After Width: | Height: | Size: 765 B |
Binary file not shown.
After Width: | Height: | Size: 381 B |
90
src/App.vue
90
src/App.vue
|
@ -17,6 +17,8 @@ import WelcomePage from './layout/grid/WelcomePage'
|
||||||
import TomatoPage from './layout/grid/TomatoPage'
|
import TomatoPage from './layout/grid/TomatoPage'
|
||||||
import useRouterStore from './useRouterStore'
|
import useRouterStore from './useRouterStore'
|
||||||
import BackupRecovery from './user/BackupRecovery'
|
import BackupRecovery from './user/BackupRecovery'
|
||||||
|
import { ConfigProvider } from 'ant-design-vue'
|
||||||
|
import zhCN from 'ant-design-vue/es/locale/zh_CN'
|
||||||
const Grid = asyncLoader(() => import('./layout/grid'))
|
const Grid = asyncLoader(() => import('./layout/grid'))
|
||||||
const Fox = asyncLoader(() => import('./fox'))
|
const Fox = asyncLoader(() => import('./fox'))
|
||||||
const settings = useSettingsStore()
|
const settings = useSettingsStore()
|
||||||
|
@ -28,51 +30,53 @@ const router = useRouterStore()
|
||||||
const layout = useLayoutStore()
|
const layout = useLayoutStore()
|
||||||
</script>
|
</script>
|
||||||
<template>
|
<template>
|
||||||
<div class="fixed left-0 top-0 w-full h-screen style-root" @contextmenu.prevent>
|
<ConfigProvider :locale="zhCN">
|
||||||
<Header />
|
<div class="fixed left-0 top-0 w-full h-screen style-root" @contextmenu.prevent>
|
||||||
<Background
|
<Header />
|
||||||
@dblclick="
|
<Background
|
||||||
() => {
|
@dblclick="
|
||||||
layout.state.simple = !layout.state.simple
|
() => {
|
||||||
}
|
layout.state.simple = !layout.state.simple
|
||||||
"
|
}
|
||||||
/>
|
"
|
||||||
<GLobalModal />
|
/>
|
||||||
<SettingsOverlay />
|
<GLobalModal />
|
||||||
<SettingsButton />
|
<SettingsOverlay />
|
||||||
|
<SettingsButton />
|
||||||
|
|
||||||
<Sider
|
<Sider
|
||||||
v-if="
|
v-if="
|
||||||
!layout.state.simple ||
|
!layout.state.simple ||
|
||||||
(layout.state.simple && settings.state.simpleModeShowString.includes('showSider'))
|
(layout.state.simple && settings.state.simpleModeShowString.includes('showSider'))
|
||||||
"
|
"
|
||||||
/>
|
/>
|
||||||
<LoginModal v-if="router.path === 'global-login'" />
|
<LoginModal v-if="router.path === 'global-login'" />
|
||||||
<Transition>
|
<Transition>
|
||||||
<Grid v-if="layout.ready && !layout.state.simple" />
|
<Grid v-if="layout.ready && !layout.state.simple" />
|
||||||
</Transition>
|
</Transition>
|
||||||
<Dock
|
<Dock
|
||||||
v-if="
|
v-if="
|
||||||
!layout.state.simple ||
|
!layout.state.simple ||
|
||||||
(layout.state.simple && settings.state.simpleModeShowString.includes('showDock'))
|
(layout.state.simple && settings.state.simpleModeShowString.includes('showDock'))
|
||||||
"
|
"
|
||||||
/>
|
/>
|
||||||
<div
|
<div
|
||||||
class="fixed z-40 right-[14%] top-8"
|
class="fixed z-40 right-[14%] top-8"
|
||||||
v-if="
|
v-if="
|
||||||
!layout.state.simple ||
|
!layout.state.simple ||
|
||||||
(layout.state.simple && settings.state.simpleModeShowString.includes('showPet'))
|
(layout.state.simple && settings.state.simpleModeShowString.includes('showPet'))
|
||||||
"
|
"
|
||||||
>
|
>
|
||||||
<Fox />
|
<Fox />
|
||||||
|
</div>
|
||||||
|
<DirModal />
|
||||||
|
<GlobalMenu />
|
||||||
|
<WelcomePage></WelcomePage>
|
||||||
|
<TomatoPage></TomatoPage>
|
||||||
|
|
||||||
|
<BackupRecovery v-if="router.path === 'global-backup'"></BackupRecovery>
|
||||||
</div>
|
</div>
|
||||||
<DirModal />
|
</ConfigProvider>
|
||||||
<GlobalMenu />
|
|
||||||
<WelcomePage></WelcomePage>
|
|
||||||
<TomatoPage></TomatoPage>
|
|
||||||
|
|
||||||
<BackupRecovery v-if="router.path === 'global-backup'"></BackupRecovery>
|
|
||||||
</div>
|
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<style lang="less">
|
<style lang="less">
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import useRouterStore, { type RouteStr } from '@/useRouterStore'
|
import useRouterStore, { type RouteStr } from '@/useRouterStore'
|
||||||
import { computed, defineComponent, ref, Transition, watch } from 'vue'
|
import { computed, defineComponent, onMounted, onUnmounted, ref, Transition, watch } from 'vue'
|
||||||
import { OhVueIcon, addIcons } from 'oh-vue-icons'
|
import { OhVueIcon, addIcons } from 'oh-vue-icons'
|
||||||
import { MdClose, MdOpeninfull, MdClosefullscreen } from 'oh-vue-icons/icons'
|
import { MdClose, MdOpeninfull, MdClosefullscreen } from 'oh-vue-icons/icons'
|
||||||
import asyncLoader from './utils/asyncLoader'
|
import asyncLoader from './utils/asyncLoader'
|
||||||
|
@ -18,18 +18,31 @@ export default defineComponent(() => {
|
||||||
router.path.startsWith('widget-') ||
|
router.path.startsWith('widget-') ||
|
||||||
router.path === 'global-search' ||
|
router.path === 'global-search' ||
|
||||||
router.path === 'global-adder' ||
|
router.path === 'global-adder' ||
|
||||||
router.path === 'global-background'
|
router.path === 'global-background'
|
||||||
)
|
)
|
||||||
const full = ref(false)
|
const full = ref(false)
|
||||||
watch(router, () => {
|
watch(router, () => {
|
||||||
full.value = false
|
full.value = false
|
||||||
})
|
})
|
||||||
|
onMounted(() => {
|
||||||
|
window.addEventListener('keydown', handleKeydown)
|
||||||
|
})
|
||||||
|
onUnmounted(() => {
|
||||||
|
// 清理事件监听
|
||||||
|
window.removeEventListener('keydown', handleKeydown)
|
||||||
|
})
|
||||||
|
function handleKeydown(e: any) {
|
||||||
|
if (e.key === 'Escape') {
|
||||||
|
router.back()
|
||||||
|
}
|
||||||
|
}
|
||||||
return () => (
|
return () => (
|
||||||
<div
|
<div
|
||||||
class="fixed left-0 top-0 z-50 w-full"
|
class="fixed left-0 top-0 z-50 w-full"
|
||||||
onContextmenu={(e) => e.stopPropagation()}
|
onContextmenu={(e) => e.stopPropagation()}
|
||||||
onKeydown={(e) => e.stopPropagation()}
|
onKeydown={(e) => {
|
||||||
|
e.stopPropagation()
|
||||||
|
}}
|
||||||
>
|
>
|
||||||
{/* 背景遮罩 */}
|
{/* 背景遮罩 */}
|
||||||
<Transition>
|
<Transition>
|
||||||
|
@ -43,7 +56,7 @@ export default defineComponent(() => {
|
||||||
)}
|
)}
|
||||||
</Transition>
|
</Transition>
|
||||||
{/* 弹框主体 */}
|
{/* 弹框主体 */}
|
||||||
<Transition >
|
<Transition>
|
||||||
{show.value && (
|
{show.value && (
|
||||||
<div
|
<div
|
||||||
class="absolute left-1/2 top-1/2 -translate-x-1/2 -translate-y-1/2 overflow-hidden "
|
class="absolute left-1/2 top-1/2 -translate-x-1/2 -translate-y-1/2 overflow-hidden "
|
||||||
|
@ -93,21 +106,20 @@ export default defineComponent(() => {
|
||||||
<AdderPage />
|
<AdderPage />
|
||||||
) : router.path === 'global-background' ? (
|
) : router.path === 'global-background' ? (
|
||||||
<BackgroundSwtich />
|
<BackgroundSwtich />
|
||||||
) :
|
) : router.path.startsWith('widget-') ? (
|
||||||
router.path.startsWith('widget-') ? (
|
(() => {
|
||||||
(() => {
|
const name = router.path.split('-')[1]
|
||||||
const name = router.path.split('-')[1]
|
const selected = widgetList.find((el) => el.name === name)
|
||||||
const selected = widgetList.find((el) => el.name === name)
|
if (!selected)
|
||||||
if (!selected)
|
return (
|
||||||
return (
|
<div class="w-full h-full flex justify-center items-center text-black/80">
|
||||||
<div class="w-full h-full flex justify-center items-center text-black/80">
|
组件维护中
|
||||||
组件维护中
|
</div>
|
||||||
</div>
|
)
|
||||||
)
|
const compo = selected.modal
|
||||||
const compo = selected.modal
|
return <compo />
|
||||||
return <compo />
|
})()
|
||||||
})()
|
) : null}
|
||||||
) : null}
|
|
||||||
</Transition>
|
</Transition>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import { defineStore } from 'pinia'
|
import { defineStore } from 'pinia'
|
||||||
import { computed, defineComponent, onUnmounted, reactive, ref } from 'vue'
|
import { computed, defineComponent, onUnmounted, reactive, ref } from 'vue'
|
||||||
import type { Block, LayoutPages } from './layout.types'
|
import type { Block } from './layout.types'
|
||||||
import clsx from 'clsx'
|
import clsx from 'clsx'
|
||||||
import useLayoutStore from './useLayoutStore'
|
import useLayoutStore from './useLayoutStore'
|
||||||
import widgetList from '@/widgets'
|
import widgetList from '@/widgets'
|
||||||
|
@ -17,10 +17,11 @@ const defaultDisplay = {
|
||||||
export const useMenuStore = defineStore('menu', () => {
|
export const useMenuStore = defineStore('menu', () => {
|
||||||
const display = reactive(defaultDisplay)
|
const display = reactive(defaultDisplay)
|
||||||
const selectPage = ref<{
|
const selectPage = ref<{
|
||||||
id: string,
|
id: string
|
||||||
label: string,
|
label: string
|
||||||
name: string,
|
name: string
|
||||||
}>()
|
}>()
|
||||||
|
const isEditPage = ref(false)
|
||||||
const showEditPage = ref(false)
|
const showEditPage = ref(false)
|
||||||
const mPos = {
|
const mPos = {
|
||||||
x: 0,
|
x: 0,
|
||||||
|
@ -49,6 +50,7 @@ export const useMenuStore = defineStore('menu', () => {
|
||||||
display,
|
display,
|
||||||
open,
|
open,
|
||||||
dismiss,
|
dismiss,
|
||||||
|
isEditPage,
|
||||||
show,
|
show,
|
||||||
selectPage,
|
selectPage,
|
||||||
showEditPage
|
showEditPage
|
||||||
|
@ -83,8 +85,8 @@ const Item = defineComponent({
|
||||||
style={
|
style={
|
||||||
props.noStyle
|
props.noStyle
|
||||||
? {
|
? {
|
||||||
padding: '6px 10px'
|
padding: '6px 10px'
|
||||||
}
|
}
|
||||||
: {}
|
: {}
|
||||||
}
|
}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
|
@ -155,6 +157,15 @@ export default defineComponent(() => {
|
||||||
>
|
>
|
||||||
添加快游戏
|
添加快游戏
|
||||||
</Item>
|
</Item>
|
||||||
|
<Item
|
||||||
|
noStyle
|
||||||
|
onClick={() => {
|
||||||
|
menu.isEditPage = true
|
||||||
|
menu.dismiss()
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
编辑主页
|
||||||
|
</Item>
|
||||||
<Item
|
<Item
|
||||||
noStyle
|
noStyle
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
|
@ -164,6 +175,15 @@ export default defineComponent(() => {
|
||||||
>
|
>
|
||||||
更换壁纸
|
更换壁纸
|
||||||
</Item>
|
</Item>
|
||||||
|
<Item
|
||||||
|
noStyle
|
||||||
|
onClick={() => {
|
||||||
|
window.location.reload()
|
||||||
|
menu.dismiss()
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
刷新
|
||||||
|
</Item>
|
||||||
</>
|
</>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -207,10 +227,18 @@ export default defineComponent(() => {
|
||||||
alert
|
alert
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
// 删除链接
|
// 删除链接
|
||||||
const idx = layout.currentPage.list.findIndex((el) => el.id === block.id)
|
console.log(menu.selectPage)
|
||||||
if (idx < 0) return
|
|
||||||
layout.currentPage.list.splice(idx, 1)
|
const idx = layout.state.content[layout.state.current].pages.findIndex(
|
||||||
|
(el) => el.id === menu.selectPage?.id
|
||||||
|
)
|
||||||
menu.dismiss()
|
menu.dismiss()
|
||||||
|
|
||||||
|
if (idx < 0) return
|
||||||
|
if (idx === layout.state.currentPage) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
layout.state.content[layout.state.current].pages.splice(idx, 1)
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
删除
|
删除
|
||||||
|
@ -309,7 +337,25 @@ export default defineComponent(() => {
|
||||||
// 链接
|
// 链接
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Item>编辑</Item>
|
<Item
|
||||||
|
onClick={() => {
|
||||||
|
useAdderPageStore().editBlockItem = {
|
||||||
|
id: block.id,
|
||||||
|
name: block.name,
|
||||||
|
link: block.link,
|
||||||
|
icon: block.icon,
|
||||||
|
text: block.text,
|
||||||
|
background: block.background,
|
||||||
|
color: block.color,
|
||||||
|
type: block.color ? 1 : 0
|
||||||
|
}
|
||||||
|
router.go('global-adder')
|
||||||
|
useAdderPageStore().type = 2
|
||||||
|
menu.dismiss()
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
编辑
|
||||||
|
</Item>
|
||||||
<Item
|
<Item
|
||||||
alert
|
alert
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
|
|
|
@ -81,10 +81,11 @@ export default defineComponent(() => {
|
||||||
const isGame = computed(() => layout.state.current === 0)
|
const isGame = computed(() => layout.state.current === 0)
|
||||||
const store = useAdderPageStore()
|
const store = useAdderPageStore()
|
||||||
const addTo = ref(layout.state.currentPage)
|
const addTo = ref(layout.state.currentPage)
|
||||||
|
|
||||||
provide(AddToToken, addTo)
|
provide(AddToToken, addTo)
|
||||||
onUnmounted(() => {
|
// onUnmounted(() => {
|
||||||
store.type = 1
|
// store.type = 1
|
||||||
})
|
// })
|
||||||
return () => (
|
return () => (
|
||||||
<div
|
<div
|
||||||
class={clsx(
|
class={clsx(
|
||||||
|
@ -144,9 +145,31 @@ export default defineComponent(() => {
|
||||||
}))}
|
}))}
|
||||||
/>
|
/>
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
<Form.Item>
|
{store.type !== 2 && (
|
||||||
<Input.Search class="w-[200px]" placeholder="搜索组件或网站" />
|
<Form.Item>
|
||||||
</Form.Item>
|
<Input.Search
|
||||||
|
onSearch={(e) => {
|
||||||
|
if (store.type === 1) {
|
||||||
|
store.search(e)
|
||||||
|
} else if (store.type === 0) {
|
||||||
|
store.widgetSearchWords = e
|
||||||
|
} else if (store.type === 3) {
|
||||||
|
store.gameSearch = e
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
class="w-[220px]"
|
||||||
|
placeholder={
|
||||||
|
store.type === 0
|
||||||
|
? '搜索想要添加的组件'
|
||||||
|
: store.type === 1
|
||||||
|
? '搜索想要添加的网址导航'
|
||||||
|
: store.type === 2
|
||||||
|
? ''
|
||||||
|
: '搜索想要添加的游戏'
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</Form.Item>
|
||||||
|
)}
|
||||||
</Form>
|
</Form>
|
||||||
<div class="w-full h-0 flex-grow p-6">
|
<div class="w-full h-0 flex-grow p-6">
|
||||||
<div class="w-full h-full relative">
|
<div class="w-full h-full relative">
|
||||||
|
|
|
@ -1,18 +1,18 @@
|
||||||
import { computed, defineComponent, inject, reactive, ref, watch } from 'vue'
|
import { computed, defineComponent, inject, onMounted, reactive, ref, watch } from 'vue'
|
||||||
import useLayoutStore from '../useLayoutStore'
|
import useLayoutStore from '../useLayoutStore'
|
||||||
import { Button, Form, Input, InputGroup } from 'ant-design-vue'
|
import { Form, Input } from 'ant-design-vue'
|
||||||
import { OhVueIcon, addIcons } from 'oh-vue-icons'
|
import { OhVueIcon, addIcons } from 'oh-vue-icons'
|
||||||
import { MdUpload, MdImage, MdCheck } from 'oh-vue-icons/icons'
|
import { MdUpload, MdImage, MdCheck } from 'oh-vue-icons/icons'
|
||||||
import useLink from '../../utils/useLink'
|
import useLink from '../../utils/useLink'
|
||||||
import { CheckOutlined } from '@ant-design/icons-vue'
|
|
||||||
import { v4 as uuid } from 'uuid'
|
import { v4 as uuid } from 'uuid'
|
||||||
import type { Block } from '../layout.types'
|
import type { Block } from '../layout.types'
|
||||||
import { ColorPicker } from 'vue3-colorpicker'
|
|
||||||
import 'vue3-colorpicker/style.css'
|
import 'vue3-colorpicker/style.css'
|
||||||
import { globalToast } from '@/main'
|
import { globalToast } from '@/main'
|
||||||
import UploadAndCut from '@/utils/UploadAndCut'
|
import UploadAndCut from '@/utils/UploadAndCut'
|
||||||
import { AddToToken } from './AdderPage'
|
import { AddToToken } from './AdderPage'
|
||||||
|
import useAdderPageStore, { type EditBlockItemType } from './useAdderPageStore'
|
||||||
|
import useRouterStore from '@/useRouterStore'
|
||||||
|
import NativeColorPicker from '@/utils/NativeColorPicker'
|
||||||
addIcons(MdUpload, MdImage, MdCheck)
|
addIcons(MdUpload, MdImage, MdCheck)
|
||||||
|
|
||||||
const TypeSelector = defineComponent({
|
const TypeSelector = defineComponent({
|
||||||
|
@ -47,6 +47,7 @@ const TypeSelector = defineComponent({
|
||||||
'update:icon': (() => true) as (val: string) => boolean
|
'update:icon': (() => true) as (val: string) => boolean
|
||||||
},
|
},
|
||||||
setup(props, ctx) {
|
setup(props, ctx) {
|
||||||
|
const layout = useLayoutStore()
|
||||||
return () => (
|
return () => (
|
||||||
<div class="flex gap-4">
|
<div class="flex gap-4">
|
||||||
<div class={'flex justify-center flex-col items-center gap-y-1'}>
|
<div class={'flex justify-center flex-col items-center gap-y-1'}>
|
||||||
|
@ -60,7 +61,7 @@ const TypeSelector = defineComponent({
|
||||||
ctx.emit('update:value', 0)
|
ctx.emit('update:value', 0)
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{!props.icon && <OhVueIcon name="md-image" fill="white" scale={1.8} />}
|
{!props.icon && <img src={'/tab/icons/bgGameCloud.png'}></img>}
|
||||||
{props.value === 0 && (
|
{props.value === 0 && (
|
||||||
<div
|
<div
|
||||||
class={
|
class={
|
||||||
|
@ -113,10 +114,11 @@ const TypeSelector = defineComponent({
|
||||||
ctx.emit('update:icon', e)
|
ctx.emit('update:icon', e)
|
||||||
}}
|
}}
|
||||||
></ImageUploader> */}
|
></ImageUploader> */}
|
||||||
<UploadAndCut onUpdate:value={(e)=> {
|
<UploadAndCut
|
||||||
|
onUpdate:value={(e) => {
|
||||||
ctx.emit('update:icon', e)
|
ctx.emit('update:icon', e)
|
||||||
|
}}
|
||||||
}}></UploadAndCut>
|
></UploadAndCut>
|
||||||
</div>
|
</div>
|
||||||
<span class={'text-[12px]'}>自定义</span>
|
<span class={'text-[12px]'}>自定义</span>
|
||||||
</div>
|
</div>
|
||||||
|
@ -134,9 +136,21 @@ export default defineComponent(() => {
|
||||||
color: 'rgb(255,255,255)',
|
color: 'rgb(255,255,255)',
|
||||||
icon: '',
|
icon: '',
|
||||||
type: 0 // 0 默认,1 文字
|
type: 0 // 0 默认,1 文字
|
||||||
})
|
} as EditBlockItemType)
|
||||||
const isGame = computed(() => layout.state.current === 0)
|
const isGame = computed(() => layout.state.current === 0)
|
||||||
const debounced = ref('')
|
const debounced = ref('')
|
||||||
|
const store = useAdderPageStore()
|
||||||
|
onMounted(() => {
|
||||||
|
if (store.editBlockItem !== null) {
|
||||||
|
form.link = store.editBlockItem.link
|
||||||
|
form.name = store.editBlockItem.name
|
||||||
|
form.icon = store.editBlockItem.icon
|
||||||
|
form.text = store.editBlockItem.text
|
||||||
|
form.background = store.editBlockItem.background
|
||||||
|
form.color = store.editBlockItem.color
|
||||||
|
form.type = store.editBlockItem.type
|
||||||
|
}
|
||||||
|
})
|
||||||
watch(
|
watch(
|
||||||
() => form.link,
|
() => form.link,
|
||||||
(val, _, onCleanup) => {
|
(val, _, onCleanup) => {
|
||||||
|
@ -155,6 +169,20 @@ export default defineComponent(() => {
|
||||||
if (val.name) form.name = val.name
|
if (val.name) form.name = val.name
|
||||||
if (val.icon) form.icon = val.icon
|
if (val.icon) form.icon = val.icon
|
||||||
})
|
})
|
||||||
|
watch(
|
||||||
|
() => form.type,
|
||||||
|
(cur, pre) => {
|
||||||
|
if (cur === 1) {
|
||||||
|
if (!form.name) {
|
||||||
|
globalToast.error('文字图标请至少填写文字或者名称')
|
||||||
|
form.type = pre
|
||||||
|
return
|
||||||
|
} else {
|
||||||
|
form.text = form.name.substring(0, 2).toLocaleUpperCase()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
const addTo = inject(AddToToken)
|
const addTo = inject(AddToToken)
|
||||||
return () => (
|
return () => (
|
||||||
<div
|
<div
|
||||||
|
@ -163,19 +191,24 @@ export default defineComponent(() => {
|
||||||
(isGame.value ? 'bg-white/20' : 'bg-white/70')
|
(isGame.value ? 'bg-white/20' : 'bg-white/70')
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
<Form labelCol={{ span: 4 }} labelAlign="left">
|
<Form>
|
||||||
<Form.Item label="地址">
|
<Form.Item label="地址" class={'relative'}>
|
||||||
<InputGroup compact style="display:flex">
|
<Input
|
||||||
<Input
|
v-model:value={form.link}
|
||||||
v-model:value={form.link}
|
placeholder="搜索想要添加的网址导航"
|
||||||
placeholder="搜索想要添加的网址导航"
|
class={isGame?.value ? '' : ' bg-black/10 '}
|
||||||
class="w-0 flex-grow"
|
/>
|
||||||
/>
|
<span
|
||||||
<Button>获取地址</Button>
|
class={'absolute right-[-70px] top-1/2 -translate-y-1/2 cursor-pointer'}
|
||||||
</InputGroup>
|
onClick={() => {
|
||||||
|
debounced.value = debounced.value + ' '
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
获取地址
|
||||||
|
</span>
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
<Form.Item label="名称">
|
<Form.Item label="名称">
|
||||||
<Input v-model:value={form.name} />
|
<Input v-model:value={form.name} class={isGame?.value ? '' : ' bg-black/10 '} />
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
<Form.Item label="图标">
|
<Form.Item label="图标">
|
||||||
<TypeSelector
|
<TypeSelector
|
||||||
|
@ -189,70 +222,81 @@ export default defineComponent(() => {
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
{form.type === 1 && (
|
{form.type === 1 && (
|
||||||
<>
|
<>
|
||||||
<div class="flex">
|
<Form.Item label="图标背景">
|
||||||
<Form.Item
|
<NativeColorPicker
|
||||||
label="文字颜色"
|
size={30}
|
||||||
class="w-0 flex-grow"
|
colorList={[
|
||||||
labelCol={{
|
'rgb(227, 127, 53)',
|
||||||
span: 8
|
'rgb(239, 195, 57)',
|
||||||
|
'rgb(65, 201, 117)',
|
||||||
|
'rgb(67, 195, 195)',
|
||||||
|
'rgb(97, 182, 255)',
|
||||||
|
'rgb(153, 91, 179)'
|
||||||
|
]}
|
||||||
|
value={form.background}
|
||||||
|
onUpdate:value={(e) => {
|
||||||
|
form.background = e
|
||||||
}}
|
}}
|
||||||
>
|
></NativeColorPicker>
|
||||||
<ColorPicker
|
</Form.Item>
|
||||||
class="shadow-lg"
|
|
||||||
format="rgb"
|
|
||||||
shape="square"
|
|
||||||
v-model:pureColor={form.color}
|
|
||||||
/>
|
|
||||||
</Form.Item>
|
|
||||||
<Form.Item
|
|
||||||
label="图标背景"
|
|
||||||
class="w-0 flex-grow"
|
|
||||||
labelCol={{
|
|
||||||
span: 8
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<ColorPicker
|
|
||||||
class="shadow-lg"
|
|
||||||
format="rgb"
|
|
||||||
shape="square"
|
|
||||||
v-model:pureColor={form.background}
|
|
||||||
/>
|
|
||||||
</Form.Item>
|
|
||||||
</div>
|
|
||||||
<Form.Item label="图标文字">
|
<Form.Item label="图标文字">
|
||||||
<Input v-model:value={form.text} maxlength={2} />
|
<Input
|
||||||
|
v-model:value={form.text}
|
||||||
|
maxlength={2}
|
||||||
|
class={isGame?.value ? '' : ' bg-black/10 '}
|
||||||
|
/>
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
<Form.Item label=" " colon={false}>
|
<Form.Item label=" " colon={false}>
|
||||||
<Button
|
<button
|
||||||
type="primary"
|
class={
|
||||||
size="large"
|
'outline-none ml-[23px] mt-2 flex items-center hover:opacity-90 text-[16px] justify-center gap-x-2 text-white w-[94px] h-[40px] rounded-lg'
|
||||||
icon={<CheckOutlined />}
|
}
|
||||||
|
style={{
|
||||||
|
background: 'linear-gradient(180deg,#ffaa4e 0%,#ff6227 100%)'
|
||||||
|
}}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
if (form.type === 1 && !form.text && !form.name) {
|
if (form.type === 1 && !form.text && !form.name) {
|
||||||
globalToast.error('文字图标请至少填写文字或者名称')
|
globalToast.error('文字图标请至少填写文字或者名称')
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
const id = uuid()
|
if (!form.link) {
|
||||||
const data: Block = {
|
globalToast.warning('请输入网站链接')
|
||||||
id,
|
return
|
||||||
link: form.link,
|
}
|
||||||
name: '',
|
if (!form.name) {
|
||||||
icon: form.type === 0 ? form.icon || info.icon : '',
|
globalToast.warning('请输入网站名称')
|
||||||
background: form.type === 0 ? '' : form.background,
|
return
|
||||||
color: form.type === 0 ? '' : form.color,
|
}
|
||||||
w: 1,
|
if (store.editBlockItem !== null) {
|
||||||
h: 1,
|
console.log(123)
|
||||||
text:
|
layout.changeBlock(form, store.editBlockItem.id)
|
||||||
form.type === 0 ? '' : form.text || form.name.substring(0, 2).toLocaleUpperCase(),
|
useRouterStore().back()
|
||||||
label: form.name
|
store.editBlockItem = null
|
||||||
|
} else {
|
||||||
|
const id = uuid()
|
||||||
|
const data: Block = {
|
||||||
|
id,
|
||||||
|
link: form.link,
|
||||||
|
name: '',
|
||||||
|
icon: form.type === 0 ? form.icon || info.icon : '',
|
||||||
|
background: form.type === 0 ? '' : form.background,
|
||||||
|
color: form.type === 0 ? '' : form.color,
|
||||||
|
w: 1,
|
||||||
|
h: 1,
|
||||||
|
text:
|
||||||
|
form.type === 0
|
||||||
|
? ''
|
||||||
|
: form.text || form.name.substring(0, 2).toLocaleUpperCase(),
|
||||||
|
label: form.name
|
||||||
|
}
|
||||||
|
layout.addBlock(data, addTo?.value)
|
||||||
}
|
}
|
||||||
layout.addBlock(data, addTo?.value)
|
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
确定
|
确定
|
||||||
</Button>
|
</button>
|
||||||
</Form.Item>
|
</Form.Item>
|
||||||
</Form>
|
</Form>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -9,6 +9,7 @@ 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'
|
||||||
|
import useAdderPageStore from './useAdderPageStore'
|
||||||
export const SECRET = 'A1Cv12olxT12dOE3xA1vPA=='
|
export const SECRET = 'A1Cv12olxT12dOE3xA1vPA=='
|
||||||
export const URL_ADDRESS = 'http://newfatfox.oss-cn-beijing.aliyuncs.com'
|
export const URL_ADDRESS = 'http://newfatfox.oss-cn-beijing.aliyuncs.com'
|
||||||
export interface GameType {
|
export interface GameType {
|
||||||
|
@ -126,9 +127,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,
|
||||||
|
@ -159,6 +160,7 @@ export default defineComponent(() => {
|
||||||
const isGame = computed(() => layout.state.current === 0)
|
const isGame = computed(() => layout.state.current === 0)
|
||||||
const appList = ref<GameType[]>([])
|
const appList = ref<GameType[]>([])
|
||||||
const selectType = ref('fc')
|
const selectType = ref('fc')
|
||||||
|
const store = useAdderPageStore()
|
||||||
const fetchGame = async (page: number) => {
|
const fetchGame = async (page: number) => {
|
||||||
const parems = `nonce=${generateRandomString(8)}&pid=PIDc8uT24mpo×tamp=${dayjs().unix()}`
|
const parems = `nonce=${generateRandomString(8)}&pid=PIDc8uT24mpo×tamp=${dayjs().unix()}`
|
||||||
const sign = MD5(parems + SECRET).toString()
|
const sign = MD5(parems + SECRET).toString()
|
||||||
|
@ -169,11 +171,11 @@ export default defineComponent(() => {
|
||||||
return res.data.items
|
return res.data.items
|
||||||
}
|
}
|
||||||
watch(
|
watch(
|
||||||
selectType,
|
[selectType, () => store.gameSearch],
|
||||||
(val) => {
|
(val) => {
|
||||||
loading.value = true
|
loading.value = true
|
||||||
if (val !== 'yiqiyoo') {
|
if (val[0] !== 'yiqiyoo') {
|
||||||
request<GameType[]>('GET', `/api/games?type=${val}`)
|
request<GameType[]>('GET', `/api/games?type=${val[0]}&keyword=${val[1]}`)
|
||||||
.then((res) => {
|
.then((res) => {
|
||||||
appList.value = res
|
appList.value = res
|
||||||
})
|
})
|
||||||
|
@ -212,41 +214,44 @@ export default defineComponent(() => {
|
||||||
)
|
)
|
||||||
return () => (
|
return () => (
|
||||||
<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 '}>
|
{!store.gameSearch && (
|
||||||
<CategoryTab
|
<div class={'w-full '}>
|
||||||
list={DefautGameTypeList}
|
<CategoryTab
|
||||||
selectType={selectType.value}
|
list={DefautGameTypeList}
|
||||||
onUpdate:type={(e) => {
|
selectType={selectType.value}
|
||||||
selectType.value = e
|
onUpdate:type={(e) => {
|
||||||
}}
|
selectType.value = e
|
||||||
v-slots={{
|
}}
|
||||||
select: (text: string) => (
|
v-slots={{
|
||||||
<button
|
select: (text: string) => (
|
||||||
class={clsx(
|
<button
|
||||||
'px-[20px] text-[14px] py-1 items-center justify-center duration-75 shrink-0 flex rounded-2xl whitespace-nowrap ',
|
class={clsx(
|
||||||
isGame.value
|
'px-[20px] text-[14px] py-1 items-center justify-center duration-75 shrink-0 flex rounded-2xl whitespace-nowrap ',
|
||||||
? 'bg-white/30 text-white bg-gradient-to-b from-[#ffaa4e] to-[#ff6227]'
|
isGame.value
|
||||||
: 'text-white bg-[#D6D6D6] bg-gradient-to-b from-[#ffaa4e] to-[#ff6227]'
|
? 'bg-white/30 text-white bg-gradient-to-b from-[#ffaa4e] to-[#ff6227]'
|
||||||
)}
|
: 'text-white bg-[#D6D6D6] bg-gradient-to-b from-[#ffaa4e] to-[#ff6227]'
|
||||||
>
|
)}
|
||||||
{text}
|
>
|
||||||
</button>
|
{text}
|
||||||
),
|
</button>
|
||||||
unSelect: (text: string) => (
|
),
|
||||||
<button
|
unSelect: (text: string) => (
|
||||||
class={clsx(
|
<button
|
||||||
'px-[20px] text-[14px] py-1 items-center justify-center duration-75 shrink-0 flex rounded-2xl whitespace-nowrap',
|
class={clsx(
|
||||||
isGame.value
|
'px-[20px] text-[14px] py-1 items-center justify-center duration-75 shrink-0 flex rounded-2xl whitespace-nowrap',
|
||||||
? ' text-[#999] bg-white/10 hover:bg-white/20'
|
isGame.value
|
||||||
: 'text-[#666] hover:bg-black/10 bg-black/[0.05] hover:bg-[#f0ecec]'
|
? ' text-[#999] bg-white/10 hover:bg-white/20'
|
||||||
)}
|
: 'text-[#666] hover:bg-black/10 bg-black/[0.05] hover:bg-[#f0ecec]'
|
||||||
>
|
)}
|
||||||
{text}
|
>
|
||||||
</button>
|
{text}
|
||||||
)
|
</button>
|
||||||
}}
|
)
|
||||||
></CategoryTab>
|
}}
|
||||||
</div>
|
></CategoryTab>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
<div class={'h-0 flex-1 w-full'}>
|
<div class={'h-0 flex-1 w-full'}>
|
||||||
<div class={'w-full h-full overflow-y-scroll scrollbar-hide'}>
|
<div class={'w-full h-full overflow-y-scroll scrollbar-hide'}>
|
||||||
{!loading.value ? (
|
{!loading.value ? (
|
||||||
|
|
|
@ -1,26 +1,11 @@
|
||||||
import CategoryTab from '@/utils/CategoryTab'
|
import CategoryTab from '@/utils/CategoryTab'
|
||||||
import request from '@/utils/request'
|
import { computed, defineComponent, inject } from 'vue'
|
||||||
import { computed, defineComponent, inject, onMounted, ref, watch } from 'vue'
|
|
||||||
import type { BackgroundType } from '../background/BackgroundSwtich'
|
|
||||||
import clsx from 'clsx'
|
import clsx from 'clsx'
|
||||||
import useLayoutStore from '../useLayoutStore'
|
import useLayoutStore from '../useLayoutStore'
|
||||||
import { AddToToken } from './AdderPage'
|
import { AddToToken } from './AdderPage'
|
||||||
import { v4 as uuid } from 'uuid'
|
import { v4 as uuid } from 'uuid'
|
||||||
|
import useAdderPageStore, { type HotAppType } from './useAdderPageStore'
|
||||||
|
|
||||||
interface HotAppCategoryType {
|
|
||||||
id: string
|
|
||||||
name: string
|
|
||||||
ordinal: number
|
|
||||||
}
|
|
||||||
interface HotAppType {
|
|
||||||
id: string
|
|
||||||
name: string
|
|
||||||
url: string
|
|
||||||
ordinal: number
|
|
||||||
desc: string
|
|
||||||
icon: string
|
|
||||||
background: string
|
|
||||||
}
|
|
||||||
export const LinkItem = defineComponent({
|
export const LinkItem = defineComponent({
|
||||||
props: {
|
props: {
|
||||||
content: {
|
content: {
|
||||||
|
@ -34,10 +19,13 @@ export const LinkItem = defineComponent({
|
||||||
const addTo = inject(AddToToken)
|
const addTo = inject(AddToToken)
|
||||||
return () => (
|
return () => (
|
||||||
<div
|
<div
|
||||||
class={clsx(' w-full h-full rounded-lg relative flex flex-col justify-between overflow-hidden shadow p-4', {
|
class={clsx(
|
||||||
'bg-white/20': isGame.value,
|
' w-full h-full rounded-lg relative flex flex-col justify-between overflow-hidden shadow p-4',
|
||||||
'bg-white/80': !isGame.value
|
{
|
||||||
})}
|
'bg-white/20': isGame.value,
|
||||||
|
'bg-white/80': !isGame.value
|
||||||
|
}
|
||||||
|
)}
|
||||||
key={props.content.name}
|
key={props.content.name}
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
|
@ -110,6 +98,9 @@ export const LinkItem = defineComponent({
|
||||||
},
|
},
|
||||||
addTo?.value
|
addTo?.value
|
||||||
)
|
)
|
||||||
|
if (addTo?.value) {
|
||||||
|
layout.state.currentPage = addTo?.value
|
||||||
|
}
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
添加
|
添加
|
||||||
|
@ -122,78 +113,55 @@ export const LinkItem = defineComponent({
|
||||||
|
|
||||||
export default defineComponent(() => {
|
export default defineComponent(() => {
|
||||||
const layout = useLayoutStore()
|
const layout = useLayoutStore()
|
||||||
const loading = ref(false)
|
|
||||||
const isGame = computed(() => layout.state.current === 0)
|
const isGame = computed(() => layout.state.current === 0)
|
||||||
const appList = ref<HotAppType[]>([])
|
|
||||||
const categoryList = ref<BackgroundType[]>([])
|
|
||||||
const selectType = ref('')
|
|
||||||
onMounted(() => {
|
|
||||||
request<HotAppCategoryType[]>('GET', '/api/app/hotAppTypes').then((res) => {
|
|
||||||
categoryList.value = res.map((el) => ({
|
|
||||||
id: el.id,
|
|
||||||
oridinal: el.ordinal,
|
|
||||||
type: el.name,
|
|
||||||
attr: 0
|
|
||||||
}))
|
|
||||||
|
|
||||||
selectType.value = res[0].id
|
const store = useAdderPageStore()
|
||||||
})
|
|
||||||
})
|
|
||||||
watch(selectType, (val) => {
|
|
||||||
loading.value = true
|
|
||||||
|
|
||||||
request<HotAppType[]>('GET', `/api/app/hotApps?hotAppsId=${val}`)
|
|
||||||
.then((res) => {
|
|
||||||
appList.value = res
|
|
||||||
})
|
|
||||||
.finally(() => {
|
|
||||||
setTimeout(() => {
|
|
||||||
loading.value = false
|
|
||||||
}, 200)
|
|
||||||
})
|
|
||||||
})
|
|
||||||
return () => (
|
return () => (
|
||||||
<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 '}>
|
{!store.isSearch && (
|
||||||
<CategoryTab
|
<div class={'w-full '}>
|
||||||
list={categoryList.value}
|
<CategoryTab
|
||||||
selectType={selectType.value}
|
list={store.categoryList}
|
||||||
onUpdate:type={(e) => {
|
selectType={store.selectType}
|
||||||
selectType.value = e
|
onUpdate:type={(e) => {
|
||||||
}}
|
store.selectType = e
|
||||||
v-slots={{
|
}}
|
||||||
select: (text: string) => (
|
v-slots={{
|
||||||
<button
|
select: (text: string) => (
|
||||||
class={clsx(
|
<button
|
||||||
'px-[20px] text-[14px] py-1 items-center justify-center duration-75 shrink-0 flex rounded-2xl whitespace-nowrap ',
|
class={clsx(
|
||||||
isGame.value
|
'px-[20px] text-[14px] py-1 items-center justify-center duration-75 shrink-0 flex rounded-2xl whitespace-nowrap ',
|
||||||
? 'bg-white/30 text-white bg-gradient-to-b from-[#ffaa4e] to-[#ff6227]'
|
isGame.value
|
||||||
: 'text-white bg-[#D6D6D6] bg-gradient-to-b from-[#ffaa4e] to-[#ff6227]'
|
? 'bg-white/30 text-white bg-gradient-to-b from-[#ffaa4e] to-[#ff6227]'
|
||||||
)}
|
: 'text-white bg-[#D6D6D6] bg-gradient-to-b from-[#ffaa4e] to-[#ff6227]'
|
||||||
>
|
)}
|
||||||
{text}
|
>
|
||||||
</button>
|
{text}
|
||||||
),
|
</button>
|
||||||
unSelect: (text: string) => (
|
),
|
||||||
<button
|
unSelect: (text: string) => (
|
||||||
class={clsx(
|
<button
|
||||||
'px-[20px] text-[14px] py-1 items-center justify-center duration-75 shrink-0 flex rounded-2xl whitespace-nowrap',
|
class={clsx(
|
||||||
isGame.value
|
'px-[20px] text-[14px] py-1 items-center justify-center duration-75 shrink-0 flex rounded-2xl whitespace-nowrap',
|
||||||
? ' text-[#999] bg-white/10 hover:bg-white/20'
|
isGame.value
|
||||||
: 'text-[#666] hover:bg-black/10 bg-black/[0.05] hover:bg-[#f0ecec]'
|
? ' text-[#999] bg-white/10 hover:bg-white/20'
|
||||||
)}
|
: 'text-[#666] hover:bg-black/10 bg-black/[0.05] hover:bg-[#f0ecec]'
|
||||||
>
|
)}
|
||||||
{text}
|
>
|
||||||
</button>
|
{text}
|
||||||
)
|
</button>
|
||||||
}}
|
)
|
||||||
></CategoryTab>
|
}}
|
||||||
</div>
|
></CategoryTab>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
<div class={'h-0 flex-1 w-full'}>
|
<div class={'h-0 flex-1 w-full'}>
|
||||||
<div class={'w-full h-full overflow-y-scroll scrollbar-hide'}>
|
<div class={'w-full h-full overflow-y-scroll scrollbar-hide'}>
|
||||||
{!loading.value ? (
|
{!store.loading ? (
|
||||||
<div class={'w-full grid grid-cols-3 gap-4 '} style="grid-auto-rows: 120px">
|
<div class={'w-full grid grid-cols-3 gap-4 '} style="grid-auto-rows: 120px">
|
||||||
{appList.value.map((el) => (
|
{store.appList.map((el) => (
|
||||||
<LinkItem content={el}></LinkItem>
|
<LinkItem content={el}></LinkItem>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -4,6 +4,7 @@ import widgetList, { type Widget } from '@/widgets'
|
||||||
import clsx from 'clsx'
|
import clsx from 'clsx'
|
||||||
import { AddToToken } from './AdderPage'
|
import { AddToToken } from './AdderPage'
|
||||||
import { v4 as uuid } from 'uuid'
|
import { v4 as uuid } from 'uuid'
|
||||||
|
import useAdderPageStore from './useAdderPageStore'
|
||||||
|
|
||||||
export const WidgetItem = defineComponent({
|
export const WidgetItem = defineComponent({
|
||||||
props: {
|
props: {
|
||||||
|
@ -32,10 +33,7 @@ export const WidgetItem = defineComponent({
|
||||||
key={props.content.name}
|
key={props.content.name}
|
||||||
>
|
>
|
||||||
<div class="flex">
|
<div class="flex">
|
||||||
<img
|
<img src={props.content.icon} class="w-[58px] h-[58px] bg-cover rounded-lg shadow-lg" />
|
||||||
src={props.content.icon}
|
|
||||||
class="w-[58px] h-[58px] bg-cover rounded-lg shadow-lg"
|
|
||||||
/>
|
|
||||||
<div class="px-2 w-0 flex-grow ">
|
<div class="px-2 w-0 flex-grow ">
|
||||||
<div
|
<div
|
||||||
class={clsx('text text-sm mb-[2px]', {
|
class={clsx('text text-sm mb-[2px]', {
|
||||||
|
@ -120,18 +118,18 @@ export const WidgetItem = defineComponent({
|
||||||
|
|
||||||
export default defineComponent(() => {
|
export default defineComponent(() => {
|
||||||
const layout = useLayoutStore()
|
const layout = useLayoutStore()
|
||||||
|
const store = useAdderPageStore()
|
||||||
return () => (
|
return () => (
|
||||||
<div class="absolute left-0 top-0 w-full h-full overflow-y-auto scrollbar-hide gap-4">
|
<div class="absolute left-0 top-0 w-full h-full overflow-y-auto scrollbar-hide gap-4">
|
||||||
<div class="w-full grid grid-cols-3 grid-flow-row-dense gap-4" style="grid-auto-rows: 120px">
|
<div class="w-full grid grid-cols-3 grid-flow-row-dense gap-4" style="grid-auto-rows: 120px">
|
||||||
{
|
{layout.state.current !== 1
|
||||||
layout.state.current !== 1 ?
|
? widgetList
|
||||||
widgetList.filter(val => val.name !== 'tomato_work').map((el) => (
|
.filter((val) => val.name !== 'tomato_work')
|
||||||
<WidgetItem content={el} key={el.name} />
|
.filter((val) => val.label.includes(store.widgetSearchWords))
|
||||||
)) :
|
.map((el) => <WidgetItem content={el} key={el.name} />)
|
||||||
widgetList.map((el) => (
|
: widgetList
|
||||||
<WidgetItem content={el} key={el.name} />
|
.filter((val) => val.label.includes(store.widgetSearchWords))
|
||||||
))
|
.map((el) => <WidgetItem content={el} key={el.name} />)}
|
||||||
}
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
|
|
|
@ -1,9 +1,95 @@
|
||||||
import { defineStore } from "pinia";
|
import request from '@/utils/request'
|
||||||
import { ref } from "vue";
|
import { defineStore } from 'pinia'
|
||||||
|
import { watch } from 'vue'
|
||||||
|
import { ref } from 'vue'
|
||||||
|
import type { BackgroundType } from '../background/BackgroundSwtich'
|
||||||
|
export interface HotAppType {
|
||||||
|
id: string
|
||||||
|
name: string
|
||||||
|
url: string
|
||||||
|
ordinal: number
|
||||||
|
desc: string
|
||||||
|
icon: string
|
||||||
|
background: string
|
||||||
|
}
|
||||||
|
export interface HotAppCategoryType {
|
||||||
|
id: string
|
||||||
|
name: string
|
||||||
|
ordinal: number
|
||||||
|
}
|
||||||
|
export type EditBlockItemType = {
|
||||||
|
id: string
|
||||||
|
link: string
|
||||||
|
name: string
|
||||||
|
text: string
|
||||||
|
background: string
|
||||||
|
color: string
|
||||||
|
icon: string
|
||||||
|
type: number // 0 默认,1 文字
|
||||||
|
}
|
||||||
|
export default defineStore('adderPage', () => {
|
||||||
|
const type = ref(1)
|
||||||
|
const selectType = ref('')
|
||||||
|
const loading = ref(false)
|
||||||
|
const appList = ref<HotAppType[]>([])
|
||||||
|
const categoryList = ref<BackgroundType[]>([])
|
||||||
|
const isSearch = ref(false)
|
||||||
|
const widgetSearchWords = ref('')
|
||||||
|
const gameSearch = ref('')
|
||||||
|
const editBlockItem = ref<EditBlockItemType | null >(null)
|
||||||
|
request<HotAppCategoryType[]>('GET', '/api/app/hotAppTypes').then((res) => {
|
||||||
|
categoryList.value = res.map((el) => ({
|
||||||
|
id: el.id,
|
||||||
|
oridinal: el.ordinal,
|
||||||
|
type: el.name,
|
||||||
|
attr: 0
|
||||||
|
}))
|
||||||
|
|
||||||
export default defineStore("adderPage", () => {
|
selectType.value = res[0].id
|
||||||
const type = ref(1)
|
})
|
||||||
return {
|
watch(selectType, (val) => {
|
||||||
type
|
getApps(val)
|
||||||
|
})
|
||||||
|
const getApps = (_selectType: string) => {
|
||||||
|
loading.value = true
|
||||||
|
request<HotAppType[]>('GET', `/api/app/hotApps?hotAppsId=${_selectType}`)
|
||||||
|
.then((res) => {
|
||||||
|
appList.value = res
|
||||||
|
})
|
||||||
|
.finally(() => {
|
||||||
|
setTimeout(() => {
|
||||||
|
loading.value = false
|
||||||
|
}, 200)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const search = (keywords: string) => {
|
||||||
|
if (keywords === '') {
|
||||||
|
isSearch.value = false
|
||||||
|
getApps(selectType.value)
|
||||||
|
return
|
||||||
}
|
}
|
||||||
})
|
isSearch.value = true
|
||||||
|
request<HotAppType[]>('GET', `/api/app/hotApp/search?keyword=${keywords}`)
|
||||||
|
.then((res) => {
|
||||||
|
appList.value = res
|
||||||
|
})
|
||||||
|
.finally(() => {
|
||||||
|
setTimeout(() => {
|
||||||
|
loading.value = false
|
||||||
|
}, 200)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
type,
|
||||||
|
widgetSearchWords,
|
||||||
|
categoryList,
|
||||||
|
appList,
|
||||||
|
loading,
|
||||||
|
selectType,
|
||||||
|
search,
|
||||||
|
isSearch,
|
||||||
|
gameSearch,
|
||||||
|
editBlockItem
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import { Button, Select, Slider } from 'ant-design-vue'
|
import { Button, Slider } from 'ant-design-vue'
|
||||||
import { defineComponent, ref, Transition, watch } from 'vue'
|
import { defineComponent, ref, Transition, watch } from 'vue'
|
||||||
import useLayoutStore from '../useLayoutStore'
|
import useLayoutStore from '../useLayoutStore'
|
||||||
import { DownloadOutlined, EyeInvisibleOutlined, SwapOutlined } from '@ant-design/icons-vue'
|
import { DownloadOutlined, EyeInvisibleOutlined, SwapOutlined } from '@ant-design/icons-vue'
|
||||||
|
@ -23,7 +23,7 @@ export default defineComponent(() => {
|
||||||
const settings = useSettingsStore()
|
const settings = useSettingsStore()
|
||||||
return () => (
|
return () => (
|
||||||
<div class="absolute left-0 top-0 w-full h-full p-4 overflow-y-auto scrollbar-hide">
|
<div class="absolute left-0 top-0 w-full h-full p-4 overflow-y-auto scrollbar-hide">
|
||||||
<SettingItem
|
{/* <SettingItem
|
||||||
noBg
|
noBg
|
||||||
v-slots={{
|
v-slots={{
|
||||||
label: () => <div>所属模式</div>
|
label: () => <div>所属模式</div>
|
||||||
|
@ -38,7 +38,7 @@ export default defineComponent(() => {
|
||||||
]}
|
]}
|
||||||
v-model:value={selected.value}
|
v-model:value={selected.value}
|
||||||
/>
|
/>
|
||||||
</SettingItem>
|
</SettingItem> */}
|
||||||
<div class="px-4">
|
<div class="px-4">
|
||||||
<div class="h-[180px]">
|
<div class="h-[180px]">
|
||||||
{layout.background.video && layout.background.type !== 'own' ? (
|
{layout.background.video && layout.background.type !== 'own' ? (
|
||||||
|
|
|
@ -154,7 +154,6 @@ export default defineComponent(() => {
|
||||||
fill={isGame.value ? 'white' : 'black'}
|
fill={isGame.value ? 'white' : 'black'}
|
||||||
name={BiChevronDown.name}
|
name={BiChevronDown.name}
|
||||||
class="group pointer-events-none absolute top-1/2 right-2.5 -translate-y-1/2 size-4 "
|
class="group pointer-events-none absolute top-1/2 right-2.5 -translate-y-1/2 size-4 "
|
||||||
aria-hidden="true"
|
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -33,11 +33,7 @@ export default defineComponent(() => {
|
||||||
<div
|
<div
|
||||||
class={clsx(
|
class={clsx(
|
||||||
'fixed bottom-4 left-1/2 duration-150 -translate-x-1/2 p-4 rounded-lg bg-white/60 backdrop-blur flex gap-4 shadow-lg',
|
'fixed bottom-4 left-1/2 duration-150 -translate-x-1/2 p-4 rounded-lg bg-white/60 backdrop-blur flex gap-4 shadow-lg',
|
||||||
setting.state.showDock === 'auto'
|
setting.state.showDock === 'auto' ? show.value ? 'bottom-4' : 'bottom-[-90px]' : 'bottom-4'
|
||||||
? show.value
|
|
||||||
? 'bottom-4'
|
|
||||||
: '-bottom-[90px]'
|
|
||||||
: 'bottom-4'
|
|
||||||
)}
|
)}
|
||||||
ref={container}
|
ref={container}
|
||||||
onMouseleave={() => {
|
onMouseleave={() => {
|
||||||
|
@ -58,8 +54,8 @@ export default defineComponent(() => {
|
||||||
style={
|
style={
|
||||||
current.value >= 0
|
current.value >= 0
|
||||||
? {
|
? {
|
||||||
transform: `translateY(${current.value === i - 1 || current.value === i + 1 ? '-5%' : current.value === i ? '-10%' : '0'}) scale(${current.value === i - 1 || current.value === i + 1 ? 1.1 : current.value === i ? 1.2 : 1})`
|
transform: `translateY(${current.value === i - 1 || current.value === i + 1 ? '-5%' : current.value === i ? '-10%' : '0'}) scale(${current.value === i - 1 || current.value === i + 1 ? 1.1 : current.value === i ? 1.2 : 1})`
|
||||||
}
|
}
|
||||||
: {}
|
: {}
|
||||||
}
|
}
|
||||||
id={block?.id || ''}
|
id={block?.id || ''}
|
||||||
|
|
|
@ -7,6 +7,9 @@ import LinkBlock from './LinkBlock'
|
||||||
import DirBlock from './DirBlock'
|
import DirBlock from './DirBlock'
|
||||||
import WidgetBlock from './WidgetBlock'
|
import WidgetBlock from './WidgetBlock'
|
||||||
import useSettingsStore from '@/settings/useSettingsStore'
|
import useSettingsStore from '@/settings/useSettingsStore'
|
||||||
|
import { useMenuStore } from '../GlobalMenu'
|
||||||
|
import { block } from '@milkdown/kit/plugin/block'
|
||||||
|
import clsx from 'clsx'
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
props: {
|
props: {
|
||||||
|
@ -22,11 +25,15 @@ export default defineComponent({
|
||||||
setup(props) {
|
setup(props) {
|
||||||
const settings = useSettingsStore()
|
const settings = useSettingsStore()
|
||||||
const layout = useLayoutStore()
|
const layout = useLayoutStore()
|
||||||
|
const menu = useMenuStore()
|
||||||
let it: any = 0
|
let it: any = 0
|
||||||
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={clsx(
|
||||||
|
'w-full h-full p-[var(--block-padding)] relative rounded-lg ',
|
||||||
|
menu.isEditPage && 'animate-wiggle'
|
||||||
|
)}
|
||||||
key={props.block.id}
|
key={props.block.id}
|
||||||
id={props.block.id}
|
id={props.block.id}
|
||||||
style={{
|
style={{
|
||||||
|
@ -115,10 +122,38 @@ export default defineComponent({
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
|
{menu.isEditPage && (
|
||||||
|
<div
|
||||||
|
v-outside-click={() => {
|
||||||
|
menu.isEditPage = false
|
||||||
|
}}
|
||||||
|
onClick={(e) => {
|
||||||
|
e.stopPropagation()
|
||||||
|
const idx = layout.state.content[layout.state.current].pages[
|
||||||
|
layout.state.currentPage
|
||||||
|
].list.findIndex((val) => val.id === props.block.id)
|
||||||
|
|
||||||
|
if (idx < 0) return
|
||||||
|
|
||||||
|
layout.state.content[layout.state.current].pages[
|
||||||
|
layout.state.currentPage
|
||||||
|
].list.splice(idx, 1)
|
||||||
|
}}
|
||||||
|
class={
|
||||||
|
'rounded-full cursor-pointer backdrop-blur-md absolute w-[20px] h-[20px] top-[8px] right-[12px] z-10 '
|
||||||
|
}
|
||||||
|
style={{
|
||||||
|
backgroundImage: `url('/tab/bg/del_icon_img.png')`,
|
||||||
|
backgroundSize: 'cover'
|
||||||
|
}}
|
||||||
|
></div>
|
||||||
|
)}
|
||||||
|
|
||||||
<div
|
<div
|
||||||
class="w-full h-full overflow-hidden relative cursor-pointer shadow-lg hover-move"
|
class="w-full h-full overflow-hidden relative cursor-pointer shadow-lg hover-move "
|
||||||
style={{
|
style={{
|
||||||
borderRadius: `calc(var(--block-radius) * var(--block-size))`
|
borderRadius: `calc(var(--block-radius) * var(--block-size))`,
|
||||||
|
transition: 'transform 0.2s'
|
||||||
}}
|
}}
|
||||||
onContextmenu={(e) => {
|
onContextmenu={(e) => {
|
||||||
e.stopPropagation()
|
e.stopPropagation()
|
||||||
|
@ -140,7 +175,7 @@ export default defineComponent({
|
||||||
</div>
|
</div>
|
||||||
{settings.state.showBlockLabel && (
|
{settings.state.showBlockLabel && (
|
||||||
<div
|
<div
|
||||||
class="absolute left-1/2 -translate-x-1/2 -bottom-3 text-sm text-white text-center w-[172px] overflow-hidden text-ellipsis whitespace-nowrap break-all font-bold"
|
class="absolute left-1/2 -translate-x-1/2 -bottom-3 text-sm text-white text-center w-[172px] overflow-hidden text-ellipsis whitespace-nowrap break-all font-bold"
|
||||||
style="text-shadow: 0 0 4px rgba(0,0,0,.6)"
|
style="text-shadow: 0 0 4px rgba(0,0,0,.6)"
|
||||||
>
|
>
|
||||||
{layout.getLabel(props.block)}
|
{layout.getLabel(props.block)}
|
||||||
|
|
|
@ -31,7 +31,7 @@ export default defineComponent({
|
||||||
jump(props.block.link)
|
jump(props.block.link)
|
||||||
}}
|
}}
|
||||||
style={{
|
style={{
|
||||||
backgroundColor: props.block.background || 'white',
|
backgroundColor: props.block.text ? props.block.background || 'white' : 'transparent',
|
||||||
color: props.block.color || 'black',
|
color: props.block.color || 'black',
|
||||||
backgroundImage: props.block.icon ? `url('${props.block.icon}')` : '',
|
backgroundImage: props.block.icon ? `url('${props.block.icon}')` : '',
|
||||||
fontSize: props.dock ? '16px' : props.brief ? '12px' : 'calc(var(--block-size) / 5)'
|
fontSize: props.dock ? '16px' : props.brief ? '12px' : 'calc(var(--block-size) / 5)'
|
||||||
|
|
|
@ -1,23 +1,63 @@
|
||||||
import SettingItem from '@/settings/SettingItem'
|
import SettingItem from '@/settings/SettingItem'
|
||||||
import useSettingsStore from '@/settings/useSettingsStore'
|
import { Button } from 'ant-design-vue'
|
||||||
import { Button, Switch } from 'ant-design-vue'
|
|
||||||
import clsx from 'clsx'
|
import clsx from 'clsx'
|
||||||
import { defineComponent } from 'vue'
|
import { computed, defineComponent, ref } from 'vue'
|
||||||
|
import useLayoutStore from '../useLayoutStore'
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
setup() {
|
setup() {
|
||||||
const settings = useSettingsStore()
|
const open = ref(false)
|
||||||
|
const layout = useLayoutStore()
|
||||||
|
const isGame = computed(() => {
|
||||||
|
return layout.state.current === 0
|
||||||
|
})
|
||||||
return () => (
|
return () => (
|
||||||
<div class="p-4 flex flex-col ">
|
<div class="p-4 flex flex-col ">
|
||||||
<SettingItem
|
<SettingItem
|
||||||
v-slots={{
|
v-slots={{
|
||||||
label: () => <div>重置</div>
|
label: () => <div >重置</div>
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Button type="primary">立即重置</Button>
|
<Button type="primary" onClick={() => {
|
||||||
|
open.value = true
|
||||||
|
}}>立即重置</Button>
|
||||||
</SettingItem>
|
</SettingItem>
|
||||||
<p class={'text-[#666] text-[12px]'}>将会把您的历史调整清空,恢复成最初的样式</p>
|
<p class={'text-[#666] text-[12px]'}>将会把您的历史调整清空,恢复成最初的样式</p>
|
||||||
</div>
|
{
|
||||||
|
open.value &&
|
||||||
|
<div class={"w-[300px] h-[210px] absolute rounded-2xl right-[-310px] z-10 bg-[#2c2e3e]"} style={
|
||||||
|
isGame.value ?
|
||||||
|
{
|
||||||
|
backgroundImage: `url('/tab/bg/addBorder.png')`,
|
||||||
|
backgroundSize: '100% 100%',
|
||||||
|
backgroundColor: '#2c2e3e'
|
||||||
|
} : {}}>
|
||||||
|
|
||||||
|
<div class={"flex flex-col w-full h-full p-7 border-b-[1px] items-center justify-between"}>
|
||||||
|
<span class={isGame.value ? "" : ""}>恢复提醒</span>
|
||||||
|
<div class={clsx("w-full h-[1px]", isGame.value ? " bg-white/20" : "bg-black/20")}></div>
|
||||||
|
<span class={clsx("text-[14px] leading-[20px] mb-2 text-center", isGame.value ? "text-[#fff9]" : "")}>此操作会将您的历史调整的数据清空,为防止您的误操作,请再次确认是否要进行重置</span>
|
||||||
|
<div class={"flex justify-between w-full"}>
|
||||||
|
<button
|
||||||
|
onClick={() => {
|
||||||
|
layout.resetAll()
|
||||||
|
|
||||||
|
open.value = false
|
||||||
|
}}
|
||||||
|
class={clsx("w-[118px] rounded-lg py-1 flex justify-center", isGame.value ? "bg-white/20" : "")}>重置</button>
|
||||||
|
<button
|
||||||
|
onClick={() => {
|
||||||
|
open.value = false
|
||||||
|
|
||||||
|
}}
|
||||||
|
class={clsx("w-[118px] rounded-lg py-1 flex justify-center", isGame.value ? "bg-[#ff7372]" : "")}>取消</button>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
|
||||||
|
</div >
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import { computed, defineComponent, ref, toRaw } from 'vue'
|
import { computed, defineComponent, ref, toRaw, watch } from 'vue'
|
||||||
import useLayoutStore from '../useLayoutStore'
|
import useLayoutStore from '../useLayoutStore'
|
||||||
import { OhVueIcon, addIcons } from 'oh-vue-icons'
|
import { OhVueIcon, addIcons } from 'oh-vue-icons'
|
||||||
import { MdAdd } from 'oh-vue-icons/icons'
|
import { MdAdd } from 'oh-vue-icons/icons'
|
||||||
|
@ -15,11 +15,21 @@ export default defineComponent(() => {
|
||||||
const layout = useLayoutStore()
|
const layout = useLayoutStore()
|
||||||
const settings = useSettingsStore()
|
const settings = useSettingsStore()
|
||||||
const router = useRouterStore()
|
const router = useRouterStore()
|
||||||
|
const scrollRef = ref<any>()
|
||||||
const container = useSortable(
|
const container = useSortable(
|
||||||
computed(() => layout.currentPage.list),
|
computed(() => layout.currentPage.list),
|
||||||
ref('page')
|
ref('page')
|
||||||
)
|
)
|
||||||
|
watch(
|
||||||
|
() => layout.currentPage.list.length,
|
||||||
|
(cur, old) => {
|
||||||
|
if (cur > old) {
|
||||||
|
if (scrollRef.value) {
|
||||||
|
scrollRef.value.scrollTop = scrollRef.value.scrollHeight
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
listenParent('openSetting', (d) => {
|
listenParent('openSetting', (d) => {
|
||||||
if (d === 'profile') {
|
if (d === 'profile') {
|
||||||
router.go('settings-user')
|
router.go('settings-user')
|
||||||
|
@ -30,6 +40,7 @@ export default defineComponent(() => {
|
||||||
})
|
})
|
||||||
return () => (
|
return () => (
|
||||||
<div
|
<div
|
||||||
|
ref={scrollRef}
|
||||||
class="absolute left-0 top-0 w-full h-screen overflow-y-auto no-scrollbar pt-[240px] px-[calc((100%_-_var(--main-width))_/_2)]"
|
class="absolute left-0 top-0 w-full h-screen overflow-y-auto no-scrollbar pt-[240px] px-[calc((100%_-_var(--main-width))_/_2)]"
|
||||||
onScroll={(e) => {
|
onScroll={(e) => {
|
||||||
const h = (e.target as any).scrollTop
|
const h = (e.target as any).scrollTop
|
||||||
|
|
|
@ -5,6 +5,8 @@ import { EditOutlined, DeleteOutlined, PlusOutlined } from '@ant-design/icons-vu
|
||||||
import asyncLoader from '@/utils/asyncLoader'
|
import asyncLoader from '@/utils/asyncLoader'
|
||||||
import ThemeProvider from '@/utils/ThemeProvider'
|
import ThemeProvider from '@/utils/ThemeProvider'
|
||||||
import { globalToast } from '@/main'
|
import { globalToast } from '@/main'
|
||||||
|
import useUserStore from '@/user/useUserStore'
|
||||||
|
import useRouterStore from '@/useRouterStore'
|
||||||
const SearchAdder = asyncLoader(() => import('./SearchAdder'))
|
const SearchAdder = asyncLoader(() => import('./SearchAdder'))
|
||||||
const SearchItem = defineComponent({
|
const SearchItem = defineComponent({
|
||||||
props: {
|
props: {
|
||||||
|
@ -157,7 +159,12 @@ export default defineComponent(() => {
|
||||||
block
|
block
|
||||||
icon={<PlusOutlined />}
|
icon={<PlusOutlined />}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
showAdder.value = null
|
if (useUserStore().isLogin) {
|
||||||
|
showAdder.value = null
|
||||||
|
} else {
|
||||||
|
globalToast.warning('请先登录')
|
||||||
|
useRouterStore().go('global-login')
|
||||||
|
}
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
添加自定义搜索引擎
|
添加自定义搜索引擎
|
||||||
|
|
|
@ -23,18 +23,19 @@ export default defineComponent(
|
||||||
props.isMini
|
props.isMini
|
||||||
? {}
|
? {}
|
||||||
: {
|
: {
|
||||||
top: layout.isCompact ? '40px' : layout.state.simple ? '230px' : '172px',
|
top: layout.isCompact ? '40px' : layout.state.simple ? '230px' : '172px',
|
||||||
width: settings.state.searchWidth + 'rem'
|
width: settings.state.searchWidth + 'rem'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
class={clsx(
|
class={clsx(
|
||||||
'w-full h-11 shadow-content overflow-hidden px-1 transition-all flex justify-between items-center gap-4 ',
|
'w-full h-11 shadow-content overflow-hidden px-1 transition-all flex justify-between items-center gap-4 ',
|
||||||
search.focus ? 'bg-white/60' : 'bg-white/40 hover:bg-white/60',
|
search.focus ? 'bg-white/60' : 'bg-white hover:bg-white',
|
||||||
props.isMini ? '' : 'max-w-[90vw] w-full'
|
props.isMini ? '' : 'max-w-[90vw] w-full'
|
||||||
)}
|
)}
|
||||||
style={{
|
style={{
|
||||||
|
opacity: search.focus ? 1 : settings.state.searchOpacity,
|
||||||
borderRadius: settings.state.searchRadius + 'px'
|
borderRadius: settings.state.searchRadius + 'px'
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
|
@ -58,7 +59,7 @@ export default defineComponent(
|
||||||
onContextmenu={(e) => e.stopPropagation()}
|
onContextmenu={(e) => e.stopPropagation()}
|
||||||
onKeydown={(e) => e.stopPropagation()}
|
onKeydown={(e) => e.stopPropagation()}
|
||||||
class="flex-1 h-full outline-none bg-transparent placeholder:text-slate-600 placeholder:tracking-widest text-slate-800 pr-4"
|
class="flex-1 h-full outline-none bg-transparent placeholder:text-slate-600 placeholder:tracking-widest text-slate-800 pr-4"
|
||||||
placeholder={`输入搜索 ${searchConfig.current.name}`}
|
placeholder={`请输入搜索内容`}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<Transition name="searchContent">{search.showSearchConfig && <SearchConfig />}</Transition>
|
<Transition name="searchContent">{search.showSearchConfig && <SearchConfig />}</Transition>
|
||||||
|
|
|
@ -42,11 +42,12 @@ const Item = defineComponent({
|
||||||
return () => (
|
return () => (
|
||||||
<div
|
<div
|
||||||
onContextmenu={(e) => {
|
onContextmenu={(e) => {
|
||||||
|
if (!props.id) return
|
||||||
useMenuStore().open('page')
|
useMenuStore().open('page')
|
||||||
useMenuStore().selectPage = {
|
useMenuStore().selectPage = {
|
||||||
id: props.id,
|
id: props.id,
|
||||||
label: props.label,
|
label: props.label,
|
||||||
name: props.name,
|
name: props.name
|
||||||
}
|
}
|
||||||
e.stopPropagation()
|
e.stopPropagation()
|
||||||
e.preventDefault()
|
e.preventDefault()
|
||||||
|
@ -106,6 +107,10 @@ export default defineComponent(() => {
|
||||||
<Transition>
|
<Transition>
|
||||||
{layout.ready && (
|
{layout.ready && (
|
||||||
<div
|
<div
|
||||||
|
onContextmenu={(e)=> {
|
||||||
|
e.stopPropagation()
|
||||||
|
e.preventDefault()
|
||||||
|
}}
|
||||||
class={clsx(
|
class={clsx(
|
||||||
'w-[130px] min-h-[620px] hover:bg-red-10 z-20 fixed top-1/2 -translate-y-1/2 bottom-0 ',
|
'w-[130px] min-h-[620px] hover:bg-red-10 z-20 fixed top-1/2 -translate-y-1/2 bottom-0 ',
|
||||||
settings.state.siderDirection === 'left' ? 'left-0' : 'right-0'
|
settings.state.siderDirection === 'left' ? 'left-0' : 'right-0'
|
||||||
|
@ -194,6 +199,10 @@ export default defineComponent(() => {
|
||||||
{/* 添加页面 */}
|
{/* 添加页面 */}
|
||||||
{menu.showEditPage && (
|
{menu.showEditPage && (
|
||||||
<div
|
<div
|
||||||
|
onContextmenu={(e) => {
|
||||||
|
e.stopPropagation()
|
||||||
|
e.preventDefault()
|
||||||
|
}}
|
||||||
class={clsx(
|
class={clsx(
|
||||||
'absolute bottom-0 w-56 rounded-lg p-4 bg-white/40 backdrop-blur shadow-lg',
|
'absolute bottom-0 w-56 rounded-lg p-4 bg-white/40 backdrop-blur shadow-lg',
|
||||||
settings.state.siderDirection === 'left' ? 'left-[70px]' : 'right-[70px]'
|
settings.state.siderDirection === 'left' ? 'left-[70px]' : 'right-[70px]'
|
||||||
|
@ -230,7 +239,16 @@ export default defineComponent(() => {
|
||||||
if (menu.selectPage) {
|
if (menu.selectPage) {
|
||||||
menu.selectPage.name = selected.value.name
|
menu.selectPage.name = selected.value.name
|
||||||
menu.selectPage.label = label.value
|
menu.selectPage.label = label.value
|
||||||
|
if (!menu.selectPage?.id) return
|
||||||
|
const idx = layout.state.content[layout.state.current].pages.findIndex(
|
||||||
|
(val) => val.id === menu.selectPage?.id
|
||||||
|
)
|
||||||
|
if (idx !== -1) {
|
||||||
|
layout.state.content[layout.state.current].pages[idx].label = label.value
|
||||||
|
layout.state.content[layout.state.current].pages[idx].name =
|
||||||
|
selected.value.name
|
||||||
|
}
|
||||||
|
menu.selectPage = undefined
|
||||||
} else {
|
} else {
|
||||||
layout.currentMode.pages.push({
|
layout.currentMode.pages.push({
|
||||||
list: [],
|
list: [],
|
||||||
|
@ -241,11 +259,10 @@ export default defineComponent(() => {
|
||||||
}
|
}
|
||||||
|
|
||||||
menu.showEditPage = false
|
menu.showEditPage = false
|
||||||
|
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<OhVueIcon name="px-check" />
|
<OhVueIcon name="px-check" />
|
||||||
{menu.selectPage ? "修改页面" : "添加页面"}
|
{menu.selectPage ? '修改页面' : '添加页面'}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
|
@ -8,6 +8,7 @@ import jump from '@/utils/jump'
|
||||||
import useSettingsStore from '@/settings/useSettingsStore'
|
import useSettingsStore from '@/settings/useSettingsStore'
|
||||||
import useUserStore from '@/user/useUserStore'
|
import useUserStore from '@/user/useUserStore'
|
||||||
import request from '@/utils/request'
|
import request from '@/utils/request'
|
||||||
|
import type { EditBlockItemType } from './adder/useAdderPageStore'
|
||||||
|
|
||||||
const defaultLayout: Layout = {
|
const defaultLayout: Layout = {
|
||||||
content: [
|
content: [
|
||||||
|
@ -26,7 +27,7 @@ const defaultLayout: Layout = {
|
||||||
export default defineStore('layout', () => {
|
export default defineStore('layout', () => {
|
||||||
const settings = useSettingsStore()
|
const settings = useSettingsStore()
|
||||||
const user = useUserStore()
|
const user = useUserStore()
|
||||||
const state = reactive(defaultLayout)
|
const state = reactive({ ...defaultLayout })
|
||||||
const ready = ref(false)
|
const ready = ref(false)
|
||||||
|
|
||||||
db.getItem<Layout>('layout').then((res) => {
|
db.getItem<Layout>('layout').then((res) => {
|
||||||
|
@ -78,6 +79,21 @@ export default defineStore('layout', () => {
|
||||||
pageList.push(block)
|
pageList.push(block)
|
||||||
globalToast.success('添加成功')
|
globalToast.success('添加成功')
|
||||||
}
|
}
|
||||||
|
const changeBlock = (item: EditBlockItemType, target: string) => {
|
||||||
|
const idx = currentPage.value.list.findIndex((el) => el.id === target)
|
||||||
|
if (idx < 0) return
|
||||||
|
currentPage.value.list.splice(idx, 1, {
|
||||||
|
...currentPage.value.list[idx],
|
||||||
|
label: item.name,
|
||||||
|
color: item.color,
|
||||||
|
text: item.text,
|
||||||
|
link: item.link,
|
||||||
|
background: item.background,
|
||||||
|
icon: item.type === 0 ? item.icon : '',
|
||||||
|
|
||||||
|
})
|
||||||
|
|
||||||
|
}
|
||||||
const openDir = ref('')
|
const openDir = ref('')
|
||||||
// 文件夹只有一个时,将当前界面的文件夹替换为图标
|
// 文件夹只有一个时,将当前界面的文件夹替换为图标
|
||||||
const checkDir = (id: string) => {
|
const checkDir = (id: string) => {
|
||||||
|
@ -112,7 +128,17 @@ export default defineStore('layout', () => {
|
||||||
}
|
}
|
||||||
return block.label || ''
|
return block.label || ''
|
||||||
}
|
}
|
||||||
|
const resetAll = () => {
|
||||||
|
request('GET', '/api/app/desktop').then((res: any) => {
|
||||||
|
if (!res) return
|
||||||
|
state.dir = res.dir
|
||||||
|
state.content = res.content
|
||||||
|
|
||||||
|
}).catch(() => {
|
||||||
|
Object.assign(state, defaultLayout)
|
||||||
|
|
||||||
|
})
|
||||||
|
}
|
||||||
const changeBackground = (url: string) => {
|
const changeBackground = (url: string) => {
|
||||||
state.content[state.current].background = url
|
state.content[state.current].background = url
|
||||||
}
|
}
|
||||||
|
@ -161,6 +187,8 @@ export default defineStore('layout', () => {
|
||||||
openDir,
|
openDir,
|
||||||
checkDir,
|
checkDir,
|
||||||
getLabel,
|
getLabel,
|
||||||
changeBackground
|
changeBackground,
|
||||||
|
resetAll,
|
||||||
|
changeBlock
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
|
@ -2,18 +2,17 @@ import './main.css'
|
||||||
import { createApp } from 'vue'
|
import { createApp } from 'vue'
|
||||||
import { createPinia } from 'pinia'
|
import { createPinia } from 'pinia'
|
||||||
import persist from 'pinia-plugin-persistedstate'
|
import persist from 'pinia-plugin-persistedstate'
|
||||||
|
|
||||||
import App from './App.vue'
|
import App from './App.vue'
|
||||||
import getFp from './utils/getFp'
|
import getFp from './utils/getFp'
|
||||||
import vOutsideClick from './utils/vOutsideClick'
|
import vOutsideClick from './utils/vOutsideClick'
|
||||||
import dayjs from 'dayjs'
|
import dayjs from 'dayjs'
|
||||||
|
import Toast, { useToast } from 'vue-toastification'
|
||||||
import Toast, { useToast, type PluginOptions } from 'vue-toastification'
|
|
||||||
import customParseFormat from 'dayjs/plugin/customParseFormat'
|
import customParseFormat from 'dayjs/plugin/customParseFormat'
|
||||||
import 'vue-toastification/dist/index.css'
|
import 'vue-toastification/dist/index.css'
|
||||||
import 'dayjs/locale/zh-cn'
|
import 'dayjs/locale/zh-cn'
|
||||||
dayjs.locale('zh-cn')
|
dayjs.locale('zh-cn')
|
||||||
|
|
||||||
|
|
||||||
const app = createApp(App)
|
const app = createApp(App)
|
||||||
export const globalToast = useToast()
|
export const globalToast = useToast()
|
||||||
dayjs.extend(customParseFormat)
|
dayjs.extend(customParseFormat)
|
||||||
|
|
|
@ -57,7 +57,7 @@ export default defineComponent(() => {
|
||||||
return layout.state.current === 0
|
return layout.state.current === 0
|
||||||
})
|
})
|
||||||
return () => (
|
return () => (
|
||||||
<div class="fixed left-0 bottom-0 z-40 w-full">
|
<div class="fixed left-0 bottom-0 z-40 w-full rounded-lg">
|
||||||
{/* 背景遮罩 */}
|
{/* 背景遮罩 */}
|
||||||
{show.value && (
|
{show.value && (
|
||||||
<div
|
<div
|
||||||
|
@ -72,13 +72,20 @@ export default defineComponent(() => {
|
||||||
{show.value && (
|
{show.value && (
|
||||||
<div
|
<div
|
||||||
class={clsx(
|
class={clsx(
|
||||||
'absolute left-6 bottom-10 w-[660px] h-[580px] rounded-lg overflow-hidden shadow-2xl flex',
|
'absolute left-6 bottom-10 w-[660px] h-[580px] rounded-2xl shadow-2xl flex',
|
||||||
isGame.value ? 'bg-[#2c2e3e] text-white' : 'text-[#000] bg-white'
|
isGame.value ? 'bg-[#2c2e3e] text-white' : 'text-[#000] bg-white'
|
||||||
)}
|
)}
|
||||||
|
style={
|
||||||
|
isGame.value &&
|
||||||
|
{
|
||||||
|
backgroundImage: `url('/tab/bg/gameModel.png')`,
|
||||||
|
backgroundSize: '100% 100%',
|
||||||
|
}
|
||||||
|
}
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
class={clsx(
|
class={clsx(
|
||||||
'w-[200px] p-4 h-full backdrop-blur flex flex-col',
|
'w-[200px] p-4 h-full flex flex-col rounded-lg',
|
||||||
isGame.value ? 'bg-[#fff]/10 text-white ' : 'bg-[#ebebeb]'
|
isGame.value ? 'bg-[#fff]/10 text-white ' : 'bg-[#ebebeb]'
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
|
@ -134,7 +141,10 @@ export default defineComponent(() => {
|
||||||
<SettingsTab label="问题反馈" path="settings-fallback" />
|
<SettingsTab label="问题反馈" path="settings-fallback" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<Content />
|
<div class={"flex-1 w-0 h-full relative"}>
|
||||||
|
<Content />
|
||||||
|
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</Transition>
|
</Transition>
|
||||||
|
|
|
@ -15,7 +15,7 @@ export default defineComponent(() => {
|
||||||
const router = useRouterStore()
|
const router = useRouterStore()
|
||||||
|
|
||||||
return () => (
|
return () => (
|
||||||
<div class="w-0 h-full flex-grow backdrop-blur">
|
<div class="w-full h-full rounded-xl">
|
||||||
<ThemeProvider>
|
<ThemeProvider>
|
||||||
{router.path === 'settings-user' ? (
|
{router.path === 'settings-user' ? (
|
||||||
<UserPage />
|
<UserPage />
|
||||||
|
|
|
@ -75,11 +75,13 @@ export default defineStore('user', () => {
|
||||||
token,
|
token,
|
||||||
async (val) => {
|
async (val) => {
|
||||||
localStorage.setItem('token', val)
|
localStorage.setItem('token', val)
|
||||||
|
|
||||||
if (!val) {
|
if (!val) {
|
||||||
sendParent(['logout'])
|
sendParent(['logout'])
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
sendParent(['login', val])
|
sendParent(['login', val])
|
||||||
|
|
||||||
const res = await request<UserInfo>('GET', '/api/profile')
|
const res = await request<UserInfo>('GET', '/api/profile')
|
||||||
Object.assign(profile, res)
|
Object.assign(profile, res)
|
||||||
},
|
},
|
||||||
|
|
|
@ -5,6 +5,8 @@ import upload from './upload'
|
||||||
import 'viewerjs/dist/viewer.css'
|
import 'viewerjs/dist/viewer.css'
|
||||||
import { api as showViewer } from 'v-viewer'
|
import { api as showViewer } from 'v-viewer'
|
||||||
import { globalToast } from '@/main'
|
import { globalToast } from '@/main'
|
||||||
|
import useUserStore from '@/user/useUserStore'
|
||||||
|
import useRouterStore from '@/useRouterStore'
|
||||||
|
|
||||||
addIcons(MdUpload, FaEye)
|
addIcons(MdUpload, FaEye)
|
||||||
|
|
||||||
|
@ -33,6 +35,7 @@ export default defineComponent({
|
||||||
},
|
},
|
||||||
setup(props, ctx) {
|
setup(props, ctx) {
|
||||||
let input: HTMLInputElement | null = null
|
let input: HTMLInputElement | null = null
|
||||||
|
|
||||||
return () => (
|
return () => (
|
||||||
<div>
|
<div>
|
||||||
<input
|
<input
|
||||||
|
@ -62,7 +65,12 @@ export default defineComponent({
|
||||||
backgroundImage: `url('${props.value}')`
|
backgroundImage: `url('${props.value}')`
|
||||||
}}
|
}}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
input?.click()
|
if (useUserStore().isLogin) {
|
||||||
|
input?.click()
|
||||||
|
} else {
|
||||||
|
globalToast.warning('请先登录')
|
||||||
|
useRouterStore().go('global-login')
|
||||||
|
}
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{props.value ? (
|
{props.value ? (
|
||||||
|
|
|
@ -1,76 +1,82 @@
|
||||||
import clsx from "clsx";
|
import clsx from 'clsx'
|
||||||
import { computed } from "vue";
|
import { computed } from 'vue'
|
||||||
import { defineComponent } from "vue";
|
import { defineComponent } from 'vue'
|
||||||
import { MdCheck } from "oh-vue-icons/icons";
|
import { MdCheck } from 'oh-vue-icons/icons'
|
||||||
import { addIcons, OhVueIcon } from "oh-vue-icons";
|
import { addIcons, OhVueIcon } from 'oh-vue-icons'
|
||||||
addIcons(MdCheck)
|
addIcons(MdCheck)
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
name: "NativeColorPicker",
|
name: 'NativeColorPicker',
|
||||||
props: {
|
props: {
|
||||||
value: {
|
value: {
|
||||||
type: String,
|
type: String,
|
||||||
default: ''
|
default: ''
|
||||||
},
|
|
||||||
colorList: {
|
|
||||||
type: Array<string>
|
|
||||||
},
|
|
||||||
class: {
|
|
||||||
type: String,
|
|
||||||
},
|
|
||||||
transparent: {
|
|
||||||
type: Boolean,
|
|
||||||
},
|
|
||||||
firstColor: {
|
|
||||||
type: String,
|
|
||||||
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
emits: {
|
colorList: {
|
||||||
"update:value": (_value: string) => true,
|
type: Array<string>
|
||||||
},
|
},
|
||||||
setup(props, context) {
|
class: {
|
||||||
const firstColor = props.value
|
type: String
|
||||||
const colorList = computed(() => {
|
},
|
||||||
let list = [
|
transparent: {
|
||||||
'#ffffff',
|
type: Boolean
|
||||||
'#000000',
|
},
|
||||||
'#FA9B3F',
|
firstColor: {
|
||||||
'#333333',
|
type: String
|
||||||
'#6D7278',
|
},
|
||||||
'#D8D8D8',
|
size: {
|
||||||
'#0091FF',
|
type: Number,
|
||||||
'#B620E0',
|
default: 20
|
||||||
'#F31260'
|
}
|
||||||
]
|
},
|
||||||
if (props.colorList) {
|
emits: {
|
||||||
list = props.colorList
|
'update:value': (_value: string) => true
|
||||||
}
|
},
|
||||||
if (props.transparent) {
|
setup(props, context) {
|
||||||
list.push('transparent')
|
const firstColor = props.value
|
||||||
}
|
const colorList = computed(() => {
|
||||||
if (!list.includes(firstColor)) {
|
let list = [
|
||||||
list.unshift(firstColor)
|
'#ffffff',
|
||||||
}
|
'#000000',
|
||||||
return list
|
'#FA9B3F',
|
||||||
})
|
'#333333',
|
||||||
return () => <div class={clsx("flex items-center gap-x-2 ", props.class)}>
|
'#6D7278',
|
||||||
{
|
'#D8D8D8',
|
||||||
colorList.value.map((item, index) => < div
|
'#0091FF',
|
||||||
key={index}
|
'#B620E0',
|
||||||
onClick={() => context.emit('update:value', item)}
|
'#F31260'
|
||||||
class="text-[12px] cursor-pointer w-[20px] h-[20px] shadow-[0_0_2px_#999] rounded-full"
|
]
|
||||||
style={{
|
if (props.colorList) {
|
||||||
backgroundColor: item === 'transparent' ? '' : item,
|
list = props.colorList
|
||||||
backgroundImage:
|
}
|
||||||
item === 'transparent'
|
if (props.transparent) {
|
||||||
? `linear-gradient(45deg, rgba(0, 0, 0, 0.4) 25%, white 25%, transparent 75%,rgba(0, 0, 0, 0.4) 75%),
|
list.push('transparent')
|
||||||
|
}
|
||||||
|
if (!list.includes(firstColor)) {
|
||||||
|
list.unshift(firstColor)
|
||||||
|
}
|
||||||
|
return list
|
||||||
|
})
|
||||||
|
return () => (
|
||||||
|
<div class={clsx('flex items-center gap-x-2 ', props.class)}>
|
||||||
|
{colorList.value.map((item, index) => (
|
||||||
|
<div
|
||||||
|
key={index}
|
||||||
|
onClick={() => context.emit('update:value', item)}
|
||||||
|
class="text-[12px] cursor-pointer w-[20px] h-[20px] flex items-center justify-center shadow-[0_0_2px_#999] rounded-full"
|
||||||
|
style={{
|
||||||
|
width: props.size + 'px',
|
||||||
|
height: props.size + 'px',
|
||||||
|
backgroundColor: item === 'transparent' ? '' : item,
|
||||||
|
backgroundImage:
|
||||||
|
item === 'transparent'
|
||||||
|
? `linear-gradient(45deg, rgba(0, 0, 0, 0.4) 25%, white 25%, transparent 75%,rgba(0, 0, 0, 0.4) 75%),
|
||||||
linear-gradient(45deg,rgba(0, 0, 0, 0.4) 25%, white 25%, transparent 75%, rgba(0, 0, 0, 0.4) 75%)`
|
linear-gradient(45deg,rgba(0, 0, 0, 0.4) 25%, white 25%, transparent 75%, rgba(0, 0, 0, 0.4) 75%)`
|
||||||
: '',
|
: '',
|
||||||
backgroundPosition: item === 'transparent' ? '0 0, 2px 2px' : '',
|
backgroundPosition: item === 'transparent' ? '0 0, 2px 2px' : '',
|
||||||
backgroundSize: item === 'transparent' ? '4px 4px' : ''
|
backgroundSize: item === 'transparent' ? '4px 4px' : ''
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{/* <Checkmark16Filled
|
{/* <Checkmark16Filled
|
||||||
class="text-[#888]"
|
class="text-[#888]"
|
||||||
: class="props.modelValue === item ? '' : 'hidden'"
|
: class="props.modelValue === item ? '' : 'hidden'"
|
||||||
: style="{
|
: style="{
|
||||||
|
@ -78,32 +84,34 @@ export default defineComponent({
|
||||||
}"
|
}"
|
||||||
>
|
>
|
||||||
</Checkmark16Filled> */}
|
</Checkmark16Filled> */}
|
||||||
{
|
{props.value === item && (
|
||||||
props.value === item &&
|
<OhVueIcon class="text-[#888]" name={MdCheck.name}></OhVueIcon>
|
||||||
<OhVueIcon class="text-[#888]" name={MdCheck.name} ></OhVueIcon>
|
)}
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
|
||||||
}
|
<div
|
||||||
</div >)
|
class="text-[12px] cursor-pointer shadow-[0_0_2px_#999] rounded-full relative"
|
||||||
}
|
style={{
|
||||||
|
width: props.size + 'px',
|
||||||
<div
|
height: props.size + 'px'
|
||||||
class="text-[12px] cursor-pointer w-[20px] h-[20px] shadow-[0_0_2px_#999] rounded-full relative"
|
}}
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
class="w-full h-full rounded-full cursor-pointer"
|
class="w-full h-full rounded-full cursor-pointer"
|
||||||
style="background: conic-gradient(#dd0010, yellow, green, #0091ff, red)"
|
style="background: conic-gradient(#dd0010, yellow, green, #0091ff, red)"
|
||||||
>
|
>
|
||||||
<input
|
<input
|
||||||
|
onInput={(e: any) => {
|
||||||
onInput={(e: any)=> {
|
context.emit('update:value', e?.target.value)
|
||||||
context.emit('update:value', e?.target.value)
|
}}
|
||||||
}}
|
ref="inputRef"
|
||||||
ref="inputRef"
|
type="color"
|
||||||
type="color"
|
style="opacity: 0"
|
||||||
style="opacity: 0"
|
/>
|
||||||
/>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div >
|
)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
|
@ -21,6 +21,7 @@ export default defineComponent({
|
||||||
},
|
},
|
||||||
|
|
||||||
components: {
|
components: {
|
||||||
|
|
||||||
Slider: {
|
Slider: {
|
||||||
railSize: 6
|
railSize: 6
|
||||||
},
|
},
|
||||||
|
|
|
@ -3,141 +3,149 @@ import { defineComponent, ref, watch } from 'vue'
|
||||||
import { VueCropper } from 'vue-cropper'
|
import { VueCropper } from 'vue-cropper'
|
||||||
import 'vue-cropper/dist/index.css'
|
import 'vue-cropper/dist/index.css'
|
||||||
import upload from './upload'
|
import upload from './upload'
|
||||||
import { v4 as uuid } from "uuid"
|
import { v4 as uuid } from 'uuid'
|
||||||
import { MdCroprotateRound, BiPlusLg } from "oh-vue-icons/icons";
|
import { MdCroprotateRound, BiPlusLg } from 'oh-vue-icons/icons'
|
||||||
import { OhVueIcon, addIcons, } from 'oh-vue-icons'
|
import { OhVueIcon, addIcons } from 'oh-vue-icons'
|
||||||
import NativeColorPicker from './NativeColorPicker'
|
import NativeColorPicker from './NativeColorPicker'
|
||||||
|
import useUserStore from '@/user/useUserStore'
|
||||||
|
import { globalToast } from '@/main'
|
||||||
|
import useRouterStore from '@/useRouterStore'
|
||||||
addIcons(MdCroprotateRound, BiPlusLg)
|
addIcons(MdCroprotateRound, BiPlusLg)
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
|
emits: {
|
||||||
|
'update:value': (() => true) as (val: string) => boolean
|
||||||
|
},
|
||||||
|
setup(props, ctx) {
|
||||||
|
const inputRef = ref<HTMLInputElement | null>(null)
|
||||||
|
const showCutModel = ref(false)
|
||||||
|
const originFile = ref('')
|
||||||
|
const fillColor = ref('transparent')
|
||||||
|
const cropper = ref<any>(null)
|
||||||
|
const handleFile = (e: any) => {
|
||||||
|
const target = e.target as HTMLInputElement
|
||||||
|
|
||||||
emits: {
|
const file = target.files?.[0]
|
||||||
'update:value': (() => true) as (val: string) => boolean
|
target.value = ''
|
||||||
},
|
|
||||||
setup(props, ctx) {
|
|
||||||
const inputRef = ref<HTMLInputElement | null>(null)
|
|
||||||
const showCutModel = ref(false)
|
|
||||||
const originFile = ref('')
|
|
||||||
const fillColor = ref('transparent')
|
|
||||||
const cropper = ref<any>(null)
|
|
||||||
const handleFile = (e: any) => {
|
|
||||||
const target = e.target as HTMLInputElement
|
|
||||||
|
|
||||||
const file = target.files?.[0]
|
if (!file) return
|
||||||
target.value = ''
|
// upload(file).then(res => {
|
||||||
|
const reader = new FileReader()
|
||||||
|
reader.readAsDataURL(file)
|
||||||
|
reader.onload = (e: any) => {
|
||||||
|
originFile.value = e?.target.result // 显示原始图片
|
||||||
|
showCutModel.value = true
|
||||||
|
}
|
||||||
|
// emit('update:icon', res || '')
|
||||||
|
// })
|
||||||
|
}
|
||||||
|
const handleFinish = (blob: any) => {
|
||||||
|
// 根据前几个字节推测文件类型(这里只考虑常见的图片类型)
|
||||||
|
|
||||||
if (!file) return
|
// 使用 FileReader 对象读取 Blob 的前几个字节
|
||||||
// upload(file).then(res => {
|
const reader = new FileReader()
|
||||||
const reader = new FileReader()
|
reader.onloadend = function () {
|
||||||
reader.readAsDataURL(file)
|
// 打印 File 对象以验证
|
||||||
reader.onload = (e: any) => {
|
|
||||||
originFile.value = e?.target.result // 显示原始图片
|
|
||||||
showCutModel.value = true
|
|
||||||
}
|
|
||||||
// emit('update:icon', res || '')
|
|
||||||
// })
|
|
||||||
}
|
|
||||||
const handleFinish = (blob: any) => {
|
|
||||||
// 根据前几个字节推测文件类型(这里只考虑常见的图片类型)
|
|
||||||
|
|
||||||
// 使用 FileReader 对象读取 Blob 的前几个字节
|
const fileData = new File([blob], `${uuid()}.png`, {
|
||||||
const reader = new FileReader()
|
type: 'image/jpg',
|
||||||
reader.onloadend = function () {
|
lastModified: Date.now()
|
||||||
// 打印 File 对象以验证
|
})
|
||||||
|
upload(fileData, 'customIcon').then((res) => {
|
||||||
|
showCutModel.value = false
|
||||||
|
|
||||||
const fileData = new File([blob], `${uuid()}.png`, {
|
ctx.emit('update:value', res || '')
|
||||||
type: 'image/jpg',
|
})
|
||||||
lastModified: Date.now()
|
}
|
||||||
})
|
reader.readAsArrayBuffer(blob)
|
||||||
upload(fileData, 'customIcon').then((res) => {
|
}
|
||||||
showCutModel.value = false
|
watch(
|
||||||
|
fillColor,
|
||||||
ctx.emit('update:value', res || '')
|
(e: string) => {
|
||||||
})
|
const elements = document.querySelectorAll('.cropper-crop-box')
|
||||||
}
|
elements.forEach((element: any) => {
|
||||||
reader.readAsArrayBuffer(blob)
|
element.style.backgroundColor = e // 修改颜色为蓝色
|
||||||
}
|
})
|
||||||
watch(
|
},
|
||||||
fillColor,
|
{
|
||||||
(e: string) => {
|
immediate: true
|
||||||
const elements = document.querySelectorAll('.cropper-crop-box')
|
}
|
||||||
elements.forEach((element: any) => {
|
)
|
||||||
element.style.backgroundColor = e // 修改颜色为蓝色
|
return () => (
|
||||||
})
|
<>
|
||||||
},
|
<Modal
|
||||||
{
|
open={showCutModel.value}
|
||||||
immediate: true
|
onCancel={() => (showCutModel.value = false)}
|
||||||
}
|
onOk={() => {
|
||||||
)
|
cropper.value.getCropBlob((blob: any) => {
|
||||||
return () => (
|
handleFinish(blob)
|
||||||
<>
|
})
|
||||||
<Modal
|
}}
|
||||||
open={showCutModel.value}
|
>
|
||||||
onCancel={() => showCutModel.value = false}
|
<div class="w-full flex flex-col items-center gap-y-2">
|
||||||
onOk={() => {
|
<div
|
||||||
cropper.value.getCropBlob((blob: any) => {
|
class="w-[250px] h-[250px]"
|
||||||
handleFinish(blob)
|
style={{
|
||||||
|
backgroundColor: fillColor.value || 'transparent'
|
||||||
})
|
}}
|
||||||
}}
|
>
|
||||||
>
|
<VueCropper
|
||||||
<div class="w-full flex flex-col items-center gap-y-2">
|
ref={cropper}
|
||||||
<div class="w-[250px] h-[250px]" style={{
|
autoCropWidth="250px"
|
||||||
backgroundColor: fillColor.value || 'transparent',
|
autoCropHeight="250px"
|
||||||
}}>
|
mode="contain"
|
||||||
<VueCropper
|
outputType="png"
|
||||||
ref={cropper}
|
img={originFile.value}
|
||||||
autoCropWidth="250px"
|
autoCrop={true}
|
||||||
autoCropHeight="250px"
|
fillColor={fillColor.value}
|
||||||
mode="contain"
|
/>
|
||||||
outputType="png"
|
</div>
|
||||||
img={originFile.value}
|
<div class="flex justify-between w-[250px]">
|
||||||
autoCrop={true}
|
{/* <ArrowRotateClockwise24Regular
|
||||||
fillColor={fillColor.value}
|
|
||||||
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div class="flex justify-between w-[250px]">
|
|
||||||
{/* <ArrowRotateClockwise24Regular
|
|
||||||
class="text-[10px] w-8 h-8 cursor-pointer"
|
class="text-[10px] w-8 h-8 cursor-pointer"
|
||||||
onClick={() => ($refs.cropper as any).rotateRight()}
|
onClick={() => ($refs.cropper as any).rotateRight()}
|
||||||
/> */}
|
/> */}
|
||||||
<span class="flex items-center cursor-pointer"
|
<span
|
||||||
onClick={() => {
|
class="flex items-center cursor-pointer"
|
||||||
cropper.value.rotateRight()
|
onClick={() => {
|
||||||
}}>
|
cropper.value.rotateRight()
|
||||||
<OhVueIcon name="md-croprotate-round" scale={1} fill="#707070" ></OhVueIcon>
|
}}
|
||||||
|
>
|
||||||
</span>
|
<OhVueIcon name="md-croprotate-round" scale={1} fill="#707070"></OhVueIcon>
|
||||||
<span class="flex items-center gap-x-1">
|
</span>
|
||||||
<svg
|
<span class="flex items-center gap-x-1">
|
||||||
onClick={() => cropper.value?.changeScale(1)}
|
<svg
|
||||||
class="icon cursor-pointer"
|
onClick={() => cropper.value?.changeScale(1)}
|
||||||
viewBox="0 0 1024 1024"
|
class="icon cursor-pointer"
|
||||||
width="2rem"
|
viewBox="0 0 1024 1024"
|
||||||
height="2rem"
|
width="2rem"
|
||||||
>
|
height="2rem"
|
||||||
<path
|
>
|
||||||
d="M886.51776 799.66208l-139.93984-139.93984c-10.50624-10.50624-23.90016-16.13824-37.60128-17.44896 42.16832-52.59264 67.54304-119.1936 67.54304-191.6928 0-169.39008-137.80992-307.2-307.2-307.2s-307.2 137.80992-307.2 307.2 137.80992 307.2 307.2 307.2c63.91808 0 123.31008-19.6608 172.52352-53.18656 0.34816 15.23712 6.22592 30.37184 17.85856 42.00448l139.93984 139.93984c11.9808 12.00128 27.72992 18.00192 43.43808 18.00192s31.45728-6.00064 43.43808-18.00192C910.52032 862.55616 910.52032 823.66464 886.51776 799.66208zM469.31968 655.38048c-112.92672 0-204.8-91.87328-204.8-204.8s91.87328-204.8 204.8-204.8 204.8 91.87328 204.8 204.8S582.2464 655.38048 469.31968 655.38048zM610.14016 450.58048c0 22.60992-18.35008 40.96-40.96 40.96l-56.32 0 0 56.32c0 22.60992-18.35008 40.96-40.96 40.96s-40.96-18.35008-40.96-40.96l0-56.32-56.32 0c-22.60992 0-40.96-18.35008-40.96-40.96s18.35008-40.96 40.96-40.96l56.32 0 0-56.32c0-22.60992 18.35008-40.96 40.96-40.96s40.96 18.35008 40.96 40.96l0 56.32 56.32 0C591.79008 409.62048 610.14016 427.95008 610.14016 450.58048z"
|
<path
|
||||||
fill="#707070"
|
d="M886.51776 799.66208l-139.93984-139.93984c-10.50624-10.50624-23.90016-16.13824-37.60128-17.44896 42.16832-52.59264 67.54304-119.1936 67.54304-191.6928 0-169.39008-137.80992-307.2-307.2-307.2s-307.2 137.80992-307.2 307.2 137.80992 307.2 307.2 307.2c63.91808 0 123.31008-19.6608 172.52352-53.18656 0.34816 15.23712 6.22592 30.37184 17.85856 42.00448l139.93984 139.93984c11.9808 12.00128 27.72992 18.00192 43.43808 18.00192s31.45728-6.00064 43.43808-18.00192C910.52032 862.55616 910.52032 823.66464 886.51776 799.66208zM469.31968 655.38048c-112.92672 0-204.8-91.87328-204.8-204.8s91.87328-204.8 204.8-204.8 204.8 91.87328 204.8 204.8S582.2464 655.38048 469.31968 655.38048zM610.14016 450.58048c0 22.60992-18.35008 40.96-40.96 40.96l-56.32 0 0 56.32c0 22.60992-18.35008 40.96-40.96 40.96s-40.96-18.35008-40.96-40.96l0-56.32-56.32 0c-22.60992 0-40.96-18.35008-40.96-40.96s18.35008-40.96 40.96-40.96l56.32 0 0-56.32c0-22.60992 18.35008-40.96 40.96-40.96s40.96 18.35008 40.96 40.96l0 56.32 56.32 0C591.79008 409.62048 610.14016 427.95008 610.14016 450.58048z"
|
||||||
/>
|
fill="#707070"
|
||||||
</svg>
|
/>
|
||||||
<svg
|
</svg>
|
||||||
onClick={() => cropper.value?.changeScale(-1)}
|
<svg
|
||||||
class="icon cursor-pointer"
|
onClick={() => cropper.value?.changeScale(-1)}
|
||||||
viewBox="0 0 1024 1024"
|
class="icon cursor-pointer"
|
||||||
width="2rem"
|
viewBox="0 0 1024 1024"
|
||||||
height="2rem"
|
width="2rem"
|
||||||
>
|
height="2rem"
|
||||||
<path
|
>
|
||||||
d="M886.51776 799.66208l-139.93984-139.93984c-10.50624-10.50624-23.90016-16.13824-37.60128-17.44896 42.16832-52.59264 67.54304-119.1936 67.54304-191.6928 0-169.39008-137.80992-307.2-307.2-307.2s-307.2 137.80992-307.2 307.2 137.80992 307.2 307.2 307.2c63.91808 0 123.31008-19.6608 172.52352-53.18656 0.34816 15.23712 6.22592 30.37184 17.85856 42.00448l139.93984 139.93984c11.9808 12.00128 27.72992 18.00192 43.43808 18.00192s31.45728-6.00064 43.43808-18.00192C910.52032 862.55616 910.52032 823.66464 886.51776 799.66208zM469.31968 655.38048c-112.92672 0-204.8-91.87328-204.8-204.8s91.87328-204.8 204.8-204.8 204.8 91.87328 204.8 204.8S582.2464 655.38048 469.31968 655.38048zM610.14016 450.58048c0 22.60992-18.35008 40.96-40.96 40.96l-194.56 0c-22.60992 0-40.96-18.35008-40.96-40.96s18.35008-40.96 40.96-40.96l194.56 0C591.79008 409.62048 610.14016 427.95008 610.14016 450.58048z"
|
<path
|
||||||
fill="#707070"
|
d="M886.51776 799.66208l-139.93984-139.93984c-10.50624-10.50624-23.90016-16.13824-37.60128-17.44896 42.16832-52.59264 67.54304-119.1936 67.54304-191.6928 0-169.39008-137.80992-307.2-307.2-307.2s-307.2 137.80992-307.2 307.2 137.80992 307.2 307.2 307.2c63.91808 0 123.31008-19.6608 172.52352-53.18656 0.34816 15.23712 6.22592 30.37184 17.85856 42.00448l139.93984 139.93984c11.9808 12.00128 27.72992 18.00192 43.43808 18.00192s31.45728-6.00064 43.43808-18.00192C910.52032 862.55616 910.52032 823.66464 886.51776 799.66208zM469.31968 655.38048c-112.92672 0-204.8-91.87328-204.8-204.8s91.87328-204.8 204.8-204.8 204.8 91.87328 204.8 204.8S582.2464 655.38048 469.31968 655.38048zM610.14016 450.58048c0 22.60992-18.35008 40.96-40.96 40.96l-194.56 0c-22.60992 0-40.96-18.35008-40.96-40.96s18.35008-40.96 40.96-40.96l194.56 0C591.79008 409.62048 610.14016 427.95008 610.14016 450.58048z"
|
||||||
/>
|
fill="#707070"
|
||||||
</svg>
|
/>
|
||||||
</span>
|
</svg>
|
||||||
</div>
|
</span>
|
||||||
<NativeColorPicker value={fillColor.value} onUpdate:value={e => fillColor.value = e} transparent={true} />
|
</div>
|
||||||
{/* {props.colorList && (
|
<NativeColorPicker
|
||||||
|
value={fillColor.value}
|
||||||
|
onUpdate:value={(e) => (fillColor.value = e)}
|
||||||
|
transparent={true}
|
||||||
|
/>
|
||||||
|
{/* {props.colorList && (
|
||||||
<div class="w-[250px] flex justify-center">
|
<div class="w-[250px] flex justify-center">
|
||||||
<NativeColorPicker
|
<NativeColorPicker
|
||||||
colorList={colorList}
|
colorList={colorList}
|
||||||
|
@ -147,23 +155,32 @@ export default defineComponent({
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
)} */}
|
)} */}
|
||||||
|
</div>
|
||||||
</div>
|
</Modal>
|
||||||
</Modal >
|
<div
|
||||||
<div class="w-full h-full bg-white flex items-center justify-center"
|
class="w-full h-full bg-white flex items-center justify-center"
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
inputRef.value?.click?.()
|
if (useUserStore().isLogin) {
|
||||||
}}
|
inputRef.value?.click?.()
|
||||||
>
|
} else {
|
||||||
<OhVueIcon name={BiPlusLg.name} fill='#666666' scale={2}></OhVueIcon>
|
globalToast.warning('请先登录')
|
||||||
</div>
|
useRouterStore().go('global-login')
|
||||||
<input
|
// useAdderPageStore().type = 2
|
||||||
ref={inputRef}
|
}
|
||||||
style={{
|
}}
|
||||||
display: 'none'
|
>
|
||||||
}} accept=".jpg,.jpeg,.png,.svg" type="file" onChange={handleFile} />
|
<OhVueIcon name={BiPlusLg.name} fill="#666666" scale={2}></OhVueIcon>
|
||||||
</>
|
</div>
|
||||||
)
|
<input
|
||||||
}
|
ref={inputRef}
|
||||||
|
style={{
|
||||||
|
display: 'none'
|
||||||
|
}}
|
||||||
|
accept=".jpg,.jpeg,.png,.svg"
|
||||||
|
type="file"
|
||||||
|
onChange={handleFile}
|
||||||
|
/>
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
||||||
})
|
})
|
||||||
|
|
|
@ -13,7 +13,7 @@ export default function useLink(url: Ref<string>) {
|
||||||
const info = reactive<LinkInfo>({
|
const info = reactive<LinkInfo>({
|
||||||
background: '',
|
background: '',
|
||||||
desc: '',
|
desc: '',
|
||||||
icon: '',
|
icon: 'https://oss.goosetab.com/000/user_upload/1/resource/120be9d6-1c68-41ba-b539-570c39ce2421.png',
|
||||||
link: '',
|
link: '',
|
||||||
name: ''
|
name: ''
|
||||||
})
|
})
|
||||||
|
|
|
@ -33,7 +33,9 @@ export default defineComponent(() => {
|
||||||
<span class={'text-[48px] font-extrabold pt-2'}>{store.state.select.format('D')}</span>
|
<span class={'text-[48px] font-extrabold pt-2'}>{store.state.select.format('D')}</span>
|
||||||
<span>{store.state.select.format('ddd')}</span>
|
<span>{store.state.select.format('ddd')}</span>
|
||||||
<span>
|
<span>
|
||||||
本年第{store.state.select.diff(store.state.select.set('date', 1).set('months', 0), 'weeks')}周,第
|
本年第
|
||||||
|
{store.state.select.diff(store.state.select.set('date', 1).set('months', 0), 'weeks')}
|
||||||
|
周,第
|
||||||
{store.state.select.diff(store.state.select.set('date', 1).set('month', 0), 'day')}天
|
{store.state.select.diff(store.state.select.set('date', 1).set('month', 0), 'day')}天
|
||||||
</span>
|
</span>
|
||||||
<div class={'w-[138px] h-[1px] flex-shrink-0 my-7 bg-black/10 relative'}></div>
|
<div class={'w-[138px] h-[1px] flex-shrink-0 my-7 bg-black/10 relative'}></div>
|
||||||
|
@ -56,10 +58,8 @@ export default defineComponent(() => {
|
||||||
>
|
>
|
||||||
宜
|
宜
|
||||||
</div>
|
</div>
|
||||||
<div class={'flex flex-col text-[#666] text-[14px]'}>
|
<div class={'flex-1 w-0 text-[#666] text-[14px]'}>
|
||||||
{lunar.value.getDayYi().map((item: any) => {
|
{lunar.value.getDayYi().join(',')}
|
||||||
return <div class={'flex items-center'}>{item},</div>
|
|
||||||
})}
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class={'flex w-full gap-x-5'}>
|
<div class={'flex w-full gap-x-5'}>
|
||||||
|
@ -70,10 +70,8 @@ export default defineComponent(() => {
|
||||||
>
|
>
|
||||||
忌
|
忌
|
||||||
</div>
|
</div>
|
||||||
<div class={'flex flex-col text-[#666] text-[14px]'}>
|
<div class={'flex-1 w-0 text-[#666] text-[14px]'}>
|
||||||
{lunar.value.getDayJi().map((item: any) => {
|
{lunar.value.getDayJi().join(',')}
|
||||||
return <div class={'flex items-center'}>{item},</div>
|
|
||||||
})}
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -158,8 +156,10 @@ export default defineComponent(() => {
|
||||||
dayjs(),
|
dayjs(),
|
||||||
'day'
|
'day'
|
||||||
),
|
),
|
||||||
'border-transparent border-solid': !el.day.isSame(dayjs(), 'day') && !el.day.isSame(store.state.select, 'day'),
|
'border-transparent border-solid':
|
||||||
'border-[#76d7f2] border-solid border-[1px]': !el.day.isSame(dayjs(), 'day') && el.day.isSame(store.state.select, 'day'),
|
!el.day.isSame(dayjs(), 'day') && !el.day.isSame(store.state.select, 'day'),
|
||||||
|
'border-[#76d7f2] border-solid border-[1px]':
|
||||||
|
!el.day.isSame(dayjs(), 'day') && el.day.isSame(store.state.select, 'day')
|
||||||
}
|
}
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
|
|
|
@ -3,9 +3,10 @@ import request from '@/utils/request'
|
||||||
import { addIcons, OhVueIcon } from 'oh-vue-icons'
|
import { addIcons, OhVueIcon } from 'oh-vue-icons'
|
||||||
import { computed, defineComponent, onMounted, ref, watch, type CSSProperties } from 'vue'
|
import { computed, defineComponent, onMounted, ref, watch, type CSSProperties } from 'vue'
|
||||||
import { FaChevronLeft } from 'oh-vue-icons/icons'
|
import { FaChevronLeft } from 'oh-vue-icons/icons'
|
||||||
import PlayImg from "~/icons/game_video_bg_play.png"
|
import PlayImg from '~/icons/game_video_bg_play.png'
|
||||||
import type { CarouselRef } from 'ant-design-vue/es/carousel'
|
import type { CarouselRef } from 'ant-design-vue/es/carousel'
|
||||||
import { randomNum } from '@/utils/tool'
|
import { randomNum } from '@/utils/tool'
|
||||||
|
import jump from '@/utils/jump'
|
||||||
addIcons(FaChevronLeft)
|
addIcons(FaChevronLeft)
|
||||||
interface Owner {
|
interface Owner {
|
||||||
face: string
|
face: string
|
||||||
|
@ -56,11 +57,19 @@ export default defineComponent(() => {
|
||||||
})
|
})
|
||||||
return () => (
|
return () => (
|
||||||
<div class="w-full h-full p-2 bg-[#17212d] relative ">
|
<div class="w-full h-full p-2 bg-[#17212d] relative ">
|
||||||
<img src={PlayImg} class={"absolute z-10 w-[40px] bg-[#ffffff24] rounded-lg backdrop-blur-sm left-1/2 top-1/2 -translate-x-1/2 -translate-y-1/2"}></img>
|
<img
|
||||||
|
src={PlayImg}
|
||||||
|
class={
|
||||||
|
'absolute z-10 w-[40px] bg-[#ffffff24] rounded-lg backdrop-blur-sm left-1/2 top-1/2 -translate-x-1/2 -translate-y-1/2'
|
||||||
|
}
|
||||||
|
></img>
|
||||||
|
|
||||||
{
|
{
|
||||||
<div
|
<div
|
||||||
class={'w-full h-full rounded-xl relative group'}
|
class={'w-full h-full rounded-xl relative group'}
|
||||||
|
onClick={() => {
|
||||||
|
jump('https://www.bilibili.com/video/av' + current.value?.aid)
|
||||||
|
}}
|
||||||
style={{
|
style={{
|
||||||
backgroundImage: `url('${current.value?.pic}')`,
|
backgroundImage: `url('${current.value?.pic}')`,
|
||||||
backgroundSize: 'cover',
|
backgroundSize: 'cover',
|
||||||
|
@ -68,7 +77,6 @@ export default defineComponent(() => {
|
||||||
backgroundRepeat: 'no-repeat'
|
backgroundRepeat: 'no-repeat'
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
|
|
||||||
<div
|
<div
|
||||||
class={
|
class={
|
||||||
'absolute bottom-0 left-1/2 -translate-x-1/2 pb-2 w-[300px] flex flex-col text-white '
|
'absolute bottom-0 left-1/2 -translate-x-1/2 pb-2 w-[300px] flex flex-col text-white '
|
||||||
|
|
|
@ -10,7 +10,7 @@ export default defineComponent(() => {
|
||||||
<img class={'w-[48px] h-[48px]'} src={'/tab/icons/hot_information_icon.png'}></img>
|
<img class={'w-[48px] h-[48px]'} src={'/tab/icons/hot_information_icon.png'}></img>
|
||||||
<div class={'flex-1 flex justify-center'}>
|
<div class={'flex-1 flex justify-center'}>
|
||||||
<div class="flex-col flex">
|
<div class="flex-col flex">
|
||||||
<span class={'text-[16px] text-white'}>热点折扣</span>
|
<span class={'text-[16px] text-white'}>热点资讯</span>
|
||||||
<div class={'flex items-center text-[#fffc] text-[12px] '}>
|
<div class={'flex items-center text-[#fffc] text-[12px] '}>
|
||||||
立即查看
|
立即查看
|
||||||
<div>
|
<div>
|
||||||
|
|
|
@ -3,6 +3,7 @@ import { useWeApplyStore } from './useWeApplyStore'
|
||||||
import { addIcons, OhVueIcon } from 'oh-vue-icons'
|
import { addIcons, OhVueIcon } from 'oh-vue-icons'
|
||||||
import { HiChevronDown } from 'oh-vue-icons/icons'
|
import { HiChevronDown } from 'oh-vue-icons/icons'
|
||||||
import clsx from 'clsx'
|
import clsx from 'clsx'
|
||||||
|
import jump from '@/utils/jump'
|
||||||
addIcons(HiChevronDown)
|
addIcons(HiChevronDown)
|
||||||
export default defineComponent(() => {
|
export default defineComponent(() => {
|
||||||
const store = useWeApplyStore()
|
const store = useWeApplyStore()
|
||||||
|
@ -13,7 +14,7 @@ export default defineComponent(() => {
|
||||||
const computIsBottom = (ref: any) => {
|
const computIsBottom = (ref: any) => {
|
||||||
if (ref) {
|
if (ref) {
|
||||||
const { scrollTop, clientHeight, scrollHeight } = ref
|
const { scrollTop, clientHeight, scrollHeight } = ref
|
||||||
return scrollTop + clientHeight >= scrollHeight -8
|
return scrollTop + clientHeight >= scrollHeight - 8
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
const handleGameScroll = () => {
|
const handleGameScroll = () => {
|
||||||
|
@ -74,7 +75,12 @@ export default defineComponent(() => {
|
||||||
{store.state.list
|
{store.state.list
|
||||||
.filter((val) => val.type === 'game')
|
.filter((val) => val.type === 'game')
|
||||||
.map((item) => (
|
.map((item) => (
|
||||||
<div class={'flex gap-x-2 items-center'}>
|
<div
|
||||||
|
class={'flex gap-x-2 items-center'}
|
||||||
|
onClick={() => {
|
||||||
|
jump(item.url)
|
||||||
|
}}
|
||||||
|
>
|
||||||
<img src={item.icon} alt="game icon" class={'w-[37px] h-[37px] rounded'}></img>
|
<img src={item.icon} alt="game icon" class={'w-[37px] h-[37px] rounded'}></img>
|
||||||
<div class={'flex-1 flex flex-col overflow-hidden'}>
|
<div class={'flex-1 flex flex-col overflow-hidden'}>
|
||||||
<span class={'text-white text-[14px]'}>{item.name}</span>
|
<span class={'text-white text-[14px]'}>{item.name}</span>
|
||||||
|
@ -107,7 +113,6 @@ export default defineComponent(() => {
|
||||||
if (workRef.value) {
|
if (workRef.value) {
|
||||||
workRef.value.scrollTop += 20
|
workRef.value.scrollTop += 20
|
||||||
handleWorkScroll()
|
handleWorkScroll()
|
||||||
|
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
|
@ -125,7 +130,12 @@ export default defineComponent(() => {
|
||||||
{store.state.list
|
{store.state.list
|
||||||
.filter((val) => val.type === 'work')
|
.filter((val) => val.type === 'work')
|
||||||
.map((item) => (
|
.map((item) => (
|
||||||
<div class={'flex gap-x-2 items-center'}>
|
<div
|
||||||
|
class={'flex gap-x-2 items-center'}
|
||||||
|
onClick={() => {
|
||||||
|
jump(item.url)
|
||||||
|
}}
|
||||||
|
>
|
||||||
<img src={item.icon} alt="game icon" class={'w-[37px] h-[37px] rounded'}></img>
|
<img src={item.icon} alt="game icon" class={'w-[37px] h-[37px] rounded'}></img>
|
||||||
<div class={'flex-1 flex flex-col overflow-hidden'}>
|
<div class={'flex-1 flex flex-col overflow-hidden'}>
|
||||||
<span class={'text-white text-[14px]'}>{item.name}</span>
|
<span class={'text-white text-[14px]'}>{item.name}</span>
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
import { defineComponent } from 'vue'
|
import { defineComponent } from 'vue'
|
||||||
import { formatSeconds } from '@/utils/tool'
|
import { formatSeconds } from '@/utils/tool'
|
||||||
import PlayStartImg from "~/icons/work/start.png"
|
import PlayStartImg from '~/icons/work/start.png'
|
||||||
import returnImg from "~/icons/work/return.png"
|
import returnImg from '~/icons/work/return.png'
|
||||||
import endImg from "~/icons/work/tomotoIconEnd.png"
|
import endImg from '~/icons/work/tomotoIconEnd.png'
|
||||||
import PlusImg from "~/icons/work/tomatoIconAdd.png"
|
import PlusImg from '~/icons/work/tomatoIconAdd.png'
|
||||||
import dayjs from 'dayjs'
|
import dayjs from 'dayjs'
|
||||||
import { Tooltip } from 'ant-design-vue'
|
import { Tooltip } from 'ant-design-vue'
|
||||||
import useTomatoStore from './useTomatoStore'
|
import useTomatoStore from './useTomatoStore'
|
||||||
|
@ -11,41 +11,54 @@ import useTomatoStore from './useTomatoStore'
|
||||||
export default defineComponent(() => {
|
export default defineComponent(() => {
|
||||||
const store = useTomatoStore()
|
const store = useTomatoStore()
|
||||||
return () => (
|
return () => (
|
||||||
<div class="w-full h-full flex relative p-6 justify-between" style={{
|
<div
|
||||||
background: `url('https://newfatfox.oss-cn-beijing.aliyuncs.com/admin/tomotoback.png')`,
|
class="w-full h-full flex relative p-6 justify-between"
|
||||||
backgroundSize: 'cover',
|
style={{
|
||||||
backgroundRepeat: 'no-repeat'
|
background: `url('https://newfatfox.oss-cn-beijing.aliyuncs.com/admin/tomotoback.png')`,
|
||||||
}}>
|
backgroundSize: 'cover',
|
||||||
<div class={"bg-[#0000004d] absolute top-0 left-0 right-0 bottom-0 "}></div>
|
backgroundRepeat: 'no-repeat'
|
||||||
<div class={"w-[115px] h-full flex flex-col items-center z-10 text-white"}>
|
}}
|
||||||
<div class={"w-full bg-white/20 text-center rounded text-[14px]"}>无目标</div>
|
>
|
||||||
<span class={"text-[42px] mb-1"}>
|
<div class={'bg-[#0000004d] absolute top-0 left-0 right-0 bottom-0 '}></div>
|
||||||
{
|
<div class={'w-[115px] h-full flex flex-col items-center z-10 text-white'}>
|
||||||
store.state.beginTime < 0 ? '15:00' : formatSeconds(store.remainingTime)
|
<div
|
||||||
|
class={
|
||||||
|
'w-full bg-white/20 text-center rounded text-[14px] overflow-hidden text-ellipsis whitespace-nowrap'
|
||||||
}
|
}
|
||||||
|
>
|
||||||
|
{store.state.list[0] ? store.state.list[0].title : '无目标'}
|
||||||
|
</div>
|
||||||
|
<span class={'text-[42px] mb-1'}>
|
||||||
|
{store.state.beginTime < 0 ? '15:00' : formatSeconds(store.remainingTime)}
|
||||||
</span>
|
</span>
|
||||||
<span class={"text-[14px]"}>你今日已完成
|
<span class={'text-[14px]'}>
|
||||||
<span class={"text-[#76e6ff] mx-1"}>
|
你今日已完成
|
||||||
|
<span class={'text-[#76e6ff] mx-1'}>
|
||||||
|
{store.state.timeList.filter((val) => dayjs(val).isSame(dayjs(), 'day')).length}
|
||||||
{store.state.timeList.filter(val => dayjs(val).isSame(dayjs(), 'day')).length}</span>
|
</span>
|
||||||
</span>
|
</span>
|
||||||
<span class={"text-[14px]"}>个番茄时</span>
|
<span class={'text-[14px]'}>个番茄时</span>
|
||||||
</div>
|
</div>
|
||||||
<div class={"flex flex-col justify-end"}>
|
<div class={'flex flex-col justify-end'}>
|
||||||
<div class={"flex gap-x-3 "}>
|
<div class={'flex gap-x-3 '}>
|
||||||
<Tooltip title={"沉浸模式"}>
|
<Tooltip title={'沉浸模式'}>
|
||||||
<div
|
<div
|
||||||
onClick={(e) => {
|
onClick={(e) => {
|
||||||
e.stopPropagation()
|
e.stopPropagation()
|
||||||
store.openFullscreen = true
|
store.openFullscreen = true
|
||||||
}}
|
}}
|
||||||
class={"w-[42px] h-[42px] bg-white/40 backdrop-blur-md flex items-center justify-center rounded-xl"}>
|
class={
|
||||||
<img src={returnImg} alt="start" class={"w-[18px]"}></img>
|
'w-[42px] h-[42px] bg-white/40 backdrop-blur-md flex items-center justify-center rounded-xl'
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<img src={returnImg} alt="start" class={'w-[18px]'}></img>
|
||||||
</div>
|
</div>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
<Tooltip title={"开始"}>
|
<Tooltip title={'开始'}>
|
||||||
<div class={"w-[42px] h-[42px] bg-white/40 backdrop-blur-md flex items-center justify-center rounded-xl"}
|
<div
|
||||||
|
class={
|
||||||
|
'w-[42px] h-[42px] bg-white/40 backdrop-blur-md flex items-center justify-center rounded-xl'
|
||||||
|
}
|
||||||
onClick={(e) => {
|
onClick={(e) => {
|
||||||
e.stopPropagation()
|
e.stopPropagation()
|
||||||
if (store.state.isStart) {
|
if (store.state.isStart) {
|
||||||
|
@ -53,30 +66,30 @@ export default defineComponent(() => {
|
||||||
} else {
|
} else {
|
||||||
store.beginTomatoTime()
|
store.beginTomatoTime()
|
||||||
store.openFullscreen = true
|
store.openFullscreen = true
|
||||||
|
|
||||||
}
|
}
|
||||||
}}>
|
}}
|
||||||
{
|
>
|
||||||
store.state.isStart ?
|
{store.state.isStart ? (
|
||||||
<img src={endImg} alt="start" class={"w-[18px]"}></img>
|
<img src={endImg} alt="start" class={'w-[18px]'}></img>
|
||||||
:
|
) : (
|
||||||
<img src={PlayStartImg} alt="start" class={"w-[18px]"}></img>
|
<img src={PlayStartImg} alt="start" class={'w-[18px]'}></img>
|
||||||
|
)}
|
||||||
}
|
|
||||||
</div>
|
</div>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
<Tooltip title={"添加目标"}>
|
<Tooltip title={'添加目标'}>
|
||||||
<div class={"w-[42px] h-[42px] bg-white/40 backdrop-blur-md flex items-center justify-center rounded-xl"}
|
<div
|
||||||
|
class={
|
||||||
|
'w-[42px] h-[42px] bg-white/40 backdrop-blur-md flex items-center justify-center rounded-xl'
|
||||||
|
}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
store.openShowModel = null
|
store.openShowModel = null
|
||||||
|
|
||||||
}, 300)
|
}, 300)
|
||||||
}}>
|
}}
|
||||||
<img src={PlusImg} alt="start" class={"w-[18px]"}></img>
|
>
|
||||||
|
<img src={PlusImg} alt="start" class={'w-[18px]'}></img>
|
||||||
</div>
|
</div>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -130,7 +130,8 @@ const EditContent = defineComponent(() => {
|
||||||
{!form.value.remindTime ? '选择时间' : dayjs(form.value.remindTime).format('HH:mm')}
|
{!form.value.remindTime ? '选择时间' : dayjs(form.value.remindTime).format('HH:mm')}
|
||||||
<img src={DownImg} class={"w-[12px] object-cover "}></img>
|
<img src={DownImg} class={"w-[12px] object-cover "}></img>
|
||||||
<TimePicker
|
<TimePicker
|
||||||
|
format={'HH:mm'}
|
||||||
|
showNow={false}
|
||||||
class={'absolute opacity-0 left-0 top-0 w-full h-full'}
|
class={'absolute opacity-0 left-0 top-0 w-full h-full'}
|
||||||
onChange={(e) => {
|
onChange={(e) => {
|
||||||
form.value.remindTime = dayjs(e).valueOf()
|
form.value.remindTime = dayjs(e).valueOf()
|
||||||
|
|
|
@ -66,7 +66,8 @@ export default defineComponent(() => {
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class={"w-[16px] hidden group-hover:flex z-10 h-[16px] absolute -right-1 -top-1 bg-[#ddd] rounded-full items-center justify-center"}
|
<div class={"w-[16px] hidden group-hover:flex z-10 h-[16px] absolute -right-1 -top-1 bg-[#ddd] rounded-full items-center justify-center"}
|
||||||
onClick={() => {
|
onClick={(e) => {
|
||||||
|
e.stopPropagation()
|
||||||
const idx = store.state.list.findIndex(val => val.id === item.id)
|
const idx = store.state.list.findIndex(val => val.id === item.id)
|
||||||
if (idx !== -1) {
|
if (idx !== -1) {
|
||||||
store.state.list.splice(0, 1)
|
store.state.list.splice(0, 1)
|
||||||
|
|
|
@ -5,8 +5,18 @@ module.exports = {
|
||||||
content: ['./index.html', './src/**/*.{vue,js,ts,jsx,tsx}'],
|
content: ['./index.html', './src/**/*.{vue,js,ts,jsx,tsx}'],
|
||||||
darkMode: 'class',
|
darkMode: 'class',
|
||||||
theme: {
|
theme: {
|
||||||
extend: {}
|
extend: {
|
||||||
|
animation: {
|
||||||
|
wiggle: 'wiggle .3s ease-in-out infinite'
|
||||||
|
},
|
||||||
|
keyframes: {
|
||||||
|
wiggle: {
|
||||||
|
'0%, 100%': { transform: 'rotate(-1deg)' },
|
||||||
|
'50%': { transform: 'rotate(1deg)' }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
plugins: [require('@tailwindcss/typography')]
|
plugins: [require('@tailwindcss/typography')]
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue