Compare commits

...

3 Commits

Author SHA1 Message Date
Your Name
a1de697f9d Merge branch 'develop' of ssh://code.linyikj.com.cn:2222/pay/pay-company into develop
All checks were successful
Build and Push Docker Image / build (push) Successful in 4m32s
* 'develop' of ssh://code.linyikj.com.cn:2222/pay/pay-company:
  fix:客户导入、员工创建初始密码

# Conflicts:
#	src/gen/Enums.ts
2026-02-06 17:40:49 +08:00
Your Name
7501a60dc7 fix:更新首页bi 2026-02-06 17:40:04 +08:00
Your Name
96f0ac68de fix:更新 2026-02-04 12:32:17 +08:00
32 changed files with 1156 additions and 158 deletions

View File

@ -31,6 +31,7 @@
"leaflet": "^1.9.4", "leaflet": "^1.9.4",
"lodash": "^4.17.21", "lodash": "^4.17.21",
"react-activation": "^0.13.4", "react-activation": "^0.13.4",
"react-countup": "^6.5.3",
"react-use": "^17.5.1", "react-use": "^17.5.1",
"valtio": "^1.13.2" "valtio": "^1.13.2"
}, },

View File

@ -1,12 +1,13 @@
import { Apis } from '@/gen/Apis'; import { Apis } from '@/gen/Apis';
import { import {
DownOutlined,
LogoutOutlined, LogoutOutlined,
UnlockOutlined, UnlockOutlined,
UserOutlined, UserOutlined,
} from '@ant-design/icons'; } from '@ant-design/icons';
import { history } from '@umijs/max'; import { history } from '@umijs/max';
import { Avatar, Dropdown, MenuProps, Space } from 'antd'; import { Avatar, Dropdown, MenuProps, Space } from 'antd';
import { useState } from 'react'; import { useEffect, useState } from 'react';
import { stateActions } from '../../libs/valtio/actions'; import { stateActions } from '../../libs/valtio/actions';
import AvatarIcon from './AvatarIcon.png'; import AvatarIcon from './AvatarIcon.png';
import ChangePassword from './ChangePassword'; import ChangePassword from './ChangePassword';
@ -41,6 +42,7 @@ export default function AvatarProps({ user }: { user: any }) {
onClick={() => { onClick={() => {
Apis.Common.Auth.Logout().then(() => { Apis.Common.Auth.Logout().then(() => {
stateActions.setLogout(); stateActions.setLogout();
sessionStorage.removeItem('loginUserInfo');
history.push('/login'); history.push('/login');
}); });
}} }}
@ -52,6 +54,15 @@ export default function AvatarProps({ user }: { user: any }) {
}, },
]; ];
useEffect(() => {
let loginUserInfo = sessionStorage.getItem('loginUserInfo');
console.log(loginUserInfo, 'loginUserInfo');
if (loginUserInfo === 'I0xZMDEyM18wMQ==') {
setOpen(true);
console.log('登录成功');
}
}, []);
return ( return (
<> <>
<Dropdown menu={{ items }} trigger={['click']}> <Dropdown menu={{ items }} trigger={['click']}>
@ -59,6 +70,7 @@ export default function AvatarProps({ user }: { user: any }) {
<Space> <Space>
<Avatar icon={<UserOutlined />} src={AvatarIcon} size={28} /> <Avatar icon={<UserOutlined />} src={AvatarIcon} size={28} />
<span>{user?.name}</span> <span>{user?.name}</span>
<DownOutlined />
</Space> </Space>
</a> </a>
</Dropdown> </Dropdown>

View File

@ -1,7 +1,8 @@
import { Apis } from '@/gen/Apis'; import { Apis } from '@/gen/Apis';
import { ModalForm, ProFormText } from '@ant-design/pro-components'; import { ModalForm, ProFormText } from '@ant-design/pro-components';
import { history } from '@umijs/max';
import { message } from 'antd'; import { message } from 'antd';
import { stateActions } from '../../libs/valtio/actions';
export default function ChangePassword({ export default function ChangePassword({
open, open,
setOpen, setOpen,
@ -14,12 +15,17 @@ export default function ChangePassword({
open={open} open={open}
wrapperCol={{ span: 24 }} wrapperCol={{ span: 24 }}
width="500px" width="500px"
title="修改密码" title="修改初始密码"
onFinish={async (values) => { onFinish={async (values) => {
return Apis.Common.Auth.ChangePassword(values) return Apis.Common.Auth.ChangePassword(values)
.then(() => { .then(() => {
message.success('修改密码成功'); message.success('修改密码成功');
Apis.Common.Auth.Logout().then(() => {
stateActions.setLogout();
sessionStorage.removeItem('loginUserInfo');
history.push('/login');
setOpen(false); setOpen(false);
});
}) })
.catch(() => false); .catch(() => false);
}} }}

View File

@ -1,15 +1,16 @@
import { MyIcons, MyIconsType, PermissionsType, useMyState } from '@/common'; import { MyIcons, MyIconsType, PermissionsType, useMyState } from '@/common';
import AvatarProps from '@/common/components/layout/AvatarProps'; import AvatarProps from '@/common/components/layout/AvatarProps';
import { SettingOutlined } from '@ant-design/icons'; import { Apis } from '@/gen/Apis';
import { Link, RuntimeConfig, history, useNavigate } from '@umijs/max'; import { Link, RuntimeConfig, history, useNavigate } from '@umijs/max';
import { AutoComplete, Button, Input, Menu, MenuProps, Space } from 'antd'; import { AutoComplete, Input, Menu, MenuProps, Select, Space } from 'antd';
import { useState } from 'react'; import { useEffect, useState } from 'react';
import './allConfig.scss'; import './allConfig.scss';
// import Logo from './logo.png'; // import Logo from './logo.png';
interface LevelKeysProps { interface LevelKeysProps {
key?: string; key?: string;
children?: LevelKeysProps[]; children?: LevelKeysProps[];
} }
const loopMenu = (permissions: PermissionsType[] | undefined) => { const loopMenu = (permissions: PermissionsType[] | undefined) => {
let tree: PermissionsType[] = []; let tree: PermissionsType[] = [];
let map: Record<number, PermissionsType> = {}; let map: Record<number, PermissionsType> = {};
@ -57,6 +58,7 @@ const loopMenu = (permissions: PermissionsType[] | undefined) => {
}; };
export const LayoutConfig: RuntimeConfig['layout'] = () => { export const LayoutConfig: RuntimeConfig['layout'] = () => {
const [getSelectProject, setSelectProject] = useState<LevelKeysProps[]>([]);
const { snap } = useMyState(); const { snap } = useMyState();
const navigate = useNavigate(); const navigate = useNavigate();
const permissionsList = (snap.session.permissions || []) const permissionsList = (snap.session.permissions || [])
@ -69,12 +71,20 @@ export const LayoutConfig: RuntimeConfig['layout'] = () => {
{ label: '收费Bi', path: '/charge/charge_bi' }, { label: '收费Bi', path: '/charge/charge_bi' },
{ label: '项目Bi', path: '/asset/asset_bi' }, { label: '项目Bi', path: '/asset/asset_bi' },
]; ];
const handleLoadProject = async () => {
let res = await Apis.Common.Auth.GetProjects();
setSelectProject(
res?.data?.map((item) => ({
value: item.id,
label: item.name,
})),
);
};
const [stateOpenKeys, setStateOpenKeys] = useState(['2', '23']); const [stateOpenKeys, setStateOpenKeys] = useState(['2', '23']);
const getLevelKeys: any = (items1: LevelKeysProps[]) => { const getLevelKeys: any = (items1: LevelKeysProps[]) => {
const key: Record<string, number> = {}; const key: Record<string, number> = {};
const func = (items2: LevelKeysProps[], level = 1) => { const func = (items2: LevelKeysProps[], level = 1) => {
console.log(items2, 'level');
items2?.forEach((item) => { items2?.forEach((item) => {
if (item.key) { if (item.key) {
key[item.key] = level; key[item.key] = level;
@ -88,18 +98,22 @@ export const LayoutConfig: RuntimeConfig['layout'] = () => {
return key; return key;
}; };
useEffect(() => {
handleLoadProject('');
}, []);
return { return {
title: '', title: '',
// 首页 logo // 首页 logo
logo: ( logo: (
<div style={{ width: 181 }}> <div style={{ width: 136, display: 'flex', justifyContent: 'center' }}>
<img <img
src={ src={
snap.session?.company_configs?.config_value?.admin_logo snap.session?.company_configs?.config_value?.admin_logo
? snap.session?.company_configs?.config_value?.admin_logo[0]?.url ? snap.session?.company_configs?.config_value?.admin_logo[0]?.url
: '' : ''
} }
style={{ height: '42px' }} style={{ width: 'auto', height: '42px' }}
/> />
</div> </div>
), ),
@ -114,45 +128,29 @@ export const LayoutConfig: RuntimeConfig['layout'] = () => {
<div className="headerContentRender"> <div className="headerContentRender">
<div style={{ display: 'flex', alignItems: 'center', gap: 20 }}> <div style={{ display: 'flex', alignItems: 'center', gap: 20 }}>
<HeaderSearch permissionsList={permissionsList} /> <HeaderSearch permissionsList={permissionsList} />
{/* <Space size={20} style={{ color: '#666' }}>
:
{quickLinks.map((q) => (
<a
key={q.path}
onClick={() => history.push(q.path)}
className="quick_link"
>
{q.label}
</a>
))}
</Space> */}
</div> </div>
<Space size={10}> <Space size={10}>
{/* <Popover {/* <Button
placement="bottom"
title="小程序二维码"
content={
<Space style={{ textAlign: 'center' }} size="large">
<div>
<Image src={ImgEmployeeWxApp} style={{ height: '120px' }} />
<div style={{ marginTop: 10 }}></div>
</div>
<div>
<Image src={ImgCustomerWxApp} style={{ height: '120px' }} />
<div style={{ marginTop: 10 }}></div>
</div>
</Space>
}
>
<Button type="default" shape="circle" icon={<TabletOutlined />} />
</Popover>
<Button type="default" shape="circle" icon={<BellOutlined />} /> */}
<Button
type="default" type="default"
shape="circle" shape="circle"
icon={<SettingOutlined />} icon={<SettingOutlined />}
onClick={() => history.push('/system/sys_permissions')} onClick={() => history.push('/system/sys_permissions')}
/> */}
<Select
onSearch={handleLoadProject}
options={getSelectProject}
allowClear
style={{ width: 160 }}
defaultValue={snap.session.current_project?.id}
onChange={(e: any) => {
Apis.Common.Auth.SwitchProject({
project_id: e ? e?.toString() : 'all',
}).then(() => {
window.location.reload();
});
console.log(e, 'e');
}}
placeholder="选择项目"
/> />
</Space> </Space>
</div> </div>
@ -202,7 +200,6 @@ export const LayoutConfig: RuntimeConfig['layout'] = () => {
const repeatIndex = openKeys const repeatIndex = openKeys
.filter((key) => key !== currentOpenKey) .filter((key) => key !== currentOpenKey)
.findIndex((key) => levelKeys[key] === levelKeys[currentOpenKey]); .findIndex((key) => levelKeys[key] === levelKeys[currentOpenKey]);
setStateOpenKeys( setStateOpenKeys(
openKeys openKeys
// remove repeat key // remove repeat key

View File

@ -41,7 +41,9 @@ export const requestConfig: RequestConfig = {
// 错误接收及处理 // 错误接收及处理
errorHandler: (error: any) => { errorHandler: (error: any) => {
if (error) { if (error) {
if (error.errorMessage !== '用户未登录') {
message.error(error.errorMessage); message.error(error.errorMessage);
}
switch (error.errorCode) { switch (error.errorCode) {
case 10000: case 10000:
if (history.location.pathname !== '/login') history.push('/login'); if (history.location.pathname !== '/login') history.push('/login');

View File

@ -17,6 +17,7 @@ export const stateActions = {
state.session.campus = res.data.campus; state.session.campus = res.data.campus;
state.session.company_configs = res.data.company_configs; state.session.company_configs = res.data.company_configs;
state.session.permissions = res.data.permissions; state.session.permissions = res.data.permissions;
state.session.current_project = res.data.current_project;
if (res.data?.token?.access_token) if (res.data?.token?.access_token)
state.storage.access_token = res.data?.token?.access_token; state.storage.access_token = res.data?.token?.access_token;
// 解析apis // 解析apis

View File

@ -48,6 +48,10 @@ type SessionType = {
permissions?: any; permissions?: any;
apiKeys: string[]; apiKeys: string[];
loading: number; loading: number;
current_project?: {
id: number;
name: string;
};
}; };
const session: SessionType = proxy({ const session: SessionType = proxy({
@ -58,6 +62,7 @@ const session: SessionType = proxy({
company_configs: {}, company_configs: {},
apiKeys: [], apiKeys: [],
loading: 0, loading: 0,
current_project: undefined,
}); });
export const state = proxy({ export const state = proxy({

View File

@ -59,7 +59,10 @@ export function MyLoginPage() {
...values, ...values,
...{ captcha_key: getCaptcha?.key }, ...{ captcha_key: getCaptcha?.key },
}) })
.then(async (res) => { .then(async (res: any) => {
let pass = btoa(`${values?.password}_01`);
console.log(pass, 'pass', values?.password);
sessionStorage.setItem('loginUserInfo', pass);
await stateActions.setLogin(res); await stateActions.setLogin(res);
navigate('/'); navigate('/');
}) })

View File

@ -11,3 +11,41 @@ export const isInTimeRange = (startTime?: string, endTime?: string) => {
export function showTime(time?: string, num?: number) { export function showTime(time?: string, num?: number) {
return time?.substring(0, num || 5) || ''; return time?.substring(0, num || 5) || '';
} }
export function useCurrentDayTime() {
let currentTime = dayjs().format('HH:mm:ss');
let intervalId: NodeJS.Timeout;
const start = () => {
intervalId = setInterval(() => {
currentTime = dayjs().format('HH:mm:ss');
}, 1000);
};
const stop = () => {
if (intervalId) {
clearInterval(intervalId);
}
};
const getTime = () => {
return currentTime;
};
return {
start,
stop,
getTime,
};
}
// 保持原有函数兼容性
export function ShowCurrentDayTime() {
const timeString = dayjs().format('HH:mm:ss');
return timeString;
}
export function GetFromNow(time: string) {
//获取时间距离现在的时间
return time ? dayjs(time).fromNow() : '';
}

View File

@ -27,11 +27,7 @@ export const renderTextHelper = {
let item: any = Object.values(Enums).find((data: any) => { let item: any = Object.values(Enums).find((data: any) => {
return data.value === '' + value; return data.value === '' + value;
}); });
return isColor ? ( return isColor ? <Tag color={item?.color}>{item?.text}</Tag> : item?.text;
<Tag color={item?.color}>{item?.text}</Tag>
) : (
<Tag>{item?.text}</Tag>
);
// return <Tag style={{ color: item.color }}>{item.text}</Tag>; // return <Tag style={{ color: item.color }}>{item.text}</Tag>;
}, },
Images(images: string[]) { Images(images: string[]) {

View File

@ -34,6 +34,7 @@ export function MyModal(props?: any) {
type={props.type || 'primary'} type={props.type || 'primary'}
size={props.size || 'small'} size={props.size || 'small'}
onClick={() => setOpen(true)} onClick={() => setOpen(true)}
{...props?.modal?.btnProps}
/> />
) )
) : ( ) : (

View File

@ -282,7 +282,7 @@ export const BannersTypeEnum= {
// 缓存类型 // 缓存类型
export const CacheTypeEnum= { export const CacheTypeEnum= {
'MobilePhoneVerificationCode': {"text":"手机验证码","color":"#e72e44","value":"MobilePhoneVerificationCode"}, 'MobilePhoneVerificationCode': {"text":"手机验证码","color":"#827a7d","value":"MobilePhoneVerificationCode"},
}; };
// CompaniesMerchantTypeEnum // CompaniesMerchantTypeEnum
@ -729,6 +729,7 @@ export const HouseRegistersTypeEnum= {
'UpdateInfo': {"text":"修改信息","color":"#722ed1","value":"UpdateInfo"}, 'UpdateInfo': {"text":"修改信息","color":"#722ed1","value":"UpdateInfo"},
'UpdatePhone': {"text":"修改电话","color":"#13c2c2","value":"UpdatePhone"}, 'UpdatePhone': {"text":"修改电话","color":"#13c2c2","value":"UpdatePhone"},
'GoodsRelease': {"text":"物品放行","color":"#a0d911","value":"GoodsRelease"}, 'GoodsRelease': {"text":"物品放行","color":"#a0d911","value":"GoodsRelease"},
'VisitorApplies': {"text":"来访申请","color":"#a0d911","value":"VisitorApplies"},
}; };
// HouseRegistersUsagePlanEnum // HouseRegistersUsagePlanEnum

View File

@ -38,13 +38,7 @@ export default function Index({ title = '项目活动' }) {
// title={title} // title={title}
/> />
), ),
enrolls: ( enrolls: <EnrollsList item={item} title="报名" reload={action?.reload} />,
<>
{item.is_enroll === 1 && (
<EnrollsList item={item} title="报名" reload={action?.reload} />
)}
</>
),
publish: ( publish: (
<MyButtons.Default <MyButtons.Default
title={ title={
@ -199,7 +193,6 @@ export default function Index({ title = '项目活动' }) {
render: (_, item: any, index, action) => ( render: (_, item: any, index, action) => (
<Space key={index}> <Space key={index}>
<ActivityShow item={item} /> <ActivityShow item={item} />
<>{tableRender(item, action)}</> <>{tableRender(item, action)}</>
</Space> </Space>
), ),

View File

@ -16,6 +16,11 @@ export default function EnrollsList(props: MyBetaModalFormProps) {
title={props.title || '报名'} title={props.title || '报名'}
type={props.item?.type || 'primary'} type={props.item?.type || 'primary'}
width="800px" width="800px"
modal={{
btnProps: {
disabled: !props?.item.is_enroll,
},
}}
node={ node={
<ProTable <ProTable
{...MyProTableProps.props} {...MyProTableProps.props}

View File

@ -1,13 +1,6 @@
import { MyPageContainer } from '@/common'; import { MyPageContainer } from '@/common';
import { ProCard } from '@ant-design/pro-components';
import { Space } from 'antd';
import MyDemoLine from './charts/DemoLine';
import MyOverviewClient from './charts/OverviewClient';
import MyOverviewFinancialData from './components/OverviewFinancialData';
import MyRanking from './components/Ranking';
import './style.scss'; import './style.scss';
export default function Index({ title = '财务Bi' }) { export default function Index({ title = '财务Bi' }) {
return ( return (
<MyPageContainer <MyPageContainer
title={title} title={title}
@ -23,18 +16,7 @@ export default function Index({ title = '财务Bi' }) {
justifyContent: 'space-between', justifyContent: 'space-between',
}} }}
> >
<Space 2
direction="vertical"
style={{ flex: 1, minWidth: '500px', marginRight: '20px' }}
size="middle"
>
<MyOverviewFinancialData />
<MyOverviewClient />
<MyDemoLine />
</Space>
<ProCard style={{ maxWidth: '33vw' }} title="项目收款排名">
<MyRanking />
</ProCard>
</div> </div>
</MyPageContainer> </MyPageContainer>
); );

View File

@ -88,9 +88,7 @@ export default function Index({ title = '登记审核' }) {
MyColumns.Option({ MyColumns.Option({
render: (_, item: any, index, action) => ( render: (_, item: any, index, action) => (
<Space key={index}> <Space key={index}>
{item?.status === HouseRegistersStatusEnum.Pending.value && (
<Audit item={item} reload={action?.reload} title={title} /> <Audit item={item} reload={action?.reload} title={title} />
)}
</Space> </Space>
), ),
}), }),

View File

@ -32,7 +32,15 @@ export default function Update(props: MyBetaModalFormProps) {
<BetaSchemaForm<ApiTypes.Archive.HouseRegisters.Update> <BetaSchemaForm<ApiTypes.Archive.HouseRegisters.Update>
{...MyModalFormProps.props} {...MyModalFormProps.props}
title={props.title} title={props.title}
trigger={<MyButtons.Default title="审核" type="primary" />} trigger={
<MyButtons.Default
title="审核"
disabled={
props.item?.status !== HouseRegistersStatusEnum.Pending.value
}
type="primary"
/>
}
wrapperCol={{ span: 24 }} wrapperCol={{ span: 24 }}
width="800px" width="800px"
modalProps={{ modalProps={{

View File

@ -106,7 +106,7 @@ export default function Index({ title = '客户列表' }) {
search: false, search: false,
}), }),
MyColumns.Option({ MyColumns.Option({
render: (_, item: any, index, action) => ( render: (_, item: any, index) => (
<Space key={index}> <Space key={index}>
<MyButtons.View <MyButtons.View
title="查看" title="查看"

View File

@ -0,0 +1,48 @@
import { MyBetaModalFormProps } from '@/common';
import { Line } from '@ant-design/plots';
import { useEffect, useState } from 'react';
export default function FinancialAnalysisLine(props: MyBetaModalFormProps) {
const [houseBillsCount, setHouseBillsCount] = useState<any>({});
const config = {
data: [],
xField: '月份',
yField: '金额',
colorField: 'name',
point: {
size: 7,
},
legend: {
position: 'top',
},
tooltip: {},
style: {
// 矩形四个方向的内边距
inset: 5,
},
// 使用双Y轴因为两个指标数值范围差异大
yAxis: [
{
title: {
text: '月收款(万元)',
},
},
{
title: {
text: '收缴率(%)',
},
grid: null,
},
],
};
useEffect(() => {
setHouseBillsCount({ ...config, data: props.item || [] });
}, [props.item]);
return (
<div style={{ height: 290 }}>
<Line {...houseBillsCount} />
</div>
);
}

View File

@ -0,0 +1,95 @@
import { MyBetaModalFormProps } from '@/common';
import { Pie } from '@ant-design/plots';
import { Space } from 'antd';
import { useEffect, useState } from 'react';
export default function WorkOrderPie(props: MyBetaModalFormProps) {
const [getPieCount, setPieCount] = useState<any>({});
const config = {
data: [],
angleField: '数量',
colorField: 'name',
innerRadius: 0.8,
radius: 0.5,
style: {
width: '60px',
},
label: {
text: '数量',
style: {
fontWeight: 'bold',
},
},
};
useEffect(() => {
setPieCount({
...config,
data: props?.item?.list || [],
legend: {
color: {
render: (legendItem: any, item: any) => {
console.log(legendItem, item, 'legendItem');
// 这里可以返回 React 节点或 HTML 字符串
return (
<Space
size="small"
style={{
display: 'flex',
alignItems: 'center',
flexWrap: 'wrap',
gap: '5px',
}}
>
{legendItem?.map((row: any, index: number) => {
return (
<div key={`item_${index}`}>
<div
style={{
width: 8,
height: 8,
display: 'inline-block',
backgroundColor: row?.color,
borderRadius: 100,
fontSize: 14,
marginRight: 4,
}}
/>
{row?.label}
{
props?.item?.list?.find(
(i: any) => i.name === row?.label,
)?.
}
</div>
);
})}
</Space>
);
},
title: false,
position: 'top',
rowPadding: 5,
},
},
annotations: [
{
type: 'text',
style: {
text: `${props?.item?.total || ''}\n工单总数`,
x: '50%',
y: '50%',
textAlign: 'center',
fontSize: 20,
fontStyle: 'bold',
},
},
],
});
}, [props?.item]);
return (
<div style={{ height: 400 }}>
<Pie {...getPieCount} />
</div>
);
}

View File

@ -0,0 +1,270 @@
import { MyColumns, MyProTableProps, renderTextHelper } from '@/common';
import { GetFromNow, ShowCurrentDayTime } from '@/common/utils/day';
import { Apis } from '@/gen/Apis';
import {
CompanyEmployeeBacklogsTypeEnum,
HouseBillsTypeEnum,
} from '@/gen/Enums';
import { ClockCircleOutlined, FundOutlined } from '@ant-design/icons';
import { ProCard, ProTable } from '@ant-design/pro-components';
import { Col, Row, Space } from 'antd';
import { useEffect, useState } from 'react';
import CountUp from 'react-countup';
import MyFinancialAnalysisLine from '../charts/FinancialAnalysisLine';
import './style_left.scss';
const RealTimeDynamicsEmployeeBacklogsTypeEnum = {
...HouseBillsTypeEnum,
...CompanyEmployeeBacklogsTypeEnum,
};
export default function LayoutLeft() {
const [financialAnalysis, setFinancialAnalysis] = useState<any>({});
const [todayDynamics, setTodayDynamics] = useState<any>({});
const [currentTime, setCurrentTime] = useState<string>(ShowCurrentDayTime());
const getFinancialAnalysis = async () => {
let res = await Apis.Statistics.IndexCount.FinancialAnalysis();
setFinancialAnalysis(res?.data);
};
const getTodayDynamics = async () => {
let res = await Apis.Statistics.IndexCount.TodayDynamics();
setTodayDynamics(res?.data);
};
useEffect(() => {
getFinancialAnalysis();
getTodayDynamics();
// 每5分钟调用一次 getTodayDynamics()
const intervalId = setInterval(() => {
getFinancialAnalysis();
getTodayDynamics();
}, 5 * 60 * 1000); // 5分钟
return () => {
clearInterval(intervalId);
};
}, []);
// 每秒更新当前时间
useEffect(() => {
const intervalId = setInterval(() => {
setCurrentTime(ShowCurrentDayTime());
}, 1000);
return () => {
clearInterval(intervalId);
};
}, []);
return (
<div style={{ display: 'flex', flexDirection: 'column', height: '100%' }}>
<div style={{ display: 'flex', gap: '15px', flexWrap: 'wrap' }}>
<ProCard
title="财务分析"
className="finance_pro_card financial_analysis_card"
style={{ flex: '0.5 1 400px', minWidth: '300px' }}
>
<div className="finance_pro_card_content pt-15">
<Row>
<Col span={8}>
<div className="finance_pro_card_item">
<div className="finance_pro_card_item_val">
{financialAnalysis?.month_receivable}
<span></span>
</div>
<div style={{ fontSize: '14px' }}></div>
</div>
</Col>
<Col span={8}>
<div className="finance_pro_card_item">
<div className="finance_pro_card_item_val">
{financialAnalysis?.month_received}
<span></span>
</div>
<div style={{ fontSize: '14px' }}></div>
</div>
</Col>
<Col span={8}>
<div className="finance_pro_card_item">
<div className="finance_pro_card_item_val">
{financialAnalysis?.month_rate}%
</div>
<div style={{ fontSize: '14px' }}></div>
</div>
</Col>
</Row>
</div>
<div className="finance_pro_card_content">
<Row>
<Col span={8}>
<div className="finance_pro_card_item">
<div className="finance_pro_card_item_val">
{financialAnalysis?.year_receivable}
<span></span>
</div>
<div style={{ fontSize: '14px' }}></div>
</div>
</Col>
<Col span={8}>
<div className="finance_pro_card_item">
<div className="finance_pro_card_item_val">
{financialAnalysis?.year_received}
<span></span>
</div>
<div style={{ fontSize: '14px' }}></div>
</div>
</Col>
<Col span={8}>
<div className="finance_pro_card_item">
<div className="finance_pro_card_item_val">
{financialAnalysis?.year_rate}%
</div>
<div style={{ fontSize: '14px' }}></div>
</div>
</Col>
</Row>
</div>
<MyFinancialAnalysisLine item={financialAnalysis?.chart_data || []} />
</ProCard>
<ProCard
title="今日动态"
style={{ flex: '1.5 1 400px', minWidth: '300px' }}
className="finance_pro_card"
extra={
<Space align="center">
<ClockCircleOutlined />
{currentTime}
<div className="circle_content">
<div className="circle_center"></div>
</div>
</Space>
}
>
<Space
direction="vertical"
style={{
width: '100%',
}}
>
<ProCard
style={{
backgroundColor: '#F3F8FB',
width: '100%',
borderRadius: '8px',
}}
title="今日收款"
extra={
<div className="today_amount_contents">
<span className="amount">
<CountUp
end={todayDynamics?.today_amount}
duration={3}
separator=","
prefix="¥"
decimals={2}
/>
</span>
<span className="unit"></span>
</div>
}
>
<Row gutter={[15, 15]}>
{todayDynamics?.type_distribution?.map(
(res: any, index: number) => {
return (
<Col span={6} key={`item_${index}`}>
<div className="card_item">
<div className="card_item_title">{res?.name}</div>
<div>
<CountUp
end={res?.value}
duration={3}
separator=","
prefix="¥"
decimals={2}
/>
</div>
</div>
</Col>
);
},
)}
</Row>
</ProCard>
<div>
<Space className="bi_min_title">
<FundOutlined />
<div className="title"></div>
</Space>
<div className="dynamics_contents">
{todayDynamics?.dynamics?.map((res: any, index: number) => {
return (
<div key={`item_${index}`} className="real_time_dynamics">
<div className={`dynamics_type type_${res?.type}`}></div>
<span>{GetFromNow(res?.time)}</span>
{res?.type === 'WorkOrder' ? '提报' : '收到'}
{res?.target}
<renderTextHelper.Tag
Enums={RealTimeDynamicsEmployeeBacklogsTypeEnum}
value={res?.type}
isColor={false}
key="type"
/>
{res?.amount ? `${res?.amount}` : ''}
</div>
);
})}
</div>
</div>
</Space>
</ProCard>
</div>
<div
style={{
flex: 1,
marginTop: '15px',
}}
>
<ProCard title="小区概括分析" className="finance_pro_card">
<ProTable
{...MyProTableProps.props}
request={async (params, sort) => {
return MyProTableProps.request(
params,
sort,
Apis.Statistics.IndexCount.CommunityOverview,
);
}}
search={false}
options={false}
columns={[
MyColumns.ID({
search: false,
}),
{
title: '项目名称',
dataIndex: 'name',
},
{
title: '房屋数',
dataIndex: 'house_count',
},
{
title: '车位数',
dataIndex: 'car_port_count',
},
{
title: '绑定客户数',
dataIndex: 'customer_count',
},
{
title: '收缴率',
dataIndex: 'collection_rate',
},
]}
/>
</ProCard>
</div>
</div>
);
}

View File

@ -0,0 +1,175 @@
import { Apis } from '@/gen/Apis';
import { RightOutlined } from '@ant-design/icons';
import { useNavigate } from '@umijs/max';
import { Col, Progress, Row, Space } from 'antd';
import { useEffect, useState } from 'react';
import CountUp from 'react-countup';
import WorkOrderPie from '../charts/WorkOrderPie';
import './style_right.scss';
export default function LayoutRight() {
const navigate = useNavigate();
const [workOrderAnalysis, setWorkOrderAnalysis] = useState<any>({});
const getWorkOrderAnalysis = () => {
Apis.Statistics.IndexCount.WorkOrderAnalysis().then((res) => {
setWorkOrderAnalysis(res.data);
});
};
useEffect(() => {
getWorkOrderAnalysis();
}, []);
return (
<div className="right_content">
<div
style={{
padding: '0 0 20px 0',
fontSize: 16,
fontWeight: 600,
}}
>
</div>
<Space direction="vertical" style={{ width: '100%' }}>
<div
style={{ backgroundColor: '#F3FAFF', borderRadius: 8, padding: 20 }}
>
<div
style={{
padding: '0 0 15px 0',
fontSize: 16,
fontWeight: 600,
}}
>
</div>
<Row gutter={[12, 12]}>
<Col span={12}>
<div
className="work_card_title"
onClick={() => {
navigate('/work_order/list');
}}
>
<RightOutlined />
</div>
<div className="work_card_number">
{/* {workOrderAnalysis?.repair || 0} */}
<CountUp
end={workOrderAnalysis?.repair}
duration={3}
separator=","
prefix=""
/>
</div>
</Col>
<Col span={12}>
<div
className="work_card_title"
onClick={() => {
navigate('/work_order/list');
}}
>
<RightOutlined />
</div>
<div className="work_card_number">
<CountUp
end={workOrderAnalysis?.incident}
duration={3}
separator=","
prefix=""
/>
</div>
</Col>
<Col span={12}>
<div
className="work_card_title"
onClick={() => {
navigate('/work_order/complaint');
}}
>
<RightOutlined size={12} />
</div>
<div className="work_card_number">
<CountUp
end={workOrderAnalysis?.complaint}
duration={3}
separator=","
prefix=""
/>
</div>
</Col>
</Row>
</div>
<div>
<div
style={{
padding: '15px 0',
fontSize: 16,
fontWeight: 600,
}}
>
</div>
<WorkOrderPie
item={{
list: workOrderAnalysis?.distribution || [],
total: workOrderAnalysis?.total || 0,
}}
/>
</div>
<div>
<div
style={{
padding: '15px 0',
fontSize: 16,
fontWeight: 600,
}}
>
</div>
<div className="work_order_data">
<div className="work_order_data_item">
<Progress
type="dashboard"
size={[160, 30]}
percent={workOrderAnalysis?.data?.completion_rate}
format={(percent) => {
return (
<div className="dashboard_progress">
<div className="dashboard_value">{`${percent}%`}</div>
<div className="dashboard_label"></div>
</div>
);
}}
/>
<div className="work_order_data_cell">
{workOrderAnalysis?.data?.completed_count || 0}
</div>
<div className="work_order_data_cell">{0}</div>
</div>
<div className="work_order_data_item">
<Progress
type="dashboard"
size={[160, 30]}
percent={workOrderAnalysis?.data?.satisfaction_rate}
format={(percent) => {
return (
<div className="dashboard_progress">
<div className="dashboard_value">{`${percent}%`}</div>
<div className="dashboard_label"></div>
</div>
);
}}
/>
<div className="work_order_data_cell">
{workOrderAnalysis?.data?.evaluated_count || 0}
</div>
<div className="work_order_data_cell">{0}</div>
</div>
</div>
</div>
</Space>
</div>
);
}

View File

@ -0,0 +1,294 @@
.finance_pro_card {
border-radius: 8px;
}
.financial_analysis_card {
width: 470px;
}
.finance_pro_card_content {
background: linear-gradient(135deg, #6499ff 0%, #95b8ff 100%);
padding: 25px 0;
border-radius: 12px;
box-shadow: 0 10px 25px -5px rgba(42, 126, 251, 0.3);
transition: all 0.3s ease;
&:hover {
transform: translateY(-2px);
box-shadow: 0 15px 30px -5px rgba(42, 126, 251, 0.4);
}
// animation: fadeInUp 0.6s ease-out forwards;
// opacity: 0;
// transform: translateY(20px);
// &.pt-15 {
// animation-delay: 0.2s;
// }
.finance_pro_card_item {
display: flex;
flex-direction: column;
align-items: center;
color: #fff;
animation: scaleIn 0.8s ease-out forwards;
opacity: 0;
transform: scale(0.9);
&:nth-child(1) {
animation-delay: 0.3s;
}
&:nth-child(2) {
animation-delay: 0.4s;
}
&:nth-child(3) {
animation-delay: 0.5s;
}
}
.finance_pro_card_item_val {
font-size: 20px;
font-weight: 700;
margin-bottom: 8px;
animation: textSlideUp 0.8s ease-out forwards;
opacity: 0;
transform: translateY(10px);
text-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
span {
font-weight: 500;
font-size: 14px;
margin-left: 4px;
opacity: 0.9;
}
}
.finance_pro_card_item > div:last-child {
font-size: 14px;
font-weight: 500;
animation: textFadeIn 1s ease-out forwards;
opacity: 0;
text-shadow: 0 1px 2px rgba(0, 0, 0, 0.1);
}
// 为每个子元素添加不同的动画延迟
.finance_pro_card_item {
&:nth-child(1) {
.finance_pro_card_item_val {
animation-delay: 0.5s;
}
> div:last-child {
animation-delay: 0.7s;
}
}
&:nth-child(2) {
.finance_pro_card_item_val {
animation-delay: 0.6s;
}
> div:last-child {
animation-delay: 0.8s;
}
}
&:nth-child(3) {
.finance_pro_card_item_val {
animation-delay: 0.7s;
}
> div:last-child {
animation-delay: 0.9s;
}
}
}
}
@keyframes fadeInUp {
0% {
opacity: 0;
transform: translateY(20px);
}
100% {
opacity: 1;
transform: translateY(0);
}
}
@keyframes scaleIn {
0% {
opacity: 0;
transform: scale(0.9);
}
100% {
opacity: 1;
transform: scale(1);
}
}
@keyframes textSlideUp {
0% {
opacity: 0;
transform: translateY(10px);
}
100% {
opacity: 1;
transform: translateY(0);
}
}
@keyframes textFadeIn {
0% {
opacity: 0;
}
100% {
opacity: 1;
}
}
.pt-15 {
margin-bottom: 15px;
}
.card_item {
background-color: #fff;
padding: 25px 20px;
border-radius: 10px;
text-align: center;
color: #3d3d3d;
font-size: 16px;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.08);
transition: all 0.3s ease;
border: 1px solid #f0f0f0;
&:hover {
transform: translateY(-3px);
box-shadow: 0 8px 20px rgba(0, 0, 0, 0.12);
border-color: #e0e7ff;
}
&_title {
font-weight: 600;
margin-bottom: 8px;
font-size: 16px;
color: #1f2937;
}
> div:last-child {
font-size: 18px;
color: #3d3d3d;
}
}
.bi_min_title {
display: flex;
align-items: center;
font-size: 16px;
font-weight: 500;
padding: 12px 0;
color: #2a7efb;
.title {
color: #333;
}
}
.dynamics_contents {
height: 170px;
overflow-y: auto;
}
.real_time_dynamics {
color: #333;
width: 100%;
font-size: 16px;
padding: 0 0 15px 0;
display: flex;
align-items: center;
.dynamics_type {
width: 7px;
height: 7px;
border-radius: 100px;
background-color: #eee;
}
span {
color: #666;
padding: 0 10px;
}
.type_WorkOrder {
background-color: #faad14;
}
.type_PropertyFee {
background-color: #00a154;
}
.type_MaintenanceFund {
background-color: #1bc0c4;
}
.type_WaterFee {
background-color: #007bff;
}
.type_ElectricityFee {
background-color: #f8860c;
}
.type_SharedWaterFee {
background-color: #60ffb2;
}
.type_SharedElectricityFee {
background-color: #7912ff;
}
.type_CarPortFee {
background-color: #ffb547;
}
.type_WorkOrderRepairFee {
background-color: #ff4141;
}
}
.today_amount_contents {
.amount {
font-size: 19px;
font-weight: 500;
color: #389e0d;
}
.unit {
font-size: 12px;
color: #666;
}
}
.circle_content {
display: flex;
align-items: center;
justify-content: center;
background-color: #deffcb;
padding: 6px;
border-radius: 100px;
position: relative;
overflow: visible;
&::before,
&::after {
content: '';
position: absolute;
width: 100%;
height: 100%;
border-radius: 50%;
background-color: rgba(73, 213, 13, 0.3);
animation: ripple 2.4s infinite;
}
&::after {
animation-delay: 1s;
}
.circle_center {
width: 9px;
height: 9px;
border-radius: 100px;
background-color: #389e0d;
position: relative;
z-index: 1;
}
}
@keyframes ripple {
0% {
transform: scale(1);
opacity: 1;
}
100% {
transform: scale(1.5);
opacity: 0;
}
}

View File

@ -0,0 +1,49 @@
.work_card_title {
color: #666666;
font-size: 16px;
cursor: pointer;
}
.right_content {
flex: 1;
background-color: #fff;
padding: 20px;
border-radius: 8px;
height: 92vh;
}
.work_card_number {
color: #333333;
font-size: 28px;
font-weight: 500;
padding-top: 5px;
}
.work_order_data {
display: flex;
align-items: center;
justify-content: space-between;
padding-top: 10px;
&_item {
width: 50%;
text-align: center;
color: #3d3d3d;
font-size: 16rpx;
}
&_cell {
padding-top: 10px;
}
}
.dashboard_progress {
width: 100%;
position: relative;
text-align: center;
// padding-top: 100px;
.dashboard_label {
font-size: 16px;
color: #3d3d3d;
width: 100%;
position: relative;
top: 50px;
}
.dashboard_value {
width: 100%;
}
}

View File

@ -1,5 +1,28 @@
// import MyModalsMapLeaflet from '@/components/ModalsMapLeaflet'; // import MyModalsMapLeaflet from '@/components/ModalsMapLeaflet';
import { Apis } from '@/gen/Apis';
import { FloatButton } from 'antd';
import LayoutLeft from './components/LayoutLeft';
import LayoutRight from './components/LayoutRight';
import './style.scss'; import './style.scss';
export default function Index() { export default function Index() {
return <div className="overview_content">2</div>; return (
<div className="overview_content">
<div className="overview_left">
<LayoutLeft />
</div>
<div className="overview_right">
<LayoutRight />
</div>
<FloatButton
// icon={<Loading3QuartersOutlined />}
description="刷新"
onClick={() => {
Apis.Statistics.IndexCount.ClearCache().then(() => {
// message.success('刷新成功');
window.location.reload();
});
}}
/>
</div>
);
} }

View File

@ -6,9 +6,11 @@
justify-content: space-between; justify-content: space-between;
gap: 15px; gap: 15px;
} }
.overview_left, .overview_left {
flex: 3;
}
.overview_right { .overview_right {
width: 500px; flex: 1;
} }
.overview_center { .overview_center {
flex: 1; flex: 1;

View File

@ -197,7 +197,6 @@ export default function Create(props: MyBetaModalFormProps) {
value: 'value', value: 'value',
}, },
labelRender: (res: any) => { labelRender: (res: any) => {
console.log(res, '222');
if (res?.label) { if (res?.label) {
return res?.label; return res?.label;
} else { } else {

View File

@ -16,7 +16,7 @@ import {
} from '@/gen/Enums'; } from '@/gen/Enums';
import { ProTable } from '@ant-design/pro-components'; import { ProTable } from '@ant-design/pro-components';
import { useSearchParams } from '@umijs/max'; import { useSearchParams } from '@umijs/max';
import { Space, Tooltip } from 'antd'; import { Dropdown, Space, Tooltip } from 'antd';
import WorkOrderAssign from './modals/WorkOrderAssign'; import WorkOrderAssign from './modals/WorkOrderAssign';
import WorkOrderCreate from './modals/WorkOrderCreate'; import WorkOrderCreate from './modals/WorkOrderCreate';
import WorkOrderShow from './modals/WorkOrderShow'; import WorkOrderShow from './modals/WorkOrderShow';
@ -52,40 +52,31 @@ export default function Index({ title = '报修报事' }) {
}); });
}; };
let tableRender = (item: any, action: any) => { let tableRender = (item: any, action: any) => {
return getCurrentPermissions({ let permissions = getCurrentPermissions({
show: <WorkOrderShow item={item} title="详情" reload={action?.reload} />,
update: (
<>
{item.status === 'Pending' && (
<WorkOrderUpdate
item={item}
reload={action?.reload}
title={title}
/>
)}
</>
),
assign: ( assign: (
<> <WorkOrderAssign item={item} reload={action?.reload} title="指派" />
{item.assign_status === 'Unassigned' &&
item.type !== 'SecurityInspection' &&
item.status !== 'Closed' && (
<WorkOrderAssign
item={item}
reload={action?.reload}
title="指派"
/>
)}
</>
), ),
completed: ( completed: (
<>
{item.status === 'Completed' && item.is_visited === 0 && (
<WorkOrderShow item={item} title="回访" reload={action?.reload} /> <WorkOrderShow item={item} title="回访" reload={action?.reload} />
)}
</>
), ),
delete: ( });
let permissionsSpace = getCurrentPermissions({
show: {
key: '1',
label: (
<WorkOrderShow item={item} title="详情" reload={action?.reload} />
),
},
update: {
key: '2',
label: (
<WorkOrderUpdate item={item} reload={action?.reload} title={title} />
),
},
delete: {
key: '3',
label: (
<MyButtons.Delete <MyButtons.Delete
onConfirm={() => onConfirm={() =>
Apis.WorkOrder.HouseWorkOrders.Delete({ Apis.WorkOrder.HouseWorkOrders.Delete({
@ -94,7 +85,15 @@ export default function Index({ title = '报修报事' }) {
} }
/> />
), ),
},
}); });
let Others = (
<Dropdown menu={{ items: permissionsSpace }} trigger={['click']}>
<MyButtons.Default title="更多" />
</Dropdown>
);
return [...permissions, ...[Others]];
}; };
return ( return (

View File

@ -33,7 +33,15 @@ export default function WorkOrderAssign(
labelCol={{ span: 5 }} labelCol={{ span: 5 }}
labelAlign="left" labelAlign="left"
trigger={ trigger={
<MyButtons.Default title={props.title || '指派'} type="primary" /> <MyButtons.Default
title={props.title || '指派'}
disabled={
props?.item.assign_status !== 'Unassigned' ||
props?.item.type === 'SecurityInspection' ||
props?.item.status === 'Closed'
}
type="primary"
/>
} }
key={new Date().getTime()} key={new Date().getTime()}
form={form} form={form}

View File

@ -41,7 +41,7 @@ export default function WorkOrderShow({
<> <>
<MyButtons.Default <MyButtons.Default
onClick={handleOpen} onClick={handleOpen}
disabled={item?.is_visited} disabled={item?.is_visited || item.status !== 'Completed'}
type={'primary'} type={'primary'}
title={title} title={title}
/> />

View File

@ -27,7 +27,7 @@ export default function WorkOrderUpdate(
width="600px" width="600px"
layout="horizontal" layout="horizontal"
key={new Date().getTime()} key={new Date().getTime()}
trigger={<MyButtons.Edit title={`编辑`} />} trigger={<MyButtons.Edit title={`编辑`} disabled={props.item?.status !== 'Pending'} />}
onOpenChange={(open: any) => { onOpenChange={(open: any) => {
if (open && props.item) { if (open && props.item) {
const formValues = { const formValues = {

View File

@ -4870,6 +4870,11 @@ cosmiconfig@^9.0.0:
js-yaml "^4.1.0" js-yaml "^4.1.0"
parse-json "^5.2.0" parse-json "^5.2.0"
countup.js@^2.8.0:
version "2.9.0"
resolved "https://registry.yarnpkg.com/countup.js/-/countup.js-2.9.0.tgz#599e6482e502e5e3f00f8816637120c9b21856ed"
integrity sha512-llqrvyXztRFPp6+i8jx25phHWcVWhrHO4Nlt0uAOSKHB8778zzQswa4MU3qKBvkXfJKftRYFJuVHez67lyKdHg==
create-ecdh@^4.0.4: create-ecdh@^4.0.4:
version "4.0.4" version "4.0.4"
resolved "https://registry.npmjs.org/create-ecdh/-/create-ecdh-4.0.4.tgz" resolved "https://registry.npmjs.org/create-ecdh/-/create-ecdh-4.0.4.tgz"
@ -10584,6 +10589,13 @@ react-activation@^0.13.4:
react-node-key "^0.4.0" react-node-key "^0.4.0"
szfe-tools "^0.0.0-beta.7" szfe-tools "^0.0.0-beta.7"
react-countup@^6.5.3:
version "6.5.3"
resolved "https://registry.yarnpkg.com/react-countup/-/react-countup-6.5.3.tgz#e892aa3eab2d6ba9c3cdba30bf4ed6764826d848"
integrity sha512-udnqVQitxC7QWADSPDOxVWULkLvKUWrDapn5i53HE4DPRVgs+Y5rr4bo25qEl8jSh+0l2cToJgGMx+clxPM3+w==
dependencies:
countup.js "^2.8.0"
react-dom@18.3.1: react-dom@18.3.1:
version "18.3.1" version "18.3.1"
resolved "https://registry.npmjs.org/react-dom/-/react-dom-18.3.1.tgz" resolved "https://registry.npmjs.org/react-dom/-/react-dom-18.3.1.tgz"
@ -11635,16 +11647,7 @@ string-convert@^0.2.0:
resolved "https://registry.npmjs.org/string-convert/-/string-convert-0.2.1.tgz" resolved "https://registry.npmjs.org/string-convert/-/string-convert-0.2.1.tgz"
integrity sha512-u/1tdPl4yQnPBjnVrmdLo9gtuLvELKsAoRapekWggdiQNvvvum+jYF329d84NAa660KQw7pB2n36KrIKVoXa3A== integrity sha512-u/1tdPl4yQnPBjnVrmdLo9gtuLvELKsAoRapekWggdiQNvvvum+jYF329d84NAa660KQw7pB2n36KrIKVoXa3A==
"string-width-cjs@npm:string-width@^4.2.0": "string-width-cjs@npm:string-width@^4.2.0", string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3:
version "4.2.3"
resolved "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz"
integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==
dependencies:
emoji-regex "^8.0.0"
is-fullwidth-code-point "^3.0.0"
strip-ansi "^6.0.1"
string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3:
version "4.2.3" version "4.2.3"
resolved "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz" resolved "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz"
integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==
@ -11734,14 +11737,7 @@ string_decoder@~1.1.1:
dependencies: dependencies:
safe-buffer "~5.1.0" safe-buffer "~5.1.0"
"strip-ansi-cjs@npm:strip-ansi@^6.0.1": "strip-ansi-cjs@npm:strip-ansi@^6.0.1", strip-ansi@^6.0.0, strip-ansi@^6.0.1:
version "6.0.1"
resolved "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz"
integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==
dependencies:
ansi-regex "^5.0.1"
strip-ansi@^6.0.0, strip-ansi@^6.0.1:
version "6.0.1" version "6.0.1"
resolved "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz" resolved "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz"
integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==
@ -12645,16 +12641,7 @@ word-wrap@^1.2.5:
resolved "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz" resolved "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz"
integrity sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA== integrity sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==
"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0": "wrap-ansi-cjs@npm:wrap-ansi@^7.0.0", wrap-ansi@^7.0.0:
version "7.0.0"
resolved "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz"
integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==
dependencies:
ansi-styles "^4.0.0"
string-width "^4.1.0"
strip-ansi "^6.0.0"
wrap-ansi@^7.0.0:
version "7.0.0" version "7.0.0"
resolved "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz" resolved "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz"
integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==