1.骨架屏组件封装

This commit is contained in:
dingyongya 2021-12-29 15:59:15 +08:00
commit 62f5bb6c18
188 changed files with 3550 additions and 4962 deletions

53
.eslintrc.js Normal file
View File

@ -0,0 +1,53 @@
module.exports = {
root: true,
env: {
browser: true,
node: true,
es6: true
},
parser: 'vue-eslint-parser',
parserOptions: {
parser: '@typescript-eslint/parser',
ecmaVersion: 2020,
sourceType: 'module',
jsxPragma: 'React',
ecmaFeatures: {
jsx: true
}
},
globals: {
AMap: false,
AMapUI: false
},
extends: [
'plugin:vue/vue3-recommended',
'plugin:@typescript-eslint/recommended',
'prettier',
'plugin:prettier/recommended'
],
rules: {
'@typescript-eslint/ban-ts-ignore': 'off',
'@typescript-eslint/explicit-function-return-type': 'off',
'@typescript-eslint/no-explicit-any': 'off',
'@typescript-eslint/no-var-requires': 'off',
'@typescript-eslint/no-empty-function': 'off',
'vue/custom-event-name-casing': 'off',
'no-use-before-define': 'off',
'@typescript-eslint/no-use-before-define': 'off',
'@typescript-eslint/ban-ts-comment': 'off',
'@typescript-eslint/ban-types': 'off',
'@typescript-eslint/no-non-null-assertion': 'off',
'@typescript-eslint/explicit-module-boundary-types': 'off',
'space-before-function-paren': 'off',
'vue/name-property-casing': ['error', 'PascalCase'],
'vue/attributes-order': 'off',
'vue/one-component-per-file': 'off',
'vue/html-closing-bracket-newline': 'off',
'vue/max-attributes-per-line': 'off',
'vue/multiline-html-element-content-newline': 'off',
'vue/singleline-html-element-content-newline': 'off',
'vue/attribute-hyphenation': 'off',
'vue/require-default-prop': 'off',
'vue/script-setup-uses-vars': 'off'
}
}

6
.prettierrc Normal file
View File

@ -0,0 +1,6 @@
{
"semi": true,
"singleQuote": false,
"tabWidth": 2
}

View File

@ -17,4 +17,4 @@ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

View File

