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

This commit is contained in:
就眠儀式 2022-12-07 21:16:24 +08:00
commit 70b35e8dca
10 changed files with 104 additions and 44 deletions

View File

@ -31,7 +31,9 @@
:size="size" :size="size"
@clear="onClear" @clear="onClear"
></lay-input> ></lay-input>
<slot v-else></slot> <div class="slot-area" v-else>
<slot></slot>
</div>
<template #content> <template #content>
<div class="layui-cascader-panel"> <div class="layui-cascader-panel">
@ -137,12 +139,19 @@ watch(
watch( watch(
() => props.modelValue, () => props.modelValue,
() => { () => {
if (props.modelValue === null || props.modelValue === "") { if (watchModelValue.value) {
onClear(); if (props.modelValue === null || props.modelValue === "") {
onClear();
} else {
updateDisplayByModelValue();
}
setTimeout(() => {
watchModelValue.value = true;
}, 0);
} }
} }
); );
const watchModelValue = ref(true);
const treeData = ref<any>([]); const treeData = ref<any>([]);
const initTreeData = () => { const initTreeData = () => {
let treeLvNum = getMaxFloor(props.options); let treeLvNum = getMaxFloor(props.options);
@ -159,31 +168,28 @@ const initTreeData = () => {
}; };
} }
} }
updateDisplayByModelValue();
};
function updateDisplayByModelValue() {
if (props.modelValue) { if (props.modelValue) {
try { try {
let valueData = props.modelValue.split(props.decollator); let valueData = props.modelValue.split(props.decollator);
let data: any[] = []; for (let index = 0; index < valueData.length; index++) {
for (let index = 0; index < treeData.value.length; index++) { const element = valueData[index];
const element = treeData.value[index]; let selectIndex = treeData.value[index].data.findIndex(
const nowValue = valueData[index]; (e: { value: string }) => e.value === element
for (let i = 0; i < element.length; i++) { );
const ele = element[i]; if (selectIndex == -1) {
if (nowValue === ele.value) { break;
data.push(ele);
element.selectIndex = i;
}
} }
selectBar(treeData.value[index].data[selectIndex], selectIndex, index);
} }
displayValue.value = data
.map((e) => {
return e.label;
})
.join(` ${props.decollator} `);
} catch (error) { } catch (error) {
console.error(error); console.error(error);
} }
} }
}; }
function getMaxFloor(treeData: any) { function getMaxFloor(treeData: any) {
//let floor = 0; //let floor = 0;
@ -274,6 +280,7 @@ const selectBar = (item: any, selectIndex: number, parentIndex: number) => {
return e.value; return e.value;
}) })
.join(props.decollator); .join(props.decollator);
watchModelValue.value = false;
emit("update:modelValue", value); emit("update:modelValue", value);
let evt = { let evt = {
display: displayValue.value, display: displayValue.value,

View File

@ -1,7 +1,7 @@
<template> <template>
<div class="lay-page-header"> <div class="lay-page-header">
<div class="lay-page-header__left" @click="emits('back')"> <div class="lay-page-header__left" @click="emits('back')">
<slot name="backIcon" <slot :name="backIconSlotName"
><i class="layui-icon" :class="[backIcon]"></i ><i class="layui-icon" :class="[backIcon]"></i
></slot> ></slot>
<div class="lay-page-header__title">{{ backText }}</div> <div class="lay-page-header__title">{{ backText }}</div>
@ -19,7 +19,8 @@ export default {
}; };
</script> </script>
<script lang="ts" setup> <script lang="ts" setup>
import { useSlots } from "vue"; import { convertSlotName } from "../../utils";
import { getCurrentInstance, useSlots } from "vue";
import "./index.less"; import "./index.less";
export interface PageHeaderProps { export interface PageHeaderProps {
@ -34,6 +35,7 @@ const props = withDefaults(defineProps<PageHeaderProps>(), {
}); });
const emits = defineEmits(["back"]); const emits = defineEmits(["back"]);
const slots = useSlots(); const slots = useSlots();
const instance = getCurrentInstance()!;
const backIconSlotName = convertSlotName(instance, "backIcon");
</script> </script>

View File

@ -206,16 +206,21 @@ provide("searchMethod", props.searchMethod);
<lay-tag-input <lay-tag-input
v-if="multiple" v-if="multiple"
v-model="multipleValue" v-model="multipleValue"
v-model:input-value="searchValue"
:allow-clear="allowClear" :allow-clear="allowClear"
:placeholder="placeholder" :placeholder="placeholder"
:collapseTagsTooltip="collapseTagsTooltip" :collapseTagsTooltip="collapseTagsTooltip"
:minCollapsedNum="minCollapsedNum" :minCollapsedNum="minCollapsedNum"
:disabledInput="true"
:disabled="disabled" :disabled="disabled"
:disabledInput="!showSearch"
:size="size" :size="size"
:class="{ 'layui-unselect': true }" :class="{ 'layui-unselect': true }"
@remove="handleRemove" @remove="handleRemove"
@clear="handleClear" @clear="handleClear"
@input-value-change="handleSearch"
@keyup.delete.capture.prevent.stop
@keyup.backspace.capture.prevent.stop
@keydown.enter.capture.prevent.stop
> >
<template #suffix> <template #suffix>
<lay-icon <lay-icon
@ -247,17 +252,6 @@ provide("searchMethod", props.searchMethod);
</lay-input> </lay-input>
<template #content> <template #content>
<dl class="layui-select-content"> <dl class="layui-select-content">
<div class="layui-select-search" v-if="multiple && showSearch">
<lay-input
size="sm"
prefix-icon="layui-icon-search"
:modelValue="searchValue"
:placeholder="searchPlaceholder"
@compositionstart="onCompositionstart"
@compositionend="onCompositionend"
@input="handleSearch"
></lay-input>
</div>
<template v-if="items"> <template v-if="items">
<lay-select-option <lay-select-option
v-for="(item, index) in items" v-for="(item, index) in items"

View File

@ -265,6 +265,19 @@ const update = () => {
} }
}; };
const horizontalScroll = (e: WheelEvent) => {
e.preventDefault()
const navSize = getNavSize();
const containerSize = navRef.value![`offset${sizeName.value}`];
const currentOffset = navOffset.value;
const scrollNextSize = scrollNextRef.value?.[`offset${sizeName.value}`] ?? 0;
const direction = Math.abs(e.deltaX) >= Math.abs(e.deltaY) ? e.deltaX : e.deltaY
const distance = 50 * (direction > 0 ? 1 : -1)
const newOffset = Math.max(currentOffset + distance, 0)
if ((navSize - currentOffset <= containerSize - scrollNextSize && direction > 0)) return;
navOffset.value = newOffset
}
const renderTabIcon = (attrs: Record<string, unknown>) => { const renderTabIcon = (attrs: Record<string, unknown>) => {
const tab = attrs.tabData as TabData; const tab = attrs.tabData as TabData;
if (typeof tab.icon === "function") { if (typeof tab.icon === "function") {
@ -335,6 +348,7 @@ provide("active", active);
> >
<ul <ul
ref="navRef" ref="navRef"
@wheel="horizontalScroll"
:class="[ :class="[
'layui-tab-title', 'layui-tab-title',
props.tabPosition ? `is-${tabPosition}` : '', props.tabPosition ? `is-${tabPosition}` : '',

View File

@ -300,12 +300,11 @@ defineExpose({
</template> </template>
</LayToopTip> </LayToopTip>
</template> </template>
<template v-if="!disabledInput">
<input <input
ref="inputRefEl" ref="inputRefEl"
class="layui-tag-input-inner-input" class="layui-tag-input-inner-input"
:style="inputStyle" :style="inputStyle"
:disabled="disabled" :disabled="(disabled || disabledInput)"
:placeholder="placeholder" :placeholder="placeholder"
:readonly="readonly" :readonly="readonly"
@keydown.enter="handleEnter" @keydown.enter="handleEnter"
@ -317,7 +316,6 @@ defineExpose({
@compositionupdate="handleComposition" @compositionupdate="handleComposition"
@compositionend="handleComposition" @compositionend="handleComposition"
/> />
</template>
</span> </span>
<span <span
v-if="allowClear && tagData?.length && !disabled" v-if="allowClear && tagData?.length && !disabled"

View File

@ -18,6 +18,8 @@
padding: 6px 10px; padding: 6px 10px;
resize: vertical; resize: vertical;
position: relative; position: relative;
transition: none;
-webkit-transition: none;
} }
.layui-textarea-wrapper { .layui-textarea-wrapper {

View File

@ -6,7 +6,8 @@ export default {
<script setup lang="ts"> <script setup lang="ts">
import { LayIcon } from "@layui/icons-vue"; import { LayIcon } from "@layui/icons-vue";
import { computed, ref } from "vue"; import { computed, ref, watch } from "vue";
import {isObject } from "@vueuse/shared"
import "./index.less"; import "./index.less";
export interface TextareaProps { export interface TextareaProps {
@ -17,6 +18,7 @@ export interface TextareaProps {
showCount?: boolean; showCount?: boolean;
allowClear?: boolean; allowClear?: boolean;
maxlength?: number; maxlength?: number;
autosize?: boolean | { minHeight: number, maxHeight: number };
} }
const props = defineProps<TextareaProps>(); const props = defineProps<TextareaProps>();
@ -31,6 +33,7 @@ interface TextareaEmits {
} }
const emit = defineEmits<TextareaEmits>(); const emit = defineEmits<TextareaEmits>();
const textareaRef = ref<HTMLTextAreaElement | null>(null);
const composing = ref(false); const composing = ref(false);
const onInput = function (event: Event) { const onInput = function (event: Event) {
@ -78,11 +81,25 @@ const wordCount = computed(() => {
} }
return count; return count;
}); });
watch([() => props.modelValue, textareaRef], () => {
if (!textareaRef.value || !props.autosize) return;
const height: number = textareaRef.value?.scrollHeight + 2; //
if (isObject(props.autosize)) {
const { minHeight, maxHeight } = props.autosize;
if (height < minHeight || height > maxHeight) return;
}
textareaRef.value!.style.height = '1px'
textareaRef.value!.style.height = `${textareaRef.value?.scrollHeight + 2}px`
}, {
immediate: true,
})
</script> </script>
<template> <template>
<div class="layui-textarea-wrapper"> <div class="layui-textarea-wrapper">
<textarea <textarea
ref="textareaRef"
class="layui-textarea" class="layui-textarea"
:value="modelValue" :value="modelValue"
:placeholder="placeholder" :placeholder="placeholder"

View File

@ -1,4 +1,4 @@
import { Component, VNode, VNodeTypes } from "vue"; import { Component, ComponentInternalInstance, VNode, VNodeTypes } from "vue";
export enum ShapeFlags { export enum ShapeFlags {
ELEMENT = 1, ELEMENT = 1,
@ -31,3 +31,27 @@ export const isArrayChildren = (
): children is VNode[] => { ): children is VNode[] => {
return Boolean(vn && vn.shapeFlag & ShapeFlags.ARRAY_CHILDREN); return Boolean(vn && vn.shapeFlag & ShapeFlags.ARRAY_CHILDREN);
}; };
/**
* back-icon backIcon
* @param vm
* @param name
*/
export function convertSlotName(vm: ComponentInternalInstance, name: string) {
const camelCaseName = camelCase(name);
const kebabCaseName = kebabCase(name);
return vm.slots[camelCaseName]
? camelCaseName
: vm.slots[kebabCaseName]
? kebabCaseName
: name
}
export function camelCase(str: string) {
return str.replace(/-(\w)/g, (_, c) => (c ? c.toUpperCase() : ''))
}
export function kebabCase(key: string) {
const result = key.replace(/([A-Z])/g, ' $1').trim();
return result.split(' ').join('-').toLowerCase()
}

View File

@ -336,9 +336,11 @@ const valueLv=ref(null)
::: :::
::: demo 使用 `默认插槽` 可以自定义回显区域的内容,并且你可以通过change回调轻松拿到回显的值同时你也可以使用`动态插槽名`来自定义你想要展示的内容,只需要在传入的数据中加入 `slot`参数,然后愉快的使用插槽自定义内容 ::: demo 使用 `默认插槽` 可以自定义回显区域的内容,并且你可以通过change回调轻松拿到回显的值同时你也可以使用`动态插槽名`来自定义你想要展示的内容,只需要在传入的数据中加入 `slot`参数,然后愉快的使用插槽自定义内容
<template> <template>
<lay-cascader :options="options" v-model="value2" @change="onChange"> <lay-cascader :options="options" v-model="value2" @change="onChange" style="width:350px;">
<lay-button type="normal">Click me ❤️</lay-button> <div style='display:flex;align-items:center'>
<lay-badge theme="orange" v-if="displayValue" style="margin-left:10px">{{displayValue}}</lay-badge> <lay-button type="normal">Click me ❤️</lay-button>
<lay-badge theme="orange" v-if="displayValue" style="margin-left:10px">{{displayValue}}</lay-badge>
</div>
</lay-cascader> </lay-cascader>
<lay-cascader :options="options2" v-model="value" placeholder="动态插槽案例" style="width:250px;margin-left:20px"> <lay-cascader :options="options2" v-model="value" placeholder="动态插槽案例" style="width:250px;margin-left:20px">
<template #Guide>🤨😐😑😶😏😒🙄😬🤥😌</template> <template #Guide>🤨😐😑😶😏😒🙄😬🤥😌</template>

View File

@ -337,7 +337,7 @@ watch(
props.success(); props.success();
} }
}, },
{ immediate: true } { immediate: true, flush: "post" }
); );
watch( watch(