Kaynağa Gözat

add:新增用户引导

lisiyan 5 ay önce
ebeveyn
işleme
83a4c29e41

+ 5 - 0
package-lock.json

@@ -2724,6 +2724,11 @@
       "integrity": "sha512-YXQl1DSa4/PQyRfgrv6aoNjhasp/p4qs9FjJ4q4cQk+8m4r6k4ZSiEyytKG8f8W9gi8WsQtIObNmKd+tMzNTmA==",
       "dev": true
     },
+    "driver.js": {
+      "version": "1.3.1",
+      "resolved": "https://registry.npmmirror.com/driver.js/-/driver.js-1.3.1.tgz",
+      "integrity": "sha512-MvUdXbqSgEsgS/H9KyWb5Rxy0aE6BhOVT4cssi2x2XjmXea6qQfgdx32XKVLLSqTaIw7q/uxU5Xl3NV7+cN6FQ=="
+    },
     "duplexer": {
       "version": "0.1.2",
       "resolved": "https://registry.npmmirror.com/duplexer/-/duplexer-0.1.2.tgz",

+ 72 - 0
src/assets/scss/common.scss

@@ -217,4 +217,76 @@ input:-ms-input-placeholder {
     padding: 20px 20px 40px;
     margin-bottom: 50px;
   }
+}
+.noviceGuide {
+  font-family: 'Poppins';
+  .driver-popover-title {
+    font-size: 16px;
+    color: #396FFA;
+    font-weight: 600;
+    text-align: left;
+    line-height: 24px;
+    user-select: none;
+    font-family: 'Poppins';
+  }
+  .driver-popover-description {
+    font-size: 14px;
+    line-height: 20px;
+    color: #52555F;
+    text-align: left;
+    font-family: 'Poppins';
+    a {
+      margin: 0 4px;
+      font-family: 'Poppins';
+      text-decoration: underline;
+    }
+  }
+  .driver-popover-prev-btn {
+    display: none !important;
+  }
+  footer.driver-popover-footer {
+    display: flex;
+    justify-content: flex-end;
+    div {
+      display: flex;
+      align-items: center;
+      input {
+        width: 18px;
+        height: 18px;
+        cursor: pointer;
+        margin-right: 8px;
+        border-radius: 4px;
+        border: 2px solid #94969D;
+      }
+      p {
+        font-size: 14px;
+        color: #94969D;
+        line-height: 20px;
+        font-family: 'Poppins';
+      }
+      svg {
+        zoom: 0.74;
+      }
+      .img {
+        display: flex;
+        margin-right: 8px;
+      }
+    }
+    .driver-popover-navigation-btns {
+      display: inline;
+      flex-grow: initial;
+      .driver-popover-next-btn{
+        border: none;
+        display: flex;
+        font-size: 14px;
+        font-weight: 600;
+        margin-left: 8px;
+        color: #396FFA;
+        line-height: 20px;
+        text-shadow: none;
+        background: #EBF1FE;
+        font-family: 'Poppins';
+      }
+    }
+  }
 }

+ 159 - 6
src/components/common/Aside.vue

@@ -9,14 +9,14 @@
             <p>{{ email }}</p>
           </div>
         </div>
-        <el-menu :default-active="path" active-text-color="#1460F3" text-color="#666" router>
+        <el-menu :default-openeds="['1','4']" :default-active="path" active-text-color="#1460F3" text-color="#666" router>
           <el-submenu index="1">
             <template slot="title">
               <Project />
               <span>{{ $t('aside.project') }}</span>
             </template>
-            <el-menu-item index="/dashboard">{{ $t('aside.dashboard') }}</el-menu-item>
-            <el-menu-item :index="'/api/keys'||'/api/keys/new-project'||'api/keys/edit-project'">{{ $t('aside.apiKeys') }}</el-menu-item>
+            <el-menu-item index="/dashboard" class="dashboard">{{ $t('aside.dashboard') }}</el-menu-item>
+            <el-menu-item class="apiKey" :index="'/api/keys'||'/api/keys/new-project'||'api/keys/edit-project'">{{ $t('aside.apiKeys') }}</el-menu-item>
             <el-menu-item index="/user/webhooks">{{ $t('aside.webhook') }}</el-menu-item>
           </el-submenu>
           <el-submenu index="2">
