2 次代碼提交 72047a0be6 ... a974e66d82

作者 SHA1 備註 提交日期
  kiboky a974e66d82 버그 수정 - 몽고 가상 컬럼 추가 5 年之前
  kiboky 62788060bb review rating 컴포넌트 추가 5 年之前

+ 352 - 0
client/components/ReviewSection.vue

@@ -0,0 +1,352 @@
+<template>
+  <div>
+    <hr />
+    <div class="reviewsMedley">
+      <div class="row">
+        <div class="col-lg-4 col-md-5 col-sm-12">
+          <!-- Total Customer reviews -->
+          <a href="#" class="a-color-base">
+            <h2>{{ product.reviews.length }} customer reviews</h2>
+          </a>
+          <div class="cr-widget-ACR">
+            <i class="fas fa-star"></i>
+            <i class="fas fa-star"></i>
+            <i class="fas fa-star"></i>
+            <i class="fas fa-star"></i>
+            <i class="fas fa-star-half-alt"></i>
+            <span>
+              <a href="#">
+                4.5 out of 5 stars
+                <i class="a-icon a-icon-popover"></i>
+              </a>
+            </span>
+          </div>
+          <div class="cr-widget-histogram">
+            <div class="row a-histogrm-row">
+              <div class="col-md-2 col-sm-2 col-2 pr-0">
+                <div class="aok-nowrap">
+                  <a href="#">5 star</a>
+                  <span class="a-letter-space"></span>
+                </div>
+              </div>
+              <div class="col-md-8 col-sm-8 col-8 pr-0">
+                <div>
+                  <a href="#">
+                    <div class="a-meter 5star">
+                      <div class="a-meter-bar a-meter-filled" style="width: 64%;"></div>
+                    </div>
+                  </a>
+                </div>
+              </div>
+              <div class="col-md-2 col-sm-2 col-2 pr-0">
+                <div class="aok-nowrap text-right">
+                  <a href="#">64%</a>
+                  <span class="a-letter-space"></span>
+                </div>
+              </div>
+            </div>
+            <div class="row a-histogrm-row" style="margin-top: -15px;">
+              <div class="col-md-2 col-sm-2 col-2 pr-0">
+                <div class="aok-nowrap">
+                  <a href="#">4 star</a>
+                  <span class="a-letter-space"></span>
+                </div>
+              </div>
+              <div class="col-md-8 col-sm-8 col-8 pr-0">
+                <div>
+                  <a href="#">
+                    <div class="a-meter 5star">
+                      <div class="a-meter-bar a-meter-filled" style="width: 14%;"></div>
+                    </div>
+                  </a>
+                </div>
+              </div>
+              <div class="col-md-2 col-sm-2 col-2 pr-0">
+                <div class="aok-nowrap text-right">
+                  <a href="#">14%</a>
+                  <span class="a-letter-space"></span>
+                </div>
+              </div>
+            </div>
+            <div class="row a-histogrm-row" style="margin-top: -15px;">
+              <div class="col-md-2 col-sm-2 col-2 pr-0">
+                <div class="aok-nowrap">
+                  <a href="#">3 star</a>
+                  <span class="a-letter-space"></span>
+                </div>
+              </div>
+              <div class="col-md-8 col-sm-8 col-8 pr-0">
+                <div>
+                  <a href="#">
+                    <div class="a-meter 5star">
+                      <div class="a-meter-bar a-meter-filled" style="width: 4%;"></div>
+                    </div>
+                  </a>
+                </div>
+              </div>
+              <div class="col-md-2 col-sm-2 col-2 pr-0">
+                <div class="aok-nowrap text-right">
+                  <a href="#">4%</a>
+                  <span class="a-letter-space"></span>
+                </div>
+              </div>
+            </div>
+            <div class="row a-histogrm-row" style="margin-top: -15px;">
+              <div class="col-md-2 col-sm-2 col-2 pr-0">
+                <div class="aok-nowrap">
+                  <a href="#">2 star</a>
+                  <span class="a-letter-space"></span>
+                </div>
+              </div>
+              <div class="col-md-8 col-sm-8 col-8 pr-0">
+                <div>
+                  <a href="#">
+                    <div class="a-meter 5star">
+                      <div class="a-meter-bar a-meter-filled" style="width: 0%;"></div>
+                    </div>
+                  </a>
+                </div>
+              </div>
+              <div class="col-md-2 col-sm-2 col-2 pr-0">
+                <div class="aok-nowrap text-right">
+                  <a href="#">0%</a>
+                  <span class="a-letter-space"></span>
+                </div>
+              </div>
+            </div>
+            <div class="row a-histogrm-row" style="margin-top: -15px;">
+              <div class="col-md-2 col-sm-2 col-2 pr-0">
+                <div class="aok-nowrap">
+                  <a href="#">1 star</a>
+                  <span class="a-letter-space"></span>
+                </div>
+              </div>
+              <div class="col-md-8 col-sm-8 col-8 pr-0">
+                <div>
+                  <a href="#">
+                    <div class="a-meter 5star">
+                      <div class="a-meter-bar a-meter-filled" style="width: 4%;"></div>
+                    </div>
+                  </a>
+                </div>
+              </div>
+              <div class="col-md-2 col-sm-2 col-2 pr-0">
+                <div class="aok-nowrap text-right">
+                  <a href="#">4%</a>
+                  <span class="a-letter-space"></span>
+                </div>
+              </div>
+            </div>
+          </div>
+          <div class="clearfix">
+            <div class="float-left">
+              <hr class="a-spacing-large" />
+            </div>
+          </div>
+          <h3 class="a-spacing-micro">Review this product</h3>
+          <div class="a-row a-spacing-large">Share your thoughts with other customers</div>
+          <div class="a-row">
+            <!-- Link to another Review page -->
+            <span class="a-button-base writeReviewButton cm-cr-button-wide">
+              <span class="a-button-inner">
+                <nuxt-link
+                  :to="`/reviews/${product._id}`"
+                  class="a-button-text"
+                >Write a customer review</nuxt-link>
+              </span>
+            </span>
+          </div>
+          <div class="clearfix">
+            <div class="float-left">
+              <hr class="mt-4 a-spacing-large" />
+            </div>
+          </div>
+        </div>
+        <div class="col-lg-6 col-md-7 col-sm-12">
+          <div class="reviews-image-gallery">
+            <h3>Customer images</h3>
+            <!-- Review Images -->
+            <div class="a-spacing-small a-spacing-top-small">
+              <img
+                v-for="review in reviews"
+                :key="review._id"
+                :src="review.photo"
+                class="img-fluid"
+                width="22.5%" />
+            </div>
+            <div>
+              <a href="#">See all customer images</a>
+            </div>
+          </div>
+          <div class="cr-lighthut">
+            <h3>Read reviews that mention</h3>
+            <div class="cr-lighthouse-terms">
+              <span class="cr-lighthouse-span">
+                <a href="#">
+                  <span class="cr-lighthouse-term">jeff bezos</span>
+                </a>
+              </span>
+              <span class="cr-lighthouse-span">
+                <a href="#">
+                  <span class="cr-lighthouse-term">everything store</span>
+                </a>
+              </span>
+              <span class="cr-lighthouse-span">
+                <a href="#">
+                  <span class="cr-lighthouse-term">brad stone</span>
+                </a>
+              </span>
+              <br />
+              <span class="cr-lighthouse-span">
+                <a href="#">
+                  <span class="cr-lighthouse-term">steve jobs</span>
+                </a>
+              </span>
+              <span class="cr-lighthouse-span">
+                <a href="#">
+                  <span class="cr-lighthouse-term">well written</span>
+                </a>
+              </span>
+              <span class="cr-lighthouse-span">
+                <a href="#">
+                  <span class="cr-lighthouse-term">great read</span>
+                </a>
+              </span>
+              <br />
+              <span class="cr-lighthouse-span">
+                <a href="#">
+                  <span class="cr-lighthouse-term">must read</span>
+                </a>
+              </span>
+              <span class="cr-lighthouse-span">
+                <a href="#">
+                  <span class="cr-lighthouse-term">great book</span>
+                </a>
+              </span>
+              <span class="cr-lighthouse-span">
+                <a href="#">
+                  <span class="cr-lighthouse-term">highly recomended</span>
+                </a>
+              </span>
+              <br />
+              <span class="cr-lighthouse-span">
+                <a href="#">
+                  <span class="cr-lighthouse-term">well searched</span>
+                </a>
+              </span>
+              <span class="cr-lighthouse-span">
+                <a href="#">
+                  <span class="cr-lighthouse-term">long term</span>
+                </a>
+              </span>
+              <span class="cr-lighthouse-span">
+                <a href="#">
+                  <span class="cr-lighthouse-term">barnes and noble</span>
+                </a>
+              </span>
+              <span class="cr-lighthouse-span">
+                <a href="#">
+                  <span class="cr-lighthouse-term">sam walton</span>
+                </a>
+              </span>
+            </div>
+          </div>
+          <div class="cr-widget-focalreviews">
+            <div class="card-padding">
+              <div class="review-header">
+                <h3>
+                  <span class="a-size-base">Showing 1-8 of {{ product.reviews.length }} reviews</span>
+                </h3>
+              </div>
+              <div class="review-sort-type">
+                <span class="a-dropdown-container">
+                  <span class="a-button a-button-dropdown">
+                    <span class="a-button-inner">
+                      <span class="a-button-text">
+                        <span class="a-dropdown-prompt">Top Reviews</span>
+                      </span>
+                      <i class="a-icon a-icon-dropdown"></i>
+                    </span>
+                  </span>
+                </span>
+              </div>
+
+              <!-- Reviews -->
+              <div v-for="review in reviews" :key="review._id" class="review-body">
+                <div class="genome-widget">
+                  <a href="#">
+                    <div class="profile-avatar">
+                      <img
+                        src="https://images-na.ssl-images-amazon.com/images/S/amazon-avatars-global/default._CR0,0,1024,1024_SX48_.png"
+                      />
+                    </div>
+                    <!-- Review Owner -->
+                    <div class="profile-content">
+                      <span class="a-profile-name">{{ review.user.name }}</span>
+                    </div>
+                  </a>
+                </div>
+                <div class="a-row">
+                  <!-- Review Star -->
+                  <a href="#">
+                    <i v-for="i in review.rating" :key="i" class="fas fa-star"></i>
+                  </a>
+                  <span class="a-letter-space"></span>
+                  <!-- Review Headline -->
+                  <a href="#" class="review-title">{{ review.headline }}</a>
+                </div>
+                <span class="review-date">June 28, 2016</span>
+                <div class="review-data">
+                  <span class="a-color-secondary">Format: Hardcover</span>
+                  <div
+                    style="width: 1px !important; height: 15px !important; background-color: #ddd; display: inline-block; margin: 0px 5px -3px 5px;"
+                  ></div>
+                  <span class="avp-badge a-color-state">Verified Purchase</span>
+                </div>
+                <!-- Review Body -->
+                <div class="review-body">
+                  <span>{{ review.body }}</span>
+                </div>
+                <div class="review-comments">
+                  <div class="a-spacing-small">
+                    <span class="a-size-base a-color-tertiary">60 people found this helpful</span>
+                  </div>
+                  <div class="cr-helpful-button">
+                    <span class="a-button-base">
+                      <span class="a-button-inner">
+                        <a href="#" class="a-button-text">
+                          <div class="cr-helpful-text">Helpful</div>
+                        </a>
+                      </span>
+                    </span>
+                  </div>
+                </div>
+              </div>
+              <!-- Reviews -->
+            </div>
+          </div>
+          <div class="review-footer">
+            <div class="a-row">
+              <span class="a-button a-button-base writeReviewButton" id="a-autoid-15">
+                <span class="a-button-inner">
+                  <nuxt-link
+                    :to="`/reviews/${product._id}`"
+                    class="a-button-text"
+                    role="button"
+                  >Write a customer review</nuxt-link>
+                </span>
+              </span>
+            </div>
+          </div>
+        </div>
+        <div class="col-lg-2 col-md-0 col-sm-0"></div>
+      </div>
+    </div>
+  </div>
+</template>
+
+<script>
+export default {
+  props: ['product', 'reviews']
+};
+</script>

