feat: save code1

This commit is contained in:
xiangbo 2025-08-15 16:41:42 +08:00
parent 4b06c403a7
commit 9d647f8645
3 changed files with 428 additions and 72 deletions

View File

@ -17,7 +17,7 @@ export function getenum(data) {
* @return {object}
*/
export function getPageList(data) {
return http.request<Res<any>>("post", `/SchoolBusiness/PageList`, {
return http.request<Res<any>>("post", `/SchoolBusiness/QueryPageList`, {
data
});
}

View File

@ -0,0 +1,354 @@
<!-- 新建赴校信息 -->
<template>
<el-dialog
v-model="dialogVisible"
title="新建赴校信息"
width="800px"
align-center
>
<el-form ref="formRef" :model="form" :rules="rules" label-width="120px">
<el-divider>基础信息</el-divider>
<el-row :gutter="12">
<el-col :span="12">
<el-form-item label="学校" prop="baseInfo.school">
<el-select
v-model="form.baseInfo.school"
placeholder="请选择学校"
clearable
filterable
>
<el-option
v-for="s in schoolOptions"
:key="s.value"
:label="s.label"
:value="s.value"
/>
</el-select>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="年级" prop="baseInfo.grade">
<el-select
v-model="form.baseInfo.grade"
placeholder="请选择年级"
clearable
>
<el-option
v-for="g in gradeOptions"
:key="g.value"
:label="g.label"
:value="g.value"
/>
</el-select>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="赴校时间" prop="baseInfo.date">
<el-date-picker
v-model="form.baseInfo.date"
type="date"
value-format="YYYY-MM-DD"
placeholder="请选择日期"
style="width: 100%"
/>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="赴校人员" prop="baseInfo.people">
<el-input
v-model="form.baseInfo.people"
placeholder="请输入人员姓名,逗号分隔"
/>
</el-form-item>
</el-col>
</el-row>
<el-divider>基础工作</el-divider>
<el-row :gutter="12">
<el-col :span="12">
<el-form-item label="开展座谈" prop="work.talk">
<el-radio-group v-model="form.work.talk">
<el-radio :label="true"></el-radio>
<el-radio :label="false"></el-radio>
</el-radio-group>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="座谈情况">
<el-input
v-model="form.work.talkDetail"
:disabled="!form.work.talk"
placeholder="请输入座谈情况"
/>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="开展班会" prop="work.classMeeting">
<el-radio-group v-model="form.work.classMeeting">
<el-radio :label="true"></el-radio>
<el-radio :label="false"></el-radio>
</el-radio-group>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="班会情况">
<el-input
v-model="form.work.classMeetingDetail"
:disabled="!form.work.classMeeting"
placeholder="请输入班会情况"
/>
</el-form-item>
</el-col>
</el-row>
<el-divider>反馈问题</el-divider>
<div style="height: 370px; overflow-y: auto">
<div
v-for="group in feedbackGroups"
:key="group.key"
class="feedback-group"
>
<div class="feedback-header">
<span class="group-title">{{ group.name }}</span>
<el-button type="primary" link @click="addProblem(group.key)"
>添加问题</el-button
>
</div>
<div
v-if="form.feedback[group.key].length === 0"
class="feedback-empty"
>
暂无问题
</div>
<div
v-for="(item, idx) in form.feedback[group.key]"
:key="item.id"
class="feedback-item"
>
<el-input
v-model="item.text"
:placeholder="`请输入${group.name}问题描述`"
/>
<el-button type="danger" text @click="removeProblem(group.key, idx)"
>删除</el-button
>
</div>
<el-divider />
</div>
</div>
</el-form>
<template #footer>
<span class="dialog-footer">
<el-button @click="onCancel"> </el-button>
<el-button type="primary" :loading="submitting" @click="onSubmit"
> </el-button
>
</span>
</template>
</el-dialog>
</template>
<script setup lang="ts" name="AddModal">
import { ref, reactive, computed } from "vue";
import type { FormInstance, FormRules } from "element-plus";
import { ElMessage } from "element-plus";
const props = defineProps<{ visible: boolean }>();
const emit = defineEmits<{ (e: "update:visible", value: boolean): void }>();
const dialogVisible = computed({
get: () => props.visible,
set: v => emit("update:visible", v)
});
const formRef = ref<FormInstance>();
function uid() {
return Math.random().toString(36).slice(2) + Date.now().toString(36);
}
const schoolOptions = ref([
{ label: "第一中学", value: "第一中学" },
{ label: "第二中学", value: "第二中学" },
{ label: "实验中学", value: "实验中学" },
{ label: "外国语学校", value: "外国语学校" }
]);
const gradeOptions = [
{ label: "高一", value: "高一" },
{ label: "高二", value: "高二" },
{ label: "高三", value: "高三" },
{ label: "初三", value: "初三" }
];
type FeedbackKey =
| "leaders"
| "classroom"
| "equipment"
| "students"
| "others";
interface FeedbackItem {
id: string;
text: string;
}
interface FormModel {
baseInfo: {
school?: string;
grade?: string;
date?: string;
people: string;
};
work: {
talk: boolean;
talkDetail: string;
classMeeting: boolean;
classMeetingDetail: string;
};
feedback: Record<FeedbackKey, FeedbackItem[]>;
}
const form = reactive<FormModel>({
baseInfo: {
school: undefined,
grade: undefined,
date: undefined,
people: ""
},
work: {
talk: false,
talkDetail: "",
classMeeting: false,
classMeetingDetail: ""
},
feedback: {
leaders: [],
classroom: [],
equipment: [],
students: [],
others: []
}
});
const rules: FormRules = {
"baseInfo.school": [
{ required: true, message: "请选择学校", trigger: "change" }
],
"baseInfo.grade": [
{ required: true, message: "请选择年级", trigger: "change" }
],
"baseInfo.date": [
{ required: true, message: "请选择赴校时间", trigger: "change" }
],
"baseInfo.people": [
{ required: true, message: "请输入赴校人员", trigger: "blur" }
]
};
const feedbackGroups = [
{ key: "leaders", name: "学校领导班子" },
{ key: "classroom", name: "双师课堂" },
{ key: "equipment", name: "设备" },
{ key: "students", name: "学生" },
{ key: "others", name: "其他" }
] as { key: FeedbackKey; name: string }[];
function addProblem(key: FeedbackKey) {
form.feedback[key].push({ id: uid(), text: "" });
}
function removeProblem(key: FeedbackKey, index: number) {
form.feedback[key].splice(index, 1);
}
//
const feedbackGroupNameMap: Record<FeedbackKey, string> = {
leaders: "学校领导班子",
classroom: "双师课堂",
equipment: "设备",
students: "学生",
others: "其他"
};
function validateFeedbackNotEmpty() {
for (const g of feedbackGroups) {
const items = form.feedback[g.key] || [];
if (items.length === 0) continue; // 0
for (const it of items) {
if (!it.text || !it.text.trim()) {
ElMessage.error(`${feedbackGroupNameMap[g.key]} 的问题内容不能为空`);
return false;
}
}
}
return true;
}
const submitting = ref(false);
function onCancel() {
dialogVisible.value = false;
}
async function onSubmit() {
if (!formRef.value) return;
await formRef.value.validate(valid => {
if (!valid) return;
//
if (!validateFeedbackNotEmpty()) return;
submitting.value = true;
setTimeout(() => {
submitting.value = false;
console.log("Submit payload:", JSON.parse(JSON.stringify(form)));
ElMessage.success("提交成功(已使用假数据)");
dialogVisible.value = false;
resetForm();
}, 600);
});
}
function resetForm() {
form.baseInfo.school = undefined;
form.baseInfo.grade = undefined;
form.baseInfo.date = undefined;
form.baseInfo.people = "";
form.work.talk = false;
form.work.talkDetail = "";
form.work.classMeeting = false;
form.work.classMeetingDetail = "";
(Object.keys(form.feedback) as FeedbackKey[]).forEach(k => {
form.feedback[k] = [];
});
}
</script>
<style scoped lang="scss">
.feedback-group {
margin-bottom: 8px;
}
.feedback-header {
display: flex;
align-items: center;
justify-content: space-between;
margin: 6px 0;
}
.group-title {
font-weight: 600;
}
.feedback-item {
display: flex;
gap: 8px;
align-items: center;
margin-bottom: 6px;
}
.feedback-empty {
margin: 4px 0 8px;
font-size: 13px;
color: #999;
}
:deep(.el-dialog__body) {
max-height: 70vh;
overflow-y: auto;
}
</style>

View File

@ -1,5 +1,5 @@
<template>
<div>
<div style="padding: 20px">
<!-- 搜索区域 -->
<el-form :model="query" inline label-width="80px" class="search-form">
<el-form-item label="学校">
@ -76,7 +76,7 @@
<el-button type="info" @click="handleExport">导出</el-button>
</div>
<!-- 表格区域 -->
<el-table :data="pagedData" border style="width: 100%">
<el-table :data="listData" border style="width: 100%">
<el-table-column prop="school" label="学校" min-width="140" />
<el-table-column prop="grade" label="年级" min-width="100" />
<el-table-column prop="people" label="赴校人员" min-width="120" />
@ -119,7 +119,7 @@
<el-pagination
background
layout="total, sizes, prev, pager, next, jumper"
:total="filteredList.length"
:total="total"
:current-page="page"
:page-size="pageSize"
:page-sizes="[10, 20, 50, 100]"
@ -128,6 +128,7 @@
/>
</div>
</div>
<AddModal v-model:visible="isShowAddModal" />
</template>
<!-- 赴校信息管理菜单 -->
<script setup lang="ts" name="Toschoolinfomanage">
@ -135,7 +136,7 @@ import { getenum, getPageList } from "@/api/toschoolinfomanage";
import { ref, reactive, computed, onMounted } from "vue";
import dayjs from "dayjs";
import { ElMessage } from "element-plus";
import AddModal from "./addModal.vue";
interface TableItem {
id: number;
school: string;
@ -154,7 +155,7 @@ const schoolOptions = ref([
{ label: "实验中学", value: "实验中学" },
{ label: "外国语学校", value: "外国语学校" }
]);
const isShowAddModal = ref(false);
onMounted(() => {
getenum({ TextName: "Name", ValueName: "Id" }).then(res => {
if (res.code === 200) {
@ -164,12 +165,7 @@ onMounted(() => {
}));
}
});
getPageList({
orderBy: "string",
// orderByType: 0,
pageIndex: 1,
pageSize: 10
});
loadList();
});
const gradeOptions = [
@ -187,11 +183,69 @@ const query = reactive({
times: [] as string[]
});
//
const appliedQuery = ref({ ...query });
const page = ref(1);
const pageSize = ref(10);
const total = ref(0);
const listData = ref<TableItem[]>([]);
function mapApiItemToRow(item: any): TableItem {
const peopleArr = Array.isArray(item.schoolBusinessUser)
? item.schoolBusinessUser
: [];
const start = item.startTime
? dayjs(item.startTime).format("YYYY-MM-DD")
: "";
let last = start;
const rec = item.solutionRecord?.record || [];
if (Array.isArray(rec) && rec.length > 0) {
const maxTs = rec
.map((r: any) => r.executionTime)
.filter(Boolean)
.map((t: string) => dayjs(t).valueOf())
.reduce((a: number, b: number) => Math.max(a, b), 0);
if (maxTs) last = dayjs(maxTs).format("YYYY-MM-DD");
}
return {
id: item.id,
school: item.schoolName || "",
grade: item.grade || "",
people: peopleArr.join(""),
times: start,
feedbackTotals: Number(item.feedbackCount) || 0,
solveTotals: Number(item.solveFeedbackCount) || 0,
status: item.solutionEnd ? 1 : 2,
lastTime: last
};
}
async function loadList() {
const payload: any = {
pageIndex: page.value,
pageSize: pageSize.value,
orderBy: "startTime"
};
if (query.school) payload.schoolId = query.school;
if (query.grade) payload.grade = query.grade;
if (query.people) payload.people = query.people.trim();
if (typeof query.status !== "undefined")
payload.solutionEnd = query.status === 1;
if (Array.isArray(query.times) && query.times.length === 2) {
payload.startTimeBegin = query.times[0];
payload.startTimeEnd = query.times[1];
}
try {
const res = await getPageList(payload);
if (res.code === 200) {
const rows = Array.isArray(res.data?.data) ? res.data.data : [];
listData.value = rows.map(mapApiItemToRow);
total.value = Number(res.data?.total) || rows.length;
} else {
ElMessage.error(res.message || "加载失败");
}
} catch (e) {
ElMessage.error("列表加载失败");
}
}
function rand(min: number, max: number) {
return Math.floor(Math.random() * (max - min + 1)) + min;
@ -204,64 +258,9 @@ function randomDate(start: string, end: string) {
return dayjs(v).format("YYYY-MM-DD");
}
function createMockList(len = 87): TableItem[] {
const peoplePool = ["张三", "李四", "王五", "赵六", "陈七", "刘八", "黄九"];
const list: TableItem[] = [];
for (let i = 1; i <= len; i++) {
const school =
schoolOptions.value[rand(0, schoolOptions.value.length - 1)].value;
const grade = gradeOptions[rand(0, gradeOptions.length - 1)].value;
const times = randomDate("2024-01-01", "2025-12-31");
const lastTime = dayjs(times).add(rand(0, 120), "day").format("YYYY-MM-DD");
const feedbackTotals = rand(0, 20);
const solveTotals = rand(0, feedbackTotals);
const status = rand(1, 2) as 1 | 2;
list.push({
id: i,
school,
grade,
people: peoplePool[rand(0, peoplePool.length - 1)],
times,
feedbackTotals,
solveTotals,
status,
lastTime
});
}
return list;
}
const allData = ref<TableItem[]>(createMockList());
const filteredList = computed(() => {
const q = appliedQuery.value;
return allData.value.filter(item => {
const matchSchool = q.school ? item.school === q.school : true;
const matchGrade = q.grade ? item.grade === q.grade : true;
const matchPeople = q.people ? item.people.includes(q.people.trim()) : true;
const matchStatus = q.status ? item.status === q.status : true;
let matchTimes = true;
if (Array.isArray(q.times) && q.times.length === 2) {
const [start, end] = q.times;
const d = dayjs(item.times);
matchTimes =
d.isAfter(dayjs(start).subtract(1, "day")) &&
d.isBefore(dayjs(end).add(1, "day"));
}
return (
matchSchool && matchGrade && matchPeople && matchStatus && matchTimes
);
});
});
const pagedData = computed(() => {
const start = (page.value - 1) * pageSize.value;
return filteredList.value.slice(start, start + pageSize.value);
});
function handleSearch() {
appliedQuery.value = { ...query } as any;
page.value = 1;
loadList();
}
function handleReset() {
@ -270,17 +269,19 @@ function handleReset() {
query.people = "";
query.status = undefined;
query.times = [];
appliedQuery.value = { ...query } as any;
page.value = 1;
loadList();
}
function handlePageChange(p: number) {
page.value = p;
loadList();
}
function handleSizeChange(s: number) {
pageSize.value = s;
page.value = 1;
loadList();
}
function onDelete(row: TableItem) {
@ -297,6 +298,7 @@ function onFollow(row: TableItem) {
function handleAdd() {
console.log("新建");
isShowAddModal.value = true;
}
function handleImport() {
console.log("批量导入");