targetDetail.vue 39 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954
  1. <template>
  2. <div style="height: 100%;">
  3. <van-nav-bar title="目标详情" left-text="返回" left-arrow @click-left="$route_back" />
  4. <div class="all-box">
  5. <template v-if="targetDetail.visible==1">
  6. <header class="header">
  7. <div class="flex-box">
  8. <!-- 目标综合状态 1-未开始 2-负责人未接收 3-运行中 4-已延期 5-已达标(进度大于等于100) 6-已结束 -->
  9. <!-- <span class="status" v-if="targetDetail.composite_state==1">未开始</span>
  10. <span class="status" v-if="targetDetail.composite_state==2">未接收</span>
  11. <span class="status" v-if="targetDetail.composite_state==3">进行中</span>
  12. <span class="status" v-if="targetDetail.composite_state==4">已延期</span>
  13. <span class="status" v-if="targetDetail.composite_state==5">已达标</span> -->
  14. <span class="status" v-if="targetDetail.composite_state==6">已结束</span>
  15. <div class="flex-1" style="font-weight: 600;padding: 0 0.2rem;padding-left: 0.1rem;">{{targetDetail.name}}</div>
  16. <van-icon name="weapp-nav" class="fontColorB" v-if="targetDetail.id" @click="isShowbelong=true"/>
  17. </div>
  18. <div class="flex-box-ce" style="margin: 0.16rem 0;">
  19. <div class="progress"><div class="progress-inner" :class="{bjYellow:targetDetail.risk_level==2,bjRed:targetDetail.risk_level==3}" :style="{width:targetDetail.process>100? '100%':targetDetail.process+'%'}"></div></div>
  20. <span style="padding-left: 0.1rem;font-size: 0.24rem;" class="blue" :class="{orange:targetDetail.risk_level==2,red:targetDetail.risk_level==3}">{{ targetDetail.process }}%</span>
  21. <template v-if="targetDetail.process != targetDetail.process_last">
  22. <span v-if="targetDetail.isUpdatePro>0" class="green" style="font-size: 12px;"><van-icon name="back-top" />{{ targetDetail.updatePro }}%</span>
  23. <span v-else class="red" style="font-size: 12px;"><van-icon name="down" />{{ targetDetail.updatePro }}%</span>
  24. </template>
  25. <div class="flex-1"></div>
  26. <div class="orange" v-if="targetDetail.score>=0">{{targetDetail.score}}</div>
  27. </div>
  28. <div class="flex-box-ce fontColorC" style="font-size: 0.26rem;">
  29. <span>{{$getEmployeeMapItem(targetDetail.owner_id).name}}</span>
  30. <span style="margin: 0 0.1rem;padding: 0 0.1rem;border-left: 1px solid #f1f1f1;border-right: 1px solid #f1f1f1;">
  31. {{targetDetail.belong_type==2? (dept_tree[targetDetail.dept_id]? dept_tree[targetDetail.dept_id].name:''):getBelongType(targetDetail.belong_type).name}}
  32. </span>
  33. <span class="flex-1">{{targetDetail.year}}年 {{targetDetail.dateStr}}</span>
  34. </div>
  35. <template v-if="targetDetail.kr_id">
  36. <div class="flex-box" v-if="targetDetail.p_kr" style="font-size: 0.26rem;margin-top: 0.2rem;border-top: 1px solid #f1f1f1;padding-top: 0.2rem;height: 0.36rem;">
  37. <span class="fontColorC">对齐目标:</span>
  38. <div class="flex-1 font-flex-word">{{targetDetail.p_kr.name}}</div>
  39. </div>
  40. </template>
  41. <template v-else-if="targetDetail.o_id">
  42. <div class="flex-box" v-if="targetDetail.p_objectives" style="font-size: 0.26rem;margin-top: 0.2rem;border-top: 1px solid #f1f1f1;padding-top: 0.2rem;height: 0.36rem;">
  43. <span class="fontColorC">对齐目标:</span>
  44. <div class="flex-1 font-flex-word">{{targetDetail.p_objectives.name}}</div>
  45. </div>
  46. </template>
  47. </header>
  48. <van-tabs v-model="tabActive" class="shadow"><van-tab :title="item" v-for="(item, index) in tabs" :key="index"></van-tab></van-tabs>
  49. <div class="scroller">
  50. <scroller ref="scroller">
  51. <template v-if="tabActive == 0">
  52. <div class="list-box" v-for="(item, index) in krsList" :key="index" @click="openDetail(item,2)">
  53. <div class="clamp2" style="margin-bottom: 5px;">{{item.name}}</div>
  54. <div class="flex-box-ce fontColorC" style="font-size: 0.26rem;">
  55. <span>{{$getEmployeeMapItem(item.owner_id).name}}</span>
  56. <span class="padding-l-r flex-box-ce">{{item.weight}}%</span>
  57. <span class="flex-box-ce" style="padding-right: 0.1rem;border-right: 1px solid #f1f1f1;margin-right: 0.1rem;"><van-icon name="star" class="yellow" />{{item.confident}}分</span>
  58. <van-circle :key="item.id" layer-color="#E5E9F2" v-model="item.currentRate" :color="item.risk_level==2? '#FF9600':item.risk_level==3? '#f56c6c':' #2879ff'" :rate="item.process" :stroke-width="120" size="20px"/>
  59. <span style="padding-left: 5px;font-size: 0.24rem;" class="blue" :class="{orange:item.risk_level==2,red:item.risk_level==3}">{{ item.process }}%</span>
  60. </div>
  61. </div>
  62. <div class="addKr fontColorC" @click="isShowAddKr=true" v-if="targetDetail.can_edit">+ 添加 KR</div>
  63. </template>
  64. <template v-if="tabActive == 1">
  65. <div class="list-box" v-for="(item, index) in krsListAll" :key="index">
  66. <div class="flex-box-ce" style="margin-bottom: 0.2rem;border-bottom: 1px solid #f1f1f1;padding-bottom: 0.2rem;">
  67. <div class="blue" style="font-size: 0.26rem;font-weight: 600;margin-right: 0.1rem;">KR{{index+1}}</div>
  68. <div class="flex-1 font-flex-word" style="padding-right: 0.2rem;font-size: 0.3rem;">{{item.name}}</div>
  69. <!-- <span style="font-size: 0.26rem;" v-if="item.plans.length>0">计划任务<span class="blue">{{returnSum(item.plans)}}</span>/<span>{{item.plans.length}}</span></span> -->
  70. </div>
  71. <div style="padding:0 0.1rem;">
  72. <div class="flex-box-ce btns" v-if="item.can_edit">
  73. <span @click="addTask(item,1)">+ 添加</span>
  74. <span @click="addTask(item,2)">+ 关联</span>
  75. </div>
  76. <template v-if="item.projects.length>0">
  77. <div class="flex-box" style="margin-top: 0.2rem;" v-for="(task_item, index3) in item.projects" :key="task_item.id" @click="openDetail(task_item,3)">
  78. <van-icon name="paid" class="blue" style="margin-right: 0.2rem;position: relative;top: 3px;"/>
  79. <div class="flex-1">
  80. <div class="clamp2" style="margin-bottom: 5px;font-size: 0.28rem;">{{task_item.name}}</div>
  81. <div class="flex-box-ce fontColorC" style="font-size: 0.26rem;">
  82. <span style="padding-right: 0.1rem;border-right: 1px solid #f1f1f1;margin-right: 0.1rem;">{{$getEmployeeMapItem(task_item.owner_id).name}}</span>
  83. <span class="flex-1">{{$moment(task_item.end_date).format('MM/DD')}} 截止</span>
  84. </div>
  85. </div>
  86. </div>
  87. </template>
  88. <template v-if="item.plans.length>0">
  89. <div class="flex-box" style="margin-top: 0.2rem;" v-for="(task_item, index2) in item.plans" :key="index2" @click="openDetail(task_item,1)">
  90. <van-icon :name="taskStatus(task_item.composite_state).icon"
  91. class="blue" style="margin-right: 0.2rem;position: relative;top: 3px;"
  92. :class="(task_item.day<=0&&$moment(task_item.end_date).format('YYYY-MM-DD')!=$moment().format('YYYY-MM-DD')) ? 'red':''"
  93. />
  94. <div class="flex-1">
  95. <div class="clamp2" style="margin-bottom: 5px;font-size: 0.28rem;">{{task_item.name}}</div>
  96. <div class="flex-box-ce fontColorC" style="font-size: 0.26rem;">
  97. <span style="padding-right: 0.1rem;border-right: 1px solid #f1f1f1;margin-right: 0.1rem;">{{$getEmployeeMapItem(task_item.owner_id).name}}</span>
  98. <span class="flex-1">{{$moment(task_item.end_date).format('MM/DD HH:mm')}} 截止</span>
  99. <template v-if="task_item.statistics.plan_total">
  100. <van-icon name="orders-o" />
  101. <span style="font-size: 0.26rem;">
  102. <span class="blue">{{task_item.statistics.plan_finish}}</span>/<span>{{task_item.statistics.plan_total}}</span>
  103. </span>
  104. </template>
  105. </div>
  106. </div>
  107. </div>
  108. </template>
  109. <div v-if="item.projects.length==0&&item.plans.length==0" style="text-align: center;margin: 0.2rem 0;font-size: 0.28rem;" class="fontColorC">用“项目/任务”推动目标达成,合理规划安排工作</div>
  110. </div>
  111. </div>
  112. </template>
  113. <template v-if="tabActive == 3">
  114. <div style="height: 100%;background-color: #fff;padding: 0.2rem;border-top: 1px solid #f1f1f1;">
  115. <div class="flex-box-end fontColorB" style="font-size: 0.3rem;" v-if="targetDetail.can_edit"><span @click="isUpdate=!isUpdate">{{isUpdate==false? '编辑':'完成'}}</span></div>
  116. <div class="flex-box-ce" style="margin-bottom: 0.2rem;">
  117. <div class="flex-1">
  118. <div class="user-title">发布人</div>
  119. <div class="flex-box-v" style="text-align: center;width: 1.06rem">
  120. <userImage :id="targetDetail.publisher_id" :user_name="$getEmployeeMapItem(targetDetail.publisher_id).name" width="0.7rem" height="0.7rem"></userImage>
  121. <div style="font-size: 0.26rem;padding: 0.1rem 0;" class="font-flex-word">{{ $getEmployeeMapItem(targetDetail.publisher_id).name }}</div>
  122. </div>
  123. </div>
  124. <div class="flex-1">
  125. <div class="user-title">负责人</div>
  126. <div class="flex-box-ce">
  127. <div style="text-align: center;width: 1.06rem">
  128. <userImage :img_url="targetDetail.owner_userInfo.img_url" :user_name="targetDetail.owner_userInfo.name" width="0.7rem" height="0.7rem"></userImage>
  129. <div style="font-size: 0.26rem;padding: 0.1rem 0;" class="font-flex-word">{{ targetDetail.owner_userInfo.name }}</div>
  130. </div>
  131. <div class="zj" v-if="isUpdate" @click="openSelectUser(1)">转交</div>
  132. </div>
  133. </div>
  134. </div>
  135. <div style="margin-bottom: 0.2rem;">
  136. <div class="user-title">参与人员</div>
  137. <div class="flex-box-ce flex-d-wrap">
  138. <div class="flex-box-v" style="margin-right: 0.2rem;text-align: center;margin-bottom: 0.2rem;position: relative;" v-for="(item, index) in targetDetail.joiner_employee_items" :key="index" >
  139. <userImage :img_url="item.img_url" :user_name="item.name" width="0.7rem" height="0.7rem"></userImage>
  140. <div style="font-size: 0.26rem;padding: 0.1rem 0;width: 1.06rem" class="font-flex-word">{{ item.name }}</div>
  141. <!-- <van-icon name="clear" class="delete-user red" /> -->
  142. <div class="zj2" v-if="isUpdate" @click="joinerUpdate(false,item.id)">删除</div>
  143. </div>
  144. <div class="addUser" v-if="isUpdate" @click="openSelectUser(2)">+</div>
  145. </div>
  146. </div>
  147. <div style="margin-bottom: 0.2rem;">
  148. <div class="user-title">关注人员</div>
  149. <div class="flex-box-ce flex-d-wrap">
  150. <div class="flex-box-v" style="margin-right: 0.2rem;text-align: center;margin-bottom: 0.2rem;" v-for="(item, index) in targetDetail.follower_employee_items" :key="index">
  151. <userImage :img_url="item.img_url" :user_name="item.name" width="0.7rem" height="0.7rem"></userImage>
  152. <div style="font-size: 0.26rem;padding: 0.1rem 0;width: 1.06rem" class="font-flex-word">{{ item.name }}</div>
  153. <div class="zj2" v-if="isUpdate" @click="deleteFollower(false,item.id)">删除</div>
  154. </div>
  155. <div class="addUser" v-if="isUpdate" @click="openSelectUser(3)">+</div>
  156. </div>
  157. </div>
  158. </div>
  159. </template>
  160. <template v-if="tabActive == 2">
  161. <div style="padding: 0.2rem;background-color: #fff;border-top: 1px solid #f1f1f1;">
  162. <div class="flex-box-ce flex-d-wrap">
  163. <div class="search-item" @click="targetType = item.id" v-for="(item, index) in targetTypeArr" :key="index" :class="item.id == targetType ? 'searchActive' : ''">{{ item.name }}</div>
  164. <div class="flex-1"></div>
  165. <div class="add-jz" v-if="targetType==1&&isOperation" @click="updateProgress">更新完成度</div>
  166. </div>
  167. <template v-if="targetType==1">
  168. <div v-for="(item, index) in processList" :key="index" style="margin-top: 0.24rem;">
  169. <div class="flex-box-ce" style="padding-bottom: 0.2rem;">
  170. <span class="biaos" :class="{ biaos2: item.risk_level == 2, biaos3: item.risk_level == 3 }"></span>
  171. <div class="black flex-1" style="font-weight: 700;font-size: 16px;">
  172. {{ item.process }}%
  173. <template v-if="item.process != item.process_last">
  174. <span v-if="item.isUpdatePro>0" class="green" style="font-size: 12px;"><van-icon name="back-top" />{{ item.updatePro }}%</span>
  175. <span v-else class="red" style="font-size: 12px;"><van-icon name="down" />{{ item.updatePro }}%</span>
  176. </template>
  177. </div>
  178. <span class="fontColorC" style="font-size: 0.26rem;">{{ item.create_time }}</span>
  179. </div>
  180. <div class="flex-box o-content">
  181. <div v-if="item.content" class="flex-1 fontColorB">{{ item.content }}</div>
  182. <div v-else class="flex-1 fontColorD">暂无内容</div>
  183. <div class="fontColorC" style="font-size: 0.24rem;" @click="compileContent(item)" v-if="isOperation"> <van-icon name="edit" /> 编辑</div>
  184. </div>
  185. </div>
  186. <div style="text-align: center;margin: 1rem 0;" class="fontColorC" v-if="processList.length==0">目标进展一目了然?赶快更新完成度吧</div>
  187. </template>
  188. <template v-if="targetType==2">
  189. <div v-for="(item, index) in processData.kr" :key="index" style="margin-top: 0.24rem;">
  190. <div class="flex-box-ce" style="padding-bottom: 0.2rem;">
  191. <span class="biaos" :class="{biaos2:item.process_info&&item.process_info.risk_level==2,biaos3:item.process_info&&item.process_info.risk_level==3}"></span>
  192. <span class="okr-index">KR{{ index + 1 }}</span>
  193. <div class="font-flex-word fontColorC flex-1" style="padding-right:0.2rem;font-size: 0.28rem;">{{item.name}}</div>
  194. <div class="black" style="font-weight: 700;font-size: 16px;">
  195. {{ item.process }}%
  196. <template v-if="item.process != item.process_last">
  197. <span v-if="item.isUpdatePro>0" class="green" style="font-size: 12px;"><van-icon name="back-top" />{{ item.updatePro }}%</span>
  198. <span v-else class="red" style="font-size: 12px;"><van-icon name="down" />{{ item.updatePro }}%</span>
  199. </template>
  200. </div>
  201. </div>
  202. <div v-if="item.process_info">
  203. <div class="o-content fontColorB">
  204. <span v-if="item.process_info.content">{{item.process_info.content}}</span>
  205. <span v-else class="fontColorD" style="font-size: 13px;">暂未更新</span>
  206. </div>
  207. <div style="padding-left: 0.34rem;font-size: 12px;" class="fontColorC">{{item.process_info.create_time}}</div>
  208. </div>
  209. <div class="pre fontColorD" v-else style="font-size: 13px;padding: 5px 0.5rem;">暂未更新</div>
  210. </div>
  211. <div style="text-align: center;margin: 1rem 0;" class="fontColorC" v-if="processData.kr==0">暂无进展</div>
  212. </template>
  213. <template v-if="targetType==3">
  214. <div v-for="(item, index) in processData.objectives" :key="index" style="margin-top: 0.24rem;">
  215. <div class="flex-box-ce" style="padding-bottom: 0.2rem;">
  216. <span class="biaos" :class="{biaos2:item.process_info&&item.process_info.risk_level==2,biaos3:item.process_info&&item.process_info.risk_level==3}"></span>
  217. <div class="huan"><span></span></div>
  218. <div class="font-flex-word fontColorC flex-1" style="padding-right:0.2rem;font-size: 0.28rem;">{{item.name}}</div>
  219. <div class="black" style="font-weight: 700;font-size: 16px;">
  220. {{ item.process }}%
  221. <template v-if="item.process != item.process_last">
  222. <span v-if="item.isUpdatePro>0" class="green" style="font-size: 12px;"><van-icon name="back-top" />{{ item.updatePro }}%</span>
  223. <span v-else class="red" style="font-size: 12px;"><van-icon name="down" />{{ item.updatePro }}%</span>
  224. </template>
  225. </div>
  226. </div>
  227. <div v-if="item.process_info">
  228. <div class="o-content fontColorB">
  229. <span v-if="item.process_info.content">{{item.process_info.content}}</span>
  230. <span v-else class="fontColorD" style="font-size: 13px;">暂未更新</span>
  231. </div>
  232. <div style="padding-left: 0.34rem;font-size: 12px;" class="fontColorC">{{item.process_info.create_time}}</div>
  233. </div>
  234. <div class="pre fontColorD" v-else style="font-size: 13px;padding: 5px 0.5rem;">暂未更新</div>
  235. </div>
  236. <div style="text-align: center;margin: 1rem 0;" class="fontColorC" v-if="processData.objectives==0">暂无进展</div>
  237. </template>
  238. </div>
  239. </template>
  240. <div style="height: 3rem;"></div>
  241. </scroller>
  242. <footer class="footer">
  243. <div class="flex-box-ce" v-if="isOperation">
  244. <div class="btn flex-1" @click="updateProgress">更新完成度</div>
  245. <van-icon name="chat-o" @click="openCommunication" style="margin-left: 0.2rem;font-size:0.6rem;" class="fontColorC"/>
  246. </div>
  247. <div v-else class="fontColorC gt" @click="openCommunication">有事沟通,可@Ta</div>
  248. </footer>
  249. </div>
  250. </template>
  251. <van-row type="flex" justify="space-around" class="c" v-else>
  252. <van-col span="24">
  253. <div style="text-align: center;margin-top: 2rem;margin-bottom: 1rem;">
  254. <img src='static/images/noPeople.png' style="width: 3.5rem;"/>
  255. </div>
  256. <p class="text_center fontColorC">暂无查看权限</p>
  257. </van-col>
  258. </van-row>
  259. </div>
  260. <van-dialog v-model="isShowProcess" show-cancel-button :beforeClose="subContent">
  261. <van-cell-group>
  262. <van-field v-model="processInfo.content" rows="4" class="textarea-box" type="textarea" maxlength="500" placeholder="请输入进展与障碍" show-word-limit/>
  263. </van-cell-group>
  264. </van-dialog>
  265. <!-- 进度 -->
  266. <Progress :visible.sync="isShwoUpdateProgress" :progressData="progressData" @confirm="update"></Progress>
  267. <!-- 添加KR -->
  268. <AddKr :o_id="Number(o_id)" :isShowWeight="false" :visible.sync="isShowAddKr" @confirm="confirmAddkr"></AddKr>
  269. <!-- 详情 -->
  270. <van-action-sheet v-model="isShowbelong" :actions="scopeArr" @select="activebelong" cancel-text="取消" close-on-click-action/>
  271. <!-- 修改基本信息 -->
  272. <UpdateTarget :visible.sync="isShwoUpdateDetail" :targetDetail="targetDetail" :o_id="Number(o_id)" @confirm="getTargetDateil"></UpdateTarget>
  273. <!-- 关联任务 -->
  274. <TaskSearch :visible.sync="isShwoTaskSearch" @confirm="ActiveRelevanceTask"></TaskSearch>
  275. <!-- 人员选择 -->
  276. <EmployeeSelector :multi="false" :isRequired="true" key="selected_user" title="选择人员" :visible.sync="selectUser" @confirm="confirmUser" :can_select_dept="false" :selected.sync="selected_user"></EmployeeSelector>
  277. <!-- 可见范围 -->
  278. <van-action-sheet v-model="isShowV" :actions="vArr" @select="activevArr" cancel-text="取消" close-on-click-action/>
  279. <!-- 关联KR -->
  280. <TargetSearch :visible.sync="isShwoKrSearch" :showSelectType="2" @confirm="ActiveRelevanceKr"></TargetSearch>
  281. </div>
  282. </template>
  283. <script>
  284. import Vue from 'vue';
  285. import { Tab, Tabs, Circle,ActionSheet } from 'vant';
  286. import UpdateTarget from '@/okr/components/public/UpdateTarget';
  287. import TaskSearch from '@/okr/components/public/TaskSearch';
  288. import AddKr from '@/okr/components/public/AddKr';
  289. import EmployeeSelector from '@/components/EmployeeSelector';
  290. import Progress from '@/okr/components/public/Progress';
  291. import TargetSearch from '@/okr/components/public/TargetSearch';
  292. import {taskStatus,getBelongType,getDateStr,getOperation} from '@/okr/utils/auth';
  293. Vue.use(Tabs).use(Tab).use(Circle).use(ActionSheet);
  294. export default {
  295. components:{UpdateTarget,AddKr,TaskSearch,EmployeeSelector,Progress,TargetSearch},
  296. data() {
  297. return {
  298. targetDetail:{visible:1},
  299. processInfo:{},
  300. getBelongType:getBelongType,
  301. taskStatus:taskStatus,
  302. userInfo: this.$userInfo(),
  303. tabActive: 0,
  304. tabs: ['OKRs', '计划', '进展','成员'],
  305. targetTypeArr: [{ name: '目标进展', id: 1 }, { name: 'KR进展', id: 2 }, { name: '子目标进展', id: 3 }],
  306. targetType: 1,
  307. isShowbelong:false,
  308. isShwoTaskSearch:false,
  309. scopeArr:[{value: 0, name:'关注目标', disabled: false},{value: 1, name:'编辑基本信息',disabled: false},{value: 2,name:'结束目标',disabled: false},{value: 4,name:'复制目标',disabled: false},{value: 3,name:'删除目标',color: '#f56c6c',disabled: false}],
  310. processList: [],
  311. processData:{kr:[],objectives:[]},
  312. isShwoUpdateDetail:false,
  313. isShowAddKr:false,
  314. userId:0,
  315. krName:'',
  316. weight:0,
  317. o_id:0,
  318. kr_id:0,
  319. krsList:[],
  320. krsListAll:[],//包括任务
  321. isFollower:false,//是否关注
  322. isUpdate:false,
  323. selectUser:false,
  324. selected_user: { dept: [], employee: [] } ,//传入已选部门
  325. isShwoUpdateProgress:false,
  326. progressData:{},
  327. isShowProcess:false,
  328. isOperation:true,
  329. dept_tree:{},
  330. // 可见范围
  331. isShowV:false,
  332. vArr:[{value: 1,name: '添加任务'},{value: 2,name: '添加项目'}],
  333. addTaskIndex:1,
  334. selectKr:{},
  335. isShwoKrSearch:false,
  336. };
  337. },
  338. watch:{
  339. isFollower(val){
  340. if(val){
  341. this.scopeArr.forEach(item=>{
  342. if(item.value==0){item.name='取消关注'}
  343. })
  344. }else{
  345. this.scopeArr.forEach(item=>{
  346. if(item.value==0){item.name='关注目标'}
  347. })
  348. }
  349. },
  350. tabActive(val){
  351. if(val==0){
  352. this.getKrList();
  353. }else if(val==1){
  354. this.getkrTaskList();
  355. }else if(val==2){
  356. this.getProcess();
  357. }
  358. }
  359. },
  360. methods: {
  361. //关联项目
  362. ActiveRelevanceKr(item){
  363. this.$axiosUser('post', '/api/pro/okr/project/bind',{project_id:item.item.id,kr_id:this.selectKr.id}).then(res => {
  364. this.getkrTaskList();
  365. });
  366. },
  367. activevArr(item){
  368. if(this.addTaskIndex==1){
  369. if(item.value==1){
  370. let data={
  371. target_type: 2,//是 string 计划绑定的对象种类 1-目标 2-KR 3-计划(分解计划下的子计划的时候)
  372. target_id:this.selectKr.id,//是 integer 绑定的对象id 跟对象种类配对使用
  373. }
  374. this.$router.push({name: 'addTask', query: data})
  375. }else{
  376. this.$router.push({name: 'addProject', query: {kr_id:this.selectKr.id}})
  377. }
  378. }else{
  379. if(item.value==1){
  380. this.kr_id=this.selectKr.id;
  381. this.isShwoTaskSearch=true;
  382. }else{
  383. this.isShwoKrSearch=true;
  384. }
  385. }
  386. },
  387. openDetail(item,type){
  388. if(type==1){
  389. this.$router.push({name: 'taskDetail', query: {id: item.id}})
  390. }else if(type==2){
  391. this.$router.push({name: 'krDetail', query: {id: item.id}})
  392. }else if(type==3){
  393. this.$router.push({name: 'projectDetail', query: {id: item.id}})
  394. }
  395. },
  396. subContent(action, done){
  397. if (action == 'confirm') {
  398. this.$axiosUser('post', '/api/pro/okr/process/content',{p_id:this.processInfo.id,content:this.processInfo.content}).then(res => {
  399. this.getProcess();
  400. this.isShowProcess=false;
  401. })
  402. done()
  403. }else{
  404. done()
  405. }
  406. },
  407. compileContent(item){
  408. this.processInfo=JSON.parse(JSON.stringify(item));
  409. this.isShowProcess=true;
  410. },
  411. update(){
  412. this.getTargetDateil();
  413. this.getProcess();
  414. },
  415. // 沟通
  416. openCommunication(){
  417. let data={
  418. item:JSON.stringify(this.targetDetail),
  419. target_type:1,
  420. }
  421. this.$router.push({name: 'communication', query:data})
  422. },
  423. // 获取部门列表
  424. returnArr(list,arr){
  425. list.forEach(item=>{
  426. arr[item.id]=item
  427. if(item.children.length>0){
  428. this.returnArr(item.children,arr)
  429. }
  430. })
  431. },
  432. // 获取部门列表
  433. get_department_list() {
  434. this.$axiosUser('get','/api/pro/department/tree','','v2').then((res) => {
  435. let list=res.data.data.list
  436. let dept_tree={};
  437. this.returnArr(list,dept_tree)
  438. this.dept_tree=dept_tree
  439. })
  440. },
  441. //更新完成度
  442. updateProgress(){
  443. let item=this.targetDetail;
  444. this.progressData={
  445. target_type:1,
  446. id:item.id,
  447. o_id:item.id,
  448. process:Number(item.process),
  449. risk_level:item.risk_level,
  450. process_conf:item.process_conf,
  451. }
  452. this.isShwoUpdateProgress=true;
  453. },
  454. //获取最新字段
  455. getProcess(){
  456. let axios= this.$axiosUser('get', '/api/pro/okr/process/list', {target_type:1,target_id:this.o_id,page:1,page_size:100})
  457. let axios2= this.$axiosUser('get', '/api/pro/okr/process/sub', {target_type:1,target_id:this.o_id})
  458. Promise.all([axios,axios2]).then(res => {
  459. let data=res[1].data.data
  460. data.objectives=this.updatePro(data.objectives);
  461. data.kr=this.updatePro(data.kr);
  462. this.processData=data;
  463. this.processList=this.updatePro(res[0].data.data.list)
  464. })
  465. },
  466. updatePro(arr){
  467. arr.forEach(item=>{
  468. item.updatePro=Math.abs(item.process-item.process_last).toFixed(2);
  469. item.isUpdatePro=item.process-item.process_last
  470. })
  471. return arr;
  472. },
  473. openSelectUser(index){
  474. this.selectUserIndex=index;
  475. this.selectUser=true;
  476. },
  477. confirmUser(data) {
  478. let user=data.employee[0]
  479. if(this.selectUserIndex==1){
  480. let params={object_id:this.targetDetail.id,owner_id:user.id,}
  481. this.$axiosUser('POST', 'api/pro/okr/obj/change_owner', params).then(res => {
  482. this.getTargetDateil();
  483. })
  484. }else if(this.selectUserIndex==2){
  485. this.joinerUpdate(true,user.id);
  486. }else{
  487. this.deleteFollower(true,user.id);
  488. }
  489. },
  490. //参与者
  491. joinerUpdate(is,id){
  492. let data={
  493. object_id:this.o_id,
  494. joiner_id:id,
  495. action:is? 'add':'remove',
  496. }
  497. this.$axiosUser('post', '/api/pro/okr/obj/change_joiner',data).then(res => {
  498. this.getTargetDateil();
  499. })
  500. },
  501. //关注者
  502. deleteFollower(is,id){
  503. let data={
  504. object_id:this.o_id,
  505. employee_id:id,
  506. action:is? 'add':'remove',
  507. }
  508. this.$axiosUser('post', '/api/pro/okr/obj/change_focus',data).then(res => {
  509. this.getTargetDateil();
  510. })
  511. },
  512. //关联母任务
  513. ActiveRelevanceTask(item){
  514. if(item.id){
  515. this.$axiosUser('POST', '/api/pro/okr/plan/relate/other',{plan_id:item.id,target_type:2,target_id:this.kr_id}).then(res => {
  516. this.getkrTaskList();
  517. })
  518. }
  519. },
  520. addTask(item,index){
  521. this.addTaskIndex=index;
  522. this.selectKr=item;
  523. if(index==1){
  524. this.vArr=[{value: 1,name: '添加任务'},{value: 2,name: '添加项目'}];
  525. }else{
  526. this.vArr=[{value: 1,name: '关联任务'},{value: 2,name: '关联项目'}];
  527. }
  528. this.isShowV=true;
  529. },
  530. confirmAddkr(item){
  531. this.$axiosUser('POST','api/pro/okr/kr/create',item).then(res => {
  532. this.$toast("已添加");
  533. this.getKrList()
  534. })
  535. },
  536. activebelong(item){
  537. if(item.value==0){//关注
  538. this.$axiosUser('POST','/api/pro/okr/obj/follow',{o_id:this.o_id}).then(res => {
  539. this.$toast(this.isFollower? "已取消":'已关注');
  540. this.getTargetDateil();
  541. })
  542. }else if(item.value==1){//编辑
  543. this.isShwoUpdateDetail=true;
  544. }else if(item.value==2){//结束目标
  545. if(item.name=='结束目标'){
  546. this.$dialog.confirm({ title:'结束', message: '确定要结束目标吗?'}).then(() => {
  547. this.$axiosUser('post', '/api/pro/okr/obj/to_finish', { object_id: this.o_id }).then(res => {
  548. this.getTargetDateil()
  549. });
  550. });
  551. }else{
  552. this.$axiosUser('POST', '/api/pro/okr/obj/to_start',{object_id:this.o_id}).then(res => {
  553. this.getTargetDateil()
  554. })
  555. }
  556. }else if(item.value==3){//删除
  557. this.$dialog.confirm({ title:'删除', message: '目标删除操作不可恢复!请谨慎操作'}).then(() => {
  558. this.$axiosUser('post', '/api/pro/okr/obj/d', { o_id: this.o_id }).then(res => {
  559. this.$toast("已删除");
  560. setTimeout(()=>{
  561. this.$route_back()
  562. },500)
  563. });
  564. });
  565. }else if(item.value==4){//复制目标
  566. this.$router.push({name: 'copyTarget', query: {id:this.o_id}})
  567. }
  568. },
  569. returnSum(arr){
  570. if(arr.length==0){return 0};
  571. let sum=0;
  572. arr.forEach(item=>{
  573. if(item.composite_state==6){
  574. sum++;
  575. }
  576. })
  577. return sum
  578. },
  579. //执行
  580. getkrTaskList(){
  581. if(!this.o_id){
  582. return false
  583. }
  584. let data={
  585. o_id:this.o_id,// 是 string 目标id
  586. plan_calc: 1
  587. }
  588. this.$axiosUser('get', '/api/pro/okr/plan/list/o',data).then(res => {
  589. let list=res.data.data.list;
  590. list.forEach(e=>{
  591. if(e.plans&&e.plans.length>0){
  592. e.plans.forEach(item=>{
  593. item.day=this.$moment(item.end_date).diff(this.$moment().format('YYYY-MM-DD'), 'day')
  594. })
  595. }
  596. })
  597. this.krsListAll=list;
  598. })
  599. },
  600. //OKRs
  601. getKrList(){
  602. this.krsList=[];
  603. this.$axiosUser('get', '/api/pro/okr/kr/list',{o_id:this.o_id}).then(res => {
  604. let list=res.data.data.list;
  605. this.krsList=list
  606. })
  607. },
  608. //目标详情
  609. getTargetDateil(){
  610. this.isFollower=false;
  611. this.$axiosUser('get', '/api/pro/okr/obj/detail',{object_id:this.o_id}).then(res => {
  612. let data=res.data.data;
  613. data.dateStr=getDateStr(data)
  614. data.owner_userInfo=this.$getEmployeeMapItem(data.owner_id);//负责人
  615. data.special_employee_items=data.special_employee_ids.map(e=>{ //可见人员
  616. return this.$getEmployeeMapItem(e)
  617. })
  618. data.joiner_employee_items=data.joiner_ids.map(e=>{//参与人员
  619. return this.$getEmployeeMapItem(e)
  620. })
  621. data.follower_employee_items=data.follower_ids.map(e=>{//关注者
  622. return this.$getEmployeeMapItem(e)
  623. })
  624. if(data.follower_ids.length>0){
  625. data.follower_ids.some(e=>{//关注者
  626. if(e==this.userInfo.id){
  627. this.isFollower=true;
  628. return true
  629. }
  630. })
  631. }
  632. if(data.finish_status!=0){
  633. this.scopeArr.forEach(item=>{
  634. if(item.value==2){item.name='重启目标'}
  635. })
  636. }else{
  637. this.scopeArr.forEach(item=>{
  638. if(item.value==2){ item.name='结束目标' }
  639. })
  640. }
  641. // 权限[{value: 0, name:'关注目标'},{value: 1, name:'编辑基本信息'},{value: 2,name:'结束目标'},{value: 3,name:'删除目标',color: '#f56c6c'}],
  642. if(!data.can_edit){ //不可编辑
  643. this.scopeArr.forEach(item=>{
  644. if(item.value==1){
  645. item.disabled=true
  646. }
  647. if(item.value==2&&item.name=='结束目标'){
  648. item.disabled=true
  649. }
  650. // if(item.value==4){
  651. // item.disabled=true
  652. // }
  653. })
  654. }
  655. if(!data.can_delete){ //不可删除
  656. this.scopeArr.forEach(item=>{
  657. if(item.value==3){item.disabled=true}
  658. })
  659. }
  660. this.isOperation=getOperation(data.publisher_id,data.owner_id);
  661. this.targetDetail=this.updatePro([data])[0];
  662. })
  663. },
  664. },
  665. mounted() {
  666. this.get_department_list();
  667. if(this.$route.query.id){
  668. this.o_id=this.$route.query.id;
  669. this.getTargetDateil();
  670. this.getKrList();
  671. }
  672. },
  673. activated() {
  674. this.getkrTaskList();
  675. }
  676. };
  677. </script>
  678. <style scoped lang="less">
  679. .textarea-box /deep/ .van-field__control{
  680. max-height: 400px;
  681. }
  682. .footer {
  683. padding: 0.14rem 0.2rem;
  684. border-top: 0.02rem solid #f1f1f1;
  685. background: #fff;
  686. position: fixed;
  687. left: 0;
  688. right: 0;
  689. bottom: 0;
  690. z-index: 999;
  691. box-shadow: 0px -3px 0.15rem #f7f8fa;
  692. }
  693. .footer .gt {
  694. border-radius: 0.5rem;
  695. padding:0.18rem 0.14rem;
  696. border: 0.02rem solid #f1f1f1;
  697. font-size: 0.32rem;
  698. }
  699. .footer i{
  700. font-size: 0.6rem;
  701. }
  702. .footer .btn {
  703. border-radius: 0.5rem;
  704. padding:0.18rem 0.14rem;
  705. // border: 0.02rem solid #26A2FF;
  706. color: #fff;
  707. text-align: center;
  708. background: #26A2FF;
  709. font-size: 0.32rem;
  710. }
  711. .huan{
  712. position: relative;
  713. width: 0.4rem;
  714. height: 0.4rem;
  715. border-radius: 100%;
  716. background-color: #E9F1FE;
  717. box-sizing: border-box;
  718. text-align: center;
  719. margin-right: 0.1rem;
  720. }
  721. .huan span{
  722. border: 2px solid #26A2FF;
  723. border-radius: 100%;
  724. width: 0.16rem;
  725. height: 0.16rem;
  726. display: inline-block;
  727. }
  728. .status{
  729. position: relative;
  730. // top: 1px;
  731. font-size: 0.24rem;
  732. border-radius: 15px;
  733. line-height:0.32rem;
  734. color: #26A2FF;
  735. height: 0.32rem;
  736. padding: 0px 0.04rem;
  737. border: 1px solid #26A2FF;
  738. }
  739. .aite{
  740. width: 1rem;
  741. height: 1rem;
  742. text-align: center;
  743. line-height: 1rem;
  744. font-size: 0.6rem;
  745. cursor: pointer;
  746. display: inline-block;
  747. box-shadow: 0 0 3px #89919f;
  748. border-radius: 100%;
  749. background-color: #fff;
  750. position: absolute;
  751. z-index: 2;
  752. bottom: 0.4rem;
  753. right: 0.4rem;
  754. }
  755. .aite i{
  756. font-size: 0.36rem;
  757. }
  758. .o-content {
  759. border-radius: 5px;
  760. padding: 5px;
  761. margin-left: 0.24rem;
  762. margin-bottom: 5px;
  763. font-size: 0.28rem;
  764. }
  765. .o-content:hover {
  766. background-color: #f7f8fa;
  767. }
  768. .o-content:hover .fontColorC {
  769. display: block !important;
  770. cursor: pointer;
  771. }
  772. .biaos {
  773. width: 0.14rem;
  774. height: 0.14rem;
  775. background-color: #26a2ff;
  776. border: 1px solid #fff;
  777. border-radius: 100%;
  778. box-shadow: 0 0 0.14rem #26a2ff;
  779. display: inline-block;
  780. margin-right: 0.14rem;
  781. }
  782. .biaos2 {
  783. background-color: #ff9600;
  784. box-shadow: 0 0 0.14rem #ff9600;
  785. }
  786. .biaos3 {
  787. background-color: #f56c6c;
  788. box-shadow: 0 0 0.14rem #f56c6c;
  789. }
  790. .add-jz {
  791. width: 1.4rem;
  792. text-align: center;
  793. padding: 0.04rem 0.08rem;
  794. color: #26a2ff;
  795. border: 1px solid #26a2ff;
  796. border-radius: 25px;
  797. cursor: pointer;
  798. font-size: 0.26rem;
  799. }
  800. .search-item {
  801. padding: 0.06rem 0.1rem;
  802. background-color: #f7f8fa;
  803. color: #89919f;
  804. width: 1.3rem;
  805. text-align: center;
  806. margin-right: 0.2rem;
  807. border-radius: 3px;
  808. font-size: 0.3rem;
  809. border-radius: 25px;
  810. font-size: 0.26rem;
  811. }
  812. .searchActive {
  813. color: #26A2FF;
  814. background-color: #e9f0fd;
  815. }
  816. .delete-user {
  817. position: absolute;
  818. right: -4px;
  819. top: -4px;
  820. padding: 0;
  821. }
  822. .btns span {
  823. width: 1.4rem;
  824. text-align: center;
  825. padding: 0.04rem 0.1rem;
  826. color: #26A2FF;
  827. margin-right: 0.2rem;
  828. border: 0.02rem solid #26A2FF;
  829. border-radius: 0.06rem;
  830. font-size: 0.26rem;
  831. }
  832. .user-title {
  833. font-size: 0.28rem;
  834. // font-weight: 700;
  835. color: #89919f;
  836. margin: 0.2rem 0;
  837. }
  838. .addUser {
  839. font-size: 0.6rem;
  840. padding: 0.2rem;
  841. border: 1px dashed #89919f;
  842. text-align: center;
  843. height: 0.4rem;
  844. width: 0.4rem;
  845. line-height: 0.4rem;
  846. color: #89919f;
  847. border-radius: 100px;
  848. }
  849. .zj {
  850. width: 1rem;
  851. text-align: center;
  852. color: #26A2FF;
  853. border: 0.02rem solid #26A2FF;
  854. border-radius: 0.04rem;
  855. font-size: 0.24rem;
  856. }
  857. .zj2 {
  858. width: 1rem;
  859. text-align: center;
  860. color: #f56c6c;
  861. border: 0.02rem solid #f56c6c;
  862. border-radius: 0.04rem;
  863. font-size: 0.24rem;
  864. }
  865. .addKr {
  866. padding: 0.2rem;
  867. font-size: 0.3rem;
  868. background-color: #fff;
  869. border-radius: 5px;
  870. text-align: center;
  871. margin: 0 0.2rem;
  872. margin-top: 0.2rem;
  873. }
  874. .padding-l-r {
  875. padding: 0 0.1rem;
  876. border-left: 1px solid #f1f1f1;
  877. border-right: 1px solid #f1f1f1;
  878. margin: 0 0.1rem;
  879. }
  880. .scroller {
  881. height: calc(100% - 3.58rem) !important;
  882. position: relative !important;
  883. }
  884. .list-box {
  885. padding: 0.2rem;
  886. font-size: 0.32rem;
  887. background-color: #fff;
  888. border-radius: 5px;
  889. margin: 0 0.2rem;
  890. margin-top: 0.2rem;
  891. }
  892. .shadow {
  893. border-bottom: 1px solid #f5f7fa;
  894. }
  895. /deep/ .van-tabs__line {
  896. width: 20px !important;
  897. background-color: #26A2FF;
  898. }
  899. /deep/ .van-tab--active {
  900. color: #26A2FF;
  901. }
  902. .all-box {
  903. height: calc(100% - 0.92rem) !important;
  904. position: relative !important;
  905. }
  906. .progress {
  907. border-radius: 100px;
  908. background-color: #ebeef5;
  909. overflow: hidden;
  910. position: relative;
  911. vertical-align: middle;
  912. height: 0.2rem;
  913. width: 3rem;
  914. }
  915. .progress-inner {
  916. width: 0%;
  917. position: absolute;
  918. left: 0;
  919. top: 0;
  920. padding-top: 1px;
  921. height: 100%;
  922. background-image: linear-gradient(to right, #99bbff 0%, #2879ff 100%);
  923. border-radius: 100px;
  924. color: #fff;
  925. white-space: nowrap;
  926. transition: width 0.6s ease;
  927. }
  928. .bjYellow{
  929. background-image: linear-gradient(to right, #fedf86 0%, #FF9600 100%);
  930. }
  931. .bjRed{
  932. background-image: linear-gradient(to right, #FEA2A2 0%, #F16060 100%);
  933. }
  934. .header {
  935. padding: 0.24rem;
  936. background-color: #fff;
  937. z-index: 9999;
  938. // background-image: linear-gradient(to bottom, #26A2FF 0%, #99BBFF 100%);
  939. }
  940. </style>