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

View File

@ -1,7 +1,7 @@
::: title 基础使用
:::
::: demo 使用 lay-button 标签, 创建一个按钮
::: demo 使用 `lay-button` 标签, 创建一个按钮
<template>
<lay-button type="primary">原始按钮</lay-button>
@ -29,7 +29,7 @@ export default {
::: title 简约按钮
:::
::: demo 使用 border 属性设置 主要按钮 边框主题
::: demo 使用 `border` 属性设置边框主题
<template>
<lay-button type="primary">原始按钮</lay-button>
@ -57,7 +57,7 @@ export default {
::: title 按钮尺寸
:::
::: demo 传入 size 属性, 创建指定尺寸的按钮, 可选值 `lg` `sm` `xs`
::: demo 传入 `size` 属性, 创建指定尺寸的按钮, 可选值 `lg` `sm` `xs`
<template>
<lay-button type="primary" size="lg">原始按钮</lay-button>
@ -85,7 +85,7 @@ export default {
::: title 流式按钮
:::
::: demo 传入 fluid 属性, 创建最大化按钮
::: demo 传入 `size` 属性, 创建最大化按钮
<template>
<lay-button type="primary" fluid>最大化按钮</lay-button>
@ -111,7 +111,7 @@ export default {
::: title 圆角按钮
:::
::: demo 传入 radius 属性,创建圆角按钮
::: demo 传入 `radius` 属性,创建圆角按钮
<template>
<lay-button type="primary" radius>原始按钮</lay-button>
@ -139,7 +139,7 @@ export default {
::: title 按钮分组
:::
::: demo 使用 lay-button-group 标签, 创建一个按钮组
::: demo 使用 `lay-button-group` 标签, 创建一个按钮组
<template>
<div>
@ -173,7 +173,7 @@ export default {
::: title 图标按钮
:::
::: demo 结合 lay-icon 组件, 创建图标按钮
::: demo 结合 `lay-icon` 组件, 创建图标按钮
<template>
<lay-button-container>
@ -199,7 +199,7 @@ export default {
::: title 按钮容器
:::
::: demo 使用 lay-button-container 标签, 创建一个按钮容器
::: demo 使用 `lay-button-container` 标签, 创建一个按钮容器
<template>
<lay-button-container>
@ -226,7 +226,7 @@ export default {
::: title 加载按钮
:::
::: demo 传入 loading 属性, 控制按钮的加载状态
::: demo 传入 `loading` 属性, 控制按钮的加载状态
<template>
<lay-button-container>
@ -255,7 +255,7 @@ export default {
::: title 事件处理
:::
::: demo 使用 @click 设置单击回调
::: demo 使用 `@click` 设置单击回调
<template>
<lay-button type="default" @click="clickHandle">单击事件</lay-button>
@ -280,31 +280,57 @@ export default {
:::
::: title 按钮属性
::: title Button 属性
:::
::: table
| 属性 | 描述 | 可选值 |
| ------ | ------ | --------------------------------------------- |
| type | 主题 | `primary` `normal` `warm` `danger` `disabled` |
| size | 尺寸 | `lg` `sm` `xs` |
| fluid | 最大化 | `true` `false` |
| radius | 圆角 | `true` `false` |
| border | 边框 | `green` `blue` `orange` `red` `black` |
| 属性 | 描述 | 类型 | 默认值 | 可选值 |
| ----------- | -------- | ------- | --------- | ----------------------------------|
| type | 主题 | string | `primary` | `primary` `normal` `warm` `danger` |
| size | 尺寸 | string | -- | `lg` `sm` `xs` |
| fluid | 最大化 | boolean | `false` | `true` `false` |
| radius | 圆角 | boolean | `false` | `true` `false` |
| 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
| 事件 | 描述 | 参数 |
| ----- | -------- | ---- |
| click | 单击事件 | `--` |
| default | 默认内容 | -- |
:::
::: 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
<template>
<lay-slider v-model="value1" :disabled="true"></lay-slider>
<lay-slider v-model="value1" :disabled="false"></lay-slider>
</template>
<script>
@ -19,10 +22,12 @@ export default {
:::
::: title 纵向
:::
::: demo
<template>
<lay-slider v-model="value2" :vertical="vertical" :disabled="true"></lay-slider>
<lay-slider v-model="value2" :vertical="true" :disabled="true"></lay-slider>
</template>
<script>
@ -31,11 +36,9 @@ import { ref } from 'vue'
export default {
setup() {
const vertical = ref(true)
const value2 = ref(10)
return {
vertical,
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
:::
:::

View File

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

View File

@ -10,6 +10,26 @@
::: demo
<template>
<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">
<ul>
<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)
:::
:::

View File

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

View File

@ -34,9 +34,40 @@ const toggle = function () {
const copy = function () {
const foundCodes = meta.value.getElementsByClassName('language-html')
if (document.hasFocus()) {
const text = foundCodes[0].textContent || "";
const foundCode = foundCodes[0];
let successful = false;
// 使http使
if (navigator.clipboard && document.hasFocus()) {
const text = foundCode.textContent || "";
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}, ()=>{})
} else {
layer.msg("复制失败", { icon : 2, time: 1000}, ()=>{})
@ -155,4 +186,4 @@ function handleScroll() {
padding-left: 10px;
padding-right: 10px;
}
</style>
</style>

View File

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

View File

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

View File

@ -309,6 +309,11 @@ const zhCN = [
component: () => import('../../docs/zh-CN/components/backtop.md'),
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',
path: '/zh-CN/components/carousel',
},
{
id: 43,
title: '数字滚动',
subTitle: 'countUp',
path: '/zh-CN/components/countup',
},
],
},
{

View File

@ -26,7 +26,7 @@
>
</div>
<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
><router-link
class="layui-inline site-down"
@ -44,7 +44,7 @@
rel="nofollow"
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
href="https://gitee.com/layui-vue"

View File

@ -1,10 +1,19 @@
{
"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",
"homepage": "https://gitee.com/layui-vue/layui-vue/blob/master/README.md",
"homepage": "http://layui-vue.pearadmin.com",
"module": "lib/layui-vue.es.js",
"main": "lib/layui-vue.umd.js",
"types": "types/index.d.ts",
"style": "lib/index.css",
"keywords": [
"layui-vue",
"layui",
"vue"
],
"exports": {
".": {
"import": "./lib/layui-vue.es.js",
@ -12,33 +21,21 @@
},
"./lib/": "./lib/"
},
"types": "types/index.d.ts",
"style": "lib/index.css",
"scripts": {
"dev": "vite",
"build": "vite build --emptyOutDir && npm run build:types",
"build:types": "rimraf types && tsc -d",
"build:example": "vite build example"
},
"keywords": [
"layui-vue",
"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"
"build:example": "vite build example",
"lint:eslint": "eslint \"src/**/*.{vue,ts,tsx}\" --fix",
"lint:prettier": "prettier --write \"src/**/*.{js,json,tsx,css,less,scss,vue,html,md}\"",
"commit": "git cz"
},
"dependencies": {
"@layui/hooks-vue": "^0.1.6",
"@layui/icons-vue": "^1.0.1",
"@layui/icons-vue": "^1.0.2",
"@layui/layer-vue": "^1.2.2",
"async-validator": "^4.0.7",
"countup.js": "^2.0.8",
"evtd": "^0.2.3",
"vue": "^3.2.26",
"vue-router": "^4.0.12"
@ -51,18 +48,27 @@
"@types/markdown-it": "^12.2.3",
"@types/markdown-it-container": "^2.0.4",
"@types/node": "^16.11.9",
"@typescript-eslint/eslint-plugin": "^5.8.0",
"@typescript-eslint/parser": "^5.8.0",
"@vitejs/plugin-vue": "^1.9.3",
"@vue/compiler-sfc": "^3.2.26",
"@vue/server-renderer": "^3.2.26",
"commitizen": "^4.2.4",
"cz-conventional-changelog": "3.3.0",
"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",
"prettier": "^2.5.1",
"prismjs": "^1.25.0",
"rimraf": "^3.0.2",
"rollup": "^2.0.0",
"rollup": "^2.61.0",
"typescript": "^4.5.2",
"vite": "2.6.14",
"vite-plugin-md": "^0.11.4"
"less": "^4.1.2",
"vite": "2.7.6",
"vite-plugin-md": "^0.11.6"
},
"files": [
"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
SSR: boolean
}
}
}

View File

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

View File

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

View File

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

View File

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

View File

@ -1,10 +1,13 @@
/** backtop **/
@width: 50px;
@height: @width;
.layui-backtop {
position: fixed;
right: 30px;
bottom: 40px;
width: 50px;
height: 50px;
width: @width;
height: @height;
display: flex;
align-items: center;
justify-content: center;
@ -19,3 +22,15 @@
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 Component from './index.vue'
import type { IDefineComponent } from '../type/index'
import type { App } from "vue";
import Component from "./index.vue";
import type { IDefineComponent } from "../type/index";
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"
ref="backtopRef"
class="layui-backtop"
:class="classBacktop"
:style="{ ...styleBacktop }"
@click.stop="handleClick"
@mousedown="handlerMousedown"
@ -12,23 +13,30 @@
<lay-icon
:type="props.icon"
:size="`${props.iconSize}px`"
:prefix="props.iconPrefix"
:color="props.iconColor"
></lay-icon>
/>
</slot>
</div>
</template>
<script lang="ts">
export default {
name: 'LayBacktop',
name: "LayBacktop",
};
</script>
<script lang="ts" setup>
import { defineProps, defineEmits, ref, shallowRef, withDefaults, computed, onMounted, } from 'vue';
import LayIcon from '../icon/index';
import './index.less';
import {
defineProps,
defineEmits,
ref,
shallowRef,
withDefaults,
computed,
onMounted,
} from "vue";
import LayIcon from "../icon/index";
import "./index.less";
export interface LayBacktopProps {
/**通用*/
@ -36,42 +44,52 @@ export interface LayBacktopProps {
showHeight?: number;
disabled?: boolean;
/**组件样式*/
position?: 'fixed' | 'absolute';
position?: "fixed" | "absolute";
right?: number;
bottom?: number;
size?: "medium" | "small";
bgcolor?: string;
opacity?: number;
color?: string;
borderRadius?: number | string;
circular?: boolean;
circle?: boolean;
/**图标样式*/
icon?: string;
iconSize?: number;
iconPrefix?: string;
iconColor?: string;
}
const props = withDefaults(defineProps<LayBacktopProps>(), {
target: 'window',
target: "window",
showHeight: 200,
right: 30,
bottom: 40,
icon: 'layui-icon-top',
icon: "layui-icon-top",
iconSize: 30,
iconPrefix: 'layui-icon',
disabled: false,
circular: false,
circle: false,
});
const emit = defineEmits(['click']);
const emit = defineEmits(["click"]);
const backtopRef = ref<HTMLElement | null>(null);
const scrollTarget = shallowRef<Window | HTMLElement | undefined>(undefined);
let visible = ref(props.showHeight === 0);
const borderRadius = computed(() => {
if (props.circular) return "50%";
return typeof props.borderRadius === 'number' ? `${props.borderRadius}px` : props.borderRadius;
const classBacktop = computed(() => {
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(() => {
return {
position: props.position,
@ -81,72 +99,119 @@ const styleBacktop = computed(() => {
opacity: props.opacity,
color: props.color,
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 = () => {
if (!scrollTarget.value) return;
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 {
let step = scrollTarget.value.scrollTop / 4;
if (scrollTarget.value.scrollTop > 0) {
scrollTarget.value.scrollTop -= Math.max(step, 10);
setTimeout(() => {
scrollToTop();
}, 1000 / 60);
}
const previous: number = Date.now();
const scrollHeight: number = scrollTarget.value.scrollTop;
const animationFunc = () => {
if (!scrollTarget.value || scrollTarget.value instanceof Window) return;
const elapsed = (Date.now() - previous) / 450;
if (elapsed < 1) {
scrollTarget.value.scrollTop = scrollHeight * (1 - easeInOut(elapsed));
window.requestAnimationFrame(animationFunc);
} else {
scrollTarget.value.scrollTop = 0;
}
};
window.requestAnimationFrame(animationFunc);
}
};
const handleScroll = () => {
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;
};
const handleClick = (event: MouseEvent) => {
if (!props.disabled) {
scrollToTop()
};
emit('click', event);
scrollToTop();
}
emit("click", event);
};
const handlerMousedown = () => {
backtopRef.value!.style.opacity = '1';
}
backtopRef.value!.style.opacity = "1";
};
const handlerMouseup = () => {
backtopRef.value!.style.opacity = '0.95';
}
backtopRef.value!.style.opacity = "0.95";
};
//
const getScrollTarget = () => {
if (props.target === 'window') {
return window || document.documentElement || document.body;
if (props.target === "window") {
return getScrollParent(backtopRef.value!, false);
} else {
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 (!targetElement.parentElement) throw new Error(`target parent element is not existed: ${props.target}`);
targetElement.parentElement.style.position = 'relative';
if (props.position === "absolute") {
if (!targetElement.parentElement){
throw new Error( `target parent element is not existed: ${props.target}`);
}
targetElement.parentElement.style.position = "relative";
// backtopRef.value!.style.position = props.position;
}
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(() => {
let timer: any = undefined;
if (!props.target) return;
scrollTarget.value = getScrollTarget();
// TODO
scrollTarget.value.addEventListener('scroll', () => {
clearTimeout(timer);
timer = setTimeout(() => {
handleScroll();
}, 100);
});
scrollTarget.value.addEventListener("scroll", throttle(handleScroll, 300));
});
</script>

View File

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

View File

@ -1,9 +1,9 @@
import type { App } from 'vue'
import Component from './index.vue'
import type { IDefineComponent } from '../type/index'
import type { App } from "vue";
import Component from "./index.vue";
import type { IDefineComponent } from "../type/index";
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>
<span :class="classes" :style="styles">
<slot v-if="type != 'dot'" />
<slot v-if="type != 'dot'"></slot>
</span>
</template>
</template>

View File

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

View File

@ -1,9 +1,9 @@
import type { App } from 'vue'
import Component from './index.vue'
import type { IDefineComponent } from '../type/index'
import type { App } from "vue";
import Component from "./index.vue";
import type { IDefineComponent } from "../type/index";
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">
import { defineProps } from "vue";
import "./index.less"
import "./index.less";
const props = defineProps<{
nm?: boolean;
@ -15,6 +15,6 @@ const props = defineProps<{
<template>
<blockquote class="layui-elem-quote" :class="{ 'layui-quote-nm': nm }">
<slot />
<slot></slot>
</blockquote>
</template>
</template>

View File

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

View File

@ -1,9 +1,9 @@
import type { App } from 'vue'
import Component from './index.vue'
import type { IDefineComponent } from '../type/index'
import type { App } from "vue";
import Component from "./index.vue";
import type { IDefineComponent } from "../type/index";
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">
export default {
name: 'LayBody',
}
name: "LayBody",
};
</script>
<script setup name="LayBody" lang="ts">
import "./index.less"
import "./index.less";
</script>
<template>
<div class="layui-body">
<slot />
<slot></slot>
</div>
</template>
</template>

View File

@ -1,9 +1,9 @@
import type { App } from 'vue'
import Component from './index.vue'
import type { IDefineComponent } from '../type/index'
import type { App } from "vue";
import Component from "./index.vue";
import type { IDefineComponent } from "../type/index";
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>
<script setup name="LayBreadcrumb" lang="ts">
import { defineProps, provide, withDefaults } from 'vue'
import { defineProps, provide, withDefaults } from "vue";
const props = withDefaults(
defineProps<{
separator?: string
separator?: string;
}>(),
{
separator: '/',
separator: "/",
}
)
);
provide('separator', props.separator)
provide("separator", props.separator);
</script>

View File

@ -1,9 +1,9 @@
import type { App } from 'vue'
import Component from './index.vue'
import type { IDefineComponent } from '../type/index'
import type { App } from "vue";
import Component from "./index.vue";
import type { IDefineComponent } from "../type/index";
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>
<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<{
title?: string
}>()
title?: string;
}>();
const separator = inject('separator')
const separator = inject("separator");
</script>

View File

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

View File

@ -1,10 +1,9 @@
import type { App } from 'vue'
import Component from './index.vue'
import type { IDefineComponent } from '../type/index'
import type { App } from "vue";
import Component from "./index.vue";
import type { IDefineComponent } from "../type/index";
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
v-if="loading"
class="
layui-icon
layui-icon-loading-one
layui-anim
layui-anim-rotate
layui-anim-loop
"
class="layui-icon layui-icon-loading-one layui-anim layui-anim-rotate layui-anim-loop"
></i>
<slot v-else />
<slot v-else></slot>
</button>
</template>
</template>

View File

@ -1,16 +1,16 @@
.layui-btn-container {
font-size: 0;
font-size: 0;
}
.layui-btn-container .layui-btn {
margin-right: 10px;
margin-bottom: 10px;
margin-right: 10px;
margin-bottom: 10px;
}
.layui-btn-container .layui-btn+.layui-btn {
margin-left: 0;
.layui-btn-container .layui-btn + .layui-btn {
margin-left: 0;
}
.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 Component from './index.vue'
import type { IDefineComponent } from '../type/index'
import type { App } from "vue";
import Component from "./index.vue";
import type { IDefineComponent } from "../type/index";
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>
<div class="layui-btn-container">
<slot />
<slot></slot>
</div>
</template>

View File

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

View File

@ -1,9 +1,9 @@
import type { App } from 'vue'
import Component from './index.vue'
import type { IDefineComponent } from '../type/index'
import type { App } from "vue";
import Component from "./index.vue";
import type { IDefineComponent } from "../type/index";
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 setup lang="ts">
import "./index.less"
import "./index.less";
</script>
<template>
<div class="layui-btn-group">
<slot />
<slot></slot>
</div>
</template>
</template>

View File

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

View File

@ -1,9 +1,9 @@
import type { App } from 'vue'
import Component from './index.vue'
import type { IDefineComponent } from '../type/index'
import type { App } from "vue";
import Component from "./index.vue";
import type { IDefineComponent } from "../type/index";
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>
<div class="layui-card">
<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>
</div>
<div class="layui-card-body">
<slot name="body" v-if="slot.body" />
<slot name="body" v-if="slot.body"></slot>
<slot v-else></slot>
</div>
</div>

View File

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

View File

@ -1,9 +1,9 @@
import type { App } from 'vue'
import Component from './index.vue'
import type { IDefineComponent } from '../type/index'
import type { App } from "vue";
import Component from "./index.vue";
import type { IDefineComponent } from "../type/index";
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>
</template>
<script setup name="LayCarouselItem" lang="ts">
import { defineProps, inject } from 'vue'
import { defineProps, inject } from "vue";
const props = defineProps<{
id: string
}>()
id: string;
}>();
const active = inject('active')
const active = inject("active");
</script>

View File

@ -1,9 +1,9 @@
import type { App } from 'vue'
import Component from './index.vue'
import type { IDefineComponent } from '../type/index'
import type { App } from "vue";
import Component from "./index.vue";
import type { IDefineComponent } from "../type/index";
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;
skin?: string;
label: string | object;
modelValue: boolean | Array<string | object>;
modelValue?: boolean | Array<string | object>;
disabled?: boolean;
}
@ -104,8 +104,8 @@ const handleClick = function () {
}"
:lay-skin="skin"
>
<span v-if="$slots?.default"><slot /></span>
<i class="layui-icon layui-icon-ok" />
<span v-if="$slots?.default"><slot></slot></span>
<i class="layui-icon layui-icon-ok"></i>
</div>
</span>
</template>

View File

@ -1,9 +1,9 @@
import type { App } from 'vue'
import Component from './index.vue'
import type { IDefineComponent } from '../type/index'
import type { App } from "vue";
import Component from "./index.vue";
import type { IDefineComponent } from "../type/index";
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>
</template>
<script lang="ts">
export default {
name: "LayCheckboxGroup",

View File

@ -1,9 +1,9 @@
import type { App } from 'vue'
import Component from './index.vue'
import type { IDefineComponent } from '../type/index'
import type { App } from "vue";
import Component from "./index.vue";
import type { IDefineComponent } from "../type/index";
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 [
props.md ? `layui-col-md${props.md}` : "",
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.mdOffset ? `layui-col-md-offset${props.mdOffset}` : "",
props.xsOffset ? `layui-col-xs-offset${props.xsOffset}` : "",
@ -34,6 +34,6 @@ const classes = computed(() => {
<template>
<div class="layui-col" :class="classes">
<slot />
<slot></slot>
</div>
</template>

View File

@ -1,9 +1,9 @@
import type { App } from 'vue'
import Component from './index.vue'
import type { IDefineComponent } from '../type/index'
import type { App } from "vue";
import Component from "./index.vue";
import type { IDefineComponent } from "../type/index";
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>
<div class="layui-collapse">
<slot />
<slot></slot>
</div>
</template>
<script setup name="LayCollapse"></script>
<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(
defineProps<{
@ -15,25 +22,24 @@ const props = withDefaults(
}>(),
{
modelValue: () => [],
accordion: false
accordion: false,
}
)
);
//
watch(
() => props.modelValue,
(val, oldVal)=>{
activeValues.value = ([] as any[]).concat(val)
() => props.modelValue,
(val, oldVal) => {
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));
provide("layCollapse", {
accordion : props.accordion,
accordion: props.accordion,
activeValues,
emit
})
emit,
});
</script>

View File

@ -1,9 +1,9 @@
import type { App } from 'vue'
import Component from './index.vue'
import type { IDefineComponent } from '../type/index'
import type { App } from "vue";
import Component from "./index.vue";
import type { IDefineComponent } from "../type/index";
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>
<div class="layui-colla-item">
<h2 :class="['layui-colla-title', {'layui-disabled' : disabled}]" @click="showHandle">
<slot name="title" :props="props">{{ title}}</slot>
<i class="layui-icon layui-colla-icon">{{ isShow ? '' : '' }}</i>
<h2
:class="['layui-colla-title', { 'layui-disabled': disabled }]"
@click="showHandle"
>
<slot name="title" :props="props">{{ title }}</slot>
<i class="layui-icon layui-colla-icon">{{ isShow ? "" : "" }}</i>
</h2>
<div class="layui-colla-content" :class="isShow ? 'layui-show' : ''">
<p>
<slot :props="props"/>
<slot :props="props"></slot>
</p>
</div>
</div>
</template>
<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(
defineProps<{
id: number | string
title: string
disabled?: boolean
id: number | string;
title: string;
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 () {
if (props.disabled) {
return ;
return;
}
const _isShow = isShow.value;
//
if (accordion) {
activeValues.value = !_isShow ? [props.id] : [];
} else if (_isShow) { // -->
activeValues.value.splice(activeValues.value.indexOf(props.id), 1)
} else { // -->
activeValues.value.push(props.id)
} else if (_isShow) {
// -->
activeValues.value.splice(activeValues.value.indexOf(props.id), 1);
} 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);
}
};
</script>

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -1,9 +1,9 @@
import type { App } from 'vue'
import Component from './index.vue'
import type { IDefineComponent } from '../type/index'
import type { App } from "vue";
import Component from "./index.vue";
import type { IDefineComponent } from "../type/index";
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">
export default {
name: "LayContainer"
}
name: "LayContainer",
};
</script>
<script setup lang="ts">
import { computed, defineProps } from 'vue'
import "./index.less"
import { computed, defineProps } from "vue";
import "./index.less";
export interface LayContainerProps {
fluid?: boolean
fluid?: boolean;
}
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>
<template>
<div :class="classes">
<slot />
<slot></slot>
</div>
</template>
</template>

View File

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

View File

@ -1,9 +1,9 @@
import type { App } from 'vue'
import Component from './index.vue'
import type { IDefineComponent } from '../type/index'
import type { App } from "vue";
import Component from "./index.vue";
import type { IDefineComponent } from "../type/index";
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>
<li>
<div class="layui-menu-body-title" @click="click">
<slot />
<slot></slot>
</div>
</li>
</template>
<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 () {
openState.value = false
}
openState.value = false;
};
</script>

View File

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

View File

@ -1,9 +1,9 @@
import type { App } from 'vue'
import Component from './index.vue'
import type { IDefineComponent } from '../type/index'
import type { App } from "vue";
import Component from "./index.vue";
import type { IDefineComponent } from "../type/index";
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 }}
</div>
</div>
</template>
</template>

View File

@ -1,9 +1,9 @@
import type { App } from 'vue'
import Component from './index.vue'
import type { IDefineComponent } from '../type/index'
import type { App } from "vue";
import Component from "./index.vue";
import type { IDefineComponent } from "../type/index";
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>
<fieldset v-if="slot.default" class="layui-elem-field">
<legend>{{ title }}</legend>
<div class="layui-field-box">
<slot />
<slot></slot>
</div>
</fieldset>
<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>
</template>
<script setup name="LayField" lang="ts">
import { defineProps, useSlots } from 'vue'
import { defineProps, useSlots } from "vue";
const slot = useSlots()
const slot = useSlots();
const props =
defineProps<{
title?: string
}>()
const props = defineProps<{
title?: string;
}>();
</script>

View File

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

View File

@ -1,9 +1,9 @@
import type { App } from 'vue'
import Component from './index.vue'
import type { IDefineComponent } from '../type/index'
import type { App } from "vue";
import Component from "./index.vue";
import type { IDefineComponent } from "../type/index";
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">
export default {
name: 'LayFooter',
}
name: "LayFooter",
};
</script>
<script setup lang="ts">
import "./index.less"
import "./index.less";
</script>
<template>
<div class="layui-footer">
<slot />
<slot></slot>
</div>
</template>
</template>

View File

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

View File

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

View File

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

View File

@ -1,9 +1,9 @@
import type { App } from 'vue'
import Component from './index.vue'
import type { IDefineComponent } from '../type/index'
import type { App } from "vue";
import Component from "./index.vue";
import type { IDefineComponent } from "../type/index";
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>
<div class="layui-form-item" ref="formItemRef">
<label class="layui-form-label">
<span 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
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>
<slot name="label" :props="{...props, model: layForm.model}">
<slot name="label" :props="{ ...props, model: layForm.model }">
{{ label }}
</slot>
</label>
<div :class="[mode ? 'layui-input-' + mode : '']">
<div ref="slotParent">
<slot :props="{...props, model: layForm.model}"/>
<slot :props="{ ...props, model: layForm.model }"></slot>
</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>
</template>
<script setup name="LayFormItem" lang="ts">
import "./index.less";
import { defineProps, inject, 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';
import {
defineProps,
inject,
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(
defineProps<{
prop?: string
mode?: string
label?: string
errorMessage?: string
rules?: Rule
required?: boolean
prop?: string;
mode?: string;
label?: string;
errorMessage?: string;
rules?: Rule;
required?: boolean;
}>(),
{
mode: 'block'
mode: "block",
}
)
);
const layForm = inject(layFormKey, {} as LayFormContext)
const formItemRef = ref<HTMLDivElement>()
const slotParent = ref<HTMLDivElement>()
const layForm = inject(layFormKey, {} as LayFormContext);
const formItemRef = ref<HTMLDivElement>();
const slotParent = ref<HTMLDivElement>();
//
const isRequired = computed(()=>{
const isRequired = computed(() => {
return props.required || layForm.required;
})
});
//
const ruleItems = computed(()=>{
const ruleItems = computed(() => {
const prop = props.prop;
if (!prop) {
return {};
}
let rulesArrs : RuleItem[] = [];
let rulesArrs: RuleItem[] = [];
if (isRequired.value) {
rulesArrs.push({required: true});
rulesArrs.push({ required: true });
}
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]) {
rulesArrs = rulesArrs.concat((layForm.rules[prop] as RuleItem | RuleItem[]));
rulesArrs = rulesArrs.concat(layForm.rules[prop] as RuleItem | RuleItem[]);
}
return rulesArrs;
});
//
const filedValue = computed(()=> props.prop ? layForm.model[props.prop] : undefined);
watch(()=>filedValue.value, (val)=> validate(), {deep: true});
const filedValue = computed(() =>
props.prop ? layForm.model[props.prop] : undefined
);
watch(
() => filedValue.value,
(val) => validate(),
{ deep: true }
);
//
const errorStatus = ref(false);
const errorMsg = ref();
//
const validate = (callback ?: ValidateCallback)=> {
const validate = (callback?: ValidateCallback) => {
if (props.prop && (ruleItems.value as RuleItem[]).length > 0) {
//
const descriptor : Rules = {};
descriptor[layForm.useCN? (props.label||props.prop ): props.prop] = ruleItems.value;
const descriptor: Rules = {};
descriptor[layForm.useCN ? props.label || props.prop : props.prop] =
ruleItems.value;
const validator = new Schema(descriptor);
let model : {[key : string]:any} = {};
let model: { [key: string]: any } = {};
let validateMessage = null;
// 使
if (layForm.useCN) {
validateMessage = Object.assign({}, cnValidateMessage, layForm.validateMessage);
model[props.label||props.prop] = filedValue.value;
validateMessage = Object.assign(
{},
cnValidateMessage,
layForm.validateMessage
);
model[props.label || props.prop] = filedValue.value;
} else {
layForm.validateMessage && (validateMessage = layForm.validateMessage);
layForm.validateMessage && (validateMessage = layForm.validateMessage);
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);
//
validator.validate(model, (errors, fields) => {
errorStatus.value = errors !== null && errors.length > 0;
const slotParentDiv = slotParent.value as HTMLDivElement;
if (errorStatus.value) {
const _errors = (errors as FieldValidateError[]);
const _errors = errors as FieldValidateError[];
// ,FieldValidateError
layForm.useCN && _errors.forEach(error => {
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');
layForm.useCN &&
_errors.forEach((error) => {
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");
callback && callback(_errors, fields);
} else {
clearValidate();
}
});
}
}
};
//
const clearValidate = ()=> {
const clearValidate = () => {
errorStatus.value = false;
errorMsg.value = '';
errorMsg.value = "";
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) {
layForm.addField(reactive({
...toRefs(props),
$el: formItemRef,
validate,
clearValidate
}) as LayFormItemContext);
layForm.addField(
reactive({
...toRefs(props),
$el: formItemRef,
validate,
clearValidate,
}) as LayFormItemContext
);
}
})
});
</script>

View File

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

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