feat: init object

This commit is contained in:
sunhe 2025-03-28 19:47:45 +08:00
commit 3736305284
53 changed files with 25927 additions and 0 deletions

16
.editorconfig Normal file
View File

@ -0,0 +1,16 @@
# http://editorconfig.org
root = true
[*]
indent_style = space
indent_size = 2
end_of_line = lf
charset = utf-8
trim_trailing_whitespace = true
insert_final_newline = true
[*.md]
trim_trailing_whitespace = false
[Makefile]
indent_style = tab

8
.eslintignore Normal file
View File

@ -0,0 +1,8 @@
/lambda/
/scripts
/config
.history
public
dist
.umi
mock

7
.eslintrc.js Normal file
View File

@ -0,0 +1,7 @@
module.exports = {
extends: [require.resolve('@umijs/lint/dist/config/eslint')],
globals: {
page: true,
REACT_APP_ENV: true,
},
};

41
.gitignore vendored Normal file
View File

@ -0,0 +1,41 @@
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
# dependencies
**/node_modules
# roadhog-api-doc ignore
/src/utils/request-temp.js
_roadhog-api-doc
# production
/dist
# misc
.DS_Store
npm-debug.log*
yarn-error.log
/coverage
.idea
yarn.lock
package-lock.json
*bak
.vscode
# visual studio code
.history
*.log
functions/*
.temp/**
# umi
.umi
.umi-production
.umi-test
# screenshot
screenshot
.firebase
.eslintcache
build

7
.husky/commit-msg Normal file
View File

@ -0,0 +1,7 @@
#!/bin/sh
. "$(dirname "$0")/_/husky.sh"
# Export Git hook params
export GIT_PARAMS=$*
npx --no-install fabric verify-commit

4
.husky/pre-commit Normal file
View File

@ -0,0 +1,4 @@
#!/bin/sh
. "$(dirname "$0")/_/husky.sh"
npx --no-install lint-staged

22
.prettierignore Normal file
View File

@ -0,0 +1,22 @@
**/*.svg
.umi
.umi-production
/dist
.dockerignore
.DS_Store
.eslintignore
*.png
*.toml
docker
.editorconfig
Dockerfile*
.gitignore
.prettierignore
LICENSE
.eslintcache
*.lock
yarn-error.log
.history
CNAME
/build
/public

21
.prettierrc.js Normal file
View File

@ -0,0 +1,21 @@
module.exports = {
singleQuote: true,
trailingComma: 'all',
printWidth: 100,
proseWrap: 'never',
endOfLine: 'lf',
overrides: [
{
files: '.prettierrc',
options: {
parser: 'json',
},
},
{
files: 'document.ejs',
options: {
parser: 'html',
},
},
],
};

57
README.md Normal file
View File

