反馈联调和职位列表交互bug
This commit is contained in:
Vendored
+1
@@ -53,6 +53,7 @@ declare module 'vue' {
|
|||||||
ResumeIssueFixDrawer: typeof import('./src/components/ResumeIssueFixDrawer.vue')['default']
|
ResumeIssueFixDrawer: typeof import('./src/components/ResumeIssueFixDrawer.vue')['default']
|
||||||
RouterLink: typeof import('vue-router')['RouterLink']
|
RouterLink: typeof import('vue-router')['RouterLink']
|
||||||
RouterView: typeof import('vue-router')['RouterView']
|
RouterView: typeof import('vue-router')['RouterView']
|
||||||
|
SettingsDeleteAccountDialog: typeof import('./src/components/SettingsDeleteAccountDialog.vue')['default']
|
||||||
SettingsDialog: typeof import('./src/components/SettingsDialog.vue')['default']
|
SettingsDialog: typeof import('./src/components/SettingsDialog.vue')['default']
|
||||||
SideNav: typeof import('./src/components/SideNav.vue')['default']
|
SideNav: typeof import('./src/components/SideNav.vue')['default']
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,18 @@
|
|||||||
|
import request from '@/utils/request'
|
||||||
|
import type { ApiResult } from '@/api/auth'
|
||||||
|
|
||||||
|
/** 提交反馈参数 */
|
||||||
|
export interface UserFeedbackParams {
|
||||||
|
/** 反馈类型 */
|
||||||
|
type: number
|
||||||
|
/** 反馈内容 */
|
||||||
|
content: string
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 提交反馈
|
||||||
|
* POST /UserFeedbackParams
|
||||||
|
*/
|
||||||
|
export function userFeedback(data: UserFeedbackParams) {
|
||||||
|
return request.post<any, ApiResult>('/user/feedback', data)
|
||||||
|
}
|
||||||
@@ -231,7 +231,7 @@
|
|||||||
&__plan-btn {
|
&__plan-btn {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
padding: 0.1rem 0;
|
padding: 0.1rem 0;
|
||||||
border-radius: 0.2rem;
|
border-radius: 0.12rem;
|
||||||
font-size: 0.13rem;
|
font-size: 0.13rem;
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
|
|||||||
@@ -0,0 +1,448 @@
|
|||||||
|
@use '../variables' as *;
|
||||||
|
|
||||||
|
// ==================== 注销账号弹窗 ====================
|
||||||
|
.delete-account-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;
|
||||||
|
}
|
||||||
|
|
||||||
|
.delete-account-dialog {
|
||||||
|
position: relative;
|
||||||
|
width: 9.0rem;
|
||||||
|
min-height: 5.6rem;
|
||||||
|
background: $bg-white;
|
||||||
|
border-radius: 0.12rem;
|
||||||
|
padding: 0.32rem 0.4rem;
|
||||||
|
box-shadow: 0 0.04rem 0.2rem rgba(0, 0, 0, 0.15);
|
||||||
|
overflow-y: auto;
|
||||||
|
max-height: 90vh;
|
||||||
|
|
||||||
|
// 关闭按钮
|
||||||
|
&__close {
|
||||||
|
position: absolute;
|
||||||
|
top: 0.16rem;
|
||||||
|
right: 0.2rem;
|
||||||
|
font-size: 0.18rem;
|
||||||
|
color: $text-light;
|
||||||
|
cursor: pointer;
|
||||||
|
z-index: 1;
|
||||||
|
transition: color 0.2s;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
color: $text-dark;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ===== 步骤条 =====
|
||||||
|
&__steps {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
margin-bottom: 0.28rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__step {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 0.06rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__step-num {
|
||||||
|
width: 0.22rem;
|
||||||
|
height: 0.22rem;
|
||||||
|
border-radius: 50%;
|
||||||
|
background: $bg-main;
|
||||||
|
color: $text-light;
|
||||||
|
font-size: 0.12rem;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__step-label {
|
||||||
|
font-size: 0.13rem;
|
||||||
|
color: $text-light;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__step--active &__step-num {
|
||||||
|
background: $accent;
|
||||||
|
color: $bg-white;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__step--active &__step-label {
|
||||||
|
color: $accent;
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__step--done &__step-num {
|
||||||
|
background: $accent;
|
||||||
|
color: $bg-white;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__step--done &__step-label {
|
||||||
|
color: $text-middle;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__step-line {
|
||||||
|
width: 0.6rem;
|
||||||
|
height: 1px;
|
||||||
|
background: $border-color;
|
||||||
|
margin: 0 0.12rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ===== 标题 =====
|
||||||
|
&__title {
|
||||||
|
font-size: 0.22rem;
|
||||||
|
font-weight: 700;
|
||||||
|
color: $text-dark;
|
||||||
|
margin: 0 0 0.08rem 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__subtitle {
|
||||||
|
font-size: 0.13rem;
|
||||||
|
color: $text-middle;
|
||||||
|
margin: 0 0 0.24rem 0;
|
||||||
|
line-height: 1.6;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ===== 主体内容 =====
|
||||||
|
&__body {
|
||||||
|
display: flex;
|
||||||
|
gap: 0.24rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ===== 左侧警告列表 =====
|
||||||
|
&__warnings {
|
||||||
|
flex: 1;
|
||||||
|
min-width: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__warning-item {
|
||||||
|
display: flex;
|
||||||
|
gap: 0.12rem;
|
||||||
|
padding: 0.16rem;
|
||||||
|
border: 1px solid $border-color;
|
||||||
|
border-radius: 0.08rem;
|
||||||
|
margin-bottom: 0.12rem;
|
||||||
|
|
||||||
|
&--danger {
|
||||||
|
border-color: rgba($danger, 0.3);
|
||||||
|
background: rgba($danger, 0.02);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&__warning-icon {
|
||||||
|
flex-shrink: 0;
|
||||||
|
width: 0.2rem;
|
||||||
|
height: 0.2rem;
|
||||||
|
border-radius: 50%;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
font-size: 0.12rem;
|
||||||
|
font-weight: 700;
|
||||||
|
margin-top: 0.02rem;
|
||||||
|
|
||||||
|
&--danger {
|
||||||
|
background: $danger;
|
||||||
|
color: $bg-white;
|
||||||
|
}
|
||||||
|
|
||||||
|
&--orange {
|
||||||
|
color: #F5A623;
|
||||||
|
background: none;
|
||||||
|
font-size: 0.1rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&__warning-content {
|
||||||
|
flex: 1;
|
||||||
|
min-width: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__warning-title {
|
||||||
|
font-size: 0.14rem;
|
||||||
|
font-weight: 700;
|
||||||
|
color: $text-dark;
|
||||||
|
margin-bottom: 0.04rem;
|
||||||
|
|
||||||
|
&--danger {
|
||||||
|
color: $danger;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&__warning-desc {
|
||||||
|
font-size: 0.12rem;
|
||||||
|
color: $text-middle;
|
||||||
|
line-height: 1.6;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ===== 确认勾选 =====
|
||||||
|
&__confirm-check {
|
||||||
|
margin-top: 0.16rem;
|
||||||
|
margin-bottom: 0.2rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__checkbox-label {
|
||||||
|
display: flex;
|
||||||
|
align-items: flex-start;
|
||||||
|
gap: 0.08rem;
|
||||||
|
font-size: 0.12rem;
|
||||||
|
color: $text-dark;
|
||||||
|
cursor: pointer;
|
||||||
|
line-height: 1.6;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__checkbox {
|
||||||
|
margin-top: 0.03rem;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ===== 底部按钮 =====
|
||||||
|
&__actions {
|
||||||
|
display: flex;
|
||||||
|
gap: 0.16rem;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
&__btn {
|
||||||
|
height: 0.44rem;
|
||||||
|
line-height: 0.44rem;
|
||||||
|
border-radius: 0.14rem;
|
||||||
|
font-size: 0.13rem;
|
||||||
|
font-weight: 500;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: all 0.2s;
|
||||||
|
border: none;
|
||||||
|
flex: 1;
|
||||||
|
|
||||||
|
&:disabled {
|
||||||
|
opacity: 0.5;
|
||||||
|
|
||||||
|
cursor: not-allowed;
|
||||||
|
}
|
||||||
|
|
||||||
|
&--outline {
|
||||||
|
background: #DBE4F0;
|
||||||
|
border: 1px solid $border-color;
|
||||||
|
color: $text-dark;
|
||||||
|
|
||||||
|
&:hover:not(:disabled) {
|
||||||
|
border-color: $text-middle;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&--danger {
|
||||||
|
background: #DC2626;
|
||||||
|
color: $bg-white;
|
||||||
|
|
||||||
|
&:hover:not(:disabled) {
|
||||||
|
background: darken(#DC2626, 8%);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&--primary {
|
||||||
|
background: $accent;
|
||||||
|
color: $bg-white;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background: $accent-hover;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ===== 右侧账户状态卡片 =====
|
||||||
|
&__account-status {
|
||||||
|
width: 2.6rem;
|
||||||
|
flex-shrink: 0;
|
||||||
|
background: $bg-white;
|
||||||
|
border: 1px solid $border-color;
|
||||||
|
border-radius: 0.1rem;
|
||||||
|
padding: 0.2rem;
|
||||||
|
align-self: flex-start;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__status-title {
|
||||||
|
font-size: 0.16rem;
|
||||||
|
font-weight: 700;
|
||||||
|
color: $text-dark;
|
||||||
|
margin: 0 0 0.16rem 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__status-row {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
padding: 0.1rem 0;
|
||||||
|
border-bottom: 1px solid $border-color;
|
||||||
|
|
||||||
|
&:last-of-type {
|
||||||
|
border-bottom: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&__status-label {
|
||||||
|
font-size: 0.12rem;
|
||||||
|
color: $text-middle;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__status-value {
|
||||||
|
font-size: 0.13rem;
|
||||||
|
color: $text-dark;
|
||||||
|
font-weight: 600;
|
||||||
|
|
||||||
|
&--danger {
|
||||||
|
color: $danger;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&__status-notice {
|
||||||
|
margin-top: 0.12rem;
|
||||||
|
padding: 0.1rem 0.12rem;
|
||||||
|
background: rgba($danger, 0.05);
|
||||||
|
border: 1px solid rgba($danger, 0.2);
|
||||||
|
border-radius: 0.06rem;
|
||||||
|
font-size: 0.11rem;
|
||||||
|
color: $danger;
|
||||||
|
line-height: 1.6;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ===== 步骤2:安全验证 =====
|
||||||
|
&__verify {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
padding: 0.2rem 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__verify-card {
|
||||||
|
background: $bg-white;
|
||||||
|
border: 1px solid $border-color;
|
||||||
|
border-radius: 0.12rem;
|
||||||
|
padding: 0.32rem 0.4rem;
|
||||||
|
width: 4.6rem;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__verify-card-title {
|
||||||
|
font-size: 0.18rem;
|
||||||
|
font-weight: 700;
|
||||||
|
color: $text-dark;
|
||||||
|
margin: 0 0 0.08rem 0;
|
||||||
|
text-align: left;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__verify-card-desc {
|
||||||
|
font-size: 0.13rem;
|
||||||
|
color: $accent;
|
||||||
|
margin: 0 0 0.24rem 0;
|
||||||
|
text-align: left;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__verify-input-row {
|
||||||
|
display: flex;
|
||||||
|
gap: 0.12rem;
|
||||||
|
margin-bottom: 0.24rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__verify-input {
|
||||||
|
flex: 1;
|
||||||
|
height: 0.4rem;
|
||||||
|
border: 1px solid $border-color;
|
||||||
|
border-radius: 0.2rem;
|
||||||
|
padding: 0 0.16rem;
|
||||||
|
font-size: 0.14rem;
|
||||||
|
outline: none;
|
||||||
|
transition: border-color 0.2s;
|
||||||
|
|
||||||
|
&:focus {
|
||||||
|
border-color: $accent;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&__verify-send {
|
||||||
|
white-space: nowrap;
|
||||||
|
padding: 0 0.2rem;
|
||||||
|
height: 0.4rem;
|
||||||
|
border: 1px solid $border-color;
|
||||||
|
border-radius: 0.2rem;
|
||||||
|
background: $bg-white;
|
||||||
|
color: $text-dark;
|
||||||
|
font-size: 0.13rem;
|
||||||
|
font-weight: 500;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: all 0.2s;
|
||||||
|
|
||||||
|
&:hover:not(:disabled) {
|
||||||
|
border-color: $accent;
|
||||||
|
color: $accent;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:disabled {
|
||||||
|
opacity: 0.5;
|
||||||
|
cursor: not-allowed;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&__verify-next-btn {
|
||||||
|
width: 100%;
|
||||||
|
height: 0.44rem;
|
||||||
|
border: none;
|
||||||
|
border-radius: 0.22rem;
|
||||||
|
background: $accent;
|
||||||
|
color: $bg-white;
|
||||||
|
font-size: 0.15rem;
|
||||||
|
font-weight: 600;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: background 0.2s;
|
||||||
|
margin-bottom: 0.12rem;
|
||||||
|
|
||||||
|
&:hover:not(:disabled) {
|
||||||
|
background: $accent-hover;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:disabled {
|
||||||
|
opacity: 0.5;
|
||||||
|
cursor: not-allowed;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&__verify-tip {
|
||||||
|
font-size: 0.12rem;
|
||||||
|
color: $text-light;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ===== 步骤3:完成 =====
|
||||||
|
&__done {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
padding: 0.6rem 0;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
&__done-icon {
|
||||||
|
width: 0.6rem;
|
||||||
|
height: 0.6rem;
|
||||||
|
border-radius: 50%;
|
||||||
|
background: $accent;
|
||||||
|
color: $bg-white;
|
||||||
|
font-size: 0.28rem;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
margin-bottom: 0.2rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -32,6 +32,7 @@
|
|||||||
@use './components/agent-match-job-add.scss';
|
@use './components/agent-match-job-add.scss';
|
||||||
@use './components/agent-apply-progress.scss';
|
@use './components/agent-apply-progress.scss';
|
||||||
@use './components/ai-thinking-indicator.scss';
|
@use './components/ai-thinking-indicator.scss';
|
||||||
|
@use './components/settings-delete-account-dialog.scss';
|
||||||
|
|
||||||
// 全局样式(优先级最高)
|
// 全局样式(优先级最高)
|
||||||
@use './auto.scss';
|
@use './auto.scss';
|
||||||
|
|||||||
@@ -0,0 +1,211 @@
|
|||||||
|
<template>
|
||||||
|
<!-- 注销账号弹窗 — 多步骤流程 -->
|
||||||
|
<Teleport to="body">
|
||||||
|
<div v-if="modelValue" class="delete-account-overlay" @click="$emit('update:modelValue', false)">
|
||||||
|
<div class="delete-account-dialog" @click.stop>
|
||||||
|
<!-- 右上角关闭按钮 -->
|
||||||
|
<span class="delete-account-dialog__close" @click="$emit('update:modelValue', false)">✕</span>
|
||||||
|
|
||||||
|
<!-- 顶部步骤条 -->
|
||||||
|
<div class="delete-account-dialog__steps">
|
||||||
|
<div class="delete-account-dialog__step" :class="{ 'delete-account-dialog__step--active': currentStep === 1, 'delete-account-dialog__step--done': currentStep > 1 }">
|
||||||
|
<span class="delete-account-dialog__step-num">1</span>
|
||||||
|
<span class="delete-account-dialog__step-label">须知确认</span>
|
||||||
|
</div>
|
||||||
|
<div class="delete-account-dialog__step-line"></div>
|
||||||
|
<div class="delete-account-dialog__step" :class="{ 'delete-account-dialog__step--active': currentStep === 2, 'delete-account-dialog__step--done': currentStep > 2 }">
|
||||||
|
<span class="delete-account-dialog__step-num">2</span>
|
||||||
|
<span class="delete-account-dialog__step-label">安全验证</span>
|
||||||
|
</div>
|
||||||
|
<div class="delete-account-dialog__step-line"></div>
|
||||||
|
<div class="delete-account-dialog__step" :class="{ 'delete-account-dialog__step--active': currentStep === 3 }">
|
||||||
|
<span class="delete-account-dialog__step-num">3</span>
|
||||||
|
<span class="delete-account-dialog__step-label">完成</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 步骤1:须知确认 -->
|
||||||
|
<template v-if="currentStep === 1">
|
||||||
|
<h2 class="delete-account-dialog__title">注销账号前,请确认以下事项</h2>
|
||||||
|
<p class="delete-account-dialog__subtitle">注销账号为不可撤回操作。请在继续前认真阅读以下内容,确认你已了解注销后的影响。</p>
|
||||||
|
|
||||||
|
<div class="delete-account-dialog__body">
|
||||||
|
<!-- 左侧警告列表 -->
|
||||||
|
<div class="delete-account-dialog__warnings">
|
||||||
|
<!-- 注销后不能撤回 -->
|
||||||
|
<div class="delete-account-dialog__warning-item delete-account-dialog__warning-item--danger">
|
||||||
|
<div class="delete-account-dialog__warning-icon delete-account-dialog__warning-icon--danger">!</div>
|
||||||
|
<div class="delete-account-dialog__warning-content">
|
||||||
|
<div class="delete-account-dialog__warning-title delete-account-dialog__warning-title--danger">注销后不能撤回</div>
|
||||||
|
<p class="delete-account-dialog__warning-desc">账号注销完成后,你将无法再使用该账号登录 Offer 派,账号也无法由你自行恢复。</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<!-- 权益清零 -->
|
||||||
|
<div class="delete-account-dialog__warning-item">
|
||||||
|
<div class="delete-account-dialog__warning-icon delete-account-dialog__warning-icon--orange">●</div>
|
||||||
|
<div class="delete-account-dialog__warning-content">
|
||||||
|
<div class="delete-account-dialog__warning-title">权益清零</div>
|
||||||
|
<p class="delete-account-dialog__warning-desc">账号内的 AI 简历优化、岗位匹配、投递追踪、内推码、求职信草稿、面试准备记录等数据与权益将被清空。</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<!-- 会员权益不退款 -->
|
||||||
|
<div class="delete-account-dialog__warning-item">
|
||||||
|
<div class="delete-account-dialog__warning-icon delete-account-dialog__warning-icon--orange">●</div>
|
||||||
|
<div class="delete-account-dialog__warning-content">
|
||||||
|
<div class="delete-account-dialog__warning-title">会员权益不退款</div>
|
||||||
|
<p class="delete-account-dialog__warning-desc">如你的账号仍有会员有效期或已购买权益,注销账号后视为你主动放弃继续使用,已开通会员权益不做退款处理;法律法规另有规定的除外。</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 确认勾选 -->
|
||||||
|
<div class="delete-account-dialog__confirm-check">
|
||||||
|
<label class="delete-account-dialog__checkbox-label">
|
||||||
|
<input type="checkbox" v-model="hasConfirmed" class="delete-account-dialog__checkbox" />
|
||||||
|
<span>我已阅读并确认《账号注销须知》,理解注销后不能撤回、权益清零、会员权益不退款。</span>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 底部按钮 -->
|
||||||
|
<div class="delete-account-dialog__actions">
|
||||||
|
<button class="delete-account-dialog__btn delete-account-dialog__btn--outline" disabled>
|
||||||
|
{{ hasConfirmed ? '已勾选确认注销须知' : '请先勾选确认注销须知' }}
|
||||||
|
</button>
|
||||||
|
<button class="delete-account-dialog__btn delete-account-dialog__btn--danger" :disabled="!hasConfirmed" @click="goToStep2">
|
||||||
|
继续注销
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 右侧账户状态卡片 -->
|
||||||
|
<div class="delete-account-dialog__account-status">
|
||||||
|
<h3 class="delete-account-dialog__status-title">当前账户状态</h3>
|
||||||
|
<div class="delete-account-dialog__status-row">
|
||||||
|
<span class="delete-account-dialog__status-label">当前会员</span>
|
||||||
|
<span class="delete-account-dialog__status-value">{{ memberType }}</span>
|
||||||
|
</div>
|
||||||
|
<div class="delete-account-dialog__status-row">
|
||||||
|
<span class="delete-account-dialog__status-label">有效期至</span>
|
||||||
|
<span class="delete-account-dialog__status-value">{{ expireDate }}</span>
|
||||||
|
</div>
|
||||||
|
<div class="delete-account-dialog__status-row">
|
||||||
|
<span class="delete-account-dialog__status-label">处理规则</span>
|
||||||
|
<span class="delete-account-dialog__status-value delete-account-dialog__status-value--danger">剩余权益将清零</span>
|
||||||
|
</div>
|
||||||
|
<div class="delete-account-dialog__status-notice">
|
||||||
|
注销后剩余会员权益将清零,不做退款处理。
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<!-- 步骤2:安全验证 -->
|
||||||
|
<template v-if="currentStep === 2">
|
||||||
|
<h2 class="delete-account-dialog__title">安全验证</h2>
|
||||||
|
<p class="delete-account-dialog__subtitle">为保护账号安全,请完成身份验证后继续注销流程。</p>
|
||||||
|
<div class="delete-account-dialog__verify">
|
||||||
|
<div class="delete-account-dialog__verify-card">
|
||||||
|
<h3 class="delete-account-dialog__verify-card-title">验证手机号</h3>
|
||||||
|
<p class="delete-account-dialog__verify-card-desc">验证码将发送至 +86 138****8888</p>
|
||||||
|
<div class="delete-account-dialog__verify-input-row">
|
||||||
|
<input v-model="verifyCode" type="text" maxlength="6" placeholder="请输入验证码" class="delete-account-dialog__verify-input" />
|
||||||
|
<button class="delete-account-dialog__verify-send" :disabled="countdown > 0" @click="sendCode">
|
||||||
|
{{ countdown > 0 ? `${countdown}s 后重发` : '获取验证码' }}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
<button class="delete-account-dialog__verify-next-btn" :disabled="verifyCode.length < 4" @click="handleConfirmDelete">下一步</button>
|
||||||
|
<p class="delete-account-dialog__verify-tip">完成验证后进入最终确认。</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<!-- 步骤3:完成 -->
|
||||||
|
<template v-if="currentStep === 3">
|
||||||
|
<div class="delete-account-dialog__done">
|
||||||
|
<div class="delete-account-dialog__done-icon">✓</div>
|
||||||
|
<h2 class="delete-account-dialog__title">账号注销申请已提交</h2>
|
||||||
|
<p class="delete-account-dialog__subtitle">你的账号将在 7 个工作日内完成注销处理。在此期间如需撤回,请联系客服。</p>
|
||||||
|
<button class="delete-account-dialog__btn delete-account-dialog__btn--primary" @click="handleFinish">我知道了</button>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</Teleport>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { ref, watch } from 'vue'
|
||||||
|
import { useRouter } from 'vue-router'
|
||||||
|
import { useStore } from 'vuex'
|
||||||
|
|
||||||
|
/** 组件 Props */
|
||||||
|
const props = defineProps<{ modelValue: boolean }>()
|
||||||
|
|
||||||
|
/** 组件 Emits */
|
||||||
|
const emit = defineEmits<{ (e: 'update:modelValue', value: boolean): void }>()
|
||||||
|
|
||||||
|
const router = useRouter()
|
||||||
|
const store = useStore()
|
||||||
|
|
||||||
|
/** 当前步骤 */
|
||||||
|
const currentStep = ref(1)
|
||||||
|
|
||||||
|
/** 是否已勾选确认 */
|
||||||
|
const hasConfirmed = ref(false)
|
||||||
|
|
||||||
|
/** 验证码 */
|
||||||
|
const verifyCode = ref('')
|
||||||
|
|
||||||
|
/** 倒计时秒数 */
|
||||||
|
const countdown = ref(0)
|
||||||
|
|
||||||
|
/** 倒计时定时器 */
|
||||||
|
let timer: ReturnType<typeof setInterval> | null = null
|
||||||
|
|
||||||
|
/** 会员类型 */
|
||||||
|
const memberType = ref('季度会员')
|
||||||
|
|
||||||
|
/** 到期日期 */
|
||||||
|
const expireDate = ref('2026-xx-xx')
|
||||||
|
|
||||||
|
/** 弹窗打开时重置状态 */
|
||||||
|
watch(() => props.modelValue, (val) => {
|
||||||
|
if (val) {
|
||||||
|
currentStep.value = 1
|
||||||
|
hasConfirmed.value = false
|
||||||
|
verifyCode.value = ''
|
||||||
|
countdown.value = 0
|
||||||
|
}
|
||||||
|
document.body.style.overflow = val ? 'hidden' : ''
|
||||||
|
})
|
||||||
|
|
||||||
|
/** 进入步骤2 */
|
||||||
|
const goToStep2 = () => {
|
||||||
|
currentStep.value = 2
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 发送验证码 */
|
||||||
|
const sendCode = () => {
|
||||||
|
countdown.value = 60
|
||||||
|
timer = setInterval(() => {
|
||||||
|
countdown.value--
|
||||||
|
if (countdown.value <= 0 && timer) {
|
||||||
|
clearInterval(timer)
|
||||||
|
timer = null
|
||||||
|
}
|
||||||
|
}, 1000)
|
||||||
|
ElMessage.success('验证码已发送')
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 确认注销 */
|
||||||
|
const handleConfirmDelete = () => {
|
||||||
|
// TODO: 调用注销接口
|
||||||
|
currentStep.value = 3
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 完成 — 关闭弹窗并退出登录 */
|
||||||
|
const handleFinish = () => {
|
||||||
|
emit('update:modelValue', false)
|
||||||
|
store.commit('SET_AUTHENTICATED', false)
|
||||||
|
router.push('/')
|
||||||
|
}
|
||||||
|
</script>
|
||||||
@@ -227,6 +227,9 @@
|
|||||||
|
|
||||||
<!-- 求职目标设置弹窗 -->
|
<!-- 求职目标设置弹窗 -->
|
||||||
<JobGoalDialog v-model="showGoalDialog" />
|
<JobGoalDialog v-model="showGoalDialog" />
|
||||||
|
|
||||||
|
<!-- 注销账号弹窗 -->
|
||||||
|
<SettingsDeleteAccountDialog v-model="showDeleteAccount" />
|
||||||
</Teleport>
|
</Teleport>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@@ -239,6 +242,7 @@ import JobGoalDialog from './JobGoalDialog.vue'
|
|||||||
import { resolveRegionName } from '@/utils/region'
|
import { resolveRegionName } from '@/utils/region'
|
||||||
import { resolveIndustryName } from '@/utils/industry'
|
import { resolveIndustryName } from '@/utils/industry'
|
||||||
import { resolveJobCategoryName } from '@/utils/jobCategory'
|
import { resolveJobCategoryName } from '@/utils/jobCategory'
|
||||||
|
import SettingsDeleteAccountDialog from './SettingsDeleteAccountDialog.vue'
|
||||||
|
|
||||||
/** 组件 Props — 控制弹窗显示/隐藏 */
|
/** 组件 Props — 控制弹窗显示/隐藏 */
|
||||||
const props = defineProps<{ modelValue: boolean }>()
|
const props = defineProps<{ modelValue: boolean }>()
|
||||||
@@ -277,6 +281,9 @@ const reminders = reactive({
|
|||||||
/** 求职目标弹窗显示状态 */
|
/** 求职目标弹窗显示状态 */
|
||||||
const showGoalDialog = ref(false)
|
const showGoalDialog = ref(false)
|
||||||
|
|
||||||
|
/** 注销账号弹窗显示状态 */
|
||||||
|
const showDeleteAccount = ref(false)
|
||||||
|
|
||||||
/** 岗位名称列表 */
|
/** 岗位名称列表 */
|
||||||
const intentionCategoryNames = computed(() => {
|
const intentionCategoryNames = computed(() => {
|
||||||
const ids = store.state.jobIntention.categoryIds || []
|
const ids = store.state.jobIntention.categoryIds || []
|
||||||
@@ -313,15 +320,9 @@ watch(() => props.modelValue, (val) => {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
/** 注销账号 — 弹出二次确认 */
|
/** 注销账号 — 打开注销账号弹窗 */
|
||||||
const handleDeleteAccount = () => {
|
const handleDeleteAccount = () => {
|
||||||
ElMessageBox.confirm('此操作将永久删除你的账号及所有数据,是否继续?', '注销账号', {
|
showDeleteAccount.value = true
|
||||||
confirmButtonText: '确认注销',
|
|
||||||
cancelButtonText: '取消',
|
|
||||||
type: 'warning',
|
|
||||||
}).then(() => {
|
|
||||||
ElMessage.success('账号已注销')
|
|
||||||
}).catch(() => {})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 管理订阅 */
|
/** 管理订阅 */
|
||||||
|
|||||||
@@ -83,11 +83,11 @@
|
|||||||
<div class="feedback-dialog__label">*你想向我们反馈什么问题?</div>
|
<div class="feedback-dialog__label">*你想向我们反馈什么问题?</div>
|
||||||
<div class="feedback-dialog__options">
|
<div class="feedback-dialog__options">
|
||||||
<div
|
<div
|
||||||
v-for="opt in feedbackOptions"
|
v-for="(opt,index) in feedbackOptions"
|
||||||
:key="opt"
|
:key="index"
|
||||||
class="feedback-dialog__option"
|
class="feedback-dialog__option"
|
||||||
:class="{ 'feedback-dialog__option--active': feedbackType === opt }"
|
:class="{ 'feedback-dialog__option--active': feedbackType === opt }"
|
||||||
@click="feedbackType = opt"
|
@click="feedbackType = opt, feedbackTypeIndex = index+1"
|
||||||
>
|
>
|
||||||
{{ opt }}
|
{{ opt }}
|
||||||
</div>
|
</div>
|
||||||
@@ -123,6 +123,7 @@ import { useStore } from 'vuex'
|
|||||||
import SettingsDialog from '@/components/SettingsDialog.vue'
|
import SettingsDialog from '@/components/SettingsDialog.vue'
|
||||||
import { checkLogin } from '@/api/auth'
|
import { checkLogin } from '@/api/auth'
|
||||||
import { fetchMessageList, fetchUnreadCount, markMessageRead } from '@/api/message'
|
import { fetchMessageList, fetchUnreadCount, markMessageRead } from '@/api/message'
|
||||||
|
import {userFeedback} from '@/api/setting'
|
||||||
import type { MessageDto } from '@/api/message'
|
import type { MessageDto } from '@/api/message'
|
||||||
import navJobsIcon from '@/assets/images/nav/nav-jobs-icon.png'
|
import navJobsIcon from '@/assets/images/nav/nav-jobs-icon.png'
|
||||||
import navResumeIcon from '@/assets/images/nav/nav-resume-icon.png'
|
import navResumeIcon from '@/assets/images/nav/nav-resume-icon.png'
|
||||||
@@ -193,7 +194,9 @@ const showMessageDialog = ref(false)
|
|||||||
const showFeedbackDialog = ref(false)
|
const showFeedbackDialog = ref(false)
|
||||||
const showSettingsDialog = ref(false)
|
const showSettingsDialog = ref(false)
|
||||||
const feedbackType = ref('')
|
const feedbackType = ref('')
|
||||||
|
const feedbackTypeIndex = ref(0)
|
||||||
const feedbackDetail = ref('')
|
const feedbackDetail = ref('')
|
||||||
|
import { ElMessage } from 'element-plus'
|
||||||
|
|
||||||
// ==================== 站内信相关 ====================
|
// ==================== 站内信相关 ====================
|
||||||
/** 未读消息数量 */
|
/** 未读消息数量 */
|
||||||
@@ -328,12 +331,30 @@ watch(showMessageDialog, (val) => {
|
|||||||
|
|
||||||
const feedbackOptions = ['Bug反馈', '功能建议', '使用体验', '订阅及会员权益相关问题', '其它']
|
const feedbackOptions = ['Bug反馈', '功能建议', '使用体验', '订阅及会员权益相关问题', '其它']
|
||||||
|
|
||||||
function handleFeedbackSubmit() {
|
|
||||||
// TODO: 调用反馈提交接口
|
/**
|
||||||
console.log('反馈类型:', feedbackType.value, '详情:', feedbackDetail.value)
|
* 提交反馈
|
||||||
feedbackType.value = ''
|
*/
|
||||||
feedbackDetail.value = ''
|
async function handleFeedbackSubmit() {
|
||||||
showFeedbackDialog.value = false
|
|
||||||
|
console.log('反馈类型:', feedbackType.value,'类型index:', feedbackTypeIndex.value, '详情:', feedbackDetail.value)
|
||||||
|
if(feedbackTypeIndex.value<1||feedbackDetail.value.trim()==''){
|
||||||
|
ElMessage.warning('请选择问题并填写反馈内容')
|
||||||
|
}else{
|
||||||
|
|
||||||
|
//反馈接口
|
||||||
|
await userFeedback({
|
||||||
|
type: feedbackTypeIndex.value,
|
||||||
|
content: feedbackDetail.value
|
||||||
|
})
|
||||||
|
ElMessage.success('提交成功')
|
||||||
|
feedbackType.value = ''
|
||||||
|
feedbackDetail.value = ''
|
||||||
|
feedbackTypeIndex.value = 0
|
||||||
|
showFeedbackDialog.value = false
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
+36
-8
@@ -236,7 +236,7 @@
|
|||||||
<div v-if="!loading && jobList.length === 0" class="jobs-page__empty">暂无数据</div>
|
<div v-if="!loading && jobList.length === 0" class="jobs-page__empty">暂无数据</div>
|
||||||
<!-- 加载更多提示 -->
|
<!-- 加载更多提示 -->
|
||||||
<div v-if="loadingMore" class="jobs-page__loading-more">加载中...</div>
|
<div v-if="loadingMore" class="jobs-page__loading-more">加载中...</div>
|
||||||
<div v-else-if="noMore && jobList.length > 0" class="jobs-page__loading-more">没有更多了</div>
|
<div v-else-if="noMore && jobList.length > 0" class="jobs-page__loading-more">没有更多符合的职位了</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<AiChat :job-id="currentAskJobId" />
|
<AiChat :job-id="currentAskJobId" />
|
||||||
@@ -363,6 +363,9 @@ const applyStatusTabs = computed(() => [
|
|||||||
function switchApplyStatus(status: number) {
|
function switchApplyStatus(status: number) {
|
||||||
applyStatusFilter.value = applyStatusFilter.value === status ? null : status
|
applyStatusFilter.value = applyStatusFilter.value === status ? null : status
|
||||||
pageNum.value = 1
|
pageNum.value = 1
|
||||||
|
hasNoMoreData.value = false
|
||||||
|
jobList.value = []
|
||||||
|
total.value = 0
|
||||||
loadApplyList()
|
loadApplyList()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -579,8 +582,11 @@ const loading = ref(false)
|
|||||||
/** 是否正在加载下一页 */
|
/** 是否正在加载下一页 */
|
||||||
const loadingMore = ref(false)
|
const loadingMore = ref(false)
|
||||||
|
|
||||||
|
/** 接口返回空列表时标记为无更多数据 */
|
||||||
|
const hasNoMoreData = ref(false)
|
||||||
|
|
||||||
/** 是否已加载全部数据 */
|
/** 是否已加载全部数据 */
|
||||||
const noMore = computed(() => jobList.value.length >= total.value && total.value > 0)
|
const noMore = computed(() => hasNoMoreData.value || (jobList.value.length >= total.value && total.value > 0))
|
||||||
|
|
||||||
/** 职位列表数据 */
|
/** 职位列表数据 */
|
||||||
const jobList = ref<JobItem[]>([])
|
const jobList = ref<JobItem[]>([])
|
||||||
@@ -661,8 +667,14 @@ async function loadNextPage() {
|
|||||||
applied: false,
|
applied: false,
|
||||||
showMenu: false,
|
showMenu: false,
|
||||||
}))
|
}))
|
||||||
jobList.value.push(...newItems)
|
// 返回空列表时标记无更多数据,停止滚动加载
|
||||||
total.value = Number(res.data.total)
|
if (newItems.length === 0) {
|
||||||
|
hasNoMoreData.value = true
|
||||||
|
pageNum.value--
|
||||||
|
} else {
|
||||||
|
jobList.value.push(...newItems)
|
||||||
|
total.value = Number(res.data.total)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
// 加载失败时回退页码
|
// 加载失败时回退页码
|
||||||
@@ -693,6 +705,9 @@ function onListScroll() {
|
|||||||
/** 筛选条件变化时重置到第一页并重新加载 */
|
/** 筛选条件变化时重置到第一页并重新加载 */
|
||||||
function reloadFirstPage() {
|
function reloadFirstPage() {
|
||||||
pageNum.value = 1
|
pageNum.value = 1
|
||||||
|
hasNoMoreData.value = false
|
||||||
|
jobList.value = []
|
||||||
|
total.value = 0
|
||||||
// 筛选条件变化时清除缓存
|
// 筛选条件变化时清除缓存
|
||||||
store.commit('SET_JOB_LIST_CACHE', null)
|
store.commit('SET_JOB_LIST_CACHE', null)
|
||||||
loadCurrentTab()
|
loadCurrentTab()
|
||||||
@@ -752,8 +767,14 @@ async function loadFavoriteNextPage() {
|
|||||||
applied: false,
|
applied: false,
|
||||||
showMenu: false,
|
showMenu: false,
|
||||||
}))
|
}))
|
||||||
jobList.value.push(...newItems)
|
// 返回空列表时标记无更多数据,停止滚动加载
|
||||||
total.value = Number(res.data.total)
|
if (newItems.length === 0) {
|
||||||
|
hasNoMoreData.value = true
|
||||||
|
pageNum.value--
|
||||||
|
} else {
|
||||||
|
jobList.value.push(...newItems)
|
||||||
|
total.value = Number(res.data.total)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
pageNum.value--
|
pageNum.value--
|
||||||
@@ -812,8 +833,14 @@ async function loadApplyNextPage() {
|
|||||||
applied: true,
|
applied: true,
|
||||||
showMenu: false,
|
showMenu: false,
|
||||||
}))
|
}))
|
||||||
jobList.value.push(...newItems)
|
// 返回空列表时标记无更多数据,停止滚动加载
|
||||||
total.value = Number(res.data.total)
|
if (newItems.length === 0) {
|
||||||
|
hasNoMoreData.value = true
|
||||||
|
pageNum.value--
|
||||||
|
} else {
|
||||||
|
jobList.value.push(...newItems)
|
||||||
|
total.value = Number(res.data.total)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
pageNum.value--
|
pageNum.value--
|
||||||
@@ -850,6 +877,7 @@ watch(activeTab, (newTab, oldTab) => {
|
|||||||
keyword.value = ''
|
keyword.value = ''
|
||||||
pageNum.value = 1
|
pageNum.value = 1
|
||||||
total.value = 0
|
total.value = 0
|
||||||
|
hasNoMoreData.value = false
|
||||||
loadCurrentTab()
|
loadCurrentTab()
|
||||||
// 切换到收藏 Tab 时刷新统计
|
// 切换到收藏 Tab 时刷新统计
|
||||||
if (newTab === 'collected') {
|
if (newTab === 'collected') {
|
||||||
|
|||||||
Reference in New Issue
Block a user