/*
 * @Author: qiuwt
 * @Email: qiuwt@hxmec.com
 * @Date: 2020-10-13 17:09:28
 * @Last Modified by: hejk
 * @Last Modified time: 2022-01-20 17:04:31
 * @Description: 图片查看器
 */

<template>
    <div class="image-viewer-wrapper">
        <div v-if="mode !== 'none'">
            <button v-if="!img"
                    @click="handleImgPreview()"
                    v-bind="extend"
                    class="image-btn">
                {{ (typeof btnLabel === 'function')?btnLabel(this):btnLabel }}
            </button>
            <img v-else
                 :src="img"
                 v-bind="Object.assign(imgStyle,extend)"
                 @click="handleImgPreview()"
                 class="cell-img">
        </div>
        <transition name="el-alert-fade">
            <section v-if="visible"
                     class="fixed-container">
                <div v-show="loading"
                     class="loading-wrapper">
                    <i class="icon-loading" />
                    <span class="text-loading">加载中...</span>
                </div>
                <div v-show="!loading"
                     class="img-wrapper">
                    <div class="img-content">
                        <span :style="`transform:translate(${dx}px,${dy}px);`"
                              class="img-content_box">
                            <img ref="img"
                                 :src="viewer[props.fileUrl]"
                                 :style="`transform: rotate(${rotate}deg) scale(${zoom})`"
                                 class="img">
                        </span>
                    </div>
                    <div class="img-tooltip">
                        <div class="tooltip-wrapper">
                            <div v-if="currentIds.length > 1"
                                 class="tooltip-page">
                                <span @click="handlePrev"
                                      class="icon-prev" />
                                <span class="page-number">
                                    {{ current+1 }}/{{ currentIds.length }}
                                </span>
                                <span @click="handleNext"
                                      class="icon-next" />
                            </div>
                            <div class="tooltip-zoom">
                                <span @click="handleZoom('in')"
                                      class="icon-zoom-in" />
                                <span @click="handleZoom('out')"
                                      class="icon-zoom-out" />
                            </div>
                            <div class="tooltip-rotate">
                                <span @click="handleRotate"
                                      class="icon-rotate" />
                                <span @click="handleDownload"
                                      class="icon-download" />
                                <span @click="handlePrinter"
                                      class="icon-printer my-el-icon" />
                            </div>
                            <div class="tooltip-close">
                                <span @click="handleClose"
                                      class="icon-close" />
                            </div>
                        </div>
                    </div>
                </div>
            </section>
        </transition>
    </div>
</template>
<script>
import { download } from '../src/utils/util';

