vue 带上传功能组件封装,级联选择器封装,附件预览组件封装

父组件,可渲染数据

<!--

 组件:职称填报
 created by hexiaobo

 参数说明:

 方法:
  setProfessionValue:向父组件传id,
  setProfessionLabel:向父组件传label,
 -->

<template>
    <div>
        <el-row :gutter="40" style="margin-left: 0;">
            <template v-if="data && data.length > 0">
                <el-col  :xs="24" :sm="12" :md="12" :lg="8" v-for="(item,index) in data" :key="index" style="padding-left: 0;">
                    <div class="zige-item card">

                        <el-popconfirm
                                title="确定删除吗?"
                                v-if="editAble"
                                @onConfirm="professionDel(item.id,index)"
                        >
                            <a class="del" slot="reference">
                                <i class="el-icon-delete"></i>
                            </a>
                        </el-popconfirm>

                        <div style="font-size: 20px;height: 60px;color: #333;padding-right: 30px;">
                            <svg class="icon" aria-hidden="true">
                                <use xlink:href="#icon-shouye-jieyezhengshu"></use>
                            </svg>
                            {{item.professionName}}
                        </div>
                        <div style="font-size: 14px;line-height: 30px;">职称编号:{{item.professionNumber}}</div>
                        <div style="font-size: 14px;line-height: 30px;margin-bottom: 15px;">获取时间:{{$formats.YYYYMMDDByHoriLine(item.certificateGetDate)}}</div>
                        <a class="showFiles" @click="showFileImg(item.files)">查看附件</a>
                    </div>
                </el-col>
            </template>
            <template v-else><el-col>暂无职称记录</el-col></template>
        </el-row>

        <!-- 查看附件 -->
        <files-preview
                :filesPreviewArr="this.filesPreviewArr"
                :dialogVisible.sync="dialogVisible"
        />
        <!--/. 查看附件 -->


        <!-- 新增form -->
        <div class="" v-if="data.length < 6 && editAble" style="margin-top: 1rem;">
            <el-form ref="professionsHolderForm" :inline="true" :model="professionsHolderForm">

                <el-form-item
                        label=""
                        :rules="[{ required: true, message: '请选择职称', trigger: 'change' }]"
                        prop="professionId"
                >
                    <profession-picker
                            ref="professionPicker"
                            :data="professionsHolderForm.professionId"
                            @setProfessionValueFC="setProfessionValue"
                            @setProfessionLabelFC="setProfessionLabel" />

                </el-form-item>
                <el-form-item label=""
                              prop="professionNumber"
                              :rules="[{ required: true, message: '请输入证书号', trigger: 'blur' }]"
                >
                    <el-input
                            v-model="professionsHolderForm.professionNumber"
                            placeholder="证书号"
                    ></el-input>
                </el-form-item>
                <el-form-item label=""
                              prop="certificateGetDate"
                              :rules="[{ required: true, message: '请选择时间', trigger: 'blur' }]"
                >
                    <el-date-picker
                            v-model="professionsHolderForm.certificateGetDate"
                    />

                </el-form-item>
                <el-form-item>
                    <el-button type="success" @click="addTitles('professionsHolderForm')">新增</el-button>
                </el-form-item>

                <div style="padding: 20px 0 10px">
                    <el-form-item prop="files" :rules="[{ required: true, message: '请上传附件'}]">
                        <el-upload
                                class="upload-box"
                                :on-success="handleUploadSuccess"
                                :before-upload="beforeUpload"
                                :on-remove="handleFileRemove"
                                :headers = "headers"
                                ref="professionUploader"
                                :limit="4"
                                :on-exceed="handleExceed"
                                :action="$baseUrl.common + '/file'"
                                >
                            <el-button size="mini" type="primary">点击上传</el-button>
                            <div slot="tip" class="" style="display: inline-block;padding-left: 1rem;font-size: 12px;">
                                请上传jpg/png/pdf文件,不超过2M
                            </div>
                        </el-upload>
                    </el-form-item>

                </div>
            </el-form>
        </div>
        <!--/. 新增form -->
    </div>
</template>

