This commit is contained in:
李志强 2023-12-28 17:49:32 +08:00
parent 284713ed44
commit 1cedf4a53e
15 changed files with 54694 additions and 6592 deletions

3
.gitignore vendored Normal file
View File

@ -0,0 +1,3 @@
node_modules
package-lock.json
src/.umi

View File

@ -1,3 +1,12 @@
/*
* @version: V1.0.0
* @Date: 2023-12-28 11:28:34
* @LastEditors: lzq
* @LastEditTime: 2023-12-28 13:31:22
* @company:
* @FilePath: \salpa-web\config\proxy.ts
* @Descripttion:
*/
/** /**
* *
* ------------------------------- * -------------------------------
@ -10,7 +19,7 @@ export default {
dev: { dev: {
'/api/': { '/api/': {
// target: 'http://192.168.103.172:8080', // target: 'http://192.168.103.172:8080',
target: 'http://192.168.2.58:8080', target: 'http://192.168.1.217:9080',
changeOrigin: true, changeOrigin: true,
pathRewrite: { '^/api': '' }, pathRewrite: { '^/api': '' },
}, },
@ -19,7 +28,7 @@ export default {
target: 'http://192.168.113.251:8080', target: 'http://192.168.113.251:8080',
changeOrigin: true, changeOrigin: true,
} },
}, },
test: { test: {
'/api/': { '/api/': {

57256
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -5,75 +5,87 @@
* *
* */ * */
import RightContent from '@/components/RightContent' import type { Settings as LayoutSettings } from '@ant-design/pro-layout';
import type { Settings as LayoutSettings } from '@ant-design/pro-layout' import { PageLoading } from '@ant-design/pro-layout';
import { PageLoading } from '@ant-design/pro-layout' import type { RunTimeLayoutConfig } from 'umi';
import type { RunTimeLayoutConfig } from 'umi' import { history, Link } from 'umi';
import { history, Link } from 'umi'
// import Footer from '@/components/Footer'; // import Footer from '@/components/Footer';
import { BookOutlined, LinkOutlined } from '@ant-design/icons' import { BookOutlined, LinkOutlined } from '@ant-design/icons';
import defaultSettings from '../config/defaultSettings' import defaultSettings from '../config/defaultSettings';
import iconStyle from '../public/樽海鞘_图案.svg' import iconStyle from '../public/樽海鞘_图案.svg';
import { getRoutersInfo, getUserInfo } from './services/session' import { getRoutersInfo, getUserInfo } from './services/session';
const isDev = process.env.NODE_ENV === 'development' const isDev = process.env.NODE_ENV === 'development';
const loginPath = '/user/login' const loginPath = '/user/login';
/** 获取用户信息比较慢的时候会展示一个 loading */ /** 获取用户信息比较慢的时候会展示一个 loading */
export const initialStateConfig = { export const initialStateConfig = {
loading: <PageLoading /> loading: <PageLoading />,
} };
/** /**
* @see https://umijs.org/zh-CN/plugins/plugin-initial-state * @see https://umijs.org/zh-CN/plugins/plugin-initial-state
* */ * */
export async function getInitialState(): Promise<{ export async function getInitialState(): Promise<{
settings?: Partial<LayoutSettings> settings?: Partial<LayoutSettings>;
currentUser?: API.CurrentUser currentUser?: API.CurrentUser;
loading?: boolean loading?: boolean;
fetchUserInfo?: () => Promise<API.CurrentUser | undefined> fetchUserInfo?: () => Promise<API.CurrentUser | undefined>;
}> { }> {
const fetchUserInfo = async () => { const fetchUserInfo = async () => {
try { try {
console.log(1111111111111111111111111111111) const resp = await getUserInfo();
const resp = await getUserInfo()
if (resp === undefined || resp.code !== 200) { if (resp === undefined || resp.code !== 200) {
history.push(loginPath) history.push(loginPath);
// const response = await login({
// username: 'admin',
// password: 'admin123',
// });
// if (response.code === 200) {
// const current = new Date();
// const expireTime = current.setTime(current.getTime() + 1000 * 12 * 60 * 60);
// setSessionToken(response.token, response.token, expireTime);
// fetchUserInfo()
// }
} else { } else {
return { ...resp.user, permissions: resp.permissions } as API.CurrentUser return { ...resp.user, permissions: resp.permissions } as API.CurrentUser;
} }
} catch (error) { } catch (error) {
history.push(loginPath) console.log(error);
history.push(loginPath);
} }
return undefined return undefined;
} };
// 如果是登录页面,不执行 // 如果是登录页面,不执行
if (history.location.pathname !== loginPath) { if (history.location.pathname !== loginPath) {
console.log(222222222222222222222222222222) console.log('chufal');
const currentUser = await fetchUserInfo()
const currentUser = await fetchUserInfo();
return { return {
settings: defaultSettings, settings: defaultSettings,
currentUser, currentUser,
fetchUserInfo fetchUserInfo,
} };
} }
return { return {
fetchUserInfo, fetchUserInfo,
settings: defaultSettings settings: defaultSettings,
} };
} }
// ProLayout 支持的api https://procomponents.ant.design/components/layout // ProLayout 支持的api https://procomponents.ant.design/components/layout
export const layout: RunTimeLayoutConfig = ({ initialState, setInitialState }) => { export const layout: RunTimeLayoutConfig = ({ initialState, setInitialState }) => {
return { return {
rightContentRender: () => <RightContent />, rightContentRender: () => <div />,
waterMarkProps: { waterMarkProps: {
content: initialState?.currentUser?.userName content: initialState?.currentUser?.userName,
}, },
// footerRender: () => <Footer />, // footerRender: () => <Footer />,
onPageChange: () => { onPageChange: () => {
const { location } = history const { location } = history;
// 如果没有登录,重定向到 login // 如果没有登录,重定向到 login
if (!initialState?.currentUser && location.pathname !== loginPath) { if (!initialState?.currentUser && location.pathname !== loginPath) {
history.push(loginPath) history.push(loginPath);
} }
}, },
links: isDev links: isDev
@ -85,27 +97,37 @@ export const layout: RunTimeLayoutConfig = ({ initialState, setInitialState }) =
<Link key="docs" to="/~docs"> <Link key="docs" to="/~docs">
<BookOutlined /> <BookOutlined />
<span></span> <span></span>
</Link> </Link>,
] ]
: [], : [],
menuHeaderRender: undefined, menuHeaderRender: false,
menu: { menu: {
// 每当 initialState?.currentUser?.userid 发生修改时重新执行 request // 每当 initialState?.currentUser?.userid 发生修改时重新执行 request
params: { params: {
userId: initialState?.currentUser?.userId userId: initialState?.currentUser?.userId,
}, },
request: async () => { request: async () => {
if (!initialState?.currentUser?.userId) { if (!initialState?.currentUser?.userId) {
return [] return [];
} }
// initialState.currentUser 中包含了所有用户信息 // initialState.currentUser 中包含了所有用户信息
const menus = await getRoutersInfo() let menus = await getRoutersInfo();
const urlParams = new URLSearchParams(window.location.search);
const hideInMenu = urlParams.get('hideInMenu'); // 替换 'hideInMenu' 为你要获取的参数名
if (hideInMenu) {
menus = menus.map((item) => {
return {
...item,
hideInMenu: true,
};
});
}
setInitialState((preInitialState: any) => ({ setInitialState((preInitialState: any) => ({
...preInitialState, ...preInitialState,
menus menus,
})) }));
return menus return menus;
} },
}, },
// 自定义 403 页面 // 自定义 403 页面
// unAccessible: <div>unAccessible</div>, // unAccessible: <div>unAccessible</div>,
@ -116,9 +138,9 @@ export const layout: RunTimeLayoutConfig = ({ initialState, setInitialState }) =
{children} {children}
{!props.location?.pathname?.includes('/login')} {!props.location?.pathname?.includes('/login')}
</div> </div>
) );
}, },
...initialState?.settings, ...initialState?.settings,
logo: <img src={iconStyle} /> logo: <img src={iconStyle} />,
} };
} };

View File

