732 lines
16 KiB
Vue
732 lines
16 KiB
Vue
<!-- src/pages/view/routine/yishiyice/addkc.vue -->
|
||
<template>
|
||
<view class="add-course-page">
|
||
<!-- 提交遮罩层 -->
|
||
<view v-if="isSubmitting" class="submit-overlay">
|
||
<view class="submit-loading">
|
||
<view class="loading-spinner"></view>
|
||
<text class="loading-text">保存中...</text>
|
||
</view>
|
||
</view>
|
||
|
||
<view class="form-container">
|
||
<scroll-view scroll-y class="form-scroll">
|
||
<!-- 基本信息 -->
|
||
<view class="section-card">
|
||
<view class="section-title">
|
||
<text class="title-text">基本信息</text>
|
||
</view>
|
||
|
||
<!-- 课程名称 -->
|
||
<view class="form-item">
|
||
<text class="item-label required">课程名称</text>
|
||
<input
|
||
v-model="formData.kcmc"
|
||
class="item-input"
|
||
placeholder="请输入课程名称"
|
||
maxlength="100"
|
||
/>
|
||
</view>
|
||
|
||
<!-- 课程描述 -->
|
||
<view class="form-item">
|
||
<text class="item-label required">课程描述</text>
|
||
<textarea
|
||
v-model="formData.kcms"
|
||
class="item-textarea"
|
||
placeholder="请输入课程描述"
|
||
maxlength="500"
|
||
:show-count="true"
|
||
/>
|
||
</view>
|
||
|
||
<!-- 课程缩略图 -->
|
||
<view class="form-item">
|
||
<text class="item-label">课程缩略图</text>
|
||
<view class="upload-section">
|
||
<view class="upload-list">
|
||
<view v-if="thumbnailImage" class="upload-item">
|
||
<image
|
||
:src="thumbnailImage.url ? imagUrl(thumbnailImage.url) : thumbnailImage.tempPath"
|
||
mode="aspectFill"
|
||
class="upload-image"
|
||
@click="previewThumbnail"
|
||
/>
|
||
<view class="upload-delete" @click="deleteThumbnail">
|
||
<text class="delete-icon">×</text>
|
||
</view>
|
||
</view>
|
||
<view
|
||
v-if="!thumbnailImage"
|
||
class="upload-add"
|
||
@click="chooseThumbnail"
|
||
>
|
||
<text class="add-icon">+</text>
|
||
<text class="add-text">添加缩略图</text>
|
||
</view>
|
||
</view>
|
||
<view class="upload-tip">建议尺寸:800×600像素</view>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 教师设置 -->
|
||
<view class="section-card">
|
||
<view class="section-title">
|
||
<text class="title-text">教师设置</text>
|
||
</view>
|
||
|
||
<!-- 课程导师(上课老师) -->
|
||
<view class="form-item">
|
||
<text class="item-label required">课程导师</text>
|
||
<BasicJsPicker
|
||
:multiple="false"
|
||
title="选择课程导师"
|
||
placeholder="请选择课程导师"
|
||
searchPlaceholder="输入教师名称查询"
|
||
@change="onTeacherChange"
|
||
:defaultValue="formData.jsId ? [formData.jsId] : []"
|
||
/>
|
||
</view>
|
||
|
||
<!-- 班主任 -->
|
||
<view class="form-item">
|
||
<text class="item-label">班主任</text>
|
||
<BasicJsPicker
|
||
:multiple="true"
|
||
title="选择班主任"
|
||
placeholder="请选择班主任"
|
||
searchPlaceholder="输入教师名称查询"
|
||
@change="onBzrChange"
|
||
:defaultValue="selectedBzrIds"
|
||
/>
|
||
</view>
|
||
|
||
<!-- 校级联系人 -->
|
||
<view class="form-item">
|
||
<text class="item-label">校级联系人</text>
|
||
<BasicJsPicker
|
||
:multiple="false"
|
||
title="选择校级联系人"
|
||
placeholder="请选择校级联系人"
|
||
searchPlaceholder="输入教师名称查询"
|
||
@change="onLjlxChange"
|
||
:defaultValue="formData.ljlxId ? [formData.ljlxId] : []"
|
||
/>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 发布设置 -->
|
||
<view class="section-card">
|
||
<view class="section-title">
|
||
<text class="title-text">发布设置</text>
|
||
</view>
|
||
|
||
<!-- 是否发布 -->
|
||
<view class="form-item">
|
||
<text class="item-label required">发布状态</text>
|
||
<picker
|
||
:range="publishOptions"
|
||
range-key="label"
|
||
:value="getPublishIndex()"
|
||
@change="onPublishChange"
|
||
class="item-picker"
|
||
>
|
||
<view class="picker-content">
|
||
<text class="picker-text">{{ getPublishLabel() }}</text>
|
||
<text class="picker-arrow">▼</text>
|
||
</view>
|
||
</picker>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 底部留白,避免被按钮遮挡 -->
|
||
<view style="height: 100px;"></view>
|
||
</scroll-view>
|
||
</view>
|
||
|
||
<!-- 底部操作按钮 -->
|
||
<view class="bottom-actions">
|
||
<view class="action-buttons">
|
||
<button @click="goBack" class="cancel-btn">取消</button>
|
||
<button @click="handleSubmit" :disabled="isSubmitting" class="submit-btn">
|
||
{{ isSubmitting ? '保存中...' : '保存' }}
|
||
</button>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
</template>
|
||
|
||
<script lang="ts" setup>
|
||
import { ref, reactive } from 'vue';
|
||
import { kcjbSaveApi } from '@/api/base/kcjbApi';
|
||
import { attachmentUpload } from '@/api/system/upload';
|
||
import { imagUrl } from '@/utils';
|
||
import BasicJsPicker from '@/components/BasicForm/components/BasicJsPicker.vue';
|
||
|
||
// 图片项类型
|
||
interface ImageItem {
|
||
tempPath?: string;
|
||
url?: string;
|
||
name?: string;
|
||
}
|
||
|
||
// 表单数据
|
||
const formData = reactive({
|
||
kcmc: '', // 课程名称
|
||
kcms: '', // 课程描述
|
||
jsId: '', // 上课老师ID
|
||
jsxm: '', // 上课老师姓名
|
||
bzrId: '', // 班主任ID(逗号分隔)
|
||
bzrxm: '', // 班主任姓名(逗号分隔)
|
||
ljlxId: '', // 校级联系人ID
|
||
ljlxxm: '', // 校级联系人姓名
|
||
kcjbtp: '', // 课程缩略图
|
||
kczt: 1 // 是否发布:1已发布,0未发布
|
||
});
|
||
|
||
// 缩略图
|
||
const thumbnailImage = ref<ImageItem | null>(null);
|
||
|
||
// 班主任选中的ID数组
|
||
const selectedBzrIds = ref<string[]>([]);
|
||
|
||
// 提交状态
|
||
const isSubmitting = ref(false);
|
||
|
||
// 发布状态选项
|
||
const publishOptions = [
|
||
{ label: '已发布', value: 1 },
|
||
{ label: '未发布', value: 0 }
|
||
];
|
||
|
||
// 获取发布状态索引
|
||
const getPublishIndex = () => {
|
||
return publishOptions.findIndex(item => item.value === formData.kczt);
|
||
};
|
||
|
||
// 获取发布状态标签
|
||
const getPublishLabel = () => {
|
||
const option = publishOptions.find(item => item.value === formData.kczt);
|
||
return option ? option.label : '已发布';
|
||
};
|
||
|
||
// 发布状态改变
|
||
const onPublishChange = (e: any) => {
|
||
const index = e.detail.value;
|
||
formData.kczt = publishOptions[index].value;
|
||
};
|
||
|
||
// 课程导师改变
|
||
const onTeacherChange = (selected: any[]) => {
|
||
console.log('课程导师选择:', selected);
|
||
if (selected && selected.length > 0) {
|
||
const teacher = selected[0];
|
||
formData.jsId = teacher.id;
|
||
formData.jsxm = teacher.jsxm;
|
||
} else {
|
||
formData.jsId = '';
|
||
formData.jsxm = '';
|
||
}
|
||
};
|
||
|
||
// 班主任改变
|
||
const onBzrChange = (selected: any[]) => {
|
||
console.log('班主任选择:', selected);
|
||
if (selected && selected.length > 0) {
|
||
selectedBzrIds.value = selected.map(item => item.id);
|
||
formData.bzrId = selected.map(item => item.id).join(',');
|
||
formData.bzrxm = selected.map(item => item.jsxm).join(',');
|
||
} else {
|
||
selectedBzrIds.value = [];
|
||
formData.bzrId = '';
|
||
formData.bzrxm = '';
|
||
}
|
||
};
|
||
|
||
// 校级联系人改变
|
||
const onLjlxChange = (selected: any[]) => {
|
||
console.log('校级联系人选择:', selected);
|
||
if (selected && selected.length > 0) {
|
||
const contact = selected[0];
|
||
formData.ljlxId = contact.id;
|
||
formData.ljlxxm = contact.jsxm;
|
||
} else {
|
||
formData.ljlxId = '';
|
||
formData.ljlxxm = '';
|
||
}
|
||
};
|
||
|
||
// 选择缩略图
|
||
const chooseThumbnail = () => {
|
||
uni.chooseImage({
|
||
count: 1,
|
||
sizeType: ['compressed'],
|
||
sourceType: ['album', 'camera'],
|
||
success: (res) => {
|
||
const tempPath = res.tempFilePaths[0];
|
||
thumbnailImage.value = { tempPath };
|
||
|
||
// 立即上传
|
||
uploadThumbnail(tempPath);
|
||
},
|
||
fail: (err) => {
|
||
console.error('选择图片失败:', err);
|
||
uni.showToast({
|
||
title: '选择图片失败',
|
||
icon: 'none'
|
||
});
|
||
}
|
||
});
|
||
};
|
||
|
||
// 上传缩略图
|
||
const uploadThumbnail = async (filePath: string) => {
|
||
uni.showLoading({ title: '上传中...' });
|
||
|
||
try {
|
||
const result = await attachmentUpload(filePath);
|
||
console.log('缩略图上传成功:', result);
|
||
|
||
if (thumbnailImage.value) {
|
||
thumbnailImage.value.url = result.url || result;
|
||
formData.kcjbtp = result.url || result;
|
||
}
|
||
|
||
uni.hideLoading();
|
||
uni.showToast({
|
||
title: '上传成功',
|
||
icon: 'success'
|
||
});
|
||
} catch (error) {
|
||
console.error('缩略图上传失败:', error);
|
||
uni.hideLoading();
|
||
uni.showToast({
|
||
title: '上传失败',
|
||
icon: 'error'
|
||
});
|
||
|
||
// 上传失败,清除缩略图
|
||
thumbnailImage.value = null;
|
||
}
|
||
};
|
||
|
||
// 删除缩略图
|
||
const deleteThumbnail = () => {
|
||
uni.showModal({
|
||
title: '提示',
|
||
content: '确定删除该缩略图吗?',
|
||
success: (res) => {
|
||
if (res.confirm) {
|
||
thumbnailImage.value = null;
|
||
formData.kcjbtp = '';
|
||
}
|
||
}
|
||
});
|
||
};
|
||
|
||
// 预览缩略图
|
||
const previewThumbnail = () => {
|
||
if (!thumbnailImage.value) return;
|
||
|
||
const url = thumbnailImage.value.url
|
||
? imagUrl(thumbnailImage.value.url)
|
||
: thumbnailImage.value.tempPath;
|
||
|
||
if (url) {
|
||
uni.previewImage({
|
||
urls: [url],
|
||
current: 0
|
||
});
|
||
}
|
||
};
|
||
|
||
// 表单验证
|
||
const validateForm = () => {
|
||
if (!formData.kcmc.trim()) {
|
||
uni.showToast({
|
||
title: '请输入课程名称',
|
||
icon: 'none'
|
||
});
|
||
return false;
|
||
}
|
||
|
||
if (!formData.kcms.trim()) {
|
||
uni.showToast({
|
||
title: '请输入课程描述',
|
||
icon: 'none'
|
||
});
|
||
return false;
|
||
}
|
||
|
||
if (!formData.jsId) {
|
||
uni.showToast({
|
||
title: '请选择课程导师',
|
||
icon: 'none'
|
||
});
|
||
return false;
|
||
}
|
||
|
||
return true;
|
||
};
|
||
|
||
// 提交表单
|
||
const handleSubmit = async () => {
|
||
if (!validateForm()) {
|
||
return;
|
||
}
|
||
|
||
isSubmitting.value = true;
|
||
|
||
try {
|
||
console.log('提交课程数据:', formData);
|
||
|
||
const submitData = {
|
||
kcmc: formData.kcmc,
|
||
kcms: formData.kcms,
|
||
jsId: formData.jsId,
|
||
jsxm: formData.jsxm,
|
||
bzrId: formData.bzrId,
|
||
bzrxm: formData.bzrxm,
|
||
ljlxId: formData.ljlxId,
|
||
ljlxxm: formData.ljlxxm,
|
||
kcjbtp: formData.kcjbtp,
|
||
kczt: formData.kczt
|
||
};
|
||
|
||
await kcjbSaveApi(submitData);
|
||
|
||
uni.showToast({
|
||
title: '保存成功',
|
||
icon: 'success',
|
||
duration: 1500
|
||
});
|
||
|
||
setTimeout(() => {
|
||
uni.navigateBack();
|
||
}, 1500);
|
||
} catch (error) {
|
||
console.error('保存课程失败:', error);
|
||
uni.showToast({
|
||
title: '保存失败,请重试',
|
||
icon: 'error'
|
||
});
|
||
} finally {
|
||
isSubmitting.value = false;
|
||
}
|
||
};
|
||
|
||
// 返回
|
||
const goBack = () => {
|
||
uni.navigateBack();
|
||
};
|
||
</script>
|
||
|
||
<style lang="scss" scoped>
|
||
.add-course-page {
|
||
min-height: 100vh;
|
||
background-color: #f5f7fa;
|
||
padding-bottom: 80px;
|
||
}
|
||
|
||
// 提交遮罩层
|
||
.submit-overlay {
|
||
position: fixed;
|
||
top: 0;
|
||
left: 0;
|
||
right: 0;
|
||
bottom: 0;
|
||
background-color: rgba(0, 0, 0, 0.6);
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
z-index: 9999;
|
||
|
||
.submit-loading {
|
||
background-color: rgba(0, 0, 0, 0.8);
|
||
padding: 30px 40px;
|
||
border-radius: 12px;
|
||
display: flex;
|
||
flex-direction: column;
|
||
align-items: center;
|
||
gap: 15px;
|
||
|
||
.loading-spinner {
|
||
width: 40px;
|
||
height: 40px;
|
||
border: 3px solid rgba(255, 255, 255, 0.3);
|
||
border-top-color: #fff;
|
||
border-radius: 50%;
|
||
animation: spin 0.8s linear infinite;
|
||
}
|
||
|
||
.loading-text {
|
||
color: #fff;
|
||
font-size: 14px;
|
||
}
|
||
}
|
||
}
|
||
|
||
@keyframes spin {
|
||
to {
|
||
transform: rotate(360deg);
|
||
}
|
||
}
|
||
|
||
// 表单容器
|
||
.form-container {
|
||
flex: 1;
|
||
|
||
.form-scroll {
|
||
height: calc(100vh - 80px);
|
||
padding: 15px;
|
||
}
|
||
}
|
||
|
||
// 区块卡片
|
||
.section-card {
|
||
background-color: #ffffff;
|
||
border-radius: 12px;
|
||
padding: 20px;
|
||
margin-bottom: 15px;
|
||
box-shadow: 0 2px 12px rgba(0, 0, 0, 0.08);
|
||
|
||
.section-title {
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: space-between;
|
||
margin-bottom: 20px;
|
||
padding-bottom: 12px;
|
||
border-bottom: 2px solid #f0f0f0;
|
||
|
||
.title-text {
|
||
font-size: 16px;
|
||
font-weight: 600;
|
||
color: #2c3e50;
|
||
position: relative;
|
||
padding-left: 12px;
|
||
|
||
&::before {
|
||
content: '';
|
||
position: absolute;
|
||
left: 0;
|
||
top: 50%;
|
||
transform: translateY(-50%);
|
||
width: 4px;
|
||
height: 16px;
|
||
background: linear-gradient(135deg, #007aff 0%, #0056cc 100%);
|
||
border-radius: 2px;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
// 表单项
|
||
.form-item {
|
||
margin-bottom: 20px;
|
||
|
||
&:last-child {
|
||
margin-bottom: 0;
|
||
}
|
||
|
||
.item-label {
|
||
display: block;
|
||
font-size: 14px;
|
||
color: #606266;
|
||
margin-bottom: 10px;
|
||
font-weight: 500;
|
||
|
||
&.required::before {
|
||
content: '*';
|
||
color: #f56c6c;
|
||
margin-right: 4px;
|
||
}
|
||
}
|
||
|
||
.item-input {
|
||
width: 100%;
|
||
padding: 12px 15px;
|
||
background-color: #f8f9fa;
|
||
border: 1px solid #e9ecef;
|
||
border-radius: 8px;
|
||
font-size: 14px;
|
||
color: #333;
|
||
box-sizing: border-box;
|
||
|
||
&::placeholder {
|
||
color: #c0c4cc;
|
||
}
|
||
}
|
||
|
||
.item-textarea {
|
||
width: 100%;
|
||
min-height: 120px;
|
||
padding: 12px 15px;
|
||
background-color: #f8f9fa;
|
||
border: 1px solid #e9ecef;
|
||
border-radius: 8px;
|
||
font-size: 14px;
|
||
color: #333;
|
||
box-sizing: border-box;
|
||
|
||
&::placeholder {
|
||
color: #c0c4cc;
|
||
}
|
||
}
|
||
|
||
.item-picker {
|
||
width: 100%;
|
||
|
||
.picker-content {
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: space-between;
|
||
padding: 12px 15px;
|
||
background-color: #f8f9fa;
|
||
border: 1px solid #e9ecef;
|
||
border-radius: 8px;
|
||
|
||
.picker-text {
|
||
font-size: 14px;
|
||
color: #333;
|
||
}
|
||
|
||
.picker-arrow {
|
||
font-size: 12px;
|
||
color: #909399;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
// 上传区域
|
||
.upload-section {
|
||
.upload-list {
|
||
display: flex;
|
||
flex-wrap: wrap;
|
||
gap: 10px;
|
||
}
|
||
|
||
.upload-item {
|
||
position: relative;
|
||
width: 100px;
|
||
height: 100px;
|
||
border-radius: 8px;
|
||
overflow: hidden;
|
||
|
||
.upload-image {
|
||
width: 100%;
|
||
height: 100%;
|
||
object-fit: cover;
|
||
}
|
||
|
||
.upload-delete {
|
||
position: absolute;
|
||
top: 0;
|
||
right: 0;
|
||
width: 24px;
|
||
height: 24px;
|
||
background-color: rgba(0, 0, 0, 0.6);
|
||
border-radius: 0 8px 0 8px;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
|
||
.delete-icon {
|
||
color: #fff;
|
||
font-size: 18px;
|
||
font-weight: bold;
|
||
}
|
||
}
|
||
}
|
||
|
||
.upload-add {
|
||
width: 100px;
|
||
height: 100px;
|
||
border: 2px dashed #dcdfe6;
|
||
border-radius: 8px;
|
||
display: flex;
|
||
flex-direction: column;
|
||
align-items: center;
|
||
justify-content: center;
|
||
background-color: #fafafa;
|
||
cursor: pointer;
|
||
|
||
.add-icon {
|
||
font-size: 32px;
|
||
color: #909399;
|
||
margin-bottom: 5px;
|
||
}
|
||
|
||
.add-text {
|
||
font-size: 12px;
|
||
color: #909399;
|
||
}
|
||
|
||
&:active {
|
||
background-color: #f0f0f0;
|
||
}
|
||
}
|
||
|
||
.upload-tip {
|
||
margin-top: 8px;
|
||
font-size: 12px;
|
||
color: #909399;
|
||
}
|
||
}
|
||
|
||
// 底部操作按钮
|
||
.bottom-actions {
|
||
position: fixed;
|
||
bottom: 0;
|
||
left: 0;
|
||
right: 0;
|
||
background-color: #fff;
|
||
padding: 15px;
|
||
box-shadow: 0 -2px 10px rgba(0, 0, 0, 0.1);
|
||
z-index: 100;
|
||
|
||
.action-buttons {
|
||
display: flex;
|
||
gap: 15px;
|
||
|
||
button {
|
||
flex: 1;
|
||
height: 44px;
|
||
border-radius: 8px;
|
||
font-size: 16px;
|
||
font-weight: 500;
|
||
border: none;
|
||
|
||
&::after {
|
||
border: none;
|
||
}
|
||
}
|
||
|
||
.cancel-btn {
|
||
background-color: #f5f7fa;
|
||
color: #606266;
|
||
|
||
&:active {
|
||
background-color: #e9ecef;
|
||
}
|
||
}
|
||
|
||
.submit-btn {
|
||
background: linear-gradient(135deg, #007aff 0%, #0056cc 100%);
|
||
color: #fff;
|
||
box-shadow: 0 4px 16px rgba(0, 122, 255, 0.3);
|
||
|
||
&:active {
|
||
opacity: 0.9;
|
||
}
|
||
|
||
&:disabled {
|
||
opacity: 0.6;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
</style>
|
||
|