619 lines
16 KiB
Vue
619 lines
16 KiB
Vue
<template>
|
||
<BasicLayout>
|
||
<!-- <template #top>
|
||
</template> -->
|
||
<view class="mb-5">
|
||
<view class="flex-row items-center white-bg-color p-15 r-md">
|
||
<!-- 左侧课程图片 -->
|
||
<view class="course-image-section" v-if="xkkc.lxtp">
|
||
<image
|
||
:src="getImageUrl(xkkc.lxtp)"
|
||
class="course-image"
|
||
mode="aspectFill"
|
||
@error="handleImageError"
|
||
/>
|
||
</view>
|
||
<view class="flex-col ml-10 flex-1 course-info">
|
||
<view class="course-name">{{ xkkc.kcmc }}</view>
|
||
<view class="course-info-item">
|
||
<view class="info-label">上课周期:</view>
|
||
<view class="info-data">{{ xkkc.skzqmc }}</view>
|
||
</view>
|
||
<view class="course-info-item">
|
||
<view class="info-label">上课时间:</view>
|
||
<view class="info-data">{{ formatClassTime(xkkc.skkstime, xkkc.skjstime) }}</view>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 四个主要部分 -->
|
||
<view class="p-15">
|
||
<!-- 第一部分:教师信息 -->
|
||
<view class="section-card mb-15">
|
||
<view class="section-title">教师信息 <text class="required">*</text></view>
|
||
<view class="teacher-info-header">
|
||
<!-- 教师头像 -->
|
||
<view class="teacher-avatar-section" v-if="teacherHeadPic">
|
||
<image
|
||
:src="getImageUrl(teacherHeadPic)"
|
||
class="teacher-avatar"
|
||
mode="aspectFill"
|
||
@error="handleTeacherAvatarError"
|
||
/>
|
||
</view>
|
||
<!-- 右侧信息 -->
|
||
<view class="teacher-details">
|
||
<view class="detail-row">
|
||
<view class="detail-item">
|
||
<text class="detail-label">开课老师:</text>
|
||
<text class="detail-value">{{ js.jsxm }}</text>
|
||
</view>
|
||
<view class="detail-item">
|
||
<text class="detail-label">上课地点:</text>
|
||
<text class="detail-value">{{ xkkc.kcdd }}</text>
|
||
</view>
|
||
<view class="detail-item">
|
||
<text class="detail-label">上课人数:</text>
|
||
<text class="detail-value">{{ xkkc.hasNum || 0 }} | {{ xkkc.maxNum || 0 }}</text>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
<!-- 老师信息输入框单独一行 -->
|
||
<view class="teacher-input-row">
|
||
<textarea
|
||
v-model="xkkc.kcjsms"
|
||
placeholder="请输入教师信息(必填)"
|
||
class="form-textarea"
|
||
/>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 第二部分:教学理念 -->
|
||
<view class="section-card mb-15">
|
||
<view class="section-title">教学理念 <text class="required">*</text></view>
|
||
<textarea
|
||
v-model="xkkc.jxll"
|
||
placeholder="请输入教学理念(必填)"
|
||
class="form-textarea"
|
||
/>
|
||
</view>
|
||
|
||
<!-- 第三部分:教学计划 -->
|
||
<view class="section-card mb-15">
|
||
<view class="section-header">
|
||
<view class="section-title">教学计划 <text class="required">*</text></view>
|
||
<view @click="addEducation" class="add-icon">
|
||
<BasicIcon type="icon-tianjia" size="25" />
|
||
</view>
|
||
</view>
|
||
<view v-if="education.xl.length > 0">
|
||
<template v-for="(item, index) in education.xl" :key="index">
|
||
<view class="po-re mb-15">
|
||
<BasicForm v-model="item.value" :schema="schema" :formsProps="{ labelWidth: 100 }" />
|
||
<view @click="deleteMemberFamily(index as number, item.value)" class="delete-icon mt-5">
|
||
<BasicIcon type="clear" size="30" />
|
||
</view>
|
||
</view>
|
||
</template>
|
||
</view>
|
||
<view v-else class="p-15 flex-row-center color-9 font-13 white-bg-color">教学计划暂无数据</view>
|
||
</view>
|
||
</view>
|
||
<template #bottom>
|
||
<view class="white-bg-color py-5">
|
||
<view class="flex-row items-center pb-10 pt-5">
|
||
<u-button text="返回" class="ml-15 mr-7" :plain="true" @click="navigateBack" />
|
||
<u-button text="提交" class="mr-15 mr-7" type="primary" @click="submit" />
|
||
</view>
|
||
</view>
|
||
</template>
|
||
</BasicLayout>
|
||
</template>
|
||
|
||
<script lang="ts" setup>
|
||
import { navigateBack } from "@/utils/uniapp";
|
||
|
||
import { useForm } from "@/components/BasicForm/hooks/useForm";
|
||
import { cloneDeep, get } from "lodash";
|
||
import { useUserStore } from "@/store/modules/user";
|
||
import { useDataStore } from "@/store/modules/data";
|
||
import { jsdXkkcSaveApi } from "@/api/base/server";
|
||
import dayjs from "dayjs";
|
||
import { BASE_IMAGE_URL } from "@/config";
|
||
|
||
const { getJs } = useUserStore();
|
||
const { getData } = useDataStore();
|
||
|
||
const js = computed(() => getJs)
|
||
const xkkc = ref<any>({})
|
||
|
||
// 获取教师头像
|
||
const teacherHeadPic = ref<string>('')
|
||
|
||
// 从localStorage获取教师头像
|
||
const getTeacherHeadPic = () => {
|
||
try {
|
||
console.log('开始获取教师头像...');
|
||
const appUserStr = uni.getStorageSync('app-user')
|
||
console.log('app-user原始数据:', appUserStr);
|
||
|
||
// 解析JSON字符串为对象
|
||
let appUser;
|
||
try {
|
||
appUser = typeof appUserStr === 'string' ? JSON.parse(appUserStr) : appUserStr;
|
||
} catch (parseError) {
|
||
console.error('JSON解析失败:', parseError);
|
||
return;
|
||
}
|
||
|
||
console.log('解析后的appUser数据:', appUser);
|
||
|
||
// 详细检查数据结构
|
||
console.log('appUser存在:', !!appUser);
|
||
console.log('appUser类型:', typeof appUser);
|
||
console.log('appUser的所有键:', Object.keys(appUser));
|
||
|
||
if (appUser) {
|
||
console.log('appUser.userdata存在:', !!appUser.userdata);
|
||
console.log('appUser.userdata类型:', typeof appUser.userdata);
|
||
if (appUser.userdata) {
|
||
console.log('appUser.userdata的所有键:', Object.keys(appUser.userdata));
|
||
console.log('appUser.userdata.js存在:', !!appUser.userdata.js);
|
||
if (appUser.userdata.js) {
|
||
console.log('appUser.userdata.js的所有键:', Object.keys(appUser.userdata.js));
|
||
console.log('appUser.userdata.js.headPic存在:', !!appUser.userdata.js.headPic);
|
||
console.log('headPic值:', appUser.userdata.js.headPic);
|
||
}
|
||
}
|
||
}
|
||
|
||
// 直接从app-user获取userdata
|
||
if (appUser && appUser.userdata && appUser.userdata.js) {
|
||
console.log('userdata.js数据:', appUser.userdata.js);
|
||
if (appUser.userdata.js.headPic) {
|
||
console.log('找到headPic:', appUser.userdata.js.headPic);
|
||
teacherHeadPic.value = appUser.userdata.js.headPic
|
||
console.log('设置teacherHeadPic为:', teacherHeadPic.value);
|
||
} else {
|
||
console.log('userdata.js中没有headPic字段');
|
||
}
|
||
} else {
|
||
console.log('没有找到userdata或js');
|
||
}
|
||
} catch (error) {
|
||
console.error('获取教师头像失败:', error)
|
||
}
|
||
}
|
||
|
||
const [register, { getValue, setValue }] = useForm({
|
||
schema: [
|
||
// 不再需要kcjsms和jxll字段,因为已经用原生textarea处理
|
||
{ colSlot: 'jxjh' },
|
||
],
|
||
});
|
||
|
||
const schema = reactive<FormsSchema[]>([
|
||
{
|
||
field: "jhjd",
|
||
label: "阶段",
|
||
component: "BasicInput",
|
||
componentProps: {},
|
||
},
|
||
{
|
||
field: "jhsj",
|
||
label: "计划时间",
|
||
component: "BasicDateTimes",
|
||
componentProps: {},
|
||
},
|
||
{
|
||
field: "jhdd",
|
||
label: "地址",
|
||
component: "BasicInput",
|
||
componentProps: {},
|
||
},
|
||
{
|
||
field: "jhnr",
|
||
label: "计划内容",
|
||
component: "BasicInput",
|
||
itemProps: {
|
||
labelPosition: "top",
|
||
},
|
||
componentProps: {
|
||
type: "textarea",
|
||
},
|
||
},
|
||
])
|
||
|
||
|
||
const education = reactive<any>(
|
||
{
|
||
xl: []
|
||
}
|
||
)
|
||
function addEducation() {
|
||
education.xl.push({ value: {
|
||
jhjd: '',
|
||
jhsj: '',
|
||
jhdd: '',
|
||
jhnr: ''
|
||
} })
|
||
}
|
||
|
||
function deleteMemberFamily(index: number, item: any) {
|
||
const list = cloneDeep(education.xl)
|
||
list.splice(index, 1)
|
||
education.xl = list
|
||
}
|
||
|
||
// 格式化上课时间
|
||
const formatClassTime = (startTime: string, endTime: string) => {
|
||
if (!startTime || !endTime) {
|
||
return '';
|
||
}
|
||
|
||
try {
|
||
// 尝试解析时间,支持多种格式
|
||
let start, end;
|
||
|
||
// 如果是时间格式(HH:mm:ss 或 HH:mm)
|
||
if (startTime.includes(':') && !startTime.includes('-') && !startTime.includes('/')) {
|
||
start = startTime;
|
||
end = endTime;
|
||
} else {
|
||
// 尝试用 dayjs 解析
|
||
const startDate = dayjs(startTime);
|
||
const endDate = dayjs(endTime);
|
||
|
||
if (startDate.isValid() && endDate.isValid()) {
|
||
start = startDate.format('HH:mm:ss');
|
||
end = endDate.format('HH:mm:ss');
|
||
} else {
|
||
// 如果解析失败,直接返回原始值
|
||
return `${startTime}~${endTime}`;
|
||
}
|
||
}
|
||
|
||
return `${start}~${end}`;
|
||
} catch (error) {
|
||
console.error('时间格式化错误:', error);
|
||
// 如果出错,返回原始值
|
||
return `${startTime}~${endTime}`;
|
||
}
|
||
};
|
||
|
||
// 获取完整的图片URL
|
||
const getImageUrl = (imagePath: string) => {
|
||
console.log('getImageUrl被调用,参数:', imagePath);
|
||
console.log('BASE_IMAGE_URL:', BASE_IMAGE_URL);
|
||
|
||
if (!imagePath) {
|
||
console.log('图片路径为空,返回空字符串');
|
||
return '';
|
||
}
|
||
// 如果已经是完整URL,直接返回
|
||
if (imagePath.startsWith('http://') || imagePath.startsWith('https://')) {
|
||
console.log('已经是完整URL,直接返回:', imagePath);
|
||
return imagePath;
|
||
}
|
||
// 否则拼接BASE_IMAGE_URL
|
||
const fullUrl = `${BASE_IMAGE_URL}${imagePath}`;
|
||
console.log('拼接后的完整URL:', fullUrl);
|
||
return fullUrl;
|
||
};
|
||
|
||
// 处理图片加载错误
|
||
const handleImageError = () => {
|
||
console.log('课程图片加载失败');
|
||
};
|
||
|
||
// 处理教师头像加载错误
|
||
const handleTeacherAvatarError = () => {
|
||
console.log('教师头像加载失败');
|
||
};
|
||
|
||
const submit = async () => {
|
||
// 必填验证
|
||
if (!xkkc.value.kcjsms || xkkc.value.kcjsms.trim() === '') {
|
||
uni.showToast({
|
||
title: '请输入教师信息',
|
||
icon: 'none',
|
||
duration: 2000
|
||
});
|
||
return;
|
||
}
|
||
|
||
if (!xkkc.value.jxll || xkkc.value.jxll.trim() === '') {
|
||
uni.showToast({
|
||
title: '请输入教学理念',
|
||
icon: 'none',
|
||
duration: 2000
|
||
});
|
||
return;
|
||
}
|
||
|
||
if (!education.xl || education.xl.length === 0) {
|
||
uni.showToast({
|
||
title: '请添加教学计划',
|
||
icon: 'none',
|
||
duration: 2000
|
||
});
|
||
return;
|
||
}
|
||
|
||
// 验证教学计划是否完整
|
||
for (let i = 0; i < education.xl.length; i++) {
|
||
const item = education.xl[i];
|
||
if (!item.value.jhjd || item.value.jhjd.trim() === '' ||
|
||
!item.value.jhsj || item.value.jhsj.trim() === '' ||
|
||
!item.value.jhdd || item.value.jhdd.trim() === '' ||
|
||
!item.value.jhnr || item.value.jhnr.trim() === '') {
|
||
uni.showToast({
|
||
title: `教学计划第${i + 1}项信息不完整`,
|
||
icon: 'none',
|
||
duration: 2000
|
||
});
|
||
return;
|
||
}
|
||
}
|
||
|
||
// 直接使用v-model绑定的值,不需要调用getValue()
|
||
// xkkc.value.kcjsms 和 xkkc.value.jxll 已经通过v-model绑定
|
||
xkkc.value.jxjh = JSON.stringify(education.xl)
|
||
|
||
await jsdXkkcSaveApi(xkkc.value).then(res => {
|
||
uni.showToast({
|
||
title: '提交成功',
|
||
icon: 'success',
|
||
duration: 2000
|
||
})
|
||
|
||
// 提交成功后延迟返回列表页
|
||
setTimeout(() => {
|
||
uni.navigateBack({
|
||
delta: 1
|
||
});
|
||
}, 1500); // 延迟1.5秒,让用户看到成功提示
|
||
|
||
}).catch(err => {
|
||
uni.showToast({
|
||
title: '提交失败',
|
||
icon: 'error',
|
||
duration: 2000
|
||
})
|
||
});
|
||
}
|
||
// 初始化
|
||
onMounted(() => {
|
||
console.log('页面初始化开始...');
|
||
xkkc.value = getData;
|
||
setValue(getData);
|
||
|
||
// 处理教学计划数据回显
|
||
if (xkkc.value.jxjh) {
|
||
try {
|
||
// 如果jxjh是字符串,先解析
|
||
let jxjhData = xkkc.value.jxjh;
|
||
if (typeof jxjhData === 'string') {
|
||
jxjhData = JSON.parse(jxjhData);
|
||
}
|
||
|
||
// 检查数据结构并转换为正确的格式
|
||
if (Array.isArray(jxjhData)) {
|
||
education.xl = jxjhData.map(item => {
|
||
// 确保每个item都有value属性
|
||
if (item.value) {
|
||
return {
|
||
value: {
|
||
jhjd: item.value.jhjd || '',
|
||
jhsj: item.value.jhsj || '',
|
||
jhdd: item.value.jhdd || '',
|
||
jhnr: item.value.jhnr || ''
|
||
}
|
||
};
|
||
}
|
||
return item;
|
||
});
|
||
console.log('教学计划数据回显成功:', education.xl);
|
||
} else {
|
||
education.xl = [];
|
||
console.log('教学计划数据格式不正确,初始化为空数组');
|
||
}
|
||
} catch (error) {
|
||
console.error('解析教学计划数据失败:', error);
|
||
education.xl = [];
|
||
}
|
||
} else {
|
||
education.xl = [];
|
||
console.log('没有教学计划数据,初始化为空数组');
|
||
}
|
||
|
||
console.log('调用getTeacherHeadPic前,teacherHeadPic值:', teacherHeadPic.value);
|
||
getTeacherHeadPic(); // 调用获取教师头像的函数
|
||
console.log('调用getTeacherHeadPic后,teacherHeadPic值:', teacherHeadPic.value);
|
||
|
||
// 添加调试信息
|
||
setTimeout(() => {
|
||
console.log('延迟检查teacherHeadPic值:', teacherHeadPic.value);
|
||
console.log('teacherHeadPic是否存在:', !!teacherHeadPic.value);
|
||
console.log('当前教学计划数据:', education.xl);
|
||
}, 1000);
|
||
})
|
||
</script>
|
||
<style lang="scss">
|
||
.delete-icon {
|
||
position: absolute;
|
||
right: 0;
|
||
top: 0;
|
||
z-index: 1;
|
||
}
|
||
|
||
.course-info {
|
||
flex: 1;
|
||
|
||
.course-name {
|
||
font-size: 16px;
|
||
font-weight: 500;
|
||
color: #333;
|
||
margin-bottom: 10px;
|
||
}
|
||
.course-info-item {
|
||
display: flex;
|
||
margin-bottom: 12px;
|
||
font-size: 12px;
|
||
align-items: center;
|
||
|
||
.info-label {
|
||
color: #949AA4;
|
||
flex: 0 0 auto;
|
||
margin-right: 4px;
|
||
white-space: nowrap;
|
||
}
|
||
.info-data {
|
||
flex: 0 0 auto;
|
||
color: #333;
|
||
font-weight: 400;
|
||
}
|
||
}
|
||
}
|
||
|
||
.course-image-section {
|
||
flex: 0 0 100px;
|
||
margin-right: 15px;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
|
||
.course-image {
|
||
width: 80px;
|
||
height: 80px;
|
||
border-radius: 8px;
|
||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
|
||
border: 1px solid #f0f0f0;
|
||
}
|
||
}
|
||
|
||
.teacher-name {
|
||
vertical-align: middle;
|
||
}
|
||
|
||
.teacher-info-header {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 15px;
|
||
margin-bottom: 15px;
|
||
}
|
||
|
||
.teacher-details {
|
||
flex: 1;
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: 10px;
|
||
}
|
||
|
||
.detail-row {
|
||
display: flex;
|
||
gap: 20px;
|
||
flex-wrap: wrap;
|
||
}
|
||
|
||
.detail-item {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 5px;
|
||
white-space: nowrap;
|
||
}
|
||
|
||
.detail-label {
|
||
font-size: 12px;
|
||
color: #666;
|
||
font-weight: 500;
|
||
}
|
||
|
||
.detail-value {
|
||
font-size: 14px;
|
||
color: #333;
|
||
font-weight: 400;
|
||
}
|
||
|
||
.teacher-input-row {
|
||
margin-top: 15px;
|
||
}
|
||
|
||
.add-icon {
|
||
cursor: pointer;
|
||
color: #007aff;
|
||
}
|
||
|
||
.teacher-avatar-section {
|
||
flex: 0 0 60px;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
|
||
.teacher-avatar {
|
||
width: 50px;
|
||
height: 50px;
|
||
border-radius: 50%;
|
||
border: 2px solid #e0e0e0;
|
||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
|
||
}
|
||
}
|
||
|
||
.teacher-input-section {
|
||
flex: 1;
|
||
}
|
||
|
||
.section-card {
|
||
background: white;
|
||
border-radius: 8px;
|
||
padding: 15px;
|
||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
|
||
}
|
||
|
||
.section-title {
|
||
font-size: 16px;
|
||
font-weight: 600;
|
||
color: #333;
|
||
margin-bottom: 15px;
|
||
padding-left: 8px;
|
||
border-left: 3px solid #007aff;
|
||
|
||
.required {
|
||
color: #f56c6c;
|
||
font-weight: 700;
|
||
margin-left: 4px;
|
||
}
|
||
}
|
||
|
||
.section-header {
|
||
display: flex;
|
||
justify-content: space-between;
|
||
align-items: center;
|
||
margin-bottom: 15px;
|
||
}
|
||
|
||
.form-textarea {
|
||
width: 100%; /* 恢复为100%宽度 */
|
||
min-height: 80px;
|
||
padding: 12px 15px; /* 左右padding保持15px */
|
||
border: 1px solid #e0e0e0;
|
||
border-radius: 6px;
|
||
font-size: 14px;
|
||
line-height: 1.5;
|
||
resize: vertical;
|
||
box-sizing: border-box; /* 确保padding和border包含在宽度内 */
|
||
|
||
&:focus {
|
||
outline: none;
|
||
border-color: #007aff;
|
||
box-shadow: 0 0 0 2px rgba(0, 122, 255, 0.1);
|
||
}
|
||
|
||
&::placeholder {
|
||
color: #999;
|
||
}
|
||
}
|
||
</style>
|