Просмотр исходного кода

Add approval logic to monthly credit limit.
https://dev.wormwood.com.sg/zentao/task-view-359.html

vbea 1 год назад
Родитель
Сommit
033f0f0b02

+ 3 - 0
Strides-Admin/src/api/apiBase.js

@@ -12,6 +12,9 @@ const apiBase = {
   getDataStatusOptions() {
     return get(prefix + "data-status-select")
   },
+  getAuditStatusOptions() {
+    return get(prefix + "audit-status-select")
+  },
   getUserGroupOptions(groupType) {
     return get(prefix + "group-select", {groupType: groupType})
   },

+ 6 - 0
Strides-Admin/src/api/limit.js

@@ -35,6 +35,12 @@ const limit = {
   },
   unassignCreditLimitPlanUser(params) {
     return post(prefix + "unassign-group-credit-limit-plan-user", params)
+  },
+  approveCreditLimit(data) {
+    return post(prefix + "group-credit-limit-approve", data)
+  },
+  rejectCreditLimit(data) {
+    return post(prefix + "group-credit-limit-reject", data)
   }
 }
 

+ 1 - 1
Strides-Admin/src/views/financial/CreditActionDialog.vue

@@ -94,7 +94,7 @@
           class="input-text"
           v-model="form.creditActionRemarks"
           type="textarea"
-          maxlength="300"
+          maxlength="1000"
           :readonly="isApprove"
           :autosize="autoSize"/>
       </el-form-item>

+ 321 - 0
Strides-Admin/src/views/limit2/approval.vue

