Skip to content

Commit

Permalink
feat: 调用github接口完成上传博客
Browse files Browse the repository at this point in the history
  • Loading branch information
xiaohaodu committed Nov 13, 2023
1 parent 686ce68 commit 780ff03
Show file tree
Hide file tree
Showing 20 changed files with 534 additions and 136 deletions.
8 changes: 8 additions & 0 deletions app/router.options.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,5 +8,13 @@ export default <RouterConfig>{
path: '/blogs/:blogPath+',
component: () => import('~/pages/blogs/[blogPath].vue'),
},
{
path: '/blogs/edit/:blogPath+',
component: () => import('~/pages/blogs/edit/[blogPath].vue'),
},
{
path: '/blogs/add/:blogPath+',
component: () => import('~/pages/blogs/add/[blogPath].vue'),
},
],
};
12 changes: 12 additions & 0 deletions components/BlogAffix.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<template>
<div class="fixed bottom-10 right-5">
<el-button @click="createBlog">

Check failure on line 3 in components/BlogAffix.vue

View workflow job for this annotation

GitHub Actions / build

Property 'createBlog' does not exist on type '{ $: ComponentInternalInstance; $data: {}; $props: Partial<{}> & Omit<{} & VNodeProps & AllowedComponentProps & ComponentCustomProps & Readonly<...>, never>; ... 10 more ...; $watch<T extends string | ((...args: any) => any)>(source: T, cb: T extends (...args: any) => infer R ? (args_0: R, args_1: R) => any : (...ar...'.
<span>新建 </span>
<el-icon><Plus /></el-icon>
</el-button>
</div>
</template>

<script lang="ts" setup></script>

<style lang="" scoped></style>
198 changes: 167 additions & 31 deletions components/BlogShow.vue
Original file line number Diff line number Diff line change
@@ -1,41 +1,177 @@
<template>
<div id="blogContent" v-html="blogContent"></div>
<div v-if="type === 'show'" class="p-4" v-html="blogContent"></div>
<div v-else-if="type === 'edit' || type === 'add'" class="flex flex-nowrap p-4">
<div class="fixed bottom-4 right-6 flex h-10 w-fit items-center justify-center">
<div
v-show="editOperation"
class="flex flex-nowrap items-center justify-center rounded bg-slate-200 bg-opacity-50"
>
<button class="flex flex-nowrap items-center justify-center px-1">
<span class="w-fit whitespace-nowrap font-mono">提交信息:</span>
<el-input
v-show="messageTag"
v-model="commitConfig.message"
type="text"
placeholder="请输入提交信息"
></el-input>
<el-switch
class="mx-1"
v-model="messageTag"
inline-prompt
active-text="自定义"
inactive-text="默认"
></el-switch>
</button>
<button class="flex flex-nowrap items-center justify-center px-1">
<el-input v-model="blogName" placeholder="请输入博客标题"></el-input>
</button>
<button class="flex flex-nowrap items-center justify-center px-1">
<span class="w-fit whitespace-nowrap font-mono">博客分类:</span>
<el-input
v-show="blogDirPathExtendTag"
v-model="blogDirPathExtend"
type="text"
placeholder="请输入博客分类,'/'作为子分类符"
></el-input>
<el-switch
class="mx-1"
v-model="blogDirPathExtendTag"
inline-prompt
active-text="自定义"
inactive-text="默认"
></el-switch>
</button>
<button
class="flex flex-nowrap items-center justify-center px-2 py-1 font-serif"
@click="putBlog"
>
推送<el-icon><Promotion /></el-icon>
</button>
</div>
<button
class="ml-2 flex items-center justify-center rounded-md bg-blue-200 px-2 py-1.5"
:class="{ 'opacity-500': editOperation, 'opacity-50': !editOperation }"
@click="switchEditOperation"
>
<el-icon><Operation /></el-icon>
</button>
</div>
<div v-html="blogContent" class="w-1/2 p-4"></div>
<textarea
ref="textarea"
placeholder="请输入内容"
:style="textareaHeight ? { height: textareaHeight } : ''"
@input="textareaInput"
class="h-[calc(100vh-100px)] w-1/2 resize-none whitespace-pre-wrap break-words border-none p-4 outline-none"
v-model="blogContentText"
></textarea>
</div>
</template>

