This commit is contained in:
2026-01-06 17:02:48 +08:00
13 changed files with 566 additions and 44 deletions

View File

@@ -2,11 +2,11 @@
<el-table-column v-for="(column, index) in props.columns" :key="index" v-bind="{...$attrs, ...margeOptions(column)}">
<template #header="scope">
<!-- 你可以在这里使用 column.renderHeader 来指定自定义的 header 插槽 -->
<component v-if="column.renderHeader" :is="column.renderHeader" v-bind="scope"></component>
<component :is="column.renderHeader" v-if="column.renderHeader" v-bind="scope"></component>
</template>
<template #default="scope">
<!-- 使用 column.renderDefault 作为动态组件并传入 scope 对象 -->
<component v-if="column.renderDefault" :is="column.renderDefault" v-bind="scope"></component>
<component :is="column.renderDefault" v-if="column.renderDefault" v-bind="scope"></component>
<TableRenderColumns v-if="column.children && column.children.length" v-bind="$attrs" :columns="column.children" />
</template>
</el-table-column>

View File

@@ -2286,5 +2286,9 @@
"spdm.message.task": "Task",
"spdm.message.data": "Data",
"spdm.message.work": "Work",
"spdm.message.approve": "Approve"
"spdm.message.approve": "Approve",
"spdm.taskMessage.title": "Task Message",
"spdm.taskMessage.des": "Task Message List",
"spdm.taskList.title": "Task List",
"spdm.taskList.des": "List of tasks I perform"
}

View File

@@ -2286,5 +2286,9 @@
"spdm.message.task": "任务",
"spdm.message.data": "数据",
"spdm.message.work": "作业",
"spdm.message.approve": "审批"
"spdm.message.approve": "审批",
"spdm.taskMessage.title": "任务消息",
"spdm.taskMessage.des": "任务消息列表",
"spdm.taskList.title": "任务列表",
"spdm.taskList.des": "我执行的任务列表_spdm"
}

11
src/spdm/api/spdm-task.ts Normal file
View File

@@ -0,0 +1,11 @@
import { post } from './request';
/**
*
* @param type: 0 我执行的 1我关注的 2所有任务 3我负责的
* @param sortOrder 0-按创建时间正序1-倒序
* @returns
*/
export const queryTaskListApi = (params: any) => {
return post(`/api/simulation/project/task/list`, params);
};

View File