@ -0,0 +1,57 @@
# Ant Design Pro
This project is initialized with [Ant Design Pro](https://pro.ant.design). Follow is the quick guide for how to use.
## Environment Prepare
Install `node_modules`:
```bash
npm install
```
or
```bash
yarn
```
## Provided Scripts
Ant Design Pro provides some useful script to help you quick start and build with web project, code style check and test.
Scripts provided in `package.json`. It's safe to modify or add additional script:
### Start project
```bash
npm start
```
### Build project
```bash
npm run build
```
### Check code style
```bash
npm run lint
```
You can also use script to auto fix some lint error:
```bash
npm run lint:fix
```
### Test code
```bash
npm test
```
## More
You can view full document on our [official website](https://pro.ant.design). And welcome any feedback in our [github](https://github.com/ant-design/ant-design-pro).

149
config/config.ts Normal file
View File

@ -0,0 +1,149 @@
// https://umijs.org/config/
import { defineConfig } from '@umijs/max';
import { join } from 'path';
import defaultSettings from './defaultSettings';
import proxy from './proxy';
import routes from './routes';
const { REACT_APP_ENV = 'dev' } = process.env;
export default defineConfig({
/**
* @name hash
* @description build hash
* @doc https://umijs.org/docs/api/config#hash
*/
hash: true,
/**
* @name
* @description ie11 使
* @doc https://umijs.org/docs/api/config#targets
*/
// targets: {
// ie: 11,
// },
/**
* @name
* @description pathcomponentroutesredirectwrapperstitle
* @doc https://umijs.org/docs/guides/routes
*/
// umi routes: https://umijs.org/docs/routing
routes,
/**
* @name
* @description less
* @doc antd的主题设置 https://ant.design/docs/react/customize-theme-cn
* @doc umi theme https://umijs.org/docs/api/config#theme
*/
theme: {
// 如果不想要 configProvide 动态设置主题需要把这个设置为 default
// 只有设置为 variable 才能使用 configProvide 动态设置主色调
'root-entry-name': 'variable',
},
/**
* @name moment
* @description js的包大小
* @doc https://umijs.org/docs/api/config#ignoremomentlocale
*/
ignoreMomentLocale: true,
/**
* @name
* @description 访
* @see 使build 使
* @doc https://umijs.org/docs/guides/proxy
* @doc https://umijs.org/docs/api/config#proxy
*/
proxy: proxy[REACT_APP_ENV as keyof typeof proxy],
/**
* @name
* @description state
*/
fastRefresh: true,
//============== 以下都是max的插件配置 ===============
/**
* @name
* @@doc https://umijs.org/docs/max/data-flow
*/
model: {},
/**
*
* @description Umi
* @doc https://umijs.org/docs/max/data-flow#%E5%85%A8%E5%B1%80%E5%88%9D%E5%A7%8B%E7%8A%B6%E6%80%81
*/
initialState: {},
/**
* @name layout
* @doc https://umijs.org/docs/max/layout-menu
*/
title: 'Ant Design Pro',
layout: {
locale: true,
...defaultSettings,
},
/**
* @name moment2dayjs
* @description moment dayjs
* @doc https://umijs.org/docs/max/moment2dayjs
*/
moment2dayjs: {
preset: 'antd',
plugins: ['duration'],
},
/**
* @name
* @doc https://umijs.org/docs/max/i18n
*/ /**
* @name antd
* @description babel import
* @doc https://umijs.org/docs/max/antd#antd
*/
antd: {},
/**
* @name
* @description axios ahooks useRequest
* @doc https://umijs.org/docs/max/request
*/
request: {},
/**
* @name
* @description initialState initialState
* @doc https://umijs.org/docs/max/access
*/
access: {},
/**
* @name <head> script
* @description <head> script
*/
headScripts: [
// 解决首次加载时白屏的问题
{
src: '/scripts/loading.js',
async: true,
},
],
//================ pro 插件配置 =================
presets: ['umi-presets-pro'],
/**
* @name openAPI
* @description openapi serve mock
* @doc https://pro.ant.design/zh-cn/docs/openapi/
*/
openAPI: [
{
requestLibPath: "import { request } from '@umijs/max'",
// 或者使用在线的版本
schemaPath: "http://localhost:8101/api/v2/api-docs",
projectName:'hebi',
// schemaPath: join(__dirname, 'oneapi.json'),
mock: false,
},
// {
// requestLibPath: "import { request } from '@umijs/max'",
// schemaPath: 'https://gw.alipayobjects.com/os/antfincdn/CA1dOm%2631B/openapi.json',
// projectName: 'swagger',
// },
],
mfsu: {
strategy: 'normal',
},
esbuildMinifyIIFE: true,
requestRecord: {},
});

28
config/defaultSettings.ts Normal file
View File

@ -0,0 +1,28 @@
import { ProLayoutProps } from '@ant-design/pro-components';
/**
* @name
*/
const Settings: ProLayoutProps & {
pwa?: boolean;
logo?: string;
} = {
navTheme: 'light',
// 拂晓蓝
colorPrimary: '#1890ff',
layout: 'mix',
contentWidth: 'Fluid',
fixedHeader: false,
fixSiderbar: true,
colorWeak: false,
title: '智能数据分析系统',
pwa: true,
logo: ' https://gw.alipayobjects.com/zos/antfincdn/upvrAjAPQX/Logo_Tech%252520UI.svg',
iconfontUrl: '',
token: {
// 参见ts声明demo 见文档通过token 修改样式
//https://procomponents.ant.design/components/layout#%E9%80%9A%E8%BF%87-token-%E4%BF%AE%E6%94%B9%E6%A0%B7%E5%BC%8F
},
};
export default Settings;

44
config/proxy.ts Normal file
View File

@ -0,0 +1,44 @@
/**
* @name
* @see
* -------------------------------
* The agent cannot take effect in the production environment
* so there is no configuration of the production environment
* For details, please see
* https://pro.ant.design/docs/deploy
*
* @doc https://umijs.org/docs/guides/proxy
*/
export default {
// 如果需要自定义本地开发服务器 请取消注释按需调整
// dev: {
// // localhost:8000/api/** -> https://preview.pro.ant.design/api/**
// '/api/': {
// // 要代理的地址
// target: 'https://preview.pro.ant.design',
// // 配置了这个可以从 http 代理到 https
// // 依赖 origin 的功能可能需要这个,比如 cookie
// changeOrigin: true,
// },
// },
/**
* @name
* @doc https://github.com/chimurai/http-proxy-middleware
*/
test: {
// localhost:8000/api/** -> https://preview.pro.ant.design/api/**
'/api/': {
target: 'https://proapi.azurewebsites.net',
changeOrigin: true,
pathRewrite: { '^': '' },
},
},
pre: {
'/api/': {
target: 'your pre url',
changeOrigin: true,
pathRewrite: { '^': '' },
},
},
};

24
config/routes.ts Normal file
View File

@ -0,0 +1,24 @@
export default [
{path: '/user',layout: false,routes: [
{ name: '登录', path: '/user/login', component: './User/Login' },
{ name: '注册', path: '/user/register', component: './User/Register' }
]},
{path:"/", redirect: "/add_chart"},
{ path: '/add_chart', name :"智能分析", icon: "barChart", component: './AddChart' },
{ path: '/add_async', name :"智能分析(异步)", icon: "DotChartOutlined", component: './AddChartAsync' },
{ path: '/my_chart', name :"我的图表", icon: "PictureOutlined", component: './MyChart' },
{ path: '/forum', name :"交流论坛", icon: "CrownOutlined", component: './Forum' },
{
path: '/admin',
name: '管理页',
icon: 'crown',
access: 'canAdmin',
routes: [
{ path: '/admin', redirect: '/admin/sub-page' },
{ path: '/admin/sub-page', name: '二级管理页', component: './Admin' },
],
},
{ path: '/', redirect: '/welcome' },
{ path: '*', layout: false, component: './404' },
];

1
icon.svg Normal file
View File

@ -0,0 +1 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1738593107479" class="icon" viewBox="0 0 1146 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="5383" width="223.828125" height="200" xmlns:xlink="http://www.w3.org/1999/xlink"><path d="M998.4 141.913043c-26.713043-23.930435-55.652174-44.521739-86.817391-60.660869-36.173913-18.921739-73.46087-32.834783-113.530435-41.182609-34.504348-7.234783-69.565217-11.130435-104.626087-9.460869-42.852174 2.226087-84.034783 10.017391-124.66087 24.486956-35.06087 12.8-68.452174 28.93913-100.173913 49.530435-22.817391 15.026087-45.078261 31.165217-64.556521 50.086956-109.078261 105.73913-220.93913 267.686957-218.156522 428.52174-4.452174 38.956522 7.791304 120.765217 13.913043 144.13913 10.573913 42.852174 25.6 84.034783 48.417392 123.547826 5.008696-15.582609 8.904348-30.052174 14.469565-44.521739 6.678261-18.921739 15.582609-36.730435 25.6-53.982609 25.6-41.73913 61.217391-68.452174 111.860869-72.904348 37.286957-3.33913 72.904348 2.226087 109.078261 8.904348 49.530435 9.46087 98.504348 21.704348 148.591305 27.269566 32.834783 3.895652 65.113043 5.565217 98.504347 3.33913 86.26087-5.565217 161.947826-37.286957 229.843479-88.486957 165.843478-125.773913 161.947826-354.504348 12.243478-488.626087z m-260.452174 421.843479c-27.826087 0-50.086957-23.373913-50.086956-51.756522 0-27.826087 22.817391-51.2 50.643478-50.643478 28.382609 0 51.2 22.817391 51.756522 51.2 0 28.93913-22.817391 51.756522-52.313044 51.2z" fill="#046FFF" p-id="5384" data-spm-anchor-id="a313x.search_index.0.i16.4e7f3a81wduNu9"></path><path d="M370.086957 455.234783c-37.286957-21.147826-79.582609-33.391304-125.773914-33.391305-81.808696 0-154.713043 38.956522-200.904347 99.06087 28.93913 22.817391 65.113043 40.626087 106.295652 46.747826 99.06087 15.582609 182.53913-71.791304 220.382609-112.417391z" fill="#045DE5" p-id="5385"></path><path d="M602.156522 885.982609c-30.052174-30.608696-67.895652-53.982609-111.304348-66.226087-78.469565-22.26087-159.165217-5.008696-220.382609 40.069565 21.704348 29.495652 51.756522 56.765217 89.043478 74.017391 91.269565 42.295652 195.33913-18.921739 242.643479-47.860869z" fill="#3B97FF" p-id="5386"></path><path d="M427.965217 1001.182609c-23.373913-1.669565-55.095652-6.678261-80.695652-21.704348-45.634783-27.269565-67.33913-52.313043-76.8-120.208696 23.373913-2.782609 49.530435-1.113043 74.573913 9.46087 60.104348 24.486957 75.686957 97.391304 82.921739 132.452174z" fill="#045CE5" p-id="5387"></path></svg>

After

Width:  |  Height:  |  Size: 2.5 KiB

11
jsconfig.json Normal file
View File

@ -0,0 +1,11 @@
{
"compilerOptions": {
"jsx": "react-jsx",
"emitDecoratorMetadata": true,
"experimentalDecorators": true,
"baseUrl": ".",
"paths": {
"@/*": ["./src/*"]
}
}
}

104
package.json Normal file
View File

@ -0,0 +1,104 @@
{
"name": "ant-design-pro",
"version": "6.0.0",
"private": true,
"description": "An out-of-box UI solution for enterprise applications",
"scripts": {
"analyze": "cross-env ANALYZE=1 max build",
"build": "max build",
"deploy": "npm run build && npm run gh-pages",
"dev": "npm run start:dev",
"gh-pages": "gh-pages -d dist",
"i18n-remove": "pro i18n-remove --locale=zh-CN --write",
"postinstall": "max setup",
"jest": "jest",
"lint": "npm run lint:js && npm run lint:prettier && npm run tsc",
"lint-staged": "lint-staged",
"lint-staged:js": "eslint --ext .js,.jsx,.ts,.tsx ",
"lint:fix": "eslint --fix --cache --ext .js,.jsx,.ts,.tsx --format=pretty ./src ",
"lint:js": "eslint --cache --ext .js,.jsx,.ts,.tsx --format=pretty ./src",
"lint:prettier": "prettier -c --write \"**/**.{js,jsx,tsx,ts,less,md,json}\" --end-of-line auto",
"openapi": "max openapi",
"prepare": "husky install",
"prettier": "prettier -c --write \"**/**.{js,jsx,tsx,ts,less,md,json}\"",
"preview": "npm run build && max preview --port 8000",
"record": "cross-env NODE_ENV=development REACT_APP_ENV=test max record --scene=login",
"serve": "umi-serve",
"start": "cross-env UMI_ENV=dev max dev",
"start:dev": "cross-env REACT_APP_ENV=dev MOCK=none UMI_ENV=dev max dev",
"start:no-mock": "cross-env MOCK=none UMI_ENV=dev max dev",
"start:pre": "cross-env REACT_APP_ENV=pre UMI_ENV=dev max dev",
"start:test": "cross-env REACT_APP_ENV=test MOCK=none UMI_ENV=dev max dev",
"test": "jest",
"test:coverage": "npm run jest -- --coverage",
"test:update": "npm run jest -- -u",
"tsc": "tsc --noEmit"
},
"lint-staged": {
"**/*.{js,jsx,ts,tsx}": "npm run lint-staged:js",
"**/*.{js,jsx,tsx,ts,less,md,json}": [
"prettier --write"
]
},
"browserslist": [
"> 1%",
"last 2 versions",
"not ie <= 10"
],
"dependencies": {
"@ant-design/icons": "^4.8.1",
"@ant-design/pro-components": "^2.6.48",
"@umijs/route-utils": "^2.2.2",
"antd": "5.24.5",
"antd-style": "^3.6.1",
"classnames": "^2.5.1",
"echarts": "^5.6.0",
"echarts-for-react": "^3.0.2",
"lodash": "^4.17.21",
"moment": "^2.30.1",
"omit.js": "^2.0.2",
"querystring": "^0.2.1",
"rc-menu": "^9.12.4",
"rc-util": "^5.38.1",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-echarts": "^0.1.1",
"react-helmet-async": "^1.3.0",
"react-intl": "^7.1.6"
},
"devDependencies": {
"@ant-design/pro-cli": "^3.3.0",
"@testing-library/react": "^13.4.0",
"@types/antd": "^1.0.4",
"@types/classnames": "^2.3.1",
"@types/express": "^4.17.21",
"@types/history": "^4.7.11",
"@types/jest": "^29.5.11",
"@types/lodash": "^4.14.202",
"@types/react": "^18.2.48",
"@types/react-dom": "^18.2.18",
"@types/react-helmet": "^6.1.11",
"@umijs/fabric": "^2.14.1",
"@umijs/lint": "^4.1.1",
"@umijs/max": "^4.1.1",
"cross-env": "^7.0.3",
"eslint": "^8.56.0",
"express": "^4.18.2",
"gh-pages": "^3.2.3",
"husky": "^7.0.4",
"jest": "^29.7.0",
"jest-environment-jsdom": "^29.7.0",
"lint-staged": "^10.5.4",
"mockjs": "^1.1.0",
"prettier": "^2.8.8",
"react-dev-inspector": "^1.9.0",
"swagger-ui-dist": "^4.19.1",
"ts-node": "^10.9.2",
"typescript": "^5.3.3",
"umi-presets-pro": "^2.0.3",
"umi-serve": "^1.9.11"
},
"engines": {
"node": ">=12.0.0"
}
}

21929
pnpm-lock.yaml generated Normal file

File diff suppressed because it is too large Load Diff

1
public/CNAME Normal file
View File

@ -0,0 +1 @@
preview.pro.ant.design

BIN
public/favicon.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.1 KiB

1
public/logo.svg Normal file
View File

@ -0,0 +1 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1738588519475" class="icon" viewBox="0 0 1033 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="12751" xmlns:xlink="http://www.w3.org/1999/xlink" width="201.7578125" height="200"><path d="M0 197.797058m108.406521-28.832589l526.886667-140.134623q108.406521-28.832589 137.239111 79.573932l140.134622 526.886667q28.832589 108.406521-79.573932 137.239111l-526.886667 140.134622q-108.406521 28.832589-137.239111-79.573932l-140.134622-526.886667q-28.832589-108.406521 79.573932-137.23911Z" fill="#0F62FE" p-id="12752"></path><path d="M262.810642 253.195618m160.250391 0l449.053646 0q160.250391 0 160.250391 160.250391l0 449.053647q0 160.250391-160.250391 160.250391l-449.053646 0q-160.250391 0-160.250391-160.250391l0-449.053647q0-160.250391 160.250391-160.250391Z" fill="#C1D0FF" fill-opacity=".4" p-id="12753"></path><path d="M463.123631 611.981822c0-17.701258 14.34882-32.050078 32.050078-32.050079h8.012519c17.701258 0 32.050078 14.34882 32.050079 32.050079v224.355355h-72.112676V611.981822zM612.694936 469.533646c0-17.701258 14.34882-32.050078 32.050078-32.050078h8.01252c17.701258 0 32.050078 14.34882 32.050078 32.050078v366.805133h-72.112676V469.533646zM762.266241 560.344338c0-17.701258 14.34882-32.050078 32.050078-32.050078h8.01252c17.701258 0 32.050078 14.34882 32.050078 32.050078v275.994441h-72.112676V560.344338z" fill="#FFFFFF" p-id="12754"></path></svg>

After

Width:  |  Height:  |  Size: 1.5 KiB

202
public/scripts/loading.js Normal file
View File

@ -0,0 +1,202 @@
/**
* loading 占位
* 解决首次加载时白屏的问题
*/
(function () {
const _root = document.querySelector('#root');
if (_root && _root.innerHTML === '') {
_root.innerHTML = `
<style>
html,
body,
#root {
height: 100%;
margin: 0;
padding: 0;
}
#root {
background-repeat: no-repeat;
background-size: 100% auto;
}
.loading-title {
font-size: 1.1rem;
}
.loading-sub-title {
margin-top: 20px;
font-size: 1rem;
color: #888;
}
.page-loading-warp {
display: flex;
align-items: center;
justify-content: center;
padding: 26px;
}
.ant-spin {
position: absolute;
display: none;
-webkit-box-sizing: border-box;
box-sizing: border-box;
margin: 0;
padding: 0;
color: rgba(0, 0, 0, 0.65);
color: #1890ff;
font-size: 14px;
font-variant: tabular-nums;
line-height: 1.5;
text-align: center;
list-style: none;
opacity: 0;
-webkit-transition: -webkit-transform 0.3s
cubic-bezier(0.78, 0.14, 0.15, 0.86);
transition: -webkit-transform 0.3s
cubic-bezier(0.78, 0.14, 0.15, 0.86);
transition: transform 0.3s cubic-bezier(0.78, 0.14, 0.15, 0.86);
transition: transform 0.3s cubic-bezier(0.78, 0.14, 0.15, 0.86),
-webkit-transform 0.3s cubic-bezier(0.78, 0.14, 0.15, 0.86);
-webkit-font-feature-settings: "tnum";
font-feature-settings: "tnum";
}
.ant-spin-spinning {
position: static;
display: inline-block;
opacity: 1;
}
.ant-spin-dot {
position: relative;
display: inline-block;
width: 20px;
height: 20px;
font-size: 20px;
}
.ant-spin-dot-item {
position: absolute;
display: block;
width: 9px;
height: 9px;
background-color: #1890ff;
border-radius: 100%;
-webkit-transform: scale(0.75);
-ms-transform: scale(0.75);
transform: scale(0.75);
-webkit-transform-origin: 50% 50%;
-ms-transform-origin: 50% 50%;
transform-origin: 50% 50%;
opacity: 0.3;
-webkit-animation: antspinmove 1s infinite linear alternate;
animation: antSpinMove 1s infinite linear alternate;
}
.ant-spin-dot-item:nth-child(1) {
top: 0;
left: 0;
}
.ant-spin-dot-item:nth-child(2) {
top: 0;
right: 0;
-webkit-animation-delay: 0.4s;
animation-delay: 0.4s;
}
.ant-spin-dot-item:nth-child(3) {
right: 0;
bottom: 0;
-webkit-animation-delay: 0.8s;
animation-delay: 0.8s;
}
.ant-spin-dot-item:nth-child(4) {
bottom: 0;
left: 0;
-webkit-animation-delay: 1.2s;
animation-delay: 1.2s;
}
.ant-spin-dot-spin {
-webkit-transform: rotate(45deg);
-ms-transform: rotate(45deg);
transform: rotate(45deg);
-webkit-animation: antrotate 1.2s infinite linear;
animation: antRotate 1.2s infinite linear;
}
.ant-spin-lg .ant-spin-dot {
width: 32px;
height: 32px;
font-size: 32px;
}
.ant-spin-lg .ant-spin-dot i {
width: 14px;
height: 14px;
}
@media all and (-ms-high-contrast: none), (-ms-high-contrast: active) {
.ant-spin-blur {
background: #fff;
opacity: 0.5;
}
}
@-webkit-keyframes antSpinMove {
to {
opacity: 1;
}
}
@keyframes antSpinMove {
to {
opacity: 1;
}
}
@-webkit-keyframes antRotate {
to {
-webkit-transform: rotate(405deg);
transform: rotate(405deg);
}
}
@keyframes antRotate {
to {
-webkit-transform: rotate(405deg);
transform: rotate(405deg);
}
}
</style>
<div style="
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
height: 100%;
min-height: 362px;
">
<div class="page-loading-warp">
<div class="ant-spin ant-spin-lg ant-spin-spinning">
<span class="ant-spin-dot ant-spin-dot-spin">
<i class="ant-spin-dot-item"></i>
<i class="ant-spin-dot-item"></i>
<i class="ant-spin-dot-item"></i>
<i class="ant-spin-dot-item"></i>
</span>
</div>
</div>
<div class="loading-title">
正在加载资源
</div>
<div class="loading-sub-title">
初次加载资源可能需要较多时间 请耐心等待
</div>
</div>
`;
}
})();

11
src/access.ts Normal file
View File

@ -0,0 +1,11 @@
//控制页面访问权限
/**
* @see https://umijs.org/docs/max/access#access
* */
export default function access(initialState: { currentUser?: API.CurrentUser } | undefined) {
const { currentUser } = initialState ?? {};
return {
canAdmin: currentUser && currentUser.access === 'admin',
};
}

116
src/app.tsx Normal file
View File

@ -0,0 +1,116 @@
import { AvatarDropdown, AvatarName, Footer, Question } from '@/components';
import { SettingDrawer } from '@ant-design/pro-components';
import type { RunTimeLayoutConfig } from '@umijs/max';
import { history} from '@umijs/max';
import { errorConfig } from './requestErrorConfig';
import { getLoginUserUsingGet } from './services/hebi/userController';
const isDev = process.env.NODE_ENV === 'development';
const loginPath = '/user/login';
/**
* @see https://umijs.org/zh-CN/plugins/plugin-initial-state
* */
export async function getInitialState(): Promise<{
currentUser?: API.LoginUserVO;
}> {
const fetchUserInfo = async () => {
try {
const res = await getLoginUserUsingGet();
return res.data;
} catch (error) {
history.push(loginPath);
}
return undefined;
};
// 如果不是登录页面,执行
const { location } = history;
if (location.pathname !== loginPath) {
const currentUser = await fetchUserInfo();
return {
currentUser,
};
}
return {
};
}
export const layout: RunTimeLayoutConfig = ({ initialState, setInitialState }) => {
return {
actionsRender: () => [<Question key="doc" />],
avatarProps: {
src: initialState?.currentUser?.userAvatar,
title: <AvatarName />,
render: (_, avatarChildren) => {
return <AvatarDropdown>{avatarChildren}</AvatarDropdown>;
},
},
waterMarkProps: {
content: initialState?.currentUser?.name,
},
footerRender: () => <Footer />,
onPageChange: () => {
const { location } = history;
// 如果没有登录,重定向到 login
if (!initialState?.currentUser && location.pathname !== loginPath) {
history.push(loginPath);
}
},
bgLayoutImgList: [
{
src: 'https://mdn.alipayobjects.com/yuyan_qk0oxh/afts/img/D2LWSqNny4sAAAAAAAAAAAAAFl94AQBr',
left: 85,
bottom: 100,
height: '303px',
},
{
src: 'https://mdn.alipayobjects.com/yuyan_qk0oxh/afts/img/C2TWRpJpiC0AAAAAAAAAAAAAFl94AQBr',
bottom: -68,
right: -45,
height: '303px',
},
{
src: 'https://mdn.alipayobjects.com/yuyan_qk0oxh/afts/img/F6vSTbj8KpYAAAAAAAAAAAAAFl94AQBr',
bottom: 0,
left: 0,
width: '331px',
},
],
menuHeaderRender: undefined,
childrenRender: (children) => {
// if (initialState?.loading) return <PageLoading />;
return (
<>
{children}
{isDev && (
<SettingDrawer
disableUrlParams
enableDarkTheme
settings={initialState?.settings}
onSettingChange={(settings) => {
setInitialState((preInitialState) => ({
...preInitialState,
settings,
}));
}}
/>
)}
</>
);
},
...initialState?.settings,
};
};
/**
* @name request
* axios ahooks useRequest
* @doc https://umijs.org/docs/max/request#配置
*/
export const request = {
baseURL:"http://localhost:8101",
withCredentials:true,
...errorConfig,
};

View File

@ -0,0 +1,35 @@
import { GithubOutlined } from '@ant-design/icons';
import { DefaultFooter } from '@ant-design/pro-components';
import React from 'react';
const Footer: React.FC = () => {
return (
<DefaultFooter
style={{
background: 'none',
}}
links={[
{
key: 'Ant Design Pro',
title: 'Ant Design Pro',
href: 'https://pro.ant.design',
blankTarget: true,
},
{
key: 'github',
title: <GithubOutlined />,
href: 'https://github.com/ant-design/ant-design-pro',
blankTarget: true,
},
{
key: 'Ant Design',
title: 'Ant Design',
href: 'https://ant.design',
blankTarget: true,
},
]}
/>
);
};
export default Footer;

View File

@ -0,0 +1,27 @@
import { Dropdown } from 'antd';
import type { DropDownProps } from 'antd/es/dropdown';
import React from 'react';
import { createStyles } from 'antd-style';
import classNames from 'classnames';
const useStyles = createStyles(({ token }) => {
return {
dropdown: {
[`@media screen and (max-width: ${token.screenXS}px)`]: {
width: '100%',
},
},
};
});
export type HeaderDropdownProps = {
overlayClassName?: string;
placement?: 'bottomLeft' | 'bottomRight' | 'topLeft' | 'topCenter' | 'topRight' | 'bottomCenter';
} & Omit<DropDownProps, 'overlay'>;
const HeaderDropdown: React.FC<HeaderDropdownProps> = ({ overlayClassName: cls, ...restProps }) => {
const { styles } = useStyles();
return <Dropdown overlayClassName={classNames(styles.dropdown, cls)} {...restProps} />;
};
export default HeaderDropdown;

View File

@ -0,0 +1,138 @@
import { LogoutOutlined, SettingOutlined, UserOutlined } from '@ant-design/icons';
import { history, useModel } from '@umijs/max';
import { Spin } from 'antd';
import { createStyles } from 'antd-style';
import { stringify } from 'querystring';
import type { MenuInfo } from 'rc-menu/lib/interface';
import React, { useCallback } from 'react';
import { flushSync } from 'react-dom';
import HeaderDropdown from '../HeaderDropdown';
import { userLogoutUsingPost } from '@/services/hebi/userController';
export type GlobalHeaderRightProps = {
menu?: boolean;
children?: React.ReactNode;
};
export const AvatarName = () => {
const { initialState } = useModel('@@initialState');
const { currentUser } = initialState || {};
return <span className="anticon">{currentUser?.userName}</span>;
};
const useStyles = createStyles(({ token }) => {
return {
action: {
display: 'flex',
height: '48px',
marginLeft: 'auto',
overflow: 'hidden',
alignItems: 'center',
padding: '0 8px',
cursor: 'pointer',
borderRadius: token.borderRadius,
'&:hover': {
backgroundColor: token.colorBgTextHover,
},
},
};
});
export const AvatarDropdown: React.FC<GlobalHeaderRightProps> = ({ menu, children }) => {
/**
* 退 url
*/
const loginOut = async () => {
await userLogoutUsingPost();
const { search, pathname } = window.location;
const urlParams = new URL(window.location.href).searchParams;
/** 此方法会跳转到 redirect 参数所在的位置 */
const redirect = urlParams.get('redirect');
// Note: There may be security issues, please note
if (window.location.pathname !== '/user/login' && !redirect) {
history.replace({
pathname: '/user/login',
search: stringify({
redirect: pathname + search,
}),
});
}
};
const { styles } = useStyles();
const { initialState, setInitialState } = useModel('@@initialState');
const onMenuClick = useCallback(
(event: MenuInfo) => {
const { key } = event;
if (key === 'logout') {
flushSync(() => {
setInitialState((s) => ({ ...s, currentUser: undefined }));
});
loginOut();
return;
}
history.push(`/account/${key}`);
},
[setInitialState],
);
const loading = (
<span className={styles.action}>
<Spin
size="small"
style={{
marginLeft: 8,
marginRight: 8,
}}
/>
</span>
);
if (!initialState) {
return loading;
}
const { currentUser } = initialState;
if (!currentUser || !currentUser.userName) {
return loading;
}
const menuItems = [
...(menu
? [
{
key: 'center',
icon: <UserOutlined />,
label: '个人中心',
},
{
key: 'settings',
icon: <SettingOutlined />,
label: '个人设置',
},
{
type: 'divider' as const,
},
]
: []),
{
key: 'logout',
icon: <LogoutOutlined />,
label: '退出登录',
},
];
return (
<HeaderDropdown
menu={{
selectedKeys: [],
onClick: onMenuClick,
items: menuItems,
}}
>
{children}
</HeaderDropdown>
);
};

View File

@ -0,0 +1,27 @@
import { QuestionCircleOutlined } from '@ant-design/icons';
import '@umijs/max';
export type SiderTheme = 'light' | 'dark';
export const SelectLang = () => {
return (
<UmiSelectLang
style={{
padding: 4,
}}
/>
);
};
export const Question = () => {
return (
<div
style={{
display: 'flex',
height: 26,
}}
onClick={() => {
window.open('https://pro.ant.design/docs/getting-started');
}}
>
<QuestionCircleOutlined />
</div>
);
};

11
src/components/index.ts Normal file
View File

@ -0,0 +1,11 @@
/**
*
* 便
*/
/**
*
*/
import Footer from './Footer';
import { Question } from './RightContent';
import { AvatarDropdown, AvatarName } from './RightContent/AvatarDropdown';
export { AvatarDropdown, AvatarName, Footer, Question};

50
src/global.css Normal file
View File

@ -0,0 +1,50 @@
html,
body,
#root {
height: 100%;
margin: 0;
padding: 0;
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, 'Noto Sans', sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol', 'Noto Color Emoji';
}
.colorWeak {
filter: invert(80%);
}
.ant-layout {
min-height: 100vh;
}
.ant-pro-sider.ant-layout-sider.ant-pro-sider-fixed {
left: unset;
}
canvas {
display: block;
}
body {
text-rendering: optimizeLegibility;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
ul,
ol {
list-style: none;
}
@media (max-width: 768px) {
.ant-table {
width: 100%;
overflow-x: auto;
}
.ant-table-thead > tr > th,
.ant-table-tbody > tr > th,
.ant-table-thead > tr > td,
.ant-table-tbody > tr > td {
white-space: pre;
}
.ant-table-thead > tr > th > span,
.ant-table-tbody > tr > th > span,
.ant-table-thead > tr > td > span,
.ant-table-tbody > tr > td > span {
display: block;
}
}
.margin-16 {
margin-bottom: 18px;
}

57
src/global.less Normal file
View File

@ -0,0 +1,57 @@
html,
body,
#root {
height: 100%;
margin: 0;
padding: 0;
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial,
'Noto Sans', sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol',
'Noto Color Emoji';
}
.colorWeak {
filter: invert(80%);
}
.ant-layout {
min-height: 100vh;
}
.ant-pro-sider.ant-layout-sider.ant-pro-sider-fixed {
left: unset;
}
canvas {
display: block;
}
body {
text-rendering: optimizeLegibility;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
ul,
ol {
list-style: none;
}
@media (max-width: 768px) {
.ant-table {
width: 100%;
overflow-x: auto;
&-thead > tr,
&-tbody > tr {
> th,
> td {
white-space: pre;
> span {
display: block;
}
}
}
}
}
.margin-16{
margin-bottom: 18px;
}

92
src/global.tsx Normal file
View File

@ -0,0 +1,92 @@
import '@umijs/max';
import { Button, message, notification } from 'antd';
import defaultSettings from '../config/defaultSettings';
const { pwa } = defaultSettings;
const isHttps = document.location.protocol === 'https:';
const clearCache = () => {
// remove all caches
if (window.caches) {
caches
.keys()
.then((keys) => {
keys.forEach((key) => {
caches.delete(key);
});
})
.catch((e) => console.log(e));
}
};
// if pwa is true
if (pwa) {
// Notify user if offline now
window.addEventListener('sw.offline', () => {
message.warning('当前处于离线状态');
});
// Pop up a prompt on the page asking the user if they want to use the latest version
window.addEventListener('sw.updated', (event: Event) => {
const e = event as CustomEvent;
const reloadSW = async () => {
// Check if there is sw whose state is waiting in ServiceWorkerRegistration
// https://developer.mozilla.org/en-US/docs/Web/API/ServiceWorkerRegistration
const worker = e.detail && e.detail.waiting;
if (!worker) {
return true;
}
// Send skip-waiting event to waiting SW with MessageChannel
await new Promise((resolve, reject) => {
const channel = new MessageChannel();
channel.port1.onmessage = (msgEvent) => {
if (msgEvent.data.error) {
reject(msgEvent.data.error);
} else {
resolve(msgEvent.data);
}
};
worker.postMessage(
{
type: 'skip-waiting',
},
[channel.port2],
);
});
clearCache();
window.location.reload();
return true;
};
const key = `open${Date.now()}`;
const btn = (
<Button
type="primary"
onClick={() => {
notification.destroy(key);
reloadSW();
}}
>
{'刷新'}
</Button>
);
notification.open({
message: '有新内容',
description: '请点击“刷新”按钮或者手动刷新页面',
btn,
key,
onClose: async () => null,
});
});
} else if ('serviceWorker' in navigator && isHttps) {
// unregister service worker
const { serviceWorker } = navigator;
if (serviceWorker.getRegistrations) {
serviceWorker.getRegistrations().then((sws) => {
sws.forEach((sw) => {
sw.unregister();
});
});
}
serviceWorker.getRegistration().then((sw) => {
if (sw) sw.unregister();
});
clearCache();
}

16
src/pages/404.tsx Normal file
View File

@ -0,0 +1,16 @@
import { history } from '@umijs/max';
import { Button, Result } from 'antd';
import React from 'react';
const NoFoundPage: React.FC = () => (
<Result
status="404"
title="404"
subTitle={'抱歉,您访问的页面不存在。'}
extra={
<Button type="primary" onClick={() => history.push('/')}>
{'返回首页'}
</Button>
}
/>
);
export default NoFoundPage;

View File

@ -0,0 +1,126 @@
import { genChartByAiUsingPost } from '@/services/hebi/chartController';
import { UploadOutlined } from '@ant-design/icons';
import {Button,Card,Col,Divider,Form,Input,message,Row,Select,Space,Spin,Upload,} from 'antd';
import TextArea from 'antd/es/input/TextArea';
import ReactECharts from 'echarts-for-react';
import React, { useState } from 'react';
/**
*
* @constructor
*/
const AddChart: React.FC = () => {
const [chart, setChart] = useState<API.BiResponse>();
const [option, setOption] = useState<any>();
const [submitting, setSubmitting] = useState<boolean>(false);
/**
*
* @param values
*/
const onFinish = async (values: any) => {
// 避免重复提交
if (submitting) {
return;
}
setSubmitting(true);
setChart(undefined);
setOption(undefined);
// 对接后端,上传数据
const params = {
...values,
file: undefined,
};
try {
const res = await genChartByAiUsingPost(params, {}, values.file.file.originFileObj);
if (!res?.data) {
message.error('分析失败');
} else {
message.success('分析成功');
const chartOption = JSON.parse(res.data.genChart ?? '');
if (!chartOption) {
throw new Error('图表代码解析错误');
} else {
setChart(res.data);
setOption(chartOption);
}
}
} catch (e: any) {
message.error('分析失败,' + e.message);
}
setSubmitting(false);
};
return (
<div className="add-chart">
<Row gutter={24}>
<Col span={12}>
<Card title="智能分析">
<Form
name="addChart"
labelAlign="left"
labelCol={{ span: 4 }}
wrapperCol={{ span: 16 }}
onFinish={onFinish}
initialValues={{}}
>
<Form.Item
name="goal"
label="分析目标"
rules={[{ required: true, message: '请输入分析目标' }]}
>
<TextArea placeholder="请输入你的分析需求,比如:分析网站用户的增长情况" />
</Form.Item>
<Form.Item name="name" label="图表名称">
<Input placeholder="请输入图表名称" />
</Form.Item>
<Form.Item name="chartType" label="图表类型">
<Select
options={[
{ value: '折线图', label: '折线图' },
{ value: '柱状图', label: '柱状图' },
{ value: '堆叠图', label: '堆叠图' },
{ value: '散点图', label: '散点图' },
{ value: '饼图', label: '饼图' },
]}
/>
</Form.Item>
<Form.Item name="file" label="原始数据">
{/* 上传文件限制数量 */}
<Upload name="file" maxCount={1}>
<Button icon={<UploadOutlined />}> excel </Button>
</Upload>
</Form.Item>
<Form.Item wrapperCol={{ span: 16, offset: 4 }}>
<Space>
<Button
type="primary"
htmlType="submit"
loading={submitting}
disabled={submitting}
>
</Button>
<Button htmlType="reset"></Button>
</Space>
</Form.Item>
</Form>
</Card>
</Col>
<Col span={12}>
<Card title="可视化图表">
{option ? <ReactECharts option={option} /> : <div></div>}
<Spin spinning={submitting} />
</Card>
<Divider />
<Card title="分析结论">
{chart?.genResult ?? <div></div>}
<Spin spinning={submitting} />
</Card>
</Col>
</Row>
</div>
);
};
export default AddChart;

View File

@ -0,0 +1,98 @@
import { genChartByAiAsyncUsingPost } from '@/services/hebi/chartController';
import { UploadOutlined } from '@ant-design/icons';
import { Button, Card, Form, Input, message, Select, Space, Upload } from 'antd';
import { useForm } from 'antd/es/form/Form';
import TextArea from 'antd/es/input/TextArea';
import React, { useState } from 'react';
/**
*
* @constructor
*/
const AddChartAsync: React.FC = () => {
const [form] = useForm();
const [submitting, setSubmitting] = useState<boolean>(false);
/**
*
* @param values
*/
const onFinish = async (values: any) => {
// 避免重复提交
if (submitting) {
return;
}
setSubmitting(true);
// 对接后端,上传数据
const params = {
...values,
file: undefined,
};
try {
const res = await genChartByAiAsyncUsingPost(params, {}, values.file.file.originFileObj);
// const res = await genChartByAiAsyncMqUsingPOST(params, {}, values.file.file.originFileObj);
if (!res?.data) {
message.error('分析失败');
} else {
message.success('分析任务提交成功,稍后请在我的图表页面查看');
form.resetFields();
}
} catch (e: any) {
message.error('分析失败,' + e.message);
}
setSubmitting(false);
};
return (
<div className="add-chart-async">
<Card title="智能分析">
<Form
form={form}
name="addChart"
labelAlign="left"
labelCol={{ span: 4 }}
wrapperCol={{ span: 16 }}
onFinish={onFinish}
initialValues={{}}
>
<Form.Item
name="goal"
label="分析目标"
rules={[{ required: true, message: '请输入分析目标' }]}
>
<TextArea placeholder="请输入你的分析需求,比如:分析网站用户的增长情况" />
</Form.Item>
<Form.Item name="name" label="图表名称">
<Input placeholder="请输入图表名称" />
</Form.Item>
<Form.Item name="chartType" label="图表类型">
<Select
options={[
{ value: '折线图', label: '折线图' },
{ value: '柱状图', label: '柱状图' },
{ value: '堆叠图', label: '堆叠图' },
{ value: '饼图', label: '饼图' },
{ value: '雷达图', label: '雷达图' },
]}
/>
</Form.Item>
<Form.Item name="file" label="原始数据">
<Upload name="file" maxCount={1}>
<Button icon={<UploadOutlined />}> CSV </Button>
</Upload>
</Form.Item>
<Form.Item wrapperCol={{ span: 16, offset: 4 }}>
<Space>
<Button type="primary" htmlType="submit" loading={submitting} disabled={submitting}>
</Button>
<Button htmlType="reset"></Button>
</Space>
</Form.Item>
</Form>
</Card>
</div>
);
};
export default AddChartAsync;

44
src/pages/Admin.tsx Normal file
View File

@ -0,0 +1,44 @@
import { HeartTwoTone, SmileTwoTone } from '@ant-design/icons';
import { PageContainer } from '@ant-design/pro-components';
import '@umijs/max';
import { Alert, Card, Typography } from 'antd';
import React from 'react';
const Admin: React.FC = () => {
return (
<PageContainer content={' 这个页面只有 admin 权限才能查看'}>
<Card>
<Alert
message={'更快更强的重型组件,已经发布。'}
type="success"
showIcon
banner
style={{
margin: -12,
marginBottom: 48,
}}
/>
<Typography.Title
level={2}
style={{
textAlign: 'center',
}}
>
<SmileTwoTone /> AIGC的智能数据分析系统 <HeartTwoTone twoToneColor="#eb2f96" />
</Typography.Title>
</Card>
<p
style={{
textAlign: 'center',
marginTop: 24,
}}
>
Want to add more pages? Please refer to{' '}
<a href="https://pro.ant.design/docs/block-cn" target="_blank" rel="noopener noreferrer">
use block
</a>
</p>
</PageContainer>
);
};
export default Admin;

405
src/pages/Forum/index.tsx Normal file
View File

@ -0,0 +1,405 @@
import {LikeFilled,LikeOutlined,MessageOutlined,PlusOutlined,SendOutlined,} from '@ant-design/icons';
import type { UploadFile, UploadProps } from 'antd';
import {Avatar,Button,Card,Comment,Divider,Form,Image,Input,List,message,Space,Tooltip,Upload} from 'antd';
import moment from 'moment';
import React, { useState } from 'react';
const { TextArea } = Input;
type CommentType = {
id: string;
author: string;
avatar: string;
content: string;
datetime: string;
likes: number;
liked: boolean;
};
type PostType = {
id: string;
author: string;
avatar: string;
content: string;
images: string[];
datetime: string;
likes: number;
liked: boolean;
comments: CommentType[];
commentVisible: boolean;
};
const Forum: React.FC = () => {
// 当前用户信息
const currentUser = {
name: '当前用户',
avatar: 'https://joeschmoe.io/api/v1/random',
};
// 帖子列表数据
const [posts, setPosts] = useState<PostType[]>([
{
id: '1',
author: '用户1',
avatar: 'https://joeschmoe.io/api/v1/1',
content: '这是一个示例帖子内容,欢迎大家讨论!',
images: ['https://zos.alipayobjects.com/rmsportal/jkjgkEfvpUPVyRjUImniVslZfWPnJuuZ.png'],
datetime: '2023-05-01 10:00:00',
likes: 5,
liked: false,
comments: [
{
id: '1-1',
author: '用户2',
avatar: 'https://joeschmoe.io/api/v1/2',
content: '这个帖子很有意义!',
datetime: '2023-05-01 10:30:00',
likes: 2,
liked: false,
},
],
commentVisible: false,
},
{
id: '2',
author: '用户3',
avatar: 'https://joeschmoe.io/api/v1/3',
content: '分享一张有趣的图片',
images: [
'https://zos.alipayobjects.com/rmsportal/jkjgkEfvpUPVyRjUImniVslZfWPnJuuZ.png',
'https://gw.alipayobjects.com/zos/rmsportal/KDpgvguMpGfqaHPjicRK.svg',
],
datetime: '2023-05-02 15:00:00',
likes: 10,
liked: true,
comments: [],
commentVisible: false,
},
]);
// 新帖子内容
const [postContent, setPostContent] = useState('');
const [fileList, setFileList] = useState<UploadFile[]>([]);
const [previewOpen, setPreviewOpen] = useState(false);
const [previewImage, setPreviewImage] = useState('');
// 处理图片预览
const handlePreview = async (file: UploadFile) => {
if (!file.url && !file.preview) {
file.preview = URL.createObjectURL(file.originFileObj as any);
}
setPreviewImage(file.url || (file.preview as string));
setPreviewOpen(true);
};
// 处理图片变化
const handleChange: UploadProps['onChange'] = ({ fileList: newFileList }) => {
setFileList(newFileList);
};
// 上传按钮
const uploadButton = (
<div>
<PlusOutlined />
<div style={{ marginTop: 8 }}></div>
</div>
);
// 发布新帖子
const handlePostSubmit = () => {
if (!postContent.trim() && fileList.length === 0) {
message.warning('请填写内容或上传图片');
return;
}
const newPost: PostType = {
id: Date.now().toString(),
author: currentUser.name,
avatar: currentUser.avatar,
content: postContent,
images: fileList.map((file) => file.url || (file.preview as string)),
datetime: moment().format('YYYY-MM-DD HH:mm:ss'),
likes: 0,
liked: false,
comments: [],
commentVisible: false,
};
setPosts([newPost, ...posts]);
setPostContent('');
setFileList([]);
message.success('帖子发布成功');
};
// 点赞帖子
const handleLikePost = (postId: string) => {
setPosts(
posts.map((post) => {
if (post.id === postId) {
return {
...post,
likes: post.liked ? post.likes - 1 : post.likes + 1,
liked: !post.liked,
};
}
return post;
}),
);
};
// 切换评论可见性
const toggleCommentVisible = (postId: string) => {
setPosts(
posts.map((post) => {
if (post.id === postId) {
return {
...post,
commentVisible: !post.commentVisible,
};
}
return post;
}),
);
};
// 添加评论
const handleAddComment = (postId: string, content: string) => {
if (!content.trim()) {
message.warning('评论内容不能为空');
return;
}
const newComment: CommentType = {
id: `${postId}-${Date.now()}`,
author: currentUser.name,
avatar: currentUser.avatar,
content: content,
datetime: moment().format('YYYY-MM-DD HH:mm:ss'),
likes: 0,
liked: false,
};
setPosts(
posts.map((post) => {
if (post.id === postId) {
return {
...post,
comments: [...post.comments, newComment],
};
}
return post;
}),
);
};
// 点赞评论
const handleLikeComment = (postId: string, commentId: string) => {
setPosts(
posts.map((post) => {
if (post.id === postId) {
return {
...post,
comments: post.comments.map((comment) => {
if (comment.id === commentId) {
return {
...comment,
likes: comment.liked ? comment.likes - 1 : comment.likes + 1,
liked: !comment.liked,
};
}
return comment;
}),
};
}
return post;
}),
);
};
return (
<div style={{ maxWidth: 800, margin: '0 auto', padding: 20 }}>
{/* 发布新帖子区域 */}
<Card title="发布新帖子" style={{ marginBottom: 20 }}>
<Form.Item>
<TextArea
rows={4}
value={postContent}
onChange={(e) => setPostContent(e.target.value)}
placeholder="分享你的想法..."
/>
</Form.Item>
<Form.Item>
<Upload
listType="picture-card"
fileList={fileList}
onPreview={handlePreview}
onChange={handleChange}
beforeUpload={() => false} // 阻止自动上传
multiple
>
{fileList.length >= 4 ? null : uploadButton}
</Upload>
</Form.Item>
<Form.Item>
<Button type="primary" onClick={handlePostSubmit} icon={<SendOutlined />}>
</Button>
</Form.Item>
</Card>
{/* 图片预览 */}
{previewImage && (
<Image
wrapperStyle={{ display: 'none' }}
preview={{
visible: previewOpen,
onVisibleChange: (visible) => setPreviewOpen(visible),
afterOpenChange: (visible) => !visible && setPreviewImage(''),
}}
src={previewImage}
/>
)}
{/* 帖子列表 */}
<List
itemLayout="vertical"
size="large"
dataSource={posts}
renderItem={(post) => (
<Card style={{ marginBottom: 20 }}>
<Comment
author={<a>{post.author}</a>}
avatar={<Avatar src={post.avatar} alt={post.author} />}
content={<p>{post.content}</p>}
datetime={
<Tooltip title={post.datetime}>
<span>{moment(post.datetime).fromNow()}</span>
</Tooltip>
}
/>
{/* 帖子图片 */}
{post.images.length > 0 && (
<div style={{ marginTop: 16 }}>
<Image.PreviewGroup>
<Space wrap>
{post.images.map((img, index) => (
<Image
key={index}
width={150}
height={150}
src={img}
style={{ objectFit: 'cover' }}
/>
))}
</Space>
</Image.PreviewGroup>
</div>
)}
{/* 帖子操作 */}
<div style={{ marginTop: 16 }}>
<Space>
<span onClick={() => handleLikePost(post.id)} style={{ cursor: 'pointer' }}>
{post.liked ? <LikeFilled style={{ color: '#1890ff' }} /> : <LikeOutlined />}
<span style={{ paddingLeft: 8 }}>{post.likes}</span>
</span>
<span onClick={() => toggleCommentVisible(post.id)} style={{ cursor: 'pointer' }}>
<MessageOutlined />
<span style={{ paddingLeft: 8 }}>{post.comments.length}</span>
</span>
</Space>
</div>
{/* 评论区域 */}
{post.commentVisible && (
<div style={{ marginTop: 16 }}>
<Divider orientation="left" plain>
</Divider>
{/* 评论列表 */}
<List
dataSource={post.comments}
renderItem={(comment) => (
<Comment
author={<a>{comment.author}</a>}
avatar={<Avatar src={comment.avatar} alt={comment.author} />}
content={<p>{comment.content}</p>}
datetime={
<Tooltip title={comment.datetime}>
<span>{moment(comment.datetime).fromNow()}</span>
</Tooltip>
}
actions={[
<span
key="comment-like"
onClick={() => handleLikeComment(post.id, comment.id)}
style={{ cursor: 'pointer' }}
>
{comment.liked ? (
<LikeFilled style={{ color: '#1890ff' }} />
) : (
<LikeOutlined />
)}
<span style={{ paddingLeft: 8 }}>{comment.likes}</span>
</span>,
]}
/>
)}
/>
{/* 添加评论 */}
<Comment
avatar={<Avatar src={currentUser.avatar} alt={currentUser.name} />}
content={
<Editor
onSubmit={(content) => handleAddComment(post.id, content)}
placeholder="写下你的评论..."
/>
}
/>
</div>
)}
</Card>
)}
/>
</div>
);
};
// 评论编辑器组件
const Editor = ({
onSubmit,
placeholder,
}: {
onSubmit: (content: string) => void;
placeholder: string;
}) => {
const [value, setValue] = useState('');
const handleSubmit = () => {
onSubmit(value);
setValue('');
};
return (
<>
<TextArea
rows={2}
onChange={(e) => setValue(e.target.value)}
value={value}
placeholder={placeholder}
/>
<Form.Item style={{ marginTop: 16, marginBottom: 0 }}>
<Button htmlType="submit" onClick={handleSubmit} type="primary" disabled={!value.trim()}>
</Button>
</Form.Item>
</>
);
};
export default Forum;

154
src/pages/MyChart/index.tsx Normal file
View File

@ -0,0 +1,154 @@
import { listChartByPageUsingPost } from '@/services/hebi/chartController';
import { useModel } from '@@/exports';
import { Avatar, Card, Input, List, message, Result } from 'antd';
import ReactECharts from 'echarts-for-react';
import React, { useEffect, useState } from 'react';
const { Search } = Input;
/**
*
* @constructor
*/
const MyChartPage: React.FC = () => {
const initSearchParams = {
current: 1,
pageSize: 6,
sortField:'createTime',
sortOrder:'desc',
};
const [searchParams, setSearchParams] = useState<API.ChartQueryRequest>({
...initSearchParams,
});
//获取用户头像可以从初始化状态中获取
const { initialState } = useModel('@@initialState');
const { currentUser } = initialState ?? {};
const [chartList, setChartList] = useState<API.Chart[]>();
const [total, setTotal] = useState<number>(0);
const [loading, setLoading] = useState<boolean>(true);
const loadData = async () => {
setLoading(true);
try {
const res = await listChartByPageUsingPost(searchParams);
if (res.data) {
setChartList(res.data.records ?? []);
setTotal(res.data.total ?? 0);
// 隐藏图标的tittle
if (res.data.records) {
res.data.records.forEach((data) => {
if(data.status==="succeed"){
const chartOption = JSON.parse(data.genChart ?? '{}');
chartOption.title = undefined;
data.genChart = JSON.stringify(chartOption);
}
});
}
} else {
message.error('获取我的图表失败');
}
} catch (e: any) {
message.error('获取我的图表失败' + e.message);
}
setLoading(false);
};
// 钩子函数
useEffect(() => {
loadData();
}, [searchParams]);
return (
<div className="my-chart-page">
<div>
<Search
placeholder="请输入图表名称"
loading={loading}
enterButton
onSearch={(value) => {
setSearchParams({
...initSearchParams,
name: value,
});
}}
/>
</div>
<div className="margin-16"></div>
<List
itemLayout="vertical"
grid={{
gutter: 16,
xs: 1,
sm: 1,
md: 1,
lg: 2,
xl: 2,
xxl: 2,
}}
pagination={{
onChange: (page, pageSize) => {
setSearchParams({
...searchParams,
current: page,
pageSize,
});
},
current: searchParams.current,
pageSize: searchParams.pageSize,
total: total,
}}
loading={loading}
dataSource={chartList}
renderItem={(item) => (
<List.Item key={item.id}>
<Card style={{ width: '100%' }}>
<List.Item.Meta
// 若第一个操作数为 false 或者可以转换为 false 的值(如 null、undefined、0、''、NaN 等),则整个表达式的结果就是这个操作数,并且不会再计算第二个操作数。
// 若第一个操作数为 true 或者可以转换为 true 的值,那么整个表达式的结果取决于第二个操作数,也就是会返回第二个操作数的值。
avatar={<Avatar src={currentUser && currentUser.userAvatar} />}
title={item.name}
description={item.chartType ? '图表类型:' + item.chartType : undefined}
/>
<>
{item.status === 'wait' && (
<>
<Result
status="warning"
title="图表待生成"
subTitle={item.execMessage ?? '当前图表生成队列繁忙,请耐心你等候'}
></Result>
</>
)}
{item.status === 'running' && (
<>
<Result status="info" title="图表生成中" subTitle={item.execMessage}></Result>
</>
)}
{item.status === 'succeed' && (
<>
{'分析目标:' + item.goal}
<ReactECharts option={JSON.parse(item.genChart ?? '{}')} />
</>
)}
{item.status === 'failed' && (
<>
<Result
status="error"
title="图表生成失败"
subTitle={item.execMessage}
></Result>
</>
)}
</>
</Card>
</List.Item>
)}
/>
{total}
</div>
);
};
export default MyChartPage;

View File

@ -0,0 +1,203 @@
import { Footer } from '@/components';
import {
LockOutlined,
UserOutlined,
} from '@ant-design/icons';
import {
LoginForm,
ProFormText,
} from '@ant-design/pro-components';
import { Helmet, history, Link, useModel } from '@umijs/max';
import { message, Tabs } from 'antd';
import { createStyles } from 'antd-style';
import React, { useState,useEffect } from 'react';
import { flushSync } from 'react-dom';
import Settings from '../../../../config/defaultSettings';
import { listChartByPageUsingPost } from '@/services/hebi/chartController';
import { getLoginUserUsingGet, userLoginUsingPost } from '@/services/hebi/userController';
const useStyles = createStyles(({ token }) => {
return {
action: {
marginLeft: '8px',
color: 'rgba(0, 0, 0, 0.2)',
fontSize: '24px',
verticalAlign: 'middle',
cursor: 'pointer',
transition: 'color 0.3s',
'&:hover': {
color: token.colorPrimaryActive,
},
},
lang: {
width: 42,
height: 42,
lineHeight: '42px',
position: 'fixed',
right: 16,
borderRadius: token.borderRadius,
':hover': {
backgroundColor: token.colorBgTextHover,
},
},
container: {
display: 'flex',
flexDirection: 'column',
height: '100vh',
overflow: 'auto',
backgroundImage:
"url('https://mdn.alipayobjects.com/yuyan_qk0oxh/afts/img/V-_oS6r-i7wAAAAAAAAAAAAAFl94AQBr')",
backgroundSize: '100% 100%',
},
};
});
const Login: React.FC = () => {
const [type, setType] = useState<string>('account');
const { setInitialState } = useModel('@@initialState');
const { styles } = useStyles();
//获取用户的登录信息
const fetchUserInfo = async () => {
const userInfo = await getLoginUserUsingGet();
if (userInfo) {
flushSync(() => {
setInitialState((s) => ({
...s,
currentUser: userInfo,
}));
});
}
};
//测试
useEffect(()=>{
listChartByPageUsingPost({}).then(res=>{
console.error('res:',res)
})
})
const handleSubmit = async (values: API.UserLoginRequest) => {
try {
// 登录
const res = await userLoginUsingPost(values);
if (res.code === 0) {
const defaultLoginSuccessMessage = '登录成功!';
message.success(defaultLoginSuccessMessage);
await fetchUserInfo();
const urlParams = new URL(window.location.href).searchParams;
history.push(urlParams.get('redirect') || '/');
return;
}else{
message.error(res.message);
}
} catch (error) {
const defaultLoginFailureMessage = '登录失败,请重试!';
console.log(error);
message.error(defaultLoginFailureMessage);
}
};
return (
//登录页面
<div className={styles.container}>
<Helmet>
<title>
{'登录'}- {Settings.title}
</title>
</Helmet>
<div
style={{
flex: '1',
padding: '32px 0',
}}
>
<LoginForm
contentStyle={{
minWidth: 280,
maxWidth: '75vw',
}}
logo={<img alt="logo" src="/logo.svg" />}
title="基于AIGC的智能数据分析系统"
subTitle={'本系统助力于让数据分析变得简单高效'}
onFinish={async (values) => {
await handleSubmit(values as API.UserLoginRequest);
}}
>
<Tabs
activeKey={type}
onChange={setType}
centered
items={[
{
key: 'account',
label: '登录',
},
]}
/>
{type === 'account' && (
<>
<ProFormText
name="userAccount"
fieldProps={{
size: 'large',
prefix: <UserOutlined />,
}}
placeholder={'请输入用户名'}
rules={[
{
required: true,
message: '用户名是必填项!',
},
]}
/>
<ProFormText.Password
name="userPassword"
fieldProps={{
size: 'large',
prefix: <LockOutlined />,
}}
placeholder={'请输入密码'}
rules={[
{
required: true,
message: '密码是必填项!',
},
]}
/>
</>
)}
<div
style={{
marginBottom: 12,
display: 'flex',
// 元素靠右排列
justifyContent: 'flex-end',
// 设置元素之间的间距
gap: 16,
}}
>
<Link
to="/user/register"
>
</Link>
</div>
</LoginForm>
</div>
<Footer />
</div>
);
};
export default Login;

View File

@ -0,0 +1,185 @@
import { Footer } from '@/components';
import { userRegisterUsingPost } from '@/services/hebi/userController';
import { LockOutlined, PictureOutlined, UserOutlined } from '@ant-design/icons';
import { ProForm, ProFormText } from '@ant-design/pro-components';
import { Helmet, history, Link } from '@umijs/max';
import { Avatar, Card, Form, message, Typography, Upload } from 'antd';
import { createStyles } from 'antd-style';
import React, { useState } from 'react';
import Settings from '../../../../config/defaultSettings';
const { Title } = Typography;
const useStyles = createStyles(({ token }) => {
return {
container: {
display: 'flex',
flexDirection: 'column',
height: '100vh',
overflow: 'auto',
backgroundImage:
"url('https://mdn.alipayobjects.com/yuyan_qk0oxh/afts/img/V-_oS6r-i7wAAAAAAAAAAAAAFl94AQBr')",
backgroundSize: '100% 100%',
},
card: {
minWidth: 320,
maxWidth: '75vw',
padding: token.paddingLG,
background: 'transparent',
border: 'none',
boxShadow: 'none',
},
header: {
textAlign: 'center',
marginBottom: 24,
display: 'flex',
justifyContent: 'center',
alignItems: 'center',
gap: '16px',
},
loginLink: {
display: 'block',
textAlign: 'center',
marginBottom: 24,
},
avatarUploader: {
display: 'flex',
justifyContent: 'center',
marginBottom: 24,
},
};
});
const Register: React.FC = () => {
const { styles } = useStyles();
const [avatarUrl, setAvatarUrl] = useState<string>();
const [form] = Form.useForm();
const handleSubmit = async (values: Record<string, any>) => {
try {
if (values.userPassword !== values.checkPassword) {
message.error('两次输入的密码不一致!');
return;
}
const res = await userRegisterUsingPost({ ...values, userAvatar: avatarUrl });
if (res.code === 0) {
message.success('注册成功!');
history.push('/user/login');
} else {
message.error(res.message);
}
} catch (error) {
message.error('注册失败,请重试!');
}
};
const uploadButton = (
<div>
<PictureOutlined />
<div style={{ marginTop: 8 }}></div>
</div>
);
return (
<div className={styles.container}>
<Helmet>
<title>{'注册'} - {Settings.title}</title>
</Helmet>
<div style={{ flex: '1', padding: '32px 0', display: 'flex', justifyContent: 'center', alignItems: 'center' }}>
<Card className={styles.card}>
<div className={styles.header}>
<img alt="logo" src="/logo.svg" style={{ height: 44 }} />
<div>
<Title level={3} style={{ margin: 0 }}>AIGC的智能数据分析系统</Title>
<div style={{ color: 'rgba(0,0,0,.45)' }}></div>
</div>
</div>
<Link to="/user/login" className={styles.loginLink}></Link>
<ProForm
form={form}
onFinish={handleSubmit}
submitter={{
searchConfig: { submitText: '注册' },
render: (_, dom) => dom.pop(),
submitButtonProps: {
size: 'large',
style: { width: '100%' },
},
}}
>
<ProForm.Item
name="userAvatar"
rules={[{ required: true, message: '请上传头像' }]}
className={styles.avatarUploader}
>
<Upload
name="avatar"
listType="picture-card"
showUploadList={false}
action="https://www.mocky.io/v2/5cc8019d300000980a055e76"
onChange={(info) => {
if (info.file.status === 'done') {
setAvatarUrl(info.file.response.url);
}
}}
>
{avatarUrl ? <Avatar src={avatarUrl} size={64} /> : uploadButton}
</Upload>
</ProForm.Item>
<ProFormText
name="userAccount"
fieldProps={{ size: 'large', prefix: <UserOutlined /> }}
placeholder="请输入账号"
rules={[
{ required: true, message: '账号是必填项!' },
{ min: 4, message: '账号长度不能小于4位' },
]}
/>
<ProFormText
name="userName"
fieldProps={{ size: 'large', prefix: <UserOutlined /> }}
placeholder="请输入用户名"
rules={[{ required: true, message: '用户名是必填项!' }]}
/>
<ProFormText.Password
name="userPassword"
fieldProps={{ size: 'large', prefix: <LockOutlined /> }}
placeholder="请输入密码"
rules={[
{ required: true, message: '密码是必填项!' },
{ min: 8, message: '密码长度不能小于8位' },
]}
/>
<ProFormText.Password
name="checkPassword"
fieldProps={{ size: 'large', prefix: <LockOutlined /> }}
placeholder="请确认密码"
rules={[
{ required: true, message: '请确认密码!' },
({ getFieldValue }) => ({
validator(_, value) {
if (!value || getFieldValue('userPassword') === value) {
return Promise.resolve();
}
return Promise.reject(new Error('两次输入的密码不一致!'));
},
}),
]}
/>
</ProForm>
</Card>
</div>
<Footer />
</div>
);
};
export default Register;

164
src/pages/Welcome.tsx Normal file
View File

@ -0,0 +1,164 @@
import { PageContainer } from '@ant-design/pro-components';
import { useModel } from '@umijs/max';
import { Card, theme } from 'antd';
import React from 'react';
/**
*
* @param param0
* @returns
*/
const InfoCard: React.FC<{
title: string;
index: number;
desc: string;
href: string;
}> = ({ title, href, index, desc }) => {
const { useToken } = theme;
const { token } = useToken();
return (
<div
style={{
backgroundColor: token.colorBgContainer,
boxShadow: token.boxShadow,
borderRadius: '8px',
fontSize: '14px',
color: token.colorTextSecondary,
lineHeight: '22px',
padding: '16px 19px',
minWidth: '220px',
flex: 1,
}}
>
<div
style={{
display: 'flex',
gap: '4px',
alignItems: 'center',
}}
>
<div
style={{
width: 48,
height: 48,
lineHeight: '22px',
backgroundSize: '100%',
textAlign: 'center',
padding: '8px 16px 16px 12px',
color: '#FFF',
fontWeight: 'bold',
backgroundImage:
"url('https://gw.alipayobjects.com/zos/bmw-prod/daaf8d50-8e6d-4251-905d-676a24ddfa12.svg')",
}}
>
{index}
</div>
<div
style={{
fontSize: '16px',
color: token.colorText,
paddingBottom: 8,
}}
>
{title}
</div>
</div>
<div
style={{
fontSize: '14px',
color: token.colorTextSecondary,
textAlign: 'justify',
lineHeight: '22px',
marginBottom: 8,
}}
>
{desc}
</div>
<a href={href} target="_blank" rel="noreferrer">
{'>'}
</a>
</div>
);
};
const Welcome: React.FC = () => {
const { token } = theme.useToken();
const { initialState } = useModel('@@initialState');
return (
<PageContainer>
<Card
style={{
borderRadius: 8,
}}
bodyStyle={{
backgroundImage:
initialState?.settings?.navTheme === 'realDark'
? 'background-image: linear-gradient(75deg, #1A1B1F 0%, #191C1F 100%)'
: 'background-image: linear-gradient(75deg, #FBFDFF 0%, #F5F7FF 100%)',
}}
>
<div
style={{
backgroundPosition: '100% -30%',
backgroundRepeat: 'no-repeat',
backgroundSize: '274px auto',
backgroundImage:
"url('https://gw.alipayobjects.com/mdn/rms_a9745b/afts/img/A*BuFmQqsB2iAAAAAAAAAAAAAAARQnAQ')",
}}
>
<div
style={{
fontSize: '20px',
color: token.colorTextHeading,
}}
>
使 AIGC的智能数据分析系统
</div>
<p
style={{
fontSize: '14px',
color: token.colorTextSecondary,
lineHeight: '22px',
marginTop: 16,
marginBottom: 32,
width: '65%',
}}
>
AIGC的智能数据分析系统 umiAnt Design ProComponents
//
</p>
<div
style={{
display: 'flex',
flexWrap: 'wrap',
gap: 16,
}}
>
<InfoCard
index={1}
href="https://umijs.org/docs/introduce/introduce"
title="了解 umi"
desc="umi 是一个可扩展的企业级前端应用框架,umi 以路由为基础的,同时支持配置式路由和约定式路由,保证路由的功能完备,并以此进行功能扩展。"
/>
<InfoCard
index={2}
title="了解 基于AIGC的智能数据分析系统"
href="https://ant.design"
desc="antd 是基于 基于AIGC的智能数据分析系统 设计体系的 React UI 组件库,主要用于研发企业级中后台产品。"
/>
<InfoCard
index={3}
title="了解 Pro Components"
href="https://procomponents.ant.design"
desc="ProComponents 是一个基于 基于AIGC的智能数据分析系统 做了更高抽象的模板组件,以 一个组件就是一个页面为开发理念,为中后台开发带来更好的体验。"
/>
</div>
</div>
</Card>
</PageContainer>
);
};
export default Welcome;

110
src/requestErrorConfig.ts Normal file
View File

@ -0,0 +1,110 @@
import type { RequestOptions } from '@@/plugin-request/request';
import type { RequestConfig } from '@umijs/max';
import { message, notification } from 'antd';
// 错误处理方案: 错误类型
enum ErrorShowType {
SILENT = 0,
WARN_MESSAGE = 1,
ERROR_MESSAGE = 2,
NOTIFICATION = 3,
REDIRECT = 9,
}
// 与后端约定的响应数据格式
interface ResponseStructure {
success: boolean;
data: any;
errorCode?: number;
errorMessage?: string;
showType?: ErrorShowType;
}
/**
* @name
* pro
* @doc https://umijs.org/docs/max/request#配置
*/
export const errorConfig: RequestConfig = {
// 错误处理: umi@3 的错误处理方案。
errorConfig: {
// 错误抛出
errorThrower: (res) => {
const { success, data, errorCode, errorMessage, showType } =
res as unknown as ResponseStructure;
if (!success) {
const error: any = new Error(errorMessage);
error.name = 'BizError';
error.info = { errorCode, errorMessage, showType, data };
throw error; // 抛出自制的错误
}
},
// 错误接收及处理
errorHandler: (error: any, opts: any) => {
if (opts?.skipErrorHandler) throw error;
// 我们的 errorThrower 抛出的错误。
if (error.name === 'BizError') {
const errorInfo: ResponseStructure | undefined = error.info;
if (errorInfo) {
const { errorMessage, errorCode } = errorInfo;
switch (errorInfo.showType) {
case ErrorShowType.SILENT:
// do nothing
break;
case ErrorShowType.WARN_MESSAGE:
message.warning(errorMessage);
break;
case ErrorShowType.ERROR_MESSAGE:
message.error(errorMessage);
break;
case ErrorShowType.NOTIFICATION:
notification.open({
description: errorMessage,
message: errorCode,
});
break;
case ErrorShowType.REDIRECT:
// TODO: redirect
break;
default:
message.error(errorMessage);
}
}
} else if (error.response) {
// Axios 的错误
// 请求成功发出且服务器也响应了状态码,但状态代码超出了 2xx 的范围
message.error(`Response status:${error.response.status}`);
} else if (error.request) {
// 请求已经成功发起,但没有收到响应
// \`error.request\` 在浏览器中是 XMLHttpRequest 的实例,
// 而在node.js中是 http.ClientRequest 的实例
message.error('None response! Please retry.');
} else {
// 发送请求时出了点问题
message.error('Request error, please retry.');
}
},
},
// 请求拦截器
requestInterceptors: [
(config: RequestOptions) => {
// 拦截请求配置,进行个性化处理。
// const url = config?.url?.concat('?token = 123');
// return { ...config, url };
return config;
},
],
// 响应拦截器
responseInterceptors: [
(response) => {
// 拦截响应数据,进行个性化处理
const { data } = response as unknown as ResponseStructure;
if (data?.success === false) {
message.error('请求失败!');
}
return response;
},
],
};

65
src/service-worker.js Normal file
View File

@ -0,0 +1,65 @@
/* eslint-disable no-restricted-globals */
/* eslint-disable no-underscore-dangle */
/* globals workbox */
workbox.core.setCacheNameDetails({
prefix: 'antd-pro',
suffix: 'v5',
});
// Control all opened tabs ASAP
workbox.clientsClaim();
/**
* Use precaching list generated by workbox in build process.
* https://developers.google.com/web/tools/workbox/reference-docs/latest/workbox.precaching
*/
workbox.precaching.precacheAndRoute(self.__precacheManifest || []);
/**
* Register a navigation route.
* https://developers.google.com/web/tools/workbox/modules/workbox-routing#how_to_register_a_navigation_route
*/
workbox.routing.registerNavigationRoute('/index.html');
/**
* Use runtime cache:
* https://developers.google.com/web/tools/workbox/reference-docs/latest/workbox.routing#.registerRoute
*
* Workbox provides all common caching strategies including CacheFirst, NetworkFirst etc.
* https://developers.google.com/web/tools/workbox/reference-docs/latest/workbox.strategies
*/
/** Handle API requests */
workbox.routing.registerRoute(/\/api\//, workbox.strategies.networkFirst());
/** Handle third party requests */
workbox.routing.registerRoute(
/^https:\/\/gw\.alipayobjects\.com\//,
workbox.strategies.networkFirst(),
);
workbox.routing.registerRoute(
/^https:\/\/cdnjs\.cloudflare\.com\//,
workbox.strategies.networkFirst(),
);
workbox.routing.registerRoute(/\/color.less/, workbox.strategies.networkFirst());
/** Response to client after skipping waiting with MessageChannel */
addEventListener('message', (event) => {
const replyPort = event.ports[0];
const message = event.data;
if (replyPort && message && message.type === 'skip-waiting') {
event.waitUntil(
self.skipWaiting().then(
() => {
replyPort.postMessage({
error: null,
});
},
(error) => {
replyPort.postMessage({
error,
});
},
),
);
}
});

View File

@ -0,0 +1,190 @@
// @ts-ignore
/* eslint-disable */
import { request } from '@umijs/max';
/** addChart POST /api/chart/add */
export async function addChartUsingPost(
body: API.ChartAddRequest,
options?: { [key: string]: any },
) {
return request<API.BaseResponseLong_>('/api/chart/add', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
data: body,
...(options || {}),
});
}
/** deleteChart POST /api/chart/delete */
export async function deleteChartUsingPost(
body: API.DeleteRequest,
options?: { [key: string]: any },
) {
return request<API.BaseResponseBoolean_>('/api/chart/delete', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
data: body,
...(options || {}),
});
}
/** editChart POST /api/chart/edit */
export async function editChartUsingPost(
body: API.ChartEditRequest,
options?: { [key: string]: any },
) {
return request<API.BaseResponseBoolean_>('/api/chart/edit', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
data: body,
...(options || {}),
});
}
/** genChartByAi POST /api/chart/gen */
export async function genChartByAiUsingPost(
// 叠加生成的Param类型 (非body参数swagger默认没有生成对象)
params: API.genChartByAiUsingPOSTParams,
body: {},
file?: File,
options?: { [key: string]: any },
) {
const formData = new FormData();
if (file) {
formData.append('file', file);
}
Object.keys(body).forEach((ele) => {
const item = (body as any)[ele];
if (item !== undefined && item !== null) {
if (typeof item === 'object' && !(item instanceof File)) {
if (item instanceof Array) {
item.forEach((f) => formData.append(ele, f || ''));
} else {
formData.append(ele, JSON.stringify(item));
}
} else {
formData.append(ele, item);
}
}
});
return request<API.BaseResponseBiResponse_>('/api/chart/gen', {
method: 'POST',
params: {
...params,
},
data: formData,
requestType: 'form',
...(options || {}),
});
}
/** genChartByAiAsync POST /api/chart/gen/async */
export async function genChartByAiAsyncUsingPost(
// 叠加生成的Param类型 (非body参数swagger默认没有生成对象)
params: API.genChartByAiAsyncUsingPOSTParams,
body: {},
file?: File,
options?: { [key: string]: any },
) {
const formData = new FormData();
if (file) {
formData.append('file', file);
}
Object.keys(body).forEach((ele) => {
const item = (body as any)[ele];
if (item !== undefined && item !== null) {
if (typeof item === 'object' && !(item instanceof File)) {
if (item instanceof Array) {
item.forEach((f) => formData.append(ele, f || ''));
} else {
formData.append(ele, JSON.stringify(item));
}
} else {
formData.append(ele, item);
}
}
});
return request<API.BaseResponseBiResponse_>('/api/chart/gen/async', {
method: 'POST',
params: {
...params,
},
data: formData,
requestType: 'form',
...(options || {}),
});
}
/** getChartById GET /api/chart/get */
export async function getChartByIdUsingGet(
// 叠加生成的Param类型 (非body参数swagger默认没有生成对象)
params: API.getChartByIdUsingGETParams,
options?: { [key: string]: any },
) {
return request<API.BaseResponseChart_>('/api/chart/get', {
method: 'GET',
params: {
...params,
},
...(options || {}),
});
}
/** listChartByPage POST /api/chart/list/page */
export async function listChartByPageUsingPost(
body: API.ChartQueryRequest,
options?: { [key: string]: any },
) {
return request<API.BaseResponsePageChart_>('/api/chart/list/page', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
data: body,
...(options || {}),
});
}
/** listMyChartByPage POST /api/chart/my/list/page */
export async function listMyChartByPageUsingPost(
body: API.ChartQueryRequest,
options?: { [key: string]: any },
) {
return request<API.BaseResponsePageChart_>('/api/chart/my/list/page', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
data: body,
...(options || {}),
});
}
/** updateChart POST /api/chart/update */
export async function updateChartUsingPost(
body: API.ChartUpdateRequest,
options?: { [key: string]: any },
) {
return request<API.BaseResponseBoolean_>('/api/chart/update', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
data: body,
...(options || {}),
});
}

