入职样式优化
This commit is contained in:
@@ -9,6 +9,7 @@ lerna-debug.log*
|
||||
|
||||
node_modules
|
||||
dist
|
||||
dist-*
|
||||
dist-ssr
|
||||
stats.html
|
||||
*.local
|
||||
|
||||
+5
-1
@@ -5,9 +5,13 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { reactive } from "vue";
|
||||
import { reactive, onMounted } from "vue";
|
||||
import { ElConfigProvider } from "element-plus";
|
||||
import zhCn from "element-plus/es/locale/lang/zh-cn";
|
||||
|
||||
const buttonConfig = reactive({ autoInsertSpace: false });
|
||||
|
||||
onMounted(() => {
|
||||
document.documentElement.classList.add("is-public-app");
|
||||
});
|
||||
</script>
|
||||
|
||||
@@ -24,7 +24,8 @@ export const onboardingPublicApi = {
|
||||
},
|
||||
|
||||
saveEmployeeInfo(params: Record<string, unknown>) {
|
||||
return publicPost<never>("save-employee-info", params);
|
||||
// “下一步”需要将业务校验结果作为阻断弹窗展示,由页面统一提示一次。
|
||||
return publicPost<never>("save-employee-info", params, { notifyError: false });
|
||||
},
|
||||
|
||||
confirmContract(params: { token: string; taskContractId: number }) {
|
||||
|
||||
@@ -18,6 +18,8 @@ import "element-plus/theme-chalk/dark/css-vars.css";
|
||||
import "@/styles/element-dark.scss";
|
||||
// custom element css
|
||||
import "@/styles/element.scss";
|
||||
// custom mobile feedback (Message / MessageBox / Dialog on mobile screens)
|
||||
import "@/styles/mobile-feedback.scss";
|
||||
// svg icons
|
||||
import "virtual:svg-icons-register";
|
||||
// element plus
|
||||
|
||||
@@ -27,9 +27,14 @@ import errorHandler from "@/utils/errorHandler";
|
||||
import "@/styles/reset.scss";
|
||||
import "@/styles/common.scss";
|
||||
import "element-plus/dist/index.css";
|
||||
import "@/styles/mobile-feedback.scss";
|
||||
import "virtual:uno.css";
|
||||
import "default-passive-events";
|
||||
|
||||
if (typeof document !== "undefined") {
|
||||
document.documentElement.classList.add("is-public-app");
|
||||
}
|
||||
|
||||
const app = createApp(PublicApp);
|
||||
const publicIcons = {
|
||||
Briefcase,
|
||||
|
||||
@@ -0,0 +1,403 @@
|
||||
/**
|
||||
* 移动端专属提示与弹窗交互体系
|
||||
* 1. 在屏幕宽度 <= 768px 时对全局生效(手机端体验极致优化)
|
||||
* 2. 在员工公开入职端 (html.is-public-app) 全面生效(无论手机真机或 PC 浏览器预览入职均享有一致的精致体验)
|
||||
* 3. 严格隔离 PC 端后台管理系统,保证 PC 管理端在桌面大屏下 100% 保持标准桌面交互规范。
|
||||
*/
|
||||
|
||||
@mixin mobile-feedback-rules {
|
||||
/* ==========================================================================
|
||||
1. ElMessage 移动端悬浮胶囊 Toast
|
||||
========================================================================== */
|
||||
.el-message {
|
||||
min-width: unset !important;
|
||||
width: max-content !important;
|
||||
max-width: calc(100vw - 32px) !important;
|
||||
padding: 10px 18px !important;
|
||||
border-radius: 9999px !important;
|
||||
box-shadow: 0 10px 25px -4px rgba(0, 0, 0, 0.12), 0 4px 10px -2px rgba(0, 0, 0, 0.05) !important;
|
||||
backdrop-filter: blur(14px) !important;
|
||||
-webkit-backdrop-filter: blur(14px) !important;
|
||||
display: inline-flex !important;
|
||||
align-items: center !important;
|
||||
justify-content: center !important;
|
||||
box-sizing: border-box !important;
|
||||
left: 50% !important;
|
||||
transform: translateX(-50%) !important;
|
||||
/* 适配刘海屏及微信导航栏安全区 */
|
||||
margin-top: env(safe-area-inset-top, 14px) !important;
|
||||
transition: top 0.3s cubic-bezier(0.34, 1.56, 0.64, 1), opacity 0.25s ease, transform 0.25s ease !important;
|
||||
pointer-events: auto !important;
|
||||
|
||||
.el-message__icon {
|
||||
margin-right: 8px !important;
|
||||
font-size: 18px !important;
|
||||
flex-shrink: 0 !important;
|
||||
}
|
||||
|
||||
.el-message__content {
|
||||
font-size: 14px !important;
|
||||
line-height: 1.45 !important;
|
||||
font-weight: 500 !important;
|
||||
word-break: break-word !important;
|
||||
text-align: center !important;
|
||||
padding: 0 !important;
|
||||
}
|
||||
|
||||
.el-message__closeBtn {
|
||||
margin-left: 10px !important;
|
||||
font-size: 14px !important;
|
||||
flex-shrink: 0 !important;
|
||||
}
|
||||
|
||||
/* 品牌成功绿 (Emerald) */
|
||||
&.el-message--success {
|
||||
background: rgba(240, 253, 244, 0.96) !important;
|
||||
border: 1px solid #86efac !important;
|
||||
|
||||
.el-message__icon {
|
||||
color: #059669 !important;
|
||||
}
|
||||
|
||||
.el-message__content {
|
||||
color: #064e3b !important;
|
||||
font-weight: 600 !important;
|
||||
}
|
||||
|
||||
.el-message__closeBtn {
|
||||
color: #059669 !important;
|
||||
}
|
||||
}
|
||||
|
||||
/* 品牌暖琥珀黄 (Amber) */
|
||||
&.el-message--warning {
|
||||
background: rgba(254, 252, 232, 0.96) !important;
|
||||
border: 1px solid #fde047 !important;
|
||||
|
||||
.el-message__icon {
|
||||
color: #d97706 !important;
|
||||
}
|
||||
|
||||
.el-message__content {
|
||||
color: #78350f !important;
|
||||
font-weight: 600 !important;
|
||||
}
|
||||
|
||||
.el-message__closeBtn {
|
||||
color: #d97706 !important;
|
||||
}
|
||||
}
|
||||
|
||||
/* 柔和玫瑰红 (Rose) */
|
||||
&.el-message--error {
|
||||
background: rgba(255, 241, 242, 0.96) !important;
|
||||
border: 1px solid #fca5a5 !important;
|
||||
|
||||
.el-message__icon {
|
||||
color: #e11d48 !important;
|
||||
}
|
||||
|
||||
.el-message__content {
|
||||
color: #881337 !important;
|
||||
font-weight: 600 !important;
|
||||
}
|
||||
|
||||
.el-message__closeBtn {
|
||||
color: #e11d48 !important;
|
||||
}
|
||||
}
|
||||
|
||||
/* 晨雾优雅蓝灰 (Slate) */
|
||||
&.el-message--info {
|
||||
background: rgba(248, 250, 252, 0.96) !important;
|
||||
border: 1px solid #cbd5e1 !important;
|
||||
|
||||
.el-message__icon {
|
||||
color: #059669 !important;
|
||||
}
|
||||
|
||||
.el-message__content {
|
||||
color: #1e293b !important;
|
||||
font-weight: 600 !important;
|
||||
}
|
||||
|
||||
.el-message__closeBtn {
|
||||
color: #64748b !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* ==========================================================================
|
||||
2. ElMessageBox 移动端原生卡片弹框 (Confirm / Alert)
|
||||
========================================================================== */
|
||||
.el-overlay.is-message-box {
|
||||
background-color: rgba(15, 23, 42, 0.52) !important;
|
||||
backdrop-filter: blur(5px) !important;
|
||||
-webkit-backdrop-filter: blur(5px) !important;
|
||||
|
||||
.el-overlay-message-box {
|
||||
display: flex !important;
|
||||
align-items: center !important;
|
||||
justify-content: center !important;
|
||||
padding: 16px !important;
|
||||
box-sizing: border-box !important;
|
||||
|
||||
/* 清除 PC 端 inline-block + vertical-align 伪类居中 */
|
||||
&::after {
|
||||
display: none !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.el-message-box {
|
||||
display: flex !important;
|
||||
flex-direction: column !important;
|
||||
width: min(calc(100vw - 36px), 336px) !important;
|
||||
max-width: 336px !important;
|
||||
padding: 24px 20px 20px !important;
|
||||
border-radius: 20px !important;
|
||||
border: 1px solid rgba(255, 255, 255, 0.9) !important;
|
||||
background: #ffffff !important;
|
||||
box-shadow: 0 20px 45px -10px rgba(6, 78, 59, 0.18), 0 10px 20px -5px rgba(0, 0, 0, 0.08) !important;
|
||||
text-align: center !important;
|
||||
overflow: hidden !important;
|
||||
box-sizing: border-box !important;
|
||||
animation: mobileBoxPop 0.26s cubic-bezier(0.34, 1.56, 0.64, 1) !important;
|
||||
|
||||
/* 弹框头部 */
|
||||
.el-message-box__header {
|
||||
padding: 0 0 10px !important;
|
||||
position: relative !important;
|
||||
border-bottom: none !important;
|
||||
|
||||
.el-message-box__title {
|
||||
justify-content: center !important;
|
||||
text-align: center !important;
|
||||
|
||||
span {
|
||||
font-size: 18px !important;
|
||||
font-weight: 700 !important;
|
||||
color: #064e3b !important;
|
||||
letter-spacing: 0.3px !important;
|
||||
}
|
||||
}
|
||||
|
||||
/* 隐藏 PC 端右上角极小叉号,避免手机端误触 */
|
||||
.el-message-box__headerbtn {
|
||||
display: none !important;
|
||||
}
|
||||
}
|
||||
|
||||
/* 弹框内容区 */
|
||||
.el-message-box__content {
|
||||
padding: 6px 4px 20px !important;
|
||||
text-align: center !important;
|
||||
}
|
||||
|
||||
.el-message-box__container {
|
||||
display: flex !important;
|
||||
flex-direction: column !important;
|
||||
align-items: center !important;
|
||||
justify-content: center !important;
|
||||
gap: 12px !important;
|
||||
}
|
||||
|
||||
/* 拟物状态大徽标 */
|
||||
.el-message-box__status {
|
||||
position: static !important;
|
||||
transform: none !important;
|
||||
margin: 0 auto 4px !important;
|
||||
font-size: 30px !important;
|
||||
width: 52px !important;
|
||||
height: 52px !important;
|
||||
border-radius: 50% !important;
|
||||
display: flex !important;
|
||||
align-items: center !important;
|
||||
justify-content: center !important;
|
||||
|
||||
&.el-message-box-icon--success {
|
||||
color: #059669 !important;
|
||||
background: #ecfdf5 !important;
|
||||
border: 2px solid #a7f3d0 !important;
|
||||
}
|
||||
|
||||
&.el-message-box-icon--warning {
|
||||
color: #d97706 !important;
|
||||
background: #fffbeb !important;
|
||||
border: 2px solid #fde68a !important;
|
||||
}
|
||||
|
||||
&.el-message-box-icon--error {
|
||||
color: #e11d48 !important;
|
||||
background: #fff1f2 !important;
|
||||
border: 2px solid #fecdd3 !important;
|
||||
}
|
||||
|
||||
&.el-message-box-icon--info {
|
||||
color: #059669 !important;
|
||||
background: #f0fdf7 !important;
|
||||
border: 2px solid #d1fae5 !important;
|
||||
}
|
||||
}
|
||||
|
||||
.el-message-box__message {
|
||||
padding: 0 !important;
|
||||
text-align: center !important;
|
||||
|
||||
p {
|
||||
margin: 0 !important;
|
||||
font-size: 14px !important;
|
||||
line-height: 1.65 !important;
|
||||
color: #475569 !important;
|
||||
word-break: break-word !important;
|
||||
}
|
||||
}
|
||||
|
||||
/* 按钮操作区 (44pt 移动端人机工效大按钮) */
|
||||
.el-message-box__btns {
|
||||
display: flex !important;
|
||||
flex-direction: row !important;
|
||||
gap: 10px !important;
|
||||
padding: 0 !important;
|
||||
justify-content: center !important;
|
||||
|
||||
.el-button {
|
||||
flex: 1 1 0 !important;
|
||||
min-width: 0 !important;
|
||||
margin: 0 !important;
|
||||
height: 44px !important;
|
||||
min-height: 44px !important;
|
||||
border-radius: 12px !important;
|
||||
font-size: 15px !important;
|
||||
font-weight: 600 !important;
|
||||
box-sizing: border-box !important;
|
||||
transition: all 0.2s cubic-bezier(0.4, 0, 0.2, 1) !important;
|
||||
cursor: pointer !important;
|
||||
|
||||
/* 点击微触觉缩放反馈 */
|
||||
&:active {
|
||||
transform: scale(0.97) !important;
|
||||
}
|
||||
}
|
||||
|
||||
/* 次要/取消按钮 ("再检查一下"、"取消") */
|
||||
.el-button:not(.el-button--primary) {
|
||||
background: #f1f5f9 !important;
|
||||
border: 1px solid #e2e8f0 !important;
|
||||
color: #475569 !important;
|
||||
|
||||
&:active {
|
||||
background: #e2e8f0 !important;
|
||||
color: #1e293b !important;
|
||||
}
|
||||
}
|
||||
|
||||
/* 主要/确认按钮 ("确认提交"、"我知道了") */
|
||||
.el-button--primary {
|
||||
background: linear-gradient(135deg, #059669 0%, #047857 100%) !important;
|
||||
border: none !important;
|
||||
color: #ffffff !important;
|
||||
box-shadow: 0 4px 12px rgba(5, 150, 105, 0.3) !important;
|
||||
|
||||
&:active {
|
||||
background: linear-gradient(135deg, #047857 0%, #065f46 100%) !important;
|
||||
box-shadow: 0 2px 6px rgba(5, 150, 105, 0.2) !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* ==========================================================================
|
||||
3. ElAlert 移动端微卡片优化
|
||||
========================================================================== */
|
||||
.el-alert {
|
||||
border-radius: 12px !important;
|
||||
padding: 10px 14px !important;
|
||||
box-sizing: border-box !important;
|
||||
|
||||
.el-alert__title {
|
||||
font-size: 13px !important;
|
||||
line-height: 1.5 !important;
|
||||
}
|
||||
|
||||
.el-alert__description {
|
||||
font-size: 12px !important;
|
||||
line-height: 1.55 !important;
|
||||
margin-top: 4px !important;
|
||||
}
|
||||
|
||||
&.el-alert--info {
|
||||
background-color: #f0fdf4 !important;
|
||||
border: 1px solid #bbf7d0 !important;
|
||||
color: #166534 !important;
|
||||
|
||||
.el-alert__icon {
|
||||
color: #059669 !important;
|
||||
}
|
||||
}
|
||||
|
||||
&.el-alert--success {
|
||||
background-color: #ecfdf5 !important;
|
||||
border: 1px solid #a7f3d0 !important;
|
||||
color: #065f46 !important;
|
||||
|
||||
.el-alert__icon {
|
||||
color: #059669 !important;
|
||||
}
|
||||
}
|
||||
|
||||
&.el-alert--warning {
|
||||
background-color: #fffbeb !important;
|
||||
border: 1px solid #fde68a !important;
|
||||
color: #92400e !important;
|
||||
|
||||
.el-alert__icon {
|
||||
color: #d97706 !important;
|
||||
}
|
||||
}
|
||||
|
||||
&.el-alert--error {
|
||||
background-color: #fff1f2 !important;
|
||||
border: 1px solid #fecdd3 !important;
|
||||
color: #9f1239 !important;
|
||||
|
||||
.el-alert__icon {
|
||||
color: #e11d48 !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* ==========================================================================
|
||||
4. ElDialog 移动端通用轻量圆角抽屉/卡片
|
||||
========================================================================== */
|
||||
.el-dialog {
|
||||
width: min(calc(100vw - 32px), 520px) !important;
|
||||
max-width: calc(100vw - 32px) !important;
|
||||
margin: 20px auto !important;
|
||||
border-radius: 18px !important;
|
||||
overflow: hidden !important;
|
||||
box-shadow: 0 20px 40px -10px rgba(0, 0, 0, 0.15) !important;
|
||||
}
|
||||
}
|
||||
|
||||
/* 屏幕宽度 <= 768px(手机、垂直平板)生效 */
|
||||
@media screen and (max-width: 768px) {
|
||||
@include mobile-feedback-rules;
|
||||
}
|
||||
|
||||
/* 员工入职前端(无论手机真机或 PC 浏览器预览均享有该高级样式) */
|
||||
html.is-public-app {
|
||||
@include mobile-feedback-rules;
|
||||
}
|
||||
|
||||
/* 移动端弹窗弹出平滑缩放进入动画 */
|
||||
@keyframes mobileBoxPop {
|
||||
0% {
|
||||
opacity: 0;
|
||||
transform: scale(0.92);
|
||||
}
|
||||
100% {
|
||||
opacity: 1;
|
||||
transform: scale(1);
|
||||
}
|
||||
}
|
||||
@@ -1,28 +1,36 @@
|
||||
<template>
|
||||
<main class="fill-page" v-loading="loading">
|
||||
<!-- 顶部品牌横幅 -->
|
||||
<header class="brand-header">
|
||||
<div class="brand-content">
|
||||
<div class="brand-badge">
|
||||
<el-icon>
|
||||
<OfficeBuilding />
|
||||
</el-icon>
|
||||
<span>智能入职服务</span>
|
||||
<main class="fill-page" :class="{ 'is-error-state': Boolean(error) }" v-loading="loading">
|
||||
<!-- 异常状态展示(居中悬浮卡片,对齐 public-status 风格) -->
|
||||
<div v-if="error" class="public-error-container">
|
||||
<section class="error-status-card">
|
||||
<div class="error-status-mark">!</div>
|
||||
<h1 class="error-status-title">{{ errorTitle }}</h1>
|
||||
<p class="error-status-desc">{{ errorDescription }}</p>
|
||||
<div class="error-status-actions">
|
||||
<el-button type="primary" class="error-retry-btn" @click="load()">重新加载</el-button>
|
||||
</div>
|
||||
<h1 class="brand-title">很高兴与你相遇,欢迎加入!</h1>
|
||||
<p class="brand-subtitle">请完成以下入职流程:填写登记信息、上传证件、阅读并签署相关入职材料,开启你的职场新旅程。</p>
|
||||
</div>
|
||||
</header>
|
||||
</section>
|
||||
</div>
|
||||
|
||||
<!-- 容器卡片 -->
|
||||
<div class="main-container">
|
||||
<el-result v-if="error" icon="error" title="访问受限" :sub-title="error">
|
||||
<template #extra>
|
||||
<el-button type="primary" @click="load()">重新加载</el-button>
|
||||
</template>
|
||||
</el-result>
|
||||
<!-- 正常入职内容 -->
|
||||
<template v-else>
|
||||
<!-- 顶部品牌横幅 -->
|
||||
<header class="brand-header">
|
||||
<div class="brand-content">
|
||||
<div class="brand-badge">
|
||||
<el-icon>
|
||||
<OfficeBuilding />
|
||||
</el-icon>
|
||||
<span>智能入职服务</span>
|
||||
</div>
|
||||
<h1 class="brand-title">很高兴与你相遇,欢迎加入!</h1>
|
||||
<p class="brand-subtitle">请完成以下入职流程:填写登记信息、上传证件、阅读并签署相关入职材料,开启你的职场新旅程。</p>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<template v-else-if="task">
|
||||
<!-- 容器卡片 -->
|
||||
<div class="main-container">
|
||||
<template v-if="task">
|
||||
<!-- 任务状态提示条 -->
|
||||
<div class="status-banner" :class="task.taskStatus">
|
||||
<div class="status-banner-main">
|
||||
@@ -457,8 +465,13 @@
|
||||
</div>
|
||||
|
||||
<div v-if="!extraVariables.length" class="all-covered-card">
|
||||
<el-result icon="success" title="个人信息已完整覆盖"
|
||||
sub-title="您在《入职登记表》中填写的姓名、身份证、电话、银行卡等信息已完整满足本次签署材料所需,无需额外补充!" />
|
||||
<div class="covered-status-mark">
|
||||
<el-icon :size="28"><Check /></el-icon>
|
||||
</div>
|
||||
<h4 class="covered-status-title">个人信息已完整覆盖</h4>
|
||||
<p class="covered-status-desc">
|
||||
您在《入职登记表》中填写的姓名、身份证、电话、银行卡等信息已完整满足本次签署材料所需,无需额外补充!
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<el-form v-else label-position="top" class="extra-form">
|
||||
@@ -664,25 +677,60 @@
|
||||
|
||||
<!-- 底部向导操作栏 -->
|
||||
<div class="wizard-footer">
|
||||
<el-button v-if="currentStep > 0" size="large" @click="currentStep--">上一步</el-button>
|
||||
<el-button v-if="currentStep < 3" type="primary" size="large" @click="nextStep">
|
||||
下一步
|
||||
<el-button v-if="currentStep > 0" size="large" :disabled="savingStep || isAnyMaterialUploading" @click="currentStep--">上一步</el-button>
|
||||
<el-button v-if="currentStep < 3" type="primary" size="large"
|
||||
:loading="savingStep || (currentStep === 2 && isAnyMaterialUploading)"
|
||||
:disabled="savingStep || isAnyMaterialUploading"
|
||||
@click="nextStep">
|
||||
{{ (currentStep === 2 && isAnyMaterialUploading) ? "材料上传中..." : (savingStep ? "保存中..." : "下一步") }}
|
||||
</el-button>
|
||||
<el-button v-if="currentStep === 3 && canEdit" type="success" size="large" icon="Check"
|
||||
:loading="submitting" @click="submitAll">
|
||||
:loading="submitting" :disabled="savingStep || isAnyMaterialUploading" @click="submitAll">
|
||||
确认无误并提交入职资料
|
||||
</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
</template>
|
||||
</main>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { computed, nextTick, onBeforeUnmount, onMounted, reactive, ref, watch } from "vue";
|
||||
import { useRoute } from "vue-router";
|
||||
import { ElMessage, ElMessageBox, FormInstance, FormRules } from "element-plus";
|
||||
import { ElMessage as RawElMessage, ElMessageBox, FormInstance, FormRules } from "element-plus";
|
||||
|
||||
/**
|
||||
* 移动端/公开端单例独占式 Toast 封装:
|
||||
* 1. 弹出新提示前自动 closeAll(),彻底杜绝手机端多条提示同时排队堆叠满屏的问题
|
||||
* 2. 启用 grouping: true 自动合并短时间内同一文案的提示
|
||||
*/
|
||||
const ElMessage = {
|
||||
success(options: any) {
|
||||
RawElMessage.closeAll();
|
||||
const config = typeof options === "string" ? { message: options } : options;
|
||||
return RawElMessage.success({ grouping: true, ...config });
|
||||
},
|
||||
warning(options: any) {
|
||||
RawElMessage.closeAll();
|
||||
const config = typeof options === "string" ? { message: options } : options;
|
||||
return RawElMessage.warning({ grouping: true, ...config });
|
||||
},
|
||||
error(options: any) {
|
||||
RawElMessage.closeAll();
|
||||
const config = typeof options === "string" ? { message: options } : options;
|
||||
return RawElMessage.error({ grouping: true, ...config });
|
||||
},
|
||||
info(options: any) {
|
||||
RawElMessage.closeAll();
|
||||
const config = typeof options === "string" ? { message: options } : options;
|
||||
return RawElMessage.info({ grouping: true, ...config });
|
||||
},
|
||||
closeAll() {
|
||||
RawElMessage.closeAll();
|
||||
}
|
||||
};
|
||||
import {
|
||||
OnboardingPublicTask,
|
||||
onboardingPublicApi
|
||||
@@ -707,11 +755,34 @@ const originalViewportContent = viewportMeta?.content;
|
||||
|
||||
const loading = ref(false);
|
||||
const submitting = ref(false);
|
||||
const savingStep = ref(false);
|
||||
const error = ref("");
|
||||
const errorTitle = computed(() => {
|
||||
const msg = error.value || "";
|
||||
if (msg.includes("链接不存在") || msg.includes("Token") || msg.includes("无效")) {
|
||||
return "入职链接无效";
|
||||
}
|
||||
if (msg.includes("过期")) {
|
||||
return "入职链接已过期";
|
||||
}
|
||||
return "访问受限";
|
||||
});
|
||||
|
||||
const errorDescription = computed(() => {
|
||||
const msg = error.value || "";
|
||||
if (msg.includes("链接不存在") || msg.includes("Token") || msg.includes("无效")) {
|
||||
return "入职链接不存在或已失效,请联系招聘人员重新获取入职链接。";
|
||||
}
|
||||
if (msg.includes("过期")) {
|
||||
return "该链接已超过有效期,不能继续填写。请联系招聘人员重新获取入职链接。";
|
||||
}
|
||||
return `${msg},请检查网络或联系招聘人员协助处理。`;
|
||||
});
|
||||
const currentTimestamp = ref(Date.now());
|
||||
let linkExpiryTimer: number | undefined;
|
||||
const currentStep = ref(0);
|
||||
const uploadingMaterials = reactive<Record<string, boolean>>({});
|
||||
const isAnyMaterialUploading = computed(() => Object.values(uploadingMaterials).some(Boolean));
|
||||
type ContractPrepareStatus = "idle" | "preparing" | "ready" | "error";
|
||||
const contractPrepareStatus = reactive<Record<string, ContractPrepareStatus>>({});
|
||||
const contractPrepareErrors = reactive<Record<string, string>>({});
|
||||
@@ -1465,36 +1536,64 @@ async function validateRegistrationForm() {
|
||||
}
|
||||
|
||||
async function nextStep() {
|
||||
if (currentStep.value === 0) {
|
||||
if (!await validateRegistrationForm()) {
|
||||
return;
|
||||
}
|
||||
if (!validateRegistrationLists()) {
|
||||
return;
|
||||
}
|
||||
if (canEdit.value) {
|
||||
await saveCurrentEmployeeInfo();
|
||||
ElMessage.success("入职登记表已临时保存");
|
||||
}
|
||||
} else if (currentStep.value === 1) {
|
||||
if (!validateExtraVariables()) {
|
||||
return;
|
||||
}
|
||||
if (canEdit.value) {
|
||||
await saveCurrentEmployeeInfo();
|
||||
}
|
||||
} else if (currentStep.value === 2) {
|
||||
const missing = materialRequirements.value.filter(m => m.required && !getUploadedMaterials(m.materialTypeId).length);
|
||||
if (missing.length) {
|
||||
ElMessage.warning(`请先上传必传材料:${missing.map((m: any) => m.materialName).join("、")}`);
|
||||
locateValidationTarget(`material-requirement-${missing[0].materialTypeId}`);
|
||||
return;
|
||||
}
|
||||
if (savingStep.value) {
|
||||
return;
|
||||
}
|
||||
currentStep.value++;
|
||||
window.scrollTo({ top: 0, behavior: "smooth" });
|
||||
if (currentStep.value === 3) {
|
||||
await prepareContracts();
|
||||
if (isAnyMaterialUploading.value) {
|
||||
ElMessage.closeAll();
|
||||
ElMessage.warning("材料正在上传中,请稍候再点击下一步");
|
||||
return;
|
||||
}
|
||||
savingStep.value = true;
|
||||
try {
|
||||
if (currentStep.value === 0) {
|
||||
if (!await validateRegistrationForm()) {
|
||||
return;
|
||||
}
|
||||
if (!validateRegistrationLists()) {
|
||||
return;
|
||||
}
|
||||
if (canEdit.value) {
|
||||
await saveCurrentEmployeeInfo();
|
||||
ElMessage.closeAll();
|
||||
ElMessage.success("入职登记表已临时保存");
|
||||
}
|
||||
} else if (currentStep.value === 1) {
|
||||
if (!validateExtraVariables()) {
|
||||
return;
|
||||
}
|
||||
if (canEdit.value) {
|
||||
await saveCurrentEmployeeInfo();
|
||||
}
|
||||
} else if (currentStep.value === 2) {
|
||||
if (isAnyMaterialUploading.value) {
|
||||
ElMessage.closeAll();
|
||||
ElMessage.warning("材料正在上传中,请稍候再点击下一步");
|
||||
return;
|
||||
}
|
||||
const missing = materialRequirements.value.filter(m => m.required && !getUploadedMaterials(m.materialTypeId).length);
|
||||
if (missing.length) {
|
||||
ElMessage.closeAll();
|
||||
ElMessage.warning(`请先上传必传材料:${missing.map((m: any) => m.materialName).join("、")}`);
|
||||
locateValidationTarget(`material-requirement-${missing[0].materialTypeId}`);
|
||||
return;
|
||||
}
|
||||
}
|
||||
currentStep.value++;
|
||||
window.scrollTo({ top: 0, behavior: "smooth" });
|
||||
if (currentStep.value === 3) {
|
||||
await prepareContracts();
|
||||
}
|
||||
} catch (error: any) {
|
||||
const message = error?.msg || error?.message || "临时保存失败,请稍后重试";
|
||||
ElMessage.closeAll();
|
||||
await ElMessageBox.alert(message, "无法进入下一步", {
|
||||
type: "warning",
|
||||
confirmButtonText: "我知道了",
|
||||
closeOnClickModal: false
|
||||
}).catch(() => undefined);
|
||||
} finally {
|
||||
savingStep.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1933,6 +2032,87 @@ onBeforeUnmount(() => {
|
||||
padding-bottom: 60px;
|
||||
}
|
||||
|
||||
/* 异常状态居中卡片样式(对齐 public-status 风格) */
|
||||
.fill-page.is-error-state {
|
||||
display: flex;
|
||||
min-height: 100vh;
|
||||
padding: 24px;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
box-sizing: border-box;
|
||||
background: #f0fdf7;
|
||||
}
|
||||
|
||||
.public-error-container {
|
||||
display: flex;
|
||||
width: 100%;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.error-status-card {
|
||||
width: min(100%, 420px);
|
||||
padding: 42px 28px;
|
||||
text-align: center;
|
||||
background: #ffffff;
|
||||
border: 1px solid #d1fae5;
|
||||
border-radius: 16px;
|
||||
box-shadow: 0 12px 36px rgb(5 150 105 / 10%);
|
||||
}
|
||||
|
||||
.error-status-mark {
|
||||
display: flex;
|
||||
width: 56px;
|
||||
height: 56px;
|
||||
margin: 0 auto 20px;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
color: #ffffff;
|
||||
font-size: 30px;
|
||||
font-weight: 700;
|
||||
background: #059669;
|
||||
border-radius: 50%;
|
||||
}
|
||||
|
||||
.error-status-title {
|
||||
margin: 0;
|
||||
color: #064e3b;
|
||||
font-size: 24px;
|
||||
font-weight: 700;
|
||||
line-height: 1.3;
|
||||
}
|
||||
|
||||
.error-status-desc {
|
||||
margin: 14px 0 0;
|
||||
color: #64748b;
|
||||
font-size: 15px;
|
||||
line-height: 1.8;
|
||||
}
|
||||
|
||||
.error-status-actions {
|
||||
margin-top: 24px;
|
||||
}
|
||||
|
||||
.error-retry-btn {
|
||||
height: 40px;
|
||||
padding: 0 26px;
|
||||
font-size: 15px;
|
||||
font-weight: 500;
|
||||
border-radius: 8px;
|
||||
background-color: #059669 !important;
|
||||
border-color: #059669 !important;
|
||||
color: #ffffff !important;
|
||||
box-shadow: 0 4px 12px rgba(5, 150, 105, 0.2);
|
||||
transition: all 0.2s ease;
|
||||
}
|
||||
|
||||
.error-retry-btn:hover {
|
||||
background-color: #047857 !important;
|
||||
border-color: #047857 !important;
|
||||
transform: translateY(-1px);
|
||||
box-shadow: 0 6px 16px rgba(5, 150, 105, 0.3);
|
||||
}
|
||||
|
||||
/* 顶部大背景 (Hero Header Background) - 纯色柔和淡绿,去渐变 */
|
||||
.brand-header {
|
||||
background: #ecfdf5;
|
||||
@@ -2625,6 +2805,72 @@ onBeforeUnmount(() => {
|
||||
background-color: #ecfdf5;
|
||||
}
|
||||
|
||||
.wizard-footer .el-button--success {
|
||||
background-color: #059669 !important;
|
||||
border-color: #059669 !important;
|
||||
color: #ffffff !important;
|
||||
font-weight: 600;
|
||||
border-radius: 6px;
|
||||
padding: 10px 24px;
|
||||
height: 40px;
|
||||
box-shadow: 0 2px 8px rgba(5, 150, 105, 0.25);
|
||||
transition: all 0.2s;
|
||||
}
|
||||
|
||||
.wizard-footer .el-button--success:hover {
|
||||
background-color: #047857 !important;
|
||||
border-color: #047857 !important;
|
||||
color: #ffffff !important;
|
||||
transform: translateY(-1px);
|
||||
box-shadow: 0 4px 12px rgba(5, 150, 105, 0.35);
|
||||
}
|
||||
|
||||
.wizard-footer .el-button--success:active {
|
||||
background-color: #065f46 !important;
|
||||
border-color: #065f46 !important;
|
||||
color: #ffffff !important;
|
||||
}
|
||||
|
||||
/* 个人信息已完整覆盖卡片与图标 */
|
||||
.all-covered-card {
|
||||
padding: 38px 24px;
|
||||
background: #ffffff;
|
||||
border: 1px solid #d1fae5;
|
||||
border-radius: 14px;
|
||||
box-shadow: 0 4px 20px rgba(5, 150, 105, 0.06);
|
||||
text-align: center;
|
||||
margin: 16px 0 24px;
|
||||
}
|
||||
|
||||
.covered-status-mark {
|
||||
display: flex;
|
||||
width: 56px;
|
||||
height: 56px;
|
||||
margin: 0 auto 16px;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
color: #ffffff;
|
||||
background: #059669;
|
||||
border-radius: 50%;
|
||||
box-shadow: 0 4px 14px rgba(5, 150, 105, 0.25);
|
||||
}
|
||||
|
||||
.covered-status-title {
|
||||
margin: 0 0 10px;
|
||||
color: #064e3b;
|
||||
font-size: 20px;
|
||||
font-weight: 700;
|
||||
letter-spacing: 0.5px;
|
||||
}
|
||||
|
||||
.covered-status-desc {
|
||||
max-width: 480px;
|
||||
margin: 0 auto;
|
||||
color: #64748b;
|
||||
font-size: 14px;
|
||||
line-height: 1.7;
|
||||
}
|
||||
|
||||
.readonly-banner {
|
||||
margin-bottom: 24px;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user