mirror of
https://github.com/doocs/md.git
synced 2025-02-10 16:55:49 +08:00
Merge c867c7728922afa53a35834c509bc3fdd5585e22 into c52593ba50ce437550a978622f1a8a1901a991fa
This commit is contained in:
commit
5b42e1fd33
@ -47,7 +47,8 @@
|
||||
"tailwindcss-animate": "^1.0.7",
|
||||
"tiny-oss": "^0.5.1",
|
||||
"uuid": "^11.0.3",
|
||||
"vue": "^3.5.13"
|
||||
"vue": "^3.5.13",
|
||||
"vue-sonner": "^1.3.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@antfu/eslint-config": "3.11.0",
|
||||
|
@ -1,74 +1,119 @@
|
||||
<script setup lang="ts">
|
||||
import { useDisplayStore, useStore } from '@/stores'
|
||||
import {
|
||||
AlertDialog,
|
||||
AlertDialogAction,
|
||||
AlertDialogCancel,
|
||||
AlertDialogContent,
|
||||
AlertDialogDescription,
|
||||
AlertDialogFooter,
|
||||
AlertDialogHeader,
|
||||
AlertDialogTitle,
|
||||
} from '@/components/ui/alert-dialog'
|
||||
import { Button } from '@/components/ui/button'
|
||||
import {
|
||||
Dialog,
|
||||
DialogContent,
|
||||
DialogDescription,
|
||||
DialogFooter,
|
||||
DialogHeader,
|
||||
DialogTitle,
|
||||
} from '@/components/ui/dialog'
|
||||
import { Input } from '@/components/ui/input'
|
||||
|
||||
import { ElMessage, ElMessageBox } from 'element-plus'
|
||||
import { useDisplayStore, useStore } from '@/stores'
|
||||
import { ref } from 'vue'
|
||||
import { toast } from 'vue-sonner'
|
||||
|
||||
const store = useStore()
|
||||
const displayStore = useDisplayStore()
|
||||
|
||||
function editTabName() {
|
||||
ElMessageBox.prompt(`请输入新的方案名称`, `编辑方案名称`, {
|
||||
confirmButtonText: `确认`,
|
||||
cancelButtonText: `取消`,
|
||||
inputValue: store.cssContentConfig.active,
|
||||
inputErrorMessage: `不能与现有方案重名`,
|
||||
inputValidator: store.validatorTabName,
|
||||
})
|
||||
.then(({ value }) => {
|
||||
if (!(`${value}`).trim()) {
|
||||
ElMessage.error(`修改失败,方案名不可为空`)
|
||||
return
|
||||
}
|
||||
store.renameTab(value)
|
||||
ElMessage.success(`修改成功~`)
|
||||
})
|
||||
const isOpenEditDialog = ref(false)
|
||||
const editInputVal = ref(``)
|
||||
|
||||
function rename(name: string) {
|
||||
editInputVal.value = name
|
||||
isOpenEditDialog.value = true
|
||||
}
|
||||
|
||||
function editTabName() {
|
||||
if (!(editInputVal.value).trim()) {
|
||||
toast.error(`新建失败,方案名不可为空`)
|
||||
return
|
||||
}
|
||||
|
||||
if (!store.validatorTabName(editInputVal.value)) {
|
||||
toast.error(`不能与现有方案重名`)
|
||||
return
|
||||
}
|
||||
store.renameTab(editInputVal.value)
|
||||
isOpenEditDialog.value = false
|
||||
toast.success(`修改成功~`)
|
||||
}
|
||||
|
||||
const isOpenAddDialog = ref(false)
|
||||
|
||||
const addInputVal = ref(``)
|
||||
|
||||
function addTab() {
|
||||
if (!(addInputVal.value).trim()) {
|
||||
toast.error(`新建失败,方案名不可为空`)
|
||||
return
|
||||
}
|
||||
|
||||
if (!store.validatorTabName(addInputVal.value)) {
|
||||
toast.error(`不能与现有方案重名`)
|
||||
return
|
||||
}
|
||||
store.addCssContentTab(addInputVal.value)
|
||||
isOpenAddDialog.value = false
|
||||
toast.success(`新建成功~`)
|
||||
}
|
||||
|
||||
const isOpenDelTabConfirmDialog = ref(false)
|
||||
const delTargetName = ref(``)
|
||||
|
||||
function handleTabsEdit(targetName: string, action: string) {
|
||||
if (action === `add`) {
|
||||
ElMessageBox.prompt(`请输入方案名称`, `新建自定义 CSS`, {
|
||||
confirmButtonText: `确认`,
|
||||
cancelButtonText: `取消`,
|
||||
inputValue: `方案${store.cssContentConfig.tabs.length + 1}`,
|
||||
inputErrorMessage: `不能与现有方案重名`,
|
||||
inputValidator: store.validatorTabName,
|
||||
})
|
||||
.then(({ value }) => {
|
||||
if (!(`${value}`).trim()) {
|
||||
ElMessage.error(`新建失败,方案名不可为空`)
|
||||
return
|
||||
}
|
||||
store.addCssContentTab(value)
|
||||
ElMessage.success(`新建成功~`)
|
||||
})
|
||||
addInputVal.value = `方案${store.cssContentConfig.tabs.length + 1}`
|
||||
isOpenAddDialog.value = true
|
||||
return
|
||||
}
|
||||
else if (action === `remove`) {
|
||||
const tabs = store.cssContentConfig.tabs
|
||||
if (tabs.length === 1) {
|
||||
ElMessage.warning(`至少保留一个方案`)
|
||||
return
|
||||
}
|
||||
let activeName = store.cssContentConfig.active
|
||||
if (activeName === targetName) {
|
||||
tabs.forEach((tab, index) => {
|
||||
if (tab.name === targetName) {
|
||||
const nextTab = tabs[index + 1] || tabs[index - 1]
|
||||
if (nextTab) {
|
||||
activeName = nextTab.name
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
store.tabChanged(activeName)
|
||||
store.cssContentConfig.tabs = tabs.filter(tab => tab.name !== targetName)
|
||||
if (action === `remove`) {
|
||||
delTargetName.value = targetName
|
||||
isOpenDelTabConfirmDialog.value = true
|
||||
}
|
||||
}
|
||||
|
||||
function delTab() {
|
||||
const tabs = store.cssContentConfig.tabs
|
||||
if (tabs.length === 1) {
|
||||
toast.warning(`至少保留一个方案`)
|
||||
return
|
||||
}
|
||||
|
||||
let activeName = store.cssContentConfig.active
|
||||
if (activeName === delTargetName.value) {
|
||||
tabs.forEach((tab, index) => {
|
||||
if (tab.name === delTargetName.value) {
|
||||
const nextTab = tabs[index + 1] || tabs[index - 1]
|
||||
if (nextTab) {
|
||||
activeName = nextTab.name
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
store.tabChanged(activeName)
|
||||
store.cssContentConfig.tabs = tabs.filter(tab => tab.name !== delTargetName.value)
|
||||
|
||||
toast.success(`删除成功~`)
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<transition enter-active-class="bounceInRight">
|
||||
<el-col v-show="displayStore.isShowCssEditor" :span="8" class="cssEditor-wrapper order-1 h-full flex flex-col border-l-1">
|
||||
<div v-show="displayStore.isShowCssEditor" class="cssEditor-wrapper order-1 h-full flex flex-col border-l-1">
|
||||
<el-tabs
|
||||
v-model="store.cssContentConfig.active"
|
||||
type="border-card"
|
||||
@ -87,7 +132,7 @@ function handleTabsEdit(targetName: string, action: string) {
|
||||
<el-icon
|
||||
v-if="store.cssContentConfig.active === item.name"
|
||||
class="ml-1"
|
||||
@click="editTabName()"
|
||||
@click="rename(item.name)"
|
||||
>
|
||||
<ElIconEditPen />
|
||||
</el-icon>
|
||||
@ -99,7 +144,66 @@ function handleTabsEdit(targetName: string, action: string) {
|
||||
type="textarea"
|
||||
placeholder="Your custom css here."
|
||||
/>
|
||||
</el-col>
|
||||
|
||||
<!-- 新增弹窗 -->
|
||||
<Dialog v-model:open="isOpenAddDialog">
|
||||
<DialogContent class="sm:max-w-[425px]">
|
||||
<DialogHeader>
|
||||
<DialogTitle>新建自定义 CSS</DialogTitle>
|
||||
<DialogDescription>
|
||||
请输入方案名称
|
||||
</DialogDescription>
|
||||
</DialogHeader>
|
||||
<Input v-model="addInputVal" />
|
||||
<DialogFooter>
|
||||
<Button variant="outline" @click="isOpenAddDialog = false">
|
||||
取消
|
||||
</Button>
|
||||
<Button @click="addTab()">
|
||||
保存
|
||||
</Button>
|
||||
</DialogFooter>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
|
||||
<!-- 重命名弹窗 -->
|
||||
<Dialog v-model:open="isOpenEditDialog">
|
||||
<DialogContent class="sm:max-w-[425px]">
|
||||
<DialogHeader>
|
||||
<DialogTitle>编辑方案名称</DialogTitle>
|
||||
<DialogDescription>
|
||||
请输入新的方案名称
|
||||
</DialogDescription>
|
||||
</DialogHeader>
|
||||
<Input v-model="editInputVal" />
|
||||
<DialogFooter>
|
||||
<Button variant="outline" @click="isOpenEditDialog = false">
|
||||
取消
|
||||
</Button>
|
||||
<Button @click="editTabName">
|
||||
保存
|
||||
</Button>
|
||||
</DialogFooter>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
|
||||
<AlertDialog v-model:open="isOpenDelTabConfirmDialog">
|
||||
<AlertDialogContent>
|
||||
<AlertDialogHeader>
|
||||
<AlertDialogTitle>提示</AlertDialogTitle>
|
||||
<AlertDialogDescription>
|
||||
此操作将删除该自定义方案,是否继续?
|
||||
</AlertDialogDescription>
|
||||
</AlertDialogHeader>
|
||||
<AlertDialogFooter>
|
||||
<AlertDialogCancel>取消</AlertDialogCancel>
|
||||
<AlertDialogAction @click="delTab">
|
||||
确认
|
||||
</AlertDialogAction>
|
||||
</AlertDialogFooter>
|
||||
</AlertDialogContent>
|
||||
</AlertDialog>
|
||||
</div>
|
||||
</transition>
|
||||
</template>
|
||||
|
||||
|
@ -12,7 +12,6 @@ import {
|
||||
legendOptions,
|
||||
themeOptions,
|
||||
} from '@/config'
|
||||
|
||||
import { useDisplayStore, useStore } from '@/stores'
|
||||
import { storeToRefs } from 'pinia'
|
||||
import { ref } from 'vue'
|
||||
|
@ -17,7 +17,7 @@ import {
|
||||
SelectTrigger,
|
||||
SelectValue,
|
||||
} from '@/components/ui/select'
|
||||
|
||||
import { Toaster } from '@/components/ui/sonner'
|
||||
import {
|
||||
altSign,
|
||||
codeBlockThemeOptions,
|
||||
@ -32,16 +32,14 @@ import {
|
||||
} from '@/config'
|
||||
import { useDisplayStore, useStore } from '@/stores'
|
||||
import { mergeCss, solveWeChatImage } from '@/utils'
|
||||
import { ElNotification } from 'element-plus'
|
||||
import { Moon, Paintbrush, Sun } from 'lucide-vue-next'
|
||||
import { storeToRefs } from 'pinia'
|
||||
|
||||
import { nextTick } from 'vue'
|
||||
import EditDropdown from './EditDropdown.vue'
|
||||
import { toast } from 'vue-sonner'
|
||||
|
||||
import EditDropdown from './EditDropdown.vue'
|
||||
import FileDropdown from './FileDropdown.vue'
|
||||
import HelpDropdown from './HelpDropdown.vue'
|
||||
|
||||
import PostInfo from './PostInfo.vue'
|
||||
import StyleDropdown from './StyleDropdown.vue'
|
||||
|
||||
@ -122,7 +120,7 @@ function copy() {
|
||||
// 公众号不支持 position, 转换为等价的 translateY
|
||||
.replace(/top:(.*?)em/g, `transform: translateY($1em)`)
|
||||
// 适配主题中的颜色变量
|
||||
.replaceAll(`var(--el-text-color-regular)`, `#3f3f3f`)
|
||||
.replaceAll(`hsl(var(--foreground))`, `#3f3f3f`)
|
||||
.replaceAll(`var(--blockquote-background)`, `#f7f7f7`)
|
||||
.replaceAll(`var(--md-primary-color)`, primaryColor.value)
|
||||
.replaceAll(/--md-primary-color:.+?;/g, ``)
|
||||
@ -152,13 +150,7 @@ function copy() {
|
||||
}
|
||||
|
||||
// 输出提示
|
||||
ElNotification({
|
||||
showClose: true,
|
||||
message: `已复制渲染后的文章到剪贴板,可直接到公众号后台粘贴`,
|
||||
offset: 80,
|
||||
duration: 1600,
|
||||
type: `success`,
|
||||
})
|
||||
toast.success(`已复制渲染后的文章到剪贴板,可直接到公众号后台粘贴`)
|
||||
|
||||
editorRefresh()
|
||||
emit(`endCopy`)
|
||||
@ -492,14 +484,9 @@ function customStyle() {
|
||||
</div>
|
||||
<div class="space-y-2">
|
||||
<h2>样式配置</h2>
|
||||
<div>
|
||||
<Button
|
||||
class="w-full"
|
||||
@click="store.resetStyleConfirm()"
|
||||
>
|
||||
重置
|
||||
</Button>
|
||||
</div>
|
||||
<Button @click="store.resetStyleConfirm">
|
||||
重置
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</PopoverContent>
|
||||
@ -509,6 +496,8 @@ function customStyle() {
|
||||
</Button>
|
||||
|
||||
<PostInfo />
|
||||
|
||||
<Toaster rich-colors position="top-center" />
|
||||
</header>
|
||||
</template>
|
||||
|
||||
|
@ -2,12 +2,10 @@
|
||||
import { Dialog, DialogContent, DialogHeader, DialogTitle } from '@/components/ui/dialog'
|
||||
import { useDisplayStore } from '@/stores'
|
||||
import { checkImage, removeLeft } from '@/utils'
|
||||
|
||||
import { UploadFilled } from '@element-plus/icons-vue'
|
||||
import CodeMirror from 'codemirror'
|
||||
|
||||
import { ElMessage } from 'element-plus'
|
||||
import { nextTick, onBeforeMount, ref, watch } from 'vue'
|
||||
import { toast } from 'vue-sonner'
|
||||
|
||||
const emit = defineEmits([`uploadImage`])
|
||||
|
||||
@ -164,28 +162,28 @@ onBeforeMount(() => {
|
||||
|
||||
function changeImgHost() {
|
||||
localStorage.setItem(`imgHost`, imgHost.value)
|
||||
ElMessage.success(`已成功切换图床`)
|
||||
toast.success(`已成功切换图床`)
|
||||
}
|
||||
|
||||
function saveGitHubConfiguration() {
|
||||
if (!(formGitHub.value.repo && formGitHub.value.accessToken)) {
|
||||
const blankElement = formGitHub.value.repo ? `token` : `GitHub 仓库`
|
||||
ElMessage.error(`参数「${blankElement}」不能为空`)
|
||||
toast.error(`参数「${blankElement}」不能为空`)
|
||||
return
|
||||
}
|
||||
|
||||
localStorage.setItem(`githubConfig`, JSON.stringify(formGitHub.value))
|
||||
ElMessage.success(`保存成功`)
|
||||
toast.success(`保存成功`)
|
||||
}
|
||||
|
||||
// const saveGiteeConfiguration = () => {
|
||||
// if (!(formGitee.value.repo && formGitee.value.accessToken)) {
|
||||
// const blankElement = formGitee.value.repo ? `私人令牌` : `Gitee 仓库`
|
||||
// ElMessage.error(`参数「${blankElement}」不能为空`)
|
||||
// toast.error(`参数「${blankElement}」不能为空`)
|
||||
// return
|
||||
// }
|
||||
// localStorage.setItem(`giteeConfig`, JSON.stringify(formGitee.value))
|
||||
// ElMessage.success(`保存成功`)
|
||||
// toast.success(`保存成功`)
|
||||
// }
|
||||
|
||||
function saveAliOSSConfiguration() {
|
||||
@ -197,11 +195,11 @@ function saveAliOSSConfiguration() {
|
||||
&& formAliOSS.value.region
|
||||
)
|
||||
) {
|
||||
ElMessage.error(`阿里云 OSS 参数配置不全`)
|
||||
toast.error(`阿里云 OSS 参数配置不全`)
|
||||
return
|
||||
}
|
||||
localStorage.setItem(`aliOSSConfig`, JSON.stringify(formAliOSS.value))
|
||||
ElMessage.success(`保存成功`)
|
||||
toast.success(`保存成功`)
|
||||
}
|
||||
function saveMinioOSSConfiguration() {
|
||||
if (
|
||||
@ -212,11 +210,11 @@ function saveMinioOSSConfiguration() {
|
||||
&& minioOSS.value.secretKey
|
||||
)
|
||||
) {
|
||||
ElMessage.error(`MinIO 参数配置不全`)
|
||||
toast.error(`MinIO 参数配置不全`)
|
||||
return
|
||||
}
|
||||
localStorage.setItem(`minioConfig`, JSON.stringify(minioOSS.value))
|
||||
ElMessage.success(`保存成功`)
|
||||
toast.success(`保存成功`)
|
||||
}
|
||||
function saveTxCOSConfiguration() {
|
||||
if (
|
||||
@ -227,11 +225,11 @@ function saveTxCOSConfiguration() {
|
||||
&& formTxCOS.value.region
|
||||
)
|
||||
) {
|
||||
ElMessage.error(`腾讯云 COS 参数配置不全`)
|
||||
toast.error(`腾讯云 COS 参数配置不全`)
|
||||
return
|
||||
}
|
||||
localStorage.setItem(`txCOSConfig`, JSON.stringify(formTxCOS.value))
|
||||
ElMessage.success(`保存成功`)
|
||||
toast.success(`保存成功`)
|
||||
}
|
||||
function saveQiniuConfiguration() {
|
||||
if (
|
||||
@ -242,24 +240,24 @@ function saveQiniuConfiguration() {
|
||||
&& formQiniu.value.domain
|
||||
)
|
||||
) {
|
||||
ElMessage.error(`七牛云 Kodo 参数配置不全`)
|
||||
toast.error(`七牛云 Kodo 参数配置不全`)
|
||||
return
|
||||
}
|
||||
localStorage.setItem(`qiniuConfig`, JSON.stringify(formQiniu.value))
|
||||
ElMessage.success(`保存成功`)
|
||||
toast.success(`保存成功`)
|
||||
}
|
||||
|
||||
function formCustomSave() {
|
||||
const str = formCustom.value.editor!.getValue()
|
||||
localStorage.setItem(`formCustomConfig`, str)
|
||||
ElMessage.success(`保存成功`)
|
||||
toast.success(`保存成功`)
|
||||
}
|
||||
|
||||
function beforeImageUpload(file: File) {
|
||||
// check image
|
||||
const checkResult = checkImage(file)
|
||||
if (!checkResult.ok) {
|
||||
ElMessage.error(checkResult.msg)
|
||||
toast.error(checkResult.msg)
|
||||
return false
|
||||
}
|
||||
// check image host
|
||||
@ -270,7 +268,7 @@ function beforeImageUpload(file: File) {
|
||||
const config = localStorage.getItem(`${imgHost}Config`)
|
||||
const isValidHost = imgHost === `default` || config
|
||||
if (!isValidHost) {
|
||||
ElMessage.error(`请先配置 ${imgHost} 图床参数`)
|
||||
toast.error(`请先配置 ${imgHost} 图床参数`)
|
||||
return false
|
||||
}
|
||||
return true
|
||||
|
@ -34,7 +34,7 @@ onMounted(() => {
|
||||
width: 100vw;
|
||||
height: 100vh;
|
||||
font-size: 18px;
|
||||
background-color: var(--el-bg-color-page);
|
||||
background-color: hsl(var(--background));
|
||||
|
||||
&::before {
|
||||
content: url('../assets/images/favicon.png');
|
||||
|
14
src/components/ui/alert-dialog/AlertDialog.vue
Normal file
14
src/components/ui/alert-dialog/AlertDialog.vue
Normal file
@ -0,0 +1,14 @@
|
||||
<script setup lang="ts">
|
||||
import { type AlertDialogEmits, type AlertDialogProps, AlertDialogRoot, useForwardPropsEmits } from 'radix-vue'
|
||||
|
||||
const props = defineProps<AlertDialogProps>()
|
||||
const emits = defineEmits<AlertDialogEmits>()
|
||||
|
||||
const forwarded = useForwardPropsEmits(props, emits)
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<AlertDialogRoot v-bind="forwarded">
|
||||
<slot />
|
||||
</AlertDialogRoot>
|
||||
</template>
|
20
src/components/ui/alert-dialog/AlertDialogAction.vue
Normal file
20
src/components/ui/alert-dialog/AlertDialogAction.vue
Normal file
@ -0,0 +1,20 @@
|
||||
<script setup lang="ts">
|
||||
import { buttonVariants } from '@/components/ui/button'
|
||||
import { cn } from '@/lib/utils'
|
||||
import { AlertDialogAction, type AlertDialogActionProps } from 'radix-vue'
|
||||
import { computed, type HTMLAttributes } from 'vue'
|
||||
|
||||
const props = defineProps<AlertDialogActionProps & { class?: HTMLAttributes[`class`] }>()
|
||||
|
||||
const delegatedProps = computed(() => {
|
||||
const { class: _, ...delegated } = props
|
||||
|
||||
return delegated
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<AlertDialogAction v-bind="delegatedProps" :class="cn(buttonVariants(), props.class)">
|
||||
<slot />
|
||||
</AlertDialogAction>
|
||||
</template>
|
27
src/components/ui/alert-dialog/AlertDialogCancel.vue
Normal file
27
src/components/ui/alert-dialog/AlertDialogCancel.vue
Normal file
@ -0,0 +1,27 @@
|
||||
<script setup lang="ts">
|
||||
import { buttonVariants } from '@/components/ui/button'
|
||||
import { cn } from '@/lib/utils'
|
||||
import { AlertDialogCancel, type AlertDialogCancelProps } from 'radix-vue'
|
||||
import { computed, type HTMLAttributes } from 'vue'
|
||||
|
||||
const props = defineProps<AlertDialogCancelProps & { class?: HTMLAttributes[`class`] }>()
|
||||
|
||||
const delegatedProps = computed(() => {
|
||||
const { class: _, ...delegated } = props
|
||||
|
||||
return delegated
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<AlertDialogCancel
|
||||
v-bind="delegatedProps"
|
||||
:class="cn(
|
||||
buttonVariants({ variant: 'outline' }),
|
||||
'mt-2 sm:mt-0',
|
||||
props.class,
|
||||
)"
|
||||
>
|
||||
<slot />
|
||||
</AlertDialogCancel>
|
||||
</template>
|
42
src/components/ui/alert-dialog/AlertDialogContent.vue
Normal file
42
src/components/ui/alert-dialog/AlertDialogContent.vue
Normal file
@ -0,0 +1,42 @@
|
||||
<script setup lang="ts">
|
||||
import { cn } from '@/lib/utils'
|
||||
import {
|
||||
AlertDialogContent,
|
||||
type AlertDialogContentEmits,
|
||||
type AlertDialogContentProps,
|
||||
AlertDialogOverlay,
|
||||
AlertDialogPortal,
|
||||
useForwardPropsEmits,
|
||||
} from 'radix-vue'
|
||||
import { computed, type HTMLAttributes } from 'vue'
|
||||
|
||||
const props = defineProps<AlertDialogContentProps & { class?: HTMLAttributes[`class`] }>()
|
||||
const emits = defineEmits<AlertDialogContentEmits>()
|
||||
|
||||
const delegatedProps = computed(() => {
|
||||
const { class: _, ...delegated } = props
|
||||
|
||||
return delegated
|
||||
})
|
||||
|
||||
const forwarded = useForwardPropsEmits(delegatedProps, emits)
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<AlertDialogPortal>
|
||||
<AlertDialogOverlay
|
||||
class="data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 fixed inset-0 z-50 bg-black/80"
|
||||
/>
|
||||
<AlertDialogContent
|
||||
v-bind="forwarded"
|
||||
:class="
|
||||
cn(
|
||||
'fixed left-1/2 top-1/2 z-50 grid w-full max-w-lg -translate-x-1/2 -translate-y-1/2 gap-4 border bg-background p-6 shadow-lg duration-200 data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[state=closed]:slide-out-to-left-1/2 data-[state=closed]:slide-out-to-top-[48%] data-[state=open]:slide-in-from-left-1/2 data-[state=open]:slide-in-from-top-[48%] sm:rounded-lg',
|
||||
props.class,
|
||||
)
|
||||
"
|
||||
>
|
||||
<slot />
|
||||
</AlertDialogContent>
|
||||
</AlertDialogPortal>
|
||||
</template>
|
25
src/components/ui/alert-dialog/AlertDialogDescription.vue
Normal file
25
src/components/ui/alert-dialog/AlertDialogDescription.vue
Normal file
@ -0,0 +1,25 @@
|
||||
<script setup lang="ts">
|
||||
import { cn } from '@/lib/utils'
|
||||
import {
|
||||
AlertDialogDescription,
|
||||
type AlertDialogDescriptionProps,
|
||||
} from 'radix-vue'
|
||||
import { computed, type HTMLAttributes } from 'vue'
|
||||
|
||||
const props = defineProps<AlertDialogDescriptionProps & { class?: HTMLAttributes[`class`] }>()
|
||||
|
||||
const delegatedProps = computed(() => {
|
||||
const { class: _, ...delegated } = props
|
||||
|
||||
return delegated
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<AlertDialogDescription
|
||||
v-bind="delegatedProps"
|
||||
:class="cn('text-sm text-muted-foreground', props.class)"
|
||||
>
|
||||
<slot />
|
||||
</AlertDialogDescription>
|
||||
</template>
|
21
src/components/ui/alert-dialog/AlertDialogFooter.vue
Normal file
21
src/components/ui/alert-dialog/AlertDialogFooter.vue
Normal file
@ -0,0 +1,21 @@
|
||||
<script setup lang="ts">
|
||||
import type { HTMLAttributes } from 'vue'
|
||||
import { cn } from '@/lib/utils'
|
||||
|
||||
const props = defineProps<{
|
||||
class?: HTMLAttributes[`class`]
|
||||
}>()
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div
|
||||
:class="
|
||||
cn(
|
||||
'flex flex-col-reverse sm:flex-row sm:justify-end sm:gap-x-2',
|
||||
props.class,
|
||||
)
|
||||
"
|
||||
>
|
||||
<slot />
|
||||
</div>
|
||||
</template>
|
16
src/components/ui/alert-dialog/AlertDialogHeader.vue
Normal file
16
src/components/ui/alert-dialog/AlertDialogHeader.vue
Normal file
@ -0,0 +1,16 @@
|
||||
<script setup lang="ts">
|
||||
import type { HTMLAttributes } from 'vue'
|
||||
import { cn } from '@/lib/utils'
|
||||
|
||||
const props = defineProps<{
|
||||
class?: HTMLAttributes[`class`]
|
||||
}>()
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div
|
||||
:class="cn('flex flex-col gap-y-2 text-center sm:text-left', props.class)"
|
||||
>
|
||||
<slot />
|
||||
</div>
|
||||
</template>
|
22
src/components/ui/alert-dialog/AlertDialogTitle.vue
Normal file
22
src/components/ui/alert-dialog/AlertDialogTitle.vue
Normal file
@ -0,0 +1,22 @@
|
||||
<script setup lang="ts">
|
||||
import { cn } from '@/lib/utils'
|
||||
import { AlertDialogTitle, type AlertDialogTitleProps } from 'radix-vue'
|
||||
import { computed, type HTMLAttributes } from 'vue'
|
||||
|
||||
const props = defineProps<AlertDialogTitleProps & { class?: HTMLAttributes[`class`] }>()
|
||||
|
||||
const delegatedProps = computed(() => {
|
||||
const { class: _, ...delegated } = props
|
||||
|
||||
return delegated
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<AlertDialogTitle
|
||||
v-bind="delegatedProps"
|
||||
:class="cn('text-lg font-semibold', props.class)"
|
||||
>
|
||||
<slot />
|
||||
</AlertDialogTitle>
|
||||
</template>
|
11
src/components/ui/alert-dialog/AlertDialogTrigger.vue
Normal file
11
src/components/ui/alert-dialog/AlertDialogTrigger.vue
Normal file
@ -0,0 +1,11 @@
|
||||
<script setup lang="ts">
|
||||
import { AlertDialogTrigger, type AlertDialogTriggerProps } from 'radix-vue'
|
||||
|
||||
const props = defineProps<AlertDialogTriggerProps>()
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<AlertDialogTrigger v-bind="props">
|
||||
<slot />
|
||||
</AlertDialogTrigger>
|
||||
</template>
|
9
src/components/ui/alert-dialog/index.ts
Normal file
9
src/components/ui/alert-dialog/index.ts
Normal file
@ -0,0 +1,9 @@
|
||||
export { default as AlertDialog } from './AlertDialog.vue'
|
||||
export { default as AlertDialogAction } from './AlertDialogAction.vue'
|
||||
export { default as AlertDialogCancel } from './AlertDialogCancel.vue'
|
||||
export { default as AlertDialogContent } from './AlertDialogContent.vue'
|
||||
export { default as AlertDialogDescription } from './AlertDialogDescription.vue'
|
||||
export { default as AlertDialogFooter } from './AlertDialogFooter.vue'
|
||||
export { default as AlertDialogHeader } from './AlertDialogHeader.vue'
|
||||
export { default as AlertDialogTitle } from './AlertDialogTitle.vue'
|
||||
export { default as AlertDialogTrigger } from './AlertDialogTrigger.vue'
|
24
src/components/ui/input/Input.vue
Normal file
24
src/components/ui/input/Input.vue
Normal file
@ -0,0 +1,24 @@
|
||||
<script setup lang="ts">
|
||||
import type { HTMLAttributes } from 'vue'
|
||||
import { cn } from '@/lib/utils'
|
||||
import { useVModel } from '@vueuse/core'
|
||||
|
||||
const props = defineProps<{
|
||||
defaultValue?: string | number
|
||||
modelValue?: string | number
|
||||
class?: HTMLAttributes[`class`]
|
||||
}>()
|
||||
|
||||
const emits = defineEmits<{
|
||||
(e: `update:modelValue`, payload: string | number): void
|
||||
}>()
|
||||
|
||||
const modelValue = useVModel(props, `modelValue`, emits, {
|
||||
passive: true,
|
||||
defaultValue: props.defaultValue,
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<input v-model="modelValue" :class="cn('flex h-10 w-full rounded-md border border-input bg-background px-3 py-2 text-sm ring-offset-background file:border-0 file:bg-transparent file:text-sm file:font-medium placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50', props.class)">
|
||||
</template>
|
1
src/components/ui/input/index.ts
Normal file
1
src/components/ui/input/index.ts
Normal file
@ -0,0 +1 @@
|
||||
export { default as Input } from './Input.vue'
|
22
src/components/ui/sonner/Sonner.vue
Normal file
22
src/components/ui/sonner/Sonner.vue
Normal file
@ -0,0 +1,22 @@
|
||||
<script lang="ts" setup>
|
||||
import { Toaster as Sonner, type ToasterProps } from 'vue-sonner'
|
||||
|
||||
const props = defineProps<ToasterProps>()
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Sonner
|
||||
class="toaster group"
|
||||
v-bind="props"
|
||||
:toast-options="{
|
||||
classes: {
|
||||
toast: 'group toast group-[.toaster]:bg-background group-[.toaster]:text-foreground group-[.toaster]:border-border group-[.toaster]:shadow-lg',
|
||||
description: 'group-[.toast]:text-muted-foreground',
|
||||
actionButton:
|
||||
'group-[.toast]:bg-primary group-[.toast]:text-primary-foreground',
|
||||
cancelButton:
|
||||
'group-[.toast]:bg-muted group-[.toast]:text-muted-foreground',
|
||||
},
|
||||
}"
|
||||
/>
|
||||
</template>
|
1
src/components/ui/sonner/index.ts
Normal file
1
src/components/ui/sonner/index.ts
Normal file
@ -0,0 +1 @@
|
||||
export { default as Toaster } from './Sonner.vue'
|
@ -15,7 +15,7 @@ const defaultTheme: Theme = {
|
||||
'padding': `0 1em`,
|
||||
'border-bottom': `2px solid var(--md-primary-color)`,
|
||||
'margin': `2em auto 1em`,
|
||||
'color': `var(--el-text-color-regular)`,
|
||||
'color': `hsl(var(--foreground))`,
|
||||
'font-size': `1.2em`,
|
||||
'font-weight': `bold`,
|
||||
'text-align': `center`,
|
||||
@ -38,7 +38,7 @@ const defaultTheme: Theme = {
|
||||
'padding-left': `8px`,
|
||||
'border-left': `3px solid var(--md-primary-color)`,
|
||||
'margin': `2em 8px 0.75em 0`,
|
||||
'color': `var(--el-text-color-regular)`,
|
||||
'color': `hsl(var(--foreground))`,
|
||||
'font-size': `1.1em`,
|
||||
'font-weight': `bold`,
|
||||
'line-height': `1.2`,
|
||||
@ -71,7 +71,7 @@ const defaultTheme: Theme = {
|
||||
p: {
|
||||
'margin': `1.5em 8px`,
|
||||
'letter-spacing': `0.1em`,
|
||||
'color': `var(--el-text-color-regular)`,
|
||||
'color': `hsl(var(--foreground))`,
|
||||
'text-align': `justify`,
|
||||
},
|
||||
|
||||
@ -91,7 +91,7 @@ const defaultTheme: Theme = {
|
||||
'display': `block`,
|
||||
'font-size': `1em`,
|
||||
'letter-spacing': `0.1em`,
|
||||
'color': `var(--el-text-color-regular)`,
|
||||
'color': `hsl(var(--foreground))`,
|
||||
},
|
||||
|
||||
blockquote_note: {
|
||||
@ -181,7 +181,7 @@ const defaultTheme: Theme = {
|
||||
ol: {
|
||||
'padding-left': `1em`,
|
||||
'margin-left': `0`,
|
||||
'color': `var(--el-text-color-regular)`,
|
||||
'color': `hsl(var(--foreground))`,
|
||||
},
|
||||
|
||||
// 无序列表
|
||||
@ -189,18 +189,18 @@ const defaultTheme: Theme = {
|
||||
'list-style': `circle`,
|
||||
'padding-left': `1em`,
|
||||
'margin-left': `0`,
|
||||
'color': `var(--el-text-color-regular)`,
|
||||
'color': `hsl(var(--foreground))`,
|
||||
},
|
||||
|
||||
footnotes: {
|
||||
'margin': `0.5em 8px`,
|
||||
'font-size': `80%`,
|
||||
'color': `var(--el-text-color-regular)`,
|
||||
'color': `hsl(var(--foreground))`,
|
||||
},
|
||||
|
||||
figure: {
|
||||
margin: `1.5em 8px`,
|
||||
color: `var(--el-text-color-regular)`,
|
||||
color: `hsl(var(--foreground))`,
|
||||
},
|
||||
|
||||
hr: {
|
||||
@ -218,7 +218,7 @@ const defaultTheme: Theme = {
|
||||
'text-indent': `-1em`,
|
||||
'display': `block`,
|
||||
'margin': `0.2em 8px`,
|
||||
'color': `var(--el-text-color-regular)`,
|
||||
'color': `hsl(var(--foreground))`,
|
||||
},
|
||||
|
||||
codespan: {
|
||||
@ -255,13 +255,13 @@ const defaultTheme: Theme = {
|
||||
'border-collapse': `collapse`,
|
||||
'text-align': `center`,
|
||||
'margin': `1em 8px`,
|
||||
'color': `var(--el-text-color-regular)`,
|
||||
'color': `hsl(var(--foreground))`,
|
||||
},
|
||||
|
||||
thead: {
|
||||
'background': `rgba(0, 0, 0, 0.05)`,
|
||||
'font-weight': `bold`,
|
||||
'color': `var(--el-text-color-regular)`,
|
||||
'color': `hsl(var(--foreground))`,
|
||||
},
|
||||
|
||||
td: {
|
||||
@ -273,7 +273,7 @@ const defaultTheme: Theme = {
|
||||
|
||||
footnote: {
|
||||
'font-size': `12px`,
|
||||
'color': `var(--el-text-color-regular)`,
|
||||
'color': `hsl(var(--foreground))`,
|
||||
},
|
||||
|
||||
figcaption: {
|
||||
@ -403,7 +403,7 @@ const graceTheme = toMerged(defaultTheme, {
|
||||
'border-spacing': `0`,
|
||||
'border-radius': `8px`,
|
||||
'margin': `1em 8px`,
|
||||
'color': `var(--el-text-color-regular)`,
|
||||
'color': `hsl(var(--foreground))`,
|
||||
'box-shadow': `0 4px 6px rgba(0,0,0,0.1)`,
|
||||
'overflow': `hidden`,
|
||||
},
|
||||
|
@ -1,6 +1,5 @@
|
||||
import type { App } from 'vue'
|
||||
import * as ElementPlusIconsVue from '@element-plus/icons-vue'
|
||||
import { ElLoading, ElMessage } from 'element-plus'
|
||||
import 'element-plus/dist/index.css'
|
||||
import 'element-plus/theme-chalk/dark/css-vars.css'
|
||||
|
||||
@ -8,9 +7,6 @@ export default {
|
||||
install(app: App<Element>) {
|
||||
// app.use(ElementPlus, { size: `default` })
|
||||
|
||||
app.config.globalProperties.$loading = ElLoading.service
|
||||
app.config.globalProperties.$message = ElMessage
|
||||
|
||||
for (const [key, component] of Object.entries(ElementPlusIconsVue)) {
|
||||
app.component(`ElIcon${key}`, component)
|
||||
}
|
||||
|
@ -6,10 +6,10 @@ import { initRenderer } from '@/utils/renderer'
|
||||
import { useDark, useStorage, useToggle } from '@vueuse/core'
|
||||
|
||||
import CodeMirror from 'codemirror'
|
||||
import { ElMessage, ElMessageBox } from 'element-plus'
|
||||
import { marked } from 'marked'
|
||||
import { defineStore } from 'pinia'
|
||||
import { computed, markRaw, onMounted, ref, toRaw, watch } from 'vue'
|
||||
import { toast } from 'vue-sonner'
|
||||
|
||||
export const useStore = defineStore(`store`, () => {
|
||||
// 是否开启深色模式
|
||||
@ -265,6 +265,8 @@ export const useStore = defineStore(`store`, () => {
|
||||
|
||||
updateCss()
|
||||
editorRefresh()
|
||||
|
||||
toast.success(`样式重置成功~`)
|
||||
}
|
||||
|
||||
// 为函数添加刷新编辑器的功能
|
||||
@ -361,7 +363,7 @@ export const useStore = defineStore(`store`, () => {
|
||||
reader.readAsText(file)
|
||||
reader.onload = (event) => {
|
||||
(editor.value!).setValue((event.target!).result as string)
|
||||
ElMessage.success(`文档导入成功`)
|
||||
toast.success(`文档导入成功`)
|
||||
}
|
||||
}
|
||||
|
||||
@ -370,28 +372,11 @@ export const useStore = defineStore(`store`, () => {
|
||||
body.removeChild(input)
|
||||
}
|
||||
|
||||
const isOpenConfirmDialog = ref(false)
|
||||
|
||||
// 重置样式
|
||||
const resetStyleConfirm = () => {
|
||||
ElMessageBox.confirm(
|
||||
`此操作将丢失本地自定义样式,是否继续?`,
|
||||
`提示`,
|
||||
{
|
||||
confirmButtonText: `确定`,
|
||||
cancelButtonText: `取消`,
|
||||
type: `warning`,
|
||||
center: true,
|
||||
},
|
||||
)
|
||||
.then(() => {
|
||||
resetStyle()
|
||||
ElMessage({
|
||||
type: `success`,
|
||||
message: `样式重置成功~`,
|
||||
})
|
||||
})
|
||||
.catch(() => {
|
||||
(editor.value!).focus()
|
||||
})
|
||||
isOpenConfirmDialog.value = true
|
||||
}
|
||||
|
||||
return {
|
||||
@ -433,7 +418,9 @@ export const useStore = defineStore(`store`, () => {
|
||||
|
||||
importMarkdownContent,
|
||||
|
||||
isOpenConfirmDialog,
|
||||
resetStyleConfirm,
|
||||
resetStyle,
|
||||
editorContent,
|
||||
|
||||
cssContentConfig,
|
||||
|
@ -4,8 +4,17 @@ import CssEditor from '@/components/CodemirrorEditor/CssEditor.vue'
|
||||
import EditorHeader from '@/components/CodemirrorEditor/EditorHeader/index.vue'
|
||||
import InsertFormDialog from '@/components/CodemirrorEditor/InsertFormDialog.vue'
|
||||
import UploadImgDialog from '@/components/CodemirrorEditor/UploadImgDialog.vue'
|
||||
|
||||
import RunLoading from '@/components/RunLoading.vue'
|
||||
import {
|
||||
AlertDialog,
|
||||
AlertDialogAction,
|
||||
AlertDialogCancel,
|
||||
AlertDialogContent,
|
||||
AlertDialogDescription,
|
||||
AlertDialogFooter,
|
||||
AlertDialogHeader,
|
||||
AlertDialogTitle,
|
||||
} from '@/components/ui/alert-dialog'
|
||||
import {
|
||||
ContextMenu,
|
||||
ContextMenuContent,
|
||||
@ -14,7 +23,6 @@ import {
|
||||
ContextMenuShortcut,
|
||||
ContextMenuTrigger,
|
||||
} from '@/components/ui/context-menu'
|
||||
|
||||
import { altKey, altSign, ctrlKey, shiftKey, shiftSign } from '@/config'
|
||||
import { useDisplayStore, useStore } from '@/stores'
|
||||
import {
|
||||
@ -24,12 +32,9 @@ import {
|
||||
} from '@/utils'
|
||||
import fileApi from '@/utils/file'
|
||||
import CodeMirror from 'codemirror'
|
||||
|
||||
import { ElCol, ElMessage } from 'element-plus'
|
||||
|
||||
import { storeToRefs } from 'pinia'
|
||||
|
||||
import { onMounted, ref, toRaw, watch } from 'vue'
|
||||
import { toast } from 'vue-sonner'
|
||||
|
||||
const store = useStore()
|
||||
const displayStore = useDisplayStore()
|
||||
@ -53,7 +58,7 @@ const {
|
||||
const isImgLoading = ref(false)
|
||||
const timeout = ref<NodeJS.Timeout>()
|
||||
|
||||
const preview = ref<typeof ElCol | null>(null)
|
||||
const preview = ref<HTMLDivElement | null>(null)
|
||||
|
||||
// 使浏览区与编辑区滚动条建立同步联系
|
||||
function leftAndRightScroll() {
|
||||
@ -63,7 +68,7 @@ function leftAndRightScroll() {
|
||||
|
||||
clearTimeout(timeout.value)
|
||||
if (text === `preview`) {
|
||||
source = preview.value!.$el
|
||||
source = preview.value!
|
||||
target = document.querySelector<HTMLElement>(`.CodeMirror-scroll`)!
|
||||
|
||||
editor.value!.off(`scroll`, editorScrollCB)
|
||||
@ -73,7 +78,7 @@ function leftAndRightScroll() {
|
||||
}
|
||||
else {
|
||||
source = document.querySelector<HTMLElement>(`.CodeMirror-scroll`)!
|
||||
target = preview.value!.$el
|
||||
target = preview.value!
|
||||
|
||||
target.removeEventListener(`scroll`, previewScrollCB, false)
|
||||
timeout.value = setTimeout(() => {
|
||||
@ -96,7 +101,7 @@ function leftAndRightScroll() {
|
||||
scrollCB(`preview`)
|
||||
}
|
||||
|
||||
(preview.value!.$el).addEventListener(`scroll`, previewScrollCB, false)
|
||||
(preview.value!).addEventListener(`scroll`, previewScrollCB, false)
|
||||
editor.value!.on(`scroll`, editorScrollCB)
|
||||
}
|
||||
|
||||
@ -131,7 +136,7 @@ function beforeUpload(file: File) {
|
||||
// validate image
|
||||
const checkResult = checkImage(file)
|
||||
if (!checkResult.ok) {
|
||||
ElMessage.error(checkResult.msg)
|
||||
toast.error(checkResult.msg!)
|
||||
return false
|
||||
}
|
||||
|
||||
@ -142,7 +147,7 @@ function beforeUpload(file: File) {
|
||||
const config = localStorage.getItem(`${imgHost}Config`)
|
||||
const isValidHost = imgHost === `default` || config
|
||||
if (!isValidHost) {
|
||||
ElMessage.error(`请先配置 ${imgHost} 图床参数`)
|
||||
toast.error(`请先配置 ${imgHost} 图床参数`)
|
||||
return false
|
||||
}
|
||||
return true
|
||||
@ -151,7 +156,7 @@ function beforeUpload(file: File) {
|
||||
// 图片上传结束
|
||||
function uploaded(imageUrl: string) {
|
||||
if (!imageUrl) {
|
||||
ElMessage.error(`上传图片未知异常`)
|
||||
toast.error(`上传图片未知异常`)
|
||||
return
|
||||
}
|
||||
toggleShowUploadImgDialog(false)
|
||||
@ -160,7 +165,7 @@ function uploaded(imageUrl: string) {
|
||||
const markdownImage = `![](${imageUrl})`
|
||||
// 将 Markdown 形式的 URL 插入编辑框光标所在位置
|
||||
toRaw(store.editor!).replaceSelection(`\n${markdownImage}\n`, cursor as any)
|
||||
ElMessage.success(`图片上传成功`)
|
||||
toast.success(`图片上传成功`)
|
||||
}
|
||||
function uploadImage(file: File, cb?: { (url: any): void, (arg0: unknown): void } | undefined) {
|
||||
isImgLoading.value = true
|
||||
@ -176,7 +181,7 @@ function uploadImage(file: File, cb?: { (url: any): void, (arg0: unknown): void
|
||||
}
|
||||
})
|
||||
.catch((err) => {
|
||||
ElMessage.error(err.message)
|
||||
toast.error(err.message)
|
||||
})
|
||||
.finally(() => {
|
||||
isImgLoading.value = false
|
||||
@ -274,12 +279,12 @@ function addFormat(cmd: string | number) {
|
||||
(editor.value as any).options.extraKeys[cmd](editor.value)
|
||||
}
|
||||
|
||||
const codeMirrorWrapper = ref<ComponentPublicInstance<typeof ElCol> | null>(null)
|
||||
const codeMirrorWrapper = ref<ComponentPublicInstance<HTMLDivElement> | null>(null)
|
||||
|
||||
// 转换 markdown 中的本地图片为线上图片
|
||||
// todo 处理事件覆盖
|
||||
function mdLocalToRemote() {
|
||||
const dom = codeMirrorWrapper.value!.$el as HTMLElement
|
||||
const dom = codeMirrorWrapper.value!
|
||||
|
||||
// 上传 md 中的图片
|
||||
const uploadMdImg = async ({ md, list }: { md: { str: string, path: string, file: File }, list: { path: string, file: File }[] }) => {
|
||||
@ -391,10 +396,9 @@ onMounted(() => {
|
||||
@end-copy="endCopy"
|
||||
/>
|
||||
<main class="container-main flex-1">
|
||||
<el-row class="container-main-section h-full border-1">
|
||||
<ElCol
|
||||
<div class="container-main-section grid h-full border-1" :class="isShowCssEditor ? 'grid-cols-3' : 'grid-cols-2'">
|
||||
<div
|
||||
ref="codeMirrorWrapper"
|
||||
:span="isShowCssEditor ? 8 : 12"
|
||||
class="codeMirror-wrapper border-r-1"
|
||||
:class="{
|
||||
'order-1': !store.isEditOnLeft,
|
||||
@ -434,8 +438,8 @@ onMounted(() => {
|
||||
</ContextMenuItem>
|
||||
</ContextMenuContent>
|
||||
</ContextMenu>
|
||||
</ElCol>
|
||||
<ElCol
|
||||
</div>
|
||||
<div
|
||||
id="preview"
|
||||
ref="preview"
|
||||
:span="isShowCssEditor ? 8 : 12"
|
||||
@ -452,9 +456,9 @@ onMounted(() => {
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</ElCol>
|
||||
</div>
|
||||
<CssEditor />
|
||||
</el-row>
|
||||
</div>
|
||||
</main>
|
||||
|
||||
<UploadImgDialog
|
||||
@ -464,6 +468,23 @@ onMounted(() => {
|
||||
<InsertFormDialog />
|
||||
|
||||
<RunLoading />
|
||||
|
||||
<AlertDialog v-model:open="store.isOpenConfirmDialog">
|
||||
<AlertDialogContent>
|
||||
<AlertDialogHeader>
|
||||
<AlertDialogTitle>提示</AlertDialogTitle>
|
||||
<AlertDialogDescription>
|
||||
此操作将丢失本地自定义样式,是否继续?
|
||||
</AlertDialogDescription>
|
||||
</AlertDialogHeader>
|
||||
<AlertDialogFooter>
|
||||
<AlertDialogCancel>取消</AlertDialogCancel>
|
||||
<AlertDialogAction @click="store.resetStyle()">
|
||||
确认
|
||||
</AlertDialogAction>
|
||||
</AlertDialogFooter>
|
||||
</AlertDialogContent>
|
||||
</AlertDialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@ -497,8 +518,8 @@ onMounted(() => {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
text-align: center;
|
||||
color: var(--el-text-color-regular);
|
||||
background-color: var(--el-bg-color);
|
||||
color: hsl(var(--foreground));
|
||||
background-color: hsl(var(--background));
|
||||
|
||||
.loading-mask-box {
|
||||
position: sticky;
|
||||
|
Loading…
x
Reference in New Issue
Block a user