antd 表单图片上传、表格拖拽排序

1. 需求

需求1:antd 表单中图片收集
需求2:表格拖拽排序

2.说明

表单提交图片时,使用a-uupload 或者 el-upload 都需要阻止图片自动提交。
表格拖拽需要使用到vuedraggable,拖拽后,保存顺序,返回到后台。

3. 核心代码

DatabaseList.vue (父)
<template>
  <div class="content-wrap database-list">
    <!-- 按钮 -->
    <div class="database-list-btn">
      <div class="add_modal">
        <a-button type="dashed" icon="plus" @click="showModal">添加数据库</a-button>
        <a-modal
          title="添加数据库"
          v-model="visible"
          :footer="null"
          width="60%"
          style="margin:0 25%"
          okText="确认"
          cancelText="取消"
          @ok=""
        >
          <a-form :form="form" @submit="handleSubmit">
            <a-form-item label="分类英文" :label-col="{ span: 2 }" :wrapper-col="{ span: 6 }">
              <a-input placeholder="分类英文"
                       v-decorator="['name_en', { rules: [{ required: true, message: '输入不能为空!' }] }]"
              />
            </a-form-item>
            <a-form-item label="分类中文" :label-col="{ span: 2 }" :wrapper-col="{ span: 6 }">
              <a-input placeholder="分类中文"
                       v-decorator="['name_cn', { rules: [{ required: true, message: '输入不能为空!' }] }]"
              />
            </a-form-item>
            <a-form-item label="分类编码" :label-col="{ span: 2 }" :wrapper-col="{ span: 6 }">
              <a-input placeholder="分类编码"
                       v-decorator="['code', { rules: [{ required: true, message: '输入不能为空!' }] }]"
              />
            </a-form-item>
            <a-form-item label="封面设置" :label-col="{ span: 2 }" :wrapper-col="{ span: 12 }">
              <template>
                <div class="clearfix">
                  <a-upload
                    listType="picture-card"
                    :beforeUpload="beforeUpload"
                    class="avatar-uploader"
                    :showUploadList="false"
                    @change="handleChange"
                    v-decorator="['cover', { rules: [{ required: true, message: '封面必须上传!' }] }]"
                  >
                    <!--直接上传base64数据,进行后台保存-->
                    <img v-if="imageUrl" :src="imageUrl" alt="cover" height="100px"/>
                    <div v-else>
                      <a-icon :type="loading ? 'loading' : 'plus'"/>
                      <div class="ant-upload-text">上传</div>
                    </div>
                  </a-upload>
                </div>
              </template>
            </a-form-item>
            <a-form-item label="备注" :label-col="{ span: 2 }" :wrapper-col="{ span: 6 }">
              <a-textarea placeholder="备注信息" :rows="4"
                          v-decorator="['des', { rules: [{ required: true, message: '输入不能为空!' }] }]"/>
            </a-form-item>
            <a-form-item :wrapper-col="{ span: 12, offset: 5 }">
              <a-button type="primary" html-type="submit">提交</a-button>
              <a-button :style="{ marginLeft: '8px' }" @click="handleReset">
                重置
              </a-button>
            </a-form-item>
          </a-form>
        </a-modal>
      </div>
      <a-button type="dashed" icon="reload" @click="reload()">刷新</a-button>
      <a-button :type="type?'danger':'dashed'" :icon="type?'check-circle':'edit'" @click="onedit()">
        {{type?'完成':'拖拽表格'}}
      </a-button>
    </div>
    <DataTable :type="type"/>
  </div>
</template>

