Merge branch 'next' into feat-tag

This commit is contained in:
sight 2022-08-27 14:09:28 +08:00
commit b0bccc3292
44 changed files with 1839 additions and 1078 deletions

View File

@ -6,6 +6,8 @@
- 3.合理的建议
- 4.当前版本
## 思路
描述大概的解决思路,可以包含 API 设计和伪代码等

View File

@ -1,15 +1,17 @@
## background
## 背景
- 1.Describe the problem you want to solve
- 1.描述你希望解决的问题
- 2.State the status of the problem
- 2.陈述问题的现状
- 3.Reasonable suggestion
- 3.合理的建议
## train of thought
- 4.当前版本
Idea describes the general solution idea, including API design, pseudo code, etc
## 思路
## follow-up
描述大概的解决思路,可以包含 API 设计和伪代码等
For subsequent editing, attach the corresponding pull request address in the form of ` - [] some task '.
## 跟进
后续编辑,附上对应的 Pull Request 地址,可以用 `- [ ] some task` 的方式。

View File

@ -11,8 +11,6 @@
layui - vue谐音类 UI) 是 一 套 Vue 3.0 的 桌 面 端 组 件 库.
QQ 群 [➀ 336720831](https://jq.qq.com/?_wv=1027&k=oQA1SC80)
**Run with code Sandbox.**
[![Edit layui-vue](https://codesandbox.io/static/img/play-codesandbox.svg)](https://codesandbox.io/s/11mvy)

View File

@ -10,8 +10,6 @@
layui - vue谐音类 UI) 是 一 套 Vue 3.0 的 桌 面 端 组 件 库.
QQ 群 [➀ 336720831](https://jq.qq.com/?_wv=1027&k=oQA1SC80)
**Run with code Sandbox.**
[![Edit layui-vue](https://codesandbox.io/static/img/play-codesandbox.svg)](https://codesandbox.io/s/11mvy)

View File

@ -1,6 +1,6 @@
{
"name": "@layui/layui-vue",
"version": "1.4.0-alpha.1",
"version": "1.4.5",
"author": "就眠儀式",
"license": "MIT",
"description": "a component library for Vue 3 base on layui-vue",

View File

@ -16,6 +16,14 @@
font-size: 14px;
}
.layui-card .layui-card-footer {
height: 42px;
line-height: 42px;
padding: 0 15px;
border-top: 1px solid #f6f6f6;
font-size: 14px;
}
.layui-card .layui-card-header .layui-card-header-extra {
float: right;
}

View File

@ -44,5 +44,8 @@ const classes = computed(() => {
<slot name="body" v-if="slot.body"></slot>
<slot v-else></slot>
</div>
<div class="layui-card-footer" v-if="slot.footer">
<slot name="footer"></slot>
</div>
</div>
</template>

View File

@ -12,10 +12,11 @@
<lay-input
:name="name"
:readonly="readonly"
v-model="dateValue"
:placeholder="placeholder"
prefix-icon="layui-icon-date"
:prefix-icon="prefixIcon"
:suffix-icon="suffixIcon"
:disabled="disabled"
v-model="dateValue"
v-if="!range"
@change="onChange"
:allow-clear="!disabled && allowClear"
@ -130,6 +131,8 @@ export interface LayDatePickerProps {
readonly?: boolean;
allowClear?: boolean;
size?: "lg" | "md" | "sm" | "xs";
prefixIcon?: string;
suffixIcon?: string;
}
const props = withDefaults(defineProps<LayDatePickerProps>(), {
@ -142,6 +145,8 @@ const props = withDefaults(defineProps<LayDatePickerProps>(), {
readonly: false,
allowClear: false,
size: "md",
prefixIcon: "layui-icon-date",
suffixIcon: "",
});
const dropdownRef = ref(null);
@ -293,22 +298,29 @@ watch(
props.range && props.modelValue
? (props.modelValue as string[])[0] || ""
: (props.modelValue as string);
hms.value.hh = dayjs(initModelValue).hour();
hms.value.mm = dayjs(initModelValue).minute();
hms.value.ss = dayjs(initModelValue).second();
hms.value.hh = isNaN(dayjs(initModelValue).hour())
? 0
: dayjs(initModelValue).hour();
hms.value.mm = isNaN(dayjs(initModelValue).minute())
? 0
: dayjs(initModelValue).minute();
hms.value.ss = isNaN(dayjs(initModelValue).second())
? 0
: dayjs(initModelValue).second();
if (initModelValue.length === 8 && props.type === "time") {
//dayjs
let modelValue = initModelValue;
modelValue = "1970-01-01 " + modelValue;
hms.value.hh = dayjs(modelValue).hour();
hms.value.mm = dayjs(modelValue).minute();
hms.value.ss = dayjs(modelValue).second();
}
currentYear.value = initModelValue ? getYear(initModelValue) : -1;
currentMonth.value = initModelValue ? getMonth(initModelValue) : -1;
currentDay.value = initModelValue ? getDay(initModelValue) : -1;
if (props.type === "date" || props.type === "datetime") {
// datedatetime
if (currentYear.value === -1) currentYear.value = dayjs().year();
if (currentMonth.value === -1) currentMonth.value = dayjs().month();
}

View File

@ -0,0 +1,33 @@
<script lang="ts">
export default {
name: "TeleportWrapper",
};
</script>
<script lang="ts" setup>
import { onMounted, ref } from "vue";
export interface TeleportWrapperProps {
to?: string;
disabled?: boolean;
}
const props = withDefaults(defineProps<TeleportWrapperProps>(), {
to: "",
disabled: false,
});
const target = ref<Element | null>(null);
onMounted(() => {
if (!props.to) return;
const el = document.querySelector(props.to);
if (el) {
target.value = el;
}
});
</script>
<template>
<Teleport :to="target" :disabled="!target || disabled">
<slot></slot>
</Teleport>
</template>

View File

@ -6,7 +6,7 @@ export default {
<script setup lang="ts">
import "./index.less";
import { ComputedRef, CSSProperties, inject, reactive, Ref } from "vue";
import { ComputedRef, CSSProperties, inject, reactive, Ref, toRefs } from "vue";
import {
computed,
nextTick,
@ -19,7 +19,6 @@ import {
} from "vue";
import {
onClickOutside,
useMouse,
useResizeObserver,
useThrottleFn,
useWindowSize,
@ -30,6 +29,7 @@ import {
ElementScrollRect,
DropdownContext,
} from "./interface";
import TeleportWrapper from "./TeleportWrapper.vue";
export type DropdownTrigger = "click" | "hover" | "focus" | "contextMenu";
@ -47,12 +47,12 @@ export interface LayDropdownProps {
blurToClose?: boolean;
clickOutsideToClose?: boolean;
contentOffset?: number;
//
mouseEnterDelay?: number;
mouseLeaveDelay?: number;
focusDelay?: number;
// ,
alignPoint?: boolean;
popupContainer?: string | HTMLElement | undefined;
popupContainer?: string | undefined;
}
const props = withDefaults(defineProps<LayDropdownProps>(), {
@ -77,18 +77,24 @@ const props = withDefaults(defineProps<LayDropdownProps>(), {
});
const childrenRefs = new Set<Ref<HTMLElement>>();
const dropdownCtx = inject<DropdownContext>(dropdownInjectionKey, undefined);
const dropdownCtx = inject<DropdownContext | undefined>(
dropdownInjectionKey,
undefined
);
const dropdownRef = shallowRef<HTMLElement | undefined>();
const contentRef = shallowRef<HTMLElement | undefined>();
const contentStyle = ref<CSSProperties>({});
const { width: windowWidth, height: windowHeight } = useWindowSize();
const { x: mouseLeft, y: mouseTop } = useMouse();
const mousePosition = reactive({
x: 0,
y: 0,
});
const { x: mouseLeft, y: mouseTop } = toRefs(mousePosition);
const openState = ref(false);
const containerRef = computed(() =>
props.popupContainer
? // @ts-ignore
document.querySelector<HTMLElement>(props.popupContainer) ?? document.body
? document.querySelector<HTMLElement>(props.popupContainer) ?? document.body
: dropdownRef.value
) as ComputedRef<HTMLElement>;
@ -96,7 +102,7 @@ const triggerMethods = computed(() =>
([] as Array<DropdownTrigger>).concat(props.trigger)
);
const emit = defineEmits(["open", "hide"]);
const emit = defineEmits(["show", "hide"]);
let delayTimer = 0;
@ -107,10 +113,10 @@ const cleanDelayTimer = () => {
}
};
const open = (delay?: number): void => {
const show = (delay?: number): void => {
if (props.disabled == false) {
changeVisible(true, delay);
emit("open");
emit("show");
}
};
@ -124,7 +130,7 @@ const toggle = (): void => {
if (openState.value) {
hide();
} else {
open();
show();
}
};
@ -166,19 +172,32 @@ const getElementScrollRect = (element: HTMLElement, containerRect: DOMRect) => {
};
};
const getTriggerRect = () => {
return {
top: mouseTop.value,
bottom: mouseTop.value,
left: mouseLeft.value,
right: mouseLeft.value,
scrollTop: mouseTop.value,
scrollBottom: mouseTop.value,
scrollLeft: mouseLeft.value,
scrollRight: mouseLeft.value,
width: 0,
height: 0,
};
};
const updateContentStyle = () => {
if (!containerRef.value || !dropdownRef.value || !contentRef.value) {
return;
}
const containerRect = containerRef.value.getBoundingClientRect();
const triggerRect = getElementScrollRect(dropdownRef.value, containerRect);
const triggerRect = props.alignPoint
? getTriggerRect()
: getElementScrollRect(dropdownRef.value, containerRect);
const contentRect = getElementScrollRect(contentRef.value, containerRect);
const { style } = getContentStyle(
props.placement,
triggerRect,
contentRect,
props.alignPoint
);
const { style } = getContentStyle(props.placement, triggerRect, contentRect);
if (props.autoFitMinWidth) {
style.minWidth = `${triggerRect.width}px`;
@ -190,10 +209,9 @@ const updateContentStyle = () => {
if (props.autoFitPosition) {
nextTick(() => {
const triggerRect = getElementScrollRect(
dropdownRef.value as HTMLElement,
containerRect
);
const triggerRect = props.alignPoint
? getTriggerRect()
: getElementScrollRect(dropdownRef.value as HTMLElement, containerRect);
const contentRect = getElementScrollRect(
contentRef.value as HTMLElement,
containerRect
@ -217,23 +235,25 @@ const updateContentStyle = () => {
}
};
const updateMousePosition = (e: MouseEvent) => {
if (props.alignPoint) {
const { pageX, pageY } = e;
mousePosition.x = pageX;
mousePosition.y = pageY;
}
};
const getContentStyle = (
placement: DropdownPlacement,
triggerRect: ElementScrollRect,
contentRect: ElementScrollRect,
isAlignPoint?: boolean,
{
customStyle = {},
}: {
customStyle?: CSSProperties;
} = {}
) => {
let { top, left } = getContentOffset(
placement,
triggerRect,
contentRect,
isAlignPoint
);
let { top, left } = getContentOffset(placement, triggerRect, contentRect);
const style = {
top: `${top}px`,
left: `${left}px`,
@ -315,15 +335,8 @@ const getFitPlacement = (
const getContentOffset = (
placement: DropdownPlacement,
triggerRect: ElementScrollRect,
contentRect: ElementScrollRect,
isAlignPoint?: boolean
contentRect: ElementScrollRect
) => {
if (isAlignPoint) {
return {
top: mouseTop.value - triggerRect.top,
left: mouseLeft.value - triggerRect.left,
};
}
switch (placement) {
case "top":
return {
@ -426,11 +439,13 @@ const handleScroll = useThrottleFn(() => {
}
}, 10);
const handleClick = () => {
const handleClick = (e: MouseEvent) => {
e.stopPropagation();
if (props.disabled || (openState.value && !props.clickToClose)) {
return;
}
if (triggerMethods.value.includes("click")) {
updateMousePosition(e);
toggle();
}
};
@ -444,6 +459,7 @@ const handleContextMenuClick = (e: MouseEvent) => {
if (props.alignPoint) {
hide();
}
updateMousePosition(e);
toggle();
}
};
@ -452,7 +468,7 @@ const handleMouseEnter = (e: MouseEvent) => {
if (props.disabled || !triggerMethods.value.includes("hover")) {
return;
}
open(props.mouseEnterDelay);
show(props.mouseEnterDelay);
};
const handleMouseEnterWithContext = (e: MouseEvent) => {
@ -482,7 +498,7 @@ const handleFocusin = () => {
if (props.disabled || !triggerMethods.value.includes("focus")) {
return;
}
open(props.focusDelay);
show(props.focusDelay);
};
const handleFocusout = () => {
@ -504,16 +520,6 @@ const removeChildRef = (ref: any) => {
dropdownCtx?.removeChildRef(ref);
};
provide(
dropdownInjectionKey,
reactive({
onMouseenter: handleMouseEnterWithContext,
onMouseleave: handleMouseLeaveWithContext,
addChildRef,
removeChildRef,
})
);
dropdownCtx?.addChildRef(contentRef);
const { stop: removeContentResizeObserver } = useResizeObserver(
@ -534,20 +540,29 @@ const { stop: removeTriggerResizeObserver } = useResizeObserver(
}
);
onClickOutside(dropdownRef, (e) => {
if (
!props.clickOutsideToClose ||
dropdownRef.value?.contains(e.target as HTMLElement) ||
contentRef.value?.contains(e.target as HTMLElement)
)
return;
for (const item of childrenRefs) {
if (item.value?.contains(e.target as HTMLElement)) {
onClickOutside(
dropdownRef,
(e) => {
if (
!props.clickOutsideToClose ||
!openState.value ||
dropdownRef.value?.contains(e.target as HTMLElement) ||
contentRef.value?.contains(e.target as HTMLElement)
) {
return;
}
for (const item of childrenRefs) {
if (item.value?.contains(e.target as HTMLElement)) {
return;
}
}
hide();
},
{
capture: false,
}
hide();
});
);
let scrollElements: HTMLElement[] | undefined;
onMounted(() => {
@ -579,24 +594,20 @@ watch(
{ immediate: true }
);
const getTriggerRect = (isAlignPoint: boolean) => {
return isAlignPoint
? ({
top: mouseTop.value,
bottom: mouseTop.value,
left: mouseLeft.value,
right: mouseLeft.value,
width: 0,
height: 0,
x: mouseLeft.value,
y: mouseTop.value,
} as DOMRect)
: dropdownRef.value!.getBoundingClientRect();
};
provide(
dropdownInjectionKey,
reactive({
onMouseenter: handleMouseEnterWithContext,
onMouseleave: handleMouseLeaveWithContext,
addChildRef,
removeChildRef,
hide,
})
);
provide("openState", openState);
defineExpose({ open, hide, toggle });
defineExpose({ show, hide, toggle });
</script>
<template>
@ -609,10 +620,10 @@ defineExpose({ open, hide, toggle });
@focusout="handleFocusout()"
:class="{ 'layui-dropdown-up': openState }"
>
<div @click="handleClick()" @contextmenu="handleContextMenuClick">
<div @click="handleClick" @contextmenu="handleContextMenuClick">
<slot></slot>
</div>
<Teleport :to="popupContainer" :disabled="!popupContainer">
<TeleportWrapper :to="popupContainer">
<dl
v-if="openState"
ref="contentRef"
@ -623,6 +634,6 @@ defineExpose({ open, hide, toggle });
>
<slot name="content"></slot>
</dl>
</Teleport>
</TeleportWrapper>
</div>
</template>

View File

@ -31,6 +31,7 @@ export interface DropdownContext {
onMouseleave: Function;
addChildRef: Function;
removeChildRef: Function;
hide: Function;
}
export const dropdownInjectionKey = Symbol("dropdownInjectKey");

View File

@ -6,6 +6,7 @@ export default {
<script setup lang="ts">
import { inject, Ref } from "vue";
import { DropdownContext, dropdownInjectionKey } from "../dropdown/interface";
export interface LayDropdownMenuItemProps {
disabled?: boolean;
@ -16,12 +17,16 @@ const props = withDefaults(defineProps<LayDropdownMenuItemProps>(), {
});
const openState: Ref<boolean> = inject("openState") as Ref<boolean>;
const dropdownCtx = inject<DropdownContext | undefined>(
dropdownInjectionKey,
undefined
);
const handleClick = () => {
if (props.disabled) {
return;
}
openState.value = false;
dropdownCtx?.hide();
};
</script>

View File

@ -71,8 +71,10 @@
border-color: #d2d2d2 !important;
}
.layui-input-clear,
.layui-input-prefix,
.layui-input-suffix {
.layui-input-suffix,
.layui-input-password {
background-color: white;
}
@ -90,8 +92,8 @@
padding: 0 15px;
}
.layui-input-password,
.layui-input-clear {
.layui-input-clear,
.layui-input-password {
flex: none;
display: flex;
align-items: center;
@ -103,6 +105,10 @@
line-height: 1.3;
}
.layui-input input::-ms-reveal {
display: none;
}
.layui-input{
&[size="lg"] {
.set-size(@input-lg);

View File

@ -50,7 +50,7 @@ const menuTheme = computed(() => props.theme);
const openKeys = computed({
get() {
return props.openKeys;
return props.collapse ? [] : props.openKeys;
},
set(val) {
emit("update:openKeys", val);
@ -77,8 +77,7 @@ watch(
} else {
openKeys.value = oldOpenKeys.value;
}
},
{ immediate: true }
}
);
provideLevel(1);

View File

@ -41,29 +41,29 @@ const props = withDefaults(defineProps<LayPageProps>(), {
const { t } = useI18n();
const slots = useSlots();
const maxPage = ref(0);
const limits = ref(props.limits);
const pages = Math.floor(props.pages / 2);
const currentPage: Ref<number> = ref(props.modelValue);
const currentPageShow: Ref<number> = ref(currentPage.value);
const inlimit = ref(props.limit);
const inlimit = computed({
get() {
return props.limit;
},
set(v: number) {
emit("limit", v);
},
});
const maxPage = ref(0);
watch(
() => props.limit,
() => {
inlimit.value = props.limit;
}
);
const totalPage = computed(() => {
maxPage.value = Math.ceil(props.total / props.limit);
let r: number[] = [],
start =
maxPage.value <= props.pages
? 1
: currentPage.value > pages
? currentPage.value - pages
: 1;
maxPage.value = Math.ceil(props.total / inlimit.value);
let r: number[] = [];
let start =
maxPage.value <= props.pages
? 1
: currentPage.value > pages
? currentPage.value - pages
: 1;
for (let i = start; ; i++) {
if (r.length >= props.pages || i > maxPage.value) {
break;
@ -73,51 +73,59 @@ const totalPage = computed(() => {
return r;
});
const currentPage: Ref<number> = ref(props.modelValue);
const currentPageShow: Ref<number> = ref(currentPage.value);
const emit = defineEmits(["update:modelValue", "update:limit", "change"]);
const emit = defineEmits(["jump", "limit", "update:modelValue"]);
const prev = function () {
const prev = () => {
if (currentPage.value === 1) {
return;
}
currentPage.value--;
emit("change", { current: currentPage.value, limit: inlimit.value });
};
const next = function () {
if (currentPage.value === maxPage.value) {
const next = () => {
if (currentPage.value === maxPage.value || maxPage.value === 0) {
return;
}
currentPage.value++;
emit("change", { current: currentPage.value, limit: inlimit.value });
};
const jump = function (page: number) {
const jump = (page: number) => {
currentPage.value = page;
emit("change", { current: currentPage.value, limit: inlimit.value });
};
const jumpPage = function () {
const jumpPage = () => {
currentPage.value = currentPageShow.value;
emit("change", { current: currentPage.value, limit: inlimit.value });
};
watch(inlimit, function () {
const changelimit = () => {
currentPage.value = 1;
emit("change", { current: currentPage.value, limit: inlimit.value });
};
const refresh = () => {
emit("change", { current: currentPage.value, limit: inlimit.value });
};
watch(inlimit, () => {
emit("update:limit", inlimit.value);
});
watch(currentPage, function () {
if (currentPage.value > totalPage.value[totalPage.value.length - 1]) {
currentPage.value = totalPage.value[totalPage.value.length - 1];
}
if (currentPage.value < totalPage.value[0]) {
currentPage.value = totalPage.value[0];
}
watch(currentPage, () => {
const min = totalPage.value[0];
const max = totalPage.value[totalPage.value.length - 1];
if (currentPage.value > max) currentPage.value = max;
if (currentPage.value < min) currentPage.value = min;
currentPageShow.value = currentPage.value;
emit("jump", { current: currentPage.value });
emit("update:modelValue", currentPage.value);
});
watch(
() => props.modelValue,
function () {
() => {
currentPage.value = props.modelValue;
currentPageShow.value = currentPage.value;
}
@ -159,13 +167,14 @@ watch(
>
</template>
</template>
<a
href="javascript:;"
class="layui-laypage-next"
:class="[
currentPage === maxPage ? 'layui-disabled' : '',
theme && currentPage !== maxPage ? 'layui-laypage-a-' + theme : '',
currentPage === maxPage || maxPage === 0 ? 'layui-disabled' : '',
theme && currentPage !== maxPage && maxPage !== 0
? 'layui-laypage-a-' + theme
: '',
]"
@click="next()"
>
@ -173,13 +182,18 @@ watch(
<template v-else>{{ t("page.next") }}</template>
</a>
<span v-if="showLimit" class="layui-laypage-limits">
<select v-model="inlimit">
<select v-model="inlimit" @change="changelimit">
<option v-for="val of limits" :key="val" :value="val">
{{ val }} /
</option>
</select>
</span>
<a v-if="showRefresh" href="javascript:;" class="layui-laypage-refresh">
<a
v-if="showRefresh"
href="javascript:;"
@click="refresh"
class="layui-laypage-refresh"
>
<i class="layui-icon layui-icon-refresh"></i>
</a>
<span v-if="props.showSkip" class="layui-laypage-skip">
@ -194,7 +208,7 @@ watch(
type="button"
class="layui-laypage-btn"
@click="jumpPage()"
:disabled="currentPageShow > maxPage"
:disabled="currentPageShow > maxPage || currentPageShow == currentPage"
>
确定
</button>

View File

@ -15,10 +15,7 @@ const props = defineProps<LayQuoteProps>();
</script>
<template>
<blockquote
class="layui-quote"
:class="[type ? `layui-quote-${props.type}` : '']"
>
<div class="layui-quote" :class="[type ? `layui-quote-${props.type}` : '']">
<slot></slot>
</blockquote>
</div>
</template>

View File

@ -56,8 +56,8 @@ const props = withDefaults(defineProps<LayScrollProps>(), {
const emit = defineEmits(["arrive"]);
const scrollRef = ref<HTMLElement | null>(null);
const barRef = ref<HTMLElement | null>(null);
const scrollRef = ref<HTMLElement | null | undefined>();
const barRef = ref<HTMLElement | null | undefined>();
const data = reactive({
translateY: 0, //
@ -66,38 +66,33 @@ const data = reactive({
winWidth: document.body.clientWidth, //
});
let time = null; //
let time: NodeJS.Timeout; //
let isMove = false; //
let moveClientY = 0; //
let trackHeight = 0; //
let wrapHeight = 0; //
let wrapContentHeight = 0; //
//
onMounted(() => {
monitorWindow(); //
monitorScrollBar(); //
monitorWindow();
monitorScrollBar();
nextTick(() => {
//dom
calculationLength(); //
calculationLength();
});
});
//
onUnmounted(() => {
window.clearInterval(time);
time = null;
});
//
const monitorWindow = function () {
let time; //
let time: NodeJS.Timeout;
window.addEventListener("resize", () => {
data.winWidth = document.body.clientWidth; //
data.winWidth = document.body.clientWidth;
clearTimeout(time);
time = setTimeout(() => {
//500
// console.log("");
initScrollListner();
}, 500);
});
@ -105,24 +100,23 @@ const monitorWindow = function () {
//
const monitorScrollBar = function () {
var monitorUl = scrollRef.value.children[0];
// var monitorDiv= document; // document
// @ts-ignore
var monitorUl = scrollRef.value;
let MutationObserver =
window.MutationObserver ||
// @ts-ignore
window.WebKitMutationObserver ||
// @ts-ignore
window.MozMutationObserver;
let observer = new MutationObserver(function (mutations) {
// console.log("");
let observer = new MutationObserver((mutations) => {
initScrollListner();
});
//
observer.observe(monitorUl, {
attributes: true,
childList: true,
});
if (monitorUl) {
observer.observe(monitorUl, {
attributes: true,
childList: true,
});
}
};
//
@ -137,7 +131,6 @@ const calculationLength = function () {
// 500
setTimeout(() => {
window.clearInterval(time);
time = null;
}, 2000);
};
@ -146,11 +139,10 @@ const initScrollListner = function () {
let scroll = scrollRef.value;
let bar = barRef.value;
// scroll
if (scroll) {
if (scroll && bar) {
wrapContentHeight = scroll.scrollHeight;
wrapHeight = scroll.clientHeight;
trackHeight = bar.clientHeight;
// console.log(wrapContentHeight ,wrapHeight);
// / 100 150
data.heightPre = wrapHeight / wrapContentHeight;
//
@ -159,11 +151,7 @@ const initScrollListner = function () {
};
//
const onMosewheel = function (e) {
// scrollTop
// offsetHeight
// scrollHeight
// data.translateY
const onMosewheel = (e: any) => {
data.translateY = e.target.scrollTop * data.heightPre;
if (data.translateY == 0) {
//
@ -178,12 +166,12 @@ const onMosewheel = function (e) {
};
//
const arrive = function name(tb) {
const arrive = (tb: string) => {
emit("arrive", tb);
};
//
const moveStart = function (e) {
const moveStart = (e: any) => {
isMove = true;
// clientYy
// data.translateY
@ -194,7 +182,7 @@ const moveStart = function (e) {
};
// thumbscrollTop
const moveTo = function () {
const moveTo = () => {
document.onmousemove = (e) => {
//
if (isMove) {
@ -203,14 +191,16 @@ const moveTo = function () {
// translateY
data.translateY = trackHeight - data.barHeight;
} else if (e.clientY - moveClientY < 0) {
// translateY
// translateY
data.translateY = 0;
} else {
//
data.translateY = e.clientY - moveClientY;
}
//
scrollRef.value.scrollTop = data.translateY / data.heightPre;
if (scrollRef.value) {
scrollRef.value.scrollTop = data.translateY / data.heightPre;
}
}
};
};

View File

@ -0,0 +1,26 @@
import { defineComponent } from "vue";
import type { PropType } from "vue";
export default defineComponent({
name: "Renderer",
props: {
renderFn: {
type: Function,
default: null,
},
data: {
type: Object as PropType<Record<string, any>>,
default: undefined,
},
},
setup(props) {
return () => {
if (typeof props.renderFn !== "function") {
return null;
}
return props.renderFn(props.data);
};
},
});

View File

@ -0,0 +1,42 @@
.layui-space {
display: inline-flex;
&-horizontal {
.layui-space-item {
display: flex;
align-items: center;
}
}
&-vertical {
flex-direction: column;
}
&-wrap {
flex-wrap: wrap;
}
&-fill {
display: flex;
}
&-align-start {
align-items: flex-start;
}
&-align-center {
align-items: center;
}
&-align-end {
align-items: flex-end;
}
&-align-baseline {
align-items: baseline;
}
&-item {
width: inherit;
}
}

View File

@ -0,0 +1,5 @@
import { withInstall, WithInstallType } from "../../utils";
import Component from "./index.vue";
const component: WithInstallType<typeof Component> = withInstall(Component);
export default component;

View File

@ -0,0 +1,132 @@
<script lang="ts">
export default {
name: "LaySpace",
};
</script>
<script lang="ts" setup>
import "./index.less";
import {
computed,
h,
useSlots,
Comment,
VNode,
Fragment,
isVNode,
createTextVNode,
VNodeArrayChildren,
StyleValue,
} from "vue";
import Renderer from "./Renderer";
export type SpaceSize = "lg" | "md" | "sm" | "xs" | number | string;
export interface LaySpaceProps {
/* 对齐方式 */
align?: "start" | "end" | "center" | "baseline";
/* 间距方向 */
direction?: "horizontal" | "vertical";
/* 子元素是否填充父容器 */
fill?: boolean;
/* 间距大小 */
size?: SpaceSize | [SpaceSize, SpaceSize];
/* 是否自动折行 */
wrap?: boolean;
}
const props = withDefaults(defineProps<LaySpaceProps>(), {
align: "center",
direction: "horizontal",
size: "sm",
});
const slots = useSlots();
const extractChildren = () => {
const result: VNode[] = [];
const children = slots.default && (slots?.default() as VNodeArrayChildren);
const elementData = Array.isArray(children) ? [...children] : [];
while (elementData.length) {
const vnode = elementData.shift();
if (vnode === null) continue;
if (Array.isArray(vnode)) {
elementData.unshift(...vnode);
}
if (!isVNode(vnode) || vnode.type === Comment) continue;
if (vnode.type === Fragment && Array.isArray(vnode.children)) {
elementData.unshift(vnode.children);
} else if (typeof vnode === "string" || typeof vnode === "number") {
result.push(createTextVNode(vnode));
} else {
result.push(vnode);
}
}
return result;
};
const spaceClass = computed(() => [
"layui-space",
{
[`layui-space-align-${props.align}`]: props.align,
[`layui-space-${props.direction}`]: props.direction,
[`layui-space-wrap`]: props.wrap,
[`layui-space-fill`]: props.fill,
},
]);
const spaceStyle = computed(() => {
const sizeMap = { xs: "4px", sm: "8px", md: "16px", lg: "24px" };
let gap = "";
if (Array.isArray(props.size)) {
gap = props.size
.map((size) => {
if (typeof size === "number") {
return `${size}px`;
}
if (typeof size === "string") {
return sizeMap[props.size as keyof Omit<SpaceSize, number>] || size;
}
return size;
})
.join(" ");
} else if (typeof props.size === "string") {
gap = sizeMap[props.size as keyof Omit<SpaceSize, string>] || props.size;
} else if (typeof props.size === "number") {
gap = `${props.size}px`;
}
return {
gap,
};
});
const itemStyle = computed<StyleValue>(() => [
props.fill ? { flexGrow: 1, minWidth: "100%" } : {},
]);
const children = extractChildren();
const renderSpaceItems = () =>
children.map((child, index) => {
return h(
"div",
{
class: "layui-space-item",
style: itemStyle.value,
},
h(child)
);
});
</script>
<template>
<div :class="spaceClass" :style="spaceStyle">
<Renderer :renderFn="renderSpaceItems"></Renderer>
</div>
</template>

View File

@ -1,4 +1,5 @@
.layui-tab {
display: flex;
margin: 10px 0;
text-align: left !important;
}
@ -7,14 +8,20 @@
overflow: hidden;
}
.layui-tab.is-left {
flex-direction: row;
}
.layui-tab.is-right {
display: flex;
flex-direction: row-reverse;
justify-content: space-between
}
.layui-tab.is-top {
flex-direction: column;
}
.layui-tab.is-bottom {
display: flex;
flex-direction: column-reverse
}
@ -316,6 +323,7 @@
.layui-tab-content {
padding: 15px 0;
flex: 1;
}
.layui-tab.is-right>.layui-tab-content,

View File

@ -0,0 +1,62 @@
<script lang="ts">
export default {
name: "TablePage",
};
</script>
<script lang="ts" setup>
import { LayIcon } from "@layui/icons-vue";
import LayPage from "../page/index.vue";
export interface LayTablePageProps {
showPage?: boolean;
showSkip?: boolean;
showLimit?: boolean;
showCount?: boolean;
showRefresh?: boolean;
modelValue: number;
limits?: number[];
pages?: number;
total: number;
limit?: number;
theme?: string;
}
const props = withDefaults(defineProps<LayTablePageProps>(), {
showPage: true,
showLimit: true,
showSkip: true,
});
const emit = defineEmits(["update:modelValue", "update:limit", "change"]);
const change = (pageData: any) => {
emit("change", pageData);
emit("update:modelValue", pageData.current);
emit("update:limit", pageData.limit);
};
</script>
<template>
<lay-page
:total="total"
:show-page="showPage"
:show-skip="showSkip"
:show-limit="showLimit"
:show-count="showCount"
:show-refresh="showRefresh"
:limits="limits"
:theme="theme"
:pages="pages"
v-model:modelValue="modelValue"
v-model:limit="limit"
@change="change"
>
<template #prev>
<lay-icon type="layui-icon-left" />
</template>
<template #next>
<lay-icon type="layui-icon-right" />
</template>
</lay-page>
</template>

View File

@ -39,6 +39,7 @@ export interface LayTableRowProps {
}
const slot = useSlots();
const emit = defineEmits([
"row",
"row-double",
@ -183,7 +184,7 @@ const renderFixedStyle = (column: any, columnIndex: number) => {
props.columns[i].fixed == "left" &&
props.tableColumnKeys.includes(props.columns[i].key)
) {
left = left + props.columns[i]?.width.replace("px", "");
left = left + props.columns[i]?.width?.replace("px", "");
}
}
return { left: `${left}px` } as StyleValue;
@ -195,7 +196,7 @@ const renderFixedStyle = (column: any, columnIndex: number) => {
props.columns[i].fixed == "right" &&
props.tableColumnKeys.includes(props.columns[i].key)
) {
right = right + props.columns[i]?.width.replace("px", "");
right = right + props.columns[i]?.width?.replace("px", "");
}
}
return { right: `${right}px` } as StyleValue;

View File

@ -1,6 +1,8 @@
@import "../checkbox/index.less";
@import "../radio/index.less";
@import "../dropdown/index.less";
@import "../page/index.less";
.layui-table-col-special {
width: 34px;
}
@ -17,6 +19,13 @@
font-weight: 400;
}
.layui-table-footer {
min-height: 50px;
line-height: 30px;
background-color: #FAFAFA;
padding: 10px 15px;
}
.layui-table-mend,
.layui-table-tool,
.layui-table-total,

View File

@ -15,15 +15,16 @@ import {
StyleValue,
WritableComputedRef,
computed,
onBeforeUnmount,
} from "vue";
import { v4 as uuidv4 } from "../../utils/guidUtil";
import { Recordable } from "../../types";
import { LayIcon } from "@layui/icons-vue";
import LayCheckbox from "../checkbox/index.vue";
import LayDropdown from "../dropdown/index.vue";
import LayPage from "../page/index.vue";
import LayEmpty from "../empty/index.vue";
import TableRow from "./TableRow.vue";
import TablePage from "./TablePage.vue";
import { nextTick } from "vue";
export interface LayTableProps {
@ -92,6 +93,7 @@ const emit = defineEmits([
const slot = useSlots();
const slots = slot.default && slot.default();
const s = "";
const allChecked = ref(false);
const hasChecked = ref(false);
const tableDataSource = ref<any[]>([...props.dataSource]);
@ -209,6 +211,10 @@ const findFinalNode = (level: number, columns: any[]) => {
if (!tableHeadColumns.value[level]) {
tableHeadColumns.value[level] = [];
}
// width ,
if (column.fixed && !column.width) {
column.type ? (column.width = "50px") : (column.width = "100px");
}
tableHeadColumns.value[level].push(column);
findFinalNode(level + 1, column.children);
} else {
@ -217,6 +223,10 @@ const findFinalNode = (level: number, columns: any[]) => {
if (!tableHeadColumns.value[level]) {
tableHeadColumns.value[level] = [];
}
// width ,
if (column.fixed && !column.width) {
column.type ? (column.width = "50px") : (column.width = "100px");
}
tableHeadColumns.value[level].push(column);
}
});
@ -256,6 +266,8 @@ watch(
() => props.dataSource,
() => {
tableDataSource.value = [...props.dataSource];
tableSelectedKeys.value = [];
tableSelectedKey.value = s;
nextTick(() => {
getScrollWidth();
});
@ -264,17 +276,15 @@ watch(
);
const changeAll = (isChecked: boolean) => {
// Selected
if (isChecked) {
const datasources = props.dataSource.filter((item: any, index: number) => {
return !props.getCheckboxProps(item, index).disabled;
return !props.getCheckboxProps(item, index)?.disabled;
});
const ids = datasources.map((item) => {
return item[props.id];
});
tableSelectedKeys.value = [...ids];
} else {
// unSelected
tableSelectedKeys.value = [];
}
};
@ -317,11 +327,12 @@ const rowDoubleClick = function (data: any, evt: MouseEvent) {
emit("row-double", data, evt);
};
const rowContextmenu = function (data: any, evt: MouseEvent) {
const rowContextmenu = (data: any, evt: MouseEvent) => {
emit("row-contextmenu", data, evt);
};
const print = function () {
//
const print = () => {
let subOutputRankPrint = document.getElementById(tableId) as HTMLElement;
let newContent = subOutputRankPrint.innerHTML;
let oldContent = document.body.innerHTML;
@ -331,9 +342,7 @@ const print = function () {
document.body.innerHTML = oldContent;
};
/**
* excel 导出
*/
//
const exportData = () => {
var tableStr = ``;
for (let tableHeadColumn of tableHeadColumns.value) {
@ -381,11 +390,12 @@ const exportData = () => {
return;
};
//base64
// BASE64
function base64(s: string) {
return window.btoa(unescape(encodeURIComponent(s)));
}
//
const sortTable = (e: any, key: string, sort: string) => {
let currentSort = e.target.parentNode.getAttribute("lay-sort");
if (sort === "desc") {
@ -437,6 +447,15 @@ const classes = computed(() => {
];
});
watch(
() => [props.height, props.maxHeight, props.dataSource],
() => {
nextTick(() => {
getScrollWidth();
});
}
);
onMounted(() => {
getScrollWidth();
getFixedColumn();
@ -504,7 +523,7 @@ const renderFixedStyle = (column: any, columnIndex: number) => {
props.columns[i].fixed == "left" &&
tableColumnKeys.value.includes(props.columns[i].key)
) {
left = left + props.columns[i]?.width.replace("px", "");
left = left + props.columns[i]?.width?.replace("px", "");
}
}
return { left: `${left}px` } as StyleValue;
@ -516,7 +535,7 @@ const renderFixedStyle = (column: any, columnIndex: number) => {
props.columns[i].fixed == "right" &&
tableColumnKeys.value.includes(props.columns[i].key)
) {
right = right + props.columns[i]?.width.replace("px", "");
right = right + props.columns[i]?.width?.replace("px", "");
}
}
return { right: `${right}px` } as StyleValue;
@ -592,6 +611,10 @@ const renderTotalRowCell = (column: any) => {
}
}
};
onBeforeUnmount(() => {
window.onresize = null;
});
</script>
<template>
@ -833,24 +856,26 @@ const renderTotalRowCell = (column: any) => {
</div>
</template>
</div>
<div class="layui-table-footer" v-if="slot.footer">
<slot name="footer"></slot>
</div>
</div>
<div v-if="page" class="layui-table-page">
<lay-page
show-page
show-skip
show-limit
<table-page
:show-page="page.showPage"
:showSkip="page.showSkip"
:showLimit="page.showLimit"
:showCount="page.showCount"
:limits="page.limits"
:showRefresh="page.showRefresh"
:total="page.total"
:limit="page.limit"
:pages="page.pages"
:theme="page.theme"
v-model="page.current"
@jump="change"
v-model:limit="page.limit"
@change="change"
>
<template #prev>
<lay-icon type="layui-icon-left" />
</template>
<template #next>
<lay-icon type="layui-icon-right" />
</template>
</lay-page>
</table-page>
</div>
</div>
</div>

View File

@ -5,6 +5,7 @@ export default {
</script>
<script setup lang="ts">
import { LayIcon } from "@layui/icons-vue";
import { computed } from "vue";
import "./index.less";

View File

@ -21,12 +21,13 @@ import { templateRef } from "@vueuse/core";
import { LayLayer } from "@layui/layer-vue";
import LayButton from "../button/index.vue";
import Cropper from "cropperjs";
//
//https://www.layuiweb.com/doc/modules/upload.html#options
import { arrayExpression } from "@babel/types";
export interface LayerButton {
text: string;
callback: Function;
}
export interface LayerModal {
title?: string;
resize?: boolean;
@ -46,6 +47,7 @@ export interface LayerModal {
anim?: boolean;
isOutAnim?: boolean;
}
export interface cutOptions {
layerOption: LayerModal;
copperOption?: typeof Cropper;
@ -55,7 +57,7 @@ export interface LayUploadProps {
url?: string;
data?: any;
headers?: Recordable;
acceptMime?: "images" | "file" | "video" | "audio";
acceptMime?: string;
field?: string;
size?: number;
multiple?: boolean;
@ -73,7 +75,6 @@ const getCutDownResult = () => {
let imgData = canvas.toDataURL('"image/png"');
let currentTimeStamp = new Date().valueOf();
let orgInfo = activeUploadFiles.value[0];
console.log(orgInfo);
emit(
"cutdone",
Object.assign({ currentTimeStamp, cutResult: imgData, orginal: orgInfo })
@ -111,9 +112,10 @@ let defaultCutLayerOption: LayerModal = {
shadeClose: true,
type: "component",
};
const props = withDefaults(defineProps<LayUploadProps>(), {
acceptMime: "images",
field: "file",
acceptMime: "MIME_type",
size: 0,
multiple: false,
number: 0,
@ -136,11 +138,8 @@ const emit = defineEmits([
"cutcancel",
]);
//
const isDragEnter = ref(false);
//
const activeUploadFiles = ref<any[]>([]);
//
const activeUploadFilesImgs = ref<any[]>([]);
const orgFileInput = templateRef<HTMLElement>("orgFileInput");
let _cropper: any = null;
@ -153,25 +152,27 @@ if (props.cutOptions && props.cutOptions.layerOption) {
computedCutLayerOption = computed(() => defaultCutLayerOption);
}
//
const defaultErrorMsg = "上传失败";
const urlErrorMsg = "上传地址格式不合法";
const numberErrorMsg = "文件上传超过规定的个数";
const sizeErrorMsg = "文件大小超过限制";
const uploadRemoteErrorMsg = "请求上传接口出现异常";
const cutInitErrorMsg = "剪裁插件初始化失败";
//
const uploadSuccess = "上传成功";
// -> start
//
interface localUploadTransaction {
url: string;
files: File[] | Blob[];
[propMame: string]: any;
}
interface localUploadOption {
url: string;
[propMame: string]: any;
}
const innerCutVisible = ref<boolean>(false);
const localUploadTransaction = (option: localUploadTransaction) => {
const { url, files } = option;
let formData = new FormData();
@ -182,10 +183,9 @@ const localUploadTransaction = (option: localUploadTransaction) => {
if (Array.isArray(files) && files.length > 0) {
for (let i = 0; i < files.length; i++) {
let _file = files[i];
formData.append("file[" + i + "]", _file);
formData.append(props.field + "[" + i + "]", _file);
}
}
// Uploaddata,
if (props.data && props.data instanceof Object) {
let _requestDate = props.data;
for (const key in _requestDate) {
@ -199,11 +199,6 @@ const localUploadTransaction = (option: localUploadTransaction) => {
}, 200);
};
//
interface localUploadOption {
url: string;
[propMame: string]: any;
}
const dataURLtoFile = (dataurl: string) => {
let arr: any[] = dataurl.split(",");
let mime: string = "";
@ -234,10 +229,7 @@ const localUpload = (option: localUploadOption, callback: Function) => {
url = option.url;
let formData = option.formData;
const cb = callback;
//
// event callbacks
xhr.onreadystatechange = function () {
//
let currentTimeStamp = new Date().valueOf();
if (xhr.readyState === 1) {
if (
@ -252,9 +244,7 @@ const localUpload = (option: localUploadOption, callback: Function) => {
);
}
} else if (xhr.readyState === 4) {
//
let successText = xhr.responseText ? xhr.responseText : uploadSuccess;
console.log(xhr);
if (
(xhr.status >= 200 && xhr.status <= 300) ||
xhr.status === 304 ||
@ -265,8 +255,7 @@ const localUpload = (option: localUploadOption, callback: Function) => {
}
}
};
xhr.open("post", url, true); //GET, getURL
// Uploadheaders,
xhr.open("post", url, true);
if (props.headers) {
for (let key in props.headers) {
xhr.setRequestHeader(key, props.headers[key]);
@ -274,7 +263,6 @@ const localUpload = (option: localUploadOption, callback: Function) => {
} else {
xhr.setRequestHeader("Accept", "application/json, text/javascript");
}
//
let currentTimeStamp = new Date().valueOf();
emit("before", Object.assign(option, currentTimeStamp));
xhr.send(formData);
@ -282,6 +270,7 @@ const localUpload = (option: localUploadOption, callback: Function) => {
cb();
}
};
const filetoDataURL = (file: File, fn: Function) => {
const reader = new FileReader();
reader.onloadend = function (e: any) {
@ -289,15 +278,14 @@ const filetoDataURL = (file: File, fn: Function) => {
};
reader.readAsDataURL(file);
};
const getUploadChange = (e: any) => {
const files = e.target.files;
const _files = [...files];
// Uploadnumber,
if (props.multiple && props.number != 0 && props.number < _files.length) {
errorF(numberErrorMsg);
return;
}
// Uploadsize,
if (props.size && props.size != 0) {
let _cache = [];
for (let i = 0; i < _files.length; i++) {
@ -324,8 +312,14 @@ const getUploadChange = (e: any) => {
activeUploadFilesImgs.value.push(res);
});
}
let arm1 = props.cut && props.acceptMime == "images" && !props.multiple;
let arm2 = props.cut && props.acceptMime == "images" && props.multiple;
let arm1 =
props.cut &&
props.acceptMime.indexOf("image") != -1 &&
props.multiple == false;
let arm2 =
props.cut &&
props.acceptMime.indexOf("image") != -1 &&
props.multiple == true;
if (arm1) {
innerCutVisible.value = true;
setTimeout(() => {
@ -339,12 +333,13 @@ const getUploadChange = (e: any) => {
} else {
if (arm2) {
console.warn(
"layui-vue:当前版本暂不支持单次多文件剪裁,尝试设置 multiple 为false,通过@done获取返回文件对象"
"当前版本暂不支持单次多文件剪裁,尝试设置 multiple 为 false, 通过 @done 获取返回文件对象"
);
}
commonUploadTransaction(_files);
}
};
const commonUploadTransaction = (_files: any[]) => {
let currentTimeStamp = new Date().valueOf();
let successText = uploadSuccess;
@ -357,16 +352,19 @@ const commonUploadTransaction = (_files: any[]) => {
emit("done", { currentTimeStamp, msg: successText, data: _files });
}
};
const chooseFile = () => {
let _target = orgFileInput.value;
if (_target) {
_target.click();
}
};
const clickOrgInput = () => {
let currentTimeStamp = new Date().valueOf();
emit("choose", currentTimeStamp);
};
const cutTransaction = () => {};
</script>

View File

@ -61,7 +61,6 @@ import LayDropdownSubMenu from "./component/dropdownSubMenu/index";
import LayTab from "./component/tab/index";
import LayTabItem from "./component/tabItem/index";
import LayTree from "./component/tree/index";
import LayTreeSelect from "./component/treeSelect/index";
import LayTable from "./component/table/index";
import LayPage from "./component/page/index";
import LayTransfer from "./component/transfer/index";
@ -89,8 +88,7 @@ import LayNoticeBar from "./component/noticeBar/index";
import LayPageHeader from "./component/pageHeader/index";
import LayCascader from "./component/cascader/index";
import LayAffix from "./component/affix/index";
import LayTag from "./component/tag/index";
import LayTagInput from "./component/tagInput/index";
import LaySpace from "./component/space/index";
import LayConfigProvider from "./provider";
import { InstallOptions } from "./types";
@ -149,7 +147,6 @@ const components: Record<string, Plugin> = {
LayTabItem,
LayIconPicker,
LayTree,
LayTreeSelect,
LayTable,
LayPage,
LayTransfer,
@ -178,8 +175,7 @@ const components: Record<string, Plugin> = {
LayPageHeader,
LayCascader,
LayAffix,
LayTag,
LayTagInput,
LaySpace,
};
const install = (app: App, options?: InstallOptions): void => {
@ -245,7 +241,6 @@ export {
LayTabItem,
LayIconPicker,
LayTree,
LayTreeSelect,
LayTable,
LayPage,
LayTransfer,
@ -274,8 +269,7 @@ export {
LayPageHeader,
LayCascader,
LayAffix,
LayTag,
LayTagInput,
LaySpace,
install,
};

View File

@ -55,6 +55,9 @@ export default {
<template v-slot:body>
内容
</template>
<template v-slot:footer>
底部
</template>
</lay-card>
</div>
</template>
@ -213,12 +216,13 @@ export default {
::: table
| 插槽 | 描述 | 可选值 |
| ------- | -------- | ------ |
| default | 默认插槽 | -- |
| header | 头部插槽 | -- |
| body | 内容插槽 | -- |
| extra | 扩展插槽 | -- |
| 插槽 | 描述 | 可选值 | 版本 |
| ------- | -------- | ------ |------ |
| default | 默认插槽 | -- |-- |
| header | 头部插槽 | -- |-- |
| body | 内容插槽 | -- |-- |
| extra | 扩展插槽 | -- |-- |
| footer | 扩展插槽 | -- |`1.4.3`|
:::
::: contributor card

View File

@ -49,7 +49,7 @@ import { ref } from 'vue'
export default {
setup() {
const endTime2 = ref("2022-06-04 17:35:00");
const endTime2 = ref("");
return {
endTime2
@ -254,15 +254,17 @@ const rangeTime3 = ref(['2022-01-01','2023-02-1']);
::: table
| 属性 | 描述 | 类型 | 默认值 | 可选值 |
| ------------- | ------------------------------------------------------------ | -------------- | ------ | -------------- |
| v-model | 当前时间 | `string` | -- | — |
| type | 选择类型 | `string` | `date` | `date` `datetime` `year` `month` `time` `yearmonth` |
| disabled | 是否禁止修改 | `boolean` | false | — |
| simple | 一次性选择,无需点击确认按钮 | `boolean` | false | — |
| readonly | 只读 | `boolean` | false | — |
| allowClear | 允许清空 | `boolean` | true | — |
| size | 尺寸 | `string` | `lg` `md` `sm` `xs` | `md` |
| 属性 | 描述 | 类型 | 默认值 | 可选值 | 版本 |
| ------------- | ------------------------------------------------------------ | -------------- | ------ | -------------- |-------------- |
| v-model | 当前时间 | `string` | -- | -- | -- |
| type | 选择类型 | `string` | `date` | `date` `datetime` `year` `month` `time` `yearmonth` | -- |
| disabled | 是否禁止修改 | `boolean` | false | — | — |
| simple | 一次性选择,无需点击确认按钮 | `boolean` | false | -- | -- |
| readonly | 只读 | `boolean` | false | -- | -- |
| allowClear | 允许清空 | `boolean` | true | -- | -- |
| size | 尺寸 | `string` | `lg` `md` `sm` `xs` | `md` | -- |
| prefix-icon | 前置图标 | `string` | `layui-icon-date` | 内置图标集 | `1.4.0` |
| suffix-icon | 后置图标 | `string` | -- | 内置图标集 | `1.4.0` |
:::

View File

@ -120,7 +120,7 @@ export default {
::: demo
<template>
<lay-button @click="manualRef.open()">打开</lay-button>
<lay-button @click="manualRef.show()">打开</lay-button>
<lay-button @click="manualRef.hide()">关闭</lay-button>
<br><br>
<lay-dropdown ref="manualRef" :clickOutsideToClose="false" :clickToClose="false" updateAtScroll>
@ -222,7 +222,7 @@ export default {
::: demo
<template>
<lay-dropdown @open="stat='开启'" @hide="stat='关闭'" updateAtScroll>
<lay-dropdown @show="stat='开启'" @hide="stat='关闭'" updateAtScroll>
<lay-button type="primary" >当前状态:{{stat}}</lay-button>
<template #content>
<lay-dropdown-menu>
@ -516,42 +516,11 @@ export default {
<lay-dropdown-menu-item>选项三</lay-dropdown-menu-item>
</lay-dropdown-menu>
</template>
</lay-dropdown>
&nbsp;&nbsp;
<br><br>
<lay-button @click="triggerHeight += 100">改变触发器尺寸</lay-button>
<lay-button @click="contentHeight += 100">改变面板尺寸</lay-button>
<br><br>
<lay-dropdown placement="bottom-left" trigger="focus" :autoFitPosition="true" :autoFixPosition="true" :blurToClose="false" :clickOutsideToClose="false" updateAtScroll>
<lay-input placeholder="autoFixPosition" :style="{height: triggerHeight + 'px'}"></lay-input>
<template #content>
<div :style="{width:'350px', height: contentHeight + 'px'}"></div>
</template>
</lay-dropdown>
</template>
<script>
<script setup>
import { ref, computed } from 'vue'
export default {
setup() {
const btnSize = ref('')
const toogleSize = () => {
btnSize.value = btnSize.value ? '' : 'lg'
}
const triggerHeight = ref(100)
const contentHeight = ref(200)
return {
btnSize,
toogleSize,
triggerWidth,
triggerStyle,
}
}
}
</script>
:::
@ -576,6 +545,9 @@ export default {
| blurToClose | 是否在触发器失去焦点时关闭面板,默认 `true` |`true` `false`|
| clickOutsideToClose| 是否点击外部关闭下拉面板,默认 `true`|`true` `false`|
| contentOffset | 下拉面板距离触发器的偏移距离,默认 2| -|
| mouseEnterDelay | mouseEnter 事件延迟触发的时间, trigger hover 有效 | - |
| mouseLeaveDelay | mouseLeave 事件延迟触发的时间, trigger hover 有效| - |
| focusDelay| focus 事件延迟触发的时间, trigger focus 有效 | - |
:::
@ -599,8 +571,8 @@ export default {
| 插槽 | 描述 | 参数 |
| ------- | -------- | ------ |
| hide | 隐藏下拉内容后触发 | -- |
| open | 显示下拉内容后触发 | -- |
| hide | 隐藏下拉面板后触发 | -- |
| show | 显示下拉面板后触发 | -- |
:::

View File

@ -142,7 +142,7 @@ export default {
:::
::: title 表单基本校验功能
::: title 表单验证
:::
::: demo
@ -231,7 +231,7 @@ export default {
:::
::: title 校验规则 - 通过表单配置
::: title 校验规则
:::
::: demo

View File

@ -735,6 +735,18 @@ export default {
:::
::: title Menu 事件
:::
::: table
| 属性 | 描述 | 参数 |
| ------------------- | ------------------- | -------------- |
| changeSelectedKey | 选中菜单回调 | value: string |
| changeOpenKeys | 展开目录回调 | value: string[] |
:::
::: title Menu Item 属性
:::

View File

@ -13,7 +13,7 @@
::: demo 使用 `lay-page` 标签, 创建分页
<template>
<lay-page v-model="currentPage" :limit="limit" @limit="limit = $event" :total="total" :show-page="showPage"></lay-page>
<lay-page v-model="currentPage" :limit="limit" :total="total" :show-page="true"></lay-page>
</template>
<script>
@ -24,13 +24,11 @@ export default {
const limit = ref(20)
const total = ref(100)
const showPage = ref(true)
const currentPage = ref(2);
return {
limit,
total,
showPage,
currentPage
}
}
@ -39,13 +37,13 @@ export default {
:::
::: title 简模式
::: title 简模式
:::
::: demo
<template>
<lay-page :limit="limit" @limit="limit = $event" :total="total"></lay-page>
<lay-page :limit="limit1" v-model="current1" :total="total1"></lay-page>
</template>
<script>
@ -54,12 +52,14 @@ import { ref } from 'vue'
export default {
setup() {
const limit = ref(20)
const total = ref(100)
const limit1 = ref(10);
const total1 = ref(100);
const current1 = ref(1);
return {
limit,
total
limit1,
total1,
current1
}
}
}
@ -67,16 +67,13 @@ export default {
:::
::: title 插槽使用
::: title 设置主题
:::
::: demo
<template>
<lay-page :limit="limit" @limit="limit = $event" :total="total">
<template v-slot:prev></template>
<template v-slot:next></template>
</lay-page>
<lay-page :limit="limit2" :total="total2" :show-page="true" theme="blue"></lay-page>
</template>
<script>
@ -85,12 +82,12 @@ import { ref } from 'vue'
export default {
setup() {
const limit = ref(20)
const total = ref(100)
const limit2 = ref(20)
const total2 = ref(100)
return {
limit,
total
limit2,
total2,
}
}
}
@ -98,17 +95,13 @@ export default {
:::
::: title 不同主题
::: title 分页容量
:::
::: demo
<template>
<lay-page :limit="limit" @limit="limit = $event" :total="total" :show-page="showPage" theme="red"></lay-page>
<br>
<lay-page :limit="limit" @limit="limit = $event" :total="total" :show-page="showPage" theme="blue"></lay-page>
<br>
<lay-page :limit="limit" @limit="limit = $event" :total="total" :show-page="showPage" theme="orange"></lay-page>
<lay-page :limit="limit3" :total="total3" showCount showPage :limits="limits3"></lay-page>
</template>
<script>
@ -117,14 +110,14 @@ import { ref } from 'vue'
export default {
setup() {
const limit = ref(20)
const total = ref(100)
const showPage = ref(true)
const limit3 = ref(5)
const total3 = ref(100)
const limits3 = ref([5, 10, 50, 100, 200])
return {
limit,
total,
showPage
limit3,
total3,
limits3
}
}
}
@ -132,58 +125,33 @@ export default {
:::
::: title 指定分页容量
::: title 回调事件
:::
::: demo
<template>
<lay-page :limit="limit" :total="total" showCount showPage :limits="[10,50,100,200]" @limit="limit=$event"></lay-page>
<lay-page :limit="limit4" :total="total4" @change="change4" :show-page="true"></lay-page>
</template>
<script>
import { ref } from 'vue'
import { layer } from "@layui/layui-vue";
export default {
setup() {
const limit = ref(20)
const total = ref(100)
return {
limit,
total
const limit4 = ref(20)
const total4 = ref(100)
const change4 = ({ current, limit }) => {
layer.msg("current:" + current + " limit:" + limit);
}
}
}
</script>
:::
::: title 每页数量切换事件(limit)
:::
::: demo
<template>
<lay-page :limit="limit" showPage showCount :total="total" @limit="limit=$event" :show-limit="showLimit" ></lay-page>
<div>每页数量:{{limit}}</div>
</template>
<script>
import { ref } from 'vue'
export default {
setup() {
const limit = ref(5)
const total = ref(9999)
const showLimit = ref(true)
return {
limit,
total,
showLimit,
limit4,
total4,
change4
}
}
}
@ -197,8 +165,12 @@ export default {
::: demo
<template>
<lay-page :limit="limit1" :pages="pages1" :total="total1" :show-count="showCount1" @limit="limit1=$event" :show-page="showPage1" :show-limit="showLimit1" :show-refresh="showRefresh1" :showSkip="showSkip1"></lay-page>
每页数量:{{limit1}}
<lay-button-container>
<lay-button type="primary" size="sm" @click="changeCurrent5">update model {{ current5 }}</lay-button>
<lay-button type="primary" size="sm" @click="changeLimit5">update limit {{ limit5 }}</lay-button>
</lay-button-container>
<br/>
<lay-page v-model="current5" v-model:limit="limit5" :pages="pages5" :total="total5" :show-count="true" :show-page="true" :show-limit="true" :show-refresh="true" :showSkip="true" @change="change5"></lay-page>
</template>
<script>
@ -207,60 +179,27 @@ import { ref } from 'vue'
export default {
setup() {
const limit1 = ref(5)
const total1 = ref(99)
const showCount1 = ref(true)
const showPage1 = ref(true)
const showLimit1 = ref(true)
const showRefresh1 = ref(true)
const showSkip1 = ref(true)
const pages1 = ref(6);
return {
limit1,
total1,
pages1,
showCount1,
showPage1,
showLimit1,
showRefresh1,
showSkip1
const limit5 = ref(10)
const total5 = ref(99)
const pages5 = ref(6);
const current5 = ref(1);
const changeCurrent5 = () => {
current5.value = 2;
}
}
}
</script>
:::
::: title 页码切换事件(jump)
:::
::: demo
<template>
<lay-page :limit="limit" :total="total" @jump="jump" @limit="limit = $event" :show-page="showSkip"></lay-page>
</template>
<script>
import { ref } from 'vue'
export default {
setup() {
const limit = ref(20)
const total = ref(100)
const showPage = ref(true)
const showSkip = ref(true)
const jump = function({ current }) {
console.log("当前页:" + current)
const changeLimit5 = () => {
limit5.value = 20;
}
const change5 = ({ current, limit }) => {
layer.msg("current:" + current + " limit:" + limit);
}
return {
limit,
total,
jump,
showPage,
showSkip
limit5,
total5,
pages5,
current5,
changeCurrent5,
changeLimit5,
change5
}
}
}
@ -294,10 +233,11 @@ export default {
::: table
| 事件 | 描述 | 参数 |
| ---- | -------- | --------------------- |
| jump | 切换回调 | { current: 当前页面 } |
| limit | 每页数量变化 | 变化后的值 |
| 事件 | 描述 | 参数 | 版本 |
| ---- | -------- | --------------------- |--------------------- |
| jump | 切换回调 | { current: 当前页面 } | `移除` |
| limit | 每页数量变化 | 变化后的值 | `移除` |
| change | 分页事件 | { current: 当前页码, limit: 每页数量 } | `1.4.3` |
:::

View File

@ -13,8 +13,10 @@
::: demo 使用 `lay-scroll` 标签, 创建一个虚拟滚动容器
<template>
<lay-button @click="changeTotal">修改数据</lay-button>
<lay-button @click="changeMaxTotal">修改数据</lay-button>
<lay-scroll height="200px" style="background-color:whitesmoke;">
<lay-panel v-for="(n,index) in 50" :key="n" style="margin:10px;padding:10px;">内容</lay-panel>
<lay-panel v-for="(n,index) in total" :key="n" style="margin:10px;padding:10px;">内容</lay-panel>
</lay-scroll>
</template>
@ -23,8 +25,20 @@ import { ref } from 'vue'
export default {
setup() {
const total = ref(50);
const changeTotal = () => {
total.value = 2;
}
const changeMaxTotal = () => {
total.value = 50;
}
return {
total,
changeTotal,
changeMaxTotal
}
}
}

View File

@ -0,0 +1,245 @@
::: anchor
:::
::: title 基本介绍
:::
::: describe 控制组件之间的间距。
:::
::: title 基础使用
:::
::: demo 默认横向排列,控制相邻组件的水平间距
<template>
<lay-space>
<lay-button v-for="idx of 5" type="normal">按钮 {{idx}}</lay-button>
</lay-space>
</template>
<script>
import { ref } from 'vue'
export default {
setup() {
return {
}
}
}
</script>
:::
::: title 垂直方向
:::
::: demo 通过 `direction="vertical"` 设置垂直方向
<template>
<lay-space direction="vertical" fill wrap>
<lay-button v-for="idx of 5" type="normal" fluid="true">按钮 {{idx}}</lay-button>
</lay-space>
</template>
<script>
import { ref } from 'vue'
export default {
setup() {
return {
}
}
}
</script>
:::
::: title 间距大小
:::
::: demo 通过 `size` 控制组件大小 `xs, sm, md , lg`, 分别对应 `4px, 8px, 16px, 24px` 的间距,默认`md`。`size` 也支持通过数组设置间距 `[row-gap, column-gap]`
<template>
<lay-radio
v-for="sizeKey of sizeKeys"
v-model="sizeSelected"
name="action"
:value="sizeKey"
@change="changeSize">
{{sizeKey}}
</lay-radio>
<br><br>
<lay-space :size="spaceSize">
<lay-button v-for="idx of 5" type="normal">按钮 {{idx}}</lay-button>
</lay-space>
</template>
<script>
import { ref } from 'vue'
export default {
setup() {
const sizeKeys = ["xs","sm","md","lg"];
const spaceSize = ref();
const sizeSelected = ref("sm");
const changeSize = function( key ) {
spaceSize.value = key;
}
return {
sizeSelected,
changeSize,
spaceSize,
}
}
}
</script>
:::
::: title 自定义间距大小
:::
::: demo
<template>
<lay-slider v-model="customSize"></lay-slider>
<br><br>
<lay-space :size="customSize">
<lay-button v-for="idx of 5" type="normal">按钮 {{idx}}</lay-button>
</lay-space>
</template>
<script>
import { ref } from 'vue'
export default {
setup() {
const customSize = ref(8);
return {
customSize,
}
}
}
</script>
:::
::: title 对齐方式
:::
::: demo
<template>
<lay-radio
v-for="alignKey of alignKeys"
v-model="alignSelected"
name="action"
:value="alignKey"
@change="changeAlign">
{{alignKey}}
</lay-radio>
<br><br>
<lay-space :align="spaceAlign" style="backgroundColor: whitesmoke;padding: 10px;">
<span>Space:</span>
<lay-button>默认按钮</lay-button>
<lay-card title="标题">
内容
</lay-card>
</lay-space>
</template>
<script>
import { ref } from 'vue'
export default {
setup() {
const alignKeys = ["start", "center","end","baseline"];
const spaceAlign = ref();
const alignSelected = ref("center");
const changeAlign = function( key ) {
spaceAlign.value = key;
}
return {
alignSelected,
changeAlign,
spaceAlign,
}
}
}
</script>
:::
::: title 自动换行
:::
::: demo
<template>
<lay-space wrap :size="[16,24]">
<lay-button v-for="_ of 20">默认按钮{{_}}</lay-button>
</lay-space>
</template>
<script>
import { ref } from 'vue'
export default {
setup() {
return {
}
}
}
</script>
:::
::: title Space 属性
:::
::: table
| 属性 | 描述 | 类型 | 默认值 | 可选值 |
| ----------- | -------- | ------ | ------ | ------ |
| align | 对齐方式 | string| `center`| `start` `end` `center` `baseline`|
| direction | 间距方向 | string | `horizontal` | `horizontal` `vertical`|
| fill | 子元素是否填充父容器 | boolean| `false`| -|
| size | 间距大小 | string | md | `lg` `md` `sm` `xs` `number` `string` `[spaceSize,spaceSize]`|
| wrap | 是否自动折行 | boolean| `false`| -|
:::
>`type spaceSize = "lg" | "md" | "sm" | "xs" | number | string`
> `[row-gap, column-gap], eg: ['xs','md'] ['10px', '15px'] [10, 15]`
:::title Space 插槽
:::
:::table
| 插槽 | 描述 | 参数 |
|------ |----------|-----------|
| default | 默认插槽 | - |
:::
::: contributor space
:::
::: previousNext space
:::

View File

@ -119,23 +119,26 @@ export default {
::: demo
<template>
page props: {{ page3 }}
<lay-table :columns="columns3" :data-source="dataSource3" :page="page3" @change="change3"></lay-table>
</template>
<script>
import { ref } from 'vue'
import { ref } from 'vue';
import { layer } from "@layui/layer-vue";
export default {
setup() {
const page3 = {
const page3 = ref({
total: 100,
limit: 10,
current: 2
}
current: 2,
showRefresh: true,
})
const change3 = function({ current }){
console.log("当前页:" + JSON.stringify(current))
const change3 = ({ current, limit }) => {
layer.msg("current:" + current + " limit:" + limit);
}
const columns3 = [
@ -697,8 +700,9 @@ export default {
::: demo 通过 `columns` 配置 `type:'checkbox'` 开启单选列。
<template>
<lay-button @click="changeSelectedKeys">修改选中值 {{ selectedKeys5 }}</lay-button>
<lay-table :columns="columns23" :data-source="dataSource23" v-model:selectedKeys="selectedKeys5" :getCheckboxProps="getCheckboxProps"></lay-table>
<lay-button @click="changeSelectedKeys">修改选中</lay-button>
<lay-button @click="changeDataSource23">修改数据</lay-button>
<lay-table :columns="columns23" :data-source="dataSource23" v-model:selectedKeys="selectedKeys5"></lay-table>
</template>
<script>
@ -720,14 +724,23 @@ export default {
selectedKeys5.value = ["2"]
}
const changeDataSource23 = () => {
dataSource23.value = [
{id:"1",username:"root", password:"root",sex:"男", age:"18", remark: 'layui - vue谐音类 UI) '},
{id:"2",username:"root", password:"root",sex:"男", age:"18", remark: 'layui - vue谐音类 UI) '}
]
}
const columns23 = [
{
fixed: "left",
type: "checkbox",
},
{
title:"账户",
width:"200px",
key:"username"
key:"username",
fixed: "left"
},{
title:"密码",
width: "300px",
@ -747,20 +760,21 @@ export default {
}
]
const dataSource23 = [
const dataSource23 = ref([
{id:"1",username:"root", password:"root",sex:"男", age:"18", remark: 'layui - vue谐音类 UI) '},
{id:"2",username:"root", password:"root",sex:"男", age:"18", remark: 'layui - vue谐音类 UI) '},
{id:"3",username:"woow", password:"woow",sex:"男", age:"20", remark: 'layui - vue谐音类 UI) '},
{id:"4",username:"woow", password:"woow",sex:"男", age:"20", remark: 'layui - vue谐音类 UI) '},
{id:"5",username:"woow", password:"woow",sex:"男", age:"20", remark: 'layui - vue谐音类 UI) '}
]
])
return {
columns23,
dataSource23,
selectedKeys5,
changeSelectedKeys,
getCheckboxProps
getCheckboxProps,
changeDataSource23
}
}
}
@ -1455,7 +1469,7 @@ export default {
| row | 行单击 | data : 当前行 |
| row-double | 行双击 | data : 当前行 |
| row-contextmenu | 行右击 | data : 当前行 |
| change | 分页事件 | { current: 当前页码, limit: 每页数量 } |
:::
::: title Table 插槽
@ -1463,9 +1477,10 @@ export default {
::: table
| 插槽 | 描述 | 参数 |
| ------- | ------------ | ---- |
| toolbar | 自定义工具栏 | -- |
| 插槽 | 描述 | 参数 | 版本 |
| ------- | ------------ | ---- |---- |
| toolbar | 自定义工具栏 | -- |-- |
| footer | 底部扩展 | -- | `1.4.4` |
:::
@ -1476,7 +1491,7 @@ export default {
::: table
| 插槽 | 描述 | 类型 | 默认值 | 可选值 | 可选值 |
| 插槽 | 描述 | 类型 | 默认值 | 可选值 | 版本 |
| --------------- | ------------------------------ | --------- | ------- | --------------------------- | --------------------------- |
| title | 列标题 | -- | -- | -- | -- |
| key | 数据字段 | -- | -- | -- | -- |

View File

@ -13,7 +13,7 @@
::: demo 使用 `lay-upload` 标签, 创建一个上传按钮
<template>
<lay-upload @done="getUploadFile" @choose="beginChoose">
<lay-upload @done="getUploadFile" field="bigFile" @choose="beginChoose">
<template #preview>
<div v-for="(item,index) in picList" :key="`demo1-pic-'${index}`">
<img :src="item"/>
@ -249,7 +249,7 @@ export default {
| url | 服务端上传接口的地址 | string | -- | -- |
| data | 请求上传接口的额外参数 | object | -- | -- |
| headers | 接口的请求头 | object | -- | -- |
| acceptMime | 文件选择框时的可选文件类型 | string | `images` | `images` |
| acceptMime | 文件选择框时的可选文件类型 | string | `MIME_type` | `MIME_type` |
| field | 设定文件域的字段名 | string | `file` | -- |
| size | 设置文件最大可允许上传的大小,单位 KB。不支持ie8/9 | number | `0(不限制)` | -- |
| multiple | 是否允许多文件上传。设置 true即可开启。不支持ie8/9 | boolean | false | -- |

View File

@ -10,6 +10,115 @@
::: demo
<template>
<lay-timeline>
<lay-timeline-item title="1.4.x">
<ul>
<a name="1-4-6"></a>
<li>
<h3>1.4.6 <span class="layui-badge-rim">2022-0x-xx</span></h3>
<ul>
<li>[修复] dropdown 组件 hide 事件触发异常。</li>
<li>[优化] dropdown 组件 open 方法修改为 show 方法。</li>
</ul>
</li>
</ul>
<ul>
<a name="1-4-5"></a>
<li>
<h3>1.4.5 <span class="layui-badge-rim">2022-08-26</span></h3>
<ul>
<li>[修复] upload 组件 field 属性无效。</li>
<li>[修复] upload 组件 acceptMime 属性 默认值 无效。</li>
<li>[修复] menu 组件 changeOpenKeys 事件, 初始化时回调的问题。</li>
<li>[修复] dropdown 组件 popupContainer 不适用于 vue 自身渲染的元素的问题。</li>
<li>[优化] table 组件 page 属性, 与 page 组件属性对应, 并全部启用。 </li>
<li>[优化] input 组件 password 属性, 在 edge 的兼容问题。 </li>
<li>[优化] page 组件 total 属性为 0 时, 下一页仍可用的问题。</li>
<li>[优化] upload 组件 acceptMime 属性默认值为 MIME_type。</li>
</ul>
</li>
</ul>
<ul>
<a name="1-4-4"></a>
<li>
<h3>1.4.4 <span class="layui-badge-rim">2022-08-18</span></h3>
<ul>
<li>[新增] table 组件 footer 插槽, 用在 page 与 body 之间自定义内容。</li>
<li>[修复] date-picker 组件 v-model 为空时, 无法完成日期时间选择。</li>
<li>[修复] quote 组件 margin 属性错误 。</li>
</ul>
</li>
</ul>
<ul>
<a name="1-4-3"></a>
<li>
<h3>1.4.3 <span class="layui-badge-rim">2022-08-16</span></h3>
<ul>
<li>[新增] page 组件 change 事件。</li>
<li>[新增] card 组件 footer 插槽, 用于自定义底部。</li>
<li>[新增] table 组件 change 事件 limit 参数, 代表每页数量。</li>
<li>[修复] scroll 组件 default slots 改变时, 滑块不更新的问题。</li>
<li>[修复] table 组件 loading 属性造成的单元格错位。</li>
<li>[优化] page 组件 跳转 操作, 当输入页码为当前页启用禁用状态。</li>
<li>[过时] page 组件 limit 事件, 由 change 事件代替。</li>
<li>[过时] page 组件 jump 事件, 由 change 事件代替。</li>
</ul>
</li>
</ul>
<ul>
<a name="1-4-2"></a>
<li>
<h3>1.4.2 <span class="layui-badge-rim">2022-08-15</span></h3>
<ul>
<li>[修复] table 组件 fixed 属性开启时, 不设置 width 产生的错误。</li>
<li>[修复] table 组件 dataSource 属性改变时, 清空 selectedKeys 内容。</li>
<li>[修复] table 组件 dataSource 属性改变时, 清空 selectedKey 内容。</li>
<li>[优化] table 组件 fixed 属性开启时, 根据 column 的 type 属性, 设置默认宽度。 </li>
</ul>
</li>
</ul>
<ul>
<a name="1-4-1"></a>
<li>
<h3>1.4.1 <span class="layui-badge-rim">2022-08-14</span></h3>
<ul>
<li>[修复] 表格开启复选框之后不使用getCheckboxProps属性点击时全选会报错。</li>
</ul>
</li>
</ul>
<ul>
<a name="1-4-0"></a>
<li>
<h3>1.4.0 <span class="layui-badge-rim">2022-08-13</span></h3>
<ul>
<li>[新增] button 组件 dropdown 下拉 demo。</li>
<li>[新增] button 组件 loading-icon 属性, 允许自定义加载图标。</li>
<li>[新增] table 组件 loading 属性, 数据过渡。</li>
<li>[新增] table 组件 column 属性 children 配置, 支持表头分组。</li>
<li>[新增] table 组件 getRadioProps 属性, 启用单选列时, 定义 radio 配置选项。</li>
<li>[新增] table 组件 getCheckboxProps 属性, 启用复选列, 定义 checkbox 配置选项。</li>
<li>[新增] transfer 组件 datasource 属性 disabled 配置, 允许选项禁用。</li>
<li>[新增] switch 组件 loading 属性, 开启加载状态, 默认为 false。</li>
<li>[新增] switch 组件 loading-icon 属性, 允许自定义加载图标, 可选值为内置图标集。</li>
<li>[新增] date-picker 组件 prefix-icon 属性, 用于自定义输入框前置图标, layui-icon-date 为默认值。</li>
<li>[新增] date-picker 组件 suffix-icon 属性, 用于自定义输入框后置图标。</li>
<li>[修复] table 组件 column 属性为 fixed 时, 隐藏该列时不重新计算距离。</li>
<li>[修复] input 组件 v-model 属性输入拼字阶段触发更新的问题。</li>
<li>[修复] table 组件 height 属性修改时, 造成单元格错位。</li>
<li>[修复] table 组件 demand 模式缺失 radio.css 文件。</li>
<li>[修复] menu 组件 demand 模式缺失 dropdown.css 文件。</li>
<li>[修复] textarea 组件无法解析 lay-icon 的警告。</li>
<li>[优化] input 组件 password 属性开启时的默认图标。</li>
<li>[优化] table 组件 dropdown 筛选列面板随滚动条移动。</li>
<li>[优化] table 组件 column 无对应列时仍保持列占位。</li>
<li>[优化] table 组件 skin 属性为 row 时 header 高出 1 像素。</li>
<li>[优化] transfer 组件 title 在特殊分辨率下显示不全。</li>
<li>[优化] notice-bar 组件 width 越界。</li>
<li>[优化] input 组件 clear 操作背景透明的问题。</li>
<li>[优化] input 组件 password 操作背景透明的问题。</li>
</ul>
</li>
</ul>
</lay-timeline-item>
<lay-timeline-item title="1.3.x">
<ul>
<a name="1-3-14"></a>

View File

@ -429,6 +429,11 @@ const zhCN = [
component: () => import("../document/zh-CN/components/tagInput.md"),
meta: { title: "标签" },
},
{
path: "/zh-CN/components/space",
component: () => import("../document/zh-CN/components/space.md"),
meta: { title: "间距" },
},
],
},
],

