Procházet zdrojové kódy

Merge branch 'dev5.7.4' of Software/android-cloud-H5 into master

guocanfeng před 1 rokem
rodič
revize
b7e3e57e90

binární
assets/image/transferDuration/STARPRO_icon.png


binární
assets/image/transferDuration/STARRYSKY_icon.png


binární
assets/image/transferDuration/STAR_icon.png


binární
assets/image/transferDuration/SVIP_icon.png


binární
assets/image/transferDuration/VIP_icon.png


binární
assets/image/transferDuration/activeTansferDuration.png


binární
assets/image/transferDuration/activeTransferRecord.png


binární
assets/image/transferDuration/arrow-right.png


binární
assets/image/transferDuration/phone_time.png


binární
assets/image/transferDuration/tansferDuration.png


binární
assets/image/transferDuration/transferRecord.png


+ 36 - 0
assets/style/main.scss

@@ -9,3 +9,39 @@ html {
 body {
   // overscroll-behavior-y: contain;
 }
+
+
+::-webkit-scrollbar {
+	width: 5px;
+	/* 滚动条宽度为0 */
+	height: 5px;
+	/* 滚动条高度为0 */
+	display: block;
+	/* 滚动条隐藏 */
+}
+
+::-webkit-scrollbar {
+	width: 5px !important;
+	/* 滚动条宽度为0 */
+	height: 5px !important;
+	/* 滚动条高度为0 */
+	display: block !important;
+	/* 滚动条隐藏 */
+}
+
+::-webkit-scrollbar-track-piece {
+	background-color: rgba(0, 0, 0, 0.2) !important;
+	-webkit-border-radius: 6px !important;
+}
+
+::-webkit-scrollbar-thumb:vertical {
+	height: 5px !important;
+	background-color: rgba(125, 125, 125, 0.7) !important;
+	-webkit-border-radius: 6px !important;
+}
+
+::-webkit-scrollbar-thumb:horizontal {
+	width: 5px !important;
+	background-color: rgba(125, 125, 125, 0.7) !important;
+	-webkit-border-radius: 6px !important;
+}

+ 1 - 0
nuxt.config.js

@@ -84,6 +84,7 @@ export default {
     '~/plugins/jweixin',
     // '~/plugins/umeng-datasources',
     '@/plugins/vant',
+    '~/plugins/common'
   ],
 
   // Auto import components: https://go.nuxtjs.dev/config-components

+ 33 - 8
package-lock.json

@@ -11,6 +11,7 @@
         "@nuxtjs/auth-next": "5.0.0-1648802546.c9880dc",
         "@nuxtjs/axios": "^5.13.6",
         "axios": "^0.27.2",
+        "bignumber.js": "^9.1.2",
         "callapp-lib": "^3.5.2",
         "clipboard": "^2.0.11",
         "clipboard-polyfill": "^4.0.0-rc1",
@@ -20,6 +21,7 @@
         "dayjs": "^1.11.3",
         "format-number": "^3.0.0",
         "jmuxer": "^2.0.4",
+        "js-sha256": "^0.10.1",
         "jsencrypt": "^3.2.1",
         "jweixin-module": "^1.6.0",
         "nativeshare": "^2.1.5",
@@ -5324,10 +5326,9 @@
       }
     },
     "node_modules/bignumber.js": {
-      "version": "2.4.0",
-      "resolved": "https://registry.npmmirror.com/bignumber.js/-/bignumber.js-2.4.0.tgz",
-      "integrity": "sha512-uw4ra6Cv483Op/ebM0GBKKfxZlSmn6NgFRby5L3yGTlunLj53KQgndDlqy2WVFOwgvurocApYkSud0aO+mvrpQ==",
-      "dev": true,
+      "version": "9.1.2",
+      "resolved": "https://registry.npmmirror.com/bignumber.js/-/bignumber.js-9.1.2.tgz",
+      "integrity": "sha512-2/mKyZH9K85bzOEfhXDBFZTGd1CTs+5IHpeFQo9luiBG7hghdC851Pj2WAhb6E3R6b9tZj/XKhbg4fum+Kepug==",
       "engines": {
         "node": "*"
       }
@@ -11746,6 +11747,15 @@
         "url-regex": "^3.0.0"
       }
     },
+    "node_modules/jimp/node_modules/bignumber.js": {
+      "version": "2.4.0",
+      "resolved": "https://registry.npmmirror.com/bignumber.js/-/bignumber.js-2.4.0.tgz",
+      "integrity": "sha512-uw4ra6Cv483Op/ebM0GBKKfxZlSmn6NgFRby5L3yGTlunLj53KQgndDlqy2WVFOwgvurocApYkSud0aO+mvrpQ==",
+      "dev": true,
+      "engines": {
+        "node": "*"
+      }
+    },
     "node_modules/jimp/node_modules/file-type": {
       "version": "3.9.0",
       "resolved": "https://registry.npmmirror.com/file-type/-/file-type-3.9.0.tgz",
@@ -11805,6 +11815,11 @@
       "integrity": "sha512-Ni9PffhJtYtdD7VwxH6V2MnievekGfUefosGCHadog0/jAevRu6HPjYeMHbUemn0IPE8d4wGa8UsOGsX+iKy2g==",
       "dev": true
     },
