Quellcode durchsuchen

Enhancement Transaction v2
https://dev.wormwood.com.sg/zentao/task-view-482.html

vbea vor 1 Jahr
Ursprung
Commit
fc1a20b3ed

+ 18 - 1
Strides-Admin/src/http/api/transaction.js

@@ -1,12 +1,29 @@
 import {get, post, download} from '../http'
 
+const prefix = "dawn/api/v1/"
+
 const transaction = {
   getTransactionFilters: () => get('transaction/getTransactionFilters'),
+  getTransactionStatus() {
+    return get(prefix + "transaction/status")
+  },
   getTransactionPages: (params) => post('transaction/getTransactionPages', params),
+  getTransactionPagesV2(params) {
+    return post(prefix + 'transaction/pages', params)
+  },
   getTransactionDetail: (params) => get('transaction/getTransactionDetail', params),
+  getTransactionDetailV2(params) {
+    return get(prefix + 'transaction/details/' + params.chargingPk, params)
+  },
   downloadInvoice: (chargingPk) => download('transaction/downloadInvoice', {transactionPk: chargingPk}),
   getMeasurandOptions: () => get('transaction/getMeasurands'),
-  endTransaction: (params) => get('transaction/endTransaction', params)
+  getMeasurandOptionsV2() {
+    return get(prefix + 'transaction/measurands')
+  },
+  endTransaction: (params) => get('transaction/endTransaction', params),
+  endTransactionV2(params) {
+    return get(prefix + 'transaction/manual-settlement', params)
+  } 
 }
 
 export default transaction;

+ 34 - 3
Strides-Admin/src/router/ActivityRouter.js

@@ -2,7 +2,7 @@ import Layout from '@/layout'
 
 export default {
   path: '/station-activities',
-  redirect: '/station-activities/transactions',
+  redirect: 'noRedirect',
   component: Layout,
   alwaysShow: true,
   meta: {
@@ -22,6 +22,17 @@ export default {
         affix: false,
       }
     },
+    {
+      path: '/station-activities/transactions/v2',
+      component: () => import('@/views/transaction/transactions_v2'),
+      name: 'transactions',
+      meta: {
+        title: 'Transactions',
+        icon: 'sidebar-submenu-item',
+        activeIcon: 'sidebar-submenu-item-active',
+        affix: false,
+      }
+    },
     {
       path: '/station-activities/reservations',
       component: () => import('@/views/transaction/reservations'),
@@ -41,9 +52,29 @@ export default {
         title: 'View Transaction',
         icon: 'sidebar-submenu-item',
         activeIcon: 'sidebar-submenu-item-active',
-        activeMenu: '/station-activities/transactions'
+        activeMenu: '/station-activities/transactions',
+        parent: {
+          title: 'Transactions',
+          path: "/station-activities/transactions"
+        }
       },
       hidden: true
-    }
+    },
+    {
+      path: '/station-activities/transactions/v2/:id',
+      component: () => import('@/views/transaction/view_transaction_v2'),
+      name: 'transactions',
+      meta: {
+        title: 'View Transaction',
+        icon: 'sidebar-submenu-item',
+        activeIcon: 'sidebar-submenu-item-active',
+        activeMenu: '/station-activities/transactions/v2',
+        parent: {
+          title: 'Transactions',
+          path: "/station-activities/transactions/v2"
+        }
+      },
+      hidden: true
+    },
   ]
 }

+ 521 - 0
Strides-Admin/src/views/transaction/transactions_v2.vue

