写添加菜单

This commit is contained in:
expressgy 2025-04-27 04:17:35 +08:00
parent fd361f332c
commit 3d5b72aea1
64 changed files with 2064 additions and 279 deletions

View File

@ -3,5 +3,6 @@
<template>
<NuxtLayout>
<NuxtPage />
<NiMessage/>
</NuxtLayout>
</template>

View File

@ -1,3 +1,12 @@
@import "../fonts/vfonts/Lato.css";
@import "../fonts/vfonts/FiraCode.css";
@import "../icon/startMessage/iconfont.css";
[data-prefix^="Ni"] {
font-family: v-sans, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol";
}
/*宽高比*/
.a11 {
aspect-ratio: 1;
@ -39,10 +48,15 @@
--Ni-theme-color-T3: #e8e8e7;
--Ni-theme-color-T4: #EFEFED;
--Ni-theme-color-T0-10: #37352f11;
--Ni-theme-color-T0-40: #37352f44;
--Ni-theme-color-T0-60: #37352f66;
--Ni-theme-color-T0-80: #37352f88;
--Ni-theme-color-T0-a0: #37352faa;
--Ni-theme-color-T0-c0: #37352fcc;
--Ni-theme-color: var(--Ni-theme-color-T0);
--Ni-theme-color: var(--Ni-theme-color-T1);
--Ni-theme-border-color: var(--Ni-theme-color-T0-40);
--Ni-theme-border-color-hover: var(--Ni-theme-color-T0-c0);
@ -66,6 +80,15 @@
--Ni-M-A: 1rem;
/*tertiary*/
/*default*/
--Ni-button-default-bg-default: #fefefe;
--Ni-button-default-bg-hover: #91918e33;
--Ni-button-default-bg-click: #37352f33;
/*浅色*/
--Ni-button-default-bg-default-light: #5f5e5b29;/*29-16*/
--Ni-button-default-bg-hover-light: #91918e38;/*38-22*/
--Ni-button-default-bg-click-light: #37352f47;/*47-28*/
/*primary*/
--Ni-button-primary-bg-default: #5f5e5b;
--Ni-button-primary-bg-hover: #91918e;

View File

@ -0,0 +1,5 @@
@font-face {
font-family: "v-mono";
font-weight: 400;
src: url("./assets/FiraCode-Regular.woff2");
}

View File

@ -0,0 +1,11 @@
@font-face {
font-family: "v-sans";
font-weight: 400;
src: url("./assets/FiraSans-Regular.woff2");
}
@font-face {
font-family: "v-sans";
font-weight: 500;
src: url("./assets/FiraSans-Medium.woff2");
}

View File

@ -0,0 +1,5 @@
@font-face {
font-family: "v-mono";
font-weight: 400;
src: url("./assets/IBMPlexMono-Regular.ttf");
}

View File

@ -0,0 +1,11 @@
@font-face {
font-family: "v-sans";
font-weight: 400;
src: url("./assets/IBMPlexSans-Regular.ttf");
}
@font-face {
font-family: "v-sans";
font-weight: 500;
src: url("./assets/IBMPlexSans-Medium.ttf");
}

View File

@ -0,0 +1,11 @@
@font-face {
font-family: "v-sans";
font-weight: 400;
src: url("./assets/Inter-Regular.woff2");
}
@font-face {
font-family: "v-sans";
font-weight: 500;
src: url("./assets/Inter-Medium.woff2");
}

View File

@ -0,0 +1,11 @@
@font-face {
font-family: "v-sans";
font-weight: 400;
src: url("./assets/LatoLatin-Regular.woff2");
}
@font-face {
font-family: "v-sans";
font-weight: 600;
src: url("./assets/LatoLatin-Semibold.woff2");
}

View File

@ -0,0 +1,11 @@
@font-face {
font-family: "v-sans";
font-weight: 400;
src: url("./assets/OpenSans-Regular.ttf");
}
@font-face {
font-family: "v-sans";
font-weight: 600;
src: url("./assets/OpenSans-SemiBold.ttf");
}

View File

@ -0,0 +1,44 @@
# vfonts
Integreted fonts for easy usage.
## Usage
```js
// in your js
import 'vfonts/{font-name}.css'
// for available fonts, see the following section
```
```css
/** in your css */
selector {
font-family: v-sans, v-mono, other-fallbacks;
}
/** for available font weights, see the following section */
selector {
font-weight: 400; /** regular */
}
selector {
font-weight: 500; /** medium */
}
```
## Available Fonts
### `v-sans`
- `FiraSans.css`
- font weight `400`, `500`
- `IBMPlexSans.css`
- font weight `400`, `500`
- `Inter.css`
- font weight `400`, `500`
- `Lato.css`
- font weight `400`, `600`
- `OpenSans.css`
- font weight `400`, `600`
- `Roboto.css`
- font weight `400`, `500`
- `RobotoSlab.css`
- font weight `400`, `500`
### `v-mono`
- `FiraCode.css`
- font weight `400`

View File

@ -0,0 +1,11 @@
@font-face {
font-family: "v-sans";
font-weight: 400;
src: url("./assets/Roboto-Regular.ttf");
}
@font-face {
font-family: "v-sans";
font-weight: 500;
src: url("./assets/Roboto-Medium.ttf");
}

View File

@ -0,0 +1,11 @@
@font-face {
font-family: "v-sans";
font-weight: 400;
src: url("./assets/RobotoSlab-Regular.ttf");
}
@font-face {
font-family: "v-sans";
font-weight: 500;
src: url("./assets/RobotoSlab-Medium.ttf");
}

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -0,0 +1,93 @@
Copyright (c) 2014, The Fira Code Project Authors (https://github.com/tonsky/FiraCode)
This Font Software is licensed under the SIL Open Font License, Version 1.1.
This license is copied below, and is also available with a FAQ at:
http://scripts.sil.org/OFL
-----------------------------------------------------------
SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
-----------------------------------------------------------
PREAMBLE
The goals of the Open Font License (OFL) are to stimulate worldwide
development of collaborative font projects, to support the font creation
efforts of academic and linguistic communities, and to provide a free and
open framework in which fonts may be shared and improved in partnership
with others.
The OFL allows the licensed fonts to be used, studied, modified and
redistributed freely as long as they are not sold by themselves. The
fonts, including any derivative works, can be bundled, embedded,
redistributed and/or sold with any software provided that any reserved
names are not used by derivative works. The fonts and derivatives,
however, cannot be released under any other type of license. The
requirement for fonts to remain under this license does not apply
to any document created using the fonts or their derivatives.
DEFINITIONS
"Font Software" refers to the set of files released by the Copyright
Holder(s) under this license and clearly marked as such. This may
include source files, build scripts and documentation.
"Reserved Font Name" refers to any names specified as such after the
copyright statement(s).
"Original Version" refers to the collection of Font Software components as
distributed by the Copyright Holder(s).
"Modified Version" refers to any derivative made by adding to, deleting,
or substituting -- in part or in whole -- any of the components of the
Original Version, by changing formats or by porting the Font Software to a
new environment.
"Author" refers to any designer, engineer, programmer, technical
writer or other person who contributed to the Font Software.
PERMISSION & CONDITIONS
Permission is hereby granted, free of charge, to any person obtaining
a copy of the Font Software, to use, study, copy, merge, embed, modify,
redistribute, and sell modified and unmodified copies of the Font
Software, subject to the following conditions:
1) Neither the Font Software nor any of its individual components,
in Original or Modified Versions, may be sold by itself.
2) Original or Modified Versions of the Font Software may be bundled,
redistributed and/or sold with any software, provided that each copy
contains the above copyright notice and this license. These can be
included either as stand-alone text files, human-readable headers or
in the appropriate machine-readable metadata fields within text or
binary files as long as those fields can be easily viewed by the user.
3) No Modified Version of the Font Software may use the Reserved Font
Name(s) unless explicit written permission is granted by the corresponding
Copyright Holder. This restriction only applies to the primary font name as
presented to the users.
4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
Software shall not be used to promote, endorse or advertise any
Modified Version, except to acknowledge the contribution(s) of the
Copyright Holder(s) and the Author(s) or with their explicit written
permission.
5) The Font Software, modified or unmodified, in part or in whole,
must be distributed entirely under this license, and must not be
distributed under any other license. The requirement for fonts to
remain under this license does not apply to any document created
using the Font Software.
TERMINATION
This license becomes null and void if any of the above conditions are
not met.
DISCLAIMER
THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
OTHER DEALINGS IN THE FONT SOFTWARE.

View File