View File

@ -0,0 +1,44 @@
// @ts-ignore
/* eslint-disable */
import { request } from '@umijs/max';
/** uploadFile POST /api/file/upload */
export async function uploadFileUsingPost(
// 叠加生成的Param类型 (非body参数swagger默认没有生成对象)
params: API.uploadFileUsingPOSTParams,
body: {},
file?: File,
options?: { [key: string]: any },
) {
const formData = new FormData();
if (file) {
formData.append('file', file);
}
Object.keys(body).forEach((ele) => {
const item = (body as any)[ele];
if (item !== undefined && item !== null) {
if (typeof item === 'object' && !(item instanceof File)) {
if (item instanceof Array) {
item.forEach((f) => formData.append(ele, f || ''));
} else {
formData.append(ele, JSON.stringify(item));
}
} else {
formData.append(ele, item);
}
}
});
return request<API.BaseResponseString_>('/api/file/upload', {
method: 'POST',
params: {
...params,
},
data: formData,
requestType: 'form',
...(options || {}),
});
}

View File

@ -0,0 +1,20 @@
// @ts-ignore
/* eslint-disable */
// API 更新时间:
// API 唯一标识:
import * as chartController from './chartController';
import * as fileController from './fileController';
import * as postController from './postController';
import * as postFavourController from './postFavourController';
import * as postThumbController from './postThumbController';
import * as queueController from './queueController';
import * as userController from './userController';
export default {
chartController,
fileController,
postController,
postFavourController,
postThumbController,
queueController,
userController,
};

