This commit is contained in:
zlwu committed 2025-10-25 17:19:54 +08:00
1 parent 9310a7323f
commit 384373a2c1
11 files changed
+2053 -1810

No files matched your search

+26
View File
@@ -159,6 +159,32 @@ page {
background: #0051d9 !important;
}
/* Navbar导航栏主题色覆盖 - 勤工俭学模块统一背景色 */
.u-navbar {
background-color: #0051d9 !important;
}
.u-navbar__content {
background-color: #0051d9 !important;
}
.u-navbar__content__title {
color: #fff !important;
}
/* uni-app 原生导航栏样式覆盖 */
uni-page-head {
background-color: #0051d9 !important;
}
.uni-page-head {
background-color: #0051d9 !important;
}
.uni-page-head__title {
color: #fff !important;
}
/* ========== 全局温暖商务风格样式 ========== */
/* 全局页面背景 */
+4 -4
View File
@@ -274,7 +274,7 @@
"path": "pages/workStudy/index",
"style": {
"navigationBarTitleText": "勤工俭学",
"navigationBarBackgroundColor": "#1890ff",
"navigationBarBackgroundColor": "#0051d9",
"navigationBarTextStyle": "white"
}
},
@@ -282,7 +282,7 @@
"path": "pages/workStudy/detail",
"style": {
"navigationBarTitleText": "岗位详情",
"navigationBarBackgroundColor": "#1890ff",
"navigationBarBackgroundColor": "#0051d9",
"navigationBarTextStyle": "white"
}
},
@@ -290,7 +290,7 @@
"path": "pages/workStudy/apply",
"style": {
"navigationBarTitleText": "申请岗位",
"navigationBarBackgroundColor": "#1890ff",
"navigationBarBackgroundColor": "#0051d9",
"navigationBarTextStyle": "white"
}
},
@@ -298,7 +298,7 @@
"path": "pages/workStudy/applicationDetail",
"style": {
"navigationBarTitleText": "申请详情",
"navigationBarBackgroundColor": "#1890ff",
"navigationBarBackgroundColor": "#0051d9",
"navigationBarTextStyle": "white"
}
},
+190 -234
View File
@@ -1,5 +1,12 @@
<template>
<view class="internship-page">
<!-- 顶部导航 -->
<u-navbar title="实习打卡" :border-bottom="false" bg-color="#3c9cff" title-color="#fff">
<view slot="left" @click="goBack">
<u-icon name="arrow-left" color="#fff" size="22"></u-icon>
</view>
</u-navbar>
<!-- 顶部用户信息卡片 -->
<view class="user-card">
<view class="user-info">
@@ -23,8 +30,10 @@
<!-- 实习信息卡片 -->
<view class="internship-card" v-if="internshipInfo">
<view class="card-header">
<u-icon name="bookmark" size="18" color="#409eff"></u-icon>
<text class="card-title">实习信息</text>
<view class="header-left">
<u-icon name="file-text" size="18" color="#409eff"></u-icon>
<text class="card-title">实习信息</text>
</view>
</view>
<view class="internship-info">
<view class="info-row">
@@ -59,9 +68,11 @@
<!-- 今日签到卡片 -->
<view class="checkin-card">
<view class="card-header">
<u-icon name="clock" size="18" color="#67c23a"></u-icon>
<text class="card-title">今日签到</text>
<view class="card-header card-header-between">
<view class="header-left">
<u-icon name="checkmark-circle" size="18" color="#67c23a"></u-icon>
<text class="card-title">今日签到</text>
</view>
<text class="date-text">{{ currentDate }}</text>
</view>
@@ -85,14 +96,20 @@
<view class="checkin-actions" v-else>
<u-button
type="primary"
size="large"
shape="round"
:loading="checkinLoading"
:disabled="checkinLoading || !canCheckin"
@click="handleCheckin"
:custom-style="checkinButtonStyle"
class="checkin-btn"
:custom-style="{ height: '88rpx' }"
>
<u-icon name="map" size="20" color="#fff" style="margin-right: 8px"></u-icon>
{{ checkinLoading ? '签到中...' : '立即签到' }}
<u-icon
name="checkmark-circle"
size="20"
color="#fff"
style="margin-right: 8rpx"
></u-icon>
<text style="color: #fff">{{ checkinLoading ? '签到中...' : '立即签到' }}</text>
</u-button>
<view class="checkin-tips" v-if="!canCheckin">
@@ -106,6 +123,7 @@
<view class="location-card">
<view class="card-header">
<view class="header-left">
<u-icon name="map" size="18" color="#f56c6c"></u-icon>
<text class="card-title">签到位置</text>
</view>
</view>
@@ -114,14 +132,14 @@
<view class="location-row">
<u-button
type="primary"
size="default"
plain
shape="round"
@click="handleGetLocation"
:loading="locationLoading"
class="location-btn"
:custom-style="{ height: '88rpx' }"
>
<u-icon name="map-fill" size="20" color="#2979ff" />
<text style="margin-left: 8rpx">
<u-icon name="map-fill" size="20" color="#fff" />
<text style="margin-left: 8rpx; color: #fff">
{{ checkIsWechatBrowser() ? '微信定位' : '定位' }}
</text>
</u-button>
@@ -129,13 +147,23 @@
<!-- 位置信息显示 -->
<view v-if="currentLocation" class="location-info-section">
<!-- 坐标信息 -->
<view class="location-coords-container">
<!-- 坐标信息和查看位置按钮 -->
<view class="location-coords-row">
<text class="location-coords">
经纬度:{{ currentLocation.latitude.toFixed(6) }},{{
currentLocation.longitude.toFixed(6)
}}
</text>
<u-button
size="small"
type="primary"
shape="round"
@click="openLocationInMap"
class="view-map-btn"
>
<u-icon name="eye" size="14" color="#fff" />
<text style="margin-left: 4rpx; color: #fff">查看位置</text>
</u-button>
</view>
<!-- 精度信息 -->
@@ -158,20 +186,6 @@
></u-tag>
</view>
</view>
<!-- 查看位置按钮 -->
<view class="location-action-container">
<u-button
size="default"
type="primary"
plain
@click="openLocationInMap"
class="view-map-btn"
>
<u-icon name="eye" size="16" color="#2979ff" />
<text>查看位置</text>
</u-button>
</view>
</view>
</view>
@@ -179,7 +193,7 @@
<view class="location-error-card" v-if="locationError && !currentLocation">
<view class="card-header">
<view class="header-left">
<u-icon name="map-pin" size="18" color="#f56c6c"></u-icon>
<u-icon name="map" size="18" color="#f56c6c"></u-icon>
<text class="card-title">位置信息</text>
</view>
<u-button
@@ -243,8 +257,10 @@
<!-- 考勤统计 -->
<view class="statistics-card">
<view class="card-header">
<u-icon size="18" color="#e6a23c"></u-icon>
<text class="card-title">本月考勤</text>
<view class="header-left">
<u-icon name="calendar" size="18" color="#e6a23c"></u-icon>
<text class="card-title">本月考勤</text>
</view>
</view>
<view class="stats-grid">
<view class="stat-item">
@@ -354,21 +370,15 @@ export default {
// 当前日期
currentDate: '',
// 样式
checkinButtonStyle: {
width: '100%',
height: '50px',
borderRadius: '25px',
background: 'linear-gradient(135deg, #667eea 0%, #764ba2 100%)',
border: 'none',
fontSize: '16px',
fontWeight: 'bold',
},
};
},
methods: {
// 返回上一页
goBack() {
uni.navigateBack();
},
// === 微信SDK相关方法 ===
// 检查微信环境并自动初始化SDK
@@ -1435,18 +1445,17 @@ export default {
<style scoped>
.internship-page {
min-height: 100vh;
background: #f5f7fa;
padding: 16px;
background-color: #f5f6fa;
padding-bottom: 40rpx;
}
/* 用户信息卡片 */
.user-card {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
border-radius: 16px;
padding: 20px;
margin-bottom: 16px;
color: white;
box-shadow: 0 8px 32px rgba(102, 126, 234, 0.3);
background-color: #ffffff;
border-radius: 16rpx;
padding: 30rpx;
margin: 20rpx;
box-shadow: 0 2rpx 12rpx rgba(0, 0, 0, 0.08);
}
.user-info {
@@ -1455,7 +1464,7 @@ export default {
}
.avatar-section {
margin-right: 16px;
margin-right: 24rpx;
}
.info-section {
@@ -1463,15 +1472,16 @@ export default {
}
.user-name {
font-size: 18px;
font-size: 32rpx;
font-weight: bold;
margin-bottom: 4px;
color: #303133;
margin-bottom: 8rpx;
}
.user-detail {
font-size: 14px;
opacity: 0.9;
margin-bottom: 2px;
font-size: 26rpx;
color: #606266;
margin-bottom: 4rpx;
}
/* 通用卡片样式 */
@@ -1480,216 +1490,222 @@ export default {
.location-card,
.statistics-card,
.records-card {
background: white;
border-radius: 12px;
padding: 16px;
margin-bottom: 16px;
box-shadow: 0 2px 12px rgba(0, 0, 0, 0.08);
background-color: #ffffff;
border-radius: 16rpx;
padding: 24rpx;
margin: 0 20rpx 20rpx 20rpx;
box-shadow: 0 2rpx 12rpx rgba(0, 0, 0, 0.08);
}
.card-header {
display: flex;
align-items: center;
justify-content: space-between;
margin-bottom: 16px;
padding-bottom: 12px;
margin-bottom: 24rpx;
padding-bottom: 20rpx;
border-bottom: 1px solid #f0f0f0;
min-height: 32px; /* 确保最小高度 */
gap: 8px; /* 确保元素之间有间距 */
min-height: 48rpx;
}
.card-header-between {
justify-content: space-between;
}
.header-left {
display: flex;
align-items: center;
flex: 1;
min-width: 0; /* 允许flex子项收缩 */
margin-right: 12px; /* 与右侧按钮保持距离 */
gap: 8rpx;
}
.card-title {
font-size: 16px;
font-size: 30rpx;
font-weight: bold;
margin-left: 8px;
margin-left: 0;
color: #303133;
white-space: nowrap; /* 防止文字换行 */
overflow: hidden; /* 隐藏溢出文字 */
text-overflow: ellipsis; /* 显示省略号 */
flex-shrink: 1; /* 允许收缩 */
white-space: nowrap;
}
.refresh-btn {
flex-shrink: 0;
min-width: 70px; /* 确保按钮最小宽度 */
min-width: 100rpx;
}
.date-text {
font-size: 12px;
font-size: 24rpx;
color: #909399;
}
/* 实习信息 */
.internship-info {
padding-top: 8px;
padding-top: 12rpx;
}
.info-row {
display: flex;
align-items: center;
margin-bottom: 8px;
margin-bottom: 16rpx;
}
.label {
font-size: 14px;
font-size: 26rpx;
color: #606266;
width: 80px;
min-width: 140rpx;
flex-shrink: 0;
}
.value {
font-size: 14px;
font-size: 26rpx;
color: #303133;
flex: 1;
}
/* 签到状态 */
.checkin-status {
padding: 16px;
padding: 24rpx;
background: #f0f9ff;
border-radius: 8px;
border-left: 4px solid #67c23a;
border-radius: 12rpx;
border-left: 6rpx solid #67c23a;
}
.status-item {
display: flex;
align-items: center;
margin-bottom: 8px;
margin-bottom: 12rpx;
}
.status-text {
font-size: 16px;
font-size: 30rpx;
font-weight: bold;
color: #67c23a;
margin-left: 8px;
margin-left: 12rpx;
}
.time-info,
.location-info {
display: flex;
align-items: center;
margin-bottom: 4px;
margin-bottom: 8rpx;
}
.time-label,
.location-label {
font-size: 14px;
font-size: 26rpx;
color: #606266;
margin-right: 8px;
margin-right: 12rpx;
}
.time-value,
.location-value {
font-size: 14px;
font-size: 26rpx;
color: #303133;
}
/* 签到操作 */
.checkin-actions {
padding: 16px 0;
padding: 24rpx 0;
}
.checkin-btn {
width: 100%;
}
.checkin-tips {
display: flex;
align-items: center;
justify-content: center;
margin-top: 12px;
margin-top: 16rpx;
}
.tips-text {
font-size: 12px;
font-size: 24rpx;
color: #909399;
margin-left: 4px;
margin-left: 8rpx;
}
/* 位置信息(参考请假页面样式) */
/* 位置信息 */
.location-row {
margin-bottom: 16px;
margin-bottom: 24rpx;
}
.location-btn {
width: 100%;
height: 44px;
height: 72rpx;
display: flex;
align-items: center;
justify-content: center;
border-radius: 8px;
border: 1px solid #2979ff;
background: #fff;
color: #2979ff;
border-radius: 12rpx;
}
.location-info-section {
margin-top: 16px;
padding: 16px;
margin-top: 24rpx;
padding: 24rpx;
background: #f8f9fa;
border-radius: 8px;
border-radius: 12rpx;
border: 1px solid #e4e7ed;
}
.location-coords-container {
margin-bottom: 12px;
margin-bottom: 20rpx;
}
.location-coords-row {
display: flex;
align-items: center;
justify-content: space-between;
margin-bottom: 20rpx;
gap: 16rpx;
}
.location-coords {
font-size: 14px;
font-size: 26rpx;
color: #303133;
font-family: monospace;
line-height: 1.5;
word-break: break-all;
flex: 1;
}
.location-action-container {
display: flex;
justify-content: center;
margin-top: 12px;
margin-top: 20rpx;
}
.view-map-btn {
min-width: 120px;
height: 36px;
display: flex;
align-items: center;
justify-content: center;
gap: 6px;
height: 56rpx !important;
min-width: auto !important;
width: auto !important;
flex-shrink: 0;
padding: 0 20rpx !important;
}
/* 精度信息 */
.accuracy-section {
margin-bottom: 16px;
margin-bottom: 24rpx;
}
.accuracy-info {
display: flex;
align-items: center;
padding: 10px 12px;
padding: 16rpx 20rpx;
background: #f0f9ff;
border-radius: 8px;
border-radius: 12rpx;
border: 1px solid #b3e5fc;
}
.accuracy-icon {
margin-right: 8px;
margin-right: 12rpx;
flex-shrink: 0;
}
.accuracy-text {
font-size: 13px;
font-size: 26rpx;
color: #67c23a;
font-weight: 500;
flex: 1;
}
.accuracy-tag {
margin-left: 8px;
margin-left: 12rpx;
flex-shrink: 0;
}
@@ -1697,41 +1713,37 @@ export default {
.location-actions {
display: flex;
justify-content: center;
margin-top: 4px; /* 与上方内容保持间距 */
margin-top: 12rpx;
}
.map-btn {
min-width: 140px;
width: 100%; /* 在小屏幕上占满宽度 */
max-width: 200px; /* 最大宽度限制 */
min-width: 200rpx;
width: 100%;
max-width: 300rpx;
}
.btn-text {
margin-left: 6px;
margin-left: 8rpx;
}
/* 小屏幕适配 */
@media screen and (max-width: 375px) {
.card-header {
flex-wrap: nowrap; /* 保持单行,避免换行 */
gap: 4px;
min-height: 36px; /* 稍微增加高度 */
flex-wrap: nowrap;
min-height: 36px;
}
.header-left {
min-width: 0; /* 允许完全收缩 */
flex: 1;
margin-right: 6px;
gap: 6rpx;
}
.card-title {
font-size: 15px; /* 稍微减小字体 */
max-width: 100px; /* 限制最大宽度 */
font-size: 28rpx;
}
.refresh-btn {
min-width: 58px; /* 小屏幕下减小按钮宽度 */
font-size: 12px; /* 减小按钮字体 */
min-width: 58px;
font-size: 12px;
}
.map-btn {
@@ -1742,17 +1754,12 @@ export default {
/* 极小屏幕适配(iPhone SE等) */
@media screen and (max-width: 320px) {
.card-header {
gap: 2px;
}
.header-left {
margin-right: 4px;
gap: 4rpx;
}
.card-title {
font-size: 14px;
max-width: 80px;
font-size: 26rpx;
}
.refresh-btn {
@@ -1783,55 +1790,55 @@ export default {
.location-error-card {
background: #fffbe6;
border: 1px solid #ffe58f;
border-radius: 12px;
padding: 16px;
margin-bottom: 16px;
box-shadow: 0 2px 12px rgba(0, 0, 0, 0.08);
border-radius: 16rpx;
padding: 24rpx;
margin: 0 20rpx 20rpx 20rpx;
box-shadow: 0 2rpx 12rpx rgba(0, 0, 0, 0.08);
}
.error-content {
padding-top: 8px;
padding-top: 12rpx;
}
.error-header {
display: flex;
align-items: center;
justify-content: center;
margin-bottom: 16px;
margin-bottom: 24rpx;
}
.error-icon {
margin-right: 8px;
margin-right: 12rpx;
}
.error-title {
font-size: 16px;
font-size: 30rpx;
font-weight: bold;
color: #e6a23c;
}
.error-message {
display: block;
font-size: 14px;
font-size: 26rpx;
color: #faad14;
text-align: center;
margin-bottom: 20px;
margin-bottom: 30rpx;
line-height: 1.5;
}
.error-tips-section {
background: #fff7e6;
border-radius: 8px;
padding: 16px;
border-radius: 12rpx;
padding: 24rpx;
border: 1px solid #ffd591;
}
.tips-title {
display: block;
font-size: 14px;
font-size: 26rpx;
font-weight: bold;
color: #d48806;
margin-bottom: 12px;
margin-bottom: 20rpx;
}
.tips-list {
@@ -1841,7 +1848,7 @@ export default {
.tip-item {
display: flex;
align-items: center;
margin-bottom: 8px;
margin-bottom: 12rpx;
}
.tip-item:last-child {
@@ -1849,12 +1856,12 @@ export default {
}
.tip-icon {
margin-right: 8px;
margin-right: 12rpx;
flex-shrink: 0;
}
.tip-text {
font-size: 13px;
font-size: 24rpx;
color: #d48806;
line-height: 1.4;
}
@@ -1863,40 +1870,40 @@ export default {
.stats-grid {
display: flex;
justify-content: space-between;
padding-top: 8px;
padding-top: 12rpx;
}
.stat-item {
flex: 1;
text-align: center;
padding: 12px 8px;
padding: 20rpx 12rpx;
background: #f8f9fa;
border-radius: 8px;
margin: 0 4px;
border-radius: 12rpx;
margin: 0 8rpx;
}
.stat-number {
display: block;
font-size: 20px;
font-size: 36rpx;
font-weight: bold;
color: #409eff;
margin-bottom: 4px;
color: #2979ff;
margin-bottom: 8rpx;
}
.stat-label {
font-size: 12px;
font-size: 24rpx;
color: #606266;
}
/* 签到记录 */
.records-list {
padding-top: 8px;
padding-top: 12rpx;
}
.record-item {
display: flex;
align-items: center;
padding: 12px 0;
padding: 20rpx 0;
border-bottom: 1px solid #f0f0f0;
}
@@ -1905,17 +1912,17 @@ export default {
}
.record-date {
font-size: 14px;
font-size: 26rpx;
color: #303133;
width: 100px;
width: 160rpx;
flex-shrink: 0;
}
.record-time {
font-size: 14px;
font-size: 26rpx;
color: #606266;
flex: 1;
margin-left: 12px;
margin-left: 20rpx;
}
.record-status {
@@ -1927,72 +1934,21 @@ export default {
display: flex;
flex-direction: column;
align-items: center;
padding: 32px 0;
padding: 60rpx 0;
}
.empty-text {
font-size: 14px;
font-size: 26rpx;
color: #909399;
margin-top: 8px;
margin-top: 12rpx;
}
/* 响应式设计 */
@media screen and (max-width: 375px) {
.coord-row {
padding: 10px;
}
.coord-divider {
margin: 0 12px;
}
.coord-value {
font-size: 13px;
}
.map-btn {
min-width: 120px;
font-size: 14px;
}
.error-tips-section {
padding: 12px;
}
/* uview组件样式调整 */
::v-deep .u-tag {
margin: 0;
}
@media screen and (min-width: 414px) {
.location-address {
padding: 16px;
}
.coord-row {
padding: 16px;
}
.accuracy-info {
padding: 12px 16px;
}
.coord-value {
font-size: 15px;
}
}
/* 深色模式适配 */
@media (prefers-color-scheme: dark) {
.location-address,
.coord-row {
background: rgba(255, 255, 255, 0.05);
}
.accuracy-info {
background: rgba(103, 194, 58, 0.1);
border-color: rgba(103, 194, 58, 0.3);
}
.error-tips-section {
background: rgba(255, 247, 230, 0.1);
border-color: rgba(255, 213, 145, 0.3);
}
::v-deep .u-button {
border-radius: 12rpx;
}
</style>
File diff suppressed because it is too large. Load diff
+235 -193
View File
@@ -1,173 +1,196 @@
<template>
<view class="visit-detail-container">
<!-- 学生信息 -->
<view class="info-section">
<view class="section-title">学生信息</view>
<view class="section-content">
<view class="info-item">
<text class="info-label">学生姓名:</text>
<text class="info-value">{{ visitInfo.studentName || '-' }}</text>
</view>
<view class="info-item">
<text class="info-label">学号:</text>
<text class="info-value">{{ visitInfo.studentNo || '-' }}</text>
</view>
<view class="info-item">
<text class="info-label">班级:</text>
<text class="info-value">{{ visitInfo.className || '-' }}</text>
</view>
<view class="info-item">
<text class="info-label">专业:</text>
<text class="info-value">{{ visitInfo.majorName || '-' }}</text>
</view>
<view class="info-item">
<text class="info-label">学院:</text>
<text class="info-value">{{ visitInfo.collegeName || '-' }}</text>
</view>
<!-- 顶部导航 -->
<u-navbar title="走访详情" :border-bottom="false" bg-color="#3c9cff" title-color="#fff">
<view slot="left" @click="goBack">
<u-icon name="arrow-left" color="#fff" size="22"></u-icon>
</view>
</view>
</u-navbar>
<!-- 企业信息 -->
<view class="info-section">
<view class="section-title">企业信息</view>
<view class="section-content">
<view class="info-item">
<text class="info-label">企业名称:</text>
<text class="info-value">{{ visitInfo.companyName || '-' }}</text>
</view>
<view class="info-item">
<text class="info-label">企业地址:</text>
<text class="info-value">{{ visitInfo.companyAddress || '-' }}</text>
</view>
</view>
</view>
<!-- 加载状态 -->
<u-loading-page :loading="pageLoading" loading-text="加载中..."></u-loading-page>
<!-- 走访基本信息 -->
<view class="info-section">
<view class="section-title">走访基本信息</view>
<view class="section-content">
<view class="info-item">
<text class="info-label">走访日期:</text>
<text class="info-value">{{ visitInfo.visitDate || '-' }}</text>
</view>
<view class="info-item">
<text class="info-label">走访人员:</text>
<text class="info-value">{{ visitInfo.visitorName || '-' }}</text>
</view>
<view class="info-item" v-if="visitInfo.visitorPosition">
<text class="info-label">走访人职务:</text>
<text class="info-value">{{ visitInfo.visitorPosition }}</text>
</view>
<view class="info-item">
<text class="info-label">走访状态:</text>
<u-tag
:text="getStatusText(visitInfo.status)"
:type="getStatusType(visitInfo.status)"
size="mini"
/>
</view>
<view class="info-item info-item-full" v-if="visitInfo.visitPurpose">
<text class="info-label">走访目的:</text>
<text class="info-value info-value-multiline">{{ visitInfo.visitPurpose }}</text>
</view>
</view>
</view>
<!-- 走访内容 -->
<view class="info-section">
<view class="section-title">走访内容</view>
<view class="section-content">
<view class="info-item info-item-full" v-if="visitInfo.companySituation">
<text class="info-label">企业情况:</text>
<text class="info-value info-value-multiline">{{ visitInfo.companySituation }}</text>
</view>
<view class="info-item info-item-full" v-if="visitInfo.studentPerformance">
<text class="info-label">学生表现:</text>
<text class="info-value info-value-multiline">{{ visitInfo.studentPerformance }}</text>
</view>
<view class="info-item info-item-full" v-if="visitInfo.problemsFound">
<text class="info-label">发现问题:</text>
<text class="info-value info-value-multiline">{{ visitInfo.problemsFound }}</text>
</view>
<view class="info-item info-item-full" v-if="visitInfo.suggestions">
<text class="info-label">改进建议:</text>
<text class="info-value info-value-multiline">{{ visitInfo.suggestions }}</text>
</view>
<view class="info-item info-item-full" v-if="visitInfo.visitSummary">
<text class="info-label">走访总结:</text>
<text class="info-value info-value-multiline">{{ visitInfo.visitSummary }}</text>
</view>
</view>
</view>
<!-- 现场照片 -->
<view class="info-section" v-if="imageList.length > 0">
<view class="section-title">现场照片</view>
<view class="section-content">
<view class="image-grid">
<view
class="image-item"
v-for="(image, index) in imageList"
:key="index"
@click="previewImage(index)"
>
<image :src="image.url" mode="aspectFill" />
<!-- 内容区域 -->
<view v-if="!pageLoading" class="content-wrapper">
<!-- 学生信息 -->
<view class="info-section">
<view class="section-title">学生信息</view>
<view class="section-content">
<view class="info-item">
<text class="info-label">学生姓名:</text>
<text class="info-value">{{ visitInfo.studentName || '-' }}</text>
</view>
<view class="info-item">
<text class="info-label">学号:</text>
<text class="info-value">{{ visitInfo.studentNo || '-' }}</text>
</view>
<view class="info-item">
<text class="info-label">班级:</text>
<text class="info-value">{{ visitInfo.className || '-' }}</text>
</view>
<view class="info-item">
<text class="info-label">专业:</text>
<text class="info-value">{{ visitInfo.majorName || '-' }}</text>
</view>
<view class="info-item">
<text class="info-label">学院:</text>
<text class="info-value">{{ visitInfo.collegeName || '-' }}</text>
</view>
</view>
</view>
</view>
<!-- 备注信息 -->
<view class="info-section" v-if="visitInfo.remark">
<view class="section-title">备注信息</view>
<view class="section-content">
<view class="info-item info-item-full">
<text class="info-value info-value-multiline">{{ visitInfo.remark }}</text>
<!-- 企业信息 -->
<view class="info-section">
<view class="section-title">企业信息</view>
<view class="section-content">
<view class="info-item">
<text class="info-label">企业名称:</text>
<text class="info-value">{{ visitInfo.companyName || '-' }}</text>
</view>
<view class="info-item">
<text class="info-label">企业地址:</text>
<text class="info-value">{{ visitInfo.companyAddress || '-' }}</text>
</view>
</view>
</view>
</view>
<!-- 系统信息 -->
<view class="info-section">
<view class="section-title">系统信息</view>
<view class="section-content">
<view class="info-item">
<text class="info-label">创建时间:</text>
<text class="info-value">{{ visitInfo.createTime || '-' }}</text>
</view>
<view class="info-item" v-if="visitInfo.updateTime">
<text class="info-label">更新时间:</text>
<text class="info-value">{{ visitInfo.updateTime }}</text>
<!-- 走访基本信息 -->
<view class="info-section">
<view class="section-title">走访基本信息</view>
<view class="section-content">
<view class="info-item">
<text class="info-label">走访日期:</text>
<text class="info-value">{{ visitInfo.visitDate || '-' }}</text>
</view>
<view class="info-item">
<text class="info-label">走访人员:</text>
<text class="info-value">{{ visitInfo.visitorName || '-' }}</text>
</view>
<view class="info-item" v-if="visitInfo.visitorPosition">
<text class="info-label">走访人职务:</text>
<text class="info-value">{{ visitInfo.visitorPosition }}</text>
</view>
<view class="info-item">
<text class="info-label">走访状态:</text>
<u-tag
:text="getStatusText(visitInfo.status)"
:type="getStatusType(visitInfo.status)"
size="mini"
/>
</view>
<view class="info-item info-item-full" v-if="visitInfo.visitPurpose">
<text class="info-label">走访目的:</text>
<text class="info-value info-value-multiline">
{{ visitInfo.visitPurpose }}
</text>
</view>
</view>
</view>
</view>
<!-- 操作按钮 -->
<view class="action-section" v-if="canEdit">
<u-button
type="primary"
@click="handleEdit"
:loading="loading"
style="margin-right: 20rpx;"
>
编辑走访记录
</u-button>
<u-button
type="error"
@click="handleDelete"
:loading="loading"
plain
>
删除记录
</u-button>
</view>
<!-- 走访内容 -->
<view class="info-section">
<view class="section-title">走访内容</view>
<view class="section-content">
<view class="info-item info-item-full" v-if="visitInfo.companySituation">
<text class="info-label">企业情况:</text>
<text class="info-value info-value-multiline">
{{ visitInfo.companySituation }}
</text>
</view>
<view class="info-item info-item-full" v-if="visitInfo.studentPerformance">
<text class="info-label">学生表现:</text>
<text class="info-value info-value-multiline">
{{ visitInfo.studentPerformance }}
</text>
</view>
<view class="info-item info-item-full" v-if="visitInfo.problemsFound">
<text class="info-label">发现问题:</text>
<text class="info-value info-value-multiline">
{{ visitInfo.problemsFound }}
</text>
</view>
<view class="info-item info-item-full" v-if="visitInfo.suggestions">
<text class="info-label">改进建议:</text>
<text class="info-value info-value-multiline">
{{ visitInfo.suggestions }}
</text>
</view>
<view class="info-item info-item-full" v-if="visitInfo.visitSummary">
<text class="info-label">走访总结:</text>
<text class="info-value info-value-multiline">
{{ visitInfo.visitSummary }}
</text>
</view>
</view>
</view>
<!-- 加载遮罩 -->
<u-loading-page
:loading="pageLoading"
loading-text="加载中..."
loading-mode="circle"
/>
<!-- 现场照片 -->
<view class="info-section" v-if="imageList.length > 0">
<view class="section-title">现场照片</view>
<view class="section-content">
<view class="image-grid">
<view
class="image-item"
v-for="(image, index) in imageList"
:key="index"
@click="previewImage(index)"
>
<image :src="image.url" mode="aspectFill" />
</view>
</view>
</view>
</view>
<!-- 备注信息 -->
<view class="info-section" v-if="visitInfo.remark">
<view class="section-title">备注信息</view>
<view class="section-content">
<view class="info-item info-item-full">
<text class="info-value info-value-multiline">{{ visitInfo.remark }}</text>
</view>
</view>
</view>
<!-- 系统信息 -->
<view class="info-section">
<view class="section-title">系统信息</view>
<view class="section-content">
<view class="info-item">
<text class="info-label">创建时间:</text>
<text class="info-value">{{ visitInfo.createTime || '-' }}</text>
</view>
<view class="info-item" v-if="visitInfo.updateTime">
<text class="info-label">更新时间:</text>
<text class="info-value">{{ visitInfo.updateTime }}</text>
</view>
</view>
</view>
<!-- 操作按钮 -->
<view class="action-section" v-if="canEdit">
<u-button
type="primary"
shape="round"
@click="handleEdit"
:loading="loading"
style="flex: 1; margin-right: 20rpx"
>
<u-icon name="edit-pen" size="16" style="margin-right: 4rpx"></u-icon>
编辑走访记录
</u-button>
<u-button
type="error"
shape="round"
@click="handleDelete"
:loading="loading"
plain
style="flex: 1"
>
<u-icon name="trash" size="16" style="margin-right: 4rpx"></u-icon>
删除记录
</u-button>
</view>
</view>
</view>
</template>
@@ -180,29 +203,31 @@ export default {
return {
// 页面参数
visitId: '',
// 页面状态
pageLoading: true,
loading: false,
// 走访信息
visitInfo: {},
// 图片数据
imageList: []
imageList: [],
};
},
computed: {
// 是否可以编辑
canEdit() {
// 只有计划中或草稿状态的记录可以编辑
return this.visitInfo.status === 'planned' ||
this.visitInfo.status === '0' ||
this.visitInfo.status === 'draft';
}
return (
this.visitInfo.status === 'planned' ||
this.visitInfo.status === '0' ||
this.visitInfo.status === 'draft'
);
},
},
onLoad(options) {
if (options.id) {
this.visitId = options.id;
@@ -212,17 +237,22 @@ export default {
uni.navigateBack();
}
},
methods: {
// 返回上一页
goBack() {
uni.navigateBack();
},
// 加载走访记录详情
async loadVisitDetail() {
this.pageLoading = true;
try {
const response = await getVisit(this.visitId);
if (response.code === 200 && response.data) {
this.visitInfo = response.data;
// 处理图片数据
if (response.data.visitImages) {
try {
@@ -232,7 +262,6 @@ export default {
this.imageList = [];
}
}
} else {
uni.showToast({ title: response.msg || '获取数据失败', icon: 'none' });
uni.navigateBack();
@@ -245,7 +274,7 @@ export default {
this.pageLoading = false;
}
},
// 获取状态文本
getStatusText(status) {
switch (status) {
@@ -262,7 +291,7 @@ export default {
return status || '未知';
}
},
// 获取状态类型
getStatusType(status) {
switch (status) {
@@ -279,23 +308,23 @@ export default {
return 'default';
}
},
// 预览图片
previewImage(index) {
const urls = this.imageList.map(item => item.url);
const urls = this.imageList.map((item) => item.url);
uni.previewImage({
urls,
current: index
current: index,
});
},
// 编辑走访记录
handleEdit() {
uni.navigateTo({
url: `/pages/visit/edit?id=${this.visitId}`
url: `/pages/visit/edit?id=${this.visitId}`,
});
},
// 删除走访记录
handleDelete() {
uni.showModal({
@@ -305,16 +334,16 @@ export default {
if (res.confirm) {
await this.deleteVisit();
}
}
},
});
},
// 执行删除
async deleteVisit() {
this.loading = true;
try {
const response = await delVisit(this.visitId);
if (response.code === 200) {
uni.showToast({ title: '删除成功', icon: 'success' });
setTimeout(() => {
@@ -329,8 +358,8 @@ export default {
} finally {
this.loading = false;
}
}
}
},
},
};
</script>
@@ -338,6 +367,9 @@ export default {
.visit-detail-container {
min-height: 100vh;
background-color: #f8f8f8;
}
.content-wrapper {
padding: 20rpx;
padding-bottom: 40rpx;
}
@@ -347,16 +379,17 @@ export default {
border-radius: 12rpx;
margin-bottom: 20rpx;
overflow: hidden;
box-shadow: 0 2rpx 12rpx rgba(0, 0, 0, 0.08);
.section-title {
padding: 24rpx 20rpx 16rpx;
font-size: 32rpx;
font-weight: 600;
color: #303133;
border-bottom: 1rpx solid #ebeef5;
background: #f8f9fa;
background: linear-gradient(135deg, #f8f9fa 0%, #ffffff 100%);
}
.section-content {
padding: 20rpx;
}
@@ -366,16 +399,16 @@ export default {
display: flex;
align-items: flex-start;
margin-bottom: 16rpx;
&:last-child {
margin-bottom: 0;
}
&.info-item-full {
flex-direction: column;
align-items: stretch;
}
.info-label {
font-size: 28rpx;
color: #606266;
@@ -383,15 +416,16 @@ export default {
flex-shrink: 0;
margin-bottom: 8rpx;
}
.info-value {
font-size: 28rpx;
color: #303133;
flex: 1;
&.info-value-multiline {
line-height: 1.6;
line-height: 1.8;
word-break: break-all;
color: #606266;
}
}
}
@@ -400,13 +434,19 @@ export default {
display: flex;
flex-wrap: wrap;
gap: 16rpx;
.image-item {
width: 200rpx;
height: 200rpx;
border-radius: 8rpx;
overflow: hidden;
box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.1);
transition: all 0.3s ease;
&:active {
transform: scale(0.95);
}
image {
width: 100%;
height: 100%;
@@ -416,9 +456,11 @@ export default {
.action-section {
display: flex;
gap: 20rpx;
padding: 20rpx;
background: #ffffff;
border-radius: 12rpx;
margin-bottom: 20rpx;
box-shadow: 0 2rpx 12rpx rgba(0, 0, 0, 0.08);
}
</style>
</style>
+276 -251
View File
@@ -1,216 +1,232 @@
<template>
<view class="visit-edit-container">
<!-- 顶部导航 -->
<u-navbar title="编辑走访" :border-bottom="false" bg-color="#3c9cff" title-color="#fff">
<view slot="left" @click="goBack">
<u-icon name="arrow-left" color="#fff" size="22"></u-icon>
</view>
</u-navbar>
<!-- 加载状态 -->
<u-loading-page :loading="pageLoading" loading-text="加载中..."></u-loading-page>
<!-- 表单内容 -->
<u-form
ref="formRef"
:model="form"
:rules="rules"
label-width="180rpx"
label-position="left"
>
<!-- 学生信息展示 -->
<view class="form-section">
<view class="section-title">学生信息</view>
<view class="student-info-section">
<view class="info-row">
<text class="info-label">学生姓名:</text>
<text class="info-value">{{ visitInfo.studentName || '-' }}</text>
</view>
<view class="info-row">
<text class="info-label">学号:</text>
<text class="info-value">{{ visitInfo.studentNo || '-' }}</text>
</view>
<view class="info-row">
<text class="info-label">班级:</text>
<text class="info-value">{{ visitInfo.className || '-' }}</text>
</view>
<view class="info-row">
<text class="info-label">专业:</text>
<text class="info-value">{{ visitInfo.majorName || '-' }}</text>
</view>
<view class="info-row">
<text class="info-label">实习单位:</text>
<text class="info-value">{{ visitInfo.companyName || '-' }}</text>
<view v-if="!pageLoading" class="content-wrapper">
<u-form
ref="formRef"
:model="form"
:rules="rules"
label-width="180rpx"
label-position="left"
>
<!-- 学生信息展示 -->
<view class="form-section">
<view class="section-title">学生信息</view>
<view class="student-info-section">
<view class="info-row">
<text class="info-label">学生姓名:</text>
<text class="info-value">{{ visitInfo.studentName || '-' }}</text>
</view>
<view class="info-row">
<text class="info-label">学号:</text>
<text class="info-value">{{ visitInfo.studentNo || '-' }}</text>
</view>
<view class="info-row">
<text class="info-label">班级:</text>
<text class="info-value">{{ visitInfo.className || '-' }}</text>
</view>
<view class="info-row">
<text class="info-label">专业:</text>
<text class="info-value">{{ visitInfo.majorName || '-' }}</text>
</view>
<view class="info-row">
<text class="info-label">实习单位:</text>
<text class="info-value">{{ visitInfo.companyName || '-' }}</text>
</view>
</view>
</view>
</view>
<!-- 走访基本信息 -->
<view class="form-section">
<view class="section-title">走访信息</view>
<u-form-item label="走访日期" prop="visitDate" required>
<view @click="showVisitDatePicker = true" style="width: 100%">
<u-input
v-model="form.visitDate"
placeholder="请选择走访日期"
readonly
border="none"
suffix-icon="calendar"
suffix-icon-style="color: #c8c9cc;"
style="pointer-events: none"
/>
</view>
</u-form-item>
<u-form-item label="走访人员" prop="visitorName" required>
<u-input
v-model="form.visitorName"
placeholder="请输入走访人员姓名"
border="none"
clearable
/>
</u-form-item>
<u-form-item label="走访人职务" prop="visitorPosition">
<u-input
v-model="form.visitorPosition"
placeholder="请输入走访人员职务"
border="none"
clearable
/>
</u-form-item>
<u-form-item label="走访目的" prop="visitPurpose" required>
<u-textarea
v-model="form.visitPurpose"
placeholder="请输入走访目的"
:maxlength="500"
show-confirm-bar
height="120"
auto-height
/>
</u-form-item>
</view>
<!-- 走访基本信息 -->
<view class="form-section">
<view class="section-title">走访信息</view>
<!-- 走访内容 -->
<view class="form-section">
<view class="section-title">走访内容</view>
<u-form-item label="企业情况" prop="companySituation">
<u-textarea
v-model="form.companySituation"
placeholder="请描述企业基本情况"
:maxlength="1000"
show-confirm-bar
height="120"
auto-height
/>
</u-form-item>
<u-form-item label="学生表现" prop="studentPerformance">
<u-textarea
v-model="form.studentPerformance"
placeholder="请描述学生实习表现"
:maxlength="1000"
show-confirm-bar
height="120"
auto-height
/>
</u-form-item>
<u-form-item label="发现问题" prop="problemsFound">
<u-textarea
v-model="form.problemsFound"
placeholder="请描述发现的问题"
:maxlength="1000"
show-confirm-bar
height="120"
auto-height
/>
</u-form-item>
<u-form-item label="改进建议" prop="suggestions">
<u-textarea
v-model="form.suggestions"
placeholder="请提出改进建议"
:maxlength="1000"
show-confirm-bar
height="120"
auto-height
/>
</u-form-item>
<u-form-item label="走访总结" prop="visitSummary">
<u-textarea
v-model="form.visitSummary"
placeholder="请输入走访总结"
:maxlength="1000"
show-confirm-bar
height="120"
auto-height
/>
</u-form-item>
</view>
<!-- 现场照片 -->
<view class="form-section">
<view class="section-title">现场照片</view>
<view class="upload-section">
<view class="image-list">
<view
class="image-item"
v-for="(image, index) in imageList"
:key="index"
>
<image
:src="image.url"
mode="aspectFill"
@click="previewImage(index)"
<u-form-item label="走访日期" prop="visitDate" required>
<view @click="showVisitDatePicker = true" style="width: 100%">
<u-input
v-model="form.visitDate"
placeholder="请选择走访日期"
readonly
border="none"
suffix-icon="calendar"
suffix-icon-style="color: #c8c9cc;"
style="pointer-events: none"
/>
<view class="delete-btn" @click="removeImage(index)">
<u-icon name="close" size="12" color="#ffffff" />
</view>
</u-form-item>
<u-form-item label="走访人员" prop="visitorName" required>
<u-input
v-model="form.visitorName"
placeholder="请输入走访人员姓名"
border="none"
clearable
/>
</u-form-item>
<u-form-item label="走访人职务" prop="visitorPosition">
<u-input
v-model="form.visitorPosition"
placeholder="请输入走访人员职务"
border="none"
clearable
/>
</u-form-item>
<u-form-item label="走访目的" prop="visitPurpose" required>
<u-textarea
v-model="form.visitPurpose"
placeholder="请输入走访目的"
:maxlength="500"
show-confirm-bar
height="120"
auto-height
/>
</u-form-item>
</view>
<!-- 走访内容 -->
<view class="form-section">
<view class="section-title">走访内容</view>
<u-form-item label="企业情况" prop="companySituation">
<u-textarea
v-model="form.companySituation"
placeholder="请描述企业基本情况"
:maxlength="1000"
show-confirm-bar
height="120"
auto-height
/>
</u-form-item>
<u-form-item label="学生表现" prop="studentPerformance">
<u-textarea
v-model="form.studentPerformance"
placeholder="请描述学生实习表现"
:maxlength="1000"
show-confirm-bar
height="120"
auto-height
/>
</u-form-item>
<u-form-item label="发现问题" prop="problemsFound">
<u-textarea
v-model="form.problemsFound"
placeholder="请描述发现的问题"
:maxlength="1000"
show-confirm-bar
height="120"
auto-height
/>
</u-form-item>
<u-form-item label="改进建议" prop="suggestions">
<u-textarea
v-model="form.suggestions"
placeholder="请提出改进建议"
:maxlength="1000"
show-confirm-bar
height="120"
auto-height
/>
</u-form-item>
<u-form-item label="走访总结" prop="visitSummary">
<u-textarea
v-model="form.visitSummary"
placeholder="请输入走访总结"
:maxlength="1000"
show-confirm-bar
height="120"
auto-height
/>
</u-form-item>
</view>
<!-- 现场照片 -->
<view class="form-section">
<view class="section-title">现场照片</view>
<view class="upload-section">
<view class="image-list">
<view
class="image-item"
v-for="(image, index) in imageList"
:key="index"
>
<image
:src="image.url"
mode="aspectFill"
@click="previewImage(index)"
/>
<view class="delete-btn" @click="removeImage(index)">
<u-icon name="close" size="12" color="#ffffff" />
</view>
</view>
<view
class="upload-btn"
@click="chooseImage"
v-if="imageList.length < maxImageCount"
>
<u-icon name="camera" size="24" color="#c8c9cc" />
<text class="upload-text">添加照片</text>
</view>
</view>
<view
class="upload-btn"
@click="chooseImage"
v-if="imageList.length < maxImageCount"
>
<u-icon name="camera" size="24" color="#c8c9cc" />
<text class="upload-text">添加照片</text>
</view>
<text class="upload-tip">最多上传{{ maxImageCount }}张照片</text>
</view>
<text class="upload-tip">最多上传{{ maxImageCount }}张照片</text>
</view>
</view>
<!-- 备注信息 -->
<view class="form-section">
<view class="section-title">备注信息</view>
<u-form-item label="备注" prop="remark">
<u-textarea
v-model="form.remark"
placeholder="请输入备注信息(可选)"
:maxlength="500"
show-confirm-bar
height="120"
auto-height
/>
</u-form-item>
</view>
</u-form>
<!-- 备注信息 -->
<view class="form-section">
<view class="section-title">备注信息</view>
<u-form-item label="备注" prop="remark">
<u-textarea
v-model="form.remark"
placeholder="请输入备注信息(可选)"
:maxlength="500"
show-confirm-bar
height="120"
auto-height
/>
</u-form-item>
</view>
</u-form>
</view>
<!-- 底部操作按钮 -->
<view class="bottom-actions">
<u-button
type="default"
<view v-if="!pageLoading" class="bottom-actions">
<u-button
type="info"
shape="round"
@click="saveDraft"
:loading="draftLoading"
style="margin-right: 20rpx;"
style="margin-right: 20rpx"
>
<u-icon name="folder" size="16" style="margin-right: 4rpx"></u-icon>
保存草稿
</u-button>
<u-button
type="primary"
<u-button
type="primary"
shape="round"
@click="submitForm"
:loading="submitLoading"
style="flex: 1;"
style="flex: 1"
>
<u-icon name="checkmark-circle" size="16" style="margin-right: 4rpx"></u-icon>
更新走访记录
</u-button>
</view>
@@ -226,11 +242,7 @@
/>
<!-- 加载遮罩 -->
<u-loading-page
:loading="pageLoading"
loading-text="加载中..."
loading-mode="circle"
/>
<u-loading-page :loading="pageLoading" loading-text="加载中..." loading-mode="circle" />
</view>
</template>
@@ -243,13 +255,13 @@ export default {
return {
// 页面参数
visitId: '',
// 页面状态
pageLoading: true,
// 走访信息
visitInfo: {},
// 表单数据
form: {
visitId: '',
@@ -268,37 +280,37 @@ export default {
visitSummary: '',
visitImages: '',
remark: '',
status: ''
status: '',
},
// 表单验证规则
rules: {
visitDate: [
{ required: true, message: '请选择走访日期', trigger: ['blur', 'change'] }
{ required: true, message: '请选择走访日期', trigger: ['blur', 'change'] },
],
visitorName: [
{ required: true, message: '请输入走访人员姓名', trigger: ['blur', 'change'] }
{ required: true, message: '请输入走访人员姓名', trigger: ['blur', 'change'] },
],
visitPurpose: [
{ required: true, message: '请输入走访目的', trigger: ['blur', 'change'] }
]
{ required: true, message: '请输入走访目的', trigger: ['blur', 'change'] },
],
},
// 日期选择
showVisitDatePicker: false,
visitDateValue: Date.now(),
maxDate: Date.now() + 30 * 24 * 60 * 60 * 1000, // 30天后
// 图片上传
imageList: [],
maxImageCount: 10,
// 加载状态
submitLoading: false,
draftLoading: false
draftLoading: false,
};
},
onLoad(options) {
if (options.id) {
this.visitId = options.id;
@@ -308,17 +320,22 @@ export default {
uni.navigateBack();
}
},
methods: {
// 返回上一页
goBack() {
uni.navigateBack();
},
// 加载走访记录详情
async loadVisitDetail() {
this.pageLoading = true;
try {
const response = await getVisit(this.visitId);
if (response.code === 200 && response.data) {
this.visitInfo = response.data;
// 填充表单数据
this.form = {
visitId: response.data.visitId,
@@ -337,9 +354,9 @@ export default {
visitSummary: response.data.visitSummary,
visitImages: response.data.visitImages,
remark: response.data.remark,
status: response.data.status
status: response.data.status,
};
// 处理图片数据
if (response.data.visitImages) {
try {
@@ -349,12 +366,11 @@ export default {
this.imageList = [];
}
}
// 设置日期选择器的值
if (response.data.visitDate) {
this.visitDateValue = new Date(response.data.visitDate).getTime();
}
} else {
uni.showToast({ title: response.msg || '获取数据失败', icon: 'none' });
uni.navigateBack();
@@ -367,14 +383,14 @@ export default {
this.pageLoading = false;
}
},
// 走访日期确认
handleVisitDateConfirm(value) {
const date = new Date(value);
this.form.visitDate = this.formatDate(date);
this.showVisitDatePicker = false;
},
// 格式化日期
formatDate(date) {
const year = date.getFullYear();
@@ -382,7 +398,7 @@ export default {
const day = String(date.getDate()).padStart(2, '0');
return `${year}-${month}-${day}`;
},
// 选择图片
chooseImage() {
const remainCount = this.maxImageCount - this.imageList.length;
@@ -392,24 +408,24 @@ export default {
sourceType: ['album', 'camera'],
success: (res) => {
this.handleImageUpload(res.tempFilePaths);
}
},
});
},
// 处理图片上传
async handleImageUpload(filePaths) {
uni.showLoading({ title: '上传中...' });
try {
for (const filePath of filePaths) {
// 这里应该调用具体的文件上传接口
// 暂时使用本地路径作为示例
this.imageList.push({
url: filePath,
path: filePath
path: filePath,
});
}
// 更新表单中的图片数据
this.form.visitImages = JSON.stringify(this.imageList);
} catch (error) {
@@ -419,33 +435,33 @@ export default {
uni.hideLoading();
}
},
// 预览图片
previewImage(index) {
const urls = this.imageList.map(item => item.url);
const urls = this.imageList.map((item) => item.url);
uni.previewImage({
urls,
current: index
current: index,
});
},
// 删除图片
removeImage(index) {
this.imageList.splice(index, 1);
this.form.visitImages = JSON.stringify(this.imageList);
},
// 保存草稿
async saveDraft() {
this.draftLoading = true;
try {
const formData = {
...this.form,
status: 'planned' // 草稿状态
status: 'planned', // 草稿状态
};
const response = await updateVisit(formData);
if (response.code === 200) {
uni.showToast({ title: '草稿保存成功', icon: 'success' });
setTimeout(() => {
@@ -461,7 +477,7 @@ export default {
this.draftLoading = false;
}
},
// 提交表单
async submitForm() {
// 表单验证
@@ -471,16 +487,16 @@ export default {
console.log('表单验证失败:', error);
return;
}
this.submitLoading = true;
try {
const formData = {
...this.form,
status: 'completed' // 已完成状态
status: 'completed', // 已完成状态
};
const response = await updateVisit(formData);
if (response.code === 200) {
uni.showToast({ title: '更新成功', icon: 'success' });
setTimeout(() => {
@@ -495,8 +511,8 @@ export default {
} finally {
this.submitLoading = false;
}
}
}
},
},
};
</script>
@@ -504,6 +520,9 @@ export default {
.visit-edit-container {
min-height: 100vh;
background-color: #f8f8f8;
}
.content-wrapper {
padding-bottom: 120rpx; // 为底部按钮留出空间
}
@@ -511,7 +530,13 @@ export default {
background: #ffffff;
margin-bottom: 20rpx;
padding: 0 20rpx;
border-radius: 12rpx;
box-shadow: 0 2rpx 12rpx rgba(0, 0, 0, 0.08);
&:first-child {
margin-top: 20rpx;
}
.section-title {
padding: 24rpx 0 16rpx;
font-size: 32rpx;
@@ -527,21 +552,21 @@ export default {
border-radius: 8rpx;
padding: 20rpx;
margin-top: 16rpx;
.info-row {
display: flex;
margin-bottom: 12rpx;
&:last-child {
margin-bottom: 0;
}
.info-label {
font-size: 28rpx;
color: #606266;
width: 140rpx;
}
.info-value {
font-size: 28rpx;
color: #303133;
@@ -556,19 +581,19 @@ export default {
flex-wrap: wrap;
gap: 16rpx;
margin-bottom: 16rpx;
.image-item {
position: relative;
width: 160rpx;
height: 160rpx;
border-radius: 8rpx;
overflow: hidden;
image {
width: 100%;
height: 100%;
}
.delete-btn {
position: absolute;
top: 8rpx;
@@ -582,7 +607,7 @@ export default {
justify-content: center;
}
}
.upload-btn {
width: 160rpx;
height: 160rpx;
@@ -592,7 +617,7 @@ export default {
flex-direction: column;
align-items: center;
justify-content: center;
.upload-text {
font-size: 24rpx;
color: #c8c9cc;
@@ -600,7 +625,7 @@ export default {
}
}
}
.upload-tip {
font-size: 24rpx;
color: #909399;
@@ -619,4 +644,4 @@ export default {
align-items: center;
z-index: 100;
}
</style>
</style>
+247 -177
View File
@@ -1,15 +1,21 @@
<template>
<view class="visit-container">
<!-- 顶部导航 -->
<u-navbar title="班主任走访" :border-bottom="false" bg-color="#3c9cff" title-color="#fff">
<view slot="left" @click="goBack">
<u-icon name="arrow-left" color="#fff" size="22"></u-icon>
</view>
</u-navbar>
<!-- 搜索栏 -->
<view class="search-bar">
<u-search
v-model="searchKeyword"
<u-search
v-model="searchKeyword"
placeholder="搜索学生姓名、学号或企业"
:show-action="false"
@search="handleSearch"
@custom="handleSearch"
bg-color="#f6f6f6"
height="72"
shape="round"
/>
</view>
@@ -47,19 +53,21 @@
</view>
</view>
<view class="filter-actions">
<u-button
type="primary"
size="small"
<u-button
type="primary"
shape="round"
size="small"
@click="handleFilter"
:loading="loading"
>
筛选
</u-button>
<u-button
type="default"
size="small"
<u-button
type="info"
shape="round"
size="small"
@click="resetFilter"
style="margin-left: 10rpx;"
style="margin-left: 10rpx"
>
重置
</u-button>
@@ -68,30 +76,36 @@
<!-- 筛选按钮 -->
<view class="action-bar">
<u-button
type="default"
size="small"
<u-button
type="info"
shape="round"
size="small"
:icon="showFilter ? 'arrow-up' : 'arrow-down'"
@click="showFilter = !showFilter"
>
{{ showFilter ? '收起筛选' : '展开筛选' }}
</u-button>
<u-button
type="primary"
size="small"
icon="plus"
@click="handleAdd"
>
<u-button type="primary" shape="round" size="small" icon="plus" @click="handleAdd">
新增走访
</u-button>
</view>
<!-- 加载状态 -->
<u-loading-page
:loading="loading && visitList.length === 0"
loading-text="加载中..."
></u-loading-page>
<!-- 走访记录列表 -->
<view class="list-container">
<view class="list-content" v-if="visitList.length > 0">
<view
class="visit-card"
v-for="item in visitList"
<view v-if="!loading || visitList.length > 0" class="content-wrapper">
<view v-if="visitList.length === 0" class="empty-container">
<u-empty text="暂无走访记录" mode="data"></u-empty>
</view>
<view class="list-content" v-else>
<view
class="visit-card"
v-for="item in visitList"
:key="item.visitId"
@click="handleView(item)"
>
@@ -102,83 +116,99 @@
<text class="student-no">{{ item.studentNo }}</text>
</view>
<view class="status-badge">
<u-tag
:text="item.statusName || item.status"
<u-tag
:text="getStatusLabel(item.status)"
:type="getStatusType(item.status)"
size="mini"
/>
</view>
</view>
<!-- 企业信息 -->
<view class="card-row">
<u-icon name="home" size="16" color="#909399" />
<text class="info-text">{{ item.companyName || '-' }}</text>
</view>
<!-- 走访信息 -->
<view class="card-row">
<u-icon name="calendar" size="16" color="#909399" />
<text class="info-text">{{ item.visitDate || '-' }}</text>
<u-icon name="account" size="16" color="#909399" style="margin-left: 20rpx;" />
<u-icon
name="account"
size="16"
color="#909399"
style="margin-left: 20rpx"
/>
<text class="info-text">{{ item.visitorName || '-' }}</text>
</view>
<!-- 班级专业信息 -->
<view class="card-row">
<u-icon name="bookmark" size="16" color="#909399" />
<text class="info-text">{{ item.className || '-' }} / {{ item.majorName || '-' }}</text>
<text class="info-text">
{{ item.className || '-' }} / {{ item.majorName || '-' }}
</text>
</view>
<!-- 操作按钮 -->
<view class="card-actions">
<u-button
type="default"
size="mini"
<u-button
type="primary"
shape="round"
size="small"
@click.stop="handleEdit(item)"
v-if="item.status === 'planned' || item.status === '0'"
>
<u-icon
name="edit-pen"
size="14"
color="#fff"
style="margin-right: 4rpx"
></u-icon>
编辑
</u-button>
<u-button
type="error"
size="mini"
<u-button
type="error"
shape="round"
size="small"
@click.stop="handleDelete(item)"
style="margin-left: 10rpx;"
style="margin-left: 10rpx"
v-if="item.status === 'planned' || item.status === '0'"
>
<u-icon
name="trash"
size="14"
color="#fff"
style="margin-right: 4rpx"
></u-icon>
删除
</u-button>
<u-button
type="info"
shape="round"
size="small"
plain
@click.stop="handleView(item)"
v-else
>
<u-icon name="eye" size="14" style="margin-right: 4rpx"></u-icon>
查看
</u-button>
</view>
</view>
</view>
<!-- 空状态 -->
<view class="empty-state" v-else-if="!loading">
<u-empty
mode="data"
text="暂无走访记录"
textColor="#c8c9cc"
iconColor="#c8c9cc"
<!-- 上拉加载更多 -->
<view class="load-more" v-if="hasMore">
<u-loadmore
:status="loadMoreStatus"
@loadmore="loadMore"
load-text="加载更多"
loading-text="正在加载..."
no-more-text="没有更多了"
/>
</view>
<!-- 加载状态 -->
<view class="loading-state" v-if="loading">
<u-loading-icon mode="circle" />
<text style="margin-left: 10rpx; color: #909399;">加载中...</text>
</view>
</view>
<!-- 上拉加载更多 -->
<view class="load-more" v-if="hasMore && visitList.length > 0">
<u-loadmore
:status="loadMoreStatus"
@loadmore="loadMore"
load-text="加载更多"
loading-text="正在加载..."
no-more-text="没有更多了"
/>
</view>
<!-- 状态选择器 -->
@@ -218,51 +248,56 @@ export default {
visitorName: '',
status: '',
beginVisitDate: '',
endVisitDate: ''
endVisitDate: '',
},
// 列表数据
visitList: [],
total: 0,
loading: false,
hasMore: true,
loadMoreStatus: 'loadmore',
// 状态选择
showStatusPicker: false,
statusOptions: [],
currentStatusIndex: -1,
statusText: '',
statusDict: [],
// 日期选择
showDatePicker: false,
dateRangeText: '',
selectedDateRange: []
selectedDateRange: [],
};
},
onLoad() {
this.initPage();
},
onPullDownRefresh() {
this.refreshList();
},
onReachBottom() {
if (this.hasMore && this.loadMoreStatus === 'loadmore') {
this.loadMore();
}
},
methods: {
// 返回上一页
goBack() {
uni.navigateBack();
},
// 初始化页面
async initPage() {
await this.loadStatusDict();
this.getList();
},
// 加载状态字典
async loadStatusDict() {
try {
@@ -272,55 +307,57 @@ export default {
this.statusOptions = [
[
{ text: '全部状态', value: '' },
...response.data.map(item => ({
...response.data.map((item) => ({
text: item.dictLabel,
value: item.dictValue
}))
]
value: item.dictValue,
})),
],
];
}
} catch (error) {
console.error('加载状态字典失败:', error);
// 设置默认状态选项
this.statusOptions = [[
{ text: '全部状态', value: '' },
{ text: '计划中', value: 'planned' },
{ text: '已完成', value: 'completed' },
{ text: '已取消', value: 'cancelled' }
]];
this.statusOptions = [
[
{ text: '全部状态', value: '' },
{ text: '计划中', value: 'planned' },
{ text: '已完成', value: 'completed' },
{ text: '已取消', value: 'cancelled' },
],
];
}
},
// 获取列表数据
async getList(isLoadMore = false) {
if (this.loading) return;
this.loading = true;
if (!isLoadMore) {
this.loadMoreStatus = 'loadmore';
} else {
this.loadMoreStatus = 'loading';
}
try {
const response = await listVisit(this.queryParams);
if (response.code === 200) {
// 后端返回的是TableDataInfo格式,直接包含rows和total字段
const { rows, total } = response;
if (isLoadMore) {
this.visitList = [...this.visitList, ...rows || []];
this.visitList = [...this.visitList, ...(rows || [])];
} else {
this.visitList = rows || [];
}
this.total = total || 0;
// 判断是否还有更多数据
const currentTotal = this.visitList.length;
this.hasMore = currentTotal < this.total;
if (!this.hasMore) {
this.loadMoreStatus = 'nomore';
}
@@ -337,13 +374,13 @@ export default {
}
}
},
// 刷新列表
refreshList() {
this.queryParams.pageNum = 1;
this.getList();
},
// 加载更多
loadMore() {
if (this.hasMore && !this.loading) {
@@ -351,18 +388,18 @@ export default {
this.getList(true);
}
},
// 搜索
handleSearch() {
this.queryParams.studentKeyword = this.searchKeyword;
this.refreshList();
},
// 筛选
handleFilter() {
this.refreshList();
},
// 重置筛选
resetFilter() {
this.queryParams = {
@@ -372,7 +409,7 @@ export default {
visitorName: '',
status: '',
beginVisitDate: '',
endVisitDate: ''
endVisitDate: '',
};
this.statusText = '';
this.dateRangeText = '';
@@ -380,7 +417,7 @@ export default {
this.currentStatusIndex = -1;
this.refreshList();
},
// 状态选择确认
handleStatusConfirm(value) {
const selected = value.value[0];
@@ -389,7 +426,7 @@ export default {
this.currentStatusIndex = value.index[0];
this.showStatusPicker = false;
},
// 日期选择确认
handleDateConfirm(value) {
this.selectedDateRange = value;
@@ -404,7 +441,33 @@ export default {
}
this.showDatePicker = false;
},
// 获取状态标签文本
getStatusLabel(status) {
// 如果statusDict字典已加载,从字典中查找
if (this.statusDict && this.statusDict.length > 0) {
const dictItem = this.statusDict.find((item) => item.dictValue === String(status));
if (dictItem) {
return dictItem.dictLabel;
}
}
// 如果字典未加载或找不到,使用默认映射
switch (String(status)) {
case 'planned':
case '0':
return '计划中';
case 'completed':
case '1':
return '已完成';
case 'cancelled':
case '2':
return '已取消';
default:
return status || '未知';
}
},
// 获取状态类型
getStatusType(status) {
switch (status) {
@@ -421,28 +484,28 @@ export default {
return 'default';
}
},
// 查看详情
handleView(item) {
uni.navigateTo({
url: `/pages/visit/detail?id=${item.visitId}`
url: `/pages/visit/detail?id=${item.visitId}`,
});
},
// 新增走访
handleAdd() {
uni.navigateTo({
url: '/pages/visit/add'
url: '/pages/visit/add',
});
},
// 编辑走访
handleEdit(item) {
uni.navigateTo({
url: `/pages/visit/edit?id=${item.visitId}`
url: `/pages/visit/edit?id=${item.visitId}`,
});
},
// 删除走访
handleDelete(item) {
uni.showModal({
@@ -452,16 +515,16 @@ export default {
if (res.confirm) {
await this.deleteVisit(item.visitId);
}
}
},
});
},
// 执行删除
async deleteVisit(id) {
try {
uni.showLoading({ title: '删除中...' });
const response = await delVisit(id);
if (response.code === 200) {
uni.showToast({ title: '删除成功', icon: 'success' });
this.refreshList();
@@ -474,8 +537,8 @@ export default {
} finally {
uni.hideLoading();
}
}
}
},
},
};
</script>
@@ -494,11 +557,11 @@ export default {
background: #ffffff;
padding: 0 20rpx 20rpx;
border-bottom: 1rpx solid #ebeef5;
.filter-item {
margin-bottom: 20rpx;
}
.filter-actions {
display: flex;
justify-content: flex-end;
@@ -515,76 +578,83 @@ export default {
border-bottom: 1rpx solid #ebeef5;
}
.list-container {
.content-wrapper {
padding: 20rpx;
}
.visit-card {
background: #ffffff;
border-radius: 12rpx;
padding: 24rpx;
margin-bottom: 20rpx;
box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.06);
.card-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 16rpx;
.student-info {
.student-name {
font-size: 32rpx;
font-weight: 600;
color: #303133;
margin-right: 16rpx;
}
.student-no {
font-size: 26rpx;
color: #909399;
}
}
}
.card-row {
display: flex;
align-items: center;
margin-bottom: 12rpx;
.info-text {
font-size: 28rpx;
color: #606266;
margin-left: 8rpx;
}
&:last-of-type {
margin-bottom: 16rpx;
}
}
.card-actions {
display: flex;
justify-content: flex-end;
gap: 10rpx;
padding-top: 16rpx;
border-top: 1rpx solid #ebeef5;
}
}
.empty-state {
.empty-container {
padding: 80rpx 20rpx;
text-align: center;
}
.loading-state {
display: flex;
align-items: center;
justify-content: center;
padding: 40rpx;
.list-content {
.visit-card {
background: #ffffff;
border-radius: 12rpx;
padding: 24rpx;
margin-bottom: 20rpx;
box-shadow: 0 2rpx 12rpx rgba(0, 0, 0, 0.08);
transition: all 0.3s ease;
&:active {
transform: scale(0.99);
box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.06);
}
.card-header {
display: flex;
justify-content: space-between;
align-items: flex-start;
margin-bottom: 16rpx;
.student-info {
flex: 1;
.student-name {
font-size: 32rpx;
font-weight: 600;
color: #303133;
margin-right: 16rpx;
}
.student-no {
font-size: 26rpx;
color: #909399;
}
}
.status-badge {
flex-shrink: 0;
}
}
.card-row {
display: flex;
align-items: center;
margin-bottom: 12rpx;
.info-text {
font-size: 28rpx;
color: #606266;
margin-left: 8rpx;
}
&:last-of-type {
margin-bottom: 16rpx;
}
}
.card-actions {
display: flex;
justify-content: flex-end;
gap: 10rpx;
padding-top: 16rpx;
border-top: 1rpx solid #ebeef5;
}
}
}
.load-more {
padding: 20rpx;
padding: 20rpx 0;
}
</style>
+118 -95
View File
@@ -1,11 +1,18 @@
<template>
<view class="detail-container">
<!-- 顶部导航 -->
<u-navbar title="申请详情" :border-bottom="false" bg-color="#0051d9" title-color="#fff">
<view slot="left" @click="goBack">
<u-icon name="arrow-left" color="#fff" size="22"></u-icon>
</view>
</u-navbar>
<!-- 申请状态卡片 -->
<view class="status-card" v-if="applicationData">
<view class="card-title">申请状态</view>
<view class="status-info">
<view class="main-status">
<u-tag
<u-tag
:text="getApplicationStatusText(applicationData.status)"
:type="getApplicationStatusType(applicationData.status)"
size="large"
@@ -17,7 +24,10 @@
<text class="status-label">申请时间:</text>
<text class="status-value">{{ applicationData.createTime }}</text>
</view>
<view class="status-item" v-if="applicationData.updateTime !== applicationData.createTime">
<view
class="status-item"
v-if="applicationData.updateTime !== applicationData.createTime"
>
<text class="status-label">更新时间:</text>
<text class="status-value">{{ applicationData.updateTime }}</text>
</view>
@@ -32,11 +42,11 @@
<view class="position-name">{{ applicationData.positionName }}</view>
<view class="position-detail">
<view class="detail-item">
<u-icon name="list" size="28" color="#666" />
<u-icon name="list" size="18" color="#666" />
<text>岗位类型:{{ applicationData.positionType }}</text>
</view>
<view class="detail-item">
<u-icon name="map" size="28" color="#666" />
<u-icon name="map" size="18" color="#666" />
<text>工作地点:{{ applicationData.workLocation }}</text>
</view>
</view>
@@ -61,7 +71,9 @@
</view>
<view class="info-item">
<text class="info-label">联系电话:</text>
<text class="info-value contact-phone" @click="callPhone">{{ applicationData.contactPhone }}</text>
<text class="info-value contact-phone" @click="callPhone">
{{ applicationData.contactPhone }}
</text>
</view>
<view class="info-item">
<text class="info-label">学院:</text>
@@ -124,12 +136,12 @@
<!-- 审核记录 -->
<view class="review-card" v-if="hasReviewRecord">
<view class="card-title">审核记录</view>
<!-- 学院审核 -->
<view class="review-item" v-if="applicationData.collegeReviewStatus">
<view class="review-header">
<view class="review-title">学院审核</view>
<u-tag
<u-tag
:text="getReviewStatusText(applicationData.collegeReviewStatus)"
:type="getReviewStatusType(applicationData.collegeReviewStatus)"
size="mini"
@@ -156,7 +168,7 @@
<view class="review-item" v-if="applicationData.finalReviewStatus">
<view class="review-header">
<view class="review-title">学生处审核</view>
<u-tag
<u-tag
:text="getReviewStatusText(applicationData.finalReviewStatus)"
:type="getReviewStatusType(applicationData.finalReviewStatus)"
size="mini"
@@ -182,18 +194,18 @@
<!-- 底部操作按钮 -->
<view class="bottom-actions" v-if="showActions">
<u-button
type="warning"
size="large"
<u-button
type="warning"
size="large"
@click="editApplication"
v-if="canEdit"
style="margin-right: 20rpx;"
style="margin-right: 20rpx"
>
编辑申请
</u-button>
<u-button
type="error"
size="large"
<u-button
type="error"
size="large"
@click="cancelApplication"
v-if="canCancel"
:loading="canceling"
@@ -208,10 +220,7 @@
</template>
<script>
import {
getApplicationDetail,
cancelApplication
} from '@/api/workStudy.js';
import { getApplicationDetail, cancelApplication } from '@/api/workStudy.js';
export default {
name: 'ApplicationDetail',
@@ -220,35 +229,41 @@ export default {
applicationId: '',
applicationData: null,
loading: false,
canceling: false
canceling: false,
};
},
computed: {
// 是否有审核记录
hasReviewRecord() {
return this.applicationData &&
(this.applicationData.collegeReviewStatus || this.applicationData.finalReviewStatus);
return (
this.applicationData &&
(this.applicationData.collegeReviewStatus || this.applicationData.finalReviewStatus)
);
},
// 是否显示操作按钮
showActions() {
return this.canEdit || this.canCancel;
},
// 是否可以编辑
canEdit() {
return this.applicationData &&
['DRAFT', 'COLLEGE_REJECTED'].includes(this.applicationData.status);
return (
this.applicationData &&
['DRAFT', 'COLLEGE_REJECTED'].includes(this.applicationData.status)
);
},
// 是否可以撤销
canCancel() {
return this.applicationData &&
['DRAFT', 'SUBMITTED', 'COLLEGE_REVIEWING'].includes(this.applicationData.status);
}
return (
this.applicationData &&
['DRAFT', 'SUBMITTED', 'COLLEGE_REVIEWING'].includes(this.applicationData.status)
);
},
},
onLoad(options) {
if (options.id) {
this.applicationId = options.id;
@@ -256,22 +271,27 @@ export default {
} else {
uni.showToast({
title: '申请ID不能为空',
icon: 'none'
icon: 'none',
});
setTimeout(() => {
uni.navigateBack();
}, 1500);
}
},
methods: {
// 返回上一页
goBack() {
uni.navigateBack();
},
// 加载申请详情
async loadApplicationDetail() {
this.loading = true;
try {
const response = await getApplicationDetail(this.applicationId);
if (response.code === 200) {
this.applicationData = response.data;
} else {
@@ -281,9 +301,9 @@ export default {
console.error('加载申请详情失败:', error);
uni.showToast({
title: error.message || '加载失败',
icon: 'none'
icon: 'none',
});
setTimeout(() => {
uni.navigateBack();
}, 1500);
@@ -291,36 +311,36 @@ export default {
this.loading = false;
}
},
// 编辑申请
editApplication() {
if (!this.applicationData) return;
uni.navigateTo({
url: `/pages/workStudy/apply?positionId=${this.applicationData.positionId}&applicationId=${this.applicationData.id}&mode=edit`
url: `/pages/workStudy/apply?positionId=${this.applicationData.positionId}&applicationId=${this.applicationData.id}&mode=edit`,
});
},
// 撤销申请
async cancelApplication() {
try {
const { confirm } = await uni.showModal({
title: '确认撤销',
content: '确定要撤销此申请吗?',
showCancel: true
showCancel: true,
});
if (!confirm) return; // 用户取消
this.canceling = true;
const response = await cancelApplication(this.applicationData.id);
if (response.code === 200) {
uni.showToast({
title: '撤销成功',
icon: 'success'
icon: 'success',
});
// 重新加载数据
setTimeout(() => {
this.loadApplicationDetail();
@@ -332,13 +352,13 @@ export default {
console.error('撤销申请失败:', error);
uni.showToast({
title: error.message || '撤销失败',
icon: 'none'
icon: 'none',
});
} finally {
this.canceling = false;
}
},
// 拨打电话
callPhone() {
if (this.applicationData && this.applicationData.contactPhone) {
@@ -348,96 +368,99 @@ export default {
console.error('拨打电话失败:', error);
uni.showToast({
title: '拨打电话失败',
icon: 'none'
icon: 'none',
});
}
},
});
}
},
// 格式化身份证号(中间位隐藏)
formatIdCard(idCard) {
if (!idCard || idCard.length < 10) return idCard;
return idCard.substring(0, 6) + '********' + idCard.substring(idCard.length - 4);
},
// 获取申请状态文字
getApplicationStatusText(status) {
const statusMap = {
'DRAFT': '草稿',
'SUBMITTED': '已提交',
'COLLEGE_REVIEWING': '学院审核中',
'COLLEGE_APPROVED': '学院审核通过',
'COLLEGE_REJECTED': '学院审核未通过',
'FINAL_REVIEWING': '学生处审核中',
'FINAL_APPROVED': '审核通过',
'FINAL_REJECTED': '审核未通过',
'CANCELLED': '已撤销'
DRAFT: '草稿',
SUBMITTED: '已提交',
COLLEGE_REVIEWING: '学院审核中',
COLLEGE_APPROVED: '学院审核通过',
COLLEGE_REJECTED: '学院审核未通过',
FINAL_REVIEWING: '学生处审核中',
FINAL_APPROVED: '审核通过',
FINAL_REJECTED: '审核未通过',
CANCELLED: '已撤销',
};
return statusMap[status] || '未知状态';
},
// 获取申请状态类型
getApplicationStatusType(status) {
const typeMap = {
'DRAFT': 'info',
'SUBMITTED': 'warning',
'COLLEGE_REVIEWING': 'warning',
'COLLEGE_APPROVED': 'success',
'COLLEGE_REJECTED': 'error',
'FINAL_REVIEWING': 'warning',
'FINAL_APPROVED': 'success',
'FINAL_REJECTED': 'error',
'CANCELLED': 'info'
DRAFT: 'info',
SUBMITTED: 'warning',
COLLEGE_REVIEWING: 'warning',
COLLEGE_APPROVED: 'success',
COLLEGE_REJECTED: 'error',
FINAL_REVIEWING: 'warning',
FINAL_APPROVED: 'success',
FINAL_REJECTED: 'error',
CANCELLED: 'info',
};
return typeMap[status] || 'default';
},
// 获取审核状态文字
getReviewStatusText(status) {
const statusMap = {
'PENDING': '待审核',
'APPROVED': '通过',
'REJECTED': '未通过'
PENDING: '待审核',
APPROVED: '通过',
REJECTED: '未通过',
};
return statusMap[status] || '未知';
},
// 获取审核状态类型
getReviewStatusType(status) {
const typeMap = {
'PENDING': 'warning',
'APPROVED': 'success',
'REJECTED': 'error'
PENDING: 'warning',
APPROVED: 'success',
REJECTED: 'error',
};
return typeMap[status] || 'default';
}
}
},
},
};
</script>
<style scoped>
.detail-container {
min-height: 100vh;
background-color: #f5f5f5;
background-color: #f5f6fa;
padding-bottom: 120rpx; /* 为底部按钮留空间 */
}
.status-card, .info-card, .content-card, .review-card {
.status-card,
.info-card,
.content-card,
.review-card {
background-color: #fff;
margin: 20rpx;
border-radius: 12rpx;
border-radius: 16rpx;
padding: 30rpx;
box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.1);
box-shadow: 0 2rpx 12rpx rgba(0, 0, 0, 0.08);
}
.card-title {
font-size: 32rpx;
font-weight: bold;
color: #333;
color: #303133;
margin-bottom: 24rpx;
padding-bottom: 16rpx;
border-bottom: 1rpx solid #f0f0f0;
border-bottom: 1px solid #f0f0f0;
}
/* 状态卡片 */
@@ -463,12 +486,12 @@ export default {
.status-label {
font-size: 28rpx;
color: #666;
color: #606266;
}
.status-value {
font-size: 28rpx;
color: #333;
color: #303133;
}
/* 岗位信息 */
@@ -516,14 +539,14 @@ export default {
.info-label {
font-size: 28rpx;
color: #666;
color: #606266;
min-width: 160rpx;
flex-shrink: 0;
}
.info-value {
font-size: 28rpx;
color: #333;
color: #303133;
flex: 1;
line-height: 1.5;
}
@@ -536,7 +559,7 @@ export default {
/* 内容卡片 */
.content-text {
font-size: 28rpx;
color: #666;
color: #606266;
line-height: 1.8;
white-space: pre-wrap;
}
@@ -563,7 +586,7 @@ export default {
.review-title {
font-size: 30rpx;
font-weight: 500;
color: #333;
color: #303133;
}
.review-info {
+236 -209
View File
@@ -1,5 +1,12 @@
<template>
<view class="apply-container">
<!-- 顶部导航 -->
<u-navbar title="申请岗位" :border-bottom="false" bg-color="#0051d9" title-color="#fff">
<view slot="left" @click="goBack">
<u-icon name="arrow-left" color="#fff" size="22"></u-icon>
</view>
</u-navbar>
<!-- 岗位信息卡片 -->
<view class="position-card" v-if="positionData">
<view class="card-title">申请岗位</view>
@@ -14,59 +21,38 @@
<!-- 申请表单 -->
<view class="form-container">
<u-form
:model="formData"
ref="form"
label-width="140"
label-position="left"
>
<u-form :model="formData" ref="form" label-width="140" label-position="left">
<!-- 基本信息 -->
<view class="form-section">
<view class="section-title">基本信息</view>
<u-form-item
label="学生姓名"
prop="studentName"
:required="true"
>
<u-input
v-model="formData.studentName"
<u-form-item label="学生姓名" prop="studentName" :required="true">
<u-input
v-model="formData.studentName"
placeholder="请输入学生姓名"
:disabled="true"
/>
</u-form-item>
<u-form-item
label="学号"
prop="studentNumber"
:required="true"
>
<u-input
v-model="formData.studentNumber"
<u-form-item label="学号" prop="studentNumber" :required="true">
<u-input
v-model="formData.studentNumber"
placeholder="请输入学号"
:disabled="true"
/>
</u-form-item>
<u-form-item
label="身份证号"
prop="idCard"
:required="true"
>
<u-input
v-model="formData.idCard"
<u-form-item label="身份证号" prop="idCard" :required="true">
<u-input
v-model="formData.idCard"
placeholder="请输入身份证号"
maxlength="18"
/>
</u-form-item>
<u-form-item
label="联系电话"
prop="contactPhone"
:required="true"
>
<u-input
v-model="formData.contactPhone"
<u-form-item label="联系电话" prop="contactPhone" :required="true">
<u-input
v-model="formData.contactPhone"
placeholder="请输入联系电话"
maxlength="11"
/>
@@ -76,133 +62,109 @@
<!-- 学籍信息 -->
<view class="form-section">
<view class="section-title">学籍信息</view>
<u-form-item
label="学院名称"
prop="collegeName"
:required="true"
>
<u-input
v-model="formData.collegeName"
<u-form-item label="学院名称" prop="collegeName" :required="true">
<u-input
v-model="formData.collegeName"
placeholder="学院名称"
:disabled="true"
/>
</u-form-item>
<u-form-item
label="专业名称"
prop="majorName"
:required="true"
>
<u-input
v-model="formData.majorName"
<u-form-item label="专业名称" prop="majorName" :required="true">
<u-input
v-model="formData.majorName"
placeholder="专业名称"
:disabled="true"
/>
</u-form-item>
<u-form-item
label="班级名称"
prop="className"
:required="true"
>
<u-input
v-model="formData.className"
<u-form-item label="班级名称" prop="className" :required="true">
<u-input
v-model="formData.className"
placeholder="班级名称"
:disabled="true"
/>
</u-form-item>
</view>
<!-- 申请信息 -->
<view class="form-section">
<view class="section-title">申请信息</view>
<u-form-item
label="家庭经济情况"
prop="familyFinancialStatus"
:required="true"
>
<u-radio-group v-model="formData.familyFinancialStatus">
<u-radio
v-for="option in familyStatusOptions"
:key="option.value"
:name="option.value"
:label="option.label"
style="margin-right: 20rpx; margin-bottom: 15rpx; font-size: 28rpx;"
/>
</u-radio-group>
<u-form-item label="家庭经济情况" prop="familyFinancialStatus" :required="true">
<u-picker
:columns="familyStatusColumns"
v-model="familyStatusIndex"
:show="showFamilyStatusPicker"
@confirm="onFamilyStatusConfirm"
@close="showFamilyStatusPicker = false"
></u-picker>
<view class="picker-input" @click="showFamilyStatusPicker = true">
<text
class="picker-text"
:class="{ placeholder: !formData.familyFinancialStatus }"
>
{{ formData.familyFinancialStatus || '请选择家庭经济情况' }}
</text>
<u-icon name="arrow-right" size="14" color="#c8c9cc"></u-icon>
</view>
</u-form-item>
<u-form-item
label="计划工作时长"
prop="plannedWorkHours"
>
<u-input
v-model="formData.plannedWorkHours"
<u-form-item label="计划工作时长" prop="plannedWorkHours">
<u-input
v-model="formData.plannedWorkHours"
placeholder="请输入计划工作时长(小时/周)"
type="number"
placeholder-style="font-size: 24rpx"
/>
</u-form-item>
<u-form-item
label="申请理由"
prop="applicationReason"
:required="true"
>
<u-textarea
v-model="formData.applicationReason"
<u-form-item label="申请理由" prop="applicationReason" :required="true">
<u-textarea
v-model="formData.applicationReason"
placeholder="请详细说明申请此岗位的理由"
:maxlength="500"
:show-word-limit="true"
height="200"
count
placeholder-style="font-size: 24rpx"
/>
</u-form-item>
<u-form-item
label="个人能力及经验"
prop="personalAbility"
:required="true"
>
<u-textarea
v-model="formData.personalAbility"
<u-form-item label="个人能力及经验" prop="personalAbility" :required="true">
<u-textarea
v-model="formData.personalAbility"
placeholder="请描述与岗位相关的个人能力和经验"
:maxlength="500"
:show-word-limit="true"
height="200"
count
placeholder-style="font-size: 24rpx"
/>
</u-form-item>
<u-form-item
label="时间安排"
prop="timeArrangement"
:required="true"
>
<u-textarea
v-model="formData.timeArrangement"
<u-form-item label="时间安排" prop="timeArrangement" :required="true">
<u-textarea
v-model="formData.timeArrangement"
placeholder="请说明您的时间安排,确保能够胜任此岗位工作"
:maxlength="300"
:show-word-limit="true"
height="150"
count
placeholder-style="font-size: 24rpx"
/>
</u-form-item>
<u-form-item
label="备注"
prop="remark"
>
<u-textarea
v-model="formData.remark"
<u-form-item label="备注" prop="remark">
<u-textarea
v-model="formData.remark"
placeholder="其他需要说明的情况(选填)"
:maxlength="200"
:show-word-limit="true"
height="120"
count
placeholder-style="font-size: 24rpx"
/>
</u-form-item>
</view>
@@ -211,42 +173,34 @@
<!-- 底部操作按钮 -->
<view class="bottom-actions">
<u-button
type="default"
size="large"
<u-button
plain
type="info"
size="large"
@click="saveDraft"
:loading="saving"
style="margin-right: 20rpx;"
:custom-style="draftButtonStyle"
>
保存草稿
</u-button>
<u-button
type="primary"
size="large"
@click="submitApplication"
:loading="submitting"
>
<u-button type="primary" size="large" @click="submitApplication" :loading="submitting">
提交申请
</u-button>
</view>
<!-- 加载状态 -->
<u-loading-page :loading="loading" loading-text="加载中..." />
</view>
</template>
<script>
import {
import {
getPositionWithApplicationStatus,
getCurrentStudentInfo,
getApplicationOptions,
addApplication,
updateApplication,
getApplicationDetail
getApplicationDetail,
} from '@/api/workStudy.js';
export default {
@@ -258,7 +212,7 @@ export default {
mode: 'create', // create 或 edit
positionData: null,
studentInfo: null,
// 表单数据
formData: {
studentName: '',
@@ -274,69 +228,118 @@ export default {
applicationReason: '',
personalAbility: '',
timeArrangement: '',
remark: ''
remark: '',
},
// 家庭经济情况选项(与web端保持一致)
familyStatusOptions: [
{ value: '特困', label: '特困' },
{ value: '贫困', label: '贫困' },
{ value: '一般', label: '一般' },
{ value: '良好', label: '良好' }
{ value: '良好', label: '良好' },
],
// 家庭经济情况选择器相关
showFamilyStatusPicker: false,
familyStatusIndex: [0],
// uview-ui 2.x 单列选择器:使用纯字符串数组(二维)最稳定
// 格式:[[字符串1, 字符串2, ...]] - 外层数组表示列,内层数组表示选项
familyStatusColumns: [['特困', '贫困', '一般', '良好']],
// 加载状态
loading: false,
saving: false,
submitting: false
submitting: false,
// 按钮样式
draftButtonStyle: {
marginRight: '20rpx',
},
};
},
onLoad(options) {
if (options.positionId) {
this.positionId = options.positionId;
this.applicationId = options.applicationId || '';
this.mode = options.mode || 'create';
this.initData();
} else {
uni.showToast({
title: '岗位ID不能为空',
icon: 'none'
icon: 'none',
});
setTimeout(() => {
uni.navigateBack();
}, 1500);
}
},
methods: {
// 返回上一页
goBack() {
uni.navigateBack();
},
// 家庭经济情况选择器确认
// uview-ui 2.x Picker 组件 - 纯字符串数组模式
// confirm 事件返回:{ value: [选中值1, 选中值2, ...], indexs: [索引1, 索引2, ...] }
// 单列选择器:value 是 ['贫困'],indexs 是 [1]
onFamilyStatusConfirm(e) {
console.log('=== 家庭经济情况选择器确认 ===');
console.log('完整事件参数:', e);
console.log('e.value:', e.value); // 例如:['贫困']
console.log('e.indexs:', e.indexs); // 例如:[1]
// 单列选择器,取第一列的值
if (e && e.value && Array.isArray(e.value) && e.value.length > 0) {
const selectedValue = e.value[0]; // 直接获取字符串,如 "贫困"
console.log('选中的值:', selectedValue);
// 直接赋值
this.formData.familyFinancialStatus = selectedValue;
// 更新索引用于回显
if (e.indexs && Array.isArray(e.indexs)) {
this.familyStatusIndex = e.indexs;
console.log('更新索引为:', this.familyStatusIndex);
}
console.log(
'✅ 更新成功,familyFinancialStatus =',
this.formData.familyFinancialStatus,
);
} else {
console.error('❌ 无效的选择器返回值:', e);
}
this.showFamilyStatusPicker = false;
console.log('=== 选择器关闭 ===\n');
},
// 初始化数据
async initData() {
this.loading = true;
try {
// 并行加载数据
const promises = [
this.loadPositionData(),
this.loadStudentInfo()
];
const promises = [this.loadPositionData(), this.loadStudentInfo()];
await Promise.all(promises);
// 如果是编辑模式,加载申请详情
if (this.mode === 'edit' && this.applicationId) {
await this.loadApplicationDetail();
}
// 填充学生基本信息
this.fillStudentInfo();
} catch (error) {
console.error('初始化数据失败:', error);
uni.showToast({
title: '加载数据失败',
icon: 'none'
icon: 'none',
});
setTimeout(() => {
uni.navigateBack();
@@ -345,7 +348,7 @@ export default {
this.loading = false;
}
},
// 加载岗位数据
async loadPositionData() {
const response = await getPositionWithApplicationStatus(this.positionId);
@@ -355,7 +358,7 @@ export default {
throw new Error(response.msg || '获取岗位信息失败');
}
},
// 加载学生信息
async loadStudentInfo() {
const response = await getCurrentStudentInfo();
@@ -365,43 +368,40 @@ export default {
throw new Error(response.msg || '获取学生信息失败');
}
},
// 加载申请详情(编辑模式)
async loadApplicationDetail() {
const response = await getApplicationDetail(this.applicationId);
if (response.code === 200) {
const data = response.data;
// 填充表单数据
Object.keys(this.formData).forEach(key => {
Object.keys(this.formData).forEach((key) => {
if (data[key] !== undefined) {
this.formData[key] = data[key];
}
});
}
},
// 填充学生基本信息
fillStudentInfo() {
if (this.studentInfo) {
// 修改字段映射以匹配后端返回的字段名
this.formData.studentName = this.studentInfo.name || ''; // 修正:name
this.formData.studentNumber = this.studentInfo.studentNo || ''; // 修正:studentNo
this.formData.studentName = this.studentInfo.name || ''; // 修正:name
this.formData.studentNumber = this.studentInfo.studentNo || ''; // 修正:studentNo
this.formData.collegeName = this.studentInfo.collegeName || '';
this.formData.majorName = this.studentInfo.majorName || '';
this.formData.className = this.studentInfo.className || '';
// 如果是创建模式,填充其他基本信息
if (this.mode === 'create') {
this.formData.idCard = this.studentInfo.idCard || '';
this.formData.contactPhone = this.studentInfo.phone || ''; // 修正:phone
this.formData.contactPhone = this.studentInfo.phone || ''; // 修正:phone
}
}
},
// 表单验证
validateForm() {
const requiredFields = [
@@ -415,54 +415,59 @@ export default {
{ field: 'familyFinancialStatus', name: '家庭经济情况' },
{ field: 'applicationReason', name: '申请理由' },
{ field: 'personalAbility', name: '个人能力及经验' },
{ field: 'timeArrangement', name: '时间安排' }
{ field: 'timeArrangement', name: '时间安排' },
];
for (const item of requiredFields) {
if (!this.formData[item.field] || this.formData[item.field].trim() === '') {
uni.showToast({
title: `请填写${item.name}`,
icon: 'none'
icon: 'none',
});
return false;
}
}
// 验证身份证号格式
if (this.formData.idCard && !/^[1-9]\d{5}(18|19|20)\d{2}((0[1-9])|(1[0-2]))(([0-2][1-9])|10|20|30|31)\d{3}[0-9Xx]$/.test(this.formData.idCard)) {
if (
this.formData.idCard &&
!/^[1-9]\d{5}(18|19|20)\d{2}((0[1-9])|(1[0-2]))(([0-2][1-9])|10|20|30|31)\d{3}[0-9Xx]$/.test(
this.formData.idCard,
)
) {
uni.showToast({
title: '身份证号格式不正确',
icon: 'none'
icon: 'none',
});
return false;
}
// 验证手机号格式
if (this.formData.contactPhone && !/^1[3-9]\d{9}$/.test(this.formData.contactPhone)) {
uni.showToast({
title: '手机号格式不正确',
icon: 'none'
icon: 'none',
});
return false;
}
return true;
},
// 保存草稿
async saveDraft(autoNavigate = true) {
if (!this.validateForm()) return false;
this.saving = true;
try {
const requestData = {
...this.formData,
positionId: this.positionId,
positionName: this.positionData.positionName,
positionType: this.positionData.positionType || ''
positionType: this.positionData.positionType || '',
};
let response;
if (this.mode === 'edit' && this.applicationId) {
requestData.id = this.applicationId;
@@ -470,13 +475,13 @@ export default {
} else {
response = await addApplication(requestData);
}
if (response.code === 200) {
uni.showToast({
title: '保存成功',
icon: 'success'
icon: 'success',
});
if (autoNavigate) {
setTimeout(() => {
uni.navigateBack();
@@ -490,82 +495,81 @@ export default {
console.error('保存草稿失败:', error);
uni.showToast({
title: error.message || '保存失败',
icon: 'none'
icon: 'none',
});
return false;
} finally {
this.saving = false;
}
},
// 提交申请
async submitApplication() {
if (!this.validateForm()) return;
try {
const { confirm } = await uni.showModal({
title: '确认提交',
content: '确定要提交申请吗?提交后将无法修改。',
showCancel: true
showCancel: true,
});
if (!confirm) return; // 用户取消
this.submitting = true;
// 先保存申请(不自动跳转)
const saveSuccess = await this.saveDraft(false);
if (saveSuccess) {
// 这里可以添加提交申请的逻辑
// 如果后端有单独的提交接口,可以调用
uni.showToast({
title: '提交成功',
icon: 'success'
icon: 'success',
});
setTimeout(() => {
uni.navigateBack();
}, 1000);
}
} catch (error) {
console.error('提交申请失败:', error);
uni.showToast({
title: error.message || '提交失败',
icon: 'none'
icon: 'none',
});
} finally {
this.submitting = false;
}
}
}
},
},
};
</script>
<style scoped>
.apply-container {
min-height: 100vh;
background-color: #f5f5f5;
background-color: #f5f6fa;
padding-bottom: 120rpx; /* 为底部按钮留空间 */
}
.position-card {
background-color: #fff;
margin: 20rpx;
border-radius: 12rpx;
border-radius: 16rpx;
padding: 30rpx;
box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.1);
box-shadow: 0 2rpx 12rpx rgba(0, 0, 0, 0.08);
}
.card-title {
font-size: 32rpx;
font-weight: bold;
color: #333;
color: #303133;
margin-bottom: 20rpx;
padding-bottom: 16rpx;
border-bottom: 1rpx solid #f0f0f0;
border-bottom: 1px solid #f0f0f0;
}
.position-info {
@@ -593,9 +597,9 @@ export default {
.form-container {
background-color: #fff;
margin: 20rpx;
border-radius: 12rpx;
border-radius: 16rpx;
padding: 30rpx;
box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.1);
box-shadow: 0 2rpx 12rpx rgba(0, 0, 0, 0.08);
}
.form-section {
@@ -609,10 +613,10 @@ export default {
.section-title {
font-size: 30rpx;
font-weight: bold;
color: #333;
color: #303133;
margin-bottom: 30rpx;
padding-bottom: 12rpx;
border-bottom: 2rpx solid #1890ff;
border-bottom: 2rpx solid #3c9cff;
position: relative;
}
@@ -623,7 +627,7 @@ export default {
left: 0;
width: 60rpx;
height: 2rpx;
background-color: #1890ff;
background-color: #3c9cff;
}
.bottom-actions {
@@ -661,4 +665,27 @@ export default {
font-size: 24rpx;
color: #999;
}
/* picker输入框样式 */
.picker-input {
display: flex;
align-items: center;
justify-content: space-between;
width: 100%;
padding: 16rpx 24rpx;
background-color: #f8f9fa;
border-radius: 12rpx;
border: 1px solid #eee;
}
.picker-text {
font-size: 28rpx;
color: #303133;
flex: 1;
}
.picker-text.placeholder {
font-size: 24rpx; /* placeholder 字体更小 */
color: #c8c9cc;
}
</style>
+147 -115
View File
@@ -1,12 +1,19 @@
<template>
<view class="detail-container">
<!-- 顶部导航 -->
<u-navbar title="岗位详情" :border-bottom="false" bg-color="#0051d9" title-color="#fff">
<view slot="left" @click="goBack">
<u-icon name="arrow-left" color="#fff" size="22"></u-icon>
</view>
</u-navbar>
<!-- 岗位基本信息 -->
<view class="info-card" v-if="positionData">
<view class="card-header">
<view class="position-title">
<view class="position-name">{{ positionData.positionName }}</view>
<view class="position-status">
<u-tag
<u-tag
:text="getStatusText(positionData.status)"
:type="getStatusType(positionData.status)"
size="mini"
@@ -24,15 +31,19 @@
<view class="core-info">
<view class="info-item">
<view class="info-label">
<u-icon name="account" size="32" color="#1890ff" />
<u-icon name="account" size="18" color="#303133" />
<text>招录情况</text>
</view>
<view class="info-content">
<view class="recruitment-info">
<text class="recruitment-text">需求 {{ positionData.requiredCount }} 人</text>
<text class="recruitment-text current">已招录 {{ positionData.currentCount || 0 }} 人</text>
<text class="recruitment-text">
需求 {{ positionData.requiredCount }} 人
</text>
<text class="recruitment-text current">
已招录 {{ positionData.currentCount || 0 }} 人
</text>
</view>
<u-line-progress
<u-line-progress
:percent="getRecruitmentProgress()"
:active-color="getProgressColor()"
height="12"
@@ -43,7 +54,7 @@
<view class="info-item">
<view class="info-label">
<u-icon name="clock" size="32" color="#1890ff" />
<u-icon name="clock" size="18" color="#303133" />
<text>工作时间</text>
</view>
<view class="info-content">
@@ -53,17 +64,19 @@
<view class="info-item">
<view class="info-label">
<u-icon name="calendar" size="32" color="#1890ff" />
<u-icon name="calendar" size="18" color="#303133" />
<text>有效期限</text>
</view>
<view class="info-content">
<text class="info-value">{{ positionData.startDate }} 至 {{ positionData.endDate }}</text>
<text class="info-value">
{{ positionData.startDate }} 至 {{ positionData.endDate }}
</text>
</view>
</view>
<view class="info-item">
<view class="info-label">
<u-icon name="account" size="32" color="#1890ff" />
<u-icon name="account" size="18" color="#303133" />
<text>联系老师</text>
</view>
<view class="info-content">
@@ -73,17 +86,19 @@
<view class="info-item">
<view class="info-label">
<u-icon name="phone" size="32" color="#1890ff" />
<u-icon name="phone" size="18" color="#303133" />
<text>联系电话</text>
</view>
<view class="info-content">
<text class="info-value contact-phone" @click="callPhone">{{ positionData.contactPhone }}</text>
<text class="info-value contact-phone" @click="callPhone">
{{ positionData.contactPhone }}
</text>
</view>
</view>
<view class="info-item" v-if="positionData.salaryInfo">
<view class="info-label">
<u-icon name="rmb-circle" size="32" color="#1890ff" />
<u-icon name="rmb-circle" size="18" color="#303133" />
<text>薪资情况</text>
</view>
<view class="info-content">
@@ -96,7 +111,7 @@
<!-- 岗位要求 -->
<view class="content-card" v-if="positionData && positionData.positionRequirements">
<view class="card-title">
<u-icon name="file-text" size="32" color="#1890ff" />
<u-icon name="file-text" size="18" color="#303133" />
<text>岗位要求</text>
</view>
<view class="content-text">{{ positionData.positionRequirements }}</view>
@@ -105,7 +120,7 @@
<!-- 工作内容 -->
<view class="content-card" v-if="positionData && positionData.workContent">
<view class="card-title">
<u-icon name="list" size="32" color="#1890ff" />
<u-icon name="list" size="18" color="#303133" />
<text>工作内容</text>
</view>
<view class="content-text">{{ positionData.workContent }}</view>
@@ -114,13 +129,13 @@
<!-- 申请状态 -->
<view class="status-card" v-if="applicationStatus">
<view class="card-title">
<u-icon name="info-circle" size="32" color="#1890ff" />
<u-icon name="info-circle" size="18" color="#303133" />
<text>我的申请状态</text>
</view>
<view class="status-content">
<view class="status-item">
<text class="status-label">申请状态:</text>
<u-tag
<u-tag
:text="applicationStatus.applicationStatusText"
:type="getApplicationStatusType(applicationStatus.applicationStatus)"
size="mini"
@@ -131,21 +146,24 @@
<text class="status-label">申请时间:</text>
<text class="status-value">{{ applicationStatus.lastApplicationTime }}</text>
</view>
<view class="status-actions" v-if="applicationStatus.canEdit || applicationStatus.canCancel">
<u-button
type="warning"
size="small"
<view
class="status-actions"
v-if="applicationStatus.canEdit || applicationStatus.canCancel"
>
<u-button
type="warning"
size="small"
@click="editApplication"
v-if="applicationStatus.canEdit"
>
编辑申请
</u-button>
<u-button
type="error"
size="small"
<u-button
type="error"
size="small"
@click="cancelApplication"
v-if="applicationStatus.canCancel"
style="margin-left: 20rpx;"
style="margin-left: 20rpx"
>
撤销申请
</u-button>
@@ -156,7 +174,7 @@
<!-- 备注信息 -->
<view class="content-card" v-if="positionData && positionData.remark">
<view class="card-title">
<u-icon name="chat" size="32" color="#1890ff" />
<u-icon name="chat" size="18" color="#303133" />
<text>备注信息</text>
</view>
<view class="content-text">{{ positionData.remark }}</view>
@@ -164,9 +182,9 @@
<!-- 底部操作按钮 -->
<view class="bottom-actions">
<u-button
type="primary"
size="large"
<u-button
type="primary"
size="large"
@click="handleApply"
:disabled="!canApply()"
:loading="submitting"
@@ -181,10 +199,7 @@
</template>
<script>
import {
getPositionWithApplicationStatus,
cancelApplication
} from '@/api/workStudy.js';
import { getPositionWithApplicationStatus, cancelApplication } from '@/api/workStudy.js';
export default {
name: 'WorkStudyDetail',
@@ -194,10 +209,10 @@ export default {
positionData: null,
applicationStatus: null,
loading: false,
submitting: false
submitting: false,
};
},
onLoad(options) {
if (options.id) {
this.positionId = options.id;
@@ -205,22 +220,27 @@ export default {
} else {
uni.showToast({
title: '岗位ID不能为空',
icon: 'none'
icon: 'none',
});
setTimeout(() => {
uni.navigateBack();
}, 1500);
}
},
methods: {
// 返回上一页
goBack() {
uni.navigateBack();
},
// 加载岗位详情
async loadPositionDetail() {
this.loading = true;
try {
const response = await getPositionWithApplicationStatus(this.positionId);
if (response.code === 200) {
this.positionData = response.data.position;
this.applicationStatus = response.data.applicationStatus;
@@ -231,9 +251,9 @@ export default {
console.error('加载岗位详情失败:', error);
uni.showToast({
title: error.message || '加载失败',
icon: 'none'
icon: 'none',
});
setTimeout(() => {
uni.navigateBack();
}, 1500);
@@ -241,62 +261,66 @@ export default {
this.loading = false;
}
},
// 申请岗位
handleApply() {
if (!this.canApply()) {
uni.showToast({
title: this.getApplyButtonText(),
icon: 'none'
icon: 'none',
});
return;
}
// 如果已有申请且可以编辑,则编辑申请
if (this.applicationStatus && this.applicationStatus.hasApplication && this.applicationStatus.canEdit) {
if (
this.applicationStatus &&
this.applicationStatus.hasApplication &&
this.applicationStatus.canEdit
) {
this.editApplication();
return;
}
// 否则新建申请
uni.navigateTo({
url: `/pages/workStudy/apply?positionId=${this.positionId}`
url: `/pages/workStudy/apply?positionId=${this.positionId}`,
});
},
// 编辑申请
editApplication() {
if (this.applicationStatus && this.applicationStatus.applicationId) {
uni.navigateTo({
url: `/pages/workStudy/apply?positionId=${this.positionId}&applicationId=${this.applicationStatus.applicationId}&mode=edit`
url: `/pages/workStudy/apply?positionId=${this.positionId}&applicationId=${this.applicationStatus.applicationId}&mode=edit`,
});
}
},
// 撤销申请
async cancelApplication() {
if (!this.applicationStatus || !this.applicationStatus.applicationId) {
return;
}
try {
const { confirm } = await uni.showModal({
title: '确认撤销',
content: '确定要撤销此申请吗?',
showCancel: true
showCancel: true,
});
if (!confirm) return; // 用户取消
this.submitting = true;
const response = await cancelApplication(this.applicationStatus.applicationId);
if (response.code === 200) {
uni.showToast({
title: '撤销成功',
icon: 'success'
icon: 'success',
});
// 重新加载数据
setTimeout(() => {
this.loadPositionDetail();
@@ -308,13 +332,13 @@ export default {
console.error('撤销申请失败:', error);
uni.showToast({
title: error.message || '撤销失败',
icon: 'none'
icon: 'none',
});
} finally {
this.submitting = false;
}
},
// 拨打电话
callPhone() {
if (this.positionData && this.positionData.contactPhone) {
@@ -324,41 +348,41 @@ export default {
console.error('拨打电话失败:', error);
uni.showToast({
title: '拨打电话失败',
icon: 'none'
icon: 'none',
});
}
},
});
}
},
// 获取状态文字
getStatusText(status) {
const statusMap = {
'DRAFT': '草稿',
'PUBLISHED': '已发布',
'RECRUITING': '招录中',
'FULL': '已满员',
'EXPIRED': '已过期',
'SUSPENDED': '已暂停',
'CANCELLED': '已取消'
DRAFT: '草稿',
PUBLISHED: '已发布',
RECRUITING: '招录中',
FULL: '已满员',
EXPIRED: '已过期',
SUSPENDED: '已暂停',
CANCELLED: '已取消',
};
return statusMap[status] || '未知状态';
},
// 获取状态类型
getStatusType(status) {
const typeMap = {
'DRAFT': 'info',
'PUBLISHED': 'warning',
'RECRUITING': 'success',
'FULL': 'primary',
'EXPIRED': 'error',
'SUSPENDED': 'warning',
'CANCELLED': 'error'
DRAFT: 'info',
PUBLISHED: 'warning',
RECRUITING: 'success',
FULL: 'primary',
EXPIRED: 'error',
SUSPENDED: 'warning',
CANCELLED: 'error',
};
return typeMap[status] || 'default';
},
// 获取招录进度
getRecruitmentProgress() {
if (!this.positionData || !this.positionData.requiredCount) return 0;
@@ -366,7 +390,7 @@ export default {
const required = this.positionData.requiredCount;
return Math.min(Math.round((current / required) * 100), 100);
},
// 获取进度条颜色
getProgressColor() {
const progress = this.getRecruitmentProgress();
@@ -375,49 +399,55 @@ export default {
if (progress >= 50) return '#1890ff';
return '#52c41a';
},
// 判断是否可以申请
canApply() {
if (!this.positionData) return false;
// 岗位状态必须是招录中
if (this.positionData.status !== 'RECRUITING') return false;
// 如果已有申请,判断是否可以重新申请
if (this.applicationStatus && this.applicationStatus.hasApplication) {
// 如果申请被拒绝,可以重新申请
return this.applicationStatus.applicationStatus === 'FINAL_REJECTED' ||
this.applicationStatus.applicationStatus === 'COLLEGE_REJECTED';
return (
this.applicationStatus.applicationStatus === 'FINAL_REJECTED' ||
this.applicationStatus.applicationStatus === 'COLLEGE_REJECTED'
);
}
// 检查是否已满员
const current = this.positionData.currentCount || 0;
const required = this.positionData.requiredCount || 0;
return current < required;
},
// 获取申请按钮文字
getApplyButtonText() {
if (!this.positionData) return '加载中...';
if (this.positionData.status !== 'RECRUITING') {
return '岗位暂不可申请';
}
// 检查是否已满员
const current = this.positionData.currentCount || 0;
const required = this.positionData.requiredCount || 0;
if (current >= required) {
return '岗位已满员';
}
// 如果有申请记录
if (this.applicationStatus && this.applicationStatus.hasApplication) {
const status = this.applicationStatus.applicationStatus;
if (status === 'DRAFT') {
return '完善申请信息';
} else if (status === 'SUBMITTED' || status === 'COLLEGE_REVIEWING' || status === 'FINAL_REVIEWING') {
} else if (
status === 'SUBMITTED' ||
status === 'COLLEGE_REVIEWING' ||
status === 'FINAL_REVIEWING'
) {
return '申请审核中';
} else if (status === 'FINAL_APPROVED') {
return '申请已通过';
@@ -426,45 +456,47 @@ export default {
} else if (status === 'CANCELLED') {
return '重新申请';
}
return '查看申请状态';
}
return '申请此岗位';
},
// 获取申请状态类型
getApplicationStatusType(status) {
const typeMap = {
'DRAFT': 'info',
'SUBMITTED': 'warning',
'COLLEGE_REVIEWING': 'warning',
'COLLEGE_APPROVED': 'success',
'COLLEGE_REJECTED': 'error',
'FINAL_REVIEWING': 'warning',
'FINAL_APPROVED': 'success',
'FINAL_REJECTED': 'error',
'CANCELLED': 'info'
DRAFT: 'info',
SUBMITTED: 'warning',
COLLEGE_REVIEWING: 'warning',
COLLEGE_APPROVED: 'success',
COLLEGE_REJECTED: 'error',
FINAL_REVIEWING: 'warning',
FINAL_APPROVED: 'success',
FINAL_REJECTED: 'error',
CANCELLED: 'info',
};
return typeMap[status] || 'default';
}
}
},
},
};
</script>
<style scoped>
.detail-container {
min-height: 100vh;
background-color: #f5f5f5;
background-color: #f5f6fa;
padding-bottom: 120rpx; /* 为底部按钮留空间 */
}
.info-card, .content-card, .status-card {
.info-card,
.content-card,
.status-card {
background-color: #fff;
margin: 20rpx;
border-radius: 12rpx;
border-radius: 16rpx;
padding: 30rpx;
box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.1);
box-shadow: 0 2rpx 12rpx rgba(0, 0, 0, 0.08);
}
.card-header {
@@ -481,7 +513,7 @@ export default {
.position-name {
font-size: 36rpx;
font-weight: bold;
color: #333;
color: #303133;
flex: 1;
line-height: 1.3;
}
@@ -530,7 +562,7 @@ export default {
.info-label text {
font-size: 28rpx;
font-weight: 500;
color: #333;
color: #303133;
margin-left: 12rpx;
}
@@ -540,7 +572,7 @@ export default {
.info-value {
font-size: 28rpx;
color: #666;
color: #606266;
line-height: 1.5;
}
@@ -566,7 +598,7 @@ export default {
}
.recruitment-text.current {
color: #1890ff;
color: #0051d9;
font-weight: 500;
}
@@ -579,15 +611,15 @@ export default {
}
.card-title text {
font-size: 32rpx;
font-size: 30rpx;
font-weight: 500;
color: #333;
color: #303133;
margin-left: 12rpx;
}
.content-text {
font-size: 28rpx;
color: #666;
color: #606266;
line-height: 1.8;
white-space: pre-wrap;
}
File diff suppressed because it is too large. Load diff