@ -0,0 +1 @@
FiraSans is licensed under the [Open Font License](https://scripts.sil.org/cms/scripts/page.php?site_id=nrsi&id=OFL).

View File

@ -0,0 +1,93 @@
Copyright © 2017 IBM Corp. with Reserved Font Name "Plex"
This Font Software is licensed under the SIL Open Font License, Version 1.1.
This license is copied below, and is also available with a FAQ at: http://scripts.sil.org/OFL
-----------------------------------------------------------
SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
-----------------------------------------------------------
PREAMBLE
The goals of the Open Font License (OFL) are to stimulate worldwide
development of collaborative font projects, to support the font creation
efforts of academic and linguistic communities, and to provide a free and
open framework in which fonts may be shared and improved in partnership
with others.
The OFL allows the licensed fonts to be used, studied, modified and
redistributed freely as long as they are not sold by themselves. The
fonts, including any derivative works, can be bundled, embedded,
redistributed and/or sold with any software provided that any reserved
names are not used by derivative works. The fonts and derivatives,
however, cannot be released under any other type of license. The
requirement for fonts to remain under this license does not apply
to any document created using the fonts or their derivatives.
DEFINITIONS
"Font Software" refers to the set of files released by the Copyright
Holder(s) under this license and clearly marked as such. This may
include source files, build scripts and documentation.
"Reserved Font Name" refers to any names specified as such after the
copyright statement(s).
"Original Version" refers to the collection of Font Software components as
distributed by the Copyright Holder(s).
"Modified Version" refers to any derivative made by adding to, deleting,
or substituting -- in part or in whole -- any of the components of the
Original Version, by changing formats or by porting the Font Software to a
new environment.
"Author" refers to any designer, engineer, programmer, technical
writer or other person who contributed to the Font Software.
PERMISSION & CONDITIONS
Permission is hereby granted, free of charge, to any person obtaining
a copy of the Font Software, to use, study, copy, merge, embed, modify,
redistribute, and sell modified and unmodified copies of the Font
Software, subject to the following conditions:
1) Neither the Font Software nor any of its individual components,
in Original or Modified Versions, may be sold by itself.
2) Original or Modified Versions of the Font Software may be bundled,
redistributed and/or sold with any software, provided that each copy
contains the above copyright notice and this license. These can be
included either as stand-alone text files, human-readable headers or
in the appropriate machine-readable metadata fields within text or
binary files as long as those fields can be easily viewed by the user.
3) No Modified Version of the Font Software may use the Reserved Font
Name(s) unless explicit written permission is granted by the corresponding
Copyright Holder. This restriction only applies to the primary font name as
presented to the users.
4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
Software shall not be used to promote, endorse or advertise any
Modified Version, except to acknowledge the contribution(s) of the
Copyright Holder(s) and the Author(s) or with their explicit written
permission.
5) The Font Software, modified or unmodified, in part or in whole,
must be distributed entirely under this license, and must not be
distributed under any other license. The requirement for fonts to
remain under this license does not apply to any document created
using the Font Software.
TERMINATION
This license becomes null and void if any of the above conditions are
not met.
DISCLAIMER
THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
OTHER DEALINGS IN THE FONT SOFTWARE.

View File

@ -0,0 +1,94 @@
Copyright (c) 2016-2020 The Inter Project Authors.
"Inter" is trademark of Rasmus Andersson.
https://github.com/rsms/inter
This Font Software is licensed under the SIL Open Font License, Version 1.1.
This license is copied below, and is also available with a FAQ at:
http://scripts.sil.org/OFL
-----------------------------------------------------------
SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
-----------------------------------------------------------
PREAMBLE
The goals of the Open Font License (OFL) are to stimulate worldwide
development of collaborative font projects, to support the font creation
efforts of academic and linguistic communities, and to provide a free and
open framework in which fonts may be shared and improved in partnership
with others.
The OFL allows the licensed fonts to be used, studied, modified and
redistributed freely as long as they are not sold by themselves. The
fonts, including any derivative works, can be bundled, embedded,
redistributed and/or sold with any software provided that any reserved
names are not used by derivative works. The fonts and derivatives,
however, cannot be released under any other type of license. The
requirement for fonts to remain under this license does not apply
to any document created using the fonts or their derivatives.
DEFINITIONS
"Font Software" refers to the set of files released by the Copyright
Holder(s) under this license and clearly marked as such. This may
include source files, build scripts and documentation.
"Reserved Font Name" refers to any names specified as such after the
copyright statement(s).
"Original Version" refers to the collection of Font Software components as
distributed by the Copyright Holder(s).
"Modified Version" refers to any derivative made by adding to, deleting,
or substituting -- in part or in whole -- any of the components of the
Original Version, by changing formats or by porting the Font Software to a
new environment.
"Author" refers to any designer, engineer, programmer, technical
writer or other person who contributed to the Font Software.
PERMISSION AND CONDITIONS
Permission is hereby granted, free of charge, to any person obtaining
a copy of the Font Software, to use, study, copy, merge, embed, modify,
redistribute, and sell modified and unmodified copies of the Font
Software, subject to the following conditions:
1) Neither the Font Software nor any of its individual components,
in Original or Modified Versions, may be sold by itself.
2) Original or Modified Versions of the Font Software may be bundled,
redistributed and/or sold with any software, provided that each copy
contains the above copyright notice and this license. These can be
included either as stand-alone text files, human-readable headers or
in the appropriate machine-readable metadata fields within text or
binary files as long as those fields can be easily viewed by the user.
3) No Modified Version of the Font Software may use the Reserved Font
Name(s) unless explicit written permission is granted by the corresponding
Copyright Holder. This restriction only applies to the primary font name as
presented to the users.
4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
Software shall not be used to promote, endorse or advertise any
Modified Version, except to acknowledge the contribution(s) of the
Copyright Holder(s) and the Author(s) or with their explicit written
permission.
5) The Font Software, modified or unmodified, in part or in whole,
must be distributed entirely under this license, and must not be
distributed under any other license. The requirement for fonts to
remain under this license does not apply to any document created
using the Font Software.
TERMINATION
This license becomes null and void if any of the above conditions are
not met.
DISCLAIMER
THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
OTHER DEALINGS IN THE FONT SOFTWARE.

View File