@ -5,111 +5,119 @@
* @Last Modified time: 2022-02-21 11:12:12 * @Last Modified time: 2022-02-21 11:12:12
*/ */
import { SortableElement } from 'react-sortable-hoc';
import { CloseOutlined } from '@ant-design/icons'; import { CloseOutlined } from '@ant-design/icons';
import { useModel, useHistory, useAliveController } from 'umi' import { SortableElement } from 'react-sortable-hoc';
import { useAliveController, useHistory, useModel } from 'umi';
import styles from './index.less'; import styles from './index.less';
interface ITab { interface ITab {
value: { value: {
hash: string hash: string;
key: string key: string;
title: string title: string;
pathname: string pathname: string;
query: Record<string, any> query: Record<string, any>;
search: string search: string;
state: any state: any;
keepAliveName: string keepAliveName: string;
} };
// index: number // index: number
tabIndex: number tabIndex: number;
} }
const SortableTab = (props: ITab) => { const SortableTab = (props: ITab) => {
const history = useHistory() const history = useHistory();
const { value, tabIndex } = props; const { value, tabIndex } = props;
const { active, dispatch, tabWidth, tarnslateX, showTabs, tabsWidth, tabList } = useModel("system"); const { active, dispatch, tabWidth, tarnslateX, showTabs, tabsWidth, tabList } =
const { dropScope } = useAliveController() useModel('system');
const closable = tabList.length > 1 const { dropScope } = useAliveController();
return ( const closable = tabList.length > 1;
const urlParams = new URLSearchParams(window.location.search);
const hideInMenu = urlParams.get('hideInMenu'); // 替换 'hideInMenu' 为你要获取的参数名
return !hideInMenu ? (
<div
className={`${styles.tabItem} link-tab ${tabIndex === active ? styles.active : ''}`}
title={value.title}
onClick={() => {
// translate了 多少个
const tarnsNumber = Math.floor(tarnslateX / tabWidth);
// 隐藏了多少
const isBeyondDistance = tarnslateX - tarnsNumber * tabWidth;
if (tabIndex - tarnsNumber <= 0) {
dispatch({
type: 'CHANGESTATE',
payload: { active: tabIndex, tarnslateX: tarnslateX - isBeyondDistance },
});
history.push({ ...value });
return;
}
// 是否在可视区域内
if (tabIndex - tarnsNumber + 1 === showTabs) {
// 需要移动的距离计算
const x = (tabIndex + 1) * (tabWidth as number) - (tabsWidth - 100);
dispatch({ type: 'CHANGESTATE', payload: { active: tabIndex, tarnslateX: x } });
history.push({ ...value });
return;
}
dispatch({ type: 'CHANGESTATE', payload: { active: tabIndex } });
history.push({ ...value });
}}
>
{value.title}
{closable && (
<div <div
className={`${styles.tabItem} link-tab ${tabIndex === active ? styles.active : ''}`} className={styles.closeIcon}
title={value.title} onClick={(e) => {
onClick={() => { e.stopPropagation();
// translate了 多少个 const currentName = value.keepAliveName;
const tarnsNumber = Math.floor(tarnslateX / tabWidth) // 如果关闭激活中的 KeepAlive Tab需要先离开当前路由
// 隐藏了多少 // 触发 KeepAlive unactivated 后再进行 drop
const isBeyondDistance = tarnslateX - tarnsNumber * tabWidth; const localTablist = JSON.parse(JSON.stringify(tabList));
if (tabIndex - tarnsNumber <= 0) { localTablist.splice(tabIndex, 1);
dispatch({ type: 'CHANGESTATE', payload: { active: tabIndex, tarnslateX: tarnslateX - isBeyondDistance } }) let activeIndex: number = 0;
history.push({ ...value }) if (tabIndex < active) {
return; // 我在前面
} activeIndex = active - 1;
// 是否在可视区域内 dropScope(currentName);
if ((tabIndex - tarnsNumber + 1) === showTabs) { } else if (tabIndex === active) {
// 需要移动的距离计算 // 点击后面当前窗口
const x = (tabIndex + 1) * (tabWidth as number) - (tabsWidth - 100) if (active > 0) {
dispatch({ type: 'CHANGESTATE', payload: { active: tabIndex, tarnslateX: x } }) activeIndex = active - 1;
history.push({ ...value }) const timer = setTimeout(() => {
return; clearTimeout(timer);
} history.push(tabList[activeIndex]);
dispatch({ type: 'CHANGESTATE', payload: { active: tabIndex } }) }, 10);
history.push({ ...value }) } else {
activeIndex = 0;
}}> const timer = setTimeout(() => {
{value.title} clearTimeout(timer);
{ history.push(localTablist[activeIndex]);
closable && ( }, 10);
<div }
className={styles.closeIcon} const unlisten = history.listen(() => {
onClick={(e) => { unlisten();
e.stopPropagation() const dropTimer = setTimeout(() => {
const currentName = value.keepAliveName clearTimeout(dropTimer);
// 如果关闭激活中的 KeepAlive Tab需要先离开当前路由 dropScope(currentName);
// 触发 KeepAlive unactivated 后再进行 drop }, 10);
const localTablist = JSON.parse(JSON.stringify(tabList)) });
localTablist.splice(tabIndex, 1) } else {
let activeIndex: number = 0; activeIndex = active;
if (tabIndex < active) { dropScope(currentName);
// 我在前面
activeIndex = active - 1;
dropScope(currentName)
} else if (tabIndex === active) {
// 点击后面当前窗口
if (active > 0) {
activeIndex = active - 1
const timer = setTimeout(() => {
clearTimeout(timer)
history.push(tabList[activeIndex])
}, 10)
} else {
activeIndex = 0
const timer = setTimeout(() => {
clearTimeout(timer)
history.push(localTablist[activeIndex])
}, 10)
}
const unlisten = history.listen(() => {
unlisten()
const dropTimer = setTimeout(() => {
clearTimeout(dropTimer)
dropScope(currentName)
}, 10)
})
} else {
activeIndex = active
dropScope(currentName)
}
dispatch({ type: "CHANGESTATE", payload: { tabList: localTablist, active: activeIndex, tarnslateX: 0 } })
}}>
<CloseOutlined />
</div>
)
} }
dispatch({
type: 'CHANGESTATE',
payload: { tabList: localTablist, active: activeIndex, tarnslateX: 0 },
});
}}
>
<CloseOutlined />
</div> </div>
); )}
} </div>
) : (
<div />
);
};
export default SortableElement(SortableTab); export default SortableElement(SortableTab);

View File

@ -5,238 +5,246 @@
* @Last Modified time: 2022-02-21 11:12:12 * @Last Modified time: 2022-02-21 11:12:12
*/ */
import { DownOutlined, MoreOutlined } from '@ant-design/icons';
import { Divider, Dropdown, Menu } from 'antd';
import { useEffect, useRef } from 'react'; import { useEffect, useRef } from 'react';
import { SortableContainer } from 'react-sortable-hoc'; import { SortableContainer } from 'react-sortable-hoc';
import { Dropdown, Menu, Divider } from 'antd'; import { history, useAliveController, useModel } from 'umi';
import { DownOutlined, MoreOutlined } from '@ant-design/icons'; import SortableTab from './components/SortableTab';
import { useModel, history, useAliveController } from 'umi'
import SortableTab from './components/SortableTab'
import styles from './index.less'; import styles from './index.less';
const SortableList = SortableContainer(() => { const SortableList = SortableContainer(() => {
const tabsRef = useRef<any>() const tabsRef = useRef<any>();
const { tabList, tarnslateX } = useModel("system"); const { tabList, tarnslateX } = useModel('system');
return ( return (
<div className={`${styles.tabList}`} ref={tabsRef} > <div className={`${styles.tabList}`} ref={tabsRef}>
{/* <div className={styles.linkTabs} style={{ transform: `translateX(-${tarnslateX}px)` }}> */} {/* <div className={styles.linkTabs} style={{ transform: `translateX(-${tarnslateX}px)` }}> */}
<div className={styles.linkTabs} style={{ transform: `translateX(-${tarnslateX}px)` }}> <div className={styles.linkTabs} style={{ transform: `translateX(-${tarnslateX}px)` }}>
{ {tabList.map((value, index: number) => (
tabList.map((value, index: number) => ( <SortableTab key={`item-${index}`} index={index} value={value} tabIndex={index} />
<SortableTab key={`item-${index}`} index={index} value={value} tabIndex={index} /> ))}
)) </div>
} </div>
</div> );
</div >
);
}); });
const KeepAliveTabs = () => { const KeepAliveTabs = () => {
const { initialState } = useModel<any>("@@initialState"); const { initialState } = useModel<any>('@@initialState');
const { collapsed } = initialState; const { collapsed } = initialState;
const { tabList, dispatch, active, showTabs, tabsWidth, tabWidth, tarnslateX } = useModel("system"); const { tabList, dispatch, active, showTabs, tabsWidth, tabWidth, tarnslateX } =
const { dropScope, clear } = useAliveController(); useModel('system');
const { dropScope, clear } = useAliveController();
const onSortEnd = ({ oldIndex, newIndex }: { oldIndex: number, newIndex: number }) => { const onSortEnd = ({ oldIndex, newIndex }: { oldIndex: number; newIndex: number }) => {
const dataSource = JSON.parse(JSON.stringify(tabList)) const dataSource = JSON.parse(JSON.stringify(tabList));
const activeItem = dataSource[active as number] const activeItem = dataSource[active as number];
dataSource.splice(newIndex, 0, dataSource.splice(oldIndex, 1)[0]); dataSource.splice(newIndex, 0, dataSource.splice(oldIndex, 1)[0]);
const movedActiveIndex = dataSource.findIndex((item: string) => item === activeItem) const movedActiveIndex = dataSource.findIndex((item: string) => item === activeItem);
dispatch({ type: "CHANGESTATE", payload: { tabList: dataSource, active: movedActiveIndex } }) dispatch({ type: 'CHANGESTATE', payload: { tabList: dataSource, active: movedActiveIndex } });
} };
// 当前的索引值active showTabs一排能展示多少个
// 计算应该在菜单展示的菜单有哪些
// 当前的索引值active showTabs一排能展示多少个 const arr: any[] = [];
// 计算应该在菜单展示的菜单有哪些 if (tabList.length > showTabs) {
// 前面隐藏的元素
const beforeTab = Math.floor(tarnslateX / tabWidth);
// 后面隐藏的元素
const afterTab = Math.floor(tarnslateX / tabWidth) + showTabs;
tabList.forEach((item, index) => {
if (index < beforeTab) {
arr.push(item);
}
if (index >= afterTab) {
arr.push(item);
}
});
}
const arr: any[] = [] const menuMore = (
if (tabList.length > showTabs) { <Menu
// 前面隐藏的元素 onClick={(e) => {
const beforeTab = Math.floor(tarnslateX / tabWidth); // 判断点击多余tab的展示移动距离是多少
// 后面隐藏的元素 // 计算超出了多少
const afterTab = Math.floor(tarnslateX / tabWidth) + showTabs // tabsWidth tabWidth showTabs 100是右边操作的距离目前写死
tabList.forEach((item, index) => { const isBeyondDistance =
if (index < beforeTab) { (showTabs as number) * (tabWidth as number) - (tabsWidth as number) + 100;
arr.push(item) // TODO 找到当前点击的索引值
} const curClickIndex = tabList?.findIndex((item) => item.pathname === e.key) as number;
if (index >= afterTab) { // 能展示多少个
arr.push(item) const totalShowIndex = (showTabs as number) - 1;
} if (curClickIndex > totalShowIndex) {
}) // 计算移动的距离
} const x = (curClickIndex - totalShowIndex) * (tabWidth as number) + isBeyondDistance;
dispatch({ type: 'CHANGESTATE', payload: { tarnslateX: x, active: curClickIndex } });
} else {
const menuMore = ( dispatch({
<Menu onClick={(e) => { type: 'CHANGESTATE',
// 判断点击多余tab的展示移动距离是多少 payload: { tarnslateX: tabWidth * curClickIndex, active: curClickIndex },
// 计算超出了多少 });
// tabsWidth tabWidth showTabs 100是右边操作的距离目前写死 }
const isBeyondDistance = ((showTabs as number) * (tabWidth as number)) - (tabsWidth as number) + 100; history.push({ ...tabList[curClickIndex] });
// TODO 找到当前点击的索引值 }}
const curClickIndex = tabList?.findIndex(item => item.pathname === e.key) as number; >
// 能展示多少个 {arr.map((item) => {
const totalShowIndex = (showTabs as number) - 1; return <Menu.Item key={item.pathname}> {item.title}</Menu.Item>;
if (curClickIndex > totalShowIndex) { })}
// 计算移动的距离 </Menu>
const x = (curClickIndex - totalShowIndex) * (tabWidth as number) + isBeyondDistance );
dispatch({ type: 'CHANGESTATE', payload: { tarnslateX: x, active: curClickIndex } }) const menu = (
<Menu
onClick={(e) => {
let activeIndex: number = 0;
const localTablist = JSON.parse(JSON.stringify(tabList));
switch (e.key) {
case 'closeCurrent': {
const currentName = localTablist[active].keepAliveName;
if (active > 0) {
activeIndex = active - 1;
const timer = setTimeout(() => {
clearTimeout(timer);
history.push(tabList[activeIndex]);
}, 10);
} else { } else {
dispatch({ type: 'CHANGESTATE', payload: { tarnslateX: tabWidth * curClickIndex, active: curClickIndex } }) activeIndex = 0;
const timer = setTimeout(() => {
clearTimeout(timer);
history.push(localTablist[activeIndex]);
}, 10);
} }
history.push({ ...tabList[curClickIndex] }) const unlisten = history.listen(() => {
}}> unlisten();
{ const dropTimer = setTimeout(() => {
arr.map(item => { clearTimeout(dropTimer);
return <Menu.Item key={item.pathname}> {item.title}</Menu.Item> dropScope(currentName);
}) }, 10);
} });
</Menu> localTablist.splice(active, 1);
); dispatch({
const menu = ( type: 'CHANGESTATE',
<Menu onClick={(e) => { payload: { tabList: localTablist, active: activeIndex, tarnslateX: 0 },
let activeIndex: number = 0; });
const localTablist = JSON.parse(JSON.stringify(tabList)) break;
switch (e.key) { }
case "closeCurrent": { case 'closeOther': {
const currentName = localTablist[active].keepAliveName const needDelete = localTablist.filter((item: any, index: number) => index !== active);
if (active > 0) { const needUpdate = localTablist.filter((item: any, index: number) => index === active);
activeIndex = active - 1 needDelete.forEach((item: any) => dropScope(item.keepAliveName));
const timer = setTimeout(() => { dispatch({
clearTimeout(timer) type: 'CHANGESTATE',
history.push(tabList[activeIndex]) payload: { tabList: needUpdate, active: 0, tarnslateX: 0 },
}, 10) });
} else { break;
activeIndex = 0 }
const timer = setTimeout(() => { case 'closeAll': {
clearTimeout(timer) const unlisten = history.listen(() => {
history.push(localTablist[activeIndex]) unlisten();
}, 10) const dropTimer = setTimeout(() => {
} clearTimeout(dropTimer);
const unlisten = history.listen(() => { clear();
unlisten() }, 10);
const dropTimer = setTimeout(() => { });
clearTimeout(dropTimer) const timer = setTimeout(() => {
dropScope(currentName) clearTimeout(timer);
}, 10) history.push('/');
}) }, 10);
localTablist.splice(active, 1) dispatch({ type: 'CHANGESTATE', payload: { tabList: [], active: 0, tarnslateX: 0 } });
dispatch({ type: "CHANGESTATE", payload: { tabList: localTablist, active: activeIndex, tarnslateX: 0 } }) break;
break; }
} case 'closeLeft': {
case "closeOther": { const needDelete = localTablist.filter((item: any, index: number) => index < active);
const needDelete = localTablist.filter((item: any, index: number) => index !== active); const needUpdate = localTablist.filter((item: any, index: number) => index >= active);
const needUpdate = localTablist.filter((item: any, index: number) => index === active); needDelete.forEach((item: any) => dropScope(item.keepAliveName));
needDelete.forEach((item: any) => dropScope(item.keepAliveName)); dispatch({
dispatch({ type: "CHANGESTATE", payload: { tabList: needUpdate, active: 0, tarnslateX: 0 } }) type: 'CHANGESTATE',
break; payload: { tabList: needUpdate, active: 0, tarnslateX: 0 },
} });
case "closeAll": { break;
const unlisten = history.listen(() => { }
unlisten() case 'closeRight': {
const dropTimer = setTimeout(() => { const needDelete = localTablist.filter((item: any, index: number) => index > active);
clearTimeout(dropTimer) const needUpdate = localTablist.filter((item: any, index: number) => index <= active);
clear() needDelete.forEach((item: any) => dropScope(item.keepAliveName));
}, 10) dispatch({ type: 'CHANGESTATE', payload: { tabList: needUpdate, tarnslateX: 0 } });
}) break;
const timer = setTimeout(() => { }
clearTimeout(timer)
history.push("/")
}, 10)
dispatch({ type: "CHANGESTATE", payload: { tabList: [], active: 0, tarnslateX: 0 } })
break;
}
case "closeLeft": {
const needDelete = localTablist.filter((item: any, index: number) => index < active);
const needUpdate = localTablist.filter((item: any, index: number) => index >= active);
needDelete.forEach((item: any) => dropScope(item.keepAliveName));
dispatch({ type: "CHANGESTATE", payload: { tabList: needUpdate, active: 0, tarnslateX: 0 } })
break;
}
case "closeRight": {
const needDelete = localTablist.filter((item: any, index: number) => index > active);
const needUpdate = localTablist.filter((item: any, index: number) => index <= active);
needDelete.forEach((item: any) => dropScope(item.keepAliveName));
dispatch({ type: "CHANGESTATE", payload: { tabList: needUpdate, tarnslateX: 0 } })
break;
}
}
}}>
<Menu.Item key="closeCurrent"></Menu.Item>
<Menu.Item key="closeOther"></Menu.Item>
<Menu.Item key="closeAll"></Menu.Item>
<Menu.Item key="closeLeft" disabled={active === 0}></Menu.Item>
<Menu.Item key="closeRight" disabled={active === tabList.length - 1}></Menu.Item>
</Menu>
);
useEffect(() => {
window.onresize = () => {
const width = document.getElementById("contentContainer") ? document.getElementById("contentContainer")!.getBoundingClientRect()!.width : 0;
dispatch({ type: "CHANGESTATE", payload: { tabsWidth: width, tarnslateX: 0 } })
} }
}}
>
<Menu.Item key="closeCurrent"></Menu.Item>
<Menu.Item key="closeOther"></Menu.Item>
<Menu.Item key="closeAll"></Menu.Item>
<Menu.Item key="closeLeft" disabled={active === 0}>
</Menu.Item>
<Menu.Item key="closeRight" disabled={active === tabList.length - 1}>
</Menu.Item>
</Menu>
);
const timer = setTimeout(() => { useEffect(() => {
const width = document.getElementById("contentContainer") ? document.getElementById("contentContainer")!.getBoundingClientRect()!.width : 0; window.onresize = () => {
dispatch({ type: "CHANGESTATE", payload: { tabsWidth: width } }) const width = document.getElementById('contentContainer')
}, 100); ? document.getElementById('contentContainer')!.getBoundingClientRect()!.width
return () => { : 0;
clearTimeout(timer) dispatch({ type: 'CHANGESTATE', payload: { tabsWidth: width, tarnslateX: 0 } });
} };
}, [collapsed])
const timer = setTimeout(() => {
const width = document.getElementById('contentContainer')
? document.getElementById('contentContainer')!.getBoundingClientRect()!.width
: 0;
dispatch({ type: 'CHANGESTATE', payload: { tabsWidth: width } });
}, 100);
return () => {
clearTimeout(timer);
};
}, [collapsed]);
useEffect(() => { useEffect(() => {
const timer = setTimeout(() => { const timer = setTimeout(() => {
// 需要重新计算拿到当前tab的宽度 // 需要重新计算拿到当前tab的宽度
const itemWidth = document.getElementsByClassName("link-tab")[0] ? document.getElementsByClassName("link-tab")[0]!.getBoundingClientRect().width : 120; const itemWidth = document.getElementsByClassName('link-tab')[0]
//计算一排能展示多少个tab 需要减去操作占用的空间100 ? document.getElementsByClassName('link-tab')[0]!.getBoundingClientRect().width
const isShowTabs = Math.ceil((tabsWidth as number - 100) / itemWidth); : 120;
if (itemWidth > 0 && tabWidth > 0) { //计算一排能展示多少个tab 需要减去操作占用的空间100
dispatch({ type: "CHANGESTATE", payload: { showTabs: isShowTabs, tabWidth: itemWidth } }) const isShowTabs = Math.ceil(((tabsWidth as number) - 100) / itemWidth);
} if (itemWidth > 0 && tabWidth > 0) {
}, 100); dispatch({ type: 'CHANGESTATE', payload: { showTabs: isShowTabs, tabWidth: itemWidth } });
return () => { }
clearTimeout(timer) }, 100);
} return () => {
}, [tabsWidth]) clearTimeout(timer);
};
}, [tabsWidth]);
return (
// <div className={styles.tabs} style={{ width: initialState?.collapsed ? "calc(100vw - 41px)" : "calc(100vw - 249px)" }}>
return ( <div className={styles.tabs} id="contentContainer">
// <div className={styles.tabs} style={{ width: initialState?.collapsed ? "calc(100vw - 41px)" : "calc(100vw - 249px)" }}> {tabList.length > 0 && <SortableList onSortEnd={onSortEnd} axis={'x'} distance={1} />}
<div className={styles.tabs} id="contentContainer"> <div className={`${styles.tabLeftMenu} ${tabList.length >= showTabs && styles.boxShadow}`}>
{tabList.length > 0 && <SortableList onSortEnd={onSortEnd} axis={'x'} distance={1} />} {tabList.length > showTabs && (
<div className={`${styles.tabLeftMenu} ${tabList.length >= showTabs && styles.boxShadow}`}> <>
{ <Dropdown overlay={menuMore} className={styles.tabMore}>
tabList.length > showTabs && ( <a className="ant-dropdown-link" onClick={(e) => e.preventDefault()}>
<> <MoreOutlined />
<Dropdown overlay={menuMore} className={styles.tabMore}> </a>
<a className="ant-dropdown-link" onClick={e => e.preventDefault()}> </Dropdown>
<MoreOutlined /> <Divider type="vertical" />
</a> </>
</Dropdown> )}
<Divider type='vertical' /> {tabList.length > 1 && (
</> <Dropdown overlay={menu} className={styles.menuRight}>
) <a className="ant-dropdown-link" onClick={(e) => e.preventDefault()}>
} <DownOutlined />
{ </a>
tabList.length > 1 && ( </Dropdown>
<Dropdown overlay={menu} className={styles.menuRight}> )}
<a className="ant-dropdown-link" onClick={e => e.preventDefault()}> </div>
<DownOutlined /> </div>
</a> );
</Dropdown> };
)
}
</div>
</div>
);
}
export default KeepAliveTabs; export default KeepAliveTabs;

View File

@ -56,29 +56,28 @@ ol {
} }
} }
// .ant-layout{
// background: #fff;
// }
// .ant-layout-header{
// //display: none;
// }
.ant-layout{ // #contentContainer{
background: #fff; // //display: none;
} // }
.ant-layout-header{ // .ant-pro-right-content{
//display: none; // display: none;
} // }
// .ant-modal-mask{
#contentContainer{ // background-color: rgba(255, 0, 0, 0);
//display: none; // }
} // .chiner-modal{
.ant-pro-right-content{ // background-color: rgba(255, 0, 0, 0);
display: none; // }
} // .ant-pro-top-nav-header-logo{
.ant-modal-mask{ // display: none;
background-color: rgba(255, 0, 0, 0); // }
} // .ant-pro-top-nav-header-main-left{
.chiner-modal{ // min-width: 0px;
background-color: rgba(255, 0, 0, 0); // }
}
.ant-pro-top-nav-header-logo{
display: none;
}
.ant-pro-top-nav-header-main-left{
min-width: 0px;
}