@@ -0,0 +1,321 @@
+<template>
+  <el-dialog
+    title="VIEW APPROVAL"
+    :visible="visible"
+    :before-close="e => hideDialog(false)"
+    :close-on-click-modal="!isApproval"
+    class="approval-limit-dialog">
+    <el-form
+      ref="apvForm"
+      :model="form"
+      :rules="rules"
+      label-width="190px"
+      label-position="right"
+      v-loading="loading.initial">
+      <el-form-item
+        prop="userName"
+        class="form-item"
+        label="Credit Limit Amount:">
+        <label class="flex-item">{{data.creditLimitAmount}}</label>
+      </el-form-item>
+      <el-form-item
+        prop="userName"
+        class="form-item"
+        label="Approved users:">
+        <label class="flex-item">{{data.approvedUsers}}</label>
+      </el-form-item>
+      <el-form-item
+        prop="userName"
+        class="form-item"
+        label="No. of Group Plan Users:">
+        <label class="flex-item">{{data.approvedUsers}}</label>
+      </el-form-item>
+      <el-form-item
+        prop="userName"
+        class="form-item"
+        label="Individual Plan Amount:">
+        <label class="flex-item">{{data.individualCredit}}</label>
+      </el-form-item>
+      <el-form-item
+        prop="userName"
+        class="form-item"
+        label="No. of Individual Plan Users:">
+        <label class="flex-item">{{data.individualApprovedUsers}}</label>
+      </el-form-item>
+      <el-form-item
+        prop="userName"
+        class="form-item"
+        label="Status:">
+        <label class="flex-item" :class="'status-' + data.creditLimitAudit.auditStatusName">{{data.creditLimitAudit.auditStatusName}}</label>
+      </el-form-item>
+      <el-form-item
+        prop="userName"
+        class="form-item"
+        label="Requester:">
+        <label class="flex-item">{{data.requester}}</label>
+      </el-form-item>
+      <el-form-item
+        prop="userName"
+        class="form-item"
+        label="Role:">
+        <label class="flex-item">{{data.requesterRoleName}}</label>
+      </el-form-item>
+      <el-form-item
+        prop="auditRemark"
+        class="form-item"
+        label-width="0">
+        <div>
+          <label>Remarks:</label>
+          <el-input
+            class="input-text"
+            v-model="form.auditRemark"
+            type="textarea"
+            maxlength="1000"
+            :readonly="!isApproval"
+            :autosize="autoSize"/>
+        </div>
+      </el-form-item>
+      <div
+        class="flexcc"
+        style="padding-top: 20px;"
+        v-if="isApproval">
+        <el-button
+          class="cancel-button button-reject"
+          @click="onClickApprove(false)"
+          :loading="loading.reject">
+          REJECT
+        </el-button>
+        <el-button
+          type="accent"
+          @click="onClickApprove(true)"
+          :disabled="loading.approve">
+          APPROVE
+        </el-button>
+      </div>
+      <div
+        class="flexcc"
+        style="padding-top: 20px;"
+        v-else>
+        <el-button
+          class="cancel-button"
+          @click="hideDialog(false)">
+          CLOSE
+        </el-button>
+      </div>
+    </el-form>
+  </el-dialog>
+</template>
+
+<script>
+import limit from '@/api/limit';
+export default {
+  name: "LimitApproval",
+  props: {
+    visible: {
+      type: Boolean,
+      default: false
+    },
+    id: {
+      type: Number|String,
+      default: ""
+    },
+  },
+  data() {
+    return {
+      loading: {
+        initial: false,
+        approve: false,
+        reject: false
+      },
+      data: {
+        creditLimitAudit: {}
+      },
+      form: {
+        groupCreditPk: undefined,
+        auditRemark: "",
+      },
+      rules: {
+        auditRemark: {
+          required: true,
+          trigger: 'blur',
+          message: 'Please input remarks'
+        }
+      },
+      isApproval: false,
+      autoSize: {
+        minRows: 3,
+        maxRows: 8
+      }
+    };
+  },
+  watch: {
+    id(n, o) {
+      if (this.visible && n) {
+        this.$nextTick(() => {
+          this.form.groupCreditPk = this.id;
+          this.getLimitInfo();
+        });
+      }
+    }
+  },
+  mounted() {
+    
+  },
+  methods: {
+    hideDialog(success) {
+      this.init();
+      this.$emit("hide", success || false);
+    },
+    init() {
+      this.form = {
+        groupCreditPk: undefined,
+        auditRemark: ""
+      }
+      this.isApproval = false;
+      this.loading.initial = false;
+      this.$nextTick(() => {
+        if (this.$refs['apvForm']) {
+          this.$refs['apvForm'].clearValidate();
+        }
+      })
+    },
+    getLimitInfo() {
+      this.loading.initial = true;
+      limit.viewCreditLimit(this.id).then(res => {
+        if (res.data) {
+          this.data = res.data
+          this.data.auditStatusName 
+          if (this.data.creditLimitAudit) {
+            this.isApproval = (this.data.creditLimitAudit.auditStatusName == 'Pending')
+            if (this.data.creditLimitAudit.auditRemark) {
+              this.form.auditRemark = this.data.creditLimitAudit.auditRemark
+            }
+          }
+        }
+      }).catch(err => {
+        this.$message({
+          message: err,
+          type: 'error'
+        })
+      }).finally(() => {
+        this.loading.initial = false;
+      });
+    },
+    onClickApprove(Approved) {
+      this.$refs.apvForm.validate((valid) => {
+        if (valid) {
+          this.$confirm("Confirm operation?", status, {
+            confirmButtonText: 'Confirm',
+            cancelButtonText: 'Cancel',
+            type: 'warning'
+          }).then(res => {
+            Approved
+            ? this.onApprove()
+            : this.onReject()
+          })
+        }
+      });
+    },
+    onApprove() {
+      this.loading.approve = true;
+      limit.approveCreditLimit(this.form).then(res => {
+        this.$message({
+          message: res.msg,
+          type: 'success',
+        });
+        this.hideDialog(true);
+      }).catch(err => {
+        this.$message.error(err);
+      }).finally(() => {
+        this.loading.approve = false;
+      })
+    },
+    onReject() {
+      this.loading.reject = true;
+      limit.rejectCreditLimit(this.form).then(res => {
+        this.$message({
+          message: res.msg,
+          type: 'success',
+        });
+        this.hideDialog(true);
+      }).catch(err => {
+        this.$message.error(err);
+      }).finally(() => {
+        this.loading.reject = false;
+      })
+    }
+  }
+}
+</script>
+
+<style scoped>
+.approval-limit-dialog {
+  display: flex;
+  align-items: center;
+  flex-direction: column;
+  justify-content: center;
+}
+.approval-limit-dialog >>> .el-dialog {
+  width: 100%;
+  max-width: 600px;
+  margin-top: 0 !important;
+}
+.approval-limit-dialog >>> .el-dialog__body {
+  padding: 10px 20px 30px;
+}
+.approval-limit-dialog >>> .el-form {
+  padding-right: 10px;
+}
+.form-row {
+  display: flex;
+  flex-wrap: wrap;
+  padding-bottom: 5px;
+}
+.form-item {
+  flex: 1;
+  margin-left: 10px;
+  margin-bottom: 10px;
+}
+.form-item >>> label {
+  padding: 0;
+}
+.flex-item {
+  width: 100%;
+  min-width: 200px;
+  max-width: 270px;
+}
+.flex-item2 {
+  width: 100%;
+  min-width: 200px;
+}
+label.flex-item {
+  color: #000;
+  font-size: 16px;
+  max-width: none;
+  padding-left: 15px;
+}
+.input-text >>> .el-textarea__inner {
+  font-family: sans-serif;
+}
+@media screen and (max-width: 500px) {
+  .flex-item {
+    width: 100%;
+    max-width: none;
+  }
+}
+label.status-Pending {
+  color: #ffa028;
+}
+label.status-Approved {
+  color: #07a378;
+}
+label.status-Rejected {
+  color: #ed3f3f;
+}
+.cancel-button.button-reject {
+  border-color: #ED3F3F;
+}
+.cancel-button.button-reject >>> span {
+  color: #ED3F3F;
+}
+</style>

