taskDetailsPopup.vue 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608
  1. <template>
  2. <div>
  3. <!-- 任务详情弹窗 -->
  4. <el-drawer :visible.sync="Delay_to_open" :with-header="false" :size="'500px'" :before-close="handleClose" :custom-class="'drawer_details'">
  5. <div class="details_title">{{ title }}</div>
  6. <div class="details_content" v-if="workDetailData" v-loading="loading">
  7. <div class="flex-box flex-v-ce">
  8. <userImage class="user_img person_imghead" width="46px" height="46px" :user_name="workDetailData.employee_name" :img_url="workDetailData.img_url"></userImage>
  9. <div class="d_userMessage">
  10. <div>{{ workDetailData.employee_name }}</div>
  11. <div v-if="detailType != 2 && workDetailData.dept_list[0]">{{ workDetailData.dept_list[0].dept_name }}</div>
  12. </div>
  13. <div style="margin-left: 5px;" class="flex-box flex-v-ce">
  14. <div v-if="workDetailData.point_config.base_point > 0" class="red">+{{ workDetailData.point_config.base_point }}</div>
  15. <div v-else class="green">{{ workDetailData.point_config.base_point }}</div>
  16. <div style="margin-left: 5px;">{{ $getTypsName(workDetailData.pt_id) }}</div>
  17. </div>
  18. </div>
  19. <ul>
  20. <li class="flex-box">
  21. <div class="label">任务内容</div>
  22. <div class="content_text">{{ workDetailData.task_name }}</div>
  23. </li>
  24. <li class="flex-box">
  25. <div class="label">任务备注</div>
  26. <textarea class="flex-1" disabled="disabled" v-model="workDetailData.task_remark" style="border: none;height:100px"></textarea>
  27. </li>
  28. <li class="flex-box" v-if="workDetailData.task_file_list && workDetailData.task_file_list.length > 0">
  29. <div class="label"></div>
  30. <div class="content_text">
  31. <el-image
  32. v-for="(item, index) in workDetailData.task_file_list"
  33. :key="index"
  34. style="width: 100px; height: 100px;margin-right:8px"
  35. :src="item"
  36. :preview-src-list="workDetailData.task_file_list"
  37. ></el-image>
  38. </div>
  39. </li>
  40. <li class="flex-box">
  41. <div class="label">积分种类</div>
  42. <div class="content_text">{{ workDetailData.pt_name }}</div>
  43. </li>
  44. <li class="flex-box">
  45. <div class="label">任务积分</div>
  46. <div class="content_text">{{ workDetailData.point_config.base_point }}{{ workDetailData.pt_name }}</div>
  47. </li>
  48. <li class="flex-box" v-if="workDetailData.point_config.review_point">
  49. <div class="label">最终分</div>
  50. <div class="content_text">{{ workDetailData.point_config.review_point }}</div>
  51. </li>
  52. <li class="flex-box">
  53. <div class="label">审批人</div>
  54. <div class="content_text">{{ workDetailData.reviewer_name }}</div>
  55. </li>
  56. <li class="flex-box">
  57. <div class="label">发布人</div>
  58. <div class="content_text">{{ workDetailData.publisher_name }}</div>
  59. </li>
  60. <li class="flex-box" v-if="workDetailData.create_time">
  61. <div class="label">发布时间</div>
  62. <div class="content_text">{{ workDetailData.create_time }}</div>
  63. </li>
  64. <li class="flex-box">
  65. <div class="label">截止时间</div>
  66. <div class="content_text">{{ workDetailData.expire_time }}</div>
  67. </li>
  68. <li class="flex-box" v-if="workDetailData.point_config.ahead_award_point > 0">
  69. <div class="label">提前奖分</div>
  70. <div class="content_text">{{ workDetailData.point_config.ahead_award_point }} B分/天</div>
  71. </li>
  72. <li class="flex-box" v-if="workDetailData.point_config.timeout_deduction_point > 0">
  73. <div class="label">逾期扣分</div>
  74. <div class="content_text">{{ workDetailData.point_config.timeout_deduction_point }} B分/天</div>
  75. </li>
  76. <li class="flex-box" v-if="workDetailData.complete_task && workDetailData.complete_task.time">
  77. <div class="label">完成时间</div>
  78. <div class="content_text">{{ workDetailData.complete_task.time }}</div>
  79. </li>
  80. <li class="flex-box" v-if="workDetailData.complete_task && workDetailData.complete_task.remark">
  81. <div class="label">完成备注</div>
  82. <div class="content_text">{{ workDetailData.complete_task.remark }}</div>
  83. </li>
  84. <li class="flex-box" v-if="workDetailData.complete_task && workDetailData.complete_task.files && workDetailData.complete_task.files.length > 0">
  85. <div class="label"></div>
  86. <div class="content_text">
  87. <el-image
  88. v-for="(item, index) in workDetailData.complete_task.files"
  89. :key="index"
  90. style="width: 100px; height: 100px;margin-right:8px"
  91. :src="item"
  92. :preview-src-list="workDetailData.complete_task.files"
  93. ></el-image>
  94. </div>
  95. </li>
  96. </ul>
  97. <div v-show="workDetailData.point_config && workDetailData.point_config.item_info">
  98. <p class="row_title">规则依据</p>
  99. <el-row :gutter="10" v-if="workDetailData.point_config.rule_info">
  100. <el-col :span="4">规则分类</el-col>
  101. <el-col :span="19">{{ workDetailData.point_config.rule_info.name }}</el-col>
  102. </el-row>
  103. <el-row v-if="workDetailData.point_config.item_info">
  104. <el-col :span="4">积分规则</el-col>
  105. <el-col :span="19">{{ workDetailData.point_config.item_info.remark }}</el-col>
  106. </el-row>
  107. <el-row v-if="workDetailData.point_config.item_info">
  108. <el-col :span="4">积分</el-col>
  109. <el-col :span="19" v-show="workDetailData.point_config.item_info.min_point == workDetailData.point_config.item_info.max_point">{{ workDetailData.point_config.item_info.min_point }} {{workDetailData.pt_name}}</el-col>
  110. <el-col :span="19" v-show="workDetailData.point_config.item_info.min_point != workDetailData.point_config.item_info.max_point">
  111. {{ workDetailData.point_config.item_info.min_point }} ~ {{ workDetailData.point_config.item_info.max_point }} {{workDetailData.pt_name}}
  112. </el-col>
  113. </el-row>
  114. </div>
  115. <div v-show="showWork" style="border-top: 2px solid #f8f8f8;">
  116. <div class="d_progress">
  117. <div class="flex-box ">
  118. <div class="flex-1">工作进度({{ workDetailData.progress }}%)</div>
  119. <div class="fontColorF addJf" @click="sliderShow" v-if="workDetailData.employee_id == userId && workDetailData.status==1">+更新进度</div>
  120. </div>
  121. <el-progress :percentage="workDetailData.progress"></el-progress>
  122. </div>
  123. <div>
  124. <el-tabs v-model="activeName">
  125. <el-tab-pane label="工作记录" name="work">
  126. <div class="flex-box">
  127. <div class="flex-1"></div>
  128. <div class="fontColorF addJf" @click="isOne = true" v-if="workDetailData.employee_id == userId && workDetailData.status < 3 ">+记一条工作记录</div>
  129. </div>
  130. <div class="work_box" style="padding-top:10px">
  131. <div class="work_item" v-for="(item, index) in text_list" :key="index" style="margin: 0 0 15px 0">
  132. <div class="flex-box">
  133. <userImage class="user_img person_imghead" width="40px" height="40px" :user_name="item.recorder" :img_url="item.img_url"></userImage>
  134. <div style="width: 100%;" class="d_name">
  135. <div class="flex-box flex-d-center">
  136. <div class="flex-1">{{ item.recorder }}
  137. <span v-if="item.point * 1 > 0">+{{ item.point }}</span>
  138. <span v-if="item.point * 1 < 0">{{ item.point }}</span>
  139. </div>
  140. <div class="d_date fontColorF">{{ item.time }} <span class="delete_jfjl" v-if="item.recorder_id == userId && workDetailData.status < 3 " @click="deletejf_cli(index,0)"><i class="el-icon-delete"></i></span> </div>
  141. </div>
  142. <div class="fontColorB" style="margin-top: 5px;">{{ item.remark }}</div>
  143. </div>
  144. </div>
  145. </div>
  146. <div v-if="text_list.length==0" class="fontColorF" style="text-align: center;">暂无工作记录</div>
  147. </div>
  148. </el-tab-pane>
  149. <el-tab-pane label="记分记录" name="participation">
  150. <div class="flex-box" style="padding-bottom: 10px;border-bottom: 1px solid #f1f1f1;">
  151. <div class="flex-1 blue">
  152. <span v-if="point_total > 0">合计:+{{ point_total }}</span>
  153. <span v-else>合计:{{ point_total }}</span>
  154. </div>
  155. <div class="fontColorF addJf" @click="isIntegral = true" v-if="keepTheScore">+记分</div>
  156. </div>
  157. <div class="work_box" style="padding-top:10px">
  158. <div class="work_item" v-for="(item, index) in point_list" :key="index" style="margin: 0 0 15px 0">
  159. <div class="flex-box">
  160. <userImage class="user_img person_imghead" width="40px" height="40px" :user_name="item.recorder" :img_url="item.img_url"></userImage>
  161. <div style="width: 100%;" class="d_name">
  162. <div class="flex-box flex-d-center">
  163. <div class="flex-1">{{ item.recorder }}
  164. <span class="red" v-if="item.point * 1 > 0">+{{ item.point }}</span>
  165. <span class="green" v-if="item.point * 1 < 0">{{ item.point }}</span>
  166. </div>
  167. <div class="d_date fontColorF">{{ item.time }} <span class="delete_jfjl" v-if="userId == item.recorder_id && workDetailData.status < 3" @click="deletejf_cli(index,1)"><i class="el-icon-delete"></i></span> </div>
  168. </div>
  169. <div class="fontColorB" style="margin-top: 5px;">{{ item.remark }}</div>
  170. </div>
  171. </div>
  172. </div>
  173. <div v-if="point_list.length==0" class="fontColorF" style="text-align: center;">暂无积分记录</div>
  174. </div>
  175. </el-tab-pane>
  176. </el-tabs>
  177. </div>
  178. </div>
  179. </div>
  180. </el-drawer>
  181. <!-- 更新进度 -->
  182. <el-dialog title="更新进度" :close-on-click-modal="false" :visible.sync="isSlider" :before-close="publicClose" width="40%">
  183. <div class="slider">
  184. <div class="fontColorF">拖动滑杆更新进度</div>
  185. <el-slider v-model="progress"></el-slider>
  186. </div>
  187. <span slot="footer">
  188. <el-button @click="publicClose()">取消</el-button>
  189. <el-button type="primary" @click="sliderSend">完成</el-button>
  190. </span>
  191. </el-dialog>
  192. <!-- 记一条 -->
  193. <el-dialog title="记一条" :close-on-click-modal="false" :visible.sync="isOne" destroy-on-close :before-close="publicClose" width="40%">
  194. <div class="flex-box">
  195. <div style="width: 80px;">工作记录</div>
  196. <el-input type="textarea" :rows="3" placeholder="请输入内容" v-model="textarea"></el-input>
  197. </div>
  198. <span slot="footer">
  199. <el-button @click="publicClose()">取消</el-button>
  200. <el-button type="primary" @click="onerecord">完成</el-button>
  201. </span>
  202. </el-dialog>
  203. <!-- 记分记录 -->
  204. <el-dialog title="记分" :visible.sync="isIntegral" :before-close="publicClose" width="40%" destroy-on-close :close-on-click-modal="false">
  205. <el-form :model="integral" ref="integral" label-width="80px" :rules="formRules">
  206. <el-form-item label="记录" prop="text" :rules="[{ required: true, message: '记录不能为空'}]">
  207. <el-input type="textarea" :rows="3" v-model="integral.text"></el-input>
  208. </el-form-item>
  209. <!-- 记分不能为空 -->
  210. <el-form-item label="记分" prop="num" >
  211. <!-- <el-form-item label="记分" prop="num" :rules="[{ required: false, message: '记分不能为空'},{ type: 'number', message: '积分必须为数字值'}]"> -->
  212. <div class="num" :class="[integral.type=='1'?'add':'jian']"></div>
  213. <el-input placeholder="请输入内容" type="Number" v-model.number="integral.num" @input="(val)=>{integral.num = val.replace(/[^\d]/g, '')}" >
  214. <el-select v-model="integral.type" slot="prepend" placeholder="请选择" style="width: 80px;">
  215. <el-option label="奖分" value="1"></el-option>
  216. <el-option label="扣分" value="2"></el-option>
  217. </el-select>
  218. </el-input>
  219. </el-form-item>
  220. </el-form>
  221. <span slot="footer">
  222. <el-button @click="publicClose()">取消</el-button>
  223. <el-button type="primary" @click="integralSend('integral')">完成</el-button>
  224. </span>
  225. </el-dialog>
  226. </div>
  227. </template>
  228. <script>
  229. const validatorNoZero = (rule, value, callback) => {//设置记分的验证
  230. if (value === 0) {
  231. return callback(new Error("记分不能为0"));
  232. } else if (value === '') {
  233. return callback(new Error("记分不能为空"));
  234. }else if (isNaN(value)){
  235. return callback(new Error("积分必须为数字值"));
  236. }else{
  237. callback();
  238. }
  239. };
  240. export default {
  241. name: 'taskDetailsPopup',
  242. props: {
  243. title: {
  244. type: String,
  245. default: ''
  246. },
  247. visible: {
  248. type: Boolean,
  249. default: false
  250. },
  251. id: {
  252. type: Number,
  253. default: 0
  254. },
  255. showWork: {
  256. type: Boolean,
  257. default: true
  258. },
  259. detailType: {
  260. type: String,
  261. default: ''
  262. }
  263. },
  264. data() {
  265. return {
  266. Delay_to_open: false, //打开抽屉
  267. loading: false,
  268. workDetailData: {
  269. process: [],
  270. dept_list: [],
  271. point_config: {
  272. base_point: '0'
  273. }
  274. },
  275. // itemId: getId,
  276. isOne: false,
  277. text_list: [],
  278. isIntegral: false,
  279. point_total: 0,
  280. point_list: [],
  281. activeName: 'work',
  282. getDataUrl: '/api/integral/work',
  283. params: {},
  284. userId: this.$getUserData().id,
  285. isSlider: false,//更新进度弹窗
  286. progress: 0,//更新进度modus
  287. isOne: false,//记一条
  288. textarea: '',//记录一条内容
  289. isIntegral:false,//记分记录
  290. integral:{
  291. text:'',
  292. num:0,
  293. type:"1",
  294. },
  295. formRules:{//记分验证
  296. num: [
  297. {
  298. required: true,
  299. validator: validatorNoZero,
  300. trigger: "blur"
  301. }
  302. ],
  303. },
  304. keepTheScore: false,
  305. employeeMe: {},
  306. };
  307. },
  308. mounted() {
  309. this.detailType == 2 ? (this.getDataUrl = '/api/integral/schedule') : (this.getDataUrl = '/api/integral/work');
  310. this.$nextTick(() => {
  311. this.getData();
  312. this.Delay_to_open = this.visible; //更换打开抽屉时机,避免打开两次
  313. });
  314. },
  315. methods: {
  316. employee_me(data){
  317. let params = {
  318. id: data.employee_id
  319. };
  320. this.$axios('get', '/api/employee/info', params)
  321. .then(res => {
  322. this.employeeMe = res.data.data
  323. this.keepTheScore = this.keepThe_score(data)
  324. })
  325. },
  326. keepThe_score(cer){
  327. if(cer.status > 2){
  328. return false
  329. }
  330. if(this.userId == cer.reviewer_id){
  331. return true
  332. }
  333. return this.employeeMe.employee_detail.superior_list.some(x =>{
  334. if(this.userId == x.id){
  335. return true
  336. }
  337. })
  338. },
  339. deletejf_cli(cor,cur){
  340. this.$confirm('确定永久删除此项?', '提示', {
  341. confirmButtonText: '确定',
  342. cancelButtonText: '取消',
  343. type: 'warning'
  344. }).then(() => {
  345. let lier = cur == 1 ? this.point_list : this.text_list
  346. let libf = cur == 1 ? this.text_list : this.point_list
  347. lier.splice(cor,1)
  348. let data = {
  349. work_id: this.workDetailData.id,
  350. process: []
  351. }
  352. data.process =lier.concat(libf)
  353. if (data.process.length == 0) {
  354. data.process = "[1]"
  355. } else {
  356. data.process = JSON.stringify(data.process);
  357. }
  358. this.$axios('post', '/api/integral/work', data).then(res =>{
  359. if(res.data.code == 1){
  360. this.getData();
  361. }
  362. })
  363. }).catch(() => {});
  364. },
  365. //记分记录
  366. integralSend(formName){
  367. this.$refs[formName].validate((valid) => {
  368. if (valid) {
  369. var items = this.workDetailData.process.list || [];
  370. var process = {
  371. img_url: this.$getUserData().img_url,
  372. point: this.integral.type == "1" ? this.integral.num : '-' + this.integral.num,
  373. recorder_id: this.$getUserData().id,
  374. recorder: this.$getUserData().name,
  375. remark: this.integral.text,
  376. time: this.$moment().format('YYYY-MM-DD HH:mm')
  377. }
  378. var data = {
  379. work_id: this.workDetailData.id,
  380. process: [],
  381. }
  382. items.unshift(process);
  383. data.process = JSON.stringify(items);
  384. this.$axios('post','/api/integral/work',data).then(res => {
  385. if (res.data.code == 1) {
  386. this.publicClose();
  387. this.getData();
  388. }
  389. })
  390. }
  391. })
  392. },
  393. //记一条
  394. onerecord(){
  395. if(!this.textarea){
  396. this.$message.error("请输入备注内容");
  397. return
  398. }
  399. var items = this.workDetailData.process.list || [];
  400. var process = {
  401. img_url: this.$getUserData().img_url,
  402. point: 0,
  403. recorder_id: this.$getUserData().id,
  404. recorder: this.$getUserData().name,
  405. remark: this.textarea,
  406. time: this.$moment().format('YYYY-MM-DD HH:mm')
  407. }
  408. var data={
  409. work_id:this.workDetailData.id,
  410. process:[],
  411. }
  412. items.unshift(process);
  413. data.process=JSON.stringify(items);
  414. this.$axios('post', '/api/integral/work', data).then(res => {
  415. if (res.data.code == 1) {
  416. this.publicClose();
  417. this.getData();
  418. }
  419. })
  420. },
  421. //更新进度
  422. sliderShow(){
  423. this.progress = this.workDetailData.progress
  424. this.isSlider = true
  425. },
  426. sliderSend(){
  427. var self = this;
  428. let data = {
  429. work_id: self.workDetailData.id,
  430. progress: self.progress,
  431. }
  432. self.$axios('post','/api/integral/work',data).then(res => {
  433. if (res.data.code == 1) {
  434. this.getData();
  435. self.isSlider = false;
  436. }
  437. })
  438. },
  439. publicClose(){
  440. this.textarea = '';
  441. this.integral = {
  442. text: '',
  443. num: 0,
  444. type: "1",
  445. };
  446. this.isSlider = false;
  447. this.isOne = false;
  448. this.isIntegral = false;
  449. },
  450. // 关闭弹窗
  451. handleClose() {
  452. this.$emit('update:visible', false);
  453. },
  454. // 删除
  455. delItem() {
  456. console.log('删除');
  457. },
  458. // 获取数据
  459. getData() {
  460. this.loading = true;
  461. let data = this.detailType == 2 ? { schedule_id: this.id } : { work_id: this.id };
  462. this.$axios('get', this.getDataUrl, data).then(res => {
  463. if (res.data.code == 1) {
  464. this.workDetailData = res.data.data;
  465. this.employee_me(res.data.data)
  466. this.text_list = []
  467. this.point_list = []
  468. this.point_total = 0
  469. if (this.workDetailData.process.list && this.workDetailData.process.list.length > 0) {
  470. for (let i in this.workDetailData.process.list) {
  471. this.point_total += this.workDetailData.process.list[i].point * 1
  472. if (this.workDetailData.process.list[i].point != 0) {
  473. this.point_list.push(this.workDetailData.process.list[i])
  474. } else {
  475. if(this.workDetailData.process.list[i].recorder_id == this.workDetailData.employee_id) {
  476. this.text_list.push(this.workDetailData.process.list[i])
  477. }
  478. }
  479. }
  480. }
  481. }
  482. }).finally(() => {
  483. this.loading = false;
  484. });
  485. }
  486. }
  487. };
  488. </script>
  489. <style lang="scss" scoped="scoped">
  490. .details_content {
  491. & .d_userMessage {
  492. margin-left: 10px;
  493. }
  494. & .d_userMessage div:nth-child(1) {
  495. font-size: 16px;
  496. }
  497. & .d_userMessage div:nth-child(2) {
  498. font-size: 12px;
  499. color: #909399;
  500. }
  501. & .d_progress {
  502. padding: 12px 0;
  503. border-bottom: 1px solid #f1f1f1;
  504. margin-bottom: 10px;
  505. }
  506. & ul {
  507. padding: 12px 0;
  508. border-bottom: 1px solid #f1f1f1;
  509. & li {
  510. padding: 6px 0;
  511. }
  512. & .label {
  513. width: 80px;
  514. text-align: left;
  515. color: #909399;
  516. }
  517. & .content_text {
  518. flex: 1;
  519. }
  520. }
  521. }
  522. .fontColorF {
  523. color: #909399;
  524. }
  525. .details_content {
  526. padding: 20px;
  527. height: calc(100vh - 60px);
  528. overflow: auto;
  529. padding-bottom: 100px;
  530. .row_title {
  531. position: relative;
  532. margin: 0 0 20px 0;
  533. padding-top: 12px;
  534. font-size: 16px;
  535. color: #303133;
  536. line-height: 22px;
  537. }
  538. .row_title:before {
  539. position: absolute;
  540. top: 0;
  541. content: ' ';
  542. width: 100%;
  543. border-top: 1px #f8f8f8 solid;
  544. }
  545. .el-row {
  546. margin-bottom: 10px;
  547. font-size: 14px;
  548. .el-col-4 {
  549. color: #606266;
  550. }
  551. }
  552. }
  553. .details_title {
  554. font-size: 18px;
  555. padding: 20px;
  556. border-bottom: 1px #efefef solid;
  557. }
  558. .d_name{
  559. margin-left: 10px;
  560. }
  561. .d_content{
  562. margin-left: 50px;
  563. }
  564. .addJf{
  565. font-size: 13px;
  566. cursor: pointer;
  567. margin-bottom: 5px;
  568. padding: 5px;
  569. transition: all .3s;
  570. }
  571. .addJf:hover{
  572. color: #26a2ff !important;
  573. }
  574. .num{
  575. position: absolute;
  576. height: 22px;
  577. width: 20px;
  578. z-index: 999;
  579. left: 85px;
  580. line-height: 40px !important;
  581. }
  582. .add:before{
  583. position: absolute;
  584. content: "+";
  585. color: #f56c6c;
  586. }
  587. .jian:before{
  588. position: absolute;
  589. content: "-";
  590. color: #67c23a;
  591. }
  592. .delete_jfjl{
  593. transition: .3s all;
  594. cursor: pointer;
  595. margin: 0 0 0 10px;
  596. }
  597. .delete_jfjl:hover{
  598. color: #26a2ff;
  599. }
  600. </style>