Browse Source

feat: "科研会议管理"

master
xuesinan 4 years ago
parent
commit
2e176f7137
  1. 16
      src/App.vue
  2. 4
      src/apis/index.js
  3. 54
      src/components/tall/center/ProjectDetail.vue
  4. 4
      src/components/tall/left/Projects.vue
  5. 100
      src/components/tall/task/MeetingManagement.vue
  6. 3
      src/components/tall/task/PlanAssignment.vue
  7. 102
      src/components/tall/task/TaskConList.vue
  8. 38
      src/components/tall/top/Navbar.vue
  9. 5
      src/store/tall/layout/mutations.js
  10. 1
      src/store/tall/layout/state.js
  11. 13
      src/store/tall/task/index.js
  12. 90
      src/views/detail/Test.vue

16
src/App.vue

@ -104,12 +104,20 @@ body,
background: #fff;
}
.font-14 {
font-size: 14px !important;
}
.color-3 {
color: #333;
color: #333 !important;
}
.color-6 {
color: #666;
color: #666 !important;
}
.color-c {
color: #cccccc !important;
}
.border-radius-10 {
@ -120,8 +128,4 @@ body,
display: block;
margin-bottom: 5px;
}
::-webkit-scrollbar {
width: 0 !important;
}
</style>

4
src/apis/index.js

