form.vue 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481
  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" hasChildren="hasChildren1" children="childrenList">
  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)" :disabled="row?.isShowChildren==0">分解子计划</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. <template #extra>
  77. <el-button size="large" type="primary" @click="addplan()">新增</el-button>
  78. </template>
  79. <HcTable :isIndex="false" :column="tableSubplanColumn" :datas="tableSubplanData">
  80. <template #planTaskType="{row,index}">
  81. <el-select v-model="row.planTaskType" :disabled="!row.isEdit">
  82. <el-option label="选项1" value="1"/>
  83. <el-option label="选项2" value="2"/>
  84. </el-select>
  85. </template>
  86. <template #planTaskDesc="{row,index}">
  87. <el-input v-model="row.planTaskDesc" :disabled="!row.isEdit"/>
  88. </template>
  89. <template #planTarget="{row,index}">
  90. <el-input v-model="row.planTarget" :disabled="!row.isEdit"/>
  91. </template>
  92. <template #key7="{row,index}">
  93. <HcDatePicker :dates="[row.planStartTime,row.planEndTime]" @change="subbetweenTimeUpdate($event,row)" :disabled="!row.isEdit"/>
  94. </template>
  95. <template #planDays="{row,index}" >
  96. <!-- <el-input v-model="row.planDays" disabled/> -->
  97. <el-input v-model="row.planDays" disabled />
  98. </template>
  99. <template #key9="{row,index}">
  100. <el-input v-model="row.key9"/>
  101. </template>
  102. <template #action="{row,index}">
  103. <el-button size="small" type="success" v-if="row.isEdit" @click="getWorkDays(row)">保存</el-button>
  104. <el-button size="small" type="primary" v-else @click="row.isEdit = true">编辑</el-button>
  105. </template>
  106. </HcTable>
  107. </HcDialog>
  108. <!--关联回款里程碑-->
  109. <HcDialog bgColor="#ffffff" isToBody isTable :footer="false" :show="relatedModal" title="关联回款里程碑" widths="70%" @close="relatedCloseClick">
  110. <HcTable :column="tableRelatedColumn" :datas="tableRelatedData">
  111. <template #action="{row,index}">
  112. <el-button size="small" type="success" v-if="row.isRelation==1" @click="relation(row,0)">取消关联</el-button>
  113. <el-button size="small" type="primary" v-else @click="relation(row,1)">关联</el-button>
  114. </template>
  115. </HcTable>
  116. </HcDialog>
  117. </HcCard>
  118. </template>
  119. <script setup>
  120. import {useRouter, useRoute} from 'vue-router'
  121. import {onActivated, ref,watch} from "vue";
  122. import projectApi from '~api/program/project.js';
  123. import contractApi from '~api/project/project-contract.js';
  124. import {getArrValue,getObjValue} from "js-fast-way"
  125. import { getuserList} from "~api/other";
  126. import {useAppStore} from "~src/store";
  127. const useAppState = useAppStore();
  128. //初始变量
  129. const router = useRouter()
  130. const useRoutes = useRoute()
  131. const dataId = ref(useRoutes?.query?.id ?? '')
  132. const dataType = ref(useRoutes?.query?.type ?? '')
  133. //缓存页面被激活时
  134. onActivated(() => {
  135. dataId.value = useRoutes?.query?.id ?? ''
  136. dataType.value = useRoutes?.query?.type ?? ''
  137. getUserDict()
  138. if(dataType.value!=='add'){
  139. getPlanByProjectId()
  140. }else if(dataType.value=='add'){
  141. }
  142. })
  143. const detailData=ref({})
  144. const constructionData=ref([])
  145. const buildData=ref([])
  146. const supervisorUnitData=ref([])
  147. //获取详情
  148. const getPlanByProjectId=async()=>{
  149. const {error, code, data} = await projectApi.getPlanByProjectId({id: dataId.value})
  150. if (!error && code === 200) {
  151. console.log(getObjValue(data),'详情');
  152. detailData.value=getObjValue(data)
  153. constructionData.value=detailData.value?.constructUnit||[]
  154. buildData.value=detailData.value?.buildUnit||[]
  155. supervisorUnitData.value=detailData.value?.supervisorUnit||[]
  156. tabKey.value='construction'
  157. radioType.value=1
  158. // milestoneData.value=getArrValue(data)
  159. } else {
  160. // milestoneData.value=[]
  161. }
  162. }
  163. //获取所有员工
  164. const userList=ref([])
  165. //获取部门人员列表
  166. const getUserDict=async()=>{
  167. const {error, code, data} = await getuserList({tenantId:useAppState.tenantId})
  168. if (!error && code === 200) {
  169. userList.value = getArrValue(data)
  170. } else {
  171. userList.value = []
  172. }
  173. }
  174. //类型tab数据和相关处理
  175. const tabKey = ref('')
  176. const tabTab = ref([
  177. {key: 'build', name: '施工单位成本'},
  178. {key: 'supervision', name: '监理单位成本'},
  179. {key: 'construction', name: '建设单位成本'}
  180. ]);
  181. const originTableData=ref([ {}])
  182. const tabChange = ({key}) => {
  183. tabKey.value = key
  184. if(key=='supervision'){
  185. tableData.value=supervisorUnitData.value&&supervisorUnitData.value[radioType.value]?.length>0?supervisorUnitData.value:originTableData.value
  186. detailData.value.supervisorUnit=tableData.value
  187. }else if(key=='construction'){
  188. tableData.value=constructionData.value[radioType.value]?.length>0?constructionData.value:originTableData.value
  189. detailData.value.constructUnit=tableData.value
  190. }else if(key=='bulid'){
  191. tableData.value=buildData.value[radioType.value]?.length>0?buildData.value:originTableData.value
  192. detailData.value.buildUnit=tableData.value
  193. }
  194. }
  195. const radioType = ref('')
  196. //深度监听
  197. watch(() => [
  198. radioType.value,
  199. tabKey.value
  200. ], ([radioType]) => {
  201. if(tabKey.value==='construction'){
  202. console.log(constructionData.value[radioType],'constructionData.value');
  203. tableData.value=constructionData.value[radioType]
  204. }else if(tabKey.value==='build'){
  205. console.log(buildData.value[radioType],'buildData.value');
  206. tableData.value=buildData.value[radioType]
  207. }else if(tabKey.value==='supervision'){
  208. console.log(supervisorUnitData.value[radioType],'supervisorUnitData.value');
  209. tableData.value=supervisorUnitData.value[radioType]
  210. }
  211. }, {deep: true})
  212. //表格
  213. const tableColumn = [
  214. {key: 'projectProcessValue', name: '项目环节', width: '160', align: 'center'},
  215. {key: 'budgetTypeValue', name: '预算类型', width: '160', align: 'center'},
  216. {key: 'taskDetailValue', name: '任务明细', width: '160', align: 'center'},
  217. {key: 'planTaskType', name: '任务类型', width: '160', align: 'center'},
  218. {key: 'planTaskDesc', name: '任务描述', minWidth: '200', align: 'center', isTooltip: true},
  219. {key: 'planTarget', name: '完成指标', minWidth: '200', align: 'center', isTooltip: true},
  220. {key: 'key7', name: '计划起止日期', width: '280', align: 'center'},
  221. {key: 'planDays', name: '预计工作量(小数/整数/天)', width: '160', align: 'center'},
  222. {key: 'postTypeValue', name: '投入岗位类型(日单价)', width: '160', align: 'center'},
  223. {key: 'staffCount', name: '投入人员数量', width: '160', align: 'center'},
  224. {key: 'budgetStaffCost', name: '预计人工成本(元)', width: '160', align: 'center'},
  225. {key: 'budgetTravelExpense', name: '预计差旅费(元)', width: '160', align: 'center'},
  226. {key: 'outsourceUnitPrice', name: '外包单价', width: '160', align: 'center'},
  227. {key: 'outsourcePeopleCount', name: '外包数量', width: '160', align: 'center'},
  228. {key: 'outsourceCountMoney', name: '外包金额', width: '160', align: 'center'},
  229. {key: 'otherBudgetMoney', name: '其他预算额', width: '160', align: 'center'},
  230. {key: 'budgetCountMoney', name: '总预算金额', width: '160', align: 'center'},
  231. {key: 'budgetRemark ', name: '测算备注', width: '160', align: 'center'},
  232. {key: 'returnedValue', name: '关联回款里程碑', minWidth: '200', isTooltip: true},
  233. {key: 'action', name: '操作', width: '280', align: 'center', fixed: 'right'},
  234. ]
  235. const tableData = ref([
  236. ])
  237. //表格行样式
  238. const tableRowStyle = ({row, rowIndex}) => {
  239. if (row.taskFinishedStatus === 1) {
  240. return {
  241. 'background-color': '#E99D42',
  242. '--el-fill-color-lighter': '#E99D42',
  243. '--el-table-row-hover-bg-color': '#E99D42'
  244. }
  245. } else if (row.taskFinishedStatus === 2 ) {
  246. return {
  247. 'background-color': '#7e9559',
  248. '--el-fill-color-lighter': '#7e9559',
  249. '--el-table-row-hover-bg-color': '#7e9559'
  250. }
  251. }
  252. }
  253. //日期时间被选择
  254. const betweenTime = ref(null)
  255. const betweenTimeUpdate = ({arr, query},item) => {
  256. item.planStartTime=arr[0]
  257. item.planEndTime=arr[1]
  258. item.betweenTime=arr
  259. }
  260. //日期时间被选择
  261. const subbetweenTime = ref(null)
  262. const subbetweenTimeUpdate = ({arr, query},item) => {
  263. console.log(item,'item');
  264. item.planStartTime=arr[0]
  265. item.planEndTime=arr[1]
  266. item.subbetweenTime=arr
  267. }
  268. const subplanModal = ref(false)
  269. const subPlanItem=ref({})
  270. const subplanModalShow = (row) => {
  271. console.log(row,'row');
  272. subPlanItem.value=row
  273. subplanModal.value = true
  274. tableSubplanData.value=row?.childrenList||[]
  275. }
  276. //表格
  277. const tableSubplanColumn = [
  278. {key: 'projectProcessValue', name: '项目环节', width: '160', align: 'center'},
  279. {key: 'budgetTypeValue', name: '预算类型', width: '160', align: 'center'},
  280. {key: 'taskDetailValue', name: '任务明细', width: '160', align: 'center'},
  281. {key: 'planTaskType', name: '任务类型', width: '160', align: 'center'},
  282. {key: 'planTaskDesc', name: '任务描述', minWidth: '200', align: 'center', isTooltip: true},
  283. {key: 'planTarget', name: '完成指标', minWidth: '200', align: 'center', isTooltip: true},
  284. {key: 'key7', name: '计划起止日期', width: '280', align: 'center'},
  285. {key: 'planDays', name: '预计工作量(小数/整数/天)', width: '160', align: 'center'},
  286. {key: 'action', name: '操作', width: '80', align: 'center', fixed: 'right'},
  287. ]
  288. const tableSubplanData = ref([
  289. ])
  290. const subplanCloseClick = () => {
  291. subplanModal.value = false
  292. }
  293. //分解子计划保存
  294. const subplanSaveClick = () => {
  295. tableData.value.forEach((ele)=>{
  296. if(ele.id==subPlanItem.value.id){
  297. ele.childrenList=tableSubplanData.value
  298. }
  299. })
  300. subplanModal.value = false
  301. console.log(tableData.value,'tableData.value');
  302. // subPlanItem.value.childrenList=tableSubplanData.value
  303. }
  304. const addplan=()=>{
  305. tableSubplanData.value.push({
  306. projectProcessValue:subPlanItem.value?.projectProcessValue,
  307. budgetTypeValue:subPlanItem.value?.budgetTypeValue,
  308. taskDetailValue:subPlanItem.value?.taskDetailValue,
  309. planTaskType:subPlanItem.value?.planTaskType,
  310. isEdit:true
  311. })
  312. }
  313. const relatedModal = ref(false)
  314. const rePlanid=ref('')
  315. const relatedModalShow = (row) => {
  316. relatedModal.value = true
  317. rePlanid.value=row.id
  318. getListByProjectId(row.projectId)
  319. }
  320. //保存获取工作时长
  321. const getWorkDays=async(row)=>{
  322. row.isEdit = false
  323. if(row?.planStartTime&&row?.planEndTime){
  324. const {error, code, data,msg} = await projectApi.getWorkDays( {
  325. startDate:row.planStartTime,
  326. endDate:row.planEndTime,
  327. })
  328. if (!error && code === 200) {
  329. if(data){
  330. row.planDays=data
  331. }
  332. }
  333. }
  334. }
  335. //关联回款
  336. const relation=async(row,type)=>{
  337. const {error, code, data,msg} = await projectApi.relationPlanAndReturned( {
  338. planId:rePlanid.value,
  339. returnedId:row.id,
  340. type
  341. })
  342. if (!error && code === 200) {
  343. window.$message.success(msg)
  344. getListByProjectId(row.projectId).then()
  345. }
  346. }
  347. //表格
  348. const tableRelatedColumn = [
  349. {key: 'returnedCondition', name: '回款条件', minWidth: '260'},
  350. {key: 'shouldReturnedTime', name: '应收回款时间', width: '160', align: 'center'},
  351. {key: 'shouldReturnedMoney', name: '应收回款金额(元)', width: '160', align: 'center'},
  352. {key: 'reminderUserName', name: '催款执行人', width: '160', align: 'center'},
  353. {key: 'action', name: '操作', width: '130', align: 'center'},
  354. ]
  355. const tableRelatedData = ref([
  356. {id: 1, key1: 'xx', key2: 'xx', key3: 'xx', key4: 'xx', key5: 'xx', key6: 'xx', key7: 'xx', key8: 'xx', key9: 'xx', key10: 'xx'},
  357. {id: 2, key1: 'xx', key2: 'xx', key3: 'xx', key4: 'xx', key5: 'xx', key6: 'xx', key7: 'xx', key8: 'xx', key9: 'xx', key10: 'xx'},
  358. {id: 3, key1: 'xx', key2: 'xx', key3: 'xx', key4: 'xx', key5: 'xx', key6: 'xx', key7: 'xx', key8: 'xx', key9: 'xx', key10: 'xx'},
  359. {id: 4, key1: 'xx', key2: 'xx', key3: 'xx', key4: 'xx', key5: 'xx', key6: 'xx', key7: 'xx', key8: 'xx', key9: 'xx', key10: 'xx'},
  360. {id: 5, key1: 'xx', key2: 'xx', key3: 'xx', key4: 'xx', key5: 'xx', key6: 'xx', key7: 'xx', key8: 'xx', key9: 'xx', key10: 'xx'},
  361. {id: 6, key1: 'xx', key2: 'xx', key3: 'xx', key4: 'xx', key5: 'xx', key6: 'xx', key7: 'xx', key8: 'xx', key9: 'xx', key10: 'xx'},
  362. ])
  363. //合同里程碑
  364. const getListByProjectId=async(projectId,planId)=>{
  365. const {error, code, data} = await contractApi.getListByProjectId({projectId,planId:rePlanid.value})
  366. if (!error && code === 200) {
  367. tableRelatedData.value=getArrValue(data)
  368. } else {
  369. tableRelatedData.value=[]
  370. }
  371. }
  372. const relatedCloseClick = () => {
  373. relatedModal.value = false
  374. }
  375. //修改合同
  376. const updatePlan=async(obj)=>{
  377. console.log(obj,'编辑');
  378. saveLoaing.value=true;
  379. const {error, code, data,msg} = await projectApi.updatePlan( obj)
  380. saveLoaing.value=false;
  381. if (!error && code === 200) {
  382. window.$message.success(msg)
  383. getPlanByProjectId()
  384. }
  385. }
  386. const isEmptyObj=(obj)=> {
  387. let arr = Object.keys(obj);
  388. return(arr.length == 0)
  389. }
  390. //批量保存
  391. const saveLoaing=ref(false)
  392. const saveClick = () => {
  393. console.log(tableData.value,'tableData.value');
  394. console.log( detailData.value,' detailData.value');
  395. //取消空对象提交
  396. if(detailData.value?.supervisorUnit?.length>0){
  397. let suisnullObj= isEmptyObj(detailData.value?.supervisorUnit[0])
  398. if(suisnullObj===true){
  399. detailData.value.supervisorUnit=null
  400. }
  401. }
  402. if(detailData.value?.constructUnit?.length>0){
  403. let coisnullObj= isEmptyObj(detailData.value?.constructUnit[0])
  404. if(coisnullObj){
  405. detailData.value.constructUnit=null
  406. }
  407. }
  408. if(detailData.value?.buildUnit?.length>0){
  409. let buisnullObj= isEmptyObj(detailData.value?.buildUnit[0])
  410. if(buisnullObj){
  411. detailData.value.buildUnit=null
  412. }
  413. }
  414. updatePlan(detailData.value)
  415. }
  416. //取消并返回
  417. const goBackClick = () => {
  418. router.back()
  419. }
  420. </script>
  421. <style lang="scss">
  422. .hc-program-project-form-radio-group {
  423. position: relative;
  424. width: 100%;
  425. .el-radio-group, .el-radio-group .el-radio-button .el-radio-button__inner {
  426. width: 100%;
  427. }
  428. .el-radio-group .el-radio-button {
  429. flex: 1;
  430. }
  431. }
  432. </style>