@@ -0,0 +1,521 @@
+<template>
+  <div class="app-container">
+    <div class="filter-container">
+      <el-form
+        :model="filter"
+        v-bind:inline="true"
+        label-position="left"
+        label-width="0px"
+        style="width: 100%;">
+        <div class="filter-view">
+          <div>
+            <el-date-picker
+              v-model="filter.pageCriteria.dateRange"
+              type="daterange"
+              range-separator="-"
+              :clearable="false"
+              start-placeholder="Start Date"
+              end-placeholder="End Date"
+              value-format="yyyy-MM-dd"
+              @change="handleFilter"/>
+          </div>
+          <el-select
+            class="filter-view-item"
+            v-model="filter.pageCriteria.transactionStatus"
+            placeholder="Charging Status"
+            @change="handleFilter"
+            clearable>
+            <el-option
+              v-for="(item, index) in filterOptions"
+              :key="index"
+              :label="item.value"
+              :value="item.key"/>
+          </el-select>
+          <el-select
+            class="filter-view-item"
+            v-model="filter.pageCriteria.chargingPlatform"
+            placeholder="Charging Platform"
+            @change="changeUserType"
+            clearable>
+            <el-option
+              v-for="item in chargePlatOptions"
+              :key="item"
+              :label="item"
+              :value="item" />
+          </el-select>
+          <el-select
+            class="filter-view-item"
+            v-model="filter.pageCriteria.userType"
+            placeholder="User Type"
+            @change="changeUserType"
+            clearable>
+            <el-option
+              v-for="item in userTypeOptions"
+              :key="item"
+              :label="item"
+              :value="item" />
+          </el-select>
+          <el-select
+            class="filter-view-item group-select"
+            :class="{hide: groupOptions.length == 0}"
+            v-model="filter.pageCriteria.groupPk"
+            placeholder="Group Name"
+            @change="handleFilter"
+            clearable>
+            <el-option
+              v-for="(item,index) in groupOptions"
+              :key="index"
+              :label="item.name"
+              :value="item.value"/>
+          </el-select>
+          <el-select
+            class="filter-view-item"
+            v-model="filter.pageCriteria.paymentMethod"
+            placeholder="Credit Type"
+            @change="handleFilter"
+            clearable>
+            <el-option
+              v-for="item in payTypeOptions"
+              :key="item.key"
+              :label="item.value"
+              :value="item.key" />
+          </el-select>
+          <div style="flex: 1; max-width: 500px;">
+            <el-input
+              v-model="filter.pageCriteria.criteria"
+              prefix-icon="el-icon-search"
+              placeholder="Search by Station ID, Vehicle, Transaction ID, Email, Carpark Code"
+              @keyup.enter.native="handleFilter"
+              @change="handleFilter"
+              clearable/>
+          </div>
+          <!-- <div>
+            <el-button
+              type="primary"
+              icon="el-icon-search"
+              @click="handleFilter">
+              Search
+            </el-button>
+          </div> -->
+        </div>
+      </el-form>
+    </div>
+    <el-table
+      v-loading="listLoading"
+      :data="tableList"
+      class="no-border"
+      style="width: 100%;min-height: 65vh;">
+      <el-table-column
+        min-width="120"
+        label="Transaction ID"
+        align="center">
+        <template slot-scope="{row}">
+          <span
+            class="link-type"
+            @click="viewTransiction(row)">
+            {{ row.transactionPk }}
+          </span>
+        </template>
+      </el-table-column>
+      <!--el-table-column
+        label="User ID"
+        align="center"
+        width="80">
+          <template slot-scope="{row}">
+            <span>{{ row.userPk }}</span>
+          </template>
+      </el-table-column-->
+      <el-table-column
+        label="Email"
+        align="center"
+        min-width="100">
+        <template slot-scope="{row}">
+          <span :title="row.email">{{ row.email }}</span>
+        </template>
+      </el-table-column>
+      <!--el-table-column
+        label="Phone No."
+        align="center">
+        <template slot-scope="{row}">
+          <span :title="row.phoneNo">{{ row.phoneNo }}</span>
+        </template>
+      </el-table-column-->
+      <el-table-column
+        label="Charging Platform"
+        align="center"
+        min-width="120">
+        <template slot-scope="{row}">
+          <span :title="row.chargingPlatform">{{ row.chargingPlatform }}</span>
+        </template>
+      </el-table-column>
+      <el-table-column
+        label="User Type"
+        align="center"
+        min-width="120">
+        <template slot-scope="{row}">
+          <span :title="row.userType">{{ row.userType }}</span>
+        </template>
+      </el-table-column>
+      <el-table-column
+        label="Credit Type"
+        prop="paymentMethod"
+        align="center"
+        min-width="120"/>
+      <!-- <el-table-column
+        label="Vehicle"
+        align="center"
+        prop="vehicle"
+        min-width="120">
+        <template slot-scope="{row}" v-if="row.vehicles">
+          <span
+            v-for="(item, idx) in row.vehicles"
+            :key="idx"
+            style="display: block;"
+            :title="item">
+            {{item}}
+          </span>
+        </template>
+      </el-table-column> -->
+      <el-table-column
+        label="Station ID"
+        align="center"
+        min-width="120">
+        <template slot-scope="{row}">
+          <div
+            class="link-detail"
+            @click="viewTransiction(row)"
+            :title="row.chargeBoxId">
+            {{ row.chargeBoxId }}
+          </div>
+        </template>
+      </el-table-column>
+      <el-table-column
+        label="Carpark Code"
+        prop="carParkCode"
+        align="center"
+        min-width="90"/>
+      <el-table-column
+        min-width="90"
+        label="Connector"
+        align="center">
+        <template slot-scope="{row}">
+          <span>{{ row.connectorId }}</span>
+        </template>
+      </el-table-column>
+      <el-table-column
+        label="Start Date/Time"
+        align="center"
+        min-width="140">
+        <template slot-scope="{row}">
+          <span :title="row.chargingStartTime">{{ row.chargingStartTime }}</span>
+        </template>
+      </el-table-column>
+      <el-table-column
+        label="End Date/Time"
+        align="center"
+        min-width="140">
+        <template slot-scope="{row}">
+          <span :title="row.chargingStopTime">{{ row.chargingStopTime  }}</span>
+        </template>
+      </el-table-column>
+      <el-table-column
+        label="Power"
+        align="center"
+        min-width="120">
+        <template slot-scope="{row}">
+          <span :title="row.totalPowerStr">{{ row.totalPowerStr  }}</span>
+        </template>
+      </el-table-column>
+      <el-table-column
+        label="Rate"
+        align="center"
+        min-width="150">
+        <template slot-scope="{row}" v-if="row.rate && row.rate.rateItems">
+          <span
+            v-for="(item, idx) in row.rate.rateItems"
+            :key="idx"
+            style="display: block;"
+            :title="item">
+            {{item.discountRate}}
+          </span>
+        </template>
+      </el-table-column>
+      <el-table-column
+        label="Duration"
+        align="center"
+        min-width="100">
+        <template slot-scope="{row}">
+          <span :title="row.duration">{{ row.duration }}</span>
+        </template>
+      </el-table-column>
+      <!-- <el-table-column
+        label="Total Min"
+        align="center"
+        min-width="100">
+        <template slot-scope="{row}">
+          <span :title="row.totalMin">{{ row.totalMin }}</span>
+        </template>
+      </el-table-column> -->
+      <el-table-column
+        label="Charges"
+        align="center"
+        min-width="100">
+        <template slot-scope="{row}">
+          <span :title="row.charge">{{ row.charge }}</span>
+        </template>
+      </el-table-column>
+      <!-- <el-table-column
+        label="Invoice"
+        align="center"
+        min-width="80">
+        <template slot-scope="{row}">
+          <i
+            class="download-invoice el-icon-download"
+            title="Download"
+            @click="downloadPdf(row.transactionPk)"></i>
+        </template>
+      </el-table-column> -->
+    </el-table>
+    <div class="right">
+      <pagination
+        v-show="total > 0"
+        :total="total"
+        :page.sync="filter.pageNum"
+        :limit.sync="filter.pageSize"
+        @pagination="getList" />
+    </div>
+  </div>
+</template>
+
+<script>
+import Pagination from '@/components/Pagination'
+import api from '../../http/api/transaction'
+import apiUser from '../../http/api/apiUser.js'
+import financial from '@/http/api/financial'
+import { parseTime } from '@/utils'
+
+export default {
+  components: { Pagination },
+  data() {
+    return {
+      filter: {
+        pageNum: 1,
+        pageSize: 10,
+        pageCriteria: {
+          groupPk: "",
+          criteria: "",
+          userType: "",
+          dateRange: [],
+          paymentMethod: "",
+          chargingPlatform : "",
+          transactionStatus: ""
+        }
+      },
+      groupOptions: [],
+      filterOptions: [],
+      payTypeOptions: [],
+      userTypeOptions: [],
+      chargePlatOptions: [],
+      listLoading: true,
+      tableList: [],
+      total: 0,
+      listQuery: {
+        page: 1,
+        limit: 10,
+      }
+    }
+  },
+  created() {
+    this.init();
+    this.getUserTypeOption();
+    this.getPaymentTypeOption();
+    this.getChargePlatformList();
+    this.getFilters();
+  },
+  methods: {
+    init() {
+      const date = new Date();
+      const year = date.getFullYear();
+      const month = date.getMonth();
+      const firstDay = new Date(year, month, 1);
+      //const lastDay = new Date(year, month + 1, 0);
+      this.filter.pageCriteria.dateRange = [
+        parseTime(firstDay, "{y}-{m}-{d}"),
+        parseTime(date, "{y}-{m}-{d}")
+      ]
+    },
+    handleFilter() {
+      this.filter.pageNum = 1;
+      this.getList();
+    },
+    getFilters() {
+      api.getTransactionStatus().then(res => {
+        if (res.data) {
+          this.filterOptions = res.data
+          this.filter.pageCriteria.transactionStatus = res.data[0].value
+          this.getList()
+        }
+      })
+    },
+    changeUserType() {
+      this.filter.pageCriteria.groupPk = "";
+      this.getGroupOptions();
+    },
+    getGroupOptions() {
+      if (this.filter.pageCriteria.userType == "") {
+        this.groupOptions = [];
+        this.getList();
+        return;
+      }
+      this.listLoading = true;
+      apiUser.getGroupByType(this.filter.pageCriteria.userType).then(res => {
+        if (res.data) {
+          this.groupOptions = res.data
+        } else {
+          this.groupOptions = []
+        }
+      }).catch(err => {
+        this.$message({
+          message: err,
+          type: 'error'
+        })
+        this.groupOptions = []
+      }).finally(() => {
+        setTimeout(() => {
+          this.getList();
+        }, 100)
+      })
+    },
+    getUserTypeOption() {
+      apiUser.getUserTypeOptions().then(res => {
+        if (res.data) {
+          this.userTypeOptions = res.data
+        }
+      }).catch(error => {
+        this.$message({
+          message: error,
+          type: 'error'
+        })
+      })
+    },
+    getPaymentTypeOption() {
+      financial.getPaymentTypeOptions().then(res => {
+        if (res.data) {
+          this.payTypeOptions = res.data
+        }
+      }).catch(error => {
+        this.$message({
+          message: error,
+          type: 'error'
+        })
+      })
+    },
+    getChargePlatformList() {
+      financial.getChargePlatformOptions().then(res => {
+        if (res.data) {
+          this.chargePlatOptions = res.data
+        }
+      }).catch(error => {
+        this.$message({
+          message: error,
+          type: 'error'
+        })
+      })
+    },
+    getList() {
+      this.listLoading = true;
+      api.getTransactionPagesV2(this.filter).then(res => {
+        if (res.data.records && res.data.totalRow) {
+          this.tableList = res.data.records;
+          this.total = res.data.totalRow;
+        } else {
+          this.tableList = []
+          this.total = 0
+        }
+      }).catch(err => {
+        this.$message({
+          type: "error",
+          message: err
+        })
+        this.tableList = []
+        this.total = 0
+      }).finally(() => {
+        this.listLoading = false
+      })
+    },
+    async downloadPdf(id) {
+      try {
+        this.listLoading = true;
+        const res = await api.downloadInvoice(id);
+        if (res && res.size > 0) {
+          const name = 'T' + id + '-' + new Date().getTime() + '.pdf'
+          this.downloadFile(res, name);
+        } else {
+          this.$message.error('Empty files');
+        }
+      } catch (err) {
+        this.$message({
+          type: "error",
+          message: err
+        })
+      } finally {
+        this.listLoading = false;
+      }
+    },
+    downloadFile(res, fileName) {
+      const blob = new Blob([res], {
+        type: 'application/pdf;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)
+      }
+    },
+    viewTransiction(row) {
+      this.$router.push({
+        path: "/station-activities/transactions/v2/" + row.chargingPk
+      })
+    }
+  }
+}
+</script>
+<style scoped="scoped">
+  .table-link {
+    color: #333;
+    display: inline-block;
+    cursor: pointer;
+    white-space: nowrap;
+    text-decoration: underline;
+  }
+  .table-link:hover {
+    color: #1290BF;
+    text-decoration: none;
+  }
+  .el-table >>> tr div {
+    word-break: break-word;
+  }
+  .download-invoice {
+    color: #0000FF;
+    cursor: pointer;
+  }
+  .group-select {
+    overflow: hidden;
+    transition: all .3s;
+  }
+  .group-select.hide {
+    margin: 0;
+    min-width: 0;
+    max-width: 0;
+  }
+</style>

+ 518 - 0
Strides-Admin/src/views/transaction/view_transaction_v2.vue

@@ -0,0 +1,518 @@
+<template>
+  <div class="card-container">
+    <div class="card-content">
+      <el-form
+        v-loading="loading"
+        label-width="130px"
+        label-position="left">
+        <div class="section-title">Transaction Overview</div>
+        <el-row :gutter="20">
+          <el-col :xs="24" :md="12">
+            <el-form-item
+              label="Transaction ID:">
+              <el-input
+                class="add-text"
+                v-model="details.transactionPk"
+                readonly/>
+            </el-form-item>
+          </el-col>
+          <el-col :xs="24" :md="12">
+            <el-form-item
+              label="Start Date/Time:">
+              <el-input
+                class="add-text"
+                v-model="details.chargingStartTime"
+                readonly/>
+            </el-form-item>
+          </el-col>
+        </el-row>
+        <el-row :gutter="20">
+          <el-col :xs="24" :md="12">
+            <el-form-item
+              label="User Id:">
+              <el-input
+                class="add-text"
+                v-model="details.userPk"
+                readonly/>
+            </el-form-item>
+          </el-col>
+          <el-col :xs="24" :md="12">
+            <el-form-item
+              label="End Date/Time:">
+              <el-input
+                class="add-text"
+                v-model="details.chargingStopTime"
+                readonly/>
+            </el-form-item>
+          </el-col>
+        </el-row>
+        <el-row :gutter="20">
+          <el-col :xs="24" :md="12">
+            <el-form-item
+              label="Station ID:">
+              <el-input
+                class="add-text"
+                v-model="details.chargeBoxId"
+                readonly/>
+            </el-form-item>
+          </el-col>
+          <el-col :xs="24" :md="12">
+            <el-form-item
+              label="Stop Reason:">
+              <el-input
+                class="add-text"
+                v-model="details.stopReason"
+                readonly/>
+            </el-form-item>
+          </el-col>
+        </el-row>
+        <el-row :gutter="20">
+          <el-col :xs="24" :md="12">
+            <el-form-item
+              label="Connector ID:">
+              <el-input
+                class="add-text"
+                v-model="details.connectorId"
+                readonly/>
+            </el-form-item>
+          </el-col>
+          <el-col :xs="24" :md="12">
+            <el-form-item
+              label="Stop Event Actor:">
+              <el-input
+                class="add-text"
+                v-model="details.stopEventActor"
+                readonly/>
+            </el-form-item>
+          </el-col>
+        </el-row>
+        <div class="section-title">Charges</div>
+        <el-row :gutter="20">
+          <el-col :xs="24" :md="12">
+            <el-form-item
+              label="Duration:">
+              <el-input
+                class="add-text"
+                v-model="details.duration"
+                readonly/>
+            </el-form-item>
+          </el-col>
+          <el-col :xs="24" :md="12" v-if="details.rate">
+            <el-form-item
+              label="Rate:">
+              <div
+                v-if="details.rate.rateItems"
+                class="link-detail"
+                @click="showRatesDrawer">View {{details.rate.rateItems.length}} Rates</div>
+            </el-form-item>
+          </el-col>
+        </el-row>
+        <el-row :gutter="20">
+          <el-col :xs="24" :md="12">
+            <el-form-item
+              label="Total Power:">
+              <el-input
+                class="add-text"
+                v-model="details.totalPowerStr"
+                readonly/>
+            </el-form-item>
+          </el-col>
+          <el-col :xs="24" :md="12">
+            <el-form-item
+              label="Charges:">
+              <el-input
+                class="add-text"
+                v-model="details.charge"
+                readonly/>
+            </el-form-item>
+          </el-col>
+        </el-row>
+        <div class="flexcr" style="margin: 10px 0;">
+          <div class="section-title" style="margin: 20px 0;">Intermediate Meter Values</div>
+          <el-button
+            type="primary"
+            style="margin: 0 10px;"
+            v-if="!details.chargingStopTime || !details.totalPower"
+            @click="onClickEnd"
+            icon="el-icon-switch-button"
+            :loading="endLoading">
+            End Transaction
+          </el-button>
+          <el-button
+            v-else-if="false"
+            type="primary"
+            style="margin: 0 10px;"
+            icon="el-icon-download"
+            @click="downloadInvoice"
+            :loading="endLoading">
+            Download Invoice
+          </el-button>
+          <div class="flex1"></div>
+          <el-select v-model="measurand" @change="getDeatil">
+            <el-option
+              value=""
+              label="All"/>
+            <el-option
+              v-for="(item, index) in optionsMeasurand"
+              :key="index"
+              :value="item"
+              :label="item"/>
+          </el-select>
+        </div>
+        <el-table :data="listData">
+          <el-table-column
+            label="Timestamp"
+            align="center"
+            class-name="fixed-width"
+            min-width="130">
+              <template slot-scope="{row}">
+                <span>{{ row.timestamp }}</span>
+              </template>
+          </el-table-column>
+          <el-table-column
+            label="Value"
+            align="center"
+            min-width="80">
+              <template slot-scope="{row}">
+                <span>{{ row.value }}</span>
+              </template>
+          </el-table-column>
+          <el-table-column
+            label="Reading Context"
+            align="center"
+            class-name="fixed-width"
+            min-width="130">
+              <template slot-scope="{row}">
+                <span>{{ row.readingContext }}</span>
+              </template>
+          </el-table-column>
+          <el-table-column
+            label="Format"
+            align="center"
+            min-width="100">
+              <template slot-scope="{row}">
+                <span>{{ row.format }}</span>
+              </template>
+          </el-table-column>
+          <el-table-column
+            label="Measureand"
+            align="center"
+            class-name="fixed-width"
+            min-width="250">
+              <template slot-scope="{row}">
+                <span>{{ row.measureand }}</span>
+              </template>
+          </el-table-column>
+          <el-table-column
+            label="Location"
+            align="center"
+            min-width="100">
+              <template slot-scope="{row}">
+                <span>{{ row.location }}</span>
+              </template>
+          </el-table-column>
+          <el-table-column
+            label="Unit"
+            align="center"
+            min-width="80">
+              <template slot-scope="{row}">
+                <span>{{ row.unit }}</span>
+              </template>
+          </el-table-column>
+          <el-table-column
+            label="Phase"
+            align="center"
+            min-width="80">
+              <template slot-scope="{row}">
+                <span>{{ row.phase }}</span>
+              </template>
+          </el-table-column>
+        </el-table>
+        <div class="pagination" v-if="listData">
+          <Pagination
+            v-show="details.meters.length > 0"
+            :total="details.meters.length"
+            :page.sync="page"
+            :limit.sync="pageSize"
+            :autoScroll="false"
+            @pagination="handlePageChange" />
+        </div>
+      </el-form>
+    </div>
+    <el-drawer
+      :visible.sync="drawerRate"
+      direction="rtl">
+      <el-timeline>
+        <el-timeline-item
+          v-for="(item, index) in details.rate.rateItems"
+          size="large"
+          :key="index"
+          hide-timestamp>
+          <div>
+            <div
+              class="rate-unit-text"
+              v-if="details.rate.hasDiscount">
+              <span>Rate: {{item.discountRate}}</span>
+              <span class="rate-unit-discount">{{item.withoutDiscountRate}}</span>
+            </div>
+            <div
+              class="rate-unit-text"
+              v-else>Rate: {{item.discountRate}}</div>
+            <div class="rate-date-text">Start: {{item.startTime}}</div>
+            <div class="rate-date-text">&nbsp;End: {{item.endTime}}</div>
+          </div>
+        </el-timeline-item>
+      </el-timeline>
+    </el-drawer>
+  </div>
+</template>
+
+<script>
+  import Pagination from '@/components/Pagination'
+  import api from '../../http/api/transaction'
+  
+  export default {
+    components: { Pagination },
+    data() {
+      return {
+        page: 1,
+        pageSize: 10,
+        details: {},
+        listData: [],
+        loading: false,
+        measurand: "",
+        endLoading: false,
+        drawerRate: false,
+        optionsMeasurand: []
+      }
+    },
+    created() {
+      this.getMeasuOptions()
+      this.getDeatil()
+    },
+    methods: {
+      getMeasuOptions() {
+        api.getMeasurandOptions().then(res => {
+          if (res.data) {
+            this.optionsMeasurand = res.data
+          }
+        }).catch(err => {
+          
+        })
+      },
+      getDeatil() {
+        this.loading = true;
+        api.getTransactionDetailV2({
+          chargingPk: this.$route.params.id,
+          measurand: this.measurand
+        }).then(res => {
+          if (res.data) {
+            this.details = res.data
+            /*if (this.details.duration) {
+              this.details.duration = Number((this.details.duration * 60).toFixed(2))
+            }*/
+            if (this.details.meters && this.details.meters.length) {
+              this.handlePageChange()
+            } else {
+              this.listData = []
+            }
+          }
+        }).catch(err => {
+          this.$message({
+            type: 'error',
+            message: err
+          })
+        }).finally(() => {
+          this.loading = false
+        })
+      },
+      onClickEnd() {
+        this.$confirm('This operation only for settling abnormal transactions and will not end charging. Select Confirm to proceed.', 'Warning', {
+          confirmButtonText: 'Confirm',
+          cancelButtonText: 'Cancel',
+          type: 'warning'
+        }).then(res => {
+          this.endTransaction();
+        })
+      },
+      endTransaction() {
+        this.endLoading = true;
+        api.endTransactionV2({
+          transactionPk: this.details.transactionPk
+        }).then(res => {
+          this.$message({
+            type: 'success',
+            message: 'Operation Successfully!'
+          })
+          this.getDeatil()
+        }).catch(err => {
+          this.$message({
+            type: 'error',
+            message: err
+          })
+        }).finally(() => {
+          this.endLoading = false
+        })
+      },
+      downloadInvoice() {
+        this.endLoading = true;
+        api.downloadInvoice(this.$route.params.id).then(res => {
+          if (res && res.size > 0) {
+            const name = 'T' + this.$route.params.id + '-' + new Date().getTime() + '.pdf'
+            this.downloadFile(res, name);
+          } else {
+            this.$message.error('Empty files');
+          }
+        }).catch(err => {
+          this.$message({
+            type: 'error',
+            message: err
+          })
+        }).finally(() => {
+          this.endLoading = false
+        })
+      },
+      downloadFile(res, fileName) {
+        const blob = new Blob([res], {
+          type: 'application/pdf;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)
+        }
+      },
+      handlePageChange() {
+        this.listData = []
+        var i = (this.page - 1) * this.pageSize
+        var max = this.page * this.pageSize
+        if (max > this.details.meters.length)
+          max = this.details.meters.length
+        for (i; i < max; i++) {
+          this.listData.push(this.details.meters[i])
+        }
+      },
+      showRatesDrawer() {
+        this.drawerRate = true;
+      }
+    }
+  }
+</script>
+
+<style scoped="scoped" lang='scss'>
+  @import '../../styles/variables.scss';
+
+  .card-container {
+    width: 100%;
+    padding: 20px 60px;
+    min-height: $mainAppMinHeight;
+    background-color: #F0F5FC;
+  }
+  .card-content {
+    padding: 15px 80px;
+    border-radius: 6px;
+    background-color: white;
+  }
+  .section-title {
+    color: #333;
+    margin-top: 20px;
+    margin-bottom: 30px;
+    font-size: 16px;
+    user-select: none;
+    line-height: 24px;
+    font-weight: 500;
+    font-family: sans-serif;
+    text-transform: uppercase;
+  }
+  .add-text {
+    width: 100%;
+    max-width: 300px;
+  }
+  .add-text ::v-deep .el-textarea__inner {
+    font-family: sans-serif;
+  }
+  .hr {
+    height: 2px;
+    margin: 10px -40px;
+    background-color: #F0F5FC;
+  }
+  .hr-full {
+    height: 2px;
+    margin: 20px -80px;
+    background-color: #F0F5FC;
+  }
+  .rate-list-view {
+    display: flex;
+    flex-wrap: wrap;
+    align-items: center;
+  }
+  .rate-text {
+    max-width: 150px;
+    padding-right: 14px;
+  }
+  .list-item-icon {
+    width: 30px;
+    height: 30px;
+    cursor: pointer;
+    margin: 0 10px 22px;
+  }
+  .buttons {
+    padding-top: 15px;
+    padding-bottom: 30px;
+  }
+  @media screen and (max-width: 500px) {
+    .card-container {
+      padding: 0px;
+    }
+    .card-content {
+      padding: 15px 40px;
+    }
+  }
+  .form-unit ::v-deep .el-form-item__label {
+    line-height: 30px;
+  }
+  .item-unit {
+    top: 12px;
+    left: -130px;
+    font-size: 12px;
+    position: absolute;
+  }
+  .el-table ::v-deep tr div {
+    font-size: 12px;
+    white-space: nowrap;
+  }
+  .pagination {
+    width: 100%;
+    text-align: right;
+    overflow-x: auto;
+  }
+  .rate-date-text {
+    color: #666;
+    font-size: 12px;
+    padding-top: 2px;
+  }
+  .rate-unit-text {
+    color: #000;
+    font-size: 15px;
+    font-weight: 600;
+    padding-top: 2px;
+    padding-bottom: 5px;
+  }
+  .rate-unit-discount {
+    color: #666;
+    font-size: 14px;
+    padding-left: 8px;
+    font-weight: normal;
+    text-decoration: line-through;
+  }
+</style>