莳松-行政管理系统
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
ss-oa-manage-web/src/views/OKR/Meeting/MeetingInfo.vue

502 lines
17 KiB

3 months ago
<template>
<div>
2 months ago
<!-- <el-affix postion="top" :offset="95" v-if="!isDetail"> -->
<div class="flex justify-between mb-4 bg-white" v-if="!isDetail">
<b class="text-20px">{{ form.meetingId ? '修改会议' : '新增会议' }}</b>
2 weeks ago
<div>
<el-button @click="submit()">保存至草稿</el-button>
<el-button type="success" @click="submit()">保存</el-button>
</div>
2 months ago
</div>
<!-- </el-affix> -->
2 months ago
<el-form
:model="form"
ref="formRef"
:rules="rules"
label-width="120px"
v-loading="loading"
:disabled="!!isDetail"
>
3 months ago
<el-row :gutter="20">
<el-col :xl="12" :lg="12" :md="12" :sm="24" :xs="24">
2 months ago
<el-form-item label="会议主题" prop="meetingSubject">
2 months ago
<el-input v-model="form.meetingSubject" placeholder="请输入会议主题" clearable />
3 months ago
</el-form-item>
</el-col>
<el-col :xl="12" :lg="12" :md="12" :sm="24" :xs="24">
2 months ago
<el-form-item label="okr节点" prop="nodeId">
<el-tree-select
v-model="form.nodeId"
:data="peroidList"
:props="defaultProps"
:render-after-expand="false"
:default-expand-all="false"
check-strictly
clearable
placeholder="选择OKR节点"
style="width: 100%"
/>
</el-form-item>
3 months ago
</el-col>
<el-col :xl="6" :lg="6" :md="12" :sm="12" :xs="24">
2 months ago
<el-form-item label="会议时间" prop="startTime">
3 months ago
<el-date-picker
2 months ago
v-model="form.startTime"
3 months ago
type="datetime"
format="YYYY-MM-DD HH:mm"
value-format="YYYY-MM-DD HH:mm"
placeholder="请选择会议开始时间"
/>
</el-form-item>
</el-col>
<el-col :xl="6" :lg="6" :md="12" :sm="12" :xs="24">
2 months ago
<el-form-item label="预计结束时间" prop="expectEndTime">
3 months ago
<el-date-picker
2 months ago
v-model="form.expectEndTime"
3 months ago
type="datetime"
format="YYYY-MM-DD HH:mm"
value-format="YYYY-MM-DD HH:mm"
placeholder="请选择预计结束时间"
/>
</el-form-item>
</el-col>
<el-col :xl="6" :lg="6" :md="12" :sm="12" :xs="24">
2 months ago
<el-form-item label="会议地点" prop="meetingRoom">
2 months ago
<el-input v-model="form.meetingRoom" placeholder="请输入会议地点" clearable />
3 months ago
</el-form-item>
</el-col>
<el-col :xl="6" :lg="6" :md="12" :sm="12" :xs="24">
<el-form-item label="会议状态" prop="status">
<el-select v-model="form.status" placeholder="请选择会议状态" style="width: 100%">
2 months ago
<el-option label="未开始" value="1" />
<el-option label="已结束" value="2" />
2 months ago
<el-option label="已取消" value="3" disabled />
3 months ago
</el-select>
</el-form-item>
</el-col>
<el-col :span="24" :offset="0">
<el-form-item label="预约参会人员" prop="expectUsers">
<el-select
v-model="form.expectUsers"
placeholder="选择参会人员"
filterable
style="width: 100%"
multiple
@change="handleUserChange"
>
<el-option
v-for="item in userOptions"
2 months ago
:key="item.id"
:label="item.name"
:value="item.id"
2 weeks ago
:disabled="item.status == 1 || item.id == userStore.getUser.id"
3 months ago
/>
</el-select>
</el-form-item>
</el-col>
2 months ago
<el-col :span="24" :offset="0" v-if="form.meetingId">
3 months ago
<el-form-item label="实际参会人员" prop="actualUsers">
<el-checkbox-group v-model="form.actualUsers">
<el-checkbox
v-for="item in expectUserOptions"
2 months ago
:key="item.id"
:label="item.name"
:value="item.id"
3 months ago
>
2 months ago
{{ item.name }}
3 months ago
</el-checkbox>
</el-checkbox-group>
</el-form-item>
</el-col>
<el-col :span="24" :offset="0" v-if="!isAllActived">
2 months ago
<el-form-item label="缺席原因" prop="absentReason">
<el-input v-model="form.absentReason" placeholder="请输入缺席原因" />
3 months ago
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="20">
2 months ago
<el-col :xl="16" :lg="16" :md="24" :sm="24" :xs="24">
2 weeks ago
<el-tabs v-model="currentUserId" tab-position="top">
<el-tab-pane
v-for="item in form.contentList"
:key="item.id"
:label="item.name"
:name="item.id"
@click="userTabChange(item)"
/>
</el-tabs>
<el-tabs v-model="currentContentId" tab-position="left" addable @edit="handleTabsEdit">
<el-tab-pane
v-for="(item, index) in form.contentList.find((it) => it.id == currentUserId)
?.contentArr"
:key="item.id"
:label="item.title"
:name="item.id"
:closable="index > 0"
>
<div v-if="!!isDetail" v-dompurify-html="item.content" class="w-full"></div>
<Editor v-else v-model="item.content" height="500px" style="width: 100%" />
</el-tab-pane>
</el-tabs>
<!-- <el-form-item label="会议内容" prop="meetingContent">
2 months ago
<div v-if="!!isDetail" v-dompurify-html="form.meetingContent" class="w-full"></div>
2 months ago
<Editor v-else v-model="form.meetingContent" height="500px" style="width: 100%" />
2 weeks ago
</el-form-item> -->
3 months ago
</el-col>
2 months ago
<el-col :xl="8" :lg="8" :md="24" :sm="24" :xs="24" v-if="!!form.meetingId">
2 weeks ago
<div v-if="!!isDetail" v-dompurify-html="form.meetingSummary" class="w-full"></div>
<div v-else>
<el-tabs v-model="summaryIdx" addable @edit="meetingSummaryEdit">
<el-tab-pane
v-for="(item, index) in form.meetingList"
:key="index"
:label="`会议纪要${index || ''}`"
:name="index"
:closable="index > 0"
>
<Editor
v-model="item.meetingSummary"
:toolbarConfig="toolbarConfig"
height="300px"
placeholder="请输入会议纪要"
style="width: 100%"
/>
<div class="mt-10px">
<el-form-item label="是否创建待办" label-width="auto">
<el-radio-group v-model="item.createWait">
<el-radio :label="true" :value="true"> 创建待办 </el-radio>
<el-radio :label="false" :value="false"> 不创建待办 </el-radio>
</el-radio-group>
</el-form-item>
</div>
<div class="flex items-center" v-if="item.createWait">
<el-select
class="flex-1"
v-model="item.userIdList"
placeholder="选择执行人"
clearable
filterable
multiple
>
<el-option
v-for="it in userOptions"
:key="it.id"
:label="it.name"
:value="it.id"
:disabled="it.status == 1"
/>
</el-select>
<el-date-picker
class="flex-1 ml-10px"
v-model="item.endDate"
type="date"
format="YYYY-MM-DD"
value-format="YYYY-MM-DD"
placeholder="选择截止时间"
/>
</div>
</el-tab-pane>
</el-tabs>
<el-divider />
<div class="text-black text-12px">
如果选择创建待办请选择执行人及截止日期默认每天9:00循环提醒可于待办中修改
</div>
<div class="mt-10px">
<el-form-item label="会议纪要发送至群聊:" label-width="auto">
<el-input v-model="form.groupName" placeholder="请输入群聊名称" />
</el-form-item>
</div>
</div>
<!-- <el-form-item label="会议纪要" prop="meetingSummary" label-width="80px">
<div v-if="!!isDetail" v-dompurify-html="form.meetingSummary" class="w-full"></div>
<Editor
2 months ago
v-else
2 months ago
v-model="form.meetingSummary"
2 weeks ago
:toolbarConfig="toolbarConfig"
height="300px"
style="width: 100%"
2 months ago
/>
2 weeks ago
</el-form-item> -->
3 months ago
</el-col>
</el-row>
</el-form>
</div>
</template>
<script setup name="MeetingInfo">
3 months ago
import { listToTree } from '@/utils/tree'
import { getAllNodeTree } from '@/api/okr/okr'
2 months ago
import * as MeetingApi from '@/api/okr/meeting'
import { formatDate } from '@/utils/formatTime'
import { getEmployeeSimpleList } from '@/api/pers/employee'
import { useTagsViewStore } from '@/store/modules/tagsView'
2 weeks ago
import { useUserStore } from '@/store/modules/user'
3 months ago
3 months ago
const route = useRoute()
2 months ago
const message = useMessage()
const tagsViewStore = useTagsViewStore()
2 weeks ago
const userStore = useUserStore()
2 months ago
3 months ago
const defaultProps = {
value: 'nodeId',
label: 'nodeName',
children: 'children'
}
const isDetail = route.query.isDetail
2 weeks ago
const currentUserId = ref(undefined)
const currentContentId = ref('') // 默认选中第一个标签页
const summaryIdx = ref(0) // 会议纪要的索引
const toolbarConfig = {
toolbarKeys: []
}
3 months ago
2 months ago
onMounted(async () => {
await getOptions()
3 months ago
if (route.params.id && route.params.id != 0) {
// 这里可以调用API获取会议详情数据
2 months ago
getMeetingInfo(route.params.id)
3 months ago
} else {
2 weeks ago
form.value.contentList = [
{
id: userStore.getUser.id + '',
name: userStore.getUser.nickname,
contentArr: [
{
id: crypto.randomUUID(),
title: '主要内容',
content: ''
}
]
}
]
currentContentId.value = form.value.contentList[0].contentArr[0].id
3 months ago
}
})
3 months ago
const peroidList = ref([])
function getOptions() {
2 months ago
return Promise.all([getAllNodeTree(), getEmployeeSimpleList()])
.then(([okrResp, employeeResp]) => {
peroidList.value = listToTree(okrResp?.tree || [], {
id: 'nodeId',
pid: 'parentId',
children: 'children'
})
userOptions.value = employeeResp.map((it) => ({ ...it, id: it.id + '' }))
2 weeks ago
form.value.expectUsers = [userStore.getUser.id + ''] // 默认添加当前用户为预约参会人员
currentUserId.value = userStore.getUser.id + '' // 默认选中当前用户
2 months ago
// handleUserChange()
})
.catch((error) => {
console.error('获取数据失败:', error)
3 months ago
})
2 months ago
// // 获取OKR节点数据
// getAllNodeTree().then((resp) => {
// peroidList.value = listToTree(resp?.tree || [], {
// id: 'nodeId',
// pid: 'parentId',
// children: 'children'
// })
// })
// // 获取人员数据
// getEmployeeSimpleList().then((data) => {
// userOptions.value = data.map((it) => ({ ...it, id: it.id + '' }))
// })
3 months ago
}
3 months ago
const form = ref({
2 months ago
meetingId: undefined,
meetingSubject: '',
startTime: '',
meetingRoom: '',
expectEndTime: '',
3 months ago
expectUsers: [],
actualUsers: [],
okrNodeName: '',
2 months ago
status: '1',
meetingContent: '',
meetingSummary: '',
2 weeks ago
absentReason: '',
contentList: [],
meetingList: []
3 months ago
})
const rules = {
2 months ago
meetingSubject: [{ required: true, message: '请输入会议主题', trigger: 'blur' }],
startTime: [{ required: true, message: '请选择会议开始时间', trigger: 'change' }],
expectEndTime: [{ required: true, message: '请选择预计结束时间', trigger: 'change' }],
meetingRoom: [{ required: true, message: '请输入会议地点', trigger: 'blur' }],
expectUsers: [{ required: true, message: '请选择参会人员', trigger: 'change' }]
3 months ago
}
const formRef = ref(null)
2 months ago
const userOptions = ref([])
3 months ago
const expectUserOptions = ref([])
const isAllActived = computed(() => {
// 判断实际参会人员是否包含所有预约参会人员
return form.value.expectUsers.every((item) => form.value.actualUsers.includes(item))
})
2 months ago
const loading = ref(false)
3 months ago
// 获取详情
2 months ago
const getMeetingInfo = async (meetingId) => {
try {
loading.value = true
// 调用API获取会议详情
const resp = await MeetingApi.getMeetingDetail({ meetingId })
loading.value = false
if (resp) {
form.value = {
...form.value,
...resp,
startTime: formatDate(resp.startTime, 'YYYY-MM-DD HH:mm'),
expectEndTime: formatDate(resp.expectEndTime, 'YYYY-MM-DD HH:mm'),
expectUsers: resp.expectUsers || [],
2 weeks ago
actualUsers: resp.actualUsers || [],
meetingList: resp.meetingList || [
{
meetingSummary: resp.meetingSummary || '',
userIdList: [],
endDate: '',
createWait: true
}
]
2 months ago
}
2 weeks ago
handleUserChange(resp.expectUsers)
3 months ago
}
2 months ago
} catch (error) {
loading.value = false
console.error('获取会议详情失败:', error)
}
3 months ago
}
2 weeks ago
function userTabChange(val) {
currentContentId.value = val.contentArr[0].id
}
function handleTabsEdit(targetName, action) {
if (action === 'add') {
form.value.contentList.forEach((item) => {
if (item.id === currentUserId.value) {
item.contentArr.push({
id: crypto.randomUUID(),
title: '次要内容',
content: ''
})
}
})
} else if (action === 'remove') {
form.value.contentList.forEach((item) => {
if (item.id === currentUserId.value) {
const idx = item.contentArr.findIndex((item) => item.id == targetName)
item.contentArr.splice(idx, 1)
// 如果删除的是当前选中的标签页,则切换到下一个标签页
if (currentContentId.value === targetName) {
currentContentId.value = item.contentArr[idx - 1]?.id || item.contentArr[idx + 1]?.id
}
}
})
const tabs = editableTabs.value
let activeName = editableTabsValue.value
if (activeName === targetName) {
tabs.forEach((tab, index) => {
if (tab.name === targetName) {
const nextTab = tabs[index + 1] || tabs[index - 1]
if (nextTab) {
activeName = nextTab.name
}
}
})
}
editableTabsValue.value = activeName
editableTabs.value = tabs.filter((tab) => tab.name !== targetName)
}
}
function meetingSummaryEdit(targetName, action) {
if (action === 'add') {
form.value.meetingList.push({
meetingSummary: '',
userIdList: [],
endDate: '',
createWait: true
})
} else if (action === 'remove') {
form.value.meetingList.splice(targetName, 1)
summaryIdx.value = 0
}
}
function handleUserChange(val) {
3 months ago
// 当预约参会人员变化时,更新实际参会人员选项
expectUserOptions.value = userOptions.value.filter((user) =>
2 months ago
form.value.expectUsers.some((it) => it == user.id)
3 months ago
)
2 months ago
if (!isDetail) {
form.value.actualUsers = [...form.value.expectUsers]
}
2 weeks ago
// 先过滤掉不存在的参会人员
form.value.contentList = form.value.contentList.filter((item) => {
return val.some((it) => it == item.id)
})
// 再补充新增的
val.map((item) => {
if (!form.value.contentList.some((it) => it.id == item)) {
form.value.contentList.push({
id: item,
name: userOptions.value.find((it) => it.id == item).name,
contentArr: [
{
id: crypto.randomUUID(),
title: '主要内容',
content: ''
}
]
})
}
})
3 months ago
}
2 months ago
const router = useRouter()
async function submit() {
// 校验表单
if (!formRef.value) return
const valid = await formRef.value.validate()
if (!valid) return
try {
// 提交表单数据
if (form.value.meetingId) {
if (form.value.status == 2 && !form.value.meetingSummary) {
message.error('会议结束时,会议纪要不能为空')
return
}
// 更新会议
await MeetingApi.updateMeeting(form.value)
message.success('会议更新成功')
3 months ago
} else {
2 months ago
if (form.value.status == 1 && !form.value.meetingContent) {
message.error('预约会议时,会议内容不能为空')
return
}
2 months ago
form.value.actualUsers = []
2 months ago
// 新增会议
await MeetingApi.createMeeting(form.value)
message.success('会议创建成功')
3 months ago
}
2 months ago
tagsViewStore.delView(route)
const visitedViews = tagsViewStore.getVisitedViews
const latestView = visitedViews.slice(-1)[0]
router.push(latestView)
} catch (error) {
console.error('保存会议数据失败:', error)
}
3 months ago
}
</script>
<style lang="scss" scoped></style>