View File

@ -0,0 +1,135 @@
// @ts-ignore
/* eslint-disable */
import { request } from '@umijs/max';
/** addPost POST /api/post/add */
export async function addPostUsingPost(body: API.PostAddRequest, options?: { [key: string]: any }) {
return request<API.BaseResponseLong_>('/api/post/add', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
data: body,
...(options || {}),
});
}
/** deletePost POST /api/post/delete */
export async function deletePostUsingPost(
body: API.DeleteRequest,
options?: { [key: string]: any },
) {
return request<API.BaseResponseBoolean_>('/api/post/delete', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
data: body,
...(options || {}),
});
}
/** editPost POST /api/post/edit */
export async function editPostUsingPost(
body: API.PostEditRequest,
options?: { [key: string]: any },
) {
return request<API.BaseResponseBoolean_>('/api/post/edit', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
data: body,
...(options || {}),
});
}
/** getPostVOById GET /api/post/get/vo */
export async function getPostVoByIdUsingGet(
// 叠加生成的Param类型 (非body参数swagger默认没有生成对象)
params: API.getPostVOByIdUsingGETParams,
options?: { [key: string]: any },
) {
return request<API.BaseResponsePostVO_>('/api/post/get/vo', {
method: 'GET',
params: {
...params,
},
...(options || {}),
});
}
/** listPostByPage POST /api/post/list/page */
export async function listPostByPageUsingPost(
body: API.PostQueryRequest,
options?: { [key: string]: any },
) {
return request<API.BaseResponsePagePost_>('/api/post/list/page', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
data: body,
...(options || {}),
});
}
/** listPostVOByPage POST /api/post/list/page/vo */
export async function listPostVoByPageUsingPost(
body: API.PostQueryRequest,
options?: { [key: string]: any },
) {
return request<API.BaseResponsePagePostVO_>('/api/post/list/page/vo', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
data: body,
...(options || {}),
});
}
/** listMyPostVOByPage POST /api/post/my/list/page/vo */
export async function listMyPostVoByPageUsingPost(
body: API.PostQueryRequest,
options?: { [key: string]: any },
) {
return request<API.BaseResponsePagePostVO_>('/api/post/my/list/page/vo', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
data: body,
...(options || {}),
});
}
/** searchPostVOByPage POST /api/post/search/page/vo */
export async function searchPostVoByPageUsingPost(
body: API.PostQueryRequest,
options?: { [key: string]: any },
) {
return request<API.BaseResponsePagePostVO_>('/api/post/search/page/vo', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
data: body,
...(options || {}),
});
}
/** updatePost POST /api/post/update */
export async function updatePostUsingPost(
body: API.PostUpdateRequest,
options?: { [key: string]: any },
) {
return request<API.BaseResponseBoolean_>('/api/post/update', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
data: body,
...(options || {}),
});
}

View File

@ -0,0 +1,48 @@
// @ts-ignore
/* eslint-disable */
import { request } from '@umijs/max';
/** doPostFavour POST /api/post_favour/ */
export async function doPostFavourUsingPost(
body: API.PostFavourAddRequest,
options?: { [key: string]: any },
) {
return request<API.BaseResponseInt_>('/api/post_favour/', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
data: body,
...(options || {}),
});
}
/** listFavourPostByPage POST /api/post_favour/list/page */
export async function listFavourPostByPageUsingPost(
body: API.PostFavourQueryRequest,
options?: { [key: string]: any },
) {
return request<API.BaseResponsePagePostVO_>('/api/post_favour/list/page', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
data: body,
...(options || {}),
});
}
/** listMyFavourPostByPage POST /api/post_favour/my/list/page */
export async function listMyFavourPostByPageUsingPost(
body: API.PostQueryRequest,
options?: { [key: string]: any },
) {
return request<API.BaseResponsePagePostVO_>('/api/post_favour/my/list/page', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
data: body,
...(options || {}),
});
}

View File

@ -0,0 +1,18 @@
// @ts-ignore
/* eslint-disable */
import { request } from '@umijs/max';
/** doThumb POST /api/post_thumb/ */
export async function doThumbUsingPost(
body: API.PostThumbAddRequest,
options?: { [key: string]: any },
) {
return request<API.BaseResponseInt_>('/api/post_thumb/', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
data: body,
...(options || {}),
});
}

