| | |
| | | <el-form-item label="日志标题"> |
| | | <el-input v-model="searchForm.title" placeholder="请输入日志标题" clearable /> |
| | | </el-form-item> |
| | | <el-form-item label="创建人"> |
| | | <el-input v-model="searchForm.createUserName" placeholder="请输入创建人" clearable /> |
| | | </el-form-item> |
| | | <el-form-item label="创建时间"> |
| | | <el-date-picker |
| | | v-model="searchForm.dateRange" |
| | |
| | | <span> |
| | | <el-icon><Calendar /></el-icon> |
| | | 日报 |
| | | <span class="tab-count" v-if="journalCount.daily > 0">({{ journalCount.daily }})</span> |
| | | </span> |
| | | </template> |
| | | </el-tab-pane> |
| | |
| | | <span> |
| | | <el-icon><Document /></el-icon> |
| | | 周报 |
| | | <span class="tab-count" v-if="journalCount.weekly > 0">({{ journalCount.weekly }})</span> |
| | | </span> |
| | | </template> |
| | | </el-tab-pane> |
| | |
| | | <span> |
| | | <el-icon><DataAnalysis /></el-icon> |
| | | 月报 |
| | | <span class="tab-count" v-if="journalCount.monthly > 0">({{ journalCount.monthly }})</span> |
| | | </span> |
| | | </template> |
| | | </el-tab-pane> |
| | |
| | | <!-- 日志表格 --> |
| | | <el-table :data="journalList" v-loading="loading" border style="width: 100%"> |
| | | <el-table-column type="index" label="序号" width="60" align="center" /> |
| | | <el-table-column prop="title" label="日志标题" min-width="200" show-overflow-tooltip> |
| | | <el-table-column prop="title" label="日志标题" show-overflow-tooltip /> |
| | | <el-table-column prop="reportType" label="报告类型" width="100" align="center"> |
| | | <template #default="scope"> |
| | | <el-link type="primary" @click="handleView(scope.row)">{{ scope.row.title }}</el-link> |
| | | <el-tag :type="getTypeTagType(scope.row.reportType)">{{ getTypeText(scope.row.reportType) }}</el-tag> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column prop="type" label="日志类型" width="100" align="center"> |
| | | <el-table-column prop="logType" label="日志类型" align="center"> |
| | | <template #default="scope"> |
| | | <el-tag :type="getTypeTagType(scope.row.type)">{{ getTypeText(scope.row.type) }}</el-tag> |
| | | <el-tag>{{ getLogTypeText(scope.row.logType) }}</el-tag> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column prop="content" label="日志内容" min-width="300" show-overflow-tooltip /> |
| | | <el-table-column prop="createUserName" label="创建人" width="120" align="center" /> |
| | | <el-table-column prop="createTime" label="创建时间" width="160" align="center" /> |
| | | <el-table-column prop="pushStatus" label="推送状态" width="100" align="center"> |
| | | <template #default="scope"> |
| | | <el-tag :type="scope.row.pushStatus === 1 ? 'success' : 'info'"> |
| | | {{ scope.row.pushStatus === 1 ? '已推送' : '未推送' }} |
| | | </el-tag> |
| | | </template> |
| | | </el-table-column> |
| | | <el-table-column prop="createUserName" label="创建人" align="center" /> |
| | | <el-table-column prop="createTime" label="创建时间" align="center" /> |
| | | <el-table-column prop="ccUserName" label="抄送人" align="center" /> |
| | | <el-table-column label="操作" width="250" align="center" fixed="right"> |
| | | <template #default="scope"> |
| | | <el-button link type="primary" @click="handleView(scope.row)">查看</el-button> |
| | | <el-button link type="primary" @click="handleEdit(scope.row)">编辑</el-button> |
| | | <el-button link type="success" @click="handlePush(scope.row)" v-if="scope.row.pushStatus !== 1">推送</el-button> |
| | | <el-button link type="primary" @click="handleView(scope.row)">查看</el-button> |
| | | <el-button link type="danger" @click="handleDelete(scope.row.id)">删除</el-button> |
| | | </template> |
| | | </el-table-column> |
| | |
| | | <pagination |
| | | v-show="total > 0" |
| | | :total="total" |
| | | v-model:page="queryParams.pageNum" |
| | | v-model:limit="queryParams.pageSize" |
| | | @pagination="getList" |
| | | v-model:page="queryParams.current" |
| | | v-model:limit="queryParams.size" |
| | | @pagination="({ page, limit }) => { queryParams.current = page; queryParams.size = limit; getList(); }" |
| | | /> |
| | | |
| | | <!-- 空状态 --> |
| | |
| | | <el-form ref="formRef" :model="form" :rules="rules" label-width="100px"> |
| | | <el-row> |
| | | <el-col :span="12"> |
| | | <el-form-item label="日志类型" prop="type"> |
| | | <el-select v-model="form.type" placeholder="请选择日志类型" style="width: 100%"> |
| | | <el-form-item label="报告类型"> |
| | | <el-select v-model="form.reportType" disabled style="width: 100%"> |
| | | <el-option label="日报" value="daily" /> |
| | | <el-option label="周报" value="weekly" /> |
| | | <el-option label="月报" value="monthly" /> |
| | |
| | | </el-form-item> |
| | | </el-col> |
| | | <el-col :span="12"> |
| | | <el-form-item label="日志类型" prop="logType"> |
| | | <el-select v-model="form.logType" placeholder="请选择日志类型" style="width: 100%"> |
| | | <el-option label="工作" value="work" /> |
| | | <el-option label="项目" value="project" /> |
| | | <el-option label="问题" value="problem" /> |
| | | <el-option label="其他" value="other" /> |
| | | </el-select> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | <el-row> |
| | | <el-col :span="24"> |
| | | <el-form-item label="日志标题" prop="title"> |
| | | <el-input v-model="form.title" placeholder="请输入日志标题" /> |
| | | </el-form-item> |
| | |
| | | </el-row> |
| | | <el-row> |
| | | <el-col :span="24"> |
| | | <el-form-item label="抄送人" prop="ccUserIds"> |
| | | <el-select |
| | | v-model="form.ccUserIds" |
| | | multiple |
| | | filterable |
| | | remote |
| | | reserve-keyword |
| | | placeholder="请输入抄送人名称搜索" |
| | | :remote-method="searchPushUsers" |
| | | :loading="userLoading" |
| | | style="width: 100%" |
| | | > |
| | | <el-option |
| | | v-for="item in userOptions" |
| | | :key="item.userId" |
| | | :label="item.nickName" |
| | | :value="item.userId" |
| | | > |
| | | </el-option> |
| | | </el-select> |
| | | </el-form-item> |
| | | </el-col> |
| | | </el-row> |
| | | <el-row> |
| | | <el-col :span="24"> |
| | | <el-form-item label="备注"> |
| | | <el-input |
| | | v-model="form.remark" |
| | |
| | | </el-row> |
| | | </el-form> |
| | | <template #footer> |
| | | <div class="dialog-footer"> |
| | | <el-button type="primary" @click="submitForm">保 存</el-button> |
| | | <el-button type="success" @click="submitAndPushForm">保存并推送</el-button> |
| | | <el-button @click="dialogVisible = false">取 消</el-button> |
| | | </div> |
| | | <el-button type="primary" @click="submitForm">保存</el-button> |
| | | <el-button @click="dialogVisible = false">取 消</el-button> |
| | | </template> |
| | | </el-dialog> |
| | | |
| | |
| | | <div class="detail-header"> |
| | | <h3 class="detail-title">{{ currentJournal.title }}</h3> |
| | | <div class="detail-meta"> |
| | | <el-tag :type="getTypeTagType(currentJournal.type)">{{ getTypeText(currentJournal.type) }}</el-tag> |
| | | <el-tag :type="getTypeTagType(currentJournal.reportType)">{{ getTypeText(currentJournal.reportType) }}</el-tag> |
| | | <el-tag>{{ getLogTypeText(currentJournal.logType) }}</el-tag> |
| | | <el-tag :type="currentJournal.pushStatus === 1 ? 'success' : 'info'"> |
| | | {{ currentJournal.pushStatus === 1 ? '已推送' : '未推送' }} |
| | | </el-tag> |
| | | </div> |
| | | </div> |
| | | <div class="detail-info"> |
| | | <span><el-icon><User /></el-icon> 创建人:{{ currentJournal.createUserName }}</span> |
| | | <span><el-icon><User /></el-icon> 创建人:{{ currentJournal.createUser }}</span> |
| | | <span><el-icon><Timer /></el-icon> 创建时间:{{ currentJournal.createTime }}</span> |
| | | </div> |
| | | <el-divider /> |
| | |
| | | </div> |
| | | </div> |
| | | <template #footer> |
| | | <div class="dialog-footer"> |
| | | <el-button type="primary" @click="handleEditFromView" v-if="currentJournal.pushStatus !== 1">编 辑</el-button> |
| | | <el-button type="success" @click="handlePushFromView" v-if="currentJournal.pushStatus !== 1">推 送</el-button> |
| | | <el-button @click="viewDialogVisible = false">关 闭</el-button> |
| | | </div> |
| | | <el-button type="primary" @click="handleEditFromView" v-if="currentJournal.pushStatus !== 1">编 辑</el-button> |
| | | <el-button type="success" @click="handlePushFromView" v-if="currentJournal.pushStatus !== 1">推 送</el-button> |
| | | <el-button @click="viewDialogVisible = false">关 闭</el-button> |
| | | </template> |
| | | </el-dialog> |
| | | |
| | |
| | | <p><strong>日志类型:</strong>{{ getTypeText(currentJournal.type) }}</p> |
| | | </div> |
| | | <el-form ref="pushFormRef" :model="pushForm" :rules="pushRules" label-width="100px"> |
| | | <el-form-item label="推送人员" prop="userIds"> |
| | | <el-form-item label="抄送人" prop="userIds"> |
| | | <el-select |
| | | v-model="pushForm.userIds" |
| | | multiple |
| | | filterable |
| | | remote |
| | | reserve-keyword |
| | | placeholder="请输入用户名搜索" |
| | | placeholder="请输入抄送人名称搜索" |
| | | :remote-method="remoteSearchUser" |
| | | :loading="userLoading" |
| | | style="width: 100%" |
| | |
| | | <el-option |
| | | v-for="item in userOptions" |
| | | :key="item.userId" |
| | | :label="item.userName" |
| | | :label="item.nickName" |
| | | :value="item.userId" |
| | | > |
| | | <span>{{ item.userName }}</span> |
| | | <span style="float: right; color: #8492a6; font-size: 13px">{{ item.deptName }}</span> |
| | | </el-option> |
| | | </el-select> |
| | | </el-form-item> |
| | |
| | | import useUserStore from "@/store/modules/user"; |
| | | import { |
| | | listJournal, |
| | | getJournal, |
| | | addJournal, |
| | | updateJournal, |
| | | delJournal, |
| | | pushJournal, |
| | | listUser |
| | | delJournal |
| | | } from "@/api/collaborativeApproval/journal.js"; |
| | | import { userListNoPage } from "@/api/system/user.js"; |
| | | import Pagination from "@/components/Pagination/index.vue"; |
| | | |
| | | const userStore = useUserStore(); |
| | |
| | | const data = reactive({ |
| | | searchForm: { |
| | | title: "", |
| | | createUserName: "", |
| | | dateRange: [] |
| | | }, |
| | | form: { |
| | | id: undefined, |
| | | type: "daily", |
| | | reportType: "daily", |
| | | logType: "work", |
| | | title: "", |
| | | content: "", |
| | | remark: "", |
| | | pushStatus: 0 |
| | | pushStatus: 0, |
| | | ccUserIds: [] |
| | | }, |
| | | rules: { |
| | | type: [ |
| | | reportType: [ |
| | | { required: true, message: "请选择报告类型", trigger: "change" } |
| | | ], |
| | | logType: [ |
| | | { required: true, message: "请选择日志类型", trigger: "change" } |
| | | ], |
| | | title: [ |
| | |
| | | |
| | | // 查询参数 |
| | | const queryParams = reactive({ |
| | | pageNum: 1, |
| | | pageSize: 10 |
| | | current: 1, |
| | | size: 10 |
| | | }); |
| | | |
| | | // 推送表单 |
| | |
| | | const getList = () => { |
| | | loading.value = true; |
| | | const params = { |
| | | pageNum: queryParams.pageNum, |
| | | pageSize: queryParams.pageSize, |
| | | type: activeJournalType.value |
| | | current: queryParams.current, |
| | | size: queryParams.size, |
| | | reportType: activeJournalType.value |
| | | }; |
| | | |
| | | |
| | | if (searchForm.value.title) { |
| | | params.title = searchForm.value.title; |
| | | } |
| | | if (searchForm.value.createUserName) { |
| | | params.createUserName = searchForm.value.createUserName; |
| | | } |
| | | if (searchForm.value.dateRange && searchForm.value.dateRange.length === 2) { |
| | | params.startTime = searchForm.value.dateRange[0]; |
| | |
| | | |
| | | // 获取所有类型数量 |
| | | const getAllTypeCounts = () => { |
| | | ['daily', 'weekly', 'monthly'].forEach(type => { |
| | | listJournal({ pageNum: 1, pageSize: 1, type }).then(res => { |
| | | ['daily', 'weekly', 'monthly'].forEach(reportType => { |
| | | listJournal({ current: 1, size: 1, reportType }).then(res => { |
| | | if (res.code === 200) { |
| | | journalCount[type] = res.data.total || 0; |
| | | journalCount[reportType] = res.data.total || 0; |
| | | } |
| | | }); |
| | | }); |
| | |
| | | // Tab切换 |
| | | const handleTabChange = (tabName) => { |
| | | activeJournalType.value = tabName; |
| | | queryParams.pageNum = 1; |
| | | queryParams.current = 1; |
| | | getList(); |
| | | }; |
| | | |
| | | // 搜索 |
| | | const handleQuery = () => { |
| | | queryParams.pageNum = 1; |
| | | queryParams.current = 1; |
| | | getList(); |
| | | }; |
| | | |
| | |
| | | const resetQuery = () => { |
| | | searchForm.value = { |
| | | title: "", |
| | | createUserName: "", |
| | | dateRange: [] |
| | | }; |
| | | handleQuery(); |
| | | }; |
| | | |
| | | // 获取类型文本 |
| | | const getTypeText = (type) => { |
| | | const getTypeText = (reportType) => { |
| | | const typeMap = { daily: "日报", weekly: "周报", monthly: "月报" }; |
| | | return typeMap[type] || type; |
| | | return typeMap[reportType] || reportType; |
| | | }; |
| | | |
| | | // 获取日志类型文本 |
| | | const getLogTypeText = (logType) => { |
| | | const typeMap = { work: "工作", project: "项目", problem: "问题", other: "其他" }; |
| | | return typeMap[logType] || logType; |
| | | }; |
| | | |
| | | // 获取类型标签样式 |
| | | const getTypeTagType = (type) => { |
| | | const getTypeTagType = (reportType) => { |
| | | const typeMap = { daily: "primary", weekly: "success", monthly: "warning" }; |
| | | return typeMap[type] || ""; |
| | | return typeMap[reportType] || ""; |
| | | }; |
| | | |
| | | // 打开表单 |
| | |
| | | dialogTitle.value = "编写日志"; |
| | | form.value = { |
| | | id: undefined, |
| | | type: activeJournalType.value, |
| | | reportType: activeJournalType.value, |
| | | logType: "work", |
| | | title: "", |
| | | content: "", |
| | | remark: "", |
| | | pushStatus: 0 |
| | | pushStatus: 0, |
| | | ccUserIds: [] |
| | | }; |
| | | } |
| | | dialogVisible.value = true; |
| | | loadUserOptions(); |
| | | }; |
| | | |
| | | // 编辑日志 |
| | | const handleEdit = (row) => { |
| | | if (row.pushStatus === 1) { |
| | | ElMessage.warning("已推送的日志不可编辑"); |
| | | return; |
| | | } |
| | | dialogTitle.value = "编辑日志"; |
| | | form.value = { ...row }; |
| | | form.value = { ...row, ccUserIds: row.ccUserIds || [] }; |
| | | dialogVisible.value = true; |
| | | loadUserOptions(); |
| | | }; |
| | | |
| | | // 从查看页面编辑 |
| | |
| | | |
| | | // 查看日志 |
| | | const handleView = (row) => { |
| | | getJournal(row.id).then(res => { |
| | | if (res.code === 200) { |
| | | currentJournal.value = res.data; |
| | | viewDialogVisible.value = true; |
| | | } |
| | | }); |
| | | currentJournal.value = { ...row }; |
| | | viewDialogVisible.value = true; |
| | | }; |
| | | |
| | | // 删除日志 |
| | |
| | | type: "warning" |
| | | } |
| | | ).then(() => { |
| | | delJournal(id).then(res => { |
| | | delJournal([id]).then(res => { |
| | | if (res.code === 200) { |
| | | ElMessage.success("删除成功"); |
| | | getList(); |
| | | getAllTypeCounts(); |
| | | } |
| | | }); |
| | | }); |
| | |
| | | |
| | | // 加载用户选项 |
| | | const loadUserOptions = () => { |
| | | listUser({ pageNum: 1, pageSize: 100 }).then(res => { |
| | | userListNoPage().then(res => { |
| | | if (res.code === 200) { |
| | | userOptions.value = res.data.records || res.data || []; |
| | | userOptions.value = res.data || []; |
| | | } |
| | | }); |
| | | }; |
| | | |
| | | // 远程搜索用户 |
| | | const remoteSearchUser = (query) => { |
| | | if (query) { |
| | | userLoading.value = true; |
| | | listUser({ userName: query, pageNum: 1, pageSize: 50 }).then(res => { |
| | | if (res.code === 200) { |
| | | userOptions.value = res.data.records || res.data || []; |
| | | } |
| | | userLoading.value = false; |
| | | }).catch(() => { |
| | | userLoading.value = false; |
| | | }); |
| | | } |
| | | // 远程搜索抄送用户 |
| | | const searchPushUsers = (query) => { |
| | | userLoading.value = true; |
| | | userListNoPage().then(res => { |
| | | if (res.code === 200) { |
| | | userOptions.value = res.data || []; |
| | | } |
| | | userLoading.value = false; |
| | | }).catch(() => { |
| | | userLoading.value = false; |
| | | }); |
| | | }; |
| | | |
| | | // 提交表单 |
| | | const submitForm = () => { |
| | | formRef.value.validate((valid) => { |
| | | if (valid) { |
| | | const api = form.value.id ? updateJournal : addJournal; |
| | | api(form.value).then(res => { |
| | | const isEdit = !!form.value.id; |
| | | const api = isEdit ? updateJournal : addJournal; |
| | | const saveData = { ...form.value }; |
| | | |
| | | api(saveData).then(res => { |
| | | if (res.code === 200) { |
| | | ElMessage.success(form.value.id ? "修改成功" : "新增成功"); |
| | | ElMessage.success(isEdit ? "修改成功" : "保存成功"); |
| | | dialogVisible.value = false; |
| | | getList(); |
| | | getAllTypeCounts(); |
| | | } |
| | | }); |
| | | } |
| | | }); |
| | | }; |
| | | |
| | | // 提交并推送 |
| | | const submitAndPushForm = () => { |
| | | formRef.value.validate((valid) => { |
| | | if (valid) { |
| | | const api = form.value.id ? updateJournal : addJournal; |
| | | api(form.value).then(res => { |
| | | if (res.code === 200) { |
| | | const journalId = form.value.id || res.data; |
| | | dialogVisible.value = false; |
| | | // 打开推送弹窗 |
| | | currentJournal.value = { ...form.value, id: journalId }; |
| | | pushForm.journalId = journalId; |
| | | pushForm.userIds = []; |
| | | pushForm.remark = ""; |
| | | pushDialogVisible.value = true; |
| | | loadUserOptions(); |
| | | getList(); |
| | | getAllTypeCounts(); |
| | | } |
| | | }); |
| | | } |
| | | }); |
| | | }; |
| | | |
| | | // 提交推送 |
| | | const submitPush = () => { |
| | | pushFormRef.value.validate((valid) => { |
| | | if (valid) { |
| | | pushJournal(pushForm).then(res => { |
| | | if (res.code === 200) { |
| | | ElMessage.success("推送成功"); |
| | | pushDialogVisible.value = false; |
| | | getList(); |
| | | getAllTypeCounts(); |
| | | } |
| | | }); |
| | | } |
| | |
| | | |
| | | onMounted(() => { |
| | | getList(); |
| | | getAllTypeCounts(); |
| | | }); |
| | | </script> |
| | | |