ソースを参照

see also #420

ali 2 年 前
親
コミット
8f53dcec0b

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

@@ -1,5 +1,6 @@
 <template>
   <q-card class="ContentCategoryItem"
+          :class="listType"
           flat>
     <div class="thumbnail"
          @click="goToCategoryPageShow">
@@ -33,6 +34,10 @@ export default {
     showCategoryRouteName: {
       type: String,
       default: 'Public.ContentCategory.Show'
+    },
+    listType: {
+      type: String,
+      default: 'category_parent_parent'
     }
   },
   methods: {
@@ -52,6 +57,8 @@ export default {
   .thumbnail {
     .q-img {
       width: 100%;
+      border-top-left-radius: 8px;
+      border-top-right-radius: 8px;
     }
   }
   .cation {

+ 94 - 3
src/components/Widgets/ContentCategory/ContentCategoryList/ContentCategoryList.vue

@@ -1,8 +1,35 @@
 <template>
   <div class="ContentCategoryList"
+       :class="localOptions.listType"
        :style="localOptions.style">
-    <breadcrumbs class="q-mb-xl"
-                 style="margin-top: 29px; margin-bottom: 19px;" />
+    <div class="ContentCategory-info-wrapper">
+      <breadcrumbs class="q-mb-xl"
+                   style="margin-top: 29px; margin-bottom: 19px;" />
+      <div v-if="contentCategory.id"
+           class="ContentCategory-info">
+        <div class="ContentCategory-thumbnail">
+          <q-img :src="contentCategory.thumbnail" />
+        </div>
+        <div class="ContentCategory-meta">
+          <q-banner>
+            {{ contentCategory.title }}
+          </q-banner>
+          <div class="ContentCategory-desc"
+               v-html="contentCategory.description" />
+        </div>
+      </div>
+    </div>
+    <q-banner class="ContentCategoryList-banner">
+      <span v-if="localOptions.listType === 'category_parent_parent'">
+        دسته بندی های کلی
+      </span>
+      <span v-if="localOptions.listType === 'category_parent'">
+        دسته بندی اصلی
+      </span>
+      <span v-if="localOptions.listType === 'category'">
+        دسته بندی جزئی
+      </span>
+    </q-banner>
     <entity-index v-if="mounted"
                   ref="entityIndex"
                   v-model:value="inputs"
@@ -26,8 +53,8 @@
 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 ContentCategoryItem from './ContentCategoryItem.vue'
 import { ContentCategory } from 'src/models/ContentCategory.js'
 import Breadcrumbs from 'src/components/Widgets/Breadcrumbs/Breadcrumbs.vue'
 
@@ -187,7 +214,12 @@ export default {
 
 <style scoped lang="scss">
 .ContentCategoryList {
+  .ContentCategoryList-banner {
+    margin-bottom: 39px;
+    margin-top: 50px;
+  }
   :deep(.entity-index) {
+    margin-top: 24px;
     .quasar-crud-index-table {
       .q-table__top {
         display: none;
@@ -214,5 +246,64 @@ export default {
     justify-content: flex-end;
     margin-bottom: 10px;
   }
+  .ContentCategory-info-wrapper {
+    background: white;
+    width: 100%;
+    .ContentCategory-info {
+      background: #E5FDE6;
+      padding: 24px;
+      border: 1px solid #DFE1EC;
+      display: flex;
+      justify-content: flex-start;
+      .ContentCategory-thumbnail {
+        width: 189px;
+        border-radius: 8px;
+        margin-right: 24px;
+        overflow: hidden;
+      }
+      .ContentCategory-meta {
+        width: calc( 100% - 213px );
+        .q-banner {
+          margin-bottom: 21px;
+        }
+        .ContentCategory-desc {
+
+        }
+      }
+    }
+  }
+  &.category_parent {
+    .ContentCategory-info-wrapper {
+      .ContentCategory-info {
+        .ContentCategory-thumbnail {
+        }
+        .ContentCategory-meta {
+          .q-banner {
+            border-color: $primary;
+          }
+          .ContentCategory-desc {
+
+          }
+        }
+      }
+    }
+  }
+  &.category {
+    .ContentCategory-info-wrapper {
+      .ContentCategory-info {
+        background: #E5F2FD;
+        .ContentCategory-thumbnail {
+        }
+        .ContentCategory-meta {
+          .q-banner {
+            border-color: #475F4A;
+          }
+          .ContentCategory-desc {
+
+          }
+        }
+      }
+    }
+  }
 }
 </style>

+ 288 - 0
src/components/Widgets/ContentCategory/ContentCategoryShow/ContentCategoryShow.vue

@@ -0,0 +1,288 @@
+<template>
+  <div class="ContentCategoryShow"
+       :class="localOptions.listType"
+       :style="localOptions.style">
+    <div class="ContentCategory-info-wrapper">
+      <breadcrumbs class="q-mb-xl"
+                   style="margin-top: 29px; margin-bottom: 19px;" />
+      <div v-if="contentCategory.id"
+           class="ContentCategory-info">
+        <div class="ContentCategory-thumbnail">
+          <q-img :src="contentCategory.thumbnail" />
+        </div>
+        <div class="ContentCategory-meta">
+          <q-banner>
+            {{ contentCategory.title }}
+          </q-banner>
+          <div class="ContentCategory-desc"
+               v-html="contentCategory.description" />
+        </div>
+      </div>
+    </div>
+    <q-banner class="ContentCategoryShow-banner">
+      محصولات
+      {{ contentCategory.title }}
+    </q-banner>
+    <entity-index v-if="mounted && contentCategory.id"
+                  ref="entityIndex"
+                  v-model:value="productInputs"
+                  class="product-index"
+                  :api="productApi"
+                  :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}">
+        <product-item :product="getProduct(inputData.props.row)" />
+      </template>
+    </entity-index>
+    <q-banner class="ContentCategoryShow-banner">
+      محتواهای
+      {{ contentCategory.title }}
+    </q-banner>
+    <entity-index v-if="mounted && contentCategory.id"
+                  ref="entityIndex"
+                  v-model:value="contentInputs"
+                  class="product-index"
+                  :api="contentApi"
+                  :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-item :content="getContent(inputData.props.row)" />
+      </template>
+    </entity-index>
+  </div>
+</template>
+
+<script>
+import { EntityIndex } from 'quasar-crud'
+import { Content } from 'src/models/Content.js'
+import { Product } from 'src/models/Product.js'
+import { mixinWidget } from 'src/mixin/Mixins.js'
+import { APIGateway } from 'src/api/APIGateway.js'
+import { FormBuilderAssist } from 'quasar-form-builder'
+import { ContentCategory } from 'src/models/ContentCategory.js'
+import Breadcrumbs from 'src/components/Widgets/Breadcrumbs/Breadcrumbs.vue'
+import productItem from 'src/components/Widgets/Store/Shop/components/productItem.vue'
+import ContentItem from './ContentItem.vue'
+
+export default {
+  name: 'ContentCategoryShow',
+  components: { EntityIndex, Breadcrumbs, productItem, ContentItem },
+  mixins: [mixinWidget],
+  data () {
+    return {
+      productApi: APIGateway.product.APIAdresses.base,
+      contentApi: APIGateway.content.APIAdresses.base,
+      tableKeys: {
+        data: 'results',
+        total: 'count',
+        currentPage: 'current',
+        perPage: 'per_page',
+        pageKey: 'page'
+      },
+      productInputs: [
+        { type: 'hidden', name: 'content_category', value: null },
+        { type: 'hidden', name: 'per_page', value: 12 }
+      ],
+      contentInputs: [
+        { type: 'hidden', name: 'category', value: null },
+        { type: 'hidden', name: 'per_page', value: 12 }
+      ],
+      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.contentCategoryParentParent.id) {
+        path.push({
+          label: this.contentCategoryParentParent.title,
+          to: { name: 'Public.ContentCategoryParentParent.Show', params: { category_id: this.contentCategoryParentParent.id } }
+        })
+      }
+      if (this.contentCategoryParent.id) {
+        path.push({
+          label: this.contentCategoryParent.title,
+          to: { name: 'Public.ContentCategoryParent.Show', params: { category_id: this.contentCategoryParent.id } }
+        })
+      }
+      if (this.contentCategory.id) {
+        path.push({
+          label: this.contentCategory.title,
+          to: { name: 'Public.ContentCategory.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()
+    FormBuilderAssist.setAttributeByName(this.productInputs, 'content_category', 'value', this.contentCategoryId)
+    FormBuilderAssist.setAttributeByName(this.contentInputs, 'category', 'value', this.contentCategoryId)
+    if (this.contentCategoryId) {
+      this.loadContentCategory()
+    }
+  },
+  mounted () {
+    this.mounted = true
+  },
+  methods: {
+    getContent (data) {
+      return new Content(data)
+    },
+    getProduct (data) {
+      return new Product(data)
+    },
+    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">
+.ContentCategoryShow {
+  .ContentCategoryShow-banner {
+    margin-bottom: 39px;
+    margin-top: 50px;
+  }
+  :deep(.entity-index) {
+    margin-top: 24px;
+    .quasar-crud-index-table {
+      .q-table__top {
+        display: none;
+      }
+      .q-table__grid-content {
+        $gap: 24px;
+        flex-wrap: wrap;
+        gap: $gap;
+        justify-content: center;
+        .product-item {
+          flex: 0 0 calc( 25% - #{$gap} );
+          box-sizing: border-box;
+        }
+      }
+      .q-table__bottom {
+        .q-table__control {
+          display: none;
+        }
+      }
+    }
+  }
+  .ContentCategory-info-wrapper {
+    background: white;
+    width: 100%;
+    .ContentCategory-info {
+      background: #F7E9F0;
+      padding: 24px;
+      border: 1px solid #DFE1EC;
+      display: flex;
+      justify-content: flex-start;
+      .ContentCategory-thumbnail {
+        width: 189px;
+        border-radius: 8px;
+        margin-right: 24px;
+        overflow: hidden;
+      }
+      .ContentCategory-meta {
+        width: calc( 100% - 213px );
+        .q-banner {
+          margin-bottom: 21px;
+          border-color: $primary;
+        }
+        .ContentCategory-desc {
+
+        }
+      }
+    }
+  }
+}
+</style>

+ 89 - 0
src/components/Widgets/ContentCategory/ContentCategoryShow/ContentItem.vue

@@ -0,0 +1,89 @@
+<template>
+  <div class="col-md-4 col-sm-6 col-12">
+    <q-card flat
+            class="ContentItem">
+      <div class="thumbnail">
+        <router-link :to="{name: 'Public.Content.Show', params: {id: content.id}}">
+          <q-img :src="content.thumbnail" />
+        </router-link>
+      </div>
+      <div class="title ellipsis-2-lines">
+        <router-link :to="{name: 'Public.Content.Show', params: {id: content.id}}">
+          {{content.title}}
+        </router-link>
+      </div>
+      <div class="child-category ellipsis-2-lines">
+        <router-link :to="{name: 'Public.ContentCategory.Show', params: {category_id: content.category }}">
+          <q-icon name="account_tree" />
+          دسته:
+          {{content.category_info?.title}}
+        </router-link>
+      </div>
+      <q-card-actions class="action-section">
+        <q-btn color="primary"
+               class="btn-show-content"
+               :to="{name: 'Public.Content.Show', params: {id: content.id}}">
+          مشاهده جزئیات
+        </q-btn>
+      </q-card-actions>
+    </q-card>
+  </div>
+</template>
+
+<script>
+import { defineComponent } from 'vue'
+import { Content } from 'src/models/Content.js'
+
+export default defineComponent({
+  name: 'ContentItem',
+  props: {
+    content: {
+      type: Content,
+      default: new Content()
+    }
+  }
+})
+</script>
+
+<style lang="scss" scoped>
+.ContentItem {
+  background-color: #f6f6f6;
+  border-radius: 54px 0 54px 54px;
+  padding: 20px 20px 30px;
+  margin: 8px;
+  .thumbnail {
+    margin-bottom: 43px;
+    .q-img {
+      border-radius: 54px 0 54px 54px;
+    }
+  }
+  .title {
+    font-size: 17px;
+    letter-spacing: 0;
+    padding-right: 26px;
+    padding-left: 26px;
+    margin-bottom: 16px;
+    a {
+      color: #475F4A;
+    }
+  }
+  .child-category {
+    font-size: 17px;
+    letter-spacing: 0;
+    padding-right: 26px;
+    padding-left: 26px;
+    margin-bottom: 31px;
+    a {
+      color: #475F4A;
+    }
+  }
+  .action-section {
+    .btn-show-content {
+      width: 100%;
+      height: 48px;
+      max-width: 252px;
+      margin: auto;
+    }
+  }
+}
+</style>

+ 49 - 0
src/components/Widgets/ContentCategory/ContentCategoryShow/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>

+ 1 - 0
src/components/Widgets/Store/Shop/components/productItem.vue

@@ -225,6 +225,7 @@ export default {
   }
   &:hover {
     .product-item__badges {
+      z-index: 1;
       .product-item__badge {
         .product-item__badge-title {
           display: block;

+ 1 - 0
src/models/ContentCategory.js

@@ -6,6 +6,7 @@ class ContentCategory extends Model {
       { key: 'id' },
       { key: 'title' },
       { key: 'thumbnail' },
+      { key: 'description' },
       { key: 'parent' },
       { key: 'parent_info' },
       {