View File

@ -0,0 +1,26 @@
// @ts-ignore
/* eslint-disable */
import { request } from '@umijs/max';
/** add GET /api/queue/add */
export async function addUsingGet(
// 叠加生成的Param类型 (非body参数swagger默认没有生成对象)
params: API.addUsingGETParams,
options?: { [key: string]: any },
) {
return request<any>('/api/queue/add', {
method: 'GET',
params: {
...params,
},
...(options || {}),
});
}
/** get GET /api/queue/get */
export async function getUsingGet(options?: { [key: string]: any }) {
return request<string>('/api/queue/get', {
method: 'GET',
...(options || {}),
});
}

426
src/services/hebi/typings.d.ts vendored Normal file
View File

@ -0,0 +1,426 @@
declare namespace API {
type addUsingGETParams = {
/** name */
name?: string;
};
type BaseResponseBiResponse_ = {
code?: number;
data?: BiResponse;
message?: string;
};
type BaseResponseBoolean_ = {
code?: number;
data?: boolean;
message?: string;
};
type BaseResponseChart_ = {
code?: number;
data?: Chart;
message?: string;
};
type BaseResponseInt_ = {
code?: number;
data?: number;
message?: string;
};
type BaseResponseLoginUserVO_ = {
code?: number;
data?: LoginUserVO;
message?: string;
};
type BaseResponseLong_ = {
code?: number;
data?: number;
message?: string;
};
type BaseResponsePageChart_ = {
code?: number;
data?: PageChart_;
message?: string;
};
type BaseResponsePagePost_ = {
code?: number;
data?: PagePost_;
message?: string;
};
type BaseResponsePagePostVO_ = {
code?: number;
data?: PagePostVO_;
message?: string;
};
type BaseResponsePageUser_ = {
code?: number;
data?: PageUser_;
message?: string;
};
type BaseResponsePageUserVO_ = {
code?: number;
data?: PageUserVO_;
message?: string;
};
type BaseResponsePostVO_ = {
code?: number;
data?: PostVO;
message?: string;
};
type BaseResponseString_ = {
code?: number;
data?: string;
message?: string;
};
type BaseResponseUser_ = {
code?: number;
data?: User;
message?: string;
};
type BaseResponseUserVO_ = {
code?: number;
data?: UserVO;
message?: string;
};
type BiResponse = {
chartId?: number;
genChart?: string;
genResult?: string;
};
type Chart = {
chartData?: string;
chartType?: string;
createTime?: string;
execMessage?: string;
genChart?: string;
genResult?: string;
goal?: string;
id?: number;
isDelete?: number;
name?: string;
status?: string;
updateTime?: string;
userId?: number;
};
type ChartAddRequest = {
chartData?: string;
chartType?: string;
goal?: string;
name?: string;
};
type ChartEditRequest = {
chartData?: string;
chartType?: string;
goal?: string;
id?: number;
name?: string;
};
type ChartQueryRequest = {
chartType?: string;
current?: number;
goal?: string;
id?: number;
name?: string;
pageSize?: number;
sortField?: string;
sortOrder?: string;
userId?: number;
};
type ChartUpdateRequest = {
chartData?: string;
chartType?: string;
createTime?: string;
genChart?: string;
genResult?: string;
goal?: string;
id?: number;
isDelete?: number;
name?: string;
updateTime?: string;
userId?: number;
};
type DeleteRequest = {
id?: number;
};
type genChartByAiAsyncUsingPOSTParams = {
chartType?: string;
goal?: string;
name?: string;
};
type genChartByAiUsingPOSTParams = {
chartType?: string;
goal?: string;
name?: string;
};
type getChartByIdUsingGETParams = {
/** id */
id?: number;
};
type getPostVOByIdUsingGETParams = {
/** id */
id?: number;
};
type getUserByIdUsingGETParams = {
/** id */
id?: number;
};
type getUserVOByIdUsingGETParams = {
/** id */
id?: number;
};
type LoginUserVO = {
createTime?: string;
id?: number;
updateTime?: string;
userAvatar?: string;
userName?: string;
userProfile?: string;
userRole?: string;
};
type OrderItem = {
asc?: boolean;
column?: string;
};
type PageChart_ = {
countId?: string;
current?: number;
maxLimit?: number;
optimizeCountSql?: boolean;
orders?: OrderItem[];
pages?: number;
records?: Chart[];
searchCount?: boolean;
size?: number;
total?: number;
};
type PagePost_ = {
countId?: string;
current?: number;
maxLimit?: number;
optimizeCountSql?: boolean;
orders?: OrderItem[];
pages?: number;
records?: Post[];
searchCount?: boolean;
size?: number;
total?: number;
};
type PagePostVO_ = {
countId?: string;
current?: number;
maxLimit?: number;
optimizeCountSql?: boolean;
orders?: OrderItem[];
pages?: number;
records?: PostVO[];
searchCount?: boolean;
size?: number;
total?: number;
};
type PageUser_ = {
countId?: string;
current?: number;
maxLimit?: number;
optimizeCountSql?: boolean;
orders?: OrderItem[];
pages?: number;
records?: User[];
searchCount?: boolean;
size?: number;
total?: number;
};
type PageUserVO_ = {
countId?: string;
current?: number;
maxLimit?: number;
optimizeCountSql?: boolean;
orders?: OrderItem[];
pages?: number;
records?: UserVO[];
searchCount?: boolean;
size?: number;
total?: number;
};
type Post = {
content?: string;
createTime?: string;
favourNum?: number;
id?: number;
isDelete?: number;
tags?: string;
thumbNum?: number;
title?: string;
updateTime?: string;
userId?: number;
};
type PostAddRequest = {
content?: string;
tags?: string[];
title?: string;
};
type PostEditRequest = {
content?: string;
id?: number;
tags?: string[];
title?: string;
};
type PostFavourAddRequest = {
postId?: number;
};
type PostFavourQueryRequest = {
current?: number;
pageSize?: number;
postQueryRequest?: PostQueryRequest;
sortField?: string;
sortOrder?: string;
userId?: number;
};
type PostQueryRequest = {
content?: string;
current?: number;
favourUserId?: number;
id?: number;
notId?: number;
orTags?: string[];
pageSize?: number;
searchText?: string;
sortField?: string;
sortOrder?: string;
tags?: string[];
title?: string;
userId?: number;
};
type PostThumbAddRequest = {
postId?: number;
};
type PostUpdateRequest = {
content?: string;
id?: number;
tags?: string[];
title?: string;
};
type PostVO = {
content?: string;
createTime?: string;
favourNum?: number;
hasFavour?: boolean;
hasThumb?: boolean;
id?: number;
tagList?: string[];
thumbNum?: number;
title?: string;
updateTime?: string;
user?: UserVO;
userId?: number;
};
type uploadFileUsingPOSTParams = {
biz?: string;
};
type User = {
createTime?: string;
id?: number;
isDelete?: number;
updateTime?: string;
userAccount?: string;
userAvatar?: string;
userName?: string;
userPassword?: string;
userRole?: string;
};
type UserAddRequest = {
userAccount?: string;
userAvatar?: string;
userName?: string;
userRole?: string;
};
type UserLoginRequest = {
userAccount?: string;
userPassword?: string;
};
type UserQueryRequest = {
current?: number;
id?: number;
mpOpenId?: string;
pageSize?: number;
sortField?: string;
sortOrder?: string;
unionId?: string;
userName?: string;
userProfile?: string;
userRole?: string;
};
type UserRegisterRequest = {
checkPassword?: string;
userAccount?: string;
userPassword?: string;
};
type UserUpdateMyRequest = {
userAvatar?: string;
userName?: string;
userProfile?: string;
};
type UserUpdateRequest = {
id?: number;
userAvatar?: string;
userName?: string;
userProfile?: string;
userRole?: string;
};
type UserVO = {
createTime?: string;
id?: number;
userAvatar?: string;
userName?: string;
userProfile?: string;
userRole?: string;
};
}

