- 新增响应式工具函数与全局移动端样式,统一适配≤768px屏幕 - 添加移动端底部导航组件,替代桌面端侧边栏菜单 - 为经销商、客户、报备列表页新增移动端卡片视图替换表格布局 - 调整所有页面的表单、弹窗与布局适配移动端尺寸 - 优化移动端viewport配置,适配全面屏安全区域 - 修复密码哈希工具,在非安全上下文下回退使用js-sha256库 - 更新依赖包,新增@vueuse/core与js-sha256
890 lines
29 KiB
Vue
890 lines
29 KiB
Vue
<template>
|
||
<div class="report-page">
|
||
<el-card>
|
||
<template #header>
|
||
<div class="card-header">
|
||
<div>
|
||
<span>报备管理</span>
|
||
<el-tag type="info" size="small" style="margin-left: 10px">
|
||
保护期:{{ protectDays }} 天
|
||
</el-tag>
|
||
</div>
|
||
<el-button type="primary" @click="handleAdd" v-if="!userStore.isAdmin">
|
||
<el-icon><Plus /></el-icon>
|
||
提交报备
|
||
</el-button>
|
||
</div>
|
||
</template>
|
||
|
||
<!-- 查询表单 -->
|
||
<el-form :inline="!isMobile" :model="queryForm" class="query-form">
|
||
<el-form-item label="学校名称">
|
||
<el-input v-model="queryForm.customerName" placeholder="请输入学校名称" clearable />
|
||
</el-form-item>
|
||
<el-form-item label="状态" style="width: 180px;">
|
||
<el-select v-model="queryForm.status" placeholder="请选择状态" clearable>
|
||
<el-option label="待审核" :value="0" />
|
||
<el-option label="已通过" :value="1" />
|
||
<el-option label="已驳回" :value="2" />
|
||
<el-option label="已失效" :value="3" />
|
||
<el-option label="已作废" :value="4" />
|
||
</el-select>
|
||
</el-form-item>
|
||
<el-form-item>
|
||
<el-button type="primary" @click="handleQuery">
|
||
<el-icon><Search /></el-icon>
|
||
查询
|
||
</el-button>
|
||
<el-button @click="handleReset">重置</el-button>
|
||
</el-form-item>
|
||
</el-form>
|
||
|
||
<!-- 数据表格 -->
|
||
<el-table v-if="!isMobile" :data="tableData" border stripe v-loading="loading">
|
||
<el-table-column prop="dealerName" label="经销商" v-if="userStore.isAdmin"/>
|
||
<el-table-column prop="schoolName" label="学校名称" />
|
||
<el-table-column prop="product" label="所属产品" show-overflow-tooltip />
|
||
<el-table-column prop="projectType" label="项目类型" show-overflow-tooltip />
|
||
<el-table-column prop="contactPerson" label="负责人" show-overflow-tooltip />
|
||
<el-table-column prop="contactPhone" label="负责人电话" show-overflow-tooltip />
|
||
<el-table-column prop="description" label="报备说明" show-overflow-tooltip />
|
||
<el-table-column prop="status" label="状态">
|
||
<template #default="{ row }">
|
||
<el-tag v-if="row.status === 0" type="warning">待审核</el-tag>
|
||
<el-tag v-else-if="row.status === 1" type="success">已通过</el-tag>
|
||
<el-tag v-else-if="row.status === 2" type="danger">已驳回</el-tag>
|
||
<el-tag v-else-if="row.status === 3" type="info">已失效</el-tag>
|
||
<el-tag v-else-if="row.status === 4" type="warning">已作废</el-tag>
|
||
</template>
|
||
</el-table-column>
|
||
<el-table-column prop="protectEndDate" label="保护期截止" width="180">
|
||
<template #default="{ row }">
|
||
{{ formatDate(row.protectEndDate) }}
|
||
</template>
|
||
</el-table-column>
|
||
<el-table-column prop="createdAt" label="创建时间" width="180" />
|
||
<el-table-column label="操作" width="280" fixed="right">
|
||
<template #default="{ row }">
|
||
<el-button link type="primary" @click="handleView(row)">查看</el-button>
|
||
<el-button
|
||
v-if="row.status === 1"
|
||
link
|
||
type="info"
|
||
@click="handleShowProgress(row)"
|
||
>
|
||
进展
|
||
</el-button>
|
||
<el-button
|
||
v-if="userStore.isAdmin && row.status === 0"
|
||
link
|
||
type="success"
|
||
@click="handleAudit(row)"
|
||
>
|
||
审核
|
||
</el-button>
|
||
<el-button
|
||
v-if="userStore.isAdmin && row.status === 1"
|
||
link
|
||
type="warning"
|
||
@click="handleEdit(row)"
|
||
>
|
||
编辑
|
||
</el-button>
|
||
<el-button
|
||
v-if="!userStore.isAdmin && row.status === 0"
|
||
link
|
||
type="danger"
|
||
@click="handleWithdraw(row)"
|
||
>
|
||
撤回
|
||
</el-button>
|
||
</template>
|
||
</el-table-column>
|
||
</el-table>
|
||
|
||
<!-- 移动端卡片视图 -->
|
||
<div v-if="isMobile" class="mobile-card-list" v-loading="loading">
|
||
<div v-for="row in tableData" :key="row.id" class="mobile-card">
|
||
<div class="card-top">
|
||
<span class="card-title">{{ row.schoolName }}</span>
|
||
<el-tag v-if="row.status === 0" type="warning" size="small">待审核</el-tag>
|
||
<el-tag v-else-if="row.status === 1" type="success" size="small">已通过</el-tag>
|
||
<el-tag v-else-if="row.status === 2" type="danger" size="small">已驳回</el-tag>
|
||
<el-tag v-else-if="row.status === 3" type="info" size="small">已失效</el-tag>
|
||
<el-tag v-else-if="row.status === 4" type="warning" size="small">已作废</el-tag>
|
||
</div>
|
||
<div v-if="userStore.isAdmin" class="card-row"><span class="label">经销商</span><span class="val">{{ row.dealerName || '-' }}</span></div>
|
||
<div class="card-row"><span class="label">所属产品</span><span class="val">{{ row.product || '-' }}</span></div>
|
||
<div class="card-row"><span class="label">项目类型</span><span class="val">{{ row.projectType || '-' }}</span></div>
|
||
<div class="card-row"><span class="label">负责人</span><span class="val">{{ row.contactPerson || '-' }}</span></div>
|
||
<div class="card-row"><span class="label">负责人电话</span><span class="val">{{ row.contactPhone || '-' }}</span></div>
|
||
<div class="card-row"><span class="label">报备说明</span><span class="val">{{ row.description || '-' }}</span></div>
|
||
<div class="card-row"><span class="label">保护期截止</span><span class="val">{{ formatDate(row.protectEndDate) }}</span></div>
|
||
<div class="card-row"><span class="label">创建时间</span><span class="val">{{ row.createdAt }}</span></div>
|
||
<div class="card-actions">
|
||
<el-button link type="primary" @click="handleView(row)">查看</el-button>
|
||
<el-button v-if="row.status === 1" link type="info" @click="handleShowProgress(row)">进展</el-button>
|
||
<el-button v-if="userStore.isAdmin && row.status === 0" link type="success" @click="handleAudit(row)">审核</el-button>
|
||
<el-button v-if="userStore.isAdmin && row.status === 1" link type="warning" @click="handleEdit(row)">编辑</el-button>
|
||
<el-button v-if="!userStore.isAdmin && row.status === 0" link type="danger" @click="handleWithdraw(row)">撤回</el-button>
|
||
</div>
|
||
</div>
|
||
<el-empty v-if="!loading && tableData.length === 0" description="暂无报备数据" />
|
||
</div>
|
||
|
||
<!-- 分页 -->
|
||
<el-config-provider :locale="zhCn">
|
||
<el-pagination
|
||
v-model:current-page="queryForm.current"
|
||
v-model:page-size="queryForm.size"
|
||
:total="total"
|
||
:page-sizes="[10, 20, 50, 100]"
|
||
:layout="isMobile ? 'prev, pager, next' : 'total, sizes, prev, pager, next, jumper'"
|
||
prev-text="上一页"
|
||
next-text="下一页"
|
||
@size-change="fetchData"
|
||
@current-change="fetchData"
|
||
:style="{ marginTop: '20px', justifyContent: isMobile ? 'center' : 'flex-end' }"
|
||
/>
|
||
</el-config-provider>
|
||
</el-card>
|
||
|
||
<!-- 提交报备对话框 -->
|
||
<el-dialog v-model="dialogVisible" title="提交报备" :width="isMobile ? '92%' : '650px'" @close="handleDialogClose">
|
||
<el-form ref="formRef" :model="formData" :rules="rules" label-width="100px">
|
||
<el-form-item label="学校" prop="schoolName">
|
||
<el-autocomplete
|
||
v-model="formData.schoolName"
|
||
:fetch-suggestions="searchSchool"
|
||
placeholder="请输入学校名称"
|
||
:trigger-on-focus="false"
|
||
style="width: 100%"
|
||
@select="handleSchoolSelect"
|
||
value-key="schoolName"
|
||
clearable
|
||
>
|
||
<template #default="{ item }">
|
||
<div>{{ item.schoolName }}</div>
|
||
<div style="font-size: 12px; color: #999;">{{ item.location || '无地址信息' }}</div>
|
||
</template>
|
||
</el-autocomplete>
|
||
</el-form-item>
|
||
<el-form-item label="所属产品" prop="product">
|
||
<el-input
|
||
v-model="formData.product"
|
||
placeholder="请输入所属产品"
|
||
clearable
|
||
/>
|
||
</el-form-item>
|
||
<el-form-item label="项目类型" prop="projectType">
|
||
<el-input
|
||
v-model="formData.projectType"
|
||
placeholder="请输入项目类型"
|
||
clearable
|
||
/>
|
||
</el-form-item>
|
||
<el-form-item label="负责人" prop="contactPerson">
|
||
<el-input
|
||
v-model="formData.contactPerson"
|
||
placeholder="请输入负责人姓名"
|
||
clearable
|
||
/>
|
||
</el-form-item>
|
||
<el-form-item label="负责人电话" prop="contactPhone">
|
||
<el-input
|
||
v-model="formData.contactPhone"
|
||
placeholder="请输入负责人电话"
|
||
clearable
|
||
/>
|
||
</el-form-item>
|
||
<el-form-item label="报备说明" prop="description">
|
||
<el-input
|
||
v-model="formData.description"
|
||
type="textarea"
|
||
:rows="4"
|
||
placeholder="请输入报备说明"
|
||
/>
|
||
</el-form-item>
|
||
</el-form>
|
||
<template #footer>
|
||
<el-button @click="dialogVisible = false">取消</el-button>
|
||
<el-button type="primary" @click="handleSubmit">提交</el-button>
|
||
</template>
|
||
</el-dialog>
|
||
|
||
<!-- 审核对话框 -->
|
||
<el-dialog v-model="auditDialogVisible" title="审核报备" :width="isMobile ? '92%' : '600px'">
|
||
<el-form :model="auditForm" label-width="100px">
|
||
<el-form-item label="审核结果">
|
||
<el-radio-group v-model="auditForm.approved">
|
||
<el-radio :label="true">通过</el-radio>
|
||
<el-radio :label="false">驳回</el-radio>
|
||
</el-radio-group>
|
||
</el-form-item>
|
||
<el-form-item label="驳回原因" prop="rejectReason" v-if="!auditForm.approved">
|
||
<el-input
|
||
v-model="auditForm.rejectReason"
|
||
type="textarea"
|
||
:rows="4"
|
||
placeholder="请输入驳回原因"
|
||
/>
|
||
</el-form-item>
|
||
</el-form>
|
||
<template #footer>
|
||
<el-button @click="auditDialogVisible = false">取消</el-button>
|
||
<el-button type="primary" @click="handleAuditSubmit">确定</el-button>
|
||
</template>
|
||
</el-dialog>
|
||
|
||
<!-- 编辑报备对话框 -->
|
||
<el-dialog v-model="editDialogVisible" title="编辑报备" :width="isMobile ? '92%' : '600px'" @close="handleEditDialogClose">
|
||
<el-form ref="editFormRef" :model="editFormData" :rules="editRules" label-width="100px">
|
||
<el-form-item label="学校名称">
|
||
<el-input :model-value="currentReport?.schoolName" disabled />
|
||
</el-form-item>
|
||
<el-form-item label="所属产品" prop="product">
|
||
<el-input
|
||
v-model="editFormData.product"
|
||
placeholder="请输入所属产品"
|
||
clearable
|
||
/>
|
||
</el-form-item>
|
||
<el-form-item label="项目类型" prop="projectType">
|
||
<el-input
|
||
v-model="editFormData.projectType"
|
||
placeholder="请输入项目类型"
|
||
clearable
|
||
/>
|
||
</el-form-item>
|
||
<el-form-item label="负责人" prop="contactPerson">
|
||
<el-input
|
||
v-model="editFormData.contactPerson"
|
||
placeholder="请输入负责人姓名"
|
||
clearable
|
||
/>
|
||
</el-form-item>
|
||
<el-form-item label="负责人电话" prop="contactPhone">
|
||
<el-input
|
||
v-model="editFormData.contactPhone"
|
||
placeholder="请输入负责人电话"
|
||
clearable
|
||
/>
|
||
</el-form-item>
|
||
<el-form-item label="报备说明" prop="description">
|
||
<el-input
|
||
v-model="editFormData.description"
|
||
type="textarea"
|
||
:rows="4"
|
||
placeholder="请输入报备说明"
|
||
/>
|
||
</el-form-item>
|
||
<el-form-item label="状态" prop="status">
|
||
<el-select v-model="editFormData.status" placeholder="请选择状态" style="width: 100%">
|
||
<el-option label="已通过" :value="1" />
|
||
<el-option label="已作废" :value="4" />
|
||
</el-select>
|
||
</el-form-item>
|
||
<el-form-item label="作废原因" prop="cancelReason" v-if="editFormData.status === 4">
|
||
<el-input
|
||
v-model="editFormData.cancelReason"
|
||
type="textarea"
|
||
:rows="4"
|
||
placeholder="请填写作废原因"
|
||
/>
|
||
</el-form-item>
|
||
</el-form>
|
||
<template #footer>
|
||
<el-button @click="editDialogVisible = false">取消</el-button>
|
||
<el-button type="primary" @click="handleEditSubmit">确定</el-button>
|
||
</template>
|
||
</el-dialog>
|
||
|
||
<!-- 报备详情对话框 -->
|
||
<el-dialog v-model="detailDialogVisible" title="报备详情" :width="isMobile ? '92%' : '700px'">
|
||
<el-descriptions :column="isMobile ? 1 : 2" border>
|
||
<el-descriptions-item label="经销商">
|
||
{{ currentReport?.dealerName || '-' }}
|
||
</el-descriptions-item>
|
||
<el-descriptions-item label="学校名称">
|
||
{{ currentReport?.schoolName || '-' }}
|
||
</el-descriptions-item>
|
||
<el-descriptions-item label="所属产品">
|
||
{{ currentReport?.product || '-' }}
|
||
</el-descriptions-item>
|
||
<el-descriptions-item label="项目类型">
|
||
{{ currentReport?.projectType || '-' }}
|
||
</el-descriptions-item>
|
||
<el-descriptions-item label="项目负责人">
|
||
{{ currentReport?.contactPerson || '-' }}
|
||
</el-descriptions-item>
|
||
<el-descriptions-item label="项目电话">
|
||
{{ currentReport?.contactPhone || '-' }}
|
||
</el-descriptions-item>
|
||
<el-descriptions-item label="状态" :span="2">
|
||
<el-tag v-if="currentReport?.status === 0" type="warning">待审核</el-tag>
|
||
<el-tag v-else-if="currentReport?.status === 1" type="success">已通过</el-tag>
|
||
<el-tag v-else-if="currentReport?.status === 2" type="danger">已驳回</el-tag>
|
||
<el-tag v-else-if="currentReport?.status === 3" type="info">已失效</el-tag>
|
||
<el-tag v-else-if="currentReport?.status === 4" type="warning">已作废</el-tag>
|
||
</el-descriptions-item>
|
||
<el-descriptions-item label="保护期开始" :span="1">
|
||
{{ formatDate(currentReport?.protectStartDate) }}
|
||
</el-descriptions-item>
|
||
<el-descriptions-item label="保护期结束" :span="1">
|
||
{{ formatDate(currentReport?.protectEndDate) }}
|
||
</el-descriptions-item>
|
||
<el-descriptions-item label="剩余保护天数" v-if="currentReport?.status === 1 && currentReport?.remainDays !== undefined">
|
||
<el-tag :type="currentReport.remainDays > 7 ? 'success' : currentReport.remainDays > 0 ? 'warning' : 'danger'">
|
||
{{ currentReport.remainDays }} 天
|
||
</el-tag>
|
||
</el-descriptions-item>
|
||
<el-descriptions-item label="驳回原因" v-if="currentReport?.status === 2" :span="2">
|
||
<span style="color: #f56c6c;">{{ currentReport?.rejectReason || '-' }}</span>
|
||
</el-descriptions-item>
|
||
<el-descriptions-item label="作废原因" v-if="currentReport?.status === 4" :span="2">
|
||
<span style="color: #e6a23c;">{{ currentReport?.cancelReason || '-' }}</span>
|
||
</el-descriptions-item>
|
||
<el-descriptions-item label="报备说明" :span="2">
|
||
<div style="white-space: pre-wrap;">{{ currentReport?.description || '-' }}</div>
|
||
</el-descriptions-item>
|
||
<el-descriptions-item label="创建时间" :span="2">
|
||
{{ currentReport?.createdAt || '-' }}
|
||
</el-descriptions-item>
|
||
<el-descriptions-item label="更新时间" :span="2">
|
||
{{ currentReport?.updatedAt || '-' }}
|
||
</el-descriptions-item>
|
||
</el-descriptions>
|
||
<template #footer>
|
||
<el-button type="primary" @click="detailDialogVisible = false">关闭</el-button>
|
||
</template>
|
||
</el-dialog>
|
||
|
||
<!-- 进展记录对话框 -->
|
||
<el-dialog v-model="progressDialogVisible" title="报备进展记录" :width="isMobile ? '92%' : '1000px'" :fullscreen="isMobile" @close="handleProgressDialogClose">
|
||
<el-card>
|
||
<template #header>
|
||
<div class="card-header">
|
||
<span>添加进展记录</span>
|
||
</div>
|
||
</template>
|
||
<el-form ref="progressFormRef" :model="progressFormData" :rules="progressRules" label-width="100px">
|
||
<el-form-item label="进展内容" prop="progressContent">
|
||
<el-input
|
||
v-model="progressFormData.progressContent"
|
||
type="textarea"
|
||
:rows="4"
|
||
placeholder="请输入进展内容"
|
||
/>
|
||
</el-form-item>
|
||
</el-form>
|
||
<template #footer>
|
||
<el-button v-if="editingProgressId" @click="handleCancelEdit">取消编辑</el-button>
|
||
<el-button type="primary" @click="handleSaveProgress">{{ editingProgressId ? '保存修改' : '添加' }}</el-button>
|
||
</template>
|
||
</el-card>
|
||
|
||
<el-divider content-position="left">进展记录列表</el-divider>
|
||
|
||
<el-table :data="progressList" border stripe v-loading="progressLoading">
|
||
<el-table-column prop="progressContent" label="进展内容" show-overflow-tooltip />
|
||
<el-table-column prop="createdAt" label="创建时间" width="180" />
|
||
<el-table-column prop="updatedAt" label="更新时间" width="180" />
|
||
<el-table-column label="操作" width="150" fixed="right">
|
||
<template #default="{ row }">
|
||
<el-button
|
||
v-if="!userStore.isAdmin"
|
||
link
|
||
type="primary"
|
||
@click="handleEditProgress(row)"
|
||
>
|
||
编辑
|
||
</el-button>
|
||
</template>
|
||
</el-table-column>
|
||
</el-table>
|
||
</el-dialog>
|
||
</div>
|
||
</template>
|
||
|
||
<script setup lang="ts">
|
||
import { ref, reactive, onMounted } from 'vue'
|
||
import { useRoute } from 'vue-router'
|
||
import { ElMessage, ElMessageBox, type FormInstance, type FormRules } from 'element-plus'
|
||
import { useUserStore } from '@/stores/user'
|
||
import zhCn from 'element-plus/dist/locale/zh-cn.mjs'
|
||
import { getReportPage, createReport, auditReport, withdrawReport, updateReport } from '@/api/report'
|
||
import { getProgressByReportId, createProgress, updateProgress } from '@/api/report-progress'
|
||
import { searchSchoolByName } from '@/api/school'
|
||
import { getConfigValue } from '@/api/system'
|
||
import type { Report, ReportForm, ReportProgress, ReportProgressForm, ReportUpdateForm } from '@/types'
|
||
import { useResponsive } from '@/composables/useResponsive'
|
||
|
||
const route = useRoute()
|
||
const userStore = useUserStore()
|
||
|
||
const protectDays = ref<number>(90)
|
||
const { isMobile } = useResponsive()
|
||
|
||
const loading = ref(false)
|
||
const tableData = ref<Report[]>([])
|
||
const total = ref(0)
|
||
const dialogVisible = ref(false)
|
||
const auditDialogVisible = ref(false)
|
||
const detailDialogVisible = ref(false)
|
||
const editDialogVisible = ref(false)
|
||
const auditReportId = ref<number>()
|
||
const editingReportId = ref<number>()
|
||
const formRef = ref<FormInstance>()
|
||
const editFormRef = ref<FormInstance>()
|
||
const selectedSchool = ref<any>(null)
|
||
const currentReport = ref<Report | null>(null)
|
||
|
||
// 进展记录相关
|
||
const progressDialogVisible = ref(false)
|
||
const progressFormRef = ref<FormInstance>()
|
||
const progressFormData = reactive<ReportProgressForm>({
|
||
reportId: 0,
|
||
progressContent: ''
|
||
})
|
||
const progressList = ref<ReportProgress[]>([])
|
||
const editingProgressId = ref<number | undefined>()
|
||
const progressLoading = ref(false)
|
||
|
||
const progressRules: FormRules = {
|
||
progressContent: [{ required: true, message: '请输入进展内容', trigger: 'blur' }]
|
||
}
|
||
|
||
const queryForm = reactive({
|
||
current: 1,
|
||
size: 10,
|
||
customerName: '',
|
||
status: undefined as number | undefined
|
||
})
|
||
|
||
const formData = reactive<ReportForm>({
|
||
schoolId: undefined,
|
||
schoolName: '',
|
||
product: '',
|
||
projectType: '',
|
||
description: '',
|
||
contactPerson: '',
|
||
contactPhone: ''
|
||
})
|
||
|
||
const auditForm = reactive({
|
||
approved: true,
|
||
rejectReason: ''
|
||
})
|
||
|
||
const editFormData = reactive<ReportUpdateForm>({
|
||
product: '',
|
||
projectType: '',
|
||
description: '',
|
||
contactPerson: '',
|
||
contactPhone: '',
|
||
status: 1,
|
||
cancelReason: ''
|
||
})
|
||
|
||
const rules: FormRules = {
|
||
schoolName: [{ required: true, message: '请输入学校名称', trigger: 'blur' }],
|
||
product: [{ required: true, message: '请输入所属产品', trigger: 'blur' }],
|
||
projectType: [{ required: true, message: '请输入项目类型', trigger: 'blur' }]
|
||
}
|
||
|
||
const editRules: FormRules = {
|
||
product: [{ required: true, message: '请输入所属产品', trigger: 'blur' }],
|
||
projectType: [{ required: true, message: '请输入项目类型', trigger: 'blur' }],
|
||
status: [{ required: true, message: '请选择状态', trigger: 'change' }],
|
||
cancelReason: [{ required: true, message: '请填写作废原因', trigger: 'blur' }]
|
||
}
|
||
|
||
// 格式化日期,去掉时分秒
|
||
const formatDate = (dateStr: string | null | undefined) => {
|
||
if (!dateStr) return '-'
|
||
return dateStr.split(' ')[0] // 只保留日期部分,去掉时间
|
||
}
|
||
|
||
// 显示进展记录对话框
|
||
const handleShowProgress = async (row: Report) => {
|
||
progressFormData.reportId = row.id
|
||
progressFormData.progressContent = ''
|
||
editingProgressId.value = undefined
|
||
await fetchProgressList(row.id)
|
||
progressDialogVisible.value = true
|
||
}
|
||
|
||
// 获取进展记录列表
|
||
const fetchProgressList = async (reportId: number) => {
|
||
progressLoading.value = true
|
||
try {
|
||
const res = await getProgressByReportId(reportId)
|
||
progressList.value = res
|
||
} catch (error) {
|
||
console.error('获取进展记录失败', error)
|
||
progressList.value = []
|
||
} finally {
|
||
progressLoading.value = false
|
||
}
|
||
}
|
||
|
||
// 保存进展记录
|
||
const handleSaveProgress = async () => {
|
||
if (!progressFormRef.value) return
|
||
await progressFormRef.value.validate(async (valid) => {
|
||
if (valid) {
|
||
try {
|
||
if (editingProgressId.value) {
|
||
// 编辑模式
|
||
await updateProgress(editingProgressId.value, progressFormData)
|
||
ElMessage.success('更新成功')
|
||
} else {
|
||
// 新增模式
|
||
await createProgress(progressFormData)
|
||
ElMessage.success('添加成功')
|
||
}
|
||
// 重新获取进展记录列表
|
||
await fetchProgressList(progressFormData.reportId)
|
||
// 重置表单
|
||
progressFormData.progressContent = ''
|
||
editingProgressId.value = undefined
|
||
} catch (error) {
|
||
console.error('保存进展记录失败', error)
|
||
}
|
||
}
|
||
})
|
||
}
|
||
|
||
// 编辑进展记录
|
||
const handleEditProgress = (row: ReportProgress) => {
|
||
editingProgressId.value = row.id
|
||
progressFormData.progressContent = row.progressContent
|
||
}
|
||
|
||
// 取消编辑
|
||
const handleCancelEdit = () => {
|
||
editingProgressId.value = undefined
|
||
progressFormData.progressContent = ''
|
||
}
|
||
|
||
// 关闭进展记录对话框
|
||
const handleProgressDialogClose = () => {
|
||
progressDialogVisible.value = false
|
||
progressFormRef.value?.resetFields()
|
||
progressFormData.progressContent = ''
|
||
editingProgressId.value = undefined
|
||
progressList.value = []
|
||
}
|
||
|
||
// 查询数据
|
||
const fetchData = async () => {
|
||
loading.value = true
|
||
try {
|
||
const res = await getReportPage(queryForm)
|
||
tableData.value = res.records
|
||
total.value = res.total
|
||
} catch (error) {
|
||
console.error('查询失败', error)
|
||
} finally {
|
||
loading.value = false
|
||
}
|
||
}
|
||
|
||
// 搜索学校
|
||
const searchSchool = async (queryString: string, cb: any) => {
|
||
if (!queryString || queryString.trim().length < 1) {
|
||
cb([])
|
||
return
|
||
}
|
||
try {
|
||
const schools = await searchSchoolByName(queryString)
|
||
const suggestions = schools.map((school: any) => ({
|
||
...school,
|
||
value: school.schoolName
|
||
}))
|
||
cb(suggestions)
|
||
} catch (error) {
|
||
console.error('搜索学校失败', error)
|
||
cb([])
|
||
}
|
||
}
|
||
|
||
// 学校选择回调
|
||
const handleSchoolSelect = (item: any) => {
|
||
selectedSchool.value = item
|
||
formData.schoolId = item.id
|
||
}
|
||
|
||
// 查询按钮
|
||
const handleQuery = () => {
|
||
queryForm.current = 1
|
||
fetchData()
|
||
}
|
||
|
||
// 重置按钮
|
||
const handleReset = () => {
|
||
queryForm.customerName = ''
|
||
queryForm.status = undefined
|
||
queryForm.current = 1
|
||
fetchData()
|
||
}
|
||
|
||
// 新增按钮
|
||
const handleAdd = () => {
|
||
// 自动填充经销商的联系人和电话
|
||
if (userStore.userInfo?.dealerContactPerson) {
|
||
formData.contactPerson = userStore.userInfo.dealerContactPerson
|
||
}
|
||
if (userStore.userInfo?.dealerContactPhone) {
|
||
formData.contactPhone = userStore.userInfo.dealerContactPhone
|
||
}
|
||
dialogVisible.value = true
|
||
}
|
||
|
||
// 查看详情
|
||
const handleView = (row: Report) => {
|
||
currentReport.value = row
|
||
detailDialogVisible.value = true
|
||
}
|
||
|
||
// 审核按钮
|
||
const handleAudit = (row: Report) => {
|
||
auditReportId.value = row.id
|
||
auditForm.approved = true
|
||
auditForm.rejectReason = ''
|
||
auditDialogVisible.value = true
|
||
}
|
||
|
||
// 编辑按钮
|
||
const handleEdit = (row: Report) => {
|
||
editingReportId.value = row.id
|
||
currentReport.value = row
|
||
editFormData.product = row.product || ''
|
||
editFormData.projectType = row.projectType || ''
|
||
editFormData.description = row.description || ''
|
||
editFormData.contactPerson = row.contactPerson || ''
|
||
editFormData.contactPhone = row.contactPhone || ''
|
||
editFormData.status = row.status
|
||
editFormData.cancelReason = row.cancelReason || ''
|
||
editDialogVisible.value = true
|
||
}
|
||
|
||
// 编辑对话框关闭
|
||
const handleEditDialogClose = () => {
|
||
editFormRef.value?.resetFields()
|
||
Object.assign(editFormData, {
|
||
product: '',
|
||
projectType: '',
|
||
description: '',
|
||
contactPerson: '',
|
||
contactPhone: '',
|
||
status: 1,
|
||
cancelReason: ''
|
||
})
|
||
editingReportId.value = undefined
|
||
}
|
||
|
||
// 编辑提交
|
||
const handleEditSubmit = async () => {
|
||
if (!editingReportId.value) return
|
||
|
||
// 验证表单
|
||
if (editFormData.status === 4 && !editFormData.cancelReason) {
|
||
ElMessage.warning('请填写作废原因')
|
||
return
|
||
}
|
||
|
||
try {
|
||
await updateReport(editingReportId.value, editFormData)
|
||
ElMessage.success('编辑成功')
|
||
editDialogVisible.value = false
|
||
fetchData()
|
||
} catch (error) {
|
||
console.error('编辑失败', error)
|
||
}
|
||
}
|
||
|
||
// 撤回按钮
|
||
const handleWithdraw = async (row: Report) => {
|
||
try {
|
||
await ElMessageBox.confirm(
|
||
'确定要撤回该报备吗?',
|
||
'提示',
|
||
{
|
||
confirmButtonText: '确定',
|
||
cancelButtonText: '取消',
|
||
type: 'warning'
|
||
}
|
||
)
|
||
await withdrawReport(row.id)
|
||
ElMessage.success('撤回成功')
|
||
fetchData()
|
||
} catch (error) {
|
||
// 取消撤回
|
||
}
|
||
}
|
||
|
||
// 提交报备
|
||
const handleSubmit = async () => {
|
||
if (!formRef.value) return
|
||
await formRef.value.validate(async (valid) => {
|
||
if (valid) {
|
||
try {
|
||
await createReport(formData)
|
||
ElMessage.success('提交成功')
|
||
dialogVisible.value = false
|
||
fetchData()
|
||
} catch (error) {
|
||
console.error('提交失败', error)
|
||
}
|
||
}
|
||
})
|
||
}
|
||
|
||
// 审核提交
|
||
const handleAuditSubmit = async () => {
|
||
if (!auditReportId.value) return
|
||
if (!auditForm.approved && !auditForm.rejectReason) {
|
||
ElMessage.warning('请输入驳回原因')
|
||
return
|
||
}
|
||
try {
|
||
await auditReport(auditReportId.value, auditForm)
|
||
ElMessage.success('审核成功')
|
||
auditDialogVisible.value = false
|
||
fetchData()
|
||
} catch (error) {
|
||
console.error('审核失败', error)
|
||
}
|
||
}
|
||
|
||
// 对话框关闭
|
||
const handleDialogClose = () => {
|
||
formRef.value?.resetFields()
|
||
Object.assign(formData, {
|
||
schoolId: undefined,
|
||
schoolName: '',
|
||
product: '',
|
||
projectType: '',
|
||
description: '',
|
||
contactPerson: '',
|
||
contactPhone: ''
|
||
})
|
||
selectedSchool.value = null
|
||
}
|
||
|
||
// 获取保护期配置
|
||
const fetchProtectDays = async () => {
|
||
try {
|
||
const res = await getConfigValue('report.protect.days')
|
||
protectDays.value = parseInt(res) || 90
|
||
} catch (error) {
|
||
console.error('获取配置失败', error)
|
||
}
|
||
}
|
||
|
||
onMounted(() => {
|
||
fetchProtectDays()
|
||
|
||
// 从路由查询参数中读取初始查询条件
|
||
const { status } = route.query
|
||
if (status !== undefined) {
|
||
queryForm.status = status === '' ? undefined : parseInt(status as string)
|
||
}
|
||
|
||
fetchData()
|
||
})
|
||
</script>
|
||
|
||
<style scoped>
|
||
.report-page {
|
||
width: 100%;
|
||
}
|
||
|
||
.card-header {
|
||
display: flex;
|
||
justify-content: space-between;
|
||
align-items: center;
|
||
}
|
||
|
||
.query-form {
|
||
margin-bottom: 20px;
|
||
}
|
||
|
||
/* 详情对话框样式优化 */
|
||
:deep(.el-descriptions) {
|
||
margin-top: 10px;
|
||
}
|
||
|
||
:deep(.el-descriptions__label) {
|
||
font-weight: 600;
|
||
background-color: #fafafa;
|
||
}
|
||
|
||
:deep(.el-descriptions__content) {
|
||
color: #333;
|
||
}
|
||
|
||
/* ===== 移动端卡片视图 ===== */
|
||
.mobile-card-list {
|
||
display: flex;
|
||
flex-direction: column;
|
||
gap: 10px;
|
||
}
|
||
|
||
.mobile-card {
|
||
background: #fff;
|
||
border: 1px solid #ebeef5;
|
||
border-radius: 8px;
|
||
padding: 12px;
|
||
}
|
||
|
||
.card-top {
|
||
display: flex;
|
||
justify-content: space-between;
|
||
align-items: center;
|
||
margin-bottom: 8px;
|
||
}
|
||
|
||
.card-title {
|
||
font-size: 15px;
|
||
font-weight: 600;
|
||
color: #303133;
|
||
}
|
||
|
||
.card-row {
|
||
display: flex;
|
||
align-items: flex-start;
|
||
font-size: 13px;
|
||
color: #606266;
|
||
line-height: 1.8;
|
||
}
|
||
|
||
.card-row .label {
|
||
width: 80px;
|
||
flex-shrink: 0;
|
||
color: #909399;
|
||
}
|
||
|
||
.card-row .val {
|
||
flex: 1;
|
||
word-break: break-all;
|
||
}
|
||
|
||
.card-actions {
|
||
display: flex;
|
||
flex-wrap: wrap;
|
||
gap: 8px;
|
||
margin-top: 8px;
|
||
padding-top: 8px;
|
||
border-top: 1px solid #f0f0f0;
|
||
}
|
||
|
||
@media (max-width: 768px) {
|
||
.card-header {
|
||
flex-wrap: wrap;
|
||
gap: 10px;
|
||
}
|
||
}
|
||
</style>
|