@ -0,0 +1 @@
Lato is licensed under the [Open Font License](https://scripts.sil.org/cms/scripts/page.php?site_id=nrsi&id=OFL).

View File

@ -0,0 +1 @@
Open Sans is licensed under the [Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0)

View File

@ -0,0 +1 @@
Roboto is licensed under the [Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0)

View File

@ -0,0 +1 @@
Roboto is licensed under the [Apache License, Version 2.0](http://www.apache.org/licenses/LICENSE-2.0)

View File

@ -0,0 +1,539 @@
/* Logo 字体 */
@font-face {
font-family: "iconfont logo";
src: url('https://at.alicdn.com/t/font_985780_km7mi63cihi.eot?t=1545807318834');
src: url('https://at.alicdn.com/t/font_985780_km7mi63cihi.eot?t=1545807318834#iefix') format('embedded-opentype'),
url('https://at.alicdn.com/t/font_985780_km7mi63cihi.woff?t=1545807318834') format('woff'),
url('https://at.alicdn.com/t/font_985780_km7mi63cihi.ttf?t=1545807318834') format('truetype'),
url('https://at.alicdn.com/t/font_985780_km7mi63cihi.svg?t=1545807318834#iconfont') format('svg');
}
.logo {
font-family: "iconfont logo";
font-size: 160px;
font-style: normal;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
/* tabs */
.nav-tabs {
position: relative;
}
.nav-tabs .nav-more {
position: absolute;
right: 0;
bottom: 0;
height: 42px;
line-height: 42px;
color: #666;
}
#tabs {
border-bottom: 1px solid #eee;
}
#tabs li {
cursor: pointer;
width: 100px;
height: 40px;
line-height: 40px;
text-align: center;
font-size: 16px;
border-bottom: 2px solid transparent;
position: relative;
z-index: 1;
margin-bottom: -1px;
color: #666;
}
#tabs .active {
border-bottom-color: #f00;
color: #222;
}
.tab-container .content {
display: none;
}
/* 页面布局 */
.main {
padding: 30px 100px;
width: 960px;
margin: 0 auto;
}
.main .logo {
color: #333;
text-align: left;
margin-bottom: 30px;
line-height: 1;
height: 110px;
margin-top: -50px;
overflow: hidden;
*zoom: 1;
}
.main .logo a {
font-size: 160px;
color: #333;
}
.helps {
margin-top: 40px;
}
.helps pre {
padding: 20px;
margin: 10px 0;
border: solid 1px #e7e1cd;
background-color: #fffdef;
overflow: auto;
}
.icon_lists {
width: 100% !important;
overflow: hidden;
*zoom: 1;
}
.icon_lists li {
width: 100px;
margin-bottom: 10px;
margin-right: 20px;
text-align: center;
list-style: none !important;
cursor: default;
}
.icon_lists li .code-name {
line-height: 1.2;
}
.icon_lists .icon {
display: block;
height: 100px;
line-height: 100px;
font-size: 42px;
margin: 10px auto;
color: #333;
-webkit-transition: font-size 0.25s linear, width 0.25s linear;
-moz-transition: font-size 0.25s linear, width 0.25s linear;
transition: font-size 0.25s linear, width 0.25s linear;
}
.icon_lists .icon:hover {
font-size: 100px;
}
.icon_lists .svg-icon {
/* 通过设置 font-size 来改变图标大小 */
width: 1em;
/* 图标和文字相邻时,垂直对齐 */
vertical-align: -0.15em;
/* 通过设置 color 来改变 SVG 的颜色/fill */
fill: currentColor;
/* path stroke 溢出 viewBox 部分在 IE 下会显示
normalize.css 中也包含这行 */
overflow: hidden;
}
.icon_lists li .name,
.icon_lists li .code-name {
color: #666;
}
/* markdown 样式 */
.markdown {
color: #666;
font-size: 14px;
line-height: 1.8;
}
.highlight {
line-height: 1.5;
}
.markdown img {
vertical-align: middle;
max-width: 100%;
}
.markdown h1 {
color: #404040;
font-weight: 500;
line-height: 40px;
margin-bottom: 24px;
}
.markdown h2,
.markdown h3,
.markdown h4,
.markdown h5,
.markdown h6 {
color: #404040;
margin: 1.6em 0 0.6em 0;
font-weight: 500;
clear: both;
}
.markdown h1 {
font-size: 28px;
}
.markdown h2 {
font-size: 22px;
}
.markdown h3 {
font-size: 16px;
}
.markdown h4 {
font-size: 14px;
}
.markdown h5 {
font-size: 12px;
}
.markdown h6 {
font-size: 12px;
}
.markdown hr {
height: 1px;
border: 0;
background: #e9e9e9;
margin: 16px 0;
clear: both;
}
.markdown p {
margin: 1em 0;
}
.markdown>p,
.markdown>blockquote,
.markdown>.highlight,
.markdown>ol,
.markdown>ul {
width: 80%;
}
.markdown ul>li {
list-style: circle;
}
.markdown>ul li,
.markdown blockquote ul>li {
margin-left: 20px;
padding-left: 4px;
}
.markdown>ul li p,
.markdown>ol li p {
margin: 0.6em 0;
}
.markdown ol>li {
list-style: decimal;
}
.markdown>ol li,
.markdown blockquote ol>li {
margin-left: 20px;
padding-left: 4px;
}
.markdown code {
margin: 0 3px;
padding: 0 5px;
background: #eee;
border-radius: 3px;
}
.markdown strong,
.markdown b {
font-weight: 600;
}
.markdown>table {
border-collapse: collapse;
border-spacing: 0px;
empty-cells: show;
border: 1px solid #e9e9e9;
width: 95%;
margin-bottom: 24px;
}
.markdown>table th {
white-space: nowrap;
color: #333;
font-weight: 600;
}
.markdown>table th,
.markdown>table td {
border: 1px solid #e9e9e9;
padding: 8px 16px;
text-align: left;
}
.markdown>table th {
background: #F7F7F7;
}
.markdown blockquote {
font-size: 90%;
color: #999;
border-left: 4px solid #e9e9e9;
padding-left: 0.8em;
margin: 1em 0;
}
.markdown blockquote p {
margin: 0;
}
.markdown .anchor {
opacity: 0;
transition: opacity 0.3s ease;
margin-left: 8px;
}
.markdown .waiting {
color: #ccc;
}
.markdown h1:hover .anchor,
.markdown h2:hover .anchor,
.markdown h3:hover .anchor,
.markdown h4:hover .anchor,
.markdown h5:hover .anchor,
.markdown h6:hover .anchor {
opacity: 1;
display: inline-block;
}
.markdown>br,
.markdown>p>br {
clear: both;
}
.hljs {
display: block;
background: white;
padding: 0.5em;
color: #333333;
overflow-x: auto;
}
.hljs-comment,
.hljs-meta {
color: #969896;
}
.hljs-string,
.hljs-variable,
.hljs-template-variable,
.hljs-strong,
.hljs-emphasis,
.hljs-quote {
color: #df5000;
}
.hljs-keyword,
.hljs-selector-tag,
.hljs-type {
color: #a71d5d;
}
.hljs-literal,
.hljs-symbol,
.hljs-bullet,
.hljs-attribute {
color: #0086b3;
}
.hljs-section,
.hljs-name {
color: #63a35c;
}
.hljs-tag {
color: #333333;
}
.hljs-title,
.hljs-attr,
.hljs-selector-id,
.hljs-selector-class,
.hljs-selector-attr,
.hljs-selector-pseudo {
color: #795da3;
}
.hljs-addition {
color: #55a532;
background-color: #eaffea;
}
.hljs-deletion {
color: #bd2c00;
background-color: #ffecec;
}
.hljs-link {
text-decoration: underline;
}
/* 代码高亮 */
/* PrismJS 1.15.0
https://prismjs.com/download.html#themes=prism&languages=markup+css+clike+javascript */
/**
* prism.js default theme for JavaScript, CSS and HTML
* Based on dabblet (http://dabblet.com)
* @author Lea Verou
*/
code[class*="language-"],
pre[class*="language-"] {
color: black;
background: none;
text-shadow: 0 1px white;
font-family: Consolas, Monaco, 'Andale Mono', 'Ubuntu Mono', monospace;
text-align: left;
white-space: pre;
word-spacing: normal;
word-break: normal;
word-wrap: normal;
line-height: 1.5;
-moz-tab-size: 4;
-o-tab-size: 4;
tab-size: 4;
-webkit-hyphens: none;
-moz-hyphens: none;
-ms-hyphens: none;
hyphens: none;
}
pre[class*="language-"]::-moz-selection,
pre[class*="language-"] ::-moz-selection,
code[class*="language-"]::-moz-selection,
code[class*="language-"] ::-moz-selection {
text-shadow: none;
background: #b3d4fc;
}
pre[class*="language-"]::selection,
pre[class*="language-"] ::selection,
code[class*="language-"]::selection,
code[class*="language-"] ::selection {
text-shadow: none;
background: #b3d4fc;
}
@media print {
code[class*="language-"],
pre[class*="language-"] {
text-shadow: none;
}
}
/* Code blocks */
pre[class*="language-"] {
padding: 1em;
margin: .5em 0;
overflow: auto;
}
:not(pre)>code[class*="language-"],
pre[class*="language-"] {
background: #f5f2f0;
}
/* Inline code */
:not(pre)>code[class*="language-"] {
padding: .1em;
border-radius: .3em;
white-space: normal;
}
.token.comment,
.token.prolog,
.token.doctype,
.token.cdata {
color: slategray;
}
.token.punctuation {
color: #999;
}
.namespace {
opacity: .7;
}
.token.property,
.token.tag,
.token.boolean,
.token.number,
.token.constant,
.token.symbol,
.token.deleted {
color: #905;
}
.token.selector,
.token.attr-name,
.token.string,
.token.char,
.token.builtin,
.token.inserted {
color: #690;
}
.token.operator,
.token.entity,
.token.url,
.language-css .token.string,
.style .token.string {
color: #9a6e3a;
background: hsla(0, 0%, 100%, .5);
}
.token.atrule,
.token.attr-value,
.token.keyword {
color: #07a;
}
.token.function,
.token.class-name {
color: #DD4A68;
}
.token.regex,
.token.important,
.token.variable {
color: #e90;
}
.token.important,
.token.bold {
font-weight: bold;
}
.token.italic {
font-style: italic;
}
.token.entity {
cursor: help;
}

View File

