Browse Source

add: 添加 VPP 私有化流程

liutian 10 months ago
parent
commit
46e618a5f3

+ 1 - 0
.env.premise

@@ -0,0 +1 @@
+VITE_BASE_URL = http://192.168.10.128

+ 1 - 0
.eslintrc.cjs

@@ -23,6 +23,7 @@ module.exports = {
     "vue/require-default-prop": "off",
     "vue/multi-word-component-names": "off",
     "vue/prefer-import-from-vue": "off",
+    "no-debugger": "off",
 
     // reactivity transform
     "vue/no-setup-props-destructure": "off",

+ 2 - 1
package.json

@@ -2,7 +2,7 @@
   "name": "pdf_tech_front_end",
   "version": "0.0.0",
   "scripts": {
-    "dev": "vite --mode development",
+    "dev": "vite --mode premise",
     "preparing": "vite --mode preparing build",
     "build": "vite --mode production build",
     "preview": "vite preview --port 4173",
@@ -13,6 +13,7 @@
   },
   "dependencies": {
     "axios": "^1.2.1",
+    "copy-to-clipboard": "^3.3.3",
     "crypto-js": "^4.1.1",
     "echarts": "^5.4.1",
     "element-ui": "^2.15.12",

File diff suppressed because it is too large
+ 1454 - 1500
pnpm-lock.yaml


+ 1 - 1
src/assets/style/element-variables.scss

@@ -212,4 +212,4 @@ li.el-menu-item,
   &::placeholder {
     color: #808185;
   }
-}
+}

+ 8 - 7
src/components/sideMenu.vue

@@ -1,5 +1,4 @@
 <script>
-import { onMounted, ref } from "vue"
 import { get } from '../../utils/request'
 import Dashboard from '@/components/icon/menu_dashboard.vue'
 import Product from '@/components/icon/menu_product.vue'
@@ -9,7 +8,6 @@ import Device from '@/components/icon/menu_device.vue'
 import Settings from '@/components/icon/menu_setting.vue'
 import Support from '@/components/icon/menu_support.vue'
 import Logout from '@/components/icon/logout.vue'
-import { Aside } from 'element-ui'
 import { userStore } from '@/store/userInfo'
 
 export default {
@@ -95,6 +93,9 @@ export default {
       } else {
         return 'Team member'
       }
+    },
+    init(){
+      return userStore().getInit
     }
   }
 }
