develop #6
@ -17,8 +17,8 @@ export default defineConfig({
|
|||||||
},
|
},
|
||||||
proxy: {
|
proxy: {
|
||||||
'/api/': {
|
'/api/': {
|
||||||
// target: 'http://10.39.13.78:8001/',
|
target: 'http://10.39.13.78:8001/',
|
||||||
target: 'https://test-company.linyikj.com.cn/',
|
// target: 'https://test-company.linyikj.com.cn/',
|
||||||
// target: 'https://company.linyikj.com.cn/',
|
// target: 'https://company.linyikj.com.cn/',
|
||||||
changeOrigin: true,
|
changeOrigin: true,
|
||||||
pathRewrite: { '^': '' },
|
pathRewrite: { '^': '' },
|
||||||
|
|||||||
@ -49,3 +49,7 @@ export function GetFromNow(time: string) {
|
|||||||
//获取时间距离现在的时间
|
//获取时间距离现在的时间
|
||||||
return time ? dayjs(time).fromNow() : '';
|
return time ? dayjs(time).fromNow() : '';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function getTodayDate(): string {
|
||||||
|
return dayjs().format('YYYY-MM-DD');
|
||||||
|
}
|
||||||
|
|||||||
2
src/common/utils/mapConfig.ts
Normal file
2
src/common/utils/mapConfig.ts
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
export const mapKey = '4ce26ecef55ae1ec47910a72a098efc0';
|
||||||
|
// export const mapKey = '35fe6f4443fddf86cdcca3b97fd8c872';
|
||||||
109
src/components/ModalsAssetsProjectSelectList.tsx
Normal file
109
src/components/ModalsAssetsProjectSelectList.tsx
Normal file
@ -0,0 +1,109 @@
|
|||||||
|
import {
|
||||||
|
MyBetaModalFormProps,
|
||||||
|
MyButtons,
|
||||||
|
MyColumns,
|
||||||
|
MyProTableProps,
|
||||||
|
} from '@/common';
|
||||||
|
import { MyModal } from '@/components/MyModal';
|
||||||
|
import { Apis } from '@/gen/Apis';
|
||||||
|
import { AssetProjectsPropertyTypeEnum } from '@/gen/Enums';
|
||||||
|
import { ProTable } from '@ant-design/pro-components';
|
||||||
|
import { useRef, useState } from 'react';
|
||||||
|
interface DataType {
|
||||||
|
key?: React.Key;
|
||||||
|
id?: React.Key;
|
||||||
|
}
|
||||||
|
export default function SurveysSelectList(
|
||||||
|
props: MyBetaModalFormProps & {
|
||||||
|
onChange?: (selectedRows: DataType[]) => void;
|
||||||
|
type?: 'checkbox' | 'radio';
|
||||||
|
},
|
||||||
|
) {
|
||||||
|
const modalRef = useRef<any>();
|
||||||
|
// const [selectedDataRow, setSelectedDataRow] = useState<any>({});
|
||||||
|
const [getSelectedRow, setSelectedRow] = useState<any>([]);
|
||||||
|
const rowSelection: any = {
|
||||||
|
onChange: (selectedRowKeys: React.Key[], selectedRows: DataType[]) => {
|
||||||
|
console.log(selectedRows, 'selectedRows[0]');
|
||||||
|
setSelectedRow(selectedRows);
|
||||||
|
},
|
||||||
|
getCheckboxProps: (record: any) => ({
|
||||||
|
disabled: record.deleted_at,
|
||||||
|
checked: props?.item?.some((item: any) => {
|
||||||
|
console.log(item, record);
|
||||||
|
return item?.id === record?.id;
|
||||||
|
}),
|
||||||
|
}),
|
||||||
|
defaultSelectedRowKeys: props?.item?.map((item: any) => item?.id) || [],
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<MyModal
|
||||||
|
title={'选择关联项目'}
|
||||||
|
width="1000px"
|
||||||
|
myRef={modalRef}
|
||||||
|
size="middle"
|
||||||
|
onOpen={() => {
|
||||||
|
setSelectedRow(props?.item);
|
||||||
|
console.log(props?.item, 'props?.item?.id');
|
||||||
|
}}
|
||||||
|
node={
|
||||||
|
<ProTable
|
||||||
|
{...MyProTableProps.props}
|
||||||
|
request={async (params, sort) =>
|
||||||
|
MyProTableProps.request(params, sort, Apis.Asset.AssetProjects.List)
|
||||||
|
}
|
||||||
|
rowSelection={{
|
||||||
|
type: props?.type ? props?.type : 'checkbox',
|
||||||
|
...rowSelection,
|
||||||
|
}}
|
||||||
|
options={false}
|
||||||
|
tableAlertOptionRender={() => {
|
||||||
|
return (
|
||||||
|
<MyButtons.Default
|
||||||
|
key="okSelect"
|
||||||
|
size="middle"
|
||||||
|
type="primary"
|
||||||
|
onClick={() => {
|
||||||
|
let res: any = getSelectedRow;
|
||||||
|
props?.onChange?.(res);
|
||||||
|
modalRef.current?.close();
|
||||||
|
}}
|
||||||
|
title="确定选项"
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}}
|
||||||
|
columns={[
|
||||||
|
MyColumns.ID({
|
||||||
|
search: false,
|
||||||
|
}),
|
||||||
|
{
|
||||||
|
title: '项目名称',
|
||||||
|
dataIndex: 'name',
|
||||||
|
},
|
||||||
|
MyColumns.EnumTag({
|
||||||
|
title: '类型',
|
||||||
|
dataIndex: 'property_type',
|
||||||
|
valueEnum: AssetProjectsPropertyTypeEnum,
|
||||||
|
search: false,
|
||||||
|
}),
|
||||||
|
{
|
||||||
|
title: '地址',
|
||||||
|
render: (_, i: any) => {
|
||||||
|
return `${i?.province || ''} ${i?.city || ''} ${
|
||||||
|
i?.district || ''
|
||||||
|
}${i?.address || ''}`;
|
||||||
|
},
|
||||||
|
search: false,
|
||||||
|
},
|
||||||
|
MyColumns.DeletedAt({
|
||||||
|
title: '启/禁用',
|
||||||
|
dataIndex: 'deleted_at',
|
||||||
|
search: false,
|
||||||
|
}),
|
||||||
|
]}
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
></MyModal>
|
||||||
|
);
|
||||||
|
}
|
||||||
@ -75,6 +75,7 @@ export const SysSelects = {
|
|||||||
valueType: 'select',
|
valueType: 'select',
|
||||||
hideInTable: hideInTable,
|
hideInTable: hideInTable,
|
||||||
formItemProps: { ...(required ? rulesHelper.number : {}) },
|
formItemProps: { ...(required ? rulesHelper.number : {}) },
|
||||||
|
...rest,
|
||||||
fieldProps: {
|
fieldProps: {
|
||||||
mode: 'multiple',
|
mode: 'multiple',
|
||||||
showSearch: false,
|
showSearch: false,
|
||||||
@ -82,9 +83,37 @@ export const SysSelects = {
|
|||||||
label: 'name',
|
label: 'name',
|
||||||
value: 'id',
|
value: 'id',
|
||||||
},
|
},
|
||||||
|
...rest?.fieldProps,
|
||||||
},
|
},
|
||||||
request: async () => (await Apis.Permission.Roles.Select()).data,
|
request: async () => (await Apis.Permission.Roles.Select()).data,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
SysEmployeeRoles(props?: PropsType): ReturnType {
|
||||||
|
const {
|
||||||
|
title = '员工角色',
|
||||||
|
key = 'employee_roles_id',
|
||||||
|
required = true,
|
||||||
|
hideInTable = true,
|
||||||
|
...rest
|
||||||
|
} = props ?? {};
|
||||||
|
|
||||||
|
return {
|
||||||
|
title: title,
|
||||||
|
key: key,
|
||||||
|
valueType: 'select',
|
||||||
|
hideInTable: hideInTable,
|
||||||
|
formItemProps: { ...(required ? rulesHelper.number : {}) },
|
||||||
...rest,
|
...rest,
|
||||||
|
fieldProps: {
|
||||||
|
mode: 'multiple',
|
||||||
|
showSearch: false,
|
||||||
|
fieldNames: {
|
||||||
|
label: 'name',
|
||||||
|
value: 'id',
|
||||||
|
},
|
||||||
|
...rest?.fieldProps,
|
||||||
|
},
|
||||||
|
request: async () => (await Apis.Company.EmployeeRoles.Select()).data,
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|||||||
23
src/gen/ApiTypes.d.ts
vendored
23
src/gen/ApiTypes.d.ts
vendored
@ -1138,6 +1138,28 @@ declare namespace ApiTypes {
|
|||||||
"company_name"?: string; // 模糊搜索:名称
|
"company_name"?: string; // 模糊搜索:名称
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
namespace EmployeeRoles {
|
||||||
|
type List = {
|
||||||
|
"name"?: string; // 模糊搜索:名称
|
||||||
|
};
|
||||||
|
type Store = {
|
||||||
|
"name": string; // 角色名称
|
||||||
|
};
|
||||||
|
type Update = {
|
||||||
|
"id": number; // ID
|
||||||
|
"name": string; // 角色名称
|
||||||
|
};
|
||||||
|
type Delete = {
|
||||||
|
"id": number; // ID
|
||||||
|
};
|
||||||
|
type GetPermissions = {
|
||||||
|
"id": number; // 角色ID
|
||||||
|
};
|
||||||
|
type SetPermissions = {
|
||||||
|
"id": number; // ID
|
||||||
|
"permissions_ids": string[]; // 权限ID
|
||||||
|
};
|
||||||
|
}
|
||||||
namespace OrganizationProjects {
|
namespace OrganizationProjects {
|
||||||
type List = {
|
type List = {
|
||||||
"organizations_id"?: number; // 组织id,[ref:organizations]
|
"organizations_id"?: number; // 组织id,[ref:organizations]
|
||||||
@ -1882,6 +1904,7 @@ declare namespace ApiTypes {
|
|||||||
namespace Roles {
|
namespace Roles {
|
||||||
type List = {
|
type List = {
|
||||||
"name"?: string; // 模糊搜索:名称
|
"name"?: string; // 模糊搜索:名称
|
||||||
|
"guard_name"?: string; // 角色类型
|
||||||
};
|
};
|
||||||
type Store = {
|
type Store = {
|
||||||
"name": string; // 角色名称
|
"name": string; // 角色名称
|
||||||
|
|||||||
@ -633,6 +633,32 @@ export const Apis = {
|
|||||||
return request('company/company/company_receipt_accounts/select', { data });
|
return request('company/company/company_receipt_accounts/select', { data });
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
EmployeeRoles: {
|
||||||
|
List(data?: ApiTypes.Company.EmployeeRoles.List): Promise<MyResponseType> {
|
||||||
|
return request('company/company/employee_roles/list', { data });
|
||||||
|
},
|
||||||
|
Store(data: ApiTypes.Company.EmployeeRoles.Store): Promise<MyResponseType> {
|
||||||
|
return request('company/company/employee_roles/store', { data });
|
||||||
|
},
|
||||||
|
Update(data: ApiTypes.Company.EmployeeRoles.Update): Promise<MyResponseType> {
|
||||||
|
return request('company/company/employee_roles/update', { data });
|
||||||
|
},
|
||||||
|
Delete(data: ApiTypes.Company.EmployeeRoles.Delete): Promise<MyResponseType> {
|
||||||
|
return request('company/company/employee_roles/delete', { data });
|
||||||
|
},
|
||||||
|
Select(): Promise<MyResponseType> {
|
||||||
|
return request('company/company/employee_roles/select', {});
|
||||||
|
},
|
||||||
|
GetPermissions(data: ApiTypes.Company.EmployeeRoles.GetPermissions): Promise<MyResponseType> {
|
||||||
|
return request('company/company/employee_roles/get_permissions', { data });
|
||||||
|
},
|
||||||
|
SetPermissions(data: ApiTypes.Company.EmployeeRoles.SetPermissions): Promise<MyResponseType> {
|
||||||
|
return request('company/company/employee_roles/set_permissions', { data });
|
||||||
|
},
|
||||||
|
PermissionTree(): Promise<MyResponseType> {
|
||||||
|
return request('company/company/employee_roles/permission_tree', {});
|
||||||
|
},
|
||||||
|
},
|
||||||
OrganizationProjects: {
|
OrganizationProjects: {
|
||||||
List(data?: ApiTypes.Company.OrganizationProjects.List): Promise<MyResponseType> {
|
List(data?: ApiTypes.Company.OrganizationProjects.List): Promise<MyResponseType> {
|
||||||
return request('company/company/organization_projects/list', { data });
|
return request('company/company/organization_projects/list', { data });
|
||||||
|
|||||||
1374
src/gen/Enums.ts
1374
src/gen/Enums.ts
File diff suppressed because it is too large
Load Diff
68
src/pages/attendance/attendance_configs/index.tsx
Normal file
68
src/pages/attendance/attendance_configs/index.tsx
Normal file
@ -0,0 +1,68 @@
|
|||||||
|
import { MyColumns, MyPageContainer, MyProTableProps } from '@/common';
|
||||||
|
import { Apis } from '@/gen/Apis';
|
||||||
|
import { ProTable } from '@ant-design/pro-components';
|
||||||
|
import { Space } from 'antd';
|
||||||
|
import Create from './modals/Create';
|
||||||
|
import Update from './modals/Update';
|
||||||
|
|
||||||
|
export default function Index({ title = '打卡配置' }) {
|
||||||
|
return (
|
||||||
|
<MyPageContainer
|
||||||
|
title={title}
|
||||||
|
enableTabs={true}
|
||||||
|
tabKey="attendance_configs"
|
||||||
|
tabLabel={title}
|
||||||
|
>
|
||||||
|
<ProTable
|
||||||
|
{...MyProTableProps.props}
|
||||||
|
// search={false}
|
||||||
|
request={async (params, sort) =>
|
||||||
|
MyProTableProps.request(
|
||||||
|
params,
|
||||||
|
sort,
|
||||||
|
Apis.Attendance.AttendanceConfigs.List,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
headerTitle="打卡参数配置"
|
||||||
|
toolBarRender={(action) => [
|
||||||
|
<Create key="Create" reload={action?.reload} title={title} />,
|
||||||
|
]}
|
||||||
|
search={false}
|
||||||
|
columns={[
|
||||||
|
MyColumns.ID({
|
||||||
|
search: false,
|
||||||
|
}),
|
||||||
|
{
|
||||||
|
title: '可打卡范围(米内)',
|
||||||
|
dataIndex: 'check_in_range',
|
||||||
|
search: false,
|
||||||
|
},
|
||||||
|
MyColumns.Boolean({
|
||||||
|
dataIndex: 'require_photo',
|
||||||
|
title: '是否要求拍照打卡',
|
||||||
|
search: false,
|
||||||
|
}),
|
||||||
|
// MyColumns.Boolean({
|
||||||
|
// dataIndex: 'allow_out_range_checkin',
|
||||||
|
// title: '是否允许范围外打卡',
|
||||||
|
// search: false,
|
||||||
|
// }),
|
||||||
|
// MyColumns.IsEnabled({
|
||||||
|
// onRestore: Apis.Attendance.AttendanceConfigs.Enable,
|
||||||
|
// onSoftDelete: Apis.Attendance.AttendanceConfigs.Enable,
|
||||||
|
// search: false,
|
||||||
|
// }),
|
||||||
|
// MyColumns.CreatedAt(),
|
||||||
|
MyColumns.UpdatedAt(),
|
||||||
|
MyColumns.Option({
|
||||||
|
render: (_, item: any, index, action) => (
|
||||||
|
<Space key={index}>
|
||||||
|
<Update item={item} reload={action?.reload} title={title} />
|
||||||
|
</Space>
|
||||||
|
),
|
||||||
|
}),
|
||||||
|
]}
|
||||||
|
/>
|
||||||
|
</MyPageContainer>
|
||||||
|
);
|
||||||
|
}
|
||||||
63
src/pages/attendance/attendance_configs/modals/Create.tsx
Normal file
63
src/pages/attendance/attendance_configs/modals/Create.tsx
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
import {
|
||||||
|
MyBetaModalFormProps,
|
||||||
|
MyButtons,
|
||||||
|
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.Attendance.AttendanceConfigs.Store>
|
||||||
|
{...MyModalFormProps.props}
|
||||||
|
title={`添加${props.title}`}
|
||||||
|
wrapperCol={{ span: 24 }}
|
||||||
|
width="500px"
|
||||||
|
key={new Date().getTime()}
|
||||||
|
form={form}
|
||||||
|
onOpenChange={(open: any) => {
|
||||||
|
if (open) {
|
||||||
|
form.resetFields(); // 清空表单数据
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
trigger={<MyButtons.Create title={`配置`} />}
|
||||||
|
onFinish={async (values) =>
|
||||||
|
Apis.Attendance.AttendanceConfigs.Store(values)
|
||||||
|
.then(() => {
|
||||||
|
props.reload?.();
|
||||||
|
message.success(props.title + '成功');
|
||||||
|
return true;
|
||||||
|
})
|
||||||
|
.catch(() => false)
|
||||||
|
}
|
||||||
|
columns={[
|
||||||
|
{
|
||||||
|
key: 'check_in_range',
|
||||||
|
title: '可打卡范围',
|
||||||
|
valueType: 'digit',
|
||||||
|
fieldProps: {
|
||||||
|
suffix: '米内',
|
||||||
|
style: { width: '100%' },
|
||||||
|
},
|
||||||
|
formItemProps: { ...rulesHelper.number },
|
||||||
|
colProps: { span: 24 },
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '是否要求拍照打卡',
|
||||||
|
key: 'require_photo',
|
||||||
|
valueType: 'switch',
|
||||||
|
colProps: { span: 12 },
|
||||||
|
},
|
||||||
|
// {
|
||||||
|
// title: '是否允许范围外打卡',
|
||||||
|
// key: 'allow_out_range_checkin',
|
||||||
|
// valueType: 'switch',
|
||||||
|
// colProps: { span: 12 },
|
||||||
|
// },
|
||||||
|
]}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
66
src/pages/attendance/attendance_configs/modals/Update.tsx
Normal file
66
src/pages/attendance/attendance_configs/modals/Update.tsx
Normal file
@ -0,0 +1,66 @@
|
|||||||
|
import {
|
||||||
|
MyBetaModalFormProps,
|
||||||
|
MyButtons,
|
||||||
|
MyModalFormProps,
|
||||||
|
rulesHelper,
|
||||||
|
} from '@/common';
|
||||||
|
import { Apis } from '@/gen/Apis';
|
||||||
|
import { BetaSchemaForm } from '@ant-design/pro-components';
|
||||||
|
import { Form, message } from 'antd';
|
||||||
|
|
||||||
|
export default function Update(props: MyBetaModalFormProps) {
|
||||||
|
const [form] = Form.useForm();
|
||||||
|
return (
|
||||||
|
<BetaSchemaForm<ApiTypes.Attendance.AttendanceConfigs.UpdateConfig>
|
||||||
|
{...MyModalFormProps.props}
|
||||||
|
title={`打卡配置`}
|
||||||
|
trigger={<MyButtons.Edit title={`修改`} />}
|
||||||
|
wrapperCol={{ span: 24 }}
|
||||||
|
key={new Date().getTime()}
|
||||||
|
width="500px"
|
||||||
|
form={form}
|
||||||
|
onOpenChange={(open: any) => {
|
||||||
|
if (open && props.item) {
|
||||||
|
form.setFieldsValue(props.item);
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
onFinish={async (values) =>
|
||||||
|
Apis.Attendance.AttendanceConfigs.UpdateConfig({
|
||||||
|
...values,
|
||||||
|
id: props.item?.id ?? 1,
|
||||||
|
})
|
||||||
|
.then(() => {
|
||||||
|
props.reload?.();
|
||||||
|
message.success(props.title + '编辑成功');
|
||||||
|
return true;
|
||||||
|
})
|
||||||
|
.catch(() => false)
|
||||||
|
}
|
||||||
|
columns={[
|
||||||
|
{
|
||||||
|
key: 'check_in_range',
|
||||||
|
title: '可打卡范围',
|
||||||
|
valueType: 'digit',
|
||||||
|
fieldProps: {
|
||||||
|
suffix: '米内',
|
||||||
|
style: { width: '100%' },
|
||||||
|
},
|
||||||
|
formItemProps: { ...rulesHelper.number },
|
||||||
|
colProps: { span: 24 },
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '是否要求拍照打卡',
|
||||||
|
key: 'require_photo',
|
||||||
|
valueType: 'switch',
|
||||||
|
colProps: { span: 12 },
|
||||||
|
},
|
||||||
|
// {
|
||||||
|
// title: '是否允许范围外打卡',
|
||||||
|
// key: 'allow_out_range_checkin',
|
||||||
|
// valueType: 'switch',
|
||||||
|
// colProps: { span: 12 },
|
||||||
|
// },
|
||||||
|
]}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
116
src/pages/attendance/attendance_employees/index.tsx
Normal file
116
src/pages/attendance/attendance_employees/index.tsx
Normal file
@ -0,0 +1,116 @@
|
|||||||
|
import {
|
||||||
|
MyColumns,
|
||||||
|
MyImportModal,
|
||||||
|
MyPageContainer,
|
||||||
|
MyProTableProps,
|
||||||
|
renderTextHelper,
|
||||||
|
useCurrentPermissions,
|
||||||
|
} from '@/common';
|
||||||
|
import { Apis } from '@/gen/Apis';
|
||||||
|
import { CompanyEmployeesTypeEnum } from '@/gen/Enums';
|
||||||
|
import { ProTable } from '@ant-design/pro-components';
|
||||||
|
import { Space } from 'antd';
|
||||||
|
import Change from './modals/Change';
|
||||||
|
import EmployeeCreate from './modals/EmployeeCreate';
|
||||||
|
import EmployeeUpdate from './modals/EmployeeUpdate';
|
||||||
|
|
||||||
|
export default function Index({ title = '外包人员' }) {
|
||||||
|
const getCurrentPermissions = useCurrentPermissions();
|
||||||
|
return (
|
||||||
|
<MyPageContainer
|
||||||
|
title={title}
|
||||||
|
enableTabs={true}
|
||||||
|
tabKey="attendance_employees"
|
||||||
|
tabLabel={title}
|
||||||
|
>
|
||||||
|
<ProTable
|
||||||
|
{...MyProTableProps.props}
|
||||||
|
// search={false}
|
||||||
|
headerTitle="外包人员列表"
|
||||||
|
tooltip="通过企微同步的员工信息的修改,需在企微修改后,同步到系统,才能生效。"
|
||||||
|
request={async (params, sort) =>
|
||||||
|
MyProTableProps.request(
|
||||||
|
{
|
||||||
|
type: 'External',
|
||||||
|
...params,
|
||||||
|
},
|
||||||
|
sort,
|
||||||
|
Apis.Company.CompanyEmployees.List,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
toolBarRender={(action) => [
|
||||||
|
<MyImportModal
|
||||||
|
key="ImportHouse"
|
||||||
|
title="导入外部人员"
|
||||||
|
type="default"
|
||||||
|
size="middle"
|
||||||
|
templateApi={Apis.Company.CompanyEmployees.DownloadTemplate}
|
||||||
|
importApi={Apis.Company.CompanyEmployees.Import}
|
||||||
|
reload={action?.reload}
|
||||||
|
/>,
|
||||||
|
<EmployeeCreate key="Create" reload={action?.reload} title="员工" />,
|
||||||
|
]}
|
||||||
|
columns={[
|
||||||
|
MyColumns.ID({ search: false }),
|
||||||
|
{
|
||||||
|
title: '所在组织',
|
||||||
|
dataIndex: 'organization_path',
|
||||||
|
search: {
|
||||||
|
transform: (value) => {
|
||||||
|
return { organization_name: value };
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '姓名',
|
||||||
|
dataIndex: 'name',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '手机号',
|
||||||
|
dataIndex: 'phone',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '角色',
|
||||||
|
dataIndex: 'roles',
|
||||||
|
renderText: renderTextHelper.TagList,
|
||||||
|
hideInSearch: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '岗位',
|
||||||
|
dataIndex: ['position', 'name'],
|
||||||
|
search: false,
|
||||||
|
},
|
||||||
|
MyColumns.EnumTag({
|
||||||
|
title: '来源',
|
||||||
|
dataIndex: 'type',
|
||||||
|
valueEnum: CompanyEmployeesTypeEnum,
|
||||||
|
search: false,
|
||||||
|
}),
|
||||||
|
MyColumns.SoftDelete({
|
||||||
|
title: '启/禁用',
|
||||||
|
onRestore: Apis.Company.CompanyEmployees.Restore,
|
||||||
|
onSoftDelete: Apis.Company.CompanyEmployees.SoftDelete,
|
||||||
|
search: false,
|
||||||
|
setPermissions: getCurrentPermissions({
|
||||||
|
enableDisable: true,
|
||||||
|
}),
|
||||||
|
}),
|
||||||
|
MyColumns.UpdatedAt(),
|
||||||
|
MyColumns.Option({
|
||||||
|
render: (_, item: any, index, action) => (
|
||||||
|
<Space key={index}>
|
||||||
|
<EmployeeUpdate
|
||||||
|
item={item}
|
||||||
|
reload={action?.reload}
|
||||||
|
title={title}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<Change item={item} reload={action?.reload} title={title} />
|
||||||
|
</Space>
|
||||||
|
),
|
||||||
|
}),
|
||||||
|
]}
|
||||||
|
/>
|
||||||
|
</MyPageContainer>
|
||||||
|
);
|
||||||
|
}
|
||||||
68
src/pages/attendance/attendance_employees/modals/Change.tsx
Normal file
68
src/pages/attendance/attendance_employees/modals/Change.tsx
Normal file
@ -0,0 +1,68 @@
|
|||||||
|
import {
|
||||||
|
MyBetaModalFormProps,
|
||||||
|
MyButtons,
|
||||||
|
MyModalFormProps,
|
||||||
|
rulesHelper,
|
||||||
|
} from '@/common';
|
||||||
|
|
||||||
|
import { Selects } from '@/components/Select';
|
||||||
|
import { Apis } from '@/gen/Apis';
|
||||||
|
import { CompanyEmployeesTypeEnum } from '@/gen/Enums';
|
||||||
|
import { BetaSchemaForm } from '@ant-design/pro-components';
|
||||||
|
import { Form, message } from 'antd';
|
||||||
|
export default function Update(props: MyBetaModalFormProps) {
|
||||||
|
const [form] = Form.useForm();
|
||||||
|
return (
|
||||||
|
<BetaSchemaForm<ApiTypes.Company.CompanyEmployees.Update>
|
||||||
|
{...MyModalFormProps.props}
|
||||||
|
title={`组织调整`}
|
||||||
|
trigger={
|
||||||
|
<MyButtons.Edit
|
||||||
|
title="组织"
|
||||||
|
disabled={
|
||||||
|
props.item?.type !== CompanyEmployeesTypeEnum.External.value
|
||||||
|
}
|
||||||
|
type="primary"
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
wrapperCol={{ span: 24 }}
|
||||||
|
width="500px"
|
||||||
|
key={new Date().getTime()}
|
||||||
|
form={form}
|
||||||
|
onOpenChange={(open: any) => {
|
||||||
|
if (open) {
|
||||||
|
form.resetFields(); // 清空表单数据
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
onFinish={async (values: any) =>
|
||||||
|
Apis.Company.CompanyEmployees.Update({
|
||||||
|
...values,
|
||||||
|
id: props.item?.id ?? 0,
|
||||||
|
name: props.item?.name ?? '',
|
||||||
|
phone: props.item?.phone ?? '',
|
||||||
|
type: props.item?.type ?? '',
|
||||||
|
organizations_id:
|
||||||
|
values?.organizations_id?.[values.organizations_id.length - 1],
|
||||||
|
})
|
||||||
|
.then(() => {
|
||||||
|
props.reload?.();
|
||||||
|
message.success(props.title + '成功');
|
||||||
|
return true;
|
||||||
|
})
|
||||||
|
.catch(() => false)
|
||||||
|
}
|
||||||
|
columns={[
|
||||||
|
Selects?.OrganizationsTree({
|
||||||
|
title: '选择组织',
|
||||||
|
key: 'organizations_id',
|
||||||
|
params: { companies_id: props?.item?.companies_id },
|
||||||
|
colProps: { span: 24 },
|
||||||
|
fieldProps: {
|
||||||
|
showSearch: true,
|
||||||
|
},
|
||||||
|
formItemProps: { ...rulesHelper.text },
|
||||||
|
}),
|
||||||
|
]}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
@ -0,0 +1,96 @@
|
|||||||
|
import {
|
||||||
|
MyBetaModalFormProps,
|
||||||
|
MyButtons,
|
||||||
|
MyFormItems,
|
||||||
|
MyModalFormProps,
|
||||||
|
rulesHelper,
|
||||||
|
} from '@/common';
|
||||||
|
import { Selects } from '@/components/Select';
|
||||||
|
import { SysSelects } from '@/components/SysSelects';
|
||||||
|
import { Apis } from '@/gen/Apis';
|
||||||
|
import { CompanyEmployeesTypeEnum, SexEnum } 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.Company.CompanyEmployees.Store>
|
||||||
|
{...MyModalFormProps.props}
|
||||||
|
title={`添加外部人员`}
|
||||||
|
layout="horizontal"
|
||||||
|
labelCol={{ span: 5 }}
|
||||||
|
wrapperCol={{ span: 19 }}
|
||||||
|
labelAlign="left"
|
||||||
|
width="500px"
|
||||||
|
key={new Date().getTime()}
|
||||||
|
trigger={<MyButtons.Create title={`外部人员`} />}
|
||||||
|
form={form}
|
||||||
|
onOpenChange={(open: any) => {
|
||||||
|
if (open) {
|
||||||
|
form.resetFields(); // 清空表单数据
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
onFinish={async (values: any) =>
|
||||||
|
Apis.Company.CompanyEmployees.Store({
|
||||||
|
...values,
|
||||||
|
companies_id: values?.companies_id || props?.item?.id,
|
||||||
|
type: CompanyEmployeesTypeEnum.External.value,
|
||||||
|
password: 'Gc#123',
|
||||||
|
organizations_id:
|
||||||
|
values?.organizations_id?.[values.organizations_id.length - 1],
|
||||||
|
})
|
||||||
|
.then(() => {
|
||||||
|
props.reload?.();
|
||||||
|
message.success(props.title + '成功');
|
||||||
|
return true;
|
||||||
|
})
|
||||||
|
.catch(() => false)
|
||||||
|
}
|
||||||
|
columns={[
|
||||||
|
Selects?.OrganizationsTree({
|
||||||
|
title: '选择组织',
|
||||||
|
key: 'organizations_id',
|
||||||
|
colProps: { span: 24 },
|
||||||
|
formItemProps: { ...rulesHelper.text },
|
||||||
|
}),
|
||||||
|
|
||||||
|
{
|
||||||
|
key: 'name',
|
||||||
|
title: '姓名',
|
||||||
|
colProps: { span: 24 },
|
||||||
|
formItemProps: { ...rulesHelper.text },
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'phone',
|
||||||
|
title: '手机号',
|
||||||
|
valueType: 'number',
|
||||||
|
fieldProps: {
|
||||||
|
maxLength: 11,
|
||||||
|
},
|
||||||
|
colProps: { span: 24 },
|
||||||
|
formItemProps: { ...rulesHelper.phone },
|
||||||
|
},
|
||||||
|
MyFormItems.EnumRadio({
|
||||||
|
key: 'sex',
|
||||||
|
title: '性别',
|
||||||
|
colProps: { span: 24 },
|
||||||
|
valueEnum: SexEnum,
|
||||||
|
required: true,
|
||||||
|
}),
|
||||||
|
Selects?.Positions({
|
||||||
|
title: '岗位',
|
||||||
|
key: 'positions_id',
|
||||||
|
formItemProps: { ...rulesHelper.text },
|
||||||
|
}),
|
||||||
|
SysSelects.SysRoles(),
|
||||||
|
{
|
||||||
|
key: 'remark',
|
||||||
|
title: '备注',
|
||||||
|
colProps: { span: 24 },
|
||||||
|
valueType: 'textarea',
|
||||||
|
},
|
||||||
|
]}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
@ -0,0 +1,121 @@
|
|||||||
|
import {
|
||||||
|
MyBetaModalFormProps,
|
||||||
|
MyButtons,
|
||||||
|
MyFormItems,
|
||||||
|
MyModalFormProps,
|
||||||
|
rulesHelper,
|
||||||
|
} from '@/common';
|
||||||
|
|
||||||
|
import { Selects } from '@/components/Select';
|
||||||
|
import { Apis } from '@/gen/Apis';
|
||||||
|
import { SexEnum } from '@/gen/Enums';
|
||||||
|
import { BetaSchemaForm } from '@ant-design/pro-components';
|
||||||
|
import { Form, message } from 'antd';
|
||||||
|
export default function Update(props: MyBetaModalFormProps) {
|
||||||
|
const [form] = Form.useForm();
|
||||||
|
return (
|
||||||
|
<BetaSchemaForm<ApiTypes.Company.CompanyEmployees.Update>
|
||||||
|
{...MyModalFormProps.props}
|
||||||
|
title={`编辑员工`}
|
||||||
|
trigger={
|
||||||
|
<MyButtons.Default
|
||||||
|
title="编辑"
|
||||||
|
type="primary"
|
||||||
|
// variant="solid"
|
||||||
|
size="small"
|
||||||
|
// disabled={
|
||||||
|
// props.item?.type !== CompanyEmployeesTypeEnum.External.value
|
||||||
|
// }
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
layout="horizontal"
|
||||||
|
labelCol={{ span: 5 }}
|
||||||
|
wrapperCol={{ span: 19 }}
|
||||||
|
labelAlign="left"
|
||||||
|
width="500px"
|
||||||
|
key={new Date().getTime()}
|
||||||
|
form={form}
|
||||||
|
onOpenChange={(open: any) => {
|
||||||
|
if (open && props.item) {
|
||||||
|
form.setFieldsValue({
|
||||||
|
...props.item,
|
||||||
|
roles_id: props.item?.roles?.map((item: any) => item.value),
|
||||||
|
positions_id: props.item?.positions_id ?? '',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
onFinish={async (values: any) =>
|
||||||
|
Apis.Company.CompanyEmployees.Update({
|
||||||
|
...values,
|
||||||
|
id: props.item?.id ?? 0,
|
||||||
|
type: props.item?.type,
|
||||||
|
password: null,
|
||||||
|
organizations_id: props.item?.organizations_id ?? '',
|
||||||
|
})
|
||||||
|
.then(() => {
|
||||||
|
props.reload?.();
|
||||||
|
message.success(props.title + '成功');
|
||||||
|
return true;
|
||||||
|
})
|
||||||
|
.catch(() => false)
|
||||||
|
}
|
||||||
|
columns={[
|
||||||
|
{
|
||||||
|
key: 'name',
|
||||||
|
title: '姓名',
|
||||||
|
colProps: { span: 24 },
|
||||||
|
formItemProps: { ...rulesHelper.text },
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'phone',
|
||||||
|
title: '手机号',
|
||||||
|
valueType: 'number',
|
||||||
|
fieldProps: {
|
||||||
|
maxLength: 11,
|
||||||
|
},
|
||||||
|
colProps: { span: 24 },
|
||||||
|
formItemProps: { ...rulesHelper.phone },
|
||||||
|
},
|
||||||
|
MyFormItems.EnumRadio({
|
||||||
|
key: 'sex',
|
||||||
|
title: '性别',
|
||||||
|
colProps: { span: 24 },
|
||||||
|
valueEnum: SexEnum,
|
||||||
|
required: true,
|
||||||
|
}),
|
||||||
|
// MyFormItems.EnumRadio({
|
||||||
|
// key: 'type',
|
||||||
|
// title: '来源',
|
||||||
|
// colProps: { span: 24 },
|
||||||
|
// valueEnum: CompanyEmployeesTypeEnum,
|
||||||
|
// required: true,
|
||||||
|
// }),
|
||||||
|
Selects?.Positions({
|
||||||
|
title: '岗位',
|
||||||
|
key: 'positions_id',
|
||||||
|
formItemProps: { ...rulesHelper.text },
|
||||||
|
params: {
|
||||||
|
name: props.item?.position?.name ?? '',
|
||||||
|
},
|
||||||
|
fieldProps: {
|
||||||
|
showSearch: true,
|
||||||
|
placeholder: '请输入关键字搜索',
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
|
||||||
|
// {
|
||||||
|
// key: 'password',
|
||||||
|
// title: '密码',
|
||||||
|
// colProps: { span: 24 },
|
||||||
|
// valueType: 'password',
|
||||||
|
// },
|
||||||
|
{
|
||||||
|
key: 'remark',
|
||||||
|
title: '备注',
|
||||||
|
colProps: { span: 24 },
|
||||||
|
valueType: 'textarea',
|
||||||
|
},
|
||||||
|
]}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
109
src/pages/attendance/attendance_employees_tracks/index.tsx
Normal file
109
src/pages/attendance/attendance_employees_tracks/index.tsx
Normal file
@ -0,0 +1,109 @@
|
|||||||
|
import {
|
||||||
|
MyButtons,
|
||||||
|
MyColumns,
|
||||||
|
MyPageContainer,
|
||||||
|
MyProTableProps,
|
||||||
|
renderTextHelper,
|
||||||
|
} from '@/common';
|
||||||
|
import { Selects } from '@/components/Select';
|
||||||
|
import { Apis } from '@/gen/Apis';
|
||||||
|
import { CompanyEmployeesTypeEnum } from '@/gen/Enums';
|
||||||
|
import { ProTable } from '@ant-design/pro-components';
|
||||||
|
import { useNavigate } from '@umijs/max';
|
||||||
|
import { Space } from 'antd';
|
||||||
|
|
||||||
|
export default function Index({ title = '员工轨迹' }) {
|
||||||
|
const navigate = useNavigate();
|
||||||
|
return (
|
||||||
|
<MyPageContainer
|
||||||
|
title={title}
|
||||||
|
enableTabs={true}
|
||||||
|
tabKey="attendance_employees"
|
||||||
|
tabLabel={title}
|
||||||
|
>
|
||||||
|
<ProTable
|
||||||
|
{...MyProTableProps.props}
|
||||||
|
// search={false}
|
||||||
|
headerTitle="员工轨迹列表"
|
||||||
|
request={async (params, sort) =>
|
||||||
|
MyProTableProps.request(
|
||||||
|
{
|
||||||
|
type: 'External',
|
||||||
|
...params,
|
||||||
|
},
|
||||||
|
sort,
|
||||||
|
Apis.Company.CompanyEmployees.List,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
columns={[
|
||||||
|
MyColumns.ID({ search: false }),
|
||||||
|
Selects?.OrganizationSearch({
|
||||||
|
title: '所属组织',
|
||||||
|
key: 'organizations_id',
|
||||||
|
colProps: { span: 24 },
|
||||||
|
fieldProps: {
|
||||||
|
showSearch: true,
|
||||||
|
},
|
||||||
|
search: {
|
||||||
|
transform: (value) => {
|
||||||
|
return {
|
||||||
|
organization_name:
|
||||||
|
value.length > 0 ? value[value.length - 1] : '',
|
||||||
|
};
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
{
|
||||||
|
title: '所在组织',
|
||||||
|
dataIndex: 'organization_path',
|
||||||
|
// search: {
|
||||||
|
// transform: (value) => {
|
||||||
|
// return { organization_name: value };
|
||||||
|
// },
|
||||||
|
// },
|
||||||
|
search: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '姓名',
|
||||||
|
dataIndex: 'name',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '手机号',
|
||||||
|
dataIndex: 'phone',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '角色',
|
||||||
|
dataIndex: 'roles',
|
||||||
|
renderText: renderTextHelper.TagList,
|
||||||
|
hideInSearch: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '岗位',
|
||||||
|
dataIndex: ['position', 'name'],
|
||||||
|
search: false,
|
||||||
|
},
|
||||||
|
MyColumns.EnumTag({
|
||||||
|
title: '来源',
|
||||||
|
dataIndex: 'type',
|
||||||
|
valueEnum: CompanyEmployeesTypeEnum,
|
||||||
|
search: false,
|
||||||
|
}),
|
||||||
|
MyColumns.UpdatedAt(),
|
||||||
|
MyColumns.Option({
|
||||||
|
render: (_, item: any, index) => (
|
||||||
|
<Space key={index}>
|
||||||
|
<MyButtons.Default
|
||||||
|
title="查看轨迹"
|
||||||
|
type="primary"
|
||||||
|
onClick={() =>
|
||||||
|
navigate(`/attendance/employee_tracks/${item.id}`)
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</Space>
|
||||||
|
),
|
||||||
|
}),
|
||||||
|
]}
|
||||||
|
/>
|
||||||
|
</MyPageContainer>
|
||||||
|
);
|
||||||
|
}
|
||||||
@ -0,0 +1,59 @@
|
|||||||
|
import {
|
||||||
|
MyBetaModalFormProps,
|
||||||
|
MyButtons,
|
||||||
|
MyModalFormProps,
|
||||||
|
rulesHelper,
|
||||||
|
} from '@/common';
|
||||||
|
|
||||||
|
import { Selects } from '@/components/Select';
|
||||||
|
import { Apis } from '@/gen/Apis';
|
||||||
|
import { BetaSchemaForm } from '@ant-design/pro-components';
|
||||||
|
import { Form, message } from 'antd';
|
||||||
|
export default function Update(props: MyBetaModalFormProps) {
|
||||||
|
const [form] = Form.useForm();
|
||||||
|
return (
|
||||||
|
<BetaSchemaForm<ApiTypes.Company.CompanyEmployees.Update>
|
||||||
|
{...MyModalFormProps.props}
|
||||||
|
title={`组织调整`}
|
||||||
|
trigger={<MyButtons.Default title="组织调整" type="link" />}
|
||||||
|
wrapperCol={{ span: 24 }}
|
||||||
|
width="500px"
|
||||||
|
key={new Date().getTime()}
|
||||||
|
form={form}
|
||||||
|
onOpenChange={(open: any) => {
|
||||||
|
if (open) {
|
||||||
|
form.resetFields(); // 清空表单数据
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
onFinish={async (values: any) =>
|
||||||
|
Apis.Company.CompanyEmployees.Update({
|
||||||
|
...values,
|
||||||
|
id: props.item?.id ?? 0,
|
||||||
|
name: props.item?.name ?? '',
|
||||||
|
phone: props.item?.phone ?? '',
|
||||||
|
type: props.item?.type,
|
||||||
|
organizations_id:
|
||||||
|
values?.organizations_id?.[values.organizations_id.length - 1],
|
||||||
|
})
|
||||||
|
.then(() => {
|
||||||
|
props.reload?.();
|
||||||
|
message.success(props.title + '成功');
|
||||||
|
return true;
|
||||||
|
})
|
||||||
|
.catch(() => false)
|
||||||
|
}
|
||||||
|
columns={[
|
||||||
|
Selects?.OrganizationsTree({
|
||||||
|
title: '选择组织',
|
||||||
|
key: 'organizations_id',
|
||||||
|
params: { companies_id: props?.item?.companies_id },
|
||||||
|
colProps: { span: 24 },
|
||||||
|
fieldProps: {
|
||||||
|
showSearch: true,
|
||||||
|
},
|
||||||
|
formItemProps: { ...rulesHelper.text },
|
||||||
|
}),
|
||||||
|
]}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
@ -0,0 +1,95 @@
|
|||||||
|
import {
|
||||||
|
MyBetaModalFormProps,
|
||||||
|
MyButtons,
|
||||||
|
MyFormItems,
|
||||||
|
MyModalFormProps,
|
||||||
|
rulesHelper,
|
||||||
|
} from '@/common';
|
||||||
|
import { Selects } from '@/components/Select';
|
||||||
|
import { SysSelects } from '@/components/SysSelects';
|
||||||
|
import { Apis } from '@/gen/Apis';
|
||||||
|
import { CompanyEmployeesTypeEnum, SexEnum } 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.Company.CompanyEmployees.Store>
|
||||||
|
{...MyModalFormProps.props}
|
||||||
|
title={`添加外部人员`}
|
||||||
|
layout="horizontal"
|
||||||
|
labelCol={{ span: 5 }}
|
||||||
|
wrapperCol={{ span: 19 }}
|
||||||
|
labelAlign="left"
|
||||||
|
width="500px"
|
||||||
|
trigger={<MyButtons.Create title={`外部人员`} />}
|
||||||
|
form={form}
|
||||||
|
onOpenChange={(open: any) => {
|
||||||
|
if (open) {
|
||||||
|
form.resetFields(); // 清空表单数据
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
onFinish={async (values: any) =>
|
||||||
|
Apis.Company.CompanyEmployees.Store({
|
||||||
|
...values,
|
||||||
|
companies_id: values?.companies_id || props?.item?.id,
|
||||||
|
type: CompanyEmployeesTypeEnum.External.value,
|
||||||
|
password: 'Gc#123',
|
||||||
|
organizations_id:
|
||||||
|
values?.organizations_id?.[values.organizations_id.length - 1],
|
||||||
|
})
|
||||||
|
.then(() => {
|
||||||
|
props.reload?.();
|
||||||
|
message.success(props.title + '成功');
|
||||||
|
return true;
|
||||||
|
})
|
||||||
|
.catch(() => false)
|
||||||
|
}
|
||||||
|
columns={[
|
||||||
|
Selects?.OrganizationsTree({
|
||||||
|
title: '选择组织',
|
||||||
|
key: 'organizations_id',
|
||||||
|
colProps: { span: 24 },
|
||||||
|
formItemProps: { ...rulesHelper.text },
|
||||||
|
}),
|
||||||
|
|
||||||
|
{
|
||||||
|
key: 'name',
|
||||||
|
title: '姓名',
|
||||||
|
colProps: { span: 24 },
|
||||||
|
formItemProps: { ...rulesHelper.text },
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'phone',
|
||||||
|
title: '手机号',
|
||||||
|
valueType: 'number',
|
||||||
|
fieldProps: {
|
||||||
|
maxLength: 11,
|
||||||
|
},
|
||||||
|
colProps: { span: 24 },
|
||||||
|
formItemProps: { ...rulesHelper.phone },
|
||||||
|
},
|
||||||
|
MyFormItems.EnumRadio({
|
||||||
|
key: 'sex',
|
||||||
|
title: '性别',
|
||||||
|
colProps: { span: 24 },
|
||||||
|
valueEnum: SexEnum,
|
||||||
|
required: true,
|
||||||
|
}),
|
||||||
|
Selects?.Positions({
|
||||||
|
title: '岗位',
|
||||||
|
key: 'positions_id',
|
||||||
|
formItemProps: { ...rulesHelper.text },
|
||||||
|
}),
|
||||||
|
SysSelects.SysRoles(),
|
||||||
|
{
|
||||||
|
key: 'remark',
|
||||||
|
title: '备注',
|
||||||
|
colProps: { span: 24 },
|
||||||
|
valueType: 'textarea',
|
||||||
|
},
|
||||||
|
]}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
@ -0,0 +1,107 @@
|
|||||||
|
import {
|
||||||
|
MyBetaModalFormProps,
|
||||||
|
MyButtons,
|
||||||
|
MyFormItems,
|
||||||
|
MyModalFormProps,
|
||||||
|
rulesHelper,
|
||||||
|
} from '@/common';
|
||||||
|
|
||||||
|
import { Selects } from '@/components/Select';
|
||||||
|
import { SysSelects } from '@/components/SysSelects';
|
||||||
|
import { Apis } from '@/gen/Apis';
|
||||||
|
import { SexEnum } from '@/gen/Enums';
|
||||||
|
import { BetaSchemaForm } from '@ant-design/pro-components';
|
||||||
|
import { Form, message } from 'antd';
|
||||||
|
export default function Update(props: MyBetaModalFormProps) {
|
||||||
|
const [form] = Form.useForm();
|
||||||
|
return (
|
||||||
|
<BetaSchemaForm<ApiTypes.Company.CompanyEmployees.Update>
|
||||||
|
{...MyModalFormProps.props}
|
||||||
|
title={`编辑员工`}
|
||||||
|
trigger={<MyButtons.Edit />}
|
||||||
|
wrapperCol={{ span: 24 }}
|
||||||
|
width="500px"
|
||||||
|
key={new Date().getTime()}
|
||||||
|
form={form}
|
||||||
|
onOpenChange={(open: any) => {
|
||||||
|
if (open && props.item) {
|
||||||
|
form.setFieldsValue({
|
||||||
|
...props.item,
|
||||||
|
roles_id: props.item?.roles?.map((item: any) => item.value),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
onFinish={async (values: any) =>
|
||||||
|
Apis.Company.CompanyEmployees.Update({
|
||||||
|
...values,
|
||||||
|
id: props.item?.id ?? 0,
|
||||||
|
type: props.item?.type,
|
||||||
|
organizations_id:
|
||||||
|
values?.organizations_id?.[values.organizations_id.length - 1] ||
|
||||||
|
props.item?.organizations_id,
|
||||||
|
})
|
||||||
|
.then(() => {
|
||||||
|
props.reload?.();
|
||||||
|
message.success(props.title + '成功');
|
||||||
|
return true;
|
||||||
|
})
|
||||||
|
.catch(() => false)
|
||||||
|
}
|
||||||
|
columns={[
|
||||||
|
{
|
||||||
|
key: 'name',
|
||||||
|
title: '姓名',
|
||||||
|
colProps: { span: 8 },
|
||||||
|
formItemProps: { ...rulesHelper.text },
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'phone',
|
||||||
|
title: '手机号',
|
||||||
|
valueType: 'number',
|
||||||
|
fieldProps: {
|
||||||
|
maxLength: 11,
|
||||||
|
},
|
||||||
|
colProps: { span: 10 },
|
||||||
|
formItemProps: { ...rulesHelper.phone },
|
||||||
|
},
|
||||||
|
MyFormItems.EnumRadio({
|
||||||
|
key: 'sex',
|
||||||
|
title: '性别',
|
||||||
|
colProps: { span: 6 },
|
||||||
|
valueEnum: SexEnum,
|
||||||
|
required: true,
|
||||||
|
}),
|
||||||
|
{
|
||||||
|
key: 'password',
|
||||||
|
title: '密码',
|
||||||
|
valueType: 'password',
|
||||||
|
colProps: { span: 24 },
|
||||||
|
fieldProps: {
|
||||||
|
placeholder: '不修改密码请留空',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Selects?.Positions({
|
||||||
|
title: '岗位',
|
||||||
|
key: 'positions_id',
|
||||||
|
formItemProps: { ...rulesHelper.text },
|
||||||
|
fieldProps: {
|
||||||
|
showSearch: true,
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
SysSelects.SysRoles(),
|
||||||
|
// {
|
||||||
|
// key: 'password',
|
||||||
|
// title: '密码',
|
||||||
|
// colProps: { span: 24 },
|
||||||
|
// valueType: 'password',
|
||||||
|
// },
|
||||||
|
{
|
||||||
|
key: 'remark',
|
||||||
|
title: '备注',
|
||||||
|
colProps: { span: 24 },
|
||||||
|
valueType: 'textarea',
|
||||||
|
},
|
||||||
|
]}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
120
src/pages/attendance/attendance_records/index.tsx
Normal file
120
src/pages/attendance/attendance_records/index.tsx
Normal file
@ -0,0 +1,120 @@
|
|||||||
|
import { MyColumns, MyPageContainer, MyProTableProps } from '@/common';
|
||||||
|
import { MyExport } from '@/components/MyExport';
|
||||||
|
import { Selects } from '@/components/Select';
|
||||||
|
import { Apis } from '@/gen/Apis';
|
||||||
|
import {
|
||||||
|
AttendanceRecordsCheckinTypeEnum,
|
||||||
|
AttendanceRecordsStatusEnum,
|
||||||
|
} from '@/gen/Enums';
|
||||||
|
import { ProTable } from '@ant-design/pro-components';
|
||||||
|
import { Image } from 'antd';
|
||||||
|
import { useState } from 'react';
|
||||||
|
|
||||||
|
export default function Index({ title = '打卡记录' }) {
|
||||||
|
const [getParams, setParams] = useState({});
|
||||||
|
|
||||||
|
return (
|
||||||
|
<MyPageContainer
|
||||||
|
title={title}
|
||||||
|
enableTabs={true}
|
||||||
|
tabKey="attendance_records"
|
||||||
|
tabLabel={title}
|
||||||
|
>
|
||||||
|
<ProTable
|
||||||
|
{...MyProTableProps.props}
|
||||||
|
request={async (params, sort) =>
|
||||||
|
MyProTableProps.request(
|
||||||
|
params,
|
||||||
|
sort,
|
||||||
|
Apis.Attendance.AttendanceRecords.List,
|
||||||
|
(e) => {
|
||||||
|
setParams(e);
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
headerTitle={title}
|
||||||
|
toolBarRender={() => [
|
||||||
|
<MyExport
|
||||||
|
key="export"
|
||||||
|
item={getParams}
|
||||||
|
download={Apis.Attendance.AttendanceRecords}
|
||||||
|
/>,
|
||||||
|
]}
|
||||||
|
columns={[
|
||||||
|
MyColumns.ID({ search: false }),
|
||||||
|
Selects?.AssetProjects({
|
||||||
|
title: '选择项目',
|
||||||
|
key: 'asset_projects_id',
|
||||||
|
hidden: true,
|
||||||
|
}),
|
||||||
|
{
|
||||||
|
title: '关联项目',
|
||||||
|
dataIndex: ['asset_project', 'name'],
|
||||||
|
search: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '员工',
|
||||||
|
dataIndex: ['company_employee', 'name'],
|
||||||
|
search: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '打卡时间',
|
||||||
|
dataIndex: 'checkin_time',
|
||||||
|
valueType: 'dateRange',
|
||||||
|
hidden: true,
|
||||||
|
},
|
||||||
|
MyColumns.EnumTag({
|
||||||
|
title: '打卡类型',
|
||||||
|
dataIndex: 'checkin_type',
|
||||||
|
valueEnum: AttendanceRecordsCheckinTypeEnum,
|
||||||
|
}),
|
||||||
|
MyColumns.EnumTag({
|
||||||
|
title: '打卡结果',
|
||||||
|
dataIndex: 'status',
|
||||||
|
valueEnum: AttendanceRecordsStatusEnum,
|
||||||
|
}),
|
||||||
|
{
|
||||||
|
title: '打卡时间',
|
||||||
|
dataIndex: 'checkin_time',
|
||||||
|
search: false,
|
||||||
|
render: (_, item: any) => {
|
||||||
|
return item?.status === 'CheckIn'
|
||||||
|
? item?.shift_periods?.work_start_time
|
||||||
|
: item?.shift_periods?.work_end_time;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '员工打卡时间',
|
||||||
|
dataIndex: 'checkin_time',
|
||||||
|
search: false,
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
title: '打卡拍照',
|
||||||
|
dataIndex: 'checkin_time',
|
||||||
|
search: false,
|
||||||
|
render: (_, item: any) => {
|
||||||
|
return (
|
||||||
|
<Image.PreviewGroup
|
||||||
|
preview={{
|
||||||
|
onChange: (current, prev) =>
|
||||||
|
console.log(
|
||||||
|
`current index: ${current}, prev index: ${prev}`,
|
||||||
|
),
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{item?.photo?.map((res: any, index?: number) => {
|
||||||
|
return (
|
||||||
|
<Image key={`item_${index}`} height={30} src={res?.url} />
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</Image.PreviewGroup>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
// MyColumns.CreatedAt(),
|
||||||
|
]}
|
||||||
|
/>
|
||||||
|
</MyPageContainer>
|
||||||
|
);
|
||||||
|
}
|
||||||
158
src/pages/attendance/attendance_schedules/index.tsx
Normal file
158
src/pages/attendance/attendance_schedules/index.tsx
Normal file
@ -0,0 +1,158 @@
|
|||||||
|
import {
|
||||||
|
MyButtons,
|
||||||
|
MyColumns,
|
||||||
|
MyPageContainer,
|
||||||
|
MyProTableProps,
|
||||||
|
} from '@/common';
|
||||||
|
import { Selects } from '@/components/Select';
|
||||||
|
import { Apis } from '@/gen/Apis';
|
||||||
|
import { AttendanceSchedulesStatusEnum } from '@/gen/Enums';
|
||||||
|
import { ProTable } from '@ant-design/pro-components';
|
||||||
|
import { useNavigate } from '@umijs/max';
|
||||||
|
import { Space, Tooltip } from 'antd';
|
||||||
|
import Update from './modals/Update';
|
||||||
|
|
||||||
|
export default function Index({ title = '排班管理' }) {
|
||||||
|
const navigate = useNavigate();
|
||||||
|
|
||||||
|
return (
|
||||||
|
<MyPageContainer
|
||||||
|
title={title}
|
||||||
|
enableTabs={true}
|
||||||
|
tabKey="attendance_schedules"
|
||||||
|
tabLabel={title}
|
||||||
|
>
|
||||||
|
<ProTable
|
||||||
|
{...MyProTableProps.props}
|
||||||
|
request={async (params, sort) =>
|
||||||
|
MyProTableProps.request(
|
||||||
|
{ ...params, status: AttendanceSchedulesStatusEnum.Active.value },
|
||||||
|
sort,
|
||||||
|
Apis.Attendance.AttendanceSchedules.List,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
headerTitle="排班信息"
|
||||||
|
toolBarRender={() => [
|
||||||
|
<MyButtons.Default
|
||||||
|
key="Create"
|
||||||
|
size="middle"
|
||||||
|
type="primary"
|
||||||
|
onClick={() => {
|
||||||
|
navigate('/attendance/attendance_schedules/pages/create');
|
||||||
|
}}
|
||||||
|
title="批量排班 / 批量调整"
|
||||||
|
/>,
|
||||||
|
]}
|
||||||
|
columns={[
|
||||||
|
// MyColumns.ID({
|
||||||
|
// search: false,
|
||||||
|
// }),
|
||||||
|
|
||||||
|
Selects?.AssetProjects({
|
||||||
|
title: '选择项目',
|
||||||
|
key: 'asset_projects_id',
|
||||||
|
hidden: true,
|
||||||
|
}),
|
||||||
|
{
|
||||||
|
title: '排班日期',
|
||||||
|
dataIndex: 'schedule_date',
|
||||||
|
valueType: 'date',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '关联项目',
|
||||||
|
dataIndex: ['asset_project', 'name'],
|
||||||
|
// search: {
|
||||||
|
// transform: (value) => {
|
||||||
|
// return { project_name: value };
|
||||||
|
// },
|
||||||
|
// },
|
||||||
|
search: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '员工',
|
||||||
|
dataIndex: ['company_employee', 'name'],
|
||||||
|
search: {
|
||||||
|
transform: (value) => {
|
||||||
|
return { employee_name: value };
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '班次',
|
||||||
|
dataIndex: ['attendance_shift', 'name'],
|
||||||
|
search: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '时段要求',
|
||||||
|
dataIndex: 'shift_periods',
|
||||||
|
search: false,
|
||||||
|
render: (_, item: any) => {
|
||||||
|
const periods = item?.shift_periods || [];
|
||||||
|
const periodTexts = periods.map((res: any) => {
|
||||||
|
return `时段${
|
||||||
|
res?.period_order
|
||||||
|
}: ${res?.work_start_time?.substring(
|
||||||
|
0,
|
||||||
|
5,
|
||||||
|
)}-${res?.work_end_time?.substring(0, 5)}`;
|
||||||
|
});
|
||||||
|
const allPeriodsText = periodTexts.join(' ');
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Tooltip
|
||||||
|
title={
|
||||||
|
periodTexts.map((text: string, index: number) => (
|
||||||
|
<div key={index}>{text}</div>
|
||||||
|
)) || ''
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
whiteSpace: 'nowrap',
|
||||||
|
overflow: 'hidden',
|
||||||
|
textOverflow: 'ellipsis',
|
||||||
|
maxWidth: '150px',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{allPeriodsText}
|
||||||
|
</div>
|
||||||
|
</Tooltip>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
// MyColumns.EnumTag({
|
||||||
|
// title: '状态',
|
||||||
|
// dataIndex: 'status',
|
||||||
|
// valueEnum: AttendanceSchedulesStatusEnum,
|
||||||
|
// }),
|
||||||
|
{
|
||||||
|
title: '排班人',
|
||||||
|
dataIndex: ['created_employee', 'name'],
|
||||||
|
search: false,
|
||||||
|
},
|
||||||
|
// MyColumns.CreatedAt(),
|
||||||
|
MyColumns.Option({
|
||||||
|
render: (_, item: any, index, action) => (
|
||||||
|
<Space key={index}>
|
||||||
|
<Update item={item} reload={action?.reload} title={title} />
|
||||||
|
<MyButtons.Default
|
||||||
|
disabled={item?.status === 'Cancelled'}
|
||||||
|
isConfirm
|
||||||
|
title="取消班次"
|
||||||
|
danger
|
||||||
|
description="是否确定取消?"
|
||||||
|
onConfirm={() =>
|
||||||
|
Apis.Attendance.AttendanceSchedules.Cancel({
|
||||||
|
id: item.id,
|
||||||
|
}).then(() => action?.reload())
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</Space>
|
||||||
|
),
|
||||||
|
}),
|
||||||
|
]}
|
||||||
|
/>
|
||||||
|
</MyPageContainer>
|
||||||
|
);
|
||||||
|
}
|
||||||
117
src/pages/attendance/attendance_schedules/modals/Create.tsx
Normal file
117
src/pages/attendance/attendance_schedules/modals/Create.tsx
Normal file
@ -0,0 +1,117 @@
|
|||||||
|
import {
|
||||||
|
MyBetaModalFormProps,
|
||||||
|
MyButtons,
|
||||||
|
MyModalFormProps,
|
||||||
|
rulesHelper,
|
||||||
|
} from '@/common';
|
||||||
|
import { Selects } from '@/components/Select';
|
||||||
|
import { Apis } from '@/gen/Apis';
|
||||||
|
import { BetaSchemaForm, EditableProTable } from '@ant-design/pro-components';
|
||||||
|
import { Form, message } from 'antd';
|
||||||
|
import { useState } from 'react';
|
||||||
|
|
||||||
|
export default function Create(props: MyBetaModalFormProps) {
|
||||||
|
const [form] = Form.useForm();
|
||||||
|
const [dataSource, setDataSource] = useState([]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<BetaSchemaForm<ApiTypes.Attendance.AttendanceSchedules.BatchStore>
|
||||||
|
{...MyModalFormProps.props}
|
||||||
|
title={`批量${props.title}`}
|
||||||
|
wrapperCol={{ span: 24 }}
|
||||||
|
width="800px"
|
||||||
|
key={new Date().getTime()}
|
||||||
|
form={form}
|
||||||
|
onOpenChange={(open: any) => {
|
||||||
|
if (open) {
|
||||||
|
form.resetFields(); // 清空表单数据
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
trigger={<MyButtons.Create title={`批量${props.title}`} />}
|
||||||
|
onFinish={async (values) =>
|
||||||
|
Apis.Attendance.AttendanceSchedules.BatchStore(values)
|
||||||
|
.then(() => {
|
||||||
|
props.reload?.();
|
||||||
|
message.success('新增成功');
|
||||||
|
return true;
|
||||||
|
})
|
||||||
|
.catch(() => false)
|
||||||
|
}
|
||||||
|
submitter={false}
|
||||||
|
columns={[
|
||||||
|
Selects?.OrganizationsTree({
|
||||||
|
title: '选择组织',
|
||||||
|
key: 'organizations_id',
|
||||||
|
colProps: { span: 24 },
|
||||||
|
formItemProps: { ...rulesHelper.text },
|
||||||
|
}),
|
||||||
|
{
|
||||||
|
title: '排班日期',
|
||||||
|
key: 'schedule_date',
|
||||||
|
valueType: 'date',
|
||||||
|
fieldProps: {
|
||||||
|
format: 'YYYY-MM-DD',
|
||||||
|
},
|
||||||
|
colProps: { span: 24 },
|
||||||
|
formItemProps: { ...rulesHelper.text },
|
||||||
|
},
|
||||||
|
{
|
||||||
|
valueType: 'dependency',
|
||||||
|
name: ['organizations_id', 'schedule_date'],
|
||||||
|
colProps: { span: 24 },
|
||||||
|
columns: ({ schedule_date, organizations_id }) => {
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
renderFormItem: () => {
|
||||||
|
console.log(schedule_date, organizations_id);
|
||||||
|
return (
|
||||||
|
<MyButtons.Default
|
||||||
|
type="primary"
|
||||||
|
size="middle"
|
||||||
|
title="获取员工列表"
|
||||||
|
style={{ width: '100%' }}
|
||||||
|
onClick={() => {
|
||||||
|
// getEmployeesExternal(organizations_id || []); //获取外部员工
|
||||||
|
Apis.Attendance.AttendanceSchedules.ShiftList({
|
||||||
|
schedule_date: schedule_date,
|
||||||
|
organizations_id:
|
||||||
|
organizations_id?.[organizations_id?.length - 1],
|
||||||
|
}).then((res) => {
|
||||||
|
setDataSource(res?.data);
|
||||||
|
console.log(res);
|
||||||
|
});
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
renderFormItem: () => {
|
||||||
|
return (
|
||||||
|
<EditableProTable<any>
|
||||||
|
headerTitle="可编辑表格"
|
||||||
|
bordered
|
||||||
|
recordCreatorProps={false}
|
||||||
|
columns={[
|
||||||
|
{
|
||||||
|
title: '员工ID',
|
||||||
|
key: 'id',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '员工',
|
||||||
|
key: 'name',
|
||||||
|
},
|
||||||
|
]}
|
||||||
|
rowKey="id"
|
||||||
|
value={dataSource}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
];
|
||||||
|
},
|
||||||
|
},
|
||||||
|
]}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
98
src/pages/attendance/attendance_schedules/modals/Update.tsx
Normal file
98
src/pages/attendance/attendance_schedules/modals/Update.tsx
Normal file
@ -0,0 +1,98 @@
|
|||||||
|
import {
|
||||||
|
MyBetaModalFormProps,
|
||||||
|
MyButtons,
|
||||||
|
MyModalFormProps,
|
||||||
|
rulesHelper,
|
||||||
|
} from '@/common';
|
||||||
|
import { Selects } from '@/components/Select';
|
||||||
|
import { Apis } from '@/gen/Apis';
|
||||||
|
import { BetaSchemaForm } from '@ant-design/pro-components';
|
||||||
|
import { Form, message } from 'antd';
|
||||||
|
|
||||||
|
export default function Update(props: MyBetaModalFormProps) {
|
||||||
|
const [form] = Form.useForm();
|
||||||
|
return (
|
||||||
|
<BetaSchemaForm<ApiTypes.Attendance.AttendanceSchedules.Update>
|
||||||
|
{...MyModalFormProps.props}
|
||||||
|
title={`调整班次`}
|
||||||
|
trigger={
|
||||||
|
<MyButtons.Default
|
||||||
|
type="primary"
|
||||||
|
title="调班"
|
||||||
|
disabled={props.item?.status !== 'Active'}
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
key={new Date().getTime()}
|
||||||
|
layout="horizontal"
|
||||||
|
labelCol={{ span: 5 }}
|
||||||
|
wrapperCol={{ span: 19 }}
|
||||||
|
labelAlign="left"
|
||||||
|
width="500px"
|
||||||
|
form={form}
|
||||||
|
onOpenChange={(open: any) => {
|
||||||
|
if (open && props.item) {
|
||||||
|
form.setFieldsValue(props.item);
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
onFinish={async (values) =>
|
||||||
|
Apis.Attendance.AttendanceSchedules.Update({
|
||||||
|
...values,
|
||||||
|
id: props.item?.id ?? 0,
|
||||||
|
})
|
||||||
|
.then(() => {
|
||||||
|
props.reload?.();
|
||||||
|
message.success('编辑成功');
|
||||||
|
return true;
|
||||||
|
})
|
||||||
|
.catch(() => false)
|
||||||
|
}
|
||||||
|
columns={[
|
||||||
|
Selects?.Employees({
|
||||||
|
title: '员工信息',
|
||||||
|
key: 'company_employees_id',
|
||||||
|
colProps: { span: 24 },
|
||||||
|
formItemProps: { ...rulesHelper.number },
|
||||||
|
required: true,
|
||||||
|
fieldProps: {
|
||||||
|
disabled: true,
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
|
||||||
|
Selects?.AssetProjects({
|
||||||
|
title: '项目信息',
|
||||||
|
key: 'asset_projects_id',
|
||||||
|
colProps: { span: 24 },
|
||||||
|
formItemProps: { ...rulesHelper.number },
|
||||||
|
fieldProps: {
|
||||||
|
disabled: true,
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
{
|
||||||
|
title: '班次日期',
|
||||||
|
key: 'schedule_date',
|
||||||
|
valueType: 'date',
|
||||||
|
colProps: { span: 24 },
|
||||||
|
formItemProps: { ...rulesHelper.text },
|
||||||
|
fieldProps: {
|
||||||
|
style: {
|
||||||
|
width: '100%',
|
||||||
|
},
|
||||||
|
disabled: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Selects?.AttendanceShiftsSelect({
|
||||||
|
key: 'attendance_shifts_id',
|
||||||
|
title: '选择班次',
|
||||||
|
colProps: { span: 24 },
|
||||||
|
formItemProps: { ...rulesHelper.number },
|
||||||
|
}),
|
||||||
|
{
|
||||||
|
title: '备注',
|
||||||
|
key: 'remark',
|
||||||
|
valueType: 'textarea',
|
||||||
|
colProps: { span: 24 },
|
||||||
|
},
|
||||||
|
]}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
304
src/pages/attendance/attendance_schedules/pages/create.tsx
Normal file
304
src/pages/attendance/attendance_schedules/pages/create.tsx
Normal file
@ -0,0 +1,304 @@
|
|||||||
|
import {
|
||||||
|
MyButtons,
|
||||||
|
MyModalFormProps,
|
||||||
|
MyPageContainer,
|
||||||
|
rulesHelper,
|
||||||
|
} from '@/common';
|
||||||
|
import { Selects } from '@/components/Select';
|
||||||
|
import { Apis } from '@/gen/Apis';
|
||||||
|
import {
|
||||||
|
BetaSchemaForm,
|
||||||
|
EditableProTable,
|
||||||
|
ProCard,
|
||||||
|
} from '@ant-design/pro-components';
|
||||||
|
import { useNavigate } from '@umijs/max';
|
||||||
|
import { Form, message, Space } from 'antd';
|
||||||
|
import dayjs from 'dayjs';
|
||||||
|
import { useState } from 'react';
|
||||||
|
let optionsData: any = [];
|
||||||
|
export default function Index({ title = '批量排班' }) {
|
||||||
|
const [form] = Form.useForm();
|
||||||
|
const navigate = useNavigate();
|
||||||
|
const [startDate, setStartDate] = useState([]);
|
||||||
|
const [dataSource, setDataSource] = useState([]);
|
||||||
|
|
||||||
|
// 动态提取日期(排除 id 和 name 字段)
|
||||||
|
const extractDates = (data: any) => {
|
||||||
|
const firstPerson = data[0];
|
||||||
|
return Object.keys(firstPerson).filter(
|
||||||
|
(key) => key !== 'id' && key !== 'name' && !isNaN(Date.parse(key)),
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const getOptionsData = async (params: any) => {
|
||||||
|
if (optionsData?.length) {
|
||||||
|
return optionsData || [];
|
||||||
|
}
|
||||||
|
let res: any = await Apis.Attendance.AttendanceShifts.Select({
|
||||||
|
...params,
|
||||||
|
name: params?.keyWords || undefined,
|
||||||
|
});
|
||||||
|
optionsData = [...res?.data, ...[{ id: -1, name: '取消班次' }]];
|
||||||
|
console.log(optionsData, '1');
|
||||||
|
return optionsData || [];
|
||||||
|
};
|
||||||
|
// 生成未来7天日期的函数
|
||||||
|
const generateFutureDates = (startDate: string) => {
|
||||||
|
const dates = [];
|
||||||
|
|
||||||
|
const columns = [
|
||||||
|
{
|
||||||
|
title: '员工ID',
|
||||||
|
dataIndex: 'id',
|
||||||
|
readonly: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '员工',
|
||||||
|
dataIndex: 'name',
|
||||||
|
readonly: true,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
const start = dayjs(startDate);
|
||||||
|
for (let i = 0; i < 7; i++) {
|
||||||
|
const currentDate = start.clone().add(i, 'day').format('YYYY-MM-DD');
|
||||||
|
dates.push({
|
||||||
|
title: currentDate,
|
||||||
|
key: currentDate,
|
||||||
|
dataIndex: currentDate,
|
||||||
|
formItemProps: { ...rulesHelper.number },
|
||||||
|
valueType: 'select',
|
||||||
|
request: async (params: any) => {
|
||||||
|
return getOptionsData(params);
|
||||||
|
},
|
||||||
|
fieldProps: {
|
||||||
|
showSearch: true,
|
||||||
|
allowClear: false,
|
||||||
|
// placeholder: '请选择班次',
|
||||||
|
fieldNames: {
|
||||||
|
label: 'name',
|
||||||
|
value: 'id',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
console.log(dates);
|
||||||
|
if (startDate && dates?.length) {
|
||||||
|
return [...columns, ...dates];
|
||||||
|
} else {
|
||||||
|
return columns;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<MyPageContainer
|
||||||
|
title={
|
||||||
|
<Space
|
||||||
|
style={{ cursor: 'pointer' }}
|
||||||
|
onClick={() => {
|
||||||
|
navigate(-1);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{/* <LeftCircleOutlined size={34} /> */}
|
||||||
|
{title}
|
||||||
|
</Space>
|
||||||
|
}
|
||||||
|
enableTabs={false}
|
||||||
|
tabKey="charge-standards-create"
|
||||||
|
tabLabel={title}
|
||||||
|
>
|
||||||
|
<ProCard>
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
width: '1200px',
|
||||||
|
minHeight: '83vh',
|
||||||
|
margin: '0 auto',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<BetaSchemaForm<ApiTypes.Contract.Contracts.Store>
|
||||||
|
{...MyModalFormProps.props}
|
||||||
|
title={title}
|
||||||
|
// 基础表单
|
||||||
|
layoutType="Form"
|
||||||
|
labelCol={{ span: 24 }}
|
||||||
|
wrapperCol={{ span: 24 }}
|
||||||
|
labelAlign="left"
|
||||||
|
width="900px"
|
||||||
|
form={form}
|
||||||
|
submitter={{
|
||||||
|
render: (props) => {
|
||||||
|
return [
|
||||||
|
<MyButtons.Default
|
||||||
|
key="Review"
|
||||||
|
size="middle"
|
||||||
|
type="primary"
|
||||||
|
onClick={() => {
|
||||||
|
props.submit();
|
||||||
|
}}
|
||||||
|
title="提交保存"
|
||||||
|
/>,
|
||||||
|
];
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
onFinish={async () => {
|
||||||
|
const dates = extractDates(dataSource);
|
||||||
|
const missingData: string[] = [];
|
||||||
|
|
||||||
|
// // 先检查所有数据是否完整
|
||||||
|
for (const person of dataSource) {
|
||||||
|
for (const date of dates) {
|
||||||
|
const shiftId = person[date];
|
||||||
|
if (shiftId === undefined || shiftId === null) {
|
||||||
|
missingData.push(`员工 ${person?.name} 在 ${date}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 如果有缺失数据,提示并停止执行(只提示一次)
|
||||||
|
// if (missingData.length > 0) {
|
||||||
|
// message.error(
|
||||||
|
// `存在 ${missingData.length} 条未填写的排班数据,请完善后再提交`,
|
||||||
|
// );
|
||||||
|
// return false;
|
||||||
|
// }
|
||||||
|
// 数据完整,进行转换和提交
|
||||||
|
const transformedData: any = dataSource.flatMap((person: any) => {
|
||||||
|
console.log(person, 'person');
|
||||||
|
|
||||||
|
return dates.map((date) => {
|
||||||
|
let startDateItem: any = {};
|
||||||
|
if (person[date] === -1) {
|
||||||
|
startDate?.flatMap((item: any) => {
|
||||||
|
if (item?.id === person?.id) {
|
||||||
|
startDateItem = item;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
console.log(startDateItem, 'startDateItem');
|
||||||
|
return {
|
||||||
|
company_employees_id: person.id,
|
||||||
|
attendance_shifts_id:
|
||||||
|
person[date] === -1
|
||||||
|
? startDateItem[date]
|
||||||
|
: person[date] || 0,
|
||||||
|
schedule_date: date,
|
||||||
|
status: person[date] < 0 ? 'Cancelled' : '',
|
||||||
|
};
|
||||||
|
});
|
||||||
|
});
|
||||||
|
const filteredData = transformedData.filter(
|
||||||
|
(item: any) =>
|
||||||
|
item.attendance_shifts_id !== null &&
|
||||||
|
item.attendance_shifts_id !== 0,
|
||||||
|
);
|
||||||
|
console.log(transformedData, startDate, 'startDate');
|
||||||
|
|
||||||
|
console.log(filteredData, 'filteredData');
|
||||||
|
// return;
|
||||||
|
Apis.Attendance.AttendanceSchedules.BatchStore({
|
||||||
|
schedules: filteredData || [],
|
||||||
|
})
|
||||||
|
.then(() => {
|
||||||
|
navigate(-1);
|
||||||
|
message.success('提交成功');
|
||||||
|
})
|
||||||
|
.catch(() => false);
|
||||||
|
console.log(transformedData);
|
||||||
|
}}
|
||||||
|
columns={[
|
||||||
|
Selects?.OrganizationsTree({
|
||||||
|
title: '第一步:选择组织',
|
||||||
|
key: 'organizations_id',
|
||||||
|
colProps: { span: 24 },
|
||||||
|
formItemProps: { ...rulesHelper.text },
|
||||||
|
}),
|
||||||
|
{
|
||||||
|
title: '第二步:选择开始日期',
|
||||||
|
tooltip: '排班开始日期,最大排班日期为当前日期',
|
||||||
|
key: 'schedule_date',
|
||||||
|
valueType: 'date',
|
||||||
|
fieldProps: {
|
||||||
|
format: 'YYYY-MM-DD',
|
||||||
|
style: { width: '100%' },
|
||||||
|
},
|
||||||
|
colProps: { span: 24 },
|
||||||
|
formItemProps: { ...rulesHelper.text },
|
||||||
|
},
|
||||||
|
{
|
||||||
|
valueType: 'dependency',
|
||||||
|
name: ['organizations_id', 'schedule_date'],
|
||||||
|
colProps: { span: 24 },
|
||||||
|
columns: ({ schedule_date, organizations_id }) => {
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
renderFormItem: () => {
|
||||||
|
console.log(schedule_date, organizations_id);
|
||||||
|
return (
|
||||||
|
<MyButtons.Default
|
||||||
|
type="primary"
|
||||||
|
size="middle"
|
||||||
|
title="第三步:获取环卫人员信息"
|
||||||
|
style={{ width: '100%' }}
|
||||||
|
onClick={() => {
|
||||||
|
// getEmployeesExternal(organizations_id || []); //获取外部员工
|
||||||
|
Apis.Attendance.AttendanceSchedules.ShiftList({
|
||||||
|
schedule_date: schedule_date,
|
||||||
|
organizations_id:
|
||||||
|
organizations_id?.[
|
||||||
|
organizations_id?.length - 1
|
||||||
|
],
|
||||||
|
}).then((res) => {
|
||||||
|
setStartDate(
|
||||||
|
JSON.parse(JSON.stringify(res?.data)),
|
||||||
|
);
|
||||||
|
setDataSource(
|
||||||
|
JSON.parse(JSON.stringify(res?.data)),
|
||||||
|
);
|
||||||
|
console.log(res);
|
||||||
|
});
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
renderFormItem: () => {
|
||||||
|
return (
|
||||||
|
<ProCard bordered size="small">
|
||||||
|
<EditableProTable<any>
|
||||||
|
key={schedule_date}
|
||||||
|
headerTitle="第四步:设置员工班次(按周)"
|
||||||
|
bordered
|
||||||
|
recordCreatorProps={false}
|
||||||
|
columns={generateFutureDates(schedule_date || '')}
|
||||||
|
rowKey="id"
|
||||||
|
value={dataSource}
|
||||||
|
onChange={(values) => {
|
||||||
|
setDataSource(values);
|
||||||
|
}}
|
||||||
|
editable={{
|
||||||
|
type: 'multiple',
|
||||||
|
editableKeys: dataSource?.map(
|
||||||
|
(item: any) => item?.id,
|
||||||
|
),
|
||||||
|
onValuesChange: (record, recordList: any) => {
|
||||||
|
setDataSource(recordList);
|
||||||
|
},
|
||||||
|
onSave: async (rowKey, data, row) => {
|
||||||
|
console.log(rowKey, data, row);
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</ProCard>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
];
|
||||||
|
},
|
||||||
|
},
|
||||||
|
]}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</ProCard>
|
||||||
|
</MyPageContainer>
|
||||||
|
);
|
||||||
|
}
|
||||||
154
src/pages/attendance/attendance_shifts/index.tsx
Normal file
154
src/pages/attendance/attendance_shifts/index.tsx
Normal file
@ -0,0 +1,154 @@
|
|||||||
|
import {
|
||||||
|
MyButtons,
|
||||||
|
MyColumns,
|
||||||
|
MyPageContainer,
|
||||||
|
MyProTableProps,
|
||||||
|
} from '@/common';
|
||||||
|
import { Selects } from '@/components/Select';
|
||||||
|
import { Apis } from '@/gen/Apis';
|
||||||
|
import { ProTable } from '@ant-design/pro-components';
|
||||||
|
import { useNavigate } from '@umijs/max';
|
||||||
|
import { Space, Tooltip } from 'antd';
|
||||||
|
|
||||||
|
export default function Index({ title = '班次管理' }) {
|
||||||
|
const navigate = useNavigate();
|
||||||
|
|
||||||
|
return (
|
||||||
|
<MyPageContainer
|
||||||
|
title={title}
|
||||||
|
enableTabs={true}
|
||||||
|
tabKey="attendance_shifts"
|
||||||
|
tabLabel={title}
|
||||||
|
>
|
||||||
|
<ProTable
|
||||||
|
{...MyProTableProps.props}
|
||||||
|
request={async (params, sort) =>
|
||||||
|
MyProTableProps.request(
|
||||||
|
params,
|
||||||
|
sort,
|
||||||
|
Apis.Attendance.AttendanceShifts.List,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
headerTitle={title}
|
||||||
|
toolBarRender={() => [
|
||||||
|
<MyButtons.Default
|
||||||
|
key="Create"
|
||||||
|
size="middle"
|
||||||
|
type="primary"
|
||||||
|
onClick={() => {
|
||||||
|
navigate('/attendance/attendance_shifts/pages/create');
|
||||||
|
}}
|
||||||
|
title="新增班次"
|
||||||
|
/>,
|
||||||
|
]}
|
||||||
|
columns={[
|
||||||
|
MyColumns.ID({
|
||||||
|
search: false,
|
||||||
|
}),
|
||||||
|
Selects?.AssetProjects({
|
||||||
|
title: '选择项目',
|
||||||
|
key: 'asset_projects_id',
|
||||||
|
hidden: true,
|
||||||
|
}),
|
||||||
|
{
|
||||||
|
title: '关联项目',
|
||||||
|
dataIndex: ['asset_project', 'name'],
|
||||||
|
// search: {
|
||||||
|
// transform: (value) => {
|
||||||
|
// return { project_name: value };
|
||||||
|
// },
|
||||||
|
// },
|
||||||
|
search: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '班次名称',
|
||||||
|
dataIndex: 'name',
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
title: '时段要求',
|
||||||
|
dataIndex: 'attendance_shift_periods',
|
||||||
|
search: false,
|
||||||
|
render: (_, item: any) => {
|
||||||
|
const periods = item?.attendance_shift_periods || [];
|
||||||
|
const periodTexts = periods.map((res: any) => {
|
||||||
|
return `时段${
|
||||||
|
res?.period_order
|
||||||
|
}: ${res?.work_start_time?.substring(
|
||||||
|
0,
|
||||||
|
5,
|
||||||
|
)}-${res?.work_end_time?.substring(0, 5)}`;
|
||||||
|
});
|
||||||
|
const allPeriodsText = periodTexts.join(' ');
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Tooltip
|
||||||
|
title={
|
||||||
|
periodTexts.map((text: string, index: number) => (
|
||||||
|
<div key={index}>{text}</div>
|
||||||
|
)) || ''
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
whiteSpace: 'nowrap',
|
||||||
|
overflow: 'hidden',
|
||||||
|
textOverflow: 'ellipsis',
|
||||||
|
maxWidth: '150px',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{allPeriodsText}
|
||||||
|
</div>
|
||||||
|
</Tooltip>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
title: '可打卡时间范围',
|
||||||
|
render(_, record) {
|
||||||
|
return `${record?.allow_checkin_start} - ${record?.allow_checkin_end}`;
|
||||||
|
},
|
||||||
|
search: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '备注',
|
||||||
|
dataIndex: 'remark',
|
||||||
|
search: false,
|
||||||
|
},
|
||||||
|
MyColumns.Boolean({
|
||||||
|
dataIndex: 'is_enabled',
|
||||||
|
title: '启用',
|
||||||
|
}),
|
||||||
|
|
||||||
|
// MyColumns.CreatedAt(),
|
||||||
|
MyColumns.Option({
|
||||||
|
render: (_, item: any, index, action) => (
|
||||||
|
<Space key={index}>
|
||||||
|
<MyButtons.Default
|
||||||
|
key="Update"
|
||||||
|
size="small"
|
||||||
|
type="primary"
|
||||||
|
onClick={() => {
|
||||||
|
navigate(
|
||||||
|
`/attendance/attendance_shifts/pages/update?id=${item.id}`,
|
||||||
|
);
|
||||||
|
}}
|
||||||
|
title="编辑"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<MyButtons.Delete
|
||||||
|
onConfirm={() =>
|
||||||
|
Apis.Attendance.AttendanceShifts.Delete({
|
||||||
|
id: item.id,
|
||||||
|
}).then(() => action?.reload())
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</Space>
|
||||||
|
),
|
||||||
|
}),
|
||||||
|
]}
|
||||||
|
/>
|
||||||
|
</MyPageContainer>
|
||||||
|
);
|
||||||
|
}
|
||||||
259
src/pages/attendance/attendance_shifts/pages/create.tsx
Normal file
259
src/pages/attendance/attendance_shifts/pages/create.tsx
Normal file
@ -0,0 +1,259 @@
|
|||||||
|
import {
|
||||||
|
MyFormItems,
|
||||||
|
MyModalFormProps,
|
||||||
|
MyPageContainer,
|
||||||
|
rulesHelper,
|
||||||
|
} from '@/common';
|
||||||
|
import { Selects } from '@/components/Select';
|
||||||
|
import { Apis } from '@/gen/Apis';
|
||||||
|
import { BetaSchemaForm, ProCard } from '@ant-design/pro-components';
|
||||||
|
import { useNavigate } from '@umijs/max';
|
||||||
|
import { Form, message, Space } from 'antd';
|
||||||
|
import { useState } from 'react';
|
||||||
|
export default function Index({ title = '新增班次' }) {
|
||||||
|
const [form] = Form.useForm();
|
||||||
|
const navigate = useNavigate();
|
||||||
|
const [dataSource, setDataSource] = useState<
|
||||||
|
ApiTypes.Asset.AssetProjects.List[]
|
||||||
|
>([]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<MyPageContainer
|
||||||
|
title={
|
||||||
|
<Space
|
||||||
|
style={{ cursor: 'pointer' }}
|
||||||
|
onClick={() => {
|
||||||
|
navigate(-1);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{/* <LeftCircleOutlined size={34} /> */}
|
||||||
|
{title}
|
||||||
|
</Space>
|
||||||
|
}
|
||||||
|
enableTabs={false}
|
||||||
|
tabKey="attendance-shifts-create"
|
||||||
|
tabLabel={title}
|
||||||
|
>
|
||||||
|
<ProCard>
|
||||||
|
<div style={{ width: 900, minHeight: '83vh', margin: '0 auto' }}>
|
||||||
|
<BetaSchemaForm<ApiTypes.Attendance.AttendanceShifts.Store>
|
||||||
|
{...MyModalFormProps.props}
|
||||||
|
title={title}
|
||||||
|
// 基础表单
|
||||||
|
layoutType="Form"
|
||||||
|
labelCol={{ span: 24 }}
|
||||||
|
wrapperCol={{ span: 24 }}
|
||||||
|
labelAlign="left"
|
||||||
|
width="900px"
|
||||||
|
form={form}
|
||||||
|
onFinish={async (values: any) => {
|
||||||
|
// 处理时间格式转换,将HH:mm转换为HH:mm:ss格式,秒数固定为00
|
||||||
|
const formattedValues = { ...values };
|
||||||
|
|
||||||
|
// 处理work_start_time和work_end_time数组
|
||||||
|
if (formattedValues.periods) {
|
||||||
|
formattedValues.periods = formattedValues.periods.map(
|
||||||
|
(period: any) => {
|
||||||
|
return {
|
||||||
|
...period,
|
||||||
|
work_start_time: period.work_start_time
|
||||||
|
? `${period.work_start_time}:00`
|
||||||
|
: '',
|
||||||
|
work_end_time: period.work_end_time
|
||||||
|
? `${period.work_end_time}:00`
|
||||||
|
: '',
|
||||||
|
};
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 处理可打卡开始和结束时间
|
||||||
|
if (formattedValues.allow_checkin_start) {
|
||||||
|
formattedValues.allow_checkin_start = `${formattedValues.allow_checkin_start}:00`;
|
||||||
|
}
|
||||||
|
if (formattedValues.allow_checkin_end) {
|
||||||
|
formattedValues.allow_checkin_end = `${formattedValues.allow_checkin_end}:00`;
|
||||||
|
}
|
||||||
|
|
||||||
|
Apis.Attendance.AttendanceShifts.Store({
|
||||||
|
...formattedValues,
|
||||||
|
// is_enabled: values.is_enabled ? true : false,
|
||||||
|
is_enabled: true,
|
||||||
|
})
|
||||||
|
.then(() => {
|
||||||
|
//提交
|
||||||
|
navigate(-1);
|
||||||
|
message.success('提交成功');
|
||||||
|
})
|
||||||
|
.catch(() => false);
|
||||||
|
}}
|
||||||
|
columns={[
|
||||||
|
{
|
||||||
|
title: '班次名称',
|
||||||
|
key: 'name',
|
||||||
|
formItemProps: { ...rulesHelper.text },
|
||||||
|
fieldProps: {
|
||||||
|
placeholder: '请输入(如:早班、晚班)',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
valueType: 'formList',
|
||||||
|
dataIndex: 'periods',
|
||||||
|
title: '设置需打卡时段',
|
||||||
|
formItemProps: { ...rulesHelper.array },
|
||||||
|
fieldProps: {
|
||||||
|
copyIconProps: false,
|
||||||
|
// deleteIconProps: false,
|
||||||
|
},
|
||||||
|
columns: [
|
||||||
|
{
|
||||||
|
valueType: 'group',
|
||||||
|
colProps: { span: 24 },
|
||||||
|
columns: [
|
||||||
|
MyFormItems.EnumSelect({
|
||||||
|
key: 'period_order',
|
||||||
|
valueEnum: {
|
||||||
|
1: '1',
|
||||||
|
2: '2',
|
||||||
|
3: '3',
|
||||||
|
4: '4',
|
||||||
|
5: '5',
|
||||||
|
},
|
||||||
|
required: true,
|
||||||
|
colProps: { span: 4 },
|
||||||
|
}),
|
||||||
|
{
|
||||||
|
key: 'work_start_time',
|
||||||
|
valueType: 'time',
|
||||||
|
fieldProps: {
|
||||||
|
style: { width: '100%' },
|
||||||
|
placeholder: '请输入班次开始时间',
|
||||||
|
format: 'HH:mm',
|
||||||
|
},
|
||||||
|
colProps: { span: 8 },
|
||||||
|
formItemProps: { ...rulesHelper.text },
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'work_end_time',
|
||||||
|
valueType: 'time',
|
||||||
|
fieldProps: {
|
||||||
|
style: { width: '100%' },
|
||||||
|
placeholder: '请输入班次开始时间',
|
||||||
|
format: 'HH:mm',
|
||||||
|
},
|
||||||
|
colProps: { span: 8 },
|
||||||
|
formItemProps: { ...rulesHelper.text },
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{ valueType: 'divider' },
|
||||||
|
{
|
||||||
|
title: '可打卡开始时间',
|
||||||
|
key: 'allow_checkin_start',
|
||||||
|
valueType: 'time',
|
||||||
|
fieldProps: {
|
||||||
|
style: { width: '100%' },
|
||||||
|
format: 'HH:mm',
|
||||||
|
},
|
||||||
|
colProps: { span: 12 },
|
||||||
|
formItemProps: { ...rulesHelper.text },
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '可打卡结束时间',
|
||||||
|
key: 'allow_checkin_end',
|
||||||
|
fieldProps: {
|
||||||
|
style: { width: '100%' },
|
||||||
|
format: 'HH:mm',
|
||||||
|
},
|
||||||
|
valueType: 'time',
|
||||||
|
colProps: { span: 12 },
|
||||||
|
formItemProps: { ...rulesHelper.text },
|
||||||
|
},
|
||||||
|
// {
|
||||||
|
// title: '启用',
|
||||||
|
// key: 'is_enabled',
|
||||||
|
// valueType: 'switch',
|
||||||
|
// colProps: { span: 24 },
|
||||||
|
// },
|
||||||
|
// {
|
||||||
|
// colProps: { span: 24 },
|
||||||
|
// title: '选择关联项目',
|
||||||
|
// tooltip: '选择后对应项目的人员可排班',
|
||||||
|
// key: 'asset_projects_id',
|
||||||
|
// formItemProps: { ...rulesHelper.text },
|
||||||
|
// renderFormItem: () => {
|
||||||
|
// return (
|
||||||
|
// <ProCard size="small" bordered>
|
||||||
|
// <ProTable
|
||||||
|
// {...MyProTableProps.props}
|
||||||
|
// options={false}
|
||||||
|
// size="small"
|
||||||
|
// search={false}
|
||||||
|
// pagination={false}
|
||||||
|
// dataSource={dataSource || []}
|
||||||
|
// headerTitle={
|
||||||
|
// <MyAssetsProjectSelectList
|
||||||
|
// key="select_project"
|
||||||
|
// item={dataSource}
|
||||||
|
// type="radio"
|
||||||
|
// onChange={(e: any) => {
|
||||||
|
// let row = e?.[0];
|
||||||
|
// form.setFieldsValue({
|
||||||
|
// asset_projects_id:
|
||||||
|
// row?.asset_projects_id || row?.id,
|
||||||
|
// });
|
||||||
|
// setDataSource(e);
|
||||||
|
// }}
|
||||||
|
// />
|
||||||
|
// }
|
||||||
|
// columns={[
|
||||||
|
// MyColumns.ID({
|
||||||
|
// search: false,
|
||||||
|
// }),
|
||||||
|
// {
|
||||||
|
// title: '名称',
|
||||||
|
// dataIndex: 'name',
|
||||||
|
// },
|
||||||
|
// MyColumns.Option({
|
||||||
|
// render: (_, item: any, index) => (
|
||||||
|
// <Space key={index}>
|
||||||
|
// <MyButtons.Delete
|
||||||
|
// onConfirm={() => {
|
||||||
|
// setDataSource(
|
||||||
|
// dataSource.filter(
|
||||||
|
// (res: any) => res?.id !== item?.id,
|
||||||
|
// ),
|
||||||
|
// );
|
||||||
|
// }}
|
||||||
|
// />
|
||||||
|
// </Space>
|
||||||
|
// ),
|
||||||
|
// }),
|
||||||
|
// ]}
|
||||||
|
// />
|
||||||
|
// </ProCard>
|
||||||
|
// );
|
||||||
|
// },
|
||||||
|
// },
|
||||||
|
|
||||||
|
Selects?.AssetProjects({
|
||||||
|
title: '关联项目',
|
||||||
|
key: 'asset_projects_id',
|
||||||
|
colProps: { span: 24 },
|
||||||
|
formItemProps: { ...rulesHelper.text },
|
||||||
|
}),
|
||||||
|
{
|
||||||
|
title: '备注',
|
||||||
|
key: 'remark',
|
||||||
|
valueType: 'textarea',
|
||||||
|
colProps: { span: 24 },
|
||||||
|
},
|
||||||
|
]}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</ProCard>
|
||||||
|
</MyPageContainer>
|
||||||
|
);
|
||||||
|
}
|
||||||
263
src/pages/attendance/attendance_shifts/pages/update.tsx
Normal file
263
src/pages/attendance/attendance_shifts/pages/update.tsx
Normal file
@ -0,0 +1,263 @@
|
|||||||
|
import {
|
||||||
|
MyFormItems,
|
||||||
|
MyModalFormProps,
|
||||||
|
MyPageContainer,
|
||||||
|
rulesHelper,
|
||||||
|
} from '@/common';
|
||||||
|
import { Selects } from '@/components/Select';
|
||||||
|
import { Apis } from '@/gen/Apis';
|
||||||
|
import { BetaSchemaForm, ProCard } from '@ant-design/pro-components';
|
||||||
|
import { useNavigate, useSearchParams } from '@umijs/max';
|
||||||
|
import { Form, message, Space } from 'antd';
|
||||||
|
import { useEffect, useState } from 'react';
|
||||||
|
|
||||||
|
export default function Index({ title = '编辑班次' }) {
|
||||||
|
const [form] = Form.useForm();
|
||||||
|
const navigate = useNavigate();
|
||||||
|
const [searchParams] = useSearchParams();
|
||||||
|
const id = searchParams.get('id') ?? 0;
|
||||||
|
const [data, setShow] = useState<any>({});
|
||||||
|
const [dataSource, setDataSource] = useState<
|
||||||
|
ApiTypes.Asset.AssetProjects.List[]
|
||||||
|
>([]);
|
||||||
|
|
||||||
|
const loadShow = () => {
|
||||||
|
Apis.Attendance.AttendanceShifts.Show({ id: Number(id) }).then((res) => {
|
||||||
|
setShow(res?.data);
|
||||||
|
setDataSource([res?.data?.asset_project]);
|
||||||
|
|
||||||
|
// 处理时间格式,将hh:mm:ss转换为hh:mm用于表单显示
|
||||||
|
const formData = { ...res?.data };
|
||||||
|
|
||||||
|
// 处理可打卡开始和结束时间
|
||||||
|
if (formData.allow_checkin_start) {
|
||||||
|
formData.allow_checkin_start = formData.allow_checkin_start.slice(0, 5);
|
||||||
|
}
|
||||||
|
if (formData.allow_checkin_end) {
|
||||||
|
formData.allow_checkin_end = formData.allow_checkin_end.slice(0, 5);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 处理时间段数组中的时间格式
|
||||||
|
if (formData.attendance_shift_periods) {
|
||||||
|
formData.periods = formData.attendance_shift_periods.map(
|
||||||
|
(period: any) => {
|
||||||
|
return {
|
||||||
|
...period,
|
||||||
|
work_start_time: period.work_start_time
|
||||||
|
? period.work_start_time.slice(0, 5)
|
||||||
|
: '',
|
||||||
|
work_end_time: period.work_end_time
|
||||||
|
? period.work_end_time.slice(0, 5)
|
||||||
|
: '',
|
||||||
|
};
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
form.setFieldsValue({
|
||||||
|
...formData,
|
||||||
|
asset_projects_id: res?.data?.asset_project?.id,
|
||||||
|
}); // 编辑赋值
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
loadShow();
|
||||||
|
}, [id]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<MyPageContainer
|
||||||
|
title={
|
||||||
|
<Space
|
||||||
|
style={{ cursor: 'pointer' }}
|
||||||
|
onClick={() => {
|
||||||
|
navigate(-1);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{/* <LeftCircleOutlined size={34} /> */}
|
||||||
|
{title}
|
||||||
|
</Space>
|
||||||
|
}
|
||||||
|
enableTabs={false}
|
||||||
|
tabKey="attendance-shifts-update"
|
||||||
|
tabLabel={title}
|
||||||
|
>
|
||||||
|
<ProCard>
|
||||||
|
<div style={{ width: 900, minHeight: '83vh', margin: '0 auto' }}>
|
||||||
|
<BetaSchemaForm<ApiTypes.Attendance.AttendanceShifts.Update>
|
||||||
|
{...MyModalFormProps.props}
|
||||||
|
title={title}
|
||||||
|
// 基础表单
|
||||||
|
layoutType="Form"
|
||||||
|
labelCol={{ span: 24 }}
|
||||||
|
wrapperCol={{ span: 24 }}
|
||||||
|
labelAlign="left"
|
||||||
|
width="900px"
|
||||||
|
form={form}
|
||||||
|
onFinish={async (values: any) => {
|
||||||
|
// 处理时间格式转换,将HH:mm转换为HH:mm:ss格式,秒数固定为00
|
||||||
|
const formattedValues = { ...values };
|
||||||
|
|
||||||
|
// 处理work_start_time和work_end_time数组
|
||||||
|
if (formattedValues.periods) {
|
||||||
|
formattedValues.periods = formattedValues.periods.map(
|
||||||
|
(period: any) => {
|
||||||
|
return {
|
||||||
|
...period,
|
||||||
|
work_start_time: period.work_start_time
|
||||||
|
? `${period.work_start_time}:00`
|
||||||
|
: '',
|
||||||
|
work_end_time: period.work_end_time
|
||||||
|
? `${period.work_end_time}:00`
|
||||||
|
: '',
|
||||||
|
};
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 处理可打卡开始和结束时间
|
||||||
|
if (formattedValues.allow_checkin_start) {
|
||||||
|
formattedValues.allow_checkin_start = `${formattedValues.allow_checkin_start}:00`;
|
||||||
|
}
|
||||||
|
if (formattedValues.allow_checkin_end) {
|
||||||
|
formattedValues.allow_checkin_end = `${formattedValues.allow_checkin_end}:00`;
|
||||||
|
}
|
||||||
|
|
||||||
|
Apis.Attendance.AttendanceShifts.Update({
|
||||||
|
...formattedValues,
|
||||||
|
is_enabled: values.is_enabled ? true : false,
|
||||||
|
id: data?.id,
|
||||||
|
})
|
||||||
|
.then(() => {
|
||||||
|
//提交
|
||||||
|
navigate(-1);
|
||||||
|
message.success('编辑成功');
|
||||||
|
})
|
||||||
|
.catch(() => false);
|
||||||
|
}}
|
||||||
|
columns={[
|
||||||
|
{
|
||||||
|
title: '班次名称',
|
||||||
|
key: 'name',
|
||||||
|
formItemProps: { ...rulesHelper.text },
|
||||||
|
colProps: { span: 12 },
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '状态',
|
||||||
|
key: 'is_enabled',
|
||||||
|
valueType: 'switch',
|
||||||
|
colProps: { span: 12 },
|
||||||
|
fieldProps(form, config) {
|
||||||
|
return {
|
||||||
|
...config.fieldProps,
|
||||||
|
style: { width: '100%' },
|
||||||
|
checkedChildren: '启用',
|
||||||
|
unCheckedChildren: '停用',
|
||||||
|
};
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
valueType: 'formList',
|
||||||
|
dataIndex: 'periods',
|
||||||
|
title: '设置需打卡时段',
|
||||||
|
formItemProps: { ...rulesHelper.array },
|
||||||
|
fieldProps: {
|
||||||
|
copyIconProps: false,
|
||||||
|
// deleteIconProps: false,
|
||||||
|
},
|
||||||
|
columns: [
|
||||||
|
{
|
||||||
|
valueType: 'group',
|
||||||
|
colProps: { span: 24 },
|
||||||
|
columns: [
|
||||||
|
MyFormItems.EnumSelect({
|
||||||
|
key: 'period_order',
|
||||||
|
valueEnum: {
|
||||||
|
1: '1',
|
||||||
|
2: '2',
|
||||||
|
3: '3',
|
||||||
|
4: '4',
|
||||||
|
5: '5',
|
||||||
|
},
|
||||||
|
required: true,
|
||||||
|
colProps: { span: 4 },
|
||||||
|
}),
|
||||||
|
// {
|
||||||
|
// key: 'period_order',
|
||||||
|
// valueType: 'digit',
|
||||||
|
// colProps: { span: 6 },
|
||||||
|
// formItemProps: { ...rulesHelper.text },
|
||||||
|
// fieldProps: {
|
||||||
|
// style: { width: '100%' },
|
||||||
|
// placeholder: '请输入班次序号',
|
||||||
|
// },
|
||||||
|
// },
|
||||||
|
{
|
||||||
|
key: 'work_start_time',
|
||||||
|
valueType: 'time',
|
||||||
|
fieldProps: {
|
||||||
|
style: { width: '100%' },
|
||||||
|
placeholder: '请输入班次开始时间',
|
||||||
|
format: 'HH:mm',
|
||||||
|
},
|
||||||
|
colProps: { span: 8 },
|
||||||
|
formItemProps: { ...rulesHelper.text },
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'work_end_time',
|
||||||
|
fieldProps: {
|
||||||
|
style: { width: '100%' },
|
||||||
|
placeholder: '请输入班次开始时间',
|
||||||
|
format: 'HH:mm',
|
||||||
|
},
|
||||||
|
valueType: 'time',
|
||||||
|
colProps: { span: 8 },
|
||||||
|
formItemProps: { ...rulesHelper.text },
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '可打卡开始时间',
|
||||||
|
key: 'allow_checkin_start',
|
||||||
|
valueType: 'time',
|
||||||
|
fieldProps: {
|
||||||
|
style: { width: '100%' },
|
||||||
|
format: 'HH:mm',
|
||||||
|
},
|
||||||
|
colProps: { span: 12 },
|
||||||
|
formItemProps: { ...rulesHelper.text },
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '可打卡结束时间',
|
||||||
|
key: 'allow_checkin_end',
|
||||||
|
fieldProps: {
|
||||||
|
style: { width: '100%' },
|
||||||
|
format: 'HH:mm',
|
||||||
|
},
|
||||||
|
valueType: 'time',
|
||||||
|
colProps: { span: 12 },
|
||||||
|
formItemProps: { ...rulesHelper.text },
|
||||||
|
},
|
||||||
|
|
||||||
|
Selects?.AssetProjects({
|
||||||
|
title: '关联项目',
|
||||||
|
key: 'asset_projects_id',
|
||||||
|
colProps: { span: 24 },
|
||||||
|
formItemProps: { ...rulesHelper.text },
|
||||||
|
}),
|
||||||
|
|
||||||
|
{
|
||||||
|
title: '备注',
|
||||||
|
key: 'remark',
|
||||||
|
valueType: 'textarea',
|
||||||
|
colProps: { span: 24 },
|
||||||
|
},
|
||||||
|
]}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</ProCard>
|
||||||
|
</MyPageContainer>
|
||||||
|
);
|
||||||
|
}
|
||||||
289
src/pages/attendance/employee_tracks/$id.tsx
Normal file
289
src/pages/attendance/employee_tracks/$id.tsx
Normal file
@ -0,0 +1,289 @@
|
|||||||
|
import { MyPageContainer } from '@/common';
|
||||||
|
import { getTodayDate } from '@/common/utils/day';
|
||||||
|
import { mapKey } from '@/common/utils/mapConfig';
|
||||||
|
import { Apis } from '@/gen/Apis';
|
||||||
|
import { LeftOutlined } from '@ant-design/icons';
|
||||||
|
import { ProCard } from '@ant-design/pro-components';
|
||||||
|
import { useNavigate, useParams } from '@umijs/max';
|
||||||
|
import { Button, DatePicker, Space } from 'antd';
|
||||||
|
import dayjs from 'dayjs';
|
||||||
|
import * as L from 'leaflet';
|
||||||
|
import 'leaflet/dist/leaflet.css';
|
||||||
|
import { useEffect, useRef } from 'react';
|
||||||
|
import './style.scss';
|
||||||
|
const dateFormat = 'YYYY/MM/DD';
|
||||||
|
export default function Index({ title = '员工轨迹' }) {
|
||||||
|
const navigate = useNavigate();
|
||||||
|
const { id } = useParams<{ id: string }>();
|
||||||
|
const mapRef = useRef<HTMLDivElement>(null);
|
||||||
|
const mapInstanceRef = useRef<L.Map | null>(null);
|
||||||
|
// 初始化地图
|
||||||
|
const initMap = () => {
|
||||||
|
if (!mapRef.current) return;
|
||||||
|
|
||||||
|
// 创建地图实例
|
||||||
|
const map = L.map(mapRef.current, {
|
||||||
|
center: [30.258134, 120.19382669582967], // 默认中心点(长江三角洲)
|
||||||
|
zoom: 10,
|
||||||
|
maxZoom: 18,
|
||||||
|
zoomControl: false,
|
||||||
|
attributionControl: false,
|
||||||
|
});
|
||||||
|
|
||||||
|
// 添加天地图底图层(矢量地图)
|
||||||
|
L.tileLayer(
|
||||||
|
`https://t{s}.tianditu.gov.cn/DataServer?T=vec_w&x={x}&y={y}&l={z}&tk=${mapKey}`,
|
||||||
|
{
|
||||||
|
subdomains: ['0', '1', '2', '3', '4', '5', '6', '7'],
|
||||||
|
attribution: '天地图',
|
||||||
|
minZoom: 1,
|
||||||
|
maxZoom: 18,
|
||||||
|
},
|
||||||
|
).addTo(map);
|
||||||
|
|
||||||
|
// 添加天地图文字描述层(矢量注记)
|
||||||
|
L.tileLayer(
|
||||||
|
`https://t{s}.tianditu.gov.cn/DataServer?T=cva_w&x={x}&y={y}&l={z}&tk=${mapKey}`,
|
||||||
|
{
|
||||||
|
subdomains: ['0', '1', '2', '3', '4', '5', '6', '7'],
|
||||||
|
attribution: '天地图',
|
||||||
|
minZoom: 1,
|
||||||
|
maxZoom: 18,
|
||||||
|
},
|
||||||
|
).addTo(map);
|
||||||
|
|
||||||
|
mapInstanceRef.current = map;
|
||||||
|
};
|
||||||
|
|
||||||
|
// 显示轨迹
|
||||||
|
const showTrack = (data: any[]) => {
|
||||||
|
if (!mapInstanceRef.current) return;
|
||||||
|
|
||||||
|
const map = mapInstanceRef.current;
|
||||||
|
|
||||||
|
// 首先清除所有旧的轨迹(无论是否有新数据)
|
||||||
|
map.eachLayer((layer: L.Layer) => {
|
||||||
|
if (layer instanceof L.Polyline || layer instanceof L.Marker) {
|
||||||
|
map.removeLayer(layer);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// 如果没有新的轨迹数据,直接返回
|
||||||
|
if (!data || data.length === 0) return;
|
||||||
|
|
||||||
|
// 过滤有效坐标并保留原始数据
|
||||||
|
const validPoints = data.filter(
|
||||||
|
(point) => point.latitude && point.longitude,
|
||||||
|
);
|
||||||
|
|
||||||
|
// 转换为Leaflet可识别的坐标格式
|
||||||
|
const trackPoints: L.LatLngTuple[] = validPoints.map(
|
||||||
|
(point) =>
|
||||||
|
[
|
||||||
|
parseFloat(point.latitude),
|
||||||
|
parseFloat(point.longitude),
|
||||||
|
] as L.LatLngTuple,
|
||||||
|
);
|
||||||
|
|
||||||
|
// 如果过滤后没有有效坐标,返回
|
||||||
|
if (trackPoints.length === 0) return;
|
||||||
|
|
||||||
|
// 创建轨迹线
|
||||||
|
const trackLine = L.polyline(trackPoints, {
|
||||||
|
color: '#f00',
|
||||||
|
weight: 4,
|
||||||
|
opacity: 1,
|
||||||
|
}).addTo(map);
|
||||||
|
|
||||||
|
// 添加起点标记
|
||||||
|
const startMarker = L.marker(trackPoints[0], {
|
||||||
|
icon: L.divIcon({
|
||||||
|
className: 'track-start-marker',
|
||||||
|
html: "<img width='40' height='40' src='https://gc-prod-1385694945.cos.ap-shanghai.myqcloud.com/uploads/cs-test/01KH0R6TZSPBNF3X8ARGXE5R6C.png' />",
|
||||||
|
}),
|
||||||
|
}).addTo(map);
|
||||||
|
|
||||||
|
// 为起点添加弹窗
|
||||||
|
const startPoint = validPoints[0];
|
||||||
|
startMarker.bindPopup(`
|
||||||
|
<div style="padding: 10px;">
|
||||||
|
<h4 style="margin: 0 0 10px 0; color: #333;">起点</h4>
|
||||||
|
<p><strong>序号:</strong> 1</p>
|
||||||
|
<p><strong>时间:</strong> ${startPoint.track_time || '未知'}</p>
|
||||||
|
<p><strong>坐标:</strong> ${startPoint.latitude}, ${
|
||||||
|
startPoint.longitude
|
||||||
|
}</p>
|
||||||
|
</div>
|
||||||
|
`);
|
||||||
|
|
||||||
|
// 添加终点标记
|
||||||
|
const endMarker = L.marker(trackPoints[trackPoints.length - 1], {
|
||||||
|
icon: L.divIcon({
|
||||||
|
className: 'track-end-marker',
|
||||||
|
html: "<img width='40' height='40' src='https://gc-prod-1385694945.cos.ap-shanghai.myqcloud.com/uploads/cs-test/01KH0STS12KMARBH8F9SJVCN1S.png' />",
|
||||||
|
}),
|
||||||
|
}).addTo(map);
|
||||||
|
|
||||||
|
// 为终点添加弹窗
|
||||||
|
const endPoint = validPoints[validPoints.length - 1];
|
||||||
|
endMarker.bindPopup(`
|
||||||
|
<div style="padding: 10px;">
|
||||||
|
<h4 style="margin: 0 0 10px 0; color: #333;">终点</h4>
|
||||||
|
<p><strong>序号:</strong> ${validPoints.length}</p>
|
||||||
|
<p><strong>时间:</strong> ${endPoint.track_time || '未知'}</p>
|
||||||
|
<p><strong>坐标:</strong> ${endPoint.latitude}, ${
|
||||||
|
endPoint.longitude
|
||||||
|
}</p>
|
||||||
|
</div>
|
||||||
|
`);
|
||||||
|
|
||||||
|
// 为每个轨迹点添加序号标记和弹窗
|
||||||
|
trackPoints.forEach((point, index) => {
|
||||||
|
// 跳过起点和终点,只标记中间点
|
||||||
|
if (index === 0 || index === trackPoints.length - 1) return;
|
||||||
|
|
||||||
|
const currentPoint = validPoints[index];
|
||||||
|
const marker = L.marker(point, {
|
||||||
|
icon: L.divIcon({
|
||||||
|
className: 'track-point-marker',
|
||||||
|
html: `<div style="width: 24px; height: 24px; border-radius: 50%; background-color: #3498db; color: white; display: flex; align-items: center; justify-content: center; font-size: 12px; font-weight: bold; box-shadow: 0 2px 4px rgba(0,0,0,0.2);">${
|
||||||
|
index + 1
|
||||||
|
}</div>`,
|
||||||
|
iconSize: [24, 24],
|
||||||
|
}),
|
||||||
|
}).addTo(map);
|
||||||
|
|
||||||
|
// 为中间点添加弹窗
|
||||||
|
marker.bindPopup(`
|
||||||
|
<div style="padding: 10px;">
|
||||||
|
<h4 style="margin: 0 0 10px 0; color: #333;">点位 ${index + 1}</h4>
|
||||||
|
<p><strong>时间:</strong> ${currentPoint.track_time || '未知'}</p>
|
||||||
|
<p><strong>坐标:</strong> ${currentPoint.latitude}, ${
|
||||||
|
currentPoint.longitude
|
||||||
|
}</p>
|
||||||
|
</div>
|
||||||
|
`);
|
||||||
|
});
|
||||||
|
|
||||||
|
// 自动调整地图视图以显示整个轨迹
|
||||||
|
map.fitBounds(trackLine.getBounds());
|
||||||
|
};
|
||||||
|
|
||||||
|
const loadShow = (day: any) => {
|
||||||
|
// showTrack([
|
||||||
|
// { latitude: 22.566729, longitude: 114.062952 },
|
||||||
|
// { latitude: 22.567982, longitude: 114.06278 },
|
||||||
|
// { latitude: 22.569394, longitude: 114.062694 },
|
||||||
|
// ]);
|
||||||
|
Apis.Attendance.AttendanceEmployeeTracks.Heatmap({
|
||||||
|
company_employees_id: Number(id),
|
||||||
|
date: day, // 转换为Date类型
|
||||||
|
}).then((res) => {
|
||||||
|
console.log(res);
|
||||||
|
// 假设API返回的数据结构为{ data: [{ latitude, longitude, time }, ...] }
|
||||||
|
showTrack(res?.data);
|
||||||
|
// setTrackData(res.data || []);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
useEffect(() => {
|
||||||
|
// 初始化地图
|
||||||
|
initMap();
|
||||||
|
// 加载轨迹数据
|
||||||
|
loadShow(new Date(getTodayDate()));
|
||||||
|
|
||||||
|
// 组件卸载时清理地图实例
|
||||||
|
return () => {
|
||||||
|
if (mapInstanceRef.current) {
|
||||||
|
mapInstanceRef.current.remove();
|
||||||
|
mapInstanceRef.current = null;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}, [id]);
|
||||||
|
|
||||||
|
// // 当轨迹数据变化时,显示轨迹
|
||||||
|
// useEffect(() => {
|
||||||
|
// if (trackData.length > 0) {
|
||||||
|
// showTrack(trackData);
|
||||||
|
// }
|
||||||
|
// }, [trackData]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<MyPageContainer
|
||||||
|
enableTabs
|
||||||
|
tabKey="attendance-employee-tracks"
|
||||||
|
tabLabel={title}
|
||||||
|
title={title}
|
||||||
|
>
|
||||||
|
<ProCard
|
||||||
|
title={
|
||||||
|
<Button
|
||||||
|
type="link"
|
||||||
|
size="small"
|
||||||
|
icon={<LeftOutlined />}
|
||||||
|
onClick={() => navigate(-1)}
|
||||||
|
>
|
||||||
|
员工轨迹底图
|
||||||
|
</Button>
|
||||||
|
}
|
||||||
|
headerBordered
|
||||||
|
extra={
|
||||||
|
<DatePicker
|
||||||
|
defaultValue={dayjs(getTodayDate(), dateFormat)}
|
||||||
|
format={dateFormat}
|
||||||
|
onChange={(e: any, dateString: any) => {
|
||||||
|
loadShow(dateString);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
ref={mapRef}
|
||||||
|
style={{
|
||||||
|
width: '100%',
|
||||||
|
height: 'calc(100vh - 200px)',
|
||||||
|
borderRadius: '8px',
|
||||||
|
overflow: 'hidden',
|
||||||
|
border: '1px solid #e8e8e8',
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<Space
|
||||||
|
style={{
|
||||||
|
paddingTop: '15px',
|
||||||
|
fontSize: '12px',
|
||||||
|
display: 'flex',
|
||||||
|
alignItems: 'center',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
fontSize: '12px',
|
||||||
|
display: 'flex',
|
||||||
|
alignItems: 'center',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<span>起点:</span>
|
||||||
|
<img
|
||||||
|
src="https://gc-prod-1385694945.cos.ap-shanghai.myqcloud.com/uploads/cs-test/01KH0R6TZSPBNF3X8ARGXE5R6C.png"
|
||||||
|
width="18"
|
||||||
|
height="18"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
fontSize: '12px',
|
||||||
|
display: 'flex',
|
||||||
|
alignItems: 'center',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<span>终点:</span>
|
||||||
|
<img
|
||||||
|
src="https://gc-prod-1385694945.cos.ap-shanghai.myqcloud.com/uploads/cs-test/01KH0STS12KMARBH8F9SJVCN1S.png"
|
||||||
|
width="18"
|
||||||
|
height="18"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</Space>
|
||||||
|
</ProCard>
|
||||||
|
</MyPageContainer>
|
||||||
|
);
|
||||||
|
}
|
||||||
26
src/pages/attendance/employee_tracks/style.scss
Normal file
26
src/pages/attendance/employee_tracks/style.scss
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
.track-start-marker {
|
||||||
|
background-color: #007bff;
|
||||||
|
color: #fff;
|
||||||
|
width: 200px;
|
||||||
|
height: 200px;
|
||||||
|
border-radius: 100px;
|
||||||
|
font-size: 0.6em;
|
||||||
|
text-align: center;
|
||||||
|
line-height: 200px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
.track-end-marker {
|
||||||
|
background-color: #f00;
|
||||||
|
color: #fff;
|
||||||
|
width: 200px;
|
||||||
|
height: 200px;
|
||||||
|
border-radius: 100px;
|
||||||
|
font-size: 0.6em;
|
||||||
|
text-align: center;
|
||||||
|
line-height: 200px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
@ -130,8 +130,14 @@ export default function Index({ title = '员工管理' }) {
|
|||||||
search: false,
|
search: false,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: '系统角色',
|
title: '员工端角色',
|
||||||
dataIndex: 'roles',
|
dataIndex: 'employee_roles',
|
||||||
|
renderText: renderTextHelper.TagList,
|
||||||
|
hideInSearch: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '系统后台角色',
|
||||||
|
dataIndex: 'company_roles',
|
||||||
renderText: renderTextHelper.TagList,
|
renderText: renderTextHelper.TagList,
|
||||||
hideInSearch: true,
|
hideInSearch: true,
|
||||||
},
|
},
|
||||||
|
|||||||
@ -90,7 +90,14 @@ export default function Create(props: MyBetaModalFormProps) {
|
|||||||
placeholder: '请输入关键字搜索',
|
placeholder: '请输入关键字搜索',
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
SysSelects.SysRoles(),
|
SysSelects.SysEmployeeRoles({
|
||||||
|
title: '员工角色',
|
||||||
|
required: false,
|
||||||
|
}),
|
||||||
|
SysSelects.SysRoles({
|
||||||
|
title: '系统角色',
|
||||||
|
required: false,
|
||||||
|
}),
|
||||||
{
|
{
|
||||||
key: 'remark',
|
key: 'remark',
|
||||||
title: '备注',
|
title: '备注',
|
||||||
|
|||||||
@ -7,6 +7,7 @@ import {
|
|||||||
} from '@/common';
|
} from '@/common';
|
||||||
|
|
||||||
import { Selects } from '@/components/Select';
|
import { Selects } from '@/components/Select';
|
||||||
|
import { SysSelects } from '@/components/SysSelects';
|
||||||
import { Apis } from '@/gen/Apis';
|
import { Apis } from '@/gen/Apis';
|
||||||
import { SexEnum } from '@/gen/Enums';
|
import { SexEnum } from '@/gen/Enums';
|
||||||
import { BetaSchemaForm } from '@ant-design/pro-components';
|
import { BetaSchemaForm } from '@ant-design/pro-components';
|
||||||
@ -29,7 +30,10 @@ export default function Update(props: MyBetaModalFormProps) {
|
|||||||
if (open && props.item) {
|
if (open && props.item) {
|
||||||
form.setFieldsValue({
|
form.setFieldsValue({
|
||||||
...props.item,
|
...props.item,
|
||||||
roles_id: props.item?.roles?.map((item: any) => item.value),
|
roles_id: props.item?.company_roles?.map((item: any) => item.value),
|
||||||
|
employee_roles_id: props.item?.employee_roles?.map(
|
||||||
|
(item: any) => item.value,
|
||||||
|
),
|
||||||
positions_id: props.item?.positions_id ?? '',
|
positions_id: props.item?.positions_id ?? '',
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -92,7 +96,36 @@ export default function Update(props: MyBetaModalFormProps) {
|
|||||||
placeholder: '请输入关键字搜索',
|
placeholder: '请输入关键字搜索',
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
|
SysSelects.SysEmployeeRoles({
|
||||||
|
title: '员工角色',
|
||||||
|
required: false,
|
||||||
|
fieldProps: {
|
||||||
|
labelRender: (res: any) => {
|
||||||
|
if (res?.label) {
|
||||||
|
return res?.label;
|
||||||
|
} else {
|
||||||
|
return props.item?.employee_roles?.map(
|
||||||
|
(item: any) => item.label,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
SysSelects.SysRoles({
|
||||||
|
title: '系统角色',
|
||||||
|
required: false,
|
||||||
|
fieldProps: {
|
||||||
|
labelRender: (res: any) => {
|
||||||
|
if (res?.label) {
|
||||||
|
return res?.label;
|
||||||
|
} else {
|
||||||
|
return props.item?.company_roles?.map(
|
||||||
|
(item: any) => item.label,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}),
|
||||||
// {
|
// {
|
||||||
// key: 'password',
|
// key: 'password',
|
||||||
// title: '密码',
|
// title: '密码',
|
||||||
|
|||||||
@ -5,6 +5,7 @@ import { ProTable } from '@ant-design/pro-components';
|
|||||||
import { Space } from 'antd';
|
import { Space } from 'antd';
|
||||||
import { useNavigate } from 'umi';
|
import { useNavigate } from 'umi';
|
||||||
import Audit from './modals/Audit';
|
import Audit from './modals/Audit';
|
||||||
|
import Show from './modals/Show';
|
||||||
|
|
||||||
export default function Index({ title = '登记审核' }) {
|
export default function Index({ title = '登记审核' }) {
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
@ -89,6 +90,7 @@ export default function Index({ title = '登记审核' }) {
|
|||||||
render: (_, item: any, index, action) => (
|
render: (_, item: any, index, action) => (
|
||||||
<Space key={index}>
|
<Space key={index}>
|
||||||
<Audit item={item} reload={action?.reload} title={title} />
|
<Audit item={item} reload={action?.reload} title={title} />
|
||||||
|
<Show item={item} title={title} />,
|
||||||
</Space>
|
</Space>
|
||||||
),
|
),
|
||||||
}),
|
}),
|
||||||
|
|||||||
232
src/pages/customer/house_registers_audit/modals/Show.tsx
Normal file
232
src/pages/customer/house_registers_audit/modals/Show.tsx
Normal file
@ -0,0 +1,232 @@
|
|||||||
|
import {
|
||||||
|
MyBetaModalFormProps,
|
||||||
|
MyButtons,
|
||||||
|
MyColumns,
|
||||||
|
MyModalFormProps,
|
||||||
|
MyProTableProps,
|
||||||
|
renderTextHelper,
|
||||||
|
} from '@/common';
|
||||||
|
|
||||||
|
import {
|
||||||
|
HouseOccupantsCardTypeEnum,
|
||||||
|
HouseOccupantsResidentialRelationEnum,
|
||||||
|
HouseRegistersTypeEnum,
|
||||||
|
} from '@/gen/Enums';
|
||||||
|
import {
|
||||||
|
BetaSchemaForm,
|
||||||
|
ProCard,
|
||||||
|
ProDescriptions,
|
||||||
|
ProTable,
|
||||||
|
} from '@ant-design/pro-components';
|
||||||
|
import { useNavigate } from '@umijs/max';
|
||||||
|
import { Form, Image, Space } from 'antd';
|
||||||
|
export default function Show(props: MyBetaModalFormProps) {
|
||||||
|
const [form] = Form.useForm();
|
||||||
|
const navigate = useNavigate();
|
||||||
|
|
||||||
|
return (
|
||||||
|
<BetaSchemaForm<ApiTypes.Archive.HouseRegisters.Show>
|
||||||
|
{...MyModalFormProps.props}
|
||||||
|
title={props.title}
|
||||||
|
trigger={<MyButtons.Default title="查看" type="primary" />}
|
||||||
|
wrapperCol={{ span: 24 }}
|
||||||
|
width="800px"
|
||||||
|
modalProps={{
|
||||||
|
bodyStyle: { maxHeight: '70vh', overflowY: 'auto' },
|
||||||
|
}}
|
||||||
|
key={new Date().getTime()}
|
||||||
|
form={form}
|
||||||
|
columns={[
|
||||||
|
{
|
||||||
|
// title: '登记信息',
|
||||||
|
dataIndex: 'info_display',
|
||||||
|
valueType: 'text',
|
||||||
|
renderFormItem: () => (
|
||||||
|
<Space direction="vertical" style={{ width: '100%' }}>
|
||||||
|
<ProCard size="small">
|
||||||
|
<ProDescriptions bordered size="small" column={1}>
|
||||||
|
<ProDescriptions.Item label="房屋信息">
|
||||||
|
<a
|
||||||
|
onClick={() =>
|
||||||
|
navigate(
|
||||||
|
`/customer/archive/show/${props?.item?.model_id}`,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
>
|
||||||
|
{props?.item?.asset_house?.full_name || '-'}
|
||||||
|
</a>
|
||||||
|
</ProDescriptions.Item>
|
||||||
|
<ProDescriptions.Item label="登记类型">
|
||||||
|
<renderTextHelper.Tag
|
||||||
|
Enums={HouseRegistersTypeEnum}
|
||||||
|
value={props?.item?.type}
|
||||||
|
/>
|
||||||
|
</ProDescriptions.Item>
|
||||||
|
|
||||||
|
<ProDescriptions.Item label="申请时间">
|
||||||
|
{props?.item?.created_at || '-'}
|
||||||
|
</ProDescriptions.Item>
|
||||||
|
</ProDescriptions>
|
||||||
|
</ProCard>
|
||||||
|
|
||||||
|
{props?.item?.customer_info &&
|
||||||
|
props?.item?.customer_info?.length > 0 && (
|
||||||
|
<ProCard size="small">
|
||||||
|
<ProTable
|
||||||
|
{...MyProTableProps.props}
|
||||||
|
search={false}
|
||||||
|
toolBarRender={false}
|
||||||
|
pagination={false}
|
||||||
|
dataSource={props?.item?.customer_info}
|
||||||
|
rowKey={(record, index) => record?.id_card || index}
|
||||||
|
size="small"
|
||||||
|
columns={[
|
||||||
|
MyColumns.EnumTag({
|
||||||
|
title: '关联身份',
|
||||||
|
dataIndex: 'residential_relation',
|
||||||
|
valueEnum: HouseOccupantsResidentialRelationEnum,
|
||||||
|
}),
|
||||||
|
{
|
||||||
|
title: '姓名',
|
||||||
|
dataIndex: 'name',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '手机号',
|
||||||
|
dataIndex: 'phone',
|
||||||
|
},
|
||||||
|
MyColumns.EnumTag({
|
||||||
|
title: '证件类型',
|
||||||
|
dataIndex: 'card_type',
|
||||||
|
valueEnum: HouseOccupantsCardTypeEnum,
|
||||||
|
}),
|
||||||
|
{
|
||||||
|
title: '证件号码',
|
||||||
|
dataIndex: 'id_card',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '证件资料',
|
||||||
|
render: (_, item) => {
|
||||||
|
return (
|
||||||
|
<Space>
|
||||||
|
{item?.card_front_image?.[0] && (
|
||||||
|
<Image
|
||||||
|
height={30}
|
||||||
|
style={{
|
||||||
|
width: 'auto',
|
||||||
|
objectFit: 'contain',
|
||||||
|
}}
|
||||||
|
src={item?.card_front_image[0]?.url}
|
||||||
|
placeholder="正面"
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
{item?.card_back_image?.[0] && (
|
||||||
|
<Image
|
||||||
|
height={30}
|
||||||
|
style={{
|
||||||
|
width: 'auto',
|
||||||
|
objectFit: 'contain',
|
||||||
|
}}
|
||||||
|
src={item?.card_back_image[0]?.url}
|
||||||
|
placeholder="反面"
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</Space>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
]}
|
||||||
|
/>
|
||||||
|
</ProCard>
|
||||||
|
)}
|
||||||
|
{(props?.item?.type === 'UpdateInfo' ||
|
||||||
|
props?.item?.type === 'UpdatePhone') && (
|
||||||
|
<ProCard title="更新信息" size="small">
|
||||||
|
<ProTable
|
||||||
|
{...MyProTableProps.props}
|
||||||
|
search={false}
|
||||||
|
toolBarRender={false}
|
||||||
|
pagination={false}
|
||||||
|
dataSource={[props?.item?.update_info]}
|
||||||
|
rowKey={(record, index) => record?.id_card || index}
|
||||||
|
size="small"
|
||||||
|
columns={[
|
||||||
|
{
|
||||||
|
title: '姓名',
|
||||||
|
dataIndex: 'name',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '手机号',
|
||||||
|
dataIndex: 'phone',
|
||||||
|
},
|
||||||
|
MyColumns.EnumTag({
|
||||||
|
title: '证件类型',
|
||||||
|
dataIndex: 'card_type',
|
||||||
|
valueEnum: HouseOccupantsCardTypeEnum,
|
||||||
|
}),
|
||||||
|
{
|
||||||
|
title: '证件号码',
|
||||||
|
dataIndex: 'id_card',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '证件资料',
|
||||||
|
render: (_, item) => {
|
||||||
|
return (
|
||||||
|
<Space>
|
||||||
|
{item?.card_front_image?.[0] && (
|
||||||
|
<Image
|
||||||
|
height={30}
|
||||||
|
style={{
|
||||||
|
width: 'auto',
|
||||||
|
objectFit: 'contain',
|
||||||
|
}}
|
||||||
|
src={item?.card_front_image[0]?.url}
|
||||||
|
placeholder="正面"
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
{item?.card_back_image?.[0] && (
|
||||||
|
<Image
|
||||||
|
height={30}
|
||||||
|
style={{
|
||||||
|
width: 'auto',
|
||||||
|
objectFit: 'contain',
|
||||||
|
}}
|
||||||
|
src={item?.card_back_image[0]?.url}
|
||||||
|
placeholder="反面"
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</Space>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
]}
|
||||||
|
/>
|
||||||
|
</ProCard>
|
||||||
|
)}
|
||||||
|
{props?.item?.type === 'AddOwner' &&
|
||||||
|
props?.item?.ownership_info &&
|
||||||
|
props?.item?.ownership_info?.length > 0 && (
|
||||||
|
<ProCard title="产证信息" size="small">
|
||||||
|
<Image.PreviewGroup>
|
||||||
|
<Space wrap>
|
||||||
|
{props?.item?.ownership_info?.map(
|
||||||
|
(res: any, index: number) => (
|
||||||
|
<Image
|
||||||
|
key={`${res?.name}_${index}`}
|
||||||
|
height={50}
|
||||||
|
src={res?.url || ''}
|
||||||
|
placeholder="产证资料"
|
||||||
|
/>
|
||||||
|
),
|
||||||
|
)}
|
||||||
|
</Space>
|
||||||
|
</Image.PreviewGroup>
|
||||||
|
</ProCard>
|
||||||
|
)}
|
||||||
|
</Space>
|
||||||
|
),
|
||||||
|
colProps: { span: 24 },
|
||||||
|
},
|
||||||
|
]}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
@ -164,6 +164,7 @@
|
|||||||
margin-bottom: 8px;
|
margin-bottom: 8px;
|
||||||
font-size: 16px;
|
font-size: 16px;
|
||||||
color: #1f2937;
|
color: #1f2937;
|
||||||
|
white-space: nowrap;
|
||||||
}
|
}
|
||||||
|
|
||||||
> div:last-child {
|
> div:last-child {
|
||||||
|
|||||||
@ -8,7 +8,7 @@
|
|||||||
background-color: #fff;
|
background-color: #fff;
|
||||||
padding: 20px;
|
padding: 20px;
|
||||||
border-radius: 8px;
|
border-radius: 8px;
|
||||||
height: 92vh;
|
min-height: 92vh;
|
||||||
}
|
}
|
||||||
.work_card_number {
|
.work_card_number {
|
||||||
color: #333333;
|
color: #333333;
|
||||||
|
|||||||
@ -5,7 +5,7 @@ export default function Login() {
|
|||||||
<div>
|
<div>
|
||||||
<MyLoginPage />
|
<MyLoginPage />
|
||||||
<div className="filing_info">
|
<div className="filing_info">
|
||||||
零壹科技有限公司 |
|
邻壹科技有限公司 |
|
||||||
<a
|
<a
|
||||||
href="https://beian.miit.gov.cn/#/Integrated/index"
|
href="https://beian.miit.gov.cn/#/Integrated/index"
|
||||||
target="_blank"
|
target="_blank"
|
||||||
|
|||||||
53
src/pages/patrol/index.tsx
Normal file
53
src/pages/patrol/index.tsx
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
import { MyPageContainer } from '@/common';
|
||||||
|
import MyPatrolWork from '@/pages/work_order/patrol_work';
|
||||||
|
import { useSearchParams } from '@umijs/max';
|
||||||
|
import { Tabs } from 'antd';
|
||||||
|
import { useEffect, useState } from 'react';
|
||||||
|
import MyPatrolLocations from './patrol_locations';
|
||||||
|
import MyPatrolRoutes from './patrol_routes';
|
||||||
|
import MyPatrolTasks from './patrol_tasks';
|
||||||
|
|
||||||
|
export default function Index({ title = '安防巡更' }) {
|
||||||
|
const [searchParams] = useSearchParams();
|
||||||
|
const [activeKey, setActiveKey] = useState('MyPatrolTasks');
|
||||||
|
|
||||||
|
const items = [
|
||||||
|
{
|
||||||
|
key: '1',
|
||||||
|
label: '巡更任务',
|
||||||
|
children: <MyPatrolTasks />,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: '2',
|
||||||
|
label: '路线配置',
|
||||||
|
children: <MyPatrolRoutes />,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: '3',
|
||||||
|
label: '点位配置',
|
||||||
|
children: <MyPatrolLocations />,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: '4',
|
||||||
|
label: '巡更工单',
|
||||||
|
children: <MyPatrolWork />,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (searchParams?.get('key')) {
|
||||||
|
setActiveKey(searchParams?.get('key') || '1');
|
||||||
|
}
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<MyPageContainer
|
||||||
|
title={title}
|
||||||
|
enableTabs={true}
|
||||||
|
tabKey="patrol_page"
|
||||||
|
tabLabel={title}
|
||||||
|
>
|
||||||
|
<Tabs type="card" defaultActiveKey={activeKey} items={items} />
|
||||||
|
</MyPageContainer>
|
||||||
|
);
|
||||||
|
}
|
||||||
244
src/pages/patrol/patrol_locations/index.tsx
Normal file
244
src/pages/patrol/patrol_locations/index.tsx
Normal file
@ -0,0 +1,244 @@
|
|||||||
|
import { MyButtons, MyColumns, MyProTableProps } from '@/common';
|
||||||
|
import { Selects } from '@/components/Select';
|
||||||
|
import { Apis } from '@/gen/Apis';
|
||||||
|
import { ProTable } from '@ant-design/pro-components';
|
||||||
|
import { message, Progress, Space } from 'antd';
|
||||||
|
import { saveAs } from 'file-saver';
|
||||||
|
import JSZip from 'jszip';
|
||||||
|
import { useState } from 'react';
|
||||||
|
import Create from './modals/Create';
|
||||||
|
import Update from './modals/Update';
|
||||||
|
interface DataType {
|
||||||
|
key?: React.Key;
|
||||||
|
id?: React.Key;
|
||||||
|
}
|
||||||
|
export default function Index({ title = '点位配置' }) {
|
||||||
|
const [count, setCount] = useState(0);
|
||||||
|
const [selectedRowKeys, setSelectedRowKeys] = useState<React.Key[]>([]);
|
||||||
|
const [downloadProgress, setDownloadProgress] = useState(0);
|
||||||
|
const [downloading, setDownloading] = useState(false);
|
||||||
|
const [getSelectedRow, setSelectedRow] = useState<any>([]);
|
||||||
|
const rowSelection: any = {
|
||||||
|
selectedRowKeys,
|
||||||
|
onChange: (selectedRowKeys: React.Key[], selectedRows: DataType[]) => {
|
||||||
|
console.log(selectedRows, 'selectedRows[0]');
|
||||||
|
if (selectedRowKeys.length > 10) {
|
||||||
|
message.warning('最多批量下载10个');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
setSelectedRowKeys(selectedRowKeys);
|
||||||
|
setSelectedRow(selectedRows);
|
||||||
|
},
|
||||||
|
getCheckboxProps: (record: any) => ({
|
||||||
|
disabled: !record.is_enabled,
|
||||||
|
}),
|
||||||
|
onSelectAll: (selected: boolean, selectedRows: DataType[]) => {
|
||||||
|
if (selected && selectedRows.length > 10) {
|
||||||
|
message.warning('全选数量超过10个!');
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
defaultSelectedRowKeys: !count
|
||||||
|
? []
|
||||||
|
: getSelectedRow?.map((item: any) => item.id),
|
||||||
|
};
|
||||||
|
|
||||||
|
// 模拟获取二维码接口
|
||||||
|
const getQRCode = async (id?: string) => {
|
||||||
|
let res = await Apis.Patrol.PatrolLocations.PatrolLocationQrCode({
|
||||||
|
id: Number(id),
|
||||||
|
});
|
||||||
|
return res?.data?.qr_code;
|
||||||
|
};
|
||||||
|
|
||||||
|
// 生成带海报的二维码
|
||||||
|
const generatePosterQR = async (base64: string, res: any) => {
|
||||||
|
return new Promise((resolve) => {
|
||||||
|
const canvas = document.createElement('canvas');
|
||||||
|
const ctx: any = canvas.getContext('2d');
|
||||||
|
|
||||||
|
canvas.width = 500;
|
||||||
|
canvas.height = 800;
|
||||||
|
|
||||||
|
// 海报样式
|
||||||
|
ctx.fillStyle = '#1890ff';
|
||||||
|
ctx.fillRect(0, 0, canvas.width, canvas.height);
|
||||||
|
|
||||||
|
ctx.fillStyle = 'white';
|
||||||
|
ctx.font = 'bold 36px Arial';
|
||||||
|
ctx.textAlign = 'center';
|
||||||
|
ctx.fillText(`${res?.name}`, canvas.width / 2, 130);
|
||||||
|
|
||||||
|
const qrImage = new Image();
|
||||||
|
qrImage.onload = () => {
|
||||||
|
const qrSize = 300;
|
||||||
|
const qrX = (canvas.width - qrSize) / 2;
|
||||||
|
const qrY = 210;
|
||||||
|
ctx.drawImage(qrImage, qrX, qrY, qrSize, qrSize);
|
||||||
|
|
||||||
|
ctx.font = '20px Arial';
|
||||||
|
ctx.fillText('扫描二维码完成签到', canvas.width / 2, 570);
|
||||||
|
ctx.font = '16px Arial';
|
||||||
|
ctx.fillText(res?.code, canvas.width / 2, 605);
|
||||||
|
|
||||||
|
resolve(canvas.toDataURL('image/png'));
|
||||||
|
};
|
||||||
|
qrImage.src = base64;
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const download = async (type: number) => {
|
||||||
|
if (!getSelectedRow?.length) {
|
||||||
|
message.error('请选择巡逻位置!');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
setDownloading(true);
|
||||||
|
setDownloadProgress(0);
|
||||||
|
try {
|
||||||
|
const zip = new JSZip();
|
||||||
|
const total = getSelectedRow.length;
|
||||||
|
for (let i = 0; i < total; i++) {
|
||||||
|
let res = getSelectedRow?.[i];
|
||||||
|
if (res?.id) {
|
||||||
|
await new Promise((resolve: any) => {
|
||||||
|
setTimeout(resolve, 1000);
|
||||||
|
});
|
||||||
|
const qrCode = await getQRCode(res?.id);
|
||||||
|
if (type === 1) {
|
||||||
|
const base64Data = qrCode.split(',')[1];
|
||||||
|
zip.file(`${res?.name}_${res?.code}.png`, base64Data, {
|
||||||
|
base64: true,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if (type === 2) {
|
||||||
|
// 带海报的二维码
|
||||||
|
const posterData: any = await generatePosterQR(qrCode, res);
|
||||||
|
const fileData = posterData.split(',')[1];
|
||||||
|
zip.file(`${res?.name}_${res?.code}.png`, fileData, {
|
||||||
|
base64: true,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
setCount(i + 1);
|
||||||
|
const progress = Math.round(((i + 1) / total) * 100);
|
||||||
|
setDownloadProgress(progress);
|
||||||
|
// 添加延迟
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const content = await zip.generateAsync({ type: 'blob' });
|
||||||
|
saveAs(content, `扫码签到二维码.zip`);
|
||||||
|
setTimeout(() => {
|
||||||
|
setCount(0);
|
||||||
|
}, 1000);
|
||||||
|
} catch (error) {
|
||||||
|
console.error('下载失败:', error);
|
||||||
|
message.error('下载失败,请重试');
|
||||||
|
} finally {
|
||||||
|
setDownloading(false);
|
||||||
|
setDownloadProgress(0);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<ProTable
|
||||||
|
{...MyProTableProps.props}
|
||||||
|
loading={{
|
||||||
|
spinning: downloading,
|
||||||
|
tip: `正在生成二维码... ${downloadProgress}%`,
|
||||||
|
indicator: (
|
||||||
|
<Progress type="circle" percent={downloadProgress} size={20} />
|
||||||
|
),
|
||||||
|
}}
|
||||||
|
request={async (params, sort) =>
|
||||||
|
MyProTableProps.request(params, sort, Apis.Patrol.PatrolLocations.List)
|
||||||
|
}
|
||||||
|
rowSelection={{ type: 'checkbox', ...rowSelection }}
|
||||||
|
toolBarRender={(action) => [
|
||||||
|
<MyButtons.Default
|
||||||
|
key="download_qrcode"
|
||||||
|
size="middle"
|
||||||
|
title={`下载纯二维码${
|
||||||
|
getSelectedRow?.length ? `${count}/${getSelectedRow?.length}` : ''
|
||||||
|
}`}
|
||||||
|
onClick={() => download(1)}
|
||||||
|
/>,
|
||||||
|
<MyButtons.Default
|
||||||
|
key="download_qrcode"
|
||||||
|
size="middle"
|
||||||
|
title={`下载海报二维码${
|
||||||
|
getSelectedRow?.length ? `${count}/${getSelectedRow?.length}` : ''
|
||||||
|
}`}
|
||||||
|
onClick={() => download(2)}
|
||||||
|
/>,
|
||||||
|
<Create key="Create" reload={action?.reload} title={title} />,
|
||||||
|
]}
|
||||||
|
columns={[
|
||||||
|
MyColumns.ID({
|
||||||
|
search: false,
|
||||||
|
}),
|
||||||
|
Selects?.AssetProjects({
|
||||||
|
title: '选择项目',
|
||||||
|
key: 'asset_projects_id',
|
||||||
|
hidden: true,
|
||||||
|
}),
|
||||||
|
{
|
||||||
|
title: '关联项目',
|
||||||
|
dataIndex: ['asset_project', 'name'],
|
||||||
|
search: {
|
||||||
|
transform: (value) => {
|
||||||
|
return { project_name: value };
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '点位名称',
|
||||||
|
dataIndex: 'name',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '位置编码',
|
||||||
|
dataIndex: 'code',
|
||||||
|
},
|
||||||
|
MyColumns.Boolean({
|
||||||
|
dataIndex: 'is_enabled',
|
||||||
|
title: '启用/禁用',
|
||||||
|
search: false,
|
||||||
|
}),
|
||||||
|
MyColumns.CreatedAt(),
|
||||||
|
MyColumns.Option({
|
||||||
|
render: (_, item: any, index, action) => (
|
||||||
|
<Space key={index}>
|
||||||
|
<Update item={item} reload={action?.reload} title={title} />
|
||||||
|
<MyButtons.Default
|
||||||
|
title={item.is_enabled === 1 ? '禁用' : '启用'}
|
||||||
|
type={item.is_enabled === 1 ? 'default' : 'primary'}
|
||||||
|
danger={item.is_enabled === 1}
|
||||||
|
isConfirm={true}
|
||||||
|
description={
|
||||||
|
item.is_enabled === 1
|
||||||
|
? `确定要禁用点位吗?`
|
||||||
|
: `确定要启用点位吗?`
|
||||||
|
}
|
||||||
|
onConfirm={() =>
|
||||||
|
Apis.Patrol.PatrolLocations.Update({
|
||||||
|
id: item.id ?? 0,
|
||||||
|
is_enabled: item.is_enabled === 1 ? 0 : 1,
|
||||||
|
name: item.name,
|
||||||
|
code: item.code,
|
||||||
|
asset_projects_id: item.asset_projects_id,
|
||||||
|
}).then(() => action?.reload())
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<MyButtons.Delete
|
||||||
|
onConfirm={() =>
|
||||||
|
Apis.Patrol.PatrolLocations.Delete({ id: item.id }).then(() =>
|
||||||
|
action?.reload(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</Space>
|
||||||
|
),
|
||||||
|
}),
|
||||||
|
]}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
80
src/pages/patrol/patrol_locations/modals/Create.tsx
Normal file
80
src/pages/patrol/patrol_locations/modals/Create.tsx
Normal file
@ -0,0 +1,80 @@
|
|||||||
|
import {
|
||||||
|
MyBetaModalFormProps,
|
||||||
|
MyButtons,
|
||||||
|
MyModalFormProps,
|
||||||
|
rulesHelper,
|
||||||
|
} from '@/common';
|
||||||
|
import { Selects } from '@/components/Select';
|
||||||
|
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.Patrol.PatrolLocations.Store>
|
||||||
|
{...MyModalFormProps.props}
|
||||||
|
title={`添加${props.title}`}
|
||||||
|
layout="horizontal"
|
||||||
|
labelCol={{ span: 5 }}
|
||||||
|
wrapperCol={{ span: 19 }}
|
||||||
|
labelAlign="left"
|
||||||
|
width="600px"
|
||||||
|
key={new Date().getTime()}
|
||||||
|
form={form}
|
||||||
|
onOpenChange={(open: any) => {
|
||||||
|
if (open) {
|
||||||
|
form.resetFields(); // 清空表单数据
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
trigger={<MyButtons.Create title={`添加${props.title}`} />}
|
||||||
|
onFinish={async (values: any) =>
|
||||||
|
Apis.Patrol.PatrolLocations.Store({
|
||||||
|
...values,
|
||||||
|
longitude: values?.location ? values.location.split(',')[0] : '',
|
||||||
|
latitude: values?.location ? values.location.split(',')[1] : '',
|
||||||
|
is_enabled: values.is_enabled ? 1 : 0,
|
||||||
|
})
|
||||||
|
.then(() => {
|
||||||
|
props.reload?.();
|
||||||
|
message.success(props.title + '成功');
|
||||||
|
return true;
|
||||||
|
})
|
||||||
|
.catch(() => false)
|
||||||
|
}
|
||||||
|
columns={[
|
||||||
|
Selects?.AssetProjects({
|
||||||
|
title: '关联项目',
|
||||||
|
key: 'asset_projects_id',
|
||||||
|
colProps: { span: 24 },
|
||||||
|
formItemProps: { ...rulesHelper.text },
|
||||||
|
}),
|
||||||
|
{
|
||||||
|
key: 'name',
|
||||||
|
title: '点位名称',
|
||||||
|
formItemProps: { ...rulesHelper.text },
|
||||||
|
colProps: { span: 24 },
|
||||||
|
fieldProps: {
|
||||||
|
placeholder: '请输入点位名称',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '位置信息',
|
||||||
|
key: 'remark',
|
||||||
|
formItemProps: { ...rulesHelper.text },
|
||||||
|
valueType: 'textarea',
|
||||||
|
colProps: { span: 24 },
|
||||||
|
fieldProps: {
|
||||||
|
placeholder: '请输入点位的具体位置信息',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '是否启用',
|
||||||
|
key: 'is_enabled',
|
||||||
|
valueType: 'switch',
|
||||||
|
colProps: { span: 24 },
|
||||||
|
},
|
||||||
|
]}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
86
src/pages/patrol/patrol_locations/modals/Update.tsx
Normal file
86
src/pages/patrol/patrol_locations/modals/Update.tsx
Normal file
@ -0,0 +1,86 @@
|
|||||||
|
import {
|
||||||
|
MyBetaModalFormProps,
|
||||||
|
MyButtons,
|
||||||
|
MyModalFormProps,
|
||||||
|
rulesHelper,
|
||||||
|
} from '@/common';
|
||||||
|
import { Selects } from '@/components/Select';
|
||||||
|
import { Apis } from '@/gen/Apis';
|
||||||
|
import { BetaSchemaForm } from '@ant-design/pro-components';
|
||||||
|
import { Form, message } from 'antd';
|
||||||
|
|
||||||
|
export default function Update(props: MyBetaModalFormProps) {
|
||||||
|
const [form] = Form.useForm();
|
||||||
|
return (
|
||||||
|
<BetaSchemaForm<ApiTypes.Patrol.PatrolLocations.Update>
|
||||||
|
{...MyModalFormProps.props}
|
||||||
|
title={`编辑${props.title}`}
|
||||||
|
trigger={<MyButtons.Edit />}
|
||||||
|
key={new Date().getTime()}
|
||||||
|
layout="horizontal"
|
||||||
|
labelCol={{ span: 5 }}
|
||||||
|
wrapperCol={{ span: 19 }}
|
||||||
|
labelAlign="left"
|
||||||
|
width="600px"
|
||||||
|
form={form}
|
||||||
|
onOpenChange={(open: any) => {
|
||||||
|
if (open && props.item) {
|
||||||
|
form.setFieldsValue({
|
||||||
|
...props.item,
|
||||||
|
location: `${props.item?.longitude || ''},${
|
||||||
|
props.item?.latitude || ''
|
||||||
|
}`,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
onFinish={async (values: any) =>
|
||||||
|
Apis.Patrol.PatrolLocations.Update({
|
||||||
|
...values,
|
||||||
|
longitude: values?.location ? values.location.split(',')[0] : '',
|
||||||
|
latitude: values?.location ? values.location.split(',')[1] : '',
|
||||||
|
is_enabled: values.is_enabled ? 1 : 0,
|
||||||
|
id: props.item?.id ?? 0,
|
||||||
|
})
|
||||||
|
.then(() => {
|
||||||
|
props.reload?.();
|
||||||
|
message.success(props.title + '编辑成功');
|
||||||
|
return true;
|
||||||
|
})
|
||||||
|
.catch(() => false)
|
||||||
|
}
|
||||||
|
columns={[
|
||||||
|
Selects?.AssetProjects({
|
||||||
|
title: '选择项目',
|
||||||
|
key: 'asset_projects_id',
|
||||||
|
colProps: { span: 24 },
|
||||||
|
formItemProps: { ...rulesHelper.text },
|
||||||
|
}),
|
||||||
|
{
|
||||||
|
key: 'name',
|
||||||
|
title: '点位名称',
|
||||||
|
formItemProps: { ...rulesHelper.text },
|
||||||
|
colProps: { span: 24 },
|
||||||
|
fieldProps: {
|
||||||
|
placeholder: '请输入点位名称',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '位置信息',
|
||||||
|
key: 'remark',
|
||||||
|
formItemProps: { ...rulesHelper.text },
|
||||||
|
valueType: 'textarea',
|
||||||
|
colProps: { span: 24 },
|
||||||
|
fieldProps: {
|
||||||
|
placeholder: '请输入点位的具体位置信息',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '是否启用',
|
||||||
|
key: 'is_enabled',
|
||||||
|
valueType: 'switch',
|
||||||
|
colProps: { span: 24 },
|
||||||
|
},
|
||||||
|
]}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
163
src/pages/patrol/patrol_routes/index.tsx
Normal file
163
src/pages/patrol/patrol_routes/index.tsx
Normal file
@ -0,0 +1,163 @@
|
|||||||
|
import { MyButtons, MyColumns, MyProTableProps } from '@/common';
|
||||||
|
import { showTime } from '@/common/utils/day';
|
||||||
|
import { Selects } from '@/components/Select';
|
||||||
|
import { Apis } from '@/gen/Apis';
|
||||||
|
import { PatrolRoutesGenerationMethodEnum } from '@/gen/Enums';
|
||||||
|
import { ProTable } from '@ant-design/pro-components';
|
||||||
|
import { Space, Tooltip } from 'antd';
|
||||||
|
import Create from './modals/Create';
|
||||||
|
import Update from './modals/Update';
|
||||||
|
|
||||||
|
export default function Index() {
|
||||||
|
return (
|
||||||
|
<ProTable
|
||||||
|
{...MyProTableProps.props}
|
||||||
|
request={async (params, sort) =>
|
||||||
|
MyProTableProps.request(params, sort, Apis.Patrol.PatrolRoutes.List)
|
||||||
|
}
|
||||||
|
toolBarRender={(action) => [
|
||||||
|
<Create key="Create" reload={action?.reload} title="路线" />,
|
||||||
|
]}
|
||||||
|
columns={[
|
||||||
|
MyColumns.ID({
|
||||||
|
search: false,
|
||||||
|
}),
|
||||||
|
Selects?.AssetProjects({
|
||||||
|
title: '选择项目',
|
||||||
|
key: 'asset_projects_id',
|
||||||
|
hidden: true,
|
||||||
|
}),
|
||||||
|
{
|
||||||
|
title: '关联项目',
|
||||||
|
dataIndex: ['asset_project', 'name'],
|
||||||
|
// search: {
|
||||||
|
// transform: (value) => {
|
||||||
|
// return { project_name: value };
|
||||||
|
// },
|
||||||
|
// },
|
||||||
|
search: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '路线名称',
|
||||||
|
dataIndex: 'name',
|
||||||
|
search: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '点位数',
|
||||||
|
render: (_, item: any) => {
|
||||||
|
const locations = item?.patrol_route_locations || [];
|
||||||
|
const locationNames = locations
|
||||||
|
.map((res: any) => {
|
||||||
|
return res?.patrol_location?.name || '';
|
||||||
|
})
|
||||||
|
.filter(Boolean);
|
||||||
|
const pointCount = locationNames.length;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Tooltip
|
||||||
|
title={
|
||||||
|
locationNames.map((name: string, index: number) => (
|
||||||
|
<div key={index}>
|
||||||
|
点位{index + 1}:{name}
|
||||||
|
</div>
|
||||||
|
)) || ''
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
whiteSpace: 'nowrap',
|
||||||
|
overflow: 'hidden',
|
||||||
|
textOverflow: 'ellipsis',
|
||||||
|
maxWidth: '150px',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{pointCount}
|
||||||
|
</div>
|
||||||
|
</Tooltip>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
MyColumns.EnumTag({
|
||||||
|
title: '巡更周期',
|
||||||
|
dataIndex: 'generation_method',
|
||||||
|
valueEnum: PatrolRoutesGenerationMethodEnum,
|
||||||
|
}),
|
||||||
|
{
|
||||||
|
title: '周期',
|
||||||
|
search: false,
|
||||||
|
|
||||||
|
render: (_, item: any) => {
|
||||||
|
if (
|
||||||
|
item?.generation_method ===
|
||||||
|
PatrolRoutesGenerationMethodEnum.Daily.value
|
||||||
|
) {
|
||||||
|
return '每天';
|
||||||
|
} else if (
|
||||||
|
item?.generation_method ===
|
||||||
|
PatrolRoutesGenerationMethodEnum.Weekly.value
|
||||||
|
) {
|
||||||
|
const dateArray = item?.date || [];
|
||||||
|
if (dateArray.length === 0) return '每周';
|
||||||
|
// 检查dateArray中的元素是否为对象,若是则提取weekday属性
|
||||||
|
const weekdays = dateArray
|
||||||
|
.map((res: any) => {
|
||||||
|
if (typeof res === 'object' && res !== null) {
|
||||||
|
return res?.weekday || '';
|
||||||
|
}
|
||||||
|
return res;
|
||||||
|
})
|
||||||
|
.filter(Boolean);
|
||||||
|
return `每周${weekdays.join('/')}`;
|
||||||
|
} else if (
|
||||||
|
item?.generation_method ===
|
||||||
|
PatrolRoutesGenerationMethodEnum.Monthly.value
|
||||||
|
) {
|
||||||
|
const dateArray = item?.date || [];
|
||||||
|
if (dateArray.length === 0) return '每月';
|
||||||
|
return `每月${dateArray.join('/')}`;
|
||||||
|
}
|
||||||
|
return '';
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '任务时段',
|
||||||
|
search: false,
|
||||||
|
render: (_, item: any) => {
|
||||||
|
return `${showTime(item.task_start_time)}-${showTime(
|
||||||
|
item.task_end_time,
|
||||||
|
)}`;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
// {
|
||||||
|
// title: '可执行时段',
|
||||||
|
// search: false,
|
||||||
|
// render: (_, item: any) => {
|
||||||
|
// return `${showTime(item.clock_start_time)}-${showTime(
|
||||||
|
// item.clock_end_time,
|
||||||
|
// )}`;
|
||||||
|
// },
|
||||||
|
// },
|
||||||
|
MyColumns.Boolean({
|
||||||
|
dataIndex: 'is_enabled',
|
||||||
|
title: '是否启用',
|
||||||
|
}),
|
||||||
|
MyColumns.CreatedAt(),
|
||||||
|
MyColumns.Option({
|
||||||
|
render: (_, item: any, index, action) => (
|
||||||
|
<Space key={index}>
|
||||||
|
<Update item={item} reload={action?.reload} title="路线" />
|
||||||
|
<MyButtons.Delete
|
||||||
|
onConfirm={() =>
|
||||||
|
Apis.Patrol.PatrolRoutes.Delete({ id: item.id }).then(() =>
|
||||||
|
action?.reload(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</Space>
|
||||||
|
),
|
||||||
|
}),
|
||||||
|
]}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
324
src/pages/patrol/patrol_routes/modals/Create.tsx
Normal file
324
src/pages/patrol/patrol_routes/modals/Create.tsx
Normal file
@ -0,0 +1,324 @@
|
|||||||
|
import {
|
||||||
|
MyBetaModalFormProps,
|
||||||
|
MyButtons,
|
||||||
|
MyFormItems,
|
||||||
|
MyModalFormProps,
|
||||||
|
rulesHelper,
|
||||||
|
} from '@/common';
|
||||||
|
import { Selects } from '@/components/Select';
|
||||||
|
import { Apis } from '@/gen/Apis';
|
||||||
|
import { PatrolRoutesGenerationMethodEnum } from '@/gen/Enums';
|
||||||
|
import { BetaSchemaForm } from '@ant-design/pro-components';
|
||||||
|
import { Form, message } from 'antd';
|
||||||
|
import { useEffect, useRef } from 'react';
|
||||||
|
|
||||||
|
export default function Create(props: MyBetaModalFormProps) {
|
||||||
|
const [form] = Form.useForm();
|
||||||
|
const actionRef = useRef<any>();
|
||||||
|
|
||||||
|
// 使用useEffect添加样式,确保组件挂载时样式正确应用
|
||||||
|
useEffect(() => {
|
||||||
|
const styleId = 'custom-checkbox-styles';
|
||||||
|
let styleElement = document.getElementById(styleId) as HTMLStyleElement;
|
||||||
|
|
||||||
|
if (!styleElement) {
|
||||||
|
styleElement = document.createElement('style');
|
||||||
|
styleElement.id = styleId;
|
||||||
|
document.head.appendChild(styleElement);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 添加checkbox样式,确保每个选项宽度一致且label和checkbox在同一行,一行显示7个
|
||||||
|
styleElement.textContent = `
|
||||||
|
/* 为checkbox组的容器设置样式 */
|
||||||
|
.custom-checkbox-group {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
gap: 4px;
|
||||||
|
width: 100%;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 为每个checkbox选项设置固定宽度,使一行能显示7个,并确保在同一行 */
|
||||||
|
.custom-checkbox-group .ant-checkbox-wrapper {
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
width: calc(14.2857% - 4px); /* 一行7个,减去间距 */
|
||||||
|
min-width: 60px;
|
||||||
|
text-align: center;
|
||||||
|
margin-right: 0;
|
||||||
|
margin-bottom: 0;
|
||||||
|
white-space: nowrap;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 确保label和checkbox在同一行 */
|
||||||
|
.custom-checkbox-group .ant-checkbox + span {
|
||||||
|
display: inline;
|
||||||
|
width: auto;
|
||||||
|
margin-left: 4px;
|
||||||
|
text-align: center;
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 确保整个选项作为一个整体居中 */
|
||||||
|
.custom-checkbox-group .ant-checkbox-wrapper-inner {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 调整checkbox本身的大小 */
|
||||||
|
.custom-checkbox-group .ant-checkbox {
|
||||||
|
transform: scale(0.9);
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
// 清理函数
|
||||||
|
return () => {
|
||||||
|
if (styleElement && styleElement.parentNode) {
|
||||||
|
styleElement.parentNode.removeChild(styleElement);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}, []);
|
||||||
|
return (
|
||||||
|
<BetaSchemaForm<ApiTypes.Patrol.PatrolLocations.Store>
|
||||||
|
{...MyModalFormProps.props}
|
||||||
|
title={`添加${props.title}`}
|
||||||
|
layout="horizontal"
|
||||||
|
labelCol={{ span: 4 }}
|
||||||
|
wrapperCol={{ span: 20 }}
|
||||||
|
labelAlign="left"
|
||||||
|
width="720px"
|
||||||
|
key={new Date().getTime()}
|
||||||
|
form={form}
|
||||||
|
onOpenChange={(open: any) => {
|
||||||
|
if (open) {
|
||||||
|
form.resetFields(); // 清空表单数据
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
trigger={<MyButtons.Create title={`添加${props.title}`} />}
|
||||||
|
onFinish={async (values: any) =>
|
||||||
|
Apis.Patrol.PatrolRoutes.Store({
|
||||||
|
...values,
|
||||||
|
is_enabled: 1,
|
||||||
|
patrol_location: values.patrol_location?.map(
|
||||||
|
(item: any) => item.location_id,
|
||||||
|
),
|
||||||
|
clock_start_time: values.task_start_time,
|
||||||
|
clock_end_time: values.task_end_time,
|
||||||
|
})
|
||||||
|
.then(() => {
|
||||||
|
props.reload?.();
|
||||||
|
message.success(props.title + '成功');
|
||||||
|
return true;
|
||||||
|
})
|
||||||
|
.catch(() => false)
|
||||||
|
}
|
||||||
|
columns={[
|
||||||
|
{
|
||||||
|
title: '路线名称',
|
||||||
|
key: 'name',
|
||||||
|
colProps: { span: 24 },
|
||||||
|
formItemProps: { ...rulesHelper.text },
|
||||||
|
},
|
||||||
|
Selects?.AssetProjects({
|
||||||
|
title: '选择项目',
|
||||||
|
key: 'asset_projects_id',
|
||||||
|
colProps: { span: 24 },
|
||||||
|
formItemProps: { ...rulesHelper.text },
|
||||||
|
}),
|
||||||
|
{
|
||||||
|
valueType: 'dependency',
|
||||||
|
name: ['asset_projects_id'],
|
||||||
|
columns: ({ asset_projects_id }) => {
|
||||||
|
return asset_projects_id
|
||||||
|
? [
|
||||||
|
{
|
||||||
|
valueType: 'formList',
|
||||||
|
dataIndex: 'patrol_location',
|
||||||
|
title: '选择点位',
|
||||||
|
formItemProps: { ...rulesHelper.array },
|
||||||
|
fieldProps: {
|
||||||
|
actionRef: actionRef,
|
||||||
|
copyIconProps: false,
|
||||||
|
addButtonProps: {
|
||||||
|
children: '添加点位',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
columns: [
|
||||||
|
{
|
||||||
|
valueType: 'group',
|
||||||
|
colProps: { span: 24 },
|
||||||
|
columns: [
|
||||||
|
Selects?.PatrolLocationsSelect({
|
||||||
|
title: '',
|
||||||
|
key: 'location_id',
|
||||||
|
tooltip: '点位的巡更顺序未为【自上而下】',
|
||||||
|
colProps: { span: 24 },
|
||||||
|
formItemProps: { ...rulesHelper.text },
|
||||||
|
params: {
|
||||||
|
asset_projects_id: asset_projects_id,
|
||||||
|
},
|
||||||
|
fieldProps: {
|
||||||
|
mode: 'single',
|
||||||
|
placeholder:
|
||||||
|
'请选择巡逻点位(如空白,请在“点位配置”中添加)',
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
]
|
||||||
|
: [];
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'task_start_time',
|
||||||
|
title: '任务时段',
|
||||||
|
colProps: { span: 12 },
|
||||||
|
valueType: 'time',
|
||||||
|
required: true,
|
||||||
|
formItemProps: {
|
||||||
|
labelCol: { span: 8 },
|
||||||
|
wrapperCol: { span: 16 },
|
||||||
|
...rulesHelper.text,
|
||||||
|
},
|
||||||
|
fieldProps: {
|
||||||
|
placeholder: '请设置任务开始时间',
|
||||||
|
format: 'HH:mm',
|
||||||
|
style: { width: '100%' },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'task_end_time',
|
||||||
|
valueType: 'time',
|
||||||
|
colProps: { span: 12 },
|
||||||
|
formItemProps: {
|
||||||
|
labelCol: { span: 8 },
|
||||||
|
wrapperCol: { span: 16 },
|
||||||
|
...rulesHelper.text,
|
||||||
|
},
|
||||||
|
fieldProps: {
|
||||||
|
placeholder: '请设置任务结束时间',
|
||||||
|
format: 'HH:mm',
|
||||||
|
style: { width: '100%' },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
// {
|
||||||
|
// title: '执行时段',
|
||||||
|
// tooltip:
|
||||||
|
// '任务的具体执行中,不能早于可执行开始时间,不能晚于可执行结束时间',
|
||||||
|
// key: 'clock_start_time',
|
||||||
|
// colProps: { span: 12 },
|
||||||
|
// valueType: 'time',
|
||||||
|
// required: true,
|
||||||
|
// formItemProps: {
|
||||||
|
// labelCol: { span: 8 },
|
||||||
|
// wrapperCol: { span: 16 },
|
||||||
|
// ...rulesHelper.text,
|
||||||
|
// },
|
||||||
|
// fieldProps: {
|
||||||
|
// placeholder: '请设置任务可执行开始时间',
|
||||||
|
// format: 'HH:mm',
|
||||||
|
// style: { width: '100%' },
|
||||||
|
// },
|
||||||
|
// },
|
||||||
|
// {
|
||||||
|
// key: 'clock_end_time',
|
||||||
|
// valueType: 'time',
|
||||||
|
// colProps: { span: 12 },
|
||||||
|
// formItemProps: {
|
||||||
|
// labelCol: { span: 8 },
|
||||||
|
// wrapperCol: { span: 16 },
|
||||||
|
// ...rulesHelper.text,
|
||||||
|
// },
|
||||||
|
// fieldProps: {
|
||||||
|
// placeholder: '请设置任务可执行结束时间',
|
||||||
|
// format: 'HH:mm',
|
||||||
|
// style: { width: '100%' },
|
||||||
|
// },
|
||||||
|
// },
|
||||||
|
MyFormItems.EnumRadio({
|
||||||
|
key: 'generation_method',
|
||||||
|
title: '巡更周期',
|
||||||
|
tooltip: '每天巡更一次/每周x巡更一次/每月x号巡更一次',
|
||||||
|
valueEnum: PatrolRoutesGenerationMethodEnum,
|
||||||
|
colProps: { span: 24 },
|
||||||
|
formItemProps: { ...rulesHelper.text },
|
||||||
|
fieldProps: {
|
||||||
|
onChange: () => {
|
||||||
|
// 切换巡更周期时清空生成时段
|
||||||
|
form.setFieldValue('date', undefined);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
{
|
||||||
|
valueType: 'dependency',
|
||||||
|
name: ['generation_method'],
|
||||||
|
columns: ({ generation_method }) => {
|
||||||
|
return generation_method === 'Weekly' ||
|
||||||
|
generation_method === 'Monthly'
|
||||||
|
? [
|
||||||
|
{
|
||||||
|
title: '生成时段',
|
||||||
|
key: 'date',
|
||||||
|
valueType: 'checkbox',
|
||||||
|
formItemProps: { ...rulesHelper.array },
|
||||||
|
colProps: { span: 24 },
|
||||||
|
fieldProps: {
|
||||||
|
style: { width: '100%' },
|
||||||
|
// 使用CSS方式设置每个checkbox选项的宽度
|
||||||
|
className: 'custom-checkbox-group',
|
||||||
|
},
|
||||||
|
valueEnum: () => {
|
||||||
|
if (generation_method === 'Weekly') {
|
||||||
|
const weekDays = [
|
||||||
|
{ label: '周一', value: 1 },
|
||||||
|
{ label: '周二', value: 2 },
|
||||||
|
{ label: '周三', value: 3 },
|
||||||
|
{ label: '周四', value: 4 },
|
||||||
|
{ label: '周五', value: 5 },
|
||||||
|
{ label: '周六', value: 6 },
|
||||||
|
{ label: '周日', value: 7 },
|
||||||
|
];
|
||||||
|
return weekDays.reduce((acc: any, day) => {
|
||||||
|
acc[day.value] = { text: day.label };
|
||||||
|
return acc;
|
||||||
|
}, {});
|
||||||
|
}
|
||||||
|
if (generation_method === 'Monthly') {
|
||||||
|
const monthDays = Array.from(
|
||||||
|
{ length: 31 },
|
||||||
|
(_, index) => {
|
||||||
|
const day = index + 1;
|
||||||
|
return {
|
||||||
|
label: `${day < 10 ? `0${day}` : day}号`,
|
||||||
|
value: day,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
return monthDays.reduce((acc: any, day) => {
|
||||||
|
acc[day.value] = { text: day.label };
|
||||||
|
return acc;
|
||||||
|
}, {});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
]
|
||||||
|
: [];
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
title: '备注',
|
||||||
|
key: 'remark',
|
||||||
|
valueType: 'textarea',
|
||||||
|
colProps: { span: 24 },
|
||||||
|
},
|
||||||
|
]}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
338
src/pages/patrol/patrol_routes/modals/Update.tsx
Normal file
338
src/pages/patrol/patrol_routes/modals/Update.tsx
Normal file
@ -0,0 +1,338 @@
|
|||||||
|
import {
|
||||||
|
MyBetaModalFormProps,
|
||||||
|
MyButtons,
|
||||||
|
MyFormItems,
|
||||||
|
MyModalFormProps,
|
||||||
|
rulesHelper,
|
||||||
|
} from '@/common';
|
||||||
|
import { Selects } from '@/components/Select';
|
||||||
|
import { Apis } from '@/gen/Apis';
|
||||||
|
import { PatrolRoutesGenerationMethodEnum } from '@/gen/Enums';
|
||||||
|
import { BetaSchemaForm } from '@ant-design/pro-components';
|
||||||
|
import { Form, message } from 'antd';
|
||||||
|
import { useEffect, useRef } from 'react';
|
||||||
|
|
||||||
|
export default function Update(props: MyBetaModalFormProps) {
|
||||||
|
const [form] = Form.useForm();
|
||||||
|
const actionRef = useRef<any>();
|
||||||
|
|
||||||
|
const loadShow = () => {
|
||||||
|
Apis.Patrol.PatrolRoutes.Show({ id: props.item?.id ?? 0 }).then((res) => {
|
||||||
|
form.setFieldsValue({
|
||||||
|
...res?.data,
|
||||||
|
patrol_location: res?.data?.patrol_route_locations?.map(
|
||||||
|
(item: any) => ({
|
||||||
|
location_id: item.patrol_locations_id,
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
// 使用useEffect添加样式,确保组件挂载时样式正确应用
|
||||||
|
useEffect(() => {
|
||||||
|
const styleId = 'custom-checkbox-styles';
|
||||||
|
let styleElement = document.getElementById(styleId) as HTMLStyleElement;
|
||||||
|
|
||||||
|
if (!styleElement) {
|
||||||
|
styleElement = document.createElement('style');
|
||||||
|
styleElement.id = styleId;
|
||||||
|
document.head.appendChild(styleElement);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 添加checkbox样式,确保每个选项宽度一致且label和checkbox在同一行,一行显示7个
|
||||||
|
styleElement.textContent = `
|
||||||
|
/* 为checkbox组的容器设置样式 */
|
||||||
|
.custom-checkbox-group {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
gap: 4px;
|
||||||
|
width: 100%;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 为每个checkbox选项设置固定宽度,使一行能显示7个,并确保在同一行 */
|
||||||
|
.custom-checkbox-group .ant-checkbox-wrapper {
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
width: calc(14.2857% - 4px); /* 一行7个,减去间距 */
|
||||||
|
min-width: 60px;
|
||||||
|
text-align: center;
|
||||||
|
margin-right: 0;
|
||||||
|
margin-bottom: 0;
|
||||||
|
white-space: nowrap;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 确保label和checkbox在同一行 */
|
||||||
|
.custom-checkbox-group .ant-checkbox + span {
|
||||||
|
display: inline;
|
||||||
|
width: auto;
|
||||||
|
margin-left: 4px;
|
||||||
|
text-align: center;
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 确保整个选项作为一个整体居中 */
|
||||||
|
.custom-checkbox-group .ant-checkbox-wrapper-inner {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 调整checkbox本身的大小 */
|
||||||
|
.custom-checkbox-group .ant-checkbox {
|
||||||
|
transform: scale(0.9);
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
// 清理函数
|
||||||
|
return () => {
|
||||||
|
if (styleElement && styleElement.parentNode) {
|
||||||
|
styleElement.parentNode.removeChild(styleElement);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}, [props.item?.id]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<BetaSchemaForm<ApiTypes.Patrol.PatrolLocations.Update>
|
||||||
|
{...MyModalFormProps.props}
|
||||||
|
title={`编辑${props.title}`}
|
||||||
|
trigger={<MyButtons.Edit />}
|
||||||
|
key={new Date().getTime()}
|
||||||
|
layout="horizontal"
|
||||||
|
labelCol={{ span: 4 }}
|
||||||
|
wrapperCol={{ span: 20 }}
|
||||||
|
labelAlign="left"
|
||||||
|
width="720px"
|
||||||
|
form={form}
|
||||||
|
onOpenChange={(open: any) => {
|
||||||
|
if (open && props.item) {
|
||||||
|
props.item?.id ? loadShow() : null;
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
onFinish={async (values: any) =>
|
||||||
|
Apis.Patrol.PatrolRoutes.Update({
|
||||||
|
...values,
|
||||||
|
patrol_location: values.patrol_location?.map(
|
||||||
|
(item: any) => item.location_id,
|
||||||
|
),
|
||||||
|
is_enabled: values.is_enabled ? 1 : 0,
|
||||||
|
id: props.item?.id ?? 0,
|
||||||
|
clock_start_time: values.task_start_time,
|
||||||
|
clock_end_time: values.task_end_time,
|
||||||
|
})
|
||||||
|
.then(() => {
|
||||||
|
props.reload?.();
|
||||||
|
message.success(props.title + '编辑成功');
|
||||||
|
return true;
|
||||||
|
})
|
||||||
|
.catch(() => false)
|
||||||
|
}
|
||||||
|
columns={[
|
||||||
|
{
|
||||||
|
title: '路线名称',
|
||||||
|
key: 'name',
|
||||||
|
colProps: { span: 24 },
|
||||||
|
formItemProps: { ...rulesHelper.text },
|
||||||
|
},
|
||||||
|
Selects?.AssetProjects({
|
||||||
|
title: '选择项目',
|
||||||
|
key: 'asset_projects_id',
|
||||||
|
colProps: { span: 24 },
|
||||||
|
formItemProps: { ...rulesHelper.text },
|
||||||
|
}),
|
||||||
|
{
|
||||||
|
valueType: 'dependency',
|
||||||
|
name: ['asset_projects_id'],
|
||||||
|
columns: ({ asset_projects_id }) => {
|
||||||
|
return asset_projects_id
|
||||||
|
? [
|
||||||
|
{
|
||||||
|
valueType: 'formList',
|
||||||
|
dataIndex: 'patrol_location',
|
||||||
|
title: '选择点位',
|
||||||
|
formItemProps: { ...rulesHelper.array },
|
||||||
|
fieldProps: {
|
||||||
|
actionRef: actionRef,
|
||||||
|
copyIconProps: false,
|
||||||
|
addButtonProps: {
|
||||||
|
children: '添加点位',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
columns: [
|
||||||
|
{
|
||||||
|
valueType: 'group',
|
||||||
|
colProps: { span: 24 },
|
||||||
|
columns: [
|
||||||
|
Selects?.PatrolLocationsSelect({
|
||||||
|
title: '',
|
||||||
|
key: 'location_id',
|
||||||
|
colProps: { span: 24 },
|
||||||
|
tooltip: '点位的巡更顺序未为【自上而下】',
|
||||||
|
formItemProps: { ...rulesHelper.text },
|
||||||
|
params: {
|
||||||
|
asset_projects_id: asset_projects_id,
|
||||||
|
},
|
||||||
|
fieldProps: {
|
||||||
|
mode: 'single',
|
||||||
|
placeholder:
|
||||||
|
'请选择巡逻点位(如空白,请在“点位配置”中添加)',
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
]
|
||||||
|
: [];
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'task_start_time',
|
||||||
|
title: '任务时段',
|
||||||
|
colProps: { span: 12 },
|
||||||
|
valueType: 'time',
|
||||||
|
required: true,
|
||||||
|
formItemProps: {
|
||||||
|
labelCol: { span: 8 },
|
||||||
|
wrapperCol: { span: 16 },
|
||||||
|
...rulesHelper.text,
|
||||||
|
},
|
||||||
|
fieldProps: {
|
||||||
|
placeholder: '请设置任务开始时间',
|
||||||
|
format: 'HH:mm',
|
||||||
|
style: { width: '100%' },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
key: 'task_end_time',
|
||||||
|
valueType: 'time',
|
||||||
|
colProps: { span: 12 },
|
||||||
|
formItemProps: {
|
||||||
|
labelCol: { span: 8 },
|
||||||
|
wrapperCol: { span: 16 },
|
||||||
|
...rulesHelper.text,
|
||||||
|
},
|
||||||
|
fieldProps: {
|
||||||
|
placeholder: '请设置任务结束时间',
|
||||||
|
format: 'HH:mm',
|
||||||
|
style: { width: '100%' },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
// {
|
||||||
|
// title: '执行时段',
|
||||||
|
// tooltip:
|
||||||
|
// '任务的具体执行中,不能早于可执行开始时间,不能晚于可执行结束时间',
|
||||||
|
// key: 'clock_start_time',
|
||||||
|
// colProps: { span: 12 },
|
||||||
|
// valueType: 'time',
|
||||||
|
// required: true,
|
||||||
|
// formItemProps: {
|
||||||
|
// labelCol: { span: 8 },
|
||||||
|
// wrapperCol: { span: 16 },
|
||||||
|
// ...rulesHelper.text,
|
||||||
|
// },
|
||||||
|
// fieldProps: {
|
||||||
|
// placeholder: '请设置任务可执行开始时间',
|
||||||
|
// format: 'HH:mm',
|
||||||
|
// style: { width: '100%' },
|
||||||
|
// },
|
||||||
|
// },
|
||||||
|
// {
|
||||||
|
// key: 'clock_end_time',
|
||||||
|
// valueType: 'time',
|
||||||
|
// colProps: { span: 12 },
|
||||||
|
// formItemProps: {
|
||||||
|
// labelCol: { span: 8 },
|
||||||
|
// wrapperCol: { span: 16 },
|
||||||
|
// ...rulesHelper.text,
|
||||||
|
// },
|
||||||
|
// fieldProps: {
|
||||||
|
// placeholder: '请设置任务可执行结束时间',
|
||||||
|
// format: 'HH:mm',
|
||||||
|
// style: { width: '100%' },
|
||||||
|
// },
|
||||||
|
// },
|
||||||
|
MyFormItems.EnumRadio({
|
||||||
|
key: 'generation_method',
|
||||||
|
title: '巡更周期',
|
||||||
|
tooltip: '每天巡更一次/每周x巡更一次/每月x号巡更一次',
|
||||||
|
valueEnum: PatrolRoutesGenerationMethodEnum,
|
||||||
|
colProps: { span: 24 },
|
||||||
|
formItemProps: { ...rulesHelper.text },
|
||||||
|
fieldProps: {
|
||||||
|
onChange: () => {
|
||||||
|
form.setFieldValue('date', undefined);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
{
|
||||||
|
valueType: 'dependency',
|
||||||
|
name: ['generation_method'],
|
||||||
|
columns: ({ generation_method }) => {
|
||||||
|
return generation_method === 'Weekly' ||
|
||||||
|
generation_method === 'Monthly'
|
||||||
|
? [
|
||||||
|
{
|
||||||
|
title: '生成时段',
|
||||||
|
key: 'date',
|
||||||
|
valueType: 'checkbox',
|
||||||
|
formItemProps: { ...rulesHelper.array },
|
||||||
|
colProps: { span: 24 },
|
||||||
|
fieldProps: {
|
||||||
|
style: { width: '100%' },
|
||||||
|
// 使用CSS方式设置每个checkbox选项的宽度
|
||||||
|
className: 'custom-checkbox-group',
|
||||||
|
},
|
||||||
|
valueEnum: () => {
|
||||||
|
if (generation_method === 'Weekly') {
|
||||||
|
const weekDays = [
|
||||||
|
{ label: '周一', value: 1 },
|
||||||
|
{ label: '周二', value: 2 },
|
||||||
|
{ label: '周三', value: 3 },
|
||||||
|
{ label: '周四', value: 4 },
|
||||||
|
{ label: '周五', value: 5 },
|
||||||
|
{ label: '周六', value: 6 },
|
||||||
|
{ label: '周日', value: 7 },
|
||||||
|
];
|
||||||
|
return weekDays.reduce((acc: any, day) => {
|
||||||
|
acc[day.value] = { text: day.label };
|
||||||
|
return acc;
|
||||||
|
}, {});
|
||||||
|
}
|
||||||
|
if (generation_method === 'Monthly') {
|
||||||
|
const monthDays = Array.from(
|
||||||
|
{ length: 31 },
|
||||||
|
(_, index) => {
|
||||||
|
const day = index + 1;
|
||||||
|
return {
|
||||||
|
label: `${day < 10 ? `0${day}` : day}号`,
|
||||||
|
value: day,
|
||||||
|
};
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
return monthDays.reduce((acc: any, day) => {
|
||||||
|
acc[day.value] = { text: day.label };
|
||||||
|
return acc;
|
||||||
|
}, {});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
]
|
||||||
|
: [];
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
title: '备注',
|
||||||
|
key: 'remark',
|
||||||
|
valueType: 'textarea',
|
||||||
|
colProps: { span: 24 },
|
||||||
|
},
|
||||||
|
]}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
139
src/pages/patrol/patrol_tasks/index.tsx
Normal file
139
src/pages/patrol/patrol_tasks/index.tsx
Normal file
@ -0,0 +1,139 @@
|
|||||||
|
import { MyButtons, MyColumns, MyProTableProps } from '@/common';
|
||||||
|
import { Selects } from '@/components/Select';
|
||||||
|
import { Apis } from '@/gen/Apis';
|
||||||
|
import { PatrolTasksCreateTypeEnum, PatrolTasksStatusEnum } from '@/gen/Enums';
|
||||||
|
import { ProTable } from '@ant-design/pro-components';
|
||||||
|
import { Space } from 'antd';
|
||||||
|
import Create from './modals/Create';
|
||||||
|
import PatrolOrderAssign from './modals/PatrolOrderAssign';
|
||||||
|
import PatrolOrderShow from './modals/PatrolOrderShow';
|
||||||
|
|
||||||
|
export default function Index({ title = '巡更任务' }) {
|
||||||
|
return (
|
||||||
|
<ProTable
|
||||||
|
{...MyProTableProps.props}
|
||||||
|
request={async (params, sort) =>
|
||||||
|
MyProTableProps.request(params, sort, Apis.Patrol.PatrolTasks.List)
|
||||||
|
}
|
||||||
|
toolBarRender={(action) => [
|
||||||
|
<Create key="Create" reload={action?.reload} title={title} />,
|
||||||
|
]}
|
||||||
|
columns={[
|
||||||
|
MyColumns.ID({
|
||||||
|
search: false,
|
||||||
|
}),
|
||||||
|
Selects?.AssetProjects({
|
||||||
|
title: '选择项目',
|
||||||
|
key: 'asset_projects_id',
|
||||||
|
hidden: true,
|
||||||
|
}),
|
||||||
|
{
|
||||||
|
title: '关联项目',
|
||||||
|
dataIndex: ['asset_project', 'name'],
|
||||||
|
search: {
|
||||||
|
transform: (value) => {
|
||||||
|
return { project_name: value };
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '路线名称',
|
||||||
|
dataIndex: ['patrol_route', 'name'],
|
||||||
|
search: {
|
||||||
|
transform: (value) => {
|
||||||
|
return { patrol_route_name: value };
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
title: '任务时段',
|
||||||
|
dataIndex: 'start_time',
|
||||||
|
valueType: 'dateTime',
|
||||||
|
render: (_, item: any) => {
|
||||||
|
// 格式化开始时间(显示日期和时分)
|
||||||
|
const formatStartTime = (timeStr: string) => {
|
||||||
|
if (!timeStr) return '';
|
||||||
|
const [date, time] = timeStr.split(' ');
|
||||||
|
if (!time) return date;
|
||||||
|
return `${date} ${time.split(':').slice(0, 2).join(':')}`;
|
||||||
|
};
|
||||||
|
|
||||||
|
// 格式化结束时间(只显示时分)
|
||||||
|
const formatEndTime = (timeStr: string) => {
|
||||||
|
if (!timeStr) return '';
|
||||||
|
const [, time] = timeStr.split(' ');
|
||||||
|
if (!time) return '';
|
||||||
|
return time.split(':').slice(0, 2).join(':');
|
||||||
|
};
|
||||||
|
|
||||||
|
return `${formatStartTime(item.start_time)} - ${formatEndTime(
|
||||||
|
item.end_time,
|
||||||
|
)}`;
|
||||||
|
},
|
||||||
|
search: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '任务时段',
|
||||||
|
dataIndex: 'date_time',
|
||||||
|
valueType: 'dateRange',
|
||||||
|
hideInTable: true,
|
||||||
|
},
|
||||||
|
MyColumns.EnumTag({
|
||||||
|
title: '生成方式',
|
||||||
|
dataIndex: 'create_type',
|
||||||
|
valueEnum: PatrolTasksCreateTypeEnum,
|
||||||
|
}),
|
||||||
|
MyColumns.EnumTag({
|
||||||
|
title: '任务状态',
|
||||||
|
dataIndex: 'status',
|
||||||
|
valueEnum: PatrolTasksStatusEnum,
|
||||||
|
search: false,
|
||||||
|
}),
|
||||||
|
{
|
||||||
|
title: '指派时间',
|
||||||
|
dataIndex: 'assign_time',
|
||||||
|
search: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '关联工单',
|
||||||
|
dataIndex: 'house_work_orders_id',
|
||||||
|
search: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '完成时间',
|
||||||
|
dataIndex: 'complete_time',
|
||||||
|
search: false,
|
||||||
|
},
|
||||||
|
MyColumns.CreatedAt(),
|
||||||
|
MyColumns.Option({
|
||||||
|
render: (_, item: any, index, action) => (
|
||||||
|
<Space key={index}>
|
||||||
|
{item?.status === 'Unassigned' && (
|
||||||
|
<PatrolOrderAssign
|
||||||
|
item={item}
|
||||||
|
reload={action?.reload}
|
||||||
|
title="指派"
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
{item.status !== 'Unassigned' && (
|
||||||
|
<PatrolOrderShow
|
||||||
|
item={item}
|
||||||
|
reload={action?.reload}
|
||||||
|
title="查看"
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
<MyButtons.Delete
|
||||||
|
onConfirm={() =>
|
||||||
|
Apis.Patrol.PatrolTasks.Delete({ id: item.id }).then(() =>
|
||||||
|
action?.reload(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</Space>
|
||||||
|
),
|
||||||
|
}),
|
||||||
|
]}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
125
src/pages/patrol/patrol_tasks/modals/Create.tsx
Normal file
125
src/pages/patrol/patrol_tasks/modals/Create.tsx
Normal file
@ -0,0 +1,125 @@
|
|||||||
|
import {
|
||||||
|
MyBetaModalFormProps,
|
||||||
|
MyButtons,
|
||||||
|
MyFormItems,
|
||||||
|
MyModalFormProps,
|
||||||
|
rulesHelper,
|
||||||
|
} from '@/common';
|
||||||
|
import { Selects } from '@/components/Select';
|
||||||
|
import { Apis } from '@/gen/Apis';
|
||||||
|
import { HouseWorkOrdersLevelEnum } from '@/gen/Enums';
|
||||||
|
import { BetaSchemaForm } from '@ant-design/pro-components';
|
||||||
|
import { Form, message } from 'antd';
|
||||||
|
import dayjs from 'dayjs';
|
||||||
|
|
||||||
|
export default function Create(props: MyBetaModalFormProps) {
|
||||||
|
const [form] = Form.useForm();
|
||||||
|
return (
|
||||||
|
<BetaSchemaForm<ApiTypes.Patrol.PatrolTasks.Store>
|
||||||
|
{...MyModalFormProps.props}
|
||||||
|
title={`添加临时任务`}
|
||||||
|
layout="horizontal"
|
||||||
|
labelCol={{ span: 4 }}
|
||||||
|
wrapperCol={{ span: 20 }}
|
||||||
|
labelAlign="left"
|
||||||
|
width="600px"
|
||||||
|
key={new Date().getTime()}
|
||||||
|
form={form}
|
||||||
|
onOpenChange={(open: any) => {
|
||||||
|
if (open) {
|
||||||
|
form.resetFields(); // 清空表单数据
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
trigger={<MyButtons.Create title={`临时任务`} />}
|
||||||
|
onFinish={async (values) =>
|
||||||
|
Apis.Patrol.PatrolTasks.Store({
|
||||||
|
...values,
|
||||||
|
})
|
||||||
|
.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 asset_projects_id
|
||||||
|
? [
|
||||||
|
Selects?.PatrolRoutesSelect({
|
||||||
|
title: '选择路线',
|
||||||
|
key: 'patrol_routes_id',
|
||||||
|
params: {
|
||||||
|
asset_projects_id: asset_projects_id,
|
||||||
|
},
|
||||||
|
colProps: { span: 24 },
|
||||||
|
formItemProps: { ...rulesHelper.text },
|
||||||
|
}),
|
||||||
|
]
|
||||||
|
: [
|
||||||
|
Selects?.PatrolRoutesSelect({
|
||||||
|
title: '选择路线',
|
||||||
|
key: 'patrol_routes_id',
|
||||||
|
colProps: { span: 24 },
|
||||||
|
formItemProps: { ...rulesHelper.text },
|
||||||
|
fieldProps: { disabled: true },
|
||||||
|
}),
|
||||||
|
];
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '开始时间',
|
||||||
|
key: 'start_time',
|
||||||
|
valueType: 'dateTime',
|
||||||
|
colProps: { span: 24 },
|
||||||
|
fieldProps: {
|
||||||
|
style: { width: '100%' },
|
||||||
|
placeholder: '请设置开始时间',
|
||||||
|
disabledDate: (current: any) => {
|
||||||
|
// 只能选择当天及之后的时间
|
||||||
|
return current && current < dayjs().startOf('day');
|
||||||
|
},
|
||||||
|
},
|
||||||
|
formItemProps: {
|
||||||
|
...rulesHelper.text,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '结束时间',
|
||||||
|
key: 'end_time',
|
||||||
|
valueType: 'dateTime',
|
||||||
|
colProps: { span: 24 },
|
||||||
|
fieldProps: {
|
||||||
|
style: { width: '100%' },
|
||||||
|
placeholder: '请设置结束日期',
|
||||||
|
disabledDate: (current: any) => {
|
||||||
|
//选择时间必须在开始时间之后,且为同一天
|
||||||
|
return (
|
||||||
|
current && current < dayjs(form.getFieldValue('start_time'))
|
||||||
|
);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
formItemProps: {
|
||||||
|
...rulesHelper.text,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
MyFormItems.EnumRadio({
|
||||||
|
key: 'level',
|
||||||
|
title: '任务等级',
|
||||||
|
colProps: { span: 24 },
|
||||||
|
valueEnum: HouseWorkOrdersLevelEnum,
|
||||||
|
required: true,
|
||||||
|
}),
|
||||||
|
]}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
78
src/pages/patrol/patrol_tasks/modals/PatrolOrderAssign.tsx
Normal file
78
src/pages/patrol/patrol_tasks/modals/PatrolOrderAssign.tsx
Normal file
@ -0,0 +1,78 @@
|
|||||||
|
import {
|
||||||
|
MyBetaModalFormProps,
|
||||||
|
MyButtons,
|
||||||
|
MyModalFormProps,
|
||||||
|
rulesHelper,
|
||||||
|
} from '@/common';
|
||||||
|
import { Selects } from '@/components/Select';
|
||||||
|
import { Apis } from '@/gen/Apis';
|
||||||
|
import { BetaSchemaForm } from '@ant-design/pro-components';
|
||||||
|
import { Form, message } from 'antd';
|
||||||
|
|
||||||
|
const JudgmentEnum = {
|
||||||
|
Yes: { text: '有效工单', color: '#ff0000', value: 'Yes' },
|
||||||
|
No: { text: '无效工单', color: '#00ff00', value: 'No' },
|
||||||
|
};
|
||||||
|
|
||||||
|
type FormDataType = ApiTypes.WorkOrder.HouseWorkOrders.Assign & {
|
||||||
|
attachments?: any[];
|
||||||
|
judgment?: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default function WorkOrderAssign(
|
||||||
|
props: MyBetaModalFormProps & { item: any },
|
||||||
|
) {
|
||||||
|
const [form] = Form.useForm();
|
||||||
|
return (
|
||||||
|
<BetaSchemaForm<ApiTypes.WorkOrder.HouseWorkOrders.Assign>
|
||||||
|
{...MyModalFormProps.props}
|
||||||
|
title={`指派工单`}
|
||||||
|
wrapperCol={{ span: 19 }}
|
||||||
|
width="500px"
|
||||||
|
layout="horizontal"
|
||||||
|
labelCol={{ span: 5 }}
|
||||||
|
trigger={
|
||||||
|
<MyButtons.Default title={props.item.title || '指派'} type="primary" />
|
||||||
|
}
|
||||||
|
key={new Date().getTime()}
|
||||||
|
form={form}
|
||||||
|
onOpenChange={(open: any) => {
|
||||||
|
if (open) {
|
||||||
|
form.resetFields(); // 清空表单数据
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
onFinish={async (values: any) => {
|
||||||
|
return Apis.WorkOrder.HouseWorkOrders.Assign({
|
||||||
|
...values,
|
||||||
|
id: props.item.house_work_orders_id,
|
||||||
|
predict_complete_at: props.item.end_time,
|
||||||
|
level: props.item.level,
|
||||||
|
})
|
||||||
|
.then(() => {
|
||||||
|
props.reload?.();
|
||||||
|
message.success('指派工单成功');
|
||||||
|
return true;
|
||||||
|
})
|
||||||
|
.catch(() => false);
|
||||||
|
}}
|
||||||
|
columns={[
|
||||||
|
Selects?.Employees({
|
||||||
|
title: '选择处理人',
|
||||||
|
key: 'assign_employees_id',
|
||||||
|
colProps: { span: 24 },
|
||||||
|
formItemProps: { ...rulesHelper.text },
|
||||||
|
required: true,
|
||||||
|
}),
|
||||||
|
{
|
||||||
|
title: '备注',
|
||||||
|
key: 'assign_remark',
|
||||||
|
valueType: 'textarea',
|
||||||
|
colProps: { span: 24 },
|
||||||
|
fieldProps: {
|
||||||
|
style: { width: '100%' },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
]}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
182
src/pages/patrol/patrol_tasks/modals/PatrolOrderShow.tsx
Normal file
182
src/pages/patrol/patrol_tasks/modals/PatrolOrderShow.tsx
Normal file
@ -0,0 +1,182 @@
|
|||||||
|
import { MyButtons, MyProTableProps } from '@/common';
|
||||||
|
import { Apis } from '@/gen/Apis';
|
||||||
|
import {
|
||||||
|
PatrolTaskLocationsStatusEnum,
|
||||||
|
PatrolTasksStatusEnum,
|
||||||
|
} from '@/gen/Enums';
|
||||||
|
import { ProDescriptions, ProTable } from '@ant-design/pro-components';
|
||||||
|
import { Divider, Image, Modal, Space } from 'antd';
|
||||||
|
import { useState } from 'react';
|
||||||
|
import PatrolOrderAssign from './PatrolOrderAssign';
|
||||||
|
|
||||||
|
interface PatrolOrderShowProps {
|
||||||
|
item: any;
|
||||||
|
title?: string;
|
||||||
|
reload?: () => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function PatrolOrderShow({
|
||||||
|
item,
|
||||||
|
title = '巡更详情',
|
||||||
|
reload,
|
||||||
|
}: PatrolOrderShowProps) {
|
||||||
|
const [open, setOpen] = useState(false);
|
||||||
|
const [data, setData] = useState<any>(null);
|
||||||
|
|
||||||
|
const handleOpen = async () => {
|
||||||
|
try {
|
||||||
|
const res = await Apis.Patrol.PatrolTasks.Show({ id: item.id });
|
||||||
|
setData(res.data);
|
||||||
|
setOpen(true);
|
||||||
|
} catch (error) {
|
||||||
|
console.error('获取详情失败:', error);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<MyButtons.Default onClick={handleOpen} type="primary" title={title} />
|
||||||
|
<Modal
|
||||||
|
title={title}
|
||||||
|
open={open}
|
||||||
|
onCancel={() => setOpen(false)}
|
||||||
|
footer={null}
|
||||||
|
width={800}
|
||||||
|
>
|
||||||
|
{data && (
|
||||||
|
<>
|
||||||
|
<ProDescriptions
|
||||||
|
column={2}
|
||||||
|
dataSource={data}
|
||||||
|
columns={[
|
||||||
|
{
|
||||||
|
title: '任务ID',
|
||||||
|
dataIndex: 'id',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '工单ID',
|
||||||
|
dataIndex: 'house_work_orders_id',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '项目名称',
|
||||||
|
dataIndex: ['asset_project', 'name'],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '项目名称',
|
||||||
|
dataIndex: ['patrol_route', 'name'],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '任务状态',
|
||||||
|
dataIndex: 'status',
|
||||||
|
valueEnum: PatrolTasksStatusEnum,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '处理人',
|
||||||
|
dataIndex: 'assign_employee_name',
|
||||||
|
span: 2,
|
||||||
|
render: (_, record) => {
|
||||||
|
const assigneeInfo = record?.house_work_order
|
||||||
|
?.assign_employee?.name
|
||||||
|
? `${
|
||||||
|
record.house_work_order.assign_employee.name || ''
|
||||||
|
}-${
|
||||||
|
record.house_work_order.assign_employee.phone || ''
|
||||||
|
}`
|
||||||
|
: '未分配';
|
||||||
|
return (
|
||||||
|
<Space>
|
||||||
|
<span>{assigneeInfo}</span>
|
||||||
|
<PatrolOrderAssign
|
||||||
|
item={{ ...item, title: '重新分配' }}
|
||||||
|
reload={handleOpen}
|
||||||
|
title="重新分配"
|
||||||
|
/>
|
||||||
|
</Space>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '开始时间',
|
||||||
|
dataIndex: 'start_time',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '结束时间',
|
||||||
|
dataIndex: 'end_time',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '创建时间',
|
||||||
|
dataIndex: 'created_at',
|
||||||
|
valueType: 'dateTime',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '更新时间',
|
||||||
|
dataIndex: 'updated_at',
|
||||||
|
valueType: 'dateTime',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '完成时间',
|
||||||
|
dataIndex: 'complete_time',
|
||||||
|
},
|
||||||
|
]}
|
||||||
|
/>
|
||||||
|
<Divider />
|
||||||
|
|
||||||
|
<ProTable
|
||||||
|
{...MyProTableProps.props}
|
||||||
|
search={false}
|
||||||
|
toolBarRender={false}
|
||||||
|
pagination={false}
|
||||||
|
dataSource={data?.patrol_task_locations}
|
||||||
|
rowKey={(record, index) => record?.id_card || index}
|
||||||
|
size="small"
|
||||||
|
columns={[
|
||||||
|
{
|
||||||
|
title: '处理人',
|
||||||
|
dataIndex: 'name',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '点位名称',
|
||||||
|
dataIndex: ['patrol_location_json', 'name'],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '任务状态',
|
||||||
|
dataIndex: 'status',
|
||||||
|
valueEnum: PatrolTaskLocationsStatusEnum,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '完成时间',
|
||||||
|
dataIndex: 'updated_at',
|
||||||
|
render: (_, item) => {
|
||||||
|
return item?.status ===
|
||||||
|
PatrolTaskLocationsStatusEnum.Completed.value
|
||||||
|
? item?.updated_at
|
||||||
|
: '-';
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '现场照片',
|
||||||
|
render: (_, item) => {
|
||||||
|
return (
|
||||||
|
<Space>
|
||||||
|
{item?.images?.[0] && (
|
||||||
|
<Image
|
||||||
|
height={30}
|
||||||
|
style={{
|
||||||
|
width: 'auto',
|
||||||
|
objectFit: 'contain',
|
||||||
|
}}
|
||||||
|
src={item?.images[0]?.url}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</Space>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
]}
|
||||||
|
/>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</Modal>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
229
src/pages/system/sys_roles/components/AdminRole/index.tsx
Normal file
229
src/pages/system/sys_roles/components/AdminRole/index.tsx
Normal file
@ -0,0 +1,229 @@
|
|||||||
|
import { MyButtons, MyProTableProps, useCurrentPermissions } from '@/common';
|
||||||
|
import { flattenToMultiLevelFormatWithRowSpanAdvancedNew } from '@/common/utils/flattenIterative';
|
||||||
|
import { Apis } from '@/gen/Apis';
|
||||||
|
import { ProCard, ProTable } from '@ant-design/pro-components';
|
||||||
|
import { Checkbox, message, Space, Tabs } from 'antd';
|
||||||
|
import { useEffect, useState } from 'react';
|
||||||
|
import Create from '../../modals/Create';
|
||||||
|
interface SelectedBuilding {
|
||||||
|
id: number;
|
||||||
|
name: string;
|
||||||
|
}
|
||||||
|
export default function Index({ title = '角色' }) {
|
||||||
|
const getCurrentPermissions = useCurrentPermissions();
|
||||||
|
const [selectedBuilding, setSelectedBuilding] =
|
||||||
|
useState<SelectedBuilding | null>(null);
|
||||||
|
const [selectedPermissionsIds, setSelectedPermissionsIds] = useState<any[]>(
|
||||||
|
[],
|
||||||
|
);
|
||||||
|
const [dataSource, setDataSource] = useState<any>([]);
|
||||||
|
const [dataTabsSource, setDataTabsSource] = useState<any>([]);
|
||||||
|
const [tabsKey, setTabsKey] = useState<any>('');
|
||||||
|
const getSysPermissions = () => {
|
||||||
|
Apis.Permission.Roles.PermissionTree().then((res) => {
|
||||||
|
setDataSource(
|
||||||
|
flattenToMultiLevelFormatWithRowSpanAdvancedNew(
|
||||||
|
res?.data[0]?.children || [],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
console.log(res, 'res');
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const onSave = () => {
|
||||||
|
if (selectedPermissionsIds?.length && selectedBuilding?.id) {
|
||||||
|
Apis.Permission.Roles.SetPermissions({
|
||||||
|
permissions_ids: selectedPermissionsIds,
|
||||||
|
id: selectedBuilding?.id || 0,
|
||||||
|
}).then(() => {
|
||||||
|
message.success('保存成功');
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
message.error('请选择角色和勾选权限!');
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const getPermissions = (id: any) => {
|
||||||
|
setTabsKey(id);
|
||||||
|
// 更新选中的角色信息
|
||||||
|
const selectedRole = dataTabsSource.find((item: any) => item.id === id);
|
||||||
|
if (selectedRole) {
|
||||||
|
setSelectedBuilding({
|
||||||
|
id: selectedRole.id,
|
||||||
|
name: selectedRole.name,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
Apis.Permission.Roles.GetPermissions({
|
||||||
|
id: id ?? 0,
|
||||||
|
}).then((res) => {
|
||||||
|
setSelectedPermissionsIds(res?.data?.permissions_ids || []);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const getSysRoles = () => {
|
||||||
|
Apis.Permission.Roles.List({ guard_name: 'Company' }).then((res) => {
|
||||||
|
setDataTabsSource(res?.data || []);
|
||||||
|
if (res?.data?.length) {
|
||||||
|
const firstRole = res?.data[0];
|
||||||
|
getPermissions(firstRole?.id || 0);
|
||||||
|
// 初始化选中第一个角色
|
||||||
|
setSelectedBuilding({
|
||||||
|
id: firstRole?.id,
|
||||||
|
name: firstRole?.name,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
console.log(res, 'res');
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const onSelect = () => {
|
||||||
|
//删除角色
|
||||||
|
Apis.Permission.Roles.Delete({
|
||||||
|
id: tabsKey,
|
||||||
|
}).then(() => {
|
||||||
|
getSysRoles();
|
||||||
|
message.success('删除成功');
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
getSysRoles();
|
||||||
|
getSysPermissions();
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
let toolBarRender = () => {
|
||||||
|
return getCurrentPermissions({
|
||||||
|
add: <Create key="Create" reload={() => getSysRoles()} title={title} />,
|
||||||
|
delete: (
|
||||||
|
<MyButtons.Default
|
||||||
|
key="delete"
|
||||||
|
size="middle"
|
||||||
|
color="danger"
|
||||||
|
variant="solid"
|
||||||
|
isConfirm
|
||||||
|
description="是否确定删除当前角色?"
|
||||||
|
title="删除当前角色"
|
||||||
|
onConfirm={() => onSelect()}
|
||||||
|
// 如果当前选中角色是管理员,则禁用删除按钮
|
||||||
|
disabled={selectedBuilding?.name === '管理员'}
|
||||||
|
/>
|
||||||
|
),
|
||||||
|
save: (
|
||||||
|
<MyButtons.Default
|
||||||
|
key="save"
|
||||||
|
type="primary"
|
||||||
|
size="middle"
|
||||||
|
title="保存权限"
|
||||||
|
onClick={() => onSave()}
|
||||||
|
/>
|
||||||
|
),
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<ProCard
|
||||||
|
title="系统角色配置"
|
||||||
|
extra={<Space>{toolBarRender()}</Space>}
|
||||||
|
headerBordered
|
||||||
|
>
|
||||||
|
<div style={{ display: 'flex' }}>
|
||||||
|
<div style={{ width: '130px' }}>
|
||||||
|
<Tabs
|
||||||
|
tabPosition="left"
|
||||||
|
items={dataTabsSource.map((item: any) => ({
|
||||||
|
label: item?.name,
|
||||||
|
key: item?.id,
|
||||||
|
}))}
|
||||||
|
activeKey={tabsKey}
|
||||||
|
onChange={(key: any) => {
|
||||||
|
getPermissions(key);
|
||||||
|
console.log(key, 'key');
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div style={{ flex: 1 }}>
|
||||||
|
<Checkbox.Group
|
||||||
|
style={{ width: '100%' }}
|
||||||
|
value={selectedPermissionsIds}
|
||||||
|
onChange={(e) => {
|
||||||
|
setSelectedPermissionsIds(e);
|
||||||
|
console.log(e, 'e');
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<ProTable
|
||||||
|
{...MyProTableProps.props}
|
||||||
|
search={false}
|
||||||
|
pagination={false}
|
||||||
|
// style={{ width: '100%' }}
|
||||||
|
// tableExtraRender={() => (
|
||||||
|
// <MyEnumRadioGroup
|
||||||
|
// enums={getSysModuleEnum}
|
||||||
|
// onChange={(e) => {
|
||||||
|
// setGuardName(e as string);
|
||||||
|
// }}
|
||||||
|
// value={guardName}
|
||||||
|
// />
|
||||||
|
// )}
|
||||||
|
style={{
|
||||||
|
width: '100%',
|
||||||
|
maxHeight: 'calc(100vh - 280px)', // 设置最大高度,可根据实际需求调整
|
||||||
|
overflowY: 'auto', // 启用垂直滚动
|
||||||
|
}}
|
||||||
|
dataSource={dataSource}
|
||||||
|
options={false}
|
||||||
|
size="small"
|
||||||
|
columns={[
|
||||||
|
{
|
||||||
|
title: '目录',
|
||||||
|
dataIndex: 'name',
|
||||||
|
key: 'name',
|
||||||
|
width: '120px',
|
||||||
|
render: (_, item: any) => {
|
||||||
|
return <Checkbox value={item?.id1}>{item?.name}</Checkbox>;
|
||||||
|
},
|
||||||
|
onCell: (res, index?: number) => {
|
||||||
|
const rowSpan = res.row_spans?.rowSpan;
|
||||||
|
const firstIndex = res.row_spans?.firstIndex;
|
||||||
|
if (index === firstIndex && rowSpan > 0) {
|
||||||
|
return { rowSpan };
|
||||||
|
}
|
||||||
|
return { rowSpan: 0 };
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '页面',
|
||||||
|
dataIndex: 'name2',
|
||||||
|
key: 'name2',
|
||||||
|
width: '160px',
|
||||||
|
render: (_, item: any) => {
|
||||||
|
return item?.name2 ? (
|
||||||
|
<Checkbox value={item?.id2}>{item?.name2}</Checkbox>
|
||||||
|
) : null;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '页签/按钮',
|
||||||
|
width: '800px',
|
||||||
|
render: (_, item: any) => {
|
||||||
|
if (item?.buttonList?.length) {
|
||||||
|
return item?.buttonList?.map(
|
||||||
|
(res: any, index: number) => {
|
||||||
|
return res?.name ? (
|
||||||
|
<Checkbox value={res?.id} key={`index_${index}`}>
|
||||||
|
{res?.name}
|
||||||
|
</Checkbox>
|
||||||
|
) : null;
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
]}
|
||||||
|
/>
|
||||||
|
</Checkbox.Group>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</ProCard>
|
||||||
|
);
|
||||||
|
}
|
||||||
227
src/pages/system/sys_roles/components/EmployeeRole/index.tsx
Normal file
227
src/pages/system/sys_roles/components/EmployeeRole/index.tsx
Normal file
@ -0,0 +1,227 @@
|
|||||||
|
import { MyButtons, MyProTableProps, useCurrentPermissions } from '@/common';
|
||||||
|
import { flattenToMultiLevelFormatWithRowSpanAdvancedNew } from '@/common/utils/flattenIterative';
|
||||||
|
import { Apis } from '@/gen/Apis';
|
||||||
|
import { ProCard, ProTable } from '@ant-design/pro-components';
|
||||||
|
import { Checkbox, message, Space, Tabs } from 'antd';
|
||||||
|
import { useEffect, useState } from 'react';
|
||||||
|
import Create from './modals/Create';
|
||||||
|
interface SelectedBuilding {
|
||||||
|
id: number;
|
||||||
|
name: string;
|
||||||
|
}
|
||||||
|
export default function Index({ title = '角色' }) {
|
||||||
|
const getCurrentPermissions = useCurrentPermissions();
|
||||||
|
const [selectedBuilding, setSelectedBuilding] =
|
||||||
|
useState<SelectedBuilding | null>(null);
|
||||||
|
const [selectedPermissionsIds, setSelectedPermissionsIds] = useState<any[]>(
|
||||||
|
[],
|
||||||
|
);
|
||||||
|
const [dataSource, setDataSource] = useState<any>([]);
|
||||||
|
const [dataTabsSource, setDataTabsSource] = useState<any>([]);
|
||||||
|
const [tabsKey, setTabsKey] = useState<any>('');
|
||||||
|
const getSysPermissions = () => {
|
||||||
|
Apis.Company.EmployeeRoles.PermissionTree().then((res) => {
|
||||||
|
setDataSource(
|
||||||
|
flattenToMultiLevelFormatWithRowSpanAdvancedNew(
|
||||||
|
res?.data[0]?.children || [],
|
||||||
|
),
|
||||||
|
);
|
||||||
|
console.log(res, 'res');
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const onSave = () => {
|
||||||
|
if (selectedPermissionsIds?.length && selectedBuilding?.id) {
|
||||||
|
Apis.Company.EmployeeRoles.SetPermissions({
|
||||||
|
permissions_ids: selectedPermissionsIds,
|
||||||
|
id: selectedBuilding?.id || 0,
|
||||||
|
}).then(() => {
|
||||||
|
message.success('保存成功');
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
message.error('请选择角色和勾选权限!');
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const getPermissions = (id: any) => {
|
||||||
|
setTabsKey(id);
|
||||||
|
// 更新选中的角色信息
|
||||||
|
const selectedRole = dataTabsSource.find((item: any) => item.id === id);
|
||||||
|
if (selectedRole) {
|
||||||
|
setSelectedBuilding({
|
||||||
|
id: selectedRole.id,
|
||||||
|
name: selectedRole.name,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
Apis.Company.EmployeeRoles.GetPermissions({
|
||||||
|
id: id ?? 0,
|
||||||
|
}).then((res) => {
|
||||||
|
setSelectedPermissionsIds(res?.data?.permissions_ids || []);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const getSysRoles = () => {
|
||||||
|
Apis.Company.EmployeeRoles.List().then((res) => {
|
||||||
|
setDataTabsSource(res?.data || []);
|
||||||
|
if (res?.data?.length) {
|
||||||
|
const firstRole = res?.data[0];
|
||||||
|
getPermissions(firstRole?.id || 0);
|
||||||
|
// 初始化选中第一个角色
|
||||||
|
setSelectedBuilding({
|
||||||
|
id: firstRole?.id,
|
||||||
|
name: firstRole?.name,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
console.log(res, 'res');
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const onSelect = () => {
|
||||||
|
//删除角色
|
||||||
|
Apis.Company.EmployeeRoles.Delete({
|
||||||
|
id: tabsKey,
|
||||||
|
}).then(() => {
|
||||||
|
getSysRoles();
|
||||||
|
message.success('删除成功');
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
getSysRoles();
|
||||||
|
getSysPermissions();
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
let toolBarRender = () => {
|
||||||
|
return getCurrentPermissions({
|
||||||
|
add: <Create key="Create" reload={() => getSysRoles()} title={title} />,
|
||||||
|
delete: (
|
||||||
|
<MyButtons.Default
|
||||||
|
key="delete"
|
||||||
|
size="middle"
|
||||||
|
color="danger"
|
||||||
|
variant="solid"
|
||||||
|
isConfirm
|
||||||
|
description="是否确定删除当前角色?"
|
||||||
|
title="删除当前角色"
|
||||||
|
onConfirm={() => onSelect()}
|
||||||
|
/>
|
||||||
|
),
|
||||||
|
save: (
|
||||||
|
<MyButtons.Default
|
||||||
|
key="save"
|
||||||
|
type="primary"
|
||||||
|
size="middle"
|
||||||
|
title="保存权限"
|
||||||
|
onClick={() => onSave()}
|
||||||
|
/>
|
||||||
|
),
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<ProCard
|
||||||
|
title="员工角色配置"
|
||||||
|
headerBordered
|
||||||
|
extra={<Space>{toolBarRender()}</Space>}
|
||||||
|
>
|
||||||
|
<div style={{ display: 'flex' }}>
|
||||||
|
<div style={{ width: '130px' }}>
|
||||||
|
<Tabs
|
||||||
|
tabPosition="left"
|
||||||
|
items={dataTabsSource.map((item: any) => ({
|
||||||
|
label: item?.name,
|
||||||
|
key: item?.id,
|
||||||
|
}))}
|
||||||
|
activeKey={tabsKey}
|
||||||
|
onChange={(key: any) => {
|
||||||
|
getPermissions(key);
|
||||||
|
console.log(key, 'key');
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div style={{ flex: 1 }}>
|
||||||
|
<Checkbox.Group
|
||||||
|
style={{ width: '100%' }}
|
||||||
|
value={selectedPermissionsIds}
|
||||||
|
onChange={(e) => {
|
||||||
|
setSelectedPermissionsIds(e);
|
||||||
|
console.log(e, 'e');
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<ProTable
|
||||||
|
{...MyProTableProps.props}
|
||||||
|
search={false}
|
||||||
|
pagination={false}
|
||||||
|
// style={{ width: '100%' }}
|
||||||
|
// tableExtraRender={() => (
|
||||||
|
// <MyEnumRadioGroup
|
||||||
|
// enums={getSysModuleEnum}
|
||||||
|
// onChange={(e) => {
|
||||||
|
// setGuardName(e as string);
|
||||||
|
// }}
|
||||||
|
// value={guardName}
|
||||||
|
// />
|
||||||
|
// )}
|
||||||
|
style={{
|
||||||
|
width: '100%',
|
||||||
|
maxHeight: 'calc(100vh - 280px)', // 设置最大高度,可根据实际需求调整
|
||||||
|
overflowY: 'auto', // 启用垂直滚动
|
||||||
|
}}
|
||||||
|
dataSource={dataSource}
|
||||||
|
options={false}
|
||||||
|
size="small"
|
||||||
|
columns={[
|
||||||
|
{
|
||||||
|
title: '目录',
|
||||||
|
dataIndex: 'name',
|
||||||
|
key: 'name',
|
||||||
|
width: '120px',
|
||||||
|
render: (_, item: any) => {
|
||||||
|
return <Checkbox value={item?.id1}>{item?.name}</Checkbox>;
|
||||||
|
},
|
||||||
|
onCell: (res, index?: number) => {
|
||||||
|
const rowSpan = res.row_spans?.rowSpan;
|
||||||
|
const firstIndex = res.row_spans?.firstIndex;
|
||||||
|
if (index === firstIndex && rowSpan > 0) {
|
||||||
|
return { rowSpan };
|
||||||
|
}
|
||||||
|
return { rowSpan: 0 };
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '页面',
|
||||||
|
dataIndex: 'name2',
|
||||||
|
key: 'name2',
|
||||||
|
width: '160px',
|
||||||
|
render: (_, item: any) => {
|
||||||
|
return item?.name2 ? (
|
||||||
|
<Checkbox value={item?.id2}>{item?.name2}</Checkbox>
|
||||||
|
) : null;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '页签/按钮',
|
||||||
|
width: '800px',
|
||||||
|
render: (_, item: any) => {
|
||||||
|
if (item?.buttonList?.length) {
|
||||||
|
return item?.buttonList?.map(
|
||||||
|
(res: any, index: number) => {
|
||||||
|
return res?.name ? (
|
||||||
|
<Checkbox value={res?.id} key={`index_${index}`}>
|
||||||
|
{res?.name}
|
||||||
|
</Checkbox>
|
||||||
|
) : null;
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
]}
|
||||||
|
/>
|
||||||
|
</Checkbox.Group>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</ProCard>
|
||||||
|
);
|
||||||
|
}
|
||||||
@ -0,0 +1,47 @@
|
|||||||
|
import {
|
||||||
|
MyBetaModalFormProps,
|
||||||
|
MyButtons,
|
||||||
|
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.Company.EmployeeRoles.Store>
|
||||||
|
{...MyModalFormProps.props}
|
||||||
|
form={form}
|
||||||
|
title={`添加${props.title}`}
|
||||||
|
wrapperCol={{ span: 24 }}
|
||||||
|
key={new Date().getTime()}
|
||||||
|
width="420px"
|
||||||
|
trigger={<MyButtons.Create title={`添加${props.title}`} />}
|
||||||
|
onOpenChange={(open: any) => {
|
||||||
|
if (open) {
|
||||||
|
form.resetFields(); // 清空表单数据
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
onFinish={async (values) =>
|
||||||
|
Apis.Company.EmployeeRoles.Store(values)
|
||||||
|
.then(() => {
|
||||||
|
props.reload?.();
|
||||||
|
message.success(props.title + '成功');
|
||||||
|
return true;
|
||||||
|
})
|
||||||
|
.catch(() => false)
|
||||||
|
}
|
||||||
|
columns={[
|
||||||
|
{
|
||||||
|
key: 'name',
|
||||||
|
title: '角色名称',
|
||||||
|
formItemProps: { ...rulesHelper.text },
|
||||||
|
},
|
||||||
|
// MyFormItems.ColorPicker(),
|
||||||
|
]}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
@ -1,128 +1,20 @@
|
|||||||
import {
|
import { MyPageContainer } from '@/common';
|
||||||
MyButtons,
|
import { Tabs, TabsProps } from 'antd';
|
||||||
MyPageContainer,
|
import AdminRole from './components/AdminRole';
|
||||||
MyProTableProps,
|
import EmployeeRole from './components/EmployeeRole';
|
||||||
useCurrentPermissions,
|
|
||||||
} from '@/common';
|
|
||||||
import { flattenToMultiLevelFormatWithRowSpanAdvancedNew } from '@/common/utils/flattenIterative';
|
|
||||||
import { Apis } from '@/gen/Apis';
|
|
||||||
import { ProCard, ProTable } from '@ant-design/pro-components';
|
|
||||||
import { Checkbox, message, Space, Tabs } from 'antd';
|
|
||||||
import { useEffect, useState } from 'react';
|
|
||||||
import Create from './modals/Create';
|
|
||||||
interface SelectedBuilding {
|
|
||||||
id: number;
|
|
||||||
name: string;
|
|
||||||
}
|
|
||||||
export default function Index({ title = '角色' }) {
|
export default function Index({ title = '角色' }) {
|
||||||
const getCurrentPermissions = useCurrentPermissions();
|
const items: TabsProps['items'] = [
|
||||||
const [selectedBuilding, setSelectedBuilding] =
|
{
|
||||||
useState<SelectedBuilding | null>(null);
|
key: '1',
|
||||||
const [selectedPermissionsIds, setSelectedPermissionsIds] = useState<any[]>(
|
label: '后台角色',
|
||||||
[],
|
children: <AdminRole />,
|
||||||
);
|
},
|
||||||
const [dataSource, setDataSource] = useState<any>([]);
|
{
|
||||||
const [dataTabsSource, setDataTabsSource] = useState<any>([]);
|
key: '2',
|
||||||
const [tabsKey, setTabsKey] = useState<any>('');
|
label: '员工角色',
|
||||||
const getSysPermissions = () => {
|
children: <EmployeeRole />,
|
||||||
Apis.Permission.Roles.PermissionTree().then((res) => {
|
},
|
||||||
setDataSource(
|
];
|
||||||
flattenToMultiLevelFormatWithRowSpanAdvancedNew(
|
|
||||||
res?.data[0]?.children || [],
|
|
||||||
),
|
|
||||||
);
|
|
||||||
console.log(res, 'res');
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
const onSave = () => {
|
|
||||||
if (selectedPermissionsIds?.length && selectedBuilding?.id) {
|
|
||||||
Apis.Permission.Roles.SetPermissions({
|
|
||||||
permissions_ids: selectedPermissionsIds,
|
|
||||||
id: selectedBuilding?.id || 0,
|
|
||||||
}).then(() => {
|
|
||||||
message.success('保存成功');
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
message.error('请选择角色和勾选权限!');
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const getPermissions = (id: any) => {
|
|
||||||
setTabsKey(id);
|
|
||||||
// 更新选中的角色信息
|
|
||||||
const selectedRole = dataTabsSource.find((item: any) => item.id === id);
|
|
||||||
if (selectedRole) {
|
|
||||||
setSelectedBuilding({
|
|
||||||
id: selectedRole.id,
|
|
||||||
name: selectedRole.name,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
Apis.Permission.Roles.GetPermissions({
|
|
||||||
id: id ?? 0,
|
|
||||||
}).then((res) => {
|
|
||||||
setSelectedPermissionsIds(res?.data?.permissions_ids || []);
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
const getSysRoles = () => {
|
|
||||||
Apis.Permission.Roles.List().then((res) => {
|
|
||||||
setDataTabsSource(res?.data || []);
|
|
||||||
if (res?.data?.length) {
|
|
||||||
const firstRole = res?.data[0];
|
|
||||||
getPermissions(firstRole?.id || 0);
|
|
||||||
// 初始化选中第一个角色
|
|
||||||
setSelectedBuilding({
|
|
||||||
id: firstRole?.id,
|
|
||||||
name: firstRole?.name,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
console.log(res, 'res');
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
const onSelect = () => {
|
|
||||||
//删除角色
|
|
||||||
Apis.Permission.Roles.Delete({
|
|
||||||
id: tabsKey,
|
|
||||||
}).then(() => {
|
|
||||||
getSysRoles();
|
|
||||||
message.success('删除成功');
|
|
||||||
});
|
|
||||||
};
|
|
||||||
useEffect(() => {
|
|
||||||
getSysRoles();
|
|
||||||
getSysPermissions();
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
let toolBarRender = () => {
|
|
||||||
return getCurrentPermissions({
|
|
||||||
add: <Create key="Create" reload={() => getSysRoles()} title={title} />,
|
|
||||||
delete: (
|
|
||||||
<MyButtons.Default
|
|
||||||
key="delete"
|
|
||||||
size="middle"
|
|
||||||
color="danger"
|
|
||||||
variant="solid"
|
|
||||||
isConfirm
|
|
||||||
description="是否确定删除当前角色?"
|
|
||||||
title="删除当前角色"
|
|
||||||
onConfirm={() => onSelect()}
|
|
||||||
// 如果当前选中角色是管理员,则禁用删除按钮
|
|
||||||
disabled={selectedBuilding?.name === '管理员'}
|
|
||||||
/>
|
|
||||||
),
|
|
||||||
save: (
|
|
||||||
<MyButtons.Default
|
|
||||||
key="save"
|
|
||||||
type="primary"
|
|
||||||
size="middle"
|
|
||||||
title="保存权限"
|
|
||||||
onClick={() => onSave()}
|
|
||||||
/>
|
|
||||||
),
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<MyPageContainer
|
<MyPageContainer
|
||||||
@ -131,103 +23,7 @@ export default function Index({ title = '角色' }) {
|
|||||||
tabKey="system-roles"
|
tabKey="system-roles"
|
||||||
tabLabel={title}
|
tabLabel={title}
|
||||||
>
|
>
|
||||||
<ProCard
|
<Tabs defaultActiveKey="1" type="card" items={items} />
|
||||||
title="权限配置"
|
|
||||||
style={{ width: '100%' }}
|
|
||||||
headerBordered
|
|
||||||
extra={<Space size="small">{toolBarRender()}</Space>}
|
|
||||||
>
|
|
||||||
<div style={{ display: 'flex' }}>
|
|
||||||
<div style={{ width: '130px' }}>
|
|
||||||
<Tabs
|
|
||||||
tabPosition="left"
|
|
||||||
items={dataTabsSource.map((item: any) => ({
|
|
||||||
label: item?.name,
|
|
||||||
key: item?.id,
|
|
||||||
}))}
|
|
||||||
onChange={(key: any) => {
|
|
||||||
getPermissions(key);
|
|
||||||
console.log(key, 'key');
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div style={{ flex: 1 }}>
|
|
||||||
<Checkbox.Group
|
|
||||||
style={{ width: '100%' }}
|
|
||||||
value={selectedPermissionsIds}
|
|
||||||
onChange={(e) => {
|
|
||||||
setSelectedPermissionsIds(e);
|
|
||||||
console.log(e, 'e');
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<ProTable
|
|
||||||
{...MyProTableProps.props}
|
|
||||||
search={false}
|
|
||||||
pagination={false}
|
|
||||||
// style={{ width: '100%' }}
|
|
||||||
style={{
|
|
||||||
width: '100%',
|
|
||||||
maxHeight: 'calc(100vh - 280px)', // 设置最大高度,可根据实际需求调整
|
|
||||||
overflowY: 'auto', // 启用垂直滚动
|
|
||||||
}}
|
|
||||||
dataSource={dataSource}
|
|
||||||
options={false}
|
|
||||||
size="small"
|
|
||||||
columns={[
|
|
||||||
{
|
|
||||||
title: '目录',
|
|
||||||
dataIndex: 'name',
|
|
||||||
key: 'name',
|
|
||||||
width: '120px',
|
|
||||||
render: (_, item: any) => {
|
|
||||||
return (
|
|
||||||
<Checkbox value={item?.id1}>{item?.name}</Checkbox>
|
|
||||||
);
|
|
||||||
},
|
|
||||||
onCell: (res, index?: number) => {
|
|
||||||
const rowSpan = res.row_spans?.rowSpan;
|
|
||||||
const firstIndex = res.row_spans?.firstIndex;
|
|
||||||
if (index === firstIndex && rowSpan > 0) {
|
|
||||||
return { rowSpan };
|
|
||||||
}
|
|
||||||
return { rowSpan: 0 };
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: '页面',
|
|
||||||
dataIndex: 'name2',
|
|
||||||
key: 'name2',
|
|
||||||
width: '160px',
|
|
||||||
render: (_, item: any) => {
|
|
||||||
return item?.name2 ? (
|
|
||||||
<Checkbox value={item?.id2}>{item?.name2}</Checkbox>
|
|
||||||
) : null;
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: '页签/按钮',
|
|
||||||
width: '800px',
|
|
||||||
render: (_, item: any) => {
|
|
||||||
if (item?.buttonList?.length) {
|
|
||||||
return item?.buttonList?.map(
|
|
||||||
(res: any, index: number) => {
|
|
||||||
return res?.name ? (
|
|
||||||
<Checkbox value={res?.id} key={`index_${index}`}>
|
|
||||||
{res?.name}
|
|
||||||
</Checkbox>
|
|
||||||
) : null;
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
},
|
|
||||||
]}
|
|
||||||
/>
|
|
||||||
</Checkbox.Group>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</ProCard>
|
|
||||||
</MyPageContainer>
|
</MyPageContainer>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -16,7 +16,7 @@ import {
|
|||||||
} from '@/gen/Enums';
|
} from '@/gen/Enums';
|
||||||
import { ProTable } from '@ant-design/pro-components';
|
import { ProTable } from '@ant-design/pro-components';
|
||||||
import { useSearchParams } from '@umijs/max';
|
import { useSearchParams } from '@umijs/max';
|
||||||
import { Dropdown, Space, Tooltip } from 'antd';
|
import { Space, Tooltip } from 'antd';
|
||||||
import WorkOrderAssign from './modals/WorkOrderAssign';
|
import WorkOrderAssign from './modals/WorkOrderAssign';
|
||||||
import WorkOrderCreate from './modals/WorkOrderCreate';
|
import WorkOrderCreate from './modals/WorkOrderCreate';
|
||||||
import WorkOrderShow from './modals/WorkOrderShow';
|
import WorkOrderShow from './modals/WorkOrderShow';
|
||||||
@ -28,17 +28,25 @@ export default function Index({ title = '报修报事' }) {
|
|||||||
let toolBarRender = (action: any) => {
|
let toolBarRender = (action: any) => {
|
||||||
return getCurrentPermissions({
|
return getCurrentPermissions({
|
||||||
create: (
|
create: (
|
||||||
<WorkOrderCreate
|
<WorkOrderCreate key="Create" reload={action?.reload} title={title} />
|
||||||
key="Create"
|
),
|
||||||
reload={action?.reload}
|
});
|
||||||
title={title}
|
};
|
||||||
|
let tableRender = (item: any, action: any) => {
|
||||||
|
let permissions = getCurrentPermissions({
|
||||||
|
show: <WorkOrderShow item={item} title="详情" reload={action?.reload} />,
|
||||||
|
update: (
|
||||||
|
<>
|
||||||
|
<WorkOrderUpdate
|
||||||
item={{
|
item={{
|
||||||
|
...item,
|
||||||
typeEnum: () => {
|
typeEnum: () => {
|
||||||
let obj: any = JSON.parse(
|
let obj: any = JSON.parse(
|
||||||
JSON.stringify(HouseWorkOrdersTypeEnum),
|
JSON.stringify(HouseWorkOrdersTypeEnum),
|
||||||
);
|
);
|
||||||
|
delete obj.RenovationAcceptance;
|
||||||
delete obj.EquipmentMaintenance;
|
delete obj.EquipmentMaintenance;
|
||||||
|
delete obj.RenovationInspection;
|
||||||
delete obj.Emergency;
|
delete obj.Emergency;
|
||||||
delete obj.EmergEquipmentMaintenancency;
|
delete obj.EmergEquipmentMaintenancency;
|
||||||
delete obj.SecurityInspection;
|
delete obj.SecurityInspection;
|
||||||
@ -47,37 +55,26 @@ export default function Index({ title = '报修报事' }) {
|
|||||||
return obj;
|
return obj;
|
||||||
},
|
},
|
||||||
}}
|
}}
|
||||||
|
reload={action?.reload}
|
||||||
|
title={title}
|
||||||
/>
|
/>
|
||||||
|
</>
|
||||||
),
|
),
|
||||||
});
|
|
||||||
};
|
|
||||||
let tableRender = (item: any, action: any) => {
|
|
||||||
let permissions = getCurrentPermissions({
|
|
||||||
assign: (
|
assign: (
|
||||||
|
<>
|
||||||
<WorkOrderAssign item={item} reload={action?.reload} title="指派" />
|
<WorkOrderAssign item={item} reload={action?.reload} title="指派" />
|
||||||
|
</>
|
||||||
),
|
),
|
||||||
completed: (
|
// completed: (
|
||||||
<WorkOrderShow item={item} title="回访" reload={action?.reload} />
|
// <>
|
||||||
),
|
// {item.status === 'Completed' && item.is_visited === 0 && (
|
||||||
});
|
// <WorkOrderShow item={item} title="回访" reload={action?.reload} />
|
||||||
|
// )}
|
||||||
let permissionsSpace = getCurrentPermissions({
|
// </>
|
||||||
show: {
|
// ),
|
||||||
key: '1',
|
delete: (
|
||||||
label: (
|
|
||||||
<WorkOrderShow item={item} title="详情" reload={action?.reload} />
|
|
||||||
),
|
|
||||||
},
|
|
||||||
update: {
|
|
||||||
key: '2',
|
|
||||||
label: (
|
|
||||||
<WorkOrderUpdate item={item} reload={action?.reload} title={title} />
|
|
||||||
),
|
|
||||||
},
|
|
||||||
delete: {
|
|
||||||
key: '3',
|
|
||||||
label: (
|
|
||||||
<MyButtons.Delete
|
<MyButtons.Delete
|
||||||
|
disabled={item.status !== 'Pending'}
|
||||||
onConfirm={() =>
|
onConfirm={() =>
|
||||||
Apis.WorkOrder.HouseWorkOrders.Delete({
|
Apis.WorkOrder.HouseWorkOrders.Delete({
|
||||||
id: item.id,
|
id: item.id,
|
||||||
@ -85,15 +82,9 @@ export default function Index({ title = '报修报事' }) {
|
|||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
),
|
),
|
||||||
},
|
|
||||||
});
|
});
|
||||||
|
|
||||||
let Others = (
|
return [...permissions];
|
||||||
<Dropdown menu={{ items: permissionsSpace }} trigger={['click']}>
|
|
||||||
<MyButtons.Default title="更多" />
|
|
||||||
</Dropdown>
|
|
||||||
);
|
|
||||||
return [...permissions, ...[Others]];
|
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|||||||
@ -47,7 +47,11 @@ export default function WorkOrderCreate(props: MyBetaModalFormProps) {
|
|||||||
title: '工单类型',
|
title: '工单类型',
|
||||||
colProps: { span: 24 },
|
colProps: { span: 24 },
|
||||||
// valueEnum: HouseWorkOrdersTypeEnum,
|
// valueEnum: HouseWorkOrdersTypeEnum,
|
||||||
valueEnum: props?.item?.typeEnum,
|
valueEnum: () => {
|
||||||
|
let obj: any = JSON.parse(JSON.stringify(HouseWorkOrdersTypeEnum));
|
||||||
|
delete obj.Complaint;
|
||||||
|
return obj;
|
||||||
|
},
|
||||||
required: true,
|
required: true,
|
||||||
}),
|
}),
|
||||||
{
|
{
|
||||||
|
|||||||
@ -39,12 +39,7 @@ export default function WorkOrderShow({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<MyButtons.Default
|
<MyButtons.Default onClick={handleOpen} type={'primary'} title={title} />
|
||||||
onClick={handleOpen}
|
|
||||||
disabled={item?.is_visited || item.status !== 'Completed'}
|
|
||||||
type={'primary'}
|
|
||||||
title={title}
|
|
||||||
/>
|
|
||||||
<Modal
|
<Modal
|
||||||
title={title}
|
title={title}
|
||||||
open={open}
|
open={open}
|
||||||
|
|||||||
@ -27,7 +27,12 @@ export default function WorkOrderUpdate(
|
|||||||
width="600px"
|
width="600px"
|
||||||
layout="horizontal"
|
layout="horizontal"
|
||||||
key={new Date().getTime()}
|
key={new Date().getTime()}
|
||||||
trigger={<MyButtons.Edit title={`编辑`} disabled={props.item?.status !== 'Pending'} />}
|
trigger={
|
||||||
|
<MyButtons.Edit
|
||||||
|
title={`编辑`}
|
||||||
|
disabled={props.item?.status !== 'Pending'}
|
||||||
|
/>
|
||||||
|
}
|
||||||
onOpenChange={(open: any) => {
|
onOpenChange={(open: any) => {
|
||||||
if (open && props.item) {
|
if (open && props.item) {
|
||||||
const formValues = {
|
const formValues = {
|
||||||
@ -61,7 +66,11 @@ export default function WorkOrderUpdate(
|
|||||||
title: '工单类型',
|
title: '工单类型',
|
||||||
colProps: { span: 24 },
|
colProps: { span: 24 },
|
||||||
// valueEnum: HouseWorkOrdersTypeEnum,
|
// valueEnum: HouseWorkOrdersTypeEnum,
|
||||||
valueEnum: props?.item?.typeEnum,
|
valueEnum: () => {
|
||||||
|
let obj: any = JSON.parse(JSON.stringify(HouseWorkOrdersTypeEnum));
|
||||||
|
delete obj.Complaint;
|
||||||
|
return obj;
|
||||||
|
},
|
||||||
required: true,
|
required: true,
|
||||||
}),
|
}),
|
||||||
{
|
{
|
||||||
|
|||||||
245
src/pages/work_order/patrol_work/index.tsx
Normal file
245
src/pages/work_order/patrol_work/index.tsx
Normal file
@ -0,0 +1,245 @@
|
|||||||
|
import {
|
||||||
|
MyButtons,
|
||||||
|
MyColumns,
|
||||||
|
MyProTableProps,
|
||||||
|
useCurrentPermissions,
|
||||||
|
} from '@/common';
|
||||||
|
import { Selects } from '@/components/Select';
|
||||||
|
import { Apis } from '@/gen/Apis';
|
||||||
|
import {
|
||||||
|
HouseWorkOrdersAssignStatusEnum,
|
||||||
|
HouseWorkOrdersLevelEnum,
|
||||||
|
HouseWorkOrdersLocationEnum,
|
||||||
|
HouseWorkOrdersTypeEnum,
|
||||||
|
} from '@/gen/Enums';
|
||||||
|
import { ProTable } from '@ant-design/pro-components';
|
||||||
|
import { Space } from 'antd';
|
||||||
|
import WorkOrderAssign from '../list/modals/WorkOrderAssign';
|
||||||
|
import WorkOrderCreate from '../list/modals/WorkOrderCreate';
|
||||||
|
import WorkOrderShow from '../list/modals/WorkOrderShow';
|
||||||
|
import WorkOrderUpdate from '../list/modals/WorkOrderUpdate';
|
||||||
|
|
||||||
|
export const RenovationWorkOrdersStatusEnum = {
|
||||||
|
Pending: { text: '待处理', color: '#FFA500', value: 'Pending' },
|
||||||
|
Processing: { text: '处理中', color: '#1E90FF', value: 'Processing' },
|
||||||
|
Completed: { text: '已完成', color: '#28A745', value: 'Completed' },
|
||||||
|
Closed: { text: '已关闭', color: '#6C757D', value: 'Closed' },
|
||||||
|
};
|
||||||
|
|
||||||
|
export default function PatrolWorkIndex({ title = '巡更工单' }) {
|
||||||
|
const getCurrentPermissions = useCurrentPermissions();
|
||||||
|
|
||||||
|
let tableRender = (item: any, action: any) => {
|
||||||
|
return getCurrentPermissions(
|
||||||
|
{
|
||||||
|
show: (
|
||||||
|
<WorkOrderShow item={item} title="详情" reload={action?.reload} />
|
||||||
|
),
|
||||||
|
assign: (
|
||||||
|
<>
|
||||||
|
{item.assign_status === 'Unassigned' &&
|
||||||
|
item.type !== 'SecurityInspection' && (
|
||||||
|
<WorkOrderAssign
|
||||||
|
item={item}
|
||||||
|
reload={action?.reload}
|
||||||
|
title="指派"
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</>
|
||||||
|
),
|
||||||
|
update: (
|
||||||
|
<>
|
||||||
|
{item.status === 'Pending' && (
|
||||||
|
<WorkOrderUpdate
|
||||||
|
item={{
|
||||||
|
...item,
|
||||||
|
// typeEnum: WorkTypeEnum,
|
||||||
|
}}
|
||||||
|
reload={action?.reload}
|
||||||
|
title={title}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</>
|
||||||
|
),
|
||||||
|
delete: (
|
||||||
|
<MyButtons.Delete
|
||||||
|
onConfirm={() =>
|
||||||
|
Apis.WorkOrder.HouseWorkOrders.SoftDelete({
|
||||||
|
id: item.id,
|
||||||
|
}).then(() => action?.reload())
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
),
|
||||||
|
},
|
||||||
|
'MyDecorationWorkorder',
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const WorkTypeEnum: any = () => {
|
||||||
|
let obj: any = JSON.parse(JSON.stringify(HouseWorkOrdersTypeEnum));
|
||||||
|
delete obj.Repair;
|
||||||
|
delete obj.Incident;
|
||||||
|
delete obj.Complaint;
|
||||||
|
delete obj.RenovationInspection;
|
||||||
|
delete obj.RenovationAcceptance;
|
||||||
|
delete obj.Emergency;
|
||||||
|
delete obj.EquipmentMaintenance;
|
||||||
|
delete obj.QualityCheck;
|
||||||
|
return obj;
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<ProTable<Record<any, any>>
|
||||||
|
{...MyProTableProps.props}
|
||||||
|
headerTitle={title}
|
||||||
|
request={async (params, sort) => {
|
||||||
|
return MyProTableProps.request(
|
||||||
|
{
|
||||||
|
...params,
|
||||||
|
type: [HouseWorkOrdersTypeEnum.SecurityInspection.value],
|
||||||
|
},
|
||||||
|
sort,
|
||||||
|
Apis.WorkOrder.HouseWorkOrders.List,
|
||||||
|
);
|
||||||
|
}}
|
||||||
|
toolBarRender={(action) => [
|
||||||
|
<WorkOrderCreate
|
||||||
|
key="Create"
|
||||||
|
reload={action?.reload}
|
||||||
|
title={title}
|
||||||
|
item={
|
||||||
|
{
|
||||||
|
// typeEnum: WorkTypeEnum,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/>,
|
||||||
|
]}
|
||||||
|
columns={[
|
||||||
|
MyColumns.ID({ search: false }),
|
||||||
|
Selects?.AssetProjects({
|
||||||
|
title: '选择项目',
|
||||||
|
key: 'asset_projects_id',
|
||||||
|
hidden: true,
|
||||||
|
}),
|
||||||
|
MyColumns.EnumTag({
|
||||||
|
title: '处理状态',
|
||||||
|
dataIndex: 'status',
|
||||||
|
valueEnum: RenovationWorkOrdersStatusEnum,
|
||||||
|
}),
|
||||||
|
{
|
||||||
|
title: '项目名称',
|
||||||
|
dataIndex: 'project_name',
|
||||||
|
hidden: true,
|
||||||
|
},
|
||||||
|
MyColumns.EnumTag({
|
||||||
|
title: '分配状态',
|
||||||
|
dataIndex: 'assign_status',
|
||||||
|
valueEnum: HouseWorkOrdersAssignStatusEnum,
|
||||||
|
}),
|
||||||
|
MyColumns.EnumTag({
|
||||||
|
title: '工单类型',
|
||||||
|
dataIndex: 'type',
|
||||||
|
valueEnum: HouseWorkOrdersTypeEnum,
|
||||||
|
search: false,
|
||||||
|
}),
|
||||||
|
MyColumns.EnumTag({
|
||||||
|
title: '报修位置',
|
||||||
|
dataIndex: 'location',
|
||||||
|
valueEnum: HouseWorkOrdersLocationEnum,
|
||||||
|
search: false,
|
||||||
|
}),
|
||||||
|
{
|
||||||
|
title: '位置信息',
|
||||||
|
dataIndex: ['asset_house', 'full_name'],
|
||||||
|
render: (_, record) => {
|
||||||
|
return (
|
||||||
|
<Space>
|
||||||
|
{record?.asset_house?.full_name
|
||||||
|
? record?.asset_house?.full_name
|
||||||
|
: record?.asset_project?.name}
|
||||||
|
</Space>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
search: {
|
||||||
|
transform: (value) => {
|
||||||
|
return { house_name: value };
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '工单描述',
|
||||||
|
dataIndex: 'content',
|
||||||
|
search: false,
|
||||||
|
width: 120, // 关键:固定列宽(若父容器过窄,可设 minWidth: 200 优先保证列宽)
|
||||||
|
render: (text) => (
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
width: '100%', // 继承列宽
|
||||||
|
// height: '60px', // 设置固定高度,约显示3行文本
|
||||||
|
overflow: 'hidden', // 超出隐藏
|
||||||
|
textOverflow: 'ellipsis', // 省略号
|
||||||
|
display: '-webkit-box',
|
||||||
|
WebkitBoxOrient: 'vertical',
|
||||||
|
WebkitLineClamp: 1, // 显示3行
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{text}
|
||||||
|
</div>
|
||||||
|
),
|
||||||
|
},
|
||||||
|
MyColumns.EnumTag({
|
||||||
|
title: '优先级',
|
||||||
|
dataIndex: 'level',
|
||||||
|
valueEnum: HouseWorkOrdersLevelEnum,
|
||||||
|
search: false,
|
||||||
|
}),
|
||||||
|
|
||||||
|
{
|
||||||
|
title: '处理人',
|
||||||
|
dataIndex: ['assign_employee', 'name'],
|
||||||
|
search: false,
|
||||||
|
render: (_, record) => {
|
||||||
|
return `${record?.assign_employee?.name || ''}-${
|
||||||
|
record?.assign_employee?.phone || ''
|
||||||
|
}`;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
MyColumns.CreatedAt(),
|
||||||
|
MyColumns.Option({
|
||||||
|
render: (_, item: any, index, action) => (
|
||||||
|
<Space key={index}>
|
||||||
|
<WorkOrderShow item={item} title="详情" reload={action?.reload} />
|
||||||
|
|
||||||
|
{item.assign_status === 'Unassigned' &&
|
||||||
|
item.type !== 'SecurityInspection' && (
|
||||||
|
<WorkOrderAssign
|
||||||
|
item={item}
|
||||||
|
reload={action?.reload}
|
||||||
|
title="指派"
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{item.status === 'Pending' && (
|
||||||
|
<WorkOrderUpdate
|
||||||
|
item={{
|
||||||
|
...item,
|
||||||
|
// typeEnum: WorkTypeEnum,
|
||||||
|
}}
|
||||||
|
reload={action?.reload}
|
||||||
|
title={title}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
<MyButtons.Delete
|
||||||
|
onConfirm={() =>
|
||||||
|
Apis.WorkOrder.HouseWorkOrders.SoftDelete({
|
||||||
|
id: item.id,
|
||||||
|
}).then(() => action?.reload())
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</Space>
|
||||||
|
),
|
||||||
|
}),
|
||||||
|
]}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user