ali 1 год назад
Родитель
Сommit
2539c8cd29

+ 2 - 2
src/assets/ShamsiDate.js

@@ -15,8 +15,8 @@ class ShamsiDate {
     return moment(dateTime, 'YYYY-M-D HH:mm:ss').format('HH:mm:ss jYYYY/jMM/jDD')
   }
 
-  static getTitledDateTime (dateTime) {
-    return moment(dateTime, 'YYYY-M-D HH:mm:ss').format('jD jMMMM jYYYY')
+  static getTitledDateTime (dateTime, format = 'jD jMMMM jYYYY') {
+    return moment(dateTime, 'YYYY-M-D HH:mm:ss').format(format)
   }
 }
 

+ 192 - 0
src/components/Widgets/Post/LastPosts/LastPosts.vue

@@ -0,0 +1,192 @@
+<template>
+  <div class="LastPostsCarousel">
+    <div class="LastPostsCarousel-title">
+      <div>
+        <q-banner class="banner">
+          آخرین
+          اخبار
+        </q-banner>
+      </div>
+      <div>
+        <q-btn flat
+               :to="{name: 'Public.Post.List.News'}">
+          مشاهده همه
+          <svg xmlns="http://www.w3.org/2000/svg"
+               width="18.387"
+               height="11.502"
+               viewBox="0 0 18.387 11.502"
+               class="q-ml-sm">
+            <path id="Combined_Shape"
+                  data-name="Combined Shape"
+                  d="M-9.338,11.408a.748.748,0,0,0,.388-.656V6.5h8.2A.751.751,0,0,0,0,5.75.75.75,0,0,0-.75,5h-8.2V.75A.751.751,0,0,0-9.338.093.769.769,0,0,0-9.7,0a.735.735,0,0,0-.4.115l-7.938,5a.747.747,0,0,0-.35.635.742.742,0,0,0,.35.634l7.938,5a.751.751,0,0,0,.4.116A.746.746,0,0,0-9.338,11.408ZM-10.45,9.392l-5.78-3.641,5.78-3.642Z"
+                  transform="translate(18.387)"
+                  fill="#eac38a" />
+          </svg>
+        </q-btn>
+      </div>
+    </div>
+    <div class="carousel-section">
+      <div v-if="!posts.loading && posts.list.length > 0"
+           ref="slider"
+           class="splide"
+           role="group"
+           aria-label="Splide Basic HTML Example">
+        <div class="splide__track">
+          <ul class="splide__list">
+            <li v-for="(post, postIndex) in posts.list"
+                :key="postIndex"
+                class="splide__slide">
+              <post-item :post="post" />
+            </li>
+          </ul>
+        </div>
+      </div>
+      <div v-else-if="!posts.loading && posts.list.length === 0">
+        رویدادی یافت نشد.
+      </div>
+      <div v-else-if="posts.loading">
+        کمی صبر کنید ...
+      </div>
+    </div>
+  </div>
+</template>
+
+<script>
+import Splide from '@splidejs/splide'
+import { PostList } from 'src/models/Post.js'
+import PostItem from './components/PostItem.vue'
+import ShamsiDate from 'src/assets/ShamsiDate.js'
+import { APIGateway } from 'src/api/APIGateway.js'
+import '@splidejs/splide/dist/css/splide-core.min.css'
+import { mixinPrefetchServerData, mixinWidget } from 'src/mixin/Mixins.js'
+
+export default {
+  name: 'LastPosts',
+  components: {
+    PostItem
+  },
+  mixins: [mixinPrefetchServerData, mixinWidget],
+  data () {
+    return {
+      loading: false,
+      slider: null,
+      posts: new PostList(),
+      defaultOptions: {
+        categoryId: null,
+        categoryCategoryId: null,
+        categoryCategoryCategoryId: null,
+        categoryFromQueryParams: false,
+        categoryCategoryFromQueryParams: false,
+        categoryCategoryCategoryFromQueryParams: false
+      },
+      breakpoints: {
+        1920: {
+          perPage: 1
+        },
+        900: {
+          perPage: 1
+        },
+        500: {
+          perPage: 1
+        }
+      },
+      maximizedToggle: true
+    }
+  },
+  computed: {
+    postCategoryId () {
+      if (this.localOptions.categoryId) {
+        return this.localOptions.categoryId
+      }
+
+      return null
+    }
+  },
+  mounted () {
+    this.loadSlider()
+  },
+  methods: {
+    loadSlider () {
+      this.$nextTick(() => {
+        if (!this.$refs.slider) {
+          return
+        }
+        new Splide(this.$refs.slider, {
+          direction: 'rtl',
+          paginationDirection: 'rtl',
+          // type: 'loop',
+          focus: 0,
+          gap: 24,
+          snap: true,
+          // focus: 'center',
+          breakpoints: this.breakpoints
+        }).mount()
+      })
+    },
+    prefetchServerDataPromise () {
+      return this.getLastPosts()
+    },
+    prefetchServerDataPromiseThen (postList) {
+      this.posts = new PostList(postList.list)
+      this.posts.loading = false
+      this.loadSlider()
+    },
+    prefetchServerDataPromiseCatch () {
+      this.posts.loading = false
+    },
+
+    getTerm (event) {
+      if (!event.beginning_registration_period) {
+        return '-'
+      }
+      return ShamsiDate.getTerm(event.beginning_registration_period)
+    },
+    getLastPosts () {
+      const params = {}
+
+      if (this.localOptions.categoryCategoryCategoryId) {
+        params.category__parent__parent = this.localOptions.categoryCategoryCategoryId
+      }
+
+      if (this.localOptions.categoryCategoryId) {
+        params.category__parent = this.localOptions.categoryCategoryId
+      }
+
+      if (this.localOptions.categoryId) {
+        params.category = this.localOptions.categoryId
+      }
+
+      this.posts.loading = true
+      return APIGateway.post.index(params)
+    }
+  }
+}
+</script>
+
+<style scoped lang="scss">
+.LastPostsCarousel {
+  padding: 60px 0;
+  .LastPostsCarousel-title {
+    display: flex;
+    flex-flow: row;
+    justify-content: space-between;
+    padding-bottom: 33px;
+  }
+  :deep(.carousel-section) {
+    .splide__arrows {
+      top: auto;
+      bottom: 0;
+      .splide__arrow {
+        &.splide__arrow--prev {
+          left: 50%;
+          transform: translateX(-120%);
+        }
+        &.splide__arrow--next {
+          right: 50%;
+          transform: translateX(120%);
+        }
+      }
+    }
+  }
+}
+</style>