<script>
    import {mapGetters} from 'vuex'
    import DataTable from "@/components/Database/DataTable"
    import {update_structure, add_type} from '@/api/db'

    function getBase64(img, callback) {
        const reader = new FileReader();
        reader.addEventListener('load', () => callback(reader.result));
        reader.readAsDataURL(img);
    }

    export default {
        name: "databaselist",
        data() {
            return {
                type: false,
                visible: false,
                form: this.$form.createForm(this, {name: "add_database"}),
                imageUrl: '',
                loading: false,
            }
        },
        components: {
            DataTable
        },
        methods: {
            showModal() {
                this.visible = true;
            },
            handleReset() {
                this.imageUrl = ''
                // 发送请求删除图片
                this.form.resetFields();
            },
            handleSubmit(e) {
                e.preventDefault();
                this.form.validateFields((err, values) => {
                    if (!err) {
                        console.log("Received values of form: ", values);
                        values.cover = this.file
                        add_type(values).then(res => {
                            console.log(res)
                        }).catch(err => {
                            console.log(err)
                        })
                    }
                })
            },
            handleChange(info) {
                if (info.file.status === 'uploading') {
                    this.loading = true;
                    return;
                }
                if (info.file.status === 'done') {
                    // Get this url from response in real world.
                    getBase64(info.file.originFileObj, imageUrl => {
                        this.imageUrl = imageUrl;
                        this.loading = false;
                    });
                }
            },
            beforeUpload(file) {
                const isJPG = file.type === 'image/jpeg';
                if (!isJPG) {
                    this.$message.error('You can only upload JPG file!');
                }
                const isLt2M = file.size / 1024 / 1024 < 20;
                if (!isLt2M) {
                    this.$message.error('Image must smaller than 20MB!');
                }
                //创建临时的路径来展示图片
                const windowURL = window.URL || window.webkitURL;
                this.imageUrl = windowURL.createObjectURL(file);
                this.file = file
                //阻止默认提交
                // return isJPG && isLt2M;
                return false
            },
            // 点击刷新
            reload() {
                console.log('1111');

            },
            // 点击编辑表格,拖拽
            onedit() {
                this.type = !this.type
                if (!this.type) {
                    // 完成编辑模式时,发送请求,修改数据
                    const data = this.db_types.map((item) => {
                        return item.id
                    });
                    update_structure(data).then(res => {
                        this.$message({
                            message: '更新成功',
                            type: 'success'
                        })
                    }).catch(err => {
                        console.log(err)
                    })
                }
            }
        },
        computed: {
            ...mapGetters([
                'db_types'
            ]),
        }
    }
</script>

<style lang="scss" scoped>
  .database-list {
    &-btn {
      display: flex;
      padding: 0 0 20px 0;

      .ant-btn,
      .ant-btn:active,
      .ant-btn:focus {
        margin: 0 15px 0 0;
      }
    }
  }
</style>
<style scoped>
  .avatar-uploader > .ant-upload {
    width: 300px;
    height: 300px;
  }

  .ant-upload-select-picture-card i {
    font-size: 32px;
    color: #999;
  }

  .ant-upload-select-picture-card .ant-upload-text {
    margin-top: 8px;
    color: #666;
  }
</style>

