综合办公系统
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

newBudgetInfo.vue 42KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003
  1. <!--
  2. * @Author: ysh
  3. * @Date: 2025-05-07 11:01:39
  4. * @LastEditors: wrh
  5. * @LastEditTime: 2025-12-31 16:22:00
  6. -->
  7. <template>
  8. <div class="main" v-loading="loading">
  9. <h2 class="text-center">项目直接生产成本预算表</h2>
  10. <p style="text-align: center">编制人:{{ budgetForm.compilerUser ? budgetForm.compilerUser.nickName : '' }} | 编制时间:{{ budgetForm.createTime }} </p>
  11. <el-divider></el-divider>
  12. <div class="mt20 mb20" v-if="showAlter && taskName">
  13. <el-alert title="任务被退回,请修改后重新提交" type="error" :closable="false">
  14. <return-comment :taskForm="taskForm" @isReturn="isReturn"></return-comment>
  15. </el-alert>
  16. </div>
  17. <el-descriptions :column="3" border class="descriptions">
  18. <el-descriptions-item label="项目编号">
  19. {{ project.projectNumber }}
  20. </el-descriptions-item>
  21. <el-descriptions-item label="项目名称">
  22. {{ project.projectName }}
  23. </el-descriptions-item>
  24. <el-descriptions-item label="项目负责人">
  25. {{ getUserName(project.projectLeader) }}
  26. </el-descriptions-item>
  27. <el-descriptions-item label="承担部门">
  28. {{ getDeptNames(project.undertakingDept) }}
  29. </el-descriptions-item>
  30. <el-descriptions-item label="项目备注" :span="3">
  31. {{ project.remark }}
  32. </el-descriptions-item>
  33. <el-descriptions-item label="预算表单备注" :span="5">
  34. {{ budgetForm.remark }}
  35. </el-descriptions-item>
  36. <el-descriptions-item label="预算附件" :span="5" v-if="budgetForm.document">
  37. <el-link type="primary" @click="reviewWord(`${baseUrl}${'/profile/upload' + budgetForm.document}`)">
  38. {{ getFileName(budgetForm.document) }}
  39. </el-link>
  40. <el-link class="ml20" type="warning" :href="`${baseUrl}${'/profile/upload' + budgetForm.document}`"
  41. :underline="false" target="_blank">
  42. <span class="el-icon-download">下载文件</span>
  43. </el-link>
  44. </el-descriptions-item>
  45. <el-descriptions-item label="项目计划工作量" :span="3">
  46. <div>
  47. <table border="1" style="width: 100%;">
  48. <tr style="background-color:#f8f8f9">
  49. <td style="width: 250px">工作内容</td>
  50. <td style="width: 100px">比例尺/等级</td>
  51. <td style="width: 100px">单位</td>
  52. <td style="width: 100px">工作量</td>
  53. <td style="width: 100px">要求完成时间</td>
  54. <td style="width: 200px">备注</td>
  55. </tr>
  56. <tr v-for="(work, index) in workContentList" :key="index">
  57. <td>{{ work.content }}</td>
  58. <td>{{ work.scale }} </td>
  59. <td>{{ work.unit }}</td>
  60. <td>{{ work.workload }}</td>
  61. <td>{{ work.deadline }}</td>
  62. <td class="remark-cell">{{ work.remark }}
  63. </td>
  64. </tr>
  65. </table>
  66. </div>
  67. </el-descriptions-item>
  68. <el-descriptions-item label="内业绩效额" :span="3">
  69. <table border="1" style="width:100%">
  70. <tr style="background-color:#f8f8f9" v-if="workList.length != 0">
  71. <td style="min-width:50px">工作简述</td>
  72. <td style="min-width:50px">工作内容</td>
  73. <td style="min-width:50px">内容细项</td>
  74. <td style="min-width:50px">比例尺/等级</td>
  75. <td style="min-width:50px">数量</td>
  76. <td style="min-width:50px">单价</td>
  77. <td style="min-width:50px">单位</td>
  78. <td style="min-width:50px">系数</td>
  79. <td style="min-width:80px">金额</td>
  80. <td style="min-width: 150px;">备注</td>
  81. </tr>
  82. <tr v-for="work in workList">
  83. <td>{{ work.content }}</td>
  84. <td>{{ work.workItem }}</td>
  85. <td>{{ work.subItem }}</td>
  86. <td>
  87. {{ work.scaleGrade }}
  88. </td>
  89. <td v-if="taskName == '分管审核'">
  90. <el-input-number style="width:80px;" :controls="false" v-model="work.workload" :min="0" :step="0.01"
  91. @change="(val) => updateSettle(work, val, 'workload')"></el-input-number>
  92. </td>
  93. <td v-else>
  94. {{ work.workload }}
  95. </td>
  96. <td>{{ work.price }}</td>
  97. <td>{{ work.unit }}</td>
  98. <td v-if="taskName == '分管审核'">
  99. <el-input-number style="width:80px;" :controls="false" v-model="work.coefficient" :min="0" :step="0.01"
  100. @change="(val) => updateSettle(work, val, 'coefficient')"></el-input-number>
  101. </td>
  102. <td v-else>
  103. {{ work.coefficient }}
  104. </td>
  105. <td style="text-align:right;">{{ work.settle ? Number(work.settle).toFixed(2) : '0.00' }}</td>
  106. <td class="remark-cell">{{ work.remark ? work.remark : '' }}</td>
  107. </tr>
  108. <tr>
  109. <td :colspan="8" class="head amount">内业绩效额合计</td>
  110. <td :colspan="1" class="head amount" :class="{ 'performance-error': isPerformanceExceeded() }"
  111. style="text-align:right;">
  112. {{ budgetForm.settleExpense ? budgetForm.settleExpense.toFixed(2) : '0.00' }}
  113. </td>
  114. <td></td>
  115. </tr>
  116. </table>
  117. </el-descriptions-item>
  118. <el-descriptions-item label="内业人员成本" :span="3" v-if="innerStaffList.length > 0">
  119. <table border="1" style="width:100%">
  120. <tr style="background-color:#f8f8f9">
  121. <td>序号</td>
  122. <td>姓名</td>
  123. <td>固定成本(天)</td>
  124. <td style="width:120px">预算天数</td>
  125. <td>固定成本小计</td>
  126. <td>绩效成本小计</td>
  127. <td style="width:120px">金额</td>
  128. <td style="width:120px">备注</td>
  129. </tr>
  130. <tr v-for="staff, index in innerStaffList" :key="'innerUser' + staff.userId">
  131. <td>{{ index + 1 }}</td>
  132. <td>{{ staff.user ? staff.user.nickName : '' }}</td>
  133. <td>{{ staff.dayCost }}</td>
  134. <td v-if="taskName == '分管审核'">
  135. <el-input-number style="width:80px;" :controls="false" v-model="staff.days" :min="0" :step="0.01"
  136. @change="(val) => updateInnerStaffAmount(staff, val, 'days')"></el-input-number>
  137. </td>
  138. <td v-else>{{ staff.days }}</td>
  139. <td style="text-align:right;">{{ staff.staffCost }}</td>
  140. <td v-if="taskName == '分管审核'">
  141. <el-input-number style="width:80px;" :controls="false" v-model="staff.performance" :min="0" :step="0.01"
  142. @change="(val) => updateInnerStaffAmount(staff, val, 'performance')"></el-input-number>
  143. </td>
  144. <td v-else style="text-align:right;">{{ staff.performance }}</td>
  145. <td style="text-align:right;">{{ staff.amount }}</td>
  146. <td class="remark-cell">{{ staff.remark }}</td>
  147. </tr>
  148. <tr>
  149. <td colspan="4" class="head amount">内业人员成本合计</td>
  150. <td class="head amount" style="text-align:right;">{{innerStaffList.reduce((sum, staff) => sum +
  151. (Number(staff.staffCost) || 0),
  152. 0).toFixed(2)}}</td>
  153. <td class="head amount" :class="{ 'performance-error': isPerformanceExceeded() }" style="text-align:right;">
  154. {{innerStaffList.reduce((sum, staff) => sum +
  155. (Number(staff.performance) || 0),
  156. 0).toFixed(2)}}</td>
  157. <td class="head amount" style="text-align:right;">{{innerStaffList.reduce((sum, staff) => sum +
  158. (Number(staff.amount) || 0),
  159. 0).toFixed(2)}}</td>
  160. </tr>
  161. </table>
  162. </el-descriptions-item>
  163. <el-descriptions-item label="外业人员成本" :span="3" v-if="outerStaffList.length > 0">
  164. <table border="1" style="width:100%">
  165. <tr style="background-color:#f8f8f9">
  166. <td>序号</td>
  167. <td>姓名</td>
  168. <td>固定成本(天)</td>
  169. <td>单价</td>
  170. <td style="width:120px">预算天数</td>
  171. <td>预算系数</td>
  172. <td>固定成本小计</td>
  173. <td>绩效成本小计</td>
  174. <td style="width:120px">金额</td>
  175. <td style="width:120px">备注</td>
  176. </tr>
  177. <tr v-for="staff, index in outerStaffList" :key="'outUser' + staff.userId">
  178. <td>{{ index + 1 }}</td>
  179. <td>{{ staff.user ? staff.user.nickName : '' }}</td>
  180. <td>{{ staff.dayCost }}</td>
  181. <td>200</td>
  182. <td v-if="taskName == '分管审核'">
  183. <el-input-number style="width:80px;" :controls="false" v-model="staff.days" :min="0" :step="0.01"
  184. @change="(val) => updateOuterStaffAmount(staff, val, 'days')"></el-input-number>
  185. </td>
  186. <td v-else>{{ staff.days }}</td>
  187. <td v-if="taskName == '分管审核'">
  188. <el-input-number style="width:80px;" :controls="false" v-model="staff.coefficient" :min="0" :step="0.01"
  189. @change="(val) => updateOuterStaffAmount(staff, val, 'coefficient')"></el-input-number>
  190. </td>
  191. <td v-else>{{ staff.coefficient }}</td>
  192. <td style="text-align:right;">{{ staff.staffCost }}</td>
  193. <td style="text-align:right;">{{ staff.performance }}</td>
  194. <td style="text-align:right;">{{ staff.amount }}</td>
  195. <td class="remark-cell">{{ staff.remark }}</td>
  196. </tr>
  197. <tr>
  198. <td colspan="4" class="head amount">外业人员成本合计</td>
  199. <td class="head amount" style="text-align:right;">{{outerStaffList.reduce((sum, staff) => sum +
  200. (Number(staff.days) || 0),
  201. 0).toFixed(2)}}</td>
  202. <td>——</td>
  203. <td class="head amount" style="text-align:right;">{{outerStaffList.reduce((sum, staff) => sum +
  204. (Number(staff.staffCost) || 0),
  205. 0).toFixed(2)}}</td>
  206. <td class="head amount" style="text-align:right;">{{outerStaffList.reduce((sum, staff) => sum +
  207. (Number(staff.performance) || 0),
  208. 0).toFixed(2)}}</td>
  209. <td class="head amount" style="text-align:right;">{{outerStaffList.reduce((sum, staff) => sum +
  210. (Number(staff.amount) || 0),
  211. 0).toFixed(2)}}</td>
  212. </tr>
  213. </table>
  214. </el-descriptions-item>
  215. <el-descriptions-item label="车辆成本" :span="3" v-if="carList.length > 0">
  216. <table border="1" style="width:100%;">
  217. <tr style="background-color:#f8f8f9">
  218. <td>序号</td>
  219. <td>车牌号</td>
  220. <td>折旧成本(天)</td>
  221. <td>预算天数</td>
  222. <td>金额</td>
  223. </tr>
  224. <tr v-for="car, index in carList" :key="'car' + car.carId">
  225. <td>{{ index + 1 }}</td>
  226. <td>{{ car.car ? car.car.licensePlate : '' }}</td>
  227. <td>{{ car.car ? car.car.dayCost : '' }}</td>
  228. <td v-if="taskName == '分管审核'">
  229. <el-input-number style="width:80px;" :controls="false" v-model="car.days" :min="0" :step="0.01"
  230. @change="(val) => updateCarAmount(car, val)"></el-input-number>
  231. </td>
  232. <td v-else>{{ car.days }}</td>
  233. <td style="text-align:right;">{{ car.amount }}</td>
  234. </tr>
  235. <tr>
  236. <td colspan="4" class="head amount">车辆成本合计</td>
  237. <td class="head amount" style="text-align:right;">{{carList.reduce((sum, car) => sum + (Number(car.amount)
  238. || 0), 0).toFixed(2)}}
  239. </td>
  240. </tr>
  241. </table>
  242. </el-descriptions-item>
  243. <el-descriptions-item label="设备成本" :span="3" v-if="deviceList.length > 0">
  244. <table border="1" style="width:100%;">
  245. <tr style="background-color:#f8f8f9">
  246. <td>序号</td>
  247. <td>设备名称</td>
  248. <td>规格型号</td>
  249. <td>品牌</td>
  250. <td>折旧成本(天)</td>
  251. <td>预算天数</td>
  252. <td>金额</td>
  253. </tr>
  254. <tr v-for="device, index in deviceList" :key="'device' + device.deviceId">
  255. <td>{{ index + 1 }}</td>
  256. <td>{{ device.device ? device.device.name : '' }}</td>
  257. <td>{{ device.device ? device.device.series : '' }}</td>
  258. <td>{{ device.device ? device.device.brand : '' }}</td>
  259. <td>{{ device.device ? device.device.dayCost : '' }}</td>
  260. <td v-if="taskName == '分管审核'">
  261. <el-input-number style="width:80px;" :controls="false" v-model="device.days" :min="0" :step="0.01"
  262. @change="(val) => updateDeviceAmount(device, val)"></el-input-number>
  263. </td>
  264. <td v-else>{{ device.days }}</td>
  265. <td style="text-align:right;">{{ device.amount }}</td>
  266. </tr>
  267. <tr>
  268. <td colspan="6" class="head amount">设备成本合计</td>
  269. <td class="head amount" style="text-align:right;">{{deviceList.reduce((sum, device) => sum +
  270. (Number(device.amount) || 0),
  271. 0).toFixed(2)}}</td>
  272. </tr>
  273. </table>
  274. </el-descriptions-item>
  275. <el-descriptions-item label="现场开支" :span="3" v-if="siteList.length > 0">
  276. <table border="1" style="width:100%;">
  277. <tr style="background-color:#f8f8f9">
  278. <td style="min-width:50px">序号</td>
  279. <td style="min-width:120px">开支项</td>
  280. <td style="min-width:120px">金额</td>
  281. <td>备注</td>
  282. </tr>
  283. <tr v-for="site, index in siteList" :key="'site' + site.expenseId">
  284. <td>{{ index + 1 }}</td>
  285. <td>{{ site.name }}</td>
  286. <td v-if="taskName == '分管审核'">
  287. <el-input-number style="width:100%;" :controls="false" v-model="site.amount" :min="0" :step="0.01"
  288. @change="(val) => updateSiteAmount(site, val)"></el-input-number>
  289. </td>
  290. <td v-else style="text-align:right;">{{ site.amount }}</td>
  291. <td class="remark-cell">{{ site.remark }}</td>
  292. </tr>
  293. <tr>
  294. <td colspan="2" class="head amount">现场开支合计</td>
  295. <td class="head amount" style="text-align:right;">{{siteList.reduce((sum, site) => sum +
  296. (Number(site.amount) || 0), 0).toFixed(2)}}
  297. </td>
  298. <td></td>
  299. </tr>
  300. </table>
  301. </el-descriptions-item>
  302. <el-descriptions-item label="经营相关" :span="3">
  303. <div v-if="taskName == '经营审核'" class="alert alert-warning"
  304. style="padding: 10px; border: 1px solid #f0ad4e; background-color: #fcf8e3; color: #8a6d3b;">
  305. 请填写以下内容
  306. </div>
  307. <table border="1" style="width:100%;" :class="{ 'business-section': taskName == '经营审核' }">
  308. <tr style="background-color:#f8f8f9">
  309. <td style="min-width:50px">序号</td>
  310. <td style="min-width:120px">名称</td>
  311. <td style="min-width:120px">金额</td>
  312. <td>备注</td>
  313. </tr>
  314. <tr>
  315. <td>{{ 1 }}</td>
  316. <td>外协费用</td>
  317. <td v-if="taskName == '经营审核'">
  318. <el-input-number style="width:100%;" :controls="false" v-model="budgetForm.outExpense" :min="0"
  319. :step="0.01" @change="updateTotalJYAmount"></el-input-number>
  320. </td>
  321. <td v-else style="text-align:right;">{{ budgetForm.outExpense }}</td>
  322. <td class="remark-cell">{{ budgetForm.outRemark }}</td>
  323. </tr>
  324. <tr>
  325. <td>{{ 2 }}</td>
  326. <td>保函费用</td>
  327. <td v-if="taskName == '经营审核'">
  328. <el-input-number style="width:100%;" :controls="false" v-model="budgetForm.letterExpense" :min="0"
  329. :step="0.01" @change="updateTotalJYAmount"></el-input-number>
  330. </td>
  331. <td v-else style="text-align:right;">{{ budgetForm.letterExpense }}</td>
  332. <td class="remark-cell">{{ budgetForm.letterRemark }}</td>
  333. </tr>
  334. <tr>
  335. <td>{{ 3 }}</td>
  336. <td>中标服务费</td>
  337. <td v-if="taskName == '经营审核'">
  338. <el-input-number style="width:100%;" :controls="false" v-model="budgetForm.winExpense" :min="0"
  339. :step="0.01" @change="updateTotalJYAmount"></el-input-number>
  340. </td>
  341. <td v-else style="text-align:right;">{{ budgetForm.winExpense }}</td>
  342. <td class="remark-cell">{{ budgetForm.winRemark }}</td>
  343. </tr>
  344. <tr>
  345. <td>{{ 4 }}</td>
  346. <td>税费</td>
  347. <td v-if="taskName == '经营审核'">
  348. <el-input-number style="width:100%;" :controls="false" v-model="budgetForm.taxExpense" :min="0"
  349. :step="0.01" @change="updateTotalJYAmount"></el-input-number>
  350. </td>
  351. <td v-else style="text-align:right;">{{ budgetForm.taxExpense }}</td>
  352. <td class="remark-cell">{{ budgetForm.taxRemark }}</td>
  353. </tr>
  354. <tr>
  355. <td>{{ 5 }}</td>
  356. <td>管理出差</td>
  357. <td v-if="taskName == '经营审核'">
  358. <el-input-number style="width:100%;" :controls="false" v-model="budgetForm.travelExpense" :min="0"
  359. :step="0.01" @change="updateTotalJYAmount"></el-input-number>
  360. </td>
  361. <td v-else style="text-align:right;">{{ budgetForm.travelExpense }}</td>
  362. <td class="remark-cell">{{ budgetForm.travelRemark }}</td>
  363. </tr>
  364. <tr>
  365. <td colspan="2" class="head amount">经营相关合计</td>
  366. <td class="head amount" style="text-align:right;">{{ totalJYAmount.toFixed(2) }}
  367. </td>
  368. <td></td>
  369. </tr>
  370. </table>
  371. </el-descriptions-item>
  372. <el-descriptions-item label="预算总额" :span="3">
  373. <table border="1" style="width:100%;" class="budget-summary">
  374. <tr class="header">
  375. <td><b>名称</b></td>
  376. <td><b>金额</b></td>
  377. </tr>
  378. <tr>
  379. <td>人员成本</td>
  380. <td style="text-align:right;">{{ isNaN(budgetForm.staffCost) ? 0 : Number(budgetForm.staffCost).toFixed(2)
  381. }}</td>
  382. </tr>
  383. <tr>
  384. <td>车辆成本</td>
  385. <td style="text-align:right;">{{ isNaN(budgetForm.carCost) ? 0 : Number(budgetForm.carCost).toFixed(2) }}
  386. </td>
  387. </tr>
  388. <tr>
  389. <td>设备成本</td>
  390. <td style="text-align:right;">{{ isNaN(budgetForm.deviceCost) ? 0 : Number(budgetForm.deviceCost).toFixed(2)
  391. }}</td>
  392. </tr>
  393. <tr>
  394. <td>现场开支成本</td>
  395. <td style="text-align:right;">{{ isNaN(budgetForm.siteSum) ? 0 : Number(budgetForm.siteSum).toFixed(2) }}
  396. </td>
  397. </tr>
  398. <tr>
  399. <td>经营相关成本</td>
  400. <td style="text-align:right;">{{ isNaN(totalJYAmount) ? 0 : Number(totalJYAmount).toFixed(2) }}</td>
  401. </tr>
  402. <tr class="total-budget">
  403. <td><b>预算总成本</b></td>
  404. <td style="text-align:right;"><b>{{ isNaN(budgetForm.totalBudget) ? 0 : budgetForm.totalBudget }}</b></td>
  405. </tr>
  406. </table>
  407. </el-descriptions-item>
  408. <el-descriptions-item label="合同总价" :span="3" v-hasRole="['leader', 'finance', 'nfinance', 'admin']">
  409. <table border="1" style="width:100%;" v-if="contractList.length > 0">
  410. <tr style="background-color:#f8f8f9">
  411. <td>序号</td>
  412. <td>合同名称</td>
  413. <td>合同总价</td>
  414. </tr>
  415. <tr v-for="contract, index in contractList" :key="contract.contractId">
  416. <td>{{ index + 1 }}</td>
  417. <td>{{ contract.contractName }}</td>
  418. <td>{{ contract.amount }}</td>
  419. </tr>
  420. </table>
  421. <div v-else>
  422. <div class="alert alert-warning">
  423. 暂无合同信息
  424. </div>
  425. </div>
  426. </el-descriptions-item>
  427. <el-descriptions-item label="分包合同总价" :span="3" v-hasRole="['leader', 'finance', 'nfinance', 'admin']">
  428. <table border="1" style="width:100%;" v-if="subContractList.length > 0">
  429. <tr style="background-color:#f8f8f9">
  430. <td>序号</td>
  431. <td>分包合同名称</td>
  432. <td>分包合同总价</td>
  433. </tr>
  434. <tr v-for="subContract, index in subContractList" :key="subContract.subContractId">
  435. <td>{{ index + 1 }}</td>
  436. <td>{{ subContract.subContractName }}</td>
  437. <td>{{ subContract.subAmount }}</td>
  438. </tr>
  439. </table>
  440. <div v-else>
  441. <div class="alert alert-warning">
  442. 暂无分包合同信息
  443. </div>
  444. </div>
  445. </el-descriptions-item>
  446. <el-descriptions-item label="分管领导审核意见" :span="3">
  447. <div v-if="taskName == '分管审核' && !budgetForm.managerComment" class="alert alert-warning"
  448. style="padding: 10px; border: 1px solid #f0ad4e; background-color: #fcf8e3; color: #8a6d3b;">
  449. 请填写以下内容
  450. </div>
  451. <div :class="{ 'business-section': taskName == '分管审核' && !budgetForm.managerComment }">
  452. <el-input :disabled="taskName != '分管审核'" type="textarea" placeholder="请输入审核意见"
  453. v-model="budgetForm.managerComment" :autosize="{ minRows: 4 }"></el-input>
  454. <div class="sign mt10" v-if="budgetForm.manager">
  455. <div class="mr20">签名:<span class="auditor">{{ getUserName(budgetForm.manager) }}</span>
  456. </div>
  457. <div class="ml20"><span>审核时间:{{ budgetForm.managerTime }}</span></div>
  458. </div>
  459. </div>
  460. </el-descriptions-item>
  461. <el-descriptions-item label="总经理审核意见" :span="3">
  462. <div v-if="taskName == '总经理审核'" class="alert alert-warning"
  463. style="padding: 10px; border: 1px solid #f0ad4e; background-color: #fcf8e3; color: #8a6d3b;">
  464. 请填写以下内容
  465. </div>
  466. <div :class="{ 'business-section': taskName == '总经理审核' }">
  467. <el-input :disabled="taskName != '总经理审核'" type="textarea" placeholder="请输入审核意见"
  468. v-model="budgetForm.zjlComment" :autosize="{ minRows: 4 }"></el-input>
  469. <div class="sign mt10" v-if="budgetForm.auditor">
  470. <div class="mr20">签名:<span class="auditor">{{ getUserName(budgetForm.auditor) }}</span>
  471. </div>
  472. <div class="ml20"><span>审核时间:{{ budgetForm.zjlTime }}</span></div>
  473. </div>
  474. </div>
  475. </el-descriptions-item>
  476. </el-descriptions>
  477. <div class="text-center mt20 mb20" v-if="taskName">
  478. <el-button type="danger" @click="returnOpen = true">退 回</el-button>
  479. <el-button type="warning" @click="preserve()">保 存</el-button>
  480. <el-button type="success" @click="confirmSucess()">提交下一个流程</el-button>
  481. </div>
  482. <el-dialog title="退回" :visible.sync="returnOpen" width="40%" append-to-body>
  483. <return-btn :taskForm="taskForm" :comment="commentByRole()" @goBack="$emit('goBack')"
  484. @cancel="returnOpen = false"></return-btn>
  485. </el-dialog>
  486. </div>
  487. </template>
  488. <script>
  489. import { listBudget, updateBudget } from "@/api/oa/budget/budget";
  490. import { listBudgetCar, updateBudgetCar } from "@/api/oa/budget/budgetCar";
  491. import { listBudgetDevice, updateBudgetDevice } from "@/api/oa/budget/budgetDevice";
  492. import { listBudgetSettle, updateBudgetSettle } from "@/api/oa/budget/budgetSettle";
  493. import { listBudgetStaff, updateBudgetStaff } from "@/api/oa/budget/budgetStaff";
  494. import { listProjectWork } from "@/api/oa/project/projectWork";
  495. import { listSite, updateSite } from "@/api/oa/budget/budgetSite.js";
  496. import { getProject } from "@/api/oa/project/project";
  497. import { getUsersManageLeader, getUserByPost } from "@/api/system/post";
  498. import { complete, getNextFlowNode } from "@/api/flowable/todo";
  499. import { listProjectContract } from "@/api/oa/contract/projectContract";
  500. import { listProjectSubcontract } from "@/api/oa/contract/projectSubcontract";
  501. import { getContract } from "@/api/oa/contract/contract";
  502. import { getSubContract } from "@/api/oa/contract/subContract";
  503. import { currentNodeByFormId } from "@/api/flowable/definition";
  504. import InnerStaffCost from './components/InnerStaffCost.vue';
  505. import OuterStaffCost from './components/OuterStaffCost.vue';
  506. import CarCost from './components/CarCost.vue';
  507. import DeviceCost from './components/DeviceCost.vue';
  508. import ReturnComment from '@/views/flowable/form/components/flowBtn/returnComment.vue';
  509. import ReturnBtn from '@/views/flowable/form/components/flowBtn/returnBtn.vue';
  510. export default {
  511. components: {
  512. InnerStaffCost,
  513. OuterStaffCost,
  514. CarCost,
  515. DeviceCost,
  516. ReturnComment,
  517. ReturnBtn
  518. },
  519. props: {
  520. taskForm: {
  521. type: Object,
  522. require: true
  523. },
  524. taskName: {
  525. type: String,
  526. }
  527. },
  528. watch: {
  529. 'taskForm.procInsId'() {
  530. this.initBudgetForm();
  531. this.getProjectWorkList();
  532. this.getContractInfo();
  533. this.getSubContractInfo();
  534. }
  535. },
  536. data() {
  537. return {
  538. baseUrl: process.env.VUE_APP_BASE_API,
  539. loading: true,
  540. budgetForm: {},
  541. project: {},
  542. workContentList: [],
  543. workList: [],
  544. innerStaffList: [],
  545. outerStaffList: [],
  546. carList: [],
  547. deviceList: [],
  548. siteList: [],
  549. totalJYAmount: 0,
  550. returnOpen: false,
  551. contractList: [],
  552. showAlter: true,
  553. subContractList: [],
  554. }
  555. },
  556. created() {
  557. this.initBudgetForm();
  558. this.getProjectWorkList();
  559. this.getContractInfo();
  560. this.getSubContractInfo();
  561. },
  562. methods: {
  563. initBudgetForm() {
  564. this.loading = true;
  565. listBudget({ pageNum: 1, pageSize: 20, projectId: this.taskForm.formId, procInstId:this.taskForm.procInsId }).then(async res => {
  566. this.budgetForm = res.rows[0];
  567. if (this.budgetForm) {
  568. this.budgetForm.outExpense = Number(this.budgetForm.outExpense).toFixed(2);
  569. this.budgetForm.letterExpense = Number(this.budgetForm.letterExpense).toFixed(2);
  570. this.budgetForm.winExpense = Number(this.budgetForm.winExpense).toFixed(2);
  571. this.budgetForm.taxExpense = Number(this.budgetForm.taxExpense).toFixed(2);
  572. this.budgetForm.travelExpense = Number(this.budgetForm.travelExpense).toFixed(2);
  573. this.budgetForm.totalBudget = Number(this.budgetForm.totalBudget).toFixed(2);
  574. this.totalJYAmount = Number(this.budgetForm.outExpense || 0) +
  575. Number(this.budgetForm.letterExpense || 0) +
  576. Number(this.budgetForm.winExpense || 0) +
  577. Number(this.budgetForm.taxExpense || 0) +
  578. Number(this.budgetForm.travelExpense || 0);
  579. const budgetId = this.budgetForm.budgetId;
  580. // 获取设备数据
  581. let deviceRes = await listBudgetDevice({ pageSize: 100, budgetId });
  582. this.deviceList = deviceRes.rows;
  583. this.deviceList = this.deviceList.map(device => {
  584. return {
  585. ...device,
  586. amount: Number(device.amount).toFixed(2),
  587. };
  588. });
  589. // 获取车辆数据
  590. let carRes = await listBudgetCar({ pageSize: 100, budgetId });
  591. this.carList = carRes.rows;
  592. this.carList = this.carList.map(car => {
  593. return {
  594. ...car,
  595. amount: Number(car.amount).toFixed(2),
  596. };
  597. });
  598. // 获取内业人员数据
  599. let innerStaffRes = await listBudgetStaff({ pageSize: 100, budgetId, type: '内业' });
  600. this.innerStaffList = innerStaffRes.rows;
  601. this.innerStaffList = this.innerStaffList.map(staff => {
  602. return {
  603. ...staff,
  604. dayCost: Number(staff.dayCost).toFixed(2),
  605. performance: Number(staff.performance).toFixed(2),
  606. amount: Number(staff.amount).toFixed(2),
  607. staffCost: Number(staff.staffCost).toFixed(2)
  608. };
  609. });
  610. // 获取外业人员数据
  611. let outerStaffRes = await listBudgetStaff({ pageSize: 100, budgetId, type: '外业' });
  612. this.outerStaffList = outerStaffRes.rows;
  613. this.outerStaffList = this.outerStaffList.map(staff => {
  614. return {
  615. ...staff,
  616. dayCost: Number(staff.dayCost).toFixed(2),
  617. performance: Number(staff.performance).toFixed(2),
  618. amount: Number(staff.amount).toFixed(2),
  619. staffCost: Number(staff.staffCost).toFixed(2)
  620. };
  621. });
  622. // 获取内业绩效额数据
  623. let settleRes = await listBudgetSettle({ pageSize: 100, budgetId });
  624. this.workList = settleRes.rows;
  625. for (let work of this.workList) {
  626. if (work.checkPriceId) {
  627. work.workType = work.cmcCheckPrice.workType
  628. work.workItem = work.cmcCheckPrice.workItem
  629. work.subItem = work.cmcCheckPrice.subItem
  630. work.scaleGrade = work.cmcCheckPrice.scaleGrade
  631. work.unit = work.cmcCheckPrice.unit
  632. if (work.groundType == '0') {
  633. work.price = work.cmcCheckPrice.commonPrice
  634. } else {
  635. work.price = work.cmcCheckPrice.complexPrice
  636. }
  637. } else {
  638. work.workType = work.cmcPrice.workType
  639. work.workItem = work.cmcPrice.workItem
  640. work.subItem = work.cmcPrice.subItem
  641. work.scaleGrade = work.cmcPrice.scaleGrade
  642. work.unit = work.cmcPrice.unit
  643. if (work.groundType == '0') {
  644. work.price = work.cmcPrice.commonPrice
  645. } else {
  646. work.price = work.cmcPrice.complexPrice
  647. }
  648. }
  649. }
  650. // 获取现场开支
  651. let siteRes = await listSite({ pageSize: 100, budgetId });
  652. this.siteList = siteRes.rows;
  653. this.siteList = this.siteList.map(site => {
  654. return {
  655. ...site,
  656. amount: Number(site.amount).toFixed(2),
  657. };
  658. });
  659. this.budgetForm.siteSum = this.siteList.reduce((sum, site) => sum + (Number(site.amount) || 0), 0);
  660. // 检查内业人员绩效总额是否超过内业绩效总额
  661. const totalPerformance = this.innerStaffList.reduce((sum, staff) => sum + (Number(staff.performance) || 0), 0);
  662. if (totalPerformance > Number(this.budgetForm.settleExpense)) {
  663. this.$message.warning('内业人员绩效总额超过内业绩效总额,请检查');
  664. }
  665. this.getCurrentTaskName(this.budgetForm.projectId).then(response => {
  666. if (response != '总经理审核' && response != '预算批准') {
  667. this.$set(this.budgetForm, 'managerComment', undefined);
  668. this.$set(this.budgetForm, 'manager', undefined);
  669. this.$set(this.budgetForm, 'managerTime', undefined);
  670. }
  671. if (response != '预算批准') {
  672. this.$set(this.budgetForm, 'zjlComment', undefined);
  673. this.$set(this.budgetForm, 'auditor', undefined);
  674. this.$set(this.budgetForm, 'zjlTime', undefined);
  675. }
  676. })
  677. }
  678. this.loading = false;
  679. }).catch(() => {
  680. this.loading = false;
  681. });
  682. },
  683. getContractInfo() {
  684. listProjectContract({ projectId: this.taskForm.formId }).then(res => {
  685. let contractIds = res.rows;
  686. this.contractList = [];
  687. for (let contractId of contractIds) {
  688. getContract(contractId.contractId).then(response => {
  689. this.contractList.push(response.data);
  690. })
  691. }
  692. })
  693. },
  694. getSubContractInfo() {
  695. listProjectSubcontract({ projectId: this.taskForm.formId }).then(res => {
  696. let subContractIds = res.rows;
  697. this.subContractList = [];
  698. for (let subContract of subContractIds) {
  699. getSubContract(subContract.subContractId).then(response => {
  700. this.subContractList.push(response.data);
  701. })
  702. }
  703. })
  704. },
  705. safeNumber(value) {
  706. const num = Number(value);
  707. return isNaN(num) ? 0 : num;
  708. },
  709. getProjectWorkList() {
  710. getProject(this.taskForm.formId).then(res => {
  711. this.project = res.data;
  712. })
  713. listProjectWork({ pageSize: 999, projectId: this.taskForm.formId }).then(res => {
  714. this.workContentList = res.rows;
  715. })
  716. },
  717. async getCurrentTaskName(projectId) {
  718. let resData = await currentNodeByFormId({ formId: projectId, name: '3045' })
  719. if (resData.code == 200) {
  720. return resData.msg;
  721. }
  722. else
  723. return ''
  724. },
  725. async preserve() {
  726. this.$message.warning('正在保存,请勿关闭页面');
  727. if (this.taskName == '分管审核') {
  728. this.budgetForm.manager = this.$store.getters.userId;
  729. this.budgetForm.managerTime = this.parseTime(new Date(), '{y}-{m}-{d}');
  730. } else if (this.taskName == '总经理审核') {
  731. this.budgetForm.auditor = this.$store.getters.userId;
  732. this.budgetForm.zjlTime = this.parseTime(new Date(), '{y}-{m}-{d}');
  733. }
  734. if (this.taskName != '经营审核') {
  735. await Promise.all([
  736. ...this.carList.map(car => updateBudgetCar(car)),
  737. ...this.deviceList.map(device => updateBudgetDevice(device)),
  738. ...this.innerStaffList.map(staff => updateBudgetStaff(staff)),
  739. ...this.outerStaffList.map(staff => updateBudgetStaff(staff)),
  740. ...this.workList.map(work => updateBudgetSettle(work)),
  741. ...this.siteList.map(site => updateSite(site))
  742. ]);
  743. }
  744. await updateBudget(this.budgetForm)
  745. this.$message.success('保存成功');
  746. },
  747. confirmSucess() {
  748. this.preserve();
  749. this.$modal.confirm('是否提交到下一个流程?').then(async () => {
  750. if (this.taskName == '经营审核') {
  751. let managerLeaderRes = await getUsersManageLeader({ userId: this.budgetForm.compiler });
  752. let managerLeader = managerLeaderRes.data;
  753. let managerList = managerLeader.map(item => item.userId);
  754. this.$set(this.taskForm.variables, 'approvalList', managerList);
  755. }
  756. else if (this.taskName == '分管审核') {
  757. if (!this.budgetForm.managerComment) {
  758. this.$message.error('请填写审核意见')
  759. return
  760. }
  761. let zjlRes = await getUserByPost({ postName: '总经理' });
  762. let zjlUserId = zjlRes.data[0].userId;
  763. this.$set(this.taskForm.variables, 'approval', zjlUserId);
  764. }
  765. else if (this.taskName == '总经理审核') {
  766. if (!this.budgetForm.zjlComment) {
  767. this.$message.error('请填写审核意见')
  768. return
  769. }
  770. let dszRes = await getUserByPost({ postName: '董事长' });
  771. let dszUserId = dszRes.data[0].userId;
  772. this.$set(this.taskForm.variables, 'approval', dszUserId);
  773. this.$set(this.taskForm.variables, "skip", true);
  774. }
  775. //提交到下一个流程
  776. getNextFlowNode({ taskId: this.taskForm.taskId }).then(res => {
  777. complete(this.taskForm).then(response => {
  778. this.$modal.msgSuccess(response.msg);
  779. this.$emit("goBack");
  780. });
  781. });
  782. })
  783. },
  784. updateTotalJYAmount() {
  785. this.totalJYAmount = Number(this.budgetForm.outExpense || 0) +
  786. Number(this.budgetForm.letterExpense || 0) +
  787. Number(this.budgetForm.winExpense || 0) +
  788. Number(this.budgetForm.taxExpense || 0) +
  789. Number(this.budgetForm.travelExpense || 0);
  790. const innerStaffTotal = this.innerStaffList.reduce((sum, staff) => sum + (Number(staff.amount) || 0), 0);
  791. const outerStaffTotal = this.outerStaffList.reduce((sum, staff) => sum + (Number(staff.amount) || 0), 0);
  792. const carTotal = this.carList.reduce((sum, car) => sum + (Number(car.amount) || 0), 0);
  793. const deviceTotal = this.deviceList.reduce((sum, device) => sum + (Number(device.amount) || 0), 0);
  794. const siteTotal = this.siteList.reduce((sum, site) => sum + (Number(site.amount) || 0), 0);
  795. this.budgetForm.totalBudget = (innerStaffTotal +
  796. outerStaffTotal +
  797. carTotal +
  798. deviceTotal +
  799. siteTotal +
  800. this.totalJYAmount).toFixed(2);
  801. },
  802. commentByRole() {
  803. if (this.taskName == '分管审核') {
  804. return this.budgetForm.managerComment
  805. } else if (this.taskName == '总经理审核') {
  806. return this.budgetForm.zjlComment
  807. } else if (this.taskName == '董事长批准') {
  808. return this.budgetForm.dszComment
  809. }
  810. },
  811. updateSettle(work, value, field) {
  812. work[field] = value;
  813. work.settle = (Number(work.workload) * Number(work.price) * Number(work.coefficient)).toFixed(2);
  814. this.budgetForm.settleExpense = this.workList.reduce((sum, work) => {
  815. return sum + (Number(work.settle) || 0);
  816. }, 0);
  817. this.updateTotalJYAmount();
  818. },
  819. updateInnerStaffAmount(staff, value, field) {
  820. staff[field] = value;
  821. staff.staffCost = (Number(staff.days) * Number(staff.dayCost)).toFixed(2);
  822. staff.amount = (Number(staff.staffCost) + Number(staff.performance)).toFixed(2);
  823. let innerStaffTotal = this.innerStaffList.reduce((sum, staff) => sum + (Number(staff.amount) || 0), 0);
  824. let outerStaffTotal = this.outerStaffList.reduce((sum, staff) => sum + (Number(staff.amount) || 0), 0);
  825. let innerStaffPerformance = this.innerStaffList.reduce((sum, staff) => sum + (Number(staff.performance) || 0), 0);
  826. this.budgetForm.staffCost = (innerStaffTotal + outerStaffTotal).toFixed(2);
  827. this.updateTotalJYAmount();
  828. if (innerStaffPerformance > this.budgetForm.settleExpense) {
  829. this.$message.error('内业人员成本超过内业绩效总额,请修改');
  830. return;
  831. }
  832. },
  833. updateOuterStaffAmount(staff, value, field) {
  834. staff[field] = value;
  835. staff.performance = (200 * Number(staff.days) * Number(staff.coefficient)).toFixed(2);
  836. staff.amount = (Number(staff.staffCost) + Number(staff.performance)).toFixed(2);
  837. let innerStaffTotal = this.innerStaffList.reduce((sum, staff) => sum + (Number(staff.amount) || 0), 0);
  838. let outerStaffTotal = this.outerStaffList.reduce((sum, staff) => sum + (Number(staff.amount) || 0), 0);
  839. this.budgetForm.staffCost = (innerStaffTotal + outerStaffTotal).toFixed(2);
  840. this.updateTotalJYAmount();
  841. },
  842. updateCarAmount(car, value) {
  843. car.days = value;
  844. car.amount = (Number(car.car.dayCost) * Number(car.days)).toFixed(2);
  845. let carTotal = this.carList.reduce((sum, car) => sum + (Number(car.amount) || 0), 0);
  846. this.budgetForm.carCost = carTotal.toFixed(2);
  847. this.updateTotalJYAmount();
  848. },
  849. updateDeviceAmount(device, value) {
  850. device.days = value;
  851. device.amount = (Number(device.device.dayCost) * Number(device.days)).toFixed(2);
  852. let deviceTotal = this.deviceList.reduce((sum, device) => sum + (Number(device.amount) || 0), 0);
  853. this.budgetForm.deviceCost = deviceTotal.toFixed(2);
  854. this.updateTotalJYAmount();
  855. },
  856. updateSiteAmount(site, value) {
  857. site.amount = value;
  858. let siteTotal = this.siteList.reduce((sum, site) => sum + (Number(site.amount) || 0), 0);
  859. this.budgetForm.siteSum = siteTotal.toFixed(2);
  860. this.updateTotalJYAmount();
  861. },
  862. isPerformanceExceeded() {
  863. const totalPerformance = this.innerStaffList.reduce((sum, staff) => sum + (Number(staff.performance) || 0), 0);
  864. return totalPerformance > Number(this.budgetForm.settleExpense);
  865. },
  866. isReturn(val) {
  867. this.showAlter = val;
  868. },
  869. },
  870. }
  871. </script>
  872. <style lang="scss" scoped>
  873. @import "@/assets/styles/element-reset.scss";
  874. .mylabel {
  875. font-weight: bold;
  876. }
  877. .main {
  878. width: 90%;
  879. margin: 0 auto;
  880. text-align: center;
  881. }
  882. .descriptions {
  883. width: 100%;
  884. margin: 0 auto;
  885. }
  886. .amount {
  887. font-weight: bold;
  888. font-family: Arial, Helvetica, sans-serif;
  889. }
  890. table {
  891. text-align: center;
  892. border-collapse: collapse;
  893. margin: 0 auto;
  894. /*设置背景颜色*/
  895. /* background-color: #bfa; */
  896. td {
  897. padding: 5px;
  898. }
  899. }
  900. .remark-cell {
  901. text-align: left;
  902. }
  903. .adjust {
  904. color: #F56C6C;
  905. width: 120px;
  906. }
  907. .total-budget {
  908. font-size: 16px;
  909. font-weight: bold;
  910. color: #4CAF50;
  911. text-align: center;
  912. padding: 10px;
  913. border: 2px solid #4CAF50;
  914. border-radius: 5px;
  915. background-color: #f0f8f5;
  916. }
  917. ::v-deep .el-descriptions .is-bordered .el-descriptions-item__cell {
  918. border: 1px solid #838894;
  919. }
  920. ::v-deep .el-descriptions-item__label.is-bordered-label {
  921. color: #434141;
  922. background: #eaeaea;
  923. }
  924. .business-section {
  925. border: 2px solid #E6A23C !important;
  926. box-shadow: 0 0 10px rgba(64, 158, 255, 0.2);
  927. tr {
  928. background-color: #f0f7ff;
  929. &:hover {
  930. background-color: #e6f1ff;
  931. }
  932. }
  933. .el-input-number {
  934. .el-input__inner {
  935. background-color: #fff;
  936. border: 1px solid #E6A23C;
  937. &:focus {
  938. border-color: #e2af62;
  939. box-shadow: 0 0 5px rgba(64, 158, 255, 0.3);
  940. }
  941. }
  942. }
  943. .head.amount {
  944. background-color: #ecf5ff;
  945. color: #E6A23C;
  946. font-weight: bold;
  947. }
  948. }
  949. .sign {
  950. display: flex;
  951. justify-content: flex-end;
  952. align-items: center;
  953. padding-right: 80px;
  954. line-height: 30px;
  955. /* 如果需要垂直居中 */
  956. }
  957. .performance-error {
  958. background-color: #fef0f0 !important;
  959. color: #f56c6c !important;
  960. border: 1px solid #fbc4c4 !important;
  961. }
  962. .performance-error:hover {
  963. background-color: #fde2e2 !important;
  964. }
  965. .performance-error .el-input-number {
  966. .el-input__inner {
  967. background-color: #fef0f0;
  968. border-color: #f56c6c;
  969. color: #f56c6c;
  970. }
  971. }
  972. </style>