ali 2 anni fa
parent
commit
006dea05d4

+ 2 - 1
src/api/models/PageSetting.js

@@ -63,7 +63,8 @@ export default class PageSettingAPI extends APIRepository {
   }
 
   update (data) {
-    const settingKey = encodeURI(data.key)
+    // const settingKey = encodeURI(data.key)
+    const settingKey = data.key
     const settingValue = data.value
     return this.sendRequest({
       apiMethod: 'post',

+ 20 - 0
src/components/Widgets/Banner/Banner.vue

@@ -0,0 +1,20 @@
+<template>
+  <q-banner :style="localOptions.style"
+            v-html="localOptions.text" />
+</template>
+
+<script>
+import { mixinWidget, mixinPrefetchServerData } from 'src/mixin/Mixins.js'
+
+export default {
+  name: 'Banner',
+  mixins: [mixinPrefetchServerData, mixinWidget],
+  data() {
+    return {
+      defaultOptions: {
+        text: null
+      }
+    }
+  }
+}
+</script>

+ 40 - 0
src/components/Widgets/Banner/OptionPanel.vue

@@ -0,0 +1,40 @@
+<template>
+  <option-panel-tabs v-model:options="localOptions">
+    <template #main-tab>
+      <div class="option-panel-container">
+        <div class="input-container q-py-md">
+          <div class="outsideLabel">text</div>
+          <editor v-model:value="localOptions.text" />
+        </div>
+      </div>
+    </template>
+  </option-panel-tabs>
+</template>
+<script>
+import { defineComponent } from 'vue'
+import Editor from 'src/components/Utils/Editor.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, Editor },
+  mixins: [mixinOptionPanel],
+  props: {
+    options: {
+      type: Object,
+      default() {
+        return {}
+      }
+    }
+  },
+  watch: {
+    localOptions: {
+      handler(newVal) {
+        this.$emit('update:options', newVal)
+      },
+      deep: true
+    }
+  }
+})
+</script>

+ 113 - 43
src/components/Widgets/FeatureBox/FeatureBox.vue

@@ -1,18 +1,25 @@
 <template>
-  <q-card class="feature-card">
-    <q-card-section class="feature-horizontal-section"
-                    :class="localOptions.className"
-                    :horizontal="localOptions.horizontal">
-      <img class="feature-image"
-           :src="localOptions.image">
-      <q-card-section>
-        <div class="feature-title ellipsis">{{ localOptions.title }}</div>
-        <div class="feature-description">
-          {{ localOptions.description }}
-        </div>
-      </q-card-section>
-    </q-card-section>
-  </q-card>
+  <div class="feature-card"
+       :style="localOptions.style">
+    <img class="feature-image"
+         :class="{'has-url': localOptions.url}"
+         :src="localOptions.image"
+         @click="goToUrl">
+    <div class="feature-meta">
+      <div class="feature-title ellipsis-2-lines">
+        <template v-if="localOptions.url">
+          <a :href="localOptions.url">
+            {{ localOptions.title }}
+          </a>
+        </template>
+        <template v-else>
+          {{ localOptions.title }}
+        </template>
+      </div>
+      <div class="feature-description"
+           v-html="localOptions.description" />
+    </div>
+  </div>
 </template>
 
 <script>
@@ -27,49 +34,112 @@ export default {
         title: null,
         description: null,
         image: null,
-        horizontal: true
+        horizontal: true,
+        cssHoverEffects: {
+          boxShadows: [],
+          borderStyle: {
+            borderCssString: '',
+            borderRadiusCssString: ''
+          },
+          transition: {
+            time: 0
+          },
+          transform: {
+            rotate: 0,
+            scaleX: 1,
+            scaleY: 1,
+            skewX: 0,
+            skewY: 0,
+            translateX: 0,
+            translateY: 0
+          }
+        }
       }
     }
