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 useRouterStore from './useRouterStore'
|
||||
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 Fox = asyncLoader(() => import('./fox'))
|
||||
const settings = useSettingsStore()
|
||||
|
@ -28,51 +30,53 @@ const router = useRouterStore()
|
|||
const layout = useLayoutStore()
|
||||
</script>
|
||||
<template>
|
||||
<div class="fixed left-0 top-0 w-full h-screen style-root" @contextmenu.prevent>
|
||||
<Header />
|
||||
<Background
|
||||
@dblclick="
|
||||
() => {
|
||||
layout.state.simple = !layout.state.simple
|
||||
}
|
||||
"
|
||||
/>
|
||||
<GLobalModal />
|
||||
<SettingsOverlay />
|
||||
<SettingsButton />
|
||||
<ConfigProvider :locale="zhCN">
|
||||
<div class="fixed left-0 top-0 w-full h-screen style-root" @contextmenu.prevent>
|
||||
<Header />
|
||||
<Background
|
||||
@dblclick="
|
||||
() => {
|
||||
layout.state.simple = !layout.state.simple
|
||||
}
|
||||
"
|
||||
/>
|
||||
<GLobalModal />
|
||||
<SettingsOverlay />
|
||||
<SettingsButton />
|
||||
|
||||
<Sider
|
||||
v-if="
|
||||
!layout.state.simple ||
|
||||
(layout.state.simple && settings.state.simpleModeShowString.includes('showSider'))
|
||||
"
|
||||
/>
|
||||
<LoginModal v-if="router.path === 'global-login'" />
|
||||
<Transition>
|
||||
<Grid v-if="layout.ready && !layout.state.simple" />
|
||||
</Transition>
|
||||
<Dock
|
||||
v-if="
|
||||
!layout.state.simple ||
|
||||
(layout.state.simple && settings.state.simpleModeShowString.includes('showDock'))
|
||||
"
|
||||
/>
|
||||
<div
|
||||
class="fixed z-40 right-[14%] top-8"
|
||||
v-if="
|
||||
!layout.state.simple ||
|
||||
(layout.state.simple && settings.state.simpleModeShowString.includes('showPet'))
|
||||
"
|
||||
>
|
||||
<Fox />
|
||||
<Sider
|
||||
v-if="
|
||||
!layout.state.simple ||
|
||||
(layout.state.simple && settings.state.simpleModeShowString.includes('showSider'))
|
||||
"
|
||||
/>
|
||||
<LoginModal v-if="router.path === 'global-login'" />
|
||||
<Transition>
|
||||
<Grid v-if="layout.ready && !layout.state.simple" />
|
||||
</Transition>
|
||||
<Dock
|
||||
v-if="
|
||||
!layout.state.simple ||
|
||||
(layout.state.simple && settings.state.simpleModeShowString.includes('showDock'))
|
||||
"
|
||||
/>
|
||||
<div
|
||||
class="fixed z-40 right-[14%] top-8"
|
||||
v-if="
|
||||
!layout.state.simple ||
|
||||
(layout.state.simple && settings.state.simpleModeShowString.includes('showPet'))
|
||||
"
|
||||
>
|
||||
<Fox />
|
||||
</div>
|
||||
<DirModal />
|
||||
<GlobalMenu />
|
||||
<WelcomePage></WelcomePage>
|
||||
<TomatoPage></TomatoPage>
|
||||
|
||||
<BackupRecovery v-if="router.path === 'global-backup'"></BackupRecovery>
|
||||
</div>
|
||||
<DirModal />
|
||||
<GlobalMenu />
|
||||
<WelcomePage></WelcomePage>
|
||||
<TomatoPage></TomatoPage>
|
||||
|
||||
<BackupRecovery v-if="router.path === 'global-backup'"></BackupRecovery>
|
||||
</div>
|
||||
</ConfigProvider>
|
||||
</template>
|
||||
|
||||
<style lang="less">
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
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 { MdClose, MdOpeninfull, MdClosefullscreen } from 'oh-vue-icons/icons'
|
||||
import asyncLoader from './utils/asyncLoader'
|
||||
|
@ -18,18 +18,31 @@ export default defineComponent(() => {
|
|||
router.path.startsWith('widget-') ||
|
||||
router.path === 'global-search' ||
|
||||
router.path === 'global-adder' ||
|
||||
router.path === 'global-background'
|
||||
router.path === 'global-background'
|
||||
)
|
||||
const full = ref(false)
|
||||
watch(router, () => {
|
||||
full.value = false
|
||||
})
|
||||
|
||||
onMounted(() => {
|
||||
window.addEventListener('keydown', handleKeydown)
|
||||
})
|
||||
onUnmounted(() => {
|
||||
// 清理事件监听
|
||||
window.removeEventListener('keydown', handleKeydown)
|
||||
})
|
||||
function handleKeydown(e: any) {
|
||||
if (e.key === 'Escape') {
|
||||
router.back()
|
||||
}
|
||||
}
|
||||
return () => (
|
||||
<div
|
||||
class="fixed left-0 top-0 z-50 w-full"
|
||||
onContextmenu={(e) => e.stopPropagation()}
|
||||
onKeydown={(e) => e.stopPropagation()}
|
||||
onKeydown={(e) => {
|
||||
e.stopPropagation()
|
||||
}}
|
||||
>
|
||||
{/* 背景遮罩 */}
|
||||
<Transition>
|
||||
|
@ -43,7 +56,7 @@ export default defineComponent(() => {
|
|||
)}
|
||||
</Transition>
|
||||
{/* 弹框主体 */}
|
||||
<Transition >
|
||||
<Transition>
|
||||
{show.value && (
|
||||
<div
|
||||
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 />
|
||||
) : router.path === 'global-background' ? (
|
||||
<BackgroundSwtich />
|
||||
) :
|
||||
router.path.startsWith('widget-') ? (
|
||||
(() => {
|
||||
const name = router.path.split('-')[1]
|
||||
const selected = widgetList.find((el) => el.name === name)
|
||||
if (!selected)
|
||||
return (
|
||||
<div class="w-full h-full flex justify-center items-center text-black/80">
|
||||
组件维护中
|
||||
</div>
|
||||
)
|
||||
const compo = selected.modal
|
||||
return <compo />
|
||||
})()
|
||||
) : null}
|
||||
) : router.path.startsWith('widget-') ? (
|
||||
(() => {
|
||||
const name = router.path.split('-')[1]
|
||||
const selected = widgetList.find((el) => el.name === name)
|
||||
if (!selected)
|
||||
return (
|
||||
<div class="w-full h-full flex justify-center items-center text-black/80">
|
||||
组件维护中
|
||||
</div>
|
||||
)
|
||||
const compo = selected.modal
|
||||
return <compo />
|
||||
})()
|
||||
) : null}
|
||||
</Transition>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import { defineStore } from 'pinia'
|
||||
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 useLayoutStore from './useLayoutStore'
|
||||
import widgetList from '@/widgets'
|
||||
|
@ -17,10 +17,11 @@ const defaultDisplay = {
|
|||
export const useMenuStore = defineStore('menu', () => {
|
||||
const display = reactive(defaultDisplay)
|
||||
const selectPage = ref<{
|
||||
id: string,
|
||||
label: string,
|
||||
name: string,
|
||||
id: string
|
||||
label: string
|
||||
name: string
|
||||
}>()
|
||||
const isEditPage = ref(false)
|
||||
const showEditPage = ref(false)
|
||||
const mPos = {
|
||||
x: 0,
|
||||
|
@ -49,6 +50,7 @@ export const useMenuStore = defineStore('menu', () => {
|
|||
display,
|
||||
open,
|
||||
dismiss,
|
||||
isEditPage,
|
||||
show,
|
||||
selectPage,
|
||||
showEditPage
|
||||
|
@ -83,8 +85,8 @@ const Item = defineComponent({
|
|||
style={
|
||||
props.noStyle
|
||||
? {
|
||||
padding: '6px 10px'
|
||||
}
|
||||
padding: '6px 10px'
|
||||
}
|
||||
: {}
|
||||
}
|
||||
onClick={() => {
|
||||
|
@ -155,6 +157,15 @@ export default defineComponent(() => {
|
|||
>
|
||||
添加快游戏
|
||||
</Item>
|
||||
<Item
|
||||
noStyle
|
||||
onClick={() => {
|
||||
menu.isEditPage = true
|
||||
menu.dismiss()
|
||||
}}
|
||||
>
|
||||
编辑主页
|
||||
</Item>
|
||||
<Item
|
||||
noStyle
|
||||
onClick={() => {
|
||||
|
@ -164,6 +175,15 @@ export default defineComponent(() => {
|
|||
>
|
||||
更换壁纸
|
||||
</Item>
|
||||
<Item
|
||||
noStyle
|
||||
onClick={() => {
|
||||
window.location.reload()
|
||||
menu.dismiss()
|
||||
}}
|
||||
>
|
||||
刷新
|
||||
</Item>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
@ -207,10 +227,18 @@ export default defineComponent(() => {
|
|||
alert
|
||||
onClick={() => {
|
||||
// 删除链接
|
||||
const idx = layout.currentPage.list.findIndex((el) => el.id === block.id)
|
||||
if (idx < 0) return
|
||||
layout.currentPage.list.splice(idx, 1)
|
||||
console.log(menu.selectPage)
|
||||
|
||||
const idx = layout.state.content[layout.state.current].pages.findIndex(
|
||||
(el) => el.id === menu.selectPage?.id
|
||||
)
|
||||
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 (
|
||||
<>
|
||||
<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
|
||||
alert
|
||||
onClick={() => {
|
||||
|
|
|
@ -81,10 +81,11 @@ export default defineComponent(() => {
|
|||
const isGame = computed(() => layout.state.current === 0)
|
||||
const store = useAdderPageStore()
|
||||
const addTo = ref(layout.state.currentPage)
|
||||
|
||||
provide(AddToToken, addTo)
|
||||
onUnmounted(() => {
|
||||
store.type = 1
|
||||
})
|
||||
// onUnmounted(() => {
|
||||
// store.type = 1
|
||||
// })
|
||||
return () => (
|
||||
<div
|
||||
class={clsx(
|
||||
|
@ -144,9 +145,31 @@ export default defineComponent(() => {
|
|||
}))}
|
||||
/>
|
||||
</Form.Item>
|
||||
<Form.Item>
|
||||
<Input.Search class="w-[200px]" placeholder="搜索组件或网站" />
|
||||
</Form.Item>
|
||||
{store.type !== 2 && (
|
||||
<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>
|
||||
<div class="w-full h-0 flex-grow p-6">
|
||||
<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 { Button, Form, Input, InputGroup } from 'ant-design-vue'
|
||||
import { Form, Input } from 'ant-design-vue'
|
||||
import { OhVueIcon, addIcons } from 'oh-vue-icons'
|
||||
import { MdUpload, MdImage, MdCheck } from 'oh-vue-icons/icons'
|
||||
import useLink from '../../utils/useLink'
|
||||
import { CheckOutlined } from '@ant-design/icons-vue'
|
||||
import { v4 as uuid } from 'uuid'
|
||||
import type { Block } from '../layout.types'
|
||||
import { ColorPicker } from 'vue3-colorpicker'
|
||||
import 'vue3-colorpicker/style.css'
|
||||
import { globalToast } from '@/main'
|
||||
import UploadAndCut from '@/utils/UploadAndCut'
|
||||
import { AddToToken } from './AdderPage'
|
||||
|
||||
import useAdderPageStore, { type EditBlockItemType } from './useAdderPageStore'
|
||||
import useRouterStore from '@/useRouterStore'
|
||||
import NativeColorPicker from '@/utils/NativeColorPicker'
|
||||
addIcons(MdUpload, MdImage, MdCheck)
|
||||
|
||||
const TypeSelector = defineComponent({
|
||||
|
@ -47,6 +47,7 @@ const TypeSelector = defineComponent({
|
|||
'update:icon': (() => true) as (val: string) => boolean
|
||||
},
|
||||
setup(props, ctx) {
|
||||
const layout = useLayoutStore()
|
||||
return () => (
|
||||
<div class="flex gap-4">
|
||||
<div class={'flex justify-center flex-col items-center gap-y-1'}>
|
||||
|
@ -60,7 +61,7 @@ const TypeSelector = defineComponent({
|
|||
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 && (
|
||||
<div
|
||||
class={
|
||||
|
@ -113,10 +114,11 @@ const TypeSelector = defineComponent({
|
|||
ctx.emit('update:icon', e)
|
||||
}}
|
||||
></ImageUploader> */}
|
||||
<UploadAndCut onUpdate:value={(e)=> {
|
||||
<UploadAndCut
|
||||
onUpdate:value={(e) => {
|
||||
ctx.emit('update:icon', e)
|
||||
|
||||
}}></UploadAndCut>
|
||||
}}
|
||||
></UploadAndCut>
|
||||
</div>
|
||||
<span class={'text-[12px]'}>自定义</span>
|
||||
</div>
|
||||
|
@ -134,9 +136,21 @@ export default defineComponent(() => {
|
|||
color: 'rgb(255,255,255)',
|
||||
icon: '',
|
||||
type: 0 // 0 默认,1 文字
|
||||
})
|
||||
} as EditBlockItemType)
|
||||
const isGame = computed(() => layout.state.current === 0)
|
||||
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(
|
||||
() => form.link,
|
||||
(val, _, onCleanup) => {
|
||||
|
@ -155,6 +169,20 @@ export default defineComponent(() => {
|
|||
if (val.name) form.name = val.name
|
||||
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)
|
||||
return () => (
|
||||
<div
|
||||
|
@ -163,19 +191,24 @@ export default defineComponent(() => {
|
|||
(isGame.value ? 'bg-white/20' : 'bg-white/70')
|
||||
}
|
||||
>
|
||||
<Form labelCol={{ span: 4 }} labelAlign="left">
|
||||
<Form.Item label="地址">
|
||||
<InputGroup compact style="display:flex">
|
||||
<Input
|
||||
v-model:value={form.link}
|
||||
placeholder="搜索想要添加的网址导航"
|
||||
class="w-0 flex-grow"
|
||||
/>
|
||||
<Button>获取地址</Button>
|
||||
</InputGroup>
|
||||
<Form>
|
||||
<Form.Item label="地址" class={'relative'}>
|
||||
<Input
|
||||
v-model:value={form.link}
|
||||
placeholder="搜索想要添加的网址导航"
|
||||
class={isGame?.value ? '' : ' bg-black/10 '}
|
||||
/>
|
||||
<span
|
||||
class={'absolute right-[-70px] top-1/2 -translate-y-1/2 cursor-pointer'}
|
||||
onClick={() => {
|
||||
debounced.value = debounced.value + ' '
|
||||
}}
|
||||
>
|
||||
获取地址
|
||||
</span>
|
||||
</Form.Item>
|
||||
<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 label="图标">
|
||||
<TypeSelector
|
||||
|
@ -189,70 +222,81 @@ export default defineComponent(() => {
|
|||
</Form.Item>
|
||||
{form.type === 1 && (
|
||||
<>
|
||||
<div class="flex">
|
||||
<Form.Item
|
||||
label="文字颜色"
|
||||
class="w-0 flex-grow"
|
||||
labelCol={{
|
||||
span: 8
|
||||
<Form.Item label="图标背景">
|
||||
<NativeColorPicker
|
||||
size={30}
|
||||
colorList={[
|
||||
'rgb(227, 127, 53)',
|
||||
'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
|
||||
}}
|
||||
>
|
||||
<ColorPicker
|
||||
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>
|
||||
></NativeColorPicker>
|
||||
</Form.Item>
|
||||
<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 label=" " colon={false}>
|
||||
<Button
|
||||
type="primary"
|
||||
size="large"
|
||||
icon={<CheckOutlined />}
|
||||
<button
|
||||
class={
|
||||
'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'
|
||||
}
|
||||
style={{
|
||||
background: 'linear-gradient(180deg,#ffaa4e 0%,#ff6227 100%)'
|
||||
}}
|
||||
onClick={() => {
|
||||
if (form.type === 1 && !form.text && !form.name) {
|
||||
globalToast.error('文字图标请至少填写文字或者名称')
|
||||
return
|
||||
}
|
||||
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
|
||||
if (!form.link) {
|
||||
globalToast.warning('请输入网站链接')
|
||||
return
|
||||
}
|
||||
if (!form.name) {
|
||||
globalToast.warning('请输入网站名称')
|
||||
return
|
||||
}
|
||||
if (store.editBlockItem !== null) {
|
||||
console.log(123)
|
||||
layout.changeBlock(form, store.editBlockItem.id)
|
||||
useRouterStore().back()
|
||||
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>
|
||||
</div>
|
||||
|
|
|
@ -9,6 +9,7 @@ import { frontAddress, ossBase } from '@/config'
|
|||
import dayjs from 'dayjs'
|
||||
import { generateRandomString } from '@/utils/tool'
|
||||
import MD5 from 'crypto-js/md5'
|
||||
import useAdderPageStore from './useAdderPageStore'
|
||||
export const SECRET = 'A1Cv12olxT12dOE3xA1vPA=='
|
||||
export const URL_ADDRESS = 'http://newfatfox.oss-cn-beijing.aliyuncs.com'
|
||||
export interface GameType {
|
||||
|
@ -126,9 +127,9 @@ export const GameItem = defineComponent({
|
|||
id: uuid(),
|
||||
link: !props.content.rom.startsWith('http')
|
||||
? `${frontAddress}/emu/#/home?params=${JSON.stringify({
|
||||
...props.content,
|
||||
rom: ossBase + '/' + props.content.rom
|
||||
})}`
|
||||
...props.content,
|
||||
rom: ossBase + '/' + props.content.rom
|
||||
})}`
|
||||
: props.content.rom,
|
||||
name: '',
|
||||
label: props.content.name,
|
||||
|
@ -159,6 +160,7 @@ export default defineComponent(() => {
|
|||
const isGame = computed(() => layout.state.current === 0)
|
||||
const appList = ref<GameType[]>([])
|
||||
const selectType = ref('fc')
|
||||
const store = useAdderPageStore()
|
||||
const fetchGame = async (page: number) => {
|
||||
const parems = `nonce=${generateRandomString(8)}&pid=PIDc8uT24mpo×tamp=${dayjs().unix()}`
|
||||
const sign = MD5(parems + SECRET).toString()
|
||||
|
@ -169,11 +171,11 @@ export default defineComponent(() => {
|
|||
return res.data.items
|
||||
}
|
||||
watch(
|
||||
selectType,
|
||||
[selectType, () => store.gameSearch],
|
||||
(val) => {
|
||||
loading.value = true
|
||||
if (val !== 'yiqiyoo') {
|
||||
request<GameType[]>('GET', `/api/games?type=${val}`)
|
||||
if (val[0] !== 'yiqiyoo') {
|
||||
request<GameType[]>('GET', `/api/games?type=${val[0]}&keyword=${val[1]}`)
|
||||
.then((res) => {
|
||||
appList.value = res
|
||||
})
|
||||
|
@ -212,41 +214,44 @@ export default defineComponent(() => {
|
|||
)
|
||||
return () => (
|
||||
<div class={'w-full h-full flex flex-col gap-y-4'}>
|
||||
<div class={'w-full '}>
|
||||
<CategoryTab
|
||||
list={DefautGameTypeList}
|
||||
selectType={selectType.value}
|
||||
onUpdate:type={(e) => {
|
||||
selectType.value = e
|
||||
}}
|
||||
v-slots={{
|
||||
select: (text: string) => (
|
||||
<button
|
||||
class={clsx(
|
||||
'px-[20px] text-[14px] py-1 items-center justify-center duration-75 shrink-0 flex rounded-2xl whitespace-nowrap ',
|
||||
isGame.value
|
||||
? 'bg-white/30 text-white bg-gradient-to-b from-[#ffaa4e] to-[#ff6227]'
|
||||
: 'text-white bg-[#D6D6D6] bg-gradient-to-b from-[#ffaa4e] to-[#ff6227]'
|
||||
)}
|
||||
>
|
||||
{text}
|
||||
</button>
|
||||
),
|
||||
unSelect: (text: string) => (
|
||||
<button
|
||||
class={clsx(
|
||||
'px-[20px] text-[14px] py-1 items-center justify-center duration-75 shrink-0 flex rounded-2xl whitespace-nowrap',
|
||||
isGame.value
|
||||
? ' text-[#999] bg-white/10 hover:bg-white/20'
|
||||
: 'text-[#666] hover:bg-black/10 bg-black/[0.05] hover:bg-[#f0ecec]'
|
||||
)}
|
||||
>
|
||||
{text}
|
||||
</button>
|
||||
)
|
||||
}}
|
||||
></CategoryTab>
|
||||
</div>
|
||||
{!store.gameSearch && (
|
||||
<div class={'w-full '}>
|
||||
<CategoryTab
|
||||
list={DefautGameTypeList}
|
||||
selectType={selectType.value}
|
||||
onUpdate:type={(e) => {
|
||||
selectType.value = e
|
||||
}}
|
||||
v-slots={{
|
||||
select: (text: string) => (
|
||||
<button
|
||||
class={clsx(
|
||||
'px-[20px] text-[14px] py-1 items-center justify-center duration-75 shrink-0 flex rounded-2xl whitespace-nowrap ',
|
||||
isGame.value
|
||||
? 'bg-white/30 text-white bg-gradient-to-b from-[#ffaa4e] to-[#ff6227]'
|
||||
: 'text-white bg-[#D6D6D6] bg-gradient-to-b from-[#ffaa4e] to-[#ff6227]'
|
||||
)}
|
||||
>
|
||||
{text}
|
||||
</button>
|
||||
),
|
||||
unSelect: (text: string) => (
|
||||
<button
|
||||
class={clsx(
|
||||
'px-[20px] text-[14px] py-1 items-center justify-center duration-75 shrink-0 flex rounded-2xl whitespace-nowrap',
|
||||
isGame.value
|
||||
? ' text-[#999] bg-white/10 hover:bg-white/20'
|
||||
: 'text-[#666] hover:bg-black/10 bg-black/[0.05] hover:bg-[#f0ecec]'
|
||||
)}
|
||||
>
|
||||
{text}
|
||||
</button>
|
||||
)
|
||||
}}
|
||||
></CategoryTab>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div class={'h-0 flex-1 w-full'}>
|
||||
<div class={'w-full h-full overflow-y-scroll scrollbar-hide'}>
|
||||
{!loading.value ? (
|
||||
|
|
|
@ -1,26 +1,11 @@
|
|||
import CategoryTab from '@/utils/CategoryTab'
|
||||
import request from '@/utils/request'
|
||||
import { computed, defineComponent, inject, onMounted, ref, watch } from 'vue'
|
||||
import type { BackgroundType } from '../background/BackgroundSwtich'
|
||||
import { computed, defineComponent, inject } from 'vue'
|
||||
import clsx from 'clsx'
|
||||
import useLayoutStore from '../useLayoutStore'
|
||||
import { AddToToken } from './AdderPage'
|
||||
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({
|
||||
props: {
|
||||
content: {
|
||||
|
@ -34,10 +19,13 @@ export const LinkItem = defineComponent({
|
|||
const addTo = inject(AddToToken)
|
||||
return () => (
|
||||
<div
|
||||
class={clsx(' w-full h-full rounded-lg relative flex flex-col justify-between overflow-hidden shadow p-4', {
|
||||
'bg-white/20': isGame.value,
|
||||
'bg-white/80': !isGame.value
|
||||
})}
|
||||
class={clsx(
|
||||
' w-full h-full rounded-lg relative flex flex-col justify-between overflow-hidden shadow p-4',
|
||||
{
|
||||
'bg-white/20': isGame.value,
|
||||
'bg-white/80': !isGame.value
|
||||
}
|
||||
)}
|
||||
key={props.content.name}
|
||||
>
|
||||
<div
|
||||
|
@ -110,6 +98,9 @@ export const LinkItem = defineComponent({
|
|||
},
|
||||
addTo?.value
|
||||
)
|
||||
if (addTo?.value) {
|
||||
layout.state.currentPage = addTo?.value
|
||||
}
|
||||
}}
|
||||
>
|
||||
添加
|
||||
|
@ -122,78 +113,55 @@ export const LinkItem = defineComponent({
|
|||
|
||||
export default defineComponent(() => {
|
||||
const layout = useLayoutStore()
|
||||
const loading = ref(false)
|
||||
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
|
||||
})
|
||||
})
|
||||
watch(selectType, (val) => {
|
||||
loading.value = true
|
||||
const store = useAdderPageStore()
|
||||
|
||||
request<HotAppType[]>('GET', `/api/app/hotApps?hotAppsId=${val}`)
|
||||
.then((res) => {
|
||||
appList.value = res
|
||||
})
|
||||
.finally(() => {
|
||||
setTimeout(() => {
|
||||
loading.value = false
|
||||
}, 200)
|
||||
})
|
||||
})
|
||||
return () => (
|
||||
<div class={'w-full h-full flex flex-col gap-y-4'}>
|
||||
<div class={'w-full '}>
|
||||
<CategoryTab
|
||||
list={categoryList.value}
|
||||
selectType={selectType.value}
|
||||
onUpdate:type={(e) => {
|
||||
selectType.value = e
|
||||
}}
|
||||
v-slots={{
|
||||
select: (text: string) => (
|
||||
<button
|
||||
class={clsx(
|
||||
'px-[20px] text-[14px] py-1 items-center justify-center duration-75 shrink-0 flex rounded-2xl whitespace-nowrap ',
|
||||
isGame.value
|
||||
? 'bg-white/30 text-white bg-gradient-to-b from-[#ffaa4e] to-[#ff6227]'
|
||||
: 'text-white bg-[#D6D6D6] bg-gradient-to-b from-[#ffaa4e] to-[#ff6227]'
|
||||
)}
|
||||
>
|
||||
{text}
|
||||
</button>
|
||||
),
|
||||
unSelect: (text: string) => (
|
||||
<button
|
||||
class={clsx(
|
||||
'px-[20px] text-[14px] py-1 items-center justify-center duration-75 shrink-0 flex rounded-2xl whitespace-nowrap',
|
||||
isGame.value
|
||||
? ' text-[#999] bg-white/10 hover:bg-white/20'
|
||||
: 'text-[#666] hover:bg-black/10 bg-black/[0.05] hover:bg-[#f0ecec]'
|
||||
)}
|
||||
>
|
||||
{text}
|
||||
</button>
|
||||
)
|
||||
}}
|
||||
></CategoryTab>
|
||||
</div>
|
||||
{!store.isSearch && (
|
||||
<div class={'w-full '}>
|
||||
<CategoryTab
|
||||
list={store.categoryList}
|
||||
selectType={store.selectType}
|
||||
onUpdate:type={(e) => {
|
||||
store.selectType = e
|
||||
}}
|
||||
v-slots={{
|
||||
select: (text: string) => (
|
||||
<button
|
||||
class={clsx(
|
||||
'px-[20px] text-[14px] py-1 items-center justify-center duration-75 shrink-0 flex rounded-2xl whitespace-nowrap ',
|
||||
isGame.value
|
||||
? 'bg-white/30 text-white bg-gradient-to-b from-[#ffaa4e] to-[#ff6227]'
|
||||
: 'text-white bg-[#D6D6D6] bg-gradient-to-b from-[#ffaa4e] to-[#ff6227]'
|
||||
)}
|
||||
>
|
||||
{text}
|
||||
</button>
|
||||
),
|
||||
unSelect: (text: string) => (
|
||||
<button
|
||||
class={clsx(
|
||||
'px-[20px] text-[14px] py-1 items-center justify-center duration-75 shrink-0 flex rounded-2xl whitespace-nowrap',
|
||||
isGame.value
|
||||
? ' text-[#999] bg-white/10 hover:bg-white/20'
|
||||
: 'text-[#666] hover:bg-black/10 bg-black/[0.05] hover:bg-[#f0ecec]'
|
||||
)}
|
||||
>
|
||||
{text}
|
||||
</button>
|
||||
)
|
||||
}}
|
||||
></CategoryTab>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div class={'h-0 flex-1 w-full'}>
|
||||
<div class={'w-full h-full overflow-y-scroll scrollbar-hide'}>
|
||||
{!loading.value ? (
|
||||
{!store.loading ? (
|
||||
<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>
|
||||
))}
|
||||
</div>
|
||||
|
|
|
@ -4,6 +4,7 @@ import widgetList, { type Widget } from '@/widgets'
|
|||
import clsx from 'clsx'
|
||||
import { AddToToken } from './AdderPage'
|
||||
import { v4 as uuid } from 'uuid'
|
||||
import useAdderPageStore from './useAdderPageStore'
|
||||
|
||||
export const WidgetItem = defineComponent({
|
||||
props: {
|
||||
|
@ -32,10 +33,7 @@ export const WidgetItem = defineComponent({
|
|||
key={props.content.name}
|
||||
>
|
||||
<div class="flex">
|
||||
<img
|
||||
src={props.content.icon}
|
||||
class="w-[58px] h-[58px] bg-cover rounded-lg shadow-lg"
|
||||
/>
|
||||
<img 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={clsx('text text-sm mb-[2px]', {
|
||||
|
@ -120,18 +118,18 @@ export const WidgetItem = defineComponent({
|
|||
|
||||
export default defineComponent(() => {
|
||||
const layout = useLayoutStore()
|
||||
const store = useAdderPageStore()
|
||||
return () => (
|
||||
<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">
|
||||
{
|
||||
layout.state.current !== 1 ?
|
||||
widgetList.filter(val => val.name !== 'tomato_work').map((el) => (
|
||||
<WidgetItem content={el} key={el.name} />
|
||||
)) :
|
||||
widgetList.map((el) => (
|
||||
<WidgetItem content={el} key={el.name} />
|
||||
))
|
||||
}
|
||||
{layout.state.current !== 1
|
||||
? widgetList
|
||||
.filter((val) => val.name !== 'tomato_work')
|
||||
.filter((val) => val.label.includes(store.widgetSearchWords))
|
||||
.map((el) => <WidgetItem content={el} key={el.name} />)
|
||||
: widgetList
|
||||
.filter((val) => val.label.includes(store.widgetSearchWords))
|
||||
.map((el) => <WidgetItem content={el} key={el.name} />)}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
|
|
|
@ -1,9 +1,95 @@
|
|||
import { defineStore } from "pinia";
|
||||
import { ref } from "vue";
|
||||
import request from '@/utils/request'
|
||||
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", () => {
|
||||
const type = ref(1)
|
||||
return {
|
||||
type
|
||||
selectType.value = res[0].id
|
||||
})
|
||||
watch(selectType, (val) => {
|
||||
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 useLayoutStore from '../useLayoutStore'
|
||||
import { DownloadOutlined, EyeInvisibleOutlined, SwapOutlined } from '@ant-design/icons-vue'
|
||||
|
@ -23,7 +23,7 @@ export default defineComponent(() => {
|
|||
const settings = useSettingsStore()
|
||||
return () => (
|
||||
<div class="absolute left-0 top-0 w-full h-full p-4 overflow-y-auto scrollbar-hide">
|
||||
<SettingItem
|
||||
{/* <SettingItem
|
||||
noBg
|
||||
v-slots={{
|
||||
label: () => <div>所属模式</div>
|
||||
|
@ -38,7 +38,7 @@ export default defineComponent(() => {
|
|||
]}
|
||||
v-model:value={selected.value}
|
||||
/>
|
||||
</SettingItem>
|
||||
</SettingItem> */}
|
||||
<div class="px-4">
|
||||
<div class="h-[180px]">
|
||||
{layout.background.video && layout.background.type !== 'own' ? (
|
||||
|
|
|
@ -154,7 +154,6 @@ export default defineComponent(() => {
|
|||
fill={isGame.value ? 'white' : 'black'}
|
||||
name={BiChevronDown.name}
|
||||
class="group pointer-events-none absolute top-1/2 right-2.5 -translate-y-1/2 size-4 "
|
||||
aria-hidden="true"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -33,11 +33,7 @@ export default defineComponent(() => {
|
|||
<div
|
||||
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',
|
||||
setting.state.showDock === 'auto'
|
||||
? show.value
|
||||
? 'bottom-4'
|
||||
: '-bottom-[90px]'
|
||||
: 'bottom-4'
|
||||
setting.state.showDock === 'auto' ? show.value ? 'bottom-4' : 'bottom-[-90px]' : 'bottom-4'
|
||||
)}
|
||||
ref={container}
|
||||
onMouseleave={() => {
|
||||
|
@ -58,8 +54,8 @@ export default defineComponent(() => {
|
|||
style={
|
||||
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 || ''}
|
||||
|
|
|
@ -7,6 +7,9 @@ import LinkBlock from './LinkBlock'
|
|||
import DirBlock from './DirBlock'
|
||||
import WidgetBlock from './WidgetBlock'
|
||||
import useSettingsStore from '@/settings/useSettingsStore'
|
||||
import { useMenuStore } from '../GlobalMenu'
|
||||
import { block } from '@milkdown/kit/plugin/block'
|
||||
import clsx from 'clsx'
|
||||
|
||||
export default defineComponent({
|
||||
props: {
|
||||
|
@ -22,11 +25,15 @@ export default defineComponent({
|
|||
setup(props) {
|
||||
const settings = useSettingsStore()
|
||||
const layout = useLayoutStore()
|
||||
const menu = useMenuStore()
|
||||
let it: any = 0
|
||||
const hover = ref(false)
|
||||
return () => (
|
||||
<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}
|
||||
id={props.block.id}
|
||||
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
|
||||
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={{
|
||||
borderRadius: `calc(var(--block-radius) * var(--block-size))`
|
||||
borderRadius: `calc(var(--block-radius) * var(--block-size))`,
|
||||
transition: 'transform 0.2s'
|
||||
}}
|
||||
onContextmenu={(e) => {
|
||||
e.stopPropagation()
|
||||
|
@ -140,7 +175,7 @@ export default defineComponent({
|
|||
</div>
|
||||
{settings.state.showBlockLabel && (
|
||||
<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)"
|
||||
>
|
||||
{layout.getLabel(props.block)}
|
||||
|
|
|
@ -31,7 +31,7 @@ export default defineComponent({
|
|||
jump(props.block.link)
|
||||
}}
|
||||
style={{
|
||||
backgroundColor: props.block.background || 'white',
|
||||
backgroundColor: props.block.text ? props.block.background || 'white' : 'transparent',
|
||||
color: props.block.color || 'black',
|
||||
backgroundImage: props.block.icon ? `url('${props.block.icon}')` : '',
|
||||
fontSize: props.dock ? '16px' : props.brief ? '12px' : 'calc(var(--block-size) / 5)'
|
||||
|
|
|
@ -1,23 +1,63 @@
|
|||
import SettingItem from '@/settings/SettingItem'
|
||||
import useSettingsStore from '@/settings/useSettingsStore'
|
||||
import { Button, Switch } from 'ant-design-vue'
|
||||
import { Button } from 'ant-design-vue'
|
||||
import clsx from 'clsx'
|
||||
import { defineComponent } from 'vue'
|
||||
import { computed, defineComponent, ref } from 'vue'
|
||||
import useLayoutStore from '../useLayoutStore'
|
||||
|
||||
export default defineComponent({
|
||||
setup() {
|
||||
const settings = useSettingsStore()
|
||||
const open = ref(false)
|
||||
const layout = useLayoutStore()
|
||||
const isGame = computed(() => {
|
||||
return layout.state.current === 0
|
||||
})
|
||||
return () => (
|
||||
<div class="p-4 flex flex-col ">
|
||||
<SettingItem
|
||||
v-slots={{
|
||||
label: () => <div>重置</div>
|
||||
label: () => <div >重置</div>
|
||||
}}
|
||||
>
|
||||
<Button type="primary">立即重置</Button>
|
||||
<Button type="primary" onClick={() => {
|
||||
open.value = true
|
||||
}}>立即重置</Button>
|
||||
</SettingItem>
|
||||
<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 { OhVueIcon, addIcons } from 'oh-vue-icons'
|
||||
import { MdAdd } from 'oh-vue-icons/icons'
|
||||
|
@ -15,11 +15,21 @@ export default defineComponent(() => {
|
|||
const layout = useLayoutStore()
|
||||
const settings = useSettingsStore()
|
||||
const router = useRouterStore()
|
||||
const scrollRef = ref<any>()
|
||||
const container = useSortable(
|
||||
computed(() => layout.currentPage.list),
|
||||
ref('page')
|
||||
)
|
||||
|
||||
watch(
|
||||
() => layout.currentPage.list.length,
|
||||
(cur, old) => {
|
||||
if (cur > old) {
|
||||
if (scrollRef.value) {
|
||||
scrollRef.value.scrollTop = scrollRef.value.scrollHeight
|
||||
}
|
||||
}
|
||||
}
|
||||
)
|
||||
listenParent('openSetting', (d) => {
|
||||
if (d === 'profile') {
|
||||
router.go('settings-user')
|
||||
|
@ -30,6 +40,7 @@ export default defineComponent(() => {
|
|||
})
|
||||
return () => (
|
||||
<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)]"
|
||||
onScroll={(e) => {
|
||||
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 ThemeProvider from '@/utils/ThemeProvider'
|
||||
import { globalToast } from '@/main'
|
||||
import useUserStore from '@/user/useUserStore'
|
||||
import useRouterStore from '@/useRouterStore'
|
||||
const SearchAdder = asyncLoader(() => import('./SearchAdder'))
|
||||
const SearchItem = defineComponent({
|
||||
props: {
|
||||
|
@ -157,7 +159,12 @@ export default defineComponent(() => {
|
|||
block
|
||||
icon={<PlusOutlined />}
|
||||
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
|
||||
? {}
|
||||
: {
|
||||
top: layout.isCompact ? '40px' : layout.state.simple ? '230px' : '172px',
|
||||
width: settings.state.searchWidth + 'rem'
|
||||
}
|
||||
top: layout.isCompact ? '40px' : layout.state.simple ? '230px' : '172px',
|
||||
width: settings.state.searchWidth + 'rem'
|
||||
}
|
||||
}
|
||||
>
|
||||
<div
|
||||
class={clsx(
|
||||
'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'
|
||||
)}
|
||||
style={{
|
||||
opacity: search.focus ? 1 : settings.state.searchOpacity,
|
||||
borderRadius: settings.state.searchRadius + 'px'
|
||||
}}
|
||||
>
|
||||
|
@ -58,7 +59,7 @@ export default defineComponent(
|
|||
onContextmenu={(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"
|
||||
placeholder={`输入搜索 ${searchConfig.current.name}`}
|
||||
placeholder={`请输入搜索内容`}
|
||||
/>
|
||||
</div>
|
||||
<Transition name="searchContent">{search.showSearchConfig && <SearchConfig />}</Transition>
|
||||
|
|
|
@ -42,11 +42,12 @@ const Item = defineComponent({
|
|||
return () => (
|
||||
<div
|
||||
onContextmenu={(e) => {
|
||||
if (!props.id) return
|
||||
useMenuStore().open('page')
|
||||
useMenuStore().selectPage = {
|
||||
id: props.id,
|
||||
label: props.label,
|
||||
name: props.name,
|
||||
name: props.name
|
||||
}
|
||||
e.stopPropagation()
|
||||
e.preventDefault()
|
||||
|
@ -106,6 +107,10 @@ export default defineComponent(() => {
|
|||
<Transition>
|
||||
{layout.ready && (
|
||||
<div
|
||||
onContextmenu={(e)=> {
|
||||
e.stopPropagation()
|
||||
e.preventDefault()
|
||||
}}
|
||||
class={clsx(
|
||||
'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'
|
||||
|
@ -194,6 +199,10 @@ export default defineComponent(() => {
|
|||
{/* 添加页面 */}
|
||||
{menu.showEditPage && (
|
||||
<div
|
||||
onContextmenu={(e) => {
|
||||
e.stopPropagation()
|
||||
e.preventDefault()
|
||||
}}
|
||||
class={clsx(
|
||||
'absolute bottom-0 w-56 rounded-lg p-4 bg-white/40 backdrop-blur shadow-lg',
|
||||
settings.state.siderDirection === 'left' ? 'left-[70px]' : 'right-[70px]'
|
||||
|
@ -230,7 +239,16 @@ export default defineComponent(() => {
|
|||
if (menu.selectPage) {
|
||||
menu.selectPage.name = selected.value.name
|
||||
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 {
|
||||
layout.currentMode.pages.push({
|
||||
list: [],
|
||||
|
@ -241,11 +259,10 @@ export default defineComponent(() => {
|
|||
}
|
||||
|
||||
menu.showEditPage = false
|
||||
|
||||
}}
|
||||
>
|
||||
<OhVueIcon name="px-check" />
|
||||
{menu.selectPage ? "修改页面" : "添加页面"}
|
||||
{menu.selectPage ? '修改页面' : '添加页面'}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
|
|
@ -8,6 +8,7 @@ import jump from '@/utils/jump'
|
|||
import useSettingsStore from '@/settings/useSettingsStore'
|
||||
import useUserStore from '@/user/useUserStore'
|
||||
import request from '@/utils/request'
|
||||
import type { EditBlockItemType } from './adder/useAdderPageStore'
|
||||
|
||||
const defaultLayout: Layout = {
|
||||
content: [
|
||||
|
@ -26,7 +27,7 @@ const defaultLayout: Layout = {
|
|||
export default defineStore('layout', () => {
|
||||
const settings = useSettingsStore()
|
||||
const user = useUserStore()
|
||||
const state = reactive(defaultLayout)
|
||||
const state = reactive({ ...defaultLayout })
|
||||
const ready = ref(false)
|
||||
|
||||
db.getItem<Layout>('layout').then((res) => {
|
||||
|
@ -78,6 +79,21 @@ export default defineStore('layout', () => {
|
|||
pageList.push(block)
|
||||
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 checkDir = (id: string) => {
|
||||
|
@ -112,7 +128,17 @@ export default defineStore('layout', () => {
|
|||
}
|
||||
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) => {
|
||||
state.content[state.current].background = url
|
||||
}
|
||||
|
@ -161,6 +187,8 @@ export default defineStore('layout', () => {
|
|||
openDir,
|
||||
checkDir,
|
||||
getLabel,
|
||||
changeBackground
|
||||
changeBackground,
|
||||
resetAll,
|
||||
changeBlock
|
||||
}
|
||||
})
|
||||
|
|
|
@ -2,18 +2,17 @@ import './main.css'
|
|||
import { createApp } from 'vue'
|
||||
import { createPinia } from 'pinia'
|
||||
import persist from 'pinia-plugin-persistedstate'
|
||||
|
||||
import App from './App.vue'
|
||||
import getFp from './utils/getFp'
|
||||
import vOutsideClick from './utils/vOutsideClick'
|
||||
import dayjs from 'dayjs'
|
||||
|
||||
import Toast, { useToast, type PluginOptions } from 'vue-toastification'
|
||||
import Toast, { useToast } from 'vue-toastification'
|
||||
import customParseFormat from 'dayjs/plugin/customParseFormat'
|
||||
import 'vue-toastification/dist/index.css'
|
||||
import 'dayjs/locale/zh-cn'
|
||||
dayjs.locale('zh-cn')
|
||||
|
||||
|
||||
const app = createApp(App)
|
||||
export const globalToast = useToast()
|
||||
dayjs.extend(customParseFormat)
|
||||
|
|
|
@ -57,7 +57,7 @@ export default defineComponent(() => {
|
|||
return layout.state.current === 0
|
||||
})
|
||||
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 && (
|
||||
<div
|
||||
|
@ -72,13 +72,20 @@ export default defineComponent(() => {
|
|||
{show.value && (
|
||||
<div
|
||||
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'
|
||||
)}
|
||||
style={
|
||||
isGame.value &&
|
||||
{
|
||||
backgroundImage: `url('/tab/bg/gameModel.png')`,
|
||||
backgroundSize: '100% 100%',
|
||||
}
|
||||
}
|
||||
>
|
||||
<div
|
||||
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]'
|
||||
)}
|
||||
>
|
||||
|
@ -134,7 +141,10 @@ export default defineComponent(() => {
|
|||
<SettingsTab label="问题反馈" path="settings-fallback" />
|
||||
</div>
|
||||
</div>
|
||||
<Content />
|
||||
<div class={"flex-1 w-0 h-full relative"}>
|
||||
<Content />
|
||||
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</Transition>
|
||||
|
|
|
@ -15,7 +15,7 @@ export default defineComponent(() => {
|
|||
const router = useRouterStore()
|
||||
|
||||
return () => (
|
||||
<div class="w-0 h-full flex-grow backdrop-blur">
|
||||
<div class="w-full h-full rounded-xl">
|
||||
<ThemeProvider>
|
||||
{router.path === 'settings-user' ? (
|
||||
<UserPage />
|
||||
|
|
|
@ -75,11 +75,13 @@ export default defineStore('user', () => {
|
|||
token,
|
||||
async (val) => {
|
||||
localStorage.setItem('token', val)
|
||||
|
||||
if (!val) {
|
||||
sendParent(['logout'])
|
||||
return
|
||||
}
|
||||
sendParent(['login', val])
|
||||
|
||||
const res = await request<UserInfo>('GET', '/api/profile')
|
||||
Object.assign(profile, res)
|
||||
},
|
||||
|
|
|
@ -5,6 +5,8 @@ import upload from './upload'
|
|||
import 'viewerjs/dist/viewer.css'
|
||||
import { api as showViewer } from 'v-viewer'
|
||||
import { globalToast } from '@/main'
|
||||
import useUserStore from '@/user/useUserStore'
|
||||
import useRouterStore from '@/useRouterStore'
|
||||
|
||||
addIcons(MdUpload, FaEye)
|
||||
|
||||
|
@ -33,6 +35,7 @@ export default defineComponent({
|
|||
},
|
||||
setup(props, ctx) {
|
||||
let input: HTMLInputElement | null = null
|
||||
|
||||
return () => (
|
||||
<div>
|
||||
<input
|
||||
|
@ -62,7 +65,12 @@ export default defineComponent({
|
|||
backgroundImage: `url('${props.value}')`
|
||||
}}
|
||||
onClick={() => {
|
||||
input?.click()
|
||||
if (useUserStore().isLogin) {
|
||||
input?.click()
|
||||
} else {
|
||||
globalToast.warning('请先登录')
|
||||
useRouterStore().go('global-login')
|
||||
}
|
||||
}}
|
||||
>
|
||||
{props.value ? (
|
||||
|
|
|
@ -1,76 +1,82 @@
|
|||
import clsx from "clsx";
|
||||
import { computed } from "vue";
|
||||
import { defineComponent } from "vue";
|
||||
import { MdCheck } from "oh-vue-icons/icons";
|
||||
import { addIcons, OhVueIcon } from "oh-vue-icons";
|
||||
import clsx from 'clsx'
|
||||
import { computed } from 'vue'
|
||||
import { defineComponent } from 'vue'
|
||||
import { MdCheck } from 'oh-vue-icons/icons'
|
||||
import { addIcons, OhVueIcon } from 'oh-vue-icons'
|
||||
addIcons(MdCheck)
|
||||
export default defineComponent({
|
||||
name: "NativeColorPicker",
|
||||
props: {
|
||||
value: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
colorList: {
|
||||
type: Array<string>
|
||||
},
|
||||
class: {
|
||||
type: String,
|
||||
},
|
||||
transparent: {
|
||||
type: Boolean,
|
||||
},
|
||||
firstColor: {
|
||||
type: String,
|
||||
|
||||
}
|
||||
name: 'NativeColorPicker',
|
||||
props: {
|
||||
value: {
|
||||
type: String,
|
||||
default: ''
|
||||
},
|
||||
emits: {
|
||||
"update:value": (_value: string) => true,
|
||||
colorList: {
|
||||
type: Array<string>
|
||||
},
|
||||
setup(props, context) {
|
||||
const firstColor = props.value
|
||||
const colorList = computed(() => {
|
||||
let list = [
|
||||
'#ffffff',
|
||||
'#000000',
|
||||
'#FA9B3F',
|
||||
'#333333',
|
||||
'#6D7278',
|
||||
'#D8D8D8',
|
||||
'#0091FF',
|
||||
'#B620E0',
|
||||
'#F31260'
|
||||
]
|
||||
if (props.colorList) {
|
||||
list = props.colorList
|
||||
}
|
||||
if (props.transparent) {
|
||||
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] shadow-[0_0_2px_#999] rounded-full"
|
||||
style={{
|
||||
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%),
|
||||
class: {
|
||||
type: String
|
||||
},
|
||||
transparent: {
|
||||
type: Boolean
|
||||
},
|
||||
firstColor: {
|
||||
type: String
|
||||
},
|
||||
size: {
|
||||
type: Number,
|
||||
default: 20
|
||||
}
|
||||
},
|
||||
emits: {
|
||||
'update:value': (_value: string) => true
|
||||
},
|
||||
setup(props, context) {
|
||||
const firstColor = props.value
|
||||
const colorList = computed(() => {
|
||||
let list = [
|
||||
'#ffffff',
|
||||
'#000000',
|
||||
'#FA9B3F',
|
||||
'#333333',
|
||||
'#6D7278',
|
||||
'#D8D8D8',
|
||||
'#0091FF',
|
||||
'#B620E0',
|
||||
'#F31260'
|
||||
]
|
||||
if (props.colorList) {
|
||||
list = props.colorList
|
||||
}
|
||||
if (props.transparent) {
|
||||
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%)`
|
||||
: '',
|
||||
backgroundPosition: item === 'transparent' ? '0 0, 2px 2px' : '',
|
||||
backgroundSize: item === 'transparent' ? '4px 4px' : ''
|
||||
}}
|
||||
>
|
||||
{/* <Checkmark16Filled
|
||||
: '',
|
||||
backgroundPosition: item === 'transparent' ? '0 0, 2px 2px' : '',
|
||||
backgroundSize: item === 'transparent' ? '4px 4px' : ''
|
||||
}}
|
||||
>
|
||||
{/* <Checkmark16Filled
|
||||
class="text-[#888]"
|
||||
: class="props.modelValue === item ? '' : 'hidden'"
|
||||
: style="{
|
||||
|
@ -78,32 +84,34 @@ export default defineComponent({
|
|||
}"
|
||||
>
|
||||
</Checkmark16Filled> */}
|
||||
{
|
||||
props.value === item &&
|
||||
<OhVueIcon class="text-[#888]" name={MdCheck.name} ></OhVueIcon>
|
||||
{props.value === item && (
|
||||
<OhVueIcon class="text-[#888]" name={MdCheck.name}></OhVueIcon>
|
||||
)}
|
||||
</div>
|
||||
))}
|
||||
|
||||
}
|
||||
</div >)
|
||||
}
|
||||
|
||||
<div
|
||||
class="text-[12px] cursor-pointer w-[20px] h-[20px] shadow-[0_0_2px_#999] rounded-full relative"
|
||||
>
|
||||
<div
|
||||
class="w-full h-full rounded-full cursor-pointer"
|
||||
style="background: conic-gradient(#dd0010, yellow, green, #0091ff, red)"
|
||||
>
|
||||
<input
|
||||
|
||||
onInput={(e: any)=> {
|
||||
context.emit('update:value', e?.target.value)
|
||||
}}
|
||||
ref="inputRef"
|
||||
type="color"
|
||||
style="opacity: 0"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div >
|
||||
}
|
||||
})
|
||||
<div
|
||||
class="text-[12px] cursor-pointer shadow-[0_0_2px_#999] rounded-full relative"
|
||||
style={{
|
||||
width: props.size + 'px',
|
||||
height: props.size + 'px'
|
||||
}}
|
||||
>
|
||||
<div
|
||||
class="w-full h-full rounded-full cursor-pointer"
|
||||
style="background: conic-gradient(#dd0010, yellow, green, #0091ff, red)"
|
||||
>
|
||||
<input
|
||||
onInput={(e: any) => {
|
||||
context.emit('update:value', e?.target.value)
|
||||
}}
|
||||
ref="inputRef"
|
||||
type="color"
|
||||
style="opacity: 0"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
})
|
||||
|
|
|
@ -21,6 +21,7 @@ export default defineComponent({
|
|||
},
|
||||
|
||||
components: {
|
||||
|
||||
Slider: {
|
||||
railSize: 6
|
||||
},
|
||||
|
|
|
@ -3,141 +3,149 @@ import { defineComponent, ref, watch } from 'vue'
|
|||
import { VueCropper } from 'vue-cropper'
|
||||
import 'vue-cropper/dist/index.css'
|
||||
import upload from './upload'
|
||||
import { v4 as uuid } from "uuid"
|
||||
import { MdCroprotateRound, BiPlusLg } from "oh-vue-icons/icons";
|
||||
import { OhVueIcon, addIcons, } from 'oh-vue-icons'
|
||||
import { v4 as uuid } from 'uuid'
|
||||
import { MdCroprotateRound, BiPlusLg } from 'oh-vue-icons/icons'
|
||||
import { OhVueIcon, addIcons } from 'oh-vue-icons'
|
||||
import NativeColorPicker from './NativeColorPicker'
|
||||
import useUserStore from '@/user/useUserStore'
|
||||
import { globalToast } from '@/main'
|
||||
import useRouterStore from '@/useRouterStore'
|
||||
addIcons(MdCroprotateRound, BiPlusLg)
|
||||
|
||||
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: {
|
||||
'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
|
||||
const file = target.files?.[0]
|
||||
target.value = ''
|
||||
|
||||
const file = target.files?.[0]
|
||||
target.value = ''
|
||||
if (!file) return
|
||||
// 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
|
||||
// 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) => {
|
||||
// 根据前几个字节推测文件类型(这里只考虑常见的图片类型)
|
||||
// 使用 FileReader 对象读取 Blob 的前几个字节
|
||||
const reader = new FileReader()
|
||||
reader.onloadend = function () {
|
||||
// 打印 File 对象以验证
|
||||
|
||||
// 使用 FileReader 对象读取 Blob 的前几个字节
|
||||
const reader = new FileReader()
|
||||
reader.onloadend = function () {
|
||||
// 打印 File 对象以验证
|
||||
const fileData = new File([blob], `${uuid()}.png`, {
|
||||
type: 'image/jpg',
|
||||
lastModified: Date.now()
|
||||
})
|
||||
upload(fileData, 'customIcon').then((res) => {
|
||||
showCutModel.value = false
|
||||
|
||||
const fileData = new File([blob], `${uuid()}.png`, {
|
||||
type: 'image/jpg',
|
||||
lastModified: Date.now()
|
||||
})
|
||||
upload(fileData, 'customIcon').then((res) => {
|
||||
showCutModel.value = false
|
||||
|
||||
ctx.emit('update:value', res || '')
|
||||
})
|
||||
}
|
||||
reader.readAsArrayBuffer(blob)
|
||||
}
|
||||
watch(
|
||||
fillColor,
|
||||
(e: string) => {
|
||||
const elements = document.querySelectorAll('.cropper-crop-box')
|
||||
elements.forEach((element: any) => {
|
||||
element.style.backgroundColor = e // 修改颜色为蓝色
|
||||
})
|
||||
},
|
||||
{
|
||||
immediate: true
|
||||
}
|
||||
)
|
||||
return () => (
|
||||
<>
|
||||
<Modal
|
||||
open={showCutModel.value}
|
||||
onCancel={() => showCutModel.value = false}
|
||||
onOk={() => {
|
||||
cropper.value.getCropBlob((blob: any) => {
|
||||
handleFinish(blob)
|
||||
|
||||
})
|
||||
}}
|
||||
>
|
||||
<div class="w-full flex flex-col items-center gap-y-2">
|
||||
<div class="w-[250px] h-[250px]" style={{
|
||||
backgroundColor: fillColor.value || 'transparent',
|
||||
}}>
|
||||
<VueCropper
|
||||
ref={cropper}
|
||||
autoCropWidth="250px"
|
||||
autoCropHeight="250px"
|
||||
mode="contain"
|
||||
outputType="png"
|
||||
img={originFile.value}
|
||||
autoCrop={true}
|
||||
fillColor={fillColor.value}
|
||||
|
||||
/>
|
||||
</div>
|
||||
<div class="flex justify-between w-[250px]">
|
||||
{/* <ArrowRotateClockwise24Regular
|
||||
ctx.emit('update:value', res || '')
|
||||
})
|
||||
}
|
||||
reader.readAsArrayBuffer(blob)
|
||||
}
|
||||
watch(
|
||||
fillColor,
|
||||
(e: string) => {
|
||||
const elements = document.querySelectorAll('.cropper-crop-box')
|
||||
elements.forEach((element: any) => {
|
||||
element.style.backgroundColor = e // 修改颜色为蓝色
|
||||
})
|
||||
},
|
||||
{
|
||||
immediate: true
|
||||
}
|
||||
)
|
||||
return () => (
|
||||
<>
|
||||
<Modal
|
||||
open={showCutModel.value}
|
||||
onCancel={() => (showCutModel.value = false)}
|
||||
onOk={() => {
|
||||
cropper.value.getCropBlob((blob: any) => {
|
||||
handleFinish(blob)
|
||||
})
|
||||
}}
|
||||
>
|
||||
<div class="w-full flex flex-col items-center gap-y-2">
|
||||
<div
|
||||
class="w-[250px] h-[250px]"
|
||||
style={{
|
||||
backgroundColor: fillColor.value || 'transparent'
|
||||
}}
|
||||
>
|
||||
<VueCropper
|
||||
ref={cropper}
|
||||
autoCropWidth="250px"
|
||||
autoCropHeight="250px"
|
||||
mode="contain"
|
||||
outputType="png"
|
||||
img={originFile.value}
|
||||
autoCrop={true}
|
||||
fillColor={fillColor.value}
|
||||
/>
|
||||
</div>
|
||||
<div class="flex justify-between w-[250px]">
|
||||
{/* <ArrowRotateClockwise24Regular
|
||||
class="text-[10px] w-8 h-8 cursor-pointer"
|
||||
onClick={() => ($refs.cropper as any).rotateRight()}
|
||||
/> */}
|
||||
<span class="flex items-center cursor-pointer"
|
||||
onClick={() => {
|
||||
cropper.value.rotateRight()
|
||||
}}>
|
||||
<OhVueIcon name="md-croprotate-round" scale={1} fill="#707070" ></OhVueIcon>
|
||||
|
||||
</span>
|
||||
<span class="flex items-center gap-x-1">
|
||||
<svg
|
||||
onClick={() => cropper.value?.changeScale(1)}
|
||||
class="icon cursor-pointer"
|
||||
viewBox="0 0 1024 1024"
|
||||
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"
|
||||
fill="#707070"
|
||||
/>
|
||||
</svg>
|
||||
<svg
|
||||
onClick={() => cropper.value?.changeScale(-1)}
|
||||
class="icon cursor-pointer"
|
||||
viewBox="0 0 1024 1024"
|
||||
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"
|
||||
fill="#707070"
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
</div>
|
||||
<NativeColorPicker value={fillColor.value} onUpdate:value={e => fillColor.value = e} transparent={true} />
|
||||
{/* {props.colorList && (
|
||||
<span
|
||||
class="flex items-center cursor-pointer"
|
||||
onClick={() => {
|
||||
cropper.value.rotateRight()
|
||||
}}
|
||||
>
|
||||
<OhVueIcon name="md-croprotate-round" scale={1} fill="#707070"></OhVueIcon>
|
||||
</span>
|
||||
<span class="flex items-center gap-x-1">
|
||||
<svg
|
||||
onClick={() => cropper.value?.changeScale(1)}
|
||||
class="icon cursor-pointer"
|
||||
viewBox="0 0 1024 1024"
|
||||
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"
|
||||
fill="#707070"
|
||||
/>
|
||||
</svg>
|
||||
<svg
|
||||
onClick={() => cropper.value?.changeScale(-1)}
|
||||
class="icon cursor-pointer"
|
||||
viewBox="0 0 1024 1024"
|
||||
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"
|
||||
fill="#707070"
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
</div>
|
||||
<NativeColorPicker
|
||||
value={fillColor.value}
|
||||
onUpdate:value={(e) => (fillColor.value = e)}
|
||||
transparent={true}
|
||||
/>
|
||||
{/* {props.colorList && (
|
||||
<div class="w-[250px] flex justify-center">
|
||||
<NativeColorPicker
|
||||
colorList={colorList}
|
||||
|
@ -147,23 +155,32 @@ export default defineComponent({
|
|||
/>
|
||||
</div>
|
||||
)} */}
|
||||
|
||||
</div>
|
||||
</Modal >
|
||||
<div class="w-full h-full bg-white flex items-center justify-center"
|
||||
onClick={() => {
|
||||
inputRef.value?.click?.()
|
||||
}}
|
||||
>
|
||||
<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} />
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
</div>
|
||||
</Modal>
|
||||
<div
|
||||
class="w-full h-full bg-white flex items-center justify-center"
|
||||
onClick={() => {
|
||||
if (useUserStore().isLogin) {
|
||||
inputRef.value?.click?.()
|
||||
} else {
|
||||
globalToast.warning('请先登录')
|
||||
useRouterStore().go('global-login')
|
||||
// useAdderPageStore().type = 2
|
||||
}
|
||||
}}
|
||||
>
|
||||
<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>({
|
||||
background: '',
|
||||
desc: '',
|
||||
icon: '',
|
||||
icon: 'https://oss.goosetab.com/000/user_upload/1/resource/120be9d6-1c68-41ba-b539-570c39ce2421.png',
|
||||
link: '',
|
||||
name: ''
|
||||
})
|
||||
|
|
|
@ -33,7 +33,9 @@ export default defineComponent(() => {
|
|||
<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.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')}天
|
||||
</span>
|
||||
<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 class={'flex flex-col text-[#666] text-[14px]'}>
|
||||
{lunar.value.getDayYi().map((item: any) => {
|
||||
return <div class={'flex items-center'}>{item},</div>
|
||||
})}
|
||||
<div class={'flex-1 w-0 text-[#666] text-[14px]'}>
|
||||
{lunar.value.getDayYi().join(',')}
|
||||
</div>
|
||||
</div>
|
||||
<div class={'flex w-full gap-x-5'}>
|
||||
|
@ -70,10 +70,8 @@ export default defineComponent(() => {
|
|||
>
|
||||
忌
|
||||
</div>
|
||||
<div class={'flex flex-col text-[#666] text-[14px]'}>
|
||||
{lunar.value.getDayJi().map((item: any) => {
|
||||
return <div class={'flex items-center'}>{item},</div>
|
||||
})}
|
||||
<div class={'flex-1 w-0 text-[#666] text-[14px]'}>
|
||||
{lunar.value.getDayJi().join(',')}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -158,8 +156,10 @@ export default defineComponent(() => {
|
|||
dayjs(),
|
||||
'day'
|
||||
),
|
||||
'border-transparent border-solid': !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'),
|
||||
'border-transparent border-solid':
|
||||
!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 { computed, defineComponent, onMounted, ref, watch, type CSSProperties } from 'vue'
|
||||
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 { randomNum } from '@/utils/tool'
|
||||
import jump from '@/utils/jump'
|
||||
addIcons(FaChevronLeft)
|
||||
interface Owner {
|
||||
face: string
|
||||
|
@ -56,11 +57,19 @@ export default defineComponent(() => {
|
|||
})
|
||||
return () => (
|
||||
<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
|
||||
class={'w-full h-full rounded-xl relative group'}
|
||||
onClick={() => {
|
||||
jump('https://www.bilibili.com/video/av' + current.value?.aid)
|
||||
}}
|
||||
style={{
|
||||
backgroundImage: `url('${current.value?.pic}')`,
|
||||
backgroundSize: 'cover',
|
||||
|
@ -68,7 +77,6 @@ export default defineComponent(() => {
|
|||
backgroundRepeat: 'no-repeat'
|
||||
}}
|
||||
>
|
||||
|
||||
<div
|
||||
class={
|
||||
'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>
|
||||
<div class={'flex-1 flex justify-center'}>
|
||||
<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>
|
||||
|
|
|
@ -3,6 +3,7 @@ import { useWeApplyStore } from './useWeApplyStore'
|
|||
import { addIcons, OhVueIcon } from 'oh-vue-icons'
|
||||
import { HiChevronDown } from 'oh-vue-icons/icons'
|
||||
import clsx from 'clsx'
|
||||
import jump from '@/utils/jump'
|
||||
addIcons(HiChevronDown)
|
||||
export default defineComponent(() => {
|
||||
const store = useWeApplyStore()
|
||||
|
@ -13,7 +14,7 @@ export default defineComponent(() => {
|
|||
const computIsBottom = (ref: any) => {
|
||||
if (ref) {
|
||||
const { scrollTop, clientHeight, scrollHeight } = ref
|
||||
return scrollTop + clientHeight >= scrollHeight -8
|
||||
return scrollTop + clientHeight >= scrollHeight - 8
|
||||
}
|
||||
}
|
||||
const handleGameScroll = () => {
|
||||
|
@ -74,7 +75,12 @@ export default defineComponent(() => {
|
|||
{store.state.list
|
||||
.filter((val) => val.type === 'game')
|
||||
.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>
|
||||
<div class={'flex-1 flex flex-col overflow-hidden'}>
|
||||
<span class={'text-white text-[14px]'}>{item.name}</span>
|
||||
|
@ -107,7 +113,6 @@ export default defineComponent(() => {
|
|||
if (workRef.value) {
|
||||
workRef.value.scrollTop += 20
|
||||
handleWorkScroll()
|
||||
|
||||
}
|
||||
}}
|
||||
>
|
||||
|
@ -125,7 +130,12 @@ export default defineComponent(() => {
|
|||
{store.state.list
|
||||
.filter((val) => val.type === 'work')
|
||||
.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>
|
||||
<div class={'flex-1 flex flex-col overflow-hidden'}>
|
||||
<span class={'text-white text-[14px]'}>{item.name}</span>
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
import { defineComponent } from 'vue'
|
||||
import { formatSeconds } from '@/utils/tool'
|
||||
import PlayStartImg from "~/icons/work/start.png"
|
||||
import returnImg from "~/icons/work/return.png"
|
||||
import endImg from "~/icons/work/tomotoIconEnd.png"
|
||||
import PlusImg from "~/icons/work/tomatoIconAdd.png"
|
||||
import PlayStartImg from '~/icons/work/start.png'
|
||||
import returnImg from '~/icons/work/return.png'
|
||||
import endImg from '~/icons/work/tomotoIconEnd.png'
|
||||
import PlusImg from '~/icons/work/tomatoIconAdd.png'
|
||||
import dayjs from 'dayjs'
|
||||
import { Tooltip } from 'ant-design-vue'
|
||||
import useTomatoStore from './useTomatoStore'
|
||||
|
@ -11,41 +11,54 @@ import useTomatoStore from './useTomatoStore'
|
|||
export default defineComponent(() => {
|
||||
const store = useTomatoStore()
|
||||
return () => (
|
||||
<div class="w-full h-full flex relative p-6 justify-between" style={{
|
||||
background: `url('https://newfatfox.oss-cn-beijing.aliyuncs.com/admin/tomotoback.png')`,
|
||||
backgroundSize: 'cover',
|
||||
backgroundRepeat: 'no-repeat'
|
||||
}}>
|
||||
<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"}>
|
||||
<div class={"w-full bg-white/20 text-center rounded text-[14px]"}>无目标</div>
|
||||
<span class={"text-[42px] mb-1"}>
|
||||
{
|
||||
store.state.beginTime < 0 ? '15:00' : formatSeconds(store.remainingTime)
|
||||
<div
|
||||
class="w-full h-full flex relative p-6 justify-between"
|
||||
style={{
|
||||
background: `url('https://newfatfox.oss-cn-beijing.aliyuncs.com/admin/tomotoback.png')`,
|
||||
backgroundSize: 'cover',
|
||||
backgroundRepeat: 'no-repeat'
|
||||
}}
|
||||
>
|
||||
<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'}>
|
||||
<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 class={"text-[14px]"}>你今日已完成
|
||||
<span class={"text-[#76e6ff] mx-1"}>
|
||||
|
||||
|
||||
{store.state.timeList.filter(val => dayjs(val).isSame(dayjs(), 'day')).length}</span>
|
||||
<span class={'text-[14px]'}>
|
||||
你今日已完成
|
||||
<span class={'text-[#76e6ff] mx-1'}>
|
||||
{store.state.timeList.filter((val) => dayjs(val).isSame(dayjs(), 'day')).length}
|
||||
</span>
|
||||
</span>
|
||||
<span class={"text-[14px]"}>个番茄时</span>
|
||||
<span class={'text-[14px]'}>个番茄时</span>
|
||||
</div>
|
||||
<div class={"flex flex-col justify-end"}>
|
||||
<div class={"flex gap-x-3 "}>
|
||||
<Tooltip title={"沉浸模式"}>
|
||||
<div class={'flex flex-col justify-end'}>
|
||||
<div class={'flex gap-x-3 '}>
|
||||
<Tooltip title={'沉浸模式'}>
|
||||
<div
|
||||
onClick={(e) => {
|
||||
e.stopPropagation()
|
||||
store.openFullscreen = true
|
||||
}}
|
||||
class={"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>
|
||||
class={
|
||||
'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>
|
||||
</Tooltip>
|
||||
<Tooltip title={"开始"}>
|
||||
<div class={"w-[42px] h-[42px] bg-white/40 backdrop-blur-md flex items-center justify-center rounded-xl"}
|
||||
<Tooltip title={'开始'}>
|
||||
<div
|
||||
class={
|
||||
'w-[42px] h-[42px] bg-white/40 backdrop-blur-md flex items-center justify-center rounded-xl'
|
||||
}
|
||||
onClick={(e) => {
|
||||
e.stopPropagation()
|
||||
if (store.state.isStart) {
|
||||
|
@ -53,30 +66,30 @@ export default defineComponent(() => {
|
|||
} else {
|
||||
store.beginTomatoTime()
|
||||
store.openFullscreen = true
|
||||
|
||||
}
|
||||
}}>
|
||||
{
|
||||
store.state.isStart ?
|
||||
<img src={endImg} alt="start" class={"w-[18px]"}></img>
|
||||
:
|
||||
<img src={PlayStartImg} alt="start" class={"w-[18px]"}></img>
|
||||
|
||||
}
|
||||
}}
|
||||
>
|
||||
{store.state.isStart ? (
|
||||
<img src={endImg} alt="start" class={'w-[18px]'}></img>
|
||||
) : (
|
||||
<img src={PlayStartImg} alt="start" class={'w-[18px]'}></img>
|
||||
)}
|
||||
</div>
|
||||
</Tooltip>
|
||||
<Tooltip title={"添加目标"}>
|
||||
<div class={"w-[42px] h-[42px] bg-white/40 backdrop-blur-md flex items-center justify-center rounded-xl"}
|
||||
<Tooltip title={'添加目标'}>
|
||||
<div
|
||||
class={
|
||||
'w-[42px] h-[42px] bg-white/40 backdrop-blur-md flex items-center justify-center rounded-xl'
|
||||
}
|
||||
onClick={() => {
|
||||
setTimeout(() => {
|
||||
store.openShowModel = null
|
||||
|
||||
}, 300)
|
||||
}}>
|
||||
<img src={PlusImg} alt="start" class={"w-[18px]"}></img>
|
||||
}}
|
||||
>
|
||||
<img src={PlusImg} alt="start" class={'w-[18px]'}></img>
|
||||
</div>
|
||||
</Tooltip>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -130,7 +130,8 @@ const EditContent = defineComponent(() => {
|
|||
{!form.value.remindTime ? '选择时间' : dayjs(form.value.remindTime).format('HH:mm')}
|
||||
<img src={DownImg} class={"w-[12px] object-cover "}></img>
|
||||
<TimePicker
|
||||
|
||||
format={'HH:mm'}
|
||||
showNow={false}
|
||||
class={'absolute opacity-0 left-0 top-0 w-full h-full'}
|
||||
onChange={(e) => {
|
||||
form.value.remindTime = dayjs(e).valueOf()
|
||||
|
|
|
@ -66,7 +66,8 @@ export default defineComponent(() => {
|
|||
</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"}
|
||||
onClick={() => {
|
||||
onClick={(e) => {
|
||||
e.stopPropagation()
|
||||
const idx = store.state.list.findIndex(val => val.id === item.id)
|
||||
if (idx !== -1) {
|
||||
store.state.list.splice(0, 1)
|
||||
|
|
|
@ -5,8 +5,18 @@ module.exports = {
|
|||
content: ['./index.html', './src/**/*.{vue,js,ts,jsx,tsx}'],
|
||||
darkMode: 'class',
|
||||
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')]
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue