新增待办信息

This commit is contained in:
2025-12-16 22:22:58 +08:00
parent e5b6f7a2a6
commit 5884a3125c
23 changed files with 374 additions and 171 deletions

View File

@@ -34,7 +34,7 @@ import java.io.Serial;
@Column(name = "operation_type", attrName = "operationType", label = "操作类型", isQuery = false),
@Column(name = "status_name", attrName = "statusName", label = "操作状态", isQuery = false),
@Column(name = "flow_content", attrName = "flowContent", label = "流程内容", isQuery = false),
}, orderBy = "a.calendar_flow_id DESC"
}, orderBy = "a.create_time DESC"
)
@Data
public class BizCalendarFlow extends DataEntity<BizCalendarFlow> implements Serializable {

View File

@@ -37,12 +37,13 @@ import java.io.Serial;
@Column(name = "avatar", attrName = "avatar", label = "头像图标", isUpdate = false, isUpdateForce = true),
@Column(name = "title", attrName = "title", label = "通知标题", queryType = QueryType.LIKE),
@Column(name = "title_delete", attrName = "titleDelete", label = "是否删除"),
@Column(name = "datetime", attrName = "datetime", label = "发送时间", isQuery = false),
@Column(name = "datetime", attrName = "datetime", label = "发送时间"),
@Column(name = "type", attrName = "type", label = "类型标识"),
@Column(name = "read_flag", attrName = "readFlag", label = "是否已读"),
@Column(name = "description", attrName = "description", label = "描述信息", isQuery = false),
@Column(name = "click_close", attrName = "clickClose", label = "是否关闭"),
@Column(name = "extra", attrName = "extra", label = "待办状态"),
@Column(name = "extra_desc", attrName = "extraDesc", label = "待办意见"),
@Column(name = "color", attrName = "color", label = "颜色值", isQuery = false),
@Column(name = "update_time", attrName = "updateTime", label = "更新时间", isQuery = false),
@Column(name = "login_user", attrName = "loginUser", label = "接收用户"),
@@ -69,6 +70,7 @@ public class BizListItem extends DataEntity<BizListItem> implements Serializable
private String description; // 描述信息
private String clickClose; // 是否关闭
private String extra; // 待办状态
private String extraDesc; //待办意见
private String color; // 颜色值
private Date updateTime; // 更新时间
private String loginUser;
@@ -82,15 +84,15 @@ public class BizListItem extends DataEntity<BizListItem> implements Serializable
@ExcelFields({
@ExcelField(title = "创建时间", attrName = "createTime", align = Align.CENTER, sort = 10, dataFormat = "yyyy-MM-dd hh:mm"),
@ExcelField(title = "唯一标识", attrName = "id", align = Align.CENTER, sort = 20),
@ExcelField(title = "头像图标", attrName = "avatar", align = Align.CENTER, sort = 30),
@ExcelField(title = "通知标题", attrName = "title", align = Align.CENTER, sort = 40),
@ExcelField(title = "是否删除", attrName = "titleDelete", align = Align.CENTER, sort = 50),
@ExcelField(title = "发送时间", attrName = "datetime", align = Align.CENTER, sort = 60, dataFormat = "yyyy-MM-dd"),
@ExcelField(title = "类型标识", attrName = "type", align = Align.CENTER, sort = 70),
@ExcelField(title = "是否已读", attrName = "readFlag", align = Align.CENTER, sort = 80),
@ExcelField(title = "是否删除", attrName = "titleDelete", dictType = "is_open", align = Align.CENTER, sort = 50),
@ExcelField(title = "到期时间", attrName = "datetime", align = Align.CENTER, sort = 60, dataFormat = "yyyy-MM-dd"),
@ExcelField(title = "所属类型", attrName = "type", dictType = "msg_type", align = Align.CENTER, sort = 70),
@ExcelField(title = "是否已读", attrName = "readFlag", dictType = "is_open", align = Align.CENTER, sort = 80),
@ExcelField(title = "描述信息", attrName = "description", align = Align.CENTER, sort = 90),
@ExcelField(title = "是否关闭", attrName = "clickClose", align = Align.CENTER, sort = 100),
@ExcelField(title = "是否关闭", attrName = "clickClose", dictType = "is_open", align = Align.CENTER, sort = 100),
@ExcelField(title = "待办状态", attrName = "extra", align = Align.CENTER, sort = 110),
@ExcelField(title = "待办意见", attrName = "extraDesc", align = Align.CENTER, sort = 120),
})
public BizListItem() {
this(null);
@@ -116,4 +118,13 @@ public class BizListItem extends DataEntity<BizListItem> implements Serializable
sqlMap.getWhere().and("create_time", QueryType.LTE, createTime);
}
public Date getDateTime_gte() {
return sqlMap.getWhere().getValue("datetime", QueryType.GTE);
}
public void setDateTime_gte(Date createTime) {
sqlMap.getWhere().and("datetime", QueryType.GTE, createTime);
}
}

View File

@@ -52,7 +52,7 @@ import java.io.Serial;
columns = {
@Column(name = "account_name", attrName = "accountName", label = "配置名称"),
}),
}, orderBy = "a.id DESC"
}, orderBy = "a.create_time DESC"
)
@Data
public class BizMailReceived extends DataEntity<BizMailReceived> implements Serializable {

View File

@@ -1,6 +1,7 @@
package com.jeesite.modules.biz.web;
import java.util.Arrays;
import java.util.Date;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;
@@ -97,7 +98,7 @@ public class BizListItemController extends BaseController {
@ResponseBody
public String save(@Validated BizListItem bizListItem) {
User user = UserUtils.getUser();
if (bizListItem.getType().equals("3")){
if (bizListItem.getType().equals("3")) {
User loginUser = UserUtils.getByLoginCode(bizListItem.getLoginUser());
bizListItem.setUserName(loginUser.getUserName());
}
@@ -178,6 +179,7 @@ public class BizListItemController extends BaseController {
listItem.setClickClose("0");
listItem.setTitleDelete("0");
listItem.setType(type.getCode());
listItem.setDateTime_gte(new Date());
if (type.getCode().equals("3")) {
listItem.setLoginUser(user.getLoginCode());
}

View File

@@ -23,6 +23,7 @@ export interface BizListItem extends BasicModel<BizListItem> {
description: string; // 描述信息
clickClose?: string; // 是否关闭
extra?: string; // 待办状态
extraDesc?: string; // 待办意见
color?: string; // 颜色值
updateTime: string; // 更新时间
ftenantId?: string; // 租户id

View File

@@ -34,7 +34,7 @@
const { t } = useI18n('biz.listItem');
const { showMessage } = useMessage();
const { meta } = unref(router.currentRoute);
const ustatus = ref<string>('0');
const types = ref<string>('');
const record = ref<BizListItem>({} as BizListItem);
@@ -88,6 +88,28 @@
required: true,
colProps: { md: 24, lg: 24 },
},
{
label: t('是否关闭'),
field: 'clickClose',
defaultValue: ustatus.value,
component: 'Select',
componentProps: {
dictType: 'is_open',
allowClear: true,
},
required: true,
},
{
label: t('是否删除'),
field: 'titleDelete',
defaultValue: ustatus.value,
component: 'Select',
componentProps: {
dictType: 'is_open',
allowClear: true,
},
required: true,
},
{
label: t('内容信息'),
field: 'description',

View File

@@ -185,14 +185,6 @@
width: 130,
align: 'left',
dictType: 'is_open',
},
{
title: t('内容信息'),
dataIndex: 'description',
key: 'a.description',
sorter: true,
width: 225,
align: 'left',
},
{
title: t('接收用户'),
@@ -256,7 +248,6 @@
confirm: handleDelete.bind(this, record),
},
auth: 'biz:listItem:edit',
ifShow: record.clickClose == '9'
},
],
};

View File

@@ -10,9 +10,6 @@
<TabPane key="3" tab="附件箱">
<MailAttachments />
</TabPane>
<TabPane key="4" tab="邮箱配置">
<MailAccount />
</TabPane>
</Tabs>
</div>
</template>
@@ -22,7 +19,6 @@
import MailSent from './sent/list.vue';
import MailReceived from './received/list.vue';
import MailAttachments from './attachments/list.vue';
import MailAccount from './account/list.vue';
const activeKey = ref('1');
</script>

View File

@@ -0,0 +1,32 @@
<template>
<div class="web-page-container">
<Tabs v-model:activeKey="activeKey">
<TabPane key="1" tab="系统配置">
<QuickLogin />
</TabPane>
<TabPane key="2" tab="邮箱配置">
<MailAccount />
</TabPane>
</Tabs>
</div>
</template>
<script lang="ts" setup name="MailAccount">
import { h, ref } from 'vue';
import { Tag, Tabs ,TabPane } from 'ant-design-vue';
import MailAccount from './account/list.vue';
import QuickLogin from './quickLogin/list.vue';
const activeKey = ref('1');
</script>
<style scoped lang="less">
// 整体容器样式
.web-page-container {
width: 100%;
background-color: #e8f4f8;
display: flex;
flex-direction: column; // 垂直布局
overflow: hidden; // 防止内容溢出
}
</style>

View File

@@ -58,6 +58,7 @@
const [register, { closeModal }] = useModalInner(async (data: any) => {
if (!data) return;
NoticeList.value = Array.isArray(data) ? data[0] : data;
TodoValue.value = NoticeList.value.extraDesc|| '';
});
const getTypeText = (type: string) => {
@@ -77,8 +78,8 @@
const params = {
id: NoticeList.value?.id ?? '',
extra: TodoValue.value ?? {},
readFlag: true,
extraDesc: TodoValue.value ?? {},
readFlag: '1',
};
try {
const res = await bizListItemSflow(params);
@@ -171,12 +172,11 @@
box-shadow: 0 1px 3px rgba(0,0,0,0.05);
}
/* 数据库格式展示样式 - 优化字体和间距 */
.db-content {
margin: 0;
font-family: 'Consolas', 'Monaco', 'Courier New', monospace;
white-space: pre-wrap; /* 保留换行和空格 */
word-break: break-all; /* 防止长文本溢出 */
white-space: pre-wrap;
word-break: break-all;
color: #333;
}

View File

@@ -0,0 +1,137 @@
<template>
<Card title="常用应用" class="common-app-card">
<div class="app-list">
<div class="app-item" v-for="(app, index) in appList" :key="index">
<a-button type="link" size="small" @click="goToMorePage(app)" class="app-btn">
<img :src="app.iconClass" class="app-icon" />
</a-button>
<span class="app-text">{{ app.systemName }}</span>
</div>
</div>
</Card>
</template>
<script lang="ts" setup name="BizApp">
import { ref, onMounted } from 'vue';
import { Card } from 'ant-design-vue';
import { useRouter } from 'vue-router';
import { BizQuickLogin, bizQuickLoginListAll } from '@jeesite/biz/api/biz/quickLogin';
const appList = ref<BizQuickLogin[]>([]);
const router = useRouter();
const loading = ref(true);
const fetchAppList = async () => {
try {
const params = {
isEnabled: '1',
systemType: '1'
};
const result = await bizQuickLoginListAll(params);
appList.value = result || [];
} catch (error) {
console.error('获取应用列表失败:', error);
appList.value = [];
}
};
const goToMorePage = (app : BizQuickLogin) => {
router.push(app.homepageUrl);
};
onMounted(() => {
const timer = setTimeout(() => {
fetchAppList();
loading.value = false;
clearTimeout(timer);
}, 800);
});
</script>
<style scoped lang="less">
.common-app-card {
width: 100%;
margin: 4px 0;
border-radius: 8px;
flex-shrink: 0; // 确保不被压缩
// 移除Card默认的内边距让内容更紧凑
:deep(.ant-card-body) {
padding: 8px 12px !important; // 减小卡片内部边距
}
// 应用列表容器 - 占满整行
.app-list {
display: flex;
flex-wrap: wrap; // 超出自动换行
gap: 8px; // 从12px改为8px
width: 100%;
padding: 4px 0; // 从8px改为4px
// 移除inline-flex改用flex确保布局稳定
.app-item {
display: flex;
flex-direction: column;
align-items: center;
gap: 4px;
padding: 6px 10px; // 从8px 16px改为6px 10px
flex: 0 0 auto; // 固定宽度,不拉伸
// 按钮重置
.app-btn {
padding: 0;
height: auto;
&:hover,
&:focus {
background: transparent;
}
}
// 应用图标样式
.app-icon {
width: 40px;
height: 28px;
object-fit: contain;
border-radius: 8px;
border: 1px solid #b3d9f2;
padding: 2px;
transition: border-color 0.2s ease;
}
&:hover .app-icon {
border-color: #7cb9e8;
}
// 应用文字样式
.app-text {
font-size: 12px;
color: #333;
white-space: nowrap;
font-weight: 500;
text-align: center;
}
}
}
}
@media (max-width: 768px) {
.common-app-card .app-list {
gap: 6px;
.app-item {
padding: 4px 8px;
.app-icon {
width: 36px;
height: 24px;
}
.app-text {
font-size: 11px;
}
}
}
}
</style>

View File

@@ -133,7 +133,7 @@ export default defineComponent({
/* 列表滚动容器 - 完全紧贴边缘 */
.dynamic-list-container {
height: 30vh;
height: 33vh;
overflow-y: auto;
padding: 4px 0; /* 仅保留上下内边距左右为0 */
box-sizing: border-box;

View File

@@ -46,15 +46,12 @@
import { defineComponent, onMounted, ref } from 'vue';
import { Card } from 'ant-design-vue';
import { Icon } from '@jeesite/core/components/Icon';
import { useRouter } from 'vue-router';
import { BizProjectInfo, bizProjectInfoListAll } from '@jeesite/biz/api/biz/projectInfo';
export default defineComponent({
components: { Card, CardGrid: Card.Grid, Icon },
setup() {
const listData = ref<BizProjectInfo[]>([]);
const router = useRouter();
const getDataList = async () => {
try {
const params = { projectStatus: '2' };

View File

@@ -46,12 +46,35 @@
<span class="trigger-time">{{ item.triggerTime }}</span>
<!-- 预警类型固定宽度 -->
<span class="alert-type">{{ item.alertType }}</span>
<!-- 预警标题 -->
<span class="alert-title"> <a-button type="link" size="small" @click="openModal(true, item)">{{ item.alertTitle }}</a-button></span>
<!-- 预警标题 - 仅优化这部分 -->
<span
class="alert-title"
@mouseenter="showFullContent(item.alertTitle || '', $event)"
@mouseleave="hideFullContent"
>
<a-button type="link" size="small" @click="openModal(true, item)">
<span class="title-text">{{ item.alertTitle || '--' }}</span>
</a-button>
</span>
</div>
</div>
</div>
</Card>
<!-- 新增预警标题悬浮提示框 -->
<div
v-if="showContentTooltip"
class="custom-tooltip"
:style="{
top: `${tooltipTop}px`,
left: `${tooltipLeft}px`,
zIndex: 9999
}"
>
<pre class="tooltip-content">{{ fullContent }}</pre>
<div class="tooltip-arrow"></div>
</div>
<Modal @register="register" @modalClose="fetchAppList(currentStatus)" />
</template>
@@ -78,6 +101,35 @@ const statusOptions = ref([
]);
const currentStatus = ref<string>('0'); // 默认选中未处理
// 新增:预警标题悬浮提示相关
const showContentTooltip = ref(false);
const fullContent = ref('');
const tooltipTop = ref(0);
const tooltipLeft = ref(0);
// 新增:显示完整标题
const showFullContent = (content: string, event: MouseEvent) => {
// 内容为空或无截断时不显示提示
if (!content) return;
const target = event.target as HTMLElement;
// 判断内容是否被截断(容器宽度 < 内容宽度)
if (target.scrollWidth <= target.clientWidth) return;
// 直接赋值原始内容,不做任何格式化处理
fullContent.value = content;
// 定位提示框(鼠标位置偏移,避免遮挡鼠标)
tooltipTop.value = event.clientY + 10;
tooltipLeft.value = event.clientX + 10;
showContentTooltip.value = true;
};
// 新增:隐藏提示框
const hideFullContent = () => {
showContentTooltip.value = false;
fullContent.value = '';
};
// 处理状态切换(新增方法)
const handleStatusChange = (status: string) => {
if (currentStatus.value === status) return; // 避免重复请求
@@ -176,12 +228,12 @@ onMounted(() => {
/* 列表外层容器:移除边框 + 滚动核心样式 + 极致紧凑 */
.alert-list-container {
width: 100%;
height: 35vh; /* 限制最大高度,超出滚动 */
height: 32vh; /* 限制最大高度,超出滚动 */
overflow-y: auto; /* 垂直滚动 */
overflow-x: hidden; /* 隐藏横向滚动 */
padding: 0; /* 移除内边距 */
margin: 0; /* 移除外边距 */
background: transparent; /* 透明背景贴合Card */
background: transparent;
}
/* 空数据/加载提示 */
@@ -258,7 +310,7 @@ onMounted(() => {
text-overflow: ellipsis;
}
/* 预警标题(最后,自适应宽度,超长省略 */
/* 预警标题(仅修改这部分样式 */
.alert-title {
width: 155px; /* 固定宽度可根据需求调整比如200px/400px */
flex-shrink: 0; /* 关键:禁止宽度被压缩,保证固定宽度生效 */
@@ -267,6 +319,79 @@ onMounted(() => {
overflow: hidden; /* 隐藏超出宽度的内容 */
text-overflow: ellipsis; /* 超出显示省略号 */
margin-left: 4px; /* 保留与类型的微小间距 */
cursor: pointer;
}
/* 新增:标题文本样式 - 确保省略号生效 */
.title-text {
width: 100%;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
display: inline-block;
padding: 0 4px;
}
/* 新增:自定义提示框样式 */
.custom-tooltip {
position: fixed;
background: #1f2937;
color: #ffffff;
padding: 12px 16px;
border-radius: 8px;
max-width: 500px;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
font-size: 14px;
line-height: 1.6;
z-index: 9999;
animation: fadeIn 0.2s ease-in-out;
max-height: 400px;
overflow-y: auto;
}
.tooltip-content {
font-family: "Consolas", "Monaco", "Courier New", monospace;
white-space: pre-wrap;
word-wrap: break-word;
color: #e5e7eb;
margin: 0;
padding: 0;
}
.custom-tooltip::-webkit-scrollbar {
width: 4px;
}
.custom-tooltip::-webkit-scrollbar-track {
background: #2d3748;
border-radius: 2px;
}
.custom-tooltip::-webkit-scrollbar-thumb {
background: #4a5568;
border-radius: 2px;
}
.tooltip-arrow {
position: absolute;
top: -8px;
left: 10px;
width: 0;
height: 0;
border-left: 8px solid transparent;
border-right: 8px solid transparent;
border-bottom: 8px solid #1f2937;
}
@keyframes fadeIn {
from {
opacity: 0;
transform: translateY(5px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
/* 滚动条样式优化 */

View File

@@ -108,8 +108,6 @@ import { useMessage } from '@jeesite/core/hooks/web/useMessage';
import { BasicModal, useModalInner } from '@jeesite/core/components/Modal';
import { BizWarningAlert, bizWarningAlertSave } from '@jeesite/biz/api/biz/warningAlert';
export default defineComponent({
components: { BasicModal },
emits: ['modalClose'],

View File

@@ -4,18 +4,7 @@
<WorkbenchHeader class="workbench-header" />
</template>
<div class="workbench-container">
<!-- 常用应用卡片 - 独占一行 -->
<Card title="常用应用" class="common-app-card">
<div class="app-list">
<div class="app-item" v-for="(app, index) in appList" :key="index">
<a-button type="link" size="small" @click="goToMorePage(app)" class="app-btn">
<img :src="app.iconClass" class="app-icon" />
</a-button>
<span class="app-text">{{ app.systemName }}</span>
</div>
</div>
</Card>
<!-- 主内容区域 - 保持原有4个组件布局 -->
<BizApps />
<div class="content-wrapper">
<div class="workbench-main">
<ProjectCard :loading="loading" class="workbench-card" />
@@ -34,42 +23,17 @@
import { ref, onMounted } from 'vue';
import { Card } from 'ant-design-vue';
import { PageWrapper } from '@jeesite/core/components/Page';
import { useRouter } from 'vue-router';
import WorkbenchHeader from './components/WorkbenchHeader.vue';
import BizApps from './components/BizApps.vue';
import ProjectCard from './components/ProjectCard.vue';
import DynamicInfo from './components/DynamicInfo.vue';
import QuickLogin from './components/QuickLogin.vue';
import WarningAlert from './components/WarningAlert.vue';
import { BizQuickLogin, bizQuickLoginListAll } from '@jeesite/biz/api/biz/quickLogin';
const loading = ref(true);
const appList = ref<BizQuickLogin[]>([]);
const router = useRouter();
const goToMorePage = (app : BizQuickLogin) => {
router.push(app.homepageUrl);
};
const fetchAppList = async () => {
try {
const params = {
isEnabled: '1',
systemType: '1'
};
const result = await bizQuickLoginListAll(params);
appList.value = result || [];
} catch (error) {
console.error('获取应用列表失败:', error);
appList.value = [];
}
};
// 优化 loading 逻辑,避免闪屏
onMounted(() => {
const timer = setTimeout(() => {
fetchAppList();
loading.value = false;
clearTimeout(timer);
}, 800);
@@ -167,91 +131,4 @@ onMounted(() => {
flex-direction: column;
height: 100%;
}
// 常用应用Card样式 - 重点优化
.common-app-card {
width: 100%;
margin: 4px 0;
border-radius: 8px;
flex-shrink: 0; // 确保不被压缩
// 移除Card默认的内边距让内容更紧凑
:deep(.ant-card-body) {
padding: 8px 12px !important; // 减小卡片内部边距
}
// 应用列表容器 - 占满整行
.app-list {
display: flex;
flex-wrap: wrap; // 超出自动换行
gap: 8px; // 从12px改为8px
width: 100%;
padding: 4px 0; // 从8px改为4px
// 移除inline-flex改用flex确保布局稳定
.app-item {
display: flex;
flex-direction: column;
align-items: center;
gap: 4px;
padding: 6px 10px; // 从8px 16px改为6px 10px
flex: 0 0 auto; // 固定宽度,不拉伸
// 按钮重置
.app-btn {
padding: 0;
height: auto;
&:hover,
&:focus {
background: transparent;
}
}
// 应用图标样式
.app-icon {
width: 40px;
height: 28px;
object-fit: contain;
border-radius: 8px;
border: 1px solid #b3d9f2;
padding: 2px;
transition: border-color 0.2s ease;
}
&:hover .app-icon {
border-color: #7cb9e8;
}
// 应用文字样式
.app-text {
font-size: 12px;
color: #333;
white-space: nowrap;
font-weight: 500;
text-align: center;
}
}
}
}
// 响应式适配
@media (max-width: 768px) {
.common-app-card .app-list {
gap: 6px;
.app-item {
padding: 4px 8px;
.app-icon {
width: 36px;
height: 24px;
}
.app-text {
font-size: 11px;
}
}
}
}
</style>

View File

@@ -66,6 +66,12 @@
componentProps: {
maxlength: 8,
},
colProps: { md: 24, lg: 24 },
},
{
label: t('待办意见'),
field: 'extraDesc',
component: 'InputTextArea',
required: true,
colProps: { md: 24, lg: 24 },
},

View File

@@ -161,6 +161,14 @@
width: 225,
align: 'left',
},
{
title: t('待办意见'),
dataIndex: 'extraDesc',
key: 'a.extra_desc',
sorter: true,
width: 225,
align: 'left',
},
{
title: t('是否关闭'),
dataIndex: 'clickClose',
@@ -207,7 +215,7 @@
confirm: handleDelete.bind(this, record),
},
auth: 'biz:listItem:edit',
ifShow: record.clickClose == '9'
ifShow: record.titleDelete == '1'
},
],
};
@@ -248,7 +256,7 @@
url: ctxAdminPath + '/biz/listItem/exportData',
params: {
... getForm().getFieldsValue(),
createUser: userinfo.value.loginCode ,
loginUser: userinfo.value.loginCode ,
}
});
loading.value = false;