resume_check

Boss|智联|前程无忧|猎聘|more...

  1. // ==UserScript==
  2. // @name resume_check
  3. // @namespace http://tampermonkey.net/
  4. // @version 2.1
  5. // @description Boss|智联|前程无忧|猎聘|more...
  6. // @author Lonely
  7. // @match https://www.zhipin.com/web/boss/*
  8. // @match https://www.zhipin.com/chat/im*
  9. // @match https://lpt.liepin.com/cvview/showresumedetail*
  10. // @match https://lpt.liepin.com/im/imresourceload*
  11. // @match https://rd6.zhaopin.com/resume/detail*
  12. // @match https://ehire.51job.com/Candidate/ResumeView.aspx*
  13. // @match https://ehire.51job.com/Candidate/ResumeViewFolderV2.aspx*
  14. // @require https://cdn.jsdelivr.net/npm/jquery@3.4.1/dist/jquery.slim.min.js
  15. // @require https://cdn.jsdelivr.net/npm/vue
  16. // @require https://unpkg.com/element-ui/lib/index.js
  17. // @grant GM_xmlhttpRequest
  18. // ==/UserScript==
  19.  
  20.  
  21. (function () {
  22. 'use strict';
  23. //jq方式导入 element-ui.css
  24. $("head").append($(`<link rel="stylesheet" href="https://unpkg.com/element-ui/lib/theme-chalk/index.css">`));
  25.  
  26. (function () {
  27. // 做一个div套着,后续直接innerHTML一把嗦
  28. var div = document.createElement('div')
  29. div.id = 'script_ele_box'
  30. div.style.height = '10rem'
  31. div.style.width = '18rem'
  32. div.style.background = '#F7EED6'
  33. div.style.opacity = '0.7'
  34. div.style.borderRadius = '10px'
  35. div.style.position = 'fixed'
  36. div.style.zIndex = '999'
  37. div.innerHTML = `
  38. <div id="resume_app" v-show='is_show()'>
  39. <el-button type="warning" plain :loading="loading_flag" @click="match">{{ fresh_text }}</el-button>
  40. <p style="font-size: 1.5rem; position: absolute;top: 4rem; pointer-events: none"> {{ message }}</p>
  41. <a class="preview" href='#' target='_blank' style="font-size: 1.8rem position: absolute;top: 1rem"></a>
  42. <el-table
  43. border
  44. ref="singleTable"
  45. :data="tableData"
  46. tooltip-effect="dark"
  47. :header-cell-style="{background:'#F7EED6',color:'#606266'}"
  48. :row-style="{background:'#F7EED6',color:'#606266'}"
  49. style="width: 100%;position: absolute;top: 150px; pointer-events: auto;border-radius: 10px">
  50. <el-table-column
  51. type="index"
  52. min-width="5%"
  53. align="center">
  54. </el-table-column>
  55. <el-table-column
  56. property="type"
  57. label="类别"
  58. min-width="20%"
  59. align="center">
  60. </el-table-column>
  61. <el-table-column
  62. :show-overflow-tooltip="true"
  63. property="exp"
  64. label="经验"
  65. min-width="55%"
  66. align="center">
  67. </el-table-column>
  68. <el-table-column
  69. property="checked"
  70. label="存在"
  71. min-width="20%"
  72. align="center">
  73. </el-table-column>
  74. </el-table>
  75. </div>
  76. `
  77. document.body.appendChild(div);
  78. window.script_ele_box = div; // 把这个div赋值给window对象, 方便后续程序改变其可见性
  79.  
  80. })()
  81.  
  82. // 拖动
  83. class Drag {
  84. constructor() {
  85. this.ele = document.querySelector("#script_ele_box");
  86. this.m = this.move.bind(this);
  87. this.u = this.up.bind(this);
  88.  
  89. this.init();
  90. this.addEvent();
  91. }
  92.  
  93. init() {
  94. this.pos = localStorage.getItem("pos") ? JSON.parse(localStorage.getItem("pos")) : {
  95. l: 200,
  96. t: 100
  97. };
  98. this.ele.style.left = this.pos.l + "px";
  99. this.ele.style.top = this.pos.t + "px"
  100. }
  101.  
  102. addEvent() {
  103. this.ele.addEventListener('mousedown', this.down.bind(this));
  104. }
  105.  
  106. down(eve) {
  107. var e = eve || window.event;
  108. this.x = e.offsetX;
  109. this.y = e.offsetY;
  110.  
  111. document.addEventListener('mousemove', this.m);
  112. document.addEventListener('mouseup', this.u);
  113.  
  114. }
  115.  
  116. move(eve) {
  117. // console.log(this)
  118. var e = eve;
  119. //移动时的鼠标坐标
  120. this.ele.style.left = e.clientX - this.x + "px";
  121. this.ele.style.top = e.clientY - this.y + "px";
  122. }
  123.  
  124. up() {
  125. var pos = {
  126. l: this.ele.offsetLeft,
  127. t: this.ele.offsetTop
  128. }
  129. localStorage.setItem("pos", JSON.stringify(pos))
  130. //删除移动和抬起事件
  131. document.removeEventListener('mousemove', this.m)
  132. document.removeEventListener('mouseup', this.u)
  133. }
  134. }
  135.  
  136. new Drag();
  137.  
  138. var app = new Vue({
  139. el: '#resume_app',
  140. data: {
  141. message: '',
  142. // update_area: 0,
  143. intervalId: null,
  144. resume_detail: {
  145. // url和css选择器的一个键值对, 下面的extract_experience 根据此键值对获取其简历区域的html代码发给后端匹配
  146. 'https://www.zhipin.com/web/boss/index': '.resume-detail',
  147. 'https://www.zhipin.com/chat/im': '.resume-item-content',
  148. 'https://rd6.zhaopin.com/resume/detail': '.resume-detail',
  149. 'https://ehire.51job.com/Candidate/ResumeView.aspx': '#divResume',
  150. 'https://ehire.51job.com/Candidate/ResumeViewFolderV2.aspx': '#divResume',
  151. 'https://lpt.liepin.com/cvview/showresumedetail': '#water-mark-wrap',
  152. 'https://lpt.liepin.com/im/imresourceload': '.__im_pro__resume-container',
  153. },
  154. tableData: [], // 从python端匹配结果, 数组
  155. loading_flag: false,
  156. fresh_text: '刷新'
  157. },
  158. methods: {
  159. is_show: function () {
  160. // 如果当前链接在可显示的数组中, 则返回true, 即显示插件内容
  161. var match_url = document.location.origin + document.location.pathname
  162. var flag = match_url in this.resume_detail
  163. if (flag) {
  164. window.script_ele_box.style.visibility = "visible"
  165. } else {
  166. window.script_ele_box.style.visibility = "hidden"
  167. }
  168. return flag
  169. },
  170. extract_experience: function () {
  171. // 提取经历部分的HTML代码
  172. var match_url = document.location.origin + document.location.pathname
  173.  
  174. if (document.querySelector(this.resume_detail[match_url])) {
  175. return document.querySelector(this.resume_detail[match_url]).outerHTML
  176. }
  177. return null
  178. },
  179. match: function () {
  180. this.loading_flag = true
  181. this.fresh_text = '加载中'
  182. var experience_html = this.extract_experience()
  183. if (!experience_html) {
  184. alert('请在在线简历页面点击匹配')
  185. this.loading_flag = false
  186. this.fresh_text = '刷新'
  187. return
  188. }
  189. var self = this
  190. GM_xmlhttpRequest({
  191. method: "POST",
  192. timeout: 5000,
  193. url: "http://183.62.69.218:9882/api/v1/resume/parse_html_exp",
  194. data: 'data=' + encodeURIComponent(experience_html),
  195. headers: {
  196. 'accept': 'application/json',
  197. 'Content-Type': 'application/x-www-form-urlencoded',
  198. },
  199. onload: function (e) {
  200. if (e.status !== 200) {
  201. alert("插件错误:" + e.statusText)
  202. self.fresh_text = '刷新'
  203. self.loading_flag = false
  204. }
  205. self.loading_flag = false
  206. self.fresh_text = '刷新'
  207. var python_response = JSON.parse(e.response)
  208. var _a = document.querySelector('a.preview')
  209. self.tableData = python_response.data.detail
  210. self.message = `${python_response.data.name}:${python_response.data.score}分`
  211. if (python_response.data.score > 0) {
  212. _a.href = python_response.data.file_path
  213. _a.text = '简历预览'
  214. } else {
  215. _a.href = ''
  216. _a.text = ''
  217. }
  218. },
  219. onerror: function (e) {
  220. alert("插件错误: UNKNOWN")
  221. self.fresh_text = '刷新'
  222. self.loading_flag = false
  223. },
  224. // 配合timeout超时控制, 超时提示并将加载中归位
  225. ontimeout: function (e) {
  226. alert("插件错误: TIMEOUT")
  227. self.fresh_text = '刷新'
  228. self.loading_flag = false
  229. }
  230. })
  231. },
  232. },
  233. })
  234.  
  235. })();