View File

@ -1,235 +1,227 @@
<!DOCTYPE html> <!DOCTYPE html>
<html lang="en"> <html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta
name="keywords"
content="antd,umi,umijs,ant design,Scaffolding, layout, Ant Design, project, Pro, admin, console, homepage, out-of-the-box, middle and back office, solution, component library"
/>
<meta
name="description"
content="
An out-of-box UI solution for enterprise applications as a React boilerplate."
/>
<meta
name="description"
content="
Out-of-the-box mid-stage front-end/design solution."
/>
<meta
name="viewport"
content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0"
/>
<title>Salpa</title>
<link rel="icon" href="<%= context.config.publicPath +'樽海鞘_图案.svg'%>" type="image/x-icon" />
</head>
<body>
<noscript>
<div class="noscript-container">
Hi there! Please
<div class="noscript-enableJS">
<a href="https://www.enablejavascript.io/en" target="_blank" rel="noopener noreferrer">
<b>enable Javascript</b>
</a>
</div>
in your browser to use Ant Design, Out-of-the-box mid-stage front/design solution!
</div>
</noscript>
<div id="root">
<style>
html,
body,
#root {
height: 100%;
margin: 0;
padding: 0;
}
#root {
background-repeat: no-repeat;
background-size: 100% auto;
}
.noscript-container {
display: flex;
align-content: center;
justify-content: center;
margin-top: 90px;
font-size: 20px;
font-family: 'Lucida Sans', 'Lucida Sans Regular', 'Lucida Grande', 'Lucida Sans Unicode',
Geneva, Verdana, sans-serif;
}
.noscript-enableJS {
padding-right: 3px;
padding-left: 3px;
}
.page-loading-warp {
display: flex;
align-items: center;
justify-content: center;
padding: 98px;
}
.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 { <head>
position: static; <meta charset="UTF-8" />
display: inline-block; <meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="keywords"
content="antd,umi,umijs,ant design,Scaffolding, layout, Ant Design, project, Pro, admin, console, homepage, out-of-the-box, middle and back office, solution, component library" />
<meta name="description" content="
An out-of-box UI solution for enterprise applications as a React boilerplate." />
<meta name="description" content="
Out-of-the-box mid-stage front-end/design solution." />
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0" />
<title>Salpa</title>
<link rel="icon" href="<%= context.config.publicPath +'樽海鞘_图案.svg'%>" type="image/x-icon" />
<script src="https://cdn.jsdelivr.net/npm/xlsx@latest/dist/xlsx.full.min.js"></script>
</head>
<body>
<noscript>
<div class="noscript-container">
Hi there! Please
<div class="noscript-enableJS">
<a href="https://www.enablejavascript.io/en" target="_blank" rel="noopener noreferrer">
<b>enable Javascript</b>
</a>
</div>
in your browser to use Ant Design, Out-of-the-box mid-stage front/design solution!
</div>
</noscript>
<div id="root">
<style>
html,
body,
#root {
height: 100%;
margin: 0;
padding: 0;
}
#root {
background-repeat: no-repeat;
background-size: 100% auto;
}
.noscript-container {
display: flex;
align-content: center;
justify-content: center;
margin-top: 90px;
font-size: 20px;
font-family: 'Lucida Sans', 'Lucida Sans Regular', 'Lucida Grande', 'Lucida Sans Unicode',
Geneva, Verdana, sans-serif;
}
.noscript-enableJS {
padding-right: 3px;
padding-left: 3px;
}
.page-loading-warp {
display: flex;
align-items: center;
justify-content: center;
padding: 98px;
}
.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; opacity: 1;
} }
}
.ant-spin-dot { @keyframes antSpinMove {
position: relative; to {
display: inline-block; opacity: 1;
width: 20px;
height: 20px;
font-size: 20px;
} }
}
.ant-spin-dot-item { @-webkit-keyframes antRotate {
position: absolute; to {
display: block; -webkit-transform: rotate(405deg);
width: 9px; transform: rotate(405deg);
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) { @keyframes antRotate {
top: 0; to {
left: 0; -webkit-transform: rotate(405deg);
transform: rotate(405deg);
} }
}
.ant-spin-dot-item:nth-child(2) { </style>
top: 0; <div style="
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; display: flex;
flex-direction: column; flex-direction: column;
align-items: center; align-items: center;
justify-content: center; justify-content: center;
height: 100%; height: 100%;
min-height: 420px; min-height: 420px;
" ">
> <h1>Salpa</h1>
<h1>Salpa</h1> <div class="page-loading-warp">
<div class="page-loading-warp"> <div class="ant-spin ant-spin-lg ant-spin-spinning">
<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
<span class="ant-spin-dot ant-spin-dot-spin" class="ant-spin-dot-item"></i><i class="ant-spin-dot-item"></i><i class="ant-spin-dot-item"></i></span>
><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 style="display: flex; align-items: center; justify-content: center">
<img
src="<%= context.config.publicPath +'樽海鞘_图案.svg'%>"
width="32"
style="margin-right: 8px"
/>
Salpa
</div> </div>
</div> </div>
<div style="display: flex; align-items: center; justify-content: center">
<img src="<%= context.config.publicPath +'樽海鞘_图案.svg'%>" width="32" style="margin-right: 8px" />
Salpa
</div>
</div> </div>
</body> </div>
</body>
</html> </html>

View File

@ -1,8 +1,6 @@
// import { getUserConfig } from '../../lib/middle'; // import { getUserConfig } from '../../lib/middle';
import { openLoading, closeLoading, STATUS } from '../common';
import allLangData from '../../lang'; import allLangData from '../../lang';
import { setMemoryCache } from '../../lib/cache'; import { openLoading } from '../common';
import { CONFIG } from '../../lib/variable';
export const SAVE_USER_CONFIG_SUCCESS = 'SAVE_USER_CONFIG_SUCCESS'; // 保存成功 export const SAVE_USER_CONFIG_SUCCESS = 'SAVE_USER_CONFIG_SUCCESS'; // 保存成功
export const SAVE_USER_CONFIG_FAIL = 'SAVE_USER_CONFIG_FAIL'; // 保存失败 export const SAVE_USER_CONFIG_FAIL = 'SAVE_USER_CONFIG_FAIL'; // 保存失败
@ -52,13 +50,16 @@ export const getUserConfigData = () => {
export const saveUserConfigSome = (data) => { export const saveUserConfigSome = (data) => {
return (dispatch, getState) => { return (dispatch, getState) => {
const configData = getState()?.config?.data || []; const configData = getState()?.config?.data || [];
const config = {...configData[0], ...data}; const config = { ...configData[0], ...data };
saveUserConfigData(configData.map((d, index) => { saveUserConfigData(
if (index === 0) { configData.map((d, index) => {
return config; if (index === 0) {
} return config;
return d; }
}), allLangData[config.lang].updateConfig)(dispatch); return d;
}),
allLangData[config.lang].updateConfig,
)(dispatch);
}; };
}; };
@ -87,59 +88,70 @@ export const removeHistory = (h) => {
return (dispatch, getState) => { return (dispatch, getState) => {
const configData = getState()?.config?.data || []; const configData = getState()?.config?.data || [];
const config = configData[0]; const config = configData[0];
saveUserConfigData(configData.map((d, index) => { saveUserConfigData(
if (index === 0) { configData.map((d, index) => {
return { if (index === 0) {
...d, return {
projectHistories: (d.projectHistories || []).filter(p => (p.path !== h.path)), ...d,
projectHistories: (d.projectHistories || []).filter((p) => p.path !== h.path),
};
} }
} return d;
return d; }),
}), allLangData[config.lang].updateConfig)(dispatch); allLangData[config.lang].updateConfig,
} )(dispatch);
};
}; };
export const addHistory = (data, cb) => { export const addHistory = (data, cb) => {
return (dispatch, getState) => { return (dispatch, getState) => {
const configData = getState()?.config?.data || []; const configData = getState()?.config?.data || [];
const config = configData[0]; const config = configData[0];
saveUserConfigData(configData.map((d, index) => { saveUserConfigData(
if (index === 0) { configData.map((d, index) => {
const tempProjectHistories = [...(d.projectHistories || [])] if (index === 0) {
.filter(h => h.path !== data.path); // 移除当前的项目信息 const tempProjectHistories = [...(d.projectHistories || [])].filter(
tempProjectHistories.unshift(data); (h) => h.path !== data.path,
return { ); // 移除当前的项目信息
...d, tempProjectHistories.unshift(data);
projectHistories: tempProjectHistories, return {
}; ...d,
} projectHistories: tempProjectHistories,
return d; };
}), allLangData[config.lang].updateConfig, cb)(dispatch); }
} return d;
}),
allLangData[config.lang].updateConfig,
cb,
)(dispatch);
};
}; };
export const updateHistory = (oldData, newData) => { export const updateHistory = (oldData, newData) => {
return (dispatch, getState) => { return (dispatch, getState) => {
const configData = getState()?.config?.data || []; const configData = getState()?.config?.data || [];
const config = configData[0]; const config = configData[0];
saveUserConfigData(configData.map((d, index) => { saveUserConfigData(
if (index === 0) { configData.map((d, index) => {
return { if (index === 0) {
...d, return {
projectHistories: (d.projectHistories || []).map((h) => { ...d,
if (h.path === oldData.path) { projectHistories: (d.projectHistories || []).map((h) => {
return { if (h.path === oldData.path) {
describe: newData.describe || '', return {
name: newData.name || '', describe: newData.describe || '',
avatar: newData.avatar || '', name: newData.name || '',
path: newData.path, avatar: newData.avatar || '',
}; path: newData.path,
} };
return h; }
}), return h;
}; }),
} };
return d; }
}), allLangData[config.lang].updateConfig)(dispatch); return d;
} }),
allLangData[config.lang].updateConfig,
)(dispatch);
};
}; };

View File

@ -1,94 +1,115 @@
/* eslint-disable no-const-assign */ /* eslint-disable no-const-assign */
/* eslint-disable no-undef */ /* eslint-disable no-undef */
import _ from 'lodash/object' import _ from 'lodash/object';
import moment from 'moment' import moment from 'moment';
import { openLoading, closeLoading, STATUS, optReset } from '../common' import version from '../../../../../package.json';
import { reduceProject, transformationData } from '../../lib/datasource_util' import { setMemoryCache } from '../../lib/cache';
import { setMemoryCache } from '../../lib/cache' import { transformationData } from '../../lib/datasource_util';
import * as template from '../../lib/template' import * as template from '../../lib/template';
import { compareVersion } from '../../lib/update' import { closeLoading, openLoading, optReset, STATUS } from '../common';
import version from '../../../../../package.json'
// import {emptyProject} from '../../lib/template/empty'; // import {emptyProject} from '../../lib/template/empty';
import { removeHistory, addHistory, updateHistory } from '../config' import allLangData from '../../lang';
import { projectSuffix } from '../../profile'
import allLangData from '../../lang'
import { import {
basename, saveVersion, deleteFile, ensureDirectoryExistence, openFileOrDirPath, renameVersion, dirSplicing, fileExists, saveJsonPromiseAs, updateAllVersion, saveJsonPromise, getBackupAllFile, writeLog, deleteVersion, openProjectFilePath, readJsonPromise, getAllVersionProject basename,
} from '../../lib/middle' deleteFile,
deleteVersion,
dirSplicing,
ensureDirectoryExistence,
fileExists,
getAllVersionProject,
getBackupAllFile,
openFileOrDirPath,
openProjectFilePath,
readJsonPromise,
renameVersion,
saveJsonPromise,
saveJsonPromiseAs,
saveVersion,
updateAllVersion,
writeLog,
} from '../../lib/middle';
import { projectSuffix } from '../../profile';
import { addHistory, removeHistory, updateHistory } from '../config';
/* /*
* 核心的action 负责整个项目的保存和删除 * 核心的action 负责整个项目的保存和删除
* */ * */
/* /*
* action 类型 * action 类型
*/ */
export const SAVE_PROJECT_SUCCESS = 'SAVE_PROJECT_SUCCESS' // 保存成功 export const SAVE_PROJECT_SUCCESS = 'SAVE_PROJECT_SUCCESS'; // 保存成功
export const SAVE_PROJECT_FAIL = 'SAVE_PROJECT_FAIL' // 保存失败 export const SAVE_PROJECT_FAIL = 'SAVE_PROJECT_FAIL'; // 保存失败
export const READ_PROJECT_SUCCESS = 'READ_PROJECT_SUCCESS' // 读取成功 export const READ_PROJECT_SUCCESS = 'READ_PROJECT_SUCCESS'; // 读取成功
export const READ_PROJECT_FAIL = 'READ_PROJECT_FAIL' // 读取失败 export const READ_PROJECT_FAIL = 'READ_PROJECT_FAIL'; // 读取失败
export const CREATE_PROJECT_SUCCESS = 'CREATE_PROJECT_SUCCESS' // 创建成功 export const CREATE_PROJECT_SUCCESS = 'CREATE_PROJECT_SUCCESS'; // 创建成功
export const CREATE_PROJECT_ERROR = 'CREATE_PROJECT_ERROR' // 创建失败 export const CREATE_PROJECT_ERROR = 'CREATE_PROJECT_ERROR'; // 创建失败
export const SAVE_VERSION_SUCCESS = 'SAVE_VERSION_SUCCESS' // 保存版本信息成功 export const SAVE_VERSION_SUCCESS = 'SAVE_VERSION_SUCCESS'; // 保存版本信息成功
export const SAVE_VERSION_FAIL = 'SAVE_VERSION_FAIL' // 保存版本信息失败 export const SAVE_VERSION_FAIL = 'SAVE_VERSION_FAIL'; // 保存版本信息失败
export const SAVE_ALL_VERSION_SUCCESS = 'SAVE_ALL_VERSION_SUCCESS' export const SAVE_ALL_VERSION_SUCCESS = 'SAVE_ALL_VERSION_SUCCESS';
export const SAVE_ALL_VERSION_FAIL = 'SAVE_ALL_VERSION_FAIL' export const SAVE_ALL_VERSION_FAIL = 'SAVE_ALL_VERSION_FAIL';
export const REMOVE_VERSION_SUCCESS = 'REMOVE_VERSION_SUCCESS' // 版本信息删除成功 export const REMOVE_VERSION_SUCCESS = 'REMOVE_VERSION_SUCCESS'; // 版本信息删除成功
export const REMOVE_ALL_VERSION_SUCCESS = 'REMOVE_ALL_VERSION_SUCCESS' // 所有版本信息删除成功 export const REMOVE_ALL_VERSION_SUCCESS = 'REMOVE_ALL_VERSION_SUCCESS'; // 所有版本信息删除成功
export const UPDATE_PROJECT = 'UPDATE_PROJECT' // 更新项目 export const UPDATE_PROJECT = 'UPDATE_PROJECT'; // 更新项目
export const CLOSE_PROJECT = 'CLOSE_PROJECT' // 关闭项目 export const CLOSE_PROJECT = 'CLOSE_PROJECT'; // 关闭项目
export const UPDATE_PROJECT_INFO = 'UPDATE_PROJECT_INFO' // 更新项目信息 export const UPDATE_PROJECT_INFO = 'UPDATE_PROJECT_INFO'; // 更新项目信息
export const updateProjectInfo = (info) => { export const updateProjectInfo = (info) => {
return { return {
type: UPDATE_PROJECT_INFO, type: UPDATE_PROJECT_INFO,
data: info, data: info,
} };
} };
export const updateProject = (data) => { export const updateProject = (data) => {
return { return {
type: UPDATE_PROJECT, type: UPDATE_PROJECT,
data, data,
} };
} };
export const updateDataSource = (data) => {
return {
type: UPDATE_DATASOURCE,
data,
};
};
const saveAllVersionSuccess = (data) => { const saveAllVersionSuccess = (data) => {
return { return {
type: SAVE_ALL_VERSION_SUCCESS, type: SAVE_ALL_VERSION_SUCCESS,
data, data,
} };
} };
const saveAllVersionFail = (err) => { const saveAllVersionFail = (err) => {
return { return {
type: SAVE_ALL_VERSION_FAIL, type: SAVE_ALL_VERSION_FAIL,
data: err, data: err,
} };
} };
const removeVersionSuccess = (versionInfo) => { const removeVersionSuccess = (versionInfo) => {
return { return {
type: REMOVE_VERSION_SUCCESS, type: REMOVE_VERSION_SUCCESS,
data: versionInfo, data: versionInfo,
} };
} };
const saveVersionSuccess = (data, oldData) => { const saveVersionSuccess = (data, oldData) => {
return { return {
type: SAVE_VERSION_SUCCESS, type: SAVE_VERSION_SUCCESS,
data: { data, oldData }, data: { data, oldData },
} };
} };
const saveVersionFail = (err) => { const saveVersionFail = (err) => {
return { return {
type: SAVE_VERSION_FAIL, type: SAVE_VERSION_FAIL,
data: err, data: err,
} };
} };
const createProjectSuccess = (data, path) => { const createProjectSuccess = (data, path) => {
return { return {
type: CREATE_PROJECT_SUCCESS, type: CREATE_PROJECT_SUCCESS,
@ -97,82 +118,82 @@ const createProjectSuccess = (data, path) => {
data, data,
versionsData: [], versionsData: [],
}, },
} };
} };
const createProjectError = (error) => { const createProjectError = (error) => {
return { return {
type: CREATE_PROJECT_ERROR, type: CREATE_PROJECT_ERROR,
data: error, data: error,
} };
} };
const saveProjectFail = (err) => { const saveProjectFail = (err) => {
return { return {
type: SAVE_PROJECT_FAIL, type: SAVE_PROJECT_FAIL,
data: err, data: err,
} };
} };
const saveProjectSuccess = (data) => { const saveProjectSuccess = (data) => {
return { return {
type: SAVE_PROJECT_SUCCESS, type: SAVE_PROJECT_SUCCESS,
data, data,
} };
} };
let isSaving = false let isSaving = false;
export const saveProject = (data, saveAs, callback) => { export const saveProject = (data, saveAs, callback) => {
if (!isSaving) { if (!isSaving) {
// 此处为异步操作 // 此处为异步操作
const time = moment().format('YYYY-M-D HH:mm:ss') const time = moment().format('YYYY-M-D HH:mm:ss');
const tempData = { const tempData = {
...data, ...data,
createdTime: saveAs ? time : data.createdTime || time, createdTime: saveAs ? time : data.createdTime || time,
updatedTime: time, updatedTime: time,
version, version,
} };
return (dispatch, getState) => { return (dispatch, getState) => {
const info = getState()?.core?.info const info = getState()?.core?.info;
const getName = (p) => { const getName = (p) => {
const name = basename(p, '.json') const name = basename(p, '.json');
return name.split('.')[0] return name.split('.')[0];
} };
if (saveAs) { if (saveAs) {
const projectModelInfo = { const projectModelInfo = {
projectName: data.name, projectName: data.name,
jsonFile: typeof data === 'string' ? data : JSON.stringify(data, null, 2) jsonFile: typeof data === 'string' ? data : JSON.stringify(data, null, 2),
} };
// 保存操作 // 保存操作
if (data) { if (data) {
saveJsonPromiseAs(projectModelInfo).then(() => { saveJsonPromiseAs(projectModelInfo).then(() => {
getBackupAllFile(getState(), () => { getBackupAllFile(getState(), () => {
setMemoryCache('data', tempData) setMemoryCache('data', tempData);
callback && callback() callback && callback();
dispatch(saveProjectSuccess(tempData)) dispatch(saveProjectSuccess(tempData));
}) });
}) });
} else { } else {
callback && callback(err) callback && callback(err);
dispatch(saveProjectFail(err)) dispatch(saveProjectFail(err));
} }
} else { } else {
saveJsonPromise(info, tempData) saveJsonPromise(info, tempData)
.then(() => { .then(() => {
getBackupAllFile(getState(), () => { getBackupAllFile(getState(), () => {
isSaving = false isSaving = false;
setMemoryCache('data', tempData) setMemoryCache('data', tempData);
callback && callback() callback && callback();
dispatch(saveProjectSuccess(tempData)) dispatch(saveProjectSuccess(tempData));
}) });
}) })
.catch((err) => { .catch((err) => {
isSaving = false isSaving = false;
callback && callback(err) callback && callback(err);
dispatch(saveProjectFail(err)) dispatch(saveProjectFail(err));
}) });
} }
} };
} }
// return () => {}; // return () => {};
} };
const readProjectSuccess = (data, versionsData, path, isDemoProject) => { const readProjectSuccess = (data, versionsData, path, isDemoProject) => {
return { return {
type: READ_PROJECT_SUCCESS, type: READ_PROJECT_SUCCESS,
@ -182,276 +203,307 @@ const readProjectSuccess = (data, versionsData, path, isDemoProject) => {
data, data,
versionsData, versionsData,
}, },
} };
} };
export const autoSaveProject = (data) => { export const autoSaveProject = (data) => {
// 此处为异步操作 // 此处为异步操作
const time = moment().format('YYYY-M-D HH:mm:ss') const time = moment().format('YYYY-M-D HH:mm:ss');
const tempData = { const tempData = {
...data, ...data,
updatedTime: time, updatedTime: time,
version, version,
} };
return (dispatch, getState) => { return (dispatch, getState) => {
const info = getState()?.core?.info const info = getState()?.core?.info;
getBackupAllFile(getState(), () => { getBackupAllFile(getState(), () => {
saveJsonPromise(info, tempData) saveJsonPromise(info, tempData).catch((err) => {
.catch((err) => { writeLog(err);
writeLog(err) });
}) });
}) };
} };
}
export const removeVersionData = (title, versionInfo) => { export const removeVersionData = (title, versionInfo) => {
return (dispatch, getState) => { return (dispatch, getState) => {
const { data, info } = getState()?.core || {} const { data, info } = getState()?.core || {};
dispatch(openLoading(title)) dispatch(openLoading(title));
deleteVersion(versionInfo, data, info) deleteVersion(versionInfo, data, info);
dispatch(removeVersionSuccess(versionInfo)) dispatch(removeVersionSuccess(versionInfo));
dispatch(closeLoading(STATUS[1], null)) dispatch(closeLoading(STATUS[1], null));
} };
} };
export const updateAllVersionData = (versionDataCallBack, title, dataSource) => { export const updateAllVersionData = (versionDataCallBack, title, dataSource) => {
return (dispatch, getState) => { return (dispatch, getState) => {
const { data, info, versionsData } = getState()?.core || {} const { data, info, versionsData } = getState()?.core || {};
if (info) { if (info) {
dispatch(openLoading(title)) dispatch(openLoading(title));
const finalData = versionDataCallBack(versionsData) const finalData = versionDataCallBack(versionsData);
dispatch(autoSaveProject(dataSource)) dispatch(autoSaveProject(dataSource));
updateAllVersion(finalData, info, data).then(() => { updateAllVersion(finalData, info, data)
dispatch(saveAllVersionSuccess(finalData)) .then(() => {
dispatch(closeLoading(STATUS[1], null)) dispatch(saveAllVersionSuccess(finalData));
}).catch((err) => { dispatch(closeLoading(STATUS[1], null));
dispatch(saveAllVersionFail(err)) })
dispatch(closeLoading(STATUS[2], err)) .catch((err) => {
}) dispatch(saveAllVersionFail(err));
dispatch(closeLoading(STATUS[2], err));
});
} }
} };
} };
export const saveVersionData = (versionData, oldVersion, dataSource, title) => { export const saveVersionData = (versionData, oldVersion, dataSource, title) => {
return (dispatch, getState) => { return (dispatch, getState) => {
dispatch(openLoading(title)) dispatch(openLoading(title));
const info = getState()?.core?.info const info = getState()?.core?.info;
return new Promise((res, rej) => { return new Promise((res, rej) => {
saveVersion(versionData, oldVersion, info, dataSource).then(() => { saveVersion(versionData, oldVersion, info, dataSource)
dispatch(saveVersionSuccess(versionData, oldVersion)) .then(() => {
dispatch(closeLoading(STATUS[1], null)) dispatch(saveVersionSuccess(versionData, oldVersion));
res(versionData) dispatch(closeLoading(STATUS[1], null));
}).catch((err) => { res(versionData);
dispatch(saveVersionFail(err)) })
rej(err) .catch((err) => {
}) dispatch(saveVersionFail(err));
}) rej(err);
} });
} });
};
};
export const renameProject = (newData, oldData, title, dataInfo) => { export const renameProject = (newData, oldData, title, dataInfo) => {
// 判断项目名和项目目录是否已经修改 该方法无只需触发loading操作的action // 判断项目名和项目目录是否已经修改 该方法无只需触发loading操作的action
return (dispatch, getState) => { return (dispatch, getState) => {
dispatch(openLoading(title)) dispatch(openLoading(title));
// 1.需要调整项目文件 先新建 再删除 // 1.需要调整项目文件 先新建 再删除
const oldFilePath = dirSplicing(dataInfo.path, '') const oldFilePath = dirSplicing(dataInfo.path, '');
const newFilePath = dirSplicing(newData.path, `${newData.name}.${projectSuffix}.json`) const newFilePath = dirSplicing(newData.path, `${newData.name}.${projectSuffix}.json`);
const config = getState()?.config?.data[0] const config = getState()?.config?.data[0];
if (fileExists(newFilePath) && (oldFilePath !== newFilePath)) { if (fileExists(newFilePath) && oldFilePath !== newFilePath) {
dispatch(closeLoading(STATUS[2], allLangData[config.lang].createProjectExists)) dispatch(closeLoading(STATUS[2], allLangData[config.lang].createProjectExists));
} else { } else {
saveJsonPromise(newFilePath, _.omit({ saveJsonPromise(
...oldData, newFilePath,
...newData, _.omit(
updatedTime: moment().format('YYYY-M-D HH:mm:ss'), {
}, ['path'])).then(() => { ...oldData,
if (oldFilePath !== newFilePath) { ...newData,
// 删除 updatedTime: moment().format('YYYY-M-D HH:mm:ss'),
deleteFile(oldFilePath) },
// 更新版本文件 ['path'],
renameVersion(oldFilePath, newFilePath, oldData, newData) ),
} )
// 2.需要更新用户配置文件 .then(() => {
updateHistory({ if (oldFilePath !== newFilePath) {
...oldData, // 删除
path: oldFilePath, deleteFile(oldFilePath);
}, { // 更新版本文件
describe: newData.describe || '', renameVersion(oldFilePath, newFilePath, oldData, newData);
name: newData.name || '', }
avatar: newData.avatar || '', // 2.需要更新用户配置文件
path: newFilePath, updateHistory(
})(dispatch, getState) {
}).catch((err) => { ...oldData,
dispatch(closeLoading(STATUS[2], err)) path: oldFilePath,
}) },
{
describe: newData.describe || '',
name: newData.name || '',
avatar: newData.avatar || '',
path: newFilePath,
},
)(dispatch, getState);
})
.catch((err) => {
dispatch(closeLoading(STATUS[2], err));
});
} }
} };
} };
export const removeProject = (data, title) => { export const removeProject = (data, title) => {
return (dispatch, getState) => { return (dispatch, getState) => {
dispatch(openLoading(title)) dispatch(openLoading(title));
// 删除项目 // 删除项目
deleteFile(data.path) deleteFile(data.path);
// 更新历史记录 // 更新历史记录
removeHistory(data)(dispatch, getState) removeHistory(data)(dispatch, getState);
} };
} };
export const createProject = (data, path, title, type) => { export const createProject = (data, path, title, type) => {
return (dispatch, getState) => { return (dispatch, getState) => {
dispatch(openLoading(title)) dispatch(openLoading(title));
// 如果传递的路径则直接保存 反之则需要选取路径 // 如果传递的路径则直接保存 反之则需要选取路径
new Promise((res, rej) => { new Promise((res, rej) => {
if (!path) { if (!path) {
openFileOrDirPath([], ['openDirectory']).then((dir) => { openFileOrDirPath([], ['openDirectory'])
res(dir) .then((dir) => {
}).catch(rej) res(dir);
})
.catch(rej);
} else { } else {
// 校验目录是否存在 如果不存在则需要创建 // 校验目录是否存在 如果不存在则需要创建
ensureDirectoryExistence(path) ensureDirectoryExistence(path);
res(path) res(path);
} }
}).then((projectDir) => {
// 拼接路径和项目名
const realFilePath = dirSplicing(projectDir, `${data.name}.${projectSuffix}.json`)
const config = getState()?.config?.data[0]
if (fileExists(realFilePath)) {
throw new Error(allLangData[config.lang].createProjectExists)
} else {
const time = moment().format('YYYY-M-D HH:mm:ss')
const newData = {
// ...emptyProject,
...data,
version,
createdTime: time,
updatedTime: time,
}
saveJsonPromise(realFilePath, newData).then(() => {
addHistory({
describe: newData.describe || '',
name: newData.name || '',
avatar: newData.avatar || '',
path: realFilePath,
}, (err) => {
if (!err) {
dispatch(createProjectSuccess(newData, realFilePath))
dispatch(closeLoading(STATUS[1], null, '', type))
} else {
// dispatch(createProjectError(err));
dispatch(closeLoading(STATUS[2], err))
}
})(dispatch, getState)
}).catch((err) => {
dispatch(createProjectError(err))
// dispatch(closeLoading(STATUS[2], err));
})
}
}).catch((err) => {
dispatch(closeLoading(STATUS[2], err))
// dispatch(createProjectError(err));
}) })
} .then((projectDir) => {
} // 拼接路径和项目名
const realFilePath = dirSplicing(projectDir, `${data.name}.${projectSuffix}.json`);
const config = getState()?.config?.data[0];
if (fileExists(realFilePath)) {
throw new Error(allLangData[config.lang].createProjectExists);
} else {
const time = moment().format('YYYY-M-D HH:mm:ss');
const newData = {
// ...emptyProject,
...data,
version,
createdTime: time,
updatedTime: time,
};
saveJsonPromise(realFilePath, newData)
.then(() => {
addHistory(
{
describe: newData.describe || '',
name: newData.name || '',
avatar: newData.avatar || '',
path: realFilePath,
},
(err) => {
if (!err) {
dispatch(createProjectSuccess(newData, realFilePath));
dispatch(closeLoading(STATUS[1], null, '', type));
} else {
// dispatch(createProjectError(err));
dispatch(closeLoading(STATUS[2], err));
}
},
)(dispatch, getState);
})
.catch((err) => {
dispatch(createProjectError(err));
// dispatch(closeLoading(STATUS[2], err));
});
}
})
.catch((err) => {
dispatch(closeLoading(STATUS[2], err));
// dispatch(createProjectError(err));
});
};
};
/* /*
* action 创建函数 * action 创建函数
*/ */
export const openDemoProject = (h, t, title, type) => { export const openDemoProject = (h, t, title, type) => {
return (dispatch, getState) => { return (dispatch, getState) => {
dispatch(openLoading(title)) dispatch(openLoading(title));
let tempH = h let tempH = h;
let isDemoProject = t || getState().core.isDemoProject let isDemoProject = t || getState().core.isDemoProject;
if (!tempH) { if (!tempH) {
tempH = template[isDemoProject] tempH = template[isDemoProject];
} }
const data = const data =
// compareVersion('3.5.0', tempH.version.split('.')) // compareVersion('3.5.0', tempH.version.split('.'))
// ? reduceProject(tempH, 'defKey') : // ? reduceProject(tempH, 'defKey') :
tempH tempH;
setMemoryCache('data', data) setMemoryCache('data', data);
dispatch(readProjectSuccess(data, [], '', isDemoProject)) dispatch(readProjectSuccess(data, [], '', isDemoProject));
dispatch(closeLoading(STATUS[1], null, '', type)) dispatch(closeLoading(STATUS[1], null, '', type));
} };
} };
export const close = () => { export const close = () => {
return { return {
type: CLOSE_PROJECT, type: CLOSE_PROJECT,
} };
} };
export const closeProject = (type) => { export const closeProject = (type) => {
return (dispatch) => { return (dispatch) => {
dispatch(optReset(type)) dispatch(optReset(type));
dispatch(close()) dispatch(close());
} };
} };
export const readProject = (path, title, getState, type, isDemoProject) => { export const readProject = (path, title, getState, type, isDemoProject) => {
return (dispatch) => { return (dispatch) => {
dispatch(openLoading(title)) // 开启全局loading dispatch(openLoading(title)); // 开启全局loading
// 判断项目文件是否为只读权限 // 判断项目文件是否为只读权限
readJsonPromise(path) readJsonPromise(path)
.then((data) => { .then((data) => {
if (!data.version) { if (!data.version) {
// 无效的项目 // 无效的项目
const config = getState()?.config?.data[0] const config = getState()?.config?.data[0];
const err = new Error(allLangData[config.lang].invalidProjectData) const err = new Error(allLangData[config.lang].invalidProjectData);
dispatch(readProjectFail(err)) dispatch(readProjectFail(err));
dispatch(closeLoading(STATUS[2], err)) dispatch(closeLoading(STATUS[2], err));
} else if (!isDemoProject) { } else if (!isDemoProject) {
const newData = transformationData(data) const newData = transformationData(data);
// 将打开的项目记录存储到用户信息中 // 将打开的项目记录存储到用户信息中
addHistory({ addHistory(
describe: newData.describe || '', {
name: newData.name || '', describe: newData.describe || '',
avatar: newData.avatar || '', name: newData.name || '',
path, avatar: newData.avatar || '',
}, (err) => { path,
if (!err) { },
setMemoryCache('data', newData) (err) => {
getAllVersionProject(path, newData).then((versionData) => { if (!err) {
dispatch(readProjectSuccess(newData, versionData, path)) setMemoryCache('data', newData);
}).catch(() => { getAllVersionProject(path, newData)
setMemoryCache('data', newData) .then((versionData) => {
dispatch(readProjectSuccess(newData, versionData, path)) dispatch(readProjectSuccess(newData, versionData, path));
}).finally(() => { })
dispatch(closeLoading(STATUS[1], null, '', type)) .catch(() => {
}) setMemoryCache('data', newData);
} else { dispatch(readProjectSuccess(newData, versionData, path));
dispatch(readProjectFail(err)) })
dispatch(closeLoading(STATUS[2], err)) .finally(() => {
} dispatch(closeLoading(STATUS[1], null, '', type));
})(dispatch, getState) });
} else {
dispatch(readProjectFail(err));
dispatch(closeLoading(STATUS[2], err));
}
},
)(dispatch, getState);
} else { } else {
const newData = transformationData(data) const newData = transformationData(data);
setMemoryCache('data', newData) setMemoryCache('data', newData);
dispatch(readProjectSuccess(newData, [], path, isDemoProject)) dispatch(readProjectSuccess(newData, [], path, isDemoProject));
dispatch(closeLoading(STATUS[1], null, '', type)) dispatch(closeLoading(STATUS[1], null, '', type));
} }
}) })
.catch((err) => { .catch((err) => {
dispatch(readProjectFail(err)) dispatch(readProjectFail(err));
dispatch(closeLoading(STATUS[2], err)) dispatch(closeLoading(STATUS[2], err));
}) });
} };
} };
const readProjectFail = (err) => { const readProjectFail = (err) => {
return { return {
type: READ_PROJECT_FAIL, type: READ_PROJECT_FAIL,
data: err, data: err,
} };
} };
// 打开项目 // 打开项目
export const openProject = (title, type, path, suffix, isDemoProject) => { export const openProject = (title, type, path, suffix, isDemoProject) => {
// 从系统中选择项目 无需传递路径 // 从系统中选择项目 无需传递路径
return (dispatch, getState) => { return (dispatch, getState) => {
if (path) { if (path) {
readProject(path, title, getState, type)(dispatch) readProject(path, title, getState, type)(dispatch);
} else { } else {
const config = getState()?.config?.data[0] const config = getState()?.config?.data[0];
openProjectFilePath(allLangData[config.lang].invalidProjectFile, suffix).then((filePath) => { openProjectFilePath(allLangData[config.lang].invalidProjectFile, suffix)
readProject(filePath, title, getState, type, isDemoProject)(dispatch) .then((filePath) => {
}).catch((err) => { readProject(filePath, title, getState, type, isDemoProject)(dispatch);
dispatch(readProjectFail(err)) })
}) .catch((err) => {
dispatch(readProjectFail(err));
});
} }
} };
} };

