tenderDetail.vue 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509
  1. <template>
  2. <div class="card">
  3. <div class="df aic jcsb mb10">
  4. <div class="t20">招标信息</div>
  5. <div class="df aic">
  6. <div
  7. class="mr10 fs18"
  8. :style="{ color: tenderData.status === 1 ? '#00B050' : '#FFA500' }"
  9. >
  10. {{ tenderData.status === 1 ? "招标中" : "" }}
  11. {{ tenderData.status === 2 ? "招标完成" : "" }}
  12. {{ tenderData.status === 3 ? "取消招标" : "" }}
  13. </div>
  14. <div class="mr10" v-if="tenderData.status === 1">
  15. 竞标截止剩余: {{ getLeftTime(tenderData.bidDeadlineDatetime) }}
  16. </div>
  17. </div>
  18. </div>
  19. <div class="card mb20">
  20. <div class="df aic mb10">
  21. <div class="info-title">发起时间:</div>
  22. <div class="info-text">{{ tenderData.operatorDate }}</div>
  23. <div class="info-title">货种:</div>
  24. <div class="info-text">{{ tenderData.cargo }}</div>
  25. <div class="info-title">装货港:</div>
  26. <div class="info-text">{{ tenderData.loadPortName }}</div>
  27. </div>
  28. <div class="df aic mb10">
  29. <div class="info-title">截止时间:</div>
  30. <div class="info-text">{{ tenderData.bidDeadlineDatetime }}</div>
  31. <div class="info-title">发货吨位:</div>
  32. <div class="info-text">{{ tenderData.tons }} 吨</div>
  33. <div class="info-title">卸货港:</div>
  34. <div class="info-text">{{ tenderData.dischargePortName }}</div>
  35. </div>
  36. </div>
  37. <div v-if="tenderData.initMethod === 3">
  38. <div class="t20 mb10">前置航次信息</div>
  39. <div class="card">
  40. <AMapContainer
  41. :ships="[voyage]"
  42. :loadPorts="loadPorts"
  43. :dischargePorts="dischargePorts"
  44. style="width: 100%; height: 500px"
  45. ></AMapContainer>
  46. <div class="card mt10">
  47. <div class="t16 mb10">航次信息</div>
  48. <div class="df aic mb10">
  49. <div class="info-title">船名:</div>
  50. <div class="info-text">{{ voyage.shipName }}</div>
  51. <div class="info-title">装货港:</div>
  52. <div class="info-text">{{ voyage.loadPort }}</div>
  53. <div class="info-title">货种:</div>
  54. <div class="info-text">{{ voyage.cargo }}</div>
  55. </div>
  56. <div class="df aic mb10">
  57. <div class="info-title">MMSI:</div>
  58. <div class="info-text">{{ voyage.mmsi }}</div>
  59. <div class="info-title">卸货港:</div>
  60. <div class="info-text">{{ voyage.dischargePorts }}</div>
  61. <div class="info-title">装载吨位:</div>
  62. <div class="info-text">{{ voyage.tons }} 吨</div>
  63. </div>
  64. </div>
  65. </div>
  66. </div>
  67. <div class="t20 mt20 mb10">运力要求</div>
  68. <div class="card">
  69. <pre style="white-space: pre-wrap">{{
  70. tenderData.capacityRequirements
  71. }}</pre>
  72. </div>
  73. <div class="t20 mt20 mb10">报价要求</div>
  74. <div class="card">
  75. <pre style="white-space: pre-wrap">{{ tenderData.quotationRequest }}</pre>
  76. </div>
  77. <el-divider></el-divider>
  78. <div class="t20 mb10">投标信息</div>
  79. <div class="df aic mb10">
  80. <div class="mr10">参与竞标公司数量:</div>
  81. <div class="mr20">{{ tenderData.participateProxyNum }}</div>
  82. <el-button
  83. v-if="tenderData.status === 1"
  84. size="small"
  85. type="primary"
  86. @click="showAddTenderCompany()"
  87. >
  88. 增加竞标公司
  89. </el-button>
  90. <el-dialog v-model="isAddProxyCompanyVisible" title="增加竞标公司">
  91. <div class="df aic mb10">
  92. <div class="red">*</div>
  93. <div>请选择新代理公司(可多选):</div>
  94. <el-select
  95. class="w300 ml20"
  96. v-model="currentProxyCompanies"
  97. value-key="id"
  98. placeholder="请选择参与竞标代理公司"
  99. multiple
  100. clearable
  101. @change="proxyCompaniesChange"
  102. >
  103. <el-option
  104. v-for="item in proxyCompanies"
  105. :key="item.id"
  106. :label="item.companyName"
  107. :value="item"
  108. ></el-option>
  109. </el-select>
  110. </div>
  111. <el-table
  112. v-if="currentProxyCompanies.length > 0"
  113. :data="currentProxyCompanies"
  114. border
  115. stripe
  116. >
  117. <el-table-column label="公司名称" prop="companyName" />
  118. <el-table-column label="联系人" prop="contactName" />
  119. <el-table-column label="手机号" prop="contactPhone" />
  120. <el-table-column label="邮箱" prop="contactEmail" />
  121. <el-table-column label="操作" width="80">
  122. <template #default="scope">
  123. <el-button type="danger" @click="remove(scope)" size="small">
  124. 移除
  125. </el-button>
  126. </template>
  127. </el-table-column>
  128. </el-table>
  129. <div class="df aic jcfe mt20 pr20">
  130. <el-button @click="cancelAddTenderCompany()">取消</el-button>
  131. <el-button type="primary" @click="addTenderProxy()">
  132. 确定添加
  133. </el-button>
  134. </div>
  135. </el-dialog>
  136. </div>
  137. <div class="df aic mb10">
  138. <div class="mr10">未参与竞标公司数量:</div>
  139. <div class="mr20">{{ tenderData.notCompleteBidProxyNum }}</div>
  140. <div class="mr10">完成竞标公司数量:</div>
  141. <div class="mr20">{{ tenderData.completeBidProxyNum }}</div>
  142. </div>
  143. <div class="card mb30" v-for="(item, index) in tenderData.tenderProxies">
  144. <div class="df aic jcsb mb10">
  145. <div>{{ item.proxyCompanyName }}</div>
  146. <div class="df aic" v-if="tenderData.status === 1">
  147. <div class="mr10">邮件通知状态:</div>
  148. <div
  149. class="mr10 fs18"
  150. :style="{
  151. color: item.emailSendStatus === 0 ? '#9d0016' : '#00B050',
  152. }"
  153. >
  154. {{ item.emailSendStatus === 0 ? "未发送" : "" }}
  155. {{ item.emailSendStatus === 1 ? "已发送" : "" }}
  156. </div>
  157. <el-button
  158. v-if="item.emailSendStatus === 0"
  159. size="small"
  160. type="primary"
  161. >
  162. 发送
  163. </el-button>
  164. <el-button
  165. v-if="item.emailSendStatus === 1"
  166. size="small"
  167. type="primary"
  168. >
  169. 再次发送
  170. </el-button>
  171. </div>
  172. </div>
  173. <div v-if="item.bidStatus !== 2">
  174. <AMapContainer
  175. :mapId="'map' + index"
  176. :ships="item.tenderProxyBids"
  177. style="height: 300px"
  178. class="mb10"
  179. ></AMapContainer>
  180. <el-table
  181. :data="item.tenderProxyBids"
  182. border
  183. stripe
  184. show-summary
  185. :summary-method="getSummaries"
  186. >
  187. <el-table-column label="船名" prop="shipName"></el-table-column>
  188. <el-table-column label="MMSI" prop="shipMmsi"></el-table-column>
  189. <el-table-column label="装载吨位" prop="loadTons"></el-table-column>
  190. <el-table-column
  191. label="报价(元/吨)"
  192. prop="quotePrice"
  193. ></el-table-column>
  194. <el-table-column
  195. label="预计运费(元,单船)"
  196. prop="estimatedCost"
  197. ></el-table-column>
  198. <el-table-column label="操作">
  199. <template #default="scope">
  200. <div class="df aic jcc" v-if="tenderData.status === 1">
  201. <el-button
  202. v-if="scope.row.status === 1"
  203. size="small"
  204. type="primary"
  205. @click="selectTenderShip(scope.row, 1)"
  206. >
  207. 选择中标
  208. </el-button>
  209. <el-button
  210. v-if="scope.row.status === 2"
  211. size="small"
  212. type="danger"
  213. @click="selectTenderShip(scope.row, 2)"
  214. >
  215. 取消中标
  216. </el-button>
  217. </div>
  218. <el-tag
  219. type="success"
  220. v-if="scope.row.status === 2 && tenderData.status === 2"
  221. >
  222. 已中标
  223. </el-tag>
  224. </template>
  225. </el-table-column>
  226. </el-table>
  227. </div>
  228. <div v-else>
  229. <el-result
  230. class="card"
  231. icon="error"
  232. title="放弃竞标"
  233. :sub-title="item.giveUpRemark"
  234. ></el-result>
  235. </div>
  236. </div>
  237. <div v-if="tenderData.status === 1" class="df aic jcfe mt30">
  238. <div>
  239. <el-button type="default" @click="completeTender(2)">
  240. 取消招标
  241. </el-button>
  242. <el-button type="primary" @click="completeTender(1)">
  243. 完成招标
  244. </el-button>
  245. </div>
  246. </div>
  247. </div>
  248. </template>
  249. <script setup>
  250. import api from "../../apis/fetch";
  251. import store from "../../store";
  252. import router from "../../router";
  253. import { ref, onMounted, reactive, computed } from "vue";
  254. import { ElNotification, ElMessage, ElMessageBox } from "element-plus";
  255. import { mapGetters } from "vuex";
  256. import { useRoute } from "vue-router";
  257. const route = useRoute();
  258. const tenderData = ref({
  259. operatorDate: "",
  260. bidDeadlineDatetime: "",
  261. cargo: "",
  262. loadPortName: "",
  263. tons: "",
  264. dischargePortName: "",
  265. participateProxyNum: "",
  266. capacityRequirements: "",
  267. quotationRequest: "",
  268. tenderProxies: [],
  269. });
  270. const voyage = ref({
  271. // shipName: "航次船舶",
  272. // loadPort: "武汉",
  273. // cargo: "豆粕",
  274. // mmsi: "998221222",
  275. // dischargePorts: "北仑",
  276. // tons: 5000,
  277. // longitude: 120.487381,
  278. // latitude: 32.042158,
  279. });
  280. const tenderTableData = ref([]);
  281. const getSummaries = (param) => {
  282. const { columns, data } = param;
  283. const sums = [];
  284. columns.forEach((column, index) => {
  285. if (index === 0) {
  286. sums[index] = "预估总运费";
  287. return;
  288. }
  289. const values = data.map((item) => Number(item[column.property]));
  290. if (index == 4) {
  291. sums[index] = `${values.reduce((prev, curr) => {
  292. const value = Number(curr);
  293. if (!Number.isNaN(value)) {
  294. return prev + curr;
  295. } else {
  296. return prev;
  297. }
  298. }, 0)} 元`;
  299. }
  300. });
  301. return sums;
  302. };
  303. const loadPorts = ref([]);
  304. const dischargePorts = ref([]);
  305. async function getTenderDetail() {
  306. let { data } = await api.getTenderDetail({
  307. tenderId: route.query.id,
  308. });
  309. if (data.status === 0) {
  310. tenderData.value = data.result;
  311. for (let i of tenderData.value.tenderProxies) {
  312. for (let j of i.tenderProxyBids) {
  313. j.estimatedCost = j.loadTons * j.quotePrice;
  314. }
  315. }
  316. } else {
  317. tenderData.value = {
  318. operatorDate: "",
  319. bidDeadlineDatetime: "",
  320. cargo: "",
  321. loadPortName: "",
  322. tons: "",
  323. dischargePortName: "",
  324. participateProxyNum: "",
  325. capacityRequirements: "",
  326. quotationRequest: "",
  327. tenderProxies: [],
  328. };
  329. }
  330. }
  331. function getLeftTime(givenTime = "2099-12-31 23:59:59") {
  332. givenTime = new Date(givenTime);
  333. const currentTime = new Date();
  334. // 检查当前时间是否超出了截止时间
  335. if (currentTime > givenTime) {
  336. return "已超过截止时间";
  337. }
  338. const differenceInMilliseconds = Math.abs(givenTime - currentTime);
  339. const days = Math.floor(differenceInMilliseconds / (1000 * 60 * 60 * 24));
  340. const hours = Math.floor(
  341. (differenceInMilliseconds % (1000 * 60 * 60 * 24)) / (1000 * 60 * 60)
  342. );
  343. return `${days}天${hours}小时`;
  344. }
  345. const isAddProxyCompanyVisible = ref(false);
  346. const proxyCompanies = ref([]);
  347. async function getTenderProxySelect() {
  348. let { data } = await api.getTenderProxySelect({
  349. cargoId: tenderData.value.cargoId,
  350. });
  351. if (data.status === 0) {
  352. proxyCompanies.value = data.result.filter(
  353. (item) =>
  354. !tenderData.value.tenderProxies.some(
  355. (item1) => item.id === item1.proxyCompanyId
  356. )
  357. );
  358. } else {
  359. proxyCompanies.value = [];
  360. }
  361. }
  362. const currentProxyCompanies = ref([]);
  363. function proxyCompaniesChange(e) {
  364. currentProxyCompanies.value = e;
  365. }
  366. function remove(scope) {
  367. currentProxyCompanies.value.splice(scope.$index, 1);
  368. }
  369. function showAddTenderCompany() {
  370. isAddProxyCompanyVisible.value = true;
  371. getTenderProxySelect();
  372. }
  373. function cancelAddTenderCompany() {
  374. currentProxyCompanies.value = [];
  375. isAddProxyCompanyVisible.value = false;
  376. }
  377. async function addTenderProxy() {
  378. if (currentProxyCompanies.value.length === 0) {
  379. ElMessage({
  380. type: "warning",
  381. message: "请选择代理公司!",
  382. });
  383. return;
  384. }
  385. let { data } = await api.addTenderProxy({
  386. tenderId: route.query.id,
  387. proxyCompanyIds: currentProxyCompanies.value
  388. .map((item) => item.id)
  389. .join(","),
  390. });
  391. if (data.status === 0) {
  392. ElNotification({
  393. type: "success",
  394. message: "添加成功!",
  395. });
  396. getTenderDetail();
  397. } else {
  398. ElNotification({
  399. type: "error",
  400. message: data.msg,
  401. });
  402. }
  403. cancelAddTenderCompany();
  404. }
  405. function completeTender(status) {
  406. ElMessageBox.confirm(`确认${status === 2 ? "取消" : "完成"}招标?`, "提示", {
  407. confirmButtonText: "确认",
  408. cancelButtonText: "取消",
  409. type: "warning",
  410. }).then(async () => {
  411. let { data } = await api.completeTender({
  412. tenderId: route.query.id,
  413. status,
  414. });
  415. if (data.status === 0) {
  416. ElNotification({
  417. type: "success",
  418. message: `${status === 2 ? "取消" : "完成"}成功!`,
  419. });
  420. } else {
  421. ElNotification({
  422. type: "error",
  423. message: data.msg,
  424. });
  425. }
  426. getTenderDetail();
  427. });
  428. }
  429. function selectTenderShip(row, status) {
  430. ElMessageBox.confirm(
  431. `确认${status === 1 ? "选择" : "取消"}${row.shipName}中标?`,
  432. "提示",
  433. {
  434. confirmButtonText: "确认",
  435. cancelButtonText: "取消",
  436. type: "warning",
  437. }
  438. ).then(async () => {
  439. let { data } = await api.selectTenderShip({
  440. tenderId: route.query.id,
  441. status,
  442. tenderProxyBidId: row.id,
  443. });
  444. if (data.status === 0) {
  445. ElNotification({
  446. type: "success",
  447. message: `${status === 1 ? "选择" : "取消"}成功!`,
  448. });
  449. getTenderDetail();
  450. } else {
  451. ElNotification({
  452. type: "error",
  453. message: data.msg,
  454. });
  455. console.log(data);
  456. }
  457. });
  458. }
  459. onMounted(() => {
  460. getTenderDetail();
  461. });
  462. </script>
  463. <style scoped>
  464. .info-title {
  465. width: 110px;
  466. text-align: right;
  467. color: #666;
  468. margin-right: 10px;
  469. }
  470. .info-text {
  471. width: 160px;
  472. color: #333;
  473. }
  474. .table-title {
  475. color: #666;
  476. margin-right: 10px;
  477. }
  478. .table-text {
  479. color: #333;
  480. margin-right: 30px;
  481. }
  482. </style>