@ -6,7 +6,7 @@ const apiUrl = import.meta.env.VITE_API_URL;
const users = `${apiUrl}/gateway/tall3/v3.0/users`;
const tall = `${apiUrl}/gateway/tall3/v3.0`;
const experiment = `${apiUrl}/gateway/experiment`;
const defaultwbs = `${apiUrl}/gateway/sports`;
const filedeal = `${apiUrl}/filedeal`;
// 根据userId 获取token
// eslint-disable-next-line import/prefer-default-export
@ -65,4 +65,4 @@ export const importWbs = async e => {
export const create = param => http.post(`${experiment}/experiment/create`, { params: { param } });
// 上传文件
export const uploadImg = `${defaultwbs}/file/upload`;
export const uploadImg = `${filedeal}/file/upload/multiple`;

54
src/components/tall/center/ProjectDetail.vue

@ -1,4 +1,5 @@
<template>
<!-- 项目标题 -->
<div class="navbar flex items-center justify-between">
<div class="project-name">{{ project.name }}</div>
<div class="project-action">
@ -7,6 +8,7 @@
</div>
</div>
<!-- 角色 -->
<div class="role-list flex items-center">
<div class="role-box relative" v-for="(item, index) in roles" :key="index">
<div class="role-name" :class="{ mine: item.mine === 1 && currRoleId === item.id }">{{ item.name }}</div>
@ -14,13 +16,18 @@
</div>
</div>
<!-- 日常任务 -->
<div class="global">
<div class="global-box" v-if="permanentObj.permanentList && permanentObj.permanentList.length > 0">
<div class="global-task cursor-pointer" v-for="(item, index) in permanentObj.permanentList" :key="index" @click="toDetail(item)">
{{ item.name }}
<template v-for="v in item.plugins">
<template v-if="v[0].pluginId == 1">{{ item.name }}</template>
</template>
</div>
</div>
</div>
<!-- 定期任务 -->
<div class="task-list" :style="{ height: 'calc(100vh - 160px - (' + globalHeight + 'px))' }">
<div class="task-box" v-for="(item, index) in taskObj.tasks" :key="index">
<div class="task-time flex items-center justify-between">
@ -34,7 +41,11 @@
<div class="task-info">
<div>
<div class="task-card">
<div class="task-name cursor-pointer" @click="toDetail(item)">{{ item.name }}</div>
<div class="task-name cursor-pointer" @click="toDetail(item)">
<template v-for="v in item.plugins">
<template v-if="v[0].pluginId == 1">{{ item.name }}</template>
</template>
</div>
<div class="task-con" v-if="item.sonList && item.sonList.length > 0">
<div v-for="(val, key) in item.sonList" :key="key">
@ -68,11 +79,24 @@ const roles = ref([
]);
const permanents = computed(() => store.state.role.permanents); //
const permanentObj = reactive({ permanentList: [] });
const permanentObj = reactive({ permanentList: [] }); //
const tasks = computed(() => store.state.role.tasks); //
const taskObj = reactive({ tasks: [] });
const taskObj = reactive({ tasks: [] }); //
const globalHeight = ref(0); //
init();
//
watch(project, async () => {
permanentObj.permanentList = permanents.value;
taskObj.tasks = tasks.value;
await getRoles(project.value.id); // id
await getPermanentData(currRoleId.value); //
await getTasks({ roleId: currRoleId.value });
});
// ID
async function init() {
try {
if (projectId) {
@ -89,18 +113,6 @@ async function init() {
}
}
init();
//
watch(project, async newVal => {
console.log('newVal', newVal);
permanentObj.permanentList = permanents.value;
taskObj.tasks = tasks.value;
await getRoles(project.value.id); // id
await getPermanentData(currRoleId.value); //
await getTasks({ roleId: currRoleId.value });
});
//
function setInitialRoleId(visibleList) {
if (!visibleList || !visibleList.length) return;
@ -191,7 +203,11 @@ async function getTasks(query) {
taskObj.tasks = data;
data.forEach(item => {
getSonTask(item.detailId);
item.plugins.forEach(val => {
if (Number(val[0].pluginId) === 6) {
getSonTask(item.detailId);
}
});
});
store.commit('task/setUpTasks', data);
@ -320,4 +336,8 @@ function toDetail(item) {
:deep(.ant-checkbox + span) {
color: #607d8b;
}
.task-list::-webkit-scrollbar {
width: 0 !important;
}
</style>

4
src/components/tall/left/Projects.vue

@ -165,6 +165,10 @@ async function deleteProject(param) {
overflow-y: auto;
}
.list-flex::-webkit-scrollbar {
width: 0 !important;
}
.ant-divider-horizontal {
height: 16px;
background: #eeeeee;

100
src/components/tall/task/MeetingManagement.vue

@ -20,20 +20,59 @@
<a-form-item>
<label class="color-3">会议通知</label>
<a-upload
v-model:file-list="fileList"
name="param"
<a-upload-dragger
v-model:fileList="fileList"
name="files"
:multiple="true"
:action="action"
:headers="headers"
:accept="'.pdf'"
@change="handleChange"
>
<p class="ant-upload-drag-icon">
<inbox-outlined></inbox-outlined>
</p>
<p class="ant-upload-text color-3 font-14">点击或拖拽文件到区域内上传交付物</p>
<p class="ant-upload-hint color-c">格式pdf</p>
</a-upload-dragger>
</a-form-item>
<a-form-item>
<label class="color-3">会议纪要</label>
<a-upload-dragger
v-model:fileList="fileList"
name="files"
:multiple="true"
:action="action"
:headers="headers"
:accept="'.pdf'"
@change="handleChange"
>
<a-button>
<upload-outlined></upload-outlined>
Click to Upload
</a-button>
</a-upload>
<p class="ant-upload-drag-icon">
<inbox-outlined></inbox-outlined>
</p>
<p class="ant-upload-text color-3 font-14">点击或拖拽文件到区域内上传交付物</p>
<p class="ant-upload-hint color-c">格式pdf</p>
</a-upload-dragger>
</a-form-item>
<a-form-item>
<label class="color-3">照片附件/其他</label>
<a-upload-dragger
v-model:fileList="fileList"
name="files"
:multiple="true"
:action="action"
:headers="headers"
:accept="'.pdf'"
@change="handleChange"
>
<p class="ant-upload-drag-icon">
<inbox-outlined></inbox-outlined>
</p>
<p class="ant-upload-text color-3 font-14">点击或拖拽文件到区域内上传交付物</p>
<p class="ant-upload-hint color-c">格式pdf</p>
</a-upload-dragger>
</a-form-item>
<a-form-item class="text-right">
@ -47,16 +86,15 @@
// import { computed, watch, ref } from 'vue';
import { ref, computed } from 'vue';
import { useStore } from 'vuex';
import { message } from 'ant-design-vue';
import { UploadOutlined } from '@ant-design/icons-vue';
// import { message } from 'ant-design-vue';
import { InboxOutlined } from '@ant-design/icons-vue';
import { uploadImg } from 'apis';
const store = useStore();
const formRef = ref(null);
const token = computed(() => store.getters['user/token']);
console.log('token', token.value);
// const token = sessionStorage.getItem('anyringToken');
const headers = { Authorization: `Bearer ${token}` };
const headers = { Authorization: `Bearer ${token.value}` };
const action = uploadImg;
const fileList = ref([]);
const topicMeetFormData = ref({
name: '',
@ -65,18 +103,34 @@ const topicMeetFormData = ref({
fileList: [],
});
const action = uploadImg;
const handleChange = info => {
if (info.file.status !== 'uploading') {
console.log(info.file, info.fileList);
}
const resFileList = [...info.fileList];
//
const arr = ref([]);
resFileList.forEach(file => {
let flag = false;
arr.value.forEach(item => {
if (file.name === item.name) {
flag = true;
}
});
if (!flag) {
arr.value.push(file);
}
});
//
arr.value = arr.value.map(file => {
if (file.response) {
file.url = file.response.data[0].visitUrl;
}
return file;
});
if (info.file.status === 'done') {
message.success(`${info.file.name} file uploaded successfully`);
} else if (info.file.status === 'error') {
message.error(`${info.file.name} file upload failed.`);
}
fileList.value = arr.value;
};
</script>

3
src/components/tall/task/PlanAssignment.vue

@ -0,0 +1,3 @@
<template>
<div class="">ssssssssss</div>
</template>

102
src/components/tall/task/TaskConList.vue

@ -0,0 +1,102 @@
<template>
<div class="list-box">
<div class="task-box">
<div class="task-time flex items-center justify-between">
<div class="flex items-center">
<div class="circular"></div>
<span>2020年08月16日 13:30</span>
</div>
<div class="task-action"></div>
</div>
<div class="task-info">
<div>
<div class="task-card">
<div class="task-name cursor-pointer flex justify-between items-center">
<span class="leading-none color-3">第一次科研需求分析会议</span>
<RightOutlined style="color: #666" />
</div>
</div>
</div>
</div>
</div>
<div class="task-box">
<div class="task-time flex items-center justify-between">
<div class="flex items-center">
<div class="circular"></div>
<span>2020年08月16日 13:30</span>
</div>
<div class="task-action"></div>
</div>
<div class="task-info">
<div>
<div class="task-card">
<div class="task-name cursor-pointer flex justify-between items-center">
<span class="leading-none color-3">第一次科研需求分析会议</span>
<RightOutlined style="color: #666" />
</div>
</div>
</div>
</div>
</div>
</div>
</template>
<script setup>
import { RightOutlined } from '@ant-design/icons-vue';
</script>
<style scoped>
.list-box {
padding: 10px 16px 50px;
overflow-y: auto;
}
.task-box {
}
.task-time {
height: 32px;
}
.task-time .circular {
margin-right: 8px;
width: 14px;
height: 14px;
border-radius: 50%;
background-color: #1890ff;
}
.task-time span {
font-size: 14px;
color: #595959;
}
.task-info {
margin: 8px 0;
padding-left: 8px;
}
.task-info > div {
padding-left: 15px;
border-left: 1px solid #d2d2d2;
}
.task-info .task-card {
padding: 16px;
border-radius: 8px;
-moz-box-shadow: 0px 3px 6px 0px rgba(0, 0, 0, 0.12);
-webkit-box-shadow: 0px 3px 6px 0px rgba(0, 0, 0, 0.12);
box-shadow: 0px 3px 6px 0px rgba(0, 0, 0, 0.12);
}
.task-con {
margin-top: 16px;
}
.task-con > div {
height: 30px;
}
</style>

38
src/components/tall/top/Navbar.vue

@ -1,26 +1,46 @@
<template>
<h1>{{ taskInfo.name }}</h1>
<div class="flex justify-between items-center">
<h1>{{ taskDetail.name }}</h1>
<div
class="flex items-center cursor-pointer"
:style="{ color: isShowList ? '#1890FF' : '' }"
v-if="label == 'KT_KYHY'"
@click="changeShowStatus"
>
<ClockCircleOutlined style="font-size: 18px" />
<span style="font-size: 16px; margin-left: 8px">历史记录</span>
</div>
</div>
</template>
<script setup>
import { computed, watch, ref } from 'vue';
import { computed } from 'vue';
import { useStore } from 'vuex';
// import dayjs from 'dayjs';
import { ClockCircleOutlined } from '@ant-design/icons-vue';
const store = useStore();
const taskDetail = computed(() => store.state.task.taskDetail); //
const taskInfo = ref({});
const sessionTaskDetail = sessionStorage.getItem('taskDetail');
const label = computed(() => store.state.task.label); // code
const sessionLabel = sessionStorage.getItem('label'); // code
const isShowList = computed(() => store.state.layout.isShowListStatus); //
if (sessionTaskDetail) {
taskInfo.value = JSON.parse(sessionTaskDetail);
const obj = JSON.parse(sessionTaskDetail);
store.commit('task/setTaskDetail', obj);
}
if (sessionLabel) {
store.commit('task/setLabel', sessionLabel);
}
//
watch(taskDetail, newVal => {
taskInfo.value = newVal;
store.commit('task/setTaskDetail', newVal);
});
// watch([taskDetail, label], () => {});
//
function changeShowStatus() {
store.commit('layout/setListStatus', !isShowList.value);
}
</script>
<style scoped>

5
src/store/tall/layout/mutations.js

@ -8,6 +8,11 @@ const mutations = {
setDisplay(state, { prop, show }) {
state.display[prop] = show;
},
// 设置是否显示列表
setListStatus(state, data) {
state.isShowListStatus = data;
},
};
export default mutations;

1
src/store/tall/layout/state.js

@ -3,6 +3,7 @@ const state = {
display: {
left: true, // 是否显示左栏
},
isShowListStatus: false, // 是否显示任务详情列表
};
export default state;

13
src/store/tall/task/index.js

@ -26,6 +26,7 @@ export default {
newProjectInfo: {},
showScrollTo: false, // 是否可以设置时间轴自动滚动的位置
taskDetail: {}, // 当前点击任务信息
label: '', // 任务code
},
getters: {
@ -299,6 +300,18 @@ export default {
sessionStorage.removeItem('taskDetail');
}
},
/**
* 设置当前任务的code
*/
setLabel(state, data) {
state.label = data;
if (data) {
sessionStorage.setItem('label', data);
} else {
sessionStorage.removeItem('label');
}
},
},
actions: {

90
src/views/detail/Test.vue

@ -1,13 +1,22 @@
<template>
<div class="task-detail">
<div class="task-con">
<div class="task-con flex">
<!-- {{ taskInfo.name }} -->
<div class="task-con-box">
<!-- 课题 -->
<!-- 查看课题进展 -->
<CheckSubjectProgress v-if="label === 'KT_KTJZ'" />
<!-- 查看课题进展 -->
<CheckSubjectProgress v-if="label === 'KT_KTJZ'" />
<!-- 科研会议管理 -->
<MeetingManagement v-if="label === 'KT_KYHY'" />
<!-- 科研会议管理 -->
<MeetingManagement v-if="label === 'KT_KYHY'" />
<!-- 计划任务书 -->
<PlanAssignment v-if="label === 'KT_JHRWS'" />
</div>
<div class="task-con-list" v-if="isShowList">
<TaskConList />
</div>
</div>
</div>
</template>
@ -15,38 +24,41 @@
<script setup>
import { computed, watch, ref } from 'vue';
import { useStore } from 'vuex';
import CheckSubjectProgress from 'components/tall/task/CheckSubjectProgress.vue';
import MeetingManagement from 'components/tall/task/MeetingManagement.vue';
import CheckSubjectProgress from 'components/tall/task/CheckSubjectProgress.vue'; //
import MeetingManagement from 'components/tall/task/MeetingManagement.vue'; //
import PlanAssignment from 'components/tall/task/PlanAssignment.vue'; //
import TaskConList from 'components/tall/task/TaskConList.vue'; //
const store = useStore();
const taskDetail = computed(() => store.state.task.taskDetail); //
// const taskObj = ref({});
const label = ref(null);
const sessionTaskDetail = sessionStorage.getItem('taskDetail');
const isShowList = computed(() => store.state.layout.isShowListStatus); //
if (sessionTaskDetail) {
// taskObj.value = JSON.parse(sessionTaskDetail);
const taskInfo = JSON.parse(sessionTaskDetail);
if (taskInfo.plugins && taskInfo.plugins.length > 0) {
taskInfo.plugins[0].forEach((item, index) => {
if (index === 0) {
label.value = item.param;
taskInfo.plugins.forEach(item => {
if (Number(item[0].pluginId) === 1) {
label.value = item[0].param;
store.commit('task/setLabel', label.value);
}
});
}
}
//
watch(taskDetail, () => {
// taskObj.value = taskDetail.value;
watch([taskDetail], () => {
if (!taskDetail.value) return;
const taskInfo = taskDetail.value;
if (taskInfo.plugins && taskInfo.plugins.length > 0) {
taskInfo.plugins[0].forEach((item, index) => {
if (index === 0) {
label.value = item.param;
taskInfo.plugins.forEach(item => {
if (Number(item[0].pluginId) === 1) {
label.value = item[0].param;
store.commit('task/setLabel', label.value);
}
});
}
@ -58,14 +70,18 @@ watch(taskDetail, () => {
width: 100%;
height: 100%;
padding: 16px;
overflow-y: auto;
overflow: auto;
}
.task-con {
width: 100%;
min-width: 760px;
min-height: 500px;
}
.task-con-box {
flex: 1;
}
.task-form {
padding: 24px;
display: flex;
@ -73,10 +89,18 @@ watch(taskDetail, () => {
}
.task-form :deep(.ant-form) {
width: 80%;
width: 100%;
max-width: 680px;
}
.task-con-list {
min-width: 320px;
max-width: 460px;
background: #fff;
border-radius: 10px;
box-shadow: -5px 0px 5px 0px rgba(3, 27, 49, 0.1);
}
:deep(.ant-input) {
height: 38px;
border: 1px solid #cccccc;
@ -100,4 +124,30 @@ watch(taskDetail, () => {
border: 1px solid #cccccc;
border-radius: 4px;
}
:deep(.ant-upload) {
width: 100%;
}
/* 上传样式 */
:deep(.ant-upload) .upload-box {
padding: 20px 0;
border: 1px dashed #cccccc;
background: #fafafa;
border-radius: 4px;
}
:deep(.ant-upload) .upload-box img {
width: 32px;
}
:deep(.ant-upload) .upload-box p:first-of-type {
margin-top: 24px;
line-height: 1;
}
:deep(.ant-upload) .upload-box p:last-of-type {
margin-top: 16px;
line-height: 1;
}
</style>

Loading…
Cancel
Save