targetDetail.vue 38 KB

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