Merge branch 'next' into feat-tag
This commit is contained in:
commit
b0bccc3292
@ -6,6 +6,8 @@
|
||||
|
||||
- 3.合理的建议
|
||||
|
||||
- 4.当前版本
|
||||
|
||||
## 思路
|
||||
|
||||
描述大概的解决思路,可以包含 API 设计和伪代码等
|
||||
|
18
.github/ISSUE_TEMPLATE.md
vendored
18
.github/ISSUE_TEMPLATE.md
vendored
@ -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` 的方式。
|
@ -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)
|
||||
|
@ -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)
|
||||
|
@ -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",
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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>
|
||||
|
@ -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") {
|
||||
// date与datetime容错
|
||||
if (currentYear.value === -1) currentYear.value = dayjs().year();
|
||||
if (currentMonth.value === -1) currentMonth.value = dayjs().month();
|
||||
}
|
||||
|
33
package/component/src/component/dropdown/TeleportWrapper.vue
Normal file
33
package/component/src/component/dropdown/TeleportWrapper.vue
Normal 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>
|
@ -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>
|
||||
|
@ -31,6 +31,7 @@ export interface DropdownContext {
|
||||
onMouseleave: Function;
|
||||
addChildRef: Function;
|
||||
removeChildRef: Function;
|
||||
hide: Function;
|
||||
}
|
||||
|
||||
export const dropdownInjectionKey = Symbol("dropdownInjectKey");
|
||||
|
@ -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>
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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>
|
||||
|
@ -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>
|
||||
|
@ -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;
|
||||
// clientY:当鼠标事件发生时,鼠标相对于浏览器(这里说的是浏览器的有效区域)y轴的位置
|
||||
// data.translateY 滚动块平移的距离
|
||||
@ -194,7 +182,7 @@ const moveStart = function (e) {
|
||||
};
|
||||
|
||||
// 鼠标移动,改变thumb的位置以及容器scrollTop的位置
|
||||
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;
|
||||
}
|
||||
}
|
||||
};
|
||||
};
|
||||
|
26
package/component/src/component/space/Renderer.ts
Normal file
26
package/component/src/component/space/Renderer.ts
Normal 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);
|
||||
};
|
||||
},
|
||||
});
|
42
package/component/src/component/space/index.less
Normal file
42
package/component/src/component/space/index.less
Normal 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;
|
||||
}
|
||||
}
|
5
package/component/src/component/space/index.ts
Normal file
5
package/component/src/component/space/index.ts
Normal 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;
|
132
package/component/src/component/space/index.vue
Normal file
132
package/component/src/component/space/index.vue
Normal 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>
|
@ -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,
|
||||
|
62
package/component/src/component/table/TablePage.vue
Normal file
62
package/component/src/component/table/TablePage.vue
Normal 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>
|
@ -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;
|
||||
|
@ -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,
|
||||
|
@ -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>
|
||||
|
@ -5,6 +5,7 @@ export default {
|
||||
</script>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { LayIcon } from "@layui/icons-vue";
|
||||
import { computed } from "vue";
|
||||
import "./index.less";
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
// 对应Upload属性的data字段,额外的上传参数
|
||||
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, get请求数据发送只能拼接在URL后面
|
||||
// 对应Upload属性的headers字段,额外的上传参数
|
||||
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];
|
||||
// 对应Upload属性的number字段,控制单次上传个数
|
||||
if (props.multiple && props.number != 0 && props.number < _files.length) {
|
||||
errorF(numberErrorMsg);
|
||||
return;
|
||||
}
|
||||
// 对应Upload属性的size字段,控制上传图片的大小
|
||||
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>
|
||||
|
||||
|
@ -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,
|
||||
};
|
||||
|
||||
|
@ -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
|
||||
|
@ -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` |
|
||||
|
||||
:::
|
||||
|
||||
|
@ -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>
|
||||
|
||||
<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 | 显示下拉面板后触发 | -- |
|
||||
|
||||
:::
|
||||
|
||||
|
@ -142,7 +142,7 @@ export default {
|
||||
|
||||
:::
|
||||
|
||||
::: title 表单基本校验功能
|
||||
::: title 表单验证
|
||||
:::
|
||||
|
||||
::: demo
|
||||
@ -231,7 +231,7 @@ export default {
|
||||
|
||||
:::
|
||||
|
||||
::: title 校验规则 - 通过表单配置
|
||||
::: title 校验规则
|
||||
:::
|
||||
|
||||
::: demo
|
||||
|
@ -735,6 +735,18 @@ export default {
|
||||
|
||||
:::
|
||||
|
||||
::: title Menu 事件
|
||||
:::
|
||||
|
||||
::: table
|
||||
|
||||
| 属性 | 描述 | 参数 |
|
||||
| ------------------- | ------------------- | -------------- |
|
||||
| changeSelectedKey | 选中菜单回调 | value: string |
|
||||
| changeOpenKeys | 展开目录回调 | value: string[] |
|
||||
|
||||
:::
|
||||
|
||||
::: title Menu Item 属性
|
||||
:::
|
||||
|
||||
|
@ -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` |
|
||||
|
||||
:::
|
||||
|
||||
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
:::
|
@ -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 | 数据字段 | -- | -- | -- | -- |
|
||||
|
@ -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 | -- |
|
||||
|
@ -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>
|
||||
|
@ -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: "间距" },
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
|
@ -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>
|
||||
|
@ -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;
|
||||
|
1265
pnpm-lock.yaml
1265
pnpm-lock.yaml
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue
Block a user