+ 71 - 0
src/components/Widgets/Post/LastPosts/OptionPanel.vue

@@ -0,0 +1,71 @@
+<template>
+  <option-panel-tabs v-model:options="localOptions">
+    <template #main-tab>
+      <div class="q-mt-md">
+        کد دسته پدربزرگ
+      </div>
+      <div class="row">
+        <div class="col-md-4 col-12">
+          <q-checkbox v-model="localOptions.categoryCategoryCategoryFromQueryParams"
+                      label="استفاده از کوری پارام صفحه" />
+        </div>
+        <div v-if="!localOptions.categoryCategoryCategoryFromQueryParams"
+             class="col-md-8 col-12">
+          <q-input v-model="localOptions.categoryCategoryCategoryId" />
+        </div>
+      </div>
+      <div>
+        کد دسته پدر
+      </div>
+      <div class="row">
+        <div class="col-md-4 col-12">
+          <q-checkbox v-model="localOptions.categoryCategoryFromQueryParams"
+                      label="استفاده از کوری پارام صفحه" />
+        </div>
+        <div v-if="!localOptions.categoryCategoryFromQueryParams"
+             class="col-md-8 col-12">
+          <q-input v-model="localOptions.categoryCategoryId" />
+        </div>
+      </div>
+      <div>
+        کد دسته فرزند
+      </div>
+      <div class="row">
+        <div class="col-md-4 col-12">
+          <q-checkbox v-model="localOptions.categoryFromQueryParams"
+                      label="استفاده از کوری پارام صفحه" />
+        </div>
+        <div v-if="!localOptions.categoryFromQueryParams"
+             class="col-md-8 col-12">
+          <q-input v-model="localOptions.categoryId" />
+        </div>
+      </div>
+    </template>
+  </option-panel-tabs>
+</template>
+
+<script>
+import { defineComponent } from 'vue'
+import { mixinOptionPanel } from 'quasar-ui-q-page-builder'
+import OptionPanelTabs from 'quasar-ui-q-page-builder/src/components/OptionPanelComponents/OptionPanelTabs.vue'
+
+export default defineComponent({
+  name: 'OptionPanel',
+  components: {
+    OptionPanelTabs
+  },
+  mixins: [mixinOptionPanel],
+  data () {
+    return {
+      defaultOptions: {
+        categoryId: null,
+        categoryCategoryId: null,
+        categoryCategoryCategoryId: null,
+        categoryFromQueryParams: false,
+        categoryCategoryFromQueryParams: false,
+        categoryCategoryCategoryFromQueryParams: false
+      }
+    }
+  }
+})
+</script>

