This commit is contained in:
plightfield 2024-09-13 12:19:12 +08:00
parent fc7b1bdad5
commit 15a560ea25
15 changed files with 417 additions and 242 deletions

View File

@ -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'
]

View File

@ -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>
) )
}) })

View File

@ -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>
)
}) })

View File

@ -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>
}) })

View File

@ -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
}

View File

@ -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>
) )

View File

@ -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>
) )
}) })

View File

@ -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[] }

View File

@ -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

View File

@ -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
} }
}) })

View File

@ -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>

View File

@ -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>
)
})

View File

@ -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,

View File

@ -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>
) )
} }

View File

@ -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>
)
}
})