支付宝下单和其他细节优化
This commit is contained in:
+63
-2
@@ -2,6 +2,18 @@
|
||||
<div class="jobs-page dflex">
|
||||
<SideNav />
|
||||
<div class="jobs-page__content">
|
||||
|
||||
<div v-if="false" class="fs18 p20 bgeee">
|
||||
支付
|
||||
<iframe
|
||||
v-if="paymentFormHtml"
|
||||
:srcdoc="paymentFormHtml"
|
||||
frameborder="0"
|
||||
style="width: 100%; height: 600px;"
|
||||
scrolling="auto"
|
||||
></iframe>
|
||||
</div>
|
||||
|
||||
<!-- 页面标题 + Tab 切换 -->
|
||||
<JobPageHeader v-model:activeTab="activeTab" :favoriteCount="favoriteTotal" :applyCount="applyTotal" />
|
||||
<!-- 收藏统计栏 -->
|
||||
@@ -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 @@
|
||||
|
||||
<!-- 职位问题反馈弹窗 -->
|
||||
<JobFeedbackDialog ref="feedbackDialogRef" v-model="showFeedbackDialog" :job-id="feedbackJobId" />
|
||||
|
||||
<!-- 欢迎上传简历弹窗 -->
|
||||
<ProfileWelcomeDialog v-model="showWelcomeDialog" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -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(`
|
||||
<form name="punchout_form" method="post" action="https://openapi-sandbox.dl.alipaydev.com/gateway.do?charset=UTF-8&method=alipay.trade.page.pay&sign=o8MQ7cEAh4yrf6ri5iT9gXbtR%2BTncX36p9JNZf8nmXxEp1WKO6dF1HDC6DPF8nxJbus6OG9E5UPSoiHgqrEiu3aeOs4LMcYzXXZP0UQzG%2F6pXyZrvlL70G8uwpnV6MgQojzhKheG0vYIJgSPaGsHKjwcp%2BT8yOZg3eEDIgFYEBf2a3c7kANc7%2FuOOGCSD9Ip9zqwX9LQ3b433NPH3AbGQkFSLsdGOTY62I7yI8hEF7EM7nNm%2BYryieqju21yN76JrV4k%2BgIGHn83I5oT3y7zurzdTqHLD4UawpPWdJ5B7p6wnJ4cN4uzO8JgRpNqogYqqKBPcHIlB9o4uax3bcRqhg%3D%3D¬ify_url=http%3A%2F%2F39.108.134.218%3A10106%2Fapi%2F%2Fpublic%2FalipayNotify%2Fpay&version=1.0&app_id=2018062160374941&sign_type=RSA2×tamp=2026-05-20+15%3A29%3A21&alipay_sdk=alipay-sdk-java-4.40.791.ALL&format=json">
|
||||
<input type="hidden" name="biz_content" value='{"out_trade_no":"OP2057000709638877186","product_code":"FAST_INSTANT_TRADE_PAY","qr_pay_mode":"4","qrcode_width":200,"subject":"周会员","total_amount":"0.10"}'>
|
||||
<input type="submit" value="立即支付" style="display:none">
|
||||
</form>
|
||||
<script>document.forms[0].submit();<\/script>
|
||||
`)
|
||||
|
||||
// ==================== 路由相关 ====================
|
||||
|
||||
const router = useRouter()
|
||||
@@ -273,6 +300,9 @@ const store = useStore()
|
||||
/** 登录状态 — 从 store 读取 */
|
||||
const isAuthenticated = computed(() => store.state.isAuthenticated)
|
||||
|
||||
/** 用户昵称 — 从全局 store 读取 */
|
||||
const nickName = computed(() => store.state.userInfo?.nick || '')
|
||||
|
||||
// ==================== 页面状态 ====================
|
||||
|
||||
/** 当前激活的 Tab,从 URL query 参数读取,默认"推荐" */
|
||||
@@ -302,6 +332,9 @@ const feedbackJobId = ref<string | null>(null)
|
||||
/** 当前问助手的岗位 ID(传给 AiChat 组件) */
|
||||
const currentAskJobId = ref<string>('')
|
||||
|
||||
/** 欢迎弹窗的显示状态 */
|
||||
const showWelcomeDialog = ref(false)
|
||||
|
||||
/** 点击"问助手"按钮,传入岗位 ID 给 AiChat */
|
||||
function askAssistant(job: JobItem) {
|
||||
currentAskJobId.value = job.id
|
||||
@@ -516,6 +549,19 @@ onMounted(async () => {
|
||||
|
||||
// 仅登录状态下才加载需要鉴权的数据
|
||||
if (isAuthenticated.value) {
|
||||
// 加载用户个人信息到全局 store
|
||||
store.dispatch('loadUserInfo')
|
||||
|
||||
// 检查个人资料是否存在,不存在则弹出欢迎弹窗
|
||||
try {
|
||||
const profileRes = await fetchProfile()
|
||||
if (profileRes.code === '0' && (!profileRes.data || profileRes.data === null)) {
|
||||
showWelcomeDialog.value = true
|
||||
}
|
||||
} catch {
|
||||
// 接口异常不阻塞页面加载
|
||||
}
|
||||
|
||||
// 加载收藏统计(用于 Tab 标签显示)
|
||||
loadFavoriteCount()
|
||||
|
||||
@@ -861,6 +907,21 @@ watch(
|
||||
{ deep: true },
|
||||
)
|
||||
|
||||
// 监听登录状态变化 — 登录成功后检查个人资料是否存在
|
||||
watch(isAuthenticated, async (newVal, oldVal) => {
|
||||
if (newVal && !oldVal) {
|
||||
// 从未登录变为已登录,检查个人资料
|
||||
try {
|
||||
const profileRes = await fetchProfile()
|
||||
if (profileRes.code === '0' && (!profileRes.data || profileRes.data === null)) {
|
||||
showWelcomeDialog.value = true
|
||||
}
|
||||
} catch {
|
||||
// 接口异常不阻塞
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
// 监听 Tab 切换,重置列表状态并加载对应数据
|
||||
watch(activeTab, (newTab, oldTab) => {
|
||||
if (initializing) return
|
||||
|
||||
+45
-24
@@ -9,6 +9,9 @@
|
||||
:initial-data="editInitialData"
|
||||
@save="handleSaveEdit"
|
||||
/>
|
||||
|
||||
<!-- 欢迎上传简历弹窗 -->
|
||||
<ProfileWelcomeDialog v-model="showWelcomeDialog" />
|
||||
|
||||
<!-- 页面标题 -->
|
||||
<div class="profile-page__header">
|
||||
@@ -51,12 +54,13 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, onMounted } from 'vue'
|
||||
import { ref, onMounted, watch, computed } from 'vue'
|
||||
import { useRouter } from 'vue-router'
|
||||
import { useStore } from 'vuex'
|
||||
import SideNav from '@/components/SideNav.vue'
|
||||
import ProfileEditDrawer from '@/components/ProfileEditDrawer.vue'
|
||||
import ProfilePageContent from '@/components/ProfilePageContent.vue'
|
||||
import ProfileWelcomeDialog from '@/components/ProfileWelcomeDialog.vue'
|
||||
import { saveProfile, fetchProfile, fetchEducation, saveEducation, fetchWork, saveWork, fetchInternship, saveInternship, fetchProject, saveProject, fetchCompetition, saveCompetition } from '@/api/profile'
|
||||
import type { SaveEducationItem, SaveWorkItem, SaveProjectItem, SaveCompetitionItem } from '@/api/profile'
|
||||
|
||||
@@ -68,8 +72,26 @@ onMounted(async () => {
|
||||
if (!store.state.regions.length) {
|
||||
store.dispatch('loadCommonData')
|
||||
}
|
||||
// 请求个人资料主表数据,填充非数组字段
|
||||
await loadProfile()
|
||||
// 请求个人资料主表数据,判断是否需要弹出欢迎弹窗
|
||||
const profileRes = await fetchProfile()
|
||||
if (profileRes.code === '0' && (!profileRes.data || profileRes.data === null)) {
|
||||
// 无个人资料数据,弹出欢迎上传弹窗
|
||||
showWelcomeDialog.value = true
|
||||
return
|
||||
}
|
||||
// 有数据,正常填充
|
||||
if (profileRes.code === '0' && profileRes.data) {
|
||||
const d = profileRes.data
|
||||
profile.value.name = d.name || ''
|
||||
profile.value.phone = d.mobileNumber || ''
|
||||
profile.value.email = d.email || ''
|
||||
profile.value.idNumber = d.idCard || ''
|
||||
profile.value.regionCode = d.regionCode || ''
|
||||
profile.value.wechat = d.wechatNumber || ''
|
||||
profile.value.skills = d.skills || []
|
||||
profile.value.certificates = d.certificates || []
|
||||
profile.value.portfolioUrl = d.portfolioUrl || ''
|
||||
}
|
||||
// 请求教育经历数据
|
||||
await loadEducation()
|
||||
// 请求工作经历数据
|
||||
@@ -82,27 +104,6 @@ onMounted(async () => {
|
||||
await loadCompetition()
|
||||
})
|
||||
|
||||
/** 加载个人资料主表数据 */
|
||||
async function loadProfile() {
|
||||
try {
|
||||
const res = await fetchProfile()
|
||||
if (res.code === '0' && res.data) {
|
||||
const d = res.data
|
||||
profile.value.name = d.name || ''
|
||||
profile.value.phone = d.mobileNumber || ''
|
||||
profile.value.email = d.email || ''
|
||||
profile.value.idNumber = d.idCard || ''
|
||||
profile.value.regionCode = d.regionCode || ''
|
||||
profile.value.wechat = d.wechatNumber || ''
|
||||
profile.value.skills = d.skills || []
|
||||
profile.value.certificates = d.certificates || []
|
||||
profile.value.portfolioUrl = d.portfolioUrl || ''
|
||||
}
|
||||
} catch {
|
||||
console.error('[Profile] 加载个人资料失败')
|
||||
}
|
||||
}
|
||||
|
||||
/** 加载教育经历数据 */
|
||||
async function loadEducation() {
|
||||
try {
|
||||
@@ -213,6 +214,26 @@ async function loadCompetition() {
|
||||
/** 编辑抽屉的显示状态 */
|
||||
const showEditDrawer = ref(false)
|
||||
|
||||
/** 欢迎弹窗的显示状态 */
|
||||
const showWelcomeDialog = ref(false)
|
||||
|
||||
/** 登录状态 */
|
||||
const isAuthenticated = computed(() => store.state.isAuthenticated)
|
||||
|
||||
/** 监听登录状态变化 — 登录成功后检查个人资料是否存在 */
|
||||
watch(isAuthenticated, async (newVal, oldVal) => {
|
||||
if (newVal && !oldVal) {
|
||||
try {
|
||||
const profileRes = await fetchProfile()
|
||||
if (profileRes.code === '0' && (!profileRes.data || profileRes.data === null)) {
|
||||
showWelcomeDialog.value = true
|
||||
}
|
||||
} catch {
|
||||
// 接口异常不阻塞
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
/** 当前编辑的模块名称 */
|
||||
const editModule = ref('info')
|
||||
|
||||
|
||||
+12
-77
@@ -15,7 +15,11 @@
|
||||
|
||||
<!-- 简历表格 -->
|
||||
<div class="resume-page__table-wrap">
|
||||
<table class="resume-page__table">
|
||||
<!-- 空状态提示 -->
|
||||
<div v-if="resumeList.length === 0" class="resume-page__empty">
|
||||
<p>暂无简历,请点击上方按钮上传简历</p>
|
||||
</div>
|
||||
<table v-else class="resume-page__table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="resume-page__th">简历</th>
|
||||
@@ -82,40 +86,21 @@
|
||||
@saved="loadResumeList"
|
||||
/>
|
||||
|
||||
<!-- 导出简历格式选择弹窗 -->
|
||||
<el-dialog
|
||||
<!-- 导出简历弹窗组件 -->
|
||||
<ResumeExportDialog
|
||||
v-model="exportDialogVisible"
|
||||
title="导出简历"
|
||||
width="3.6rem"
|
||||
:close-on-click-modal="false"
|
||||
class="resume-export-dialog"
|
||||
>
|
||||
<!-- 格式选择 -->
|
||||
<el-radio-group v-model="exportFormat" class="resume-export-dialog__radio-group">
|
||||
<el-radio value="pdf">PDF 简历</el-radio>
|
||||
<el-radio value="word">Word 简历</el-radio>
|
||||
</el-radio-group>
|
||||
<template #footer>
|
||||
<el-button @click="exportDialogVisible = false">取消</el-button>
|
||||
<el-button type="primary" :loading="exporting" @click="doExport">下载</el-button>
|
||||
</template>
|
||||
</el-dialog>
|
||||
|
||||
<!-- 隐藏的简历模板,用于导出时渲染DOM -->
|
||||
<div v-if="exportTemplateData" style="position:absolute;left:-9999px;top:0;">
|
||||
<JobResumeTemplate ref="exportTemplateRef" :resume-data="exportTemplateData" />
|
||||
</div>
|
||||
:resume-id="exportResumeId"
|
||||
:resume-name="exportResumeName"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, onMounted, nextTick } from 'vue'
|
||||
import { ref, onMounted } from 'vue'
|
||||
import { useRouter } from 'vue-router'
|
||||
import SideNav from '@/components/SideNav.vue'
|
||||
import ResumeEditNameDialog from '@/components/ResumeEditNameDialog.vue'
|
||||
import JobResumeTemplate from '@/components/JobResumeTemplate.vue'
|
||||
import type { ResumeTemplateData } from '@/components/JobResumeTemplate.vue'
|
||||
import { exportResumePdf, exportResumeWord, loadResumeTemplateData } from '@/utils/resumeExport'
|
||||
import ResumeExportDialog from '@/components/ResumeExportDialog.vue'
|
||||
import { uploadResume } from '@/utils/aiRequest'
|
||||
import {
|
||||
fetchResumeList, deleteResume, type ResumeListItem,
|
||||
@@ -228,60 +213,10 @@ const editTargetPosition = ref('')
|
||||
|
||||
/** 导出弹窗是否可见 */
|
||||
const exportDialogVisible = ref(false)
|
||||
/** 导出格式:pdf 或 word */
|
||||
const exportFormat = ref<'pdf' | 'word'>('pdf')
|
||||
/** 导出中状态 */
|
||||
const exporting = ref(false)
|
||||
/** 当前导出的简历 ID */
|
||||
const exportResumeId = ref('')
|
||||
/** 当前导出的简历名称(用于文件名) */
|
||||
const exportResumeName = ref('')
|
||||
/** 导出用的简历模板数据 */
|
||||
const exportTemplateData = ref<ResumeTemplateData | null>(null)
|
||||
/** 导出用的简历模板组件引用 */
|
||||
const exportTemplateRef = ref<InstanceType<typeof JobResumeTemplate> | null>(null)
|
||||
|
||||
/** 执行导出下载 */
|
||||
async function doExport() {
|
||||
exporting.value = true
|
||||
try {
|
||||
// 1. 加载简历完整数据
|
||||
const data = await loadResumeTemplateData(exportResumeId.value)
|
||||
if (!data) {
|
||||
ElMessage.error('获取简历数据失败')
|
||||
return
|
||||
}
|
||||
|
||||
// 2. 设置模板数据,等待DOM渲染
|
||||
exportTemplateData.value = data
|
||||
await nextTick()
|
||||
|
||||
// 3. 获取渲染后的DOM
|
||||
const element = exportTemplateRef.value?.resumeRef
|
||||
if (!element) {
|
||||
ElMessage.error('简历模板渲染失败')
|
||||
return
|
||||
}
|
||||
|
||||
const fileName = exportResumeName.value || '简历'
|
||||
|
||||
if (exportFormat.value === 'pdf') {
|
||||
await exportResumePdf(element, fileName)
|
||||
} else {
|
||||
exportResumeWord(element, fileName)
|
||||
}
|
||||
|
||||
ElMessage.success('导出成功')
|
||||
exportDialogVisible.value = false
|
||||
} catch (err) {
|
||||
console.error('[导出简历] 失败', err)
|
||||
ElMessage.error('导出失败,请稍后重试')
|
||||
} finally {
|
||||
exporting.value = false
|
||||
// 清理隐藏模板数据
|
||||
exportTemplateData.value = null
|
||||
}
|
||||
}
|
||||
|
||||
/** 弹出菜单操作项 */
|
||||
const popupActions = ['设为默认简历', '编辑名称岗位', '导出简历', '删除']
|
||||
|
||||
@@ -29,6 +29,22 @@
|
||||
@submit="handleFixSubmit"
|
||||
/>
|
||||
|
||||
<!-- 导出简历弹窗组件 -->
|
||||
<ResumeExportDialog
|
||||
v-model="exportDialogVisible"
|
||||
:resume-id="resumeId"
|
||||
:resume-name="resumeMain.resumeName || '简历'"
|
||||
/>
|
||||
|
||||
<!-- 编辑简历名称弹窗 -->
|
||||
<ResumeEditNameDialog
|
||||
v-model="editNameVisible"
|
||||
:resume-id="resumeId"
|
||||
:resume-name="resumeMain.resumeName || ''"
|
||||
:target-position="resumeMain.targetPosition || ''"
|
||||
@saved="loadResumeDetail"
|
||||
/>
|
||||
|
||||
<!-- 顶部标题 -->
|
||||
<h2 class="resume-detail__page-title">我的简历</h2>
|
||||
|
||||
@@ -43,7 +59,6 @@
|
||||
<span class="resume-detail__tab-name">{{ resumeMain.resumeName || '未命名简历' }}</span>
|
||||
</div>
|
||||
<div class="resume-detail__toolbar-right">
|
||||
<button class="resume-detail__tool-btn" @click="handleFeedback">问题反馈</button>
|
||||
<button class="resume-detail__tool-btn" @click="handleEdit">
|
||||
<svg viewBox="0 0 16 16" fill="none" class="resume-detail__tool-icon">
|
||||
<path d="M11.5 2.5l2 2L5 13H3v-2l8.5-8.5z" stroke="currentColor" stroke-width="1.2" stroke-linejoin="round"/>
|
||||
@@ -407,6 +422,8 @@ import SideNav from '@/components/SideNav.vue'
|
||||
import ProfileEditDrawer from '@/components/ProfileEditDrawer.vue'
|
||||
import ResumeAnalysisReportDrawer from '@/components/ResumeAnalysisReportDrawer.vue'
|
||||
import ResumeIssueFixDrawer from '@/components/ResumeIssueFixDrawer.vue'
|
||||
import ResumeExportDialog from '@/components/ResumeExportDialog.vue'
|
||||
import ResumeEditNameDialog from '@/components/ResumeEditNameDialog.vue'
|
||||
import {
|
||||
fetchResumeMain,
|
||||
fetchResumeEducation,
|
||||
@@ -675,14 +692,23 @@ function goBack() {
|
||||
router.push('/resume')
|
||||
}
|
||||
|
||||
/** 问题反馈 */
|
||||
function handleFeedback() { console.log('问题反馈') }
|
||||
|
||||
/** 编辑简历信息 */
|
||||
function handleEdit() { console.log('编辑简历信息') }
|
||||
|
||||
/** 导出简历 */
|
||||
function handleExport() { console.log('导出') }
|
||||
/** 编辑简历名称弹窗显示状态 */
|
||||
const editNameVisible = ref(false)
|
||||
|
||||
/** 编辑简历信息 — 打开编辑名称弹窗 */
|
||||
function handleEdit() {
|
||||
editNameVisible.value = true
|
||||
}
|
||||
|
||||
/** 导出简历弹窗显示状态 */
|
||||
const exportDialogVisible = ref(false)
|
||||
|
||||
/** 导出简历 — 打开导出弹窗 */
|
||||
function handleExport() {
|
||||
exportDialogVisible.value = true
|
||||
}
|
||||
|
||||
/** 删除简历 — 二次确认后调用删除接口,成功后返回简历列表 */
|
||||
async function handleDelete() {
|
||||
|
||||
Reference in New Issue
Block a user