| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493 |
- <template>
- <div class="safety-platform-container">
- <header class="safety-platform-container__header">
- <div class="breadcrumb-title"> 举一反三管理(管理员侧)</div>
- </header>
- <main class="safety-platform-container__main">
- <div class="search-table-container">
- <header>
- <div style="position: relative">
- <el-button type="primary" class="search-table-container--button" @click="handleCreate">
- 新增举一反三
- </el-button>
- <el-button plain class="search-table-container--button" @click="handleDownload">
- 导出
- </el-button>
- </div>
- <div class="act-search">
- <section class="select-box">
- <div class="select-box--item">
- <span>隐患问题:</span>
- <el-input
- v-model="tableQuery.queryParam.problem"
- placeholder="请输入隐患问题"
- class="act-search-input"
- />
- </div>
- <div class="select-box--item">
- <span>状态:</span>
- <el-select
- v-model="tableQuery.queryParam.statusId"
- placeholder="请选择状态"
- clearable
- >
- <el-option label="未下发" :value="2" />
- <el-option label="待反馈" :value="3" />
- <el-option label="待审核" :value="4" />
- <el-option label="已完成" :value="5" />
- <el-option label="已作废" :value="6" />
- </el-select>
- </div>
- <div class="select-box--item">
- <span>计划日期范围:</span>
- <el-date-picker
- v-model="planDateRange"
- type="daterange"
- range-separator="至"
- start-placeholder="开始日期"
- end-placeholder="结束日期"
- value-format="YYYY-MM-DD"
- clearable
- class="act-search-input"
- />
- </div>
- </section>
- <section class="search-btn">
- <el-button type="primary" @click="handleSearch">查询</el-button>
- <el-button @click="handleReset">重置</el-button>
- </section>
- </div>
- </header>
- <div class="batch-table">
- <BasicTable
- ref="basicTableRef"
- :tableData="tableData"
- :tableConfig="tableConfig"
- @update:pageSize="handleSizeChange"
- @update:pageNumber="handleCurrentChange"
- >
- <template #status="scope">
- <span>{{ scope.row.statusName || '-' }}</span>
- </template>
- <template #feedbackRatio="scope">
- <span>{{ scope.row.feedbackRatio ?? (scope.row.issueCount ? `${scope.row.feedbackCount ?? 0}/${scope.row.issueCount}` : '-') }}</span>
- </template>
- <template #action="scope">
- <div class="action-container--div" style="justify-content: left">
- <!-- 未下发 statusId=1 或 2 -->
- <template v-if="scope.row.statusId === 1 || scope.row.statusId === 2">
- <ActionButton text="编辑" @click="handleEdit(scope.row.id)" />
- <ActionButton
- text="删除"
- :popconfirm="{ title: '确定要删除?' }"
- @confirm="handleDelete(scope.row.id)"
- />
- <ActionButton text="通知对象" @click="handleNotifyTarget(scope.row)" />
- <ActionButton text="发送" @click="handleSend(scope.row)" />
- </template>
- <!-- 待反馈 statusId=3 -->
- <template v-else-if="scope.row.statusId === 3">
- <ActionButton text="通知对象" @click="handleNotifyTarget(scope.row)" />
- <!-- <ActionButton text="发送" @click="handleSend(scope.row)" /> -->
- <ActionButton
- text="作废"
- :popconfirm="{ title: '确定要作废该记录?' }"
- @confirm="handleCancel(scope.row.id)"
- />
- </template>
- <!-- 待审核 statusId=4 -->
- <template v-else-if="scope.row.statusId === 4">
- <ActionButton text="通知对象" @click="handleNotifyTarget(scope.row)" />
- <ActionButton text="审核" @click="handleAudit(scope.row.id)" />
- <ActionButton
- text="作废"
- :popconfirm="{ title: '确定要作废该记录?' }"
- @confirm="handleCancel(scope.row.id)"
- />
- </template>
- <!-- 已完成 statusId=5 -->
- <template v-else-if="scope.row.statusId === 5">
- <ActionButton text="通知对象" @click="handleNotifyTarget(scope.row)" />
- </template>
- <!-- 已作废 statusId=6 -->
- <template v-else-if="scope.row.statusId === 6">
- <ActionButton text="通知对象" @click="handleNotifyTarget(scope.row)" />
- <ActionButton
- text="删除"
- :popconfirm="{ title: '确定要删除?' }"
- @confirm="handleDelete(scope.row.id)"
- />
- </template>
- </div>
- </template>
- </BasicTable>
- </div>
- </div>
- </main>
- <!-- 下发举一反三弹窗:点击「发送」弹出,保存时调用 api/drawLessons/admin/issue -->
- <el-dialog
- v-model="showIssueDialog"
- title="下发举一反三"
- width="520px"
- destroy-on-close
- @close="resetIssueForm"
- >
- <el-form ref="issueFormRef" :model="issueForm" :rules="issueRules" label-width="120px">
- <el-form-item label="下发分组名称" prop="groupDeptId" required>
- <el-select
- v-model="issueForm.groupDeptId"
- placeholder="请选择分组名称"
- clearable
- filterable
- style="width: 100%"
- @change="onIssueGroupChange"
- >
- <el-option
- v-for="d in groupOptions"
- :key="d.id"
- :label="d.deptName"
- :value="d.id"
- />
- </el-select>
- </el-form-item>
- <el-form-item label="计划开始日期" prop="planStartDate" required>
- <el-date-picker
- v-model="issueForm.planStartDate"
- type="date"
- value-format="YYYY-MM-DD"
- placeholder="选择计划开始日期"
- style="width: 100%"
- />
- </el-form-item>
- <el-form-item label="计划结束时间" prop="planEndTime" required>
- <el-date-picker
- v-model="issueForm.planEndTime"
- type="date"
- value-format="YYYY-MM-DD"
- placeholder="选择计划结束日期"
- style="width: 100%"
- />
- </el-form-item>
- </el-form>
- <template #footer>
- <el-button @click="showIssueDialog = false">取消</el-button>
- <el-button type="primary" @click="handleIssueSubmit">保存</el-button>
- </template>
- </el-dialog>
- <BatchImport
- v-if="batchImportVisible"
- :visible="batchImportVisible"
- :import-api-url="importApiUrl"
- :template-url="templateUrl"
- template-name="下载模板"
- :show-template="false"
- @close="batchImportVisible = false"
- @update="handleUpdate"
- />
- </div>
- </template>
- <script setup lang="ts">
- import { onMounted, reactive, ref } from 'vue';
- import { ElMessage } from 'element-plus';
- import BasicTable from '@/components/BasicTable.vue';
- import useTableConfig from '@/hooks/useTableConfigHook';
- import ActionButton from '@/components/ActionButton.vue';
- import { TABLE_OPTIONS, DRAW_LESSONS_TABLE_COLUMNS } from './configs/tables';
- import { useRouter } from 'vue-router';
- import type { QueryPageRequest } from '@/types/basic-query';
- import type { FormInstance, FormRules } from 'element-plus';
- import {
- queryDrawLessonsAdminPage,
- deleteDrawLessons,
- voidDrawLessons,
- issueDrawLessons,
- type DrawLessonsQueryParam,
- } from '@/api/drawLessons';
- import { getAllDepartments } from '@/api/auth/dept';
- import type { DeptTree } from '@/types/dept/type';
- import { downloadByData } from '@/utils/file/download';
- import { useGlobSetting } from '@/hooks/setting';
- import urlJoin from 'url-join';
- const router = useRouter();
- // 表格
- const basicTableRef = ref<InstanceType<typeof BasicTable>>();
- const { tableConfig, pagination } = useTableConfig(DRAW_LESSONS_TABLE_COLUMNS, TABLE_OPTIONS);
- const tableData = ref<any[]>([]);
- const planDateRange = ref<[string, string] | null>(null);
- const tableQuery = reactive<QueryPageRequest<DrawLessonsQueryParam>>({
- pageNumber: pagination.pageNumber,
- pageSize: pagination.pageSize,
- queryParam: {
- problem: '',
- statusId: undefined,
- startTime: undefined,
- endTime: undefined,
- },
- });
- const handleSizeChange = (value: number) => {
- pagination.pageSize = value;
- tableQuery.pageSize = value;
- getTableData();
- };
- const handleCurrentChange = (value: number) => {
- pagination.pageNumber = value;
- tableQuery.pageNumber = value;
- getTableData();
- };
- function applyPlanDateRange() {
- if (planDateRange.value && planDateRange.value.length === 2) {
- tableQuery.queryParam.startTime = planDateRange.value[0];
- tableQuery.queryParam.endTime = planDateRange.value[1];
- } else {
- tableQuery.queryParam.startTime = undefined;
- tableQuery.queryParam.endTime = undefined;
- }
- }
- async function getTableData() {
- applyPlanDateRange();
- tableConfig.loading = true;
- try {
- const res = await queryDrawLessonsAdminPage(tableQuery);
- if (res?.records) {
- tableData.value = res.records.map((item: any) => ({
- id: item.id,
- dangerId: item.dangerId,
- problem: item.problem,
- statusId: item.statusId,
- statusName: item.statusName,
- associationOneThree: item.associationOneThree,
- associationOtObligationDeptName: item.associationOtObligationDeptName,
- issueCount: item.issueCount ?? item.sendCount ?? 0,
- feedbackCount: item.feedbackCount ?? 0,
- feedbackRatio: item.feedbackRatio,
- associationOtTimeLimit: item.associationOtTimeLimit,
- }));
- pagination.total = (res as any).totalRow ?? (res as any).total ?? 0;
- }
- } catch (e) {
- console.error('获取举一反三列表失败:', e);
- tableData.value = [];
- pagination.total = 0;
- } finally {
- tableConfig.loading = false;
- }
- }
- const handleSearch = () => {
- pagination.pageNumber = 1;
- tableQuery.pageNumber = 1;
- getTableData();
- };
- const handleReset = () => {
- tableQuery.queryParam.problem = '';
- tableQuery.queryParam.statusId = undefined;
- tableQuery.queryParam.startTime = undefined;
- tableQuery.queryParam.endTime = undefined;
- planDateRange.value = null;
- handleSearch();
- };
- const { urlPrefix } = useGlobSetting();
- const exportApiUrl = ref(urlJoin(urlPrefix, '/api/production/drawLessons/admin/queryPage'));
- const handleDownload = async () => {
- try {
- // 后端如有专门导出接口,可在此替换
- await queryDrawLessonsAdminPage(tableQuery);
- ElMessage.success('导出逻辑待实现');
- } catch (e) {
- console.error('导出举一反三失败:', e);
- ElMessage.error('导出失败,请重试');
- }
- };
- const handleCreate = () => {
- router.push({
- name: 'oneByOneManagementItem',
- query: {
- operate: 'one-by-one-create',
- },
- });
- };
- const handleEdit = (id: number) => {
- router.push({
- name: 'oneByOneManagementItem',
- query: {
- id,
- operate: 'one-by-one-edit',
- },
- });
- };
- const handleDelete = async (id: number) => {
- try {
- await deleteDrawLessons(id);
- ElMessage.success('删除成功');
- getTableData();
- } catch (e) {
- console.error('删除举一反三记录失败:', e);
- ElMessage.error('删除失败,请重试');
- }
- };
- const handleView = (id: number) => {
- router.push({
- name: 'oneByOneManagementItem',
- query: {
- id: String(id),
- operate: 'one-by-one-view',
- },
- });
- };
- /** 审核:跳转审核详情页 */
- const handleAudit = (id: number) => {
- router.push({
- name: 'oneByOneManagementItem',
- query: {
- id: String(id),
- operate: 'one-by-one-audit-detail',
- },
- });
- };
- /** 通知对象:跳转通知对象页(与安全考核管理(管)的考核对象一致) */
- const handleNotifyTarget = (row: { id: number }) => {
- router.push({
- name: 'oneByOneManagementItem',
- query: {
- id: String(row.id),
- operate: 'one-by-one-notify-target',
- },
- });
- };
- /** 发送:弹出「下发举一反三」弹窗,保存时调用 api/drawLessons/admin/issue */
- const showIssueDialog = ref(false);
- const currentIssueRow = ref<{
- id: number;
- dangerId?: number;
- associationOneThree?: string;
- } | null>(null);
- const issueFormRef = ref<FormInstance>();
- const issueForm = ref({
- groupDeptId: undefined as number | undefined,
- groupDeptName: '',
- planStartDate: '',
- planEndTime: '',
- });
- const issueRules: FormRules = {
- groupDeptId: [{ required: true, message: '请选择分组名称', trigger: 'change' }],
- planStartDate: [{ required: true, message: '请选择计划开始日期', trigger: 'change' }],
- planEndTime: [{ required: true, message: '请选择计划结束时间', trigger: 'change' }],
- };
- const groupOptions = ref<Array<{ id: number; deptName: string }>>([]);
- function flattenDeptTree(nodes: DeptTree[] | undefined): Array<{ id: number; deptName: string }> {
- if (!nodes?.length) return [];
- const list: Array<{ id: number; deptName: string }> = [];
- const walk = (items: DeptTree[]) => {
- items.forEach((n) => {
- if (n.id != null) list.push({ id: n.id, deptName: n.deptName ?? '' });
- if (n.children?.length) walk(n.children);
- });
- };
- walk(nodes);
- return list;
- }
- async function loadGroupOptions() {
- try {
- const res = await getAllDepartments();
- const tree = (res as DeptTree[]) ?? [];
- groupOptions.value = flattenDeptTree(Array.isArray(tree) && tree[0]?.children ? tree[0].children : tree);
- } catch (e) {
- console.error('获取分组列表失败:', e);
- groupOptions.value = [];
- }
- }
- function onIssueGroupChange(deptId: number) {
- const d = groupOptions.value.find((x) => x.id === deptId);
- issueForm.value.groupDeptName = d?.deptName ?? '';
- }
- function resetIssueForm() {
- issueForm.value = {
- groupDeptId: undefined,
- groupDeptName: '',
- planStartDate: '',
- planEndTime: '',
- };
- currentIssueRow.value = null;
- }
- function handleSend(row: { id: number; dangerId?: number; associationOneThree?: string }) {
- currentIssueRow.value = { id: row.id, dangerId: row.dangerId, associationOneThree: row.associationOneThree };
- issueForm.value = { groupDeptId: undefined, groupDeptName: '', planStartDate: '', planEndTime: '' };
- showIssueDialog.value = true;
- }
- async function handleIssueSubmit() {
- await issueFormRef.value?.validate?.().catch(() => {});
- if (!currentIssueRow.value) return;
- try {
- await issueDrawLessons({
- associationOtId: currentIssueRow.value.id,
- dangerId: currentIssueRow.value.dangerId,
- associationOtObligationDeptId: issueForm.value.groupDeptId,
- associationOtObligationDeptName: issueForm.value.groupDeptName,
- associationOneThree: currentIssueRow.value.associationOneThree,
- associationOtTimeLimit: issueForm.value.planEndTime || undefined,
- planStartDate: issueForm.value.planStartDate || undefined,
- planEndTime: issueForm.value.planEndTime || undefined,
- });
- ElMessage.success('下发成功');
- showIssueDialog.value = false;
- getTableData();
- } catch (e) {
- console.error('下发失败:', e);
- ElMessage.error('下发失败,请重试');
- }
- }
- /** 作废:调用作废接口变更为已作废状态 */
- const handleCancel = async (id: number) => {
- try {
- await voidDrawLessons(id);
- ElMessage.success('已作废');
- getTableData();
- } catch (e) {
- console.error('作废失败:', e);
- ElMessage.error('作废失败,请重试');
- }
- };
- onMounted(() => {
- loadGroupOptions();
- getTableData();
- });
- </script>
- <style scoped lang="scss">
- @use '@/styles/page-details-layout.scss' as *;
- @use '@/styles/page-main-layout.scss' as *;
- @use '@/styles/basic-table-action.scss' as *;
- @use '@/views/traffic/violation/style/act-search-table.scss' as *;
- </style>
|