@@ -0,0 +1,231 @@
<script lang="ts">
export default {
title: 'spdm.taskList.title', // '任务消息'
icon: 'Star',
name: 'TaskList',
description: 'spdm.taskList.des', // '任务消息列表'
};
</script>
<template>
<el-card class="h-[580px] box-card">
<template #header>
<div class="card-header">
<span>{{ t('spdm.taskList.title') }}</span>
<el-button link class="button" text @click="goMoreFun">{{ t('home.moreTip') }}</el-button>
</div>
</template>
<div v-if="taskList.length">
<div class="table-wrapper">
<TableRender
style="width: 100%"
row-key="id"
border
:data="taskList"
:columns="tableColumns">
</TableRender>
</div>
<el-pagination layout="prev, pager, next" :total="100" @current-change="currentPageChangeFun"/>
</div>
<el-empty v-else class="empty" :image-size="48" :description="'暂无任务'" />
</el-card>
</template>
<script setup lang="ts" name="TaskMessageDashboard">
// import {getMessageUserPage} from '/@/api/message';
import { ref, onMounted } from 'vue';
import { useI18n } from 'vue-i18n';
import TableRender from '/@/components/TableRender/index.vue';
const router = useRouter();
const { t } = useI18n();
const taskList1 = ref([]);
const taskList = ref<any[]>([
{
"id": 1,
"taskName": "搭建用户登录模块数据库表结构",
"taskStatus": "进行中",
"taskRisk": "低(技术方案已确认,无明确风险)",
"taskProgress": "70%(已完成核心字段设计,待优化索引)",
"beginTime": "2025-10-28",
"endTime": "2025-11-01",
"project": "XX用户管理系统开发",
"phase": "开发阶段",
"person": "张三"
},
{
"id": 2,
"taskName": "设计双11促销活动海报",
"taskStatus": "待开始",
"taskRisk": "中(需与运营确认活动主题,可能影响设计进度)",
"taskProgress": "0%",
"beginTime": "2025-11-02",
"endTime": "2025-11-05",
"project": "2025双11电商促销项目",
"phase": "策划设计阶段",
"person": "张三"
},
{
"id": 3,
"taskName": "测试APP订单支付异常场景",
"taskStatus": "已完成",
"taskRisk": "低(测试用例覆盖全面)",
"taskProgress": "100%发现2个bug已修复",
"beginTime": "2025-10-25",
"endTime": "2025-10-27",
"project": "XX电商APP V3.5迭代",
"phase": "测试阶段",
"person": "张三"
},
{
"id": 4,
"taskName": "整理Q3用户运营数据报告",
"taskStatus": "进行中",
"taskRisk": "低(数据已收集完毕,仅需汇总分析)",
"taskProgress": "80%(已完成数据清洗,待生成可视化图表)",
"beginTime": "2025-10-30",
"endTime": "2025-11-03",
"project": "Q3用户运营复盘项目",
"phase": "数据复盘阶段",
"person": "张三"
},
{
"id": 5,
"taskName": "开发商品分类筛选功能前端页面",
"taskStatus": "已暂停",
"taskRisk": "高UI设计稿临时变更需重新调整布局",
"taskProgress": "40%(已完成基础框架,待对接新设计资源)",
"beginTime": "2025-10-26",
"endTime": "2025-11-01",
"project": "XX电商小程序优化",
"phase": "开发阶段",
"person": "张三"
},
{
"id": 6,
"taskName": "撰写新品上市推广软文",
"taskStatus": "待开始",
"taskRisk": "中(需等待产品部提供核心卖点,可能延期)",
"taskProgress": "0%",
"beginTime": "2025-11-04",
"endTime": "2025-11-06",
"project": "XX新品上市推广项目",
"phase": "内容准备阶段",
"person": "张三"
},
{
"id": 7,
"taskName": "修复用户反馈的APP闪退问题",
"taskStatus": "进行中",
"taskRisk": "低(已定位问题原因,正在编写修复代码)",
"taskProgress": "60%(修复方案已确定,待测试验证)",
"beginTime": "2025-10-29",
"endTime": "2025-11-02",
"project": "XXAPP紧急bug修复项目",
"phase": "紧急修复阶段",
"person": "张三"
},
{
"id": 8,
"taskName": "统计双11预热期用户点击量",
"taskStatus": "已完成",
"taskRisk": "低(数据统计工具正常,无异常)",
"taskProgress": "100%(数据已同步给运营团队)",
"beginTime": "2025-10-24",
"endTime": "2025-10-26",
"project": "2025双11电商促销项目",
"phase": "预热数据监控阶段",
"person": "张三"
},
{
"id": 9,
"taskName": "设计用户个人中心新版界面",
"taskStatus": "进行中",
"taskRisk": "中(用户调研反馈未完全收集,可能需调整设计)",
"taskProgress": "50%(已完成初稿设计,待收集反馈)",
"beginTime": "2025-10-31",
"endTime": "2025-11-04",
"project": "XX用户管理系统开发",
"phase": "UI设计阶段",
"person": "张三"
},
{
"id": 10,
"taskName": "编写APP版本更新说明文档",
"taskStatus": "待开始",
"taskRisk": "低(功能变更清单已确认,无不确定因素)",
"taskProgress": "0%",
"beginTime": "2025-11-05",
"endTime": "2025-11-07",
"project": "XX电商APP V3.5迭代",
"phase": "上线准备阶段",
"person": "张三"
},
]);
const tableColumns = ref([
{
// t('hooks.requirement.09115201-21')
label: '任务名称',
prop: 'taskName',
width: 160,
},
{
label: '状态',
prop: 'taskStatus',
width: 100,
},
{
label: '负责人',
prop: 'person',
width: 100,
},
{
label: '计划完成时间',
prop: 'endTime',
width: 140,
},
])
const currentPageChangeFun = (val: number) => {
console.log(val, '------------------');
}
// 更多按钮
const goMoreFun = () => {
router.push('spdm/task/execute'); // 跳转到我执行的页面
}
onMounted(() => {
// const params = {
// size: 10,
// current: 1,
// msgCategory: 99,
// msgTitle: '任务通知'
// }
// getMessageUserPage(params).then((res) => {
// console.log(res, '------------------------');
// taskMessageList.value = res.data.records;
// });
})
</script>
<style scoped lang="scss">
.card-header {
display: flex;
justify-content: space-between;
align-items: center;
.button {
// 鼠标hover 文字变成蓝色
&:hover {
color: #409eff;
}
}
}
.table-wrapper {
height: 445px;
width: 100%;
overflow: hidden;
}
.empty {
margin-top: 100px;
}
</style>

