2026-06-22 13:06:57 +08:00
|
|
|
<template>
|
|
|
|
|
<div v-if="enabled" class="section-container">
|
|
|
|
|
<div class="section-title">
|
2026-06-24 14:59:23 +08:00
|
|
|
<span>执行反馈 <span class="en-sub">· Execution Feedback</span></span>
|
2026-06-22 13:06:57 +08:00
|
|
|
<el-button size="mini" type="text" icon="el-icon-refresh" @click="$emit('refresh')" title="刷新执行反馈"></el-button>
|
|
|
|
|
</div>
|
|
|
|
|
<div v-if="executeList.length > 0" class="card-grid">
|
2026-06-24 14:59:23 +08:00
|
|
|
<div v-for="item in executeList" :key="item.relId" class="opinion-card">
|
|
|
|
|
<div class="opinion-card-header">
|
|
|
|
|
<div class="opinion-dept">
|
|
|
|
|
<span class="opinion-dept-icon">◆</span>
|
|
|
|
|
<span>{{ getDeptName(item.deptId) }}</span>
|
|
|
|
|
</div>
|
|
|
|
|
<el-tag v-if="item.rejectMark === 1" type="danger" size="mini">已驳回</el-tag>
|
|
|
|
|
<el-tag v-else-if="item.rejectMark === 2" type="info" size="mini">已隐藏</el-tag>
|
|
|
|
|
<el-tag v-else-if="item.executeStatus === 0" type="warning" size="mini">待执行</el-tag>
|
2026-06-22 13:06:57 +08:00
|
|
|
<el-tag v-else-if="item.executeStatus === 1" type="success" size="mini">已反馈</el-tag>
|
|
|
|
|
</div>
|
2026-06-24 14:59:23 +08:00
|
|
|
<div class="opinion-card-body">
|
|
|
|
|
<div v-if="item.executeResult" class="opinion-content" v-html="item.executeResult"></div>
|
|
|
|
|
<div v-else class="opinion-empty">— No feedback yet · 暂无反馈 —</div>
|
|
|
|
|
<div v-if="item.feedbackFile" class="opinion-file">
|
2026-06-22 13:06:57 +08:00
|
|
|
<FileList :ossIds="item.feedbackFile" />
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
2026-06-24 14:59:23 +08:00
|
|
|
<div class="opinion-card-footer">
|
|
|
|
|
<span v-if="item.feedbackNo" class="opinion-footer-item">{{ item.feedbackNo }}</span>
|
|
|
|
|
<span v-if="item.feedbackTime" class="opinion-footer-item">{{ parseTime(item.feedbackTime, '{y}-{m}-{d} {h}:{i}:{s}') }}</span>
|
2026-06-22 13:06:57 +08:00
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
<div v-else class="empty-data">暂无执行反馈</div>
|
|
|
|
|
</div>
|
|
|
|
|
</template>
|
|
|
|
|
|
|
|
|
|
<script>
|
|
|
|
|
import FileList from "@/components/FileList/index.vue";
|
|
|
|
|
|
|
|
|
|
export default {
|
|
|
|
|
name: 'ExecutionFeedbackSection',
|
|
|
|
|
components: { FileList },
|
|
|
|
|
props: {
|
|
|
|
|
enabled: {
|
|
|
|
|
type: Boolean,
|
|
|
|
|
default: true
|
|
|
|
|
},
|
|
|
|
|
executeList: {
|
|
|
|
|
type: Array,
|
|
|
|
|
default: () => []
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
methods: {
|
|
|
|
|
getDeptName(deptId) {
|
|
|
|
|
const map = { 1: '生产部', 2: '质量部', 3: '销售部' };
|
|
|
|
|
return map[deptId] || '部门' + deptId;
|
|
|
|
|
},
|
|
|
|
|
parseTime(time, option) {
|
|
|
|
|
if (!time) return '-';
|
|
|
|
|
const date = new Date(time);
|
|
|
|
|
if (isNaN(date.getTime())) return time;
|
|
|
|
|
const formatStr = option || '{y}-{m}-{d} {h}:{i}:{s}';
|
|
|
|
|
const year = date.getFullYear();
|
|
|
|
|
const month = date.getMonth() + 1;
|
|
|
|
|
const day = date.getDate();
|
|
|
|
|
const hours = date.getHours();
|
|
|
|
|
const minutes = date.getMinutes();
|
|
|
|
|
const seconds = date.getSeconds();
|
|
|
|
|
return formatStr.replace('{y}', year)
|
|
|
|
|
.replace('{m}', month < 10 ? '0' + month : month)
|
|
|
|
|
.replace('{d}', day < 10 ? '0' + day : day)
|
|
|
|
|
.replace('{h}', hours < 10 ? '0' + hours : hours)
|
|
|
|
|
.replace('{i}', minutes < 10 ? '0' + minutes : minutes)
|
|
|
|
|
.replace('{s}', seconds < 10 ? '0' + seconds : seconds);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
</script>
|
|
|
|
|
|
|
|
|
|
<style scoped>
|
|
|
|
|
.section-title {
|
2026-06-24 14:59:23 +08:00
|
|
|
font-family: 'Georgia', 'Times New Roman', 'Noto Serif SC', 'SimSun', serif;
|
2026-06-22 13:06:57 +08:00
|
|
|
width: 100%;
|
|
|
|
|
font-size: 15px;
|
2026-06-24 14:59:23 +08:00
|
|
|
font-weight: 700;
|
|
|
|
|
color: #1a1a1a;
|
|
|
|
|
margin: 32px 0 16px 0;
|
2026-06-22 13:06:57 +08:00
|
|
|
padding: 0 0 10px 0;
|
2026-06-24 14:59:23 +08:00
|
|
|
border-bottom: 1px solid #d4d0c8;
|
2026-06-22 13:06:57 +08:00
|
|
|
white-space: nowrap;
|
|
|
|
|
display: flex;
|
|
|
|
|
align-items: center;
|
2026-06-24 14:59:23 +08:00
|
|
|
gap: 10px;
|
|
|
|
|
letter-spacing: 0.3px;
|
2026-06-22 13:06:57 +08:00
|
|
|
}
|
|
|
|
|
.section-title:first-child {
|
|
|
|
|
margin-top: 0;
|
|
|
|
|
}
|
2026-06-24 14:59:23 +08:00
|
|
|
.section-title .en-sub {
|
|
|
|
|
font-size: 11px;
|
|
|
|
|
font-weight: 400;
|
|
|
|
|
color: #8c8c8c;
|
|
|
|
|
letter-spacing: 0.5px;
|
|
|
|
|
font-family: 'Georgia', 'Times New Roman', serif;
|
|
|
|
|
font-style: italic;
|
|
|
|
|
}
|
|
|
|
|
.section-title i {
|
|
|
|
|
font-size: 16px;
|
|
|
|
|
color: #1a3c6e;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* ===== 反馈卡片(正式文档风格) ===== */
|
2026-06-22 13:06:57 +08:00
|
|
|
.card-grid {
|
|
|
|
|
display: flex;
|
|
|
|
|
flex-wrap: wrap;
|
2026-06-24 14:59:23 +08:00
|
|
|
gap: 14px;
|
2026-06-22 13:06:57 +08:00
|
|
|
}
|
2026-06-24 14:59:23 +08:00
|
|
|
|
|
|
|
|
.opinion-card {
|
|
|
|
|
flex: 0 0 calc((100% - 14px) / 2);
|
|
|
|
|
background: #ffffff;
|
|
|
|
|
border: 1px solid #e8e4de;
|
2026-06-22 13:06:57 +08:00
|
|
|
border-radius: 2px;
|
2026-06-24 14:59:23 +08:00
|
|
|
padding: 14px 16px 12px;
|
2026-06-22 13:06:57 +08:00
|
|
|
display: flex;
|
|
|
|
|
flex-direction: column;
|
|
|
|
|
}
|
2026-06-24 14:59:23 +08:00
|
|
|
|
|
|
|
|
.opinion-card-header {
|
2026-06-22 13:06:57 +08:00
|
|
|
display: flex;
|
|
|
|
|
align-items: center;
|
|
|
|
|
justify-content: space-between;
|
2026-06-24 14:59:23 +08:00
|
|
|
margin-bottom: 10px;
|
|
|
|
|
padding-bottom: 8px;
|
|
|
|
|
border-bottom: 1px dashed #e0dcd6;
|
2026-06-22 13:06:57 +08:00
|
|
|
}
|
2026-06-24 14:59:23 +08:00
|
|
|
|
|
|
|
|
.opinion-dept {
|
|
|
|
|
display: flex;
|
|
|
|
|
align-items: center;
|
|
|
|
|
gap: 6px;
|
|
|
|
|
font-family: 'Georgia', 'Times New Roman', 'Noto Serif SC', 'SimSun', serif;
|
|
|
|
|
font-size: 14px;
|
|
|
|
|
font-weight: 700;
|
|
|
|
|
color: #1a3c6e;
|
|
|
|
|
letter-spacing: 0.3px;
|
2026-06-22 13:06:57 +08:00
|
|
|
}
|
2026-06-24 14:59:23 +08:00
|
|
|
|
|
|
|
|
.opinion-dept-icon {
|
|
|
|
|
font-size: 10px;
|
|
|
|
|
color: #1a3c6e;
|
2026-06-22 13:06:57 +08:00
|
|
|
}
|
2026-06-24 14:59:23 +08:00
|
|
|
|
|
|
|
|
.opinion-card-body {
|
2026-06-22 13:06:57 +08:00
|
|
|
flex: 1;
|
|
|
|
|
}
|
2026-06-24 14:59:23 +08:00
|
|
|
|
|
|
|
|
.opinion-content {
|
|
|
|
|
font-size: 13px;
|
|
|
|
|
color: #3a3a3a;
|
|
|
|
|
line-height: 1.7;
|
2026-06-22 13:06:57 +08:00
|
|
|
word-break: break-all;
|
2026-06-24 14:59:23 +08:00
|
|
|
max-height: 100px;
|
2026-06-22 13:06:57 +08:00
|
|
|
overflow-y: auto;
|
|
|
|
|
}
|
2026-06-24 14:59:23 +08:00
|
|
|
|
|
|
|
|
.opinion-content /deep/ p {
|
2026-06-22 13:06:57 +08:00
|
|
|
margin: 0;
|
|
|
|
|
}
|
2026-06-24 14:59:23 +08:00
|
|
|
|
|
|
|
|
.opinion-empty {
|
|
|
|
|
color: #bab5ae;
|
2026-06-22 13:06:57 +08:00
|
|
|
font-size: 12px;
|
2026-06-24 14:59:23 +08:00
|
|
|
font-style: italic;
|
|
|
|
|
font-family: 'Georgia', 'Times New Roman', serif;
|
2026-06-22 13:06:57 +08:00
|
|
|
}
|
2026-06-24 14:59:23 +08:00
|
|
|
|
|
|
|
|
.opinion-file {
|
|
|
|
|
margin-top: 8px;
|
2026-06-22 13:06:57 +08:00
|
|
|
}
|
2026-06-24 14:59:23 +08:00
|
|
|
|
|
|
|
|
.opinion-card-footer {
|
2026-06-22 13:06:57 +08:00
|
|
|
display: flex;
|
|
|
|
|
flex-wrap: wrap;
|
2026-06-24 14:59:23 +08:00
|
|
|
gap: 14px;
|
|
|
|
|
margin-top: 10px;
|
|
|
|
|
padding-top: 8px;
|
|
|
|
|
border-top: 1px dashed #e0dcd6;
|
2026-06-22 13:06:57 +08:00
|
|
|
}
|
2026-06-24 14:59:23 +08:00
|
|
|
|
|
|
|
|
.opinion-footer-item {
|
|
|
|
|
font-family: 'Georgia', 'Times New Roman', serif;
|
2026-06-22 13:06:57 +08:00
|
|
|
font-size: 11px;
|
2026-06-24 14:59:23 +08:00
|
|
|
color: #8c8c8c;
|
2026-06-22 13:06:57 +08:00
|
|
|
}
|
2026-06-24 14:59:23 +08:00
|
|
|
|
2026-06-22 13:06:57 +08:00
|
|
|
.empty-data {
|
2026-06-24 14:59:23 +08:00
|
|
|
color: #8c8c8c;
|
2026-06-22 13:06:57 +08:00
|
|
|
font-size: 13px;
|
|
|
|
|
padding: 8px 0;
|
2026-06-24 14:59:23 +08:00
|
|
|
font-style: italic;
|
2026-06-22 13:06:57 +08:00
|
|
|
}
|
|
|
|
|
</style>
|