View File

@ -32,7 +32,7 @@
>
<span
>{{ t("home.download") }}<em class="site-showdowns"
>20,894</em
>24,177</em
></span
>
</div>
@ -43,7 +43,7 @@
rel="nofollow"
class="site-star"
>
<i class="layui-icon"></i> Star <cite id="getStars">1398</cite>
<i class="layui-icon"></i> Star <cite id="getStars">1465</cite>
</a>
<a
href="https://gitee.com/layui-vue"
@ -179,14 +179,14 @@
</lay-tooltip>
</lay-col>
<lay-col :md="3">
<lay-tooltip content="Pear Admin" position="top">
<lay-tooltip content="Sa-Token" position="top">
<a
style="height: 40px; display: inline-block"
href="http://www.pearadmin.com"
href="https://sa-token.dev33.cn/"
target="_blank"
>
<lay-avatar
src="https://portrait.gitee.com/uploads/avatars/namespace/2319/6958819_pear-admin_1643085106.png!avatar100"
src="https://sa-token.dev33.cn/doc/logo.png"
style="background: transparent"
></lay-avatar>
</a>

View File

@ -88,6 +88,12 @@ const menus = [
subTitle: "skeleton",
path: "/zh-CN/components/skeleton",
},
{
id: 12,
title: "间距",
subTitle: "space",
path: "/zh-CN/components/space",
},
],
},
{
@ -427,26 +433,6 @@ const menus = [
},
],
},
{
id: 1,
title: "新特性",
children: [
{
id: 101,
title: "标签",
flag: "new",
subTitle: "modal",
path: "/zh-CN/components/tag",
},
{
id: 102,
title: "标签输入框",
flag: "new",
subTitle: "load",
path: "/zh-CN/components/tagInput",
},
],
},
];
export default menus;

File diff suppressed because it is too large Load Diff