View File

@@ -0,0 +1,102 @@
<script lang="ts">
export default {
title: 'spdm.taskMessage.title', // '任务消息'
icon: 'Star',
name: 'TaskMessage',
description: 'spdm.taskMessage.des', // '任务消息列表'
};
</script>
<template>
<el-card class="h-[580px] box-card">
<template #header>
<div class="card-header">
<span>{{ t('spdm.taskMessage.title') }}</span>
<el-button link class="button" text @click="goMoreFun">{{ t('home.moreTip') }}</el-button>
</div>
</template>
<div v-if="taskMessageList.length">
<div class="table-wrapper">
<TableRender
style="width: 100%"
row-key="id"
border
:data="taskMessageList"
:columns="tableColumns">
</TableRender>
</div>
<el-pagination layout="prev, pager, next" :total="1" @current-change="currentPageChangeFun"/>
</div>
<el-empty v-else class="empty" :image-size="48" :description="'暂无任务消息'" />
</el-card>
</template>
<script setup lang="ts" name="TaskMessageDashboard">
import {getMessageUserPage} from '/@/api/message';
import { ref, onMounted } from 'vue';
import { useI18n } from 'vue-i18n';
const router = useRouter();
const { t } = useI18n();
const taskMessageList = ref([]);
const tableColumns = ref([
{
label: '消息内容',
prop: 'msgBody',
width: 200,
},
{
label: '创建时间',
prop: 'receiveTime',
width: 170,
},
{
label: '创建人',
prop: 'username',
width: 120,
},
])
// 更多按钮
const goMoreFun = () => {
router.push('/message/records?type=task'); // 跳转到消息记录页面 todo 带参数跳转
// router.push({name: 'moduleRoutes.messageRecords', params: {type: 'task'}})
}
const currentPageChangeFun = (val: number) => {
console.log(val, '------------------');
}
onMounted(() => {
const params = {
size: 10,
current: 1,
msgCategory: 99,
msgTitle: '任务通知'
}
getMessageUserPage(params).then((res) => {
// console.log(res, '------------------------');
taskMessageList.value = res.data.records;
});
})
</script>
<style scoped lang="scss">
.card-header {
display: flex;
justify-content: space-between;
align-items: center;
.button {
// 鼠标hover 文字变成蓝色
&:hover {
color: #409eff;
}
}
}
.table-wrapper {
height: 445px;
width: 100%;
overflow: hidden;
}
.empty {
margin-top: 100px;
}
</style>

View File

@@ -0,0 +1,107 @@
<script lang="ts" setup name="TopNav">
import { color } from 'echarts';
import {getTaskCountApi} from '/@/api/admin/tenant';
import {queryTaskListApi} from '/@/spdm/api/spdm-task';
import {getTenantAssignmentsListByUsername} from '/@/api/admin/user';
import {useUserInfo} from '/@/stores/userInfo';
const {t} = useI18n();
const {userInfos, userId} = useUserInfo();
const tenantInfo = ref<any>(null);
const taskCountMap = ref<Record<string, number>>({});
const list = computed(() => [
{
label:'我关注的',
prop: 'createCount',
color: 'rgb(221, 221, 9)',
path: '/spdm/task/attention',
},
{
label: '今明任务',
prop: 'backlogCount',
color: '#FF9900',
path: '/spdm/task/execute',
},
{
label: '进行中任务',
prop: 'finishCount',
color: '#1890ff',
path: '/spdm/task/execute',
},
{
label: '逾期任务',
prop: 'allCount',
color: '#FF0000',
path: '/spdm/task/execute',
},
]);
const getTenantList = () => {
return getTenantAssignmentsListByUsername(userId).then((response) => {
// 从后端接口获取租户列表
tenantInfo.value = response.data?.[0];
});
};
const getTaskCount = async () => {
const res = await getTaskCountApi();
taskCountMap.value = res.data;
taskCountMap.value.allCount = Number(taskCountMap.value.backlogCount) + Number(taskCountMap.value.createCount) + Number(taskCountMap.value.finishCount)
console.log(taskCountMap.value,'taskCountMap.value');
};
const getFocusCount = () => {
const params = {
current: 1,
size: 10,
sortOrder: 1,
type: 1
}
// queryTaskListApi(params).then((res) => {})
}
onActivated(() => {
getTenantList();
getTaskCount();
getFocusCount();
});
watchEffect(() => {
getTenantList();
getTaskCount();
getFocusCount();
});
const router = useRouter();
const handleClick = (path: string) => {
router.push(path);
};
</script>
<template>
<div class="grid grid-cols-3 gap-4 mb-4 bg-white el-card is-always-shadow py-2 px-4 h-24 items-center">
<div class="col-span-1 pl-4 flex items-center">
<ImageAvatar class="!h-[60px] !w-[60px]" :src="userInfos.user.avatar" />
<div class="flex flex-col h-[40px] justify-between ml-6">
<div class="text-sm text-[#000000A6]">
<span>{{ tenantInfo?.deptNameList?.[0] }}</span>
<span></span>
<span>{{ tenantInfo?.postNameList?.[0] }}</span>
</div>
<div class="font-bold text-lg">{{ userInfos.user.nickname }}</div>
</div>
</div>
<div class="col-span-2 grid grid-cols-4 h-[40px] justify-between">
<div v-for="item in list" :key="item.prop" class="flex flex-col justify-between cursor-pointer" @click="handleClick(item.path)">
<div class="text-sm text-[#000000A6]">{{ item.label }}</div>
<div class="text-xl" :style="{ color: item.color }" >{{
taskCountMap[item.prop] ?? 0 }}</div>
</div>
</div>
</div>
</template>

View File

