├── .DS_Store ├── .gitignore ├── .npmrc ├── .umirc.ts ├── LICENSE ├── README.md ├── package.json ├── pnpm-lock.yaml ├── src ├── .DS_Store ├── api │ └── index.ts ├── assets │ ├── .DS_Store │ ├── cx.png │ ├── dr.png │ ├── logo.png │ ├── preson.png │ ├── qt.png │ └── xx.jpeg ├── components │ └── Upload │ │ └── index.tsx ├── global.less ├── layouts │ ├── index.less │ └── index.tsx ├── pages │ ├── components │ │ └── CardList │ │ │ ├── index.less │ │ │ └── index.tsx │ ├── index.less │ ├── index.tsx │ ├── midPage.less │ ├── midPage.tsx │ ├── share.less │ └── share.tsx └── utils │ ├── config.ts │ └── req.ts ├── tsconfig.json └── typings.d.ts /.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MrXujiang/t-nav/835990006424a80b537986d16df5c11024f1f28e/.DS_Store -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /node_modules 2 | /.env.local 3 | /.umirc.local.ts 4 | /config/config.local.ts 5 | /src/.umi 6 | /src/.umi-production 7 | /src/.umi-test 8 | /dist 9 | .swc 10 | -------------------------------------------------------------------------------- /.npmrc: -------------------------------------------------------------------------------- 1 | registry=https://registry.npmmirror.com/ 2 | strict-peer-dependencies=false 3 | -------------------------------------------------------------------------------- /.umirc.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig } from "umi"; 2 | 3 | const isDev = process.env.NODE_ENV === 'development'; 4 | 5 | export default defineConfig({ 6 | routes: [ 7 | { path: "/h5", component: "share", layout: false }, 8 | { path: "/", component: "index" }, 9 | { path: "/midPage", component: "midPage" }, 10 | ], 11 | metas: [ 12 | { name: 'keywords', content: '橙讯点评,工具,软件分享,热门工具推荐,在线工具,APP精选' }, 13 | { name: 'description', content: '专注于分享前沿实用的互联网软件及工具' }, 14 | ], 15 | title: '橙讯点评', 16 | publicPath: '/tool/', 17 | npmClient: 'pnpm', 18 | outputPath: '../basic-services/static/tool', 19 | define: { 20 | // https://192.168.3.7:3000 21 | // https://192.168.31.105:3000 22 | serverApi: 'https://www.turntip.cn' 23 | // serverApi: isDev ? 'https://192.168.31.105:3000' : 'https://www.turntip.cn' 24 | }, 25 | scripts: [` 26 | var _hmt = _hmt || []; 27 | (function() { 28 | var hm = document.createElement("script"); 29 | hm.src = "https://hm.baidu.com/hm.js?f8591e98fdeee2e20d80b6f3cda54212"; 30 | var s = document.getElementsByTagName("script")[0]; 31 | s.parentNode.insertBefore(hm, s); 32 | })(); 33 | `] 34 | }); 35 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 徐小夕 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | ## t-nav 4 | 5 | Out-of-the-box open-source navigation project. 6 | 7 | ## Tech stack 8 | 9 | - react17+ 10 | - antd5.0+ 11 | - umi4.0+ 12 | - xijs 13 | - qrcode 14 | - weixin js sdk 15 | - pnpm 16 | 17 | ## run 18 | 19 | install pkg: 20 | ``` 21 | yarn 22 | ``` 23 | 24 | run local: 25 | 26 | ``` 27 | yarn start 28 | ``` 29 | 30 | config file need deal, like server config. 31 | 32 | ## Design philosophy 33 | 34 | Configuration takes precedence. 35 | 36 | ## Background services 37 | 38 | Since the backend is not a core service, you can choose the language you are familiar with, if you want to get the backend code, please contact the author. 39 | 40 | [xuxiaoxi](https://github.com/MrXujiang) 41 | 42 | ## Partner project 43 | 44 | * [h5-dooring - 零代码解决方案](https://github.com/MrXujiang/h5-Dooring) 45 | * [ react-cropper-pro - 轻量强大的图片上传/裁切/压缩组件](https://github.com/MrXujiang/react-cropper-pro) 46 | * [mitu-editor - 轻量级且强大的图片编辑器](https://github.com/H5-Dooring/mitu-editor) 47 | * [powerNice - 一款轻量级文档管理编辑器](http://h5.dooring.cn/powernice/views) 48 | * [rc-drag - 基于react的轻量级拖拽缩放组件](https://github.com/MrXujiang/rc-drag) 49 | 50 | 51 | 52 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "private": true, 3 | "author": "xujiang ", 4 | "scripts": { 5 | "dev": "umi dev", 6 | "build": "umi build", 7 | "postinstall": "umi setup", 8 | "setup": "umi setup", 9 | "start": "npm run dev" 10 | }, 11 | "dependencies": { 12 | "@ant-design/icons": "^5.1.4", 13 | "antd": "^5.7.2", 14 | "antd-img-crop": "^4.12.2", 15 | "axios": "^1.4.0", 16 | "qrcode.react": "^3.1.0", 17 | "umi": "^4.0.72", 18 | "weixin-js-sdk": "^1.6.0", 19 | "xijs": "^1.2.6" 20 | }, 21 | "devDependencies": { 22 | "@types/react": "^18.0.33", 23 | "@types/react-dom": "^18.0.11", 24 | "@umijs/plugins": "^4.0.72", 25 | "typescript": "^5.0.3" 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MrXujiang/t-nav/835990006424a80b537986d16df5c11024f1f28e/src/.DS_Store -------------------------------------------------------------------------------- /src/api/index.ts: -------------------------------------------------------------------------------- 1 | import req from '@/utils/req'; 2 | 3 | interface LoginApiType { 4 | name: string; 5 | pwd: string; 6 | } 7 | 8 | export const loginApi = (data: LoginApiType) => req.post('/login', data) 9 | 10 | interface ListApiType { 11 | type: string; 12 | sub_type: string; 13 | keyword: string; 14 | page: number; 15 | pagesize: number; 16 | } 17 | export const getListApi = (params: ListApiType) => req.get('/list', {params}) 18 | 19 | interface AddListApiType { 20 | title: string, 21 | desc: string, 22 | type: string, 23 | sub_type: string, 24 | tags: string[], 25 | star: number, 26 | img: any[], 27 | url: string 28 | } 29 | 30 | export const addListApi = (data: AddListApiType) => req.post('/add', data) 31 | 32 | interface ModListApiType { 33 | id: string; 34 | title: string, 35 | desc: string, 36 | type: string, 37 | sub_type: string, 38 | tags: string[], 39 | star: number, 40 | img: any[], 41 | url: string 42 | } 43 | 44 | export const modListApi = (data: ModListApiType) => req.put('/mod', data) 45 | 46 | export const delListApi = (id: string) => req.delete(`/del?id=${id}`) 47 | 48 | export const getDetailApi = (id: string) => req.get(`/detail?id=${id}`) 49 | 50 | export const getWxConfigApi = (url: string) => req.post('/wechat/getConfig', { url }) -------------------------------------------------------------------------------- /src/assets/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MrXujiang/t-nav/835990006424a80b537986d16df5c11024f1f28e/src/assets/.DS_Store -------------------------------------------------------------------------------- /src/assets/cx.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MrXujiang/t-nav/835990006424a80b537986d16df5c11024f1f28e/src/assets/cx.png -------------------------------------------------------------------------------- /src/assets/dr.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MrXujiang/t-nav/835990006424a80b537986d16df5c11024f1f28e/src/assets/dr.png -------------------------------------------------------------------------------- /src/assets/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MrXujiang/t-nav/835990006424a80b537986d16df5c11024f1f28e/src/assets/logo.png -------------------------------------------------------------------------------- /src/assets/preson.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MrXujiang/t-nav/835990006424a80b537986d16df5c11024f1f28e/src/assets/preson.png -------------------------------------------------------------------------------- /src/assets/qt.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MrXujiang/t-nav/835990006424a80b537986d16df5c11024f1f28e/src/assets/qt.png -------------------------------------------------------------------------------- /src/assets/xx.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MrXujiang/t-nav/835990006424a80b537986d16df5c11024f1f28e/src/assets/xx.jpeg -------------------------------------------------------------------------------- /src/components/Upload/index.tsx: -------------------------------------------------------------------------------- 1 | import { 2 | Upload, 3 | } from 'antd'; 4 | import ImgCrop from 'antd-img-crop'; 5 | import { getToken } from '@/utils/req'; 6 | 7 | import { memo, useState, useEffect } from 'react'; 8 | 9 | export default memo((props) => { 10 | const { fileList, onChange } = props; 11 | const [files, setFiles] = useState([]); 12 | const handleChange = (value) => { 13 | const { file, fileList } = value; 14 | setFiles(fileList); 15 | if(file.status === 'done') { 16 | onChange && onChange(fileList.map(v => { 17 | if(v.originFileObj) { 18 | return { 19 | ...v?.response?.data 20 | } 21 | } 22 | return v 23 | })) 24 | } 25 | } 26 | 27 | const handleRemove = (file: File) => { 28 | const newFiles = files.filter(v => v.uid !== file.uid); 29 | onChange && onChange(newFiles) 30 | setFiles(newFiles) 31 | } 32 | 33 | useEffect(() => { 34 | if(fileList) { 35 | setFiles(fileList.map((v: any, i: number) => { 36 | return { 37 | ...v, 38 | uid: 'cxzk_' + i, 39 | status: 'done' 40 | } 41 | })) 42 | } 43 | }, [fileList]) 44 | return 45 | 56 | + 上传 57 | 58 | 59 | }) -------------------------------------------------------------------------------- /src/global.less: -------------------------------------------------------------------------------- 1 | body { 2 | margin: 0; 3 | } 4 | 5 | a { 6 | color: #1677ff; 7 | text-decoration: none; 8 | } 9 | 10 | .cxzk_to_new_page { 11 | display: none; 12 | } -------------------------------------------------------------------------------- /src/layouts/index.less: -------------------------------------------------------------------------------- 1 | .wrap { 2 | background-color: #f0f0f0; 3 | min-height: 100vh; 4 | .navs { 5 | background-color: #fff; 6 | padding: 8px 80px 10px; 7 | display: flex; 8 | .logo { 9 | margin-right: 20px; 10 | img { 11 | width: 40px; 12 | vertical-align: middle; 13 | } 14 | span { 15 | margin-left: 6px; 16 | font-size: 18px; 17 | font-weight: bold; 18 | span { 19 | font-size: 12px; 20 | transform: translateY(-6px); 21 | font-weight: normal; 22 | } 23 | @media screen and (max-width: 1100px){ 24 | span { 25 | display: none; 26 | } 27 | } 28 | @media screen and (max-width: 500px){ 29 | & { 30 | font-size: 16px; 31 | } 32 | } 33 | } 34 | } 35 | @media screen and (max-width: 500px){ 36 | & { 37 | padding: 8px 15px 10px; 38 | } 39 | } 40 | .searchArea { 41 | margin-left: auto; 42 | width: 400px; 43 | } 44 | .menu { 45 | display: flex; 46 | align-items: center; 47 | margin-left: auto; 48 | .menuItem { 49 | padding-right: 26px; 50 | cursor: pointer; 51 | } 52 | } 53 | @media screen and (max-width: 1260px){ 54 | .searchArea { 55 | width: 310px; 56 | } 57 | } 58 | @media screen and (max-width: 960px){ 59 | .menu { 60 | // display: none; 61 | .menuItem:nth-child(n+2) { 62 | display: none; 63 | } 64 | } 65 | } 66 | @media screen and (max-width: 800px){ 67 | .searchArea { 68 | width: 260px; 69 | } 70 | .menu { 71 | .menuItem:nth-child(n+1) { 72 | display: none; 73 | } 74 | } 75 | } 76 | @media screen and (max-width: 641px){ 77 | .searchArea { 78 | width: 200px; 79 | } 80 | } 81 | @media screen and (max-width: 590px){ 82 | .menu { 83 | display: none; 84 | } 85 | } 86 | 87 | } 88 | main { 89 | min-height: calc(100vh - 220px); 90 | } 91 | footer { 92 | background-color: #fff; 93 | .first { 94 | max-width: 1024px; 95 | margin: 0 auto; 96 | padding: 16px 0 18px; 97 | display: flex; 98 | align-items: center; 99 | .code { 100 | img { 101 | width: 100px; 102 | } 103 | } 104 | .content { 105 | margin-left: 12px; 106 | font-size: 14px; 107 | .title { 108 | font-size: 18px; 109 | padding-bottom: 10px; 110 | } 111 | .desc { 112 | padding-bottom: 10px; 113 | } 114 | .friendLink { 115 | a { 116 | color: #1677ff; 117 | text-decoration: none; 118 | } 119 | } 120 | } 121 | } 122 | .second { 123 | padding: 2px 0 5px; 124 | text-align: center; 125 | font-size: 12px; 126 | a { 127 | margin-left: 4px; 128 | } 129 | } 130 | } 131 | @media screen and (max-width: 800px){ 132 | footer { 133 | .first { 134 | justify-content: center; 135 | } 136 | .second { 137 | padding: 0 30px; 138 | } 139 | } 140 | } 141 | } 142 | -------------------------------------------------------------------------------- /src/layouts/index.tsx: -------------------------------------------------------------------------------- 1 | import { Outlet, history } from 'umi'; 2 | import { 3 | Button, 4 | Input, 5 | Tag, 6 | ConfigProvider, 7 | Modal, 8 | Form, 9 | Popover, 10 | Rate, 11 | Select, 12 | InputNumber 13 | } from 'antd'; 14 | import { useEffect, useState } from 'react'; 15 | import codeJpg from '../assets/xx.jpeg'; 16 | import Logo from '../assets/logo.png'; 17 | import Preson from '../assets/preson.png'; 18 | import zhCN from 'antd/locale/zh_CN'; 19 | import { url2obj } from 'xijs'; 20 | import { setToken, getToken } from '@/utils/req' 21 | import { 22 | loginApi, 23 | addListApi, 24 | modListApi 25 | } from '@/api'; 26 | import { types, sub_types } from '@/utils/config'; 27 | import MyUpload from '@/components/Upload'; 28 | import styles from './index.less'; 29 | 30 | const { Search } = Input; 31 | const { Option } = Select; 32 | const { TextArea } = Input; 33 | 34 | const normFile = (e: any) => { 35 | if (Array.isArray(e)) { 36 | return e; 37 | } 38 | return e?.fileList; 39 | }; 40 | 41 | export default function Layout() { 42 | const [showLogin, setShowLogin] = useState(false); 43 | const [canPublish, setCanPublish] = useState(false); 44 | const [loginForm] = Form.useForm(); 45 | const [pubForm] = Form.useForm(); 46 | const [keyword, setKeyWord] = useState(''); 47 | const onSearch = (value: string) => { 48 | setKeyWord(value) 49 | } 50 | const handleOk = () => { 51 | loginForm.validateFields().then(values => { 52 | const { username, password } = values; 53 | loginApi({ 54 | name: username, 55 | pwd: password 56 | }).then(res => { 57 | const { data: { token } } = res; 58 | setToken(token); 59 | history.push('/'); 60 | setShowLogin(false) 61 | }) 62 | }); 63 | } 64 | const handleCancel = () => { 65 | setShowLogin(false) 66 | } 67 | 68 | const handlePubOk = () => { 69 | pubForm.validateFields().then(values => { 70 | if(values.id) { 71 | modListApi(values).then(res => { 72 | setCanPublish(false) 73 | }) 74 | return 75 | } 76 | 77 | addListApi(values).then(res => { 78 | setCanPublish(false) 79 | }) 80 | }); 81 | } 82 | 83 | const handleShowPublish = () => { 84 | if(getToken()) { 85 | setCanPublish(true); 86 | pubForm.resetFields(); 87 | }else { 88 | Modal.info({ 89 | title: "加下方微信申请", 90 | content:
91 | }) 92 | } 93 | } 94 | const handlePubCancel = () => { 95 | setCanPublish(false); 96 | } 97 | 98 | const handlePubEdit = (row: any) => { 99 | setCanPublish(true); 100 | pubForm.setFieldsValue({ 101 | ...row 102 | }) 103 | } 104 | 105 | useEffect(() => { 106 | const search = window.location.search; 107 | const loginFlag = url2obj(search)?.flag; 108 | if(+loginFlag === 1) { 109 | setShowLogin(true); 110 | } 111 | }, []) 112 | return ( 113 | 114 |
115 |
116 |
117 | 118 | 工具导航 Ctrl + D 收藏网站 119 |
120 |
121 | 122 |
123 |
124 |
125 | } trigger="hover"> 126 | 官方媒体 127 | 128 |
129 |
130 | } trigger="hover"> 131 | 合作咨询 132 | 133 |
134 |
135 | } trigger="hover"> 136 | 技术服务 137 | 138 |
139 | 140 |
141 |
142 |
143 | 144 |
145 |
146 |
147 |
148 | 橙讯点评 149 |
150 |
151 |
分享最实用前沿的应用和工具
152 |
软件交流: cxzk_168
153 |
154 | 友情链接: 155 | Dooring零代码 156 |
157 |
158 |
159 |
160 | ©版权所有: 趣谈前端 - Dooring 161 |
162 |
163 | 164 |
172 | 177 | 178 | 179 | 180 | 185 | 186 | 187 |
188 |
189 | 190 |
198 | 205 | 210 | 211 | 212 | 217 | 227 | 228 | prevValues.type !== currentValues.type} 231 | > 232 | {({ getFieldValue }) => 233 | getFieldValue('type') === 'online' ? ( 234 | 239 | 240 | 250 | 251 | ) : null 252 | } 253 | 254 | 259 | 302 | 303 | 308 |