Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: init post list #475

Merged
merged 2 commits into from
Dec 18, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 7 additions & 1 deletion src/components/CodemirrorEditor/EditorHeader/index.vue
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ import {
} from '@/config'
import { useDisplayStore, useStore } from '@/stores'
import { mergeCss, solveWeChatImage } from '@/utils'
import { Moon, Paintbrush, Sun } from 'lucide-vue-next'
import { Moon, Paintbrush, PanelLeftClose, PanelLeftOpen, Sun } from 'lucide-vue-next'
import { storeToRefs } from 'pinia'
import { nextTick, ref, useTemplateRef } from 'vue'
import PickColors from 'vue-pick-colors'
Expand Down Expand Up @@ -219,6 +219,12 @@ const formatOptions = ref<Format[]>([`rgb`, `hex`, `hsl`, `hsv`])
<HelpDropdown />
</Menubar>

<Button v-if="!store.isOpenPostSlider" variant="outline" @click="store.isOpenPostSlider = true" class="mr-2">
<PanelLeftOpen class="size-4" />
</Button>
<Button v-else variant="outline" @click="store.isOpenPostSlider = false" class="mr-2">
<PanelLeftClose class="size-4" />
</Button>
<Popover>
<PopoverTrigger>
<Button variant="outline">
Expand Down
189 changes: 189 additions & 0 deletions src/components/CodemirrorEditor/PostSlider.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,189 @@
<script setup lang="ts">
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,
DialogTrigger,
} from '@/components/ui/dialog'
import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuTrigger,
} from '@/components/ui/dropdown-menu'
import { Input } from '@/components/ui/input'
import { useStore } from '@/stores'
import { Edit3, Ellipsis, Plus, Trash } from 'lucide-vue-next'
import { ref, watch } from 'vue'
import { toast } from 'vue-sonner'

const store = useStore()

const isOpen = ref(false)

const addPostInputVal = ref(``)

watch(isOpen, () => {
if (isOpen.value) {
addPostInputVal.value = ``
}
})

function addPost() {
if (addPostInputVal.value === ``) {
toast.error(`文章标题不可为空`)
return
}
store.addPost(addPostInputVal.value)
isOpen.value = false
toast.success(`文章新增成功`)
}

const editTarget = ref(-1)
const isOpenEditDialog = ref(false)
const renamePostInputVal = ref(``)
function startRenamePost(index: number) {
editTarget.value = index
renamePostInputVal.value = store.posts[index].title
isOpenEditDialog.value = true
}

function renamePost() {
if (renamePostInputVal.value === ``) {
toast.error(`文章标题不可为空`)
return
}
store.renamePost(editTarget.value, renamePostInputVal.value)
isOpenEditDialog.value = false
toast.success(`文章更名成功`)
}

const isOpenDelPostConfirmDialog = ref(false)
function startDelPost(index: number) {
editTarget.value = index
isOpenDelPostConfirmDialog.value = true
}
function delPost() {
store.delPost(editTarget.value)
isOpenDelPostConfirmDialog.value = false
toast.success(`文章删除成功`)
}
</script>

<template>
<div
class="overflow-hidden border-r bg-gray/20 transition-width dark:bg-gray/40"
:class="{
'w-0': !store.isOpenPostSlider,
'w-50': store.isOpenPostSlider,
}"
>
<nav class="space-y-1 h-full overflow-auto p-2">
<Dialog v-model:open="isOpen">
<DialogTrigger as-child>
<Button variant="outline" class="w-full" size="xs">
<Plus /> 新增文章
</Button>
</DialogTrigger>
<DialogContent>
<DialogHeader>
<DialogTitle>新增文章</DialogTitle>
<DialogDescription>
请输入文章名称
</DialogDescription>
</DialogHeader>
<Input v-model="addPostInputVal" />
<DialogFooter>
<Button @click="addPost()">
确 定
</Button>
</DialogFooter>
</DialogContent>
</Dialog>
<a
v-for="(post, index) in store.posts"
:key="post.title"
href="#"
:class="{
'bg-primary text-primary-foreground': store.currentPostIndex === index,
}"
class="hover:text-primary-foreground hover:bg-primary/90 dark:bg-muted dark:hover:bg-muted h-8 w-full inline-flex items-center justify-start gap-2 whitespace-nowrap rounded px-2 text-sm transition-colors dark:text-white dark:hover:text-white"
@click="store.currentPostIndex = index"
>
<span class="line-clamp-1">{{ post.title }}</span>
<DropdownMenu>
<DropdownMenuTrigger as-child>
<Button size="xs" variant="ghost" class="ml-auto px-1.5">
<Ellipsis class="size-4" />
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent>
<DropdownMenuItem @click.stop="startRenamePost(index)">
<Edit3 class="mr-2 size-4" />
更名
</DropdownMenuItem>
<DropdownMenuItem @click.stop="startDelPost(index)">
<Trash class="mr-2 size-4" />
删除
</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
</a>
<!-- 重命名弹窗 -->
<Dialog v-model:open="isOpenEditDialog">
<DialogContent class="sm:max-w-[425px]">
<DialogHeader>
<DialogTitle>编辑文章名称</DialogTitle>
<DialogDescription>
请输入新的文章名称
</DialogDescription>
</DialogHeader>
<Input v-model="renamePostInputVal" />
<DialogFooter>
<Button variant="outline" @click="isOpenEditDialog = false">
取消
</Button>
<Button @click="renamePost()">
保存
</Button>
</DialogFooter>
</DialogContent>
</Dialog>

