[其他] 初始化项目结构

This commit is contained in:
就眠仪式 2021-09-27 06:09:33 +08:00
commit b2fa2b90b8
73 changed files with 9752 additions and 0 deletions

3
.browserslistrc Normal file
View File

@ -0,0 +1,3 @@
current node
last 2 versions and > 2%
ie > 10

26
.eslintrc.js Normal file
View File

@ -0,0 +1,26 @@
module.exports = {
env: {
browser: true,
node: true,
},
parser: 'vue-eslint-parser',
parserOptions: {
parser: '@typescript-eslint/parser',
sourceType: 'module',
ecmaVersion: 10,
},
plugins: ['@typescript-eslint', 'prettier'],
extends: [
'eslint:recommended',
'plugin:vue/vue3-recommended',
'plugin:@typescript-eslint/recommended',
],
rules: {
'no-console': process.env.NODE_ENV === 'production' ? 'error' : 'off',
'no-debugger': process.env.NODE_ENV === 'production' ? 'error' : 'off',
quotes: ['error', 'single'],
semi: ['error', 'never'],
'no-unused-vars': 'off',
'@typescript-eslint/no-unused-vars': 'off',
},
}

27
.github/workflows/build docs.yml vendored Normal file
View File

@ -0,0 +1,27 @@
name: build docs
on:
push:
branches: [ master ]
jobs:
build-and-deploy:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v2.3.1
with:
persist-credentials: false
- name: Install and Build
run: |
npm install
npm run build:docs
- name: Deploy
uses: JamesIves/github-pages-deploy-action@3.6.2
with:
GITHUB_TOKEN: ${{ secrets.TOKEN }}
BRANCH: gh-pages
FOLDER: docs/dist
CLEAN: true

20
.github/workflows/test.yml vendored Normal file
View File

@ -0,0 +1,20 @@
name: test
on:
pull_request:
branches: [ master ]
jobs:
test:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v2.3.1
with:
persist-credentials: false
- name: Install and Test
run: |
npm install
npm run test

20
.gitignore vendored Normal file
View File

@ -0,0 +1,20 @@
.DS_Store
node_modules/
dist/
example/dist/
lib/
/types/
npm-debug.log*
yarn-debug.log*
yarn-error.log*
yarn.lock
package-lock.json
# Editor directories and files
.idea
.vscode
*.suo
*.ntvs*
*.njsproj
*.sln

53
.helperrc.js Normal file
View File

