项目建档:列表页、新建、查看
This commit is contained in:
@ -121,6 +121,19 @@ export default [
|
|||||||
path: '/ToAgencyAgent',
|
path: '/ToAgencyAgent',
|
||||||
component: './Agency/AgencyManager/agentIndex',
|
component: './Agency/AgencyManager/agentIndex',
|
||||||
},
|
},
|
||||||
|
/**
|
||||||
|
* 项目建档
|
||||||
|
*/
|
||||||
|
{
|
||||||
|
// 项目建档列表
|
||||||
|
path: '/ProjectFiles',
|
||||||
|
component: './ProjectFiles',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
// 项目建档新增
|
||||||
|
path: '/ProjectFiles/file',
|
||||||
|
component: './ProjectFiles/file',
|
||||||
|
},
|
||||||
//==============================================================引入的业务路由
|
//==============================================================引入的业务路由
|
||||||
...approvalForm,//审批单
|
...approvalForm,//审批单
|
||||||
...juryRoom,//评标室内所有路由
|
...juryRoom,//评标室内所有路由
|
||||||
|
26
mock/projectFile.js
Normal file
26
mock/projectFile.js
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
export default {
|
||||||
|
'GET /api/projectFiles': (req, res) => {
|
||||||
|
res.json({
|
||||||
|
data: [
|
||||||
|
{
|
||||||
|
id: 1,
|
||||||
|
name: '项目1',
|
||||||
|
projectCode: '123456',
|
||||||
|
createTime: '2021-01-01',
|
||||||
|
purchaseType: '1',
|
||||||
|
purchaseType: '1',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 2,
|
||||||
|
name: '项目2',
|
||||||
|
projectCode: '123456',
|
||||||
|
createTime: '2021-01-01',
|
||||||
|
purchaseType: '1',
|
||||||
|
purchaseType: '1',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
success: true,
|
||||||
|
total: 2,
|
||||||
|
});
|
||||||
|
},
|
||||||
|
}
|
336
src/components/CitySelect.tsx
Normal file
336
src/components/CitySelect.tsx
Normal file
@ -0,0 +1,336 @@
|
|||||||
|
import React, { useState, useEffect } from 'react';
|
||||||
|
import { Select, Row, Col } from 'antd';
|
||||||
|
import type { FormInstance } from 'antd/lib/form';
|
||||||
|
|
||||||
|
const { Option } = Select;
|
||||||
|
|
||||||
|
// 省市区数据结构
|
||||||
|
interface District {
|
||||||
|
value: string;
|
||||||
|
label: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface City {
|
||||||
|
value: string;
|
||||||
|
label: string;
|
||||||
|
districts: District[];
|
||||||
|
}
|
||||||
|
|
||||||
|
interface Province {
|
||||||
|
value: string;
|
||||||
|
label: string;
|
||||||
|
cities: City[];
|
||||||
|
}
|
||||||
|
|
||||||
|
// 模拟省市区数据
|
||||||
|
const provinceData: Province[] = [
|
||||||
|
{
|
||||||
|
value: 'beijing',
|
||||||
|
label: '北京市',
|
||||||
|
cities: [
|
||||||
|
{
|
||||||
|
value: 'dongcheng',
|
||||||
|
label: '东城区',
|
||||||
|
districts: [
|
||||||
|
{ value: 'chaoyangmen', label: '朝阳门街道' },
|
||||||
|
{ value: 'jianguomennei', label: '建国门内街道' },
|
||||||
|
{ value: 'donghuamen', label: '东华门街道' },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: 'xicheng',
|
||||||
|
label: '西城区',
|
||||||
|
districts: [
|
||||||
|
{ value: 'xichang', label: '西长安街街道' },
|
||||||
|
{ value: 'xinjieku', label: '新街口街道' },
|
||||||
|
{ value: 'yuetan', label: '月坛街道' },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: 'chaoyang',
|
||||||
|
label: '朝阳区',
|
||||||
|
districts: [
|
||||||
|
{ value: 'jianwai', label: '建外街道' },
|
||||||
|
{ value: 'chaowai', label: '朝外街道' },
|
||||||
|
{ value: 'hujialou', label: '呼家楼街道' },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: 'shanghai',
|
||||||
|
label: '上海市',
|
||||||
|
cities: [
|
||||||
|
{
|
||||||
|
value: 'huangpu',
|
||||||
|
label: '黄浦区',
|
||||||
|
districts: [
|
||||||
|
{ value: 'nanking', label: '南京东路街道' },
|
||||||
|
{ value: 'waitan', label: '外滩街道' },
|
||||||
|
{ value: 'yuyuan', label: '豫园街道' },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: 'xuhui',
|
||||||
|
label: '徐汇区',
|
||||||
|
districts: [
|
||||||
|
{ value: 'xujiahui', label: '徐家汇街道' },
|
||||||
|
{ value: 'tianlin', label: '田林街道' },
|
||||||
|
{ value: 'kangjianlou', label: '康健楼街道' },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: 'changning',
|
||||||
|
label: '长宁区',
|
||||||
|
districts: [
|
||||||
|
{ value: 'huayang', label: '华阳路街道' },
|
||||||
|
{ value: 'jiangsu', label: '江苏路街道' },
|
||||||
|
{ value: 'xinhua', label: '新华路街道' },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: 'guangdong',
|
||||||
|
label: '广东省',
|
||||||
|
cities: [
|
||||||
|
{
|
||||||
|
value: 'guangzhou',
|
||||||
|
label: '广州市',
|
||||||
|
districts: [
|
||||||
|
{ value: 'yuexiu', label: '越秀区' },
|
||||||
|
{ value: 'liwan', label: '荔湾区' },
|
||||||
|
{ value: 'haizhu', label: '海珠区' },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: 'shenzhen',
|
||||||
|
label: '深圳市',
|
||||||
|
districts: [
|
||||||
|
{ value: 'futian', label: '福田区' },
|
||||||
|
{ value: 'luohu', label: '罗湖区' },
|
||||||
|
{ value: 'nanshan', label: '南山区' },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: 'zhuhai',
|
||||||
|
label: '珠海市',
|
||||||
|
districts: [
|
||||||
|
{ value: 'xiangzhou', label: '香洲区' },
|
||||||
|
{ value: 'doumen', label: '斗门区' },
|
||||||
|
{ value: 'jinwan', label: '金湾区' },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: 'jiangsu',
|
||||||
|
label: '江苏省',
|
||||||
|
cities: [
|
||||||
|
{
|
||||||
|
value: 'nanjing',
|
||||||
|
label: '南京市',
|
||||||
|
districts: [
|
||||||
|
{ value: 'xuanwu', label: '玄武区' },
|
||||||
|
{ value: 'qinhuai', label: '秦淮区' },
|
||||||
|
{ value: 'jianye', label: '建邺区' },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: 'suzhou',
|
||||||
|
label: '苏州市',
|
||||||
|
districts: [
|
||||||
|
{ value: 'gusu', label: '姑苏区' },
|
||||||
|
{ value: 'wuzhong', label: '吴中区' },
|
||||||
|
{ value: 'xiangcheng', label: '相城区' },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
export interface CitySelectValue {
|
||||||
|
province?: string;
|
||||||
|
city?: string;
|
||||||
|
district?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface CitySelectProps {
|
||||||
|
value?: CitySelectValue;
|
||||||
|
onChange?: (value: CitySelectValue) => void;
|
||||||
|
form?: FormInstance;
|
||||||
|
placeholder?: {
|
||||||
|
province?: string;
|
||||||
|
city?: string;
|
||||||
|
district?: string;
|
||||||
|
};
|
||||||
|
disabled?: boolean;
|
||||||
|
size?: 'large' | 'middle' | 'small';
|
||||||
|
}
|
||||||
|
|
||||||
|
const CitySelect: React.FC<CitySelectProps> = ({
|
||||||
|
value = {},
|
||||||
|
onChange,
|
||||||
|
form,
|
||||||
|
placeholder = {
|
||||||
|
province: '请选择省份',
|
||||||
|
city: '请选择城市',
|
||||||
|
district: '请选择区域',
|
||||||
|
},
|
||||||
|
disabled = false,
|
||||||
|
size = 'middle',
|
||||||
|
}) => {
|
||||||
|
const [cities, setCities] = useState<City[]>([]);
|
||||||
|
const [districts, setDistricts] = useState<District[]>([]);
|
||||||
|
const [selectedProvince, setSelectedProvince] = useState<string | undefined>(value.province);
|
||||||
|
const [selectedCity, setSelectedCity] = useState<string | undefined>(value.city);
|
||||||
|
const [selectedDistrict, setSelectedDistrict] = useState<string | undefined>(value.district);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// 处理省份选择变化
|
||||||
|
const handleProvinceChange = (provinceValue?: string) => {
|
||||||
|
setSelectedProvince(provinceValue);
|
||||||
|
setSelectedCity(undefined);
|
||||||
|
setSelectedDistrict(undefined);
|
||||||
|
setDistricts([]);
|
||||||
|
|
||||||
|
if (provinceValue) {
|
||||||
|
const province = provinceData.find(p => p.value === provinceValue);
|
||||||
|
setCities(province?.cities || []);
|
||||||
|
} else {
|
||||||
|
setCities([]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 清空城市和区域的表单字段
|
||||||
|
if (form) {
|
||||||
|
form.setFieldsValue({
|
||||||
|
city: undefined,
|
||||||
|
district: undefined,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// 触发变化回调
|
||||||
|
const newValue: CitySelectValue = {
|
||||||
|
province: provinceValue,
|
||||||
|
city: undefined,
|
||||||
|
district: undefined,
|
||||||
|
};
|
||||||
|
onChange?.(newValue);
|
||||||
|
};
|
||||||
|
|
||||||
|
// 处理城市选择变化
|
||||||
|
const handleCityChange = (cityValue?: string) => {
|
||||||
|
setSelectedCity(cityValue);
|
||||||
|
setSelectedDistrict(undefined);
|
||||||
|
|
||||||
|
if (cityValue && selectedProvince) {
|
||||||
|
const province = provinceData.find(p => p.value === selectedProvince);
|
||||||
|
const city = province?.cities.find(c => c.value === cityValue);
|
||||||
|
setDistricts(city?.districts || []);
|
||||||
|
} else {
|
||||||
|
setDistricts([]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 清空区域的表单字段
|
||||||
|
if (form) {
|
||||||
|
form.setFieldsValue({
|
||||||
|
district: undefined,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// 触发变化回调
|
||||||
|
const newValue: CitySelectValue = {
|
||||||
|
province: selectedProvince,
|
||||||
|
city: cityValue,
|
||||||
|
district: undefined,
|
||||||
|
};
|
||||||
|
onChange?.(newValue);
|
||||||
|
};
|
||||||
|
|
||||||
|
// 处理区域选择变化
|
||||||
|
const handleDistrictChange = (districtValue?: string) => {
|
||||||
|
setSelectedDistrict(districtValue);
|
||||||
|
|
||||||
|
// 触发变化回调
|
||||||
|
const newValue: CitySelectValue = {
|
||||||
|
province: selectedProvince,
|
||||||
|
city: selectedCity,
|
||||||
|
district: districtValue,
|
||||||
|
};
|
||||||
|
onChange?.(newValue);
|
||||||
|
};
|
||||||
|
|
||||||
|
// 监听外部值变化
|
||||||
|
useEffect(() => {
|
||||||
|
if (value.province !== selectedProvince) {
|
||||||
|
setSelectedProvince(value.province);
|
||||||
|
handleProvinceChange(value.province);
|
||||||
|
}
|
||||||
|
if (value.city !== selectedCity) {
|
||||||
|
setSelectedCity(value.city);
|
||||||
|
handleCityChange(value.city);
|
||||||
|
}
|
||||||
|
if (value.district !== selectedDistrict) {
|
||||||
|
setSelectedDistrict(value.district);
|
||||||
|
}
|
||||||
|
}, [value]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Row gutter={8}>
|
||||||
|
<Col span={8}>
|
||||||
|
<Select
|
||||||
|
placeholder={placeholder.province}
|
||||||
|
value={selectedProvince}
|
||||||
|
onChange={handleProvinceChange}
|
||||||
|
disabled={disabled}
|
||||||
|
size={size}
|
||||||
|
style={{ width: '100%' }}
|
||||||
|
allowClear
|
||||||
|
>
|
||||||
|
{provinceData.map(province => (
|
||||||
|
<Option key={province.value} value={province.value}>
|
||||||
|
{province.label}
|
||||||
|
</Option>
|
||||||
|
))}
|
||||||
|
</Select>
|
||||||
|
</Col>
|
||||||
|
<Col span={8}>
|
||||||
|
<Select
|
||||||
|
placeholder={placeholder.city}
|
||||||
|
value={selectedCity}
|
||||||
|
onChange={handleCityChange}
|
||||||
|
disabled={disabled || !selectedProvince}
|
||||||
|
size={size}
|
||||||
|
style={{ width: '100%' }}
|
||||||
|
allowClear
|
||||||
|
>
|
||||||
|
{cities.map(city => (
|
||||||
|
<Option key={city.value} value={city.value}>
|
||||||
|
{city.label}
|
||||||
|
</Option>
|
||||||
|
))}
|
||||||
|
</Select>
|
||||||
|
</Col>
|
||||||
|
<Col span={8}>
|
||||||
|
<Select
|
||||||
|
placeholder={placeholder.district}
|
||||||
|
value={selectedDistrict}
|
||||||
|
onChange={handleDistrictChange}
|
||||||
|
disabled={disabled || !selectedCity}
|
||||||
|
size={size}
|
||||||
|
style={{ width: '100%' }}
|
||||||
|
allowClear
|
||||||
|
>
|
||||||
|
{districts.map(district => (
|
||||||
|
<Option key={district.value} value={district.value}>
|
||||||
|
{district.label}
|
||||||
|
</Option>
|
||||||
|
))}
|
||||||
|
</Select>
|
||||||
|
</Col>
|
||||||
|
</Row>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default CitySelect;
|
@ -1,7 +1,7 @@
|
|||||||
import request from '@/utils/request';
|
import request from '@/utils/request';
|
||||||
|
|
||||||
export async function fgetUserMsg(params: any) {
|
export async function fgetUserMsg(params: any) {
|
||||||
return request('/api/core-service-ebtp-userinfo/v1/userinfo/get', {
|
return request('/api/sys-manager-ebtp-project/v1/userinfo/get', {
|
||||||
method: 'GET',
|
method: 'GET',
|
||||||
headers: { 'Authorization': params },
|
headers: { 'Authorization': params },
|
||||||
data: params,
|
data: params,
|
||||||
|
16
src/pages/ProjectFiles/data.d.ts
vendored
Normal file
16
src/pages/ProjectFiles/data.d.ts
vendored
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
export type TableListItem = {
|
||||||
|
id: string;
|
||||||
|
name: string;
|
||||||
|
projectCode: string;
|
||||||
|
createTime: string;
|
||||||
|
purchaseType: string;
|
||||||
|
purchaseType: string;
|
||||||
|
status: string;
|
||||||
|
version: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type TableListPagination = {
|
||||||
|
total: number;
|
||||||
|
pageSize: number;
|
||||||
|
current: number;
|
||||||
|
};
|
137
src/pages/ProjectFiles/dict.ts
Normal file
137
src/pages/ProjectFiles/dict.ts
Normal file
@ -0,0 +1,137 @@
|
|||||||
|
// 资金性质
|
||||||
|
const fundNatureOptions = [
|
||||||
|
{ label: '固定资产投资', value: '1' },
|
||||||
|
{ label: '费用成本开支', value: '2' },
|
||||||
|
{ label: '通用物资固定资产投资为主', value: '3' },
|
||||||
|
{ label: '通用物资费用成本开支为主', value: '4' },
|
||||||
|
{ label: '无收无支', value: '5' },
|
||||||
|
{ label: '通用物资固定资产购置为主', value: '6' },
|
||||||
|
{ label: '固定资产购置', value: '7' },
|
||||||
|
];
|
||||||
|
|
||||||
|
// 资金来源
|
||||||
|
const fundsProviderOptions = [
|
||||||
|
{ label: '国企自筹', value: '1' },
|
||||||
|
{ label: '中央投资', value: '2' },
|
||||||
|
{ label: '地方政府投资', value: '3' },
|
||||||
|
{ label: '财政资金', value: '4' },
|
||||||
|
{ label: '外贷', value: '5' },
|
||||||
|
{ label: '其他', value: '6' },
|
||||||
|
];
|
||||||
|
|
||||||
|
// 预算类型
|
||||||
|
const budgetTypeOptions = [
|
||||||
|
{ label: '标段预算', value: '1' },
|
||||||
|
{ label: '仅有整体项目预算', value: '2' },
|
||||||
|
{ label: '有预估采购规模(标段预计金额)', value: '3' },
|
||||||
|
{ label: '无预算', value: '4' },
|
||||||
|
];
|
||||||
|
|
||||||
|
// 项目所在行政区域类型
|
||||||
|
const regionDictTypeOptions = [
|
||||||
|
{ label: '境内', value: '1' },
|
||||||
|
{ label: '境外', value: '2' },
|
||||||
|
];
|
||||||
|
const regionOutsideOptions = [
|
||||||
|
{ label: '亚洲(除中国)', value: '1' },
|
||||||
|
{ label: '欧洲', value: '2' },
|
||||||
|
{ label: '北美洲', value: '3' },
|
||||||
|
{ label: '南美洲', value: '4' },
|
||||||
|
{ label: '非洲', value: '5' },
|
||||||
|
{ label: '大洋洲', value: '6' },
|
||||||
|
{ label: '南极洲', value: '7' },
|
||||||
|
];
|
||||||
|
|
||||||
|
// 币种
|
||||||
|
const currencyCodeOptions = [
|
||||||
|
{ label: 'CNY', value: 'CNY' },
|
||||||
|
{ label: 'EUR', value: 'EUR' },
|
||||||
|
{ label: 'USD', value: 'USD' },
|
||||||
|
];
|
||||||
|
|
||||||
|
// 招标类型
|
||||||
|
const openTenderFormEnum = {
|
||||||
|
1: '依法必招',
|
||||||
|
2: '自愿招标',
|
||||||
|
};
|
||||||
|
|
||||||
|
// 采购方式
|
||||||
|
const purchaseTypeEnum = {
|
||||||
|
1: '公开招标',
|
||||||
|
2: '邀请招标',
|
||||||
|
};
|
||||||
|
|
||||||
|
// 资审方式
|
||||||
|
const reviewMethodEnum = {
|
||||||
|
1: '后审',
|
||||||
|
2: '资审',
|
||||||
|
};
|
||||||
|
|
||||||
|
// 组织形式
|
||||||
|
const organizationFormEnum = {
|
||||||
|
1: '自主招标',
|
||||||
|
2: '委托代理',
|
||||||
|
};
|
||||||
|
|
||||||
|
// 招标代理机构
|
||||||
|
const tenderAgencyEnum = {
|
||||||
|
1: '广州中远海运建设实业有限公司',
|
||||||
|
};
|
||||||
|
|
||||||
|
// 报价方式
|
||||||
|
const bidMethodEnum = {
|
||||||
|
1: '总价',
|
||||||
|
2: '单价',
|
||||||
|
3: '优惠率',
|
||||||
|
4: '折扣率',
|
||||||
|
};
|
||||||
|
|
||||||
|
// 评价方法
|
||||||
|
const evaluationMethodEnum = {
|
||||||
|
1: '最低价法',
|
||||||
|
2: '综合评估法',
|
||||||
|
3: '合理低价法',
|
||||||
|
};
|
||||||
|
|
||||||
|
// 资格审查方法
|
||||||
|
const reviewWayEnum = {
|
||||||
|
1: '合格制',
|
||||||
|
2: '有限数量制',
|
||||||
|
};
|
||||||
|
|
||||||
|
// 流程类型
|
||||||
|
const processEnum = {
|
||||||
|
1: '第一轮初审详审,固定流程',
|
||||||
|
};
|
||||||
|
|
||||||
|
// 标的类别1
|
||||||
|
const subjectTypeOptions = [
|
||||||
|
{ label: '货物', value: '1' },
|
||||||
|
{ label: '工程', value: '2' },
|
||||||
|
{ label: '服务', value: '3' },
|
||||||
|
]
|
||||||
|
|
||||||
|
// 标的类别2
|
||||||
|
const subjectType2Options = [
|
||||||
|
{ label: '土地、建筑物及构筑物', value: '1' },
|
||||||
|
];
|
||||||
|
|
||||||
|
export {
|
||||||
|
fundNatureOptions,
|
||||||
|
fundsProviderOptions,
|
||||||
|
budgetTypeOptions,
|
||||||
|
regionDictTypeOptions,
|
||||||
|
regionOutsideOptions,
|
||||||
|
currencyCodeOptions,
|
||||||
|
purchaseTypeEnum,
|
||||||
|
openTenderFormEnum,
|
||||||
|
reviewMethodEnum,
|
||||||
|
organizationFormEnum,
|
||||||
|
tenderAgencyEnum,
|
||||||
|
bidMethodEnum,
|
||||||
|
evaluationMethodEnum,
|
||||||
|
reviewWayEnum,
|
||||||
|
processEnum,
|
||||||
|
subjectTypeOptions,
|
||||||
|
subjectType2Options,
|
||||||
|
};
|
747
src/pages/ProjectFiles/file.tsx
Normal file
747
src/pages/ProjectFiles/file.tsx
Normal file
@ -0,0 +1,747 @@
|
|||||||
|
import { CloseCircleOutlined, MinusCircleOutlined, PlusOutlined } from '@ant-design/icons';
|
||||||
|
import { Button, Card, Col, Form, Popover, Row, message, Divider, Select, Input, Upload } from 'antd';
|
||||||
|
import { history, useLocation } from 'umi';
|
||||||
|
import type { Location } from 'umi';
|
||||||
|
|
||||||
|
import type { FC } from 'react';
|
||||||
|
import React, { useState, Fragment } from 'react';
|
||||||
|
import ProForm, {
|
||||||
|
ProFormSelect,
|
||||||
|
ProFormText,
|
||||||
|
ProFormRadio,
|
||||||
|
ProFormDigit,
|
||||||
|
ProFormDependency,
|
||||||
|
ProFormUploadButton,
|
||||||
|
} from '@ant-design/pro-form';
|
||||||
|
import { PageContainer, FooterToolbar } from '@ant-design/pro-layout';
|
||||||
|
import { submitForm } from './service';
|
||||||
|
import styles from './style.less';
|
||||||
|
import {
|
||||||
|
fundNatureOptions,
|
||||||
|
fundsProviderOptions,
|
||||||
|
budgetTypeOptions,
|
||||||
|
regionDictTypeOptions,
|
||||||
|
regionOutsideOptions,
|
||||||
|
currencyCodeOptions,
|
||||||
|
purchaseTypeEnum,
|
||||||
|
openTenderFormEnum,
|
||||||
|
reviewMethodEnum,
|
||||||
|
organizationFormEnum,
|
||||||
|
tenderAgencyEnum,
|
||||||
|
bidMethodEnum,
|
||||||
|
evaluationMethodEnum,
|
||||||
|
reviewWayEnum,
|
||||||
|
processEnum,
|
||||||
|
subjectTypeOptions,
|
||||||
|
subjectType2Options,
|
||||||
|
} from './dict';
|
||||||
|
import CitySelect from '@/components/CitySelect';
|
||||||
|
|
||||||
|
|
||||||
|
type InternalNamePath = (string | number)[];
|
||||||
|
|
||||||
|
const fieldLabels = {
|
||||||
|
projectName: '项目名称',
|
||||||
|
ProjectBizNo: '项目编号',
|
||||||
|
tendereeOrgId: '项目归属单位', //
|
||||||
|
tendereeId: '项目归属部门', //
|
||||||
|
appManagerId: '项目负责人',
|
||||||
|
appManagerTel: '联系电话',
|
||||||
|
fundNature: '资金性质', //
|
||||||
|
fundsProviderDict: '资金来源',
|
||||||
|
budgetType: '预算类型', //
|
||||||
|
regionDictType: '项目所在行政区域类型', //?
|
||||||
|
regionDict: '项目所在行政区域', //?
|
||||||
|
budgetAmount: '项目预算',
|
||||||
|
currencyCode: '币种',
|
||||||
|
openTenderForm: '招标类型',
|
||||||
|
bidMethodDict: '采购方式',
|
||||||
|
reviewMethod: '资审方式', //
|
||||||
|
bidOrgDict: '组织形式', //
|
||||||
|
tenderAgencyId: '招标代理机构', //
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
interface ErrorField {
|
||||||
|
name: InternalNamePath;
|
||||||
|
errors: string[];
|
||||||
|
}
|
||||||
|
|
||||||
|
// 主要标的表单控件
|
||||||
|
const SubjectFormItem = ({ value = [], onChange, disabled = false }: { value?: string[], onChange?: (value: string[]) => void, disabled?: boolean }) => {
|
||||||
|
return (
|
||||||
|
<Row gutter={8}>
|
||||||
|
<Col span={8}>
|
||||||
|
<Select
|
||||||
|
value={value[0]}
|
||||||
|
onChange={(v1: string) => onChange?.([v1, value[1] || ''])}
|
||||||
|
placeholder="请选择主要标的类别"
|
||||||
|
disabled={disabled}
|
||||||
|
>
|
||||||
|
{subjectTypeOptions.map(item => (
|
||||||
|
<Select.Option key={item.value} value={item.value}>{item.label}</Select.Option>
|
||||||
|
))}
|
||||||
|
</Select>
|
||||||
|
</Col>
|
||||||
|
<Col span={16}>
|
||||||
|
<Select
|
||||||
|
value={value[1]}
|
||||||
|
onChange={(v2: string) => onChange?.([value[0] || '', v2])}
|
||||||
|
placeholder="请选择主要标的类别"
|
||||||
|
disabled={disabled}
|
||||||
|
>
|
||||||
|
{subjectType2Options.map(item => (
|
||||||
|
<Select.Option key={item.value} value={item.value}>{item.label}</Select.Option>
|
||||||
|
))}
|
||||||
|
</Select>
|
||||||
|
</Col>
|
||||||
|
</Row>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
const normFile = (e: any) => {
|
||||||
|
console.log('Upload event:', e);
|
||||||
|
if (Array.isArray(e)) {
|
||||||
|
return e;
|
||||||
|
}
|
||||||
|
return e?.fileList;
|
||||||
|
};
|
||||||
|
|
||||||
|
const ProjectFileCreate: FC<Record<string, any>> = () => {
|
||||||
|
const { query }: Location = useLocation();
|
||||||
|
const readOnly = query?.action === 'view';
|
||||||
|
const id = query?.id; // 文件 id
|
||||||
|
|
||||||
|
const [regionDictType, setRegionDictType] = useState<string>('1');
|
||||||
|
const [error, setError] = useState<ErrorField[]>([]);
|
||||||
|
const getErrorInfo = (errors: ErrorField[]) => {
|
||||||
|
const errorCount = errors.filter((item) => item.errors.length > 0).length;
|
||||||
|
if (!errors || errorCount === 0) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
const scrollToField = (fieldKey: string) => {
|
||||||
|
const labelNode = document.querySelector(`label[for="${fieldKey}"]`);
|
||||||
|
if (labelNode) {
|
||||||
|
labelNode.scrollIntoView(true);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
const errorList = errors.map((err) => {
|
||||||
|
if (!err || err.errors.length === 0) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
const key = err.name[0] as string;
|
||||||
|
return (
|
||||||
|
<li key={key} className={styles.errorListItem} onClick={() => scrollToField(key)}>
|
||||||
|
<CloseCircleOutlined className={styles.errorIcon} />
|
||||||
|
<div className={styles.errorMessage}>{err.errors[0]}</div>
|
||||||
|
<div className={styles.errorField}>{fieldLabels[key as keyof typeof fieldLabels]}</div>
|
||||||
|
</li>
|
||||||
|
);
|
||||||
|
});
|
||||||
|
return (
|
||||||
|
<span className={styles.errorIcon}>
|
||||||
|
<Popover
|
||||||
|
title="表单校验信息"
|
||||||
|
content={errorList}
|
||||||
|
overlayClassName={styles.errorPopover}
|
||||||
|
trigger="click"
|
||||||
|
getPopupContainer={(trigger: HTMLElement) => {
|
||||||
|
if (trigger && trigger.parentNode) {
|
||||||
|
return trigger.parentNode as HTMLElement;
|
||||||
|
}
|
||||||
|
return trigger;
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<CloseCircleOutlined />
|
||||||
|
</Popover>
|
||||||
|
{errorCount}
|
||||||
|
</span>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const onFinish = async (values: Record<string, any>) => {
|
||||||
|
setError([]);
|
||||||
|
try {
|
||||||
|
console.log(values);
|
||||||
|
await submitForm(values);
|
||||||
|
message.success('提交成功');
|
||||||
|
} catch {
|
||||||
|
// console.log
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const onFinishFailed = (errorInfo: any) => {
|
||||||
|
setError(errorInfo.errorFields);
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className={styles.createProjectFile} style={{ backgroundColor: '#f8f8f8' }}>
|
||||||
|
<ProForm
|
||||||
|
layout="horizontal"
|
||||||
|
requiredMark
|
||||||
|
submitter={{
|
||||||
|
searchConfig: {
|
||||||
|
submitText: '建档完成',
|
||||||
|
},
|
||||||
|
render: (props, dom) => {
|
||||||
|
if (readOnly) {
|
||||||
|
return (
|
||||||
|
<FooterToolbar className={styles.footerToolbar}>
|
||||||
|
<Button key="cancel" onClick={() => {
|
||||||
|
history.replace('/ProjectFiles');
|
||||||
|
}}>
|
||||||
|
返回
|
||||||
|
</Button>
|
||||||
|
</FooterToolbar>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
const _dom = dom.filter((item: JSX.Element) => item.key !== 'rest').concat([
|
||||||
|
<Button key="stash" type="primary" onClick={() => {
|
||||||
|
console.log('暂存');
|
||||||
|
}}>
|
||||||
|
暂存
|
||||||
|
</Button>
|
||||||
|
]).concat([
|
||||||
|
<Button key="cancel" onClick={() => {
|
||||||
|
history.replace('/ProjectFiles');
|
||||||
|
}}>
|
||||||
|
取消
|
||||||
|
</Button>
|
||||||
|
]);
|
||||||
|
return (
|
||||||
|
<FooterToolbar className={styles.footerToolbar}>
|
||||||
|
{getErrorInfo(error)}
|
||||||
|
{_dom}
|
||||||
|
</FooterToolbar>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
initialValues={{
|
||||||
|
budgetType: '1',
|
||||||
|
regionDictType: '1',
|
||||||
|
budgetAmount: 0,
|
||||||
|
currencyCode: 'CNY',
|
||||||
|
bidSection: [{
|
||||||
|
bidSectionIndex: 1,
|
||||||
|
bidSectionName: '',
|
||||||
|
bidSectionNo: '',
|
||||||
|
bidSectionBudget: 0,
|
||||||
|
bidMethod: '',
|
||||||
|
bidSectionCategory: [{
|
||||||
|
type: [],
|
||||||
|
percent: 0,
|
||||||
|
}],
|
||||||
|
}],
|
||||||
|
}}
|
||||||
|
onFinish={onFinish}
|
||||||
|
onFinishFailed={onFinishFailed}
|
||||||
|
labelCol={{ span: 5 }}
|
||||||
|
wrapperCol={{ span: 19 }}
|
||||||
|
>
|
||||||
|
<PageContainer title="新建项目" style={{ paddingBottom: 50 }}>
|
||||||
|
<Card title="招标项目概况" className={styles.card} bordered={false}>
|
||||||
|
<Row gutter={16}>
|
||||||
|
<Col span={12}>
|
||||||
|
<ProFormText
|
||||||
|
label={fieldLabels.projectName}
|
||||||
|
name="projectName"
|
||||||
|
rules={[{ required: true, message: '请输入项目名称' }]}
|
||||||
|
placeholder="请输入"
|
||||||
|
fieldProps={{
|
||||||
|
disabled: readOnly,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</Col>
|
||||||
|
<Col span={12}>
|
||||||
|
<ProFormText
|
||||||
|
label={fieldLabels.ProjectBizNo}
|
||||||
|
name="ProjectBizNo"
|
||||||
|
rules={[{ required: true, message: '请输入项目编号' }]}
|
||||||
|
placeholder="请输入"
|
||||||
|
fieldProps={{
|
||||||
|
disabled: readOnly,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</Col>
|
||||||
|
</Row>
|
||||||
|
<Row gutter={16}>
|
||||||
|
<Col span={12}>
|
||||||
|
<ProFormText
|
||||||
|
label={fieldLabels.tendereeOrgId}
|
||||||
|
name="tendereeOrgId"
|
||||||
|
rules={[{ required: true, message: '请输入项目归属单位' }]}
|
||||||
|
fieldProps={{
|
||||||
|
disabled: true,
|
||||||
|
}}
|
||||||
|
placeholder="请输入"
|
||||||
|
/>
|
||||||
|
</Col>
|
||||||
|
<Col span={12}>
|
||||||
|
<ProFormText
|
||||||
|
label={fieldLabels.tendereeId}
|
||||||
|
name="tendereeId"
|
||||||
|
rules={[{ required: true, message: '请输入项目归属部门' }]}
|
||||||
|
fieldProps={{
|
||||||
|
disabled: true,
|
||||||
|
}}
|
||||||
|
placeholder="请输入"
|
||||||
|
/>
|
||||||
|
</Col>
|
||||||
|
</Row>
|
||||||
|
<Row gutter={16}>
|
||||||
|
<Col span={12}>
|
||||||
|
<ProFormText
|
||||||
|
label={fieldLabels.appManagerId}
|
||||||
|
name="appManagerId"
|
||||||
|
rules={[{ required: true, message: '请输入项目负责人' }]}
|
||||||
|
placeholder="请输入"
|
||||||
|
fieldProps={{
|
||||||
|
disabled: readOnly,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</Col>
|
||||||
|
<Col span={12}>
|
||||||
|
<ProFormText
|
||||||
|
label={fieldLabels.appManagerTel}
|
||||||
|
name="appManagerTel"
|
||||||
|
rules={[{ required: true, message: '请输入联系电话' }]}
|
||||||
|
fieldProps={{
|
||||||
|
disabled: readOnly,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</Col>
|
||||||
|
</Row>
|
||||||
|
<Row gutter={16}>
|
||||||
|
<Col span={12}>
|
||||||
|
<ProFormSelect
|
||||||
|
label={fieldLabels.fundNature}
|
||||||
|
name="fundNature"
|
||||||
|
rules={[{ required: true, message: '请选择资金性质' }]}
|
||||||
|
options={fundNatureOptions}
|
||||||
|
fieldProps={{
|
||||||
|
disabled: readOnly,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</Col>
|
||||||
|
<Col span={12}>
|
||||||
|
<ProFormSelect
|
||||||
|
label={fieldLabels.fundsProviderDict}
|
||||||
|
name="fundsProviderDict"
|
||||||
|
rules={[{ required: true, message: '请选择资金来源' }]}
|
||||||
|
options={fundsProviderOptions}
|
||||||
|
fieldProps={{
|
||||||
|
disabled: readOnly,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</Col>
|
||||||
|
</Row>
|
||||||
|
<Row gutter={16}>
|
||||||
|
<Col span={12}>
|
||||||
|
<ProFormSelect
|
||||||
|
label={fieldLabels.budgetType}
|
||||||
|
name="budgetType"
|
||||||
|
rules={[{ required: true, message: '请选择预算类型' }]}
|
||||||
|
options={budgetTypeOptions}
|
||||||
|
fieldProps={{
|
||||||
|
disabled: readOnly,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</Col>
|
||||||
|
<Col span={12}>
|
||||||
|
<ProFormRadio.Group
|
||||||
|
name="regionDictType"
|
||||||
|
label={fieldLabels.regionDictType}
|
||||||
|
rules={[{ required: true, message: '请选择项目所在行政区域类型' }]}
|
||||||
|
options={regionDictTypeOptions}
|
||||||
|
fieldProps={{
|
||||||
|
onChange: (e) => {
|
||||||
|
setRegionDictType(e.target.value);
|
||||||
|
},
|
||||||
|
disabled: readOnly,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</Col>
|
||||||
|
</Row>
|
||||||
|
<Row gutter={16}>
|
||||||
|
<Col span={12}>
|
||||||
|
<ProFormDependency name={['bidSection']}>
|
||||||
|
{({ bidSection }) => {
|
||||||
|
// 计算所有标段预算之和
|
||||||
|
const total = Array.isArray(bidSection)
|
||||||
|
? bidSection.reduce((sum, item) => sum + (parseFloat(item?.bidSectionBudget) || 0), 0)
|
||||||
|
: 0;
|
||||||
|
return (
|
||||||
|
<ProFormDigit
|
||||||
|
label={fieldLabels.budgetAmount}
|
||||||
|
name="budgetAmount"
|
||||||
|
fieldProps={{
|
||||||
|
precision: 2,
|
||||||
|
value: total,
|
||||||
|
disabled: true,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}}
|
||||||
|
</ProFormDependency>
|
||||||
|
</Col>
|
||||||
|
<Col span={12}>
|
||||||
|
{regionDictType === '1' ? (
|
||||||
|
<ProForm.Item
|
||||||
|
name="regionDict"
|
||||||
|
label={<span>​</span>}
|
||||||
|
required={false}
|
||||||
|
colon={false}
|
||||||
|
rules={[
|
||||||
|
{
|
||||||
|
required: true,
|
||||||
|
validator: (_, value) => {
|
||||||
|
if (!value || !value.province || !value.city || !value.district) {
|
||||||
|
return Promise.reject(new Error('请完整选择省市区'));
|
||||||
|
}
|
||||||
|
return Promise.resolve();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]}
|
||||||
|
>
|
||||||
|
<CitySelect
|
||||||
|
placeholder={{
|
||||||
|
province: '请选择省份',
|
||||||
|
city: '请选择城市',
|
||||||
|
district: '请选择区域',
|
||||||
|
}}
|
||||||
|
disabled={readOnly}
|
||||||
|
/>
|
||||||
|
</ProForm.Item>
|
||||||
|
) : (
|
||||||
|
<ProFormSelect
|
||||||
|
name="regionDict"
|
||||||
|
label={<span>​</span>}
|
||||||
|
required={false}
|
||||||
|
colon={false}
|
||||||
|
rules={[{ required: true, message: '请选择项目所在行政区域' }]}
|
||||||
|
options={regionOutsideOptions}
|
||||||
|
placeholder="请选择行政区域"
|
||||||
|
fieldProps={{
|
||||||
|
disabled: readOnly,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</Col>
|
||||||
|
</Row>
|
||||||
|
<Row gutter={16}>
|
||||||
|
<Col span={12}>
|
||||||
|
<ProFormSelect
|
||||||
|
label={fieldLabels.currencyCode}
|
||||||
|
name="currencyCode"
|
||||||
|
rules={[{ required: true, message: '请选择币种' }]}
|
||||||
|
options={currencyCodeOptions}
|
||||||
|
fieldProps={{
|
||||||
|
disabled: readOnly,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</Col>
|
||||||
|
<Col span={12}>
|
||||||
|
<ProFormSelect
|
||||||
|
label={fieldLabels.openTenderForm}
|
||||||
|
name="openTenderForm"
|
||||||
|
rules={[{ required: true, message: '请选择招标类型' }]}
|
||||||
|
valueEnum={openTenderFormEnum}
|
||||||
|
fieldProps={{
|
||||||
|
disabled: readOnly,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</Col>
|
||||||
|
</Row>
|
||||||
|
</Card>
|
||||||
|
<Card title="拟采用的招标方式和组织形式" className={styles.card} bordered={false}>
|
||||||
|
<Row gutter={16}>
|
||||||
|
<Col span={12}>
|
||||||
|
<ProFormSelect
|
||||||
|
label={fieldLabels.bidMethodDict}
|
||||||
|
name="bidMethodDict"
|
||||||
|
rules={[{ required: true, message: '请选择采购方式' }]}
|
||||||
|
valueEnum={purchaseTypeEnum}
|
||||||
|
fieldProps={{
|
||||||
|
disabled: readOnly,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</Col>
|
||||||
|
<Col span={12}>
|
||||||
|
<ProFormSelect
|
||||||
|
label={fieldLabels.reviewMethod}
|
||||||
|
name="reviewMethod"
|
||||||
|
rules={[{ required: true, message: '请选择资审方式' }]}
|
||||||
|
valueEnum={reviewMethodEnum}
|
||||||
|
fieldProps={{
|
||||||
|
disabled: readOnly,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</Col>
|
||||||
|
</Row>
|
||||||
|
<Row gutter={16}>
|
||||||
|
<Col span={12}>
|
||||||
|
<ProFormSelect
|
||||||
|
label={fieldLabels.bidOrgDict}
|
||||||
|
name="bidOrgDict"
|
||||||
|
rules={[{ required: true, message: '请选择组织形式' }]}
|
||||||
|
valueEnum={organizationFormEnum}
|
||||||
|
fieldProps={{
|
||||||
|
disabled: readOnly,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</Col>
|
||||||
|
<Col span={12}>
|
||||||
|
<ProFormSelect
|
||||||
|
label={fieldLabels.tenderAgencyId}
|
||||||
|
name="tenderAgencyId"
|
||||||
|
rules={[{ required: true, message: '请选择招标代理机构' }]}
|
||||||
|
valueEnum={tenderAgencyEnum}
|
||||||
|
fieldProps={{
|
||||||
|
disabled: readOnly,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</Col>
|
||||||
|
</Row>
|
||||||
|
</Card>
|
||||||
|
<Card title="标段(包)信息" className={styles.card} bordered={false}>
|
||||||
|
<Row>
|
||||||
|
<Col span={24}>
|
||||||
|
<Form.List name="bidSection">
|
||||||
|
{(fields, { add, remove }) => (
|
||||||
|
<>
|
||||||
|
<Row style={{ marginBottom: 16 }}>
|
||||||
|
<Col span={24}>
|
||||||
|
<Button type="primary" onClick={() => add({ bidSectionIndex: fields.length + 1, bidSectionCategory: [{ type: [], percent: 0 }] })} disabled={readOnly}>
|
||||||
|
新增标段
|
||||||
|
</Button>
|
||||||
|
</Col>
|
||||||
|
</Row>
|
||||||
|
{fields.map(field => (
|
||||||
|
<Fragment key={field.key}>
|
||||||
|
{field.key !== 0 && <Divider />}
|
||||||
|
<Row >
|
||||||
|
<Col span={8}>
|
||||||
|
<ProFormText
|
||||||
|
{...field}
|
||||||
|
label="标段序号"
|
||||||
|
name={[field.name, 'bidSectionIndex']}
|
||||||
|
readonly
|
||||||
|
/>
|
||||||
|
</Col>
|
||||||
|
<Col span={8}>
|
||||||
|
<ProFormText
|
||||||
|
{...field}
|
||||||
|
label="标段名称"
|
||||||
|
name={[field.name, 'bidSectionName']}
|
||||||
|
rules={[{ required: true, message: '请输入标段名称' }]}
|
||||||
|
fieldProps={{
|
||||||
|
disabled: readOnly,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</Col>
|
||||||
|
|
||||||
|
<Col span={8}>
|
||||||
|
<Button danger onClick={() => remove(field.name)} style={{ marginLeft: 16 }} hidden={fields.length === 1} disabled={readOnly}>
|
||||||
|
删除标段
|
||||||
|
</Button>
|
||||||
|
</Col>
|
||||||
|
|
||||||
|
<Col span={8}>
|
||||||
|
<ProFormText
|
||||||
|
{...field}
|
||||||
|
label="标段编号"
|
||||||
|
name={[field.name, 'bidSectionNo']}
|
||||||
|
rules={[{ required: true, message: '请输入标段编号' }]}
|
||||||
|
fieldProps={{
|
||||||
|
disabled: readOnly,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</Col>
|
||||||
|
<Col span={8} style={{ position: 'relative' }}>
|
||||||
|
<ProFormDependency name={['currencyCode']}>
|
||||||
|
{({ currencyCode }) => {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<ProFormDigit
|
||||||
|
label="标段预算"
|
||||||
|
name={[field.name, 'bidSectionBudget']}
|
||||||
|
rules={[{ required: true, message: '请输入标段预算' }]}
|
||||||
|
fieldProps={{ precision: 2, style: { width: '80%' } }}
|
||||||
|
disabled={readOnly}
|
||||||
|
/>
|
||||||
|
<span style={{ position: 'absolute', right: '6%', top: 5 }}>{currencyCode}</span>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}}
|
||||||
|
</ProFormDependency>
|
||||||
|
</Col>
|
||||||
|
|
||||||
|
<Col span={8} />
|
||||||
|
|
||||||
|
<Col span={8}>
|
||||||
|
<ProFormSelect
|
||||||
|
label="报价方式"
|
||||||
|
name={[field.name, 'bidMethod']}
|
||||||
|
rules={[{ required: true, message: '请选择报价方式' }]}
|
||||||
|
valueEnum={bidMethodEnum}
|
||||||
|
fieldProps={{
|
||||||
|
disabled: readOnly,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</Col>
|
||||||
|
<Col span={8}>
|
||||||
|
<ProFormSelect
|
||||||
|
label="评价方法"
|
||||||
|
name={[field.name, 'evaluationMethod']}
|
||||||
|
rules={[{ required: true, message: '请选择评价方法' }]}
|
||||||
|
valueEnum={evaluationMethodEnum}
|
||||||
|
fieldProps={{
|
||||||
|
disabled: readOnly,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</Col>
|
||||||
|
<Col span={8}>
|
||||||
|
<ProFormSelect
|
||||||
|
label="资格审查方法"
|
||||||
|
name={[field.name, 'reviewWay']}
|
||||||
|
rules={[{ required: true, message: '请选择资格审查方法' }]}
|
||||||
|
valueEnum={reviewWayEnum}
|
||||||
|
fieldProps={{
|
||||||
|
disabled: readOnly,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</Col>
|
||||||
|
<Col span={8}>
|
||||||
|
<ProFormSelect
|
||||||
|
label="流程类型"
|
||||||
|
name={[field.name, 'process']}
|
||||||
|
rules={[{ required: true, message: '请选择流程类型' }]}
|
||||||
|
valueEnum={processEnum}
|
||||||
|
fieldProps={{
|
||||||
|
disabled: readOnly,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</Col>
|
||||||
|
<Col span={8}>
|
||||||
|
<ProFormDigit
|
||||||
|
label="评标专家人数"
|
||||||
|
name={[field.name, 'reviewExpertCount']}
|
||||||
|
rules={[{ required: true, message: '请输入评标专家人数' }]}
|
||||||
|
fieldProps={{ precision: 0, disabled: readOnly }}
|
||||||
|
min={0}
|
||||||
|
/>
|
||||||
|
</Col>
|
||||||
|
<Col span={8} />
|
||||||
|
|
||||||
|
<Col span={24}>
|
||||||
|
<Form.List name={[field.name, 'bidSectionCategory']} >
|
||||||
|
{(subFields, { add: addCategory, remove: removeCategory }) => (
|
||||||
|
<>
|
||||||
|
{subFields.map(subField => (
|
||||||
|
<Fragment key={subField.key}>
|
||||||
|
<Row gutter={8}>
|
||||||
|
<Col span={8}>
|
||||||
|
<Form.Item label="主要标的类别" name={[subField.name, 'type']} rules={[
|
||||||
|
{
|
||||||
|
required: true,
|
||||||
|
validator: (_, value) => {
|
||||||
|
if (!value || !value[0] || !value[1]) {
|
||||||
|
return Promise.reject(new Error('请完整选择主要标的类别'));
|
||||||
|
}
|
||||||
|
return Promise.resolve();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]}>
|
||||||
|
<SubjectFormItem disabled={readOnly} />
|
||||||
|
</Form.Item>
|
||||||
|
</Col>
|
||||||
|
<Col span={8} style={{ paddingLeft: 2, position: 'relative' }}>
|
||||||
|
<ProFormDigit
|
||||||
|
label="主要标的类别占百分比"
|
||||||
|
name={[subField.name, 'percent']}
|
||||||
|
rules={[{ required: true, message: '请输入主要标的类别占百分比' }]}
|
||||||
|
fieldProps={{ precision: 2, style: { width: '80%' } }}
|
||||||
|
disabled={readOnly}
|
||||||
|
/>
|
||||||
|
<span style={{ position: 'absolute', right: '8%', top: 5 }}>%</span>
|
||||||
|
</Col>
|
||||||
|
<Col span={1} hidden={subFields.length === 1}>
|
||||||
|
<MinusCircleOutlined style={{ fontSize: 16, marginTop: 7, color: 'red' }} onClick={() => removeCategory(subField.name)} disabled={readOnly} />
|
||||||
|
</Col>
|
||||||
|
</Row>
|
||||||
|
</Fragment>
|
||||||
|
))}
|
||||||
|
<Row>
|
||||||
|
<Col span={16}>
|
||||||
|
<Button type="dashed" onClick={() => addCategory()} block icon={<PlusOutlined />} disabled={readOnly}>
|
||||||
|
增加主要标的类别
|
||||||
|
</Button>
|
||||||
|
</Col>
|
||||||
|
</Row>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</Form.List>
|
||||||
|
</Col>
|
||||||
|
</Row>
|
||||||
|
</Fragment>
|
||||||
|
))}
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</Form.List>
|
||||||
|
</Col>
|
||||||
|
</Row>
|
||||||
|
|
||||||
|
</Card>
|
||||||
|
<Card title="备注" className={styles.card} bordered={false}>
|
||||||
|
<Form.Item name="remark" labelCol={{ span: 0 }} wrapperCol={{ span: 24 }}>
|
||||||
|
<Input.TextArea
|
||||||
|
autoSize={{ minRows: 3 }}
|
||||||
|
placeholder="请输入备注"
|
||||||
|
onPressEnter={(e) => {
|
||||||
|
e.preventDefault();
|
||||||
|
}}
|
||||||
|
disabled={readOnly}
|
||||||
|
/>
|
||||||
|
</Form.Item>
|
||||||
|
</Card>
|
||||||
|
<Card title="附件信息" className={styles.card} bordered={false}>
|
||||||
|
<Form.Item name="attachments" labelCol={{ span: 0 }} wrapperCol={{ span: 24 }} valuePropName="fileList" getValueFromEvent={normFile}>
|
||||||
|
<ProFormUploadButton
|
||||||
|
extra="支持格式:.rar .zip .doc .docx .pdf, 单个文件不超过20M"
|
||||||
|
name="file"
|
||||||
|
title="上传文件"
|
||||||
|
accept='.rar,.zip,.doc,.docx,.pdf'
|
||||||
|
disabled={readOnly}
|
||||||
|
// action={'/api/file/upload'}
|
||||||
|
fieldProps={{
|
||||||
|
beforeUpload: (file) => {
|
||||||
|
const isRarOrZipOrDocOrDocxOrPdf = file.type === 'application/x-rar-compressed' ||
|
||||||
|
file.type === 'application/zip' ||
|
||||||
|
file.type === 'application/msword' ||
|
||||||
|
file.type === 'application/vnd.openxmlformats-officedocument.wordprocessingml.document' ||
|
||||||
|
file.type === 'application/pdf';
|
||||||
|
|
||||||
|
if (!isRarOrZipOrDocOrDocxOrPdf) {
|
||||||
|
message.error('你只能上传 rar/zip/doc/docx/pdf 文件!');
|
||||||
|
return Upload.LIST_IGNORE;
|
||||||
|
}
|
||||||
|
|
||||||
|
const isLt20M = file.size / 1024 / 1024 < 20;
|
||||||
|
if (!isLt20M) {
|
||||||
|
message.error('文件大小不能超过 20MB!');
|
||||||
|
return Upload.LIST_IGNORE;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</Form.Item>
|
||||||
|
</Card>
|
||||||
|
</PageContainer>
|
||||||
|
</ProForm>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default ProjectFileCreate;
|
104
src/pages/ProjectFiles/index.tsx
Normal file
104
src/pages/ProjectFiles/index.tsx
Normal file
@ -0,0 +1,104 @@
|
|||||||
|
import { Button } from 'antd';
|
||||||
|
import React, { useRef } from 'react';
|
||||||
|
import { history } from 'umi';
|
||||||
|
import { PageContainer } from '@ant-design/pro-layout';
|
||||||
|
import type { ProColumns, ActionType } from '@ant-design/pro-table';
|
||||||
|
import ProTable from '@ant-design/pro-table';
|
||||||
|
import { fetchProjectFileList } from './service';
|
||||||
|
import type { TableListItem, TableListPagination } from './data';
|
||||||
|
import { purchaseTypeEnum } from './dict';
|
||||||
|
|
||||||
|
const ProjectFiles: React.FC = () => {
|
||||||
|
const actionRef = useRef<ActionType>();
|
||||||
|
|
||||||
|
const columns: ProColumns<TableListItem>[] = [
|
||||||
|
{
|
||||||
|
title: '序号',
|
||||||
|
dataIndex: 'index',
|
||||||
|
renderText: (text, record, index) => index + 1,
|
||||||
|
width: 50,
|
||||||
|
hideInSearch: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '项目名称',
|
||||||
|
dataIndex: 'projectName',
|
||||||
|
valueType: 'text',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '项目编号',
|
||||||
|
dataIndex: 'ebpProjectNumber',
|
||||||
|
hideInSearch: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '创建时间',
|
||||||
|
dataIndex: 'createDate',
|
||||||
|
hideInSearch: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '采购方式',
|
||||||
|
dataIndex: 'purchaseType',
|
||||||
|
valueType: 'select',
|
||||||
|
valueEnum: purchaseTypeEnum,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '采购类型',
|
||||||
|
dataIndex: 'purchaseType',
|
||||||
|
hideInSearch: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '状态',
|
||||||
|
dataIndex: 'status',
|
||||||
|
valueType: 'select',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '版本号',
|
||||||
|
dataIndex: 'version',
|
||||||
|
hideInSearch: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '操作',
|
||||||
|
dataIndex: 'option',
|
||||||
|
valueType: 'option',
|
||||||
|
render: (_, record) => [
|
||||||
|
<a
|
||||||
|
key="config"
|
||||||
|
onClick={() => {
|
||||||
|
history.push(`/ProjectFiles/file?action=view&id=${record.id}`);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
查看
|
||||||
|
</a>,
|
||||||
|
],
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="project-file-container" style={{ backgroundColor: '#f8f8f8' }}>
|
||||||
|
<PageContainer title="我创建的项目">
|
||||||
|
<ProTable<TableListItem, TableListPagination>
|
||||||
|
actionRef={actionRef}
|
||||||
|
rowKey="id"
|
||||||
|
search={{
|
||||||
|
labelWidth: 120,
|
||||||
|
}}
|
||||||
|
toolBarRender={() => [
|
||||||
|
<Button
|
||||||
|
type="primary"
|
||||||
|
key="primary"
|
||||||
|
onClick={() => {
|
||||||
|
history.push('/ProjectFiles/file?action=create');
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
新建
|
||||||
|
</Button>,
|
||||||
|
]}
|
||||||
|
options={{ density: false, reload: false, setting: false, fullScreen: false }}
|
||||||
|
request={fetchProjectFileList}
|
||||||
|
columns={columns}
|
||||||
|
/>
|
||||||
|
</PageContainer>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default ProjectFiles;
|
36
src/pages/ProjectFiles/service.ts
Normal file
36
src/pages/ProjectFiles/service.ts
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
// @ts-ignore
|
||||||
|
/* eslint-disable */
|
||||||
|
import { request } from 'umi';
|
||||||
|
import { TableListItem } from './data';
|
||||||
|
|
||||||
|
/** 获取规则列表 GET /api/rule */
|
||||||
|
export async function fetchProjectFileList(
|
||||||
|
params: {
|
||||||
|
// query
|
||||||
|
/** 当前的页码 */
|
||||||
|
current?: number;
|
||||||
|
/** 页面的容量 */
|
||||||
|
pageSize?: number;
|
||||||
|
},
|
||||||
|
) {
|
||||||
|
console.log(params);
|
||||||
|
|
||||||
|
return request<{
|
||||||
|
data: TableListItem[];
|
||||||
|
/** 列表的内容总数 */
|
||||||
|
total?: number;
|
||||||
|
success?: boolean;
|
||||||
|
}>('/api/projectFiles', {
|
||||||
|
method: 'GET',
|
||||||
|
params: {
|
||||||
|
...params,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function submitForm(params: any) {
|
||||||
|
return request('/api/projectFile', {
|
||||||
|
method: 'POST',
|
||||||
|
data: params,
|
||||||
|
});
|
||||||
|
}
|
70
src/pages/ProjectFiles/style.less
Normal file
70
src/pages/ProjectFiles/style.less
Normal file
@ -0,0 +1,70 @@
|
|||||||
|
@import '~antd/es/style/themes/default.less';
|
||||||
|
|
||||||
|
.card {
|
||||||
|
margin-bottom: 24px;
|
||||||
|
|
||||||
|
:global {
|
||||||
|
.ant-legacy-form-item .ant-legacy-form-item-control-wrapper {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.errorIcon {
|
||||||
|
margin-right: 24px;
|
||||||
|
color: @error-color;
|
||||||
|
cursor: pointer;
|
||||||
|
|
||||||
|
span.anticon {
|
||||||
|
margin-right: 4px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.errorPopover {
|
||||||
|
:global {
|
||||||
|
.ant-popover-inner-content {
|
||||||
|
min-width: 256px;
|
||||||
|
max-height: 290px;
|
||||||
|
padding: 0;
|
||||||
|
overflow: auto;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.errorListItem {
|
||||||
|
padding: 8px 16px;
|
||||||
|
list-style: none;
|
||||||
|
border-bottom: 1px solid @border-color-split;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: all 0.3s;
|
||||||
|
&:hover {
|
||||||
|
background: @item-active-bg;
|
||||||
|
}
|
||||||
|
&:last-child {
|
||||||
|
border: 0;
|
||||||
|
}
|
||||||
|
.errorIcon {
|
||||||
|
float: left;
|
||||||
|
margin-top: 4px;
|
||||||
|
margin-right: 12px;
|
||||||
|
padding-bottom: 22px;
|
||||||
|
color: @error-color;
|
||||||
|
}
|
||||||
|
.errorField {
|
||||||
|
margin-top: 2px;
|
||||||
|
color: @text-color-secondary;
|
||||||
|
font-size: 12px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.createProjectFile {
|
||||||
|
min-height: 100vh;
|
||||||
|
.footerToolbar {
|
||||||
|
justify-content: center;
|
||||||
|
:global {
|
||||||
|
.ant-pro-footer-bar-left {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Reference in New Issue
Block a user