+ 16 - 3
client/pages/index.vue

@@ -31,14 +31,14 @@
                       <div class="col-sm-9">
                         <div class="a-row a-spacing-small">
                           <!-- Title and date -->
-                          <a href="" class="a-link-normal">
+                          <nuxt-link :to="`/products/${product._id}`" class="a-link-normal">
                             <h2 class="a-size-medium">
                               {{ product.title }}
                               <span class="a-letter-space"></span>
                               <span class="a-letter-space"></span>
                               <span class="a-size-small a-color-secondary">Sep 3, 2019</span>
                             </h2>
-                          </a>
+                          </nuxt-link>
                         </div>
 
                         <!--Author name -->
@@ -93,7 +93,18 @@
                             <div class="col-sm-5">
                               <div class="a-row a-spacing-mini">
                                 <!-- star rating -->
-
+                                <no-ssr>
+                                  <star-rating
+                                    :rating="product.averageRating"
+                                    :show-rating="false"
+                                    :glow="1"
+                                    :border-width="1"
+                                    :rounded-corners="true"
+                                    :read-only="true"
+                                    :star-size="18"
+                                    :star-points="[23,2,14,17,0,19,10,34,7,50,23,43,38,50,36,34,46,19,31,17]"
+                                  ></star-rating>
+                                </no-ssr>
                               </div>
                             </div>
                           </div>