@ -0,0 +1,53 @@
const helper = require('components-helper')
const { name, version } = require('./package.json')
helper({
name,
version,
entry: 'docs/docs/en-US/components/*.md',
outDir: 'lib',
reComponentName,
reDocUrl,
reAttribute,
titleRegExp: '#+\\s+(.*)\n+>\\s*([^(#|\\n)]*)',
tableRegExp:
'#+\\s+(.*\\s*Props|.*\\s*Events|.*\\s*Slots|.*\\s*Directives)\\s*\\n+(\\|?.+\\|.+)\\n\\|?\\s*:?-+:?\\s*\\|.+((\\n\\|?.+\\|.+)+)',
})
function reComponentName(title) {
return 'ele-' + title.replace(/\B([A-Z])/g, '-$1').toLowerCase()
}
function reDocUrl(fileName, header) {
// TODO: `zh-CN` -> `en-US`
const docs = 'https://gitee.com/Jmysy/layui-vue'
const _header = header ? header.replace(/[ ]+/g, '-') : undefined
return docs + fileName + (_header ? '#' + header : '')
}
function reAttribute(value, key) {
if (key === 'Name' && /^(-|—)$/.test(value)) {
return 'default'
} else if (key === 'Name' && /v-model:(.+)/.test(value)) {
const _value = value.match(/v-model:(.+)/)
return _value ? _value[1] : undefined
} else if (key === 'Name' && /v-model/.test(value)) {
return 'model-value'
} else if (key === 'Name') {
return value.replace(/\B([A-Z])/g, '-$1').toLowerCase()
} else if (key === 'Type') {
return value
.replace(/\s*\/\s*/g, '|')
.replace(/\s*,\s*/g, '|')
.replace(/\(.*\)/g, '')
.toLowerCase()
} else if (value === '' || /^(-|—)$/.test(value)) {
return undefined
} else if (key === 'Options') {
return /\[.+\]\(.+\)/.test(value) || /^\*$/.test(value)
? undefined
: value.replace(/`/g, '')
} else {
return value
}
}

4
.lintstagedrc Normal file
View File

@ -0,0 +1,4 @@
{
"*.{ts,vue,js,tsx,jsx}": ["prettier --write --no-verify ", "eslint --fix"],
"*.{html,css,md,json}": "prettier --write",
}

9
.postcssrc.json Normal file
View File

@ -0,0 +1,9 @@
{
"map": false,
"plugins": {
"postcss-preset-env": {
"stage": 1
},
"autoprefixer": {}
}
}

4
.prettierrc Normal file
View File

@ -0,0 +1,4 @@
{
"singleQuote": true,
"semi": false
}

20
LICENSE Normal file
View File

@ -0,0 +1,20 @@
The MIT License (MIT)
Copyright 2019 Jmys
Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software is furnished to do so,
subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
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.

102
README.md Normal file
View File

@ -0,0 +1,102 @@
<img src="/docs/src/assets/logo.svg" width="400px" style="margin:50px" />
a component library for Vue 3 base on element-plus
<p>
<a href="#">
<img src="https://img.shields.io/badge/Vue-3.0.0+-green.svg" alt="Vue Version">
</a>
<a href="#">
<img src="https://img.shields.io/badge/Vue Router-4.0.0+-green.svg" alt="Vue Router Version">
</a>
<a href="#">
<img src="https://img.shields.io/badge/Element Plus-1.0.0+-green.svg" alt="Element Plus Version">
</a>
</p>
[开 发 文 档](https://jmysy.github.io/layui-vue) | [更 新 日 志](https://gitee.com/Jmysy/layui-vue/releases) | [常 见 问 题](https://gitee.com/Jmysy/layui-vue/issues)
##### 📖 概述
Element Enhance 是基于 Element Plus 而开发的模板组件,提供了更高级别的抽象支持,开箱即用,更加专注于页面。
### Status: Beta
This project is still under heavy development. Feel free to join us and make your first pull request.
[![Edit layui-vue](https://codesandbox.io/static/img/play-codesandbox.svg)](https://codesandbox.io/s/layui-vue-template-vh9bg?fontsize=14&hidenavigation=1&theme=dark)
##### 📢 理念
Element Plus 定义了基础的设计规范,对应也提供了大量的基础组件。但是对于中后台类应用,我们希望提供更高程度的抽象,提供更上层的设计规范,并且对应提供相应的组件使得开发者可以快速搭建出高质量的页面。
列表页应该可以用 EleLayout + EleTable 完成,编辑页应该使用 EleLayout + EleForm 完成,详情页可以用 EleLayout + EleDescriptions 完成。 一个页面在开发工程中只需要关注几个重型组件,降低心智负担,专注于更核心的业务逻辑。
##### ✒️ 特性
该组件库的开发理念就是面向未来,如果查看源码你就会发现像是 vue 3 的 [script setup](https://github.com/vuejs/rfcs/pull/227.) 实验性功能、像是 CSSNext 的 [CSS Variables](https://developer.mozilla.org/en-US/docs/Web/CSS/--*)。在保证大部分浏览器的兼容性的情况下,会更多的使用新特性、新功能来开发
##### 💡 计划
- [x] PageContainer: 为了减少繁杂的面包屑配置和标题。
- [ ] StatisticCard: 指标卡结合统计数值用于展示某主题的核心指标。
- [ ] Search: 有些是时候表单要与别的组件组合使用,需要一些特殊形态的表单。
##### ☁️ 入门
让 Element Plus 更简单, 更通用, 更流行
<img src="/docs/src/assets/banner.png"/>
安装
```
npm install element-plus --save
npm install layui-vue --save
```
引入
```js
import { createApp } from 'vue'
import App from './App.vue'
import ElementEnhance from 'layui-vue'
import 'layui-vue/lib/style.css'
import ElementPlus from 'element-plus'
import 'element-plus/lib/theme-chalk/index.css'
const app = createApp(App)
app.use(ElementEnhance)
app.use(ElementPlus)
app.mount('#app')
```
使用
```vue
<template>
<ele-layout multi-tab="true" breadcrumb="true">
<template #logo></template>
</ele-layout>
</template>
```
效果
![输入图片说明](https://images.gitee.com/uploads/images/2021/0530/172502_95f955fc_4835367.png '屏幕截图.png')
##### 🔥 案例
[Element Enhance Admin](https://gitee.com/Jmysy/layui-vue-admin) : 基于 Element Enhance 的 前端 Cli template
##### 📈 趋势
[![Giteye chart](https://chart.giteye.net/gitee/Jmysy/layui-vue/9X8CXNEY.png)](https://giteye.net/chart/9X8CXNEY)
##### 🍚 贡献
组件库还处于早期开发阶段,功能还需要完善。如果你希望参与贡献,欢迎 [Pull Request](https://github.com/Jmysy/layui-vue/pulls)。如果只是简单的文档相关的错误修正,你可以直接 PR但如果是当前组件的 BUG 或者新功能、新组件等,最好优先提 [issues](https://github.com/Jmysy/layui-vue/issues)
[![Giteye chart](https://chart.giteye.net/gitee/Jmysy/layui-vue/57W94KFG.png)](https://giteye.net/chart/57W94KFG)

View File

@ -0,0 +1,700 @@
##### 基础
::: demo 传入 columns 数据,自动生成表格
<template>
<ele-table
:data="data"
:columns="columns"
/>
</template>
<script>
import { ref } from 'vue'
export default {
setup() {
const columns = ref([
{
label: '日期',
prop: 'date',
},
{
label: '姓名',
prop: 'name',
},
{
label: '地址',
prop: 'address',
},
])
const data = ref([
{
date: '2016-05-03',
name: 'Tom',
address: 'No. 189, Grove St, Los Angeles',
},
{
date: '2016-05-02',
name: 'Tom',
address: 'No. 189, Grove St, Los Angeles',
},
{
date: '2016-05-04',
name: 'Tom',
address: 'No. 189, Grove St, Los Angeles',
},
{
date: '2016-05-01',
name: 'Tom',
address: 'No. 189, Grove St, Los Angeles',
},
])
return {
data,
columns,
}
}
}
</script>
:::
##### 索引
::: demo 通过配置 index 显示索引列,支持 columns 的参数
<template>
<ele-table
:data="data"
:columns="columns"
:index="{ label: '#' }"
/>
</template>
<script>
import { ref } from 'vue'
export default {
setup() {
const columns = ref([
{
label: '日期',
prop: 'date',
},
{
label: '姓名',
prop: 'name',
},
{
label: '地址',
prop: 'address',
},
])
const data = ref([
{
date: '2016-05-03',
name: 'Tom',
address: 'No. 189, Grove St, Los Angeles',
},
{
date: '2016-05-02',
name: 'Tom',
address: 'No. 189, Grove St, Los Angeles',
},
{
date: '2016-05-04',
name: 'Tom',
address: 'No. 189, Grove St, Los Angeles',
},
{
date: '2016-05-01',
name: 'Tom',
address: 'No. 189, Grove St, Los Angeles',
},
])
return {
data,
columns,
}
}
}
</script>
:::
##### 多选
::: demo 通过配置 selection 显示多选框,支持 columns 的参数
<template>
<ele-table
:data="data"
:columns="columns"
selection
/>
</template>
<script>
import { ref } from 'vue'
export default {
setup() {
const columns = ref([
{
label: '日期',
prop: 'date',
},
{
label: '姓名',
prop: 'name',
},
{
label: '地址',
prop: 'address',
},
])
const data = ref([
{
date: '2016-05-03',
name: 'Tom',
address: 'No. 189, Grove St, Los Angeles',
},
{
date: '2016-05-02',
name: 'Tom',
address: 'No. 189, Grove St, Los Angeles',
},
{
date: '2016-05-04',
name: 'Tom',
address: 'No. 189, Grove St, Los Angeles',
},
{
date: '2016-05-01',
name: 'Tom',
address: 'No. 189, Grove St, Los Angeles',
},
])
return {
data,
columns,
}
}
}
</script>
:::
##### 展开
::: demo 通过配置 expand 开启展开插槽,通过 #expand 插槽定制显示内容,支持 columns 的参数
<template>
<ele-table
:data="data"
:columns="columns"
expand
>
<template #expand="{ row }">
{{ row }}
</template>
</ele-table>
</template>
<script>
import { ref } from 'vue'
export default {
setup() {
const columns = ref([
{
label: '日期',
prop: 'date',
},
{
label: '姓名',
prop: 'name',
},
{
label: '地址',
prop: 'address',
},
])
const data = ref([
{
date: '2016-05-03',
name: 'Tom',
address: 'No. 189, Grove St, Los Angeles',
},
{
date: '2016-05-02',
name: 'Tom',
address: 'No. 189, Grove St, Los Angeles',
},
{
date: '2016-05-04',
name: 'Tom',
address: 'No. 189, Grove St, Los Angeles',
},
{
date: '2016-05-01',
name: 'Tom',
address: 'No. 189, Grove St, Los Angeles',
},
])
return {
data,
columns,
}
}
}
</script>
:::
##### 操作
::: demo 通过配置 menu 开启按钮插槽,通过 #menu 插槽定制显示内容,支持 columns 的参数
<template>
<ele-table
:data="data"
:columns="columns"
:menu="menu"
>
<template #menu="{ size }">
<el-button
:size="size"
type="text"
>
详情
</el-button>
</template>
</ele-table>
</template>
<script>
import { ref } from 'vue'
export default {
setup() {
const menu = ref({
label: '操作',
align: 'center',
})
const columns = ref([
{
label: '日期',
prop: 'date',
},
{
label: '姓名',
prop: 'name',
},
{
label: '地址',
prop: 'address',
},
])
const data = ref([
{
date: '2016-05-03',
name: 'Tom',
address: 'No. 189, Grove St, Los Angeles',
},
{
date: '2016-05-02',
name: 'Tom',
address: 'No. 189, Grove St, Los Angeles',
},
{
date: '2016-05-04',
name: 'Tom',
address: 'No. 189, Grove St, Los Angeles',
},
{
date: '2016-05-01',
name: 'Tom',
address: 'No. 189, Grove St, Los Angeles',
},
])
return {
menu,
data,
columns,
}
}
}
</script>
:::
##### 插槽
::: demo
<template>
<ele-table
:data="data"
:columns="columns3"
>
<template #name-header="{ column }">
<s>{{ column.label }}</s>
</template>
<template #name="{ row, size }">
<el-tag :size="size">
{{ row?.name }}
</el-tag>
</template>
<template #menu="{ size }">
<el-button
:size="size"
type="text"
>
详情
</el-button>
</template>
</ele-table>
</template>
<script>
import { ref } from 'vue'
export default {
setup() {
const columns3 = ref([
{
label: '日期',
prop: 'date',
},
{
label: '姓名',
prop: 'name',
slot: true,
},
{
label: '地址',
prop: 'address',
},
])
const data = ref([
{
date: '2016-05-03',
name: 'Tom',
address: 'No. 189, Grove St, Los Angeles',
},
{
date: '2016-05-02',
name: 'Tom',
address: 'No. 189, Grove St, Los Angeles',
},
{
date: '2016-05-04',
name: 'Tom',
address: 'No. 189, Grove St, Los Angeles',
},
{
date: '2016-05-01',
name: 'Tom',
address: 'No. 189, Grove St, Los Angeles',
},
])
return {
data,
columns3,
}
}
}
</script>
:::
##### 分页
::: demo 当传入 total 数据时,将自动显示分页。可以通过 `v-model:current-page` 绑定当前页数、通过 `v-model:page-size` 绑定每页显示条目个数
<template>
<ele-table
v-model:current-page="currentPage"
v-model:page-size="pageSize"
:data="data"
:columns="columns"
:total="total"
/>
</template>
<script>
import { ref } from 'vue'
export default {
setup() {
const currentPage = ref(1)
const pageSize = ref(10)
const total = ref(50)
const columns = ref([
{
label: '日期',
prop: 'date',
},
{
label: '姓名',
prop: 'name',
},
{
label: '地址',
prop: 'address',
},
])
const data = ref([
{
date: '2016-05-03',
name: 'Tom',
address: 'No. 189, Grove St, Los Angeles',
},
{
date: '2016-05-02',
name: 'Tom',
address: 'No. 189, Grove St, Los Angeles',
},
{
date: '2016-05-04',
name: 'Tom',
address: 'No. 189, Grove St, Los Angeles',
},
{
date: '2016-05-01',
name: 'Tom',
address: 'No. 189, Grove St, Los Angeles',
},
])
return {
currentPage,
pageSize,
total,
data,
columns,
}
}
}
</script>
:::
##### 多级
::: demo 通过 columns 的 `children` 配置多级表头
<template>
<ele-table
:data="data"
:columns="columns2"
/>
</template>
<script>
import { ref } from 'vue'
export default {
setup() {
const columns2 = ref([
{
label: '日期',
prop: 'date',
},
{
label: '用户',
children: [
{
label: '姓名',
prop: 'name',
},
{
label: '地址',
prop: 'address',
},
],
},
])
const data = ref([
{
date: '2016-05-03',
name: 'Tom',
address: 'No. 189, Grove St, Los Angeles',
},
{
date: '2016-05-02',
name: 'Tom',
address: 'No. 189, Grove St, Los Angeles',
},
{
date: '2016-05-04',
name: 'Tom',
address: 'No. 189, Grove St, Los Angeles',
},
{
date: '2016-05-01',
name: 'Tom',
address: 'No. 189, Grove St, Los Angeles',
},
])
return {
data,
columns2,
}
}
}
</script>
:::
##### 配置
| 参数 | 说明 | 类型 | 可选值 | 默认值 |
| :---------------------- | :------------------------------------------------------------------ | :------------------------------------------------------ | :----------------------------- | :--------------------------------------------------- |
| data | 显示的数据 | array | - | - |
| columns | 自动生成表单的参数,参考下面 columns | array | - | - |
| selection | 显示多选框,支持 columns 的配置 | boolean / object | - | false |
| index | 显示索引,支持 columns 的配置 | boolean / object | - | false |
| expand | 开启展开插槽,支持 columns 的配置 | boolean / object | - | false |
| menu | 开启操作按钮插槽,支持 columns 的配置 | boolean / object | - | false |
| show-overflow-tooltip | 当内容过长被隐藏时显示 tooltip | boolean | - | false |
| align | 对齐方式 | string | left / center / right | left |
| header-align | 表头对齐方式 | string | left / center / right | 同 align |
| total | 总条目数 | number | - | - |
| current-page | 当前页数,可以通过 `v-model:current-page` 绑定值 | number | - | - |
| page-size | 每页显示条目个数,可以通过 `v-model:page-size` 绑定值 | number | - | - |
| pagination | pagination 的配置,同 el-pagination | object | - | [参考全局配置](../guide/index#全局配置) |
| height | Table 的高度 | string / number | - | 自动高度 |
| max-height | Table 的最大高度 | string / number | - | - |
| stripe | 是否为斑马纹 table | boolean | - | false |
| border | 是否带有纵向边框 | boolean | - | false |
| size | Table 的尺寸 | string | medium / small / mini | - |
| fit | 列的宽度是否自撑开 | boolean | - | true |
| show-header | 是否显示表头 | boolean | - | true |
| highlight-current-row | 是否要高亮当前行 | boolean | - | false |
| current-row-key | 当前行的 key只写属性 | string / number | - | - |
| row-class-name | 为行增加 className | Function({row, rowIndex}) / string | - | - |
| row-style | 为行增加 style | Function({row, rowIndex}) / object | - | - |
| cell-class-name | 为单元格增加 className | Function({row, column, rowIndex, columnIndex}) / string | - | - |
| cell-style | 为单元格增加 style | Function({row, column, rowIndex, columnIndex}) / object | - | - |
| header-row-class-name | 为表头行增加 className | Function({row, rowIndex}) / string | - | - |
| header-row-style | 为表头行增加 style | Function({row, rowIndex}) / object | - | - |
| header-cell-class-name | 为表头单元格增加 className | Function({row, column, rowIndex, columnIndex}) / string | - | - |
| header-cell-style | 为表头单元格增加 style | Function({row, column, rowIndex, columnIndex}) / object | - | - |
| row-key | 行数据的 Key使用 reserveSelection 功能时必填 | Function(row) / string | - | - |
| empty-text | 空数据时显示的文本内容 | string | - | 暂无数据 |
| default-expand-all | 是否默认展开所有行 | boolean | - | false |
| expand-row-keys | Table 目前的展开行,与 row-key 配合使用 | array | - | - |
| default-sort | 默认的排序列的 prop 和顺序 | Object | `order`: ascending, descending | ascending |
| tooltip-effect | tooltip `effect` 属性 | String | dark / light | - |
| show-summary | 是否在表尾显示合计行 | Boolean | - | false |
| sum-text | 合计行第一列的文本 | String | - | 合计 |
| summary-method | 自定义的合计计算方法 | Function({ columns, data }) | - | - |
| span-method | 合并行或列的计算方法 | Function({ row, column, rowIndex, columnIndex }) | - | - |
| select-on-indeterminate | 当仅有部分行被选中时,点击表头的多选框时的行为,配合 selection 使用 | boolean | - | true |
| indent | 展示树形数据时,树节点的缩进 | number | - | 16 |
| lazy | 是否懒加载子节点数据 | boolean | - | - |
| load | 加载子节点数据的函数lazy 为 true 时生效 | Function(row, treeNode, resolve) | - | - |
| tree-props | 渲染嵌套数据的配置选项 | Object | - | { hasChildren: 'hasChildren', children: 'children' } |
##### 参数
| 参数 | 说明 | 类型 | 可选值 | 默认值 |
| :------------------ | :-------------------------------------------------------------------- | :-------------------------------------- | :------------------------------------------------------------------------------------------------------------------------------ | :-------------------------------- |
| prop | 对应 data 的字段名 (**必填,需要是唯一值**) | string | - | - |
| label | 显示的标题 | string | - | - |
| slot | 是否开启自定义插槽功能 | boolean | - | false |
| hide | 是否在表格中隐藏 | boolean | - | false |
| children | 实现多级表头 | array | - | - |
| columnKey | 当前项的 key使用 filter-change 事件时需要 | string | - | - |
| width | 对应列的宽度 | string | - | - |
| minWidth | 对应列的最小宽度 | string | - | - |
| fixed | 列是否固定true 表示固定在左侧 | string / boolean | true / left / right | - |
| renderHeader | 列标题 Label 区域渲染使用的 Function | Function(h, { column, $index }) | - | - |
| sortable | 对应列是否可以排序 | boolean / string | true / false / 'custom' | false |
| sortMethod | 对数据进行排序的时候使用的方法 | Function(a, b) | - | - |
| sortBy | 指定数据按照哪个属性进行排序 | string / array / Function(row, index) | - | - |
| sortOrders | 数据在排序时所使用排序策略的轮转顺序 | array | `ascending` 表示升序,`descending` 表示降序,`null` 表示还原为原始顺序 | ['ascending', 'descending', null] |
| resizable | 对应列是否可以通过拖动改变宽度,配合 border 使用 | boolean | - | true |
| formatter | 用来格式化内容 | Function(row, column, cellValue, index) | - | - |
| showOverflowTooltip | 当内容过长被隐藏时显示 tooltip | Boolean | - | false |
| align | 对齐方式 | string | left / center / right | left |
| headerAlign | 表头对齐方式 | string | left / center / right | 同 align |
| className | 列的 className | string | - | - |
| labelClassName | 当前列标题的自定义类名 | string | - | - |
| filters | 数据过滤的选项 | Array[{ text, value }] | - | - |
| filterPlacement | 过滤弹出框的定位 | string | top / top-start / top-end / bottom / bottom-start / bottom-end / left / left-start / left-end / right / right-start / right-end | - |
| filterMultiple | 数据过滤的选项是否多选 | boolean | - | true |
| filterMethod | 数据过滤使用的方法 | Function(value, row, column) | - | - |
| filteredValue | 选中的数据过滤项 | array | - | - |
| index | 自定义索引,只能够在 index 中配置 | Function(index) / number | - | - |
| selectable | 这一行的 CheckBox 是否可以勾选,只能够在 selection 中配置 | Function(row, index) | - | - |
| reserveSelection | 是否保留之前选中的数据(需指定 `row-key`),只能够在 selection 中配置 | boolean | - | false |
##### 事件
| 事件名 | 说明 | 参数 |
| ------------------ | -------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------- |
| select | 当用户手动勾选数据行的 Checkbox 时触发的事件 | selection, row |
| select-all | 当用户手动勾选全选 Checkbox 时触发的事件 | selection |
| selection-change | 当选择项发生变化时会触发该事件 | selection |
| cell-mouse-enter | 当单元格 hover 进入时会触发该事件 | row, column, cell, event |
| cell-mouse-leave | 当单元格 hover 退出时会触发该事件 | row, column, cell, event |
| cell-click | 当某个单元格被点击时会触发该事件 | row, column, cell, event |
| cell-dblclick | 当某个单元格被双击击时会触发该事件 | row, column, cell, event |
| row-click | 当某一行被点击时会触发该事件 | row, column, event |
| row-contextmenu | 当某一行被鼠标右键点击时会触发该事件 | row, column, event |
| row-dblclick | 当某一行被双击时会触发该事件 | row, column, event |
| header-click | 当某一列的表头被点击时会触发该事件 | column, event |
| header-contextmenu | 当某一列的表头被鼠标右键点击时触发该事件 | column, event |
| sort-change | 当表格的排序条件发生变化的时候会触发该事件 | { column, prop, order } |
| filter-change | 当表格的筛选条件发生变化的时候会触发该事件,参数的值是一个对象,对象的 key 是 column 的 columnKey对应的 value 为用户选择的筛选条件的数组。 | filters |
| current-change | 当表格的当前行发生变化的时候会触发该事件,如果要高亮当前行,请打开表格的 highlight-current-row 属性 | currentRow, oldCurrentRow |
| header-dragend | 当拖动表头改变了列的宽度的时候会触发该事件 | newWidth, oldWidth, column, event |
| expand-change | 当用户对某一行展开或者关闭的时候会触发该事件(展开行时,回调的第二个参数为 expandedRows树形表格时第二参数为 expanded | row, (expandedRows \| expanded) |
| size-change | pageSize 改变时会触发 | 每页条数 |
| current-change | currentPage 改变时会触发 | 当前页 |
| prev-click | 用户点击上一页按钮改变当前页后触发 | 当前页 |
| next-click | 用户点击下一页按钮改变当前页后触发 | 当前页 |
##### 方法
| 方法名 | 说明 | 参数 |
| ------------------ | ----------------------------------------------------------------------------------------------------------------------- | --------------------------- |
| clearSelection | 用于多选表格,清空用户的选择 | - |
| toggleRowSelection | 用于多选表格切换某一行的选中状态如果使用了第二个参数则是设置这一行选中与否selected 为 true 则选中) | row, selected |
| toggleAllSelection | 用于多选表格,切换全选和全不选 | - |
| toggleRowExpansion | 用于可展开表格与树形表格切换某一行的展开状态如果使用了第二个参数则是设置这一行展开与否expanded 为 true 则展开) | row, expanded |
| setCurrentRow | 用于单选表格,设定某一行为选中行,如果调用时不加参数,则会取消目前高亮行的选中状态。 | row |
| clearSort | 用于清空排序条件,数据会恢复成未排序的状态 | - |
| clearFilter | 不传入参数时用于清空所有过滤条件,数据会恢复成未过滤的状态,也可传入由 columnKey 组成的数组以清除指定列的过滤条件 | columnKey |
| doLayout | 对 Table 进行重新布局。当 Table 或其祖先元素由隐藏切换为显示时,可能需要调用此方法 | - |
| sort | 手动对 Table 进行排序。参数`prop`属性指定排序列,`order`指定排序顺序。 | prop: string, order: string |
::: tip 提示
如果使用 `typescript` 可以从组件中导出 `ITableExpose` 提供更好的类型推导
:::
### 插槽
| name | 说明 |
| :------------ | :----------------------------------------------------------------------- |
| - | 在右侧菜单前插入的任意内容 |
| menu | 表格右侧自定义按钮,参数为 { size, row, column, $index } |
| expand | 当 expand 为 true 时,配置展开显示的内容,参数为 { row, column, $index } |
| append | 插入至表格最后一行之后的内容 |
| [prop] | 当前这列的内容,参数为 { size, row, column, $index } |
| [prop]-header | 当前这列表头的内容,参数为 { size, column, $index } |
::: tip 提示
[prop] 为 columns 中定义的 prop
:::

View File

@ -0,0 +1 @@
##### 介绍

View File

@ -0,0 +1,5 @@
##### 安装
```
npm install layui-vue --save
```

View File

@ -0,0 +1,32 @@
##### 基础
::: demo 传入 columns 数据,自动生成表格
<template>
<lay-button type="primary">原始按钮</lay-button>
<lay-button type="default">默认按钮</lay-button>
<lay-button type="normal">百搭按钮</lay-button>
<lay-button type="warm">暖色按钮</lay-button>
<lay-button type="danger">警告按钮</lay-button>
<lay-button type="disabled">禁用按钮</lay-button>
</template>
<script>
import { ref } from 'vue'
export default {
setup() {
return {
}
}
}
</script>
:::
#### 插槽
| name | 说明 |
| :------------ | :----------------------------------------------------------------------- |
| type | 按钮类型 |

View File

@ -0,0 +1 @@
##### 介绍

View File

@ -0,0 +1,5 @@
##### 安装
```
npm install layui-vue --save
```

23
docs/index.html Normal file
View File

@ -0,0 +1,23 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" />
<meta name="renderer" content="webkit" />
<meta name="force-rendering" content="webkit" />
<meta name="applicable-device" content="pc,mobile" />
<meta name="author" content="Jmys <jmys1992@gmail.com>" />
<meta
name="keywords"
content="element-pro,pro-components,admin,element-plus,components,vue,ui"
/>
<link rel="icon" href="/favicon.ico" />
<title></title>
<!--preload-links-->
</head>
<body>
<div id="app"><!--app-html--></div>
<script type="module" src="/src/entry-client.ts"></script>
</body>
</html>

47
docs/prerender.js Normal file
View File

@ -0,0 +1,47 @@
/* eslint-disable @typescript-eslint/no-var-requires */
const fs = require('fs')
const path = require('path')
const toAbsolute = (p) => path.resolve(__dirname, p).replace(/\\/, '/')
const manifest = require('./dist/static/ssr-manifest.json')
const template = fs.readFileSync(toAbsolute('dist/static/index.html'), 'utf-8')
const { render } = require('./dist/server/entry-server.js')
const writeFileRecursive = function (path, buffer) {
const lastPath = path.substring(0, path.lastIndexOf('/'))
fs.mkdir(lastPath, { recursive: true }, () => {
fs.writeFileSync(path, buffer)
})
}
const fileDisplay = (file) => {
fs.readdirSync(toAbsolute(file)).forEach(async (filename) => {
const filedir = path.join(file, filename).replace(/\\/, '/')
if (fs.statSync(toAbsolute(filedir)).isDirectory()) {
fileDisplay(filedir)
} else {
const url = filedir
.replace(/^docs/, '')
.replace(/\.(vue|md)$/, '')
.replace(/index$/, '')
.replace(/\/([^/]*)$/, (item) =>
item.replace(/\B([A-Z])/g, '-$1').toLowerCase()
)
const [appHtml, preloadLinks] = await render(url, manifest)
const html = template
.replace('<!--preload-links-->', preloadLinks)
.replace('<!--app-html-->', appHtml)
const filePath = `dist/static${url.replace(/\/$/, '/index')}.html`
writeFileRecursive(toAbsolute(filePath), html)
console.log('pre-rendered:', filePath)
}
})
}
fileDisplay('docs')
fs.unlinkSync(toAbsolute('dist/static/ssr-manifest.json'))

BIN
docs/public/favicon.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

3
docs/src/App.vue Normal file
View File

@ -0,0 +1,3 @@
<template>
<router-view />
</template>

View File

@ -0,0 +1,152 @@
<template>
<div class="lay-code">
<div class="source">
<slot />
</div>
<div
ref="meta"
class="meta"
>
<div
v-if="$slots.description"
class="description"
>
<slot name="description" />
</div>
<div class="language-html">
<slot name="code" />
</div>
</div>
<div
:class="{ 'is-fixed': isFixContorl }"
class="control"
@click="toggleShow"
>
<i :class="[show ? 'el-icon-caret-top' : 'el-icon-caret-bottom']" />
</div>
</div>
</template>
<script setup lang="ts">
import { onMounted, onUnmounted, ref, watch } from 'vue'
const meta = ref<HTMLElement>({} as HTMLElement)
const isFixContorl = ref(false)
const codeAreaHeight = ref(0)
const show = ref(false);
const toggleShow = function() {
show.value = !show.value;
}
onMounted(() => {
const foundDescs = meta.value.getElementsByClassName('description')
const foundCodes = meta.value.getElementsByClassName('language-html')
if (foundDescs.length) {
codeAreaHeight.value =
foundDescs[0].clientHeight + foundCodes[0].clientHeight + 30
} else {
codeAreaHeight.value = foundCodes[0].clientHeight + 20
}
})
onUnmounted(() => {
window.removeEventListener('scroll', handleScroll)
})
watch(show, (value) => {
if (value) {
meta.value.style.height = `${codeAreaHeight.value}px`
window.addEventListener('scroll', handleScroll)
setTimeout(handleScroll, 100)
} else {
meta.value.style.height = '0'
window.removeEventListener('scroll', handleScroll)
}
})
function handleScroll() {
const { top, bottom } = meta.value.getBoundingClientRect()
isFixContorl.value =
bottom > window.innerHeight && top + 44 <= window.innerHeight
}
</script>
<style>
.lay-code {
margin: 1rem 0;
border: 1px solid whitesmoke;
border-radius: 3px;
background: var(--c-bg);
transition: all 0.2s;
}
.lay-code:hover {
box-shadow: var(--shadow-2);
}
.lay-code .source {
padding: 24px;
}
.lay-code .meta {
padding: 0 10px;
height: 0;
background-color: var(--c-page-background);
overflow: hidden;
transition: height 0.2s;
}
.lay-code .meta .description {
padding: 20px;
margin: 10px 0;
border: 1px solid var(--c-divider);
box-sizing: border-box;
background: var(--c-bg);
font-size: 14px;
line-height: 22px;
color: var(--c-text-light-1);
word-break: break-word;
}
.lay-code .meta .description p {
margin: 0 !important;
line-height: 26px !important;
}
.lay-code .meta .description code {
display: inline-block;
padding: 1px 5px;
margin: 0 4px;
height: 18px;
border-radius: 3px;
background-color: var(--code-inline-bg-color);
font-size: 12px;
line-height: 18px;
color: var(--c-text-light);
}
.lay-code .control {
height: 44px;
box-sizing: border-box;
border-top: 1px solid whitesmoke;
border-bottom-left-radius: 4px;
border-bottom-right-radius: 4px;
background: var(--c-bg);
text-align: center;
color: var(--c-text);
cursor: pointer;
width: 100%;
user-select: none;
}
.lay-code .control.is-fixed {
position: sticky;
z-index: 11;
bottom: 0;
}
.lay-code .control:hover {
background-color: var(--c-page-background);
color: var(--c-brand);
}
.lay-code .control > i {
display: inline-block;
font-size: 16px;
line-height: 44px;
transition: all 0.3s;
}
</style>

7
docs/src/entry-client.ts Normal file
View File

@ -0,0 +1,7 @@
import { createApp } from './main'
const { app, router } = createApp()
router.isReady().then(() => {
app.mount('#app')
})

51
docs/src/entry-server.ts Normal file
View File

@ -0,0 +1,51 @@
import { createApp } from './main'
import { renderToString } from '@vue/server-renderer'
export async function render(url, manifest): Promise<string[]> {
const { app, router } = createApp()
// set the router to the desired URL before rendering
router.push(url)
await router.isReady()
// passing SSR context object which will be available via useSSRContext()
// @vitejs/plugin-vue injects code into a component's setup() that registers
// itself on ctx.modules. After the render, ctx.modules would contain all the
// components that have been instantiated during this render call.
const ctx = {}
const html = await renderToString(app, ctx)
// the SSR manifest generated by Vite contains module -> chunk/asset mapping
// which we can then use to determine what files need to be preloaded for this
// request.
const preloadLinks = renderPreloadLinks(ctx.modules, manifest)
return [html, preloadLinks]
}
function renderPreloadLinks(modules, manifest) {
let links = ''
const seen = new Set()
modules.forEach((id) => {
const files = manifest[id]
if (files) {
files.forEach((file) => {
if (!seen.has(file)) {
seen.add(file)
links += renderPreloadLink(file)
}
})
}
})
return links
}
function renderPreloadLink(file) {
if (file.endsWith('.js')) {
return `<link rel="modulepreload" crossorigin href="${file}">`
} else if (file.endsWith('.css')) {
return `<link rel="stylesheet" href="${file}">`
} else {
// TODO
return ''
}
}

View File

@ -0,0 +1,25 @@
<template>
<lay-layout>
<lay-header>
<lay-logo>layui-logo</lay-logo>
</lay-header>
<lay-side>
<router-link to="/zh-CN/components/layout">布局</router-link>
<router-link to="/zh-CN/components/button">按钮</router-link>
</lay-side>
<lay-body>
<div style="padding: 20px">
<router-view></router-view>
</div>
</lay-body>
<lay-footer></lay-footer>
</lay-layout>
</template>
<script>
import { ref } from 'vue'
export default {
setup() {
return {}
},
}
</script>

25
docs/src/main.ts Normal file
View File

@ -0,0 +1,25 @@
import Layout from './App.vue'
import { App, createApp as _createApp, createSSRApp } from 'vue'
import { createRouter } from './router/index'
import { Router } from 'vue-router'
import Layui from '/@src/index'
import LayCode from './components/LayCode.vue'
import './styles/index.css'
export function createApp(): {
app: App<Element>
router: Router
} {
const app =
import.meta.env.MODE === 'production'
? createSSRApp(Layout)
: _createApp(Layout)
const router = createRouter()
app
.use(router)
.component('LayCode', LayCode)
.use(Layui)
return { app, router }
}

View File

@ -0,0 +1,47 @@
import vue from '@vitejs/plugin-vue'
import Markdown from 'vite-plugin-md'
import container from 'markdown-it-container'
import highlight from './highlight'
import snippet from './snippet'
import demo from './demo'
import createContainer from './create-container'
import preWrapper from './pre-wrapper'
import type Token from 'markdown-it/lib/token'
const plugins = [
vue({
include: [/\.vue$/, /\.md$/],
}),
Markdown({
markdownItOptions: {
html: true,
linkify: true,
typographer: true,
highlight,
},
markdownItSetup(md) {
md.use(snippet)
.use(preWrapper)
.use(container, 'demo', demo)
.use(...createContainer('tip', 'TIP'))
.use(...createContainer('warning', 'WARNING'))
.use(...createContainer('danger', 'WARNING'))
.use(container, 'v-pre', {
render: (tokens: Token[], idx: number) =>
tokens[idx].nesting === 1 ? '<div v-pre>\n' : '</div>\n',
})
.use(container, 'details', {
render: (tokens: Token[], idx: number) => {
const info = tokens[idx].info.trim().slice(7).trim() // 7 = 'details'.length
return tokens[idx].nesting === 1
? `<details class="custom-block details">${
info ? `<summary>${info}</summary>` : ''
}\n`
: '</details>'
},
})
},
}),
]
export default plugins

View File

@ -0,0 +1,33 @@
import container from 'markdown-it-container'
import type Token from 'markdown-it/lib/token'
type ContainerArgs = [
typeof container,
string,
{
render(tokens: Token[], idx: number): string
}
]
export default function createContainer(
klass: string,
defaultTitle: string
): ContainerArgs {
return [
container,
klass,
{
render(tokens, idx) {
const token = tokens[idx]
const info = token.info.trim().slice(klass.length).trim()
if (token.nesting === 1) {
return `<div class="${klass} custom-block"><p class="custom-block-title">${
info || defaultTitle
}</p>\n`
} else {
return '</div>\n'
}
},
},
]
}

146
docs/src/plugin/demo.ts Normal file
View File

@ -0,0 +1,146 @@
import markdown from 'markdown-it'
import highlight from './highlight'
import type Token from 'markdown-it/lib/token'
/**
* Combine the script content
* @param {string} script script string
*/
function assignScript(script: string) {
const dependencies = {} as Record<string, string[]>
const attrs = {} as Record<string, string>
const content = script
// import { ref } from 'vue' -> ''
.replace(/import\s?\{.*\}.*/g, (item) => {
const key = getInnerString(item.replace(/'/g, '"'), '"', '"')
const value = getInnerString(item.replace(/\s+/g, ''), '{', '}')
const list = value ? value.split(',') : []
if (key && dependencies[key]) {
dependencies[key] = dependencies[key].concat(list)
} else if (key) {
dependencies[key] = list
}
return ''
})
/**
* const -> let
*
* const a = -> let a =
* const a = -> a =
*/
.replace(/(const|let|var)\s\w*\s?=/g, (item) => {
const attr = getInnerString(item, '\\s', '\\s?=')
if (attr && !(attr in attrs)) {
attrs[attr] = attr
return `let ${attr} =`
} else {
return attr + ' ='
}
})
// Remove extra line breaks
.replace(/\n+/gm, '\n')
// Combine the import
const reImport = Object.keys(dependencies).reduce((all, item) => {
const filterAttrs = [...new Set(dependencies[item])]
return all + `import {${filterAttrs + ','}} from '${item}';\n`
}, '')
return reImport + content
}
/**
* Extract part of the new string from the middle of the string
* @param {string} string string
* @param {string} prefix RegExp string
* @param {string} postfix RegExp string
* @param {string} type g | m | i
*/
function getInnerString(
string: string,
prefix: string,
postfix = '',
type: 'i' | 'g' | 'm' = 'i'
): string | undefined {
const result = new RegExp(`${prefix}(.*)${postfix}`, type)
const match = string.match(result)
return match ? match[1].trim() : undefined
}
let script = '' // Record the <script> label of the current page
export default {
render: (tokens: Token[], idx: number): string => {
// the `demo` block of the current page
const htmlBlock = tokens.filter((item) => item.type === 'html_block')
const { nesting, info = '', map } = tokens[idx]
if (nesting === -1) {
return '</lay-code>'
}
const matchedInfo = info.trim().match(/^demo\s+(.*)$/)
const description = matchedInfo && matchedInfo[1]
const descTemplate = markdown().render(description || '')
let str = '' // copy the current `demo` block code
let lastLine = NaN
for (let i = 0; i < htmlBlock.length; i++) {
const item = htmlBlock[i]
if (item.map && map && item.map[0] >= map[0] && item.map[1] <= map[1]) {
const { map, content } = item
const delta = map[0] - (lastLine || map[1])
if (delta > 0) {
str += '\n'.repeat(delta)
}
str += content
lastLine = map[1]
if (i === 0) {
script = ''
}
// Remove top <template>
if (/^<template>/.test(content)) {
const reContent = content.match(/^<template>((\s|\S)*)<\/template>/m)
htmlBlock[i].content = (reContent && reContent[1]) || ''
}
// Extract the <script> label content
if (content.includes('<script')) {
if (/export\sdefault\s?\{/m.test(content)) {
const setup = content.match(
/setup\s?\(\)\s?\{((\s|\S)*)return\s?\{/m
)
const reContent = content.replace(
/export\sdefault\s?\{((\s|\S)*)\}/m,
(setup && setup[1]) || ''
)
const reScript = reContent.match(
/^<script\s?.*?>((\s|\S)*)<\/script>/m
)
script += (reScript && reScript[1]) || ''
} else {
const reScript = content.match(
/^<script\s?.*?>((\s|\S)*)<\/script>/m
)
script += (reScript && reScript[1]) || ''
}
htmlBlock[i].content = ''
}
// Change the last content to <script> of the current page
if (i + 1 === htmlBlock.length) {
htmlBlock[i].content = `
<script setup>
${assignScript(script)}
</script>`
}
}
}
return `
<lay-code>
${description ? `<template #description>${descTemplate}</template>` : ''}
<template #code>${highlight(str, 'vue')}</template>
`
},
}

View File

@ -0,0 +1,46 @@
// copy from [vitepress](https://github.com/vuejs/vitepress)
import prism from 'prismjs'
import loadLanguages from 'prismjs/components/index'
import escapeHtml from 'escape-html'
// required to make embedded highlighting work...
loadLanguages(['markup', 'css', 'javascript'])
function wrap(code: string, lang: string): string {
if (lang === 'text') {
code = escapeHtml(code)
}
return `<pre v-pre><code>${code}</code></pre>`
}
export default (str: string, lang: string): string => {
if (!lang) {
return wrap(str, 'text')
}
lang = lang.toLowerCase()
const rawLang = lang
if (lang === 'vue' || lang === 'html') {
lang = 'markup'
}
if (lang === 'md') {
lang = 'markdown'
}
if (lang === 'ts') {
lang = 'typescript'
}
if (lang === 'py') {
lang = 'python'
}
if (!prism.languages[lang]) {
try {
loadLanguages([lang])
} catch (e) {
console.warn(lang, e)
}
}
if (prism.languages[lang]) {
const code = prism.highlight(str, prism.languages[lang], lang)
return wrap(code, rawLang)
}
return wrap(str, 'text')
}

View File

@ -0,0 +1,11 @@
import MarkdownIt from 'markdown-it'
export default (md: MarkdownIt): void => {
const fence = md.renderer.rules.fence!
md.renderer.rules.fence = (...args) => {
const [tokens, idx] = args
const token = tokens[idx]
const rawCode = fence(...args)
return `<div class="language-${token.info.trim()}">${rawCode}</div>`
}
}

View File

@ -0,0 +1,49 @@
// copy from [vitepress](https://github.com/vuejs/vitepress)
import fs from 'fs'
import MarkdownIt from 'markdown-it'
import { RuleBlock } from 'markdown-it/lib/parser_block'
export default (md: MarkdownIt): void => {
const parser: RuleBlock = (state, startLine, endLine, silent) => {
const CH = '<'.charCodeAt(0)
const pos = state.bMarks[startLine] + state.tShift[startLine]
const max = state.eMarks[startLine]
// if it's indented more than 3 spaces, it should be a code block
if (state.sCount[startLine] - state.blkIndent >= 4) {
return false
}
for (let i = 0; i < 3; ++i) {
const ch = state.src.charCodeAt(pos + i)
if (ch !== CH || pos + i >= max) return false
}
if (silent) {
return true
}
const start = pos + 3
const end = state.skipSpacesBack(max, pos)
const rawPath = state.src
.slice(start, end)
.trim()
.replace(/^@/, process.cwd())
const content = fs.existsSync(rawPath)
? fs.readFileSync(rawPath).toString()
: 'Not found: ' + rawPath
const meta = rawPath.replace(rawPath, '')
state.line = startLine + 1
const token = state.push('fence', 'code', 0)
token.info = rawPath.split('.').pop() + meta
token.content = content
token.markup = '```'
token.map = [startLine, startLine + 1]
return true
}
md.block.ruler.before('fence', 'snippet', parser)
}

20
docs/src/router/index.ts Normal file
View File

@ -0,0 +1,20 @@
import {
createMemoryHistory,
createRouter as _createRouter,
createWebHistory,
Router,
} from 'vue-router'
import zhCN from './zh-CN'
import type { IRouteRecordRaw } from '/@src/index'
const routes: IRouteRecordRaw[] = [...zhCN]
export function createRouter(): Router {
const baseUrl = import.meta.env.BASE_URL
return _createRouter({
history: import.meta.env.SSR
? createMemoryHistory(baseUrl)
: createWebHistory(baseUrl),
routes: routes
})
}

32
docs/src/router/zh-CN.ts Normal file
View File

@ -0,0 +1,32 @@
import BaseLayout from '../layouts/Layout.vue'
const zhCN = [
{
path: '/',
redirect: '/zh-CN/guide/',
component: BaseLayout,
meta: { title: '指南', icon: 'el-icon-position' },
children: [
{
path: '/zh-CN/guide/',
component: () => import('../../docs/zh-CN/guide/home.md'),
meta: { title: '介绍' },
}
],
},
{
path: '/zh-CN/components/',
redirect: '/zh-CN/components/button',
component: BaseLayout,
meta: { title: '组件', icon: 'el-icon-copy-document' },
children: [
{
path: '/zh-CN/components/button',
component: () => import('../../docs/zh-CN/components/button.md'),
meta: { title: '按钮' },
}
],
},
]
export default zhCN

214
docs/src/styles/code.css Normal file
View File

@ -0,0 +1,214 @@
/* copy from [vitepress](https://github.com/vuejs/vitepress) */
code {
margin: 0;
border-radius: 3px;
padding: 0.25rem 0.5rem;
font-family: var(--code-font-family);
font-size: 0.85em;
color: var(--c-text-light);
background-color: var(--code-inline-bg-color);
}
code .token.deleted {
color: #ec5975;
}
code .token.inserted {
color: var(--c-brand);
}
div[class*='language-'] {
position: relative;
margin: 1rem -1.5rem;
background-color: #fafafa;
overflow-x: auto;
border: 1px solid #eaeefb;
}
li > div[class*='language-'] {
border-radius: 6px 0 0 6px;
margin: 1rem -1.5rem 1rem -1.25rem;
}
@media (min-width: 420px) {
div[class*='language-'] {
margin: 1rem 0;
border-radius: 6px;
}
li > div[class*='language-'] {
margin: 1rem 0 1rem 0rem;
border-radius: 6px;
}
}
[class*='language-'] pre,
[class*='language-'] code {
text-align: left;
white-space: pre;
word-spacing: normal;
word-break: normal;
word-wrap: normal;
-moz-tab-size: 4;
-o-tab-size: 4;
tab-size: 4;
-webkit-hyphens: none;
-moz-hyphens: none;
-ms-hyphens: none;
hyphens: none;
}
[class*='language-'] pre {
position: relative;
z-index: 1;
margin: 0;
padding: 1.25rem 1.5rem;
background: transparent;
overflow-x: auto;
}
[class*='language-'] pre p {
margin: auto !important;
}
[class*='language-'] code {
padding: 0;
line-height: var(--code-line-height);
font-size: var(--code-font-size);
color: #000;
background-color: #fafafa;
}
/* Line highlighting */
.highlight-lines {
position: absolute;
top: 0;
bottom: 0;
left: 0;
padding: 1.25rem 0;
width: 100%;
line-height: var(--code-line-height);
font-family: var(--code-font-family);
font-size: var(--code-font-size);
user-select: none;
overflow: hidden;
}
.highlight-lines .highlighted {
background-color: rgba(0, 0, 0, 0.66);
}
/* Line numbers mode */
div[class*='language-'].line-numbers-mode {
padding-left: 3.5rem;
}
.line-numbers-wrapper {
position: absolute;
top: 0;
bottom: 0;
left: 0;
z-index: 3;
border-right: 1px solid rgba(0, 0, 0, 0.5);
padding: 1.25rem 0;
width: 3.5rem;
text-align: center;
line-height: var(--code-line-height);
font-family: var(--code-font-family);
font-size: var(--code-font-size);
color: #888;
}
/* Language marker */
[class*='language-']:before {
position: absolute;
top: 0.6em;
right: 1em;
z-index: 2;
font-size: 0.8rem;
color: #888;
}
/**
* prism.js tomorrow night eighties for JavaScript, CoffeeScript, CSS and HTML.
* Based on https://github.com/chriskempson/tomorrow-theme
*
* @author Rose Pritchard
*/
.token.comment,
.token.block-comment,
.token.prolog,
.token.doctype,
.token.cdata {
color: #999;
}
.token.punctuation {
color: #ccc;
}
.token.tag,
.token.attr-name,
.token.namespace,
.token.deleted {
color: #e2777a;
}
.token.function-name {
color: #6196cc;
}
.token.boolean,
.token.number,
.token.function {
color: #f08d49;
}
.token.property,
.token.class-name,
.token.constant,
.token.symbol {
color: #f8c555;
}
.token.selector,
.token.important,
.token.atrule,
.token.keyword,
.token.builtin {
color: #cc99cd;
}
.token.string,
.token.char,
.token.attr-value,
.token.regex,
.token.variable {
color: #7ec699;
}
.token.operator,
.token.entity,
.token.url {
color: #67cdcc;
}
.token.important,
.token.bold {
font-weight: bold;
}
.token.italic {
font-style: italic;
}
.token.entity {
cursor: help;
}
.token.inserted {
color: green;
}

View File

@ -0,0 +1,71 @@
.custom-block.tip,
.custom-block.warning,
.custom-block.danger {
margin: 1rem 0;
border-left: 0.3rem solid;
padding: 0.1rem 1.5rem;
overflow-x: auto;
border-radius: 4px;
}
.custom-block.tip {
background-color: #f3f5f7;
border-color: #409eff;
}
.custom-block.warning {
border-color: #e7c000;
color: #6b5900;
background-color: rgba(255, 229, 100, 0.3);
}
.custom-block.warning .custom-block-title {
color: #b29400;
}
.custom-block.warning a {
color: var(--c-text);
}
.custom-block.danger {
border-color: #c00;
color: #4d0000;
background-color: #ffe6e6;
}
.custom-block.danger .custom-block-title {
color: #900;
}
.custom-block.danger a {
color: var(--c-text);
}
.custom-block.details {
position: relative;
display: block;
border-radius: 2px;
margin: 1.6em 0;
padding: 1.6em;
background-color: #eee;
}
.custom-block.details h4 {
margin-top: 0;
}
.custom-block.details figure:last-child,
.custom-block.details p:last-child {
margin-bottom: 0;
padding-bottom: 0;
}
.custom-block.details summary {
outline: none;
cursor: pointer;
}
.custom-block-title {
margin-bottom: -0.4rem;
font-weight: 600;
}

View File

@ -0,0 +1,4 @@
@import './code.css';
@import './custom-blocks.css';
@import './markdown.css';
@import './vars.css';

View File

@ -0,0 +1,220 @@
html {
line-height: 1.4;
font-size: 16px;
-webkit-text-size-adjust: 100%;
}
h4 {
font-weight: 700 !important;
font-size: 16.8px !important;
margin-top: 20px !important;
margin-bottom: 20px !important;
}
h5 {
font-weight: 700;
font-size: 16.8px;
}
h1,
h2,
h3,
h4,
h5,
h6 {
margin: 0;
color: #303133;
line-height: 1.25;
}
h1,
h2,
h3,
h4,
h5,
h6,
strong,
b {
font-weight: 600;
}
h1:hover .header-anchor,
h1:focus .header-anchor,
h2:hover .header-anchor,
h2:focus .header-anchor,
h3:hover .header-anchor,
h3:focus .header-anchor,
h4:hover .header-anchor,
h4:focus .header-anchor,
h5:hover .header-anchor,
h5:focus .header-anchor,
h6:hover .header-anchor,
h6:focus .header-anchor {
opacity: 1;
}
h1 {
margin-top: 1.5rem;
font-size: 1.9rem;
}
@media screen and (min-width: 420px) {
h1 {
font-size: 2.2rem;
}
}
h2 {
margin-top: 2.25rem;
margin-bottom: 1.25rem;
border-bottom: 1px solid var(--c-divider);
padding-bottom: 0.3rem;
line-height: 1.25;
font-size: 1.65rem;
/* overflow-x: auto; */
}
h2 + h3 {
margin-top: 1.5rem;
}
h3 {
margin-top: 2rem;
font-size: 1.35rem;
}
h4 {
font-size: 1.15rem;
}
h1 {
margin-top: 4px !important;
margin-bottom: 22px !important;
color: #000000d9 !important;
font-weight: 500 !important;
font-size: 20px !important;
font-family: Avenir, -apple-system, BlinkMacSystemFont, segoe ui, Roboto,
helvetica neue, Arial, noto sans, sans-serif, apple color emoji,
segoe ui emoji, segoe ui symbol, noto color emoji, sans-serif;
line-height: 38px;
}
h2 {
margin-top: 4px !important;
margin-bottom: 16px !important;
color: #000000d9 !important;
font-weight: 500 !important;
font-size: 16px !important;
border-bottom: 0px;
font-family: Avenir, -apple-system, BlinkMacSystemFont, segoe ui, Roboto,
helvetica neue, Arial, noto sans, sans-serif, apple color emoji,
segoe ui emoji, segoe ui symbol, noto color emoji, sans-serif;
line-height: 38px;
}
p,
ol,
ul {
margin: 1rem 0;
line-height: 1.7;
}
p {
color: #454d64 !important;
margin-top: 20px !important;
font-size: 14px !important;
}
a,
area,
button,
[role='button'],
input,
label,
select,
summary,
textarea {
touch-action: manipulation;
}
a {
text-decoration: none;
color: var(--c-brand);
}
a:hover {
text-decoration: underline;
}
a.header-anchor {
float: left;
margin-top: 0.125em;
margin-left: -0.87em;
padding-right: 0.23em;
font-size: 0.85em;
opacity: 0;
}
a.header-anchor:hover,
a.header-anchor:focus {
text-decoration: none;
}
figure {
margin: 0;
}
img {
max-width: 100%;
}
ul,
ol {
padding-left: 1.25em;
}
li > ul,
li > ol {
margin: 0;
}
blockquote {
margin: 1rem 0;
border-left: 0.2rem solid #dfe2e5;
padding: 0.25rem 0 0.25rem 1rem;
font-size: 1rem;
color: #999;
}
blockquote > p {
margin: 0;
}
form {
margin: 0;
}
table {
width: 100%; /*表格宽度*/
border-collapse: collapse; /*使用单一线条的边框*/
empty-cells: show; /*单元格无内容依旧绘制边框*/
}
table th,
table td {
font-size: 14px;
color: #606266;
height: 50px; /*统一每一行的默认高度*/
border-top: 1px solid whitesmoke; /*内部边框样式*/
padding: 0 10px; /*内边距*/
}
table tr:hover {
background: #efefef;
}
table th {
white-space: nowrap; /*表头内容强制在一行显示*/
}
table td {
white-space: nowrap;
}

52
docs/src/styles/vars.css Normal file
View File

@ -0,0 +1,52 @@
/* TODO: rebuild... copy from [vitepress](https://github.com/vuejs/vitepress) */
:root {
--c-white: #ffffff;
--c-black: #000000;
--c-divider-light: rgba(60, 60, 67, 0.12);
--c-divider-dark: rgba(84, 84, 88, 0.48);
--c-text-light-1: #2c3e50;
--c-text-light-2: #476582;
--c-text-light-3: #90a4b7;
--c-brand: #409eff;
--c-brand-light: #4abf8a;
/**
* Typography
* --------------------------------------------------------------------- */
--font-family-base: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto,
Oxygen, Ubuntu, Cantarell, 'Fira Sans', 'Droid Sans', 'Helvetica Neue',
sans-serif;
--font-family-mono: source-code-pro, Menlo, Monaco, Consolas, 'Courier New',
monospace;
/**
* Shadows
* --------------------------------------------------------------------- */
--shadow-1: 0 1px 2px rgba(0, 0, 0, 0.04), 0 1px 2px rgba(0, 0, 0, 0.06);
--shadow-2: 0 3px 12px rgba(0, 0, 0, 0.07), 0 1px 4px rgba(0, 0, 0, 0.07);
--shadow-3: 0 12px 32px rgba(0, 0, 0, 0.1), 0 2px 6px rgba(0, 0, 0, 0.08);
--shadow-4: 0 14px 44px rgba(0, 0, 0, 0.12), 0 3px 9px rgba(0, 0, 0, 0.12);
--shadow-5: 0 18px 56px rgba(0, 0, 0, 0.16), 0 4px 12px rgba(0, 0, 0, 0.16);
}
/** Fallback Styles */
:root {
--c-divider: var(--c-divider-light);
--c-text: var(--c-text-light-1);
--c-text-light: var(--c-text-light-2);
--c-text-lighter: var(--c-text-light-3);
--c-bg: var(--c-white);
--code-line-height: 24px;
--code-font-family: var(--font-family-mono);
--code-font-size: 14px;
--code-inline-bg-color: rgba(27, 31, 35, 0.05);
--code-bg-color: #282c34;
}

4
docs/src/tsconfig.json Normal file
View File

@ -0,0 +1,4 @@
{
"extends": "../../tsconfig.json",
"include": [".", "../../shims-vue.d.ts"]
}

13
docs/vite.config.ts Normal file
View File

@ -0,0 +1,13 @@
import path from 'path'
import { defineConfig } from 'vite'
import plugins from './src/plugin/common-plugins'
export default defineConfig({
base: '/layui-vue/',
resolve: {
alias: {
'/@src': path.resolve(__dirname, '../src'),
},
},
plugins,
})

27
jest.config.js Normal file
View File

@ -0,0 +1,27 @@
module.exports = {
roots: ['<rootDir>/test'],
preset: 'ts-jest',
testEnvironment: 'jsdom',
moduleFileExtensions: ['ts', 'vue', 'js', 'jsx', 'tsx', 'json', 'node'],
transformIgnorePatterns: ['node_modules/(?!(@vue/shared|element-plus)/)'],
moduleNameMapper: { '\\.css$': '<rootDir>/test/__mocks__/css.ts' },
transform: {
'^.+\\.vue$': 'vue-jest',
'^.+\\.(t|j)sx?$': [
'babel-jest',
{
presets: [
[
'@babel/preset-env',
{
targets: {
node: 'current',
},
},
],
'@babel/preset-typescript',
],
},
],
},
}

98
package.json Normal file
View File

@ -0,0 +1,98 @@
{
"name": "layui-vue",
"version": "0.3.7",
"description": "a component library for Vue 3 base on layui-vue",
"main": "lib/layui-vue.umd.js",
"module": "lib/layui-vue.es.js",
"exports": {
".": {
"import": "./lib/layui-vue.es.js",
"require": "./lib/layui-vue.umd.js"
},
"./lib/": "./lib/"
},
"types": "types/index.d.ts",
"style": "lib/style.css",
"scripts": {
"dev": "vite",
"build": "vite build --emptyOutDir && npm run build:helper && npm run build:types",
"build:types": "rimraf types && tsc -d",
"build:helper": "node .helperrc.js",
"build:docs": "vite build docs",
"test": "jest",
"lint": "eslint . --fix --ext .ts,.vue,.js --ignore-pattern \"/lib/\" --ignore-pattern \"/types/\"",
"prettier": "prettier --check --write --ignore-unknown \"{example,src,docs,test}/**\""
},
"repository": {
"type": "git",
"url": "https://gitee.com/Jmysy/layui-vue"
},
"keywords": [
"layui-vue",
"layui",
"vue"
],
"author": "<就眠仪式 jmys1992@gmail.com>",
"license": "MIT",
"bugs": {
"url": "https://gitee.com/Jmysy/layui-vue/issues"
},
"homepage": "https://gitee.com/Jmysy/layui-vue/blob/master/README.md",
"peerDependencies": {
"vue": "^3.1.2",
"vue-router": "^4.0.10"
},
"dependencies": {
"vue": "^3.1.2",
"vue-router": "^4.0.10"
},
"devDependencies": {
"@babel/core": "^7.14.0",
"@babel/preset-env": "^7.14.0",
"@babel/preset-typescript": "^7.13.0",
"@types/jest": "^26.0.23",
"@types/markdown-it": "^12.0.1",
"@types/markdown-it-container": "^2.0.3",
"@typescript-eslint/eslint-plugin": "^4.22.0",
"@typescript-eslint/parser": "^4.22.0",
"@vitejs/plugin-vue": "^1.2.2",
"@vue/compiler-sfc": "^3.1.2",
"@vue/server-renderer": "^3.1.1",
"@vue/test-utils": "^2.0.0-rc.6",
"babel-jest": "^26.6.3",
"components-helper": "^1.0.3",
"escape-html": "^1.0.3",
"eslint": "^7.25.0",
"eslint-plugin-prettier": "^3.4.0",
"eslint-plugin-vue": "^7.9.0",
"husky": "^4.3.6",
"jest": "^26.6.3",
"lint-staged": "^10.5.4",
"markdown-it-container": "^3.0.0",
"postcss": "^8.2.13",
"postcss-preset-env": "^6.7.0",
"prettier": "2.2.1",
"prismjs": "^1.23.0",
"rimraf": "^3.0.2",
"rollup-plugin-babel": "^4.4.0",
"ts-jest": "^26.5.5",
"typescript": "^4.2.4",
"vite": "2.3.7",
"vite-plugin-md": "^0.6.3",
"vue-jest": "^5.0.0-alpha.8"
},
"vetur": {
"tags": "lib/tags.json",
"attributes": "lib/attributes.json"
},
"web-types": "lib/web-types.json",
"files": [
"lib",
"types"
],
"husky": {
"hooks": {
"pre-commit": "lint-staged"
}
}
}

25
shims-vue.d.ts vendored Normal file
View File

@ -0,0 +1,25 @@
declare module '*.vue' {
import { DefineComponent } from 'vue'
const comp: DefineComponent
export default comp
}
declare module '*.md' {
import { DefineComponent } from 'vue'
const comp: DefineComponent
export default comp
}
declare module 'prismjs'
declare module 'prismjs/components/index'
declare module 'escape-html'
interface ImportMeta {
env: {
MODE: string
BASE_URL: string
PROD: boolean
DEV: boolean
SSR: boolean
}
}

6593
src/css/layui.css Normal file

File diff suppressed because it is too large Load Diff

BIN
src/font/iconfont.eot Normal file

Binary file not shown.

387
src/font/iconfont.svg Normal file

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 309 KiB

BIN
src/font/iconfont.ttf Normal file

Binary file not shown.

BIN
src/font/iconfont.woff Normal file

Binary file not shown.

BIN
src/font/iconfont.woff2 Normal file

Binary file not shown.

50
src/index.ts Normal file
View File

@ -0,0 +1,50 @@
import type { App } from 'vue'
import type { IDefineComponent, InstallOptions } from './module/type/index'
import "./css/layui.css";
import LayRadio from './module/radio/index'
import LayButton from './module/button/index'
import LayIcon from './module/icon/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"
const components: Record<string, IDefineComponent> = {
LayRadio,
LayButton,
LayIcon,
LayLayout,
LaySide,
LayHeader,
LayBody,
LayFooter,
LayLogo
}
const install = (app: App, options?: InstallOptions): void => {
const _options = options;
app.config.globalProperties.$PROOPTIONS = _options
for (const key in components) {
const item = components[key]
app.component(item.name || key, item)
}
}
export {
LayRadio,
LayIcon,
LayButton,
LayLayout,
LaySide,
LayHeader,
LayBody,
LayFooter,
LayLogo,
install,
}
export default { install }

9
src/module/body/index.ts Normal file
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 || 'LayBody', Component)
}
export default Component as IDefineComponent

