From 8b6d424b2fef65a66d4a12a55fd263c7ce71810e Mon Sep 17 00:00:00 2001 From: xuxin <15279969124@163.com> Date: Tue, 26 May 2026 19:20:34 +0800 Subject: [PATCH] =?UTF-8?q?=E5=B8=A6=E6=96=87=E4=BB=B6=E6=8B=96=E5=8A=A8?= =?UTF-8?q?=E4=B8=8A=E4=BC=A0=E7=9A=84=E6=96=B0=E7=89=88=E7=AE=80=E5=8E=86?= =?UTF-8?q?=E4=B8=8A=E4=BC=A0=E5=8A=9F=E8=83=BD=EF=BC=8C=E7=8B=AC=E7=AB=8B?= =?UTF-8?q?=E6=96=87=E4=BB=B6=E6=93=8D=E4=BD=9C=E7=BB=84=E4=BB=B6=EF=BC=8C?= =?UTF-8?q?=E7=AE=80=E5=8E=86=E5=88=97=E8=A1=A8=E9=A1=B5=E5=92=8C=E4=B8=AA?= =?UTF-8?q?=E4=BA=BA=E8=B5=84=E6=96=99=E9=A1=B5=E6=9C=89=E8=B0=83=E7=94=A8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- components.d.ts | 1 + src/api/agent.ts | 4 +- .../components/profile-welcome-dialog.scss | 5 + .../components/resume-upload-dialog.scss | 205 ++++++++++++++++++ src/assets/styles/index.scss | 62 ++++++ src/components/AgentTaskListDropdown.vue | 2 +- src/components/ProfileWelcomeDialog.vue | 125 +++++++---- src/components/ResumeUploadDialog.vue | 150 +++++++++++++ src/views/Profile.vue | 40 +++- src/views/Resume.vue | 75 ++++--- 10 files changed, 582 insertions(+), 87 deletions(-) create mode 100644 src/assets/styles/components/resume-upload-dialog.scss create mode 100644 src/components/ResumeUploadDialog.vue diff --git a/components.d.ts b/components.d.ts index 5e9e617..6560a2a 100644 --- a/components.d.ts +++ b/components.d.ts @@ -53,6 +53,7 @@ declare module 'vue' { ResumeEditNameDialog: typeof import('./src/components/ResumeEditNameDialog.vue')['default'] ResumeExportDialog: typeof import('./src/components/ResumeExportDialog.vue')['default'] ResumeIssueFixDrawer: typeof import('./src/components/ResumeIssueFixDrawer.vue')['default'] + ResumeUploadDialog: typeof import('./src/components/ResumeUploadDialog.vue')['default'] RouterLink: typeof import('vue-router')['RouterLink'] RouterView: typeof import('vue-router')['RouterView'] SettingsDeleteAccountDialog: typeof import('./src/components/SettingsDeleteAccountDialog.vue')['default'] diff --git a/src/api/agent.ts b/src/api/agent.ts index 191b21c..25418e8 100644 --- a/src/api/agent.ts +++ b/src/api/agent.ts @@ -148,9 +148,9 @@ export function applyJob(params: JobApplyParams) { /** * 取消投递 / 从待投递移除 * DELETE /job/apply?jobId=xxx - * @param jobId 岗位 ID + * @param jobId 岗位 ID(字符串,避免大整数精度丢失) */ -export function cancelApplyJob(jobId: number) { +export function cancelApplyJob(jobId: string | number) { return request.delete('/job/apply', { params: { jobId }, }) diff --git a/src/assets/styles/components/profile-welcome-dialog.scss b/src/assets/styles/components/profile-welcome-dialog.scss index 9804677..b1a0dd6 100644 --- a/src/assets/styles/components/profile-welcome-dialog.scss +++ b/src/assets/styles/components/profile-welcome-dialog.scss @@ -58,6 +58,11 @@ &:hover { background: darken(#F3F4F5, 3%); } + + // 拖拽悬停时背景色变化 + &.is-dragover { + background: darken(#F3F4F5, 6%); + } } // 上传图标 diff --git a/src/assets/styles/components/resume-upload-dialog.scss b/src/assets/styles/components/resume-upload-dialog.scss new file mode 100644 index 0000000..eaf15d6 --- /dev/null +++ b/src/assets/styles/components/resume-upload-dialog.scss @@ -0,0 +1,205 @@ +@use '../variables' as *; + +// ==================== 简历上传弹窗 ==================== +.resume-upload-overlay { + position: fixed; + top: 0; + left: 0; + right: 0; + bottom: 0; + background: $overlay-bg; + z-index: 2200; + display: flex; + align-items: center; + justify-content: center; +} + +.resume-upload-dialog { + width: 5.6rem; + background: $bg-white; + border-radius: 0.12rem; + padding: 0.32rem 0.36rem; + box-shadow: 0 0.04rem 0.2rem rgba(0, 0, 0, 0.15); + + // 标题行 + &__header { + display: flex; + align-items: center; + justify-content: space-between; + margin-bottom: 0.08rem; + } + + // 标题 + &__title { + font-size: 0.2rem; + font-weight: 700; + color: $text-dark; + margin: 0; + } + + // 关闭按钮 + &__close { + width: 0.28rem; + height: 0.28rem; + border: none; + background: none; + cursor: pointer; + color: $text-middle; + display: flex; + align-items: center; + justify-content: center; + border-radius: 0.04rem; + transition: background 0.2s; + + &:hover { + background: $bg-main; + } + } + + // 副标题说明 + &__desc { + font-size: 0.13rem; + color: $accent; + margin: 0 0 0.2rem 0; + } + + // 拖拽上传区域 + &__drop-zone { + border: 1px dashed $border-color; + border-radius: 0.1rem; + padding: 0.5rem 0.2rem; + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + cursor: pointer; + transition: background 0.2s, border-color 0.2s; + background: $theme-color; + margin-bottom: 0.28rem; + + &:hover { + border-color: $accent; + } + + // 拖拽悬停状态 — 背景色加深 + &.is-dragover { + background: darken(#F6FCFC, 4%); + border-color: $accent; + } + + // 已选择文件状态 + &.has-file { + padding: 0.3rem 0.2rem; + } + } + + // 上传图标(加号圆圈) + &__drop-icon { + width: 0.52rem; + height: 0.52rem; + border-radius: 50%; + background: $bg-white; + display: flex; + align-items: center; + justify-content: center; + color: $accent; + margin-bottom: 0.14rem; + box-shadow: 0 0.02rem 0.06rem rgba(0, 0, 0, 0.06); + } + + // 主提示文字 + &__drop-text { + font-size: 0.14rem; + font-weight: 600; + color: $text-dark; + margin: 0 0 0.06rem 0; + } + + // 格式提示 + &__drop-hint { + font-size: 0.12rem; + color: $text-middle; + margin: 0; + } + + // 已选文件信息 + &__file-info { + display: flex; + align-items: center; + gap: 0.08rem; + } + + &__file-icon { + font-size: 0.2rem; + } + + &__file-name { + font-size: 0.14rem; + color: $text-dark; + font-weight: 500; + max-width: 3.6rem; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + } + + &__file-remove { + border: none; + background: none; + color: $text-middle; + font-size: 0.14rem; + cursor: pointer; + padding: 0.02rem 0.04rem; + border-radius: 0.03rem; + transition: color 0.2s; + + &:hover { + color: $danger; + } + } + + // 底部按钮区域 + &__footer { + display: flex; + align-items: center; + justify-content: space-between; + } + + // 按钮通用 + &__btn { + height: 0.42rem; + border-radius: 0.21rem; + font-size: 0.14rem; + font-weight: 500; + cursor: pointer; + transition: opacity 0.2s; + border: none; + + // 取消按钮 + &--cancel { + width: 1.4rem; + background: $bg-main; + color: $text-dark; + + &:hover { + opacity: 0.8; + } + } + + // 确认上传按钮(渐变色) + &--confirm { + width: 1.8rem; + background: $gradient-bg; + color: $bg-white; + + &:hover:not(:disabled) { + opacity: 0.9; + } + + &:disabled { + opacity: 0.4; + cursor: not-allowed; + } + } + } +} diff --git a/src/assets/styles/index.scss b/src/assets/styles/index.scss index f025951..b10f06b 100644 --- a/src/assets/styles/index.scss +++ b/src/assets/styles/index.scss @@ -35,6 +35,7 @@ @use './components/settings-delete-account-dialog.scss'; @use './components/profile-welcome-dialog.scss'; @use './components/settings-invite-dialog.scss'; +@use './components/resume-upload-dialog.scss'; // 全局样式(优先级最高) @use './auto.scss'; @@ -67,6 +68,67 @@ } } +// ==================== Element Plus MessageBox rem 适配修正 ==================== +.el-overlay { + // 确保遮罩层 z-index 足够高 + z-index: 2100 !important; +} + +.el-message-box { + width: 420px !important; + padding: 20px !important; + border-radius: 8px !important; + font-size: 14px !important; + + &__header { + padding: 0 0 12px 0 !important; + } + + &__title { + font-size: 16px !important; + line-height: 1.4 !important; + } + + &__headerbtn { + top: 20px !important; + right: 20px !important; + width: 16px !important; + height: 16px !important; + font-size: 16px !important; + } + + &__content { + padding: 12px 0 !important; + font-size: 14px !important; + } + + &__message { + font-size: 14px !important; + line-height: 1.5 !important; + + p { + font-size: 14px !important; + line-height: 1.5 !important; + } + } + + &__status { + font-size: 20px !important; + } + + &__btns { + padding: 8px 0 0 0 !important; + + .el-button { + font-size: 14px !important; + padding: 8px 20px !important; + height: 32px !important; + border-radius: 4px !important; + min-width: 60px !important; + } + } +} + // ==================== Element Plus Loading rem 适配修正 + 品牌色覆盖 ==================== .el-loading-mask { // 全屏 loading 遮罩层 z-index 确保最高 diff --git a/src/components/AgentTaskListDropdown.vue b/src/components/AgentTaskListDropdown.vue index 48bdd89..934faff 100644 --- a/src/components/AgentTaskListDropdown.vue +++ b/src/components/AgentTaskListDropdown.vue @@ -116,7 +116,7 @@ async function loadCompletedList() { /** 移除岗位 — 调用 cancelApplyJob 接口并通知父组件 */ async function removeJob(job: JobListItem) { try { - await cancelApplyJob(Number(job.id)) + await cancelApplyJob(job.id) emit('removed', job.id) ElMessage.success('已移除') } catch { diff --git a/src/components/ProfileWelcomeDialog.vue b/src/components/ProfileWelcomeDialog.vue index 084e197..f510f72 100644 --- a/src/components/ProfileWelcomeDialog.vue +++ b/src/components/ProfileWelcomeDialog.vue @@ -9,7 +9,15 @@
*简历
-
+
@@ -101,13 +107,15 @@ import { useRouter } from 'vue-router' import SideNav from '@/components/SideNav.vue' import ResumeEditNameDialog from '@/components/ResumeEditNameDialog.vue' import ResumeExportDialog from '@/components/ResumeExportDialog.vue' +import ResumeUploadDialog from '@/components/ResumeUploadDialog.vue' import { uploadResume } from '@/utils/aiRequest' import { fetchResumeList, deleteResume, type ResumeListItem, } from '@/api/resume' import { ElMessage, ElMessageBox, ElLoading } from 'element-plus' -// ElLoading.service() 是命令式调用,按需引入插件不会自动加载其样式,需手动引入 +// 命令式调用的组件,按需引入插件不会自动加载其样式,需手动引入 import 'element-plus/es/components/loading/style/css' +import 'element-plus/es/components/message-box/style/css' const router = useRouter() @@ -218,6 +226,11 @@ const exportResumeId = ref('') /** 当前导出的简历名称(用于文件名) */ const exportResumeName = ref('') +// ==================== 上传简历弹窗状态 ==================== + +/** 上传弹窗是否可见 */ +const uploadDialogVisible = ref(false) + /** 弹出菜单操作项 */ const popupActions = ['设为默认简历', '编辑名称岗位', '导出简历', '删除'] @@ -276,46 +289,38 @@ async function handleAction(action: string, id: string) { } } -/** 上传简历 — 弹出文件选择,选择后调用 AI 接口上传 */ +/** 上传简历 — 打开上传弹窗 */ function handleUpload() { - const input = document.createElement('input') - input.type = 'file' - // 限定 pdf 和 word 格式 - input.accept = '.pdf,.doc,.docx,application/pdf,application/msword,application/vnd.openxmlformats-officedocument.wordprocessingml.document' + uploadDialogVisible.value = true +} - input.onchange = async () => { - const file = input.files?.[0] - if (!file) return +/** 上传弹窗确认回调 — 接收 File 对象,调用接口上传 */ +async function handleUploadConfirm(file: File) { + // 全屏加载提示,AI 接口响应较慢 + const loading = ElLoading.service({ + lock: true, + text: '简历解析中,请耐心等待…', + background: 'rgba(0, 0, 0, 0.5)', + customClass: 'resume-upload-loading', + }) - // 全屏加载提示,AI 接口响应较慢 - const loading = ElLoading.service({ - lock: true, - text: '简历解析中,请耐心等待…', - background: 'rgba(0, 0, 0, 0.5)', - customClass: 'resume-upload-loading', - }) - - try { - const res = await uploadResume(file) - if (res.code === 0) { - // 上传成功,刷新列表 - loadResumeList() - // 等待让后端异步处理数据,再关闭加载动画并跳转详情页 - await new Promise(resolve => setTimeout(resolve, 2000)) - loading.close() - // resumeId 已经是字符串(transformResponse 处理过),直接使用 - goDetail(res.data.resumeId) - } else { - loading.close() - ElMessage.error(res.msg || '上传失败') - } - } catch { + try { + const res = await uploadResume(file) + if (res.code === 0) { + // 上传成功,刷新列表 + loadResumeList() + // 等待让后端异步处理数据,再关闭加载动画并跳转详情页 + await new Promise(resolve => setTimeout(resolve, 2000)) loading.close() - ElMessage.error('上传失败,请稍后重试') + goDetail(res.data.resumeId) + } else { + loading.close() + ElMessage.error(res.msg || '上传失败') } + } catch { + loading.close() + ElMessage.error('上传失败,请稍后重试') } - - input.click() } /** 跳转到简历详情页 */