diff --git a/config/config.UAT.ts b/config/config.UAT.ts index a022290..6e23f3e 100644 --- a/config/config.UAT.ts +++ b/config/config.UAT.ts @@ -12,7 +12,7 @@ export default defineConfig({ //密码加密参数 REACT_APP_PASSWORD_CIPHERMODE:'1', - REACT_APP_PASSWORD_PUBLICKEY:'0428D625CEEB71CE823BD7D78DFEE7B122F2DA5C4D21E32253AD684D0FE21810394A799639C0CDFBFEB535A1DFD6A366A637E582CE0B1466A5FE7858841135DE6B', + REACT_APP_PASSWORD_PUBLICKEY:'MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCvLBkALIYR/x9Rv5TiXQGWAXTzraN/He80r9gQovSQ5oTP8qllL9+Oc1LdTijPFRsddHWg37umvFliwhmukU1NT+o2loGcKpyMHFkc/UPNjQLvd+YFR4nYhgP8l+dmRNOtQWawOt5dbksRKTghMjA+FKT2+itMsawSs1+Ic+zoIwIDAQAB', //当前环境 START_ENV:'UAT', diff --git a/config/config.dev.ts b/config/config.dev.ts index d7f0893..06c4372 100644 --- a/config/config.dev.ts +++ b/config/config.dev.ts @@ -12,7 +12,7 @@ export default defineConfig({ //密码加密参数 REACT_APP_PASSWORD_CIPHERMODE: '1', - REACT_APP_PASSWORD_PUBLICKEY: '0428D625CEEB71CE823BD7D78DFEE7B122F2DA5C4D21E32253AD684D0FE21810394A799639C0CDFBFEB535A1DFD6A366A637E582CE0B1466A5FE7858841135DE6B', + REACT_APP_PASSWORD_PUBLICKEY: 'MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCvLBkALIYR/x9Rv5TiXQGWAXTzraN/He80r9gQovSQ5oTP8qllL9+Oc1LdTijPFRsddHWg37umvFliwhmukU1NT+o2loGcKpyMHFkc/UPNjQLvd+YFR4nYhgP8l+dmRNOtQWawOt5dbksRKTghMjA+FKT2+itMsawSs1+Ic+zoIwIDAQAB', //当前环境 START_ENV: 'DEV', diff --git a/config/config.prod.ts b/config/config.prod.ts index 61ff507..5fbd4a6 100644 --- a/config/config.prod.ts +++ b/config/config.prod.ts @@ -12,7 +12,7 @@ export default defineConfig({ //密码加密参数 REACT_APP_PASSWORD_CIPHERMODE:'1', - REACT_APP_PASSWORD_PUBLICKEY:'04819CF427F9150FEEBD91E8D2346F203FC47312D212022A967D8372EA30B9581CCEEFCE2670BDDAF2E8DA1620EA73948126078ED9FF9773AA3A94EE6C80035A18', + REACT_APP_PASSWORD_PUBLICKEY:'MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCvLBkALIYR/x9Rv5TiXQGWAXTzraN/He80r9gQovSQ5oTP8qllL9+Oc1LdTijPFRsddHWg37umvFliwhmukU1NT+o2loGcKpyMHFkc/UPNjQLvd+YFR4nYhgP8l+dmRNOtQWawOt5dbksRKTghMjA+FKT2+itMsawSs1+Ic+zoIwIDAQAB', //当前环境 START_ENV:'PROD', diff --git a/config/config.sim.ts b/config/config.sim.ts index 43156ec..ebda6bf 100644 --- a/config/config.sim.ts +++ b/config/config.sim.ts @@ -12,7 +12,7 @@ export default defineConfig({ //密码加密参数 REACT_APP_PASSWORD_CIPHERMODE: '1', - REACT_APP_PASSWORD_PUBLICKEY: '0428D625CEEB71CE823BD7D78DFEE7B122F2DA5C4D21E32253AD684D0FE21810394A799639C0CDFBFEB535A1DFD6A366A637E582CE0B1466A5FE7858841135DE6B', + REACT_APP_PASSWORD_PUBLICKEY: 'MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCvLBkALIYR/x9Rv5TiXQGWAXTzraN/He80r9gQovSQ5oTP8qllL9+Oc1LdTijPFRsddHWg37umvFliwhmukU1NT+o2loGcKpyMHFkc/UPNjQLvd+YFR4nYhgP8l+dmRNOtQWawOt5dbksRKTghMjA+FKT2+itMsawSs1+Ic+zoIwIDAQAB', //当前环境 START_ENV: 'sim', diff --git a/config/config.ts b/config/config.ts index c3955ff..e708dfb 100644 --- a/config/config.ts +++ b/config/config.ts @@ -2,7 +2,10 @@ import { defineConfig } from 'umi'; import defaultSettings from './defaultSettings'; // import proxy from './proxy'; import PageRoutes from './router.config' +import theme from './theme'; + const { REACT_APP_ENV } = process.env; + export default defineConfig({ hash: true, antd: {}, @@ -25,10 +28,7 @@ export default defineConfig({ // umi routes: https://umijs.org/docs/routing routes: PageRoutes, // Theme for antd: https://ant.design/docs/react/customize-theme-cn - theme: { - // ...darkTheme, - 'primary-color': defaultSettings.primaryColor, - }, + theme, // @ts-ignore title: false, ignoreMomentLocale: true, diff --git a/config/defaultSettings.ts b/config/defaultSettings.ts index d281703..ca8c2e9 100644 --- a/config/defaultSettings.ts +++ b/config/defaultSettings.ts @@ -6,7 +6,7 @@ type DefaultSettings = ProSettings & { const proSettings: DefaultSettings = { navTheme: 'light', - primaryColor: '#b30000', + primaryColor: '#014F8F', layout: 'side', contentWidth: 'Fluid', fixedHeader: false, diff --git a/config/proxy.ts b/config/proxy.ts index 15913ac..ab6d58e 100644 --- a/config/proxy.ts +++ b/config/proxy.ts @@ -7,16 +7,16 @@ export default { // }, '/api/*': { // target: 'http://10.242.37.148:18022',//连接天宫的ng - target: 'http://localhost:3000',//连接天宫的ng + target: 'http://10.60.161.52:18030/',//连接天宫的ng changeOrigin: true, - pathRewrite: { '^': '' }, + pathRewrite: { '^/api': '' }, }, }, UAT: { - '/api/core-service-ebtp-userinfo': { + '/api/sys-manager-ebtp-project': { target: 'http://localhost:18023', changeOrigin: true, - pathRewrite: { '/api/core-service-ebtp-userinfo': '' }, + pathRewrite: { '/api/sys-manager-ebtp-project': '' }, }, '/api/biz-service-ebtp-bid': { target: 'http://localhost:18003', @@ -33,11 +33,16 @@ export default { changeOrigin: true, pathRewrite: { '/api/biz-service-ebtp-project': '' }, }, - '/api/sys-manager-ebtp-project': { - target: 'http://localhost:18030', + '/api/biz-service-ebtp-process': { + target: 'http://localhost:18011', changeOrigin: true, - pathRewrite: { '/api/sys-manager-ebtp-project': '' }, + pathRewrite: { '/api/biz-service-ebtp-process': '' }, }, + // '/api/sys-manager-ebtp-project': { + // target: 'http://localhost:18030', + // changeOrigin: true, + // pathRewrite: { '/api/sys-manager-ebtp-project': '' }, + // }, '/api/biz-service-ebtp-rsms': { target: 'http://localhost:18014', changeOrigin: true, diff --git a/config/router.config.ts b/config/router.config.ts index 2e27551..50da76f 100644 --- a/config/router.config.ts +++ b/config/router.config.ts @@ -121,6 +121,24 @@ export default [ path: '/ToAgencyAgent', component: './Agency/AgencyManager/agentIndex', }, + /** + * 项目建档 + */ + { + // 项目建档列表 + path: '/ProjectFiles', + component: './ProjectFiles', + }, + { + // 项目建档新增 + path: '/ProjectFiles/file', + component: './ProjectFiles/file', + }, + { + name: 'BidReOffer', + path: '/biddingAnnouncement/BiddingAnnoStructureForm', + component: './Bid/BiddingAnnouncement/structure/BiddingAnnoStructureForm', + }, //==============================================================引入的业务路由 ...approvalForm,//审批单 ...juryRoom,//评标室内所有路由 diff --git a/config/router_transfer.ts b/config/router_transfer.ts index 81d12df..b6adc34 100644 --- a/config/router_transfer.ts +++ b/config/router_transfer.ts @@ -1,5 +1,9 @@ const { LOGIN_PATH } = process.env; export default [ + { + path: '/internal-login', + component: './Login/internal', + }, // {//内部人员便捷登陆页 // path: '/loginFake', // component: './Login/index', diff --git a/config/theme.js b/config/theme.js new file mode 100644 index 0000000..3fdf767 --- /dev/null +++ b/config/theme.js @@ -0,0 +1,24 @@ +export default { + // 主题色 + 'primary-color': '#014F8F', + // 按钮圆角 + 'border-radius-base': '4px', + // layout background + 'layout-background': '#F5F7FA', + // 链接色 + 'link-color': '#014F8F', + // layout-header-background + 'layout-header-background': '#ffffff', + // error + 'error-color': '#D7000F', + // menu background + 'menu-list-bg': '#F2F6FC', + // menu item selected background + 'menu-item-selected-bg': '#D9ECFF', + // table header + 'table-header-bg': '#F2F6FC', + // table header text color + 'table-header-color': '#8F9298', + // table select row background + 'table-selected-row-bg': '#F2F6FC', +} \ No newline at end of file diff --git a/mock/projectFile.js b/mock/projectFile.js new file mode 100644 index 0000000..360d877 --- /dev/null +++ b/mock/projectFile.js @@ -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, + }); + }, +} \ No newline at end of file diff --git a/package.json b/package.json index d694096..f3905d0 100644 --- a/package.json +++ b/package.json @@ -75,6 +75,7 @@ "dva": "2.4.1", "echarts": "^5.2.2", "echarts-for-react": "^3.0.2", + "jsencrypt": "^3.3.2", "lodash": "4.17.21", "moment": "^2.29.4", "omit.js": "2.0.2", diff --git a/src/assets/home.svg b/src/assets/home.svg new file mode 100644 index 0000000..8405c6c --- /dev/null +++ b/src/assets/home.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/shutdown.svg b/src/assets/shutdown.svg new file mode 100644 index 0000000..917c660 --- /dev/null +++ b/src/assets/shutdown.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/assets/user.svg b/src/assets/user.svg new file mode 100644 index 0000000..44c668d --- /dev/null +++ b/src/assets/user.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/baseStyle.less b/src/baseStyle.less index 3165e50..2dec159 100644 --- a/src/baseStyle.less +++ b/src/baseStyle.less @@ -3,12 +3,17 @@ top: 0; left: 0; .ant-pro-global-header{ - background-color: #b30000; + background-color: @layout-header-background; height: 56px; - color: #fff; + color: @primary-color; } } +// 通用页面布局样式 +.layout-container { + background-color: @layout-background; + min-height: 100vh; +} .ant-pro-sider-logo { background-color: #b30000; @@ -160,11 +165,11 @@ } -.ant-pro-table{ - .ant-card-body{ - padding: 0px; - } -} +// .ant-pro-table{ +// .ant-card-body{ +// padding: 0px; +// } +// } .ant-pro-table-search { padding: 10px 24px 6px 24px; @@ -308,3 +313,23 @@ input::-webkit-outer-spin-button, padding: 6px 24px; border-bottom: 1px solid #ddd; } + +#root { + .ant-table-tbody > tr.ant-table-row:hover > td, + .ant-table-tbody > tr > td.ant-table-cell-row-hover { + background-color: @table-selected-row-bg; + } +} + +// 控制左侧菜单背景色 +.ant-menu.ant-menu-sub.ant-menu-inline { + background-color: @menu-list-bg; + + .ant-menu-item-selected { + background-color: @menu-item-selected-bg; + } + .ant-menu-item-selected a { + color: @primary-color; + font-weight: bold; + } +} diff --git a/src/components/CaptchaInput/index.less b/src/components/CaptchaInput/index.less new file mode 100644 index 0000000..fa599b1 --- /dev/null +++ b/src/components/CaptchaInput/index.less @@ -0,0 +1,48 @@ +.captchaContainer { + width: 100%; + + :global { + .ant-input-affix-wrapper { + padding-right: 4px; + } + } + + .captchaWrapper { + display: flex; + justify-content: end; + align-items: center; + gap: 8px; + height: 40px; + width: 120px; + } + + .captchaImage { + height: 40px; + width: 100%; + cursor: pointer; + border-radius: 4px; + border: 1px solid #d9d9d9; + object-fit: contain; + background: #fff; + + &:hover { + border-color: #40a9ff; + box-shadow: 0 0 0 2px rgba(24, 144, 255, 0.2); + } + } + + .refreshIcon { + font-size: 16px; + color: #1890ff; + cursor: pointer; + transition: all 0.3s; + padding: 4px; + + &:hover { + color: #40a9ff; + transform: rotate(180deg); + background: rgba(24, 144, 255, 0.1); + border-radius: 4px; + } + } +} \ No newline at end of file diff --git a/src/components/CaptchaInput/index.tsx b/src/components/CaptchaInput/index.tsx new file mode 100644 index 0000000..02752ca --- /dev/null +++ b/src/components/CaptchaInput/index.tsx @@ -0,0 +1,72 @@ +import React, { useState, useEffect, forwardRef, useImperativeHandle } from 'react'; +import { Input, Spin } from 'antd'; +import { SafetyOutlined } from '@ant-design/icons'; +import styles from './index.less'; +import { getCaptcha } from '@/services/login'; + +interface CaptchaInputProps { + value?: { + captcha: string; + captchaToken: string; + }; + onChange?: (value: { captcha: string; captchaToken: string }) => void; + placeholder?: string; +} + +export interface CaptchaInputRef { + refresh: () => void; +} + +const CaptchaInput = forwardRef((props: CaptchaInputProps, ref) => { + const { value, onChange, placeholder = '请输入验证码' } = props; + const [imgUrl, setImgUrl] = useState(''); + const [captchaToken, setCaptchaToken] = useState(''); + const [loading, setLoading] = useState(false); + + // 获取验证码 + const fetchCaptcha = async () => { + setLoading(true); + try { + const res = await getCaptcha(); + if (res?.success) { + setImgUrl('data:image/png;base64,' + res.data.base64Image); + setCaptchaToken(res.data.code); + } + } catch (error) { + console.error('获取验证码失败:', error); + } finally { + setLoading(false); + } + }; + + // 组件挂载时获取验证码 + useEffect(() => { + fetchCaptcha(); + }, []); + + useImperativeHandle(ref, () => ({ + refresh: fetchCaptcha, + })); + + return ( +
+ onChange?.({ captcha: e.target.value, captchaToken: captchaToken })} + placeholder={placeholder} + maxLength={4} + prefix={} + suffix={ +
+ {loading ? : 验证码} +
+ } + /> +
+ ); +}); + +CaptchaInput.displayName = 'CaptchaInput'; + +export default CaptchaInput; \ No newline at end of file diff --git a/src/components/CitySelect.tsx b/src/components/CitySelect.tsx new file mode 100644 index 0000000..d661d8c --- /dev/null +++ b/src/components/CitySelect.tsx @@ -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 = ({ + value = {}, + onChange, + form, + placeholder = { + province: '请选择省份', + city: '请选择城市', + district: '请选择区域', + }, + disabled = false, + size = 'middle', +}) => { + const [cities, setCities] = useState([]); + const [districts, setDistricts] = useState([]); + const [selectedProvince, setSelectedProvince] = useState(value.province); + const [selectedCity, setSelectedCity] = useState(value.city); + const [selectedDistrict, setSelectedDistrict] = useState(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 ( + + + + + + + + + + + + ); +}; + +export default CitySelect; diff --git a/src/components/GlobalHeader/RightContent.tsx b/src/components/GlobalHeader/RightContent.tsx index ad7dc52..aad3ee8 100644 --- a/src/components/GlobalHeader/RightContent.tsx +++ b/src/components/GlobalHeader/RightContent.tsx @@ -9,27 +9,32 @@ import './index.less'; import { getMenu, getLogout } from './services' import { getSessionUserData } from "@/utils/session"; import { getToSecondUrl } from '@/pages/LoadingPage/service'; +import userIcon from '@/assets/user.svg'; +import homeIcon from '@/assets/home.svg'; +import shutdownIcon from '@/assets/shutdown.svg'; +import { logout } from './services'; +import cookie from 'react-cookies'; const GlobalHeaderRight: React.FC<{}> = (props) => { // let className = styles.right; - let data = getSessionUserData(); + const data = getSessionUserData(); const [dataMenu, setDataMenu] = React.useState([]); const urlRef = useRef(null); const handelRole = (item: any) => { sessionStorage.setItem('roleData', JSON.stringify(item)); sessionStorage.setItem('roleAuthority', JSON.stringify([item.roleCode])); - let params = { + const params = { roleIdList: [item.roleId] } history.push('/Dashboard') window.location.reload() - getMenu(params).then(res => { - if (res?.code == 1) { - setDataMenu(res?.data) - } else { - message.error("数据错误请联系管理员") - } - }) + // getMenu(params).then(res => { + // if (res?.code == 1) { + // setDataMenu(res?.data) + // } else { + // message.error("数据错误请联系管理员") + // } + // }) } //角色退出登录 const toLogout = () => { @@ -40,15 +45,21 @@ const GlobalHeaderRight: React.FC<{}> = (props) => { title: '请确认是否退出?', content: false, onOk() { - getLogout().then((res) => { + logout().then((res) => { if (res?.success) { - if (data?.userType == "0") {//联通智慧门户 - window.close(); - } else if (data?.userType == "1") {//合作方 - window.close(); - } else if (data?.userType == "2") {//专家 - window.location.href = "/userformal/login" - } + // if (data?.userType == "0") {//联通智慧门户 + // window.close(); + // } else if (data?.userType == "1") {//合作方 + // window.close(); + // } else if (data?.userType == "2") {//专家 + // window.location.href = "/userformal/login" + // } + message.success('退出登录成功'); + sessionStorage.clear(); + cookie.remove('mall3_token'); + setTimeout(() => { + history.push('/internal-login'); + }, 1000); } }) }, @@ -62,17 +73,17 @@ const GlobalHeaderRight: React.FC<{}> = (props) => { const droMenu = ( {data?.authorityList != undefined ? - data?.authorityList?.map((item: any, index: any) => ( - + data?.authorityList?.map((item: any) => ( + handelRole(item)}>{item.roleName} )) : null} - <> + {/* <> toLogout()}>退出登录 - + */} ); @@ -89,21 +100,35 @@ const GlobalHeaderRight: React.FC<{}> = (props) => { return (
- 中国联通智慧供应链平台 | 招标采购中心 +
+
+ +
+
+ 采购平台供应商统一管理系统 + CHINA COSCO SHIPPING CORPORATION LIMITED +
+
diff --git a/src/components/GlobalHeader/index.less b/src/components/GlobalHeader/index.less index 72eeb15..f3dfeec 100644 --- a/src/components/GlobalHeader/index.less +++ b/src/components/GlobalHeader/index.less @@ -25,8 +25,8 @@ float: left; line-height: 56px; font-size: 14px; - color: #fff; - padding: 0 14px; + color: inherit; + padding: 0 12px; list-style: none; span { @@ -35,7 +35,7 @@ } a { - color: #fff; + color: #131414 !important; } } } diff --git a/src/components/GlobalHeader/services.ts b/src/components/GlobalHeader/services.ts index 171b0b3..a6ba67f 100644 --- a/src/components/GlobalHeader/services.ts +++ b/src/components/GlobalHeader/services.ts @@ -9,7 +9,7 @@ export async function getMenu(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', headers: { 'Authorization': params }, data: params, @@ -17,8 +17,14 @@ export async function fgetUserMsg(params: any) { } export async function getLogout() { // 退出登录,注销 - return request('/api/core-service-ebtp-userinfo/v1/userinfo/logout', { + return request('/api/sys-manager-ebtp-project/v1/userinfo/logout', { method: 'get', // params }); } + +export async function logout() { + return request('/api/v1/login/logout', { + method: 'post', + }); +} \ No newline at end of file diff --git a/src/global.less b/src/global.less index c85eb4b..af3d51a 100644 --- a/src/global.less +++ b/src/global.less @@ -51,7 +51,7 @@ ol { } // 兼容IE11 -@media screen and(-ms-high-contrast: active), (-ms-high-contrast: none) { +@media screen and (-ms-high-contrast: active), (-ms-high-contrast: none) { body .ant-design-pro > .ant-layout { min-height: 100vh; } diff --git a/src/pages/Bid/BiddingAnnouncement/components/BiddingAnnouncementList.tsx b/src/pages/Bid/BiddingAnnouncement/components/BiddingAnnouncementList.tsx index 6b4f94b..5051e20 100644 --- a/src/pages/Bid/BiddingAnnouncement/components/BiddingAnnouncementList.tsx +++ b/src/pages/Bid/BiddingAnnouncement/components/BiddingAnnouncementList.tsx @@ -35,6 +35,8 @@ interface DataItem { } + + const BiddingAnnouncementList: React.FC<{}> = (props) => { const [Refresh, setRefresh] = useState(Math.random); @@ -62,12 +64,18 @@ const BiddingAnnouncementList: React.FC<{}> = (props) => { const [approvalViewVisible, setApprovalViewVisible] = useState(false);//查看审批流程弹窗 const [approvalViewUrl, setApprovalViewUrl] = useState("");//查看审批流程参数-url + const [annoPreviewVisible, setAnnoPreviewVisible] = useState(false);//公告结构化预览 + const [annoContent, setAnnoContent] = useState("");//公告正文 + const modalHeight = window.innerHeight * 96 / 100; + + let name1 = "中标"; let name2 = "评标"; let name3 = "开标"; let name4 = "标段"; let proName = ""; let proDict = getProMethod(); + // const proOpenTenderForm = getProOpenTenderForm(); let defId = getDefId(); if (proDict == "procurement_mode_1" || proDict == "procurement_mode_2") { proName = "招标"; @@ -177,7 +185,7 @@ const BiddingAnnouncementList: React.FC<{}> = (props) => { ); let btn3 = ( <> - + + + + + ); + let updatebtn = ( + <> + + + ); + let viewbtn = ( + <> + + + ); + let delbtn = ( + <> + + + ); + let releasebtn = ( + <> + + + ); + let approvalbtn = ( + <> + + + ); + let approvalviewbtn = ( + <> + + + ); + let changebtn = ( + <> + + + ); + let releaseOfflinebtn = ( + <> + + + ); + let hasCtpspbtn = ( + <> + + + ); /*if(true){ return btn3; - }else*/ - if (record.status == 1) { - return btn1; - } else if (record.status == 2) { - return btn2; - } else if (record.status == 3) { - return btn3; - } else if (record.status == 4) { - return btn4; - } else if (record.status == 5) { - return btn5; - } else if (record.status == 6) { - return btn6; - } else if (record.status == 9) { - return btn2; + }else*///&& proOpenTenderForm === "open_tender_form_1" + if (proDict == "procurement_mode_1" && record?.structuralFileId) { + if (record.status == 1) { + return [updatebtn, jghbtn, approvalbtn, hasCtpspbtn, delbtn]; + } else if (record.status == 2) { + return [viewbtn, jghbtn, approvalviewbtn, hasCtpspbtn]; + } else if (record.status == 3) { + return [releasebtn, viewbtn, jghbtn, approvalviewbtn, hasCtpspbtn]; + } else if (record.status == 4) { + return [updatebtn, jghbtn, approvalbtn, approvalviewbtn, hasCtpspbtn, delbtn]; + } else if (record.status == 5) { + return [viewbtn, jghbtn, approvalviewbtn, hasCtpspbtn, changebtn]; + } else if (record.status == 6) { + return [releasebtn, releaseOfflinebtn, viewbtn, jghbtn, approvalviewbtn, hasCtpspbtn]; + } else if (record.status == 9) { + return [viewbtn, jghbtn, approvalviewbtn, hasCtpspbtn]; + } else { + return (<>) + } } else { - return (<>) + if (record.status == 1) { + return btn1; + } else if (record.status == 2) { + return btn2; + } else if (record.status == 3) { + return btn3; + } else if (record.status == 4) { + return btn4; + } else if (record.status == 5) { + return btn5; + } else if (record.status == 6) { + return btn6; + } else if (record.status == 9) { + return btn2; + } else { + return (<>) + } } }, @@ -297,7 +376,7 @@ const BiddingAnnouncementList: React.FC<{}> = (props) => { ); let btn3 = ( <> - + + , - , + // , + , + , , @@ -671,6 +804,22 @@ const BiddingAnnouncementList: React.FC<{}> = (props) => { /> ) : null} + {annoPreviewVisible ? ( + { setAnnoPreviewVisible(false); setAnnoContent("") }}>关闭 ]} + > +
+
+
+
+ ) : null} {updateVisible ? ( setRefresh(Math.random() + 1)} titleName={updateChange} pkId={pkId} type={type} tpId={tpId} @@ -696,7 +845,7 @@ const BiddingAnnouncementList: React.FC<{}> = (props) => { } }} data={approvalData} annoId={recordData?.id} /> ) : null} - { setApprovalViewVisible(false) }} url={approvalViewUrl} /> + { setApprovalViewVisible(false); setRecordData({}) }} record={recordData} />
) diff --git a/src/pages/Bid/BiddingAnnouncement/service.ts b/src/pages/Bid/BiddingAnnouncement/service.ts index ba3929d..e7515a4 100644 --- a/src/pages/Bid/BiddingAnnouncement/service.ts +++ b/src/pages/Bid/BiddingAnnouncement/service.ts @@ -240,4 +240,41 @@ export async function saveAnno(params?: any) { ...params }, }) +} + + +/** + * 公告结构化-根据项目id获取结构化信息 + * @param projectId + * @returns + */ +export async function getAnnoStructuralValue(params?: any) { + return request('/api/biz-service-ebtp-bid/v1/bizannostructural/getStructuralValue/', { + method: "post", + data: { + ...params + }, + }) +} + +/** + * 公告结构化-保存数据 + * @param data + * @returns + */ +export async function saveAnnoStructuralFormData(data: any) { + return request('/api/biz-service-ebtp-bid/v1/bizannostructural/saveStructural', { + method: 'POST', + data: { ...data }, + }) +} + +/*招标文件-招标文件维护增加相关招标公告预览及备注信息*/ +export async function findAnnoByBsIds(params: any) { + return request('/api/biz-service-ebtp-bid/v1/bizannostructural/selectListByBsIds', { + method: "post", + data: [ + ...params + ] + }) } \ No newline at end of file diff --git a/src/pages/Bid/ReviewConfig/Config/components/detailedMain.tsx b/src/pages/Bid/ReviewConfig/Config/components/detailedMain.tsx index 26333ab..a226e63 100644 --- a/src/pages/Bid/ReviewConfig/Config/components/detailedMain.tsx +++ b/src/pages/Bid/ReviewConfig/Config/components/detailedMain.tsx @@ -397,7 +397,44 @@ const DetailedMain: React.FC = (props) => { } }} value={parseInt(record.effectiveType)}> 全部报价 - 去掉最高和最低各一家后的报价 + +
+
去掉最高: + {returnInput(record, 'removeMaxNumber', 1, parseInt(record.effectiveType), record.removeMaxNumber, 2)} 家报价 +
+
+
+
+
+
去掉最低: + {returnInput(record, 'removeMinNumber', 1, parseInt(record.effectiveType), record.removeMinNumber, 2)} 家报价 +
+
+
+
+ +
+
高于拦标价的报价: + {returnInput(record, 'includeLowPrice', 1, parseInt(record.effectiveType), record.includeLowPrice, 6)} 元{/*TODO zyx-获取币种*/} +
+
+ { + const value = event.target.value; + if (value != undefined) { + record.includeLowStatus = value; + reTable(); + } + }} + > + 含税 + +
+
+
+ {/* 去掉最高和最低各一家后的报价 去掉最两家高和最低各一家后的报价 去掉最低一家后的报价 去掉最高一家后的报价 @@ -444,7 +481,7 @@ const DetailedMain: React.FC = (props) => {
- + */}
{showNameT.pb}专家手动勾选 @@ -675,7 +712,7 @@ const DetailedMain: React.FC = (props) => { initialValue="请选择" rules={[{ required: true, message: '请选择类别' }]} > - @@ -687,7 +724,7 @@ const DetailedMain: React.FC = (props) => { {({ getFieldValue }) => { const selectedType = getFieldValue('type'); return ( - selectedType !== '报价' && ( + selectedType !== '报价' && selectedType!='请选择' && ( = (props) => { useEffect(() => { if (show !== '1') { queryDetData(); - detailTem().then(res => { - res?.code == 200 ? oidSet(res.data.documentCenterId) : null; - }); + //TODO zyx 假数据-start + oidSet("1673219453012992001"); + // detailTem().then(res => { + // res?.code == 200 ? oidSet(res.data.documentCenterId) : null; + // }); + //TODO zyx 假数据-end } }, [show]); @@ -1256,6 +1296,8 @@ const DetailedMain: React.FC = (props) => { includeHighPrice: '', includeLowStatus: 1,//最低价 含税不含税 includeLowPrice: '', + removeMaxNumber: '', + removeMinNumber: '', }] } let data1T: any = [...data1] @@ -1416,6 +1458,8 @@ const DetailedMain: React.FC = (props) => { case 'priceMultiplierSecond': now.priceMultiplierSecond = value; break; case 'lowScore': now.lowScore = value; break; case 'lowScoreSecond': now.lowScoreSecond = value; break; + case 'removeMaxNumber': now.removeMaxNumber = value; break; + case 'removeMinNumber': now.removeMinNumber = value; break; default: break; } setData4(data); diff --git a/src/pages/Bid/ReviewConfig/Config/components/fileTree.tsx b/src/pages/Bid/ReviewConfig/Config/components/fileTree.tsx index c2190a5..7c6f6ce 100644 --- a/src/pages/Bid/ReviewConfig/Config/components/fileTree.tsx +++ b/src/pages/Bid/ReviewConfig/Config/components/fileTree.tsx @@ -240,37 +240,39 @@ export const getTreeDataOut = async (assessRoomId: any, turnSort: any) => { let data: any = []; let rootT: any = []; let childT: any = []; - await getTree({ assessRoomId: assessRoomId, turnSort: turnSort }).then((res) => { - if (res.data != undefined) { - res.data.map((item: any, index: any) => { - const title1 = item.name; - const key1 = item.id; - let children1: any = []; - if (item.children != undefined) { - const children = item.children.map((item: any, index: any) => { - const title2 = item.name; - const key2 = item.id; - let children2: any = []; - if (item.children != undefined) { - const children = item.children.map((item: any, index: any) => { - const title3 = item.name; - const key3 = item.id; - childT.push(key3) - return { title: title3, key: key3, pId: key2 } - }); - children2 = children; - } - childT.push(key2) - return { title: title2, key: key2, children: children2, pId: key1 } - }); - children1 = children; - } - const first = { title: title1, key: key1, children: children1, pId: null } - rootT.push(key1) - data.push(first); - }); - } - }) + //TODO zyx假数据-start + // await getTree({ assessRoomId: assessRoomId, turnSort: turnSort }).then((res) => { + // if (res.data != undefined) { + // res.data.map((item: any, index: any) => { + // const title1 = item.name; + // const key1 = item.id; + // let children1: any = []; + // if (item.children != undefined) { + // const children = item.children.map((item: any, index: any) => { + // const title2 = item.name; + // const key2 = item.id; + // let children2: any = []; + // if (item.children != undefined) { + // const children = item.children.map((item: any, index: any) => { + // const title3 = item.name; + // const key3 = item.id; + // childT.push(key3) + // return { title: title3, key: key3, pId: key2 } + // }); + // children2 = children; + // } + // childT.push(key2) + // return { title: title2, key: key2, children: children2, pId: key1 } + // }); + // children1 = children; + // } + // const first = { title: title1, key: key1, children: children1, pId: null } + // rootT.push(key1) + // data.push(first); + // }); + // } + // }) + //TODO zyx假数据-end return { roots: rootT, childs: childT, treeData: data } } export default FileTree; diff --git a/src/pages/Bid/ReviewConfig/Config/components/firstMain.tsx b/src/pages/Bid/ReviewConfig/Config/components/firstMain.tsx index 985d138..95e50ef 100644 --- a/src/pages/Bid/ReviewConfig/Config/components/firstMain.tsx +++ b/src/pages/Bid/ReviewConfig/Config/components/firstMain.tsx @@ -246,7 +246,7 @@ const FirstMain: React.FC = (props) => { onClick={showConfirm} > 继承上轮 - + } = () => { roomType = getURLInformation("roomType"); } //查评分办法和采购方式 - getBizInfo(bizId).then((res) => { - let methodT = false; - if (res.code == 200) { - const data = res.data; - if (roomType == '2') { - data.evalMethodDict === 'eval_method_2' ? methodT = true : null;//评分办法 1最低价 2综合评估 - } - data.ptcpMode === 'ptcp_mode_2' ? methodT = true : null;//采购方式 1合格制 2有限数量制 - } - methodSet(methodT); - }); + //TODO zyx假数据 start + // getBizInfo(bizId).then((res) => { + let methodT = false; + // if (res.code == 200) { + // const data = res.data; + const data = { + "createBy": "ex-wanghy623", "createDate": "2025-05-09 08:58:57", + "updateBy": "ex-wanghy623", "updateDate": null, "tenantId": "ebtp_mall", "tenantName": "ebtp_mall", "deleteFlag": "normal", + "lastUpdateTime": "2024-10-28 15:56:21", "id": "1920644626720436224", "projectId": "1920644626460389376", + "bidSectBizNum": "ZX36102410002138", "bidSectCode": 1, "bidSectName": "测试001", "bidSectContent": null, "bidSectTypeDict": null, + "bidSectContractPrice": 1000.00000, "contractPriceCurrencyDict": null, "selectionMethod": null, "reviewRules": null, + "priceUnitDict": null, "bidderQual": null, "quotationMethodDict": "quotation_method_2", "tenderOpeningTime": null, + "ptcpMode": null, "ptcpCount": null, "businessModule": 8, "status": 2, "attDatasetId": null, "checkMethodDict": null, + "chooseProcess": "choose_process_4", "reviewReportId": null, "sectionNumber": null, "evalMethodDict": "eval_method_2", + "procurementTypeDict": null, "miitSectionId": null, "monetaryUnit": null, "remarks": null, "parentSectionId": "1916736930678689792", + "projectPlanId": "1850785019690168356", "subprojectId": "1850783951401635842", "isAnnouncement": null, "priceCeiling": 800.00000, + "priceCeilingExplain": null, "isStructureQuote": "1", "structureQuoteModel": "003", "structureQuoteLimitType": "002", + "bidNumberIsDefinite": "1", "projBidRatioInfoVoList": [], "bidDefiniteNumber": "2", "structureNote": null, + "basePriceUnitLimit": null, "materialList": [], "purcModel": null, "preBidAnalysisFlag": 0, "quotePreResultFlag": "0" + }; + //TODO zyx假数据 end + if (roomType == '2') { + data.evalMethodDict === 'eval_method_2' ? methodT = true : null;//评分办法 1最低价 2综合评估 + } + data.ptcpMode === 'ptcp_mode_2' ? methodT = true : null;//采购方式 1合格制 2有限数量制 + // } + methodSet(methodT); + // }); + //TODO zyx假数据 end }, []) return ( diff --git a/src/pages/Bid/ReviewConfig/List/index.tsx b/src/pages/Bid/ReviewConfig/List/index.tsx index 3ff5b06..5483b5b 100644 --- a/src/pages/Bid/ReviewConfig/List/index.tsx +++ b/src/pages/Bid/ReviewConfig/List/index.tsx @@ -194,7 +194,7 @@ const List: React.FC<{}> = () => { //查询已有配置的标段 const getHaveConfigSections = async (evalMethod: any) => { let secs: any[] = [] - await usePage({ projectId: getProId(), roomType: roomType, evalMethod: evalMethod }).then((res) => { + await usePage({ projectId: proId, roomType: roomType, evalMethod: evalMethod }).then((res) => { if (res.code == 200) { const data = res.data; secs = [...data]; @@ -220,7 +220,7 @@ const List: React.FC<{}> = () => { // tableAlertRender={false} params={pageData} request={(params) => - getPage({ ...params, projectId: getProId(), roomType: roomType }).then((res) => { + getPage({ ...params, projectId: proId, roomType: roomType }).then((res) => { const result = { data: res.data.records, total: res.data.total, diff --git a/src/pages/Contract/ContractList.tsx b/src/pages/Contract/ContractList.tsx new file mode 100644 index 0000000..f086af3 --- /dev/null +++ b/src/pages/Contract/ContractList.tsx @@ -0,0 +1,359 @@ +import React, {useState, useRef, useCallback} from 'react'; +import { history } from 'umi'; +import {Input, Button, Modal, Form, Select, message, DatePicker} from 'antd'; +import { SearchOutlined , UndoOutlined} from '@ant-design/icons'; +import ProTable, {ActionType, ProColumns} from '@ant-design/pro-table'; +import {saveDateTimeFormatter} from "@/utils/DateUtils"; +import { fetchContracts, deleteContract,updateContract, getContract } from "@/pages/Contract/ContractService"; +import moment from "moment"; +const { Option } = Select; + +interface Contract { + contractType: string; + contractCode: string; + contractName: string; + supplierName: string; + contractAmount: number; + supplierCreditCode: string; + status: string; + createTime: string; +} + +const contractTypeList = [ + { + label: '我已执行合同,录入阳光购系统', + value: '0' + }, + { + label: '我要与供应商进行在线合同磋商', + value: '1' + } +]; + + +const ContractList: React.FC = () => { + const [form] = Form.useForm(); + const [isModalVisible, setIsModalVisible] = useState(false); + const [searchParams, setSearchParams] = useState({ + contractCode: '', + contractName: '', + supplierName: '', + projectName: '', + biddingName: '', + createDate: undefined + }); + const actionRef = useRef(); + const handleEdit = useCallback((selectedRecord: any, opt: string) => { + getContract(selectedRecord.id).then((res) => { + history.push({ + pathname: '/stepOne', + state: { + contractInfo: res.data, + opt: opt + } + }); + }); + },[]); + + // 选择合同类型 + const handleChoose = () => { + // 重置表单到初始值 + form.resetFields(); + setIsModalVisible(true); + }; + + const createContract = async () => { + // 验证表单并获取值 + const values = await form.validateFields(); + //路由跳转 + history.push({ + pathname: '/stepOne', + state: { + contractType: values.contractType + } + }); + // 关闭模态框 + setIsModalVisible(false); + }; + + // 处理搜索 + const handleSearch = useCallback(() => { + if (searchParams.createDate) { + searchParams.createDate = saveDateTimeFormatter(moment(searchParams.createDate)) + setSearchParams({ + ...searchParams + }) + } + actionRef.current?.reload() + }, []); + + const goPerformance = useCallback((selectedRecord: any) => { + getContract(selectedRecord.id).then((res) => { + if(res?.code === 200){ + history.push({ + pathname: '/performance', + state: { + contractInfo: res.data, + opt: 'edit' + } + }); + } + }); + },[]) + + const handleUpdate = useCallback((optRecord: any) => { + updateContract(optRecord).then((r: any) => { + if (r?.code == 200) { + message.success('操作成功'); + } else { + message.error('操作失败'); + } + }).finally(() => actionRef.current?.reload()); + },[]) + + const handleOperation = useCallback((optRecord: any, status: string) => { + optRecord.status = status; + const operation = status == '2' ? '中止' : status == '3' ? '终止' : ''; + Modal.confirm({ + title: '确认' + operation + '该合同?', + onOk: async () => { + await handleUpdate(optRecord); + } + }) + },[]) + + const handleDelete = useCallback((id: string) => { + Modal.confirm({ + title: '确认删除该合同?', + onOk: async () => { + await deleteContract(id).then((r: any) => { + if (r?.code == 200) { + message.success('删除成功'); + } else { + message.error('删除失败'); + } + }) + .finally(() => actionRef.current?.reload()); + }, + }); + },[]) + + // 处理重置 + const handleReset = () => { + setSearchParams({ + contractCode: '', + contractName: '', + supplierName: '', + projectName: '', + biddingName: '', + createDate: undefined + }); + actionRef.current?.reload(); + }; + + // 定义ProTable的列配置 + const columns: ProColumns[] = [ + { + title: '合同/协议编号', + dataIndex: 'contractCode', + key: 'contractCode', + width: 150, + }, + { + title: '合同/协议名称', + dataIndex: 'contractName', + key: 'contractName', + width: 200, + }, + { + title: '供应商名称', + dataIndex: 'supplierName', + key: 'supplierName', + width: 180, + }, + { + title: '合同金额', + dataIndex: 'amount', + key: 'amount', + width: 120, + render: (text: any) => `${text}`, + }, + { + title: '创建时间', + dataIndex: 'createDate', + key: 'createDate', + width: 160, + }, + { + title: '状态', + dataIndex: 'status', + key: 'status', + width: 120, + render: (_: any, rec: Contract) => { + const status = rec.status;//合同状态 + if (status === '0') { + return (<>合同草稿已创建) + }else if (status === '1') { + return (<>合同执行中) + }else if (status === '2') { + return (<>合同中止) + }else if (status === '3') { + return (<>合同终止) + }else if (status === '4') { + return (<>合同结束) + } else { + return (<>) + } + } + }, + { + title: '操作', + key: 'operation', + width: 250, + valueType: 'option', + render: (text: any, optRecord: any) => ( + <> + {optRecord.status === '0' && ()} + {(optRecord.status === '0'|| optRecord.status === '2' )&& ()} + {optRecord.status === '1' && ()} + {optRecord.status === '1' && ()} + {optRecord.status === '1' && ()} + {()} + + ) + }, + ]; + + // 定义ProTable的请求函数 + const request = async (params: any, sorter: any) => { + try { + // 构建请求参数,合并搜索参数 + const requestParams = { + ...params, + ...searchParams, + basePageRequest: { + pageNo: params.current || 1, + pageSize: params.pageSize || 10 + }, + }; + const result = await fetchContracts(requestParams); + // 转换数据格式以适应ProTable的要求 + return { + data: result.data.records, + total: result.data.total, + success: true, + }; + } catch (error) { + message.error('获取合同列表失败'); + return { + data: [], + total: 0, + success: false, + }; + } + }; + + return ( +
+ setIsModalVisible(false)} + onOk={createContract} + okText="确定" + > +
+ + + +
+
+ + {/* 标题部分 */} +
+ 合同管理 +
+ + {/* 第一行搜索框 */} +
+ setSearchParams({...searchParams, contractCode: e.target.value})} + style={{ width: 200, marginRight: 16 }} + /> + setSearchParams({...searchParams, contractName: e.target.value})} + style={{ width: 200, marginRight: 16 }} + /> + setSearchParams({...searchParams, supplierName: e.target.value})} + style={{ width: 200, marginRight: 16 }} + /> +
+ + +
+
+ + {/* 第二行搜索框 */} +
+ setSearchParams({...searchParams, projectName: e.target.value})} + style={{ width: 200, marginRight: 16 }} + /> + setSearchParams({...searchParams, biddingName: e.target.value})} + style={{ width: 200, marginRight: 16 }} + /> + { + // @ts-ignore + setSearchParams({...searchParams, createDate: date})} + } style={{ width: 200, marginRight: 16 }}/> + +
+ + {/* 使用ProTable替代Table */} + + actionRef={actionRef}//action触发后更新表格 + columns={columns} + request={request} + rowKey="contractCode" + pagination={{ + pageSize: 10, + showSizeChanger: true, + pageSizeOptions: ['10', '20', '50', '100'], + }} + // 移除操作按钮 + options={false} + search={false} + /> +
+ ); +}; + +export default ContractList; diff --git a/src/pages/Contract/ContractService.ts b/src/pages/Contract/ContractService.ts new file mode 100644 index 0000000..aa34f47 --- /dev/null +++ b/src/pages/Contract/ContractService.ts @@ -0,0 +1,146 @@ +import request from "@/utils/request"; + +// 字典项类型定义 +export interface CoscoContract { + // 基本信息 + id: string; + contractType: number; + projectName: string; + biddingName: string; + biddingCode: string; + + // 采购单位信息 + purchaserName: string; + purchaserCode: string; + industryCode: string; + + // 供应商信息 + supplierId: string; + supplierName: string; + supplierCode: string; + signerName: string; + signerContact: string; + + // 合同基本信息 + frameworkAgreementId: string; + contractCode: string; + contractName: string; + categoryCode: string; + signedDatetime: string; // 日期时间通常在前端使用字符串表示 + valuationType: number; + amount: number; // BigDecimal 转为 number + payType: number; + startDatetime: string; + endDatetime: string; + + // 履约信息 + performancePlace: string; + guaranteeType: number; + note: string; + contactAccessory: string; +} + +// 获取字典列表 +// export const fetchDictionaries = async (): Promise => { +// const response = await axios.get('/api/sys-manager-ebtp-project/v1/dictProject/getDictList?parentCode=procurement_type&toParentCode=entrust'); +// return response.data.data; +// }; + +const prefix = '/api/biz-service-ebtp-project/'; + +export async function fetchContracts(params: any) { + return request(prefix + 'v1/contract/getPage', { + data: params, + method: 'POST' + }); +} + +export async function updateContract(params: any) { + return request(prefix + 'v1/contract/update', { + data: params, + method: 'POST' + }); +} + +export async function createContract(params: any) { + return request(prefix + 'v1/contract', { + data: params, + method: 'POST' + }); +} + +export async function getContract(param: any) { + return request(prefix + 'v1/contract/' + param, { + method: 'get' + }); +} + +export async function listNegotiate(params: any) { + return request(prefix + 'v1/negotiate/list', { + data: params, + method: 'POST' + }); +} + +export async function deleteContract(param: any) { + return request(prefix + 'v1/contract/delete/' + param, { + method: 'get', + }); +} + +export async function createPerformance(params: any) { + return request(prefix + 'v1/performance', { + data: params, + method: 'POST' + }); +} + +export async function updatePerformance(params: any) { + return request(prefix + 'v1/performance/update', { + data: params, + method: 'POST' + }); +} + +export async function createAmountItem(params: any) { + return request(prefix + 'v1/amount/item', { + data: params, + method: 'POST' + }); +} + +export async function updateAmountItem(params: any) { + return request(prefix + 'v1/amount/item/update', { + data: params, + method: 'POST' + }); +} + + +export async function createNegotiate(params: any) { + return request(prefix + 'v1/negotiate', { + data: params, + method: 'POST' + }); +} + +export async function updateNegotiate(params: any) { + return request(prefix + 'v1/negotiate/update', { + data: params, + method: 'POST' + }); +} + + +export async function deleteAmountItem(param: any) { + return request(prefix + 'v1/amount/item/delete/' + param, { + method: 'get', + }); +} + +export async function getPerformanceById(params: any) { + return request(prefix + 'v1/performance', { + params: params, + method: 'POST' + }); +} diff --git a/src/pages/Contract/Negotiate.tsx b/src/pages/Contract/Negotiate.tsx new file mode 100644 index 0000000..7b400bc --- /dev/null +++ b/src/pages/Contract/Negotiate.tsx @@ -0,0 +1,250 @@ +import React, {useEffect, useState} from 'react'; +import {Table, Button, Upload, message, Form} from 'antd'; +import {PlusOutlined, DeleteOutlined, SendOutlined, CheckOutlined} from '@ant-design/icons'; +import moment, {Moment} from "moment"; +import {createNegotiate, listNegotiate} from "@/pages/Contract/ContractService"; +import {useLocation} from "umi"; +import {saveDateTimeFormatter} from "@/utils/DateUtils"; +import TextArea from "antd/es/input/TextArea"; +import {history} from "@@/core/history"; + +const createDate = moment() + +const tableData: any[] | (() => any[]) = [ + // { + // key: '1', + // id: '', + // contractFileId: '', + // contractFileName: '2024年度汽车保养服务合同.doc', + // createBy: '集团', + // createDate: createDate, + // note: '我方已上传,请确认', + // }, + // { + // key: '2', + // id: '', + // contractFileId: '', + // contractFileName: '2024年度汽车保养服务合同-20241123.doc', + // createBy: '北京风向文化有限公司', + // createDate: createDate, + // note: '已对合同内容进行修改,请确认。', + // }, +]; +const Negotiate: React.FC = () => { + const location = useLocation(); + const contractInfo = location.state?.contractInfo; + const opt = location.state?.opt; + let readOnly = false; + if (opt === 'detail') { + readOnly = true; + } + // 模拟表格数据 + const [tableDataState, setTableDataState] = useState(tableData); + + // 处理TextArea内容变化 + const handleNoteChange = (e: React.ChangeEvent, key: string) => { + const newTableData = tableDataState.map(item => { + if (item.key === key) { + return {...item, note: e.target.value}; + } + return item; + }); + setTableDataState(newTableData); + }; + + const handleDelete = (key: string) => { + const newTableData = tableDataState.filter((row) => row.key !== key); + newTableData.forEach((item, index) => { + item.key = index.toString(); + }) + setTableDataState(newTableData); + }; + + // rowSelection object indicates the need for row selection + const rowSelection = { + onChange: (selectedRowKeys: React.Key[], selectedRows: any[]) => { + console.log(`selectedRowKeys: ${selectedRowKeys}`, 'selectedRows: ', selectedRows); + } + }; + + const fetchData = async () => { + try { + await listNegotiate({ + contractId: contractInfo.id + }).then((res) => { + if (res?.code === 200) { + setTableDataState(res.data); + if (res.data && res.data.length != 0) { + for (const i in res.data) { + const record = res.data[i]; + record.readOnly = true; + } + } + } + }); // 传递搜索参数 + + } catch (error) { + message.error('获取磋商信息列表失败'); + } finally { + } + }; + + useEffect(() => { + fetchData().then(res => {} ); + }, []); + + const saveNegotiateItem = (record: any) => { + delete record.key; + record.contractId = contractInfo.id; + record.createDate = saveDateTimeFormatter(moment(record.createDate)) + createNegotiate(record).then((res) => { + if (res?.code === 200) { + message.success("发送成功") + } else { + message.error("发送失败") + } + }) + } + // @ts-ignore + const columns: Table.ColumnType[] = [ + { + title: '序号', + dataIndex: 'key', + key: 'key', + render: (_: any, record: any, index: number) => index + 1, + }, + { + title: '合同文件名称', + dataIndex: 'contractFileName', + key: 'contractFileName', + render: (text: string) => ( + + {text ? {text} : ( + { + console.log('Uploaded file info:', info); + message.success('文件上传成功'); + }} + > + + + )} + + ), + }, + { + title: '上传方', + dataIndex: 'createBy', + key: 'createBy', + }, + { + title: '创建时间', + dataIndex: 'createDate', + key: 'createDate', + render: (value: Moment, record: any) => value ? moment(value).format('yyyy-MM-DD') : null + }, + { + title: '备注', + dataIndex: 'note', + key: 'note', + render: (text: string, record: any) => + ( +