DataTable(子)
<template>
  <div class="table_wrap">
    <!-- <el-table :data="tableData" border style="width: 96%; margin: 10px auto">
    <el-table-column type="index" width="100" label="序号"></el-table-column>
    <el-table-column prop="name_en" label="分类英文" width="180"></el-table-column>
    <el-table-column prop="name_zh" label="分类中文" width="180"></el-table-column>
    <el-table-column prop="t_code" label="分类编码"></el-table-column>
    <el-table-column prop="t_des" label="备注"></el-table-column>
    <el-table-column prop="add_time" label="创建时间"></el-table-column>
    <el-table-column prop="active" label="状态">
      <el-button type="success" plain size="mini">启用中</el-button>
    </el-table-column>
    <el-table-column label="操作">
      <template slot-scope="scope">
        <el-button size="mini" @click="handleEdit(scope.$index, scope.row)">编辑</el-button>
        <el-button size="mini" type="danger" @click="handleDelete(scope.$index, scope.row)">删除</el-button>
      </template>
    </el-table-column>
    </el-table>-->
    <!-- antd -->
    <!--    <a-table :columns="columns" :dataSource="db_types" :pagination="false" bordered>-->
    <!--      <template-->
    <!--        v-for="col in ['name_en', 'name_cn', 'des','code']"-->
    <!--        :slot="col"-->
    <!--        slot-scope="text, record, index"-->
    <!--      >-->
    <!--        <div :key="col">-->
    <!--          <a-input-->
    <!--            v-if="record.editable"-->
    <!--            style="margin: -5px 0"-->
    <!--            :value="text"-->
    <!--            @change="e => handleChange(e.target.value, record.key, col)"-->
    <!--          />-->
    <!--          <template v-else>{{text}}</template>-->
    <!--        </div>-->
    <!--      </template>-->
    <!--      <template slot="active" slot-scope="active">-->
    <!--        <span>-->
    <!--          &lt;!&ndash; <a-tag v-for="item in active" color="blue" :key="item">{{item}}</a-tag> &ndash;&gt;-->
    <!--          <a-switch :defaultChecked="active?true:false" @change="onChange">-->
    <!--            <a-icon type="check" slot="checkedChildren"/>-->
    <!--            <a-icon type="close" slot="unCheckedChildren"/>-->
    <!--          </a-switch>-->
    <!--        </span>-->
    <!--      </template>-->
    <!--      <template slot="operation" slot-scope="text, record, index">-->
    <!--        <div class="editable-row-operations">-->
    <!--          <span v-if="record.editable">-->
    <!--            <a @click="() => save(record.key)">确认</a>-->
    <!--            <a-popconfirm title="确定取消本次编辑吗?" @confirm="() => cancel(record.key)" okText="确定" cancelText="取消">-->
    <!--              <a>取消</a>-->
    <!--            </a-popconfirm>-->
    <!--          </span>-->
    <!--          <span v-else>-->
    <!--            <a @click="() => edit(record.key)">编辑</a>-->
    <!--          </span>-->
    <!--        </div>-->
    <!--      </template>-->
    <!--    </a-table>-->
    <!--原生table-->
    <table class="table">
      <thead>
      <th>序号</th>
      <th>中文</th>
      <th>英文</th>
      <th>编码</th>
      <th>备注</th>
      <th>创建时间</th>
      <th>封面</th>
      <th>状态</th>
      <th>操作</th>
      </thead>
      <draggable v-model="db_types" ghost-class="ghost" :disabled="!type" element="tbody">
        <tr v-for="(item,index) in db_types" :key="index" :class="{'tr_cursor':type}">
          <td width="5%">
            {{item.key}}
          </td>
          <td width="14%">
            <a-input
              v-if="item.editable"
              style="margin: -5px 0"
              :value="item.name_cn"
              @change="e => handleChange(e.target.value, item.key,'name_cn')"/>
            <span v-else>{{item.name_cn}}</span>
          </td>
          <td width="14%">
            <a-input
              v-if="item.editable"
              style="margin: -5px 0"
              :value="item.name_en"
              @change="e => handleChange(e.target.value, item.key,'name_en')"/>
            <span v-else>{{item.name_en}}</span>
          </td>
          <td width="8%">
            <a-input
              v-if="item.editable"
              style="margin: -5px 0"
              :value="item.code"
              @change="e => handleChange(e.target.value, item.key,'name_code')"/>
            <span v-else>{{item.code}}</span>
          </td>
          <td width="12%">
            <a-input
              v-if="item.editable"
              style="margin: -5px 0"
              :value="item.des"
              @change="e => handleChange(e.target.value, item.key,'des')"/>
            <span v-else>{{item.des}}</span>
          </td>
          <td width="12%">
            {{item.add_time}}
          </td>
          <td width="12%">
            {{item.cover}}
          </td>
          <td width="8%">
            <a-switch :defaultChecked="item.active?true:false"
                      @change="onChange(item.key,!item.active,'active')">
              <a-icon type="check" slot="checkedChildren"/>
              <a-icon type="close" slot="unCheckedChildren"/>
            </a-switch>
          </td>
          <td width="15%">
            <span v-if="item.editable">
              <a @click="() => save(item.key)" class="btn ok_btn">确认</a>
              <a-popconfirm title="确定取消本次编辑吗?" @confirm="() => cancel(item.key)" okText="确定" cancelText="取消">
              <a class="btn no_btn">取消</a>
              </a-popconfirm>
            </span>
            <span v-else class="span">
              <span type="primary" @click="edit(item.key)" class="btn tr_cursor">编辑</span>
              <span type="primary" @click="del(item.key)" class="btn tr_cursor">删除</span>
            </span>
          </td>
        </tr>
      </draggable>
    </table>
  </div>
