mirror of
https://github.com/doocs/md.git
synced 2025-01-23 04:14:42 +08:00
chore: update layout
This commit is contained in:
parent
de2cc52e15
commit
28ec097bb6
@ -103,7 +103,7 @@ function tabChanged(tabName: string | number) {
|
||||
|
||||
<template>
|
||||
<transition enter-active-class="bounceInRight">
|
||||
<div v-show="displayStore.isShowCssEditor" class="cssEditor-wrapper order-1 h-full flex flex-col border-l-1">
|
||||
<div v-show="displayStore.isShowCssEditor" class="cssEditor-wrapper h-full flex flex-col border-l-1">
|
||||
<Tabs
|
||||
v-model="store.cssContentConfig.active"
|
||||
@update:model-value="tabChanged"
|
||||
|
@ -1,39 +1,16 @@
|
||||
<script setup lang="ts">
|
||||
import type { Format } from 'vue-pick-colors'
|
||||
import { Toaster } from '@/components/ui/sonner'
|
||||
import {
|
||||
altSign,
|
||||
codeBlockThemeOptions,
|
||||
colorOptions,
|
||||
ctrlKey,
|
||||
ctrlSign,
|
||||
fontFamilyOptions,
|
||||
fontSizeOptions,
|
||||
legendOptions,
|
||||
shiftSign,
|
||||
themeOptions,
|
||||
} from '@/config'
|
||||
import { useDisplayStore, useStore } from '@/stores'
|
||||
import {
|
||||
addPrefix,
|
||||
processClipboardContent,
|
||||
} from '@/utils'
|
||||
import {
|
||||
ChevronDownIcon,
|
||||
Moon,
|
||||
PanelLeftClose,
|
||||
PanelLeftOpen,
|
||||
Settings,
|
||||
Sun,
|
||||
} from 'lucide-vue-next'
|
||||
import PickColors from 'vue-pick-colors'
|
||||
import { useStore } from '@/stores'
|
||||
import { addPrefix, mergeCss, solveWeChatImage } from '@/utils'
|
||||
import { ChevronDownIcon, PanelRightClose, PanelRightOpen } from 'lucide-vue-next'
|
||||
|
||||
const emit = defineEmits([
|
||||
`addFormat`,
|
||||
`formatContent`,
|
||||
`startCopy`,
|
||||
`endCopy`,
|
||||
])
|
||||
const emit = defineEmits([`addFormat`, `formatContent`, `startCopy`, `endCopy`])
|
||||
|
||||
const formatItems = [
|
||||
{
|
||||
@ -69,7 +46,6 @@ const formatItems = [
|
||||
] as const
|
||||
|
||||
const store = useStore()
|
||||
const displayStore = useDisplayStore()
|
||||
|
||||
const { isDark, isCiteStatus, isCountStatus, output, primaryColor } = storeToRefs(store)
|
||||
|
||||
@ -123,54 +99,30 @@ function copy() {
|
||||
})
|
||||
}, 350)
|
||||
}
|
||||
|
||||
function customStyle() {
|
||||
displayStore.toggleShowCssEditor()
|
||||
setTimeout(() => {
|
||||
store.cssEditor!.refresh()
|
||||
}, 50)
|
||||
}
|
||||
|
||||
const pickColorsContainer = useTemplateRef<HTMLElement | undefined>(
|
||||
`pickColorsContainer`,
|
||||
)
|
||||
const format = ref<Format>(`rgb`)
|
||||
const formatOptions = ref<Format[]>([`rgb`, `hex`, `hsl`, `hsv`])
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<header class="header-container h-15 flex items-center px-5">
|
||||
<Menubar class="menubar mr-auto">
|
||||
<header class="header-container h-15 flex items-center justify-between border-b px-5">
|
||||
<div class="space-x-2 flex">
|
||||
<Menubar class="menubar">
|
||||
<FileDropdown />
|
||||
|
||||
<MenubarMenu>
|
||||
<MenubarTrigger> 格式 </MenubarTrigger>
|
||||
<MenubarContent class="w-60" align="start">
|
||||
<MenubarCheckboxItem
|
||||
v-for="{ label, kbd, emitArgs } in formatItems"
|
||||
:key="label"
|
||||
@click="
|
||||
emitArgs[0] === 'addFormat'
|
||||
? $emit(emitArgs[0], emitArgs[1])
|
||||
: $emit(emitArgs[0])
|
||||
"
|
||||
v-for="{ label, kbd, emitArgs } in formatItems" :key="label"
|
||||
@click="emitArgs[0] === 'addFormat' ? $emit(emitArgs[0], emitArgs[1]) : $emit(emitArgs[0])"
|
||||
>
|
||||
{{ label }}
|
||||
<MenubarShortcut>
|
||||
<kbd
|
||||
v-for="item in kbd"
|
||||
:key="item"
|
||||
class="mx-1 bg-gray-2 dark:bg-stone-9"
|
||||
>
|
||||
<kbd v-for="item in kbd" :key="item" class="mx-1 bg-gray-2 dark:bg-stone-9">
|
||||
{{ item }}
|
||||
</kbd>
|
||||
</MenubarShortcut>
|
||||
</MenubarCheckboxItem>
|
||||
<MenubarSeparator />
|
||||
<MenubarCheckboxItem
|
||||
:checked="isCiteStatus"
|
||||
@click="citeStatusChanged()"
|
||||
>
|
||||
<MenubarCheckboxItem :checked="isCiteStatus" @click="citeStatusChanged()">
|
||||
微信外链转底部引用
|
||||
</MenubarCheckboxItem>
|
||||
<MenubarSeparator />
|
||||
@ -186,350 +138,10 @@ const formatOptions = ref<Format[]>([`rgb`, `hex`, `hsl`, `hsv`])
|
||||
<StyleDropdown />
|
||||
<HelpDropdown />
|
||||
</Menubar>
|
||||
|
||||
<Button
|
||||
v-if="!store.isOpenPostSlider"
|
||||
variant="outline"
|
||||
class="mr-2"
|
||||
@click="store.isOpenPostSlider = true"
|
||||
>
|
||||
<PanelLeftOpen class="size-4" />
|
||||
</Button>
|
||||
<Button
|
||||
v-else
|
||||
variant="outline"
|
||||
class="mr-2"
|
||||
@click="store.isOpenPostSlider = false"
|
||||
>
|
||||
<PanelLeftClose class="size-4" />
|
||||
</Button>
|
||||
<Popover>
|
||||
<PopoverTrigger>
|
||||
<Button variant="outline">
|
||||
<Settings class="h-4 w-4" />
|
||||
</Button>
|
||||
</PopoverTrigger>
|
||||
<PopoverContent class="h-100 w-100 overflow-auto px-6" align="end">
|
||||
<div class="space-y-4">
|
||||
<div class="space-y-2">
|
||||
<h2>主题</h2>
|
||||
<div class="grid grid-cols-3 justify-items-center gap-2">
|
||||
<Button
|
||||
v-for="{ label, value } in themeOptions"
|
||||
:key="value"
|
||||
class="w-full"
|
||||
variant="outline"
|
||||
:class="{
|
||||
'border-black dark:border-white': store.theme === value,
|
||||
}"
|
||||
@click="store.themeChanged(value)"
|
||||
>
|
||||
{{ label }}
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="space-y-2">
|
||||
<h2>字体</h2>
|
||||
<div class="grid grid-cols-3 justify-items-center gap-2">
|
||||
<Button
|
||||
v-for="{ label, value } in fontFamilyOptions"
|
||||
:key="value"
|
||||
variant="outline"
|
||||
class="w-full"
|
||||
:class="{
|
||||
'border-black dark:border-white': store.fontFamily === value,
|
||||
}"
|
||||
@click="store.fontChanged(value)"
|
||||
>
|
||||
{{ label }}
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="space-y-2">
|
||||
<h2>字号</h2>
|
||||
<div class="grid grid-cols-5 justify-items-center gap-2">
|
||||
<Button
|
||||
v-for="{ value, desc } in fontSizeOptions"
|
||||
:key="value"
|
||||
variant="outline"
|
||||
class="w-full"
|
||||
:class="{
|
||||
'border-black dark:border-white': store.fontSize === value,
|
||||
}"
|
||||
@click="store.sizeChanged(value)"
|
||||
>
|
||||
{{ desc }}
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="space-y-2">
|
||||
<h2>主题色</h2>
|
||||
<div class="grid grid-cols-3 justify-items-center gap-2">
|
||||
<Button
|
||||
v-for="{ label, value } in colorOptions"
|
||||
:key="value"
|
||||
class="w-full"
|
||||
variant="outline"
|
||||
:class="{
|
||||
'border-black dark:border-white':
|
||||
store.primaryColor === value,
|
||||
}"
|
||||
@click="store.colorChanged(value)"
|
||||
>
|
||||
<span
|
||||
class="mr-2 inline-block h-4 w-4 rounded-full"
|
||||
:style="{
|
||||
background: value,
|
||||
}"
|
||||
/>
|
||||
{{ label }}
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="space-y-2">
|
||||
<h2>自定义主题色</h2>
|
||||
<div ref="pickColorsContainer">
|
||||
<PickColors
|
||||
v-if="pickColorsContainer"
|
||||
v-model:value="primaryColor"
|
||||
show-alpha
|
||||
:format="format"
|
||||
:format-options="formatOptions"
|
||||
:theme="store.isDark ? 'dark' : 'light'"
|
||||
:popup-container="pickColorsContainer"
|
||||
@change="store.colorChanged"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="space-y-2">
|
||||
<h2>代码块主题</h2>
|
||||
<div>
|
||||
<Select
|
||||
v-model="store.codeBlockTheme"
|
||||
@update:model-value="store.codeBlockThemeChanged"
|
||||
>
|
||||
<SelectTrigger>
|
||||
<SelectValue placeholder="Select a fruit" />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectItem
|
||||
v-for="{ label, value } in codeBlockThemeOptions"
|
||||
:key="label"
|
||||
:value="value"
|
||||
>
|
||||
{{ label }}
|
||||
</SelectItem>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="space-y-2">
|
||||
<h2>图注格式</h2>
|
||||
<div class="grid grid-cols-3 justify-items-center gap-2">
|
||||
<Button
|
||||
v-for="{ label, value } in legendOptions"
|
||||
:key="value"
|
||||
class="w-full"
|
||||
variant="outline"
|
||||
:class="{
|
||||
'border-black dark:border-white': store.legend === value,
|
||||
}"
|
||||
@click="store.legendChanged(value)"
|
||||
>
|
||||
{{ label }}
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="space-y-2">
|
||||
<h2>Mac 代码块</h2>
|
||||
<div class="grid grid-cols-5 justify-items-center gap-2">
|
||||
<Button
|
||||
class="w-full"
|
||||
variant="outline"
|
||||
:class="{
|
||||
'border-black dark:border-white': store.isMacCodeBlock,
|
||||
}"
|
||||
@click="!store.isMacCodeBlock && store.macCodeBlockChanged()"
|
||||
>
|
||||
开启
|
||||
</Button>
|
||||
<Button
|
||||
class="w-full"
|
||||
variant="outline"
|
||||
:class="{
|
||||
'border-black dark:border-white': !store.isMacCodeBlock,
|
||||
}"
|
||||
@click="store.isMacCodeBlock && store.macCodeBlockChanged()"
|
||||
>
|
||||
关闭
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="space-y-2">
|
||||
<h2>微信外链转底部引用</h2>
|
||||
<div class="grid grid-cols-5 justify-items-center gap-2">
|
||||
<Button
|
||||
class="w-full"
|
||||
variant="outline"
|
||||
:class="{
|
||||
'border-black dark:border-white': store.isCiteStatus,
|
||||
}"
|
||||
@click="!store.isCiteStatus && store.citeStatusChanged()"
|
||||
>
|
||||
开启
|
||||
</Button>
|
||||
<Button
|
||||
class="w-full"
|
||||
variant="outline"
|
||||
:class="{
|
||||
'border-black dark:border-white': !store.isCiteStatus,
|
||||
}"
|
||||
@click="store.isCiteStatus && store.citeStatusChanged()"
|
||||
>
|
||||
关闭
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="space-y-2">
|
||||
<h2>统计字数和阅读时间</h2>
|
||||
<div class="grid grid-cols-5 justify-items-center gap-2">
|
||||
<Button
|
||||
class="w-full"
|
||||
variant="outline"
|
||||
:class="{
|
||||
'border-black dark:border-white': store.isCountStatus,
|
||||
}"
|
||||
@click="!store.isCountStatus && store.countStatusChanged()"
|
||||
>
|
||||
开启
|
||||
</Button>
|
||||
<Button
|
||||
class="w-full"
|
||||
variant="outline"
|
||||
:class="{
|
||||
'border-black dark:border-white': !store.isCountStatus,
|
||||
}"
|
||||
@click="store.isCountStatus && store.countStatusChanged()"
|
||||
>
|
||||
关闭
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="space-y-2">
|
||||
<h2>段落首行缩进</h2>
|
||||
<div class="grid grid-cols-5 justify-items-center gap-2">
|
||||
<Button
|
||||
class="w-full"
|
||||
variant="outline"
|
||||
:class="{
|
||||
'border-black dark:border-white': store.isUseIndent,
|
||||
}"
|
||||
@click="!store.isUseIndent && store.useIndentChanged()"
|
||||
>
|
||||
开启
|
||||
</Button>
|
||||
<Button
|
||||
class="w-full"
|
||||
variant="outline"
|
||||
:class="{
|
||||
'border-black dark:border-white': !store.isUseIndent,
|
||||
}"
|
||||
@click="store.isUseIndent && store.useIndentChanged()"
|
||||
>
|
||||
关闭
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="space-y-2">
|
||||
<h2>自定义 CSS 面板</h2>
|
||||
<div class="grid grid-cols-5 justify-items-center gap-2">
|
||||
<Button
|
||||
class="w-full"
|
||||
variant="outline"
|
||||
:class="{
|
||||
'border-black dark:border-white':
|
||||
displayStore.isShowCssEditor,
|
||||
}"
|
||||
@click="!displayStore.isShowCssEditor && customStyle()"
|
||||
>
|
||||
开启
|
||||
</Button>
|
||||
<Button
|
||||
class="w-full"
|
||||
variant="outline"
|
||||
:class="{
|
||||
'border-black dark:border-white': !displayStore.isShowCssEditor,
|
||||
}"
|
||||
@click="displayStore.isShowCssEditor && customStyle()"
|
||||
>
|
||||
关闭
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="space-y-2">
|
||||
<h2>编辑区位置</h2>
|
||||
<div class="grid grid-cols-5 justify-items-center gap-2">
|
||||
<Button
|
||||
class="w-full"
|
||||
variant="outline"
|
||||
:class="{
|
||||
'border-black dark:border-white': store.isEditOnLeft,
|
||||
}"
|
||||
@click="!store.isEditOnLeft && store.toggleEditOnLeft()"
|
||||
>
|
||||
左侧
|
||||
</Button>
|
||||
<Button
|
||||
class="w-full"
|
||||
variant="outline"
|
||||
:class="{
|
||||
'border-black dark:border-white': !store.isEditOnLeft,
|
||||
}"
|
||||
@click="store.isEditOnLeft && store.toggleEditOnLeft()"
|
||||
>
|
||||
右侧
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="space-y-2">
|
||||
<h2>模式</h2>
|
||||
<div class="grid grid-cols-5 justify-items-center gap-2">
|
||||
<Button
|
||||
class="w-full"
|
||||
variant="outline"
|
||||
:class="{
|
||||
'border-black dark:border-white': !isDark,
|
||||
}"
|
||||
@click="store.toggleDark(false)"
|
||||
>
|
||||
<Sun class="h-4 w-4" />
|
||||
</Button>
|
||||
<Button
|
||||
class="w-full"
|
||||
variant="outline"
|
||||
:class="{
|
||||
'border-black dark:border-white': isDark,
|
||||
}"
|
||||
@click="store.toggleDark(true)"
|
||||
>
|
||||
<Moon class="h-4 w-4" />
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="space-y-2">
|
||||
<h2>样式配置</h2>
|
||||
<Button @click="store.resetStyleConfirm">
|
||||
重置
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</PopoverContent>
|
||||
</Popover>
|
||||
|
||||
<div
|
||||
class="space-x-1 bg-background text-background-foreground mx-2 flex items-center border rounded-md"
|
||||
>
|
||||
<div class="space-x-2 flex">
|
||||
<div class="space-x-1 bg-background text-background-foreground mx-2 flex items-center border rounded-md">
|
||||
<Button variant="ghost" class="shadow-none" @click="copy">
|
||||
复制
|
||||
</Button>
|
||||
@ -540,7 +152,11 @@ const formatOptions = ref<Format[]>([`rgb`, `hex`, `hsl`, `hsv`])
|
||||
<ChevronDownIcon class="text-secondary-foreground h-4 w-4" />
|
||||
</Button>
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent align="end" :align-offset="-5" class="w-[200px]">
|
||||
<DropdownMenuContent
|
||||
align="end"
|
||||
:align-offset="-5"
|
||||
class="w-[200px]"
|
||||
>
|
||||
<DropdownMenuRadioGroup v-model="copyMode">
|
||||
<DropdownMenuRadioItem value="txt">
|
||||
公众号格式
|
||||
@ -555,7 +171,13 @@ const formatOptions = ref<Format[]>([`rgb`, `hex`, `hsl`, `hsv`])
|
||||
|
||||
<PostInfo />
|
||||
|
||||
<Button variant="outline" @click="store.isOpenRightSlider = !store.isOpenRightSlider">
|
||||
<PanelRightOpen v-show="!store.isOpenRightSlider" class="size-4" />
|
||||
<PanelRightClose v-show="store.isOpenRightSlider" class="size-4" />
|
||||
</Button>
|
||||
|
||||
<Toaster rich-colors position="top-center" />
|
||||
</div>
|
||||
</header>
|
||||
</template>
|
||||
|
||||
|
@ -57,7 +57,7 @@ function delPost() {
|
||||
|
||||
<template>
|
||||
<div
|
||||
class="overflow-hidden border-r bg-gray/20 transition-width dark:bg-gray/40"
|
||||
class="overflow-hidden bg-gray/20 transition-width dark:bg-gray/40"
|
||||
:class="{
|
||||
'w-0': !store.isOpenPostSlider,
|
||||
'w-50': store.isOpenPostSlider,
|
||||
|
271
src/components/CodemirrorEditor/RightSlider.vue
Normal file
271
src/components/CodemirrorEditor/RightSlider.vue
Normal file
@ -0,0 +1,271 @@
|
||||
<script setup lang="ts">
|
||||
import {
|
||||
codeBlockThemeOptions,
|
||||
colorOptions,
|
||||
fontFamilyOptions,
|
||||
fontSizeOptions,
|
||||
legendOptions,
|
||||
themeOptions,
|
||||
} from '@/config'
|
||||
import { useDisplayStore, useStore } from '@/stores'
|
||||
import { Moon, Sun } from 'lucide-vue-next'
|
||||
import PickColors, { type Format } from 'vue-pick-colors'
|
||||
|
||||
const store = useStore()
|
||||
const displayStore = useDisplayStore()
|
||||
|
||||
const { isDark, primaryColor } = storeToRefs(store)
|
||||
|
||||
function customStyle() {
|
||||
displayStore.toggleShowCssEditor()
|
||||
setTimeout(() => {
|
||||
store.cssEditor!.refresh()
|
||||
}, 50)
|
||||
}
|
||||
|
||||
const isOpen = ref(false)
|
||||
|
||||
const addPostInputVal = ref(``)
|
||||
|
||||
watch(isOpen, () => {
|
||||
if (isOpen.value) {
|
||||
addPostInputVal.value = ``
|
||||
}
|
||||
})
|
||||
|
||||
const pickColorsContainer = useTemplateRef<HTMLElement | undefined>(`pickColorsContainer`)
|
||||
const format = ref<Format>(`rgb`)
|
||||
const formatOptions = ref<Format[]>([`rgb`, `hex`, `hsl`, `hsv`])
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div
|
||||
class="overflow-hidden bg-gray/20 transition-width dark:bg-gray/40"
|
||||
:class="{
|
||||
'w-0': !store.isOpenRightSlider,
|
||||
'w-100': store.isOpenRightSlider,
|
||||
}"
|
||||
>
|
||||
<div class="space-y-4 h-full overflow-auto p-4">
|
||||
<div class="space-y-2">
|
||||
<h2>主题</h2>
|
||||
<div class="grid grid-cols-3 justify-items-center gap-2">
|
||||
<Button
|
||||
v-for="{ label, value } in themeOptions" :key="value" class="w-full" variant="outline" :class="{
|
||||
'border-black dark:border-white': store.theme === value,
|
||||
}" @click="store.themeChanged(value)"
|
||||
>
|
||||
{{ label }}
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="space-y-2">
|
||||
<h2>字体</h2>
|
||||
<div class="grid grid-cols-3 justify-items-center gap-2">
|
||||
<Button
|
||||
v-for="{ label, value } in fontFamilyOptions" :key="value" variant="outline" class="w-full"
|
||||
:class="{ 'border-black dark:border-white': store.fontFamily === value }"
|
||||
@click="store.fontChanged(value)"
|
||||
>
|
||||
{{ label }}
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="space-y-2">
|
||||
<h2>字号</h2>
|
||||
<div class="grid grid-cols-5 justify-items-center gap-2">
|
||||
<Button
|
||||
v-for="{ value, desc } in fontSizeOptions" :key="value" variant="outline" class="w-full" :class="{
|
||||
'border-black dark:border-white': store.fontSize === value,
|
||||
}" @click="store.sizeChanged(value)"
|
||||
>
|
||||
{{ desc }}
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="space-y-2">
|
||||
<h2>主题色</h2>
|
||||
<div class="grid grid-cols-3 justify-items-center gap-2">
|
||||
<Button
|
||||
v-for="{ label, value } in colorOptions" :key="value" class="w-full" variant="outline" :class="{
|
||||
'border-black dark:border-white': store.primaryColor === value,
|
||||
}" @click="store.colorChanged(value)"
|
||||
>
|
||||
<span
|
||||
class="mr-2 inline-block h-4 w-4 rounded-full" :style="{
|
||||
background: value,
|
||||
}"
|
||||
/>
|
||||
{{ label }}
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="space-y-2">
|
||||
<h2>自定义主题色</h2>
|
||||
<div ref="pickColorsContainer">
|
||||
<PickColors
|
||||
v-if="pickColorsContainer"
|
||||
v-model:value="primaryColor"
|
||||
show-alpha :format="format"
|
||||
:format-options="formatOptions"
|
||||
:theme="store.isDark ? 'dark' : 'light'"
|
||||
:popup-container="pickColorsContainer"
|
||||
@change="store.colorChanged"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="space-y-2">
|
||||
<h2>代码块主题</h2>
|
||||
<div>
|
||||
<Select v-model="store.codeBlockTheme" @update:model-value="store.codeBlockThemeChanged">
|
||||
<SelectTrigger>
|
||||
<SelectValue placeholder="Select a fruit" />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectItem v-for="{ label, value } in codeBlockThemeOptions" :key="label" :value="value">
|
||||
{{ label }}
|
||||
</SelectItem>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="space-y-2">
|
||||
<h2>图注格式</h2>
|
||||
<div class="grid grid-cols-3 justify-items-center gap-2">
|
||||
<Button
|
||||
v-for="{ label, value } in legendOptions" :key="value" class="w-full" variant="outline" :class="{
|
||||
'border-black dark:border-white': store.legend === value,
|
||||
}" @click="store.legendChanged(value)"
|
||||
>
|
||||
{{ label }}
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="space-y-2">
|
||||
<h2>Mac 代码块</h2>
|
||||
<div class="grid grid-cols-5 justify-items-center gap-2">
|
||||
<Button
|
||||
class="w-full" variant="outline" :class="{
|
||||
'border-black dark:border-white': store.isMacCodeBlock,
|
||||
}" @click="!store.isMacCodeBlock && store.macCodeBlockChanged()"
|
||||
>
|
||||
开启
|
||||
</Button>
|
||||
<Button
|
||||
class="w-full" variant="outline" :class="{
|
||||
'border-black dark:border-white': !store.isMacCodeBlock,
|
||||
}" @click="store.isMacCodeBlock && store.macCodeBlockChanged()"
|
||||
>
|
||||
关闭
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="space-y-2">
|
||||
<h2>微信外链转底部引用</h2>
|
||||
<div class="grid grid-cols-5 justify-items-center gap-2">
|
||||
<Button
|
||||
class="w-full" variant="outline" :class="{
|
||||
'border-black dark:border-white': store.isCiteStatus,
|
||||
}" @click="!store.isCiteStatus && store.citeStatusChanged()"
|
||||
>
|
||||
开启
|
||||
</Button>
|
||||
<Button
|
||||
class="w-full" variant="outline" :class="{
|
||||
'border-black dark:border-white': !store.isCiteStatus,
|
||||
}" @click="store.isCiteStatus && store.citeStatusChanged()"
|
||||
>
|
||||
关闭
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="space-y-2">
|
||||
<h2>段落首行缩进</h2>
|
||||
<div class="grid grid-cols-5 justify-items-center gap-2">
|
||||
<Button
|
||||
class="w-full" variant="outline" :class="{
|
||||
'border-black dark:border-white': store.isUseIndent,
|
||||
}" @click="!store.isUseIndent && store.useIndentChanged()"
|
||||
>
|
||||
开启
|
||||
</Button>
|
||||
<Button
|
||||
class="w-full" variant="outline" :class="{
|
||||
'border-black dark:border-white': !store.isUseIndent,
|
||||
}" @click="store.isUseIndent && store.useIndentChanged()"
|
||||
>
|
||||
关闭
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="space-y-2">
|
||||
<h2>自定义 CSS 面板</h2>
|
||||
<div class="grid grid-cols-5 justify-items-center gap-2">
|
||||
<Button
|
||||
class="w-full" variant="outline" :class="{
|
||||
'border-black dark:border-white': displayStore.isShowCssEditor,
|
||||
}" @click="!displayStore.isShowCssEditor && customStyle()"
|
||||
>
|
||||
开启
|
||||
</Button>
|
||||
<Button
|
||||
class="w-full" variant="outline" :class="{
|
||||
'border-black dark:border-white': !displayStore.isShowCssEditor,
|
||||
}" @click="displayStore.isShowCssEditor && customStyle()"
|
||||
>
|
||||
关闭
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="space-y-2">
|
||||
<h2>编辑区位置</h2>
|
||||
<div class="grid grid-cols-5 justify-items-center gap-2">
|
||||
<Button
|
||||
class="w-full" variant="outline" :class="{
|
||||
'border-black dark:border-white': store.isEditOnLeft,
|
||||
}" @click="!store.isEditOnLeft && store.toggleEditOnLeft()"
|
||||
>
|
||||
左侧
|
||||
</Button>
|
||||
<Button
|
||||
class="w-full" variant="outline" :class="{
|
||||
'border-black dark:border-white': !store.isEditOnLeft,
|
||||
}" @click="store.isEditOnLeft && store.toggleEditOnLeft()"
|
||||
>
|
||||
右侧
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="space-y-2">
|
||||
<h2>模式</h2>
|
||||
<div class="grid grid-cols-5 justify-items-center gap-2">
|
||||
<Button
|
||||
class="w-full" variant="outline" :class="{
|
||||
'border-black dark:border-white': !isDark,
|
||||
}" @click="store.toggleDark(false)"
|
||||
>
|
||||
<Sun class="h-4 w-4" />
|
||||
</Button>
|
||||
<Button
|
||||
class="w-full" variant="outline" :class="{
|
||||
'border-black dark:border-white': isDark,
|
||||
}" @click="store.toggleDark(true)"
|
||||
>
|
||||
<Moon class="h-4 w-4" />
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="space-y-2">
|
||||
<h2>样式配置</h2>
|
||||
<Button @click="store.resetStyleConfirm">
|
||||
重置
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped lang="less">
|
||||
|
||||
</style>
|
14
src/components/ui/tooltip/Tooltip.vue
Normal file
14
src/components/ui/tooltip/Tooltip.vue
Normal file
@ -0,0 +1,14 @@
|
||||
<script setup lang="ts">
|
||||
import { TooltipRoot, type TooltipRootEmits, type TooltipRootProps, useForwardPropsEmits } from 'radix-vue'
|
||||
|
||||
const props = defineProps<TooltipRootProps>()
|
||||
const emits = defineEmits<TooltipRootEmits>()
|
||||
|
||||
const forwarded = useForwardPropsEmits(props, emits)
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<TooltipRoot v-bind="forwarded">
|
||||
<slot />
|
||||
</TooltipRoot>
|
||||
</template>
|
31
src/components/ui/tooltip/TooltipContent.vue
Normal file
31
src/components/ui/tooltip/TooltipContent.vue
Normal file
@ -0,0 +1,31 @@
|
||||
<script setup lang="ts">
|
||||
import { cn } from '@/lib/utils'
|
||||
import { TooltipContent, type TooltipContentEmits, type TooltipContentProps, TooltipPortal, useForwardPropsEmits } from 'radix-vue'
|
||||
import { computed, type HTMLAttributes } from 'vue'
|
||||
|
||||
defineOptions({
|
||||
inheritAttrs: false,
|
||||
})
|
||||
|
||||
const props = withDefaults(defineProps<TooltipContentProps & { class?: HTMLAttributes[`class`] }>(), {
|
||||
sideOffset: 4,
|
||||
})
|
||||
|
||||
const emits = defineEmits<TooltipContentEmits>()
|
||||
|
||||
const delegatedProps = computed(() => {
|
||||
const { class: _, ...delegated } = props
|
||||
|
||||
return delegated
|
||||
})
|
||||
|
||||
const forwarded = useForwardPropsEmits(delegatedProps, emits)
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<TooltipPortal>
|
||||
<TooltipContent v-bind="{ ...forwarded, ...$attrs }" :class="cn('z-50 overflow-hidden rounded-md border bg-popover px-3 py-1.5 text-sm text-popover-foreground shadow-md animate-in fade-in-0 zoom-in-95 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2', props.class)">
|
||||
<slot />
|
||||
</TooltipContent>
|
||||
</TooltipPortal>
|
||||
</template>
|
11
src/components/ui/tooltip/TooltipProvider.vue
Normal file
11
src/components/ui/tooltip/TooltipProvider.vue
Normal file
@ -0,0 +1,11 @@
|
||||
<script setup lang="ts">
|
||||
import { TooltipProvider, type TooltipProviderProps } from 'radix-vue'
|
||||
|
||||
const props = defineProps<TooltipProviderProps>()
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<TooltipProvider v-bind="props">
|
||||
<slot />
|
||||
</TooltipProvider>
|
||||
</template>
|
11
src/components/ui/tooltip/TooltipTrigger.vue
Normal file
11
src/components/ui/tooltip/TooltipTrigger.vue
Normal file
@ -0,0 +1,11 @@
|
||||
<script setup lang="ts">
|
||||
import { TooltipTrigger, type TooltipTriggerProps } from 'radix-vue'
|
||||
|
||||
const props = defineProps<TooltipTriggerProps>()
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<TooltipTrigger v-bind="props">
|
||||
<slot />
|
||||
</TooltipTrigger>
|
||||
</template>
|
4
src/components/ui/tooltip/index.ts
Normal file
4
src/components/ui/tooltip/index.ts
Normal file
@ -0,0 +1,4 @@
|
||||
export { default as Tooltip } from './Tooltip.vue'
|
||||
export { default as TooltipContent } from './TooltipContent.vue'
|
||||
export { default as TooltipProvider } from './TooltipProvider.vue'
|
||||
export { default as TooltipTrigger } from './TooltipTrigger.vue'
|
@ -56,6 +56,8 @@ export const useStore = defineStore(`store`, () => {
|
||||
// 预备弃用
|
||||
const editorContent = useStorage(`__editor_content`, DEFAULT_CONTENT)
|
||||
|
||||
const isOpenRightSlider = useStorage(addPrefix(`is_open_right_slider`), false)
|
||||
|
||||
const isOpenPostSlider = useStorage(addPrefix(`is_open_post_slider`), false)
|
||||
// 文章列表
|
||||
const posts = useStorage(addPrefix(`posts`), [{
|
||||
@ -492,6 +494,7 @@ export const useStore = defineStore(`store`, () => {
|
||||
renamePost,
|
||||
delPost,
|
||||
isOpenPostSlider,
|
||||
isOpenRightSlider,
|
||||
}
|
||||
})
|
||||
|
||||
|
@ -9,11 +9,11 @@ import {
|
||||
} from '@/utils'
|
||||
import fileApi from '@/utils/file'
|
||||
import CodeMirror from 'codemirror'
|
||||
import { PanelLeftClose, PanelLeftOpen } from 'lucide-vue-next'
|
||||
|
||||
const store = useStore()
|
||||
const displayStore = useDisplayStore()
|
||||
const { isDark, output, editor, readingTime } = storeToRefs(store)
|
||||
const { isShowCssEditor } = storeToRefs(displayStore)
|
||||
|
||||
const {
|
||||
editorRefresh,
|
||||
@ -363,18 +363,45 @@ onMounted(() => {
|
||||
|
||||
<template>
|
||||
<div ref="container" class="container flex flex-col">
|
||||
<EditorHeader @add-format="addFormat" @format-content="formatContent" @start-copy="startCopy" @end-copy="endCopy" />
|
||||
<EditorHeader
|
||||
@add-format="addFormat"
|
||||
@format-content="formatContent"
|
||||
@start-copy="startCopy"
|
||||
@end-copy="endCopy"
|
||||
/>
|
||||
<main class="container-main flex-1">
|
||||
<div class="container-main-section h-full flex border-1">
|
||||
<div class="container-main-section h-full flex">
|
||||
<div class="flex flex-col border-r p-1">
|
||||
<TooltipProvider :delay-duration="200">
|
||||
<Tooltip>
|
||||
<TooltipTrigger as-child>
|
||||
<Button size="icon" variant="ghost" @click="store.isOpenPostSlider = !store.isOpenPostSlider">
|
||||
<PanelLeftOpen v-show="!store.isOpenPostSlider" class="size-4" />
|
||||
<PanelLeftClose v-show="store.isOpenPostSlider" class="size-4" />
|
||||
</Button>
|
||||
</TooltipTrigger>
|
||||
<TooltipContent side="right">
|
||||
{{ store.isOpenPostSlider ? "关闭" : "展开" }}
|
||||
</TooltipContent>
|
||||
</Tooltip>
|
||||
</TooltipProvider>
|
||||
</div>
|
||||
<PostSlider />
|
||||
<div
|
||||
ref="codeMirrorWrapper" class="codeMirror-wrapper flex-1 border-r-1" :class="{
|
||||
'order-1': !store.isEditOnLeft,
|
||||
ref="codeMirrorWrapper"
|
||||
class="codeMirror-wrapper flex-1"
|
||||
:class="{
|
||||
'order-1 border-l': !store.isEditOnLeft,
|
||||
'border-r': store.isEditOnLeft,
|
||||
}"
|
||||
>
|
||||
<ContextMenu>
|
||||
<ContextMenuTrigger>
|
||||
<textarea id="editor" type="textarea" placeholder="Your markdown text here." />
|
||||
<textarea
|
||||
id="editor"
|
||||
type="textarea"
|
||||
placeholder="Your markdown text here."
|
||||
/>
|
||||
</ContextMenuTrigger>
|
||||
<ContextMenuContent class="w-64">
|
||||
<ContextMenuItem inset @click="toggleShowUploadImgDialog()">
|
||||
@ -403,7 +430,11 @@ onMounted(() => {
|
||||
</ContextMenuContent>
|
||||
</ContextMenu>
|
||||
</div>
|
||||
<div id="preview" ref="preview" :span="isShowCssEditor ? 8 : 12" class="preview-wrapper flex-1 p-5">
|
||||
<div
|
||||
id="preview"
|
||||
ref="preview"
|
||||
class="preview-wrapper flex-1 p-5"
|
||||
>
|
||||
<div id="output-wrapper" :class="{ output_night: !backLight }">
|
||||
<div class="preview border shadow-xl">
|
||||
<section id="output" v-html="output" />
|
||||
@ -416,7 +447,8 @@ onMounted(() => {
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<CssEditor class="flex-1" />
|
||||
<CssEditor class="order-2 flex-1" />
|
||||
<RightSlider class="order-2" />
|
||||
</div>
|
||||
<footer class="flex flex-1 justify-end pr-5 text-[12px]">
|
||||
字数 {{ readingTime?.words }}, 阅读大约需 {{ Math.ceil(readingTime?.minutes ?? 0) }} 分钟
|
||||
@ -461,8 +493,6 @@ onMounted(() => {
|
||||
|
||||
.container-main {
|
||||
overflow: hidden;
|
||||
padding: 20px;
|
||||
padding-top: 0;
|
||||
}
|
||||
|
||||
#output-wrapper {
|
||||
|
Loading…
Reference in New Issue
Block a user