vue3公共组件

119 阅读3分钟

tableSearch.vue table公共组件

<template>
    <div>
        <el-form :inline="true" label-width="12rem" :model="searchform" v-if="searchList.length > 0">
            <el-row>
                <el-col :span="8" v-for="(item, index) in searchList" :key="'search' + index"
                    v-show="index < searchShowNum || (index >= searchShowNum && searchShow)">
                    <el-form-item :label="item.label">
                        <el-input v-if="item.type == 'input'" :placeholder="item.pl" v-model="searchform[item.prop]" clearable/>
                        <el-select v-else-if="item.type == 'select'" :placeholder="item.pl"
                            v-model="searchform[item.prop]">
                            <el-option v-for="(item2, index2) in item.options" :key="'search' + item.prop + index2"
                                :label="item2[item.optionSetting.label]" :value="item2[item.optionSetting.value]" />
                        </el-select>
                        <elSelectLoad v-if="item.type == 'selectload'" @getData="item.setting.getData" v-model:value="searchform[item.prop]" :obj="item.setting.obj" :loadmoreClass="item.setting.loadmoreClass" />
                    </el-form-item>
                </el-col>

                <el-col :span="7" :offset="1">
                    <el-button type="primary" @click="search">查询</el-button>
                    <el-button plain @click="clearAction">清空</el-button>
                    <span class="screen" @click="searchShow = !searchShow"  v-if="searchList.length > searchShowNum">
                        {{ searchShow ? "收起查询条件" : "展开查询条件" }}
                        <el-icon v-show="!searchShow">
                            <ArrowDown />
                        </el-icon>
                        <el-icon v-show="searchShow">
                            <ArrowUp />
                        </el-icon>
                    </span>
                </el-col>
            </el-row>
        </el-form>
        <el-row :gutter="10" v-if="optionObj.topBtn && optionObj.topBtn.length > 0">
            <el-col :span="24">
                <slot v-for="(item) in optionObj.topBtn" :name="item"></slot>
            </el-col>
        </el-row>

        <el-table v-loading="loading" :data="tableData" border
            :header-cell-style="{ background: '#FAFAFA', borderRight: 'none' }" style="width: 100%">
            <el-table-column v-for="(item, index) in tableList" :key="'table' + index" :prop="item.prop"
                :label="item.label" :width="item.width" align="center" show-overflow-tooltip>
                <template #default="scope">
                    <slot v-if="item.slot" :name="item.prop" :row="scope.row"></slot>
                </template>
            </el-table-column>
        </el-table>
        <el-pagination v-if="isPagination" v-model:currentPage="searchform.page" :page-size="searchform.size"
            :page-sizes="[10, 30, 50, 100]" layout="total, prev, pager, next, sizes , jumper" :total="total"
            @size-change="showSizeChangeEl" @current-change="handlePageChanges" />
    </div>
</template>

<script setup>
import { ref, computed, watchEffect, watch } from 'vue';
import { deepClone } from '@/utils/index.js';
const prop = defineProps({
    optionObj: {
        type: Object,
        default: () => {
            return {

            };
        }
    },
    searchShowNum: { //搜索栏显示个数
        type: Number,
        default: 2
    },
    isPagination: { //是否分页
        type: Boolean,
        default: true
    },
    fixSearch: {
        type: Object,
        default: null
    },
    extraSearch: {
        type: Object,
        default: null
    },
    initGetData: {
        type: Boolean,
        default: true
    }
});
const emit = defineEmits(['getData']);
const searchShow = ref(false);
const loading = ref(false);
const searchform = ref({});
const total = ref(0);
const searchList = computed(() => {
    return prop.optionObj.table.filter((item) => {
        return item.type;
    });
});
const tableList = computed(() => {
    return prop.optionObj.table.filter((item) => {
        return !item.notable;
    });
});
const tableData = ref([]);
const searchformcp = computed(() => {
    console.log(prop.fixSearch);
    let obj = {};
    searchList.value.forEach((item) => {
        obj[item.prop] = '';
    });
    if (prop.isPagination) {
        obj = Object.assign({}, obj, { page: searchform.value.page || 1, size: searchform.value.size || 10 });
    }
    
    if(prop.fixSearch){
        obj = Object.assign({}, obj, prop.fixSearch);
    }
    return obj;
});

watchEffect(() => {
    searchform.value = deepClone(searchformcp.value);
});
// watch(() => prop.extraSearch, (e) => {
//     searchform.value = Object.assign({}, searchform.value, e);
//     search();
// }, { deep: true });
// watchEffect(() => {

// })
function search () {
    searchform.value.page = 1;
    getdata();
}
function extraSearchFunc (){
    searchform.value = Object.assign({}, searchform.value, prop.extraSearch);
    search();
}
function clearAction () {
    searchform.value = deepClone(searchformcp.value);
}
function showSizeChangeEl (size) {
    searchform.value.size = size;
    searchform.value.page = 1;
    getdata();
}
function handlePageChanges (page) {
    searchform.value.page = page;
    getdata();
}
function getdata (){
    loading.value = !loading.value;
    emit('getData', searchform.value, successfunc, errorfunc);
    console.log(prop.fixSearch);
}
function successfunc (data, totals){
    console.log(data, totals);
    loading.value = false;
    tableData.value = data;
    total.value = totals;
}
function errorfunc (err){
    console.log(err);
    loading.value = false;
    tableData.value = [];
    total.value = 0;
}
if(prop.initGetData){
    getdata();
}
defineExpose({
    searchform,
    getdata,
    extraSearchFunc
});
</script>

<style lang="scss" scoped>
.el-pagination{
    margin-top: 1.5rem;
    margin-right: 1.5rem;
    margin-bottom: 4rem;
    justify-content: flex-end;
    position: relative;
}

