综合办公系统
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 38KB

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