Files
ks-app-employment-service/packageB/jobFair/detailPerson.vue

1029 lines
22 KiB
Vue
Raw Normal View History

<template>
<view class="job-detail">
<view class="company-header">
<view class="company-img">
<image :src="`${imgBaseUrl}/jobfair/company.png`" mode=""></image>
</view>
<view>
<view class="company-name">
{{ fair.jobFairTitle }}
<view class="job-type">
{{ fair.jobFairType == 1 ? "线上" : "线下" }}
</view>
</view>
<view class="company-Address">
{{ fair.jobFairAddress }}
<view class="activity-status">
{{
getActivityStatus(fair.jobFairStartTime, fair.jobFairEndTime).text
}}
</view>
</view>
</view>
</view>
<template v-if="isExpanded">
<view class="jobfair-title"> 招聘会单位 </view>
<view class="jobfair-content">
<view> 1招聘会协办单位{{ fair.jobFairHelpUnit }} </view>
<view> 2招聘会主办单位{{ fair.jobFairHostUnit }} </view>
<view> 3招聘会承办单位{{ fair.jobFairOrganizeUnit }} </view>
</view>
<view class="jobfair-title"> 内容描述 </view>
<view class="jobfair-content">
{{ fair.jobFairIntroduction }}
</view>
<view class="jobfair-title"> 联系电话 </view>
<view class="jobfair-content">
{{ fair.jobFairPhone }}
</view>
<view class="jobfair-title"> 招聘会备注 </view>
<view class="jobfair-content">
{{ fair.jobFairRemark }}
</view>
<view class="jobfair-content">
<view class="card-times">
<view class="time-left">
<view class="left-date">{{
parseDateTime(fair.jobFairStartTime).time
}}</view>
<view class="left-dateDay">{{
parseDateTime(fair.jobFairStartTime).date
}}</view>
</view>
<view class="line"></view>
<view class="time-center">
<view class="center-date">
{{
getTimeStatus(fair.jobFairStartTime, fair.jobFairEndTime)
.statusText
}}
</view>
<view class="center-dateDay">
{{
getHoursBetween(fair.jobFairStartTime, fair.jobFairEndTime)
}}小时
</view>
</view>
<view class="line"></view>
<view class="time-right">
<view class="left-date">{{
parseDateTime(fair.jobFairEndTime).time
}}</view>
<view class="left-dateDay">{{
parseDateTime(fair.jobFairEndTime).date
}}</view>
</view>
</view>
</view>
<view class="jobfair-title"> 招聘会照片 </view>
<view class="jobfair-content">
<image :src="publicUrl + '/file/file/minio' + fair.jobFairImage" alt="" v-if="fair.jobFairImage" />
<text v-else>暂无照片</text>
</view>
</template>
<view class="expand" @click="expand">
<text>{{ isExpanded ? "收起" : "展开" }}</text>
<image class="expand-img" :class="{ 'expand-img-active': !isExpanded }" src="@/static/icon/downs.png">
</image>
</view>
<view class="num-box">
<view class="num-item">
<view class="num">
{{ fair.enterpriseNum?fair.enterpriseNum : 0 }}
</view>
<view class="num-desc">
参会企业
</view>
</view>
<view class="num-item">
<view class="num">
{{fair.deliverNum?fair.deliverNum : 0}}
</view>
<view class="num-desc">
已投递简历
</view>
</view>
</view>
<view class="detail-box">
<view class="" v-for="(item1, index1) in jobList" :key="index1">
<view class="company-header1">
<view class="left">
<image :src="`${imgBaseUrl}/jobfair/gs.png`" alt="" class="company-icon" />
<text class="name1">{{ item1.companyName }}</text>
<!-- <div class="header-detail">企业详情 ></div> -->
</view>
</view>
<view class="detail-item" v-for="(job, index) in item1.jobInfoList" :key="index">
<view class="gw">
<text>{{ job.jobTitle }}</text>
<view v-if="job.minSalary && job.maxSalary" class="salary">
{{ job.minSalary }}-{{ job.maxSalary }}
</view>
</view>
<view class="bottom">
<view class="tag">
<view class="tag-item">{{job.industry}}</view>
<view class="tag-item success">{{job.scale}}</view>
</view>
</view>
<view class="name">
<!-- 应聘状态1已投递2已邀请面试3已录用4不录用 -->
<view class="status" v-if="job.jobFairPersonJob?.status">
<text :style="{ color: getStatusText(job.jobFairPersonJob?.status).color }">{{ getStatusText(job.jobFairPersonJob?.status).text }}</text>
</view>
<view v-else class="btn">
<button plain style="color: #1685f7;border-color:#1685f7" @click="deliverResume(job)">简历投递</button>
</view>
</view>
</view>
</view>
</view>
</view>
</template>
<script setup>
import config from "@/config.js";
import {
ref,
reactive,
onMounted,
nextTick,
watch,
inject
} from "vue";
import {
onLoad
} from "@dcloudio/uni-app";
import {
useRouter,
useRoute
} from "vue-router";
const {
$api
} = inject("globalFunction");
const imgBaseUrl = config.imgBaseUrl;
const router = useRouter();
const route = useRoute();
const interviewPopup = ref(null);
const feedBackPopup = ref(null);
const loading = ref(false);
const hasMore = ref(true);
const jobFairId = ref("");
const userInfo = ref({});
const isExpanded = ref(false);
const publicUrl = config.LCBaseUrl;
onLoad((option) => {
jobFairId.value = option.jobFairId;
getUser();
});
// 响应式数据
const interviewForm = reactive({
date: "",
time: "",
address: "",
radio: "1",
});
const feedBack = ref("");
const pages = reactive({
pageNum: 1,
pageSize: 10,
});
const detail = ref({});
const jobList = ref({})
const fair = reactive({});
const openGw = reactive({});
const isAccept = ref(true);
function expand() {
isExpanded.value = !isExpanded.value;
}
// 获取状态文本
const getStatusText = (status) => {
switch (status) {
case "1":
return {
text: "已投递",
color: "#2aa553"
};
case "2":
return {
text: "已邀请面试",
color: "#409EFF"
};
case "3":
return {
text: "已录用",
color: "#67C23A"
};
case "4":
return {
text: "不录用",
color: "#F56C6C"
};
default:
return {
text: "未知状态",
color: "#2aa553"
};
}
};
const baseUrl = config.imgBaseUrl;
const getItemBackgroundStyle = (imageName) => ({
backgroundImage: `url(${baseUrl}/jobfair/${imageName})`,
backgroundSize: "100% 100%", // 覆盖整个容器
backgroundPosition: "center", // 居中
backgroundRepeat: "no-repeat",
});
const getDetail = () => {
const data = {
jobFairId: jobFairId.value,
personId: userInfo.value.info.userId,
};
$api.myRequest("/jobfair/public/jobfair/detail", data).then((resData) => {
// 直接将数据合并到fair对象
Object.assign(fair, resData.data);
console.log(fair, "fair data");
});
};
function changeAddress(e) {
interviewForm.radio = e.detail.value;
}
const getUser = () => {
const raw = uni.getStorageSync("Padmin-Token");
const token = typeof raw === "string" ? raw.trim() : "";
const headers = token ? {
Authorization: raw.startsWith("Bearer ") ? raw : `Bearer ${token}`,
} : {};
$api
.myRequest("/system/user/login/user/info", {}, "GET", 10100, headers)
.then((resData) => {
userInfo.value = resData;
getDetail();
getList(false);
});
};
// 岗位列表
const getList = (isLoadMore = false) => {
if (loading.value) return;
loading.value = true;
try {
$api
.myRequest(
"/jobfair/public/jobfair/enterprises-with-jobs-by-job-fair-id", {
personId: userInfo.value.info.userId,
jobFairId: jobFairId.value,
}
)
.then((data) => {
if (data.code === 200) {
jobList.value = data.data
}
});
} catch (error) {
console.log(error, "error");
uni.showToast({
title: "获取数据失败",
icon: "none",
});
} finally {
loading.value = false;
}
};
// 面试邀请
const interviewBtn = (item) => {
Object.assign(openGw, item);
interviewPopup.value.open();
};
// 关闭面试弹窗
const closeInterviewPopup = () => {
interviewPopup.value.close();
};
// 关闭反馈弹窗
const closeFeedBackPopup = () => {
feedBackPopup.value.close();
};
// 岗位投递
function deliverResume(job) {
const raw = uni.getStorageSync("Padmin-Token");
const token = typeof raw === "string" ? raw.trim() : "";
const headers = token ? {
Authorization: raw.startsWith("Bearer ") ? raw : `Bearer ${token}`
} : {};
$api.myRequest("/dashboard/auth/heart", {}, "POST", 10100, headers).then((resData1) => {
if (resData1.code == 200) {
$api.myRequest("/system/user/login/user/info", {}, "GET", 10100, headers).then((resData) => {
$api.myRequest("/jobfair/public/job-fair-person-job/insert", {
jobFairId: job.jobFairId, // 招聘会id
personId: resData.info.userId, // 当前登录用户id
enterpriseId: job.companyId, // 企业id
jobId: job.jobId, // 岗位id
idCard:resData.info.personCardNo
}, "post", 9100, {
"Content-Type": "application/json"
}).then((data) => {
if (data && data.code === 200) {
$api.msg("简历投递成功");
getList(false);
} else {
$api.msg((data && data.msg) || "简历投递失败");
}
});
});
} else {
$api.msg('请先登录')
}
});
}
// 提交面试邀请
const submitInterview = () => {
if (!interviewForm.date) {
uni.showToast({
title: "请选择面试日期",
icon: "none",
});
return;
}
if (!interviewForm.time) {
uni.showToast({
title: "请选择面试时间",
icon: "none",
});
return;
}
if (interviewForm.radio == "2" && !interviewForm.address) {
uni.showToast({
title: "请输入面试地址",
icon: "none",
});
return;
}
const data = {
jobFairPersonJobId: openGw.jobFairPersonJobId,
status: "2",
interviewTime: `${interviewForm.date} ${interviewForm.time}`,
interviewAddress: interviewForm.radio == "2" ? interviewForm.address : fair.jobFairAddress,
};
update(data, "面试邀请成功");
};
function toIOSDate(input) {
if (!input) return null;
if (input instanceof Date) return isNaN(input.getTime()) ? null : input;
if (typeof input === "number") {
const d = new Date(input);
return isNaN(d.getTime()) ? null : d;
}
if (typeof input !== "string") return null;
let s = input.trim();
// "YYYY-MM-DD HH:mm[:ss]" -> "YYYY-MM-DDTHH:mm[:ss]"
if (/^\d{4}-\d{2}-\d{2}\s+\d{2}:\d{2}(:\d{2})?$/.test(s)) {
s = s.replace(" ", "T");
// 仅到分钟则补秒
if (/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}$/.test(s)) {
s = s + ":00";
}
}
// 其余已是 iOS 可解析格式:"YYYY/MM/DD[ HH:mm:ss]"、"YYYY-MM-DD"、ISO 8601 等
const d = new Date(s);
return isNaN(d.getTime()) ? null : d;
}
function parseDateTime(datetimeStr) {
if (!datetimeStr)
return {
time: "",
date: "",
};
const dateObj = toIOSDate(datetimeStr);
if (!dateObj)
return {
time: "",
date: "",
}; // 无效时间
const year = dateObj.getFullYear();
const month = String(dateObj.getMonth() + 1).padStart(2, "0");
const day = String(dateObj.getDate()).padStart(2, "0");
const hours = String(dateObj.getHours()).padStart(2, "0");
const minutes = String(dateObj.getMinutes()).padStart(2, "0");
return {
time: `${hours}:${minutes}`,
date: `${year}${month}${day}`,
};
}
function getTimeStatus(startTimeStr, endTimeStr) {
const now = new Date();
const startTime = toIOSDate(startTimeStr);
const endTime = toIOSDate(endTimeStr);
if (!startTime || !endTime) {
return {
status: 1,
statusText: "时间异常",
color: "#999999",
};
}
// 判断状态0 开始中1 过期2 未开始
let status = 0;
let statusText = "开始中";
let color = "#13C57C"; // 进行中 - 绿色
if (now < startTime) {
status = 2; // 未开始
statusText = "未开始";
color = "#015EEA"; // 未开始 - 蓝色
} else if (now > endTime) {
status = 1; // 已过期
statusText = "已过期";
color = "#999999"; // 已过期 - 灰色
} else {
status = 0; // 进行中
statusText = "进行中";
color = "#13C57C"; // 进行中 - 绿色
}
return {
status, // 0: 进行中1: 已过期2: 未开始
statusText,
color,
};
}
function getHoursBetween(startTimeStr, endTimeStr) {
const start = toIOSDate(startTimeStr);
const end = toIOSDate(endTimeStr);
if (!start || !end) return 0;
const diffMs = end - start;
const diffHours = diffMs / (1000 * 60 * 60);
return +diffHours.toFixed(2); // 保留 2 位小数
}
// 更新简历状态
const update = (data, msg) => {
$api
.myRequest(
"/jobfair/public/job-fair-person-job/update",
data,
"POST",
9100, {
"Content-Type": "application/json",
}
)
.then((resData) => {
if (resData.code == 200) {
uni.showToast({
title: msg,
icon: "success",
});
closeInterviewPopup();
closeFeedBackPopup();
// 重新加载列表
pages.pageNum = 1;
getList(false);
} else {
uni.showToast({
title: "操作失败",
icon: "none",
});
}
});
};
// 提交反馈
const submitFeedBack = () => {
if (!feedBack.value) {
uni.showToast({
title: "请输入内容",
icon: "none",
});
return;
}
const data = {
jobFairPersonJobId: openGw.jobFairPersonJobId,
status: isAccept.value ? "3" : "4",
feedback: feedBack.value,
};
update(data, "操作成功");
feedBack.value = "";
};
// 录用/不录用按钮
const acceptBtn = (item, num) => {
isAccept.value = num == 0;
Object.assign(openGw, item);
feedBackPopup.value.open();
};
// 返回
const goBack = () => {
router.go(-1);
};
// 获取活动状态
const getActivityStatus = (startTime, endTime) => {
// 获取当前时间戳(毫秒)
const now = Date.now();
// 将开始时间和结束时间转换为时间戳
const start =
typeof startTime === "string" ? new Date(startTime).getTime() : startTime;
const end =
typeof endTime === "string" ? new Date(endTime).getTime() : endTime;
// 处理无效时间
if (isNaN(start) || isNaN(end)) {
return {
text: "未知状态",
style: {},
};
}
// 判断状态
if (now < start) {
return {
status: 0,
text: "未开始",
style: {},
};
} else if (now > end) {
return {
status: 1,
text: "已结束",
style: {},
};
} else {
return {
status: 2,
text: "进行中",
style: {},
};
}
};
// 滚动到底部加载更多
const onReachBottom = () => {
if (!loading.value && hasMore.value) {
pages.pageNum++;
getList(true);
}
};
</script>
<style lang="scss" scoped>
.jobfair-content {
color: #656565;
margin-bottom: 20rpx;
line-height: 44rpx;
image {
width: 300rpx;
height: 300rpx;
}
}
.jobfair-title {
width: max-content;
color: #404040;
font-weight: 600;
font-size: 28rpx;
margin-bottom: 25rpx;
position: relative;
&::after {
bottom: -8px;
position: absolute;
left: 31%;
content: "";
display: block;
width: 37rpx;
height: 9rpx;
background: linear-gradient(to right, #ffad58 0%, #ff7a5b 100%);
border-radius: 10rpx;
}
}
.job-detail {
width: 100%;
height: 100vh;
overflow-y: auto;
padding: 20rpx;
background: #e9f2ff;
box-sizing: border-box;
.detail-box {
padding-top: 20rpx;
box-sizing: border-box;
.company-header1 {
display: flex;
align-items: center;
justify-content: space-between;
margin-top: 12px;
.name1 {
font-weight: 600;
margin-right: 12px;
font-size: 18px;
}
.company-icon {
width: 20px;
height: 20px;
margin-right: 15px;
}
.left {
display: flex;
align-items: center;
.header-detail {
background: #5599ff;
border-radius: 18px;
padding: 2px 16px;
color: #fff;
font-size: 14px;
}
}
.icon {
margin-right: 6px;
color: #409eff;
}
.booth-no {
color: #909399;
}
}
.detail-item {
margin-top: 16rpx;
background: linear-gradient(to bottom, #deecff 0%, #ffffff 100%);
box-shadow: 0 0 10rpx rgba(0, 95, 169, 0.19);
border-radius: 12rpx;
border: 2rpx solid #ffffff;
padding: 30rpx;
padding-bottom: 0;
}
.name {
font-size: 32rpx;
font-weight: 600;
display: flex;
align-items: center;
justify-content: flex-end;
.status {
width: 160rpx;
text-align: center;
margin-left: 24rpx;
font-size: 24rpx;
background-size: 100% 100%;
font-weight: 600;
padding: 6rpx 0;
margin: 18rpx 0;
}
.btn {
width: 100%;
display: flex;
align-items: center;
justify-content: flex-end;
button {
margin: 10rpx;
font-size: 24rpx;
}
}
}
.bottom {
display: flex;
justify-content: space-between;
align-items: center;
flex-wrap: wrap;
.tag {
width: 100%;
display: flex;
align-items: center;
gap: 16rpx;
flex-wrap: wrap;
.tag-item {
padding: 6rpx 30rpx;
border-radius: 6rpx;
background-color: #E5F1FF;
color: #589BFF;
font-size: 28rpx;
&.success {
background-color: #E6F8EB;
color: #21AA5B;
}
}
}
}
.gw {
display: flex;
align-items: center;
justify-content: space-between;
color: #1477f1;
font-weight: 600;
font-size: 30rpx;
}
}
.company-header {
display: flex;
align-items: center;
gap: 20rpx;
margin-bottom: 26rpx;
.company-img {
width: 110rpx;
height: 107rpx;
border-radius: 20rpx;
image {
width: 100%;
height: 100%;
}
}
}
.company-Address {
font-size: 26rpx;
color: #404040;
display: flex;
align-items: center;
gap: 10rpx;
.activity-status {
font-size: 24rpx;
color: #0088ff;
font-weight: 600;
position: relative;
margin-left: 10rpx;
&::before {
content: "";
display: inline-block;
width: 10rpx;
height: 10rpx;
background: #0088ff;
border-radius: 50rpx;
margin-right: 6rpx;
}
}
}
.company-name {
display: flex;
align-items: center;
font-size: 38rpx;
font-weight: bold;
color: #4d4d4d;
position: relative;
margin-bottom: 10rpx;
gap: 10rpx;
.job-type {
padding: 0 20rpx;
color: #fff;
background: #fd9a33;
border-radius: 20rpx;
font-weight: normal;
font-size: 24rpx;
}
}
.loading,
.no-more {
text-align: center;
padding: 20rpx 0;
color: #909399;
font-size: 28rpx;
}
}
// 弹窗样式
.popup-content {
width: 90vw;
height: 70vh;
// max-width: 600rpx;
// background: #fff;
border-radius: 20rpx;
padding: 10rpx 20rpx;
}
.btn-box {
width: 100%;
padding: 15rpx 0 20rpx;
display: flex;
justify-content: center;
align-items: center;
gap: 50rpx;
button {
padding: 0 80rpx;
height: 80rpx;
line-height: 80rpx;
}
}
.job-dialog {
// background: url("../../../assets/images/job/company/module/") no-repeat 100% 100%;
.dialog-content {
padding: 10rpx;
height: 80%;
.detail-item1 {
// display: flex;
// align-items: center;
margin: 30rpx 0;
.label {
width: 200rpx;
font-size: 32rpx;
color: #303133;
margin-bottom: 15rpx;
}
.value {
flex: 1;
display: flex;
gap: 20rpx;
align-items: center;
}
.picker {
width: 275rpx;
border: 1px solid #dcdfe6;
border-radius: 8rpx;
padding: 16rpx;
}
}
}
.title {
font-size: 38rpx;
font-weight: 600;
color: #303133;
padding: 20rpx;
padding-bottom: 0;
display: flex;
align-items: center;
position: sticky;
top: 0;
image {
width: 14rpx;
height: 33rpx;
margin-right: 14rpx;
}
}
textarea {
width: 100%;
height: 300rpx;
border: 1px solid #dcdfe6;
border-radius: 8rpx;
padding: 20rpx;
box-sizing: border-box;
}
input {
width: 100%;
border: 1px solid #dcdfe6;
border-radius: 8rpx;
height: 75rpx;
line-height: 75rpx;
}
}
.card-times {
display: flex;
justify-content: space-between;
align-items: center;
margin-top: 24rpx;
.left-date {
font-weight: 600;
font-size: 48rpx;
font-family: "PingFangSC-Medium", "PingFang SC", "Helvetica Neue", Helvetica,
Arial, "Microsoft YaHei", sans-serif;
background: linear-gradient(180deg, #015eea 0%, #00c0fa 100%);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
}
.left-dateDay {
font-weight: 400;
font-size: 24rpx;
color: #333333;
margin-top: 12rpx;
}
.line {
width: 40rpx;
height: 2rpx;
background: #7bb6ff;
margin-top: 7rpx;
}
.time-center {
text-align: center;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
}
}
.card-times .time-center .center-dateDay {
font-weight: 400;
font-size: 24rpx;
color: #fff;
margin-top: 6rpx;
line-height: 48rpx;
width: max-content;
height: 48rpx;
background: #71b1ff;
border-radius: 8rpx;
padding: 0 30rpx;
}
.card-times .time-left,
.card-times .time-right {
text-align: center;
}
.expand {
display: flex;
flex-wrap: nowrap;
white-space: nowrap;
justify-content: center;
margin-bottom: 12rpx;
font-weight: 400;
font-size: 28rpx;
color: #256bfa;
.expand-img {
width: 40rpx;
height: 40rpx;
}
.expand-img-active {
transform: rotate(180deg);
}
}
.num-box {
width: 100%;
display: flex;
justify-content: center;
align-items: center;
.num-item {
width: 50%;
text-align: center;
.num {
font-size: 40rpx;
font-weight: 600;
background: linear-gradient(184deg, #0BBAFB 0%, #4285EC 100%);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
}
.num-desc {
font-size: 30rpx;
color: #0C458A;
}
}
}
</style>