resultValueEntryAll.vue 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472
  1. <template>
  2. <div :class="[{ bg_fff: skeletonLoad }]">
  3. <van-nav-bar title="结果值数据录入" left-text="" left-arrow @click-left="routerBak" />
  4. <VanSkeleton :skeLoad="skeletonLoad">
  5. <div style="height: 1rem;background-color: #fff;font-size: 0.32rem;" class="flex-box-ce flex-center-center" @click="selectUser=true">
  6. <span style="max-width: 4rem;" class="font-flex-word">{{userName}} </span>
  7. <van-icon name="arrow-down"/>
  8. </div>
  9. <header>
  10. <van-tabs v-model="active" swipeable><van-tab v-for="(item, index) in minFormactive" :title="item.name + '(' + item.age + ')'" :key="index"></van-tab></van-tabs>
  11. </header>
  12. <scroller :isNeed="isNeed" ref="work_bench_scroller" class="all" :class="{ isIos: isIos }" :style="{height:active == 0? 'calc(100% - 4.2rem)':'calc(100% - 1rem)'}">
  13. <div style="padding-bottom:2.65rem;" v-if="rvenotList.length > 0">
  14. <div v-for="(item, index) in rvenotList" :key="index" style="margin:.2rem 0;padding:0.24rem;background-color:#fff;">
  15. <div style="display:flex;border-bottom: 1px dashed #f3f3f3;padding-bottom: .25rem;">
  16. <userImage class="about-me__avatar" :id="item.employee_id" :user_name="item.employee_name" fontSize=".28" width="0.6rem" height="0.6rem"></userImage>
  17. <span style="font-size:.3rem;padding: 0.09rem 0px 0px 0.18rem;">{{ item.employee_name }}</span>
  18. <span class="font-flex-word" style="width: 4rem;display:inline-block;color:#a7a7a7;">
  19. <span v-for="(depts, keys) in item.dept_list" :key="keys" style="font-size:.25rem;padding: 0.14rem 0px 0px 0.15rem;display: inline-block;">
  20. {{ depts.dept_name }}
  21. <span v-if="item.dept_list.length - keys > 1">,</span>
  22. </span>
  23. </span>
  24. </div>
  25. <div>
  26. <div style="font-size:.32rem;margin: 0.2rem 0;">{{ item.index_name }}</div>
  27. <div class="flex-box" style="margin-bottom: 0.14rem;" v-for="(arr, att) in item.dil" :key="att" v-if="arr.prop">
  28. <div style="color:#929292;font-size:.27rem;width: 1.4rem;">{{ arr.lab }}:</div>
  29. <div class="flex-1" style="font-size:.28rem;">{{ arr.prop }}</div>
  30. </div>
  31. <div style="border:1px solid #e4e4e4;margin: .2rem 0 0 0;" v-if="active == 0">
  32. <van-field v-model="item.result_cache" placeholder="请输入结果值" v-if="item.index_type==1" />
  33. <van-cell><Uploader v-model="item.result_file.imgs" :max-count="3" :beforeRead="beforeRead" :accept="accept"/></van-cell>
  34. <van-cell v-if="item.result_file.append.length>0">
  35. <template >
  36. <div style="font-size: 0.24rem;" class="orange">文件仅支持在PC上‘上传’、‘删除’</div>
  37. <div class="blue" style="padding-top: 5px;font-size: 0.28rem;" v-for="(e,index2) in item.result_file.append" :key="index2" @click="downWgt(e.url,e.name)">{{e.name}}</div>
  38. </template>
  39. </van-cell>
  40. </div>
  41. <span v-else>
  42. <span style="color:#929292;font-size:.27rem;width: 1.4rem;display: inline-block;">结果值:</span>
  43. <span style="font-size:.28rem;" class="orange" v-if="item.index_type==1">{{ item.result_cache }} {{ item.unit }}</span>
  44. <template v-if="item.result_file.images">
  45. <div class="flex-box-ce" style="margin: 10px 0;" v-if="item.result_file.images.length>0">
  46. <van-image @click="openImg(item.result_file.images,index2)" v-for="(e,index2) in item.result_file.images" :key="index2" style="border-radius: 3px;margin-right: 10px;" width="100" height="100" :src="e.url"/>
  47. </div>
  48. </template>
  49. <template v-if="item.result_file.append.length>0">
  50. <div style="font-size: 0.24rem;" class="orange">文件仅支持在PC上‘上传’、‘删除’</div>
  51. <div class="blue" style="padding-top: 5px;font-size: 0.28rem;" v-for="(e,index2) in item.result_file.append" :key="index2" @click="downWgt(e.url,e.name)">{{e.name}}</div>
  52. </template>
  53. </span>
  54. </div>
  55. </div>
  56. </div>
  57. <van-empty description="暂无结果值录入" v-else />
  58. </scroller>
  59. <footer v-if="active == 0 && rvenotList.length > 0">
  60. <van-row class="footfoot">
  61. <van-col span="14" class="footcol" @click="save(1)">
  62. <div style="width:1rem;margin: auto;">
  63. <icon name="save" style="width: 0.5rem;height: 0.5rem;"></icon>
  64. <div style="font-size: .24rem;color:#8a8a8a;">暂存</div>
  65. </div>
  66. </van-col>
  67. <van-col span="10" class="footBut">
  68. <button style="" class="footcolButno" @click="rveno">取消</button>
  69. <button class="footcolButok" @click="save(0)">提交</button>
  70. </van-col>
  71. </van-row>
  72. </footer>
  73. <van-popup v-model:show="isResult" round >
  74. <div style="width: 7rem;padding: 0.24rem;box-sizing: border-box;">
  75. <div style="font-size: 0.28rem;margin-bottom: 0.24rem;">提交结果</div>
  76. <van-progress :percentage="percentage" stroke-width="8" />
  77. <div style="margin-top: 16px;border: 1px solid #f1f1f1;max-height: 500px;overflow-y: auto;" class="scroll-bar">
  78. <div class="flex-box-ce results" style="font-weight: 600;">
  79. <div style="border-right: 1px solid #f1f1f1;width: 40px;">序号</div>
  80. <div class="flex-1" style="border-right: 1px solid #f1f1f1;">姓名</div>
  81. <div class="flex-2" style="border-right: 1px solid #f1f1f1;">指标名称</div>
  82. <div class="flex-2">提交结果</div>
  83. </div>
  84. <div class="flex-box-ce results" v-for="(item, index) in results" :key="index">
  85. <div style="border-right: 1px solid #f1f1f1;width: 40px;">{{ results.length - index }}</div>
  86. <div class="flex-1" style="border-right: 1px solid #f1f1f1;">{{ item.info.employee_name }}</div>
  87. <div class="flex-2" style="border-right: 1px solid #f1f1f1;">{{ item.info.index_name }}</div>
  88. <div class="flex-2 red" v-if="item.msg">{{item.msg}}</div>
  89. <div class="flex-2 green" v-else>提交成功</div>
  90. </div>
  91. </div>
  92. <span slot="footer">
  93. <div class="flex-box-end" style="margin-top: 12px;" >
  94. <van-button type="primary" @click="isResult = false" size="small">确 定</van-button>
  95. </div>
  96. </span>
  97. </div>
  98. </van-popup>
  99. </VanSkeleton>
  100. <!-- 人员选择 -->
  101. <EmployeeSelector
  102. title="选择人员"
  103. :visible.sync="selectUser"
  104. @confirm="confirmCreator"
  105. :can_select_dept="false"
  106. :selected.sync="selected_data"
  107. ></EmployeeSelector>
  108. </div>
  109. </template>
  110. <script>
  111. import Vue from 'vue';
  112. import VanSkeleton from '@/performance/components/public/VanSkeleton';
  113. import EmployeeSelector from '@/components/EmployeeSelector';
  114. import Uploader from '@/components/OssUploader';
  115. import { Tab, Tabs,ImagePreview,Popup,Progress,DropdownMenu, DropdownItem, Icon } from 'vant';
  116. Vue.use(Tab).use(Tabs).use(ImagePreview).use(Popup).use(Progress).use(DropdownMenu).use(DropdownItem).use(Icon);
  117. export default {
  118. data() {
  119. return {
  120. isNeed:!this.$getCache('isAndroid'),
  121. skeletonLoad: true, // 骨架屏
  122. rvenotList: [],
  123. minFormactive: [{ name: '未录入', age: 0 }, { name: '已录入', age: 0 }],
  124. active: 0,
  125. pe_ids: 0, // 个人考核记录集合
  126. packageName: '', // 考核包名
  127. noStatusList: [], // 未录入指标
  128. statusList: [], // 已录入指标
  129. bootBool: false, // 防止多次请求
  130. isIos: this.$getCache('iPhone'),
  131. // 上传图标与附件
  132. img_fileList: [], // 图片附件
  133. images: [],
  134. accept:'image/jpeg,image/png,image/jpg',
  135. // 长连接结果
  136. results: [], //提交的返回结果集合
  137. isResult: false,
  138. percentage: 0,
  139. resultList: [], //要发送数据的集合
  140. resultIndex: 0,
  141. userName:'全部人员',
  142. selectUser:false,
  143. employee_list: [],
  144. selected_data: { dept: [], employee: [] } //传入已选部门
  145. };
  146. },
  147. components: {VanSkeleton,Uploader,EmployeeSelector },
  148. watch: {
  149. isResult(val) {
  150. if (!val) {
  151. this.results = []; //提交的返回结果集合
  152. this.isResult = false;
  153. this.percentage = 0;
  154. this.resultList = []; //要发送数据的集合
  155. this.resultIndex = 0;
  156. this.getPackageDtail();
  157. }
  158. },
  159. active(val) {
  160. if (val == 0) {
  161. this.rvenotList = this.noStatusList;
  162. } else if (val == 1) {
  163. this.rvenotList = this.statusList;
  164. }
  165. },
  166. },
  167. methods: {
  168. confirmCreator(data) {
  169. let userName = '';
  170. this.userName="全部人员"
  171. this.employee_list=[];
  172. if (data.employee !== null && data.employee.length != 0) {
  173. data.employee.forEach(item=>{
  174. this.employee_list.push(item.id);
  175. userName+=(item.name+',')
  176. })
  177. this.userName = userName;
  178. }
  179. this.selected_data = data;
  180. this.selectUser = false;
  181. this.getPackageDtail();
  182. },
  183. downWgt (url,name) {
  184. let self = this
  185. if(!window.plus){
  186. window.open(url, '_blank');
  187. return false
  188. }
  189. plus.downloader.createDownload(encodeURI(url),{filename:'_doc/update/'},
  190. function (d, status) {
  191. if (status == 200) {
  192. plus.runtime.openFile(d.filename,{},(err)=>{
  193. // console.log(JSON.stringify(err))
  194. });
  195. } else {
  196. self.$toast.clear()
  197. Notify({ type: 'danger', message: '下载失败,请稍后重试', duration: 1000 })
  198. }
  199. }
  200. ).start()
  201. },
  202. openImg(imgs,index){
  203. let imgArr=imgs.map(item=>{
  204. return item.url
  205. })
  206. ImagePreview({
  207. images:imgArr,
  208. startPosition:index,
  209. });
  210. },
  211. // 返回布尔值
  212. beforeRead(file) {
  213. const isJPG = /^image\/(jpeg|png|jpg)$/.test(file.type);
  214. // const isLt2M = file.size / 1024 / 1024 < 2;
  215. if (!isJPG) {
  216. this.$toast('上传图片只能是 jpeg|png|jpg 格式!');
  217. }
  218. // if (!isLt2M) {
  219. // this.$toast('上传图片大小不能超过 2MB!');
  220. // }
  221. return isJPG;
  222. },
  223. // 返回上一页
  224. routerBak() {
  225. this.$route_back();
  226. },
  227. rveno() {
  228. // 取消
  229. this.$route_back();
  230. },
  231. // 提交
  232. save(num) {
  233. let isLr = false;
  234. let parameterArr = [];
  235. // let data = {
  236. // id: this.id, // 个人考核包ID
  237. // cache: num, // 是否暂存 1 是 0 否(提交)
  238. // result_info: '' // 结果值信息
  239. // };
  240. this.noStatusList.some(item => {
  241. let images=[];//图片
  242. let result_info = [];
  243. if( item.result_file.imgs){
  244. item.result_file.imgs.forEach(imgItem=>{
  245. let name=imgItem.split('/');
  246. let obj={
  247. name:name[name.length-1],
  248. url:imgItem,
  249. }
  250. images.push(obj)
  251. })
  252. }
  253. item.result_file.images=images;
  254. result_info.push({
  255. dimension_key: item.dimension_id, //维度索引
  256. index_key: item.index_key, //指标索引
  257. index_id: item.index_id, //指标ID
  258. result_file: item.result_file
  259. });
  260. if(item.index_type == 1){
  261. result_info[0].result=item.result_cache
  262. }
  263. let data = {
  264. id: item.pe_id, //个人考核包ID
  265. cache: num, //是否暂存 1 是 0 否(提交)
  266. info: item,
  267. result_info: JSON.stringify(result_info),
  268. sub: num==3? 0:1
  269. };
  270. if (item.index_type == 1 && item.result_cache!==null && item.result_cache!=='') {
  271. parameterArr.push(data);
  272. }
  273. if (item.index_type != 1 && (item.result_file.append.length > 0 || item.result_file.images.length > 0)) {
  274. parameterArr.push(data);
  275. }
  276. });
  277. if (parameterArr.length==0) {
  278. this.$toast.fail('至少输入一项结果值或非量化指标添加一份附件');
  279. return false;
  280. }
  281. this.webSocket(parameterArr);
  282. return;
  283. if (this.bootBool) {
  284. return;
  285. }
  286. this.bootBool = true;
  287. this.$axiosUser('post', '/api/pro/per/package/record_result', data)
  288. .then(res => {
  289. this.$toast.success('设置成功');
  290. this.getPackageDtail();
  291. })
  292. .finally(() => {
  293. setTimeout(() => {
  294. this.bootBool = false;
  295. }, 3000);
  296. });
  297. },
  298. webSocket(data) {
  299. this.resultList = data;
  300. this.resultIndex = 0;
  301. this.percentage = 0;
  302. this.results = [];
  303. this.isResult = true;
  304. this.opneWebSocket();
  305. },
  306. opneWebSocket() {
  307. let wsData = this.resultList;
  308. if (wsData[this.resultIndex]) {
  309. this.$axiosUser('post', '/api/pro/per/package/record_result', wsData[this.resultIndex]).then(res => {
  310. this.onmessageWS(wsData[this.resultIndex],true);
  311. }).catch(e=>{
  312. let data=wsData[this.resultIndex];
  313. data.msg=e.data.msg;
  314. this.onmessageWS(data,false);
  315. // console.log(e)
  316. });
  317. }
  318. },
  319. onmessageWS(data,isJx) {
  320. this.results.unshift(data);
  321. this.resultIndex++;
  322. if(!isJx){
  323. return false
  324. }
  325. this.opneWebSocket();
  326. // 进度条
  327. let lng = this.resultList.length;
  328. this.percentage += Math.floor(100 / lng);
  329. if (lng == this.results.length) {
  330. this.percentage = 100;
  331. }
  332. },
  333. getPackageDtail() {
  334. this.$axiosUser('get', '/api/pro/per/package/result_job_list', { pe_ids: this.pe_ids, page: 1, page_size: 2000,employee_ids:JSON.stringify(this.employee_list) }).then(res => {
  335. let list = res.data.data.list;
  336. let statusList = []; // 已录入指标
  337. let noStatusList = []; // 未录入指标
  338. list.forEach(item => {
  339. let imgs=[];
  340. if(item.result_file){
  341. item.result_file.images.forEach(img => {
  342. imgs.push(img.url);
  343. })
  344. item.result_file.imgs=imgs;
  345. }
  346. item.dept_list = this.$getEmployeeMapItem(item.employee_id).employee_detail.dept_list;
  347. item.dil = [
  348. { lab: '考核标准', prop: item.index_standard ? item.index_standard : null},
  349. { lab: '权重%', prop: item.index_weight ? item.index_weight : null },
  350. { lab: '备注', prop: item.index_remark ? item.index_remark : null },
  351. { lab: '目标值', prop: item.target ? item.target : null },
  352. ];
  353. if (item.index_result_status == 1) {
  354. noStatusList.push(item);
  355. }
  356. if (item.index_result_status == 2) {
  357. statusList.push(item);
  358. }
  359. });
  360. this.statusList = statusList;
  361. this.noStatusList = noStatusList;
  362. if (this.active == 0) {
  363. this.rvenotList = this.noStatusList;
  364. } else if (this.active == 1) {
  365. this.rvenotList = this.statusList;
  366. }
  367. this.minFormactive[0].age = noStatusList.length; // 未
  368. this.minFormactive[1].age = statusList.length; // 已
  369. })
  370. .finally(() => {
  371. this.skeletonLoad = false;
  372. });
  373. }
  374. },
  375. created() {
  376. if (this.$route.query.pe_ids) {
  377. this.pe_ids = this.$route.query.pe_ids;
  378. this.packageName = this.$route.query.name;
  379. this.getPackageDtail();
  380. }
  381. },
  382. mounted() {}
  383. };
  384. </script>
  385. <style scoped lang="less">
  386. .userName{
  387. position: absolute;
  388. left: 0%;
  389. height: 50px;
  390. width: 100%;
  391. opacity: 0;
  392. }
  393. .results {
  394. border-bottom: 1px solid #f1f1f1;
  395. text-align: center;
  396. font-size: 0.24rem;
  397. }
  398. .results div {
  399. padding: 10px;
  400. }
  401. .line-feed {
  402. white-space: pre-wrap;
  403. }
  404. .all {
  405. height: calc(100% - 3.2rem);
  406. position: relative !important;
  407. background-color: #f5f7fa;
  408. padding-bottom: 0.3rem;
  409. overflow-y: scroll;
  410. }
  411. header {
  412. background-color: #fff;
  413. font-size: 0.3rem;
  414. z-index: 1;
  415. }
  416. .isIos {
  417. height: calc(100% - 3.6rem);
  418. }
  419. footer {
  420. // position: fixed;
  421. // left: 0px;
  422. // bottom: 0px;
  423. // width: 100%;
  424. background-color: #ffffff;
  425. z-index: 999;
  426. border-top: 1px solid #e2e2e2;
  427. .footfoot {
  428. padding: 0.1rem;
  429. // padding-bottom: 0.4rem !important;
  430. .footcol {
  431. text-align: center;
  432. }
  433. .footBut {
  434. display: flex;
  435. }
  436. .footcolButno {
  437. border: 1px solid #42a8ff;
  438. width: 1.4rem;
  439. height: 0.8rem;
  440. font-size: 0.28rem;
  441. color: #2f9eff;
  442. background-color: #ffffff;
  443. border-top-left-radius: 3px;
  444. border-bottom-left-radius: 3px;
  445. }
  446. .footcolButok {
  447. border: 0;
  448. width: 1.4rem;
  449. height: 0.8rem;
  450. font-size: 0.28rem;
  451. color: #fff;
  452. background-color: #42a8ff;
  453. border-top-right-radius: 3px;
  454. border-bottom-right-radius: 3px;
  455. }
  456. }
  457. }
  458. </style>