Merge branch 'develop' of https://gitee.com/layui-vue/layui-vue into develop

This commit is contained in:
castleiMac
2022-01-04 14:49:37 +08:00
86 changed files with 1107 additions and 313 deletions

View File

@@ -2844,6 +2844,25 @@ body .layui-table-tips .layui-layer-content {
color: #5fb878;
}
.layui-nav .layui-show.layui-anim-upbit .layui-show.layui-anim-upbit {
top: 0px;
left: 106px;
}
.layui-nav .layui-show.layui-anim-upbit .layui-nav-item {
display: block;
height: 40px;
line-height: 40px;
}
.layui-nav .layui-show.layui-anim-upbit .layui-nav-item.layui-this {
background-color: whitesmoke;
}
.layui-nav .layui-show.layui-anim-upbit .layui-nav-item.layui-this:after {
display: none;
}
.layui-nav {
position: relative;
padding: 0 20px;
@@ -2868,7 +2887,7 @@ body .layui-table-tips .layui-layer-content {
.layui-nav .layui-nav-item a {
display: block;
padding: 0 20px;
padding: 0 30px;
color: #fff;
color: rgba(255, 255, 255, 0.7);
-webkit-transition: all 0.3s;
@@ -2881,7 +2900,7 @@ body .layui-table-tips .layui-layer-content {
left: 0;
top: 0;
width: 0;
height: 5px;
height: 3px;
background-color: #5fb878;
transition: all 0.2s;
-webkit-transition: all 0.2s;
@@ -2917,7 +2936,7 @@ body .layui-table-tips .layui-layer-content {
.layui-nav .layui-nav-more {
position: absolute;
top: 0;
right: 3px;
right: 8px;
left: auto !important;
margin-top: 0;
font-size: 12.5px !important;
@@ -2988,13 +3007,13 @@ body .layui-table-tips .layui-layer-content {
.layui-nav-tree .layui-nav-item {
display: block;
width: 100%;
line-height: 40px;
line-height: 42px;
}
.layui-nav-tree .layui-nav-item a {
position: relative;
height: 46px;
line-height: 46px;
height: 42px;
line-height: 42px;
text-overflow: ellipsis;
overflow: hidden;
white-space: nowrap;

View File

@@ -6,7 +6,7 @@ import "@layui/layer-vue/lib/index.css";
import "@layui/icons-vue/lib/index.css";
import { layer } from "@layui/layer-vue";
import LayModal from "./module/layer/modal/index";
import LayLayer from "./module/layer/index";
import LayBacktop from "./module/backTop/index";
import LayAvatar from "./module/avatar/index";
import LayRadio from "./module/radio/index";
@@ -40,7 +40,6 @@ import LayContainer from "./module/container/index";
import LayCountUp from "./module/countUp/index";
import LayMenu from "./module/menu/index";
import LayMenuItem from "./module/menuItem/index";
import LayMenuChildItem from "./module/menuChildItem/index";
import LayCheckbox from "./module/checkbox/index";
import LayCheckboxGroup from "./module/checkboxGroup/index";
import LayForm from "./module/form/index";
@@ -67,6 +66,8 @@ import LayCarouselItem from "./module/carouselItem/index";
import LayColorPicker from "./module/colorPicker/index";
import LayTooltip from "./module/tooltip/index";
import LayInputNumber from "./module/inputNumber/index";
import LaySkeleton from './module/skeleton/index';
import LaySkeletonItem from './module/skeletonItem/index';
const components: Record<string, IDefineComponent> = {
LayRadio,
@@ -99,7 +100,6 @@ const components: Record<string, IDefineComponent> = {
LayContainer,
LayMenu,
LayMenuItem,
LayMenuChildItem,
LayCheckbox,
LayForm,
LayBreadcrumb,
@@ -126,9 +126,11 @@ const components: Record<string, IDefineComponent> = {
LayCarousel,
LayCarouselItem,
LayColorPicker,
LayModal,
LayLayer,
LayTooltip,
LayInputNumber,
LaySkeleton,
LaySkeletonItem,
LayCountUp,
};
@@ -142,6 +144,8 @@ const install = (app: App, options?: InstallOptions): void => {
};
export {
LaySkeleton,
LaySkeletonItem,
LayRadio,
LayIcon,
LayButton,
@@ -172,7 +176,6 @@ export {
LayContainer,
LayMenu,
LayMenuItem,
LayMenuChildItem,
LayCheckbox,
LayForm,
LayBreadcrumb,
@@ -199,7 +202,7 @@ export {
LayCarousel,
LayCarouselItem,
LayColorPicker,
LayModal
LayLayer
};
export { layer };

View File

@@ -0,0 +1,9 @@
import type { App } from "vue";
import { LayLayer } from "@layui/layer-vue";
import type { IDefineComponent } from "../type/index";
LayLayer.install = (app: App) => {
app.component(LayLayer.name || "LayLayer", LayLayer);
};
export default LayLayer as IDefineComponent;

View File

@@ -1,9 +0,0 @@
import type { App } from "vue";
import { LayModal } from "@layui/layer-vue";
import type { IDefineComponent } from "../../type/index";
LayModal.install = (app: App) => {
app.component(LayModal.name || "LayModal", LayModal);
};
export default LayModal as IDefineComponent;

View File

@@ -1,9 +0,0 @@
import type { App } from "vue";
import Component from "./index.vue";
import type { IDefineComponent } from "../type/index";
Component.install = (app: App) => {
app.component(Component.name || "LayMenuChildItem", Component);
};
export default Component as IDefineComponent;

View File

@@ -1,25 +0,0 @@
<template>
<dd :class="[selectedKey === id ? 'layui-this' : '']" @click="selectHandle()">
<slot v-if="slots.title" name="title"></slot>
<a v-else href="javascript:void(0)">
{{ title }}
</a>
</dd>
</template>
<script setup name="LayMenuChildItem" lang="ts">
import { defineProps, inject, Ref, useSlots } from "vue";
const slots = useSlots();
const props = defineProps<{
id: string;
title: string;
}>();
const selectedKey: Ref<string> = inject("selectedKey") as Ref<string>;
const selectHandle = function () {
selectedKey.value = props.id;
};
</script>

View File

@@ -14,21 +14,21 @@
@attr : ~'[position=@{position}]';
&{
border: 1px solid @m-border-color;
&.layui-popper@{attr}{
margin-@{contrary_position}: 6px;
.layui-popper-arrow {
@{contrary_position}: -6px;
border-@{contrary_position}-width: 0;
border-@{position}-color: @m-border-color;
&::after{
@{contrary_position}: 1px;
&@{attr}{
margin-@{contrary_position}: 6px;
.layui-popper-arrow {
@{contrary_position}: -6px;
border-@{contrary_position}-width: 0;
margin-@{margin_postion}: -6px;
border-@{position}-color: @color;
border-@{position}-color: @m-border-color;
&::after{
@{contrary_position}: 1px;
border-@{contrary_position}-width: 0;
margin-@{margin_postion}: -6px;
border-@{position}-color: @color;
}
}
}
}
}
}
// 统一设置四个方向的主题
.theme(@background-color, @color, @border-color) {
@@ -43,7 +43,7 @@
// 箭头默认居中
.arrow-default-center(@position, @prop) {
@attr : ~'[position=@{position}]';
&.layui-popper@{attr} {
&@{attr} {
.layui-popper-arrow{
@{prop}: -moz-calc(50% - 6px);
@{prop}: -webkit-calc(50% - 6px);
@@ -58,6 +58,23 @@
.arrow-default-center(right, top);
}
// 填充popper支持可以移动到popper使用到
.single-fill-popper(@position, @contrary_position, @zeroPosition, @all, @seven){
@attr : ~'[position=@{position}]';
&@{attr}::after{
@{contrary_position}: -7px;
@{zeroPosition}: 0;
@{all}: 100%;
@{seven}: 7px;
}
}
.fill-popper(){
.single-fill-popper(top, bottom, left, width, height);
.single-fill-popper(bottom, top, left, width, height);
.single-fill-popper(left, right, bottom, height, width);
.single-fill-popper(right, left, bottom, height, width);
}
// 样式开始
.layui-popper {
position: fixed;
@@ -76,9 +93,17 @@
// 箭头默认居中
.all-arrow-default-center();
&::after{
content: " ";
position: absolute;
display: block;
}
// 填充
.fill-popper();
.layui-popper-arrow {
&,&::after{
position: absolute;
position: absolute;
display: block;
width: 0;
height: 0;

View File

@@ -1,15 +1,17 @@
<template>
<transition v-show="innerVisible">
<div
ref="popper"
:class="['layui-popper', { 'layui-dark': innnerIsDark }]"
:style="style"
:position="innnerPosition"
>
<slot>{{ content.value }}</slot>
<div class="layui-popper-arrow"></div>
</div>
</transition>
<teleport to="body" v-if="isExist">
<transition v-show="innerVisible">
<div
ref="popper"
:class="['layui-popper', { 'layui-dark': isDark }]"
:style="style"
:position="position"
>
<slot>{{ content }}</slot>
<div class="layui-popper-arrow"></div>
</div>
</transition>
</teleport>
</template>
<script lang="ts">
const NAME = "LayPopper";
@@ -25,30 +27,33 @@ import {
CSSProperties,
ref,
watch,
onUpdated,
defineEmits,
onMounted,
Ref,
} from "vue";
import { on } from "../../tools/domUtil";
const props = withDefaults(
defineProps<{
el: any;
content?: Ref<string | Number>;
position?: Ref<string>;
content?: string | Number;
position?: string;
trigger?: string;
enterable?: boolean;
isDark?: Ref<boolean>;
disabled?: Ref<boolean>;
visible?: Ref<boolean>;
isCanHide?: Ref<boolean>;
updateVisible?: Function;
isDark?: boolean;
disabled?: boolean;
visible?: boolean;
isCanHide?: boolean;
}>(),
{
position: 'top',
isDark: true,
disabled: false,
enterable: true,
visible: true,
isCanHide: true,
trigger: "hover",
}
);
const emit = defineEmits(["update:visible"]);
const EVENT_MAP: any = {
hover: ["mouseenter", null, "mouseleave", false],
@@ -63,55 +68,52 @@ if (!triggerArr) {
const style = ref<CSSProperties>({ top: -window.innerHeight + "px", left: 0 });
const checkTarget = ref(false);
const popper = ref<HTMLDivElement>({} as HTMLDivElement);
const tempPosition = props.position ?? ref("top");
const innnerPosition = ref(tempPosition.value);
const innnerIsDark = ref(props.isDark ?? true);
const innnerDisabled = ref(props.disabled ?? false);
const innnerPosition = ref(props.position);
const innerVisible = ref(props.visible ?? true);
const isExist = ref(false);
watch(innerVisible, (val) => {
invokeShowPosistion();
props.updateVisible && props.updateVisible(val);
emit("update:visible", val);
});
watch(innnerDisabled, (val) => {
innerVisible.value = false;
watch(popper, (val) => {
if (props.trigger === 'hover' && props.enterable) {
on(popper.value, EVENT_MAP['hover'][0], doShow);
on(popper.value, EVENT_MAP['hover'][2], doHidden);
}
});
watch(
() => props.content?.value,
() => props.content,
(val) => {
innerVisible.value && invokeShowPosistion();
}
);
const doShow = function () {
if (!innnerDisabled.value) {
if (!props.disabled) {
innerVisible.value = true;
isExist.value = true;
}
};
const doHidden = function (e: MouseEvent) {
// ||(props.enterable && popper.value.contains(e.target as Node))
if (
(checkTarget.value && props.el.contains(e.target)) ||
(props.enterable && popper.value.contains(e.target as Node))
(checkTarget.value && props.el.contains(e.target))
)
return;
// style.value = {top: (-window.innerHeight) + 'px',left:0};
// popper.value.remove();
if (props.isCanHide?.value !== false) {
if (props.isCanHide !== false) {
innerVisible.value = false;
}
innnerPosition.value = tempPosition.value;
innnerPosition.value = props.position;
};
// 事件绑定
on(props.el, triggerArr[0], doShow);
on(triggerArr[1] ?? props.el, triggerArr[2], doHidden);
checkTarget.value = triggerArr[3];
// 计算位置显示
const showPosistion = function () {
postionFns[tempPosition.value] &&
(style.value = postionFns[tempPosition.value](
postionFns[props.position] &&
(style.value = postionFns[props.position](
props.el,
popper.value,
innnerPosition
@@ -126,6 +128,12 @@ const invokeShowPosistion = function () {
setTimeout(() => innerVisible.value && showPosistion(), 2);
}
};
// 事件绑定
on(props.el, triggerArr[0], doShow);
on(triggerArr[1] ?? props.el, triggerArr[2], doHidden);
checkTarget.value = triggerArr[3];
onMounted(() => {
invokeShowPosistion();
});

View File

@@ -1,47 +0,0 @@
import { h, ref, render, watchEffect, watch } from "vue";
import popper from "./index.vue";
import { once } from "../../tools/domUtil";
const EVENT_MAP: any = {
hover: "mouseenter",
click: "click",
};
const usePopper = {
createPopper(el: HTMLElement, props: any, trigger: string) {
const _this = this;
once(el, EVENT_MAP[trigger], () => {
// TODO 临时解决方案
const _props: any = { el };
for (const key in props) {
_props[key] = ref(props[key]);
}
_props.updateVisible = function (val: boolean) {
_props.visible && (_props.visible.value = val);
};
_this.renderPopper(_props);
watchEffect(() => {
for (const key in _props) {
if (key === "visible") {
continue;
}
_props[key].value = props[key];
}
});
watch(
() => props.visible,
(val: boolean) => {
_props.updateVisible(val);
}
);
});
},
renderPopper(props: any) {
const container: HTMLDivElement = document.createElement("div");
// container.setAttribute("class", "lay-div");
const node = h(popper, props);
render(node, container);
container.firstElementChild &&
document.body.appendChild(container.firstElementChild);
return node;
},
};
export default usePopper;

View File

@@ -0,0 +1,72 @@
.lay-skeleton{
.lay-skeleton-item {
height: 16px;
border-radius: 5px;
margin-bottom: 16px;
background: #eeeeee;
}
.lay-skeleton-type--p{
height: 16px;
border-radius: 5px;
margin-bottom: 16px;
background: #eeeeee;
}
.lay-skeleton-type--image{
width: 240px;
height: 240px;
background: #eeeeee;
display: flex;
justify-content: center;
align-items: center;
i{
font-size: 40px;
}
}
}
.lay-skeleton-animated {
.lay-skeleton-type--image{
width: 240px;
height: 240px !important;
display: flex;
justify-content: center;
align-items: center;
i{
font-size: 40px;
}
}
.lay-skeleton-item {
height: 16px;
border-radius: 5px;
margin-bottom: 16px;
background: #eeeeee;
background: linear-gradient(
90deg,#f2f2f2 25%,#ececec 37%,#f2f2f2 63%);
background-size: 400% 100%;
animation: lay-skeleton-loading 1.2s ease infinite;
}
}
.lay-skeleton-first {
width: 30%;
}
.lay-skeleton-last {
width: 62.8%;
}
@keyframes lay-skeleton-loading {
0% {
background-position: 100% 50%;
}
100% {
background-position: 0 50%;
}
}

View File

@@ -0,0 +1,9 @@
import type { App } from 'vue'
import Component from './index.vue'
import type { IDefineComponent } from '../type/index'
Component.install = (app: App) => {
app.component(Component.name || 'LaySkeleton', Component)
}
export default Component as IDefineComponent

View File

@@ -0,0 +1,34 @@
<template>
<div :class="['lay-skeleton', animated ? 'lay-skeleton-animated': '',]" v-bind="$attrs">
<template v-if="loading">
<slot name="skeleton">
<lay-skeleton-item
v-for="item in rows"
:class="[
item===1? 'lay-skeleton-first': '',
item === rows? 'lay-skeleton-last': '']"
type="p"
></lay-skeleton-item>
</slot>
</template>
<slot v-else></slot>
</div>
</template>
<script setup name="LaySkeleton" lang="ts">
import LaySkeletonItem from '../skeletonItem/index.vue'
import './index.less'
import { defineProps, withDefaults} from "vue";
export interface LaySkeletonProps {
rows?: number;
loading?: boolean;
animated?: boolean;
}
const props = withDefaults(defineProps<LaySkeletonProps>(), {
rows: 4,
loading: false,
animated: false,
});
</script>-

View File

@@ -0,0 +1,9 @@
import type { App } from 'vue'
import Component from './index.vue'
import type { IDefineComponent } from '../type/index'
Component.install = (app: App) => {
app.component(Component.name || 'LaySkeletonItem', Component)
}
export default Component as IDefineComponent

View File

@@ -0,0 +1,19 @@
<template>
<div :class="['lay-skeleton-item',`lay-skeleton-type--${type}`]" v-bind="$attrs">
<div v-if="type==='image'" >
<lay-icon type="layui-icon-picture"></lay-icon>
</div>
</div>
</template>
<script setup name="LaySkeletonItem" lang="ts">
import { defineProps, withDefaults} from "vue";
export interface LaySkeletonProps {
type?: string;
}
const props = withDefaults(defineProps<LaySkeletonProps>(), {
type: 'p',
});
</script>-

View File

@@ -98,4 +98,11 @@
height: 100%;
position: relative;
cursor: pointer;
}
}
.layui-slider-vrange {
width: 100%;
height: 100%;
position: relative;
cursor: pointer;
}

View File

@@ -1,6 +1,11 @@
<template>
<div class="layui-slider-vertical" v-if="vertical">
<div v-if="range">range vertical slider</div>
<div ref="rangetracker2" class="layui-slider-vrange" v-if="range">
<div class="layui-slider-vertical-btn"></div>
<div class="layui-slider-vertical-btn"></div>
<div class="layui-slider-vertical-line"></div>
<div class="layui-slider-vertical-rate"></div>
</div>
<div
ref="verticaltracker"
@@ -26,26 +31,34 @@
</div>
<div class="layui-slider-v" v-else>
<div @mousedown.stop="handle_mousedown" ref="rangetracker1" class="layui-slider-srange" v-if="range">
<div
@mousedown.stop="handle_mousedown"
ref="rangetracker1"
class="layui-slider-srange"
v-if="range"
>
<lay-tooltip :content="rangeValue[0] + ''">
<div :style="{ left: rangeValue[0] + '%' }" class="layui-slider-btn-v"></div>
<div
:style="{ left: rangeValue[0] + '%' }"
class="layui-slider-btn-v"
></div>
</lay-tooltip>
<lay-tooltip :content="rangeValue[1]">
<div :style="{ left: rangeValue[1] + '%' }" class="layui-slider-btn-v"></div>
<div
:style="{ left: rangeValue[1] + '%' }"
class="layui-slider-btn-v"
></div>
</lay-tooltip>
<div class="layui-slider-line-v"></div>
<div :style="{ width:rangeValue[1]-rangeValue[0] +'%', left: rangeValue[0]+'%' }" class="layui-slider-rate-v"></div>
<div
:style="{
width: rangeValue[1] - rangeValue[0] + '%',
left: rangeValue[0] + '%',
}"
class="layui-slider-rate-v"
></div>
</div>
<div
ref="standardtracker"
@@ -71,7 +84,7 @@
</div>
</template>
<script setup lang="ts">
import { defineProps, onMounted, reactive, Ref, ref } from "vue";
import { defineProps, reactive, Ref, ref } from "vue";
import { on, off } from "evtd";
import "./index.less";
@@ -93,13 +106,23 @@ const props = withDefaults(defineProps<LaySliderProps>(), {
disabled: false,
});
let rangeValue:Ref<number[]> = ref([0,0])
if(Array.isArray(props.modelValue)){
rangeValue.value = props.modelValue
let rangeValue: Ref<number[]> = ref([0, 0]);
if (Array.isArray(props.modelValue)) {
// eslint-disable-next-line vue/no-setup-props-destructure
rangeValue.value = props.modelValue;
}
let verticalRangeValue: Ref<number[]> = ref([0, 0]);
if (Array.isArray(props.modelValue)) {
// eslint-disable-next-line vue/no-setup-props-destructure
verticalRangeValue.value = props.modelValue;
}
const standardtracker = ref<HTMLElement | null>(null);
const verticaltracker = ref<HTMLElement | null>(null);
const rangetracker1 = ref<HTMLElement | null>(null);
const rangetracker2 = ref<HTMLElement | null>(null);
const standard_style = reactive({
left: props.modelValue,
width: props.modelValue,
@@ -131,6 +154,10 @@ function handle_mousemove(e: MouseEvent) {
if (props.vertical === false && props.range === true) {
starndardRangeMove(e);
}
if (props.vertical === true && props.range === true) {
verticalRangeMove(e);
}
}
function handle_mouseup() {
@@ -188,7 +215,7 @@ const verticalMove = (e: MouseEvent) => {
emit("update:modelValue", vertical_style.bottom);
};
const starndardRangeMove =(e:MouseEvent)=>{
const starndardRangeMove = (e: MouseEvent) => {
if (!rangetracker1.value) {
return;
}
@@ -196,31 +223,32 @@ const starndardRangeMove =(e:MouseEvent)=>{
let origin_left = tracker_rect.left;
let point_left = e.clientX;
let distance = point_left - origin_left;
if(distance < 0){
rangeValue.value[0] = 0
if (distance < 0) {
rangeValue.value[0] = 0;
} else {
let rate = (distance / tracker_rect.width) * 100;
let idx = moveNeighbor(Math.floor(rate))
rangeValue.value[idx] = Math.floor(rate)
if(rangeValue.value[1] > 100) {
rangeValue.value[1] = 100
let idx = moveNeighbor(Math.floor(rate));
rangeValue.value[idx] = Math.floor(rate);
if (rangeValue.value[1] > 100) {
rangeValue.value[1] = 100;
}
if(rangeValue.value[0] < 0) {
rangeValue.value[0] = 0
if (rangeValue.value[0] < 0) {
rangeValue.value[0] = 0;
}
}
emit("update:modelValue", rangeValue.value);
}
};
const verticalRangeMove = (e: MouseEvent) => {};
function moveNeighbor(rate: number) {
let d1 = Math.abs(rate - rangeValue.value[0]);
let d2 = Math.abs(rate - rangeValue.value[1]);
if(d1 > d2){
return 1
if (d1 > d2) {
return 1;
} else {
return 0
return 0;
}
}
</script>

View File

@@ -1,8 +1,15 @@
<template>
<slot></slot>
<lay-popper v-if="isMounted" v-bind="innerProps"></lay-popper>
</template>
<script lang="ts">
import usePopper from "../popper/usePopper";
import { defineComponent } from "vue";
import LayPopper from "../popper/index.vue";
import { defineComponent, ref } from "vue";
export default defineComponent({
name: "LayTooltip",
components: {
LayPopper
},
props: {
content: {
type: [Number, String],
@@ -22,21 +29,26 @@ export default defineComponent({
},
visible: {
type: Boolean,
default: true,
default: false,
},
isCanHide: {
type: Boolean,
default: true,
},
},
setup(){
const isMounted = ref(false);
return {
isMounted
}
},
computed: {
innerProps(){
return {el: this.$el.nextElementSibling, ...this.$props};
}
},
mounted() {
const _this = this;
this.$nextTick(() => {
usePopper.createPopper(_this.$el, _this.$props, "hover");
});
},
render() {
return this.$slots.default && this.$slots.default()[0];
},
this.$nextTick(() => this.isMounted = true);
}
});
</script>