View File

@ -0,0 +1,166 @@
// @ts-ignore
/* eslint-disable */
import { request } from '@umijs/max';
/** addUser POST /api/user/add */
export async function addUserUsingPost(body: API.UserAddRequest, options?: { [key: string]: any }) {
return request<API.BaseResponseLong_>('/api/user/add', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
data: body,
...(options || {}),
});
}
/** deleteUser POST /api/user/delete */
export async function deleteUserUsingPost(
body: API.DeleteRequest,
options?: { [key: string]: any },
) {
return request<API.BaseResponseBoolean_>('/api/user/delete', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
data: body,
...(options || {}),
});
}
/** getUserById GET /api/user/get */
export async function getUserByIdUsingGet(
// 叠加生成的Param类型 (非body参数swagger默认没有生成对象)
params: API.getUserByIdUsingGETParams,
options?: { [key: string]: any },
) {
return request<API.BaseResponseUser_>('/api/user/get', {
method: 'GET',
params: {
...params,
},
...(options || {}),
});
}
/** getLoginUser GET /api/user/get/login */
export async function getLoginUserUsingGet(options?: { [key: string]: any }) {
return request<API.BaseResponseLoginUserVO_>('/api/user/get/login', {
method: 'GET',
...(options || {}),
});
}
/** getUserVOById GET /api/user/get/vo */
export async function getUserVoByIdUsingGet(
// 叠加生成的Param类型 (非body参数swagger默认没有生成对象)
params: API.getUserVOByIdUsingGETParams,
options?: { [key: string]: any },
) {
return request<API.BaseResponseUserVO_>('/api/user/get/vo', {
method: 'GET',
params: {
...params,
},
...(options || {}),
});
}
/** listUserByPage POST /api/user/list/page */
export async function listUserByPageUsingPost(
body: API.UserQueryRequest,
options?: { [key: string]: any },
) {
return request<API.BaseResponsePageUser_>('/api/user/list/page', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
data: body,
...(options || {}),
});
}
/** listUserVOByPage POST /api/user/list/page/vo */
export async function listUserVoByPageUsingPost(
body: API.UserQueryRequest,
options?: { [key: string]: any },
) {
return request<API.BaseResponsePageUserVO_>('/api/user/list/page/vo', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
data: body,
...(options || {}),
});
}
/** userLogin POST /api/user/login */
export async function userLoginUsingPost(
body: API.UserLoginRequest,
options?: { [key: string]: any },
) {
return request<API.BaseResponseLoginUserVO_>('/api/user/login', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
data: body,
...(options || {}),
});
}
/** userLogout POST /api/user/logout */
export async function userLogoutUsingPost(options?: { [key: string]: any }) {
return request<API.BaseResponseBoolean_>('/api/user/logout', {
method: 'POST',
...(options || {}),
});
}
/** userRegister POST /api/user/register */
export async function userRegisterUsingPost(
body: API.UserRegisterRequest,
options?: { [key: string]: any },
) {
return request<API.BaseResponseLong_>('/api/user/register', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
data: body,
...(options || {}),
});
}
/** updateUser POST /api/user/update */
export async function updateUserUsingPost(
body: API.UserUpdateRequest,
options?: { [key: string]: any },
) {
return request<API.BaseResponseBoolean_>('/api/user/update', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
data: body,
...(options || {}),
});
}
/** updateMyUser POST /api/user/update/my */
export async function updateMyUserUsingPost(
body: API.UserUpdateMyRequest,
options?: { [key: string]: any },
) {
return request<API.BaseResponseBoolean_>('/api/user/update/my', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
data: body,
...(options || {}),
});
}

