diff --git a/components.d.ts b/components.d.ts index 70ade84..5e9e617 100644 --- a/components.d.ts +++ b/components.d.ts @@ -47,14 +47,17 @@ declare module 'vue' { MemberDialog: typeof import('./src/components/MemberDialog.vue')['default'] ProfileEditDrawer: typeof import('./src/components/ProfileEditDrawer.vue')['default'] ProfilePageContent: typeof import('./src/components/ProfilePageContent.vue')['default'] + ProfileWelcomeDialog: typeof import('./src/components/ProfileWelcomeDialog.vue')['default'] RegionSelector: typeof import('./src/components/tools/RegionSelector.vue')['default'] ResumeAnalysisReportDrawer: typeof import('./src/components/ResumeAnalysisReportDrawer.vue')['default'] ResumeEditNameDialog: typeof import('./src/components/ResumeEditNameDialog.vue')['default'] + ResumeExportDialog: typeof import('./src/components/ResumeExportDialog.vue')['default'] ResumeIssueFixDrawer: typeof import('./src/components/ResumeIssueFixDrawer.vue')['default'] RouterLink: typeof import('vue-router')['RouterLink'] RouterView: typeof import('vue-router')['RouterView'] SettingsDeleteAccountDialog: typeof import('./src/components/SettingsDeleteAccountDialog.vue')['default'] SettingsDialog: typeof import('./src/components/SettingsDialog.vue')['default'] + SettingsInviteDialog: typeof import('./src/components/SettingsInviteDialog.vue')['default'] SideNav: typeof import('./src/components/SideNav.vue')['default'] } export interface GlobalDirectives { diff --git a/src/api/auth.ts b/src/api/auth.ts index dee6ecc..7c563e3 100644 --- a/src/api/auth.ts +++ b/src/api/auth.ts @@ -57,3 +57,45 @@ export function checkLogin() { export function logout() { return request.post('/public/logout') } + +/** + * 注销账号 + * POST /user/manage/cancel + * Cookie 自动携带 Token + */ +export function cancelAccount() { + return request.post('/user/manage/cancel') +} + +/** 用户个人信息 DTO */ +export interface UserInfo { + /** 用户ID */ + id?: number + /** 手机号 */ + mobileNumber?: string + /** 邮箱 */ + email?: string + /** 昵称 */ + nick?: string + /** 真实姓名 */ + realName?: string + /** 头像 */ + picture?: string + /** 生日 */ + birthday?: { seconds?: number; nanos?: number } + /** 性别 1男 2女 */ + sex?: number + /** 邀请码 */ + inviteCode?: string + /** 注册时间 */ + createTime?: { seconds?: number; nanos?: number } +} + +/** + * 查询个人信息 + * GET /user/manage/info + * Cookie 自动携带 Token + */ +export function fetchUserInfo() { + return request.get>('/user/manage/info') +} diff --git a/src/api/member.ts b/src/api/member.ts new file mode 100644 index 0000000..87baaa0 --- /dev/null +++ b/src/api/member.ts @@ -0,0 +1,97 @@ +import request from '@/utils/request' +import type { ApiResult } from '@/api/auth' + +/** 会员商品项类型(后端返回) */ +export interface MemberProduct { + id: number + /** 商品名称 */ + productName: string + /** 标签,如"限时优惠"、"最划算" */ + tag: string + /** 主推标识 0=否 1=是 */ + isFeatured: number + /** 购买按钮文字 */ + buyButtonText: string + /** 实付价格(分) */ + price: number + /** 划线价(分) */ + originalPrice: number + /** 折算月价(分) */ + monthlyPrice: number + /** 有效天数 */ + durationDays: number + /** 排序,越小越靠前 */ + sortOrder: number + /** 状态 0=下架 1=上架 */ + status: number + /** 创建时间 */ + createTime: { seconds: number; nanos: number } + /** 更新时间 */ + updateTime: { seconds: number; nanos: number } + /** 逻辑删除 0=正常 非0=已删除 */ + isDelete: number +} + +/** + * 查询会员商品列表 + * GET /member/product/list + */ +export function fetchMemberProductList() { + return request.get>('/member/product/list') +} + +/** 创建订单请求参数 */ +export interface CreateOrderParams { + /** 商品ID */ + productId: string + /** 支付渠道 1=微信 2=支付宝 */ + payChannel: number +} + +/** 创建订单返回数据 */ +export interface CreateOrderResult { + /** 订单ID,用于轮询状态 */ + orderId: string + /** 支付渠道 */ + payChannel: number + /** 支付数据(支付宝表单HTML / 微信二维码链接) */ + payData: string +} + +/** + * 创建会员订单 + * POST /member/product/createOrder + */ +export function createMemberOrder(data: CreateOrderParams) { + return request.post>('/member/product/createOrder', data) +} + +/** 订单详情返回数据 */ +export interface OrderDetailResult { + /** 订单ID */ + orderId: string + /** 订单编号 */ + orderNo: string + /** 商品名称 */ + productName: string + /** 实付金额(分) */ + payAmount: number + /** 订单状态 0=待支付 1=已支付 2=已退款 3=已关闭 */ + status: number + /** 支付渠道 1=微信 2=支付宝 */ + payChannel: number + /** 支付时间 */ + payTime: { seconds: number; nanos: number } + /** 下单时间 */ + createTime: { seconds: number; nanos: number } +} + +/** + * 查询订单详情 + * GET /member/product/orderDetail + */ +export function fetchOrderDetail(orderId: string) { + return request.get>('/member/product/orderDetail', { + params: { orderId }, + }) +} diff --git a/src/api/profile.ts b/src/api/profile.ts index aa2ee2d..5319f69 100644 --- a/src/api/profile.ts +++ b/src/api/profile.ts @@ -75,6 +75,17 @@ export function fetchProfile() { return request.get>('/user/profile') } +/** + * 根据简历ID同步更新个人资料 + * POST /user/profile/syncFromResume?resumeId=xxx + * Cookie 自动携带 Token + */ +export function syncProfileFromResume(resumeId: string) { + return request.post('/user/profile/syncFromResume', null, { + params: { resumeId }, + }) +} + // ==================== 教育经历相关 ==================== /** 描述段落 */ diff --git a/src/assets/images/nav/nav-share-icon.png b/src/assets/images/nav/nav-share-icon.png new file mode 100644 index 0000000..eb2569e Binary files /dev/null and b/src/assets/images/nav/nav-share-icon.png differ diff --git a/src/assets/styles/components/industry-selector.scss b/src/assets/styles/components/industry-selector.scss index bb25e49..90f569c 100644 --- a/src/assets/styles/components/industry-selector.scss +++ b/src/assets/styles/components/industry-selector.scss @@ -200,7 +200,7 @@ // 双击提示文字 &__hint { font-size: 0.11rem; - color: $text-light; + color: #666; margin-bottom: 0.06rem; line-height: 1; } diff --git a/src/assets/styles/components/job-category-selector.scss b/src/assets/styles/components/job-category-selector.scss index 3b453ce..0ec37a2 100644 --- a/src/assets/styles/components/job-category-selector.scss +++ b/src/assets/styles/components/job-category-selector.scss @@ -206,7 +206,7 @@ // 双击提示文字 &__hint { font-size: 0.11rem; - color: $text-light; + color: #666; margin-bottom: 0.06rem; line-height: 1; } diff --git a/src/assets/styles/components/member-dialog.scss b/src/assets/styles/components/member-dialog.scss index b34681d..9a0bae8 100644 --- a/src/assets/styles/components/member-dialog.scss +++ b/src/assets/styles/components/member-dialog.scss @@ -10,18 +10,20 @@ display: flex; align-items: center; justify-content: center; + } .member-dialog { background: $bg-white; border-radius: 0.2rem; - width: 8.4rem; + width: 8.8rem; height: 80vh; position: relative; box-shadow: 0 0.1rem 0.4rem rgba(0, 0, 0, 0.15); display: flex; flex-direction: column; overflow: hidden; + font-size: 0.14rem; // 关闭按钮 &__close { @@ -520,12 +522,11 @@ &__order-body { flex: 1; display: flex; - overflow: hidden; + overflow-y: auto; } &__order-left { flex: 1; - overflow-y: auto; padding: 0.3rem; } @@ -533,7 +534,6 @@ width: 2.8rem; padding: 0.3rem 0.24rem; background: $bg-main; - overflow-y: auto; flex-shrink: 0; } @@ -1116,7 +1116,9 @@ padding: 0.3rem 0.4rem; text-align: center; position: relative; - min-width: 3.2rem; + width: 5rem; + max-height: 80vh; + overflow-y: auto; box-shadow: 0 0.1rem 0.4rem rgba(0, 0, 0, 0.15); } @@ -1163,21 +1165,27 @@ } &__qrcode-image { - width: 1.8rem; - height: 1.8rem; + width: 2.1rem; + height: 2.1rem; margin: 0 auto 0.16rem; display: flex; align-items: center; justify-content: center; } + &__payment-iframe { + width: 100%; + height: 100%; + border: none; + border-radius: 0.08rem; + } + &__qrcode-placeholder { width: 100%; height: 100%; background: $bg-main; border: 1px dashed $border-color; border-radius: 0.08rem; - // TODO: 替换为真实二维码 } &__qrcode-amount { diff --git a/src/assets/styles/components/profile-welcome-dialog.scss b/src/assets/styles/components/profile-welcome-dialog.scss new file mode 100644 index 0000000..9804677 --- /dev/null +++ b/src/assets/styles/components/profile-welcome-dialog.scss @@ -0,0 +1,130 @@ +@use '../variables' as *; + +// ==================== 欢迎使用弹窗 ==================== +.profile-welcome-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; +} + +.profile-welcome-dialog { + width: 5.6rem; + background: $bg-white; + border-radius: 0.12rem; + padding: 0.36rem 0.4rem; + box-shadow: 0 0.04rem 0.2rem rgba(0, 0, 0, 0.15); + + // 标题 + &__title { + font-size: 0.22rem; + font-weight: 700; + color: $text-dark; + margin: 0 0 0.24rem 0; + } + + // 表单区域 + &__form { + margin-bottom: 0.2rem; + } + + // 标签 + &__label { + font-size: 0.14rem; + font-weight: 600; + color: $text-dark; + margin-bottom: 0.1rem; + } + + // 上传区域 + &__upload-area { + background: $bg-main; + border-radius: 0.08rem; + padding: 0.32rem 0.2rem; + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + cursor: pointer; + transition: background 0.2s; + margin-bottom: 0.14rem; + + &:hover { + background: darken(#F3F4F5, 3%); + } + } + + // 上传图标 + &__upload-icon { + width: 0.44rem; + height: 0.44rem; + background: $text-dark; + border-radius: 0.06rem; + display: flex; + align-items: center; + justify-content: center; + color: $bg-white; + margin-bottom: 0.1rem; + + &--done { + background: $accent; + font-size: 0.2rem; + font-weight: 700; + } + } + + // 上传提示文字 + &__upload-tip { + font-size: 0.12rem; + color: $text-middle; + margin: 0; + text-align: center; + } + + // 隐私说明 + &__privacy { + font-size: 0.11rem; + color: $text-middle; + line-height: 1.7; + margin: 0; + } + + // 开始匹配按钮 + &__start-btn { + width: 100%; + height: 0.46rem; + border: none; + border-radius: 0.06rem; + background: $text-dark; + color: $bg-white; + font-size: 0.15rem; + font-weight: 600; + cursor: pointer; + transition: opacity 0.2s; + + &:hover:not(:disabled) { + opacity: 0.85; + } + + &:disabled { + opacity: 0.4; + cursor: not-allowed; + } + } +} + +// 全屏加载遮罩 — z-index 需高于弹窗 overlay(2200) +.profile-welcome-loading { + z-index: 2300 !important; +} + +// 上传中弹窗层级降低,让 loading 遮罩盖在上面 +.profile-welcome-overlay--behind { + z-index: 2000 !important; +} diff --git a/src/assets/styles/components/settings-delete-account-dialog.scss b/src/assets/styles/components/settings-delete-account-dialog.scss index a68f1a5..9b6cfeb 100644 --- a/src/assets/styles/components/settings-delete-account-dialog.scss +++ b/src/assets/styles/components/settings-delete-account-dialog.scss @@ -317,7 +317,82 @@ line-height: 1.6; } - // ===== 步骤2:安全验证 ===== + // ===== 步骤2:二次确认 ===== + &__confirm-card { + display: flex; + flex-direction: column; + align-items: center; + text-align: center; + padding: 0.5rem 0.8rem; + } + + &__confirm-icon { + width: 0.56rem; + height: 0.56rem; + border-radius: 50%; + background: rgba($danger, 0.1); + color: $danger; + font-size: 0.24rem; + font-weight: 700; + display: flex; + align-items: center; + justify-content: center; + margin-bottom: 0.2rem; + } + + &__confirm-title { + font-size: 0.22rem; + font-weight: 700; + color: $text-dark; + margin: 0 0 0.12rem 0; + } + + &__confirm-desc { + font-size: 0.13rem; + color: $text-middle; + line-height: 1.6; + margin: 0 0 0.28rem 0; + max-width: 3.6rem; + } + + &__confirm-actions { + display: flex; + gap: 0.2rem; + width: 100%; + max-width: 4.0rem; + } + + &__confirm-btn { + flex: 1; + height: 0.44rem; + border-radius: 0.06rem; + font-size: 0.15rem; + font-weight: 600; + cursor: pointer; + transition: all 0.2s; + + &--cancel { + background: #EDF1F7; + border: none; + color: $text-dark; + + &:hover { + border-color: $text-middle; + } + } + + &--danger { + background: #DC2626; + border: none; + color: $bg-white; + + &:hover { + background: darken(#DC2626, 8%); + } + } + } + + // ===== 步骤2(备用):安全验证 ===== &__verify { display: flex; align-items: center; @@ -424,25 +499,60 @@ } // ===== 步骤3:完成 ===== - &__done { + &__done-card { display: flex; flex-direction: column; align-items: center; justify-content: center; - padding: 0.6rem 0; + background: $bg-white; + border: 1px solid $border-color; + border-radius: 0.12rem; + padding: 0.6rem 0.8rem; + margin-top: 0.4rem; text-align: center; } &__done-icon { - width: 0.6rem; - height: 0.6rem; + width: 0.56rem; + height: 0.56rem; border-radius: 50%; - background: $accent; - color: $bg-white; - font-size: 0.28rem; + background: rgba($accent, 0.15); + color: $accent; + font-size: 0.26rem; display: flex; align-items: center; justify-content: center; margin-bottom: 0.2rem; } + + &__done-title { + font-size: 0.22rem; + font-weight: 700; + color: $text-dark; + margin: 0 0 0.12rem 0; + } + + &__done-desc { + font-size: 0.13rem; + color: $text-middle; + margin: 0 0 0.28rem 0; + line-height: 1.6; + } + + &__done-btn { + width: 2.6rem; + height: 0.44rem; + border: none; + border-radius: 0.12rem; + background: $accent; + color: $bg-white; + font-size: 0.15rem; + font-weight: 600; + cursor: pointer; + transition: background 0.2s; + + &:hover { + background: $accent-hover; + } + } } diff --git a/src/assets/styles/components/settings-invite-dialog.scss b/src/assets/styles/components/settings-invite-dialog.scss new file mode 100644 index 0000000..c4527fb --- /dev/null +++ b/src/assets/styles/components/settings-invite-dialog.scss @@ -0,0 +1,195 @@ +@use '../variables' as *; + +// ==================== 邀请注册送会员弹窗 ==================== +.settings-invite-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; + font-size: 0.14rem; +} + +.settings-invite-dialog { + position: relative; + width: 4.2rem; + max-height: 85vh; + background: $bg-white; + border-radius: 0.12rem; + padding: 0.32rem 0.28rem; + box-shadow: 0 0.04rem 0.2rem rgba(0, 0, 0, 0.15); + overflow-y: auto; + + // 关闭按钮 + &__close { + position: absolute; + top: 0.14rem; + right: 0.18rem; + font-size: 0.18rem; + color: $text-light; + cursor: pointer; + transition: color 0.2s; + + &:hover { + color: $text-dark; + } + } + + // 标题 + &__title { + font-size: 0.18rem; + font-weight: 700; + color: $text-dark; + text-align: center; + margin: 0 0 0.2rem 0; + } + + // 邀请链接区域 + &__link-box { + background: $bg-main; + border-radius: 0.08rem; + padding: 0.14rem 0.16rem; + margin-bottom: 0.14rem; + } + + &__link-text { + font-size: 0.12rem; + color: $text-dark; + line-height: 1.7; + margin: 0; + word-break: break-all; + } + + // 复制链接按钮 + &__copy-btn { + width: 100%; + height: 0.4rem; + border: none; + border-radius: 0.2rem; + background: $text-dark; + color: $bg-white; + font-size: 0.14rem; + font-weight: 600; + cursor: pointer; + margin-bottom: 0.18rem; + transition: opacity 0.2s; + + &:hover { + opacity: 0.85; + } + } + + // 步骤提示 + &__steps-tip { + font-size: 0.12rem; + color: $text-middle; + text-align: center; + margin: 0 0 0.14rem 0; + } + + // 步骤卡片 + &__step { + border: 1px solid $border-color; + border-radius: 0.08rem; + padding: 0.12rem 0.16rem; + margin-bottom: 0.1rem; + background: #F2F4F7; + } + + &__step-title { + font-size: 0.13rem; + font-weight: 700; + color: $text-dark; + margin-bottom: 0.04rem; + } + + &__step-desc { + font-size: 0.11rem; + color: $text-middle; + margin: 0; + line-height: 1.5; + } + + // 统计数据 + &__stats { + display: flex; + gap: 0.12rem; + margin-top: 0.16rem; + margin-bottom: 0.16rem; + } + + &__stat-item { + flex: 1; + border: 1px solid $border-color; + border-radius: 0.08rem; + padding: 0.12rem; + } + + &__stat-label { + font-size: 0.11rem; + color: $text-middle; + margin-bottom: 0.06rem; + } + + &__stat-value { + font-size: 0.16rem; + font-weight: 700; + color: $text-dark; + } + + // 活动规则 + &__rules { + display: flex; + align-items: center; + justify-content: space-between; + border: 1px solid $border-color; + border-radius: 0.08rem; + padding: 0.12rem 0.16rem; + cursor: pointer; + transition: background 0.2s; + + &:hover { + background: $bg-main; + } + } + + &__rules-label { + font-size: 0.13rem; + font-weight: 600; + color: $text-dark; + } + + &__rules-arrow { + width: 0.14rem; + height: 0.14rem; + color: $text-middle; + transition: transform 0.2s; + + &--open { + transform: rotate(180deg); + } + } + + &__rules-content { + margin-top: 0.1rem; + padding: 0.12rem 0.16rem; + background: $bg-main; + border-radius: 0.06rem; + + p { + font-size: 0.11rem; + color: $text-middle; + line-height: 1.8; + margin: 0 0 0.04rem 0; + + &:last-child { + margin-bottom: 0; + } + } + } +} diff --git a/src/assets/styles/index.scss b/src/assets/styles/index.scss index 5b5fec4..f025951 100644 --- a/src/assets/styles/index.scss +++ b/src/assets/styles/index.scss @@ -33,6 +33,8 @@ @use './components/agent-apply-progress.scss'; @use './components/ai-thinking-indicator.scss'; @use './components/settings-delete-account-dialog.scss'; +@use './components/profile-welcome-dialog.scss'; +@use './components/settings-invite-dialog.scss'; // 全局样式(优先级最高) @use './auto.scss'; diff --git a/src/assets/styles/pages/job-detail.scss b/src/assets/styles/pages/job-detail.scss index f27cb53..57e178a 100644 --- a/src/assets/styles/pages/job-detail.scss +++ b/src/assets/styles/pages/job-detail.scss @@ -13,6 +13,7 @@ background: $bg-main; display: flex; flex-direction: column; + font-size: 0.14rem; // 手动包裹的两层 div,需要撑满剩余高度并允许内部滚动 > .bg-white { diff --git a/src/assets/styles/pages/jobs.scss b/src/assets/styles/pages/jobs.scss index a906841..ee0e348 100644 --- a/src/assets/styles/pages/jobs.scss +++ b/src/assets/styles/pages/jobs.scss @@ -14,6 +14,7 @@ background: $bg-main; display: flex; flex-direction: column; + font-size: 0.14rem; } // 页面标题 diff --git a/src/assets/styles/pages/profile.scss b/src/assets/styles/pages/profile.scss index 8540ffc..37926d1 100644 --- a/src/assets/styles/pages/profile.scss +++ b/src/assets/styles/pages/profile.scss @@ -12,6 +12,7 @@ background: $bg-main; display: flex; flex-direction: column; + font-size: 0.14rem; } // 页面标题区 diff --git a/src/assets/styles/pages/resume.scss b/src/assets/styles/pages/resume.scss index c435a77..b21961b 100644 --- a/src/assets/styles/pages/resume.scss +++ b/src/assets/styles/pages/resume.scss @@ -12,6 +12,7 @@ background: $bg-main; display: flex; flex-direction: column; + font-size: 0.14rem; } &__header { @@ -160,6 +161,14 @@ height: 0.16rem; } + // 空状态提示 + &__empty { + padding: 0.6rem 0.2rem; + text-align: center; + color: $text-light; + font-size: 0.14rem; + } + // 弹出菜单 &__popup { position: absolute; diff --git a/src/assets/styles/variables.scss b/src/assets/styles/variables.scss index 01cfd6a..58ed21a 100644 --- a/src/assets/styles/variables.scss +++ b/src/assets/styles/variables.scss @@ -48,10 +48,10 @@ $btn-dark: #4FC2C9; $btn-dark-hover: #42A8B3; // 次要深色按钮背景(除非特意指定使用否则不用这个按钮色) -$btn-dark: #1A1A2E; +//$btn-dark: #1A1A2E; // 次要深色按钮悬停态(除非特意指定使用否则不用这个按钮色) -$btn-dark-hover: #2E3142; +//$btn-dark-hover: #2E3142; // 渐变色背景 $gradient-bg: linear-gradient(to right, #4FC2C9, #42A8B3); diff --git a/src/components/AiChat.vue b/src/components/AiChat.vue index 55bb18e..1fd20e8 100644 --- a/src/components/AiChat.vue +++ b/src/components/AiChat.vue @@ -20,7 +20,7 @@
-
欢迎回来,李华!
+
欢迎回来,{{nickName}}!
很高兴再次见到你,让我们继续您通往理想工作的旅程吧。
@@ -92,6 +92,10 @@ const props = defineProps<{ const currentRoute = useRoute() const store = useStore() + +/** 用户昵称 — 从全局 store 读取 */ +const nickName = computed(() => store.state.userInfo?.nick || '') + // ==================== 状态 ==================== /** 会员购买弹窗的显示状态 */ diff --git a/src/components/MemberDialog.vue b/src/components/MemberDialog.vue index 4b622a6..29b2df1 100644 --- a/src/components/MemberDialog.vue +++ b/src/components/MemberDialog.vue @@ -108,14 +108,14 @@
‹ 返回会员介绍 - +
-
- 1 +
+ {{ showQrCode ? '✓' : '1' }} 选择套餐
-
-
+
+
2 支付方式
@@ -144,12 +144,15 @@ @click="selectedPlan = plan.key" >
★ 推荐
- -
-
-
+
-
{{ plan.name }}
+
+
{{ plan.name }}
+ +
+
+
+
¥ {{ plan.price }} @@ -157,6 +160,7 @@
{{ plan.orderDesc }}
+
@@ -317,33 +321,36 @@

支付成功,求职加速已开启

你已成功解锁 AI 求职加速权益,现在可以开始优化简历、匹配岗位并准备面试。

- +
- -
+ +
- +
{{ selectedPayment === 'wechat' ? '💬' : '🔷' }} {{ selectedPayment === 'wechat' ? '微信支付' : '支付宝' }}
-

扫码完成支付

-

请使用{{ selectedPayment === 'wechat' ? '微信' : '支付宝' }} App 扫描二维码完成支付,完成后此窗口会自动关闭。

- +

{{ selectedPayment === 'alipay' ? '扫码完成支付' : '扫码完成支付' }}

+

{{ selectedPayment === 'alipay' ? '请在支付宝页面完成支付,完成后点击下方按钮确认。' : '请使用微信 App 扫描二维码完成支付,完成后此窗口会自动关闭。' }}

+
- -
+
¥{{ currentPlan.price }}
- -
@@ -352,20 +359,23 @@ diff --git a/src/components/ProfileWelcomeDialog.vue b/src/components/ProfileWelcomeDialog.vue new file mode 100644 index 0000000..084e197 --- /dev/null +++ b/src/components/ProfileWelcomeDialog.vue @@ -0,0 +1,156 @@ + + + diff --git a/src/components/ResumeExportDialog.vue b/src/components/ResumeExportDialog.vue new file mode 100644 index 0000000..ac398fb --- /dev/null +++ b/src/components/ResumeExportDialog.vue @@ -0,0 +1,114 @@ + + + diff --git a/src/components/SettingsDeleteAccountDialog.vue b/src/components/SettingsDeleteAccountDialog.vue index 358899f..f3c8ff8 100644 --- a/src/components/SettingsDeleteAccountDialog.vue +++ b/src/components/SettingsDeleteAccountDialog.vue @@ -98,8 +98,21 @@ - + + + + @@ -136,6 +149,7 @@ import { ref, watch } from 'vue' import { useRouter } from 'vue-router' import { useStore } from 'vuex' +import { cancelAccount } from '@/api/auth' /** 组件 Props */ const props = defineProps<{ modelValue: boolean }>() @@ -196,10 +210,19 @@ const sendCode = () => { ElMessage.success('验证码已发送') } -/** 确认注销 */ -const handleConfirmDelete = () => { - // TODO: 调用注销接口 - currentStep.value = 3 +/** 确认注销 — 调用后端注销接口 */ +const handleConfirmDelete = async () => { + try { + await cancelAccount() + // 注销成功,清空登录状态 + store.commit('SET_AUTHENTICATED', false) + // 清空浏览器缓存数据(聊天记录、session 等) + localStorage.clear() + sessionStorage.clear() + currentStep.value = 3 + } catch { + ElMessage.error('注销请求失败,请稍后重试') + } } /** 完成 — 关闭弹窗并退出登录 */ diff --git a/src/components/SettingsDialog.vue b/src/components/SettingsDialog.vue index 1b99bd4..5e68ed6 100644 --- a/src/components/SettingsDialog.vue +++ b/src/components/SettingsDialog.vue @@ -48,7 +48,7 @@

账号与安全

-

130****2222

+

{{ userPhone }}

注销我的账号
@@ -80,14 +80,16 @@
-
订阅状态异常?
-

- 如果你已经和完成了付款或更改了订阅但是没有看到最新状态,你可以尝试更新状态或联系我们获取帮助。 -

-
- - -
+ + + + + + + + + +
@@ -127,34 +129,34 @@ -
-
即时岗位提醒
-
-
-
开启即时岗位更新提醒
-
- 抢先申请 —— 在岗位发布后一小时内,即可收到为你量身定制的最新职位提醒 -
-
- -
-
-
-
岗位更新提醒频率
-
-
-
- 会员用户每天可接收无限次岗位更新提醒,免费用户每天最多接收 1 次。 -
-
- - - - - - -
-
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -243,9 +248,10 @@ import { resolveRegionName } from '@/utils/region' import { resolveIndustryName } from '@/utils/industry' import { resolveJobCategoryName } from '@/utils/jobCategory' import SettingsDeleteAccountDialog from './SettingsDeleteAccountDialog.vue' +import SettingsInviteDialog from './SettingsInviteDialog.vue' -/** 组件 Props — 控制弹窗显示/隐藏 */ -const props = defineProps<{ modelValue: boolean }>() +/** 组件 Props — 控制弹窗显示/隐藏,可指定初始 Tab */ +const props = defineProps<{ modelValue: boolean; initialTab?: string }>() /** 组件 Emits — 通知父组件更新 modelValue */ const emit = defineEmits<{ (e: 'update:modelValue', value: boolean): void }>() @@ -258,18 +264,24 @@ const store = useStore() const tabs = [ { key: 'account', label: '账号与安全', icon: '👤' }, { key: 'member', label: '会员', icon: '🏅' }, - { key: 'reminder', label: '岗位更新提醒', icon: '🔔' }, + { key: 'reminder', label: '目标岗位设置', icon: '🔔' }, ] /** 当前选中的 Tab */ const activeTab = ref('account') +/** 用户手机号 — 从全局 store 读取 */ +const userPhone = computed(() => store.state.userInfo?.mobileNumber || '') + /** 退出登录确认弹窗的显示状态 */ const showLogout = ref(false) /** 监听弹窗开关 — 打开时锁定背景页面滚动,关闭时恢复 */ watch(() => props.modelValue, (val) => { document.body.style.overflow = val ? 'hidden' : '' + if (val && props.initialTab) { + activeTab.value = props.initialTab + } }) /** 岗位更新提醒的配置项 */ @@ -284,6 +296,9 @@ const showGoalDialog = ref(false) /** 注销账号弹窗显示状态 */ const showDeleteAccount = ref(false) +/** 邀请注册弹窗显示状态 */ +const showInviteDialog = ref(false) + /** 岗位名称列表 */ const intentionCategoryNames = computed(() => { const ids = store.state.jobIntention.categoryIds || [] diff --git a/src/components/SettingsInviteDialog.vue b/src/components/SettingsInviteDialog.vue new file mode 100644 index 0000000..1083115 --- /dev/null +++ b/src/components/SettingsInviteDialog.vue @@ -0,0 +1,107 @@ + + + diff --git a/src/components/SideNav.vue b/src/components/SideNav.vue index a17b8e9..18565a3 100644 --- a/src/components/SideNav.vue +++ b/src/components/SideNav.vue @@ -112,7 +112,9 @@ - + + + @@ -132,6 +134,7 @@ import navAgentIcon from '@/assets/images/nav/nav-agent-icon.png' import navMessageIcon from '@/assets/images/nav/nav-message-icon.png' import navSettingIcon from '@/assets/images/nav/nav-setting-icon.png' import navFeedbackIcon from '@/assets/images/nav/nav-feedback-icon.png' +import navShareIcon from '@/assets/images/nav/nav-share-icon.png' const route = useRoute() const router = useRouter() @@ -190,13 +193,18 @@ const mainMenus = computed(() => { return [...staticMenus, ...dynamicMenuItems.value] }) +const showShareDialog = ref(false) const showMessageDialog = ref(false) const showFeedbackDialog = ref(false) -const showSettingsDialog = ref(false) +const showSettingsDialog = computed({ + get: () => store.state.showSettings, + set: (val: boolean) => store.commit('SET_SHOW_SETTINGS', val), +}) const feedbackType = ref('') const feedbackTypeIndex = ref(0) const feedbackDetail = ref('') import { ElMessage } from 'element-plus' +import SettingsInviteDialog from "@/components/SettingsInviteDialog.vue"; // ==================== 站内信相关 ==================== /** 未读消息数量 */ @@ -374,6 +382,7 @@ const settingsMenu = computed(() => { }) const footerMenus = computed(() => [ + { iconImg: navShareIcon, label: '分享送会员', action: () => { showShareDialog.value = true } }, { iconImg: navMessageIcon, label: '消息通知', badge: unreadCount.value > 0 ? 'NEW' : '', action: () => { showMessageDialog.value = true } }, { iconImg: settingsMenu.value.iconImg, label: settingsMenu.value.label, action: () => { handleSettingsNav() } }, { iconImg: navFeedbackIcon, label: '反馈', action: () => { showFeedbackDialog.value = true } }, @@ -387,6 +396,7 @@ async function handleSettingsNav() { const res = await checkLogin() if (res.code === '0' && res.data === true) { store.commit('SET_AUTHENTICATED', true) + store.commit('SET_SETTINGS_TAB', 'account') showSettingsDialog.value = true } else { store.commit('SET_AUTHENTICATED', false) diff --git a/src/components/tools/IndustrySelector.vue b/src/components/tools/IndustrySelector.vue index 1b7e4d1..e809982 100644 --- a/src/components/tools/IndustrySelector.vue +++ b/src/components/tools/IndustrySelector.vue @@ -69,7 +69,7 @@ -
双击可选中一级行业分类
+
(双击可选中一级行业分类)
diff --git a/src/components/tools/JobCategorySelector.vue b/src/components/tools/JobCategorySelector.vue index 1e3358d..8c8cffe 100644 --- a/src/components/tools/JobCategorySelector.vue +++ b/src/components/tools/JobCategorySelector.vue @@ -71,7 +71,7 @@
-
双击可选中一级/二级岗位分类
+
(双击可选中一级/二级岗位分类)
diff --git a/src/stores/index.ts b/src/stores/index.ts index ff0db89..a70c263 100644 --- a/src/stores/index.ts +++ b/src/stores/index.ts @@ -7,6 +7,8 @@ import { fetchIndustryTree, fetchJobCategoryTree, fetchRegionTree } from '@/api/ import type { IndustryItem, JobCategoryItem, RegionItem } from '@/api/common' import { fetchJobIntention, saveJobIntention } from '@/api/jobs' import type { JobIntention } from '@/api/jobs' +import { fetchUserInfo } from '@/api/auth' +import type { UserInfo } from '@/api/auth' /** 职位列表页缓存数据(从详情页返回时恢复用) */ export interface JobListCache { @@ -79,6 +81,18 @@ export interface RootState { * 由 loadJobIntention action 从接口加载,saveJobIntention action 保存后更新 */ jobIntention: JobIntention + + /** + * 用户个人信息 — 登录后从 /user/manage/info 接口获取 + * 多个页面可直接从 store 读取 + */ + userInfo: UserInfo | null + + /** + * 设置弹窗 — 控制显示/隐藏及初始 Tab + */ + showSettings: boolean + settingsTab: string } export default createStore({ @@ -100,6 +114,9 @@ export default createStore({ industryIds: [], employmentType: 0, }, + userInfo: null, + showSettings: false, + settingsTab: 'account', }, getters: { getAppName: (state) => state.appName, @@ -151,6 +168,15 @@ export default createStore({ employmentType: data.employmentType ?? 0, } }, + SET_USER_INFO(state, data: UserInfo | null) { + state.userInfo = data + }, + SET_SHOW_SETTINGS(state, show: boolean) { + state.showSettings = show + }, + SET_SETTINGS_TAB(state, tab: string) { + state.settingsTab = tab + }, }, actions: { updateAppName({ commit }, name: string) { @@ -201,6 +227,7 @@ export default createStore({ commit('SET_AUTHENTICATED', false) commit('SET_SHOW_LOGIN', false) commit('SET_LOGIN_REDIRECT', '') + commit('SET_USER_INFO', null) // 清除 Jobs 页面缓存数据 commit('SET_JOB_LIST_CACHE', null) commit('SET_JOB_INTENTION', { @@ -263,6 +290,21 @@ export default createStore({ } }, + /** + * 加载用户个人信息到 store + * 登录状态下调用,多个页面可直接读取 store.state.userInfo + */ + async loadUserInfo({ commit }) { + try { + const res = await fetchUserInfo() + if (res.code === '0' && res.data) { + commit('SET_USER_INFO', res.data) + } + } catch (err) { + console.error('[store] 加载用户信息失败', err) + } + }, + /** * 保存求职意向:已登录时调接口保存并更新 store,未登录时仅更新 store * @param data 求职意向数据 diff --git a/src/views/Jobs.vue b/src/views/Jobs.vue index 7c862ae..42f7e35 100644 --- a/src/views/Jobs.vue +++ b/src/views/Jobs.vue @@ -2,6 +2,18 @@
+ +
+ 支付 + +
+ @@ -43,7 +55,7 @@ :categoryIds="selectedCategoryIds" :maxSelect="3" :level="3" - :allowParentSelect="false" + :allowParentSelect="true" @update:categoryIds="onCategoryChange" /> @@ -52,7 +64,7 @@ :industryIds="selectedIndustryIds" :maxSelect="3" :level="2" - :allowParentSelect="false" + :allowParentSelect="true" @update:industryIds="onIndustryChange" /> @@ -246,6 +258,9 @@ + + +
@@ -261,9 +276,21 @@ import JobFeedbackDialog from '@/components/JobFeedbackDialog.vue' import IndustrySelector from '@/components/tools/IndustrySelector.vue' import JobCategorySelector from '@/components/tools/JobCategorySelector.vue' import RegionSelector from '@/components/tools/RegionSelector.vue' +import ProfileWelcomeDialog from '@/components/ProfileWelcomeDialog.vue' +import { fetchProfile } from '@/api/profile' import { fetchJobList, fetchFavoriteList, toggleJobFavorite, removeJobFavorite, fetchFavoriteCount, fetchApplyList, fetchApplyCount, removeJobFromList } from '@/api/jobs' import type { JobListItem, JobListParams, FavoriteListParams, ApplyListParams, ApplyCountData } from '@/api/jobs' + +// 2. 注意:这里的字符串不需要再手动转义双引号了 +const paymentFormHtml = ref(` +
+ + +
+