/* 更具体的 CSS 选择器 */

:deep(.el-pagination__total) {

    position: absolute;
    left: 3rem;
    transform: translateY(-50%);
}


:deep(.el-table) {
    transform: scale(1);
    transform-origin: 0 0;
}
:deep(.el-table tr) {
    height: 5.2rem;
}
:deep(.el-pagination__editor.el-input) {
    width: 5.6rem;
}
:deep(.el-table th > .cell) {
  border-right: 1px solid #d8e3e5;
}
:deep(.el-table--border td,
.el-table--border th,
.el-table__body-wrapper .el-table--border.is-scrolling-left ~ .el-table__fixed) {
  border-right: none !important;
}
:deep(.el-table) {
    margin-top: 1rem;
    overflow-x: auto !important;
    overflow-y: auto;
}
:deep(.el-table__body-wrapper) {
    overflow-y: auto;
}
:deep(.el-pager li.is-active) {
    color: var(--themeColor);
}
:deep(.el-pager li:hover) {
    color: var(--themeColor);
}
:deep(.el-pagination button:hover) {
    color: var(--themeColor);
}
:deep(.el-table__header th div.cell) {
    color: #002226;
}
</style>


elSelectLoad.vue 分页加载select下拉框公共组件

<template>
    <el-select v-model="newValue" clearable placeholder="请选择" 
        v-select-loadmore:[loadmoreClass]="onLoadmore"
        :popper-class="loadmoreClass"
        filterable remote :remote-method="userSearch" @visible-change="handleDropdownVisibleChange">
            <el-option
            v-for="item in userList"
            :key="item.id"
            :label="item.name"
            :value="returndata?item.name:item"
            />
    </el-select>
</template>

<script setup>
import { watch, ref } from 'vue';

const prop = defineProps({
    value: {
        type: String,
        default: ''
    },
    loadmoreClass: {
        type: String,
        default: ''
    },
    obj: {
        type: Object,
        default: () => {
            return {};
        }
    },
    returndata: {
        type: Number,
        default: 1
    }
});
const newValue = ref(prop.value);
const newObj = ref(prop.obj);
const userList = ref([]);

const emit = defineEmits(['update:value', 'getData']);
watch(newValue, (newValue) => {
    emit('update:value', newValue);
});

// 下拉框滚动
function onLoadmore (){
    newObj.value.page++;
    getUsers();
};
function userSearch (e) {
    if (e) {
        newObj.value.search = e;
        userList.value = [];
        newObj.value.page = 1;
        getUsers();
    }
};
function getUsers (){
    emit('getData', newObj.value, (list) => {
        list.map((item) => {
            userList.value.push(item);
        });
    });
    
}
// 下拉框关闭
function handleDropdownVisibleChange (open){
    if (!open) {
        userList.value = [];
        newObj.value.page = 1;
        newObj.value.search = '';
    }
    else getUsers();
};
</script>

<style lang="scss" scoped>

</style>

表格组件使用规则

//isPagination是否分页,默认分页 @getData调用的接口 下面方法固定写法,将数组和total传过去,fixSearch参数为搜索固定条件,不会变动必带的参数,如果需要父组件中变动的参数,使用extraSearch,改了extraSearch,需要重新调用暴露出来的获取数据方法,对应initGetData属性,设为false默认不调用获取数据接口,等待你主动调
<TableSearch ref="tableref" :optionObj="optionObj" :initGetData="flase" @getData="getDataList" :isPagination="false" :fixSearch="{ worksapceId: $route.query?.id }" :extraSearch="extraSearch">
    <template v-for="(item,index) in optionObj.topBtn" #[item] :key="index">
        <el-button class="plain" plain @click="ActionFunc(index)">{{ item }}</el-button>
    </template>
    <template #action="{row}">
            <span class="mainColor" @click="getDetail(row.id, 1)">编辑</span>
            <span class="mainColor" style="margin-left: 1rem" @click="getDetail(row.id, 2)">删除</span>
        </template>
</TableSearch>
const optionObj = reactive({
    topBtn: ['新增'], //顶部按钮
    table: [
        { prop: 'name', label: '标准名称', type: 'input', pl: '请输入标准名称', notable: true },
        { prop: 'selectTest', label: '选择框', type: 'select', pl: '请输入标准名称', notable: true,options: [{ name: '张三', key: '1' }],optionSetting: { label: 'name', value: 'key' }  },
        { prop: 'cnName', label: '标准名称' },
        { prop: 'enName', label: '标准英文缩写', type: 'input' },
        { prop: 'creator', label: '创建人', type: 'selectload', setting: {
            loadmoreClass: 'single22-select-loadmore',
            obj: {
                page: 1,
                size: 10,
                search: '',
                status: 1
            },
            getData: getUsersCopy
        } },
        { prop: 'createTime', label: '创建时间', width: '200px' },
        { prop: 'modifier', label: '最近更新人' },
        { prop: 'updateTime', label: '最近更新时间', width: '200px' },
        { prop: 'action', label: '操作', slot: true, width: '220px' }
    ]
});
const extraSearch = reactive({})
function ActionFunc (index){
    console.log('🚀 ~ file: workspace.vue:35 ~ ActionFunc ~ index:', index);

}
// 获取数据
function getDataList (data, callback, error) {
    nextTick(() => {
        getField(data).then((res) => {
            callback && callback(res.data.records, res.data.totals);
        })
        .catch((err) => {
            error && error(err);
        });
    });

};
//selectload 获取数据
async function getUsersCopy (data, callback) {
    const res = await getUser(data);
    callback && callback(res.records);
}