@@ -113,7 +114,7 @@ export default {
       </div>
       <el-menu :default-active="path" active-text-color="#1460F3" text-color="#232A40" router>
         <div v-if="role?.indexOf('2') !== -1 || role?.indexOf('1') !== -1">
-          <el-menu-item index="/dashboard">
+          <el-menu-item index="/dashboard" :disabled="!init">
             <Dashboard />
             <span>Home</span>
           </el-menu-item>
@@ -121,7 +122,7 @@ export default {
             <Product />
             <span>Product Management</span>
           </el-menu-item>
-          <el-submenu index="3">
+          <el-submenu index="3" :disabled="!init">
             <template slot="title">
               <Team />
               <span>Team Management</span>
@@ -130,7 +131,7 @@ export default {
             <el-menu-item index="/manage-member">Manage Member</el-menu-item>
             <el-menu-item index="/manage-admin">Manage Admin</el-menu-item>
           </el-submenu>
-          <el-submenu index="4">
+          <el-submenu index="4" :disabled="!init">
             <template slot="title"> 
               <License />
               <span>Manage License</span>
@@ -139,7 +140,7 @@ export default {
             <el-menu-item index="/assign-license">Assign License</el-menu-item>
             <el-menu-item index="/batch-cancel-license">Batch Remove</el-menu-item>
           </el-submenu>
-          <el-menu-item index="/manage-device">
+          <el-menu-item index="/manage-device" :disabled="!init">
             <Device />
             <span>Unbind Device</span>
           </el-menu-item>
@@ -229,4 +230,4 @@ export default {
     }
   }
 }
-</style>
+</style>

+ 48 - 33
src/router/index.js

@@ -2,7 +2,6 @@ import Vue from "vue"
 import VueRouter from "vue-router"
 import cookies from 'vue-cookies'
 import { userStore } from '@/store/userInfo'
-import { Message } from 'element-ui'
 
 Vue.use(VueRouter)
 
@@ -13,12 +12,13 @@ let originReplace=VueRouter.prototype.replace
 // 重写
 VueRouter.prototype.push=function(location,res,rej){
   // 如果传了成功,失败的回调
-  if(res&&rej){
-      originPush.call(this,location,res,rej)
-  }else{
-      originPush.call(this,location,()=>{},()=>{})
+  if(res && rej){
+    originPush.call(this,location,res,rej)
+  } else {
+    originPush.call(this,location,()=>{},()=>{})
   }
 }
+
 VueRouter.prototype.replace=function(location,res,rej){
   if (res && rej) {
       originReplace.call(this, location, res, rej)
@@ -43,6 +43,11 @@ const router = new VueRouter({
         title: 'Log In PDF Tech Console'
       }
     },
+    {
+      path: "/upload-file",
+      name: "upload",
+      component: () => import("../views/UploadFile.vue")
+    },
     {
       path: "/non-admin-user",
       name: "noadmin",
@@ -233,33 +238,8 @@ const router = new VueRouter({
     }
   ],
 })
-router.beforeEach((to, from, next) => {
-  const whiteList = '/login'
-  const token = cookies.get('accessToken')
-  //路由守卫,进行权限判断
-  if (token === 'expired') {
-    cookies.remove('accessToken')
-    // 判断是否登录
-    Message({
-      duration: 5000,
-      message: 'Your session has expired. Please log in again.',
-      type: "error",
-    })
-    next('/login')
-  } else if (token && to.path !== whiteList && to.path !== '/sign-up' && to.path !== '/non-admin-user' && to.path !== '/forget-password') {
-    // 判断权限
-    if (userStore().user.role?.indexOf("2") !== -1 || userStore().user.role?.indexOf("1") !== -1 || userStore().user.role?.indexOf("4") !== -1) {
-      next()
-    } else {
-      next('/non-admin-user')
-    }
-  } else {
-    next()
-  }
-})
-
-// 设置title
-router.beforeEach((to, from, next) => {
+router.beforeEach(async(to, from, next) => {
+  // 设置title
   if (to && to.meta && to.meta.title) {
     document.title = to.meta.title
   }
@@ -270,7 +250,42 @@ router.beforeEach((to, from, next) => {
   meta.content = to.meta.content
   head[0].appendChild(meta)
 
-  next()
+  const whiteList = ['/login', '/sign-up', '/non-admin-user', '/forget-password', '/upload-file']
+  const token = cookies.get('accessToken')
+  //路由守卫,进行权限判断
+  if (token) {
+    try {
+      await userStore().getUserInfo()
+      // 判断权限
+      if (userStore().user.role?.indexOf("2") !== -1 || userStore().user.role?.indexOf("1") !== -1 || userStore().user.role?.indexOf("4") !== -1) {
+        next()
+      } else {
+        debugger
+        next('/non-admin-user')
+      }
+      const init = await userStore().getInitorUpdate()
+      if (!init) {
+        next('/product')
+      }
+    } catch (error) {
+      console.log(error)
+      const { response } = error
+      if (response && response.data) {
+        const data = response.data
+        if (data.code === 310 || data.code === 313) {
+          cookies.remove('accessToken')
+          next('/login')
+        }
+      }
+      next()
+    }
+  } else {
+    if (whiteList.indexOf(to.path) !== -1) {
+      next()
+    } else {
+      next('/login')
+    }
+  }
 })
 
 export default router

+ 29 - 2
src/store/userInfo.js

@@ -1,13 +1,40 @@
 import { defineStore } from 'pinia'
+import { get, post } from '../../utils/request'
 
 export const userStore = defineStore('user', {
     state: () => ({
-        user: JSON.parse(localStorage.getItem('userInfo')) || ''
+        user: JSON.parse(localStorage.getItem('userInfo')) || '',
+        init: false
     }),
     getters: {
-        getUser: (state) => state.user
+        getUser: (state) => state.user,
+        getInit: (state) => state.init
     },
     actions: {
+        async getInitorUpdate() {
+            try {
+                const res = await post('/pdf-tech/vppKeyFile/updateOrInit')
+                if (res.data.code === 200 && res.data.result) {
+                    this.init = true
+                } else {
+                    this.init = false
+                }
+                return this.init
+            } catch (error) {
+                this.init = false
+                return Promise.reject(error)
+            }
+        },
+        async getUserInfo() {
+            try {
+                const res = await get('/pdf-tech/vppMember/getMemberInfo')
+                if (res.data.code === 200 && res.data.msg == 'success') {
+                    this.setUserInfo(res.data.result)
+                }
+            } catch (error) {
+                return Promise.reject(error)
+            }
+        },
         // 获取用户信息
         setUserInfo(user) {
             if (user) {

+ 89 - 9
src/views/ProductManagement.vue

@@ -1,12 +1,11 @@
 <script setup>
-import { onMounted, ref } from 'vue'
-import Download from '@/components/icon/download.vue'
-import Warning from '@/components/icon/warning.vue'
-import Search from '@/components/icon/search.vue'
-import { get } from '../../utils/request'
+import { onMounted, ref, getCurrentInstance } from 'vue'
+import copy from 'copy-to-clipboard'
+import { get, post } from '../../utils/request'
 import { userStore } from '@/store/userInfo'
 import { productListNameMapping } from '../../utils/mapping'
 
+const { proxy } = getCurrentInstance()
 const currentPage = ref(1)
 const size = ref(5)
 const tableData = ref([])
@@ -18,6 +17,9 @@ const productList = ref([])
 const productId = ref('')
 const url = ref('')
 const role = userStore().user.role
+const serverId = ref('')
+const serverIdDialogVisible = ref(false)
+const file = ref(null)
 
 onMounted(() => {
   let pageText = document.getElementsByClassName('el-pagination__jump')[0]
@@ -78,10 +80,89 @@ const handleCurrentChange = (value) => {
   currentPage.value = value
   pagingQuery()
 }
+
+const upLoadChange = (value) => {
+  file.value = value.raw
+  const formData = new FormData()
+  formData.append("keyFile", file.value)
+  post('/pdf-tech/vppKeyFile/parseKeyFile', formData)
+    .then((res) => {
+      if (res.data.code === 200) {
+        proxy.$message({
+          duration: 5000,
+          message: 'Upload Successfully',
+          type: 'success'
+        })
+      } else {
+        proxy.$message({
+          duration: 5000,
+          message: 'Upload Failed',
+          type: 'error'
+        })
+      }
+    })
+    .catch(() => {
+      proxy.$message({
+        duration: 5000,
+        message: 'Upload Failed',
+        type: 'error'
+      })
+    })
+}
+
+const handleGetKeyFile = async () => {
+  await post('/pdf-tech/vppDevice/getServerId').then((res) => {
+    if (res.data.code === 200) {
+      serverId.value = res.data.result
+    }
+  })
+  dialogVisible.value = false
+  serverIdDialogVisible.value = true
+}
+
+const copyServerId = () => {
+  copy(serverId.value)
+  proxy.$message({
+    duration: 5000,
+    message: 'Copy Successfully',
+    type: 'success'
+  })
+  serverIdDialogVisible.value = false
+}
 </script>
 
 <template>
   <div class="flex flex-col items-center">
+    <el-dialog title="" :visible.sync="dialogVisible" width="376px" top="30vh" center :show-close="false">
+      <h3 class="text-16px font-bold mt-16px">Update Your License</h3>
+      <p>
+        Don't have a key file yet? <span class="text-16px text-[#1460F3] cursor-pointer" @click="handleGetKeyFile">Get one</span>
+      </p>
+      <div slot="footer" class="dialog-footer">
+        <el-button @click="dialogVisible = false">Cancel</el-button>
+        <el-upload
+          class="inline-block ml-8px"
+          :on-change="upLoadChange"
+          :auto-upload="false"
+          :show-file-list="false"
+          action=""
+        >
+          <el-button type="primary">
+            Upload a key file
+          </el-button>
+        </el-upload>
+      </div>
+    </el-dialog>
+    <el-dialog title="" :visible.sync="serverIdDialogVisible" width="376px" top="30vh" :show-close="false">
+      <h3 class="text-16px font-bold mt-16px">Copy Server ID</h3>
+      <p>Contact us with your Server ID to get your key file</p>
+      <h3 class="text-16px font-bold mt-16px">Sever ID:</h3>
+      <p>{{ serverId }}</p>
+      <span slot="footer" class="dialog-footer">
+        <el-button @click="serverIdDialogVisible = false">Cancel</el-button>
+        <el-button type="primary" @click="copyServerId">Copy</el-button>
+      </span>
+    </el-dialog>
     <div class="w-full">
       <h1 class="leading-40px">Product Management</h1>
       <div
@@ -96,13 +177,12 @@ const handleCurrentChange = (value) => {
         "
       >
         <span>Content</span>
-        <a 
+        <button 
           v-if="role === '1'"
-          target="_blank" 
-          :href="url"
           class="h-28px px-10px py-4px bg-[#1460F3] rounded-4px text-14px leading-20px font-400 text-[#fff] cursor-pointer hover:opacity-80"
+          @click="handleClick"
         >
-          Buy More Product</a>
+          Update Key File</button>
       </div>
       <div class="flex bg-[#fff] pt-32px px-24px rounded-t-8px w-full">
         <select v-model="productId" class="w-140px" :class="{ '!text-[#232A40]': productId !== '' }">

+ 1 - 0
utils/mapping.js

@@ -8,6 +8,7 @@ function productListNameMapping (data) {
     'com.brother.pdfreaderpro.windows.product_3': 'PDF Reader Pro for Windows (With PDF to Office Pack)',
     'com.brother.pdfreaderpro.cross.platform.product_3': 'PDF Reader Pro Cross-Platform (With PDF to Office Pack)'
   }
+  if (!data[0]) return []
   return data.map((item) => {
     const code = item.code || item.productCode
     if (code && Object.prototype.hasOwnProperty.call(nameMapping, code)) {

+ 2 - 2
utils/request.js

@@ -58,10 +58,10 @@ instance.interceptors.response.use(
     msg.close()
     if (response && response.data) {
       //没有登录或者登录过期跳到登录页面
-      if(response.data.code === 310 && response.data.msg === "无效的token或者token已过期"){
+      if((response.data.code === 310 && response.data.msg === "无效的token或者token已过期") || response.data.code === 313){
         const token = cookies.get('accessToken')
         if (token) {
-          cookies.set('accessToken', 'expired')
+          cookies.remove('accessToken')
         }
         userStore().clearUserInfo()
         window.location.href = '/login'

+ 54 - 0
web.config

@@ -0,0 +1,54 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<configuration>
+    <system.webServer>
+        <defaultDocument>
+            <files>
+                <remove value="iisstart.htm" />
+                <remove value="index.htm" />
+                <remove value="Default.asp" />
+                <remove value="Default.htm" />
+            </files>
+        </defaultDocument>
+        <rewrite>
+            <rules>
+                <clear />
+
+
+                <rule name="json" stopProcessing="true">
+                    <match url="^(\d\d?\.\d\d?).+/(.+\.json)$" />
+                    <conditions logicalGrouping="MatchAll" trackAllCaptures="false" />
+                    <action type="Rewrite" url="{R:1}/{R:2}" />
+                </rule>
+                <rule name="login" enabled="true" stopProcessing="true">
+                    <match url="^$" />
+                    <action type="None" />
+                </rule>
+                <rule name="pdf-tech" enabled="true" stopProcessing="true">
+                    <match url="^pdf-tech(.*)" />
+                    <action type="Rewrite" url="http://192.168.10.128:8032/pdf-tech{R:1}" logRewrittenUrl="true" />
+                </rule>
+                <rule name="index" enabled="true">
+                    <match url="^(.*)$" ignoreCase="false" />
+                    <conditions logicalGrouping="MatchAll" trackAllCaptures="false">
+                        <add input="{REQUEST_FILENAME}" matchType="IsDirectory" ignoreCase="false" negate="true" />
+                        <add input="{REQUEST_FILENAME}" matchType="IsFile" ignoreCase="false" negate="true" />
+                    </conditions>
+                    <action type="Rewrite" url="index.html?{R:1}" appendQueryString="true" />
+                </rule>
+					
+            </rules>
+            <outboundRules>
+                <rule name="Add the STS header in HTTPS responses">
+                    <match serverVariable="RESPONSE_Strict_Transport_Security" pattern=".*" />
+                    <conditions>
+                        <add input="{HTTPS}" pattern="on" />
+                    </conditions>
+                    <action type="Rewrite" value="max-age=31536000" />
+                </rule>
+            </outboundRules>
+        </rewrite>
+        <security>
+            <requestFiltering removeServerHeader="true" />
+        </security>
+    </system.webServer>
+</configuration>