|
|
@@ -0,0 +1,891 @@
|
|
|
+<template>
|
|
|
+ <view class="visit-add-container">
|
|
|
+ <!-- 表单内容 -->
|
|
|
+ <u-form
|
|
|
+ ref="formRef"
|
|
|
+ :model="form"
|
|
|
+ :rules="rules"
|
|
|
+ label-width="180rpx"
|
|
|
+ label-position="left"
|
|
|
+ >
|
|
|
+ <!-- 学生选择 -->
|
|
|
+ <view class="form-section">
|
|
|
+ <view class="section-title">学生信息</view>
|
|
|
+
|
|
|
+ <u-form-item label="选择学生" prop="studentId" required>
|
|
|
+ <view @click="handleStudentSelect" style="width: 100%">
|
|
|
+ <u-input
|
|
|
+ v-model="studentDisplayText"
|
|
|
+ placeholder="请选择学生"
|
|
|
+ readonly
|
|
|
+ border="none"
|
|
|
+ suffix-icon="arrow-down"
|
|
|
+ suffix-icon-style="color: #c8c9cc;"
|
|
|
+ style="pointer-events: none"
|
|
|
+ />
|
|
|
+ </view>
|
|
|
+ </u-form-item>
|
|
|
+
|
|
|
+ <!-- 学生基础信息展示 -->
|
|
|
+ <view class="student-info-section" v-if="selectedStudent">
|
|
|
+ <view class="info-row">
|
|
|
+ <text class="info-label">学号:</text>
|
|
|
+ <text class="info-value">{{ selectedStudent.studentNo || '-' }}</text>
|
|
|
+ </view>
|
|
|
+ <view class="info-row">
|
|
|
+ <text class="info-label">班级:</text>
|
|
|
+ <text class="info-value">{{ selectedStudent.className || '-' }}</text>
|
|
|
+ </view>
|
|
|
+ <view class="info-row">
|
|
|
+ <text class="info-label">专业:</text>
|
|
|
+ <text class="info-value">{{ selectedStudent.majorName || '-' }}</text>
|
|
|
+ </view>
|
|
|
+ <view class="info-row">
|
|
|
+ <text class="info-label">实习单位:</text>
|
|
|
+ <text class="info-value">{{ selectedStudent.companyName || '-' }}</text>
|
|
|
+ </view>
|
|
|
+ </view>
|
|
|
+ </view>
|
|
|
+
|
|
|
+ <!-- 走访基本信息 -->
|
|
|
+ <view class="form-section">
|
|
|
+ <view class="section-title">走访信息</view>
|
|
|
+
|
|
|
+ <u-form-item label="走访日期" prop="visitDate" required>
|
|
|
+ <view @click="openVisitDatePicker" style="width: 100%">
|
|
|
+ <u-input
|
|
|
+ v-model="form.visitDate"
|
|
|
+ placeholder="请选择走访日期"
|
|
|
+ readonly
|
|
|
+ border="none"
|
|
|
+ suffix-icon="calendar"
|
|
|
+ suffix-icon-style="color: #c8c9cc;"
|
|
|
+ />
|
|
|
+ </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>
|
|
|
+ <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="bottom-actions">
|
|
|
+ <u-button
|
|
|
+ type="primary"
|
|
|
+ @click="submitForm"
|
|
|
+ :loading="submitLoading"
|
|
|
+ style="width: 100%;"
|
|
|
+ >
|
|
|
+ 提交走访记录
|
|
|
+ </u-button>
|
|
|
+ </view>
|
|
|
+
|
|
|
+ <!-- 学生选择弹窗 -->
|
|
|
+ <u-popup
|
|
|
+ :show="showStudentSelector"
|
|
|
+ mode="bottom"
|
|
|
+ height="80%"
|
|
|
+ :round="10"
|
|
|
+ :closeable="false"
|
|
|
+ @close="showStudentSelector = false"
|
|
|
+ >
|
|
|
+ <view class="student-selector">
|
|
|
+ <view class="selector-header">
|
|
|
+ <text class="selector-title">选择学生</text>
|
|
|
+ <u-icon
|
|
|
+ name="close"
|
|
|
+ size="20"
|
|
|
+ @click="showStudentSelector = false"
|
|
|
+ />
|
|
|
+ </view>
|
|
|
+
|
|
|
+ <view class="search-section">
|
|
|
+ <u-search
|
|
|
+ v-model="studentSearchKeyword"
|
|
|
+ placeholder="搜索学生姓名或学号"
|
|
|
+ @search="searchStudents"
|
|
|
+ @custom="searchStudents"
|
|
|
+ height="72"
|
|
|
+ shape="round"
|
|
|
+ />
|
|
|
+ </view>
|
|
|
+
|
|
|
+ <view class="student-list">
|
|
|
+ <view
|
|
|
+ class="student-item"
|
|
|
+ v-for="student in studentList"
|
|
|
+ :key="student.studentId"
|
|
|
+ @click="selectStudent(student)"
|
|
|
+ >
|
|
|
+ <view class="student-main">
|
|
|
+ <text class="student-name">{{ student.studentName }}</text>
|
|
|
+ <text class="student-no">{{ student.studentNo }}</text>
|
|
|
+ </view>
|
|
|
+ <view class="student-detail">
|
|
|
+ <text class="detail-text">{{ student.className || '-' }}</text>
|
|
|
+ <text class="detail-text">{{ student.companyName || '暂无实习信息' }}</text>
|
|
|
+ </view>
|
|
|
+ <u-icon
|
|
|
+ name="checkmark"
|
|
|
+ size="16"
|
|
|
+ color="#007AFF"
|
|
|
+ v-if="form.studentId === student.studentId"
|
|
|
+ />
|
|
|
+ </view>
|
|
|
+
|
|
|
+ <view class="student-empty" v-if="studentList.length === 0 && !studentLoading">
|
|
|
+ <u-empty
|
|
|
+ mode="search"
|
|
|
+ text="暂无学生数据"
|
|
|
+ textColor="#c8c9cc"
|
|
|
+ iconColor="#c8c9cc"
|
|
|
+ />
|
|
|
+ </view>
|
|
|
+
|
|
|
+ <view class="student-loading" v-if="studentLoading">
|
|
|
+ <u-loading-icon mode="circle" />
|
|
|
+ <text style="margin-left: 10rpx; color: #909399;">加载中...</text>
|
|
|
+ </view>
|
|
|
+ </view>
|
|
|
+ </view>
|
|
|
+ </u-popup>
|
|
|
+
|
|
|
+ <!-- 日期选择器 -->
|
|
|
+ <u-datetime-picker
|
|
|
+ :show="showVisitDatePicker"
|
|
|
+ v-model="visitDateValue"
|
|
|
+ mode="date"
|
|
|
+ :min-date="minDate"
|
|
|
+ :max-date="maxDate"
|
|
|
+ @confirm="handleVisitDateConfirm"
|
|
|
+ @cancel="showVisitDatePicker = false"
|
|
|
+ />
|
|
|
+ </view>
|
|
|
+</template>
|
|
|
+
|
|
|
+<script>
|
|
|
+import { addVisit, getStudentInternshipList, searchStudents } from '@/api/visit.js';
|
|
|
+import { userStorage } from '@/common/storage.js';
|
|
|
+
|
|
|
+export default {
|
|
|
+ name: 'VisitAdd',
|
|
|
+ data() {
|
|
|
+ return {
|
|
|
+ // 表单数据
|
|
|
+ form: {
|
|
|
+ studentId: '',
|
|
|
+ internshipId: '',
|
|
|
+ companyName: '',
|
|
|
+ companyAddress: '',
|
|
|
+ visitDate: '',
|
|
|
+ visitorName: '',
|
|
|
+ visitorPosition: '',
|
|
|
+ visitPurpose: '',
|
|
|
+ companySituation: '',
|
|
|
+ studentPerformance: '',
|
|
|
+ problemsFound: '',
|
|
|
+ suggestions: '',
|
|
|
+ visitSummary: '',
|
|
|
+ visitImages: '',
|
|
|
+ remark: '',
|
|
|
+ status: 'planned' // 默认状态为计划中
|
|
|
+ },
|
|
|
+
|
|
|
+ // 表单验证规则
|
|
|
+ rules: {
|
|
|
+ studentId: [
|
|
|
+ { required: true, message: '请选择学生', trigger: ['blur', 'change'] }
|
|
|
+ ],
|
|
|
+ visitDate: [
|
|
|
+ { required: true, message: '请选择走访日期', trigger: ['blur', 'change'] }
|
|
|
+ ],
|
|
|
+ visitorName: [
|
|
|
+ { required: true, message: '请输入走访人员姓名', trigger: ['blur', 'change'] }
|
|
|
+ ],
|
|
|
+ visitPurpose: [
|
|
|
+ { required: true, message: '请输入走访目的', trigger: ['blur', 'change'] }
|
|
|
+ ]
|
|
|
+ },
|
|
|
+
|
|
|
+ // 学生选择
|
|
|
+ showStudentSelector: false,
|
|
|
+ studentList: [],
|
|
|
+ selectedStudent: null,
|
|
|
+ studentDisplayText: '',
|
|
|
+ studentSearchKeyword: '',
|
|
|
+ studentLoading: false,
|
|
|
+
|
|
|
+ // 日期选择
|
|
|
+ showVisitDatePicker: false,
|
|
|
+ visitDateValue: Date.now(),
|
|
|
+ minDate: Date.now() - 30 * 24 * 60 * 60 * 1000, // 最小日期为30天前
|
|
|
+ maxDate: Date.now() + 365 * 24 * 60 * 60 * 1000, // 最大日期为一年后
|
|
|
+
|
|
|
+ // 图片上传
|
|
|
+ imageList: [],
|
|
|
+ maxImageCount: 10,
|
|
|
+
|
|
|
+ // 加载状态
|
|
|
+ submitLoading: false
|
|
|
+ };
|
|
|
+ },
|
|
|
+
|
|
|
+ onLoad() {
|
|
|
+ this.initForm();
|
|
|
+ },
|
|
|
+
|
|
|
+ methods: {
|
|
|
+ // 初始化表单
|
|
|
+ async initForm() {
|
|
|
+ // 获取当前登录用户信息,自动填入走访人
|
|
|
+ try {
|
|
|
+ const userInfo = userStorage.getUserInfo();
|
|
|
+ // 修复:用户信息结构为 {user: {userId, nickName, ...}, permissions, roles}
|
|
|
+ const user = userInfo?.user;
|
|
|
+ if (user && user.nickName) {
|
|
|
+ this.form.visitorName = user.nickName;
|
|
|
+ }
|
|
|
+ if (user && user.position) {
|
|
|
+ this.form.visitorPosition = user.position;
|
|
|
+ }
|
|
|
+ } catch (error) {
|
|
|
+ console.warn('获取用户信息失败:', error);
|
|
|
+ }
|
|
|
+
|
|
|
+ // 加载学生列表
|
|
|
+ this.loadStudentList();
|
|
|
+ },
|
|
|
+
|
|
|
+ // 加载学生列表
|
|
|
+ async loadStudentList() {
|
|
|
+ if (this.studentLoading) return;
|
|
|
+
|
|
|
+ this.studentLoading = true;
|
|
|
+ try {
|
|
|
+ // 获取当前登录用户信息,获取班主任ID
|
|
|
+ const userInfo = userStorage.getUserInfo();
|
|
|
+ // 修复:从正确的路径获取用户ID
|
|
|
+ const classTeacherId = userInfo?.user?.userId;
|
|
|
+
|
|
|
+ if (!classTeacherId) {
|
|
|
+ uni.showToast({ title: '无法获取班主任信息', icon: 'none' });
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ const params = {
|
|
|
+ classTeacherId,
|
|
|
+ pageNum: 1,
|
|
|
+ pageSize: 100 // 获取较多数据供选择
|
|
|
+ };
|
|
|
+
|
|
|
+ const response = await getStudentInternshipList(params);
|
|
|
+
|
|
|
+ if (response.code === 200) {
|
|
|
+ // 实习信息接口返回TableDataInfo格式
|
|
|
+ this.studentList = response.rows || [];
|
|
|
+ } else {
|
|
|
+ uni.showToast({ title: '获取学生列表失败', icon: 'none' });
|
|
|
+ }
|
|
|
+ } catch (error) {
|
|
|
+ console.error('加载学生列表失败:', error);
|
|
|
+ uni.showToast({ title: '网络异常,请稍后重试', icon: 'none' });
|
|
|
+ } finally {
|
|
|
+ this.studentLoading = false;
|
|
|
+ }
|
|
|
+ },
|
|
|
+
|
|
|
+ // 搜索学生
|
|
|
+ async searchStudents() {
|
|
|
+ if (!this.studentSearchKeyword.trim()) {
|
|
|
+ this.loadStudentList();
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (this.studentLoading) return;
|
|
|
+
|
|
|
+ this.studentLoading = true;
|
|
|
+ try {
|
|
|
+ const userInfo = userStorage.getUserInfo();
|
|
|
+ // 修复:从正确的路径获取用户ID
|
|
|
+ const classTeacherId = userInfo?.user?.userId;
|
|
|
+
|
|
|
+ const response = await searchStudents(this.studentSearchKeyword, classTeacherId);
|
|
|
+
|
|
|
+ if (response.code === 200) {
|
|
|
+ // 实习信息接口返回TableDataInfo格式
|
|
|
+ this.studentList = response.rows || [];
|
|
|
+ } else {
|
|
|
+ this.studentList = [];
|
|
|
+ }
|
|
|
+ } catch (error) {
|
|
|
+ console.error('搜索学生失败:', error);
|
|
|
+ this.studentList = [];
|
|
|
+ } finally {
|
|
|
+ this.studentLoading = false;
|
|
|
+ }
|
|
|
+ },
|
|
|
+
|
|
|
+ // 显示学生选择器
|
|
|
+ handleStudentSelect() {
|
|
|
+ this.showStudentSelector = true;
|
|
|
+ },
|
|
|
+
|
|
|
+ // 选择学生
|
|
|
+ selectStudent(student) {
|
|
|
+ this.selectedStudent = student;
|
|
|
+ this.form.studentId = student.studentId;
|
|
|
+ this.form.internshipId = student.internshipId;
|
|
|
+ this.form.companyName = student.companyName;
|
|
|
+ this.form.companyAddress = student.companyAddress;
|
|
|
+ this.studentDisplayText = `${student.studentName}(${student.studentNo})`;
|
|
|
+ this.showStudentSelector = false;
|
|
|
+ },
|
|
|
+
|
|
|
+ // 打开走访日期选择器
|
|
|
+ openVisitDatePicker() {
|
|
|
+ console.log('打开日期选择器,当前表单日期:', this.form.visitDate);
|
|
|
+ console.log('当前visitDateValue:', this.visitDateValue);
|
|
|
+
|
|
|
+ // 如果表单中已有日期,使用该日期初始化选择器
|
|
|
+ if (this.form.visitDate) {
|
|
|
+ try {
|
|
|
+ const existingDate = new Date(this.form.visitDate);
|
|
|
+ if (!isNaN(existingDate.getTime())) {
|
|
|
+ this.visitDateValue = existingDate.getTime();
|
|
|
+ console.log('使用现有日期初始化:', this.visitDateValue);
|
|
|
+ }
|
|
|
+ } catch (error) {
|
|
|
+ console.log('解析现有日期失败:', error);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ this.showVisitDatePicker = true;
|
|
|
+ },
|
|
|
+
|
|
|
+ // 走访日期确认
|
|
|
+ handleVisitDateConfirm(value) {
|
|
|
+ console.log('=== 走访日期确认开始 ===');
|
|
|
+ console.log('接收到的value:', value);
|
|
|
+ console.log('value类型:', typeof value);
|
|
|
+ console.log('value是否为null/undefined:', value == null);
|
|
|
+
|
|
|
+ if (value == null) {
|
|
|
+ console.error('接收到空值');
|
|
|
+ uni.showToast({ title: '请选择有效日期', icon: 'none' });
|
|
|
+ this.showVisitDatePicker = false;
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ try {
|
|
|
+ let timestamp;
|
|
|
+
|
|
|
+ // 处理uview-ui返回的对象格式 {value: timestamp, mode: 'date'}
|
|
|
+ if (typeof value === 'object' && value.value !== undefined) {
|
|
|
+ timestamp = value.value;
|
|
|
+ console.log('从对象中提取timestamp:', timestamp);
|
|
|
+ } else if (typeof value === 'number') {
|
|
|
+ timestamp = value;
|
|
|
+ console.log('直接使用number类型timestamp:', timestamp);
|
|
|
+ } else if (typeof value === 'string') {
|
|
|
+ timestamp = parseInt(value);
|
|
|
+ console.log('从string转换timestamp:', timestamp);
|
|
|
+ } else {
|
|
|
+ throw new Error('无法识别的value格式');
|
|
|
+ }
|
|
|
+
|
|
|
+ // 创建Date对象
|
|
|
+ const date = new Date(timestamp);
|
|
|
+ console.log('创建的Date对象:', date);
|
|
|
+ console.log('Date.getTime():', date.getTime());
|
|
|
+ console.log('是否为有效日期:', !isNaN(date.getTime()));
|
|
|
+
|
|
|
+ if (isNaN(date.getTime())) {
|
|
|
+ throw new Error('无效的日期值');
|
|
|
+ }
|
|
|
+
|
|
|
+ const formattedDate = this.formatDate(date);
|
|
|
+ console.log('格式化后的日期:', formattedDate);
|
|
|
+
|
|
|
+ if (!formattedDate) {
|
|
|
+ throw new Error('日期格式化失败');
|
|
|
+ }
|
|
|
+
|
|
|
+ this.form.visitDate = formattedDate;
|
|
|
+ this.visitDateValue = timestamp; // 保存时间戳而不是原始对象
|
|
|
+
|
|
|
+ console.log('最终form.visitDate:', this.form.visitDate);
|
|
|
+ console.log('最终visitDateValue:', this.visitDateValue);
|
|
|
+ console.log('日期设置成功');
|
|
|
+
|
|
|
+ } catch (error) {
|
|
|
+ console.error('日期处理异常:', error.message);
|
|
|
+ console.error('异常详情 - value:', value, 'type:', typeof value);
|
|
|
+ uni.showToast({ title: '日期选择失败,请重试', icon: 'none' });
|
|
|
+ }
|
|
|
+
|
|
|
+ this.showVisitDatePicker = false;
|
|
|
+ console.log('=== 走访日期确认结束 ===');
|
|
|
+ },
|
|
|
+
|
|
|
+ // 格式化日期
|
|
|
+ formatDate(date) {
|
|
|
+ console.log('formatDate - 输入的date:', date);
|
|
|
+ console.log('formatDate - date类型:', typeof date);
|
|
|
+
|
|
|
+ if (!date) {
|
|
|
+ console.log('formatDate - date为空');
|
|
|
+ return '';
|
|
|
+ }
|
|
|
+
|
|
|
+ if (isNaN(date.getTime())) {
|
|
|
+ console.log('formatDate - date.getTime()为NaN');
|
|
|
+ return '';
|
|
|
+ }
|
|
|
+
|
|
|
+ try {
|
|
|
+ const year = date.getFullYear();
|
|
|
+ const month = String(date.getMonth() + 1).padStart(2, '0');
|
|
|
+ const day = String(date.getDate()).padStart(2, '0');
|
|
|
+ const result = `${year}-${month}-${day}`;
|
|
|
+
|
|
|
+ console.log('formatDate - 格式化结果:', result);
|
|
|
+ return result;
|
|
|
+ } catch (error) {
|
|
|
+ console.error('formatDate - 格式化异常:', error);
|
|
|
+ return '';
|
|
|
+ }
|
|
|
+ },
|
|
|
+
|
|
|
+ // 选择图片
|
|
|
+ chooseImage() {
|
|
|
+ const remainCount = this.maxImageCount - this.imageList.length;
|
|
|
+ uni.chooseImage({
|
|
|
+ count: remainCount,
|
|
|
+ sizeType: ['compressed'],
|
|
|
+ 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
|
|
|
+ });
|
|
|
+ }
|
|
|
+
|
|
|
+ // 更新表单中的图片数据
|
|
|
+ this.form.visitImages = JSON.stringify(this.imageList);
|
|
|
+ } catch (error) {
|
|
|
+ console.error('上传图片失败:', error);
|
|
|
+ uni.showToast({ title: '上传图片失败', icon: 'none' });
|
|
|
+ } finally {
|
|
|
+ uni.hideLoading();
|
|
|
+ }
|
|
|
+ },
|
|
|
+
|
|
|
+ // 预览图片
|
|
|
+ previewImage(index) {
|
|
|
+ const urls = this.imageList.map(item => item.url);
|
|
|
+ uni.previewImage({
|
|
|
+ urls,
|
|
|
+ current: index
|
|
|
+ });
|
|
|
+ },
|
|
|
+
|
|
|
+ // 删除图片
|
|
|
+ removeImage(index) {
|
|
|
+ this.imageList.splice(index, 1);
|
|
|
+ this.form.visitImages = JSON.stringify(this.imageList);
|
|
|
+ },
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+ // 提交表单
|
|
|
+ async submitForm() {
|
|
|
+ // 表单验证
|
|
|
+ try {
|
|
|
+ await this.$refs.formRef.validate();
|
|
|
+ } catch (error) {
|
|
|
+ console.log('表单验证失败:', error);
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ this.submitLoading = true;
|
|
|
+ try {
|
|
|
+ const formData = {
|
|
|
+ ...this.form,
|
|
|
+ status: '1' // 已提交状态 (0-草稿 1-已提交 2-已审核)
|
|
|
+ };
|
|
|
+
|
|
|
+ const response = await addVisit(formData);
|
|
|
+
|
|
|
+ if (response.code === 200) {
|
|
|
+ uni.showToast({ title: '提交成功', icon: 'success' });
|
|
|
+ setTimeout(() => {
|
|
|
+ uni.navigateBack();
|
|
|
+ }, 1500);
|
|
|
+ } else {
|
|
|
+ uni.showToast({ title: response.msg || '提交失败', icon: 'none' });
|
|
|
+ }
|
|
|
+ } catch (error) {
|
|
|
+ console.error('提交走访记录失败:', error);
|
|
|
+ uni.showToast({ title: '网络异常,请稍后重试', icon: 'none' });
|
|
|
+ } finally {
|
|
|
+ this.submitLoading = false;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+};
|
|
|
+</script>
|
|
|
+
|
|
|
+<style lang="scss" scoped>
|
|
|
+.visit-add-container {
|
|
|
+ min-height: 100vh;
|
|
|
+ background-color: #f8f8f8;
|
|
|
+ padding-bottom: 120rpx; // 为底部按钮留出空间
|
|
|
+}
|
|
|
+
|
|
|
+.form-section {
|
|
|
+ background: #ffffff;
|
|
|
+ margin-bottom: 20rpx;
|
|
|
+ padding: 0 20rpx;
|
|
|
+
|
|
|
+ .section-title {
|
|
|
+ padding: 24rpx 0 16rpx;
|
|
|
+ font-size: 32rpx;
|
|
|
+ font-weight: 600;
|
|
|
+ color: #303133;
|
|
|
+ border-bottom: 1rpx solid #ebeef5;
|
|
|
+ margin-bottom: 20rpx;
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+.student-info-section {
|
|
|
+ background: #f8f9fa;
|
|
|
+ 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: 120rpx;
|
|
|
+ }
|
|
|
+
|
|
|
+ .info-value {
|
|
|
+ font-size: 28rpx;
|
|
|
+ color: #303133;
|
|
|
+ flex: 1;
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+.upload-section {
|
|
|
+ .image-list {
|
|
|
+ display: flex;
|
|
|
+ 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;
|
|
|
+ right: 8rpx;
|
|
|
+ width: 32rpx;
|
|
|
+ height: 32rpx;
|
|
|
+ border-radius: 50%;
|
|
|
+ background: rgba(0, 0, 0, 0.6);
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ justify-content: center;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ .upload-btn {
|
|
|
+ width: 160rpx;
|
|
|
+ height: 160rpx;
|
|
|
+ border: 2rpx dashed #c8c9cc;
|
|
|
+ border-radius: 8rpx;
|
|
|
+ display: flex;
|
|
|
+ flex-direction: column;
|
|
|
+ align-items: center;
|
|
|
+ justify-content: center;
|
|
|
+
|
|
|
+ .upload-text {
|
|
|
+ font-size: 24rpx;
|
|
|
+ color: #c8c9cc;
|
|
|
+ margin-top: 8rpx;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ .upload-tip {
|
|
|
+ font-size: 24rpx;
|
|
|
+ color: #909399;
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+.bottom-actions {
|
|
|
+ position: fixed;
|
|
|
+ bottom: 0;
|
|
|
+ left: 0;
|
|
|
+ right: 0;
|
|
|
+ background: #ffffff;
|
|
|
+ padding: 20rpx;
|
|
|
+ border-top: 1rpx solid #ebeef5;
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ z-index: 100;
|
|
|
+}
|
|
|
+
|
|
|
+.student-selector {
|
|
|
+ height: 100%;
|
|
|
+ display: flex;
|
|
|
+ flex-direction: column;
|
|
|
+ overflow: hidden;
|
|
|
+
|
|
|
+ .selector-header {
|
|
|
+ display: flex;
|
|
|
+ justify-content: space-between;
|
|
|
+ align-items: center;
|
|
|
+ padding: 24rpx 20rpx;
|
|
|
+ border-bottom: 1rpx solid #ebeef5;
|
|
|
+
|
|
|
+ .selector-title {
|
|
|
+ font-size: 32rpx;
|
|
|
+ font-weight: 600;
|
|
|
+ color: #303133;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ .search-section {
|
|
|
+ padding: 20rpx;
|
|
|
+ border-bottom: 1rpx solid #ebeef5;
|
|
|
+ }
|
|
|
+
|
|
|
+ .student-list {
|
|
|
+ height: 600rpx; /* 固定高度,确保可以滚动 */
|
|
|
+ padding: 0 20rpx;
|
|
|
+ overflow-y: scroll;
|
|
|
+ -webkit-overflow-scrolling: touch;
|
|
|
+ position: relative;
|
|
|
+ border: 1px solid #eee; /* 添加边框便于调试 */
|
|
|
+ box-sizing: border-box;
|
|
|
+
|
|
|
+ .student-item {
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ padding: 24rpx 0;
|
|
|
+ border-bottom: 1rpx solid #ebeef5;
|
|
|
+
|
|
|
+ .student-main {
|
|
|
+ flex: 1;
|
|
|
+
|
|
|
+ .student-name {
|
|
|
+ font-size: 32rpx;
|
|
|
+ font-weight: 600;
|
|
|
+ color: #303133;
|
|
|
+ margin-right: 16rpx;
|
|
|
+ }
|
|
|
+
|
|
|
+ .student-no {
|
|
|
+ font-size: 28rpx;
|
|
|
+ color: #909399;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ .student-detail {
|
|
|
+ flex: 2;
|
|
|
+ margin-left: 20rpx;
|
|
|
+
|
|
|
+ .detail-text {
|
|
|
+ font-size: 26rpx;
|
|
|
+ color: #606266;
|
|
|
+ display: block;
|
|
|
+ margin-bottom: 4rpx;
|
|
|
+
|
|
|
+ &:last-child {
|
|
|
+ margin-bottom: 0;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ .student-empty,
|
|
|
+ .student-loading {
|
|
|
+ padding: 80rpx 20rpx;
|
|
|
+ text-align: center;
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ justify-content: center;
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+</style>
|