@ -0,0 +1,280 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8"/>
<title>iconfont Demo</title>
<link rel="shortcut icon" href="//img.alicdn.com/imgextra/i4/O1CN01Z5paLz1O0zuCC7osS_!!6000000001644-55-tps-83-82.svg" type="image/x-icon"/>
<link rel="icon" type="image/svg+xml" href="//img.alicdn.com/imgextra/i4/O1CN01Z5paLz1O0zuCC7osS_!!6000000001644-55-tps-83-82.svg"/>
<link rel="stylesheet" href="https://g.alicdn.com/thx/cube/1.3.2/cube.min.css">
<link rel="stylesheet" href="demo.css">
<link rel="stylesheet" href="iconfont.css">
<script src="iconfont.js"></script>
<!-- jQuery -->
<script src="https://a1.alicdn.com/oss/uploads/2018/12/26/7bfddb60-08e8-11e9-9b04-53e73bb6408b.js"></script>
<!-- 代码高亮 -->
<script src="https://a1.alicdn.com/oss/uploads/2018/12/26/a3f714d0-08e6-11e9-8a15-ebf944d7534c.js"></script>
<style>
.main .logo {
margin-top: 0;
height: auto;
}
.main .logo a {
display: flex;
align-items: center;
}
.main .logo .sub-title {
margin-left: 0.5em;
font-size: 22px;
color: #fff;
background: linear-gradient(-45deg, #3967FF, #B500FE);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
}
</style>
</head>
<body>
<div class="main">
<h1 class="logo"><a href="https://www.iconfont.cn/" title="iconfont 首页" target="_blank">
<img width="200" src="https://img.alicdn.com/imgextra/i3/O1CN01Mn65HV1FfSEzR6DKv_!!6000000000514-55-tps-228-59.svg">
</a></h1>
<div class="nav-tabs">
<ul id="tabs" class="dib-box">
<li class="dib active"><span>Unicode</span></li>
<li class="dib"><span>Font class</span></li>
<li class="dib"><span>Symbol</span></li>
</ul>
<a href="https://www.iconfont.cn/manage/index?manage_type=myprojects&projectId=4906634" target="_blank" class="nav-more">查看项目</a>
</div>
<div class="tab-container">
<div class="content unicode" style="display: block;">
<ul class="icon_lists dib-box">
<li class="dib">
<span class="icon startMessage">&#xe839;</span>
<div class="name">error-fill</div>
<div class="code-name">&amp;#xe839;</div>
</li>
<li class="dib">
<span class="icon startMessage">&#xe83d;</span>
<div class="name">info-fill</div>
<div class="code-name">&amp;#xe83d;</div>
</li>
<li class="dib">
<span class="icon startMessage">&#xe842;</span>
<div class="name">success-fill</div>
<div class="code-name">&amp;#xe842;</div>
</li>
<li class="dib">
<span class="icon startMessage">&#xe871;</span>
<div class="name">user-defined-fill</div>
<div class="code-name">&amp;#xe871;</div>
</li>
</ul>
<div class="article markdown">
<h2 id="unicode-">Unicode 引用</h2>
<hr>
<p>Unicode 是字体在网页端最原始的应用方式,特点是:</p>
<ul>
<li>支持按字体的方式去动态调整图标大小,颜色等等。</li>
<li>默认情况下不支持多色,直接添加多色图标会自动去色。</li>
</ul>
<blockquote>
<p>注意:新版 iconfont 支持两种方式引用多色图标SVG symbol 引用方式和彩色字体图标模式。(使用彩色字体图标需要在「编辑项目」中开启「彩色」选项后并重新生成。)</p>
</blockquote>
<p>Unicode 使用步骤如下:</p>
<h3 id="-font-face">第一步:拷贝项目下面生成的 <code>@font-face</code></h3>
<pre><code class="language-css"
>@font-face {
font-family: 'startMessage';
src: url('iconfont.woff2?t=1745672562595') format('woff2'),
url('iconfont.woff?t=1745672562595') format('woff'),
url('iconfont.ttf?t=1745672562595') format('truetype');
}
</code></pre>
<h3 id="-iconfont-">第二步:定义使用 iconfont 的样式</h3>
<pre><code class="language-css"
>.startMessage {
font-family: "startMessage" !important;
font-size: 16px;
font-style: normal;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
</code></pre>
<h3 id="-">第三步:挑选相应图标并获取字体编码,应用于页面</h3>
<pre>
<code class="language-html"
>&lt;span class="startMessage"&gt;&amp;#x33;&lt;/span&gt;
</code></pre>
<blockquote>
<p>"startMessage" 是你项目下的 font-family。可以通过编辑项目查看默认是 "iconfont"。</p>
</blockquote>
</div>
</div>
<div class="content font-class">
<ul class="icon_lists dib-box">
<li class="dib">
<span class="icon startMessage startMessageerror-fill"></span>
<div class="name">
error-fill
</div>
<div class="code-name">.startMessageerror-fill
</div>
</li>
<li class="dib">
<span class="icon startMessage startMessageinfo-fill"></span>
<div class="name">
info-fill
</div>
<div class="code-name">.startMessageinfo-fill
</div>
</li>
<li class="dib">
<span class="icon startMessage startMessagesuccess-fill"></span>
<div class="name">
success-fill
</div>
<div class="code-name">.startMessagesuccess-fill
</div>
</li>
<li class="dib">
<span class="icon startMessage startMessageuser-defined-fill"></span>
<div class="name">
user-defined-fill
</div>
<div class="code-name">.startMessageuser-defined-fill
</div>
</li>
</ul>
<div class="article markdown">
<h2 id="font-class-">font-class 引用</h2>
<hr>
<p>font-class 是 Unicode 使用方式的一种变种,主要是解决 Unicode 书写不直观,语意不明确的问题。</p>
<p>与 Unicode 使用方式相比,具有如下特点:</p>
<ul>
<li>相比于 Unicode 语意明确,书写更直观。可以很容易分辨这个 icon 是什么。</li>
<li>因为使用 class 来定义图标,所以当要替换图标时,只需要修改 class 里面的 Unicode 引用。</li>
</ul>
<p>使用步骤如下:</p>
<h3 id="-fontclass-">第一步:引入项目下面生成的 fontclass 代码:</h3>
<pre><code class="language-html">&lt;link rel="stylesheet" href="./iconfont.css"&gt;
</code></pre>
<h3 id="-">第二步:挑选相应图标并获取类名,应用于页面:</h3>
<pre><code class="language-html">&lt;span class="startMessage startMessagexxx"&gt;&lt;/span&gt;
</code></pre>
<blockquote>
<p>"
startMessage" 是你项目下的 font-family。可以通过编辑项目查看默认是 "iconfont"。</p>
</blockquote>
</div>
</div>
<div class="content symbol">
<ul class="icon_lists dib-box">
<li class="dib">
<svg class="icon svg-icon" aria-hidden="true">
<use xlink:href="#startMessageerror-fill"></use>
</svg>
<div class="name">error-fill</div>
<div class="code-name">#startMessageerror-fill</div>
</li>
<li class="dib">
<svg class="icon svg-icon" aria-hidden="true">
<use xlink:href="#startMessageinfo-fill"></use>
</svg>
<div class="name">info-fill</div>
<div class="code-name">#startMessageinfo-fill</div>
</li>
<li class="dib">
<svg class="icon svg-icon" aria-hidden="true">
<use xlink:href="#startMessagesuccess-fill"></use>
</svg>
<div class="name">success-fill</div>
<div class="code-name">#startMessagesuccess-fill</div>
</li>
<li class="dib">
<svg class="icon svg-icon" aria-hidden="true">
<use xlink:href="#startMessageuser-defined-fill"></use>
</svg>
<div class="name">user-defined-fill</div>
<div class="code-name">#startMessageuser-defined-fill</div>
</li>
</ul>
<div class="article markdown">
<h2 id="symbol-">Symbol 引用</h2>
<hr>
<p>这是一种全新的使用方式,应该说这才是未来的主流,也是平台目前推荐的用法。相关介绍可以参考这篇<a href="">文章</a>
这种用法其实是做了一个 SVG 的集合,与另外两种相比具有如下特点:</p>
<ul>
<li>支持多色图标了,不再受单色限制。</li>
<li>通过一些技巧,支持像字体那样,通过 <code>font-size</code>, <code>color</code> 来调整样式。</li>
<li>兼容性较差,支持 IE9+,及现代浏览器。</li>
<li>浏览器渲染 SVG 的性能一般,还不如 png。</li>
</ul>
<p>使用步骤如下:</p>
<h3 id="-symbol-">第一步:引入项目下面生成的 symbol 代码:</h3>
<pre><code class="language-html">&lt;script src="./iconfont.js"&gt;&lt;/script&gt;
</code></pre>
<h3 id="-css-">第二步:加入通用 CSS 代码(引入一次就行):</h3>
<pre><code class="language-html">&lt;style&gt;
.icon {
width: 1em;
height: 1em;
vertical-align: -0.15em;
fill: currentColor;
overflow: hidden;
}
&lt;/style&gt;
</code></pre>
<h3 id="-">第三步:挑选相应图标并获取类名,应用于页面:</h3>
<pre><code class="language-html">&lt;svg class="icon" aria-hidden="true"&gt;
&lt;use xlink:href="#icon-xxx"&gt;&lt;/use&gt;
&lt;/svg&gt;
</code></pre>
</div>
</div>
</div>
</div>
<script>
$(document).ready(function () {
$('.tab-container .content:first').show()
$('#tabs li').click(function (e) {
var tabContent = $('.tab-container .content')
var index = $(this).index()
if ($(this).hasClass('active')) {
return
} else {
$('#tabs li').removeClass('active')
$(this).addClass('active')
tabContent.hide().eq(index).fadeIn()
}
})
})
</script>
</body>
</html>

View File

@ -0,0 +1,31 @@
@font-face {
font-family: "startMessageIconFont"; /* Project id 4906634 */
src: url('iconfont.woff2?t=1745672562595') format('woff2'),
url('iconfont.woff?t=1745672562595') format('woff'),
url('iconfont.ttf?t=1745672562595') format('truetype');
}
.startMessageIconFont {
font-family: "startMessageIconFont" !important;
font-size: 16px;
font-style: normal;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
.startMessageerror-fill:before {
content: "\e839";
}
.startMessageinfo-fill:before {
content: "\e83d";
}
.startMessagesuccess-fill:before {
content: "\e842";
}
.startMessageuser-defined-fill:before {
content: "\e871";
}

View File

@ -0,0 +1 @@
window._iconfont_svg_string_4906634='<svg><symbol id="startMessageerror-fill" viewBox="0 0 1024 1024"><path d="M512 97.52381c228.912762 0 414.47619 185.563429 414.47619 414.47619s-185.563429 414.47619-414.47619 414.47619S97.52381 740.912762 97.52381 512 283.087238 97.52381 512 97.52381z m129.29219 233.447619l-129.267809 129.29219-129.316571-129.29219-51.736381 51.736381 129.316571 129.267809-129.316571 129.316571 51.736381 51.736381L512 563.687619l129.29219 129.316571 51.736381-51.73638L563.687619 512l129.316571-129.29219-51.73638-51.736381z" ></path></symbol><symbol id="startMessageinfo-fill" viewBox="0 0 1024 1024"><path d="M512 97.52381c228.912762 0 414.47619 185.563429 414.47619 414.47619s-185.563429 414.47619-414.47619 414.47619S97.52381 740.912762 97.52381 512 283.087238 97.52381 512 97.52381z m36.571429 341.333333h-73.142858v292.571428h73.142858V438.857143z m0-121.904762h-73.142858v73.142857h73.142858v-73.142857z" ></path></symbol><symbol id="startMessagesuccess-fill" viewBox="0 0 1024 1024"><path d="M512 97.52381c228.912762 0 414.47619 185.563429 414.47619 414.47619s-185.563429 414.47619-414.47619 414.47619S97.52381 740.912762 97.52381 512 283.087238 97.52381 512 97.52381z m193.194667 218.331428L447.21981 581.315048l-103.936-107.812572-52.662858 50.761143 156.379429 162.230857 310.662095-319.683047-52.467809-50.956191z" ></path></symbol><symbol id="startMessageuser-defined-fill" viewBox="0 0 1024 1024"><path d="M512 134.095238c212.089905 0 377.904762 148.187429 377.904762 354.279619 0 56.56381-17.871238 98.986667-64.926476 117.857524-18.358857 7.338667-36.230095 8.704-56.173715 6.802286-6.144-0.585143-12.263619-1.462857-19.407238-2.681905l-22.186666-4.022857c-29.842286-5.12-43.032381-3.900952-56.783238 5.144381-5.851429 3.876571-11.995429 9.411048-18.456381 17.066666-18.041905 21.382095-20.358095 38.034286-11.117715 73.289143l3.120762 11.166476 4.559238 15.408762 2.730667 9.654857c1.584762 5.851429 2.852571 11.093333 3.900952 16.335239 4.705524 23.844571 4.559238 45.29981-4.461714 67.535238C629.808762 873.569524 582.851048 889.904762 512 889.904762c-208.700952 0-377.904762-169.20381-377.904762-377.904762 0-208.700952 169.20381-377.904762 377.904762-377.904762z m-27.306667 509.44a73.142857 73.142857 0 1 0-144.847238 20.358095 73.142857 73.142857 0 0 0 144.847238-20.358095z m-33.913904-241.444571a73.142857 73.142857 0 1 0-144.871619 20.358095 73.142857 73.142857 0 0 0 144.871619-20.358095zM692.175238 368.152381a73.142857 73.142857 0 1 0-144.847238 20.358095 73.142857 73.142857 0 0 0 144.847238-20.358095z" ></path></symbol></svg>',(n=>{var e=(t=(t=document.getElementsByTagName("script"))[t.length-1]).getAttribute("data-injectcss"),t=t.getAttribute("data-disable-injectsvg");if(!t){var o,i,s,a,l,d=function(e,t){t.parentNode.insertBefore(e,t)};if(e&&!n.__iconfont__svg__cssinject__){n.__iconfont__svg__cssinject__=!0;try{document.write("<style>.svgfont {display: inline-block;width: 1em;height: 1em;fill: currentColor;vertical-align: -0.1em;font-size:16px;}</style>")}catch(e){console&&console.log(e)}}o=function(){var e,t=document.createElement("div");t.innerHTML=n._iconfont_svg_string_4906634,(t=t.getElementsByTagName("svg")[0])&&(t.setAttribute("aria-hidden","true"),t.style.position="absolute",t.style.width=0,t.style.height=0,t.style.overflow="hidden",t=t,(e=document.body).firstChild?d(t,e.firstChild):e.appendChild(t))},document.addEventListener?~["complete","loaded","interactive"].indexOf(document.readyState)?setTimeout(o,0):(i=function(){document.removeEventListener("DOMContentLoaded",i,!1),o()},document.addEventListener("DOMContentLoaded",i,!1)):document.attachEvent&&(s=o,a=n.document,l=!1,r(),a.onreadystatechange=function(){"complete"==a.readyState&&(a.onreadystatechange=null,c())})}function c(){l||(l=!0,s())}function r(){try{a.documentElement.doScroll("left")}catch(e){return void setTimeout(r,50)}c()}})(window);

View File

@ -0,0 +1,37 @@
{
"id": "4906634",
"name": "startMessage",
"font_family": "startMessage",
"css_prefix_text": "startMessage",
"description": "",
"glyphs": [
{
"icon_id": "34452976",
"name": "error-fill",
"font_class": "error-fill",
"unicode": "e839",
"unicode_decimal": 59449
},
{
"icon_id": "34453007",
"name": "info-fill",
"font_class": "info-fill",
"unicode": "e83d",
"unicode_decimal": 59453
},
{
"icon_id": "34453063",
"name": "success-fill",
"font_class": "success-fill",
"unicode": "e842",
"unicode_decimal": 59458
},
{
"icon_id": "34453306",
"name": "user-defined-fill",
"font_class": "user-defined-fill",
"unicode": "e871",
"unicode_decimal": 59505
}
]
}

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -27,7 +27,7 @@
align-items: center;
.logoContent{
position: relative;
padding: 5px 20px;
padding: 2.5px 20px;
& > div{
position: relative;
}

View File

@ -3,7 +3,7 @@
</script>
<template>
<div>
<div data-prefix="Ni">
<slot/>
<div id="NiBaseNotificationContainer"></div>
</div>

View File

@ -4,7 +4,7 @@ const props = defineProps({
//
type: String,
default: 'default',
validator: (value: string) => ['default', 'tertiary', 'primary', 'info', 'success', 'warning', 'error'].includes(value)
validator: (value: string) => ['default', 'primary', 'info', 'success', 'warning', 'error'].includes(value)
},
strong: {
//
@ -34,7 +34,13 @@ const props = defineProps({
default: false,
}
})
consola.info(toValue(props))
const emit = defineEmits(['click']);
const handleClick = () => {
if (!props.disabled && !props.loading) {
emit('click');
}
};
const typeS = ref(props.type);
const strong = ref(props.strong);
const grade = ref(props.grade);
@ -57,6 +63,7 @@ const NiButtonClass = computed(() => {
grade.value == 'light' ? 'light' : '',
grade.value == 'halfTransparent' ? 'halfTransparent' : '',
grade.value == 'transparent' ? 'transparent' : '',
typeS.value == 'default' ? 'defaultType' : '',
].join(' ');
})
const style = computed(() => {
@ -86,10 +93,11 @@ const style = computed(() => {
})
</script>
<template>
<template data-prefix="Ni">
<div class="NiButton"
:style
:class="NiButtonClass"
@click="handleClick"
>
<slot name="icons"/>
<span v-if="loading" class="sxIconFont" :class="loading && 'loading'">&#xe687;</span>
@ -118,10 +126,10 @@ const style = computed(() => {
align-items: center;
background-color: var(--NiButton-bg-default);
color: var(--NiButton-color-default);
border: 1px solid var(--NiButton-bg-default);
padding: var(--Ni-button-padding);
width: fit-content;
height: var(--Ni-button-height);
border: 1px solid var(--NiButton-bg-default);
cursor: pointer;
transition: color .3s ease-in-out, background-color .3s ease-in-out, border-color .3s ease-in-out, width .3s ease-in-out;
//height: var(--Ni-button-height);
@ -139,6 +147,21 @@ const style = computed(() => {
border: 1px solid var(--NiButton-bg-click);
}
}
.NiButton.defaultType{
background-color: rgba(0, 0, 0, 0);
color: var(--Ni-theme-color-T1);
border: 1px solid var(--Ni-theme-border-color);
&:hover {
background-color: rgba(0, 0, 0, 0);
color: var(--Ni-theme-color-T2);
border: 1px solid var(--Ni-theme-border-color-hover);
}
&:active {
background-color: rgba(0, 0, 0, 0);
color: var(--Ni-theme-color-T0);
border: 1px solid var(--Ni-theme-color-T0);
}
}
.NiButton.disabled{
opacity: var(--Ni-button-disable-opacity);
cursor: not-allowed;

23
components/Ni/Form.vue Normal file
View File

@ -0,0 +1,23 @@
<script setup lang="ts">
const props = defineProps({
data: {
//
type: Object,
required: true
},
rules:{
//
type: Object,
}
})
</script>
<template>
<div class="NiForm" data-prefix="Ni">
<slot></slot>
</div>
</template>
<style scoped>
</style>

View File

@ -0,0 +1,69 @@
<script setup lang="ts">
const props = defineProps({
label: {
type: String,
required: true,
},
name:{
type: String,
default: '',
},
labelWidth: {
type: String,
default: '8',
}
})
const itemLabel = ref('')
const itemName = ref('')
const itemLabelWidth = ref('8')
watchEffect(() => {
itemLabel.value = props.label
itemName.value = props.name
itemLabelWidth.value = props.labelWidth
})
</script>
<template>
<div class="NiFormItem" data-prefix="Ni">
<div class="content">
<div class="formLabel" :style="{width: itemLabelWidth+'em'}">
<span>{{itemLabel}}</span>
</div>
<div class="formValue"><slot/></div>
</div>
<div class="feedback"></div>
</div>
</template>
<style scoped lang="scss">
.NiFormItem{
position: relative;
height: 3.6rem;
padding: .5rem 0;
& > div.content{
position: relative;
display: flex;
height: 2.4rem;
&>div.formLabel{
position: relative;
flex-shrink: 0;
font-weight: bold;
color: var(--Ni-theme-color);
height: 100%;
display: flex;
align-items: center;
}
&>div.formValue{
position: relative;
flex: 1;
}
}
& > div.feedback{
position: relative;
height: 1.2rem;
}
}
</style>

118
components/Ni/Input.vue Normal file
View File

@ -0,0 +1,118 @@
<script setup lang="ts">
const props = defineProps({
modelValue: { // v-model
type: [String, Number],
default: ''
},
clear: {
type: Boolean,
default: false
},
type: {
//
type: String,
default: 'text',
validator: (value: string) => ['text', 'password'].includes(value)
},
disabled: {
//
type: Boolean,
default: false,
},
showPasswordOn: {
//
type: String,
default: 'click',
validator: (value: string) => ['click', 'mouseDown'].includes(value)
},
})
const emit = defineEmits([
'update:modelValue', // v-model
'focus', //
'blur' //
])
const inputRef = ref(null)
const clear = ref(props.clear)
const type = ref(props.type)
const disabled = ref(props.disabled)
const showPasswordOn = ref(props.showPasswordOn)
watchEffect(() => {
clear.value = props.clear;
type.value = props.type;
disabled.value = props.disabled;
showPasswordOn.value = props.showPasswordOn
})
//
const handleInput = (event) => {
emit('update:modelValue', event.target.value)
}
//
const handleFocus = (event) => {
emit('focus', event)
}
//
const handleBlur = (event) => {
emit('blur', event)
}
//
defineExpose({
focus: () => inputRef.value.focus(),
blur: () => inputRef.value.blur()
})
</script>
<template>
<div class="NiInput" data-prefix="Ni">
<div class="before">
<slot name="before"></slot>
</div>
<div class="inputMain">
<!-- 透传所有原生属性 v-bind="$attrs" -->
<input type="text" ref="inputRef"
class="ni-input"
:value="modelValue"
v-bind="$attrs"
@input="handleInput"
@focus="handleFocus"
@blur="handleBlur"></div>
<div class="show-password"></div>
<div class="after">
<slot name="after"></slot>
</div>
</div>
</template>
<style scoped lang="scss">
.NiInput {
position: relative;
height: 100%;
& > div.inputMain {
position: relative;
width: 100%;
height: 100%;
display: flex;
align-items: center;
& > input {
height: 2rem;
width: 100%;
outline: none;
border: 1px solid var(--Ni-theme-border-color);
padding: .2rem .5rem;
border-radius: var(--Ni-border-radius);
box-sizing: border-box;
transition: border 0.3s ease-in-out;
font-size: 1rem;
font-family: v-sans, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol";
&:hover {
border-color: var(--Ni-theme-border-color-hover);
}
}
}
}
</style>

129
components/Ni/Message.vue Normal file
View File

@ -0,0 +1,129 @@
<!-- components/MessageContainer.vue -->
<script setup>
const {messages} = useNiMessage()
</script>
<template>
<teleport to="#teleports">
<TransitionGroup name="niMessage" tag="div" class="NiMessage" data-prefix="Ni">
<div class="niMessageContent" v-for="msg in messages" :key="msg.id" @mouseenter="msg.pause()"
@mouseleave="msg.resume()">
<div class="niMessageIcon">
<div v-if="msg.messageType == 'log'" class="startMessageIconFont messageLog">
<NiLogo/>
</div>
<div v-else-if="msg.messageType == 'info'" class="startMessageIconFont messageInfo">&#xe871;</div>
<div v-else-if="msg.messageType == 'success'" class="startMessageIconFont messageSuccess">&#xe842;
</div>
<div v-else-if="msg.messageType == 'warning'" class="startMessageIconFont messageWarning">&#xe83d;
</div>
<div v-else-if="msg.messageType == 'error'" class="startMessageIconFont messageError">&#xe839;</div>
</div>
<div class="niMessageBody">
{{ msg.content }}
</div>
</div>
</TransitionGroup>
</teleport>
</template>
<style scoped lang="scss">
//
.niMessage-enter-active, .niMessage-leave-active {
transition: all .5s ease-in-out;
}
.niMessage-enter-from, .niMessage-leave-to {
transform: translateY(-100%);
opacity: 0;
}
.NiMessage {
position: fixed;
top: 20px;
left: 50%;
transform: translateX(-50%);
display: flex;
flex-direction: column; /* 新消息在上方 */
gap: 10px;
z-index: 9999;
pointer-events: none; /* 容器不阻挡点击 */
}
.niMessageContent {
position: relative;
display: flex;
align-items: center;
padding: var(--Ni-content-padding);
transition: all 0.3s;
box-shadow: 0 2px 12px rgba(0, 0, 0, 0.15);
border-radius: var(--Ni-border-radius);
background: #fefefe;
cursor: pointer;
letter-spacing: 1px;
pointer-events: auto; /* 单个消息可交互 */
max-width: min(1000px, 80vw);
& > div.niMessageIcon {
position: relative;
flex-shrink: 0;
margin-right: .8rem;
& > div {
font-size: 1.6rem;
}
& > div.messageLog {
font-size: 6px;
}
& > div.messageInfo {
color: var(--Ni-button-info-bg-default);
}
& > div.messageSuccess {
color: var(--Ni-button-success-bg-default);
}
& > div.messageWarning {
color: var(--Ni-button-warning-bg-default);
}
& > div.messageError {
color: var(--Ni-button-error-bg-default);
}
}
& > div.niMessageBody {
position: relative;
display: flex;
align-items: center;
}
}
/* 入场动画 */
.nimessage-enter-active {
animation: slideIn 0.3s ease-out;
}
/* 离场动画 */
.nimessage-leave-active {
transition: opacity 0.3s ease-out;
position: absolute;
}
.nimessage-leave-to {
opacity: 0;
}
@keyframes slideIn {
from {
transform: translateY(-100%);
opacity: 0;
}
to {
transform: translateY(0);
opacity: 1;
}
}
</style>

View File

@ -80,7 +80,7 @@ onMounted(() => {
<template>
<teleport to="#teleports">
<Transition name="NiPopup">
<Transition name="NiPopup" data-prefix="Ni">
<div
v-if="visible"
ref="NiPopup"
@ -93,7 +93,7 @@ onMounted(() => {
<div ref="NiPopupContainer" :style="{ '--width': width + 'px' }" class="NiPopupContainer">
<header>
<div class="logo"><NiLogo/></div>
<div class="title"><slot name="title">标题</slot></div>
<div class="title"><slot name="header">标题</slot></div>
<div class="closeBar"></div>
</header>
<div class="headerLine"></div>
@ -104,7 +104,9 @@ onMounted(() => {
</main>
<footer>
<div class="button">
<div><slot name="footer"/></div>
<NiSpace>
<slot name="footer"/>
</NiSpace>
</div>
</footer>
</div>
@ -138,7 +140,6 @@ onMounted(() => {
position: relative;
will-change: transform; /* 提前告知浏览器会变化的属性 will-change: transform, opacity; 优化性能*/
--width: 600px;
aspect-ratio: 1/0.618;
max-width: 80vw;
max-height: 80vh;
width: var(--width);
@ -149,6 +150,18 @@ onMounted(() => {
background-color: var(--Ni-content-bg-color);
display: flex;
flex-direction: column;
@media (min-width: 768px){
&{
aspect-ratio: 1/0.618;
}
}
@media (max-width: 767px){
&{
height: auto;
}
}
}
//
.NiPopupContainer>header{
@ -184,6 +197,7 @@ onMounted(() => {
user-select: none;
color: var(--Ni-theme-color);
margin-left: .7rem;
font-size: 1.2rem;
&:before{
content: ' ';
@ -238,4 +252,5 @@ onMounted(() => {
animation-direction: reverse;/* 反向播放时 */
}
</style>

28
components/Ni/Space.vue Normal file
View File

@ -0,0 +1,28 @@
<script setup lang="ts">
const props = defineProps({
gap: {
type: String,
default: ''
}
})
const gapWith = ref('1');
watch(() => props.gap, (value) => {
gapWith.value = value
})
</script>
<template>
<div class="NiSpace" data-prefix="Ni" :style="{ '--width': gapWith + 'rem' }" >
<slot/>
</div>
</template>
<style scoped lang="scss">
.NiSpace {
position: relative;
--NiSpace-gap: 1rem;
display: flex;
flex-wrap: wrap;
gap: var(--NiSpace-gap) /* 行和列之间的间距 */
}
</style>

View File

@ -0,0 +1,50 @@
// composables/useMessage.ts
interface Message {
id: number
content: string
messageType: string
close: () => void
pause: () => void
resume: () => void
}
const messages = reactive<Message[]>([])
export const useNiMessage = () => {
const createMessage = (content: string, messageType: string) => {
const id = Date.now()
let timeout: NodeJS.Timeout | null = null
const close = () => {
const index = messages.findIndex(msg => msg.id === id)
if (index > -1) messages.splice(index, 1)
}
const startTimer = () => {
timeout = setTimeout(close, 3000)
}
const message: Message = {
id,
content,
messageType,
close,
pause: () => timeout && clearTimeout(timeout),
resume: () => startTimer()
}
messages.unshift(message) // 新消息显示在最上方
startTimer()
return message
}
return {
messages,
log: (data: string) => createMessage(data, 'log'),
info: (data: string) => createMessage(data, 'info'),
warning: (data: string) => createMessage(data, 'warning'),
error: (data: string) => createMessage(data, 'error'),
success: (data: string) => createMessage(data, 'success'),
}
}

View File

@ -2,146 +2,154 @@
// 每次热更新都会执行defineNuxtConfig
import {exec} from "node:child_process";
import open, {apps} from "open";
import type {AddressInfo} from "node:net";
export default defineNuxtConfig({
compatibilityDate: '2024-11-01',
devtools: { enabled: true },
modules: ['@nuxt/eslint'],
devServer: {
host: '0.0.0.0',
port: 3000,
},
features: {
devLogs: 'silent' // 这是默认值,确保它没有被设置为 true日志区分server和client false为不在client打印
},
css: [
'~/assets/css/style.css',
'~/assets/css/font.css',
'~/assets/css/value.css',
'~/assets/css/iconfont.css',
'~/assets/css/transitions.css',
'~/assets/css/Ni.css',
],
app:{
head:{
link: [
{
// LXGW WenKai 落霞孤鹜
rel: "stylesheet",
href: 'https://chinese-fonts-cdn.deno.dev/packages/lxgwwenkai/dist/LXGWWenKai-Regular/result.css'
vite: {
esbuild: {
target: 'esnext' // 或 'es2020'
},
{
// Huiwen-mincho 汇文明朝体
rel: "stylesheet",
href: 'https://chinese-fonts-cdn.deno.dev/packages/hwmct/dist/%E6%B1%87%E6%96%87%E6%98%8E%E6%9C%9D%E4%BD%93/result.css'
// article {
// font-family:'Huiwen-mincho';
// font-weight:'400'
// };
optimizeDeps: {
esbuildOptions: {
target: 'esnext'
}
}
},
compatibilityDate: '2024-11-01',
devtools: {enabled: true},
modules: ['@nuxt/eslint'],
devServer: {
host: '0.0.0.0',
port: 3000,
},
features: {
devLogs: 'silent' // 这是默认值,确保它没有被设置为 true日志区分server和client false为不在client打印
},
css: [
'~/assets/css/style.css',
'~/assets/css/font.css',
'~/assets/css/value.css',
'~/assets/css/iconfont.css',
'~/assets/css/transitions.css',
'~/assets/css/Ni.css',
],
app: {
head: {
link: [
{
// LXGW WenKai 落霞孤鹜
rel: "stylesheet",
href: 'https://chinese-fonts-cdn.deno.dev/packages/lxgwwenkai/dist/LXGWWenKai-Regular/result.css'
},
{
// Huiwen-mincho 汇文明朝体
rel: "stylesheet",
href: 'https://chinese-fonts-cdn.deno.dev/packages/hwmct/dist/%E6%B1%87%E6%96%87%E6%98%8E%E6%9C%9D%E4%BD%93/result.css'
// article {
// font-family:'Huiwen-mincho';
// font-weight:'400'
// };
},
]
},
layoutTransition: {
name: 'indexFade',
mode: 'out-in',
type: 'transition', // 明确指定动画类型
duration: {
enter: 300,
leave: 300
},
appear: true
},
]
},
layoutTransition:{
name: 'indexFade',
mode: 'out-in',
type: 'transition', // 明确指定动画类型
duration: {
enter: 300,
leave: 300
},
appear: true
hooks: {
'listen': (server, listener) => {
const {address, url} = listener
startBroswer(address.address, url)
},
},
},
hooks: {
'listen': (server) => {
startBroswer(server.address())
runtimeConfig: {
redis: {
host: process.env.REDIS_HOST || '127.0.0.1',
port: Number(process.env.REDIS_PORT) || 6379,
connectName: 'star-writ',
database: Number(process.env.REDIS_DB) || 9,
username: 'default',
password: process.env.REDIS_PASSWORD || 'Hxl1314521',
},
mysql: {
host: process.env.DB_HOST || '127.0.0.1',
port: Number(process.env.DB_PORT) || 3306,
user: process.env.DB_USER || 'root',
password: process.env.DB_PASSWORD,
database: process.env.DB_NAME || 'yuheng',
// ssl:
// process.env.NODE_ENV === 'production'
// ? {
// rejectUnauthorized: false,
// servername: '', // 明确置空servername参数
// }
// : null,
},
jwt: {
secret: process.env.JWT_SECRET || 'Hxl1314521',
accessExpiresIn: process.env.JWT_ACCESS_EXPIRES || '20m',
refreshExpiresIn: process.env.JWT_REFRESH_EXPIRES || '14d',
whitelist: [
'/user/login',
'/user/register',
'/user/refreshToken',
'/',
// '/module',
'/docs*',
'/docs/json'
],
},
defaultToken: {
username: process.env.USERNAME || 'expressgy',
nickname: process.env.PASSWORD || 'Nie',
userId: '1'
}
},
},
runtimeConfig: {
redis: {
host: process.env.REDIS_HOST || '127.0.0.1',
port: Number(process.env.REDIS_PORT) || 6379,
connectName: 'star-writ',
database: Number(process.env.REDIS_DB) || 9,
username: 'default',
password: process.env.REDIS_PASSWORD || 'Hxl1314521',
},
mysql:{
host: process.env.DB_HOST || '127.0.0.1',
port: Number(process.env.DB_PORT) || 3306,
user: process.env.DB_USER || 'root',
password: process.env.DB_PASSWORD,
database: process.env.DB_NAME || 'yuheng',
// ssl:
// process.env.NODE_ENV === 'production'
// ? {
// rejectUnauthorized: false,
// servername: '', // 明确置空servername参数
// }
// : null,
},
jwt: {
secret: process.env.JWT_SECRET || 'Hxl1314521',
accessExpiresIn: process.env.JWT_ACCESS_EXPIRES || '20m',
refreshExpiresIn: process.env.JWT_REFRESH_EXPIRES || '14d',
whitelist: [
'/user/login',
'/user/register',
'/user/refreshToken',
'/',
// '/module',
'/docs*',
'/docs/json'
],
},
defaultToken: {
username: process.env.USERNAME || 'expressgy',
nickname: process.env.PASSWORD || 'Nie',
userId: '1'
}
},
})
// 首次启动浏览器
async function startBroswer(address: AddressInfo | string | null) {
if(!process.env.START){
if (address && typeof address !== 'string') {
console.log('Hooks: Listen')
const host = address.address === '::' ? 'localhost' : address.address
const port = address.port
const url = `http://${host}:${port}`;
try {
await open(url, {
app: {
name: apps.chrome,
arguments: [
'--remote-debugging-port=9222',
'--auto-open-devtools-for-tabs'
]
}
});
} catch (e) {
console.error((e as Error).message)
switch (process.platform) {
case 'win32':
console.log('Windows 系统');
exec(`start ${url}`)
break;
case 'darwin':
console.log('macOS 系统');
exec(`open ${url}`)
break;
case 'linux':
console.log('Linux 系统');
exec(`xdg-open ${url}`)
break;
default:
console.log('其他系统:', process.platform);
async function startBroswer(address: string, url: string) {
if (!process.env.START) {
const host = address === '::' ? 'localhost' : address
const port = url.split(':')[2]
const rrealUrl = `http://${host}:${port}`;
console.info(`Listening on ${rrealUrl}`);
try {
await open(rrealUrl, {
app: {
name: apps.chrome,
arguments: [
'--remote-debugging-port=9222',
'--auto-open-devtools-for-tabs'
]
}
});
} catch (e) {
console.error((e as Error).message)
switch (process.platform) {
case 'win32':
console.log('Windows 系统');
exec(`start ${url}`)
break;
case 'darwin':
console.log('macOS 系统');
exec(`open ${url}`)
break;
case 'linux':
console.log('Linux 系统');
exec(`xdg-open ${url}`)
break;
default:
console.log('其他系统:', process.platform);
}
}
}
process.env.START = String(true)
process.env.START = String(true)
}
}
}

3
package-lock.json generated
View File

@ -16,6 +16,7 @@
"mysql2": "^3.14.0",
"nuxt": "^3.16.2",
"redis": "^4.7.0",
"rfdc": "^1.4.1",
"vue": "^3.5.13",
"vue-router": "^4.5.0",
"vueuc": "^0.4.64"
@ -14054,7 +14055,7 @@
},
"node_modules/rfdc": {
"version": "1.4.1",
"resolved": "https://registry.npmmirror.com/rfdc/-/rfdc-1.4.1.tgz",
"resolved": "https://registry.npmjs.org/rfdc/-/rfdc-1.4.1.tgz",
"integrity": "sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA==",
"license": "MIT"
},

View File

@ -23,6 +23,7 @@
"mysql2": "^3.14.0",
"nuxt": "^3.16.2",
"redis": "^4.7.0",
"rfdc": "^1.4.1",
"vue": "^3.5.13",
"vue-router": "^4.5.0",
"vueuc": "^0.4.64"

View File

@ -1,4 +1,9 @@
<script setup lang="ts">
import rfdc from "rfdc";
import type {blogMenu} from "~/drizzle/schema";
const clone = rfdc()
const niMessage = useNiMessage()
definePageMeta({
layout: 'home',
pageTransition: {
@ -6,39 +11,67 @@ definePageMeta({
mode: 'out-in',
},
})
//
type InsertBlogMenu = typeof blogMenu.$inferInsert;
//
const menuList = ref([]);
const originalBlogMenuLis = shallowRef([])
const blogMenuPidMap = ref(new Map)
const blogMenuList = ref<InsertBlogMenu[]>([]);
// blog
const blogMenuObject = ref<InsertBlogMenu>(null);
//
const menuCollapseStatus = ref(true)
// blog
const blogInfoPopupStatus = ref(false)
const blogInfoPopupStatus2 = ref(false)
const getMenuListFetch = async () => {
const { data, pending, error } = await useFetch('/api/blog/blogMenu');
if (error.value) {
consola.error('数据获取失败:', error.value);
//
const formatBlogMenuListToPidMap = (blogMenuList: Array<InsertBlogMenu>) => {
const list = clone(blogMenuList)
const pidObj: Record<string, InsertBlogMenu[]> = {};
for(const menuItem of list){
if(pidObj[menuItem.pid]){
pidObj[menuItem.pid].push(menuItem)
}else{
pidObj[menuItem.pid] = [menuItem]
}
}
menuList.value = data.value?.filter(i => i.pid === '0')
Object.keys(pidObj).forEach(key => {
blogMenuPidMap.value.set(key, pidObj[key]);
})
}
const sendMessage = async () => {
// nMessage.success('??')
// blog
const getMenuListFetch = async () => {
const { data, error } = await useFetch('/api/blog/blogMenu');
if (error.value) {
throw error.value;
}
originalBlogMenuLis.value = data.value;
formatBlogMenuListToPidMap(data.value);
blogMenuList.value = blogMenuPidMap.value.get('0')
}
const handleCloseBefore = (s) => {
consola.info('要关闭啦', s)
// blog
const handleCreateBlogMenuItemEvent = (pid: string) => {
blogMenuObject.value = {
pid,
name: '',
desc: ''
}
blogInfoPopupStatus.value = true;
}
//
const handleCreateBlogMenuItemAck = async () => {
consola.info(blogMenuObject.value)
const resd= await $fetch('/api/blog/blogMenu',{
method: 'POST',
body: blogMenuObject.value
});
consola.info(resd);
}
const loading = ref(false)
setTimeout(() => {
loading.value = true
}, 5000)
// SSR
await getMenuListFetch()
onMounted(() => {
consola.log(menuList.value)
consola.log(blogMenuList.value)
})
</script>
@ -48,16 +81,16 @@ onMounted(() => {
<div class="blogMenuContainer">
<header class="contentBox">
<div class="title">博客目录</div>
<div class="bar add star-blogIconFont awaitShow a11 allCenter">&#xe608;</div>
<div class="bar add star-blogIconFont awaitShow a11 allCenter" @click="handleCreateBlogMenuItemEvent('0')">&#xe608;</div>
<div class="bar star-blogIconFont awaitShow a11 allCenter" @click="menuCollapseStatus = !menuCollapseStatus">&#xe67f;</div>
</header>
<div class="line"/>
<div class="blogMenuContent">
<div class="contentBox blogMenuItem" v-for="(item, index) in menuList" :key="item.id">
<div class="contentBox blogMenuItem" v-for="(item, index) in blogMenuList" :key="item.id">
<div class="addIcon star-blogIconFont a11 allCenter">&#xe608;</div>
<div class="text oneLineOverMore">{{item.name}}</div>
<div class="text"><div class="oneLineOverMore">{{item.name}}</div></div>
<div class="barBox" :class="item.pid === '0' && 'awaistShows'">
<div class="star-blogIconFont bar a11 allCenter">&#xe608;</div>
<div class="star-blogIconFont bar a11 allCenter" @click="handleCreateBlogMenuItemEvent(item.pid)">&#xe608;</div>
<div class="star-blogIconFont bar a11 allCenter">&#xe73a;</div>
</div>
</div>
@ -72,101 +105,26 @@ onMounted(() => {
</div>
</header>
<main>
<div @click="blogInfoPopupStatus = !blogInfoPopupStatus">点击</div>
<div>默认</div>
<div>
<NiButton>默认按钮</NiButton>
<NiButton type="tertiary">tertiary按钮</NiButton>
<NiButton loading type="primary">primary按钮</NiButton>
<NiButton strong type="info">info按钮</NiButton>
<NiButton loading type="success">success按钮</NiButton>
<NiButton type="warning">warning按钮</NiButton>
<NiButton type="error">error按钮</NiButton>
</div>
<div>grade 虚线边框</div>
<div>
<NiButton>默认按钮</NiButton>
<NiButton grade="dotted" type="tertiary">tertiary按钮</NiButton>
<NiButton grade="dotted" loading type="primary">primary按钮</NiButton>
<NiButton grade="dotted" strong type="info">info按钮</NiButton>
<NiButton grade="dotted" loading type="success">success按钮</NiButton>
<NiButton grade="dotted" type="warning">warning按钮</NiButton>
<NiButton grade="dotted" type="error">error按钮</NiButton>
</div>
<div>grade 浅色</div>
<div>
<NiButton>默认按钮</NiButton>
<NiButton grade="light" type="tertiary">tertiary按钮</NiButton>
<NiButton grade="light" loading type="primary">primary按钮</NiButton>
<NiButton grade="light" strong type="info">info按钮</NiButton>
<NiButton grade="light" loading type="success">success按钮</NiButton>
<NiButton grade="light" type="warning">warning按钮</NiButton>
<NiButton grade="light" type="error">error按钮</NiButton>
</div>
<div>grade halfTransparent</div>
<div>
<NiButton>默认按钮</NiButton>
<NiButton grade="halfTransparent" type="tertiary">tertiary按钮</NiButton>
<NiButton grade="halfTransparent" loading type="primary">primary按钮</NiButton>
<NiButton grade="halfTransparent" strong type="info">info按钮</NiButton>
<NiButton grade="halfTransparent" loading type="success">success按钮</NiButton>
<NiButton grade="halfTransparent" type="warning">warning按钮</NiButton>
<NiButton grade="halfTransparent" type="error">error按钮</NiButton>
</div>
<div>grade Transparent</div>
<div>
<NiButton>默认按钮</NiButton>
<NiButton grade="transparent" type="tertiary">tertiary按钮</NiButton>
<NiButton grade="transparent" loading type="primary">primary按钮</NiButton>
<NiButton grade="transparent" strong type="info">info按钮</NiButton>
<NiButton grade="transparent" loading type="success">success按钮</NiButton>
<NiButton grade="transparent" type="warning">warning按钮</NiButton>
<NiButton grade="transparent" type="error">error按钮</NiButton>
</div>
<div>disable</div>
<div>
<NiButton>默认按钮</NiButton>
<NiButton disabled type="tertiary">tertiary按钮</NiButton>
<NiButton disabled type="primary">primary按钮</NiButton>
<NiButton loading disabled type="info">info按钮</NiButton>
<NiButton disabled type="success">success按钮</NiButton>
<NiButton disabled type="warning">warning按钮</NiButton>
<NiButton disabled type="error">error按钮</NiButton>
</div>
<div>半圆角</div>
<div>
<NiButton>默认按钮</NiButton>
<NiButton angle="round" type="tertiary">tertiary按钮</NiButton>
<NiButton angle="round" type="primary">primary按钮</NiButton>
<NiButton :loading="loading" angle="round" type="info">info按钮</NiButton>
<NiButton angle="round" type="success">success按钮</NiButton>
<NiButton angle="round" type="warning">warning按钮</NiButton>
<NiButton angle="round" type="error">error按钮</NiButton>
</div>
<div>圆角</div>
<div>
<NiButton>A</NiButton>
<NiButton angle="circle" type="tertiary">B</NiButton>
<NiButton angle="circle" type="primary">C</NiButton>
<NiButton :loading="loading" angle="circle" type="info">D</NiButton>
<NiButton loading angle="circle" type="success">E</NiButton>
<NiButton loading angle="circle" type="warning">F</NiButton>
<NiButton angle="circle" type="error">G</NiButton>
</div>
</main>
</div>
<div class="right"></div>
<NiPopup v-model:status="blogInfoPopupStatus" width="900" @handleClose="handleCloseBefore">
<div @click="blogInfoPopupStatus2 = true">关闭</div>
<NiPopup v-model:status="blogInfoPopupStatus">
<template #header>博客菜单</template>
<NiForm :data="blogMenuObject">
<NiFormItem label="博客名称" name="name" label-width="6">
<NiInput v-model="blogMenuObject.name"></NiInput>
</NiFormItem>
<NiFormItem label="博客描述" name="name" label-width="6">
<NiInput v-model="blogMenuObject.desc"></NiInput>
</NiFormItem>
<div>{{ blogMenuObject.name }}</div>
<div>{{ blogMenuObject.desc }}</div>
</NiForm>
<template #footer>
关闭
<NiButton @click="blogInfoPopupStatus = false">取消</NiButton>
<NiButton type="primary" @click="handleCreateBlogMenuItemAck">确认</NiButton>
</template>
</NiPopup>
<NiPopup v-model:status="blogInfoPopupStatus2">
<div @click="blogInfoPopupStatus2 = false">
</div>
</NiPopup>
</div>
</template>
@ -274,33 +232,36 @@ onMounted(() => {
position: relative;
font-size: 1rem;
display: flex;
& > div{
position: relative;
}
& > div.addIcon{
position: relative;
font-size: 1rem;
margin-right: .5rem;
}
& > div.text{
height: 100%;
position: relative;
flex: 1;
min-width: 0;//
display: flex;
align-items: center;
overflow: hidden;
}
& > div.barBox{
margin-left: .2rem;
height: 0;
width: 0;
opacity: 0;
overflow: hidden;
display: flex;
transition: opacity .3s;
& > div.bar{
opacity: 1;
}
}
&:hover div.barBox{
height: 100% !important;
width: auto !important;
opacity: 1 !important;
& > div.bar{
opacity: 1;
}
}
}
}

View File

@ -8,7 +8,6 @@ const leaveTime = ref(false)
//
onBeforeRouteLeave((to, from, next) => {
leaveTime.value = true
consola.error('leave')
setTimeout(() => {
next(true)
}, 1000)

View File

@ -1,11 +1,19 @@
export default defineEventHandler(async event => {
const requAuth = event.context.auth
const body = await readBody(event)
if(!requAuth.isTrue) {
// 判断为正常登录
throw createError({
import {BlogMenuDB} from "~/server/services/blog/blogMenu";
import consola from "consola";
})
}
return 'Hello blog/blogMenu.post'
export default defineEventHandler(async event => {
// 获取登录信息
const headerAuth = event.context.auth
// 获取参数
const body = await readBody(event)
// 初始化DB
body.id = event.context.getId()
const blogMenuDB = new BlogMenuDB(event)
const resd = await blogMenuDB.insertBlogMenu(body, headerAuth)
consola.info(body, resd)
// if(!requAuth.isTrue) {
// // 判断为正常登录
// }
return resd
})

View File

@ -2,10 +2,11 @@ import consola from "consola";
export default defineEventHandler(async event => {
// console.log(await event.context.redis.get('SI HI'))
consola.info('API')
consola.info('API TimeOut')
const result = await event.context.redis.get('SI HI');
return {
'SI HI': result
}
// consola.info('API')
// consola.info('API TimeOut')
// const result = await event.context.redis.get('SI HI');
// return {
// 'SI HI': result
// }
return 's'
})

View File

@ -2,6 +2,7 @@ import { createClient } from 'redis';
import consola from 'consola'
export default defineNitroPlugin(async (nitroApp) => {
return
const {redis: config} = useRuntimeConfig()
consola.info('Redis ...');
const redisConnect = createClient({
@ -17,8 +18,8 @@ export default defineNitroPlugin(async (nitroApp) => {
});
redisConnect.on('error', (err) => {
console.log(err)
consola.error('Redis error: ', err);
// console.log(err)
// consola.error('Redis error: ', err);
});
// 连接到 Redis

View File

@ -0,0 +1,22 @@
export default defineNitroPlugin(nitroApp => {
nitroApp.hooks.hook('request', async (event) => {
event.context.getId = generateEnhancedId
})
})
const EPOCH = 1609459200000;
let sequence = 0;
function generateEnhancedId(): string {
const now = Date.now();
const timestamp = now - EPOCH;
// 10 位序列号(支持 1024 个 ID/ms
sequence = (sequence + 1) % 0x400;
// 组合成 64 位 ID
const high = (timestamp << 14) | sequence;
const low = Math.floor(Math.random() * 0x100000000);
return ((BigInt(high) << 32n) | BigInt(low)).toString();
}

View File

@ -35,6 +35,6 @@ export class BlogMenuDB {
.from(blogMenu)
.where(
and(eq(blogMenu.createdBy, headerAuth.userId), eq(blogMenu.deleted, 0), headerAuth.isTrue ? undefined : eq(blogMenu.public, 1))
).orderBy(asc(blogMenu.sort), desc(blogMenu.createdAt));
).orderBy(asc(blogMenu.sort), asc(blogMenu.createdAt));
}
}