@@ -40,11 +40,21 @@
               <Support />
               <span>{{ $t('aside.support') }}</span>
             </template>
-            <el-menu-item index="">
-              <a :href="apiDomain + '/api/docs/introduction'" class="downloadDoc">
+            <el-menu-item index="" class="introduction">
+              <a :href="apiDomain + '/contact-us'" target="_blank" class="downloadDoc">
+                {{ $t('aside.contact') }}
+              </a>
+            </el-menu-item>
+            <el-menu-item index="" class="introduction">
+              <a :href="apiDomain + '/api/docs/introduction'" target="_blank" class="downloadDoc">
                 {{ $t('aside.document') }}
               </a>
             </el-menu-item>
+            <el-menu-item index="" class="introduction">
+              <a :href="apiDomain + '/api-libraries/overview'" target="_blank" class="downloadDoc">
+                {{ $t('aside.libraries') }}
+              </a>
+            </el-menu-item>
           </el-submenu>
         </el-menu>
       </div>
@@ -81,10 +91,20 @@
       <el-submenu index="4">
         <template slot="title"><span>{{ $t('aside.support') }}</span></template>
         <el-menu-item index="">
-          <a :href="apiDomain + '/api/docs/introduction'" class="downloadDoc">
+          <a :href="apiDomain + '/contact-us'" target="_blank" class="downloadDoc">
+            {{ $t('aside.contact') }}
+          </a>
+        </el-menu-item>
+        <el-menu-item index="">
+          <a :href="apiDomain + '/api/docs/introduction'" target="_blank" class="downloadDoc">
             {{ $t('aside.document') }}
           </a>
         </el-menu-item>
+        <el-menu-item index="">
+          <a :href="apiDomain + '/api-libraries/overview'" target="_blank" class="downloadDoc">
+            {{ $t('aside.libraries') }}
+          </a>
+        </el-menu-item>
       </el-submenu>
       <el-submenu index="5">
         <template slot="title"><span>{{ $i18n.locale === 'en' ? 'English' : '简体中文' }}</span></template>
@@ -103,6 +123,10 @@
 import { Component, Vue } from 'vue-property-decorator'
 import { loginStore } from '@/store/loginStore'
 import i18n from '@/i18n'
+import { driver } from 'driver.js'
+import { neverPrompt } from '@/request/api'
+import 'driver.js/dist/driver.css'
+import Cookie from 'js-cookie'
 import Project from '@/components/icon/menu_project.vue'
 import Settings from '@/components/icon/menu_setting.vue'
 import Billing from '@/components/icon/menu_billing.vue'