+  },
+  computed: {
+    hoverShadows () {
+      const shadows = []
+      if (!Array.isArray(this.localOptions.cssHoverEffects?.boxShadows)) {
+        return ''
+      }
+      this.localOptions.cssHoverEffects.boxShadows.forEach(shadow => {
+        shadows.push(shadow.cssString)
+      })
+
+      return shadows.join(', ')
+    },
+    cssHoverEffectsBorderStyle () {
+      const borderCssString = this.localOptions.cssHoverEffects?.borderStyle?.borderCssString ? this.localOptions.cssHoverEffects?.borderStyle?.borderCssString : ''
+      const borderRadiusCssString = this.localOptions.cssHoverEffects?.borderStyle?.borderRadiusCssString ? this.localOptions.cssHoverEffects?.borderStyle?.borderRadiusCssString : ''
+
+      return {
+        borderCssString,
+        borderRadiusCssString
+      }
+    }
+  },
+  methods: {
+    goToUrl () {
+      window.location.href = this.localOptions.url
+    }
   }
 }
 </script>
 
 <style lang="scss" scoped>
-.feature-card {
-  width: 100%;
-  max-width: 100%;
-  box-shadow: none;
-  .feature-horizontal-section {
+$hoverShadows: v-bind('hoverShadows');
+$hoverBorder: v-bind('cssHoverEffectsBorderStyle.borderCssString');
+$hoverBorderRadius: v-bind('cssHoverEffectsBorderStyle.borderRadiusCssString');
+$skewX: v-bind('localOptions.cssHoverEffects.transform.skewX');
+$skewY: v-bind('localOptions.cssHoverEffects.transform.skewY');
+$rotate: v-bind('localOptions.cssHoverEffects.transform.rotate');
+$scaleX: v-bind('localOptions.cssHoverEffects.transform.scaleX');
+$scaleY: v-bind('localOptions.cssHoverEffects.transform.scaleY');
+$translateX: v-bind('localOptions.cssHoverEffects.transform.translateX');
+$translateY: v-bind('localOptions.cssHoverEffects.transform.translateY');
+$transitionTime: v-bind('localOptions.cssHoverEffects.transition.time');
 
-    .feature-image {
-      width: 100px;
-      height: 100px;
+.feature-card {
+  display: flex;
+  align-items: flex-start;
+  justify-content: flex-start;
+  gap: 33px;
+  .feature-image {
+    width: 215px;
+    height: auto;
+    border-radius: 20px;
+    &.has-url {
+      cursor: pointer;
     }
-
+  }
+  .feature-meta {
     .feature-title {
-      font-weight: 700;
+      font-size: 18px;
+      letter-spacing: 0;
+      color: #212121;
+    }
+    .feature-description {
       font-size: 20px;
-      line-height: 35px;
-
-      @media screen and (max-width: 600px) {
-        font-weight: 600;
-        font-size: 18px;
-        line-height: 26px;
-      }
+      letter-spacing: 0;
+      color: #707070;
     }
+  }
 
-    .feature-description {
-      max-width: 300px;
-      font-weight: 400;
-      font-size: 18px;
-      line-height: 31px;
+  &:hover {
+    transform: rotate(calc(#{$rotate} * 1deg)) translate(calc(#{$translateX} * 1px), calc(#{$translateY} * 1px)) scale($scaleX, $scaleY) skew(calc(#{$skewX} * 1deg), calc(#{$skewY} * 1deg));
+    transition: all calc(#{$transitionTime} * 1s);
+    box-shadow: $hoverShadows;
+    -webkit-box-shadow: $hoverShadows;
+    -moz-box-shadow: $hoverShadows;
+    border-radius: $hoverBorderRadius;
+    -webkit-border-radius: $hoverBorderRadius;
+    -moz-border-radius: $hoverBorderRadius;
+    border: $hoverBorder;
+  }
 
-      @media screen and (max-width: 600px) {
-        font-weight: 400;
-        font-size: 14px;
-        line-height: 22px;
-      }
-    }
+  @media screen and (max-width: 800px) {
+    flex-flow: column;
   }
 }
 </style>

+ 42 - 7
src/components/Widgets/FeatureBox/OptionPanel.vue

@@ -1,21 +1,25 @@
 <template>
-  <option-panel-tabs v-model:options="localOptions">
+  <option-panel-tabs v-model:options="localOptions"
+                     :show-hover-effects-tab="true">
     <template #main-tab>
       <div class="option-panel-container">
         <div class="input-container q-py-md">
           <div class="outsideLabel">image</div>
-          <q-input v-model="localOptions.image"
-                   label="image" />
+          <image-uploader v-model:file="localOptions.image" />
         </div>
         <div class="input-container q-py-md">
           <div class="outsideLabel">title</div>
           <q-input v-model="localOptions.title"
-                   label="image" />
+                   label="title" />
+        </div>
+        <div class="input-container q-py-md">
+          <div class="outsideLabel">url</div>
+          <q-input v-model="localOptions.url"
+                   label="url" />
         </div>
         <div class="input-container q-py-md">
           <div class="outsideLabel">description</div>
-          <q-input v-model="localOptions.description"
-                   label="image" />
+          <editor v-model:value="localOptions.description" />
         </div>
       </div>
     </template>
@@ -23,12 +27,14 @@
 </template>
 <script>
 import { defineComponent } from 'vue'
+import Editor from 'src/components/Utils/Editor.vue'
 import { mixinOptionPanel } from 'quasar-ui-q-page-builder'
+import ImageUploader from 'src/components/ImageUploader.vue'
 import OptionPanelTabs from 'quasar-ui-q-page-builder/src/components/OptionPanelComponents/OptionPanelTabs.vue'
 
 export default defineComponent({
   name: 'OptionPanel',
-  components: { OptionPanelTabs },
+  components: { OptionPanelTabs, ImageUploader, Editor },
   mixins: [mixinOptionPanel],
   props: {
     options: {
@@ -38,6 +44,35 @@ export default defineComponent({
       }
     }
   },
+  data() {
+    return {
+      defaultOptions: {
+        title: null,
+        description: null,
+        image: null,
+        horizontal: true,
+        cssHoverEffects: {
+          boxShadows: [],
+          borderStyle: {
+            borderCssString: '',
+            borderRadiusCssString: ''
+          },
+          transition: {
+            time: 0
+          },
+          transform: {
+            rotate: 0,
+            scaleX: 1,
+            scaleY: 1,
+            skewX: 0,
+            skewY: 0,
+            translateX: 0,
+            translateY: 0
+          }
+        }
+      }
+    }
+  },
   watch: {
     localOptions: {
       handler(newVal) {

+ 2 - 1
src/components/Widgets/TextWidget/OptionPanel.vue

@@ -1,5 +1,6 @@
 <template>
-  <option-panel-tabs v-model:options="localOptions">
+  <option-panel-tabs v-model:options="localOptions"
+                     :show-border-style-tab="true">
     <template #main-tab>
       <div class="option-panel-container">
         <div class="row q-mt-sm">

+ 42 - 30
src/components/Widgets/TextWidget/TextWidget.vue

@@ -50,6 +50,10 @@ export default {
           fontWeight: null,
           fontStyle: null,
           lineHeight: null
+        },
+        borderStyle: {
+          borderCssString: '',
+          borderRadiusCssString: ''
         }
       }
     }
@@ -58,40 +62,48 @@ export default {
 </script>
 
 <style scoped lang="scss">
-.text {
-  line-height: v-bind('localOptions.xl.lineHeight');
-  color: v-bind('localOptions.color');
-  font-size: v-bind('localOptions.xl.fontSize');
-  font-weight: v-bind('localOptions.xl.fontWeight');
-  font-family: v-bind('localOptions.fontFamily');
-  font-style: v-bind('localOptions.xl.fontStyle');
+$border: v-bind('localOptions.borderStyle.borderCssString');
+$borderRadius: v-bind('localOptions.borderStyle.borderRadiusCssString');
 
-  @media screen and (max-width: 1920px) {
-    font-size: v-bind('localOptions.lg.fontSize');
-    font-weight: v-bind('localOptions.lg.fontWeight');
-    font-style: v-bind('localOptions.lg.fontStyle');
-    line-height: v-bind('localOptions.lg.lineHeight');
-  }
+.text-widget-container {
+  -webkit-border-radius: $borderRadius;
+  -moz-border-radius: $borderRadius;
+  border: $border;
+  .text {
+    line-height: v-bind('localOptions.xl.lineHeight');
+    color: v-bind('localOptions.color');
+    font-size: v-bind('localOptions.xl.fontSize');
+    font-weight: v-bind('localOptions.xl.fontWeight');
+    font-family: v-bind('localOptions.fontFamily');
+    font-style: v-bind('localOptions.xl.fontStyle');
 
-  @media screen and (max-width: 1440px) {
-    font-size: v-bind('localOptions.md.fontSize');
-    font-weight: v-bind('localOptions.md.fontWeight');
-    font-style: v-bind('localOptions.md.fontStyle');
-    line-height: v-bind('localOptions.md.lineHeight');
-  }
+    @media screen and (max-width: 1920px) {
+      font-size: v-bind('localOptions.lg.fontSize');
+      font-weight: v-bind('localOptions.lg.fontWeight');
+      font-style: v-bind('localOptions.lg.fontStyle');
+      line-height: v-bind('localOptions.lg.lineHeight');
+    }
 
-  @media screen and (max-width: 1024px) {
-    font-size: v-bind('localOptions.sm.fontSize');
-    font-weight: v-bind('localOptions.sm.fontWeight');
-    font-style: v-bind('localOptions.sm.fontStyle');
-    line-height: v-bind('localOptions.sm.lineHeight');
-  }
+    @media screen and (max-width: 1440px) {
+      font-size: v-bind('localOptions.md.fontSize');
+      font-weight: v-bind('localOptions.md.fontWeight');
+      font-style: v-bind('localOptions.md.fontStyle');
+      line-height: v-bind('localOptions.md.lineHeight');
+    }
+
+    @media screen and (max-width: 1024px) {
+      font-size: v-bind('localOptions.sm.fontSize');
+      font-weight: v-bind('localOptions.sm.fontWeight');
+      font-style: v-bind('localOptions.sm.fontStyle');
+      line-height: v-bind('localOptions.sm.lineHeight');
+    }
 
-  @media screen and (max-width: 600px) {
-    font-size: v-bind('localOptions.xs.fontSize');
-    font-weight: v-bind('localOptions.xs.fontWeight');
-    font-style: v-bind('localOptions.xs.fontStyle');
-    line-height: v-bind('localOptions.xs.lineHeight');
+    @media screen and (max-width: 600px) {
+      font-size: v-bind('localOptions.xs.fontSize');
+      font-weight: v-bind('localOptions.xs.fontWeight');
+      font-style: v-bind('localOptions.xs.fontStyle');
+      line-height: v-bind('localOptions.xs.lineHeight');
+    }
   }
 }
 </style>

+ 19 - 4
src/router/routes/Public.js

@@ -175,12 +175,27 @@ export default {
     },
     {
       name: 'Public.Professor',
-      path: 'professor/:professor_name',
+      path: 'professor',
       meta: {
-        hasDynamicSetting: true,
-        hasDynamicSettingWithParams: true
+        hasDynamicSetting: true
       },
-      component: () => import('src/pages/Public/professorLanding.vue')
+      component: () => import('layouts/bareLayout.vue'),
+      children: [
+        {
+          path: '',
+          name: 'Public.Professor.List',
+          component: () => import('src/pages/BasicDynamicPage.vue')
+        },
+        {
+          path: ':professor_name',
+          name: 'Public.Professor.Show',
+          meta: {
+            hasDynamicSetting: true,
+            hasDynamicSettingWithParams: true
+          },
+          component: () => import('src/pages/Public/professorLanding.vue')
+        }
+      ]
     },
     {
       name: 'Public.Rules',