ソースを参照

#13608 Develop label management

vbea 2 年 前
コミット
51387a38d6

+ 28 - 1
Strides-Admin/src/http/api/site.js

@@ -1,4 +1,4 @@
-import {get, post} from '../http'
+import {get, post, put, del} from '../http'
 
 const MODULE_NAME = 'siteManagement'
 
@@ -62,6 +62,33 @@ const site = {
   },
   getHistoryRates(params) {
     return get(MODULE_NAME + '/site-rates', params)
+  },
+  getLabelPages(params) {
+    return post("label/label-pages", params)
+  },
+  addSiteLabel(params) {
+    return post("label/labels", params)
+  },
+  updateSiteLabel(params) {
+    return put("label/labels", params)
+  },
+  viewSiteLabel(siteLabelId) {
+    return get("label/labels/" + siteLabelId)
+  },
+  deleteSiteLabel(siteLabelId) {
+    return del("label/labels/" + siteLabelId)
+  },
+  getLabelAssignPages(params) {
+    return post("label/assign-site-pages", params)
+  },
+  getAssignStatusOptions() {
+    return get("label/assignment-statuses")
+  },
+  assignSiteLabel(params) {
+    return post("label/assign-sites", params)
+  },
+  unassignSiteLabel(params) {
+    return post("label/un-assign-sites", params)
   }
 }
 

+ 11 - 0
Strides-Admin/src/router/SiteRouter.js

@@ -21,6 +21,17 @@ export default {
         activeIcon: 'sidebar-submenu-item-active',
       }
     },