+    "node_modules/js-sha256": {
+      "version": "0.10.1",
+      "resolved": "https://registry.npmmirror.com/js-sha256/-/js-sha256-0.10.1.tgz",
+      "integrity": "sha512-5obBtsz9301ULlsgggLg542s/jqtddfOpV5KJc4hajc9JV8GeY2gZHSVpYBn4nWqAUTJ9v+xwtbJ1mIBgIH5Vw=="
+    },
     "node_modules/js-tokens": {
       "version": "4.0.0",
       "resolved": "https://registry.npmmirror.com/js-tokens/-/js-tokens-4.0.0.tgz",
@@ -26262,10 +26277,9 @@
       "integrity": "sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ=="
     },
     "bignumber.js": {
-      "version": "2.4.0",
-      "resolved": "https://registry.npmmirror.com/bignumber.js/-/bignumber.js-2.4.0.tgz",
-      "integrity": "sha512-uw4ra6Cv483Op/ebM0GBKKfxZlSmn6NgFRby5L3yGTlunLj53KQgndDlqy2WVFOwgvurocApYkSud0aO+mvrpQ==",
-      "dev": true
+      "version": "9.1.2",
+      "resolved": "https://registry.npmmirror.com/bignumber.js/-/bignumber.js-9.1.2.tgz",
+      "integrity": "sha512-2/mKyZH9K85bzOEfhXDBFZTGd1CTs+5IHpeFQo9luiBG7hghdC851Pj2WAhb6E3R6b9tZj/XKhbg4fum+Kepug=="
     },
     "binary-extensions": {
       "version": "2.2.0",
@@ -31361,6 +31375,12 @@
         "url-regex": "^3.0.0"
       },
       "dependencies": {
+        "bignumber.js": {
+          "version": "2.4.0",
+          "resolved": "https://registry.npmmirror.com/bignumber.js/-/bignumber.js-2.4.0.tgz",
+          "integrity": "sha512-uw4ra6Cv483Op/ebM0GBKKfxZlSmn6NgFRby5L3yGTlunLj53KQgndDlqy2WVFOwgvurocApYkSud0aO+mvrpQ==",
+          "dev": true
+        },
         "file-type": {
           "version": "3.9.0",
           "resolved": "https://registry.npmmirror.com/file-type/-/file-type-3.9.0.tgz",
@@ -31406,6 +31426,11 @@
       "integrity": "sha512-Ni9PffhJtYtdD7VwxH6V2MnievekGfUefosGCHadog0/jAevRu6HPjYeMHbUemn0IPE8d4wGa8UsOGsX+iKy2g==",
       "dev": true
     },
+    "js-sha256": {
+      "version": "0.10.1",
+      "resolved": "https://registry.npmmirror.com/js-sha256/-/js-sha256-0.10.1.tgz",
+      "integrity": "sha512-5obBtsz9301ULlsgggLg542s/jqtddfOpV5KJc4hajc9JV8GeY2gZHSVpYBn4nWqAUTJ9v+xwtbJ1mIBgIH5Vw=="
+    },
     "js-tokens": {
       "version": "4.0.0",
       "resolved": "https://registry.npmmirror.com/js-tokens/-/js-tokens-4.0.0.tgz",

+ 2 - 0
package.json

@@ -24,6 +24,7 @@
     "@nuxtjs/auth-next": "5.0.0-1648802546.c9880dc",
     "@nuxtjs/axios": "^5.13.6",
     "axios": "^0.27.2",
+    "bignumber.js": "^9.1.2",
     "callapp-lib": "^3.5.2",
     "clipboard": "^2.0.11",
     "clipboard-polyfill": "^4.0.0-rc1",
@@ -33,6 +34,7 @@
     "dayjs": "^1.11.3",
     "format-number": "^3.0.0",
     "jmuxer": "^2.0.4",
+    "js-sha256": "^0.10.1",
     "jsencrypt": "^3.2.1",
     "jweixin-module": "^1.6.0",
     "nativeshare": "^2.1.5",

+ 299 - 0
pages/transferDuration/components/comoros.vue

@@ -0,0 +1,299 @@
+<template>
+    <div>
+        <div @click="visibleFun">
+            <slot>
+                <div class="comoros-input">
+                    <div v-if="!Object.keys(data).length" class="comoros-input-empty">
+                        <div>选择云手机</div>
+                        <img src="@/assets/image/transferDuration/arrow-right.png" alt="" class="comoros-input-empty-img" />
+                    </div>
+                    <div v-else>
+                        <div class="comoros-list-item_left" style="margin: 0 12px;">
+                            <img :src="require(`@/assets/image/transferDuration/${data.buyVipType}_icon.png`)" alt=""
+                                class="comoros-list-item_left-img" />
+                            <div>
+                                <div class="comoros-list-item_left-title">{{ data.diskName }}</div>
+                                <div v-if="data.userCardType === 1" class="comoros-list-item_left-tips">
+                                    剩{{ timeStamp(data.validTime, data.userCardType) }}
+                                </div>
+                                <div v-else-if="data.validTime > 0"
+                                    :class="['comoros-list-item_left-tips', { 'red-time': data.validTime <= 4320 }]">
+                                    剩{{ timeStamp(data.validTime, data.userCardType) }}</div>
+                                <div v-else class="comoros-list-item_left-tips red-time">已过期</div>
+                            </div>
+                        </div>
+                    </div>
+                </div>
+            </slot>
+        </div>
+        <van-popup v-model="visible" position="bottom">
+            <div class="comoros-list">
+                <div style="display: flex;justify-content: space-between;margin-bottom: 16px;">
+                    <div>请选择云手机</div>
+                </div>
+                <div class="comoros-list-container">
+                    <van-checkbox-group v-model="id" ref="checkboxGroup" v-if="filterTransferPhoneList.length">
+                        <div v-for="item in filterTransferPhoneList" :key="item.id" class="comoros-list-item">
+                            <div class="comoros-list-item_left">
+                                <img :src="require(`@/assets/image/transferDuration/${item.buyVipType}_icon.png`)" alt=""
+                                    class="comoros-list-item_left-img" />
+                                <div>
+                                    <div class="comoros-list-item_left-title">{{ item.diskName }}</div>
+                                    <div v-if="item.userCardType === 1" class="comoros-list-item_left-tips">
+                                        剩{{ timeStamp(item.validTime, item.userCardType) }}
+                                    </div>
+                                    <div v-else-if="item.validTime > 0"
+                                        :class="['comoros-list-item_left-tips', { 'red-time': item.validTime <= 4320 }]">
+                                        剩{{ timeStamp(item.validTime, item.userCardType) }}</div>
+                                    <div v-else class="comoros-list-item_left-tips red-time">已过期</div>
+                                </div>
+                            </div>
+                            <div class="comoros-list-item_right"><van-checkbox :name="item.id" shape="square"
+                                    icon-size="15px" @click="changeCheckbox(item)"></van-checkbox></div>
+                        </div>
+                    </van-checkbox-group>
+                    <div v-else style="height: 100%;display: flex;align-self: center;justify-content: center;">
+                        <van-empty description="暂时没有数据哦~"></van-empty>
+                    </div>
+                </div>
+                <div class="comoros-list-btn">
+                    <van-button type="info" block :disabled="!id.length" @click="confirm">确认</van-button>
+                </div>
+            </div>
+        </van-popup>
+    </div>
+</template>
+
+<script>
+export default {
+    name: 'comoros',
+    props: {
+        // 云机数据
+        transferPhoneList: {
+            type: Array,
+            default: () => {
+                return []
+            }
+        },
+        // 云机类型
+        buyVipType: {
+            type: String,
+            default: ''
+        },
+
+        value: {
+            type: [String, Number],
+            default: ''
+        },
+
+        // 不能勾选需要过滤的云机
+        disabledId: {
+            type: [String, Number],
+            default: ''
+        },
+        // 云机名字
+        name: {
+            type: String,
+            default: ''
+        },
+        // distinguishBool  false是减少、true是增加时长云机列表
+        distinguishBool: {
+            type: Boolean,
+            default: false
+        },
+        dayTime: {
+            type: [String, Number],
+            default: ''
+        },
+        // 安卓版本
+        androidVersion: {
+            type: [String, Number],
+            default: ''
+        }
+    },
+    data() {
+        return {
+            // 显示弹窗
+            visible: false,
+            // 选中的数据
+            id: [],
+            data: {},
+            cruuntItem: {}
+        };
+    },
+    computed: {
+        // 过滤云机
+        filterTransferPhoneList() {
+            return this.transferPhoneList.filter(item => {
+                if (this.distinguishBool) {
+                    const bool = ((this.buyVipType === item.buyVipType) && (this.disabledId !== item.id))
+                    const bool1 =  item.buyVipType === 'SVIP' ? bool && this.androidVersion === item.androidVersion : bool
+                    return this.buyVipType ? bool1 : true
+                } else {
+                    return this.dayTime ? item.validTime > +this.dayTime : true
+                }
+            })
+        }
+    },
+    methods: {
+        // 点击显示弹框
+        visibleFun() {
+            this.visible = true
+            this.id = this.value ? [this.value] : []
+            setTimeout(() => {
+                const documents = document.getElementsByClassName('comoros-list-container')
+                const length = documents.length
+                for (let i = 0; i < length; i++) {
+                    documents[i].scrollTop = 0
+                }
+            })
+        },
+        // 转化时长
+        timeStamp(StatusMinute, userCardType, nextSendTime) {
+            var day = parseInt(StatusMinute / 60 / 24);
+            var hour = parseInt((StatusMinute / 60) % 24);
+            var min = parseInt(StatusMinute % 60);
+            StatusMinute = '';
+            if (day > 0) {
+                StatusMinute = day + '天';
+            }
+            if (hour > 0) {
+                StatusMinute += hour + '小时';
+            }
+            if (day === 0 && min > 0) {
+                StatusMinute += parseFloat(min) + '分钟';
+            }
+            return StatusMinute.length ? StatusMinute : userCardType === 1 ? '0小时' : '';
+        },
+        // 点击复选框触发
+        changeCheckbox(data) {
+            if (!this.id.length) return
+            if (this.id.includes(data.id)) {
+                this.id = []
+            }
+            this.id = [data.id]
+            this.cruuntItem = data
+        },
+
+        // 确认选择云机
+        confirm() {
+            this.visible = false
+            this.$emit('input', this.id[0])
+            this.data = this.cruuntItem
+            this.$emit('update:name', this.cruuntItem.diskName)
+            this.$emit('confirm', { ...this.data })
+        }
+    },
+};
+</script>
+
+<style lang="scss" scoped>
+.comoros-input {
+    margin-top: 8px;
+    height: 52px;
+    background: #2C2C2D;
+    border-radius: 8px;
+    // line-height: 52px;
+    display: flex;
+    align-items: center;
+    font-size: 12px;
+
+    .comoros-input-empty {
+        width: 100%;
+        margin: 0 12px;
+        display: flex;
+        justify-content: space-between;
+
+        .comoros-input-empty-img {
+            width: 16px;
+            height: 16px;
+        }
+    }
+
+
+}
+
+.comoros-list {
+    background: #fff;
+    margin: 0 10px;
+    background: #2C2C2D;
+    border-radius: 20px 20px 0 0;
+    padding: 24px 16px 8px;
+    color: #CFD1D4;
+    height: 60vh;
+    overflow-y: auto;
+    display: flex;
+    flex-direction: column;
+
+    ::v-deep .van-checkbox__label {
+        color: #CFD1D4;
+    }
+
+    .comoros-list-container {
+        flex: 1;
+        overflow-y: auto;
+
+        .comoros-list-item {
+            box-sizing: border-box;
+            padding: 16px 16px 16px 10px;
+            height: 65px;
+            background: #363636;
+            border-radius: 6px;
+            margin-top: 12px;
+            display: flex;
+            justify-content: space-between;
+
+            .comoros-list-item_right {
+                display: flex;
+                align-items: center;
+            }
+
+
+
+        }
+    }
+
+    .comoros-list-btn {
+        margin: 16px 0 9px;
+    }
+
+
+}
+
+
+.comoros-list-item_left {
+    display: flex;
+    vertical-align: middle;
+    font-size: 14px;
+    line-height: 18px;
+
+    .comoros-list-item_left-img {
+        width: 34px;
+        height: 34px;
+        vertical-align: middle;
+        margin-right: 8px;
+    }
+
+    .comoros-list-item_left-tips {
+        &>img {
+            width: 12px;
+            height: 12px;
+            margin-bottom: 2px;
+            margin-right: 5px;
+            vertical-align: middle;
+        }
+
+        font-size: 12px;
+        font-weight: 100;
+        vertical-align: middle;
+
+        &.red-time {
+            color: #F04646;
+        }
+    }
+}
+
+::v-deep .van-popup {
+    background: transparent;
+}
+</style>

+ 350 - 0
pages/transferDuration/components/tansferDuration.vue

@@ -0,0 +1,350 @@
+<template>
+    <div class="transfer">
+        <div style="flex: 1;overflow-y: auto;padding: 0 16px;" class="transfer-box">
+            <div>
+                <div>减少时长的云机</div>
+                <div>
+                    <comoros :key="transferUserCardKey" :dayTime="countObj.transferMinDay"
+                        :transferPhoneList="transferPhoneList" v-model="params.transferUserCardId"
+                        :name.sync="transferUserCardName" @confirm="comorosConfirm" />
+                </div>
+            </div>
+            <div class="transfer-item">
+                <div>增加时长的云机</div>
+                <div>
+                    <comoros :key="acceptUserCardKey" :disabledId="params.transferUserCardId"
+                        :transferPhoneList="transferPhoneList" :buyVipType="buyVipType" v-model="params.acceptUserCardId"
+                        :name.sync="acceptUserCardName" :androidVersion="androidVersion" distinguishBool />
+                </div>
+            </div>
+            <div class="transfer-item">
+                <div>转移的时间</div>
+                <div class="transfer-item-input">
+                    <van-field placeholder="请输入转移的时间" v-model="params.transferTime"
+                        @input="params.transferTime = params.transferTime.replace(/\D/g, '')" />
+                    天
+                </div>
+            </div>
+            <span style="font-size: 12px;font-weight: 400;color: #3B7FFF;line-height: 16px;margin-top: 5px;"
+                @click="(params.transferTime && Object.keys(countObj).length) && serviceCount()"
+                :class="{ opacity: !params.transferTime || !Object.keys(countObj).length }">{{ countLoading ? '正在计算中...' :
+                    '服务费计算'
+                }}</span>
+            <div class="transfer-tip">
+                <div>
+                    温馨提示:
+                </div>
+                <div class="transfer-tip-content">
+                    <div v-html="html"></div>
+                </div>
+            </div>
+        </div>
+        <div class="transfer-btn" v-show="btnVisible">
+            <van-button type="info" block
+                :disabled="!params.transferUserCardId || !params.acceptUserCardId || !params.transferTime"
+                :loading="allocateLoading" @click="changePopUpType(0)" loading-text="正在分配时间中...">
+                分配时间
+            </van-button>
+        </div>
+
+        <van-dialog v-model="visible" :title="clickType ? '服务费计算' : ''" confirmButtonColor="#3B7FFF" className="dialog"
+            showCancelButton cancelButtonColor="#999999" @confirm="!clickType && confirm()"
+            @close="allocateLoading = false">
+            <div style="padding: 16px;font-weight: 100;">
+                <template v-if="clickType">
+                    <div style="display: flex; justify-content: space-between;">
+                        <div>转移天数:</div>
+                        <div style="color: #F9F9F9;">{{ params.transferTime }}天</div>
+                    </div>
+                    <div style="display: flex; justify-content: space-between;">
+                        <div>服务费:</div>
+                        <div style="color: #F9F9F9;">{{ serviceCharge }}天</div>
+                    </div>
+
+                </template>
+
+                <template v-else>
+                    <div style="line-height: 30px;">
+                        <div style="text-align: center;font-weight:bold;">
+                            确定要将【{{ transferUserCardName }}】
+                        </div>
+                        <div style="text-align: center;">
+                            时间分配给【{{ acceptUserCardName }}】
+                        </div>
+                        <div style="text-align: center;margin-top: 10px">
+                            服务费:{{ serviceCharge }}天
+                        </div>
+                    </div>
+                </template>
+            </div>
+        </van-dialog>
+    </div>
+</template>
+
+<script>
+import {
+    sha256
+} from 'js-sha256';
+import BigNumber from "bignumber.js";
+import { Toast } from 'vant'
+const comoros = () => import('./comoros.vue')
+export default {
+    name: 'tansferDuration',
+    components: {
+        comoros
+    },
+    props: {
+        token: {
+            type: String,
+            default: ''
+        }
+    },
+    data() {
+        return {
+            visible: false,// 显示弹窗
+            transferPhoneList: [], // 云机列表
+            buyVipType: '', // 当前选中的云机类型
+            params: {},
+            acceptUserCardKey: +new Date(),
+            transferUserCardKey: +new Date(),
+            acceptUserCardName: '',
+            transferUserCardName: '',
+            // 点击类型 0 是确认转移、1是计算手续费
+            clickType: 0,
+            // 计算loading
+            countLoading: false,
+            // 计算服务费的对象
+            countObj: {},
+            // 服务费
+            serviceCharge: '',
+            // 分配时间loading
+            allocateLoading: false,
+            html: '',
+            androidVersion: '',
+            btnVisible: true
+        };
+    },
+
+    mounted() {
+        this.init()
+        this.getFeeRatio()
+        this.getRule()
+        const userAgent = navigator.userAgent
+        if (userAgent.includes("Android")) {
+            window.onresize = this.monitoringDevices
+        }
+    },
+    beforeDestroy() {
+        window.onresize = null
+    },
+    methods: {
+        init() {
+            this.params = {
+                acceptUserCardId: '', // 接受设备ID
+                transferUserCardId: '', // 转移设备ID
+                transferTime: '' // 转移天数
+            }
+            this.acceptUserCardKey = +new Date()
+            this.transferUserCardKey = +new Date()
+            this.buyVipType = ''
+            this.serviceCharge = ''
+            this.getTransferPhoneList()
+        },
+        // 获取云机列表
+        getTransferPhoneList() {
+            this.$axios.$get('resources/v5/time/transfer/getTransferPhoneList', { header: { token: this.token } }).then(res => {
+                if (res.success) {
+                    this.transferPhoneList = res.data
+                }
+            })
+        },
+        // 确认框
+        comorosConfirm(data) {
+            this.buyVipType = data.buyVipType
+            this.buyVipType === 'SVIP' && (this.androidVersion = data.androidVersion);
+            this.acceptUserCardKey = +new Date()
+            this.params.acceptUserCardId = ''
+        },
+
+        // 获取手续费比例
+        getFeeRatio() {
+            this.$axios.$get('resources/v5/time/transfer/getFeeRatio', { headers: { Authorization: this.token, versionName: '5.7.4' } }).then(res => {
+                if (res.success) {
+                    res.data.transferMinDay = res.data.transferMinDay * 24 * 60
+                    this.countObj = res.data
+                }
+            }).catch(err => {
+                Toast.fail(err.message)
+            })
+        },
+
+        // 确定提交
+        async confirm() {
+            // 获取服务器时间
+            let requestTime = await this.$axios.$get('pay/v1/order/getSystemTime', { headers: { Authorization: this.token, versionName: '5.7.4' } })
+
+            requestTime = new Date(requestTime.data * 1000).$formatTime()
+            const requestTimeSign = sha256("Register_SZX_2023:" + requestTime)
+            this.$axios.$post('resources/v5/time/transfer/transferDurationOperation', {
+                ...this.params,
+                requestTime
+            }, { headers: { Authorization: this.token, versionName: '5.7.4', requestTimeSign } }).then(res => {
+                if (res.success) {
+                    this.init()
+                    Toast.success(res.msg)
+                }
+            }).catch(err => {
+                Toast.fail(err.message)
+            }).finally(() => {
+                this.allocateLoading = false
+            })
+
+        },
+
+        // 服务费计算
+        serviceCount(bool = true) {
+            if (this.countLoading) return
+            if (bool) this.countLoading = true
+            const arr = this.countObj.list.sort((a, b) => {
+                console.log(a, b)
+                return a.timeConsumingEnd - b.timeConsumingEnd
+            })
+            let commission = null
+            for (const i of arr) {
+                if (this.params.transferTime <= i.timeConsumingEnd) {
+                    commission = i.commission
+                    break
+                }
+            }
+
+            if (!commission) commission = this.countObj.commissionSet
+            this.serviceCharge = +new BigNumber(this.params.transferTime).times(new BigNumber(commission).div(100))
+            this.countLoading = false
+            if (bool) this.changePopUpType(1)
+        },
+
+        changePopUpType(e) {
+            if (!Object.keys(this.countObj).length) {
+                Toast.fail('活动已结束')
+                return
+            }
+            if (!e) {
+                this.serviceCount(false)
+                this.allocateLoading = true
+            }
+            this.visible = true
+            this.clickType = e
+        },
+
+        // 获取规则
+        getRule() {
+            this.$axios.$get('/public/v4/agreement/content', {
+                params: {
+                    agreementCoding: 'YJSBSJZY2024'
+                }, headers: { Authorization: this.token, versionName: '5.7.4' }
+            }).then(res => {
+                const html = res.data.content;
+                const rx = /<body[^>]*>([\s\S]+?)<\/body>/i;
+                let m = rx.exec(html);
+                if (m) {
+                    m = m[1]
+                };
+                this.html = m
+
+            })
+        },
+        monitoringDevices() {
+            this.btnVisible = !this.btnVisible
+        }
+    },
+};
+</script>
+
+<style lang="scss" scoped>
+.transfer {
+    padding: 0 0 30px;
+    display: flex;
+    flex-direction: column;
+    height: 100%;
+
+    .transfer-item {
+        margin-top: 16px;
+    }
+
+    .transfer-tip {
+        margin-top: 24px;
+    }
+
+    .transfer-item-input {
+        margin-top: 8px;
+        display: flex;
+        align-items: center;
+    }
+
+    .transfer-tip-content {
+        margin-top: 8px;
+    }
+
+    .transfer-tip-content {
+        font-size: 12px;
+        font-family: PingFangSC, PingFang SC;
+        font-weight: 400;
+        color: #959799;
+        line-height: 17px;
+    }
+
+    .transfer-btn {
+        padding: 0 16px;
+        height: 48px;
+
+        .van-button {
+            height: 100%;
+            border-radius: 5px;
+        }
+    }
+
+    .van-field {
+        background: #2C2C2D;
+        margin-right: 12px;
+        border-radius: 5px;
+        height: 52px !important;
+        font-weight: 500 !important;
+        align-items: center;
+
+        ::v-deep .van-field__control {
+            color: #fff;
+        }
+
+        ::v-deep .van-field__control::-webkit-input-placeholder {
+            color: #999999 !important;
+        }
+    }
+}
+
+.dialog {
+    color: #CFD1D4;
+
+    ::v-deep [class*=van-hairline]::after {
+        border-color: rgba(72, 72, 72, 0.5);
+    }
+
+    &,
+    ::v-deep .van-button__content {
+        background: #2C2C2D;
+    }
+}
+
+.opacity {
+    opacity: .4;
+}
+
+.transfer-tip-content {
+    ::v-deep ol {
+        list-style-type: decimal;
+    }
+
+    ::v-deep ul {
+        list-style-type: disc;
+    }
+}
+</style>

+ 145 - 0
pages/transferDuration/components/transferRecord.vue

@@ -0,0 +1,145 @@
+<template>
+    <div class="record">
+        <van-list v-model="loading" :finished="finished" finished-text="没有更多了" @load="onLoad" v-if="list.length">
+            <van-row v-for="(item, index) in list" :key="index" class="record-item">
+                <van-col span="24">
+                    <div class="flex-jub record-border">
+                        <div>
+                            <span>单号:</span><span style="font-weight: bold;color: #CFD1D4;">{{ item.synthesisId }}</span>
+                        </div>
+                        <div>
+                            <span>转移天数:</span><span style="font-weight: bold;color: #CFD1D4;">{{ item.transferDurationStr
+                            }}</span>
+                        </div>
+                    </div>
+                </van-col>
+                <van-col span="24">
+                    <div class="record-details-item">
+                        <span>扣除天数:</span><span style="font-weight: bold;color: #CFD1D4;">{{ item.commissionStr }}</span>
+                    </div>
+                </van-col>
+                <van-col span="24">
+                    <div class="flex-jub record-details-item">
+                        <div>
+                            <span>主设备名称:</span><span style="font-weight: bold;color: #CFD1D4;">{{ item.transferDiskName
+                            }}</span>
+                        </div>
+                        <div>
+                            <span>设备天数:</span><span style="font-weight: bold;color: #CFD1D4;">{{ item.mainTransferTime
+                            }}</span>
+                        </div>
+                    </div>
+                </van-col>
+                <van-col span="24">
+                    <div class="flex-jub record-details-item">
+                        <div>
+                            <span>副设备名称:</span><span style="font-weight: bold;color: #CFD1D4;">{{ item.acceptDiskName
+                            }}</span>
+                        </div>
+                        <div>
+                            <span>设备天数:</span><span style="font-weight: bold;color: #CFD1D4;">{{ item.obtainTime }}</span>
+                        </div>
+                    </div>
+                </van-col>
+                <van-col span="24">
+                    <div class="record-details-item">
+                        <span>转移时间:</span><span style="font-weight: bold;color: #CFD1D4;">{{ item.createTime
+                        }}</span>
+                    </div>
+                </van-col>
+            </van-row>
+        </van-list>
+        <div v-else-if="pageLoading" style="height: 100%;position: relative;">
+            <div style="position: absolute;top: 50%;left: 50%;transform: translate(-50%, -50%);">
+                <van-loading type="spinner" />
+            </div>
+        </div>
+        <div style="height: 100%;display: flex;align-self: center;justify-content: center;" v-else>
+            <van-empty description="暂时没有数据哦~"></van-empty>
+        </div>
+    </div>
+</template>
+
+<script>
+export default {
+    name: 'transferRecord',
+    props: {
+        token: {
+            type: String,
+            default: ''
+        }
+    },
+    data() {
+        return {
+            loading: false,
+            finished: false,
+            params: {
+                pageNum: 1,
+                pageSize: 10
+            },
+            list: [],
+            total: 0,
+            pageLoading: false
+        };
+    },
+
+    mounted() {
+        this.getTransferDurationRecode()
+    },
+
+    methods: {
+        // 获取转移记录
+        getTransferDurationRecode() {
+            this.pageLoading = true
+            this.$axios.$post('/resources/v5/time/transfer/getUserTransferRecode', { ...this.params }, { headers: { Authorization: this.token, versionName: '5.7.4' } }).then(res => {
+                if (res.success) {
+                    this.list.push(...res.data.list)
+                    this.total = res.data.total
+                    this.loading = false;
+                    this.pageLoading = false
+                }
+            })
+        },
+        // 加载分页
+        onLoad() {
+            if (this.list.length >= this.total) {
+                this.finished = true
+                return
+            }
+            this.params.pageNum++
+            this.getTransferDurationRecode()
+        }
+    },
+};
+</script>
+
+<style lang="scss" scoped>
+.record {
+    padding: 0 16px;
+    font-weight: 100;
+    height: 100%;
+
+    .record-item {
+        margin-bottom: 16px;
+        padding: 24px 24px 24px 16px;
+        background: #2C2C2D;
+        box-shadow: 0px 3px 7px 0px #1E2022;
+        border-radius: 8px;
+    }
+
+    .record-details-item {
+        margin-top: 12px;
+    }
+
+    .record-border {
+        padding-bottom: 12px;
+        border-bottom: 1px solid rgba(255, 255, 255, 0.08);
+    }
+
+    .flex-jub {
+        display: flex;
+        justify-content: space-between;
+
+    }
+}
+</style>

+ 129 - 0
pages/transferDuration/index.vue

@@ -0,0 +1,129 @@
+<template>
+    <div :style="{ 'height': '100vh', 'background': '#1c1c1e' }">
+        <div class="transfer-duration">
+            <div class="transfer-duration-title">转移时长</div>
+            <div class="transfer-duration-tabs">
+                <!-- <div class="transfer-duration-tabs_item active">123456</div>
+                <div class="transfer-duration-tabs_item">1432123</div> -->
+                <div v-for="item in tabs" :key="item.key" :class="['transfer-duration-tabs_item', { active: tabsActive === item.key }]" @click="changeTabs(item.key)">
+                    <img :src="tabsActive === item.key ? item.activeIcon : item.icon" alt=""
+                        class="transfer-duration-tabs_item-icon">
+                    {{ item.name }}
+                </div>
+                <div :class="['transfer-duration-tabs_item pab', { active: tabsActive === 'transferRecord' }]"></div>
+            </div>
+            <div class="transfer-duration-com" :key="tabsActive">
+                <components :is="tabsActive" :token="token" />
+            </div>
+        </div>
+    </div>
+</template>
+
+<script>
+const transferRecord = () => import('./components/transferRecord.vue')
+const tansferDuration = () => import('./components/tansferDuration.vue')
+export default {
+    auth: false,
+    name: 'transferDuration',
+    data() {
+        return {
+            tabs: [{
+                name: '转移时长',
+                key: 'tansferDuration',
+                icon: require('@/assets/image/transferDuration/tansferDuration.png'),
+                activeIcon: require('@/assets/image/transferDuration/activeTansferDuration.png')
+            }, {
+                name: '转移记录',
+                key: 'transferRecord',
+                icon: require('@/assets/image/transferDuration/transferRecord.png'),
+                activeIcon: require('@/assets/image/transferDuration/activeTransferRecord.png')
+            }],
+            tabsActive: 'tansferDuration',
+            token: ''
+        }
+    },
+    components: { transferRecord, tansferDuration },
+    head: {
+        title: '转移时长',
+    },
+    created() {
+        this.token = this.$route.query.token
+    },
+    methods: {
+        changeTabs(key) {
+            this.tabsActive = key
+        }
+    }
+}
+</script>
+
+<style lang="scss" scoped>
+.transfer-duration {
+    font-family: PingFangSC, PingFang SC;
+    color: #C0C1C4;
+    display: flex;
+    flex-direction: column;
+    // padding: 16px 16px 0;
+    font-size: 14px;
+    font-weight: bold;
+    // overflow: hidden;
+    height: 100%;
+
+    .transfer-duration-title {
+        font-size: 18px;
+        padding: 0 26px;
+        line-height: 40px;
+        font-weight: 500;
+    }
+
+    .transfer-duration-tabs {
+        height: 48px;
+        margin: 0 16px;
+        background: #363636;
+        border-radius: 8px;
+        box-sizing: border-box;
+        position: relative;
+        padding: 0 10px;
+        display: flex;
+        align-items: center;
+        z-index: 1;
+
+        .transfer-duration-tabs_item {
+            height: 40px;
+            border-radius: 8px;
+            width: 159px;
+            text-align: center;
+            line-height: 40px;
+            position: relative;
+            z-index: 1;
+            &.pab {
+                position: absolute;
+                background: linear-gradient(180deg, #575759 0%, #2C2C2D 100%);
+                box-shadow: 0px 2px 4px 0px rgba(0, 0, 0, 0.5);
+                z-index: -1;
+                transition: all .5s;
+                &.active{
+                    transform: translateX(159px);
+                }
+            }
+
+            .transfer-duration-tabs_item-icon {
+                width: 14px;
+                height: 14px;
+                vertical-align: middle;
+                margin-bottom: 3px;
+            }
+
+            &.active {
+                color: #fff;
+            }
+        }
+    }
+
+    .transfer-duration-com {
+        margin-top: 16px;
+        flex: 1;
+        overflow-y: auto;
+    }
+}
+</style>

+ 24 - 0
plugins/common.js

@@ -0,0 +1,24 @@
+import Vue from 'vue'
+
+const common = {
+	install(Vue) {
+
+		// 手机号码格式成秘文形式  格式为137****3151
+		String.prototype.$formatPhone = function () {
+			return this.replace(/(\d{3})\d+(\d{4})/, '$1****$2')
+		}
+
+		// 格式化时间  格式为 2023-10-02 12:00:00
+		Date.prototype.$formatTime = function () {
+			var Y = this.getFullYear() + '-';
+			var M = (this.getMonth() + 1 < 10 ? '0' + (this.getMonth() + 1) : this.getMonth() + 1) + '-';
+			var D = (this.getDate() < 10 ? '0' + this.getDate() : this.getDate()) + ' ';
+			var h = (this.getHours() < 10 ? '0' + this.getHours() : this.getHours()) + ':';
+			var m = (this.getMinutes() < 10 ? '0' + this.getMinutes() : this.getMinutes()) + ':';
+			var s = this.getSeconds() < 10 ? '0' + this.getSeconds() : this.getSeconds();
+			return Y + M + D + h + m + s;
+		}
+	}
+}
+
+Vue.use(common)