<AlertDialog v-model:open="isOpenDelPostConfirmDialog">
<AlertDialogContent>
<AlertDialogHeader>
<AlertDialogTitle>提示</AlertDialogTitle>
<AlertDialogDescription>
此操作将删除该文章,是否继续?
</AlertDialogDescription>
</AlertDialogHeader>
<AlertDialogFooter>
<AlertDialogCancel>取消</AlertDialogCancel>
<AlertDialogAction @click="delPost()">
确认
</AlertDialogAction>
</AlertDialogFooter>
</AlertDialogContent>
</AlertDialog>
</nav>
</div>
</template>

<style scoped lang="less">

</style>
47 changes: 45 additions & 2 deletions src/stores/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,12 +52,50 @@ export const useStore = defineStore(`store`, () => {
// 内容编辑器编辑器
const editor = ref<CodeMirror.EditorFromTextArea | null>(null)
// 编辑区域内容
// 预备弃用
const editorContent = useStorage(`__editor_content`, DEFAULT_CONTENT)

const isOpenPostSlider = useStorage(addPrefix(`is_open_post_slider`), false)
// 文章列表
const posts = useStorage(addPrefix(`posts`), [{
title: `文章1`,
content: DEFAULT_CONTENT,
}])
// 当前文章
const currentPostIndex = useStorage(addPrefix(`current_post_index`), 0)

const addPost = (title: string) => {
currentPostIndex.value = posts.value.push({
title,
content: DEFAULT_CONTENT,
}) - 1
}

const renamePost = (index: number, title: string) => {
posts.value[index].title = title
}

const delPost = (index: number) => {
posts.value.splice(index, 1)
currentPostIndex.value = 0
}

watch(currentPostIndex, () => {
toRaw(editor.value!).setValue(posts.value[currentPostIndex.value].content)
})

onMounted(() => {
// 迁移阶段,兼容之前的方案
if (editorContent.value !== DEFAULT_CONTENT) {
posts.value[currentPostIndex.value].content = editorContent.value
editorContent.value = DEFAULT_CONTENT
}
})

// 格式化文档
const formatContent = () => {
formatDoc((editor.value!).getValue()).then((doc) => {
editorContent.value = doc
posts.value[currentPostIndex.value].content = doc
toRaw(editor.value!).setValue(doc)
})
}
Expand Down Expand Up @@ -421,14 +459,19 @@ export const useStore = defineStore(`store`, () => {
isOpenConfirmDialog,
resetStyleConfirm,
resetStyle,
editorContent,

cssContentConfig,
addCssContentTab,
validatorTabName,
setCssEditorValue,
tabChanged,
renameTab,
posts,
currentPostIndex,
addPost,
renamePost,
delPost,
isOpenPostSlider,
}
})

Expand Down
17 changes: 10 additions & 7 deletions src/views/CodemirrorEditor.vue
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import type { ComponentPublicInstance } from 'vue'
import CssEditor from '@/components/CodemirrorEditor/CssEditor.vue'
import EditorHeader from '@/components/CodemirrorEditor/EditorHeader/index.vue'
import InsertFormDialog from '@/components/CodemirrorEditor/InsertFormDialog.vue'
import PostSlider from '@/components/CodemirrorEditor/PostSlider.vue'
import UploadImgDialog from '@/components/CodemirrorEditor/UploadImgDialog.vue'
import RunLoading from '@/components/RunLoading.vue'
import {
Expand All @@ -15,6 +16,7 @@ import {
AlertDialogHeader,
AlertDialogTitle,
} from '@/components/ui/alert-dialog'

import {
ContextMenu,
ContextMenuContent,
Expand All @@ -38,7 +40,7 @@ import { toast } from 'vue-sonner'

const store = useStore()
const displayStore = useDisplayStore()
const { isDark, output, editor, editorContent } = storeToRefs(store)
const { isDark, output, editor } = storeToRefs(store)
const { isShowCssEditor } = storeToRefs(displayStore)

const {
Expand Down Expand Up @@ -201,7 +203,7 @@ function initEditor() {
const editorDom = document.querySelector<HTMLTextAreaElement>(`#editor`)!

if (!editorDom.value) {
editorDom.value = editorContent.value
editorDom.value = store.posts[store.currentPostIndex].content
}
editor.value = CodeMirror.fromTextArea(editorDom, {
mode: `text/x-markdown`,
Expand Down Expand Up @@ -248,7 +250,7 @@ function initEditor() {
clearTimeout(changeTimer.value)
changeTimer.value = setTimeout(() => {
onEditorRefresh()
editorContent.value = e.getValue()
store.posts[store.currentPostIndex].content = e.getValue()
}, 300)
})

Expand Down Expand Up @@ -396,10 +398,11 @@ onMounted(() => {
@end-copy="endCopy"
/>
<main class="container-main flex-1">
<div class="container-main-section grid h-full border-1" :class="isShowCssEditor ? 'grid-cols-3' : 'grid-cols-2'">
<div class="container-main-section h-full flex border-1">
<PostSlider />
<div
ref="codeMirrorWrapper"
class="codeMirror-wrapper border-r-1"
class="codeMirror-wrapper flex-1 border-r-1"
:class="{
'order-1': !store.isEditOnLeft,
}"
Expand Down Expand Up @@ -443,7 +446,7 @@ onMounted(() => {
id="preview"
ref="preview"
:span="isShowCssEditor ? 8 : 12"
class="preview-wrapper p-5"
class="preview-wrapper flex-1 p-5"
>
<div id="output-wrapper" :class="{ output_night: !backLight }">
<div class="preview border shadow-xl">
Expand All @@ -457,7 +460,7 @@ onMounted(() => {
</div>
</div>
</div>
<CssEditor />
<CssEditor class="flex-1" />
</div>
</main>

Expand Down
Loading