+    {
+      path: '/site-management/label-management',
+      component: () => import('@/views/site-label/index'),
+      name: 'site-label-management',
+      meta: {
+        breadcrumb: true,
+        title: 'Site Label Management',
+        icon: 'sidebar-submenu-item',
+        activeIcon: 'sidebar-submenu-item-active',
+      }
+    },
     {
       path: '/site-management/dynamic-rate-configuration',
       component: () => import('@/views/rate-dynamic/index'),

+ 11 - 6
Strides-Admin/src/router/permission.js

@@ -64,13 +64,18 @@ router.beforeEach(async(to, from, next) => {
     const routes = store.getters.resources
     if (routes.length == 0) {
       getAuthRoutes(rs => {
-        if (rs && rs.length) {
-          store.commit("user/SET_RESOURCES", rs);
-          store.commit('permission/SET_ROUTES', rs);
-          routes.push(...rs);
-          handleRoute(to, routes, next);
+        if (rs) {
+          if (rs.length) {
+            store.commit("user/SET_RESOURCES", rs);
+            store.commit('permission/SET_ROUTES', rs);
+            routes.push(...rs);
+            handleRoute(to, routes, next);
+          } else {
+            next({ path: '/404' })
+            NProgress.done()
+          }
         } else {
-          next({ path: '/404' })
+          next({ path: '/login' })
           NProgress.done()
         }
       });

+ 1 - 0
Strides-Admin/src/styles/index.scss

@@ -186,6 +186,7 @@ aside {
 .link-type:focus {
   color: #337ab7;
   cursor: pointer;
+  display: inline-block;
   position: relative;
   transition: all .3s;
   

+ 3 - 1
Strides-Admin/src/utils/auth.js

@@ -27,9 +27,11 @@ export function getAuthRoutes(back) {
       if (res.data) {
         setAuthRoutes(res.data)
         back(res.data)
+      } else {
+        back([])
       }
     }).catch(err => {
-      back(getAuthRoutesLocale())
+      back(false)
     })
   } else {
     back(getAuthRoutesLocale())

+ 3 - 2
Strides-Admin/src/views/charging/LoadBalanceView.vue

@@ -33,8 +33,8 @@
         </div>
       </div>
       <el-form class="view-content form">
-        <div class="flexcr">
-          <div class="section-title">LOAD BALANCING Settings</div>
+        <div class="section-title flexcr">
+          LOAD BALANCING Settings
           <div class="section-sub-title" v-if="detailData.siteName">(<b>Site Name:</b> {{detailData.siteName}})</div>
         </div>
         <Balancing v-model="balancingForm" :isEdit="true"/>
@@ -217,6 +217,7 @@ export default {
     font-size: 15px;
     line-height: 24px;
     padding-left: 10px;
+    font-weight: normal;
     font-family: sans-serif;
     text-transform: uppercase;
   }

+ 2 - 1
Strides-Admin/src/views/rate-base/index.vue

@@ -74,7 +74,8 @@
         <template v-slot="{ row }">
           <el-dropdown
             class="action-dropdown"
-            @command="(v) => handleCommand(v, row)">
+            @command="(v) => handleCommand(v, row)"
+            v-if="row.dataStatus != 'Inactive'">
             <i class="el-icon-more icon-action"></i>
             <el-dropdown-menu slot="dropdown">
               <el-dropdown-item

+ 2 - 1
Strides-Admin/src/views/rate-dynamic/index.vue

@@ -74,7 +74,8 @@
         <template v-slot="{ row }">
           <el-dropdown
             class="action-dropdown"
-            @command="(v) => handleCommand(v, row)">
+            @command="(v) => handleCommand(v, row)"
+            v-if="row.dataStatus != 'Inactive'">
             <i class="el-icon-more icon-action"></i>
             <el-dropdown-menu slot="dropdown">
               <el-dropdown-item

+ 310 - 0
Strides-Admin/src/views/site-label/assignment.vue

@@ -0,0 +1,310 @@
+<template>
+  <el-dialog
+    :title="title"
+    :visible="visible"
+    :before-close="onHide"
+    class="assign-label-dialog">
+    <div class="filter-container filter-view">
+      <el-select
+        style="min-width: 70px; max-width: 120px;"
+        clearable
+        v-model="filter.pageVo.assignmentStatus"
+        placeholder="Status"
+        @change="onSearch">
+        <el-option
+          v-for="(item, index) in statusOptions"
+          :key="index"
+          :label="item"
+          :value="item"/>
+      </el-select>
+      <div style="flex: 1; min-width: 150px; max-width: 300px;">
+        <el-input
+          clearable
+          v-model="filter.pageVo.criteria"
+          placeholder="Search by Site Name or Service Provider"
+          @keyup.enter.native="onSearch"/>
+      </div>
+      <el-button
+        type="primary"
+        @click="onSearch">
+        Search
+      </el-button>
+    </div>
+    <div class="assign-label-actions">
+      <el-button
+        type="danger"
+        :disabled="selectRow.length == 0"
+        :loading="loading.unassign"
+        @click="onClickUnassign">
+        Batch Un-assign
+      </el-button>
+      <el-button
+        type="accent"
+        :disabled="selectRow.length == 0"
+        :loading="loading.assign"
+        @click="onClickAssign">
+        Batch Assign
+      </el-button>
+    </div>
+    <div class="table-view" v-loading="table.loading">
+      <el-table
+        :data="table.data"
+        height="100%"
+        class="no-border"
+        @selection-change="changeSelection">
+        <el-table-column
+          align="center"
+          label="Site Name"
+          prop="siteName"
+          min-width="120"/>
+        <el-table-column
+          align="center"
+          label="Address"
+          prop="address"
+          min-width="120"/>
+        <el-table-column
+          align="center"
+          label="Service Provider"
+          min-width="140">
+          <template slot-scope="{row}">
+            <div v-for="item in row.serviceProviders" :key="item">{{item}}</div>
+          </template>
+        </el-table-column>
+        <el-table-column
+          align="center"
+          label="Assignment Status"
+          prop="assignmentStatus"
+          min-width="120"/>
+        <el-table-column
+          align="center"
+          label="Select"
+          type="selection"/>
+      </el-table>
+    </div>
+    <div class="center" style="margin-bottom: -20px;">
+      <Pagination
+        v-show="table.total"
+        :total="table.total"
+        :page.sync="filter.pageNo"
+        :limit.sync="filter.pageSize"
+        @pagination="getTableData"/>
+    </div>
+  </el-dialog>
+</template>
+
+<script>
+import api from '../../http/api/site.js'
+import Pagination from '@/components/Pagination'
+export default {
+  name: "AssignmentSiteLabel",
+  props: {
+    visible: {
+      type: Boolean,
+      default: false
+    },
+    item: {
+      type: Object,
+      default: () => ({})
+    }
+  },
+  data() {
+    return {
+      filter: {
+        pageSize: 10,
+        pageNo: 1,
+        pageVo: {
+          criteria: "",
+          siteLabelId: "",
+          assignmentStatus: ""
+        }
+      },
+      table: {
+        data: [],
+        total: 0,
+        loading: false
+      },
+      loading: {
+        assign: false,
+        unassign: false
+      },
+      statusOptions: [],
+      selectRow: []
+    };
+  },
+  components: {Pagination},
+  computed: {
+    title() {
+      return "ASSIGN SITES (Label: " + this.item.siteLabelName + ")" 
+    }
+  },
+  watch: {
+    visible: {
+      handler(n, o) {
+        console.log("watch.visible", n, o);
+        if (n) {
+          this.filter.pageVo.siteLabelId = this.item.siteLabelId;
+          this.onSearch()
+        }
+      }
+    }
+  },
+  mounted() {
+    this.getStatusOptions();
+  },
+  methods: {
+    onHide() {
+      this.$emit("hide", true);
+    },
+    onSearch() {
+      this.filter.pageNo = 1;
+      this.getTableData();
+    },
+    getStatusOptions() {
+      api.getAssignStatusOptions().then(res => {
+        if (res.data) {
+          this.statusOptions = res.data
+        }
+      }).catch(error => {
+        this.$message({
+          type: 'error',
+          message: error
+        })
+      })
+    },
+    getTableData() {
+      api.getLabelAssignPages(this.filter).then(res => {
+        if (res.total && res.data) {
+          this.table.total = res.total;
+          this.table.data = res.data;
+        } else {
+          this.table.total = 0;
+          this.table.data = [];
+        }
+        this.table.loading = false;
+      }).catch(error => {
+        this.$message({
+          type: 'error',
+          message: error
+        })
+        this.table.total = 0;
+        this.table.data = [];
+        this.table.loading = false;
+      })
+    },
+    changeSelection(val) {
+      this.selectRow = val;
+    },
+    getSelectIds() {
+      const ids = [];
+      this.selectRow.forEach(item => {
+        ids.push(item.sitePk)
+      })
+      return ids;
+    },
+    onClickAssign() {
+      const params = {
+        siteLabelId: this.item.siteLabelId,
+        sitePks: this.getSelectIds()
+      }
+      this.loading.assign = true;
+      api.assignSiteLabel(params).then(res => {
+        this.$message({
+          type: 'success',
+          message: res.msg || "Success"
+        })
+        this.getTableData()
+      }).catch(error => {
+        this.$message({
+          type: 'error',
+          message: error
+        })
+      }).finally(() => {
+        this.loading.assign = false;
+      })
+    },
+    onClickUnassign() {
+      const params = {
+        siteLabelId: this.item.siteLabelId,
+        sitePks: this.getSelectIds()
+      }
+      this.loading.unassign = true;
+      api.unassignSiteLabel(params).then(res => {
+        this.$message({
+          type: 'success',
+          message: res.msg || "Success"
+        })
+        this.getTableData()
+      }).catch(error => {
+        this.$message({
+          type: 'error',
+          message: error
+        })
+      }).finally(() => {
+        this.loading.unassign = false;
+      })
+    },
+  }
+}
+</script>
+
+<style lang="scss" scoped>
+.assign-label-dialog
+  ::v-deep .el-dialog {
+    width: 65vw;
+    height: 90vh;
+    display: flex;
+    max-width: 1200px;
+    flex-direction: column;
+    margin-top: 5vh !important;
+  .el-dialog__header {
+    padding: 20px 20px 0;
+    font-weight: bold;
+  }
+  .el-dialog__body {
+    flex: 1;
+    padding: 20px;
+    display: flex;
+    overflow: hidden;
+    flex-direction: column;
+  }
+  @media screen and (max-width: 1200px) {
+    & {
+      width: 70vw;
+    }
+  }
+  @media screen and (max-width: 1000px) {
+    & {
+      width: 80vw;
+    }
+  }
+  @media screen and (max-width: 800px) {
+    & {
+      width: 90vw;
+    }
+  }
+  @media screen and (max-width: 700px) {
+    & {
+      width: 99vw;
+    }
+  }
+  @media screen and (max-width: 320px) {
+    & {
+      width: 100%;
+      min-width: 300px;
+    }
+  }
+}
+.assign-label-dialog .table-view {
+  flex: 1;
+  overflow-y: auto;
+  padding-top: 10px;
+  margin-bottom: -10px;
+}
+.assign-label-actions {
+  display: flex;
+  padding-top: 5px;
+  flex-wrap: wrap-reverse;
+  align-items: center;
+  justify-content: flex-end;
+}
+</style>

+ 191 - 0
Strides-Admin/src/views/site-label/detail.vue

@@ -0,0 +1,191 @@
+<template>
+  <el-dialog
+    class="dialog-site-label"
+    :visible="visible"
+    :before-close="e => hideDialog(false)"
+    :title="isEdit ? 'Edit Label' : 'Add Label'">
+    <el-form
+      ref="slbForm"
+      :model="form"
+      :rules="rules"
+      label-position="top"
+      v-loading="initial">
+      <div class="form-row">
+        <el-form-item
+          prop="siteLabelName"
+          class="form-item"
+          label="Label Name:">
+          <el-input
+            v-model="form.siteLabelName"
+            class="flex-item"
+            maxlength="30"/>
+        </el-form-item>
+      </div>
+      <div class="flexcc" style="margin-top: 80px;">
+        <el-button
+          class="button"
+          type="primary"
+          :loading="loading"
+          @click="onFormSave">
+          <span style="padding: 0 20px;">SAVE</span>
+        </el-button>
+      </div>
+    </el-form>
+  </el-dialog>
+</template>
+
+<script>
+import api from '../../http/api/site.js'
+
+export default {
+  name: "SiteLabelDetail",
+  props: {
+    visible: {
+      type: Boolean,
+      default: false
+    },
+    isEdit: {
+      type: Boolean,
+      default: false
+    },
+    id: {
+      type: String|Number,
+      default: ''
+    }
+  },
+  data() {
+    return {
+      initial: false,
+      loading: false,
+      form: {
+        siteLabelId: "",
+        siteLabelName: ""
+      },
+      rules: {
+        siteLabelName: {
+          required: true,
+          message: "Label name is required",
+          trigger: "blur"
+        }
+      }
+    };
+  },
+  watch: {
+    id(n, o) {
+      if (this.visible && n) {
+        this.$nextTick(() => {
+          this.getLabelInfo();
+        })
+      }
+    }
+  },
+  methods: {
+    init() {
+      this.form = {
+        siteLabelId: "",
+        siteLabelName: ""
+      }
+      this.loading = false;
+      this.$nextTick(() => {
+        if (this.$refs['slbForm']) {
+          this.$refs['slbForm'].clearValidate();
+        }
+      })
+    },
+    hideDialog(success) {
+      this.init();
+      this.$emit("hide", success || false);
+    },
+    getLabelInfo() {
+      api.viewSiteLabel(this.id).then(res => {
+        if (res.data) {
+          this.form = res.data
+        }
+      }).catch(err => {
+        this.$message({
+          type: 'error',
+          message: err
+        })
+      }).finally(() => {
+        this.initial = false;
+      })
+    },
+    onFormSave() {
+      this.$refs['slbForm'].validate((valid) => {
+        if (valid) {
+          this.isEdit ? this.updateLabel() : this.addLabel();
+        }
+      })
+    },
+    addLabel() {
+      this.loading = true;
+      api.addSiteLabel(this.form).then(res => {
+        this.$message({
+          type: 'success',
+          message: "Add successfully"
+        });
+        this.hideDialog(true);
+      }).catch(err => {
+        this.loading = false;
+        this.$message({
+          type: 'error',
+          message: err
+        })
+      });
+    },
+    updateLabel() {
+      this.loading = true;
+      api.updateSiteLabel(this.form).then(res => {
+        this.$message({
+          type: 'success',
+          message: "Update successfully"
+        });
+        this.hideDialog(true);
+      }).catch(err => {
+        this.loading = false;
+        this.$message({
+          type: 'error',
+          message: err
+        })
+      });
+    }
+  }
+}
+</script>
+
+<style scoped>
+.dialog-site-label >>> .el-dialog {
+  width: 100%;
+  max-width: 480px;
+}
+.dialog-site-label >>> .el-form {
+  padding-right: 10px;
+}
+.form-row {
+  display: flex;
+  flex-wrap: wrap;
+  padding: 0 0 10px;
+}
+.form-item {
+  flex: 1;
+  margin-left: 10px;
+  margin-bottom: 10px;
+}
+.form-item >>> label {
+  padding: 0;
+}
+.flex-item {
+  min-width: 200px;
+  max-width: 450px;
+}
+.flex-item2 {
+  width: 100%;
+  min-width: 200px;
+}
+@media screen and (max-width: 500px) {
+  .flex-item {
+    width: 100%;
+    max-width: none;
+  }
+}
+</style>

+ 241 - 0
Strides-Admin/src/views/site-label/index.vue

@@ -0,0 +1,241 @@
+<template>
+  <div class="app-container">
+    <div class="filter-container">
+      <div class="filter-view">
+        <el-input
+          class="filter-input"
+          v-model="filters.pageVo.criteria"
+          placeholder="Search by Label Name"
+          prefix-icon="el-icon-search"
+          clearable/>
+        <div>
+          <el-button
+            @click="onClickSearch"
+            icon="el-icon-search"
+            type="primary">
+            Search
+          </el-button>
+        </div>
+        <div
+          class="filter-flex-button"
+          v-if="!$route.meta.onlyView">
+          <el-button
+            icon="el-icon-plus"
+            type="primary"
+            @click="onClickAdd">
+            Add Label
+          </el-button>
+        </div>
+      </div>
+    </div>
+    <el-table
+      v-loading="loading"
+      :data="table.list">
+      <el-table-column
+        align="center"
+        label="Label ID"
+        prop="userPk"
+        min-width="120">
+        <template slot-scope="{row}" >
+          <span
+            class="link-type"
+            @click="onClickEdit(row)"
+            v-if="!$route.meta.onlyView">
+            {{ row.siteLabelId }}
+          </span>
+          <span v-else>{{ row.siteLabelId }}</span>
+        </template>
+      </el-table-column>
+      <el-table-column
+        align="center"
+        label="Label Name"
+        prop="siteLabelName"
+        min-width="120"/>
+      <el-table-column
+        align="center"
+        label="No. of Sites"
+        prop="assignedSiteCount"
+        min-width="120"/>
+      <el-table-column
+        align="center"
+        label="Status"
+        prop="dataStatus"
+        min-width="120"/>
+      <el-table-column
+        align="center"
+        label="Update Time"
+        prop="updateTime"
+        min-width="120"/>
+      <el-table-column
+        align="center"
+        label="Action"
+        min-width="70"
+        v-if="!$route.meta.onlyView">
+        <template v-slot="{ row }">
+          <el-dropdown
+            class="action-dropdown"
+            @command="(v) => handleCommand(v, row)"
+            v-if="row.dataStatus != 'Inactive'">
+            <i class="el-icon-more icon-action"></i>
+            <el-dropdown-menu slot="dropdown">
+              <el-dropdown-item
+                command="assignSites">
+                Assign Sites
+              </el-dropdown-item>
+              <el-dropdown-item
+                command="onClickEdit">
+                Edit
+              </el-dropdown-item>
+              <el-dropdown-item
+                command="onClickDelete">
+                Delete
+              </el-dropdown-item>
+            </el-dropdown-menu>
+          </el-dropdown>
+        </template>
+      </el-table-column>
+    </el-table>
+    <div class="right">
+      <pagination
+        v-show="table.total > 0"
+        :total="table.total"
+        :page.sync="filters.pageNo"
+        :limit.sync="filters.pageSize"
+        @pagination="getTableData" />
+    </div>
+    <DialogDetail
+      v-bind="dialogAction"
+      @hide="hideActionDialog"/>
+    <DialogAssignment
+      v-bind="dialogAssign"
+      @hide="hideAssignDialog"/>
+  </div>
+</template>
+
+<script>
+import TableAction from '@/components/TableAction.vue'
+import Pagination from '@/components/Pagination'
+import DialogDetail from './detail.vue'
+import DialogAssignment from './assignment.vue'
+import api from '../../http/api/site.js'
+export default {
+  data() {
+    return {
+      loading: false,
+      filters: {
+        pageNo: 1,
+        pageSize: 10,
+        pageVo: {
+          criteria: ""
+        }
+      },
+      table: {
+        list: [],
+        total: 0
+      },
+      dialogAssign: {
+        item: {},
+        visible: false
+      },
+      dialogAction: {
+        id: "",
+        isEdit: false,
+        visible: false
+      }
+    };
+  },
+  components: { Pagination, TableAction,DialogDetail,DialogAssignment },
+  created() {
+    this.onClickSearch()
+  },
+  methods: {
+    onClickSearch() {
+      this.filters.pageNo = 1
+      this.getTableData()
+    },
+    getTableData() {
+      this.loading = true;
+      api.getLabelPages(this.filters).then(res => {
+        if (res.data && res.total) {
+          this.table.total = res.total;
+          this.table.list = res.data;
+        }
+      }).catch(err => {
+        this.$message({
+          type: 'error',
+          message: err
+        })
+        this.table.total = 0;
+        this.table.list = [];
+      }).finally(() => {
+        this.loading = false;
+      })
+    },
+    handleCommand(cb, item) {
+      this[cb](item)
+    },
+    onClickAdd() {
+      this.dialogAction.id = "";
+      this.dialogAction.isEdit = false;
+      this.dialogAction.visible = true;
+    },
+    onClickEdit(row) {
+      this.dialogAction.id = row.siteLabelId;
+      this.dialogAction.isEdit = true;
+      this.dialogAction.visible = true;
+    },
+    onClickDelete(row) {
+      this.$confirm('Are you sure you want to delete this label?', 'Delete', {
+        confirmButtonText: 'OK',
+        cancelButtonText: 'Cancel',
+        type: 'warning'
+      }).then(res => {
+        this.deleteSiteLabel(row.siteLabelId);
+      })
+    },
+    deleteSiteLabel(id) {
+      this.loading = true;
+      api.deleteSiteLabel(id).then(res => {
+        this.$message({
+          type: 'success',
+          message: "Delete success."
+        })
+        this.getTableData()
+      }).catch(err => {
+        this.$message({
+          type: 'error',
+          message: err
+        })
+        this.loading = false;
+      })
+    },
+    assignSites(row) {
+      if (row) {
+        this.dialogAssign.item = row;
+        this.dialogAssign.visible = true;
+      }
+    },
+    hideActionDialog(e) {
+      this.dialogAction.id = "";
+      this.dialogAction.visible = false;
+      if (e) {
+        this.getTableData();
+      }
+    },
+    hideAssignDialog(e) {
+      this.dialogAssign.item = {};
+      this.dialogAssign.visible = false;
+      if (e) {
+        this.getTableData();
+      }
+    }
+  }
+}
+</script>
+
+<style scoped>
+.filter-input {
+  min-width: 100px;
+  max-width: 300px;
+}
+</style>