This commit is contained in:
2022-12-09 16:41:41 +08:00
parent c1cce5a7c2
commit ff7aa8774f
2003 changed files with 156639 additions and 140 deletions

View File

@@ -0,0 +1,147 @@
import type { BuiltInParserName } from "prettier";
import { zlibSync, unzlibSync, strToU8, strFromU8 } from "fflate";
import extraCss from "../assets/css/index.css?raw";
const scriptRe = /<script[^>]*>([\s\S]*)<\/script>/;
const exportDefaultRe = /export\s*default\s*\{([\s\S]*)\}\;?\s*<\/script>/;
const setupRe = /setup\s*\(\)\s*\{([\s\S]*)return\s*\{([\s\S]*)\}\;?\s*\}\;?/;
const layerRe = /import\s*\{\s*layer\s*\}\s*from\s*[\"|\'][^\;\"\']*[\"|\']/;
const MAIN_FILE_NAME = "App.vue";
/**
* 将代码编码为 URI hash, 生成 playground 链接
* @param source 源码
* @param convertSetupSugar 转换 setup仅字符串替换没有做任何语法分析
* @returns 处理后的代码URI hsah playground 链接
}
*/
export const usePlayGround = async (
source: string,
convertSetupSugar: boolean
) => {
const decodeCode = source;
const scriptResult = decodeCode.match(scriptRe);
// 替换 script 标签
// $1 正则第一个括号匹配的内容
let code: string | undefined = decodeCode;
if (convertSetupSugar) {
if (scriptResult) {
code = decodeCode.replace(
scriptRe,
`<script lang="ts" setup>$1
</script>`
);
} else {
code = `${decodeCode}
<script lang="ts" setup>
</script>`;
}
// 去除 export default,保留其中的内容
const exportDefaultResult = code.match(exportDefaultRe);
if (exportDefaultResult) {
code = code.replace(
exportDefaultRe,
trimBr(exportDefaultResult[1] + `</script>`).trim()
);
// console.log("export",code);
}
// 去除 setup 函数,保留其中的内容
const setupResult = code.match(setupRe);
if (setupResult) {
code = code.replace(setupRe, trimBr(setupResult[1]));
// console.log("setup",code);
}
}
// 替换 layer 引入语句
// playground 中使用最新版 layer 请从 @layui/layer-vue 引入
if (code.match(layerRe)) {
code = code.replace(layerRe, `import { layer } from "@layui/layer-vue"`);
// console.log("layer",code);
}
code = await formatCode(MAIN_FILE_NAME, code);
const originCode = {
[MAIN_FILE_NAME]: code,
"index.css": extraCss,
};
const encoded = utoa(JSON.stringify(originCode));
const link = `https://layui-vue.gitee.io/sandbox-vue/#${encoded}`;
return {
code,
encoded,
link,
};
};
/**
*
* @returns 格式化代码
*/
async function formatCode(filename: string, data: string) {
const [format, parserHtml, parserTypeScript, parserBabel, parserPostcss] =
await Promise.all([
import("prettier/standalone").then((r) => r.format),
import("prettier/parser-html").then((m) => m.default),
import("prettier/parser-typescript").then((m) => m.default),
import("prettier/parser-babel").then((m) => m.default),
import("prettier/parser-postcss").then((m) => m.default),
]);
let code = data;
let parser: BuiltInParserName;
if (filename.endsWith(".vue")) {
parser = "vue";
} else if (filename.endsWith(".js")) {
parser = "babel";
} else if (filename.endsWith(".ts")) {
parser = "typescript";
} else if (filename.endsWith(".json")) {
parser = "json";
} else {
return;
}
code = format(code, {
parser,
plugins: [parserHtml, parserTypeScript, parserBabel, parserPostcss],
semi: false, // 语句末尾打印分号
singleQuote: true, // 使用单引号
vueIndentScriptAndStyle: false, // 是否缩进 Vue 文件中的 script 和 style 标签
});
return code;
}
/**
* 去除字符串两端的空白行
* @param str
* @returns
*/
function trimBr(str: string): string {
return str.replace(/(^[\r\n]*)|([\r\n]*$)/, "");
}
export function utoa(data: string): string {
const buffer = strToU8(data);
const zipped = zlibSync(buffer, { level: 9 });
const binary = strFromU8(zipped, true);
return btoa(binary);
}
export function atou(base64: string): string {
const binary = atob(base64);
// zlib header (x78), level 9 (xDA)
if (binary.startsWith("\x78\xDA")) {
const buffer = strToU8(binary, true);
const unzipped = unzlibSync(buffer);
return strFromU8(unzipped);
}
// old unicode hacks for backward compatibility
// https://base64.guru/developers/javascript/examples/unicode-strings
return decodeURIComponent(escape(binary));
}

View File

@@ -0,0 +1,20 @@
<script lang="ts">
export default {
name: "UploadDragIcon",
};
</script>
<script setup lang="ts">
import LayIcon from "../component/icon/index";
const props = defineProps<{
color?: string;
size?: string;
}>();
</script>
<template>
<lay-icon
:color="props.color"
:size="props.size"
type="layui-icon-upload-drag"
/>
</template>

View File

@@ -0,0 +1,723 @@
::: anchor
:::
::: title 基本介绍
:::
::: describe 将数据按照指定的格式传入后分层分级,通过此组件逐级查看并选择。
:::
::: title 基础使用
:::
::: demo 使用 `lay-cascader` 标签创建级联选择器
<template>
<lay-cascader :options="options" v-model="value" placeholder="点我试一试" allow-clear>
</lay-cascader>
<span style="margin-left:20px">输出的值:{{value}}</span>
</template>
<script setup>
import { ref } from "vue";
const value=ref(null)
const options = [
{
value: "Guide",
label: "指南",
children: [
{
value: "shejiyuanze",
label: "设计原则",
children: [
{
value: "yizhi",
label: "一致",
},
{
value: "fankui",
label: "反馈",
},
{
value: "xiaolv",
label: "效率",
},
{
value: "kekong",
label: "可控",
},
],
},
{
value: "daohang",
label: "导航",
children: [
{
value: "cexiangdaohang",
label: "侧向导航",
},
{
value: "dingbudaohang",
label: "顶部导航",
},
],
},
],
},
{
value: "Components",
label: "组件",
children: [
{
value: "basic",
label: "Basic",
children: [
{
value: "layout",
label: "Layout 布局",
},
{
value: "color",
label: "Color 色彩",
},
{
value: "typography",
label: "Typography 字体",
},
{
value: "icon",
label: "Icon 图标",
},
{
value: "button",
label: "Button 按钮",
},
],
},
{
value: "form",
label: "Form",
children: [
{
value: "radio",
label: "Radio 单选框",
},
{
value: "checkbox",
label: "Checkbox 多选框",
},
{
value: "input",
label: "Input 输入框",
},
{
value: "input-number",
label: "InputNumber 计数器",
},
{
value: "select",
label: "Select 选择器",
},
{
value: "cascader",
label: "Cascader 级联选择器",
},
{
value: "switch",
label: "Switch 开关",
},
{
value: "slider",
label: "Slider 滑块",
},
{
value: "time-picker",
label: "TimePicker 时间选择器",
},
{
value: "date-picker",
label: "DatePicker 日期选择器",
},
{
value: "datetime-picker",
label: "DateTimePicker 日期时间选择器",
},
{
value: "upload",
label: "Upload 上传",
},
{
value: "rate",
label: "Rate 评分",
},
{
value: "form",
label: "Form 表单",
},
],
},
{
value: "data",
label: "Data",
children: [
{
value: "table",
label: "Table 表格",
},
{
value: "tag",
label: "Tag 标签",
},
{
value: "progress",
label: "Progress 进度条",
},
{
value: "tree",
label: "Tree 树形控件",
},
{
value: "pagination",
label: "Pagination 分页",
},
{
value: "badge",
label: "Badge 标记",
},
],
},
{
value: "notice",
label: "Notice",
children: [
{
value: "alert",
label: "Alert 警告",
},
{
value: "loading",
label: "Loading 加载",
},
{
value: "message",
label: "Message 消息提示",
},
{
value: "message-box",
label: "MessageBox 弹框",
},
{
value: "notification",
label: "Notification 通知",
},
],
},
{
value: "navigation",
label: "Navigation",
children: [
{
value: "menu",
label: "NavMenu 导航菜单",
},
{
value: "tabs",
label: "Tabs 标签页",
},
{
value: "breadcrumb",
label: "Breadcrumb 面包屑",
},
{
value: "dropdown",
label: "Dropdown 下拉菜单",
},
{
value: "steps",
label: "Steps 步骤条",
},
],
},
{
value: "others",
label: "Others",
children: [
{
value: "dialog",
label: "Dialog 对话框",
},
{
value: "tooltip",
label: "Tooltip 文字提示",
},
{
value: "popover",
label: "Popover 弹出框",
},
{
value: "card",
label: "Card 卡片",
},
{
value: "carousel",
label: "Carousel 走马灯",
},
{
value: "collapse",
label: "Collapse 折叠面板",
},
],
},
],
},
{
value: "Resource",
label: "资源",
children: [
{
value: "axure",
label: "Axure Components",
},
{
value: "sketch",
label: "Sketch Templates",
},
{
value: "jiaohu",
label: "组件交互文档",
},
],
},
];
</script>
:::
::: title 自定义分割符号
:::
::: demo 使用 `decollator` 属性 自定义分割符号
<template>
<lay-cascader :options="options" v-model="value1" decollator="-" placeholder="我可以自定义分割符号" allow-clear ></lay-cascader>
<span style="margin-left:20px">输出的值:{{value1}}</span>
</template>
<script setup>
import { ref } from "vue";
const value1=ref(null)
</script>
:::
::: title 控制回显层级
:::
::: demo 使用 `onlyLastLevel` 属性 可以仅在回显的displayValue显示选中项最后一级的标签而不是完整路径, 注意绑定的v-model仍然是完整的。
<template>
<lay-cascader :options="options" v-model="valueLv" :onlyLastLevel="true" allow-clear placeholder="仅显示最后一级" ></lay-cascader>
<span style="margin-left:20px">输出的值:{{valueLv}}</span>
</template>
<script setup>
import { ref } from "vue";
const valueLv=ref(null)
</script>
:::
::: title 触发方式
:::
::: demo 本组件弹出效果依托于DropDown组件触发方式与其保持一致
<template>
<lay-cascader :options="options" placeholder="click触发(默认)" style="width:250px;margin-right:20px"></lay-cascader>
<lay-cascader :options="options" placeholder="hover触发" style="width:250px;margin-right:20px" trigger="hover"></lay-cascader>
<lay-cascader :options="options" placeholder="右键触发" trigger="contextMenu"></lay-cascader>
</template>
:::
::: title Cascader 插槽
:::
::: demo 使用 `默认插槽` 可以自定义回显区域的内容,并且你可以通过change回调轻松拿到回显的值同时你也可以使用`动态插槽名`来自定义你想要展示的内容,只需要在传入的数据中加入 `slot`参数,然后愉快的使用插槽自定义内容
<template>
<lay-cascader :options="options" v-model="value2" @change="onChange">
<lay-button type="normal">Click me ❤️</lay-button>
<lay-badge theme="orange" v-if="displayValue" style="margin-left:10px">{{displayValue}}</lay-badge>
</lay-cascader>
<lay-cascader :options="options2" v-model="value" placeholder="动态插槽案例" style="width:250px;margin-left:20px">
<template #Guide>🤨😐😑😶😏😒🙄😬🤥😌</template>
<template #Components>👋🤚🖐️✋🖖👌🤌🤏🤞🤟</template>
<template #Resource>📱📲📶📳📴☎📞📟📠🤳</template>
</lay-cascader>
</template>
<script setup>
import { ref } from "vue";
const value2=ref(null)
const displayValue=ref(null)
const onChange=(evt)=>{
displayValue.value=evt.display
}
const options2 = [
{
value: "Guide",
label: "指南",
slot:"Guide",
children: [
{
value: "shejiyuanze",
label: "设计原则",
children: [
{
value: "yizhi",
label: "一致",
},
{
value: "fankui",
label: "反馈",
},
{
value: "xiaolv",
label: "效率",
},
{
value: "kekong",
label: "可控",
},
],
},
{
value: "daohang",
label: "导航",
children: [
{
value: "cexiangdaohang",
label: "侧向导航",
},
{
value: "dingbudaohang",
label: "顶部导航",
},
],
},
],
},
{
value: "Components",
label: "组件",
slot:"Components",
children: [
{
value: "basic",
label: "Basic",
children: [
{
value: "layout",
label: "Layout 布局",
},
{
value: "color",
label: "Color 色彩",
},
{
value: "typography",
label: "Typography 字体",
},
{
value: "icon",
label: "Icon 图标",
},
{
value: "button",
label: "Button 按钮",
},
],
},
{
value: "form",
label: "Form",
children: [
{
value: "radio",
label: "Radio 单选框",
},
{
value: "checkbox",
label: "Checkbox 多选框",
},
{
value: "input",
label: "Input 输入框",
},
{
value: "input-number",
label: "InputNumber 计数器",
},
{
value: "select",
label: "Select 选择器",
},
{
value: "cascader",
label: "Cascader 级联选择器",
},
{
value: "switch",
label: "Switch 开关",
},
{
value: "slider",
label: "Slider 滑块",
},
{
value: "time-picker",
label: "TimePicker 时间选择器",
},
{
value: "date-picker",
label: "DatePicker 日期选择器",
},
{
value: "datetime-picker",
label: "DateTimePicker 日期时间选择器",
},
{
value: "upload",
label: "Upload 上传",
},
{
value: "rate",
label: "Rate 评分",
},
{
value: "form",
label: "Form 表单",
},
],
},
{
value: "data",
label: "Data",
children: [
{
value: "table",
label: "Table 表格",
},
{
value: "tag",
label: "Tag 标签",
},
{
value: "progress",
label: "Progress 进度条",
},
{
value: "tree",
label: "Tree 树形控件",
},
{
value: "pagination",
label: "Pagination 分页",
},
{
value: "badge",
label: "Badge 标记",
},
],
},
{
value: "notice",
label: "Notice",
children: [
{
value: "alert",
label: "Alert 警告",
},
{
value: "loading",
label: "Loading 加载",
},
{
value: "message",
label: "Message 消息提示",
},
{
value: "message-box",
label: "MessageBox 弹框",
},
{
value: "notification",
label: "Notification 通知",
},
],
},
{
value: "navigation",
label: "Navigation",
children: [
{
value: "menu",
label: "NavMenu 导航菜单",
},
{
value: "tabs",
label: "Tabs 标签页",
},
{
value: "breadcrumb",
label: "Breadcrumb 面包屑",
},
{
value: "dropdown",
label: "Dropdown 下拉菜单",
},
{
value: "steps",
label: "Steps 步骤条",
},
],
},
{
value: "others",
label: "Others",
children: [
{
value: "dialog",
label: "Dialog 对话框",
},
{
value: "tooltip",
label: "Tooltip 文字提示",
},
{
value: "popover",
label: "Popover 弹出框",
},
{
value: "card",
label: "Card 卡片",
},
{
value: "carousel",
label: "Carousel 走马灯",
},
{
value: "collapse",
label: "Collapse 折叠面板",
},
],
},
],
},
{
value: "Resource",
label: "资源",
slot:"Resource",
children: [
{
value: "axure",
label: "Axure Components",
},
{
value: "sketch",
label: "Sketch Templates",
},
{
value: "jiaohu",
label: "组件交互文档",
},
],
},
];
</script>
:::
::: title Cascader 自定义字段名
:::
::: demo 也许你当前数据键名并不是`label`、`value`、`children`,这时只需要使用replaceFields属性来自定义key
<template>
<lay-cascader :options="options3" :replaceFields="replaceFields" placeholder="自义定key" :disabled="true"></lay-cascader>
</template>
<script setup>
import { ref } from "vue";
const replaceFields={
label:'name',
value:'id',
children:'group'
}
const options3=[
{
name:"张三",
id:1,
group:[
{
name:"张三-1",
id:2
},
{
name:"张三-2",
id:3
},
{
name:"张三-3",
id:4
}
]
},
{
name:"李四",
id:5,
group:[
{
name:"李四-1",
id:6
},
{
name:"李四-2",
id:7
},
{
name:"李四-3",
id:8
}
]
}
]
</script>
:::
::: title Cascader 属性
:::
::: table
| 属性 | 描述 | 类型 | 可选值 | 默认值 |
| ----------------------- | -------------------- |-------------------- |-------------------- |-------------------- |
| placeholder | 提示信息 | | | |
| v-model / modelValue | 值 | | | |
| decollator | 分割符号,默认为 / | | | |
| options | 选项参数 格式请见上面的demo | | | |
| onlyLastLevel | 回显display仅显示最后一级默认为 `false` | | | |
| replaceFields | 字段名称配置, 配置项为 `label`,`value`,`children` | | | |
| allow-clear | 默认slot提供清空功能与lay-input保持一致| | | |
| size | 尺寸参数与lay-input保持一致| | | |
| contentStyle | 内容自定义样式 | `StyleValue` | -- | -- |
| contentClass | 内容自定义Class | `string` `Array<string \| object>` `object` | -- | -- |
| disabled | 禁用 | `boolean` |-- |-- |
:::
::: title Cascader 事件
:::
::: table
| 方法名 | 描述 |用法|
| ---- | ------------ |--------|
| change | 选中后数据改变的回调 |onChange( evt ){ <br> &nbsp; &nbsp;/* evt.display<br> &nbsp; &nbsp;/* evt.value,<br> &nbsp; &nbsp;/* evt.label<br> &nbsp; &nbsp;/* evt.currentClick<br>}|
:::
::: contributor cascader
:::
::: previousNext cascader
:::

View File

@@ -0,0 +1,20 @@
The MIT License (MIT)
Copyright 2021 就眠儀式
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.