From 1637ee5bbcd0a8af04674411b6609391e2e720c0 Mon Sep 17 00:00:00 2001 From: xuxin <15279969124@163.com> Date: Thu, 21 May 2026 15:10:02 +0800 Subject: [PATCH 1/2] =?UTF-8?q?=E6=94=AF=E4=BB=98=E5=AE=9D=E4=B8=8B?= =?UTF-8?q?=E5=8D=95=E5=92=8C=E5=85=B6=E4=BB=96=E7=BB=86=E8=8A=82=E4=BC=98?= =?UTF-8?q?=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- components.d.ts | 3 + src/api/auth.ts | 42 +++ src/api/member.ts | 97 +++++++ src/api/profile.ts | 11 + src/assets/images/nav/nav-share-icon.png | Bin 0 -> 3968 bytes .../styles/components/industry-selector.scss | 2 +- .../components/job-category-selector.scss | 2 +- .../styles/components/member-dialog.scss | 24 +- .../components/profile-welcome-dialog.scss | 130 +++++++++ .../settings-delete-account-dialog.scss | 126 ++++++++- .../components/settings-invite-dialog.scss | 195 ++++++++++++++ src/assets/styles/index.scss | 2 + src/assets/styles/pages/job-detail.scss | 1 + src/assets/styles/pages/jobs.scss | 1 + src/assets/styles/pages/profile.scss | 1 + src/assets/styles/pages/resume.scss | 9 + src/assets/styles/variables.scss | 4 +- src/components/AiChat.vue | 6 +- src/components/MemberDialog.vue | 246 ++++++++++++++---- src/components/ProfileWelcomeDialog.vue | 156 +++++++++++ src/components/ResumeExportDialog.vue | 114 ++++++++ .../SettingsDeleteAccountDialog.vue | 43 ++- src/components/SettingsDialog.vue | 95 ++++--- src/components/SettingsInviteDialog.vue | 107 ++++++++ src/components/SideNav.vue | 14 +- src/components/tools/IndustrySelector.vue | 2 +- src/components/tools/JobCategorySelector.vue | 2 +- src/stores/index.ts | 42 +++ src/views/Jobs.vue | 65 ++++- src/views/Profile.vue | 69 +++-- src/views/Resume.vue | 89 +------ src/views/ResumeDetail.vue | 40 ++- vite.config.ts | 3 +- 33 files changed, 1511 insertions(+), 232 deletions(-) create mode 100644 src/api/member.ts create mode 100644 src/assets/images/nav/nav-share-icon.png create mode 100644 src/assets/styles/components/profile-welcome-dialog.scss create mode 100644 src/assets/styles/components/settings-invite-dialog.scss create mode 100644 src/components/ProfileWelcomeDialog.vue create mode 100644 src/components/ResumeExportDialog.vue create mode 100644 src/components/SettingsInviteDialog.vue 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 0000000000000000000000000000000000000000..eb2569ed557a6e54128e0e64787c41f2001fc54e GIT binary patch literal 3968 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 EKW .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(` +
+ + +
+