Ver código fonte

enhance driver management

vbea 3 anos atrás
pai
commit
1dcec6969f

+ 25 - 1
Strides-Admin/src/components/Breadcrumb/index.vue

@@ -2,6 +2,12 @@
   <el-breadcrumb class="app-breadcrumb" separator-class="el-icon-arrow-right">
     <transition-group name="breadcrumb">
       <el-breadcrumb-item v-for="(item,index) in levelList" :key="item.path">
+        <div class="parent-router" v-if="item.meta.parent">
+          <a v-if="item.meta.parent.path" @click.prevent="handleParent(item.meta.parent)">
+            {{ item.meta.parent.title }}
+          </a>
+          <i class="el-breadcrumb__separator el-icon-arrow-right" style="display: inline;"></i>
+        </div>
         <span v-if="item.redirect==='noRedirect'||index==levelList.length-1" class="no-redirect">
           {{ item.meta.title }}
           <template v-if="item.meta.subTitle">{{ item.meta.subTitle }}</template>
@@ -43,7 +49,7 @@ export default {
       // only show routes with meta.title
       let matched = this.$route.matched.filter(item => item.meta && item.meta.title)
 
-      this.levelList = matched.filter(item => item.meta && item.meta.title && item.meta.breadcrumb !== false)
+      this.levelList = matched.filter(item => item.meta && item.meta.title && item.meta.breadcrumb !== false);
     },
     pathCompile(path) {
       // To solve this problem https://github.com/PanJiaChen/vue-element-admin/issues/561
@@ -67,6 +73,11 @@ export default {
         })
 
       }
+    },
+    handleParent(parent) {
+      if (parent.path) {
+        this.$router.push({path: parent.path})
+      }
     }
   }
 }
@@ -84,7 +95,20 @@ export default {
     color: #333333;
     cursor: text;
   }
+  .parent-router {  
+    display: inline;
+    a {
+      color: #303133;
+      cursor: pointer;
+      height: 30px;
+    }
+    a:hover {
+      color: #001489;
+      cursor: pointer;
+    }
+  }
 }
