1115 lines
32 KiB
Vue
1115 lines
32 KiB
Vue
|
|
<!-- 作品任务提交页面 -->
|
|||
|
|
<template>
|
|||
|
|
<view class="zp-submit-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 v-if="isLoading" class="loading-indicator">加载中...</view>
|
|||
|
|
<view v-else class="content-wrapper" :class="{ 'full-height': hideBottomBtn }">
|
|||
|
|
<view class="p-15">
|
|||
|
|
<!-- 第一部分:任务要求 -->
|
|||
|
|
<view class="zp-info-section">
|
|||
|
|
<view class="section-title">任务要求</view>
|
|||
|
|
|
|||
|
|
<!-- 任务名称 -->
|
|||
|
|
<view class="info-item">
|
|||
|
|
<text class="label">任务名称:</text>
|
|||
|
|
<text class="value title-bold">{{ zp.zpmc || '作品任务' }}</text>
|
|||
|
|
</view>
|
|||
|
|
|
|||
|
|
<!-- 任务描述 -->
|
|||
|
|
<view v-if="zp.zpms" class="info-item">
|
|||
|
|
<text class="label">任务描述:</text>
|
|||
|
|
<text class="value">{{ zp.zpms }}</text>
|
|||
|
|
</view>
|
|||
|
|
|
|||
|
|
<!-- 任务时间 -->
|
|||
|
|
<view v-if="zp.zpkstime || zp.zpjstime" class="info-item">
|
|||
|
|
<text class="label">任务时间:</text>
|
|||
|
|
<text class="value">{{ formatTimeRange(zp.zpkstime, zp.zpjstime) }}</text>
|
|||
|
|
</view>
|
|||
|
|
|
|||
|
|
<!-- 附件预览 -->
|
|||
|
|
<view v-if="zp.fileUrl" class="file-preview mt-15">
|
|||
|
|
<BasicFilePreview
|
|||
|
|
:file-url="zp.fileUrl"
|
|||
|
|
:file-name="zp.fileName"
|
|||
|
|
:file-format="zp.fileFormat"
|
|||
|
|
/>
|
|||
|
|
</view>
|
|||
|
|
</view>
|
|||
|
|
|
|||
|
|
<!-- 第二部分:任务执行 -->
|
|||
|
|
<view class="zp-execute-section">
|
|||
|
|
<view class="section-title">作品提交</view>
|
|||
|
|
|
|||
|
|
<!-- 动态分组渲染 -->
|
|||
|
|
<view v-for="category in dynamicCategories" :key="category.zpfldm">
|
|||
|
|
<view v-if="groupedSchema[category.zpfldm] && groupedSchema[category.zpfldm].length > 0" class="part-section">
|
|||
|
|
<view class="part-title" :data-debug="`分类标题: ${category.label}`">{{ category.label }}</view>
|
|||
|
|
<view class="execute-form">
|
|||
|
|
<BasicForm :schema="groupedSchema[category.zpfldm]" v-model="formData">
|
|||
|
|
</BasicForm>
|
|||
|
|
</view>
|
|||
|
|
</view>
|
|||
|
|
</view>
|
|||
|
|
|
|||
|
|
<!-- 未分类的任务项 -->
|
|||
|
|
<view v-if="groupedSchema[''] && groupedSchema[''].length > 0" class="part-section">
|
|||
|
|
<view class="execute-form">
|
|||
|
|
<BasicForm :schema="groupedSchema['']" v-model="formData">
|
|||
|
|
</BasicForm>
|
|||
|
|
</view>
|
|||
|
|
</view>
|
|||
|
|
</view>
|
|||
|
|
</view>
|
|||
|
|
</view>
|
|||
|
|
|
|||
|
|
<!-- 提交按钮 - 固定在底部 -->
|
|||
|
|
<view v-if="showSubmitButton" class="submit-button-fixed" :class="{ 'hide-bottom': hideBottomBtn }">
|
|||
|
|
<button class="action-button" @click="saveZpzx" :disabled="isSubmitting">
|
|||
|
|
{{ isSubmitting ? '提交中...' : '提交' }}
|
|||
|
|
</button>
|
|||
|
|
</view>
|
|||
|
|
</view>
|
|||
|
|
</template>
|
|||
|
|
|
|||
|
|
<script lang="ts" setup>
|
|||
|
|
import { ref, computed } from 'vue';
|
|||
|
|
import { onLoad } from '@dcloudio/uni-app';
|
|||
|
|
import { zpFindDetailByIdApi, zpzxSaveApi, zpqdFindByZpzxIdApi } from "@/api/base/server";
|
|||
|
|
import { useUserStore } from "@/store/modules/user";
|
|||
|
|
import { ImageVideoUpload, COMPRESS_PRESETS } from "@/components/ImageVideoUpload";
|
|||
|
|
import { attachmentUpload } from "@/api/system/upload";
|
|||
|
|
import BasicFilePreview from "@/components/BasicFile/preview.vue";
|
|||
|
|
import { imagUrl } from "@/utils";
|
|||
|
|
|
|||
|
|
// 动态分类列表(从任务类型列表中提取)
|
|||
|
|
const dynamicCategories = ref<Array<{ zpfldm: string; label: string }>>([]);
|
|||
|
|
|
|||
|
|
// 从任务类型列表中提取所有不同的 zpfldm 值
|
|||
|
|
const extractCategoriesFromZplxList = () => {
|
|||
|
|
const categoryMap = new Map<string, { zpfldm: string; label: string; minSort: number }>();
|
|||
|
|
|
|||
|
|
zplxList.value.forEach((zplx: ZplxItem, index: number) => {
|
|||
|
|
const zpfldm = (zplx.zpfldm || '').trim();
|
|||
|
|
if (zpfldm) {
|
|||
|
|
// 使用索引作为排序依据(保持原始顺序)
|
|||
|
|
const sort = index;
|
|||
|
|
if (!categoryMap.has(zpfldm) || categoryMap.get(zpfldm)!.minSort > sort) {
|
|||
|
|
categoryMap.set(zpfldm, {
|
|||
|
|
zpfldm: zpfldm,
|
|||
|
|
label: zpfldm,
|
|||
|
|
minSort: sort
|
|||
|
|
});
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
// 转换为数组并按 minSort 排序
|
|||
|
|
const categories = Array.from(categoryMap.values())
|
|||
|
|
.sort((a, b) => {
|
|||
|
|
return a.minSort - b.minSort;
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
dynamicCategories.value = categories.map(cat => ({
|
|||
|
|
zpfldm: cat.zpfldm,
|
|||
|
|
label: cat.label
|
|||
|
|
}));
|
|||
|
|
|
|||
|
|
console.log('提取的分类列表:', dynamicCategories.value);
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
// 按 zpfldm 动态分组 schema
|
|||
|
|
const groupedSchema = computed(() => {
|
|||
|
|
const groups: Record<string, any[]> = {};
|
|||
|
|
|
|||
|
|
// 初始化所有分类的分组
|
|||
|
|
dynamicCategories.value.forEach(category => {
|
|||
|
|
groups[category.zpfldm] = [];
|
|||
|
|
});
|
|||
|
|
// 添加未分类分组
|
|||
|
|
groups[''] = [];
|
|||
|
|
|
|||
|
|
// 将 schema 项分配到对应分组
|
|||
|
|
schema.value.forEach((item: any) => {
|
|||
|
|
const zpfldm = (item.zpfldm || '').trim();
|
|||
|
|
if (groups.hasOwnProperty(zpfldm)) {
|
|||
|
|
groups[zpfldm].push(item);
|
|||
|
|
} else {
|
|||
|
|
// 如果分类不存在,放入未分类
|
|||
|
|
groups[''].push(item);
|
|||
|
|
}
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
return groups;
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
// 接口定义
|
|||
|
|
interface ZpInfo {
|
|||
|
|
id?: string;
|
|||
|
|
zpmc?: string; // 作品名称
|
|||
|
|
zpms?: string; // 作品描述
|
|||
|
|
zpkstime?: string; // 开始时间
|
|||
|
|
zpjstime?: string; // 结束时间
|
|||
|
|
fileUrl?: string; // 附件URL
|
|||
|
|
fileName?: string; // 附件名称
|
|||
|
|
[key: string]: any;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
interface ZplxItem {
|
|||
|
|
id: string;
|
|||
|
|
zpbt?: string; // 作品标题
|
|||
|
|
zpfl?: string; // 作品分类(任务类型)
|
|||
|
|
isbt?: number; // 是否必填
|
|||
|
|
remark?: string; // 备注(选项内容等)
|
|||
|
|
zpfldm?: string; // 部分代码:第一部分/第二部分/第三部分
|
|||
|
|
[key: string]: any;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
interface ZpqdItem {
|
|||
|
|
id?: string;
|
|||
|
|
zplxId?: string; // 作品类型ID
|
|||
|
|
zpqdtx?: string; // 作品清单内容
|
|||
|
|
wjmc?: string; // 文件名
|
|||
|
|
[key: string]: any;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 响应式数据
|
|||
|
|
const formData: any = ref({});
|
|||
|
|
const zpzxId = ref<string>(''); // 作品执行ID
|
|||
|
|
const xsId = ref<string>(''); // 学生ID
|
|||
|
|
const isSubmitting = ref<boolean>(false);
|
|||
|
|
const submitTimer = ref<any>(null);
|
|||
|
|
const showSubmitButton = ref<boolean>(false);
|
|||
|
|
const isLoading = ref(false);
|
|||
|
|
const zplxList = ref<ZplxItem[]>([]);
|
|||
|
|
const zp = ref<ZpInfo>({});
|
|||
|
|
const schema = ref<any[]>([]);
|
|||
|
|
const zpqdList = ref<ZpqdItem[]>([]);
|
|||
|
|
const userStore = useUserStore();
|
|||
|
|
const zpzxData = ref<any>({}); // 作品执行数据,用于获取 ispj 字段
|
|||
|
|
// 从路由参数中获取的状态字段
|
|||
|
|
const routeZpzt = ref<string>(''); // 从路由参数获取的 zpzt
|
|||
|
|
const routeIspj = ref<string>(''); // 从路由参数获取的 ispj
|
|||
|
|
const hideBottomBtn = ref(false); // 控制底部按钮显示/隐藏
|
|||
|
|
|
|||
|
|
// 处理选择器弹窗状态变化
|
|||
|
|
const handlePickerPopupChange = (e: any) => {
|
|||
|
|
// e.show 为 true 表示弹窗打开,false 表示关闭
|
|||
|
|
hideBottomBtn.value = e.show;
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
// 格式化时间范围
|
|||
|
|
const formatTimeRange = (startTime?: string, endTime?: string) => {
|
|||
|
|
if (!startTime && !endTime) return '未设置';
|
|||
|
|
const formatTime = (time: string) => {
|
|||
|
|
if (!time) return '';
|
|||
|
|
const date = new Date(time);
|
|||
|
|
return date.toLocaleDateString('zh-CN', {
|
|||
|
|
year: 'numeric',
|
|||
|
|
month: '2-digit',
|
|||
|
|
day: '2-digit'
|
|||
|
|
});
|
|||
|
|
};
|
|||
|
|
const start = formatTime(startTime || '');
|
|||
|
|
const end = formatTime(endTime || '');
|
|||
|
|
if (start && end) {
|
|||
|
|
return `${start} 至 ${end}`;
|
|||
|
|
} else if (start) {
|
|||
|
|
return `从 ${start} 开始`;
|
|||
|
|
} else if (end) {
|
|||
|
|
return `至 ${end} 结束`;
|
|||
|
|
}
|
|||
|
|
return '未设置';
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
|
|||
|
|
// 页面加载
|
|||
|
|
onLoad(async (options) => {
|
|||
|
|
console.log('作品任务提交页面加载参数:', options);
|
|||
|
|
|
|||
|
|
const zpId = options?.zpId || '';
|
|||
|
|
zpzxId.value = options?.zpzxId || '';
|
|||
|
|
xsId.value = options?.xsId || '';
|
|||
|
|
const kcId = options?.kcId || ''; // 保存课程ID用于返回
|
|||
|
|
routeZpzt.value = options?.zpzt || ''; // 从路由参数获取 zpzt
|
|||
|
|
routeIspj.value = options?.ispj || ''; // 从路由参数获取 ispj
|
|||
|
|
|
|||
|
|
if (!zpId) {
|
|||
|
|
uni.showToast({ title: '缺少任务ID', icon: 'error' });
|
|||
|
|
setTimeout(() => {
|
|||
|
|
uni.navigateBack();
|
|||
|
|
}, 1500);
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 获取学生ID
|
|||
|
|
if (!xsId.value) {
|
|||
|
|
const curXs = userStore.curXs;
|
|||
|
|
xsId.value = curXs?.id || '';
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if (!xsId.value) {
|
|||
|
|
uni.showToast({ title: '请先选择学生', icon: 'error' });
|
|||
|
|
setTimeout(() => {
|
|||
|
|
uni.navigateBack();
|
|||
|
|
}, 1500);
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
await loadTaskDetail(zpId);
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
// 加载任务详情
|
|||
|
|
const loadTaskDetail = async (zpId: string) => {
|
|||
|
|
try {
|
|||
|
|
isLoading.value = true;
|
|||
|
|
console.log('加载作品任务详情,任务ID:', zpId);
|
|||
|
|
|
|||
|
|
const response: any = await zpFindDetailByIdApi({ id: zpId });
|
|||
|
|
const detailData = response?.result || response?.data || response;
|
|||
|
|
|
|||
|
|
if (!detailData) {
|
|||
|
|
throw new Error('未找到任务数据');
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 1. 填充任务基本信息
|
|||
|
|
const zpData = detailData.zp || {};
|
|||
|
|
zp.value = zpData;
|
|||
|
|
|
|||
|
|
// 2. 保存作品执行数据(用于获取 ispj 字段)
|
|||
|
|
zpzxData.value = detailData.zpzx || {};
|
|||
|
|
|
|||
|
|
// 3. 加载任务类型列表(作品类型列表)
|
|||
|
|
zplxList.value = detailData.zplxList || [];
|
|||
|
|
console.log('任务类型列表:', zplxList.value);
|
|||
|
|
|
|||
|
|
// 4. 提取分类信息
|
|||
|
|
extractCategoriesFromZplxList();
|
|||
|
|
|
|||
|
|
// 5. 生成表单schema
|
|||
|
|
generateFormSchema();
|
|||
|
|
|
|||
|
|
// 6. 加载已提交的作品清单数据
|
|||
|
|
await loadZpqdList();
|
|||
|
|
|
|||
|
|
} catch (error) {
|
|||
|
|
console.error('加载任务详情失败:', error);
|
|||
|
|
uni.showToast({
|
|||
|
|
title: error instanceof Error ? error.message : '加载失败,请重试',
|
|||
|
|
icon: 'error'
|
|||
|
|
});
|
|||
|
|
} finally {
|
|||
|
|
isLoading.value = false;
|
|||
|
|
}
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
// 生成表单schema
|
|||
|
|
const generateFormSchema = () => {
|
|||
|
|
schema.value = [];
|
|||
|
|
|
|||
|
|
for (let i = 0; i < zplxList.value.length; i++) {
|
|||
|
|
const zplx = zplxList.value[i];
|
|||
|
|
const fieldId = zplx.id;
|
|||
|
|
|
|||
|
|
console.log(`任务项 ${i + 1}:`, {
|
|||
|
|
zpbt: zplx.zpbt,
|
|||
|
|
zpfl: zplx.zpfl,
|
|||
|
|
isbt: zplx.isbt,
|
|||
|
|
remark: zplx.remark,
|
|||
|
|
zpfldm: zplx.zpfldm
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
// 根据作品分类(任务类型)生成不同的表单组件
|
|||
|
|
if (zplx.zpfl === "sctp" || zplx.zpfl === "scsp" || zplx.zpfl === "scwd") {
|
|||
|
|
// 上传类型:图片、视频、文档
|
|||
|
|
const fieldName = zplx.zpfl === "sctp" ? `${fieldId}_imageList` :
|
|||
|
|
zplx.zpfl === "scsp" ? `${fieldId}_videoList` :
|
|||
|
|
`${fieldId}_fileList`;
|
|||
|
|
|
|||
|
|
// 初始化表单数据
|
|||
|
|
formData.value[fieldName] = [];
|
|||
|
|
|
|||
|
|
let componentConfig: any = {};
|
|||
|
|
|
|||
|
|
if (zplx.zpfl === "sctp") {
|
|||
|
|
// 上传图片
|
|||
|
|
componentConfig = {
|
|||
|
|
component: "ImageVideoUpload",
|
|||
|
|
componentProps: {
|
|||
|
|
enableImage: true,
|
|||
|
|
enableVideo: false,
|
|||
|
|
enableFile: false,
|
|||
|
|
maxImageCount: 30,
|
|||
|
|
uploadApi: attachmentUpload,
|
|||
|
|
compressConfig: COMPRESS_PRESETS.medium,
|
|||
|
|
imageList: formData.value[fieldName] || [],
|
|||
|
|
showSectionTitle: false
|
|||
|
|
}
|
|||
|
|
};
|
|||
|
|
} else if (zplx.zpfl === "scsp") {
|
|||
|
|
// 上传视频
|
|||
|
|
componentConfig = {
|
|||
|
|
component: "ImageVideoUpload",
|
|||
|
|
componentProps: {
|
|||
|
|
enableImage: false,
|
|||
|
|
enableVideo: true,
|
|||
|
|
enableFile: false,
|
|||
|
|
maxVideoCount: 30,
|
|||
|
|
uploadApi: attachmentUpload,
|
|||
|
|
compressConfig: COMPRESS_PRESETS.medium,
|
|||
|
|
videoList: formData.value[fieldName] || [],
|
|||
|
|
showSectionTitle: false
|
|||
|
|
}
|
|||
|
|
};
|
|||
|
|
} else if (zplx.zpfl === "scwd") {
|
|||
|
|
// 上传文档
|
|||
|
|
componentConfig = {
|
|||
|
|
component: "ImageVideoUpload",
|
|||
|
|
componentProps: {
|
|||
|
|
enableImage: false,
|
|||
|
|
enableVideo: false,
|
|||
|
|
enableFile: true,
|
|||
|
|
maxFileCount: 30,
|
|||
|
|
allowedFileTypes: ['pdf', 'doc', 'docx', 'xls', 'xlsx', 'ppt', 'pptx', 'txt', 'mp3', 'wav', 'zip', 'rar'],
|
|||
|
|
uploadApi: attachmentUpload,
|
|||
|
|
compressConfig: COMPRESS_PRESETS.medium,
|
|||
|
|
fileList: formData.value[fieldName] || [],
|
|||
|
|
showSectionTitle: false
|
|||
|
|
}
|
|||
|
|
};
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
const labelText = zplx.zpbt || '作品上传';
|
|||
|
|
console.log('生成表单字段 - zpbt:', labelText, '长度:', labelText.length);
|
|||
|
|
schema.value.push({
|
|||
|
|
field: fieldName,
|
|||
|
|
label: labelText,
|
|||
|
|
required: !!(zplx.isbt === 1 || zplx.isbt === true),
|
|||
|
|
zpfldm: zplx.zpfldm || '',
|
|||
|
|
itemProps: {
|
|||
|
|
labelPosition: "top",
|
|||
|
|
},
|
|||
|
|
...componentConfig
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
} else if (zplx.zpfl === "fwb") {
|
|||
|
|
// 富文本类型
|
|||
|
|
const labelText = zplx.zpbt || '作品描述';
|
|||
|
|
console.log('生成表单字段 - zpbt:', labelText, '长度:', labelText.length);
|
|||
|
|
schema.value.push({
|
|||
|
|
field: fieldId,
|
|||
|
|
label: labelText,
|
|||
|
|
component: "BasicEditor",
|
|||
|
|
required: !!(zplx.isbt === 1 || zplx.isbt === true),
|
|||
|
|
zpfldm: zplx.zpfldm || '',
|
|||
|
|
itemProps: {
|
|||
|
|
labelPosition: "top",
|
|||
|
|
},
|
|||
|
|
componentProps: {
|
|||
|
|
placeholder: "请输入作品描述,支持插入图片"
|
|||
|
|
},
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
} else if (zplx.zpfl === "text") {
|
|||
|
|
// 普通文本类型
|
|||
|
|
const labelText = zplx.zpbt || '作品描述';
|
|||
|
|
console.log('生成表单字段 - zpbt:', labelText, '长度:', labelText.length);
|
|||
|
|
schema.value.push({
|
|||
|
|
field: fieldId,
|
|||
|
|
label: labelText,
|
|||
|
|
component: "BasicInput",
|
|||
|
|
required: !!(zplx.isbt === 1 || zplx.isbt === true),
|
|||
|
|
zpfldm: zplx.zpfldm || '',
|
|||
|
|
itemProps: {
|
|||
|
|
labelPosition: "top",
|
|||
|
|
},
|
|||
|
|
componentProps: {
|
|||
|
|
type: "textarea",
|
|||
|
|
placeholder: "请输入作品描述"
|
|||
|
|
},
|
|||
|
|
});
|
|||
|
|
|
|||
|
|
} else if (zplx.zpfl === "dxsx" || zplx.zpfl === "dxxz") {
|
|||
|
|
// 选择类型:多项选择或单项选择
|
|||
|
|
// 支持中文分号(;)和英文分号(;)分割选项
|
|||
|
|
let options = (zplx.remark || '').split(/[;;]/).filter(Boolean);
|
|||
|
|
let range = options.map(opt => ({ name: opt.trim() }));
|
|||
|
|
|
|||
|
|
const labelText = zplx.zpbt || '作品选择';
|
|||
|
|
console.log('生成表单字段 - zpbt:', labelText, '长度:', labelText.length);
|
|||
|
|
schema.value.push({
|
|||
|
|
field: fieldId,
|
|||
|
|
label: labelText,
|
|||
|
|
component: "BasicPicker",
|
|||
|
|
required: !!(zplx.isbt === 1 || zplx.isbt === true),
|
|||
|
|
zpfldm: zplx.zpfldm || '',
|
|||
|
|
itemProps: {
|
|||
|
|
labelPosition: "top",
|
|||
|
|
},
|
|||
|
|
componentProps: {
|
|||
|
|
range: range,
|
|||
|
|
rangeKey: "name",
|
|||
|
|
savaKey: "name",
|
|||
|
|
onPopupChange: handlePickerPopupChange,
|
|||
|
|
},
|
|||
|
|
});
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
console.log('生成的表单schema:', schema.value);
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
// 加载作品清单数据
|
|||
|
|
const loadZpqdList = async () => {
|
|||
|
|
// 如果已有作品执行ID,查询已提交的数据
|
|||
|
|
if (zpzxId.value) {
|
|||
|
|
try {
|
|||
|
|
const response: any = await zpqdFindByZpzxIdApi({ zpzxId: zpzxId.value });
|
|||
|
|
const pageData = response?.result || response;
|
|||
|
|
|
|||
|
|
if (pageData && pageData.rows) {
|
|||
|
|
zpqdList.value = pageData.rows || [];
|
|||
|
|
} else if (Array.isArray(response)) {
|
|||
|
|
zpqdList.value = response;
|
|||
|
|
} else {
|
|||
|
|
zpqdList.value = [];
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
console.log('查询到的作品清单数据:', zpqdList.value);
|
|||
|
|
|
|||
|
|
// 如果 ispj 为 'A'(已评价),隐藏提交按钮
|
|||
|
|
// 优先使用路由参数中的 ispj,如果没有则使用从后端加载的数据
|
|||
|
|
const ispjValue = routeIspj.value || (zpzxData.value && zpzxData.value.ispj) || '';
|
|||
|
|
const isEvaluated = ispjValue === 'A';
|
|||
|
|
// 只有 ispj 为 'A'(已评价)时,才隐藏提交按钮,不考虑是否有提交数据
|
|||
|
|
showSubmitButton.value = !isEvaluated;
|
|||
|
|
} catch (error) {
|
|||
|
|
console.error('查询作品清单失败:', error);
|
|||
|
|
zpqdList.value = [];
|
|||
|
|
// 检查状态决定是否显示提交按钮
|
|||
|
|
// 只有 ispj 为 'A'(已评价)时,才隐藏提交按钮
|
|||
|
|
const ispjValue = routeIspj.value || (zpzxData.value && zpzxData.value.ispj) || '';
|
|||
|
|
const isEvaluated = ispjValue === 'A';
|
|||
|
|
showSubmitButton.value = !isEvaluated;
|
|||
|
|
}
|
|||
|
|
} else {
|
|||
|
|
// 新任务,检查状态决定是否显示提交按钮
|
|||
|
|
// 只有 ispj 为 'A'(已评价)时,才隐藏提交按钮
|
|||
|
|
const ispjValue = routeIspj.value || (zpzxData.value && zpzxData.value.ispj) || '';
|
|||
|
|
const isEvaluated = ispjValue === 'A';
|
|||
|
|
showSubmitButton.value = !isEvaluated;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 处理数据回显
|
|||
|
|
if (zpqdList.value && zpqdList.value.length > 0) {
|
|||
|
|
handleDataEcho();
|
|||
|
|
}
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
// 处理数据回显
|
|||
|
|
const handleDataEcho = () => {
|
|||
|
|
console.log('开始处理数据回显,zpqdList.value:', zpqdList.value);
|
|||
|
|
const showData: Record<string, any> = {};
|
|||
|
|
|
|||
|
|
for (let i = 0; i < zpqdList.value.length; i++) {
|
|||
|
|
const record: ZpqdItem = zpqdList.value[i];
|
|||
|
|
const zplxId = record.zplxId;
|
|||
|
|
const zpqdtx = record.zpqdtx;
|
|||
|
|
|
|||
|
|
// 查找对应的任务类型
|
|||
|
|
const taskType = zplxList.value.find((item: ZplxItem) => item.id === zplxId);
|
|||
|
|
|
|||
|
|
if (taskType && zplxId) {
|
|||
|
|
if (taskType.zpfl === "sctp") {
|
|||
|
|
// 图片上传类型
|
|||
|
|
if (zpqdtx) {
|
|||
|
|
const urls = zpqdtx.split(',').filter(Boolean);
|
|||
|
|
const names = record.wjmc ? record.wjmc.split(',').filter(Boolean) : [];
|
|||
|
|
const images = urls.map((url: string, index: number) => ({
|
|||
|
|
url: imagUrl(url.trim()),
|
|||
|
|
name: names[index] || url.split('/').pop() || 'image.jpg',
|
|||
|
|
tempPath: undefined
|
|||
|
|
}));
|
|||
|
|
showData[`${zplxId}_imageList`] = images;
|
|||
|
|
}
|
|||
|
|
} else if (taskType.zpfl === "scsp") {
|
|||
|
|
// 视频上传类型
|
|||
|
|
if (zpqdtx) {
|
|||
|
|
const urls = zpqdtx.split(',').filter(Boolean);
|
|||
|
|
const names = record.wjmc ? record.wjmc.split(',').filter(Boolean) : [];
|
|||
|
|
const videos = urls.map((url: string, index: number) => ({
|
|||
|
|
url: imagUrl(url.trim()),
|
|||
|
|
name: names[index] || url.split('/').pop() || 'video.mp4',
|
|||
|
|
tempPath: undefined
|
|||
|
|
}));
|
|||
|
|
showData[`${zplxId}_videoList`] = videos;
|
|||
|
|
}
|
|||
|
|
} else if (taskType.zpfl === "scwd") {
|
|||
|
|
// 文档上传类型
|
|||
|
|
if (zpqdtx) {
|
|||
|
|
const urls = zpqdtx.split(',').filter(Boolean);
|
|||
|
|
const names = record.wjmc ? record.wjmc.split(',').filter(Boolean) : [];
|
|||
|
|
const files = urls.map((url: string, index: number) => ({
|
|||
|
|
url: imagUrl(url.trim()),
|
|||
|
|
name: names[index] || url.split('/').pop() || 'document',
|
|||
|
|
tempPath: undefined,
|
|||
|
|
type: 'document',
|
|||
|
|
extension: url.split('.').pop() || ''
|
|||
|
|
}));
|
|||
|
|
showData[`${zplxId}_fileList`] = files;
|
|||
|
|
}
|
|||
|
|
} else {
|
|||
|
|
// 文本等其他类型
|
|||
|
|
if (zplxId) {
|
|||
|
|
showData[zplxId] = zpqdtx;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 合并已保存的数据
|
|||
|
|
formData.value = { ...formData.value, ...showData };
|
|||
|
|
|
|||
|
|
// 更新 schema 中上传组件的初始值
|
|||
|
|
for (let i = 0; i < schema.value.length; i++) {
|
|||
|
|
const schemaItem = schema.value[i];
|
|||
|
|
if (schemaItem.component === "ImageVideoUpload" && schemaItem.field) {
|
|||
|
|
const fieldName = schemaItem.field;
|
|||
|
|
|
|||
|
|
if (fieldName.includes('_imageList') && formData.value[fieldName]) {
|
|||
|
|
schemaItem.componentProps.imageList = formData.value[fieldName];
|
|||
|
|
} else if (fieldName.includes('_videoList') && formData.value[fieldName]) {
|
|||
|
|
schemaItem.componentProps.videoList = formData.value[fieldName];
|
|||
|
|
} else if (fieldName.includes('_fileList') && formData.value[fieldName]) {
|
|||
|
|
schemaItem.componentProps.fileList = formData.value[fieldName];
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
console.log('数据回显完成 - formData.value:', formData.value);
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
// 提交作品
|
|||
|
|
const saveZpzx = async () => {
|
|||
|
|
if (isSubmitting.value) {
|
|||
|
|
console.log('正在提交中,请勿重复点击');
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if (submitTimer.value) {
|
|||
|
|
clearTimeout(submitTimer.value);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
submitTimer.value = setTimeout(async () => {
|
|||
|
|
await performSubmit();
|
|||
|
|
}, 300);
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
// 执行提交
|
|||
|
|
const performSubmit = async () => {
|
|||
|
|
if (isSubmitting.value) {
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 验证必填项
|
|||
|
|
const result = [];
|
|||
|
|
for (let i = 0; i < zplxList.value.length; i++) {
|
|||
|
|
const zplx = zplxList.value[i];
|
|||
|
|
const fieldId = zplx.id;
|
|||
|
|
let fieldValue = formData.value[fieldId];
|
|||
|
|
|
|||
|
|
// 处理上传类型的任务
|
|||
|
|
let fileNames = '';
|
|||
|
|
if (zplx.zpfl === "sctp" || zplx.zpfl === "scsp" || zplx.zpfl === "scwd") {
|
|||
|
|
const fileUrls: string[] = [];
|
|||
|
|
const names: string[] = [];
|
|||
|
|
|
|||
|
|
if (zplx.zpfl === "sctp") {
|
|||
|
|
const images = formData.value[`${fieldId}_imageList`] || [];
|
|||
|
|
images.forEach((img: any) => {
|
|||
|
|
if (img.url) {
|
|||
|
|
fileUrls.push(img.url);
|
|||
|
|
names.push(img.name || img.url.split('/').pop() || 'image.jpg');
|
|||
|
|
}
|
|||
|
|
});
|
|||
|
|
} else if (zplx.zpfl === "scsp") {
|
|||
|
|
const videos = formData.value[`${fieldId}_videoList`] || [];
|
|||
|
|
videos.forEach((video: any) => {
|
|||
|
|
if (video.url) {
|
|||
|
|
fileUrls.push(video.url);
|
|||
|
|
names.push(video.name || video.url.split('/').pop() || 'video.mp4');
|
|||
|
|
}
|
|||
|
|
});
|
|||
|
|
} else if (zplx.zpfl === "scwd") {
|
|||
|
|
const files = formData.value[`${fieldId}_fileList`] || [];
|
|||
|
|
files.forEach((file: any) => {
|
|||
|
|
if (file.url) {
|
|||
|
|
fileUrls.push(file.url);
|
|||
|
|
names.push(file.name || file.url.split('/').pop() || 'document');
|
|||
|
|
}
|
|||
|
|
});
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
fieldValue = fileUrls.join(',');
|
|||
|
|
fileNames = names.join(',');
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 验证必填项
|
|||
|
|
if (zplx.isbt === 1 || zplx.isbt === true) {
|
|||
|
|
let isEmpty = false;
|
|||
|
|
|
|||
|
|
if (zplx.zpfl === "sctp" || zplx.zpfl === "scsp" || zplx.zpfl === "scwd") {
|
|||
|
|
if (zplx.zpfl === "sctp") {
|
|||
|
|
const images = formData.value[`${fieldId}_imageList`] || [];
|
|||
|
|
isEmpty = images.length === 0;
|
|||
|
|
} else if (zplx.zpfl === "scsp") {
|
|||
|
|
const videos = formData.value[`${fieldId}_videoList`] || [];
|
|||
|
|
isEmpty = videos.length === 0;
|
|||
|
|
} else if (zplx.zpfl === "scwd") {
|
|||
|
|
const files = formData.value[`${fieldId}_fileList`] || [];
|
|||
|
|
isEmpty = files.length === 0;
|
|||
|
|
}
|
|||
|
|
} else {
|
|||
|
|
isEmpty = !fieldValue || fieldValue === "";
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if (isEmpty) {
|
|||
|
|
uni.showToast({ title: `请填写必填项:${zplx.zpbt || '作品内容'}`, icon: 'none' });
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 查找对应的作品清单记录ID
|
|||
|
|
const existingRecord = zpqdList.value.find((item: ZpqdItem) => item.zplxId === fieldId);
|
|||
|
|
const recordId = existingRecord ? existingRecord.id : undefined;
|
|||
|
|
|
|||
|
|
result.push({
|
|||
|
|
id: recordId,
|
|||
|
|
zplxId: fieldId,
|
|||
|
|
zpqdtx: fieldValue,
|
|||
|
|
wjmc: fileNames || undefined,
|
|||
|
|
});
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
try {
|
|||
|
|
isSubmitting.value = true;
|
|||
|
|
|
|||
|
|
if (!xsId.value) {
|
|||
|
|
uni.showToast({ title: '缺少学生ID,无法提交', icon: 'error' });
|
|||
|
|
isSubmitting.value = false;
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 获取学生信息
|
|||
|
|
const curXs = userStore.curXs;
|
|||
|
|
if (!curXs) {
|
|||
|
|
uni.showToast({ title: '请先选择学生', icon: 'error' });
|
|||
|
|
isSubmitting.value = false;
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
const currentXsId = xsId.value || curXs?.id || '';
|
|||
|
|
|
|||
|
|
if (!currentXsId) {
|
|||
|
|
uni.showToast({ title: '请先选择学生', icon: 'error' });
|
|||
|
|
isSubmitting.value = false;
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 验证必须有作品执行ID
|
|||
|
|
if (!zpzxId.value) {
|
|||
|
|
uni.showToast({ title: '缺少作品执行ID,无法提交', icon: 'error' });
|
|||
|
|
isSubmitting.value = false;
|
|||
|
|
return;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 构建提交数据:只传递 id 和作品清单
|
|||
|
|
const submitData: any = {
|
|||
|
|
id: zpzxId.value, // 作品执行ID(必须)
|
|||
|
|
zpqdDtos: result // 作品清单
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
await zpzxSaveApi(submitData);
|
|||
|
|
|
|||
|
|
uni.showToast({ title: '提交成功', icon: 'success' });
|
|||
|
|
|
|||
|
|
// 延迟返回并刷新列表页面
|
|||
|
|
setTimeout(() => {
|
|||
|
|
// 获取当前页面的参数
|
|||
|
|
const pages = getCurrentPages();
|
|||
|
|
const currentPage = pages[pages.length - 1];
|
|||
|
|
const options = currentPage.options || {};
|
|||
|
|
const kcId = options.kcId || '';
|
|||
|
|
const xsIdParam = options.xsId || xsId.value || '';
|
|||
|
|
|
|||
|
|
// 返回到 index.vue 并刷新
|
|||
|
|
uni.navigateBack({
|
|||
|
|
delta: 1,
|
|||
|
|
success: () => {
|
|||
|
|
// 通知列表页面刷新
|
|||
|
|
uni.$emit('refreshTaskList');
|
|||
|
|
},
|
|||
|
|
fail: () => {
|
|||
|
|
// 如果返回失败,直接跳转到列表页面
|
|||
|
|
const pages = getCurrentPages();
|
|||
|
|
if (pages.length > 1) {
|
|||
|
|
uni.navigateBack({ delta: 1 });
|
|||
|
|
} else {
|
|||
|
|
uni.redirectTo({
|
|||
|
|
url: `/pages/base/xszp/index${kcId ? `?kcId=${kcId}` : ''}${xsIdParam ? `&xsId=${xsIdParam}` : ''}`,
|
|||
|
|
success: () => {
|
|||
|
|
uni.$emit('refreshTaskList');
|
|||
|
|
}
|
|||
|
|
});
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
});
|
|||
|
|
}, 1500);
|
|||
|
|
|
|||
|
|
} catch (error) {
|
|||
|
|
console.error('提交失败:', error);
|
|||
|
|
uni.showToast({
|
|||
|
|
title: error instanceof Error ? error.message : '提交失败,请重试',
|
|||
|
|
icon: 'error'
|
|||
|
|
});
|
|||
|
|
} finally {
|
|||
|
|
isSubmitting.value = false;
|
|||
|
|
}
|
|||
|
|
};
|
|||
|
|
</script>
|
|||
|
|
|
|||
|
|
<style scoped lang="scss">
|
|||
|
|
.zp-submit-page {
|
|||
|
|
background-color: #f4f5f7;
|
|||
|
|
min-height: 100vh;
|
|||
|
|
padding-bottom: 80px;
|
|||
|
|
box-sizing: border-box;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.loading-indicator {
|
|||
|
|
text-align: center;
|
|||
|
|
color: #999;
|
|||
|
|
padding: 40px 15px;
|
|||
|
|
font-size: 14px;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.content-wrapper {
|
|||
|
|
flex: 1;
|
|||
|
|
transition: height 0.3s ease;
|
|||
|
|
|
|||
|
|
&.full-height {
|
|||
|
|
height: 100vh;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.p-15 {
|
|||
|
|
padding: 15px;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.mt-15 {
|
|||
|
|
margin-top: 15px;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 第一部分:任务要求
|
|||
|
|
.zp-info-section {
|
|||
|
|
margin-bottom: 20px;
|
|||
|
|
padding: 15px;
|
|||
|
|
background: #fff;
|
|||
|
|
border-radius: 8px;
|
|||
|
|
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
|
|||
|
|
|
|||
|
|
.section-title {
|
|||
|
|
font-size: 16px;
|
|||
|
|
font-weight: bold;
|
|||
|
|
margin-bottom: 15px;
|
|||
|
|
color: #333;
|
|||
|
|
border-bottom: 2px solid #4e73df;
|
|||
|
|
padding-bottom: 5px;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.info-item {
|
|||
|
|
display: flex;
|
|||
|
|
margin-bottom: 15px;
|
|||
|
|
|
|||
|
|
&:last-child {
|
|||
|
|
margin-bottom: 0;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.label {
|
|||
|
|
width: 80px;
|
|||
|
|
color: #666;
|
|||
|
|
font-size: 14px;
|
|||
|
|
flex-shrink: 0;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.value {
|
|||
|
|
flex: 1;
|
|||
|
|
color: #333;
|
|||
|
|
font-size: 14px;
|
|||
|
|
word-break: break-word;
|
|||
|
|
|
|||
|
|
&.title-bold {
|
|||
|
|
font-weight: bold;
|
|||
|
|
font-size: 16px;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 第二部分:任务执行
|
|||
|
|
.zp-execute-section {
|
|||
|
|
margin-bottom: 20px;
|
|||
|
|
padding: 15px;
|
|||
|
|
background: #fff;
|
|||
|
|
|
|||
|
|
.part-section {
|
|||
|
|
margin-bottom: 30px;
|
|||
|
|
|
|||
|
|
&:last-child {
|
|||
|
|
margin-bottom: 0;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.part-title {
|
|||
|
|
font-size: 16px;
|
|||
|
|
font-weight: bold;
|
|||
|
|
margin-bottom: 15px;
|
|||
|
|
color: #4e73df;
|
|||
|
|
padding: 10px 15px;
|
|||
|
|
background: linear-gradient(135deg, #f0f4ff 0%, #e8f0ff 100%);
|
|||
|
|
border-left: 4px solid #4e73df;
|
|||
|
|
border-radius: 4px;
|
|||
|
|
// 允许换行 - 调试样式
|
|||
|
|
white-space: normal !important;
|
|||
|
|
word-break: break-word !important;
|
|||
|
|
word-wrap: break-word !important;
|
|||
|
|
overflow-wrap: break-word !important;
|
|||
|
|
line-height: 1.5 !important;
|
|||
|
|
// 调试边框(可以临时启用查看)
|
|||
|
|
// border: 1px solid red !important;
|
|||
|
|
// background: rgba(255, 0, 0, 0.1) !important;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
border-radius: 8px;
|
|||
|
|
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
|
|||
|
|
|
|||
|
|
.section-title {
|
|||
|
|
font-size: 16px;
|
|||
|
|
font-weight: bold;
|
|||
|
|
margin-bottom: 15px;
|
|||
|
|
color: #333;
|
|||
|
|
border-bottom: 2px solid #4e73df;
|
|||
|
|
padding-bottom: 5px;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.execute-form {
|
|||
|
|
padding-top: 5px;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 提交按钮 - 固定在底部
|
|||
|
|
.submit-button-fixed {
|
|||
|
|
position: fixed;
|
|||
|
|
bottom: 0;
|
|||
|
|
left: 0;
|
|||
|
|
right: 0;
|
|||
|
|
padding: 15px;
|
|||
|
|
background-color: #fff;
|
|||
|
|
box-shadow: 0 -2px 10px rgba(0, 0, 0, 0.1);
|
|||
|
|
z-index: 10;
|
|||
|
|
border-top: 1px solid #eee;
|
|||
|
|
transition: transform 0.3s ease, opacity 0.3s ease;
|
|||
|
|
|
|||
|
|
&.hide-bottom {
|
|||
|
|
transform: translateY(100%);
|
|||
|
|
opacity: 0;
|
|||
|
|
pointer-events: none;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.action-button {
|
|||
|
|
width: 100%;
|
|||
|
|
height: 44px;
|
|||
|
|
line-height: 44px;
|
|||
|
|
font-size: 16px;
|
|||
|
|
font-weight: 500;
|
|||
|
|
border-radius: 8px;
|
|||
|
|
background-color: #4e73df;
|
|||
|
|
color: #ffffff;
|
|||
|
|
border: none;
|
|||
|
|
|
|||
|
|
&:active:not(:disabled) {
|
|||
|
|
background-color: #2e59d9;
|
|||
|
|
transform: translateY(1px);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
&:disabled {
|
|||
|
|
background-color: #d9d9d9 !important;
|
|||
|
|
color: #999 !important;
|
|||
|
|
cursor: not-allowed;
|
|||
|
|
opacity: 0.6;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 提交遮罩层样式
|
|||
|
|
.submit-overlay {
|
|||
|
|
position: fixed;
|
|||
|
|
top: 0;
|
|||
|
|
left: 0;
|
|||
|
|
right: 0;
|
|||
|
|
bottom: 0;
|
|||
|
|
background-color: rgba(0, 0, 0, 0.5);
|
|||
|
|
display: flex;
|
|||
|
|
align-items: center;
|
|||
|
|
justify-content: center;
|
|||
|
|
z-index: 9999;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.submit-loading {
|
|||
|
|
display: flex;
|
|||
|
|
flex-direction: column;
|
|||
|
|
align-items: center;
|
|||
|
|
justify-content: center;
|
|||
|
|
background-color: #fff;
|
|||
|
|
padding: 30px;
|
|||
|
|
border-radius: 12px;
|
|||
|
|
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.15);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.loading-spinner {
|
|||
|
|
width: 40px;
|
|||
|
|
height: 40px;
|
|||
|
|
border: 3px solid #f3f3f3;
|
|||
|
|
border-top: 3px solid #4e73df;
|
|||
|
|
border-radius: 50%;
|
|||
|
|
animation: spin 1s linear infinite;
|
|||
|
|
margin-bottom: 16px;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
@keyframes spin {
|
|||
|
|
0% { transform: rotate(0deg); }
|
|||
|
|
100% { transform: rotate(360deg); }
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
.loading-text {
|
|||
|
|
font-size: 16px;
|
|||
|
|
color: #333;
|
|||
|
|
font-weight: 500;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 为输入框添加基本边框
|
|||
|
|
:deep(.uni-input),
|
|||
|
|
:deep(.uni-textarea) {
|
|||
|
|
width: 100% !important;
|
|||
|
|
min-height: 35px !important;
|
|||
|
|
font-size: 13px !important;
|
|||
|
|
border: 1px #CCCCCC solid !important;
|
|||
|
|
border-radius: 3px !important;
|
|||
|
|
padding: 8px 12px !important;
|
|||
|
|
box-sizing: border-box !important;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
:deep(input),
|
|||
|
|
:deep(textarea) {
|
|||
|
|
width: 100% !important;
|
|||
|
|
min-height: 35px !important;
|
|||
|
|
font-size: 13px !important;
|
|||
|
|
border: 1px #CCCCCC solid !important;
|
|||
|
|
border-radius: 3px !important;
|
|||
|
|
padding: 8px 12px !important;
|
|||
|
|
box-sizing: border-box !important;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 只针对 zpbt 字段(label 文本)的换行问题
|
|||
|
|
:deep(.zp-execute-section) {
|
|||
|
|
// 针对 FormsItem 中的 label 容器
|
|||
|
|
// 只影响 forms-item-row 中直接包含 label 的 flex-row,不影响选择器内部的 flex-row
|
|||
|
|
.forms-item-row {
|
|||
|
|
// label 容器(flex-row)- 移除宽度限制,允许换行
|
|||
|
|
// 使用 :not() 排除选择器组件内部的 flex-row
|
|||
|
|
> .flex-row:not(.wh-full) {
|
|||
|
|
// 关键:移除固定宽度限制(FormsItem.vue 中默认是 80px)
|
|||
|
|
width: 100% !important;
|
|||
|
|
max-width: 100% !important;
|
|||
|
|
min-width: 0 !important;
|
|||
|
|
// 不允许 flex 容器换行,保持 * 号和文本在同一行
|
|||
|
|
flex-wrap: nowrap !important;
|
|||
|
|
display: flex !important;
|
|||
|
|
align-items: flex-start !important;
|
|||
|
|
|
|||
|
|
// 必填标记(红色星号)- 与文本保持在同一行
|
|||
|
|
> view:first-child {
|
|||
|
|
flex-shrink: 0 !important;
|
|||
|
|
width: 10px !important;
|
|||
|
|
// 确保 * 号与文本顶部对齐
|
|||
|
|
align-self: flex-start !important;
|
|||
|
|
padding-top: 2px !important;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 图标容器(如果有)
|
|||
|
|
> view:nth-child(2) {
|
|||
|
|
flex-shrink: 0 !important;
|
|||
|
|
align-self: flex-start !important;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 包含 label 文本的 view(第15行的 view,zpbt 字段)
|
|||
|
|
> view:last-child {
|
|||
|
|
// 允许文本在容器内换行,但不与 * 号分离
|
|||
|
|
white-space: normal !important;
|
|||
|
|
word-break: break-word !important;
|
|||
|
|
word-wrap: break-word !important;
|
|||
|
|
overflow-wrap: break-word !important;
|
|||
|
|
width: auto !important;
|
|||
|
|
max-width: calc(100% - 20px) !important; // 减去 * 号和图标的空间
|
|||
|
|
min-width: 0 !important;
|
|||
|
|
flex: 1 1 auto !important;
|
|||
|
|
display: block !important;
|
|||
|
|
line-height: 1.5 !important;
|
|||
|
|
box-sizing: border-box !important;
|
|||
|
|
// 确保文本与 * 号顶部对齐
|
|||
|
|
align-self: flex-start !important;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 确保选择器内部的提示信息可以换行
|
|||
|
|
.wh-full.flex-row {
|
|||
|
|
flex-wrap: wrap !important;
|
|||
|
|
|
|||
|
|
// 提示信息容器(第一个 view)
|
|||
|
|
> view:first-child {
|
|||
|
|
white-space: normal !important;
|
|||
|
|
word-break: break-word !important;
|
|||
|
|
word-wrap: break-word !important;
|
|||
|
|
overflow-wrap: break-word !important;
|
|||
|
|
flex: 1 1 auto !important;
|
|||
|
|
min-width: 0 !important;
|
|||
|
|
max-width: 100% !important;
|
|||
|
|
|
|||
|
|
// 文本内容
|
|||
|
|
text,
|
|||
|
|
span {
|
|||
|
|
white-space: normal !important;
|
|||
|
|
word-break: break-word !important;
|
|||
|
|
word-wrap: break-word !important;
|
|||
|
|
overflow-wrap: break-word !important;
|
|||
|
|
display: block !important;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 右侧图标保持不换行
|
|||
|
|
> view:last-child {
|
|||
|
|
flex-shrink: 0 !important;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
|
|||
|
|
</style>
|