├── src ├── vite-env.d.ts ├── styles │ ├── var.less │ ├── theme.less │ ├── reset.less │ └── common.less ├── assets │ ├── images │ │ ├── logo.png │ │ ├── avatar.png │ │ ├── welcome.png │ │ ├── home_show.png │ │ ├── language.png │ │ ├── list_show.png │ │ ├── welcome01.png │ │ ├── editor_show.png │ │ ├── login_left.png │ │ ├── login_left1.png │ │ ├── login_left2.png │ │ ├── login_left3.png │ │ ├── login_left4.png │ │ ├── lowcode_show.png │ │ ├── favicon.svg │ │ └── login_bg.svg │ └── react.svg ├── types │ ├── index.d.ts │ └── modal.ts ├── views │ ├── proComponents │ │ ├── index.module.less │ │ └── index.tsx │ ├── welcome │ │ ├── index.tsx │ │ └── index.module.less │ ├── editor │ │ ├── components │ │ │ ├── ComponentLib.module.less │ │ │ ├── EditHeader.module.less │ │ │ ├── EditCanvas.module.less │ │ │ ├── Layers.module.less │ │ │ ├── leftPanel.tsx │ │ │ ├── ComponentProp.tsx │ │ │ ├── PageSetting.tsx │ │ │ ├── RightPanel.tsx │ │ │ ├── ComponentLib.tsx │ │ │ ├── EditCanvas.tsx │ │ │ ├── EditToolbar.tsx │ │ │ ├── EditHeader.tsx │ │ │ └── Layers.tsx │ │ ├── index.module.less │ │ ├── index.tsx │ │ └── useEditor.ts │ ├── lowcode │ │ ├── index.module.less │ │ ├── components │ │ │ ├── ListSearch.tsx │ │ │ ├── Card.module.less │ │ │ ├── Card.tsx │ │ │ └── CardNew.tsx │ │ └── index.tsx │ ├── 404.tsx │ ├── 403.tsx │ ├── dashboard │ │ └── index.module.less │ ├── login │ │ ├── index.module.less │ │ └── Login.tsx │ ├── order │ │ ├── OrderCluster │ │ │ └── index.tsx │ │ └── OrderList │ │ │ └── components │ │ │ ├── OrderRoute.tsx │ │ │ ├── OrderDetail.tsx │ │ │ └── OrderMarker.tsx │ └── system │ │ ├── role │ │ ├── CreateRole.tsx │ │ ├── SetPermission.tsx │ │ └── index.tsx │ │ ├── dept │ │ ├── CreateDept.tsx │ │ └── index.tsx │ │ └── menu │ │ └── CreateMenu.tsx ├── layout │ ├── index.module.less │ └── index.tsx ├── utils │ ├── loading │ │ ├── loading.less │ │ ├── loading.tsx │ │ └── index.tsx │ ├── AntdGlobal.tsx │ ├── storage.ts │ ├── request.ts │ └── index.ts ├── api │ ├── proComponentsApi.ts │ ├── lowCodeApi.ts │ ├── roleApi.ts │ ├── orderApi.ts │ └── index.ts ├── components │ ├── LowCodeComponents │ │ ├── LowCodeInput │ │ │ ├── interface.ts │ │ │ ├── index.ts │ │ │ ├── Component.tsx │ │ │ ├── Component.test.tsx │ │ │ └── PropComponent.tsx │ │ ├── LowCodeInfo │ │ │ ├── interface.ts │ │ │ ├── index.ts │ │ │ ├── Component.test.tsx │ │ │ ├── Component.tsx │ │ │ └── PropComponent.tsx │ │ ├── LowCodeTextarea │ │ │ ├── interface.ts │ │ │ ├── index.ts │ │ │ ├── Component.test.tsx │ │ │ ├── Component.tsx │ │ │ └── PropComponent.tsx │ │ ├── LowCodeParagraph │ │ │ ├── interface.ts │ │ │ ├── index.ts │ │ │ ├── Component.test.tsx │ │ │ ├── Component.tsx │ │ │ └── PropComponent.tsx │ │ ├── LowCodeTitle │ │ │ ├── interface.ts │ │ │ ├── index.ts │ │ │ ├── Component.test.tsx │ │ │ ├── Component.tsx │ │ │ └── PropComponent.tsx │ │ ├── LowCodeRadio │ │ │ ├── index.ts │ │ │ ├── interface.ts │ │ │ ├── Component.tsx │ │ │ ├── Component.test.tsx │ │ │ └── PropComponent.tsx │ │ ├── LowCodeCheckbox │ │ │ ├── index.ts │ │ │ ├── interface.ts │ │ │ ├── Component.tsx │ │ │ ├── Component.test.tsx │ │ │ └── PropComponent.tsx │ │ └── index.ts │ ├── NavFooter │ │ ├── index.module.less │ │ └── index.tsx │ ├── Menu │ │ ├── index.module.less │ │ └── index.tsx │ ├── AuthButton.tsx │ ├── SearchForm.tsx │ ├── DragSortable │ │ ├── SortableItem.tsx │ │ └── SortableContainer.tsx │ ├── CitySelect │ │ ├── useCityData.ts │ │ ├── interface.ts │ │ ├── utils.ts │ │ └── index.tsx │ ├── NavHeader │ │ ├── index.module.less │ │ ├── BreadCrumb.tsx │ │ └── index.tsx │ └── Tabs.tsx ├── main.tsx ├── router │ ├── AuthLoader.ts │ ├── LazyLoad.tsx │ └── index.tsx ├── hook │ ├── useCharts.ts │ └── useBindCanvasKeyPress.ts ├── language │ ├── index.ts │ └── modules │ │ ├── zh.ts │ │ └── en.ts ├── App.less ├── store │ ├── utils.ts │ └── useUserStore.ts ├── config │ └── index.ts ├── App.tsx └── _mock │ ├── dashboard.ts │ ├── role.ts │ └── lowCode.ts ├── typings.d.ts ├── .editorconfig ├── tsconfig.node.json ├── .gitignore ├── .env.production ├── .env.development ├── .env.test ├── .prettierrc.cjs ├── .eslintrc.cjs ├── tsconfig.json ├── vite.config.ts ├── public └── vite.svg ├── index.html ├── package.json └── README.md /src/vite-env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | -------------------------------------------------------------------------------- /src/styles/var.less: -------------------------------------------------------------------------------- 1 | /* Global definition less */ 2 | @primary-color: #1890ff; 3 | -------------------------------------------------------------------------------- /src/assets/images/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zczhao1992/react-manager/HEAD/src/assets/images/logo.png -------------------------------------------------------------------------------- /src/assets/images/avatar.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zczhao1992/react-manager/HEAD/src/assets/images/avatar.png -------------------------------------------------------------------------------- /src/assets/images/welcome.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zczhao1992/react-manager/HEAD/src/assets/images/welcome.png -------------------------------------------------------------------------------- /src/assets/images/home_show.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zczhao1992/react-manager/HEAD/src/assets/images/home_show.png -------------------------------------------------------------------------------- /src/assets/images/language.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zczhao1992/react-manager/HEAD/src/assets/images/language.png -------------------------------------------------------------------------------- /src/assets/images/list_show.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zczhao1992/react-manager/HEAD/src/assets/images/list_show.png -------------------------------------------------------------------------------- /src/assets/images/welcome01.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zczhao1992/react-manager/HEAD/src/assets/images/welcome01.png -------------------------------------------------------------------------------- /src/assets/images/editor_show.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zczhao1992/react-manager/HEAD/src/assets/images/editor_show.png -------------------------------------------------------------------------------- /src/assets/images/login_left.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zczhao1992/react-manager/HEAD/src/assets/images/login_left.png -------------------------------------------------------------------------------- /src/assets/images/login_left1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zczhao1992/react-manager/HEAD/src/assets/images/login_left1.png -------------------------------------------------------------------------------- /src/assets/images/login_left2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zczhao1992/react-manager/HEAD/src/assets/images/login_left2.png -------------------------------------------------------------------------------- /src/assets/images/login_left3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zczhao1992/react-manager/HEAD/src/assets/images/login_left3.png -------------------------------------------------------------------------------- /src/assets/images/login_left4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zczhao1992/react-manager/HEAD/src/assets/images/login_left4.png -------------------------------------------------------------------------------- /src/assets/images/lowcode_show.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zczhao1992/react-manager/HEAD/src/assets/images/lowcode_show.png -------------------------------------------------------------------------------- /src/types/index.d.ts: -------------------------------------------------------------------------------- 1 | interface Window { 2 | BMapGL: { 3 | [propName: string]: any 4 | } 5 | BMapGLLib: any 6 | BMapLib: any 7 | } 8 | -------------------------------------------------------------------------------- /src/views/proComponents/index.module.less: -------------------------------------------------------------------------------- 1 | .warp { 2 | background-color: var(--dark-bg-color); 3 | height: 100vh; 4 | // text-align: center; 5 | padding: 20px; 6 | } 7 | -------------------------------------------------------------------------------- /typings.d.ts: -------------------------------------------------------------------------------- 1 | import axios from 'axios' 2 | 3 | declare module 'axios' { 4 | interface AxiosRequestConfig { 5 | showLoading?: boolean 6 | showError?: boolean 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # https://editorconfig.org 2 | root = true 3 | 4 | [*] 5 | charset = utf-8 6 | indent_style = tab 7 | indent_size = 2 8 | end_of_line = lf 9 | insert_final_newline = true 10 | trim_trailing_whitespace = true -------------------------------------------------------------------------------- /src/layout/index.module.less: -------------------------------------------------------------------------------- 1 | .content { 2 | background-color: var(--dark-home-bg-color); 3 | height: calc(100vh - 90px); 4 | padding: 20px; 5 | overflow: auto; 6 | } 7 | .wrapper { 8 | min-height: calc(100vh - 210px); 9 | } 10 | -------------------------------------------------------------------------------- /src/utils/loading/loading.less: -------------------------------------------------------------------------------- 1 | #loading { 2 | position: fixed; 3 | top: 0; 4 | left: 0; 5 | bottom: 0; 6 | right: 0; 7 | display: flex; 8 | flex-direction: column; 9 | align-items: center; 10 | justify-content: center; 11 | font-size: 20px; 12 | } 13 | -------------------------------------------------------------------------------- /tsconfig.node.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "composite": true, 4 | "skipLibCheck": true, 5 | "module": "ESNext", 6 | "moduleResolution": "node", 7 | "allowSyntheticDefaultImports": true 8 | }, 9 | "include": [ 10 | "vite.config.ts" 11 | ] 12 | } 13 | -------------------------------------------------------------------------------- /src/api/proComponentsApi.ts: -------------------------------------------------------------------------------- 1 | import request from '@/utils/request' 2 | import { ProComponents } from '@/types/api' 3 | import '@/_mock/city' 4 | 5 | export default { 6 | // 获取城市列表 7 | getCityList() { 8 | return request.get('/city/list') 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /src/views/welcome/index.tsx: -------------------------------------------------------------------------------- 1 | import welcome from '@/assets/images/welcome01.png' 2 | import styles from './index.module.less' 3 | 4 | export default function Welcome() { 5 | return ( 6 |
7 | welcome 8 |
9 | ) 10 | } 11 | -------------------------------------------------------------------------------- /src/styles/theme.less: -------------------------------------------------------------------------------- 1 | :root { 2 | --dark-bg-color: #fff; 3 | --dark-color: #000; 4 | --dark-home-bg-color: #f0f2f5; 5 | --dark-logo-color: #001529; 6 | } 7 | 8 | .dark { 9 | --dark-bg-color: #141414; 10 | --dark-color: #fff; 11 | --dark-home-bg-color: #000; 12 | --dark-logo-color: #141414; 13 | } 14 | -------------------------------------------------------------------------------- /src/utils/loading/loading.tsx: -------------------------------------------------------------------------------- 1 | import { Spin } from 'antd' 2 | import './loading.less' 3 | 4 | export default function Loading({ tip = 'Loading' }: { tip?: string }) { 5 | return ( 6 | 7 |
8 | 9 | ) 10 | } 11 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | pnpm-debug.log* 8 | lerna-debug.log* 9 | 10 | node_modules 11 | dist 12 | dist-ssr 13 | *.local 14 | 15 | # Editor directories and files 16 | .vscode/* 17 | !.vscode/extensions.json 18 | .idea 19 | .DS_Store 20 | *.suo 21 | *.ntvs* 22 | *.njsproj 23 | *.sln 24 | *.sw? 25 | -------------------------------------------------------------------------------- /src/components/LowCodeComponents/LowCodeInput/interface.ts: -------------------------------------------------------------------------------- 1 | export type LowCodeInputPropsType = { 2 | title?: string 3 | placeholder?: string 4 | 5 | onChange?: (newProps: LowCodeInputPropsType) => void 6 | disabled?: boolean 7 | } 8 | 9 | export const LowCodeInputDefaultProps: LowCodeInputPropsType = { 10 | title: '输入框标题', 11 | placeholder: '请输入...' 12 | } 13 | -------------------------------------------------------------------------------- /src/components/LowCodeComponents/LowCodeInfo/interface.ts: -------------------------------------------------------------------------------- 1 | export type LowCodeInfoPropsType = { 2 | title?: string 3 | desc?: string 4 | 5 | // 用于 PropComponent 6 | onChange?: (newProps: LowCodeInfoPropsType) => void 7 | disabled?: boolean 8 | } 9 | 10 | export const LowCodeInfoDefaultProps: LowCodeInfoPropsType = { 11 | title: '问卷标题', 12 | desc: '问卷描述' 13 | } 14 | -------------------------------------------------------------------------------- /.env.production: -------------------------------------------------------------------------------- 1 | # 设置NODE_ENV环境模式 2 | NODE_ENV=production 3 | 4 | # 接口API地址 5 | VITE_BASE_API = /api 6 | 7 | # 上传API 8 | VITE_UPLOAD_API = http://api-driver.marsview.cc 9 | 10 | # CDN 地址 11 | VITE_CDN = http://www.aliyun.com 12 | 13 | # mock 开关 14 | VITE_MOCK = false 15 | 16 | # MOCK API 17 | VITE_MOCK_API = https://www.fastmock.site/mock/5841b82d5672783b6fd62bb2a06aeb1f/api 18 | -------------------------------------------------------------------------------- /.env.development: -------------------------------------------------------------------------------- 1 | # 设置NODE_ENV环境模式 2 | NODE_ENV=development 3 | 4 | # 接口API地址 5 | VITE_BASE_API = /api 6 | 7 | # 上传API 8 | VITE_UPLOAD_API = http://api-driver-dev.marsview.cc 9 | 10 | # CDN 地址 11 | VITE_CDN = http://www.aliyun.com 12 | 13 | # mock 开关 14 | VITE_MOCK = true 15 | 16 | # MOCK API 17 | VITE_MOCK_API = https://www.fastmock.site/mock/5841b82d5672783b6fd62bb2a06aeb1f/api 18 | -------------------------------------------------------------------------------- /src/components/LowCodeComponents/LowCodeTextarea/interface.ts: -------------------------------------------------------------------------------- 1 | export type LowCodeTextareaPropsType = { 2 | title?: string 3 | placeholder?: string 4 | 5 | onChange?: (newProps: LowCodeTextareaPropsType) => void 6 | disabled?: boolean 7 | } 8 | 9 | export const LowCodeTextareaDefaultProps: LowCodeTextareaPropsType = { 10 | title: '输入框标题', 11 | placeholder: '请输入...' 12 | } 13 | -------------------------------------------------------------------------------- /src/main.tsx: -------------------------------------------------------------------------------- 1 | // import React from 'react' 2 | import ReactDOM from 'react-dom/client' 3 | import App from './App' 4 | import '@/styles/reset.less' 5 | import './styles/theme.less' 6 | import '@/styles/common.less' 7 | import '@/language/index' 8 | 9 | ReactDOM.createRoot(document.getElementById('root')!).render( 10 | // 11 | 12 | // 13 | ) 14 | -------------------------------------------------------------------------------- /src/components/LowCodeComponents/LowCodeParagraph/interface.ts: -------------------------------------------------------------------------------- 1 | export type LowCodeParagraphPropsType = { 2 | text?: string 3 | isCenter?: boolean 4 | 5 | // 用于 PropComponent 6 | onChange?: (newProps: LowCodeParagraphPropsType) => void 7 | disabled?: boolean 8 | } 9 | 10 | export const LowCodeParagraphDefaultProps: LowCodeParagraphPropsType = { 11 | text: '一行段落', 12 | isCenter: false 13 | } 14 | -------------------------------------------------------------------------------- /.env.test: -------------------------------------------------------------------------------- 1 | # 设置NODE_ENV环境模式 2 | NODE_ENV=test 3 | 4 | # 接口API地址 5 | VITE_BASE_API = http://api-driver-stg.marsview.cc/api 6 | 7 | # 上传API 8 | VITE_UPLOAD_API = http://api-driver-stg.marsview.cc 9 | 10 | # CDN 地址 11 | VITE_CDN = http://www.aliyun.com 12 | 13 | # mock 开关 14 | VITE_MOCK = false 15 | 16 | # MOCK API 17 | VITE_MOCK_API = https://www.fastmock.site/mock/5841b82d5672783b6fd62bb2a06aeb1f/api 18 | 19 | -------------------------------------------------------------------------------- /src/components/LowCodeComponents/LowCodeTitle/interface.ts: -------------------------------------------------------------------------------- 1 | export type LowCodeTitlePropsType = { 2 | text?: string 3 | level?: 1 | 2 | 3 | 4 | 5 4 | isCenter?: boolean 5 | 6 | onChange?: (newProps: LowCodeTitlePropsType) => void 7 | disabled?: boolean 8 | } 9 | 10 | export const LowCodeTitleDefaultProps: LowCodeTitlePropsType = { 11 | text: '一行标题', 12 | level: 1, 13 | isCenter: false 14 | } 15 | -------------------------------------------------------------------------------- /src/components/NavFooter/index.module.less: -------------------------------------------------------------------------------- 1 | .footer { 2 | text-align: center; 3 | line-height: 30px; 4 | color: #b0aeae; 5 | font-size: 14px; 6 | margin-top: 20px; 7 | background-color: var(--dark-bg-color); 8 | height: 30px; 9 | :global(a) { 10 | color: #b0aeae; 11 | &:hover { 12 | color: #ed6c00; 13 | } 14 | } 15 | :global(.gutter) { 16 | margin: 0 10px; 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/views/editor/components/ComponentLib.module.less: -------------------------------------------------------------------------------- 1 | .wrapper { 2 | margin-bottom: 12px; 3 | cursor: pointer; 4 | background-color: #f7f7f7; 5 | border: 1px solid #f7f7f7; 6 | padding: 12px; 7 | border-radius: 3px; 8 | 9 | &:hover { 10 | border-color: #d9d9d9; 11 | } 12 | } 13 | 14 | .component { 15 | pointer-events: none; // 屏蔽鼠标的行为 16 | } 17 | 18 | .listWarp { 19 | height: 80vh; 20 | overflow-y: auto; 21 | } 22 | -------------------------------------------------------------------------------- /.prettierrc.cjs: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | // 每行最大列,超过换行 3 | printWidth: 120, 4 | // 使用制表符而不是空格缩进 5 | useTabs: false, 6 | // 缩进 7 | tabWidth: 2, 8 | // 结尾不用分号 9 | semi: false, 10 | // 使用单引号 11 | singleQuote: true, 12 | // 在JSX中使用单引号而不是双引号 13 | jsxSingleQuote: true, 14 | // 箭头函数里面,如果是一个参数的时候,去掉括号 15 | arrowParens: 'avoid', 16 | // 对象、数组括号与文字间添加空格 17 | bracketSpacing: true, 18 | // 尾随逗号 19 | trailingComma: 'none' 20 | } 21 | -------------------------------------------------------------------------------- /src/types/modal.ts: -------------------------------------------------------------------------------- 1 | import { MutableRefObject } from 'react' 2 | import { User } from './api' 3 | 4 | export type IAction = 'create' | 'edit' | 'delete' 5 | 6 | export interface IModalProp { 7 | mRef: MutableRefObject<{ open: (type: IAction, data: T) => void } | undefined> 8 | update: () => void 9 | } 10 | 11 | export interface IDetailProp { 12 | mRef: MutableRefObject<{ open: (orderId: string) => void } | undefined> 13 | } 14 | -------------------------------------------------------------------------------- /src/components/LowCodeComponents/LowCodeInfo/index.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @description 问卷 info 组件 3 | * 4 | */ 5 | 6 | import Component from './Component' 7 | import PropComponent from './PropComponent' 8 | import { LowCodeInfoDefaultProps } from './interface' 9 | 10 | export * from './interface' 11 | 12 | export default { 13 | title: '问卷信息', 14 | type: 'LowCodeInfo', 15 | Component, 16 | PropComponent, 17 | defaultProps: LowCodeInfoDefaultProps 18 | } 19 | -------------------------------------------------------------------------------- /src/components/LowCodeComponents/LowCodeRadio/index.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @description 问卷 radio 3 | * 4 | */ 5 | 6 | import Component from './Component' 7 | import PropComponent from './PropComponent' 8 | 9 | import { LowCodeRadioDefaultProps } from './interface' 10 | 11 | export * from './interface' 12 | 13 | export default { 14 | title: '单选', 15 | type: 'LowCodeRadio', 16 | Component, 17 | PropComponent, 18 | defaultProps: LowCodeRadioDefaultProps 19 | } 20 | -------------------------------------------------------------------------------- /src/components/Menu/index.module.less: -------------------------------------------------------------------------------- 1 | .navSide { 2 | background-color: var(--dark-bg-color); 3 | color: var(--dark-bolor); 4 | height: 100vh; 5 | } 6 | .logo { 7 | display: flex; 8 | align-items: center; 9 | font-size: 16px; 10 | background-color: var(--dark-logo-color); 11 | color: #fff; 12 | height: 50px; 13 | line-height: 50px; 14 | cursor: pointer; 15 | } 16 | 17 | .img { 18 | width: 32px; 19 | height: 32px; 20 | margin: 0 16px; 21 | } 22 | -------------------------------------------------------------------------------- /src/components/LowCodeComponents/LowCodeCheckbox/index.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @description 问卷 checkbox 3 | * 4 | */ 5 | 6 | import Component from './Component' 7 | import PropComponent from './PropComponent' 8 | import { LowCodeCheckboxDefaultProps } from './interface' 9 | 10 | export * from './interface' 11 | 12 | export default { 13 | title: '多选', 14 | type: 'LowCodeCheckbox', // 要和后端统一好 15 | Component, 16 | PropComponent, 17 | defaultProps: LowCodeCheckboxDefaultProps 18 | } 19 | -------------------------------------------------------------------------------- /src/components/LowCodeComponents/LowCodeTitle/index.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @description 问卷 标题 3 | * 4 | */ 5 | 6 | import Component from './Component' 7 | import PropComponent from './PropComponent' 8 | import { LowCodeTitleDefaultProps } from './interface' 9 | 10 | export * from './interface' 11 | 12 | // Title 组件的配置 13 | export default { 14 | title: '标题', 15 | type: 'LowCodeTitle', // 要和后端统一好 16 | Component, 17 | PropComponent, 18 | defaultProps: LowCodeTitleDefaultProps 19 | } 20 | -------------------------------------------------------------------------------- /src/components/LowCodeComponents/LowCodeInput/index.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * 问卷 输入框 3 | * 4 | * 5 | */ 6 | 7 | import Component from './Component' 8 | import PropComponent from './PropComponent' 9 | import { LowCodeInputDefaultProps } from './interface' 10 | 11 | export * from './interface' 12 | 13 | // Input 组件的配置 14 | export default { 15 | title: '输入框', 16 | type: 'LowCodeInput', // 要和后端统一好 17 | Component, // 画布显示的组件 18 | PropComponent, 19 | defaultProps: LowCodeInputDefaultProps 20 | } 21 | -------------------------------------------------------------------------------- /src/components/NavFooter/index.tsx: -------------------------------------------------------------------------------- 1 | import styles from './index.module.less' 2 | 3 | const NavFooter = () => { 4 | return ( 5 |
6 |
7 | 8 | zc github主页 9 | 10 | | 11 | 2023 © React-Manager By ZC. 12 |
13 |
14 | ) 15 | } 16 | 17 | export default NavFooter 18 | -------------------------------------------------------------------------------- /src/components/LowCodeComponents/LowCodeParagraph/index.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @description 问卷 - 段落 3 | * 4 | */ 5 | 6 | import Component from './Component' 7 | import PropComponent from './PropComponent' 8 | import { LowCodeParagraphDefaultProps } from './interface' 9 | 10 | export * from './interface' 11 | 12 | // Paragraph 组件的配置 13 | export default { 14 | title: '段落', 15 | type: 'LowCodeParagraph', // 要和后端统一好 16 | Component, 17 | PropComponent, 18 | defaultProps: LowCodeParagraphDefaultProps 19 | } 20 | -------------------------------------------------------------------------------- /src/views/editor/components/EditHeader.module.less: -------------------------------------------------------------------------------- 1 | .header-wrapper { 2 | background-color: #fff; 3 | border-bottom: 1px solid #e8e8e8; 4 | padding: 12px 0; 5 | } 6 | 7 | .header { 8 | display: flex; 9 | margin: 0 24px; 10 | 11 | h1 { 12 | font-size: 18px; 13 | margin-bottom: 0; 14 | line-height: 1; 15 | } 16 | 17 | .left { 18 | flex: 1; 19 | } 20 | 21 | .main { 22 | flex: 1; 23 | text-align: center; 24 | } 25 | 26 | .right { 27 | flex: 1; 28 | text-align: right; 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/components/LowCodeComponents/LowCodeTextarea/index.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @description 问卷 多行输入 3 | * 4 | */ 5 | 6 | import Component from './Component' 7 | import PropComponent from './PropComponent' 8 | import { LowCodeTextareaDefaultProps } from './interface' 9 | 10 | export * from './interface' 11 | 12 | // Textarea 组件的配置 13 | export default { 14 | title: '多行输入', 15 | type: 'LowCodeTextarea', // 要和后端统一好 16 | Component, // 画布显示的组件 17 | PropComponent, // 修改属性 18 | defaultProps: LowCodeTextareaDefaultProps 19 | } 20 | -------------------------------------------------------------------------------- /src/router/AuthLoader.ts: -------------------------------------------------------------------------------- 1 | import api from '@/api' 2 | import { Menu } from '@/types/api' 3 | import { getMenuPath } from '@/utils' 4 | export interface IAuthLoader { 5 | buttonList: string[] 6 | menuList: Menu.MenuItem[] 7 | menuPathList: string[] 8 | } 9 | export default async function AuthLoader() { 10 | const data = await api.getPermissionList() 11 | 12 | const menuPathList = getMenuPath(data.menuList) 13 | return { 14 | buttonList: data.buttonList, 15 | menuList: data.menuList, 16 | menuPathList 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/utils/loading/index.tsx: -------------------------------------------------------------------------------- 1 | import './loading.less' 2 | 3 | let count = 0 4 | 5 | export const showLoading = () => { 6 | if (count === 0) { 7 | const loading = document.getElementById('loading') as HTMLDivElement 8 | loading.style.setProperty('display', 'flex') 9 | } 10 | count++ 11 | } 12 | 13 | export const hideLoading = () => { 14 | count-- 15 | 16 | if (count === 0) { 17 | const loading = document.getElementById('loading') as HTMLDivElement 18 | loading.style.setProperty('display', 'none') 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/hook/useCharts.ts: -------------------------------------------------------------------------------- 1 | import * as echarts from 'echarts' 2 | import { useEffect, useRef, useState, RefObject } from 'react' 3 | 4 | export const useCharts = (): [RefObject, echarts.EChartsType | undefined] => { 5 | const chartRef = useRef(null) 6 | const [chartInstance, setChartInstance] = useState() 7 | useEffect(() => { 8 | const chart = echarts.init(chartRef.current as HTMLElement) 9 | setChartInstance(chart) 10 | }, []) 11 | 12 | return [chartRef, chartInstance] 13 | } 14 | -------------------------------------------------------------------------------- /src/views/lowcode/index.module.less: -------------------------------------------------------------------------------- 1 | .header { 2 | display: flex; 3 | margin-bottom: 20px; 4 | background-color: var(--dark-bg-color); 5 | padding: 20px; 6 | 7 | .left { 8 | flex: 1; 9 | } 10 | 11 | .right { 12 | flex: 1; 13 | text-align: right; 14 | } 15 | } 16 | 17 | .content { 18 | display: flex; 19 | flex-wrap: wrap; 20 | justify-content: space-between; 21 | padding: 10px; 22 | background-color: var(--dark-bg-color); 23 | margin-bottom: 20px; 24 | } 25 | 26 | .footer { 27 | text-align: center; 28 | } 29 | -------------------------------------------------------------------------------- /src/components/LowCodeComponents/LowCodeParagraph/Component.test.tsx: -------------------------------------------------------------------------------- 1 | import { render, screen } from '@testing-library/react' 2 | import Component from './Component' 3 | 4 | test('默认属性', () => { 5 | render() 6 | const span = screen.getByText('一行段落') 7 | expect(span).toBeInTheDocument() 8 | }) 9 | 10 | test('传入属性', () => { 11 | render() 12 | 13 | const span = screen.getByText('a') 14 | expect(span).toBeInTheDocument() 15 | 16 | expect(span).toHaveTextContent('a') 17 | expect(span).not.toHaveTextContent('ab') // 被换行了 18 | }) 19 | -------------------------------------------------------------------------------- /src/language/index.ts: -------------------------------------------------------------------------------- 1 | import i18n from "i18next"; 2 | import enUsTrans from "./modules/en"; 3 | import zhCnTrans from "./modules/zh"; 4 | import { initReactI18next } from "react-i18next"; 5 | 6 | i18n.use(initReactI18next).init({ 7 | resources: { 8 | en: { 9 | translation: enUsTrans 10 | }, 11 | zh: { 12 | translation: zhCnTrans 13 | } 14 | }, 15 | // 选择默认语言,选择内容为上述配置中的 key,即 en/zh 16 | fallbackLng: "zh", 17 | debug: false, 18 | interpolation: { 19 | escapeValue: false // not needed for react as it escapes by default 20 | } 21 | }); 22 | 23 | export default i18n; 24 | -------------------------------------------------------------------------------- /src/views/editor/components/EditCanvas.module.less: -------------------------------------------------------------------------------- 1 | .canvas { 2 | min-height: 100%; 3 | height: 88vh; 4 | background-color: #fff; 5 | overflow-y: auto; 6 | } 7 | 8 | .component-wrapper { 9 | margin: 12px; 10 | border: 1px solid #fff; 11 | padding: 12px; 12 | border-radius: 3px; 13 | 14 | &:hover { 15 | border-color: #d9d9d9; 16 | } 17 | } 18 | 19 | .selected { 20 | border-color: #1890ff !important; 21 | } 22 | 23 | .locked { 24 | opacity: 0.5; 25 | cursor: not-allowed; 26 | } 27 | 28 | .component { 29 | pointer-events: none; // 屏蔽鼠标行为,组件不让被点击到 30 | } 31 | -------------------------------------------------------------------------------- /src/router/LazyLoad.tsx: -------------------------------------------------------------------------------- 1 | import { Suspense } from 'react' 2 | import { Spin } from 'antd' 3 | /** 4 | * 组件懒加载,结合Suspense实现 5 | * @param Component 组件对象 6 | * @returns 返回新组件 7 | */ 8 | export const lazyLoad = (Component: React.LazyExoticComponent<() => JSX.Element>): React.ReactNode => { 9 | return ( 10 | 16 | } 17 | > 18 | 19 | 20 | ) 21 | } 22 | -------------------------------------------------------------------------------- /src/views/editor/components/Layers.module.less: -------------------------------------------------------------------------------- 1 | .wrapper { 2 | padding: 6px 0; 3 | border-bottom: 1px solid rgba(0, 0, 0, 0.06); 4 | display: flex; 5 | height: 100%; 6 | // overflow-y: auto; 7 | .title { 8 | flex: auto; 9 | line-height: 2; 10 | cursor: pointer; 11 | } 12 | 13 | .selected { 14 | color: #1890ff; 15 | } 16 | 17 | .handler { 18 | width: 50px; 19 | text-align: end; 20 | 21 | .btn { 22 | opacity: 0.2; 23 | } 24 | } 25 | 26 | &:hover { 27 | .handler { 28 | .btn { 29 | opacity: 1; 30 | } 31 | } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/components/LowCodeComponents/LowCodeTitle/Component.test.tsx: -------------------------------------------------------------------------------- 1 | import { render, screen } from '@testing-library/react' 2 | import Component from './Component' 3 | 4 | test('默认属性', () => { 5 | render() 6 | 7 | const h = screen.getByText('一行标题') 8 | 9 | expect(h).toBeInTheDocument() 10 | }) 11 | 12 | test('传入属性', () => { 13 | render() 14 | 15 | const h = screen.getByText('wdawd') 16 | expect(h).toBeInTheDocument() 17 | 18 | expect(h.matches('h2')).toBeTruthy() 19 | 20 | const style = h.style 21 | expect(style.textAlign).toBe('center') 22 | }) 23 | -------------------------------------------------------------------------------- /src/components/AuthButton.tsx: -------------------------------------------------------------------------------- 1 | import { IAuthLoader } from '@/router/AuthLoader' 2 | import { useUserStore } from '@/store/useUserStore' 3 | import { Button } from 'antd' 4 | import { useRouteLoaderData } from 'react-router-dom' 5 | 6 | export default function AuthButton(props: any) { 7 | const data = useRouteLoaderData('layout') as IAuthLoader 8 | const role = useUserStore(state => state.userInfo.role) 9 | if (!props.auth) return 10 | if (data.buttonList.includes(props.auth) || role == 1) { 11 | return 12 | } 13 | return <> 14 | } 15 | -------------------------------------------------------------------------------- /src/components/LowCodeComponents/LowCodeInput/Component.tsx: -------------------------------------------------------------------------------- 1 | import { FC } from 'react' 2 | import { Typography, Input } from 'antd' 3 | import { LowCodeInputPropsType, LowCodeInputDefaultProps } from './interface' 4 | 5 | const { Paragraph } = Typography 6 | 7 | const LowCodeInput: FC = (props: LowCodeInputPropsType) => { 8 | const { title, placeholder } = { ...LowCodeInputDefaultProps, ...props } 9 | 10 | return ( 11 |
12 | {title} 13 |
14 | 15 |
16 |
17 | ) 18 | } 19 | 20 | export default LowCodeInput 21 | -------------------------------------------------------------------------------- /src/views/lowcode/components/ListSearch.tsx: -------------------------------------------------------------------------------- 1 | import { FC, useState } from 'react' 2 | import type { ChangeEvent } from 'react' 3 | import { Input } from 'antd' 4 | 5 | const { Search } = Input 6 | 7 | const ListSearch: FC = () => { 8 | const [value, setValue] = useState('') 9 | 10 | function handleChange(event: ChangeEvent) { 11 | setValue(event.target.value) 12 | } 13 | 14 | return ( 15 | 23 | ) 24 | } 25 | 26 | export default ListSearch 27 | -------------------------------------------------------------------------------- /src/components/SearchForm.tsx: -------------------------------------------------------------------------------- 1 | import { Form, Space, Button } from 'antd' 2 | /** 3 | * 搜索表单容器组件封装 4 | * @param props 5 | * @returns 6 | */ 7 | export default function SearchForm(props: any) { 8 | return ( 9 |
10 | {props.children} 11 | 12 | 13 | 16 | 19 | 20 | 21 |
22 | ) 23 | } 24 | -------------------------------------------------------------------------------- /src/views/404.tsx: -------------------------------------------------------------------------------- 1 | import { Button, Result } from 'antd' 2 | import { useTranslation } from 'react-i18next' 3 | import { useNavigate } from 'react-router-dom' 4 | 5 | function NotFound() { 6 | const navigate = useNavigate() 7 | 8 | const { t } = useTranslation() 9 | 10 | const handleClick = () => { 11 | navigate('/') 12 | } 13 | 14 | return ( 15 | 21 | {t('result.backHome')} 22 | 23 | } 24 | /> 25 | ) 26 | } 27 | 28 | export default NotFound 29 | -------------------------------------------------------------------------------- /src/views/403.tsx: -------------------------------------------------------------------------------- 1 | import { Button, Result } from 'antd' 2 | import { useTranslation } from 'react-i18next' 3 | import { useNavigate } from 'react-router-dom' 4 | 5 | function NotFound() { 6 | const navigate = useNavigate() 7 | 8 | const { t } = useTranslation() 9 | 10 | const handleClick = () => { 11 | navigate('/') 12 | } 13 | 14 | return ( 15 | 21 | {t('result.backHome')} 22 | 23 | } 24 | /> 25 | ) 26 | } 27 | 28 | export default NotFound 29 | -------------------------------------------------------------------------------- /src/utils/AntdGlobal.tsx: -------------------------------------------------------------------------------- 1 | // Entry component 2 | import { App } from 'antd' 3 | import type { MessageInstance } from 'antd/es/message/interface' 4 | import type { ModalStaticFunctions } from 'antd/es/modal/confirm' 5 | import type { NotificationInstance } from 'antd/es/notification/interface' 6 | 7 | let message: MessageInstance 8 | let notification: NotificationInstance 9 | let modal: Omit 10 | 11 | export default () => { 12 | const staticFunction = App.useApp() 13 | message = staticFunction.message 14 | modal = staticFunction.modal 15 | notification = staticFunction.notification 16 | return null 17 | } 18 | 19 | export { message, notification, modal } 20 | -------------------------------------------------------------------------------- /src/components/LowCodeComponents/LowCodeInput/Component.test.tsx: -------------------------------------------------------------------------------- 1 | import { render, screen } from '@testing-library/react' 2 | 3 | import Component from './Component' 4 | 5 | test('默认属性', () => { 6 | render() 7 | 8 | const p = screen.getByText('输入框标题') 9 | expect(p).toBeInTheDocument() 10 | 11 | const input = screen.getByPlaceholderText('请输入...') 12 | expect(input).toBeInTheDocument() 13 | }) 14 | 15 | test('传入属性', () => { 16 | render() 17 | 18 | const p = screen.getByText('hello') 19 | expect(p).toBeInTheDocument() 20 | 21 | const input = screen.getByPlaceholderText('world') 22 | expect(input).toBeInTheDocument() 23 | }) 24 | -------------------------------------------------------------------------------- /src/components/DragSortable/SortableItem.tsx: -------------------------------------------------------------------------------- 1 | import { FC } from 'react' 2 | import { useSortable } from '@dnd-kit/sortable' 3 | import { CSS } from '@dnd-kit/utilities' 4 | 5 | type PropsType = { 6 | id: string 7 | children: JSX.Element 8 | } 9 | 10 | const SortableItem: FC = ({ id, children }) => { 11 | const { attributes, listeners, setNodeRef, transform, transition } = useSortable({ id }) 12 | 13 | const style = { 14 | transform: CSS.Transform.toString(transform), 15 | transition 16 | } 17 | 18 | return ( 19 |
20 | {children} 21 |
22 | ) 23 | } 24 | 25 | export default SortableItem 26 | -------------------------------------------------------------------------------- /src/components/LowCodeComponents/LowCodeTextarea/Component.test.tsx: -------------------------------------------------------------------------------- 1 | import { render, screen } from '@testing-library/react' 2 | 3 | import Component from './Component' 4 | 5 | test('默认属性', () => { 6 | render() 7 | 8 | const p = screen.getByText('输入框标题') 9 | expect(p).toBeInTheDocument() 10 | 11 | const textarea = screen.getByPlaceholderText('请输入...') 12 | expect(textarea).toBeInTheDocument() 13 | }) 14 | 15 | test('传入属性', () => { 16 | render() 17 | 18 | const p = screen.getByText('hello') 19 | expect(p).toBeInTheDocument() 20 | 21 | const textarea = screen.getByPlaceholderText('world') 22 | expect(textarea).toBeInTheDocument() 23 | }) 24 | -------------------------------------------------------------------------------- /src/components/LowCodeComponents/LowCodeTextarea/Component.tsx: -------------------------------------------------------------------------------- 1 | import { FC } from 'react' 2 | import { Typography, Input } from 'antd' 3 | import { LowCodeTextareaPropsType, LowCodeTextareaDefaultProps } from './interface' 4 | 5 | const { Paragraph } = Typography 6 | const { TextArea } = Input 7 | 8 | const LowCodeTextarea: FC = (props: LowCodeTextareaPropsType) => { 9 | const { title, placeholder } = { ...LowCodeTextareaDefaultProps, ...props } 10 | 11 | return ( 12 |
13 | {title} 14 |
15 | 16 |
17 |
18 | ) 19 | } 20 | 21 | export default LowCodeTextarea 22 | -------------------------------------------------------------------------------- /src/api/lowCodeApi.ts: -------------------------------------------------------------------------------- 1 | import request from '@/utils/request' 2 | import { ResultData, LowCode } from '@/types/api' 3 | import '@/_mock/lowCode' 4 | 5 | export default { 6 | // 获取单个表单信息 7 | getLowCodeService(id: string) { 8 | return request.get(`/lowcode/item/${id}`) 9 | }, 10 | // 创建表单 11 | createLowCodeService() { 12 | return request.post('/lowcode/create') 13 | }, 14 | // 获取(查询)表单列表 15 | getLowCodeListService(params: LowCode.LowCodeSearch) { 16 | return request.get>('/lowcode/list', params) 17 | }, 18 | // 更新表单 19 | updateLowCodeService(id: string, params: LowCode.LowCodeUpdateItem) { 20 | return request.post(`/lowcode/update/${id}`, params) 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/components/CitySelect/useCityData.ts: -------------------------------------------------------------------------------- 1 | import { useEffect, useState } from 'react' 2 | import api from '@/api/proComponentsApi' 3 | import { CityMapType, CityMapItem } from './interface' 4 | import { getCityDataMap } from './utils' 5 | 6 | function useCityData() { 7 | const [cityList, setCityList] = useState([]) 8 | const [cityMap, setCityMap] = useState({}) 9 | 10 | const getCityList = async () => { 11 | const cityData = await api.getCityList() 12 | const { cityMap, cityList } = getCityDataMap(cityData) 13 | setCityMap(cityMap) 14 | setCityList(cityList) 15 | } 16 | 17 | useEffect(() => { 18 | getCityList() 19 | }, []) 20 | 21 | return { 22 | cityList, 23 | cityMap 24 | } 25 | } 26 | 27 | export default useCityData 28 | -------------------------------------------------------------------------------- /src/components/NavHeader/index.module.less: -------------------------------------------------------------------------------- 1 | .navHeader { 2 | display: flex; 3 | justify-content: space-between; 4 | align-items: center; 5 | height: 50px; 6 | padding: 0 20px; 7 | 8 | background-color: var(--dark-bg-color); 9 | color: var(--dark-color); 10 | } 11 | 12 | .left { 13 | display: flex; 14 | align-items: center; 15 | } 16 | .right { 17 | display: flex; 18 | 19 | align-items: center; 20 | 21 | .icon { 22 | width: 23px; 23 | height: 23px; 24 | margin-right: 20px; 25 | } 26 | } 27 | .info { 28 | display: flex; 29 | align-items: center; 30 | justify-content: space-around; 31 | width: 90px; 32 | cursor: pointer; 33 | } 34 | 35 | .nickName { 36 | color: var(--dark-color); 37 | } 38 | 39 | .userImg { 40 | width: 26px; 41 | height: 26px; 42 | } 43 | -------------------------------------------------------------------------------- /src/components/LowCodeComponents/LowCodeRadio/interface.ts: -------------------------------------------------------------------------------- 1 | export type OptionType = { 2 | value: string 3 | text: string 4 | } 5 | 6 | export type LowCodeRadioPropsType = { 7 | title?: string 8 | isVertical?: boolean 9 | options?: OptionType[] 10 | value?: string 11 | 12 | // 用于 PropComponent 13 | onChange?: (newProps: LowCodeRadioPropsType) => void 14 | disabled?: boolean 15 | } 16 | 17 | export const LowCodeRadioDefaultProps: LowCodeRadioPropsType = { 18 | title: '单选标题', 19 | isVertical: false, 20 | options: [ 21 | { value: 'item1', text: '选项1' }, 22 | { value: 'item2', text: '选项2' }, 23 | { value: 'item3', text: '选项3' } 24 | ], 25 | value: '' 26 | } 27 | 28 | // 统计组件的属性类型 29 | export type LowCodeRadioStatPropsType = { 30 | stat: Array<{ name: string; count: number }> 31 | } 32 | -------------------------------------------------------------------------------- /.eslintrc.cjs: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | env: { 3 | browser: true, 4 | es2021: true, 5 | node: true 6 | }, 7 | extends: ['eslint:recommended', 'plugin:@typescript-eslint/recommended', 'plugin:react-hooks/recommended'], 8 | overrides: [], 9 | parser: '@typescript-eslint/parser', 10 | parserOptions: { 11 | ecmaVersion: 'latest', 12 | sourceType: 'module' 13 | }, 14 | plugins: ['react-refresh'], 15 | /* 16 | * "off" 或 0 ==> 关闭规则 17 | * "warn" 或 1 ==> 打开的规则作为警告(不影响代码执行) 18 | * "error" 或 2 ==> 规则作为一个错误(代码不能执行,界面报错) 19 | */ 20 | rules: { 21 | 'react/react-in-jsx-scope': 'off', 22 | 'no-console': 'off', // 禁止使用console 23 | 'no-unused-vars': 'off', // 禁止定义未使用的变量 24 | 'no-debugger': 'error', // 禁止使用debugger 25 | 'no-var': 'error' // 要求使用 let 或 const 而不是 var 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/components/LowCodeComponents/LowCodeInfo/Component.test.tsx: -------------------------------------------------------------------------------- 1 | import { render, screen } from '@testing-library/react' 2 | import Component from './Component' 3 | 4 | test('默认属性', () => { 5 | render() 6 | 7 | const h = screen.getByText('问卷标题') 8 | 9 | expect(h).toBeInTheDocument() 10 | }) 11 | 12 | test('传入属性', () => { 13 | render() 14 | 15 | const h = screen.getByText('wdawd') 16 | 17 | expect(h).toBeInTheDocument() 18 | 19 | const p = screen.getByText('dddd') 20 | expect(p).toBeInTheDocument() 21 | }) 22 | 23 | test('多行文字', () => { 24 | render() 25 | 26 | const span = screen.getByText('a') 27 | expect(span).toBeInTheDocument() 28 | 29 | expect(span).toHaveTextContent('a') 30 | expect(span).not.toHaveTextContent('ab') // 被换行了 31 | }) 32 | -------------------------------------------------------------------------------- /src/App.less: -------------------------------------------------------------------------------- 1 | * { 2 | padding: 0; 3 | margin: 0; 4 | box-sizing: border-box; 5 | } 6 | .welcome { 7 | font-size: 30px; 8 | margin-top: 200px; 9 | text-align: center; 10 | } 11 | .search-form { 12 | background-color: var(--dark-bg-color); 13 | padding: 20px; 14 | border-radius: 5px; 15 | color: #000; 16 | } 17 | 18 | .base-table { 19 | background-color: var(--dark-bg-color); 20 | border-radius: 5px; 21 | color: #000; 22 | .header-wrapper { 23 | display: flex; 24 | justify-content: space-between; 25 | align-items: center; 26 | border-radius: 5px 5px 0 0; 27 | color: var(--dark-color); 28 | padding: 15px; 29 | .action button { 30 | margin-left: 15px; 31 | } 32 | } 33 | } 34 | 35 | .search-form + .base-table { 36 | margin-top: 20px; 37 | } 38 | 39 | .mr10 { 40 | margin-right: 10px; 41 | } 42 | -------------------------------------------------------------------------------- /src/views/editor/components/leftPanel.tsx: -------------------------------------------------------------------------------- 1 | import { FC } from 'react' 2 | import { Tabs } from 'antd' 3 | import { AppstoreOutlined, BarsOutlined } from '@ant-design/icons' 4 | import ComponentLib from './ComponentLib' 5 | import Layers from './Layers' 6 | 7 | const LeftPanel: FC = () => { 8 | const tabsItems = [ 9 | { 10 | key: 'componentLib', 11 | label: ( 12 | 13 | 14 | 组件库 15 | 16 | ), 17 | children: 18 | }, 19 | { 20 | key: 'layers', 21 | label: ( 22 | 23 | 24 | 图层 25 | 26 | ), 27 | children: 28 | } 29 | ] 30 | 31 | return 32 | } 33 | 34 | export default LeftPanel 35 | -------------------------------------------------------------------------------- /src/components/LowCodeComponents/LowCodeCheckbox/interface.ts: -------------------------------------------------------------------------------- 1 | export type OptionType = { 2 | value: string 3 | text: string 4 | checked: boolean 5 | } 6 | 7 | export type LowCodeCheckboxPropsType = { 8 | title?: string 9 | isVertical?: boolean 10 | list?: OptionType[] 11 | 12 | // 用于 PropComponent 13 | onChange?: (newProps: LowCodeCheckboxPropsType) => void 14 | disabled?: boolean 15 | } 16 | 17 | export const LowCodeCheckboxDefaultProps: LowCodeCheckboxPropsType = { 18 | title: '多选标题', 19 | isVertical: false, 20 | list: [ 21 | { value: 'item1', text: '选项1', checked: false }, 22 | { value: 'item2', text: '选项2', checked: false }, 23 | { value: 'item3', text: '选项3', checked: false } 24 | ] 25 | } 26 | 27 | // 统计组件的属性类型 28 | export type LowCodeCheckboxStatPropsType = { 29 | stat: Array<{ name: string; count: number }> 30 | } 31 | -------------------------------------------------------------------------------- /src/language/modules/zh.ts: -------------------------------------------------------------------------------- 1 | export default { 2 | login: { 3 | confirm: '登录', 4 | username: '用户名', 5 | password: '密码', 6 | userNameRuleMes: '请输入用户名!', 7 | passwordRuleMes: '请输入密码!', 8 | success: '登录成功!' 9 | }, 10 | result: { 11 | backHome: '回到首页', 12 | authorizedError: '抱歉,您当前没有权限访问此页面。', 13 | notFoundPage: '抱歉,您访问的页面不存在。' 14 | }, 15 | tabs: { 16 | home: '首页' 17 | }, 18 | dashboard: { 19 | desTitle: '欢迎新同学,每天都要开心!', 20 | userID: '用户ID', 21 | email: '邮箱', 22 | status: '状态', 23 | mobile: '手机号', 24 | job: '岗位', 25 | department: '部门', 26 | refresh: '刷新', 27 | amount: '数量', 28 | total: '总流水', 29 | orderTotal: '总订单', 30 | openingCities: '开通城市' 31 | }, 32 | header: { 33 | darkMode: '暗黑', 34 | lightMode: '浅色', 35 | email: '邮箱', 36 | logout: '退出登录' 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/components/LowCodeComponents/LowCodeInfo/Component.tsx: -------------------------------------------------------------------------------- 1 | import { FC } from 'react' 2 | import { Typography } from 'antd' 3 | import { LowCodeInfoPropsType, LowCodeInfoDefaultProps } from './interface' 4 | 5 | const { Title, Paragraph } = Typography 6 | 7 | const Component: FC = (props: LowCodeInfoPropsType) => { 8 | const { title, desc = '' } = { ...LowCodeInfoDefaultProps, ...props } 9 | 10 | const descTextList = desc.split('\n') 11 | 12 | return ( 13 |
14 | {title} 15 | 16 | {descTextList.map((t, index) => ( 17 | 18 | {index > 0 &&
} 19 | {t} 20 |
21 | ))} 22 |
23 |
24 | ) 25 | } 26 | 27 | export default Component 28 | -------------------------------------------------------------------------------- /src/utils/storage.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * localStorage模块封装 3 | */ 4 | 5 | export default { 6 | /** 7 | * storage存储 8 | * @param key {string} 参数名称 9 | * @param value {any} 写入值 10 | */ 11 | set(key: string, value: any) { 12 | localStorage.setItem(key, JSON.stringify(value)) 13 | }, 14 | /** 15 | * storage读取 16 | * @param key {string} 参数名称 17 | * @returns storage值 18 | */ 19 | get(key: string) { 20 | const value = localStorage.getItem(key) 21 | if (!value) return '' 22 | try { 23 | return JSON.parse(value) 24 | } catch (error) { 25 | return value 26 | } 27 | }, 28 | /** 29 | * 删除localStorage值 30 | * @param key {string} 参数名称 31 | */ 32 | remove(key: string) { 33 | localStorage.removeItem(key) 34 | }, 35 | /** 36 | * 清空localStorage值 37 | */ 38 | clear() { 39 | localStorage.clear() 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ES2020", 4 | "useDefineForClassFields": true, 5 | "lib": [ 6 | "ES2020", 7 | "DOM", 8 | "DOM.Iterable" 9 | ], 10 | "module": "ESNext", 11 | "skipLibCheck": true, 12 | /* Bundler mode */ 13 | "moduleResolution": "node", 14 | "allowSyntheticDefaultImports": true, 15 | // "allowImportingTsExtensions": true, 16 | "resolveJsonModule": true, 17 | "isolatedModules": true, 18 | "noEmit": true, 19 | "jsx": "react-jsx", 20 | /* Linting */ 21 | "strict": true, 22 | "noUnusedLocals": true, 23 | "noUnusedParameters": true, 24 | "noFallthroughCasesInSwitch": true, 25 | "baseUrl": "./", 26 | "paths": { 27 | "@/*": [ 28 | "src/*" 29 | ] 30 | } 31 | }, 32 | "include": [ 33 | "src", 34 | "typings.d.ts" 35 | ], 36 | "references": [ 37 | { 38 | "path": "./tsconfig.node.json" 39 | } 40 | ] 41 | } 42 | -------------------------------------------------------------------------------- /src/api/roleApi.ts: -------------------------------------------------------------------------------- 1 | import request from '@/utils/request' 2 | import { ResultData, Role } from '@/types/api' 3 | import '@/_mock/role' 4 | 5 | export default { 6 | // 获取角色列表 7 | getRoleList(params: Role.Params) { 8 | return request.get>('/roles/list', params) 9 | }, 10 | // 创建角色 11 | createRole(params: Role.CreateParams) { 12 | return request.post('/roles/create', params) 13 | }, 14 | // 编辑角色 15 | editRole(params: Role.EditParams) { 16 | return request.post('/roles/edit', params) 17 | }, 18 | // 删除角色 19 | delRole(params: { _id: string }) { 20 | return request.post('/roles/delete', params) 21 | }, 22 | // 设置权限 23 | updatePermission(params: Role.Permission) { 24 | return request.post('/roles/update/permission', params) 25 | }, 26 | // 获取所有角色列表 27 | getAllRoleList() { 28 | return request.get('/roles/allList') 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/components/NavHeader/BreadCrumb.tsx: -------------------------------------------------------------------------------- 1 | import { IAuthLoader } from '@/router/AuthLoader' 2 | import { findTreeNode } from '@/utils' 3 | import { Breadcrumb } from 'antd' 4 | import { ReactNode, useEffect, useState } from 'react' 5 | import { useTranslation } from 'react-i18next' 6 | import { useLocation, useRouteLoaderData } from 'react-router-dom' 7 | 8 | export default function BreadCrumb() { 9 | const { pathname } = useLocation() 10 | const [breadList, setBreadList] = useState<(string | ReactNode)[]>([]) 11 | // 权限判断 12 | const data = useRouteLoaderData('layout') as IAuthLoader 13 | 14 | const { t } = useTranslation() 15 | 16 | useEffect(() => { 17 | const list = findTreeNode(data.menuList, pathname, []) 18 | setBreadList([{t('tabs.home')}, ...list]) 19 | }, [pathname]) 20 | return ({ title: item }))} style={{ marginLeft: 10 }} /> 21 | } 22 | -------------------------------------------------------------------------------- /src/components/LowCodeComponents/LowCodeParagraph/Component.tsx: -------------------------------------------------------------------------------- 1 | import { FC } from 'react' 2 | import { Typography } from 'antd' 3 | import { LowCodeParagraphPropsType, LowCodeParagraphDefaultProps } from './interface' 4 | 5 | const { Paragraph } = Typography 6 | 7 | const Component: FC = (props: LowCodeParagraphPropsType) => { 8 | const { text = '', isCenter = false } = { 9 | ...LowCodeParagraphDefaultProps, 10 | ...props 11 | } 12 | 13 | // 尽量不要使用 dangerouslySetInnerHTML ,不安全 14 | 15 | const textList = text.split('\n') // 例如 ['hello', '123', '456'] 16 | 17 | return ( 18 | 19 | {textList.map((t, index) => ( 20 | 21 | {index > 0 &&
} 22 | {t} 23 |
24 | ))} 25 |
26 | ) 27 | } 28 | 29 | export default Component 30 | -------------------------------------------------------------------------------- /src/components/LowCodeComponents/LowCodeCheckbox/Component.tsx: -------------------------------------------------------------------------------- 1 | import { FC } from 'react' 2 | import { Typography, Space, Checkbox } from 'antd' 3 | import { LowCodeCheckboxDefaultProps, LowCodeCheckboxPropsType } from './interface' 4 | 5 | const { Paragraph } = Typography 6 | 7 | const Component: FC = (props: LowCodeCheckboxPropsType) => { 8 | const { title, isVertical, list = [] } = { ...LowCodeCheckboxDefaultProps, ...props } 9 | 10 | return ( 11 |
12 | {title} 13 | 14 | {list.map(opt => { 15 | const { value, text, checked } = opt 16 | return ( 17 | 18 | {text} 19 | 20 | ) 21 | })} 22 | 23 |
24 | ) 25 | } 26 | 27 | export default Component 28 | -------------------------------------------------------------------------------- /src/views/editor/index.module.less: -------------------------------------------------------------------------------- 1 | .container { 2 | display: flex; 3 | flex-direction: column; 4 | height: 100vh; 5 | background-color: #f0f2f5; 6 | overflow: hidden; 7 | } 8 | 9 | .content-wrapper { 10 | flex: auto; 11 | padding: 12px 0; 12 | } 13 | 14 | .content { 15 | padding: 12px 0; 16 | margin: 0 24px; 17 | display: flex; 18 | justify-content: space-between; 19 | height: 100%; 20 | 21 | .left { 22 | width: 295px; 23 | background-color: #fff; 24 | padding: 0 12px; 25 | } 26 | 27 | .main { 28 | // flex: 1; 29 | 30 | .canvas-wrapper { 31 | // position: absolute; 32 | width: 400px; 33 | // height: 712px; 34 | // top: 50%; 35 | // left: 50%; 36 | // transform: translateX(-50%) translateY(-50%); 37 | 38 | box-shadow: 0 2px 10px #0000001f; 39 | } 40 | } 41 | 42 | .right { 43 | width: 300px; 44 | background-color: #fff; 45 | padding: 0 12px; 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/views/proComponents/index.tsx: -------------------------------------------------------------------------------- 1 | // import { useState } from 'react' 2 | import { Card, Space } from 'antd' 3 | // import CitySelect from '@/components/CitySelect' 4 | import styles from './index.module.less' 5 | 6 | export default function ProComponents() { 7 | // const [cityData, setCityData] = useState({}) 8 | // const initValue = { 9 | // area: ['130104', '120105', '120116'], 10 | // city: ['130100', '120100'], 11 | // province: ['120000', '130000'] 12 | // } 13 | // const onSelectChange = (val: any) => { 14 | // setCityData(val) 15 | // } 16 | 17 | return ( 18 |
19 | 20 | 21 | {/* onSelectChange(val)} /> */} 22 | 23 | {/*
{JSON.stringify(cityData)}
*/} 24 |
25 | 26 |

待更新。。。

27 |
28 |
29 | ) 30 | } 31 | -------------------------------------------------------------------------------- /src/components/LowCodeComponents/LowCodeRadio/Component.tsx: -------------------------------------------------------------------------------- 1 | import { FC } from 'react' 2 | import { Typography, Radio, Space } from 'antd' 3 | import { LowCodeRadioPropsType, LowCodeRadioDefaultProps } from './interface' 4 | 5 | const { Paragraph } = Typography 6 | 7 | const Component: FC = (props: LowCodeRadioPropsType) => { 8 | const { title, options = [], value, isVertical } = { ...LowCodeRadioDefaultProps, ...props } 9 | 10 | return ( 11 |
12 | {title} 13 | 14 | 15 | {options.map(opt => { 16 | const { value, text } = opt 17 | return ( 18 | 19 | {text} 20 | 21 | ) 22 | })} 23 | 24 | 25 |
26 | ) 27 | } 28 | 29 | export default Component 30 | -------------------------------------------------------------------------------- /src/components/LowCodeComponents/LowCodeTitle/Component.tsx: -------------------------------------------------------------------------------- 1 | import { FC } from 'react' 2 | import { Typography } from 'antd' 3 | import { LowCodeTitlePropsType, LowCodeTitleDefaultProps } from './interface' 4 | 5 | const { Title } = Typography 6 | 7 | const LowCodeTitle: FC = (props: LowCodeTitlePropsType) => { 8 | const { text = '', level = 1, isCenter = false } = { ...LowCodeTitleDefaultProps, ...props } 9 | 10 | const genFontSize = (level: number) => { 11 | if (level === 1) return '24px' 12 | if (level === 2) return '20px' 13 | if (level === 3) return '16px' 14 | return '16px' 15 | } 16 | 17 | return ( 18 |
19 | 27 | {text} 28 | 29 |
30 | ) 31 | } 32 | 33 | export default LowCodeTitle 34 | -------------------------------------------------------------------------------- /src/store/utils.ts: -------------------------------------------------------------------------------- 1 | // import { ComponentInfoType, lowCodeStateType } from './useLowCodeStore' 2 | import { LowCode } from '@/types/api' 3 | /** 4 | * 获取下一个 selectedId 5 | * @param fe_id 当前的 id 6 | * @param componentList 组件列表 7 | */ 8 | export function getNextSelectedId(fe_id: string, componentList: Array) { 9 | const visibleComponentList = componentList.filter(c => !c.isHidden) 10 | const index = visibleComponentList.findIndex(c => c.fe_id === fe_id) 11 | if (index < 0) return '' 12 | // 重新计算 selectedId 13 | let newSelectedId = '' 14 | const length = visibleComponentList.length 15 | if (length <= 1) { 16 | // 组件长度就一个,被删除了,就没有组件 17 | newSelectedId = '' 18 | } else { 19 | // 组件长度 > 1 20 | if (index + 1 === length) { 21 | // 要删除最后一个,就要选中上一个 22 | newSelectedId = visibleComponentList[index - 1].fe_id 23 | } else { 24 | // 要删除的不是最后一个,删除以后,选中下一个 25 | newSelectedId = visibleComponentList[index + 1].fe_id 26 | } 27 | } 28 | return newSelectedId 29 | } 30 | -------------------------------------------------------------------------------- /src/language/modules/en.ts: -------------------------------------------------------------------------------- 1 | export default { 2 | login: { 3 | confirm: 'Login', 4 | username: 'username', 5 | password: 'password', 6 | userNameRuleMes: 'Please input your username!', 7 | passwordRuleMes: 'Please input your password!', 8 | success: 'success' 9 | }, 10 | result: { 11 | backHome: 'Back Home', 12 | authorizedError: 'Sorry, you are not authorized to access this page.', 13 | notFoundPage: 'Sorry, the page you visited does not exist.' 14 | }, 15 | tabs: { 16 | home: 'Home' 17 | }, 18 | dashboard: { 19 | desTitle: 'Welcome! God Bless You!', 20 | userID: 'UserID', 21 | email: 'Email', 22 | status: 'Status', 23 | mobile: 'Mobile', 24 | job: 'Job', 25 | department: 'Department', 26 | refresh: 'Refresh', 27 | amount: 'Amount', 28 | total: 'Total', 29 | orderTotal: 'Total order', 30 | openingCities: 'Opening Cities' 31 | }, 32 | header: { 33 | darkMode: 'Dark Mode', 34 | lightMode: 'Light Mode', 35 | email: 'Email', 36 | logout: 'Logout' 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/views/editor/components/ComponentProp.tsx: -------------------------------------------------------------------------------- 1 | import { FC } from 'react' 2 | import { useLowCodeStore } from '@/store/useLowCodeStore' 3 | import { getComponentConfByType, ComponentPropsType } from '@/components/LowCodeComponents' 4 | 5 | const NoProp: FC = () => { 6 | return
未选中组件
7 | } 8 | 9 | const ComponentProp: FC = () => { 10 | const { selectedComponent, changeComponentProps } = useLowCodeStore() 11 | 12 | if (selectedComponent == null) return 13 | 14 | const { type, props, isLocked, isHidden } = selectedComponent 15 | const componentConf = getComponentConfByType(type) 16 | if (componentConf == null) return 17 | 18 | function changeProps(newProps: ComponentPropsType) { 19 | if (selectedComponent == null) return 20 | const { fe_id } = selectedComponent 21 | changeComponentProps({ fe_id, newProps }) 22 | } 23 | 24 | const { PropComponent } = componentConf 25 | 26 | return 27 | } 28 | 29 | export default ComponentProp 30 | -------------------------------------------------------------------------------- /src/views/welcome/index.module.less: -------------------------------------------------------------------------------- 1 | // .welcome { 2 | // display: flex; 3 | // justify-content: center; 4 | // align-items: center; 5 | // background-color: var(--dark-bg-color); 6 | // border-radius: 5px; 7 | // height: calc(100vh - 170px); 8 | // } 9 | // .content { 10 | // position: relative; 11 | // bottom: 40px; 12 | // .subTitle { 13 | // font-size: 30px; 14 | // line-height: 42px; 15 | // color: var(--dark-color); 16 | // } 17 | // .title { 18 | // font-size: 40px; 19 | // line-height: 62px; 20 | // color: #ed6c00; 21 | // } 22 | // .desc { 23 | // text-align: center; 24 | // font-size: 14px; 25 | // color: gray; 26 | // } 27 | // } 28 | // .img { 29 | // background: url('/imgs/welcome-bg.png') no-repeat; 30 | // background-size: contain; 31 | // width: 370px; 32 | // height: 320px; 33 | // margin-left: 100px; 34 | // } 35 | 36 | .home { 37 | display: flex; 38 | align-items: center; 39 | justify-content: center; 40 | background-color: var(--dark-bg-color); 41 | height: 100%; 42 | img { 43 | width: 70%; 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/views/dashboard/index.module.less: -------------------------------------------------------------------------------- 1 | .dashboard { 2 | background-color: var(--dark-bg-color); 3 | padding: 20px; 4 | } 5 | .userInfo { 6 | display: flex; 7 | align-items: center; 8 | } 9 | .userImg { 10 | width: 80px; 11 | height: 80px; 12 | border-radius: 50%; 13 | margin-right: 25px; 14 | } 15 | .report { 16 | display: flex; 17 | } 18 | .card { 19 | flex: 1; 20 | height: 100px; 21 | padding: 10px; 22 | margin-right: 20px; 23 | border-radius: 5px; 24 | color: var(--dark-color); 25 | font-size: 14px; 26 | &:nth-child(1) { 27 | background-color: #f4864f; 28 | } 29 | &:nth-child(2) { 30 | background-color: #887edc; 31 | } 32 | &:nth-child(3) { 33 | background-color: #4f95e5; 34 | } 35 | &:nth-child(4) { 36 | background-color: #6dc3d7; 37 | } 38 | &:last-child { 39 | margin-right: 0; 40 | } 41 | } 42 | .data { 43 | text-align: center; 44 | font-size: 24px; 45 | } 46 | .chart { 47 | margin-top: 50px; 48 | } 49 | .pieChart { 50 | display: flex; 51 | } 52 | .itemPie { 53 | flex: 1; 54 | height: 400px; 55 | } 56 | .itemChart { 57 | height: 400px; 58 | } 59 | -------------------------------------------------------------------------------- /src/components/LowCodeComponents/LowCodeInfo/PropComponent.tsx: -------------------------------------------------------------------------------- 1 | import { FC, useEffect } from 'react' 2 | import { Form, Input } from 'antd' 3 | import { LowCodeInfoPropsType } from './interface' 4 | 5 | const { TextArea } = Input 6 | 7 | const PropComponent: FC = (props: LowCodeInfoPropsType) => { 8 | const { title, desc, onChange, disabled } = props 9 | const [form] = Form.useForm() 10 | 11 | useEffect(() => { 12 | form.setFieldsValue({ title, desc }) 13 | }, [title, desc]) 14 | 15 | function handleValuesChange() { 16 | if (onChange) { 17 | onChange(form.getFieldsValue()) 18 | } 19 | } 20 | 21 | return ( 22 |
29 | 30 | 31 | 32 | 33 |