整合前端
This commit is contained in:
552
ruoyi-ui/src/views/oa/feedback/index.vue
Normal file
552
ruoyi-ui/src/views/oa/feedback/index.vue
Normal file
@@ -0,0 +1,552 @@
|
||||
<template>
|
||||
<div class="message-view-page">
|
||||
<el-row>
|
||||
<!-- 左侧:消息列表(展平,顶部有搜索和新增按钮,底部有分页) -->
|
||||
<el-col :span="8" class="message-list" v-loading="loading">
|
||||
<!-- 搜索和新增按钮区域 -->
|
||||
<div class="list-header">
|
||||
<el-input placeholder="搜索消息..." v-model="queryParams.title" style="width: 200px; margin-right: 10px;"
|
||||
@change="getList" clearable></el-input>
|
||||
<!-- 筛选图表, 点击后切换下方筛选条件的显示和隐藏 -->
|
||||
<el-button type="primary" icon="el-icon-search" @click="toggleFilter"></el-button>
|
||||
<el-button type="primary" icon="el-icon-plus" @click="dialogVisible = true"></el-button>
|
||||
</div>
|
||||
<div v-show="showFilter" style="display: flex;align-items: center;">
|
||||
<!-- 日期时间选择器,选定开始时间和结束时间 -->
|
||||
<el-date-picker v-model="queryParams.timeRange" @change="getList" type="datetimerange" placeholder="选择开始时间"
|
||||
value-format="yyyy-MM-dd HH:mm:ss"></el-date-picker>
|
||||
<project-select v-model="queryParams.projectId" style="width: 200px; margin-right: 10px;"
|
||||
@change="getList"></project-select>
|
||||
</div>
|
||||
<!-- 消息列表 -->
|
||||
<div v-for="(msg, index) in filteredMessageList" :key="msg.feedbackId" class="message-item"
|
||||
:class="{ 'unread': msg.state === 0 }" @click="handleSelectMessage(msg)">
|
||||
<!-- 消息标题和操作区 -->
|
||||
<div class="message-header">
|
||||
<div class="message-title">
|
||||
<!-- 已读/未读标记 -->
|
||||
<span class="state-dot"></span>
|
||||
<span>{{ msg.title }}</span>
|
||||
</div>
|
||||
<el-button icon="el-icon-delete" @click.stop="handleDel(msg)" circle size="mini"></el-button>
|
||||
</div>
|
||||
|
||||
<!-- 消息状态和附加信息区 -->
|
||||
<div class="message-info">
|
||||
<div class="message-status">
|
||||
{{ msg.state === 0 ? '点击查看' : '已读' }}
|
||||
</div>
|
||||
<!-- 项目信息展示区域 -->
|
||||
<div class="message-extra">
|
||||
<!-- 项目信息弹窗展示 -->
|
||||
<el-popover v-if="msg.projectId" placement="top" trigger="hover" :width="300">
|
||||
<!-- 截断显示的项目名称 -->
|
||||
<span slot="reference" class="project-name">
|
||||
{{ truncateText(msg.projectName, 15) }}
|
||||
</span>
|
||||
<div>
|
||||
<el-descriptions column="1" border>
|
||||
<el-descriptions-item label="项目代号">
|
||||
<el-tag v-if="msg.projectCode == null" type="danger">无</el-tag>
|
||||
<el-tag v-else>{{ msg.projectCode }}</el-tag>
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="项目名称">{{ msg.projectName }}</el-descriptions-item>
|
||||
<el-descriptions-item label="项目编号">{{ msg.projectNum }}</el-descriptions-item>
|
||||
</el-descriptions>
|
||||
</div>
|
||||
</el-popover>
|
||||
<!-- 无项目信息时显示 -->
|
||||
<span v-else class="no-project">无项目</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- 分页组件 -->
|
||||
<pagination :total="total" :page.sync="queryParams.pageNum" :limit.sync="queryParams.pageSize"
|
||||
@pagination="getList" style="margin-top: 10px;" />
|
||||
</el-col>
|
||||
|
||||
<!-- 右侧:显示消息详情或空状态 -->
|
||||
<el-col :span="16" class="message-detail" v-loading="msgLoading">
|
||||
<!-- 详情内容 -->
|
||||
<div v-if="selectedMessage" class="detail-content">
|
||||
<!-- 消息头部信息(标题、创建时间、创建人) -->
|
||||
<div class="detail-header">
|
||||
<h2 class="detail-title">{{ selectedMessage.title }}</h2>
|
||||
<div class="detail-meta">
|
||||
<span class="meta-item">
|
||||
<i class="el-icon-user"></i>
|
||||
创建人:{{ selectedMessage.createBy || '未知' }}
|
||||
</span>
|
||||
<span class="meta-item">
|
||||
<i class="el-icon-time"></i>
|
||||
创建时间:{{ formatTime(selectedMessage.createTime) }}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 消息内容分割线 -->
|
||||
<div class="detail-divider"></div>
|
||||
|
||||
<!-- 消息正文内容 -->
|
||||
<div class="detail-body" v-html="selectedMessage.content"></div>
|
||||
</div>
|
||||
|
||||
<!-- 空状态 -->
|
||||
<div v-else class="detail-empty">
|
||||
<el-empty description="请选择一条消息查看"></el-empty>
|
||||
</div>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<!-- 对话框:新增 Feedback -->
|
||||
<el-dialog title="新增反馈" :visible.sync="dialogVisible" width="800px" @close="resetForm">
|
||||
<el-form ref="formRef" :model="form" label-width="80px" :rules="rules" status-icon>
|
||||
<el-form-item label="标题" prop="title">
|
||||
<el-input v-model="form.title"></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item label="反馈内容">
|
||||
<!-- 这里假设 editor 组件已正确引入,若实际无此组件,可替换为 el-input 等 -->
|
||||
<editor v-model="form.content" :min-height="192" />
|
||||
</el-form-item>
|
||||
<el-form-item label="项目" prop="projectId">
|
||||
<ProjectSelect v-model="form.projectId" />
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
|
||||
<span slot="footer" class="dialog-footer">
|
||||
<el-button @click="dialogVisible = false">取消</el-button>
|
||||
<el-button type="primary" @click="handleAddFeedback">提交</el-button>
|
||||
</span>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { addFeedback, delFeedback, listFeedback, toRead } from "@/api/oa/feedback";
|
||||
import ProjectSelect from '@/components/fad-service/ProjectSelect/index.vue';
|
||||
|
||||
export default {
|
||||
name: "MessageViewPage",
|
||||
components: {
|
||||
ProjectSelect,
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
// 搜索文本
|
||||
searchText: '',
|
||||
// 消息列表数据
|
||||
messageList: [],
|
||||
// 过滤后的消息列表
|
||||
filteredMessageList: [],
|
||||
// 当前选中的消息
|
||||
selectedMessage: null,
|
||||
|
||||
// 分页查询参数
|
||||
queryParams: {
|
||||
pageSize: 10,
|
||||
pageNum: 1,
|
||||
title: undefined,
|
||||
projectId: undefined,
|
||||
timeRange: [],
|
||||
type: 'feedback'
|
||||
},
|
||||
total: 0,
|
||||
loading: false,
|
||||
msgLoading: false,
|
||||
|
||||
// 新增反馈对话框的显示控制
|
||||
dialogVisible: false,
|
||||
|
||||
// 表单数据
|
||||
form: {
|
||||
title: "",
|
||||
content: "",
|
||||
projectId: null,
|
||||
},
|
||||
|
||||
// 表单校验规则
|
||||
rules: {
|
||||
title: [
|
||||
{ required: true, message: "请输入标题", trigger: "blur" },
|
||||
{ min: 2, max: 50, message: "标题长度在 2 到 50 个字符", trigger: "blur" },
|
||||
],
|
||||
content: [
|
||||
{ required: true, message: "请输入反馈内容", trigger: "blur" },
|
||||
],
|
||||
},
|
||||
showFilter: false,
|
||||
};
|
||||
},
|
||||
created () {
|
||||
this.getList();
|
||||
},
|
||||
watch: {
|
||||
searchText: {
|
||||
handler () {
|
||||
this.filterMessages();
|
||||
},
|
||||
immediate: true
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
/**
|
||||
* 处理删除消息
|
||||
* @param {Object} row - 消息对象
|
||||
*/
|
||||
handleDel (row) {
|
||||
this.$confirm('确定要删除这条消息吗?', '提示', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning'
|
||||
}).then(() => {
|
||||
delFeedback(row.feedbackId)
|
||||
.then(() => {
|
||||
this.$message.success("删除成功!");
|
||||
this.getList();
|
||||
// 如果删除的是当前选中的消息,则清空选中状态
|
||||
if (this.selectedMessage && this.selectedMessage.feedbackId === row.feedbackId) {
|
||||
this.selectedMessage = null;
|
||||
}
|
||||
})
|
||||
.catch(err => {
|
||||
this.$message.error(err.message || "删除失败");
|
||||
});
|
||||
}).catch(() => {
|
||||
this.$message.info('已取消删除');
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* 获取反馈列表
|
||||
*/
|
||||
getList () {
|
||||
this.loading = true;
|
||||
listFeedback({
|
||||
...this.queryParams,
|
||||
startTime: this.queryParams.timeRange[0] ? this.queryParams.timeRange[0] : undefined,
|
||||
endTime: this.queryParams.timeRange[1] ? this.queryParams.timeRange[1] : undefined
|
||||
})
|
||||
.then((res) => {
|
||||
this.messageList = res.rows || [];
|
||||
this.total = res.total || 0;
|
||||
this.filterMessages();
|
||||
})
|
||||
.catch(err => {
|
||||
this.$message.error(err.message || "获取消息列表失败");
|
||||
})
|
||||
.finally(() => {
|
||||
this.loading = false;
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* 过滤消息列表
|
||||
*/
|
||||
filterMessages () {
|
||||
if (!this.searchText) {
|
||||
this.filteredMessageList = this.messageList;
|
||||
return;
|
||||
}
|
||||
this.filteredMessageList = this.messageList.filter(msg =>
|
||||
msg.title.includes(this.searchText) ||
|
||||
msg.projectName?.includes(this.searchText) ||
|
||||
msg.projectCode?.includes(this.searchText)
|
||||
);
|
||||
},
|
||||
|
||||
/**
|
||||
* 选中某条消息
|
||||
* @param {Object} msg - 消息对象
|
||||
*/
|
||||
handleSelectMessage (msg) {
|
||||
this.msgLoading = true;
|
||||
this.selectedMessage = { ...msg }; // 深拷贝避免直接修改原数据
|
||||
|
||||
// 将未读消息改为已读
|
||||
if (msg.state === 0) {
|
||||
toRead(msg)
|
||||
.then(() => {
|
||||
msg.state = 1; // 更新列表中的状态
|
||||
// 过滤后列表也需要更新状态,这里简单重新过滤一次
|
||||
this.filterMessages();
|
||||
})
|
||||
.catch(err => {
|
||||
this.$message.error(err.message || "更新消息状态失败");
|
||||
})
|
||||
.finally(() => {
|
||||
this.msgLoading = false;
|
||||
});
|
||||
} else {
|
||||
this.msgLoading = false;
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* 提交新增反馈
|
||||
*/
|
||||
handleAddFeedback () {
|
||||
this.$refs.formRef.validate((valid) => {
|
||||
if (!valid) return;
|
||||
|
||||
addFeedback(this.form)
|
||||
.then(() => {
|
||||
this.$message.success("反馈新增成功!");
|
||||
this.dialogVisible = false;
|
||||
this.getList();
|
||||
this.resetForm();
|
||||
})
|
||||
.catch(err => {
|
||||
this.$message.error(err.message || "新增失败");
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* 重置表单
|
||||
*/
|
||||
resetForm () {
|
||||
this.$refs.formRef && this.$refs.formRef.resetFields();
|
||||
this.form = {
|
||||
title: "",
|
||||
content: "",
|
||||
projectId: null,
|
||||
};
|
||||
},
|
||||
|
||||
/**
|
||||
* 格式化时间
|
||||
* @param {String} time - 时间字符串
|
||||
* @returns {String} 格式化后的时间
|
||||
*/
|
||||
formatTime (time) {
|
||||
if (!time) return '';
|
||||
// 可根据需要调整时间格式化方式
|
||||
return new Date(time).toLocaleString();
|
||||
},
|
||||
|
||||
/**
|
||||
* 截断文本(用于项目名称过长时)
|
||||
* @param {String} text - 需要截断的文本
|
||||
* @param {Number} length - 保留的长度
|
||||
* @returns {String} 截断后的文本
|
||||
*/
|
||||
truncateText (text, length = 15) {
|
||||
if (!text) return '';
|
||||
if (text.length <= length) return text;
|
||||
return text.substring(0, length) + '...';
|
||||
},
|
||||
toggleFilter () {
|
||||
this.showFilter = !this.showFilter;
|
||||
}
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.message-list {
|
||||
height: calc(100vh - 160px);
|
||||
/* 调整高度,为分页留出空间 */
|
||||
overflow-y: scroll;
|
||||
overflow-x: hidden;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.message-view-page {
|
||||
padding: 20px;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
/* 列表头部(搜索和新增按钮) */
|
||||
.list-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
|
||||
/* 消息列表项样式 */
|
||||
.message-item {
|
||||
cursor: pointer;
|
||||
padding: 12px 15px;
|
||||
margin-bottom: 8px;
|
||||
border-bottom: 1px solid #ebeef5;
|
||||
transition: background-color 0.2s;
|
||||
}
|
||||
|
||||
.message-item:hover {
|
||||
background-color: #f5f7fa;
|
||||
}
|
||||
|
||||
.message-item.unread {
|
||||
background-color: #fff8f8;
|
||||
}
|
||||
|
||||
/* 消息头部(标题和删除按钮) */
|
||||
.message-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
/* 消息标题 */
|
||||
.message-title {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
font-weight: 500;
|
||||
color: #303133;
|
||||
}
|
||||
|
||||
/* 已读/未读标记 */
|
||||
.state-dot {
|
||||
display: inline-block;
|
||||
width: 8px;
|
||||
height: 8px;
|
||||
border-radius: 50%;
|
||||
background-color: #cccccc;
|
||||
/* 已读 */
|
||||
margin-right: 8px;
|
||||
}
|
||||
|
||||
.message-item.unread .state-dot {
|
||||
background-color: #fa5555;
|
||||
/* 未读 */
|
||||
}
|
||||
|
||||
/* 消息信息区域(状态和额外信息) */
|
||||
.message-info {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
/* 消息状态(已读/未读提示) */
|
||||
.message-status {
|
||||
color: #666;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
/* 额外信息区域 */
|
||||
.message-extra {
|
||||
color: #909399;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
/* 项目名称样式 */
|
||||
.project-name {
|
||||
cursor: pointer;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.project-name:hover {
|
||||
color: #409eff;
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
/* 无项目信息样式 */
|
||||
.no-project {
|
||||
color: #c0c4cc;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
/* 右侧详情区域 */
|
||||
.message-detail {
|
||||
padding-left: 20px;
|
||||
position: relative;
|
||||
overflow-y: scroll;
|
||||
overflow-x: hidden;
|
||||
height: calc(100vh - 120px);
|
||||
}
|
||||
|
||||
/* 详情内容容器 */
|
||||
.detail-container {
|
||||
height: 600px;
|
||||
overflow-y: auto;
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
/* 空状态样式 */
|
||||
.detail-empty {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
height: 600px;
|
||||
border: 1px solid #ebeef5;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
/* 对话框底部按钮 */
|
||||
.dialog-footer {
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
/* 详情内容容器 */
|
||||
.detail-content {
|
||||
padding: 20px;
|
||||
background-color: #fff;
|
||||
border-radius: 4px;
|
||||
min-height: 600px;
|
||||
/* 确保内容区高度一致 */
|
||||
box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.05);
|
||||
}
|
||||
|
||||
/* 详情头部 */
|
||||
.detail-header {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
/* 详情标题 */
|
||||
.detail-title {
|
||||
font-size: 18px;
|
||||
font-weight: 600;
|
||||
color: #303133;
|
||||
margin-bottom: 12px;
|
||||
padding-bottom: 8px;
|
||||
border-bottom: 1px solid #f0f0f0;
|
||||
}
|
||||
|
||||
/* 元信息(创建人、时间等) */
|
||||
.detail-meta {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
/* 支持换行,避免内容溢出 */
|
||||
gap: 15px;
|
||||
/* 元素间距 */
|
||||
color: #606266;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
/* 单个元信息项 */
|
||||
.meta-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 5px;
|
||||
}
|
||||
|
||||
/* 分割线 */
|
||||
.detail-divider {
|
||||
height: 1px;
|
||||
background-color: #f0f0f0;
|
||||
margin: 15px 0;
|
||||
}
|
||||
|
||||
/* 正文内容 */
|
||||
.detail-body {
|
||||
color: #303133;
|
||||
line-height: 1.8;
|
||||
/* 优化行高,提升可读性 */
|
||||
font-size: 15px;
|
||||
padding: 10px 0;
|
||||
}
|
||||
|
||||
/* 空状态容器 */
|
||||
.detail-empty {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
height: 600px;
|
||||
background-color: #fff;
|
||||
border-radius: 4px;
|
||||
box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.05);
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user