升级umi4.0,修复新建脚本

This commit is contained in:
whyour 2022-09-18 20:40:59 +08:00
parent 518e8903a3
commit 1e55f4065d
28 changed files with 227 additions and 126 deletions

View File

@ -1,20 +1,15 @@
import { defineConfig } from 'umi';
import { defineConfig } from '@umijs/max';
const CompressionPlugin = require('compression-webpack-plugin');
export default defineConfig({
hash: true,
layout: false,
antd: {},
outputPath: 'static/dist',
nodeModulesTransform: {
type: 'none',
fastRefresh: true,
favicons: ['/images/favicon.svg'],
mfsu: {
strategy: 'eager',
},
fastRefresh: {},
esbuild: {},
webpack5: {},
dynamicImport: {
loading: '@/components/pageLoading',
},
favicon: '/images/favicon.svg',
proxy: {
'/api/public': {
target: 'http://127.0.0.1:5400/',
@ -26,7 +21,7 @@ export default defineConfig({
ws: true,
},
},
chainWebpack: (config) => {
chainWebpack: ((config: any) => {
config.plugin('compression-webpack-plugin').use(
new CompressionPlugin({
algorithm: 'gzip',
@ -35,13 +30,13 @@ export default defineConfig({
minRatio: 0.6,
}),
);
},
}) as any,
externals: {
react: 'window.React',
'react-dom': 'window.ReactDOM',
},
scripts: [
'https://gw.alipayobjects.com/os/lib/react/16.13.1/umd/react.production.min.js',
'https://gw.alipayobjects.com/os/lib/react-dom/16.13.1/umd/react-dom.production.min.js',
headScripts: [
'https://gw.alipayobjects.com/os/lib/react/18.2.0/umd/react.production.min.js',
'https://gw.alipayobjects.com/os/lib/react-dom/18.2.0/umd/react-dom.production.min.js',
],
});

View File

@ -115,8 +115,11 @@ export default (app: Router) => {
if (!originFilename) {
originFilename = filename;
}
const originFilePath = `${path}${originFilename.replace(/\//g, '')}`;
const filePath = `${path}${filename.replace(/\//g, '')}`;
const originFilePath = join(
path,
`${originFilename.replace(/\//g, '')}`,
);
const filePath = join(path, `${filename.replace(/\//g, '')}`);
if (fs.existsSync(originFilePath)) {
fs.copyFileSync(
originFilePath,

View File

@ -13,7 +13,7 @@ export default async () => {
// 生成内置token
let tokenCommand = `ts-node-transpile-only ${config.rootPath}/back/token.ts`;
const tokenFile = `${config.rootPath}/static/build/token.js`;
const tokenFile = `${config.rootPath}static/build/token.js`;
if (await fileExist(tokenFile)) {
tokenCommand = `node ${tokenFile}`;
}

View File

@ -2,16 +2,16 @@
"private": true,
"scripts": {
"start": "concurrently -n w: npm:start:*",
"start:front": "umi dev",
"start:front": "max dev",
"start:back": "nodemon",
"start:public": "ts-node back/public.ts",
"build:front": "umi build",
"build:front": "max build",
"build:back": "tsc -p tsconfig.back.json",
"panel": "npm run build:back && node static/build/app.js",
"schedule": "npm run build:back && node static/build/schedule.js",
"public": "npm run build:back && node static/build/public.js",
"prettier": "prettier --write '**/*.{js,jsx,tsx,ts,less,md,json}'",
"postinstall": "umi generate tmp 2>/dev/null || true",
"postinstall": "max setup 2>/dev/null || true",
"test": "umi-test",
"test:coverage": "umi-test --coverage"
},
@ -28,9 +28,26 @@
},
"pnpm": {
"peerDependencyRules": {
"ignoreMissing": [
"react",
"react-dom",
"antd",
"dva",
"postcss",
"webpack",
"eslint",
"stylelint",
"redux",
"@babel/core",
"monaco-editor",
"rc-field-form",
"@types/lodash.merge",
"rollup"
],
"allowedVersions": {
"react": "17",
"react-dom": "17"
"react": "18",
"react-dom": "18",
"dva-core": "2"
}
}
},
@ -86,16 +103,14 @@
"@types/node-schedule": "^1.3.2",
"@types/nodemailer": "^6.4.4",
"@types/qrcode.react": "^1.0.2",
"@types/react": "^17.0.39",
"@types/react-dom": "^17.0.13",
"@types/react": "^18.0.20",
"@types/react-dom": "^18.0.6",
"@types/serve-handler": "^6.1.1",
"@types/sockjs": "^0.3.33",
"@types/sockjs-client": "^1.5.1",
"@types/uuid": "^8.3.4",
"@umijs/plugin-antd": "^0.15.0",
"@umijs/plugin-esbuild": "^1.4.1",
"@umijs/max": "^4.0.21",
"@umijs/ssr-darkreader": "^4.9.45",
"@umijs/test": "^3.5.21",
"ansi-to-react": "^6.1.6",
"antd": "^4.20.5",
"antd-img-crop": "^4.2.3",
@ -107,19 +122,19 @@
"prettier": "^2.5.1",
"qiniu": "^7.4.0",
"qrcode.react": "^1.0.1",
"query-string": "^7.1.1",
"rc-tween-one": "^3.0.6",
"react": "17.x",
"react": "18.x",
"react-codemirror2": "^7.2.1",
"react-diff-viewer": "^3.1.1",
"react-dnd": "^14.0.2",
"react-dnd-html5-backend": "^14.0.0",
"react-dom": "17.x",
"react-dom": "18.x",
"react-split-pane": "^0.1.92",
"sockjs-client": "^1.6.0",
"ts-node": "^10.6.0",
"tslib": "^2.4.0",
"typescript": "^4.6.2",
"umi": "^3.5.21",
"umi-request": "^1.4.0",
"vh-check": "^2.0.5",
"webpack": "^5.70.0",

View File

@ -2,7 +2,7 @@ import React, { useEffect, useState, useRef } from 'react';
import ProLayout, { PageLoading } from '@ant-design/pro-layout';
import * as DarkReader from '@umijs/ssr-darkreader';
import defaultProps from './defaultProps';
import { Link, history } from 'umi';
import { Link, history, Outlet, useLocation } from '@umijs/max';
import {
LogoutOutlined,
MenuFoldOutlined,
@ -21,7 +21,18 @@ import SockJS from 'sockjs-client';
import * as Sentry from '@sentry/react';
import { init } from '../utils/init';
export default function (props: any) {
export interface SharedContext {
headerStyle: React.CSSProperties;
isPhone: boolean;
theme: 'vs' | 'vs-dark';
user: any;
reloadUser: (needLoading?: boolean) => void;
reloadTheme: () => void;
socketMessage: any;
}
export default function () {
const location = useLocation();
const ctx = useCtx();
const { theme, reloadTheme } = useTheme();
const [user, setUser] = useState<any>({});
@ -73,7 +84,7 @@ export default function (props: any) {
.then(({ code, data }) => {
if (code === 200 && data.username) {
setUser(data);
if (props.location.pathname === '/') {
if (location.pathname === '/') {
history.push('/crontab');
}
} else {
@ -94,7 +105,7 @@ export default function (props: any) {
if (systemInfo && systemInfo.isInitialized && !user) {
getUser();
}
}, [props.location.pathname]);
}, [location.pathname]);
useEffect(() => {
if (!systemInfo) {
@ -138,7 +149,9 @@ export default function (props: any) {
useEffect(() => {
if (!user || !user.username) return;
ws.current = new SockJS(
`${location.origin}/api/ws?token=${localStorage.getItem(config.authKey)}`,
`${window.location.origin}/api/ws?token=${localStorage.getItem(
config.authKey,
)}`,
);
ws.current.onmessage = (e: any) => {
@ -183,30 +196,27 @@ export default function (props: any) {
};
}, []);
if (
['/login', '/initialization', '/error'].includes(props.location.pathname)
) {
if (['/login', '/initialization', '/error'].includes(location.pathname)) {
document.title = `${
(config.documentTitleMap as any)[props.location.pathname]
(config.documentTitleMap as any)[location.pathname]
} - `;
if (
systemInfo?.isInitialized &&
props.location.pathname === '/initialization'
) {
if (systemInfo?.isInitialized && location.pathname === '/initialization') {
history.push('/crontab');
}
if (systemInfo || props.location.pathname === '/error') {
return React.Children.map(props.children, (child) => {
return React.cloneElement(child, {
...ctx,
theme,
user,
reloadUser,
reloadTheme,
ws: ws.current,
});
});
if (systemInfo || location.pathname === '/error') {
return (
<Outlet
context={{
...ctx,
theme,
user,
reloadUser,
reloadTheme,
ws: ws.current,
}}
/>
);
}
}
@ -227,7 +237,7 @@ export default function (props: any) {
<PageLoading />
) : (
<ProLayout
selectedKeys={[props.location.pathname]}
selectedKeys={[location.pathname]}
loading={loading}
ErrorBoundary={Sentry.ErrorBoundary}
logo={<Image preview={false} src="http://qn.whyour.cn/logo.png" />}
@ -320,16 +330,16 @@ export default function (props: any) {
)}
{...defaultProps}
>
{React.Children.map(props.children, (child) => {
return React.cloneElement(child, {
<Outlet
context={{
...ctx,
theme,
user,
reloadUser,
reloadTheme,
socketMessage,
});
})}
}}
/>
</ProLayout>
);
}

View File

@ -11,8 +11,11 @@ import { PageContainer } from '@ant-design/pro-layout';
import { request } from '@/utils/http';
import Editor from '@monaco-editor/react';
import { Controlled as CodeMirror } from 'react-codemirror2';
import { useOutletContext } from '@umijs/max';
import { SharedContext } from '@/layouts';
const Config = ({ headerStyle, isPhone, theme }: any) => {
const Config = () => {
const { headerStyle, isPhone, theme } = useOutletContext<SharedContext>();
const [value, setValue] = useState('');
const [loading, setLoading] = useState(true);
const [title, setTitle] = useState('config.sh');

View File

@ -44,12 +44,13 @@ import CronDetailModal from './detail';
import cron_parser from 'cron-parser';
import { diffTime } from '@/utils/date';
import { getTableScroll } from '@/utils/index';
import { history } from 'umi';
import { history, useOutletContext } from '@umijs/max';
import './index.less';
import ViewCreateModal from './viewCreateModal';
import ViewManageModal from './viewManageModal';
import pagination from 'antd/lib/pagination';
import { FilterValue, SorterResult } from 'antd/lib/table/interface';
import { SharedContext } from '@/layouts';
const { Text, Paragraph } = Typography;
const { Search } = Input;
@ -81,7 +82,8 @@ enum OperationPath {
'unpin',
}
const Crontab = ({ headerStyle, isPhone, theme }: any) => {
const Crontab = () => {
const { headerStyle, isPhone, theme } = useOutletContext<SharedContext>();
const columns: any = [
{
title: '名称',
@ -199,10 +201,10 @@ const Crontab = ({ headerStyle, isPhone, theme }: any) => {
>
{record.last_execution_time
? new Date(record.last_execution_time * 1000)
.toLocaleString(language, {
hour12: false,
})
.replace(' 24:', ' 00:')
.toLocaleString(language, {
hour12: false,
})
.replace(' 24:', ' 00:')
: '-'}
</span>
);
@ -411,9 +413,16 @@ const Crontab = ({ headerStyle, isPhone, theme }: any) => {
const getCrons = () => {
setLoading(true);
const { page, size, sorter, filters } = pageConf;
let url = `${config.apiPrefix}crons?searchValue=${searchText}&page=${page}&size=${size}&filters=${JSON.stringify(filters)}`;
let url = `${
config.apiPrefix
}crons?searchValue=${searchText}&page=${page}&size=${size}&filters=${JSON.stringify(
filters,
)}`;
if (sorter && sorter.field) {
url += `&sorter=${JSON.stringify({ field: sorter.field, type: sorter.order === 'ascend' ? 'ASC' : 'DESC' })}`;
url += `&sorter=${JSON.stringify({
field: sorter.field,
type: sorter.order === 'ascend' ? 'ASC' : 'DESC',
})}`;
}
if (viewConf) {
url += `&queryString=${JSON.stringify({
@ -577,7 +586,8 @@ const Crontab = ({ headerStyle, isPhone, theme }: any) => {
onOk() {
request
.put(
`${config.apiPrefix}crons/${record.isDisabled === 1 ? 'enable' : 'disable'
`${config.apiPrefix}crons/${
record.isDisabled === 1 ? 'enable' : 'disable'
}`,
{
data: [record.id],
@ -622,7 +632,8 @@ const Crontab = ({ headerStyle, isPhone, theme }: any) => {
onOk() {
request
.put(
`${config.apiPrefix}crons/${record.isPinned === 1 ? 'unpin' : 'pin'
`${config.apiPrefix}crons/${
record.isPinned === 1 ? 'unpin' : 'pin'
}`,
{
data: [record.id],
@ -828,7 +839,12 @@ const Crontab = ({ headerStyle, isPhone, theme }: any) => {
sorter: SorterResult<any> | SorterResult<any>[],
) => {
const { current, pageSize } = pagination;
setPageConf({ page: current as number, size: pageSize as number, sorter, filters });
setPageConf({
page: current as number,
size: pageSize as number,
sorter,
filters,
});
localStorage.setItem('pageSize', String(pageSize));
};
@ -865,7 +881,7 @@ const Crontab = ({ headerStyle, isPhone, theme }: any) => {
page: 1,
size: parseInt(localStorage.getItem('pageSize') || '20'),
sorter: {},
filters: {}
filters: {},
});
setTimeout(() => {
setTableScrollHeight(getTableScroll());

View File

@ -29,6 +29,8 @@ import { HTML5Backend } from 'react-dnd-html5-backend';
import './index.less';
import { getTableScroll } from '@/utils/index';
import DependenceLogModal from './logModal';
import { useOutletContext } from '@umijs/max';
import { SharedContext } from '@/layouts';
const { Text } = Typography;
const { Search } = Input;
@ -48,7 +50,9 @@ enum StatusColor {
'error',
}
const Dependence = ({ headerStyle, isPhone, socketMessage }: any) => {
const Dependence = () => {
const { headerStyle, isPhone, socketMessage } =
useOutletContext<SharedContext>();
const columns: any = [
{
title: '序号',

View File

@ -101,7 +101,9 @@ const DependenceModal = ({
>
<Select>
{config.dependenceTypes.map((x, i) => (
<Option value={i}>{x}</Option>
<Option key={i} value={i}>
{x}
</Option>
))}
</Select>
</Form.Item>

View File

@ -17,4 +17,8 @@
.ant-form-item {
margin-bottom: 8px;
}
+ section {
height: calc(100% - 40px) !important;
}
}

View File

@ -6,10 +6,13 @@ import { request } from '@/utils/http';
import './index.less';
import { DiffEditor } from '@monaco-editor/react';
import ReactDiffViewer from 'react-diff-viewer';
import { useOutletContext } from '@umijs/max';
import { SharedContext } from '@/layouts';
const { Option } = Select;
const Diff = ({ headerStyle, isPhone, theme }: any) => {
const Diff = () => {
const { headerStyle, isPhone, theme } = useOutletContext<SharedContext>();
const [origin, setOrigin] = useState('config.sample.sh');
const [current, setCurrent] = useState('config.sh');
const [originValue, setOriginValue] = useState('');
@ -95,7 +98,9 @@ const Diff = ({ headerStyle, isPhone, theme }: any) => {
<Form.Item label="源文件">
<Select value={origin} onChange={originFileChange}>
{files.map((x) => (
<Option value={x.value}>{x.title}</Option>
<Option key={x.value} value={x.value}>
{x.title}
</Option>
))}
</Select>
</Form.Item>
@ -104,7 +109,9 @@ const Diff = ({ headerStyle, isPhone, theme }: any) => {
<Form.Item label="当前文件">
<Select value={current} onChange={currentFileChange}>
{files.map((x) => (
<Option value={x.value}>{x.title}</Option>
<Option key={x.value} value={x.value}>
{x.title}
</Option>
))}
</Select>
</Form.Item>

View File

@ -26,6 +26,8 @@ import { DndProvider, useDrag, useDrop } from 'react-dnd';
import { HTML5Backend } from 'react-dnd-html5-backend';
import './index.less';
import { exportJson, getTableScroll } from '@/utils/index';
import { useOutletContext } from '@umijs/max';
import { SharedContext } from '@/layouts';
const { Text, Paragraph } = Typography;
const { Search } = Input;
@ -96,7 +98,8 @@ const DragableBodyRow = ({
);
};
const Env = ({ headerStyle, isPhone, theme }: any) => {
const Env = () => {
const { headerStyle, isPhone, theme } = useOutletContext<SharedContext>();
const columns: any = [
{
title: '序号',

View File

@ -3,11 +3,13 @@ import config from '@/utils/config';
import { request } from '@/utils/http';
import Terminal, { ColorMode, LineType } from '../../components/terminal';
import { PageLoading } from '@ant-design/pro-layout';
import { history } from 'umi';
import { history, useOutletContext } from '@umijs/max';
import Ansi from 'ansi-to-react';
import './index.less';
import { SharedContext } from '@/layouts';
const Error = ({ user, theme }: any) => {
const Error = () => {
const { user, theme } = useOutletContext<SharedContext>();
const [loading, setLoading] = useState(false);
const [data, setData] = useState('暂无日志');

View File

@ -10,7 +10,7 @@ import {
Select,
} from 'antd';
import config from '@/utils/config';
import { history } from 'umi';
import { history } from '@umijs/max';
import styles from './index.less';
import { request } from '@/utils/http';
@ -116,7 +116,9 @@ const Initialization = () => {
{config.notificationModes
.filter((x) => x.value !== 'closed')
.map((x) => (
<Option value={x.value}>{x.label}</Option>
<Option key={x.value} value={x.value}>
{x.label}
</Option>
))}
</Select>
</Form.Item>

View File

@ -6,8 +6,7 @@
overflow: hidden;
position: relative;
background-color: @component-background;
height: calc(100vh - 128px);
height: calc(100vh - var(--vh-offset, 0px) - 128px);
height: 100%;
display: flex;
flex-direction: column;
}

View File

@ -7,6 +7,8 @@ import { request } from '@/utils/http';
import styles from './index.module.less';
import { Controlled as CodeMirror } from 'react-codemirror2';
import SplitPane from 'react-split-pane';
import { useOutletContext } from '@umijs/max';
import { SharedContext } from '@/layouts';
function getFilterData(keyword: string, data: any) {
const expandedKeys: string[] = [];
@ -36,7 +38,8 @@ function getFilterData(keyword: string, data: any) {
return { tree: data, expandedKeys };
}
const Log = ({ headerStyle, isPhone, theme }: any) => {
const Log = () => {
const { headerStyle, isPhone, theme } = useOutletContext<SharedContext>();
const [title, setTitle] = useState('请选择日志文件');
const [value, setValue] = useState('请选择日志文件');
const [select, setSelect] = useState<any>();

View File

@ -9,16 +9,18 @@ import {
Statistic,
} from 'antd';
import config from '@/utils/config';
import { history, Link } from 'umi';
import { history, useOutletContext } from '@umijs/max';
import styles from './index.less';
import { request } from '@/utils/http';
import { useTheme } from '@/utils/hooks';
import { MobileOutlined } from '@ant-design/icons';
import { SharedContext } from '@/layouts';
const FormItem = Form.Item;
const { Countdown } = Statistic;
const Login = ({ reloadUser }: any) => {
const Login = () => {
const { reloadUser } = useOutletContext<SharedContext>();
const [loading, setLoading] = useState(false);
const [waitTime, setWaitTime] = useState<any>();
const { theme } = useTheme();

View File

@ -36,7 +36,7 @@ const EditScriptNameModal = ({
const handleOk = async (values: any) => {
setLoading(true);
const { path = '', filename: inputFilename, directory } = values;
const { path = '', filename: inputFilename, directory = '' } = values;
const formData = new FormData();
formData.append('file', file as any);
formData.append('filename', inputFilename);
@ -50,11 +50,11 @@ const EditScriptNameModal = ({
.then(({ code, data }) => {
if (code === 200) {
message.success(directory ? '新建文件夹成功' : '新建文件成功');
const key = path ? `${values.path}/` : '';
const key = path ? `${path}/` : '';
const filename = file ? file.name : inputFilename;
handleCancel({
filename,
path: values.path,
path,
key: `${key}${filename}`,
});
} else {

View File

@ -6,8 +6,7 @@
overflow: hidden;
position: relative;
background-color: @component-background;
height: calc(100vh - 128px);
height: calc(100vh - var(--vh-offset, 0px) - 128px);
height: 100%;
display: flex;
flex-direction: column;
}

View File

@ -33,7 +33,10 @@ import {
} from '@ant-design/icons';
import EditScriptNameModal from './editNameModal';
import debounce from 'lodash/debounce';
import { history } from 'umi';
import { history, useOutletContext, useLocation } from '@umijs/max';
import { parse } from 'query-string';
import { depthFirstSearch } from '@/utils';
import { SharedContext } from '@/layouts';
const { Text } = Typography;
@ -72,7 +75,9 @@ const LangMap: any = {
'.ts': 'typescript',
};
const Script = ({ headerStyle, isPhone, theme, socketMessage }: any) => {
const Script = () => {
const { headerStyle, isPhone, theme, socketMessage } =
useOutletContext<SharedContext>();
const [title, setTitle] = useState('请选择脚本文件');
const [value, setValue] = useState('请选择脚本文件');
const [select, setSelect] = useState<any>();
@ -111,7 +116,7 @@ const Script = ({ headerStyle, isPhone, theme, socketMessage }: any) => {
};
const initGetScript = () => {
const { p, s } = history.location.query as any;
const { p, s } = parse(history.location.search);
if (s) {
const vkey = `${p}/${s}`;
const obj = {
@ -313,20 +318,17 @@ const Script = ({ headerStyle, isPhone, theme, socketMessage }: any) => {
},
) => {
if (filename) {
const newData = [...data];
let newData = [...data];
const _file = { title: filename, key, parent: path };
if (path) {
// TODO: 更新左侧树数据
const parentNodeIndex = newData.findIndex((x) => x.key === path);
if (parentNodeIndex !== -1) {
const parentNode = newData[parentNodeIndex];
if (parentNode.children && parentNode.children.length > 0) {
parentNode.children.unshift(_file);
} else {
parentNode.children = [_file];
}
newData.splice(parentNodeIndex, 1, { ...parentNode });
}
newData = depthFirstSearch(newData, (c) => c.key === path, _file);
const keys = path.split('/');
const sKeys: string[] = [];
keys.reduce((p, c) => {
sKeys.push(p);
return `${p}/${c}`;
});
setExpandedKeys([...expandedKeys, ...sKeys, path]);
} else {
newData.unshift(_file);
}
@ -360,10 +362,6 @@ const Script = ({ headerStyle, isPhone, theme, socketMessage }: any) => {
const word = searchValue || '';
const { tree } = getFilterData(word.toLocaleLowerCase(), data);
setFilterData(tree);
setSelect('');
setCurrentNode(null);
setTitle('请选择脚本文件');
setValue('请选择脚本文件');
}, [data]);
useEffect(() => {

View File

@ -91,7 +91,11 @@ const AppModal = ({
style={{ width: '100%' }}
>
{config.scopes.map((x) => {
return <Select.Option value={x.value}>{x.name}</Select.Option>;
return (
<Select.Option key={x.value} value={x.value}>
{x.name}
</Select.Option>
);
})}
</Select>
</Form.Item>

View File

@ -29,6 +29,8 @@ import LoginLog from './loginLog';
import NotificationSetting from './notification';
import CheckUpdate from './checkUpdate';
import About from './about';
import { useOutletContext } from '@umijs/max';
import { SharedContext } from '@/layouts';
const { Text } = Typography;
const optionsWithDisabled = [
@ -37,14 +39,9 @@ const optionsWithDisabled = [
{ label: '跟随系统', value: 'auto' },
];
const Setting = ({
headerStyle,
isPhone,
user,
reloadUser,
reloadTheme,
socketMessage,
}: any) => {
const Setting = () => {
const { headerStyle, isPhone, user, reloadUser, reloadTheme, socketMessage } =
useOutletContext<SharedContext>();
const columns = [
{
title: '名称',

View File

@ -64,6 +64,7 @@ const NotificationSetting = ({ data }: any) => {
</Form.Item>
{fields.map((x) => (
<Form.Item
key={x.label}
label={x.label}
name={x.label}
extra={x.tip}

View File

@ -2,7 +2,7 @@ import React, { useEffect, useState } from 'react';
import { Typography, Input, Form, Button, message, Avatar, Upload } from 'antd';
import { request } from '@/utils/http';
import config from '@/utils/config';
import { history } from 'umi';
import { history } from '@umijs/max';
import QRCode from 'qrcode.react';
import { PageLoading } from '@ant-design/pro-layout';
import { UploadOutlined, UserOutlined } from '@ant-design/icons';

View File

@ -30,9 +30,10 @@ import { PageContainer } from '@ant-design/pro-layout';
import { request } from '@/utils/http';
import SubscriptionModal from './modal';
import { getTableScroll } from '@/utils/index';
import { history } from 'umi';
import { history, useOutletContext } from '@umijs/max';
import './index.less';
import SubscriptionLogModal from './logModal';
import { SharedContext } from '@/layouts';
const { Text, Paragraph } = Typography;
const { Search } = Input;
@ -57,7 +58,10 @@ export enum SubscriptionType {
'file' = '单文件',
}
const Subscription = ({ headerStyle, isPhone, socketMessage }: any) => {
const Subscription = () => {
const { headerStyle, isPhone, socketMessage } =
useOutletContext<SharedContext>();
const columns: any = [
{
title: '名称',

View File

@ -1,7 +1,7 @@
import { extend } from 'umi-request';
import { message } from 'antd';
import config from './config';
import { history } from 'umi';
import { history } from '@umijs/max';
message.config({
duration: 1.5,

View File

@ -241,3 +241,31 @@ export function exportJson(name: string, data: string) {
createA.download = name;
automaticClick(createA);
}
export function depthFirstSearch<
T extends Record<string, any> & { children?: T[] },
>(children: T[], condition: (column: T) => boolean, item: T) {
const c = [...children];
const keys = [];
(function find(cls: T[] | undefined) {
if (!cls) return;
for (let i = 0; i < cls?.length; i++) {
if (condition(cls[i])) {
if (cls[i].children) {
cls[i].children!.unshift(item);
} else {
cls[i].children = [item];
}
return;
}
if (cls[i].children) {
keys.push(cls[i].key);
find(cls[i].children);
}
}
})(c);
console.log(keys);
return c;
}