change
This commit is contained in:
parent
fc7b1bdad5
commit
15a560ea25
|
@ -1,10 +1,13 @@
|
||||||
export const aIUrl = 'https://metaso.cn/?s=uitab&referrer_s=uitab&q='
|
export const aIUrl = 'https://metaso.cn/?s=uitab&referrer_s=uitab&q='
|
||||||
export const translateUrl = 'https://fanyi.baidu.com/mtpe-individual/multimodal?lang=zh2en&query='
|
export const translateUrl = 'https://fanyi.baidu.com/mtpe-individual/multimodal?lang=zh2en&query='
|
||||||
// 获取 oss 根目录地址
|
// oss地址
|
||||||
export const ossBase = import.meta.env.PROD
|
export const ossBase = import.meta.env.PROD
|
||||||
? 'http://btab.oss-cn-hangzhou.aliyuncs.com'
|
? 'http://btab.oss-cn-hangzhou.aliyuncs.com'
|
||||||
: 'http://btab.oss-cn-hangzhou.aliyuncs.com'
|
: 'http://btab.oss-cn-hangzhou.aliyuncs.com'
|
||||||
|
|
||||||
|
// oss cdn 加速地址
|
||||||
|
export const ossCdnBase = import.meta.env.PROD ? ossBase : ossBase
|
||||||
|
|
||||||
// 后端地址
|
// 后端地址
|
||||||
export const apiBase = import.meta.env.PROD
|
export const apiBase = import.meta.env.PROD
|
||||||
? 'http://192.168.110.28:8300'
|
? 'http://192.168.110.28:8300'
|
||||||
|
@ -12,3 +15,52 @@ export const apiBase = import.meta.env.PROD
|
||||||
|
|
||||||
// 后端 cdn 加速地址
|
// 后端 cdn 加速地址
|
||||||
export const cdnBase = import.meta.env.PROD ? apiBase : apiBase
|
export const cdnBase = import.meta.env.PROD ? apiBase : apiBase
|
||||||
|
|
||||||
|
// 图片后缀名
|
||||||
|
export const imgArr = [
|
||||||
|
'bmp',
|
||||||
|
'cod',
|
||||||
|
'gif',
|
||||||
|
'lef',
|
||||||
|
'jpe',
|
||||||
|
'jpeg',
|
||||||
|
'jpg',
|
||||||
|
'jfif',
|
||||||
|
'svg',
|
||||||
|
'tif',
|
||||||
|
'tiff',
|
||||||
|
'ras',
|
||||||
|
'cmx',
|
||||||
|
'ico',
|
||||||
|
'pnm',
|
||||||
|
'pbm',
|
||||||
|
'pgm',
|
||||||
|
'rgb',
|
||||||
|
'xbm',
|
||||||
|
'xpm',
|
||||||
|
'xwd',
|
||||||
|
'png'
|
||||||
|
]
|
||||||
|
// 视频后缀名
|
||||||
|
export const videoArr = [
|
||||||
|
'mp4',
|
||||||
|
'mpg',
|
||||||
|
'mpeg',
|
||||||
|
'mpe',
|
||||||
|
'qt',
|
||||||
|
'mov',
|
||||||
|
'm4v',
|
||||||
|
'wmv',
|
||||||
|
'avi',
|
||||||
|
'webm',
|
||||||
|
'flv',
|
||||||
|
'mp2',
|
||||||
|
'mpa',
|
||||||
|
'mpv2',
|
||||||
|
'lsf',
|
||||||
|
'lsx',
|
||||||
|
'asf',
|
||||||
|
'asr',
|
||||||
|
'asx',
|
||||||
|
'movie'
|
||||||
|
]
|
||||||
|
|
|
@ -5,6 +5,7 @@ import { OhVueIcon, addIcons } from 'oh-vue-icons'
|
||||||
import { MdKeyboardcommandkey, FaCompass, FaPencilRuler } from 'oh-vue-icons/icons'
|
import { MdKeyboardcommandkey, FaCompass, FaPencilRuler } from 'oh-vue-icons/icons'
|
||||||
import CustomAdder from './CustomAdder'
|
import CustomAdder from './CustomAdder'
|
||||||
import clsx from 'clsx'
|
import clsx from 'clsx'
|
||||||
|
import ThemeProvider from '@/utils/ThemeProvider'
|
||||||
addIcons(MdKeyboardcommandkey, FaCompass, FaPencilRuler)
|
addIcons(MdKeyboardcommandkey, FaCompass, FaPencilRuler)
|
||||||
|
|
||||||
const ItemButton = defineComponent({
|
const ItemButton = defineComponent({
|
||||||
|
@ -54,55 +55,57 @@ export default defineComponent(() => {
|
||||||
const isGame = computed(() => layout.state.current === 0)
|
const isGame = computed(() => layout.state.current === 0)
|
||||||
const type = ref(1)
|
const type = ref(1)
|
||||||
return () => (
|
return () => (
|
||||||
<div class={clsx("w-full h-full relative flex", isGame.value&&"bg-[#2c2e3e]")}>
|
<div class={clsx('w-full h-full relative flex', isGame.value && 'bg-[#2c2e3e]')}>
|
||||||
{isGame.value && <AdderPageBack />}
|
<ThemeProvider dark={isGame.value}>
|
||||||
<div
|
{isGame.value && <AdderPageBack />}
|
||||||
class={
|
<div
|
||||||
'w-[200px] h-full relative z-10 pt-[100px] ' +
|
class={
|
||||||
(isGame.value ? 'pl-6 pr-2' : 'bg-white/60 backdrop-blur px-4')
|
'w-[200px] h-full relative z-10 pt-[100px] ' +
|
||||||
}
|
(isGame.value ? 'pl-6 pr-2' : 'bg-white/60 backdrop-blur px-4')
|
||||||
>
|
}
|
||||||
<ItemButton
|
>
|
||||||
name="md-keyboardcommandkey"
|
<ItemButton
|
||||||
label="功能组件"
|
name="md-keyboardcommandkey"
|
||||||
active={type.value === 0}
|
label="功能组件"
|
||||||
onClick={() => {
|
active={type.value === 0}
|
||||||
type.value = 0
|
onClick={() => {
|
||||||
}}
|
type.value = 0
|
||||||
/>
|
}}
|
||||||
<ItemButton
|
/>
|
||||||
name="fa-compass"
|
<ItemButton
|
||||||
label="网站图标"
|
name="fa-compass"
|
||||||
active={type.value === 1}
|
label="网站图标"
|
||||||
onClick={() => {
|
active={type.value === 1}
|
||||||
type.value = 1
|
onClick={() => {
|
||||||
}}
|
type.value = 1
|
||||||
/>
|
}}
|
||||||
<ItemButton
|
/>
|
||||||
name="fa-pencil-ruler"
|
<ItemButton
|
||||||
label="自定义网址"
|
name="fa-pencil-ruler"
|
||||||
active={type.value === 2}
|
label="自定义网址"
|
||||||
onClick={() => {
|
active={type.value === 2}
|
||||||
type.value = 2
|
onClick={() => {
|
||||||
}}
|
type.value = 2
|
||||||
/>
|
}}
|
||||||
</div>
|
/>
|
||||||
<div
|
</div>
|
||||||
class={
|
<div
|
||||||
'w-0 h-full flex-grow relative z-10 flex flex-col ' +
|
class={
|
||||||
(isGame.value ? '' : 'bg-white/80 backdrop-blur')
|
'w-0 h-full flex-grow relative z-10 flex flex-col ' +
|
||||||
}
|
(isGame.value ? '' : 'bg-white/80 backdrop-blur')
|
||||||
onContextmenu={(e) => e.stopPropagation()}
|
}
|
||||||
>
|
onContextmenu={(e) => e.stopPropagation()}
|
||||||
<div class="w-full h-[60px]"></div>
|
>
|
||||||
<div class="w-full h-0 flex-grow p-6">
|
<div class="w-full h-[60px]"></div>
|
||||||
<div class="w-full h-full relative">
|
<div class="w-full h-0 flex-grow p-6">
|
||||||
<Transition>
|
<div class="w-full h-full relative">
|
||||||
{type.value === 0 ? '' : type.value === 1 ? '' : <CustomAdder />}
|
<Transition>
|
||||||
</Transition>
|
{type.value === 0 ? '' : type.value === 1 ? '' : <CustomAdder />}
|
||||||
|
</Transition>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</ThemeProvider>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
|
@ -1,11 +1,11 @@
|
||||||
import { computed, defineComponent, reactive, ref, watch } from 'vue'
|
import { computed, defineComponent, reactive, ref, watch } from 'vue'
|
||||||
import useLayoutStore from '../useLayoutStore'
|
import useLayoutStore from '../useLayoutStore'
|
||||||
import { ConfigProvider, Form, Input, theme } from 'ant-design-vue'
|
import { Button, Form, Input, InputGroup } 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 clsx from 'clsx'
|
|
||||||
import ImageUploader from '@/utils/ImageUploader'
|
import ImageUploader from '@/utils/ImageUploader'
|
||||||
import useLink from './useLink'
|
import useLink from './useLink'
|
||||||
|
import { CheckOutlined } from '@ant-design/icons-vue'
|
||||||
|
|
||||||
addIcons(MdUpload, MdImage, MdCheck)
|
addIcons(MdUpload, MdImage, MdCheck)
|
||||||
|
|
||||||
|
@ -108,112 +108,76 @@ const TypeSelector = defineComponent({
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
export default defineComponent(() => {
|
||||||
export default defineComponent({
|
const layout = useLayoutStore()
|
||||||
props: {
|
const form = reactive({
|
||||||
mode: {
|
link: '',
|
||||||
type: Number,
|
name: '',
|
||||||
default: 0
|
text: '',
|
||||||
},
|
background: '#f7a94e',
|
||||||
page: {
|
color: 'rgba(255,255,255,1)',
|
||||||
type: Number,
|
icon: '',
|
||||||
default: 0
|
type: 0 // 0 默认,1 文字
|
||||||
|
})
|
||||||
|
const isGame = computed(() => layout.state.current === 0)
|
||||||
|
const debounced = ref('')
|
||||||
|
watch(
|
||||||
|
() => form.link,
|
||||||
|
(val, _, onCleanup) => {
|
||||||
|
const it = setTimeout(() => {
|
||||||
|
debounced.value = val
|
||||||
|
}, 500)
|
||||||
|
onCleanup(() => {
|
||||||
|
clearTimeout(it)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
},
|
)
|
||||||
setup(props, ctx) {
|
|
||||||
const layout = useLayoutStore()
|
const info = useLink(debounced)
|
||||||
const form = reactive({
|
console.log(info)
|
||||||
link: '',
|
|
||||||
name: '',
|
watch(info, (val) => {
|
||||||
text: '',
|
console.log(val)
|
||||||
background: '#f7a94e',
|
|
||||||
color: 'rgba(255,255,255,1)',
|
if (val.name) form.name = val.name
|
||||||
icon: '',
|
if (val.icon) form.icon = val.icon
|
||||||
type: 0 // 0 默认,1 文字
|
})
|
||||||
})
|
return () => (
|
||||||
const isGame = computed(() => layout.state.current === 0)
|
<div
|
||||||
const debounced = ref('')
|
class={
|
||||||
watch(
|
'absolute left-0 top-0 w-full h-full rounded-2xl py-5 pl-6 pr-[30%] ' +
|
||||||
() => form.link,
|
(isGame.value ? 'bg-white/20' : 'bg-white/20')
|
||||||
(val, _, onCleanup) => {
|
|
||||||
const it = setTimeout(() => {
|
|
||||||
debounced.value = val
|
|
||||||
}, 500)
|
|
||||||
onCleanup(() => {
|
|
||||||
clearTimeout(it)
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
)
|
>
|
||||||
|
<Form labelCol={{ span: 4 }} labelAlign="left">
|
||||||
const info = useLink(debounced)
|
<Form.Item label="地址">
|
||||||
console.log(info);
|
<InputGroup compact style="display:flex">
|
||||||
|
<Input
|
||||||
watch(info, (val) => {
|
v-model:value={form.link}
|
||||||
console.log(val);
|
|
||||||
|
|
||||||
if (val.name) form.name = val.name
|
|
||||||
if (val.icon) form.icon = val.icon
|
|
||||||
|
|
||||||
})
|
|
||||||
return () => (
|
|
||||||
<div
|
|
||||||
class={
|
|
||||||
'absolute left-0 top-0 w-full h-full rounded-2xl p-5 ' +
|
|
||||||
(isGame.value ? 'bg-white/20' : 'bg-white/20')
|
|
||||||
}
|
|
||||||
>
|
|
||||||
<div class="flex flex-col">
|
|
||||||
<div></div>
|
|
||||||
</div>
|
|
||||||
<div class={clsx('flex flex-col text-[14px] gap-y-3', isGame.value ? 'text-white' : '')}>
|
|
||||||
<div class={clsx('gap-x-6 flex items-center')}>
|
|
||||||
<span>地址</span>
|
|
||||||
<input
|
|
||||||
v-model={form.link}
|
|
||||||
placeholder="搜索想要添加的网址导航"
|
placeholder="搜索想要添加的网址导航"
|
||||||
class={clsx(
|
class="w-0 flex-grow"
|
||||||
'w-[350px] rounded-md outline-none h-[40px] px-[10px]',
|
|
||||||
isGame.value ? 'bg-black/[.1]' : ''
|
|
||||||
)}
|
|
||||||
></input>
|
|
||||||
<span class={'cursor-pointer'}>获取地址</span>
|
|
||||||
</div>
|
|
||||||
<div class={clsx('gap-x-6 flex items-center')}>
|
|
||||||
<span>名称</span>
|
|
||||||
<input
|
|
||||||
v-model={form.name}
|
|
||||||
placeholder="网站名称"
|
|
||||||
class={clsx(
|
|
||||||
'w-[350px] rounded-md outline-none h-[40px] px-[10px]',
|
|
||||||
isGame.value ? 'bg-black/[.1]' : ''
|
|
||||||
)}
|
|
||||||
></input>
|
|
||||||
</div>
|
|
||||||
<div class={clsx('gap-x-6 flex items-start')}>
|
|
||||||
<span>图标</span>
|
|
||||||
<TypeSelector
|
|
||||||
v-model:value={form.type}
|
|
||||||
v-model:icon={form.icon}
|
|
||||||
text={form.text}
|
|
||||||
color={form.color}
|
|
||||||
background={form.background}
|
|
||||||
/>
|
/>
|
||||||
</div>
|
<Button>获取地址</Button>
|
||||||
<div class={clsx('gap-x-6 flex items-center')}>
|
</InputGroup>
|
||||||
<span></span>
|
</Form.Item>
|
||||||
<button
|
<Form.Item label="名称">
|
||||||
v-model:value={form.name}
|
<Input v-model:value={form.name} />
|
||||||
placeholder="网站名称"
|
</Form.Item>
|
||||||
class={clsx(
|
<Form.Item label="图标">
|
||||||
'bg-gradient-to-b from-[#ffaa4e] to-[#ff6227] rounded-lg w-[94px] h-[40px] text-[16px] ',
|
<TypeSelector
|
||||||
isGame.value ? 'bg-black/[.1]' : ''
|
v-model:value={form.type}
|
||||||
)}
|
v-model:icon={form.icon}
|
||||||
>
|
text={form.text}
|
||||||
确定
|
color={form.color}
|
||||||
</button>
|
background={form.background}
|
||||||
</div>
|
/>
|
||||||
</div>
|
</Form.Item>
|
||||||
</div>
|
<Form.Item label=" " colon={false}>
|
||||||
)
|
<Button type="primary" size="large" icon={<CheckOutlined />}>
|
||||||
}
|
确定
|
||||||
|
</Button>
|
||||||
|
</Form.Item>
|
||||||
|
</Form>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
})
|
})
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import { defineComponent } from 'vue'
|
import { defineComponent } from 'vue'
|
||||||
|
|
||||||
export default defineComponent(() => {
|
export default defineComponent(() => {
|
||||||
return () => <div class="absolute left-0 top-0 w-full h-full p-4">this is background</div>
|
return () => <div class="absolute left-0 top-0 w-full h-full p-4"></div>
|
||||||
})
|
})
|
||||||
|
|
|
@ -0,0 +1,97 @@
|
||||||
|
import { ossCdnBase, videoArr } from '@/config'
|
||||||
|
import db from '@/db'
|
||||||
|
import { reactive, watch, type Ref } from 'vue'
|
||||||
|
|
||||||
|
const defaultBackground =
|
||||||
|
'https://aihlp.com.cn/admin/wallpaper/508d3994-3727-4839-bfe9-41b97ccf4fba_66a3272c874d41e5b9ec7562fe5822cd (1).jpg'
|
||||||
|
|
||||||
|
const defaultResource = {
|
||||||
|
image: '',
|
||||||
|
video: '',
|
||||||
|
brief: ''
|
||||||
|
}
|
||||||
|
export default function useResource(tag: Ref<string>, type: string) {
|
||||||
|
const resource = reactive(defaultResource)
|
||||||
|
watch(
|
||||||
|
tag,
|
||||||
|
(val) => {
|
||||||
|
// '' 表示使用默认,如果是背景,使用默认背景图片
|
||||||
|
if (!val) {
|
||||||
|
Object.assign(resource, {
|
||||||
|
...defaultResource,
|
||||||
|
image: type === 'background' ? defaultBackground : ''
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (val.startsWith('http')) {
|
||||||
|
// 内源
|
||||||
|
if (val.startsWith(ossCdnBase)) {
|
||||||
|
// 地址后缀
|
||||||
|
const suffix = val.split('.').pop()
|
||||||
|
if (!suffix) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (videoArr.includes(suffix)) {
|
||||||
|
// 内部视频
|
||||||
|
// 先显示截图,再去数据库看是否有存货
|
||||||
|
resource.video = ''
|
||||||
|
resource.image = val + '?x-oss-process=video/snapshot,t_0,f_jpg,m_fast'
|
||||||
|
resource.brief = val + '?x-oss-process=video/snapshot,t_0,f_jpg,w_400,h_225,m_fast'
|
||||||
|
db.getItem<{ tag: string; file: Blob }[]>('videoList').then((res) => {
|
||||||
|
if (!res) return
|
||||||
|
const item = res.find((item) => item.tag === val)
|
||||||
|
if (item) {
|
||||||
|
resource.video = URL.createObjectURL(item.file)
|
||||||
|
resource.image = ''
|
||||||
|
} else {
|
||||||
|
// 不存在,需要存入
|
||||||
|
fetch(val)
|
||||||
|
.then((res) => res.blob())
|
||||||
|
.then((blob) => {
|
||||||
|
if (res.length > 10) {
|
||||||
|
res.pop()
|
||||||
|
}
|
||||||
|
res.unshift({ tag: val, file: blob })
|
||||||
|
resource.video = URL.createObjectURL(blob)
|
||||||
|
resource.image = ''
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
// 图片
|
||||||
|
resource.image = val
|
||||||
|
resource.video = ''
|
||||||
|
resource.brief = val + '?x-oss-process=image/resize,w_400,h_225'
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// 外部资源
|
||||||
|
const suffix = val.split('.').pop()
|
||||||
|
if (!suffix) {
|
||||||
|
resource.image = val
|
||||||
|
resource.video = ''
|
||||||
|
resource.brief = val
|
||||||
|
return
|
||||||
|
}
|
||||||
|
resource.image = videoArr.includes(suffix) ? '' : val
|
||||||
|
resource.video = videoArr.includes(suffix) ? val : ''
|
||||||
|
resource.brief = val
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// 本地
|
||||||
|
db.getItem<{ tag: string; file: Blob; type: 'image' | 'video' }[]>('localList').then(
|
||||||
|
(res) => {
|
||||||
|
if (!res) return
|
||||||
|
const item = res.find((item) => item.tag === val)
|
||||||
|
if (!item) return
|
||||||
|
const url = URL.createObjectURL(item.file)
|
||||||
|
resource.image = item.type === 'image' ? url : ''
|
||||||
|
resource.video = item.type === 'video' ? url : ''
|
||||||
|
resource.brief = url
|
||||||
|
}
|
||||||
|
)
|
||||||
|
},
|
||||||
|
{ immediate: true }
|
||||||
|
)
|
||||||
|
return resource
|
||||||
|
}
|
|
@ -1,20 +1,20 @@
|
||||||
import { defineComponent } from 'vue'
|
import { defineComponent } from 'vue'
|
||||||
import useBackgroundStore from './useBackgroundStore'
|
import useLayoutStore from '../useLayoutStore'
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
setup() {
|
setup() {
|
||||||
const background = useBackgroundStore()
|
const layout = useLayoutStore()
|
||||||
return () => (
|
return () => (
|
||||||
<div class="absolute left-0 top-0 w-full h-screen z-0">
|
<div class="absolute left-0 top-0 w-full h-screen z-0">
|
||||||
{background.resource.type === 'image' ? (
|
{layout.background.video ? (
|
||||||
|
<video src={layout.background.video} class="w-full h-full" />
|
||||||
|
) : (
|
||||||
<div
|
<div
|
||||||
class="w-full h-full bg-center bg-cover bg-no-repeat"
|
class="w-full h-full bg-center bg-cover bg-no-repeat"
|
||||||
style={{
|
style={{
|
||||||
backgroundImage: `url('${background.resource.url}')`
|
backgroundImage: `url('${layout.background.image}')`
|
||||||
}}
|
}}
|
||||||
></div>
|
></div>
|
||||||
) : (
|
|
||||||
<video src={background.resource.url} class="w-full h-full" />
|
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
|
|
|
@ -1,8 +1,9 @@
|
||||||
import { defineComponent, ref } from 'vue'
|
import { defineComponent, ref } from 'vue'
|
||||||
import { Button, Checkbox, ConfigProvider, Divider, message, Modal, theme } from 'ant-design-vue'
|
import { Button, Checkbox, Divider, message, Modal } from 'ant-design-vue'
|
||||||
import useSearchConfigStore from './useSearchConfigStore'
|
import useSearchConfigStore from './useSearchConfigStore'
|
||||||
import { EditOutlined, DeleteOutlined, PlusOutlined } from '@ant-design/icons-vue'
|
import { EditOutlined, DeleteOutlined, PlusOutlined } from '@ant-design/icons-vue'
|
||||||
import asyncLoader from '@/utils/asyncLoader'
|
import asyncLoader from '@/utils/asyncLoader'
|
||||||
|
import ThemeProvider from '@/utils/ThemeProvider'
|
||||||
const SearchAdder = asyncLoader(() => import('./SearchAdder'))
|
const SearchAdder = asyncLoader(() => import('./SearchAdder'))
|
||||||
const SearchItem = defineComponent({
|
const SearchItem = defineComponent({
|
||||||
props: {
|
props: {
|
||||||
|
@ -118,7 +119,7 @@ export default defineComponent(() => {
|
||||||
class="w-full h-full bg-white/80 backdrop-blur p-4 flex flex-col select-text"
|
class="w-full h-full bg-white/80 backdrop-blur p-4 flex flex-col select-text"
|
||||||
onContextmenu={(e) => e.stopPropagation()}
|
onContextmenu={(e) => e.stopPropagation()}
|
||||||
>
|
>
|
||||||
<ConfigProvider theme={{ token: theme.defaultAlgorithm(theme.defaultSeed) }}>
|
<ThemeProvider>
|
||||||
<h2 class="text-center tracking-widest font-bold text-xl text-black/80">管理搜索引擎</h2>
|
<h2 class="text-center tracking-widest font-bold text-xl text-black/80">管理搜索引擎</h2>
|
||||||
<div class="grid grid-cols-2 gap-4">
|
<div class="grid grid-cols-2 gap-4">
|
||||||
<Divider>默认</Divider>
|
<Divider>默认</Divider>
|
||||||
|
@ -177,7 +178,7 @@ export default defineComponent(() => {
|
||||||
>
|
>
|
||||||
<SearchAdder selected={showAdder.value as any} />
|
<SearchAdder selected={showAdder.value as any} />
|
||||||
</Modal>
|
</Modal>
|
||||||
</ConfigProvider>
|
</ThemeProvider>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
|
@ -25,7 +25,11 @@ export interface Block {
|
||||||
export type LayoutPages = { list: Block[]; label: string; name: string }[]
|
export type LayoutPages = { list: Block[]; label: string; name: string }[]
|
||||||
|
|
||||||
export interface Layout {
|
export interface Layout {
|
||||||
content: [LayoutPages, LayoutPages, LayoutPages]
|
content: [
|
||||||
|
{ background: ''; pages: LayoutPages },
|
||||||
|
{ background: ''; pages: LayoutPages },
|
||||||
|
{ background: ''; pages: LayoutPages }
|
||||||
|
]
|
||||||
current: 0 | 1 | 2 // 游戏,工作,轻娱
|
current: 0 | 1 | 2 // 游戏,工作,轻娱
|
||||||
currentPage: number
|
currentPage: number
|
||||||
dir: { [key: string]: Block[] }
|
dir: { [key: string]: Block[] }
|
||||||
|
|
|
@ -77,7 +77,7 @@ export default defineComponent(() => {
|
||||||
<div class="w-full h-[64px]" />
|
<div class="w-full h-[64px]" />
|
||||||
<div class="w-full h-0 flex-grow overflow-auto relative no-scrollbar">
|
<div class="w-full h-0 flex-grow overflow-auto relative no-scrollbar">
|
||||||
<TransitionGroup>
|
<TransitionGroup>
|
||||||
{layout.state.content[layout.state.current].map((el, idx) => (
|
{layout.currentMode.pages.map((el, idx) => (
|
||||||
<Item
|
<Item
|
||||||
key={idx}
|
key={idx}
|
||||||
name={el.name}
|
name={el.name}
|
||||||
|
@ -91,7 +91,7 @@ export default defineComponent(() => {
|
||||||
))}
|
))}
|
||||||
</TransitionGroup>
|
</TransitionGroup>
|
||||||
<Transition>
|
<Transition>
|
||||||
{layout.state.content[layout.state.current]?.length > 0 && (
|
{layout.currentMode.pages.length > 0 && (
|
||||||
<div
|
<div
|
||||||
class="absolute w-full h-[56px] rounded-lg bg-white/40 left-0 transition-all"
|
class="absolute w-full h-[56px] rounded-lg bg-white/40 left-0 transition-all"
|
||||||
style={{
|
style={{
|
||||||
|
@ -164,7 +164,7 @@ export default defineComponent(() => {
|
||||||
<div
|
<div
|
||||||
class="w-full mt-2 py-1 rounded-lg bg-white text-center text-sm shadow hover:shadow-lg transition-all cursor-pointer"
|
class="w-full mt-2 py-1 rounded-lg bg-white text-center text-sm shadow hover:shadow-lg transition-all cursor-pointer"
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
layout.state.content[layout.state.current].push({
|
layout.currentMode.pages.push({
|
||||||
list: [],
|
list: [],
|
||||||
label: label.value,
|
label: label.value,
|
||||||
name: selected.value.name
|
name: selected.value.name
|
||||||
|
|
|
@ -2,9 +2,14 @@ import { defineStore } from 'pinia'
|
||||||
import type { Layout } from './layout.types'
|
import type { Layout } from './layout.types'
|
||||||
import { computed, reactive, ref, toRaw, watch } from 'vue'
|
import { computed, reactive, ref, toRaw, watch } from 'vue'
|
||||||
import db from '@/db'
|
import db from '@/db'
|
||||||
|
import useResource from './background/getResource'
|
||||||
|
|
||||||
const defaultLayout: Layout = {
|
const defaultLayout: Layout = {
|
||||||
content: [[], [], []],
|
content: [
|
||||||
|
{ background: '', pages: [] },
|
||||||
|
{ background: '', pages: [] },
|
||||||
|
{ background: '', pages: [] }
|
||||||
|
],
|
||||||
current: 0,
|
current: 0,
|
||||||
currentPage: 0,
|
currentPage: 0,
|
||||||
dir: {},
|
dir: {},
|
||||||
|
@ -35,15 +40,29 @@ export default defineStore('layout', () => {
|
||||||
state.currentPage = 0
|
state.currentPage = 0
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
const currentPageList = computed(() =>
|
const currentMode = computed(() => state.content[state.current])
|
||||||
state.simple ? [] : state.content[state.current]?.[state.currentPage]?.list || []
|
const currentPage = computed(() => {
|
||||||
|
return (
|
||||||
|
currentMode.value.pages[state.simple ? 0 : state.currentPage] || {
|
||||||
|
list: [],
|
||||||
|
label: '',
|
||||||
|
name: ''
|
||||||
|
}
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
const background = useResource(
|
||||||
|
computed(() => state.content[state.current]?.background || ''),
|
||||||
|
'background'
|
||||||
)
|
)
|
||||||
// 是让时间和搜索改变位置,使画面更紧凑 —— @/layout/grid
|
// 是让时间和搜索改变位置,使画面更紧凑 —— @/layout/grid
|
||||||
const isCompact = ref(false)
|
const isCompact = ref(false)
|
||||||
return {
|
return {
|
||||||
state,
|
state,
|
||||||
ready,
|
ready,
|
||||||
currentPageList,
|
currentMode,
|
||||||
isCompact
|
currentPage,
|
||||||
|
isCompact,
|
||||||
|
background
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
|
@ -2,9 +2,7 @@ import AvatarCircle from '@/user/AvatarCircle'
|
||||||
import useRouterStore from '@/useRouterStore'
|
import useRouterStore from '@/useRouterStore'
|
||||||
import asyncLoader from '@/utils/asyncLoader'
|
import asyncLoader from '@/utils/asyncLoader'
|
||||||
import { computed, defineComponent, Transition } from 'vue'
|
import { computed, defineComponent, Transition } from 'vue'
|
||||||
|
const Content = asyncLoader(() => import('./SettingsOverlayContent'))
|
||||||
const UserPage = asyncLoader(() => import('@/user/UserPage'))
|
|
||||||
const BackgroundPage = asyncLoader(() => import('@/layout/background/BackgroundPage'))
|
|
||||||
|
|
||||||
const SettingsTab = defineComponent({
|
const SettingsTab = defineComponent({
|
||||||
props: {
|
props: {
|
||||||
|
@ -77,15 +75,7 @@ export default defineComponent(() => {
|
||||||
<SettingsTab label="重置" path="settings-reset" />
|
<SettingsTab label="重置" path="settings-reset" />
|
||||||
<SettingsTab label="问题反馈" path="settings-fallback" />
|
<SettingsTab label="问题反馈" path="settings-fallback" />
|
||||||
</div>
|
</div>
|
||||||
<div class="w-0 h-full flex-grow bg-white/80 backdrop-blur">
|
<Content />
|
||||||
<Transition>
|
|
||||||
{router.path === 'settings-user' ? (
|
|
||||||
<UserPage />
|
|
||||||
) : router.path === 'settings-background' ? (
|
|
||||||
<BackgroundPage />
|
|
||||||
) : null}
|
|
||||||
</Transition>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</Transition>
|
</Transition>
|
||||||
|
|
|
@ -0,0 +1,21 @@
|
||||||
|
import useRouterStore from '@/useRouterStore'
|
||||||
|
import { defineComponent, Transition } from 'vue'
|
||||||
|
import UserPage from '@/user/UserPage'
|
||||||
|
import BackgroundPage from '@/layout/background/BackgroundPage'
|
||||||
|
import ThemeProvider from '@/utils/ThemeProvider'
|
||||||
|
export default defineComponent(() => {
|
||||||
|
const router = useRouterStore()
|
||||||
|
return () => (
|
||||||
|
<div class="w-0 h-full flex-grow bg-white/80 backdrop-blur">
|
||||||
|
<ThemeProvider>
|
||||||
|
<Transition>
|
||||||
|
{router.path === 'settings-user' ? (
|
||||||
|
<UserPage />
|
||||||
|
) : router.path === 'settings-background' ? (
|
||||||
|
<BackgroundPage />
|
||||||
|
) : null}
|
||||||
|
</Transition>
|
||||||
|
</ThemeProvider>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
})
|
|
@ -7,7 +7,6 @@ export default defineStore(
|
||||||
'settings',
|
'settings',
|
||||||
() => {
|
() => {
|
||||||
const state = reactive({
|
const state = reactive({
|
||||||
backgroundTag: '',
|
|
||||||
// 显示隐藏
|
// 显示隐藏
|
||||||
showSider: 'show' as VisibleState,
|
showSider: 'show' as VisibleState,
|
||||||
showDock: 'show' as VisibleState,
|
showDock: 'show' as VisibleState,
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
import { defineComponent } from 'vue'
|
import { defineComponent } from 'vue'
|
||||||
import { OhVueIcon, addIcons } from 'oh-vue-icons'
|
import { OhVueIcon, addIcons } from 'oh-vue-icons'
|
||||||
import { MdUpload, FaEye } from 'oh-vue-icons/icons'
|
import { MdUpload, FaEye } from 'oh-vue-icons/icons'
|
||||||
import { ConfigProvider, message, theme } from 'ant-design-vue'
|
import { message } from 'ant-design-vue'
|
||||||
import upload from './upload'
|
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'
|
||||||
|
@ -35,62 +35,60 @@ export default defineComponent({
|
||||||
let input: HTMLInputElement | null = null
|
let input: HTMLInputElement | null = null
|
||||||
return () => (
|
return () => (
|
||||||
<div>
|
<div>
|
||||||
<ConfigProvider theme={{ token: theme.defaultAlgorithm(theme.defaultSeed) }}>
|
<input
|
||||||
<input
|
type="file"
|
||||||
type="file"
|
accept=".png,.jpeg,.jpg,.svg"
|
||||||
accept=".png,.jpeg,.jpg,.svg"
|
style="display:none"
|
||||||
style="display:none"
|
ref={(el) => (input = el as any)}
|
||||||
ref={(el) => (input = el as any)}
|
onChange={(e) => {
|
||||||
onChange={(e) => {
|
const el = e.target as any
|
||||||
const el = e.target as any
|
const file: File | undefined = el.files?.[0]
|
||||||
const file: File | undefined = el.files?.[0]
|
el.value = ''
|
||||||
el.value = ''
|
if (!file) return
|
||||||
if (!file) return
|
console.log(file.size, props.size)
|
||||||
console.log(file.size, props.size)
|
if (file.size > props.size * 1000 * 1000) {
|
||||||
if (file.size > props.size * 1000 * 1000) {
|
message.warn(`大小不得超过${props.size}mb`)
|
||||||
message.warn(`大小不得超过${props.size}mb`)
|
return
|
||||||
return
|
}
|
||||||
}
|
upload(file, 'test').then((res) => {
|
||||||
upload(file, 'test').then((res) => {
|
ctx.emit('update:value', res)
|
||||||
ctx.emit('update:value', res)
|
})
|
||||||
})
|
}}
|
||||||
}}
|
/>
|
||||||
/>
|
<div
|
||||||
<div
|
class="flex justify-center items-center rounded bg-slate-200 hover:bg-slate-300 transition-all bg-cover bg-no-repeat bg-center cursor-pointer shadow"
|
||||||
class="flex justify-center items-center rounded bg-slate-200 hover:bg-slate-300 transition-all bg-cover bg-no-repeat bg-center cursor-pointer shadow"
|
style={{
|
||||||
style={{
|
width: props.width + 'px',
|
||||||
width: props.width + 'px',
|
height: props.width / props.ratio + 'px',
|
||||||
height: props.width / props.ratio + 'px',
|
backgroundImage: `url('${props.value}')`
|
||||||
backgroundImage: `url('${props.value}')`
|
}}
|
||||||
}}
|
onClick={() => {
|
||||||
onClick={() => {
|
input?.click()
|
||||||
input?.click()
|
}}
|
||||||
}}
|
>
|
||||||
>
|
{props.value ? (
|
||||||
{props.value ? (
|
<div class="w-full h-full opacity-0 hover:opacity-100 relative transition-all bg-slate-800/20 flex justify-center items-center">
|
||||||
<div class="w-full h-full opacity-0 hover:opacity-100 relative transition-all bg-slate-800/20 flex justify-center items-center">
|
<div
|
||||||
<div
|
class="px-1 rounded hover:bg-white/20"
|
||||||
class="px-1 rounded hover:bg-white/20"
|
onClick={(e) => {
|
||||||
onClick={(e) => {
|
e.stopPropagation()
|
||||||
e.stopPropagation()
|
showViewer({
|
||||||
showViewer({
|
images: [props.value],
|
||||||
images: [props.value],
|
options: {
|
||||||
options: {
|
navbar: false,
|
||||||
navbar: false,
|
toolbar: false
|
||||||
toolbar: false
|
}
|
||||||
}
|
})
|
||||||
})
|
}}
|
||||||
}}
|
>
|
||||||
>
|
<OhVueIcon name="fa-eye" scale={1.2} fill="rgba(255,255,255,.4)" />
|
||||||
<OhVueIcon name="fa-eye" scale={1.2} fill="rgba(255,255,255,.4)" />
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
) : (
|
</div>
|
||||||
<OhVueIcon name="md-upload" scale={2} fill="rgba(0,0,0,0.2)" />
|
) : (
|
||||||
)}
|
<OhVueIcon name="md-upload" scale={2} fill="rgba(0,0,0,0.2)" />
|
||||||
</div>
|
)}
|
||||||
{/* <div class="text-xs mt-1 text-black/60">支持上传 .png, .jpeg, .jpg, .svg</div>{' '} */}
|
</div>
|
||||||
</ConfigProvider>
|
{/* <div class="text-xs mt-1 text-black/60">支持上传 .png, .jpeg, .jpg, .svg</div>{' '} */}
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,27 @@
|
||||||
|
import { ConfigProvider, theme } from 'ant-design-vue'
|
||||||
|
import { defineComponent } from 'vue'
|
||||||
|
|
||||||
|
export default defineComponent({
|
||||||
|
props: {
|
||||||
|
dark: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
setup(props, ctx) {
|
||||||
|
return () => (
|
||||||
|
<ConfigProvider
|
||||||
|
theme={{
|
||||||
|
algorithm: props.dark ? theme.darkAlgorithm : theme.defaultAlgorithm,
|
||||||
|
token: {
|
||||||
|
colorPrimary: '#f7a94e',
|
||||||
|
colorBgBase: props.dark ? '#393a41' : '#fff',
|
||||||
|
colorBorder: 'transparent'
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{ctx.slots.default?.()}
|
||||||
|
</ConfigProvider>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
})
|
Loading…
Reference in New Issue