export default {
    name: 'ImgViewer',
    props: {
        /**
         * 模式
         * btn 显示占位按钮
         * img 显示占位图片
         * none 无占位
         *  */
        mode: {
            type: String,
            default: 'none'
        },
        // 图片形象或id
        value: {
            type: [Array, String, Object],
            default: ''
        },
        // 按钮名称
        btnLabel: {
            type: [String, Function],
            default: '查看文件'
        },
        http: {
            type: Object,
            default: () => ({
                filePath: '/code/base/fe/info'
            })
        },
        // 获得数据上字段的内容
        props: {
            type: Object,
            default: () => ({
                code: 'code', // 响应状态码
                data: 'data', // 响应数据
                message: 'msg', // 响应文案信息
                successCode: 0, // 成功码
                fileId: 'id', // 文件标识字段
                fileUrl: 'url', // 文件地址
                fileName: 'originName', // 文件名
                fileSuffix: 'ext' // 文件后缀
            })
        },
        /**
         * 站位属性
         */
        extend: {
            type: Object,
            default: () => ({})
        },
        downloadFN: {
            type: [Function, String],
            default: ''
        },
        fetch: {
            type: [Function, String],
            default: ''
        }
    },
    data() {
        return {
            // 移动图片用
            dx: 0,
            dy: 0,
            $img: '', // dom
            moveing: false,
            regUrl: /.(jpg|JPG|bmp|BMP|gif|GIF|ico|pcx|jpeg|JPEG|tif|png|PNG|raw|tga)$/, // 图片正则
            imgStyle: {
                style: {
                    maxWidth: '200px',
                    maxHeight: '200px'
                }
            },
            viewer: {},
            img: '', // 图片地址
            visible: false,
            loading: true,
            rotate: 0,
            zoom: 1,
            currentIds: [],
            // 当前id
            current: 0,
            // 图片集合
            storage: {}
        };
    },
    watch: {
        value(now, old) {
            if (now === old) return;
            this.initData(now);
        },
        /**
         * esc 关闭
         */
        visible(n, o) {
            if (n === o) return;
            if (n) {
                document.body.addEventListener('keydown', this.handleKeydownHide, false);
            }
        }
    },
    created() {
        this.initData(this.value);
    },
    methods: {
        /**
         * 资源信息
         */
        initData(val) {
            const {
                props, storage, regUrl, mode 
            } = this;
            this.current = 0;
            // 数据转换
            if (typeof val === 'string') {
                val = val.split(',');
            }
            // 是否是数组
            let isArray = Array.isArray(val);
            if (typeof val === 'object' && !isArray) {
                val = [val];
                isArray = true;
            }
            // init img
            if (isArray) {
                const ids = [];
                // url
                val.forEach(item => {
                    const type = typeof item;
                    switch (type) {
                    case 'object': {
                        const url = item[props.fileUrl];
                        const id = item[props.fileId] || url;
                        storage[id] = item;
                        ids.push(id);
                        break;
                    }
                    case 'string': {
                        if (item.match(regUrl)) {
                            // 链接
                            storage[item] = {
                                [props.fileUrl]: item
                            };
                        }
                        ids.push(item);

                        break;
                    }
                    default: {
                        break;
                    }
                    }
                });
                this.currentIds = ids;
                // 图片占位模式
                if (mode === 'img') {
                    const img = storage[ids[0]] || '';
                    if (img) {
                        this.img = img[props.fileUrl];
                    } else {
                        this.fetchFile(ids[0]).then(res => {
                            if (res) this.img = res[props.data][props.fileUrl];
                        });
                    }
                }
            }
        },
        /**
         * 关闭
         */
        handleKeydownHide(e) {
            if (e.key === 'Escape') {
                this.handleClose();
                e.stopPropagation();
            }
        },
        handleClose() {
            this.zoom = 1;
            this.rotate = 0;
            this.visible = false;
            this.restImgMove();
            document.body.removeEventListener('keydown', this.handleKeydownHide, false);
        },
        /**
         * 打印
         */
        handlePrinter() {
            window.print();
        },
        /**
         * 重制预览位置
         */
        restImgMove() {
            this.moveing = false;
            this.dy = 0;
            this.dx = 0;
            document.onmousemove = null;
            if (this.$img) this.$img.removeEventListener('mousedown', this.handleImgMouseDown, false);
        },
        /**
         * 预览图拖动
         */
        handleImgMouseDown(event) {
            // 点击位置
            const left = event.clientX;
            const top = event.clientY;
            // 盒子当前位置
            const boxX = this.dx;
            const boxY = this.dy;
            if (!this.moveing) {
                this.moveing = true;
                document.onmousemove = e => {
                    // 拖动的距离
                    const x = e.clientX - left;
                    const y = e.clientY - top;
                    // 偏移位置
                    this.dx = boxX + Number(x);
                    this.dy = boxY + Number(y);
                };
            } else {
                document.onmousemove = null;
                this.moveing = false;
            }
            document.onmouseup = () => {
                this.moveing = false;
                document.onmousemove = null;
                document.onmouseup = null;
            };
            event.preventDefault();
        },
        /**
         * 预览图片
         * @param {object} params 图片数据
         */
        handleImgPreview(params) {
            if (params) {
                this.initData(params);
            }
            this.showImage();
            this.visible = true;
        },
        handleRotate() {
            this.rotate += 90;
        },
        /**
         * 图片缩放
         * @param {string} type 缩放类型
         */
        handleZoom(type) {
            switch (type) {
            case 'in':
                this.zoom += 0.3;
                break;
            case 'out':
                this.zoom = Math.max(this.zoom - 0.3, 0.1);
                break;
            default:
            }
        },
        /**
         * 显示图片
         */
        showImage() {
            this.loading = true;
            // 当前要显示的id
            const id = this.currentIds[this.current];
            if (this.storage[id]) {
                this.loading = false;
                this.viewer = this.storage[id];
                setTimeout(() => {
                    this.$img = this.$el.querySelector('.img-wrapper');
                    this.$img.addEventListener('mousedown', this.handleImgMouseDown, false);
                }, 500);
            } else {
                const { props } = this;
                this.fetchFile(id).then(
                    res => {
                        // 图片对象
                        if (res) {
                            const img = new Image();
                            const file = res[props.data];
                            if (!file) {
                                this.loading = false;
                                this.handleClose();
                                const tip = '未找到对应文件信息，请查看props配置和接口返回';
                                if (this.$message && this.$message.error) {
                                    this.$message.error(tip);
                                } else {
                                    console.error(tip);
                                }
                                return;
                            }
                            const fileUrl = file[props.fileUrl];
                            // 加载完毕显示图片
                            img.onload = () => {
                                this.showImage();
                            };
                            img.onerror = () => {
                                this.showImage();
                            };
                            img.src = fileUrl;
                            this.storage[id] = file;
                        } else {
                            this.loading = false;
                            const tip = '文件返回失败，请查看props配置和接口返回信息';
                            if (this.$message && this.$message.error) {
                                this.$message.error(tip);
                            } else {
                                console.error(tip);
                            }
                            this.handleClose();
                        }
                    },
                    () => {
                        this.loading = false;
                        const tip = '文件请求失败，请检查http配置';
                        if (this.$message && this.$message.error) {
                            this.$message.error(tip);
                        } else {
                            console.error(tip);
                        }
                        this.handleClose();
                    }
                );
            }
        },
        /**
         * 下一页
         */
        handleNext() {
            const index = this.current + 1;
            if (index < this.currentIds.length) {
                this.current = index;
                this.showImage();
            }
        },
        /**
         * 上一页
         */
        handlePrev() {
            const index = this.current - 1;
            if (index >= 0) {
                this.current = index;
                this.showImage();
            }
        },
        /**
         * 加载文件资源
         *
         */
        fetchFile(id) {
            return new Promise((success, err) => {
                const { props, http } = this;
                const params = {
                    [props.fileId]: id
                };
                Object.assign(params, http.params);
                let Fetch = this.$fetch;
                if (typeof this.fetch === 'function') {
                    Fetch = this.fetch;
                }
                if (Fetch) {
                    Fetch({
                        url: http.filePath,
                        params
                    }).then(
                        res => {
                            success(res);
                        },
                        error => {
                            err(error);
                        }
                    );
                } else {
                    err(new Error('缺少请求方法this.$fetch'));
                }
            });
        },
        handleDownload() {
            const { fileName, fileUrl } = this.props;
            const name = this.viewer[fileName];
            const url = this.viewer[fileUrl];
            if (this.downloadFN) {
                this.downloadFN(this.viewer);
            } else {
                download(url, name);
            }
        }
    }
};
</script>
<style lang="less" scoped>
.image-viewer-wrapper {
    .image-btn {
        border: 1px solid #ccc;
        background: none;
        cursor: pointer;
    }
    .fixed-container {
        position: fixed;
        left: 0;
        top: 0;
        right: 0;
        bottom: 0;
        background-color: rgba(0, 0, 0, 0.6);
        z-index: 99;
    }
    .loading-wrapper {
        position: absolute;
        left: 50%;
        top: 50%;
        margin-left: -33.5px;
        margin-top: -33.5px;
        text-align: center;

        .icon-loading {
            display: block;
            width: 67px;
            height: 67px;
            background: url('../src/images/icon-loading.png') no-repeat;
            animation: rotating 0.5s linear infinite;
        }
        .text-loading {
            display: inline-block;
            margin-top: 10px;
            color: #fff;
            font-size: 14px;
        }
    }
    .img-wrapper {
        cursor: move;
        height: 100%;
    }
    .img-content {
        position: absolute;
        top: 40px;
        right: 40px;
        bottom: 80px;
        left: 40px;
        overflow: hidden;
        text-align: center;
        &::before {
            content: '';
            display: inline-block;
            height: 100%;
            vertical-align: middle;
        }
        .img-content_box {
            display: inline-block;
            width: 100%;
            height: 100%;
        }
        .img {
            display: inline-block;
            vertical-align: middle;
            max-width: 100%;
            max-height: 100%;
        }
    }
    .img-tooltip {
        position: absolute;
        bottom: 20px;
        width: 100%;
        height: 40px;
        text-align: center;

        .tooltip-wrapper {
            display: inline-block;
            padding: 12px 20px;
            height: 40px;
            background-color: rgba(0, 0, 0, 0.6);
            box-sizing: border-box;
            border-radius: 20px;
        }
        .tooltip-page,
        .tooltip-zoom,
        .tooltip-rotate {
            display: inline-block;
            height: 100%;
            border-right: 1px solid rgba(255, 255, 255, 0.3);
        }
        .icon-prev,
        .icon-next,
        .icon-zoom-in,
        .icon-zoom-out,
        .icon-rotate,
        .icon-download,
        .my-el-icon,
        .icon-close {
            display: inline-block;
            width: 16px;
            height: 16px;
            vertical-align: top;
            opacity: 0.8;
            transition: all 0.5 ease;
            cursor: pointer;
            &:hover {
                opacity: 1;
            }
        }
        .my-el-icon {
            color: #fff;
            text-align: center;
            font-size: 16px;
            background: url('../src/images/print.png') no-repeat;
            background-size: 100%;
        }
        .page-number {
            display: inline-block;
            margin: 0 15px;
            height: 16px;
            line-height: 16px;
            vertical-align: top;
            opacity: 0.8;
        }
        .tooltip-page {
            color: #fff;
            padding-right: 14px;
            margin-right: 14px;
            .icon-prev {
                background: url('../src/images/icon-prev.png') no-repeat;
            }
            .icon-next {
                background: url('../src/images/icon-next.png') no-repeat;
            }
        }
        .tooltip-zoom {
            padding-right: 14px;
            .icon-zoom-in {
                margin-right: 14px;
                background: url('../src/images/icon-zoom-in.png') no-repeat;
            }
            .icon-zoom-out {
                background: url('../src/images/icon-zoom-out.png') no-repeat;
            }
        }
        .tooltip-rotate {
            padding: 0 14px;
            .icon-rotate {
                margin-right: 14px;
                background: url('../src/images/icon-rotate.png') no-repeat;
            }
            .icon-download {
                margin-right: 14px;
                background: url('../src/images/icon-download.png') no-repeat;
            }
        }
        .tooltip-close {
            display: inline-block;
            padding-left: 14px;
            .icon-close {
                background: url('../src/images/icon-close.png') no-repeat;
            }
        }
    }
}

@keyframes rotating {
    0% {
        transform: rotateZ(0deg);
    }
    100% {
        transform: rotateZ(360deg);
    }
}
</style>
