fix: 新增临时费用;修复审核相关; 信息账单复制;

This commit is contained in:
uiujun 2026-06-12 14:55:35 +08:00
parent aa77a2e5e7
commit 60cb6f16e0
54 changed files with 5573 additions and 3508 deletions

View File

@ -1,9 +1,20 @@
import { MyIcons, MyIconsType, PermissionsType, useMyState } from '@/common';
import AvatarProps from '@/common/components/layout/AvatarProps';
import { Apis } from '@/gen/Apis';
import { BellOutlined } from '@ant-design/icons';
import { Link, RuntimeConfig, history, useNavigate } from '@umijs/max';
import { AutoComplete, Input, Menu, MenuProps, Select, Space } from 'antd';
import { useEffect, useState } from 'react';
import {
AutoComplete,
Badge,
Button,
Input,
Menu,
MenuProps,
Select,
Space,
Tooltip,
} from 'antd';
import { useEffect, useRef, useState } from 'react';
import './allConfig.scss';
// import Logo from './logo.png';
interface LevelKeysProps {
@ -61,6 +72,41 @@ export const LayoutConfig: RuntimeConfig['layout'] = () => {
const [getSelectProject, setSelectProject] = useState<LevelKeysProps[]>([]);
const { snap } = useMyState();
const navigate = useNavigate();
const [pendingCount, setPendingCount] = useState<number>();
const intervalRef = useRef<NodeJS.Timeout | null>(null);
// 获取待审核数量
const fetchPendingCount = async () => {
try {
const res = await Apis.Approval.ApprovalInstances.PendingCount();
setPendingCount(res?.data?.count || '');
} catch (error) {
console.error('Failed to fetch pending count:', error);
}
};
// 组件挂载和用户状态变化时获取数据
useEffect(() => {
if (!snap.session.user) {
return;
}
fetchPendingCount();
if (intervalRef.current !== null) {
clearInterval(intervalRef.current);
}
const interval = setInterval(() => {
fetchPendingCount().catch((error) => {
console.error('Interval fetch failed:', error);
});
}, 3000000);
intervalRef.current = interval;
return () => {
if (intervalRef.current !== null) {
clearInterval(intervalRef.current);
intervalRef.current = null;
}
};
}, [snap.session.user]);
const permissionsList = (snap.session.permissions || [])
.filter((p: any) => p.type !== 'Button' && p.path)
.sort((a: any, b: any) => a._lft - b._lft)
@ -74,7 +120,7 @@ export const LayoutConfig: RuntimeConfig['layout'] = () => {
const handleLoadProject = async () => {
let res = await Apis.Common.Auth.GetProjects();
setSelectProject(
res?.data?.map((item) => ({
res?.data?.map((item: any) => ({
value: item.id,
label: item.name,
})),
@ -130,12 +176,6 @@ export const LayoutConfig: RuntimeConfig['layout'] = () => {
<HeaderSearch permissionsList={permissionsList} />
</div>
<Space size={10}>
{/* <Button
type="default"
shape="circle"
icon={<SettingOutlined />}
onClick={() => history.push('/system/sys_permissions')}
/> */}
<Select
onSearch={handleLoadProject}
options={getSelectProject}
@ -152,6 +192,16 @@ export const LayoutConfig: RuntimeConfig['layout'] = () => {
}}
placeholder="选择项目"
/>
<Tooltip title="待审核">
<Badge count={pendingCount} showZero>
<Button
type="default"
shape="circle"
icon={<BellOutlined />}
onClick={() => history.push('/approval/pending')}
/>
</Badge>
</Tooltip>
</Space>
</div>
),

146
src/gen/ApiTypes.d.ts vendored
View File

@ -743,17 +743,19 @@ declare namespace ApiTypes {
}
namespace AttendanceSchedules {
type List = {
"organizations_id"?: number; // 组织机构ID
"company_employees_id"?: number; // 员工ID
"asset_projects_id"?: number; // 项目ID
"schedule_date"?: Date; // 排班日期
"status"?: string; // 状态,[enum:AttendanceSchedulesStatusEnum]
"project_name"?: string; // 项目名称
"employee_name"?: string; // -
"schedule_dates"?: string[]; // -
};
type Store = {
"company_employees_id": number; // 员工ID
"attendance_shifts_id": number; // 班次ID
"asset_projects_id": number; // 项目ID
"asset_projects_id"?: number; // 项目ID
"schedule_date": Date; // 排班日期
"remark"?: string; // 备注
};
@ -801,7 +803,7 @@ declare namespace ApiTypes {
};
type Store = {
"name": string; // 班次名称
"asset_projects_id": number; // 关联项目IDs
"asset_projects_id"?: number; // 关联项目IDs
"allow_checkin_start": date_format:H:i:s; // 可打卡开始时间
"allow_checkin_end": date_format:H:i:s; // 可打卡结束时间
"is_enabled"?: boolean; // 状态
@ -894,6 +896,45 @@ declare namespace ApiTypes {
"type"?: string; // 类型,[enum:BillPaymentsTypeEnum]
"flow_type"?: string; // 收支类型,[enum:BillsFlowTypeEnum]
"status"?: string; // 账单状态,[enum:BillsStatusEnum]
"year"?: number; // 年
"month"?: number; // 月
};
type Store = {
"type": string; // 类型,[enum:BillPaymentsTypeEnum]
"flow_type": string; // 收支类型,[enum:BillsFlowTypeEnum]
"business_id"?: number; // 业务记录ID根据type自动关联对应模型
"amount": number; // 金额(元)
"payable_amount"?: number; // 应付金额不传则等于amount
"phone"?: string; // 手机号
"year"?: number; // 年
"month"?: number; // 月
"company_receipt_accounts_id": number; // 公司收款账户id,[ref:company_receipt_accounts]
"payer"?: string; // 付款人
"payer_bank"?: string; // 付款银行
"payer_account"?: string; // 付款账号
"asset_projects_id"?: number; // 项目ID,[ref:asset_projects]
"asset_buildings_id"?: number; // 楼栋ID,[ref:asset_buildings]
"asset_units_id"?: number; // 单元ID,[ref:asset_units]
"asset_houses_id"?: number; // 房屋ID,[ref:asset_houses]
"remark"?: string; // 备注
};
type Update = {
"id": number; // 账单ID
"type"?: string; // 类型,[enum:BillPaymentsTypeEnum]
"flow_type"?: string; // 收支类型,[enum:BillsFlowTypeEnum]
"amount"?: number; // 金额(元)
"payable_amount"?: number; // 应付金额(元)
"phone"?: string; // 手机号
"year"?: number; // 年
"month"?: number; // 月
"company_receipt_accounts_id": number; // 公司收款账户id,[ref:company_receipt_accounts]
"payer"?: string; // 付款人
"payer_bank"?: string; // 付款银行
"payer_account"?: string; // 付款账号
"remark"?: string; // 备注
"approval_templates_id": number; // 审批模板id
"approval_remark"?: string; // 审批备注
"node_approvers"?: string[]; // 各节点审批人员列表
};
type Show = {
"id": number; // id
@ -907,6 +948,38 @@ declare namespace ApiTypes {
type Delete = {
"id": number; // id
};
type Export = {
"phone"?: string; // 模糊搜索:手机号
"type"?: string; // 类型,[enum:BillPaymentsTypeEnum]
"flow_type"?: string; // 收支类型,[enum:BillsFlowTypeEnum]
"status"?: string; // 账单状态,[enum:BillsStatusEnum]
"year"?: number; // 年
"month"?: number; // 月
"asset_projects_id"?: number; // 项目ID,[ref:asset_projects]
"current"?: number; // 页码download_type=page时使用
"download_type": string; // 下载类型page 当前页(含查询条件)query 所有页(含查询条件)all所有记录
};
type OfflinePayment = {
"id": number; // 账单ID
"payment_method": string; // 支付方式,[enum:HouseOrdersPaymentMethodEnum]
"paid_time": Date; // 支付时间
"accept_account_name"?: string; // 收款账户名称
"accept_account_number"?: string; // 收款账号
"accept_serial_number"?: string; // 收款流水号
"pay_certificate"?: string[]; // 支付凭证(图片)
"remark"?: string; // 备注
};
type GetPayCode = {
"id": number; // 账单ID
};
type AlipayQrCode = {
"id": number; // 账单ID
};
type AuditPayment = {
"id": number; // 支付记录IDbill_payments.id
"status": string; // 审核状态,[enum:BillPaymentsStatusEnum]
"rejected_reason"?: string; // 驳回原因
};
}
namespace HouseBills {
type List = {
@ -918,7 +991,10 @@ declare namespace ApiTypes {
"year"?: number; // 账单年份
"month"?: number; // 账单月份
"type"?: string; // 账单类型,[enum:HouseBillsTypeEnum]
"types"?: string[]; // 账单类型,[enum:HouseBillsTypeEnum]
"has_refunding"?: boolean; // 是否有退款中:false-无,true-有
"car_port_name"?: string; // 模糊搜索:车位名称
"paid_time"?: string[]; // 支付时间范围
};
type SummaryBillList = {
"project_name"?: string; // 模糊搜索:项目名称
@ -986,6 +1062,9 @@ declare namespace ApiTypes {
"year"?: number; // 账单年份
"month"?: number; // 账单月份
"type"?: string; // 账单类型,[enum:HouseBillsTypeEnum]
"types"?: string[]; // 账单类型(数组),[enum:HouseBillsTypeEnum]
"car_port_name"?: string; // 模糊搜索:车位名称
"paid_time"?: string[]; // 支付时间范围
"current"?: number; // 页码
"download_type": string; // 下载类型page 当前页(含查询条件)query 所有页(含查询条件)all所有记录
};
@ -2773,6 +2852,69 @@ declare namespace ApiTypes {
"id": number; // id
};
}
namespace HouseDoorCards {
type List = {
"card_number"?: string; // 模糊搜索:卡号
"is_enable"?: boolean; // 是否启用:0-否,1-是
"name"?: string; // 模糊搜索:客户姓名
"phone"?: string; // 模糊搜索:手机号码
"full_name"?: string; // 模糊搜索:房屋名称
};
type Store = {
"card_number": string; // 门禁卡编号
"name": string; // 客户姓名
"phone": string; // 手机号码
"process_date": Date; // 办理日期
"is_enable": boolean; // 是否启用:0-否,1-是
"labor_cost": number; // 人工成本费(元)
"asset_houses_id": number; // 房屋id,[ref:asset_houses]
};
type Update = {
"id": number; // id
"card_number": string; // 门禁卡编号
"name": string; // 客户姓名
"phone": string; // 手机号码
"process_date": Date; // 办理日期
"is_enable": number; // 是否启用:0-否,1-是
"labor_cost": number; // 人工成本费(元)
"asset_houses_id": number; // 房屋id,[ref:asset_houses]
};
type Show = {
"id": number; // id
};
type SoftDelete = {
"id": number; // id
};
type Restore = {
"id": number; // id
};
type Delete = {
"id": number; // id
};
type CreateBill = {
"id": number; // 门卡id
"amount": number; // 金额(元)
"payment_method": string; // 支付方式,[enum:HouseOrdersPaymentMethodEnum]
"company_receipt_accounts_id": number; // 收款账号id,[ref:company_receipt_accounts]
"accept_serial_number"?: string; // 收款流水号(线下支付必填)
"pay_certificate"?: string[]; // 支付凭证(线下支付必填)
"remark"?: string; // 备注
};
type GetBill = {
"id": number; // 门卡id
};
type OfflinePay = {
"bills_id": number; // 账单id
"amount": number; // 金额(元)
"payment_method": string; // 支付方式,[enum:HouseOrdersPaymentMethodEnum]
"paid_time": Date; // 支付时间
"accept_account_name"?: string; // 收款账号名称
"accept_account_number"?: string; // 收款账号
"accept_serial_number": string; // 收款流水号
"pay_certificate"?: string[]; // 支付凭证
"remark"?: string; // 备注
};
}
}
namespace HouseOrder {
namespace HouseOrderPayments {

File diff suppressed because it is too large Load Diff

View File

@ -71,6 +71,7 @@ export const ApprovalTemplatesTypeEnum= {
'ContractPayment': {"text":"合同支付","color":"#00bcd4","value":"ContractPayment"},
'OtherContractSeal': {"text":"其它合同用印","color":"#795548","value":"OtherContractSeal"},
'HouseBillUpdate': {"text":"物业账单修改","color":"#607d8b","value":"HouseBillUpdate"},
'BillUpdate': {"text":"其他账单修改","color":"#795548","value":"BillUpdate"},
};
// 车位产权类型
@ -327,13 +328,15 @@ export const BillsRefundStatusEnum= {
export const BillsStatusEnum= {
'PendingPayment': {"text":"待支付","color":"#facc15","value":"PendingPayment"},
'Paid': {"text":"已支付","color":"#10b981","value":"Paid"},
'ToBeConfirmed': {"text":"待确认","color":"#f97316","value":"ToBeConfirmed"},
'Overdue': {"text":"已逾期","color":"#ef4444","value":"Overdue"},
'UnderApproval': {"text":"审批中","color":"#8b5cf6","value":"UnderApproval"},
'Cancelled': {"text":"已取消","color":"#9ca3af","value":"Cancelled"},
};
// 缓存类型
export const CacheTypeEnum= {
'MobilePhoneVerificationCode': {"text":"手机验证码","color":"#40ec73","value":"MobilePhoneVerificationCode"},
'MobilePhoneVerificationCode': {"text":"手机验证码","color":"#40a1af","value":"MobilePhoneVerificationCode"},
};
// CompaniesMerchantTypeEnum

View File

@ -1,4 +1,5 @@
import { MyColumns, MyPageContainer, MyProTableProps } from '@/common';
import { Selects } from '@/components/Select';
import { Apis } from '@/gen/Apis';
import {
ApprovalInstancesStatusEnum,
@ -6,13 +7,14 @@ import {
} from '@/gen/Enums';
import { ProTable } from '@ant-design/pro-components';
import { Space } from 'antd';
import Show from './modals/Show';
export default function Index({ title = '抄送我的' }) {
import AuditShow from '../common_info/AuditShow';
export default function Index({ title = '审批列表' }) {
return (
<MyPageContainer
title={title}
enableTabs={true}
tabKey="my_apply_list"
tabKey="all_approval_instances"
tabLabel={title}
>
<ProTable
@ -21,12 +23,28 @@ export default function Index({ title = '抄送我的' }) {
MyProTableProps.request(
params,
sort,
Apis.Approval.ApprovalInstances.CcList,
Apis.Approval.ApprovalInstances.List,
)
}
headerTitle={title}
columns={[
MyColumns.ID({ search: false }),
Selects?.AssetProjects({
title: '选择项目',
key: 'asset_projects_id',
hidden: true,
}),
MyColumns.EnumTag({
title: '状态',
dataIndex: 'status',
valueEnum: ApprovalInstancesStatusEnum,
}),
MyColumns.EnumTag({
dataIndex: 'type',
title: '类型',
valueEnum: ApprovalTemplatesTypeEnum,
search: false,
}),
{
title: '申请事项',
dataIndex: 'title',
@ -37,33 +55,29 @@ export default function Index({ title = '抄送我的' }) {
dataIndex: ['asset_project', 'name'],
search: false,
},
MyColumns.EnumTag({
title: '状态',
dataIndex: 'status',
valueEnum: ApprovalInstancesStatusEnum,
}),
MyColumns.EnumTag({
dataIndex: 'type',
title: '业务类型',
valueEnum: ApprovalTemplatesTypeEnum,
{
title: '申请人',
dataIndex: ['applicant', 'name'],
search: false,
}),
// {
// title: '项目ID',
// dataIndex: 'asset_projects_id',
// hidden: true,
// },
// {
// title: '申请人',
// dataIndex: 'applicant_name',
// search: false,
// },
MyColumns.CreatedAt(),
},
{
title: '当前审批人',
dataIndex: ['approval_records', 'approver_name'],
search: false,
render: (_, item: any) =>
`${item?.approval_records?.[0]?.company_employee?.name || ''}-${
item?.approval_records?.[0]?.company_employee?.phone || ''
}`,
},
{
title: '申请人',
dataIndex: 'applicant_name',
hidden: true,
},
MyColumns.Option({
render: (_, item: any, index, action) => (
<Space key={index}>
<Show item={item} reload={action?.reload} title={title} />
<AuditShow item={item} reload={action?.reload} title={title} />
</Space>
),
}),

View File

@ -1,250 +0,0 @@
import {
MyBetaModalFormProps,
MyButtons,
MyModalFormProps,
renderTextHelper,
} from '@/common';
import { Apis } from '@/gen/Apis';
import { RefundsTypeEnum } from '@/gen/Enums';
import BIllInfo from '@/pages/bills/house_bills/modals/BIllInfo';
import {
BetaSchemaForm,
ProCard,
ProDescriptions,
} from '@ant-design/pro-components';
import { useNavigate } from '@umijs/max';
import { Form, Space, Steps } from 'antd';
export default function Update(props: MyBetaModalFormProps) {
const [form] = Form.useForm();
const navigate = useNavigate();
return (
<BetaSchemaForm<ApiTypes.Archive.HouseRegisters.Update>
{...MyModalFormProps.props}
title={props.title}
trigger={<MyButtons.Default title="查看" type="primary" />}
wrapperCol={{ span: 24 }}
width="600px"
key={new Date().getTime()}
form={form}
onOpenChange={() => {
if (props?.item?.id) {
Apis.Approval.ApprovalInstances.Show({
id: props.item?.model_id,
}).then((res) => {
form.setFieldsValue({
info_display: res?.data,
});
});
}
}}
columns={[
{
// title: '登记信息',
dataIndex: 'info_display',
valueType: 'text',
renderFormItem: (_, config) => (
<Space direction="vertical" style={{ width: '100%' }}>
<div>
{/* 退款详情 */}
{config?.value?.type === 'Refund' && (
<ProCard size="small">
<ProDescriptions size="small" column={2}>
<ProDescriptions.Item label="申请事项" span={2}>
{config?.value?.title || '-'}
</ProDescriptions.Item>
<ProDescriptions.Item label="账单类型">
<renderTextHelper.Tag
Enums={RefundsTypeEnum}
value={config?.value?.model?.type}
/>
</ProDescriptions.Item>
<ProDescriptions.Item label="退款金额">
<Space>
{config?.value?.model?.refund_amount || '-'}
<BIllInfo
item={{
id: config?.value?.model?.refundable_id,
type: 'link',
}}
title="查看账单"
/>
</Space>
</ProDescriptions.Item>
<ProDescriptions.Item label="付款信息" span={2}>
{config?.value?.model?.payer_name || '-'}
{config?.value?.model?.payer_bank || '-'}
{config?.value?.model?.payer_account || '-'}
</ProDescriptions.Item>
<ProDescriptions.Item label="收款信息" span={2}>
{config?.value?.model?.payee_name || '-'}
{config?.value?.model?.payee_bank || '-'}
{config?.value?.model?.payee_account || '-'}
</ProDescriptions.Item>
<ProDescriptions.Item label="申请人" span={2}>
{props?.item?.applicant?.name || '-'}:
{props?.item?.applicant?.phone || '-'}
</ProDescriptions.Item>
<ProDescriptions.Item label="申请时间">
{props?.item?.created_at || '-'}
</ProDescriptions.Item>
</ProDescriptions>
</ProCard>
)}
{/* 合同详情 */}
{config?.value?.type === 'Contract' && (
<ProCard size="small">
<ProDescriptions size="small" column={2}>
<ProDescriptions.Item label="合同名称" span={2}>
{config?.value?.model?.name || '-'}
</ProDescriptions.Item>
<ProDescriptions.Item label="合同编号" span={2}>
<Space>
{config?.value?.model?.code || '-'}
<BIllInfo
item={{
id: config?.value?.model?.refundable_id,
type: 'Contract',
}}
title="查看合同"
/>
<MyButtons.View
title="查看"
key="configInfo"
onClick={() => {
navigate(
`/contract/contracts/show/${config?.value?.model?.id}`,
);
}}
/>
</Space>
</ProDescriptions.Item>
{/* <ProDescriptions.Item label="">
<renderTextHelper.Tag
Enums={ContractTemplatesIncomeExpenseTypeEnum}
value={config?.value?.model?.income_expense_type}
/>
</ProDescriptions.Item> */}
{/* <ProDescriptions.Item label="">
<renderTextHelper.Tag
Enums={ContractsContractNatureEnum}
value={config?.value?.model?.contract_nature}
/>
</ProDescriptions.Item> */}
<ProDescriptions.Item label="合同类型">
{config?.value?.model?.contract_type_name || '-'}
</ProDescriptions.Item>
{/* <ProDescriptions.Item label="">
<renderTextHelper.Tag
Enums={ContractsSettlementModeEnum}
value={config?.value?.model?.settlement_mode}
/>
</ProDescriptions.Item> */}
<ProDescriptions.Item label="合同金额">
{config?.value?.model?.total_amount || '-'}
</ProDescriptions.Item>
<ProDescriptions.Item label="保证金金额">
{config?.value?.model?.deposit_amount || '无'}
</ProDescriptions.Item>
<ProDescriptions.Item label="签约主体">
{config?.value?.model?.sign_subject || '-'}
</ProDescriptions.Item>
<ProDescriptions.Item label="签约部门">
{config?.value?.model?.sign_department || '-'}
</ProDescriptions.Item>
<ProDescriptions.Item label="关联项目">
{config?.value?.model?.project_name || '-'}
</ProDescriptions.Item>
<ProDescriptions.Item label="合同对接人">
{config?.value?.model?.contract_liaison || '-'}
</ProDescriptions.Item>
<ProDescriptions.Item label="合同有效期" span={2}>
{config?.value?.model?.start_time?.substring(0, 10)}
{config?.value?.model?.end_time?.substring(0, 10)}
</ProDescriptions.Item>
<ProDescriptions.Item label="申请人" span={2}>
{props?.item?.applicant?.name || '-'}:
{props?.item?.applicant?.phone || '-'}
</ProDescriptions.Item>
<ProDescriptions.Item label="申请时间" span={2}>
{props?.item?.created_at || '-'}
</ProDescriptions.Item>
<ProDescriptions.Item label="附件" span={2}>
{config?.value?.model?.attachments.map((item: any) => {
const handleDownload = async (
e: React.MouseEvent,
) => {
e.preventDefault();
try {
const response = await fetch(item.url);
const blob = await response.blob();
const url = window.URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = item.name;
document.body.appendChild(a);
a.click();
window.URL.revokeObjectURL(url);
document.body.removeChild(a);
} catch (error) {
console.error('下载失败:', error);
// 如果下载失败,则在新窗口打开
window.open(item.url, '_blank');
}
};
return (
<div key={item.url}>
<a href={item.url} onClick={handleDownload}>
{item.name}
</a>
</div>
);
})}
</ProDescriptions.Item>
</ProDescriptions>
</ProCard>
)}
<ProCard>
<ProDescriptions>
<ProDescriptions.Item label="审核记录">
<Space direction="vertical" style={{ width: '100%' }}>
<Steps
progressDot
direction="vertical"
current={config?.value?.approval_records.length}
items={config?.value?.approval_records.map(
(item: any) =>
item?.node_type === 'Approver'
? {
title: `${
item.company_employee?.name || '-'
}-${item?.company_employee?.phone}`,
description: `${
item.status === 'Approved'
? `通过 - ${item.opinion || '-'} - ${
item.created_at || '-'
}`
: '待审核'
}`,
}
: '',
)}
/>
</Space>
</ProDescriptions.Item>
</ProDescriptions>
</ProCard>
</div>
</Space>
),
colProps: { span: 24 },
},
]}
/>
);
}

View File

@ -0,0 +1,64 @@
import { MyBetaModalFormProps } from '@/common';
import React from 'react';
import { ProCard, ProDescriptions } from '@ant-design/pro-components';
import { useNavigate } from '@umijs/max';
import { Form, Space, Steps } from 'antd';
export default function Update(props: MyBetaModalFormProps) {
const [form] = Form.useForm();
const navigate = useNavigate();
return (
<>
<Space direction="vertical" style={{ width: '100%' }}>
<ProCard title="审批流程" size="small">
<ProDescriptions.Item label="审核记录">
<Space direction="vertical" style={{ width: '100%' }}>
<Steps
progressDot
direction="vertical"
current={props?.item?.approval_records.length - 1}
items={props?.item?.approval_records.map((item: any) =>
item?.node_type === 'Approver'
? {
title: `${item.company_employee?.name || '-'}-${
item?.company_employee?.phone
}`,
description: `${
item.status === 'Approved'
? `通过 - ${item.opinion || '-'} - ${
item.created_at || '-'
}`
: item.status === 'Rejected'
? `拒绝 - ${item.opinion || '-'} - ${
item.created_at || '-'
}`
: '待审核'
}`,
}
: '',
)}
/>
</Space>
</ProDescriptions.Item>
{props?.item?.approval_records?.filter(
(item: any) => item?.node_type === 'CC',
).length > 0 && (
<ProDescriptions.Item label="抄送对象">
{props?.item?.approval_records
.filter((item: any) => item?.node_type === 'CC')
.map((item: any, index: number) => (
<React.Fragment key={index}>
{index > 0 && ''}
{item.company_employee?.name || '-'}-
{item?.company_employee?.phone || '-'}
</React.Fragment>
))}
</ProDescriptions.Item>
)}
</ProCard>
</Space>
</>
);
}

View File

@ -0,0 +1,57 @@
import { MyBetaModalFormProps } from '@/common';
import ArchiveShow from '@/pages/contract/contract_archives/modals/Show';
import ArchiveBorrowShow from '@/pages/contract/contract_borrows/borrows/modals/Show';
import { ProCard, ProDescriptions } from '@ant-design/pro-components';
import { useNavigate } from '@umijs/max';
import { Form, Space } from 'antd';
export default function Update(props: MyBetaModalFormProps) {
const [form] = Form.useForm();
const navigate = useNavigate();
return (
<>
<Space direction="vertical" style={{ width: '100%' }}>
<ProCard title="基本信息" size="small">
<ProDescriptions size="small" column={2}>
<ProDescriptions.Item label="档案id">
{props?.item?.model?.contract_archives_id}
<ArchiveShow
item={{
id: props?.item?.model?.contract_archives_id,
type: 'link',
}}
title="查看档案"
/>
</ProDescriptions.Item>
<ProDescriptions.Item label="借阅人">
{props?.item?.model?.borrower_name}
</ProDescriptions.Item>
<ProDescriptions.Item label="借阅日期">
{props?.item?.model?.borrow_date}
</ProDescriptions.Item>
<ProDescriptions.Item label="是否需要归还">
{props?.item?.model?.is_need_return ? '是' : '否'}
</ProDescriptions.Item>
<ProDescriptions.Item label="预计归还日期">
{props?.item?.model?.expected_return_date}
</ProDescriptions.Item>
<ProDescriptions.Item label="申请说明" span={2}>
{props?.item?.model?.remark}
</ProDescriptions.Item>
<ProDescriptions.Item label="借阅文件" span={2}>
<ArchiveBorrowShow
item={{
id: props?.item?.model?.id,
type: 'link',
}}
title="查看详情"
/>
</ProDescriptions.Item>
</ProDescriptions>
</ProCard>
</Space>
</>
);
}

View File

@ -0,0 +1,86 @@
import { MyBetaModalFormProps, MyButtons, MyModalFormProps } from '@/common';
import { Apis } from '@/gen/Apis';
import { BetaSchemaForm } from '@ant-design/pro-components';
import { useNavigate } from '@umijs/max';
import { Form, Space } from 'antd';
import ApprovalRecord from './ApprovalRecord';
import ArchiveBorrowInfo from './ArchiveBorrowInfo';
import ChargeStandardInfo from './ChargeStandardInfo';
import ContractInfo from './ContractInfo';
import ContractPaymentInfo from './ContractPaymentInfo';
import ContractTerminationInfo from './ContractTerminationInfo';
import RefundInfo from './RefundInfo';
import SealInfo from './SealInfo';
import {
default as BillUpdateInfo,
default as TemporaryBillUpdateInfo,
} from './TemporaryBillUpdateInfo';
export default function AuditShow(props: MyBetaModalFormProps) {
const [form] = Form.useForm();
const navigate = useNavigate();
return (
<BetaSchemaForm<ApiTypes.Approval.ApprovalInstances.Show>
{...MyModalFormProps.props}
title={props.title}
trigger={<MyButtons.Default title="查看" type="primary" />}
wrapperCol={{ span: 24 }}
width="600px"
key={new Date().getTime()}
form={form}
onOpenChange={() => {
if (props?.item?.id) {
Apis.Approval.ApprovalInstances.Show({
id: props.item?.id,
}).then((res) => {
form.setFieldsValue({
info_display: res?.data,
});
});
}
}}
columns={[
{
// title: '登记信息',
dataIndex: 'info_display',
valueType: 'text',
renderFormItem: (_, config) => (
<Space direction="vertical" style={{ width: '100%' }}>
{config?.value?.type === 'Contract' && (
<ContractInfo item={config?.value} />
)}
{config?.value?.type === 'ContractTermination' && (
<ContractTerminationInfo item={config?.value} />
)}
{config?.value?.type === 'ContractPayment' && (
<ContractPaymentInfo item={config?.value} />
)}
{config?.value?.type === 'Refund' && (
<RefundInfo item={config?.value} />
)}
{config?.value?.type === 'ContractBorrow' && (
<ArchiveBorrowInfo item={config?.value} />
)}
{config?.value?.type === 'OtherContractSeal' && (
<SealInfo item={config?.value} />
)}
{config?.value?.type === 'HouseBillUpdate' && (
<BillUpdateInfo item={config?.value} />
)}
{config?.value?.type === 'ChargeStandardModification' && (
<ChargeStandardInfo item={config?.value} />
)}
{config?.value?.type === 'BillUpdate' && (
<TemporaryBillUpdateInfo item={config?.value} />
)}
<ApprovalRecord item={config?.value} />
</Space>
),
colProps: { span: 24 },
},
]}
/>
);
}

View File

@ -0,0 +1,82 @@
import { MyBetaModalFormProps, renderTextHelper } from '@/common';
import { HouseBillsTypeEnum } from '@/gen/Enums';
import BIllInfo from '@/pages/bills/house_bills/modals/BIllInfo';
import { ProCard, ProDescriptions } from '@ant-design/pro-components';
import { Space } from 'antd';
export default function BillUpdateInfo(props: MyBetaModalFormProps) {
const originalData = props?.item?.model?.original_data;
const newData = props?.item?.model?.new_data;
// 格式化金额
const formatAmount = (amount: any) => {
if (!amount || amount === '-') return '-';
return parseFloat(amount).toFixed(2) + ' 元';
};
// 渲染数据项
const renderDataItems = (data: any) => (
<ProDescriptions size="small" column={1}>
<ProDescriptions.Item label="账单类型">
<renderTextHelper.Tag Enums={HouseBillsTypeEnum} value={data?.type} />
</ProDescriptions.Item>
<ProDescriptions.Item label="账单金额">
{formatAmount(data?.amount)}
</ProDescriptions.Item>
<ProDescriptions.Item label="优惠金额">
{formatAmount(data?.discount_amount)}
</ProDescriptions.Item>
<ProDescriptions.Item label="滞纳金">
{formatAmount(data?.late_fee)}
</ProDescriptions.Item>
<ProDescriptions.Item label="账单月份">
{data?.year}-{String(data?.month || 1).padStart(2, '0')}
</ProDescriptions.Item>
<ProDescriptions.Item label="计费周期">
{data?.start_date} {data?.end_date}
</ProDescriptions.Item>
<ProDescriptions.Item label="备注">
{data?.remark || '-'}
</ProDescriptions.Item>
</ProDescriptions>
);
return (
<>
<Space direction="vertical" style={{ width: '100%' }}>
<ProCard title="基本信息" size="small">
<ProDescriptions size="small" column={2}>
<ProDescriptions.Item label="申请事项">
{props?.item?.title || '-'}
</ProDescriptions.Item>
<ProDescriptions.Item label="关联原账单">
<BIllInfo
item={{
id: props?.item?.model?.model_id,
type: 'link',
}}
title="查看账单"
/>
</ProDescriptions.Item>
<ProDescriptions.Item label="申请人">
{props?.item?.applicant_name || '-'}
</ProDescriptions.Item>
<ProDescriptions.Item label="申请时间">
{props?.item?.created_at || '-'}
</ProDescriptions.Item>
</ProDescriptions>
</ProCard>
<ProCard title="修改内容对比" size="small" split="vertical">
<ProCard title="修改前" type="inner" bordered headerBordered>
{renderDataItems(originalData)}
</ProCard>
<ProCard title="修改后" type="inner" bordered headerBordered>
{renderDataItems(newData)}
</ProCard>
</ProCard>
</Space>
</>
);
}

View File

@ -0,0 +1,119 @@
import { MyBetaModalFormProps, renderTextHelper } from '@/common';
import {
HouseBillsTypeEnum,
HouseChargeStandardsApportionmentMethodEnum,
HouseChargeStandardsCalculationMethodEnum,
HouseChargeStandardsCalculationModeEnum,
HouseChargeStandardsCalculationPeriodEnum,
HouseChargeStandardsPriceAlgorithmEnum,
} from '@/gen/Enums';
import { ProCard, ProDescriptions } from '@ant-design/pro-components';
import { Space } from 'antd';
export default function ChargeStandardInfo(props: MyBetaModalFormProps) {
const model = props?.item?.model?.new_data;
return (
<>
<Space direction="vertical" style={{ width: '100%' }}>
<ProCard title="收费标准申请信息" size="small">
<ProDescriptions size="small" column={2}>
<ProDescriptions.Item label="申请事项" span={2}>
{props?.item?.title || '-'}
</ProDescriptions.Item>
<ProDescriptions.Item label="关联项目">
{props?.item?.asset_project?.name || '-'}
</ProDescriptions.Item>
<ProDescriptions.Item label="收费标准名称">
{model?.name || '-'}
</ProDescriptions.Item>
<ProDescriptions.Item label="收款帐号">
{model?.company_receipt_account?.company_bank}
{model?.company_receipt_account?.company_account || '-'}
</ProDescriptions.Item>
<ProDescriptions.Item label="收费类型">
<renderTextHelper.Tag
Enums={HouseBillsTypeEnum}
value={model?.charge_type}
/>
</ProDescriptions.Item>
<ProDescriptions.Item label="是否公摊">
{model?.is_apportionment ? '是' : '否'}
</ProDescriptions.Item>
{model?.apportionment_method && (
<ProDescriptions.Item label="分摊方式">
<renderTextHelper.Tag
Enums={HouseChargeStandardsApportionmentMethodEnum}
value={model?.apportionment_method}
/>
</ProDescriptions.Item>
)}
<ProDescriptions.Item label="计量单位">
<renderTextHelper.Tag
Enums={HouseChargeStandardsCalculationMethodEnum}
value={model?.calculation_method}
/>
</ProDescriptions.Item>
<ProDescriptions.Item label="计费模式">
<renderTextHelper.Tag
Enums={HouseChargeStandardsCalculationModeEnum}
value={model?.calculation_mode}
/>
</ProDescriptions.Item>
<ProDescriptions.Item label="计费算法">
<renderTextHelper.Tag
Enums={HouseChargeStandardsPriceAlgorithmEnum}
value={model?.price_algorithm}
/>
</ProDescriptions.Item>
<ProDescriptions.Item label="价格">
{model?.price_algorithm === 'Fixed'
? `${model?.price || '-'}`
: model?.tiered_rates?.map((rate: any, index: number) => (
<div key={index}>
{rate?.min_quantity} - {rate?.max_quantity}: {rate?.price}{' '}
</div>
)) || '-'}
</ProDescriptions.Item>
<ProDescriptions.Item label="计费周期">
<renderTextHelper.Tag
Enums={HouseChargeStandardsCalculationPeriodEnum}
value={model?.calculation_period}
/>
</ProDescriptions.Item>
<ProDescriptions.Item label="生成日期">
{model?.auto_date || '-'}
</ProDescriptions.Item>
<ProDescriptions.Item label="启用滞纳金">
{model?.has_late_fee ? '是' : '否'}
</ProDescriptions.Item>
{model?.has_late_fee && (
<>
<ProDescriptions.Item label="滞纳金起算天数">
{model?.late_fee_start_days || '-'}
</ProDescriptions.Item>
<ProDescriptions.Item label="滞纳金费率">
{model?.late_fee_rate || '-'} %/
</ProDescriptions.Item>
<ProDescriptions.Item label="滞纳金封顶天数">
{model?.late_fee_cap_days || '-'}
</ProDescriptions.Item>
</>
)}
<ProDescriptions.Item label="申请人">
{props?.item?.applicant_name || '-'}
</ProDescriptions.Item>
<ProDescriptions.Item label="申请时间">
{props?.item?.created_at || '-'}
</ProDescriptions.Item>
<ProDescriptions.Item label="备注" span={2}>
{model?.remark || '-'}
</ProDescriptions.Item>
</ProDescriptions>
</ProCard>
</Space>
</>
);
}

View File

@ -0,0 +1,129 @@
import { MyBetaModalFormProps, MyButtons, renderTextHelper } from '@/common';
import {
ContractsContractNatureEnum,
ContractsSettlementModeEnum,
ContractTemplatesIncomeExpenseTypeEnum,
} from '@/gen/Enums';
import { ProCard, ProDescriptions } from '@ant-design/pro-components';
import { useNavigate } from '@umijs/max';
import { Form, Space } from 'antd';
export default function Update(props: MyBetaModalFormProps) {
const [form] = Form.useForm();
const navigate = useNavigate();
return (
<>
<Space direction="vertical" style={{ width: '100%' }}>
<ProCard title="基本信息" size="small">
<ProDescriptions size="small" column={2}>
<ProDescriptions.Item label="合同名称" span={2}>
{props?.item?.model?.name || '-'}
</ProDescriptions.Item>
<ProDescriptions.Item label="合同编号" span={2}>
<Space>
{props?.item?.model?.code || '-'}
<MyButtons.View
title="查看详情"
key="configInfo"
onClick={() => {
navigate(
`/contract/contracts/show/${props?.item?.model?.id}`,
);
}}
/>
</Space>
</ProDescriptions.Item>
<ProDescriptions.Item label="收支类型">
<renderTextHelper.Tag
Enums={ContractTemplatesIncomeExpenseTypeEnum}
value={props?.item?.model?.income_expense_type}
/>
</ProDescriptions.Item>
<ProDescriptions.Item label="合同性质">
<renderTextHelper.Tag
Enums={ContractsContractNatureEnum}
value={props?.item?.model?.contract_nature}
/>
</ProDescriptions.Item>
<ProDescriptions.Item label="合同类型">
{props?.item?.model?.contract_type_name || '-'}
</ProDescriptions.Item>
<ProDescriptions.Item label="结算模式">
<renderTextHelper.Tag
Enums={ContractsSettlementModeEnum}
value={props?.item?.model?.settlement_mode}
/>
</ProDescriptions.Item>
<ProDescriptions.Item label="合同金额">
{props?.item?.model?.total_amount &&
props?.item?.model?.total_amount !== '-'
? parseFloat(props?.item?.model?.total_amount).toFixed(2)
: '-'}
</ProDescriptions.Item>
<ProDescriptions.Item label="保证金金额">
{props?.item?.model?.deposit_amount &&
props?.item?.model?.deposit_amount !== '-'
? parseFloat(props?.item?.model?.deposit_amount).toFixed(2)
: '-'}
</ProDescriptions.Item>
<ProDescriptions.Item label="签约主体">
{props?.item?.model?.sign_subject || '-'}
</ProDescriptions.Item>
<ProDescriptions.Item label="签约部门">
{props?.item?.model?.sign_department || '-'}
</ProDescriptions.Item>
<ProDescriptions.Item label="关联项目">
{props?.item?.model?.project_name || '-'}
</ProDescriptions.Item>
<ProDescriptions.Item label="合同对接人">
{props?.item?.model?.contract_liaison || '-'}
</ProDescriptions.Item>
<ProDescriptions.Item label="合同有效期" span={2}>
{props?.item?.model?.start_time?.substring(0, 10)}
{props?.item?.model?.end_time?.substring(0, 10)}
</ProDescriptions.Item>
<ProDescriptions.Item label="申请时间" span={2}>
{props?.item?.created_at || '-'}
</ProDescriptions.Item>
<ProDescriptions.Item label="附件" span={2}>
{props?.item?.model?.attachments.map((item: any) => {
const handleDownload = async (e: React.MouseEvent) => {
e.preventDefault();
try {
const response = await fetch(item.url);
const blob = await response.blob();
const url = window.URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = item.name;
document.body.appendChild(a);
a.click();
window.URL.revokeObjectURL(url);
document.body.removeChild(a);
} catch (error) {
console.error('下载失败:', error);
// 如果下载失败,则在新窗口打开
window.open(item.url, '_blank');
}
};
return (
<div key={item.url}>
<a href={item.url} onClick={handleDownload}>
{item.name}
</a>
</div>
);
})}
</ProDescriptions.Item>
</ProDescriptions>
</ProCard>
</Space>
</>
);
}

View File

@ -0,0 +1,162 @@
import { MyBetaModalFormProps, MyButtons, renderTextHelper } from '@/common';
import { ContractBillsIncomeExpenseTypeEnum } from '@/gen/Enums';
import { ProCard, ProDescriptions } from '@ant-design/pro-components';
import { useNavigate } from '@umijs/max';
import { Form, Space } from 'antd';
export default function Update(props: MyBetaModalFormProps) {
const [form] = Form.useForm();
const navigate = useNavigate();
return (
<>
<Space direction="vertical" style={{ width: '100%' }}>
<ProCard title="基本信息" size="small">
<ProDescriptions size="small" column={2}>
<ProDescriptions.Item label="标的名称" span={2}>
<Space>
{props?.item?.model?.name || '-'}
<MyButtons.View
title="查看合同"
key="configInfo"
onClick={() => {
navigate(
`/contract/contracts/show/${props?.item?.model?.contracts_id}`,
);
}}
/>
</Space>
</ProDescriptions.Item>
<ProDescriptions.Item label="支付金额">
{props?.item?.model?.amount && props?.item?.model?.amount !== '-'
? (parseFloat(props?.item?.model?.amount) / 100).toFixed(2)
: '-'}
</ProDescriptions.Item>
<ProDescriptions.Item label="账单变更">
<renderTextHelper.Tag
Enums={ContractBillsIncomeExpenseTypeEnum}
value={props?.item?.model?.cost_type}
/>
</ProDescriptions.Item>
<ProDescriptions.Item label="变更金额">
{props?.item?.model?.cost_amount &&
props?.item?.model?.cost_amount !== '-'
? (parseFloat(props?.item?.model?.cost_amount) / 100).toFixed(2)
: '-'}
</ProDescriptions.Item>
<ProDescriptions.Item label="变更后金额">
{(() => {
const amount = props?.item?.model?.amount
? parseFloat(props?.item?.model?.amount) / 100
: 0;
const costAmount = props?.item?.model?.cost_amount
? parseFloat(props?.item?.model?.cost_amount) / 100
: 0;
const costType = props?.item?.model?.cost_type;
let finalAmount = amount;
if (costType === 'Increase') {
finalAmount = amount + costAmount;
} else if (costType === 'Decrease') {
finalAmount = amount - costAmount;
}
return finalAmount >= 0 ? finalAmount.toFixed(2) : '0.00';
})()}
</ProDescriptions.Item>
<ProDescriptions.Item label="申请人" span={2}>
{props?.item?.applicant_name || '-'}
</ProDescriptions.Item>
<ProDescriptions.Item label="申请时间" span={2}>
{props?.item?.created_at || '-'}
</ProDescriptions.Item>
<ProDescriptions.Item label="标的说明" span={2}>
{props?.item?.model?.completed_description || '-'}
</ProDescriptions.Item>
<ProDescriptions.Item label="标的附件" span={2}>
{props?.item?.model?.completed_attachments &&
props?.item?.model?.completed_attachments.length > 0 && (
<div style={{ marginBottom: 8 }}>
<button
onClick={() => {
props?.item?.model?.completed_attachments.forEach(
(file: any, index: number) => {
setTimeout(async () => {
const fileName = file?.name || `文件${index + 1}`;
try {
const response = await fetch(file?.url);
const blob = await response.blob();
const url = window.URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = fileName;
document.body.appendChild(a);
a.click();
window.URL.revokeObjectURL(url);
document.body.removeChild(a);
} catch (error) {
console.error('下载失败:', error);
window.open(file?.url, '_blank');
}
}, index * 100); // 延迟100ms避免浏览器阻塞
},
);
}}
style={{
padding: '4px 12px',
marginBottom: '8px',
backgroundColor: '#1890ff',
color: 'white',
border: 'none',
borderRadius: '4px',
cursor: 'pointer',
}}
>
</button>
</div>
)}
{props?.item?.model?.completed_attachments?.map(
(file: any, index: number) => {
const fileName = file?.name || `文件${index + 1}`;
const handleDownload = async (e: React.MouseEvent) => {
e.preventDefault();
try {
const response = await fetch(file?.url);
const blob = await response.blob();
const url = window.URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = fileName;
document.body.appendChild(a);
a.click();
window.URL.revokeObjectURL(url);
document.body.removeChild(a);
} catch (error) {
console.error('下载失败:', error);
// 如果下载失败,则在新窗口打开
window.open(file?.url, '_blank');
}
};
return (
<div key={index}>
<a href={file?.url} onClick={handleDownload}>
{fileName}
</a>
</div>
);
},
) || '-'}
</ProDescriptions.Item>
</ProDescriptions>
</ProCard>
</Space>
</>
);
}

View File

@ -0,0 +1,94 @@
import { MyBetaModalFormProps, MyButtons, renderTextHelper } from '@/common';
import {
ContractsContractNatureEnum,
ContractsSettlementModeEnum,
ContractTemplatesIncomeExpenseTypeEnum,
} from '@/gen/Enums';
import { ProCard, ProDescriptions } from '@ant-design/pro-components';
import { useNavigate } from '@umijs/max';
import { Form, Space } from 'antd';
export default function Update(props: MyBetaModalFormProps) {
const [form] = Form.useForm();
const navigate = useNavigate();
return (
<>
<Space direction="vertical" style={{ width: '100%' }}>
<ProCard title="基本信息" size="small">
<ProDescriptions size="small" column={2}>
<ProDescriptions.Item label="合同名称" span={2}>
{props?.item?.model?.name || '-'}
</ProDescriptions.Item>
<ProDescriptions.Item label="合同编号" span={2}>
<Space>
{props?.item?.model?.code || '-'}
<MyButtons.View
title="查看详情"
key="configInfo"
onClick={() => {
navigate(
`/contract/contracts/show/${props?.item?.model?.id}`,
);
}}
/>
</Space>
</ProDescriptions.Item>
<ProDescriptions.Item label="收支类型">
<renderTextHelper.Tag
Enums={ContractTemplatesIncomeExpenseTypeEnum}
value={props?.item?.model?.income_expense_type}
/>
</ProDescriptions.Item>
<ProDescriptions.Item label="合同性质">
<renderTextHelper.Tag
Enums={ContractsContractNatureEnum}
value={props?.item?.model?.contract_nature}
/>
</ProDescriptions.Item>
<ProDescriptions.Item label="合同类型">
{props?.item?.model?.contract_type_name || '-'}
</ProDescriptions.Item>
<ProDescriptions.Item label="结算模式">
<renderTextHelper.Tag
Enums={ContractsSettlementModeEnum}
value={props?.item?.model?.settlement_mode}
/>
</ProDescriptions.Item>
<ProDescriptions.Item label="合同金额">
{props?.item?.model?.total_amount || '-'}
</ProDescriptions.Item>
<ProDescriptions.Item label="保证金金额">
{props?.item?.model?.deposit_amount || '无'}
</ProDescriptions.Item>
<ProDescriptions.Item label="签约主体">
{props?.item?.model?.sign_subject || '-'}
</ProDescriptions.Item>
<ProDescriptions.Item label="关联项目">
{props?.item?.model?.project_name || '-'}
</ProDescriptions.Item>
<ProDescriptions.Item label="合同有效期" span={2}>
{props?.item?.model?.start_time?.substring(0, 10)}
{props?.item?.model?.end_time?.substring(0, 10)}
</ProDescriptions.Item>
<ProDescriptions.Item label="申请人" span={2}>
{props?.item?.model?.applicant?.name || '-'}
{props?.item?.model?.applicant?.phone || '-'}
</ProDescriptions.Item>
<ProDescriptions.Item label="申请时间" span={2}>
{props?.item?.created_at || '-'}
</ProDescriptions.Item>
<ProDescriptions.Item label="申请说明" span={2}>
{props?.item?.model?.remark || '-'}
</ProDescriptions.Item>
</ProDescriptions>
</ProCard>
</Space>
</>
);
}

View File

@ -0,0 +1,64 @@
import { MyBetaModalFormProps, renderTextHelper } from '@/common';
import { RefundsTypeEnum } from '@/gen/Enums';
import BIllInfo from '@/pages/bills/house_bills/modals/BIllInfo';
import { ProCard, ProDescriptions } from '@ant-design/pro-components';
import { useNavigate } from '@umijs/max';
import { Form, Space } from 'antd';
export default function Update(props: MyBetaModalFormProps) {
const [form] = Form.useForm();
const navigate = useNavigate();
return (
<>
<Space direction="vertical" style={{ width: '100%' }}>
<ProCard title="基本信息" size="small">
<ProDescriptions size="small" column={2}>
<ProDescriptions.Item label="申请事项" span={2}>
{props?.item?.title || '-'}
</ProDescriptions.Item>
<ProDescriptions.Item label="账单类型">
<renderTextHelper.Tag
Enums={RefundsTypeEnum}
value={props?.item?.model?.type}
/>
</ProDescriptions.Item>
<ProDescriptions.Item label="退款金额">
<Space>
{props?.item?.model?.refund_amount &&
props?.item?.model?.refund_amount !== '-'
? (
parseFloat(props?.item?.model?.refund_amount) / 100
).toFixed(2)
: '-'}
<BIllInfo
item={{
id: props?.item?.model?.refundable_id,
type: 'link',
}}
title="查看账单"
/>
</Space>
</ProDescriptions.Item>
<ProDescriptions.Item label="付款信息" span={2}>
{props?.item?.model?.payer_name || '-'}
{props?.item?.model?.payer_bank || '-'}
{props?.item?.model?.payer_account || '-'}
</ProDescriptions.Item>
<ProDescriptions.Item label="收款信息" span={2}>
{props?.item?.model?.payee_name || '-'}
{props?.item?.model?.payee_bank || '-'}
{props?.item?.model?.payee_account || '-'}
</ProDescriptions.Item>
<ProDescriptions.Item label="申请时间">
{props?.item?.created_at || '-'}
</ProDescriptions.Item>
</ProDescriptions>
</ProCard>
</Space>
</>
);
}

View File

@ -0,0 +1,149 @@
import { MyBetaModalFormProps, renderTextHelper } from '@/common';
import { CompanySealsTypeEnum } from '@/gen/Enums';
// import SealInfo from '@/pages/contract/contracts_other/modals/Show';
import { ProCard, ProDescriptions } from '@ant-design/pro-components';
import { useNavigate } from '@umijs/max';
import { Form, Space } from 'antd';
export default function Update(props: MyBetaModalFormProps) {
const [form] = Form.useForm();
const navigate = useNavigate();
return (
<>
<Space direction="vertical" style={{ width: '100%' }}>
<ProCard title="基本信息" size="small">
<ProDescriptions size="small" column={2}>
<ProDescriptions.Item label="申请事项" span={2}>
<Space>
{props?.item?.title || '-'}
{/* <SealInfo
item={{
id: props?.item?.model?.id,
type: 'link',
}}
title="查看详情"
/> */}
</Space>
</ProDescriptions.Item>
{/* 基本信息 */}
<ProDescriptions.Item label="关联项目">
{props?.item?.asset_project?.name || '-'}
</ProDescriptions.Item>
<ProDescriptions.Item label="申请人">
{props?.item?.model?.company_employee_name || '-'}
</ProDescriptions.Item>
{/* 用印详情 */}
<ProDescriptions.Item label="申请份数">
{props?.item?.model?.copies ||
props?.item?.model?.number_contract_copies ||
'-'}
</ProDescriptions.Item>
<ProDescriptions.Item label="申请时间">
{props?.item?.created_at || '-'}
</ProDescriptions.Item>
<ProDescriptions.Item label="简要说明" span={2}>
{props?.item?.model?.reason || '-'}
</ProDescriptions.Item>
{/* 印章信息 */}
<ProDescriptions.Item label="申请印章" span={2}>
{(props?.item?.model?.seals || props?.item?.model?.seals)?.map(
(sealId: string, index: number) => (
<Space key={index} style={{ marginRight: 16 }}>
{props?.item?.model?.seals?.[index]?.company_supplier
?.name || '-'}
<renderTextHelper.Tag
Enums={CompanySealsTypeEnum}
value={props?.item?.model?.seals?.[index]?.type || '-'}
key={`seal_id_${index}`}
/>
</Space>
),
) || '-'}
</ProDescriptions.Item>
{/* 申请信息 */}
<ProDescriptions.Item label="附件" span={3}>
{props?.item?.model?.files &&
props?.item?.model?.files.length > 0 && (
<div style={{ marginBottom: 8 }}>
<button
onClick={() => {
props?.item?.model?.files.forEach(
(file: any, index: number) => {
setTimeout(async () => {
const fileName = file?.name || `文件${index + 1}`;
try {
const response = await fetch(file?.url);
const blob = await response.blob();
const url = window.URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = fileName;
document.body.appendChild(a);
a.click();
window.URL.revokeObjectURL(url);
document.body.removeChild(a);
} catch (error) {
console.error('下载失败:', error);
window.open(file?.url, '_blank');
}
}, index * 100); // 延迟100ms避免浏览器阻塞
},
);
}}
style={{
padding: '4px 12px',
marginBottom: '8px',
backgroundColor: '#1890ff',
color: 'white',
border: 'none',
borderRadius: '4px',
cursor: 'pointer',
}}
>
</button>
</div>
)}
{props?.item?.model?.files?.map((file: any, index: number) => {
const fileName = file?.name || `文件${index + 1}`;
const handleDownload = async (e: React.MouseEvent) => {
e.preventDefault();
try {
const response = await fetch(file?.url);
const blob = await response.blob();
const url = window.URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = fileName;
document.body.appendChild(a);
a.click();
window.URL.revokeObjectURL(url);
document.body.removeChild(a);
} catch (error) {
console.error('下载失败:', error);
// 如果下载失败,则在新窗口打开
window.open(file?.url, '_blank');
}
};
return (
<div key={index}>
<a href={file?.url} onClick={handleDownload}>
{fileName}
</a>
</div>
);
}) || '-'}
</ProDescriptions.Item>
</ProDescriptions>
</ProCard>
</Space>
</>
);
}

View File

@ -0,0 +1,69 @@
import { MyBetaModalFormProps } from '@/common';
import BIllInfo from '@/pages/bills/temporary/modals/BIllInfo';
import { ProCard, ProDescriptions } from '@ant-design/pro-components';
import { Space } from 'antd';
export default function BillUpdateInfo(props: MyBetaModalFormProps) {
const originalData = props?.item?.model?.original_data;
const newData = props?.item?.model?.new_data;
// 格式化金额
const formatAmount = (amount: any) => {
if (!amount || amount === '-') return '-';
return parseFloat(amount).toFixed(2) + ' 元';
};
// 渲染数据项
const renderDataItems = (data: any) => (
<ProDescriptions size="small" column={1}>
<ProDescriptions.Item label="账单金额">
{formatAmount(data?.amount)}
</ProDescriptions.Item>
<ProDescriptions.Item label="账单月份">
{data?.year}-{String(data?.month).padStart(2, '0')}
</ProDescriptions.Item>
<ProDescriptions.Item label="备注">
{data?.remark || '-'}
</ProDescriptions.Item>
</ProDescriptions>
);
return (
<>
<Space direction="vertical" style={{ width: '100%' }}>
<ProCard title="基本信息" size="small">
<ProDescriptions size="small" column={2}>
<ProDescriptions.Item label="申请事项">
{props?.item?.title || '-'}
</ProDescriptions.Item>
<ProDescriptions.Item label="关联原账单">
<BIllInfo
item={{
id: props?.item?.model?.model_id,
type: 'link',
}}
title="查看账单"
/>
</ProDescriptions.Item>
<ProDescriptions.Item label="申请人">
{props?.item?.applicant_name || '-'}
</ProDescriptions.Item>
<ProDescriptions.Item label="申请时间">
{props?.item?.created_at || '-'}
</ProDescriptions.Item>
</ProDescriptions>
</ProCard>
<ProCard title="修改内容对比" size="small" split="vertical">
<ProCard title="修改前" type="inner" bordered headerBordered>
{renderDataItems(originalData)}
</ProCard>
<ProCard title="修改后" type="inner" bordered headerBordered>
{renderDataItems(newData)}
</ProCard>
</ProCard>
</Space>
</>
);
}

View File

@ -11,7 +11,9 @@ import {
} from '@/gen/Enums';
import { ProTable } from '@ant-design/pro-components';
import { Space } from 'antd';
export default function Index({ title = '我的发起' }) {
import AuditShow from '../common_info/AuditShow';
export default function Index({ title = '我发起的' }) {
return (
<MyPageContainer
title={title}
@ -61,21 +63,11 @@ export default function Index({ title = '我的发起' }) {
item?.approval_records?.[0]?.company_employee?.phone || ''
}`,
},
// {
// title: '项目ID',
// dataIndex: 'asset_projects_id',
// hidden: true,
// },
// {
// title: '申请人',
// dataIndex: 'applicant_name',
// search: false,
// },
MyColumns.CreatedAt(),
MyColumns.Option({
render: (_, item: any, index, action) => (
<Space key={index}>
{/* <Update item={item} reload={action?.reload} title={title} /> */}
<AuditShow item={item} reload={action?.reload} title={title} />
<MyButtons.Default
title="撤销"
isConfirm

View File

@ -1,4 +1,5 @@
import { MyColumns, MyPageContainer, MyProTableProps } from '@/common';
import { Selects } from '@/components/Select';
import { Apis } from '@/gen/Apis';
import {
ApprovalInstancesStatusEnum,
@ -6,8 +7,8 @@ import {
} from '@/gen/Enums';
import { ProTable } from '@ant-design/pro-components';
import { Space } from 'antd';
import Show from './modals/Show';
export default function Index({ title = '我的已办' }) {
import AuditShow from '../common_info/AuditShow';
export default function Index({ title = '我的发起' }) {
return (
<MyPageContainer
title={title}
@ -27,6 +28,11 @@ export default function Index({ title = '我的已办' }) {
headerTitle={title}
columns={[
MyColumns.ID({ search: false }),
Selects?.AssetProjects({
title: '选择项目',
key: 'asset_projects_id',
hidden: true,
}),
{
title: '申请事项',
dataIndex: 'title',
@ -41,18 +47,19 @@ export default function Index({ title = '我的已办' }) {
title: '状态',
dataIndex: 'status',
valueEnum: ApprovalInstancesStatusEnum,
search: false,
}),
MyColumns.EnumTag({
dataIndex: 'type',
title: '业务类型',
valueEnum: ApprovalTemplatesTypeEnum,
search: false,
}),
MyColumns.CreatedAt(),
// MyColumns.CreatedAt(),
MyColumns.UpdatedAt(),
MyColumns.Option({
render: (_, item: any, index, action) => (
<Space key={index}>
<Show item={item} reload={action?.reload} title={title} />
<AuditShow item={item} reload={action?.reload} title={title} />
</Space>
),
}),

View File

@ -1,250 +0,0 @@
import { MyBetaModalFormProps, MyButtons, MyModalFormProps } from '@/common';
import { Apis } from '@/gen/Apis';
// import {
// ContractsContractNatureEnum,
// ContractsSettlementModeEnum,
// ContractTemplatesIncomeExpenseTypeEnum,
// RefundsTypeEnum,
// } from '@/gen/Enums';
import BIllInfo from '@/pages/bills/house_bills/modals/BIllInfo';
import {
BetaSchemaForm,
ProCard,
ProDescriptions,
} from '@ant-design/pro-components';
import { useNavigate } from '@umijs/max';
import { Form, Space, Steps } from 'antd';
export default function Update(props: MyBetaModalFormProps) {
const [form] = Form.useForm();
const navigate = useNavigate();
return (
<BetaSchemaForm<ApiTypes.Archive.HouseRegisters.Update>
{...MyModalFormProps.props}
title={props.title}
trigger={<MyButtons.Default title="查看" type="primary" />}
wrapperCol={{ span: 24 }}
width="600px"
key={new Date().getTime()}
form={form}
onOpenChange={() => {
if (props?.item?.id) {
Apis.Approval.ApprovalInstances.Show({
id: props.item?.model_id,
}).then((res) => {
form.setFieldsValue({
info_display: res?.data,
});
});
}
}}
columns={[
{
// title: '登记信息',
dataIndex: 'info_display',
valueType: 'text',
renderFormItem: (_, config) => (
<Space direction="vertical" style={{ width: '100%' }}>
<div>
{/* 退款详情 */}
{config?.value?.type === 'Refund' && (
<ProCard size="small">
<ProDescriptions size="small" column={2}>
<ProDescriptions.Item label="申请事项" span={2}>
{config?.value?.title || '-'}
</ProDescriptions.Item>
{/* <ProDescriptions.Item label="">
<renderTextHelper.Tag
Enums={RefundsTypeEnum}
value={config?.value?.model?.type}
/>
</ProDescriptions.Item> */}
<ProDescriptions.Item label="退款金额">
<Space>
{config?.value?.model?.refund_amount || '-'}
<BIllInfo
item={{
id: config?.value?.model?.refundable_id,
type: 'link',
}}
title="查看账单"
/>
</Space>
</ProDescriptions.Item>
<ProDescriptions.Item label="付款信息" span={2}>
{config?.value?.model?.payer_name || '-'}
{config?.value?.model?.payer_bank || '-'}
{config?.value?.model?.payer_account || '-'}
</ProDescriptions.Item>
<ProDescriptions.Item label="收款信息" span={2}>
{config?.value?.model?.payee_name || '-'}
{config?.value?.model?.payee_bank || '-'}
{config?.value?.model?.payee_account || '-'}
</ProDescriptions.Item>
<ProDescriptions.Item label="申请人" span={2}>
{props?.item?.applicant?.name || '-'}:
{props?.item?.applicant?.phone || '-'}
</ProDescriptions.Item>
<ProDescriptions.Item label="申请时间" span={2}>
{props?.item?.created_at || '-'}
</ProDescriptions.Item>
</ProDescriptions>
</ProCard>
)}
{/* 合同详情 */}
{config?.value?.type === 'Contract' && (
<ProCard size="small">
<ProDescriptions size="small" column={2}>
<ProDescriptions.Item label="合同名称" span={2}>
{config?.value?.model?.name || '-'}
</ProDescriptions.Item>
<ProDescriptions.Item label="合同编号" span={2}>
<Space>
{config?.value?.model?.code || '-'}
<BIllInfo
item={{
id: config?.value?.model?.refundable_id,
type: 'Contract',
}}
title="查看合同"
/>
<MyButtons.View
title="查看"
key="configInfo"
onClick={() => {
navigate(
`/contract/contracts/show/${config?.value?.model?.id}`,
);
}}
/>
</Space>
</ProDescriptions.Item>
{/* <ProDescriptions.Item label="">
<renderTextHelper.Tag
Enums={ContractTemplatesIncomeExpenseTypeEnum}
value={config?.value?.model?.income_expense_type}
/>
</ProDescriptions.Item> */}
{/* <ProDescriptions.Item label="">
<renderTextHelper.Tag
Enums={ContractsContractNatureEnum}
value={config?.value?.model?.contract_nature}
/>
</ProDescriptions.Item> */}
<ProDescriptions.Item label="合同类型">
{config?.value?.model?.contract_type_name || '-'}
</ProDescriptions.Item>
{/* <ProDescriptions.Item label="">
<renderTextHelper.Tag
Enums={ContractsSettlementModeEnum}
value={config?.value?.model?.settlement_mode}
/>
</ProDescriptions.Item> */}
<ProDescriptions.Item label="合同金额">
{config?.value?.model?.total_amount || '-'}
</ProDescriptions.Item>
<ProDescriptions.Item label="保证金金额">
{config?.value?.model?.deposit_amount || '无'}
</ProDescriptions.Item>
<ProDescriptions.Item label="签约主体">
{config?.value?.model?.sign_subject || '-'}
</ProDescriptions.Item>
<ProDescriptions.Item label="签约部门">
{config?.value?.model?.sign_department || '-'}
</ProDescriptions.Item>
<ProDescriptions.Item label="关联项目">
{config?.value?.model?.project_name || '-'}
</ProDescriptions.Item>
<ProDescriptions.Item label="合同对接人">
{config?.value?.model?.contract_liaison || '-'}
</ProDescriptions.Item>
<ProDescriptions.Item label="合同有效期" span={2}>
{config?.value?.model?.start_time?.substring(0, 10)}
{config?.value?.model?.end_time?.substring(0, 10)}
</ProDescriptions.Item>
<ProDescriptions.Item label="申请人" span={2}>
{props?.item?.applicant?.name || '-'}:
{props?.item?.applicant?.phone || '-'}
</ProDescriptions.Item>
<ProDescriptions.Item label="申请时间" span={2}>
{props?.item?.created_at || '-'}
</ProDescriptions.Item>
<ProDescriptions.Item label="附件" span={2}>
{config?.value?.model?.attachments.map((item: any) => {
const handleDownload = async (
e: React.MouseEvent,
) => {
e.preventDefault();
try {
const response = await fetch(item.url);
const blob = await response.blob();
const url = window.URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = item.name;
document.body.appendChild(a);
a.click();
window.URL.revokeObjectURL(url);
document.body.removeChild(a);
} catch (error) {
console.error('下载失败:', error);
// 如果下载失败,则在新窗口打开
window.open(item.url, '_blank');
}
};
return (
<div key={item.url}>
<a href={item.url} onClick={handleDownload}>
{item.name}
</a>
</div>
);
})}
</ProDescriptions.Item>
</ProDescriptions>
</ProCard>
)}
<ProCard>
<ProDescriptions>
<ProDescriptions.Item label="审核记录">
<Space direction="vertical" style={{ width: '100%' }}>
<Steps
progressDot
direction="vertical"
current={config?.value?.approval_records?.length}
items={config?.value?.approval_records?.map(
(item: any) =>
item?.node_type === 'Approver'
? {
title: `${
item.company_employee?.name || '-'
}-${item?.company_employee?.phone}`,
description: `${
item.status === 'Approved'
? `通过 - ${item.opinion || '-'} - ${
item.created_at || '-'
}`
: '待审核'
}`,
}
: '',
)}
/>
</Space>
</ProDescriptions.Item>
</ProDescriptions>
</ProCard>
</div>
</Space>
),
colProps: { span: 24 },
},
]}
/>
);
}

View File

@ -1,9 +1,4 @@
import {
MyButtons,
MyColumns,
MyPageContainer,
MyProTableProps,
} from '@/common';
import { MyColumns, MyPageContainer, MyProTableProps } from '@/common';
import { Apis } from '@/gen/Apis';
import {
ApprovalInstancesStatusEnum,
@ -11,6 +6,7 @@ import {
} from '@/gen/Enums';
import { ProTable } from '@ant-design/pro-components';
import { Space } from 'antd';
import Audit from './modals/Audit';
import Forwarded from './modals/Forwarded';
export default function Index({ title = '我的待办' }) {
@ -62,20 +58,7 @@ export default function Index({ title = '我的待办' }) {
render: (_, item: any, index, action) => (
<Space key={index}>
<Audit item={item} reload={action?.reload} title={title} />
<Forwarded item={item} reload={action?.reload} title="转办" />
<MyButtons.Default
title="撤销"
isConfirm
description="确认撤销该申请吗?"
disabled={item.status !== 'Pending'}
color="danger"
variant="solid"
onConfirm={() =>
Apis.Approval.ApprovalInstances.Cancel({
id: item.id,
}).then(() => action?.reload())
}
/>
<Forwarded item={item} reload={action?.reload} title="转交" />
</Space>
),
}),

View File

@ -3,20 +3,24 @@ import {
MyButtons,
MyFormItems,
MyModalFormProps,
renderTextHelper,
rulesHelper,
} from '@/common';
import { Apis } from '@/gen/Apis';
import { HouseRegistersStatusEnum, RefundsTypeEnum } from '@/gen/Enums';
import BIllInfo from '@/pages/bills/house_bills/modals/BIllInfo';
import {
BetaSchemaForm,
ProCard,
ProDescriptions,
} from '@ant-design/pro-components';
import { HouseRegistersStatusEnum } from '@/gen/Enums';
import { BetaSchemaForm } from '@ant-design/pro-components';
import { useNavigate } from '@umijs/max';
import { Form, message, Space, Steps } from 'antd';
import { Form, message, Space } from 'antd';
import ApprovalRecord from '../../common_info/ApprovalRecord';
import ArchiveBorrowInfo from '../../common_info/ArchiveBorrowInfo';
import BillUpdateInfo from '../../common_info/BillUpdateInfo';
import ChargeStandardInfo from '../../common_info/ChargeStandardInfo';
import ContractInfo from '../../common_info/ContractInfo';
import ContractPaymentInfo from '../../common_info/ContractPaymentInfo';
import ContractTerminationInfo from '../../common_info/ContractTerminationInfo';
import RefundInfo from '../../common_info/RefundInfo';
import SealInfo from '../../common_info/SealInfo';
import TemporaryBillUpdateInfo from '../../common_info/TemporaryBillUpdateInfo';
export default function Update(props: MyBetaModalFormProps) {
const [form] = Form.useForm();
@ -68,7 +72,7 @@ export default function Update(props: MyBetaModalFormProps) {
})
.then(() => {
props.reload?.();
message.success(props.title + '成功');
message.success('审核成功');
return true;
})
.catch(() => false)
@ -80,192 +84,34 @@ export default function Update(props: MyBetaModalFormProps) {
valueType: 'text',
renderFormItem: (_, config) => (
<Space direction="vertical" style={{ width: '100%' }}>
<div>
{/* 退款详情 */}
{config?.value?.type === 'Refund' && (
<ProCard size="small">
<ProDescriptions size="small" column={2}>
<ProDescriptions.Item label="申请事项" span={2}>
{config?.value?.title || '-'}
</ProDescriptions.Item>
<ProDescriptions.Item label="账单类型">
<renderTextHelper.Tag
Enums={RefundsTypeEnum}
value={config?.value?.model?.type}
/>
</ProDescriptions.Item>
<ProDescriptions.Item label="退款金额">
<Space>
{config?.value?.model?.refund_amount || '-'}
<BIllInfo
item={{
id: config?.value?.model?.refundable_id,
type: 'link',
}}
title="查看账单"
/>
</Space>
</ProDescriptions.Item>
<ProDescriptions.Item label="付款信息" span={2}>
{config?.value?.model?.payer_name || '-'}
{config?.value?.model?.payer_bank || '-'}
{config?.value?.model?.payer_account || '-'}
</ProDescriptions.Item>
<ProDescriptions.Item label="收款信息" span={2}>
{config?.value?.model?.payee_name || '-'}
{config?.value?.model?.payee_bank || '-'}
{config?.value?.model?.payee_account || '-'}
</ProDescriptions.Item>
<ProDescriptions.Item label="申请时间">
{props?.item?.created_at || '-'}
</ProDescriptions.Item>
</ProDescriptions>
</ProCard>
)}
{/* 合同详情 */}
{config?.value?.type === 'Contract' && (
<ProCard size="small">
<ProDescriptions size="small" column={2}>
<ProDescriptions.Item label="合同名称" span={2}>
{config?.value?.model?.name || '-'}
</ProDescriptions.Item>
<ProDescriptions.Item label="合同编号" span={2}>
<Space>
{config?.value?.model?.code || '-'}
<BIllInfo
item={{
id: config?.value?.model?.refundable_id,
type: 'Contract',
}}
title="查看合同"
/>
<MyButtons.View
title="查看"
key="configInfo"
onClick={() => {
navigate(
`/contract/contracts/show/${config?.value?.model?.id}`,
);
}}
/>
</Space>
</ProDescriptions.Item>
{/* <ProDescriptions.Item label="">
<renderTextHelper.Tag
Enums={ContractTemplatesIncomeExpenseTypeEnum}
value={config?.value?.model?.income_expense_type}
/>
</ProDescriptions.Item> */}
{/* <ProDescriptions.Item label="">
<renderTextHelper.Tag
Enums={ContractsContractNatureEnum}
value={config?.value?.model?.contract_nature}
/>
</ProDescriptions.Item> */}
<ProDescriptions.Item label="合同类型">
{config?.value?.model?.contract_type_name || '-'}
</ProDescriptions.Item>
{/* <ProDescriptions.Item label="">
<renderTextHelper.Tag
Enums={ContractsSettlementModeEnum}
value={config?.value?.model?.settlement_mode}
/>
</ProDescriptions.Item> */}
<ProDescriptions.Item label="合同金额">
{config?.value?.model?.total_amount || '-'}
</ProDescriptions.Item>
<ProDescriptions.Item label="保证金金额">
{config?.value?.model?.deposit_amount || '无'}
</ProDescriptions.Item>
<ProDescriptions.Item label="签约主体">
{config?.value?.model?.sign_subject || '-'}
</ProDescriptions.Item>
<ProDescriptions.Item label="签约部门">
{config?.value?.model?.sign_department || '-'}
</ProDescriptions.Item>
<ProDescriptions.Item label="关联项目">
{config?.value?.model?.project_name || '-'}
</ProDescriptions.Item>
<ProDescriptions.Item label="合同对接人">
{config?.value?.model?.contract_liaison || '-'}
</ProDescriptions.Item>
<ProDescriptions.Item label="合同有效期" span={2}>
{config?.value?.model?.start_time?.substring(0, 10)}
{config?.value?.model?.end_time?.substring(0, 10)}
</ProDescriptions.Item>
<ProDescriptions.Item label="申请时间" span={2}>
{props?.item?.created_at || '-'}
</ProDescriptions.Item>
<ProDescriptions.Item label="附件" span={2}>
{config?.value?.model?.attachments.map((item: any) => {
const handleDownload = async (
e: React.MouseEvent,
) => {
e.preventDefault();
try {
const response = await fetch(item.url);
const blob = await response.blob();
const url = window.URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = item.name;
document.body.appendChild(a);
a.click();
window.URL.revokeObjectURL(url);
document.body.removeChild(a);
} catch (error) {
console.error('下载失败:', error);
// 如果下载失败,则在新窗口打开
window.open(item.url, '_blank');
}
};
return (
<div key={item.url}>
<a href={item.url} onClick={handleDownload}>
{item.name}
</a>
</div>
);
})}
</ProDescriptions.Item>
</ProDescriptions>
</ProCard>
)}
<ProCard>
<ProDescriptions>
<ProDescriptions.Item label="审核记录">
<Space direction="vertical" style={{ width: '100%' }}>
<Steps
progressDot
direction="vertical"
current={config?.value?.approval_records.length}
items={config?.value?.approval_records.map(
(item: any) =>
item?.node_type === 'Approver'
? {
title: `${
item.company_employee?.name || '-'
}-${item?.company_employee?.phone}`,
description: `${
item.status === 'Approved'
? `通过 - ${item.opinion || '-'} - ${
item.created_at || '-'
}`
: '待审核'
}`,
}
: '',
)}
/>
</Space>
</ProDescriptions.Item>
</ProDescriptions>
</ProCard>
</div>
{config?.value?.type === 'Contract' && (
<ContractInfo item={config?.value} />
)}
{config?.value?.type === 'ContractTermination' && (
<ContractTerminationInfo item={config?.value} />
)}
{config?.value?.type === 'ContractPayment' && (
<ContractPaymentInfo item={config?.value} />
)}
{config?.value?.type === 'Refund' && (
<RefundInfo item={config?.value} />
)}
{config?.value?.type === 'ContractBorrow' && (
<ArchiveBorrowInfo item={config?.value} />
)}
{config?.value?.type === 'OtherContractSeal' && (
<SealInfo item={config?.value} />
)}
{config?.value?.type === 'HouseBillUpdate' && (
<BillUpdateInfo item={config?.value} />
)}
{config?.value?.type === 'ChargeStandardModification' && (
<ChargeStandardInfo item={config?.value} />
)}
{config?.value?.type === 'BillUpdate' && (
<TemporaryBillUpdateInfo item={config?.value} />
)}
<ApprovalRecord item={config?.value} />
</Space>
),
colProps: { span: 24 },

View File

@ -11,6 +11,7 @@ import { ApprovalTemplatesTypeEnum } from '@/gen/Enums';
import { ProTable } from '@ant-design/pro-components';
import BatchModifyApprover from './modals/BatchModifyApprover';
import Create from './modals/Create';
import Show from './modals/Show';
import Update from './modals/Update';
export default function Index({ title = '审批模板' }) {
@ -76,6 +77,9 @@ export default function Index({ title = '审批模板' }) {
render: (_, item: any, _index, action) => (
<MyTableActions
actions={{
show: (
<Show item={item} reload={action?.reload} title={title} />
),
update: (
<Update item={item} reload={action?.reload} title={title} />
),

View File

@ -12,7 +12,31 @@ import {
ApprovalTemplatesTypeEnum,
} from '@/gen/Enums';
import { BetaSchemaForm } from '@ant-design/pro-components';
import { Form, message } from 'antd';
import { Button, Form, message } from 'antd';
// 默认的4个节点配置
const getDefaultNodes = () => [
{
node_type: ApprovalTemplateNodesNodeTypeEnum.Approver.value,
name: '',
members: [],
},
{
node_type: ApprovalTemplateNodesNodeTypeEnum.Approver.value,
name: '',
members: [],
},
{
node_type: ApprovalTemplateNodesNodeTypeEnum.Approver.value,
name: '',
members: [],
},
{
node_type: ApprovalTemplateNodesNodeTypeEnum.CC.value,
name: '抄送对象',
members: [],
},
];
export default function Create(props: MyBetaModalFormProps) {
const [form] = Form.useForm();
@ -27,12 +51,44 @@ export default function Create(props: MyBetaModalFormProps) {
onOpenChange={(open: any) => {
if (open) {
form.resetFields(); // 清空表单数据
// 设置默认的4个节点
form.setFieldsValue({
nodes: getDefaultNodes(),
});
}
}}
trigger={<MyButtons.Create title={`${props.title}`} />}
onFinish={async (values) =>
Apis.Approval.ApprovalTemplates.Store({
onFinish={async (values) => {
const { nodes } = values;
// 校验:只能提交一行 node_type 为 "cc" 的数据
const ccNodes = nodes?.filter(
(node: any) =>
node?.node_type === ApprovalTemplateNodesNodeTypeEnum.CC.value,
);
if (ccNodes?.length > 1) {
message.error('只能提交一行抄送节点');
return false;
}
// 过滤掉 node_type 为 "cc" 且 members 为空的行
const filteredNodes = nodes?.filter((node: any) => {
if (node?.node_type === ApprovalTemplateNodesNodeTypeEnum.CC.value) {
return node?.members?.length > 0;
}
return true;
});
const nodeNames = filteredNodes?.map((node: any) => node?.name);
const hasDuplicate = nodeNames?.length !== new Set(nodeNames).size;
if (hasDuplicate) {
message.error('节点名称不能重复');
return false;
}
return Apis.Approval.ApprovalTemplates.Store({
...values,
nodes: filteredNodes,
is_enabled: true,
})
.then(() => {
@ -40,27 +96,35 @@ export default function Create(props: MyBetaModalFormProps) {
message.success(props.title + '成功');
return true;
})
.catch(() => false)
}
.catch(() => false);
}}
columns={[
MyFormItems.EnumRadio({
MyFormItems.EnumSelect({
key: 'type',
title: '业务类型',
valueEnum: ApprovalTemplatesTypeEnum,
// valueEnum: ApprovalTemplatesTypeEnum,
valueEnum: () => {
const obj: Record<string, any> = JSON.parse(
JSON.stringify(ApprovalTemplatesTypeEnum),
);
delete obj.Finance;
delete obj.BillModification;
return obj;
},
required: true,
colProps: { span: 24 },
colProps: { span: 8 },
}),
{
key: 'name',
title: '模板名称',
formItemProps: { ...rulesHelper.text },
colProps: { span: 12 },
colProps: { span: 8 },
},
{
key: 'code',
title: '模板编码',
formItemProps: { ...rulesHelper.text },
colProps: { span: 12 },
colProps: { span: 8 },
},
{
valueType: 'formList',
@ -68,7 +132,52 @@ export default function Create(props: MyBetaModalFormProps) {
title: '设置审批节点',
fieldProps: {
copyIconProps: false,
// deleteIconProps: false,
creatorButtonProps: {
creatorButtonText: '添加节点',
},
actionRender: (field: any, action: any) => [
<Button
key="add"
type="link"
size="small"
onClick={() => {
const currentNodeType = form.getFieldValue([
'nodes',
field.name,
'node_type',
]);
if (
currentNodeType ===
ApprovalTemplateNodesNodeTypeEnum.CC.value
) {
message.warning('抄送节点不允许添加');
return;
}
action.add(
{
node_type:
ApprovalTemplateNodesNodeTypeEnum.Approver.value,
name: '',
members: [],
},
field.name + 1,
);
}}
>
</Button>,
<Button
key="delete"
type="link"
size="small"
danger
onClick={() => {
action.remove(field.name);
}}
>
</Button>,
],
},
formItemProps: {
...rulesHelper.array,
@ -113,13 +222,17 @@ export default function Create(props: MyBetaModalFormProps) {
key: 'members',
title: `审批人员`,
colProps: { span: 13 },
// formItemProps: {
// ...rulesHelper.array,
// },
fieldProps: {
mode: 'multiple',
showSearch: true,
maxCount:
node_type ===
ApprovalTemplateNodesNodeTypeEnum.Approver.value
? 1
: 9,
: 99,
},
}),
];
@ -133,7 +246,7 @@ export default function Create(props: MyBetaModalFormProps) {
title: '备注',
key: 'description',
colProps: { span: 24 },
valueType: 'textarea',
// valueType: 'textarea',
},
]}
/>

View File

@ -0,0 +1,104 @@
import { MyBetaModalFormProps, renderTextHelper } from '@/common';
import { MyModal } from '@/components/MyModal';
import { Apis } from '@/gen/Apis';
import {
ApprovalTemplateNodesNodeTypeEnum,
ApprovalTemplatesTypeEnum,
} from '@/gen/Enums';
import { ProCard, ProDescriptions, ProTable } from '@ant-design/pro-components';
import { Space, Spin } from 'antd';
import { useState } from 'react';
export default function Show(props: MyBetaModalFormProps) {
const [loading, setLoading] = useState(true);
const [data, setData] = useState<any>({});
return (
<MyModal
title={props.title || '查看'}
type={'primary'}
width="800px"
onOpen={() => {
if (props?.item?.id) {
setLoading(true);
Apis.Approval.ApprovalTemplates.Show({ id: props.item.id })
.then((res) => {
setData(res?.data || {});
})
.finally(() => {
setLoading(false);
});
}
}}
node={
<Space direction="vertical" style={{ width: '100%' }}>
<ProCard extra={props.extra} title="基础信息">
<Spin spinning={loading}>
<ProDescriptions column={2}>
<ProDescriptions.Item label="业务类型">
<renderTextHelper.Tag
Enums={ApprovalTemplatesTypeEnum}
value={data?.type}
/>
</ProDescriptions.Item>
<ProDescriptions.Item label="模板名称">
{data?.name}
</ProDescriptions.Item>
<ProDescriptions.Item label="模板编码">
{data?.code}
</ProDescriptions.Item>
<ProDescriptions.Item label="备注" span={2}>
{data?.description || '-'}
</ProDescriptions.Item>
</ProDescriptions>
</Spin>
</ProCard>
<ProCard title="审批节点">
<Spin spinning={loading}>
<ProTable
search={false}
toolBarRender={false}
pagination={false}
dataSource={data?.approval_template_nodes || []}
rowKey="id"
columns={[
{
title: '类型',
dataIndex: 'node_type',
render: (_, record) => (
<renderTextHelper.Tag
Enums={ApprovalTemplateNodesNodeTypeEnum}
value={record?.node_type}
/>
),
},
{
title: '节点名称',
dataIndex: 'name',
},
{
title: '审批人员',
dataIndex: 'members',
render: (_, record) => {
if (
record?.approval_template_node_members &&
record?.approval_template_node_members.length > 0
) {
return record.approval_template_node_members
.map(
(m: any) => m?.company_employee?.name || '未知人员',
)
.join('');
}
return '-';
},
},
]}
/>
</Spin>
</ProCard>
</Space>
}
/>
);
}

View File

@ -12,7 +12,7 @@ import {
ApprovalTemplatesTypeEnum,
} from '@/gen/Enums';
import { BetaSchemaForm } from '@ant-design/pro-components';
import { Form, message } from 'antd';
import { Button, Form, message } from 'antd';
let showInfo: any = [];
export default function Update(props: MyBetaModalFormProps) {
const [form] = Form.useForm();
@ -43,9 +43,37 @@ export default function Update(props: MyBetaModalFormProps) {
});
}
}}
onFinish={async (values) =>
Apis.Approval.ApprovalTemplates.Update({
onFinish={async (values) => {
const { nodes } = values;
// 校验:只能提交一行 node_type 为 "cc" 的数据
const ccNodes = nodes?.filter(
(node: any) =>
node?.node_type === ApprovalTemplateNodesNodeTypeEnum.CC.value,
);
if (ccNodes?.length > 1) {
message.error('只能提交一行抄送节点');
return false;
}
// 过滤掉 node_type 为 "cc" 且 members 为空的行
const filteredNodes = nodes?.filter((node: any) => {
if (node?.node_type === ApprovalTemplateNodesNodeTypeEnum.CC.value) {
return node?.members?.length > 0;
}
return true;
});
const nodeNames = filteredNodes?.map((node: any) => node?.name);
const hasDuplicate = nodeNames?.length !== new Set(nodeNames).size;
if (hasDuplicate) {
message.error('节点名称不能重复');
return false;
}
return Apis.Approval.ApprovalTemplates.Update({
...values,
nodes: filteredNodes,
id: props.item?.id ?? 0,
})
.then(() => {
@ -53,27 +81,35 @@ export default function Update(props: MyBetaModalFormProps) {
message.success(props.title + '编辑成功');
return true;
})
.catch(() => false)
}
.catch(() => false);
}}
columns={[
MyFormItems.EnumRadio({
MyFormItems.EnumSelect({
key: 'type',
title: '业务类型',
valueEnum: ApprovalTemplatesTypeEnum,
// valueEnum: ApprovalTemplatesTypeEnum,
valueEnum: () => {
const obj: Record<string, any> = JSON.parse(
JSON.stringify(ApprovalTemplatesTypeEnum),
);
delete obj.Finance;
delete obj.BillModification;
return obj;
},
required: true,
colProps: { span: 24 },
colProps: { span: 8 },
}),
{
key: 'name',
title: '名称',
formItemProps: { ...rulesHelper.text },
colProps: { span: 12 },
colProps: { span: 8 },
},
{
key: 'code',
title: '模板编码',
formItemProps: { ...rulesHelper.text },
colProps: { span: 12 },
colProps: { span: 8 },
},
{
@ -82,7 +118,52 @@ export default function Update(props: MyBetaModalFormProps) {
title: '审批节点',
fieldProps: {
copyIconProps: false,
// deleteIconProps: false,
creatorButtonProps: {
creatorButtonText: '添加节点',
},
actionRender: (field: any, action: any) => [
<Button
key="add"
type="link"
size="small"
onClick={() => {
const currentNodeType = form.getFieldValue([
'nodes',
field.name,
'node_type',
]);
if (
currentNodeType ===
ApprovalTemplateNodesNodeTypeEnum.CC.value
) {
message.warning('抄送节点不允许添加');
return;
}
action.add(
{
node_type:
ApprovalTemplateNodesNodeTypeEnum.Approver.value,
name: '',
members: [],
},
field.name + 1,
);
}}
>
</Button>,
<Button
key="delete"
type="link"
size="small"
danger
onClick={() => {
action.remove(field.name);
}}
>
</Button>,
],
},
formItemProps: {
...rulesHelper.array,
@ -160,7 +241,7 @@ export default function Update(props: MyBetaModalFormProps) {
title: '备注',
key: 'description',
colProps: { span: 24 },
valueType: 'textarea',
// valueType: 'textarea',
},
]}
/>

View File

@ -16,6 +16,7 @@ import { useNavigate } from '@umijs/max';
import { Modal, Space, Tooltip } from 'antd';
import { useState } from 'react';
import Update from './modals/Update';
import ViewCalendar from './modals/ViewCalendar';
export default function Index({ title = '排班管理' }) {
const navigate = useNavigate();
@ -78,29 +79,41 @@ export default function Index({ title = '排班管理' }) {
// search: false,
// }),
Selects?.AssetProjects({
title: '选择项目',
key: 'asset_projects_id',
hidden: true,
Selects?.OrganizationsTree({
title: '选择组织',
key: 'organizations_id',
search: {
transform: (value) => {
return { organizations_id: value[value.length - 1] };
},
},
}),
{
title: '排班日期',
dataIndex: 'schedule_date',
valueType: 'date',
sorter: (a, b) =>
new Date(a.schedule_date).getTime() -
new Date(b.schedule_date).getTime(),
},
{
title: '关联项目',
dataIndex: ['asset_project', 'name'],
title: '所在组织',
dataIndex: 'organization_path',
search: false,
// search: {
// transform: (value) => {
// return { project_name: value };
// return { organization_name: value };
// },
// },
search: false,
},
{
title: '员工',
dataIndex: ['company_employee', 'name'],
sorter: (a, b) =>
a?.company_employee?.name?.localeCompare?.(
b?.company_employee?.name ?? '',
) ?? 0,
search: {
transform: (value) => {
return { employee_name: value };
@ -161,12 +174,13 @@ export default function Index({ title = '排班管理' }) {
dataIndex: ['created_employee', 'name'],
search: false,
},
// MyColumns.CreatedAt(),
MyColumns.UpdatedAt(),
MyColumns.Option({
render: (_, item: any, index, action) => (
<Space key={index}>
<MyTableActions
actions={{
view: <ViewCalendar item={item} reload={action?.reload} />,
update: (
<Update
item={item}

View File

@ -0,0 +1,252 @@
import { MyButtons } from '@/common';
import { Apis } from '@/gen/Apis';
import { AttendanceSchedulesStatusEnum } from '@/gen/Enums';
import { Calendar, message, Modal, Popconfirm, Select } from 'antd';
import dayjs, { Dayjs } from 'dayjs';
import { useCallback, useEffect, useRef, useState } from 'react';
const yearOptions = Array.from({ length: 10 }, (_, i) => {
const y = dayjs().year() - 5 + i;
return { label: `${y}`, value: y };
});
const monthOptions = Array.from({ length: 12 }, (_, i) => ({
label: `${i + 1}`,
value: i,
}));
interface ScheduleItem {
id: number;
schedule_date: string;
attendance_shifts_id?: number;
attendance_shift?: { name: string; id: number };
asset_projects_id?: number;
status?: string;
}
export default function ViewCalendar({
item,
reload: reloadList,
}: {
item: Record<string, any>;
reload?: () => void;
}) {
const [open, setOpen] = useState(false);
const [schedules, setSchedules] = useState<ScheduleItem[]>([]);
const [currentMonth, setCurrentMonth] = useState(dayjs());
const [shiftOptions, setShiftOptions] = useState<
{ label: string; value: number }[]
>([]);
const monthRef = useRef(dayjs());
const employeeId = item?.company_employees_id;
const projectId = item?.asset_projects_id;
const organizationId = item?.organizations_id;
const scheduleDates = item?.schedule_date;
const fetchSchedules = useCallback(
(month: Dayjs) => {
const startDate = month.startOf('month').format('YYYY-MM-DD');
const endDate = month.endOf('month').format('YYYY-MM-DD');
Apis.Attendance.AttendanceSchedules.List({
company_employees_id: employeeId,
status: AttendanceSchedulesStatusEnum.Active.value,
schedule_dates: [startDate, endDate],
perPage: 100,
} as any).then((res) => {
const list: ScheduleItem[] = res?.data || [];
const start = month.startOf('month');
const end = month.endOf('month');
const filtered = list.filter((s) => {
const d = dayjs(s.schedule_date);
return (
(d.isAfter(start) || d.isSame(start, 'day')) &&
(d.isBefore(end) || d.isSame(end, 'day'))
);
});
setSchedules(filtered);
});
},
[employeeId],
);
const reload = useCallback(() => {
fetchSchedules(monthRef.current);
}, [fetchSchedules]);
const fetchShiftOptions = useCallback(() => {
Apis.Attendance.AttendanceShifts.Select({}).then((res) => {
const list = (res?.data || []) as { name: string; id: number }[];
setShiftOptions(list.map((s) => ({ label: s.name, value: s.id })));
});
}, []);
useEffect(() => {
if (open) {
const now = dayjs();
monthRef.current = now;
setCurrentMonth(now);
fetchSchedules(now);
fetchShiftOptions();
} else {
setSchedules([]);
}
}, [open]);
const handleShiftChange = (
scheduleId: number,
shiftId: number,
date: Dayjs,
isNew: boolean,
) => {
if (!isNew) {
Apis.Attendance.AttendanceSchedules.Update({
id: scheduleId,
attendance_shifts_id: shiftId,
}).then(() => {
message.success('修改成功');
reload();
});
} else {
Apis.Attendance.AttendanceSchedules.Store({
company_employees_id: employeeId!,
attendance_shifts_id: shiftId,
asset_projects_id: projectId!,
organizations_id: organizationId,
schedule_date: date.format('YYYY-MM-DD') as unknown as Date,
} as any).then(() => {
message.success('添加成功');
reload();
});
}
};
const handleDelete = (scheduleId: number) => {
Apis.Attendance.AttendanceSchedules.Cancel({
id: scheduleId,
}).then(() => {
message.success('取消成功');
reload();
});
};
const dateCellRender = (date: Dayjs) => {
const matched = schedules.filter((s) =>
dayjs(s.schedule_date).isSame(date, 'day'),
);
const isCancelled = (status?: string) =>
status === 'Cancelled' || status === 'cancelled';
return (
<div>
{matched.map((s) => (
<div
key={s.id}
style={{
display: 'flex',
alignItems: 'center',
opacity: isCancelled(s.status) ? 0.4 : 1,
lineHeight: '18px',
}}
>
<Select
size="small"
variant="borderless"
value={s.attendance_shifts_id || s.attendance_shift?.id}
options={shiftOptions}
style={{ flex: 1, fontSize: 12 }}
disabled={isCancelled(s.status)}
onChange={(val) =>
val != null && handleShiftChange(s.id, val, date, false)
}
/>
{!isCancelled(s.status) && (
<Popconfirm
title="是否取消该班次?"
onConfirm={() => handleDelete(s.id)}
okText="是"
cancelText="否"
>
<a style={{ fontSize: 12, color: '#ff4d4f', flexShrink: 0 }}>
×
</a>
</Popconfirm>
)}
</div>
))}
{matched.length === 0 && (
<Select
size="small"
variant="borderless"
placeholder="+"
value={undefined}
options={shiftOptions}
style={{ width: '100%', fontSize: 12 }}
onChange={(val) =>
val != null && handleShiftChange(0, val, date, true)
}
/>
)}
</div>
);
};
return (
<>
<MyButtons.Default
type="link"
size="small"
title="查看"
onClick={() => setOpen(true)}
/>
<Modal
title={`${item?.company_employee?.name || ''} - 排班日历`}
open={open}
onCancel={() => {
setOpen(false);
reloadList?.();
}}
footer={null}
width={1000}
>
<Calendar
value={currentMonth}
onPanelChange={(date) => {
monthRef.current = date;
setCurrentMonth(date);
fetchSchedules(date);
}}
headerRender={() => (
<div style={{ display: 'flex', gap: 8, padding: '8px 0' }}>
<Select
options={yearOptions}
value={currentMonth.year()}
onChange={(y) => {
const next = monthRef.current.year(y);
monthRef.current = next;
setCurrentMonth(next);
fetchSchedules(next);
}}
/>
<Select
options={monthOptions}
value={currentMonth.month()}
onChange={(m) => {
const next = monthRef.current.month(m);
monthRef.current = next;
setCurrentMonth(next);
fetchSchedules(next);
}}
/>
</div>
)}
cellRender={(date, info) => {
if (info.type === 'date') return dateCellRender(date);
return info.originNode;
}}
/>
</Modal>
</>
);
}

View File

@ -5,7 +5,6 @@ import {
MyTableActions,
MyToolBarActions,
} from '@/common';
import { Selects } from '@/components/Select';
import { Apis } from '@/gen/Apis';
import { ProTable } from '@ant-design/pro-components';
import { Space, Tooltip } from 'antd';
@ -58,11 +57,11 @@ export default function Index({
MyColumns.ID({
search: false,
}),
Selects?.AssetProjects({
title: '选择项目',
key: 'asset_projects_id',
hidden: true,
}),
// Selects?.AssetProjects({
// title: '选择项目',
// key: 'asset_projects_id',
// hidden: true,
// }),
// {
// title: '关联项目',
// dataIndex: ['asset_project', 'name'],
@ -144,7 +143,7 @@ export default function Index({
<Space key={index}>
<MyTableActions
actions={{
update: (
shift_update: (
<Update
key="Update"
item={item}
@ -152,7 +151,7 @@ export default function Index({
title={title}
/>
),
delete: (
shift_delete: (
<MyButtons.Delete
onConfirm={() =>
Apis.Attendance.AttendanceShifts.Delete({

View File

@ -16,9 +16,12 @@ import {
HouseBillsBillStatusEnum,
HouseBillsTypeEnum,
} from '@/gen/Enums';
import { CopyOutlined } from '@ant-design/icons';
import { ProTable } from '@ant-design/pro-components';
import { useNavigate } from '@umijs/max';
import { message, Space } from 'antd';
import { useState } from 'react';
import BillCopy from './modals/BillCopy';
import BillCreate from './modals/BillCreate';
import BIllInfo from './modals/BIllInfo';
import BillRefund from './modals/BillRefund';
@ -44,7 +47,18 @@ export default function Index({ title = '账单明细' }) {
request={async (params, sort) => {
setParams(params);
return MyProTableProps.request(
params,
{
...params,
types: [
HouseBillsTypeEnum.MaintenanceFund.value,
HouseBillsTypeEnum.PropertyFee.value,
HouseBillsTypeEnum.WaterFee.value,
HouseBillsTypeEnum.ElectricityFee.value,
HouseBillsTypeEnum.SharedWaterFee.value,
HouseBillsTypeEnum.SharedElectricityFee.value,
HouseBillsTypeEnum.CarPortFee.value,
],
},
sort,
Apis.Bill.HouseBills.List,
);
@ -145,20 +159,67 @@ export default function Index({ title = '账单明细' }) {
},
},
render: (_, record) => {
const name = record?.asset_house?.full_name;
return record.asset_houses_id ? (
<MyButtons.View
title={`${record?.asset_house?.full_name || '-'}`}
type="link"
onClick={() => {
navigate(`/bills/summary/show/${record.asset_houses_id}`);
}}
/>
<Space size={4}>
<MyButtons.View
title={name || '-'}
type="link"
onClick={() => {
navigate(`/bills/summary/show/${record.asset_houses_id}`);
}}
/>
{name && (
<CopyOutlined
style={{ color: '#999' }}
onClick={() => {
navigator.clipboard.writeText(name);
message.success('已复制');
}}
/>
)}
</Space>
) : (
'车位| ' + record.asset_car_port.full_name
'-'
);
},
},
{
title: '关联车位',
dataIndex: ['asset_car_port', 'full_name'],
search: {
transform: (value) => {
return { car_port_name: value };
},
},
render: (_, record) => {
const name = record?.asset_car_port?.full_name;
return record.asset_car_ports_id ? (
<Space size={4}>
<MyButtons.View
title={name || '-'}
type="link"
onClick={() => {
navigate(
`/bills/summary/car_port_show/${record.asset_car_ports_id}`,
);
}}
/>
{name && (
<CopyOutlined
style={{ color: '#999' }}
onClick={() => {
navigator.clipboard.writeText(name);
message.success('已复制');
}}
/>
)}
</Space>
) : (
'-'
);
},
},
{
title: '账单月份',
render: (_, record) => {
@ -267,6 +328,16 @@ export default function Index({ title = '账单明细' }) {
title={title}
/>
),
copy: (
<BillCopy
key="Copy"
item={{
...item,
}}
reload={action?.reload}
title="复制"
/>
),
delete: (
<MyButtons.Delete
disabled={

View File

@ -0,0 +1,216 @@
import {
MyBetaModalFormProps,
MyButtons,
MyFormItems,
MyModalFormProps,
rulesHelper,
} from '@/common';
import { Selects } from '@/components/Select';
import { Apis } from '@/gen/Apis';
import { HouseBillsTypeEnum } from '@/gen/Enums';
import { BetaSchemaForm } from '@ant-design/pro-components';
import { Form, message } from 'antd';
export default function Copy(props: MyBetaModalFormProps) {
const [form] = Form.useForm();
const isCarPortFee =
props.item?.type === 'CarPortFee' || props.item?.type === 'CarPortDeposit';
return (
<BetaSchemaForm<ApiTypes.Bill.HouseBills.Store>
{...MyModalFormProps.props}
title={`账单复制`}
trigger={<MyButtons.Default title="复制" type="primary" size="small" />}
wrapperCol={{ span: 24 }}
width="800px"
key={new Date().getTime()}
form={form}
onOpenChange={(open: any) => {
if (open && props.item) {
const formValues = {
type: props.item?.type,
amount: props.item?.amount,
discount_amount: props.item?.discount_amount,
late_fee: props.item?.late_fee,
company_receipt_accounts_id:
props.item?.company_receipt_accounts_id,
remark: props.item?.remark,
};
form.setFieldsValue(formValues);
}
}}
onFinish={async (values) => {
const isCarPortFee =
props.item?.type === 'CarPortFee' ||
props.item?.type === 'CarPortDeposit';
return Apis.Bill.HouseBills.Store({
...values,
type: props.item?.type,
asset_car_ports_id: isCarPortFee
? props.item?.asset_car_ports_id
: undefined,
asset_houses_id: !isCarPortFee
? props.item?.asset_houses_id
: undefined,
})
.then(() => {
props.reload?.();
message.success('手动添加账单成功');
return true;
})
.catch(() => false);
}}
columns={[
{
valueType: 'dependency',
name: ['type'],
columns: ({ type }) => {
const field =
type === 'CarPortFee' || type === 'CarPortDeposit'
? {
key: 'asset_car_port_full_name',
title: '车位信息',
colProps: { span: 18 },
fieldProps: {
disabled: true,
},
initialValue: props.item?.asset_car_port?.full_name,
}
: {
key: 'asset_house_full_name',
title: '房屋信息',
colProps: { span: 18 },
fieldProps: {
disabled: true,
},
initialValue: props.item?.asset_house?.full_name,
};
return [field];
},
},
MyFormItems.EnumSelect({
key: 'type',
title: '类型',
colProps: { span: 6 },
valueEnum: HouseBillsTypeEnum,
required: true,
fieldProps: {
disabled: true,
},
}),
{
key: 'amount',
title: '金额',
valueType: 'digit',
colProps: { span: 8 },
fieldProps: {
style: {
width: '100%',
},
},
formItemProps: { ...rulesHelper.number },
},
{
key: 'discount_amount',
title: '优惠金额',
valueType: 'digit',
fieldProps: {
style: {
width: '100%',
},
},
colProps: { span: 8 },
},
{
key: 'late_fee',
title: '滞纳金',
valueType: 'digit',
fieldProps: {
style: {
width: '100%',
},
},
colProps: { span: 8 },
},
{
key: 'month',
title: '账单月份',
valueType: 'date',
colProps: { span: 8 },
fieldProps: {
picker: 'month',
format: 'YYYY-MM',
valueFormat: 'YYYY-MM',
style: {
width: '100%',
},
onChange: (e: any, dateString: string) => {
form.setFieldsValue({
start_date: rulesHelper.getMonthStartDate(dateString),
end_date: rulesHelper.getMonthEndDate(dateString),
});
},
},
formItemProps: { ...rulesHelper.text },
},
{
key: 'start_date',
title: '计费开始日期',
valueType: 'date',
colProps: { span: 8 },
fieldProps: {
style: {
width: '100%',
},
},
formItemProps: { ...rulesHelper.text },
},
{
key: 'end_date',
title: '计费结束日期',
valueType: 'date',
colProps: { span: 8 },
fieldProps: {
style: {
width: '100%',
},
},
formItemProps: { ...rulesHelper.text },
},
{
valueType: 'dependency',
name: ['asset_projects_id'],
columns: ({ asset_projects_id }) => {
return [
{
valueType: 'group',
columns: [
Selects?.ProjectAccounts({
key: 'company_receipt_accounts_id',
title: '选择收款账户',
params: {
projects_id: asset_projects_id,
},
colProps: { span: 24 },
formItemProps: { ...rulesHelper.number },
fieldProps: {
showSearch: true,
},
}),
],
},
];
},
},
{
title: '备注',
key: 'remark',
valueType: 'textarea',
colProps: { span: 24 },
},
]}
/>
);
}

View File

@ -7,20 +7,39 @@ import {
} from '@/common';
import { Selects } from '@/components/Select';
import { Apis } from '@/gen/Apis';
import { HouseBillsTypeEnum } from '@/gen/Enums';
import {
ApprovalTemplateNodesNodeTypeEnum,
ApprovalTemplatesTypeEnum,
HouseBillsBillStatusEnum,
HouseBillsTypeEnum,
} from '@/gen/Enums';
import { BetaSchemaForm } from '@ant-design/pro-components';
import { Form, message } from 'antd';
import { Button, Form, message } from 'antd';
import { useState } from 'react';
export default function Update(props: MyBetaModalFormProps) {
const [form] = Form.useForm();
const [ApprovalTemplates, setApprovalTemplates] = useState<any>([]);
return (
<BetaSchemaForm<ApiTypes.Bill.HouseBills.Update>
{...MyModalFormProps.props}
title={`${props.title}编辑`}
trigger={<MyButtons.Edit />}
trigger={
<MyButtons.Default
title="编辑"
type="primary"
size="small"
disabled={
props?.item?.bill_status === HouseBillsBillStatusEnum.Paid.value ||
props?.item?.bill_status ===
HouseBillsBillStatusEnum.Cancelled.value
}
/>
}
wrapperCol={{ span: 24 }}
width="800px"
key={new Date().getTime()}
key={props.item?.id}
form={form}
onOpenChange={(open: any) => {
if (open && props.item) {
@ -37,15 +56,64 @@ export default function Update(props: MyBetaModalFormProps) {
form.setFieldsValue(formValues);
}
}}
onFinish={async (values) =>
Apis.Bill.HouseBills.Update({ ...values, id: props.item?.id ?? 0 })
onFinish={async (values: any) => {
const { node_approvers } = values;
if (ApprovalTemplates?.length > 0 && node_approvers?.length > 0) {
// 遍历模板节点,通过名称找到对应的提交节点进行校验
for (const templateNode of ApprovalTemplates) {
// 通过名称找到对应的提交节点
const submittedNode = node_approvers.find(
(node: any) => node?.name === templateNode?.name,
);
// 如果模板节点在提交数据中不存在,说明被删除了,报错
if (!submittedNode) {
message.error(`节点"${templateNode?.name}"不能删除`);
return false;
}
// 校验审批人
if (
templateNode?.node_type ===
ApprovalTemplateNodesNodeTypeEnum.Approver.value
) {
const originalMembers =
templateNode?.approval_template_node_members?.map(
(member: any) => member?.company_employees_id,
);
if (originalMembers?.length > 0) {
const submittedMembers = submittedNode?.members || [];
const isMatch =
originalMembers.length === submittedMembers.length &&
originalMembers.every((memberId: number) =>
submittedMembers.includes(memberId),
);
if (!isMatch) {
message.error(
`节点"${templateNode?.name}"的审批人必须与审批模板设置保持一致`,
);
return false;
}
}
}
}
}
return Apis.Bill.HouseBills.Update({
...values,
id: props.item?.id ?? 0,
})
.then(() => {
props.reload?.();
message.success(props.title + '成功');
return true;
})
.catch(() => false)
}
.catch(() => false);
}}
columns={[
MyFormItems.EnumRadio({
key: 'type',
@ -166,6 +234,182 @@ export default function Update(props: MyBetaModalFormProps) {
valueType: 'textarea',
colProps: { span: 24 },
},
Selects?.ApprovalTemplates({
key: 'approval_templates_id',
title: '审批模版',
params: {
type: ApprovalTemplatesTypeEnum.HouseBillUpdate.value,
is_enabled: '1',
},
colProps: { span: 24 },
formItemProps: { ...rulesHelper.number },
fieldProps: {
onChange: (e: any) => {
if (e) {
const templateId = typeof e === 'object' ? e.value : e;
Apis.Approval.ApprovalTemplates.Show({
id: templateId,
}).then((res) => {
setApprovalTemplates(res?.data?.approval_template_nodes);
form.setFieldsValue({
approval_templates_id: templateId,
node_approvers: res?.data?.approval_template_nodes?.map(
(item: any) => ({
...item,
members: item?.approval_template_node_members?.map(
(member: any) => member?.company_employees_id,
),
}),
),
});
});
}
},
},
}),
{
valueType: 'dependency',
name: ['approval_templates_id'],
columns: ({ approval_templates_id }) => {
return approval_templates_id
? [
{
valueType: 'formList',
dataIndex: 'node_approvers',
fieldProps: {
creatorButtonProps: false,
actionRender: (field: any, action: any) => [
<Button
key="add"
type="link"
size="small"
onClick={() => {
const currentNodeType = form.getFieldValue([
'node_approvers',
field.name,
'node_type',
]);
if (
currentNodeType ===
ApprovalTemplateNodesNodeTypeEnum.CC.value
) {
message.warning('抄送节点不允许添加');
return;
}
action.add(
{
node_type:
ApprovalTemplateNodesNodeTypeEnum.Approver
.value,
},
field.name + 1,
);
}}
>
</Button>,
<Button
key="delete"
type="link"
size="small"
danger
onClick={() => {
action.remove(field.name);
}}
>
</Button>,
],
},
formItemProps: {
...rulesHelper.array,
wrapperCol: { span: 24 },
},
columns: [
{
valueType: 'group',
colProps: { span: 24 },
columns: [
MyFormItems.EnumSelect({
key: 'node_type',
valueEnum: ApprovalTemplateNodesNodeTypeEnum,
colProps: { span: 5 },
formItemProps: {
...rulesHelper.text,
},
}),
{
key: 'name',
colProps: { span: 6 },
formItemProps: {
...rulesHelper.text,
},
},
{
valueType: 'dependency',
name: ['node_type'],
columns: ({ node_type }) => {
return [
Selects.Employees({
key: 'members',
title: ``,
colProps: { span: 13 },
formItemProps: {
...rulesHelper.array,
},
fieldProps: {
mode: 'multiple',
showSearch: true,
maxCount:
node_type ===
ApprovalTemplateNodesNodeTypeEnum.Approver
.value
? 1
: 9,
maxTagTextLength: 20,
labelRender: (res: any) => {
if (res?.label) {
return res?.label;
} else {
return ApprovalTemplates?.map(
(item: any) => {
if (
item?.node_type === node_type &&
item
?.approval_template_node_members
?.length
) {
return item?.approval_template_node_members?.map(
(i: any) => {
if (
i?.company_employees_id ===
res?.value
) {
return (
i?.company_employee
?.name || ''
);
}
},
);
}
},
);
}
},
},
}),
];
},
},
],
},
],
},
]
: [];
},
},
]}
/>
);

View File

@ -119,6 +119,7 @@ export default function PayCreate(props: PayCreateProps): React.ReactElement {
delete obj.Alipay;
delete obj.TongLian;
delete obj.Prepayment;
delete obj.CCB;
return obj;
},
required: true,

View File

@ -1,7 +1,12 @@
import { MyBetaModalFormProps, MyButtons, MyModalFormProps } from '@/common';
import {
MyBetaModalFormProps,
MyButtons,
MyModalFormProps,
renderTextHelper,
} from '@/common';
import { Apis } from '@/gen/Apis';
import { HouseBillsBillStatusEnum } from '@/gen/Enums';
import { BetaSchemaForm } from '@ant-design/pro-components';
import { HouseBillsBillStatusEnum, HouseBillsTypeEnum } from '@/gen/Enums';
import { BetaSchemaForm, ProDescriptions } from '@ant-design/pro-components';
import { Form, QRCode, Tabs } from 'antd';
import { useState } from 'react';
@ -10,7 +15,6 @@ export default function ShowQrCode(props: MyBetaModalFormProps) {
const [activeTab, setActiveTab] = useState<string>('wechat');
const [wechatQrCode, setWechatQrCode] = useState<string>('');
const [alipayQrCode, setAlipayQrCode] = useState<string>('');
const [orderInfo, setOrderInfo] = useState<any>(null);
const fetchQrCode = (type: 'wechat' | 'alipay') => {
if (props?.item?.amount && props?.item?.accept_amount) {
@ -24,13 +28,10 @@ export default function ShowQrCode(props: MyBetaModalFormProps) {
if (type === 'wechat') {
Apis.HouseOrder.HouseOrders.GetPayCode(params).then((res: any) => {
setWechatQrCode(res?.data?.qr_code || '');
setOrderInfo(res?.data?.order);
});
} else {
Apis.HouseOrder.HouseOrders.AlipayQrCode(params).then((res: any) => {
// 支付宝返回的 qr_code 是对象,实际链接在 payinfo 字段
setAlipayQrCode(res?.data?.qr_code?.payinfo || '');
setOrderInfo(res?.data?.order);
setAlipayQrCode(res?.data?.payinfo || '');
});
}
}
@ -60,7 +61,6 @@ export default function ShowQrCode(props: MyBetaModalFormProps) {
if (open) {
setWechatQrCode('');
setAlipayQrCode('');
setOrderInfo(null);
setActiveTab('wechat');
fetchQrCode('wechat');
}
@ -72,10 +72,31 @@ export default function ShowQrCode(props: MyBetaModalFormProps) {
key: 'qrcode',
renderFormItem() {
return (
<div style={{ textAlign: 'center' }}>
<div style={{ fontSize: '16px', fontWeight: 'bold', marginBottom: '16px' }}>
{props?.item?.asset_house?.full_name}
</div>
<div style={{ textAlign: 'left' }}>
<ProDescriptions
column={2}
size="small"
bordered
style={{ marginBottom: '16px' }}
>
<ProDescriptions.Item label="关联房屋/车位" span={2}>
{props?.item?.asset_houses_id
? ` ${props?.item?.asset_house?.full_name || ''}`
: props?.item?.asset_car_port?.full_name
? ` ${props?.item?.asset_car_port?.full_name}`
: ''}
</ProDescriptions.Item>
<ProDescriptions.Item label="费用类型">
<renderTextHelper.Tag
Enums={HouseBillsTypeEnum}
value={props?.item?.type}
key="type"
/>
</ProDescriptions.Item>
<ProDescriptions.Item label="应收金额">
¥{props?.item?.total_payable_amount || 0}
</ProDescriptions.Item>
</ProDescriptions>
<Tabs
activeKey={activeTab}
onChange={(key) => {
@ -105,7 +126,15 @@ export default function ShowQrCode(props: MyBetaModalFormProps) {
alt="微信支付二维码"
/>
) : (
<div style={{ width: '220px', height: '220px', display: 'flex', alignItems: 'center', justifyContent: 'center' }}>
<div
style={{
width: '220px',
height: '220px',
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
}}
>
...
</div>
)}
@ -114,7 +143,7 @@ export default function ShowQrCode(props: MyBetaModalFormProps) {
},
{
key: 'alipay',
label: '支付宝支付',
label: '支付宝',
children: (
<div
style={{
@ -126,7 +155,15 @@ export default function ShowQrCode(props: MyBetaModalFormProps) {
{alipayQrCode ? (
<QRCode value={alipayQrCode} size={220} />
) : (
<div style={{ width: '220px', height: '220px', display: 'flex', alignItems: 'center', justifyContent: 'center' }}>
<div
style={{
width: '220px',
height: '220px',
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
}}
>
...
</div>
)}
@ -135,17 +172,6 @@ export default function ShowQrCode(props: MyBetaModalFormProps) {
},
]}
/>
<div style={{ fontSize: '16px', fontWeight: 'bold' }}>
<span style={{ color: '#f00' }}>
¥
{(
parseFloat(orderInfo?.total_payable_amount || '0') || 0
).toFixed(2)}
</span>
</div>
<div>ID{orderInfo?.id || ''}</div>
</div>
);
},

View File

@ -0,0 +1,62 @@
import { MyPageContainer } from '@/common';
import { Apis } from '@/gen/Apis';
import { ProCard } from '@ant-design/pro-components';
import { useParams } from '@umijs/max';
import { Tabs } from 'antd';
import { useEffect, useState } from 'react';
import CarPortBillInfo from '../components/CarPortBillInfo';
import PaidBill from '../table/PaidBill';
import UnpaidBill from '../table/UnpaidBill';
export default function CarPortShow({ title = '车位账单详情' }) {
const { id } = useParams<{ id: string }>();
const [data, setData] = useState<any>({});
const loadShow = () => {
Apis.Asset.AssetCarPorts.Show({ id: Number(id) }).then((res) => {
setData(res?.data);
});
};
useEffect(() => {
loadShow();
}, [id]);
const billItem = { ...data, asset_car_ports_id: id };
const items = [
{
label: '未缴账单',
key: '1',
closable: false,
children: <UnpaidBill item={billItem} />,
},
{
label: '已缴账单',
key: '2',
closable: false,
children: <PaidBill item={billItem} />,
},
// {
// label: '退款账单',
// key: '4',
// closable: false,
// children: <MyRefund item={{ id, asset_car_ports_id: id }} />,
// },
];
return (
<MyPageContainer
title={title}
enableTabs
tabKey={`car-port-bill-detail-${id}`}
tabLabel={data?.full_name || title}
>
<CarPortBillInfo item={data} reload={loadShow} />
<ProCard style={{ marginTop: 16 }}>
<Tabs type="card" items={items} defaultActiveKey="1" />
</ProCard>
</MyPageContainer>
);
}

View File

@ -0,0 +1,19 @@
import { MyBetaModalFormProps } from '@/common';
import { ProCard, ProDescriptions } from '@ant-design/pro-components';
export default function CarPortBillInfo(props: MyBetaModalFormProps) {
const { item } = props;
return (
<ProCard title="基本信息">
<ProDescriptions bordered>
<ProDescriptions.Item label="车位名称" span={2}>
{item?.full_name || '-'}
</ProDescriptions.Item>
<ProDescriptions.Item label="所属项目">
{item?.asset_project?.name || '-'}
</ProDescriptions.Item>
</ProDescriptions>
</ProCard>
);
}

View File

@ -2,7 +2,9 @@ import { MyColumns, MyProTableProps } from '@/common';
import { Apis } from '@/gen/Apis';
import { HouseBillsBillStatusEnum, HouseBillsTypeEnum } from '@/gen/Enums';
import { ProTable } from '@ant-design/pro-components';
import { Space } from 'antd';
import { useEffect, useRef, useState } from 'react';
import BillCopy from '../../house_bills/modals/BillCopy';
import PayCreate from './modals/PayCreate';
import ShowQrCode from './modals/ShowQrCode';
@ -122,13 +124,21 @@ export default function Index({ ...rest }) {
},
search: false,
},
// MyColumns.Option({
// render: (_, item: any, index, action) => (
// <Space key={index}>
// <BillUpdate item={item} reload={action?.reload} title="编辑" />
// </Space>
// ),
// }),
MyColumns.Option({
width: 100,
render: (_, item: any, index, action) => (
<Space key={index}>
<BillCopy
key="Copy"
item={{
...item,
}}
reload={action?.reload}
title="复制"
/>
</Space>
),
}),
]}
/>
</>

View File

@ -1,6 +1,6 @@
import { MyBetaModalFormProps, MyButtons, MyModalFormProps } from '@/common';
import { Apis } from '@/gen/Apis';
import { BetaSchemaForm } from '@ant-design/pro-components';
import { BetaSchemaForm, ProDescriptions } from '@ant-design/pro-components';
import { Form, QRCode, Tabs } from 'antd';
import { useState } from 'react';
@ -9,7 +9,6 @@ export default function ShowQrCode(props: MyBetaModalFormProps) {
const [activeTab, setActiveTab] = useState<string>('wechat');
const [wechatQrCode, setWechatQrCode] = useState<string>('');
const [alipayQrCode, setAlipayQrCode] = useState<string>('');
const [orderInfo, setOrderInfo] = useState<any>(null);
const fetchQrCode = (type: 'wechat' | 'alipay') => {
let total_amount = 0;
@ -26,12 +25,10 @@ export default function ShowQrCode(props: MyBetaModalFormProps) {
if (type === 'wechat') {
Apis.HouseOrder.HouseOrders.GetPayCode(params).then((res: any) => {
setWechatQrCode(res?.data?.qr_code || '');
setOrderInfo(res?.data?.order);
});
} else {
Apis.HouseOrder.HouseOrders.AlipayQrCode(params).then((res: any) => {
setAlipayQrCode(res?.data?.qr_code?.payinfo || '');
setOrderInfo(res?.data?.order);
setAlipayQrCode(res?.data?.payinfo || '');
});
}
}
@ -57,7 +54,6 @@ export default function ShowQrCode(props: MyBetaModalFormProps) {
if (open) {
setWechatQrCode('');
setAlipayQrCode('');
setOrderInfo(null);
setActiveTab('wechat');
fetchQrCode('wechat');
}
@ -69,10 +65,29 @@ export default function ShowQrCode(props: MyBetaModalFormProps) {
key: 'qrcode',
renderFormItem() {
return (
<div style={{ textAlign: 'center' }}>
<div style={{ fontSize: '16px', fontWeight: 'bold', marginBottom: '16px' }}>
{props?.item?.asset_house?.full_name}
</div>
<div style={{ textAlign: 'left' }}>
<ProDescriptions
column={2}
size="small"
bordered
style={{ marginBottom: '16px' }}
>
<ProDescriptions.Item label="关联房屋/车位" span={2}>
{props?.item?.asset_houses_id
? props?.item?.asset_house?.full_name || ''
: props?.item?.asset_car_port?.full_name || ''}
</ProDescriptions.Item>
<ProDescriptions.Item label="应收金额">
¥
{props?.item?.list
?.reduce(
(sum: number, item: any) =>
sum + Number(item?.total_payable_amount || 0),
0,
)
.toFixed(2) || 0}
</ProDescriptions.Item>
</ProDescriptions>
<Tabs
activeKey={activeTab}
onChange={(key) => {
@ -102,7 +117,15 @@ export default function ShowQrCode(props: MyBetaModalFormProps) {
alt="微信支付二维码"
/>
) : (
<div style={{ width: '220px', height: '220px', display: 'flex', alignItems: 'center', justifyContent: 'center' }}>
<div
style={{
width: '220px',
height: '220px',
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
}}
>
...
</div>
)}
@ -111,7 +134,7 @@ export default function ShowQrCode(props: MyBetaModalFormProps) {
},
{
key: 'alipay',
label: '支付宝支付',
label: '支付宝',
children: (
<div
style={{
@ -123,7 +146,15 @@ export default function ShowQrCode(props: MyBetaModalFormProps) {
{alipayQrCode ? (
<QRCode value={alipayQrCode} size={220} />
) : (
<div style={{ width: '220px', height: '220px', display: 'flex', alignItems: 'center', justifyContent: 'center' }}>
<div
style={{
width: '220px',
height: '220px',
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
}}
>
...
</div>
)}
@ -132,15 +163,6 @@ export default function ShowQrCode(props: MyBetaModalFormProps) {
},
]}
/>
<div style={{ fontSize: '16px', fontWeight: 'bold' }}>
<span style={{ color: '#f00' }}>
¥
{parseFloat(orderInfo?.total_payable_amount || '0').toFixed(2)}
</span>
</div>
<div>ID{orderInfo?.id || ''}</div>
</div>
);
},

View File

@ -0,0 +1,256 @@
import {
MyButtons,
MyColumns,
MyPageContainer,
MyProTableProps,
MyTableActions,
MyToolBarActions,
} from '@/common';
import { MyExport } from '@/components/MyExport';
import { Selects } from '@/components/Select';
import { Apis } from '@/gen/Apis';
import {
ApprovalTemplatesTypeEnum,
BillPaymentsTypeEnum,
BillsStatusEnum,
} from '@/gen/Enums';
import { ProTable } from '@ant-design/pro-components';
import { useNavigate } from '@umijs/max';
import { Tooltip } from 'antd';
import { useState } from 'react';
import AuditPayment from './modals/AuditPayment';
import BillCopy from './modals/BillCopy';
import BillCreate from './modals/BillCreate';
import BIllInfo from './modals/BIllInfo';
import BillRefund from './modals/BillRefund';
import BillUpdate from './modals/BillUpdate';
import PayCreate from './modals/Pay';
import ShowQrCode from './modals/QrCode';
export default function Index({ title = '临时收费' }) {
const navigate = useNavigate();
const [getParams, setParams] = useState({});
return (
<MyPageContainer
title={title}
enableTabs={true}
tabKey="bills"
tabLabel={title}
>
<ProTable
{...MyProTableProps.props}
headerTitle={title}
request={async (params, sort) => {
setParams(params);
return MyProTableProps.request(params, sort, Apis.Bill.Bills.List);
}}
toolBarRender={(action: any) => [
<MyToolBarActions
key="toolbar"
actions={{
export: (
<MyExport
key="export"
title="账单导出"
item={getParams}
download={Apis.Bill.Bills}
/>
),
add: (
<BillCreate
key="Create"
reload={action?.reload}
title={title}
/>
),
}}
/>,
]}
columns={[
MyColumns.ID({ search: false }),
Selects?.AssetProjects({
title: '选择项目',
key: 'asset_projects_id',
hidden: true,
}),
{
key: 'year',
title: '账单年',
valueType: 'date',
fieldProps: {
picker: 'year',
format: 'YYYY',
valueFormat: 'YYYY',
style: {
width: '100%',
},
},
hidden: true,
},
{
key: 'month',
title: '账单月',
valueType: 'date',
fieldProps: {
picker: 'month',
format: 'YYYY-MM',
valueFormat: 'YYYY/MM',
style: {
width: '100%',
},
},
search: {
transform: (value) => {
let month = value.split('-');
return { year: month[0], month: month[1] };
},
},
hidden: true,
},
{
title: '所属项目',
dataIndex: ['asset_project', 'name'],
search: false,
},
{
title: '账单月份',
render: (_, record) => {
return `${record.year}-${String(record.month).padStart(2, '0')}`;
},
search: false,
},
MyColumns.EnumTag({
title: '类型',
dataIndex: 'type',
valueEnum: BillPaymentsTypeEnum,
}),
MyColumns.EnumTag({
title: '状态',
dataIndex: 'status',
valueEnum: BillsStatusEnum,
}),
{
title: '账单金额',
dataIndex: 'amount',
search: false,
},
{
title: '账单备注',
dataIndex: 'remark',
search: false,
render: (_, item: any) => {
const text = item?.remark || '';
if (text.length > 4) {
return <Tooltip title={text}>{text.slice(0, 10)}...</Tooltip>;
}
return text;
},
},
{
title: '已收金额',
dataIndex: ['bill_payment', 'amount'],
search: false,
},
{
title: '收款日期',
dataIndex: ['bill_payment', 'paid_time'],
render: (_, item: any) => {
return item?.bill_payment?.paid_time?.slice(0, 10) || '-';
},
search: false,
},
{
title: '收款人',
render: (_, record) => {
return `${record?.reconciliation_person || ''}-${
record?.reconciliation_person_phone || ''
}`;
},
search: false,
},
MyColumns.Boolean({
title: '退款申请',
dataIndex: 'has_refunding',
}),
MyColumns.Option({
render: (_, item: any, _index, action) => (
<MyTableActions
actions={{
show: (
<BIllInfo
item={{ ...item, type: 'primary' }}
reload={action?.reload}
title="查看"
/>
),
qrcode: (
<ShowQrCode key="ShowQrCode" item={item} title="收款码" />
),
pay: (
<PayCreate
item={{ ...item, size: 'small' }}
reload={action?.reload}
title={title}
/>
),
update: (
<BillUpdate
item={item}
reload={action?.reload}
title={title}
/>
),
refund: (
<BillRefund
item={{
...item,
type: item?.type,
approval_type: ApprovalTemplatesTypeEnum.Refund.value,
total_paid_amount: item.total_paid_amount,
}}
reload={action?.reload}
title={title}
/>
),
audit_payment: (
<AuditPayment item={item} reload={action?.reload} />
),
copy: (
<BillCopy
key="Copy"
item={{
...item,
}}
reload={action?.reload}
title="复制"
/>
),
delete: (
<MyButtons.Delete
disabled={
item.status === BillsStatusEnum.Paid.value ||
item.status === BillsStatusEnum.ToBeConfirmed.value ||
item.status === BillsStatusEnum.UnderApproval.value
}
onConfirm={() =>
Apis.Bill.Bills.Delete({ id: item.id }).then(() =>
action?.reload(),
)
}
/>
),
}}
/>
),
}),
]}
/>
</MyPageContainer>
);
}

View File

@ -0,0 +1,161 @@
import {
MyBetaModalFormProps,
MyButtons,
MyFormItems,
MyModalFormProps,
renderTextHelper,
rulesHelper,
} from '@/common';
import { Apis } from '@/gen/Apis';
import { BillsStatusEnum, HouseOrdersPaymentMethodEnum } from '@/gen/Enums';
import { BetaSchemaForm } from '@ant-design/pro-components';
import { request } from '@umijs/max';
import { Image as AntImage, Descriptions, Form, message } from 'antd';
import * as React from 'react';
const AuditStatusEnum = {
Approved: { text: '通过', value: 'Approved' },
Rejected: { text: '驳回', value: 'Rejected' },
};
export default function AuditPayment(
props: MyBetaModalFormProps,
): React.ReactElement {
const [form] = Form.useForm();
return (
<BetaSchemaForm<ApiTypes.Bill.Bills.AuditPayment>
{...MyModalFormProps.props}
title="审核线下收款"
trigger={
<MyButtons.Default
title="审核收款"
size="small"
disabled={props.item?.status !== BillsStatusEnum.ToBeConfirmed.value}
/>
}
width="600px"
form={form}
key={new Date().getTime()}
onOpenChange={(open: boolean): void => {
if (open) {
form.resetFields();
}
}}
onFinish={async (values: any) => {
if (values.status === 'Approved' && values.serial_number) {
await request('company/bill/bills/update_serial_number', {
data: {
id: props.item?.id ?? 0,
serial_number: values.serial_number,
},
});
}
return Apis.Bill.Bills.AuditPayment({
...values,
id: props.item?.id ?? 0,
})
.then(() => {
props.reload?.();
message.success('审核成功');
return true;
})
.catch(() => false);
}}
columns={[
{
valueType: 'divider',
fieldProps: {
orientation: 'left',
children: '收款信息',
},
},
{
colProps: { span: 24 },
renderFormItem: () => (
<Descriptions
column={2}
size="small"
bordered
style={{ marginBottom: '12px' }}
>
<Descriptions.Item label="账单ID">
{props.item?.id || '-'}
</Descriptions.Item>
<Descriptions.Item label="关联项目">
{props.item?.asset_project?.name || '-'}
</Descriptions.Item>
<Descriptions.Item label="收款金额">
¥ {props.item?.bill_payment?.amount || 0}
</Descriptions.Item>
<Descriptions.Item label="收款方式">
{renderTextHelper.Tag({
Enums: HouseOrdersPaymentMethodEnum,
value: props.item?.bill_payment?.payment_method,
}) || '-'}
</Descriptions.Item>
<Descriptions.Item label="收款日期">
{props.item?.bill_payment?.paid_time?.slice(0, 10) || '-'}
</Descriptions.Item>
<Descriptions.Item label="备注">
{props.item?.bill_payment?.remark || '-'}
</Descriptions.Item>
{props.item?.bill_payment?.pay_certificate?.length > 0 && (
<Descriptions.Item label="收款凭证" span={2}>
<AntImage.PreviewGroup>
{props.item?.bill_payment?.pay_certificate?.map(
(img: any) => (
<AntImage
key={img.uid}
height={30}
src={img.url}
style={{ marginRight: 12 }}
/>
),
)}
</AntImage.PreviewGroup>
</Descriptions.Item>
)}
</Descriptions>
),
},
MyFormItems.EnumRadio({
key: 'status',
title: '审核意见',
valueEnum: AuditStatusEnum,
colProps: { span: 24 },
formItemProps: { ...rulesHelper.text },
}),
{
valueType: 'dependency',
name: ['status'],
columns: ({ status }) => {
if (status === 'Approved') {
return [
{
title: '流水号',
valueType: 'text',
key: 'serial_number',
formItemProps: { ...rulesHelper.text },
},
{
title: '审核备注',
valueType: 'textarea',
key: 'rejected_reason',
},
];
}
return [
{
title: '驳回理由',
valueType: 'textarea',
key: 'rejected_reason',
formItemProps: { ...rulesHelper.text },
},
];
},
},
]}
/>
);
}

View File

@ -0,0 +1,114 @@
import { MyBetaModalFormProps, renderTextHelper } from '@/common';
import { MyModal } from '@/components/MyModal';
import { Apis } from '@/gen/Apis';
import {
BillPaymentsTypeEnum,
HouseOrdersPaymentMethodEnum,
} from '@/gen/Enums';
import { ProCard, ProDescriptions } from '@ant-design/pro-components';
import { Image as AntImage, Space, Spin } from 'antd';
import { useState } from 'react';
export default function BillInfo(props: MyBetaModalFormProps) {
const [loading, setLoading] = useState(true);
const [data, setData] = useState<any>({});
return (
<MyModal
title={props.title || '查看账单'}
type={props.item?.type || 'primary'}
width="600px"
onOpen={() => {
if (props?.item?.id) {
setLoading(true);
Apis.Bill.Bills.Show({ id: props.item.id })
.then((res) => {
setData(res?.data || {});
})
.finally(() => {
setLoading(false);
});
}
}}
node={
<Space direction="vertical" style={{ width: '100%' }}>
<ProCard extra={props.extra}>
<Spin spinning={loading}>
<ProDescriptions column={2}>
{/* 项目相关信息 */}
<ProDescriptions.Item label="项目名称" span={2}>
{data?.asset_project?.name || ''}
</ProDescriptions.Item>
{/* 账单基本信息 */}
<ProDescriptions.Item label="账单月份">
{data?.year || ''}-{data?.month || ''}
</ProDescriptions.Item>
<ProDescriptions.Item label="费用类型">
<renderTextHelper.Tag
Enums={BillPaymentsTypeEnum}
value={data?.type}
key="type"
/>
</ProDescriptions.Item>
<ProDescriptions.Item label="账单金额">
{data?.amount || 0}
</ProDescriptions.Item>
<ProDescriptions.Item label="应收金额">
{data?.mount || 0}
</ProDescriptions.Item>
<ProDescriptions.Item label="账单月份" span={2}>
{data?.year || ''}-{data?.month || ''}
</ProDescriptions.Item>
{/* 其他信息 */}
<ProDescriptions.Item label="收款账户" span={2}>
{data?.receipt_account?.company_name || ''} |{' '}
{data?.receipt_account?.company_bank || ''} |{' '}
{data?.receipt_account?.company_account || ''}
</ProDescriptions.Item>
<ProDescriptions.Item label="账单备注" span={3}>
{data?.remark || ''}
</ProDescriptions.Item>
{/* 收款信息 */}
<ProDescriptions.Item label="收款金额">
¥ {data?.bill_payment?.amount || '-'}
</ProDescriptions.Item>
<ProDescriptions.Item label="收款方式">
{renderTextHelper.Tag({
Enums: HouseOrdersPaymentMethodEnum,
value: data?.bill_payment?.payment_method,
}) || '-'}
</ProDescriptions.Item>
<ProDescriptions.Item label="收款日期">
{data?.bill_payment?.paid_time?.slice(0, 10) || '-'}
</ProDescriptions.Item>
<ProDescriptions.Item label="收款人">
{data?.reconciliation_person || '-'}-
{data?.reconciliation_person_phone || '-'}
</ProDescriptions.Item>
<ProDescriptions.Item label="收款备注" span={3}>
{data?.bill_payment?.remark || '-'}
</ProDescriptions.Item>
{data?.bill_payment?.pay_certificate?.length > 0 && (
<ProDescriptions.Item label="收款凭证" span={2}>
<AntImage.PreviewGroup>
{data?.bill_payment?.pay_certificate?.map((img: any) => (
<AntImage
key={img.uid}
height={30}
src={img.url}
style={{ marginRight: 12 }}
/>
))}
</AntImage.PreviewGroup>
</ProDescriptions.Item>
)}
</ProDescriptions>
</Spin>
</ProCard>
</Space>
}
/>
);
}

View File

@ -0,0 +1,214 @@
import {
MyBetaModalFormProps,
MyButtons,
MyFormItems,
MyModalFormProps,
rulesHelper,
} from '@/common';
import { Selects } from '@/components/Select';
import { Apis } from '@/gen/Apis';
import { HouseBillsTypeEnum } from '@/gen/Enums';
import { BetaSchemaForm } from '@ant-design/pro-components';
import { Form, message } from 'antd';
export default function Copy(props: MyBetaModalFormProps) {
const [form] = Form.useForm();
const isCarPortFee =
props.item?.type === 'CarPortFee' || props.item?.type === 'CarPortDeposit';
return (
<BetaSchemaForm<ApiTypes.Bill.HouseBills.Store>
{...MyModalFormProps.props}
title={`账单复制`}
trigger={<MyButtons.Default title="复制" type="primary" size="small" />}
wrapperCol={{ span: 24 }}
width="800px"
key={new Date().getTime()}
form={form}
onOpenChange={(open: any) => {
if (open && props.item) {
const formValues = {
type: props.item?.type,
amount: props.item?.amount,
company_receipt_accounts_id:
props.item?.company_receipt_accounts_id,
remark: props.item?.remark,
};
form.setFieldsValue(formValues);
}
}}
onFinish={async (values) => {
const isCarPortFee =
props.item?.type === 'CarPortFee' ||
props.item?.type === 'CarPortDeposit';
return Apis.Bill.HouseBills.Store({
...values,
type: props.item?.type,
asset_car_ports_id: isCarPortFee
? props.item?.asset_car_ports_id
: undefined,
asset_houses_id: !isCarPortFee
? props.item?.asset_houses_id
: undefined,
})
.then(() => {
props.reload?.();
message.success('手动添加账单成功');
return true;
})
.catch(() => false);
}}
columns={[
{
valueType: 'dependency',
name: ['type'],
columns: ({ type }) => {
const field =
type === 'CarPortFee' || type === 'CarPortDeposit'
? {
key: 'asset_car_port_full_name',
title: '车位信息',
colProps: { span: 18 },
fieldProps: {
disabled: true,
},
initialValue: props.item?.asset_car_port?.full_name,
}
: {
key: 'asset_house_full_name',
title: '房屋信息',
colProps: { span: 18 },
fieldProps: {
disabled: true,
},
initialValue: props.item?.asset_house?.full_name,
};
return [field];
},
},
MyFormItems.EnumSelect({
key: 'type',
title: '类型',
colProps: { span: 6 },
valueEnum: HouseBillsTypeEnum,
required: true,
fieldProps: {
disabled: true,
},
}),
{
key: 'amount',
title: '金额',
valueType: 'digit',
colProps: { span: 8 },
fieldProps: {
style: {
width: '100%',
},
},
formItemProps: { ...rulesHelper.number },
},
{
key: 'discount_amount',
title: '优惠金额',
valueType: 'digit',
fieldProps: {
style: {
width: '100%',
},
},
colProps: { span: 8 },
},
{
key: 'late_fee',
title: '滞纳金',
valueType: 'digit',
fieldProps: {
style: {
width: '100%',
},
},
colProps: { span: 8 },
},
{
key: 'month',
title: '账单月份',
valueType: 'date',
colProps: { span: 8 },
fieldProps: {
picker: 'month',
format: 'YYYY-MM',
valueFormat: 'YYYY-MM',
style: {
width: '100%',
},
onChange: (e: any, dateString: string) => {
form.setFieldsValue({
start_date: rulesHelper.getMonthStartDate(dateString),
end_date: rulesHelper.getMonthEndDate(dateString),
});
},
},
formItemProps: { ...rulesHelper.text },
},
{
key: 'start_date',
title: '计费开始日期',
valueType: 'date',
colProps: { span: 8 },
fieldProps: {
style: {
width: '100%',
},
},
formItemProps: { ...rulesHelper.text },
},
{
key: 'end_date',
title: '计费结束日期',
valueType: 'date',
colProps: { span: 8 },
fieldProps: {
style: {
width: '100%',
},
},
formItemProps: { ...rulesHelper.text },
},
{
valueType: 'dependency',
name: ['asset_projects_id'],
columns: ({ asset_projects_id }) => {
return [
{
valueType: 'group',
columns: [
Selects?.ProjectAccounts({
key: 'company_receipt_accounts_id',
title: '选择收款账户',
params: {
projects_id: asset_projects_id,
},
colProps: { span: 24 },
formItemProps: { ...rulesHelper.number },
fieldProps: {
showSearch: true,
},
}),
],
},
];
},
},
{
title: '备注',
key: 'remark',
valueType: 'textarea',
colProps: { span: 24 },
},
]}
/>
);
}

View File

@ -0,0 +1,124 @@
import {
MyBetaModalFormProps,
MyButtons,
MyFormItems,
MyModalFormProps,
rulesHelper,
} from '@/common';
import { Selects } from '@/components/Select';
import { Apis } from '@/gen/Apis';
import { BillPaymentsTypeEnum, BillsFlowTypeEnum } from '@/gen/Enums';
import { BetaSchemaForm } from '@ant-design/pro-components';
import { Form, message } from 'antd';
export default function Create(props: MyBetaModalFormProps) {
const [form] = Form.useForm();
return (
<BetaSchemaForm<ApiTypes.Bill.Bills.Store>
{...MyModalFormProps.props}
title={`手动添加账单`}
layout="horizontal"
labelCol={{ span: 4 }}
wrapperCol={{ span: 20 }}
// wrapperCol={{ span: 24 }}
width="600px"
trigger={<MyButtons.Create title={`手动添加账单`} />}
key={new Date().getTime()}
form={form}
onOpenChange={(open: any) => {
if (open) {
form.resetFields(); // 清空表单数据
}
}}
onFinish={async (values: any) =>
Apis.Bill.Bills.Store({
...values,
flow_type: BillsFlowTypeEnum.Income.value,
year: parseInt(values.year_month.split('-')[0]),
month: parseInt(values.year_month.split('-')[1]),
})
.then(() => {
props.reload?.();
message.success('手动添加账单成功');
return true;
})
.catch(() => false)
}
columns={[
Selects?.AssetProjects({
title: '选择项目',
key: 'asset_projects_id',
colProps: { span: 24 },
formItemProps: { ...rulesHelper.text },
}),
{
valueType: 'dependency',
name: ['asset_projects_id'],
columns: ({ asset_projects_id }) => {
return [
{
valueType: 'group',
columns: [
Selects?.ProjectAccounts({
key: 'company_receipt_accounts_id',
title: '收款账户',
params: {
projects_id: asset_projects_id,
},
colProps: { span: 24 },
formItemProps: { ...rulesHelper.number },
fieldProps: {
showSearch: true,
},
}),
],
},
];
},
},
MyFormItems.EnumRadio({
key: 'type',
title: '费用类型',
colProps: { span: 24 },
valueEnum: BillPaymentsTypeEnum,
required: true,
}),
{
key: 'amount',
title: '账单金额',
valueType: 'digit',
colProps: { span: 24 },
fieldProps: {
addonAfter: '元',
style: {
width: '100%',
},
},
formItemProps: { ...rulesHelper.number },
},
{
key: 'year_month',
title: '账单月份',
valueType: 'date',
colProps: { span: 24 },
fieldProps: {
picker: 'month',
format: 'YYYY-MM',
valueFormat: 'YYYY-MM',
style: {
width: '100%',
},
},
formItemProps: { ...rulesHelper.text },
},
{
title: '备注',
key: 'remark',
valueType: 'textarea',
colProps: { span: 24 },
},
]}
/>
);
}

View File

@ -0,0 +1,253 @@
import {
MyBetaModalFormProps,
MyButtons,
MyFormItems,
MyModalFormProps,
rulesHelper,
} from '@/common';
import { Selects } from '@/components/Select';
import { Apis } from '@/gen/Apis';
import { ApprovalTemplateNodesNodeTypeEnum } from '@/gen/Enums';
import { BetaSchemaForm } from '@ant-design/pro-components';
import { Form, message } from 'antd';
import { useState } from 'react';
export default function Update(props: MyBetaModalFormProps) {
const [form] = Form.useForm();
const [ApprovalTemplates, setApprovalTemplates] = useState<any>([]);
return (
<BetaSchemaForm<ApiTypes.Bill.BillRefunds.Store>
{...MyModalFormProps.props}
title={`退款申请单`}
trigger={
<MyButtons.Default
title="申请退款"
// disabled={props?.item?.bill_status !== 'Paid'}
/>
}
layout="horizontal"
labelCol={{ span: 4 }}
wrapperCol={{ span: 20 }}
labelAlign="left"
width="600px"
form={form}
key={new Date().getDate()}
onOpenChange={(open: any) => {
if (open && props.item) {
form.resetFields();
form.setFieldsValue({
...props.item,
});
}
}}
onFinish={async (values: any) =>
Apis.Refund.Refunds.Store({
...values,
business_id: props.item?.id ?? '',
type: 'HouseBill',
})
.then(() => {
props.reload?.();
message.success(props.title + '成功');
return true;
})
.catch(() => false)
}
columns={[
Selects?.CompanyAccounts({
key: 'company_receipt_accounts_id',
title: '付款账户',
colProps: { span: 24 },
formItemProps: { ...rulesHelper.number },
fieldProps: {
showSearch: true,
},
}),
{
key: 'refund_amount',
title: '退款金额',
valueType: 'digit',
colProps: { span: 24 },
fieldProps: {
style: {
width: '100%',
},
addonAfter: '元',
max: props.item?.total_paid_amount,
placeholder: `请输入退款金额(最大${props.item?.total_paid_amount}元)`,
},
formItemProps: { ...rulesHelper.number },
},
{
key: 'payee_name',
title: '收款人',
colProps: { span: 24 },
formItemProps: { ...rulesHelper.text },
},
{
key: 'payee_bank',
title: '收款银行',
colProps: { span: 24 },
formItemProps: { ...rulesHelper.text },
},
{
key: 'payee_account',
title: '收款账号',
colProps: { span: 24 },
formItemProps: { ...rulesHelper.text },
},
{
title: '退款原因',
key: 'remark',
valueType: 'textarea',
colProps: { span: 24 },
formItemProps: { ...rulesHelper.text },
},
Selects?.ApprovalTemplates({
key: 'approval_templates_id',
title: '审批模版',
params: { type: props.item?.approval_type || '', is_enabled: '1' },
colProps: { span: 24 },
formItemProps: { ...rulesHelper.number },
fieldProps: {
onChange: (e: any) => {
// 确保e是有效值并且使用正确的value字段
if (e) {
const templateId = typeof e === 'object' ? e.value : e;
Apis.Approval.ApprovalTemplates.Show({
id: templateId,
}).then((res) => {
setApprovalTemplates(res?.data?.approval_template_nodes);
form.setFieldsValue({
approval_templates_id: templateId,
node_approvers: res?.data?.approval_template_nodes?.map(
(item: any) => ({
...item,
members: item?.approval_template_node_members?.map(
(member: any) => member?.company_employees_id,
),
}),
),
});
});
}
// 不返回任何值,防止默认行为
},
},
}),
{
valueType: 'dependency',
name: ['approval_templates_id'],
columns: ({ approval_templates_id }) => {
return approval_templates_id
? [
{
valueType: 'formList',
dataIndex: 'node_approvers',
// title: '审批节点',
fieldProps: {
copyIconProps: false,
deleteIconProps: false,
},
formItemProps: {
...rulesHelper.array,
wrapperCol: { span: 24 },
},
columns: [
{
valueType: 'group',
colProps: { span: 24 },
columns: [
MyFormItems.EnumSelect({
key: 'node_type',
// title: `类型`,
valueEnum: ApprovalTemplateNodesNodeTypeEnum,
colProps: { span: 5 },
formItemProps: {
...rulesHelper.text,
},
fieldProps: {
disabled: true,
},
}),
{
// title: '节点名称',
key: 'name',
colProps: { span: 6 },
formItemProps: {
...rulesHelper.text,
},
fieldProps: {
disabled: true,
},
},
{
valueType: 'dependency',
name: ['node_type'],
columns: ({}) => {
return [
Selects.Employees({
key: 'members',
title: ``,
colProps: { span: 13 },
formItemProps: {
...rulesHelper.array,
},
fieldProps: {
mode: 'multiple',
showSearch: true,
// maxCount:
// node_type ===
// ApprovalTemplateNodesNodeTypeEnum.Approver
// .value
// ? 1
// : 9,
maxTagTextLength: 3,
labelRender: (res: any) => {
console.log(res, '222');
if (res?.label) {
return res?.label;
} else {
return ApprovalTemplates?.map(
(item: any) => {
if (
item
?.approval_template_node_members
?.length
) {
return item?.approval_template_node_members?.map(
(i: any) => {
if (
i?.company_employees_id ===
res?.value
) {
return (
i?.company_employee
?.name || ''
);
}
},
);
}
},
);
}
},
},
}),
];
},
},
],
},
],
},
]
: [];
},
},
]}
/>
);
}

View File

@ -0,0 +1,372 @@
import {
MyBetaModalFormProps,
MyButtons,
MyFormItems,
MyModalFormProps,
rulesHelper,
} from '@/common';
import { Selects } from '@/components/Select';
import { Apis } from '@/gen/Apis';
import {
ApprovalTemplateNodesNodeTypeEnum,
ApprovalTemplatesTypeEnum,
BillPaymentsTypeEnum,
BillsFlowTypeEnum,
BillsStatusEnum,
} from '@/gen/Enums';
import { BetaSchemaForm } from '@ant-design/pro-components';
import { Button, Form, message } from 'antd';
import { useState } from 'react';
export default function Update(props: MyBetaModalFormProps) {
const [form] = Form.useForm();
const [ApprovalTemplates, setApprovalTemplates] = useState<any>([]);
return (
<BetaSchemaForm<ApiTypes.Bill.Bills.Update>
{...MyModalFormProps.props}
title={`${props.title}编辑`}
trigger={
<MyButtons.Default
title="编辑"
type="primary"
size="small"
disabled={
props?.item?.status === BillsStatusEnum.Paid.value ||
props?.item?.status === BillsStatusEnum.Cancelled.value ||
props?.item?.status === BillsStatusEnum.UnderApproval.value ||
props?.item?.status === BillsStatusEnum.ToBeConfirmed.value
}
/>
}
layout="horizontal"
labelCol={{ span: 4 }}
wrapperCol={{ span: 20 }}
width="800px"
key={props.item?.id}
form={form}
onOpenChange={(open: any) => {
if (open && props.item) {
const formValues = {
...props.item,
company_receipt_accounts_id: props.item?.receipt_account?.id || [],
year_month:
props.item.year && props.item.month
? `${props.item.year}-${String(props.item.month).padStart(
2,
'0',
)}`
: props.item.month,
};
form.setFieldsValue(formValues);
}
}}
onFinish={async (values: any) => {
const { node_approvers } = values;
if (ApprovalTemplates?.length > 0 && node_approvers?.length > 0) {
// 遍历模板节点,通过名称找到对应的提交节点进行校验
for (const templateNode of ApprovalTemplates) {
// 通过名称找到对应的提交节点
const submittedNode = node_approvers.find(
(node: any) => node?.name === templateNode?.name,
);
// 如果模板节点在提交数据中不存在,说明被删除了,报错
if (!submittedNode) {
message.error(`节点"${templateNode?.name}"不能删除`);
return false;
}
// 校验审批人
if (
templateNode?.node_type ===
ApprovalTemplateNodesNodeTypeEnum.Approver.value
) {
const originalMembers =
templateNode?.approval_template_node_members?.map(
(member: any) => member?.company_employees_id,
);
if (originalMembers?.length > 0) {
const submittedMembers = submittedNode?.members || [];
const isMatch =
originalMembers.length === submittedMembers.length &&
originalMembers.every((memberId: number) =>
submittedMembers.includes(memberId),
);
if (!isMatch) {
message.error(
`节点"${templateNode?.name}"的审批人必须与审批模板设置保持一致`,
);
return false;
}
}
}
}
}
return Apis.Bill.Bills.Update({
...values,
id: props.item?.id ?? 0,
year: parseInt(values.year_month.split('-')[0]),
month: parseInt(values.year_month.split('-')[1]),
flow_type: BillsFlowTypeEnum.Income.value,
})
.then(() => {
props.reload?.();
message.success(props.title + '成功');
return true;
})
.catch(() => false);
}}
columns={[
{
valueType: 'dependency',
name: ['asset_projects_id'],
columns: ({ asset_projects_id }) => {
return [
{
valueType: 'group',
columns: [
Selects?.ProjectAccounts({
key: 'company_receipt_accounts_id',
title: '选择收款账户',
params: {
projects_id: asset_projects_id,
},
colProps: { span: 24 },
formItemProps: { ...rulesHelper.number },
fieldProps: {
showSearch: true,
},
}),
],
},
];
},
},
MyFormItems.EnumRadio({
key: 'type',
title: '类型',
colProps: { span: 24 },
valueEnum: BillPaymentsTypeEnum,
required: true,
}),
{
key: 'amount',
title: '金额',
valueType: 'digit',
colProps: { span: 24 },
fieldProps: {
style: {
width: '100%',
},
},
formItemProps: { ...rulesHelper.number },
},
{
key: 'year_month',
title: '账单月份',
valueType: 'date',
colProps: { span: 24 },
fieldProps: {
picker: 'month',
format: 'YYYY-MM',
valueFormat: 'YYYY-MM',
style: {
width: '100%',
},
},
formItemProps: { ...rulesHelper.text },
},
{
title: '备注',
key: 'remark',
valueType: 'textarea',
colProps: { span: 24 },
},
Selects?.ApprovalTemplates({
key: 'approval_templates_id',
title: '审批模版',
params: {
type: ApprovalTemplatesTypeEnum.BillUpdate.value,
is_enabled: '1',
},
colProps: { span: 24 },
formItemProps: { ...rulesHelper.number },
fieldProps: {
onChange: (e: any) => {
if (e) {
const templateId = typeof e === 'object' ? e.value : e;
Apis.Approval.ApprovalTemplates.Show({
id: templateId,
}).then((res) => {
setApprovalTemplates(res?.data?.approval_template_nodes);
form.setFieldsValue({
approval_templates_id: templateId,
node_approvers: res?.data?.approval_template_nodes?.map(
(item: any) => ({
...item,
members: item?.approval_template_node_members?.map(
(member: any) => member?.company_employees_id,
),
}),
),
});
});
}
},
},
}),
{
valueType: 'dependency',
name: ['approval_templates_id'],
columns: ({ approval_templates_id }) => {
return approval_templates_id
? [
{
valueType: 'formList',
dataIndex: 'node_approvers',
fieldProps: {
creatorButtonProps: false,
actionRender: (field: any, action: any) => [
<Button
key="add"
type="link"
size="small"
onClick={() => {
const currentNodeType = form.getFieldValue([
'node_approvers',
field.name,
'node_type',
]);
if (
currentNodeType ===
ApprovalTemplateNodesNodeTypeEnum.CC.value
) {
message.warning('抄送节点不允许添加');
return;
}
action.add(
{
node_type:
ApprovalTemplateNodesNodeTypeEnum.Approver
.value,
},
field.name + 1,
);
}}
>
</Button>,
<Button
key="delete"
type="link"
size="small"
danger
onClick={() => {
action.remove(field.name);
}}
>
</Button>,
],
},
formItemProps: {
...rulesHelper.array,
wrapperCol: { span: 24 },
},
columns: [
{
valueType: 'group',
colProps: { span: 24 },
columns: [
MyFormItems.EnumSelect({
key: 'node_type',
valueEnum: ApprovalTemplateNodesNodeTypeEnum,
colProps: { span: 5 },
formItemProps: {
...rulesHelper.text,
},
}),
{
key: 'name',
colProps: { span: 6 },
formItemProps: {
...rulesHelper.text,
},
},
{
valueType: 'dependency',
name: ['node_type'],
columns: ({ node_type }) => {
return [
Selects.Employees({
key: 'members',
title: ``,
colProps: { span: 13 },
formItemProps: {
...rulesHelper.array,
},
fieldProps: {
mode: 'multiple',
showSearch: true,
maxCount:
node_type ===
ApprovalTemplateNodesNodeTypeEnum.Approver
.value
? 1
: 9,
maxTagTextLength: 20,
labelRender: (res: any) => {
if (res?.label) {
return res?.label;
} else {
return ApprovalTemplates?.map(
(item: any) => {
if (
item?.node_type === node_type &&
item
?.approval_template_node_members
?.length
) {
return item?.approval_template_node_members?.map(
(i: any) => {
if (
i?.company_employees_id ===
res?.value
) {
return (
i?.company_employee
?.name || ''
);
}
},
);
}
},
);
}
},
},
}),
];
},
},
],
},
],
},
]
: [];
},
},
]}
/>
);
}

View File

@ -0,0 +1,153 @@
import {
MyBetaModalFormProps,
MyButtons,
MyFormItems,
MyModalFormProps,
rulesHelper,
} from '@/common';
import { Apis } from '@/gen/Apis';
import { BillsStatusEnum, HouseOrdersPaymentMethodEnum } from '@/gen/Enums';
import { BetaSchemaForm } from '@ant-design/pro-components';
import { Form, message } from 'antd';
import * as React from 'react';
export interface PayCreateProps extends MyBetaModalFormProps {
// 单个账单收款,不需要选择多个账单
}
export default function PayCreate(props: PayCreateProps): React.ReactElement {
const [form] = Form.useForm();
return (
<BetaSchemaForm<ApiTypes.Bill.Bills.OfflinePayment>
{...MyModalFormProps.props}
title="录入收款信息"
trigger={
<MyButtons.Default
type="primary"
size="small"
title="线下收款"
disabled={
props?.item?.status === BillsStatusEnum.Paid.value ||
props?.item?.status === BillsStatusEnum.Cancelled.value ||
props?.item?.status === BillsStatusEnum.UnderApproval.value ||
props?.item?.status === BillsStatusEnum.ToBeConfirmed.value
}
/>
}
labelAlign="left"
width="800px"
form={form}
key={new Date().getTime()}
onOpenChange={(open: boolean): void => {
if (open) {
form.resetFields(); // 清空表单数据
form.setFieldsValue({
item: props.item,
});
}
}}
onFinish={async (values: any) =>
Apis.Bill.Bills.OfflinePayment({
...values,
total_paid_amount: parseFloat(props?.item?.payable_amount || 0),
id: props.item?.id || '',
})
.then(() => {
props.reload?.();
message.success('手动添加账单成功');
return true;
})
.catch(() => false)
}
columns={[
{
valueType: 'divider',
fieldProps: {
orientation: 'left',
children: '账单信息',
},
},
{
colProps: { span: 24 },
renderFormItem: () => (
<div
style={{
padding: '12px',
backgroundColor: '#f5f5f5',
borderRadius: '6px',
}}
>
<p>ID{props.item?.id || '-'}</p>
<p>
{props.item?.asset_project?.name || '-'}
</p>
<p>
¥ {parseFloat(props?.item?.payable_amount || 0)}
</p>
<p>{props.item?.remark || '-'}</p>
</div>
),
},
MyFormItems.EnumRadio({
key: 'payment_method',
title: '收款方式',
colProps: { span: 12 },
valueEnum: () => {
const obj: Record<string, any> = JSON.parse(
JSON.stringify(HouseOrdersPaymentMethodEnum),
);
delete obj.WeChat;
delete obj.Alipay;
delete obj.TongLian;
delete obj.Prepayment;
delete obj.CCB;
return obj;
},
required: true,
}),
// {
// key: 'total_paid_amount',
// title: '收款金额',
// valueType: 'digit',
// colProps: { span: 6 },
// fieldProps: {
// style: { width: '100%' },
// addonAfter: '元',
// disabled: true,
// },
// formItemProps: { ...rulesHelper.number },
// },
{
key: 'paid_time',
title: '收款日期',
valueType: 'date',
formItemProps: { ...rulesHelper.text },
colProps: { span: 8 },
},
{
title: '收款备注',
dataIndex: 'remark',
colProps: { span: 24 },
},
MyFormItems.UploadImages({
key: 'pay_certificate',
title: '收款凭证图片',
uploadType: 'file',
colProps: { span: 24 },
max: 10,
}),
{
colProps: { span: 24 },
renderFormItem: () => (
<span style={{ color: '#5b5b5bff' }}>
</span>
),
},
]}
/>
);
}

View File

@ -0,0 +1,174 @@
import {
MyBetaModalFormProps,
MyButtons,
MyModalFormProps,
renderTextHelper,
} from '@/common';
import { Apis } from '@/gen/Apis';
import { BillPaymentsTypeEnum, BillsStatusEnum } from '@/gen/Enums';
import { BetaSchemaForm, ProDescriptions } from '@ant-design/pro-components';
import { Form, QRCode, Tabs } from 'antd';
import { useState } from 'react';
export default function ShowQrCode(props: MyBetaModalFormProps) {
const [form] = Form.useForm();
const [activeTab, setActiveTab] = useState<string>('wechat');
const [wechatQrCode, setWechatQrCode] = useState<string>('');
const [alipayQrCode, setAlipayQrCode] = useState<string>('');
const fetchQrCode = (type: 'wechat' | 'alipay') => {
if (!props?.item?.id) return;
const params = {
id: props.item.id,
};
if (type === 'wechat') {
Apis.Bill.Bills.GetPayCode(params).then((res: any) => {
setWechatQrCode(res?.data?.qr_code || '');
});
} else {
Apis.Bill.Bills.AlipayQrCode(params).then((res: any) => {
setAlipayQrCode(res?.data?.payinfo || '');
});
}
};
return (
<BetaSchemaForm<ApiTypes.Bill.Bills.GetPayCode>
{...MyModalFormProps.props}
title={props.title}
width="500px"
wrapperCol={{ span: 24 }}
trigger={
<MyButtons.Default
type="primary"
size="small"
title={props.title}
disabled={
props?.item?.status === BillsStatusEnum.Paid.value ||
props?.item?.status === BillsStatusEnum.Cancelled.value ||
props?.item?.status === BillsStatusEnum.UnderApproval.value ||
props?.item?.status === BillsStatusEnum.ToBeConfirmed.value
}
/>
}
form={form}
submitter={false}
onOpenChange={(open: any) => {
if (open) {
setWechatQrCode('');
setAlipayQrCode('');
setActiveTab('wechat');
fetchQrCode('wechat');
}
}}
columns={[
{
title: '',
colProps: { span: 24 },
key: 'qrcode',
renderFormItem() {
return (
<div style={{ textAlign: 'left' }}>
<ProDescriptions
column={2}
size="small"
bordered
style={{ marginBottom: '16px' }}
>
<ProDescriptions.Item label="项目" span={2}>
{props?.item?.asset_project?.name || '-'}
</ProDescriptions.Item>
<ProDescriptions.Item label="费用类型">
<renderTextHelper.Tag
Enums={BillPaymentsTypeEnum}
value={props?.item?.type}
key="type"
/>
</ProDescriptions.Item>
<ProDescriptions.Item label="支付金额">
¥{props?.item?.amount || 0}
</ProDescriptions.Item>
<ProDescriptions.Item label="说明">
{props?.item?.remark || '-'}
</ProDescriptions.Item>
</ProDescriptions>
<Tabs
activeKey={activeTab}
onChange={(key) => {
setActiveTab(key);
fetchQrCode(key as 'wechat' | 'alipay');
}}
items={[
{
key: 'wechat',
label: '微信支付',
children: (
<div
style={{
display: 'flex',
padding: '20px 0',
justifyContent: 'center',
}}
>
{wechatQrCode ? (
<img
style={{ width: '220px', height: '220px' }}
src={wechatQrCode}
alt="微信支付二维码"
/>
) : (
<div
style={{
width: '220px',
height: '220px',
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
}}
>
...
</div>
)}
</div>
),
},
{
key: 'alipay',
label: '支付宝',
children: (
<div
style={{
display: 'flex',
padding: '20px 0',
justifyContent: 'center',
}}
>
{alipayQrCode ? (
<QRCode value={alipayQrCode} size={220} />
) : (
<div
style={{
width: '220px',
height: '220px',
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
}}
>
...
</div>
)}
</div>
),
},
]}
/>
</div>
);
},
},
]}
/>
);
}

View File

@ -0,0 +1,84 @@
import {
MyBetaModalFormProps,
MyButtons,
MyFormItems,
MyModalFormProps,
rulesHelper,
} from '@/common';
import { Apis } from '@/gen/Apis';
import { BetaSchemaForm } from '@ant-design/pro-components';
import { Form, message } from 'antd';
export default function Create(props: MyBetaModalFormProps) {
const [form] = Form.useForm();
return (
<BetaSchemaForm<ApiTypes.Refund.Refunds.Complete>
{...MyModalFormProps.props}
title={`添加${props.title}`}
wrapperCol={{ span: 24 }}
width="700px"
key={new Date().getTime()}
form={form}
onOpenChange={(open: any) => {
if (open) {
form.resetFields(); // 清空表单数据
}
}}
trigger={
<MyButtons.Default
type="primary"
size="small"
title="退款登记"
disabled={!props.item?.has_refunding}
/>
}
onFinish={async (values) =>
Apis.Refund.Refunds.Complete({
...values,
id: props.item?.id ?? '',
})
.then(() => {
props.reload?.();
message.success(props.title + '成功');
return true;
})
.catch(() => false)
}
columns={[
// Selects?.CompanyAccounts({
// key: 'company_receipt_accounts_id',
// title: '付款账户',
// colProps: { span: 24 },
// formItemProps: { ...rulesHelper.number },
// fieldProps: {
// showSearch: true,
// },
// }),
{
key: 'refund_time',
title: '退款日期',
valueType: 'date',
formItemProps: { ...rulesHelper.text },
colProps: { span: 24 },
},
{
key: 'serial_number',
title: '退款流水号',
formItemProps: { ...rulesHelper.text },
colProps: { span: 24 },
},
MyFormItems.UploadImages({
key: 'voucher',
title: '上传退款凭证',
tooltip: '支持上传任意格式的文件',
uploadType: 'file',
colProps: { span: 24 },
// accept: '.docx,.doc,.pdf',
max: 100,
required: true,
}),
]}
/>
);
}

View File

@ -267,6 +267,8 @@ export default function Index({ title = '账单生成' }) {
>
<Select
style={{ width: '100%' }}
showSearch
optionFilterProp="children"
value={selectedProject || undefined}
onChange={(value) => {
setSelectedProject(value);
@ -291,6 +293,8 @@ export default function Index({ title = '账单生成' }) {
>
<Select
style={{ width: '100%' }}
showSearch
optionFilterProp="children"
value={selectedStandard || undefined}
onChange={(value, option) => {
console.log(value, option);

View File

@ -9,7 +9,6 @@ import { Apis } from '@/gen/Apis';
import {
HouseOccupantsCardTypeEnum,
HouseOccupantsHouseRelationEnum,
HouseRegistersCustomerTypeEnum,
} from '@/gen/Enums';
import { BetaSchemaForm, ProCard } from '@ant-design/pro-components';
import { Form, message } from 'antd';
@ -37,6 +36,14 @@ export default function Create(props: MyBetaModalFormProps) {
...values,
asset_houses_id: props?.item?.id,
type: 'Transfer',
customer_info: values.customer_info?.map((res: any) => {
return {
...res,
house_relation: 'Owner',
relation_with_owner: 'Self',
residential_relation: 'PropertyOwner',
};
}),
})
.then(() => {
props.reload?.();
@ -60,12 +67,12 @@ export default function Create(props: MyBetaModalFormProps) {
// valueEnum: HouseRegistersUsagePlanEnum,
// formItemProps: { ...rulesHelper.text },
// }),
MyFormItems.EnumSelect({
key: 'customer_type',
title: '产权归属',
colProps: { span: 6 },
valueEnum: HouseRegistersCustomerTypeEnum,
}),
// MyFormItems.EnumSelect({
// key: 'customer_type',
// title: '产权归属',
// colProps: { span: 6 },
// valueEnum: HouseRegistersCustomerTypeEnum,
// }),
MyFormItems.UploadImages({
key: 'ownership_info',
title: '产权文件',
@ -153,6 +160,7 @@ export default function Create(props: MyBetaModalFormProps) {
key: 'card_front_image',
title: '证件正面',
uploadType: 'file',
required: true,
max: 1,
colProps: { span: 6 },
}),
@ -160,6 +168,7 @@ export default function Create(props: MyBetaModalFormProps) {
key: 'card_back_image',
title: '证件反面',
uploadType: 'file',
required: true,
max: 1,
colProps: { span: 6 },
}),

View File

@ -37,6 +37,7 @@ export default function Index({ title = '登记审核' }) {
HouseRegistersTypeEnum.MoveIn.value,
HouseRegistersTypeEnum.UpdateInfo.value,
HouseRegistersTypeEnum.UpdatePhone.value,
HouseRegistersTypeEnum.Transfer.value,
],
},
sort,

View File

@ -33,7 +33,7 @@ export default function Create(props: MyBetaModalFormProps) {
});
}
}}
onFinish={async (values) =>
onFinish={async (values: any) =>
Apis.Banner.Banners.Store(values)
.then(() => {
props.reload?.();

View File

@ -6,9 +6,10 @@ import {
MyTableActions,
} from '@/common';
import { Apis } from '@/gen/Apis';
import { VisitorAppliesStatusEnum } from '@/gen/Enums';
import { ProTable } from '@ant-design/pro-components';
import { message, Space } from 'antd';
import Review from './modals/Review';
import Reject from './modals/Reject';
export default function Index({ title = '访客预约' }) {
return (
@ -35,7 +36,7 @@ export default function Index({ title = '访客预约' }) {
MyColumns.EnumTag({
title: '申请状态',
dataIndex: 'status',
// valueEnum: VisitorAppliesStatusEnum,
valueEnum: VisitorAppliesStatusEnum,
}),
{
title: '房屋',
@ -74,7 +75,8 @@ export default function Index({ title = '访客预约' }) {
render: (_, item: any) => {
return (
<Space>
{item?.visit_start_time}{item?.code_expired_at}
{item?.visit_start_time?.slice(0, 10)}
{item?.code_expired_at?.slice(0, 10)}
</Space>
);
},
@ -117,8 +119,8 @@ export default function Index({ title = '访客预约' }) {
}}
/>
),
view: (
<Review item={item} reload={action?.reload} title={title} />
reject: (
<Reject item={item} reload={action?.reload} title={title} />
),
}}
/>

View File

@ -8,7 +8,7 @@ import { Apis } from '@/gen/Apis';
import { BetaSchemaForm } from '@ant-design/pro-components';
import { message } from 'antd';
export default function Update(props: MyBetaModalFormProps) {
export default function Reject(props: MyBetaModalFormProps) {
return (
<BetaSchemaForm<ApiTypes.Visitor.VisitorApplies.Reject>
{...MyModalFormProps.props}