View File

@ -0,0 +1,9 @@
<template>
<div class="layui-body">
<slot></slot>
</div>
</template>
<script setup name="LayBody" 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 || 'LayButton ', Component)
}
export default Component as IDefineComponent

View File

@ -0,0 +1,14 @@
<template>
<button class="layui-btn" :class="[type ? 'layui-btn-' + type : '']">
<slot></slot>
</button>
</template>
<script setup name="LayButton" lang="ts">
import { defineProps } from "@vue/runtime-core";
const props = defineProps<{
type?: string
}>()
</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 || 'LayFooter', Component)
}
export default Component as IDefineComponent

View File

@ -0,0 +1,9 @@
<template>
<div class="layui-footer">
<slot></slot>
</div>
</template>
<script setup name="LayFooter" 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 || 'LayHeader', Component)
}
export default Component as IDefineComponent

View File

@ -0,0 +1,9 @@
<template>
<div class="layui-header">
<slot></slot>
</div>
</template>
<script setup name="LayHeader" lang="ts">
</script>

9
src/module/icon/index.ts Normal file
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 || 'LayIcon', Component)
}
export default Component as IDefineComponent

View File

@ -0,0 +1,7 @@
<template>
<button class="layui-btn">按钮</button>
</template>
<script setup name="LayButton" 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 || 'LayLayout', Component)
}
export default Component as IDefineComponent

View File

@ -0,0 +1,9 @@
<template>
<div class="layui-layout layui-layout-admin">
<slot></slot>
</div>
</template>
<script setup name="LayLayout" lang="ts">
</script>

9
src/module/logo/index.ts Normal file
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 || 'LayLogo', Component)
}
export default Component as IDefineComponent

View File

@ -0,0 +1,9 @@
<template>
<div class="layui-logo">
<slot></slot>
</div>
</template>
<script setup name="LayLogo" 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 || 'LayRadio ', Component)
}
export default Component as IDefineComponent

View File

@ -0,0 +1,7 @@
<template>
<div></div>
</template>
<script setup name="LayRadio" lang="ts">
</script>

9
src/module/side/index.ts Normal file
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 || 'LaySide', Component)
}
export default Component as IDefineComponent

View File

@ -0,0 +1,9 @@
<template>
<div class="layui-side">
<slot></slot>
</div>
</template>
<script setup name="LaySide" lang="ts">
</script>

1
src/module/type/index.ts Normal file
View File

@ -0,0 +1 @@
export * from './public'

17
src/module/type/public.ts Normal file
View File

@ -0,0 +1,17 @@
import type { App, DefineComponent } from 'vue'
export type StringObject = Record<string, unknown>
export type UnknownObject = Record<string | number, unknown>
export type UnknownFunction = (...arg: unknown[]) => unknown
export type IDefineComponent<Props = UnknownObject> = DefineComponent<Props> & {
install: (app: App, options?: InstallOptions) => void
}
export interface InstallOptions extends StringObject {
/** Pagination Attributes */
pagination?: null
/** Menu Attributes */
menu?: null
}