@ -1,12 +1,17 @@
<p align="center"> <p align="center">
<a href="http://www.layui.com"> <a href="http://www.layui.com">
<img src="https://sentsin.gitee.io/res/images/layui/layui.png" alt="layui" width="360"> <img src="https://images.gitee.com/uploads/images/2021/1213/173638_d6911839_4835367.png" alt="layui" width="360">
</a> </a>
</p> </p>
<p align="center"> <p align="center">
A component library for Vue 3 base on layui A component library for Vue 3 base on layui
</p> </p>
<p align="center">
基 于 vue 3.0 的 组 件 库 桌 面 级 , layui 的 另 一 种 呈 现 方 式
</p>
<p align="center"> <p align="center">
<a href="https://www.npmjs.com/package/@layui/layui-vue"><img src="https://img.shields.io/npm/v/@layui/layui-vue.svg?sanitize=true" alt="Version"></a> <a href="https://www.npmjs.com/package/@layui/layui-vue"><img src="https://img.shields.io/npm/v/@layui/layui-vue.svg?sanitize=true" alt="Version"></a>
<a href="https://www.npmjs.com/package/layui"><img src="https://img.shields.io/npm/l/layui.svg?sanitize=true" alt="License"></a> <a href="https://www.npmjs.com/package/layui"><img src="https://img.shields.io/npm/l/layui.svg?sanitize=true" alt="License"></a>
@ -23,11 +28,9 @@
--- ---
Vue 3.0 的 桌 面 端 组 件 库 , Layui 的 另 一 种 呈 现 方 式 ### 快速上手 : Beta
## 快速上手 : Beta 获得 layui-vue 你只需要使用 npm 引入 :
获得 layui-vue 你只需要使用 npm 引入下述文件:
``` ```
npm install @layui/layui-vue npm install @layui/layui-vue
@ -44,7 +47,7 @@ import App from './App.vue'
createApp(App).use(Layui).mount('#app') createApp(App).use(Layui).mount('#app')
``` ```
这是一个基本的入门页面: 接下来,你就可以使用 layui-vue 来开发你的应用 :
```html ```html
<template> <template>
@ -63,13 +66,13 @@ createApp(App).use(Layui).mount('#app')
## [阅读文档](http://layui-vue.pearadmin.com/) ## [阅读文档](http://layui-vue.pearadmin.com/)
从现在开始,尽情地拥抱 layui 吧!但愿她能成为你长远的开发伴侣,化作你方寸屏幕前的亿万字节! 从现在开始,用另一种方式拥抱 layui, 继续化作你方寸屏幕前的亿万字节!
## 使用趋势 ### 使用趋势
[![Giteye chart](https://chart.giteye.net/gitee/layui-vue/layui-vue/5ZQ67WWS.png)](https://giteye.net/chart/5ZQ67WWS) [![Giteye chart](https://images.gitee.com/uploads/images/2021/1222/233241_e13a5449_4835367.png)](https://giteye.net/chart/5ZQ67WWS)
## 贡献者 ### 贡献代码
> 大概是因为 layui - vue 让开发者变得更懒,所以贡献者才这么少? > 大概是因为 layui - vue 让开发者变得更懒,所以贡献者才这么少?
> 好的,姑且就这样认为吧。 > 好的,姑且就这样认为吧。
@ -82,4 +85,4 @@ createApp(App).use(Layui).mount('#app')
## 相关 ## 相关
[官网](http://layui-vue.pearadmin.com/)、[更新日志](http://layui-vue.pearadmin.com/zh-CN/guide/changelog)、[交流群](https://jq.qq.com/?_wv=1027&k=ffiUQgnE) [官网](http://layui-vue.pearadmin.com/)、[更新日志](http://layui-vue.pearadmin.com/zh-CN/guide/changelog)、[交流群](https://jq.qq.com/?_wv=1027&k=ffiUQgnE)

View File

@ -1,12 +1,11 @@
::: title 基础使用 ::: title 基础使用
###### 回到顶部组件的默认样式,通过滑动来查看页面右下角的正方形按钮。 ###### 回到顶部组件的默认样式,<code>lay-backtop</code> 会自动寻找最近的可滚动祖先元素,也可以使用 `target` 属性指定触发滚动事件的元素,通过滑动来查看页面右下角的正方形按钮。
::: :::
::: demo ::: demo
<template> <template>
<lay-backtop target=".layui-body"></lay-backtop> <lay-backtop></lay-backtop>
</template> </template>
::: :::
@ -17,18 +16,17 @@
::: demo ::: demo
<!-- 使用默认插槽自定义组件内容,也可以使用组件提供的样式属性快速定义常用样式,样式属性能满足大多数场景,lay-backtop组件可搭配 lay-tooltip 组件使用--> <!-- 使用默认插槽自定义组件内容,也可以使用组件提供的样式属性快速定义常用样式,样式属性能满足大多数场景lay-backtop组件可搭配 lay-tooltip 组件使用-->
<template> <template>
<!-- 使用默认插槽自定义 --> <!-- 使用默认插槽自定义 -->
<lay-tooltip content="插槽自定义 backtop " position="left"> <lay-tooltip content="插槽自定义 backtop " position="left">
<lay-backtop target=".layui-body" :showHeight="0" :bottom="160" bgcolor="#5FB878" circular disabled> <lay-backtop @click="handlerClick" :showHeight="0" :bottom="160" bgcolor="#5FB878" circle disabled>
<lay-icon type="layui-icon-dialogue" size="30px"></lay-icon> <lay-icon type="layui-icon-dialogue" size="30px"></lay-icon>
</lay-backtop> </lay-backtop>
</lay-tooltip> </lay-tooltip>
<!-- 使用样式属性自定义 --> <!-- 使用样式属性自定义 -->
<lay-tooltip content="属性自定义 backtop " position="left"> <lay-tooltip content="属性自定义 backtop " position="left">
<lay-backtop target=".layui-body" :bottom="100" bgcolor="#5FB878" icon="layui-icon-up" circular> <lay-backtop :bottom="100" bgcolor="#5FB878" icon="layui-icon-up" circle></lay-backtop>
</lay-backtop>
</lay-tooltip> </lay-tooltip>
</template> </template>
@ -51,20 +49,18 @@ export default {
::: :::
::: title 滚动容器 ::: title 挂载容器
###### 通过设置 <code>target</code><code>position="absolute"</code>参数 ,可对特定容器进行返回顶部操作 ###### 通过设置 <code>target</code><code>position="absolute"</code>参数,可对特定容器进行返回顶部操作
::: :::
::: demo ::: demo
<template> <template>
<!-- 需要用一个 div 包裹触发滚动事件的目标元素和 lay-backtop 组件 --> <!-- 需要用一个 div 包裹触发滚动事件的目标元素和 lay-backtop 组件 -->
<div class="scrollContainer" style="width:700px; height:300px;"> <div class="wrapper" style="width:700px; height:300px;">
<div id="scrollContent" style="overflow-y:auto; overflow-x:auto; width:700px; height:300px;"> <div id="scrollContent" style="overflow-y:auto; overflow-x:auto; width:700px; height:300px;background-color:whitesmoke;padding:10px;">
<p v-for="(n,index) in 50" :key="n" style="height:32px;border-bottom:0.5px solid #5FB878;margin-bottom:10px;line-height:35px"> <lay-panel v-for="(n,index) in 50" :key="n" style="margin-top:5px;margin-bottom:5px;padding:10px;">内容</lay-panel>
{{index + ". &nbsp;layui-vue , 基 于 vue 3.0 的 桌 面 端 组 件 库 , layui 的 另 一 种 呈 现 方 式"}}
</p>
</div> </div>
<lay-backtop target="#scrollContent" :showHeight="100" :bottom="30" position="absolute"></lay-backtop> <lay-backtop target="#scrollContent" :showHeight="100" :bottom="30" position="absolute"></lay-backtop>
</div> </div>
@ -80,26 +76,23 @@ export default {
::: table ::: table
| 属性 | 说明 | 类型 | 可选值 | | 属性 | 描述 | 类型 | 默认值 | 可选值 |
| ------------------------- | --------------------------------------------- | ------- | -------------------------- | | ------------ | --------------------------------------------- | ---------------- | ---------------- | ------------------------------ |
| <strong>通用属性</strong> | | target | 可选,触发滚动事件的对象 | string \| Window | `window` | `CSS/Element 选择器` `window` |
| target | 可选,触发滚动事件的对象 | string | CSS 选择器 \| window(默认) | | showHeight | 可选,滚动高度达到该值后<br>显示回到顶部按钮 | number | `200` | — |
| showHeight | 可选,滚动高度达到该值后显示回到顶部按钮 | number | 200(默认) | | disabled | 可选,禁用点击返回顶部 | boolean | `false` | `true` `false` |
| disabled | 可选,禁用点击返回顶部 | boolean | true \| false(默认) | | position | 可选,定位方式,特定容器内部<br>需设置为 absolute | string | `fixed` | `absolute` `fixed` |
| <strong>组件样式</strong> | | right | 可选,按钮距离页面右边距 | number | `30` | — |
| position | 可选,定位方式,特定容器内部需设置为 absolute | string | absolute \| fixed(默认) | | size | 可选,按钮大小 | string | —— | `medium` `small` |
| right | 可选,按钮距离页面右边距,单位 px | number | 30(默认) | | bottom | 可选,按钮距离页面底部位置 | number | `40` | `40` |
| bottom | 可选,按钮距离页面底部位置,单位 px | number | 40(默认) | | bgcolor | 可选,背景颜色 | string | `#9F9F9F` | — |
| bgcolor | 可选,背景颜色 | string | \#9F9F9F(默认) | | opacity | 可选,不透明度 | number | `0.95` | `0-1` |
| opacity | 可选,不透明度 | number | 0.0-1.0 | | color | 可选,前景颜色 | string | `#FFFFFF` | — |
| color | 可选,前景颜色 | string | #FFFFFF | | borderRadius | 可选,添加圆角 | number \| string | `2` | e: 2 \| 2px \|50% |
| borderRadius | 可选,添加圆角 | string | 2px(默认) | | circle | 可选,使用圆形按钮 | boolean | `false` | `true` `false` |
| circular | 可选, 使用圆形按钮 | boolean | true \| false(默认) | icon | 可选,图标类型 | string | `layui-icon-top` | `lay-icon`组件支持的所有类型 |
| <strong>图标样式</strong> | | iconSize | 可选,图标大小 | number | `30` | `30` |
| icon | 可选,图标类型 | string | layui-icon-top(默认) | | iconColor | 可选,图标颜色 | string | `#FFFFFF` | — |
| iconSize | 可选,图标大小 | number | 30 |
| iconPrefix | 可选,图标前缀 layui-icon layui-icon-top | string | layui-icon(默认) |
| iconColor | 可选,图标颜色 | string | #FFFFFF(默认) |
::: :::
@ -108,7 +101,7 @@ export default {
::: table ::: table
| 事件名 | 说明 | 回调参数 | | 事件 | 描述 | 参数 |
| ------ | -------------------------- | -------- | | ------ | -------------------------- | -------- |
| click | 点击回到顶部按钮的回调函数 | event | | click | 点击回到顶部按钮的回调函数 | event |
@ -119,8 +112,8 @@ export default {
::: table ::: table
| 插槽 | 说明 | | 插槽 | 说明 |
| ------ | ---------- | | ------ | ---------- |
| | 自定义内容 | | default| 自定义内容 |
::: :::

View File

@ -1,7 +1,7 @@
::: title 基础使用 ::: title 基础使用
::: :::
::: demo 使用 lay-button 标签, 创建一个按钮 ::: demo 使用 `lay-button` 标签, 创建一个按钮
<template> <template>
<lay-button type="primary">原始按钮</lay-button> <lay-button type="primary">原始按钮</lay-button>
@ -29,7 +29,7 @@ export default {
::: title 简约按钮 ::: title 简约按钮
::: :::
::: demo 使用 border 属性设置 主要按钮 边框主题 ::: demo 使用 `border` 属性设置边框主题
<template> <template>
<lay-button type="primary">原始按钮</lay-button> <lay-button type="primary">原始按钮</lay-button>
@ -57,7 +57,7 @@ export default {
::: title 按钮尺寸 ::: title 按钮尺寸
::: :::
::: demo 传入 size 属性, 创建指定尺寸的按钮, 可选值 `lg` `sm` `xs` ::: demo 传入 `size` 属性, 创建指定尺寸的按钮, 可选值 `lg` `sm` `xs`
<template> <template>
<lay-button type="primary" size="lg">原始按钮</lay-button> <lay-button type="primary" size="lg">原始按钮</lay-button>
@ -85,7 +85,7 @@ export default {
::: title 流式按钮 ::: title 流式按钮
::: :::
::: demo 传入 fluid 属性, 创建最大化按钮 ::: demo 传入 `size` 属性, 创建最大化按钮
<template> <template>
<lay-button type="primary" fluid>最大化按钮</lay-button> <lay-button type="primary" fluid>最大化按钮</lay-button>
@ -111,7 +111,7 @@ export default {
::: title 圆角按钮 ::: title 圆角按钮
::: :::
::: demo 传入 radius 属性,创建圆角按钮 ::: demo 传入 `radius` 属性,创建圆角按钮
<template> <template>
<lay-button type="primary" radius>原始按钮</lay-button> <lay-button type="primary" radius>原始按钮</lay-button>
@ -139,7 +139,7 @@ export default {
::: title 按钮分组 ::: title 按钮分组
::: :::
::: demo 使用 lay-button-group 标签, 创建一个按钮组 ::: demo 使用 `lay-button-group` 标签, 创建一个按钮组
<template> <template>
<div> <div>
@ -173,7 +173,7 @@ export default {
::: title 图标按钮 ::: title 图标按钮
::: :::
::: demo 结合 lay-icon 组件, 创建图标按钮 ::: demo 结合 `lay-icon` 组件, 创建图标按钮
<template> <template>
<lay-button-container> <lay-button-container>
@ -199,7 +199,7 @@ export default {
::: title 按钮容器 ::: title 按钮容器
::: :::
::: demo 使用 lay-button-container 标签, 创建一个按钮容器 ::: demo 使用 `lay-button-container` 标签, 创建一个按钮容器
<template> <template>
<lay-button-container> <lay-button-container>
@ -226,7 +226,7 @@ export default {
::: title 加载按钮 ::: title 加载按钮
::: :::
::: demo 传入 loading 属性, 控制按钮的加载状态 ::: demo 传入 `loading` 属性, 控制按钮的加载状态
<template> <template>
<lay-button-container> <lay-button-container>
@ -255,7 +255,7 @@ export default {
::: title 事件处理 ::: title 事件处理
::: :::
::: demo 使用 @click 设置单击回调 ::: demo 使用 `@click` 设置单击回调
<template> <template>
<lay-button type="default" @click="clickHandle">单击事件</lay-button> <lay-button type="default" @click="clickHandle">单击事件</lay-button>
@ -280,31 +280,57 @@ export default {
::: :::
::: title 按钮属性 ::: title Button 属性
::: :::
::: table ::: table
| 属性 | 描述 | 可选值 | | 属性 | 描述 | 类型 | 默认值 | 可选值 |
| ------ | ------ | --------------------------------------------- | | ----------- | -------- | ------- | --------- | ----------------------------------|
| type | 主题 | `primary` `normal` `warm` `danger` `disabled` | | type | 主题 | string | `primary` | `primary` `normal` `warm` `danger` |
| size | 尺寸 | `lg` `sm` `xs` | | size | 尺寸 | string | -- | `lg` `sm` `xs` |
| fluid | 最大化 | `true` `false` | | fluid | 最大化 | boolean | `false` | `true` `false` |
| radius | 圆角 | `true` `false` | | radius | 圆角 | boolean | `false` | `true` `false` |
| border | 边框 | `green` `blue` `orange` `red` `black` | | border | 边框 | string | `green` | `green` `blue` `orange` `red` |
| disabled | 禁用 | boolean | `false` | `true` `false` |
| loading | 加载 | boolean | `false` | `true` `false` |
| native-type | 原生类型 | string | `button` | `button` `submit` `reset` |
::: :::
::: title 按钮事件 ::: title Button 插槽
:::
::: table
| 名称 | 描述 | 参数 |
| ----- | -------- | ---- |
| default | 默认内容 | -- |
:::
::: title Button Group 属性
:::
::: table
| 属性 | 描述 | 类型 | 默认值 | 可选值 |
| ----------- | -------- | ------- | --------- | ----------------------------------|
| - | - | - | - |
:::
::: title Button Group 插槽
::: :::
::: table ::: table
| 事件 | 描述 | 参数 | | 事件 | 描述 | 参数 |
| ----- | -------- | ---- | | ----- | -------- | ---- |
| click | 单击事件 | `--` | | default | 默认内容 | -- |
::: :::
::: comment ::: comment
::: :::

View File

@ -0,0 +1,123 @@
::: title 基础使用
:::
::: demo
<template>
<h1 style="padding:20px 15px">
<lay-count-up :end-val="countVal" :decimalPlaces="2"></lay-count-up>
</h1>
</template>
<script>
import { ref } from 'vue'
export default {
setup() {
const countVal = 2862.9888;
return {
}
}
}
</script>
:::
::: title 自定义前缀&后缀
:::
::: demo
<template>
<!-- 属性 -->
<lay-card style="width:200px;height:120px;display:inline-block; border:1px solid #eeeeee;box-shadow: 3px 3px 5px #888888">
<h1 style="padding:20px 15px">
<lay-count-up :end-val="countVal2" prefix="¥" suffix="↑"></lay-count-up>
</h1>
</lay-card>
<!-- 插槽 -->
<lay-card style="width:200px;height:120px;display:inline-block; margin-left:50px; border:1px solid #eeeeee;box-shadow: 3px 3px 5px #888888">
<h1 style="padding:20px 15px">
<lay-count-up :end-val="18" :duration="1">
<template #prefix>
<span style="font-size:75%">雷雨&nbsp</span>
</template>
<template #suffix>
<span style="font-size:75%">
<sub></sub>
</span>
</template>
</lay-count-up>
</h1>
</lay-card>
<lay-button @click="handlerClick" type="primary" border="blue" size="sm" style="margin:0px 30px;">更新</lay-button>
</template>
<script>
import { ref } from 'vue'
export default {
setup() {
const countVal2 = ref(98626);
const handlerClick = () => {
countVal2.value += 1000;
}
return {
handlerClick,
}
}
}
</script>
:::
::: title countUp 属性
:::
::: table
| 属性 | 描述 | 类型 | 默认值 | 可选值 |
| ------------- | ------------------------------------------------------------ | -------------- | ------ | -------------- |
| endVal | 显示的值 | number | `0` | — |
| decimalPlaces | 小数位数 | number | `0` | — |
| useGrouping | 使用千位分隔符 | boolean | `true` | `true` `false` |
| separator | 分隔符 | string | `,` | — |
| useEasing | 使用动画 | boolean | `true` | `true` `false` |
| duration | 动画持续时间 | number | `2` | — |
| prefix | 前缀 | string | — | — |
| suffix | 后缀 | string | — | — |
| option | 选项,用于多实例的总体设置,<br>props会覆盖option的属性使用更灵活 | CountUpOptions | — | 参见[CountUpOptions](#opts) |
:::
::: title CountUpOptions 属性
:::
::: table
| 属性 | 描述 | 类型 | 默认值 | 可选值 |
| ------------- | ------------------------------------------------------------ | -------------- | ------ | -------------- |
| startVal | 开始值 | number | `0` | — |
| decimalPlaces | 小数位数 | number | `0` | — |
| useGrouping | 使用千位分隔符 | boolean | `true` | `true` `false` |
| separator | 分隔符 | string | `,` | — |
| useEasing | 使用动画 | boolean | `true` | `true` `false` |
| duration | 动画持续时间 | number | `2` | — |
| prefix | 前缀 | string | — | — |
| suffix | 后缀 | string | — | — | |
:::
::: title countUp 插槽
:::
::: table
| 名称 | 描述 | 参数 |
| ----- | -------- | ---- |
| prefix | 前缀 | -- |
| suffix | 后缀 | -- |
:::

View File

@ -1,7 +1,10 @@
::: title 横向
:::
::: demo ::: demo
<template> <template>
<lay-slider v-model="value1" :disabled="true"></lay-slider> <lay-slider v-model="value1" :disabled="false"></lay-slider>
</template> </template>
<script> <script>
@ -19,10 +22,12 @@ export default {
::: :::
::: title 纵向
:::
::: demo ::: demo
<template> <template>
<lay-slider v-model="value2" :vertical="vertical" :disabled="true"></lay-slider> <lay-slider v-model="value2" :vertical="true" :disabled="true"></lay-slider>
</template> </template>
<script> <script>
@ -31,11 +36,9 @@ import { ref } from 'vue'
export default { export default {
setup() { setup() {
const vertical = ref(true)
const value2 = ref(10) const value2 = ref(10)
return { return {
vertical,
value2 value2
} }
} }
@ -44,5 +47,49 @@ export default {
::: :::
::: title 区间
:::
::: demo
<template>
<lay-slider v-model="value3" :range="true"></lay-slider>
</template>
<script>
import { ref } from 'vue'
export default {
setup() {
const value3 = ref([20,50])
return {
value3
}
}
}
</script>
:::
::: title 纵向区间
:::
::: demo
<template>
<lay-slider v-model="value3" :range="true" :vertical="true"></lay-slider>
</template>
<script>
import { ref } from 'vue'
export default {
setup() {
const value4 = ref([20,50])
return {
value4
}
}
}
</script>
:::
::: comment ::: comment
::: :::

View File

@ -270,24 +270,24 @@ export default {
::: :::
::: title 表格属性 ::: title Table 属性
::: :::
::: table ::: table
| 属性 | 描述 | 可选值 | | 属性 | 描述 | 类型 | 默认值 | 可选值 |
| ------------------------ | ---------- | -------------- | | -------------------- | ----------------------------- | ---- | ------ | -------------- |
| columns | 列配置 | -- | | columns | 列配置 - [更多](#tableColumn) | -- | -- | -- |
| dataSource | 数据源 | -- | | dataSource | 数据源 | -- | -- | -- |
| checkbox | 开启复现框 | -- | | checkbox | 开启复选框 | -- | -- | -- |
| id | 主键 | -- | | id | 主键 | -- | -- | -- |
| selectedKeys ( v-model ) | 选中项 | -- | | v-model:selectedKeys | 选中项 | -- | -- | -- |
| default-toolbar | 开启工具栏 | `lg` `md` `sm` | | default-toolbar | 工具栏 | -- | -- | -- |
| size | 尺寸 | -- | | size | 尺寸 | -- | -- | `lg` `md` `sm` |
::: :::
::: title 表格事件 ::: title Table 事件
::: :::
::: table ::: table
@ -299,29 +299,32 @@ export default {
::: :::
::: title 表格插槽 ::: title Table 插槽
::: :::
::: table ::: table
| 插槽 | 描述 | 默认 | | 插槽 | 描述 | 参数 |
| ------- | ------------ | ---- | | ------- | ------------ | ---- |
| toolbar | 自定义工具栏 | -- | | toolbar | 自定义工具栏 | -- |
::: :::
::: title 列配置 ### <div id="tableColumn"></div>
::: title Table Column 属性
::: :::
::: table ::: table
| 插槽 | 描述 | 默认 | | 插槽 | 描述 | 默认 |
| ------- | ------------ | ---- | | ---------- | ---------- | ---- |
| title | 列标题 | -- | | title | 列标题 | -- |
| key | 数据字段 | -- | | key | 数据字段 | -- |
| customSlot | 插槽名称 | -- | | customSlot | 自定义插槽 | -- |
| width | 宽度 | -- | | width | 宽度 | -- |
::: :::
::: comment ::: comment
::: :::

View File

@ -10,6 +10,26 @@
::: demo ::: demo
<template> <template>
<lay-timeline> <lay-timeline>
<lay-timeline-item title="0.3.x">
<ul>
<a name="0-3-1"> </a>
<li>
<h3>0.3.1 <span class="layui-badge-rim">2021-12-28</span></h3>
<ul>
<li>[新增] count-up 数字滚动组件。</li>
<li>[新增] slider 滑块 range 属性, 支持区间取值。</li>
<li>[新增] button 按钮 disabled 属性, 删除 type 属性 disabled 值。</li>
<li>[修复] 演示站点剪贴板功能http下不能使用的问题。</li>
<li>[修复] checkbox 复选框 modelValue 属性必填警告。</li>
<li>[修复] formItem 内下拉框组件校验不通过边框未标红问题。</li>
<li>[修复] rate 评分 mouseleave 事件绑定警告。</li>
<li>[修复] npm 安装 layui-vue 不必要的依赖警告。</li>
<li>[集成] eslint, prettier 规范插件 。</li>
<li>[升级] icons-vue 1.0.2。</li>
</ul>
</li>
</ul>
</lay-timeline-item>
<lay-timeline-item title="0.2.x"> <lay-timeline-item title="0.2.x">
<ul> <ul>
<a name="0-2-9"> </a> <a name="0-2-9"> </a>

View File

@ -1,10 +1,154 @@
::: title 贡献代码 ::: title 团队成员
::: :::
<lay-card>
<lay-row>
<lay-col md="1">
<lay-avatar src="https://portrait.gitee.com/uploads/avatars/user/1611/4835367_Jmysy_1578975358.png"></lay-avatar>
</lay-col>
<lay-col md="2">
就眠仪式
</lay-col>
<lay-col md="6">
焦点layui-vue
</lay-col>
<lay-col md="4">
地点China Beijing
</lay-col>
<lay-col md="5">
生态Pear Admin
</lay-col>
<lay-col md="6">
其他Gitee Github
</lay-col>
</lay-row>
</lay-card>
<lay-card>
<lay-row>
<lay-col md="1">
<lay-avatar src="https://portrait.gitee.com/uploads/avatars/user/1755/5267877_jobin_jia_1608578025.png"></lay-avatar>
</lay-col>
<lay-col md="2">
落小梅
</lay-col>
<lay-col md="6">
焦点tree table
</lay-col>
<lay-col md="4">
地点China Hunan
</lay-col>
<lay-col md="5">
生态Pear Admin
</lay-col>
<lay-col md="6">
其他Gitee Github
</lay-col>
</lay-row>
</lay-card>
::: describe <lay-card>
<lay-row>
<lay-col md="1">
<lay-avatar src="https://portrait.gitee.com/uploads/avatars/user/702/2106738_wanglin300_1639442830.png"></lay-avatar>
</lay-col>
<lay-col md="2">
halo
</lay-col>
<lay-col md="6">
焦点slider
</lay-col>
<lay-col md="4">
地点China Beijing
</lay-col>
<lay-col md="5">
生态:暂无
</lay-col>
<lay-col md="6">
其他Gitee Github
</lay-col>
</lay-row>
</lay-card>
<lay-card>
<lay-row>
<lay-col md="1">
<lay-avatar src="https://portrait.gitee.com/uploads/avatars/user/1871/5614379_xumisky_1607057214.png"></lay-avatar>
</lay-col>
<lay-col md="2">
须弥
</lay-col>
<lay-col md="6">
焦点layui-vue
</lay-col>
<lay-col md="4">
地点:未知
</lay-col>
<lay-col md="5">
生态:暂无
</lay-col>
<lay-col md="6">
其他Gitee Github
</lay-col>
</lay-row>
</lay-card>
<lay-card>
<lay-row>
<lay-col md="1">
<lay-avatar src="https://portrait.gitee.com/uploads/avatars/user/2469/7407590_wcg666_1640528494.png"></lay-avatar>
</lay-col>
<lay-col md="2">
Sight
</lay-col>
<lay-col md="6">
焦点count-up backtop
</lay-col>
<lay-col md="4">
地点:未知
</lay-col>
<lay-col md="5">
生态:暂无
</lay-col>
<lay-col md="6">
其他Gitee Github
</lay-col>
</lay-row>
</lay-card>
<lay-card>
<lay-row>
<lay-col md="1">
<lay-avatar src="https://portrait.gitee.com/uploads/avatars/user/2596/7789823_finalsummer_1613993823.png"></lay-avatar>
</lay-col>
<lay-col md="2">
finalsummer
</lay-col>
<lay-col md="6">
焦点layer-vue
</lay-col>
<lay-col md="4">
地点:未知
</lay-col>
<lay-col md="5">
生态:暂无
</lay-col>
<lay-col md="6">
其他Gitee Github
</lay-col>
</lay-row>
</lay-card>
<br>
::: title 社区伙伴
:::
::: block
在 人 的 尺 度 里,世 界 既 无 始,也 无 终,唯 一 的 信 仰 是,总 有 人 认 得 你 站 在 暴 风 雪 里 的 样 子。
:::
::: describe
[![Giteye chart](https://chart.giteye.net/gitee/layui-vue/layui-vue/DBC9Z6HQ.png)](https://giteye.net/chart/DBC9Z6HQ) [![Giteye chart](https://chart.giteye.net/gitee/layui-vue/layui-vue/DBC9Z6HQ.png)](https://giteye.net/chart/DBC9Z6HQ)
::: :::

View File

@ -1,2 +1,5 @@
::: title 常见问题 ::: title 常见问题
:::
::: comment
::: :::

View File

@ -34,9 +34,40 @@ const toggle = function () {
const copy = function () { const copy = function () {
const foundCodes = meta.value.getElementsByClassName('language-html') const foundCodes = meta.value.getElementsByClassName('language-html')
if (document.hasFocus()) { const foundCode = foundCodes[0];
const text = foundCodes[0].textContent || ""; let successful = false;
// 使http使
if (navigator.clipboard && document.hasFocus()) {
const text = foundCode.textContent || "";
navigator.clipboard.writeText(text); navigator.clipboard.writeText(text);
successful = true;
} else if (window.getSelection()){
// 使document.execCommand
// div使div
var range = document.createRange();
let copyDiv;
if (show.value) {
range.selectNode(foundCode);
} else {
copyDiv = document.createElement('div');
copyDiv.innerHTML = foundCode.innerHTML;
copyDiv.style.position="fixed";
copyDiv.style.left="-9999px";
document.body.appendChild(copyDiv);
range.selectNode(copyDiv);
}
window.getSelection()?.addRange(range);
try {
successful = document.execCommand('copy');
} catch(err) {
successful = false;
console.error(err);
}
window.getSelection()?.removeAllRanges();
copyDiv?.remove();
}
if (successful) {
layer.msg("复制成功", { icon : 1, time: 1000}, ()=>{}) layer.msg("复制成功", { icon : 1, time: 1000}, ()=>{})
} else { } else {
layer.msg("复制失败", { icon : 2, time: 1000}, ()=>{}) layer.msg("复制失败", { icon : 2, time: 1000}, ()=>{})
@ -155,4 +186,4 @@ function handleScroll() {
padding-left: 10px; padding-left: 10px;
padding-right: 10px; padding-right: 10px;
} }
</style> </style>

View File

@ -28,9 +28,9 @@
.lay-table-box table th { .lay-table-box table th {
color: #666; color: #666;
font-weight: 500; font-weight: 500;
white-space: nowrap; /*表头内容强制在一行显示*/
background-color: #fafafa; background-color: #fafafa;
} }
.lay-table-box table th,
.lay-table-box table td { .lay-table-box table td {
white-space: nowrap; white-space: nowrap;
} }

View File

@ -43,7 +43,7 @@
</a> </a>
</li> </li>
<li class="layui-nav-item"> <li class="layui-nav-item">
<a href="javascript:void(0)"> 0.2.9 </a> <a href="javascript:void(0)"> 0.3.1 </a>
</li> </li>
</ul> </ul>
</lay-header> </lay-header>
@ -333,6 +333,12 @@ export default {
subTitle: 'backtop', subTitle: 'backtop',
path: '/zh-CN/components/backtop', path: '/zh-CN/components/backtop',
}, },
{
id: 43,
title: '数字滚动',
subTitle: 'countup',
path: '/zh-CN/components/countup',
},
] ]
const handleClick = function (menu) { const handleClick = function (menu) {

View File

@ -309,6 +309,11 @@ const zhCN = [
component: () => import('../../docs/zh-CN/components/backtop.md'), component: () => import('../../docs/zh-CN/components/backtop.md'),
meta: { title: '返回顶部' }, meta: { title: '返回顶部' },
}, },
{
path: '/zh-CN/components/countup',
component: () => import('../../docs/zh-CN/components/countup.md'),
meta: { title: '数字滚动' },
},
], ],
}, },
{ {

View File

@ -314,6 +314,12 @@ export default {
subTitle: 'carousel', subTitle: 'carousel',
path: '/zh-CN/components/carousel', path: '/zh-CN/components/carousel',
}, },
{
id: 43,
title: '数字滚动',
subTitle: 'countUp',
path: '/zh-CN/components/countup',
},
], ],
}, },
{ {

View File

@ -26,7 +26,7 @@
> >
</div> </div>
<div class="site-version"> <div class="site-version">
<span>当前版本v<cite class="site-showv">0.2.9</cite></span> <span>当前版本v<cite class="site-showv">0.3.1</cite></span>
<span <span
><router-link ><router-link
class="layui-inline site-down" class="layui-inline site-down"
@ -44,7 +44,7 @@
rel="nofollow" rel="nofollow"
class="site-star" class="site-star"
> >
<i class="layui-icon"></i> Star <cite id="getStars">336</cite> <i class="layui-icon"></i> Star <cite id="getStars">521</cite>
</a> </a>
<a <a
href="https://gitee.com/layui-vue" href="https://gitee.com/layui-vue"

View File

@ -1,10 +1,19 @@
{ {
"name": "@layui/layui-vue", "name": "@layui/layui-vue",
"version": "0.2.9", "version": "0.3.1",
"author": "sleeprite",
"license": "MIT",
"description": "a component library for Vue 3 base on layui-vue", "description": "a component library for Vue 3 base on layui-vue",
"homepage": "https://gitee.com/layui-vue/layui-vue/blob/master/README.md", "homepage": "http://layui-vue.pearadmin.com",
"module": "lib/layui-vue.es.js", "module": "lib/layui-vue.es.js",
"main": "lib/layui-vue.umd.js", "main": "lib/layui-vue.umd.js",
"types": "types/index.d.ts",
"style": "lib/index.css",
"keywords": [
"layui-vue",
"layui",
"vue"
],
"exports": { "exports": {
".": { ".": {
"import": "./lib/layui-vue.es.js", "import": "./lib/layui-vue.es.js",
@ -12,33 +21,21 @@
}, },
"./lib/": "./lib/" "./lib/": "./lib/"
}, },
"types": "types/index.d.ts",
"style": "lib/index.css",
"scripts": { "scripts": {
"dev": "vite", "dev": "vite",
"build": "vite build --emptyOutDir && npm run build:types", "build": "vite build --emptyOutDir && npm run build:types",
"build:types": "rimraf types && tsc -d", "build:types": "rimraf types && tsc -d",
"build:example": "vite build example" "build:example": "vite build example",
}, "lint:eslint": "eslint \"src/**/*.{vue,ts,tsx}\" --fix",
"keywords": [ "lint:prettier": "prettier --write \"src/**/*.{js,json,tsx,css,less,scss,vue,html,md}\"",
"layui-vue", "commit": "git cz"
"layui",
"vue"
],
"author": "就眠儀式",
"license": "MIT",
"peerDependencies": {
"@layui/hooks-vue": "^0.1.6",
"@layui/icons-vue": "^1.0.1",
"@layui/layer-vue": "^1.2.2",
"vue": "^3.2.26",
"vue-router": "^4.0.12"
}, },
"dependencies": { "dependencies": {
"@layui/hooks-vue": "^0.1.6", "@layui/hooks-vue": "^0.1.6",
"@layui/icons-vue": "^1.0.1", "@layui/icons-vue": "^1.0.2",
"@layui/layer-vue": "^1.2.2", "@layui/layer-vue": "^1.2.2",
"async-validator": "^4.0.7", "async-validator": "^4.0.7",
"countup.js": "^2.0.8",
"evtd": "^0.2.3", "evtd": "^0.2.3",
"vue": "^3.2.26", "vue": "^3.2.26",
"vue-router": "^4.0.12" "vue-router": "^4.0.12"
@ -51,18 +48,27 @@
"@types/markdown-it": "^12.2.3", "@types/markdown-it": "^12.2.3",
"@types/markdown-it-container": "^2.0.4", "@types/markdown-it-container": "^2.0.4",
"@types/node": "^16.11.9", "@types/node": "^16.11.9",
"@typescript-eslint/eslint-plugin": "^5.8.0",
"@typescript-eslint/parser": "^5.8.0",
"@vitejs/plugin-vue": "^1.9.3", "@vitejs/plugin-vue": "^1.9.3",
"@vue/compiler-sfc": "^3.2.26", "@vue/compiler-sfc": "^3.2.26",
"@vue/server-renderer": "^3.2.26", "@vue/server-renderer": "^3.2.26",
"commitizen": "^4.2.4",
"cz-conventional-changelog": "3.3.0",
"escape-html": "^1.0.3", "escape-html": "^1.0.3",
"less": "^4.1.2", "eslint": "^8.5.0",
"eslint-config-prettier": "^8.3.0",
"eslint-plugin-prettier": "^4.0.0",
"eslint-plugin-vue": "^8.2.0",
"markdown-it-container": "^3.0.0", "markdown-it-container": "^3.0.0",
"prettier": "^2.5.1",
"prismjs": "^1.25.0", "prismjs": "^1.25.0",
"rimraf": "^3.0.2", "rimraf": "^3.0.2",
"rollup": "^2.0.0", "rollup": "^2.61.0",
"typescript": "^4.5.2", "typescript": "^4.5.2",
"vite": "2.6.14", "less": "^4.1.2",
"vite-plugin-md": "^0.11.4" "vite": "2.7.6",
"vite-plugin-md": "^0.11.6"
}, },
"files": [ "files": [
"lib", "lib",

File diff suppressed because it is too large Load Diff

2
shims-vue.d.ts vendored
View File

@ -22,4 +22,4 @@ interface ImportMeta {
DEV: boolean DEV: boolean
SSR: boolean SSR: boolean
} }
} }

View File

@ -182,7 +182,7 @@ a cite {
} }
.layui-clear:after { .layui-clear:after {
content: '\20'; content: "\20";
clear: both; clear: both;
*zoom: 1; *zoom: 1;
display: block; display: block;
@ -683,7 +683,7 @@ a cite {
color: #c2c2c2 !important; color: #c2c2c2 !important;
} }
.layui-input-number[type='number'] { .layui-input-number[type="number"] {
-moz-appearance: textfield; -moz-appearance: textfield;
} }
@ -740,8 +740,8 @@ a cite {
padding: 0 10px; padding: 0 10px;
} }
.layui-form input[type='checkbox'], .layui-form input[type="checkbox"],
.layui-form input[type='radio'], .layui-form input[type="radio"],
.layui-form select { .layui-form select {
display: none; display: none;
} }
@ -757,7 +757,7 @@ a cite {
} }
.layui-form-item:after { .layui-form-item:after {
content: '\20'; content: "\20";
clear: both; clear: both;
*zoom: 1; *zoom: 1;
display: block; display: block;
@ -1122,7 +1122,7 @@ a cite {
margin-top: 4px; margin-top: 4px;
} }
.layui-form-checkbox[lay-skin='primary'] { .layui-form-checkbox[lay-skin="primary"] {
height: auto !important; height: auto !important;
line-height: normal !important; line-height: normal !important;
min-width: 18px; min-width: 18px;
@ -1134,7 +1134,7 @@ a cite {
background: 0 0; background: 0 0;
} }
.layui-form-checkbox[lay-skin='primary'] span { .layui-form-checkbox[lay-skin="primary"] span {
padding-left: 0; padding-left: 0;
padding-right: 15px; padding-right: 15px;
line-height: 18px; line-height: 18px;
@ -1142,7 +1142,7 @@ a cite {
color: #666; color: #666;
} }
.layui-form-checkbox[lay-skin='primary'] i { .layui-form-checkbox[lay-skin="primary"] i {
right: auto; right: auto;
left: 0; left: 0;
width: 16px; width: 16px;
@ -1156,27 +1156,27 @@ a cite {
transition: 0.1s linear; transition: 0.1s linear;
} }
.layui-form-checkbox[lay-skin='primary']:hover i { .layui-form-checkbox[lay-skin="primary"]:hover i {
border-color: #5fb878; border-color: #5fb878;
color: #fff; color: #fff;
} }
.layui-form-checked[lay-skin='primary'] i { .layui-form-checked[lay-skin="primary"] i {
border-color: #5fb878 !important; border-color: #5fb878 !important;
background-color: #5fb878; background-color: #5fb878;
color: #fff; color: #fff;
} }
.layui-checkbox-disabled[lay-skin='primary'] span { .layui-checkbox-disabled[lay-skin="primary"] span {
background: 0 0 !important; background: 0 0 !important;
color: #c2c2c2 !important; color: #c2c2c2 !important;
} }
.layui-checkbox-disabled[lay-skin='primary']:hover i { .layui-checkbox-disabled[lay-skin="primary"]:hover i {
border-color: #d2d2d2; border-color: #d2d2d2;
} }
.layui-form-item .layui-form-checkbox[lay-skin='primary'] { .layui-form-item .layui-form-checkbox[lay-skin="primary"] {
margin-top: 10px; margin-top: 10px;
} }
@ -1391,7 +1391,7 @@ a cite {
} }
.layui-form-item .layui-inline:after { .layui-form-item .layui-inline:after {
content: '\20'; content: "\20";
clear: both; clear: both;
display: block; display: block;
height: 0; height: 0;
@ -1758,8 +1758,8 @@ a cite {
.layui-table-tool, .layui-table-tool,
.layui-table-total, .layui-table-total,
.layui-table-view, .layui-table-view,
.layui-table[lay-skin='line'], .layui-table[lay-skin="line"],
.layui-table[lay-skin='row'] { .layui-table[lay-skin="row"] {
border-width: 1px; border-width: 1px;
border-style: solid; border-style: solid;
border-color: #eee; border-color: #eee;
@ -1774,18 +1774,18 @@ a cite {
font-size: 14px; font-size: 14px;
} }
.layui-table[lay-skin='line'] td, .layui-table[lay-skin="line"] td,
.layui-table[lay-skin='line'] th { .layui-table[lay-skin="line"] th {
border-width: 0 0 1px; border-width: 0 0 1px;
} }
.layui-table[lay-skin='row'] td, .layui-table[lay-skin="row"] td,
.layui-table[lay-skin='row'] th { .layui-table[lay-skin="row"] th {
border-width: 0 1px 0 0; border-width: 0 1px 0 0;
} }
.layui-table[lay-skin='nob'] td, .layui-table[lay-skin="nob"] td,
.layui-table[lay-skin='nob'] th { .layui-table[lay-skin="nob"] th {
border: none; border: none;
} }
@ -1793,23 +1793,23 @@ a cite {
max-width: 100px; max-width: 100px;
} }
.layui-table[lay-size='lg'] td, .layui-table[lay-size="lg"] td,
.layui-table[lay-size='lg'] th { .layui-table[lay-size="lg"] th {
padding: 15px 30px; padding: 15px 30px;
} }
.layui-table-view .layui-table[lay-size='lg'] .layui-table-cell { .layui-table-view .layui-table[lay-size="lg"] .layui-table-cell {
height: 40px; height: 40px;
line-height: 40px; line-height: 40px;
} }
.layui-table[lay-size='sm'] td, .layui-table[lay-size="sm"] td,
.layui-table[lay-size='sm'] th { .layui-table[lay-size="sm"] th {
font-size: 12px; font-size: 12px;
padding: 5px 10px; padding: 5px 10px;
} }
.layui-table-view .layui-table[lay-size='sm'] .layui-table-cell { .layui-table-view .layui-table[lay-size="sm"] .layui-table-cell {
height: 20px; height: 20px;
line-height: 20px; line-height: 20px;
} }
@ -1829,11 +1829,11 @@ a cite {
border-collapse: separate; border-collapse: separate;
} }
.layui-table-view .layui-table[lay-skin='line'] { .layui-table-view .layui-table[lay-skin="line"] {
border-width: 0 1px 0 0; border-width: 0 1px 0 0;
} }
.layui-table-view .layui-table[lay-skin='row'] { .layui-table-view .layui-table[lay-skin="row"] {
border-width: 0 0 1px; border-width: 0 0 1px;
} }
@ -1856,11 +1856,11 @@ a cite {
cursor: default; cursor: default;
} }
.layui-table-view .layui-table td[data-edit='text'] { .layui-table-view .layui-table td[data-edit="text"] {
cursor: text; cursor: text;
} }
.layui-table-view .layui-form-checkbox[lay-skin='primary'] i { .layui-table-view .layui-form-checkbox[lay-skin="primary"] i {
width: 18px; width: 18px;
height: 18px; height: 18px;
} }
@ -1973,7 +1973,7 @@ a cite {
transition: all 0.3s; transition: all 0.3s;
} }
.layui-table-tool-panel li .layui-form-checkbox[lay-skin='primary'] { .layui-table-tool-panel li .layui-form-checkbox[lay-skin="primary"] {
width: 100%; width: 100%;
padding-left: 28px; padding-left: 28px;
} }
@ -1982,13 +1982,13 @@ a cite {
background-color: #f6f6f6; background-color: #f6f6f6;
} }
.layui-table-tool-panel li .layui-form-checkbox[lay-skin='primary'] i { .layui-table-tool-panel li .layui-form-checkbox[lay-skin="primary"] i {
position: absolute; position: absolute;
left: 0; left: 0;
top: 0; top: 0;
} }
.layui-table-tool-panel li .layui-form-checkbox[lay-skin='primary'] span { .layui-table-tool-panel li .layui-form-checkbox[lay-skin="primary"] span {
padding: 0; padding: 0;
} }
@ -2042,11 +2042,11 @@ a cite {
border-top-color: #666; border-top-color: #666;
} }
.layui-table-sort[lay-sort='asc'] .layui-table-sort-asc { .layui-table-sort[lay-sort="asc"] .layui-table-sort-asc {
border-bottom-color: #000; border-bottom-color: #000;
} }
.layui-table-sort[lay-sort='desc'] .layui-table-sort-desc { .layui-table-sort[lay-sort="desc"] .layui-table-sort-desc {
border-top-color: #000; border-top-color: #000;
} }
@ -2057,7 +2057,7 @@ a cite {
box-sizing: border-box; box-sizing: border-box;
} }
.layui-table-cell .layui-form-checkbox[lay-skin='primary'] { .layui-table-cell .layui-form-checkbox[lay-skin="primary"] {
top: -1px; top: -1px;
padding: 0; padding: 0;
} }
@ -2527,7 +2527,7 @@ body .layui-table-tips .layui-layer-content {
top: 0; top: 0;
bottom: 0; bottom: 0;
border-right: 3px solid #5fb878; border-right: 3px solid #5fb878;
content: ''; content: "";
} }
.layui-menu-body-title { .layui-menu-body-title {
@ -2564,7 +2564,7 @@ body .layui-table-tips .layui-layer-content {
} }
.layui-menu-body-panel:before { .layui-menu-body-panel:before {
content: ''; content: "";
position: absolute; position: absolute;
width: 20px; width: 20px;
left: -16px; left: -16px;
@ -2618,13 +2618,13 @@ body .layui-table-tips .layui-layer-content {
background-color: #fff; background-color: #fff;
box-sizing: border-box; box-sizing: border-box;
} }
.layui-dropdown dl::before{ .layui-dropdown dl::before {
content: ' '; content: " ";
display: block; display: block;
height: 4px; height: 4px;
width: 100%; width: 100%;
} }
.layui-dropdown dl>.layui-dropdown-menu{ .layui-dropdown dl > .layui-dropdown-menu {
border: 1px solid #eee; border: 1px solid #eee;
border-radius: 4px; border-radius: 4px;
} }
@ -2876,7 +2876,7 @@ body .layui-table-tips .layui-layer-content {
.layui-nav .layui-this:after, .layui-nav .layui-this:after,
.layui-nav-bar { .layui-nav-bar {
content: ''; content: "";
position: absolute; position: absolute;
left: 0; left: 0;
top: 0; top: 0;
@ -2892,7 +2892,7 @@ body .layui-table-tips .layui-layer-content {
z-index: 1000; z-index: 1000;
} }
.layui-nav[lay-bar='disabled'] .layui-nav-bar { .layui-nav[lay-bar="disabled"] .layui-nav-bar {
display: none; display: none;
} }
@ -3181,7 +3181,7 @@ body .layui-table-tips .layui-layer-content {
position: absolute; position: absolute;
left: 0; left: 0;
top: 0; top: 0;
content: ''; content: "";
width: 100%; width: 100%;
height: 41px; height: 41px;
border-width: 1px; border-width: 1px;
@ -3363,7 +3363,7 @@ body .layui-table-tips .layui-layer-content {
.layui-carousel > [carousel-item]:before { .layui-carousel > [carousel-item]:before {
position: absolute; position: absolute;
content: '\e63d'; content: "\e63d";
left: 50%; left: 50%;
top: 50%; top: 50%;
width: 100px; width: 100px;
@ -3416,22 +3416,22 @@ body .layui-table-tips .layui-layer-content {
cursor: pointer; cursor: pointer;
} }
.layui-carousel-arrow[lay-type='add'] { .layui-carousel-arrow[lay-type="add"] {
left: auto !important; left: auto !important;
right: 10px; right: 10px;
} }
.layui-carousel:hover .layui-carousel-arrow[lay-type='add'], .layui-carousel:hover .layui-carousel-arrow[lay-type="add"],
.layui-carousel[lay-arrow='always'] .layui-carousel-arrow[lay-type='add'] { .layui-carousel[lay-arrow="always"] .layui-carousel-arrow[lay-type="add"] {
right: 20px; right: 20px;
} }
.layui-carousel[lay-arrow='always'] .layui-carousel-arrow { .layui-carousel[lay-arrow="always"] .layui-carousel-arrow {
opacity: 1; opacity: 1;
left: 20px; left: 20px;
} }
.layui-carousel[lay-arrow='none'] .layui-carousel-arrow { .layui-carousel[lay-arrow="none"] .layui-carousel-arrow {
display: none; display: none;
} }
@ -3455,19 +3455,19 @@ body .layui-table-tips .layui-layer-content {
font-size: 0; font-size: 0;
} }
.layui-carousel[lay-indicator='outside'] { .layui-carousel[lay-indicator="outside"] {
margin-bottom: 30px; margin-bottom: 30px;
} }
.layui-carousel[lay-indicator='outside'] .layui-carousel-ind { .layui-carousel[lay-indicator="outside"] .layui-carousel-ind {
top: 10px; top: 10px;
} }
.layui-carousel[lay-indicator='outside'] .layui-carousel-ind ul { .layui-carousel[lay-indicator="outside"] .layui-carousel-ind ul {
background-color: rgba(0, 0, 0, 0.5); background-color: rgba(0, 0, 0, 0.5);
} }
.layui-carousel[lay-indicator='none'] .layui-carousel-ind { .layui-carousel[lay-indicator="none"] .layui-carousel-ind {
display: none; display: none;
} }
@ -3533,23 +3533,23 @@ body .layui-table-tips .layui-layer-content {
left: 100%; left: 100%;
} }
.layui-carousel[lay-anim='updown'] .layui-carousel-arrow { .layui-carousel[lay-anim="updown"] .layui-carousel-arrow {
left: 50% !important; left: 50% !important;
top: 20px; top: 20px;
margin: 0 0 0 -18px; margin: 0 0 0 -18px;
} }
.layui-carousel[lay-anim='updown'] > [carousel-item] > *, .layui-carousel[lay-anim="updown"] > [carousel-item] > *,
.layui-carousel[lay-anim='fade'] > [carousel-item] > * { .layui-carousel[lay-anim="fade"] > [carousel-item] > * {
left: 0 !important; left: 0 !important;
} }
.layui-carousel[lay-anim='updown'] .layui-carousel-arrow[lay-type='add'] { .layui-carousel[lay-anim="updown"] .layui-carousel-arrow[lay-type="add"] {
top: auto !important; top: auto !important;
bottom: 20px; bottom: 20px;
} }
.layui-carousel[lay-anim='updown'] .layui-carousel-ind { .layui-carousel[lay-anim="updown"] .layui-carousel-ind {
position: absolute; position: absolute;
top: 50%; top: 50%;
right: 20px; right: 20px;
@ -3557,66 +3557,66 @@ body .layui-table-tips .layui-layer-content {
height: auto; height: auto;
} }
.layui-carousel[lay-anim='updown'] .layui-carousel-ind ul { .layui-carousel[lay-anim="updown"] .layui-carousel-ind ul {
padding: 3px 5px; padding: 3px 5px;
} }
.layui-carousel[lay-anim='updown'] .layui-carousel-ind li { .layui-carousel[lay-anim="updown"] .layui-carousel-ind li {
display: block; display: block;
margin: 6px 0; margin: 6px 0;
} }
.layui-carousel[lay-anim='updown'] > [carousel-item] > .layui-this { .layui-carousel[lay-anim="updown"] > [carousel-item] > .layui-this {
top: 0; top: 0;
} }
.layui-carousel[lay-anim='updown'] > [carousel-item] > .layui-carousel-prev { .layui-carousel[lay-anim="updown"] > [carousel-item] > .layui-carousel-prev {
top: -100%; top: -100%;
} }
.layui-carousel[lay-anim='updown'] > [carousel-item] > .layui-carousel-next { .layui-carousel[lay-anim="updown"] > [carousel-item] > .layui-carousel-next {
top: 100%; top: 100%;
} }
.layui-carousel[lay-anim='updown'] .layui-carousel[lay-anim="updown"]
> [carousel-item] > [carousel-item]
> .layui-carousel-next.layui-carousel-left, > .layui-carousel-next.layui-carousel-left,
.layui-carousel[lay-anim='updown'] .layui-carousel[lay-anim="updown"]
> [carousel-item] > [carousel-item]
> .layui-carousel-prev.layui-carousel-right { > .layui-carousel-prev.layui-carousel-right {
top: 0; top: 0;
} }
.layui-carousel[lay-anim='updown'] .layui-carousel[lay-anim="updown"]
> [carousel-item] > [carousel-item]
> .layui-this.layui-carousel-left { > .layui-this.layui-carousel-left {
top: -100%; top: -100%;
} }
.layui-carousel[lay-anim='updown'] .layui-carousel[lay-anim="updown"]
> [carousel-item] > [carousel-item]
> .layui-this.layui-carousel-right { > .layui-this.layui-carousel-right {
top: 100%; top: 100%;
} }
.layui-carousel[lay-anim='fade'] > [carousel-item] > .layui-carousel-next, .layui-carousel[lay-anim="fade"] > [carousel-item] > .layui-carousel-next,
.layui-carousel[lay-anim='fade'] > [carousel-item] > .layui-carousel-prev { .layui-carousel[lay-anim="fade"] > [carousel-item] > .layui-carousel-prev {
opacity: 0; opacity: 0;
} }
.layui-carousel[lay-anim='fade'] .layui-carousel[lay-anim="fade"]
> [carousel-item] > [carousel-item]
> .layui-carousel-next.layui-carousel-left, > .layui-carousel-next.layui-carousel-left,
.layui-carousel[lay-anim='fade'] .layui-carousel[lay-anim="fade"]
> [carousel-item] > [carousel-item]
> .layui-carousel-prev.layui-carousel-right { > .layui-carousel-prev.layui-carousel-right {
opacity: 1; opacity: 1;
} }
.layui-carousel[lay-anim='fade'] .layui-carousel[lay-anim="fade"]
> [carousel-item] > [carousel-item]
> .layui-this.layui-carousel-left, > .layui-this.layui-carousel-left,
.layui-carousel[lay-anim='fade'] .layui-carousel[lay-anim="fade"]
> [carousel-item] > [carousel-item]
> .layui-this.layui-carousel-right { > .layui-this.layui-carousel-right {
opacity: 0; opacity: 0;
@ -4078,7 +4078,7 @@ body .layui-util-face .layui-layer-content {
} }
.layui-slider-wrap:after { .layui-slider-wrap:after {
content: ''; content: "";
height: 100%; height: 100%;
display: inline-block; display: inline-block;
vertical-align: middle; vertical-align: middle;
@ -4110,7 +4110,7 @@ body .layui-util-face .layui-layer-content {
} }
.layui-slider-tips:after { .layui-slider-tips:after {
content: ''; content: "";
position: absolute; position: absolute;
bottom: -12px; bottom: -12px;
left: 50%; left: 50%;
@ -4252,7 +4252,7 @@ body .layui-util-face .layui-layer-content {
} }
.layui-tree-line .layui-tree-set .layui-tree-set:after { .layui-tree-line .layui-tree-set .layui-tree-set:after {
content: ''; content: "";
position: absolute; position: absolute;
top: 14px; top: 14px;
left: -9px; left: -9px;
@ -4288,7 +4288,7 @@ body .layui-util-face .layui-layer-content {
} }
.layui-tree-line .layui-tree-set:before { .layui-tree-line .layui-tree-set:before {
content: ''; content: "";
position: absolute; position: absolute;
top: 0; top: 0;
left: -9px; left: -9px;
@ -4335,7 +4335,7 @@ body .layui-util-face .layui-layer-content {
} }
.layui-tree-iconArrow:after { .layui-tree-iconArrow:after {
content: ''; content: "";
position: absolute; position: absolute;
left: 4px; left: 4px;
top: 3px; top: 3px;

View File

@ -1,74 +1,74 @@
import type { App } from 'vue' import type { App } from "vue";
import type { IDefineComponent, InstallOptions } from './module/type/index' import type { IDefineComponent, InstallOptions } from "./module/type/index";
import './css/layui.css'; import "./css/layui.css";
import '@layui/layer-vue/lib/index.css'; import "@layui/layer-vue/lib/index.css";
import '@layui/icons-vue/lib/index.css'; import "@layui/icons-vue/lib/index.css";
import { layer } from "@layui/layer-vue";
import { layer } from '@layui/layer-vue' import LayModal from "./module/layer/modal/index";
import LayBacktop from "./module/backTop/index";
import LayModal from './module/layer/modal/index' import LayAvatar from "./module/avatar/index";
import LayBacktop from './module/backTop/index' import LayRadio from "./module/radio/index";
import LayAvatar from './module/avatar/index' import LayButton from "./module/button/index";
import LayRadio from './module/radio/index' import LayButtonContainer from "./module/buttonContainer/index";
import LayButton from './module/button/index' import LayButtonGroup from "./module/buttonGroup/index";
import LayButtonContainer from './module/buttonContainer/index' import LayIcon from "./module/icon/index";
import LayButtonGroup from './module/buttonGroup/index' import LayIconPicker from "./module/iconPicker/index";
import LayIcon from './module/icon/index' import LayCard from "./module/card/index";
import LayIconPicker from './module/iconPicker/index' import LayLayout from "./module/layout/index";
import LayCard from './module/card/index' import LaySide from "./module/side/index";
import LayLayout from './module/layout/index' import LayBody from "./module/body/index";
import LaySide from './module/side/index' import LayHeader from "./module/header/index";
import LayBody from './module/body/index' import LayFooter from "./module/footer/index";
import LayHeader from './module/header/index' import LayLogo from "./module/logo/index";
import LayFooter from './module/footer/index' import LayPanel from "./module/panel/index";
import LayLogo from './module/logo/index' import LayProgress from "./module/progress/index";
import LayPanel from './module/panel/index' import LayCol from "./module/col/index";
import LayProgress from './module/progress/index' import LayRow from "./module/row/index";
import LayCol from './module/col/index' import LayInput from "./module/input/index";
import LayRow from './module/row/index' import LayBadge from "./module/badge/index";
import LayInput from './module/input/index' import LayBlock from "./module/block/index";
import LayBadge from './module/badge/index' import LayLine from "./module/line/index";
import LayBlock from './module/block/index' import LayTimeline from "./module/timeline/index";
import LayLine from './module/line/index' import LayTimelineItem from "./module/timelineItem/index";
import LayTimeline from './module/timeline/index' import LayTextarea from "./module/textarea/index";
import LayTimelineItem from './module/timelineItem/index' import LaySwitch from "./module/switch/index";
import LayTextarea from './module/textarea/index' import LayCollapse from "./module/collapse/index";
import LaySwitch from './module/switch/index' import LayCollapseItem from "./module/collapseItem/index";
import LayCollapse from './module/collapse/index' import LayContainer from "./module/container/index";
import LayCollapseItem from './module/collapseItem/index' import LayCountUp from "./module/countUp/index";
import LayContainer from './module/container/index' import LayMenu from "./module/menu/index";
import LayMenu from './module/menu/index' import LayMenuItem from "./module/menuItem/index";
import LayMenuItem from './module/menuItem/index' import LayMenuChildItem from "./module/menuChildItem/index";
import LayMenuChildItem from './module/menuChildItem/index' import LayCheckbox from "./module/checkbox/index";
import LayCheckbox from './module/checkbox/index' import LayCheckboxGroup from "./module/checkboxGroup/index";
import LayCheckboxGroup from './module/checkboxGroup/index' import LayForm from "./module/form/index";
import LayForm from './module/form/index' import LayBreadcrumb from "./module/breadcrumb/index";
import LayBreadcrumb from './module/breadcrumb/index' import LayBreadcrumbItem from "./module/breadcrumbItem/index";
import LayBreadcrumbItem from './module/breadcrumbItem/index' import LayField from "./module/field/index";
import LayField from './module/field/index' import LaySelect from "./module/select/index";
import LaySelect from './module/select/index' import LaySelectOption from "./module/selectOption/index";
import LaySelectOption from './module/selectOption/index' import LayScroll from "./module/scroll/index";
import LayScroll from './module/scroll/index' import LayEmpty from "./module/empty/index";
import LayEmpty from './module/empty/index' import LayFormItem from "./module/formItem/index";
import LayFormItem from './module/formItem/index' import LayRate from "./module/rate/index";
import LayRate from './module/rate/index' import LayDropdown from "./module/dropdown/index";
import LayDropdown from './module/dropdown/index' import LayDropdownItem from "./module/dropdownItem/index";
import LayDropdownItem from './module/dropdownItem/index' import LayTab from "./module/tab/index";
import LayTab from './module/tab/index' import LayTabItem from "./module/tabItem/index";
import LayTabItem from './module/tabItem/index' import LayTree from "./module/tree/index";
import LayTree from './module/tree/index' import LayTable from "./module/table/index";
import LayTable from './module/table/index' import LayPage from "./module/page/index";
import LayPage from './module/page/index' import LayTransfer from "./module/transfer/index";
import LayTransfer from './module/transfer/index' import LaySlider from "./module/slider/index";
import LaySlider from './module/slider/index' import LayCarousel from "./module/carousel/index";
import LayCarousel from './module/carousel/index' import LayCarouselItem from "./module/carouselItem/index";
import LayCarouselItem from './module/carouselItem/index' import LayColorPicker from "./module/colorPicker/index";
import LayColorPicker from './module/colorPicker/index' import LayTooltip from "./module/tooltip/index";
import LayTooltip from './module/tooltip/index' import LayInputNumber from "./module/inputNumber/index";
import LayInputNumber from './module/inputNumber/index' import LaySkeleton from './module/skeleton/index';
import LaySkeleton from './module/skeleton/index' import LaySkeletonItem from './module/skeletonItem/index';
import LaySkeletonItem from './module/skeletonItem/index'
const components: Record<string, IDefineComponent> = { const components: Record<string, IDefineComponent> = {
LayRadio, LayRadio,
@ -133,17 +133,17 @@ const components: Record<string, IDefineComponent> = {
LayInputNumber, LayInputNumber,
LaySkeleton, LaySkeleton,
LaySkeletonItem, LaySkeletonItem,
} LayCountUp,
};
const install = (app: App, options?: InstallOptions): void => { const install = (app: App, options?: InstallOptions): void => {
const _options = options const _options = options;
app.config.globalProperties.$PROOPTIONS = _options app.config.globalProperties.$PROOPTIONS = _options;
for (const key in components) { for (const key in components) {
const item = components[key] const item = components[key];
app.component(item.name || key, item) app.component(item.name || key, item);
} }
} };
export { export {
LaySkeleton, LaySkeleton,
@ -205,9 +205,9 @@ export {
LayCarousel, LayCarousel,
LayCarouselItem, LayCarouselItem,
LayColorPicker, LayColorPicker,
LayModal, LayModal
install, };
layer
}
export default { install } export { layer };
export default { install };

View File

@ -1,42 +1,42 @@
.layui-avatar { .layui-avatar {
box-sizing: border-box; box-sizing: border-box;
margin: 0; margin: 0;
padding: 0; padding: 0;
color: #000000d9; color: #000000d9;
font-size: 14px; font-size: 14px;
font-variant: tabular-nums; font-variant: tabular-nums;
line-height: 1.5715; line-height: 1.5715;
list-style: none; list-style: none;
font-feature-settings: tnum; font-feature-settings: tnum;
position: relative; position: relative;
display: inline-block; display: inline-block;
overflow: hidden; overflow: hidden;
color: #fff; color: #fff;
white-space: nowrap; white-space: nowrap;
text-align: center; text-align: center;
vertical-align: middle; vertical-align: middle;
background: #ccc; background: #ccc;
width: 32px; width: 32px;
height: 32px; height: 32px;
line-height: 32px; line-height: 32px;
border-radius: 4px; border-radius: 4px;
} }
.layui-avatar.layui-avatar-radius { .layui-avatar.layui-avatar-radius {
border-radius: 50%; border-radius: 50%;
} }
.layui-avatar.layui-avatar-sm { .layui-avatar.layui-avatar-sm {
height: 30px; height: 30px;
width: 30px; width: 30px;
} }
.layui-avatar.layui-avatar-lg { .layui-avatar.layui-avatar-lg {
height: 36px; height: 36px;
width: 36px; width: 36px;
} }
.layui-avatar.layui-avatar-xs { .layui-avatar.layui-avatar-xs {
height: 28px; height: 28px;
width: 28px; width: 28px;
} }

View File

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

View File

@ -1,18 +1,18 @@
<script lang="ts"> <script lang="ts">
export default { export default {
name: "LayAvatar" name: "LayAvatar",
} };
</script> </script>
<script setup lang="ts"> <script setup lang="ts">
import { defineProps } from 'vue' import { defineProps } from "vue";
import "./index.less" import "./index.less";
const props = defineProps<{ const props = defineProps<{
src?: String src?: String;
radius?: boolean radius?: boolean;
size?: string size?: string;
}>() }>();
</script> </script>
<template> <template>
@ -24,4 +24,4 @@ const props = defineProps<{
size ? 'layui-avatar-' + size : '', size ? 'layui-avatar-' + size : '',
]" ]"
/> />
</template> </template>

View File

@ -1,10 +1,13 @@
/** backtop **/ /** backtop **/
@width: 50px;
@height: @width;
.layui-backtop { .layui-backtop {
position: fixed; position: fixed;
right: 30px; right: 30px;
bottom: 40px; bottom: 40px;
width: 50px; width: @width;
height: 50px; height: @height;
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: center; justify-content: center;
@ -19,3 +22,15 @@
opacity: 0.85; opacity: 0.85;
} }
} }
.layui-backtop-medium{
width: @width - 10px;
height: @height - 10px;
font-size: 30px;
}
.layui-backtop-small{
width: @width - 20px;
height: @height - 20px;
font-size: 20px;
}

View File

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

View File

@ -3,6 +3,7 @@
v-show="visible" v-show="visible"
ref="backtopRef" ref="backtopRef"
class="layui-backtop" class="layui-backtop"
:class="classBacktop"
:style="{ ...styleBacktop }" :style="{ ...styleBacktop }"
@click.stop="handleClick" @click.stop="handleClick"
@mousedown="handlerMousedown" @mousedown="handlerMousedown"
@ -12,23 +13,30 @@
<lay-icon <lay-icon
:type="props.icon" :type="props.icon"
:size="`${props.iconSize}px`" :size="`${props.iconSize}px`"
:prefix="props.iconPrefix"
:color="props.iconColor" :color="props.iconColor"
></lay-icon> />
</slot> </slot>
</div> </div>
</template> </template>
<script lang="ts"> <script lang="ts">
export default { export default {
name: 'LayBacktop', name: "LayBacktop",
}; };
</script> </script>
<script lang="ts" setup> <script lang="ts" setup>
import { defineProps, defineEmits, ref, shallowRef, withDefaults, computed, onMounted, } from 'vue'; import {
import LayIcon from '../icon/index'; defineProps,
import './index.less'; defineEmits,
ref,
shallowRef,
withDefaults,
computed,
onMounted,
} from "vue";
import LayIcon from "../icon/index";
import "./index.less";
export interface LayBacktopProps { export interface LayBacktopProps {
/**通用*/ /**通用*/
@ -36,42 +44,52 @@ export interface LayBacktopProps {
showHeight?: number; showHeight?: number;
disabled?: boolean; disabled?: boolean;
/**组件样式*/ /**组件样式*/
position?: 'fixed' | 'absolute'; position?: "fixed" | "absolute";
right?: number; right?: number;
bottom?: number; bottom?: number;
size?: "medium" | "small";
bgcolor?: string; bgcolor?: string;
opacity?: number; opacity?: number;
color?: string; color?: string;
borderRadius?: number | string; borderRadius?: number | string;
circular?: boolean; circle?: boolean;
/**图标样式*/ /**图标样式*/
icon?: string; icon?: string;
iconSize?: number; iconSize?: number;
iconPrefix?: string;
iconColor?: string; iconColor?: string;
} }
const props = withDefaults(defineProps<LayBacktopProps>(), { const props = withDefaults(defineProps<LayBacktopProps>(), {
target: 'window', target: "window",
showHeight: 200, showHeight: 200,
right: 30, icon: "layui-icon-top",
bottom: 40,
icon: 'layui-icon-top',
iconSize: 30, iconSize: 30,
iconPrefix: 'layui-icon',
disabled: false, disabled: false,
circular: false, circle: false,
}); });
const emit = defineEmits(['click']); const emit = defineEmits(["click"]);
const backtopRef = ref<HTMLElement | null>(null); const backtopRef = ref<HTMLElement | null>(null);
const scrollTarget = shallowRef<Window | HTMLElement | undefined>(undefined); const scrollTarget = shallowRef<Window | HTMLElement | undefined>(undefined);
let visible = ref(props.showHeight === 0); let visible = ref(props.showHeight === 0);
const borderRadius = computed(() => {
if (props.circular) return "50%"; const classBacktop = computed(() => {
return typeof props.borderRadius === 'number' ? `${props.borderRadius}px` : props.borderRadius; return {
'layui-backtop-medium': props.size === "medium",
'layui-backtop-small': props.size === "small"
}
}); });
const borderRadius = computed(() => {
if (props.circle) {
return "50%"
};
return typeof props.borderRadius === "number"
? `${props.borderRadius}px`
: props.borderRadius;
});
const styleBacktop = computed(() => { const styleBacktop = computed(() => {
return { return {
position: props.position, position: props.position,
@ -81,72 +99,119 @@ const styleBacktop = computed(() => {
opacity: props.opacity, opacity: props.opacity,
color: props.color, color: props.color,
borderRadius: borderRadius.value, borderRadius: borderRadius.value,
} };
}); });
// TODO // TODO
const easeInOut = (value: number): number => {
return value < 0.5 ? 2 * value * value : 1 - 2 * (value - 1) * (value - 1);
};
const scrollToTop = () => { const scrollToTop = () => {
if (!scrollTarget.value) return; if (!scrollTarget.value) return;
if (scrollTarget.value instanceof Window) { if (scrollTarget.value instanceof Window) {
window.scrollTo({ top: 0, left: 0, behavior: 'smooth' }); // smooth | instant(default) window.scrollTo({ top: 0, left: 0, behavior: "smooth" }); // smooth | instant(default)
} else { } else {
let step = scrollTarget.value.scrollTop / 4; const previous: number = Date.now();
if (scrollTarget.value.scrollTop > 0) { const scrollHeight: number = scrollTarget.value.scrollTop;
scrollTarget.value.scrollTop -= Math.max(step, 10); const animationFunc = () => {
setTimeout(() => { if (!scrollTarget.value || scrollTarget.value instanceof Window) return;
scrollToTop(); const elapsed = (Date.now() - previous) / 450;
}, 1000 / 60); if (elapsed < 1) {
} scrollTarget.value.scrollTop = scrollHeight * (1 - easeInOut(elapsed));
window.requestAnimationFrame(animationFunc);
} else {
scrollTarget.value.scrollTop = 0;
}
};
window.requestAnimationFrame(animationFunc);
} }
}; };
const handleScroll = () => { const handleScroll = () => {
if (!scrollTarget.value) return; if (!scrollTarget.value) return;
const scrollTop = scrollTarget.value instanceof Window ? window.pageYOffset : scrollTarget.value.scrollTop; const scrollTop =
scrollTarget.value instanceof Window
? window.pageYOffset
: scrollTarget.value.scrollTop;
visible.value = scrollTop >= props.showHeight; visible.value = scrollTop >= props.showHeight;
}; };
const handleClick = (event: MouseEvent) => { const handleClick = (event: MouseEvent) => {
if (!props.disabled) { if (!props.disabled) {
scrollToTop() scrollToTop();
}; }
emit('click', event); emit("click", event);
}; };
const handlerMousedown = () => { const handlerMousedown = () => {
backtopRef.value!.style.opacity = '1'; backtopRef.value!.style.opacity = "1";
} };
const handlerMouseup = () => { const handlerMouseup = () => {
backtopRef.value!.style.opacity = '0.95'; backtopRef.value!.style.opacity = "0.95";
} };
// //
const getScrollTarget = () => { const getScrollTarget = () => {
if (props.target === 'window') { if (props.target === "window") {
return window || document.documentElement || document.body; return getScrollParent(backtopRef.value!, false);
} else { } else {
const targetElement = document.querySelector<HTMLElement>(props.target); const targetElement = document.querySelector<HTMLElement>(props.target);
if (!targetElement) throw new Error(`target is not existed: ${props.target}`); if (!targetElement){
throw new Error(`target is not existed: ${props.target}`);
}
// //
if (props.position === 'absolute') { if (props.position === "absolute") {
if (!targetElement.parentElement) throw new Error(`target parent element is not existed: ${props.target}`); if (!targetElement.parentElement){
targetElement.parentElement.style.position = 'relative'; throw new Error( `target parent element is not existed: ${props.target}`);
}
targetElement.parentElement.style.position = "relative";
// backtopRef.value!.style.position = props.position; // backtopRef.value!.style.position = props.position;
} }
return targetElement; return targetElement;
} }
}; };
//
const getScrollParent = (
element: HTMLElement,
includeHidden: boolean
): HTMLElement => {
let style: CSSStyleDeclaration = getComputedStyle(element);
let excludeStaticParent: boolean = style.position === "absolute";
let overflowRegex: RegExp = includeHidden
? /(auto|scroll|hidden)/
: /(auto|scroll)/;
//if (style.position === "fixed") return document.documentElement || document.body || window;
for (let parent: HTMLElement = element; (parent = parent.parentElement!); ) {
style = getComputedStyle(parent);
if (excludeStaticParent && style.position === "static") {
continue;
}
if (overflowRegex.test(style.overflow + style.overflowY + style.overflowX)){
return parent;
}
}
return document.documentElement || document.body || window;
};
//
const throttle = (func: Function, wait: number) => {
var timer: any = null;
return (...args: any) => {
if (!timer) {
timer = setTimeout(() => {
timer = null;
func.apply(this, args);
}, wait);
}
};
};
onMounted(() => { onMounted(() => {
let timer: any = undefined; if (!props.target) return;
scrollTarget.value = getScrollTarget(); scrollTarget.value = getScrollTarget();
// TODO scrollTarget.value.addEventListener("scroll", throttle(handleScroll, 300));
scrollTarget.value.addEventListener('scroll', () => {
clearTimeout(timer);
timer = setTimeout(() => {
handleScroll();
}, 100);
});
}); });
</script> </script>

View File

@ -1,33 +1,33 @@
.layui-badge, .layui-badge,
.layui-badge-dot, .layui-badge-dot,
.layui-badge-rim { .layui-badge-rim {
position: relative; position: relative;
display: inline-block; display: inline-block;
padding: 0 6px; padding: 0 6px;
font-size: 12px; font-size: 12px;
text-align: center; text-align: center;
background-color: #ff5722; background-color: #ff5722;
color: #fff; color: #fff;
border-radius: 2px; border-radius: 2px;
} }
.layui-badge { .layui-badge {
height: 18px; height: 18px;
line-height: 18px; line-height: 18px;
} }
.layui-badge-dot { .layui-badge-dot {
width: 8px; width: 8px;
height: 8px; height: 8px;
padding: 0; padding: 0;
border-radius: 50%; border-radius: 50%;
} }
.layui-badge-rim { .layui-badge-rim {
height: 18px; height: 18px;
line-height: 18px; line-height: 18px;
border-width: 1px; border-width: 1px;
border-style: solid; border-style: solid;
background-color: #fff; background-color: #fff;
color: #666; color: #666;
} }

View File

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

View File

@ -33,6 +33,6 @@ const styles = computed(() => {
<template> <template>
<span :class="classes" :style="styles"> <span :class="classes" :style="styles">
<slot v-if="type != 'dot'" /> <slot v-if="type != 'dot'"></slot>
</span> </span>
</template> </template>

View File

@ -1,14 +1,14 @@
.layui-elem-quote { .layui-elem-quote {
margin-bottom: 10px; margin-bottom: 10px;
padding: 15px; padding: 15px;
line-height: 1.6; line-height: 1.6;
border-left: 5px solid #5fb878; border-left: 5px solid #5fb878;
border-radius: 0 2px 2px 0; border-radius: 0 2px 2px 0;
background-color: #fafafa; background-color: #fafafa;
} }
.layui-quote-nm { .layui-quote-nm {
border-style: solid; border-style: solid;
border-width: 1px 1px 1px 5px; border-width: 1px 1px 1px 5px;
background: 0 0; background: 0 0;
} }

View File

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

View File

@ -6,7 +6,7 @@ export default {
<script setup lang="ts"> <script setup lang="ts">
import { defineProps } from "vue"; import { defineProps } from "vue";
import "./index.less" import "./index.less";
const props = defineProps<{ const props = defineProps<{
nm?: boolean; nm?: boolean;
@ -15,6 +15,6 @@ const props = defineProps<{
<template> <template>
<blockquote class="layui-elem-quote" :class="{ 'layui-quote-nm': nm }"> <blockquote class="layui-elem-quote" :class="{ 'layui-quote-nm': nm }">
<slot /> <slot></slot>
</blockquote> </blockquote>
</template> </template>

View File

@ -1,8 +1,8 @@
.layui-body { .layui-body {
display: block; display: block;
flex: 1; flex: 1;
overflow: auto; overflow: auto;
height: 100%; height: 100%;
box-sizing: border-box; box-sizing: border-box;
min-height: 300px; min-height: 300px;
} }

View File

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

View File

@ -1,15 +1,15 @@
<script lang="ts"> <script lang="ts">
export default { export default {
name: 'LayBody', name: "LayBody",
} };
</script> </script>
<script setup name="LayBody" lang="ts"> <script setup name="LayBody" lang="ts">
import "./index.less" import "./index.less";
</script> </script>
<template> <template>
<div class="layui-body"> <div class="layui-body">
<slot /> <slot></slot>
</div> </div>
</template> </template>

View File

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

View File

@ -5,16 +5,16 @@
</template> </template>
<script setup name="LayBreadcrumb" lang="ts"> <script setup name="LayBreadcrumb" lang="ts">
import { defineProps, provide, withDefaults } from 'vue' import { defineProps, provide, withDefaults } from "vue";
const props = withDefaults( const props = withDefaults(
defineProps<{ defineProps<{
separator?: string separator?: string;
}>(), }>(),
{ {
separator: '/', separator: "/",
} }
) );
provide('separator', props.separator) provide("separator", props.separator);
</script> </script>

View File

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

View File

@ -11,13 +11,13 @@
</template> </template>
<script setup name="LayBreadcrumbItem" lang="ts"> <script setup name="LayBreadcrumbItem" lang="ts">
import { defineProps, inject, useSlots } from 'vue' import { defineProps, inject, useSlots } from "vue";
const slot = useSlots() const slot = useSlots();
const props = defineProps<{ const props = defineProps<{
title?: string title?: string;
}>() }>();
const separator = inject('separator') const separator = inject("separator");
</script> </script>

View File

@ -1,104 +1,104 @@
.layui-btn { .layui-btn {
height: 38px; height: 38px;
line-height: 36px; line-height: 36px;
border: 1px solid transparent; border: 1px solid transparent;
padding: 0 18px; padding: 0 18px;
background-color: #009688; background-color: #009688;
color: #fff; color: #fff;
white-space: nowrap; white-space: nowrap;
text-align: center; text-align: center;
font-size: 14px; font-size: 14px;
border-radius: 2px; border-radius: 2px;
cursor: pointer; cursor: pointer;
} }
.layui-btn:hover { .layui-btn:hover {
opacity: 0.8; opacity: 0.8;
filter: alpha(opacity=80); filter: alpha(opacity=80);
color: #fff; color: #fff;
} }
.layui-btn:active { .layui-btn:active {
opacity: 1; opacity: 1;
filter: alpha(opacity=100); filter: alpha(opacity=100);
} }
.layui-btn+.layui-btn { .layui-btn + .layui-btn {
margin-left: 10px; margin-left: 10px;
} }
.layui-btn-radius { .layui-btn-radius {
border-radius: 100px; border-radius: 100px;
} }
.layui-btn .layui-icon { .layui-btn .layui-icon {
padding: 0 2px; padding: 0 2px;
vertical-align: middle\9; vertical-align: middle\9;
vertical-align: bottom; vertical-align: bottom;
} }
.layui-btn-primary { .layui-btn-primary {
border-color: #d2d2d2; border-color: #d2d2d2;
background: 0 0; background: 0 0;
color: #666; color: #666;
} }
.layui-btn-primary:hover { .layui-btn-primary:hover {
border-color: #009688; border-color: #009688;
color: #333; color: #333;
} }
.layui-btn-normal { .layui-btn-normal {
background-color: #1e9fff; background-color: #1e9fff;
} }
.layui-btn-warm { .layui-btn-warm {
background-color: #ffb800; background-color: #ffb800;
} }
.layui-btn-danger { .layui-btn-danger {
background-color: #ff5722; background-color: #ff5722;
} }
.layui-btn-checked { .layui-btn-checked {
background-color: #5fb878; background-color: #5fb878;
} }
.layui-btn-disabled, .layui-btn-disabled,
.layui-btn-disabled:active, .layui-btn-disabled:active,
.layui-btn-disabled:hover { .layui-btn-disabled:hover {
border-color: #eee !important; border-color: #eee !important;
background-color: #fbfbfb !important; background-color: #fbfbfb !important;
color: #d2d2d2 !important; color: #d2d2d2 !important;
cursor: not-allowed !important; cursor: not-allowed !important;
opacity: 1; opacity: 1;
} }
.layui-btn-lg { .layui-btn-lg {
height: 44px; height: 44px;
line-height: 44px; line-height: 44px;
padding: 0 25px; padding: 0 25px;
font-size: 16px; font-size: 16px;
} }
.layui-btn-sm { .layui-btn-sm {
height: 30px; height: 30px;
line-height: 30px; line-height: 30px;
padding: 0 10px; padding: 0 10px;
font-size: 12px; font-size: 12px;
} }
.layui-btn-xs { .layui-btn-xs {
height: 22px; height: 22px;
line-height: 22px; line-height: 22px;
padding: 0 5px; padding: 0 5px;
font-size: 12px; font-size: 12px;
} }
.layui-btn-xs i { .layui-btn-xs i {
font-size: 12px !important; font-size: 12px !important;
} }
.layui-btn-fluid { .layui-btn-fluid {
width: 100%; width: 100%;
} }

View File

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

View File

@ -51,14 +51,8 @@ const classes = computed(() => {
> >
<i <i
v-if="loading" v-if="loading"
class=" class="layui-icon layui-icon-loading-one layui-anim layui-anim-rotate layui-anim-loop"
layui-icon
layui-icon-loading-one
layui-anim
layui-anim-rotate
layui-anim-loop
"
></i> ></i>
<slot v-else /> <slot v-else></slot>
</button> </button>
</template> </template>

View File

@ -1,16 +1,16 @@
.layui-btn-container { .layui-btn-container {
font-size: 0; font-size: 0;
} }
.layui-btn-container .layui-btn { .layui-btn-container .layui-btn {
margin-right: 10px; margin-right: 10px;
margin-bottom: 10px; margin-bottom: 10px;
} }
.layui-btn-container .layui-btn+.layui-btn { .layui-btn-container .layui-btn + .layui-btn {
margin-left: 0; margin-left: 0;
} }
.layui-table .layui-btn-container .layui-btn { .layui-table .layui-btn-container .layui-btn {
margin-bottom: 9px; margin-bottom: 9px;
} }

View File

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

View File

@ -1,6 +1,6 @@
<template> <template>
<div class="layui-btn-container"> <div class="layui-btn-container">
<slot /> <slot></slot>
</div> </div>
</template> </template>

View File

@ -1,41 +1,41 @@
.layui-btn-group { .layui-btn-group {
vertical-align: middle; vertical-align: middle;
font-size: 0; font-size: 0;
} }
.layui-btn-group .layui-btn { .layui-btn-group .layui-btn {
margin-left: 0 !important; margin-left: 0 !important;
margin-right: 0 !important; margin-right: 0 !important;
border-left: 1px solid rgba(255, 255, 255, 0.5); border-left: 1px solid rgba(255, 255, 255, 0.5);
border-radius: 0; border-radius: 0;
} }
.layui-btn-group .layui-btn-primary { .layui-btn-group .layui-btn-primary {
border-left: none; border-left: none;
} }
.layui-btn-group .layui-btn-primary:hover { .layui-btn-group .layui-btn-primary:hover {
border-color: #d2d2d2; border-color: #d2d2d2;
color: #009688; color: #009688;
} }
.layui-btn-group .layui-btn:first-child { .layui-btn-group .layui-btn:first-child {
border-left: none; border-left: none;
border-radius: 2px 0 0 2px; border-radius: 2px 0 0 2px;
} }
.layui-btn-group .layui-btn-primary:first-child { .layui-btn-group .layui-btn-primary:first-child {
border-left: 1px solid #d2d2d2; border-left: 1px solid #d2d2d2;
} }
.layui-btn-group .layui-btn:last-child { .layui-btn-group .layui-btn:last-child {
border-radius: 0 2px 2px 0; border-radius: 0 2px 2px 0;
} }
.layui-btn-group .layui-btn+.layui-btn { .layui-btn-group .layui-btn + .layui-btn {
margin-left: 0; margin-left: 0;
} }
.layui-btn-group+.layui-btn-group { .layui-btn-group + .layui-btn-group {
margin-left: 10px; margin-left: 10px;
} }

View File

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

View File

@ -5,11 +5,11 @@ export default {
</script> </script>
<script setup lang="ts"> <script setup lang="ts">
import "./index.less" import "./index.less";
</script> </script>
<template> <template>
<div class="layui-btn-group"> <div class="layui-btn-group">
<slot /> <slot></slot>
</div> </div>
</template> </template>

View File

@ -1,41 +1,41 @@
.layui-card { .layui-card {
margin-bottom: 15px; margin-bottom: 15px;
border-radius: 2px; border-radius: 2px;
background-color: #fff; background-color: #fff;
box-shadow: 0 1px 2px 0 rgba(0, 0, 0, 0.05); box-shadow: 0 1px 2px 0 rgba(0, 0, 0, 0.05);
} }
.layui-card:last-child { .layui-card:last-child {
margin-bottom: 0; margin-bottom: 0;
} }
.layui-card-header { .layui-card-header {
height: 42px; height: 42px;
line-height: 42px; line-height: 42px;
padding: 0 15px; padding: 0 15px;
border-bottom: 1px solid #f6f6f6; border-bottom: 1px solid #f6f6f6;
color: #333; color: #333;
border-radius: 2px 2px 0 0; border-radius: 2px 2px 0 0;
font-size: 14px; font-size: 14px;
} }
.layui-card-body { .layui-card-body {
padding: 10px 15px; padding: 10px 15px;
line-height: 24px; line-height: 24px;
} }
.layui-card-body[pad15] { .layui-card-body[pad15] {
padding: 15px; padding: 15px;
} }
.layui-card-body[pad20] { .layui-card-body[pad20] {
padding: 20px; padding: 20px;
} }
.layui-card-body .layui-table { .layui-card-body .layui-table {
margin: 5px 0; margin: 5px 0;
} }
.layui-card .layui-tab { .layui-card .layui-tab {
margin: 0; margin: 0;
} }

View File

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

View File

@ -1,11 +1,11 @@
<template> <template>
<div class="layui-card"> <div class="layui-card">
<div class="layui-card-header" v-if="slot.header || title"> <div class="layui-card-header" v-if="slot.header || title">
<slot name="header" v-if="slot.header" /> <slot name="header" v-if="slot.header"></slot>
<span v-else>{{ title }}</span> <span v-else>{{ title }}</span>
</div> </div>
<div class="layui-card-body"> <div class="layui-card-body">
<slot name="body" v-if="slot.body" /> <slot name="body" v-if="slot.body"></slot>
<slot v-else></slot> <slot v-else></slot>
</div> </div>
</div> </div>

View File

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

View File

@ -24,13 +24,13 @@
lay-type="sub" lay-type="sub"
@click="prev" @click="prev"
> >
{{ anim === 'updown' ? '' : '' }}</button {{ anim === "updown" ? "" : "" }}</button
><button ><button
class="layui-icon layui-carousel-arrow" class="layui-icon layui-carousel-arrow"
lay-type="add" lay-type="add"
@click="next" @click="next"
> >
{{ anim === 'updown' ? '' : '' }} {{ anim === "updown" ? "" : "" }}
</button> </button>
</div> </div>
</template> </template>
@ -42,68 +42,68 @@ import {
useSlots, useSlots,
ref, ref,
computed, computed,
} from 'vue' } from "vue";
const slot = useSlots() as any const slot = useSlots() as any;
const slots = slot.default && (slot.default() as any[]) const slots = slot.default && (slot.default() as any[]);
const props = withDefaults( const props = withDefaults(
defineProps<{ defineProps<{
width?: string width?: string;
height?: string height?: string;
modelValue: string modelValue: string;
anim?: string anim?: string;
arrow?: string arrow?: string;
indicator?: string indicator?: string;
}>(), }>(),
{ {
width: '100%', width: "100%",
height: '280px', height: "280px",
anim: 'default', anim: "default",
arrow: 'hover', arrow: "hover",
indicator: 'inside', indicator: "inside",
} }
) );
const active = computed({ const active = computed({
get() { get() {
return props.modelValue return props.modelValue;
}, },
set(val) { set(val) {
emit('update:modelValue', val) emit("update:modelValue", val);
}, },
}) });
const emit = defineEmits(['update:modelValue', 'change']) const emit = defineEmits(["update:modelValue", "change"]);
const change = function (id: any) { const change = function (id: any) {
emit('change', id) emit("change", id);
active.value = id active.value = id;
} };
provide('active', active) provide("active", active);
const prev = function () { const prev = function () {
for (var i = 0; i < slots.length; i++) { for (var i = 0; i < slots.length; i++) {
if (slots[i].props.id === active.value) { if (slots[i].props.id === active.value) {
if (i === 0) { if (i === 0) {
active.value = slots[slots.length - 1].props.id active.value = slots[slots.length - 1].props.id;
} }
active.value = slots[i - 1].props.id active.value = slots[i - 1].props.id;
break break;
} }
} }
} };
const next = function () { const next = function () {
for (var i = 0; i < slots.length; i++) { for (var i = 0; i < slots.length; i++) {
if (slots[i].props.id === active.value) { if (slots[i].props.id === active.value) {
if (i === slots.length - 1) { if (i === slots.length - 1) {
active.value = slots[0].props.id active.value = slots[0].props.id;
} }
active.value = slots[i + 1].props.id active.value = slots[i + 1].props.id;
break break;
} }
} }
} };
</script> </script>

View File

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

View File

@ -4,11 +4,11 @@
</li> </li>
</template> </template>
<script setup name="LayCarouselItem" lang="ts"> <script setup name="LayCarouselItem" lang="ts">
import { defineProps, inject } from 'vue' import { defineProps, inject } from "vue";
const props = defineProps<{ const props = defineProps<{
id: string id: string;
}>() }>();
const active = inject('active') const active = inject("active");
</script> </script>

View File

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

View File

@ -12,7 +12,7 @@ export interface LayCheckboxProps {
name?: string; name?: string;
skin?: string; skin?: string;
label: string | object; label: string | object;
modelValue: boolean | Array<string | object>; modelValue?: boolean | Array<string | object>;
disabled?: boolean; disabled?: boolean;
} }
@ -104,8 +104,8 @@ const handleClick = function () {
}" }"
:lay-skin="skin" :lay-skin="skin"
> >
<span v-if="$slots?.default"><slot /></span> <span v-if="$slots?.default"><slot></slot></span>
<i class="layui-icon layui-icon-ok" /> <i class="layui-icon layui-icon-ok"></i>
</div> </div>
</span> </span>
</template> </template>

View File

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

View File

@ -4,7 +4,6 @@
</div> </div>
</template> </template>
<script lang="ts"> <script lang="ts">
export default { export default {
name: "LayCheckboxGroup", name: "LayCheckboxGroup",

View File

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

View File

@ -22,7 +22,7 @@ const classes = computed(() => {
return [ return [
props.md ? `layui-col-md${props.md}` : "", props.md ? `layui-col-md${props.md}` : "",
props.xs ? `layui-col-xs${props.xs}` : "", props.xs ? `layui-col-xs${props.xs}` : "",
props.sm ? `layui-col-sm${props.sm}` : "", props.sm ? `layui-col-sm${props.sm}` : "",
props.lg ? `layui-col-lg${props.lg}` : "", props.lg ? `layui-col-lg${props.lg}` : "",
props.mdOffset ? `layui-col-md-offset${props.mdOffset}` : "", props.mdOffset ? `layui-col-md-offset${props.mdOffset}` : "",
props.xsOffset ? `layui-col-xs-offset${props.xsOffset}` : "", props.xsOffset ? `layui-col-xs-offset${props.xsOffset}` : "",
@ -34,6 +34,6 @@ const classes = computed(() => {
<template> <template>
<div class="layui-col" :class="classes"> <div class="layui-col" :class="classes">
<slot /> <slot></slot>
</div> </div>
</template> </template>

View File

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

View File

@ -1,12 +1,19 @@
<template> <template>
<div class="layui-collapse"> <div class="layui-collapse">
<slot /> <slot></slot>
</div> </div>
</template> </template>
<script setup name="LayCollapse"></script> <script setup name="LayCollapse"></script>
<script setup lang="ts"> <script setup lang="ts">
import { withDefaults, defineProps, provide, ref, defineEmits, watch } from 'vue' import {
withDefaults,
defineProps,
provide,
ref,
defineEmits,
watch,
} from "vue";
const props = withDefaults( const props = withDefaults(
defineProps<{ defineProps<{
@ -15,25 +22,24 @@ const props = withDefaults(
}>(), }>(),
{ {
modelValue: () => [], modelValue: () => [],
accordion: false accordion: false,
} }
) );
// //
watch( watch(
() => props.modelValue, () => props.modelValue,
(val, oldVal)=>{ (val, oldVal) => {
activeValues.value = ([] as any[]).concat(val) activeValues.value = ([] as any[]).concat(val);
} }
) );
const emit = defineEmits(["update:modelValue", "change"]) const emit = defineEmits(["update:modelValue", "change"]);
const activeValues = ref<Array<any>>(([] as any[]).concat(props.modelValue)); const activeValues = ref<Array<any>>(([] as any[]).concat(props.modelValue));
provide("layCollapse", { provide("layCollapse", {
accordion : props.accordion, accordion: props.accordion,
activeValues, activeValues,
emit emit,
}) });
</script> </script>

View File

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

View File

@ -1,50 +1,60 @@
<template> <template>
<div class="layui-colla-item"> <div class="layui-colla-item">
<h2 :class="['layui-colla-title', {'layui-disabled' : disabled}]" @click="showHandle"> <h2
<slot name="title" :props="props">{{ title}}</slot> :class="['layui-colla-title', { 'layui-disabled': disabled }]"
<i class="layui-icon layui-colla-icon">{{ isShow ? '' : '' }}</i> @click="showHandle"
>
<slot name="title" :props="props">{{ title }}</slot>
<i class="layui-icon layui-colla-icon">{{ isShow ? "" : "" }}</i>
</h2> </h2>
<div class="layui-colla-content" :class="isShow ? 'layui-show' : ''"> <div class="layui-colla-content" :class="isShow ? 'layui-show' : ''">
<p> <p>
<slot :props="props"/> <slot :props="props"></slot>
</p> </p>
</div> </div>
</div> </div>
</template> </template>
<script setup name="LayCollapseItem" lang="ts"> <script setup name="LayCollapseItem" lang="ts">
import { withDefaults, defineProps, inject, computed, ref } from 'vue' import { withDefaults, defineProps, inject, computed, ref } from "vue";
const props = withDefaults( const props = withDefaults(
defineProps<{ defineProps<{
id: number | string id: number | string;
title: string title: string;
disabled?: boolean disabled?: boolean;
}>(), }>(),
{ {
disabled: false disabled: false,
} }
) );
const {accordion, activeValues, emit} = inject('layCollapse') as any const { accordion, activeValues, emit } = inject("layCollapse") as any;
let isShow = computed(()=>{return activeValues.value.includes(props.id)}) let isShow = computed(() => {
return activeValues.value.includes(props.id);
});
const showHandle = function () { const showHandle = function () {
if (props.disabled) { if (props.disabled) {
return ; return;
} }
const _isShow = isShow.value; const _isShow = isShow.value;
// //
if (accordion) { if (accordion) {
activeValues.value = !_isShow ? [props.id] : []; activeValues.value = !_isShow ? [props.id] : [];
} else if (_isShow) { // --> } else if (_isShow) {
activeValues.value.splice(activeValues.value.indexOf(props.id), 1) // -->
} else { // --> activeValues.value.splice(activeValues.value.indexOf(props.id), 1);
activeValues.value.push(props.id) } else {
// -->
activeValues.value.push(props.id);
} }
emit("update:modelValue", (accordion ? activeValues.value[0] || null : activeValues.value)); emit(
"update:modelValue",
accordion ? activeValues.value[0] || null : activeValues.value
);
emit("change", props.id, !_isShow, activeValues.value); emit("change", props.id, !_isShow, activeValues.value);
} };
</script> </script>

View File

@ -1,86 +1,86 @@
<script lang="ts"> <script lang="ts">
export default { export default {
name: 'ColorBox', name: "ColorBox",
} };
</script> </script>
<script setup lang="ts"> <script setup lang="ts">
import { Nullable } from '../type' import { Nullable } from "../type";
import { computed, onMounted, ref } from 'vue' import { computed, onMounted, ref } from "vue";
import { HSBToHEX, RGBSTo, RGBToHSB } from './colorUtil' import { HSBToHEX, RGBSTo, RGBToHSB } from "./colorUtil";
import ColorPicker from './ColorPicker.vue' import ColorPicker from "./ColorPicker.vue";
import { usePosition } from '@layui/hooks-vue' import { usePosition } from "@layui/hooks-vue";
interface BoxProps { interface BoxProps {
color?: string color?: string;
size?: Nullable<string> size?: Nullable<string>;
alpha?: boolean alpha?: boolean;
format?: 'hex' | 'rgb' format?: "hex" | "rgb";
predefine?: boolean predefine?: boolean;
colors?: string[] colors?: string[];
} }
const colorBoxProps = withDefaults(defineProps<BoxProps>(), { const colorBoxProps = withDefaults(defineProps<BoxProps>(), {
color: '', color: "",
size: () => null, size: () => null,
alpha: false, alpha: false,
format: 'hex', format: "hex",
predefine: false, predefine: false,
colors: () => [ colors: () => [
// //
'#009688', "#009688",
'#5FB878', "#5FB878",
'#1E9FFF', "#1E9FFF",
'#FF5722', "#FF5722",
'#FFB800', "#FFB800",
'#01AAED', "#01AAED",
'#999', "#999",
'#c00', "#c00",
'#ff8c00', "#ff8c00",
'#ffd700', "#ffd700",
'#90ee90', "#90ee90",
'#00ced1', "#00ced1",
'#1e90ff', "#1e90ff",
'#c71585', "#c71585",
'rgb(0, 186, 189)', "rgb(0, 186, 189)",
'rgb(255, 120, 0)', "rgb(255, 120, 0)",
'rgb(250, 212, 0)', "rgb(250, 212, 0)",
'#393D49', "#393D49",
'rgba(0,0,0,.5)', "rgba(0,0,0,.5)",
'rgba(255, 69, 0, 0.68)', "rgba(255, 69, 0, 0.68)",
'rgba(144, 240, 144, 0.5)', "rgba(144, 240, 144, 0.5)",
'rgba(31, 147, 255, 0.73)', "rgba(31, 147, 255, 0.73)",
], ],
}) });
const triggerSpanStyle = computed(() => { const triggerSpanStyle = computed(() => {
let bgstr = '' let bgstr = "";
if (colorBoxProps.color) { if (colorBoxProps.color) {
bgstr = colorBoxProps.color bgstr = colorBoxProps.color;
if ((colorBoxProps.color.match(/[0-9]{1,3}/g) || []).length > 3) { if ((colorBoxProps.color.match(/[0-9]{1,3}/g) || []).length > 3) {
// //
if (!(colorBoxProps.alpha && colorBoxProps.format == 'rgb')) { if (!(colorBoxProps.alpha && colorBoxProps.format == "rgb")) {
bgstr = '#' + HSBToHEX(RGBToHSB(RGBSTo(colorBoxProps.color))) bgstr = "#" + HSBToHEX(RGBToHSB(RGBSTo(colorBoxProps.color)));
} }
} }
} }
return { return {
background: bgstr, background: bgstr,
} };
}) });
const colorPickerWrapper = computed(() => { const colorPickerWrapper = computed(() => {
return colorBoxProps.size ? `layui-colorpicker-${colorBoxProps.size}` : '' return colorBoxProps.size ? `layui-colorpicker-${colorBoxProps.size}` : "";
}) });
const colorBoxRefEl = ref<HTMLElement | null>(null) const colorBoxRefEl = ref<HTMLElement | null>(null);
const colorPickerRefEl = ref<HTMLElement | null>(null) const colorPickerRefEl = ref<HTMLElement | null>(null);
onMounted(() => { onMounted(() => {
console.log('colorPickerRefEl =>>>', colorPickerRefEl.value.teleportRefEl) console.log("colorPickerRefEl =>>>", colorPickerRefEl.value.teleportRefEl);
usePosition(colorBoxRefEl.value, colorPickerRefEl.value.teleportRefEl) usePosition(colorBoxRefEl.value, colorPickerRefEl.value.teleportRefEl);
}) });
</script> </script>
<template> <template>
<div ref="colorBoxRefEl" class="layui-unselect layui-colorpicker"> <div ref="colorBoxRefEl" class="layui-unselect layui-colorpicker">
@ -107,7 +107,7 @@ onMounted(() => {
:visible="true" :visible="true"
:alpha="alpha" :alpha="alpha"
:predefine="predefine" :predefine="predefine"
></ColorPicker> />
</div> </div>
</template> </template>
<style scoped lang="less"></style> <style scoped lang="less"></style>

View File

@ -1,23 +1,23 @@
<script lang="ts"> <script lang="ts">
export default { export default {
name: 'ColorPicker', name: "ColorPicker",
} };
</script> </script>
<script setup lang="ts"> <script setup lang="ts">
import { ref } from 'vue' import { ref } from "vue";
interface CProps { interface CProps {
visible: boolean visible: boolean;
alpha: boolean alpha: boolean;
predefine: boolean predefine: boolean;
} }
const props = defineProps<CProps>() const props = defineProps<CProps>();
const domRefEl = ref<HTMLElement | null>(null) const domRefEl = ref<HTMLElement | null>(null);
defineExpose({ defineExpose({
teleportRefEl: domRefEl, teleportRefEl: domRefEl,
}) });
</script> </script>
<template> <template>
<teleport to="body"> <teleport to="body">

View File

@ -3,32 +3,32 @@ export interface ColorPickerProps {
* 使 hexrgb rgba * 使 hexrgb rgba
* v-model:color * v-model:color
*/ */
color: string color: string;
/** /**
* / hexrgb * / hexrgb
* rgb rgba #000 * rgb rgba #000
* default: hex 16 * default: hex 16
*/ */
format: 'hex' | 'rgb' format: "hex" | "rgb";
/** /**
* hex rgb * hex rgb
* 1 rgba * 1 rgba
* rgba 使 * rgba 使
* default: false * default: false
*/ */
alpha: boolean alpha: boolean;
/** /**
* *
* default: false * default: false
*/ */
predefine: boolean predefine: boolean;
/** /**
* predefine: true 使 * predefine: true 使
* ['#ff4500','#1e90ff','rgba(255, 69, 0, 0.68)','rgb(255, 120, 0)'] * ['#ff4500','#1e90ff','rgba(255, 69, 0, 0.68)','rgb(255, 120, 0)']
*/ */
colors: string[] colors: string[];
/** /**
* lgsmxs * lgsmxs
*/ */
size: 'lg' | 'sm' | 'xs' size: "lg" | "sm" | "xs";
} }

View File

@ -1,113 +1,113 @@
export interface RGB { export interface RGB {
h: number h: number;
s: number s: number;
b: number b: number;
} }
// RGB转HSB // RGB转HSB
export function RGBToHSB(rgb: any) { export function RGBToHSB(rgb: any) {
const hsb = { h: 0, s: 0, b: 0 } const hsb = { h: 0, s: 0, b: 0 };
const min = Math.min(rgb.r, rgb.g, rgb.b) const min = Math.min(rgb.r, rgb.g, rgb.b);
const max = Math.max(rgb.r, rgb.g, rgb.b) const max = Math.max(rgb.r, rgb.g, rgb.b);
const delta = max - min const delta = max - min;
hsb.b = max hsb.b = max;
hsb.s = max != 0 ? (255 * delta) / max : 0 hsb.s = max != 0 ? (255 * delta) / max : 0;
if (hsb.s != 0) { if (hsb.s != 0) {
if (rgb.r == max) { if (rgb.r == max) {
hsb.h = (rgb.g - rgb.b) / delta hsb.h = (rgb.g - rgb.b) / delta;
} else if (rgb.g == max) { } else if (rgb.g == max) {
hsb.h = 2 + (rgb.b - rgb.r) / delta hsb.h = 2 + (rgb.b - rgb.r) / delta;
} else { } else {
hsb.h = 4 + (rgb.r - rgb.g) / delta hsb.h = 4 + (rgb.r - rgb.g) / delta;
} }
} else { } else {
hsb.h = -1 hsb.h = -1;
} }
if (max == min) { if (max == min) {
hsb.h = 0 hsb.h = 0;
} }
hsb.h *= 60 hsb.h *= 60;
if (hsb.h < 0) { if (hsb.h < 0) {
hsb.h += 360 hsb.h += 360;
} }
hsb.s *= 100 / 255 hsb.s *= 100 / 255;
hsb.b *= 100 / 255 hsb.b *= 100 / 255;
return hsb return hsb;
} }
// HEX转HSB // HEX转HSB
export function HEXToHSB(hex: any) { export function HEXToHSB(hex: any) {
hex = hex.indexOf('#') > -1 ? hex.substring(1) : hex hex = hex.indexOf("#") > -1 ? hex.substring(1) : hex;
if (hex.length == 3) { if (hex.length == 3) {
const num = hex.split('') const num = hex.split("");
hex = num[0] + num[0] + num[1] + num[1] + num[2] + num[2] hex = num[0] + num[0] + num[1] + num[1] + num[2] + num[2];
} }
hex = parseInt(hex, 16) hex = parseInt(hex, 16);
const rgb = { r: hex >> 16, g: (hex & 0x00ff00) >> 8, b: hex & 0x0000ff } const rgb = { r: hex >> 16, g: (hex & 0x00ff00) >> 8, b: hex & 0x0000ff };
return RGBToHSB(rgb) return RGBToHSB(rgb);
} }
// HSB转RGB // HSB转RGB
export function HSBToRGB(hsb: any) { export function HSBToRGB(hsb: any) {
const rgb: any = {} const rgb: any = {};
let h = hsb.h let h = hsb.h;
const s = (hsb.s * 255) / 100 const s = (hsb.s * 255) / 100;
const b = (hsb.b * 255) / 100 const b = (hsb.b * 255) / 100;
if (s == 0) { if (s == 0) {
rgb.r = rgb.g = rgb.b = b rgb.r = rgb.g = rgb.b = b;
} else { } else {
const t1 = b const t1 = b;
const t2 = ((255 - s) * b) / 255 const t2 = ((255 - s) * b) / 255;
const t3 = ((t1 - t2) * (h % 60)) / 60 const t3 = ((t1 - t2) * (h % 60)) / 60;
if (h == 360) h = 0 if (h == 360) h = 0;
if (h < 60) { if (h < 60) {
rgb.r = t1 rgb.r = t1;
rgb.b = t2 rgb.b = t2;
rgb.g = t2 + t3 rgb.g = t2 + t3;
} else if (h < 120) { } else if (h < 120) {
rgb.g = t1 rgb.g = t1;
rgb.b = t2 rgb.b = t2;
rgb.r = t1 - t3 rgb.r = t1 - t3;
} else if (h < 180) { } else if (h < 180) {
rgb.g = t1 rgb.g = t1;
rgb.r = t2 rgb.r = t2;
rgb.b = t2 + t3 rgb.b = t2 + t3;
} else if (h < 240) { } else if (h < 240) {
rgb.b = t1 rgb.b = t1;
rgb.r = t2 rgb.r = t2;
rgb.g = t1 - t3 rgb.g = t1 - t3;
} else if (h < 300) { } else if (h < 300) {
rgb.b = t1 rgb.b = t1;
rgb.g = t2 rgb.g = t2;
rgb.r = t2 + t3 rgb.r = t2 + t3;
} else if (h < 360) { } else if (h < 360) {
rgb.r = t1 rgb.r = t1;
rgb.g = t2 rgb.g = t2;
rgb.b = t1 - t3 rgb.b = t1 - t3;
} else { } else {
rgb.r = 0 rgb.r = 0;
rgb.g = 0 rgb.g = 0;
rgb.b = 0 rgb.b = 0;
} }
} }
return { r: Math.round(rgb.r), g: Math.round(rgb.g), b: Math.round(rgb.b) } return { r: Math.round(rgb.r), g: Math.round(rgb.g), b: Math.round(rgb.b) };
} }
// HSB转HEX // HSB转HEX
export function HSBToHEX(hsb: any) { export function HSBToHEX(hsb: any) {
const rgb = HSBToRGB(hsb) const rgb = HSBToRGB(hsb);
const hex = [rgb.r.toString(16), rgb.g.toString(16), rgb.b.toString(16)] const hex = [rgb.r.toString(16), rgb.g.toString(16), rgb.b.toString(16)];
hex.forEach((val, nr) => { hex.forEach((val, nr) => {
if (val.length == 1) { if (val.length == 1) {
hex[nr] = '0' + val hex[nr] = "0" + val;
} }
}) });
return hex.join('') return hex.join("");
} }
//转化成所需rgb格式 //转化成所需rgb格式
export function RGBSTo(rgbs: any) { export function RGBSTo(rgbs: any) {
const regexp = /[0-9]{1,3}/g const regexp = /[0-9]{1,3}/g;
const re = rgbs.match(regexp) || [] const re = rgbs.match(regexp) || [];
return { r: re[0], g: re[1], b: re[2] } return { r: re[0], g: re[1], b: re[2] };
} }

View File

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

View File

@ -1,58 +1,58 @@
<script lang="ts"> <script lang="ts">
export default { export default {
name: 'LayColorPicker', name: "LayColorPicker",
} };
</script> </script>
<script lang="ts" setup> <script lang="ts" setup>
import { Nullable } from '/@src/module/type' import { Nullable } from "/@src/module/type";
import ColorBox from './ColorBox.vue' import ColorBox from "./ColorBox.vue";
interface ColorPickerProps { interface ColorPickerProps {
color?: string color?: string;
size?: Nullable<string> size?: Nullable<string>;
alpha?: boolean alpha?: boolean;
format?: 'hex' | 'rgb' format?: "hex" | "rgb";
predefine?: boolean predefine?: boolean;
colors?: string[] colors?: string[];
} }
const colorPickerProps = withDefaults(defineProps<ColorPickerProps>(), { const colorPickerProps = withDefaults(defineProps<ColorPickerProps>(), {
color: '', color: "",
size: () => null, size: () => null,
alpha: false, alpha: false,
format: 'hex', format: "hex",
predefine: false, predefine: false,
colors: () => [ colors: () => [
// //
'#009688', "#009688",
'#5FB878', "#5FB878",
'#1E9FFF', "#1E9FFF",
'#FF5722', "#FF5722",
'#FFB800', "#FFB800",
'#01AAED', "#01AAED",
'#999', "#999",
'#c00', "#c00",
'#ff8c00', "#ff8c00",
'#ffd700', "#ffd700",
'#90ee90', "#90ee90",
'#00ced1', "#00ced1",
'#1e90ff', "#1e90ff",
'#c71585', "#c71585",
'rgb(0, 186, 189)', "rgb(0, 186, 189)",
'rgb(255, 120, 0)', "rgb(255, 120, 0)",
'rgb(250, 212, 0)', "rgb(250, 212, 0)",
'#393D49', "#393D49",
'rgba(0,0,0,.5)', "rgba(0,0,0,.5)",
'rgba(255, 69, 0, 0.68)', "rgba(255, 69, 0, 0.68)",
'rgba(144, 240, 144, 0.5)', "rgba(144, 240, 144, 0.5)",
'rgba(31, 147, 255, 0.73)', "rgba(31, 147, 255, 0.73)",
], ],
}) });
</script> </script>
<template> <template>
<div class="layui-inline'"> <div class="layui-inline'">
<ColorBox></ColorBox> <ColorBox />
</div> </div>
</template> </template>

View File

@ -1,12 +1,12 @@
.layui-container { .layui-container {
position: relative; position: relative;
margin: 0 auto; margin: 0 auto;
padding: 0 15px; padding: 0 15px;
box-sizing: border-box; box-sizing: border-box;
} }
.layui-fluid { .layui-fluid {
position: relative; position: relative;
margin: 0 auto; margin: 0 auto;
padding: 0 15px; padding: 0 15px;
} }

View File

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

View File

@ -1,26 +1,28 @@
<script lang="ts"> <script lang="ts">
export default { export default {
name: "LayContainer" name: "LayContainer",
} };
</script> </script>
<script setup lang="ts"> <script setup lang="ts">
import { computed, defineProps } from 'vue' import { computed, defineProps } from "vue";
import "./index.less" import "./index.less";
export interface LayContainerProps { export interface LayContainerProps {
fluid?: boolean fluid?: boolean;
} }
const props = withDefaults(defineProps<LayContainerProps>(), { const props = withDefaults(defineProps<LayContainerProps>(), {
fluid: false fluid: false,
}); });
const classes = computed(() => props.fluid ? 'layui-fluid' : 'layui-container') const classes = computed(() =>
props.fluid ? "layui-fluid" : "layui-container"
);
</script> </script>
<template> <template>
<div :class="classes"> <div :class="classes">
<slot /> <slot></slot>
</div> </div>
</template> </template>

View File

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

View File

@ -1,5 +1,5 @@
<template> <template>
<span class="layui-count" /> <span class="layui-count"></span>
</template> </template>
<script setup name="LayCount" lang="ts"></script> <script setup name="LayCount" lang="ts"></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 || "LayCountUp", Component);
};
export default Component as IDefineComponent;

View File

@ -0,0 +1,74 @@
<template>
<slot name="prefix"></slot>
<span ref="counterRef" style="font-family:sans-serif;" />
<slot name="suffix"></slot>
</template>
<script setup lang="ts">
import { onMounted, ref, watch } from 'vue'
import { CountUp } from 'countup.js'
import type { CountUpOptions } from 'countup.js'
export interface LayCountupProps {
endVal?: number; //
decimalPlaces?: number; //
useGrouping?: boolean; // 使
separator?: string; //
useEasing?: boolean; // 使
duration?: number; //
prefix?: string; //
suffix?: string; //
option?: CountUpOptions; //
}
const props = withDefaults(defineProps<LayCountupProps>(), {
endVal: 0,
option: () => {
return {}
}
});
const counterRef = ref<HTMLDivElement | null>(null);
const instance = ref<CountUp | null>(null);
const {decimalPlaces,useGrouping,separator,useEasing,duration,prefix,suffix} = props;
const defaultOptions: CountUpOptions = {
startVal: 0, //
decimalPlaces: decimalPlaces ? decimalPlaces : 0, //
useEasing: useEasing ? useEasing : true, // 使
duration: duration ? duration : 2, //
useGrouping: useGrouping ? useGrouping : true, // 使
separator:separator ? separator : ",", //
decimal:".", //
prefix: prefix ? prefix : "", //
suffix: suffix ? suffix : "", //
}
watch(
() => props.endVal,
() => {
update(props.endVal)
}
);
onMounted(() => {
createCounter()
})
const createCounter = () => {
if (!counterRef.value) return
const opts: CountUpOptions = Object.assign(defaultOptions, props.option)
instance.value = new CountUp(counterRef?.value, props.endVal, opts);
start();
}
const start = () => {
if (!instance.value) return
instance?.value.start();
}
const update = (newEndVal: number) => {
if (!instance.value) return
instance?.value.update(newEndVal);
}
</script>

View File

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

View File

@ -1,45 +1,45 @@
<script lang="ts"> <script lang="ts">
export default { export default {
name: "LayDropdown" name: "LayDropdown",
} };
</script> </script>
<script setup lang="ts"> <script setup lang="ts">
import { defineProps, provide, ref, watch } from 'vue' import { defineProps, provide, ref, watch } from "vue";
import { useClickOutside } from '@layui/hooks-vue' import { useClickOutside } from "@layui/hooks-vue";
const dropdownRef = ref<null | HTMLElement>(null) const dropdownRef = ref<null | HTMLElement>(null);
const isClickOutside = useClickOutside(dropdownRef) const isClickOutside = useClickOutside(dropdownRef);
export interface LayDropdownProps { export interface LayDropdownProps {
trigger?: string trigger?: string;
} }
const props = withDefaults(defineProps<LayDropdownProps>(),{ const props = withDefaults(defineProps<LayDropdownProps>(), {
trigger: 'click', trigger: "click",
}) });
const openState = ref(false) const openState = ref(false);
const open = function () { const open = function () {
openState.value = true openState.value = true;
} };
const hide = function () { const hide = function () {
openState.value = false openState.value = false;
} };
const toggle = function () { const toggle = function () {
openState.value = !openState.value openState.value = !openState.value;
} };
watch(isClickOutside, () => { watch(isClickOutside, () => {
if (isClickOutside.value) { if (isClickOutside.value) {
openState.value = false openState.value = false;
} }
}) });
provide('openState', openState) provide("openState", openState);
defineExpose({ open, hide, toggle }); defineExpose({ open, hide, toggle });
</script> </script>
@ -52,11 +52,11 @@ defineExpose({ open, hide, toggle });
:class="[openState ? 'layui-dropdown-up' : '']" :class="[openState ? 'layui-dropdown-up' : '']"
> >
<div @click="toggle"> <div @click="toggle">
<slot /> <slot></slot>
</div> </div>
<dl class="layui-anim layui-anim-upbit"> <dl class="layui-anim layui-anim-upbit">
<ul class="layui-menu layui-dropdown-menu"> <ul class="layui-menu layui-dropdown-menu">
<slot name="content" /> <slot name="content"></slot>
</ul> </ul>
</dl> </dl>
</div> </div>
@ -68,12 +68,12 @@ defineExpose({ open, hide, toggle });
@mouseleave="hide" @mouseleave="hide"
> >
<div> <div>
<slot /> <slot></slot>
</div> </div>
<dl class="layui-anim layui-anim-upbit"> <dl class="layui-anim layui-anim-upbit">
<ul class="layui-menu layui-dropdown-menu"> <ul class="layui-menu layui-dropdown-menu">
<slot name="content" /> <slot name="content"></slot>
</ul> </ul>
</dl> </dl>
</div> </div>
</template> </template>

View File

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

View File

@ -1,17 +1,17 @@
<template> <template>
<li> <li>
<div class="layui-menu-body-title" @click="click"> <div class="layui-menu-body-title" @click="click">
<slot /> <slot></slot>
</div> </div>
</li> </li>
</template> </template>
<script setup name="LayDropdownItem" lang="ts"> <script setup name="LayDropdownItem" lang="ts">
import { inject, Ref } from 'vue' import { inject, Ref } from "vue";
const openState: Ref<boolean> = inject('openState') as Ref<boolean> const openState: Ref<boolean> = inject("openState") as Ref<boolean>;
const click = function () { const click = function () {
openState.value = false openState.value = false;
} };
</script> </script>

View File

@ -1,20 +1,20 @@
.layui-empty { .layui-empty {
margin: 0 8px; margin: 0 8px;
font-size: 14px; font-size: 14px;
line-height: 22px; line-height: 22px;
text-align: center; text-align: center;
} }
.layui-empty-image { .layui-empty-image {
height: 100px; height: 100px;
margin-bottom: 8px; margin-bottom: 8px;
} }
.layui-empty-image img { .layui-empty-image img {
height: 100%; height: 100%;
margin: auto; margin: auto;
} }
.layui-empty-description { .layui-empty-description {
margin: 0; margin: 0;
} }

View File

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

View File

@ -27,4 +27,4 @@ const props = withDefaults(defineProps<LayEmptyProps>(), {
{{ description }} {{ description }}
</div> </div>
</div> </div>
</template> </template>

View File

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

View File

@ -1,25 +1,24 @@
<template> <template>
<fieldset v-if="slot.default" class="layui-elem-field"> <fieldset v-if="slot.default" class="layui-elem-field">
<legend>{{ title }}</legend> <legend>{{ title }}</legend>
<div class="layui-field-box"> <div class="layui-field-box">
<slot /> <slot></slot>
</div> </div>
</fieldset> </fieldset>
<fieldset v-else class="layui-elem-field layui-field-title"> <fieldset v-else class="layui-elem-field layui-field-title">
<legend><a name="docend">{{title}}</a></legend> <legend>
<a name="docend">{{ title }}</a>
</legend>
</fieldset> </fieldset>
</template> </template>
<script setup name="LayField" lang="ts"> <script setup name="LayField" lang="ts">
import { defineProps, useSlots } from 'vue' import { defineProps, useSlots } from "vue";
const slot = useSlots() const slot = useSlots();
const props = const props = defineProps<{
defineProps<{ title?: string;
title?: string }>();
}>()
</script> </script>

View File

@ -1,3 +1,3 @@
.layui-footer { .layui-footer {
box-sizing: border-box; box-sizing: border-box;
} }

View File

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

View File

@ -1,15 +1,15 @@
<script lang="ts"> <script lang="ts">
export default { export default {
name: 'LayFooter', name: "LayFooter",
} };
</script> </script>
<script setup lang="ts"> <script setup lang="ts">
import "./index.less" import "./index.less";
</script> </script>
<template> <template>
<div class="layui-footer"> <div class="layui-footer">
<slot /> <slot></slot>
</div> </div>
</template> </template>

View File

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

View File

@ -1,140 +1,162 @@
<template> <template>
<form class="layui-form" :onsubmit="submit"> <form class="layui-form" :onsubmit="submit">
<slot /> <slot></slot>
</form> </form>
</template> </template>
<script lang="ts"> <script lang="ts">
export default{ export default {
name: 'LayForm' name: "LayForm",
} };
</script> </script>
<script setup lang="ts"> <script setup lang="ts">
import { toRefs, provide,reactive, onMounted } from "vue" import { toRefs, provide, reactive, onMounted } from "vue";
import { Rule, ValidateError, ValidateMessages } from "async-validator" import { Rule, ValidateError, ValidateMessages } from "async-validator";
import {layFormKey, LayFormItemContext, FormCallback, modelType} from "../type/form" import {
layFormKey,
LayFormItemContext,
FormCallback,
modelType,
} from "../type/form";
const props = withDefaults( const props = withDefaults(
defineProps<{ defineProps<{
model?: modelType model?: modelType;
required?: boolean, required?: boolean;
rules?: Rule, rules?: Rule;
initValidate?: boolean, initValidate?: boolean;
requiredIcons?: string, requiredIcons?: string;
requiredErrorMessage?: string, requiredErrorMessage?: string;
validateMessage?: ValidateMessages, validateMessage?: ValidateMessages;
useCN?: boolean useCN?: boolean;
}>(), }>(),
{ {
model: function(){ model: function () {
return {} return {};
}, },
useCN : true, useCN: true,
requiredIcons : '', requiredIcons: "",
initValidate : false initValidate: false,
} }
) );
const formItems : LayFormItemContext[] = []; const formItems: LayFormItemContext[] = [];
const formItemMap : {[key:string]:LayFormItemContext} = {}; const formItemMap: { [key: string]: LayFormItemContext } = {};
const emit = defineEmits(['submit']) const emit = defineEmits(["submit"]);
// //
onMounted(()=>{ onMounted(() => {
props.initValidate && validate()?.catch(err => {}); props.initValidate && validate()?.catch((err) => {});
}) });
// //
const submit = function () { const submit = function () {
let _isValidate = false; let _isValidate = false;
validate((isValidate, model, errors) => { validate((isValidate, model, errors) => {
_isValidate = isValidate as boolean; _isValidate = isValidate as boolean;
emit('submit', isValidate, model, errors); emit("submit", isValidate, model, errors);
}); });
// //
return _isValidate; return _isValidate;
} };
/** /**
* 校验表单数据 * 校验表单数据
* @param fields 需要校验的表单字段(string|string[]); 该字段如果为function, 则默认为回调函数校验全部字段; * @param fields 需要校验的表单字段(string|string[]); 该字段如果为function, 则默认为回调函数校验全部字段;
* @param callback 校验表单之后的回调函数 * @param callback 校验表单之后的回调函数
**/ **/
const validate = function(fields?: string|string[]|FormCallback|null, callback?: FormCallback | null){ const validate = function (
fields?: string | string[] | FormCallback | null,
callback?: FormCallback | null
) {
// //
let validateItems : LayFormItemContext[] = formItems; let validateItems: LayFormItemContext[] = formItems;
if (typeof fields === 'function') { if (typeof fields === "function") {
callback = fields; callback = fields;
} else if (typeof fields === 'string' || (Array.isArray(fields) && fields.length > 0)) { } else if (
typeof fields === "string" ||
(Array.isArray(fields) && fields.length > 0)
) {
validateItems = []; validateItems = [];
const validateFields = !fields ? [] : ([] as string[]).concat(fields); const validateFields = !fields ? [] : ([] as string[]).concat(fields);
validateFields.forEach(field => formItemMap[field] && validateItems.push(formItemMap[field])); validateFields.forEach(
(field) => formItemMap[field] && validateItems.push(formItemMap[field])
);
} }
// //
let errorsArrs: ValidateError[] = []; let errorsArrs: ValidateError[] = [];
validateItems.forEach(filed => { validateItems.forEach((filed) => {
filed.validate((errors, _fields)=>{ filed.validate((errors, _fields) => {
errorsArrs = errorsArrs.concat(errors as ValidateError[]); errorsArrs = errorsArrs.concat(errors as ValidateError[]);
}); });
}); });
const isValidate = errorsArrs.length === 0; const isValidate = errorsArrs.length === 0;
// //
if (typeof callback === 'function') { if (typeof callback === "function") {
isValidate ? callback(true, props.model, null) : callback(false, props.model, errorsArrs); isValidate
? callback(true, props.model, null)
: callback(false, props.model, errorsArrs);
return null; return null;
} }
// Promise // Promise
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
const callbackParams = { const callbackParams = {
isValidate, isValidate,
model : props.model, model: props.model,
errors: isValidate ? null : errorsArrs errors: isValidate ? null : errorsArrs,
}; };
callbackParams.isValidate ? resolve(callbackParams) : reject(callbackParams); callbackParams.isValidate
? resolve(callbackParams)
: reject(callbackParams);
}); });
} };
/** /**
* 清除校验 * 清除校验
* @param fields 需要进行清除校验的表单字段(string|string[]); 该字段如果为null, 则默认为全部字段清除校验; * @param fields 需要进行清除校验的表单字段(string|string[]); 该字段如果为null, 则默认为全部字段清除校验;
**/ **/
const clearValidate = function(fields?: string | string[]){ const clearValidate = function (fields?: string | string[]) {
const clearFields = !fields ? [] : ([] as string[]).concat(fields); const clearFields = !fields ? [] : ([] as string[]).concat(fields);
if (clearFields.length === 0) { if (clearFields.length === 0) {
formItems.forEach(filed => filed.clearValidate()); formItems.forEach((filed) => filed.clearValidate());
} else { } else {
clearFields.forEach(field => formItemMap[field] && formItemMap[field].clearValidate()); clearFields.forEach(
(field) => formItemMap[field] && formItemMap[field].clearValidate()
);
} }
} };
/** /**
* 重置表单所有值 * 重置表单所有值
**/ **/
const reset = function(){ const reset = function () {
for (const key in props.model) { for (const key in props.model) {
props.model[key] = null; props.model[key] = null;
} }
// //
setTimeout(()=>validate()?.catch(err => {}), 0); setTimeout(() => validate()?.catch((err) => {}), 0);
} };
// //
const addField = function(item : LayFormItemContext) { const addField = function (item: LayFormItemContext) {
formItems.push(item); formItems.push(item);
formItemMap[item.prop as string] = item; formItemMap[item.prop as string] = item;
} };
defineExpose({validate, clearValidate, reset}); defineExpose({ validate, clearValidate, reset });
provide(layFormKey, reactive({ provide(
formItems, layFormKey,
addField, reactive({
clearValidate, formItems,
validate, addField,
...toRefs(props) clearValidate,
})); validate,
...toRefs(props),
})
);
</script> </script>

View File

@ -1,49 +1,49 @@
import { ValidateMessages } from "async-validator"; import { ValidateMessages } from "async-validator";
// 中文翻译 --> 根据 async-validator 中 ValidateMessages 进行翻译 // 中文翻译 --> 根据 async-validator 中 ValidateMessages 进行翻译
export default { export default {
default: "%s验证失败", default: "%s验证失败",
required: "%s不能为空", required: "%s不能为空",
enum: "%s不在枚举%s里面", enum: "%s不在枚举%s里面",
whitespace: "%s不能为空", whitespace: "%s不能为空",
date: { date: {
format: "%s日期%s不是一个有效格式的日期%s", format: "%s日期%s不是一个有效格式的日期%s",
parse: "%s无法解析为日期,%s是无效的", parse: "%s无法解析为日期,%s是无效的",
invalid: "%s日期%s是无效的" invalid: "%s日期%s是无效的",
}, },
types: { types: {
number: '%s不是一个有效的数字', number: "%s不是一个有效的数字",
boolean: '%s不是一个有效的布尔类型', boolean: "%s不是一个有效的布尔类型",
method: '%s不是一个有效的方法', method: "%s不是一个有效的方法",
regexp: '%s不是一个有效的正则表达式', regexp: "%s不是一个有效的正则表达式",
integer: '%s不是一个有效的整型数字', integer: "%s不是一个有效的整型数字",
float: '%s不是一个有效的浮点小数', float: "%s不是一个有效的浮点小数",
array: '%s不是一个有效的数组', array: "%s不是一个有效的数组",
object: '%s不是一个有效的对象', object: "%s不是一个有效的对象",
enum: '%s不是一个有效的枚举', enum: "%s不是一个有效的枚举",
date: '%s不是一个有效的日期', date: "%s不是一个有效的日期",
url: '%s不是一个有效的url', url: "%s不是一个有效的url",
hex: '%s不是一个有效的十六进制', hex: "%s不是一个有效的十六进制",
email: '%s不是一个有效的邮箱' email: "%s不是一个有效的邮箱",
}, },
string: { string: {
len: "%s必须是长度为%s个字符", len: "%s必须是长度为%s个字符",
min: "%s最小长度为%s个字符", min: "%s最小长度为%s个字符",
max: "%s最长%s个字符", max: "%s最长%s个字符",
range: "%s字符长度需要在%s和%s直接" range: "%s字符长度需要在%s和%s直接",
}, },
number: { number: {
len: "%s长度必须为%s", len: "%s长度必须为%s",
min: "%s必须小于%s", min: "%s必须小于%s",
max: "%s必须大于%s", max: "%s必须大于%s",
range: "%s需要在%s和%s之间" range: "%s需要在%s和%s之间",
}, },
array: { array: {
len: "%s长度必须为%s", len: "%s长度必须为%s",
min: "%s长度必须小于%s", min: "%s长度必须小于%s",
max: "%s长度必须大于%s", max: "%s长度必须大于%s",
range: "%s长度需要在%s和%s之间" range: "%s长度需要在%s和%s之间",
}, },
pattern: { pattern: {
"mismatch": "%s值%s不能匹配%s" mismatch: "%s值%s不能匹配%s",
} },
} as ValidateMessages; } as ValidateMessages;

View File

@ -1,39 +1,40 @@
@error_color : red; @error_color: red;
.layui-required{ .layui-required {
color: @error_color; color: @error_color;
font-size: 12px; font-size: 12px;
line-height: 1; line-height: 1;
} }
.layui-form .layui-form-item{ .layui-form .layui-form-item {
.layui-input-block .layui-input-block,
,.layui-input-inline{ .layui-input-inline {
.layui-form-danger { .layui-form-danger {
border-color: #ff5722 !important; &, .layui-input {
} border-color: #ff5722 !important;
}
} }
}
} }
.layui-error-message { .layui-error-message {
color: @error_color; color: @error_color;
font-size: 12px; font-size: 12px;
line-height: 1; line-height: 1;
padding-top: 2px; padding-top: 2px;
position: absolute; position: absolute;
top: 100%; top: 100%;
left: 0; left: 0;
} }
.layui-error-message-anim { .layui-error-message-anim {
-ms-transform-origin: 0 0; -ms-transform-origin: 0 0;
-webkit-transform-origin: 0 0; -webkit-transform-origin: 0 0;
transform-origin: 0 0; transform-origin: 0 0;
-webkit-animation: layui-top-show-anim 0.3s ease 1; -webkit-animation: layui-top-show-anim 0.3s ease 1;
animation: layui-top-show-anim 0.3s ease 1; animation: layui-top-show-anim 0.3s ease 1;
} }
@keyframes layui-top-show-anim { @keyframes layui-top-show-anim {
0% { 0% {
opacity: 0.3; opacity: 0.3;
@ -44,4 +45,4 @@
opacity: 1; opacity: 1;
transform: rotateX(0); transform: rotateX(0);
} }
} }

View File

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

View File

@ -1,140 +1,194 @@
<template> <template>
<div class="layui-form-item" ref="formItemRef"> <div class="layui-form-item" ref="formItemRef">
<label class="layui-form-label"> <label class="layui-form-label">
<span v-if="props.prop &&isRequired" :class="['layui-required', 'layui-icon'].concat(layForm.requiredIcons??'')"> <span
<slot name="required" :props="{...props, model: layForm.model}">{{layForm.requiredIcons? '' : '*'}}</slot> v-if="props.prop && isRequired"
:class="
['layui-required', 'layui-icon'].concat(layForm.requiredIcons ?? '')
"
>
<slot name="required" :props="{ ...props, model: layForm.model }">{{
layForm.requiredIcons ? "" : "*"
}}</slot>
</span> </span>
<slot name="label" :props="{...props, model: layForm.model}"> <slot name="label" :props="{ ...props, model: layForm.model }">
{{ label }} {{ label }}
</slot> </slot>
</label> </label>
<div :class="[mode ? 'layui-input-' + mode : '']"> <div :class="[mode ? 'layui-input-' + mode : '']">
<div ref="slotParent"> <div ref="slotParent">
<slot :props="{...props, model: layForm.model}"/> <slot :props="{ ...props, model: layForm.model }"></slot>
</div> </div>
<span v-if="errorStatus" :class="['layui-error-message', {'layui-error-message-anim': errorStatus}]">{{errorMsg}}</span> <span
v-if="errorStatus"
:class="[
'layui-error-message',
{ 'layui-error-message-anim': errorStatus },
]"
>{{ errorMsg }}</span
>
</div> </div>
</div> </div>
</template> </template>
<script setup name="LayFormItem" lang="ts"> <script setup name="LayFormItem" lang="ts">
import "./index.less"; import "./index.less";
import { defineProps, inject, withDefaults, ref, reactive, toRefs, onMounted, computed, watch} from 'vue' import {
import {layFormKey, LayFormContext, LayFormItemContext, FieldValidateError} from "../type/form" defineProps,
import Schema, { Rule, RuleItem, Rules, ValidateCallback, ValidateError, ValidateMessages} from 'async-validator'; inject,
import cnValidateMessage from './cnValidateMessage'; withDefaults,
ref,
reactive,
toRefs,
onMounted,
computed,
watch,
} from "vue";
import {
layFormKey,
LayFormContext,
LayFormItemContext,
FieldValidateError,
} from "../type/form";
import Schema, {
Rule,
RuleItem,
Rules,
ValidateCallback,
ValidateError,
ValidateMessages,
} from "async-validator";
import cnValidateMessage from "./cnValidateMessage";
const props = withDefaults( const props = withDefaults(
defineProps<{ defineProps<{
prop?: string prop?: string;
mode?: string mode?: string;
label?: string label?: string;
errorMessage?: string errorMessage?: string;
rules?: Rule rules?: Rule;
required?: boolean required?: boolean;
}>(), }>(),
{ {
mode: 'block' mode: "block",
} }
) );
const layForm = inject(layFormKey, {} as LayFormContext) const layForm = inject(layFormKey, {} as LayFormContext);
const formItemRef = ref<HTMLDivElement>() const formItemRef = ref<HTMLDivElement>();
const slotParent = ref<HTMLDivElement>() const slotParent = ref<HTMLDivElement>();
// //
const isRequired = computed(()=>{ const isRequired = computed(() => {
return props.required || layForm.required; return props.required || layForm.required;
}) });
// //
const ruleItems = computed(()=>{ const ruleItems = computed(() => {
const prop = props.prop; const prop = props.prop;
if (!prop) { if (!prop) {
return {}; return {};
} }
let rulesArrs : RuleItem[] = []; let rulesArrs: RuleItem[] = [];
if (isRequired.value) { if (isRequired.value) {
rulesArrs.push({required: true}); rulesArrs.push({ required: true });
} }
if (props.rules) { if (props.rules) {
rulesArrs = rulesArrs.concat((props.rules as RuleItem | RuleItem[])); rulesArrs = rulesArrs.concat(props.rules as RuleItem | RuleItem[]);
} }
if (layForm.rules && layForm.rules[prop]) { if (layForm.rules && layForm.rules[prop]) {
rulesArrs = rulesArrs.concat((layForm.rules[prop] as RuleItem | RuleItem[])); rulesArrs = rulesArrs.concat(layForm.rules[prop] as RuleItem | RuleItem[]);
} }
return rulesArrs; return rulesArrs;
}); });
// //
const filedValue = computed(()=> props.prop ? layForm.model[props.prop] : undefined); const filedValue = computed(() =>
watch(()=>filedValue.value, (val)=> validate(), {deep: true}); props.prop ? layForm.model[props.prop] : undefined
);
watch(
() => filedValue.value,
(val) => validate(),
{ deep: true }
);
// //
const errorStatus = ref(false); const errorStatus = ref(false);
const errorMsg = ref(); const errorMsg = ref();
// //
const validate = (callback ?: ValidateCallback)=> { const validate = (callback?: ValidateCallback) => {
if (props.prop && (ruleItems.value as RuleItem[]).length > 0) { if (props.prop && (ruleItems.value as RuleItem[]).length > 0) {
// //
const descriptor : Rules = {}; const descriptor: Rules = {};
descriptor[layForm.useCN? (props.label||props.prop ): props.prop] = ruleItems.value; descriptor[layForm.useCN ? props.label || props.prop : props.prop] =
ruleItems.value;
const validator = new Schema(descriptor); const validator = new Schema(descriptor);
let model : {[key : string]:any} = {}; let model: { [key: string]: any } = {};
let validateMessage = null; let validateMessage = null;
// 使 // 使
if (layForm.useCN) { if (layForm.useCN) {
validateMessage = Object.assign({}, cnValidateMessage, layForm.validateMessage); validateMessage = Object.assign(
model[props.label||props.prop] = filedValue.value; {},
cnValidateMessage,
layForm.validateMessage
);
model[props.label || props.prop] = filedValue.value;
} else { } else {
layForm.validateMessage && (validateMessage = layForm.validateMessage); layForm.validateMessage && (validateMessage = layForm.validateMessage);
model[props.prop] = filedValue.value; model[props.prop] = filedValue.value;
} }
// //
layForm.requiredErrorMessage && (validateMessage = Object.assign(validateMessage, {required : layForm.requiredErrorMessage})); layForm.requiredErrorMessage &&
(validateMessage = Object.assign(validateMessage, {
required: layForm.requiredErrorMessage,
}));
validateMessage && validator.messages(validateMessage); validateMessage && validator.messages(validateMessage);
// //
validator.validate(model, (errors, fields) => { validator.validate(model, (errors, fields) => {
errorStatus.value = errors !== null && errors.length > 0; errorStatus.value = errors !== null && errors.length > 0;
const slotParentDiv = slotParent.value as HTMLDivElement; const slotParentDiv = slotParent.value as HTMLDivElement;
if (errorStatus.value) { if (errorStatus.value) {
const _errors = (errors as FieldValidateError[]); const _errors = errors as FieldValidateError[];
// ,FieldValidateError // ,FieldValidateError
layForm.useCN && _errors.forEach(error => { layForm.useCN &&
error.label = props.label; _errors.forEach((error) => {
error.field = props.prop; error.label = props.label;
}) error.field = props.prop;
errorMsg.value = props.errorMessage??_errors[0].message; });
slotParentDiv.childElementCount > 0 && slotParentDiv.firstElementChild?.classList.add('layui-form-danger'); errorMsg.value = props.errorMessage ?? _errors[0].message;
slotParentDiv.childElementCount > 0 &&
slotParentDiv.firstElementChild?.classList.add("layui-form-danger");
callback && callback(_errors, fields); callback && callback(_errors, fields);
} else { } else {
clearValidate(); clearValidate();
} }
}); });
} }
} };
// //
const clearValidate = ()=> { const clearValidate = () => {
errorStatus.value = false; errorStatus.value = false;
errorMsg.value = ''; errorMsg.value = "";
const slotParentDiv = slotParent.value as HTMLDivElement; const slotParentDiv = slotParent.value as HTMLDivElement;
slotParentDiv.childElementCount > 0 && slotParentDiv.firstElementChild?.classList.remove('layui-form-danger'); slotParentDiv.childElementCount > 0 &&
} slotParentDiv.firstElementChild?.classList.remove("layui-form-danger");
};
defineExpose({validate, clearValidate}); defineExpose({ validate, clearValidate });
onMounted(()=>{ onMounted(() => {
if (props.prop) { if (props.prop) {
layForm.addField(reactive({ layForm.addField(
...toRefs(props), reactive({
$el: formItemRef, ...toRefs(props),
validate, $el: formItemRef,
clearValidate validate,
}) as LayFormItemContext); clearValidate,
}) as LayFormItemContext
);
} }
}) });
</script> </script>

View File

@ -1,4 +1,4 @@
.layui-header { .layui-header {
box-sizing: border-box; box-sizing: border-box;
height: 60px; height: 60px;
} }

Some files were not shown because too many files have changed in this diff Show More