integral_application.vue 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555
  1. <template>
  2. <div>
  3. <van-nav-bar title="积分申请" left-text="返回" @click-left="$route_back" left-arrow></van-nav-bar>
  4. <div class="body_com">
  5. <scroller :isNeed="isNeed">
  6. <van-cell-group>
  7. <EmployeeSelectorCell required bar_title="选择录入对象" title="录入对象"
  8. v-model="employee_list"
  9. iconType="friends-o"
  10. :multi="false"
  11. :is_filtration_creator="true"
  12. ></EmployeeSelectorCell>
  13. <van-cell title="指定规则">
  14. <template slot="title">
  15. <span @click="openText">指定规则<van-icon name="question" class="fontColorC" style="position: relative;top: 2px;left: 3px;color:#969799;"/></span>
  16. </template>
  17. <template slot="right-icon">
  18. <van-switch :disabled="specified_rule_item" v-model="rule_switch" size="24"/>
  19. </template>
  20. </van-cell>
  21. <!--选择规则 -->
  22. <van-cell
  23. v-if="rule_switch"
  24. title="选择规则"
  25. @click="showRuleSelector = true"
  26. required
  27. :value="itemRule.length <= 0 ? '' : `已选${itemRule.length}条`"
  28. />
  29. <RuleScopeSelector :visible.sync = showRuleSelector :selected="itemRule" multi @confirm="selected => itemRule = selected" />
  30. <!-- <RuleCategorySelectorCell v-if="rule_switch" required ref="rule_selector" name="选择规则" title="选择规则" v-model="itemRule" scope/>-->
  31. </van-cell-group>
  32. <div v-for="(item, index) in items" :key="index">
  33. <van-cell-group>
  34. <template v-if="item.item_id">
  35. <div class="flex-box-ce" style="padding: 0.24rem 0.32rem;font-size: 0.32rem;padding-bottom: 0rem;">
  36. <div class="flex-1 item-title">已选规则({{index+1}})</div>
  37. <div @click="diy_item_del(item,index)" class="red" style="font-size: 0.28rem">删除</div>
  38. </div>
  39. <div style="background-color: #f5f7fa;border-radius: 5px;padding:0.2rem;margin: 0.24rem 0.32rem;font-size: 0.28rem;">
  40. <div style="word-break:break-all">{{ item.ruleData.remark }}</div>
  41. <div v-if="item.ruleData.range_type == 1">
  42. <span :class="item.ruleData.min_point > 0 ? 'red' : 'green'">{{ item.ruleData.min_point }}</span>
  43. {{ $getTypesName(item.pt_id) }}
  44. </div>
  45. <div v-if="item.ruleData.range_type == 2">
  46. <span :class="item.ruleData.min_point > 0 ? 'red' : 'green'">{{ item.ruleData.min_point }}</span>
  47. <span :class="item.ruleData.max_point > 0 ? 'red' : 'green'">{{ item.ruleData.max_point }}</span>
  48. {{ $getTypesName(item.pt_id) }}
  49. </div>
  50. </div>
  51. </template>
  52. <!--申请事由 -->
  53. <van-cell required >
  54. <div class="flex-box-ce" style="font-size: 0.32rem;">
  55. <div class="flex-1">事件内容及描述</div>
  56. <div class="blue" @click="item.remark=''" style="font-size: 0.28rem">清空</div>
  57. </div>
  58. <Mtextarea v-model="item.remark" placeholder="请输入事件内容" name="申请事由" v-validate="'required|max:300'"
  59. :text_max="300" :imgs_max="3" images :imgs.sync="item.files"
  60. ></Mtextarea>
  61. </van-cell>
  62. <!--发生时间 -->
  63. <DateCell required title="发生时间" name="日期" :maxDate="maxDate" v-model="item.event_time"></DateCell>
  64. <!--选择递交审批人 -->
  65. <div>
  66. <van-cell v-if="change_reviewer" is-link required title="审批人" class="noInput" :value="userName" ></van-cell>
  67. <EmployeeSelectorCell
  68. v-else
  69. is_employee_list
  70. bar_title="选择递交审批人"
  71. title="递交审批"
  72. v-model="item.reviewer_list"
  73. :multi="false"
  74. iconType="records"
  75. :max="1"
  76. :employee_list="superior_list"
  77. ></EmployeeSelectorCell>
  78. </div>
  79. </van-cell-group>
  80. </div>
  81. <div style="height: 1rem;"></div>
  82. </scroller>
  83. </div>
  84. <div style="padding:0.32rem;" class="flex-box-ce">
  85. <van-button size="large" plain type="info" style="margin-right: 0.2rem;width: 2rem;" @click="openSelect" v-if="rule_switch">已选{{items.length}}条</van-button>
  86. <van-button size="large" @click="data_verify" :disabled="subloading" type="info">提交</van-button>
  87. </div>
  88. <!-- 提交结果 -->
  89. <van-popup v-model="isResult" style="width: 90%;border-radius: 5px;">
  90. <div v-if="!isShowError" style="padding: 0.24rem;">
  91. <van-progress :percentage="percentage" />
  92. <div style="margin-top: 10px;border: 1px solid #f1f1f1;max-height: 7rem;overflow-y: auto;" class="scroll-bar">
  93. <div class="flex-box-ce results" style="font-weight: 600;">
  94. <div style="border-right: 1px solid #f1f1f1;width: 40px;">序号</div>
  95. <div class="flex-1" style="border-right: 1px solid #f1f1f1;">申请对象</div>
  96. <div class="flex-1" >处理结果</div>
  97. </div>
  98. <div class="flex-box-ce results" v-for="(item, index) in results" :key="index">
  99. <div style="border-right: 1px solid #f1f1f1;width: 40px;">{{results.length-index}}</div>
  100. <div class="flex-1" style="border-right: 1px solid #f1f1f1;">{{ item.target }}</div>
  101. <div class="flex-1 font-flex-word" v-if="item.status == 1">
  102. <span class="green">提交成功</span>
  103. </div>
  104. <div class="flex-1 red" v-else>{{ item.msg }}</div>
  105. </div>
  106. </div>
  107. <div class="flex-box-end" style="margin-top: 20px;" v-show="results.length==resultList.length">
  108. <van-button type="info" @click="isResult = false" size="small">确 定</van-button>
  109. </div>
  110. </div>
  111. <div v-else>
  112. <div style="text-align: center;" class="red">{{errorMsg}}</div>
  113. <div>
  114. <div class="flex-box-end" style="margin-top: 10px;">
  115. <van-button type="info" @click="isResult = false" size="small">确 定</van-button>
  116. </div>
  117. </div>
  118. </div>
  119. </van-popup>
  120. </div>
  121. </template>
  122. <script>
  123. import Mtextarea from '@/components/Mtextarea'
  124. import EmployeeSelectorCell from '@/components/EmployeeSelectorCell'
  125. // import RuleCategorySelectorCell from '@/components/RuleCategorySelectorCell1'
  126. import CategorySelectorCell from '@/components/CategorySelectorCell'
  127. import DateCell from '@/components/DateCell'
  128. import moment from 'moment'
  129. import Vue from 'vue'
  130. import {Switch,Progress,Icon } from 'vant'
  131. import RuleScopeSelector from "../../../components/RuleScopeSelector.vue";
  132. Vue.use(Switch).use(Progress).use(Icon)
  133. export default {
  134. name: 'integral_application',
  135. components: {
  136. RuleScopeSelector,
  137. DateCell,
  138. Mtextarea,
  139. EmployeeSelectorCell,
  140. // RuleCategorySelectorCell,
  141. CategorySelectorCell,
  142. },
  143. data () {
  144. let date = moment().format('YYYY-MM-DD')
  145. return {
  146. showRuleSelector:false,
  147. specified_rule_item:this.$userInfo().site_config.specified_rule_item? true:false,//奖扣时是否必选规则
  148. isNeed:!this.$getCache('isAndroid'),
  149. close_buttom: false,
  150. superior_list:this.$userInfo().employee_detail.superior_list,
  151. employee_list: [this.$userInfo()],
  152. employeeid:this.$userInfo().id,
  153. ptId:3,
  154. change_reviewer:Number(this.$userInfo().site_config.change_reviewer),//是否允许员工修改审批人
  155. userName:'',
  156. items: [],
  157. item:{
  158. rule_id: '',
  159. item_id: '',
  160. remark: '',
  161. ruleData:{},
  162. event_time: moment().format('YYYY-MM-DD'),
  163. // 审批人信息
  164. reviewer_id: '',
  165. reviewer_list:[],
  166. // 附件
  167. fileList: [],
  168. files: [],
  169. },
  170. subloading: false,
  171. itemRule:[],//规则数组
  172. rule_switch:true,
  173. maxDate: new Date(),
  174. // 长连接结果
  175. results: [], //提交的返回结果集合
  176. isResult: false,
  177. percentage: 0,
  178. resultList:[],//要发送数据的集合
  179. resultIndex:0,
  180. isShowError:false,
  181. errorMsg:'服务器繁忙,请稍后再试',
  182. }
  183. },
  184. watch: {
  185. rule_switch(){
  186. if(this.rule_switch){
  187. this.items=[];
  188. }else{
  189. this.items=[JSON.parse(JSON.stringify(this.item))];
  190. }
  191. },
  192. itemRule(rules){
  193. if (rules.length > 0) {
  194. let items = []
  195. rules.forEach(e => {
  196. if(this.isItemId(e.id,1)){
  197. items.push(this.isItemId(e.id,1))
  198. }else{
  199. let item = JSON.parse(JSON.stringify(this.item));
  200. item.remark = e.remark;
  201. item.ruleData = e;
  202. item.rule_id = e.rule_id;
  203. item.item_id = e.id;
  204. item.pt_id = e.pt_id;
  205. items.push(item);
  206. }
  207. });
  208. this.items = items;
  209. } else {
  210. this.items = [];
  211. }
  212. },
  213. employee_list(val) {
  214. if (val.length > 0) {
  215. this.employeeid=val[0].id;
  216. this.initializesReviewer(val[0].id);
  217. } else {
  218. this.employeeid = ''
  219. this.items.forEach(item=>{
  220. item.reviewer_id='';
  221. item.reviewer_list=[];
  222. })
  223. }
  224. },
  225. isResult(val){
  226. if(!val){
  227. this.isShowError = false;
  228. this.errorMsg='服务器繁忙,请稍后再试';
  229. this.itemRule=[];//规则数组
  230. this.rule_switch=true;
  231. this.items=[];
  232. // this.employee_list= [];
  233. this.$socketApi.closewebsocket();
  234. }
  235. },
  236. },
  237. mounted() {
  238. this.initializesReviewer(this.employeeid);
  239. if (window.plus) { //针对IOS 11的优化
  240. var str = window.plus.device.model;
  241. if (str.indexOf('11') >= 0) {
  242. this.maxDate=new Date(2050,12,30);
  243. }
  244. }
  245. },
  246. methods: {
  247. openText(){
  248. this.$dialog.alert({
  249. message: '指定规则:根据公司已经制定好的积分规则标准事由来进行奖扣或申请积分\n不指定规则:可以自由填写事由(即积分规则标准以外的内容)进行奖扣或申请积分',
  250. }).then(() => {
  251. // on close
  252. });
  253. },
  254. diy_item_del (item,index) {
  255. this.$dialog.confirm({
  256. message:'你确定要删除该奖扣规则吗'
  257. }).then(() => {
  258. if(this.rule_switch){
  259. this.itemRule.some((e,index2)=>{
  260. if(item.item_id==e.id){
  261. this.itemRule.splice(index2, 1);
  262. this.items.splice(index, 1)
  263. return true;
  264. }
  265. })
  266. }else{
  267. this.items.splice(index, 1)
  268. }
  269. }).catch(() => {});
  270. },
  271. //判断是否已经存在
  272. isItemId(id,index){
  273. let item='';
  274. this.items.some((x)=> {
  275. if(index==1){
  276. if(x.item_id==id){
  277. item=x
  278. return true;
  279. }
  280. }else{
  281. if(x.rule_id==id){
  282. item=x
  283. return true;
  284. }
  285. }
  286. });
  287. return item
  288. },
  289. // 初始化审核人
  290. initializesReviewer(id) {
  291. var superiorList=this.$getEmployeeMapItem(id).employee_detail.superior_list;//选择录入对象的上级集合
  292. this.superior_list=superiorList;//当前选中人的 上级
  293. let obj = this.item;
  294. this.$axiosUser('get','/api/pro/employee/superior',{employee_id:id,manage_scope: '1',platform:'1'}).then(res => {
  295. var list=res.data.data.list;
  296. var users= [...list].filter(x => [...superiorList].some(y => y.id === x.id));//获取录入对象的上级与直属上级的交集
  297. if(users.length>0){
  298. obj.reviewer_id = users[0].id;
  299. obj.reviewer_list=[users[0]];
  300. this.userName=users[0].name
  301. }else{
  302. obj.reviewer_id = list[0].id;
  303. obj.reviewer_list=[list[0]];
  304. this.userName=list[0].name
  305. }
  306. this.items.forEach(item=>{
  307. if(users.length>0){
  308. item.reviewer_id = users[0].id;
  309. item.reviewer_list=[users[0]];
  310. }else{
  311. item.reviewer_id = list[0].id;
  312. item.reviewer_list=[list[0]];
  313. }
  314. })
  315. });
  316. },
  317. openSelect(){
  318. if(this.rule_switch){
  319. this.$refs.rule_selector.show_dept_selector=true;
  320. }
  321. },
  322. showMessage(str){
  323. this.$notify({type: 'danger', message: str})
  324. },
  325. data_verify () {
  326. let self = this
  327. if(!this.employeeid){
  328. this.showMessage('请选择录入对象')
  329. return false
  330. }
  331. if(this.items.length==0){
  332. this.showMessage('请选择规则')
  333. return false
  334. }
  335. let str='';
  336. let isError=false
  337. this.items.some((item,i)=>{
  338. if(!item.remark){
  339. isError=true;
  340. str='第' + (i + 1) + '条申请,请输入事件内容及描述';
  341. return true;
  342. }
  343. if(item.reviewer_list.length==0){
  344. isError=true;
  345. str='第' + (i + 1) + '条申请,请选择审批人';
  346. return true;
  347. }
  348. if(item.event_time>moment().format('YYYY-MM-DD')){
  349. isError=true;
  350. str='第' + (i + 1) + '条申请,发生时间不能大于今天';
  351. return true;
  352. }
  353. })
  354. if(isError){
  355. this.showMessage(str)
  356. return false;
  357. }
  358. let data = [];
  359. this.items.forEach(element => {
  360. let obj={
  361. rule_id: element.rule_id || 0,
  362. employee_id: this.employeeid || 0,
  363. item_id: element.item_id || 0,
  364. remark: element.remark,
  365. event_time: element.event_time,
  366. files: element.files,
  367. type:'point_apply'
  368. }
  369. if(element.reviewer_list[0]){
  370. obj.reviewer_id=element.reviewer_list[0].id;
  371. }
  372. data.push(obj);
  373. });
  374. if(data[0].item_id){
  375. this.webSocket(data);
  376. return false;
  377. }
  378. this.subloading=true;
  379. let obj={items:data}
  380. this.$axiosUser('post', '/api/pro/integral/review/apply', obj, 'v2').then(res => {
  381. let item=res.data.data.list[0]
  382. if(item.status===0){
  383. this.showMessage(item.msg)
  384. }else{
  385. this.$toast('提交成功')
  386. this.itemRule=[];//规则数组
  387. this.rule_switch=true;
  388. this.items=[];
  389. }
  390. }).finally(() => {
  391. this.subloading = false;
  392. })
  393. },
  394. webSocket(data){
  395. this.resultList=data;
  396. this.resultIndex=0;
  397. this.percentage=0;
  398. this.results=[];
  399. this.isResult=true;
  400. this.opneWebSocket()
  401. },
  402. opneWebSocket() {
  403. let wsData=this.resultList;
  404. if(wsData[this.resultIndex]&&!this.isShowError){
  405. this.$socketApi.sendData(wsData[this.resultIndex],this.onmessageWS)
  406. }
  407. },
  408. onmessageWS(e){
  409. if(e.type=='point_apply'){
  410. this.results.unshift(e.result);
  411. this.resultIndex++;
  412. this.opneWebSocket();
  413. // 进度条
  414. let lng = this.resultList.length;
  415. this.percentage += Math.floor(100 / lng);
  416. if (lng == this.results.length) {
  417. this.percentage = 100;
  418. }
  419. }
  420. // 连接不上
  421. if(e.type=='error'){
  422. this.errorMsg=e.msg
  423. this.isShowError = true;
  424. }
  425. },
  426. }
  427. }
  428. </script>
  429. <style scoped>
  430. /deep/ .van-dialog__message{
  431. text-align: left;
  432. }
  433. .noInput{
  434. background-color: #F5F7FA;
  435. border-color: #E4E7ED;
  436. color: #C0C4CC;
  437. cursor: not-allowed;
  438. }
  439. .results{
  440. font-size: 0.28rem;
  441. text-align: center;
  442. border-bottom: 1px solid #f1f1f1;
  443. }
  444. .results div {
  445. padding: 3px;
  446. }
  447. .item-title{
  448. font-size: 0.28rem;
  449. }
  450. .search-icon {
  451. font-size: 16px;
  452. line-height: inherit;
  453. }
  454. .body_com {
  455. height: calc(100% - 2.6rem);
  456. position: relative;
  457. overflow-y: scroll;
  458. }
  459. .result{
  460. height: calc(100vh - 6.6rem);
  461. position: relative;
  462. }
  463. .add_item_cell .van-icon {
  464. color: #1989fa;
  465. }
  466. .add_item_cell .van-cell__title span {
  467. color: #1989fa;
  468. }
  469. .item-del-btn {
  470. float: right;
  471. }
  472. .point-remark-box {
  473. color: #969799;
  474. padding: 0.16rem 0.32rem;
  475. position: relative;
  476. padding-top: 0;
  477. padding-left: 2.1rem;
  478. font-size: 0.24rem;
  479. }
  480. .point-remark-box:after {
  481. position: absolute;
  482. box-sizing: border-box;
  483. content: ' ';
  484. pointer-events: none;
  485. right: 0;
  486. bottom: 0;
  487. left: 0.32rem;
  488. border-bottom: 0.02rem solid #ebedf0;
  489. -webkit-transform: scaleY(0.5);
  490. transform: scaleY(0.5);
  491. }
  492. .rentrun {
  493. width:100vw;
  494. background: #fff;
  495. }
  496. .successful_alert{
  497. padding-top:0.5rem;
  498. }
  499. .successful_alert /deep/ .head_icon .sucessful_icon{
  500. width:1.28rem;
  501. height:1.28rem;
  502. color:#26A2FF;
  503. padding-top:0.5rem;
  504. }
  505. .successful_alert /deep/ .head_icon .overdue_deduction{
  506. width:1.28rem;
  507. height:1.28rem;
  508. color:#F56C6C;
  509. padding-top:0.5rem;
  510. }
  511. .row_line_bg{
  512. background: #f1f1f1;
  513. font-size: 0.32rem;
  514. padding: 0.1rem 0.32rem;
  515. }
  516. .buttom_btn{
  517. line-height: 0.83rem;
  518. text-align: center;
  519. font-size: 0.32rem;
  520. position: relative;
  521. /* color: #909399 */
  522. }
  523. .buttom_btn.one::after{
  524. content: '';
  525. width: 1px;
  526. height: 0.85rem;
  527. background: #f1f1f1;
  528. display: block;
  529. position: absolute;
  530. right: 0;
  531. top: 0;
  532. bottom: 0;
  533. }
  534. </style>