myProject.vue 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622
  1. <template>
  2. <div class="boxMinHeight">
  3. <div class="flex-box">
  4. <!-- 我的部门 -->
  5. <div style="width: 160px;background-color: #fff;border-radius: 10px;margin-right: 10px;padding: 20px 10px;height: calc(100vh - 80px);" v-if="myTargertType==4">
  6. <template v-if="userInfo.employee_detail.dept_list.length>0">
  7. <div style="margin-bottom:10px;font-size:16px;text-align: center;" class="blue cursor">
  8. <el-select v-model="dept_id" placeholder="请选择" class="selectUser" :clearable="false">
  9. <el-option v-for="item in dept_tree" :key="item.id" :label="item.name" :value="item.id"></el-option>
  10. </el-select>
  11. </div>
  12. <div style="height: calc(100vh - 160px);overflow-y:auto;" class="scroll-bar">
  13. <div class="flex-box-ce user-item" :class="ownerUserInfo.id==item.id? 'userActive':''" v-for="item in employeeOptions" :key="item.id" @click="ownerUserInfo=item">
  14. <userImage :id="item.id" width="36px" height="36px" fontSize="14" :user_name="item.name"></userImage>
  15. <Tooltip :preHtml="item.name">
  16. <div class="font-flex-word" style="margin-left: 10px;font-size: 14px;">{{ item.name }}</div>
  17. </Tooltip>
  18. </div>
  19. <div v-if="employeeOptions.length==0" style="text-align: center;margin-top: 30px;" class="fontColorC">暂无人员</div>
  20. <div style="height:30px"></div>
  21. </div>
  22. </template>
  23. <div class="fontColorC" v-else style="margin-top: 30px;text-align: center;">暂没有所属部门</div>
  24. </div>
  25. <div class="flex-1">
  26. <div class="flex-box-ce flex-d-center header">
  27. <div class="flex-1 flex-box-ce" style="max-width: 800px;">
  28. <template v-if="myTargertType==1">
  29. <div class="flex-box-ce" >
  30. <userImage :id="userInfo.id" width="50px" height="50px" :user_name="userInfo.name"></userImage>
  31. <span style="margin-left: 10px;font-size: 18px;font-weight: 700;color: #3F4755;">{{ userInfo.name }}</span>
  32. </div>
  33. <div class="flex-box-ce span-item" style="margin-left: 20px;">
  34. <span :class="idText=='owner_id'? 'spanActive':''" @click="idText='owner_id'">我负责的</span>
  35. <span :class="idText=='joiner_id'? 'spanActive':''" @click="idText='joiner_id'">我参与的</span>
  36. <span :class="idText=='publisher_id'? 'spanActive':''" @click="idText='publisher_id'">我创建的</span>
  37. </div>
  38. </template>
  39. <div v-if="myTargertType==2" style="font-size: 24px;font-weight: 600;padding: 9px 0;">公开项目</div>
  40. <div v-if="myTargertType==3" style="font-size: 24px;font-weight: 600;padding: 9px 0;">公司全部项目</div>
  41. <template v-if="myTargertType==4">
  42. <div class="flex-box-ce" >
  43. <userImage :id="ownerUserInfo.id" width="50px" height="50px" :user_name="ownerUserInfo.name"></userImage>
  44. <span style="margin-left: 10px;font-size: 18px;font-weight: 700;color: #3F4755;">{{ ownerUserInfo.name }}</span>
  45. </div>
  46. <div class="flex-box-ce span-item" style="margin-left: 20px;">
  47. <span :class="idText=='owner_id'? 'spanActive':''" @click="idText='owner_id'">Ta负责的</span>
  48. <span :class="idText=='joiner_id'? 'spanActive':''" @click="idText='joiner_id'">Ta参与的</span>
  49. <span :class="idText=='publisher_id'? 'spanActive':''" @click="idText='publisher_id'">Ta创建的</span>
  50. </div>
  51. </template>
  52. </div>
  53. <div class="flex-box-ce">
  54. <el-button type="primary" size="small" round style="margin-right: 10px;" @click="isShowAdd=true" v-if="myTargertType==1">创建项目</el-button>
  55. <el-input @input="keyinput" class="input" maxlength="20" prefix-icon="el-icon-search" style="width: 206px;" size="small" v-model="formData.keyword" clearable placeholder="请输入关键字" />
  56. <el-select class="select" size="small" v-model="formData.composite_state" placeholder="类型" style="width: 100px;">
  57. <el-option v-for="item in statusArr" :key="item.value" :label="item.label" :value="item.value"></el-option>
  58. </el-select>
  59. <div class="selectBox">
  60. <SelectProjectGj :formData="formData" :myTargertType="myTargertType" :idText="idText" @confirm="selectPeriodGjConfirm" ></SelectProjectGj>
  61. </div>
  62. </div>
  63. </div>
  64. <div v-loading="loading">
  65. <template v-if="projectList.length>0">
  66. <div class="itemBox" v-for="(item,index) in projectList" :key="item.id" @click="openDetail(item)">
  67. <div class="flex-box-ce flex-d-center">
  68. <div class="item-left flex-box">
  69. <div class="project-name clamp2">{{item.name}}</div>
  70. <div class="project-status" style="height: 22px;line-height: 22px;border-radius: 3px;padding: 0 5px;position: relative;top: 2px;font-size: 12px;background-color: #f1f1f1;">{{item.stateText}}</div>
  71. </div>
  72. <div class="fontColorB flex-box-ce" style="flex-shrink:0;">
  73. <div class="flex-box-ce">
  74. <svg-icon icon-class="#icon-biaoqian_wode" class="svgIcon" style="width: 1rem;height: 1rem;"></svg-icon>
  75. <span>&nbsp;{{item.userInfo.name}}</span>
  76. </div>
  77. <div class="flex-box-ce" style="margin-left: 30px;">
  78. <template v-if="item.day>30">
  79. <span>{{$moment(item.end_date).format('MM/DD')}}截止</span>
  80. </template>
  81. <template v-else>
  82. <span v-if="item.day>0" class="green">剩余{{item.day}}天</span>
  83. <span v-if="item.day==0" class="green">剩余1天</span>
  84. <span v-if="item.day<0&&item.composite_state != 4" class="red">逾期{{Math.abs(item.day)}}天</span>
  85. <span v-if="item.composite_state == 4">{{$moment(item.end_date).format('YY/MM/DD')}}截止</span>
  86. </template>
  87. <Progress :inputStyle="{ height: '14px', width: '140px', lineHeight: '14px' }" :status="item.composite_state==3? 3:1" :value="Number(item.process)" style="margin-left: 10px;"></Progress>
  88. </div>
  89. </div>
  90. </div>
  91. </div>
  92. <div style="text-align: center;background-color: #fff;padding:10px 0;border-radius: 10px;">
  93. <el-pagination layout="total,prev,pager,next,sizes" :current-page.sync="formData.page" :total="total"
  94. @size-change="handleSizeChange" @current-change="handleCurrentChange" :page-sizes="[10, 20, 50, 100]"
  95. :page-size="formData.page_size"></el-pagination>
  96. </div>
  97. </template>
  98. <!-- 没有列表时 -->
  99. <div class="flex-box-v flex-center-center" style="height: 600px;" v-else>
  100. <noData isSolt imgUrl="static/images/no_data.png" imgW="150px" imgH="100px">
  101. <div v-if="myTargertType==1" style="text-align: center;margin: 0 0;line-height: 30px;">暂无项目,去<span @click="isShowAdd=true" class="blue cursor">创建项目</span></div>
  102. <div v-else style="text-align: center;margin: 0 0;line-height: 30px;">暂无项目</div>
  103. </noData>
  104. </div>
  105. </div>
  106. </div>
  107. </div>
  108. <AddProject :visible.sync="isShowAdd" @confirm="ActiveAddProject"></AddProject>
  109. </div>
  110. </template>
  111. <script>
  112. import Tooltip from '@/components/Tooltip'; //鼠标悬浮显示文字
  113. import { _debounce} from '@/utils/auth';
  114. import EmployeeSelector from '@/components/EmployeeSelector';
  115. import SelectProjectGj from '@/okr/components/project/SelectProjectGj'; //
  116. import AddProject from '@/okr/components/project/AddProject'; //
  117. import Progress from '@/okr/components/public/Progress'; //进度条
  118. export default {
  119. name: 'myProject',
  120. components: {EmployeeSelector,Tooltip,SelectProjectGj,AddProject,Progress},
  121. props:{
  122. myTargertType:{ //从不同页面加载 1:我的项目 2:公开项目 3全部项目 4我的部门
  123. type:Number,
  124. default:1
  125. }
  126. },
  127. data() {
  128. return {
  129. userInfo: this.$userInfo(),
  130. isShowAdd: false,
  131. idText: 'owner_id',
  132. projectList:[],
  133. loading: false,
  134. total: 0,
  135. formData: {
  136. page: 1,
  137. page_size: 10,
  138. dateTime:[],
  139. owner_id:'',// 否 string 负责人id 默认为0
  140. joiner_id:'',// 否 string 参与者id 默认为0
  141. publisher_id:'',// 否 string 发布者id 默认为0
  142. scope_type:0,// 否 string 可见范围 1-相关人员可见 2-全员可见 默认为0不区分
  143. composite_state:0,// 否 strin g 综合状态 1-未开始 2-进行中 3-逾期 4-达标 默认为0不区分
  144. dept_id:'',// 否 string 部门id,默认为0
  145. start_date:'',// 否 string 开始日期 格式:2024-01-01 默认为空字符串
  146. end_date:'',// 否 string 结束日期 格式:2024-01-01 默认为空字符串
  147. sort:'ct_desc',// 否 string 排序 ct_asc-创建时间正序 ct_desc-创建时间倒序 up_asc-更新时间正序 up_desc-更新时间倒序 默认为ct_desc
  148. keyword:'',// 否 string 关键字
  149. },
  150. statusArr: [
  151. {value: 0,label: '全部状态'},
  152. {value: 1,label: '未开始'},
  153. {value: 2,label: '进行中'},
  154. {value: 3,label: '已逾期'},
  155. {value: 4,label: '已完成'},
  156. ],
  157. // 我的部门相关
  158. dept_id:'',
  159. employeeOptions:[],
  160. ownerUserInfo:{},
  161. dept_tree:[],
  162. };
  163. },
  164. watch: {
  165. 'formData.composite_state'(){
  166. this.getProjectList();
  167. },
  168. ownerUserInfo(val){
  169. this.getProjectList();
  170. },
  171. idText(){
  172. this.getProjectList();
  173. },
  174. dept_id(){
  175. this.get_employee_list();
  176. },
  177. },
  178. methods: {
  179. get_employee_list() {
  180. this.$axiosUser('get', '/api/pro/employee/list', { dept_id: this.dept_id,page:1,page_size:1000,include_sub:0},'v2').then(res => {
  181. this.employeeOptions = res.data.data.list;
  182. if(this.employeeOptions.length>0){
  183. this.ownerUserInfo=this.employeeOptions[0];
  184. }
  185. });
  186. },
  187. openDetail(item){
  188. this.$router.push({path:'/projectDetail',query:{id:item.id}})
  189. },
  190. keyinput:_debounce(function(val) {
  191. this.getProjectList();
  192. }),
  193. ActiveAddProject(){
  194. this.getProjectList();
  195. },
  196. getProjectList(is){
  197. is? '':this.formData.page=1;
  198. let data={
  199. page:is? this.formData.page:1,
  200. page_size:this.formData.page_size,
  201. scope_type:0,// 否 string 可见范围 1-相关人员可见 2-全员可见 默认为0不区分
  202. composite_state:this.formData.composite_state,// 否 strin g 综合状态 1-未开始 2-进行中 3-逾期 4-达标 默认为0不区分
  203. dept_id:this.formData.dept_id? this.formData.dept_id[this.formData.dept_id.length-1]:0,// 否 string 部门id,默认为0
  204. sort:this.formData.sort,// 否 string 排序 ct_asc-创建时间正序 ct_desc-创建时间倒序 up_asc-更新时间正序 up_desc-更新时间倒序 默认为ct_desc
  205. keyword:this.formData.keyword,// 否 string 关键字
  206. }
  207. if(this.formData.owner_id){
  208. data.owner_id=this.formData.owner_id
  209. }
  210. if(this.formData.joiner_id){
  211. data.joiner_id=this.formData.joiner_id
  212. }
  213. if(this.myTargertType==1){
  214. if(this.idText=='owner_id'){
  215. data.owner_id=this.$userInfo().id;
  216. }
  217. if(this.idText=='joiner_id'){
  218. data.joiner_id=this.$userInfo().id;
  219. }
  220. if(this.idText=='publisher_id'){
  221. data.publisher_id=this.$userInfo().id;
  222. }
  223. }else if(this.myTargertType==2){//公开项目
  224. data.scope_type=2
  225. }else if(this.myTargertType==3){//公司项目
  226. data.scope_type=0
  227. }else if(this.myTargertType==4){//部门项目
  228. if(this.idText=='owner_id'){
  229. data.owner_id=this.ownerUserInfo.id;
  230. }
  231. if(this.idText=='joiner_id'){
  232. data.joiner_id=this.ownerUserInfo.id;
  233. }
  234. if(this.idText=='publisher_id'){
  235. data.publisher_id=this.ownerUserInfo.id;
  236. }
  237. }
  238. if(this.formData.dateTime&&this.formData.dateTime.length>0){
  239. data.start_date=this.formData.dateTime[0];// 否 string 开始日期 格式:2024-01-01 默认为空字符串
  240. data.end_date=this.formData.dateTime[1];// 否 string 结束日期 格式:2024-01-01 默认为空字符串
  241. }
  242. this.loading = true;
  243. this.$axiosUser('get','api/pro/okr/project/list',data).then(res => {
  244. let list=res.data.data.list;
  245. list.forEach(item=>{
  246. item.userInfo=this.$getEmployeeMapItem(item.owner_id);
  247. item.stateText=this.returnStatus(item.composite_state);
  248. item.day=this.$moment(item.end_date).diff(this.$moment().format('YYYY-MM-DD'), 'day')
  249. })
  250. this.projectList=list;
  251. this.total=res.data.data.total;
  252. }).finally(()=>{
  253. this.loading = false;
  254. })
  255. },
  256. returnStatus(index){
  257. let arr=['未开始','进行中','已逾期','已完成'];
  258. return arr[index-1];
  259. },
  260. selectPeriodGjConfirm(data){
  261. this.formData.sort = data.sort;
  262. this.formData.owner_id = data.owner_id;
  263. this.formData.joiner_id = data.joiner_id;
  264. this.formData.dateTime = data.dateTime;
  265. this.formData.dept_id = data.dept_id;
  266. this.getProjectList();
  267. },
  268. handleSizeChange(val) {
  269. this.formData.page=1;
  270. this.formData.page_size = val;
  271. this.getProjectList();
  272. },
  273. // 页面跳转
  274. handleCurrentChange(val) {
  275. this.formData.page = val;
  276. this.getProjectList(true);
  277. },
  278. },
  279. created() {
  280. if(this.myTargertType==2||this.myTargertType==3){
  281. this.idText='';
  282. }
  283. if(this.myTargertType==4){
  284. let dept_tree=[];
  285. this.userInfo.employee_detail.manage_dept_ids.forEach(id=>{
  286. dept_tree.push(this.$getDept(id));
  287. })
  288. this.dept_tree=dept_tree;
  289. this.$nextTick(()=>{
  290. if(dept_tree.length>0){
  291. this.dept_id=dept_tree[0].id
  292. }
  293. })
  294. }
  295. },
  296. mounted() {
  297. if(this.myTargertType==1){
  298. this.getProjectList();
  299. }
  300. },
  301. };
  302. </script>
  303. <style scoped lang="scss">
  304. .project-name{
  305. font-size: 18px;
  306. font-weight: 550;
  307. color: rgb(20, 28, 40);
  308. max-width: 700px;
  309. padding-right: 6px;
  310. }
  311. .width-120 {
  312. width: 100px;
  313. overflow: hidden;
  314. text-overflow: ellipsis;
  315. white-space: nowrap;
  316. }
  317. .selectUser ::v-deep input {
  318. border-radius: 20px;
  319. }
  320. .user-item {
  321. padding: 4px 6px;
  322. border-radius: 5px;
  323. cursor: pointer;
  324. }
  325. .userActive {
  326. background-color: #ecf5ff;
  327. color: #409EFF;
  328. }
  329. .span-item span {
  330. font-size: 14px;
  331. padding: 8px 20px;
  332. border-bottom: 2px solid #f1f1f1;
  333. // border-radius: 3px;
  334. color: #89919f;
  335. cursor: pointer;
  336. }
  337. .spanActive {
  338. border-bottom: 2px solid #409EFF !important;
  339. color: #409EFF !important;
  340. }
  341. .okrs-box {
  342. padding: 10px 0;
  343. border-bottom: 1px solid #f1f1f1;
  344. cursor: pointer;
  345. }
  346. .okrs-box:hover {
  347. background-color: #f4f6f9;
  348. }
  349. .message-items {
  350. margin-top: 10px;
  351. font-size: 12px;
  352. }
  353. .message-items div {
  354. margin-right: 20px;
  355. cursor: pointer;
  356. }
  357. .message-items i {
  358. padding-right: 5px;
  359. }
  360. .weight {
  361. width: 90px;
  362. margin-right: 5px;
  363. padding: 5px;
  364. border: 1px dotted #fff;
  365. border-radius: 5px;
  366. cursor: pointer;
  367. }
  368. .weight:hover {
  369. border: 1px dotted #ccc;
  370. }
  371. .weight span {
  372. display: inline-block;
  373. width: 50px;
  374. text-align: center;
  375. color: #e6a23c;
  376. }
  377. .kr-item {
  378. padding: 4px 0;
  379. position: relative;
  380. margin-bottom: 8px;
  381. border-radius: 5px;
  382. }
  383. .kr-item:hover {
  384. background-color: #f4f6f9;
  385. }
  386. .huan {
  387. position: relative;
  388. width: 24px;
  389. height: 24px;
  390. border-radius: 100%;
  391. background-color: #E9F1FE;
  392. box-sizing: border-box;
  393. margin: 0 3px;
  394. text-align: center;
  395. line-height: 25px;
  396. }
  397. .huan span {
  398. border: 2px solid #409EFF;
  399. border-radius: 100%;
  400. width: 12px;
  401. height: 12px;
  402. display: inline-block;
  403. }
  404. .add-task-title {
  405. padding: 10px;
  406. position: relative;
  407. }
  408. .add-task-title::after {
  409. content: "";
  410. position: absolute;
  411. width: 4px;
  412. height: 14px;
  413. border-radius: 5px;
  414. background-color: #409EFF;
  415. left: 0;
  416. top: 13px;
  417. }
  418. .inputDc {
  419. position: absolute;
  420. top: 0;
  421. right: 0;
  422. left: 0;
  423. bottom: 0;
  424. z-index: 9;
  425. cursor: pointer;
  426. }
  427. .el-icon-more {
  428. width: 20px;
  429. height: 20px;
  430. box-sizing: border-box;
  431. text-align: center;
  432. line-height: 20px;
  433. cursor: pointer;
  434. }
  435. .el-icon-more:hover {
  436. background-color: #606266;
  437. border-radius: 25px;
  438. color: #fff;
  439. }
  440. .border {
  441. -webkit-appearance: none;
  442. background-color: #fff;
  443. background-image: none;
  444. border-radius: 4px;
  445. border: 1px solid #dcdfe6;
  446. -webkit-box-sizing: border-box;
  447. box-sizing: border-box;
  448. color: #C0C4CF;
  449. font-size: inherit;
  450. height: auto;
  451. outline: 0;
  452. padding: 0 15px;
  453. padding-right: 10px;
  454. line-height: 34px;
  455. width: 270px;
  456. position: relative;
  457. cursor: pointer;
  458. }
  459. .border .font-flex-word {
  460. color: #606266;
  461. }
  462. .border:hover {
  463. border: 1px solid #c0c4cc;
  464. }
  465. .add-task-item {
  466. position: relative;
  467. }
  468. .add-task-item:hover i {
  469. cursor: pointer;
  470. opacity: 1 !important;
  471. }
  472. .add-task {
  473. // padding-left: 30px;
  474. }
  475. .oBox {
  476. box-shadow: 0 0px 10px #F0F4FA;
  477. border-radius: 3px;
  478. padding: 5px 8px;
  479. }
  480. .add-task ::v-deep input {
  481. border: none;
  482. padding-right: 80px;
  483. }
  484. .oBox ::v-deep input {
  485. border: none;
  486. font-size: 18px;
  487. font-weight: 600;
  488. color: #141c28;
  489. padding-right: 80px;
  490. }
  491. .oBox ::v-deep input::-webkit-input-placeholder {
  492. font-weight: 500;
  493. }
  494. .edit {
  495. display: none;
  496. position: relative;
  497. top: 1px;
  498. padding-left: 5px;
  499. transition: all .3s;
  500. }
  501. .edit:hover {
  502. color: #409EFF !important;
  503. }
  504. .selectBox {
  505. background-color: #fff;
  506. border-radius: 25px;
  507. padding: 10px 20px;
  508. padding-left: 0;
  509. }
  510. .header {
  511. margin-bottom: 10px;
  512. border-radius: 10px;
  513. padding: 10px;
  514. background-color: #fff;
  515. }
  516. .caret {
  517. position: absolute;
  518. width: 30px;
  519. height: 30px;
  520. text-align: center;
  521. line-height: 30px;
  522. left: -30px;
  523. top: 50%;
  524. margin-top: -15px;
  525. box-sizing: border-box;
  526. cursor: pointer;
  527. }
  528. .jt {
  529. width: 24px;
  530. position: relative;
  531. top: 2px;
  532. left: 4px;
  533. transform: rotateY(180deg);
  534. margin-left: 0px;
  535. }
  536. .itemBox {
  537. padding: 20px;
  538. border-radius: 10px;
  539. background-color: #fff;
  540. margin-bottom: 10px;
  541. box-shadow: 0 2px 12px #EBF0F6;
  542. cursor: pointer;
  543. }
  544. ::v-deep .el-tabs__item {
  545. color: #89919f;
  546. }
  547. ::v-deep .is-active {
  548. color: #409EFF;
  549. font-weight: 600;
  550. }
  551. ::v-deep .el-tabs__nav .is-active {
  552. font-size: 16px;
  553. }
  554. ::v-deep .el-tabs__header {
  555. margin: 0px;
  556. }
  557. .select {
  558. margin-right: 6px;
  559. }
  560. .select ::v-deep .el-input__inner {
  561. border-radius: 25px;
  562. }
  563. .input ::v-deep .el-input__inner {
  564. border: none;
  565. border-bottom: 1px solid #f1f1f1;
  566. border-radius: 0px;
  567. }
  568. </style>