<script>
    import Vue from "vue"
    import {DatePicker,Dialog,Message,Carousel,CarouselItem} from "element-ui"
    import ProfessionPicker from "./pickers/ProfessionPicker";
    import FilesPreview from "./FilesPreview";

    Vue.use(DatePicker);
    Vue.use(Dialog);
    Vue.use(Carousel);
    Vue.use(CarouselItem);
    export default {
        name: "ProfessionalTitles",
        components: {FilesPreview, ProfessionPicker},
        data() {
            return {
                headers:{},
                options: [],
                filesPreviewArr: [], //附件查看数组
                dialogVisible: false,

                //职称暂存
                professionsHolderForm: {
                    "professionId":"",
                    "professionName": "",
                    "files": [],
                    "professionNumber": "",
                    "certificateGetDate": "",
                },
            }
        },
        props: {
            data: "",
            editAble:{default:true},
            width: {
                default: "80%"
            }

        },
        mounted() {
            //this.getOptions();
            this.headers ={Authorization : 'Bearer '+(sessionStorage.getItem('token') || '')}
        },
        methods: {
            getOptions() {
                this.$HTTP.get(this.$baseUrl.global + '/titles-option', {
                    params: {}
                }).then(response => {
                    let data = response.data;
                    this.options = data.data;
                })
            },
            //查看附件图片
            showFileImg(files){
                this.dialogVisible = true;
                this.filesPreviewArr = files;
            },
            setProfessionValue(v){
                this.professionsHolderForm.professionId = v;
            },
            setProfessionLabel(v){
                //取最后一级
                this.professionsHolderForm.professionName = v;
            },
            //新增职称
            addTitles(formName) {
                this.$refs[formName].validate((valid) => {
                    if (valid) {
                        //Message.success('新增成功');
                        if(this.isRepeat()){
                            this.$emit('professionAdd',this.professionsHolderForm);
                            this.professionsHolderForm = {
                                "professionId": "",
                                "files": [],
                                "professionName": "",
                                "professionNumber": "",
                                "certificateGetDate": ""
                            }
                        }

                    } else {
                        console.log('error submit!!');
                        return false;
                    }
                });
            },
            //判断是否重复添加
            isRepeat(){
                let idArr = [],
                    data = this.data,
                    nowId =  this.professionsHolderForm.professionId;
                for(let i in data){
                    idArr.push(data[i].professionId);
                }
                if(idArr.includes(nowId)){
                    Message.warning('该职称已存在。');
                    return false;
                }else{
                    return true;
                }
            },
            //附件上传成功
            handleUploadSuccess(response, file ,fileList){
                console.log(file)
                let data = response;
                if(!data.code){
                    this.professionsHolderForm.files.push({
                        id:file.response.data,
                        name:file.name,
                        type:file.raw.type
                    });
                    this.$refs.professionsHolderForm.validateField('files')
                }else{
                    Message.error(data.msg);
                    this.clearUploadeList()
                }
            },
            //上传之前的检查
            beforeUpload(file) {
                const isJPG = file.type === 'image/jpeg' || file.type === 'image/png' || file.type === 'application/pdf';
                const isLt = file.size / 1024 < 2000;
                if (!isJPG) {
                    Message.error('请上传jpg/png/pdf文件');
                    return false;
                }
                if (!isLt) {
                    Message.error('图片大小不能超过 2M!');
                    return false;
                }
                return isJPG && isLt;
            },
            //删除数组中特定项
            deleteItem(item, list){
                for (var key in list) {
                    list[key].id === item ? list.splice(key, 1) : null;
                }
            },
            //删除附件
            handleFileRemove(file, fileList){
                let id=file.response.data;
                this.deleteItem(id,this.professionsHolderForm.files); //删除数组中特定id项
            },
            handleExceed(files, fileList) {
                Message.warning("附件数量已到上限。");
            },
            //清空上传列表
            clearUploadeList(){
                this.$refs.professionUploader.clearFiles()
            },
            //删除职称
            professionDel(id,index){
                this.$emit("professionDel",id,index)
            },
            clearPicker(){
                this.$refs.professionPicker.clearCascader();
            },
            clearForm(){
                this.clearUploadeList();
                this.clearPicker();
            }
        },
        computed: {

        },
        watch: {
            /*'professionsHolderForm.professionId':{
                handler:function(newV){
                    if(newV){
                        this.$refs.professionsHolderForm.validateField('professionId')
                    }
                }
            }*/
        }

    }
</script>

<style scoped lang="scss">

    .icon{
        width: 1.4em;
        height: 1.4em;
        vertical-align: middle;
    }
    .card{
        @extend %card;
        height: 200px;
        padding: 20px;
        color: #6d7592;
    }
    .zige-item{
        font-size: 14px;
        line-height: 26px;
        margin-bottom: .4rem;
        width: 100%;
        position: relative;

        a.showFiles{
            border-radius: 3px;
            padding: 4px 10px;
            background: $mainGraybg;
            color: #737373;
        }
        .del{
            position: absolute;
            top: 7px;
            height: 46px;
            padding: 0 15px;
            line-height: 46px;
            font-size: 16px;
            right: 7px;
            &:hover{
                background-color: $mainGraybg;
            }
        }
    }
</style>

级联选择器:

