form.vue 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432
  1. <template>
  2. <HcCard actionUi="text-center" :title="detailData?.projectName">
  3. <template #extra>
  4. <HcNewSwitch :datas="tabTab" :keys="tabKey" @change="tabChange" :round="false"/>
  5. </template>
  6. <template #search>
  7. <div class="hc-program-project-form-radio-group">
  8. <el-radio-group v-model="radioType" size="large">
  9. <el-radio-button label="1">市场部</el-radio-button>
  10. <el-radio-button label="2">研发部</el-radio-button>
  11. <el-radio-button label="3">实施部</el-radio-button>
  12. <el-radio-button label="4">维护部</el-radio-button>
  13. <el-radio-button label="5">管理支出</el-radio-button>
  14. <el-radio-button label="6">外包劳务</el-radio-button>
  15. </el-radio-group>
  16. </div>
  17. </template>
  18. <HcTable :isIndex="false" :column="tableColumn" :datas="tableData" :row-style="tableRowStyle">
  19. <template #planTaskType="{row,index}">
  20. <el-select v-model="row.planTaskType" v-if="row.isEdit">
  21. <el-option label="选项1" value="选项1"/>
  22. <el-option label="选项2" value="选项2"/>
  23. </el-select>
  24. <span v-else>{{row.planTaskType}}</span>
  25. </template>
  26. <template #planTaskDesc="{row,index}">
  27. <el-input v-model="row.planTaskDesc" v-if="row.isEdit"/>
  28. <span v-else>{{row.planTaskDesc}}</span>
  29. </template>
  30. <template #planTarget="{row,index}">
  31. <el-input v-model="row.planTarget" v-if="row.isEdit"/>
  32. <span v-else>{{row.planTarget}}</span>
  33. </template>
  34. <template #key7="{row,index}">
  35. <HcDatePicker :dates="[row.planStartTime,row.planEndTime]" @change="betweenTimeUpdate($event,row)" v-if="row.isEdit"/>
  36. <!-- <span v-else>{{row.key7}}</span> -->
  37. <span v-else>
  38. <span >{{row.planStartTime?row.planStartTime:''}}</span>
  39. <span v-if="row.planEndTime">~</span>
  40. <span >{{row.planEndTime?row.planEndTime:''}}</span>
  41. </span>
  42. </template>
  43. <template #planDays="{row,index}">
  44. <el-input v-model="row.planDays" v-if="row.isEdit" disabled/>
  45. <span v-else>{{row.planDays}}</span>
  46. </template>
  47. <!-- <template #key9="{row,index}">
  48. <el-input v-model="row.key9" v-if="row.isEdit"/>
  49. <span v-else>{{row.key9}}</span>
  50. </template> -->
  51. <template #action="{row,index}">
  52. <el-button size="small" type="success" v-if="row.isEdit" @click="getWorkDays(row)">保存</el-button>
  53. <el-button size="small" type="primary" v-else @click="row.isEdit = true">编辑</el-button>
  54. <el-button size="small" type="warning" @click="relatedModalShow(row)">关联回款</el-button>
  55. <el-button size="small" type="danger" @click="subplanModalShow(row)">分解子计划</el-button>
  56. </template>
  57. </HcTable>
  58. <template #action>
  59. <el-button size="large" type="info" hc-btn @click="goBackClick">
  60. <HcIcon name="arrow-go-back"/>
  61. <span>取消并返回</span>
  62. </el-button>
  63. <el-button size="large" type="primary" hc-btn @click="saveClick" :loading="saveLoaing">
  64. <HcIcon name="check-double"/>
  65. <span>提交保存</span>
  66. </el-button>
  67. </template>
  68. <!--分解子计划-->
  69. <HcDialog bgColor="#ffffff" isToBody isTable
  70. title="分解子计划" widths="80%"
  71. saveText="保存"
  72. :show="subplanModal"
  73. @close="subplanCloseClick"
  74. @save="subplanSaveClick"
  75. >
  76. <HcTable :isIndex="false" :column="tableSubplanColumn" :datas="tableSubplanData">
  77. <template #planTaskType="{row,index}">
  78. <el-select v-model="row.planTaskType">
  79. <el-option label="选项1" value="选项1"/>
  80. <el-option label="选项2" value="选项2"/>
  81. </el-select>
  82. </template>
  83. <template #planTaskDesc="{row,index}">
  84. <el-input v-model="row.planTaskDesc"/>
  85. </template>
  86. <template #planTarget="{row,index}">
  87. <el-input v-model="row.planTarget"/>
  88. </template>
  89. <template #key7="{row,index}">
  90. <HcDatePicker :dates="[row.planStartTime,row.planEndTime]" @change="subbetweenTimeUpdate($event,row)" v-if="true"/>
  91. <span v-else>
  92. <span >{{row.planStartTime?row.planStartTime:''}}</span>
  93. <span v-if="row.planEndTime">~</span>
  94. <span >{{row.planEndTime?row.planEndTime:''}}</span>
  95. </span>
  96. </template>
  97. <template #planDays="{row,index}" >
  98. <!-- <el-input v-model="row.planDays" disabled/> -->
  99. <el-input v-model="row.planDays" disabled />
  100. </template>
  101. <template #key9="{row,index}">
  102. <el-input v-model="row.key9"/>
  103. </template>
  104. <template #action="{row,index}">
  105. <el-button size="small" type="success" v-if="row.isEdit" @click="getWorkDays(row)">保存</el-button>
  106. <el-button size="small" type="primary" v-else @click="row.isEdit = true">编辑</el-button>
  107. </template>
  108. </HcTable>
  109. </HcDialog>
  110. <!--关联回款里程碑-->
  111. <HcDialog bgColor="#ffffff" isToBody isTable :footer="false" :show="relatedModal" title="关联回款里程碑" widths="70%" @close="relatedCloseClick">
  112. <HcTable :column="tableRelatedColumn" :datas="tableRelatedData">
  113. <template #action="{row,index}">
  114. <el-button size="small" type="success" v-if="row.isRelation==1" @click="relation(row,0)">取消关联</el-button>
  115. <el-button size="small" type="primary" v-else @click="relation(row,1)">关联</el-button>
  116. </template>
  117. </HcTable>
  118. </HcDialog>
  119. </HcCard>
  120. </template>
  121. <script setup>
  122. import {useRouter, useRoute} from 'vue-router'
  123. import {onActivated, ref,watch} from "vue";
  124. import projectApi from '~api/program/project.js';
  125. import contractApi from '~api/project/project-contract.js';
  126. import {getArrValue,getObjValue} from "js-fast-way"
  127. //初始变量
  128. const router = useRouter()
  129. const useRoutes = useRoute()
  130. const dataId = ref(useRoutes?.query?.id ?? '')
  131. const dataType = ref(useRoutes?.query?.type ?? '')
  132. //缓存页面被激活时
  133. onActivated(() => {
  134. dataId.value = useRoutes?.query?.id ?? ''
  135. dataType.value = useRoutes?.query?.type ?? ''
  136. if(dataType.value!=='add'){
  137. getPlanByProjectId()
  138. }else if(dataType.value=='add'){
  139. }
  140. })
  141. const detailData=ref({})
  142. const constructionData=ref([])
  143. const buildData=ref([])
  144. const supervisorUnitData=ref([])
  145. //获取详情
  146. const getPlanByProjectId=async()=>{
  147. const {error, code, data} = await projectApi.getPlanByProjectId({id: dataId.value})
  148. if (!error && code === 200) {
  149. console.log(getObjValue(data),'详情');
  150. detailData.value=getObjValue(data)
  151. constructionData.value=detailData.value?.constructUnit||[]
  152. buildData.value=detailData.value?.buildDatatUnit||[]
  153. supervisorUnitData.value=detailData.value?.supervisorUnit||[]
  154. tabKey.value='construction'
  155. radioType.value=1
  156. // milestoneData.value=getArrValue(data)
  157. } else {
  158. // milestoneData.value=[]
  159. }
  160. }
  161. //类型tab数据和相关处理
  162. const tabKey = ref('')
  163. const tabTab = ref([
  164. {key: 'construction', name: '施工单位成本'},
  165. {key: 'supervision', name: '监理单位成本'},
  166. {key: 'build', name: '建设单位成本'}
  167. ]);
  168. const originTableData=ref([ {}])
  169. const tabChange = ({key}) => {
  170. tabKey.value = key
  171. console.log(tableData.value,'tableData.value');
  172. if(key=='supervision'){
  173. tableData.value=supervisorUnitData.value.length>0?supervisorUnitData.value:originTableData.value
  174. detailData.value.supervisorUnit=tableData.value
  175. }else if(key=='construction'){
  176. tableData.value=constructionData.value.length>0?constructionData.value:originTableData.value
  177. detailData.value.constructUnit=tableData.value
  178. }else if(key=='bulid'){
  179. console.log(buildData.value,'=buildData.value');
  180. tableData.value=buildData.value.length>0?buildData.value:originTableData.value
  181. detailData.value.buildUnit=tableData.value
  182. }
  183. }
  184. const radioType = ref('')
  185. //深度监听
  186. watch(() => [
  187. radioType.value,
  188. tabKey.value
  189. ], ([radioType]) => {
  190. console.log(radioType,'radioType');
  191. if(tabKey.value==='construction'){
  192. console.log(constructionData.value[radioType],'constructionData.value');
  193. tableData.value=constructionData.value[radioType]
  194. }else if(tabKey.value==='build'){
  195. console.log(buildData.value[radioType],'buildData.value');
  196. tableData.value=buildData.value[radioType]
  197. }else if(tabKey.value==='supervision'){
  198. console.log(supervisorUnitData.value[radioType],'supervisorUnitData.value');
  199. tableData.value=supervisorUnitData.value[radioType]
  200. }
  201. }, {deep: true})
  202. //表格
  203. const tableColumn = [
  204. {key: 'projectProcessValue', name: '项目环节', width: '160', align: 'center'},
  205. {key: 'budgetTypeValue', name: '预算类型', width: '160', align: 'center'},
  206. {key: 'taskDetailValue', name: '任务明细', width: '160', align: 'center'},
  207. {key: 'planTaskType', name: '任务类型', width: '160', align: 'center'},
  208. {key: 'planTaskDesc', name: '任务描述', minWidth: '200', align: 'center', isTooltip: true},
  209. {key: 'planTarget', name: '完成指标', minWidth: '200', align: 'center', isTooltip: true},
  210. {key: 'key7', name: '计划起止日期', width: '280', align: 'center'},
  211. {key: 'planDays', name: '预计工作量(小数/整数/天)', width: '160', align: 'center'},
  212. {key: 'postTypeValue', name: '投入岗位类型(日单价)', width: '160', align: 'center'},
  213. {key: 'staffCount', name: '投入人员数量', width: '160', align: 'center'},
  214. {key: 'returnedValue', name: '关联回款里程碑', minWidth: '200', isTooltip: true},
  215. {key: 'action', name: '操作', width: '280', align: 'center', fixed: 'right'},
  216. ]
  217. const tableData = ref([
  218. {id: 1, key1: 'xx', key2: 'xx', key3: 'xx', key4: 'xx', key5: 'xx', key6: 'xx', key7: 'xx', key8: 'xx', key9: 'xx', key10: 'xx'},
  219. {id: 2, key1: 'xx', key2: 'xx', key3: 'xx', key4: 'xx', key5: 'xx', key6: 'xx', key7: 'xx', key8: 'xx', key9: 'xx', key10: 'xx'},
  220. {id: 3, key1: 'xx', key2: 'xx', key3: 'xx', key4: 'xx', key5: 'xx', key6: 'xx', key7: 'xx', key8: 'xx', key9: 'xx', key10: 'xx'},
  221. {id: 4, key1: 'xx', key2: 'xx', key3: 'xx', key4: 'xx', key5: 'xx', key6: 'xx', key7: 'xx', key8: 'xx', key9: 'xx', key10: 'xx'},
  222. {id: 5, key1: 'xx', key2: 'xx', key3: 'xx', key4: 'xx', key5: 'xx', key6: 'xx', key7: 'xx', key8: 'xx', key9: 'xx', key10: 'xx'},
  223. {id: 6, key1: 'xx', key2: 'xx', key3: 'xx', key4: 'xx', key5: 'xx', key6: 'xx', key7: 'xx', key8: 'xx', key9: 'xx', key10: 'xx'},
  224. ])
  225. //表格行样式
  226. const tableRowStyle = ({row, rowIndex}) => {
  227. if (row.taskFinishedStatus === 1) {
  228. return {
  229. 'background-color': '#E99D42',
  230. '--el-fill-color-lighter': '#E99D42',
  231. '--el-table-row-hover-bg-color': '#E99D42'
  232. }
  233. } else if (row.taskFinishedStatus === 2 ) {
  234. return {
  235. 'background-color': '#7e9559',
  236. '--el-fill-color-lighter': '#7e9559',
  237. '--el-table-row-hover-bg-color': '#7e9559'
  238. }
  239. }
  240. }
  241. //日期时间被选择
  242. const betweenTime = ref(null)
  243. const betweenTimeUpdate = ({arr, query},item) => {
  244. item.planStartTime=arr[0]
  245. item.planEndTime=arr[1]
  246. item.betweenTime=arr
  247. }
  248. //日期时间被选择
  249. const subbetweenTime = ref(null)
  250. const subbetweenTimeUpdate = ({arr, query},item) => {
  251. console.log(item,'item');
  252. item.planStartTime=arr[0]
  253. item.planEndTime=arr[1]
  254. item.subbetweenTime=arr
  255. }
  256. const subplanModal = ref(false)
  257. const subPlanItem=ref({})
  258. const subplanModalShow = (row) => {
  259. console.log(row,'row');
  260. subPlanItem.value=row
  261. subplanModal.value = true
  262. tableSubplanData.value=row?.childrenList||[]
  263. }
  264. //表格
  265. const tableSubplanColumn = [
  266. {key: 'projectProcessValue', name: '项目环节', width: '160', align: 'center'},
  267. {key: 'budgetTypeValue', name: '预算类型', width: '160', align: 'center'},
  268. {key: 'taskDetailValue', name: '任务明细', width: '160', align: 'center'},
  269. {key: 'planTaskType', name: '任务类型', width: '160', align: 'center'},
  270. {key: 'planTaskDesc', name: '任务描述', minWidth: '200', align: 'center', isTooltip: true},
  271. {key: 'planTarget', name: '完成指标', minWidth: '200', align: 'center', isTooltip: true},
  272. {key: 'key7', name: '计划起止日期', width: '280', align: 'center'},
  273. {key: 'planDays', name: '预计工作量(小数/整数/天)', width: '160', align: 'center'},
  274. {key: 'action', name: '操作', width: '80', align: 'center', fixed: 'right'},
  275. ]
  276. const tableSubplanData = ref([
  277. {id: 1, key1: 'xx', key2: 'xx', key3: 'xx', key4: 'xx', key5: 'xx', key6: 'xx', key7: 'xx', key8: 'xx', key9: 'xx', key10: 'xx'},
  278. {id: 2, key1: 'xx', key2: 'xx', key3: 'xx', key4: 'xx', key5: 'xx', key6: 'xx', key7: 'xx', key8: 'xx', key9: 'xx', key10: 'xx'},
  279. {id: 3, key1: 'xx', key2: 'xx', key3: 'xx', key4: 'xx', key5: 'xx', key6: 'xx', key7: 'xx', key8: 'xx', key9: 'xx', key10: 'xx'},
  280. {id: 4, key1: 'xx', key2: 'xx', key3: 'xx', key4: 'xx', key5: 'xx', key6: 'xx', key7: 'xx', key8: 'xx', key9: 'xx', key10: 'xx'},
  281. {id: 5, key1: 'xx', key2: 'xx', key3: 'xx', key4: 'xx', key5: 'xx', key6: 'xx', key7: 'xx', key8: 'xx', key9: 'xx', key10: 'xx'},
  282. {id: 6, key1: 'xx', key2: 'xx', key3: 'xx', key4: 'xx', key5: 'xx', key6: 'xx', key7: 'xx', key8: 'xx', key9: 'xx', key10: 'xx'},
  283. ])
  284. const subplanCloseClick = () => {
  285. subplanModal.value = false
  286. }
  287. //分解子计划保存
  288. const subplanSaveClick = () => {
  289. tableData.value.forEach((ele)=>{
  290. if(ele.id==subPlanItem.id){
  291. ele.childrenList=tableSubplanData.value
  292. }
  293. })
  294. subplanModal.value = false
  295. // subPlanItem.value.childrenList=tableSubplanData.value
  296. }
  297. const relatedModal = ref(false)
  298. const rePlanid=ref('')
  299. const relatedModalShow = (row) => {
  300. relatedModal.value = true
  301. rePlanid.value=row.id
  302. getListByProjectId(row.projectId)
  303. }
  304. //保存获取工作时长
  305. const getWorkDays=async(row)=>{
  306. row.isEdit = false
  307. if(row?.planStartTime&&row?.planEndTime){
  308. const {error, code, data,msg} = await projectApi.getWorkDays( {
  309. startDate:row.planStartTime,
  310. endDate:row.planEndTime,
  311. })
  312. if (!error && code === 200) {
  313. if(data){
  314. row.planDays=data
  315. }
  316. }
  317. }
  318. }
  319. //关联回款
  320. const relation=async(row,type)=>{
  321. const {error, code, data,msg} = await projectApi.relationPlanAndReturned( {
  322. planId:rePlanid.value,
  323. returnedId:row.id,
  324. type
  325. })
  326. if (!error && code === 200) {
  327. window.$message.success(msg)
  328. getListByProjectId(row.projectId).then()
  329. }
  330. }
  331. //表格
  332. const tableRelatedColumn = [
  333. {key: 'returnedCondition', name: '回款条件', minWidth: '260'},
  334. {key: 'shouldReturnedTime', name: '应收回款时间', width: '160', align: 'center'},
  335. {key: 'shouldReturnedMoney', name: '应收回款金额(元)', width: '160', align: 'center'},
  336. {key: 'reminderUserName', name: '催款执行人', width: '160', align: 'center'},
  337. {key: 'action', name: '操作', width: '130', align: 'center'},
  338. ]
  339. const tableRelatedData = ref([
  340. {id: 1, key1: 'xx', key2: 'xx', key3: 'xx', key4: 'xx', key5: 'xx', key6: 'xx', key7: 'xx', key8: 'xx', key9: 'xx', key10: 'xx'},
  341. {id: 2, key1: 'xx', key2: 'xx', key3: 'xx', key4: 'xx', key5: 'xx', key6: 'xx', key7: 'xx', key8: 'xx', key9: 'xx', key10: 'xx'},
  342. {id: 3, key1: 'xx', key2: 'xx', key3: 'xx', key4: 'xx', key5: 'xx', key6: 'xx', key7: 'xx', key8: 'xx', key9: 'xx', key10: 'xx'},
  343. {id: 4, key1: 'xx', key2: 'xx', key3: 'xx', key4: 'xx', key5: 'xx', key6: 'xx', key7: 'xx', key8: 'xx', key9: 'xx', key10: 'xx'},
  344. {id: 5, key1: 'xx', key2: 'xx', key3: 'xx', key4: 'xx', key5: 'xx', key6: 'xx', key7: 'xx', key8: 'xx', key9: 'xx', key10: 'xx'},
  345. {id: 6, key1: 'xx', key2: 'xx', key3: 'xx', key4: 'xx', key5: 'xx', key6: 'xx', key7: 'xx', key8: 'xx', key9: 'xx', key10: 'xx'},
  346. ])
  347. //合同里程碑
  348. const getListByProjectId=async(projectId,planId)=>{
  349. const {error, code, data} = await contractApi.getListByProjectId({projectId,planId:rePlanid.value})
  350. if (!error && code === 200) {
  351. tableRelatedData.value=getArrValue(data)
  352. } else {
  353. tableRelatedData.value=[]
  354. }
  355. }
  356. const relatedCloseClick = () => {
  357. relatedModal.value = false
  358. }
  359. //修改合同
  360. const updatePlan=async(obj)=>{
  361. console.log(obj,'编辑');
  362. saveLoaing.value=true;
  363. const {error, code, data,msg} = await projectApi.updatePlan( obj)
  364. saveLoaing.value=false;
  365. if (!error && code === 200) {
  366. window.$message.success(msg)
  367. getPlanByProjectId()
  368. }
  369. }
  370. //批量保存
  371. const saveLoaing=ref(false)
  372. const saveClick = () => {
  373. console.log(tableData.value,'tableData.value');
  374. console.log( detailData.value,' detailData.value');
  375. updatePlan(detailData.value)
  376. }
  377. //取消并返回
  378. const goBackClick = () => {
  379. router.back()
  380. }
  381. </script>
  382. <style lang="scss">
  383. .hc-program-project-form-radio-group {
  384. position: relative;
  385. width: 100%;
  386. .el-radio-group, .el-radio-group .el-radio-button .el-radio-button__inner {
  387. width: 100%;
  388. }
  389. .el-radio-group .el-radio-button {
  390. flex: 1;
  391. }
  392. }
  393. </style>