<!--
 * @Author: 贺永胜
 * @Date: 2021-06-07 11:15:14
 * @email: 1378431028@qq.com
 * @LastEditors: 贺永胜
 * @LastEditTime: 2021-06-30 10:02:45
 * @Description: 点击编辑
-->
<template>
    <div style="height: 100%">
        <div class="wrap">
            <!-- input -->
            <div class="input-wrap" v-if="type === 'input'">
                <div class="input-text" @click="showEdit" v-show="!editVisible">
                    {{ value }}
                </div>
                <div class="input-edit" v-show="editVisible">
                    <el-input
                        v-model="editValue"
                        type="text"
                        :placeholder="placeholder"
                        v-bind="prop.options"
                        @input="filterValue"
                        @blur="editSubmit('blur')"
                        @keyup.enter.native="editSubmit('enter')"
                        ref="editInput"
                    />
                </div>
            </div>

            <!-- textarea -->
            <div class="textarea-wrap" v-if="type === 'textarea'">
                <div class="textarea-text" @click="showEdit" v-show="!editVisible">
                    {{ value }}
                </div>
                <div class="textarea-edit" v-show="editVisible">
                    <el-input
                        v-model="editValue"
                        type="textarea"
                        autosize
                        :placeholder="placeholder"
                        v-bind="prop.options"
                        @input="filterValue"
                        @blur="editSubmit('blur')"
                        @keyup.enter.native="textareaEnter"
                        ref="editTextarea"
                    ></el-input>
                </div>
            </div>

            <!-- number -->
            <div class="number-wrap" v-if="type === 'number'">
                <div class="number-text" @click="showEdit" v-show="!editVisible">
                    {{ value }}
                </div>
                <div class="number-edit" v-show="editVisible">
                    <el-input
                        type="text"
                        v-model="editValue"
                        :placeholder="placeholder"
                        v-bind="prop.options"
                        @blur="editSubmit('blur')"
                        @keyup.enter.native="editSubmit('enter')"
                        @input="numValid('editValue')"
                        @focus="numValid('editValue')"
                        ref="editNumber"
                    ></el-input>
                </div>
            </div>

            <!-- date -->
            <div class="date-wrap" v-if="type === 'date'">
                <div class="date-text" @click="showEdit" v-show="!editVisible">
                    {{ value }}
                </div>
                <div class="date-edit" v-show="editVisible">
                    <el-date-picker
                        ref="datePicker"
                        v-model="editValue"
                        :placeholder="placeholder"
                        v-bind="prop.options"
                        @change="editSubmit('change')"
                        @blur="editSubmit('blur')"
                    />
                </div>
            </div>

            <!-- timePicker -->
            <div class="timePicker-wrap" v-if="type === 'timePicker'">
                <div class="timePicker-text" @click="showEdit" v-show="!editVisible">
                    {{ value }}
                </div>
                <div class="timePicker-edit" v-show="editVisible">
                    <el-time-picker
                        ref="timePicker"
                        v-model="editValue"
                        :placeholder="placeholder"
                        value-format="HH:mm:ss"
                        v-bind="prop.options"
                        @change="editSubmit('change')"
                        @blur="editSubmit('blur')"
                    />
                </div>
            </div>

            <!-- timeSelect -->
            <div class="timeSelect-wrap" v-if="type === 'timeSelect'">
                <div class="timeSelect-text" @click="showEdit" v-show="!editVisible">
                    {{ value }}
                </div>
                <div class="timeSelect-edit" v-show="editVisible">
                    <el-time-select
                        ref="timeSelect"
                        v-model="editValue"
                        :placeholder="placeholder"
                        v-bind="prop.options"
                        @change="editSubmit('change')"
                        @blur="editSubmit('blur')"
                    />
                </div>
            </div>

            <!-- select -->
            <div class="date-wrap" v-if="type === 'select'">
                <div class="date-text" @click="showEdit" v-show="!editVisible">
                    {{ selectValue }}
                </div>
                <div class="date-edit" v-show="editVisible">
                    <el-select
                        ref="editSelect"
                        v-model="editValue"
                        :placeholder="placeholder"
                        v-bind="prop.options"
                        @visible-change="
              (visible) => {
                visible || editSubmit('blur')
              }
            "
                    >
                        <!-- 此处不用blur事件也是因为有问题，选值的时候也会触发blur，听说开启筛选时blur也会有问题 -->
                        <el-option
                            v-for="item in prop.data"
                            :key="item.value"
                            :label="prop.label ? item[prop.label] : 'label'"
                            :value="prop.value ? item[prop.value] : 'value'"
                        />
                    </el-select>
                </div>
            </div>

            <!-- cascader -->
            <div class="cascader-wrap" v-if="type === 'cascader'">
                <div class="cascader-text" @click="showEdit" v-show="!editVisible">
                    {{ selectValue }}
                </div>
                <div class="cascader-edit" v-show="editVisible">
                    <el-cascader
                        ref="cascader"
                        v-model="editValue"
                        :placeholder="placeholder"
                        v-bind="prop.options"
                        :options="prop.data"
                        :props="prop.props"
                        @visible-change="
              (visible) => {
                visible || editSubmit('blur')
              }
            "
                    />
                </div>
            </div>
        </div>
    </div>
