├── .env.development ├── .env.production ├── .eslintrc.js ├── .gitignore ├── .gitmodules ├── .husky └── pre-commit ├── README.md ├── next.config.js ├── package.json ├── postcss.config.js ├── preview ├── 404.png ├── 500-maintain.png ├── 500.png ├── applist.png ├── applist1.png ├── applist2.png ├── applist3.png ├── design-b.png ├── design-data.png ├── design-p.png ├── design-w.png ├── design.png ├── form-echo.png ├── form-login.png ├── form.png ├── form1.png ├── home1-w.png ├── home1.png ├── home2-w.png ├── home2.png ├── login.png ├── login_alert.png └── login_form.png ├── public ├── 3d │ ├── 3d-business-little-boy-in-online-lesson.png │ ├── 3d-casual-life-data-analysis.png │ ├── 3d-casual-life-design-composition-laptop.png │ └── 3d-casual-life-young-man-drawing-a-curve-in-design-program.png ├── avatar │ ├── b1.png │ ├── b2.png │ ├── b3.png │ ├── g1.png │ ├── g2.png │ └── g3.png ├── banner │ └── flip-1.mp4 ├── baseComponent.png ├── cat.jpg ├── cover │ ├── base │ │ ├── baseButton.png │ │ └── baseImage.png │ ├── charts │ │ ├── areaLine.png │ │ ├── areaStack.png │ │ ├── baseBar.png │ │ ├── baseFunnel.png │ │ ├── baseGauge.png │ │ ├── baseLine.png │ │ ├── basePie.png │ │ ├── borderRadiusPie.png │ │ ├── centerBar.png │ │ ├── doughnutPie.png │ │ ├── dropletChart.png │ │ ├── funnelChart.png │ │ ├── nightingalePie.png │ │ ├── pointChart.png │ │ ├── smoothLine.png │ │ ├── speedChart.png │ │ ├── stackBar.png │ │ ├── stackLine.png │ │ ├── ycategoryBar.png │ │ └── ycategoryStackBar.png │ └── datav │ │ ├── border │ │ ├── borderBox1.png │ │ ├── borderBox10.png │ │ ├── borderBox11.png │ │ ├── borderBox12.png │ │ ├── borderBox13.png │ │ ├── borderBox2.png │ │ ├── borderBox3.png │ │ ├── borderBox4.png │ │ ├── borderBox5.png │ │ ├── borderBox6.png │ │ ├── borderBox7.png │ │ ├── borderBox8.png │ │ └── borderBox9.png │ │ ├── chart │ │ ├── DvCapsuleChart.png │ │ ├── DvColumnChart.png │ │ ├── DvColumnChart1.png │ │ ├── DvPercentPond.png │ │ ├── DvRingChart.png │ │ └── DvWaterLevelPond.png │ │ ├── decoration │ │ ├── Decoration1.png │ │ ├── Decoration10.png │ │ ├── Decoration11.png │ │ ├── Decoration2.png │ │ ├── Decoration3.png │ │ ├── Decoration4.png │ │ ├── Decoration5.png │ │ ├── Decoration6.png │ │ ├── Decoration7.png │ │ ├── Decoration8.png │ │ └── Decoration9.png │ │ ├── loading.png │ │ ├── number.png │ │ └── table │ │ ├── DvScrollBoard.png │ │ └── baseScrollRankingBoard.png ├── dog.jpg ├── favicon.ico ├── icon │ ├── chat.png │ ├── free.png │ ├── github.png │ ├── group.png │ ├── level │ │ ├── 1.png │ │ ├── 10.png │ │ ├── 11.png │ │ ├── 12.png │ │ ├── 13.png │ │ ├── 14.png │ │ ├── 15.png │ │ ├── 16.png │ │ ├── 17.png │ │ ├── 18.png │ │ ├── 19.png │ │ ├── 2.png │ │ ├── 20.png │ │ ├── 3.png │ │ ├── 4.png │ │ ├── 5.png │ │ ├── 6.png │ │ ├── 7.png │ │ ├── 8.png │ │ ├── 9.png │ │ ├── first.png │ │ ├── great.png │ │ ├── second.png │ │ └── third.png │ ├── pick.png │ ├── pick1.png │ ├── recommend.png │ ├── seal.png │ ├── store.png │ ├── up.png │ ├── vip.png │ └── vipt.png ├── illustrations │ ├── techny-searching-the-web-on-tablet.png │ ├── templatePage.png │ └── upload.png ├── lottie │ ├── beam-web-design-program-on-laptop-screen.json │ ├── bubble-gum-web-design.json │ ├── techny-data-dashboard.json │ ├── techny-launching-a-startup.json │ └── techny-project-management.json ├── mountains.jpg ├── vercel.png ├── wechat.png ├── wechatGroup.png └── wechatReward.png ├── src ├── api │ ├── application.ts │ ├── auth.ts │ ├── design.ts │ ├── oss.ts │ ├── template.ts │ └── user.ts ├── assets │ ├── main.css │ └── sketch.css ├── components │ ├── Application │ │ ├── AppContainer.tsx │ │ ├── ApplicationItem.tsx │ │ └── PageItem.tsx │ ├── Banner │ │ ├── Banner.module.css │ │ └── index.tsx │ ├── Copyright │ │ ├── Copyright.module.css │ │ └── index.tsx │ ├── Dialog │ │ ├── CreateDialog.tsx │ │ ├── CreateUserDialog.tsx │ │ └── MoveDialog.tsx │ ├── Form │ │ ├── FormFactory.tsx │ │ ├── FormItem.tsx │ │ └── temp │ │ │ ├── AvatarItem.tsx │ │ │ ├── ButtonGroupItem.tsx │ │ │ ├── CancelButton.tsx │ │ │ ├── CaptureItem.tsx │ │ │ ├── HelperText.tsx │ │ │ ├── InputItem.tsx │ │ │ ├── PasswordItem.tsx │ │ │ ├── SelectItem.tsx │ │ │ ├── SliderItem.tsx │ │ │ ├── SubmitButton.tsx │ │ │ └── SwitchItem.tsx │ ├── HeadMenu │ │ └── index.tsx │ ├── Header │ │ ├── Header.module.css │ │ └── index.tsx │ ├── IconGen │ │ └── index.tsx │ ├── Layout │ │ └── index.tsx │ ├── Logo │ │ ├── Logo.module.css │ │ └── index.tsx │ ├── Lottie │ │ └── index.tsx │ ├── MenuList │ │ ├── Menu.module.css │ │ ├── MenuItem.tsx │ │ ├── PopMenuItem.tsx │ │ ├── SubMenuItem.tsx │ │ └── index.tsx │ ├── MoreIcon │ │ └── index.tsx │ ├── MuiTable │ │ ├── index.tsx │ │ └── temp │ │ │ ├── User.tsx │ │ │ └── UserOptions.tsx │ ├── PageLoading │ │ └── index.tsx │ ├── Query │ │ ├── QueryFactory.tsx │ │ └── item │ │ │ ├── Button.tsx │ │ │ ├── DateRange.tsx │ │ │ ├── Select.tsx │ │ │ ├── String.tsx │ │ │ └── Tab.tsx │ ├── Svg │ │ ├── Empty.tsx │ │ ├── ErrorIcon.tsx │ │ ├── EyesIcon.tsx │ │ ├── FileIcon.tsx │ │ ├── FolderIcon.tsx │ │ ├── LoadingIcon.tsx │ │ ├── NotFoundIcon.tsx │ │ ├── RecommendIcon.tsx │ │ ├── ServerErrorIcon.tsx │ │ └── WarningIcon.tsx │ ├── Swiper │ │ └── index.tsx │ ├── ThemeSwitch │ │ └── index.tsx │ ├── TipBox │ │ └── index.tsx │ └── shimmer.tsx ├── config │ ├── constant.ts │ ├── context │ │ └── ThemeModeContext.ts │ ├── data │ │ └── index.ts │ ├── form │ │ ├── CreateApp.ts │ │ ├── CreatePage.ts │ │ ├── CreateUser.ts │ │ ├── LoginForm.ts │ │ └── TemplateForm.ts │ ├── query │ │ └── UserQuery.ts │ └── theme.ts ├── pages │ ├── 404 │ │ └── index.tsx │ ├── 500 │ │ └── index.tsx │ ├── _app.tsx │ ├── api │ │ ├── component │ │ │ └── index.ts │ │ ├── people │ │ │ └── index.ts │ │ └── static │ │ │ ├── component.ts │ │ │ └── people.ts │ ├── application │ │ ├── [appId].tsx │ │ └── index.tsx │ ├── design │ │ ├── README.md │ │ └── [did].tsx │ ├── formCreate │ │ └── index.tsx │ ├── index.tsx │ ├── login │ │ ├── Login.module.css │ │ └── index.tsx │ ├── preview │ │ └── [did].tsx │ ├── sale │ │ ├── PriceItem.tsx │ │ └── index.tsx │ ├── store │ │ ├── TemplateItem.tsx │ │ └── index.tsx │ ├── template │ │ └── [tid].tsx │ └── user │ │ ├── [uid].tsx │ │ └── index.tsx ├── store │ ├── index.ts │ ├── mixin │ │ └── index.ts │ └── slice │ │ ├── Counter │ │ ├── Counter.module.css │ │ ├── Counter.tsx │ │ ├── counterAPI.ts │ │ └── index.ts │ │ ├── Design │ │ └── index.ts │ │ └── Global │ │ └── index.ts ├── types │ ├── form.d.ts │ └── index.d.ts └── utils │ ├── axios.ts │ ├── common.ts │ └── lib │ └── AlphaVideoMaterial.ts ├── tailwind.config.js ├── tsconfig.json └── yarn.lock /.env.development: -------------------------------------------------------------------------------- 1 | NEXT_PUBLIC_BASEURL="http://localhost:8080" 2 | NEXT_PUBLIC_BASEOSS="http://localhost:8080" -------------------------------------------------------------------------------- /.env.production: -------------------------------------------------------------------------------- 1 | NEXT_PUBLIC_BASEURL="https://ccedit.com" 2 | NEXT_PUBLIC_BASEOSS="https://ccedit.com/oss" -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | 4 | # dependencies 5 | /node_modules 6 | /.pnp 7 | .pnp.js 8 | 9 | # testing 10 | /coverage 11 | 12 | # next.js 13 | /.next/ 14 | #/out/ 15 | 16 | # production 17 | /build 18 | 19 | # misc 20 | .DS_Store 21 | *.pem 22 | 23 | # debug 24 | npm-debug.log* 25 | yarn-debug.log* 26 | yarn-error.log* 27 | 28 | # local env files 29 | .env*.local 30 | 31 | # vercel 32 | .vercel 33 | 34 | # typescript 35 | *.tsbuildinfo 36 | next-env.d.ts 37 | 38 | # Editor directories and files 39 | .vscode/* 40 | !.vscode/extensions.json 41 | .idea 42 | out/ 43 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "src/bailu"] 2 | path = src/bailu 3 | url = git@github.com:Cc-Edit/CcBailu.git 4 | [submodule "src/components/Design"] 5 | path = src/components/Design 6 | url = git@github.com:Cc-Edit/CcDesign.git 7 | -------------------------------------------------------------------------------- /.husky/pre-commit: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | . "$(dirname -- "$0")/_/husky.sh" 3 | 4 | npx lint-staged -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | | 学习讨论小组🍻 | 打赏(赠送学习资料:[webNote](https://github.com/Cc-Edit/webNote)) :confetti_ball: | 4 | |:----------------------------------------------------------------------------------:|:---------------------------------------------------------------------:| 5 | | ![CcView.png](https://github.com/Cc-Edit/Cc-Edit/blob/main/public/CcView.png) | ![img.png](https://github.com/Cc-Edit/Cc-Edit/blob/main/public/img.png) | 6 | 7 | 8 | 9 | # CcView 10 | 11 | ## demo地址 12 | > https://ccedit.com/ 13 | 14 | ## 版权 15 | 仅限于个人学习使用,不可用于商业用途 16 | 商用需联系作者购买授权 17 | 软著登记号:2023SR1434342 18 | 19 | ## 核心库 20 | > 组件库以及编辑器使用git subModule的方式集成 21 | ### 低代码组件库 22 | [白露 https://github.com/Cc-Edit/CcBailu](https://github.com/Cc-Edit/CcBailu) 23 | ### 后台服务 24 | [CcServer https://github.com/Cc-Edit/CcServer](https://github.com/Cc-Edit/CcServer) 25 | 26 | 27 | ## 本地运行 28 | 29 | > $ git clone --recurse-submodules https://github.com/Cc-Edit/CcView.git 30 | 31 | 或 32 | > 33 | > $ git clone https://github.com/Cc-Edit/CcView.git 34 | > $ git submodule update --init 35 | 36 | 37 | 更新所有子模块 38 | > $ git submodule update --remote 39 | 40 | 本地运行: 41 | > $ yarn install 42 | > $ yarn dev 43 | 44 | 45 | ## 设计原则 46 | 47 | ### 数据驱动 48 | 整个应用的核心就是数据驱动,将所有可被抽象的逻辑全部抽象为 jsonSchema 49 | 整个应用遵守配置化、描述化原则 50 | 51 | ### JSON to Form 表单描述 52 | 表单的创建与校验,全部集中到配置文件中 [form](src/config/form) 53 | 登录表单: [LoginForm.ts](src%2Fconfig%2Fform%2FLoginForm.ts) 54 | ![form-login.png](preview%2Fform-login.png) 55 | 56 | 应用创建弹窗: [CreateApp.ts](src%2Fconfig%2Fform%2FCreateApp.ts) 57 | ![form1.png](preview%2Fform1.png) 58 | 59 | 表单回显(用于数据更新): 60 | ![form-echo.png](preview%2Fform-echo.png) 61 | 62 | 表单模板(所有基础表单元素示例) [TemplateForm.ts](src%2Fconfig%2Fform%2FTemplateForm.ts) 63 | ![form.png](preview%2Fform.png) 64 | 65 | ## 预览 66 | 67 | #### 登录页面 68 | 69 | > 视频透明背景播放 70 | ![](./preview/login.png) 71 | 72 | #### 登录页面-表单校验 73 | 74 | ![](./preview/login_form.png) 75 | 76 | #### 登录页面-全局提示 77 | 78 | ![](./preview/login_alert.png) 79 | 80 | #### 首页 81 | 82 | ![](./preview/home1.png) 83 | ![](./preview/home2.png) 84 | ![](./preview/home1-w.png) 85 | ![](./preview/home2-w.png) 86 | 87 | #### 应用列表 88 | ![applist.png](preview%2Fapplist.png) 89 | ![applist1.png](preview%2Fapplist1.png) 90 | ![applist3.png](preview%2Fapplist3.png) 91 | ![applist2.png](preview%2Fapplist2.png) 92 | 93 | ### 编辑器 94 | ![design.png](preview%2Fdesign.png) 95 | ![design-w.png](preview%2Fdesign-w.png) 96 | ![design-b.png](preview%2Fdesign-b.png) 97 | ![design-p.png](preview%2Fdesign-p.png) 98 | ![design-data.png](preview%2Fdesign-data.png) 99 | 100 | #### 404页面 101 | ![](./preview/404.png) 102 | 103 | #### 500页面 104 | ![](./preview/500.png) 105 | 106 | #### 维护页面 107 | ![](./preview/500-maintain.png) 108 | 109 | ### 工具类库 110 | #### 插画与图标 [https://igoutu.cn/](https://igoutu.cn/) 111 | #### Icons by icons8: [icons8.com](https://icons8.com) 112 | #### 画板标尺 (基于 react18 翻新 mb-sketch-ruler ): [mb-sketch-ruler](https://github.com/mockingbot/mb-sketch-ruler) 113 | #### 头像、默认模板背景图:千图网个人商用授权 114 | -------------------------------------------------------------------------------- /next.config.js: -------------------------------------------------------------------------------- 1 | /** @type {import('next').NextConfig} */ 2 | const withTM = require('next-transpile-modules')(['echarts', 'zrender']); 3 | const nextConfig = withTM({ 4 | eslint: { 5 | dirs: ['src/'] // Only run ESLint on the 'pages' and 'utils' directories during production builds (next build) 6 | }, 7 | images: { 8 | unoptimized: true, 9 | formats: ['image/avif', 'image/webp'], 10 | remotePatterns: [ 11 | { 12 | protocol: 'https', 13 | hostname: 'assets.vercel.com', 14 | port: '', 15 | pathname: '/image/upload/**' 16 | } 17 | ] 18 | }, 19 | webpack(config) { 20 | config.module.rules.push({ 21 | test: /\.svg$/i, 22 | issuer: /\.[jt]sx?$/, 23 | use: ['@svgr/webpack'] 24 | }); 25 | return config; 26 | }, 27 | devIndicators: { 28 | buildActivity: false 29 | } 30 | }); 31 | 32 | module.exports = nextConfig; 33 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "CcView", 3 | "version": "0.1.0", 4 | "private": true, 5 | "scripts": { 6 | "dev": "next dev", 7 | "build": "next build", 8 | "start": "next start", 9 | "publish-SSG": "ISPROD=true next build && next export", 10 | "prepare": "husky install", 11 | "analyze": "cross-env ANALYZE=true yarn build", 12 | "analyze-serve": "serve .next/analyze", 13 | "sub-update": "git submodule update --remote" 14 | }, 15 | "dependencies": { 16 | "@emotion/react": "11.11.1", 17 | "@emotion/styled": "11.11.0", 18 | "@hookform/resolvers": "3.1.0", 19 | "@jiaminghi/data-view-react": "^1.2.5", 20 | "@loadable/component": "5.15.3", 21 | "@monaco-editor/react": "4.5.2", 22 | "@mui/icons-material": "5.11.16", 23 | "@mui/material": "5.13.4", 24 | "@mui/x-date-pickers": "6.12.0", 25 | "@reduxjs/toolkit": "1.9.5", 26 | "@types/html2canvas": "1.0.0", 27 | "axios": "1.4.0", 28 | "cross-env": "7.0.3", 29 | "dayjs": "1.11.9", 30 | "echarts": "5.4.3", 31 | "echarts-liquidfill": "^3.1.0", 32 | "gsap": "3.12.0", 33 | "html2canvas": "1.4.1", 34 | "lodash": "4.17.21", 35 | "lottie-web": "5.12.2", 36 | "material-ui-confirm": "3.0.9", 37 | "next": "13.4.4", 38 | "notistack": "3.0.1", 39 | "nprogress": "0.2.0", 40 | "react": "18.2.0", 41 | "react-color": "2.19.3", 42 | "react-dom": "18.2.0", 43 | "react-hook-form": "7.44.3", 44 | "react-moveable": "0.54.1", 45 | "react-redux": "8.0.7", 46 | "react-swipeable-views": "0.14.0", 47 | "react-swipeable-views-utils": "0.14.0", 48 | "tailwindcss": "3.3.2", 49 | "three": "0.153.0" 50 | }, 51 | "devDependencies": { 52 | "@next/bundle-analyzer": "13.4.4", 53 | "@svgr/webpack": "8.0.1", 54 | "@types/loadable__component": "5.13.4", 55 | "@types/lodash": "^4.14.202", 56 | "@types/node": "20.2.5", 57 | "@types/nprogress": "0.2.0", 58 | "@types/react": "18.2.8", 59 | "@types/react-color": "3.0.6", 60 | "@types/react-dom": "18.2.4", 61 | "@types/react-swipeable-views": "0.13.2", 62 | "@types/react-swipeable-views-utils": "0.13.4", 63 | "@types/three": "0.152.1", 64 | "autoprefixer": "10.4.14", 65 | "eslint": "8.41.0", 66 | "eslint-config-next": "13.4.4", 67 | "husky": "8.0.3", 68 | "lint-staged": "13.2.2", 69 | "monaco-editor": "0.41.0", 70 | "next-transpile-modules": "10.0.1", 71 | "postcss": "8.4.24", 72 | "typescript": "5.1.3" 73 | }, 74 | "lint-staged": { 75 | "src/**/*.{js,jsx,ts,tsx}": [ 76 | "eslint --fix" 77 | ] 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /postcss.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | plugins: { 3 | tailwindcss: {}, 4 | autoprefixer: {}, 5 | }, 6 | } 7 | -------------------------------------------------------------------------------- /preview/404.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Li-vien/CcView/0623bf3bcb50a1a47e112af416db494031d11bd0/preview/404.png -------------------------------------------------------------------------------- /preview/500-maintain.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Li-vien/CcView/0623bf3bcb50a1a47e112af416db494031d11bd0/preview/500-maintain.png -------------------------------------------------------------------------------- /preview/500.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Li-vien/CcView/0623bf3bcb50a1a47e112af416db494031d11bd0/preview/500.png -------------------------------------------------------------------------------- /preview/applist.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Li-vien/CcView/0623bf3bcb50a1a47e112af416db494031d11bd0/preview/applist.png -------------------------------------------------------------------------------- /preview/applist1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Li-vien/CcView/0623bf3bcb50a1a47e112af416db494031d11bd0/preview/applist1.png -------------------------------------------------------------------------------- /preview/applist2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Li-vien/CcView/0623bf3bcb50a1a47e112af416db494031d11bd0/preview/applist2.png -------------------------------------------------------------------------------- /preview/applist3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Li-vien/CcView/0623bf3bcb50a1a47e112af416db494031d11bd0/preview/applist3.png -------------------------------------------------------------------------------- /preview/design-b.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Li-vien/CcView/0623bf3bcb50a1a47e112af416db494031d11bd0/preview/design-b.png -------------------------------------------------------------------------------- /preview/design-data.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Li-vien/CcView/0623bf3bcb50a1a47e112af416db494031d11bd0/preview/design-data.png -------------------------------------------------------------------------------- /preview/design-p.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Li-vien/CcView/0623bf3bcb50a1a47e112af416db494031d11bd0/preview/design-p.png -------------------------------------------------------------------------------- /preview/design-w.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Li-vien/CcView/0623bf3bcb50a1a47e112af416db494031d11bd0/preview/design-w.png -------------------------------------------------------------------------------- /preview/design.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Li-vien/CcView/0623bf3bcb50a1a47e112af416db494031d11bd0/preview/design.png -------------------------------------------------------------------------------- /preview/form-echo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Li-vien/CcView/0623bf3bcb50a1a47e112af416db494031d11bd0/preview/form-echo.png -------------------------------------------------------------------------------- /preview/form-login.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Li-vien/CcView/0623bf3bcb50a1a47e112af416db494031d11bd0/preview/form-login.png -------------------------------------------------------------------------------- /preview/form.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Li-vien/CcView/0623bf3bcb50a1a47e112af416db494031d11bd0/preview/form.png -------------------------------------------------------------------------------- /preview/form1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Li-vien/CcView/0623bf3bcb50a1a47e112af416db494031d11bd0/preview/form1.png -------------------------------------------------------------------------------- /preview/home1-w.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Li-vien/CcView/0623bf3bcb50a1a47e112af416db494031d11bd0/preview/home1-w.png -------------------------------------------------------------------------------- /preview/home1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Li-vien/CcView/0623bf3bcb50a1a47e112af416db494031d11bd0/preview/home1.png -------------------------------------------------------------------------------- /preview/home2-w.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Li-vien/CcView/0623bf3bcb50a1a47e112af416db494031d11bd0/preview/home2-w.png -------------------------------------------------------------------------------- /preview/home2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Li-vien/CcView/0623bf3bcb50a1a47e112af416db494031d11bd0/preview/home2.png -------------------------------------------------------------------------------- /preview/login.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Li-vien/CcView/0623bf3bcb50a1a47e112af416db494031d11bd0/preview/login.png -------------------------------------------------------------------------------- /preview/login_alert.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Li-vien/CcView/0623bf3bcb50a1a47e112af416db494031d11bd0/preview/login_alert.png -------------------------------------------------------------------------------- /preview/login_form.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Li-vien/CcView/0623bf3bcb50a1a47e112af416db494031d11bd0/preview/login_form.png -------------------------------------------------------------------------------- /public/3d/3d-business-little-boy-in-online-lesson.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Li-vien/CcView/0623bf3bcb50a1a47e112af416db494031d11bd0/public/3d/3d-business-little-boy-in-online-lesson.png -------------------------------------------------------------------------------- /public/3d/3d-casual-life-data-analysis.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Li-vien/CcView/0623bf3bcb50a1a47e112af416db494031d11bd0/public/3d/3d-casual-life-data-analysis.png -------------------------------------------------------------------------------- /public/3d/3d-casual-life-design-composition-laptop.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Li-vien/CcView/0623bf3bcb50a1a47e112af416db494031d11bd0/public/3d/3d-casual-life-design-composition-laptop.png -------------------------------------------------------------------------------- /public/3d/3d-casual-life-young-man-drawing-a-curve-in-design-program.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Li-vien/CcView/0623bf3bcb50a1a47e112af416db494031d11bd0/public/3d/3d-casual-life-young-man-drawing-a-curve-in-design-program.png -------------------------------------------------------------------------------- /public/avatar/b1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Li-vien/CcView/0623bf3bcb50a1a47e112af416db494031d11bd0/public/avatar/b1.png -------------------------------------------------------------------------------- /public/avatar/b2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Li-vien/CcView/0623bf3bcb50a1a47e112af416db494031d11bd0/public/avatar/b2.png -------------------------------------------------------------------------------- /public/avatar/b3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Li-vien/CcView/0623bf3bcb50a1a47e112af416db494031d11bd0/public/avatar/b3.png -------------------------------------------------------------------------------- /public/avatar/g1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Li-vien/CcView/0623bf3bcb50a1a47e112af416db494031d11bd0/public/avatar/g1.png -------------------------------------------------------------------------------- /public/avatar/g2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Li-vien/CcView/0623bf3bcb50a1a47e112af416db494031d11bd0/public/avatar/g2.png -------------------------------------------------------------------------------- /public/avatar/g3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Li-vien/CcView/0623bf3bcb50a1a47e112af416db494031d11bd0/public/avatar/g3.png -------------------------------------------------------------------------------- /public/banner/flip-1.mp4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Li-vien/CcView/0623bf3bcb50a1a47e112af416db494031d11bd0/public/banner/flip-1.mp4 -------------------------------------------------------------------------------- /public/baseComponent.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Li-vien/CcView/0623bf3bcb50a1a47e112af416db494031d11bd0/public/baseComponent.png -------------------------------------------------------------------------------- /public/cat.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Li-vien/CcView/0623bf3bcb50a1a47e112af416db494031d11bd0/public/cat.jpg -------------------------------------------------------------------------------- /public/cover/base/baseButton.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Li-vien/CcView/0623bf3bcb50a1a47e112af416db494031d11bd0/public/cover/base/baseButton.png -------------------------------------------------------------------------------- /public/cover/base/baseImage.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Li-vien/CcView/0623bf3bcb50a1a47e112af416db494031d11bd0/public/cover/base/baseImage.png -------------------------------------------------------------------------------- /public/cover/charts/areaLine.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Li-vien/CcView/0623bf3bcb50a1a47e112af416db494031d11bd0/public/cover/charts/areaLine.png -------------------------------------------------------------------------------- /public/cover/charts/areaStack.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Li-vien/CcView/0623bf3bcb50a1a47e112af416db494031d11bd0/public/cover/charts/areaStack.png -------------------------------------------------------------------------------- /public/cover/charts/baseBar.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Li-vien/CcView/0623bf3bcb50a1a47e112af416db494031d11bd0/public/cover/charts/baseBar.png -------------------------------------------------------------------------------- /public/cover/charts/baseFunnel.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Li-vien/CcView/0623bf3bcb50a1a47e112af416db494031d11bd0/public/cover/charts/baseFunnel.png -------------------------------------------------------------------------------- /public/cover/charts/baseGauge.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Li-vien/CcView/0623bf3bcb50a1a47e112af416db494031d11bd0/public/cover/charts/baseGauge.png -------------------------------------------------------------------------------- /public/cover/charts/baseLine.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Li-vien/CcView/0623bf3bcb50a1a47e112af416db494031d11bd0/public/cover/charts/baseLine.png -------------------------------------------------------------------------------- /public/cover/charts/basePie.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Li-vien/CcView/0623bf3bcb50a1a47e112af416db494031d11bd0/public/cover/charts/basePie.png -------------------------------------------------------------------------------- /public/cover/charts/borderRadiusPie.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Li-vien/CcView/0623bf3bcb50a1a47e112af416db494031d11bd0/public/cover/charts/borderRadiusPie.png -------------------------------------------------------------------------------- /public/cover/charts/centerBar.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Li-vien/CcView/0623bf3bcb50a1a47e112af416db494031d11bd0/public/cover/charts/centerBar.png -------------------------------------------------------------------------------- /public/cover/charts/doughnutPie.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Li-vien/CcView/0623bf3bcb50a1a47e112af416db494031d11bd0/public/cover/charts/doughnutPie.png -------------------------------------------------------------------------------- /public/cover/charts/dropletChart.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Li-vien/CcView/0623bf3bcb50a1a47e112af416db494031d11bd0/public/cover/charts/dropletChart.png -------------------------------------------------------------------------------- /public/cover/charts/funnelChart.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Li-vien/CcView/0623bf3bcb50a1a47e112af416db494031d11bd0/public/cover/charts/funnelChart.png -------------------------------------------------------------------------------- /public/cover/charts/nightingalePie.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Li-vien/CcView/0623bf3bcb50a1a47e112af416db494031d11bd0/public/cover/charts/nightingalePie.png -------------------------------------------------------------------------------- /public/cover/charts/pointChart.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Li-vien/CcView/0623bf3bcb50a1a47e112af416db494031d11bd0/public/cover/charts/pointChart.png -------------------------------------------------------------------------------- /public/cover/charts/smoothLine.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Li-vien/CcView/0623bf3bcb50a1a47e112af416db494031d11bd0/public/cover/charts/smoothLine.png -------------------------------------------------------------------------------- /public/cover/charts/speedChart.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Li-vien/CcView/0623bf3bcb50a1a47e112af416db494031d11bd0/public/cover/charts/speedChart.png -------------------------------------------------------------------------------- /public/cover/charts/stackBar.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Li-vien/CcView/0623bf3bcb50a1a47e112af416db494031d11bd0/public/cover/charts/stackBar.png -------------------------------------------------------------------------------- /public/cover/charts/stackLine.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Li-vien/CcView/0623bf3bcb50a1a47e112af416db494031d11bd0/public/cover/charts/stackLine.png -------------------------------------------------------------------------------- /public/cover/charts/ycategoryBar.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Li-vien/CcView/0623bf3bcb50a1a47e112af416db494031d11bd0/public/cover/charts/ycategoryBar.png -------------------------------------------------------------------------------- /public/cover/charts/ycategoryStackBar.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Li-vien/CcView/0623bf3bcb50a1a47e112af416db494031d11bd0/public/cover/charts/ycategoryStackBar.png -------------------------------------------------------------------------------- /public/cover/datav/border/borderBox1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Li-vien/CcView/0623bf3bcb50a1a47e112af416db494031d11bd0/public/cover/datav/border/borderBox1.png -------------------------------------------------------------------------------- /public/cover/datav/border/borderBox10.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Li-vien/CcView/0623bf3bcb50a1a47e112af416db494031d11bd0/public/cover/datav/border/borderBox10.png -------------------------------------------------------------------------------- /public/cover/datav/border/borderBox11.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Li-vien/CcView/0623bf3bcb50a1a47e112af416db494031d11bd0/public/cover/datav/border/borderBox11.png -------------------------------------------------------------------------------- /public/cover/datav/border/borderBox12.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Li-vien/CcView/0623bf3bcb50a1a47e112af416db494031d11bd0/public/cover/datav/border/borderBox12.png -------------------------------------------------------------------------------- /public/cover/datav/border/borderBox13.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Li-vien/CcView/0623bf3bcb50a1a47e112af416db494031d11bd0/public/cover/datav/border/borderBox13.png -------------------------------------------------------------------------------- /public/cover/datav/border/borderBox2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Li-vien/CcView/0623bf3bcb50a1a47e112af416db494031d11bd0/public/cover/datav/border/borderBox2.png -------------------------------------------------------------------------------- /public/cover/datav/border/borderBox3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Li-vien/CcView/0623bf3bcb50a1a47e112af416db494031d11bd0/public/cover/datav/border/borderBox3.png -------------------------------------------------------------------------------- /public/cover/datav/border/borderBox4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Li-vien/CcView/0623bf3bcb50a1a47e112af416db494031d11bd0/public/cover/datav/border/borderBox4.png -------------------------------------------------------------------------------- /public/cover/datav/border/borderBox5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Li-vien/CcView/0623bf3bcb50a1a47e112af416db494031d11bd0/public/cover/datav/border/borderBox5.png -------------------------------------------------------------------------------- /public/cover/datav/border/borderBox6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Li-vien/CcView/0623bf3bcb50a1a47e112af416db494031d11bd0/public/cover/datav/border/borderBox6.png -------------------------------------------------------------------------------- /public/cover/datav/border/borderBox7.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Li-vien/CcView/0623bf3bcb50a1a47e112af416db494031d11bd0/public/cover/datav/border/borderBox7.png -------------------------------------------------------------------------------- /public/cover/datav/border/borderBox8.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Li-vien/CcView/0623bf3bcb50a1a47e112af416db494031d11bd0/public/cover/datav/border/borderBox8.png -------------------------------------------------------------------------------- /public/cover/datav/border/borderBox9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Li-vien/CcView/0623bf3bcb50a1a47e112af416db494031d11bd0/public/cover/datav/border/borderBox9.png -------------------------------------------------------------------------------- /public/cover/datav/chart/DvCapsuleChart.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Li-vien/CcView/0623bf3bcb50a1a47e112af416db494031d11bd0/public/cover/datav/chart/DvCapsuleChart.png -------------------------------------------------------------------------------- /public/cover/datav/chart/DvColumnChart.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Li-vien/CcView/0623bf3bcb50a1a47e112af416db494031d11bd0/public/cover/datav/chart/DvColumnChart.png -------------------------------------------------------------------------------- /public/cover/datav/chart/DvColumnChart1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Li-vien/CcView/0623bf3bcb50a1a47e112af416db494031d11bd0/public/cover/datav/chart/DvColumnChart1.png -------------------------------------------------------------------------------- /public/cover/datav/chart/DvPercentPond.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Li-vien/CcView/0623bf3bcb50a1a47e112af416db494031d11bd0/public/cover/datav/chart/DvPercentPond.png -------------------------------------------------------------------------------- /public/cover/datav/chart/DvRingChart.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Li-vien/CcView/0623bf3bcb50a1a47e112af416db494031d11bd0/public/cover/datav/chart/DvRingChart.png -------------------------------------------------------------------------------- /public/cover/datav/chart/DvWaterLevelPond.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Li-vien/CcView/0623bf3bcb50a1a47e112af416db494031d11bd0/public/cover/datav/chart/DvWaterLevelPond.png -------------------------------------------------------------------------------- /public/cover/datav/decoration/Decoration1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Li-vien/CcView/0623bf3bcb50a1a47e112af416db494031d11bd0/public/cover/datav/decoration/Decoration1.png -------------------------------------------------------------------------------- /public/cover/datav/decoration/Decoration10.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Li-vien/CcView/0623bf3bcb50a1a47e112af416db494031d11bd0/public/cover/datav/decoration/Decoration10.png -------------------------------------------------------------------------------- /public/cover/datav/decoration/Decoration11.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Li-vien/CcView/0623bf3bcb50a1a47e112af416db494031d11bd0/public/cover/datav/decoration/Decoration11.png -------------------------------------------------------------------------------- /public/cover/datav/decoration/Decoration2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Li-vien/CcView/0623bf3bcb50a1a47e112af416db494031d11bd0/public/cover/datav/decoration/Decoration2.png -------------------------------------------------------------------------------- /public/cover/datav/decoration/Decoration3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Li-vien/CcView/0623bf3bcb50a1a47e112af416db494031d11bd0/public/cover/datav/decoration/Decoration3.png -------------------------------------------------------------------------------- /public/cover/datav/decoration/Decoration4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Li-vien/CcView/0623bf3bcb50a1a47e112af416db494031d11bd0/public/cover/datav/decoration/Decoration4.png -------------------------------------------------------------------------------- /public/cover/datav/decoration/Decoration5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Li-vien/CcView/0623bf3bcb50a1a47e112af416db494031d11bd0/public/cover/datav/decoration/Decoration5.png -------------------------------------------------------------------------------- /public/cover/datav/decoration/Decoration6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Li-vien/CcView/0623bf3bcb50a1a47e112af416db494031d11bd0/public/cover/datav/decoration/Decoration6.png -------------------------------------------------------------------------------- /public/cover/datav/decoration/Decoration7.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Li-vien/CcView/0623bf3bcb50a1a47e112af416db494031d11bd0/public/cover/datav/decoration/Decoration7.png -------------------------------------------------------------------------------- /public/cover/datav/decoration/Decoration8.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Li-vien/CcView/0623bf3bcb50a1a47e112af416db494031d11bd0/public/cover/datav/decoration/Decoration8.png -------------------------------------------------------------------------------- /public/cover/datav/decoration/Decoration9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Li-vien/CcView/0623bf3bcb50a1a47e112af416db494031d11bd0/public/cover/datav/decoration/Decoration9.png -------------------------------------------------------------------------------- /public/cover/datav/loading.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Li-vien/CcView/0623bf3bcb50a1a47e112af416db494031d11bd0/public/cover/datav/loading.png -------------------------------------------------------------------------------- /public/cover/datav/number.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Li-vien/CcView/0623bf3bcb50a1a47e112af416db494031d11bd0/public/cover/datav/number.png -------------------------------------------------------------------------------- /public/cover/datav/table/DvScrollBoard.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Li-vien/CcView/0623bf3bcb50a1a47e112af416db494031d11bd0/public/cover/datav/table/DvScrollBoard.png -------------------------------------------------------------------------------- /public/cover/datav/table/baseScrollRankingBoard.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Li-vien/CcView/0623bf3bcb50a1a47e112af416db494031d11bd0/public/cover/datav/table/baseScrollRankingBoard.png -------------------------------------------------------------------------------- /public/dog.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Li-vien/CcView/0623bf3bcb50a1a47e112af416db494031d11bd0/public/dog.jpg -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Li-vien/CcView/0623bf3bcb50a1a47e112af416db494031d11bd0/public/favicon.ico -------------------------------------------------------------------------------- /public/icon/chat.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Li-vien/CcView/0623bf3bcb50a1a47e112af416db494031d11bd0/public/icon/chat.png -------------------------------------------------------------------------------- /public/icon/free.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Li-vien/CcView/0623bf3bcb50a1a47e112af416db494031d11bd0/public/icon/free.png -------------------------------------------------------------------------------- /public/icon/github.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Li-vien/CcView/0623bf3bcb50a1a47e112af416db494031d11bd0/public/icon/github.png -------------------------------------------------------------------------------- /public/icon/group.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Li-vien/CcView/0623bf3bcb50a1a47e112af416db494031d11bd0/public/icon/group.png -------------------------------------------------------------------------------- /public/icon/level/1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Li-vien/CcView/0623bf3bcb50a1a47e112af416db494031d11bd0/public/icon/level/1.png -------------------------------------------------------------------------------- /public/icon/level/10.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Li-vien/CcView/0623bf3bcb50a1a47e112af416db494031d11bd0/public/icon/level/10.png -------------------------------------------------------------------------------- /public/icon/level/11.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Li-vien/CcView/0623bf3bcb50a1a47e112af416db494031d11bd0/public/icon/level/11.png -------------------------------------------------------------------------------- /public/icon/level/12.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Li-vien/CcView/0623bf3bcb50a1a47e112af416db494031d11bd0/public/icon/level/12.png -------------------------------------------------------------------------------- /public/icon/level/13.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Li-vien/CcView/0623bf3bcb50a1a47e112af416db494031d11bd0/public/icon/level/13.png -------------------------------------------------------------------------------- /public/icon/level/14.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Li-vien/CcView/0623bf3bcb50a1a47e112af416db494031d11bd0/public/icon/level/14.png -------------------------------------------------------------------------------- /public/icon/level/15.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Li-vien/CcView/0623bf3bcb50a1a47e112af416db494031d11bd0/public/icon/level/15.png -------------------------------------------------------------------------------- /public/icon/level/16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Li-vien/CcView/0623bf3bcb50a1a47e112af416db494031d11bd0/public/icon/level/16.png -------------------------------------------------------------------------------- /public/icon/level/17.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Li-vien/CcView/0623bf3bcb50a1a47e112af416db494031d11bd0/public/icon/level/17.png -------------------------------------------------------------------------------- /public/icon/level/18.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Li-vien/CcView/0623bf3bcb50a1a47e112af416db494031d11bd0/public/icon/level/18.png -------------------------------------------------------------------------------- /public/icon/level/19.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Li-vien/CcView/0623bf3bcb50a1a47e112af416db494031d11bd0/public/icon/level/19.png -------------------------------------------------------------------------------- /public/icon/level/2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Li-vien/CcView/0623bf3bcb50a1a47e112af416db494031d11bd0/public/icon/level/2.png -------------------------------------------------------------------------------- /public/icon/level/20.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Li-vien/CcView/0623bf3bcb50a1a47e112af416db494031d11bd0/public/icon/level/20.png -------------------------------------------------------------------------------- /public/icon/level/3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Li-vien/CcView/0623bf3bcb50a1a47e112af416db494031d11bd0/public/icon/level/3.png -------------------------------------------------------------------------------- /public/icon/level/4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Li-vien/CcView/0623bf3bcb50a1a47e112af416db494031d11bd0/public/icon/level/4.png -------------------------------------------------------------------------------- /public/icon/level/5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Li-vien/CcView/0623bf3bcb50a1a47e112af416db494031d11bd0/public/icon/level/5.png -------------------------------------------------------------------------------- /public/icon/level/6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Li-vien/CcView/0623bf3bcb50a1a47e112af416db494031d11bd0/public/icon/level/6.png -------------------------------------------------------------------------------- /public/icon/level/7.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Li-vien/CcView/0623bf3bcb50a1a47e112af416db494031d11bd0/public/icon/level/7.png -------------------------------------------------------------------------------- /public/icon/level/8.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Li-vien/CcView/0623bf3bcb50a1a47e112af416db494031d11bd0/public/icon/level/8.png -------------------------------------------------------------------------------- /public/icon/level/9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Li-vien/CcView/0623bf3bcb50a1a47e112af416db494031d11bd0/public/icon/level/9.png -------------------------------------------------------------------------------- /public/icon/level/first.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Li-vien/CcView/0623bf3bcb50a1a47e112af416db494031d11bd0/public/icon/level/first.png -------------------------------------------------------------------------------- /public/icon/level/great.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Li-vien/CcView/0623bf3bcb50a1a47e112af416db494031d11bd0/public/icon/level/great.png -------------------------------------------------------------------------------- /public/icon/level/second.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Li-vien/CcView/0623bf3bcb50a1a47e112af416db494031d11bd0/public/icon/level/second.png -------------------------------------------------------------------------------- /public/icon/level/third.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Li-vien/CcView/0623bf3bcb50a1a47e112af416db494031d11bd0/public/icon/level/third.png -------------------------------------------------------------------------------- /public/icon/pick.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Li-vien/CcView/0623bf3bcb50a1a47e112af416db494031d11bd0/public/icon/pick.png -------------------------------------------------------------------------------- /public/icon/pick1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Li-vien/CcView/0623bf3bcb50a1a47e112af416db494031d11bd0/public/icon/pick1.png -------------------------------------------------------------------------------- /public/icon/recommend.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Li-vien/CcView/0623bf3bcb50a1a47e112af416db494031d11bd0/public/icon/recommend.png -------------------------------------------------------------------------------- /public/icon/seal.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Li-vien/CcView/0623bf3bcb50a1a47e112af416db494031d11bd0/public/icon/seal.png -------------------------------------------------------------------------------- /public/icon/store.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Li-vien/CcView/0623bf3bcb50a1a47e112af416db494031d11bd0/public/icon/store.png -------------------------------------------------------------------------------- /public/icon/up.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Li-vien/CcView/0623bf3bcb50a1a47e112af416db494031d11bd0/public/icon/up.png -------------------------------------------------------------------------------- /public/icon/vip.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Li-vien/CcView/0623bf3bcb50a1a47e112af416db494031d11bd0/public/icon/vip.png -------------------------------------------------------------------------------- /public/icon/vipt.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Li-vien/CcView/0623bf3bcb50a1a47e112af416db494031d11bd0/public/icon/vipt.png -------------------------------------------------------------------------------- /public/illustrations/techny-searching-the-web-on-tablet.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Li-vien/CcView/0623bf3bcb50a1a47e112af416db494031d11bd0/public/illustrations/techny-searching-the-web-on-tablet.png -------------------------------------------------------------------------------- /public/illustrations/templatePage.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Li-vien/CcView/0623bf3bcb50a1a47e112af416db494031d11bd0/public/illustrations/templatePage.png -------------------------------------------------------------------------------- /public/illustrations/upload.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Li-vien/CcView/0623bf3bcb50a1a47e112af416db494031d11bd0/public/illustrations/upload.png -------------------------------------------------------------------------------- /public/mountains.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Li-vien/CcView/0623bf3bcb50a1a47e112af416db494031d11bd0/public/mountains.jpg -------------------------------------------------------------------------------- /public/vercel.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Li-vien/CcView/0623bf3bcb50a1a47e112af416db494031d11bd0/public/vercel.png -------------------------------------------------------------------------------- /public/wechat.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Li-vien/CcView/0623bf3bcb50a1a47e112af416db494031d11bd0/public/wechat.png -------------------------------------------------------------------------------- /public/wechatGroup.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Li-vien/CcView/0623bf3bcb50a1a47e112af416db494031d11bd0/public/wechatGroup.png -------------------------------------------------------------------------------- /public/wechatReward.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Li-vien/CcView/0623bf3bcb50a1a47e112af416db494031d11bd0/public/wechatReward.png -------------------------------------------------------------------------------- /src/api/application.ts: -------------------------------------------------------------------------------- 1 | import { axiosInstance } from '@/utils/axios'; 2 | import { BaseResult, PageCreate, ApplicationCreate } from '@/types/form'; 3 | 4 | export function creatApp(data: ApplicationCreate, parent: string): Promise { 5 | return axiosInstance.post(`/api/page/creatFolder`, { 6 | ...data, 7 | parent, 8 | type: 1 9 | }); 10 | } 11 | 12 | export function creatPage(data: PageCreate, parent: string): Promise { 13 | return axiosInstance.post(`/api/page/creatPage`, { 14 | ...data, 15 | parent, 16 | type: 0 17 | }); 18 | } 19 | 20 | export function updateApp(data: ApplicationCreate): Promise { 21 | return axiosInstance.post(`/api/page/update`, { 22 | ...data, 23 | type: 1 24 | }); 25 | } 26 | 27 | export function updatePage(data: PageCreate): Promise { 28 | return axiosInstance.post(`/api/page/update`, { 29 | ...data, 30 | type: 0 31 | }); 32 | } 33 | 34 | export function deleteAppOrPage(uuid: string): Promise { 35 | return axiosInstance.get(`/api/page/delete`, { 36 | params: { 37 | uuid 38 | } 39 | }); 40 | } 41 | 42 | export function copyAppOrPage(uuid: string): Promise { 43 | return axiosInstance.get(`/api/page/copy`, { 44 | params: { 45 | uuid 46 | } 47 | }); 48 | } 49 | 50 | export function movePage(origin: string, target: string): Promise { 51 | return axiosInstance.get(`/api/page/move`, { 52 | params: { 53 | target, 54 | origin 55 | } 56 | }); 57 | } 58 | 59 | export function getFolder(uuid: string): Promise { 60 | return axiosInstance.get(`/api/page/getFolder`, { 61 | params: { 62 | uuid 63 | } 64 | }); 65 | } 66 | export function getDetail(uuid: string): Promise { 67 | return axiosInstance.get(`/api/page/getPage`, { 68 | params: { 69 | uuid 70 | } 71 | }); 72 | } -------------------------------------------------------------------------------- /src/api/auth.ts: -------------------------------------------------------------------------------- 1 | import { axiosInstance } from '@/utils/axios'; 2 | import { BaseResult, LoginData } from '@/types/form'; 3 | 4 | // 图形验证码 5 | export function getCaptcha(params: { w?: number, h?: number }): Promise> { 6 | return axiosInstance.get(`/api/auth/captcha?w=${params.w}&h=${params.h}`); 7 | } 8 | 9 | // 登录 10 | export function login(data: LoginData & { captureEncode: string }): Promise { 11 | return axiosInstance.post(`/api/auth/login`, data); 12 | } 13 | 14 | // 注销 15 | export function logout() { 16 | return axiosInstance.get(`/api/auth/logout`); 17 | } -------------------------------------------------------------------------------- /src/api/design.ts: -------------------------------------------------------------------------------- 1 | import { axiosInstance } from '@/utils/axios'; 2 | import { DesignData, BaseResult } from '@/types/form'; 3 | 4 | // 保存设计 5 | export function saveDesign(data: DesignData): Promise { 6 | return axiosInstance.post(`/api/design/saveDesign`, { 7 | ...data 8 | }); 9 | } 10 | // 获取设计 11 | export function getDesign(id: string): Promise { 12 | return axiosInstance.get(`/api/design/getDesign`, { 13 | params: { 14 | id 15 | } 16 | }); 17 | } -------------------------------------------------------------------------------- /src/api/oss.ts: -------------------------------------------------------------------------------- 1 | import { axiosInstance } from '@/utils/axios'; 2 | import { BaseResult } from '@/types/form'; 3 | // 上传文件 4 | export function uploadFiles(files: File[]): Promise { 5 | let size = 0; 6 | let formData = new FormData(); 7 | files.forEach(file => { 8 | size += file.size; 9 | formData.append('files', file); 10 | }); 11 | if (size > 2048 * 1000) { 12 | return Promise.reject({ msg: '文件大小超出限制' }); 13 | } 14 | return axiosInstance.post(`/api/oss/uploadFile`, formData,); 15 | } 16 | 17 | // 下载文件 18 | export function getFile(uuid: string) { 19 | return axiosInstance.get(`/api/oss/getFile`, { 20 | params: { 21 | uuid 22 | } 23 | }); 24 | } -------------------------------------------------------------------------------- /src/api/template.ts: -------------------------------------------------------------------------------- 1 | import { axiosInstance } from '@/utils/axios'; 2 | import { BaseResult } from '@/types/form'; 3 | 4 | // 保存页面模板 5 | export function savaPageTemplate(data: {name: string, cover: string, originId: string}): Promise { 6 | return axiosInstance.post(`/api/template/saveTemplate`, { 7 | ...data, 8 | type: 0 9 | }); 10 | } 11 | // 保存组件模板 12 | export function savaComponentTemplate(data: {name: string, cover: string, templateStr: string}): Promise { 13 | return axiosInstance.post(`/api/template/saveTemplate`, { 14 | ...data, 15 | type: 1 16 | }); 17 | } 18 | // 删除模板 19 | export function deleteTemplate(uuid: string): Promise { 20 | return axiosInstance.get(`/api/template/delete`, { 21 | params: { 22 | uuid 23 | } 24 | }); 25 | } 26 | 27 | // 模板数据 28 | export function getTemplate(uuid: string): Promise { 29 | return axiosInstance.get(`/api/template/getTemplate`, { 30 | params: { 31 | uuid 32 | } 33 | }); 34 | } 35 | 36 | // 模板列表 37 | export function getTemplateList(type = 0): Promise { 38 | return axiosInstance.get(`/api/template/list`, { 39 | params: { 40 | type 41 | } 42 | }); 43 | } 44 | 45 | // 基于模板创建页面 46 | export function createTemplatePage(uuid: string): Promise { 47 | return axiosInstance.get(`/api/template/createTemplatePage`, { 48 | params: { 49 | uuid 50 | } 51 | }); 52 | } 53 | -------------------------------------------------------------------------------- /src/api/user.ts: -------------------------------------------------------------------------------- 1 | import { axiosInstance } from '@/utils/axios'; 2 | import { User } from '@/types'; 3 | import { BaseResult } from '@/types/form'; 4 | export type CreateUser = User | { 5 | phone: string, 6 | avatar: string, 7 | uuid?: string, 8 | password: string 9 | } 10 | // 创建用户 11 | export function createUser(data: CreateUser): Promise { 12 | return axiosInstance.post(`/api/user/creat`, data); 13 | } 14 | 15 | // 创建用户 16 | export function updateUser(data: CreateUser): Promise { 17 | return axiosInstance.post(`/api/user/update`, data); 18 | } 19 | 20 | // 禁用用户 21 | export function updateUserStatus(data: {uuid: string, status: number}): Promise { 22 | return axiosInstance.post(`/api/user/updateUserStatus`, data); 23 | } 24 | 25 | // 获取用户列表 26 | export function getUserList(query: Record): Promise[]}> { 27 | return axiosInstance.post(`/api/user/list`, query); 28 | } 29 | 30 | // 获取用户信息 31 | export function getUserInfo(data = {}): Promise { 32 | return axiosInstance.post(`/api/user/findByUuid`, data); 33 | } 34 | -------------------------------------------------------------------------------- /src/assets/sketch.css: -------------------------------------------------------------------------------- 1 | .sketch_ruler{ 2 | position: relative; 3 | width: 100%; /* scrollbar width */ 4 | height: 100%; 5 | z-index: 3; /* 需要比resizer高 */ 6 | pointer-events: none; 7 | font-size: 12px; 8 | overflow: hidden; 9 | } 10 | .sketch_ruler span { 11 | line-height: 1; 12 | } 13 | 14 | .sketch_corner { 15 | position: absolute; 16 | left: 0; 17 | top: 0; 18 | pointer-events: auto; 19 | cursor: pointer; 20 | transition: all 0.2s ease-in-out; 21 | box-sizing: content-box; 22 | border-right: 1px solid rgba(0,0,0,0); 23 | border-bottom: 1px solid rgba(0,0,0,0); 24 | display: flex; 25 | align-content: center; 26 | justify-content: center; 27 | } 28 | .canvas_ruler { 29 | width: 100%; 30 | height: 100%; 31 | pointer-events: auto; 32 | display: block; 33 | } 34 | .canvas_line { 35 | position: absolute; 36 | } 37 | .canvas_line .action { 38 | position: absolute; 39 | display: flex; 40 | justify-content: center; 41 | align-items: center; 42 | } 43 | .canvas_line .value { 44 | pointer-events: none; 45 | transform: scale(0.83); 46 | } 47 | .canvas_line .del { 48 | cursor: pointer; 49 | padding: 3px 5px; 50 | visibility: hidden; 51 | } 52 | .canvas_line:hover .del{ 53 | visibility: visible; 54 | } 55 | 56 | .indicator { 57 | position: absolute; 58 | pointer-events: none; 59 | } 60 | .indicator .value { 61 | position: absolute; 62 | background: white; 63 | } 64 | 65 | .h_container, .v_container { 66 | position: absolute; 67 | } 68 | .h_container .lines, .v_container .lines{ 69 | pointer-events: none; 70 | } 71 | 72 | .h_container:hover .lines, .v_container:hover .lines{ 73 | pointer-events: auto; 74 | } 75 | 76 | .h_container { 77 | top: 0; 78 | border-bottom: 1px solid rgba(0,0,0,0); 79 | } 80 | .h_container .canvas_line { 81 | height: 100vh; 82 | top: 0; 83 | padding-left: 5px; 84 | border-left: 1px solid rgba(0,0,0,0); 85 | } 86 | 87 | .h_container .action { 88 | transform: translateX(-24px); 89 | } 90 | .h_container .value { 91 | margin-left: 4px; 92 | } 93 | 94 | .h_container .indicator { 95 | top: 0; 96 | height: 100vw; 97 | border-left: 1px solid rgba(0,0,0,0) 98 | } 99 | .h_container .indicator .value { 100 | margin-left: 2px; 101 | margin-top: 4px; 102 | } 103 | 104 | .v_container { 105 | left: 0; 106 | border-right: 1px solid rgba(0,0,0,0); 107 | } 108 | .v_container .canvas_line { 109 | width: 100vw; 110 | left: 0; 111 | padding-top: 5px; 112 | border-top: 1px solid rgba(0,0,0,0); 113 | } 114 | .v_container .action { 115 | transform: translateY(-24px); 116 | flex-direction: column; 117 | } 118 | .v_container .value { 119 | margin-top: 4px; 120 | } 121 | 122 | .v_container .indicator { 123 | width: 100vw; 124 | border-bottom: 1px solid rgba(0,0,0,0) 125 | } 126 | .v_container .indicator .value { 127 | margin-left: 2px; 128 | margin-top: -5px; 129 | transform-origin: 0 0; 130 | transform: rotate(-90deg); 131 | } 132 | 133 | @keyframes closeMenu { 134 | from { 135 | opacity: 1; 136 | transform: scale(1); 137 | } 138 | to { 139 | opacity: 0; 140 | transform: scale(0.8); 141 | } 142 | } 143 | 144 | @keyframes openMenu { 145 | from { 146 | opacity: 0; 147 | transform: scale(0.8); 148 | } 149 | to { 150 | opacity: 1; 151 | transform: scale(1); 152 | } 153 | } -------------------------------------------------------------------------------- /src/components/Application/ApplicationItem.tsx: -------------------------------------------------------------------------------- 1 | import FolderIcon from '@/components/Svg/FolderIcon'; 2 | import { ApplicationData, MoreMenuEvent } from '@/types'; 3 | import { Avatar, Tooltip } from '@mui/material'; 4 | import MoreIcon from '@/components/MoreIcon'; 5 | import { useRouter } from 'next/router'; 6 | import { dateFormat } from '@/utils/common'; 7 | 8 | interface ApplicationItemProp { 9 | data: ApplicationData 10 | onEvent: (params: MoreMenuEvent) => void 11 | } 12 | 13 | export default function ApplicationItem({ data, onEvent }: ApplicationItemProp) { 14 | const router = useRouter(); 15 | function jumpToAppList() { 16 | router.push(`/application/${data.uuid}`); 17 | } 18 | return ( 19 |
20 |
21 |
22 |
23 | 24 |
25 | {data.title} 26 | {dateFormat(data.createDate, 'Y-M-D h:m')} 27 |
28 |
29 |
30 | 31 | 32 | 33 | 34 |
35 |
36 |
37 |
38 | ); 39 | } -------------------------------------------------------------------------------- /src/components/Application/PageItem.tsx: -------------------------------------------------------------------------------- 1 | import Image from 'next/image'; 2 | import { MoreMenuEvent, PageData } from '@/types'; 3 | import { useState } from 'react'; 4 | import { Avatar, Tooltip, Button } from '@mui/material'; 5 | import { DesignServices } from '@mui/icons-material'; 6 | import MoreIcon from '@/components/MoreIcon'; 7 | import { dateFormat } from '@/utils/common'; 8 | import { useRouter } from 'next/router'; 9 | 10 | interface PageItemProp { 11 | data: PageData 12 | onEvent: (params: MoreMenuEvent) => void 13 | } 14 | 15 | export default function PageItem({ data, onEvent }: PageItemProp) { 16 | const router = useRouter(); 17 | const [showButton, setShowButton] = useState(false); 18 | function toDesign() { 19 | router.push(`/design/${data.uuid}`); 20 | } 21 | return ( 22 |
23 |
24 |
25 | 26 | 27 | 28 | {data.title} 29 | 30 | 31 | 32 |
33 |
34 | 页面 44 |
{ setShowButton(false); }} onMouseEnter={() => { setShowButton(true); }}> 45 | { 46 | showButton && 49 | } 50 |
51 |
52 |
53 | 54 | 55 | {dateFormat(data.createDate, 'Y-M-D h:m')} 56 | 57 | 58 | 59 | 60 | 61 |
62 |
63 |
64 | ); 65 | } -------------------------------------------------------------------------------- /src/components/Banner/Banner.module.css: -------------------------------------------------------------------------------- 1 | .canvas { 2 | @apply absolute top-0 bottom-0 m-auto right-140 left-0 3 | } 4 | 5 | .video { 6 | @apply hidden 7 | } -------------------------------------------------------------------------------- /src/components/Copyright/Copyright.module.css: -------------------------------------------------------------------------------- 1 | .copyright { 2 | @apply absolute bottom-0 left-0 right-0 text-stone-400 pb-2 3 | } -------------------------------------------------------------------------------- /src/components/Copyright/index.tsx: -------------------------------------------------------------------------------- 1 | import Typography from '@mui/material/Typography'; 2 | import { Link } from '@mui/material'; 3 | import styles from './Copyright.module.css'; 4 | 5 | export default function Copyright() { 6 | return ( 7 | 8 | 9 | 京ICP备2023019680号 10 |    11 | 12 | Icons by icons8 13 | 14 | 15 | ); 16 | } 17 | -------------------------------------------------------------------------------- /src/components/Dialog/CreateDialog.tsx: -------------------------------------------------------------------------------- 1 | import { Dialog, DialogTitle } from '@mui/material'; 2 | import FormFactory from '@/components/Form/FormFactory'; 3 | import CreateApp from '@/config/form/CreateApp'; 4 | import CreatePage from '@/config/form/CreatePage'; 5 | import { EventParams } from '@/types'; 6 | interface CreateDialogProp { 7 | open: boolean 8 | isUpdate?: boolean 9 | defaultVal?: Record 10 | type: string 11 | defaultValue?: Record 12 | onClose: () => void 13 | onSubmit: (data: Record, refs: Record) => void 14 | } 15 | export default function CreateDialog(props: CreateDialogProp) { 16 | const { onClose, onSubmit, type, open, isUpdate, defaultVal } = props; 17 | const handleClose = () => { 18 | onClose(); 19 | }; 20 | // 表单内的事件统一处理 21 | function onEvent(eventParams: EventParams) { 22 | const { type, data, refs } = eventParams; 23 | // 提交事件 24 | if (type === 'submit') { 25 | onSubmit(data, refs); 26 | } 27 | if (type === 'cancel') { 28 | onClose(); 29 | } 30 | } 31 | return ( 32 | 33 | {isUpdate ? '更新' : '创建'}{ type === 'page' ? '页面' : '应用' } 34 |
35 | 41 |
42 |
43 | ); 44 | } -------------------------------------------------------------------------------- /src/components/Dialog/CreateUserDialog.tsx: -------------------------------------------------------------------------------- 1 | import { Dialog, DialogTitle } from '@mui/material'; 2 | import FormFactory from '@/components/Form/FormFactory'; 3 | import { CreateUser, UpdateUser } from '@/config/form/CreateUser'; 4 | import { EventParams } from '@/types'; 5 | interface CreateUserDialogProp { 6 | open: boolean 7 | isUpdate?: boolean 8 | defaultVal?: Record 9 | defaultValue?: Record 10 | onClose: () => void 11 | onSubmit: (data: Record, refs: Record) => void 12 | } 13 | export default function CreateDialog(props: CreateUserDialogProp) { 14 | const { onClose, onSubmit, open, isUpdate, defaultVal } = props; 15 | const handleClose = () => { 16 | onClose(); 17 | }; 18 | // 表单内的事件统一处理 19 | function onEvent(eventParams: EventParams) { 20 | const { type, data, refs } = eventParams; 21 | // 提交事件 22 | if (type === 'submit') { 23 | onSubmit(data, refs); 24 | } 25 | if (type === 'cancel') { 26 | onClose(); 27 | } 28 | } 29 | return ( 30 | 31 | {isUpdate ? '更新用户信息' : '创建用户'} 32 |
33 | 38 |
39 |
40 | ); 41 | } -------------------------------------------------------------------------------- /src/components/Dialog/MoveDialog.tsx: -------------------------------------------------------------------------------- 1 | import { Dialog, DialogTitle, DialogContent, List, ListItem, ListItemButton, ListItemIcon, ListItemText } from '@mui/material'; 2 | import { Check } from '@mui/icons-material'; 3 | import { getFolder } from '@api/application'; 4 | import { useEffect, useState } from 'react'; 5 | import { ApplicationData, PageData } from '@/types'; 6 | import FolderIcon from '@/components/Svg/FolderIcon'; 7 | 8 | interface MoveDialogProp { 9 | open: boolean 10 | parent: string 11 | onClose: () => void 12 | onSubmit: (target: string) => void 13 | } 14 | type AppDataType = (PageData | ApplicationData)[] 15 | 16 | export default function MoveDialog(props: MoveDialogProp) { 17 | const { onClose, onSubmit, open, parent } = props; 18 | const [appList, setAppList] = useState([]); 19 | function formatData(data: AppDataType) { 20 | const appData: ApplicationData[] = []; 21 | data.forEach(item => { 22 | if (item.type === 1) { 23 | appData.push(item as ApplicationData); 24 | } 25 | }); 26 | setAppList(appData); 27 | } 28 | const handleClose = () => { 29 | onClose(); 30 | }; 31 | const handleClick = (uuid: string) => () => { 32 | onSubmit(uuid); 33 | }; 34 | useEffect(() => { 35 | getFolder('').then(({ data }) => { 36 | formatData(data as AppDataType); 37 | }); 38 | }, []); 39 | return ( 40 | 41 | 选择目标应用 42 | 43 | 44 | {appList.map(app => { 45 | return ( 46 | : '' 50 | } 51 | disablePadding 52 | > 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | ); 61 | })} 62 | 63 | 64 | 65 | ); 66 | } -------------------------------------------------------------------------------- /src/components/Form/FormFactory.tsx: -------------------------------------------------------------------------------- 1 | import { 2 | FormControl 3 | } from '@mui/material'; 4 | import { useForm, Control } from 'react-hook-form'; 5 | import FormItem from '@/components/Form/FormItem'; 6 | import { EventParams, FormConfig, FormConfigItem } from '@/types'; 7 | import { useRef } from 'react'; 8 | interface FormCreateProp { 9 | itemClass?: string 10 | rowClass?: string 11 | isUpdate?:boolean 12 | defaultVal?:Record 13 | config: FormConfig 14 | onEvent: (para : EventParams) => void 15 | } 16 | const NotField = ['Capture', 'SubmitButton', 'CancelButton']; 17 | // 根据配置文件取出 rules 和 default 18 | function initFormConfig(config: FormConfig, defaultVal?: Record) { 19 | if (defaultVal) return [defaultVal]; 20 | const defaultValues = {}; 21 | const rowData = config.rowData; 22 | rowData.forEach(row => { 23 | row.forEach(formItem => { 24 | const { name, defaultValue, component = '' } = formItem; 25 | if (!NotField.includes(component)) { 26 | Object.assign(defaultValues, { 27 | [name]: defaultValue 28 | }); 29 | } 30 | }); 31 | }); 32 | return [defaultValues]; 33 | } 34 | 35 | // 生成每一行的表单 36 | function genFormRow(rowList : FormConfigItem[], control: Control, eventHandler: (type: string, data : Record) => void, props: FormCreateProp, key: string, refsMap: Record) { 37 | const { config: { rowClass = 'mb-6' }, isUpdate = false } = props; 38 | 39 | const rowElement = rowList.map((formItem, index) => { 40 | const container = useRef(null); 41 | Object.assign(refsMap, { 42 | [formItem.name]: container 43 | }); 44 | return ( 45 |
46 | 47 |
48 | ); 49 | }); 50 | return ( 51 |
52 | { 53 | rowElement 54 | } 55 |
56 | ); 57 | } 58 | 59 | export default function FormFactory(props: FormCreateProp) { 60 | const { config, defaultVal, isUpdate = false } = props; 61 | const [defaultValues] = initFormConfig(config, defaultVal); 62 | const refsMap = {}; 63 | // 初始化 react-hook-form 64 | const { control, resetField, handleSubmit } = useForm({ defaultValues }); 65 | 66 | // 表单事件代理 67 | const eventHandler = (type: string, data : Record) => { 68 | const { onEvent } = props; 69 | onEvent && onEvent({ 70 | isUpdate, 71 | type, 72 | data, 73 | form: { 74 | control, 75 | resetField 76 | }, 77 | refs: refsMap 78 | }); 79 | }; 80 | const fromChild = config.rowData.map((rowConfig, index) => { 81 | return genFormRow(rowConfig, control, eventHandler, props, `from-row-${index}`, refsMap); 82 | }); 83 | return { 86 | eventHandler('submit', data); 87 | })} 88 | className='w-full' 89 | > 90 | { 91 | fromChild 92 | } 93 | ; 94 | } -------------------------------------------------------------------------------- /src/components/Form/FormItem.tsx: -------------------------------------------------------------------------------- 1 | import { Controller, Control } from 'react-hook-form'; 2 | import { 3 | FormControl, 4 | FormHelperText, 5 | TooltipProps, 6 | OutlinedInputProps 7 | } from '@mui/material'; 8 | import { FormItemTempProp, FormConfigItem } from '@/types'; 9 | import PasswordItem from '@/components/Form/temp/PasswordItem'; 10 | import InputItem from '@/components/Form/temp/InputItem'; 11 | import SwitchItem from '@/components/Form/temp/SwitchItem'; 12 | import SelectItem from '@/components/Form/temp/SelectItem'; 13 | import SliderItem from '@/components/Form/temp/SliderItem'; 14 | import ButtonGroupItem from '@/components/Form/temp/ButtonGroupItem'; 15 | import CaptureItem from '@/components/Form/temp/CaptureItem'; 16 | import AvatarItem from '@/components/Form/temp/AvatarItem'; 17 | import SubmitButton from '@/components/Form/temp/SubmitButton'; 18 | import CancelButton from '@/components/Form/temp/CancelButton'; 19 | import HelperText from '@/components/Form/temp/HelperText'; 20 | import { forwardRef } from 'react'; 21 | interface FormItemProp { 22 | config: FormConfigItem 23 | control: Control 24 | isUpdate: boolean 25 | eventHandler:(type: string, data : Record) => void 26 | } 27 | 28 | // 根据类型匹配不同的输入框 29 | const MatchInputType = forwardRef((props: FormItemTempProp, ref) => { 30 | const { config } = props; 31 | const { properties } = config; 32 | const { type } = properties as OutlinedInputProps; 33 | switch (type) { 34 | case 'password': 35 | return ; 36 | case 'switch': 37 | return ; 38 | case 'select': 39 | return ; 40 | case 'slider': 41 | return ; 42 | case 'buttonGroup': 43 | return ; 44 | case 'Avatar': 45 | return ; 46 | default: 47 | return ; 48 | } 49 | }); 50 | MatchInputType.displayName = 'MatchInputType'; 51 | const FormItem = forwardRef((props: FormItemProp, ref) => { 52 | const { config, control, eventHandler, isUpdate = false } = props; 53 | const { name, component, properties, rules } = config; 54 | switch (component) { 55 | case 'Capture': { 56 | return ; 57 | } 58 | case 'SubmitButton': { 59 | return ; 60 | } 61 | case 'CancelButton': { 62 | return ; 63 | } 64 | default: { 65 | return ( 66 | ( 71 | 72 | 73 | 74 | 75 | 76 | 77 | )} 78 | /> 79 | ); 80 | } 81 | } 82 | }); 83 | 84 | FormItem.displayName = 'FormItem'; 85 | export default FormItem; -------------------------------------------------------------------------------- /src/components/Form/temp/AvatarItem.tsx: -------------------------------------------------------------------------------- 1 | import { 2 | Avatar, 3 | Tooltip, 4 | Button, 5 | InputLabel, 6 | IconButton 7 | } from '@mui/material'; 8 | import { forwardRef, ChangeEvent, useState, useEffect } from 'react'; 9 | import { FormItemTempProp } from '@/types'; 10 | import { uploadFiles } from '@api/oss'; 11 | import { useSnackbar } from 'notistack'; 12 | import { Domain } from '@/config/constant'; 13 | const defaultHead = ['/avatar/b1.png', '/avatar/b2.png', '/avatar/b3.png', '/avatar/g1.png', '/avatar/g2.png', '/avatar/g3.png']; 14 | // 头像 15 | const AvatarItem = forwardRef((props: FormItemTempProp, ref) => { 16 | const { config: { properties }, field, fieldState } = props; 17 | const [cover, setCover] = useState(field.value); 18 | const { enqueueSnackbar } = useSnackbar(); 19 | 20 | function handleFIleUpload(event: ChangeEvent) { 21 | const inputTarget = event.target; 22 | const { files } = inputTarget; 23 | if (files && files.length > 0) { 24 | const file = files[0]; 25 | uploadFiles([file]) 26 | .then(res => { 27 | if (res?.isOk) { 28 | const { data } = res; 29 | const [imageId] = data as string[]; 30 | const imageUrl = `${Domain.baseOSS}/${imageId}.${file.type.replace('image/', '')}`; 31 | setCover(imageUrl); 32 | } else { 33 | enqueueSnackbar(`头像上传失败:${res?.msg}`, { 34 | variant: 'error' 35 | }); 36 | } 37 | }) 38 | .catch(e => { 39 | const { msg = '上传异常' } = e || {}; 40 | enqueueSnackbar(`头像上传失败: ${msg}`, { 41 | variant: 'error' 42 | }); 43 | }); 44 | } 45 | } 46 | const defaultAvatar = defaultHead.map((item, index) => { 47 | return setCover(item)} className={`${item === cover ? 'border ' : ''} w-8 h-8 border-solid border-amber-500 mr-1`} sx={{ p: 0 }}> 48 | 49 | ; 50 | }); 51 | useEffect(() => { 52 | if (!cover) { 53 | setCover(defaultHead[0]); 54 | } 55 | }, []); 56 | useEffect(() => { 57 | field.onChange(cover); 58 | }, [cover]); 59 | return ( 60 |
61 |
62 | 63 | 67 | 68 |
69 |
70 |
71 | {defaultAvatar} 72 |
73 |
74 |
75 | ); 76 | }); 77 | AvatarItem.displayName = 'AvatarItem'; 78 | export default AvatarItem; -------------------------------------------------------------------------------- /src/components/Form/temp/ButtonGroupItem.tsx: -------------------------------------------------------------------------------- 1 | import { 2 | InputLabel, 3 | ToggleButton, 4 | ToggleButtonGroup 5 | } from '@mui/material'; 6 | import { forwardRef, MouseEvent } from 'react'; 7 | import { FormItemTempProp } from '@/types'; 8 | import IconGen from '@/components/IconGen'; 9 | // 按钮组 10 | const ButtonGroupItem = forwardRef((props: FormItemTempProp, ref) => { 11 | const { config: { properties }, field, fieldState } = props; 12 | const { type, options = [], ...buttonGroupProps } = properties; 13 | 14 | const handleChange = (event: MouseEvent, newValue: string | null,) => { 15 | field.onChange(newValue); 16 | }; 17 | 18 | return ( 19 | <> 20 | {buttonGroupProps.label} 21 | 26 | { 27 | (options as Record[])?.map((option, index) => { 28 | return ( 29 | { 30 | option.icon && 31 | } 32 | {option.title} 33 | ); 34 | }) 35 | } 36 | 37 | 38 | ); 39 | }); 40 | ButtonGroupItem.displayName = 'ButtonGroupItem'; 41 | export default ButtonGroupItem; -------------------------------------------------------------------------------- /src/components/Form/temp/CancelButton.tsx: -------------------------------------------------------------------------------- 1 | import { 2 | Button 3 | } from '@mui/material'; 4 | import { useState, forwardRef, useImperativeHandle } from 'react'; 5 | interface CancelButtonProp { 6 | eventHandler:(type: string, data : Record) => void, 7 | config: Record 8 | } 9 | const CancelButton = forwardRef(({ eventHandler, config }: CancelButtonProp, ref) => { 10 | const [loginState, setLoginState] = useState(false); 11 | const { buttonText = '取消' } = config; 12 | function handleCancel() { 13 | eventHandler('cancel', {}); 14 | } 15 | return ( 16 | 28 | ); 29 | }); 30 | CancelButton.displayName = 'SubmitButton'; 31 | export default CancelButton; -------------------------------------------------------------------------------- /src/components/Form/temp/CaptureItem.tsx: -------------------------------------------------------------------------------- 1 | import { 2 | Tooltip, 3 | TooltipProps 4 | } from '@mui/material'; 5 | import { useSnackbar } from 'notistack'; 6 | import { useState, useEffect, forwardRef, useImperativeHandle } from 'react'; 7 | import { getCaptcha } from '@api/auth'; 8 | import { Constant } from '@/config/constant'; 9 | import { setStorage } from '@/utils/common'; 10 | interface CaptureItemProp { 11 | config:TooltipProps, 12 | eventHandler:(type: string, data : Record) => void 13 | } 14 | const CaptureItem = forwardRef(({ config, eventHandler }: CaptureItemProp, ref) => { 15 | const [captcha, setCaptcha] = useState(''); 16 | const { enqueueSnackbar } = useSnackbar(); 17 | 18 | const refreshCaptcha = () => { 19 | eventHandler('refreshCaptcha', {}); 20 | getCaptcha({ 21 | w: 170, 22 | h: 44 23 | }).then(res => { 24 | if (res.isOk) { 25 | setStorage(Constant.CaptureEncode, res.data.captureEncode); 26 | setCaptcha(res.data.image); 27 | } else { 28 | enqueueSnackbar(`获取验证码失败:${res.msg}`, { 29 | variant: 'warning' 30 | }); 31 | } 32 | }) 33 | .catch(error => { 34 | enqueueSnackbar(`获取验证码失败:${error}`, { 35 | variant: 'error' 36 | }); 37 | }); 38 | }; 39 | useEffect(() => { 40 | refreshCaptcha(); 41 | }, []); 42 | useImperativeHandle(ref, () => ({ 43 | refreshCaptcha 44 | })); 45 | return ( 46 | 47 |
49 |
50 | ); 51 | }); 52 | CaptureItem.displayName = 'CaptureItem'; 53 | export default CaptureItem; -------------------------------------------------------------------------------- /src/components/Form/temp/HelperText.tsx: -------------------------------------------------------------------------------- 1 | import WarningIcon from '@/components/Svg/WarningIcon'; 2 | 3 | export default function HelperText({ message }: { message: string }) { 4 | if (message) { 5 | return ( 6 | <> 7 | 8 | {message} 9 | 10 | ); 11 | } 12 | return null; 13 | } -------------------------------------------------------------------------------- /src/components/Form/temp/InputItem.tsx: -------------------------------------------------------------------------------- 1 | import { 2 | TextField 3 | } from '@mui/material'; 4 | import { forwardRef } from 'react'; 5 | import { FormItemTempProp } from '@/types'; 6 | // 基础输入框 7 | const InputItem = forwardRef((props: FormItemTempProp, ref) => { 8 | const { config: { properties }, field, fieldState } = props; 9 | return ( 10 | 15 | ); 16 | }); 17 | InputItem.displayName = 'BaseInputItem'; 18 | export default InputItem; -------------------------------------------------------------------------------- /src/components/Form/temp/PasswordItem.tsx: -------------------------------------------------------------------------------- 1 | import { 2 | InputAdornment, 3 | IconButton, 4 | TextField, 5 | TextFieldProps 6 | } from '@mui/material'; 7 | import EyesIcon from '@/components/Svg/EyesIcon'; 8 | import { FormItemTempProp } from '@/types'; 9 | import { useState, forwardRef } from 'react'; 10 | 11 | const PasswordItem = forwardRef((props: FormItemTempProp, ref) => { 12 | const [showPassword, setShowPassword] = useState(false); 13 | const { config: { properties }, field, fieldState } = props; 14 | const { type, ...othProps } = (properties as TextFieldProps); 15 | return ( 16 | 23 | setShowPassword(!showPassword)} 26 | onMouseDown={event => event.preventDefault()} 27 | edge='end' 28 | > 29 | 30 | 31 | 32 | }} 33 | /> 34 | ); 35 | }); 36 | PasswordItem.displayName = 'PasswordItem'; 37 | export default PasswordItem; -------------------------------------------------------------------------------- /src/components/Form/temp/SelectItem.tsx: -------------------------------------------------------------------------------- 1 | import { 2 | InputLabel, 3 | Select, 4 | MenuItem 5 | } from '@mui/material'; 6 | import { forwardRef } from 'react'; 7 | import { FormItemTempProp } from '@/types'; 8 | // 基础开关选择 9 | const SelectItem = forwardRef((props: FormItemTempProp, ref) => { 10 | const { config: { properties }, field, fieldState } = props; 11 | const { type, options, ...selectProps } = properties; 12 | return ( 13 | <> 14 | {selectProps.label} 15 | 27 | 28 | 29 | ); 30 | }); 31 | SelectItem.displayName = 'BaseSelectItem'; 32 | export default SelectItem; -------------------------------------------------------------------------------- /src/components/Form/temp/SliderItem.tsx: -------------------------------------------------------------------------------- 1 | import { 2 | InputLabel, 3 | Slider 4 | } from '@mui/material'; 5 | import { forwardRef } from 'react'; 6 | import { FormItemTempProp } from '@/types'; 7 | // 基础滑块组件 8 | const SliderItem = forwardRef((props: FormItemTempProp, ref) => { 9 | const { config: { properties }, field, fieldState } = props; 10 | const { type, options, ...selectProps } = properties; 11 | return ( 12 | <> 13 | {selectProps.label} 14 | 19 | 20 | ); 21 | }); 22 | SliderItem.displayName = 'BaseSliderItem'; 23 | export default SliderItem; -------------------------------------------------------------------------------- /src/components/Form/temp/SubmitButton.tsx: -------------------------------------------------------------------------------- 1 | import { 2 | Button, 3 | LinearProgress 4 | } from '@mui/material'; 5 | import { useState, forwardRef, useImperativeHandle } from 'react'; 6 | interface SubmitButtonProp { 7 | eventHandler:(type: string, data : Record) => void, 8 | config: Record 9 | isUpdate: boolean 10 | } 11 | const SubmitButton = forwardRef(({ eventHandler, config, isUpdate }: SubmitButtonProp, ref) => { 12 | const [loginState, setLoginState] = useState(false); 13 | let { buttonText = ['登录中...', '登录'] } = config; 14 | if (isUpdate) buttonText = ['更新中...', '更新']; 15 | const [ingText, defaultText] = buttonText; 16 | function disableButton() { 17 | setLoginState(true); 18 | } 19 | function enableButton() { 20 | setLoginState(false); 21 | } 22 | useImperativeHandle(ref, () => ({ 23 | disableButton, 24 | enableButton 25 | })); 26 | return ( 27 | 43 | ); 44 | }); 45 | SubmitButton.displayName = 'SubmitButton'; 46 | export default SubmitButton; -------------------------------------------------------------------------------- /src/components/Form/temp/SwitchItem.tsx: -------------------------------------------------------------------------------- 1 | import { 2 | Switch, 3 | FormControlLabel 4 | } from '@mui/material'; 5 | import { forwardRef, ChangeEvent } from 'react'; 6 | import { FormItemTempProp } from '@/types'; 7 | // 基础开关 8 | const SwitchItem = forwardRef((props: FormItemTempProp, ref) => { 9 | const { config: { properties }, field, fieldState } = props; 10 | const { label, type, labelPlacement, ...switchProps } = properties; 11 | 12 | const handleChange = (event: ChangeEvent) => { 13 | field.onChange(!field.value); 14 | }; 15 | return ( 16 | } 18 | label={properties.label} 19 | labelPlacement={labelPlacement} 20 | /> 21 | ); 22 | }); 23 | SwitchItem.displayName = 'BaseSwitchItem'; 24 | export default SwitchItem; -------------------------------------------------------------------------------- /src/components/HeadMenu/index.tsx: -------------------------------------------------------------------------------- 1 | import { IconButton, Tooltip } from '@mui/material'; 2 | import { Microwave, Visibility } from '@mui/icons-material'; 3 | import { useAppSelector, useAppDispatch } from '@/store'; 4 | import { switchAttr } from '@/store/slice/Global'; 5 | import { useRouter } from 'next/router'; 6 | 7 | export default function HeadMenu(props: Record) { 8 | const router = useRouter(); 9 | const { className } = props; 10 | const showAttr = useAppSelector(state => state.global.showAttr); 11 | const designId = useAppSelector(state => state.global.designId); 12 | const dispatch = useAppDispatch(); 13 | 14 | function changeShowAttr() { 15 | dispatch(switchAttr(!showAttr)); 16 | } 17 | 18 | function goPreview() { 19 | window.open(`${document.location.origin}/preview/${designId}`, '_blank'); 20 | } 21 | return ( 22 |
23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 |
34 | ); 35 | } -------------------------------------------------------------------------------- /src/components/Header/Header.module.css: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Li-vien/CcView/0623bf3bcb50a1a47e112af416db494031d11bd0/src/components/Header/Header.module.css -------------------------------------------------------------------------------- /src/components/IconGen/index.tsx: -------------------------------------------------------------------------------- 1 | import { Edit, Ballot, Person, Home, Source, Settings, Storefront, LocalGroceryStore } from '@mui/icons-material'; 2 | 3 | type IconGenProp = { 4 | className?: string, 5 | icon: string 6 | } 7 | 8 | export default function IconGen({ icon, className }: IconGenProp) { 9 | switch (icon) { 10 | case 'Edit': 11 | return ; 12 | case 'Home': 13 | return ; 14 | case 'Source': 15 | return ; 16 | case 'Storefront': 17 | return ; 18 | case 'Person': 19 | return ; 20 | case 'Ballot': 21 | return ; 22 | case 'LocalGroceryStore': 23 | return ; 24 | case 'Settings': 25 | return ; 26 | default: 27 | return ; 28 | } 29 | } -------------------------------------------------------------------------------- /src/components/Layout/index.tsx: -------------------------------------------------------------------------------- 1 | import Header from '@/components/Header'; 2 | import MenuList from '@/components/MenuList'; 3 | import PageLoading from '@/components/PageLoading'; 4 | import { UIEvent, useState, ReactNode, useEffect, WheelEvent } from 'react'; 5 | import { delay } from '@/utils/common'; 6 | interface LayoutProps { 7 | children?: ReactNode 8 | hideMenu?: boolean 9 | hideHead?: boolean 10 | } 11 | export default function Layout({ children, hideMenu = false, hideHead = false }: LayoutProps) { 12 | const [smallHead, setSmallHead] = useState(false); 13 | const [pageLoading, setPageLoading] = useState(false); 14 | const handleScroll = (event: UIEvent) => { 15 | if (event.currentTarget.scrollTop > 10) { 16 | setSmallHead(true); 17 | } else { 18 | setSmallHead(false); 19 | } 20 | }; 21 | // 给左侧菜单动画让时间 22 | useEffect(() => { 23 | setPageLoading(true); 24 | delay(400).then(() => { 25 | setPageLoading(false); 26 | }); 27 | }, [hideMenu]); 28 | return ( 29 |
30 |
31 | { 32 | hideMenu || 33 | } 34 |
35 | { 36 | hideHead ||
37 | } 38 |
39 | { 40 | pageLoading ? : children 41 | } 42 |
43 |
44 |
45 |
46 | ); 47 | } -------------------------------------------------------------------------------- /src/components/Logo/Logo.module.css: -------------------------------------------------------------------------------- 1 | .logo { 2 | @apply inline-block absolute top-0 left-0; 3 | @apply transition-all duration-200 z-30; 4 | } -------------------------------------------------------------------------------- /src/components/Logo/index.tsx: -------------------------------------------------------------------------------- 1 | import styles from './Logo.module.css'; 2 | import { gsap } from 'gsap'; 3 | import type GSAPTimeline from 'gsap'; 4 | import { useEffect, useRef } from 'react'; 5 | import { useAppSelector } from '@/store'; 6 | import useThemeSwitch from '@/components/Design/hook/useThemeSwitch'; 7 | 8 | interface LogoProps { 9 | style?: Record 10 | className?: string 11 | hideOth?: boolean 12 | } 13 | 14 | // 学习: https://dev.to/franklin030601/animations-with-gsap-react-1nok 15 | export default function Logo({ style, className, hideOth = false }: LogoProps) { 16 | const { theme } = useThemeSwitch(); 17 | const bgColor = theme === 'dark' ? '#404040' : '#9ca3af'; 18 | const logo = useRef(null); 19 | const tl = useRef(); 20 | useEffect(() => { 21 | let ctx = gsap.context(() => { 22 | tl.current = gsap.timeline({ 23 | repeat: -1, 24 | yoyo: true 25 | }).to('.blob', { 26 | scale: 1.1, duration: 4, ease: 'none' 27 | }) 28 | .to('.blob', { 29 | scale: 1, duration: 4, ease: 'none' 30 | }) 31 | .to('.blob', { 32 | scale: 0.9, duration: 4, ease: 'none' 33 | }); 34 | }, logo); 35 | return () => ctx.revert(); 36 | }, []); 37 | return ( 38 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | Cc 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 62 | 63 | ); 64 | } -------------------------------------------------------------------------------- /src/components/Lottie/index.tsx: -------------------------------------------------------------------------------- 1 | import { useEffect, useRef } from 'react'; 2 | import lottie from 'lottie-web'; 3 | interface LottieProp { 4 | className?: string 5 | path?: string 6 | autoPlay?: boolean 7 | play?: boolean 8 | } 9 | export default function Lottie({ className = '', path = '', autoPlay = true, play = false } : LottieProp) { 10 | const container = useRef(null); 11 | useEffect(() => { 12 | lottie.loadAnimation({ 13 | container: container.current as HTMLDivElement, // the dom element that will contain the animation 14 | renderer: 'svg', 15 | loop: true, 16 | autoplay: autoPlay, 17 | path 18 | }); 19 | return () => { lottie.destroy(); }; 20 | }, []); 21 | useEffect(() => { 22 | if (autoPlay) return; 23 | if (play) { 24 | lottie.play(); 25 | } else { 26 | lottie.pause(); 27 | } 28 | }, [play]); 29 | return ( 30 |
31 | ); 32 | } -------------------------------------------------------------------------------- /src/components/MenuList/Menu.module.css: -------------------------------------------------------------------------------- 1 | .list_container { 2 | @apply h-full z-20 flex flex-col relative transition-all duration-200; 3 | @apply border-r border-0 border-solid; 4 | } 5 | 6 | .MuiListItemText-root span { 7 | @apply text-xs 8 | } 9 | 10 | .MuiSvgIcon-root { 11 | @apply w-5 h-5 12 | } -------------------------------------------------------------------------------- /src/components/MenuList/MenuItem.tsx: -------------------------------------------------------------------------------- 1 | import { MenuType } from '@/types'; 2 | import { Fragment } from 'react'; 3 | import IconGen from '@/components/IconGen'; 4 | import SubMenuItem from './SubMenuItem'; 5 | import { ExpandMore } from '@mui/icons-material'; 6 | import { ListItemButton, ListItemText, ListItemIcon } from '@mui/material'; 7 | 8 | interface MenuItemProp { 9 | data: MenuType[] 10 | isSmall: boolean // 菜单折叠 11 | activeKey: string 12 | expandMap: Record 13 | onMouseEnter: Function 14 | onMenuClick: Function 15 | } 16 | 17 | export default function MenuItem({ isSmall, activeKey, data, expandMap, onMouseEnter, onMenuClick }: MenuItemProp) { 18 | return data.map(menu => ( 19 | 20 | { 24 | onMouseEnter(event, menu); 25 | }} 26 | onClick={() => { 27 | onMenuClick(menu); 28 | }}> 29 | 30 | { 31 | menu.icon && 32 | } 33 | 34 | {menu.title} 36 | { 37 | ((menu?.child?.length || 0) > 0 && isSmall) 38 | && 39 | } 40 | 41 | { 42 | ((menu?.child?.length || 0) > 0 && isSmall) && 43 | } 44 | 45 | )); 46 | } -------------------------------------------------------------------------------- /src/components/MenuList/PopMenuItem.tsx: -------------------------------------------------------------------------------- 1 | import { MenuType } from '@/types'; 2 | import { List, ListItemButton, ListItemText, Menu } from '@mui/material'; 3 | 4 | interface PopMenuItemProp { 5 | data: MenuType | null 6 | anchorEl: null | HTMLElement 7 | activeKey: string 8 | onMenuClick: Function 9 | onClose: () => void 10 | } 11 | 12 | export default function PopMenuItem({ anchorEl, activeKey, data, onMenuClick, onClose }: PopMenuItemProp) { 13 | return 28 | 29 | { 30 | data && data?.child?.map(subMenu => ( 31 | { 32 | onMenuClick(subMenu); 33 | }}> 34 | {subMenu.title} 35 | 36 | )) 37 | } 38 | 39 | ; 40 | } -------------------------------------------------------------------------------- /src/components/MenuList/SubMenuItem.tsx: -------------------------------------------------------------------------------- 1 | import { MenuType } from '@/types'; 2 | import { List, ListItemButton, ListItemText, Collapse } from '@mui/material'; 3 | 4 | interface SubMenuItemProp { 5 | data: MenuType 6 | expandMap: Record 7 | activeKey: string 8 | onMenuClick: Function 9 | } 10 | 11 | export default function SubMenuItem({ expandMap, activeKey, data, onMenuClick }: SubMenuItemProp) { 12 | return 13 | 14 | { 15 | data.child?.map(subMenu => ( 16 | { 18 | onMenuClick(subMenu); 19 | }}> 20 | {subMenu.title} 21 | 22 | )) 23 | } 24 | 25 | ; 26 | } -------------------------------------------------------------------------------- /src/components/MenuList/index.tsx: -------------------------------------------------------------------------------- 1 | import { useState, MouseEvent, useEffect } from 'react'; 2 | import { KeyboardDoubleArrowLeft } from '@mui/icons-material'; 3 | import { List, IconButton } from '@mui/material'; 4 | import Logo from '@/components/Logo'; 5 | import { MenuData, findMenuByHref } from '@/config/data'; 6 | import { MenuType } from '@/types'; 7 | import style from './Menu.module.css'; 8 | import MenuItem from './MenuItem'; 9 | import PopMenuItem from './PopMenuItem'; 10 | import { useRouter } from 'next/router'; 11 | 12 | interface MenuListProps { 13 | hide?: boolean 14 | } 15 | 16 | export default function MenuList({ hide = false } : MenuListProps) { 17 | const [expandMap, setExpandMap] = useState>({}); 18 | const [selectMenu, setSelectMenu] = useState(null); 19 | const [activeKey, setActive] = useState(''); 20 | const [expand, setExpand] = useState(true); 21 | const [anchorElUser, setAnchorElUser] = useState(null); 22 | const router = useRouter(); 23 | // 菜单选中事件 24 | const handleSelect = ({ key, href, child = [] }: MenuType, value?: boolean) => { 25 | if (child.length > 0) { 26 | // 菜单展开时可以同时显示多个子菜单,菜单收起时,只展示最新hover的一个 27 | let oldMapData = expand ? expandMap : {}; 28 | setExpandMap({ 29 | ...oldMapData, 30 | [key]: value !== undefined ? value : !(expandMap[key]) 31 | }); 32 | } else { 33 | setActive(key); 34 | } 35 | if (href) { 36 | router.push(href); 37 | } 38 | }; 39 | // 初始化完成后选中当前菜单 40 | useEffect(() => { 41 | const { pathname, asPath } = router; 42 | if (pathname !== '/' || pathname === asPath) { 43 | const activeMenu = findMenuByHref(pathname); 44 | setActive(activeMenu?.key || 'home'); 45 | } 46 | }, [router]); 47 | // 缩小后的菜单hover弹出子菜单 48 | const handlePopoverOpen = (event: MouseEvent, menu: MenuType) => { 49 | setSelectMenu(menu); 50 | setAnchorElUser(event.currentTarget); 51 | }; 52 | 53 | const handlePopoverClose = () => { 54 | setSelectMenu(null); 55 | setAnchorElUser(null); 56 | }; 57 | 58 | return ( 59 |
60 |
61 | 63 |
64 |
65 | 66 | 67 | 68 |
69 |
70 | { 72 | setExpand(!expand); 73 | handlePopoverClose(); 74 | }}> 75 | { 76 | 77 | } 78 | 79 |
80 | { 81 | ((selectMenu?.child?.length || 0) > 0 && !expand) && 82 | } 83 |
84 | ); 85 | } -------------------------------------------------------------------------------- /src/components/MoreIcon/index.tsx: -------------------------------------------------------------------------------- 1 | import { MoreHoriz, DriveFileMove, FileCopy, Edit, Delete, Store } from '@mui/icons-material'; 2 | import { Menu, MenuItem, ListItemIcon, ListItemText } from '@mui/material'; 3 | import { useState, MouseEvent, useRef } from 'react'; 4 | import { MoreMenuEvent } from '@/types'; 5 | interface MoreIconProp { 6 | type: number 7 | uuid: string 8 | cover?: string 9 | title: string 10 | className: string 11 | onEvent: (params: MoreMenuEvent) => void 12 | } 13 | 14 | export default function MoreIcon({ type, uuid, title, cover = '', className, onEvent }: MoreIconProp) { 15 | const [anchorElUser, setAnchorElUser] = useState(null); 16 | const moreIconRef = useRef(null); 17 | const handleOpenUserMenu = () => { 18 | setAnchorElUser(moreIconRef.current); 19 | }; 20 | 21 | const handleCloseUserMenu = () => { 22 | setAnchorElUser(null); 23 | }; 24 | const clickEventHandler = (eventType: string) => { 25 | handleCloseUserMenu(); 26 | onEvent && onEvent({ 27 | type, 28 | title, 29 | cover, 30 | eventType, 31 | uuid 32 | }); 33 | }; 34 | 35 | return ( 36 | <> 37 | 38 | 53 |
54 | { clickEventHandler('edit'); }}> 55 | 56 | 57 | 58 | 修改信息 59 | 60 | { 61 | type === 0 && ( 62 | <> 63 | { clickEventHandler('copy'); }}> 64 | 65 | 66 | 67 | 复制一份 68 | 69 | { clickEventHandler('move'); }}> 70 | 71 | 72 | 73 | 移动页面 74 | 75 | { clickEventHandler('template'); }}> 76 | 77 | 78 | 79 | 保存为模板 80 | 81 | 82 | ) 83 | } 84 | { clickEventHandler('delete'); }}> 85 | 86 | 87 | 88 | 删除{type === 0 ? '页面' : '应用'} 89 | 90 |
91 |
92 | 93 | ); 94 | } -------------------------------------------------------------------------------- /src/components/MuiTable/index.tsx: -------------------------------------------------------------------------------- 1 | import { Table, TableCell, TableBody, TableContainer, TableHead, TablePagination, TableRow } from '@mui/material'; 2 | import QueryFactory from '@/components/Query/QueryFactory'; 3 | import { QueryConfig } from '@/types'; 4 | import loadable from '@loadable/component'; 5 | import CircularProgress from '@mui/material/CircularProgress'; 6 | import EmptyContainer from '@/components/Design/components/EmptyContainer'; 7 | const TableTemp = loadable((props: Record) => import(`./temp/${props.is}`), { 8 | fallback:
Loading...
, 9 | cacheKey: props => props.is 10 | }); 11 | 12 | interface Query { 13 | pageSize?: number 14 | page?: number 15 | } 16 | interface TableFactoryProp { 17 | queryConfig: QueryConfig 18 | rowData: Record[] 19 | query: Query 20 | count: number 21 | loading: boolean 22 | setQuery: (query: any) => void 23 | eventMap?: Record 24 | } 25 | export default function MuiTable(props: TableFactoryProp) { 26 | const { eventMap = {}, queryConfig, rowData, count, query = {}, setQuery, loading } = props; 27 | const { tableConfig: { columns }} = queryConfig; 28 | 29 | const handleChangePage = (event: unknown, newPage: number) => { 30 | setQuery({ ...query, page: newPage }); 31 | }; 32 | const handleChangeRowsPerPage = (event: React.ChangeEvent) => { 33 | setQuery({ ...query, pageSize: Number(event.target.value), page: 0 }); 34 | }; 35 | const HeadChild = columns.map((column, index) => ( 36 | 37 | {column.label} 38 | 39 | )); 40 | const BodyChild = rowData.map((row, index) => { 41 | return ( 42 | 43 | {columns.map(column => { 44 | const { key, component, format, align = 'left', getClass = '' } = column; 45 | const value = row[key]; 46 | let child = format ? format(value) : value; 47 | let className = getClass ? getClass(value) : ''; 48 | if (component) { 49 | child = ; 50 | } 51 | return ( 52 | 53 | {child} 54 | 55 | ); 56 | })} 57 | 58 | ); 59 | }); 60 | return ( 61 |
62 |
63 | 68 |
69 | 70 | 71 | 72 | 73 | { HeadChild } 74 | 75 | 76 | 77 | { BodyChild } 78 | 79 |
80 | { loading &&
} 81 | { BodyChild.length === 0 &&
} 82 |
83 | `前往 ${type} 页`} 92 | labelDisplayedRows={({ from, to, count }) => `第${from}条至第${to}条 共${count !== -1 ? count : to}条`} 93 | labelRowsPerPage='每页条数' 94 | onPageChange={handleChangePage} 95 | onRowsPerPageChange={handleChangeRowsPerPage} 96 | /> 97 |
98 |
99 |
100 | ); 101 | } -------------------------------------------------------------------------------- /src/components/MuiTable/temp/User.tsx: -------------------------------------------------------------------------------- 1 | import { Avatar } from '@mui/material'; 2 | import { MailOutline, LocalPhone } from '@mui/icons-material'; 3 | export default function User(data: Record) { 4 | const { eventMap, email, name, phone, avatar = '/avatar/b1.png' } = data; 5 | return ( 6 |
7 | 8 |
9 | {name} 10 |
11 |
{phone}
12 |
{email}
13 |
14 |
15 |
16 | ); 17 | } 18 | -------------------------------------------------------------------------------- /src/components/PageLoading/index.tsx: -------------------------------------------------------------------------------- 1 | import { LinearProgress } from '@mui/material'; 2 | import { useEffect, useState, useRef } from 'react'; 3 | 4 | export default function PageLoading() { 5 | const [progress, setProgress] = useState(0); 6 | 7 | useEffect(() => { 8 | setProgress(100); 9 | }, []); 10 | 11 | return ( 12 |
13 |
14 | 15 | 努力加载中... 16 |
17 |
18 | ); 19 | } 20 | -------------------------------------------------------------------------------- /src/components/Query/QueryFactory.tsx: -------------------------------------------------------------------------------- 1 | import { QueryConfig, QueryItem } from '@/types'; 2 | import { get } from 'lodash'; 3 | import loadable from '@loadable/component'; 4 | 5 | interface TableQueryProp { 6 | query: Record 7 | config: QueryConfig 8 | eventMap?: Record 9 | setQuery: (query: any) => void 10 | } 11 | const ItemComponent = loadable((props: {is: string}) => import(`./item/${props.is}.tsx`), { 12 | fallback:
Loading...
, 13 | cacheKey: props => props.is 14 | }); 15 | // 生成每一行 16 | function genQueryRow(eventMap: Record, rowList : QueryItem[], query: Record, setQuery: (newQuery: any) => void, key: string) { 17 | const rowElement = rowList.map((queryItem, index) => { 18 | const { type, className = '', mapping = {}, properties } = queryItem; 19 | const { key } = mapping; 20 | const value = get(query, key, undefined); 21 | const handlerChange = (newValue: any) => { 22 | if (key) { 23 | const newQuery = { 24 | ...query, 25 | [key]: newValue, 26 | page: 0 27 | }; 28 | setQuery?.(newQuery); 29 | } 30 | }; 31 | return ( 32 |
33 | 34 |
35 | ); 36 | }); 37 | return ( 38 |
39 | { 40 | rowElement 41 | } 42 |
43 | ); 44 | } 45 | 46 | export default function TableQuery(props: TableQueryProp) { 47 | const { query = {}, config, setQuery, eventMap = {}} = props; 48 | const fromChild = config.layout.map((rowConfig, index) => { 49 | return genQueryRow(eventMap, rowConfig, query, setQuery, `query-row-${index}`); 50 | }); 51 | return ( 52 |
53 | { 54 | fromChild 55 | } 56 |
57 | ); 58 | } -------------------------------------------------------------------------------- /src/components/Query/item/Button.tsx: -------------------------------------------------------------------------------- 1 | import { Button } from '@mui/material'; 2 | import DeleteSweep from '@mui/icons-material/DeleteSweep'; 3 | import PersonAdd from '@mui/icons-material/PersonAdd'; 4 | interface ResetProps { 5 | [key: string]: any, 6 | onChange: Function 7 | } 8 | const iconMap: Record = { 9 | DeleteSweep, 10 | PersonAdd 11 | }; 12 | export default function Reset(props: ResetProps) { 13 | const { eventName = '', eventMap = {}, is, className = '', defaultValue, value, label = '', onChange, options = [], startIcon, ...prop } = props; 14 | function handleClick() { 15 | eventMap[eventName]?.(); 16 | } 17 | const StartIconEle = iconMap[startIcon]; 18 | const startIconChild = StartIconEle ? : null; 19 | return ( 20 |
21 | 24 |
25 | ); 26 | } -------------------------------------------------------------------------------- /src/components/Query/item/DateRange.tsx: -------------------------------------------------------------------------------- 1 | import { useEffect, useState } from 'react'; 2 | import dayjs, { Dayjs } from 'dayjs'; 3 | import { DemoContainer } from '@mui/x-date-pickers/internals/demo'; 4 | import { AdapterDayjs } from '@mui/x-date-pickers/AdapterDayjs'; 5 | import { LocalizationProvider } from '@mui/x-date-pickers/LocalizationProvider'; 6 | import { DatePicker } from '@mui/x-date-pickers/DatePicker'; 7 | interface DateRangeProps { 8 | [key: string]: any, 9 | onChange: Function 10 | } 11 | export default function DateRange(props: DateRangeProps) { 12 | const { eventMap, className = '', defaultValue, value = {}, label = '', onChange, options = [], ...prop } = props; 13 | const { start, end } = value; 14 | const [startTime, setStartTime] = useState(start ? dayjs(start) : dayjs(dayjs('2023-01-01 00:00:00'))); 15 | const [endTime, setEndTime] = useState(end ? dayjs(end) : dayjs()); 16 | 17 | useEffect(() => { 18 | const start = startTime?.unix(); 19 | const end = endTime?.unix(); 20 | if (start && end) { 21 | onChange?.({ 22 | start: (start * 1000), 23 | end: (end * 1000) + (1000 * 60 * 60 * 24 - 1) // 事件计算到最后一毫秒 24 | }); 25 | } 26 | }, [startTime, endTime]); 27 | 28 | return ( 29 |
30 | {label} 31 | 32 | 33 |
34 | setStartTime(newValue)} /> 35 | 36 | setEndTime(newValue)} /> 37 |
38 |
39 |
40 |
41 | ); 42 | } -------------------------------------------------------------------------------- /src/components/Query/item/Select.tsx: -------------------------------------------------------------------------------- 1 | import { useEffect } from 'react'; 2 | import { Select as MuiSelect, MenuItem } from '@mui/material'; 3 | import { SelectChangeEvent } from '@mui/material/Select'; 4 | interface SelectProps { 5 | [key: string]: any, 6 | onChange: Function 7 | } 8 | export default function Select(props: SelectProps) { 9 | const { eventMap, className = '', defaultValue, value, label = '', onChange, options = [] } = props; 10 | function handleChange(event: SelectChangeEvent) { 11 | onChange?.(event.target.value); 12 | } 13 | const computedValue = value ?? defaultValue; 14 | useEffect(() => { 15 | if (computedValue !== value) { 16 | onChange?.(computedValue); 17 | } 18 | }, []); 19 | const OptionsChild = (options as Record[]).map(option => { 20 | const { value, label } = option; 21 | return {label}; 22 | }); 23 | return ( 24 |
25 | {label} 26 | 31 | { OptionsChild } 32 | 33 |
34 | ); 35 | } -------------------------------------------------------------------------------- /src/components/Query/item/String.tsx: -------------------------------------------------------------------------------- 1 | import { ChangeEvent, useEffect } from 'react'; 2 | import { OutlinedInput } from '@mui/material'; 3 | interface SelectProps { 4 | [key: string]: any, 5 | onChange: Function 6 | } 7 | export default function Select(props: SelectProps) { 8 | const { eventMap, is, className = '', defaultValue, value, label = '', onChange, options = [], ...prop } = props; 9 | function handleChange(event: ChangeEvent) { 10 | onChange?.(event.target.value); 11 | } 12 | const computedValue = value ?? defaultValue ?? ''; 13 | useEffect(() => { 14 | if (computedValue !== value) { 15 | onChange?.(computedValue); 16 | } 17 | }, []); 18 | 19 | return ( 20 |
21 | {label} 22 | 28 |
29 | ); 30 | } -------------------------------------------------------------------------------- /src/components/Query/item/Tab.tsx: -------------------------------------------------------------------------------- 1 | import { useEffect, SyntheticEvent } from 'react'; 2 | import { Tabs, Tab as MuiTab } from '@mui/material'; 3 | interface TabProps { 4 | [key: string]: any, 5 | onChange: Function 6 | } 7 | export default function Tab(props: TabProps) { 8 | const { eventMap, className, defaultValue, value, onChange, options = [] } = props; 9 | function handleChange(event: SyntheticEvent, newValue: string) { 10 | onChange?.(newValue); 11 | } 12 | const computedValue = value ?? defaultValue; 13 | useEffect(() => { 14 | if (computedValue !== value) { 15 | onChange?.(computedValue); 16 | } 17 | }, []); 18 | const OptionsChild = (options as Record[]).map(option => { 19 | return ; 20 | }); 21 | return ( 22 |
23 | 24 | { OptionsChild } 25 | 26 |
27 | ); 28 | } -------------------------------------------------------------------------------- /src/components/Svg/EyesIcon.tsx: -------------------------------------------------------------------------------- 1 | type EyeProps = { 2 | close: boolean, 3 | color: string, 4 | size: number 5 | } 6 | 7 | export default function EyesIcon({ close, color, size = 32 }: EyeProps) { 8 | if (close) { 9 | return 10 | 12 | ; 13 | } else { 14 | return 15 | 17 | ; 18 | } 19 | } -------------------------------------------------------------------------------- /src/components/Svg/FileIcon.tsx: -------------------------------------------------------------------------------- 1 | type FileIconProps = { 2 | type?: string, 3 | className: string, 4 | size: number 5 | } 6 | export default function FileIcon({ type, size = 32, className }: FileIconProps) { 7 | switch (type) { 8 | case 'json': 9 | return ; 10 | case 'gif': 11 | return ; 12 | case 'image': 13 | return ; 14 | case 'html': 15 | return ; 16 | default: 17 | return ; 18 | } 19 | } 20 | 21 | -------------------------------------------------------------------------------- /src/components/Svg/FolderIcon.tsx: -------------------------------------------------------------------------------- 1 | type FolderProps = { 2 | className: string, 3 | size: number 4 | } 5 | 6 | export default function EyesIcon({ size = 32, className }: FolderProps) { 7 | return ; 8 | } -------------------------------------------------------------------------------- /src/components/Svg/LoadingIcon.tsx: -------------------------------------------------------------------------------- 1 | type LoadingIconProp = { 2 | width?: number, 3 | className?: string, 4 | size?: number 5 | } 6 | 7 | export default function LoadingIcon({ className, size = 24, width = 4 }: LoadingIconProp) { 8 | return ( 9 | 11 | 13 | 15 | 16 | ); 17 | } 18 | -------------------------------------------------------------------------------- /src/components/Svg/WarningIcon.tsx: -------------------------------------------------------------------------------- 1 | type WarningProps = { 2 | color: string, 3 | className: string, 4 | size: number 5 | } 6 | 7 | export default function WarningIcon({ className, color, size = 32 }: WarningProps) { 8 | return ( 9 | 10 | 11 | 12 | ); 13 | } 14 | -------------------------------------------------------------------------------- /src/components/Swiper/index.tsx: -------------------------------------------------------------------------------- 1 | import { HomeSwiperData } from '@/config/data'; 2 | import { useState } from 'react'; 3 | import SwipeableViews from 'react-swipeable-views'; 4 | import { autoPlay } from 'react-swipeable-views-utils'; 5 | import { MobileStepper } from '@mui/material'; 6 | 7 | const AutoPlaySwipeableViews = autoPlay(SwipeableViews); 8 | import Lottie from '@/components/Lottie'; 9 | 10 | interface SwiperProps { 11 | lottieClassName?: string 12 | className?: string 13 | expand?: boolean 14 | } 15 | 16 | export default function Swiper({ lottieClassName = 'w-80', className, expand = false }: SwiperProps) { 17 | const [activeStep, setActive] = useState(0); 18 | const handleStepChange = (step: number) => { 19 | setActive(step); 20 | }; 21 | const swiper = HomeSwiperData.map((item, index) => { 22 | return ( 23 |
24 | { 25 | expand ? ( 26 | <> 27 | { 28 | (index % 2 === 0) 29 | &&
30 | } 31 |
33 |

{item.title}

34 |
    35 | { 36 | item.content.map((content, ctxIndex) =>
  • {content}
  • ) 37 | } 38 |
39 |
40 | { 41 | (index % 2 !== 0) 42 | &&
43 | } 44 | 45 | ) : ( 46 | <> 47 | 48 |
50 |

{item.title}

51 |
    52 | { 53 | item.content.map((content, ctxIndex) =>
  • {content}
  • ) 54 | } 55 |
56 |
57 | 58 | ) 59 | } 60 | 61 |
62 | ); 63 | }); 64 | return
65 | { 66 | expand ? swiper : ( 67 | <> 68 | 79 | {swiper} 80 | 81 | 90 | 91 | ) 92 | 93 | } 94 |
; 95 | } -------------------------------------------------------------------------------- /src/components/ThemeSwitch/index.tsx: -------------------------------------------------------------------------------- 1 | import { ChangeEvent, useContext } from 'react'; 2 | import { styled, Switch } from '@mui/material'; 3 | import useThemeSwitch from '@/components/Design/hook/useThemeSwitch'; 4 | const SwitchIcon = (mode: string) => { 5 | return { 6 | width: 58, 7 | height: 30, 8 | padding: 7, 9 | '& .MuiSwitch-switchBase': { 10 | margin: 1, 11 | padding: 0, 12 | transform: 'translateX(6px)', 13 | '&.Mui-checked': { 14 | color: '#fff', 15 | transform: 'translateX(22px)', 16 | '& .MuiSwitch-thumb:before': { 17 | backgroundImage: `url('data:image/svg+xml;utf8,')` 20 | }, 21 | '& + .MuiSwitch-track': { 22 | opacity: 1, 23 | backgroundColor: mode === 'dark' ? '#8796A5' : '#aab4be' 24 | } 25 | } 26 | }, 27 | '& .MuiSwitch-thumb': { 28 | backgroundColor: mode === 'dark' ? '#f59e0b' : '#c5d7ea', 29 | width: 28, 30 | height: 28, 31 | '&:before': { 32 | content: "''", 33 | position: 'absolute', 34 | width: '100%', 35 | height: '100%', 36 | left: 0, 37 | top: 0, 38 | backgroundRepeat: 'no-repeat', 39 | backgroundPosition: 'center', 40 | backgroundImage: `url('data:image/svg+xml;utf8,')` 43 | } 44 | }, 45 | '& .MuiSwitch-track': { 46 | opacity: 1, 47 | backgroundColor: mode === 'dark' ? '#8796A5' : '#aab4be', 48 | borderRadius: 20 / 2 49 | }}; 50 | }; 51 | 52 | export default function ThemeSwitch(props: Record) { 53 | const { theme, changeTheme } = useThemeSwitch(); 54 | const handleChange = (e: ChangeEvent) => { 55 | changeTheme(e.target.checked); 56 | }; 57 | 58 | const ThemeSwitchIcon = styled(Switch)(() => SwitchIcon(theme)); 59 | return ; 60 | } -------------------------------------------------------------------------------- /src/components/TipBox/index.tsx: -------------------------------------------------------------------------------- 1 | import Image from 'next/image'; 2 | import { QrCodeScanner, MailOutline } from '@mui/icons-material'; 3 | import { Popover } from '@mui/material'; 4 | import { useState, MouseEvent } from 'react'; 5 | interface TipBoxProps { 6 | className?: string 7 | } 8 | export default function TipBox({ className }: TipBoxProps) { 9 | const [anchorEl, setAnchorEl] = useState(null); 10 | 11 | const handlePopoverOpen = (event: MouseEvent) => { 12 | setAnchorEl(event.currentTarget); 13 | }; 14 | const handlePopoverClose = () => { 15 | setAnchorEl(null); 16 | }; 17 | const tipImage = '/3d/3d-business-little-boy-in-online-lesson.png'; 18 | const codeImage = '/wechatGroup.png'; 19 | const open = Boolean(anchorEl); 20 | return ( 21 |
22 |
23 | 32 |
33 |
34 |

如何获取帮助?

35 |
    36 |
  • 如果您需要联系购买
  • 37 |
  • 当您在使用过程中遇到困难
  • 38 |
  • 我们的Email: ccedit@126.com
  • 39 |
  • 也可以加入我们的客户服务群
  • 40 |
41 | 58 |
59 | 68 |
69 |
70 |
71 |
72 | ); 73 | } -------------------------------------------------------------------------------- /src/components/shimmer.tsx: -------------------------------------------------------------------------------- 1 | import { toBase64 } from '@/utils/common'; 2 | 3 | const shimmer = (w: number, h: number) => ` 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | `; 16 | 17 | export function getBase64() { 18 | return `data:image/svg+xml;base64,${toBase64(shimmer(700, 475))}`; 19 | } 20 | -------------------------------------------------------------------------------- /src/config/constant.ts: -------------------------------------------------------------------------------- 1 | export const Domain = { 2 | baseURL: process.env.NEXT_PUBLIC_BASEURL, 3 | baseOSS: process.env.NEXT_PUBLIC_BASEOSS 4 | }; 5 | // 全局常量 6 | export const Constant = { 7 | TokenKey: 'access_token', 8 | CaptureEncode: 'cpt_en', 9 | Uid: 'uid', 10 | ThemeKey: 'theme' 11 | }; 12 | // 文件类型 13 | export const FileTypeMap = { 14 | bpm: 'image/bpm', 15 | png: 'image/png', 16 | jpg: 'image/jpeg', 17 | gif: 'image/gif', 18 | mp3: 'audio/mp3', 19 | mp4: 'video/mpeg4', 20 | aac: 'audio/x-mei-aac' 21 | }; 22 | 23 | export const HideMenuList = [ 24 | '/login', 25 | '/design', 26 | '/preview', 27 | '/template', 28 | '/404', 29 | '/500' 30 | ]; 31 | export const HideHeadList = HideMenuList; 32 | export function isHideMenu(isHead = false, path: string) { 33 | return Boolean((isHead ? HideHeadList : HideMenuList).find(route => path.indexOf(route) > -1)); 34 | } -------------------------------------------------------------------------------- /src/config/context/ThemeModeContext.ts: -------------------------------------------------------------------------------- 1 | import { createContext } from 'react'; 2 | 3 | const ThemeModeContext = createContext({ toggleColorMode: (mode: 'light'|'dark') => {} }); 4 | 5 | export default ThemeModeContext; -------------------------------------------------------------------------------- /src/config/data/index.ts: -------------------------------------------------------------------------------- 1 | import { MenuType } from '@/types'; 2 | 3 | // 用户状态 4 | export const UserStatus = ['已删除', '已禁用', '正常']; 5 | 6 | // 用户角色 7 | export const UserRole = ['管理员', '用户']; 8 | 9 | // 页面状态 10 | export const PageStatus = ['已删除', '已锁定', '正常可编辑']; 11 | 12 | // 发布状态 13 | export const PublishStatus = ['已发布', '有更新', '已下线', '未发布']; 14 | 15 | // 文件类型 16 | export const FileType = ['页面', '文件夹']; 17 | 18 | // 头像菜单内容 19 | export const HeadMenuData = [ 20 | { 21 | key: 'edit', 22 | icon: 'Edit', 23 | title: '编辑资料', 24 | disable: true 25 | }, 26 | { 27 | key: 'editAccount', 28 | icon: 'Settings', 29 | title: '账户管理', 30 | disable: true 31 | } 32 | ]; 33 | 34 | // 菜单数据 35 | export const MenuData: MenuType[] = [ 36 | { 37 | key: 'home', 38 | icon: 'Home', 39 | title: '首页', 40 | contentTitle: '欢迎使用CcView', 41 | href: '/', 42 | childKey: [], 43 | child: [] 44 | }, 45 | { 46 | key: 'sale', 47 | icon: 'LocalGroceryStore', 48 | href: '/sale', 49 | title: '产品销售' 50 | }, 51 | { 52 | key: 'store', 53 | icon: 'Storefront', 54 | title: '模板商城', 55 | href: '/store', 56 | childKey: [], 57 | child: [] 58 | }, 59 | { 60 | key: 'application', 61 | icon: 'Source', 62 | title: '应用中心', 63 | href: '/application', 64 | childKey: [], 65 | child: [] 66 | }, 67 | { 68 | key: 'user', 69 | icon: 'Person', 70 | title: '用户管理', 71 | href: '/user' 72 | }, 73 | // { 74 | // key: 'editAccount', 75 | // icon: 'Settings', 76 | // title: '系统管理' 77 | // }, 78 | { 79 | key: 'example', 80 | icon: 'Ballot', 81 | title: '示例页面', 82 | childKey: ['imageDemo', 'cookieDemo', 'reduxDemo', 'aboutDemo', 'designDemo', 'apiDemo'], 83 | child: [ 84 | { 85 | key: '404', 86 | title: '404页面', 87 | href: '/404' 88 | }, 89 | { 90 | key: '500', 91 | title: '500页面', 92 | href: '/500' 93 | }, 94 | { 95 | key: 'maintenance', 96 | title: '维护页面', 97 | href: '/500?mt=1' 98 | }, 99 | { 100 | key: 'formCreate', 101 | title: 'json生成表单', 102 | href: '/formCreate' 103 | } 104 | ] 105 | } 106 | ]; 107 | // 根据当前url找到激活菜单 108 | export function findMenuByHref(href: string): MenuType { 109 | let menu = {}; 110 | MenuData.forEach(item => { 111 | if (item.href && href.indexOf(item.href) === 0) { 112 | menu = item; 113 | } 114 | if (Number(item?.child?.length) > 0) { 115 | let activeSub = item.child?.find(subMenu => subMenu.href === href); 116 | activeSub && (menu = activeSub); 117 | } 118 | }); 119 | return menu as MenuType; 120 | } 121 | 122 | // 首页轮播图数据 123 | export const HomeSwiperData = [ 124 | { 125 | title: '操作简单易上手', 126 | lottiePath: '/lottie/bubble-gum-web-design.json', 127 | content: ['通过拖拽快速完成页面布局', '简洁高效的属性调节,轻松实现自定义', '支持网页开发、小程序开发、移动App开发', '没有软件开发经验也可以创建自己的应用程序'] 128 | }, 129 | { 130 | title: '快速发布部署', 131 | lottiePath: '/lottie/techny-launching-a-startup.json', 132 | content: ['一键发布部署多个平台', '无需等待即刻使用', '让创意更快速的转化为产品', '提高产品迭代速率,更快速的达成目标'] 133 | }, 134 | { 135 | title: '专注您的产品创意', 136 | lottiePath: '/lottie/techny-project-management.json', 137 | content: ['我们认为好的产品创意更珍贵', '我们愿意帮助您共同实现您的产品创意', '80%的时间花在项目构思,20%的时间留给项目开发', '基础功能免费注册使用,优先保障项目低成本启动'] 138 | }, 139 | { 140 | title: '后台数据报表', 141 | lottiePath: '/lottie/techny-data-dashboard.json', 142 | content: ['提供基础数据分析功能', '更直观的图表数据,快速分享您的成果', '提供专属的后台管理系统'] 143 | } 144 | ]; -------------------------------------------------------------------------------- /src/config/form/CreateApp.ts: -------------------------------------------------------------------------------- 1 | import { FormConfig } from '@/types'; 2 | 3 | const CreateApp: FormConfig = { 4 | title: '创建应用', 5 | description: '创建应用', 6 | rowData: [ 7 | [ 8 | { 9 | name: 'title', 10 | className: 'w-full', 11 | properties: { 12 | type: 'text', 13 | label: '应用名称', 14 | placeholder: '请输入应用名称' 15 | }, 16 | defaultValue: '', 17 | rules: { 18 | required: '请输入应用名称', 19 | minLength: { value: 2, message: '应用名称长度至少2位' }, 20 | maxLength: { value: 10, message: '应用名称长度最多10位' } 21 | } 22 | } 23 | ], 24 | [ 25 | { 26 | name: 'desc', 27 | className: 'w-full', 28 | properties: { 29 | type: 'text', 30 | label: '应用说明', 31 | multiline: true, 32 | rows: 5, 33 | placeholder: '请输入应用说明' 34 | }, 35 | defaultValue: '', 36 | rules: { 37 | maxLength: { value: 100, message: '应用说明长度最多100位' } 38 | } 39 | } 40 | ], 41 | [ 42 | { 43 | name: 'cancel', 44 | component: 'CancelButton', 45 | className: 'w-1/2 px-2', 46 | properties: { 47 | buttonText: '取消' 48 | } 49 | }, 50 | { 51 | name: 'submit-btn', 52 | component: 'SubmitButton', 53 | className: 'w-1/2 px-2', 54 | properties: { 55 | buttonText: ['保存中...', '保存'] 56 | } 57 | } 58 | ] 59 | ] 60 | }; 61 | export default CreateApp; -------------------------------------------------------------------------------- /src/config/form/CreatePage.ts: -------------------------------------------------------------------------------- 1 | import { FormConfig } from '@/types'; 2 | 3 | const CreatePage: FormConfig = { 4 | title: '创建页面', 5 | description: '创建页面', 6 | rowData: [ 7 | [ 8 | { 9 | name: 'title', 10 | className: 'w-full', 11 | properties: { 12 | type: 'text', 13 | label: '页面名称', 14 | placeholder: '请输入页面名称' 15 | }, 16 | defaultValue: '', 17 | rules: { 18 | required: '请输入页面名称', 19 | minLength: { value: 2, message: '页面名称长度至少2位' }, 20 | maxLength: { value: 10, message: '页面名称长度最多10位' } 21 | } 22 | } 23 | ], 24 | [ 25 | { 26 | name: 'desc', 27 | className: 'w-full', 28 | properties: { 29 | type: 'text', 30 | label: '页面说明', 31 | multiline: true, 32 | rows: 5, 33 | placeholder: '请输入页面说明' 34 | }, 35 | defaultValue: '', 36 | rules: { 37 | maxLength: { value: 100, message: '页面说明长度最多100位' } 38 | } 39 | } 40 | ], 41 | [ 42 | { 43 | name: 'cancel', 44 | component: 'CancelButton', 45 | className: 'w-1/2 px-2', 46 | properties: { 47 | buttonText: '取消' 48 | } 49 | }, 50 | { 51 | name: 'submit-btn', 52 | component: 'SubmitButton', 53 | className: 'w-1/2 px-2', 54 | properties: { 55 | buttonText: ['保存中...', '保存'] 56 | } 57 | } 58 | ] 59 | ] 60 | }; 61 | export default CreatePage; -------------------------------------------------------------------------------- /src/config/form/LoginForm.ts: -------------------------------------------------------------------------------- 1 | import { FormConfig } from '@/types'; 2 | 3 | const LoginForm: FormConfig = { 4 | title: '登录表单', 5 | description: '应用登录表单', 6 | rowData: [ 7 | [ 8 | { 9 | name: 'username', 10 | className: 'w-full', 11 | properties: { 12 | type: 'text', 13 | label: '用户名', 14 | placeholder: '请输入用户名', 15 | autoComplete: 'username' 16 | }, 17 | defaultValue: 'Guest', 18 | rules: { 19 | required: '请输入用户名', 20 | minLength: { value: 5, message: '用户名长度至少5位' }, 21 | maxLength: { value: 20, message: '用户名长度最多20位' }, 22 | pattern: { value: /^[a-zA-Z0-9_]/, message: '用户名格式不正确' } 23 | } 24 | } 25 | ], 26 | [ 27 | { 28 | name: 'password', 29 | component: '', 30 | className: 'w-full', 31 | properties: { 32 | type: 'password', 33 | label: '密码', 34 | placeholder: '请输入密码', 35 | autoComplete: 'current-password' 36 | }, 37 | defaultValue: 'Aa123465@', 38 | rules: { 39 | required: '请输入密码', 40 | minLength: { value: 6, message: '密码长度至少6位' }, 41 | maxLength: { value: 30, message: '密码长度最多30位' }, 42 | pattern: { 43 | value: /^(?=.*[a-zA-Z])(?=.*[0-9])(?=.*[._~!@#$^&*])[A-Za-z0-9._~!@#$^&*]{6,30}$/, 44 | message: '密码中必须包含字母、数字和特殊字符' 45 | } 46 | } 47 | } 48 | ], 49 | [ 50 | { 51 | name: 'captcha', 52 | component: '', 53 | className: 'w-1/2 pr-1', 54 | properties: { 55 | type: 'text', 56 | label: '验证码', 57 | placeholder: '请输入图形验证码' 58 | }, 59 | defaultValue: '', 60 | rules: { 61 | required: '请输入验证码', 62 | minLength: { value: 4, message: '验证码长度至少4位' }, 63 | maxLength: { value: 6, message: '验证码长度最多6位' }, 64 | pattern: { value: /^[a-zA-Z0-9]{4,6}$/, message: '验证码格式不正确' } 65 | } 66 | }, 67 | { 68 | name: 'captcha-image', 69 | component: 'Capture', 70 | className: 'w-1/2 pl-1', 71 | properties: { 72 | title: '看不清楚?点击图片更换~', 73 | enterNextDelay: 500, 74 | enterDelay: 500, 75 | placement: 'top', 76 | arrow: true 77 | } 78 | } 79 | ], 80 | [ 81 | { 82 | name: 'submit-btn', 83 | component: 'SubmitButton', 84 | className: 'w-full', 85 | properties: {} 86 | } 87 | ] 88 | ] 89 | }; 90 | export default LoginForm; -------------------------------------------------------------------------------- /src/config/query/UserQuery.ts: -------------------------------------------------------------------------------- 1 | import { QueryConfig } from '@/types'; 2 | import { dateFormat } from '@/utils/common'; 3 | 4 | const UserStatus = ['已删除', '已禁用', '正常']; 5 | const UserStatusColor = ['text-red-500', 'text-orange-500', '']; 6 | const UserRole = ['管理员', '用户']; 7 | 8 | const UserQuery: QueryConfig = { 9 | tableConfig: { 10 | columns: [ 11 | { key: 'user', label: '用户', component: 'User', align: 'center', minWidth: 240 }, 12 | { key: 'role', label: '用户角色', align: 'center', format: (value: number) => UserRole[value] }, 13 | { key: 'status', label: '用户状态', align: 'center', getClass: (value: number) => UserStatusColor[value], format: (value: number) => UserStatus[value] }, 14 | { key: 'createDate', label: '创建日期', format: (value: number) => dateFormat(value, 'Y-M-D h:m:s') }, 15 | { key: 'updateDate', label: '最后更新日期', format: (value: number) => dateFormat(value, 'Y-M-D h:m:s') }, 16 | { key: 'options', label: '操作', component: 'UserOptions', align: 'center', minWidth: 120 } 17 | ] 18 | }, 19 | layout: [ 20 | [ 21 | { 22 | type: 'Tab', 23 | className: 'flex-1', 24 | mapping: { key: 'status' }, 25 | properties: { 26 | defaultValue: -1, 27 | options: [ 28 | { value: -1, label: '全部' }, 29 | { value: 2, label: '正常' }, 30 | { value: 1, label: '已禁用' }, 31 | { value: 0, label: '已删除' } 32 | ] 33 | } 34 | }, 35 | { 36 | type: 'Button', 37 | className: 'w-22', 38 | properties: { 39 | label: '新增用户', 40 | variant: 'outlined', 41 | startIcon: 'PersonAdd', 42 | eventName: 'handleCreat' 43 | } 44 | } 45 | ], 46 | [ 47 | { 48 | type: 'String', 49 | mapping: { key: 'info' }, 50 | className: 'w-120 mr-4', 51 | properties: { 52 | label: '用户信息', 53 | placeholder: '模糊查询用户名、邮箱、手机号' 54 | } 55 | }, 56 | { 57 | type: 'Select', 58 | className: 'w-52 mr-4', 59 | mapping: { key: 'role' }, 60 | properties: { 61 | label: '用户角色', 62 | defaultValue: -1, 63 | options: [ 64 | { value: -1, label: '全部' }, 65 | { value: 0, label: '管理员' }, 66 | { value: 1, label: '用户' } 67 | ] 68 | } 69 | }, 70 | { 71 | type: 'DateRange', 72 | className: 'w-110', 73 | mapping: { key: 'createDate' }, 74 | properties: { 75 | label: '创建时间', 76 | format: 'YYYY-MM-DD' 77 | } 78 | }, 79 | { 80 | type: 'Button', 81 | className: 'w-22', 82 | properties: { 83 | label: '重置', 84 | variant: 'outlined', 85 | startIcon: 'DeleteSweep', 86 | eventName: 'resetQuery' 87 | } 88 | } 89 | ] 90 | ] 91 | }; 92 | export default UserQuery; -------------------------------------------------------------------------------- /src/config/theme.ts: -------------------------------------------------------------------------------- 1 | import { createTheme } from '@mui/material/styles'; 2 | import { PaletteMode } from '@mui/material'; 3 | export default function getTheme(mode: PaletteMode) { 4 | const isDark = mode !== 'light'; 5 | return createTheme({ 6 | spacing: 6, 7 | typography: { 8 | // In Chinese and Japanese the characters are usually larger, 9 | // so a smaller fontsize may be appropriate. 10 | fontSize: 14 11 | }, 12 | components: { 13 | MuiTableRow: { 14 | styleOverrides: { 15 | root: { 16 | '&.MuiTableRow-hover:hover': { 17 | backgroundColor: isDark ? 'rgba(38,38,38,0.5)' : 'rgba(212,212,216,0.4)' 18 | }, 19 | '&:nth-of-type(odd)': { 20 | backgroundColor: isDark ? 'rgba(38,38,38,0.8)' : 'rgba(212,212,216,0.4)' 21 | } 22 | } 23 | } 24 | }, 25 | MuiTableCell: { 26 | styleOverrides: { 27 | root: { 28 | padding: '10px' 29 | }, 30 | head: { 31 | backgroundColor: isDark ? '#262626' : '#d4d4d8', 32 | color: isDark ? '#fafafa' : '#262626' 33 | }, 34 | body: { 35 | fontSize: 14 36 | } 37 | } 38 | }, 39 | MuiInputBase: { 40 | styleOverrides: { 41 | input: { 42 | fontSize: '14px', 43 | padding: '13px 12px', 44 | lineHeight: '18px', 45 | height: '18px', 46 | verticalAlign: 'middle' 47 | } 48 | } 49 | }, 50 | MuiOutlinedInput: { 51 | styleOverrides: { 52 | notchedOutline: { 53 | // borderColor: 'rgba(234, 179, 8, 0.5)' 54 | }, 55 | input: { 56 | fontSize: '14px', 57 | padding: '13px 12px', 58 | lineHeight: '18px', 59 | height: '18px', 60 | verticalAlign: 'middle' 61 | } 62 | } 63 | }, 64 | MuiInputLabel: { 65 | styleOverrides: { 66 | root: { 67 | fontSize: '14px', 68 | lineHeight: '18px', 69 | height: '18px', 70 | marginTop: '-3px' 71 | // transform: 'translate(14px, -6px) scale(0.9)' 72 | } 73 | } 74 | } 75 | }, 76 | palette: { 77 | mode, 78 | common: { 79 | black: '#171717', 80 | white: '#d4d4d8' 81 | }, 82 | primary: { 83 | main: '#eab308', 84 | dark: '#ca8a04', 85 | light: '#facc15' 86 | }, 87 | error: { 88 | main: '#dc2626', 89 | dark: '#b91c1c', 90 | light: '#ef4444' 91 | }, 92 | warning: { 93 | main: '#f97316', 94 | dark: '#ea580c', 95 | light: '#fb923c' 96 | }, 97 | action: { 98 | active: '#facc15', 99 | hover: 'rgba(250, 204, 21, 0.9)', 100 | focus: 'rgba(250, 204, 21, 0.12)', 101 | disabled: 'rgba(250, 204, 21, 0.3)', 102 | selected: 'rgba(250, 204, 21, 0.16)' 103 | } 104 | } 105 | }); 106 | } 107 | -------------------------------------------------------------------------------- /src/pages/404/index.tsx: -------------------------------------------------------------------------------- 1 | import NotFoundIcon from '@/components/Svg/NotFoundIcon'; 2 | import Link from 'next/link'; 3 | import { useEffect, useState } from 'react'; 4 | import Logo from '@/components/Logo'; 5 | 6 | export default function NotFound() { 7 | const [windowWidth, setWindowWidth] = useState(400); 8 | useEffect(() => { 9 | setWindowWidth(window.innerWidth); 10 | }, []); 11 | return ( 12 | <> 13 | 14 |
15 |
16 | 17 |

抱歉,您访问的页面不存在~

18 |

请检查您输入的网址是否正确,或者点击下方链接继续浏览

19 |
20 | 回首页 21 | 去登录 22 |
23 |
24 |
25 | 26 | 27 | ); 28 | } -------------------------------------------------------------------------------- /src/pages/500/index.tsx: -------------------------------------------------------------------------------- 1 | import ServerErrorIcon from '@/components/Svg/ServerErrorIcon'; 2 | import ErrorIcon from '@/components/Svg/ErrorIcon'; 3 | import Link from 'next/link'; 4 | import { useEffect, useState } from 'react'; 5 | import { useRouter } from 'next/router'; 6 | import Logo from '@/components/Logo'; 7 | 8 | export default function NotFound() { 9 | const router = useRouter(); 10 | const { mt } = router.query; 11 | const isMaintaining = Boolean(mt); 12 | const [windowWidth, setWindowWidth] = useState(400); 13 | useEffect(() => { 14 | setWindowWidth(window.innerWidth); 15 | }, []); 16 | return ( 17 | <> 18 | 19 |
20 |
21 | { 22 | isMaintaining ? ( 23 | <> 24 | 25 |

抱歉,服务器日常检修中~

26 |

服务器检修中,预计1小时后恢复正常,请耐心等候

27 | 28 | ) : ( 29 | <> 30 | 31 |

糟糕,服务器没有响应~

32 |

服务器出现未知错误,请检查您的链接,或访问其他页面

33 |
34 | 回首页 35 | 去登录 36 |
37 | 38 | ) 39 | } 40 |
41 |
42 | 43 | ); 44 | } -------------------------------------------------------------------------------- /src/pages/_app.tsx: -------------------------------------------------------------------------------- 1 | import { Provider } from 'react-redux'; 2 | import { useEffect, useState, useMemo, useLayoutEffect } from 'react'; 3 | import dynamic from 'next/dynamic'; 4 | import Head from 'next/head'; 5 | import type { AppProps } from 'next/app'; 6 | import { useRouter } from 'next/router'; 7 | import { SnackbarProvider } from 'notistack'; 8 | import { ConfirmProvider } from 'material-ui-confirm'; 9 | 10 | import NProgress from 'nprogress'; 11 | import store from '../store'; 12 | import 'nprogress/nprogress.css'; 13 | import '@/assets/main.css'; 14 | import '@/assets/sketch.css'; 15 | 16 | import CssBaseline from '@mui/material/CssBaseline'; 17 | import { ThemeProvider } from '@mui/material/styles'; 18 | import ThemeModeContext from '@/config/context/ThemeModeContext'; 19 | import { Constant, isHideMenu } from '@/config/constant'; 20 | import getTheme from '@/config/theme'; 21 | import { getStorage } from '@/utils/common'; 22 | import Layout from '@/components/Layout'; 23 | NProgress.configure({ showSpinner: false }); 24 | 25 | function MyApp({ Component, pageProps }: AppProps) { 26 | const [mode, setMode] = useState<'light' | 'dark'>(getStorage(Constant.ThemeKey, 'dark') as 'light'|'dark'); 27 | const themeMode = useMemo(() => ({ 28 | toggleColorMode: (newMode: 'light'|'dark' = 'light') => { 29 | setMode(newMode); 30 | } 31 | }), []); 32 | const theme = useMemo(() => getTheme(mode), [mode]); 33 | const router = useRouter(); 34 | 35 | useLayoutEffect(() => { 36 | // 组织窗口放大缩小,避免和Sketch冲突 37 | document.addEventListener('wheel', e => { 38 | if (e.ctrlKey || e.metaKey) { 39 | e.preventDefault(); 40 | } 41 | }, { 42 | passive: false // Add this 43 | }); 44 | document.documentElement.classList[mode === 'dark' ? 'add' : 'remove']('dark'); 45 | }, [mode]); 46 | 47 | useEffect(() => { 48 | const { pathname, asPath } = router; 49 | if (pathname === '/' && pathname !== asPath) { 50 | router.push(asPath); 51 | } 52 | const handleStart = () => { 53 | NProgress.start(); 54 | }; 55 | 56 | const handleStop = () => { 57 | NProgress.done(); 58 | }; 59 | router.events.on('routeChangeStart', handleStart); 60 | router.events.on('routeChangeComplete', handleStop); 61 | router.events.on('routeChangeError', handleStop); 62 | 63 | return () => { 64 | router.events.off('routeChangeStart', handleStart); 65 | router.events.off('routeChangeComplete', handleStop); 66 | router.events.off('routeChangeError', handleStop); 67 | }; 68 | }, [router]); 69 | 70 | const getBaiduScriptTag = () => { 71 | return { 72 | __html: ` 73 | var _hmt = _hmt || []; 74 | (function() { 75 | var hm = document.createElement("script"); 76 | hm.src = "https://hm.baidu.com/hm.js?5375c7bd8bf2c24a91b24fceced1e007"; 77 | var s = document.getElementsByTagName("script")[0]; 78 | s.parentNode.insertBefore(hm, s); 79 | })();` 80 | }; 81 | }; 82 | 83 | return ( 84 | <> 85 | 86 | Cc-Edit 87 | 88 |