❗ 重命名 docs 为 example
This commit is contained in:
3
example/src/App.vue
Normal file
3
example/src/App.vue
Normal file
@@ -0,0 +1,3 @@
|
||||
<template>
|
||||
<router-view />
|
||||
</template>
|
||||
213
example/src/assets/css/code.css
Normal file
213
example/src/assets/css/code.css
Normal file
@@ -0,0 +1,213 @@
|
||||
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 whitesmoke;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
3
example/src/assets/css/index.css
Normal file
3
example/src/assets/css/index.css
Normal file
@@ -0,0 +1,3 @@
|
||||
@import './code.css';
|
||||
@import './markdown.css';
|
||||
@import './vars.css';
|
||||
262
example/src/assets/css/markdown.css
Normal file
262
example/src/assets/css/markdown.css
Normal file
@@ -0,0 +1,262 @@
|
||||
html {
|
||||
line-height: 1.4;
|
||||
font-size: 16px;
|
||||
-webkit-text-size-adjust: 100%;
|
||||
}
|
||||
|
||||
h4 {
|
||||
font-weight: 600 !important;
|
||||
font-size: 16.8px !important;
|
||||
margin-top: 20px !important;
|
||||
margin-bottom: 20px !important;
|
||||
}
|
||||
|
||||
h5 {
|
||||
font-weight: 700;
|
||||
font-size: 16.8px;
|
||||
}
|
||||
|
||||
h1,
|
||||
h3,
|
||||
h4,
|
||||
h5,
|
||||
h6 {
|
||||
margin: 0;
|
||||
color: #333;
|
||||
line-height: 1.25;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
area,
|
||||
button,
|
||||
[role='button'],
|
||||
input,
|
||||
label,
|
||||
select,
|
||||
summary,
|
||||
textarea {
|
||||
touch-action: manipulation;
|
||||
}
|
||||
|
||||
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%;
|
||||
}
|
||||
|
||||
form {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.source .layui-avatar {
|
||||
margin-right: 30px;
|
||||
}
|
||||
|
||||
.site-doc-icon {
|
||||
margin-bottom: 50px;
|
||||
font-size: 0;
|
||||
}
|
||||
|
||||
.site-doc-icon li .doc-icon-name,
|
||||
.site-doc-icon li .doc-icon-code {
|
||||
color: #c2c2c2;
|
||||
}
|
||||
|
||||
.site-doc-icon li .doc-icon-fontclass {
|
||||
height: 40px;
|
||||
line-height: 20px;
|
||||
padding: 0 5px;
|
||||
font-size: 13px;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.site-doc-icon li {
|
||||
display: inline-block;
|
||||
vertical-align: middle;
|
||||
width: 16.5%;
|
||||
height: 105px;
|
||||
line-height: 25px;
|
||||
padding: 20px 0;
|
||||
margin-right: -1px;
|
||||
margin-bottom: -1px;
|
||||
border: 1px solid #e2e2e2;
|
||||
font-size: 14px;
|
||||
text-align: center;
|
||||
color: #666;
|
||||
transition: all 0.3s;
|
||||
-webkit-transition: all 0.3s;
|
||||
}
|
||||
|
||||
.site-doc-icon li .layui-icon {
|
||||
display: inline-block;
|
||||
font-size: 32px;
|
||||
}
|
||||
|
||||
.site-doc-color {
|
||||
margin: 15px 0;
|
||||
}
|
||||
|
||||
.site-doc-necolor li div {
|
||||
border-radius: 0;
|
||||
color: #000 !important;
|
||||
}
|
||||
|
||||
.site-doc-color li div {
|
||||
padding: 20px 10px;
|
||||
color: #fff;
|
||||
text-align: center;
|
||||
line-height: 1.6;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.site-doc-bgcolor li {
|
||||
padding: 20px 10px;
|
||||
}
|
||||
|
||||
.site-idea {
|
||||
margin: 30px 0;
|
||||
margin-top: 16px;
|
||||
font-weight: 300;
|
||||
}
|
||||
|
||||
.site-idea li {
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.site-idea li div {
|
||||
padding: 25px;
|
||||
line-height: 24px;
|
||||
border: 1px solid #d2d2d2;
|
||||
border-radius: 6px;
|
||||
}
|
||||
|
||||
.site-idea .layui-field-title {
|
||||
border-color: #d2d2d2;
|
||||
}
|
||||
|
||||
.site-idea .layui-field-title legend {
|
||||
margin: 0 20px 20px 0;
|
||||
padding: 0 20px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.anim .site-doc-icon {
|
||||
margin-bottom: 50px;
|
||||
font-size: 0;
|
||||
}
|
||||
|
||||
.anim .site-doc-icon li {
|
||||
width: 50%;
|
||||
}
|
||||
|
||||
.anim .site-doc-anim li {
|
||||
height: auto;
|
||||
}
|
||||
|
||||
.anim .site-doc-icon li {
|
||||
display: inline-block;
|
||||
vertical-align: middle;
|
||||
width: 16.5%;
|
||||
height: 105px;
|
||||
line-height: 25px;
|
||||
padding: 20px 0;
|
||||
margin-right: -1px;
|
||||
margin-bottom: -1px;
|
||||
border: 1px solid #e2e2e2;
|
||||
font-size: 14px;
|
||||
text-align: center;
|
||||
color: #666;
|
||||
transition: all 0.3s;
|
||||
-webkit-transition: all 0.3s;
|
||||
}
|
||||
|
||||
.anim .site-doc-icon li .layui-anim {
|
||||
width: 125px;
|
||||
height: 125px;
|
||||
line-height: 125px;
|
||||
margin: 0 auto 10px;
|
||||
text-align: center;
|
||||
background-color: #009688;
|
||||
cursor: pointer;
|
||||
color: #fff;
|
||||
border-radius: 50%;
|
||||
}
|
||||
|
||||
.anim .site-doc-icon li .code {
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.markdown-body th {
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
body::-webkit-scrollbar {
|
||||
width: 0px;
|
||||
height: 0px;
|
||||
}
|
||||
|
||||
.layui-menu-item-group {
|
||||
padding-left: 10px !important;
|
||||
padding-right: 10px !important;
|
||||
}
|
||||
|
||||
.layui-menu .layui-menu-item-group > .layui-menu-body-title {
|
||||
padding-left: 15px;
|
||||
}
|
||||
12
example/src/assets/css/vars.css
Normal file
12
example/src/assets/css/vars.css
Normal file
@@ -0,0 +1,12 @@
|
||||
:root {
|
||||
--c-white: #ffffff;
|
||||
--c-black: #000000;
|
||||
}
|
||||
|
||||
:root {
|
||||
--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;
|
||||
}
|
||||
BIN
example/src/assets/logo.png
Normal file
BIN
example/src/assets/logo.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 2.6 KiB |
152
example/src/components/LayCode.vue
Normal file
152
example/src/components/LayCode.vue
Normal file
@@ -0,0 +1,152 @@
|
||||
<template>
|
||||
<div class="lay-code">
|
||||
<div id="source" 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">
|
||||
<i class="layui-icon layui-icon-file" @click="copy" />
|
||||
<i class="layui-icon layui-icon-fonts-code" @click="toggle" />
|
||||
</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 toggle = function () {
|
||||
show.value = !show.value
|
||||
}
|
||||
|
||||
const copy = function () {
|
||||
var Url2 = document.getElementById('source') as any
|
||||
Url2.select()
|
||||
document.execCommand('Copy')
|
||||
}
|
||||
|
||||
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 whitesmoke;
|
||||
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: 2px;
|
||||
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;
|
||||
margin-top: 10px;
|
||||
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;
|
||||
padding-left: 10px;
|
||||
padding-right: 10px;
|
||||
}
|
||||
</style>
|
||||
124
example/src/components/LaySearch.vue
Normal file
124
example/src/components/LaySearch.vue
Normal file
@@ -0,0 +1,124 @@
|
||||
<template>
|
||||
<select :name="name" lay-verify="required" />
|
||||
<div
|
||||
class="layui-unselect layui-form-select layui-search"
|
||||
:class="[openState ? 'layui-form-selected' : '']"
|
||||
@click="open"
|
||||
style="margin-left: 20px"
|
||||
>
|
||||
<div class="layui-select-title">
|
||||
<input
|
||||
type="text"
|
||||
placeholder="Search"
|
||||
class="layui-input layui-unselect"
|
||||
:value="name"
|
||||
style="
|
||||
background: rgba(255, 255, 255, 0.05);
|
||||
border: none;
|
||||
color: rgba(255, 255, 255, 0.7);
|
||||
width: 196px;
|
||||
height: 34px;
|
||||
"
|
||||
@input="change"
|
||||
/><i class="layui-edge"></i>
|
||||
</div>
|
||||
<dl class="layui-anim layui-anim-upbit" style="">
|
||||
<dd v-if="menus.length <= 0" class="layui-select-tips">无内容</dd>
|
||||
<dd
|
||||
v-for="data in menus"
|
||||
v-else
|
||||
:key="data"
|
||||
:value="name"
|
||||
class="layui-select-tips"
|
||||
@click="jump(data)"
|
||||
>
|
||||
{{ data.title }}
|
||||
</dd>
|
||||
</dl>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup name="LaySelect" lang="ts">
|
||||
import { defineProps, ref } from 'vue'
|
||||
import { useRouter, useRoute } from 'vue-router'
|
||||
import { Recordable } from '/@src/module/type'
|
||||
|
||||
const props = defineProps<{
|
||||
datas?: Recordable[]
|
||||
}>()
|
||||
|
||||
const route = useRoute()
|
||||
const router = useRouter()
|
||||
|
||||
const openState = ref(false)
|
||||
const menus = ref(props.datas)
|
||||
const name = ref('')
|
||||
|
||||
const open = function () {
|
||||
openState.value = !openState.value
|
||||
}
|
||||
|
||||
const jump = function (data: any) {
|
||||
name.value = data.title
|
||||
router.push(data.path)
|
||||
}
|
||||
|
||||
const change = function (e: any) {
|
||||
name.value = e.target.value
|
||||
if (e.target.value === '') {
|
||||
menus.value = props.datas
|
||||
} else {
|
||||
menus.value = searchList(e.target.value, props.datas)
|
||||
}
|
||||
}
|
||||
|
||||
const searchList = function (str: string, container: any) {
|
||||
var newList = []
|
||||
var startChar = str.charAt(0)
|
||||
var strLen = str.length
|
||||
for (var i = 0; i < container.length; i++) {
|
||||
var obj = container[i]
|
||||
var isMatch = false
|
||||
for (var p in obj) {
|
||||
if (typeof obj[p] == 'function') {
|
||||
obj[p]()
|
||||
} else {
|
||||
var curItem = ''
|
||||
if (obj[p] != null) {
|
||||
curItem = obj[p]
|
||||
}
|
||||
for (var j = 0; j < curItem.length; j++) {
|
||||
if (curItem.charAt(j) == startChar) {
|
||||
if (curItem.substring(j).substring(0, strLen) == str) {
|
||||
isMatch = true
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (isMatch) {
|
||||
newList.push(obj)
|
||||
}
|
||||
}
|
||||
return newList
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
.layui-search .layui-anim::-webkit-scrollbar {
|
||||
width: 6px;
|
||||
height: 6px;
|
||||
}
|
||||
.layui-search .layui-anim::-webkit-scrollbar-corner {
|
||||
background: #f6f6f6;
|
||||
}
|
||||
.layui-search .layui-anim::-webkit-scrollbar-thumb {
|
||||
background: #e6e6e6;
|
||||
border-radius: 2px;
|
||||
}
|
||||
.layui-search .layui-anim::-webkit-scrollbar-track {
|
||||
background: white;
|
||||
border-radius: 2px;
|
||||
}
|
||||
</style>
|
||||
41
example/src/components/LayTableBox.vue
Normal file
41
example/src/components/LayTableBox.vue
Normal file
@@ -0,0 +1,41 @@
|
||||
<template>
|
||||
<div class="lay-table-box">
|
||||
<slot></slot>
|
||||
</div>
|
||||
</template>
|
||||
<style>
|
||||
.lay-table-box table {
|
||||
width: 100%; /*表格宽度*/
|
||||
border-collapse: collapse; /*使用单一线条的边框*/
|
||||
empty-cells: show; /*单元格无内容依旧绘制边框*/
|
||||
border-right: 1px solid whitesmoke;
|
||||
border-left: 1px solid whitesmoke;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.lay-table-box table tbody {
|
||||
border-bottom: 1px solid whitesmoke;
|
||||
}
|
||||
|
||||
.lay-table-box table th,
|
||||
.lay-table-box table td {
|
||||
font-size: 14px;
|
||||
width: 160px;
|
||||
max-width: 160px;
|
||||
height: 50px; /*统一每一行的默认高度*/
|
||||
border-top: 1px solid whitesmoke; /*内部边框样式*/
|
||||
padding: 0 10px; /*内边距*/
|
||||
padding-left: 28px;
|
||||
}
|
||||
|
||||
.lay-table-box table th {
|
||||
color: #666;
|
||||
font-weight: 500;
|
||||
white-space: nowrap; /*表头内容强制在一行显示*/
|
||||
background-color: #fafafa;
|
||||
}
|
||||
|
||||
.lay-table-box table td {
|
||||
white-space: nowrap;
|
||||
}
|
||||
</style>
|
||||
7
example/src/entry-client.ts
Normal file
7
example/src/entry-client.ts
Normal file
@@ -0,0 +1,7 @@
|
||||
import { createApp } from './main'
|
||||
|
||||
const { app, router } = createApp()
|
||||
|
||||
router.isReady().then(() => {
|
||||
app.mount('#app')
|
||||
})
|
||||
52
example/src/entry-server.ts
Normal file
52
example/src/entry-server.ts
Normal file
@@ -0,0 +1,52 @@
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-nocheck
|
||||
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 {
|
||||
return ''
|
||||
}
|
||||
}
|
||||
393
example/src/layouts/Layout.vue
Normal file
393
example/src/layouts/Layout.vue
Normal file
@@ -0,0 +1,393 @@
|
||||
<template>
|
||||
<lay-layout class="layui-layout-document">
|
||||
<lay-header
|
||||
><lay-logo style="box-shadow: 0 0px 2px 0 rgba(0, 0, 0, 0.15)">
|
||||
<img src="../assets/logo.png" />
|
||||
</lay-logo>
|
||||
<ul
|
||||
class="layui-nav layui-layout-left"
|
||||
style="margin-top: 0px; margin-bottom: 0px"
|
||||
>
|
||||
<li class="layui-nav-item">
|
||||
<router-link to="/zh-CN/index"> 首页 </router-link>
|
||||
</li>
|
||||
<li class="layui-nav-item">
|
||||
<router-link to="/zh-CN/guide"> 指南 </router-link>
|
||||
</li>
|
||||
<li class="layui-nav-item">
|
||||
<router-link to="/zh-CN/components"> 组件 </router-link>
|
||||
</li>
|
||||
<li class="layui-nav-item">
|
||||
<router-link to="/zh-CN/hooks"> hooks </router-link>
|
||||
</li>
|
||||
<li class="layui-nav-item">
|
||||
<lay-form>
|
||||
<lay-search :datas="menus" />
|
||||
</lay-form>
|
||||
</li>
|
||||
</ul>
|
||||
<ul
|
||||
class="layui-nav layui-layout-right"
|
||||
style="margin-top: 0px; margin-bottom: 0px"
|
||||
>
|
||||
<li class="layui-nav-item">
|
||||
<a href="https://gitee.com/layui-vue">
|
||||
<lay-icon type="layui-icon-fonts-code" size="14px"></lay-icon>
|
||||
</a>
|
||||
</li>
|
||||
<li class="layui-nav-item">
|
||||
<a
|
||||
href="https://gitee.com/layui-vue/layui-vue/issues?assignee_id=&author_id=&branch=&collaborator_ids=&issue_search=&label_ids=&label_text=&milestone_id=&priority=&private_issue=&program_id=&project_id=Jmysy%2Flayui-vue&project_type=&scope=&sort=&state=all&target_project="
|
||||
>
|
||||
<lay-icon type="layui-icon-chat" size="14px"></lay-icon>
|
||||
</a>
|
||||
</li>
|
||||
<li class="layui-nav-item">
|
||||
<a href="javascript:void(0)"> 0.2.4 </a>
|
||||
</li>
|
||||
</ul>
|
||||
</lay-header>
|
||||
<router-view></router-view>
|
||||
</lay-layout>
|
||||
</template>
|
||||
<script>
|
||||
import { ref, watch } from 'vue'
|
||||
import { useRouter, useRoute } from 'vue-router'
|
||||
export default {
|
||||
setup() {
|
||||
const route = useRoute()
|
||||
const router = useRouter()
|
||||
const currentPath = ref('/zh-CN/guide')
|
||||
|
||||
watch(
|
||||
() => route.path,
|
||||
(val) => {
|
||||
currentPath.value = val
|
||||
},
|
||||
{
|
||||
immediate: true,
|
||||
deep: true,
|
||||
}
|
||||
)
|
||||
|
||||
const menus = [
|
||||
{
|
||||
id: 1,
|
||||
title: '介绍',
|
||||
subTitle: 'introduce',
|
||||
path: '/zh-CN/guide/introduce',
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
title: '安装',
|
||||
subTitle: 'get started',
|
||||
path: '/zh-CN/guide/getStarted',
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
title: '更新',
|
||||
subTitle: 'change log',
|
||||
path: '/zh-CN/guide/changelog',
|
||||
},
|
||||
{
|
||||
id: 4,
|
||||
title: '布局',
|
||||
subTitle: 'layout',
|
||||
path: '/zh-CN/components/layout',
|
||||
},
|
||||
{
|
||||
id: 5,
|
||||
title: '容器',
|
||||
subTitle: 'container',
|
||||
path: '/zh-CN/components/container',
|
||||
},
|
||||
{
|
||||
id: 6,
|
||||
title: '按钮',
|
||||
subTitle: 'button',
|
||||
path: '/zh-CN/components/button',
|
||||
},
|
||||
{
|
||||
id: 7,
|
||||
title: '图标',
|
||||
subTitle: 'iconfont',
|
||||
path: '/zh-CN/components/icon',
|
||||
},
|
||||
{
|
||||
id: 8,
|
||||
title: '面板',
|
||||
subTitle: 'panel',
|
||||
path: '/zh-CN/components/panel',
|
||||
},
|
||||
{
|
||||
id: 9,
|
||||
title: '卡片',
|
||||
subTitle: 'card',
|
||||
path: '/zh-CN/components/card',
|
||||
},
|
||||
{
|
||||
id: 10,
|
||||
title: '动画',
|
||||
subTitle: 'animation',
|
||||
path: '/zh-CN/components/animation',
|
||||
},
|
||||
{
|
||||
id: 11,
|
||||
title: '栅格',
|
||||
subTitle: 'grid',
|
||||
path: '/zh-CN/components/grid',
|
||||
},
|
||||
{
|
||||
id: 12,
|
||||
title: '表单',
|
||||
subTitle: 'form',
|
||||
path: '/zh-CN/components/form',
|
||||
},
|
||||
{
|
||||
id: 13,
|
||||
title: '徽章',
|
||||
subTitle: 'badge',
|
||||
path: '/zh-CN/components/badge',
|
||||
},
|
||||
{
|
||||
id: 14,
|
||||
title: '区块',
|
||||
subTitle: 'block',
|
||||
path: '/zh-CN/components/block',
|
||||
},
|
||||
{
|
||||
id: 15,
|
||||
title: '分割',
|
||||
subTitle: 'line',
|
||||
path: '/zh-CN/components/line',
|
||||
},
|
||||
{
|
||||
id: 16,
|
||||
title: '菜单',
|
||||
subTitle: 'nav',
|
||||
path: '/zh-CN/components/menu',
|
||||
},
|
||||
{
|
||||
id: 17,
|
||||
title: '面包屑',
|
||||
subTitle: 'breadcrumb',
|
||||
path: '/zh-CN/components/breadcrumb',
|
||||
},
|
||||
{
|
||||
id: 18,
|
||||
title: '进度',
|
||||
subTitle: 'progress',
|
||||
path: '/zh-CN/components/progress',
|
||||
},
|
||||
{
|
||||
id: 19,
|
||||
title: '时间线',
|
||||
subTitle: 'timeline',
|
||||
path: '/zh-CN/components/timeline',
|
||||
},
|
||||
{
|
||||
id: 20,
|
||||
title: '颜色',
|
||||
subTitle: 'color',
|
||||
path: '/zh-CN/components/color',
|
||||
},
|
||||
{
|
||||
id: 21,
|
||||
title: '手风琴',
|
||||
subTitle: 'collapse',
|
||||
path: '/zh-CN/components/collapse',
|
||||
},
|
||||
{
|
||||
id: 22,
|
||||
title: '表格',
|
||||
subTitle: 'table',
|
||||
path: '/zh-CN/components/table',
|
||||
},
|
||||
{
|
||||
id: 23,
|
||||
title: '头像',
|
||||
subTitle: 'avatar',
|
||||
path: '/zh-CN/components/avatar',
|
||||
},
|
||||
{
|
||||
id: 24,
|
||||
title: '字段',
|
||||
subTitle: 'field',
|
||||
path: '/zh-CN/components/field',
|
||||
},
|
||||
{
|
||||
id: 25,
|
||||
title: '空',
|
||||
subTitle: 'empty',
|
||||
path: '/zh-CN/components/empty',
|
||||
},
|
||||
{
|
||||
id: 26,
|
||||
title: '评分',
|
||||
subTitle: 'rate',
|
||||
path: '/zh-CN/components/rate',
|
||||
},
|
||||
{
|
||||
id: 27,
|
||||
title: '下拉菜单',
|
||||
subTitle: 'dropdown',
|
||||
path: '/zh-CN/components/dropdown',
|
||||
},
|
||||
{
|
||||
id: 28,
|
||||
title: '选项卡',
|
||||
subTitle: 'tab',
|
||||
path: '/zh-CN/components/tab',
|
||||
},
|
||||
{
|
||||
id: 29,
|
||||
title: '图标选择',
|
||||
subTitle: 'iconPicker',
|
||||
path: '/zh-CN/components/iconPicker',
|
||||
},
|
||||
{
|
||||
id: 29,
|
||||
title: '分页',
|
||||
subTitle: 'page',
|
||||
path: '/zh-CN/components/page',
|
||||
},
|
||||
{
|
||||
id: 30,
|
||||
title: '树形组件',
|
||||
subTitle: 'tree',
|
||||
path: '/zh-CN/components/tree',
|
||||
},
|
||||
{
|
||||
id: 31,
|
||||
title: '穿梭框',
|
||||
subTitle: 'transfer',
|
||||
path: '/zh-CN/components/transfer',
|
||||
},
|
||||
{
|
||||
id: 32,
|
||||
title: '复选框',
|
||||
subTitle: 'checkbox',
|
||||
path: '/zh-CN/components/checkbox',
|
||||
},
|
||||
{
|
||||
id: 33,
|
||||
title: '单选框',
|
||||
subTitle: 'radio',
|
||||
path: '/zh-CN/components/radio',
|
||||
},
|
||||
{
|
||||
id: 34,
|
||||
title: '输入框',
|
||||
subTitle: 'input',
|
||||
path: '/zh-CN/components/input',
|
||||
},
|
||||
{
|
||||
id: 35,
|
||||
title: '文本域',
|
||||
subTitle: 'textarea',
|
||||
path: '/zh-CN/components/textarea',
|
||||
},
|
||||
{
|
||||
id: 36,
|
||||
title: '开关',
|
||||
subTitle: 'switch',
|
||||
path: '/zh-CN/components/switch',
|
||||
},
|
||||
{
|
||||
id: 37,
|
||||
title: '滑块',
|
||||
subTitle: 'slider',
|
||||
path: '/zh-CN/components/slider',
|
||||
},
|
||||
{
|
||||
id: 38,
|
||||
title: '轮播',
|
||||
subTitle: 'carousel',
|
||||
path: '/zh-CN/components/carousel',
|
||||
},
|
||||
{
|
||||
id: 39,
|
||||
title: '下拉选择',
|
||||
subTitle: 'select',
|
||||
path: '/zh-CN/components/select',
|
||||
},
|
||||
{
|
||||
id: 40,
|
||||
title: '颜色选择器',
|
||||
subTitle: 'colorPicker',
|
||||
path: '/zh-CN/components/colorPicker',
|
||||
},
|
||||
]
|
||||
|
||||
const handleClick = function (menu) {
|
||||
router.push(menu.path)
|
||||
}
|
||||
|
||||
return {
|
||||
menus,
|
||||
currentPath,
|
||||
handleClick,
|
||||
}
|
||||
},
|
||||
}
|
||||
</script>
|
||||
<style>
|
||||
.layui-layout-document > .layui-header {
|
||||
z-index: 9999;
|
||||
width: 100%;
|
||||
position: fixed;
|
||||
background: #393d49;
|
||||
border-bottom: 1px solid #404553;
|
||||
}
|
||||
|
||||
.layui-layout-document > .layui-layout > .layui-side {
|
||||
overflow-x: hidden;
|
||||
position: fixed;
|
||||
margin-top: 60px;
|
||||
height: calc(100% - 60px);
|
||||
box-shadow: 2px 0 8px 0 rgb(29 35 41 / 5%);
|
||||
}
|
||||
|
||||
.layui-layout-document > .layui-layout > .layui-body {
|
||||
margin-top: 60px;
|
||||
left: 200px;
|
||||
position: absolute;
|
||||
width: calc(100% - 200px);
|
||||
height: calc(100% - 60px);
|
||||
}
|
||||
|
||||
.layui-logo img {
|
||||
height: 31px;
|
||||
width: 82px;
|
||||
left: 15px;
|
||||
top: 16px;
|
||||
}
|
||||
.layui-header > .layui-nav {
|
||||
background-color: transparent;
|
||||
}
|
||||
.layui-menu-docs {
|
||||
padding-top: 10px;
|
||||
}
|
||||
.layui-menu-docs .layui-menu-body-title .layui-font-gray {
|
||||
padding-left: 10px;
|
||||
}
|
||||
.layui-side hr {
|
||||
margin: 8px;
|
||||
}
|
||||
|
||||
@media screen and (max-width: 768px) {
|
||||
.layui-side {
|
||||
width: 0px !important;
|
||||
}
|
||||
.layui-body {
|
||||
left: 0px !important;
|
||||
width: 100% !important;
|
||||
}
|
||||
.layui-logo {
|
||||
display: none !important;
|
||||
}
|
||||
.layui-layout-left {
|
||||
left: 0px !important;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
29
example/src/main.ts
Normal file
29
example/src/main.ts
Normal file
@@ -0,0 +1,29 @@
|
||||
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 LaySearch from './components/LaySearch.vue'
|
||||
import LayTableBox from './components/LayTableBox.vue'
|
||||
import './assets/css/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)
|
||||
.component('LaySearch', LaySearch)
|
||||
.component('LayTableBox', LayTableBox)
|
||||
.use(Layui)
|
||||
|
||||
return { app, router }
|
||||
}
|
||||
36
example/src/plugin/common-plugins.ts
Normal file
36
example/src/plugin/common-plugins.ts
Normal file
@@ -0,0 +1,36 @@
|
||||
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 createTitle from './create-title'
|
||||
import createBlock from './create-block'
|
||||
import createDescribe from './create-describe'
|
||||
import createTable from './create-table'
|
||||
import preWrapper from './pre-wrapper'
|
||||
|
||||
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(...createTable('table', ''))
|
||||
.use(...createBlock('block', ''))
|
||||
.use(...createTitle('title', ''))
|
||||
.use(...createDescribe('describe', ''))
|
||||
},
|
||||
}),
|
||||
] as any
|
||||
|
||||
export default plugins
|
||||
31
example/src/plugin/create-block.ts
Normal file
31
example/src/plugin/create-block.ts
Normal file
@@ -0,0 +1,31 @@
|
||||
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 `<lay-block style="margin-left:8px;margin-bottom:40px;">${info}`
|
||||
} else {
|
||||
return '</lay-block>\n'
|
||||
}
|
||||
},
|
||||
},
|
||||
]
|
||||
}
|
||||
31
example/src/plugin/create-describe.ts
Normal file
31
example/src/plugin/create-describe.ts
Normal file
@@ -0,0 +1,31 @@
|
||||
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 `<p style="margin-left: 24px;margin-bottom:20px;">${info}`
|
||||
} else {
|
||||
return '</p>\n'
|
||||
}
|
||||
},
|
||||
},
|
||||
]
|
||||
}
|
||||
31
example/src/plugin/create-table.ts
Normal file
31
example/src/plugin/create-table.ts
Normal file
@@ -0,0 +1,31 @@
|
||||
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 `<lay-table-box>${info}`
|
||||
} else {
|
||||
return '</lay-table-box>\n'
|
||||
}
|
||||
},
|
||||
},
|
||||
]
|
||||
}
|
||||
33
example/src/plugin/create-title.ts
Normal file
33
example/src/plugin/create-title.ts
Normal 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 `<lay-field title="${
|
||||
info || defaultTitle
|
||||
}" style="margin-top:20px;margin-bottom: 20px;">`
|
||||
} else {
|
||||
return '</lay-field>\n'
|
||||
}
|
||||
},
|
||||
},
|
||||
]
|
||||
}
|
||||
141
example/src/plugin/demo.ts
Normal file
141
example/src/plugin/demo.ts
Normal file
@@ -0,0 +1,141 @@
|
||||
import markdown from 'markdown-it'
|
||||
import highlight from './highlight'
|
||||
import type Token from 'markdown-it/lib/token'
|
||||
|
||||
function assignScript(script: string) {
|
||||
const dependencies = {} as Record<string, string[]>
|
||||
const attrs = {} as Record<string, string>
|
||||
const content = script
|
||||
.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>
|
||||
`
|
||||
},
|
||||
}
|
||||
44
example/src/plugin/highlight.ts
Normal file
44
example/src/plugin/highlight.ts
Normal file
@@ -0,0 +1,44 @@
|
||||
import prism from 'prismjs'
|
||||
import loadLanguages from 'prismjs/components/index'
|
||||
import escapeHtml from 'escape-html'
|
||||
|
||||
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')
|
||||
}
|
||||
11
example/src/plugin/pre-wrapper.ts
Normal file
11
example/src/plugin/pre-wrapper.ts
Normal 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>`
|
||||
}
|
||||
}
|
||||
39
example/src/plugin/snippet.ts
Normal file
39
example/src/plugin/snippet.ts
Normal file
@@ -0,0 +1,39 @@
|
||||
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 (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)
|
||||
}
|
||||
16
example/src/router/index.ts
Normal file
16
example/src/router/index.ts
Normal file
@@ -0,0 +1,16 @@
|
||||
import {
|
||||
createRouter as _createRouter,
|
||||
createWebHashHistory,
|
||||
Router,
|
||||
} from 'vue-router'
|
||||
import zhCN from './zh-CN'
|
||||
|
||||
const routes = [...zhCN]
|
||||
|
||||
export function createRouter(): Router {
|
||||
const baseUrl = import.meta.env.BASE_URL
|
||||
return _createRouter({
|
||||
history: createWebHashHistory(baseUrl),
|
||||
routes: routes,
|
||||
})
|
||||
}
|
||||
296
example/src/router/zh-CN.ts
Normal file
296
example/src/router/zh-CN.ts
Normal file
@@ -0,0 +1,296 @@
|
||||
import BaseLayout from '../layouts/Layout.vue'
|
||||
import Component from '../view/component.vue'
|
||||
import Hooks from '../view/hooks.vue'
|
||||
import Guide from '../view/guide.vue'
|
||||
import Index from '../view/index.vue'
|
||||
|
||||
const zhCN = [
|
||||
{
|
||||
path: '/',
|
||||
redirect: '/zh-CN/index',
|
||||
component: BaseLayout,
|
||||
meta: { title: '首页' },
|
||||
children: [
|
||||
{
|
||||
path: '/zh-CN/index',
|
||||
component: Index,
|
||||
meta: { title: '指南' },
|
||||
},
|
||||
{
|
||||
path: '/zh-CN/guide',
|
||||
redirect: '/zh-CN/guide/introduce',
|
||||
component: Guide,
|
||||
meta: { title: '指南' },
|
||||
children: [
|
||||
{
|
||||
path: '/zh-CN/guide/introduce',
|
||||
component: () => import('../../docs/zh-CN/guide/introduce.md'),
|
||||
meta: { title: '介绍' },
|
||||
},
|
||||
{
|
||||
path: '/zh-CN/guide/getStarted',
|
||||
component: () => import('../../docs/zh-CN/guide/getStarted.md'),
|
||||
meta: { title: '安装' },
|
||||
},
|
||||
{
|
||||
path: '/zh-CN/guide/changelog',
|
||||
component: () => import('../../docs/zh-CN/guide/changelog.md'),
|
||||
meta: { title: '更新' },
|
||||
},
|
||||
{
|
||||
path: '/zh-CN/guide/problem',
|
||||
component: () => import('../../docs/zh-CN/guide/problem.md'),
|
||||
meta: { title: '问题' },
|
||||
},
|
||||
{
|
||||
path: '/zh-CN/guide/contribution',
|
||||
component: () => import('../../docs/zh-CN/guide/contribution.md'),
|
||||
meta: { title: '贡献' },
|
||||
},
|
||||
{
|
||||
path: '/zh-CN/guide/norms',
|
||||
component: () => import('../../docs/zh-CN/guide/norms.md'),
|
||||
meta: { title: '规范' },
|
||||
},
|
||||
{
|
||||
path: '/zh-CN/guide/sandbox',
|
||||
component: () => import('../../docs/zh-CN/guide/sandbox.md'),
|
||||
meta: { title: '沙盒' },
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
path: '/zh-CN/components',
|
||||
redirect: '/zh-CN/components/color',
|
||||
component: Component,
|
||||
meta: { title: '组件' },
|
||||
children: [
|
||||
{
|
||||
path: '/zh-CN/components/layout',
|
||||
component: () => import('../../docs/zh-CN/components/layout.md'),
|
||||
meta: { title: '布局' },
|
||||
},
|
||||
{
|
||||
path: '/zh-CN/components/color',
|
||||
component: () => import('../../docs/zh-CN/components/color.md'),
|
||||
meta: { title: '颜色' },
|
||||
},
|
||||
{
|
||||
path: '/zh-CN/components/container',
|
||||
component: () => import('../../docs/zh-CN/components/container.md'),
|
||||
meta: { title: '容器' },
|
||||
},
|
||||
{
|
||||
path: '/zh-CN/components/breadcrumb',
|
||||
component: () =>
|
||||
import('../../docs/zh-CN/components/breadcrumb.md'),
|
||||
meta: { title: '面包屑' },
|
||||
},
|
||||
{
|
||||
path: '/zh-CN/components/button',
|
||||
component: () => import('../../docs/zh-CN/components/button.md'),
|
||||
meta: { title: '按钮' },
|
||||
},
|
||||
{
|
||||
path: '/zh-CN/components/icon',
|
||||
component: () => import('../../docs/zh-CN/components/icon.md'),
|
||||
meta: { title: '图标' },
|
||||
},
|
||||
{
|
||||
path: '/zh-CN/components/panel',
|
||||
component: () => import('../../docs/zh-CN/components/panel.md'),
|
||||
meta: { title: '面板' },
|
||||
},
|
||||
{
|
||||
path: '/zh-CN/components/animation',
|
||||
component: () => import('../../docs/zh-CN/components/animation.md'),
|
||||
meta: { title: '动画' },
|
||||
},
|
||||
{
|
||||
path: '/zh-CN/components/card',
|
||||
component: () => import('../../docs/zh-CN/components/card.md'),
|
||||
meta: { title: '卡片' },
|
||||
},
|
||||
{
|
||||
path: '/zh-CN/components/grid',
|
||||
component: () => import('../../docs/zh-CN/components/grid.md'),
|
||||
meta: { title: '栅格' },
|
||||
},
|
||||
{
|
||||
path: '/zh-CN/components/form',
|
||||
component: () => import('../../docs/zh-CN/components/form.md'),
|
||||
meta: { title: '表单' },
|
||||
},
|
||||
{
|
||||
path: '/zh-CN/components/badge',
|
||||
component: () => import('../../docs/zh-CN/components/badge.md'),
|
||||
meta: { title: '徽章' },
|
||||
},
|
||||
{
|
||||
path: '/zh-CN/components/block',
|
||||
component: () => import('../../docs/zh-CN/components/block.md'),
|
||||
meta: { title: '辅助' },
|
||||
},
|
||||
{
|
||||
path: '/zh-CN/components/line',
|
||||
component: () => import('../../docs/zh-CN/components/line.md'),
|
||||
meta: { title: '分割' },
|
||||
},
|
||||
{
|
||||
path: '/zh-CN/components/progress',
|
||||
component: () => import('../../docs/zh-CN/components/progress.md'),
|
||||
meta: { title: '进度' },
|
||||
},
|
||||
{
|
||||
path: '/zh-CN/components/menu',
|
||||
component: () => import('../../docs/zh-CN/components/menu.md'),
|
||||
meta: { title: '菜单' },
|
||||
},
|
||||
{
|
||||
path: '/zh-CN/components/timeline',
|
||||
component: () => import('../../docs/zh-CN/components/timeline.md'),
|
||||
meta: { title: '时间线' },
|
||||
},
|
||||
{
|
||||
path: '/zh-CN/components/collapse',
|
||||
component: () => import('../../docs/zh-CN/components/collapse.md'),
|
||||
meta: { title: '手风琴' },
|
||||
},
|
||||
{
|
||||
path: '/zh-CN/components/table',
|
||||
component: () => import('../../docs/zh-CN/components/table.md'),
|
||||
meta: { title: '表格' },
|
||||
},
|
||||
{
|
||||
path: '/zh-CN/components/avatar',
|
||||
component: () => import('../../docs/zh-CN/components/avatar.md'),
|
||||
meta: { title: '头像' },
|
||||
},
|
||||
{
|
||||
path: '/zh-CN/components/field',
|
||||
component: () => import('../../docs/zh-CN/components/field.md'),
|
||||
meta: { title: '字段' },
|
||||
},
|
||||
{
|
||||
path: '/zh-CN/components/empty',
|
||||
component: () => import('../../docs/zh-CN/components/empty.md'),
|
||||
meta: { title: '空' },
|
||||
},
|
||||
{
|
||||
path: '/zh-CN/components/rate',
|
||||
component: () => import('../../docs/zh-CN/components/rate.md'),
|
||||
meta: { title: '评分' },
|
||||
},
|
||||
{
|
||||
path: '/zh-CN/components/dropdown',
|
||||
component: () => import('../../docs/zh-CN/components/dropdown.md'),
|
||||
meta: { title: '下拉' },
|
||||
},
|
||||
{
|
||||
path: '/zh-CN/components/tab',
|
||||
component: () => import('../../docs/zh-CN/components/tab.md'),
|
||||
meta: { title: '选项卡' },
|
||||
},
|
||||
{
|
||||
path: '/zh-CN/components/iconPicker',
|
||||
component: () =>
|
||||
import('../../docs/zh-CN/components/iconPicker.md'),
|
||||
meta: { title: '图标选择' },
|
||||
},
|
||||
{
|
||||
path: '/zh-CN/components/tree',
|
||||
component: () => import('../../docs/zh-CN/components/tree.md'),
|
||||
meta: { title: '树形组件' },
|
||||
},
|
||||
{
|
||||
path: '/zh-CN/components/page',
|
||||
component: () => import('../../docs/zh-CN/components/page.md'),
|
||||
meta: { title: '分页' },
|
||||
},
|
||||
{
|
||||
path: '/zh-CN/components/transfer',
|
||||
component: () => import('../../docs/zh-CN/components/transfer.md'),
|
||||
meta: { title: '穿梭框' },
|
||||
},
|
||||
{
|
||||
path: '/zh-CN/components/checkbox',
|
||||
component: () => import('../../docs/zh-CN/components/checkbox.md'),
|
||||
meta: { title: '复选框' },
|
||||
},
|
||||
{
|
||||
path: '/zh-CN/components/radio',
|
||||
component: () => import('../../docs/zh-CN/components/radio.md'),
|
||||
meta: { title: '单选框' },
|
||||
},
|
||||
{
|
||||
path: '/zh-CN/components/input',
|
||||
component: () => import('../../docs/zh-CN/components/input.md'),
|
||||
meta: { title: '输入框' },
|
||||
},
|
||||
{
|
||||
path: '/zh-CN/components/textarea',
|
||||
component: () => import('../../docs/zh-CN/components/textarea.md'),
|
||||
meta: { title: '文本域' },
|
||||
},
|
||||
{
|
||||
path: '/zh-CN/components/switch',
|
||||
component: () => import('../../docs/zh-CN/components/switch.md'),
|
||||
meta: { title: '开关' },
|
||||
},
|
||||
{
|
||||
path: '/zh-CN/components/slider',
|
||||
component: () => import('../../docs/zh-CN/components/slider.md'),
|
||||
meta: { title: '滑块' },
|
||||
},
|
||||
{
|
||||
path: '/zh-CN/components/carousel',
|
||||
component: () => import('../../docs/zh-CN/components/carousel.md'),
|
||||
meta: { title: '轮播' },
|
||||
},
|
||||
{
|
||||
path: '/zh-CN/components/select',
|
||||
component: () => import('../../docs/zh-CN/components/select.md'),
|
||||
meta: { title: '下拉选择' },
|
||||
},
|
||||
{
|
||||
path: '/zh-CN/components/colorPicker',
|
||||
component: () =>
|
||||
import('../../docs/zh-CN/components/colorPicker.md'),
|
||||
meta: { title: '颜色选择器' },
|
||||
},
|
||||
{
|
||||
path: '/zh-CN/components/layer',
|
||||
component: () => import('../../docs/zh-CN/components/layer.md'),
|
||||
meta: { title: '弹层' },
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
path: '/zh-CN/hooks',
|
||||
redirect: '/zh-CN/hooks/useClickOutside',
|
||||
component: Hooks,
|
||||
meta: { title: 'hooks' },
|
||||
children: [
|
||||
{
|
||||
path: '/zh-CN/hooks/useClickOutside',
|
||||
component: () =>
|
||||
import('../../docs/zh-CN/hooks/useClickOutside.md'),
|
||||
meta: { title: 'useClickOutside' },
|
||||
},
|
||||
{
|
||||
path: '/zh-CN/hooks/useFullScreen',
|
||||
component: () => import('../../docs/zh-CN/hooks/useFullScreen.md'),
|
||||
meta: { title: 'useFullScreen' },
|
||||
},
|
||||
{
|
||||
path: '/zh-CN/hooks/useMove',
|
||||
component: () => import('../../docs/zh-CN/hooks/useMove.md'),
|
||||
meta: { title: 'useMove' },
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
]
|
||||
|
||||
export default zhCN
|
||||
4
example/src/tsconfig.json
Normal file
4
example/src/tsconfig.json
Normal file
@@ -0,0 +1,4 @@
|
||||
{
|
||||
"extends": "../../tsconfig.json",
|
||||
"include": [".", "../../shims-vue.d.ts"]
|
||||
}
|
||||
360
example/src/view/component.vue
Normal file
360
example/src/view/component.vue
Normal file
@@ -0,0 +1,360 @@
|
||||
<template>
|
||||
<lay-layout>
|
||||
<lay-side>
|
||||
<lay-scroll style="overflow-y: scroll">
|
||||
<ul class="layui-menu layui-menu-lg layui-menu-docs">
|
||||
<li
|
||||
v-for="menu in menus"
|
||||
:key="menu"
|
||||
class="layui-menu-item-group"
|
||||
lay-options="{type: 'group', isAllowSpread: true}"
|
||||
>
|
||||
<div class="layui-menu-body-title">{{ menu.title }}</div>
|
||||
<hr />
|
||||
<ul>
|
||||
<li
|
||||
v-for="children in menu.children"
|
||||
:key="children"
|
||||
:class="[
|
||||
currentPath === children.path
|
||||
? 'layui-menu-item-checked2'
|
||||
: '',
|
||||
]"
|
||||
@click="handleClick(children)"
|
||||
>
|
||||
<div class="layui-menu-body-title">
|
||||
<router-link :to="children.path">
|
||||
<span>{{ children.title }}</span>
|
||||
<span class="layui-font-12 layui-font-gray">
|
||||
{{ children.subTitle }}
|
||||
</span>
|
||||
</router-link>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
</lay-scroll>
|
||||
</lay-side>
|
||||
<lay-body>
|
||||
<div style="padding: 20px">
|
||||
<router-view />
|
||||
</div>
|
||||
</lay-body>
|
||||
</lay-layout>
|
||||
</template>
|
||||
<script>
|
||||
import { ref, watch } from 'vue'
|
||||
import { useRouter, useRoute } from 'vue-router'
|
||||
export default {
|
||||
setup() {
|
||||
const route = useRoute()
|
||||
const router = useRouter()
|
||||
const currentPath = ref('/zh-CN/guide')
|
||||
|
||||
watch(
|
||||
() => route.path,
|
||||
(val) => {
|
||||
currentPath.value = val
|
||||
},
|
||||
{
|
||||
immediate: true,
|
||||
deep: true,
|
||||
}
|
||||
)
|
||||
|
||||
const menus = [
|
||||
{
|
||||
id: 1,
|
||||
title: '通用',
|
||||
children: [
|
||||
{
|
||||
id: 20,
|
||||
title: '颜色',
|
||||
subTitle: 'color',
|
||||
path: '/zh-CN/components/color',
|
||||
},
|
||||
{
|
||||
id: 6,
|
||||
title: '按钮',
|
||||
subTitle: 'button',
|
||||
path: '/zh-CN/components/button',
|
||||
},
|
||||
{
|
||||
id: 7,
|
||||
title: '图标',
|
||||
subTitle: 'iconfont',
|
||||
path: '/zh-CN/components/icon',
|
||||
},
|
||||
{
|
||||
id: 10,
|
||||
title: '动画',
|
||||
subTitle: 'animation',
|
||||
path: '/zh-CN/components/animation',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
id: 1,
|
||||
title: '布局',
|
||||
children: [
|
||||
{
|
||||
id: 4,
|
||||
title: '布局',
|
||||
subTitle: 'layout',
|
||||
path: '/zh-CN/components/layout',
|
||||
},
|
||||
{
|
||||
id: 5,
|
||||
title: '容器',
|
||||
subTitle: 'container',
|
||||
path: '/zh-CN/components/container',
|
||||
},
|
||||
{
|
||||
id: 11,
|
||||
title: '栅格',
|
||||
subTitle: 'grid',
|
||||
path: '/zh-CN/components/grid',
|
||||
},
|
||||
{
|
||||
id: 8,
|
||||
title: '面板',
|
||||
subTitle: 'panel',
|
||||
path: '/zh-CN/components/panel',
|
||||
},
|
||||
{
|
||||
id: 9,
|
||||
title: '卡片',
|
||||
subTitle: 'card',
|
||||
path: '/zh-CN/components/card',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
id: 1,
|
||||
title: '导航',
|
||||
children: [
|
||||
{
|
||||
id: 16,
|
||||
title: '菜单',
|
||||
subTitle: 'nav',
|
||||
path: '/zh-CN/components/menu',
|
||||
},
|
||||
{
|
||||
id: 17,
|
||||
title: '面包屑',
|
||||
subTitle: 'breadcrumb',
|
||||
path: '/zh-CN/components/breadcrumb',
|
||||
},
|
||||
{
|
||||
id: 28,
|
||||
title: '选项卡',
|
||||
subTitle: 'tab',
|
||||
path: '/zh-CN/components/tab',
|
||||
},
|
||||
{
|
||||
id: 27,
|
||||
title: '下拉菜单',
|
||||
subTitle: 'dropdown',
|
||||
path: '/zh-CN/components/dropdown',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
id: 1,
|
||||
title: '表单',
|
||||
children: [
|
||||
{
|
||||
id: 36,
|
||||
title: '开关',
|
||||
subTitle: 'switch',
|
||||
path: '/zh-CN/components/switch',
|
||||
},
|
||||
{
|
||||
id: 32,
|
||||
title: '复选框',
|
||||
subTitle: 'checkbox',
|
||||
path: '/zh-CN/components/checkbox',
|
||||
},
|
||||
{
|
||||
id: 33,
|
||||
title: '单选框',
|
||||
subTitle: 'radio',
|
||||
path: '/zh-CN/components/radio',
|
||||
},
|
||||
{
|
||||
id: 34,
|
||||
title: '输入框',
|
||||
subTitle: 'input',
|
||||
path: '/zh-CN/components/input',
|
||||
},
|
||||
{
|
||||
id: 35,
|
||||
title: '文本域',
|
||||
subTitle: 'textarea',
|
||||
path: '/zh-CN/components/textarea',
|
||||
},
|
||||
{
|
||||
id: 39,
|
||||
title: '下拉选择',
|
||||
subTitle: 'select',
|
||||
path: '/zh-CN/components/select',
|
||||
},
|
||||
{
|
||||
id: 40,
|
||||
title: '颜色选择器',
|
||||
subTitle: 'colorPicker',
|
||||
path: '/zh-CN/components/colorPicker',
|
||||
},
|
||||
{
|
||||
id: 29,
|
||||
title: '图标选择器',
|
||||
subTitle: 'iconPicker',
|
||||
path: '/zh-CN/components/iconPicker',
|
||||
},
|
||||
{
|
||||
id: 26,
|
||||
title: '评分',
|
||||
subTitle: 'rate',
|
||||
path: '/zh-CN/components/rate',
|
||||
},
|
||||
{
|
||||
id: 37,
|
||||
title: '滑块',
|
||||
subTitle: 'slider',
|
||||
path: '/zh-CN/components/slider',
|
||||
},
|
||||
{
|
||||
id: 12,
|
||||
title: '表单',
|
||||
subTitle: 'form',
|
||||
path: '/zh-CN/components/form',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
id: 1,
|
||||
title: '展示',
|
||||
children: [
|
||||
{
|
||||
id: 18,
|
||||
title: '进度',
|
||||
subTitle: 'progress',
|
||||
path: '/zh-CN/components/progress',
|
||||
},
|
||||
{
|
||||
id: 19,
|
||||
title: '时间线',
|
||||
subTitle: 'timeline',
|
||||
path: '/zh-CN/components/timeline',
|
||||
},
|
||||
{
|
||||
id: 21,
|
||||
title: '手风琴',
|
||||
subTitle: 'collapse',
|
||||
path: '/zh-CN/components/collapse',
|
||||
},
|
||||
{
|
||||
id: 22,
|
||||
title: '表格',
|
||||
subTitle: 'table',
|
||||
path: '/zh-CN/components/table',
|
||||
},
|
||||
{
|
||||
id: 23,
|
||||
title: '头像',
|
||||
subTitle: 'avatar',
|
||||
path: '/zh-CN/components/avatar',
|
||||
},
|
||||
{
|
||||
id: 25,
|
||||
title: '空',
|
||||
subTitle: 'empty',
|
||||
path: '/zh-CN/components/empty',
|
||||
},
|
||||
{
|
||||
id: 29,
|
||||
title: '分页',
|
||||
subTitle: 'page',
|
||||
path: '/zh-CN/components/page',
|
||||
},
|
||||
{
|
||||
id: 30,
|
||||
title: '树形组件',
|
||||
subTitle: 'tree',
|
||||
path: '/zh-CN/components/tree',
|
||||
},
|
||||
{
|
||||
id: 31,
|
||||
title: '穿梭框',
|
||||
subTitle: 'transfer',
|
||||
path: '/zh-CN/components/transfer',
|
||||
},
|
||||
{
|
||||
id: 38,
|
||||
title: '轮播',
|
||||
subTitle: 'carousel',
|
||||
path: '/zh-CN/components/carousel',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
id: 1,
|
||||
title: '辅助',
|
||||
children: [
|
||||
{
|
||||
id: 13,
|
||||
title: '徽章',
|
||||
subTitle: 'badge',
|
||||
path: '/zh-CN/components/badge',
|
||||
},
|
||||
{
|
||||
id: 14,
|
||||
title: '区块',
|
||||
subTitle: 'block',
|
||||
path: '/zh-CN/components/block',
|
||||
},
|
||||
{
|
||||
id: 15,
|
||||
title: '分割',
|
||||
subTitle: 'line',
|
||||
path: '/zh-CN/components/line',
|
||||
},
|
||||
{
|
||||
id: 24,
|
||||
title: '字段',
|
||||
subTitle: 'field',
|
||||
path: '/zh-CN/components/field',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
id: 1,
|
||||
title: '反馈',
|
||||
children: [
|
||||
{
|
||||
id: 90,
|
||||
title: '弹层',
|
||||
subTitle: 'layer',
|
||||
path: '/zh-CN/components/layer',
|
||||
},
|
||||
],
|
||||
},
|
||||
]
|
||||
|
||||
const selected = ref(1)
|
||||
|
||||
const handleClick = function (menu) {
|
||||
selected.value = menu.id
|
||||
router.push(menu.path)
|
||||
}
|
||||
|
||||
return {
|
||||
menus,
|
||||
selected,
|
||||
currentPath,
|
||||
handleClick,
|
||||
}
|
||||
},
|
||||
}
|
||||
</script>
|
||||
108
example/src/view/guide.vue
Normal file
108
example/src/view/guide.vue
Normal file
@@ -0,0 +1,108 @@
|
||||
<template>
|
||||
<lay-layout>
|
||||
<lay-side>
|
||||
<lay-scroll style="overflow-y: scroll">
|
||||
<ul class="layui-menu layui-menu-lg layui-menu-docs">
|
||||
<li
|
||||
v-for="menu in menus"
|
||||
:key="menu"
|
||||
class="layui-menu-item-group"
|
||||
lay-options="{type: 'group', isAllowSpread: true}"
|
||||
>
|
||||
<div class="layui-menu-body-title">{{ menu.title }}</div>
|
||||
<hr />
|
||||
<ul>
|
||||
<li
|
||||
v-for="children in menu.children"
|
||||
:key="children"
|
||||
:class="[
|
||||
currentPath === children.path
|
||||
? 'layui-menu-item-checked2'
|
||||
: '',
|
||||
]"
|
||||
@click="handleClick(children)"
|
||||
>
|
||||
<div class="layui-menu-body-title">
|
||||
<router-link :to="children.path">
|
||||
<span>{{ children.title }}</span>
|
||||
<span class="layui-font-12 layui-font-gray">
|
||||
{{ children.subTitle }}
|
||||
</span>
|
||||
</router-link>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
</lay-scroll>
|
||||
</lay-side>
|
||||
<lay-body>
|
||||
<div style="padding: 20px">
|
||||
<router-view />
|
||||
</div>
|
||||
</lay-body>
|
||||
</lay-layout>
|
||||
</template>
|
||||
<script>
|
||||
import { ref, watch } from 'vue'
|
||||
import { useRouter, useRoute } from 'vue-router'
|
||||
export default {
|
||||
setup() {
|
||||
const route = useRoute()
|
||||
const router = useRouter()
|
||||
const currentPath = ref('/zh-CN/guide')
|
||||
|
||||
watch(
|
||||
() => route.path,
|
||||
(val) => {
|
||||
currentPath.value = val
|
||||
},
|
||||
{
|
||||
immediate: true,
|
||||
deep: true,
|
||||
}
|
||||
)
|
||||
|
||||
const menus = [
|
||||
{
|
||||
id: 1,
|
||||
title: '基础',
|
||||
children: [
|
||||
{
|
||||
id: 1,
|
||||
title: '介绍',
|
||||
subTitle: 'introduce',
|
||||
path: '/zh-CN/guide/introduce',
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
title: '安装',
|
||||
subTitle: 'get started',
|
||||
path: '/zh-CN/guide/getStarted',
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
title: '更新',
|
||||
subTitle: 'change log',
|
||||
path: '/zh-CN/guide/changelog',
|
||||
}
|
||||
],
|
||||
},
|
||||
]
|
||||
|
||||
const selected = ref(1)
|
||||
|
||||
const handleClick = function (menu) {
|
||||
selected.value = menu.id
|
||||
router.push(menu.path)
|
||||
}
|
||||
|
||||
return {
|
||||
menus,
|
||||
selected,
|
||||
currentPath,
|
||||
handleClick,
|
||||
}
|
||||
},
|
||||
}
|
||||
</script>
|
||||
108
example/src/view/hooks.vue
Normal file
108
example/src/view/hooks.vue
Normal file
@@ -0,0 +1,108 @@
|
||||
<template>
|
||||
<lay-layout>
|
||||
<lay-side>
|
||||
<lay-scroll style="overflow-y: scroll">
|
||||
<ul class="layui-menu layui-menu-lg layui-menu-docs">
|
||||
<li
|
||||
v-for="menu in menus"
|
||||
:key="menu"
|
||||
class="layui-menu-item-group"
|
||||
lay-options="{type: 'group', isAllowSpread: true}"
|
||||
>
|
||||
<div class="layui-menu-body-title">{{ menu.title }}</div>
|
||||
<hr />
|
||||
<ul>
|
||||
<li
|
||||
v-for="children in menu.children"
|
||||
:key="children"
|
||||
:class="[
|
||||
currentPath === children.path
|
||||
? 'layui-menu-item-checked2'
|
||||
: '',
|
||||
]"
|
||||
@click="handleClick(children)"
|
||||
>
|
||||
<div class="layui-menu-body-title">
|
||||
<router-link :to="children.path">
|
||||
<span>{{ children.title }}</span>
|
||||
<span class="layui-font-12 layui-font-gray">
|
||||
{{ children.subTitle }}
|
||||
</span>
|
||||
</router-link>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
</lay-scroll>
|
||||
</lay-side>
|
||||
<lay-body>
|
||||
<div style="padding: 20px">
|
||||
<router-view />
|
||||
</div>
|
||||
</lay-body>
|
||||
</lay-layout>
|
||||
</template>
|
||||
<script>
|
||||
import { ref, watch } from 'vue'
|
||||
import { useRouter, useRoute } from 'vue-router'
|
||||
export default {
|
||||
setup() {
|
||||
const route = useRoute()
|
||||
const router = useRouter()
|
||||
const currentPath = ref('/zh-CN/guide')
|
||||
|
||||
watch(
|
||||
() => route.path,
|
||||
(val) => {
|
||||
currentPath.value = val
|
||||
},
|
||||
{
|
||||
immediate: true,
|
||||
deep: true,
|
||||
}
|
||||
)
|
||||
|
||||
const menus = [
|
||||
{
|
||||
id: 1,
|
||||
title: 'hooks',
|
||||
children: [
|
||||
{
|
||||
id: 1,
|
||||
title: '事件',
|
||||
subTitle: 'useClickOutside',
|
||||
path: '/zh-CN/hooks/useClickOutside',
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
title: '拖拽',
|
||||
subTitle: 'useMove',
|
||||
path: '/zh-CN/hooks/useMove',
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
title: '全屏',
|
||||
subTitle: 'useFullScreen',
|
||||
path: '/zh-CN/hooks/useFullScreen',
|
||||
},
|
||||
],
|
||||
},
|
||||
]
|
||||
|
||||
const selected = ref(1)
|
||||
|
||||
const handleClick = function (menu) {
|
||||
selected.value = menu.id
|
||||
router.push(menu.path)
|
||||
}
|
||||
|
||||
return {
|
||||
menus,
|
||||
selected,
|
||||
currentPath,
|
||||
handleClick,
|
||||
}
|
||||
},
|
||||
}
|
||||
</script>
|
||||
248
example/src/view/index.vue
Normal file
248
example/src/view/index.vue
Normal file
@@ -0,0 +1,248 @@
|
||||
<template>
|
||||
<div
|
||||
style="
|
||||
margin-top: 60px;
|
||||
background-color: whitesmoke;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
"
|
||||
>
|
||||
<div class="site-banner">
|
||||
<div class="site-banner-main">
|
||||
<div class="site-zfj site-zfj-anim">
|
||||
<i
|
||||
class="layui-icon"
|
||||
style="color: #fff; color: rgba(255, 255, 255, 0.6)"
|
||||
></i
|
||||
>
|
||||
</div>
|
||||
<div class="layui-anim site-desc site-desc-anim">
|
||||
<p class="web-font-desc">layui - vue</p>
|
||||
<cite>layui vue, A component library for Vue 3 base on layui</cite>
|
||||
</div>
|
||||
<div class="site-download">
|
||||
<router-link
|
||||
class="layui-inline site-down"
|
||||
to="/zh-CN/guide/getStarted"
|
||||
>Get Started</router-link
|
||||
>
|
||||
</div>
|
||||
<div class="site-version">
|
||||
<span>当前版本:v<cite class="site-showv">0.2.4</cite></span>
|
||||
<span
|
||||
><router-link
|
||||
class="layui-inline site-down"
|
||||
to="/zh-CN/guide/changelog"
|
||||
>更新日志</router-link
|
||||
></span
|
||||
>
|
||||
<span>下载量:<em class="site-showdowns">1824</em></span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="site-banner-other">
|
||||
<a
|
||||
href="https://gitee.com/layui-vue"
|
||||
target="_blank"
|
||||
rel="nofollow"
|
||||
class="site-star"
|
||||
>
|
||||
<i class="layui-icon"></i> Star <cite id="getStars">257</cite>
|
||||
</a>
|
||||
<a
|
||||
href="https://gitee.com/layui-vue"
|
||||
target="_blank"
|
||||
rel="nofollow"
|
||||
class="site-fork"
|
||||
>
|
||||
Gitee
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<div style="margin-left: 10%; margin-right: 10%">
|
||||
<div>
|
||||
<ul class="layui-row layui-col-space30 site-idea">
|
||||
<li class="layui-col-md8">
|
||||
<div>
|
||||
<fieldset class="layui-elem-field layui-field-title">
|
||||
<legend>返璞归真</legend>
|
||||
<p>
|
||||
身处在前端社区的繁荣之下,我们都在有意或无意地追逐。而 layui
|
||||
偏偏回望当初,奔赴在返璞归真的漫漫征途,自信并勇敢着,追寻于原生态的书写指令,试图以最简单的方式诠释高效。
|
||||
</p>
|
||||
</fieldset>
|
||||
</div>
|
||||
</li>
|
||||
<li class="layui-col-md8">
|
||||
<div>
|
||||
<fieldset class="layui-elem-field layui-field-title">
|
||||
<legend>双面体验</legend>
|
||||
<p>
|
||||
拥有双面的不仅是人生,还有
|
||||
layui。一面极简,一面丰盈。极简是视觉所见的外在,是开发所念的简易。丰盈是倾情雕琢的内在,是信手拈来的承诺。一切本应如此,简而全,双重体验。
|
||||
</p>
|
||||
</fieldset>
|
||||
</div>
|
||||
</li>
|
||||
<li class="layui-col-md8">
|
||||
<div>
|
||||
<fieldset class="layui-elem-field layui-field-title">
|
||||
<legend>星辰大海</legend>
|
||||
<p>
|
||||
如果眼下还是一团零星之火,那运筹帷幄之后,迎面东风,就是一场烈焰燎原吧,那必定会是一番尽情的燃烧。待,秋风萧瑟时,散作满天星辰,你看那四季轮回<!--海天相接-->,正是
|
||||
layui 不灭的执念。
|
||||
</p>
|
||||
</fieldset>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<div class="footer footer-index">
|
||||
<p>
|
||||
Copyright © 2021 <a href="/index.html">layui-vue.pearadmin.com</a> MIT
|
||||
Licensed
|
||||
</p>
|
||||
<p></p>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<style>
|
||||
.site-banner {
|
||||
position: relative;
|
||||
height: 600px;
|
||||
text-align: center;
|
||||
overflow: hidden;
|
||||
background-color: #393d49;
|
||||
}
|
||||
.site-banner-bg {
|
||||
background-position: center 0;
|
||||
}
|
||||
.site-banner-bg,
|
||||
.site-banner-main {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
.site-download {
|
||||
margin-top: 72px;
|
||||
font-size: 0;
|
||||
}
|
||||
.site-download a {
|
||||
position: relative;
|
||||
padding: 0 50px 0 50px;
|
||||
height: 60px;
|
||||
line-height: 60px;
|
||||
border-radius: 4px;
|
||||
border: 1px solid #c2c2c2;
|
||||
border-color: rgba(255, 255, 255, 0.2);
|
||||
font-size: 24px;
|
||||
color: #ccc;
|
||||
transition: all 0.5s;
|
||||
-webkit-transition: all 0.5s;
|
||||
}
|
||||
.site-download a:hover {
|
||||
background-color: rgba(255, 255, 255, 0.05);
|
||||
border-radius: 50px;
|
||||
color: #ccc;
|
||||
}
|
||||
.site-zfj {
|
||||
padding-top: 25px;
|
||||
height: 220px;
|
||||
}
|
||||
.site-zfj-anim i {
|
||||
-webkit-animation-name: site-zfj;
|
||||
animation-name: site-zfj;
|
||||
-webkit-animation-duration: 5s;
|
||||
animation-duration: 5s;
|
||||
-webkit-animation-timing-function: linear;
|
||||
animation-timing-function: linear;
|
||||
}
|
||||
.site-zfj i {
|
||||
position: absolute;
|
||||
left: 50%;
|
||||
top: 50px;
|
||||
width: 200px;
|
||||
height: 200px;
|
||||
margin-left: -100px;
|
||||
font-size: 180px;
|
||||
color: #c2c2c2;
|
||||
}
|
||||
.site-desc-anim {
|
||||
-webkit-animation-name: site-desc;
|
||||
animation-name: site-desc;
|
||||
}
|
||||
.site-desc {
|
||||
position: relative;
|
||||
height: 70px;
|
||||
margin-top: 20px;
|
||||
}
|
||||
.site-desc .web-font-desc {
|
||||
color: #fff;
|
||||
color: rgba(255, 255, 255, 0.8);
|
||||
font-size: 51px;
|
||||
}
|
||||
.web-font-desc {
|
||||
font-style: normal;
|
||||
font-weight: 300;
|
||||
}
|
||||
.site-desc cite {
|
||||
position: absolute;
|
||||
bottom: -40px;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
color: #c2c2c2;
|
||||
color: rgba(255, 255, 255, 0.66);
|
||||
font-style: normal;
|
||||
}
|
||||
.site-version {
|
||||
position: relative;
|
||||
margin-top: 18px;
|
||||
color: #ccc;
|
||||
font-size: 12px;
|
||||
}
|
||||
.site-version span {
|
||||
padding: 0 3px;
|
||||
}
|
||||
.site-version * {
|
||||
font-style: normal;
|
||||
}
|
||||
.site-version a {
|
||||
color: #e2e2e2;
|
||||
text-decoration: none;
|
||||
margin-top: -4px;
|
||||
}
|
||||
.site-banner-other {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
bottom: 35px;
|
||||
width: 100%;
|
||||
text-align: center;
|
||||
font-size: 0;
|
||||
}
|
||||
.site-banner-other a {
|
||||
display: inline-block;
|
||||
vertical-align: middle;
|
||||
height: 28px;
|
||||
line-height: 28px;
|
||||
margin: 0 6px;
|
||||
padding: 0 8px;
|
||||
border-radius: 4px;
|
||||
color: #c2c2c2;
|
||||
color: rgba(255, 255, 255, 0.8);
|
||||
border: 1px solid #c2c2c2;
|
||||
border-color: rgba(255, 255, 255, 0.2);
|
||||
font-size: 14px;
|
||||
transition: all 0.5s;
|
||||
-webkit-transition: all 0.5s;
|
||||
}
|
||||
.footer {
|
||||
width: 100%;
|
||||
padding: 30px 15px;
|
||||
line-height: 30px;
|
||||
text-align: center;
|
||||
color: #666;
|
||||
font-weight: 300;
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user