</template>

<script>
export default {
    name: 'view-edit',
    props: {
        //  要展示和编辑的值
        value: {
            default: '默认值',
            type: [String, Number]
        },
        // （必填）组件类型
        type: {
            type: String,
            default: 'input'
        },
        // id 可选 将在change事件返回，可用于区分页面内多个此组件
        id: {
            type: [String, Number],
            default: undefined
        },
        placeholder: {
            type: String,
            default: ''
        },
        prop: {
            type: Object,
            default() {
                return {}
            }
        }
    },
    model: {
        prop: 'value',
        event: 'input'
    },
    data() {
        return {
            editValue: '',
            editVisible: false,
            selectValue: '' // 由于传入的值一般为id，需要回显为正确的名称
        }
    },
    watch: {
        value: {
            handler(newVal) {
                switch (this.type) {
                    case 'select':
                        // 如果是多选模式，需要将传入的字符串改为数组
                        if (this.prop.options && this.prop.options.multiple) {
                            this.editValue = this.value.split(',')
                            // numberId 表示下拉框数据的id为数字，需要将值的每一项转为数字以便回显正常
                            if (this.prop.numberId) {
                                this.editValue = this.editValue.map((item) => {
                                    return +item
                                })
                            }
                            this.selectValue = this.editValue
                                .map((item) => {
                                    return this.selectAllData[item]
                                })
                                .join(',')
                        } else {
                            this.editValue = +this.value
                            this.selectValue = this.selectAllData[newVal]
                        }
                        break
                    case 'cascader': {
                        let copyValue = newVal
                        if (typeof copyValue === 'string') {
                            copyValue = copyValue.split(',')
                        }
                        this.selectValue = copyValue
                            .map((item) => {
                                return this.cascaderAllData[item]
                            })
                            .join(',')
                    }
                        break

                    default:
                        break
                }
            },
            immediate: true,
            timer: null
        }
    },
    mounted() {
        // console.log(this.$refs.cascader)
    },
    computed: {
        /**
         * @name: 将下拉框数据改为以value为键，以label为值的形式
         * @param {*}
         * @return {*}
         */
        selectAllData() {
            const valueData = {}
            const labelName = this.prop.label || 'label'
            const valueName = this.prop.value || 'value'
            this.prop.data.map((item) => {
                valueData[item[valueName]] = item[labelName]
            })
            return valueData
        },
        cascaderAllData() {
            const valueData = {}
            const labelName = this.prop.props.label || 'label'
            const valueName = this.prop.props.value || 'value'
            const treeArr = []
            // prop.data
            this.treeToTile(this.prop.data, treeArr)
            treeArr.map((item) => {
                valueData[item[valueName]] = item[labelName]
            })

            return valueData
        }
    },
    methods: {
        /**
         * @name: 显示编辑框
         * @param {*}
         * @return {*}
         */
        showEdit() {
            // clearDefault 表示点击编辑时显示空的输入框
            // 如果没传或者传入值为false，则展示组件默认展示的值
            // editValue为处理后的值，如果有则展示，如果无则直接展示传入值
            this.editValue = this.prop.clearDefault
                ? ''
                : this.editValue || this.value
            this.editVisible = true
            switch (this.type) {
                case 'input':
                    this.$nextTick(() => {
                        this.$refs.editInput.focus()
                    })
                    break

                case 'textarea':
                    this.$nextTick(() => {
                        this.$refs.editTextarea.focus()
                    })
                    break

                case 'number':
                    this.$nextTick(() => {
                        this.$refs.editNumber.focus()
                    })
                    break

                case 'date':
                    this.$nextTick(() => {
                        this.$refs.datePicker.focus()
                    })
                    break

                case 'timeSelect':
                    this.$nextTick(() => {
                        this.$refs.timeSelect.focus()
                    })
                    break

                case 'timePicker':
                    this.$nextTick(() => {
                        this.$refs.timePicker.focus()
                    })
                    break

                case 'select':
                    this.$nextTick(() => {
                        // 为什么不用focus呢？因为el-select用focus只能获取到焦点，但不能弹出下拉框
                        this.$refs.editSelect.$el.querySelector('input').click()
                    })
                    break

                case 'cascader':
                    this.$nextTick(() => {
                        this.$refs.cascader.$el.querySelector('input').click()
                    })
                    break
            }
        },
        /**
         * @name: textarea回车事件
         * @param {*}
         * @return {*}
         */
        textareaEnter() {
            if (!this.prop.wrap) {
                this.editValue = this.editValue.replace(/[\r\n]/g, '')
                this.editSubmit('enter')
            }
        },
        /**
         * @name: 确认编辑事件
         * @param {String} handleName // 事件名称 enter / blur ……
         * @param {event} event // 事件
         * @return {*}
         */
        editSubmit(handleName) {
            // 由于回车事件触发后隐藏输入框会同时触发输入框的blur事件，所以要判断输入框当前是否为隐藏状态
            if (this.editVisible) {
                this.editVisible = false
                // 如果新值为空或者新值和旧值一样，则不进行操作
                if (!this.editValue || this.editValue === this.value) return false
                this.$emit('change', {
                    id: this.id,
                    value: this.editValue.toString(),
                    handleName
                })
                this.$emit('input', this.editValue.toString())
            }
        },
        /**
         * @name: 校验数字，移除字符内非数字内容
         * @param {String} name 变量名
         * @return {*}
         */
        numValid(name) {
            this[name] = this[name]
                // .replace(/^0/, '') // 不能以0开头
                .replace(/^0[^\.]{1,}/, '') // 如果开头为0的话，0后面只能跟小数点
                .replace(/^\./, '') // 不能以小数点开头开头
                .replace(/[^0-9.-]/g, '') // 不能包含减号小数点和数字之外的字符
                .replace('.', '$#$') // 将第一个小数点转其他字符
                .replace(/\./g, '') // 删除字符中所有的小数点
                .replace('$#$', '.') // 将第一个小数点恢复
                .replace(/^-/, '$#$') // 将开头的减号转其他字符
                .replace(/-/g, '') // 删除字符中所有的减号
                .replace('$#$', '-') // 将第一个减号恢复
        },
        /**
         * @name: 树形结构数据转为平行数组
         * @param {Array} arr 目标数组
         * @param {*} result 返回数组
         * @return {*}
         */
        treeToTile(arr, result) {
            arr.map((item) => {
                item.children &&
                item.children.length > 0 &&
                this.treeToTile(item.children, result)
                result.push(item)
            })
        },
        /**
         * @name: 输入值过滤，如果传入正则表达式，则按表达式过滤
         * @param {*}
         * @return {*}
         */
        filterValue() {
            if (!this.prop.wrap) {
                this.editValue = this.editValue.replace(/[\r\n]/g, '')
            }
            // 如果用户输入内容不符合规范，并且组件传入提示内容，则对用户进行提示
            // 为防止用户输入过快频繁触发，有时间可以加上节流
            if (!this.timer) {
                if (
                    this.editValue &&
                    this.prop.reg &&
                    this.prop.reg.test(this.editValue) &&
                    this.prop.regMessage
                ) {
                    this.$message({
                        type: 'error',
                        message: this.prop.regMessage,
                        showClose: true
                    })
                }
                this.timer = true
                setTimeout(() => {
                    this.timer = false
                }, 1000)
            }

            this.editValue = this.editValue.replace(this.prop.reg, '')
        }
    }
}
</script>
<style lang="less" scoped>
.wrap {
    height: 100%;
    overflow: hidden;
}

// .customer-input {
//   width: 100%;
//   box-sizing: border-box;
//   border: 1px solid #dcdfe6;
//   padding: 0.36em 0.72em;
//   font-size: inherit;
//   font-size: 0.9em;
//   outline: none;
//   border-radius: 0.3em;
// }
// .textarea-wrap {
//   .textarea-edit {
//     position: relative;
//     .textarea {
//       resize: none;
//       word-break: break-all;
//       font-weight: inherit;
//       position: absolute;
//       height: 100%;
//       line-height: 1.5;
//       top: 0;
//       left: 0;
//       overflow: hidden;
//       font-family: inherit;
//     }
//     .pre {
//       width: 100%;
//       display: block;
//       margin: 0;
//       box-sizing: border-box;
//       white-space: pre-wrap;
//       word-break: break-all;
//       font-weight: inherit;
//       line-height: 1.5;
//       font-family: inherit;
//       visibility: hidden;
//       min-height: 3em;
//       font-size: 0.9em;
//       padding: 0.36em 0.72em;
//     }
//   }
// }
</style>