+
 </style>
 <style>
   .el-breadcrumb__separator {

+ 139 - 0
Strides-Admin/src/components/MyUpload.vue

@@ -0,0 +1,139 @@
+<template>
+  <div>
+    <el-upload
+      v-bind="$props"
+      :http-request="onUpload">
+      <slot></slot>
+    </el-upload>
+  </div>
+</template>
+
+<script>
+  export default {
+    name: "MyUpload",
+    props: {
+      accept: String,
+      action: {
+        type: String,
+        required: true
+      },
+      autoUpload: {
+        type: Boolean,
+        default: true
+      },
+      beforeRemove:Function,
+      beforeUpload: Function,
+      disabled: Boolean,
+      drag: Boolean,
+      dragger: Boolean,
+      fileList: Array,
+      headers: Object,
+      httpRequest: Function,
+      isBlob:Boolean,
+      limit: Number,
+      listType: String,
+      multiple: Boolean,
+      name: String,
+      onChange: Function,
+      onError: Function,
+      onExceed: Function,
+      onPreview: Function,
+      onProgress: Function,
+      onRemove: Function,
+      onSuccess: Function,
+      showFileList: Boolean,      
+      type: String,
+      withCredentials: Boolean
+    },
+    data() {
+      return {
+        
+      };
+    },
+    mounted() {
+      
+    },
+    methods: {
+      onUpload(option) {
+        var _this = this;
+        if (this.httpRequest) {
+          return this.httpRequest();
+        }
+        option.isBlob = this.isBlob;
+        if (typeof XMLHttpRequest === 'undefined') {
+          return;
+        }
+        var xhr = new XMLHttpRequest();
+        var action = option.action;
+        if (xhr.upload) {
+          xhr.upload.onprogress = function progress(e) {
+            if (e.total > 0) {
+              e.percent = e.loaded / e.total * 100;
+            }
+            option.onProgress(e);
+          };
+        }
+            
+        var formData = new FormData();
+        if (option.data) {
+          Object.keys(option.data).forEach(function (key) {
+            formData.append(key, option.data[key]);
+          });
+        }
+        formData.append(option.filename, option.file, option.file.name);
+        xhr.onerror = function error(e) {
+          option.onError(e);
+        };
+            
+        xhr.onload = function() {
+          if (xhr.status < 200 || xhr.status >= 300) {
+            return option.onError(getError(action, option, xhr));
+          }
+          if (option.isBlob) {
+            option.onSuccess(xhr.response);
+          } else {
+            option.onSuccess(_this.getBody(xhr));
+          }
+        };
+        
+        if (option.isBlob) {
+          xhr.responseType="blob";
+        }
+        xhr.open('post', action, true);
+            
+        if (option.withCredentials && 'withCredentials' in xhr) {
+          xhr.withCredentials = true;
+        }
+            
+        var headers = option.headers || {};
+            
+        for (var item in headers) {
+          if (headers.hasOwnProperty(item) && headers[item] !== null) {
+            xhr.setRequestHeader(item, headers[item]);
+          }
+        }
+        xhr.send(formData);
+        return xhr;
+      },
+      getBody(xhr) {
+        /*xhr.overrideMimeType(
+          'text/plain; charset=x-user-defined'
+        );*/
+        var text = xhr.responseText || xhr.response;
+        if (!text) {
+          return text;
+        }
+      
+        try {
+          return JSON.parse(text);
+        } catch (e) {
+          return text;
+        }
+      }
+    }
+  }
+</script>
+
+<style scoped>
+
+</style>

+ 64 - 64
Strides-Admin/src/http/api/driver.js

@@ -1,67 +1,67 @@
-import { get, post } from '../http'
+import { get, post, download } from '../http'
 
-// {
-//   "pageSize": 10,
-//   "pageNo": 1,
-//   "pageVo": {
-//     "criteria": "test"
-//   }
-// }
-export function fetchDriverPages(data) {
-  return post('driver/getDriverPages', data)
+const driver = {
+  // {
+  //   "pageSize": 10,
+  //   "pageNo": 1,
+  //   "pageVo": {
+  //     "criteria": "test"
+  //   }
+  // }
+  fetchDriverPages(data) {
+    return post('driver/getDriverPages', data)
+  },
+  // userPk
+  fetchDriverDetail(params) {
+    return get('driver/getDriverDetail', params)
+  },
+  // {
+  //   "fleetCompanyId": 1,
+  //   "nickName": "nickName",
+  //   "email": "email@qq.com",
+  //   "pdvLicence": "132321321321323",
+  //   "phone": 13122222222,
+  //   "pdvLicencePictures": [
+  //     "/juicePicture/userProfile/906c9fe4bae144bf8f2b85b4dff6b336.jpg",
+  //     "/juicePicture/userProfile/906c9fe4bae144bf8f2b85b4dff6b336.jpg"
+  //   ]
+  // }
+  addDriver(data) {
+    return post('driver/addDriver', data)
+  },
+  // {
+  //   "userPk": 109,
+  //   "fleetCompanyId": 1,
+  //   "nickName": "nickName1",
+  //   "email": "email@qq.com",
+  //   "pdvLicence": "132321321321323",
+  //   "phone": 13122222222,
+  //   "pdvLicencePictures": [
+  //     "/juicePicture/userProfile/906c9fe4bae144bf8f2b85b4dff6b336.jpg",
+  //     "/juicePicture/userProfile/906c9fe4bae144bf8f2b85b4dff6b336.jpg"
+  //   ]
+  // }
+  updateDriver(data) {
+    return post('driver/updateDriver', data)
+  },
+  // userPk
+  deleteDriver(params) {
+    return get('driver/delDriver', params)
+  },
+  // userPk
+  approveDriver(params) {
+    return get('driver/approveDriver', params)
+  },
+  // userPk
+  rejectDriver(params) {
+    return get('driver/rejectDriver', params)
+  },
+  fetchFleetCompanyOptions() {
+    return get('siteManagement/getFleetCompanyList')
+  },
+  downloadTemplate() {
+    return download('driver/download/template')
+  }
 }
 
-// userPk
-export function fetchDriverDetail(params) {
-  return get('driver/getDriverDetail', params)
-}
-
-// {
-//   "fleetCompanyId": 1,
-//   "nickName": "nickName",
-//   "email": "email@qq.com",
-//   "pdvLicence": "132321321321323",
-//   "phone": 13122222222,
-//   "pdvLicencePictures": [
-//     "/juicePicture/userProfile/906c9fe4bae144bf8f2b85b4dff6b336.jpg",
-//     "/juicePicture/userProfile/906c9fe4bae144bf8f2b85b4dff6b336.jpg"
-//   ]
-// }
-export function addDriver(data) {
-  return post('driver/addDriver', data)
-}
-
-// {
-//   "userPk": 109,
-//   "fleetCompanyId": 1,
-//   "nickName": "nickName1",
-//   "email": "email@qq.com",
-//   "pdvLicence": "132321321321323",
-//   "phone": 13122222222,
-//   "pdvLicencePictures": [
-//     "/juicePicture/userProfile/906c9fe4bae144bf8f2b85b4dff6b336.jpg",
-//     "/juicePicture/userProfile/906c9fe4bae144bf8f2b85b4dff6b336.jpg"
-//   ]
-// }
-export function updateDriver(data) {
-  return post('driver/updateDriver', data)
-}
-
-// userPk
-export function deleteDriver(params) {
-  return get('driver/delDriver', params)
-}
-
-// userPk
-export function approveDriver(params) {
-  return get('driver/approveDriver', params)
-}
-
-// userPk
-export function rejectDriver(params) {
-  return get('driver/rejectDriver', params)
-}
-
-export function fetchFleetCompanyOptions() {
-  return get('siteManagement/getFleetCompanyList')
-}
+export default driver;

+ 5 - 1
Strides-Admin/src/router/ChargingProfile.js

@@ -70,7 +70,11 @@ export default {
       name: 'view-charging-profile',
       meta: {
         title: 'View Charging Profile',
-        activeMenu: '/charging-profiles/config-stations'
+        activeMenu: '/charging-profiles/config-stations',
+        parent: {
+          title: 'Configure Stations',
+          path: "/charging-profiles/config-stations"
+        }
       },
       hidden: true
     }

+ 7 - 4
Strides-Admin/src/router/UserRouter.js

@@ -6,7 +6,7 @@ export default {
   component: Layout,
   meta: {
     title: 'User Management',
-    icon: 'user-management',
+    icon: 'user-management'
   },
   children: [
     {
@@ -28,8 +28,7 @@ export default {
         title: 'PH Driver',
         icon: 'sidebar-submenu-item',
         activeIcon: 'sidebar-submenu-item-active',
-      },
-      hidden: true
+      }
     },
     {
       path: '/driver/driver-detail',
@@ -38,7 +37,11 @@ export default {
       meta: {
         title: 'Driver Detail',
         breadcrumb: true,
-        activeMenu: '/driver'
+        activeMenu: '/driver',
+        parent: {
+          title: 'PH Driver',
+          path: "/driver"
+        }
       },
       hidden: true
     },

+ 4 - 4
Strides-Admin/src/styles/variables.scss

@@ -8,9 +8,6 @@ $tiffany: #4ab7bd;
 $yellow: #fec171;
 $panGreen: #30b08f;
 
-$navigationBarHeight: 64px;
-$mainAppMinHeight: calc(100vh - #{$navigationBarHeight});
-
 // sidebar
 $menuText: #000;
 $menuActiveText: #fff;
@@ -23,9 +20,12 @@ $subMenuBg: #FFF;
 $subMenuHover: #909DD8;
 
 $itemActiveBg: #001489;
-$subMenuActiveBg: #fff;
+$subMenuActiveBg: #FFF;
 
+// size
 $sideBarWidth: 300px;
+$navigationBarHeight: 64px;
+$mainAppMinHeight: calc(100vh - #{$navigationBarHeight});
 
 // the :export directive is the magic sauce for webpack
 // https://www.bluematador.com/blog/how-to-share-variables-between-js-and-sass

+ 2 - 2
Strides-Admin/src/views/charge/AddStation.vue

@@ -673,7 +673,6 @@
     white-space: nowrap;
   }
   .copy-url {
-    flex: 1;
     color: #888;
     display: flex;
     cursor: pointer;
@@ -686,7 +685,8 @@
     padding: 5px;
     font-size: 16px;
   }
-  .copy-url:hover {
+  .copy-url:hover,
+  .copy-url:active {
     color: #000;
   }
 </style>

+ 7 - 14
Strides-Admin/src/views/driver/DriverDetail.vue

@@ -181,14 +181,7 @@
 </template>
 
 <script>
-import {
-  addDriver,
-  approveDriver,
-  fetchDriverDetail,
-  fetchFleetCompanyOptions,
-  rejectDriver,
-  updateDriver
-} from '@/http/api/driver'
+import api from '@/http/api/driver'
 import { baseURL } from '@/http/http'
 import site from '../../http/api/site'
 import { getToken } from '@/utils/auth'
@@ -307,7 +300,7 @@ export default {
     },
     async getDriverDetail() {
       if (this.isEdit) {
-        const { success, data } = await fetchDriverDetail({
+        const { success, data } = await api.fetchDriverDetail({
           userPk: this.getDriverId()
         })
         if (success) {
@@ -352,7 +345,7 @@ export default {
       const {
         success,
         data
-      } = await fetchFleetCompanyOptions()
+      } = await api.fetchFleetCompanyOptions()
       if (success) {
         this.options.fleetCompany = data
       }
@@ -361,7 +354,7 @@ export default {
       const params = { userPk: this.getDriverId() }
       this.approveButtonLoading = true
       try {
-        const { success, msg } = await approveDriver(params)
+        const { success, msg } = await api.approveDriver(params)
         if (success) {
           this.$notify.success(msg)
           this.$router.back()
@@ -375,7 +368,7 @@ export default {
       const params = { userPk: this.getDriverId() }
       this.rejectButtonLoading = true
       try {
-        const { success, msg } = await rejectDriver(params)
+        const { success, msg } = await api.rejectDriver(params)
         if (success) {
           this.$notify.success(msg)
           this.$router.back()
@@ -409,7 +402,7 @@ export default {
 
             let result
             if (this.isEdit) {
-              result = await updateDriver({
+              result = await api.updateDriver({
                 pdvLicencePictures,
                 phone,
                 countryCode,
@@ -417,7 +410,7 @@ export default {
                 ...others,
               })
             } else {
-              result = await addDriver({
+              result = await api.addDriver({
                 pdvLicencePictures,
                 countryCode,
                 callingCode,

+ 194 - 143
Strides-Admin/src/views/driver/index.vue

@@ -1,108 +1,128 @@
 <template>
-  <div class="container">
-    <div class="content">
-      <el-form ref="form" :model="form" class="form">
-        <el-row :gutter="12">
-          <el-col :span="8">
+  <div class="app-container">
+    <div class="filter-container">
+      <el-form
+        ref="form"
+        :model="form">
+        <div class="filter-view">
+          <el-form-item
+            label=""
+            class="flex-item flex1"
+            style="min-width: 200px; max-width: 400px;">
             <el-input
               v-model="form.criteria"
               placeholder="Search by Email, Phone or Fleet Company Name"
-              clearable
-            ></el-input>
-          </el-col>
-          <el-col :span="3">
-            <el-button 
-              @click="onClickSearch"
-              icon="el-icon-search"
-              type="primary"
-            >
-              Search
-            </el-button>
-          </el-col>
-          <el-col :offset="10" :span="3" >
+              clearable/>
+          </el-form-item>
+          <el-button 
+            @click="onClickSearch"
+            icon="el-icon-search"
+            type="primary">
+            Search
+          </el-button>
+          <div class="flex1"></div>
+          <my-upload
+            accept=".xls,.xlsx,.csv"
+            :limit="1"
+            :is-blob="true"
+            :action="action"
+            :headers="headers"
+            :file-list="fileList"
+            :show-file-list="false"
+            :before-upload="onImportStart"
+            :on-success="onImportExcel"
+            :on-error="onImportExcelErr">
             <el-button
-              icon="el-icon-plus"
+              icon="el-icon-upload2"
               type="primary"
-              @click="onClickAdd"
-            >
-              Add PH Driver
+              :loading="loading.upload">
+              Import Excel
             </el-button>
-          </el-col>
-        </el-row>
+          </my-upload>
+          <el-button
+            icon="el-icon-download"
+            type="primary"
+            :loading="loading.download"
+            @click="onDownloadTmp">
+            Download Template
+          </el-button>
+          <el-button
+            icon="el-icon-plus"
+            type="primary"
+            @click="onClickAdd">
+            Add PH Driver
+          </el-button>
+        </div>
       </el-form>
-      <el-table :data="table.list">
-        <el-table-column
-          align="center"
-          label="User ID"
-          prop="userPk"
-          width="100"
-        >
-          <template
-            v-slot="{ row }"
-          >
-            <router-link
-              class="table-link"
-              :to="{
-                name: 'DriverDetail',
-                query: {
-                  id: row.userPk,
-                  dispatch: true,
-                },
-              }"
-            >
-              {{ row.userPk }}
-            </router-link>
-          </template>
-        </el-table-column>
-        <el-table-column
-          width="200"
-          align="center"
-          label="Name"
-          prop="nickName"
-        ></el-table-column>
-        <el-table-column
-          align="center"
-          label="Email"
-          prop="email"
-        ></el-table-column>
-        <el-table-column
-          width="150"
-          align="center"
-          label="Phone"
-          prop="phone"
-        ></el-table-column>
-        <el-table-column
-          align="center"
-          label="Fleet Company"
-          prop="fleetCompanyName"
-        ></el-table-column>
-        <el-table-column
-          align="center"
-          label="Status"
-          prop="driverStatus"
-          width="150"
-        ></el-table-column>
-        <el-table-column
-          align="center"
-          label="Action"
-          width="210"
-        >
-          <template slot-scope="{ row }">
-            <TableAction
-              :showEdit="isDriverOperation(row)"
-              @edit="onClickEditButton(row)"
-              @delete="onClickDeleteButton(row)"/>
-          </template>
-        </el-table-column>
-      </el-table>
-      <div class="right">
-        <pagination
-          v-show="table.total > 0"
-          :total="table.total"
-          :page.sync="table.pageNo"
-          :limit.sync="table.pageSize"
-          @pagination="handlePageChange" />
-      </div>
+    </div>
+    <el-table :data="table.list" v-loading="loading.table">
+      <el-table-column
+        align="center"
+        label="User ID"
+        prop="userPk"
+        width="100">
+        <template slot-scope="{row}" >
+          <router-link
+            class="table-link"
+            :to="{
+              name: 'DriverDetail',
+              query: {
+                id: row.userPk,
+                dispatch: true,
+              },
+            }">
+            {{ row.userPk }}
+          </router-link>
+        </template>
+      </el-table-column>
+      <el-table-column
+        width="200"
+        align="center"
+        label="Name"
+        prop="nickName"
+      ></el-table-column>
+      <el-table-column
+        align="center"
+        label="Email"
+        prop="email"
+      ></el-table-column>
+      <el-table-column
+        width="150"
+        align="center"
+        label="Phone"
+        prop="phone"
+      ></el-table-column>
+      <el-table-column
+        align="center"
+        label="Fleet Company"
+        prop="fleetCompanyName"
+      ></el-table-column>
+      <el-table-column
+        align="center"
+        label="Status"
+        prop="driverStatus"
+        width="150"
+      ></el-table-column>
+      <el-table-column
+        align="center"
+        label="Action"
+        width="210"
+      >
+        <template slot-scope="{ row }">
+          <TableAction
+            :showEdit="isDriverOperation(row)"
+            @edit="onClickEditButton(row)"
+            @delete="onClickDeleteButton(row)"/>
+        </template>
+      </el-table-column>
+    </el-table>
+    <div class="right">
+      <pagination
+        v-show="table.total > 0"
+        :total="table.total"
+        :page.sync="table.pageNo"
+        :limit.sync="table.pageSize"
+        @pagination="handlePageChange" />
     </div>
   </div>
 </template>
@@ -110,11 +130,13 @@
 <script>
 import Pagination from '@/components/Pagination'
 import TableAction from '@/components/TableAction'
-import { deleteDriver, fetchDriverPages } from '@/http/api/driver'
-
+import api from '@/http/api/driver'
+import { baseURL } from '@/http/http'
+import { getToken } from '@/utils/auth'
+import MyUpload from '@/components/MyUpload'
 export default {
   name: "FleetCompanyList",
-  components: { Pagination, TableAction },
+  components: { Pagination, TableAction,MyUpload },
   data() {
     return {
       form: {
@@ -126,6 +148,22 @@ export default {
         pageNo: 1,
         pageSize: 10,
       },
+      loading: {
+        table: false,
+        upload: false,
+        download: false
+      },
+      fileList: []
+    }
+  },
+  computed: {
+    action() {
+      return baseURL + process.env.VUE_APP_API_PREFIX + '/driver/batch/create'
+    },
+    headers() {
+      return {
+        accessToken: getToken()
+      }
     }
   },
   methods: {
@@ -140,13 +178,65 @@ export default {
           criteria: this.form.criteria,
         },
       }
-      const { success, data, total } = await fetchDriverPages(params)
+      const { success, data, total } = await api.fetchDriverPages(params)
       if (success) {
         this.table.list = data
         this.table.total = total
       }
     },
-
+    onImportStart() {
+      this.loading.upload = true;
+    },
+    onImportExcel(res, file, fileList) {
+      fileList = [];
+      this.fileList = [];
+      if (res.success == undefined) {
+        this.downloadExcel(res, "batch_create_result.xls");
+        this.loading.upload = false;
+      } else {
+        this.onImportExcelErr(res.msg, file, fileList);
+      }
+    },
+    onImportExcelErr(err, file, fileList) {
+      this.$message({
+        type: 'error',
+        message: err
+      })
+      this.loading.upload = false;
+    },
+    onDownloadTmp() {
+      this.loading.download = true;
+      api.downloadTemplate().then(res => {
+        this.downloadExcel(res, "driver-template.xlsx")
+      }).catch(err => {
+        this.$message({
+          type: 'error',
+          message: err
+        })
+      }).finally(() => {
+        this.loading.download = false;
+      })
+    },
+    downloadExcel(res, fileName) {
+      const blob = new Blob([res], {
+        type: 'application/vnd.ms-excel;charset=utf-8'
+      })
+      // let href = window.URL.createObjectURL(blob)
+      if ('download' in document.createElement('a')) {
+        // 非IE下载
+        const elink = document.createElement('a')
+        elink.download = fileName
+        elink.style.display = 'none'
+        elink.href = URL.createObjectURL(blob)
+        document.body.appendChild(elink)
+        elink.click()
+        URL.revokeObjectURL(elink.href) // 释放URL 对象
+        document.body.removeChild(elink)
+      } else {
+        // IE10+下载
+        navigator.msSaveBlob(blob, fileName)
+      }
+    },
     onClickSearch() {
       this.table.pageNo = 1
       this.getDriverPages()
@@ -165,7 +255,7 @@ export default {
     },
     async deleteUser(driver) {
       try {
-        const { success, msg } = await deleteDriver({
+        const { success, msg } = await api.deleteDriver({
           userPk: driver.userPk
         })
         if (success) {
@@ -194,44 +284,5 @@ export default {
 </script>
 
 <style lang="scss" scoped>
-@import '../../styles/variables.scss';
-  .container {
-    width: 100%;
-    padding: 20px 50px;
-    background-color: #F0F5FC;
-    .content {
-      padding: 45px 45px;
-      border-radius: 6px;
-      min-height: calc(#{$mainAppMinHeight} - (2 * 20px));
-      background-color: white;
-      .form {
-        margin-bottom: 30px;
-      }
-      .table-link {
-        color: #333;
-        display: inline-block;
-        cursor: pointer;
-        white-space: nowrap;
-        text-decoration: underline;
-      }
-      .table-link:hover {
-        color: #1290BF;
-        text-decoration: none;
-      }
-      .action {
-        .editButton {
-          color: #3179E4;
-        }
-        .deleteButton {
-          color: #ED3F3F;
-        }
-      }
-    }
-  }
-  .el-button {
-    width: 100%;
-  }
-  .el-input {
-    width: 100%;
-  }
+
 </style>