@@ -122,11 +146,140 @@ export default class Aside extends Vue {
   // data
   apiDomain = process.env.VUE_APP_API_DOMAIN
   languageActive = false
+  isDisable = false
+
+  handleOpen (key:any, keyPath:any) {
+    console.log(key, keyPath)
+  }
 
   created () {
     window.addEventListener('click', this.handleGlobalClick)
   }
 
+  mounted () {
+    const base = this.$t('guide.twoDesc[0]') as string + '<a target="_blank" href=' + this.apiDomain + '/api-libraries/authentication' + '>' + this.$t('guide.twoDesc[1]') + '</a>'
+    const step = i18n.locale === 'zh-cn' ? base + this.$t('guide.twoDesc[2]') as string : base
+    this.$nextTick(() => {
+      const driverObj = driver({
+        allowClose: false,
+        showProgress: true,
+        popoverClass: 'noviceGuide',
+        disableActiveInteraction: true,
+        nextBtnText: this.$t('guide.next') as string,
+        showButtons: [
+          'next'
+        ],
+        progressText: '{{current}} / {{total}}',
+        steps: [
+          {
+            element: '.apiKey',
+            popover: {
+              title: this.$t('guide.one') as string,
+              description: this.$t('guide.oneDesc') as string,
+              onNextClick: () => {
+                this.$router.push('/api/keys')
+                this.$nextTick(() => {
+                  setTimeout(() => {
+                    driverObj.moveNext()
+                  }, 250)
+                })
+              },
+              side: 'left',
+              align: 'start'
+            }
+          },
+          {
+            element: '.el-table__row',
+            popover: {
+              title: this.$t('guide.two') as string,
+              description: step,
+              side: 'bottom',
+              align: 'center',
+              onNextClick: () => {
+                this.$router.push('/dashboard')
+                this.$nextTick(() => {
+                  setTimeout(() => {
+                    driverObj.moveNext()
+                  }, 250)
+                })
+              }
+            }
+          },
+          {
+            element: '.dashboard',
+            popover:
+              {
+                title: this.$t('guide.three') as string,
+                description: this.$t('guide.threeDesc') as string,
+                side: 'bottom',
+                align: 'start'
+              }
+          },
+          {
+            element: '.introduction',
+            popover: {
+              title: this.$t('guide.four') as string,
+              description: this.$t('guide.fourDesc') as string,
+              side: 'left',
+              align: 'start',
+              onPopoverRender: (popover, { config, state }) => {
+                const footer = document.querySelector('.driver-popover-footer') as HTMLElement | null
+                const step = document.querySelector('.driver-popover-progress-text') as HTMLElement | null
+                const next = document.querySelector('.driver-popover-next-btn') as HTMLElement | null
+                if (step && next) {
+                  next.innerText = this.$t('guide.done') as string
+                  step.style.display = 'none'
+                }
+                const firstCheck = document.createElement('input')
+                const text = document.createElement('p')
+                const div = document.createElement('div')
+                const span = document.createElement('span')
+                text.innerText = this.$t('guide.disable') as string
+                firstCheck.type = 'checkbox'
+                if (footer) {
+                  footer.style.justifyContent = 'space-between'
+                  const referenceNode = footer.firstChild
+                  footer.insertBefore(div, referenceNode)
+                  div.appendChild(span)
+                  div.appendChild(firstCheck)
+                  div.appendChild(text)
+                }
+                span.addEventListener('click', () => {
+                  if (footer && span) {
+                    span.style.display = 'none'
+                    firstCheck.style.display = 'inline'
+                    this.isDisable = false
+                  }
+                })
+                firstCheck.addEventListener('click', () => {
+                  if (footer && span) {
+                    span.className = 'img'
+                    span.style.display = 'flex'
+                    span.innerHTML = '<svg data-v-1e0db733="" width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg" class=""><rect data-v-1e0db733="" x="1" y="1" width="22" height="22" rx="4" fill="#396FFA"></rect><path data-v-1e0db733="" d="M18 9L11.333 15.667L7.09035 11.4244" stroke="white" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"></path></svg>'
+                    firstCheck.style.display = 'none'
+                    this.isDisable = true
+                  }
+                })
+              },
+              onNextClick: async () => {
+                if (this.isDisable) {
+                  await neverPrompt({}, {})
+                }
+                Cookie.set('isDisable', true, { expires: 14, domain: process.env.VUE_APP_SaaS_DOMAIN })
+                driverObj.moveNext()
+              }
+            }
+          }
+        ]
+      })
+      const screenWidth = window.innerWidth || document.documentElement.clientWidth || document.body.clientWidth
+      const isDisable = Cookie.get('isDisable')
+      if (screenWidth > 930 && isDisable === 'false') {
+        driverObj.drive()
+      }
+    })
+  }
+
   beforeDestroy () {
     window.removeEventListener('click', this.handleGlobalClick)
   }

+ 17 - 1
src/locales/en.ts

@@ -15,7 +15,9 @@ export default {
     information: 'Billing Information ',
     invoices: 'Invoices Center',
     support: 'Support',
-    document: 'Documentation '
+    document: 'Documentation ',
+    contact: 'Contact Us',
+    libraries: 'API Libraries'
   },
   dashboard: {
     title: 'Dashboard',
@@ -288,5 +290,19 @@ export default {
     success: 'Invoice issued successfully.',
     fail: 'Failed to issue invoice.',
     noData: 'No Data Available'
+  },
+  guide: {
+    one: 'Step 1',
+    oneDesc: 'Get your API keys here',
+    two: 'Step 2',
+    twoDesc: ['Click to copy your key, and integrate API into your projects referring to', 'detailed documentation'],
+    three: 'Step 3',
+    threeDesc: 'Checks the progress of your API plan and the status of API requests',
+    four: 'Step 4',
+    fourDesc: 'If you encounter problems, please contact us for 24/5 technical support',
+    next: 'Next',
+    tip: ['Tip: Learn', 'how to apply the API keys.'],
+    disable: 'Never prompt again',
+    done: 'Done'
   }
 }

+ 17 - 1
src/locales/zh.ts

@@ -15,7 +15,9 @@ export default {
     information: '账单信息',
     invoices: '发票中心',
     support: '支持',
-    document: '开发文档 '
+    document: '开发文档 ',
+    contact: '联系我们',
+    libraries: 'API 库'
   },
   dashboard: {
     title: '看板',
@@ -288,5 +290,19 @@ export default {
     success: '开票成功',
     fail: '开票失败',
     noData: '无可用数据'
+  },
+  guide: {
+    one: '第一步',
+    oneDesc: '获取您的API 密钥',
+    two: '第二步',
+    twoDesc: ['点击复制 API 密钥,参考', '开发文档', '将 API 集成到您自己的项目中'],
+    three: '第三步',
+    threeDesc: '查看您的套餐使用进度和 API 调用情况',
+    four: '第四步',
+    fourDesc: '如果您遇到问题,请联系我们,我们将提供 24/5 的技术支持',
+    next: '下一步',
+    tip: ['提示:了解', '如何添加 API 密钥'],
+    disable: '不再提示',
+    done: '完成'
   }
 }

+ 2 - 0
src/request/api.ts

@@ -94,3 +94,5 @@ export const apiChangeEmail = (data: object, config: object) => post(backgroundU
 export const apiApplyInvoice = (data: object, config: object) => post(backgroundUrl + '/user-api/v1/user/billing/applyInvoice', data, config)
 // 发送邮件
 export const apiSendEmail = (data: object, config: object) => post(backgroundUrl + '/user-api/v1/user/billing/billSendEmail', data, config)
+// 发送不在提醒
+export const neverPrompt = (data: object, config: object) => post(baseUrl + '/api/user/manual-popup/disabled', data, config)

+ 18 - 0
src/views/projects/api/apiKeys.vue

@@ -75,6 +75,10 @@
           <p slot="empty">{{ $t('apiKey.noData') }}</p>
         </el-table>
       </div>
+      <div class="tips">
+        <span>{{ $t('guide.tip')[0] }}</span>
+        <a :class="$i18n.locale === 'en' && 'en'" target="_blank" :href="apiDomain + '/api-libraries/authentication'">{{ $t('guide.tip')[1] }}</a>
+      </div>
     </div>
   </div>
 </template>
@@ -104,6 +108,7 @@ export default class apiKeys extends Vue {
   showTooltip = ''
   copyTimer: any = null
   isCopyActive = ''
+  apiDomain = process.env.VUE_APP_API_DOMAIN
 
   get showFather () {
     return (this as any).$route.meta.showFather
@@ -210,6 +215,19 @@ export default class apiKeys extends Vue {
 .min {
   display: none;
 }
+.check {
+  width: 18px;
+  height: 18px;
+  border-radius: 4px;
+  border: 1px solid #94969D;
+}
+.tips {
+  margin-top: 24px;
+  text-align: left;
+  .en {
+    margin-left: 4px;
+  }
+}
 .api-keys {
   .top {
     display: flex;