<script setup>
<script lang="ts" setup>
import { marked } from 'marked';
import hljs from 'highlight.js';
import DOMPurify from 'isomorphic-dompurify';
const props = defineProps(['blogContent']);
// $ npm i isomorphic-dompurify
//清除危险字符串,防范xss攻击
const props = defineProps({
blogContent: {
type: String,
required: true,
},
type: {
type: String as PropType<ContentShowType>,
required: true,
},
blogPath: {
type: String,
},
});
const router = useRouter();
const blogContentText =
props.type === 'show' ? computed(() => props.blogContent) : ref(props.blogContent);
const blogContent = computed(() => {
return DOMPurify.sanitize(marked.parse(props.blogContent.replace(/^[\u200B\u200C\u200D\u200E\u200F\uFEFF]/, "")));
return marked.parse(blogContentText.value.replace(/^[\u200B\u200C\u200D\u200E\u200F\uFEFF]/, ''));
});
const textarea = ref();
const textareaHeight = ref('');
const textareaHeightInit = () => {
if ((textarea.value as HTMLElement).scrollHeight !== (textarea.value as HTMLElement).clientHeight)
textareaHeight.value = (textarea.value as HTMLElement).scrollHeight + 80 + 'px';
};
let githubAuth = ref<GithubAuth>();
onMounted(() => {
githubAuth = useGithubAuth();
if (props.type === 'edit' || props.type === 'add') textareaHeightInit();
});
const textareaInput = (e: Event) => {
if ((e.target as HTMLElement).scrollHeight !== (e.target as HTMLElement).clientHeight)
textareaHeight.value = (e.target as HTMLElement).scrollHeight + 'px';
};
const editOperation = ref(true);
const switchEditOperation = () => {
editOperation.value = !editOperation.value;
};
const messageTag = ref(false);
const blogDirPathExtendTag = ref(false);
const blogDirPathExtend = ref(props.blogPath);
const blogName = ref('');
const commitConfig = ref<CommitConfig>({
message: '',
mode: '100644',
content: '',
type: 'blob',
path: '',
});
const { $loading } = useNuxtApp();
const putBlog = async () => {
if (props.type === 'add' && !blogName.value) {
return ElMessage({
type: 'error',
message: '博客标题是必填项',
center: true,
});
}
$loading();
commitConfig.value.content = blogContentText.value;
if (!commitConfig.value.message) {
if (props.type === 'edit') {
commitConfig.value.message = 'edit blog: ' + props.blogPath;
commitConfig.value.path = 'public/_blogs/' + props.blogPath;
} else if (props.type === 'add') {
commitConfig.value.message = 'add blog:' + blogDirPathExtend.value + blogName.value;
commitConfig.value.path = 'public/_blogs/' + blogDirPathExtend.value + blogName.value + '.md';
}
}
try {
const res = await $fetch('/api/github/commit', {
method: 'post',
body: {
accessToken: githubAuth.value?.access_token,
commitConfig: commitConfig.value,
},
});
router.push('/');
} catch (error) {
console.warn(error);
} finally {
$loading().close();
}
};
marked.setOptions({
renderer: new marked.Renderer(),
highlight: function (code, lang) {
const language = hljs.getLanguage(lang) ? lang : 'plaintext';
return hljs.highlight(code, { language }).value;
},
langPrefix: 'hljs language-', // highlight.js css expects a top-level 'hljs' class.
async: false,
breaks: false,
gfm: true,
headerIds: true,
headerPrefix: "",
mangle: true,
pedantic: false,
sanitize: false,
silent: false,
smartypants: false,
xhtml: false
renderer: new marked.Renderer(),
highlight: function (code, lang) {
const language = hljs.getLanguage(lang) ? lang : 'plaintext';
return hljs.highlight(code, { language }).value;
},
langPrefix: 'hljs language-', // highlight.js css expects a top-level 'hljs' class.
async: false,
breaks: false,
gfm: true,
headerIds: true,
headerPrefix: '',
mangle: true,
pedantic: false,
sanitize: false,
silent: false,
smartypants: false,
xhtml: false,
});
</script>

