2025-04-22 10:22:33 +08:00
|
|
|
|
<template>
|
|
|
|
|
|
<BasicLayout>
|
|
|
|
|
|
<!-- Default slot -->
|
|
|
|
|
|
<scroll-view scroll-y class="form-scroll-view">
|
|
|
|
|
|
<view class="form-container">
|
|
|
|
|
|
<view class="info-card main-content-card">
|
|
|
|
|
|
<view class="form-item cover-section">
|
|
|
|
|
|
<view class="cover-header">
|
|
|
|
|
|
<text class="form-label">封面</text>
|
|
|
|
|
|
<text class="cover-hint">建议尺寸xxx</text>
|
|
|
|
|
|
<text class="cover-counter"
|
|
|
|
|
|
>{{ formData.coverImage ? 1 : 0 }}/1</text
|
|
|
|
|
|
>
|
|
|
|
|
|
</view>
|
|
|
|
|
|
<view class="cover-upload-wrapper">
|
|
|
|
|
|
<CustomUpload
|
|
|
|
|
|
field="coverImage"
|
2025-07-02 22:43:30 +08:00
|
|
|
|
:value="formData.coverImage ? imagUrl(formData.coverImage) : ''"
|
2025-04-22 10:22:33 +08:00
|
|
|
|
@select="handleCoverSelected"
|
|
|
|
|
|
@close="handleCoverClosed"
|
|
|
|
|
|
>
|
|
|
|
|
|
<view class="cover-placeholder">
|
|
|
|
|
|
<uni-icons type="image" size="40" color="#b0b0b0"></uni-icons>
|
|
|
|
|
|
<text>添加封面</text>
|
|
|
|
|
|
</view>
|
|
|
|
|
|
</CustomUpload>
|
|
|
|
|
|
</view>
|
|
|
|
|
|
</view>
|
|
|
|
|
|
|
|
|
|
|
|
<view class="form-item">
|
|
|
|
|
|
<uni-easyinput
|
|
|
|
|
|
type="textarea"
|
|
|
|
|
|
autoHeight
|
|
|
|
|
|
v-model="formData.title"
|
|
|
|
|
|
placeholder="请输入通知标题 (必填)"
|
|
|
|
|
|
:inputBorder="false"
|
|
|
|
|
|
placeholder-style="font-weight:bold; font-size: 18px; color: #999;"
|
|
|
|
|
|
class="title-input"
|
|
|
|
|
|
></uni-easyinput>
|
|
|
|
|
|
</view>
|
|
|
|
|
|
|
|
|
|
|
|
<view class="form-item">
|
|
|
|
|
|
<uni-easyinput
|
|
|
|
|
|
type="textarea"
|
|
|
|
|
|
autoHeight
|
|
|
|
|
|
v-model="formData.content"
|
|
|
|
|
|
placeholder="请输入通知内容 (必填)"
|
|
|
|
|
|
:inputBorder="false"
|
|
|
|
|
|
class="content-input"
|
|
|
|
|
|
></uni-easyinput>
|
|
|
|
|
|
</view>
|
|
|
|
|
|
|
|
|
|
|
|
<view class="form-item attachments-section">
|
|
|
|
|
|
<text class="form-label">附件</text>
|
|
|
|
|
|
<view class="attachment-list">
|
|
|
|
|
|
<view
|
|
|
|
|
|
v-for="(att, index) in formData.attachments"
|
|
|
|
|
|
:key="index"
|
|
|
|
|
|
class="attachment-item"
|
|
|
|
|
|
>
|
|
|
|
|
|
<uni-icons
|
|
|
|
|
|
:type="getAttachmentIcon(att.type)"
|
|
|
|
|
|
size="20"
|
|
|
|
|
|
color="#666"
|
|
|
|
|
|
class="attachment-icon"
|
|
|
|
|
|
></uni-icons>
|
|
|
|
|
|
<text class="attachment-name" @click="previewAttachment(att)">{{
|
|
|
|
|
|
att.name
|
|
|
|
|
|
}}</text>
|
|
|
|
|
|
<uni-icons
|
|
|
|
|
|
type="closeempty"
|
|
|
|
|
|
size="18"
|
|
|
|
|
|
color="#999"
|
|
|
|
|
|
class="remove-icon"
|
|
|
|
|
|
@click="removeAttachment(index)"
|
|
|
|
|
|
></uni-icons>
|
|
|
|
|
|
</view>
|
|
|
|
|
|
</view>
|
|
|
|
|
|
<view class="add-attachment-placeholder" @click="addAttachment">
|
|
|
|
|
|
<view class="add-icon"
|
|
|
|
|
|
><uni-icons type="plusempty" size="20" color="#ccc"></uni-icons
|
|
|
|
|
|
></view>
|
2025-07-02 22:43:30 +08:00
|
|
|
|
<text class="placeholder-text">添加图文/视频/文件</text>
|
2025-04-22 10:22:33 +08:00
|
|
|
|
</view>
|
|
|
|
|
|
</view>
|
|
|
|
|
|
</view>
|
|
|
|
|
|
|
|
|
|
|
|
<view class="info-card">
|
2025-07-02 22:43:30 +08:00
|
|
|
|
<view class="card-header picker-header" @click="showClassTree">
|
|
|
|
|
|
<text class="section-title">按名单填写</text>
|
|
|
|
|
|
<view class="target-class">
|
|
|
|
|
|
<text :class="{ placeholder: !formData.targetClass }">{{
|
|
|
|
|
|
formData.targetClass || "请选择班级"
|
|
|
|
|
|
}}</text>
|
|
|
|
|
|
<uni-icons type="right" size="16" color="#999"></uni-icons>
|
2025-04-22 10:22:33 +08:00
|
|
|
|
</view>
|
2025-07-02 22:43:30 +08:00
|
|
|
|
</view>
|
2025-04-22 10:22:33 +08:00
|
|
|
|
<view class="name-tags">
|
|
|
|
|
|
<text
|
2025-07-02 22:43:30 +08:00
|
|
|
|
v-for="(name, index) in displayNames"
|
|
|
|
|
|
:key="name + index"
|
2025-04-22 10:22:33 +08:00
|
|
|
|
class="name-tag"
|
|
|
|
|
|
>{{ name }}</text
|
|
|
|
|
|
>
|
2025-07-02 22:43:30 +08:00
|
|
|
|
</view>
|
|
|
|
|
|
<view
|
|
|
|
|
|
v-if="formData.targetNames.length > maxDisplayCount"
|
|
|
|
|
|
class="more-btn-container"
|
|
|
|
|
|
>
|
|
|
|
|
|
<button size="mini" class="more-btn-full" @click="showAllStudents">
|
|
|
|
|
|
更多({{ formData.targetNames.length - maxDisplayCount }})
|
2025-04-22 10:22:33 +08:00
|
|
|
|
</button>
|
|
|
|
|
|
</view>
|
|
|
|
|
|
</view>
|
|
|
|
|
|
|
|
|
|
|
|
<view class="info-card list-item-card">
|
|
|
|
|
|
<picker
|
|
|
|
|
|
mode="selector"
|
|
|
|
|
|
:range="signatureOptions"
|
|
|
|
|
|
@change="handleSignatureChange"
|
|
|
|
|
|
>
|
|
|
|
|
|
<view class="list-item-row">
|
|
|
|
|
|
<text class="list-label">按名单签字</text>
|
|
|
|
|
|
<view class="list-value">
|
|
|
|
|
|
<text>{{ signatureStatusText }}</text>
|
|
|
|
|
|
<uni-icons type="right" size="16" color="#999"></uni-icons>
|
|
|
|
|
|
</view>
|
|
|
|
|
|
</view>
|
|
|
|
|
|
</picker>
|
|
|
|
|
|
</view>
|
|
|
|
|
|
|
|
|
|
|
|
<view class="info-card list-item-card">
|
|
|
|
|
|
<uni-datetime-picker type="datetime" v-model="formData.startTime">
|
|
|
|
|
|
<view class="list-item-row">
|
|
|
|
|
|
<text class="list-label">开始时间</text>
|
|
|
|
|
|
<view class="list-value">
|
|
|
|
|
|
<text>{{ formData.startTime || "请选择" }}</text>
|
|
|
|
|
|
<uni-icons type="right" size="16" color="#999"></uni-icons>
|
|
|
|
|
|
</view>
|
|
|
|
|
|
</view>
|
|
|
|
|
|
</uni-datetime-picker>
|
|
|
|
|
|
|
|
|
|
|
|
<uni-datetime-picker type="datetime" v-model="formData.endTime">
|
|
|
|
|
|
<view class="list-item-row no-border">
|
|
|
|
|
|
<text class="list-label">结束时间</text>
|
|
|
|
|
|
<view class="list-value">
|
|
|
|
|
|
<text>{{ formData.endTime || "请选择" }}</text>
|
|
|
|
|
|
<uni-icons type="right" size="16" color="#999"></uni-icons>
|
|
|
|
|
|
</view>
|
|
|
|
|
|
</view>
|
|
|
|
|
|
</uni-datetime-picker>
|
|
|
|
|
|
</view>
|
|
|
|
|
|
</view>
|
|
|
|
|
|
</scroll-view>
|
|
|
|
|
|
|
|
|
|
|
|
<!-- Bottom slot -->
|
|
|
|
|
|
<template #bottom>
|
2025-07-02 22:43:30 +08:00
|
|
|
|
<view class="bottom-actions">
|
|
|
|
|
|
<button class="action-btn draft-btn" @click="saveDraft">
|
|
|
|
|
|
保存草稿
|
|
|
|
|
|
</button>
|
|
|
|
|
|
<button class="action-btn preview-btn" @click="previewNotice">
|
|
|
|
|
|
预览
|
|
|
|
|
|
</button>
|
|
|
|
|
|
<button class="action-btn publish-btn" @click="publishNotice">
|
|
|
|
|
|
立即发布
|
|
|
|
|
|
</button>
|
|
|
|
|
|
</view>
|
2025-04-22 10:22:33 +08:00
|
|
|
|
</template>
|
|
|
|
|
|
|
2025-07-02 22:43:30 +08:00
|
|
|
|
<!-- 班级选择树 -->
|
|
|
|
|
|
<BasicTree
|
|
|
|
|
|
ref="treeRef"
|
|
|
|
|
|
:range="treeData"
|
|
|
|
|
|
idKey="key"
|
|
|
|
|
|
rangeKey="title"
|
|
|
|
|
|
title="选择班级"
|
|
|
|
|
|
:multiple="true"
|
|
|
|
|
|
:selectParent="false"
|
|
|
|
|
|
@confirm="onTreeConfirm"
|
|
|
|
|
|
@cancel="onTreeCancel"
|
|
|
|
|
|
/>
|
|
|
|
|
|
|
|
|
|
|
|
<!-- 学生名单弹窗 -->
|
|
|
|
|
|
<view
|
|
|
|
|
|
v-if="showStudentModal"
|
|
|
|
|
|
class="student-modal-mask"
|
|
|
|
|
|
@click="closeStudentModal"
|
|
|
|
|
|
>
|
|
|
|
|
|
<view class="student-modal" @click.stop>
|
|
|
|
|
|
<view class="student-modal-header">
|
|
|
|
|
|
<text class="modal-title">全部学生名单</text>
|
|
|
|
|
|
<text class="student-count"
|
|
|
|
|
|
>共{{ formData.targetNames.length }}人</text
|
|
|
|
|
|
>
|
|
|
|
|
|
<uni-icons
|
|
|
|
|
|
type="closeempty"
|
|
|
|
|
|
size="20"
|
|
|
|
|
|
color="#999"
|
|
|
|
|
|
@click="closeStudentModal"
|
|
|
|
|
|
class="close-icon"
|
|
|
|
|
|
></uni-icons>
|
|
|
|
|
|
</view>
|
|
|
|
|
|
<scroll-view scroll-y class="student-modal-content">
|
|
|
|
|
|
<view class="all-student-tags">
|
|
|
|
|
|
<text
|
|
|
|
|
|
v-for="(name, index) in formData.targetNames"
|
|
|
|
|
|
:key="name + index"
|
|
|
|
|
|
class="student-tag"
|
|
|
|
|
|
>{{ name }}</text
|
|
|
|
|
|
>
|
|
|
|
|
|
</view>
|
|
|
|
|
|
</scroll-view>
|
|
|
|
|
|
</view>
|
|
|
|
|
|
</view>
|
2025-04-22 10:22:33 +08:00
|
|
|
|
</BasicLayout>
|
|
|
|
|
|
</template>
|
|
|
|
|
|
|
|
|
|
|
|
<script lang="ts" setup>
|
|
|
|
|
|
import { ref, reactive, computed } from "vue";
|
|
|
|
|
|
import { onLoad } from "@dcloudio/uni-app";
|
|
|
|
|
|
import CustomUpload from "/src/components/BasicUpload/CustomUpload.vue";
|
2025-07-02 22:43:30 +08:00
|
|
|
|
import BasicTree from "@/components/BasicTree/Tree.vue";
|
|
|
|
|
|
import { attachmentUpload } from "@/api/system/upload";
|
|
|
|
|
|
import { imagUrl } from "@/utils";
|
|
|
|
|
|
import { findAllNjBjTree, mobilejlstudentListApi } from "@/api/base/server";
|
2025-04-22 10:22:33 +08:00
|
|
|
|
|
|
|
|
|
|
interface Attachment {
|
|
|
|
|
|
name: string;
|
|
|
|
|
|
type: string;
|
|
|
|
|
|
url: string;
|
|
|
|
|
|
size?: number;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
interface FormData {
|
|
|
|
|
|
id?: string;
|
|
|
|
|
|
title: string;
|
|
|
|
|
|
content: string;
|
|
|
|
|
|
coverImage: string | null;
|
|
|
|
|
|
attachments: Attachment[];
|
|
|
|
|
|
targetClass: string;
|
|
|
|
|
|
targetNames: string[];
|
|
|
|
|
|
targetStudentIds: string[];
|
2025-07-02 22:43:30 +08:00
|
|
|
|
targetNjIds: string[]; // 年级ID数组
|
|
|
|
|
|
targetBjIds: string[]; // 班级ID数组
|
2025-04-22 10:22:33 +08:00
|
|
|
|
signatureRequired: boolean;
|
|
|
|
|
|
startTime: string;
|
|
|
|
|
|
endTime: string;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const noticeId = ref<string | null>(null);
|
|
|
|
|
|
const isLoadingStudents = ref(false);
|
|
|
|
|
|
const formData = reactive<FormData>({
|
|
|
|
|
|
title: "",
|
|
|
|
|
|
content: "",
|
|
|
|
|
|
coverImage: null,
|
|
|
|
|
|
attachments: [],
|
|
|
|
|
|
targetClass: "",
|
|
|
|
|
|
targetNames: [],
|
|
|
|
|
|
targetStudentIds: [],
|
2025-07-02 22:43:30 +08:00
|
|
|
|
targetNjIds: [],
|
|
|
|
|
|
targetBjIds: [],
|
2025-04-22 10:22:33 +08:00
|
|
|
|
signatureRequired: false,
|
|
|
|
|
|
startTime: "",
|
|
|
|
|
|
endTime: "",
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
const signatureOptions = ["不启用", "启用"];
|
|
|
|
|
|
const signatureStatusText = computed(() => {
|
|
|
|
|
|
return formData.signatureRequired ? "启用" : "不启用";
|
|
|
|
|
|
});
|
|
|
|
|
|
|
2025-07-02 22:43:30 +08:00
|
|
|
|
// 树形数据
|
|
|
|
|
|
const treeData = ref([]);
|
|
|
|
|
|
const treeRef = ref();
|
2025-04-22 10:22:33 +08:00
|
|
|
|
|
2025-07-02 22:43:30 +08:00
|
|
|
|
// 学生显示相关
|
|
|
|
|
|
const maxDisplayCount = 24; // 最多显示24个学生(4行×6列)
|
|
|
|
|
|
const showStudentModal = ref(false);
|
|
|
|
|
|
|
|
|
|
|
|
// 计算显示的学生名单(最多显示前24个)
|
|
|
|
|
|
const displayNames = computed(() => {
|
|
|
|
|
|
return formData.targetNames.slice(0, maxDisplayCount);
|
2025-04-22 10:22:33 +08:00
|
|
|
|
});
|
|
|
|
|
|
|
2025-07-02 22:43:30 +08:00
|
|
|
|
// 加载树形数据
|
|
|
|
|
|
const loadTreeData = async () => {
|
|
|
|
|
|
try {
|
|
|
|
|
|
const res = await findAllNjBjTree();
|
|
|
|
|
|
if (res.resultCode === 1 && res.result) {
|
|
|
|
|
|
// 转换数据格式以适配 BasicTree 组件
|
|
|
|
|
|
treeData.value = res.result.map((item: any) => ({
|
|
|
|
|
|
key: item.key,
|
|
|
|
|
|
title: item.title,
|
|
|
|
|
|
children: item.children || [],
|
|
|
|
|
|
}));
|
|
|
|
|
|
console.log("树形数据加载成功:", treeData.value);
|
|
|
|
|
|
}
|
|
|
|
|
|
} catch (error) {
|
|
|
|
|
|
console.error("加载树形数据失败:", error);
|
|
|
|
|
|
uni.showToast({ title: "加载班级数据失败", icon: "error" });
|
|
|
|
|
|
}
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
// 在组件挂载时加载数据
|
|
|
|
|
|
loadTreeData();
|
|
|
|
|
|
|
2025-04-22 10:22:33 +08:00
|
|
|
|
const fetchStudentsByClass = async (className: string): Promise<string[]> => {
|
|
|
|
|
|
console.log(`模拟获取班级 [${className}] 的学生列表...`);
|
|
|
|
|
|
isLoadingStudents.value = true;
|
|
|
|
|
|
await new Promise((resolve) => setTimeout(resolve, 400));
|
|
|
|
|
|
|
|
|
|
|
|
const mockStudents = [
|
|
|
|
|
|
"施延兴",
|
|
|
|
|
|
"安苒溪",
|
|
|
|
|
|
"罗浩晨",
|
|
|
|
|
|
"康萌",
|
|
|
|
|
|
"范文昊",
|
|
|
|
|
|
"丁贺祥",
|
|
|
|
|
|
"韦运昊",
|
|
|
|
|
|
"萧润丽",
|
|
|
|
|
|
"谢林",
|
|
|
|
|
|
"鲍泽远",
|
|
|
|
|
|
"杨俊",
|
|
|
|
|
|
];
|
|
|
|
|
|
|
|
|
|
|
|
isLoadingStudents.value = false;
|
|
|
|
|
|
return mockStudents;
|
|
|
|
|
|
};
|
|
|
|
|
|
|
2025-07-02 22:43:30 +08:00
|
|
|
|
onLoad((options) => {});
|
2025-04-22 10:22:33 +08:00
|
|
|
|
|
2025-07-02 22:43:30 +08:00
|
|
|
|
const handleCoverSelected = async (e: any) => {
|
2025-04-22 10:22:33 +08:00
|
|
|
|
if (e.tempFilePaths && e.tempFilePaths.length > 0) {
|
2025-07-02 22:43:30 +08:00
|
|
|
|
const tempFilePath = e.tempFilePaths[0];
|
|
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
|
uni.showLoading({ title: "上传中..." });
|
|
|
|
|
|
|
|
|
|
|
|
// 直接使用 tempFilePath 作为 Blob 传给接口
|
|
|
|
|
|
const uploadResult: any = await attachmentUpload(tempFilePath as any);
|
|
|
|
|
|
|
|
|
|
|
|
if (
|
|
|
|
|
|
uploadResult.resultCode === 1 &&
|
|
|
|
|
|
uploadResult.result &&
|
|
|
|
|
|
uploadResult.result.length > 0
|
|
|
|
|
|
) {
|
|
|
|
|
|
// 保存原始的 filePath(用于提交到服务器)
|
|
|
|
|
|
const originalPath = uploadResult.result[0].filePath;
|
|
|
|
|
|
formData.coverImage = originalPath;
|
|
|
|
|
|
uni.showToast({ title: "封面上传成功", icon: "success" });
|
|
|
|
|
|
} else {
|
|
|
|
|
|
throw new Error("上传响应格式异常");
|
|
|
|
|
|
}
|
|
|
|
|
|
} catch (error) {
|
|
|
|
|
|
uni.showToast({ title: "封面上传失败", icon: "error" });
|
|
|
|
|
|
formData.coverImage = null;
|
|
|
|
|
|
} finally {
|
|
|
|
|
|
uni.hideLoading();
|
|
|
|
|
|
}
|
2025-04-22 10:22:33 +08:00
|
|
|
|
} else {
|
|
|
|
|
|
console.error("无法从选择事件中获取封面路径:", e);
|
|
|
|
|
|
}
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
const handleCoverClosed = (field: string) => {
|
|
|
|
|
|
if (field === "coverImage") {
|
|
|
|
|
|
formData.coverImage = null;
|
|
|
|
|
|
}
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
const addAttachment = () => {
|
|
|
|
|
|
uni.chooseFile({
|
|
|
|
|
|
count: 5,
|
|
|
|
|
|
type: "all",
|
2025-07-02 22:43:30 +08:00
|
|
|
|
success: async (res) => {
|
2025-04-22 10:22:33 +08:00
|
|
|
|
const tempFiles = res.tempFiles;
|
|
|
|
|
|
if (Array.isArray(tempFiles) && tempFiles.length > 0) {
|
2025-07-02 22:43:30 +08:00
|
|
|
|
uni.showLoading({ title: "上传中..." });
|
|
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
|
for (const file of tempFiles) {
|
|
|
|
|
|
const fileInfo = file as any; // 强制类型转换避免类型检查错误
|
|
|
|
|
|
let fileType = "file";
|
|
|
|
|
|
const fileName = fileInfo.name || "";
|
|
|
|
|
|
const fileExtension = fileName.split(".").pop()?.toLowerCase();
|
|
|
|
|
|
|
|
|
|
|
|
if (
|
|
|
|
|
|
["png", "jpg", "jpeg", "gif", "bmp", "webp"].includes(
|
|
|
|
|
|
fileExtension || ""
|
|
|
|
|
|
)
|
|
|
|
|
|
) {
|
|
|
|
|
|
fileType = "image";
|
|
|
|
|
|
} else if (
|
|
|
|
|
|
["mp4", "mov", "avi", "wmv", "flv"].includes(fileExtension || "")
|
|
|
|
|
|
) {
|
|
|
|
|
|
fileType = "video";
|
|
|
|
|
|
} else if (
|
|
|
|
|
|
["mp3", "wav", "aac", "ogg"].includes(fileExtension || "")
|
|
|
|
|
|
) {
|
|
|
|
|
|
fileType = "audio";
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (
|
|
|
|
|
|
fileInfo.type &&
|
|
|
|
|
|
typeof fileInfo.type === "string" &&
|
|
|
|
|
|
(fileInfo.type.startsWith("image/") ||
|
|
|
|
|
|
fileInfo.type.startsWith("video/") ||
|
|
|
|
|
|
fileInfo.type.startsWith("audio/"))
|
|
|
|
|
|
) {
|
|
|
|
|
|
fileType = fileInfo.type.split("/")[0];
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 直接使用文件路径作为 Blob 传给接口
|
|
|
|
|
|
const uploadResult: any = await attachmentUpload(
|
|
|
|
|
|
fileInfo.path as any
|
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
|
|
if (
|
|
|
|
|
|
uploadResult.resultCode === 1 &&
|
|
|
|
|
|
uploadResult.result &&
|
|
|
|
|
|
uploadResult.result.length > 0
|
|
|
|
|
|
) {
|
|
|
|
|
|
// 保存原始的 filePath(用于提交到服务器)
|
|
|
|
|
|
const originalPath = uploadResult.result[0].filePath;
|
|
|
|
|
|
formData.attachments.push({
|
|
|
|
|
|
name: fileName,
|
|
|
|
|
|
type: fileType,
|
|
|
|
|
|
url: originalPath, // 保存原始路径
|
|
|
|
|
|
size: fileInfo.size,
|
|
|
|
|
|
});
|
|
|
|
|
|
} else {
|
|
|
|
|
|
uni.showToast({ title: `${fileName} 上传失败`, icon: "error" });
|
|
|
|
|
|
}
|
2025-04-22 10:22:33 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2025-07-02 22:43:30 +08:00
|
|
|
|
uni.showToast({ title: "附件上传完成", icon: "success" });
|
|
|
|
|
|
} catch (error) {
|
|
|
|
|
|
console.error("附件上传失败:", error);
|
|
|
|
|
|
uni.showToast({ title: "附件上传失败", icon: "error" });
|
|
|
|
|
|
} finally {
|
|
|
|
|
|
uni.hideLoading();
|
|
|
|
|
|
}
|
2025-04-22 10:22:33 +08:00
|
|
|
|
} else {
|
|
|
|
|
|
console.log("未选择任何文件或返回结果异常,或 tempFiles 不是数组");
|
|
|
|
|
|
}
|
|
|
|
|
|
},
|
|
|
|
|
|
fail: (err) => {
|
|
|
|
|
|
console.error("选择附件失败:", err);
|
|
|
|
|
|
if (err.errMsg && !err.errMsg.includes("cancel")) {
|
|
|
|
|
|
uni.showToast({ title: "选择附件失败", icon: "none" });
|
|
|
|
|
|
}
|
|
|
|
|
|
},
|
|
|
|
|
|
});
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
const removeAttachment = (index: number) => {
|
|
|
|
|
|
formData.attachments.splice(index, 1);
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
const getAttachmentIcon = (type: string): string => {
|
|
|
|
|
|
if (type === "image") return "image";
|
|
|
|
|
|
if (type === "video") return "videocam";
|
|
|
|
|
|
if (type === "audio") return "mic";
|
|
|
|
|
|
return "paperclip";
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
const previewAttachment = (attachment: Attachment) => {
|
|
|
|
|
|
console.log("预览附件:", attachment);
|
2025-07-02 22:43:30 +08:00
|
|
|
|
// 如果是图片类型,可以预览
|
|
|
|
|
|
if (attachment.type === "image") {
|
|
|
|
|
|
const fullUrl = imagUrl(attachment.url);
|
|
|
|
|
|
uni.previewImage({
|
|
|
|
|
|
urls: [fullUrl],
|
|
|
|
|
|
current: fullUrl,
|
|
|
|
|
|
});
|
|
|
|
|
|
} else {
|
|
|
|
|
|
uni.showToast({
|
|
|
|
|
|
title: `预览 ${attachment.name} 功能待实现`,
|
|
|
|
|
|
icon: "none",
|
|
|
|
|
|
});
|
|
|
|
|
|
}
|
2025-04-22 10:22:33 +08:00
|
|
|
|
};
|
|
|
|
|
|
|
2025-07-02 22:43:30 +08:00
|
|
|
|
// 显示班级选择树
|
|
|
|
|
|
const showClassTree = () => {
|
|
|
|
|
|
if (treeRef.value) {
|
|
|
|
|
|
treeRef.value._show();
|
|
|
|
|
|
}
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
// 树形选择确认
|
|
|
|
|
|
const onTreeConfirm = async (selectedItems: any[]) => {
|
|
|
|
|
|
console.log("选择的班级:", selectedItems);
|
|
|
|
|
|
if (selectedItems.length > 0) {
|
|
|
|
|
|
// 处理多选情况
|
|
|
|
|
|
const classNames = selectedItems.map((item) => item.title);
|
|
|
|
|
|
formData.targetClass = classNames.join(", ");
|
2025-04-22 10:22:33 +08:00
|
|
|
|
formData.targetNames = [];
|
|
|
|
|
|
formData.targetStudentIds = [];
|
2025-07-02 22:43:30 +08:00
|
|
|
|
formData.targetNjIds = [];
|
|
|
|
|
|
formData.targetBjIds = [];
|
|
|
|
|
|
|
2025-04-22 10:22:33 +08:00
|
|
|
|
try {
|
2025-07-02 22:43:30 +08:00
|
|
|
|
uni.showLoading({ title: "获取学生列表中..." });
|
|
|
|
|
|
|
|
|
|
|
|
// 提取年级ID和班级ID
|
|
|
|
|
|
const njIds: string[] = [];
|
|
|
|
|
|
const bjIds: string[] = [];
|
|
|
|
|
|
|
|
|
|
|
|
selectedItems.forEach((item) => {
|
|
|
|
|
|
// 如果选择的是班级(有parents表示是班级)
|
|
|
|
|
|
if (item.parents && item.parents.length > 0) {
|
|
|
|
|
|
const njId = item.parents[0].key; // 年级ID:parents[0].key
|
|
|
|
|
|
const bjId = item.key; // 班级ID:item.key
|
|
|
|
|
|
njIds.push(njId);
|
|
|
|
|
|
bjIds.push(bjId);
|
|
|
|
|
|
console.log(
|
|
|
|
|
|
`选择班级: ${item.title}, 年级ID: ${njId}, 班级ID: ${bjId}`
|
|
|
|
|
|
);
|
|
|
|
|
|
}
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
if (njIds.length > 0 && bjIds.length > 0) {
|
|
|
|
|
|
// 对年级ID去重
|
|
|
|
|
|
const uniqueNjIds = [...new Set(njIds)];
|
|
|
|
|
|
|
|
|
|
|
|
// 保存选择的年级ID和班级ID到formData中
|
|
|
|
|
|
formData.targetNjIds = uniqueNjIds;
|
|
|
|
|
|
formData.targetBjIds = bjIds;
|
|
|
|
|
|
|
|
|
|
|
|
// 调用接口获取学生列表
|
|
|
|
|
|
const params = {
|
|
|
|
|
|
njId: uniqueNjIds.join(","),
|
|
|
|
|
|
bjId: bjIds.join(","),
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
const response = await mobilejlstudentListApi(params);
|
|
|
|
|
|
|
|
|
|
|
|
if (response && response.resultCode === 1 && response.result) {
|
|
|
|
|
|
// 提取学生姓名,尝试多个可能的字段名
|
|
|
|
|
|
const studentNames = response.result.map(
|
|
|
|
|
|
(student: any) =>
|
|
|
|
|
|
student.xsxm ||
|
|
|
|
|
|
student.name ||
|
|
|
|
|
|
student.studentName ||
|
|
|
|
|
|
student.xm ||
|
|
|
|
|
|
"未知姓名"
|
|
|
|
|
|
);
|
|
|
|
|
|
formData.targetNames = studentNames;
|
|
|
|
|
|
formData.targetStudentIds = response.result.map(
|
|
|
|
|
|
(student: any) =>
|
|
|
|
|
|
student.id || student.xsId || student.studentId || ""
|
|
|
|
|
|
);
|
|
|
|
|
|
} else {
|
|
|
|
|
|
throw new Error("获取学生列表失败");
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
uni.hideLoading();
|
|
|
|
|
|
uni.showToast({
|
|
|
|
|
|
title: `已选择 ${selectedItems.length} 个班级`,
|
|
|
|
|
|
icon: "success",
|
|
|
|
|
|
});
|
2025-04-22 10:22:33 +08:00
|
|
|
|
} catch (error) {
|
|
|
|
|
|
console.error("获取学生列表失败:", error);
|
2025-07-02 22:43:30 +08:00
|
|
|
|
uni.hideLoading();
|
|
|
|
|
|
uni.showToast({ title: "获取学生列表失败", icon: "error" });
|
|
|
|
|
|
// 如果接口失败,使用模拟数据
|
|
|
|
|
|
formData.targetNames = await fetchStudentsByClass(formData.targetClass);
|
2025-04-22 10:22:33 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
};
|
|
|
|
|
|
|
2025-07-02 22:43:30 +08:00
|
|
|
|
// 树形选择取消
|
|
|
|
|
|
const onTreeCancel = () => {
|
|
|
|
|
|
console.log("取消选择班级");
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
// 显示全部学生
|
|
|
|
|
|
const showAllStudents = () => {
|
|
|
|
|
|
showStudentModal.value = true;
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
// 关闭学生弹窗
|
|
|
|
|
|
const closeStudentModal = () => {
|
|
|
|
|
|
showStudentModal.value = false;
|
2025-04-22 10:22:33 +08:00
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
const handleSignatureChange = (e: any) => {
|
|
|
|
|
|
const index = e.detail.value;
|
|
|
|
|
|
formData.signatureRequired = index == 1;
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
const validateForm = (): boolean => {
|
|
|
|
|
|
if (!formData.title.trim()) {
|
|
|
|
|
|
uni.showToast({ title: "请输入通知标题", icon: "none" });
|
|
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
if (!formData.content.trim()) {
|
|
|
|
|
|
uni.showToast({ title: "请输入通知内容", icon: "none" });
|
|
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
if (
|
|
|
|
|
|
formData.startTime &&
|
|
|
|
|
|
formData.endTime &&
|
|
|
|
|
|
formData.startTime >= formData.endTime
|
|
|
|
|
|
) {
|
|
|
|
|
|
uni.showToast({ title: "结束时间必须晚于开始时间", icon: "none" });
|
|
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
return true;
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
const saveDraft = () => {
|
|
|
|
|
|
if (!validateForm()) return;
|
|
|
|
|
|
console.log("保存草稿", formData);
|
|
|
|
|
|
uni.showToast({ title: "草稿保存成功 (模拟)", icon: "success" });
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
const previewNotice = () => {
|
|
|
|
|
|
if (!validateForm()) return;
|
|
|
|
|
|
console.log("预览通知", formData);
|
|
|
|
|
|
uni.showToast({ title: "预览功能待实现", icon: "none" });
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
const publishNotice = () => {
|
|
|
|
|
|
if (!validateForm()) return;
|
2025-07-02 22:43:30 +08:00
|
|
|
|
|
|
|
|
|
|
// 准备发布数据,包含年级ID和班级ID
|
|
|
|
|
|
const publishData = {
|
|
|
|
|
|
...formData,
|
|
|
|
|
|
njIds: formData.targetNjIds.join(","), // 年级ID字符串
|
|
|
|
|
|
bjIds: formData.targetBjIds.join(","), // 班级ID字符串
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
console.log("发布通知数据:", publishData);
|
|
|
|
|
|
console.log("年级ID:", publishData.njIds);
|
|
|
|
|
|
console.log("班级ID:", publishData.bjIds);
|
|
|
|
|
|
|
|
|
|
|
|
// TODO: 调用发布接口
|
|
|
|
|
|
// uni.showLoading({ title: "发布中..." });
|
|
|
|
|
|
// setTimeout(() => {
|
|
|
|
|
|
// uni.hideLoading();
|
|
|
|
|
|
// uni.showToast({ title: "发布成功 (模拟)", icon: "success" });
|
|
|
|
|
|
// uni.navigateBack();
|
|
|
|
|
|
// }, 1000);
|
2025-04-22 10:22:33 +08:00
|
|
|
|
};
|
|
|
|
|
|
</script>
|
|
|
|
|
|
|
|
|
|
|
|
<style scoped lang="scss">
|
|
|
|
|
|
/* Remove original page container styles */
|
|
|
|
|
|
/*
|
|
|
|
|
|
.notice-publish-page {
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
flex-direction: column;
|
|
|
|
|
|
height: 100vh;
|
|
|
|
|
|
background-color: #f4f5f7;
|
|
|
|
|
|
}
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
|
|
.form-scroll-view {
|
|
|
|
|
|
/* Let BasicLayout handle height/flex */
|
|
|
|
|
|
/* flex: 1; */
|
|
|
|
|
|
/* height: 0; */
|
|
|
|
|
|
/* BasicLayout default slot might have padding, adjust if needed */
|
|
|
|
|
|
/* Or keep the scroll view if BasicLayout doesn't provide one */
|
|
|
|
|
|
height: 100%; // Assume BasicLayout's default slot needs this
|
|
|
|
|
|
box-sizing: border-box;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.form-container {
|
2025-07-02 22:43:30 +08:00
|
|
|
|
padding: 12px;
|
2025-04-22 10:22:33 +08:00
|
|
|
|
box-sizing: border-box;
|
|
|
|
|
|
/* Add padding-bottom if content gets hidden by bottom bar */
|
2025-07-02 22:43:30 +08:00
|
|
|
|
/* padding-bottom: 70px; */
|
2025-04-22 10:22:33 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.info-card {
|
|
|
|
|
|
background-color: #ffffff;
|
|
|
|
|
|
border-radius: 8px;
|
|
|
|
|
|
padding: 15px;
|
|
|
|
|
|
margin-bottom: 12px;
|
|
|
|
|
|
box-shadow: 0 1px 4px rgba(0, 0, 0, 0.05);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.main-content-card {
|
|
|
|
|
|
padding: 5px 15px;
|
|
|
|
|
|
|
|
|
|
|
|
.form-item {
|
|
|
|
|
|
padding: 12px 0;
|
|
|
|
|
|
border-bottom: 1px solid #f0f0f0;
|
|
|
|
|
|
&:last-child {
|
|
|
|
|
|
border-bottom: none;
|
|
|
|
|
|
padding-bottom: 5px;
|
|
|
|
|
|
}
|
|
|
|
|
|
&:first-child {
|
|
|
|
|
|
padding-top: 10px;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.cover-section {
|
|
|
|
|
|
padding-bottom: 10px;
|
|
|
|
|
|
|
|
|
|
|
|
.cover-header {
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
align-items: baseline;
|
|
|
|
|
|
margin-bottom: 10px;
|
|
|
|
|
|
width: 100%;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.form-label {
|
|
|
|
|
|
width: auto;
|
|
|
|
|
|
margin-right: 8px;
|
|
|
|
|
|
color: #303133;
|
|
|
|
|
|
font-size: 15px;
|
|
|
|
|
|
font-weight: 500;
|
|
|
|
|
|
flex-shrink: 0;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.cover-hint {
|
|
|
|
|
|
font-size: 12px;
|
|
|
|
|
|
color: #909399;
|
|
|
|
|
|
margin-right: auto;
|
|
|
|
|
|
padding-left: 5px;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.cover-counter {
|
|
|
|
|
|
font-size: 14px;
|
|
|
|
|
|
color: #c0c4cc;
|
|
|
|
|
|
flex-shrink: 0;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.cover-upload-wrapper {
|
|
|
|
|
|
width: 100%;
|
|
|
|
|
|
height: 150px;
|
|
|
|
|
|
border-radius: 6px;
|
|
|
|
|
|
overflow: hidden;
|
|
|
|
|
|
border: 1px dashed #dcdcdc;
|
|
|
|
|
|
background-color: #f8f8f8;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.cover-placeholder {
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
flex-direction: column;
|
|
|
|
|
|
justify-content: center;
|
|
|
|
|
|
align-items: center;
|
|
|
|
|
|
width: 100%;
|
|
|
|
|
|
height: 100%;
|
|
|
|
|
|
color: #b0b0b0;
|
|
|
|
|
|
font-size: 14px;
|
|
|
|
|
|
box-sizing: border-box;
|
|
|
|
|
|
cursor: pointer;
|
|
|
|
|
|
text {
|
|
|
|
|
|
margin-top: 8px;
|
|
|
|
|
|
}
|
|
|
|
|
|
.uni-icons {
|
|
|
|
|
|
margin-bottom: 4px;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.title-input {
|
|
|
|
|
|
padding-bottom: 5px;
|
|
|
|
|
|
}
|
|
|
|
|
|
.content-input {
|
|
|
|
|
|
padding-top: 5px;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.title-input ::v-deep .uni-easyinput__content-textarea {
|
|
|
|
|
|
font-size: 18px !important;
|
|
|
|
|
|
font-weight: bold !important;
|
|
|
|
|
|
color: #303133 !important;
|
|
|
|
|
|
padding: 0 !important;
|
|
|
|
|
|
line-height: 1.5;
|
|
|
|
|
|
}
|
|
|
|
|
|
.content-input ::v-deep .uni-easyinput__content-textarea {
|
|
|
|
|
|
font-size: 15px !important;
|
|
|
|
|
|
color: #606266 !important;
|
|
|
|
|
|
padding: 0 !important;
|
|
|
|
|
|
line-height: 1.6;
|
|
|
|
|
|
min-height: 80px;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.attachments-section {
|
|
|
|
|
|
padding-top: 15px;
|
|
|
|
|
|
.form-label {
|
|
|
|
|
|
display: block;
|
|
|
|
|
|
color: #333;
|
|
|
|
|
|
font-size: 15px;
|
|
|
|
|
|
margin-bottom: 12px;
|
|
|
|
|
|
}
|
|
|
|
|
|
.attachment-list {
|
|
|
|
|
|
margin-bottom: 12px;
|
|
|
|
|
|
}
|
|
|
|
|
|
.attachment-item {
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
align-items: center;
|
|
|
|
|
|
background-color: #f8f9fa;
|
|
|
|
|
|
border-radius: 6px;
|
|
|
|
|
|
padding: 8px 12px;
|
|
|
|
|
|
margin-bottom: 8px;
|
|
|
|
|
|
border: 1px solid #e9ecef;
|
|
|
|
|
|
|
|
|
|
|
|
.attachment-icon {
|
|
|
|
|
|
margin-right: 8px;
|
|
|
|
|
|
flex-shrink: 0;
|
|
|
|
|
|
}
|
|
|
|
|
|
.attachment-name {
|
|
|
|
|
|
flex-grow: 1;
|
|
|
|
|
|
font-size: 14px;
|
|
|
|
|
|
color: #495057;
|
|
|
|
|
|
overflow: hidden;
|
|
|
|
|
|
text-overflow: ellipsis;
|
|
|
|
|
|
white-space: nowrap;
|
|
|
|
|
|
margin-right: 10px;
|
|
|
|
|
|
cursor: pointer;
|
|
|
|
|
|
}
|
|
|
|
|
|
.remove-icon {
|
|
|
|
|
|
flex-shrink: 0;
|
|
|
|
|
|
cursor: pointer;
|
|
|
|
|
|
opacity: 0.7;
|
|
|
|
|
|
&:hover {
|
|
|
|
|
|
opacity: 1;
|
|
|
|
|
|
color: #dc3545 !important;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
.add-attachment-placeholder {
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
align-items: center;
|
|
|
|
|
|
border: 1px dashed #d5d8de;
|
|
|
|
|
|
border-radius: 6px;
|
|
|
|
|
|
padding: 15px;
|
|
|
|
|
|
background-color: #f8f8f8;
|
|
|
|
|
|
cursor: pointer;
|
|
|
|
|
|
transition: background-color 0.2s;
|
|
|
|
|
|
.add-icon {
|
|
|
|
|
|
width: 30px;
|
|
|
|
|
|
height: 30px;
|
|
|
|
|
|
border: 1px solid #d5d8de;
|
|
|
|
|
|
border-radius: 4px;
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
justify-content: center;
|
|
|
|
|
|
align-items: center;
|
|
|
|
|
|
margin-right: 12px;
|
|
|
|
|
|
background-color: #fff;
|
|
|
|
|
|
.uni-icons {
|
|
|
|
|
|
color: #999 !important;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
.placeholder-text {
|
|
|
|
|
|
font-size: 14px;
|
|
|
|
|
|
color: #909399;
|
|
|
|
|
|
}
|
|
|
|
|
|
&:active {
|
|
|
|
|
|
background-color: #eee;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.info-card picker {
|
|
|
|
|
|
width: 100%;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.picker-header {
|
|
|
|
|
|
margin-bottom: 0;
|
|
|
|
|
|
padding: 14px 0;
|
|
|
|
|
|
border-bottom: 1px solid #f0f0f0;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.card-header {
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
justify-content: space-between;
|
|
|
|
|
|
align-items: center;
|
|
|
|
|
|
cursor: pointer;
|
|
|
|
|
|
.section-title {
|
|
|
|
|
|
font-size: 16px;
|
|
|
|
|
|
font-weight: 500;
|
|
|
|
|
|
color: #303133;
|
|
|
|
|
|
padding-left: 15px;
|
|
|
|
|
|
}
|
|
|
|
|
|
.target-class {
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
align-items: center;
|
|
|
|
|
|
font-size: 14px;
|
|
|
|
|
|
color: #606266;
|
|
|
|
|
|
padding-right: 15px;
|
|
|
|
|
|
text {
|
|
|
|
|
|
margin-right: 5px;
|
|
|
|
|
|
&.placeholder {
|
|
|
|
|
|
color: #909399;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
.uni-icons {
|
|
|
|
|
|
color: #909399 !important;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.name-tags {
|
|
|
|
|
|
padding: 15px;
|
|
|
|
|
|
margin-top: 0;
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
flex-wrap: wrap;
|
|
|
|
|
|
gap: 8px 10px;
|
|
|
|
|
|
position: relative;
|
|
|
|
|
|
min-height: 30px;
|
|
|
|
|
|
|
2025-07-02 22:43:30 +08:00
|
|
|
|
.name-tag {
|
2025-04-22 10:22:33 +08:00
|
|
|
|
font-size: 13px;
|
|
|
|
|
|
padding: 5px 0;
|
|
|
|
|
|
border-radius: 4px;
|
|
|
|
|
|
text-align: center;
|
|
|
|
|
|
flex-grow: 0;
|
|
|
|
|
|
flex-shrink: 0;
|
|
|
|
|
|
box-sizing: border-box;
|
|
|
|
|
|
flex-basis: calc((100% - 50px) / 6);
|
|
|
|
|
|
height: 28px;
|
|
|
|
|
|
line-height: 18px;
|
|
|
|
|
|
background-color: #f4f4f5;
|
|
|
|
|
|
color: #909399;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.loading-spinner {
|
|
|
|
|
|
position: absolute;
|
|
|
|
|
|
top: 5px;
|
|
|
|
|
|
left: 15px;
|
|
|
|
|
|
font-size: 12px;
|
|
|
|
|
|
color: #999;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-07-02 22:43:30 +08:00
|
|
|
|
.more-btn-container {
|
|
|
|
|
|
width: 100%;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.more-btn-full {
|
|
|
|
|
|
width: 100%;
|
|
|
|
|
|
height: 35px;
|
|
|
|
|
|
background-color: #fff2e8;
|
|
|
|
|
|
color: #e6a23c;
|
|
|
|
|
|
border: 1px solid #f5dab1;
|
|
|
|
|
|
border-radius: 4px;
|
|
|
|
|
|
font-size: 14px;
|
|
|
|
|
|
&::after {
|
|
|
|
|
|
border: none;
|
|
|
|
|
|
}
|
|
|
|
|
|
&:active {
|
|
|
|
|
|
background-color: #ffecd1;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-04-22 10:22:33 +08:00
|
|
|
|
.list-item-card {
|
|
|
|
|
|
padding: 0;
|
|
|
|
|
|
.uni-datetime-picker,
|
|
|
|
|
|
.picker {
|
|
|
|
|
|
width: 100%;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.list-item-row {
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
justify-content: space-between;
|
|
|
|
|
|
align-items: center;
|
|
|
|
|
|
padding: 14px 15px;
|
|
|
|
|
|
border-bottom: 1px solid #f0f0f0;
|
|
|
|
|
|
cursor: pointer;
|
|
|
|
|
|
transition: background-color 0.2s;
|
|
|
|
|
|
|
|
|
|
|
|
&:active {
|
|
|
|
|
|
background-color: #fafafa;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
&.no-border {
|
|
|
|
|
|
border-bottom: none;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.list-label {
|
|
|
|
|
|
font-size: 15px;
|
|
|
|
|
|
color: #303133;
|
|
|
|
|
|
}
|
|
|
|
|
|
.list-value {
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
align-items: center;
|
|
|
|
|
|
font-size: 15px;
|
|
|
|
|
|
color: #909399;
|
|
|
|
|
|
text {
|
|
|
|
|
|
margin-right: 5px;
|
|
|
|
|
|
color: #606266;
|
|
|
|
|
|
&.placeholder {
|
|
|
|
|
|
color: #909399;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
.uni-icons {
|
|
|
|
|
|
color: #c0c4cc !important;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
uni-datetime-picker .list-value text,
|
|
|
|
|
|
picker .list-value text {
|
|
|
|
|
|
color: #606266;
|
|
|
|
|
|
}
|
|
|
|
|
|
uni-datetime-picker .list-value text:empty::before,
|
|
|
|
|
|
picker .list-value text:empty::before {
|
|
|
|
|
|
content: "请选择";
|
|
|
|
|
|
color: #909399;
|
|
|
|
|
|
}
|
|
|
|
|
|
picker .list-value text {
|
|
|
|
|
|
color: #606266;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.bottom-actions {
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
justify-content: space-around;
|
|
|
|
|
|
align-items: center;
|
2025-07-02 22:43:30 +08:00
|
|
|
|
padding: 12px 15px;
|
2025-04-22 10:22:33 +08:00
|
|
|
|
background-color: #ffffff;
|
|
|
|
|
|
border-top: 1px solid #e5e5e5;
|
|
|
|
|
|
|
|
|
|
|
|
.action-btn {
|
|
|
|
|
|
width: auto;
|
|
|
|
|
|
min-width: 90px;
|
|
|
|
|
|
margin: 0 5px;
|
|
|
|
|
|
font-size: 15px;
|
|
|
|
|
|
height: 40px;
|
|
|
|
|
|
line-height: 40px;
|
|
|
|
|
|
border-radius: 20px;
|
|
|
|
|
|
padding: 0 20px;
|
|
|
|
|
|
|
|
|
|
|
|
&::after {
|
|
|
|
|
|
border: none;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
&.draft-btn {
|
|
|
|
|
|
background-color: #f4f4f5;
|
|
|
|
|
|
color: #909399;
|
|
|
|
|
|
border: 1px solid #e9e9eb;
|
|
|
|
|
|
}
|
|
|
|
|
|
&.preview-btn {
|
|
|
|
|
|
background-color: #ecf5ff;
|
|
|
|
|
|
color: #409eff;
|
|
|
|
|
|
border: 1px solid #d9ecff;
|
|
|
|
|
|
}
|
|
|
|
|
|
&.publish-btn {
|
|
|
|
|
|
background-color: #409eff;
|
|
|
|
|
|
color: #ffffff;
|
|
|
|
|
|
border: none;
|
|
|
|
|
|
}
|
|
|
|
|
|
&.draft-btn:active {
|
|
|
|
|
|
background-color: #e0e0e0;
|
|
|
|
|
|
}
|
|
|
|
|
|
&.preview-btn:active {
|
|
|
|
|
|
background-color: #d9ecff;
|
|
|
|
|
|
}
|
|
|
|
|
|
&.publish-btn:active {
|
|
|
|
|
|
background-color: #3a8ee6;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.target-class text.placeholder {
|
2025-07-02 22:43:30 +08:00
|
|
|
|
color: #909399;
|
2025-04-22 10:22:33 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2025-07-02 22:43:30 +08:00
|
|
|
|
/* 学生名单弹窗样式 */
|
|
|
|
|
|
.student-modal-mask {
|
|
|
|
|
|
position: fixed;
|
|
|
|
|
|
top: 0;
|
|
|
|
|
|
left: 0;
|
|
|
|
|
|
width: 100%;
|
|
|
|
|
|
height: 100%;
|
|
|
|
|
|
background-color: rgba(0, 0, 0, 0.5);
|
|
|
|
|
|
z-index: 9999;
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
justify-content: center;
|
|
|
|
|
|
align-items: center;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.student-modal {
|
|
|
|
|
|
width: 90%;
|
|
|
|
|
|
max-height: 80%;
|
|
|
|
|
|
background-color: #ffffff;
|
|
|
|
|
|
border-radius: 8px;
|
|
|
|
|
|
overflow: hidden;
|
|
|
|
|
|
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.student-modal-header {
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
justify-content: space-between;
|
|
|
|
|
|
align-items: center;
|
|
|
|
|
|
padding: 15px 20px;
|
|
|
|
|
|
border-bottom: 1px solid #f0f0f0;
|
|
|
|
|
|
background-color: #fafafa;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.modal-title {
|
|
|
|
|
|
font-size: 16px;
|
|
|
|
|
|
font-weight: 500;
|
|
|
|
|
|
color: #303133;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.student-count {
|
|
|
|
|
|
font-size: 14px;
|
|
|
|
|
|
color: #909399;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.close-icon {
|
|
|
|
|
|
cursor: pointer;
|
|
|
|
|
|
padding: 5px;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.student-modal-content {
|
|
|
|
|
|
max-height: 400px;
|
|
|
|
|
|
padding: 20px;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.all-student-tags {
|
|
|
|
|
|
display: flex;
|
|
|
|
|
|
flex-wrap: wrap;
|
|
|
|
|
|
gap: 8px 10px;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
.student-tag {
|
|
|
|
|
|
font-size: 13px;
|
|
|
|
|
|
padding: 6px 12px;
|
|
|
|
|
|
border-radius: 4px;
|
|
|
|
|
|
text-align: center;
|
|
|
|
|
|
background-color: #f4f4f5;
|
|
|
|
|
|
color: #909399;
|
|
|
|
|
|
min-width: 60px;
|
|
|
|
|
|
box-sizing: border-box;
|
|
|
|
|
|
}
|
2025-04-22 10:22:33 +08:00
|
|
|
|
</style>
|