20
tsconfig.json Normal file
View File

@ -0,0 +1,20 @@
{
"compilerOptions": {
"baseUrl": ".",
"module": "esnext",
"target": "es2015",
"moduleResolution": "Node",
"strict": true,
"resolveJsonModule": true,
"declaration": true,
"emitDeclarationOnly": true,
"esModuleInterop": true,
"declarationDir": "types",
"lib": ["ESNext", "DOM"],
"paths": {
"/@src/*": ["./src/*"]
}
},
"include": ["src/**/*", "shims-vue.d.ts"],
"exclude": ["node_modules"]
}

44
vite.config.ts Normal file
View File

@ -0,0 +1,44 @@
import path from 'path'
import { defineConfig } from 'vite'
import babel from 'rollup-plugin-babel'
import { name } from './package.json'
import plugins from './docs/src/plugin/common-plugins'
const camelize = (name: string) =>
name.replace(/(^|-)(\w)/g, (a, b, c) => c.toUpperCase())
export default defineConfig({
root: path.resolve(__dirname, 'docs'),
resolve: {
alias: {
'/@src': path.resolve(__dirname, 'src'),
},
},
build: {
target: 'es2015',
outDir: path.resolve(__dirname, 'lib'),
lib: {
entry: path.resolve(__dirname, 'src/index.ts'),
name: camelize(name),
},
rollupOptions: {
output: {
exports: 'named',
globals: (id: string) => {
const name = id.replace(/^@/, '').split('/')[0]
return camelize(name)
},
},
external: (id: string) =>
/^(vue|@vue|element-plus|resize-observer-polyfill)/.test(id),
plugins: [
babel({
exclude: 'node_modules/**',
extensions: ['.js', '.jsx', '.ts', '.tsx', '.vue'],
presets: ['@babel/preset-env', '@babel/preset-typescript'],
}),
],
},
},
plugins,
})