2024-09-26 11:47:16 +08:00
|
|
|
|
import { computed, defineComponent, inject, reactive, ref, watch } from 'vue'
|
2024-09-11 18:00:15 +08:00
|
|
|
|
import useLayoutStore from '../useLayoutStore'
|
2024-09-13 12:19:12 +08:00
|
|
|
|
import { Button, Form, Input, InputGroup } from 'ant-design-vue'
|
2024-09-11 18:00:15 +08:00
|
|
|
|
import { OhVueIcon, addIcons } from 'oh-vue-icons'
|
2024-09-12 17:59:45 +08:00
|
|
|
|
import { MdUpload, MdImage, MdCheck } from 'oh-vue-icons/icons'
|
|
|
|
|
import ImageUploader from '@/utils/ImageUploader'
|
2024-09-25 14:25:44 +08:00
|
|
|
|
import useLink from '../../utils/useLink'
|
2024-09-13 12:19:12 +08:00
|
|
|
|
import { CheckOutlined } from '@ant-design/icons-vue'
|
2024-09-18 16:27:43 +08:00
|
|
|
|
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'
|
2024-09-27 16:35:30 +08:00
|
|
|
|
import UploadAndCut from '@/utils/UploadAndCut'
|
2024-09-26 11:47:16 +08:00
|
|
|
|
import { AddToToken } from './AdderPage'
|
2024-09-11 18:00:15 +08:00
|
|
|
|
|
2024-09-12 17:59:45 +08:00
|
|
|
|
addIcons(MdUpload, MdImage, MdCheck)
|
2024-09-11 18:00:15 +08:00
|
|
|
|
|
|
|
|
|
const TypeSelector = defineComponent({
|
|
|
|
|
props: {
|
|
|
|
|
value: {
|
|
|
|
|
type: Number,
|
|
|
|
|
default: 0
|
|
|
|
|
},
|
|
|
|
|
icon: {
|
|
|
|
|
type: String,
|
|
|
|
|
default: ''
|
|
|
|
|
},
|
|
|
|
|
text: {
|
|
|
|
|
type: String,
|
|
|
|
|
default: ''
|
|
|
|
|
},
|
|
|
|
|
color: {
|
|
|
|
|
type: String,
|
|
|
|
|
default: ''
|
|
|
|
|
},
|
|
|
|
|
background: {
|
|
|
|
|
type: String,
|
|
|
|
|
default: ''
|
2024-09-18 16:27:43 +08:00
|
|
|
|
},
|
|
|
|
|
name: {
|
|
|
|
|
type: String,
|
|
|
|
|
default: ''
|
2024-09-11 18:00:15 +08:00
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
emits: {
|
|
|
|
|
'update:value': (() => true) as (val: number) => boolean,
|
|
|
|
|
'update:icon': (() => true) as (val: string) => boolean
|
|
|
|
|
},
|
|
|
|
|
setup(props, ctx) {
|
|
|
|
|
return () => (
|
|
|
|
|
<div class="flex gap-4">
|
2024-09-12 17:59:45 +08:00
|
|
|
|
<div class={'flex justify-center flex-col items-center gap-y-1'}>
|
|
|
|
|
<div
|
|
|
|
|
class="w-[47px] relative h-[47px] bg-center bg-no-repeat bg-cover rounded-lg overflow-hidden flex justify-center items-center cursor-pointer hover:scale-105 transition-all"
|
|
|
|
|
style={{
|
|
|
|
|
backgroundImage: `url('${props.icon}')`,
|
|
|
|
|
backgroundColor: props.icon ? '' : 'rgba(0,0,0,0.2)'
|
|
|
|
|
}}
|
|
|
|
|
onClick={() => {
|
|
|
|
|
ctx.emit('update:value', 0)
|
|
|
|
|
}}
|
|
|
|
|
>
|
|
|
|
|
{!props.icon && <OhVueIcon name="md-image" fill="white" scale={1.8} />}
|
|
|
|
|
{props.value === 0 && (
|
|
|
|
|
<div
|
|
|
|
|
class={
|
|
|
|
|
'w-[20px] h-[20px] rounded bg-white/50 flex items-center justify-center absolute right-0 bottom-0'
|
|
|
|
|
}
|
|
|
|
|
>
|
|
|
|
|
<OhVueIcon name="md-check" fill="red" scale={0.8}></OhVueIcon>
|
|
|
|
|
</div>
|
|
|
|
|
)}
|
|
|
|
|
</div>
|
|
|
|
|
<span class={'text-[12px]'}>默认</span>
|
2024-09-11 18:00:15 +08:00
|
|
|
|
</div>
|
2024-09-12 17:59:45 +08:00
|
|
|
|
<div class={'flex justify-center flex-col items-center gap-y-1 '}>
|
|
|
|
|
<div
|
|
|
|
|
class="w-[44px] relative h-[44px] bg-center bg-no-repeat bg-cover rounded-lg overflow-hidden flex justify-center items-center cursor-pointer hover:scale-105 transition-all"
|
2024-09-11 18:00:15 +08:00
|
|
|
|
style={{
|
2024-09-27 16:35:30 +08:00
|
|
|
|
backgroundColor: props.background || 'transparent'
|
2024-09-12 17:59:45 +08:00
|
|
|
|
}}
|
|
|
|
|
onClick={() => {
|
|
|
|
|
ctx.emit('update:value', 1)
|
2024-09-11 18:00:15 +08:00
|
|
|
|
}}
|
|
|
|
|
>
|
2024-09-12 17:59:45 +08:00
|
|
|
|
<span
|
2024-09-18 16:27:43 +08:00
|
|
|
|
class="text-[16px]"
|
2024-09-12 17:59:45 +08:00
|
|
|
|
style={{
|
|
|
|
|
color: props.color
|
|
|
|
|
}}
|
|
|
|
|
>
|
2024-09-18 16:27:43 +08:00
|
|
|
|
{props.text
|
|
|
|
|
? props.text.substring(0, 2)
|
|
|
|
|
: props.name.substring(0, 2).toLocaleUpperCase() || 'A'}
|
2024-09-12 17:59:45 +08:00
|
|
|
|
</span>
|
|
|
|
|
{props.value === 1 && (
|
|
|
|
|
<div
|
|
|
|
|
class={
|
|
|
|
|
'w-[20px] h-[20px] rounded bg-white/50 flex items-center justify-center absolute right-0 bottom-0'
|
|
|
|
|
}
|
|
|
|
|
>
|
|
|
|
|
<OhVueIcon name="md-check" fill="red" scale={0.8}></OhVueIcon>
|
|
|
|
|
</div>
|
|
|
|
|
)}
|
|
|
|
|
</div>
|
|
|
|
|
<span class={'text-[12px]'}>文字</span>
|
2024-09-11 18:00:15 +08:00
|
|
|
|
</div>
|
2024-09-12 17:59:45 +08:00
|
|
|
|
<div class={'flex justify-center flex-col items-center gap-y-1'}>
|
2024-09-27 16:35:30 +08:00
|
|
|
|
<div class="w-[44px] h-[44px] rounded-lg flex overflow-hidden justify-center items-center bg-white shadow cursor-pointer hover:scale-105 transition-all">
|
|
|
|
|
{/* <ImageUploader
|
2024-09-12 17:59:45 +08:00
|
|
|
|
width={44}
|
|
|
|
|
onUpdate:value={(e) => {
|
|
|
|
|
ctx.emit('update:icon', e)
|
|
|
|
|
}}
|
2024-09-27 16:35:30 +08:00
|
|
|
|
></ImageUploader> */}
|
|
|
|
|
<UploadAndCut onUpdate:value={(e)=> {
|
|
|
|
|
ctx.emit('update:icon', e)
|
|
|
|
|
|
|
|
|
|
}}></UploadAndCut>
|
2024-09-12 17:59:45 +08:00
|
|
|
|
</div>
|
|
|
|
|
<span class={'text-[12px]'}>自定义</span>
|
2024-09-11 18:00:15 +08:00
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
)
|
|
|
|
|
}
|
|
|
|
|
})
|
2024-09-13 12:19:12 +08:00
|
|
|
|
export default defineComponent(() => {
|
|
|
|
|
const layout = useLayoutStore()
|
|
|
|
|
const form = reactive({
|
|
|
|
|
link: '',
|
|
|
|
|
name: '',
|
|
|
|
|
text: '',
|
2024-09-18 16:27:43 +08:00
|
|
|
|
background: 'rgb(247,169,78)',
|
|
|
|
|
color: 'rgb(255,255,255)',
|
2024-09-13 12:19:12 +08:00
|
|
|
|
icon: '',
|
|
|
|
|
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)
|
|
|
|
|
})
|
2024-09-11 18:00:15 +08:00
|
|
|
|
}
|
2024-09-13 12:19:12 +08:00
|
|
|
|
)
|
2024-09-12 17:59:45 +08:00
|
|
|
|
|
2024-09-13 12:19:12 +08:00
|
|
|
|
const info = useLink(debounced)
|
2024-09-12 17:59:45 +08:00
|
|
|
|
|
2024-09-13 12:19:12 +08:00
|
|
|
|
watch(info, (val) => {
|
|
|
|
|
if (val.name) form.name = val.name
|
|
|
|
|
if (val.icon) form.icon = val.icon
|
|
|
|
|
})
|
2024-09-26 11:47:16 +08:00
|
|
|
|
const addTo = inject(AddToToken)
|
2024-09-13 12:19:12 +08:00
|
|
|
|
return () => (
|
|
|
|
|
<div
|
|
|
|
|
class={
|
|
|
|
|
'absolute left-0 top-0 w-full h-full rounded-2xl py-5 pl-6 pr-[30%] ' +
|
|
|
|
|
(isGame.value ? 'bg-white/20' : 'bg-white/20')
|
|
|
|
|
}
|
|
|
|
|
>
|
|
|
|
|
<Form labelCol={{ span: 4 }} labelAlign="left">
|
|
|
|
|
<Form.Item label="地址">
|
|
|
|
|
<InputGroup compact style="display:flex">
|
|
|
|
|
<Input
|
|
|
|
|
v-model:value={form.link}
|
2024-09-12 17:59:45 +08:00
|
|
|
|
placeholder="搜索想要添加的网址导航"
|
2024-09-13 12:19:12 +08:00
|
|
|
|
class="w-0 flex-grow"
|
2024-09-12 17:59:45 +08:00
|
|
|
|
/>
|
2024-09-13 12:19:12 +08:00
|
|
|
|
<Button>获取地址</Button>
|
|
|
|
|
</InputGroup>
|
|
|
|
|
</Form.Item>
|
|
|
|
|
<Form.Item label="名称">
|
|
|
|
|
<Input v-model:value={form.name} />
|
|
|
|
|
</Form.Item>
|
|
|
|
|
<Form.Item label="图标">
|
|
|
|
|
<TypeSelector
|
|
|
|
|
v-model:value={form.type}
|
|
|
|
|
v-model:icon={form.icon}
|
2024-09-18 16:27:43 +08:00
|
|
|
|
name={form.name}
|
2024-09-13 12:19:12 +08:00
|
|
|
|
text={form.text}
|
|
|
|
|
color={form.color}
|
|
|
|
|
background={form.background}
|
|
|
|
|
/>
|
|
|
|
|
</Form.Item>
|
2024-09-18 16:27:43 +08:00
|
|
|
|
{form.type === 1 && (
|
|
|
|
|
<>
|
2024-09-25 14:25:44 +08:00
|
|
|
|
<div class="flex">
|
|
|
|
|
<Form.Item
|
|
|
|
|
label="文字颜色"
|
|
|
|
|
class="w-0 flex-grow"
|
|
|
|
|
labelCol={{
|
|
|
|
|
span: 8
|
|
|
|
|
}}
|
|
|
|
|
>
|
|
|
|
|
<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>
|
2024-09-18 16:27:43 +08:00
|
|
|
|
<Form.Item label="图标文字">
|
|
|
|
|
<Input v-model:value={form.text} maxlength={2} />
|
|
|
|
|
</Form.Item>
|
|
|
|
|
</>
|
|
|
|
|
)}
|
2024-09-13 12:19:12 +08:00
|
|
|
|
<Form.Item label=" " colon={false}>
|
2024-09-18 16:27:43 +08:00
|
|
|
|
<Button
|
|
|
|
|
type="primary"
|
|
|
|
|
size="large"
|
|
|
|
|
icon={<CheckOutlined />}
|
|
|
|
|
onClick={() => {
|
|
|
|
|
if (form.type === 1 && !form.text && !form.name) {
|
|
|
|
|
globalToast.error('文字图标请至少填写文字或者名称')
|
|
|
|
|
return
|
|
|
|
|
}
|
|
|
|
|
const id = uuid()
|
|
|
|
|
const data: Block = {
|
2024-09-25 14:25:44 +08:00
|
|
|
|
id,
|
2024-09-18 16:27:43 +08:00
|
|
|
|
link: form.link,
|
|
|
|
|
name: '',
|
2024-09-25 14:25:44 +08:00
|
|
|
|
icon: form.type === 0 ? form.icon || info.icon : '',
|
2024-09-18 16:27:43 +08:00
|
|
|
|
background: form.type === 0 ? '' : form.background,
|
|
|
|
|
color: form.type === 0 ? '' : form.color,
|
2024-09-25 14:25:44 +08:00
|
|
|
|
w: 1,
|
|
|
|
|
h: 1,
|
2024-09-18 16:27:43 +08:00
|
|
|
|
text:
|
|
|
|
|
form.type === 0 ? '' : form.text || form.name.substring(0, 2).toLocaleUpperCase(),
|
2024-09-25 14:25:44 +08:00
|
|
|
|
label: form.name
|
2024-09-18 16:27:43 +08:00
|
|
|
|
}
|
2024-09-26 11:47:16 +08:00
|
|
|
|
layout.addBlock(data, addTo?.value)
|
2024-09-18 16:27:43 +08:00
|
|
|
|
}}
|
|
|
|
|
>
|
2024-09-13 12:19:12 +08:00
|
|
|
|
确定
|
|
|
|
|
</Button>
|
|
|
|
|
</Form.Item>
|
|
|
|
|
</Form>
|
|
|
|
|
</div>
|
|
|
|
|
)
|
2024-09-11 18:00:15 +08:00
|
|
|
|
})
|