RenewalExpireDetail.vue 85 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309231023112312231323142315231623172318231923202321232223232324232523262327232823292330233123322333233423352336233723382339234023412342234323442345234623472348234923502351235223532354235523562357235823592360236123622363236423652366236723682369237023712372237323742375237623772378237923802381238223832384238523862387238823892390239123922393239423952396239723982399240024012402240324042405240624072408240924102411241224132414241524162417241824192420242124222423242424252426242724282429243024312432243324342435243624372438243924402441244224432444244524462447244824492450245124522453245424552456245724582459246024612462246324642465246624672468246924702471247224732474247524762477247824792480248124822483248424852486248724882489249024912492249324942495249624972498249925002501250225032504250525062507250825092510251125122513251425152516251725182519252025212522252325242525252625272528252925302531253225332534253525362537253825392540254125422543254425452546254725482549255025512552255325542555255625572558255925602561256225632564256525662567256825692570257125722573257425752576257725782579258025812582258325842585258625872588258925902591259225932594259525962597259825992600260126022603260426052606260726082609261026112612261326142615261626172618261926202621262226232624262526262627262826292630263126322633263426352636263726382639264026412642264326442645264626472648264926502651265226532654265526562657265826592660266126622663266426652666266726682669
  1. <template>
  2. <div class="expire-detail-page">
  3. <!-- 顶部绿色头部 -->
  4. <div class="header-bar">
  5. <div class="header-info">
  6. <div class="project-title">{{projects.title}}</div>
  7. <!-- <div class="project-subtitle">{{ projects.c_xmbtqc }}</div> -->
  8. <div class="project-meta">{{projects.c_fbz || projects.zbmsxx ?.zb_source_name || ''}} · {{projects.zbmsxx ?.zb_project_name || ''}} · 金额{{UTIL.formatCurrency(projects.c_nh/10000 || '0')}}万元</div>
  9. <!-- <div class="project-meta">{{projects.c_fbz || projects.zbmsxx ?.zb_source_name || '上海市杨浦区政府采购中心'}} · 杨浦区图书馆总馆物业管理服务项目 · 金额{{UTIL.formatCurrency(projects.c_nh || '0')}}万元</div> -->
  10. <!-- <div class="project-subtitle">{{ projects.c_is_xb == true ? projects.c_xmbtqc : projects.zbmsxx ?.zb_project_name || ''}}</div>
  11. <div class="project-meta">{{projects.c_is_xb == true ? projects.c_fbz : projects.zbmsxx ?.zb_source_name || ''}} · {{projects.c_is_xb == true ? projects.c_zgfw: projects.zbmsxx?.zb_project_name || ''}} · 金额{{UTIL.formatCurrency(projects.c_is_xb == true ? projects.c_nh : projects.zbmsxx ?.zb_budget_amount || '0')}}元</div> -->
  12. </div>
  13. <div class="countdown-badge">
  14. <span class="countdown-number">{{projects.days}}</span>
  15. <span class="countdown-label">天后到期</span>
  16. </div>
  17. </div>
  18. <!-- 进度指示器 -->
  19. <div class="renewal-stepper" >
  20. <div v-for="step in steps" :key="step.n" style="display: flex;align-items: center;">
  21. <div :class="['renewal-step', step.n < stage ? 'done' : step.n === stage ? 'active' : '']">
  22. <span class="renewal-step-dot">{{step.n < stage ? '✓' : step.n}}</span>
  23. {{step.label}}
  24. </div>
  25. <span class="renewal-step-arrow" v-if="step.n < steps[steps.length - 1].n">→</span>
  26. </div>
  27. </div>
  28. <!-- 主内容区域 -->
  29. <div class="main-content">
  30. <!-- 左侧内容 -->
  31. <div class="left-panel">
  32. <!-- 监控招标公告 -->
  33. <div class="panel-card monitoring-card">
  34. <div class="card-body">
  35. <h3 class="monitoring-name">
  36. {{(steps.filter(item => item.n === stage)[0] || {}).title || ''}}
  37. </h3>
  38. <p class="monitoring-desc">
  39. {{(steps.filter(item => item.n === stage)[0] || {}).text || ''}}
  40. </p>
  41. <div class="monitoring-actions" v-if="(steps.filter(item => item.n === stage)[0] || {}).left_btn_text">
  42. <div v-if="(steps.filter(item => item.n === stage)[0] || {}).label == '标书制作'" style="width: 100%;">
  43. <div v-if="stepsType == '1'" style="display: inline-block;">
  44. <el-button type="primary" class="btn enter-btn" @click="handleViewClick(steps.filter(item => item.n === stage)[0] || {},'')">
  45. 提交资料
  46. </el-button>
  47. </div>
  48. <div v-if="stepsType == '2'" style="display: inline-block;">
  49. <el-button type="primary" class="btn enter-btn" @click="handleViewClick(steps.filter(item => item.n === stage)[0] || {},'')">
  50. 生成标书
  51. </el-button>
  52. <el-button type="primary" v-if="!bidFile.isFileFlag" class="btn enter-btn" @click="dialogFormBidFileVisible = true">
  53. 上传参考标书
  54. </el-button>
  55. <!-- <el-button type="primary" :loading="swb_status == '2'" class="btn enter-btn" @click="handleViewClick(steps.filter(item => item.n === stage)[0] || {},'swb')">
  56. 生成商务标书
  57. </el-button>
  58. <el-button type="primary" :loading="jsb_status == '2'" class="btn enter-btn" @click="handleViewClick(steps.filter(item => item.n === stage)[0] || {},'jsb')">
  59. 生成技术标书
  60. </el-button> -->
  61. </div>
  62. <div v-if="stepsType == '3'" style="display: inline-block;">
  63. <el-button type="primary" :loading="stepsType == '3'" class="btn enter-btn" @click="handleViewClick(steps.filter(item => item.n === stage)[0] || {},'')">
  64. 生成标书
  65. </el-button>
  66. </div>
  67. <div v-if="stepsType == '4'" style="display: inline-block;">
  68. <el-button type="primary" :disabled="!bidFile.flag" class="btn enter-btn" @click="handleViewClick(steps.filter(item => item.n === stage)[0] || {},'')">
  69. 进入投标提交
  70. </el-button>
  71. <el-button type="primary" class="btn" @click="getBidFile()">
  72. 获取标书
  73. </el-button>
  74. <!-- <el-button type="primary" class="btn" @click="getBidFile('swb')">
  75. 获取商务标书
  76. </el-button>
  77. <el-button type="primary" class="btn" @click="getBidFile('jsb')">
  78. 获取技术标书
  79. </el-button> -->
  80. </div>
  81. <!-- <div style="float: right;">
  82. <el-button type="primary" class="btn" @click="handleViewClick(steps.filter(item => item.n === stage)[0] || {},'cqgg')">
  83. 上传澄清公告
  84. </el-button>
  85. </div> -->
  86. </div>
  87. <div v-else-if="(steps.filter(item => item.n === stage)[0] || {}).label == '招标监控'" style="width: 100%;">
  88. <!-- <div v-if="stepsType == '1'" style="display: inline-block;">
  89. <el-button type="primary" class="btn enter-btn" @click="handleViewClick(steps.filter(item => item.n === stage)[0] || {},'swb')">
  90. 添加招标监控
  91. </el-button>
  92. </div> -->
  93. <div v-if="stepsType == '1'" style="display: inline-block;">
  94. <el-button type="primary" class="btn enter-btn" @click="handleViewClick(steps.filter(item => item.n === stage)[0] || {},'swb')">
  95. 进入标书制作
  96. </el-button>
  97. </div>
  98. <!-- <div style="float: right;">
  99. <el-button type="primary" class="btn" @click="handleViewClick(steps.filter(item => item.n === stage)[0] || {},'cqgg')">
  100. 上传招标公告
  101. </el-button>
  102. </div> -->
  103. </div>
  104. <div v-else-if="(steps.filter(item => item.n === stage)[0] || {}).label == '开标监控'">
  105. <el-button type="primary" class="btn enter-btn" @click="handleViewClick(steps.filter(item => item.n === stage)[0] || {},'detail')">
  106. 查看监控详情
  107. </el-button>
  108. <el-button type="primary" class="btn enter-btn" @click="handleViewClick(steps.filter(item => item.n === stage)[0] || {},'')">
  109. 进入中标推送
  110. </el-button>
  111. </div>
  112. <div v-else>
  113. <el-button type="primary" class="btn enter-btn" @click="handleViewClick((steps.filter(item => item.n === stage)[0] || {}),'')" :disabled="left_btn_disabled" :loading="left_btn_disabled">
  114. {{(steps.filter(item => item.n === stage)[0] || {}).left_btn_text || ''}}
  115. </el-button>
  116. <el-button type="primary" class="btn enter-btn" v-if="(steps.filter(item => item.n === stage)[0] || {}).right_btn_text" @click="handleViewClick((steps.filter(item => item.n === stage)[0] || {}),'')" :disabled="right_btn_disabled" :loading="right_btn_disabled">
  117. {{(steps.filter(item => item.n === stage)[0] || {}).right_btn_text || ''}}
  118. </el-button>
  119. </div>
  120. </div>
  121. </div>
  122. </div>
  123. <!-- 投标任务清单 -->
  124. <div class="panel-card">
  125. <div class="card-header">
  126. <span class="card-title">投标任务清单</span>
  127. </div>
  128. <div class="card-body">
  129. <ul class="checklist">
  130. <li :class="['checklist-item']" v-for="task in projects.rwlcqd" :key="task.id">
  131. <span :class="['checklist-status', task.done ? 'done' : '']">{{task.done ? '✓' : '○'}} &nbsp;&nbsp; {{task.text}}</span>
  132. <!-- <span class="checklist-text">{{task.text}}</span> -->
  133. <strong style="cursor: pointer;color: var(--primary);" v-if="task.done && projects.zbmsxx?.zb_detail_url && task.id == 4" @click="handleOpenViewClick('1')">查看招标公告</strong>
  134. <div class="checklist-actions" v-if="task.seq<=stage">
  135. <el-button type="primary" size="small" :disabled="task.done" :plain="task.done" v-if="[3].includes(task.id) && !task.done && stepsType == '1'">
  136. <span v-if="!task.done" @click="confirmTask(task)">人工处理</span>
  137. </el-button>
  138. <el-button type="primary" size="small" :disabled="task.done" :plain="task.done" v-if="task.id == 3" :loading="task.type == 1">
  139. <span v-if="task.done">匹配成功</span>
  140. <span v-else @click="confirmTask(task)">{{task.type == 1 ? '匹配中' : task.type == 2 ? '待确认' :''}}</span>
  141. </el-button>
  142. <el-button type="primary" size="small" :disabled="task.done" :plain="task.done" v-if="[7].includes(task.id) && !task.done">
  143. <span v-if="!task.done" @click="confirmTask(task)">人工处理</span>
  144. </el-button>
  145. <el-button type="primary" size="small" :disabled="task.done" :plain="task.done" v-if="task.id == 7" :loading="task.type == 1">
  146. <span v-if="task.done">已完成</span>
  147. <span v-else @click="confirmTask(task)">{{task.type == 1 ? '监控中' : task.type == 2 ? '待确认' :''}}</span>
  148. </el-button>
  149. <el-button type="primary" size="small" :disabled="task.done" :plain="task.done" v-if="task.id != 3 && task.id != 7">
  150. <span v-if="task.done">已完成</span>
  151. <span v-else @click="confirmTask(task)">待确认</span>
  152. </el-button>
  153. </div>
  154. </li>
  155. </ul>
  156. </div>
  157. </div>
  158. <!-- 历史投标参考 -->
  159. <div class="panel-card">
  160. <div class="card-header">
  161. <span class="card-title">历史投标参考</span>
  162. </div>
  163. <div class="card-body">
  164. <table>
  165. <thead>
  166. <tr>
  167. <th>年份</th>
  168. <th>结果</th>
  169. <th>金额</th>
  170. <th>地址</th>
  171. <!-- <th>操作</th> -->
  172. </tr>
  173. </thead>
  174. <tbody>
  175. <tr v-for="item in historyBidList" :key="item.id">
  176. <td>{{ item.year }}</td>
  177. <td>{{ item.type }}</td>
  178. <td>{{ UTIL.formatCurrency(item.money/10000) }} 万元</td>
  179. <td>{{ item.address }}</td>
  180. <!-- <td class="link-text" >
  181. 查看
  182. </td> -->
  183. </tr>
  184. </tbody>
  185. </table>
  186. </div>
  187. </div>
  188. </div>
  189. <!-- 右侧边栏 -->
  190. <div class="right-panel">
  191. <!-- 项目信息 -->
  192. <div class="side-card">
  193. <div class="card-header">
  194. <span class="card-title">项目信息</span>
  195. </div>
  196. <div class="card-body">
  197. <!-- <div class="info-row">
  198. <span class="label">金蝶编码</span>
  199. <span class="value">{{projects.c_jdbm}}</span>
  200. </div> -->
  201. <div class="info-row">
  202. <span class="label">项目编码</span>
  203. <span class="value">{{projects.xmbh}}</span>
  204. </div>
  205. <div class="info-row">
  206. <span class="label">合同起止</span>
  207. <span class="value">{{moment(projects.c_kssj).format('YYYY.MM.DD')}}-{{moment(projects.c_dqsj).format('YYYY.MM.DD')}}</span>
  208. </div>
  209. <div class="info-row">
  210. <span class="label">合同金额</span>
  211. <span class="value">{{UTIL.formatCurrency(projects.c_nh/10000)}}万元</span>
  212. </div>
  213. <div class="info-row">
  214. <span class="label">委托服务</span>
  215. <span class="value">{{getWTFWLabel(projects.c_zgfw)}}</span>
  216. </div>
  217. <!-- <div class="info-row">
  218. <span class="label">合同状况</span>
  219. <span class="value">已签/已签</span>
  220. </div> -->
  221. </div>
  222. </div>
  223. <!-- 右侧边栏 -->
  224. <div class="right-panel">
  225. <!-- 项目信息 -->
  226. <div class="side-card">
  227. <div class="card-header">
  228. <span class="card-title">招标信息</span>
  229. </div>
  230. <div class="card-body">
  231. <div class="info-row">
  232. <span class="label">招标编码</span>
  233. <!-- <span class="value">{{projects.zbProject?.c_project_code || '-' }}</span> -->
  234. <span class="value">{{projects.zbmsxx ?.zb_project_code ? projects.zbmsxx ?.zb_project_code : projects.zbProject?.c_project_code || '-' }}</span>
  235. </div>
  236. <div class="info-row">
  237. <span class="label">招标起止</span>
  238. <!-- <span class="value">{{projects.zbProject?.c_publish_time ? moment(projects.zbProject?.c_publish_time).format('YYYY.MM.DD') : '' }}-{{projects.zbProject?.c_bid_deadline ? moment(projects.zbProject?.c_bid_deadline.replace(/\s*\(.*\)$/, '') , 'YYYY年MM月DD日 HH:mm').format('YYYY.MM.DD') : '' }}</span> -->
  239. <span class="value">{{projects.zbkssj ? moment(projects.zbkssj).format('YYYY.MM.DD') : '' }}-{{projects.zbjzsj ? moment(projects.zbjzsj).format('YYYY.MM.DD') : '' }}</span>
  240. </div>
  241. <div class="info-row">
  242. <span class="label">招标金额</span>
  243. <span class="value">{{projects.zbmsxx ?.zb_budget_amount ? UTIL.formatCurrency(projects.zbmsxx ?.zb_budget_amount / 10000 || 0) + '万元' : '-'}}</span>
  244. </div>
  245. <div class="info-row">
  246. <span class="label">数据源名称</span>
  247. <span class="value">{{projects.zbmsxx ?.zb_source_name || '-' }}</span>
  248. </div>
  249. </div>
  250. </div>
  251. </div>
  252. <!-- 关联模块 -->
  253. <div class="side-card">
  254. <div class="card-header">
  255. <span class="card-title">关联模块</span>
  256. </div>
  257. <div class="card-body">
  258. <div class="module-links">
  259. <el-button size="small" class="btn" @click="goToPage('/renewal')">投标列表</el-button>
  260. <el-button size="small" class="btn" style="margin-left: 0px;" @click="goToPage('/bid-result')">开标与中标</el-button>
  261. <span style="color: var(--text2);cursor: pointer;" @click="goToPage('/tender-pool')">查看全部招标信息→</span>
  262. <!-- <a href="#" class="module-link">标书制作</a>
  263. <a href="#" class="module-link">开标与中标</a>
  264. <a href="#" class="module-link">查看全期招标信息 →</a> -->
  265. </div>
  266. </div>
  267. </div>
  268. <!-- 进程记录 -->
  269. <!-- <div class="side-card">
  270. <div class="card-header">
  271. <span class="card-title">进程记录</span>
  272. </div>
  273. <div class="card-body">
  274. <div class="process-list">
  275. <div class="process-item">
  276. <span class="process-dot"></span>
  277. <span class="process-text">合同到期预警触发</span>
  278. </div>
  279. <div class="process-item">
  280. <span class="process-dot"></span>
  281. <span class="process-text">续标预警</span>
  282. </div>
  283. <div class="process-item">
  284. <span class="process-dot"></span>
  285. <span class="process-text">招标公告监控</span>
  286. </div>
  287. <div class="process-item">
  288. <span class="process-dot"></span>
  289. <span class="process-text">标书生成</span>
  290. </div>
  291. <div class="process-item">
  292. <span class="process-dot"></span>
  293. <span class="process-text">投标报名</span>
  294. </div>
  295. <div class="process-item">
  296. <span class="process-dot"></span>
  297. <span class="process-text">开标结果抓取</span>
  298. </div>
  299. </div>
  300. </div>
  301. </div> -->
  302. </div>
  303. </div>
  304. <el-dialog v-model="dialogKBJKVisible" draggable title="提示信息" width="500">
  305. <div style="height: auto;overflow: auto;">
  306. <el-form
  307. ref="ruleKBJKFormRef"
  308. style="max-width: 600px"
  309. :model="ruleKBJKForm"
  310. :rules="kbjkrules"
  311. label-width="auto"
  312. >
  313. <!-- <el-form-item label="标题" prop="title">
  314. <el-input v-model="ruleKBJKForm.title" placeholder="请输入标题"/>
  315. </el-form-item>
  316. <el-form-item label="描述" prop="content">
  317. <el-input v-model="ruleKBJKForm.content" placeholder="请输入描述"/>
  318. </el-form-item>
  319. <el-form-item label="监控说明" prop="c_jksm">
  320. <el-input v-model="ruleKBJKForm.c_jksm" placeholder="请输入监控说明"/>
  321. </el-form-item> -->
  322. <el-form-item label="投标日期" prop="c_zbtjr">
  323. <el-date-picker
  324. style="width: 100%"
  325. v-model="ruleKBJKForm.c_zbtjr"
  326. type="date"
  327. placeholder="请输入投标日期"
  328. />
  329. </el-form-item>
  330. <el-form-item label="投标报价" prop="c_tbbj">
  331. <el-input-number
  332. style="width: 100%"
  333. v-model="ruleKBJKForm.c_tbbj"
  334. controls-position="right"
  335. placeholder="请输入投标报价"
  336. />
  337. </el-form-item>
  338. </el-form>
  339. </div>
  340. <template #footer>
  341. <div class="dialog-footer">
  342. <el-button @click="dialogKBJKVisible = false" class="btn">取消</el-button>
  343. <el-button type="primary" class="btn enter-btn" @click="handleKBJKSubmit(ruleKBJKFormRef)">
  344. 确认
  345. </el-button>
  346. </div>
  347. </template>
  348. </el-dialog>
  349. <!-- <el-dialog v-model="dialogZBJKVisible" draggable title="提示信息" width="500">
  350. <div style="height: auto;overflow: auto;">
  351. <el-form
  352. ref="ruleZBJKFormRef"
  353. style="max-width: 600px"
  354. :model="ruleZBJKForm"
  355. :rules="zbjkrules"
  356. label-width="auto"
  357. >
  358. <el-form-item label="标题" prop="title">
  359. <el-input v-model="ruleZBJKForm.title" placeholder="请输入标题"/>
  360. </el-form-item>
  361. <el-form-item label="描述" prop="content">
  362. <el-input v-model="ruleZBJKForm.content" placeholder="请输入描述"/>
  363. </el-form-item>
  364. <el-form-item label="省份" prop="c_province">
  365. <el-input v-model="ruleZBJKForm.c_province" placeholder="请输入省份"/>
  366. </el-form-item>
  367. <el-form-item label="市区" prop="c_city">
  368. <el-input v-model="ruleZBJKForm.c_city" placeholder="请输入市区"/>
  369. </el-form-item>
  370. <el-form-item label="区县" prop="c_district">
  371. <el-input v-model="ruleZBJKForm.c_district" placeholder="请输入区县"/>
  372. </el-form-item>
  373. <el-form-item label="标题关键词" prop="c_title_key_word">
  374. <el-input v-model="ruleZBJKForm.c_title_key_word" placeholder="请输入标题关键词(逗号分隔)"/>
  375. </el-form-item>
  376. <el-form-item label="发布机构关键字" prop="c_publisher_key_word">
  377. <el-input v-model="ruleZBJKForm.c_publisher_key_word" placeholder="请输入发布机构关键字(逗号分隔)"/>
  378. </el-form-item>
  379. </el-form>
  380. </div>
  381. <template #footer>
  382. <div class="dialog-footer">
  383. <el-button @click="dialogZBJKVisible = false" class="btn">取消</el-button>
  384. <el-button type="primary" class="btn enter-btn" @click="handleZBJKSubmit(ruleZBJKFormRef)">
  385. 确认
  386. </el-button>
  387. </div>
  388. </template>
  389. </el-dialog> -->
  390. <el-dialog v-model="dialogZBJKVisible" draggable title="提示信息" width="500">
  391. <div style="height: auto;overflow: auto;">
  392. <el-select v-model="ruleZBJKForm.c_project_jkname" placeholder="请选择匹配的项目" value-key="value" @change="handleJKTypeChange" >
  393. <el-option v-for="item in zbjkProject" :key="item.id" :label="item.name" :value="item.id" />
  394. </el-select>
  395. </div>
  396. <template #footer>
  397. <div class="dialog-footer">
  398. <!-- <el-button @click="dialogZBJKVisible = false" class="btn">取消</el-button> -->
  399. <el-button type="primary" class="btn enter-btn" @click="handleJKZBJKSubmit()">
  400. 确认
  401. </el-button>
  402. </div>
  403. </template>
  404. </el-dialog>
  405. <el-dialog v-model="dialogZBVisible" draggable title="提示信息" width="500">
  406. <div style="overflow: auto;">
  407. <el-form
  408. ref="ruleZBFormRef"
  409. style="max-width: 600px"
  410. :model="ruleZBForm"
  411. :rules="zbrules"
  412. label-width="auto"
  413. >
  414. <el-form-item label="招标编码" prop="c_project_code">
  415. <el-input v-model="ruleZBForm.c_project_code" placeholder="请输入招标编码"/>
  416. </el-form-item>
  417. <el-form-item label="招标项目名称" prop="c_project_name">
  418. <el-input v-model="ruleZBForm.c_project_name" placeholder="请输入招标项目名称"/>
  419. </el-form-item>
  420. <el-form-item label="招标金额" prop="c_budget_amount">
  421. <el-input-number
  422. style="width: 100%"
  423. v-model="ruleZBForm.c_budget_amount"
  424. controls-position="right"
  425. placeholder="请输入招标金额"
  426. />
  427. </el-form-item>
  428. <el-form-item label="数据源名称" prop="c_source_name">
  429. <el-input v-model="ruleZBForm.c_source_name" placeholder="例:上海政府采购网"/>
  430. </el-form-item>
  431. <el-form-item label="发布机构" prop="c_author">
  432. <el-input v-model="ruleZBForm.c_author" placeholder="请输入发布机构"/>
  433. </el-form-item>
  434. <el-form-item label="发布时间" prop="c_publish_time">
  435. <el-date-picker
  436. style="width: 100%"
  437. v-model="ruleZBForm.c_publish_time"
  438. type="datetime"
  439. placeholder="请输入发布时间"
  440. />
  441. </el-form-item>
  442. <el-form-item label="开标时间" prop="c_bid_open_time">
  443. <el-date-picker
  444. style="width: 100%"
  445. v-model="ruleZBForm.c_bid_open_time"
  446. type="datetime"
  447. placeholder="请输入开标时间"
  448. />
  449. </el-form-item>
  450. <el-form-item label="招标详情URL" prop="c_detail_url">
  451. <el-input v-model="ruleZBForm.c_detail_url" placeholder="请输入招标详情URL"/>
  452. </el-form-item>
  453. </el-form>
  454. </div>
  455. <template #footer>
  456. <div class="dialog-footer">
  457. <el-button @click="dialogZBVisible = false" class="btn">取消</el-button>
  458. <el-button type="primary" class="btn enter-btn" @click="handleZBSubmit(ruleZBFormRef)">
  459. 确认
  460. </el-button>
  461. </div>
  462. </template>
  463. </el-dialog>
  464. <el-dialog v-model="dialogBidVisible" draggable title="提示信息" width="500">
  465. <div style="height: 500px;overflow: auto;">
  466. <el-form
  467. ref="ruleBidFormRef"
  468. style="max-width: 600px"
  469. :model="ruleBidForm"
  470. :rules="bidRules"
  471. label-width="auto"
  472. >
  473. <el-form-item label="上传招标文件" prop="file1">
  474. <el-upload
  475. class="upload-demo"
  476. drag
  477. :auto-upload="false"
  478. v-model="ruleBidForm.file1.name"
  479. :accept="'.pdf,.docx,.doc'"
  480. :max-size="10 * 1024 * 1024"
  481. :limit="1"
  482. :on-change="handleFileChange"
  483. action=""
  484. >
  485. <!-- :on-change="handleFileChange" -->
  486. <el-icon class="el-icon--upload"><upload-filled /></el-icon>
  487. <div class="el-upload__text">
  488. <em>拖拽或点击选择文件到此处</em>
  489. </div>
  490. <template #tip>
  491. <div class="el-upload__tip">
  492. 仅支持PDF、DOCX、DOC格式,大小不超过10MB
  493. </div>
  494. </template>
  495. </el-upload>
  496. </el-form-item>
  497. <el-form-item label="上传采购需求文件" prop="file2">
  498. <el-upload
  499. class="upload-demo"
  500. drag
  501. :auto-upload="false"
  502. v-model="ruleBidForm.file2.name"
  503. :accept="'.pdf,.docx,.doc'"
  504. :max-size="10 * 1024 * 1024"
  505. :limit="1"
  506. :on-change="handleFileChange2"
  507. action=""
  508. >
  509. <!-- -->
  510. <el-icon class="el-icon--upload"><upload-filled /></el-icon>
  511. <div class="el-upload__text">
  512. <em>拖拽或点击选择文件到此处</em>
  513. </div>
  514. <template #tip>
  515. <div class="el-upload__tip">
  516. 仅支持PDF、DOCX、DOC格式,大小不超过10MB
  517. </div>
  518. </template>
  519. </el-upload>
  520. </el-form-item>
  521. <el-form-item label="上传澄清公告文件" >
  522. <el-upload
  523. class="upload-demo"
  524. drag
  525. v-model="ruleBidForm.file3.name"
  526. :auto-upload="false"
  527. :accept="'.pdf,.docx,.doc'"
  528. :max-size="10 * 1024 * 1024"
  529. :limit="1"
  530. :on-change="handleFileChange3"
  531. action=""
  532. >
  533. <!-- :on-remove="handleFileRemove3"
  534. :file-list="ruleBidForm.file3.file" -->
  535. <el-icon class="el-icon--upload"><upload-filled /></el-icon>
  536. <div class="el-upload__text">
  537. <em>拖拽或点击选择文件到此处</em>
  538. </div>
  539. <template #tip>
  540. <div class="el-upload__tip">
  541. 仅支持PDF、DOCX、DOC格式,大小不超过10MB
  542. </div>
  543. </template>
  544. </el-upload>
  545. </el-form-item>
  546. </el-form>
  547. </div>
  548. <template #footer>
  549. <div class="dialog-footer">
  550. <el-button @click="dialogBidVisible = false" class="btn">取消</el-button>
  551. <el-button type="primary" class="btn enter-btn" @click="handleBidSubmit(ruleBidFormRef)">
  552. 确认
  553. </el-button>
  554. </div>
  555. </template>
  556. </el-dialog>
  557. <el-dialog v-model="dialogFinishBidVisible" draggable title="提示信息" width="350">
  558. <div style="margin-bottom: 5px;">
  559. <el-switch v-model="finishBidObjectTemp.flag" inline-prompt active-text="从中标结果中选择" inactive-text="手动输入" style="background-color: unset;" @change="handleSwitchChange"/>
  560. </div>
  561. <div v-if="finishBidObjectTemp.flag">
  562. <el-select v-model="finishBidObject" filterable placeholder="请选择" style="width: 100%;overflow: auto;" @change="handleChange" value-key="id">
  563. <el-option v-for="item in finishBidOption" :key="item.id" :label="item.title" :value="item" :title="item.title" style="width: 320px;"/>
  564. </el-select>
  565. <div v-if="finishBidObject?.c_detail_url" style="padding-top: 20px;text-align: center;color: var(--primary);">
  566. <strong style="cursor: pointer;" @click="handleOpenViewClick('0')">点击查看招标结果文件</strong>
  567. </div>
  568. </div>
  569. <div v-else>
  570. <el-input v-model="finishBidObjectTemp.url" placeholder="请输入中标结果URL"/>
  571. </div>
  572. <template #footer>
  573. <div class="dialog-footer">
  574. <el-button @click="dialogFinishBidVisible = false" class="btn">取消</el-button>
  575. <el-button type="primary" class="btn enter-btn" @click="handleFinishBidSubmit()">
  576. 确认
  577. </el-button>
  578. </div>
  579. </template>
  580. </el-dialog>
  581. <el-dialog v-model="dialogFinishBidVisible2" draggable title="提示信息" width="350">
  582. <div v-if="finishBidObject?.c_detail_url" style="padding-top: 20px;text-align: center;color: var(--primary);">
  583. <strong style="cursor: pointer;" @click="handleOpenViewClick('0')">点击查看招标结果文件</strong>
  584. </div>
  585. <template #footer>
  586. <div class="dialog-footer">
  587. <el-button @click="dialogFinishBidVisible2 = false" class="btn">取消</el-button>
  588. <el-button type="primary" class="btn enter-btn" @click="handleFinishBidSubmit()">
  589. 确认
  590. </el-button>
  591. </div>
  592. </template>
  593. </el-dialog>
  594. <el-dialog v-model="dialogBidFileVisible" draggable title="提示信息" width="300">
  595. <div>
  596. <strong style="cursor: pointer;" @click="handleOpenViewClick('1')">点击查看招标公告</strong>
  597. </div>
  598. <template #footer>
  599. <div class="dialog-footer">
  600. <el-button @click="dialogBidFileVisible = false" class="btn">取消</el-button>
  601. <el-button type="primary" class="btn enter-btn" @click="handleBidFileSubmit()">
  602. 确认
  603. </el-button>
  604. </div>
  605. </template>
  606. </el-dialog>
  607. <el-dialog v-model="dialogFormFileVisible" draggable title="提示信息" width="300">
  608. <el-input v-model="FileForm.name" autocomplete="off" placeholder="请输入文件名称"/>
  609. <div style="margin-top: 10px;">
  610. <el-upload
  611. class="upload-demo"
  612. action=""
  613. accept=".pdf,.doc,.docx"
  614. style="width: 100%;"
  615. :auto-upload="false"
  616. :on-change="handleCQFileChange"
  617. :file-list="fileList"
  618. limit="1"
  619. >
  620. <el-button size="small" type="primary" style="width: 268px;">点击上传文件</el-button>
  621. <!-- <template #tip>
  622. <div class="el-upload__tip">只能上传 jpg/png 文件,且不超过 500kb</div>
  623. </template> -->
  624. </el-upload>
  625. </div>
  626. <template #footer>
  627. <div class="dialog-footer">
  628. <el-button @click="dialogFormFileVisible = false" class="btn">取消</el-button>
  629. <el-button type="primary" class="btn enter-btn" @click="submitFileForm(FileForm.value.file.raw)">
  630. 确认
  631. </el-button>
  632. </div>
  633. </template>
  634. </el-dialog>
  635. <el-dialog v-model="dialogFormBidFileVisible" draggable title="提示信息" width="300">
  636. <!-- <el-input v-model="FileForm.name" autocomplete="off" placeholder="请输入文件名称"/> -->
  637. <div style="margin-top: 10px;">
  638. <el-upload
  639. class="upload-demo"
  640. action=""
  641. accept=".docx"
  642. style="width: 100%;"
  643. :auto-upload="false"
  644. :on-change="handleBidFileChange"
  645. :file-list="bidFileList"
  646. limit="1"
  647. >
  648. <el-button size="small" type="primary" style="width: 268px;">点击上传参考标书</el-button>
  649. <!-- <template #tip>
  650. <div class="el-upload__tip">只能上传 jpg/png 文件,且不超过 500kb</div>
  651. </template> -->
  652. </el-upload>
  653. </div>
  654. <template #footer>
  655. <div class="dialog-footer">
  656. <!-- <el-button @click="dialogFormBidFileVisible = false" class="btn">取消</el-button> -->
  657. <el-button type="primary" class="btn enter-btn" @click="submitBidFileForm">
  658. 确认
  659. </el-button>
  660. </div>
  661. </template>
  662. </el-dialog>
  663. </div>
  664. </template>
  665. <script setup lang="ts">
  666. import { ref,reactive, onMounted } from 'vue'
  667. import { useRouter } from 'vue-router'
  668. const router = useRouter()
  669. import moment from 'moment'
  670. import api from '@/api/common'
  671. import CONFIG from '@/utils/config'
  672. import UTIL from '@/utils/util'
  673. import { ElMessage,ElLoading } from 'element-plus'
  674. import type { FormInstance, FormRules } from 'element-plus'
  675. // const filePath = '/static/data/杨浦区图书馆总馆物业管理服务项目投标文件.docx'
  676. const userInfo = JSON.parse(localStorage.getItem('userInfo') || '{}')
  677. const userName = userInfo?.username =='user_xtw' ? '徐天炜' : userInfo?.username || ''
  678. const projects = ref<any>({})
  679. const soureProjects = ref<any>({})
  680. const left_btn_disabled = ref(false)
  681. const right_btn_disabled = ref(false)
  682. const dialogZBVisible = ref(false)
  683. const dialogZBJKVisible = ref(false)
  684. const tempTask = ref<any>({})
  685. const dialogBidVisible = ref(false)
  686. const ruleBidFormRef = ref<FormInstance>()
  687. const ruleBidForm = reactive<RuleForm>({
  688. title: '',
  689. content: '',
  690. file1: {
  691. name:'',
  692. file:null,
  693. },
  694. file2: {
  695. name:'',
  696. file:null,
  697. },
  698. file3: {
  699. name:'',
  700. file:null,
  701. },
  702. })
  703. const historyBidList = ref<any>([])
  704. const dialogBidFileVisible = ref(false)
  705. const bidRules = reactive<FormRules<RuleForm>>({
  706. file1: [
  707. { required: true, message: '请上传招标需求文件', trigger: 'blur' },
  708. ],
  709. file2: [
  710. { required: true, message: '请上传采购需求文件', trigger: 'blur' },
  711. ],
  712. })
  713. const steps = ref<any[]>([]);
  714. const stage = ref(Number(sessionStorage.getItem('progres')) || 0);
  715. const stepsType = ref('')
  716. // const swb_status = ref('1')
  717. // const jsb_status = ref('1')
  718. interface RuleForm {
  719. title?: string
  720. content?: string
  721. [key: string]: any
  722. }
  723. const ruleZBFormRef = ref<FormInstance>()
  724. const zbrules = reactive<FormRules<RuleForm>>({
  725. // title: [
  726. // { required: true, message: '请输入标题', trigger: 'blur' },
  727. // ],
  728. // content: [
  729. // { required: true, message: '请输入描述', trigger: 'blur' },
  730. // ],
  731. c_project_code: [
  732. { required: true, message: '请输入项目编码', trigger: 'blur' },
  733. ],
  734. c_project_name: [
  735. { required: true, message: '请输入项目名称', trigger: 'blur' },
  736. ],
  737. c_budget_amount: [
  738. { required: true, message: '请输入预算金额', trigger: 'blur' },
  739. ],
  740. c_bid_open_time: [
  741. { required: true, message: '请输入招标开始时间', trigger: 'blur' },
  742. ],
  743. c_source_name: [
  744. { required: true, message: '请输入来源名称', trigger: 'blur' },
  745. ],
  746. c_author: [
  747. { required: true, message: '请输入作者', trigger: 'blur' },
  748. ],
  749. c_publish_time: [
  750. { required: true, message: '请输入发布时间', trigger: 'blur' },
  751. ],
  752. c_detail_url: [
  753. { required: true, message: '请输入招标结果URL', trigger: 'blur' },
  754. ],
  755. })
  756. const ruleZBForm = reactive<RuleForm>({
  757. id:'',
  758. title: '',
  759. content: '',
  760. c_gp_catalog_name: '',
  761. c_province: '',
  762. c_bid_deadline: '',
  763. c_crawled_at: '',
  764. c_attachments_meta: '',
  765. c_match_score: '',
  766. c_bid_open_place: '',
  767. c_content_document: '',
  768. c_project_code:'',
  769. c_district:'',
  770. c_project_name:'',
  771. c_external_id:'',
  772. c_is_priority:'',
  773. c_bid_open_time:'',
  774. c_content_html_path:'',
  775. c_keywords_hit:'',
  776. c_path_name:'',
  777. c_content_fields:'',
  778. c_budget_amount:'',
  779. c_service_keywords:'',
  780. c_city:'',
  781. c_source_name:'',
  782. c_author:'',
  783. c_publish_time:'',
  784. c_source_id:'',
  785. c_priority:'',
  786. c_record_uuid:UTIL.generateUUID(),
  787. c_content_chunks:'',
  788. c_attachment_urls:'',
  789. c_detail_url:'',
  790. c_category_label:'',
  791. c_project_overview:'',
  792. c_procurement_method:'',
  793. c_bid_type:'0',//招标
  794. })
  795. // const dialogZBJKVisible = ref(false)
  796. // const ruleZBJKFormRef = ref<FormInstance>()
  797. // const zbjkrules = reactive<FormRules<RuleForm>>({
  798. // title: [
  799. // { required: true, message: '请输入标题', trigger: 'blur' },
  800. // ],
  801. // content: [
  802. // { required: true, message: '请输入描述', trigger: 'blur' },
  803. // ],
  804. // })
  805. const ruleZBJKForm = reactive<RuleForm>({
  806. title: '',
  807. content: '',
  808. c_province:'',
  809. c_city:'',
  810. c_district:'',
  811. c_project_code:'',
  812. c_title_key_word:'',
  813. c_publisher_key_word:'',
  814. c_complete:false,
  815. c_gl_xm:'',
  816. c_enable: true
  817. })
  818. const dialogKBJKVisible = ref(false)
  819. const ruleKBJKFormRef = ref<FormInstance>()
  820. const kbjkrules = reactive<FormRules<RuleForm>>({
  821. // title: [
  822. // { required: true, message: '请输入标题', trigger: 'blur' },
  823. // ],
  824. // content: [
  825. // { required: true, message: '请输入描述', trigger: 'blur' },
  826. // ],
  827. c_zbtjr: [
  828. { required: true, message: '请输入投标时间', trigger: 'blur' },
  829. ],
  830. c_tbbj: [
  831. { required: true, message: '请输入投标报价', trigger: 'blur' },
  832. ],
  833. })
  834. const ruleKBJKForm = reactive<RuleForm>({
  835. title: '',
  836. content: '',
  837. c_jksm:'',
  838. c_zbbh:'',
  839. c_sfzb:false,
  840. c_kbsj:'',
  841. c_xmmc:'',
  842. // c_zbtjr:moment('2026-06-29 10:00:00').format('YYYY-MM-DD HH:mm:ss'),
  843. c_zbtjr:'',
  844. c_tbbj:'',
  845. c_glxm:'',
  846. c_enable: true
  847. })
  848. const dialogFinishBidVisible = ref(false)
  849. const dialogFinishBidVisible2 = ref(false)
  850. const finishBidObjectTemp = ref({
  851. flag:false,
  852. url:''
  853. })
  854. const finishBidObject = ref<any>(null)
  855. const finishBidOption = ref([{} as RuleForm])
  856. const goToPage = (path: string) => {
  857. router.push(path)
  858. }
  859. onMounted(() => {
  860. initCName()
  861. initData()
  862. })
  863. const wtfwOptions = ref<any>([])
  864. const getWTFWLabel = (value: string) => {
  865. return wtfwOptions.value.find((item: any) => item.value == value)?.label || '-'
  866. }
  867. const getDmsTypes = async (cName: string) => {
  868. const params = {
  869. type: 0,
  870. cName: cName,
  871. }
  872. const res = await api.getDmsTypes(params)
  873. if ((res as any).code === 200) {
  874. const ret = (res as any).content
  875. return ret.map((item: any) => ({
  876. ...item,
  877. label: item.name,
  878. value: item.index
  879. }))
  880. } else {
  881. return []
  882. }
  883. }
  884. const initCName = async () => {
  885. // 并行请求两个字典,比await串行更快
  886. const [wtOptions,] = await Promise.all([
  887. getDmsTypes("sq_wtfs"),
  888. ])
  889. wtfwOptions.value = wtOptions
  890. }
  891. const initData = () => {
  892. stage.value = Number(Number(sessionStorage.getItem('progres')) || 0);
  893. getProjects();
  894. getStage()
  895. }
  896. const getStage = () => {
  897. steps.value = [
  898. { n: 1, label: '预警触发', status: '', title:'预警触发',text:'忽略预警触发,准备进入投/续标准备阶段。',left_btn_text:'忽略预警 · 启动续标',left_url:'' },
  899. { n: 2, label: '投/续标准备', status: '', title:'投/续标准备',text:'确认客户回访与现场踏勘和服务评估后,准备进入招标监控阶段。',left_btn_text:'进入招标监控',left_url:'' },
  900. { n: 3, label: '招标监控', status: '', title:'监控招标公告',text:'系统正在监控,匹配本项目续标公告。',left_btn_text:'进入标书制作',left_url:''},
  901. { n: 4, label: '标书制作', status: '', title:'制作续标投标文件',text:'招标文件已就绪,进入标书编制完成拆解与生成。',left_btn_text:'生成商务标书',left_url:'',right_btn_text:'生成技术标书',right_url:''},
  902. { n: 5, label: '投标提交', status: '', title:'提交投标并登记',text:'标书完成后,登记投标信息以便开标监控。',left_btn_text:'登记投标',left_url:'',right_btn_text:'',right_url:''},
  903. { n: 6, label: '开标监控', status: '', title:'开标监控中',text:'已登记投标,在开标时间内监控开标结果。',left_btn_text:'查看监控详情',left_url:'/bid-result',right_btn_text:'进入中标推送',right_url:''},
  904. { n: 7, label: '中标推送', status: '', title:'中标推送',text:'开标结果已出,请确认中标信息并推送至各职能部门',left_btn_text:'',left_url:'/bid-result',right_btn_text:'',right_url:''},
  905. ]
  906. steps.value.forEach((item: any) => {
  907. if (item.n < stage.value) {
  908. item.status = 'done'
  909. }else if (item.n == stage.value) {
  910. item.status = 'active'
  911. }else{
  912. item.status = ''
  913. }
  914. })
  915. }
  916. const confirmTask = async (task: any) => {
  917. tempTask.value = {}
  918. if(task.id == "1" || task.id == "2"){
  919. const msg = getMessage(2)
  920. if(msg){
  921. ElMessage.error(msg)
  922. }else{
  923. updaterwlcqdProject(task)
  924. }
  925. }else if(task.id == "3"){
  926. const msg = getMessage(3)
  927. if(msg){
  928. ElMessage.error(msg)
  929. }else{
  930. tempTask.value = task
  931. if(task.type != 2){
  932. dialogZBVisible.value = true
  933. }else{
  934. dialogZBJKVisible.value = true
  935. }
  936. }
  937. }else if(task.id == "4"){
  938. const msg = getMessage(4)
  939. if(msg){
  940. ElMessage.error(msg)
  941. }else{
  942. tempTask.value = task
  943. dialogBidFileVisible.value = true
  944. }
  945. }else if(task.id == "5"){
  946. const msg = getMessage(4)
  947. if(msg){
  948. ElMessage.error(msg)
  949. }else{
  950. updaterwlcqdProject(task)
  951. }
  952. }else if(task.id == "6"){
  953. updaterwlcqdProject(task)
  954. }else if(task.id == "7"){
  955. const res = await getfinishProjects()
  956. if(res){
  957. finishBidOption.value = res
  958. if(task.type != 2){
  959. dialogFinishBidVisible.value = true
  960. dialogFinishBidVisible2.value = false
  961. }else{
  962. dialogFinishBidVisible2.value = true
  963. dialogFinishBidVisible.value = false
  964. }
  965. }else{
  966. finishBidOption.value = []
  967. }
  968. }else if(task.id == "8"){
  969. sessionStorage.setItem('activeTab','won')
  970. sessionStorage.setItem('jkId', projects.value.c_xgjk);
  971. goToPage('/bid-result')
  972. }
  973. }
  974. const getMessage = (ids: any) => {
  975. if(ids == stage.value){
  976. return ""
  977. }else{
  978. return "请先执行完"+steps.value.filter((item: any) => item.n == stage.value)[0].label
  979. }
  980. }
  981. // 点击进入下一步
  982. const handleViewClick = (item: any,type: string) => {
  983. if(item.n == 1){
  984. updateProject()
  985. }else if(item.n == 2){
  986. let flag = projects.value.rwlcqd.find((p: any) => p.id == '1').done
  987. let flag2 = projects.value.rwlcqd.find((p: any) => p.id == '2').done
  988. if(flag && flag2){
  989. updateProject()
  990. ruleZBJKForm.title = projects.value.title,
  991. ruleZBJKForm.content = projects.value.content,
  992. ruleZBJKForm.c_province = projects.value.sourceProject.c_sfzxs,
  993. ruleZBJKForm.c_city = projects.value.sourceProject.c_sfzxs,
  994. ruleZBJKForm.c_district = projects.value.sourceProject.c_sq,
  995. // ruleZBJKForm.c_project_code = projects.value.xmbh
  996. ruleZBJKForm.c_title_key_word = projects.value.c_xmbt,
  997. ruleZBJKForm.c_publisher_key_word = projects.value.c_xfyt,
  998. ruleZBJKForm.c_complete = false,
  999. ruleZBJKForm.c_gl_xm = projects.value.id,
  1000. ruleZBJKForm.c_enable = true
  1001. addContentParams(ruleZBJKForm,CONFIG.COLUMNIDARR[5],CONFIG.MODELIDARR[2])
  1002. if(projects.value.c_xgzb){
  1003. let str = {id: (item.n+1)}
  1004. updaterwlcqdProject(str)
  1005. }
  1006. }else{
  1007. ElMessage.error("请先确认任务清单中的客户回访与现场踏勘和服务评估")
  1008. }
  1009. }else if(item.n == 3){
  1010. let flag = projects.value.rwlcqd.find((p: any) => p.id == '3').done
  1011. if(flag){
  1012. updateProject()
  1013. }else{
  1014. ElMessage.error("请先确认匹配标书,待匹配完成后,可进入标书制作")
  1015. }
  1016. }else if(item.n == 4){
  1017. if(type == 'cqgg'){
  1018. dialogFormFileVisible.value = true
  1019. }else if(stepsType.value == '1'){
  1020. let flag = projects.value.rwlcqd.find((p: any) => p.id == '4').done
  1021. if(flag){
  1022. dialogBidVisible.value = true
  1023. }else{
  1024. ElMessage.error("请先报名获取招标文件后,可进行提交资料")
  1025. }
  1026. }else if(stepsType.value == '2'){
  1027. let flag = projects.value.rwlcqd.find((p: any) => p.id == '5').done
  1028. if(flag){
  1029. if(soureProjects.value.c_history_tender){// 有历史标书
  1030. if(soureProjects.value.c_history_tender.indexOf(".docx") != -1){// 历史标书是正常的docx文件
  1031. // 生成招标文件
  1032. api.getGenerateTender({txbId: projects.value.id}).then((res: any) => {
  1033. if ((res as any).code === 200) {
  1034. updateTypeProject(3)
  1035. ElMessage.success("标书文件生成成功")
  1036. bidFile.value.isFileFlag = false
  1037. sessionStorage.setItem('isFileFlag', 'false')
  1038. }else{
  1039. ElMessage.error((res as any).content || "标书文件生成失败")
  1040. }
  1041. })
  1042. }else{
  1043. ElMessage.error("请先确认上传参考标书")
  1044. bidFile.value.isFileFlag = false
  1045. sessionStorage.setItem('isFileFlag', 'false')
  1046. }
  1047. }else{
  1048. // 无历史标书
  1049. ElMessage.error("请先确认上传参考标书")
  1050. bidFile.value.isFileFlag = false
  1051. sessionStorage.setItem('isFileFlag', 'false')
  1052. }
  1053. }else{
  1054. ElMessage.error("请先确认标书需求拆解与生成")
  1055. }
  1056. }else if(stepsType.value == '4'){
  1057. updateProject()
  1058. }
  1059. }else if(item.n == 5){
  1060. let flag = projects.value.rwlcqd.find((p: any) => p.id == '6').done
  1061. if(flag){
  1062. dialogKBJKVisible.value = true
  1063. }else{
  1064. ElMessage.error("请先确认登记投标信息")
  1065. }
  1066. }else if(item.n == 6){
  1067. if(type == 'detail'){
  1068. goToPage(item.left_url)
  1069. }else{
  1070. let flag = projects.value.rwlcqd.find((p: any) => p.id == '7').done
  1071. if(flag){
  1072. updateProject()
  1073. // 修改中标监控
  1074. updateJKLCProject()
  1075. }else{
  1076. ElMessage.error("请先确认开标监控,待开标监控完成后,可进入中标推送")
  1077. }
  1078. }
  1079. }else{
  1080. sessionStorage.setItem('activeTab','won')
  1081. sessionStorage.setItem('jkId', projects.value.c_xgjk);
  1082. goToPage(item.left_url)
  1083. }
  1084. }
  1085. //只修改项目进度
  1086. const updateProject = async () => {
  1087. let xmjd_json = projects.value.c_xmjd_json;
  1088. xmjd_json[xmjd_json.length-1].create_time = moment().format('YYYY-MM-DD HH:mm')
  1089. xmjd_json[xmjd_json.length-1].username = userName
  1090. xmjd_json[xmjd_json.length-1].userid = userInfo?.id || ''
  1091. xmjd_json[xmjd_json.length-1].type = 0
  1092. let str = {xmjd: (stage.value+1), type: 1, create_time: '', username: '', userid: ''}
  1093. xmjd_json.push(str)
  1094. const params: any = {
  1095. columnId: CONFIG.COLUMNIDARR[2],
  1096. modelId: CONFIG.MODELIDARR[0],
  1097. content: JSON.stringify({
  1098. id: projects.value.id,
  1099. c_xmjd_json: xmjd_json
  1100. }),
  1101. }
  1102. const res = await api.updateContent(params)
  1103. if ((res as any).code === 200) {
  1104. sessionStorage.setItem('progres', (stage.value+1).toString());
  1105. initData()
  1106. }else{
  1107. sessionStorage.setItem('progres', (stage.value).toString());
  1108. }
  1109. }
  1110. // 更新项目流程状态
  1111. const updaterwlcqdProject = async (item:any) => {
  1112. projects.value.rwlcqd.find((p: any) => p.id == item.id).done = true
  1113. projects.value.rwlcqd.find((p: any) => p.id == item.id).create_time = moment().format('YYYY-MM-DD HH:mm')
  1114. projects.value.rwlcqd.find((p: any) => p.id == item.id).username = userName
  1115. projects.value.rwlcqd.find((p: any) => p.id == item.id).userid = userInfo?.id || ''
  1116. projects.value.rwlcqd.find((p: any) => p.id == item.id).type = 0
  1117. const params: any = {
  1118. columnId: CONFIG.COLUMNIDARR[2],
  1119. modelId: CONFIG.MODELIDARR[0],
  1120. content: JSON.stringify({
  1121. id: projects.value.id,
  1122. rwlcqd:projects.value.rwlcqd
  1123. }),
  1124. }
  1125. const res = await api.updateContent(params)
  1126. if ((res as any).code === 200) {
  1127. initData()
  1128. }
  1129. }
  1130. // 更新投续标
  1131. const updateTXBProject = async (columnId:any,modelId:any,content:any) => {
  1132. const params: any = {
  1133. columnId: columnId,
  1134. modelId: modelId,
  1135. content: JSON.stringify(content),
  1136. }
  1137. const res = await api.updateContent(params)
  1138. if ((res as any).code === 200) {
  1139. sessionStorage.setItem('progres', (stage.value+1).toString());
  1140. initData()
  1141. }else{
  1142. sessionStorage.setItem('progres', (stage.value).toString());
  1143. }
  1144. }
  1145. const getProjects = async () => {
  1146. const params: any = {
  1147. columnId: CONFIG.COLUMNIDARR[2],
  1148. orderBy: JSON.stringify([{ field: "update_time", orderByType: 2 }]),
  1149. states: '0,1,2,3',
  1150. pageSize: 10,
  1151. page: 0
  1152. }
  1153. if (sessionStorage.getItem('dmsId')) {
  1154. params.search = JSON.stringify([
  1155. ...(params.search ? JSON.parse(params.search) : []),
  1156. {
  1157. field: "id",
  1158. searchType: 1,
  1159. content: { value: sessionStorage.getItem('dmsId') },
  1160. },
  1161. ]);
  1162. }
  1163. const res = await getDmsDataList(params)
  1164. if ((res as any).code === 200) {
  1165. // 使用 Promise.all 等待所有异步操作完成
  1166. const data = (res as any).content.data
  1167. const promises = data.map((item: any) =>
  1168. calculateRemaining(
  1169. // item.c_is_xb == true ? item.c_kssj : item.zbkssj,
  1170. // item.c_is_xb == true ? item.c_dqsj : item.zbjzsj,
  1171. item.c_kssj,
  1172. item.c_dqsj,
  1173. item.zbxmbh,
  1174. item.xmbh,
  1175. ).then(result => ({
  1176. ...item,
  1177. zbmsxx:item.zbmsxx == undefined ? {} : JSON.parse(item.zbmsxx),
  1178. c_xmjd_json:JSON.parse(item.c_xmjd_json),
  1179. rwlcqd:JSON.parse(item.rwlcqd),
  1180. ...result
  1181. }))
  1182. )
  1183. const result = await Promise.all(promises)
  1184. projects.value = result[0]
  1185. stepsType.value = projects.value.c_xmjd_json[projects.value.c_xmjd_json.length-1].type
  1186. // swb_status.value = projects.value.c_xmjd_json[projects.value.c_xmjd_json.length-1].typeObj?.swb || ''
  1187. // jsb_status.value = projects.value.c_xmjd_json[projects.value.c_xmjd_json.length-1].typeObj?.jsb || ''
  1188. historyBidList.value = []
  1189. soureProjects.value = await getSoureProjects(CONFIG.COLUMNIDARR[0],projects.value.xmbh,"c_guid")
  1190. historyBidList.value = [{
  1191. year: soureProjects.value.c_guid.substring(10,14),
  1192. name: soureProjects.value.c_xmmc,
  1193. type: "中标",
  1194. money: soureProjects.value.c_nhhtsr,
  1195. address: soureProjects.value.c_query_address || ''
  1196. }]
  1197. }
  1198. if(projects.value.rwlcqd[2].done == false){//匹配招标监控
  1199. getzbjkPorject(projects.value.id)
  1200. }
  1201. if(projects.value.rwlcqd[5].done == true && projects.value.rwlcqd[6].done == false){//匹配开标监控
  1202. getkbjkPorject(projects.value.c_xgjk)
  1203. }
  1204. }
  1205. const zbjkProject = ref<any[]>([]);
  1206. const getzbjkPorject = async (id:any) => {
  1207. const ret = await getTypeProjects(CONFIG.COLUMNIDARR[5],id,"c_gl_xm")
  1208. if(ret.c_result_json){
  1209. const reslust = JSON.parse(ret.c_result_json)
  1210. let arr:any[] = []
  1211. reslust.notices.forEach((item:any) => {
  1212. let str = {id:item.data.id,type:item.type,name:item.data.title || ''}
  1213. arr.push(str)
  1214. })
  1215. reslust.intents.forEach((item:any) => {
  1216. let str = {id:item.data.id,type:item.type,name:item.data.title || ''}
  1217. arr.push(str)
  1218. })
  1219. zbjkProject.value = arr
  1220. updaterwlcqdProject2(projects.value,2)//匹配招标监控成功,进行修改投续标流程状态
  1221. }
  1222. }
  1223. const getkbjkPorject = async (id:any) => {
  1224. const ret = await getTypeProjects(CONFIG.COLUMNIDARR[6],id,"id")
  1225. if(ret){
  1226. if(ret.c_jgjk_url){
  1227. let str = {c_detail_url:ret.c_jgjk_url}
  1228. finishBidObject.value = str
  1229. updaterwlcqdProject2(projects.value,6)//匹配开标监控成功,进行修改投续标流程状态
  1230. }
  1231. }
  1232. }
  1233. // 匹配招标有数据进行更新项目流程状态
  1234. const updaterwlcqdProject2 = async (item:any,lcid:any) => {
  1235. item.rwlcqd[lcid].type = 2
  1236. const params: any = {
  1237. columnId: CONFIG.COLUMNIDARR[2],
  1238. modelId: CONFIG.MODELIDARR[0],
  1239. content: JSON.stringify({
  1240. id: item.id,
  1241. rwlcqd:item.rwlcqd
  1242. }),
  1243. }
  1244. await api.updateContent(params)
  1245. }
  1246. // 项目类型改变时,进行更新项目类型
  1247. const handleJKTypeChange = async (val:any) => {
  1248. console.log(val)
  1249. let opt = zbjkProject.value.find((item:any) => item.id == val)
  1250. console.log(opt)
  1251. if(opt){
  1252. if(opt.type == 'notice'){//公告
  1253. let ret = await getSoureProjects(CONFIG.COLUMNIDARR[3],opt.id,"id")
  1254. console.log(ret)
  1255. ruleZBForm.c_project_code = ret.c_project_code || ""
  1256. ruleZBForm.c_project_name = ret.c_project_name || ""
  1257. ruleZBForm.c_budget_amount = ret.c_budget_amount || ""
  1258. ruleZBForm.c_bid_open_time = ret.c_bid_open_time || ""
  1259. ruleZBForm.c_bid_deadline = ret.c_bid_deadline || ""
  1260. ruleZBForm.c_source_name = ret.c_source_name || ""
  1261. ruleZBForm.c_author = ret.c_author || ""
  1262. ruleZBForm.c_publish_time = ret.c_publish_time || ""
  1263. ruleZBForm.c_detail_url = ret.c_detail_url || ""
  1264. }else{//意向
  1265. let ret = await getSoureProjects(CONFIG.COLUMNIDARR[4],opt.id,"id")
  1266. console.log(ret)
  1267. ruleZBForm.c_project_code = ret.id || ""
  1268. ruleZBForm.c_project_name = ret.title || ""
  1269. ruleZBForm.c_budget_amount = 0
  1270. ruleZBForm.c_bid_open_time = ret.c_publish_time || ""
  1271. ruleZBForm.c_bid_deadline = ret.c_publish_time || ""
  1272. ruleZBForm.c_source_name = ret.c_source_name || ""
  1273. ruleZBForm.c_author = ret.c_author || ""
  1274. ruleZBForm.c_publish_time = ret.c_publish_time || ""
  1275. ruleZBForm.c_detail_url = ret.c_detail_url || ""
  1276. }
  1277. }
  1278. }
  1279. // 匹配招标监控
  1280. const handleJKZBJKSubmit = async () => {
  1281. dialogZBJKVisible.value = false
  1282. let formData = {
  1283. c_project_code:ruleZBForm.c_project_code,
  1284. c_project_name:ruleZBForm.c_project_name,
  1285. c_budget_amount:ruleZBForm.c_budget_amount,
  1286. c_bid_open_time:moment(ruleZBForm.c_bid_open_time, 'YYYY年MM月DD日 HH:mm').format('YYYY-MM-DD HH:mm'),
  1287. c_bid_deadline:moment(ruleZBForm.c_bid_open_time, 'YYYY年MM月DD日 HH:mm').format('YYYY-MM-DD HH:mm'),
  1288. c_source_name:ruleZBForm.c_source_name,
  1289. c_author:ruleZBForm.c_author,
  1290. c_publish_time:ruleZBForm.c_publish_time,
  1291. c_detail_url:ruleZBForm.c_detail_url,
  1292. }
  1293. updateZBProject(tempTask.value,formData,ruleZBForm.c_project_code)
  1294. getProjectParams(projects.value.id,'c_gl_xm',CONFIG.COLUMNIDARR[5]).then(async res => {
  1295. const reslust = (res as any)[0]
  1296. const content = {
  1297. id: reslust.id,
  1298. c_complete: true,
  1299. c_enable: false
  1300. }
  1301. updateProjectParams(CONFIG.COLUMNIDARR[5],CONFIG.MODELIDARR[2], content)
  1302. })
  1303. }
  1304. const getSoureProjects = async (columnId:number,ids: string,type: string) =>{
  1305. const params: any = {
  1306. columnId: columnId,
  1307. states: '0,1,2,3',
  1308. pageSize: 1,
  1309. page: 0
  1310. }
  1311. if (ids !== "") {
  1312. params.search = JSON.stringify([
  1313. ...(params.search ? JSON.parse(params.search) : []),
  1314. {
  1315. field: type,
  1316. searchType: 2,
  1317. content: { value: `%${ids}%` },
  1318. },
  1319. ]);
  1320. }
  1321. const res = await getDmsDataList(params)
  1322. if((res as any).code == 200){
  1323. return (res as any).content.data[0]
  1324. }else{
  1325. return ''
  1326. }
  1327. }
  1328. const getDmsDataList = async (params: any) => {
  1329. return await api.getDmsDataList(params)
  1330. }
  1331. const calculateRemaining = async (st: string, et: string, zbxmbh: string,xmbh: string): Promise<{ days: string; daysClass: string; year: string; type: string, zbProject: any,sourceProject:any }> => {
  1332. if (!et) return { days: '', daysClass: '', year: '', type: '', zbProject: {},sourceProject:{} }
  1333. const startDate = moment(moment(st).format('YYYY-MM-DD'), 'YYYY-MM-DD')
  1334. const expireDate = moment(moment(et).add(0, 'days').format('YYYY-MM-DD'), 'YYYY-MM-DD')
  1335. if (!startDate.isValid() || !expireDate.isValid()) {
  1336. return { days: '', daysClass: '', year: '', type: '', zbProject: {},sourceProject:{} }
  1337. }
  1338. const zbProject = await getTypeProjects(CONFIG.COLUMNIDARR[3],zbxmbh,"id")
  1339. const sourceProject = await getTypeProjects(CONFIG.COLUMNIDARR[0],xmbh,"c_guid")
  1340. const today = moment().startOf('day')
  1341. const diffDays = expireDate.diff(today, 'days')
  1342. let yearNum = 0
  1343. if (today.isAfter(expireDate)) {
  1344. yearNum = expireDate.year() - startDate.year()
  1345. } else {
  1346. yearNum = today.year() - startDate.year()
  1347. if (yearNum > expireDate.year() - startDate.year()) {
  1348. yearNum = expireDate.year() - startDate.year()
  1349. }
  1350. if (yearNum < 0) yearNum = 0
  1351. }
  1352. if (diffDays < 0) {
  1353. return { days: `${Math.abs(diffDays)}`, daysClass: 'grey', year: yearNum.toString(), type: '已完成', zbProject:zbProject,sourceProject:sourceProject }
  1354. } else if (diffDays === 0) {
  1355. return { days: '0', daysClass: 'grey', year: yearNum.toString(), type: '进行中', zbProject:zbProject,sourceProject:sourceProject }
  1356. } else if (diffDays <= 60) {
  1357. return { days: `${diffDays}`, daysClass: 'red', year: yearNum.toString(), type: '进行中', zbProject:zbProject,sourceProject:sourceProject }
  1358. } else if (diffDays <= 90) {
  1359. return { days: `${diffDays}`, daysClass: 'orange', year: yearNum.toString(), type: '监控中', zbProject:zbProject,sourceProject:sourceProject }
  1360. } else if (diffDays <= 180) {
  1361. return { days: `${diffDays}`, daysClass: 'yellow', year: yearNum.toString(), type: '监控中', zbProject:zbProject,sourceProject:sourceProject }
  1362. } else {
  1363. return { days: `${diffDays}`, daysClass: 'green', year: yearNum.toString(), type: '监控中', zbProject:zbProject,sourceProject:sourceProject }
  1364. }
  1365. }
  1366. const getTypeProjects = async (columnId: number,id: string,type:string) => {
  1367. const params: any = {
  1368. columnId: columnId,
  1369. orderBy: JSON.stringify([{ field: "update_time", orderByType: 2 }]),
  1370. states: '0,1,2,3',
  1371. pageSize: 1,
  1372. page: 0
  1373. }
  1374. params.search = JSON.stringify([
  1375. ...(params.search ? JSON.parse(params.search) : []),
  1376. {
  1377. field: type,
  1378. searchType: 1,
  1379. content: { value: id },
  1380. },
  1381. ]);
  1382. const res = await getDmsDataList(params)
  1383. if ((res as any).code === 200) {
  1384. return (res as any).content.data[0]
  1385. }else{
  1386. return {}
  1387. }
  1388. }
  1389. const getfinishProjects = async () => {
  1390. const params: any = {
  1391. columnId: CONFIG.COLUMNIDARR[7],
  1392. orderBy: JSON.stringify([{ field: "update_time", orderByType: 2 }]),
  1393. states: '0,1,2,3',
  1394. pageSize: 9999,
  1395. page: 0
  1396. }
  1397. const res = await getDmsDataList(params)
  1398. if ((res as any).code === 200) {
  1399. return (res as any).content.data
  1400. }else{
  1401. return {}
  1402. }
  1403. }
  1404. // const addContent = async (formData: any) => {
  1405. // const params = {
  1406. // content: JSON.stringify(formData),
  1407. // columnId: CONFIG.COLUMNIDARR[3],
  1408. // modelId: CONFIG.MODELIDARR[1],
  1409. // }
  1410. // const res = await api.addContent(params)
  1411. // if((res as any).code === 200){
  1412. // ElMessage.success('添加成功')
  1413. // updateZBProject(tempTask.value,formData,(res as any).content)
  1414. // dialogZBVisible.value = false
  1415. // }else{
  1416. // // ElMessage.error('添加失败')
  1417. // dialogZBVisible.value = false
  1418. // }
  1419. // }
  1420. const addContentParams = async (formData: any,columnId:number,modelId:number) => {
  1421. const params = {
  1422. content: JSON.stringify(formData),
  1423. columnId: columnId,
  1424. modelId: modelId,
  1425. }
  1426. const res = await api.addContent(params)
  1427. return res
  1428. }
  1429. //更新招标数据进入到投续标
  1430. const updateZBProject = async (item:any,formData:any,contentId:any) => {
  1431. projects.value.rwlcqd.find((p: any) => p.id == item.id).done = true
  1432. projects.value.rwlcqd.find((p: any) => p.id == item.id).create_time = moment().format('YYYY-MM-DD HH:mm')
  1433. projects.value.rwlcqd.find((p: any) => p.id == item.id).username = userName
  1434. projects.value.rwlcqd.find((p: any) => p.id == item.id).userid = userInfo?.id || ''
  1435. projects.value.rwlcqd.find((p: any) => p.id == item.id).type = 0
  1436. const params: any = {
  1437. columnId: CONFIG.COLUMNIDARR[2],
  1438. modelId: CONFIG.MODELIDARR[0],
  1439. content: JSON.stringify({
  1440. id: projects.value.id,
  1441. rwlcqd:projects.value.rwlcqd,
  1442. c_xgzb:contentId,
  1443. zbxmbh:contentId,
  1444. zbmsxx:JSON.stringify({
  1445. zb_project_code:formData.c_project_code,
  1446. zb_source_name:formData.c_source_name,
  1447. zb_project_name:formData.c_project_name,
  1448. zb_budget_amount:formData.c_budget_amount,
  1449. zb_detail_url:formData.c_detail_url,
  1450. zb_publish_time:formData.c_publish_time,
  1451. zb_bid_deadline:formData.c_bid_deadline,
  1452. zb_author:formData.c_author,
  1453. }),
  1454. zbkssj:moment(formData.c_publish_time).format('YYYY-MM-DD HH:mm'),
  1455. zbjzsj:moment(formData.c_bid_deadline).format('YYYY-MM-DD HH:mm'),
  1456. }),
  1457. }
  1458. const res = await api.updateContent(params)
  1459. if ((res as any).code === 200) {
  1460. ElMessage.success('更新成功')
  1461. dialogZBVisible.value = false
  1462. setTimeout(() => {
  1463. initData()
  1464. }, 1000);
  1465. }
  1466. }
  1467. const handleZBSubmit = async (formEl: FormInstance | undefined) => {
  1468. if (!formEl) return
  1469. await formEl.validate((valid) => {
  1470. if (valid) {
  1471. let formData = {
  1472. c_project_code:ruleZBForm.c_project_code,
  1473. c_project_name:ruleZBForm.c_project_name,
  1474. c_budget_amount:ruleZBForm.c_budget_amount,
  1475. c_bid_open_time:ruleZBForm.c_bid_open_time,
  1476. c_bid_deadline:ruleZBForm.c_bid_open_time,
  1477. c_source_name:ruleZBForm.c_source_name,
  1478. c_author:ruleZBForm.c_author,
  1479. c_publish_time:ruleZBForm.c_publish_time,
  1480. c_detail_url:ruleZBForm.c_detail_url,
  1481. }
  1482. updateZBProject(tempTask.value,formData,ruleZBForm.c_project_code)
  1483. getProjectParams(projects.value.id,'c_gl_xm',CONFIG.COLUMNIDARR[5]).then(async res => {
  1484. const reslust = (res as any)[0]
  1485. const content = {
  1486. id: reslust.id,
  1487. c_complete: true,
  1488. c_enable: false
  1489. }
  1490. updateProjectParams(CONFIG.COLUMNIDARR[5],CONFIG.MODELIDARR[2], content)
  1491. })
  1492. dialogZBVisible.value = false
  1493. } else {
  1494. // ElMessage.error('请填写完整信息')
  1495. // dialogZBVisible.value = false
  1496. }
  1497. })
  1498. }
  1499. // const handleZBJKSubmit = async (formEl: FormInstance | undefined) => {
  1500. // if (!formEl) return
  1501. // await formEl.validate(async (valid) => {
  1502. // if (valid) {
  1503. // ruleZBJKForm.c_project_code = projects.value.xmbh
  1504. // ruleZBJKForm.c_gl_xm = projects.value.id
  1505. // const res = await addContentParams(ruleZBJKForm,CONFIG.COLUMNIDARR[5],CONFIG.MODELIDARR[2])
  1506. // if((res as any).code === 200){
  1507. // // updateTypeProject(2)
  1508. // }
  1509. // } else {
  1510. // }
  1511. // dialogZBJKVisible.value = false
  1512. // })
  1513. // }
  1514. const handleKBJKSubmit = async (formEl: FormInstance | undefined) => {
  1515. if (!formEl) return
  1516. await formEl.validate(async (valid) => {
  1517. if (valid) {
  1518. ruleKBJKForm.c_glxm = projects.value.xmbh
  1519. ruleKBJKForm.c_xmmc = projects.value.c_xmbt
  1520. ruleKBJKForm.c_zbbh = projects.value.zbxmbh
  1521. ruleKBJKForm.c_kbsj = moment(projects.value.zbkssj).format('YYYY-MM-DD HH:mm')
  1522. ruleKBJKForm.title = projects.value.c_xmbt
  1523. ruleKBJKForm.content = projects.value.content
  1524. ruleKBJKForm.c_enable = true
  1525. const res = await addContentParams(ruleKBJKForm,CONFIG.COLUMNIDARR[6],CONFIG.MODELIDARR[3])
  1526. projects.value.rwlcqd.find((p: any) => p.id == 5).done = true
  1527. projects.value.rwlcqd.find((p: any) => p.id == 5).create_time = moment().format('YYYY-MM-DD HH:mm')
  1528. projects.value.rwlcqd.find((p: any) => p.id == 5).username = userName
  1529. projects.value.rwlcqd.find((p: any) => p.id == 5).userid = userInfo?.id || ''
  1530. projects.value.rwlcqd.find((p: any) => p.id == 5).type = 0
  1531. let xmjd_json = projects.value.c_xmjd_json;
  1532. xmjd_json[xmjd_json.length-1].create_time = moment().format('YYYY-MM-DD HH:mm')
  1533. xmjd_json[xmjd_json.length-1].username = userName
  1534. xmjd_json[xmjd_json.length-1].userid = userInfo?.id || ''
  1535. xmjd_json[xmjd_json.length-1].type = 0
  1536. let str = {xmjd: (stage.value+1), type: 1, create_time: '', username: '', userid: ''}
  1537. xmjd_json.push(str)
  1538. let constParams = {
  1539. id: projects.value.id,
  1540. rwlcqd:projects.value.rwlcqd,
  1541. c_xmjd_json: xmjd_json,
  1542. c_xgjk:(res as any).content,
  1543. tbrq:ruleKBJKForm.c_zbtjr,
  1544. tbbj:ruleKBJKForm.c_tbbj,
  1545. }
  1546. updateTXBProject(CONFIG.COLUMNIDARR[2],CONFIG.MODELIDARR[0],constParams)
  1547. dialogKBJKVisible.value = false
  1548. } else {
  1549. }
  1550. })
  1551. }
  1552. const handleBidSubmit = async (formEl: FormInstance | undefined) => {
  1553. if (!formEl) return
  1554. await formEl.validate((valid) => {
  1555. if (valid) {
  1556. // ruleBidForm.file3.file.forEach((item: any) => {
  1557. // submitFileForm(item.raw)
  1558. // })
  1559. const loading = ElLoading.service({
  1560. lock: true,
  1561. text: '上传中...',
  1562. background: 'rgba(0, 0, 0, 0.7)',
  1563. })
  1564. updateTypeProject(2)
  1565. if(ruleBidForm.file3.name){//做澄清文件
  1566. submitFileForm(ruleBidForm.file3.raw)
  1567. }
  1568. updateFIleProject(loading)//更新项目文件
  1569. // setTimeout(() => {
  1570. // dialogBidVisible.value = false
  1571. // loading.close()
  1572. // ElMessage.success('上传成功')
  1573. // }, 2000);
  1574. } else {
  1575. }
  1576. })
  1577. }
  1578. const handleOpenViewClick = (type:any) => {
  1579. if(type == '0'){
  1580. window.open(finishBidObject.value.c_detail_url)
  1581. }else if(type == '1'){
  1582. window.open(projects.value.zbmsxx.zb_detail_url)
  1583. }
  1584. }
  1585. const handleBidFileSubmit = () => {
  1586. dialogBidFileVisible.value = false
  1587. updaterwlcqdProject(tempTask.value)
  1588. }
  1589. const dialogFormFileVisible = ref(false)
  1590. const dialogFormBidFileVisible = ref(false)
  1591. const FileForm = ref<RuleForm>({})
  1592. const fileList = ref([])
  1593. const handleCQFileChange = (file: any) => {
  1594. FileForm.value.name = file.name
  1595. FileForm.value.file = file
  1596. }
  1597. const BidFileForm = ref<RuleForm>({})
  1598. const bidFileList = ref([])
  1599. const handleBidFileChange = (file: any) => {
  1600. BidFileForm.value.name = file.name
  1601. BidFileForm.value.file = file
  1602. }
  1603. const submitBidFileForm = () => {
  1604. console.log(BidFileForm.value)
  1605. const columnId = CONFIG.COLUMNIDARR[0]
  1606. const params: any = {
  1607. columnId: columnId,
  1608. contentId: soureProjects.value.id,
  1609. paraName: 'c_history_tender',
  1610. type: 3,
  1611. file: BidFileForm.value.file.raw
  1612. }
  1613. api.uploadFile(params).then((res: any) => {
  1614. if((res as any).code === 200){
  1615. const content = res.content
  1616. const params: any = {
  1617. columnId: CONFIG.COLUMNIDARR[0],
  1618. modelId: CONFIG.MODELIDARR[7],
  1619. content: JSON.stringify({
  1620. id: soureProjects.value.id,
  1621. c_history_tender: content,
  1622. }),
  1623. }
  1624. api.updateContent(params).then((res: any) => {
  1625. if((res as any).code === 200){
  1626. dialogFormBidFileVisible.value = false
  1627. bidFile.value.isFileFlag = true
  1628. sessionStorage.setItem('isFileFlag', 'true')
  1629. }
  1630. })
  1631. }else{
  1632. ElMessage.error('上传失败')
  1633. }
  1634. })
  1635. }
  1636. const submitFileForm = (file:any) => {
  1637. const columnId = CONFIG.COLUMNIDARR[10]
  1638. const modelId = CONFIG.MODELIDARR[5]
  1639. const params: any = {
  1640. columnId: columnId,
  1641. contentId: 'content',
  1642. paraName: 'c_cqgg',
  1643. type: 3,
  1644. file: file
  1645. }
  1646. api.uploadFile(params).then((res: any) => {
  1647. if((res as any).code === 200){
  1648. // ElMessage.success('上传成功')
  1649. const path = (res as any).content
  1650. getProjectParams(projects.value.id,'c_gltxbid',columnId).then(async res2 => {
  1651. let arr = []
  1652. if((res2 as any).length > 0){
  1653. const reslust = (res2 as any)[0]
  1654. arr = await JSON.parse(reslust.c_cqgg)
  1655. arr.push({
  1656. path: path,
  1657. name: file.name,
  1658. create_time: moment().format('YYYY-MM-DD HH:mm'),
  1659. author: userName,
  1660. })
  1661. const params: any = {
  1662. columnId: columnId,
  1663. modelId: modelId,
  1664. content: JSON.stringify({
  1665. id: reslust.id,
  1666. c_cqgg: arr,
  1667. }),
  1668. }
  1669. api.updateContent(params)
  1670. }else{
  1671. arr.push({
  1672. path: path,
  1673. name: file.name,
  1674. create_time: moment().format('YYYY-MM-DD HH:mm'),
  1675. author: userName,
  1676. })
  1677. let formData = {
  1678. c_gltxbid: projects.value.id,
  1679. title: file.name,
  1680. content: file.name,
  1681. c_cqgg: JSON.stringify(arr),
  1682. }
  1683. const params = {
  1684. content: JSON.stringify(formData),
  1685. columnId: columnId,
  1686. modelId: modelId,
  1687. }
  1688. api.addContent(params)
  1689. }
  1690. })
  1691. }
  1692. })
  1693. dialogFormFileVisible.value = false
  1694. }
  1695. //获取数据
  1696. const getProjectParams = async (id: string,type: string,columnId: number) => {
  1697. const params: any = {
  1698. columnId: columnId,
  1699. states: '0,1,2,3',
  1700. pageSize: 1,
  1701. page: 0
  1702. }
  1703. if (id !== "") {
  1704. params.search = JSON.stringify([
  1705. ...(params.search ? JSON.parse(params.search) : []),
  1706. {
  1707. field: type,
  1708. searchType: 2,
  1709. content: { value: `%${id}%` },
  1710. },
  1711. ]);
  1712. }
  1713. const res = await getDmsDataList(params)
  1714. if ((res as any).code === 200) {
  1715. return (res as any).content.data
  1716. }else{
  1717. return []
  1718. }
  1719. }
  1720. // const filename = ref('')
  1721. const handleFileChange = (file: any) => {
  1722. ruleBidForm.file1 = file
  1723. }
  1724. const handleFileChange2 = (file: any) => {
  1725. ruleBidForm.file2 = file
  1726. }
  1727. const handleFileChange3 = (file: any) => {
  1728. // filename.value = file.raw.name
  1729. ruleBidForm.file3 = file
  1730. }
  1731. // const handleFileChange3 = (file: any,fileList:any) => {
  1732. // filename.value = file.raw.name
  1733. // ruleBidForm.file3.file = fileList
  1734. // }
  1735. // const handleFileRemove3 = (file: any,fileList:any) => {
  1736. // filename.value = file.raw.name
  1737. // ruleBidForm.file3.file = fileList
  1738. // }
  1739. // 更新项目进度状态
  1740. const updateTypeProject = async (type:any) => {
  1741. let xmjd_json = projects.value.c_xmjd_json;
  1742. stepsType.value = type
  1743. xmjd_json[xmjd_json.length-1].type = type
  1744. const params: any = {
  1745. columnId: CONFIG.COLUMNIDARR[2],
  1746. modelId: CONFIG.MODELIDARR[0],
  1747. content: JSON.stringify({
  1748. id: projects.value.id,
  1749. c_xmjd_json: xmjd_json
  1750. }),
  1751. }
  1752. const res = await api.updateContent(params)
  1753. if ((res as any).code === 200) {
  1754. }
  1755. }
  1756. // 更新项目文件
  1757. const updateFIleProject = async (loading:any) => {
  1758. let param = {
  1759. columnId: CONFIG.COLUMNIDARR[2],
  1760. contentId: projects.value.id,
  1761. paraName: '',
  1762. type: 3,
  1763. file: ''
  1764. }
  1765. const [file1, file2, file3] = await Promise.all([
  1766. ruleBidForm.file1.name ? getFIlePath({ ...param, paraName: ruleBidForm.file1.name, file: ruleBidForm.file1.raw }) : '',
  1767. ruleBidForm.file2.name ? getFIlePath({ ...param, paraName: ruleBidForm.file2.name, file: ruleBidForm.file2.raw }) : '',
  1768. ruleBidForm.file3.name ? getFIlePath({ ...param, paraName: ruleBidForm.file3.name, file: ruleBidForm.file3.raw }) : '',
  1769. ])
  1770. const params: any = {
  1771. columnId: CONFIG.COLUMNIDARR[2],
  1772. modelId: CONFIG.MODELIDARR[0],
  1773. content: JSON.stringify({
  1774. id: projects.value.id,
  1775. c_zbwj: file1,
  1776. c_cgxqwj: file2,
  1777. c_cqggwj: file3,
  1778. }),
  1779. }
  1780. const res = await api.updateContent(params)
  1781. dialogBidVisible.value = false
  1782. loading.close()
  1783. if ((res as any).code === 200) {
  1784. ElMessage.success('上传成功')
  1785. }else{
  1786. ElMessage.error('上传失败')
  1787. }
  1788. }
  1789. const getFIlePath = async (params: any) => {
  1790. const res = await api.uploadFile(params)
  1791. if ((res as any).code === 200) {
  1792. return (res as any).content
  1793. } else {
  1794. return null
  1795. }
  1796. }
  1797. const bidFile = ref({
  1798. flag: sessionStorage.getItem('bidFileFlag') == 'true' ? true : false, //是否获取到标书
  1799. swb:false,
  1800. jsb:false,
  1801. isFileFlag: sessionStorage.getItem('isFileFlag') == 'true' ? true : false, //是否历史标书
  1802. })
  1803. const getBidFile = async () => {
  1804. const loading = ElLoading.service({
  1805. lock: true,
  1806. text: '获取中...',
  1807. background: 'rgba(0, 0, 0, 0.7)',
  1808. })
  1809. setTimeout(() => {
  1810. // const fileName = `杨浦区图书馆总馆物业管理服务项目投标文件.docx`
  1811. // UTIL.downloadFile(fileName, filePath)
  1812. const aiscbs = projects.value.c_ai_scbs || "";
  1813. // let aiscbs = "/data/model/shenqin_data/57f7bf655e994fa8a8867e4bc55e8709/1779-杨浦区图书馆总馆物业管理服务项目招标文件.pdf-ca9d5daf-443b-44c6-a1f3-bf43890a55d8.docx"
  1814. if(aiscbs){
  1815. let filename = aiscbs.split('/').pop() || ''
  1816. filename = filename.replace(/\?.*$/, '')
  1817. if(filename.includes('.docx')){
  1818. UTIL.downloadFile(filename, CONFIG.FILEPATH + (aiscbs.replace("/data/model","")))
  1819. loading.close()
  1820. ElMessage.success('获取标书成功')
  1821. bidFile.value.flag = true
  1822. sessionStorage.setItem('bidFileFlag', "true")
  1823. }else{
  1824. sessionStorage.setItem('bidFileFlag', "false")
  1825. ElMessage.error('暂无标书文件')
  1826. }
  1827. }else{
  1828. loading.close()
  1829. // bidFile.value.flag = true
  1830. ElMessage.error('获取标书失败')
  1831. }
  1832. }, 3000);
  1833. // if(bittype == 'swb'){
  1834. // ElMessage.success('获取商务标书成功')
  1835. // bidFile.value.swb = true
  1836. // }else if(bittype == 'jsb'){
  1837. // ElMessage.success('获取技术标书成功')
  1838. // bidFile.value.jsb = true
  1839. // }
  1840. }
  1841. const handleChange = (val: any) => {
  1842. finishBidObject.value = val
  1843. }
  1844. const handleSwitchChange = (val: any) => {
  1845. finishBidObjectTemp.value.flag = val
  1846. finishBidObjectTemp.value.url = ''
  1847. }
  1848. const handleFinishBidSubmit = async () => {
  1849. dialogFinishBidVisible.value = false
  1850. dialogFinishBidVisible2.value = false
  1851. let str = {id: '7'} // 修改
  1852. updaterwlcqdProject(str)
  1853. // 修改中标监控
  1854. updateJKLCProject()
  1855. }
  1856. const updateJKLCProject = async () => {
  1857. // 修改中标监控
  1858. const steps = [
  1859. { n: 1, label: '预警触发'},
  1860. { n: 2, label: '投/续标准备'},
  1861. { n: 3, label: '招标监控'},
  1862. { n: 4, label: '标书制作'},
  1863. { n: 5, label: '投标提交'},
  1864. { n: 6, label: '开标监控'},
  1865. { n: 7, label: '开标监控完成,已中标'},
  1866. ]
  1867. let xmjd_json = projects.value.c_xmjd_json
  1868. let json = xmjd_json.map((item:any) => {
  1869. return {
  1870. create_time: item.create_time ? item.create_time : moment().format('YYYY-MM-DD HH:mm'),
  1871. desc:steps.find((s:any) => s.n == item.xmjd)?.label || '',
  1872. username:item.username ? item.username : userName,
  1873. userid:item.userid ? item.userid : userInfo?.id || '',
  1874. }
  1875. })
  1876. const content = {
  1877. id: projects.value.c_xgjk,
  1878. c_gljg:finishBidObject.value?.id || '',
  1879. c_jgjk_url: finishBidObjectTemp.value.url ? finishBidObjectTemp.value.url : finishBidObject.value?.c_detail_url || '',
  1880. c_lcsjx:json || '',
  1881. c_sfzb:true,
  1882. c_type:"投续标",
  1883. c_enable: false
  1884. }
  1885. updateProjectParams(CONFIG.COLUMNIDARR[6],CONFIG.MODELIDARR[3], content)
  1886. }
  1887. // 更新数据
  1888. const updateProjectParams = async (columnId:any,modelId:any,content:any) => {
  1889. const params: any = {
  1890. columnId: columnId,
  1891. modelId: modelId,
  1892. content: JSON.stringify(content),
  1893. }
  1894. const res = await api.updateContent(params)
  1895. if ((res as any).code === 200) {
  1896. // initData()
  1897. }
  1898. }
  1899. </script>
  1900. <style scoped>
  1901. .expire-detail-page {
  1902. width: 100%;
  1903. min-height: 100vh;
  1904. background: var(--bg);
  1905. }
  1906. th, td {
  1907. text-align: left;
  1908. padding: 10px 12px;
  1909. border-bottom: 1px solid #e8ecef;
  1910. }
  1911. /* 顶部绿色头部 */
  1912. .header-bar {
  1913. display: flex;
  1914. justify-content: space-between;
  1915. align-items: center;
  1916. background: linear-gradient(90deg, #0d9488 0%, #0f766e 100%);
  1917. padding: 20px 24px;
  1918. color: #fff;
  1919. border-radius: 8px;
  1920. }
  1921. .header-info {
  1922. display: flex;
  1923. flex-direction: column;
  1924. gap: 4px;
  1925. }
  1926. .project-title {
  1927. display: flex;
  1928. align-items: center;
  1929. gap: 8px;
  1930. font-size: 18px;
  1931. font-weight: 600;
  1932. }
  1933. .title-icon {
  1934. display: flex;
  1935. align-items: center;
  1936. }
  1937. .project-subtitle {
  1938. font-size: 14px;
  1939. opacity: 0.9;
  1940. }
  1941. .project-meta {
  1942. font-size: 12px;
  1943. opacity: 0.8;
  1944. }
  1945. .countdown-badge {
  1946. display: flex;
  1947. flex-direction: column;
  1948. align-items: center;
  1949. background: rgba(255, 255, 255, 0.15);
  1950. padding: 8px 16px;
  1951. border-radius: 6px;
  1952. min-width: 70px;
  1953. }
  1954. .countdown-number {
  1955. font-size: 28px;
  1956. font-weight: bold;
  1957. line-height: 1;
  1958. }
  1959. .countdown-label {
  1960. font-size: 10px;
  1961. opacity: 0.9;
  1962. }
  1963. .countdown-unit {
  1964. font-size: 10px;
  1965. opacity: 0.8;
  1966. }
  1967. /* 进度指示器 */
  1968. .progress-bar {
  1969. margin: 20px 0px;
  1970. background: #fff;
  1971. border-radius: 8px;
  1972. border: 1px solid var(--border);
  1973. }
  1974. .progress-container {
  1975. display: flex;
  1976. align-items: center;
  1977. justify-content: flex-start;
  1978. padding: 12px 16px;
  1979. }
  1980. .arrow {
  1981. font-size: 12px;
  1982. color: #8a9bab;
  1983. padding: 0px 10px;
  1984. }
  1985. .progress-step {
  1986. display: flex;
  1987. flex-direction: row;
  1988. align-items: center;
  1989. gap: 4px;
  1990. }
  1991. .step-circle {
  1992. width: 20px;
  1993. height: 20px;
  1994. border-radius: 50%;
  1995. background: #e8ecef;
  1996. display: flex;
  1997. align-items: center;
  1998. justify-content: center;
  1999. font-size: 10px;
  2000. color: var(--text3);
  2001. }
  2002. .progress-step.completed .step-circle {
  2003. background: var(--primary);
  2004. color: #fff;
  2005. }
  2006. .progress-step.active .step-circle {
  2007. background: #fff;
  2008. border: 2px solid var(--primary);
  2009. }
  2010. .active-circle {
  2011. background: #fff !important;
  2012. border: 2px solid var(--primary) !important;
  2013. }
  2014. .step-label {
  2015. font-size: 11px;
  2016. color: var(--text3);
  2017. text-align: center;
  2018. }
  2019. .progress-step.completed .step-label,
  2020. .progress-step.active .step-label {
  2021. color: var(--text);
  2022. font-weight: 500;
  2023. }
  2024. .progress-line {
  2025. flex: 1;
  2026. height: 2px;
  2027. background: #e8ecef;
  2028. margin: 0 8px;
  2029. min-width: 40px;
  2030. }
  2031. .progress-line.completed {
  2032. background: var(--primary);
  2033. }
  2034. /* 主内容区域 */
  2035. .main-content {
  2036. display: flex;
  2037. gap: 20px;
  2038. }
  2039. .left-panel {
  2040. flex: 1;
  2041. display: flex;
  2042. flex-direction: column;
  2043. gap: 20px;
  2044. }
  2045. .right-panel {
  2046. width: 320px;
  2047. display: flex;
  2048. flex-direction: column;
  2049. gap: 20px;
  2050. }
  2051. /* 面板卡片 */
  2052. .panel-card {
  2053. background: #fff;
  2054. width: 100%;
  2055. border-radius: 8px;
  2056. border: 1px solid var(--border);
  2057. overflow: hidden;
  2058. }
  2059. .panel-card.monitoring-card {
  2060. background: linear-gradient(135deg, #ecfdf5 0%, #f0fdf4 100%);
  2061. border-color: var(--primary);
  2062. }
  2063. .card-header {
  2064. padding: 12px 16px;
  2065. border-bottom: 1px solid var(--border);
  2066. /* background: #fafafa; */
  2067. }
  2068. .panel-card.monitoring-card .card-header {
  2069. background: transparent;
  2070. border-bottom: 1px solid rgba(13, 148, 136, 0.1);
  2071. }
  2072. .card-title {
  2073. font-size: 14px;
  2074. font-weight: 600;
  2075. color: var(--text);
  2076. }
  2077. /* 监控区域 */
  2078. .monitoring-name {
  2079. font-size: 16px;
  2080. font-weight: bold;
  2081. color: var(--text);
  2082. margin-bottom: 8px;
  2083. }
  2084. .monitoring-desc {
  2085. font-size: 13px;
  2086. color: var(--text2);
  2087. margin-bottom: 12px;
  2088. }
  2089. .monitoring-actions {
  2090. display: flex;
  2091. gap: 8px;
  2092. }
  2093. /* 历史投标表格 */
  2094. .history-table {
  2095. width: 100%;
  2096. border-collapse: collapse;
  2097. }
  2098. .history-table thead th {
  2099. padding: 8px 0;
  2100. border-bottom: 1px solid var(--border);
  2101. font-size: 12px;
  2102. color: var(--text3);
  2103. text-align: left;
  2104. font-weight: normal;
  2105. }
  2106. .history-table thead th:last-child {
  2107. text-align: right;
  2108. }
  2109. .history-table tbody tr {
  2110. border-bottom: 1px dashed #f0f0f0;
  2111. }
  2112. .history-table tbody tr:last-child {
  2113. border-bottom: none;
  2114. }
  2115. .history-table tbody td {
  2116. padding: 10px 0;
  2117. font-size: 13px;
  2118. }
  2119. .history-table tbody td:last-child {
  2120. text-align: right;
  2121. }
  2122. .table-header {
  2123. display: flex;
  2124. padding: 8px 0;
  2125. border-bottom: 1px solid var(--border);
  2126. font-size: 12px;
  2127. color: var(--text3);
  2128. }
  2129. .table-header span {
  2130. flex: 1;
  2131. }
  2132. .table-header span:last-child {
  2133. text-align: right;
  2134. }
  2135. .table-row {
  2136. display: flex;
  2137. padding: 10px 0;
  2138. border-bottom: 1px dashed #f0f0f0;
  2139. font-size: 13px;
  2140. }
  2141. .table-row:last-child {
  2142. border-bottom: none;
  2143. }
  2144. .table-row span {
  2145. flex: 1;
  2146. }
  2147. .table-row span:last-child {
  2148. text-align: right;
  2149. }
  2150. .result-badge {
  2151. font-size: 11px;
  2152. padding: 2px 8px;
  2153. border-radius: 4px;
  2154. }
  2155. .result-badge.success {
  2156. background: var(--success-soft);
  2157. color: var(--success);
  2158. }
  2159. .link-btn {
  2160. font-size: 12px;
  2161. color: var(--primary);
  2162. text-decoration: none;
  2163. }
  2164. .link-btn:hover {
  2165. text-decoration: underline;
  2166. }
  2167. /* 侧边栏卡片 */
  2168. .side-card {
  2169. background: #fff;
  2170. border-radius: 8px;
  2171. border: 1px solid var(--border);
  2172. overflow: hidden;
  2173. }
  2174. .card-body.highlight-card {
  2175. border: 1px solid #fbbf24;
  2176. background: #fffbeb;
  2177. margin: 16px;
  2178. border-radius: 8px;
  2179. }
  2180. .info-row {
  2181. display: flex;
  2182. justify-content: space-between;
  2183. padding: 6px 0;
  2184. /* border-bottom: 1px dashed #f0f0f0; */
  2185. }
  2186. .info-row:last-child {
  2187. border-bottom: none;
  2188. }
  2189. .label {
  2190. font-size: 12px;
  2191. color: var(--text3);
  2192. }
  2193. .value {
  2194. font-size: 12px;
  2195. color: var(--text);
  2196. font-weight: 500;
  2197. }
  2198. /* 招标动态 */
  2199. .dynamic-item {
  2200. margin-bottom: 0px;
  2201. }
  2202. .dynamic-badge {
  2203. display: inline-block;
  2204. font-size: 10px;
  2205. padding: 2px 6px;
  2206. background: #fbbf24;
  2207. color: #92400e;
  2208. border-radius: 4px;
  2209. margin-bottom: 8px;
  2210. }
  2211. .dynamic-item p {
  2212. font-size: 13px;
  2213. color: var(--text);
  2214. margin-bottom: 4px;
  2215. }
  2216. .dynamic-date {
  2217. font-size: 11px;
  2218. color: var(--text3);
  2219. }
  2220. .dynamic-actions {
  2221. display: flex;
  2222. gap: 8px;
  2223. }
  2224. /* 关联模块 */
  2225. .module-links {
  2226. display: flex;
  2227. flex-direction: column;
  2228. gap: 8px;
  2229. }
  2230. .module-link {
  2231. font-size: 13px;
  2232. color: var(--primary);
  2233. text-decoration: none;
  2234. padding: 4px 0;
  2235. }
  2236. .module-link:hover {
  2237. text-decoration: underline;
  2238. }
  2239. /* 进程记录 */
  2240. .process-list {
  2241. display: flex;
  2242. flex-direction: column;
  2243. gap: 8px;
  2244. }
  2245. .process-item {
  2246. display: flex;
  2247. align-items: center;
  2248. gap: 8px;
  2249. }
  2250. .process-dot {
  2251. width: 6px;
  2252. height: 6px;
  2253. border-radius: 50%;
  2254. background: var(--primary);
  2255. }
  2256. .process-text {
  2257. font-size: 12px;
  2258. color: var(--text2);
  2259. }
  2260. .renewal-stepper {
  2261. display: flex; align-items: center; gap: 0; margin: 20px 0px; overflow-x: auto;
  2262. padding: 12px 16px; background: #fff; border: 1px solid var(--border); border-radius: var(--radius);
  2263. }
  2264. .renewal-step {
  2265. display: flex; align-items: center; flex-shrink: 0; font-size: 12px; color: var(--text3);
  2266. }
  2267. .renewal-step-dot {
  2268. width: 28px; height: 28px; border-radius: 50%; background: #e8ecef; color: var(--text3);
  2269. display: flex; align-items: center; justify-content: center; font-weight: 600; font-size: 11px;
  2270. margin-right: 6px; flex-shrink: 0;
  2271. }
  2272. .renewal-step.done .renewal-step-dot {
  2273. background: var(--success); color: #fff;
  2274. }
  2275. .renewal-step.active .renewal-step-dot {
  2276. background: var(--primary);
  2277. color: #fff;
  2278. box-shadow: 0 0 0 3px var(--primary-soft);
  2279. }
  2280. .renewal-step.active {
  2281. color: var(--primary); font-weight: 600;
  2282. }
  2283. /* 投标任务清单 */
  2284. .checklist {
  2285. list-style: none;
  2286. padding: 0;
  2287. margin: 0;
  2288. }
  2289. .checklist-item {
  2290. display: flex;
  2291. align-items: center;
  2292. justify-content: space-between;
  2293. padding: 10px 0;
  2294. border-bottom: 1px dashed #f0f0f0;
  2295. }
  2296. .checklist-item:last-child {
  2297. border-bottom: none;
  2298. }
  2299. .checklist-status {
  2300. margin-right: 8px;
  2301. font-size: 14px;
  2302. }
  2303. .checklist-status.done {
  2304. text-decoration: line-through;
  2305. color: var(--text3);
  2306. }
  2307. .checklist-text {
  2308. flex: 1;
  2309. font-size: 13px;
  2310. }
  2311. .checklist-actions {
  2312. display: flex;
  2313. gap: 8px;
  2314. }
  2315. .renewal-step-arrow {
  2316. color: var(--border);
  2317. margin: 0 8px;
  2318. font-size: 14px;
  2319. }
  2320. @media (max-width: 768px) {
  2321. .main-content {
  2322. flex-direction: column;
  2323. }
  2324. .right-panel {
  2325. width: 100%;
  2326. }
  2327. .progress-indicator {
  2328. flex-wrap: wrap;
  2329. }
  2330. .progress-step {
  2331. min-width: 60px;
  2332. }
  2333. }
  2334. .drawer-content {
  2335. padding: 16px;
  2336. padding-top: 0px;
  2337. }
  2338. .pool-row-meta {
  2339. font-size: 12px;
  2340. color: #8a9bab;
  2341. display: flex;
  2342. flex-wrap: wrap;
  2343. gap: 12px;
  2344. }
  2345. .section {
  2346. margin-bottom: 10px;
  2347. }
  2348. .info {
  2349. display: grid;
  2350. grid-template-columns: 100px 1fr;
  2351. gap: 8px 12px;
  2352. font-size: 13px;
  2353. }
  2354. .info dt {
  2355. color: var(--text3);
  2356. }
  2357. .score-badge {
  2358. font-size: 11px;
  2359. padding: 2px 8px;
  2360. border-radius: 10px;
  2361. font-weight: 600;
  2362. background: var(--info-soft);
  2363. color: var(--info);
  2364. }
  2365. .score-badge.high {
  2366. background: var(--danger-soft);
  2367. color: var(--danger);
  2368. }
  2369. .section-title {
  2370. font-size: 14px;
  2371. font-weight: 600;
  2372. color: var(--text2);
  2373. margin-bottom: 12px;
  2374. padding-left: 8px;
  2375. border-left: 3px solid var(--primary);
  2376. }
  2377. .link-list {
  2378. background: #f8fafc;
  2379. border-radius: 8px;
  2380. padding: 12px;
  2381. border: 1px solid #e8ecef;
  2382. }
  2383. .link-item {
  2384. display: flex;
  2385. align-items: center;
  2386. gap: 8px;
  2387. /* padding: 6px 0; */
  2388. }
  2389. .link-icon {
  2390. font-size: 14px;
  2391. }
  2392. .link-text {
  2393. font-size: 12px;
  2394. color: var(--primary);
  2395. }
  2396. .link-url {
  2397. font-size: 12px;
  2398. color: #3b82f6;
  2399. word-break: break-all;
  2400. }
  2401. .info-grid {
  2402. display: grid;
  2403. grid-template-columns: 1fr 1fr;
  2404. gap: 10px;
  2405. }
  2406. .info-row {
  2407. display: flex;
  2408. flex-direction: column;
  2409. gap: 4px;
  2410. }
  2411. .info-label {
  2412. font-size: 12px;
  2413. color: #8a9bab;
  2414. }
  2415. .info-value {
  2416. font-size: 13px;
  2417. color: #1a2b3c;
  2418. }
  2419. .score-tag {
  2420. background: #fee2e2;
  2421. color: #dc2626;
  2422. padding: 2px 8px;
  2423. border-radius: 12px;
  2424. font-size: 12px;
  2425. font-weight: 600;
  2426. display: inline-block;
  2427. }
  2428. .summary-content {
  2429. /* background: #f8fafc; */
  2430. border-radius: 8px;
  2431. /* padding: 12px; */
  2432. font-size: 13px;
  2433. line-height: 1.6;
  2434. color: #1a2b3c;
  2435. }
  2436. .summary-content p {
  2437. margin: 0 0 8px 0;
  2438. }
  2439. .summary-content p:last-child {
  2440. margin-bottom: 0;
  2441. }
  2442. .summary-content strong {
  2443. color: #5c6f82;
  2444. }
  2445. .eval-item {
  2446. display: flex;
  2447. align-items: center;
  2448. gap: 8px;
  2449. padding: 6px 0;
  2450. }
  2451. .eval-dot {
  2452. width: 8px;
  2453. height: 8px;
  2454. border-radius: 50%;
  2455. background: #f59e0b;
  2456. }
  2457. .eval-dot.success {
  2458. background: #10b981;
  2459. }
  2460. .eval-text {
  2461. font-size: 13px;
  2462. color: #1a2b3c;
  2463. }
  2464. .action-buttons {
  2465. display: flex;
  2466. gap: 10px;
  2467. margin-bottom: 12px;
  2468. }
  2469. .secondary-buttons {
  2470. display: flex;
  2471. gap: 10px;
  2472. }
  2473. </style>