<style lang="scss" scoped>
#blogContent {
padding: 2vh;
// user-select: none;
}
</style>
134 changes: 109 additions & 25 deletions components/BlogsTree.vue
Original file line number Diff line number Diff line change
@@ -1,34 +1,118 @@
<template>
<li v-for="(item, index) in blogsTree" :key="index">
<nuxt-link
v-if="item.type == 'file'"
:to="`/blogs/${fileBlogPathHandler(item)}`"
@click="setBlogPath(item.path)"
:class="{ active: fileBlogPathHandler(item) == props.active }"
class="file"
>{{ fileNameHandler(item) }}</nuxt-link
>
<span v-if="item.type == 'dir'">{{ item.name }}</span>
<ul v-if="item.type == 'dir' && item.children.length">
<BlogsTree
:blogsTree="item.children"
v-if="item.type == 'dir' && item.children.length"
:active="props.active"
<client-only>
<div class="mb-2 flex items-baseline justify-end text-lg" v-if="tag && githubAuth">
<!-- <button @click="addFolder()" class="cursor-pointer hover:text-[#42B883]">
<span class="font-serif text-xs">FOLDER</span> <el-icon><FolderAdd /></el-icon>
</button> -->
<button @click="addFile()" class="cursor-pointer hover:text-[#42B883]">
<el-icon><DocumentAdd /></el-icon><span class="font-serif text-xs">FILE</span>
</button>
</div>
</client-only>
<li v-for="(blogTreeBranch, index) in blogsTree" :key="index">
<div v-if="blogTreeBranch.type == 'file'" class="group flex items-center justify-between">
<nuxt-link
:to="`/blogs/${fileBlogPathHandler(blogTreeBranch)}`"
@click="setBlogPath(blogTreeBranch.path as string)"
:class="{ active: fileBlogPathHandler(blogTreeBranch) == active }"
class="file"
>{{ fileNameHandler(blogTreeBranch) }}</nuxt-link
>
</BlogsTree>
</ul>
<button
@click="editBlog(blogTreeBranch)"
class="hidden hover:text-[#42B883] group-hover:block"
>
<el-icon><Edit /></el-icon>
</button>
</div>
<div v-if="blogTreeBranch.type == 'dir'">
<div class="group flex items-center justify-between">
<span>{{ blogTreeBranch.name }}</span>
<div class="flex flex-nowrap items-baseline">
<button
@click="addFile(blogTreeBranch)"
class="hidden hover:text-[#42B883] group-hover:block"
>
<el-icon><DocumentAdd /></el-icon>
</button>
<!-- <button
@click="addFolder(blogTreeBranch)"
class="hidden hover:text-[#42B883] group-hover:block"
>
<el-icon><FolderAdd /></el-icon>
</button> -->
<button
@click="switchFolderState(blogTreeBranch)"
class="hidden hover:text-[#42B883] group-hover:block"
>
<el-icon v-show="!blogTreeBranch?.expand"><FolderOpened /></el-icon>
<el-icon v-show="blogTreeBranch?.expand"><Folder /></el-icon>
</button>
</div>
</div>
<ul
v-show="blogTreeBranch.expand && blogTreeBranch.children && blogTreeBranch.children.length"
>
<BlogsTree
:showType="showType"
:tag="false"
:blogsTree="blogTreeBranch.children"

Check failure on line 59 in components/BlogsTree.vue

View workflow job for this annotation

GitHub Actions / build

Type 'BlogsTreeBranch[] | undefined' is not assignable to type 'BlogsTree'.
:setBlogPath="setBlogPath"
:active="active"
>
</BlogsTree>
</ul>
</div>
</li>
</template>

<script setup>
const props = defineProps(['blogsTree', 'active']);
const blogsTree = props.blogsTree;
const setBlogPath = inject('setBlogPath');
const fileNameHandler = (item) => {
return item.name.replace(/(.md|.js)$/, '');
<script lang="ts" setup>
const props = defineProps({
blogsTree: {
type: Object as PropType<BlogsTree>,
required: true,
},
active: {
type: String,
required: true,
},
setBlogPath: {
type: Function as PropType<(content: string) => void>,
required: true,
},
tag: {
type: Boolean,
required: true,
},
showType: {
type: String as PropType<ContentShowType>,
required: true,
},
});
let githubAuth = ref<GithubAuth>();
if (props.tag) {
onMounted(() => {
githubAuth = useGithubAuth();
});
}
const router = useRouter();
const fileNameHandler = (blogTreeBranch: BlogsTreeBranch) => {
return blogTreeBranch.name.replace(/(.md|.js)$/, '');
};
const fileBlogPathHandler = (blogTreeBranch: BlogsTreeBranch) => {
return blogTreeBranch.path!.slice(14, -3);
};
const editBlog = (blogTreeBranch?: BlogsTreeBranch) => {
router.push(`/blogs/edit/${blogTreeBranch?.path!.slice(14, -3)}`);
};
const addFile = (blogTreeBranch?: BlogsTreeBranch) => {
router.push(`/blogs/add/${blogTreeBranch?.dirPath!.slice(14)}`);
};
const fileBlogPathHandler = (item) => {
return item.path.slice(14, -3);
// const addFolder = (blogTreeBranch?: BlogsTreeBranch) => {
// console.log(blogTreeBranch);
// };
const switchFolderState = (blogsTreeBranch?: BlogsTreeBranch) => {
if (blogsTreeBranch) blogsTreeBranch.expand = !blogsTreeBranch.expand;
};
</script>

Expand Down
Loading

0 comments on commit 780ff03

Please sign in to comment.