Elements-SY

add components

<template>
<div
class="ylform"
:class="inline == true ? 'the-form' : ''"
:style="{ marginBottom: `${marginBottom}px` }"
>
<el-form
:model="tempQueryForm"
:inline="inline"
:label-position="position"
:label-width="formWidth"
:loading="loading"
ref="ruleForm"
:size="size"
>
<el-row
:gutter="rowGutter"
:type="rowType"
:justify="rowJustify"
:align="rowAlign"
:tag="rowTag"
>
<span v-for="item in tempFormConfig" :key="item.name">
<el-col
v-if="!item.hidden"
:span="item.span"
:offset="item.offset"
:tag="item.tag"
:push="item.push"
:pull="item.pull"
:xs="item.xs"
:sm="item.sm"
:md="item.md"
:lg="item.lg"
:xl="item.xl"
:style="{ width: item.colWidth }"
>
<el-form-item
:label="item.label"
:prop="item.prop"
:rules="item.rules"
:label-width="item.labelWidth"
>
<slot v-if="item.type === 'slot'"></slot>
<!-- 输入框 -->
<el-input
v-if="item.type === 'Input'"
v-model="tempQueryForm[item.prop]"
:style="{ width: item.inputWidth }"
:disabled="item.disable"
:placeholder="
item.placeholder ? item.placeholder : '请输入' + item.label
"
v-on:[item.trigger]="
(e) => {
inputEvent(e, item);
}
"
@keyup.enter.native="toEnterSearch"
clearable
>
</el-input>
<template v-if="view">
<span v-text="tempQueryForm[item.prop]"></span>
</template>
<!-- 数字输入框 -->
<el-input
v-if="item.type === 'inputNumber'"
type="number"
:min="item.min"
:max="item.max"
v-model.trim="tempQueryForm[item.prop]"
:style="{ width: item.inputWidth }"
:disabled="item.disable"
:placeholder="
item.placeholder ? item.placeholder : '请输入' + item.label
"
v-on:keyup.enter="value = value.replace(/^-?[0-9]\d*$/, '')"
clearable
>
</el-input>
<!-- InputNumber 计数器 -->
<el-input-number
v-if="item.type === 'Number'"
v-model.trim="tempQueryForm[item.prop]"
:min="0"
:max="10"
:size="item.size"
:style="{ width: item.inputWidth }"
label="描述文字"
></el-input-number>
<template v-if="view">
<span v-text="tempQueryForm[item.prop]"></span>
</template>
<!-- 下拉选择 -->
<el-select
v-if="item.type === 'Select'"
:multiple="item.multiple"
:filterable="item.filterable"
v-model.trim="tempQueryForm[item.prop]"
:style="{ width: item.inputWidth }"
:disabled="item.disable"
:loading="searchLoading"
:placeholder="
item.placeholder ? item.placeholder : '请选择' + item.label
"
clearable
>
<el-option
v-for="op in item.options.data"
:label="op[item.options.label] || op.label"
:value="op[item.options.value] || op.value"
:key="op[item.options.value] || op.value"
>
</el-option>
</el-select>
<!-- 远程搜索 -->
<template v-if="item.type === 'Search'">
<el-autocomplete
v-model.trim="tempQueryForm[item.prop]"
:style="{ width: item.inputWidth }"
:debounce="1500"
:fetch-suggestions="querySearchAsync"
:placeholder="
item.placeholder ? item.placeholder : '请选择' + item.label
"
@focus="focusKey(item)"
@select="selectSearch"
clearable
></el-autocomplete>
</template>
<!-- 日期 -->
<el-date-picker
v-if="item.type === 'Date'"
v-model.trim="tempQueryForm[item.prop]"
:style="{ width: item.inputWidth }"
:disabled="item.disable"
:placeholder="
item.placeholder ? item.placeholder : '请选择日期'
"
:value-format="item.format"
clearable
>
</el-date-picker>
<!-- 时间 -->
<el-time-select
v-if="item.type === 'Time'"
v-model.trim="tempQueryForm[item.prop]"
:style="{ width: item.inputWidth }"
:disabled="item.disable"
:placeholder="
item.placeholder ? item.placeholder : '请选择时间'
"
clearable
>
</el-time-select>
<!-- 日期时间 -->
<el-date-picker
v-if="item.type === 'DateTime'"
type="datetime"
v-model.trim="tempQueryForm[item.prop]"
:disabled="item.disable"
:placeholder="
item.placeholder ? item.placeholder : '请选择日期'
"
clearable
>
</el-date-picker>
<!-- 日期范围 -->
<el-date-picker
v-if="item.type === 'datetimerange'"
type="datetimerange"
v-model.trim="tempQueryForm[item.prop]"
:style="{ width: item.inputWidth }"
:disabled="item.disable"
range-separator="至"
start-placeholder="开始日期"
end-placeholder="结束日期"
clearable
>
</el-date-picker>
<!-- 单选框 普通的样式 Radio 的尺寸,仅在 border 为真时有效-->
<template v-if="item.type === 'radio'">
<el-radio
v-model.trim="tempQueryForm[item.prop]"
v-for="op in item.options.data"
:disabled="item.disable"
:border="tempQueryForm[item.border]"
:size="tempQueryForm[item.size]"
:label="op[item.options.label] || op.label"
:value="op[item.options.value] || op.value"
:key="op[item.options.value] || op.value"
></el-radio>
</template>
<!-- 单选框 按钮样式 -->
<el-radio-group
v-if="item.type === 'radioButtom'"
v-model.trim="tempQueryForm[item.prop]"
:size="tempQueryForm[item.size]"
:disabled="item.disable"
>
<el-radio-button
v-for="op in item.options.data"
:label="op[item.options.label] || op.label"
:value="op[item.options.value] || op.value"
:key="op[item.options.value] || op.value"
></el-radio-button>
</el-radio-group>
<!-- 文本框 -->
<el-input
v-if="item.type === 'textarea'"
v-model.trim="tempQueryForm[item.prop]"
type="textarea"
:style="{ width: item.inputWidth }"
:disabled="item.disable"
:rows="item.row"
:autosize="item.autosize"
:placeholder="
item.placeholder ? item.placeholder : '请输入' + item.label
"
clearable
>
</el-input>
<!-- 多选框 -->
<template v-if="item.type === 'Checkbox'">
<el-checkbox
v-model="tempQueryForm[item.prop]"
:disabled="item.disabled"
></el-checkbox>
</template>
</el-form-item>
</el-col>
</span>
<!-- 查询按钮 de-->
<span v-show="inline & hiddenFormBtn">
<el-form-item v-show="show">
<el-button
type="primary"
@click="handleSearch('ruleForm')"
:btnLoading="btnLoading"
>
{{ formBtn }}
</el-button>
</el-form-item>
</span>
<span v-show="inline & hiddenFormBtn">
<el-form-item v-show="show">
<el-button @click="resetFields('ruleForm')">重置</el-button>
</el-form-item>
</span>
</el-row>
</el-form>
</div>
</template>
<script>
import request from "@/utils/request";
import { deepClone } from "@/utils";
export default {
name: "YlForm",
props: {
rowGutter: {
type: Number,
default: 24,
},
rowType: {
type: String,
default: "",
},
rowJustify: {
type: String,
default: "",
},
rowAlign: {
type: String,
default: "",
},
rowTag: {
type: String,
default: "div",
},
// 由inline属性决定form表单是普通表单,还是行内表单
inline: {
//排列方式
type: Boolean,
default: true,
},
searchLoading: {
type: Boolean,
default: false,
},
hiddenFormBtn: {
//是否展示搜索按钮
type: Boolean,
default: false,
},
formWidth: {
//form表单宽度
type: String,
default: "auto",
},
position: {
type: String,
default: "",
},
formConfig: {
type: Array,
default: [],
},
queryForm: {
type: Object,
default: () => {},
},
// 用于控制该表单内组件的尺寸 medium / small / mini
size: {
type: String,
default: "small",
},
marginBottom: {
type: Number,
default: 0,
},
view: {
type: Boolean,
default: false,
},
formBtn: {
type: String,
default: "查询",
},
btnLoading: {
type: Boolean,
default: false,
},
loading: {
//加载
type: Boolean,
default: false,
},
isBtnSave: {
type: Boolean,
default: false,
},
},
data() {
return {
show: true,
tempQueryForm: {},
tempFormConfig: [],
fetchResult: [],
timeout: null,
searchMark: {}, // 搜索
};
},
mounted() {},
computed: {},
methods: {
// input事件绑定和注册
inputEvent(e, item) {
this.$emit(item.trigger, this.tempQueryForm);
},
// 回车事件
toEnterSearch() {
this.$emit("keyup", this.tempQueryForm);
},
// selectSearch 聚焦获取当前input绑定的数据
focusKey(item) {
this.searchMark = item;
},
// selectSearch 下拉选中获取当前input绑定的数据
selectSearch(item) {
console.log(item);
},
// 远程搜索查询
querySearchAsync(queryString, cb) {
let result = this.formConfig.filter((item) => {
if (item.hasOwnProperty("http")) {
if (item.prop === this.searchMark.prop) {
return item;
}
}
});
// 在搜索关键词不为空时发送http请求
if (queryString) {
if (queryString.split(" ").length > 0) {
if (result[0].http.method == "post") {
result[0].http.data[result[0].prop] = queryString.split(" ")[0];
} else {
result[0].http.params[result[0].prop] = queryString.split(" ")[0];
}
}
request(result[0].http).then((res) => {
if (res.code == 200) {
if (res.data.list.length) {
res.data.list.map((item) => {
item.value = `${item[result[0].prop]} ${item[result[0].name]}`;
});
this.fetchResult = res.data.list;
var results = queryString.split(" ")[0]
? this.fetchResult.filter(
this.createStateFilter(queryString.split(" ")[0])
)
: this.fetchResult;
clearTimeout(this.timeout);
this.timeout = setTimeout(() => {
cb(results);
}, 1500 * Math.random());
}
}
});
}
},
// 远程搜索匹配查询
createStateFilter(queryString) {
return (state) => {
return (
state[this.searchMark.prop]
.toLowerCase()
.indexOf(queryString.toLowerCase()) === 0
);
};
},
// 行内表单搜索事件
handleSearch(formName, cb) {
this.$refs[formName].validate((valid) => {
if (valid) {
cb(valid);
} else {
console.log("error submit!!");
return false;
}
});
this.$emit("handleSearch");
},
// 清空ruleForm表单事件
resetFields(formName = "ruleForm") {
this.$refs[formName].resetFields();
},
},
watch: {
queryForm: {
handler(val) {
this.tempQueryForm = val;
},
deep: true,
immediate: true,
},
formConfig: {
handler(val) {
this.tempFormConfig = deepClone(val);
},
deep: true,
immediate: true,
},
//
},
};
</script>
<style lang="scss" scoped></style>
<template>
<el-input
v-bind="$attrs"
clearable
@input="handleInput"
@focus="handleFocus"
@blur="handleBlur"
@change="handleChange"
class="yl-input"
></el-input>
</template>
<script>
export default {
props: {
rules: {
type: Array,
default: () => [],
},
},
data() {
return {};
},
computed: {},
created() {},
mounted() {},
methods: {
handleInput(event) {
// v-bind="$attrs" v-on="$listeners"
this.$emit("input", event);
},
handleFocus(event) {
this.$emit("focus", event);
},
handleBlur(event) {
this.$emit("blur", event);
},
handleChange(event) {
this.$emit("change", event);
},
},
};
</script>
<style lang='scss'>
</style>
<script>
/**
* 动态渲染 el-table-column
*/
export default {
name: 'column',
props: {
attrs: {
type: Object,
default: () => ({}),
required: true
}
},
render: function(h) {
let attrs = this.attrs;
let scopedSlots = {};
if (attrs.render) {
scopedSlots.default = scope => attrs.render(h, scope);
}
return h('el-table-column', {
attrs,
scopedSlots
});
}
};
</script>
<template>
<div class="table-container">
<el-row
v-if="attrs.btnCofig.isBtn"
style="margin-bottom: 15px"
id="group-btn"
>
<el-col>
<span
v-for="(item, i) in attrs.btnCofig.btnGroup"
:key="item.btnName"
:class="i == 0 ? 'first-span' : 'span'"
>
<el-button
:type="item.type"
:icon="item.icon"
:round="item.round"
:size="item.size"
:loading="item.loading"
@click="handleClick(item, i)"
v-if="item.btnName !== '导入' && item.btnName !== '增加'"
>{{ item.btnName }}</el-button
>
<el-upload
v-else
ref="upload"
:action="item.action"
:fileName="item.fileName"
:http-request="toUpload"
:on-remove="onRemoveUpload"
:limit="item.limit"
:accept="item.accept"
:show-file-list="false"
class="upload"
>
<el-button
:type="item.type"
:icon="item.icon"
:round="item.round"
:size="item.size"
:loading="item.loading"
>{{ item.btnName }}</el-button
>
</el-upload>
</span>
</el-col>
</el-row>
<el-table
:max-height="attrs.maxHeight"
:empty-text="emptyText"
v-bind="attrs"
v-loading="loadingTable"
:data="tableData"
@selection-change="selectionChange"
ref="tableRef"
row-key="id"
:header-cell-class-name="headerCellClassName"
>
<template v-if="columns.length">
<template v-for="item in columns">
<column v-if="!item.hidden" :key="item.id" :attrs="item"></column>
</template>
</template>
<slot name="column" v-else></slot>
</el-table>
<!-- 分页 -->
<div
v-if="pageConfig.isPagination"
id="pagination"
class="pagination-container"
:style="{ textAlign: pageConfig.position || 'right' }"
ref="paginationRef"
>
<el-pagination
background
:hide-on-single-page="false"
:current-page="pageConfig.pageData.page"
:page-sizes="[10, 15, 20, 25, 30, 35, 40]"
:page-size="pageConfig.pageData.size"
layout="total,prev, pager, next, jumper, ->, sizes"
:total="pageConfig.total"
@size-change="handleSizeChange"
@current-change="handleCurrentChange"
>
</el-pagination>
</div>
</div>
</template>
<script>
import Sortable from "sortablejs";
import { objectMerge, debounce } from "@/utils";
import column from "./column";
export default {
props: {
attrs: {
type: Object,
default: {
border: true,
isDragSort: false,
btnCofig: {
isBtn: true,
btnGroup: [
{
type: "primary", // text、primary、danger
icon: "el-icon-search", // el-icon-edit 、el-icon-delete、el-icon-plus、el-icon-download、el-icon-upload el-icon--right
btnName: "搜索",
size: "mini", // medium / small / mini
round: false,
loading: false,
event: "searchBtn",
},
],
},
},
},
emptyText: {
type: String,
default: "暂无数据",
},
loadingTable: {
type: Boolean,
default: false,
},
columns: {
type: Array,
default: () => [],
},
tableData: {
type: Array,
default: () => [],
},
pageConfig: {
type: Object,
default: {
isPagination: true,
},
required: true,
},
},
components: {
column,
},
data() {
return {
dropCol: objectMerge({}, this.columns),
trHeight: 4,
};
},
computed: {
_attrs() {
//默认table 参数
const defaultParams = {};
return Object.assign(defaultParams, this.attrs);
},
isHasAttr() {},
},
created() {
if (this.attrs.isDragSort) {
// 有没有复选框、有没有render
// this.columns
}
},
mounted() {
//阻止火狐拖拽新建新页面
document.body.addEventListener(
"drop",
(event) => {
event.preventDefault();
event.stopPropagation();
},
false
);
this.getTrCurrentHeight();
window.addEventListener("resize", this.getTrCurrentHeight);
},
destroyed() {
if (this.tableData.length) {
window.removeEventListener("resize", this.getTrCurrentHeight);
}
},
methods: {
getTrCurrentHeight: debounce(function () {
this.$nextTick(() => {
// const trEl =
// this.$refs.tableRef.$refs.bodyWrapper.children[0].children[1]
// .children[0];
// console.log(trEl)
// if (trEl) {
// this.trHeight = trEl.clientHeight;
// }
});
}, 800),
// 给表头列添加className
headerCellClassName({ row, column, rowIndex, columnIndex }) {
if (columnIndex !== 0) {
return "el-table_1_column";
}
},
useTableFunc(FuncName, ...params) {
if (!this.$refs["el-table"]) {
return console.warn("访问不到el-table");
}
if (!FuncName) {
return console.error("请传入tabel方法名字");
}
this.$refs["el-table"][FuncName](params[0]);
},
// table复选框事件
selectionChange(e) {
console.log(e);
this.$emit("selectionEvent", e);
},
// 条数变化
handleSizeChange(e) {
this.$emit("sizeChange", e);
},
// 页码变化
handleCurrentChange(e) {
this.$emit("currentChange", e);
},
// btn点击事件注册
handleClick(item, i) {
this.$emit(item.event, item, i);
},
// 拖拽行更新排序
rowDrop() {
const wrapperTr = document.querySelector(".el-table__header-wrapper tr");
// console.log("wrapperTr:", wrapperTr);
this.sortable = Sortable.create(wrapperTr, {
sort: this.attrs.isDragSort,
animation: 100,
delay: 0,
handle: ".move", // 只有带move类名的元素才能拖动,多选框禁止拖动
onEnd: (evt) => {
// 因为手动加了一个多选框, 不在表头循环数组内, 所以这里减1
let oldIndx = evt.oldIndex - 1;
let newIndx = evt.newIndex - 1;
const oldItem = this.dropCol[oldIndx];
// 真正改变列数据--变化列头,就能实现列拖动 列数据按列头索引取值 {{ scope.row[dropCol[index].prop] }}
this.dropCol.splice(oldIndx, 1); // 删除旧一行 删除为1
this.dropCol.splice(newIndx, 0, oldItem); // 插入新一行 插入为0
},
});
},
// 拖拽列更新排序
columnDrop() {
const tbody = document.querySelector(".el-table__body-wrapper tbody");
const _this = this;
Sortable.create(tbody, {
sort: this.attrs.isDragSort,
animation: 100,
delay: 0,
onStart(evt) {
// console.log("onStart:", evt);
evt.oldIndex;
},
onEnd({ newIndex, oldIndex }) {
// console.log("onEnd:", newIndex, oldIndex);
const currRow = _this.tableData.splice(oldIndex, 1)[0];
_this.tableData.splice(newIndex, 0, currRow);
},
});
},
// 选择上传
toUpload(options) {
this.$refs.upload.map((item) => {
item.clearFiles(); //上传成功之后清除历史记录
});
this.$emit("upload", options);
},
//移除上传
onRemoveUpload(file, fileList) {
this.$emit("rmfile", { file, fileList });
},
},
watch: {
data() {
this.$nextTick(() => {
this.useTableFunc("doLayout");
});
},
tableData() {
if (this.tableData.length && this.attrs.isDragSort) {
// 拖拽行更新排序
// this.rowDrop();
// 拖拽列更新排序
this.columnDrop();
}
},
},
};
</script>
<style lang="scss">
.el-table {
th {
padding: 0;
}
td {
padding: 0;
}
}
.first-span,
.span {
display: inline-block;
}
.span {
margin-left: 6px;
}
</style>