Initial Montech monorepo: Nuxt landing, Vue admin, Nest API.
Scaffold SSR site with CMS/blog/gallery/SEO admin and deploy configs for montech.ir. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -0,0 +1,100 @@
|
||||
<template>
|
||||
<div>
|
||||
<div class="row" style="justify-content: space-between; margin-bottom: 1rem">
|
||||
<h1>{{ isNew ? "پست جدید" : "ویرایش پست" }}</h1>
|
||||
<RouterLink class="btn ghost" to="/blog">بازگشت</RouterLink>
|
||||
</div>
|
||||
<form class="card" @submit.prevent="save">
|
||||
<div class="grid-2">
|
||||
<div class="field">
|
||||
<label class="label">عنوان</label>
|
||||
<input v-model="form.title" class="input" required />
|
||||
</div>
|
||||
<div class="field">
|
||||
<label class="label">اسلاگ</label>
|
||||
<input v-model="form.slug" class="input" dir="ltr" required />
|
||||
</div>
|
||||
</div>
|
||||
<div class="field">
|
||||
<label class="label">خلاصه</label>
|
||||
<textarea v-model="form.excerpt" class="textarea" />
|
||||
</div>
|
||||
<div class="field">
|
||||
<label class="label">محتوا (HTML)</label>
|
||||
<textarea v-model="form.content" class="textarea" dir="ltr" style="min-height: 240px" />
|
||||
</div>
|
||||
<div class="grid-2">
|
||||
<div class="field">
|
||||
<label class="label">کاور URL</label>
|
||||
<input v-model="form.coverUrl" class="input" dir="ltr" />
|
||||
</div>
|
||||
<div class="field">
|
||||
<label class="label">SEO Title</label>
|
||||
<input v-model="form.seoTitle" class="input" />
|
||||
</div>
|
||||
<div class="field">
|
||||
<label class="label">SEO Description</label>
|
||||
<input v-model="form.seoDesc" class="input" />
|
||||
</div>
|
||||
<div class="field">
|
||||
<label class="label">Canonical</label>
|
||||
<input v-model="form.canonical" class="input" dir="ltr" />
|
||||
</div>
|
||||
<div class="field">
|
||||
<label class="label">OG Image</label>
|
||||
<input v-model="form.ogImage" class="input" dir="ltr" />
|
||||
</div>
|
||||
<div class="field">
|
||||
<label class="label"><input v-model="form.published" type="checkbox" /> منتشر شود</label>
|
||||
</div>
|
||||
</div>
|
||||
<p v-if="msg" class="muted">{{ msg }}</p>
|
||||
<button class="btn">ذخیره</button>
|
||||
</form>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { computed, onMounted, reactive, ref } from "vue";
|
||||
import { useRoute, useRouter } from "vue-router";
|
||||
import { api } from "../lib/api";
|
||||
|
||||
const route = useRoute();
|
||||
const router = useRouter();
|
||||
const isNew = computed(() => route.name === "blog-new");
|
||||
const msg = ref("");
|
||||
const form = reactive({
|
||||
title: "",
|
||||
slug: "",
|
||||
excerpt: "",
|
||||
content: "",
|
||||
coverUrl: "",
|
||||
seoTitle: "",
|
||||
seoDesc: "",
|
||||
ogImage: "",
|
||||
canonical: "",
|
||||
published: false,
|
||||
});
|
||||
|
||||
onMounted(async () => {
|
||||
if (isNew.value) return;
|
||||
const { data } = await api.get(`/admin/blog/${route.params.id}`);
|
||||
Object.assign(form, data);
|
||||
});
|
||||
|
||||
async function save() {
|
||||
msg.value = "";
|
||||
try {
|
||||
if (isNew.value) {
|
||||
const { data } = await api.post("/admin/blog", form);
|
||||
msg.value = "ایجاد شد.";
|
||||
router.replace(`/blog/${data.id}`);
|
||||
} else {
|
||||
await api.put(`/admin/blog/${route.params.id}`, form);
|
||||
msg.value = "ذخیره شد.";
|
||||
}
|
||||
} catch {
|
||||
msg.value = "خطا در ذخیره.";
|
||||
}
|
||||
}
|
||||
</script>
|
||||
Reference in New Issue
Block a user