ali пре 2 година
родитељ
комит
52e3600969

+ 5 - 0
src/components/Template/Header/MainHeader.vue

@@ -38,6 +38,11 @@
                    label="Shop"
                    :to="{ name: 'Public.Shop' }"
                    class="top-menu-item" />
+            <q-btn stretch
+                   flat
+                   label="آرشیو محتوا"
+                   :to="{ name: 'Public.ContentCategoryParentParent.List' }"
+                   class="top-menu-item" />
             <q-btn stretch
                    flat
                    label="فروشگاه"

+ 90 - 0
src/components/Widgets/ContentCategory/ContentCategoryList/ContentCategoryItem.vue

@@ -0,0 +1,90 @@
+<template>
+  <q-card class="ContentCategoryItem"
+          flat>
+    <div class="thumbnail"
+         @click="goToCategoryPageShow">
+      <q-img :src="contentCategory.thumbnail" />
+    </div>
+    <div class="cation">
+      <q-btn color="primary"
+             :to="{name: showCategoryRouteName, params: {category_id: contentCategory.id}}">
+        مشاهده جزییات
+      </q-btn>
+    </div>
+    <div class="title ellipsis-2-lines">
+      {{ contentCategory.title }}
+    </div>
+    <div class="description ellipsis-3-lines">
+      {{ contentCategory.description }}
+    </div>
+  </q-card>
+</template>
+
+<script>
+import { ContentCategory } from 'src/models/ContentCategory.js'
+
+export default {
+  name: 'ContentCategoryItem',
+  props: {
+    contentCategory: {
+      type: ContentCategory,
+      default: new ContentCategory()
+    },
+    showCategoryRouteName: {
+      type: String,
+      default: 'Public.ContentCategory.Show'
+    }
+  },
+  methods: {
+    goToCategoryPageShow () {
+      this.$router.push({ name: this.showCategoryRouteName, params: { category_id: this.contentCategory.id } })
+    }
+  }
+}
+</script>
+
+<style lang="scss" scoped>
+.ContentCategoryItem {
+  display: flex;
+  flex-flow: column;
+  position: relative;
+  cursor: pointer;
+  .thumbnail {
+    .q-img {
+      width: 100%;
+    }
+  }
+  .cation {
+    .q-btn {
+      width: 100%;
+      border-top-right-radius: 0;
+      border-top-left-radius: 0;
+      height: 60px;
+    }
+  }
+  .title {
+    position: absolute;
+    bottom: 136px;
+    left: 21px;
+    color: #FFFFFF;
+    font-size: 16px;
+    font-weight: bold;
+  }
+  .description {
+    position: absolute;
+    left: 21px;
+    color: #FFFFFF;
+    font-size: 16px;
+    display: none;
+  }
+  &:hover {
+    .title {
+      bottom: 225px;
+    }
+    .description {
+      display: block;
+      bottom: 114px;
+    }
+  }
+}
+</style>

+ 218 - 0
src/components/Widgets/ContentCategory/ContentCategoryList/ContentCategoryList.vue