</template>

<script>
    import draggable from "vuedraggable";
    import {update_data, update_status} from '@/api/db'

    export default {
        name: "data_table",
        data() {
            return {}
        },
        props: {type: Boolean},
        components: {
            draggable
        },
        methods: {
            // 发生修改单行语句时,回调函数
            handleChange(value, key, column) {
                const newData = [...this.db_types];
                const target = newData.filter(item => key === item.key)[0];
                if (target) {
                    target[column] = value;
                    this.db_types = newData;
                }
            },
            // 编辑单行
            edit(key) {
                const newData = [...this.db_types];
                const target = newData.filter(item => key === item.key)[0];
                if (target) {
                    target.editable = true;
                    this.db_types = newData;
                }
            },
            del() {
                console.log('删除')
            },
            // 保存单行数据
            save(key) {
                const newData = [...this.db_types];
                const target = newData.filter(item => key === item.key)[0];
                if (target) {
                    delete target.editable;
                    this.db_types = newData;
                    // 保存单条修改语句--持久化
                    const id = target.id;
                    const {name_cn, name_en, code, des} = target;
                    const data = {
                        id, name_cn, name_en, code, des
                    };
                    update_data(data).then(res => {
                        this.$message({
                            message: '修改成功',
                            type: 'success'
                        })
                    }).catch(err => {
                        console.log(err)
                    })
                }
            },
            cancel(key) {
                const newData = [...this.db_types];
                const target = newData.filter(item => key === item.key)[0];
                if (target) {
                    Object.assign(
                        target,
                    );
                    delete target.editable;
                    this.db_types = newData;
                }
            },
            onChange(key, bol, col) {
                const newData = [...this.db_types];
                const target = newData.filter(item => key === item.key)[0];
                if (target) {
                    target[col] = bol;
                    this.db_types = newData;
                    // 向后台发送状态修改请求
                    update_status({id: target.id, bol, type: 'db_type'}).then(res => {
                        this.$message({
                            message: res.msg,
                            type: 'success'
                        })
                    }).catch(err => {
                        console.log(err)
                    })
                }
            }
        },
        computed: {
            db_types: {
                get() {
                    return this.$store.getters.db_types.map((item, index) => {
                        item['key'] = index + 1;
                        return item
                    })
                },
                set(val) {
                    // 修改state中的值
                    this.$store.commit('db/SET_TYPES', val)
                }
            }
        }
    };
</script>

<style lang="scss" scoped>
  .btn {
    margin-right: 15px;
    display: inline-block;
    color: white;
    padding: 4px 15px;
    border-radius: 4px;
  }

  .ok_btn {
    color: #28B78D;
    border: 1px solid #28B78D;
  }

  .ghost {
    opacity: 0.5;
    background: #c8ebfb;
  }

  .no_btn {
    color: #ccc;
    border: 1px solid #ccc;
  }

  .table {
    font-size: 14px;
    line-height: 24px;
    width: 100%;
    border-collapse: collapse;
  }

  .tr_cursor {
    cursor: pointer;
  }

  table, th, td {
    border: 1px solid #eaeaea;
  }

  th {
    padding: 6px 0;
    font-weight: normal;
    color: #333;
    height: 55px;
    background-color: #fcfcfc;
  }

  tr:hover {
    background-color: #e9f7f0;
  }

  td, th {
    text-align: left;
    padding: 10px 15px;
  }

  td {
    color: #777;

    .span {
      span {
        /*margin-right: 15px;*/
        /*display: inline-block;*/
        /*color: white;*/
        /*padding: 5px 15px;*/
        /*border-radius: 4px;*/
        &:nth-child(1) {
          background-color: rgb(64, 158, 255);

          &:hover {
            background-color: rgba(64, 158, 255, .7);
          }
        }

        &:nth-child(2) {
          background-color: #f5222d;

          &:hover {
            background-color: rgba(245, 35, 45, .7);
          }
        }
      }
    }
  }


</style>

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

推荐阅读更多精彩内容