|
|
@@ -0,0 +1,751 @@
|
|
|
+<template>
|
|
|
+ <div class="meeting-management-page">
|
|
|
+ <!-- 查询表单 -->
|
|
|
+ <div class="query-form-container">
|
|
|
+ <div class="query-form-basic">
|
|
|
+ <el-form class="query-form-fields" :inline="true" :model="queryForm" size="small">
|
|
|
+ <el-form-item label="会议标题">
|
|
|
+ <el-input v-model="queryForm.meetingTitle" clearable placeholder="请输入" style="width: 220px" />
|
|
|
+ </el-form-item>
|
|
|
+ <el-form-item label="会议日期">
|
|
|
+ <el-date-picker
|
|
|
+ v-model="queryForm.meetingDateRange"
|
|
|
+ end-placeholder="结束日期"
|
|
|
+ range-separator="至"
|
|
|
+ start-placeholder="开始日期"
|
|
|
+ style="width: 260px"
|
|
|
+ type="daterange"
|
|
|
+ value-format="yyyy-MM-dd" />
|
|
|
+ </el-form-item>
|
|
|
+ </el-form>
|
|
|
+ <div class="query-form-actions">
|
|
|
+ <el-button icon="el-icon-search" type="primary" @click="handleSearch">查询</el-button>
|
|
|
+ <el-button icon="el-icon-refresh-right" @click="handleReset">重置</el-button>
|
|
|
+ <el-divider direction="vertical" />
|
|
|
+ <el-button icon="el-icon-plus" type="success" @click="handleAdd">新增会议</el-button>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <!-- 主体内容 -->
|
|
|
+ <div class="main-content">
|
|
|
+ <div class="table-container">
|
|
|
+ <div class="table-scroll-wrapper">
|
|
|
+ <el-table
|
|
|
+ v-loading="loading"
|
|
|
+ border
|
|
|
+ class="meeting-table"
|
|
|
+ :data="tableData"
|
|
|
+ :fit="false"
|
|
|
+ :height="$baseTableHeight(1)"
|
|
|
+ stripe
|
|
|
+ style="width: 100%">
|
|
|
+ <el-table-column align="center" type="index" width="50" />
|
|
|
+ <el-table-column label="会议标题" min-width="200" prop="meetingTitle" show-overflow-tooltip />
|
|
|
+ <el-table-column label="会议日期" width="120">
|
|
|
+ <template slot-scope="{ row }">
|
|
|
+ {{ row.meetingDate ? parseTime(row.meetingDate, '{y}-{m}-{d}') : '-' }}
|
|
|
+ </template>
|
|
|
+ </el-table-column>
|
|
|
+ <el-table-column align="right" label="时长(h)" width="100">
|
|
|
+ <template slot-scope="{ row }">
|
|
|
+ {{ row.duration !== null && row.duration !== undefined ? row.duration.toFixed(1) : '-' }}
|
|
|
+ </template>
|
|
|
+ </el-table-column>
|
|
|
+ <el-table-column label="组织人" prop="organizerName" show-overflow-tooltip width="120" />
|
|
|
+ <el-table-column align="center" label="参会人数" width="100">
|
|
|
+ <template slot-scope="{ row }">
|
|
|
+ {{ row.attendees ? row.attendees.length : 0 }}
|
|
|
+ </template>
|
|
|
+ </el-table-column>
|
|
|
+ <el-table-column align="center" fixed="right" label="操作" width="240">
|
|
|
+ <template slot-scope="{ row }">
|
|
|
+ <el-button size="mini" type="text" @click="handleViewDetail(row)">详情</el-button>
|
|
|
+ <el-button size="mini" type="text" @click="handleEdit(row)">编辑</el-button>
|
|
|
+ <el-button size="mini" type="text" @click="handleDelete(row)">删除</el-button>
|
|
|
+ <el-button size="mini" type="text" @click="handleAddAttendees(row)">追加人员</el-button>
|
|
|
+ <el-button size="mini" type="text" @click="handleViewWorkHour(row)">查看工时</el-button>
|
|
|
+ </template>
|
|
|
+ </el-table-column>
|
|
|
+ </el-table>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <!-- 分页 -->
|
|
|
+ <div class="pagination-wrapper">
|
|
|
+ <el-pagination
|
|
|
+ background
|
|
|
+ :current-page.sync="queryForm.pageNum"
|
|
|
+ layout="total, sizes, prev, pager, next, jumper"
|
|
|
+ :page-size.sync="queryForm.pageSize"
|
|
|
+ :page-sizes="[10, 20, 50, 100]"
|
|
|
+ :total="total"
|
|
|
+ @current-change="handleCurrentChange"
|
|
|
+ @size-change="handleSizeChange" />
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <!-- 新增/编辑弹窗 -->
|
|
|
+ <el-dialog
|
|
|
+ :close-on-click-modal="false"
|
|
|
+ :title="dialogTitle"
|
|
|
+ :visible.sync="editDialogVisible"
|
|
|
+ width="680px"
|
|
|
+ @close="handleDialogClose">
|
|
|
+ <el-form ref="editForm" label-width="100px" :model="editForm" :rules="editRules" size="small">
|
|
|
+ <el-form-item label="会议标题" prop="meetingTitle">
|
|
|
+ <el-input v-model="editForm.meetingTitle" placeholder="请输入会议标题" />
|
|
|
+ </el-form-item>
|
|
|
+ <el-form-item label="会议内容" prop="meetingContent">
|
|
|
+ <el-input v-model="editForm.meetingContent" placeholder="请输入会议内容" :rows="4" type="textarea" />
|
|
|
+ </el-form-item>
|
|
|
+ <el-form-item label="组织人" prop="organizerName">
|
|
|
+ <el-select
|
|
|
+ v-model="editForm.organizerId"
|
|
|
+ filterable
|
|
|
+ :loading="userSearchLoading"
|
|
|
+ placeholder="请选择组织人"
|
|
|
+ remote
|
|
|
+ :remote-method="remoteSearchUsers"
|
|
|
+ reserve-keyword
|
|
|
+ @change="handleOrganizerChange">
|
|
|
+ <el-option v-for="user in userOptions" :key="user.value" :label="user.label" :value="user.value" />
|
|
|
+ </el-select>
|
|
|
+ </el-form-item>
|
|
|
+ <el-form-item label="会议日期" prop="meetingDate">
|
|
|
+ <el-date-picker
|
|
|
+ v-model="editForm.meetingDate"
|
|
|
+ placeholder="选择会议日期"
|
|
|
+ style="width: 100%"
|
|
|
+ type="date"
|
|
|
+ value-format="yyyy-MM-dd" />
|
|
|
+ </el-form-item>
|
|
|
+ <el-form-item label="会议时长(h)" prop="duration">
|
|
|
+ <el-input-number
|
|
|
+ v-model="editForm.duration"
|
|
|
+ :min="0.5"
|
|
|
+ placeholder="请输入会议时长"
|
|
|
+ :precision="1"
|
|
|
+ :step="0.5"
|
|
|
+ style="width: 100%" />
|
|
|
+ </el-form-item>
|
|
|
+ <el-form-item label="备注" prop="remark">
|
|
|
+ <el-input v-model="editForm.remark" placeholder="请输入备注" :rows="3" type="textarea" />
|
|
|
+ </el-form-item>
|
|
|
+ <el-form-item label="参会人员">
|
|
|
+ <div class="quick-select-btns">
|
|
|
+ <el-button size="mini" @click="quickSelectAll('edit')">全员</el-button>
|
|
|
+ <el-button size="mini" @click="quickSelectByRole('DevelopmentEngineer', 'edit')">研发</el-button>
|
|
|
+ <el-button size="mini" @click="quickSelectByRole('ProjectDeliveryManager', 'edit')">交付</el-button>
|
|
|
+ <el-button size="mini" @click="quickSelectByRole('TestEngineer', 'edit')">质量</el-button>
|
|
|
+ </div>
|
|
|
+ <el-select
|
|
|
+ v-model="editForm.selectedUserIds"
|
|
|
+ class="user-select"
|
|
|
+ collapse-tags
|
|
|
+ filterable
|
|
|
+ :loading="userSearchLoading"
|
|
|
+ multiple
|
|
|
+ placeholder="请选择参会人员"
|
|
|
+ remote
|
|
|
+ :remote-method="remoteSearchUsers"
|
|
|
+ reserve-keyword
|
|
|
+ @change="handleSelectedUsersChange">
|
|
|
+ <el-option v-for="user in userOptions" :key="user.value" :label="user.label" :value="user.value" />
|
|
|
+ </el-select>
|
|
|
+ </el-form-item>
|
|
|
+ </el-form>
|
|
|
+ <span slot="footer" class="dialog-footer">
|
|
|
+ <el-button size="small" @click="editDialogVisible = false">取消</el-button>
|
|
|
+ <el-button :loading="submitLoading" size="small" type="primary" @click="handleSubmit">确定</el-button>
|
|
|
+ </span>
|
|
|
+ </el-dialog>
|
|
|
+
|
|
|
+ <!-- 追加参会人员弹窗 -->
|
|
|
+ <el-dialog
|
|
|
+ :close-on-click-modal="false"
|
|
|
+ title="追加参会人员"
|
|
|
+ :visible.sync="addAttendeesDialogVisible"
|
|
|
+ width="500px"
|
|
|
+ @close="handleAttendeesDialogClose">
|
|
|
+ <el-form ref="attendeesForm" label-width="0" :model="attendeesForm" size="small">
|
|
|
+ <el-form-item>
|
|
|
+ <div class="attendees-meeting-info">会议:{{ currentRow ? currentRow.meetingTitle : '' }}</div>
|
|
|
+ </el-form-item>
|
|
|
+ <el-form-item>
|
|
|
+ <div class="quick-select-btns">
|
|
|
+ <el-button size="mini" @click="quickSelectAll('attendee')">全员</el-button>
|
|
|
+ <el-button size="mini" @click="quickSelectByRole('DevelopmentEngineer', 'attendee')">研发</el-button>
|
|
|
+ <el-button size="mini" @click="quickSelectByRole('ProjectDeliveryManager', 'attendee')">交付</el-button>
|
|
|
+ <el-button size="mini" @click="quickSelectByRole('TestEngineer', 'attendee')">质量</el-button>
|
|
|
+ </div>
|
|
|
+ <el-select
|
|
|
+ v-model="attendeesForm.selectedUserIds"
|
|
|
+ collapse-tags
|
|
|
+ filterable
|
|
|
+ :loading="userSearchLoading"
|
|
|
+ multiple
|
|
|
+ placeholder="请选择要追加的参会人员"
|
|
|
+ remote
|
|
|
+ :remote-method="remoteSearchUsers"
|
|
|
+ reserve-keyword
|
|
|
+ style="width: 100%">
|
|
|
+ <el-option v-for="user in userOptions" :key="user.value" :label="user.label" :value="user.value" />
|
|
|
+ </el-select>
|
|
|
+ </el-form-item>
|
|
|
+ </el-form>
|
|
|
+ <span slot="footer" class="dialog-footer">
|
|
|
+ <el-button size="small" @click="addAttendeesDialogVisible = false">取消</el-button>
|
|
|
+ <el-button :loading="attendeeSubmitLoading" size="small" type="primary" @click="handleAttendeesSubmit">
|
|
|
+ 确定
|
|
|
+ </el-button>
|
|
|
+ </span>
|
|
|
+ </el-dialog>
|
|
|
+
|
|
|
+ <!-- 查看工时弹窗 -->
|
|
|
+ <el-dialog :close-on-click-modal="false" title="工时记录" :visible.sync="workHourDialogVisible" width="700px">
|
|
|
+ <el-table v-loading="workHourLoading" border :data="workHourList" stripe>
|
|
|
+ <el-table-column align="center" type="index" width="50" />
|
|
|
+ <el-table-column label="姓名" prop="userName" show-overflow-tooltip width="120" />
|
|
|
+ <el-table-column label="工时日期" width="160">
|
|
|
+ <template slot-scope="{ row }">
|
|
|
+ {{ row.workDate ? parseTime(row.workDate, '{y}-{m}-{d}') : '-' }}
|
|
|
+ </template>
|
|
|
+ </el-table-column>
|
|
|
+ <el-table-column align="right" label="工时(h)" prop="workHours" width="120">
|
|
|
+ <template slot-scope="{ row }">
|
|
|
+ {{ row.workHours !== null && row.workHours !== undefined ? row.workHours.toFixed(1) : '-' }}
|
|
|
+ </template>
|
|
|
+ </el-table-column>
|
|
|
+ <el-table-column label="备注" prop="remark" show-overflow-tooltip />
|
|
|
+ </el-table>
|
|
|
+ <span slot="footer" class="dialog-footer">
|
|
|
+ <el-button size="small" @click="workHourDialogVisible = false">关闭</el-button>
|
|
|
+ </span>
|
|
|
+ </el-dialog>
|
|
|
+ </div>
|
|
|
+</template>
|
|
|
+
|
|
|
+<script>
|
|
|
+ import meetingApi from '@/api/devops/meeting'
|
|
|
+ import micro_request from '@/utils/micro_request'
|
|
|
+ import { parseTime } from '@/utils'
|
|
|
+ import { DEVOPS_DEV_DEPT_ID } from '@/config/devops.config'
|
|
|
+
|
|
|
+ export default {
|
|
|
+ name: 'MeetingManagement',
|
|
|
+ data() {
|
|
|
+ return {
|
|
|
+ queryForm: {
|
|
|
+ pageNum: 1,
|
|
|
+ pageSize: 20,
|
|
|
+ meetingTitle: '',
|
|
|
+ meetingDateRange: [],
|
|
|
+ },
|
|
|
+ tableData: [],
|
|
|
+ total: 0,
|
|
|
+ loading: false,
|
|
|
+ // 新增/编辑弹窗
|
|
|
+ editDialogVisible: false,
|
|
|
+ dialogTitle: '新增会议',
|
|
|
+ isEdit: false,
|
|
|
+ submitLoading: false,
|
|
|
+ editForm: {
|
|
|
+ id: null,
|
|
|
+ meetingTitle: '',
|
|
|
+ meetingContent: '',
|
|
|
+ organizerId: null,
|
|
|
+ organizerName: '',
|
|
|
+ meetingDate: '',
|
|
|
+ duration: null,
|
|
|
+ remark: '',
|
|
|
+ selectedUserIds: [],
|
|
|
+ selectedUserNames: [],
|
|
|
+ },
|
|
|
+ editRules: {
|
|
|
+ meetingTitle: [{ required: true, message: '请输入会议标题', trigger: 'blur' }],
|
|
|
+ organizerName: [{ required: true, message: '请选择组织人', trigger: 'change' }],
|
|
|
+ meetingDate: [{ required: true, message: '请选择会议日期', trigger: 'change' }],
|
|
|
+ duration: [{ required: true, message: '请输入会议时长', trigger: 'blur' }],
|
|
|
+ },
|
|
|
+ // 追加参会人员弹窗
|
|
|
+ addAttendeesDialogVisible: false,
|
|
|
+ attendeeSubmitLoading: false,
|
|
|
+ attendeesForm: {
|
|
|
+ selectedUserIds: [],
|
|
|
+ },
|
|
|
+ // 查看工时弹窗
|
|
|
+ workHourDialogVisible: false,
|
|
|
+ workHourList: [],
|
|
|
+ workHourLoading: false,
|
|
|
+ // 用户搜索
|
|
|
+ userOptions: [],
|
|
|
+ userSearchLoading: false,
|
|
|
+ userSearchCache: {},
|
|
|
+ // 当前操作行
|
|
|
+ currentRow: null,
|
|
|
+ }
|
|
|
+ },
|
|
|
+ created() {
|
|
|
+ this.fetchData()
|
|
|
+ },
|
|
|
+ methods: {
|
|
|
+ parseTime,
|
|
|
+ async fetchData() {
|
|
|
+ this.loading = true
|
|
|
+ try {
|
|
|
+ const params = {
|
|
|
+ pageNum: this.queryForm.pageNum,
|
|
|
+ pageSize: this.queryForm.pageSize,
|
|
|
+ }
|
|
|
+ if (this.queryForm.meetingTitle) {
|
|
|
+ params.meetingTitle = this.queryForm.meetingTitle
|
|
|
+ }
|
|
|
+ if (this.queryForm.meetingDateRange && this.queryForm.meetingDateRange.length === 2) {
|
|
|
+ params.meetingDateStart = this.queryForm.meetingDateRange[0]
|
|
|
+ params.meetingDateEnd = this.queryForm.meetingDateRange[1]
|
|
|
+ }
|
|
|
+ const res = await meetingApi.getList(params)
|
|
|
+ this.tableData = res.data?.list || []
|
|
|
+ this.total = res.data?.total || 0
|
|
|
+ } catch (error) {
|
|
|
+ this.$message.error('获取会议列表失败')
|
|
|
+ } finally {
|
|
|
+ this.loading = false
|
|
|
+ }
|
|
|
+ },
|
|
|
+ handleSearch() {
|
|
|
+ this.queryForm.pageNum = 1
|
|
|
+ this.fetchData()
|
|
|
+ },
|
|
|
+ handleReset() {
|
|
|
+ this.queryForm = {
|
|
|
+ pageNum: 1,
|
|
|
+ pageSize: 20,
|
|
|
+ meetingTitle: '',
|
|
|
+ meetingDateRange: [],
|
|
|
+ }
|
|
|
+ this.fetchData()
|
|
|
+ },
|
|
|
+ handleCurrentChange(val) {
|
|
|
+ this.queryForm.pageNum = val
|
|
|
+ this.fetchData()
|
|
|
+ },
|
|
|
+ handleSizeChange(val) {
|
|
|
+ this.queryForm.pageSize = val
|
|
|
+ this.queryForm.pageNum = 1
|
|
|
+ this.fetchData()
|
|
|
+ },
|
|
|
+ handleAdd() {
|
|
|
+ this.dialogTitle = '新增会议'
|
|
|
+ this.isEdit = false
|
|
|
+ this.resetEditForm()
|
|
|
+ this.editDialogVisible = true
|
|
|
+ },
|
|
|
+ async handleEdit(row) {
|
|
|
+ this.dialogTitle = '编辑会议'
|
|
|
+ this.isEdit = true
|
|
|
+ this.editForm.id = row.id
|
|
|
+ this.editDialogVisible = true
|
|
|
+ try {
|
|
|
+ const res = await meetingApi.getById(row.id)
|
|
|
+ const data = res.data?.data || res.data || {}
|
|
|
+ this.editForm.meetingTitle = data.meetingTitle || ''
|
|
|
+ this.editForm.meetingContent = data.meetingContent || ''
|
|
|
+ this.editForm.organizerId = data.organizerId || null
|
|
|
+ this.editForm.organizerName = data.organizerName || ''
|
|
|
+ this.editForm.meetingDate = data.meetingDate || ''
|
|
|
+ this.editForm.duration = data.duration || null
|
|
|
+ this.editForm.remark = data.remark || ''
|
|
|
+ this.editForm.deptId = data.deptId || null
|
|
|
+ const attendees = data.attendees || []
|
|
|
+ this.editForm.selectedUserIds = attendees.map((a) => a.userId)
|
|
|
+ this.editForm.selectedUserNames = attendees.map((a) => a.userName)
|
|
|
+ // 预加载已有参会人员到选项
|
|
|
+ this.userOptions = attendees.map((a) => ({
|
|
|
+ value: a.userId,
|
|
|
+ label: a.userName,
|
|
|
+ }))
|
|
|
+ this.userSearchCache = {}
|
|
|
+ attendees.forEach((a) => {
|
|
|
+ this.userSearchCache[a.userId] = { userId: a.userId, nickName: a.userName }
|
|
|
+ })
|
|
|
+ // 预加载组织人
|
|
|
+ if (data.organizerId) {
|
|
|
+ this.userOptions.unshift({
|
|
|
+ value: data.organizerId,
|
|
|
+ label: data.organizerName,
|
|
|
+ })
|
|
|
+ this.userSearchCache[data.organizerId] = {
|
|
|
+ userId: data.organizerId,
|
|
|
+ nickName: data.organizerName,
|
|
|
+ }
|
|
|
+ }
|
|
|
+ } catch (error) {
|
|
|
+ this.$message.error('获取会议详情失败')
|
|
|
+ }
|
|
|
+ },
|
|
|
+ handleViewDetail(row) {
|
|
|
+ this.handleEdit(row)
|
|
|
+ },
|
|
|
+ handleDelete(row) {
|
|
|
+ this.$confirm('确认删除该会议?', '提示', { type: 'warning' })
|
|
|
+ .then(async () => {
|
|
|
+ try {
|
|
|
+ await meetingApi.deleteByIds([row.id])
|
|
|
+ this.$message.success('删除成功')
|
|
|
+ this.fetchData()
|
|
|
+ } catch (error) {
|
|
|
+ this.$message.error('删除失败')
|
|
|
+ }
|
|
|
+ })
|
|
|
+ .catch(() => {})
|
|
|
+ },
|
|
|
+ resetEditForm() {
|
|
|
+ this.editForm = {
|
|
|
+ id: null,
|
|
|
+ meetingTitle: '',
|
|
|
+ meetingContent: '',
|
|
|
+ organizerId: null,
|
|
|
+ organizerName: '',
|
|
|
+ meetingDate: '',
|
|
|
+ duration: null,
|
|
|
+ remark: '',
|
|
|
+ selectedUserIds: [],
|
|
|
+ selectedUserNames: [],
|
|
|
+ }
|
|
|
+ this.userOptions = []
|
|
|
+ this.userSearchCache = {}
|
|
|
+ },
|
|
|
+ handleDialogClose() {
|
|
|
+ this.$refs.editForm && this.$refs.editForm.resetFields()
|
|
|
+ this.resetEditForm()
|
|
|
+ },
|
|
|
+ handleSelectedUsersChange(val) {
|
|
|
+ const names = []
|
|
|
+ val.forEach((id) => {
|
|
|
+ const cached = this.userSearchCache[id]
|
|
|
+ if (cached) {
|
|
|
+ names.push(cached.nickName)
|
|
|
+ }
|
|
|
+ })
|
|
|
+ this.editForm.selectedUserNames = names
|
|
|
+ },
|
|
|
+ handleOrganizerChange(id) {
|
|
|
+ if (id) {
|
|
|
+ const cached = this.userSearchCache[id]
|
|
|
+ this.editForm.organizerName = cached ? cached.nickName : ''
|
|
|
+ } else {
|
|
|
+ this.editForm.organizerName = ''
|
|
|
+ }
|
|
|
+ },
|
|
|
+ async remoteSearchUsers(query) {
|
|
|
+ this.userSearchLoading = true
|
|
|
+ try {
|
|
|
+ const payload = { deptId: DEVOPS_DEV_DEPT_ID, pageNum: 1, pageSize: 50 }
|
|
|
+ if (query) payload.keyWords = query
|
|
|
+ const res = await micro_request.postRequest(process.env.VUE_APP_AdminPath, 'User', 'GetList', payload)
|
|
|
+ const list = res.data?.list || []
|
|
|
+ this.userOptions = list.map((u) => {
|
|
|
+ const id = u.userId !== undefined ? u.userId : u.id
|
|
|
+ const name = u.nickName || u.name || ''
|
|
|
+ this.userSearchCache[id] = { userId: id, nickName: name }
|
|
|
+ return { value: id, label: name }
|
|
|
+ })
|
|
|
+ } catch (error) {
|
|
|
+ this.userOptions = []
|
|
|
+ } finally {
|
|
|
+ this.userSearchLoading = false
|
|
|
+ }
|
|
|
+ },
|
|
|
+ async quickSelectAll(target) {
|
|
|
+ try {
|
|
|
+ const res = await micro_request.postRequest(process.env.VUE_APP_AdminPath, 'User', 'GetList', {
|
|
|
+ deptId: DEVOPS_DEV_DEPT_ID,
|
|
|
+ pageNum: 1,
|
|
|
+ pageSize: 200,
|
|
|
+ })
|
|
|
+ const list = res.data?.list || []
|
|
|
+ const ids = []
|
|
|
+ const names = []
|
|
|
+ list.forEach((u) => {
|
|
|
+ const id = u.userId !== undefined ? u.userId : u.id
|
|
|
+ const name = u.nickName || u.name || ''
|
|
|
+ this.userSearchCache[id] = { userId: id, nickName: name }
|
|
|
+ ids.push(id)
|
|
|
+ names.push(name)
|
|
|
+ })
|
|
|
+ this.userOptions = list.map((u) => {
|
|
|
+ const id = u.userId !== undefined ? u.userId : u.id
|
|
|
+ return { value: id, label: u.nickName || u.name || '' }
|
|
|
+ })
|
|
|
+ if (target === 'edit') {
|
|
|
+ this.editForm.selectedUserIds = ids
|
|
|
+ this.editForm.selectedUserNames = names
|
|
|
+ } else if (target === 'attendee') {
|
|
|
+ this.attendeesForm.selectedUserIds = ids
|
|
|
+ }
|
|
|
+ } catch (error) {
|
|
|
+ this.$message.error('获取人员列表失败')
|
|
|
+ }
|
|
|
+ },
|
|
|
+ async quickSelectByRole(roleKey, target) {
|
|
|
+ try {
|
|
|
+ const res = await micro_request.postRequest(process.env.VUE_APP_AdminPath, 'User', 'GetUsersByRoleKeys', {
|
|
|
+ roleKeys: [roleKey],
|
|
|
+ roleIds: [],
|
|
|
+ })
|
|
|
+ const list = res.data || []
|
|
|
+ const ids = []
|
|
|
+ const names = []
|
|
|
+ list.forEach((u) => {
|
|
|
+ const id = u.userId !== undefined ? u.userId : u.id
|
|
|
+ const name = u.nickName || u.name || ''
|
|
|
+ this.userSearchCache[id] = { userId: id, nickName: name }
|
|
|
+ ids.push(id)
|
|
|
+ names.push(name)
|
|
|
+ })
|
|
|
+ this.userOptions = list.map((u) => {
|
|
|
+ const id = u.userId !== undefined ? u.userId : u.id
|
|
|
+ return { value: id, label: u.nickName || u.name || '' }
|
|
|
+ })
|
|
|
+ if (target === 'edit') {
|
|
|
+ this.editForm.selectedUserIds = ids
|
|
|
+ this.editForm.selectedUserNames = names
|
|
|
+ } else if (target === 'attendee') {
|
|
|
+ this.attendeesForm.selectedUserIds = ids
|
|
|
+ }
|
|
|
+ } catch (error) {
|
|
|
+ this.$message.error('获取角色人员失败')
|
|
|
+ }
|
|
|
+ },
|
|
|
+ handleSubmit() {
|
|
|
+ this.$refs.editForm.validate(async (valid) => {
|
|
|
+ if (!valid) return
|
|
|
+ this.submitLoading = true
|
|
|
+ try {
|
|
|
+ const data = {
|
|
|
+ meetingTitle: this.editForm.meetingTitle,
|
|
|
+ meetingContent: this.editForm.meetingContent,
|
|
|
+ organizerId: this.editForm.organizerId,
|
|
|
+ organizerName: this.editForm.organizerName,
|
|
|
+ meetingDate: this.editForm.meetingDate,
|
|
|
+ duration: this.editForm.duration,
|
|
|
+ remark: this.editForm.remark,
|
|
|
+ userIds: this.editForm.selectedUserIds,
|
|
|
+ userNames: this.editForm.selectedUserNames,
|
|
|
+ }
|
|
|
+ if (this.isEdit) {
|
|
|
+ data.id = this.editForm.id
|
|
|
+ await meetingApi.update(data)
|
|
|
+ this.$message.success('更新成功')
|
|
|
+ } else {
|
|
|
+ await meetingApi.create(data)
|
|
|
+ this.$message.success('创建成功')
|
|
|
+ }
|
|
|
+ this.editDialogVisible = false
|
|
|
+ this.fetchData()
|
|
|
+ } catch (error) {
|
|
|
+ this.$message.error(this.isEdit ? '更新失败' : '创建失败')
|
|
|
+ } finally {
|
|
|
+ this.submitLoading = false
|
|
|
+ }
|
|
|
+ })
|
|
|
+ },
|
|
|
+ handleAddAttendees(row) {
|
|
|
+ this.currentRow = row
|
|
|
+ this.attendeesForm.selectedUserIds = []
|
|
|
+ this.userOptions = []
|
|
|
+ this.userSearchCache = {}
|
|
|
+ this.addAttendeesDialogVisible = true
|
|
|
+ },
|
|
|
+ handleAttendeesDialogClose() {
|
|
|
+ this.currentRow = null
|
|
|
+ this.attendeesForm.selectedUserIds = []
|
|
|
+ this.userOptions = []
|
|
|
+ this.userSearchCache = {}
|
|
|
+ },
|
|
|
+ async handleAttendeesSubmit() {
|
|
|
+ if (!this.attendeesForm.selectedUserIds.length) {
|
|
|
+ this.$message.warning('请选择参会人员')
|
|
|
+ return
|
|
|
+ }
|
|
|
+ this.attendeeSubmitLoading = true
|
|
|
+ try {
|
|
|
+ const userNames = this.attendeesForm.selectedUserIds.map((id) => {
|
|
|
+ const cached = this.userSearchCache[id]
|
|
|
+ return cached ? cached.nickName : ''
|
|
|
+ })
|
|
|
+ await meetingApi.addAttendees({
|
|
|
+ id: this.currentRow.id,
|
|
|
+ userIds: this.attendeesForm.selectedUserIds,
|
|
|
+ userNames,
|
|
|
+ })
|
|
|
+ this.$message.success('追加成功')
|
|
|
+ this.addAttendeesDialogVisible = false
|
|
|
+ this.fetchData()
|
|
|
+ } catch (error) {
|
|
|
+ this.$message.error('追加失败')
|
|
|
+ } finally {
|
|
|
+ this.attendeeSubmitLoading = false
|
|
|
+ }
|
|
|
+ },
|
|
|
+ async handleViewWorkHour(row) {
|
|
|
+ this.currentRow = row
|
|
|
+ this.workHourDialogVisible = true
|
|
|
+ this.workHourList = []
|
|
|
+ this.workHourLoading = true
|
|
|
+ try {
|
|
|
+ const res = await meetingApi.getWorkHourList(row.id)
|
|
|
+ this.workHourList = res.data?.list || []
|
|
|
+ } catch (error) {
|
|
|
+ this.$message.error('获取工时列表失败')
|
|
|
+ } finally {
|
|
|
+ this.workHourLoading = false
|
|
|
+ }
|
|
|
+ },
|
|
|
+ },
|
|
|
+ }
|
|
|
+</script>
|
|
|
+
|
|
|
+<style lang="scss" scoped>
|
|
|
+ .meeting-management-page {
|
|
|
+ box-sizing: border-box;
|
|
|
+ display: flex;
|
|
|
+ flex-direction: column;
|
|
|
+ min-height: 0;
|
|
|
+ height: calc(100vh - 122px);
|
|
|
+ padding: 4px;
|
|
|
+ background: #f5f7fa;
|
|
|
+ overflow: hidden;
|
|
|
+
|
|
|
+ .query-form-container {
|
|
|
+ flex-shrink: 0;
|
|
|
+ margin-bottom: 4px;
|
|
|
+ padding: 8px 12px;
|
|
|
+ background: #fff;
|
|
|
+ border-radius: 6px;
|
|
|
+ box-shadow: 0 1px 4px rgba(0, 0, 0, 0.05);
|
|
|
+
|
|
|
+ .query-form-basic {
|
|
|
+ display: flex;
|
|
|
+ align-items: flex-start;
|
|
|
+ justify-content: space-between;
|
|
|
+ gap: 16px;
|
|
|
+
|
|
|
+ .query-form-fields {
|
|
|
+ flex: 1;
|
|
|
+ min-width: 0;
|
|
|
+ }
|
|
|
+
|
|
|
+ .query-form-actions {
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ justify-content: flex-end;
|
|
|
+ flex-shrink: 0;
|
|
|
+ min-height: 32px;
|
|
|
+ white-space: nowrap;
|
|
|
+ }
|
|
|
+
|
|
|
+ ::v-deep .el-form {
|
|
|
+ display: flex;
|
|
|
+ flex-wrap: wrap;
|
|
|
+ align-items: center;
|
|
|
+ row-gap: 2px;
|
|
|
+ }
|
|
|
+
|
|
|
+ ::v-deep .el-form-item {
|
|
|
+ margin-right: 20px;
|
|
|
+ margin-bottom: 2px;
|
|
|
+ }
|
|
|
+
|
|
|
+ ::v-deep .el-form-item__label {
|
|
|
+ padding-right: 8px;
|
|
|
+ font-size: 13px;
|
|
|
+ color: #606266;
|
|
|
+ }
|
|
|
+
|
|
|
+ ::v-deep .el-divider--vertical {
|
|
|
+ margin: 0 12px;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ .main-content {
|
|
|
+ flex: 1;
|
|
|
+ display: flex;
|
|
|
+ gap: 4px;
|
|
|
+ overflow: hidden;
|
|
|
+ min-height: 0;
|
|
|
+
|
|
|
+ .table-container {
|
|
|
+ flex: 1;
|
|
|
+ background: #fff;
|
|
|
+ border-radius: 4px;
|
|
|
+ padding: 6px 8px;
|
|
|
+ display: flex;
|
|
|
+ flex-direction: column;
|
|
|
+ box-shadow: 0 1px 4px rgba(0, 0, 0, 0.05);
|
|
|
+ min-width: 0;
|
|
|
+ min-height: 0;
|
|
|
+ overflow: hidden;
|
|
|
+
|
|
|
+ .table-scroll-wrapper {
|
|
|
+ display: flex;
|
|
|
+ flex: 1;
|
|
|
+ min-width: 0;
|
|
|
+ min-height: 0;
|
|
|
+ overflow: hidden;
|
|
|
+ }
|
|
|
+
|
|
|
+ .meeting-table {
|
|
|
+ flex: 1;
|
|
|
+ min-width: 0;
|
|
|
+
|
|
|
+ ::v-deep th .cell {
|
|
|
+ white-space: nowrap;
|
|
|
+ }
|
|
|
+
|
|
|
+ ::v-deep .el-button--text {
|
|
|
+ padding: 0;
|
|
|
+ font-size: 12px;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ .pagination-wrapper {
|
|
|
+ margin-top: auto;
|
|
|
+ display: flex;
|
|
|
+ justify-content: flex-end;
|
|
|
+ padding-top: 4px;
|
|
|
+
|
|
|
+ ::v-deep .el-pagination {
|
|
|
+ padding: 0;
|
|
|
+ line-height: 32px;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ .user-select {
|
|
|
+ width: 100%;
|
|
|
+ }
|
|
|
+
|
|
|
+ .quick-select-btns {
|
|
|
+ margin-bottom: 8px;
|
|
|
+
|
|
|
+ .el-button {
|
|
|
+ margin-right: 6px;
|
|
|
+ margin-bottom: 4px;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ .attendees-meeting-info {
|
|
|
+ padding: 8px 12px;
|
|
|
+ background: #f5f7fa;
|
|
|
+ border-radius: 4px;
|
|
|
+ font-size: 14px;
|
|
|
+ font-weight: 500;
|
|
|
+ color: #303133;
|
|
|
+ }
|
|
|
+ }
|
|
|
+</style>
|