提交 bef97d31 编写于 作者: X xjh22222228

feat: #31

上级 7e745b39
......@@ -97,12 +97,56 @@ npm run build
## 更新数据
只需要关注根目录 `data` 文件夹, 如果你使用了上面教程提供的自动化部署服务,那么当更新数据后大概5分钟即可看到。
```js
{
title: '工具',
icon: 'https://example/favicon.ico',
nav: [
{
subtitle: '网站',
collapsed: false, // 默认展开,设置 false 折叠
showSideIcon: false, // 右侧边图标,如果设置Icon则默认显示
nav: [
{
name: '发现导航',
desc: '发现导航 - 精选实用导航网站',
link: 'https://nav3.cn',
}
]
}
]
}
```
## 配置
所有可配置位于文件 `config/index.ts`
## 图标
图标是支持继承的,每一级的 `icon` 字段都是可选,如果当前没有就会继承上一级Icon,
```js
{
title: 'Example',
icon: 'https://example/icon',
nav: [
{
showSideIcon: false, // 如果这层设置 icon 图标会默认在右侧边栏显示ICON, 设置 false 关闭显示
subtitle: 'Example',
// icon: 'https://favicon.ico',
nav: [
{
// icon: 'https://favicon.ico',
},
]
}
]
}
```
......
......@@ -4,13 +4,13 @@
* @url https://github.com/xjh22222228/nav
*/
export const appLanguage = [
export const INDEX_LANGUAGE = [
'英文',
'中文',
'GitHub'
]
export const webpLanguage = [
export const APP_LANGUAGE = [
'EN',
'CN',
'Git'
......@@ -19,6 +19,10 @@ export const webpLanguage = [
// Git 仓库地址, 没有填空字符串
export const GIT_REPO_URL = 'https://github.com/xjh22222228/nav'
// 错误图标, 图标地址访问不了时显示
// 建议使用网络地址,放在您任何服务器上, 减少入侵
export const ERROR_ICON = 'assets/img/transparent.gif'
// 网站底部描述, 可以是 HTML
// 可以是版权信息,备案号
export const FOOTER_DESC = ''
......@@ -35,7 +39,6 @@ export const BACKGROUND_LINEAR = [
'linear-gradient(19deg, #FAACA8 0%, #DDD6F3 100%)',
'linear-gradient(147deg, #FFE53B 0%, #FF2525 74%)',
'linear-gradient(180deg, #52ACFF 25%, #FFE32C 100%)',
'linear-gradient(180deg, #FFFFFF 0%, #6284FF 50%, #FF0000 100%)',
'linear-gradient(225deg, #FF3CAC 0%, #784BA0 50%, #2B86C5 100%)',
'linear-gradient(0deg, #D9AFD9 0%, #97D9E1 100%)',
'linear-gradient(90deg, #00DBDE 0%, #FC00FF 100%)',
......@@ -50,8 +53,9 @@ export const BACKGROUND_LINEAR = [
'linear-gradient(43deg, #4158D0 0%, #C850C0 46%, #FFCC70 100%)',
'linear-gradient(135deg, #8BC6EC 0%, #9599E2 100%)',
'linear-gradient(180deg, #A9C9FF 0%, #FFBBEC 100%)',
'linear-gradient(45deg, #FBDA61 0%, #FF5ACD 100%)',
'linear-gradient(45deg, #FA8BFF 0%, #2BD2FF 52%, #2BFF88 90%)',
'linear-gradient(90deg, #74EBD5 0%, #9FACE6 100%)',
'linear-gradient(160deg, #0093E9 0%, #80D0C7 100%)'
'linear-gradient(160deg, #0093E9 0%, #80D0C7 100%)',
'linear-gradient(132deg, #F4D03F 0%, #16A085 100%)',
'linear-gradient(62deg, #FBAB7E 0%, #F7CE68 100%)',
'linear-gradient(45deg, #85FFBD 0%, #FFFB7D 100%)'
]
const DEFAULT_ICON = 'assets/icon/frontend/076.png'
export default {
title: '生态系统',
icon: DEFAULT_ICON,
nav: [
{
icon: DEFAULT_ICON,
subtitle: '官方',
nav: [
{
icon: 'assets/icon/frontend/076.png',
name: 'Node.js',
desc: 'Node.js® 是一个基于 Chrome V8 引擎 的 JavaScript 运行时',
link: 'http://nodejs.cn',
......@@ -16,7 +19,6 @@ export default {
]
},
{
icon: 'assets/icon/frontend/077.png',
name: 'npm',
desc: 'npm是JavaScript世界的包管理工具,并且是 Node.js 平台的默认包管理工具',
link: 'https://www.npmjs.com/',
......@@ -32,19 +34,16 @@ export default {
subtitle: '模块',
nav: [
{
icon: 'assets/icon/frontend/110.png',
name: 'urllib',
desc: '在复杂的世界中请求HTTP(s)URL',
link: 'https://github.com/node-modules/urllib',
},
{
icon: 'assets/icon/frontend/110.png',
name: 'formstream',
desc: 'multipart / form-data编码流,用于文件上传的帮助程序。',
link: 'https://github.com/node-modules/formstream',
},
{
icon: 'assets/icon/frontend/110.png',
name: 'validate-npm-package-name',
desc: '给我一个字符串,我会告诉你它是否是有效的npm软件包名称',
link: 'https://github.com/npm/validate-npm-package-name',
......@@ -118,4 +117,4 @@ export default {
]
}
]
}
\ No newline at end of file
}
......@@ -2,7 +2,7 @@ export default {
title: 'GUI软件',
nav: [
{
subtitle: '',
subtitle: 'GUI',
nav: [
{
icon: 'https://typora.io/img/favicon-48.png',
......
......@@ -42,6 +42,6 @@ export class AppComponent {
script.src = TONGJI_URL
script.id = 'tongji_url'
script.async = true
document.documentElement.appendChild(script)
document.head.appendChild(script)
}
}
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1605324128195" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="2831" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><defs><style type="text/css"></style></defs><path d="M927.804 352.193l-415.804 415.632-415.803-415.632 63.616-63.445 352.209 352.017 352.102-352.017z" p-id="2832" fill="#515151"></path></svg>
\ No newline at end of file
......@@ -102,6 +102,9 @@ em {
width: 1000px;
margin: 0 auto;
}
.cursor-pointer {
cursor: pointer;
}
/* 搜索高亮关键字 */
b {
......
import nav from '../../data'
import WEBSITE_LIST from '../../data'
import qs from 'qs'
import { BACKGROUND_LINEAR } from '../../config'
import { BACKGROUND_LINEAR, ERROR_ICON } from '../../config'
export function debounce(func, wait, immediate) {
let timeout
......@@ -81,7 +81,7 @@ export function totalWeb(): number {
}
}
}
r(nav)
r(WEBSITE_LIST)
return total
}
......@@ -105,7 +105,7 @@ export function randomBgImg() {
}
export function onImgError(e: any) {
e.target.src = 'assets/img/transparent.gif'
e.target.src = ERROR_ICON
}
export function queryString() {
......@@ -115,15 +115,15 @@ export function queryString() {
let id = parseInt(parseQs.id) || 0
let page = parseInt(parseQs.page) || 0
if (page > nav.length - 1) {
page = nav.length - 1;
if (page > WEBSITE_LIST.length - 1) {
page = WEBSITE_LIST.length - 1;
id = 0;
} else {
page = page;
if (id <= nav[page].nav.length - 1) {
if (id <= WEBSITE_LIST[page].nav.length - 1) {
id = id;
} else {
id = nav[page].nav.length - 1;
id = WEBSITE_LIST[page].nav.length - 1;
}
}
......@@ -134,3 +134,33 @@ export function queryString() {
page
}
}
export function getWebsiteList() {
let webSiteList = WEBSITE_LIST
const scriptElAll = document.querySelectorAll('script')
const scriptUrl = scriptElAll[scriptElAll.length - 1].src
const storageScriptUrl = window.localStorage.getItem('s_url')
// 更新数据
if (storageScriptUrl !== scriptUrl) {
window.localStorage.clear()
window.localStorage.setItem('s_url', scriptUrl)
return webSiteList
}
try {
const w = window.localStorage.getItem('website')
const json = JSON.parse(w)
if (Array.isArray(json)) {
webSiteList = json
}
} catch {}
return webSiteList
}
export function setWebsiteList(v) {
if (!v) return
window.localStorage.setItem('website', JSON.stringify(v))
}
import nav from '../../../../data';
import { Component } from '@angular/core';
import { Router, ActivatedRoute } from '@angular/router';
import { webpLanguage } from '../../../../config';
import { APP_LANGUAGE } from '../../../../config';
import { onImgError, queryString } from '../../../utils';
@Component({
......@@ -16,7 +16,7 @@ export default class WebpComponent {
id: number = 0;
page: number = 0;
open: boolean = false;
language: string[] = webpLanguage;
language: string[] = APP_LANGUAGE;
ngOnInit () {
this.activatedRoute.queryParams.subscribe(() => {
......
......@@ -10,21 +10,23 @@
*ngIf="showInput"
(blur)="showInput = false"
/>
<nav class="top-nav user-select-none">
<a
*ngFor="let item of nav; let i = index;"
*ngFor="let item of websiteList; let i = index;"
(click)="handleCilckNav(i)"
[class.active]="page === i"
class="ripple-btn">
{{ item.title }}
</a>
</nav>
<section class="index-section user-select-none">
<aside class="sidebar" id="sidebar">
<ul>
<li
class="tag"
*ngFor="let item of nav[page].nav; let i = index"
*ngFor="let item of websiteList[page].nav; let i = index"
[class.active]="id === i"
(click)="handleSidebarNav(i)">
<a class="ripple-btn" *ngIf="item.title">{{ item.title }}</a>
......@@ -36,15 +38,32 @@
</a>
</div>
</aside>
<div class="main" id="main" (scroll)="handleScroll($event)">
<app-loading *ngIf="searchLoading"></app-loading>
<ul *ngIf="list.length && list[0].nav">
<li *ngFor="let item of list">
<ul *ngIf="currentList.length && currentList[0].nav">
<li *ngFor="let item of currentList; let i=index">
<div class="title-wrapper" *ngIf="item.subtitle">
<h2 class="block-title">
{{ item.subtitle }} x {{ item.nav.length }}
<span
(click)="onCollapse(item, i)"
class="cursor-pointer"
>
{{ item.subtitle }} x {{ item.nav.length }}
</span>
<img
src="assets/img/down-arrow.svg"
class="down-arrow"
[class.active]="item.collapsed"
draggable="false"
alt=""
(click)="onCollapse(item, i)"
/>
<a
*ngIf="GIT_REPO_URL"
[href]="GIT_REPO_URL + '/tree/master/data'"
class="edit"
target="_blank"
......@@ -53,14 +72,22 @@
>
</a>
</h2>
<img
*ngIf="item.icon && item.showSideIcon !== false"
[src]="item.icon"
class="side-logo"
alt=""
/>
</div>
<div class="row">
<div class="row" [class.hide]="item.collapsed">
<div class="click-btn" *ngFor="let el of item.nav">
<a [href]="el.link" target="_blank" rel="noopener noreferer">
<div class="top">
<img
*ngIf="el.icon || item.icon; else icon"
[src]="el.icon || item.icon"
*ngIf="el.icon || item.icon || websiteList[page].nav[id].icon; else icon"
[src]="el.icon || item.icon || websiteList[page].nav[id].icon"
alt=""
class="icon"
(error)="onImgError($event)"
......@@ -72,6 +99,7 @@
</div>
<div class="desc" [innerHtml]="el.desc"></div>
</a>
<div class="mark" *ngIf="el.language && el.language.length > 0">
<div class="button-box">
<a
......@@ -108,7 +136,10 @@
</li>
</ul>
<app-no-data *ngIf="list.length && list[0].nav && !list[0].nav.length"></app-no-data>
<app-no-data
*ngIf="currentList.length && currentList[0].nav && !currentList[0].nav.length"
>
</app-no-data>
</div>
</section>
</main>
......
......@@ -118,6 +118,12 @@
padding-bottom: 50px;
overflow-y: auto;
.side-logo {
width: 30px;
height: 30px;
border-radius: 50%;
}
.icon {
display: inline-block;
width: 35px;
......@@ -207,7 +213,9 @@
.title-wrapper {
border-bottom: 1px solid #ccc;
padding: 10px 0 10px 15px;
padding: 10px 30px 10px 15px;
display: flex;
justify-content: space-between;
&:hover {
.edit {
......@@ -216,16 +224,31 @@
}
}
.down-arrow {
width: 15px;
height: 15px;
align-self: center;
margin-left: 15px;
cursor: pointer;
transition: .1s linear;
&.active {
transform: rotate(-90deg);
}
}
.block-title {
display: inline-block;
position: relative;
color: #3F51B5;
align-self: center;
display: flex;
.edit {
display: none;
position: absolute;
top: -2px;
right: -25px;
right: -35px;
width: 15px;
height: 15px;
margin-left: 5px;
......@@ -242,8 +265,13 @@
.row {
display: flex;
-webkit-flex-wrap: wrap;
flex-wrap: wrap;
transition: .1s ease-out;
overflow: hidden;
&.hide {
display: none;
}
a {
color: #333;
......
import nav from '../../../../data'
import { Component } from '@angular/core'
import { Router, ActivatedRoute } from '@angular/router'
import { debounce, fuzzySearch, randomBgImg, onImgError, queryString } from '../../../utils'
import { appLanguage, GIT_REPO_URL } from '../../../../config'
import { INDEX_LANGUAGE, GIT_REPO_URL } from '../../../../config'
import { annotate } from 'rough-notation'
import {
debounce,
fuzzySearch,
randomBgImg,
onImgError,
queryString,
getWebsiteList,
setWebsiteList
} from '../../../utils'
let equeue = []
......@@ -16,21 +23,21 @@ export default class HomeComponent {
constructor (private router: Router, private activatedRoute: ActivatedRoute) {}
nav: any[] = nav
websiteList: any[] = getWebsiteList()
currentList: any[] = []
id: number = 0
page: number = 0
list: any[] = []
searchKeyword: string = ''
showInput = false
searchLoading = false
language: string[] = appLanguage
language: string[] = INDEX_LANGUAGE
GIT_REPO_URL: string = GIT_REPO_URL
ngOnInit () {
randomBgImg()
const initList = () => {
this.list = this.nav[this.page].nav[this.id].nav
this.currentList = this.websiteList[this.page].nav[this.id].nav
}
this.activatedRoute.queryParams.subscribe(() => {
......@@ -41,7 +48,7 @@ export default class HomeComponent {
this.id = id
if (q) {
this.list = fuzzySearch(this.nav, q)
this.currentList = fuzzySearch(this.websiteList, q)
this.searchLoading = false
} else {
initList()
......@@ -136,6 +143,12 @@ export default class HomeComponent {
annotation.show()
}
onCollapse = (item, index) => {
item.collapsed = !item.collapsed
this.websiteList[this.page].nav[this.id].nav[index] = item
setWebsiteList(this.websiteList)
}
handleSearch = null
onImgError = onImgError
}
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册