20
src/typings.d.ts vendored Normal file
View File

@ -0,0 +1,20 @@
declare module 'slash2';
declare module '*.css';
declare module '*.less';
declare module '*.scss';
declare module '*.sass';
declare module '*.svg';
declare module '*.png';
declare module '*.jpg';
declare module '*.jpeg';
declare module '*.gif';
declare module '*.bmp';
declare module '*.tiff';
declare module 'omit.js';
declare module 'numeral';
declare module '@antv/data-set';
declare module 'mockjs';
declare module 'react-fittext';
declare module 'bizcharts-plugin-slider';
declare const REACT_APP_ENV: 'test' | 'dev' | 'pre' | false;

23
tsconfig.json Normal file
View File

@ -0,0 +1,23 @@
{
"compilerOptions": {
"target": "esnext",
"module": "esnext",
"moduleResolution": "node",
"importHelpers": true,
"jsx": "preserve",
"esModuleInterop": true,
"sourceMap": true,
"baseUrl": "./",
"skipLibCheck": true,
"experimentalDecorators": true,
"strict": true,
"resolveJsonModule": true,
"allowSyntheticDefaultImports": true,
"paths": {
"@/*": ["./src/*"],
"@@/*": ["./src/.umi/*"],
"@@test/*": ["./src/.umi-test/*"]
}
},
"include": ["./**/*.d.ts", "./**/*.ts", "./**/*.tsx"]
}