1
0

3 Коммиты 0dfefbfb37 ... 1afbc2997e

Автор SHA1 Сообщение Дата
  Sakulin 1afbc2997e feat(PostEditView): 添加草稿保存功能和发布成功反馈 2 месяцев назад
  Sakulin 5b65768c78 feat(store): 添加草稿存储功能及UUID生成工具 2 месяцев назад
  Sakulin 7ae9e192f7 fix(models): 将TagList和PushSuccess接口的code字段类型从number改为200 2 месяцев назад
4 измененных файлов с 87 добавлено и 20 удалено
  1. 2 2
      src/models/index.ts
  2. 24 0
      src/stores/draft.ts
  3. 11 2
      src/utils/index.ts
  4. 50 16
      src/views/PostEditView.vue

+ 2 - 2
src/models/index.ts

@@ -43,7 +43,7 @@ export interface NewTag {
 }
 
 export interface TagList {
-  code: number
+  code: 200
   data: Tag[]
 }
 
@@ -82,7 +82,7 @@ export interface PostBody {
 }
 
 export interface PushSuccess {
-  code: number
+  code: 200
   msg: string
   data: {
     id: number

+ 24 - 0
src/stores/draft.ts

@@ -0,0 +1,24 @@
+import { defineStore } from 'pinia'
+import { generateUUID } from '@/utils'
+
+export interface DraftBody {
+  content: string
+  tags: string[]
+  title: string
+  cate: string
+  timemill: number
+}
+
+export const useDraftStore = defineStore('draft', {
+  state: () => ({
+    drafts: {} as { [key: string]: DraftBody },
+  }),
+  actions: {
+    push(draft: DraftBody, id: string | null) {
+      const s = (typeof id == 'string') ? id : generateUUID()
+      this.drafts[s] = draft
+      return s
+    },
+  },
+  persist: true,
+})

+ 11 - 2
src/utils/index.ts

@@ -32,9 +32,9 @@ export function isDark() {
 
 export function colorMatchTheme(hexColor: string) {
   if (isDark()) {
-    return lightenHexColor(hexColor);
+    return lightenHexColor(hexColor)
   }
-  return hexColor;
+  return hexColor
 }
 
 export function formateDateAccurateToDay(date: Date | string | number, nick = true): string {
@@ -68,3 +68,12 @@ export async function delay(ms: number) {
     setTimeout(resolve, ms)
   })
 }
+
+export function generateUUID() {
+  let d = new Date().getTime()
+  return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) {
+    const r = (d + Math.random() * 16) % 16 | 0
+    d = Math.floor(d / 16)
+    return (c == 'x' ? r : (r & 0x3) | 0x8).toString(16)
+  })
+}

+ 50 - 16
src/views/PostEditView.vue

@@ -4,8 +4,10 @@ import CateSelect from '@/components/CateSelect.vue'
 import TagCloud from '@/components/TagCloud.vue'
 import type { PostBody, Tag } from '@/models'
 import { api } from '@/utils/axios.ts'
-import { getQuery } from '@/router'
+import { getQuery, router } from '@/router'
 import { optionOfCate } from '@/utils/cates.ts'
+import { useMessagePipeStore } from '@/stores/msg.ts'
+import { type DraftBody, useDraftStore } from '@/stores/draft.ts'
 
 const editorConfig = {
   leftToolbar: 'undo redo | image',
@@ -63,7 +65,7 @@ onMounted(() => {
 
 const title = ref('')
 
-const text = ref(`
+const content = ref(`
 # 这是一个标题
 
 这是一个段落
@@ -86,46 +88,78 @@ const cateOptions = [
 const newTags = ref<string[]>([])
 
 const validContent = computed(() => {
-  return text.value.length > 0 && title.value.length > 0 && selectedCate.value.length > 0
+  return content.value.length > 0 && title.value.length > 0 && selectedCate.value.length > 0
 })
 
+function createDraft(): DraftBody {
+  return {
+    content: content.value,
+    tags: [...activeTags.value, ...newTags.value],
+    title: title.value,
+    cate: selectedCate.value,
+    timemill: Date.now(),
+  }
+}
+
 function buildPost(): PostBody | undefined {
   if (!validContent.value) {
     return undefined
   }
   return {
-    content: text.value,
+    content: content.value,
     tags: [...activeTags.value, ...newTags.value],
     title: title.value,
     cate: selectedCate.value,
   }
 }
 
+const msg = useMessagePipeStore()
+
 function handleSubmit() {
   const postBody = buildPost()
   if (postBody) {
-    api.postPush(postBody)
+    api.postPush(postBody).then((res) => {
+      if (res.code == 200) {
+        msg.success('发布成功~')
+        router.push(`/detail?id=${res.data.id}`)
+      } else if (res.code == 401) {
+        msg.danger('认证失败,请重新登录')
+      }
+    })
   }
 }
+
+const targetDraft = ref<string | null>(null)
+
+const draftStore = useDraftStore()
+
+function handleSaveDraft() {
+  const draft = createDraft()
+  targetDraft.value = draftStore.push(draft, targetDraft.value)
+  msg.info('草稿已保存')
+}
 </script>
 
 <template>
   <div>
     <div class="post-item" style="padding: 12px 30px; overflow: hidden">
-      <div style="display: flex; align-items: end; padding-bottom: 12px">
-        <cate-select :options="cateOptions" v-model="selectedCate" placeholder="类型" />
-        <input
-          style="margin-bottom: 0"
-          class="p-input"
-          type="text"
-          placeholder="博文标题"
-          v-model="title"
-        />
+      <div style="display: flex; align-items: center; justify-content: space-between">
+        <div style="display: flex; align-items: end; padding-bottom: 12px">
+          <CateSelect :options="cateOptions" v-model="selectedCate" placeholder="类型" />
+          <input
+            style="margin-bottom: 0"
+            class="p-input"
+            type="text"
+            placeholder="博文标题"
+            v-model="title"
+          />
+        </div>
+        <div>载入草稿</div>
       </div>
     </div>
     <div class="post-item" style="padding: 0; overflow: hidden">
       <v-md-editor
-        v-model="text"
+        v-model="content"
         height="400px"
         :left-toolbar="editorConfig.leftToolbar"
         :right-toolbar="editorConfig.rightToolbar"
@@ -144,7 +178,7 @@ function handleSubmit() {
     </div>
 
     <div class="post-item footer" style="padding: 0 30px">
-      <div class="p-button">保存草稿</div>
+      <div class="p-button" @click="handleSaveDraft">保存草稿</div>
       <div
         :class="validContent ? ['p-button-success'] : ['p-button-disabled']"
         @click="handleSubmit"