uploadResume.vue 9.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420
  1. <template>
  2. <view class="upload-container">
  3. <!-- 顶部插图区域 -->
  4. <view class="header-notice">
  5. <image style="width: 30rpx;height: 30rpx;" src="https://data.qicai321.com/minlong/57d03f34-47de-43fe-8730-b764ed8bdda2.png" mode="aspectFit"></image>请上传简历文件,格式支持pdf及doc格式
  6. </view>
  7. <view class="illustration">
  8. <image class="img" src="https://data.qicai321.com/minlong/09d4daa6-6507-4133-81ff-68e47b0259e5.png" mode="aspectFit"></image>
  9. </view>
  10. <!-- 上传按钮 -->
  11. <button class="upload-btn" @click="chooseFile" :disabled="!!uploadedFile">
  12. {{uploadedFile ? '已上传文件' : '选择并上传文件'}}
  13. </button>
  14. <!-- 文件信息显示区域 -->
  15. <view class="file-info" v-if="uploadedFile">
  16. <view class="file-info-header">
  17. <view class="file-info-title">已上传文件</view>
  18. <view class="file-delete" @click="clearFile">
  19. <image class="delete-icon" src="https://data.qicai321.com/minlong/delete.png" mode="aspectFit"></image>
  20. </view>
  21. </view>
  22. <view class="file-info-content">
  23. <view class="file-name">{{uploadedFile.name}}</view>
  24. <view class="file-size">{{formatFileSize(uploadedFile.size)}}</view>
  25. <view class="file-time">上传时间:{{uploadedFile.uploadTime}}</view>
  26. </view>
  27. </view>
  28. <!-- 使用说明区域 -->
  29. <view class="instructions">
  30. <view class="title">使用说明及注意事项</view>
  31. <view class="instruction-list">
  32. <view class="instruction-item">1. 点击【选择并上传文件】按钮,上传简历;</view>
  33. <view class="instruction-item">2. 请在本地选择简历文件,文件小于30mb;</view>
  34. <view class="instruction-item">3. 支持pdf及doc格式。</view>
  35. </view>
  36. <!-- 隐私提示 -->
  37. <view class="privacy-notice">
  38. * 我们将对您的隐私保护,所内容仅用于评估岗位的匹配度。
  39. </view>
  40. </view>
  41. </view>
  42. </template>
  43. <script>
  44. import { apiBaseUrl } from '@/common/config.js';
  45. export default {
  46. data() {
  47. return {
  48. fileType: ['pdf', 'doc', 'docx'],
  49. maxSize: 30 * 1024 * 1024, // 30MB in bytes
  50. uploadedFile: null
  51. }
  52. },
  53. methods: {
  54. // 调用简历解析接口
  55. /* async parseResume(fileUrl) {
  56. try {
  57. const userInfo = JSON.parse(uni.getStorageSync('userInfo') || '{}');
  58. const userId = userInfo.id;
  59. const openid = userInfo.openid;
  60. const tenant_id = userInfo.tenant_id;
  61. if (!userId) {
  62. uni.showToast({
  63. title: '用户信息不完整,请重新登录',
  64. icon: 'none'
  65. });
  66. return false;
  67. }
  68. const response = await uni.request({
  69. url: `${apiBaseUrl}/api/system/resume-parsing-tasks/upload_resume/`,
  70. method: 'POST',
  71. data: {
  72. openid: openid,
  73. tenant_id: tenant_id,
  74. user_id: userId,
  75. file: fileUrl,
  76. description: '候选人简历'
  77. },
  78. header: {
  79. 'content-type': 'application/json'
  80. }
  81. });
  82. if (response.statusCode === 200 && response.data.code === 2000) {
  83. return true;
  84. } else {
  85. throw new Error(response.data.msg || '简历解析失败');
  86. }
  87. } catch (error) {
  88. console.error('简历解析失败:', error);
  89. uni.showToast({
  90. title: error.message || '简历解析失败',
  91. icon: 'none'
  92. });
  93. return false;
  94. }
  95. }, */
  96. clearFile() {
  97. uni.showModal({
  98. title: '提示',
  99. content: '确定要删除已上传的文件吗?',
  100. success: (res) => {
  101. if (res.confirm) {
  102. this.uploadedFile = null;
  103. uni.showToast({
  104. title: '文件已删除',
  105. icon: 'success'
  106. });
  107. }
  108. }
  109. });
  110. },
  111. formatFileSize(size) {
  112. if (size < 1024) {
  113. return size + 'B';
  114. } else if (size < 1024 * 1024) {
  115. return (size / 1024).toFixed(2) + 'KB';
  116. } else {
  117. return (size / (1024 * 1024)).toFixed(2) + 'MB';
  118. }
  119. },
  120. chooseFile() {
  121. if (this.uploadedFile) {
  122. uni.showToast({
  123. title: '请先删除已上传的文件',
  124. icon: 'none'
  125. });
  126. return;
  127. }
  128. // #ifdef H5
  129. uni.chooseFile({
  130. count: 1,
  131. type: 'file',
  132. extension: this.fileType,
  133. success: (res) => {
  134. if (res.tempFiles[0].size > this.maxSize) {
  135. uni.showToast({
  136. title: '文件大小不能超过30MB',
  137. icon: 'none'
  138. })
  139. return
  140. }
  141. this.uploadFile(res.tempFiles[0])
  142. }
  143. })
  144. // #endif
  145. // #ifdef APP-PLUS || MP
  146. uni.chooseMessageFile({
  147. count: 1,
  148. type: 'file',
  149. extension: this.fileType,
  150. success: (res) => {
  151. if (res.tempFiles[0].size > this.maxSize) {
  152. uni.showToast({
  153. title: '文件大小不能超过30MB',
  154. icon: 'none'
  155. })
  156. return
  157. }
  158. this.uploadFile(res.tempFiles[0])
  159. }
  160. })
  161. // #endif
  162. },
  163. async uploadFile(file) {
  164. try {
  165. uni.showLoading({
  166. title: '上传中...'
  167. });
  168. // 获取用户信息
  169. const userInfo = JSON.parse(uni.getStorageSync('userInfo') || '{}');
  170. console.log(userInfo);
  171. const openid = userInfo.openid || '';
  172. const tenant_id = userInfo.tenant_id || 1;
  173. const userId = userInfo.id;
  174. console.log(openid, tenant_id);
  175. if (!openid || !tenant_id || !userId) {
  176. uni.hideLoading();
  177. uni.showToast({
  178. title: '用户信息不完整,请重新登录',
  179. icon: 'none'
  180. });
  181. return;
  182. }
  183. // 直接上传到简历解析接口
  184. uni.uploadFile({
  185. url: `${apiBaseUrl}/api/system/resume-parsing-tasks/upload_resume/`,
  186. filePath: file.path,
  187. name: 'file',
  188. formData: {
  189. openid: openid,
  190. tenant_id: tenant_id,
  191. user_id: userId,
  192. description: '候选人简历'
  193. },
  194. success: (uploadRes) => {
  195. try {
  196. const res = JSON.parse(uploadRes.data);
  197. console.log(res);
  198. if (res.code === 2000) {
  199. // 保存上传文件信息
  200. this.uploadedFile = {
  201. name: file.name || '未命名文件',
  202. size: file.size,
  203. uploadTime: new Date().toLocaleString(),
  204. url: res.data.url || ''
  205. };
  206. // 显示上传成功提示
  207. uni.showToast({
  208. title: '上传成功',
  209. icon: 'success',
  210. duration: 2000
  211. });
  212. // 显示30秒的loading
  213. let seconds = 30;
  214. uni.showLoading({
  215. title: `正在处理中,请稍候...(${seconds}秒)`,
  216. mask: true
  217. });
  218. const timer = setInterval(() => {
  219. seconds--;
  220. if (seconds > 0) {
  221. uni.showLoading({
  222. title: `正在处理中,请稍候...(${seconds}秒)`,
  223. mask: true
  224. });
  225. } else {
  226. clearInterval(timer);
  227. uni.hideLoading();
  228. uni.showToast({
  229. title: '处理完成',
  230. icon: 'success'
  231. });
  232. }
  233. }, 1000);
  234. } else {
  235. uni.showToast({
  236. title: res.msg || '上传失败',
  237. icon: 'none'
  238. });
  239. }
  240. } catch (error) {
  241. uni.showToast({
  242. title: '上传失败',
  243. icon: 'none'
  244. });
  245. }
  246. },
  247. fail: (err) => {
  248. console.error('上传失败:', err);
  249. uni.showToast({
  250. title: '网络错误,请重试',
  251. icon: 'none'
  252. });
  253. },
  254. complete: () => {
  255. // 注意:这里不隐藏loading,因为成功后会显示30秒的loading
  256. if (!this.uploadedFile) {
  257. uni.hideLoading();
  258. }
  259. }
  260. });
  261. } catch (error) {
  262. uni.hideLoading();
  263. uni.showToast({
  264. title: '系统错误,请重试',
  265. icon: 'none'
  266. });
  267. }
  268. }
  269. }
  270. }
  271. </script>
  272. <style lang="scss">
  273. .upload-container {
  274. padding: 30rpx;
  275. background-color: #f5f6fa;
  276. min-height: 100vh;
  277. .header-notice{
  278. display: flex;
  279. align-items: center;
  280. justify-content: center;
  281. text-align: center;
  282. font-size: 24rpx;
  283. color: #999;
  284. line-height: 1.5;
  285. }
  286. .illustration {
  287. text-align: center;
  288. margin: 40rpx auto;
  289. padding: 20rpx;
  290. border-radius: 12rpx;
  291. image {
  292. width: 650rpx;
  293. height: 550rpx;
  294. }
  295. }
  296. .file-info {
  297. background-color: #ffffff;
  298. border-radius: 12rpx;
  299. padding: 30rpx;
  300. margin: 30rpx auto;
  301. width: 90%;
  302. box-shadow: 0 2rpx 12rpx rgba(0, 0, 0, 0.05);
  303. .file-info-header {
  304. display: flex;
  305. justify-content: space-between;
  306. align-items: center;
  307. margin-bottom: 20rpx;
  308. }
  309. .file-info-title {
  310. font-size: 28rpx;
  311. color: #333;
  312. font-weight: bold;
  313. }
  314. .file-delete {
  315. padding: 10rpx;
  316. .delete-icon {
  317. width: 32rpx;
  318. height: 32rpx;
  319. }
  320. }
  321. .file-info-content {
  322. .file-name {
  323. font-size: 26rpx;
  324. color: #666;
  325. margin-bottom: 10rpx;
  326. word-break: break-all;
  327. }
  328. .file-size {
  329. font-size: 24rpx;
  330. color: #999;
  331. margin-bottom: 10rpx;
  332. }
  333. .file-time {
  334. font-size: 24rpx;
  335. color: #999;
  336. }
  337. }
  338. }
  339. .instructions {
  340. padding: 0 30rpx;
  341. .title {
  342. font-size: 28rpx;
  343. color: #333;
  344. margin-bottom: 30rpx;
  345. }
  346. .instruction-list {
  347. .instruction-item {
  348. font-size: 26rpx;
  349. color: #666;
  350. line-height: 44rpx;
  351. margin-bottom: 20rpx;
  352. }
  353. }
  354. .privacy-notice {
  355. margin-top: 40rpx;
  356. font-size: 24rpx;
  357. color: #999;
  358. line-height: 1.5;
  359. }
  360. }
  361. .upload-btn {
  362. width: 90%;
  363. height: 88rpx;
  364. line-height: 88rpx;
  365. background-color: #2b4acb;
  366. color: #ffffff;
  367. border-radius: 8rpx;
  368. font-size: 32rpx;
  369. margin: 60rpx auto;
  370. text-align: center;
  371. &::after {
  372. border: none;
  373. }
  374. &[disabled] {
  375. background-color: #cccccc;
  376. color: #ffffff;
  377. }
  378. }
  379. }
  380. </style>