@@ -114,9 +125,11 @@
 </template>
 
 <script>
+import StarRating from 'vue-star-rating'
 import FeatureProduct from '~/components/FeatureProduct'
 export default {
   components: {
+    StarRating,
     FeatureProduct
   },
 

+ 32 - 4
client/pages/products/_id.vue

@@ -92,7 +92,20 @@
                   ></i>
                 </a> (Author)
               </div>
-              <div class="reviewGroup"></div>
+              <div class="reviewGroup">
+                <no-ssr>
+                  <star-rating
+                    :rating="product.averageRating"
+                    :show-rating="false"
+                    :glow="1"
+                    :border-width="1"
+                    :rounded-corners="true"
+                    :read-only="true"
+                    :star-size="18"
+                    :star-points="[23,2,14,17,0,19,10,34,7,50,23,43,38,50,36,34,46,19,31,17]"
+                  ></star-rating>
+                </no-ssr>
+              </div>
               <hr style="margin-top: 10px;" />
               <!-- A tags Dummy Data -->
               <div class="mediaMatrix">
@@ -342,6 +355,9 @@
             </div>
           </div>
         </div>
+
+        <ReviewSection :product="product" :reviews="reviews" />
+
       </div>
     </div>
   </main>
@@ -349,14 +365,26 @@
 
 
 <script>
+import StarRating from 'vue-star-rating'
+import ReviewSection from '~/components/ReviewSection'
+
 export default {
+  components: {
+    StarRating,
+    ReviewSection
+  },
   async asyncData({ $axios, params }) {
     try {
-      let response = await $axios.$get(`/api/products/${params.id}`)
-      console.log(response)
+      let singleProduct = $axios.$get(`/api/products/${params.id}`)
+      let manyReviews = $axios.$get(`/api/reviews/${params.id}`)
+      // console.log(response)
+      const [singleResponse, reviewsResponse] = await Promise.all([
+        singleProduct, manyReviews
+      ])
 
       return {
-        product: response.product
+        product: singleResponse.product,
+        reviews: reviewsResponse.reviews
       }
     } catch (err) {
       console.log(err)

+ 2 - 2
client/pages/reviews/_id.vue

@@ -112,11 +112,11 @@
 </template>
 
 <script>
-import StartRating from 'vue-star-rating'
+import StarRating from 'vue-star-rating'
 
 export default {
   components: {
-    StartRating
+    StarRating
   },
   async asyncData ({ $axios, params }) {
     try {

+ 17 - 1
server/models/product.js

@@ -9,7 +9,23 @@ const ProductSchema = new Schema({
   photo: String,
   price: Number,
   stockQuantity: Number,
-  rating: [{ type:Schema.Types.ObjectId, ref: 'Review' }]
+  reviews: [{ type:Schema.Types.ObjectId, ref: 'Review' }]
+}, {
+  toObject: { virtuals: true },
+  toJSON: { virtuals: true }
+})
+
+ProductSchema.virtual('averageRating').get(function () {
+  if (this.reviews.length > 0) {
+    let sum = this.reviews.reduce((total, review) => {
+      console.log('review', review)
+      return total + review.rating
+    }, 0)
+    
+    return sum / this.reviews.length
+  }
+
+  return 0
 })
 
 module.exports = mongoose.model('Product', ProductSchema)

+ 8 - 2
server/routes/products.js

@@ -29,7 +29,10 @@ router.post('/products', upload.single('photo'), async (req, res)=> {
 
 router.get('/products', async (req, res) => {
   try {
-    let products = await Product.find().populate('owner category').exec()
+    let products = await Product.find()
+      .populate('owner category')
+      .populate('reviews', 'rating')
+      .exec()
     res.json({
       success: true,
       products
@@ -44,7 +47,10 @@ router.get('/products', async (req, res) => {
 
 router.get('/products/:id', async (req, res) => {
   try {
-    let product = await Product.findOne( { _id: req.params.id }).populate('owner category').exec()
+    let product = await Product.findOne( { _id: req.params.id })
+      .populate('owner category')
+      .populate('reviews', 'rating')
+      .exec()
     res.json({
       success: true,
       product

+ 2 - 2
server/routes/review.js

@@ -14,7 +14,7 @@ router.post('/reviews/:productID', [virifyToken, upload.single('photo')], async
     review.productID =  req.params.productID
     review.user =  req.decoded._id
 
-    await Product.update({ $push: review._id })
+    await Product.update({ $push: { reviews: review._id } })
 
     const saveReviews = await review.save()
 
@@ -42,7 +42,7 @@ router.get('/reviews/:productID', async(req, res) => {
     
     res.json({
       success: true,
-      message: productReviews
+      reviews: productReviews
     })
   } catch (err) {
     res.status(500).json({