@@ -0,0 +1,218 @@
+<template>
+  <div class="ContentCategoryList"
+       :style="localOptions.style">
+    <breadcrumbs class="q-mb-xl"
+                 style="margin-top: 29px; margin-bottom: 19px;" />
+    <entity-index v-if="mounted"
+                  ref="entityIndex"
+                  v-model:value="inputs"
+                  :api="api"
+                  :table="table"
+                  :table-keys="tableKeys"
+                  :table-grid-size="true"
+                  :default-layout="false"
+                  :show-search-button="false"
+                  :show-expand-button="false"
+                  :show-reload-button="false">
+      <template #entity-index-table-item-cell="{inputData}">
+        <content-category-item :content-category="getContentCategory(inputData.props.row)"
+                               :show-category-route-name="showCategoryRouteName" />
+      </template>
+    </entity-index>
+  </div>
+</template>
+
+<script>
+import { EntityIndex } from 'quasar-crud'
+import { mixinWidget } from 'src/mixin/Mixins.js'
+import { APIGateway } from 'src/api/APIGateway.js'
+import ContentCategoryItem from './ContentCategoryItem.vue'
+import { FormBuilderAssist } from 'quasar-form-builder'
+import { ContentCategory } from 'src/models/ContentCategory.js'
+import Breadcrumbs from 'src/components/Widgets/Breadcrumbs/Breadcrumbs.vue'
+
+export default {
+  name: 'ContentCategoryList',
+  components: { ContentCategoryItem, EntityIndex, Breadcrumbs },
+  mixins: [mixinWidget],
+  data () {
+    return {
+      api: APIGateway.contentCategory.APIAdresses.base,
+      tableKeys: {
+        data: 'results',
+        total: 'count',
+        currentPage: 'current',
+        perPage: 'per_page',
+        pageKey: 'page'
+      },
+      inputs: [
+        { type: 'hidden', name: 'parent__isnull', value: false },
+        { type: 'hidden', name: 'parent', value: null }
+      ],
+      table: {
+        columns: []
+      },
+      mounted: false,
+      contentCategory: new ContentCategory(),
+      contentCategoryParent: new ContentCategory(),
+      contentCategoryParentParent: new ContentCategory(),
+      defaultOptions: {
+        listType: 'category_parent_parent'
+      }
+    }
+  },
+  computed: {
+    showCategoryRouteName () {
+      if (this.localOptions.listType === 'category_parent_parent') {
+        return 'Public.ContentCategoryParentParent.Show'
+      } else if (this.localOptions.listType === 'category_parent') {
+        return 'Public.ContentCategoryParent.Show'
+      } else if (this.localOptions.listType === 'category') {
+        return 'Public.ContentCategory.Show'
+      }
+      return 'Public.ContentCategory.Show'
+    },
+    breadcrumbsPath () {
+      const path = [
+        {
+          label: 'خانه',
+          to: { name: 'Public.Home' }
+        },
+        {
+          label: 'آرشیو محتوا',
+          to: { name: 'Public.ContentCategoryParentParent.List' }
+        }
+      ]
+
+      if (this.localOptions.listType === 'category_parent') {
+        if (this.contentCategory.id) {
+          path.push({
+            label: this.contentCategory.title,
+            to: { name: 'Public.ContentCategoryParentParent.Show', params: { category_id: this.contentCategory.id } }
+          })
+        }
+      } else if (this.localOptions.listType === 'category') {
+        if (this.contentCategoryParent.id) {
+          path.push({
+            label: this.contentCategoryParent.title,
+            to: { name: 'Public.ContentCategoryParentParent.Show', params: { category_id: this.contentCategoryParent.id } }
+          })
+        }
+        if (this.contentCategory.id) {
+          path.push({
+            label: this.contentCategory.title,
+            to: { name: 'Public.ContentCategoryParent.Show', params: { category_id: this.contentCategory.id } }
+          })
+        }
+      }
+      return path
+    },
+    contentCategoryId () {
+      if (this.$route.params.category_id) {
+        return this.$route.params.category_id
+      }
+
+      return null
+    }
+  },
+  created() {
+    this.updateBreadcrumbs()
+
+    if (this.localOptions.listType === 'category_parent_parent') {
+      FormBuilderAssist.setAttributeByName(this.inputs, 'parent__isnull', 'value', true)
+    } else if (this.localOptions.listType === 'category_parent') {
+      FormBuilderAssist.setAttributeByName(this.inputs, 'parent__isnull', 'value', null)
+      FormBuilderAssist.setAttributeByName(this.inputs, 'parent', 'value', this.contentCategoryId)
+    } else if (this.localOptions.listType === 'category') {
+      FormBuilderAssist.setAttributeByName(this.inputs, 'parent__isnull', 'value', null)
+      FormBuilderAssist.setAttributeByName(this.inputs, 'parent', 'value', this.contentCategoryId)
+    }
+    if (this.contentCategoryId) {
+      this.loadContentCategory()
+    }
+  },
+  mounted () {
+    this.mounted = true
+  },
+  methods: {
+    getContentCategory (data) {
+      return new ContentCategory(data)
+    },
+    loadContentCategory () {
+      this.contentCategory.loading = true
+      this.getContentCategoryInfo(this.contentCategoryId)
+        .then((contentCategory) => {
+          this.contentCategory = new ContentCategory(contentCategory)
+          this.updateBreadcrumbs()
+          if (this.contentCategory.parent) {
+            this.getContentCategoryInfo(this.contentCategory.parent)
+              .then((parentContentCategory) => {
+                this.contentCategoryParent = new ContentCategory(parentContentCategory)
+                this.updateBreadcrumbs()
+                if (this.contentCategoryParent.parent) {
+                  this.getContentCategoryInfo(this.contentCategoryParent.parent)
+                    .then((parentParentContentCategory) => {
+                      this.contentCategoryParentParent = new ContentCategory(parentParentContentCategory)
+                      this.updateBreadcrumbs()
+                    })
+                    .finally(() => {
+                      this.contentCategory.loading = false
+                    })
+                }
+              })
+              .finally(() => {
+                this.contentCategory.loading = false
+              })
+          }
+        })
+        .finally(() => {
+          this.contentCategory.loading = false
+        })
+    },
+    getContentCategoryInfo (contentCategoryId) {
+      return APIGateway.contentCategory.get({ data: { id: contentCategoryId } })
+    },
+    updateBreadcrumbs () {
+      const breadcrumbs = {
+        visible: true,
+        loading: false,
+        path: this.breadcrumbsPath
+      }
+
+      this.$store.commit('AppLayout/updateBreadcrumbs', breadcrumbs)
+    }
+  }
+}
+</script>
+
+<style scoped lang="scss">
+.ContentCategoryList {
+  :deep(.entity-index) {
+    .quasar-crud-index-table {
+      .q-table__top {
+        display: none;
+      }
+      .q-table__grid-content {
+        $gap: 24px;
+        flex-wrap: wrap;
+        gap: $gap;
+        .ContentCategoryItem {
+          flex: 0 0 calc( 33.33% - #{$gap} );
+          box-sizing: border-box;
+        }
+      }
+      .q-table__bottom {
+        .q-table__control {
+          display: none;
+        }
+      }
+    }
+  }
+  .more-action {
+    display: flex;
+    flex-flow: row;
+    justify-content: flex-end;
+    margin-bottom: 10px;
+  }
+}
+</style>

+ 49 - 0
src/components/Widgets/ContentCategory/ContentCategoryList/OptionPanel.vue

@@ -0,0 +1,49 @@
+<template>
+  <option-panel-tabs v-model:options="localOptions">
+    <template #main-tab>
+      <q-banner class="q-mt-md">
+        نوع لیست دسته
+      </q-banner>
+      <div>
+        <q-option-group v-model="localOptions.listType"
+                        :options="listTypeOptions"
+                        color="primary" />
+      </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 {
+      listTypeOptions: [
+        {
+          label: 'پدر بزرگ',
+          value: 'category_parent_parent'
+        },
+        {
+          label: 'پدر',
+          value: 'category_parent'
+        },
+        {
+          label: 'فرزند',
+          value: 'category'
+        }
+      ],
+      defaultOptions: {
+        listType: 'category_parent_parent'
+      }
+    }
+  }
+})
+</script>

+ 2 - 0
src/models/ContentCategory.js

@@ -5,7 +5,9 @@ class ContentCategory extends Model {
     super(data, [
       { key: 'id' },
       { key: 'title' },
+      { key: 'thumbnail' },
       { key: 'parent' },
+      { key: 'parent_info' },
       {
         key: 'children',
         defalut: []

+ 5 - 0
src/router/routes/Public.js

@@ -56,6 +56,11 @@ export default {
         hasDynamicSetting: true
       },
       children: [
+        {
+          path: 'parent/parent',
+          name: 'Public.ContentCategoryParentParent.List',
+          component: () => import('src/pages/BasicDynamicPage.vue')
+        },
         {
           path: 'parent/parent/:category_id',
           name: 'Public.ContentCategoryParentParent.Show',