+ 3 - 3
Strides-Admin/src/views/limit2/assignment.vue

@@ -3,7 +3,7 @@
     :title="title"
     :visible="visible"
     :before-close="onHide"
-    class="assign-label-dialog">
+    class="assign-limit-dialog">
     <div class="filter-container filter-view">
       <el-select
         style="min-width: 70px; max-width: 120px;"
@@ -254,7 +254,7 @@ export default {
 </script>
 
 <style lang="scss" scoped>
-.assign-label-dialog
+.assign-limit-dialog
   ::v-deep .el-dialog {
     width: 65vw;
     height: 90vh;
@@ -300,7 +300,7 @@ export default {
     }
   }
 }
-.assign-label-dialog .table-view {
+.assign-limit-dialog .table-view {
   flex: 1;
   overflow-y: auto;
   padding-top: 10px;

+ 65 - 13
Strides-Admin/src/views/limit2/index.vue

@@ -3,7 +3,8 @@
     <div class="filter-container filter-view">
       <el-select
         class="filter-view-item"
-        v-model="filters.pageCriteria.dataStatus"
+        clearable
+        v-model="filters.pageCriteria.auditStatus"
         placeholder="Status"
         @change="toSearch">
         <el-option
@@ -45,7 +46,7 @@
           <span
             class="link-type"
             @click="editLimit(row)"
-            v-if="!$route.meta.onlyView">
+            v-if="!$route.meta.onlyView && row.auditStatusName != 'Approved'">
             {{ row.groupName }}
           </span>
           <span v-else>{{ row.groupName }}</span>
@@ -79,8 +80,17 @@
       <el-table-column
         align="center"
         label="Status"
-        prop="dataStatus"
-        min-width="80"/>
+        prop="auditStatusName"
+        min-width="90">
+        <template slot-scope="{row}">
+          <div
+            class="link-type"
+            :class="'status-' + row.auditStatusName"
+            @click="viewApproval(row)">
+            {{row.auditStatusName}}
+          </div>
+        </template>
+      </el-table-column>
       <el-table-column
         align="center"
         label="Action"
@@ -94,12 +104,16 @@
             <i class="el-icon-more icon-action"></i>
             <el-dropdown-menu slot="dropdown">
               <el-dropdown-item
-                command="editLimit">
+                command="editLimit"
+                v-if="row.auditStatusName != 'Approved'">
                 Edit
               </el-dropdown-item>
               <el-dropdown-item
-                command="deleteLimit"
-                v-if="row.dataStatus == 'Active'">
+                command="viewApproval">
+                View Approval
+              </el-dropdown-item>
+              <el-dropdown-item
+                command="deleteLimit">
                 Set Inactive
               </el-dropdown-item>
             </el-dropdown-menu>
@@ -115,13 +129,17 @@
         :limit.sync="filters.pageSize"
         @pagination="getTableData" />
     </div>
+    <approval
+      v-bind="approvalDialog"
+      @hide="hideApproval"/>
   </div>
 </template>
 
 <script>
 import apiBase from '@/api/apiBase';
 import limit from '@/api/limit';
-import Pagination from '@/components/Pagination'
+import Approval from './approval.vue';
+import Pagination from '@/components/Pagination';
 export default {
   data() {
     return {
@@ -135,13 +153,19 @@ export default {
         pageSize: 10,
         pageCriteria: {
           dataStatus: "A",
+          auditStatus: "",
           criteria: ""
         }
       },
-      statusOptions: []
+      statusOptions: [],
+      approvalDialog: {
+        id: "",
+        visible: false,
+        isApproval: false
+      }
     };
   },
-  components: { Pagination },
+  components: { Approval, Pagination },
   created() {
     this.getStatusOptions();
     this.toSearch();
@@ -152,7 +176,7 @@ export default {
       this.getTableData();
     },
     getStatusOptions() {
-      apiBase.getDataStatusOptions().then(res => {
+      apiBase.getAuditStatusOptions().then(res => {
         if (res.data && res.data.length > 0) {
           this.statusOptions = res.data
         }
@@ -194,6 +218,17 @@ export default {
         path: '/partnership-management/monthly-credit-limit/edit/' + row.groupCreditPk
       });
     },
+    viewApproval(row) {
+      this.approvalDialog.id = row.groupCreditPk;
+      this.approvalDialog.visible = true;
+    },
+    hideApproval(e) {
+      this.approvalDialog.id = "";
+      this.approvalDialog.visible = false;
+      if (e) {
+        this.getTableData();
+      }
+    },
     deleteLimit(row) {
       this.$confirm('Are you sure you want set inactive to this credit limit?', 'Set Inactive', {
         confirmButtonText: 'OK',
@@ -221,6 +256,23 @@ export default {
 }
 </script>
 
-<style scoped>
-
+<style lang="scss" scoped>
+.status-Pending {
+  color: #ffa028;
+  &::before {
+    background-color: #ffa028;
+  }
+}
+.status-Approved {
+  color: #07a378;
+  &::before {
+    background-color: #07a378;
+  }
+}
+.status-Rejected {
+  color: #ed3f3f;
+  &::before {
+    background-color: #ed3f3f;
+  }
+}
 </style>

+ 24 - 7
Strides-Admin/src/views/login/Password.vue

@@ -46,7 +46,7 @@
             autocomplete="on"
             clearable
             maxlength="32"
-            @input="applyStrength"/>
+            @input="text => applyStrength('', text)"/>
           <span class="show-pwd" @click="showPwd">
             <svg-icon :icon-class="passwordType === 'password' ? 'eye' : 'eye-open'" />
           </span>
@@ -59,7 +59,13 @@
             <span :class='strength >= item ? "strength" : "no-strength"'></span>
           </template>
         </div>
-        <div class="strength-desc">
+        <div class="strength-desc" v-if="enablePassword12">
+          You Password Must Have: <br/>
+          - 12 or more characters<br/>
+          - upper and lower case letters<br/>
+          - at least one number and special characters
+        </div>
+        <div class="strength-desc" v-else>
           You Password Must Have: <br/>
           - 8 or more characters<br/>
           - upper and lower case letters<br/>
@@ -94,6 +100,7 @@
 </template>
 
 <script>
+  import settings from '../../settings.js'
   export default {
     name: "Password",
     props: {
@@ -120,7 +127,7 @@
           password: [{
             required: true,
             trigger: 'blur',
-            message: "Please type password"
+            validator: this.applyStrength
           }],
           password2: [{
             required: true,
@@ -132,7 +139,8 @@
           }]
         },
         passwordType: "password",
-        strength: 0
+        strength: 0,
+        enablePassword12: settings.enablePassword12
       };
     },
     mounted() {
@@ -152,10 +160,10 @@
           this.$refs.password.focus()
         })
       },
-      applyStrength(text) {
+      applyStrength(rule, text, callback) {
         var streng = 0;
         if (text) {
-          if (text.length >= 8) {
+          if (text.length >= (settings.enablePassword12 ? 12 : 8)) {
             streng += 1;
           }
           if (/[a-z]{1,}/.test(text)) {
@@ -165,11 +173,20 @@
             streng += 1;
           }
           if (/\d{1,}/.test(text)) {
-            streng += 1;
+            streng += (settings.enablePassword12 ? 1 : 2);
           }
           if (/\W{1,}/.test(text)) {
             streng += 1;
           }
+          if (callback) {
+            if (streng < 5) {
+              callback(new Error('The password is not strength'))
+            } else {
+              callback()
+            }
+          }
+        } else if (callback) {
+          callback(new Error('Please type password'))
         }
         this.strength = streng;
       },

+ 1 - 1
Strides-Admin/src/views/login/login.vue

@@ -223,7 +223,7 @@ export default {
   beforeMount() {
     this.$nextTick(() => {
       //const sss = document.getElementById("particles-js");
-      particlesJS('particles-js', animateJson);
+      //particlesJS('particles-js', animateJson);
     })
   },
   mounted() {