View File

@ -1,4 +1,3 @@
export default { export default {
system: { system: {
title: 'PDManer', title: 'PDManer',
@ -7,7 +6,7 @@ export default {
errorMessage: '程序出现异常,请前往日志文件查看出错日志:', errorMessage: '程序出现异常,请前往日志文件查看出错日志:',
}, },
toolbar: { toolbar: {
go:'执行', go: '执行',
save: '保存', save: '保存',
refresh: '刷新', refresh: '刷新',
saveAs: '另存为', saveAs: '另存为',
@ -49,7 +48,7 @@ export default {
exportPath: '导出文件位于:"{path}"', exportPath: '导出文件位于:"{path}"',
setting: '设置', setting: '设置',
dbConnect: '数据库', dbConnect: '数据库',
dbConnectTest:"数据库连接", dbConnectTest: '数据库连接',
search: '数据表/视图/数据字典', search: '数据表/视图/数据字典',
create: '新建', create: '新建',
open: '打开', open: '打开',
@ -153,7 +152,8 @@ export default {
emptyField: '<空字段>', emptyField: '<空字段>',
entityUniqueKeyError: '数据表中包含重复字段,请修改,重复字段有[{entities}]', entityUniqueKeyError: '数据表中包含重复字段,请修改,重复字段有[{entities}]',
entityUniqueDefKeyError: '数据表或视图已经存在,请修改,重复数据表或视图有[{entities}]', entityUniqueDefKeyError: '数据表或视图已经存在,请修改,重复数据表或视图有[{entities}]',
entityHideInGraphSizeError: '逻辑图显示字段请选择能够代表本表业务含义的典型属性字段,限制为[{size}]个,超限数据表有[{entities}],如需修改,请通过:"设置->系统参数->逻辑图最大展示字段数"进行修改', entityHideInGraphSizeError:
'逻辑图显示字段请选择能够代表本表业务含义的典型属性字段,限制为[{size}]个,超限数据表有[{entities}],如需修改,请通过:"设置->系统参数->逻辑图最大展示字段数"进行修改',
modify: '前往修改', modify: '前往修改',
formValidateMessage: '带星号为必输项,不能为空!', formValidateMessage: '带星号为必输项,不能为空!',
defKeyValidateMessage: '{name}代码不能为空', defKeyValidateMessage: '{name}代码不能为空',
@ -422,6 +422,23 @@ export default {
search: '搜索数据表', search: '搜索数据表',
selectEntityEmpty: '请选择数据表', selectEntityEmpty: '请选择数据表',
}, },
tableExportHeaders: {
defKey: '字段代码',
defName: '显示名称',
primaryKey: '主键',
notNull: '不为空',
autoIncrement: '自增',
domain: '数据域',
hideInGraph: '显示与逻辑图',
type: '数据类型',
len: '长度',
scale: '小数位数',
comment: '说明',
refDict: '数据字典',
defaultValue: '默认值',
uiHint: 'UI建议',
extProps: '拓展属性',
},
tableHeaders: { tableHeaders: {
defName: '显示名称', defName: '显示名称',
defKey: '字段代码', defKey: '字段代码',
@ -672,7 +689,8 @@ export default {
success: '配置成功', success: '配置成功',
error: '配置失败', error: '配置失败',
placeholder: '不填将自动从系统的环境变量中读取', placeholder: '不填将自动从系统的环境变量中读取',
notFoundJDK: '未检测到JDK请先安装JDK(如果已经安装,请检查环境变量是否配置正确或者前往系统【设置】-> 【系统参数】->【JAVA_HOME】指定JDK路径)', notFoundJDK:
'未检测到JDK请先安装JDK(如果已经安装,请检查环境变量是否配置正确或者前往系统【设置】-> 【系统参数】->【JAVA_HOME】指定JDK路径)',
outOfMemoryError: 'JVM所需内存不足请前往[设置=>系统参数=>JVM参数]调整', outOfMemoryError: 'JVM所需内存不足请前往[设置=>系统参数=>JVM参数]调整',
}, },
DictSQLTemplate: '数据字典SQL模板', DictSQLTemplate: '数据字典SQL模板',
@ -733,7 +751,8 @@ export default {
delete: '删除', delete: '删除',
copy: '复制', copy: '复制',
demoDbConnect: { demoDbConnect: {
mysql: 'jdbc:mysql://IP地址:端口号/数据库名?characterEncoding=UTF-8&useSSL=false&useUnicode=true&serverTimezone=UTC', mysql:
'jdbc:mysql://IP地址:端口号/数据库名?characterEncoding=UTF-8&useSSL=false&useUnicode=true&serverTimezone=UTC',
oracle: 'jdbc:oracle:thin:@IP地址:端口号/数据库名', oracle: 'jdbc:oracle:thin:@IP地址:端口号/数据库名',
sqlserver: 'jdbc:sqlserver://IP地址:端口号;DatabaseName=数据库名', sqlserver: 'jdbc:sqlserver://IP地址:端口号;DatabaseName=数据库名',
sqlserverTitle: '官方驱动默认支持java8如果您是其他版本的JDK请自定义驱动', sqlserverTitle: '官方驱动默认支持java8如果您是其他版本的JDK请自定义驱动',
@ -742,8 +761,10 @@ export default {
dm: 'jdbc:dm://IP地址:端口号/数据库名', dm: 'jdbc:dm://IP地址:端口号/数据库名',
guassdb: 'jdbc:postgresql://IP地址:端口号/数据库名', guassdb: 'jdbc:postgresql://IP地址:端口号/数据库名',
kingbase: 'jdbc:kingbase8://IP地址:端口号/数据库名', kingbase: 'jdbc:kingbase8://IP地址:端口号/数据库名',
maxcompute: 'jdbc:odps:http://IP地址:端口号/api?project=项目名&accessId=ACCESS_ID&accessKey=ACCESS_KEY', maxcompute:
sqlite: 'jdbc:sqlite:/path/db-file-name (MAC以及Linux) jdbc:sqlite://path/db-file-name (Windows)', 'jdbc:odps:http://IP地址:端口号/api?project=项目名&accessId=ACCESS_ID&accessKey=ACCESS_KEY',
sqlite:
'jdbc:sqlite:/path/db-file-name (MAC以及Linux) jdbc:sqlite://path/db-file-name (Windows)',
}, },
configExample: '配置示例:', configExample: '配置示例:',
connectSuccess: '连接成功', connectSuccess: '连接成功',
@ -769,10 +790,12 @@ export default {
parseDb: '正在解析数据库,请稍后。。。(请勿关闭当前弹窗!)', parseDb: '正在解析数据库,请稍后。。。(请勿关闭当前弹窗!)',
parseDbError: '数据库解析失败', parseDbError: '数据库解析失败',
selectEntity: '选择数据表', selectEntity: '选择数据表',
dealResult: '解析结果:共解析出[{data}]张数据表,当前模型中已经存在的有[{exists}]张表,请勾选需要添加到模型中的数据表!', dealResult:
'解析结果:共解析出[{data}]张数据表,当前模型中已经存在的有[{exists}]张表,请勾选需要添加到模型中的数据表!',
}, },
importDb: { importDb: {
dealResult: '解析结果:共解析出[{data}]张数据表,当前模型中已经存在的有[{exists}]张表,请勾选需要添加到模型中的数据表!', dealResult:
'解析结果:共解析出[{data}]张数据表,当前模型中已经存在的有[{exists}]张表,请勾选需要添加到模型中的数据表!',
}, },
group: { group: {
base: '基本信息', base: '基本信息',
@ -853,4 +876,4 @@ export default {
viewList: '视图', viewList: '视图',
current: '数据库类型:', current: '数据库类型:',
}, },
} };

View File

@ -1,259 +1,396 @@
import React, { useState, forwardRef, useImperativeHandle, useRef, useEffect } from 'react' import { executeSql } from '@/services/api.ts';
import { FormatMessage, GroupIcon, Icon, SearchSuggest, Slider, NumberInput, Modal } from '../../components' import { Divider, Upload } from 'antd';
import { SketchPicker } from 'react-color' import numeral from 'numeral';
import numeral from 'numeral' import React, { forwardRef, useImperativeHandle, useRef, useState } from 'react';
import { validateNeedSave } from '../../lib/datasource_util' import { SketchPicker } from 'react-color';
import { Divider, message } from 'antd' import {
import { executeSql } from '@/services/api.ts' FormatMessage,
import { getAllDataSQLByFilter } from '../../lib/json2code_util' GroupIcon,
import './style/index.less' Icon,
const GroupIconGroup = GroupIcon.GroupIconGroup Modal,
NumberInput,
SearchSuggest,
Slider,
} from '../../components';
import { validateNeedSave } from '../../lib/datasource_util';
import { getAllDataSQLByFilter } from '../../lib/json2code_util';
import './style/index.less';
const GroupIconGroup = GroupIcon.GroupIconGroup;
export default React.memo(forwardRef(({ currentPrefix, close, iconClick, colorChange, openModal, templateType, export default React.memo(
activeTab, resize, sliderChange, dataSource, forwardRef(
jumpPosition, jumpDetail }, ref) => { (
const dbConn = dataSource?.dbConn || [] {
const properties = dbConn[0]?.properties currentPrefix,
const [isCellSelected, setIsCellSelected] = useState(false) close,
const [scaleNumber, setScaleNumber] = useState(1) iconClick,
const [colorState, setColor] = useState({ colorChange,
fontColor: 'rgba(0, 0, 0, 0.65)', openModal,
fillColor: '#ACDAFC', templateType,
}) activeTab,
useImperativeHandle(ref, () => { resize,
return { sliderChange,
setScaleNumber, dataSource,
setIsCellSelected, restProps,
} jumpPosition,
}, []) jumpDetail,
const _colorChange = (key, value) => { },
setColor((pre) => { ref,
return { ) => {
...pre, const dbConn = dataSource?.dbConn || [];
[key]: value.hex, const properties = dbConn[0]?.properties;
} const [isCellSelected, setIsCellSelected] = useState(false);
}) const [scaleNumber, setScaleNumber] = useState(1);
colorChange && colorChange(key, value) const [colorState, setColor] = useState({
} fontColor: 'rgba(0, 0, 0, 0.65)',
const _close = () => { fillColor: '#ACDAFC',
if (validateNeedSave(dataSource)) { });
Modal.confirm({ const [fileList, setFileList] = useState([]);
title: FormatMessage.string({ id: 'closeConfirmTitle' }),
message: FormatMessage.string({ id: 'closeConfirm' }), useImperativeHandle(
onOk: () => { ref,
close() () => {
return {
setScaleNumber,
setIsCellSelected,
};
}, },
}) [],
} else { );
close() const _colorChange = (key, value) => {
} setColor((pre) => {
} return {
const defaultTemplate = ['createTable', 'createIndex', 'content'] ...pre,
const templateRef = useRef(defaultTemplate) [key]: value.hex,
const dataTypeSupports = _.get(dataSource, 'profile.dataTypeSupports', []) };
const defaultDb = _.get(dataSource, 'profile.default.db', dataTypeSupports[0]?.id) });
const [codeData, setCodeData] = useState(() => { colorChange && colorChange(key, value);
return getAllDataSQLByFilter(dataSource, };
templateType === 'dict' ? 'dictSQLTemplate' : defaultDb, templateRef.current) const _close = () => {
}) if (validateNeedSave(dataSource)) {
Modal.confirm({
title: FormatMessage.string({ id: 'closeConfirmTitle' }),
message: FormatMessage.string({ id: 'closeConfirm' }),
onOk: () => {
close();
},
});
} else {
close();
}
};
const handleExportDataBase = () => {
const { entities, domains } = dataSource;
const workBook = window.XLSX.utils.book_new();
for (let index = 0; index < entities.length; index++) {
const data = entities[index];
const headers = data.headers
.filter((item) => item.refKey !== 'isStandard')
.map((item) => FormatMessage.string({ id: `tableExportHeaders.${item.refKey}` }));
const keyList = data.headers
.filter((item) => item.refKey !== 'isStandard')
.map((item) => item.refKey);
const tableColumnData = data.fields
.map((item) => {
// 判断为哪个数据域字典
const {
len,
defName: domain,
scale,
uiHint,
} = domains.find((domain) => domain.id === item.domain);
const backGroup = async () => { // 数据字典处理
if (!properties) { const obj = { ...item, len, domain, scale, uiHint };
Modal.error({ if (item.defKey.indexOf('time') === -1) {
title: FormatMessage.string({ id: 'dbConnect.connectError' }), obj.type = 'VARCHAR';
message: '请连接数据库', } else {
}) obj.type = 'DATETIME';
} else { }
const data = { return obj;
driverName: properties.driver_class_name, })
url: properties.url, .map((item) => {
username: properties.username, return keyList.map((key) =>
password: properties.password, typeof item[key] === 'boolean' ? (item[key] ? '是' : '否') : item[key],
sql: codeData );
} });
const res = await executeSql(data) tableColumnData.unshift(headers);
if (res.code == 200) { const workSheet = window.XLSX.utils.aoa_to_sheet(tableColumnData);
Modal.success({
title: FormatMessage.string({ id: 'dbConnect.connectSuccess' }),
message: `${res.msg}`,
})
} else {
Modal.error({
bodyStyle: { width: '50%' },
contentStyle: { width: '100%', height: '100%' },
title: FormatMessage.string({ id: 'dbConnect.connectError' }),
message: `${res.msg}`
})
}
}
} if (!workSheet['!cols']) workSheet['!cols'] = [];
const [issee, setIssee] = useState(false) for (let i = 0; i <= 21; i += 1) {
const [isfill, setIsfill] = useState(false) if (i === 0 || i === 1) {
const fill = () => { workSheet['!cols'][i] = { wpx: 260 };
setIsfill(!isfill) } else {
setIssee(false) workSheet['!cols'][i] = { wpx: 150 };
}
const see = () => {
setIsfill(false)
setIssee(!issee)
}
return (
<div className={`${currentPrefix}-head`}>
<div className={`${currentPrefix}-head-logo`}>
<div className={`${currentPrefix}-head-logo-opt`}>
<span>
<Icon type="fa-angle-left" onClick={_close} />
</span>
<div>
<span>{dataSource.name}</span>
<span>{dataSource.describe}</span>
</div>
</div>
</div>
<Divider className='divider' />
<div className={`${currentPrefix}-iconstyle`}>
<GroupIconGroup>
<GroupIcon
className={`${currentPrefix}-icongroup`}
hoverTitle={FormatMessage.string({ id: 'toolbar.save' })}
dropType="icon"
groupKey="save"
icon="icon-bianzu2"
onClick={iconClick}
/>
<GroupIcon
className={`${currentPrefix}-icongroup`}
hoverTitle={FormatMessage.string({ id: 'toolbar.refresh' })}
onClick={() => iconClick(null, 'refresh')}
icon="fa-refresh"
/>
<GroupIcon
className={`${currentPrefix}-icongroup`}
hoverTitle={FormatMessage.string({ id: 'toolbar.undo' })}
onClick={() => iconClick(null, 'undo')}
icon="icon-bianzu4"
disable={activeTab?.type !== 'diagram'}
/>
<GroupIcon
className={`${currentPrefix}-icongroup`}
onClick={() => iconClick(null, 'redo')}
hoverTitle={FormatMessage.string({ id: 'toolbar.redo' })}
icon="icon-bianzu3"
disable={activeTab?.type !== 'diagram'}
/>
</GroupIconGroup>
<GroupIconGroup>
<GroupIcon
className={`${currentPrefix}-icongroup`}
hoverTitle={FormatMessage.string({ id: 'toolbar.emptyEntity' })}
icon="icon-kongbiao"
disable={activeTab?.type !== 'diagram'}
//style={{cursor: 'move'}}
onClick={iconClick}
groupKey="empty"
//onMouseDown={e => iconClick(e, 'empty')}
/>
<GroupIcon
className={`${currentPrefix}-icongroup`}
hoverTitle={FormatMessage.string({ id: 'toolbar.group' })}
icon="fa-object-group"
//style={{cursor: 'move'}}
onClick={iconClick}
groupKey="group"
disable={activeTab?.type !== 'diagram'}
//onMouseDown={e => iconClick(e, 'group')}
/>
<GroupIcon
className={`${currentPrefix}-icongroup`}
topStyle={{ height: '24px' }}
dropType="icon"
groupKey="rect"
disable={activeTab?.type !== 'diagram'}
hoverTitle={FormatMessage.string({ id: 'toolbar.rect' })}
icon="fa-square-o"
onClick={iconClick}
dropMenu={[
{ key: 'round', name: FormatMessage.string({ id: 'toolbar.round' }) },
{ key: 'circle', name: FormatMessage.string({ id: 'toolbar.circle' }) },
]}
/>
<GroupIcon
className={`${currentPrefix}-icongroup`}
hoverTitle={FormatMessage.string({ id: 'toolbar.polygon' })}
//style={{cursor: 'move'}}
icon={
<div className={`${currentPrefix}-head-rect`}>
<span>{ }</span>
</div>
} }
groupKey="polygon" }
onClick={iconClick} window.XLSX.utils.book_append_sheet(workBook, workSheet, data.defName);
disable={activeTab?.type !== 'diagram'} }
//onMouseDown={e => iconClick(e, 'polygon')}
/>
<div className='fontColor1'> window.XLSX.writeFile(workBook, '数据表.xlsx');
<Icon type="icon-zitiyanse" onClick={see} };
disable={activeTab?.type !== 'diagram' || !isCellSelected} title="字体颜色" /> const defaultTemplate = ['createTable', 'createIndex', 'content'];
{/* display: issee ? 'block' : 'none' */} const templateRef = useRef(defaultTemplate);
<div style={{ display: activeTab?.type == 'diagram' && issee && isCellSelected ? 'block' : 'none' }} className='fontColor2' > const dataTypeSupports = _.get(dataSource, 'profile.dataTypeSupports', []);
<SketchPicker const defaultDb = _.get(dataSource, 'profile.default.db', dataTypeSupports[0]?.id);
disableAlpha const [codeData, setCodeData] = useState(() => {
presetColors={[ return getAllDataSQLByFilter(
'#FFFFFF', dataSource,
'#BFBFBF', templateType === 'dict' ? 'dictSQLTemplate' : defaultDb,
'#C00000', templateRef.current,
'#FFC000', );
'#F6941D', });
'#7030A0',
'#136534', const backGroup = async () => {
'#0070C0', if (!properties) {
'#0D0D0D', Modal.error({
'#6698CC', title: FormatMessage.string({ id: 'dbConnect.connectError' }),
'#FA5A5A', message: '请连接数据库',
'#FFD966', });
'#F8CBAD', } else {
'#CB99C5', const data = {
'#9ACC98', driverName: properties.driver_class_name,
'#093299', url: properties.url,
]} username: properties.username,
color={colorState.fontColor} password: properties.password,
onChange={(color) => _colorChange('fontColor', color)} sql: codeData,
/> };
const res = await executeSql(data);
if (res.code == 200) {
Modal.success({
title: FormatMessage.string({ id: 'dbConnect.connectSuccess' }),
message: `${res.msg}`,
});
} else {
Modal.error({
bodyStyle: { width: '50%' },
contentStyle: { width: '100%', height: '100%' },
title: FormatMessage.string({ id: 'dbConnect.connectError' }),
message: `${res.msg}`,
});
}
}
};
const [issee, setIssee] = useState(false);
const [isfill, setIsfill] = useState(false);
const fill = () => {
setIsfill(!isfill);
setIssee(false);
};
const see = () => {
setIsfill(false);
setIssee(!issee);
};
const props = {
maxCount: 1,
accept: 'application/vnd.ms-excel',
beforeUpload: (file) => {
new Promise((resolve, reject) => {
const reader = new FileReader();
reader.onload = (e) => {
resolve(e.target.result);
};
reader.onerror = reject;
reader.readAsBinaryString(file);
})
.then((fileData) => {
// 解析XLSX
return XLSX.read(fileData, { type: 'binary' });
})
.then((workBook) => {
const { entities, domains } = dataSource;
const keyList = entities[0].headers
.filter((item) => item.refKey !== 'isStandard')
.map((item) => item.refKey);
let data = workBook.Sheets.forEach((sheet) => {
window.XLSX.utils.sheet_to_json(workBook.Sheets[sheet], {
headers: keyList,
});
});
});
return false;
},
fileList,
};
return (
<div className={`${currentPrefix}-head`}>
<div className={`${currentPrefix}-head-logo`}>
<div className={`${currentPrefix}-head-logo-opt`}>
<span>
<Icon type="fa-angle-left" onClick={_close} />
</span>
<div>
<span>{dataSource.name}</span>
<span>{dataSource.describe}</span>
</div>
</div> </div>
</div> </div>
<div className='fontColor1'> <Divider className="divider" />
<Icon type="icon-tianchongyanse" onClick={fill} disable={activeTab?.type !== 'diagram' || !isCellSelected} title="填充颜色" /> <div className={`${currentPrefix}-iconstyle`}>
<div style={{ display: activeTab?.type == 'diagram' && isfill && isCellSelected ? 'block' : 'none' }} className='fontColor2' > <GroupIconGroup>
<SketchPicker <GroupIcon
disableAlpha className={`${currentPrefix}-icongroup`}
presetColors={[ hoverTitle={FormatMessage.string({ id: 'toolbar.save' })}
'#FFFFFF', dropType="icon"
'#BFBFBF', groupKey="save"
'#C00000', icon="icon-bianzu2"
'#FFC000', onClick={iconClick}
'#F6941D',
'#7030A0',
'#136534',
'#0070C0',
'#0D0D0D',
'#6698CC',
'#FA5A5A',
'#FFD966',
'#F8CBAD',
'#CB99C5',
'#9ACC98',
'#093299',
]}
color={colorState.fillColor}
onChange={(color) => _colorChange('fillColor', color)}
/> />
</div> <GroupIcon
</div> className={`${currentPrefix}-icongroup`}
{/* <GroupIcon hoverTitle={FormatMessage.string({ id: 'toolbar.refresh' })}
onClick={() => iconClick(null, 'refresh')}
icon="fa-refresh"
/>
<GroupIcon
className={`${currentPrefix}-icongroup`}
hoverTitle={FormatMessage.string({ id: 'toolbar.undo' })}
onClick={() => iconClick(null, 'undo')}
icon="icon-bianzu4"
disable={activeTab?.type !== 'diagram'}
/>
<GroupIcon
className={`${currentPrefix}-icongroup`}
onClick={() => iconClick(null, 'redo')}
hoverTitle={FormatMessage.string({ id: 'toolbar.redo' })}
icon="icon-bianzu3"
disable={activeTab?.type !== 'diagram'}
/>
</GroupIconGroup>
<GroupIconGroup>
<GroupIcon
className={`${currentPrefix}-icongroup`}
hoverTitle={FormatMessage.string({ id: 'toolbar.emptyEntity' })}
icon="icon-kongbiao"
disable={activeTab?.type !== 'diagram'}
//style={{cursor: 'move'}}
onClick={iconClick}
groupKey="empty"
//onMouseDown={e => iconClick(e, 'empty')}
/>
<GroupIcon
className={`${currentPrefix}-icongroup`}
hoverTitle={FormatMessage.string({ id: 'toolbar.group' })}
icon="fa-object-group"
//style={{cursor: 'move'}}
onClick={iconClick}
groupKey="group"
disable={activeTab?.type !== 'diagram'}
//onMouseDown={e => iconClick(e, 'group')}
/>
<GroupIcon
className={`${currentPrefix}-icongroup`}
topStyle={{ height: '24px' }}
dropType="icon"
groupKey="rect"
disable={activeTab?.type !== 'diagram'}
hoverTitle={FormatMessage.string({ id: 'toolbar.rect' })}
icon="fa-square-o"
onClick={iconClick}
dropMenu={[
{ key: 'round', name: FormatMessage.string({ id: 'toolbar.round' }) },
{ key: 'circle', name: FormatMessage.string({ id: 'toolbar.circle' }) },
]}
/>
<GroupIcon
className={`${currentPrefix}-icongroup`}
hoverTitle={FormatMessage.string({ id: 'toolbar.polygon' })}
//style={{cursor: 'move'}}
icon={
<div className={`${currentPrefix}-head-rect`}>
<span>{}</span>
</div>
}
groupKey="polygon"
onClick={iconClick}
disable={activeTab?.type !== 'diagram'}
//onMouseDown={e => iconClick(e, 'polygon')}
/>
<div className="fontColor1">
<Icon
type="icon-zitiyanse"
onClick={see}
disable={activeTab?.type !== 'diagram' || !isCellSelected}
title="字体颜色"
/>
{/* display: issee ? 'block' : 'none' */}
<div
style={{
display:
activeTab?.type == 'diagram' && issee && isCellSelected ? 'block' : 'none',
}}
className="fontColor2"
>
<SketchPicker
disableAlpha
presetColors={[
'#FFFFFF',
'#BFBFBF',
'#C00000',
'#FFC000',
'#F6941D',
'#7030A0',
'#136534',
'#0070C0',
'#0D0D0D',
'#6698CC',
'#FA5A5A',
'#FFD966',
'#F8CBAD',
'#CB99C5',
'#9ACC98',
'#093299',
]}
color={colorState.fontColor}
onChange={(color) => _colorChange('fontColor', color)}
/>
</div>
</div>
<div className="fontColor1">
<Icon
type="icon-tianchongyanse"
onClick={fill}
disable={activeTab?.type !== 'diagram' || !isCellSelected}
title="填充颜色"
/>
<div
style={{
display:
activeTab?.type == 'diagram' && isfill && isCellSelected ? 'block' : 'none',
}}
className="fontColor2"
>
<SketchPicker
disableAlpha
presetColors={[
'#FFFFFF',
'#BFBFBF',
'#C00000',
'#FFC000',
'#F6941D',
'#7030A0',
'#136534',
'#0070C0',
'#0D0D0D',
'#6698CC',
'#FA5A5A',
'#FFD966',
'#F8CBAD',
'#CB99C5',
'#9ACC98',
'#093299',
]}
color={colorState.fillColor}
onChange={(color) => _colorChange('fillColor', color)}
/>
</div>
</div>
{/* <GroupIcon
className={`${currentPrefix}-icongroup`} className={`${currentPrefix}-icongroup`}
hoverTitle={FormatMessage.string({ id: 'toolbar.fontColor' })} hoverTitle={FormatMessage.string({ id: 'toolbar.fontColor' })}
icon={ icon={
@ -294,7 +431,7 @@ export default React.memo(forwardRef(({ currentPrefix, close, iconClick, colorCh
} }
disable={activeTab?.type !== 'diagram' || !isCellSelected} disable={activeTab?.type !== 'diagram' || !isCellSelected}
/> */} /> */}
{/* <GroupIcon {/* <GroupIcon
className={`${currentPrefix}-icongroup`} className={`${currentPrefix}-icongroup`}
hoverTitle={FormatMessage.string({ id: 'toolbar.fillColor' })} hoverTitle={FormatMessage.string({ id: 'toolbar.fillColor' })}
icon="icon-tianchongyanse" icon="icon-tianchongyanse"
@ -329,43 +466,43 @@ export default React.memo(forwardRef(({ currentPrefix, close, iconClick, colorCh
} }
disable={activeTab?.type !== 'diagram' || !isCellSelected} disable={activeTab?.type !== 'diagram' || !isCellSelected}
/> */} /> */}
</GroupIconGroup> </GroupIconGroup>
<GroupIconGroup> <GroupIconGroup>
<GroupIcon <GroupIcon
className={`${currentPrefix}-icongroup`} className={`${currentPrefix}-icongroup`}
disable={activeTab?.type !== 'diagram'} disable={activeTab?.type !== 'diagram'}
hoverTitle={FormatMessage.string({ id: 'toolbar.scale' })} hoverTitle={FormatMessage.string({ id: 'toolbar.scale' })}
icon={ icon={
<> <>
<span <span
className={`${currentPrefix}-head-scale className={`${currentPrefix}-head-scale
${currentPrefix}-head-scale-${activeTab?.type !== 'diagram' ? 'disable' : 'normal'}`} ${currentPrefix}-head-scale-${activeTab?.type !== 'diagram' ? 'disable' : 'normal'}`}
> >
<Icon type="fa-minus" onClick={() => resize(-0.1)} /> <Icon type="fa-minus" onClick={() => resize(-0.1)} />
<span> <span>
<NumberInput <NumberInput
readOnly={activeTab?.type !== 'diagram'} readOnly={activeTab?.type !== 'diagram'}
onBlur={(e) => sliderChange(numeral(e.target.value).divide(2).value())} onBlur={(e) => sliderChange(numeral(e.target.value).divide(2).value())}
min={0} min={0}
max={200} max={200}
value={parseInt(numeral(scaleNumber).multiply(100).value(), 10)} value={parseInt(numeral(scaleNumber).multiply(100).value(), 10)}
formatter={(value) => `${value}%`} formatter={(value) => `${value}%`}
parser={(value) => value.replace('%', '')} parser={(value) => value.replace('%', '')}
/> />
</span> </span>
<Icon type="fa-plus " onClick={() => resize(0.1)} /> <Icon type="fa-plus " onClick={() => resize(0.1)} />
</span> </span>
<div> <div>
<Slider <Slider
disable={activeTab?.type !== 'diagram'} disable={activeTab?.type !== 'diagram'}
onChange={sliderChange} onChange={sliderChange}
value={numeral(scaleNumber).multiply(50).value()} value={numeral(scaleNumber).multiply(50).value()}
/> />
</div> </div>
</> </>
} }
/> />
{/* <GroupIcon {/* <GroupIcon
className={`${currentPrefix}-icongroup`} className={`${currentPrefix}-icongroup`}
hoverTitle={FormatMessage.string({ id: 'toolbar.import' })} hoverTitle={FormatMessage.string({ id: 'toolbar.import' })}
onClick={iconClick} onClick={iconClick}
@ -422,34 +559,51 @@ export default React.memo(forwardRef(({ currentPrefix, close, iconClick, colorCh
}, },
]} ]}
/> */} /> */}
{/* <GroupIcon {/* <GroupIcon
className={`${currentPrefix}-icongroup`} className={`${currentPrefix}-icongroup`}
hoverTitle={FormatMessage.string({ id: 'toolbar.setting' })} hoverTitle={FormatMessage.string({ id: 'toolbar.setting' })}
icon="icon-shezhi" icon="icon-shezhi"
onClick={() => openModal('config')} onClick={() => openModal('config')}
/> */} /> */}
<GroupIcon <GroupIcon
className={`${currentPrefix}-icongroup`} className={`${currentPrefix}-icongroup`}
hoverTitle={FormatMessage.string({ id: 'toolbar.dbConnectTest' })} hoverTitle={FormatMessage.string({ id: 'toolbar.dbConnectTest' })}
icon="fa-database" icon="fa-database"
onClick={() => openModal('dbConnect')} onClick={() => openModal('dbConnect')}
/> />
<GroupIcon <GroupIcon
className={`${currentPrefix}-icongroup`} className={`${currentPrefix}-icongroup`}
hoverTitle={FormatMessage.string({ id: 'toolbar.go' })} hoverTitle={FormatMessage.string({ id: 'toolbar.go' })}
icon="fa-toggle-right" icon="fa-toggle-right"
onClick={backGroup} onClick={backGroup}
/> />
</GroupIconGroup>
<div className={`${currentPrefix}-head-search`}> <GroupIcon
<SearchSuggest className={`${currentPrefix}-icongroup`}
jumpPosition={jumpPosition} onClick={() => handleExportDataBase()}
jumpDetail={jumpDetail} hoverTitle={FormatMessage.string({ id: 'toolbar.export' })}
placeholder={FormatMessage.string({ id: 'toolbar.search' })} icon={<Icon type="icon-daochu" />}
dataSource={dataSource} />
/> <Upload fileList={[]} {...props}>
<GroupIcon
className={`${currentPrefix}-icongroup`}
hoverTitle={FormatMessage.string({ id: 'toolbar.import' })}
icon={<Icon type="icon-daoru" />}
/>
</Upload>
</GroupIconGroup>
<div className={`${currentPrefix}-head-search`}>
<SearchSuggest
jumpPosition={jumpPosition}
jumpDetail={jumpDetail}
placeholder={FormatMessage.string({ id: 'toolbar.search' })}
dataSource={dataSource}
/>
</div>
</div>
</div> </div>
</div> );
</div> },
) ),
})) );

File diff suppressed because it is too large Load Diff

View File

@ -14,6 +14,7 @@ import {
saveProject, saveProject,
saveVersionData, saveVersionData,
updateAllVersionData, updateAllVersionData,
updateDataSource,
updateProject, updateProject,
} from '../../actions/core'; } from '../../actions/core';
import { ConfigContent } from '../../lib/context'; import { ConfigContent } from '../../lib/context';
@ -24,7 +25,7 @@ import './style/index.less';
const Welcome = React.memo(({ prefix, getUserData, config, ...restProps }) => { const Welcome = React.memo(({ prefix, getUserData, config, ...restProps }) => {
const currentPrefix = getPrefix(prefix); const currentPrefix = getPrefix(prefix);
return ( return (
<div className={`${currentPrefix}-welcome`} > <div className={`${currentPrefix}-welcome`}>
{ {
<ConfigContent.Provider value={config}> <ConfigContent.Provider value={config}>
<Home config={config} {...restProps} /> <Home config={config} {...restProps} />
@ -48,6 +49,9 @@ const mapDispatchToProps = (dispatch) => {
getUserData: (title) => { getUserData: (title) => {
dispatch(getUserConfigData(title)); dispatch(getUserConfigData(title));
}, },
updateData: (data) => {
dispatch(saveUserConfigSome(data));
},
saveUserData: (data) => { saveUserData: (data) => {
dispatch(saveUserConfigSome(data)); dispatch(saveUserConfigSome(data));
}, },
@ -103,6 +107,10 @@ const mapDispatchToProps = (dispatch) => {
updateAllVersion: (versionData, title, dataSource) => { updateAllVersion: (versionData, title, dataSource) => {
return dispatch(updateAllVersionData(versionData, title, dataSource)); return dispatch(updateAllVersionData(versionData, title, dataSource));
}, },
updateDataSource: (dataSource) => {
console.log(dataSource);
return dispatch(updateDataSource(dataSource));
},
}; };
}; };
export default connect(mapStateToProps, mapDispatchToProps)(Welcome); export default connect(mapStateToProps, mapDispatchToProps)(Welcome);

View File

@ -1,9 +1,9 @@
import { createIcon } from '@/utils/IconUtil'; import { createIcon } from '@/utils/IconUtil';
import request from '@/utils/request' import request from '@/utils/request';
import type { MenuDataItem } from '@umijs/route-utils'; import type { MenuDataItem } from '@umijs/route-utils';
/** 获取当前的用户 GET /api/getUserInfo */ /** 获取当前的用户 GET /api/getUserInfo */
export async function getUserInfo (options?: Record<string, any>) { export async function getUserInfo(options?: Record<string, any>) {
return request<API.GetUserInfoResult>('/api/getInfo', { return request<API.GetUserInfoResult>('/api/getInfo', {
method: 'GET', method: 'GET',
...(options || {}), ...(options || {}),
@ -11,14 +11,13 @@ export async function getUserInfo (options?: Record<string, any>) {
} }
/** 退出登录接口 POST /api/login/outLogin */ /** 退出登录接口 POST /api/login/outLogin */
export async function logout (options?: Record<string, any>) { export async function logout(options?: Record<string, any>) {
return request<Record<string, any>>('/api/logout', { return request<Record<string, any>>('/api/logout', {
method: 'POST', method: 'POST',
...(options || {}), ...(options || {}),
}); });
} }
export async function getRouters(): Promise<API.GetRoutersResult> { export async function getRouters(): Promise<API.GetRoutersResult> {
return request('/api/getRouters'); return request('/api/getRouters');
} }
@ -44,9 +43,11 @@ export async function getRoutersInfo(): Promise<MenuDataItem[]> {
}); });
} }
export function getMatchMenuItem(path: string, menuData: MenuDataItem[]|undefined): MenuDataItem[] { export function getMatchMenuItem(
if(!menuData) path: string,
return []; menuData: MenuDataItem[] | undefined,
): MenuDataItem[] {
if (!menuData) return [];
let items: MenuDataItem[] = []; let items: MenuDataItem[] = [];
menuData.forEach((item) => { menuData.forEach((item) => {
if (item.path) { if (item.path) {
@ -57,13 +58,13 @@ export function getMatchMenuItem(path: string, menuData: MenuDataItem[]|undefine
if (path.length >= item.path?.length) { if (path.length >= item.path?.length) {
const exp = `${item.path}/*`; const exp = `${item.path}/*`;
if (path.match(exp)) { if (path.match(exp)) {
if(item.routes) { if (item.routes) {
const subpath = path.substr(item.path.length+1); const subpath = path.substr(item.path.length + 1);
const subItem: MenuDataItem[] = getMatchMenuItem(subpath, item.routes); const subItem: MenuDataItem[] = getMatchMenuItem(subpath, item.routes);
items = items.concat(subItem); items = items.concat(subItem);
} else { } else {
const paths = path.split('/'); const paths = path.split('/');
if(paths.length >= 2 && paths[0] === item.path && paths[1] === 'index') { if (paths.length >= 2 && paths[0] === item.path && paths[1] === 'index') {
items.push(item); items.push(item);
} }
} }