Commit 39a4ab8a authored by 左玲玲's avatar 左玲玲 😬

1723

parent cf32b0ef
...@@ -337,6 +337,12 @@ export default [ ...@@ -337,6 +337,12 @@ export default [
icon: '', icon: '',
component: './lease/contract', component: './lease/contract',
}, },
{
name: '费用分析',
path: '/lease/costanalysis',
icon: '',
component: './lease/costanalysis',
},
{ {
name: '租赁设备', name: '租赁设备',
path: '/lease/leasedevice', path: '/lease/leasedevice',
......
...@@ -13,7 +13,7 @@ export default { ...@@ -13,7 +13,7 @@ export default {
dev: { dev: {
[defaultSetting.proxypath]: { [defaultSetting.proxypath]: {
// 要代理的地址 // 要代理的地址
target: 'http://192.168.40.111:8000', //jf哥 target: 'http://192.168.40.110:8000', //jf哥
// target: 'http://192.168.40.248:8080', //jf哥 // target: 'http://192.168.40.248:8080', //jf哥
// target: 'http://192.168.40.64:28000', //gc哥 // target: 'http://192.168.40.64:28000', //gc哥
// target: 'http://192.168.40.203:8000', //dj哥 // target: 'http://192.168.40.203:8000', //dj哥
...@@ -22,7 +22,7 @@ export default { ...@@ -22,7 +22,7 @@ export default {
}, },
'/token': { '/token': {
// 要代理的地址 // 要代理的地址
target: 'http://192.168.40.111:8000', target: 'http://192.168.40.110:8000',
changeOrigin: true, changeOrigin: true,
}, },
'/staticfile/': { '/staticfile/': {
......
...@@ -73,6 +73,7 @@ ...@@ -73,6 +73,7 @@
"react-dom": "^17.0.0", "react-dom": "^17.0.0",
"react-helmet-async": "^1.2.0", "react-helmet-async": "^1.2.0",
"react-resizable": "^3.0.4", "react-resizable": "^3.0.4",
"socket.io-client": "2.2.0",
"umi-request": "^1.4.0" "umi-request": "^1.4.0"
}, },
"devDependencies": { "devDependencies": {
......
...@@ -120,7 +120,7 @@ export default (props) => { ...@@ -120,7 +120,7 @@ export default (props) => {
{props.childposition == 'bottom' && props.children} {props.childposition == 'bottom' && props.children}
</Card> </Card>
) : ( ) : (
<div className="tabsTable"> <div className="tabstable">
<div style={{ display: 'flex', justifyContent: 'space-between', padding: '16px 20px 0' }}> <div style={{ display: 'flex', justifyContent: 'space-between', padding: '16px 20px 0' }}>
<div style={{ fontSize: 16, fontWeight: 500 }}>{props.pagetitle}</div> <div style={{ fontSize: 16, fontWeight: 500 }}>{props.pagetitle}</div>
<div className="center">{renderextra()}</div> <div className="center">{renderextra()}</div>
......
...@@ -36,7 +36,11 @@ function DrawerPro(props) { ...@@ -36,7 +36,11 @@ function DrawerPro(props) {
{props?.children} {props?.children}
</div> </div>
) : ( ) : (
<InitForm {...newProps} /> <>
{props.childrenposition == 'top' && props.children}
<InitForm {...newProps} />
{props.childrenposition == 'bottom' && props.children}
</>
)} )}
{pushSettingFlag ? ( {pushSettingFlag ? (
......
...@@ -138,11 +138,11 @@ let FormRender = memo(({ fields = [], name, curindex, formRef, action }) => { ...@@ -138,11 +138,11 @@ let FormRender = memo(({ fields = [], name, curindex, formRef, action }) => {
<Col {...item.colProps} key={index}> <Col {...item.colProps} key={index}>
{curindex == 0 ? ( {curindex == 0 ? (
<div> <div>
<label htmlFor="">{item.title}</label> <label htmlFor="" style={{ marginBottom: 10, display: 'inline-block' }} >{item.title}</label>
{ {
hideInFormShowKey ? <div style={{ padding: "6px 0 0 0", margin: 0 }}>{value[hideInFormShowKey]}</div> hideInFormShowKey ? <div style={{ margin: 0 }}>{value[hideInFormShowKey]}</div>
: :
<div style={{ padding: "6px 0 0 0", margin: 0 }}> <div style={{ margin: 0 }}>
<b style={{ color: "red" }}>!</b>{" "} <b style={{ color: "red" }}>!</b>{" "}
需满足条件才可以填写{item.title} 需满足条件才可以填写{item.title}
</div> </div>
...@@ -154,7 +154,7 @@ let FormRender = memo(({ fields = [], name, curindex, formRef, action }) => { ...@@ -154,7 +154,7 @@ let FormRender = memo(({ fields = [], name, curindex, formRef, action }) => {
{ {
hideInFormShowKey ? <>{value[hideInFormShowKey]}</> hideInFormShowKey ? <>{value[hideInFormShowKey]}</>
: :
<div style={{ padding: "6px 0 0 0", margin: 0 }}> <div style={{ margin: 0 }}>
<b style={{ color: "red" }}>!</b>{" "} <b style={{ color: "red" }}>!</b>{" "}
需满足条件才可以填写{item.title} 需满足条件才可以填写{item.title}
</div> </div>
...@@ -180,8 +180,8 @@ let FormRender = memo(({ fields = [], name, curindex, formRef, action }) => { ...@@ -180,8 +180,8 @@ let FormRender = memo(({ fields = [], name, curindex, formRef, action }) => {
return curindex === 0 ? ( return curindex === 0 ? (
<Col {...item.colProps} style={{ height: 68 }} key={index}> <Col {...item.colProps} style={{ height: 68 }} key={index}>
<div> <div>
<label htmlFor="">{item.title}</label> <label htmlFor="" style={{ marginBottom: 10, display: 'inline-block' }} >{item.title}</label>
<div style={{ padding: "6px 0 0 0", margin: 0 }}> <div style={{ margin: 0 }}>
{value[item?.key ?? item?.dataIndex] ?? "-"} {value[item?.key ?? item?.dataIndex] ?? "-"}
</div> </div>
</div> </div>
...@@ -189,7 +189,7 @@ let FormRender = memo(({ fields = [], name, curindex, formRef, action }) => { ...@@ -189,7 +189,7 @@ let FormRender = memo(({ fields = [], name, curindex, formRef, action }) => {
) : ( ) : (
<Col {...item.colProps} style={{ height: 68 }} key={index}> <Col {...item.colProps} style={{ height: 68 }} key={index}>
<div> <div>
<div style={{ padding: "6px 0 0 0", margin: 0 }}> <div style={{ margin: 0 }}>
{value[item?.key ?? item?.dataIndex] ?? "-"} {value[item?.key ?? item?.dataIndex] ?? "-"}
</div> </div>
</div> </div>
...@@ -383,7 +383,7 @@ function DigitRange({ item, colProps }) { ...@@ -383,7 +383,7 @@ function DigitRange({ item, colProps }) {
} }
//Date //Date
function Date({ item, colProps }) { function Date({ item, colProps, curindex = 0 }) {
return ( return (
<> <>
<ProFormDatePicker <ProFormDatePicker
...@@ -391,7 +391,7 @@ function Date({ item, colProps }) { ...@@ -391,7 +391,7 @@ function Date({ item, colProps }) {
formItemProps={item.formItemProps} formItemProps={item.formItemProps}
name={item?.key ?? item?.dataIndex} name={item?.key ?? item?.dataIndex}
colProps={item.colProps ?? colProps} colProps={item.colProps ?? colProps}
label={item.title} label={curindex == 0 ? item.title : ""}
placeholder={`请选择${item.title}`} placeholder={`请选择${item.title}`}
width="100%" width="100%"
/> />
...@@ -1452,36 +1452,62 @@ function Expandable({ item, colProps }) { ...@@ -1452,36 +1452,62 @@ function Expandable({ item, colProps }) {
} }
function FormList(props) { function FormList(props) {
const [isEmpty, cie] = useState(false);
let { item, colProps, formRef } = props; let { item, colProps, formRef } = props;
let col = item.colProps ?? colProps; let col = item.colProps ?? colProps;
let fields = item.columns?.filter(it => !it.hideInTable) ?? []; let fields = item.columns?.filter(it => !it.hideInTable) ?? [];
useEffect(() => {
let value = props.formRef.current.getFieldValue(
item?.key ?? item?.dataIndex
);
if (value?.length == 0) {
cie(true);
}
}, [props]);
return ( return (
<Col {...col}> <Col {...col}>
<ProFormList <div className="formlist">
name={item.key ?? item.dataIndex} {
label={item.title} !isEmpty ?
min={item.min ?? 0} <ProFormList
max={item.max ?? 100}
itemContainerRender={(doms) => {
return <ProForm.Group>{doms}</ProForm.Group>;
}}
alwaysShowItemLabel={false}
copyIconProps={item?.copyIconProps ?? false}
deleteIconProps={item?.deleteIconProps ?? true}
creatorButtonProps={item?.creatorButtonProps ?? true}
>
{(f, index, action) => {
return (
<FormRender
fields={fields}
action={action}
curindex={index}
formRef={formRef}
name={item.key ?? item.dataIndex} name={item.key ?? item.dataIndex}
/> label={item.title}
); min={item.min ?? 0}
}} max={item.max ?? 100}
</ProFormList> itemContainerRender={(doms) => {
return <ProForm.Group>{doms}</ProForm.Group>;
}}
alwaysShowItemLabel={false}
copyIconProps={item?.copyIconProps ?? false}
deleteIconProps={item?.deleteIconProps ?? true}
creatorButtonProps={item?.creatorButtonProps ?? true}
>
{(f, index, action) => {
return (
<FormRender
fields={fields}
action={action}
curindex={index}
formRef={formRef}
name={item.key ?? item.dataIndex}
/>
);
}}
</ProFormList>
:
<div>
<div>{item.title}</div>
<Empty
image={"./empty.svg"}
imageStyle={{
height: 60,
}}
/>
</div>
}
</div>
</Col> </Col>
); );
} }
......
import React, { useCallback, useState, useMemo } from 'react'; import React, { useCallback, useState, useMemo, useEffect } from 'react';
import { LogoutOutlined, LockOutlined } from '@ant-design/icons'; import { LogoutOutlined, LockOutlined } from '@ant-design/icons';
import { Menu, Spin, Button, Form, Modal, message, Avatar, Select } from 'antd'; import { Menu, Spin, Button, Form, Modal, message, Avatar, Select } from 'antd';
import { history, useModel } from '@umijs/max'; import { history, useModel } from '@umijs/max';
...@@ -14,6 +14,14 @@ import ECB from 'crypto-js/mode-ecb'; ...@@ -14,6 +14,14 @@ import ECB from 'crypto-js/mode-ecb';
import Pkcs7 from 'crypto-js/pad-pkcs7'; import Pkcs7 from 'crypto-js/pad-pkcs7';
import Utf8 from 'crypto-js/enc-utf8'; import Utf8 from 'crypto-js/enc-utf8';
import moment from 'moment'; import moment from 'moment';
import io from 'socket.io-client';
const { NODE_ENV } = process.env;
const websocketUrl = NODE_ENV == "development" ? "ws://192.168.40.110:9933" : `${window.location.protocol == "http:" ? "ws" : "wss"}://${window.location.host}`;
const socket = io(websocketUrl, {
reconnectionDelay: 4000,
transports: ['websocket'],
autoConnect: true,
});
/** /**
* 退出登录,并且将当前的 url 保存 * 退出登录,并且将当前的 url 保存
*/ */
...@@ -31,6 +39,7 @@ const AvatarDropdown = ({ menu }) => { ...@@ -31,6 +39,7 @@ const AvatarDropdown = ({ menu }) => {
return res?.data || {}; return res?.data || {};
}); });
const { initialState, setInitialState } = useModel('@@initialState'); const { initialState, setInitialState } = useModel('@@initialState');
const { allWebsocket, changeWebsocket } = useModel('useGlobal');
const [visible, cv] = useState(false), const [visible, cv] = useState(false),
[formRef] = Form.useForm(), [formRef] = Form.useForm(),
{ run, loading } = useRequest(doFetch, { { run, loading } = useRequest(doFetch, {
...@@ -50,6 +59,14 @@ const AvatarDropdown = ({ menu }) => { ...@@ -50,6 +59,14 @@ const AvatarDropdown = ({ menu }) => {
} }
}, },
}); });
useEffect(() => {
changeWebsocket((s) => ({ ...s, commonWebsocket: socket }));
socket.on('iotData', (data) => {
changeWebsocket((s) => ({ ...s, commonWebmsg: data }));
});
}, []);
const fields = useMemo(() => { const fields = useMemo(() => {
return { return {
password: { password: {
...@@ -133,9 +150,9 @@ const AvatarDropdown = ({ menu }) => { ...@@ -133,9 +150,9 @@ const AvatarDropdown = ({ menu }) => {
padding: Pkcs7, padding: Pkcs7,
}).toString(); }).toString();
let newPassword = AES.encrypt(values.newPassword, Utf8.parse(timestamp), { let newPassword = AES.encrypt(values.newPassword, Utf8.parse(timestamp), {
mode: ECB, mode: ECB,
padding: Pkcs7, padding: Pkcs7,
}).toString(), }).toString(),
password = AES.encrypt(values.password, Utf8.parse(timestamp), { password = AES.encrypt(values.password, Utf8.parse(timestamp), {
mode: ECB, mode: ECB,
padding: Pkcs7, padding: Pkcs7,
...@@ -184,7 +201,7 @@ const AvatarDropdown = ({ menu }) => { ...@@ -184,7 +201,7 @@ const AvatarDropdown = ({ menu }) => {
formRef={formRef} formRef={formRef}
fields={fields} fields={fields}
col={{ span: 24 }} col={{ span: 24 }}
onChange={(changedValues, allValues) => {}} onChange={(changedValues, allValues) => { }}
submitData={(values, fn) => { submitData={(values, fn) => {
saveData(values, fn); saveData(values, fn);
}} }}
......
...@@ -368,7 +368,7 @@ ol { ...@@ -368,7 +368,7 @@ ol {
} }
} }
.tabsTable { .tabstable {
background: #fff; background: #fff;
.ant-tabs-nav { .ant-tabs-nav {
...@@ -561,26 +561,28 @@ ol { ...@@ -561,26 +561,28 @@ ol {
} }
} }
.formList {
.ledgerf {
.ant-card-head {
display: none !important;
}
.ant-card-body {
padding: 0 !important;
}
}
.formlist {
>div { >div {
>div { >div {
>div { >div {
>div { >div {
>div:last-child { >div:last-child {
border: 1px solid #dbdbdb;
padding: 12px; padding: 12px;
} }
} }
} }
} }
} }
}
.ledgerf {
.ant-card-head {
display: none !important;
}
.ant-card-body {
padding: 0 !important;
}
} }
\ No newline at end of file
import { useState, useCallback } from 'react'; import { useState, useCallback } from 'react';
export default function useGlobal() { export default function useGlobal() {
const [alive, setAlive] = useState(false), const [alive, setAlive] = useState(false),
[newMenus, setMenu] = useState({}); [newMenus, setMenu] = useState({}),
[allWebsocket, setAllWebsocket] = useState({});
const changealive = useCallback((parser) => { const changealive = useCallback((parser) => {
setAlive(parser); setAlive(parser);
...@@ -10,11 +11,16 @@ export default function useGlobal() { ...@@ -10,11 +11,16 @@ export default function useGlobal() {
const setMenuFn = useCallback((val) => { const setMenuFn = useCallback((val) => {
setMenu(val); setMenu(val);
}, []); }, []);
const changeWebsocket = useCallback((parser) => {
setAllWebsocket(parser)
}, []);
return { return {
alive, alive,
changealive, changealive,
newMenus, newMenus,
setMenuFn, setMenuFn,
allWebsocket,
changeWebsocket
}; };
} }
...@@ -3,6 +3,7 @@ import { Tooltip, Tabs } from 'antd'; ...@@ -3,6 +3,7 @@ import { Tooltip, Tabs } from 'antd';
import Filedetail from "@/components/Filedetail"; import Filedetail from "@/components/Filedetail";
import DetailPro from '@/components/DetailPro'; import DetailPro from '@/components/DetailPro';
import AutoTable from '@/components/AutoTable/mtable'; import AutoTable from '@/components/AutoTable/mtable';
import { history } from '@umijs/max';
const fields = [ const fields = [
{ {
title: '合同单号', title: '合同单号',
...@@ -188,14 +189,31 @@ const fields = [ ...@@ -188,14 +189,31 @@ const fields = [
render: (_, row) => { render: (_, row) => {
return ( return (
<Tooltip title={row.equipmentNo}> <Tooltip title={row.equipmentNo}>
<a {
className="table-cell" row.pageStatus == 0 ?
onClick={() => { <span className="table-cell">
{row.equipmentNo}
</span>
:
<a
className="table-cell"
onClick={() => {
history.push({
path: '/lease/costanalysis',
query: {
pageStatus: row.pageStatus,
extraparams: {
contractId: row.contractId,
equipmentId: row.id
}
}
})
}}
>
{row.equipmentNo}
</a>
}
}}
>
{row.equipmentNo}
</a>
</Tooltip> </Tooltip>
); );
} }
......
import React, { useMemo } from 'react';
import { Tooltip, Tabs } from 'antd';
import Filedetail from "@/components/Filedetail";
import DetailPro from '@/components/DetailPro';
import AutoTable from '@/components/AutoTable/mtable';
import { history } from '@umijs/max';
const columns = [
{
title: '设备编号',
dataIndex: 'equipmentNo',
key: 'equipmentNo'
},
{
title: '设备名称',
dataIndex: 'equipmentName',
key: 'equipmentName',
},
{
title: '设备类型',
dataIndex: 'equipmentTypeName',
key: 'equipmentType',
hideInSearch: true,
// valueType: 'select',
// options: [
// { label: '设备', value: 1 },
// { label: '智能单元', value: 2 },
// ]
},
{
title: '设备型号',
dataIndex: 'equipmentModelName',
key: 'equipmentModelName',
},
{
title: '开始租赁时间',
dataIndex: 'startLeaseDate',
key: 'startLeaseDateList',
valueType: 'dateRange'
},
{
title: '结束租赁时间',
dataIndex: 'endLeaseDate',
key: 'endLeaseDateList',
valueType: 'dateRange'
},
{
title: '实际结束时间',
dataIndex: 'realEndDate',
key: 'realEndDateList',
valueType: 'dateRange'
},
{
title: '采集状态',
dataIndex: 'collectStatusName',
key: 'collectStatusName',
hideInSearch: true
},
];
const Devices = ({ drawer }) => {
return <div>
<AutoTable
columns={columns}
path={'/lease/umContractEquipment/queryByContractId'}
resizeable={false}
pageextra="none"
bordered={false}
extraparams={{ contractId: drawer?.item?.id }}
pagination={'false'}
/>
</div>
}
export default Devices;
\ No newline at end of file
...@@ -18,7 +18,8 @@ function getcolumns(setdrawer, drawer) { ...@@ -18,7 +18,8 @@ function getcolumns(setdrawer, drawer) {
open: true, open: true,
item: row, item: row,
val: 'only', val: 'only',
title: '合同详情' title: '合同详情',
type: 'detail'
})); }));
}} }}
> >
...@@ -71,13 +72,14 @@ function getcolumns(setdrawer, drawer) { ...@@ -71,13 +72,14 @@ function getcolumns(setdrawer, drawer) {
<a <a
className="table-cell" className="table-cell"
onClick={() => { onClick={() => {
// setdrawer && setdrawer((s) => ({ setdrawer && setdrawer((s) => ({
// ...s, ...s,
// open: true, open: true,
// item: row, item: row,
// val: 'only', val: 'only',
// title: '租赁设备' title: '租赁设备',
// })); type: 'device'
}));
}} }}
> >
{row.equipmentNum} {row.equipmentNum}
......
This diff is collapsed.
import React, { useMemo, useState } from 'react';
import DrawerPro from '@/components/DrawerPro';
import AutoTable from '@/components/AutoTable/mtable';
import PremButton from '@/components/PremButton';
import { Tooltip } from "antd";
import Filedetail from "@/components/Filedetail";
import { doFetch, exportFetch } from '@/utils/doFetch';
import { message } from "antd";
const commona = [
{
title: '当期金额',
dataIndex: 'thisPrice',
key: 'thisPrice',
search: false
},
{
title: '回款日期',
dataIndex: 'collectionDate',
key: 'collectionDate',
search: false
},
{
title: '预警日期',
dataIndex: 'warnDate',
key: 'warnDate',
search: false
},
{
title: '状态',
dataIndex: 'statusName',
key: 'statusName',
search: false
}
],
commonb = [
{
title: '是否回款',
dataIndex: 'isCollectionName',
key: 'isCollectionName',
search: false
},
{
title: '回款时间',
dataIndex: 'collectionTime',
key: 'collectionTime',
search: false
},
{
title: '回款形式',
dataIndex: 'collectionTypeName',
key: 'collectionTypeName',
search: false
}
],
columnsc = [
{
title: '基本信息',
valueType: 'split'
},
{
title: '期数',
dataIndex: 'thisTenancy',
key: 'thisTenancy'
},
{
title: '当期开始时间',
dataIndex: 'tenancyStartDate',
key: 'tenancyStartDate'
},
{
title: '当期结束时间',
dataIndex: 'tenancyEndDate',
key: 'tenancyEndDate'
},
...commona,
{
title: '回款信息',
valueType: 'split'
},
...commonb,
{
title: '回款确认人',
dataIndex: 'collectionUserName',
key: 'collectionUserName'
},
{
title: '是否逾期',
dataIndex: 'isOverdueName',
key: 'isOverdueName'
},
{
title: '减免租金信息',
valueType: 'split'
},
{
title: '当期租金减免金额',
dataIndex: 'reducePrice',
key: 'reducePrice'
},
{
title: '最近租金减免时间',
dataIndex: 'reduceTime',
key: 'reduceTime'
},
{
title: '最近租金减免人',
dataIndex: 'reduceUserName',
key: 'reduceUserName'
},
{
title: '减免情况说明',
dataIndex: 'reduceRemark',
key: 'reduceRemark'
},
{
title: '相关文件',
dataIndex: 'reduceFileFileList',
key: 'reduceFileFileList',
render: (_, row) => {
return <Filedetail files={row?.['reduceFileFileList']} />
}
},
{
title: '开票信息',
valueType: 'split'
},
{
title: '是否开票',
dataIndex: 'isOpenName',
key: 'isOpenName'
},
{
title: '开票时间',
dataIndex: 'openTime',
key: 'openTime'
},
{
title: '开票记录人',
dataIndex: 'openUserName',
key: 'openUserName'
},
];
const Leaseterminformation = ({ drawer, boxRef }) => {
const [detail, cd] = useState({
open: false,
});
const columns = [
{
title: '期数',
dataIndex: 'thisTenancy',
key: 'thisTenancy',
search: false
},
...commona,
...commonb,
{
title: '是否逾期',
dataIndex: 'isOverdueName',
key: 'isOverdueName',
search: false
},
{
title: '租金减免金额',
dataIndex: 'reducePrice',
key: 'reducePrice',
search: false
},
{
title: '租金情况说明',
dataIndex: 'reduceRemark',
key: 'reduceRemark',
search: false
},
{
title: '是否开票',
dataIndex: 'isOpenName',
key: 'isOpenName',
search: false
},
{
title: '操作',
valueType: 'option',
width: 80,
render: (text, row, _, action) => rightExtra(text, row, _, action),
}
];
const rightExtra = (text, row, _, action) => {
return [
<PremButton
key='confirm'
btn={{
size: 'small',
onClick: () => {
cd((s) => ({
...s,
open: true,
item: row,
title: '租期详情',
val: 'detail'
}));
},
}}
>
详情
</PremButton>
]
}
return <div style={{ position: "relative" }}>
<div style={{ display: 'flex', justifyContent: 'flex-end' }}>
<PremButton
key='export'
btn={{
type: 'primary',
onClick: async () => {
message.warning('导出中,请稍后');
await exportFetch({ url: '/lease/umContractEquipmentTenancy/exportExcelTenancy', params: { id: drawer?.item?.id } });
}
}}
>
导出
</PremButton>
</div>
<AutoTable
columns={columns}
path={'/lease/umContractEquipmentTenancy/queryByContractEquipmentId'}
resizeable={false}
bordered={false}
extraparams={{ id: drawer?.item?.id }}
options={false}
pagination='false'
/>
<DrawerPro
{...detail}
fields={columnsc}
params={{ id: detail?.item?.id }}
detailpath='/lease/umContractEquipmentTenancy/queryDetail'
placement="right"
onClose={() => {
cd((s) => ({
...s,
open: false,
}));
}}
getContainer={() => boxRef.current}
>
</DrawerPro>
</div>
}
export default Leaseterminformation;
\ No newline at end of file
import { Tooltip, Image, Tag } from 'antd';
import { doFetch } from '@/utils/doFetch';
const statusColors = {
1: '#7ac143',
2: 'orange',
3: '#fa4659'
};
function getcolumns(setdrawer, activeKey) {
const commona = [
{
title: '客户',
dataIndex: 'customerName',
key: 'customerId',
valueType: 'select',
options: { path: '/lease/umLeaseCustomer/getSelection', params: {} },
fieldProps: {
showSearch: true
},
fixed: 'left'
},
{
title: '合同单号',
dataIndex: 'contractNo',
key: 'contractNo',
fixed: 'left'
},
{
title: '设备编号',
dataIndex: 'equipmentNo',
key: 'equipmentNo'
},
{
title: '设备名称',
dataIndex: 'equipmentName',
key: 'equipmentName',
},
{
title: '智能单元莫格云眼编号',
dataIndex: 'intelligentUnitNo',
key: 'intelligentUnitNo',
width: 170
},
{
title: '每期单价(元)',
dataIndex: 'unitPrice',
key: 'unitPrice',
hideInSearch: true
},
{
title: '智享价(元)',
dataIndex: 'zxPrice',
key: 'zxPrice',
hideInSearch: true
},
{
title: '返利金额(元)',
dataIndex: 'rebatePrice',
key: 'rebatePrice',
hideInSearch: true
},
{
title: '押金(元)',
dataIndex: 'deposit',
key: 'deposit',
hideInSearch: true
}
],
commonb = [
{
title: '开票期数',
dataIndex: 'openNum',
key: 'openNum',
hideInSearch: true
},
{
title: '已发生租金',
dataIndex: 'occurredPrice',
key: 'occurredPrice',
hideInSearch: true
},
{
title: '累计逾期欠费',
dataIndex: 'overduePrice',
key: 'overduePrice',
hideInSearch: true
},
{
title: '累计回款金额',
dataIndex: 'collectionPrice',
key: 'collectionPrice',
hideInSearch: true
},
{
title: '逾期次数',
dataIndex: 'overdueNum',
key: 'overdueNum',
hideInSearch: true
},
{
title: '开始租赁时间',
dataIndex: 'startLeaseDate',
key: 'startLeaseDateList',
valueType: 'dateRange'
},
{
title: '结束租赁时间',
dataIndex: 'endLeaseDate',
key: 'endLeaseDateList',
valueType: 'dateRange'
},
{
title: '实际结束时间',
dataIndex: 'realEndDate',
key: 'realEndDateList',
valueType: 'dateRange'
}
];
return [
{
tab: '未完成',
key: 'item-1',
columns: [
...commona,
{
title: '当前租期/租期',
dataIndex: 'thisTenancy',
key: 'thisTenancy',
render: (_, row) => {
return (
<a
className="table-cell"
onClick={() => {
setdrawer && setdrawer((s) => ({
...s,
open: true,
item: row,
val: 'only',
title: '租期信息',
type: 'detail'
}));
}}
>
{row.thisTenancy + '/' + row.tenancyTotal}
</a>
);
},
hideInSearch: true
},
...commonb,
{
title: '回款状态',
dataIndex: 'collectionStatusName',
key: 'collectionStatus',
hideInForm: true,
valueType: 'select',
options: [
{ label: '正常', value: 1 },
{ label: '预警', value: 2 },
{ label: '报警', value: 3 }
],
fixed: 'right',
render: (_, row) => {
return <span style={{ color: statusColors[row.collectionStatus] }}>{row.collectionStatusName}</span>
},
width: 100
},
]
},
{
tab: '已完成',
key: 'item-2',
columns: [
...commona,
{
title: '租期',
dataIndex: 'tenancyTotal',
key: 'tenancyTotal',
render: (_, row) => {
return (
<a
className="table-cell"
onClick={() => {
setdrawer && setdrawer((s) => ({
...s,
open: true,
item: row,
val: 'only',
title: '租期信息',
type: 'detail'
}));
}}
>
{row.tenancyTotal}
</a>
);
},
hideInSearch: true
},
...commonb,]
}
]
}
export default getcolumns;
\ No newline at end of file
import React, { useState, useMemo, useRef, useEffect } from 'react';
import DrawerPro from '@/components/DrawerPro';
import AutoTable from '@/components/AutoTable';
import PremButton from '@/components/PremButton';
import getcolumns from './columns';
import { doFetch } from '@/utils/doFetch';
import { useRequest } from "ahooks";
import { Divider, Tabs, ProDescriptions, message } from "antd";
import dayjs from 'dayjs';
import Leaseterminformation from "./Leaseterminformation";
const Costanalysis = () => {
let actionRef = useRef(), formRef = useRef(), boxRef = useRef();
const [drawer, setdrawer] = useState({
open: false,
}),
[extraparams, setextraparams] = useState({}),
[activeKey, catk] = useState('item-1');
const { run, loading, runAsync } = useRequest(doFetch, {
manual: true,
onSuccess: (res, params) => {
if (res?.code == "0000") {
actionRef?.current?.reload();
setdrawer((s) => ({
...s,
open: false,
}));
}
},
});
const columns = useMemo(() => {
let defcolumn = getcolumns(setdrawer, activeKey)?.filter(it => it.key == activeKey)?.[0]?.columns ?? [];
return defcolumn.concat({
title: '操作',
valueType: 'option',
width: activeKey == 'item-1' ? 260 : 100,
render: (text, row, _, action) => rightExtra(text, row, _, action),
});
}, [activeKey]);
const rightExtra = (text, row, _, action) => {
return [
activeKey == 'item-1' && <PremButton
key='confirm'
btn={{
size: 'small',
onClick: async () => {
let res = await doFetch({ url: '/lease/umContractEquipment/queryBeforeCollection', params: { id: row.id } });
if (res?.data?.data?.waitNum == 0) {
message.warning('该设备需回款的租期已全部回款,无需再次回款!')
} else {
setdrawer((s) => ({
...s,
open: true,
item: { ...row, waitNum: res?.data?.data?.waitNum, isColl: res?.data?.data?.isColl },
val: 'confirm',
title: '回款确认',
fields: [
{
title: '本期是否回款',
dataIndex: 'isColl',
key: 'isColl',
fieldProps: {
disabled: true
},
colProps: { span: 8 },
},
{
title: '剩余回款期数',
dataIndex: 'waitNum',
key: 'waitNum',
fieldProps: {
disabled: true
},
colProps: { span: 8 },
},
{
title: '本次回款期数',
dataIndex: 'collNum',
key: 'collNum',
valueType: 'digit',
colProps: { span: 8 },
precision: 0,
formItemProps: { rules: [{ required: true, message: '此项为必填项' }] }
},
{
title: '回款形式',
dataIndex: 'collType',
key: 'collType',
valueType: 'select',
options: [
{ label: '现金', value: 1 },
{ label: '银行承兑', value: 2 },
],
colProps: { span: 8 },
formItemProps: { rules: [{ required: true, message: '此项为必填项' }] },
},
]
}));
}
},
}}
>
回款确认
</PremButton>,
activeKey == 'item-1' && <PremButton
key='reduction'
btn={{
size: 'small',
onClick: async () => {
},
}}
>
租金减免
</PremButton>,
<PremButton
key='invoicing'
btn={{
size: 'small',
onClick: async () => {
},
}}
>
开票记录
</PremButton>
]
};
return <div ref={boxRef}>
<AutoTable
pagetitle={<h3 className="page-title">费用分析</h3>}
columns={columns}
path={activeKey == 'item-1' ? '/lease/umContractEquipment/queryNoAnalysis' : '/lease/umContractEquipment/queryYesAnalysis'}
actionRef={actionRef}
resizeable={false}
bordered={false}
x={2500}
extraparams={extraparams}
onTabChange={(key) => {
catk(key);
}}
activeTabKey={activeKey}
tabList={getcolumns()}
/>
<DrawerPro
{...drawer}
fields={drawer.fields}
params={{ id: drawer?.item?.id }}
defaultFormValue={drawer?.item ?? {}}
placement="right"
onClose={() => {
setdrawer((s) => ({
...s,
open: false,
}));
}}
>
<Leaseterminformation drawer={drawer} boxRef={boxRef} />
</DrawerPro>
</div>
}
export default Costanalysis;
\ No newline at end of file
import { Tooltip, Image, Tag } from 'antd'; import { Tooltip, Image, Tag } from 'antd';
import { doFetch } from '@/utils/doFetch'; import { doFetch } from '@/utils/doFetch';
const statusColors = {
1: '#7ac143',
2: '#00bce4',
3: '#6a737b',
4: '#fa4659'
};
function getcolumns(setdrawer, activeKey) { function getcolumns(setdrawer, activeKey) {
return activeKey == 'item-1' ? { return activeKey == 'item-1' ? {
columns: [ columns: [
...@@ -130,12 +136,15 @@ function getcolumns(setdrawer, activeKey) { ...@@ -130,12 +136,15 @@ function getcolumns(setdrawer, activeKey) {
hideInForm: true, hideInForm: true,
valueType: 'select', valueType: 'select',
options: [ options: [
{ label: '工作', value: '1' }, { label: '工作', value: 1 },
{ label: '待机', value: '2' }, { label: '待机', value: 2 },
{ label: '关机', value: '3' }, { label: '关机', value: 3 },
{ label: '报警', value: '4' } { label: '报警', value: 4 }
], ],
fixed: 'right' fixed: 'right',
render: (_, row) => {
return <span style={{ color: statusColors[row.collectStatus] }}>{row.collectStatusName}</span>
}
}, },
{ {
title: '所属信息', title: '所属信息',
......
...@@ -10,6 +10,8 @@ import TreeRender from '@/components/TreeRender'; ...@@ -10,6 +10,8 @@ import TreeRender from '@/components/TreeRender';
import { Divider, Tabs, ProDescriptions } from "antd"; import { Divider, Tabs, ProDescriptions } from "antd";
import Account from "./Account"; import Account from "./Account";
import Basemsg from "./Basemsg"; import Basemsg from "./Basemsg";
import { useModel } from "@umijs/max";
import dayjs from 'dayjs';
const Devicedata = () => { const Devicedata = () => {
let actionRef = useRef(), formRef = useRef(), boxRef = useRef(); let actionRef = useRef(), formRef = useRef(), boxRef = useRef();
const [drawer, setdrawer] = useState({ const [drawer, setdrawer] = useState({
...@@ -18,7 +20,9 @@ const Devicedata = () => { ...@@ -18,7 +20,9 @@ const Devicedata = () => {
[extraparams, setextraparams] = useState({}), [extraparams, setextraparams] = useState({}),
[activeKey, catk] = useState('item-1'), [activeKey, catk] = useState('item-1'),
[item2extraparams, citp] = useState({}), [item2extraparams, citp] = useState({}),
[searchParams, csp] = useState({}); [searchParams, csp] = useState({}),
[leaseList, clea] = useState([]);
const { allWebsocket: { commonWebsocket, commonWebmsg } } = useModel('useGlobal');
const { run, loading, runAsync } = useRequest(doFetch, { const { run, loading, runAsync } = useRequest(doFetch, {
manual: true, manual: true,
onSuccess: (res, params) => { onSuccess: (res, params) => {
...@@ -31,12 +35,42 @@ const Devicedata = () => { ...@@ -31,12 +35,42 @@ const Devicedata = () => {
} }
}, },
}), }),
tableData = useRequest(async () => { tableData = useRequest(() => doFetch({ url: '/lease/umLeaseLedger/queryLeaseList', params: { ...searchParams, ...extraparams } }), {
let res = await doFetch({ url: '/lease/umLeaseLedger/queryLeaseList', params: { ...searchParams, ...extraparams } }); refreshDeps: [extraparams, searchParams],
return res?.data?.dataList ?? []; onSuccess: (res, params) => {
}, { if (res.code == '0000') {
refreshDeps: [extraparams, searchParams] clea(res?.data?.dataList ?? [])
}) }
}
});
useEffect(() => {
const msg = commonWebmsg?.msg;
if (commonWebmsg?.wsMsgModel == 'LEASE') {
let newList = JSON.parse(JSON.stringify(leaseList));
newList = newList?.map(it => {
if (it.id == msg.id) {
it.collectStatusName = msg.collectStatusName;
it.collectStatus = msg.collectStatus;
}
return it
});
clea(newList);
}
}, [commonWebmsg]);
useEffect(() => {
try {
commonWebsocket && commonWebsocket.emit("sendMessage", {
"wsMsgModel": "LEASE",
"subId": dayjs().valueOf(),
param: '',
unsubscribe: false,
});
} catch (error) {
}
}, []);
const columns = useMemo(() => { const columns = useMemo(() => {
let defcolumn = getcolumns(setdrawer, activeKey)?.columns ?? []; let defcolumn = getcolumns(setdrawer, activeKey)?.columns ?? [];
return defcolumn; return defcolumn;
...@@ -49,7 +83,7 @@ const Devicedata = () => { ...@@ -49,7 +83,7 @@ const Devicedata = () => {
key: 'item-1', key: 'item-1',
children: <AutoTable children: <AutoTable
columns={columns} columns={columns}
dataSource={tableData?.data ?? []} dataSource={leaseList}
actionRef={actionRef} actionRef={actionRef}
resizeable={false} resizeable={false}
bordered={false} bordered={false}
...@@ -70,7 +104,7 @@ const Devicedata = () => { ...@@ -70,7 +104,7 @@ const Devicedata = () => {
children: <Account boxRef={boxRef} extraparams={item2extraparams} /> children: <Account boxRef={boxRef} extraparams={item2extraparams} />
} }
] ]
}, [extraparams, tableData?.data]); }, [extraparams, leaseList]);
const items = useMemo(() => { const items = useMemo(() => {
if (drawer.val == 'only') { if (drawer.val == 'only') {
if (activeKey == 'item-1') { if (activeKey == 'item-1') {
......
...@@ -2,6 +2,7 @@ import React, { useState, useRef } from 'react'; ...@@ -2,6 +2,7 @@ import React, { useState, useRef } from 'react';
import AutoTable from '@/components/AutoTable/mtable'; import AutoTable from '@/components/AutoTable/mtable';
import { useRequest } from 'ahooks'; import { useRequest } from 'ahooks';
import { doFetch } from '@/utils/doFetch'; import { doFetch } from '@/utils/doFetch';
import styles from "./index.less";
const columnsa = [ const columnsa = [
{ {
title: '客户名称', title: '客户名称',
...@@ -42,7 +43,10 @@ const columnsa = [ ...@@ -42,7 +43,10 @@ const columnsa = [
dataIndex: 'equipmentModelName', dataIndex: 'equipmentModelName',
key: 'equipmentModelId', key: 'equipmentModelId',
valueType: 'select', valueType: 'select',
options: { path: '/asset/equipmentModel/query/selection', params: {} } options: { path: '/asset/equipmentModel/query/selection', params: {} },
fieldProps: {
showSearch: true
}
}, },
{ {
title: '设备品牌', title: '设备品牌',
...@@ -77,25 +81,37 @@ const columnsa = [ ...@@ -77,25 +81,37 @@ const columnsa = [
title: '工作', title: '工作',
dataIndex: 'runRate', dataIndex: 'runRate',
key: 'runRate', key: 'runRate',
search: false search: false,
render: (_, row) => {
return <span style={{ color: "#7ac143" }}>{row.runRate}%</span>
}
}, },
{ {
title: '待机', title: '待机',
dataIndex: 'standbyRate', dataIndex: 'standbyRate',
key: 'standbyRate', key: 'standbyRate',
search: false search: false,
render: (_, row) => {
return <span style={{ color: "#00bce4" }}>{row.runRate}%</span>
}
}, },
{ {
title: '报警', title: '报警',
dataIndex: 'alarmRate', dataIndex: 'alarmRate',
key: 'alarmRate', key: 'alarmRate',
search: false search: false,
render: (_, row) => {
return <span style={{ color: "#fa4659" }}>{row.runRate}%</span>
}
}, },
{ {
title: '关机', title: '关机',
dataIndex: 'offRate', dataIndex: 'offRate',
key: 'offRate', key: 'offRate',
search: false search: false,
render: (_, row) => {
return <span style={{ color: "#6a737b" }}>{row.runRate}%</span>
}
}, },
]; ];
...@@ -109,7 +125,7 @@ const Leasedevice = () => { ...@@ -109,7 +125,7 @@ const Leasedevice = () => {
return res?.data?.dataList ?? []; return res?.data?.dataList ?? [];
}, { }, {
refreshDeps: [searchParams] refreshDeps: [searchParams]
}) });
return <div style={{ background: '#fff' }}> return <div style={{ background: '#fff' }}>
<div className="ant-card-head" style={{ backgroundColor: 'white' }}> <div className="ant-card-head" style={{ backgroundColor: 'white' }}>
<div className="ant-card-head-wrapper"> <div className="ant-card-head-wrapper">
...@@ -128,7 +144,7 @@ const Leasedevice = () => { ...@@ -128,7 +144,7 @@ const Leasedevice = () => {
onRow={(record) => { onRow={(record) => {
return { return {
onClick: (event) => { onClick: (event) => {
console.log(event, record); cr(record);
} }
}; };
}} }}
...@@ -139,14 +155,19 @@ const Leasedevice = () => { ...@@ -139,14 +155,19 @@ const Leasedevice = () => {
}} }}
toolBarRender={true} toolBarRender={true}
options={false} options={false}
rowKey='ledgerCustomerId'
rowClassName={(record, index) => {
return record.ledgerCustomerId == clickrow?.ledgerCustomerId ? styles.rowClass : ''
}}
/> />
</div> </div>
<div style={{ flex: 2 }}> <div style={{ flex: 2 }}>
<AutoTable <AutoTable
columns={columnsb} columns={columnsb}
path='/lease/umLeaseLedger/ledgerCustomerEquipmentUseDetail' path={clickrow?.ledgerCustomerId ? '/lease/umLeaseLedger/ledgerCustomerEquipmentUseDetail' : ''}
resizeable={false} resizeable={false}
pageextra="none" pageextra="none"
extraparams={{ ledgerCustomerId: clickrow?.ledgerCustomerId }}
/> />
</div> </div>
</div> </div>
......
.rowClass {
background-color: #e6f4ff;
}
\ No newline at end of file
...@@ -10,9 +10,9 @@ import TreeRender from '@/components/TreeRender'; ...@@ -10,9 +10,9 @@ import TreeRender from '@/components/TreeRender';
const { RangePicker } = DatePicker; const { RangePicker } = DatePicker;
const statusColors = { const statusColors = {
报警: '#fa4659', 报警: '#fa4659',
工作: '#00fff5', 工作: '#7ac143',
待机: '#80d6ff', 待机: '#00bce4',
关机: '#194769' 关机: '#6a737b'
}, },
format = 'YYYY-MM-DD HH:mm:ss'; format = 'YYYY-MM-DD HH:mm:ss';
const Devicestatus = ({ drawer }) => { const Devicestatus = ({ drawer }) => {
...@@ -151,14 +151,14 @@ const Devicestatus = ({ drawer }) => { ...@@ -151,14 +151,14 @@ const Devicestatus = ({ drawer }) => {
<div style={{ display: "flex", flexWrap: "wrap", gap: 15 }}> <div style={{ display: "flex", flexWrap: "wrap", gap: 15 }}>
{ {
Object?.keys(statusColors)?.map(it => { Object?.keys(statusColors)?.map(it => {
return <div key={it} style={{ width: '30%', display: 'flex', gap: 16, justifyContent: 'center' }} > return <div key={it} style={{ width: '31%', display: 'flex', gap: 16, justifyContent: 'center' }} >
<div style={{ color: statusColors[it] }}>{it}时长</div> <div style={{ color: statusColors[it] }}>{it}时长</div>
<div>{chartdata?.data?.data?.rateList?.filter(item => item.name == it)?.[0]?.time ?? 0}小时</div> <div>{chartdata?.data?.data?.rateList?.filter(item => item.name == it)?.[0]?.time ?? 0}小时</div>
<div>{chartdata?.data?.data?.rateList?.filter(item => item.name == it)?.[0]?.rate ?? 0}%</div> <div>{chartdata?.data?.data?.rateList?.filter(item => item.name == it)?.[0]?.rate ?? 0}%</div>
</div> </div>
}) })
} }
<div style={{ width: '30%', display: 'flex', gap: 16, justifyContent: 'center' }} > <div style={{ width: '31%', display: 'flex', gap: 16, justifyContent: 'center' }} >
<div>总时长</div> <div>总时长</div>
<div>{chartdata?.data?.data?.timeRange}小时</div> <div>{chartdata?.data?.data?.timeRange}小时</div>
</div> </div>
......
This diff is collapsed.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment