Parcourir la source

nuxt-link 사용법 + tiptap editor onupdate 메서드 사용법 + v-html 사용법

김보경 il y a 5 ans
Parent
commit
16c89db6a4

+ 1 - 1
components/CourseCard.vue

@@ -22,7 +22,7 @@
       </div>
     </div>
     <footer class="card-footer">
-      <nuxt-link :to="''" class="card-footer-item">Learn More</nuxt-link>
+      <nuxt-link :to="`/courses/${course.slug}`" class="card-footer-item">Learn More</nuxt-link>
       <a target="_" :href="course.productLink" class="card-footer-item">Enroll</a>
     </footer>
   </div>

+ 10 - 1
components/editor/CourseEditor.vue

@@ -44,12 +44,21 @@ export default {
         new OrderedList(),
         new BulletList(),
         new ListItem()
-      ]
+      ],
+      onUpdate: () => {
+        this.emitUpdate()
+      }
     })
     this.initialContent && this.editor.setContent(this.initialContent)
   },
   beforeDestroy() {
     this.editor && this.editor.destroy()
+  },
+  methods: {
+    emitUpdate() {
+      const content = this.editor.getHTML()
+      this.$emit('editorUpdated', content)
+    }
   }
 }
 </script>

+ 4 - 4
components/editor/CourseMenu.vue

@@ -6,25 +6,25 @@
       <button
         class="menubar__button"
         :class="{ 'is-active': isActive.bold() }"
-        @click="commands.bold()">
+        @click.prevent="commands.bold()">
         <icon name="bold" size="small" />
       </button>
       <button
         class="menubar__button"
         :class="{ 'is-active': isActive.italic() }"
-        @click="commands.italic()">
+        @click.prevent="commands.italic()">
         <icon name="italic" size="small" />
       </button>
       <button
         class="menubar__button"
         :class="{ 'is-active': isActive.bullet_list() }"
-        @click="commands.bullet_list">
+        @click.prevent="commands.bullet_list()">
         <icon name="list-ul" size="large" />
       </button>
       <button
         class="menubar__button"
         :class="{ 'is-active': isActive.ordered_list() }"
-        @click="commands.ordered_list">
+        @click.prevent="commands.ordered_list()">
         <icon name="list-ol" size="large" />
       </button>
     </div>

+ 7 - 2
components/instructor/LandingPage.vue

@@ -37,7 +37,10 @@
               type="text"
               placeholder="Write something catchy about the course">
             </textarea> -->
-            <CourseEditor :initialContent="course.description"/>
+            <CourseEditor
+              :initialContent="course.description"
+              @editorUpdated="(content) => emitCourseValue(content, 'description')"
+            />
           </div>
         </div>
         <div class="field">