+ 113 - 0
src/components/Widgets/Post/LastPosts/components/PostItem.vue

@@ -0,0 +1,113 @@
+<template>
+  <div class="col-md-4 col-sm-6 col-12">
+    <q-card flat
+            class="PostItem">
+      <div class="content">
+        <div class="title-and-body">
+          <div class="title ellipsis-2-lines">
+            <router-link :to="{name: 'Public.Post.Show', params: {id: post.id}}">
+              {{post.title}}
+            </router-link>
+          </div>
+          <div class="body"
+               v-html="post.text" />
+        </div>
+        <div class="meta-and-action">
+          <div class="date-time">
+            <q-icon name="calendar_month"
+                    size="20px" />
+            <span class="date-time-value">
+              {{ getDateTime(post.creation_time) }}
+            </span>
+          </div>
+          <q-btn color="primary"
+                 outline
+                 class="btn-show-post"
+                 :to="{name: 'Public.Post.Show', params: {id: post.id}}">
+            مشاهده جزئیات
+          </q-btn>
+        </div>
+      </div>
+      <div class="thumbnail">
+        <router-link :to="{name: 'Public.Post.Show', params: {id: post.id}}">
+          <q-img :src="post.thumbnail" />
+        </router-link>
+      </div>
+    </q-card>
+  </div>
+</template>
+
+<script>
+import { defineComponent } from 'vue'
+import { Post } from 'src/models/Post.js'
+import ShamsiDate from 'src/assets/ShamsiDate.js'
+
+export default defineComponent({
+  name: 'PostItem',
+  props: {
+    post: {
+      type: Post,
+      default: new Post()
+    }
+  },
+  methods: {
+    getDateTime (dateTime) {
+      return ShamsiDate.getTitledDateTime(dateTime, 'a hh:mm - jYYYY/jMM/jDD')
+    }
+  }
+})
+</script>
+
+<style lang="scss" scoped>
+.PostItem {
+  display: flex;
+  flex-flow: row;
+  justify-content: space-between;
+  background-color: white;
+  border-radius: 8px;
+  padding: 0;
+  margin: 0;
+  .thumbnail {
+    width: 45%;
+    .q-img {
+      border-radius: 20px 8px 8px 20px;
+    }
+  }
+  .content {
+    width: 55%;
+    padding: 52px 80px 52px 40px;
+    display: flex;
+    flex-flow: column;
+    justify-content: space-between;
+    .title-and-body {
+      margin-bottom: 28px;
+      .title {
+        font-size: 18px;
+        letter-spacing: 0;
+        margin-bottom: 26px;
+        a {
+          color: #212121;
+        }
+      }
+      .body {
+        font-size: 16px;
+        letter-spacing: 0;
+        color: #212121;
+        height: 220px;
+        overflow: hidden;
+      }
+    }
+    .meta-and-action {
+      display: flex;
+      justify-content: space-between;
+      align-items: center;
+      .date-time {
+        color: #929292;
+        .date-time-value {
+          margin-left: 8px;
+        }
+      }
+    }
+  }
+}
+</style>

+ 1 - 0
src/css/Theme/Controls/carousel.scss

@@ -89,6 +89,7 @@
   .splide__pagination {
     margin-top: 24px;
     gap: 4px;
+    padding-inline-start: 0;
     li {
       .splide__pagination__page {
         transition: all 0.3s;

+ 1 - 0
src/models/Post.js

@@ -12,6 +12,7 @@ class Post extends Model {
       { key: 'summary' },
       { key: 'thumbnail' },
       { key: 'category' },
+      { key: 'creation_time' },
       {
         key: 'category_info',
         relatedModel: PostCategory