555 lines
16 KiB
Vue
555 lines
16 KiB
Vue
<template>
|
||
<div>
|
||
<!-- 搜索区域 -->
|
||
<el-form :model="query" inline class="search-form">
|
||
<el-form-item label="学校">
|
||
<el-select
|
||
v-model="query.school"
|
||
placeholder="请选择学校"
|
||
clearable
|
||
filterable
|
||
style="width: 180px"
|
||
>
|
||
<el-option
|
||
v-for="s in schoolOptions"
|
||
:key="s.value"
|
||
:label="s.label"
|
||
:value="s.value"
|
||
/>
|
||
</el-select>
|
||
</el-form-item>
|
||
<el-form-item label="年级">
|
||
<el-select
|
||
v-model="query.grade"
|
||
placeholder="请选择年级"
|
||
clearable
|
||
style="width: 120px"
|
||
>
|
||
<el-option
|
||
v-for="g in gradeOptions"
|
||
:key="g.value"
|
||
:label="g.label"
|
||
:value="g.value"
|
||
/>
|
||
</el-select>
|
||
<el-input-number
|
||
v-show="query.grade"
|
||
v-model="query.gradeYear"
|
||
:min="2020"
|
||
:max="2100"
|
||
/>
|
||
</el-form-item>
|
||
<el-form-item label="赴校人员">
|
||
<el-select
|
||
v-model="query.people"
|
||
placeholder="请选择赴校人员"
|
||
clearable
|
||
filterable
|
||
style="width: 300px"
|
||
>
|
||
<el-option
|
||
v-for="p in peopleOptions"
|
||
:key="p.value"
|
||
:label="p.text"
|
||
:value="p.value"
|
||
/>
|
||
</el-select>
|
||
</el-form-item>
|
||
<el-form-item label="状态">
|
||
<el-select
|
||
v-model="query.solutionEnd"
|
||
placeholder="请选择状态"
|
||
clearable
|
||
style="width: 140px"
|
||
>
|
||
<el-option label="已完结" :value="true" />
|
||
<el-option label="跟进中" :value="false" />
|
||
</el-select>
|
||
</el-form-item>
|
||
<el-form-item label="赴校时间">
|
||
<el-date-picker
|
||
v-model="query.times"
|
||
type="daterange"
|
||
range-separator="至"
|
||
start-placeholder="开始日期"
|
||
end-placeholder="结束日期"
|
||
value-format="YYYY-MM-DD"
|
||
unlink-panels
|
||
style="width: 300px"
|
||
/>
|
||
</el-form-item>
|
||
<el-form-item> </el-form-item>
|
||
</el-form>
|
||
|
||
<!-- 操作按钮区域 -->
|
||
<div style="margin-bottom: 15px">
|
||
<el-button type="primary" :icon="Search" @click="handleSearch">查询</el-button>
|
||
<el-button @click="handleReset">重置</el-button>
|
||
</div>
|
||
<!-- 操作按钮区域 -->
|
||
<div style="margin-bottom: 10px">
|
||
<el-button type="primary" @click="handleAdd">新建</el-button>
|
||
<el-button type="success" @click="handleImport">批量导入</el-button>
|
||
<el-button type="info" @click="handleExport">导出</el-button>
|
||
<el-button type="info" @click="downLoadTpl">下载模版</el-button>
|
||
</div>
|
||
<!-- 表格区域 -->
|
||
<el-table
|
||
:data="listData"
|
||
style="width: 100%"
|
||
:max-height="500"
|
||
:row-class-name="tableRowClassName"
|
||
>
|
||
<el-table-column label="操作" width="200">
|
||
<template #default="{ row }">
|
||
<!-- <el-button size="small" type="danger" plain @click="onDelete(row)"
|
||
>删除</el-button
|
||
> -->
|
||
<el-popconfirm
|
||
confirm-button-text="确定"
|
||
cancel-button-text="取消"
|
||
icon-color="#626AEF"
|
||
title="确定删除吗?"
|
||
@confirm="onDelete(row)"
|
||
>
|
||
<template #reference>
|
||
<el-button type="danger" text size="small">删除</el-button>
|
||
</template>
|
||
</el-popconfirm>
|
||
|
||
<el-button size="small" type="primary" text @click="onDetailOrFollow(row, true)"
|
||
>详情</el-button
|
||
>
|
||
<el-button
|
||
v-if="row.canOperate && !row.solutionEnd"
|
||
size="small"
|
||
type="success"
|
||
text
|
||
@click="onDetailOrFollow(row, false)"
|
||
>跟进</el-button
|
||
>
|
||
</template>
|
||
</el-table-column>
|
||
<el-table-column prop="school" label="学校" min-width="140" />
|
||
<el-table-column label="状态" min-width="80">
|
||
<template #default="{ row }">
|
||
<el-tag :type="row.solutionEnd ? 'success' : 'warning'">
|
||
{{ row.solutionEnd ? "已完结" : "跟进中" }}
|
||
</el-tag>
|
||
</template>
|
||
</el-table-column>
|
||
<el-table-column label="年级" min-width="100">
|
||
<template #default="{ row }"> {{ row.gradeLevel + row.gradeYear }}届 </template>
|
||
</el-table-column>
|
||
<el-table-column prop="people" label="赴校人员" min-width="120" />
|
||
<el-table-column label="赴校时间/解决时间" min-width="140">
|
||
<template #default="{ row }">
|
||
{{ row.times }}{{ row.endTime ? " / " + row.endTime : "" }}
|
||
</template>
|
||
</el-table-column>
|
||
<el-table-column prop="feedbackTotals" label="反馈问题数量" min-width="140" />
|
||
<el-table-column prop="solveTotals" label="解决问题数量" min-width="140" />
|
||
|
||
<!-- <el-table-column prop="lastTime" label="最后跟进时间" min-width="160" /> -->
|
||
</el-table>
|
||
|
||
<!-- 分页 -->
|
||
<div class="pager">
|
||
<el-pagination
|
||
background
|
||
layout="total, sizes, prev, pager, next, jumper"
|
||
:total="total"
|
||
:current-page="page"
|
||
:page-size="pageSize"
|
||
:page-sizes="[10, 20, 50, 100]"
|
||
@current-change="handlePageChange"
|
||
@size-change="handleSizeChange"
|
||
/>
|
||
</div>
|
||
|
||
<!-- 新建 -->
|
||
<AddModal v-model:visible="isShowAddModal" @handleReset="handleReset" />
|
||
<!-- 跟进 -->
|
||
<EditModal
|
||
v-model:visible="isShowEditModal"
|
||
v-if="isShowEditModal"
|
||
:editModalLoading="editModalLoading"
|
||
:detailData="detailData"
|
||
:isDetail="isDetail"
|
||
@handleReset="handleReset"
|
||
/>
|
||
</div>
|
||
</template>
|
||
<!-- 赴校信息管理菜单 -->
|
||
<script setup lang="ts" name="Toschoolinfomanage">
|
||
import {
|
||
addOrEditApi,
|
||
getPageListApi,
|
||
getSchoolBusinessDetailApi,
|
||
deleteSchoolBusinessApi,
|
||
getSchoolBusinessPeopleListApi,
|
||
importExcel,
|
||
} from "@/api/toschoolinfomanage";
|
||
import { getSchoolData } from "@/api/userCenter";
|
||
import { ref, reactive, computed, onMounted } from "vue";
|
||
import dayjs from "dayjs";
|
||
import { ElMessage } from "element-plus";
|
||
import AddModal from "./addModal.vue";
|
||
import { Check, Search } from "@element-plus/icons-vue";
|
||
import EditModal from "./editModal.vue";
|
||
import { message } from "@/utils/message";
|
||
import { errorDay, warningDay } from "./config";
|
||
interface TableItem {
|
||
id: number;
|
||
school: string;
|
||
grade: string;
|
||
people: string;
|
||
gradeLevel?: string;
|
||
gradeYear?: number;
|
||
canOperate: boolean; // 是否可以跟进
|
||
times: string; // YYYY-MM-DD
|
||
endTime: string; //预计解决时间
|
||
feedbackTotals: number;
|
||
solveTotals: number;
|
||
solutionEnd: boolean; // true: 已完结, false: 跟进中
|
||
lastTime: string; // YYYY-MM-DD
|
||
}
|
||
|
||
const schoolOptions = ref([]);
|
||
const peopleOptions = ref([]);
|
||
const isDetail = ref(false);
|
||
|
||
const tableRowClassName = ({ row, rowIndex }: { row: TableItem; rowIndex: number }) => {
|
||
if (row.endTime == null || row.endTime == "" || row.solutionEnd) return "";
|
||
const nD = new Date();
|
||
const d = new Date(row.endTime + " ");
|
||
const startTime = new Date(row.times + " ");
|
||
const daysDiff1 = (nD.getTime() - startTime.getTime()) / (1000 * 60 * 60 * 24);
|
||
const daysDiff = (d.getTime() - nD.getTime()) / (1000 * 60 * 60 * 24);
|
||
if (daysDiff <= -errorDay) {
|
||
return "error-row";
|
||
} else if (daysDiff1 >= warningDay) {
|
||
return "warning-row";
|
||
}
|
||
return "";
|
||
};
|
||
|
||
/**
|
||
* 获取学校下拉数据
|
||
*/
|
||
const getSchoolDataFn = () => {
|
||
getSchoolData().then((res) => {
|
||
if (res.code == 200) {
|
||
schoolOptions.value = res.data.map((i: any) => ({
|
||
label: i.text,
|
||
value: i.value,
|
||
}));
|
||
}
|
||
});
|
||
};
|
||
/**
|
||
* 获取赴校人员下拉数据
|
||
*/
|
||
const getSchoolBusinessPeopleList = () => {
|
||
getSchoolBusinessPeopleListApi({}).then((res: any) => {
|
||
if (res.code == 200) {
|
||
peopleOptions.value = (res.data || []).map((i) => {
|
||
return {
|
||
value: i.text,
|
||
text: i.text,
|
||
};
|
||
});
|
||
}
|
||
});
|
||
};
|
||
const isShowAddModal = ref(false);
|
||
const isShowEditModal = ref(false);
|
||
const editModalLoading = ref(false);
|
||
const detailData = ref({});
|
||
onMounted(() => {
|
||
// addOrEdit();
|
||
loadList();
|
||
getSchoolDataFn();
|
||
getSchoolBusinessPeopleList();
|
||
});
|
||
|
||
const gradeOptions = [
|
||
{ label: "高", value: "高" },
|
||
{ label: "初", value: "初" },
|
||
{ label: "小", value: "小" },
|
||
];
|
||
/**
|
||
* 新建赴校信息提交
|
||
*/
|
||
const addOrEdit = () => {
|
||
addOrEditApi({
|
||
id: 0, //id传0代表新增
|
||
schoolId: 10079,
|
||
schoolName: "系统测试学校",
|
||
grade: "初二",
|
||
gradeLevel: "",
|
||
schoolBusinessUser: ["刘德华123"],
|
||
startTime: "2025-07-24T07:28:38",
|
||
// remark: "string",
|
||
feedbackQuestions: [
|
||
{
|
||
question: "xb测试反馈问题1(双师课堂)",
|
||
questionType: 10,
|
||
sort: "1111111111",
|
||
},
|
||
// {
|
||
// question: "xb测试反馈问题2(设备)",
|
||
// questionType: 15,
|
||
// sort: "2"
|
||
// },
|
||
// {
|
||
// question: "xb测试反馈问题2(学生)",
|
||
// questionType: 20,
|
||
// sort: "3"
|
||
// }
|
||
],
|
||
isDiscussion: true,
|
||
discussion: "开展座谈座谈座谈座谈座谈座谈座谈座谈座谈座谈座谈座谈座谈座谈",
|
||
isClassMeeting: true,
|
||
classMeeting: "班会情况班会情况班会情况班会情况班会情况班会情况班会情况",
|
||
});
|
||
};
|
||
|
||
const query = reactive({
|
||
school: "" as string | undefined,
|
||
grade: "" as string | undefined,
|
||
gradeYear: 2025 as number,
|
||
people: "" as string | undefined,
|
||
solutionEnd: undefined,
|
||
times: [] as string[],
|
||
});
|
||
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 || "",
|
||
gradeYear: item.gradeYear || "",
|
||
gradeLevel: item.gradeLevel || "",
|
||
people: peopleArr.join(","),
|
||
times: start,
|
||
endTime: item.endTime ? dayjs(item.endTime).format("YYYY-MM-DD") : "",
|
||
canOperate: item.canOperate || false, // 是否可以跟进
|
||
feedbackTotals: Number(item.feedbackCount) || 0,
|
||
solveTotals: Number(item.solveFeedbackCount) || 0,
|
||
solutionEnd: item.solutionEnd,
|
||
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 && query.gradeYear) payload.grade = query.grade + query.gradeYear;
|
||
if (query.people) {
|
||
payload.UserName = query.people;
|
||
}
|
||
if (typeof query.solutionEnd !== "undefined") payload.solutionEnd = query.solutionEnd;
|
||
if (Array.isArray(query.times) && query.times.length === 2) {
|
||
payload.startTime = query.times[0];
|
||
payload.endTime = query.times[1];
|
||
}
|
||
try {
|
||
const res = await getPageListApi(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;
|
||
}
|
||
|
||
function randomDate(start: string, end: string) {
|
||
const startTs = dayjs(start).valueOf();
|
||
const endTs = dayjs(end).valueOf();
|
||
const v = rand(startTs, endTs);
|
||
return dayjs(v).format("YYYY-MM-DD");
|
||
}
|
||
|
||
function handleSearch() {
|
||
page.value = 1;
|
||
loadList();
|
||
}
|
||
|
||
function handleReset() {
|
||
query.school = "";
|
||
query.grade = "";
|
||
query.people = "";
|
||
query.solutionEnd = undefined;
|
||
query.times = [];
|
||
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) {
|
||
console.log(`删除`, row);
|
||
deleteSchoolBusinessApi([row.id]).then((res) => {
|
||
if (res.code === 200) {
|
||
message("删除成功", { type: "success" });
|
||
loadList();
|
||
}
|
||
});
|
||
}
|
||
/**
|
||
* 详情或者跟进
|
||
* @param row
|
||
*/
|
||
function onDetailOrFollow(row: TableItem, disabled = false) {
|
||
isDetail.value = disabled;
|
||
editModalLoading.value = true;
|
||
getSchoolBusinessDetailApi(row.id)
|
||
.then((res) => {
|
||
if (res.code === 200 && res.data) {
|
||
detailData.value = res.data;
|
||
isShowEditModal.value = true;
|
||
}
|
||
})
|
||
.finally(() => {
|
||
editModalLoading.value = false;
|
||
});
|
||
}
|
||
/**
|
||
* 跟进
|
||
* @param row
|
||
*/
|
||
// function onFollow(row: TableItem) {
|
||
// isShowEditModal.value = true;
|
||
// editModalLoading.value = true;
|
||
// console.log(`跟进`);
|
||
// getSchoolBusinessDetailApi(row.id)
|
||
// .then(res => {
|
||
// if (res.code === 200 && res.data) {
|
||
// detailData.value = res.data;
|
||
// }
|
||
// })
|
||
// .finally(() => {
|
||
// editModalLoading.value = false;
|
||
// });
|
||
// }
|
||
|
||
function handleAdd() {
|
||
console.log("新建");
|
||
isShowAddModal.value = true;
|
||
}
|
||
function handleImport() {
|
||
console.log("批量导入");
|
||
|
||
let fileE = document.createElement("input");
|
||
fileE.type = "file";
|
||
var formData = new window.FormData();
|
||
fileE.onchange = async function () {
|
||
formData.append("file", fileE.files[0]);
|
||
let res = await importExcel(fileE.files[0]);
|
||
if (res.code != undefined) {
|
||
if (res.code !== 200) return ElMessage.error(res.message);
|
||
else {
|
||
loadList();
|
||
return ElMessage.success("所有数据录入成功");
|
||
}
|
||
} else if (res.type === "application/json") {
|
||
let json = JSON.parse(await res.text());
|
||
let code = json.code || json.Code;
|
||
if (json !== undefined && code !== 200) {
|
||
return ElMessage.error(json.Message);
|
||
} else {
|
||
loadList();
|
||
return ElMessage.success("所有数据录入成功");
|
||
}
|
||
} else if (res === undefined || res.size === 0) {
|
||
loadList();
|
||
return ElMessage.success("所有数据录入成功");
|
||
}
|
||
const url = res && window.URL.createObjectURL(res);
|
||
const link = document.createElement("a");
|
||
link.href = url;
|
||
link.setAttribute("download", "未成功导入的考试信息数据" + ".xlsx");
|
||
document.body.appendChild(link);
|
||
link.click();
|
||
document.body.removeChild(link);
|
||
ElMessage.success("导入失败,已导出错误数据");
|
||
};
|
||
try {
|
||
fileE.click();
|
||
} catch (error) {}
|
||
}
|
||
function handleExport() {
|
||
console.log("导出");
|
||
}
|
||
function downLoadTpl() {
|
||
console.log("下载模版");
|
||
const baseUrl = import.meta.env.VITE_API_BASEURL;
|
||
const excelImportUsersUrl = `${baseUrl}/SchoolBusiness/DwImportTemplate`;
|
||
const link = document.createElement("a");
|
||
link.href = excelImportUsersUrl;
|
||
link.setAttribute("download", "导入赴校信息模板" + ".xlsx");
|
||
document.body.appendChild(link);
|
||
link.click();
|
||
document.body.removeChild(link);
|
||
ElMessage.success("下载成功!");
|
||
}
|
||
</script>
|
||
|
||
<style lang="scss" scoped>
|
||
.search-form {
|
||
margin-bottom: 0px;
|
||
}
|
||
|
||
.pager {
|
||
display: flex;
|
||
justify-content: center;
|
||
margin-top: 12px;
|
||
}
|
||
|
||
:deep(.el-table .warning-row) {
|
||
--el-table-tr-bg-color: rgb(253, 246, 236);
|
||
}
|
||
|
||
:deep(.el-table .error-row) {
|
||
--el-table-tr-bg-color: rgb(255, 227, 227);
|
||
}
|
||
</style>
|