2025-06-18 09:26:56 +08:00
|
|
|
|
<script setup lang="ts">
|
|
|
|
|
import {ref, onMounted} from "vue";
|
|
|
|
|
import {useRoute, useRouter} from "vue-router";
|
|
|
|
|
import myAxios from "../../api/myAxios";
|
|
|
|
|
import {message} from "ant-design-vue";
|
|
|
|
|
|
2025-06-19 20:09:38 +08:00
|
|
|
|
function decode64(text: string): string {
|
|
|
|
|
try {
|
|
|
|
|
return decodeURIComponent(
|
|
|
|
|
Array.from(atob(text), char =>
|
|
|
|
|
'%' + ('00' + char.charCodeAt(0).toString(16)).slice(-2)
|
|
|
|
|
).join('')
|
|
|
|
|
);
|
|
|
|
|
} catch (error) {
|
|
|
|
|
console.error('Base64解码失败:', error);
|
|
|
|
|
return text; // 解码失败时返回原始文本
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-06-18 09:26:56 +08:00
|
|
|
|
const columns = [
|
|
|
|
|
{
|
|
|
|
|
title: '项目通知ID',
|
|
|
|
|
dataIndex: 'id',
|
|
|
|
|
width: 20,
|
|
|
|
|
key: 'id',
|
|
|
|
|
fixed: 'left',
|
|
|
|
|
align: 'center'
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
title: '通知标题',
|
|
|
|
|
dataIndex: 'notificationTitle',
|
|
|
|
|
key: 'notificationTitle',
|
|
|
|
|
width: 30,
|
|
|
|
|
fixed: 'left',
|
|
|
|
|
align: 'center'
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
title: '通知内容',
|
|
|
|
|
dataIndex: 'notificationContent',
|
|
|
|
|
key: 'notificationContent',
|
|
|
|
|
width: 150,
|
|
|
|
|
align: 'center'
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
title: '项目ID',
|
|
|
|
|
dataIndex: 'projectId',
|
|
|
|
|
key: 'projectId',
|
|
|
|
|
width: 20,
|
|
|
|
|
align: 'center'
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
title: '创建时间',
|
|
|
|
|
dataIndex: 'createTime',
|
|
|
|
|
key: 'createTime',
|
|
|
|
|
width: 70,
|
|
|
|
|
align: 'center'
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
title: '操作',
|
|
|
|
|
key: 'action',
|
|
|
|
|
fixed: 'right',
|
|
|
|
|
width: 100,
|
|
|
|
|
align: 'center'
|
|
|
|
|
}
|
|
|
|
|
];
|
|
|
|
|
|
|
|
|
|
// 修改接口数据类型
|
|
|
|
|
interface ProjectNotification {
|
|
|
|
|
id: number;
|
|
|
|
|
notificationTitle: string;
|
|
|
|
|
notificationContent: string;
|
|
|
|
|
projectId: number;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const route = useRoute();
|
|
|
|
|
const projectId = ref<string | number>("");
|
|
|
|
|
const originalTableData = ref<ProjectNotification[]>([]);
|
|
|
|
|
const searchedData = ref<ProjectNotification[]>([]); // 存储搜索结果
|
|
|
|
|
const displayData = ref<ProjectNotification[]>([]); // 实际显示的数据
|
|
|
|
|
|
|
|
|
|
const loading = ref(false);
|
|
|
|
|
const error = ref("");
|
|
|
|
|
const searchId = ref("");
|
|
|
|
|
|
|
|
|
|
// 修改搜索处理方法,不再调用后端接口
|
|
|
|
|
const handleIdSearch = () => {
|
|
|
|
|
const value = searchId.value.trim();
|
|
|
|
|
|
|
|
|
|
if (!value) {
|
|
|
|
|
message.warning("请输入有效的项目通知ID");
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const id = Number(value);
|
|
|
|
|
if (isNaN(id)) {
|
|
|
|
|
message.warning("ID必须为数字");
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 在原始数据中过滤
|
|
|
|
|
const result = originalTableData.value.filter(item => item.id === id);
|
|
|
|
|
|
|
|
|
|
if (result.length === 0) {
|
|
|
|
|
message.warning("未找到匹配的项目通知ID");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 更新显示数据为搜索结果
|
|
|
|
|
searchedData.value = result;
|
|
|
|
|
displayData.value = result;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const reset = () => {
|
|
|
|
|
searchId.value = "";
|
|
|
|
|
displayData.value = originalTableData.value;
|
|
|
|
|
searchedData.value = [];
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
if (typeof route.query.id === "string") {
|
|
|
|
|
projectId.value = route.query.id;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
onMounted(() => {
|
|
|
|
|
if (projectId.value) {
|
|
|
|
|
getNotifications(projectId.value);
|
|
|
|
|
}
|
|
|
|
|
});
|
2025-06-19 20:09:38 +08:00
|
|
|
|
|
2025-06-18 09:26:56 +08:00
|
|
|
|
const getNotifications = async (id: string | number) => {
|
|
|
|
|
const storedToken = localStorage.getItem('token');
|
|
|
|
|
try {
|
|
|
|
|
loading.value = true;
|
|
|
|
|
const response:any = await myAxios.post(
|
|
|
|
|
"/projectNotification/query/pid",
|
|
|
|
|
{ id },
|
|
|
|
|
{
|
|
|
|
|
headers: {
|
|
|
|
|
Authorization: storedToken,
|
|
|
|
|
"Content-Type": "application/json"
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
);
|
|
|
|
|
if (response.code === 1) {
|
2025-06-19 20:09:38 +08:00
|
|
|
|
// 对通知内容进行Base64解码
|
|
|
|
|
originalTableData.value = response.data.map((item:any) => ({
|
|
|
|
|
...item,
|
|
|
|
|
notificationContent: decode64(item.notificationContent)
|
|
|
|
|
}));
|
|
|
|
|
displayData.value = originalTableData.value;
|
2025-06-18 09:26:56 +08:00
|
|
|
|
} else {
|
|
|
|
|
error.value = "获取通知列表失败";
|
|
|
|
|
originalTableData.value = [];
|
|
|
|
|
displayData.value = [];
|
|
|
|
|
}
|
|
|
|
|
} catch (err) {
|
|
|
|
|
error.value = "数据加载失败,请重试";
|
|
|
|
|
originalTableData.value = [];
|
|
|
|
|
displayData.value = [];
|
|
|
|
|
} finally {
|
|
|
|
|
loading.value = false;
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
2025-06-19 20:09:38 +08:00
|
|
|
|
|
2025-06-18 09:26:56 +08:00
|
|
|
|
onMounted(() => {
|
|
|
|
|
if (projectId.value) {
|
|
|
|
|
getNotifications(projectId.value);
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const goToNotificationDetail = (id: number) => {
|
|
|
|
|
router.push({
|
|
|
|
|
path: '/noticeDetail',
|
|
|
|
|
query: { id }
|
|
|
|
|
});
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
onMounted(getNotifications)
|
|
|
|
|
|
|
|
|
|
//删除操作
|
|
|
|
|
const deleteNotification = async (id: number) => {
|
|
|
|
|
try {
|
|
|
|
|
const storedToken = localStorage.getItem('token');
|
|
|
|
|
const res: any = await myAxios.post(
|
|
|
|
|
"/projectNotification/delete",
|
|
|
|
|
{ id },
|
|
|
|
|
{
|
|
|
|
|
headers: {
|
|
|
|
|
Authorization: storedToken,
|
|
|
|
|
'AfterScript': 'required-script'
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
if (res.code === 1) {
|
|
|
|
|
message.success('删除成功');
|
|
|
|
|
if (projectId.value) {
|
|
|
|
|
await getNotifications (projectId.value);
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
message.error(res.message || '删除失败');
|
|
|
|
|
}
|
|
|
|
|
} catch (error) {
|
|
|
|
|
console.error('删除失败:', error);
|
|
|
|
|
message.error('删除操作失败');
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 从路由参数中获取项目ID
|
|
|
|
|
if (typeof route.query.id === "string") {
|
|
|
|
|
projectId.value = route.query.id;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 修改新增按钮的路由跳转,传递projectId到新增页面
|
|
|
|
|
const goAddProjectNotice = () => {
|
|
|
|
|
router.push({
|
|
|
|
|
path: '/addprojectNotice',
|
|
|
|
|
query: { projectId: projectId.value } // 传递项目ID到新增页面
|
|
|
|
|
});
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const router = useRouter();
|
|
|
|
|
|
|
|
|
|
const goBack = () => {
|
|
|
|
|
router.push('/project')
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
</script>
|
|
|
|
|
|
|
|
|
|
<template>
|
|
|
|
|
<!-- 搜索框 -->
|
|
|
|
|
<div class="search-box">
|
|
|
|
|
<a-form layout="inline">
|
|
|
|
|
<a-space>
|
|
|
|
|
<a-form-item label="ID">
|
|
|
|
|
<a-input-search
|
|
|
|
|
style="width: 300px"
|
|
|
|
|
placeholder="请输入项目通知ID"
|
|
|
|
|
enter-button
|
|
|
|
|
@search="handleIdSearch"
|
|
|
|
|
v-model:value="searchId"
|
|
|
|
|
type="number"
|
|
|
|
|
class="custom-search"
|
|
|
|
|
min="0"
|
|
|
|
|
/>
|
|
|
|
|
</a-form-item>
|
|
|
|
|
<a-button class="custom-button" @click="goAddProjectNotice">新增项目通知</a-button>
|
|
|
|
|
<a-button class="custom-button" @click="reset">重置搜索</a-button>
|
2025-06-19 20:09:38 +08:00
|
|
|
|
<a-button @click="goBack" class="custom-button">返回</a-button>
|
2025-06-18 09:26:56 +08:00
|
|
|
|
</a-space>
|
|
|
|
|
</a-form>
|
|
|
|
|
</div>
|
|
|
|
|
<!-- 修改表格模板 -->
|
|
|
|
|
<a-table
|
|
|
|
|
:columns="columns"
|
|
|
|
|
:data-source="displayData"
|
|
|
|
|
:scroll="{ x: 1200, y: 450 }"
|
|
|
|
|
:loading="loading"
|
|
|
|
|
bordered
|
|
|
|
|
rowKey="id"
|
|
|
|
|
>
|
|
|
|
|
<template #bodyCell="{ column, record }">
|
|
|
|
|
<template v-if="column.key === 'action'">
|
|
|
|
|
<a-space :size="8">
|
|
|
|
|
<a-button size="small" danger @click="deleteNotification(record.id)">
|
|
|
|
|
删除
|
|
|
|
|
</a-button>
|
|
|
|
|
<a-button size="small" @click="goToNotificationDetail(record.id)">
|
|
|
|
|
编辑
|
|
|
|
|
</a-button>
|
|
|
|
|
</a-space>
|
|
|
|
|
</template>
|
|
|
|
|
</template>
|
|
|
|
|
</a-table>
|
|
|
|
|
|
|
|
|
|
</template>
|
|
|
|
|
|
|
|
|
|
<style scoped>
|
|
|
|
|
.search-box {
|
|
|
|
|
margin-bottom: 20px;
|
|
|
|
|
padding: 16px;
|
|
|
|
|
background: #fff;
|
|
|
|
|
border-radius: 8px;
|
|
|
|
|
box-shadow: 0 2px 8px rgba(0,0,0,0.1);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
:deep(.ant-table-thead) {
|
|
|
|
|
background-color: #fafafa !important;
|
|
|
|
|
font-weight: 600;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
:deep(.ant-table-row:hover) {
|
|
|
|
|
background-color: #fafafa !important;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/*橙色按钮*/
|
|
|
|
|
|
|
|
|
|
.custom-button {
|
|
|
|
|
background-color: #ffa940;
|
|
|
|
|
border-color: #ffa940;
|
|
|
|
|
color: #fff;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.custom-button:hover,
|
|
|
|
|
.custom-button:focus {
|
|
|
|
|
background-color: #ffa940;
|
|
|
|
|
border-color: #ffa940;
|
|
|
|
|
color: #fff;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.custom-button[disabled] {
|
|
|
|
|
background-color: #ffa940;
|
|
|
|
|
border-color: #ffa940;
|
|
|
|
|
opacity: 0.6;
|
|
|
|
|
color: #fff;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* 危险按钮样式 */
|
|
|
|
|
.custom-button.ant-btn-dangerous {
|
|
|
|
|
background-color: #ff4d4f;
|
|
|
|
|
border-color: #ff4d4f;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.custom-button.ant-btn-dangerous:hover,
|
|
|
|
|
.custom-button.ant-btn-dangerous:focus {
|
|
|
|
|
background-color: #ff7875;
|
|
|
|
|
border-color: #ff7875;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* 保持原有的其他样式不变 */
|
|
|
|
|
.search-box {
|
|
|
|
|
margin-bottom: 10px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.custom-search :deep(.ant-input-search-button) {
|
|
|
|
|
background-color: #ffa940;
|
|
|
|
|
border-color: #ffa940;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.custom-search :deep(.ant-input-search-button:hover),
|
|
|
|
|
.custom-search :deep(.ant-input-search-button:focus) {
|
|
|
|
|
background-color: #fa8c16;
|
|
|
|
|
border-color: #fa8c16;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* 保持输入框原有样式不变 */
|
|
|
|
|
.custom-search :deep(.ant-input) {
|
|
|
|
|
border-right-color: #ffa940;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* 调整分页器位置 */
|
|
|
|
|
:deep(.ant-table-pagination.ant-pagination) {
|
|
|
|
|
margin: 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
</style>
|