src
目录下创建components/index.js
const components = require.context("./modules", true, /index\.js$/);
export default {
install: (Vue) => {
components.keys().forEach((key) => {
let component = components(key).default;
component && Vue.component(component.name, component);
});
},
};
创建src/components/modules
目录
form
modules
下创建modules/x-form/index.js
import XForm from "./index.vue";
export default XForm;
创建modules/x-form/index.vue
src/components
创建createInput.js
import { isEqual } from "lodash";
export const tagsMenu = new Map([
["text", "el-input"],
["xnumber", "x-input-number"],
["button", "el-button"],
["input", "el-input"],
["password", "el-input"],
["textarea", "el-input"],
["number", "el-input-number"],
["radio", "el-radio-group"],
["radio-item", "el-radio"],
["radio-button", "el-radio-button"],
["checkbox", "el-checkbox-group"],
["checkbox-item", "el-checkbox"],
["checkbox-button", "el-checkbox-button"],
["select", "el-select"],
["select-group", "el-option-group"],
["select-item", "el-option"],
["cascader", "el-cascader"],
["switch", "el-switch"],
["slider", "el-slider"],
["time-select", "el-time-select"],
["time", "el-time-picker"],
["date", "el-date-picker"],
["rate", "el-rate"],
["color", "el-color-picker"],
["transfer", "el-transfer"],
]);
export const defaultValuesMenu = new Map([
["radio", ""],
["checkbox", []],
["text", ""],
["input", ""],
["password", ""],
["textarea", ""],
["number", ""],
["xnumber", ""],
["select", null],
["cascader", null],
["switch", ""],
["slider", 0],
["time-select", ""],
["time", ""],
["date", ""],
["rate", null],
["color", null],
["transfer", []],
]);
export function getDefaultValue(form, defaultValue = {}, item) {
if (!item.prop) return;
let value = defaultValuesMenu.get(item.type);
if (item?.props?.multiple || item?.props?.isRange) value = [];
form[item.prop] =
form[item.prop] || item.default || defaultValue[item.prop] || value;
}
export function getPlaceholder(item) {
const inputTypes = ["input", "password", "textarea", "number", "xnumber"];
const selectType = ["select", "cascader", "time-select", "time", "date"];
const dateRangeTypes = ["datetimerange", "daterange", "monthrange"];
item.attrs = item.attrs || {};
item.props = item.props || {};
// 添加date-range的标识
if (item.props?.type && dateRangeTypes.includes(item.props.type)) {
item.props.isRange = true;
}
if (item.placeholder) {
item.attrs.placeholder = item.placeholder;
} else {
if (inputTypes.includes(item.type))
item.attrs.placeholder = `请输入${item.label}`;
if (selectType.includes(item.type))
item.attrs.placeholder = `请选择${item.label}`;
if (item.props.isRange) {
item.props.startPlaceholder = `请选择开始${item.label}`;
item.props.endPlaceholder = `请选择结束${item.label}`;
}
}
return item;
}
export function getRules(item, rules) {
if (item.rules) return item;
else if (rules && rules[item.prop]) {
item.rules = rules[item.prop];
} else if (item.required) {
item.rules = [
{
required: true,
message:
item.attrs.placeholder ||
item.props.startPlaceholder ||
item.props.endPlaceholder,
trigger: "blur",
},
];
}
return item;
}
export function createInput(h, item, _this) {
item.attrs = {
type: item.type,
options: item.type === "cascader" ? item.options : null,
...item.attrs,
};
item.props = {
disabled: item.disabled !== undefined ? item.disabled : _this.disabled,
...item.props,
};
if (item.btnType) item.attrs.type = item.btnType;
if (item.btnIcon) item.attrs.icon = item.btnIcon;
if (item.marking) item.attrs.marking = item.marking;
item.style = {
width: item.type !== "button" ? "100%" : null,
...item.style,
};
let {
type,
prop,
className = {},
style = {},
attrs = {},
props = {},
on = {},
text = "",
} = item;
const config = {
type,
prop,
class: className,
style,
attrs,
props,
on,
text,
children: generateOptions(h, item) || [],
};
if (type === "time-select" || type === "date") {
let minTimeKey = props?.pickerOptions?.minTimeKey;
let maxTimeKey = props?.pickerOptions?.maxTimeKey;
if (minTimeKey) {
if (type === "time-select") {
config.props = {
...config.props,
pickerOptions: {
...config.props.pickerOptions,
minTime: _this.form[minTimeKey],
},
};
} else {
let disabledDate = config.props.pickerOptions.disabledDate;
if (typeof disabledDate === "function") {
config.props = {
...config.props,
pickerOptions: {
...config.props.pickerOptions,
disabledDate: (time) =>
disabledDate(time, _this.form[minTimeKey]),
},
};
}
}
}
if (maxTimeKey) {
if (type === "time-select") {
config.props = {
...config.props,
pickerOptions: {
...config.props.pickerOptions,
maxTime: _this.form[maxTimeKey],
},
};
} else {
let disabledDate = config.props.pickerOptions.disabledDate;
if (typeof disabledDate === "function") {
config.props = {
...config.props,
pickerOptions: {
...config.props.pickerOptions,
disabledDate: (time) =>
disabledDate(time, _this.form[maxTimeKey]),
},
};
}
}
}
}
if (type === "text") {
config.class["el-readonly-input"] = true;
config.attrs.readonly = true;
}
return generateTag(h, config, _this);
}
export function generateOptions(h, item) {
const canTag = ["radio", "checkbox", "select"];
if (!canTag.includes(item.type)) return null;
// 后续需要添加字典
const options = item?.options || [];
return (options || []).map((option) => {
let type = `${item.type}-${item.button ? "button" : "item"}`;
if (option.options) {
const cloneItem = { ...item };
cloneItem.options = option.options;
type = `${item.type}-group`;
return h(
tagsMenu.get(type),
{
attrs: {
label: option.label,
},
},
generateOptions(h, cloneItem),
);
}
return h(
tagsMenu.get(type),
{
attrs: {
label: item.type === "select" ? option.label : option.value,
value: item.labelInValue ? option : option.value,
},
},
[item.type === "select" ? null : [option.label]],
);
});
}
export function generateTag(h, config, _this) {
config.props.value = _this.form[config.prop];
const inputEvent = config.on?.input;
const clickEvent = config.on?.click;
config.on = {
...config.on,
input: (value) => {
value = formatDateValue(value, config);
if (!isEqual(_this.form[config.prop], value)) {
_this.form[config.prop] = value;
typeof inputEvent === "function" && inputEvent(value, config);
}
},
click: (event) => {
typeof clickEvent === "function" && clickEvent(event, config);
},
};
config.nativeOn = {
keydown: (e) => {
if (e.keyCode === 13 && this.enterSubmit && config.type !== "textarea") {
_this.submit();
}
},
};
return h(tagsMenu.get(config.type), config, [
...config.children,
config.text,
]);
}
export function formatDateValue(value, config) {
if ((config.type === "time" || config.type === "date") && !value) {
return config.props.isRange ? ["", ""] : "";
}
return value;
}
创建src/styles/components.less
,并引入style/index.less
/* x-form 组件样式 */
.el-readonly-input {
.el-input__inner {
border: none;
}
.el-input__inner:focus {
border-color: #DCDFE6;
}
}
.form-item-col {
.el-form-item {
margin-bottom: 22px;
}
}
/* x-table 组件样式 */
使用案例
table
创建components/modules/x-table/index.js
import XTable from "./index.vue";
export default XTable;
创建components/modules/x-table/index.vue
修改src/styles/components.less
...
/* x-table 组件样式 */
.x-table-wrap {
width: 100%;
.x-table-bottom {
display: flex;
justify-content: space-between;
align-items: center;
margin-top: 3px;
font-size: 13px;
font-weight: 400;
color: #606266;
flex-direction: row-reverse;
}
}
.x-table-radio {
::v-deep {
.el-table__header-wrapper {
.el-checkbox {
display: none;
}
}
}
}
/* dialog 组件样式 */
使用案例
基础空表单
基础表单
自动填充和分页
自动填充高度,并且能自动处理radio、checkbox、select的值
自定义内容
多表头
多选
获取选中结果
设置选择第二项, 第三项
清空选中
单选
获取选中结果
设置选择第三项
清空选中
排序和筛选
嵌套内容
接口获取
搜索
mockjs
function getList() {
let list = new Array(99).fill({});
list = list.map((item, index) => {
return {
name: index > 20 ? `张三${index}` : "张三",
age: index.toString(),
date: Mock.mock("@date('yyyy-MM-dd')"),
sex: (index % 2).toString(),
address: `北京市朝阳区${index}号`,
};
});
return new MockPort({
template: {
code: 200,
msg: "success",
data: {
records: [],
pagesiez: 0,
current: 1,
total: 0,
},
},
action(options) {
const params = this.paramsBackRes(options.body) || {};
let { pagesize, pageno, ...search } = params;
pagesize = pagesize || 10;
pageno = pageno || 1;
let records = list.filter((item) => isMatch(item, search));
this.template.data = {
records: [...records].splice((pageno - 1) * pagesize, pagesize),
total: records.length,
pagesize,
current: pageno,
};
return this.template;
},
});
}
dialog
searchForm
searchTable
dialogForm
navTab
updateFile
lazy
theme-color
切换主题色、使用上面的动态切换主题原理实现
screenfull
全屏按钮、使用插件screenfull
screenshot
截屏按钮、使用插件html2canvas
input-number
tree