@@ -123,7 +126,9 @@ export default {
   },
   methods : {
     emitCourseValue(e, field){
-      const {value} = e.target
+      // const {value} = e.target
+      const value = e.target ? e.target.value : e
+
       if(field === 'category'){
         return this.emitCategory(value, field)
       }

+ 3 - 3
package-lock.json

@@ -7671,9 +7671,9 @@
       }
     },
     "portal-vue": {
-      "version": "2.1.5",
-      "resolved": "https://registry.npmjs.org/portal-vue/-/portal-vue-2.1.5.tgz",
-      "integrity": "sha512-vZmdMn0mOo7puvxoMQ5zju6S29aFD+9yygJxyWQtPaMXS9xunAeoYdnx6yzfL9J8HD8pMZYgSieEIbioAKhrSQ=="
+      "version": "2.1.6",
+      "resolved": "https://registry.npmjs.org/portal-vue/-/portal-vue-2.1.6.tgz",
+      "integrity": "sha512-lvCF85D4e8whd0nN32D8FqKwwkk7nYUI3Ku8UAEx4Z1reomu75dv5evRUTZNaj1EalxxWNXiNl0EHRq36fG8WA=="
     },
     "posix-character-classes": {
       "version": "0.1.1",

+ 5 - 5
package.json

@@ -11,27 +11,27 @@
     "generate": "nuxt generate"
   },
   "dependencies": {
-    "cross-env": "^5.2.0",
-    "express": "^4.16.4",
     "@nuxtjs/axios": "^5.3.6",
-    "nuxt": "^2.0.0",
     "async": "^3.0.1",
     "async-lock": "^1.2.0",
     "bcrypt": "^3.0.6",
     "body-parser": "^1.19.0",
     "bulma": "^0.7.5",
     "connect-mongodb-session": "^2.2.0",
+    "cross-env": "^5.2.0",
+    "express": "^4.16.4",
     "express-session": "^1.16.2",
     "highlight.js": "^9.15.8",
     "jsonwebtoken": "^8.5.1",
     "moment": "^2.24.0",
     "mongoose": "^5.6.0",
+    "nuxt": "^2.0.0",
     "passport": "^0.4.0",
     "passport-local": "^1.0.0",
-    "portal-vue": "^2.1.5",
+    "portal-vue": "^2.1.6",
     "slugify": "^1.3.4",
     "tiptap": "^1.23.0",
-    "tiptap-extensions": "^1.24.0",
+    "tiptap-extensions": "^1.26.1",
     "vue-toasted": "^1.1.27",
     "vuejs-paginate": "^2.1.0",
     "vuelidate": "^0.7.4"

+ 102 - 0
pages/courses/_slug.vue

@@ -0,0 +1,102 @@
+<template>
+  <div>
+    <div class="container">
+      <div class="columns">
+        <div class="column is-9">
+          <div class="section">
+            <div class="what-you-get">
+              <div class="what-you-get-title">
+                What you will learn
+              </div>
+              <ul class="what-you-get-items">
+                <!-- TODO: Iterate course wsl -->
+                <li
+                   v-for="wsl in course.wsl"
+                   :key="wsl.value"
+                   class="what-you-get-item">
+                  <span>{{wsl.value}}</span>
+                </li>
+              </ul>
+            </div>
+          </div>
+          <div class="section course-description p-t-none">
+            <div class="course-description-title">Course Info</div>
+            <div class="course-description-details">
+              <!-- TODO: use v-html for description -->
+              <div v-html="course.description"></div>
+            </div>
+          </div>
+        </div>
+      </div>
+    </div>
+  </div>
+</template>
+<script>
+export default {
+  computed: {
+    course() {
+      return this.$store.state.course.item
+    }
+  },
+  async fetch({store, params}) {
+    await store.dispatch('course/fetchCourseBySlug', params.slug)
+  }
+}
+</script>
+
+<!-- Fetch course by Slug -->
+<!-- 1. create action "fetchCourseBySlug" in store/course.js -->
+<!-- 2. send GET request '/api/v1/products/s/:slug' -->
+<!-- 3. expect to receive "course" in "then" and commit it to state -->
+<!-- 4. get course in computed properties -->
+<!-- 5. Complete TODO's -->
+<!-- 6. Navigate to detail page from home page when clicking on "Learn More" -->
+
+<style lang="scss">
+  .what-you-get {
+    background-color: #f9f9f9;
+    border: 1px solid #dedfe0;
+    padding: 10px 15px;
+    &-title {
+      font-size: 26px;
+      font-weight: bold;
+      margin-bottom: 10px;
+    }
+    &-items {
+      display: flex;
+      align-items: flex-start;
+      justify-content: space-between;
+      flex-wrap: wrap;
+    }
+    &-item {
+      display: flex;
+      align-items: center;
+      margin-bottom: 10px;
+      font-size: 17px;
+      width: 45%;
+    }
+  }
+  .course-description {
+    &-title {
+      font-size: 26px;
+      font-weight: bold;
+      margin-bottom: 10px;
+    }
+    &-details {
+      font-size: 18px;
+      ul {
+        list-style: disc;
+        margin-left: 20px;
+      }
+      ol {
+        margin-left: 20px;
+      }
+      strong {
+        font-size: 20px;
+      }
+      p {
+        min-height: 30px;
+      }
+    }
+  }
+</style>

+ 17 - 1
store/course.js

@@ -1,5 +1,6 @@
 export const state = () => ({
-  items : []
+  items: [],
+  item: {}
 })
 
 export const actions = {
@@ -10,5 +11,20 @@ export const actions = {
     commit('setItems', {resource: 'course', items: courses}, {root: true})
     console.log('store/course.js actions > fetchCourses > await this.$axios.$get > commit')
     return state.items
+  },
+  async fetchCourseBySlug({commit, state}, courseSlug) {
+    const course = await this.$axios.$get(`/api/v1/products/s/${courseSlug}`)
+    if(course.isAxiosError === true){
+      console.log(course.data)
+      return Error('')
+    }
+    commit('setCourse', course)
+    return state.course
+  }
+}
+
+export const mutations = {
+  setCourse(state, course) {
+    state.item = course
   }
 }