@@ -8,35 +8,56 @@ import { isObjectValueEqual } from '/@/utils/arrayOperation';
import mittBus from '/@/utils/mitt';
import {cloneDeep} from 'lodash';
// SPDM CODE 默认常用功能
const defaultFavoriteRoutes = [
{
contextMenuClickId: 5,
path: '/mold/list',
name: 'moduleRoutes.moldList',
meta: { icon: 'iconfont icon-yunxiazai_o', code: 'mold_management_view' },
query: {},
url: '/mold/list',
commonUrl: '',
path: '/spdm/competenceCenter/condition',
name: '仿真工况库',
meta: { icon: 'ele-Suitcase' },
url: '/spdm/competenceCenter/condition',
},
{
{
contextMenuClickId: 5,
path: '/project/list',
name: 'moduleRoutes.projectList',
meta: { icon: 'iconfont icon-diannao1', code: 'project_view' },
query: {},
url: '/project/list',
commonUrl: '',
path: '/spdm/competenceCenter/indicator',
name: '仿真指标库',
meta: { icon: 'ele-Money' },
url: '/spdm/competenceCenter/indicator',
},
{
{
contextMenuClickId: 5,
path: '/task/pending',
name: 'moduleRoutes.taskPending',
meta: { icon: 'fa fa-flag-checkered', code: 'task_view' },
query: {},
url: '/task/pending',
transUrl: '',
commonUrl: '',
path: '/spdm/simulation/process',
name: '仿真流程',
meta: { icon: 'ele-FolderChecked' },
url: '/spdm/simulation/process',
},
{
contextMenuClickId: 5,
path: '/spdm/simulation/application',
name: '应用中心',
meta: { icon: 'ele-Cellphone' },
url: '/spdm/simulation/application',
},
// {
// contextMenuClickId: 5,
// path: '/project/list',
// name: 'moduleRoutes.projectList',
// meta: { icon: 'iconfont icon-diannao1', code: 'project_view' },
// query: {},
// url: '/project/list',
// commonUrl: '',
// },
// {
// contextMenuClickId: 5,
// path: '/task/pending',
// name: 'moduleRoutes.taskPending',
// meta: { icon: 'fa fa-flag-checkered', code: 'task_view' },
// query: {},
// url: '/task/pending',
// transUrl: '',
// commonUrl: '',
// },
]
/**

View File

@@ -1,17 +1,17 @@
<template>
<el-card class="h-48 box-card">
<el-card class="h-[400px] box-card">
<template #header>
<div class="card-header">
<span>{{ props.title }}</span>
</div>
</template>
<el-row :gutter="10" v-if="showRoutes.length > 0">
<el-col class="shortcutCard" :xs="12" :sm="8" :md="8" :xl="6" :key="shortcut.id" v-for="shortcut in showRoutes">
<el-row v-if="showRoutes.length > 0" :gutter="10">
<el-col v-for="shortcut in showRoutes" :key="shortcut.id" class="shortcutCard" :xs="12" :sm="8" :md="8" :xl="6">
<SvgIcon name="ele-Close" :size="12" class="shortcutCardClose" @click="handleCloseFavorite(shortcut)" />
<shortcutCard :icon="shortcut.meta?.icon" :label="shortcut.name" @click="handleRoute(shortcut.path)" />
</el-col>
</el-row>
<el-empty :image-size="48" :description="props.emptyDescription" v-else />
<el-empty v-else :image-size="48" :description="props.emptyDescription" />
</el-card>
</template>
@@ -62,7 +62,7 @@ const handleCloseFavorite = (item: any) => {
const showRoutes = computed(() => {
if (props.type === 'flow') {
console.log(favoriteRoutes.value);
// console.log(favoriteRoutes.value, '---------favoriteRoutes------------');
return favoriteRoutes.value.filter((item) => item.path.includes('/flow/initiate?flowId'));
} else {
return favoriteRoutes.value.filter((item) => !item.path.includes('/flow/initiate?flowId'));

View File

@@ -9,21 +9,21 @@ export default {
<template>
<el-card class="h-[400px] box-card">
<Calendar
ref="calendar"
view="weekly"
:locale="locale"
:attributes="reminders"
ref="calendar"
title-position="center"
@did-move="weeknumberClick"
@dayclick="dayClick"
:masks="masks"
transparent
borderless
expanded
@did-move="weeknumberClick"
@dayclick="dayClick"
/>
<div v-if="calendar" class="py-4 px-6 w-full h-[18rem] overflow-y-auto">
<template v-for="{day, cells} in Object.values(dayCells)">
<ul v-if="cells.length > 0" class="py-2 space-y-2" :key="day">
<ul v-if="cells.length > 0" :key="day" class="py-2 space-y-2">
<li v-for="cell in cells" :key="cell">
<div class="flex items-center space-x-4">
<!--Icon-->
@@ -48,7 +48,7 @@ export default {
</li>
</ul>
</template>
<el-empty :image-size="120" v-if="reminders.length === 0" class="text-center" />
<el-empty v-if="reminders.length === 0" :image-size="120" class="text-center" />
</div>
<!-- 新增日程的表单 -->
<schedule-form ref="scheduleFormRef" @refresh="initscheduleList()" />
@@ -108,6 +108,7 @@ const weeknumberClick = (page: any) => {
//处理日程安排事件,若当前日期下没有日程则打开表单对话框,否则打开日程详情页面
const dayClick = (day: any) => {
// SPDM CODE 不需要新增待办,注释掉
if (filterCellSelected(day.id)) {
scheduleRef.value.open({date: parseDate(day.id, null)});
} else {

View File

@@ -1,15 +1,23 @@
const resultComps = {};
const files = import.meta.glob('./*.vue', { import: 'default', eager: true });
const spdmFles = import.meta.glob('../../../../spdm/views/home/components/*.vue', { import: 'default', eager: true }); // spdm卡片
const excludes = ['docking-zw-3d', 'docking-tianyu', 'docking-intesim-cae', 'demo-chart1', 'demo-chart2', 'current-user', 'flow-data'];
Object.keys(files).forEach((fileName) => {
// SPDM CODE
Object.keys({...files, ...spdmFles}).forEach((fileName) => {
if (fileName) {
const key = fileName.replace(/^\.\/(.*)\.\w+$/, '$1');
if (excludes.includes(key)) return;
// @ts-ignore
resultComps[key] = files[fileName];
if (key.includes('spdm')) {
const spdm_key = fileName.split('/').pop()?.replace(/\.\w+$/, '');
// @ts-ignore
resultComps[spdm_key] = spdmFles[fileName];
} else {
// @ts-ignore
resultComps[key] = files[fileName];
}
}
});
console.log(resultComps, 'resultComps');

View File

@@ -4,7 +4,8 @@
<template>
<div ref="main" :class="['widgets-home', customizing ? 'customizing' : '']">
<div class="widgets-content">
<TopNav />
<!-- <TopNav /> -->
<TopNavSpdm />
<div class="widgets-top">
<div class="flex justify-end custom_btn">
<el-button v-if="customizing" type="primary" round @click="save">{{ $t('widgets.index.0910663-0') }}</el-button>
@@ -113,16 +114,20 @@
<script lang="ts" name="widgets" setup>
const TopNav = defineAsyncComponent(() => import('./components/TopNav.vue'));
// SPDM CODE
const TopNavSpdm = defineAsyncComponent(() => import('../../../spdm/views/home/components/TopNavSpdm.vue'));
import draggable from 'vuedraggable';
import allComps from './components/index';
import {Local} from '/@/utils/storage';
import {useUserInfo} from '/@/stores/userInfo';
import {cloneDeep, flatten} from 'lodash';
// 默认布局设置
// todo 默认布局设置
const defaultGrid = ref({
layout: [8, 8, 8],
copmsList: [['news'], ['calendar'], ['favorite-menu', 'favorite-flow']],
// SPDM CODE
copmsList: [['calendar', 'favorite-menu'], ['TaskMessage'], ['TaskList']],
});
const customizing = ref(false);
const widgets = ref();
@@ -145,7 +150,7 @@ const allComponentList = computed(() => {
});
const myCompsList = computed(() => {
// 支持列表
// 支持列表 SPDM CODE
const myGrid = [
'docking-tianyu',
'docking-zw-3d',
@@ -164,11 +169,19 @@ const myCompsList = computed(() => {
'task-info',
'task-list',
'task-compare-chart',
'task-trend-chart'
'task-trend-chart',
'TaskMessage',
'TaskList'
];
return allComponentList.value.filter((item) => !item.disabled && myGrid.includes(item.key));
});
// setTimeout(() => {
// console.log('-----------myCompsList>>>>>>>>>', myCompsList.value);
// console.log('-----------allComponentList>>>>>>>>>', allComponentList.value);
// console.log('allComps>>>>>>', allComps) // 对象形式
// }, 5000)
const nowComponentList = computed(() => flatten(grid.value.copmsList));
const custom = () => {

View File

@@ -28,16 +28,36 @@
import {getMessageUserPage, editMessageUserIsRead, isReadAll, MessageUserVo, taskAdjust, MessageRead} from '/@/api/message';
import {useMsg} from '/@/stores/msg';
import RecordItem from './components/RecordItem.vue';
import { useRoute } from 'vue-router';
const activeName = ref<string>('');
// SPDM CODE 封装一个方法,从当前地址栏取出某个参数的值
const getQueryVariable = (variable: string) => {
const query = window.location.search.substring(1);
const vars = query.split("&");
for (let i = 0; i < vars.length; i++) {
const pair = vars[i].split("=");
if (pair[0] === variable) {
return pair[1];
}
}
}
const messageList = ref<MessageUserVo[]>([]);
const current = ref<number>(1);
const pageSize = ref<number>(10);
const isEnd = ref<boolean>(false);
const spdmMsgType = ref<boolean>(false); // SPDM CODE
const route = useRoute();
onMounted(() => {
// SPDM CODE
// const type = route.params.type;
const type = getQueryVariable('type');
if (type ==='task') {
activeName.value = '任务通知';
spdmMsgType.value = true
}
getUserMessage();
});