feat(review-meeting): 会议列表操作下拉化并支持弹窗预览纪要
parent
506c26f7ab
commit
0b140a145e
|
|
@ -91,84 +91,105 @@
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
<el-table-column
|
<el-table-column
|
||||||
label="操作"
|
label="操作"
|
||||||
width="600"
|
width="180"
|
||||||
align="center"
|
align="center"
|
||||||
:fixed="useFixedActionColumn ? 'right' : false"
|
:fixed="useFixedActionColumn ? 'right' : false"
|
||||||
>
|
>
|
||||||
<template #default="{ row }">
|
<template #default="{ row }">
|
||||||
<template v-if="row.status === 0">
|
<template v-if="row.status === 0">
|
||||||
<div class="op-group">
|
<div class="op-group compact">
|
||||||
<a v-hasPermi="['review:meeting:update']" class="op-link" @click="goToEdit(row.id)"
|
<el-dropdown
|
||||||
>编辑</a
|
trigger="click"
|
||||||
>
|
@command="(command: string) => handleActionCommand(row, command)"
|
||||||
<a
|
|
||||||
v-hasPermi="['review:meeting:send-sms']"
|
|
||||||
class="op-link"
|
|
||||||
@click="handleSendSms(row)"
|
|
||||||
>发送短信邀约</a
|
|
||||||
>
|
|
||||||
<a
|
|
||||||
v-hasPermi="['review:meeting:cancel']"
|
|
||||||
class="op-link op-danger"
|
|
||||||
@click="handleCancel(row)"
|
|
||||||
>取消</a
|
|
||||||
>
|
>
|
||||||
|
<span class="op-dropdown-trigger">更多</span>
|
||||||
|
<template #dropdown>
|
||||||
|
<el-dropdown-menu>
|
||||||
|
<el-dropdown-item v-hasPermi="['review:meeting:update']" command="edit">
|
||||||
|
编辑
|
||||||
|
</el-dropdown-item>
|
||||||
|
<el-dropdown-item v-hasPermi="['review:meeting:send-sms']" command="sendSms">
|
||||||
|
发送短信邀约
|
||||||
|
</el-dropdown-item>
|
||||||
|
<el-dropdown-item
|
||||||
|
v-hasPermi="['review:meeting:cancel']"
|
||||||
|
command="cancel"
|
||||||
|
class="danger-dropdown-item"
|
||||||
|
>
|
||||||
|
取消
|
||||||
|
</el-dropdown-item>
|
||||||
|
</el-dropdown-menu>
|
||||||
|
</template>
|
||||||
|
</el-dropdown>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<template v-else-if="row.status === 1">
|
<template v-else-if="row.status === 1">
|
||||||
<div class="op-group">
|
<div class="op-group compact">
|
||||||
<a class="op-link" @click="goToEdit(row.id, 'view')">查看</a>
|
<el-dropdown
|
||||||
<a
|
trigger="click"
|
||||||
v-hasPermi="['review:meeting:send-sms']"
|
@command="(command: string) => handleActionCommand(row, command)"
|
||||||
class="op-link"
|
|
||||||
@click="openSmsStatus(row)"
|
|
||||||
>短信状态</a
|
|
||||||
>
|
|
||||||
<a
|
|
||||||
v-if="row.mailSent"
|
|
||||||
v-hasPermi="['review:meeting:send-mail']"
|
|
||||||
class="op-link"
|
|
||||||
@click="openMailStatus(row)"
|
|
||||||
>邮件状态</a
|
|
||||||
>
|
|
||||||
<a
|
|
||||||
v-else
|
|
||||||
v-hasPermi="['review:meeting:send-mail']"
|
|
||||||
class="op-link"
|
|
||||||
@click="handleSendMail(row)"
|
|
||||||
>发送议程</a
|
|
||||||
>
|
|
||||||
<a
|
|
||||||
v-hasPermi="['review:meeting:update']"
|
|
||||||
class="op-link"
|
|
||||||
@click="triggerUploadMinutes(row)"
|
|
||||||
>上传纪要</a
|
|
||||||
>
|
|
||||||
<a
|
|
||||||
v-hasPermi="['review:meeting:export']"
|
|
||||||
class="op-link"
|
|
||||||
:class="{ disabled: isExporting(row.id) }"
|
|
||||||
@click="handleExportMaterials(row)"
|
|
||||||
>
|
|
||||||
{{ getExportText(row.id) }}
|
|
||||||
</a>
|
|
||||||
<a v-hasPermi="['review:meeting:finish']" class="op-link" @click="handleFinish(row)"
|
|
||||||
>结束</a
|
|
||||||
>
|
|
||||||
<a
|
|
||||||
v-hasPermi="['review:meeting:cancel']"
|
|
||||||
class="op-link op-danger"
|
|
||||||
@click="handleCancel(row)"
|
|
||||||
>取消</a
|
|
||||||
>
|
>
|
||||||
|
<span class="op-dropdown-trigger">更多</span>
|
||||||
|
<template #dropdown>
|
||||||
|
<el-dropdown-menu>
|
||||||
|
<el-dropdown-item command="view">查看</el-dropdown-item>
|
||||||
|
<el-dropdown-item v-hasPermi="['review:meeting:send-sms']" command="smsStatus">
|
||||||
|
短信状态
|
||||||
|
</el-dropdown-item>
|
||||||
|
<el-dropdown-item
|
||||||
|
v-hasPermi="['review:meeting:send-mail']"
|
||||||
|
:command="row.mailSent ? 'mailStatus' : 'sendMail'"
|
||||||
|
>
|
||||||
|
{{ row.mailSent ? '邮件状态' : '发送议程' }}
|
||||||
|
</el-dropdown-item>
|
||||||
|
<el-dropdown-item v-if="hasMinutesAttachment(row)" command="previewMinutes">
|
||||||
|
预览纪要
|
||||||
|
</el-dropdown-item>
|
||||||
|
<el-dropdown-item v-hasPermi="['review:meeting:update']" command="uploadMinutes">
|
||||||
|
{{ hasMinutesAttachment(row) ? '重新上传纪要' : '上传纪要' }}
|
||||||
|
</el-dropdown-item>
|
||||||
|
<el-dropdown-item
|
||||||
|
v-hasPermi="['review:meeting:export']"
|
||||||
|
command="export"
|
||||||
|
:disabled="isExporting(row.id)"
|
||||||
|
>
|
||||||
|
{{ getExportText(row.id) }}
|
||||||
|
</el-dropdown-item>
|
||||||
|
<el-dropdown-item
|
||||||
|
v-hasPermi="['review:meeting:finish']"
|
||||||
|
command="finish"
|
||||||
|
class="danger-dropdown-item"
|
||||||
|
>
|
||||||
|
结束
|
||||||
|
</el-dropdown-item>
|
||||||
|
<el-dropdown-item
|
||||||
|
v-hasPermi="['review:meeting:cancel']"
|
||||||
|
command="cancel"
|
||||||
|
class="danger-dropdown-item"
|
||||||
|
>
|
||||||
|
取消
|
||||||
|
</el-dropdown-item>
|
||||||
|
</el-dropdown-menu>
|
||||||
|
</template>
|
||||||
|
</el-dropdown>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<template v-else>
|
<template v-else>
|
||||||
<div class="op-group">
|
<div class="op-group compact">
|
||||||
<a class="op-link" @click="goToEdit(row.id, 'view')">查看</a>
|
<el-dropdown
|
||||||
<a v-hasPermi="['review:meeting:create']" class="op-link" @click="handleCopy(row)"
|
trigger="click"
|
||||||
>复制新会议</a
|
@command="(command: string) => handleActionCommand(row, command)"
|
||||||
>
|
>
|
||||||
|
<span class="op-dropdown-trigger">更多</span>
|
||||||
|
<template #dropdown>
|
||||||
|
<el-dropdown-menu>
|
||||||
|
<el-dropdown-item command="view">查看</el-dropdown-item>
|
||||||
|
<el-dropdown-item v-hasPermi="['review:meeting:create']" command="copy">
|
||||||
|
复制新会议
|
||||||
|
</el-dropdown-item>
|
||||||
|
</el-dropdown-menu>
|
||||||
|
</template>
|
||||||
|
</el-dropdown>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
</template>
|
</template>
|
||||||
|
|
@ -189,6 +210,13 @@
|
||||||
<!-- 邮件状态弹窗 -->
|
<!-- 邮件状态弹窗 -->
|
||||||
<MailStatusDialog ref="mailStatusRef" />
|
<MailStatusDialog ref="mailStatusRef" />
|
||||||
|
|
||||||
|
<FilePreviewDialog
|
||||||
|
v-model="minutesPreviewVisible"
|
||||||
|
:meeting-id="minutesPreviewMeetingId"
|
||||||
|
attachment-type="minutes"
|
||||||
|
:file-name="minutesPreviewFileName"
|
||||||
|
/>
|
||||||
|
|
||||||
<input
|
<input
|
||||||
ref="minutesUploadInputRef"
|
ref="minutesUploadInputRef"
|
||||||
type="file"
|
type="file"
|
||||||
|
|
@ -221,6 +249,7 @@ import {
|
||||||
} from '@/api/review/meeting'
|
} from '@/api/review/meeting'
|
||||||
import SmsStatusDialog from './SmsStatusDialog.vue'
|
import SmsStatusDialog from './SmsStatusDialog.vue'
|
||||||
import MailStatusDialog from './MailStatusDialog.vue'
|
import MailStatusDialog from './MailStatusDialog.vue'
|
||||||
|
import FilePreviewDialog from './components/FilePreviewDialog.vue'
|
||||||
import { formatDate } from '@/utils/formatTime'
|
import { formatDate } from '@/utils/formatTime'
|
||||||
import download from '@/utils/download'
|
import download from '@/utils/download'
|
||||||
|
|
||||||
|
|
@ -259,6 +288,9 @@ const smsStatusRef = ref()
|
||||||
const mailStatusRef = ref()
|
const mailStatusRef = ref()
|
||||||
const minutesUploadInputRef = ref<HTMLInputElement>()
|
const minutesUploadInputRef = ref<HTMLInputElement>()
|
||||||
const pendingMinutesMeeting = ref<ReviewMeetingRespVO>()
|
const pendingMinutesMeeting = ref<ReviewMeetingRespVO>()
|
||||||
|
const minutesPreviewVisible = ref(false)
|
||||||
|
const minutesPreviewMeetingId = ref(0)
|
||||||
|
const minutesPreviewFileName = ref('')
|
||||||
const exportTaskMap = reactive<Record<number, ReviewMeetingMaterialExportTaskRespVO | undefined>>(
|
const exportTaskMap = reactive<Record<number, ReviewMeetingMaterialExportTaskRespVO | undefined>>(
|
||||||
{}
|
{}
|
||||||
)
|
)
|
||||||
|
|
@ -356,11 +388,80 @@ const handleCopy = async (row: ReviewMeetingRespVO) => {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type MeetingActionCommand =
|
||||||
|
| 'edit'
|
||||||
|
| 'view'
|
||||||
|
| 'sendSms'
|
||||||
|
| 'smsStatus'
|
||||||
|
| 'mailStatus'
|
||||||
|
| 'sendMail'
|
||||||
|
| 'previewMinutes'
|
||||||
|
| 'uploadMinutes'
|
||||||
|
| 'export'
|
||||||
|
| 'finish'
|
||||||
|
| 'cancel'
|
||||||
|
| 'copy'
|
||||||
|
|
||||||
|
const handleActionCommand = async (row: ReviewMeetingRespVO, command: string) => {
|
||||||
|
const action = command as MeetingActionCommand
|
||||||
|
switch (action) {
|
||||||
|
case 'edit':
|
||||||
|
goToEdit(row.id)
|
||||||
|
break
|
||||||
|
case 'view':
|
||||||
|
goToEdit(row.id, 'view')
|
||||||
|
break
|
||||||
|
case 'sendSms':
|
||||||
|
await handleSendSms(row)
|
||||||
|
break
|
||||||
|
case 'smsStatus':
|
||||||
|
openSmsStatus(row)
|
||||||
|
break
|
||||||
|
case 'mailStatus':
|
||||||
|
openMailStatus(row)
|
||||||
|
break
|
||||||
|
case 'sendMail':
|
||||||
|
await handleSendMail(row)
|
||||||
|
break
|
||||||
|
case 'previewMinutes':
|
||||||
|
handlePreviewMinutes(row)
|
||||||
|
break
|
||||||
|
case 'uploadMinutes':
|
||||||
|
triggerUploadMinutes(row)
|
||||||
|
break
|
||||||
|
case 'export':
|
||||||
|
await handleExportMaterials(row)
|
||||||
|
break
|
||||||
|
case 'finish':
|
||||||
|
await handleFinish(row)
|
||||||
|
break
|
||||||
|
case 'cancel':
|
||||||
|
await handleCancel(row)
|
||||||
|
break
|
||||||
|
case 'copy':
|
||||||
|
await handleCopy(row)
|
||||||
|
break
|
||||||
|
default:
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const triggerUploadMinutes = (row: ReviewMeetingRespVO) => {
|
const triggerUploadMinutes = (row: ReviewMeetingRespVO) => {
|
||||||
pendingMinutesMeeting.value = row
|
pendingMinutesMeeting.value = row
|
||||||
minutesUploadInputRef.value?.click()
|
minutesUploadInputRef.value?.click()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const hasMinutesAttachment = (row: ReviewMeetingRespVO) => !!row.minutesAttachmentUrl
|
||||||
|
|
||||||
|
const handlePreviewMinutes = (row: ReviewMeetingRespVO) => {
|
||||||
|
if (!hasMinutesAttachment(row)) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
minutesPreviewMeetingId.value = row.id
|
||||||
|
minutesPreviewFileName.value = row.minutesAttachmentName || ''
|
||||||
|
minutesPreviewVisible.value = true
|
||||||
|
}
|
||||||
|
|
||||||
const handleMinutesFileChange = async (event: Event) => {
|
const handleMinutesFileChange = async (event: Event) => {
|
||||||
const input = event.target as HTMLInputElement
|
const input = event.target as HTMLInputElement
|
||||||
const file = input.files?.[0]
|
const file = input.files?.[0]
|
||||||
|
|
@ -377,9 +478,14 @@ const handleMinutesFileChange = async (event: Event) => {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
await uploadMinutesAttachment(pendingMinutesMeeting.value.id, file)
|
const meeting = pendingMinutesMeeting.value
|
||||||
|
const attachment = await uploadMinutesAttachment(meeting.id, file)
|
||||||
|
meeting.minutesAttachmentName = attachment.name
|
||||||
|
meeting.minutesAttachmentUrl = attachment.url
|
||||||
|
meeting.minutesAttachmentType = attachment.type
|
||||||
|
meeting.minutesAttachmentSize = attachment.size
|
||||||
ElMessage.success('会议纪要上传成功,整改通知后台生成中')
|
ElMessage.success('会议纪要上传成功,整改通知后台生成中')
|
||||||
getList()
|
await getList()
|
||||||
} finally {
|
} finally {
|
||||||
input.value = ''
|
input.value = ''
|
||||||
pendingMinutesMeeting.value = undefined
|
pendingMinutesMeeting.value = undefined
|
||||||
|
|
@ -729,6 +835,40 @@ onBeforeUnmount(() => {
|
||||||
.op-link.disabled:hover {
|
.op-link.disabled:hover {
|
||||||
color: #8a94a6;
|
color: #8a94a6;
|
||||||
}
|
}
|
||||||
|
.op-group.compact {
|
||||||
|
gap: 0;
|
||||||
|
}
|
||||||
|
.op-dropdown-trigger {
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 5px;
|
||||||
|
color: #295abc;
|
||||||
|
font-size: 14px;
|
||||||
|
font-weight: 500;
|
||||||
|
line-height: 1;
|
||||||
|
cursor: pointer;
|
||||||
|
user-select: none;
|
||||||
|
}
|
||||||
|
.op-dropdown-trigger::after {
|
||||||
|
content: '';
|
||||||
|
width: 6px;
|
||||||
|
height: 6px;
|
||||||
|
margin-top: -3px;
|
||||||
|
border-right: 1.5px solid currentColor;
|
||||||
|
border-bottom: 1.5px solid currentColor;
|
||||||
|
transform: rotate(45deg);
|
||||||
|
}
|
||||||
|
.op-dropdown-trigger:hover {
|
||||||
|
color: #1f4ca5;
|
||||||
|
text-decoration: underline;
|
||||||
|
}
|
||||||
|
:deep(.el-dropdown-menu__item.danger-dropdown-item) {
|
||||||
|
color: #d63b42;
|
||||||
|
}
|
||||||
|
:deep(.el-dropdown-menu__item.danger-dropdown-item:not(.is-disabled):focus) {
|
||||||
|
color: #c22e35;
|
||||||
|
background-color: rgba(252, 79, 84, 0.12);
|
||||||
|
}
|
||||||
|
|
||||||
/* ── 表格样式 ── */
|
/* ── 表格样式 ── */
|
||||||
:deep(.review-table .el-table__header-wrapper th) {
|
:deep(.review-table .el-table__header-wrapper th) {
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue