♻️ Interactive v2 #1
| @@ -26,7 +26,7 @@ func createMoment(c *fiber.Ctx) error { | |||||||
|  |  | ||||||
| 	var data struct { | 	var data struct { | ||||||
| 		Alias       string              `json:"alias" form:"alias"` | 		Alias       string              `json:"alias" form:"alias"` | ||||||
| 		Content     string              `json:"content" form:"content" validate:"required"` | 		Content     string              `json:"content" form:"content" validate:"required,max=1024"` | ||||||
| 		Hashtags    []models.Tag        `json:"hashtags" form:"hashtags"` | 		Hashtags    []models.Tag        `json:"hashtags" form:"hashtags"` | ||||||
| 		Categories  []models.Category   `json:"categories" form:"categories"` | 		Categories  []models.Category   `json:"categories" form:"categories"` | ||||||
| 		Attachments []models.Attachment `json:"attachments" form:"attachments"` | 		Attachments []models.Attachment `json:"attachments" form:"attachments"` | ||||||
| @@ -89,7 +89,7 @@ func editMoment(c *fiber.Ctx) error { | |||||||
|  |  | ||||||
| 	var data struct { | 	var data struct { | ||||||
| 		Alias       string              `json:"alias" form:"alias" validate:"required"` | 		Alias       string              `json:"alias" form:"alias" validate:"required"` | ||||||
| 		Content     string              `json:"content" form:"content" validate:"required"` | 		Content     string              `json:"content" form:"content" validate:"required,max=1024"` | ||||||
| 		PublishedAt *time.Time          `json:"published_at" form:"published_at"` | 		PublishedAt *time.Time          `json:"published_at" form:"published_at"` | ||||||
| 		Hashtags    []models.Tag        `json:"hashtags" form:"hashtags"` | 		Hashtags    []models.Tag        `json:"hashtags" form:"hashtags"` | ||||||
| 		Categories  []models.Category   `json:"categories" form:"categories"` | 		Categories  []models.Category   `json:"categories" form:"categories"` | ||||||
|   | |||||||
| @@ -83,6 +83,16 @@ func (v *PostTypeContext) GetViaAlias(alias string) (models.Feed, error) { | |||||||
| 		return item, err | 		return item, err | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
|  | 	var attachments []models.Attachment | ||||||
|  | 	if err := database.C. | ||||||
|  | 		Model(&models.Attachment{}). | ||||||
|  | 		Where(v.ColumnName+"_id = ?", item.ID). | ||||||
|  | 		Scan(&attachments).Error; err != nil { | ||||||
|  | 		return item, err | ||||||
|  | 	} else { | ||||||
|  | 		item.Attachments = attachments | ||||||
|  | 	} | ||||||
|  |  | ||||||
| 	return item, nil | 	return item, nil | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -96,6 +106,16 @@ func (v *PostTypeContext) Get(id uint, noComments ...bool) (models.Feed, error) | |||||||
| 		return item, err | 		return item, err | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
|  | 	var attachments []models.Attachment | ||||||
|  | 	if err := database.C. | ||||||
|  | 		Model(&models.Attachment{}). | ||||||
|  | 		Where(v.ColumnName+"_id = ?", id). | ||||||
|  | 		Scan(&attachments).Error; err != nil { | ||||||
|  | 		return item, err | ||||||
|  | 	} else { | ||||||
|  | 		item.Attachments = attachments | ||||||
|  | 	} | ||||||
|  |  | ||||||
| 	return item, nil | 	return item, nil | ||||||
| } | } | ||||||
|  |  | ||||||
| @@ -184,6 +204,41 @@ func (v *PostTypeContext) List(take int, offset int, noReact ...bool) ([]*models | |||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
|  | 	{ | ||||||
|  | 		var attachments []struct { | ||||||
|  | 			models.Attachment | ||||||
|  |  | ||||||
|  | 			PostID uint `json:"post_id"` | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		itemMap := lo.SliceToMap(items, func(item *models.Feed) (uint, *models.Feed) { | ||||||
|  | 			return item.ID, item | ||||||
|  | 		}) | ||||||
|  |  | ||||||
|  | 		idx := lo.Map(items, func(item *models.Feed, index int) uint { | ||||||
|  | 			return item.ID | ||||||
|  | 		}) | ||||||
|  |  | ||||||
|  | 		if err := database.C. | ||||||
|  | 			Model(&models.Attachment{}). | ||||||
|  | 			Select(v.ColumnName+"_id as post_id, *"). | ||||||
|  | 			Where(v.ColumnName+"_id IN (?)", idx). | ||||||
|  | 			Scan(&attachments).Error; err != nil { | ||||||
|  | 			return items, err | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		list := map[uint][]models.Attachment{} | ||||||
|  | 		for _, info := range attachments { | ||||||
|  | 			list[info.PostID] = append(list[info.PostID], info.Attachment) | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		for k, v := range list { | ||||||
|  | 			if post, ok := itemMap[k]; ok { | ||||||
|  | 				post.Attachments = v | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  |  | ||||||
| 	return items, nil | 	return items, nil | ||||||
| } | } | ||||||
|  |  | ||||||
|   | |||||||
| @@ -22,6 +22,7 @@ | |||||||
|     "universal-cookie": "^7.1.0", |     "universal-cookie": "^7.1.0", | ||||||
|     "unocss": "^0.58.5", |     "unocss": "^0.58.5", | ||||||
|     "vue": "^3.4.15", |     "vue": "^3.4.15", | ||||||
|  |     "vue-easy-lightbox": "next", | ||||||
|     "vue-router": "^4.2.5", |     "vue-router": "^4.2.5", | ||||||
|     "vuetify": "^3.5.7" |     "vuetify": "^3.5.7" | ||||||
|   }, |   }, | ||||||
|   | |||||||
| @@ -9,7 +9,7 @@ | |||||||
|       /> |       /> | ||||||
|     </div> |     </div> | ||||||
|  |  | ||||||
|     <div class="flex-grow-1"> |     <div class="flex-grow-1 relative"> | ||||||
|       <div class="font-bold">{{ props.item?.author.nick }}</div> |       <div class="font-bold">{{ props.item?.author.nick }}</div> | ||||||
|  |  | ||||||
|       <div v-if="props.item?.model_type === 'article'" class="text-xs text-grey-darken-4 mb-2"> |       <div v-if="props.item?.model_type === 'article'" class="text-xs text-grey-darken-4 mb-2"> | ||||||
| @@ -32,27 +32,61 @@ | |||||||
|         :reactions="props.item?.reaction_list ?? {}" |         :reactions="props.item?.reaction_list ?? {}" | ||||||
|         @update="updateReactions" |         @update="updateReactions" | ||||||
|       /> |       /> | ||||||
|  |  | ||||||
|  |       <div class="mt-1 text-xs text-opacity-60 flex gap-2 items-center"> | ||||||
|  |         <span>Posted at {{ new Date(props.item?.created_at).toLocaleString() }}</span> | ||||||
|  |       </div> | ||||||
|  |  | ||||||
|  |       <v-menu> | ||||||
|  |         <template #activator="{ props }"> | ||||||
|  |           <div class="absolute right-0 top-0"> | ||||||
|  |             <v-btn v-bind="props" icon="mdi-dots-vertical" variant="text" size="x-small" /> | ||||||
|  |           </div> | ||||||
|  |         </template> | ||||||
|  |  | ||||||
|  |         <v-list density="compact" lines="one"> | ||||||
|  |           <v-list-item disabled append-icon="mdi-flag" title="Report" /> | ||||||
|  |           <v-list-item v-if="isOwned" append-icon="mdi-pencil" title="Edit" @click="editPost" /> | ||||||
|  |           <v-list-item v-if="isOwned" append-icon="mdi-delete" title="Delete" /> | ||||||
|  |         </v-list> | ||||||
|  |       </v-menu> | ||||||
|     </div> |     </div> | ||||||
|   </div> |   </div> | ||||||
| </template> | </template> | ||||||
|  |  | ||||||
| <script setup lang="ts"> | <script setup lang="ts"> | ||||||
| import type { Component } from "vue" | import { computed, type Component } from "vue" | ||||||
|  | import { useUserinfo } from "@/stores/userinfo" | ||||||
|  | import { useEditor } from "@/stores/editor" | ||||||
| import ArticleContent from "@/components/posts/ArticleContent.vue" | import ArticleContent from "@/components/posts/ArticleContent.vue" | ||||||
| import MomentContent from "@/components/posts/MomentContent.vue" | import MomentContent from "@/components/posts/MomentContent.vue" | ||||||
| import CommentContent from "@/components/posts/CommentContent.vue" | import CommentContent from "@/components/posts/CommentContent.vue" | ||||||
| import PostAttachment from "./PostAttachment.vue" | import PostAttachment from "./PostAttachment.vue" | ||||||
| import PostReaction from "@/components/posts/PostReaction.vue" | import PostReaction from "@/components/posts/PostReaction.vue" | ||||||
|  |  | ||||||
|  | const id = useUserinfo() | ||||||
|  |  | ||||||
| const props = defineProps<{ item: any; brief?: boolean }>() | const props = defineProps<{ item: any; brief?: boolean }>() | ||||||
| const emits = defineEmits(["update:item"]) | const emits = defineEmits(["update:item"]) | ||||||
|  |  | ||||||
|  | const editor = useEditor() | ||||||
|  |  | ||||||
| const renderer: { [id: string]: Component } = { | const renderer: { [id: string]: Component } = { | ||||||
|   article: ArticleContent, |   article: ArticleContent, | ||||||
|   moment: MomentContent, |   moment: MomentContent, | ||||||
|   comment: CommentContent |   comment: CommentContent | ||||||
| } | } | ||||||
|  |  | ||||||
|  | const isOwned = computed(() => props.item?.author_id === id.userinfo.data.id) | ||||||
|  |  | ||||||
|  | function editPost() { | ||||||
|  |   editor.related.edit_to = props.item | ||||||
|  |   if(editor.show.hasOwnProperty(props.item.model_type)) { | ||||||
|  |     // @ts-ignore | ||||||
|  |     editor.show[props.item.model_type] = true | ||||||
|  |   } | ||||||
|  | } | ||||||
|  |  | ||||||
| function updateReactions(symbol: string, num: number) { | function updateReactions(symbol: string, num: number) { | ||||||
|   const item = JSON.parse(JSON.stringify(props.item)) |   const item = JSON.parse(JSON.stringify(props.item)) | ||||||
|   if (item.reaction_list == null) { |   if (item.reaction_list == null) { | ||||||
|   | |||||||
| @@ -21,6 +21,10 @@ | |||||||
|  |  | ||||||
|       <v-card-text> |       <v-card-text> | ||||||
|         <v-container class="article-container"> |         <v-container class="article-container"> | ||||||
|  |           <v-alert v-if="editor.related.edit_to" class="mb-3" type="info" variant="tonal"> | ||||||
|  |             You are editing a post with alias <b class="font-mono">{{ editor.related.edit_to?.alias }}</b> | ||||||
|  |           </v-alert> | ||||||
|  |  | ||||||
|           <v-textarea |           <v-textarea | ||||||
|             required |             required | ||||||
|             class="mb-3" |             class="mb-3" | ||||||
| @@ -40,6 +44,7 @@ | |||||||
|                     variant="solo-filled" |                     variant="solo-filled" | ||||||
|                     density="comfortable" |                     density="comfortable" | ||||||
|                     label="Title" |                     label="Title" | ||||||
|  |                     :loading="reverting" | ||||||
|                     v-model="data.title" |                     v-model="data.title" | ||||||
|                   /> |                   /> | ||||||
|  |  | ||||||
| @@ -103,7 +108,7 @@ | |||||||
| import { request } from "@/scripts/request" | import { request } from "@/scripts/request" | ||||||
| import { useEditor } from "@/stores/editor" | import { useEditor } from "@/stores/editor" | ||||||
| import { getAtk } from "@/stores/userinfo" | import { getAtk } from "@/stores/userinfo" | ||||||
| import { reactive, ref } from "vue" | import { reactive, ref, watch } from "vue" | ||||||
| import { useRouter } from "vue-router" | import { useRouter } from "vue-router" | ||||||
| import PlannedPublish from "@/components/publish/parts/PlannedPublish.vue" | import PlannedPublish from "@/components/publish/parts/PlannedPublish.vue" | ||||||
| import Media from "@/components/publish/parts/Media.vue" | import Media from "@/components/publish/parts/Media.vue" | ||||||
| @@ -116,11 +121,11 @@ const dialogs = reactive({ | |||||||
|   media: false |   media: false | ||||||
| }) | }) | ||||||
|  |  | ||||||
| const data = reactive<any>({ | const data = ref<any>({ | ||||||
|   title: "", |   title: "", | ||||||
|   content: "", |   content: "", | ||||||
|   description: "", |   description: "", | ||||||
|   publishedAt: null, |   published_at: null, | ||||||
|   attachments: [] |   attachments: [] | ||||||
| }) | }) | ||||||
|  |  | ||||||
| @@ -128,6 +133,7 @@ const router = useRouter() | |||||||
|  |  | ||||||
| const error = ref<string | null>(null) | const error = ref<string | null>(null) | ||||||
| const success = ref(false) | const success = ref(false) | ||||||
|  | const reverting = ref(false) | ||||||
| const loading = ref(false) | const loading = ref(false) | ||||||
| const uploading = ref(false) | const uploading = ref(false) | ||||||
|  |  | ||||||
| @@ -136,15 +142,20 @@ async function postArticle(evt: SubmitEvent) { | |||||||
|  |  | ||||||
|   if (uploading.value) return |   if (uploading.value) return | ||||||
|  |  | ||||||
|   if (!data.content) return |   const payload = data.value | ||||||
|   if (!data.title || !data.description) return |   console.log(payload) | ||||||
|   if (!data.publishedAt) data.publishedAt = new Date().toISOString() |   if (!payload.content) return | ||||||
|  |   if (!payload.title || !payload.description) return | ||||||
|  |   if (!payload.publishedAt) payload.publishedAt = new Date().toISOString() | ||||||
|  |  | ||||||
|  |   const url = editor.related.edit_to ? `/api/p/articles/${editor.related.edit_to?.id}` : "/api/p/articles" | ||||||
|  |   const method = editor.related.edit_to ? "PUT" : "POST" | ||||||
|  |  | ||||||
|   loading.value = true |   loading.value = true | ||||||
|   const res = await request("/api/p/articles", { |   const res = await request(url, { | ||||||
|     method: "POST", |     method: method, | ||||||
|     headers: { "Content-Type": "application/json", Authorization: `Bearer ${getAtk()}` }, |     headers: { "Content-Type": "application/json", Authorization: `Bearer ${getAtk()}` }, | ||||||
|     body: JSON.stringify(data) |     body: JSON.stringify(payload) | ||||||
|   }) |   }) | ||||||
|   if (res.status === 200) { |   if (res.status === 200) { | ||||||
|     const data = await res.json() |     const data = await res.json() | ||||||
| @@ -166,13 +177,22 @@ function pasteMedia(evt: ClipboardEvent) { | |||||||
|     Array.from(files).forEach((item) => { |     Array.from(files).forEach((item) => { | ||||||
|       media.value.upload(item).then((meta: any) => { |       media.value.upload(item).then((meta: any) => { | ||||||
|         if (meta) { |         if (meta) { | ||||||
|           data.content += `\n` |           data.value.content += `\n` | ||||||
|         } |         } | ||||||
|       }) |       }) | ||||||
|     }) |     }) | ||||||
|     evt.preventDefault() |     evt.preventDefault() | ||||||
|   } |   } | ||||||
| } | } | ||||||
|  |  | ||||||
|  | watch(editor.related, (val) => { | ||||||
|  |   if (val.edit_to && val.edit_to.model_type === "article") { | ||||||
|  |     request(`/api/p/articles/${val.edit_to.alias}`).then(async (res) => { | ||||||
|  |       data.value = await res.json() | ||||||
|  |       data.value.attachments = data.value.attachments ?? [] | ||||||
|  |     }) | ||||||
|  |   } | ||||||
|  | }) | ||||||
| </script> | </script> | ||||||
|  |  | ||||||
| <style scoped> | <style scoped> | ||||||
|   | |||||||
| @@ -2,9 +2,20 @@ | |||||||
|   <v-card title="Record a moment" :loading="loading"> |   <v-card title="Record a moment" :loading="loading"> | ||||||
|     <v-form @submit.prevent="postMoment"> |     <v-form @submit.prevent="postMoment"> | ||||||
|       <v-card-text> |       <v-card-text> | ||||||
|         <v-textarea required hide-details name="content" variant="outlined" label="What's happened?!" /> |         <v-alert v-if="editor.related.edit_to" class="mb-3" type="info" variant="tonal"> | ||||||
|  |           You are editing a post with alias <b class="font-mono">{{ editor.related.edit_to?.alias }}</b> | ||||||
|  |         </v-alert> | ||||||
|  |  | ||||||
|         <div class="flex mt-1"> |         <v-textarea | ||||||
|  |           required | ||||||
|  |           persistent-counter | ||||||
|  |           variant="outlined" | ||||||
|  |           label="What's happened?!" | ||||||
|  |           counter="1024" | ||||||
|  |           v-model="data.content" | ||||||
|  |         /> | ||||||
|  |  | ||||||
|  |         <div class="flex mt-[-18px]"> | ||||||
|           <v-tooltip text="Planned publish" location="start"> |           <v-tooltip text="Planned publish" location="start"> | ||||||
|             <template #activator="{ props }"> |             <template #activator="{ props }"> | ||||||
|               <v-btn |               <v-btn | ||||||
| @@ -17,18 +28,6 @@ | |||||||
|               /> |               /> | ||||||
|             </template> |             </template> | ||||||
|           </v-tooltip> |           </v-tooltip> | ||||||
|           <v-tooltip text="Categories" location="start"> |  | ||||||
|             <template #activator="{ props }"> |  | ||||||
|               <v-btn |  | ||||||
|                 v-bind="props" |  | ||||||
|                 type="button" |  | ||||||
|                 variant="text" |  | ||||||
|                 icon="mdi-shape" |  | ||||||
|                 size="small" |  | ||||||
|                 @click="dialogs.categories = true" |  | ||||||
|               /> |  | ||||||
|             </template> |  | ||||||
|           </v-tooltip> |  | ||||||
|           <v-tooltip text="Media" location="start"> |           <v-tooltip text="Media" location="start"> | ||||||
|             <template #activator="{ props }"> |             <template #activator="{ props }"> | ||||||
|               <v-btn |               <v-btn | ||||||
| @@ -53,8 +52,8 @@ | |||||||
|     </v-form> |     </v-form> | ||||||
|   </v-card> |   </v-card> | ||||||
|  |  | ||||||
|   <planned-publish v-model:show="dialogs.plan" v-model:value="extras.publishedAt" /> |   <planned-publish v-model:show="dialogs.plan" v-model:value="data.published_at" /> | ||||||
|   <media v-model:show="dialogs.media" v-model:uploading="uploading" v-model:value="extras.attachments" /> |   <media v-model:show="dialogs.media" v-model:uploading="uploading" v-model:value="data.attachments" /> | ||||||
|  |  | ||||||
|   <v-snackbar v-model="success" :timeout="3000">Your post has been published.</v-snackbar> |   <v-snackbar v-model="success" :timeout="3000">Your post has been published.</v-snackbar> | ||||||
|   <v-snackbar v-model="uploading" :timeout="-1"> |   <v-snackbar v-model="uploading" :timeout="-1"> | ||||||
| @@ -70,7 +69,7 @@ | |||||||
| import { request } from "@/scripts/request" | import { request } from "@/scripts/request" | ||||||
| import { useEditor } from "@/stores/editor" | import { useEditor } from "@/stores/editor" | ||||||
| import { getAtk } from "@/stores/userinfo" | import { getAtk } from "@/stores/userinfo" | ||||||
| import { reactive, ref } from "vue" | import { reactive, ref, watch } from "vue" | ||||||
| import PlannedPublish from "@/components/publish/parts/PlannedPublish.vue" | import PlannedPublish from "@/components/publish/parts/PlannedPublish.vue" | ||||||
| import Media from "@/components/publish/parts/Media.vue" | import Media from "@/components/publish/parts/Media.vue" | ||||||
|  |  | ||||||
| @@ -78,12 +77,12 @@ const editor = useEditor() | |||||||
|  |  | ||||||
| const dialogs = reactive({ | const dialogs = reactive({ | ||||||
|   plan: false, |   plan: false, | ||||||
|   categories: false, |  | ||||||
|   media: false |   media: false | ||||||
| }) | }) | ||||||
|  |  | ||||||
| const extras = reactive({ | const data = ref<any>({ | ||||||
|   publishedAt: null, |   content: "", | ||||||
|  |   published_at: null, | ||||||
|   attachments: [] |   attachments: [] | ||||||
| }) | }) | ||||||
|  |  | ||||||
| @@ -94,18 +93,18 @@ const uploading = ref(false) | |||||||
|  |  | ||||||
| async function postMoment(evt: SubmitEvent) { | async function postMoment(evt: SubmitEvent) { | ||||||
|   const form = evt.target as HTMLFormElement |   const form = evt.target as HTMLFormElement | ||||||
|   const data: any = Object.fromEntries(new FormData(form)) |   const payload = data.value | ||||||
|   if (!data.hasOwnProperty("content")) return |   if (!payload.content) return | ||||||
|   if (!extras.publishedAt) data["published_at"] = new Date().toISOString() |   if (!payload.published_at) payload.published_at = new Date().toISOString() | ||||||
|   else data["published_at"] = extras.publishedAt |  | ||||||
|  |  | ||||||
|   data["attachments"] = extras.attachments |   const url = editor.related.edit_to ? `/api/p/moments/${editor.related.edit_to?.id}` : "/api/p/moments" | ||||||
|  |   const method = editor.related.edit_to ? "PUT" : "POST" | ||||||
|  |  | ||||||
|   loading.value = true |   loading.value = true | ||||||
|   const res = await request("/api/p/moments", { |   const res = await request(url, { | ||||||
|     method: "POST", |     method: method, | ||||||
|     headers: { "Content-Type": "application/json", Authorization: `Bearer ${getAtk()}` }, |     headers: { "Content-Type": "application/json", Authorization: `Bearer ${getAtk()}` }, | ||||||
|     body: JSON.stringify(data) |     body: JSON.stringify(payload) | ||||||
|   }) |   }) | ||||||
|   if (res.status === 200) { |   if (res.status === 200) { | ||||||
|     form.reset() |     form.reset() | ||||||
| @@ -116,6 +115,12 @@ async function postMoment(evt: SubmitEvent) { | |||||||
|   } |   } | ||||||
|   loading.value = false |   loading.value = false | ||||||
| } | } | ||||||
|  |  | ||||||
|  | watch(editor.related, (val) => { | ||||||
|  |   if (val.edit_to) { | ||||||
|  |     data.value = val.edit_to | ||||||
|  |   } | ||||||
|  | }) | ||||||
| </script> | </script> | ||||||
|  |  | ||||||
| <style> | <style> | ||||||
|   | |||||||
| @@ -10,7 +10,8 @@ export const useEditor = defineStore("editor", () => { | |||||||
|     comment: false |     comment: false | ||||||
|   }) |   }) | ||||||
|  |  | ||||||
|   const related = reactive<{ comment_to: any; reply_to: any; repost_to: any }>({ |   const related = reactive<{ edit_to: any; comment_to: any; reply_to: any; repost_to: any }>({ | ||||||
|  |     edit_to: null, | ||||||
|     comment_to: null, |     comment_to: null, | ||||||
|     reply_to: null, |     reply_to: null, | ||||||
|     repost_to: null |     repost_to: null | ||||||
|   | |||||||
| @@ -15,8 +15,8 @@ | |||||||
|  |  | ||||||
|             <div class="px-3"> |             <div class="px-3"> | ||||||
|               <post-reaction |               <post-reaction | ||||||
|  |                 model="articles" | ||||||
|                 :item="post" |                 :item="post" | ||||||
|                 :model="route.params.postType" |  | ||||||
|                 :reactions="post?.reaction_list ?? {}" |                 :reactions="post?.reaction_list ?? {}" | ||||||
|                 @update="updateReactions" |                 @update="updateReactions" | ||||||
|               /> |               /> | ||||||
| @@ -32,9 +32,9 @@ | |||||||
|           <comment-list |           <comment-list | ||||||
|             model="article" |             model="article" | ||||||
|             dataset="articles" |             dataset="articles" | ||||||
|             v-model:comments="comments" |  | ||||||
|             :item="post" |             :item="post" | ||||||
|             :alias="route.params.alias" |             :alias="route.params.alias" | ||||||
|  |             v-model:comments="comments" | ||||||
|           /> |           /> | ||||||
|         </div> |         </div> | ||||||
|       </v-card> |       </v-card> | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user