Merge branch 'v1.7.0' of https://gitee.com/layui/layui-vue into v1.7.0
This commit is contained in:
commit
70b35e8dca
@ -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,
|
||||||
|
@ -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>
|
||||||
|
@ -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"
|
||||||
|
@ -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}` : '',
|
||||||
|
@ -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"
|
||||||
|
@ -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 {
|
||||||
|
@ -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"
|
||||||
|
@ -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()
|
||||||
|
}
|
||||||
|
@ -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>
|
||||||
|
@ -337,7 +337,7 @@ watch(
|
|||||||
props.success();
|
props.success();
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{ immediate: true }
|
{ immediate: true, flush: "post" }
|
||||||
);
|
);
|
||||||
|
|
||||||
watch(
|
watch(
|
||||||
|
Loading…
Reference in New Issue
Block a user