<!--

 组件:职称选择器
 created by hexiaobo

 参数说明:
 -->

<template>

    <el-cascader
            v-model="dataHolder"
            :options="options"
            :style="'width:'+ width"
            :props='{value:"id",label:"name",children:"list"}'
            :show-all-levels="false"
            placeholder="请选择职称"
            ref="cascader"
            @change="changeCascader"
    >
    </el-cascader>

</template>

<script>
    import Vue from "vue"
    import {Cascader} from "element-ui"

    Vue.use(Cascader);
    export default {
        name: "ProfessionPicker",
        data() {
            return {
                dataHolder: "",
                options: [],
                form: {}
            }
        },
        props: {
            data: {},
            width: {
                default: "100%"
            }

        },
        mounted() {
            if (sessionStorage.getItem("base_code")) {
                this.options = JSON.parse(sessionStorage.getItem("base_code")).tcprofession;
            } else {
                this.getOptions();
            }
        },
        methods: {
            getOptions() {
                this.$HTTP.get(this.$baseUrl.codeData + '/base_code.json')
                    .then(response => {
                        var obj = response.data;
                        sessionStorage.setItem("base_code", JSON.stringify(obj));
                        this.options = JSON.parse(sessionStorage.getItem("base_code")).tcprofession;
                    });
            },
            changeCascader(val) {
                let nodesObj = this.$refs['cascader'].getCheckedNodes();
                console.log(nodesObj);
                this.form.label = nodesObj[0].pathLabels.join("-");
                this.form.value = nodesObj[0].path;
                //向父级传值
                let lastId = this.form.value[this.form.value.length -1];
                let lastLabel = this.form.label.split("-")[this.form.label.split("-").length -1];


                this.$emit('setProfessionValueFC', lastId);
                this.$emit('setProfessionLabelFC', lastLabel);
            },
            //清空数据
            clearCascader(){
                this.dataHolder ="";
                this.form={
                    value:"",
                    label:""
                }
            },
            //拿到末级code,造出全级数组
            codeFormat(code){
                let arr=[],
                    lv1 = code.substring(0,2)+'0';
                arr.push(lv1,code);
                return arr;
            }
        },
        computed: {

        },
        watch: {
            data:{
                handler:function(nv,ov){
                    if(nv){
                        this.dataHolder = this.codeFormat(nv);
                    }
                },
                immediate:true
            }
        }

    }
</script>

<style scoped lang="scss">

</style>

附件预览组件:

<!--

 组件:附件预览
 created by hexiaobo

 参数说明:

 方法:

 -->

<template>
    <div>

        <el-dialog :visible.sync="dialogVisible" @close="close" width="100%" height="100%" style="height: 80%;top: 10%;width: 80%;left: 10%;">
            <el-carousel height="500px" :autoplay="false">
                <el-carousel-item v-for="file in filesPreviewArr" :key="file.id">
                    <template v-if="file.type === 'application/pdf'">
                        <a target="_blank" :href="$baseUrl.common + '/file/'+file.id" class="btn-main-theme btn-md">点击查看pdf文件</a>
                        <!--<iframe :src="$baseUrl.common + '/file/'+file.id" frameborder="0" style="width: 100%;height: 100%;"></iframe>-->
                    </template>
                    <template v-else>
                        <img style="max-height: 100%;width: auto;height: auto;max-width: 100%"
                             :src="$baseUrl.common + '/file/'+file.id" alt="file.name">
                    </template>

                </el-carousel-item>
            </el-carousel>
        </el-dialog>

    </div>
</template>

<script>
    import Vue from "vue"
    import {Carousel,CarouselItem} from "element-ui"
    Vue.use(Carousel);
    Vue.use(CarouselItem);

    export default {
        name: "FilesPreview",
        components: {},
        data() {
            return {
                //dialogVisible: false
            }
        },
        props: {
            filesPreviewArr: "",
            dialogVisible:{
                default:false
            }
        },
        mounted() {

        },
        methods: {
            close(){
                this.$emit("update:dialogVisible",false);
            }
        },
        computed: {

        },
        watch: {

        }

    }
</script>

<style scoped lang="scss">
    .el-carousel__item{
        @include display-flex();
        @include justify-content(center);
        @include align-items(center);
        overflow: auto;
    }

</style>
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 199,393评论 5 467
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 83,790评论 2 376
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 146,391评论 0 330
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 53,703评论 1 270
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 62,613评论 5 359
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,003评论 1 275
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,507评论 3 390
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,158评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,300评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,256评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,274评论 1 328
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,984评论 3 316
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,569评论 3 303
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,662评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,899评论 1 255
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,268评论 2 345
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 41,840评论 2 339