Browse Source

feat: tall PC端项目列表、项目详情、角色列表

develop
xuesinan 3 years ago
parent
commit
966361de11
  1. 219
      .drone.yml
  2. 6
      .env.development
  3. 6
      .env.production
  4. 6
      .env.test
  5. 2
      index.html
  6. 14
      package-lock.json
  7. 1
      package.json
  8. 62
      src/App.vue
  9. 116
      src/apis/index.js
  10. 89
      src/components/tall/center/Global.vue
  11. 233
      src/components/tall/center/Index.vue
  12. 92
      src/components/tall/center/RegularTask.vue
  13. 18
      src/components/tall/center/Roles.vue
  14. 13
      src/components/tall/left/Index.vue
  15. 204
      src/components/tall/left/Projects.vue
  16. 43
      src/components/tall/plugin/Plugin.vue
  17. 210
      src/components/tall/task/AssignmentExperiment.vue
  18. 453
      src/components/tall/task/AssignmentSubject.vue
  19. 192
      src/components/tall/task/CheckSubjectProgress.vue
  20. 246
      src/components/tall/task/Conclusion.vue
  21. 235
      src/components/tall/task/ContractManagement.vue
  22. 172
      src/components/tall/task/DataUnlock.vue
  23. 287
      src/components/tall/task/ExperimentalCode.vue
  24. 287
      src/components/tall/task/ExperimentalData.vue
  25. 277
      src/components/tall/task/ExperimentalResult.vue
  26. 246
      src/components/tall/task/InterimInspection.vue
  27. 274
      src/components/tall/task/LabReport.vue
  28. 292
      src/components/tall/task/MeetingManagement.vue
  29. 258
      src/components/tall/task/MemberManagement.vue
  30. 389
      src/components/tall/task/PlanAssignment.vue
  31. 271
      src/components/tall/task/Procedure.vue
  32. 308
      src/components/tall/task/PublishPatent.vue
  33. 251
      src/components/tall/task/PublishThesis.vue
  34. 251
      src/components/tall/task/PublishWork.vue
  35. 236
      src/components/tall/task/Result.vue
  36. 284
      src/components/tall/task/ScientificPayoffs.vue
  37. 246
      src/components/tall/task/SubConclusion.vue
  38. 246
      src/components/tall/task/SubInterimInspection.vue
  39. 244
      src/components/tall/task/SubMeetingManagement.vue
  40. 0
      src/components/tall/task/SubMemberManagement.vue
  41. 246
      src/components/tall/task/SubResult.vue
  42. 195
      src/components/tall/task/SubSubjectProgress.vue
  43. 144
      src/components/tall/task/TaskConList.vue
  44. 3
      src/components/tall/top/Navbar.vue
  45. 12
      src/components/tall/top/TopNavbar.vue
  46. 12
      src/plugins/p-task-title.vue
  47. 12
      src/routers/index.js
  48. 5
      src/store/tall/layout/mutations.js
  49. 1
      src/store/tall/layout/state.js
  50. 11
      src/store/tall/role/getters.js
  51. 11
      src/store/tall/role/mutations.js
  52. 1
      src/store/tall/role/state.js
  53. 218
      src/store/tall/task/index.js
  54. 26
      src/store/tall/user/index.js
  55. 46
      src/utils/axios.js
  56. 186
      src/views/detail/Test.vue
  57. 67
      src/views/home/Index.vue
  58. 180
      src/views/user/SignIn.vue
  59. 8
      vite.config.js
  60. 5
      yarn.lock

219
.drone.yml

@ -1,219 +0,0 @@
---
kind: pipeline
type: docker
name: development
# 常量值
constants:
- &DEVELOPMENT_HOST test.tall.wiki
- &DEVELOPMENT_CMD
- npm config set registry http://registry.npm.taobao.org
- npm i
- npm run build
- &DEVELOPMENT_SCP_TARGET /home/experiment
- &DEVELOPMENT_URL https://test.tall.wiki/experiment/
- &DEVELOPMENT_PORT 22
- &DEVELOPMENT_NODE_VERSION node:16
- &DEVELOPMENT_BRANCH develop
- &DEVELOPMENT_SCP_SOURCE dist/*
- &SCP_STRIP_DIR_LEVEL 1
- &NOTIFY_WECHATROBOT_WEBHOOK https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key=428e5c5d-f992-4349-939d-9c99556e50b8
# 挂载的主机卷,可以映射到docker容器中
volumes:
# maven构建缓存(宿主机目录)
- name: ssh_key
host:
path: /root/.ssh/
- name: cache
host:
path: /var/lib/cache
- name: data
host:
path: /var/lib/data
steps:
- name: restore-cache
image: drillster/drone-volume-cache
volumes:
- name: cache
path: /cache
settings:
restore: true
mount:
- ./node_modules
- name: build
image: *DEVELOPMENT_NODE_VERSION
pull: if-not-exists # default always
# volumes:
# - name: cache
# path: /root/.m2
commands: *DEVELOPMENT_CMD
- name: rebuild-cache
image: drillster/drone-volume-cache
volumes:
- name: cache
path: /cache
settings:
rebuild: true
mount:
- ./node_modules
- name: deploy-scp
image: appleboy/drone-scp
pull: if-not-exists
volumes:
- name: ssh_key
path: /root/.ssh/
settings:
host: *DEVELOPMENT_HOST
port: *DEVELOPMENT_PORT
username: root
key_path: /root/.ssh/id_rsa
rm: true # true则会删除目标目录重建
target: *DEVELOPMENT_SCP_TARGET
source: *DEVELOPMENT_SCP_SOURCE
strip_components: 1 # 去除的目录层数,如果没有该选项,则拷贝过去是 target/xxx.jar,1代表去除target
- name: notify-wechatwork
image: fifsky/drone-wechat-work
pull: if-not-exists
settings:
url: *NOTIFY_WECHATROBOT_WEBHOOK
msgtype: markdown
content: |
{{if eq .Status "success" }}
#### 🎉 ${DRONE_REPO} 测试环境构建成功
> Branch: ${DRONE_BRANCH}
> Commit: [${DRONE_COMMIT_MESSAGE} ](${DRONE_COMMIT_LINK})
> Author: ${DRONE_COMMIT_AUTHOR}
> PATH: https://test.tall.wiki/experiment/
> [点击查看](https://test.tall.wiki/experiment/)
{{else}}
#### ❌ ${DRONE_REPO} 测试环境构建失败
> Branch: ${DRONE_BRANCH}
> Commit: [${DRONE_COMMIT_MESSAGE} ](${DRONE_COMMIT_LINK})
> Author: ${DRONE_COMMIT_AUTHOR}
> 请立即修复!!!
> [点击查看](https://test.tall.wiki/experiment/)
{{end}}
when:
status:
- failure
- success
trigger:
branch:
- *DEVELOPMENT_BRANCH
---
kind: pipeline
type: docker
name: production
# 常量值
constants:
- &PRODUCTION_HOST www.tall.wiki
- &PRODUCTION_CMD
- npm config set registry http://registry.npm.taobao.org
- npm i
- npm run build:prod
- &PRODUCTION_SCP_TARGET /home/experiment
- &PRODUCTION_BRANCH master
- &PRODUCTION_PORT 22
- &PRODUCTION_NODE_VERSION node:16
- &PRODUCTION_SCP_SOURCE dist/*
- &SCP_STRIP_DIR_LEVEL 1
- &NOTIFY_WECHATROBOT_WEBHOOK https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key=428e5c5d-f992-4349-939d-9c99556e50b8
# 挂载的主机卷,可以映射到docker容器中
volumes:
# maven构建缓存(宿主机目录)
- name: ssh_key
host:
path: /root/.ssh/
- name: cache
host:
path: /var/lib/cache
- name: data
host:
path: /var/lib/data
steps:
- name: restore-cache
image: drillster/drone-volume-cache
volumes:
- name: cache
path: /cache
settings:
restore: true
mount:
- ./node_modules
- name: build
image: *PRODUCTION_NODE_VERSION
pull: if-not-exists # default always
# volumes:
# - name: cache
# path: /root/.m2
commands: *PRODUCTION_CMD
- name: rebuild-cache
image: drillster/drone-volume-cache
volumes:
- name: cache
path: /cache
settings:
rebuild: true
mount:
- ./node_modules
- name: deploy-scp
image: appleboy/drone-scp
pull: if-not-exists
volumes:
- name: ssh_key
path: /root/.ssh/
settings:
host: *PRODUCTION_HOST
port: *PRODUCTION_PORT
username: root
key_path: /root/.ssh/id_rsa
rm: true # true则会删除目标目录重建
target: *PRODUCTION_SCP_TARGET
source: *PRODUCTION_SCP_SOURCE
strip_components: 1 # 去除的目录层数,如果没有该选项,则拷贝过去是 target/xxx.jar,1代表去除target
- name: notify-wechatwork
image: fifsky/drone-wechat-work
pull: if-not-exists
settings:
url: *NOTIFY_WECHATROBOT_WEBHOOK
msgtype: markdown
content: |
{{if eq .Status "success" }}
#### 🎉 ${DRONE_REPO} 生产环境构建成功
> Branch: ${DRONE_BRANCH}
> Commit: [${DRONE_COMMIT_MESSAGE}](${DRONE_COMMIT_LINK})
> Author: ${DRONE_COMMIT_AUTHOR}
> PATH: https://www.tall.wiki/experiment/
> [点击查看](https://www.tall.wiki/experiment/)
{{else}}
#### ❌ ${DRONE_REPO} 生产环境构建失败
> Branch: ${DRONE_BRANCH}
> Commit: [${DRONE_COMMIT_MESSAGE}](${DRONE_COMMIT_LINK})
> Author: ${DRONE_COMMIT_AUTHOR}
> 请立即修复!!!
> [点击查看](https://www.tall.wiki/experiment/)
{{end}}
when:
status:
- failure
- success
trigger:
branch:
- *PRODUCTION_BRANCH

6
.env.development

@ -1 +1,5 @@
VITE_API_URL=http://localhost:3000
VITE_BASE_URL=http://101.201.226.163
VITE_API_URL=http://101.201.226.163/gateway
VITE_MSG_URL=ws://101.201.226.163:8196/message/v4.0/ws
VITE_SERVICELIST=['ZERO', 'CONTEST', 'PT']
VITE_VERSION=v4.0.0

6
.env.production

@ -1 +1,5 @@
VITE_API_URL=https://www.tall.wiki
VITE_BASE_URL=http://101.201.226.163
VITE_API_URL=http://101.201.226.163/gateway
VITE_MSG_URL=ws://101.201.226.163:8196/message/v4.0/ws
VITE_SERVICELIST=['ZERO', 'CONTEST', 'PT']
VITE_VERSION=v4.0.0

6
.env.test

@ -1 +1,5 @@
VITE_API_URL=https://test.tall.wiki
VITE_BASE_URL=http://101.201.226.163
VITE_API_URL=http://101.201.226.163/gateway
VITE_MSG_URL=ws://101.201.226.163:8196/message/v4.0/ws
VITE_SERVICELIST=['ZERO', 'CONTEST', 'PT']
VITE_VERSION=v4.0.0

2
index.html

@ -4,7 +4,7 @@
<meta charset="UTF-8" />
<link rel="icon" href="/favicon.ico" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>山大实验室管理平台</title>
<title>PT</title>
</head>
<body>
<div id="app"></div>

14
package-lock.json

@ -14,6 +14,7 @@
"dayjs": "^1.10.7",
"echarts": "^5.2.2",
"lodash": "^4.17.21",
"uuid": "^8.3.2",
"vite": "^2.6.4",
"vite-plugin-compression": "^0.3.5",
"vite-plugin-windicss": "^1.4.11",
@ -8487,6 +8488,14 @@
"integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=",
"dev": true
},
"node_modules/uuid": {
"version": "8.3.2",
"resolved": "https://registry.npmmirror.com/uuid/-/uuid-8.3.2.tgz",
"integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==",
"bin": {
"uuid": "dist/bin/uuid"
}
},
"node_modules/v8-compile-cache": {
"version": "2.3.0",
"resolved": "https://registry.nlark.com/v8-compile-cache/download/v8-compile-cache-2.3.0.tgz",
@ -15694,6 +15703,11 @@
"integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=",
"dev": true
},
"uuid": {
"version": "8.3.2",
"resolved": "https://registry.npmmirror.com/uuid/-/uuid-8.3.2.tgz",
"integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg=="
},
"v8-compile-cache": {
"version": "2.3.0",
"resolved": "https://registry.nlark.com/v8-compile-cache/download/v8-compile-cache-2.3.0.tgz",

1
package.json

@ -20,6 +20,7 @@
"dayjs": "^1.10.7",
"echarts": "^5.2.2",
"lodash": "^4.17.21",
"uuid": "^8.3.2",
"vite": "^2.6.4",
"vite-plugin-compression": "^0.3.5",
"vite-plugin-windicss": "^1.4.11",

62
src/App.vue

@ -2,74 +2,24 @@
<div style="width: 100%; height: 100%">
<router-view></router-view>
</div>
<!-- <a-config-provider v-else :locale="locale"> -->
<!-- <a-layout> -->
<!-- <a-layout-header style="background: #fff"> <TopNavbar /> </a-layout-header> -->
<!-- <a-layout> -->
<!-- 日历页-->
<!-- <a-layout-sider v-show="collapsed" style="background: #fff"><Left /></a-layout-sider> -->
<!-- <a-layout> -->
<!-- <a-layout-sider class="project-detail"><ProjectDetail /></a-layout-sider> -->
<!-- <a-layout> -->
<!-- 导航栏-->
<!-- <a-layout-header style="background: #fff"> -->
<!-- <Navbar /> -->
<!-- </a-layout-header> -->
<!-- 内容区-->
<!-- <a-layout-content><router-view></router-view></a-layout-content> -->
<!-- 脚部-->
<!-- <a-layout-footer>Footer</a-layout-footer>-->
<!-- </a-layout> -->
<!-- </a-layout> -->
<!-- </a-layout> -->
<!-- </a-layout> -->
<!-- </a-config-provider> -->
</template>
<script setup>
// import { computed } from 'vue';
import { useStore } from 'vuex';
import { useRouter } from 'vue-router';
// import zhCN from 'ant-design-vue/es/locale/zh_CN';
// import Left from 'components/tall/left/Index.vue';
// import Navbar from 'components/tall/top/Navbar.vue';
// import TopNavbar from 'components/tall/top/TopNavbar.vue';
// import ProjectDetail from 'components/tall/center/ProjectDetail.vue';
// const locale = zhCN;
import { v4 as uuidv4 } from 'uuid';
const store = useStore();
// const collapsed = computed(() => store.state.layout.display.left); //
// queryu token
// const route = useRoute();
const router = useRouter();
const userString = sessionStorage.getItem('user');
//
store.commit('layout/setDeviceId', uuidv4().split('-')[0]);
if (userString) {
const user = JSON.parse(userString);
store.commit('user/setUser', user);
} else {
router.push({ path: '/experiment/user/signIn' });
}
// useRouter()
// .isReady()
// .then(async () => {
// const u = computed(() => route.query.u);
// if (!u.value) {
// // urlu,
// console.log('');
// router.push({ path: '/experiment/user/signIn' });
// } else {
// // userId token
// await store.dispatch('user/getTokenByUserId', u.value);
// }
// });
// const token = computed(() => store.getters['user/token']);
}
</script>
<style>

116
src/apis/index.js

@ -2,43 +2,60 @@
import http from 'utils/axios';
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 apiUrl = import.meta.env.VITE_API_URL;
// const users = `${apiUrl}/gateway/tall3/v3.0/users`;
// const tall = `${apiUrl}/gateway/tall3/v3.0`;
// const filedeal = `${apiUrl}/filedeal`; // 测试
// const filedeal = `http://101.201.226.21:7180`; // 生产
const apiUrl = import.meta.env.VITE_API_URL;
const tall = `${apiUrl}/ptostall`;
const experiment = `${apiUrl}/experiment`;
// 根据userId 获取token
// eslint-disable-next-line import/prefer-default-export
export const getToken = userId => http.get(`${users}/userId`, { params: { userId } });
export const getToken = userId => http.get(`${tall}/users/userId`, { userId });
// 根据refreshToken重新获取token
export const getNewToken = refreshToken => http.get(`${tall}/users/refreshToken?refreshToken=${refreshToken}`);
// 登录api
export const signIn = params => http.post(`${users}/signin`, params);
export const signIn = params => http.post(`${tall}/users/signin`, params);
// 获取图片验证码
export const getImageCode = () => http.get(`${tall}/users/code`);
// 获取短信验证码
export const getSmsCode = params => http.get(`${tall}/users/smscode`, { params });
/**
* 项目相关
*/
// 获取项目列表
export const getProjects = (startTime, endTime) => http.post(`${tall}/project/query`, { param: { startTime, endTime } });
// 根据id获取项目信息
export const findProjectById = projectId => http.post(`${experiment}/project/findProjectById`, { param: { projectId } });
export const findProjectById = (projectId, url) => http.post(`${url}/tall/project/findProjectById`, { param: { projectId } });
// 删除项目
export const delProject = projectId => http.post(`${tall}/project/deleteProject`, { param: { projectId } });
export const delProject = (projectId, url) => http.post(`${url}/tall/project/delete`, { param: { projectId } });
// 根据项目id查找角色
export const findShowRole = projectId => http.post(`${experiment}/role/show`, { param: { projectId } });
export const findShowRole = (projectId, url) => http.post(`${url}/tall/role/show`, { param: { projectId } });
// 根据项目id查找所有成员
export const queryChecker = param => http.post(`${tall}/deliver/queryChecker`, param);
// 查找带时间的日常任务
export const getGlobal = params => http.post(`${experiment}/task/global`, params);
export const getGlobal = (params, url) => http.post(`${url}/tall/task/global`, params);
// 查找永久日常任务
export const getPermanent = params => http.post(`${experiment}/task/permanent`, params);
export const getPermanent = (params, url) => http.post(`${url}/tall/task/permanent`, params);
// 查找定期任务
export const getRegularTask = params => http.post(`${experiment}/task/regular`, params);
export const getRegularTask = (params, url) => http.post(`${url}/tall/task/regular/page`, params);
// 添加任务
export const saveTask = params => http.post(`${experiment}/task/save`, params);
@ -65,78 +82,3 @@ export const create = param => http.post(`${experiment}/experiment/create`, { pa
// 上传文件
// export const uploadImg = `${filedeal}/file/upload/multiple`;
export const uploadImg = `${experiment}/import/upload`;
// 添加/编辑计划任务书
export const savePlanTask = params => http.post(`${experiment}/experiment/savePlanTask`, params);
// 查看任务计划书
export const getPlanTask = params => http.post(`${experiment}/experiment/getPlanTask`, params);
// 添加/编辑子课题
export const saveSubExperiment = params => http.post(`${experiment}/experiment/saveSubExperiment`, params);
// 查看子课题
export const getSubExperiment = params => http.post(`${experiment}/experiment/getSubExperiment`, params);
// 查询成员
export const memberQuery = params => http.post(`${experiment}/organization/query`, params);
// 添加成员
export const saveMember = params => http.post(`${experiment}/organization/save`, params);
// 编辑成员
export const updateMember = params => http.post(`${experiment}/organization/update`, params);
// 删除成员
export const delMember = params => http.post(`${experiment}/organization/del`, params);
// 分配实验
export const createExperiment = params => http.post(`${experiment}/subExperiment/create`, params);
// 根据ID查询实验信息
export const getExperimentation = params => http.post(`${experiment}/subExperiment/getExperimentation`, params);
// 根据code查询所有试题
export const getByCode = params => http.post(`${experiment}/question/getByCode`, params);
// 提交答案
export const submitAnswer = params => http.post(`${experiment}/question/submit`, params);
// 查询知识产权题目(论文、专利、软著)
export const getIntellectual = params => http.post(`${experiment}/question/getIntellectual`, params);
// 查询知识产权列表 (论文、专利、软著)
export const getIntellectualList = params => http.post(`${experiment}/question/getIntellectualList`, params);
// 提交知识产权信息(论文、专利、软著)
export const submitIntellectual = params => http.post(`${experiment}/question/submitIntellectual`, params);
// 查询会议列表
export const getMeetQuery = params => http.post(`${experiment}/meeting/query`, params);
// 添加/修改会议信息
export const saveMeeting = params => http.post(`${experiment}/meeting/save`, params);
// 根据会议id查询会议详细信息
export const getMeetDetail = params => http.post(`${experiment}/meeting/get`, params);
// 科研成果列表
export const queryExperimentation = params => http.post(`${experiment}/subExperiment/queryExperimentation`, params);
// 审核科研成果
export const examineExperimentation = params => http.post(`${experiment}/subExperiment/examineExperimentation`, params);
// 申请解锁科研结果
export const applyUnlock = params => http.post(`${experiment}/subExperiment/applyUnlock`, params);
// 审批解锁申请
export const examineUnlock = params => http.post(`${experiment}/experiment/examineUnlock`, params);
// 查找数据追溯解锁信息
export const retrospectUnlock = params => http.post(`${experiment}/experiment/retrospectUnlock`, params);
// 课题进度
export const getExperimentProgress = params => http.post(`${experiment}/experiment/getExperimentProgress`, params);
// 子课题进度
export const getProgress = params => http.post(`${experiment}/subExperiment/getProgress`, params);

89
src/components/tall/center/Global.vue

@ -1,11 +1,35 @@
<template>
<div class="global">
<div class="global-box" v-if="permanents && permanents.length > 0">
<div class="global-task cursor-pointer" v-for="(item, index) in permanents" :key="index" @click="toDetail(item)">
<template v-for="v in item.plugins">
<template v-if="v[0].pluginId == 1">{{ item.name }}</template>
</template>
<div class="global-box" style="max-height: calc((100vh - 50px - 44px - 36px - 10px) / 5 * 4)">
<div v-if="roleInfo && roleInfo.name === '管理员'">
<a-button type="primary" block @click="toWorkbench">工作台</a-button>
</div>
<template v-if="globals.length > 0">
<div class="global-task cursor-pointer" v-for="(item, index) in globals" :key="index" @click="toDetail(item)">
<template v-if="item.plugins && item.plugins.length">
<div v-for="(pluginArr, i) in item.plugins" :key="i" class="pb-3">
<template v-if="pluginArr.length">
<Plugin
v-for="plugin in pluginArr"
:key="plugin.pluginTaskId"
:plugin-id="plugin.pluginId"
:plugin-task-id="plugin.pluginTaskId"
:business-plugin-id="plugin.businessPluginId"
:plugin-info="plugin"
:param="plugin.param"
:style-type="plugin.styleType || 0"
:task="item"
/>
</template>
</div>
</template>
<div v-else class="pb-3">
<Plugin plugin-id="1" :task="item" />
</div>
</div>
</template>
</div>
</div>
</template>
@ -13,40 +37,47 @@
<script setup>
import { computed, watch } from 'vue';
import { useStore } from 'vuex';
import { getPermanent } from 'apis';
import { getPermanent, getGlobal } from 'apis';
import { message } from 'ant-design-vue';
import Plugin from 'components/tall/plugin/Plugin.vue';
const store = useStore();
const project = computed(() => store.state.projects.project); //
const roleId = computed(() => store.state.role.roleId); //
const permanents = computed(() => store.state.task.permanents); //
const sessionPermanents = sessionStorage.getItem('permanents'); //
const roleInfo = computed(() => store.state.role.roleInfo); //
const roleId = computed(() => store.state.role.roleId); // id
const globals = computed(() => store.getters['task/globals']); //
if (sessionPermanents) {
const arr = JSON.parse(sessionPermanents);
store.commit('task/setPermanents', arr);
if (project.value && roleId.value) {
getPermanentData(roleId.value); //
getGlobalData(roleId.value);
}
if (project.value) {
if (roleId.value) {
watch([project, roleId], () => {
if (project.value && roleId.value) {
getPermanentData(roleId.value); //
}
}
watch([project, roleId], async () => {
if (roleId.value) {
await getPermanentData(roleId.value); //
getGlobalData(roleId.value);
}
});
//
async function getPermanentData(id) {
const params = { param: { roleId: id } };
const { url } = store.state.projects.project;
try {
const data = await getPermanent(params);
const data = await getPermanent(params, url);
store.commit('task/setPermanents', data);
const globalHeight = data.length * 38 + 26;
store.commit('task/setGlobalHeight', globalHeight);
} catch (error) {
message.info(error);
throw new Error(error);
}
}
async function getGlobalData(id) {
const params = { param: { roleId: id } };
const { url } = store.state.projects.project;
try {
const data = await getGlobal(params, url);
store.commit('task/setDailyTasks', data);
} catch (error) {
message.info(error);
throw new Error(error);
@ -54,12 +85,11 @@ async function getPermanentData(id) {
}
function toDetail(item) {
store.commit('task/setTaskDetail', item);
store.commit('layout/setListStatus', false);
store.commit('task/setIntellectualId', '');
store.commit('task/setMeetId', '');
store.commit('task/setSubMeetId', '');
// store.commit('task/setTaskDetail', item);
console.log(item);
}
function toWorkbench() {}
</script>
<style scoped>
@ -74,7 +104,6 @@ function toDetail(item) {
}
.global-task {
padding: 8px 0;
line-height: 22px;
line-height: 38px;
}
</style>

233
src/components/tall/center/Index.vue

@ -10,17 +10,17 @@
</div>
<!-- 角色 -->
<!-- <Roles /> -->
<div class="role-list flex items-center">
<Roles />
<!-- <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: roleId === item.id }">{{ item.name }}</div>
<div class="line-box absolute flex justify-center" v-if="roleId === item.id"><div class="line"></div></div>
</div>
</div>
</div> -->
<!-- 日常任务 -->
<!-- <Global /> -->
<div class="global">
<Global />
<!-- <div class="global">
<div class="global-box" v-if="permanents && permanents.length > 0">
<div class="global-task cursor-pointer" v-for="(item, index) in permanents" :key="index" @click="toGlobalDetail(item)">
<template v-for="v in item.plugins">
@ -28,11 +28,11 @@
</template>
</div>
</div>
</div>
</div> -->
<!-- 定期任务 -->
<!-- <RegularTask /> -->
<div class="task-list" :style="{ height: 'calc(100vh - 160px - (' + globalHeight + 'px))' }">
<RegularTask />
<!-- <div class="task-list" :style="{ height: 'calc(100vh - 160px - (' + globalHeight + 'px))' }">
<div class="task-box" v-for="(item, index) in regularTasks" :key="index">
<div class="task-time flex items-center justify-between">
<div class="flex items-center">
@ -53,29 +53,26 @@
<div class="task-con" v-if="item.sonList && item.sonList.length > 0">
<div v-for="(val, key) in item.sonList" :key="key">
<!-- <a-checkbox>{{ val.name }}</a-checkbox> -->
<span class="son-task-name cursor-pointer" @click.stop="toSonDetail(item, val.detailId)">{{ val.name }}</span>
</div>
</div>
<!-- <div class="open-icon" v-if="item.sonList" @click="openCard">
<img />
</div> -->
</div>
</div>
</div>
</div>
</div>
</div> -->
</div>
</template>
<script setup>
import { computed, watch, reactive } from 'vue';
import { computed, watch } from 'vue';
import { useStore } from 'vuex';
import dayjs from 'dayjs';
import { ReloadOutlined, MoreOutlined, PlayCircleOutlined } from '@ant-design/icons-vue';
// import dayjs from 'dayjs';
// import { ReloadOutlined, MoreOutlined, PlayCircleOutlined } from '@ant-design/icons-vue';
import { ReloadOutlined, MoreOutlined } from '@ant-design/icons-vue';
// import { message } from 'ant-design-vue';
// import { findShowRole, getPermanent, getRegularTask, findSonTask } from 'apis';
// import Roles from './Roles.vue'; //
import { message } from 'ant-design-vue';
import { findShowRole, getPermanent, getRegularTask, findSonTask } from 'apis';
// import Global from './Global.vue'; //
// import RegularTask from './RegularTask.vue'; //
@ -83,33 +80,33 @@ const store = useStore();
const project = computed(() => store.state.projects.project); //
const roleId = computed(() => store.state.role.roleId); //
const sessionRoles = sessionStorage.getItem('roles'); //
const roles = computed(() => store.state.role.visibleRoles); //
// const sessionRoles = sessionStorage.getItem('roles'); //
// const roles = computed(() => store.state.role.visibleRoles); //
const permanents = computed(() => store.state.task.permanents); //
const sessionPermanents = sessionStorage.getItem('permanents'); //
const globalHeight = computed(() => store.state.task.globalHeight); //
const sessionGlobalHeight = sessionStorage.getItem('globalHeight'); //
// const permanents = computed(() => store.state.task.permanents); //
// const sessionPermanents = sessionStorage.getItem('permanents'); //
// const globalHeight = computed(() => store.state.task.globalHeight); //
// const sessionGlobalHeight = sessionStorage.getItem('globalHeight'); //
const regularTasks = computed(() => store.state.task.regularTasks); //
const taskObj = reactive({ tasks: [] }); //
// const regularTasks = computed(() => store.state.task.regularTasks); //
// const taskObj = reactive({ tasks: [] }); //
const sessionTasks = sessionStorage.getItem('regularTasks'); //
const refreshProjects = computed(() => store.state.layout.refreshProjects); //
if (sessionRoles) {
const roleArr = JSON.parse(sessionRoles);
store.commit('role/setVisibleRoles', roleArr);
setInitialRoleId(roleArr);
}
// if (sessionRoles) {
// const roleArr = JSON.parse(sessionRoles);
// store.commit('role/setVisibleRoles', roleArr);
// setInitialRoleId(roleArr);
// }
if (sessionPermanents) {
const arr = JSON.parse(sessionPermanents);
store.commit('task/setPermanents', arr);
}
// if (sessionPermanents) {
// const arr = JSON.parse(sessionPermanents);
// store.commit('task/setPermanents', arr);
// }
if (sessionGlobalHeight) {
store.commit('task/setGlobalHeight', sessionGlobalHeight);
}
// if (sessionGlobalHeight) {
// store.commit('task/setGlobalHeight', sessionGlobalHeight);
// }
if (sessionTasks) {
const arr = JSON.parse(sessionTasks);
@ -148,48 +145,51 @@ async function init() {
* @param {object} params 提交的参数
*/
async function getRoles(params) {
try {
const data = await findShowRole(params);
store.commit('role/setInvisibleRoles', data ? data.invisibleList : []);
store.commit('role/setVisibleRoles', data ? data.visibleList : []);
setInitialRoleId(data ? data.visibleList : []);
} catch (error) {
message.info(error);
throw new Error(error);
}
console.log(params);
// try {
// const { url } = store.state.projects.project;
// const data = await findShowRole(params, url);
// store.commit('role/setInvisibleRoles', data ? data.invisibleList : []);
// store.commit('role/setVisibleRoles', data ? data.visibleList : []);
// setInitialRoleId(data ? data.visibleList : []);
// } catch (error) {
// message.info(error);
// throw new Error(error);
// }
}
//
function setInitialRoleId(visibleList) {
if (!visibleList || !visibleList.length) return;
const index = visibleList.findIndex(item => +item.mine === 1);
const currentRole = index > 0 ? visibleList[index] : visibleList[0];
const currentRoleId = currentRole ? currentRole.id : '';
store.commit('role/setRoleId', currentRoleId);
}
// function setInitialRoleId(visibleList) {
// if (!visibleList || !visibleList.length) return;
// const index = visibleList.findIndex(item => +item.mine === 1);
// const currentRole = index > 0 ? visibleList[index] : visibleList[0];
// const currentRoleId = currentRole ? currentRole.id : '';
// store.commit('role/setRoleId', currentRoleId);
// }
//
async function getPermanentData(id) {
const params = { param: { roleId: id } };
try {
const data = await getPermanent(params);
store.commit('task/setPermanents', data);
const height = data.length * 38 + 26;
store.commit('task/setGlobalHeight', height);
} catch (error) {
message.info(error);
throw new Error(error);
}
console.log(id);
// const params = { param: { roleId: id } };
// try {
// const data = await getPermanent(params);
// store.commit('task/setPermanents', data);
// const height = data.length * 38 + 26;
// store.commit('task/setGlobalHeight', height);
// } catch (error) {
// message.info(error);
// throw new Error(error);
// }
}
//
function toGlobalDetail(item) {
store.commit('task/setTaskDetail', item);
store.commit('layout/setListStatus', false);
store.commit('task/setIntellectualId', '');
store.commit('task/setMeetId', '');
store.commit('task/setSubMeetId', '');
}
// function toGlobalDetail(item) {
// store.commit('task/setTaskDetail', item);
// store.commit('layout/setListStatus', false);
// store.commit('task/setIntellectualId', '');
// store.commit('task/setMeetId', '');
// store.commit('task/setSubMeetId', '');
// }
/**
* 根据时间基准点和角色查找定期任务
@ -201,54 +201,55 @@ function toGlobalDetail(item) {
* @param {number} query.queryType 0向上查找 1向下查找(默认) 下查包含自己上查不包含
*/
async function getTasks(query) {
const params = { param: query };
try {
const data = await getRegularTask(params);
taskObj.tasks = data;
data.forEach(item => {
item.plugins.forEach(val => {
if (Number(val[0].pluginId) === 6) {
getSonTask(item.detailId);
}
});
});
store.commit('task/setRegularTasks', data);
} catch (error) {
message.info(error);
throw new Error(error);
}
console.log(query);
// const params = { param: query };
// try {
// const data = await getRegularTask(params);
// taskObj.tasks = data;
// data.forEach(item => {
// item.plugins.forEach(val => {
// if (Number(val[0].pluginId) === 6) {
// getSonTask(item.detailId);
// }
// });
// });
// store.commit('task/setRegularTasks', data);
// } catch (error) {
// message.info(error);
// throw new Error(error);
// }
}
// id
async function getSonTask(id) {
const params = { param: { detailId: id } };
try {
const data = await findSonTask(params);
taskObj.tasks.forEach(item => {
if (item.detailId === id) {
item.sonList = data;
}
});
store.commit('task/setRegularTasks', taskObj.tasks);
} catch (error) {
message.info(error);
throw new Error(error);
}
}
function toDetail(item) {
store.commit('task/setTaskDetail', item);
store.commit('task/sonDetailId', '');
store.commit('task/sonExperimentationId', '');
store.commit('layout/setListStatus', false);
}
function toSonDetail(item, id) {
store.commit('task/setTaskDetail', item);
store.commit('task/sonDetailId', id);
store.commit('task/sonExperimentationId', id);
store.commit('layout/setListStatus', false);
}
// async function getSonTask(id) {
// const params = { param: { detailId: id } };
// try {
// const data = await findSonTask(params);
// taskObj.tasks.forEach(item => {
// if (item.detailId === id) {
// item.sonList = data;
// }
// });
// store.commit('task/setRegularTasks', taskObj.tasks);
// } catch (error) {
// message.info(error);
// throw new Error(error);
// }
// }
// function toDetail(item) {
// store.commit('task/setTaskDetail', item);
// store.commit('task/sonDetailId', '');
// store.commit('task/sonExperimentationId', '');
// store.commit('layout/setListStatus', false);
// }
// function toSonDetail(item, id) {
// store.commit('task/setTaskDetail', item);
// store.commit('task/sonDetailId', id);
// store.commit('task/sonExperimentationId', id);
// store.commit('layout/setListStatus', false);
// }
</script>
<style scoped>

92
src/components/tall/center/RegularTask.vue

@ -1,5 +1,5 @@
<template>
<div class="task-list" :style="{ height: 'calc(100vh - 160px - (' + globalHeight + 'px))' }">
<div class="task-list" :style="{}">
<div class="task-box" v-for="(item, index) in regularTasks" :key="index">
<div class="task-time flex items-center justify-between">
<div class="flex items-center">
@ -35,40 +35,29 @@
</template>
<script setup>
import { computed, watch, reactive } from 'vue';
import { computed, watch } from 'vue';
import { useStore } from 'vuex';
import dayjs from 'dayjs';
import { PlayCircleOutlined } from '@ant-design/icons-vue';
import { getRegularTask, findSonTask } from 'apis';
import { getRegularTask } from 'apis';
import { message } from 'ant-design-vue';
const store = useStore();
const project = computed(() => store.state.projects.project); //
const roleId = computed(() => store.state.role.roleId); //
const globalHeight = computed(() => store.state.task.globalHeight); //
const sessionGlobalHeight = sessionStorage.getItem('globalHeight'); //
const regularTasks = computed(() => store.state.task.regularTasks); //
const taskObj = reactive({ tasks: [] }); //
const sessionTasks = sessionStorage.getItem('regularTasks'); //
const refreshProjects = computed(() => store.state.layout.refreshProjects); //
if (sessionGlobalHeight) {
store.commit('task/setGlobalHeight', sessionGlobalHeight);
}
if (sessionTasks) {
const arr = JSON.parse(sessionTasks);
store.commit('task/setRegularTasks', arr);
}
const timeNode = computed(() => store.state.task.timeNode); //
const timeUnit = computed(() => store.state.task.timeUnit); //
const currLocationTaskId = computed(() => store.state.task.currLocationTaskId); //
const businessCode = computed(() => store.state.task.businessCode); //
if (project.value) {
if (roleId.value) {
getTasks({ roleId: roleId.value }); //
}
if (project.value && roleId.value) {
getTasks({ roleId: roleId.value }); //
}
watch([project, roleId, refreshProjects], async () => {
if (roleId.value) {
watch([project, roleId], async () => {
if (project.value && roleId.value) {
await getTasks({ roleId: roleId.value }); //
}
});
@ -83,19 +72,11 @@ watch([project, roleId, refreshProjects], async () => {
* @param {number} query.queryType 0向上查找 1向下查找(默认) 下查包含自己上查不包含
*/
async function getTasks(query) {
const params = { param: query };
const params = generateGetTaskParam(query);
console.log(params);
const { url } = store.state.projects.project;
try {
const data = await getRegularTask(params);
taskObj.tasks = data;
data.forEach(item => {
item.plugins.forEach(val => {
if (Number(val[0].pluginId) === 6) {
getSonTask(item.detailId);
}
});
});
const data = await getRegularTask(params, url);
store.commit('task/setRegularTasks', data);
} catch (error) {
message.info(error);
@ -103,34 +84,27 @@ async function getTasks(query) {
}
}
// id
async function getSonTask(id) {
const params = { param: { detailId: id } };
try {
const data = await findSonTask(params);
taskObj.tasks.forEach(item => {
if (item.detailId === id) {
item.sonList = data;
}
});
store.commit('task/setRegularTasks', taskObj.tasks);
} catch (error) {
message.info(error);
throw new Error(error);
}
/**
* 生成getTasks所用的参数
* @param {object} query getTasks传递的参数
*/
function generateGetTaskParam(query) {
return {
roleId: roleId.value,
timeNode: query.timeNode || timeNode.value,
timeUnit: query.timeUnit || timeUnit.value,
queryType: query.queryType === 0 ? 0 : 1,
pageNum: query.pageNum || 1,
pageSize: query.pageSize || 10,
taskId: query.taskId || currLocationTaskId.value,
businessCode: query.businessCode || businessCode.value,
triggerType: query.triggerType || 1,
};
}
function toDetail(item) {
store.commit('task/setTaskDetail', item);
store.commit('task/sonDetailId', '');
store.commit('task/sonExperimentationId', '');
}
function toSonDetail(item, id) {
store.commit('task/setTaskDetail', item);
store.commit('task/sonDetailId', id);
store.commit('task/sonExperimentationId', id);
console.log(item);
// store.commit('task/setTaskDetail', item);
}
</script>

18
src/components/tall/center/Roles.vue

@ -1,6 +1,6 @@
<template>
<div class="role-list flex items-center">
<div class="role-box relative" v-for="(item, index) in roles" :key="index">
<div class="role-box relative" v-for="(item, index) in roleList" :key="index">
<div class="role-name" :class="{ mine: roleId === item.id }">{{ item.name }}</div>
<div class="line-box absolute flex justify-center" v-if="roleId === item.id"><div class="line"></div></div>
</div>
@ -15,18 +15,10 @@ import { message } from 'ant-design-vue';
const store = useStore();
const roleId = computed(() => store.state.role.roleId); //
const sessionRoles = sessionStorage.getItem('roles'); //
const roles = computed(() => store.state.role.visibleRoles); //
const roleList = computed(() => store.state.role.visibleRoles); //
const project = computed(() => store.state.projects.project); //
if (sessionRoles) {
const roleArr = JSON.parse(sessionRoles);
store.commit('role/setVisibleRoles', roleArr);
setInitialRoleId(roleArr);
}
if (project.value) {
if (project.value && project.value.id) {
getRoles(project.value.id); // id
}
@ -43,7 +35,8 @@ watch(project, async () => {
*/
async function getRoles(params) {
try {
const data = await findShowRole(params);
const { url } = store.state.projects.project;
const data = await findShowRole(params, url);
store.commit('role/setInvisibleRoles', data ? data.invisibleList : []);
store.commit('role/setVisibleRoles', data ? data.visibleList : []);
setInitialRoleId(data ? data.visibleList : []);
@ -59,6 +52,7 @@ function setInitialRoleId(visibleList) {
const index = visibleList.findIndex(item => +item.mine === 1);
const currentRole = index > 0 ? visibleList[index] : visibleList[0];
const currentRoleId = currentRole ? currentRole.id : '';
store.commit('role/setRoleInfo', currentRole || null);
store.commit('role/setRoleId', currentRoleId);
}
</script>

13
src/components/tall/left/Index.vue

@ -4,17 +4,6 @@
<PlusCircleFilled class="upload-btn absolute cursor-pointer" :style="{ fontSize: 42 + 'px', color: '#1890FF' }" @click="showModal" />
<!-- <div class="upload-card absolute" v-if="isShowCard">
<div class="model-list">
<div class="model-name cursor-pointer">模板项目</div>
<div class="model-name cursor-pointer">模板项目</div>
</div>
<div class="upload-box relative">
<a-button type="primary">导入WBS</a-button>
<input @change="handleUpload" class="btn-file absolute cursor-pointer" name="file" type="file" />
</div>
</div> -->
<a-modal v-model:visible="visible" title="新建课题" @ok="handleOk">
<p>是否新建课题</p>
</a-modal>
@ -57,7 +46,7 @@ async function createExperiment() {
name: data.projectName,
};
store.commit('projects/setProject', obj);
store.commit('task/setTaskDetail', null);
// store.commit('task/setTaskDetail', null);
store.commit('layout/setRefreshProjects');
return data;
} catch (error) {

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

@ -5,18 +5,22 @@
<div class="one-level h-70 cursor-pointer flex items-center" @click="toDetail(item)">
<div class="icon" @click.stop="showActionCard(item)"><img src="https://www.tall.wiki/staticrec/drag.svg" /></div>
<div class="detail">
<div class="name-box flex items-center">
<div class="name-box flex items-center" :class="{ 'mb-2': item.businessCode !== 'ZERO' }">
<div class="name truncate">{{ item.name }}</div>
<!-- <div class="precent-num">50%</div> -->
<div class="precent-num">
{{ item.status === 1 ? '进行中' : item.status === 2 ? '已结束' : item.status === 0 ? '未开始' : '暂停' }}
</div>
</div>
<div class="time">
<div class="time" v-if="item.businessCode !== 'ZERO'">
{{ dayjs(Number(item.startTime)).format('MM-DD HH:mm') }}
{{ dayjs(Number(item.endTime)).format('MM-DD HH:mm') }}
</div>
</div>
<div class="right" @click.stop="openMenu">
<div class="right flex justify-end items-center" @click.stop="openMenu">
<a-button v-if="item.businessCode === 'ZERO'" class="mr-2" shape="round" type="primary">工作台</a-button>
<RightOutlined v-if="!item.show" @click="changeShow(item)" />
<DownOutlined v-else @click="changeShow(item)" />
</div>
@ -24,12 +28,14 @@
<div class="two-box" v-if="item.show">
<div class="two-flex" v-for="(sonItem, sonIndex) in item.sonProjectList" :key="sonIndex">
<div class="two-level h-70 cursor-pointer flex items-center" @click="toDetail(sonItem, item)">
<div class="two-level h-70 cursor-pointer flex items-center" @click="toDetail(sonItem)">
<div class="icon" @click.stop="showActionCard(sonItem)"><img src="https://www.tall.wiki/staticrec/drag.svg" /></div>
<div class="detail">
<div class="name-box flex items-center">
<div class="name-box mb-2 flex items-center">
<div class="name truncate">{{ sonItem.name }}</div>
<!-- <div class="precent-num">50%</div> -->
<div class="precent-num">
{{ item.status === 1 ? '进行中' : item.status === 2 ? '已结束' : item.status === 0 ? '未开始' : '暂停' }}
</div>
</div>
<div class="time">
@ -42,28 +48,6 @@
<DownOutlined v-else @click="changeShow(sonItem)" />
</div>
</div>
<div class="three-box" v-if="sonItem.show">
<div
class="three-level h-70 cursor-pointer flex items-center"
v-for="(thirdItem, thirdIndex) in sonItem.sonProjectList"
:key="thirdIndex"
@click="toDetail(thirdItem, sonItem, item)"
>
<div class="icon" @click.stop="showActionCard(thirdItem)"><img src="https://www.tall.wiki/staticrec/drag.svg" /></div>
<div class="detail">
<div class="name-box flex items-center">
<div class="name truncate">{{ thirdItem.name }}</div>
<!-- <div class="precent-num">50%</div> -->
</div>
<div class="time">
{{ dayjs(Number(thirdItem.startTime)).format('MM-DD HH:mm') }}
{{ dayjs(Number(thirdItem.endTime)).format('MM-DD HH:mm') }}
</div>
</div>
</div>
</div>
</div>
</div>
</div>
@ -78,56 +62,24 @@
import { ref, watch, computed } from 'vue';
import { useStore } from 'vuex';
import dayjs from 'dayjs';
import { getProjects, delProject, getExperimentation } from 'apis';
import { getProjects, delProject } from 'apis';
import { RightOutlined, DownOutlined } from '@ant-design/icons-vue';
import { message } from 'ant-design-vue';
const store = useStore();
const visible = ref(false);
const deleteId = ref(null);
// const projectId = sessionStorage.getItem('projectId'); // ID
const visible = ref(false); //
const deleteId = ref(null); // id
const sessionProject = sessionStorage.getItem('project'); //
const projectInfo = computed(() => store.state.projects.project); //
const projects = computed(() => store.state.projects.projects); //
const startTime = computed(() => store.state.layout.startTime); //
const endTime = computed(() => store.state.layout.endTime); //
const refreshProjects = computed(() => store.state.layout.refreshProjects); //
//
const planStartTime = sessionStorage.getItem('planStartTime');
const planEndTime = sessionStorage.getItem('planEndTime');
//
const subStartTime = sessionStorage.getItem('subStartTime');
const subEndTime = sessionStorage.getItem('subEndTime');
//
const expreStartTime = sessionStorage.getItem('expreStartTime');
const expreEndTime = sessionStorage.getItem('expreEndTime');
if (planStartTime) {
store.commit('layout/setFirPlanTime', { startTime: planStartTime, endTime: planEndTime });
}
if (subStartTime) {
store.commit('layout/setSecPlanTime', { startTime: subStartTime, endTime: subEndTime });
}
if (expreStartTime) {
store.commit('layout/setThirdPlanTime', { startTime: expreStartTime, endTime: expreEndTime });
}
if (sessionProject) {
// const projectList = ref([]);
// store
if (!projectInfo.value.id) {
const info = JSON.parse(sessionProject);
store.commit('projects/setProject', info);
}
if (sessionProject && !projectInfo.value.id) {
// storestorestore
const info = JSON.parse(sessionProject);
store.commit('projects/setProject', info);
}
//
@ -145,7 +97,7 @@ watch([startTime, endTime, refreshProjects], () => {
getProjectsList();
});
//
//
const showActionCard = item => {
visible.value = true;
deleteId.value = item.id;
@ -155,102 +107,35 @@ const showActionCard = item => {
async function getProjectsList() {
try {
const data = await getProjects(startTime.value, endTime.value);
data.forEach(item => {
item.show = false;
if (item.id === projectInfo.value.id) {
if (item.sonProjectList.length > 0) {
item.show = true;
}
if (item.sonProjectList) {
item.sonProjectList.forEach(sonItem => {
sonItem.show = false;
if (sonItem.id === projectInfo.value.id) {
item.show = true;
sonItem.show = true;
}
if (sonItem.sonProjectList) {
sonItem.sonProjectList.forEach(val => {
val.show = false;
if (val.id === projectInfo.value.id) {
item.show = true;
sonItem.show = true;
val.show = true;
}
});
}
});
}
});
// projectList.value = [...data];
store.commit('projects/setProjects', data);
getSubProject();
} catch (error) {
message.info(error);
throw new Error(error);
}
}
function toDetail(item, sec, fir) {
item.show = true;
const obj = {
id: item.id,
name: item.name,
url: item.url,
};
// projectList.value.forEach(item => {
// item.show = false;
// if (item.id === data.id) {
// item.show = true;
// }
// if (item.sonProjectList) {
// item.sonProjectList.forEach(sonItem => {
// sonItem.show = false;
// if (sonItem.id === data.id) {
// item.show = true;
// sonItem.show = true;
// }
// if (sonItem.sonProjectList) {
// sonItem.sonProjectList.forEach(val => {
// val.show = false;
// if (val.id === data.id) {
// item.show = true;
// sonItem.show = true;
// val.show = true;
// }
// });
// }
// });
// }
// });
if (sec && fir) {
store.commit('layout/setFirPlanTime', { startTime: fir.startTime, endTime: fir.endTime });
store.commit('layout/setSecPlanTime', { startTime: sec.startTime, endTime: sec.endTime });
store.commit('layout/setThirdPlanTime', { startTime: item.startTime, endTime: item.endTime });
} else if (sec) {
store.commit('layout/setFirPlanTime', { startTime: sec.startTime, endTime: sec.endTime });
store.commit('layout/setSecPlanTime', { startTime: item.startTime, endTime: item.endTime });
} else {
store.commit('layout/setFirPlanTime', { startTime: item.startTime, endTime: item.endTime });
}
// store.commit('projects/setProjects', projectList.value);
store.commit('projects/setProject', obj);
store.commit('task/setTaskDetail', null);
//
function toDetail(item) {
clearRolesData();
clearTasksData();
store.commit('projects/setProject', item);
// store.commit('task/setTaskDetail', null);
}
//
function changeShow(item) {
item.show = !item.show;
}
//
//
async function handleOk() {
visible.value = false;
await deleteProject(deleteId.value);
@ -267,16 +152,18 @@ async function deleteProject(param) {
}
}
// ID
async function getSubProject() {
try {
const params = { param: { id: projectInfo.value.id } };
const data = await getExperimentation(params);
store.commit('projects/setExpreimentStatus', data ? data.status : 0);
} catch (error) {
message.info(error);
throw new Error(error);
}
function clearRolesData() {
store.commit('role/setInvisibleRoles', []);
store.commit('role/setVisibleRoles', []);
store.commit('role/setRoleId', '');
}
function clearTasksData() {
//
store.commit('task/setPermanents', []);
store.commit('task/setDailyTasks', []);
//
store.commit('task/clearTasks');
}
</script>
@ -325,10 +212,6 @@ async function getSubProject() {
width: calc(100% - 76px);
}
.name-box {
margin-bottom: 8px;
}
.name {
margin-right: 8px;
font-size: 14px;
@ -347,7 +230,6 @@ async function getSubProject() {
background-color: rgba(24, 144, 255, 0.2);
color: #1890ff;
font-size: 12px;
font-weight: 600;
}
.time {

43
src/components/tall/plugin/Plugin.vue

@ -0,0 +1,43 @@
<template>
<p-task-title :task="task" v-if="pluginId === '1'" class="p-2" />
<!-- 交付物插件 -->
<!-- <p-deliver v-else-if="pluginId === '15'" /> -->
<!-- <p-source-manage v-else-if="pluginId === '16'" class="p-2" /> -->
<!-- <p-finance-audit v-else-if="pluginId === '17'" class="p-2" /> -->
<!-- <p-finance v-else-if="pluginId === '18'" class="p-2" /> -->
<!-- 个人和终端按钮-->
<!-- <p-account-management v-else-if="pluginId === '19'" class="p-2" /> -->
<!-- <p-domain-source-manage v-else-if="pluginId === '20'" class="p-2" /> -->
<!-- <p-project-version-management v-else-if="pluginId === '21'" class="p-2" /> -->
<!-- 任务名和跳转详情页箭头 -->
<!-- <p-task-to-detail :task="task" v-else-if="pluginId === '24'" class="p-2"></p-task-to-detail> -->
</template>
<script setup>
import { computed, provide, defineProps } from 'vue';
import { useStore } from 'vuex';
import pTaskTitle from '@/plugins/p-task-title.vue';
const props = defineProps({
task: { default: () => {}, type: Object },
pluginId: { default: '1', type: String },
styleType: { default: 0, type: Number },
pluginTaskId: { default: '', type: String },
businessPluginId: { default: '', type: String },
param: { type: String, default: '' },
pluginInfo: { default: () => {}, type: Object },
});
provide('task', props.task);
provide('pluginInfo', props.pluginInfo);
const store = useStore();
// const roleId = computed(() => store.state.role.roleId);
// const token = computed(() => store.state.user.token);
// const userId = computed(() => store.getters['user/userId']);
const projectId = computed(() => store.getters['project/projectId']);
console.log(projectId);
</script>

210
src/components/tall/task/AssignmentExperiment.vue

@ -1,210 +0,0 @@
<template>
<div class="task-form bg-white border-radius-10">
<a-form ref="formRef" :model="assignExperFormData">
<a-form-item>
<label class="color-3">实验名称</label>
<a-input v-model:value="assignExperFormData.name" placeholder="实验名称" />
</a-form-item>
<a-form-item>
<label class="color-3">完成期限</label>
<a-space direction="vertical" :size="12">
<a-range-picker v-model:value="assignExperFormData.date" />
</a-space>
</a-form-item>
<a-form-item>
<label class="color-3">负责人</label>
<a-select
v-model:value="assignExperFormData.memberId"
show-search
optionFilterProp="label"
placeholder="负责人"
:options="options"
:filter-option="filterOption"
@search="handleSearch"
:getPopupContainer="
triggerNode => {
return triggerNode.parentNode || document.body;
}
"
></a-select>
</a-form-item>
<a-form-item>
<label class="color-3">实验目标</label>
<a-textarea v-model:value="assignExperFormData.target" placeholder="实验目标" />
</a-form-item>
<a-form-item class="text-right">
<a-button type="primary" html-type="submit" @click="onSubmit">确定</a-button>
</a-form-item>
</a-form>
</div>
</template>
<script setup>
import { ref, computed, watch } from 'vue';
import { useStore } from 'vuex';
import dayjs from 'dayjs';
import { createExperiment, memberQuery, getExperimentation } from 'apis';
import { message } from 'ant-design-vue';
const store = useStore();
const formRef = ref(null);
const projectId = computed(() => store.getters['projects/projectId']);
const options = ref([]);
const experimentationId = computed(() => store.state.task.experimentationId); // ID
//
// const subStartTime = computed(() => store.state.layout.subStartTime);
// const subEndTime = computed(() => store.state.layout.subEndTime);
// console.log(subStartTime.value, subEndTime.value);
if (experimentationId.value) {
getSubProject(experimentationId.value);
}
watch(experimentationId, async () => {
await getSubProject(experimentationId.value);
});
const assignExperFormData = ref({
projectId: projectId.value,
id: '',
name: '',
memberId: '',
date: [],
startTime: '',
endTime: '',
target: '',
});
getList(); //
const handleSearch = async value => {
await getList(value); //
};
const filterOption = (input, option) => {
return option.value.toLowerCase().indexOf(input.toLowerCase()) >= 0;
};
const onSubmit = async () => {
if (assignExperFormData.value.date.length > 0) {
assignExperFormData.value.date.forEach((item, index) => {
if (index === 0) {
assignExperFormData.value.startTime = dayjs(item).format('x');
} else {
assignExperFormData.value.endTime = dayjs(item).format('x');
}
});
// if (subStartTime.value && assignExperFormData.value.startTime < subStartTime.value) {
// message.info('');
// return false;
// }
// if (subEndTime.value && assignExperFormData.value.endTime > subEndTime.value) {
// message.info('');
// return false;
// }
}
try {
const params = { param: assignExperFormData.value };
await createExperiment(params);
store.commit('layout/setThirdPlanTime', { startTime: assignExperFormData.value.startTime, endTime: assignExperFormData.value.endTime });
await getSubProject(experimentationId.value);
} catch (error) {
message.info(error);
}
};
//
async function getList(name) {
try {
const params = { param: { projectId: projectId.value, name } };
const data = await memberQuery(params);
store.commit('task/setMembers', data);
options.value = [];
data.forEach(item => {
const obj = {
label: item.memberName,
value: item.memberId,
};
options.value.push(obj);
});
} catch (error) {
message.info(error);
throw new Error(error);
}
}
async function getSubProject(id) {
try {
const params = { param: { id } };
const data = await getExperimentation(params);
store.commit('layout/setRefreshProjects');
if (data) {
data.date = [];
if (data.startTime) {
const start = dayjs(Number(data.startTime));
const end = dayjs(Number(data.endTime));
data.date = [start, end];
}
data.projectId = projectId.value;
assignExperFormData.value = data;
} else {
assignExperFormData.value = {
projectId: projectId.value,
id: '',
name: '',
memberId: '',
date: [],
startTime: '',
endTime: '',
target: '',
};
}
} catch (error) {
message.info(error);
throw new Error(error);
}
}
</script>
<style scoped>
.task-detail {
background-color: #fff;
}
.ant-col {
margin-top: 10px;
}
.ant-col:nth-child(-n + 4) {
margin-top: 2px;
}
.deliverables .ant-input,
.deliverables-son .ant-input {
width: 23px;
height: 14px;
border-radius: 0;
padding: 0;
font-size: 12px;
color: #1890ff;
text-align: center;
margin-left: 5px;
}
.deliverables-son {
margin-top: 10px !important;
}
</style>

453
src/components/tall/task/AssignmentSubject.vue

@ -1,453 +0,0 @@
<template>
<div class="task-form bg-white border-radius-10">
<a-form ref="formRef" :model="topicSubFormData">
<a-form-item>
<label class="color-3">子课题名称</label>
<a-input v-model:value="topicSubFormData.name" placeholder="子课题名称" />
</a-form-item>
<a-form-item>
<label class="color-3">子课题负责人</label>
<a-select
v-model:value="topicSubFormData.memberId"
show-search
optionFilterProp="label"
placeholder="负责人"
:options="options"
:filter-option="filterOption"
@search="handleSearch"
:getPopupContainer="
triggerNode => {
return triggerNode.parentNode || document.body;
}
"
></a-select>
</a-form-item>
<a-form-item>
<label class="color-3">完成期限</label>
<a-space direction="vertical" :size="12">
<a-range-picker v-model:value="topicSubFormData.date" />
</a-space>
</a-form-item>
<a-form-item class="form-item-dad">
<div class="flex items-center" style="margin-bottom: 5px">
<label class="color-3" style="margin-bottom: 0; margin-right: 8px">进度安排</label>
<PlusCircleOutlined style="color: #1890ff; font-size: 16px" @click="addMilestones" />
</div>
<div class="form-item-son" style="padding-left: 16px">
<div v-for="(item, index) in stageList" :key="index">
<a-form-item>
<label class="color-3">时间</label>
<a-space direction="vertical" :size="12">
<a-range-picker v-model:value="item.date" />
</a-space>
</a-form-item>
<a-form-item>
<label class="color-3">指标</label>
<a-checkbox-group v-model:value="item.checkContent">
<a-row>
<a-col :span="5" class="deliverables">
<a-checkbox value="1" @change="handleChange($event, item)">
<span class="color-6">论文</span>
<a-input v-model:value="item.thesis" @change="handleInput($event, item, '1')" />
</a-checkbox>
</a-col>
<a-col :span="15" class="deliverables">
<a-checkbox value="2" @change="handleChange($event, item)">
<span class="color-6">专利</span>
<a-input v-model:value="item.patent" @change="handleInput($event, item, '2')" />
</a-checkbox>
</a-col>
<a-col :span="4" class="deliverables">
<a-checkbox value="3" @change="handleChange($event, item)">
<span class="color-6">软著</span>
<a-input v-model:value="item.theSoft" @change="handleInput($event, item, '3')" />
</a-checkbox>
</a-col>
<a-col :span="5" class="deliverables-son" style="padding-left: 14px">
<a-checkbox value="4" @change="handleChange($event, item)">
<span class="color-6">SCI论文</span>
<a-input v-model:value="item.sciThesis" @change="handleInput($event, item, '4')" />
</a-checkbox>
</a-col>
<a-col :span="5" class="deliverables-son" style="padding-left: 14px">
<a-checkbox value="5" @change="handleChange($event, item)">
<span class="color-6">发明专利</span>
<a-input v-model:value="item.inventPatent" @change="handleInput($event, item, '5')" />
</a-checkbox>
</a-col>
<a-col :span="5" class="deliverables-son">
<a-checkbox value="6" @change="handleChange($event, item)">
<span class="color-6">实用新型</span>
<a-input v-model:value="item.practicalPatent" @change="handleInput($event, item, '6')" />
</a-checkbox>
</a-col>
<a-col :span="5" class="deliverables-son">
<a-checkbox value="7" @change="handleChange($event, item)">
<span class="color-6">外观专利</span>
<a-input v-model:value="item.facadePatent" @change="handleInput($event, item, '7')" />
</a-checkbox>
</a-col>
<a-col :span="4" class="deliverables-son"></a-col>
</a-row>
</a-checkbox-group>
</a-form-item>
</div>
</div>
</a-form-item>
<a-form-item class="text-right">
<a-button type="primary" html-type="submit" @click="onSubmit">确定</a-button>
</a-form-item>
</a-form>
</div>
</template>
<script setup>
import { ref, computed, watch } from 'vue';
import { useStore } from 'vuex';
import { PlusCircleOutlined } from '@ant-design/icons-vue';
import { message } from 'ant-design-vue';
import { saveSubExperiment, memberQuery, getSubExperiment } from 'apis';
import dayjs from 'dayjs';
const store = useStore();
const formRef = ref(null);
const projectId = computed(() => store.getters['projects/projectId']); // ID
const options = ref([]);
const detailId = computed(() => store.state.task.detailId); // ID
//
const planStartTime = computed(() => store.state.layout.planStartTime);
const planEndTime = computed(() => store.state.layout.planEndTime);
const topicSubFormData = ref({
projectId: projectId.value,
id: detailId.value,
name: '',
memberId: '',
date: [],
startTime: '',
endTime: '',
stageDtoList: [],
});
const stageList = ref([
{
date: [],
startTime: '',
endTime: '',
checkContent: [],
thesis: '',
sciThesis: '',
patent: '',
inventPatent: '',
practicalPatent: '',
facadePatent: '',
theSoft: '',
},
]);
if (detailId.value) {
getSubProject(detailId.value);
} else {
renderData();
}
watch(detailId, async () => {
if (detailId.value) {
await getSubProject(detailId.value);
} else {
renderData();
}
});
getList(); //
//
function addMilestones() {
stageList.value.push({
date: [],
startTime: '',
endTime: '',
checkContent: [],
thesis: '',
sciThesis: '',
patent: '',
inventPatent: '',
practicalPatent: '',
facadePatent: '',
theSoft: '',
});
}
const handleChange = (e, data) => {
if (e.target.checked) {
if (e.target.value === '4') {
if (data.checkContent.indexOf('1') === -1) {
data.checkContent.push('1');
}
} else if (e.target.value === '5' || e.target.value === '6' || e.target.value === '7') {
if (data.checkContent.indexOf('2') === -1) {
data.checkContent.push('2');
}
}
} else if (e.target.value === '1') {
data.thesis = 0;
} else if (e.target.value === '2') {
data.patent = 0;
} else if (e.target.value === '3') {
data.theSoft = 0;
} else if (e.target.value === '4') {
data.sciThesis = 0;
} else if (e.target.value === '5') {
data.inventPatent = 0;
} else if (e.target.value === '6') {
data.practicalPatent = 0;
} else if (e.target.value === '7') {
data.facadePatent = 0;
}
};
const handleInput = (e, data, label) => {
if (e.data > 0) {
if (data.checkContent.indexOf(label) === -1) {
data.checkContent.push(label);
}
if (label === '4') {
if (data.checkContent.indexOf('1') === -1) data.checkContent.push('1');
}
if (label === '5' || label === '6' || label === '7') {
if (data.checkContent.indexOf('2') === -1) data.checkContent.push('2');
}
} else {
if (data.checkContent.indexOf(label) > -1) {
data.checkContent.splice(data.checkContent.indexOf(label), 1);
}
if (label === '4') {
if (data.checkContent.indexOf('1') > -1) data.checkContent.splice(data.checkContent.indexOf('1'), 1);
}
if (label === '5' || label === '6' || label === '7') {
if (data.checkContent.indexOf('2') > -1) data.checkContent.splice(data.checkContent.indexOf('2'), 1);
}
}
};
const handleSearch = async value => {
await getList(value); //
};
const filterOption = (input, option) => {
return option.value.toLowerCase().indexOf(input.toLowerCase()) >= 0;
};
//
async function getList(name) {
try {
const params = { param: { projectId: projectId.value, name } };
const data = await memberQuery(params);
store.commit('task/setMembers', data);
options.value = [];
data.forEach(item => {
const obj = {
label: item.memberName,
value: item.memberId,
};
options.value.push(obj);
});
} catch (error) {
message.info(error);
throw new Error(error);
}
}
const onSubmit = async () => {
let msgText = '';
if (topicSubFormData.value.date) {
topicSubFormData.value.date.forEach((item, index) => {
if (index === 0) {
topicSubFormData.value.startTime = dayjs(item).format('x');
} else {
topicSubFormData.value.endTime = dayjs(item).format('x');
}
});
if (topicSubFormData.value.startTime < planStartTime.value) {
msgText = '子课题开始时间不能小于项目起始时间';
}
if (topicSubFormData.value.endTime > planEndTime.value) {
msgText = '子课题结束时间不能大于项目终止时间';
}
}
stageList.value.forEach(item => {
if (item.date.length > 0) {
item.date.forEach((val, key) => {
if (key === 0) {
item.startTime = dayjs(val).format('x');
} else {
item.endTime = dayjs(val).format('x');
}
});
if (item.startTime < topicSubFormData.value.startTime || item.endTime > topicSubFormData.value.endTime) {
msgText = '子课题进度安排起止必须在子课题起止时间';
}
}
if (item.checkContent.indexOf('1') > -1) {
if (!item.thesis) msgText = '请填写论文数量';
}
if (item.checkContent.indexOf('2') > -1) {
if (!item.patent) msgText = '请填写专利数量';
}
if (item.checkContent.indexOf('3') > -1) {
if (!item.theSoft) msgText = '请填写软著数量';
}
if (item.checkContent.indexOf('4') > -1) {
if (!item.sciThesis) msgText = '请填写SCI论文数量';
}
if (item.checkContent.indexOf('5') > -1) {
if (!item.inventPatent) msgText = '请填写发明专利数量';
}
if (item.checkContent.indexOf('6') > -1) {
if (!item.practicalPatent) msgText = '请填写实用新型专利数量';
}
if (item.checkContent.indexOf('7') > -1) {
if (!item.facadePatent) msgText = '请填写外观专利数量';
}
if (item.thesis < item.sciThesis) msgText = 'SCI论文数量不能比总论文数量大';
const totalPatent = Number(item.inventPatent) + Number(item.practicalPatent) + Number(item.facadePatent);
if (item.patent < totalPatent) msgText = '发明专利数量、实用新型专利数量、外观专利数量总和不能比总专利数量大';
});
if (msgText) {
message.info(msgText);
return false;
}
topicSubFormData.value.stageDtoList = [...stageList.value];
const params = { param: topicSubFormData.value };
await saveSubExperiment(params);
store.commit('layout/setRefreshProjects');
store.commit('layout/setSecPlanTime', { startTime: topicSubFormData.value.startTime, endTime: topicSubFormData.value.endTime });
if (detailId.value) {
getSubProject(detailId.value);
} else {
renderData();
}
};
async function getSubProject(id) {
try {
const params = { param: { taskDetailId: id } };
const data = await getSubExperiment(params);
if (data) {
const start = dayjs(Number(data.startTime));
const end = dayjs(Number(data.endTime));
data.date = [start, end];
data.projectId = projectId.value;
topicSubFormData.value = data;
data.subExperimentStageDtoList.forEach(item => {
item.startTime = item.stageStartTime;
item.endTime = item.stageEndTime;
console.log(item.startTime !== '0');
if (item.startTime !== '0') {
item.date = [dayjs(Number(item.startTime)), dayjs(Number(item.endTime))];
}
item.checkContent = [];
if (item.thesis) item.checkContent.push('1');
if (item.patent) item.checkContent.push('2');
if (item.theSoft) item.checkContent.push('3');
if (item.sciThesis) item.checkContent.push('4');
if (item.inventPatent) item.checkContent.push('5');
if (item.practicalPatent) item.checkContent.push('6');
if (item.facadePatent) item.checkContent.push('7');
});
stageList.value = data.subExperimentStageDtoList;
} else {
//
renderData();
}
} catch (error) {
message.info(error);
throw new Error(error);
}
}
//
function renderData() {
topicSubFormData.value = {
projectId: projectId.value,
id: detailId.value,
name: '',
memberId: '',
date: [],
startTime: '',
endTime: '',
stageDtoList: [],
};
stageList.value = [
{
date: [],
startTime: '',
endTime: '',
checkContent: [],
thesis: '',
sciThesis: '',
patent: '',
inventPatent: '',
practicalPatent: '',
facadePatent: '',
theSoft: '',
},
];
}
</script>
<style scoped>
.task-detail {
background-color: #fff;
}
.ant-col {
margin-top: 10px;
}
.ant-col:nth-child(-n + 4) {
margin-top: 2px;
}
.deliverables .ant-input,
.deliverables-son .ant-input {
width: 23px;
height: 14px;
border-radius: 0;
padding: 0;
font-size: 12px;
color: #1890ff;
text-align: center;
margin-left: 5px;
}
.deliverables-son {
margin-top: 10px !important;
}
</style>

192
src/components/tall/task/CheckSubjectProgress.vue

@ -1,192 +0,0 @@
<template>
<div class="task-progress flex flex-wrap justify-between">
<div class="wrap overflow-hidden">
<a-card title="任务目标">
<div class="flex flex-wrap justify-center">
<div class="achievements border border-right border-bottom text-center">
<p class="num">{{ infoOne.thesis }}/{{ infoOne.totalThesis }}</p>
<p class="name">论文</p>
</div>
<div class="achievements border border-bottom text-center">
<p class="num">{{ infoOne.patent }}/{{ infoOne.totalPatent }}</p>
<p class="name">专利</p>
</div>
<div class="achievements border border-right text-center">
<p class="num">{{ infoOne.theSoft }}/{{ infoOne.totalTheSoft }}</p>
<p class="name">软著</p>
</div>
<div class="achievements border text-center">
<p class="num">{{ infoOne.meeting }}/{{ infoOne.totalMeeting }}</p>
<p class="name">会议</p>
</div>
</div>
</a-card>
</div>
<div class="wrap overflow-hidden">
<a-card title="概览">
<div class="topic">
<p>{{ infoOne.name }}</p>
<a-progress
:percent="infoOne.masterSchedule"
:strokeWidth="22"
:show-info="false"
:stroke-color="'#1890FF'"
:trail-color="'rgba(24, 144, 255, 0.2)'"
/>
</div>
<div class="sub-topic flex justify-between flex-wrap">
<div class="topic" v-for="(item, index) in infoSec" :key="index">
<p>{{ item.name }}</p>
<a-progress
:percent="item.masterSchedule"
:strokeWidth="22"
:show-info="false"
:stroke-color="colorList[index % 4].color"
:trail-color="colorList[index % 4].bgColor"
/>
</div>
</div>
</a-card>
</div>
<div class="wrap overflow-hidden" v-for="(item, index) in infoSec" :key="index">
<a-card :title="item.name">
<div class="flex flex-wrap justify-center">
<div class="achievements border border-right border-bottom text-center">
<p class="num">{{ item.thesis }}/{{ item.totalThesis }}</p>
<p class="name">论文</p>
</div>
<div class="achievements border border-bottom text-center">
<p class="num">{{ item.patent }}/{{ item.totalPatent }}</p>
<p class="name">专利</p>
</div>
<div class="achievements border border-right text-center">
<p class="num">{{ item.theSoft }}/{{ item.totalTheSoft }}</p>
<p class="name">软著</p>
</div>
<div class="achievements border text-center">
<p class="num">{{ item.meeting }}/{{ item.totalMeeting }}</p>
<p class="name">会议</p>
</div>
</div>
</a-card>
</div>
</div>
</template>
<script setup>
import { ref, computed } from 'vue';
import { getExperimentProgress } from 'apis';
import { useStore } from 'vuex';
import { message } from 'ant-design-vue';
const store = useStore();
const projectId = computed(() => store.getters['projects/projectId']);
const infoOne = ref({});
const infoSec = ref([]);
const colorList = ref([
{ color: '#FF9191', bgColor: 'rgba(255, 145, 145, 0.2)' },
{ color: '#FF934B', bgColor: 'rgba(255, 147, 75, 0.2)' },
{ color: '#B991FF', bgColor: 'rgba(185, 145, 255, 0.2)' },
{ color: '#91C1FF', bgColor: 'rgba(145, 193, 255, 0.2)' },
]);
progress();
async function progress() {
try {
const params = { param: { projectId: projectId.value } };
const data = await getExperimentProgress(params);
infoOne.value = data.target;
infoSec.value = data.subTargetList;
} catch (error) {
message.info(error);
throw new Error(error);
}
}
</script>
<style scoped>
.wrap {
margin-top: 16px;
width: calc((100% - 16px) / 2);
background-color: #fff;
border-radius: 10px;
border: 1px solid #cccccc;
}
.wrap:nth-child(-n + 2) {
margin-top: 0;
}
:deep(.ant-card-head) {
padding: 0 16px;
min-height: 45px;
max-height: 45px;
border-color: #cccccc;
font-size: 14px;
}
:deep(.ant-card-head-title) {
padding: 0;
line-height: 45px;
}
.wrap .num {
font-size: 22px;
color: #4b8aff;
}
.wrap .name {
color: #666666;
}
.wrap p {
margin: 0;
}
.border {
border-color: #fff;
}
.border-right {
border-right-color: #cccccc;
}
.border-left {
border-left-color: #cccccc;
}
.border-top {
border-top-color: #cccccc;
}
.border-bottom {
border-bottom-color: #cccccc;
}
.achievements {
width: 40%;
padding: 25px 0;
}
.topic p {
margin-bottom: 8px;
color: #666666;
}
.sub-topic .topic {
margin-top: 40px;
width: calc((100% - 32px) / 2);
}
.ant-card {
height: 100%;
}
.ant-card :deep(.ant-card-body) {
height: calc(100% - 48px);
display: flex;
flex-direction: column;
justify-content: center;
}
</style>

246
src/components/tall/task/Conclusion.vue

@ -1,246 +0,0 @@
<template>
<div class="task-form bg-white border-radius-10">
<a-form>
<div v-for="(item, index) in questionList" :key="index">
<template v-if="item.type === 1">
<a-form-item>
<label class="color-3">{{ item.question }}</label>
<a-input v-model:value="item.con" :placeholder="item.question" />
</a-form-item>
</template>
<template v-if="item.type === 2">
<a-form-item>
<label class="color-3">{{ item.question }}</label>
<a-textarea v-model:value="item.con" :placeholder="item.question" />
</a-form-item>
</template>
<template v-if="item.type === 3"> </template>
<template v-if="item.type === 4"> </template>
<template v-if="item.type === 5"> </template>
<template v-if="item.type === 6">
<a-form-item>
<label class="color-3">{{ item.question }}</label>
<a-date-picker v-model:value="item.date" />
</a-form-item>
</template>
<template v-if="item.type === 7">
<a-form-item>
<label class="color-3">{{ item.question }}</label>
<a-upload-dragger
v-model:fileList="item.files"
name="param"
:multiple="true"
:action="action"
:headers="headers"
:accept="'.pdf'"
:before-upload="beforeUpload(index)"
@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>
</template>
</div>
<a-form-item class="text-right">
<a-button type="primary" html-type="submit" @click="onSubmit">上传项目结题报告</a-button>
</a-form-item>
</a-form>
</div>
</template>
<script setup>
import { ref, computed } from 'vue';
import { useStore } from 'vuex';
import dayjs from 'dayjs';
import { InboxOutlined } from '@ant-design/icons-vue';
import { uploadImg } from 'apis';
import { message } from 'ant-design-vue';
const store = useStore();
//
const token = computed(() => store.getters['user/token']);
const headers = { Authorization: `Bearer ${token.value}` };
const action = uploadImg;
//
const projectId = computed(() => store.getters['projects/projectId']); // ID
const code = computed(() => store.state.task.label); // code
const questionList = ref([]);
//
const currIndex = ref(null);
//
const planStartTime = computed(() => store.state.layout.planStartTime);
const planEndTime = computed(() => store.state.layout.planEndTime);
getDataByCode();
const beforeUpload = index => {
currIndex.value = index;
};
const handleChange = info => {
const resFileList = [...info.fileList];
//
const arr = ref([]);
resFileList.forEach(file => {
let num = -1;
arr.value.forEach((item, index) => {
if (file.name === item.name) {
num = index;
}
});
if (num > -1) {
arr.value.splice(num, 1);
}
arr.value.push(file);
});
//
arr.value = arr.value.map(file => {
if (file.response) {
file.url = file.response.data.filePath;
file.id = file.response.data.fileId;
}
return file;
});
questionList.value[currIndex.value].files = arr.value;
};
const onSubmit = async () => {
const params = {
param: {
code: code.value,
projectId: projectId.value,
questionAndAnswerList: [],
},
};
const arr = [];
questionList.value.forEach(item => {
const obj = {
questionId: item.questionId,
answerList: [],
};
if (item.type === 1 || item.type === 2) {
obj.answerList.push(item.con);
}
if (item.type === 6 && item.date) {
const time = dayjs(item.date).format('x');
if (time < planStartTime.value || time > planEndTime.value) {
message.info('中期检查时间必须在项目起止时间之内');
return false;
}
obj.answerList.push(time);
}
if (item.type === 7) {
item.files.forEach(val => {
const file = {
id: val.id,
name: val.name,
url: val.url,
};
obj.answerList.push(JSON.stringify(file));
});
}
arr.push(obj);
});
params.param.questionAndAnswerList = arr;
await store.dispatch('task/submitAnswer', params);
getDataByCode();
};
async function getDataByCode() {
const params = {
param: {
code: code.value,
projectId: projectId.value,
intellectualId: null,
},
};
const data = await store.dispatch('task/getByCode', params);
data.forEach(item => {
if (item.type === 1 || item.type === 2) {
item.con = '';
if (item.answerList.length > 0) {
item.con = item.answerList[0].answer;
}
}
if (item.type === 6) {
item.date = null;
if (item.answerList.length > 0) {
if (item.answerList[0].answer) {
item.date = dayjs(Number(item.answerList[0].answer)).format('YYYY-MM-DD');
item.date = dayjs(item.date, 'YYYY-MM-DD');
}
}
}
if (item.type === 7) {
item.files = [];
if (item.answerList.length > 0) {
item.answerList.forEach(val => {
if (val.answer) val.answer = JSON.parse(val.answer);
item.files.push(val.answer);
});
}
}
});
questionList.value = data;
}
</script>
<style scoped>
.task-detail {
background-color: #fff;
}
.ant-col {
margin-top: 10px;
}
.ant-col:nth-child(-n + 4) {
margin-top: 2px;
}
.deliverables .ant-input,
.deliverables-son .ant-input {
width: 23px;
height: 14px;
border-radius: 0;
padding: 0;
font-size: 12px;
color: #1890ff;
text-align: center;
margin-left: 5px;
}
.deliverables-son {
margin-top: 10px !important;
}
</style>

235
src/components/tall/task/ContractManagement.vue

@ -1,235 +0,0 @@
<template>
<div class="task-form bg-white border-radius-10">
<a-form>
<div v-for="(item, index) in questionList" :key="index">
<template v-if="item.type === 1">
<a-form-item>
<label class="color-3">{{ item.question }}</label>
<a-input v-model:value="item.con" :placeholder="item.question" />
</a-form-item>
</template>
<template v-if="item.type === 2">
<a-form-item>
<label class="color-3">{{ item.question }}</label>
<a-textarea v-model:value="item.con" :placeholder="item.question" />
</a-form-item>
</template>
<template v-if="item.type === 3"> </template>
<template v-if="item.type === 4"> </template>
<template v-if="item.type === 5"> </template>
<template v-if="item.type === 6">
<a-form-item>
<label class="color-3">{{ item.question }}</label>
<a-date-picker v-model:value="item.date" />
</a-form-item>
</template>
<template v-if="item.type === 7">
<a-form-item>
<label class="color-3">{{ item.question }}</label>
<a-upload-dragger
v-model:fileList="item.files"
name="param"
:multiple="true"
:action="action"
:headers="headers"
:accept="'.pdf'"
:before-upload="beforeUpload(index)"
@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>
</template>
</div>
<a-form-item class="text-right">
<a-button type="primary" html-type="submit" @click="onSubmit">上传合同扫描件 </a-button>
</a-form-item>
</a-form>
</div>
</template>
<script setup>
import { ref, computed } from 'vue';
import { useStore } from 'vuex';
import dayjs from 'dayjs';
import { InboxOutlined } from '@ant-design/icons-vue';
import { uploadImg } from 'apis';
const store = useStore();
//
const token = computed(() => store.getters['user/token']);
const headers = { Authorization: `Bearer ${token.value}` };
const action = uploadImg;
//
const projectId = computed(() => store.getters['projects/projectId']); // ID
const code = computed(() => store.state.task.label); // code
const questionList = ref([]);
//
const currIndex = ref(null);
getDataByCode();
const beforeUpload = index => {
currIndex.value = index;
};
const handleChange = info => {
const resFileList = [...info.fileList];
//
const arr = ref([]);
resFileList.forEach(file => {
let num = -1;
arr.value.forEach((item, index) => {
if (file.name === item.name) {
num = index;
}
});
if (num > -1) {
arr.value.splice(num, 1);
}
arr.value.push(file);
});
//
arr.value = arr.value.map(file => {
if (file.response) {
file.url = file.response.data.filePath;
file.id = file.response.data.fileId;
}
return file;
});
questionList.value[currIndex.value].files = arr.value;
};
const onSubmit = async () => {
const params = {
param: {
code: code.value,
projectId: projectId.value,
questionAndAnswerList: [],
},
};
const arr = [];
questionList.value.forEach(item => {
const obj = {
questionId: item.questionId,
answerList: [],
};
if (item.type === 1 || item.type === 2) {
obj.answerList.push(item.con);
}
if (item.type === 6 && item.date) {
obj.answerList.push(dayjs(item.date).format('x'));
}
if (item.type === 7) {
item.files.forEach(val => {
const file = {
id: val.id,
name: val.name,
url: val.url,
};
obj.answerList.push(JSON.stringify(file));
});
}
arr.push(obj);
});
params.param.questionAndAnswerList = arr;
await store.dispatch('task/submitAnswer', params);
getDataByCode();
};
async function getDataByCode() {
const params = {
param: {
code: code.value,
projectId: projectId.value,
intellectualId: null,
},
};
const data = await store.dispatch('task/getByCode', params);
data.forEach(item => {
if (item.type === 1 || item.type === 2) {
item.con = '';
if (item.answerList.length > 0) {
item.con = item.answerList[0].answer;
}
}
if (item.type === 6) {
item.date = null;
if (item.answerList.length > 0) {
if (item.answerList[0].answer) {
item.date = dayjs(Number(item.answerList[0].answer)).format('YYYY-MM-DD');
item.date = dayjs(item.date, 'YYYY-MM-DD');
}
}
}
if (item.type === 7) {
item.files = [];
if (item.answerList.length > 0) {
item.answerList.forEach(val => {
if (val.answer) val.answer = JSON.parse(val.answer);
item.files.push(val.answer);
});
}
}
});
questionList.value = data;
}
</script>
<style scoped>
.task-detail {
background-color: #fff;
}
.ant-col {
margin-top: 10px;
}
.ant-col:nth-child(-n + 4) {
margin-top: 2px;
}
.deliverables .ant-input,
.deliverables-son .ant-input {
width: 23px;
height: 14px;
border-radius: 0;
padding: 0;
font-size: 12px;
color: #1890ff;
text-align: center;
margin-left: 5px;
}
.deliverables-son {
margin-top: 10px !important;
}
</style>

172
src/components/tall/task/DataUnlock.vue

@ -1,172 +0,0 @@
<template>
<div class="flex flex-wrap">
<a-card title="解锁申请" v-for="(item, index) in unlockList" :key="index">
<p>
<span class="color-9">申请人</span><span class="color-3">{{ item.proposer }}</span>
</p>
<p>
<span class="color-9">实验名称</span><span class="color-3">{{ item.experimentName }}</span>
</p>
<p>
<span class="color-9">申请时间</span><span class="color-3">{{ dayjs(Number(item.time)).format('YYYY-MM-DD HH:mm') }}</span>
</p>
<p>
<span class="color-9">申请原因</span><span class="color-3">{{ item.remark }}</span>
</p>
<div class="flex justify-end">
<a-button class="action-btn edit-btn" type="primary" @click="toExamine(0, item.experimentId)">通过</a-button>
<a-button class="action-btn del-btn" type="primary" @click="showModal(item.experimentId)">驳回</a-button>
</div>
</a-card>
</div>
<!-- 拒绝模态框 -->
<a-modal v-model:visible="visible" :closable="false" @ok="handleOk">
<div class="modal-title flex items-center">
<CloseCircleFilled style="margin-right: 8px; font-size: 18px; color: #ff5353" />
<span class="color-3" style="font-size: 18px; font-weight: 600">确定要驳回该条解锁申请吗</span>
</div>
<div class="modal-con color-9" style="padding-left: 24px; margin-top: 16px">
<div style="margin-bottom: 5px; font-size: 16px; line-height: 26px">驳回原因</div>
<a-textarea v-model:value="remark" placeholder="驳回原因" :auto-size="{ minRows: 2, maxRows: 5 }" />
</div>
</a-modal>
</template>
<script setup>
import { ref, computed } from 'vue';
import { useStore } from 'vuex';
import dayjs from 'dayjs';
import { retrospectUnlock, examineUnlock } from 'apis';
import { CloseCircleFilled } from '@ant-design/icons-vue';
import { message } from 'ant-design-vue';
const store = useStore();
const projectId = computed(() => store.getters['projects/projectId']);
const unlockList = ref([]);
const visible = ref(false);
const remark = ref(null);
const experimentId = ref(null);
getUnlockList();
const showModal = id => {
visible.value = true;
experimentId.value = id;
};
const handleOk = e => {
console.log(e);
visible.value = false;
toExamine(1, experimentId.value);
};
//
async function getUnlockList() {
try {
const params = { param: { projectId: projectId.value } };
const data = await retrospectUnlock(params);
unlockList.value = data;
} catch (error) {
message.info(error);
throw new Error(error);
}
}
//
async function toExamine(type, id) {
try {
const params = {
param: {
projectId: projectId.value,
type,
experimentId: id,
remark: remark.value,
},
};
const data = await examineUnlock(params);
unlockList.value = data;
getUnlockList();
} catch (error) {
message.info(error);
throw new Error(error);
}
}
</script>
<style scoped>
.ant-card {
border-radius: 10px;
margin-right: 16px;
margin-bottom: 16px;
}
@media screen and (max-width: 1650px) {
.ant-card {
width: calc(50% - 8px);
}
.ant-card:nth-child(2n) {
margin-right: 0;
}
}
@media screen and (min-width: 1651px) {
.ant-card {
width: calc((100% - 32px) / 3);
}
.ant-card:nth-child(3n) {
margin-right: 0;
}
}
:deep(.ant-card-head) {
padding: 0 16px;
height: 45px;
min-height: 45px;
}
:deep(.ant-card-head-title) {
padding: 0;
line-height: 45px;
font-size: 14px;
color: #333;
}
:deep(.ant-card-body) {
padding: 24px 16px;
}
:deep(.ant-card-body) p {
line-height: 1;
margin-bottom: 16px;
}
:deep(.ant-card-body) p span:first-of-type {
display: inline-block;
width: 80px;
}
.action-btn {
width: 50px !important;
height: 28px !important;
font-size: 14px !important;
padding: 0;
letter-spacing: 0 !important;
}
.edit-btn {
background: #0dc26c;
border: 0;
}
.del-btn {
margin-left: 16px;
background: #ff5353;
border: 0;
}
</style>

287
src/components/tall/task/ExperimentalCode.vue

@ -1,287 +0,0 @@
<template>
<div class="task-form bg-white border-radius-10">
<a-form>
<div v-for="(item, index) in questionList" :key="index">
<template v-if="item.type === 1">
<a-form-item>
<label class="color-3">{{ item.question }}</label>
<a-input v-model:value="item.con" :placeholder="item.question" />
</a-form-item>
</template>
<template v-if="item.type === 2">
<a-form-item>
<label class="color-3">{{ item.question }}</label>
<a-textarea v-model:value="item.con" :placeholder="item.question" />
</a-form-item>
</template>
<template v-if="item.type === 3"> </template>
<template v-if="item.type === 4"> </template>
<template v-if="item.type === 5"> </template>
<template v-if="item.type === 6">
<a-form-item>
<label class="color-3">{{ item.question }}</label>
<a-date-picker v-model:value="item.date" />
</a-form-item>
</template>
<template v-if="item.type === 7">
<a-form-item>
<label class="color-3">{{ item.question }}</label>
<a-upload-dragger
v-model:fileList="item.files"
name="param"
:multiple="true"
:action="action"
:headers="headers"
:accept="'.pdf'"
:before-upload="beforeUpload(index)"
@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>
</template>
</div>
<a-form-item class="text-right">
<a-button type="primary" html-type="submit" @click="onSubmit" :disabled="expreStatus == 0 || expreStatus == 2 ? false : true">
确定
</a-button>
</a-form-item>
</a-form>
<a-alert v-if="isShowSuccess" :message="tipsMessage" type="success" />
<a-alert v-if="isShowWarning" :message="tipsMessage" type="warning" />
</div>
</template>
<script setup>
import { ref, computed } from 'vue';
import { useStore } from 'vuex';
import dayjs from 'dayjs';
import { InboxOutlined } from '@ant-design/icons-vue';
import { uploadImg } from 'apis';
// import { message } from 'ant-design-vue';
const store = useStore();
//
const token = computed(() => store.getters['user/token']);
const headers = { Authorization: `Bearer ${token.value}` };
const action = uploadImg;
//
const projectId = computed(() => store.getters['projects/projectId']); // ID
const code = computed(() => store.state.task.label); // code
const questionList = ref([]);
//
const currIndex = ref(null);
const isShowSuccess = ref(false);
const isShowWarning = ref(false);
const tipsMessage = ref('');
//
// const expreStartTime = computed(() => store.state.layout.expreStartTime);
// const expreEndTime = computed(() => store.state.layout.expreEndTime);
//
const expreStatus = computed(() => store.state.projects.expreStatus);
const sessionStatus = sessionStorage.getItem('expreStatus');
if (!expreStatus.value && sessionStatus) {
store.commit('projects/setExpreimentStatus', sessionStatus);
}
if (expreStatus.value) {
if (expreStatus.value === 1 || expreStatus.value === 3 || expreStatus.value === 4) {
isShowWarning.value = true;
tipsMessage.value = '数据已锁定,不可操作';
} else {
isShowSuccess.value = true;
tipsMessage.value = '数据未锁定,可操作';
}
setTimeout(() => {
isShowWarning.value = false;
isShowSuccess.value = false;
}, 3000);
}
getDataByCode();
const beforeUpload = index => {
currIndex.value = index;
};
const handleChange = info => {
const resFileList = [...info.fileList];
//
const arr = ref([]);
resFileList.forEach(file => {
let num = -1;
arr.value.forEach((item, index) => {
if (file.name === item.name) {
num = index;
}
});
if (num > -1) {
arr.value.splice(num, 1);
}
arr.value.push(file);
});
//
arr.value = arr.value.map(file => {
if (file.response) {
file.url = file.response.data.filePath;
file.id = file.response.data.fileId;
}
return file;
});
questionList.value[currIndex.value].files = arr.value;
};
const onSubmit = async () => {
const params = {
param: {
code: code.value,
projectId: projectId.value,
questionAndAnswerList: [],
},
};
const arr = [];
questionList.value.forEach(item => {
const obj = {
questionId: item.questionId,
answerList: [],
};
if (item.type === 1 || item.type === 2) {
obj.answerList.push(item.con);
}
if (item.type === 6 && item.date) {
const time = dayjs(item.date).format('x');
// if (time < expreStartTime.value || time > expreEndTime.value) {
// message.info('');
// return false;
// }
obj.answerList.push(time);
}
if (item.type === 7) {
item.files.forEach(val => {
const file = {
id: val.id,
name: val.name,
url: val.url,
};
obj.answerList.push(JSON.stringify(file));
});
}
arr.push(obj);
});
params.param.questionAndAnswerList = arr;
await store.dispatch('task/submitAnswer', params);
getDataByCode();
};
async function getDataByCode() {
const params = {
param: {
code: code.value,
projectId: projectId.value,
intellectualId: null,
},
};
const data = await store.dispatch('task/getByCode', params);
data.forEach(item => {
if (item.type === 1 || item.type === 2) {
item.con = '';
if (item.answerList.length > 0) {
item.con = item.answerList[0].answer;
}
}
if (item.type === 6) {
item.date = null;
if (item.answerList.length > 0) {
if (item.answerList[0].answer) {
item.date = dayjs(Number(item.answerList[0].answer)).format('YYYY-MM-DD');
item.date = dayjs(item.date, 'YYYY-MM-DD');
}
}
}
if (item.type === 7) {
item.files = [];
if (item.answerList.length > 0) {
item.answerList.forEach(val => {
if (val.answer) val.answer = JSON.parse(val.answer);
item.files.push(val.answer);
});
}
}
});
questionList.value = data;
}
</script>
<style scoped>
.task-form {
position: relative;
}
.task-form :deep(.ant-alert) {
position: absolute;
right: 30px;
}
.task-detail {
background-color: #fff;
}
.ant-col {
margin-top: 10px;
}
.ant-col:nth-child(-n + 4) {
margin-top: 2px;
}
.deliverables .ant-input,
.deliverables-son .ant-input {
width: 23px;
height: 14px;
border-radius: 0;
padding: 0;
font-size: 12px;
color: #1890ff;
text-align: center;
margin-left: 5px;
}
.deliverables-son {
margin-top: 10px !important;
}
</style>

287
src/components/tall/task/ExperimentalData.vue

@ -1,287 +0,0 @@
<template>
<div class="task-form bg-white border-radius-10">
<a-form>
<div v-for="(item, index) in questionList" :key="index">
<template v-if="item.type === 1">
<a-form-item>
<label class="color-3">{{ item.question }}</label>
<a-input v-model:value="item.con" :placeholder="item.question" />
</a-form-item>
</template>
<template v-if="item.type === 2">
<a-form-item>
<label class="color-3">{{ item.question }}</label>
<a-textarea v-model:value="item.con" :placeholder="item.question" />
</a-form-item>
</template>
<template v-if="item.type === 3"> </template>
<template v-if="item.type === 4"> </template>
<template v-if="item.type === 5"> </template>
<template v-if="item.type === 6">
<a-form-item>
<label class="color-3">{{ item.question }}</label>
<a-date-picker v-model:value="item.date" />
</a-form-item>
</template>
<template v-if="item.type === 7">
<a-form-item>
<label class="color-3">{{ item.question }}</label>
<a-upload-dragger
v-model:fileList="item.files"
name="param"
:multiple="true"
:action="action"
:headers="headers"
:accept="'.pdf'"
:before-upload="beforeUpload(index)"
@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>
</template>
</div>
<a-form-item class="text-right">
<a-button type="primary" html-type="submit" @click="onSubmit" :disabled="expreStatus == 0 || expreStatus == 2 ? false : true">
确定
</a-button>
</a-form-item>
</a-form>
<a-alert v-if="isShowSuccess" :message="tipsMessage" type="success" />
<a-alert v-if="isShowWarning" :message="tipsMessage" type="warning" />
</div>
</template>
<script setup>
import { ref, computed } from 'vue';
import { useStore } from 'vuex';
import dayjs from 'dayjs';
import { InboxOutlined } from '@ant-design/icons-vue';
import { uploadImg } from 'apis';
// import { message } from 'ant-design-vue';
const store = useStore();
//
const token = computed(() => store.getters['user/token']);
const headers = { Authorization: `Bearer ${token.value}` };
const action = uploadImg;
//
const projectId = computed(() => store.getters['projects/projectId']); // ID
const code = computed(() => store.state.task.label); // code
const questionList = ref([]);
//
const currIndex = ref(null);
const isShowSuccess = ref(false);
const isShowWarning = ref(false);
const tipsMessage = ref('');
//
// const expreStartTime = computed(() => store.state.layout.expreStartTime);
// const expreEndTime = computed(() => store.state.layout.expreEndTime);
//
const expreStatus = computed(() => store.state.projects.expreStatus);
const sessionStatus = sessionStorage.getItem('expreStatus');
if (!expreStatus.value && sessionStatus) {
store.commit('projects/setExpreimentStatus', sessionStatus);
}
if (expreStatus.value) {
if (expreStatus.value === 1 || expreStatus.value === 3 || expreStatus.value === 4) {
isShowWarning.value = true;
tipsMessage.value = '数据已锁定,不可操作';
} else {
isShowSuccess.value = true;
tipsMessage.value = '数据未锁定,可操作';
}
setTimeout(() => {
isShowWarning.value = false;
isShowSuccess.value = false;
}, 3000);
}
getDataByCode();
const beforeUpload = index => {
currIndex.value = index;
};
const handleChange = info => {
const resFileList = [...info.fileList];
//
const arr = ref([]);
resFileList.forEach(file => {
let num = -1;
arr.value.forEach((item, index) => {
if (file.name === item.name) {
num = index;
}
});
if (num > -1) {
arr.value.splice(num, 1);
}
arr.value.push(file);
});
//
arr.value = arr.value.map(file => {
if (file.response) {
file.url = file.response.data.filePath;
file.id = file.response.data.fileId;
}
return file;
});
questionList.value[currIndex.value].files = arr.value;
};
const onSubmit = async () => {
const params = {
param: {
code: code.value,
projectId: projectId.value,
questionAndAnswerList: [],
},
};
const arr = [];
questionList.value.forEach(item => {
const obj = {
questionId: item.questionId,
answerList: [],
};
if (item.type === 1 || item.type === 2) {
obj.answerList.push(item.con);
}
if (item.type === 6 && item.date) {
const time = dayjs(item.date).format('x');
// if (time < expreStartTime.value || time > expreEndTime.value) {
// message.info('');
// return false;
// }
obj.answerList.push(time);
}
if (item.type === 7) {
item.files.forEach(val => {
const file = {
id: val.id,
name: val.name,
url: val.url,
};
obj.answerList.push(JSON.stringify(file));
});
}
arr.push(obj);
});
params.param.questionAndAnswerList = arr;
await store.dispatch('task/submitAnswer', params);
getDataByCode();
};
async function getDataByCode() {
const params = {
param: {
code: code.value,
projectId: projectId.value,
intellectualId: null,
},
};
const data = await store.dispatch('task/getByCode', params);
data.forEach(item => {
if (item.type === 1 || item.type === 2) {
item.con = '';
if (item.answerList.length > 0) {
item.con = item.answerList[0].answer;
}
}
if (item.type === 6) {
item.date = null;
if (item.answerList.length > 0) {
if (item.answerList[0].answer) {
item.date = dayjs(Number(item.answerList[0].answer)).format('YYYY-MM-DD');
item.date = dayjs(item.date, 'YYYY-MM-DD');
}
}
}
if (item.type === 7) {
item.files = [];
if (item.answerList.length > 0) {
item.answerList.forEach(val => {
if (val.answer) val.answer = JSON.parse(val.answer);
item.files.push(val.answer);
});
}
}
});
questionList.value = data;
}
</script>
<style scoped>
.task-form {
position: relative;
}
.task-form :deep(.ant-alert) {
position: absolute;
right: 30px;
}
.task-detail {
background-color: #fff;
}
.ant-col {
margin-top: 10px;
}
.ant-col:nth-child(-n + 4) {
margin-top: 2px;
}
.deliverables .ant-input,
.deliverables-son .ant-input {
width: 23px;
height: 14px;
border-radius: 0;
padding: 0;
font-size: 12px;
color: #1890ff;
text-align: center;
margin-left: 5px;
}
.deliverables-son {
margin-top: 10px !important;
}
</style>

277
src/components/tall/task/ExperimentalResult.vue

@ -1,277 +0,0 @@
<template>
<div class="task-form bg-white border-radius-10">
<a-form>
<div v-for="(item, index) in questionList" :key="index">
<template v-if="item.type === 1">
<a-form-item>
<label class="color-3">{{ item.question }}</label>
<a-input v-model:value="item.con" :placeholder="item.question" />
</a-form-item>
</template>
<template v-if="item.type === 2">
<a-form-item>
<label class="color-3">{{ item.question }}</label>
<a-textarea v-model:value="item.con" :placeholder="item.question" />
</a-form-item>
</template>
<template v-if="item.type === 3"> </template>
<template v-if="item.type === 4"> </template>
<template v-if="item.type === 5"> </template>
<template v-if="item.type === 6">
<a-form-item>
<label class="color-3">{{ item.question }}</label>
<a-date-picker v-model:value="item.date" />
</a-form-item>
</template>
<template v-if="item.type === 7">
<a-form-item>
<label class="color-3">{{ item.question }}</label>
<a-upload-dragger
v-model:fileList="item.files"
name="param"
:multiple="true"
:action="action"
:headers="headers"
:accept="'.pdf'"
:before-upload="beforeUpload(index)"
@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>
</template>
</div>
<a-form-item class="text-right">
<a-button type="primary" html-type="submit" @click="onSubmit" :disabled="expreStatus == 0 || expreStatus == 2 ? false : true">
确定
</a-button>
</a-form-item>
</a-form>
<a-alert v-if="isShowSuccess" :message="tipsMessage" type="success" />
<a-alert v-if="isShowWarning" :message="tipsMessage" type="warning" />
</div>
</template>
<script setup>
import { ref, computed } from 'vue';
import { useStore } from 'vuex';
import dayjs from 'dayjs';
import { InboxOutlined } from '@ant-design/icons-vue';
import { uploadImg } from 'apis';
const store = useStore();
//
const token = computed(() => store.getters['user/token']);
const headers = { Authorization: `Bearer ${token.value}` };
const action = uploadImg;
//
const projectId = computed(() => store.getters['projects/projectId']); // ID
const code = computed(() => store.state.task.label); // code
const questionList = ref([]);
//
const currIndex = ref(null);
const isShowSuccess = ref(false);
const isShowWarning = ref(false);
const tipsMessage = ref('');
//
const expreStatus = computed(() => store.state.projects.expreStatus);
const sessionStatus = sessionStorage.getItem('expreStatus');
if (!expreStatus.value && sessionStatus) {
store.commit('projects/setExpreimentStatus', sessionStatus);
}
if (expreStatus.value) {
if (expreStatus.value === 1 || expreStatus.value === 3 || expreStatus.value === 4) {
isShowWarning.value = true;
tipsMessage.value = '数据已锁定,不可操作';
} else {
isShowSuccess.value = true;
tipsMessage.value = '数据未锁定,可操作';
}
setTimeout(() => {
isShowWarning.value = false;
isShowSuccess.value = false;
}, 3000);
}
getDataByCode();
const beforeUpload = index => {
currIndex.value = index;
};
const handleChange = info => {
const resFileList = [...info.fileList];
//
const arr = ref([]);
resFileList.forEach(file => {
let num = -1;
arr.value.forEach((item, index) => {
if (file.name === item.name) {
num = index;
}
});
if (num > -1) {
arr.value.splice(num, 1);
}
arr.value.push(file);
});
//
arr.value = arr.value.map(file => {
if (file.response) {
file.url = file.response.data.filePath;
file.id = file.response.data.fileId;
}
return file;
});
questionList.value[currIndex.value].files = arr.value;
};
const onSubmit = async () => {
const params = {
param: {
code: code.value,
projectId: projectId.value,
questionAndAnswerList: [],
},
};
const arr = [];
questionList.value.forEach(item => {
const obj = {
questionId: item.questionId,
answerList: [],
};
if (item.type === 1 || item.type === 2) {
obj.answerList.push(item.con);
}
if (item.type === 6 && item.date) {
obj.answerList.push(dayjs(item.date).format('x'));
}
if (item.type === 7) {
item.files.forEach(val => {
const file = {
id: val.id,
name: val.name,
url: val.url,
};
obj.answerList.push(JSON.stringify(file));
});
}
arr.push(obj);
});
params.param.questionAndAnswerList = arr;
await store.dispatch('task/submitAnswer', params);
getDataByCode();
};
async function getDataByCode() {
const params = {
param: {
code: code.value,
projectId: projectId.value,
intellectualId: null,
},
};
const data = await store.dispatch('task/getByCode', params);
data.forEach(item => {
if (item.type === 1 || item.type === 2) {
item.con = '';
if (item.answerList.length > 0) {
item.con = item.answerList[0].answer;
}
}
if (item.type === 6) {
item.date = null;
if (item.answerList.length > 0) {
if (item.answerList[0].answer) {
item.date = dayjs(Number(item.answerList[0].answer)).format('YYYY-MM-DD');
item.date = dayjs(item.date, 'YYYY-MM-DD');
}
}
}
if (item.type === 7) {
item.files = [];
if (item.answerList.length > 0) {
item.answerList.forEach(val => {
if (val.answer) val.answer = JSON.parse(val.answer);
item.files.push(val.answer);
});
}
}
});
questionList.value = data;
}
</script>
<style scoped>
.task-form {
position: relative;
}
.task-form :deep(.ant-alert) {
position: absolute;
right: 30px;
}
.task-detail {
background-color: #fff;
}
.ant-col {
margin-top: 10px;
}
.ant-col:nth-child(-n + 4) {
margin-top: 2px;
}
.deliverables .ant-input,
.deliverables-son .ant-input {
width: 23px;
height: 14px;
border-radius: 0;
padding: 0;
font-size: 12px;
color: #1890ff;
text-align: center;
margin-left: 5px;
}
.deliverables-son {
margin-top: 10px !important;
}
</style>

246
src/components/tall/task/InterimInspection.vue

@ -1,246 +0,0 @@
<template>
<div class="task-form bg-white border-radius-10">
<a-form>
<div v-for="(item, index) in questionList" :key="index">
<template v-if="item.type === 1">
<a-form-item>
<label class="color-3">{{ item.question }}</label>
<a-input v-model:value="item.con" :placeholder="item.question" />
</a-form-item>
</template>
<template v-if="item.type === 2">
<a-form-item>
<label class="color-3">{{ item.question }}</label>
<a-textarea v-model:value="item.con" :placeholder="item.question" />
</a-form-item>
</template>
<template v-if="item.type === 3"> </template>
<template v-if="item.type === 4"> </template>
<template v-if="item.type === 5"> </template>
<template v-if="item.type === 6">
<a-form-item>
<label class="color-3">{{ item.question }}</label>
<a-date-picker v-model:value="item.date" />
</a-form-item>
</template>
<template v-if="item.type === 7">
<a-form-item>
<label class="color-3">{{ item.question }}</label>
<a-upload-dragger
v-model:fileList="item.files"
name="param"
:multiple="true"
:action="action"
:headers="headers"
:accept="'.pdf'"
:before-upload="beforeUpload(index)"
@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>
</template>
</div>
<a-form-item class="text-right">
<a-button type="primary" html-type="submit" @click="onSubmit">上传中期检查报告</a-button>
</a-form-item>
</a-form>
</div>
</template>
<script setup>
import { ref, computed } from 'vue';
import { useStore } from 'vuex';
import dayjs from 'dayjs';
import { InboxOutlined } from '@ant-design/icons-vue';
import { uploadImg } from 'apis';
import { message } from 'ant-design-vue';
const store = useStore();
//
const token = computed(() => store.getters['user/token']);
const headers = { Authorization: `Bearer ${token.value}` };
const action = uploadImg;
//
const projectId = computed(() => store.getters['projects/projectId']); // ID
const code = computed(() => store.state.task.label); // code
const questionList = ref([]);
//
const currIndex = ref(null);
//
const planStartTime = computed(() => store.state.layout.planStartTime);
const planEndTime = computed(() => store.state.layout.planEndTime);
getDataByCode();
const beforeUpload = index => {
currIndex.value = index;
};
const handleChange = info => {
const resFileList = [...info.fileList];
//
const arr = ref([]);
resFileList.forEach(file => {
let num = -1;
arr.value.forEach((item, index) => {
if (file.name === item.name) {
num = index;
}
});
if (num > -1) {
arr.value.splice(num, 1);
}
arr.value.push(file);
});
//
arr.value = arr.value.map(file => {
if (file.response) {
file.url = file.response.data.filePath;
file.id = file.response.data.fileId;
}
return file;
});
questionList.value[currIndex.value].files = arr.value;
};
const onSubmit = async () => {
const params = {
param: {
code: code.value,
projectId: projectId.value,
questionAndAnswerList: [],
},
};
const arr = [];
questionList.value.forEach(item => {
const obj = {
questionId: item.questionId,
answerList: [],
};
if (item.type === 1 || item.type === 2) {
obj.answerList.push(item.con);
}
if (item.type === 6 && item.date) {
const time = dayjs(item.date).format('x');
if (time < planStartTime.value || time > planEndTime.value) {
message.info('中期检查时间必须在项目起止时间之内');
return false;
}
obj.answerList.push(time);
}
if (item.type === 7) {
item.files.forEach(val => {
const file = {
id: val.id,
name: val.name,
url: val.url,
};
obj.answerList.push(JSON.stringify(file));
});
}
arr.push(obj);
});
params.param.questionAndAnswerList = arr;
await store.dispatch('task/submitAnswer', params);
getDataByCode();
};
async function getDataByCode() {
const params = {
param: {
code: code.value,
projectId: projectId.value,
intellectualId: null,
},
};
const data = await store.dispatch('task/getByCode', params);
data.forEach(item => {
if (item.type === 1 || item.type === 2) {
item.con = '';
if (item.answerList.length > 0) {
item.con = item.answerList[0].answer;
}
}
if (item.type === 6) {
item.date = null;
if (item.answerList.length > 0) {
if (item.answerList[0].answer) {
item.date = dayjs(Number(item.answerList[0].answer)).format('YYYY-MM-DD');
item.date = dayjs(item.date, 'YYYY-MM-DD');
}
}
}
if (item.type === 7) {
item.files = [];
if (item.answerList.length > 0) {
item.answerList.forEach(val => {
if (val.answer) val.answer = JSON.parse(val.answer);
item.files.push(val.answer);
});
}
}
});
questionList.value = data;
}
</script>
<style scoped>
.task-detail {
background-color: #fff;
}
.ant-col {
margin-top: 10px;
}
.ant-col:nth-child(-n + 4) {
margin-top: 2px;
}
.deliverables .ant-input,
.deliverables-son .ant-input {
width: 23px;
height: 14px;
border-radius: 0;
padding: 0;
font-size: 12px;
color: #1890ff;
text-align: center;
margin-left: 5px;
}
.deliverables-son {
margin-top: 10px !important;
}
</style>

274
src/components/tall/task/LabReport.vue

@ -1,274 +0,0 @@
<template>
<div class="task-form bg-white border-radius-10">
<a-form>
<div v-for="(item, index) in questionList" :key="index">
<template v-if="item.type === 1">
<a-form-item>
<label class="color-3">{{ item.question }}</label>
<a-input v-model:value="item.con" :placeholder="item.question" />
</a-form-item>
</template>
<template v-if="item.type === 2">
<a-form-item>
<label class="color-3">{{ item.question }}</label>
<a-textarea v-model:value="item.con" :placeholder="item.question" />
</a-form-item>
</template>
<template v-if="item.type === 3"> </template>
<template v-if="item.type === 4"> </template>
<template v-if="item.type === 5"> </template>
<template v-if="item.type === 6">
<a-form-item>
<label class="color-3">{{ item.question }}</label>
<a-date-picker v-model:value="item.date" />
</a-form-item>
</template>
<template v-if="item.type === 7">
<a-form-item>
<label class="color-3">{{ item.question }}</label>
<a-upload-dragger
v-model:fileList="item.files"
name="param"
:multiple="false"
:action="action"
:headers="headers"
:accept="'.pdf'"
@change="handleChange($event, index)"
>
<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>
</template>
</div>
<a-form-item class="text-right">
<a-button type="primary" html-type="submit" @click="onSubmit" :disabled="expreStatus == 0 || expreStatus == 2 ? false : true">
确定
</a-button>
</a-form-item>
</a-form>
<a-alert v-if="isShowSuccess" :message="tipsMessage" type="success" />
<a-alert v-if="isShowWarning" :message="tipsMessage" type="warning" />
</div>
</template>
<script setup>
import { ref, computed } from 'vue';
import { useStore } from 'vuex';
import dayjs from 'dayjs';
import { InboxOutlined } from '@ant-design/icons-vue';
import { uploadImg } from 'apis';
// import { message } from 'ant-design-vue';
const store = useStore();
//
const token = computed(() => store.getters['user/token']);
const headers = { Authorization: `Bearer ${token.value}` };
const action = uploadImg;
//
const projectId = computed(() => store.getters['projects/projectId']); // ID
const code = computed(() => store.state.task.label); // code
const questionList = ref([]);
const isShowSuccess = ref(false);
const isShowWarning = ref(false);
const tipsMessage = ref('');
//
// const expreStartTime = computed(() => store.state.layout.expreStartTime);
// const expreEndTime = computed(() => store.state.layout.expreEndTime);
//
const expreStatus = computed(() => store.state.projects.expreStatus);
const sessionStatus = sessionStorage.getItem('expreStatus');
if (!expreStatus.value && sessionStatus) {
store.commit('projects/setExpreimentStatus', sessionStatus);
}
if (expreStatus.value) {
console.log(expreStatus.value);
if (expreStatus.value === 1 || expreStatus.value === 3 || expreStatus.value === 4) {
isShowWarning.value = true;
isShowSuccess.value = false;
tipsMessage.value = '数据已锁定,不可操作';
} else {
isShowWarning.value = false;
isShowSuccess.value = true;
tipsMessage.value = '数据未锁定,可操作';
}
setTimeout(() => {
isShowWarning.value = false;
isShowSuccess.value = false;
}, 3000);
}
getDataByCode();
const handleChange = (info, index) => {
const resFileList = [...info.fileList];
//
const arr = ref([]);
resFileList.forEach((item, key) => {
if (key === resFileList.length - 1) {
arr.value.push(item);
}
});
//
arr.value = arr.value.map(file => {
if (file.response) {
file.url = file.response.data.filePath;
file.id = file.response.data.fileId;
}
return file;
});
questionList.value[index].files = arr.value;
};
const onSubmit = async () => {
const params = {
param: {
code: code.value,
projectId: projectId.value,
questionAndAnswerList: [],
},
};
const arr = [];
questionList.value.forEach(item => {
const obj = {
questionId: item.questionId,
answerList: [],
};
if (item.type === 1 || item.type === 2) {
obj.answerList.push(item.con);
}
if (item.type === 6 && item.date) {
const time = dayjs(item.date).format('x');
// if (time < expreStartTime.value || time > expreEndTime.value) {
// message.info('');
// return false;
// }
obj.answerList.push(time);
}
if (item.type === 7) {
item.files.forEach(val => {
const file = {
id: val.id,
name: val.name,
url: val.url,
};
obj.answerList.push(JSON.stringify(file));
});
}
arr.push(obj);
});
params.param.questionAndAnswerList = arr;
await store.dispatch('task/submitAnswer', params);
getDataByCode();
};
async function getDataByCode() {
const params = {
param: {
code: code.value,
projectId: projectId.value,
intellectualId: null,
},
};
const data = await store.dispatch('task/getByCode', params);
data.forEach(item => {
if (item.type === 1 || item.type === 2) {
item.con = '';
if (item.answerList.length > 0) {
item.con = item.answerList[0].answer;
}
}
if (item.type === 6) {
item.date = null;
if (item.answerList.length > 0) {
if (item.answerList[0].answer) {
item.date = dayjs(Number(item.answerList[0].answer)).format('YYYY-MM-DD');
item.date = dayjs(item.date, 'YYYY-MM-DD');
}
}
}
if (item.type === 7) {
item.files = [];
if (item.answerList.length > 0) {
item.answerList.forEach(val => {
if (val.answer) val.answer = JSON.parse(val.answer);
item.files.push(val.answer);
});
}
}
});
questionList.value = data;
}
</script>
<style scoped>
.task-form {
position: relative;
}
.task-form :deep(.ant-alert) {
position: absolute;
right: 30px;
}
.task-detail {
background-color: #fff;
}
.ant-col {
margin-top: 10px;
}
.ant-col:nth-child(-n + 4) {
margin-top: 2px;
}
.deliverables .ant-input,
.deliverables-son .ant-input {
width: 23px;
height: 14px;
border-radius: 0;
padding: 0;
font-size: 12px;
color: #1890ff;
text-align: center;
margin-left: 5px;
}
.deliverables-son {
margin-top: 10px !important;
}
</style>

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

@ -1,292 +0,0 @@
<template>
<div class="task-form bg-white border-radius-10">
<a-form :model="topicMeetFormData">
<a-form-item>
<label class="color-3">会议名称</label>
<a-input v-model:value="topicMeetFormData.name" placeholder="会议名称" />
</a-form-item>
<a-form-item>
<label class="color-3">会议日期</label>
<a-space direction="vertical" :size="12">
<a-range-picker v-model:value="topicMeetFormData.date" />
</a-space>
</a-form-item>
<a-form-item>
<label class="color-3">会议地点</label>
<a-input v-model:value="topicMeetFormData.address" placeholder="会议地点" />
</a-form-item>
<a-form-item>
<label class="color-3">会议通知</label>
<a-upload-dragger
v-model:fileList="notificationList"
name="param"
:multiple="true"
:action="action"
:headers="headers"
:accept="'.pdf'"
@change="handleChange($event, 1)"
>
<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="summaryList"
name="param"
:multiple="true"
:action="action"
:headers="headers"
:accept="'.pdf'"
@change="handleChange($event, 2)"
>
<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="attachmentList"
name="param"
:multiple="true"
:action="action"
:headers="headers"
:accept="'.jpg,.jpeg,.rar,.zip,.png'"
@change="handleChange($event, 3)"
>
<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">格式jpgjpegrarzip</p>
</a-upload-dragger>
</a-form-item>
<a-form-item class="text-right">
<a-button type="primary" html-type="submit" @click="onSubmit">上传会议资料</a-button>
</a-form-item>
</a-form>
</div>
</template>
<script setup>
import { ref, computed, watch } from 'vue';
import { useStore } from 'vuex';
import { InboxOutlined } from '@ant-design/icons-vue';
import { uploadImg, saveMeeting, getMeetDetail } from 'apis';
import dayjs from 'dayjs';
import { message } from 'ant-design-vue';
const store = useStore();
const token = computed(() => store.getters['user/token']);
const headers = { Authorization: `Bearer ${token.value}` };
const action = uploadImg;
const notificationList = ref([]);
const summaryList = ref([]);
const attachmentList = ref([]);
const meetId = computed(() => store.state.task.meetId);
const projectId = computed(() => store.getters['projects/projectId']);
const topicMeetFormData = ref({
projectId: projectId.value,
id: '',
name: '',
date: [],
startTime: '',
endTime: '',
address: '',
notificationList: [],
summaryList: [],
attachmentList: [],
});
watch(meetId, async () => {
if (meetId.value) {
await getMeetingInfo();
} else {
topicMeetFormData.value = {
projectId: projectId.value,
id: '',
name: '',
date: [],
startTime: '',
endTime: '',
address: '',
notificationList: [],
summaryList: [],
attachmentList: [],
};
notificationList.value = [];
summaryList.value = [];
attachmentList.value = [];
}
});
const handleChange = (info, index) => {
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.filePath;
file.id = file.response.data.fileId;
}
return file;
});
if (index === 1) {
notificationList.value = arr.value;
} else if (index === 2) {
summaryList.value = arr.value;
} else if (index === 3) {
attachmentList.value = arr.value;
}
};
// ID
async function getMeetingInfo() {
try {
const params = { param: { id: meetId.value } };
const data = await getMeetDetail(params);
if (data) {
data.projectId = projectId.value;
const start = dayjs(Number(data.startTime));
const end = dayjs(Number(data.endTime));
data.date = [start, end];
topicMeetFormData.value = data;
notificationList.value = [];
summaryList.value = [];
attachmentList.value = [];
data.notificationList.forEach(item => {
const obj = {
id: item.fileId,
name: item.fileName,
url: item.filePath,
};
notificationList.value.push(obj);
});
data.summaryList.forEach(item => {
const obj = {
id: item.fileId,
name: item.fileName,
url: item.filePath,
};
summaryList.value.push(obj);
});
data.attachmentList.forEach(item => {
const obj = {
id: item.fileId,
name: item.fileName,
url: item.filePath,
};
attachmentList.value.push(obj);
});
} else {
topicMeetFormData.value = {
projectId: projectId.value,
id: '',
name: '',
date: [],
startTime: '',
endTime: '',
address: '',
notificationList: [],
summaryList: [],
attachmentList: [],
};
notificationList.value = [];
summaryList.value = [];
attachmentList.value = [];
}
} catch (error) {
message.info(error);
throw new Error(error);
}
}
const onSubmit = async () => {
notificationList.value.forEach(item => {
const obj = {
fileId: item.id,
fileName: item.name,
filePath: item.url,
};
topicMeetFormData.value.notificationList.push(obj);
});
summaryList.value.forEach(item => {
const obj = {
fileId: item.id,
fileName: item.name,
filePath: item.url,
};
topicMeetFormData.value.summaryList.push(obj);
});
attachmentList.value.forEach(item => {
const obj = {
fileId: item.id,
fileName: item.name,
filePath: item.url,
};
topicMeetFormData.value.attachmentList.push(obj);
});
topicMeetFormData.value.date.forEach((item, index) => {
if (index === 0) {
topicMeetFormData.value.startTime = dayjs(item).format('x');
} else {
topicMeetFormData.value.endTime = dayjs(item).format('x');
}
});
const params = { param: topicMeetFormData.value };
await saveMeeting(params);
getMeetingInfo();
};
</script>
<style scoped>
.task-detail {
background-color: #fff;
}
</style>

258
src/components/tall/task/MemberManagement.vue

@ -1,258 +0,0 @@
<template>
<div>
<a-table
class="member-list"
:columns="columns"
:data-source="dataList"
:row-class-name="(_record, index) => (index % 2 === 1 ? null : 'table-striped')"
>
<template #bodyCell="{ column, text, record }">
<template v-if="column.key === 'action'">
<template v-if="record.isEdit === 0">
<a-button class="action-btn edit-btn" type="primary" @click="toEdit(record.key)">编辑</a-button>
<a-button class="action-btn del-btn" type="primary" @click="toDelete(record.key)">删除</a-button>
</template>
<template v-if="record.isEdit === 1">
<a-button class="action-btn edit-btn" type="primary" @click="save(record.key)">保存</a-button>
<a-button class="action-btn del-btn" type="primary" @click="cancel(record.key)">取消</a-button>
</template>
</template>
<template v-else-if="['memberName', 'phone'].includes(column.dataIndex)">
<div>
<a-input v-if="record.isEdit === 1" v-model:value="record[column.dataIndex]" style="margin: -5px 0" />
<template v-else>
{{ text }}
</template>
</div>
</template>
</template>
<template #footer><PlusCircleOutlined @click="addMember" style="font-size: 24px; color: #1890ff" /></template>
</a-table>
</div>
</template>
<script setup>
import { ref, computed } from 'vue';
import { useStore } from 'vuex';
import { PlusCircleOutlined } from '@ant-design/icons-vue';
import { memberQuery, saveMember, updateMember, delMember } from 'apis';
import { message } from 'ant-design-vue';
const store = useStore();
const projectId = computed(() => store.getters['projects/projectId']);
const isAdd = ref(false);
const columns = ref([
{
title: '序号',
dataIndex: 'indexId',
key: 'indexId',
},
{
title: '成员名',
dataIndex: 'memberName',
key: 'memberName',
},
{
title: '手机号',
dataIndex: 'phone',
key: 'phone',
},
{
title: '角色',
dataIndex: 'roles',
key: 'roles',
},
{
title: '操作',
key: 'action',
dataIndex: 'action',
},
]);
const dataList = ref([]);
getList();
async function getList() {
try {
const params = { param: { projectId: projectId.value } };
const data = await memberQuery(params);
store.commit('task/setMembers', data);
dataList.value = [];
data.forEach((item, index) => {
const obj = {
key: index + 1,
indexId: index + 1,
memberId: item.memberId,
memberName: item.memberName,
phone: item.phone,
roles: item.experimentNameList.toString(),
isEdit: 0,
};
dataList.value.push(obj);
});
} catch (error) {
message.info(error);
throw new Error(error);
}
}
const addMember = () => {
const num = dataList.value.length + 1;
const obj = { key: num, indexId: num, memberName: '', phone: '', isEdit: 1 };
dataList.value.push(obj);
isAdd.value = true;
};
function toEdit(key) {
isAdd.value = false;
dataList.value.forEach(item => {
if (item.key === key) {
item.isEdit = 1;
}
});
}
function cancel(key) {
let ind = -1;
dataList.value.filter((item, index) => {
if (item.key === key) {
if (isAdd.value) {
ind = index;
} else {
getList();
}
}
});
if (ind > -1) {
dataList.value.splice(ind, 1);
}
isAdd.value = false;
}
async function save(key) {
isAdd.value = false;
const params = {
param: {
projectId: projectId.value,
id: '',
name: '',
phone: '',
},
};
dataList.value.forEach(item => {
if (key === item.key) {
params.param.id = item.memberId;
params.param.name = item.memberName;
params.param.phone = item.phone;
}
});
const myreg = /^[1][3,4,5,7,8,9][0-9]{9}$/;
if (!myreg.test(params.param.phone)) {
message.info('请填写正确的手机号');
return false;
}
try {
if (params.param.id) {
await updateMember(params);
} else {
await saveMember(params);
}
getList();
} catch (error) {
message.info(error);
throw new Error(error);
}
}
async function toDelete(key) {
console.log(key);
const params = { param: { id: '' } };
dataList.value.forEach(item => {
if (key === item.key) {
params.param.id = item.memberId;
}
});
try {
await delMember(params);
getList();
} catch (error) {
message.info(error);
throw new Error(error);
}
}
</script>
<style scoped>
.member-list {
border-radius: 10px 10px 0 0;
overflow: hidden;
}
:deep(.table-striped) td {
background-color: #fafafa;
}
.action-btn {
width: 50px !important;
height: 28px !important;
font-size: 14px !important;
padding: 0;
letter-spacing: 0 !important;
}
:deep(.ant-table-pagination.ant-pagination) {
height: 0;
margin: 0;
}
.add-btn {
height: 60px;
background: #fff;
padding-left: 36px;
}
.edit-btn {
background: #0dc26c;
border: 0;
}
.del-btn {
margin-left: 16px;
background: #ff5353;
border: 0;
}
:deep(.ant-table-container table > thead > tr:first-child th:first-child) {
width: 80px;
text-align: center;
}
:deep(.ant-table-container table > thead > tr:first-child th:nth-child(2)) {
min-width: 100px;
}
:deep(.ant-table-container table > tbody > tr > td:first-child) {
text-align: center;
}
:deep(.ant-table-container table > thead > tr:first-child th:last-child) {
min-width: 160px;
}
</style>

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

@ -1,389 +0,0 @@
<template>
<div class="task-form bg-white border-radius-10">
<a-form ref="formRef" :model="topicMeetFormData">
<a-form-item>
<label class="color-3">课题名称</label>
<a-input v-model:value="topicMeetFormData.name" placeholder="课题名称" />
</a-form-item>
<a-form-item>
<label class="color-3">起止时间</label>
<a-space direction="vertical" :size="12">
<a-range-picker v-model:value="topicMeetFormData.date" />
</a-space>
</a-form-item>
<a-form-item>
<label class="color-3">负责人</label>
<a-select
v-model:value="topicMeetFormData.memberId"
show-search
optionFilterProp="label"
placeholder="负责人"
:options="options"
:filter-option="filterOption"
@search="handleSearch"
:getPopupContainer="
triggerNode => {
return triggerNode.parentNode || document.body;
}
"
></a-select>
</a-form-item>
<a-form-item class="form-item-dad">
<div class="flex items-center" style="margin-bottom: 5px">
<label class="color-3" style="margin-bottom: 0; margin-right: 8px">项目分阶段具体实施目标</label>
<PlusCircleOutlined style="color: #1890ff; font-size: 16px" @click="addMilestones" />
</div>
<div class="form-item-son" style="padding-left: 16px">
<div v-for="(item, index) in planTaskStageList" :key="index">
<a-form-item>
<label class="color-3">{{ index + 1 }}阶段</label>
<a-space direction="vertical" :size="12">
<a-range-picker v-model:value="item.date" />
</a-space>
</a-form-item>
<a-form-item>
<label class="color-3">实施内容与目标</label>
<a-textarea v-model:value="item.remark" placeholder="实施内容与目标" />
</a-form-item>
</div>
</div>
</a-form-item>
<a-form-item>
<label class="color-3">主要技术指标</label>
<a-textarea v-model:value="topicMeetFormData.technicalIndicator" placeholder="主要技术指标" />
</a-form-item>
<a-form-item>
<label class="color-3">经济指标</label>
<a-textarea v-model:value="topicMeetFormData.economicIndicators" placeholder="经济指标" />
</a-form-item>
<a-form-item>
<label class="color-3">社会效益</label>
<a-textarea v-model:value="topicMeetFormData.socialBenefit" placeholder="社会效益" />
</a-form-item>
<a-form-item>
<label class="color-3">验收内容</label>
<a-checkbox-group v-model:value="topicMeetFormData.checkContentList">
<a-row>
<a-col :span="5">
<a-checkbox value="1"><span class="color-6">1工作报告</span></a-checkbox>
</a-col>
<a-col :span="6">
<a-checkbox value="2"><span class="color-6">2技术报告</span></a-checkbox>
</a-col>
<a-col :span="6">
<a-checkbox value="3"><span class="color-6">3计算机软件</span></a-checkbox>
</a-col>
<a-col :span="7">
<a-checkbox value="4"><span class="color-6">4生物品种</span></a-checkbox>
</a-col>
<a-col :span="5">
<a-checkbox value="5"><span class="color-6">5样品或样机</span></a-checkbox>
</a-col>
<a-col :span="6">
<a-checkbox value="6"><span class="color-6">6成套技术设备</span></a-checkbox>
</a-col>
<a-col :span="6">
<a-checkbox value="7"><span class="color-6">7科技论文或著作</span></a-checkbox>
</a-col>
<a-col :span="7">
<a-checkbox value="8"><span class="color-6">8科技报告收录证书(必选)</span></a-checkbox>
</a-col>
</a-row>
</a-checkbox-group>
</a-form-item>
<!-- 自定义 -->
<div class="flex items-center cursor-pointer" style="margin-bottom: 20px" @click="addDefined">
<PlusCircleOutlined style="color: #1890ff; font-size: 16px" /><span class="color-3" style="margin-left: 8px">自定义</span>
</div>
<div class="form-item-son" style="padding-left: 16px">
<div v-for="(item, index) in planTaskDefinedList" :key="index">
<a-form-item>
<label class="color-3">自定义名称</label>
<a-input v-model:value="item.key" placeholder="自定义名称" />
</a-form-item>
<a-form-item>
<label class="color-3">自定义内容</label>
<a-textarea v-model:value="item.value" placeholder="自定义内容" />
</a-form-item>
</div>
</div>
<a-form-item>
<label class="color-3">计划任务书</label>
<a-upload-dragger
v-model:fileList="fileList"
name="param"
: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">
<a-button type="primary" html-type="submit" @click="onSubmit">上传计划任务书</a-button>
</a-form-item>
</a-form>
</div>
</template>
<script setup>
import { ref, computed } from 'vue';
import { useStore } from 'vuex';
import { InboxOutlined, PlusCircleOutlined } from '@ant-design/icons-vue';
import { uploadImg, memberQuery, savePlanTask, getPlanTask } from 'apis';
import dayjs from 'dayjs';
import { message } from 'ant-design-vue';
const store = useStore();
const formRef = ref(null);
const projectInfo = computed(() => store.state.projects.project); //
const projectId = computed(() => store.getters['projects/projectId']);
const token = computed(() => store.getters['user/token']);
const headers = { Authorization: `Bearer ${token.value}` };
const action = uploadImg;
const fileList = ref([]);
const topicMeetFormData = ref({
projectId: projectInfo.value.id,
name: '',
date: [],
startTime: '',
endTime: '',
memberId: '',
technicalIndicator: '',
economicIndicators: '',
socialBenefit: '',
checkContentList: [],
fileList: [],
planTaskStageList: [],
planTaskDefinedList: [],
});
const options = ref([]);
const planTaskStageList = ref([{ date: [], stageStartTime: '', stageEndTime: '', remark: '' }]);
const planTaskDefinedList = ref([]);
checkPlanTask(); //
getList(); //
const handleSearch = async value => {
await getList(value); //
};
const filterOption = (input, option) => {
return option.value.toLowerCase().indexOf(input.toLowerCase()) >= 0;
};
const handleChange = info => {
const resFileList = [...info.fileList];
//
const arr = ref([]);
resFileList.forEach(file => {
let num = -1;
arr.value.forEach((item, index) => {
if (file.name === item.name) {
num = index;
}
});
if (num > -1) {
arr.value.splice(num, 1);
}
arr.value.push(file);
});
//
arr.value = arr.value.map(file => {
if (file.response) {
file.url = file.response.data.filePath;
file.id = file.response.data.fileId;
}
return file;
});
fileList.value = arr.value;
};
//
async function checkPlanTask() {
try {
const params = { param: { projectId: projectId.value } };
const data = await getPlanTask(params);
if (data.checkContent) data.checkContentList = data.checkContent.split(',');
store.commit('layout/setFirPlanTime', { startTime: data.startTime, endTime: data.endTime });
data.date = [];
if (data.startTime) {
const start = dayjs(Number(data.startTime));
const end = dayjs(Number(data.endTime));
data.date = [start, end];
}
data.projectId = data.id;
topicMeetFormData.value = data;
planTaskDefinedList.value = data.planTaskDefinedList;
data.planTaskStageList.forEach(item => {
item.date = [];
if (item.stageStartTime) {
const planStart = dayjs(Number(item.stageStartTime));
const planEnd = dayjs(Number(item.stageEndTime));
item.date = [planStart, planEnd];
}
});
planTaskStageList.value = data.planTaskStageList;
const fileArr = [];
data.fileList.forEach(item => {
const fileInfo = {
id: item.fileId,
name: item.fileName,
url: item.filePath,
status: 'done',
};
fileArr.push(fileInfo);
});
fileList.value = fileArr;
} catch (error) {
message.info(error);
throw new Error(error);
}
}
//
async function getList(name) {
try {
const params = { param: { projectId: projectId.value, name } };
const data = await memberQuery(params);
store.commit('task/setMembers', data);
options.value = [];
data.forEach(item => {
const obj = {
label: item.memberName,
value: item.memberId,
};
options.value.push(obj);
});
} catch (error) {
message.info(error);
throw new Error(error);
}
}
//
function addMilestones() {
planTaskStageList.value.push({ date: [], stageStartTime: '', stageEndTime: '', remark: '' });
}
//
function addDefined() {
planTaskDefinedList.value.push({ key: '', value: '' });
}
const onSubmit = async () => {
fileList.value.forEach(item => {
const obj = {
fileId: item.id,
fileName: item.name,
filePath: item.url,
};
topicMeetFormData.value.fileList.push(obj);
});
if (topicMeetFormData.value.date) {
topicMeetFormData.value.date.forEach((item, index) => {
if (index === 0) {
topicMeetFormData.value.startTime = dayjs(item).format('x');
} else {
topicMeetFormData.value.endTime = dayjs(item).format('x');
}
});
}
topicMeetFormData.value.planTaskStageList = [];
planTaskStageList.value.forEach(item => {
const obj = {
stageStartTime: '',
stageEndTime: '',
remark: item.remark,
};
if (item.date.length > 0) {
item.date.forEach((val, key) => {
if (key === 0) {
obj.stageStartTime = dayjs(val).format('x');
} else {
obj.stageEndTime = dayjs(val).format('x');
}
});
if (obj.stageStartTime < topicMeetFormData.value.startTime) {
message.info('阶段任务开始时间不能小于项目起始时间');
return false;
}
if (obj.stageEndTime > topicMeetFormData.value.endTime) {
message.info('阶段任务结束时间不能小于项目终止时间');
return false;
}
}
topicMeetFormData.value.planTaskStageList.push(obj);
});
topicMeetFormData.value.planTaskDefinedList = planTaskDefinedList.value;
const params = { param: topicMeetFormData.value };
await savePlanTask(params);
projectInfo.value.name = topicMeetFormData.value.name;
store.commit('projects/setProject', projectInfo.value);
store.commit('layout/setRefreshProjects');
checkPlanTask();
};
</script>
<style scoped>
.task-detail {
background-color: #fff;
}
.ant-col {
margin-top: 10px;
}
.ant-col:nth-child(-n + 4) {
margin-top: 2px;
}
</style>

271
src/components/tall/task/Procedure.vue

@ -1,271 +0,0 @@
<template>
<div class="task-form bg-white border-radius-10">
<a-form>
<div v-for="(item, index) in questionList" :key="index">
<template v-if="item.type === 1">
<a-form-item>
<label class="color-3">{{ item.question }}</label>
<a-input v-model:value="item.con" :placeholder="item.question" />
</a-form-item>
</template>
<template v-if="item.type === 2">
<a-form-item>
<label class="color-3">{{ item.question }}</label>
<a-textarea v-model:value="item.con" :placeholder="item.question" />
</a-form-item>
</template>
<template v-if="item.type === 3"> </template>
<template v-if="item.type === 4"> </template>
<template v-if="item.type === 5"> </template>
<template v-if="item.type === 6">
<a-form-item>
<label class="color-3">{{ item.question }}</label>
<a-date-picker v-model:value="item.date" />
</a-form-item>
</template>
<template v-if="item.type === 7">
<a-form-item>
<label class="color-3">{{ item.question }}</label>
<a-upload-dragger
v-model:fileList="item.files"
name="param"
:multiple="true"
:action="action"
:headers="headers"
:accept="'.pdf'"
@change="handleChange($event, index)"
>
<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>
</template>
</div>
<a-form-item class="text-right">
<a-button type="primary" html-type="submit" @click="onSubmit" :disabled="expreStatus == 0 || expreStatus == 2 ? false : true">
确定
</a-button>
</a-form-item>
</a-form>
<a-alert v-if="isShowSuccess" :message="tipsMessage" type="success" />
<a-alert v-if="isShowWarning" :message="tipsMessage" type="warning" />
</div>
</template>
<script setup>
import { ref, computed } from 'vue';
import { useStore } from 'vuex';
import dayjs from 'dayjs';
import { InboxOutlined } from '@ant-design/icons-vue';
import { uploadImg } from 'apis';
// import { message } from 'ant-design-vue';
const store = useStore();
//
const token = computed(() => store.getters['user/token']);
const headers = { Authorization: `Bearer ${token.value}` };
const action = uploadImg;
//
const projectId = computed(() => store.getters['projects/projectId']); // ID
const code = computed(() => store.state.task.label); // code
const questionList = ref([]);
const isShowSuccess = ref(false);
const isShowWarning = ref(false);
const tipsMessage = ref('');
//
// const expreStartTime = computed(() => store.state.layout.expreStartTime);
// const expreEndTime = computed(() => store.state.layout.expreEndTime);
//
const expreStatus = computed(() => store.state.projects.expreStatus);
const sessionStatus = sessionStorage.getItem('expreStatus');
if (!expreStatus.value && sessionStatus) {
store.commit('projects/setExpreimentStatus', sessionStatus);
}
if (expreStatus.value) {
if (expreStatus.value === 1 || expreStatus.value === 3 || expreStatus.value === 4) {
isShowWarning.value = true;
tipsMessage.value = '数据已锁定,不可操作';
} else {
isShowSuccess.value = true;
tipsMessage.value = '数据未锁定,可操作';
}
setTimeout(() => {
isShowWarning.value = false;
isShowSuccess.value = false;
}, 3000);
}
getDataByCode();
const handleChange = (info, index) => {
const resFileList = [...info.fileList];
//
const arr = ref([]);
resFileList.forEach((item, key) => {
if (key === resFileList.length - 1) {
arr.value.push(item);
}
});
//
arr.value = arr.value.map(file => {
if (file.response) {
file.url = file.response.data.filePath;
file.id = file.response.data.fileId;
}
return file;
});
questionList.value[index].files = arr.value;
};
const onSubmit = async () => {
const params = {
param: {
code: code.value,
projectId: projectId.value,
questionAndAnswerList: [],
},
};
const arr = [];
questionList.value.forEach(item => {
const obj = {
questionId: item.questionId,
answerList: [],
};
if (item.type === 1 || item.type === 2) {
obj.answerList.push(item.con);
}
if (item.type === 6 && item.date) {
const time = dayjs(item.date).format('x');
// if (time < expreStartTime.value || time > expreEndTime.value) {
// message.info('');
// return false;
// }
obj.answerList.push(time);
}
if (item.type === 7) {
item.files.forEach(val => {
const file = {
id: val.id,
name: val.name,
url: val.url,
};
obj.answerList.push(JSON.stringify(file));
});
}
arr.push(obj);
});
params.param.questionAndAnswerList = arr;
await store.dispatch('task/submitAnswer', params);
getDataByCode();
};
async function getDataByCode() {
const params = {
param: {
code: code.value,
projectId: projectId.value,
intellectualId: null,
},
};
const data = await store.dispatch('task/getByCode', params);
data.forEach(item => {
if (item.type === 1 || item.type === 2) {
item.con = '';
if (item.answerList.length > 0) {
item.con = item.answerList[0].answer;
}
}
if (item.type === 6) {
item.date = null;
if (item.answerList.length > 0) {
if (item.answerList[0].answer) {
item.date = dayjs(Number(item.answerList[0].answer)).format('YYYY-MM-DD');
item.date = dayjs(item.date, 'YYYY-MM-DD');
}
}
}
if (item.type === 7) {
item.files = [];
if (item.answerList.length > 0) {
item.answerList.forEach(val => {
if (val.answer) val.answer = JSON.parse(val.answer);
item.files.push(val.answer);
});
}
}
});
questionList.value = data;
}
</script>
<style scoped>
.task-form {
position: relative;
}
.task-form :deep(.ant-alert) {
position: absolute;
right: 30px;
}
.task-detail {
background-color: #fff;
}
.ant-col {
margin-top: 10px;
}
.ant-col:nth-child(-n + 4) {
margin-top: 2px;
}
.deliverables .ant-input,
.deliverables-son .ant-input {
width: 23px;
height: 14px;
border-radius: 0;
padding: 0;
font-size: 12px;
color: #1890ff;
text-align: center;
margin-left: 5px;
}
.deliverables-son {
margin-top: 10px !important;
}
</style>

308
src/components/tall/task/PublishPatent.vue

@ -1,308 +0,0 @@
<template>
<div class="task-form bg-white border-radius-10">
<a-form>
<div v-for="(item, index) in questionList" :key="index">
<template v-if="item.type === 1">
<a-form-item>
<label class="color-3">{{ item.question }}</label>
<a-input v-model:value="item.con" :placeholder="item.question" />
</a-form-item>
</template>
<template v-if="item.type === 2">
<a-form-item>
<label class="color-3">{{ item.question }}</label>
<a-textarea v-model:value="item.con" :placeholder="item.question" />
</a-form-item>
</template>
<template v-if="item.type === 3">
<a-form-item>
<label class="color-3">{{ item.question }}</label>
<a-radio-group v-model:value="item.con" name="radioGroup" @change="handleChangeRadio">
<a-radio :value="val.submitValue" :id="val.optionId" v-for="(val, key) in item.optionList" :key="key">{{
val.showValue
}}</a-radio>
</a-radio-group>
</a-form-item>
</template>
<template v-if="item.type === 4"> </template>
<template v-if="item.type === 5">
<a-form-item>
<label class="color-3">{{ item.question }}</label>
<a-select ref="select" v-model:value="item.con">
<a-select-option :value="val.submitValue" v-for="(val, key) in item.optionList" :key="key">{{
val.showValue
}}</a-select-option>
</a-select>
</a-form-item>
</template>
<template v-if="item.type === 6">
<a-form-item>
<label class="color-3">{{ item.question }}</label>
<a-date-picker v-model:value="item.date" />
</a-form-item>
</template>
<template v-if="item.type === 7">
<a-form-item>
<label class="color-3">{{ item.question }}</label>
<a-upload-dragger
v-model:fileList="item.files"
name="param"
:multiple="true"
:action="action"
:headers="headers"
:accept="'.pdf'"
@change="handleChange($event, index)"
>
<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>
</template>
</div>
<a-form-item class="text-right">
<a-button type="primary" html-type="submit" @click="onSubmit">上传</a-button>
</a-form-item>
</a-form>
</div>
</template>
<script setup>
import { ref, computed, watch } from 'vue';
import { useStore } from 'vuex';
import dayjs from 'dayjs';
import { InboxOutlined } from '@ant-design/icons-vue';
import { uploadImg } from 'apis';
const store = useStore();
//
const token = computed(() => store.getters['user/token']);
const headers = { Authorization: `Bearer ${token.value}` };
const action = uploadImg;
//
const projectId = computed(() => store.getters['projects/projectId']); // ID
const code = computed(() => store.state.task.label); // code
const questionList = ref([]);
const intellectualId = computed(() => store.state.task.intellectualId); // ID
getDataByCode();
watch(intellectualId, () => {
getDataByCode();
});
//
const handleChangeRadio = value => {
const selectedName = value.target.value;
const selectedId = value.target.id;
let addQuestion = [];
const getQuestions = questionList.value;
let selectedIndex = -1;
questionList.value.forEach((item, index) => {
if (item.type === 3) {
item.optionList.forEach(val => {
if (val.optionId === selectedId && val.questionInfos && val.questionInfos.length > 0) {
addQuestion = val.questionInfos;
selectedIndex = index;
}
});
}
});
addQuestion.forEach((item, index) => {
let flag = false;
getQuestions.forEach(v => {
if (v.question === '授权日期') {
flag = true;
}
});
if (!flag) {
if (selectedName === '授权') {
const addIndex = selectedIndex + index + 1;
getQuestions.splice(addIndex, 0, item);
}
}
});
if (selectedName === '申请') {
let flag = false;
let delIndex = -1;
getQuestions.forEach((v, k) => {
if (v.question === '授权日期') {
flag = true;
delIndex = k;
}
});
if (flag) {
getQuestions.splice(delIndex, 1);
}
}
questionList.value = getQuestions;
};
//
const handleChange = (info, currIndex) => {
const resFileList = [...info.fileList];
//
const arr = ref([]);
resFileList.forEach(file => {
let num = -1;
arr.value.forEach((item, index) => {
if (file.name === item.name) {
num = index;
}
});
if (num > -1) {
arr.value.splice(num, 1);
}
arr.value.push(file);
});
//
arr.value = arr.value.map(file => {
if (file.response) {
file.url = file.response.data.filePath;
file.id = file.response.data.fileId;
}
return file;
});
questionList.value[currIndex].files = arr.value;
};
const onSubmit = async () => {
const params = {
param: {
code: code.value,
projectId: projectId.value,
intellectualId: intellectualId.value,
questionAndAnswerList: [],
},
};
const arr = [];
questionList.value.forEach(item => {
const obj = {
questionId: item.questionId,
answerList: [],
};
if (item.type === 1 || item.type === 2 || item.type === 3 || item.type === 5) {
obj.answerList.push(item.con);
}
if (item.type === 6 && item.date) {
obj.answerList.push(dayjs(item.date).format('x'));
}
if (item.type === 7) {
item.files.forEach(val => {
const file = {
id: val.id,
name: val.name,
url: val.url,
};
obj.answerList.push(JSON.stringify(file));
});
}
arr.push(obj);
});
params.param.questionAndAnswerList = arr;
await store.dispatch('task/submitIntellectual', params);
getDataByCode();
};
async function getDataByCode() {
const params = {
param: {
code: code.value,
projectId: projectId.value,
intellectualId: intellectualId.value,
},
};
const data = await store.dispatch('task/getIntellectual', params);
data.forEach(item => {
if (item.type === 1 || item.type === 2 || item.type === 3 || item.type === 5) {
item.con = '';
if (item.answerList.length > 0) {
item.con = item.answerList[0].answer;
}
}
if (item.type === 6) {
item.date = null;
if (item.answerList.length > 0) {
if (item.answerList[0].answer) {
item.date = dayjs(Number(item.answerList[0].answer)).format('YYYY-MM-DD');
item.date = dayjs(item.date, 'YYYY-MM-DD');
}
}
}
if (item.type === 7) {
item.files = [];
if (item.answerList.length > 0) {
item.answerList.forEach(val => {
if (val.answer) val.answer = JSON.parse(val.answer);
item.files.push(val.answer);
});
}
}
});
questionList.value = data;
}
</script>
<style scoped>
.task-detail {
background-color: #fff;
}
.ant-col {
margin-top: 10px;
}
.ant-col:nth-child(-n + 4) {
margin-top: 2px;
}
.deliverables .ant-input,
.deliverables-son .ant-input {
width: 23px;
height: 14px;
border-radius: 0;
padding: 0;
font-size: 12px;
color: #1890ff;
text-align: center;
margin-left: 5px;
}
.deliverables-son {
margin-top: 10px !important;
}
</style>

251
src/components/tall/task/PublishThesis.vue

@ -1,251 +0,0 @@
<template>
<div class="task-form bg-white border-radius-10">
<a-form>
<div v-for="(item, index) in questionList" :key="index">
<template v-if="item.type === 1">
<a-form-item>
<label class="color-3">{{ item.question }}</label>
<a-input v-model:value="item.con" :placeholder="item.question" />
</a-form-item>
</template>
<template v-if="item.type === 2">
<a-form-item>
<label class="color-3">{{ item.question }}</label>
<a-textarea v-model:value="item.con" :placeholder="item.question" />
</a-form-item>
</template>
<template v-if="item.type === 3"> </template>
<template v-if="item.type === 4"> </template>
<template v-if="item.type === 5">
<a-form-item>
<label class="color-3">{{ item.question }}</label>
<a-select ref="select" v-model:value="item.con">
<a-select-option :value="val.submitValue" v-for="(val, key) in item.optionList" :key="key">{{
val.showValue
}}</a-select-option>
</a-select>
</a-form-item>
</template>
<template v-if="item.type === 6">
<a-form-item>
<label class="color-3">{{ item.question }}</label>
<a-date-picker v-model:value="item.date" />
</a-form-item>
</template>
<template v-if="item.type === 7">
<a-form-item>
<label class="color-3">{{ item.question }}</label>
<a-upload-dragger
v-model:fileList="item.files"
name="param"
:multiple="true"
:action="action"
:headers="headers"
:accept="'.pdf'"
:before-upload="beforeUpload(index)"
@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>
</template>
</div>
<a-form-item class="text-right">
<a-button type="primary" html-type="submit" @click="onSubmit">上传</a-button>
</a-form-item>
</a-form>
</div>
</template>
<script setup>
import { ref, computed, watch } from 'vue';
import { useStore } from 'vuex';
import dayjs from 'dayjs';
import { InboxOutlined } from '@ant-design/icons-vue';
import { uploadImg } from 'apis';
const store = useStore();
//
const token = computed(() => store.getters['user/token']);
const headers = { Authorization: `Bearer ${token.value}` };
const action = uploadImg;
//
const projectId = computed(() => store.getters['projects/projectId']); // ID
const code = computed(() => store.state.task.label); // code
const questionList = ref([]);
//
const currIndex = ref(null);
const intellectualId = computed(() => store.state.task.intellectualId); // ID
getDataByCode();
watch(intellectualId, () => {
getDataByCode();
});
const beforeUpload = index => {
currIndex.value = index;
};
const handleChange = info => {
const resFileList = [...info.fileList];
//
const arr = ref([]);
resFileList.forEach(file => {
let num = -1;
arr.value.forEach((item, index) => {
if (file.name === item.name) {
num = index;
}
});
if (num > -1) {
arr.value.splice(num, 1);
}
arr.value.push(file);
});
//
arr.value = arr.value.map(file => {
if (file.response) {
file.url = file.response.data.filePath;
file.id = file.response.data.fileId;
}
return file;
});
questionList.value[currIndex.value].files = arr.value;
};
const onSubmit = async () => {
const params = {
param: {
code: code.value,
projectId: projectId.value,
intellectualId: intellectualId.value,
questionAndAnswerList: [],
},
};
const arr = [];
questionList.value.forEach(item => {
const obj = {
questionId: item.questionId,
answerList: [],
};
if (item.type === 1 || item.type === 2 || item.type === 5) {
obj.answerList.push(item.con);
}
if (item.type === 6 && item.date) {
obj.answerList.push(dayjs(item.date).format('x'));
}
if (item.type === 7) {
item.files.forEach(val => {
const file = {
id: val.id,
name: val.name,
url: val.url,
};
obj.answerList.push(JSON.stringify(file));
});
}
arr.push(obj);
});
params.param.questionAndAnswerList = arr;
await store.dispatch('task/submitIntellectual', params);
getDataByCode();
};
async function getDataByCode() {
const params = {
param: {
code: code.value,
projectId: projectId.value,
intellectualId: intellectualId.value,
},
};
const data = await store.dispatch('task/getIntellectual', params);
data.forEach(item => {
if (item.type === 1 || item.type === 2 || item.type === 5) {
item.con = '';
if (item.answerList.length > 0) {
item.con = item.answerList[0].answer;
}
}
if (item.type === 6) {
item.date = null;
if (item.answerList.length > 0) {
if (item.answerList[0].answer) {
item.date = dayjs(Number(item.answerList[0].answer)).format('YYYY-MM-DD');
item.date = dayjs(item.date, 'YYYY-MM-DD');
}
}
}
if (item.type === 7) {
item.files = [];
if (item.answerList.length > 0) {
item.answerList.forEach(val => {
if (val.answer) val.answer = JSON.parse(val.answer);
item.files.push(val.answer);
});
}
}
});
questionList.value = data;
}
</script>
<style scoped>
.task-detail {
background-color: #fff;
}
.ant-col {
margin-top: 10px;
}
.ant-col:nth-child(-n + 4) {
margin-top: 2px;
}
.deliverables .ant-input,
.deliverables-son .ant-input {
width: 23px;
height: 14px;
border-radius: 0;
padding: 0;
font-size: 12px;
color: #1890ff;
text-align: center;
margin-left: 5px;
}
.deliverables-son {
margin-top: 10px !important;
}
</style>

251
src/components/tall/task/PublishWork.vue

@ -1,251 +0,0 @@
<template>
<div class="task-form bg-white border-radius-10">
<a-form>
<div v-for="(item, index) in questionList" :key="index">
<template v-if="item.type === 1">
<a-form-item>
<label class="color-3">{{ item.question }}</label>
<a-input v-model:value="item.con" :placeholder="item.question" />
</a-form-item>
</template>
<template v-if="item.type === 2">
<a-form-item>
<label class="color-3">{{ item.question }}</label>
<a-textarea v-model:value="item.con" :placeholder="item.question" />
</a-form-item>
</template>
<template v-if="item.type === 3"> </template>
<template v-if="item.type === 4"> </template>
<template v-if="item.type === 5">
<a-form-item>
<label class="color-3">{{ item.question }}</label>
<a-select ref="select" v-model:value="item.con">
<a-select-option :value="val.submitValue" v-for="(val, key) in item.optionList" :key="key">{{
val.showValue
}}</a-select-option>
</a-select>
</a-form-item>
</template>
<template v-if="item.type === 6">
<a-form-item>
<label class="color-3">{{ item.question }}</label>
<a-date-picker v-model:value="item.date" />
</a-form-item>
</template>
<template v-if="item.type === 7">
<a-form-item>
<label class="color-3">{{ item.question }}</label>
<a-upload-dragger
v-model:fileList="item.files"
name="param"
:multiple="true"
:action="action"
:headers="headers"
:accept="'.pdf'"
:before-upload="beforeUpload(index)"
@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>
</template>
</div>
<a-form-item class="text-right">
<a-button type="primary" html-type="submit" @click="onSubmit">上传</a-button>
</a-form-item>
</a-form>
</div>
</template>
<script setup>
import { ref, computed, watch } from 'vue';
import { useStore } from 'vuex';
import dayjs from 'dayjs';
import { InboxOutlined } from '@ant-design/icons-vue';
import { uploadImg } from 'apis';
const store = useStore();
//
const token = computed(() => store.getters['user/token']);
const headers = { Authorization: `Bearer ${token.value}` };
const action = uploadImg;
//
const projectId = computed(() => store.getters['projects/projectId']); // ID
const code = computed(() => store.state.task.label); // code
const questionList = ref([]);
//
const currIndex = ref(null);
const intellectualId = computed(() => store.state.task.intellectualId); // ID
getDataByCode();
watch(intellectualId, () => {
getDataByCode();
});
const beforeUpload = index => {
currIndex.value = index;
};
const handleChange = info => {
const resFileList = [...info.fileList];
//
const arr = ref([]);
resFileList.forEach(file => {
let num = -1;
arr.value.forEach((item, index) => {
if (file.name === item.name) {
num = index;
}
});
if (num > -1) {
arr.value.splice(num, 1);
}
arr.value.push(file);
});
//
arr.value = arr.value.map(file => {
if (file.response) {
file.url = file.response.data.filePath;
file.id = file.response.data.fileId;
}
return file;
});
questionList.value[currIndex.value].files = arr.value;
};
const onSubmit = async () => {
const params = {
param: {
code: code.value,
projectId: projectId.value,
intellectualId: intellectualId.value,
questionAndAnswerList: [],
},
};
const arr = [];
questionList.value.forEach(item => {
const obj = {
questionId: item.questionId,
answerList: [],
};
if (item.type === 1 || item.type === 2 || item.type === 5) {
obj.answerList.push(item.con);
}
if (item.type === 6 && item.date) {
obj.answerList.push(dayjs(item.date).format('x'));
}
if (item.type === 7) {
item.files.forEach(val => {
const file = {
id: val.id,
name: val.name,
url: val.url,
};
obj.answerList.push(JSON.stringify(file));
});
}
arr.push(obj);
});
params.param.questionAndAnswerList = arr;
await store.dispatch('task/submitIntellectual', params);
getDataByCode();
};
async function getDataByCode() {
const params = {
param: {
code: code.value,
projectId: projectId.value,
intellectualId: intellectualId.value,
},
};
const data = await store.dispatch('task/getIntellectual', params);
data.forEach(item => {
if (item.type === 1 || item.type === 2 || item.type === 5) {
item.con = '';
if (item.answerList.length > 0) {
item.con = item.answerList[0].answer;
}
}
if (item.type === 6) {
item.date = null;
if (item.answerList.length > 0) {
if (item.answerList[0].answer) {
item.date = dayjs(Number(item.answerList[0].answer)).format('YYYY-MM-DD');
item.date = dayjs(item.date, 'YYYY-MM-DD');
}
}
}
if (item.type === 7) {
item.files = [];
if (item.answerList.length > 0) {
item.answerList.forEach(val => {
if (val.answer) val.answer = JSON.parse(val.answer);
item.files.push(val.answer);
});
}
}
});
questionList.value = data;
}
</script>
<style scoped>
.task-detail {
background-color: #fff;
}
.ant-col {
margin-top: 10px;
}
.ant-col:nth-child(-n + 4) {
margin-top: 2px;
}
.deliverables .ant-input,
.deliverables-son .ant-input {
width: 23px;
height: 14px;
border-radius: 0;
padding: 0;
font-size: 12px;
color: #1890ff;
text-align: center;
margin-left: 5px;
}
.deliverables-son {
margin-top: 10px !important;
}
</style>

236
src/components/tall/task/Result.vue

@ -1,236 +0,0 @@
<template>
<div class="task-form bg-white border-radius-10">
<a-form>
<div v-for="(item, index) in questionList" :key="index">
<template v-if="item.type === 1">
<a-form-item>
<label class="color-3">{{ item.question }}</label>
<a-input v-model:value="item.con" :placeholder="item.question" />
</a-form-item>
</template>
<template v-if="item.type === 2">
<a-form-item>
<label class="color-3">{{ item.question }}</label>
<a-textarea v-model:value="item.con" :placeholder="item.question" />
</a-form-item>
</template>
<template v-if="item.type === 3"> </template>
<template v-if="item.type === 4"> </template>
<template v-if="item.type === 5"> </template>
<template v-if="item.type === 6">
<a-form-item>
<label class="color-3">{{ item.question }}</label>
<a-date-picker v-model:value="item.date" />
</a-form-item>
</template>
<template v-if="item.type === 7">
<a-form-item>
<label class="color-3">{{ item.question }}</label>
<a-upload-dragger
v-model:fileList="item.files"
name="param"
:multiple="true"
:action="action"
:headers="headers"
:accept="'.pdf'"
:before-upload="beforeUpload(index)"
@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>
</template>
</div>
<a-form-item class="text-right">
<a-button type="primary" html-type="submit" @click="onSubmit">上传验收证书</a-button>
</a-form-item>
</a-form>
</div>
</template>
<script setup>
import { ref, computed } from 'vue';
import { useStore } from 'vuex';
import dayjs from 'dayjs';
import { InboxOutlined } from '@ant-design/icons-vue';
import { uploadImg } from 'apis';
const store = useStore();
//
const token = computed(() => store.getters['user/token']);
const headers = { Authorization: `Bearer ${token.value}` };
const action = uploadImg;
//
const projectId = computed(() => store.getters['projects/projectId']); // ID
const code = computed(() => store.state.task.label); // code
const questionList = ref([]);
//
const currIndex = ref(null);
getDataByCode();
const beforeUpload = index => {
currIndex.value = index;
};
const handleChange = info => {
const resFileList = [...info.fileList];
//
const arr = ref([]);
resFileList.forEach(file => {
let num = -1;
arr.value.forEach((item, index) => {
if (file.name === item.name) {
num = index;
}
});
if (num > -1) {
arr.value.splice(num, 1);
}
arr.value.push(file);
});
//
arr.value = arr.value.map(file => {
if (file.response) {
file.url = file.response.data.filePath;
file.id = file.response.data.fileId;
}
return file;
});
questionList.value[currIndex.value].files = arr.value;
};
const onSubmit = async () => {
const params = {
param: {
code: code.value,
projectId: projectId.value,
questionAndAnswerList: [],
},
};
const arr = [];
questionList.value.forEach(item => {
const obj = {
questionId: item.questionId,
answerList: [],
};
if (item.type === 1 || item.type === 2) {
obj.answerList.push(item.con);
}
if (item.type === 6 && item.date) {
obj.answerList.push(dayjs(item.date).format('x'));
}
if (item.type === 7) {
item.files.forEach(val => {
const file = {
id: val.id,
name: val.name,
url: val.url,
};
obj.answerList.push(JSON.stringify(file));
});
}
arr.push(obj);
});
params.param.questionAndAnswerList = arr;
await store.dispatch('task/submitAnswer', params);
getDataByCode();
};
async function getDataByCode() {
const params = {
param: {
code: code.value,
projectId: projectId.value,
intellectualId: null,
},
};
const data = await store.dispatch('task/getByCode', params);
data.forEach(item => {
if (item.type === 1 || item.type === 2) {
item.con = '';
if (item.answerList.length > 0) {
item.con = item.answerList[0].answer;
}
}
if (item.type === 6) {
item.date = null;
if (item.answerList.length > 0) {
if (item.answerList[0].answer) {
item.date = dayjs(Number(item.answerList[0].answer)).format('YYYY-MM-DD');
item.date = dayjs(item.date, 'YYYY-MM-DD');
}
}
}
if (item.type === 7) {
item.files = [];
if (item.answerList.length > 0) {
item.answerList.forEach(val => {
if (val.answer) val.answer = JSON.parse(val.answer);
item.files.push(val.answer);
});
}
}
});
questionList.value = data;
}
</script>
<style scoped>
.task-detail {
background-color: #fff;
}
.ant-col {
margin-top: 10px;
}
.ant-col:nth-child(-n + 4) {
margin-top: 2px;
}
.deliverables .ant-input,
.deliverables-son .ant-input {
width: 23px;
height: 14px;
border-radius: 0;
padding: 0;
font-size: 12px;
color: #1890ff;
text-align: center;
margin-left: 5px;
}
.deliverables-son {
margin-top: 10px !important;
}
</style>

284
src/components/tall/task/ScientificPayoffs.vue

@ -1,284 +0,0 @@
<template>
<div>
<a-table
class="member-list"
:columns="columns"
:data-source="dataList"
:row-class-name="(_record, index) => (index % 2 === 1 ? null : 'table-striped')"
>
<template #bodyCell="{ column, text, record }">
<div class="flex items-center" style="min-width: 160px" v-if="column.key === 'action'">
<a-button
:disabled="record.status === 0 ? false : true"
class="action-btn edit-btn"
type="primary"
@click="showModal(record.id, 'success')"
>
通过
</a-button>
<a-button
:disabled="record.status === 0 ? false : true"
class="action-btn del-btn"
type="primary"
@click="showModal(record.id, 'fail')"
>
驳回
</a-button>
<img v-if="record.status === 0" style="width: 28px" src="https://www.tall.wiki/staticrec/experiment/unlock.png" />
<img
v-if="record.status === 1"
class="cursor-pointer"
style="width: 28px"
src="https://www.tall.wiki/staticrec/experiment/locking.png"
@click="showModal(record.id, 'tips')"
/>
<div v-if="record.status === 3" class="status-btn" style="background: #cccccc"></div>
<div
v-if="record.status === 4"
class="status-btn cursor-pointer"
style="background: #ff5353"
@click="showModal(record.id, 'tips')"
>
</div>
</div>
<template v-else-if="['information', 'result', 'sourceCode'].includes(column.dataIndex)">
<a class="break-all" style="color: #1890ff" :href="text" target="_blank" :title="text">{{ text }}</a>
</template>
<template v-else-if="['report', 'course'].includes(column.dataIndex)">
<a
class="break-all"
style="color: #1890ff"
:href="!text || !text.url ? '' : text.url"
target="_blank"
:title="!text || !text.name ? '' : text.name"
>
{{ !text || !text.name ? '' : text.name }}
</a>
</template>
</template>
</a-table>
</div>
<!-- 确定模态框 -->
<a-modal v-model:visible="success" :closable="false" @ok="handleOk('success')">
<div class="modal-title flex items-center">
<CheckCircleFilled style="margin-right: 8px; font-size: 18px; color: #52c41a" />
<span class="color-3" style="font-size: 18px; font-weight: 600">确定要审核通过该实验交付物吗</span>
</div>
<div class="modal-con color-9" style="padding-left: 24px; margin-top: 16px; font-size: 16px; line-height: 26px">
确定通过审核后改实验交付物将被锁定如需修改需向课题主持人提交解锁请求通过后即可修改
</div>
</a-modal>
<!-- 拒绝模态框 -->
<a-modal v-model:visible="fail" :closable="false" @ok="handleOk('fail')">
<div class="modal-title flex items-center">
<CloseCircleFilled style="margin-right: 8px; font-size: 18px; color: #ff5353" />
<span class="color-3" style="font-size: 18px; font-weight: 600">确定要驳回该实验交付物吗</span>
</div>
<div class="modal-con color-9" style="padding-left: 24px; margin-top: 16px">
<div style="margin-bottom: 5px; font-size: 16px; line-height: 26px">驳回原因</div>
<a-textarea v-model:value="remark1" placeholder="驳回原因" :auto-size="{ minRows: 2, maxRows: 5 }" />
</div>
</a-modal>
<!-- 提示模态框 -->
<a-modal v-model:visible="tips" :closable="false" @ok="handleOk('tips')">
<div class="modal-title flex items-center">
<ExclamationCircleFilled style="margin-right: 8px; font-size: 18px; color: #fa8c16" />
<span class="color-3" style="font-size: 18px; font-weight: 600">确定要申请解锁该项实验交付物吗</span>
</div>
<div class="modal-con color-9" style="padding-left: 24px; margin-top: 16px">
<div style="margin-bottom: 5px; font-size: 16px; line-height: 26px">申请原因</div>
<a-textarea v-model:value="remark2" placeholder="申请原因" :auto-size="{ minRows: 2, maxRows: 5 }" />
</div>
</a-modal>
</template>
<script setup>
import { ref, computed } from 'vue';
import { useStore } from 'vuex';
import { queryExperimentation, examineExperimentation, applyUnlock } from 'apis';
import { CheckCircleFilled, CloseCircleFilled, ExclamationCircleFilled } from '@ant-design/icons-vue';
import { message } from 'ant-design-vue';
const store = useStore();
const projectId = computed(() => store.getters['projects/projectId']);
const success = ref(false);
const fail = ref(false);
const tips = ref(false);
const currId = ref(null);
const remark1 = ref(null);
const remark2 = ref(null);
const columns = ref([
{ title: '序号', dataIndex: 'indexId', key: 'indexId' },
{ title: '实验名称', dataIndex: 'name', key: 'name' },
{ title: '实验报告', dataIndex: 'report', key: 'report' },
{ title: '实验过程', dataIndex: 'course', key: 'course' },
{ title: '实验数据', dataIndex: 'information', key: 'information' },
{ title: '程序代码', dataIndex: 'sourceCode', key: 'sourceCode' },
{ title: '实验结果', dataIndex: 'result', key: 'result' },
{ title: '审核', key: 'action', dataIndex: 'action' },
]);
const dataList = ref([]);
getExperimentations();
//
const showModal = (id, tip) => {
currId.value = id;
if (tip === 'success') {
success.value = true;
} else if (tip === 'fail') {
fail.value = true;
} else if (tip === 'tips') {
tips.value = true;
}
};
//
const handleOk = async tip => {
if (tip === 'success') {
success.value = false;
await examine(0);
} else if (tip === 'fail') {
fail.value = false;
await examine(1);
} else if (tip === 'tips') {
tips.value = false;
await toUnlock();
}
};
async function getExperimentations() {
try {
const params = { param: { projectId: projectId.value } };
const data = await queryExperimentation(params);
data.forEach((item, index) => {
item.indexId = index + 1;
if (item.report) item.report = JSON.parse(item.report);
if (item.course) item.course = JSON.parse(item.course);
});
dataList.value = data;
} catch (error) {
message.info(error);
throw new Error(error);
}
}
// /
async function examine(type) {
try {
const params = {
param: {
id: currId.value,
type,
remark: remark1.value ? remark1.value : '',
},
};
await examineExperimentation(params);
getExperimentations();
} catch (error) {
message.info(error);
throw new Error(error);
}
}
//
async function toUnlock() {
try {
const params = {
param: {
id: currId.value,
remark: remark2.value ? remark2.value : '',
},
};
await applyUnlock(params);
getExperimentations();
} catch (error) {
message.info(error);
throw new Error(error);
}
}
</script>
<style scoped>
.member-list {
border-radius: 10px 10px 0 0;
overflow: hidden;
}
:deep(.table-striped) td {
background-color: #fafafa;
}
.action-btn {
flex-shrink: 0;
width: 50px !important;
height: 28px !important;
font-size: 14px !important;
padding: 0;
letter-spacing: 0 !important;
}
:deep(.ant-table-pagination.ant-pagination) {
height: 0;
margin: 0;
}
.add-btn {
height: 60px;
background: #fff;
padding-left: 36px;
}
.edit-btn {
background: #0dc26c;
border: 0;
}
.del-btn {
margin-left: 16px;
margin-right: 16px;
background: #ff5353;
border: 0;
}
.status-btn {
width: 28px;
height: 28px;
color: #fff;
border-radius: 50%;
text-align: center;
line-height: 28px;
}
:deep(.ant-table-container table > thead > tr th) {
min-width: 100px;
}
:deep(.ant-table-container table > thead > tr th:first-child) {
min-width: 70px;
text-align: center;
}
:deep(.ant-table-container table > tbody > tr > td:first-child) {
text-align: center;
}
:deep(.ant-table-container table > thead > tr th:last-child) {
min-width: 192px;
}
:deep(.ant-btn-primary[disabled]) {
color: #fff;
background: #cccccc;
}
</style>

246
src/components/tall/task/SubConclusion.vue

@ -1,246 +0,0 @@
<template>
<div class="task-form bg-white border-radius-10">
<a-form>
<div v-for="(item, index) in questionList" :key="index">
<template v-if="item.type === 1">
<a-form-item>
<label class="color-3">{{ item.question }}</label>
<a-input v-model:value="item.con" :placeholder="item.question" />
</a-form-item>
</template>
<template v-if="item.type === 2">
<a-form-item>
<label class="color-3">{{ item.question }}</label>
<a-textarea v-model:value="item.con" :placeholder="item.question" />
</a-form-item>
</template>
<template v-if="item.type === 3"> </template>
<template v-if="item.type === 4"> </template>
<template v-if="item.type === 5"> </template>
<template v-if="item.type === 6">
<a-form-item>
<label class="color-3">{{ item.question }}</label>
<a-date-picker v-model:value="item.date" />
</a-form-item>
</template>
<template v-if="item.type === 7">
<a-form-item>
<label class="color-3">{{ item.question }}</label>
<a-upload-dragger
v-model:fileList="item.files"
name="param"
:multiple="true"
:action="action"
:headers="headers"
:accept="'.pdf'"
:before-upload="beforeUpload(index)"
@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>
</template>
</div>
<a-form-item class="text-right">
<a-button type="primary" html-type="submit" @click="onSubmit">上传课题结题报告</a-button>
</a-form-item>
</a-form>
</div>
</template>
<script setup>
import { ref, computed } from 'vue';
import { useStore } from 'vuex';
import dayjs from 'dayjs';
import { InboxOutlined } from '@ant-design/icons-vue';
import { uploadImg } from 'apis';
// import { message } from 'ant-design-vue';
const store = useStore();
//
const token = computed(() => store.getters['user/token']);
const headers = { Authorization: `Bearer ${token.value}` };
const action = uploadImg;
//
const projectId = computed(() => store.getters['projects/projectId']); // ID
const code = computed(() => store.state.task.label); // code
const questionList = ref([]);
//
const currIndex = ref(null);
//
// const subStartTime = computed(() => store.state.layout.subStartTime);
// const subEndTime = computed(() => store.state.layout.subEndTime);
getDataByCode();
const beforeUpload = index => {
currIndex.value = index;
};
const handleChange = info => {
const resFileList = [...info.fileList];
//
const arr = ref([]);
resFileList.forEach(file => {
let num = -1;
arr.value.forEach((item, index) => {
if (file.name === item.name) {
num = index;
}
});
if (num > -1) {
arr.value.splice(num, 1);
}
arr.value.push(file);
});
//
arr.value = arr.value.map(file => {
if (file.response) {
file.url = file.response.data.filePath;
file.id = file.response.data.fileId;
}
return file;
});
questionList.value[currIndex.value].files = arr.value;
};
const onSubmit = async () => {
const params = {
param: {
code: code.value,
projectId: projectId.value,
questionAndAnswerList: [],
},
};
const arr = [];
questionList.value.forEach(item => {
const obj = {
questionId: item.questionId,
answerList: [],
};
if (item.type === 1 || item.type === 2) {
obj.answerList.push(item.con);
}
if (item.type === 6 && item.date) {
const time = dayjs(item.date).format('x');
// if (time < subStartTime.value || time > subEndTime.value) {
// message.info('');
// return false;
// }
obj.answerList.push(time);
}
if (item.type === 7) {
item.files.forEach(val => {
const file = {
id: val.id,
name: val.name,
url: val.url,
};
obj.answerList.push(JSON.stringify(file));
});
}
arr.push(obj);
});
params.param.questionAndAnswerList = arr;
await store.dispatch('task/submitAnswer', params);
getDataByCode();
};
async function getDataByCode() {
const params = {
param: {
code: code.value,
projectId: projectId.value,
intellectualId: null,
},
};
const data = await store.dispatch('task/getByCode', params);
data.forEach(item => {
if (item.type === 1 || item.type === 2) {
item.con = '';
if (item.answerList.length > 0) {
item.con = item.answerList[0].answer;
}
}
if (item.type === 6) {
item.date = null;
if (item.answerList.length > 0) {
if (item.answerList[0].answer) {
item.date = dayjs(Number(item.answerList[0].answer)).format('YYYY-MM-DD');
item.date = dayjs(item.date, 'YYYY-MM-DD');
}
}
}
if (item.type === 7) {
item.files = [];
if (item.answerList.length > 0) {
item.answerList.forEach(val => {
if (val.answer) val.answer = JSON.parse(val.answer);
item.files.push(val.answer);
});
}
}
});
questionList.value = data;
}
</script>
<style scoped>
.task-detail {
background-color: #fff;
}
.ant-col {
margin-top: 10px;
}
.ant-col:nth-child(-n + 4) {
margin-top: 2px;
}
.deliverables .ant-input,
.deliverables-son .ant-input {
width: 23px;
height: 14px;
border-radius: 0;
padding: 0;
font-size: 12px;
color: #1890ff;
text-align: center;
margin-left: 5px;
}
.deliverables-son {
margin-top: 10px !important;
}
</style>

246
src/components/tall/task/SubInterimInspection.vue

@ -1,246 +0,0 @@
<template>
<div class="task-form bg-white border-radius-10">
<a-form>
<div v-for="(item, index) in questionList" :key="index">
<template v-if="item.type === 1">
<a-form-item>
<label class="color-3">{{ item.question }}</label>
<a-input v-model:value="item.con" :placeholder="item.question" />
</a-form-item>
</template>
<template v-if="item.type === 2">
<a-form-item>
<label class="color-3">{{ item.question }}</label>
<a-textarea v-model:value="item.con" :placeholder="item.question" />
</a-form-item>
</template>
<template v-if="item.type === 3"> </template>
<template v-if="item.type === 4"> </template>
<template v-if="item.type === 5"> </template>
<template v-if="item.type === 6">
<a-form-item>
<label class="color-3">{{ item.question }}</label>
<a-date-picker v-model:value="item.date" />
</a-form-item>
</template>
<template v-if="item.type === 7">
<a-form-item>
<label class="color-3">{{ item.question }}</label>
<a-upload-dragger
v-model:fileList="item.files"
name="param"
:multiple="true"
:action="action"
:headers="headers"
:accept="'.pdf'"
:before-upload="beforeUpload(index)"
@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>
</template>
</div>
<a-form-item class="text-right">
<a-button type="primary" html-type="submit" @click="onSubmit">上传中期检查报告</a-button>
</a-form-item>
</a-form>
</div>
</template>
<script setup>
import { ref, computed } from 'vue';
import { useStore } from 'vuex';
import dayjs from 'dayjs';
import { InboxOutlined } from '@ant-design/icons-vue';
import { uploadImg } from 'apis';
// import { message } from 'ant-design-vue';
const store = useStore();
//
const token = computed(() => store.getters['user/token']);
const headers = { Authorization: `Bearer ${token.value}` };
const action = uploadImg;
//
const projectId = computed(() => store.getters['projects/projectId']); // ID
const code = computed(() => store.state.task.label); // code
const questionList = ref([]);
//
const currIndex = ref(null);
//
// const subStartTime = computed(() => store.state.layout.subStartTime);
// const subEndTime = computed(() => store.state.layout.subEndTime);
getDataByCode();
const beforeUpload = index => {
currIndex.value = index;
};
const handleChange = info => {
const resFileList = [...info.fileList];
//
const arr = ref([]);
resFileList.forEach(file => {
let num = -1;
arr.value.forEach((item, index) => {
if (file.name === item.name) {
num = index;
}
});
if (num > -1) {
arr.value.splice(num, 1);
}
arr.value.push(file);
});
//
arr.value = arr.value.map(file => {
if (file.response) {
file.url = file.response.data.filePath;
file.id = file.response.data.fileId;
}
return file;
});
questionList.value[currIndex.value].files = arr.value;
};
const onSubmit = async () => {
const params = {
param: {
code: code.value,
projectId: projectId.value,
questionAndAnswerList: [],
},
};
const arr = [];
questionList.value.forEach(item => {
const obj = {
questionId: item.questionId,
answerList: [],
};
if (item.type === 1 || item.type === 2) {
obj.answerList.push(item.con);
}
if (item.type === 6 && item.date) {
const time = dayjs(item.date).format('x');
// if (time < subStartTime.value || time > subEndTime.value) {
// message.info('');
// return false;
// }
obj.answerList.push(time);
}
if (item.type === 7) {
item.files.forEach(val => {
const file = {
id: val.id,
name: val.name,
url: val.url,
};
obj.answerList.push(JSON.stringify(file));
});
}
arr.push(obj);
});
params.param.questionAndAnswerList = arr;
await store.dispatch('task/submitAnswer', params);
getDataByCode();
};
async function getDataByCode() {
const params = {
param: {
code: code.value,
projectId: projectId.value,
intellectualId: null,
},
};
const data = await store.dispatch('task/getByCode', params);
data.forEach(item => {
if (item.type === 1 || item.type === 2) {
item.con = '';
if (item.answerList.length > 0) {
item.con = item.answerList[0].answer;
}
}
if (item.type === 6) {
item.date = null;
if (item.answerList.length > 0) {
if (item.answerList[0].answer) {
item.date = dayjs(Number(item.answerList[0].answer)).format('YYYY-MM-DD');
item.date = dayjs(item.date, 'YYYY-MM-DD');
}
}
}
if (item.type === 7) {
item.files = [];
if (item.answerList.length > 0) {
item.answerList.forEach(val => {
if (val.answer) val.answer = JSON.parse(val.answer);
item.files.push(val.answer);
});
}
}
});
questionList.value = data;
}
</script>
<style scoped>
.task-detail {
background-color: #fff;
}
.ant-col {
margin-top: 10px;
}
.ant-col:nth-child(-n + 4) {
margin-top: 2px;
}
.deliverables .ant-input,
.deliverables-son .ant-input {
width: 23px;
height: 14px;
border-radius: 0;
padding: 0;
font-size: 12px;
color: #1890ff;
text-align: center;
margin-left: 5px;
}
.deliverables-son {
margin-top: 10px !important;
}
</style>

244
src/components/tall/task/SubMeetingManagement.vue

@ -1,244 +0,0 @@
<template>
<div class="task-form bg-white border-radius-10">
<a-form :model="topicMeetFormData">
<a-form-item>
<label class="color-3">会议名称</label>
<a-input v-model:value="topicMeetFormData.name" placeholder="会议名称" />
</a-form-item>
<a-form-item>
<label class="color-3">会议日期</label>
<a-space direction="vertical" :size="12">
<a-range-picker v-model:value="topicMeetFormData.date" />
</a-space>
</a-form-item>
<a-form-item>
<label class="color-3">会议地点</label>
<a-input v-model:value="topicMeetFormData.address" placeholder="会议地点" />
</a-form-item>
<a-form-item>
<label class="color-3">会议纪要</label>
<a-upload-dragger
v-model:fileList="summaryList"
name="param"
:multiple="true"
:action="action"
:headers="headers"
:accept="'.pdf'"
@change="handleChange($event, 1)"
>
<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="attachmentList"
name="param"
:multiple="true"
:action="action"
:headers="headers"
:accept="'.jpg,.jpeg,.rar,.zip,.png'"
@change="handleChange($event, 2)"
>
<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">格式jpgjpegrarzip</p>
</a-upload-dragger>
</a-form-item>
<a-form-item class="text-right">
<a-button type="primary" html-type="submit" @click="onSubmit">上传会议资料</a-button>
</a-form-item>
</a-form>
</div>
</template>
<script setup>
import { ref, computed, watch } from 'vue';
import { useStore } from 'vuex';
import { InboxOutlined } from '@ant-design/icons-vue';
import { uploadImg, saveMeeting, getMeetDetail } from 'apis';
import dayjs from 'dayjs';
import { message } from 'ant-design-vue';
const store = useStore();
const token = computed(() => store.getters['user/token']);
const headers = { Authorization: `Bearer ${token.value}` };
const action = uploadImg;
const summaryList = ref([]);
const attachmentList = ref([]);
const subMeetId = computed(() => store.state.task.subMeetId);
const projectId = computed(() => store.getters['projects/projectId']);
const topicMeetFormData = ref({
projectId: projectId.value,
id: '',
name: '',
date: [],
startTime: '',
endTime: '',
address: '',
summaryList: [],
attachmentList: [],
});
getMeetingInfo();
watch(subMeetId, async () => {
if (subMeetId.value) {
await getMeetingInfo();
} else {
topicMeetFormData.value = {
projectId: projectId.value,
id: '',
name: '',
date: [],
startTime: '',
endTime: '',
address: '',
summaryList: [],
attachmentList: [],
};
summaryList.value = [];
attachmentList.value = [];
}
});
const handleChange = (info, index) => {
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.filePath;
file.id = file.response.data.fileId;
}
return file;
});
if (index === 1) {
summaryList.value = arr.value;
} else if (index === 2) {
attachmentList.value = arr.value;
}
};
// ID
async function getMeetingInfo() {
try {
const params = { param: { id: subMeetId.value } };
const data = await getMeetDetail(params);
if (data) {
data.projectId = projectId.value;
const start = dayjs(Number(data.startTime));
const end = dayjs(Number(data.endTime));
data.date = [start, end];
topicMeetFormData.value = data;
summaryList.value = [];
attachmentList.value = [];
data.summaryList.forEach(item => {
const obj = {
id: item.fileId,
name: item.fileName,
url: item.filePath,
};
summaryList.value.push(obj);
});
data.attachmentList.forEach(item => {
const obj = {
id: item.fileId,
name: item.fileName,
url: item.filePath,
};
attachmentList.value.push(obj);
});
} else {
topicMeetFormData.value = {
projectId: projectId.value,
id: '',
name: '',
date: [],
startTime: '',
endTime: '',
address: '',
summaryList: [],
attachmentList: [],
};
summaryList.value = [];
attachmentList.value = [];
}
} catch (error) {
message.info(error);
throw new Error(error);
}
}
const onSubmit = async () => {
summaryList.value.forEach(item => {
const obj = {
fileId: item.id,
fileName: item.name,
filePath: item.url,
};
topicMeetFormData.value.summaryList.push(obj);
});
attachmentList.value.forEach(item => {
const obj = {
fileId: item.id,
fileName: item.name,
filePath: item.url,
};
topicMeetFormData.value.attachmentList.push(obj);
});
topicMeetFormData.value.date.forEach((item, index) => {
if (index === 0) {
topicMeetFormData.value.startTime = dayjs(item).format('x');
} else {
topicMeetFormData.value.endTime = dayjs(item).format('x');
}
});
const params = { param: topicMeetFormData.value };
await saveMeeting(params);
getMeetingInfo();
};
</script>
<style scoped>
.task-detail {
background-color: #fff;
}
</style>

0
src/components/tall/task/SubMemberManagement.vue

246
src/components/tall/task/SubResult.vue

@ -1,246 +0,0 @@
<template>
<div class="task-form bg-white border-radius-10">
<a-form>
<div v-for="(item, index) in questionList" :key="index">
<template v-if="item.type === 1">
<a-form-item>
<label class="color-3">{{ item.question }}</label>
<a-input v-model:value="item.con" :placeholder="item.question" />
</a-form-item>
</template>
<template v-if="item.type === 2">
<a-form-item>
<label class="color-3">{{ item.question }}</label>
<a-textarea v-model:value="item.con" :placeholder="item.question" />
</a-form-item>
</template>
<template v-if="item.type === 3"> </template>
<template v-if="item.type === 4"> </template>
<template v-if="item.type === 5"> </template>
<template v-if="item.type === 6">
<a-form-item>
<label class="color-3">{{ item.question }}</label>
<a-date-picker v-model:value="item.date" />
</a-form-item>
</template>
<template v-if="item.type === 7">
<a-form-item>
<label class="color-3">{{ item.question }}</label>
<a-upload-dragger
v-model:fileList="item.files"
name="param"
:multiple="true"
:action="action"
:headers="headers"
:accept="'.pdf'"
:before-upload="beforeUpload(index)"
@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>
</template>
</div>
<a-form-item class="text-right">
<a-button type="primary" html-type="submit" @click="onSubmit">上传验收证书</a-button>
</a-form-item>
</a-form>
</div>
</template>
<script setup>
import { ref, computed } from 'vue';
import { useStore } from 'vuex';
import dayjs from 'dayjs';
import { InboxOutlined } from '@ant-design/icons-vue';
import { uploadImg } from 'apis';
// import { message } from 'ant-design-vue';
const store = useStore();
//
const token = computed(() => store.getters['user/token']);
const headers = { Authorization: `Bearer ${token.value}` };
const action = uploadImg;
//
const projectId = computed(() => store.getters['projects/projectId']); // ID
const code = computed(() => store.state.task.label); // code
const questionList = ref([]);
//
const currIndex = ref(null);
//
// const subStartTime = computed(() => store.state.layout.subStartTime);
// const subEndTime = computed(() => store.state.layout.subEndTime);
getDataByCode();
const beforeUpload = index => {
currIndex.value = index;
};
const handleChange = info => {
const resFileList = [...info.fileList];
//
const arr = ref([]);
resFileList.forEach(file => {
let num = -1;
arr.value.forEach((item, index) => {
if (file.name === item.name) {
num = index;
}
});
if (num > -1) {
arr.value.splice(num, 1);
}
arr.value.push(file);
});
//
arr.value = arr.value.map(file => {
if (file.response) {
file.url = file.response.data.filePath;
file.id = file.response.data.fileId;
}
return file;
});
questionList.value[currIndex.value].files = arr.value;
};
const onSubmit = async () => {
const params = {
param: {
code: code.value,
projectId: projectId.value,
questionAndAnswerList: [],
},
};
const arr = [];
questionList.value.forEach(item => {
const obj = {
questionId: item.questionId,
answerList: [],
};
if (item.type === 1 || item.type === 2) {
obj.answerList.push(item.con);
}
if (item.type === 6 && item.date) {
const time = dayjs(item.date).format('x');
// if (time < subStartTime.value || time > subEndTime.value) {
// message.info('');
// return false;
// }
obj.answerList.push(time);
}
if (item.type === 7) {
item.files.forEach(val => {
const file = {
id: val.id,
name: val.name,
url: val.url,
};
obj.answerList.push(JSON.stringify(file));
});
}
arr.push(obj);
});
params.param.questionAndAnswerList = arr;
await store.dispatch('task/submitAnswer', params);
getDataByCode();
};
async function getDataByCode() {
const params = {
param: {
code: code.value,
projectId: projectId.value,
intellectualId: null,
},
};
const data = await store.dispatch('task/getByCode', params);
data.forEach(item => {
if (item.type === 1 || item.type === 2) {
item.con = '';
if (item.answerList.length > 0) {
item.con = item.answerList[0].answer;
}
}
if (item.type === 6) {
item.date = null;
if (item.answerList.length > 0) {
if (item.answerList[0].answer) {
item.date = dayjs(Number(item.answerList[0].answer)).format('YYYY-MM-DD');
item.date = dayjs(item.date, 'YYYY-MM-DD');
}
}
}
if (item.type === 7) {
item.files = [];
if (item.answerList.length > 0) {
item.answerList.forEach(val => {
if (val.answer) val.answer = JSON.parse(val.answer);
item.files.push(val.answer);
});
}
}
});
questionList.value = data;
}
</script>
<style scoped>
.task-detail {
background-color: #fff;
}
.ant-col {
margin-top: 10px;
}
.ant-col:nth-child(-n + 4) {
margin-top: 2px;
}
.deliverables .ant-input,
.deliverables-son .ant-input {
width: 23px;
height: 14px;
border-radius: 0;
padding: 0;
font-size: 12px;
color: #1890ff;
text-align: center;
margin-left: 5px;
}
.deliverables-son {
margin-top: 10px !important;
}
</style>

195
src/components/tall/task/SubSubjectProgress.vue

@ -1,195 +0,0 @@
<template>
<div class="task-progress flex flex-wrap justify-between">
<div class="wrap overflow-hidden">
<a-card title="任务目标">
<div class="flex flex-wrap justify-center">
<div class="achievements border border-right border-bottom text-center">
<p class="num">{{ infoOne.thesis }}/{{ infoOne.totalThesis }}</p>
<p class="name">论文</p>
</div>
<div class="achievements border border-bottom text-center">
<p class="num">{{ infoOne.patent }}/{{ infoOne.totalPatent }}</p>
<p class="name">专利</p>
</div>
<div class="achievements border border-right text-center">
<p class="num">{{ infoOne.theSoft }}/{{ infoOne.totalTheSoft }}</p>
<p class="name">软著</p>
</div>
<div class="achievements border text-center">
<p class="num">{{ infoOne.meeting }}/{{ infoOne.totalMeeting }}</p>
<p class="name">会议</p>
</div>
</div>
</a-card>
</div>
<div class="wrap overflow-hidden">
<a-card title="概览">
<div class="topic">
<p>{{ infoOne.name }}</p>
<a-progress
:percent="infoOne.masterSchedule"
:strokeWidth="22"
:show-info="false"
:stroke-color="'#1890FF'"
:trail-color="'rgba(24, 144, 255, 0.2)'"
/>
</div>
<div class="sub-topic flex justify-between flex-wrap">
<div class="topic" v-for="(item, index) in infoSec" :key="index">
<p>{{ item.name }}</p>
<a-progress
:percent="item.masterSchedule"
:strokeWidth="22"
:show-info="false"
:stroke-color="colorList[index % 4].color"
:trail-color="colorList[index % 4].bgColor"
/>
</div>
</div>
</a-card>
</div>
<div class="wrap overflow-hidden" v-for="(item, index) in infoSec" :key="index">
<a-card :title="item.name">
<div class="flex flex-wrap justify-center">
<div class="achievements border border-right border-bottom text-center">
<p class="num">{{ item.report }}</p>
<p class="name">实验报告</p>
</div>
<div class="achievements border border-bottom text-center">
<p class="num">{{ item.course }}</p>
<p class="name">实验过程</p>
</div>
<div class="achievements border border-right text-center">
<p class="num">{{ item.information }}</p>
<p class="name">实验数据</p>
</div>
<div class="achievements border text-center">
<p class="num">{{ item.result }}</p>
<p class="name">实验结果</p>
</div>
</div>
</a-card>
</div>
</div>
</template>
<script setup>
import { ref, computed } from 'vue';
import { useStore } from 'vuex';
import { getProgress } from 'apis';
import { message } from 'ant-design-vue';
const store = useStore();
const projectId = computed(() => store.getters['projects/projectId']);
const infoOne = ref({});
const infoSec = ref([]);
const colorList = ref([
{ color: '#FF9191', bgColor: 'rgba(255, 145, 145, 0.2)' },
{ color: '#FF934B', bgColor: 'rgba(255, 147, 75, 0.2)' },
{ color: '#B991FF', bgColor: 'rgba(185, 145, 255, 0.2)' },
{ color: '#91C1FF', bgColor: 'rgba(145, 193, 255, 0.2)' },
]);
progress();
async function progress() {
try {
const params = { param: { projectId: projectId.value } };
const data = await getProgress(params);
infoOne.value = data.target;
infoSec.value = data.subTargetList;
} catch (error) {
message.info(error);
throw new Error(error);
}
}
</script>
<style scoped>
.wrap {
margin-top: 16px;
width: calc((100% - 16px) / 2);
background-color: #fff;
border-radius: 10px;
border: 1px solid #cccccc;
}
.wrap:nth-child(-n + 2) {
margin-top: 0;
}
:deep(.ant-card-head) {
padding: 0 16px;
min-height: 45px;
max-height: 45px;
border-color: #cccccc;
font-size: 14px;
}
:deep(.ant-card-head-title) {
padding: 0;
line-height: 45px;
}
.wrap .num {
font-size: 22px;
color: #4b8aff;
}
.wrap .name {
color: #666666;
}
.wrap p {
margin: 0;
}
.border {
border-color: #fff;
}
.border-right {
border-right-color: #cccccc;
}
.border-left {
border-left-color: #cccccc;
}
.border-top {
border-top-color: #cccccc;
}
.border-bottom {
border-bottom-color: #cccccc;
}
.achievements {
width: 40%;
padding: 25px 0;
}
.topic p {
margin-bottom: 8px;
color: #666666;
}
.sub-topic .topic {
margin-top: 40px;
width: calc((100% - 32px) / 2);
}
.ant-card {
height: 100%;
}
.ant-card :deep(.ant-card-body) {
height: calc(100% - 48px);
display: flex;
flex-direction: column;
justify-content: center;
}
</style>

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

@ -1,144 +0,0 @@
<template>
<div class="list-box">
<div class="task-box" v-for="(item, index) in lists" :key="index">
<div class="task-time flex items-center justify-between">
<div class="flex items-center">
<div class="circular"></div>
<span>{{ dayjs(Number(item.time)).format('YYYY年MM月DD日 HH:mm') }}</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" @click="toDetail(item)">
<span class="leading-none truncate color-3" style="width: calc(100% - 30px)" :title="item.name">{{ item.name }}</span>
<RightOutlined style="color: #666" />
</div>
</div>
</div>
</div>
</div>
</div>
</template>
<script setup>
import { computed, ref } from 'vue';
import { useStore } from 'vuex';
import dayjs from 'dayjs';
import { RightOutlined } from '@ant-design/icons-vue';
import { getMeetQuery } from 'apis';
import { message } from 'ant-design-vue';
const store = useStore();
const code = computed(() => store.state.task.label); // code
const projectId = computed(() => store.getters['projects/projectId']); // ID
const lists = ref([]);
init();
async function init() {
if (code.value === 'ZKT_LW' || code.value === 'ZKT_ZL' || code.value === 'ZKT_RZ') {
await getAchievementsList();
} else if (code.value === 'ZKT_HYGL') {
await getMeetList(1);
} else if (code.value === 'KT_KYHY') {
await getMeetList(0);
}
}
//
async function getAchievementsList() {
const params = {
param: {
code: code.value,
projectId: projectId.value,
},
};
const data = await store.dispatch('task/getIntellectualList', params);
lists.value = [...data];
}
//
async function getMeetList(type) {
const params = {
param: {
type,
projectId: projectId.value,
},
};
try {
const data = await getMeetQuery(params);
data.forEach(item => {
item.time = item.startTime;
});
lists.value = [...data];
} catch (error) {
message.info(error);
throw new Error(error);
}
}
function toDetail(item) {
if (code.value === 'ZKT_HYGL') {
store.commit('task/setSubMeetId', item.id);
} else if (code.value === 'KT_KYHY') {
store.commit('task/setMeetId', item.id);
} else if (code.value === 'ZKT_LW' || code.value === 'ZKT_ZL' || code.value === 'ZKT_RZ') {
store.commit('task/setIntellectualId', item.intellectualId);
}
}
</script>
<style scoped>
.list-box {
padding: 10px 16px 50px;
overflow-y: auto;
}
.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>

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

@ -27,7 +27,8 @@ const isShowList = computed(() => store.state.layout.isShowListStatus); // 是
if (sessionTaskDetail) {
const obj = JSON.parse(sessionTaskDetail);
store.commit('task/setTaskDetail', obj);
console.log(obj);
// store.commit('task/setTaskDetail', obj);
}
if (sessionLabel) {

12
src/components/tall/top/TopNavbar.vue

@ -11,7 +11,8 @@
<CaretDownOutlined />
<div class="user-action absolute">
<div @click="signOut">退出登录</div>
<div v-if="!userId" @click="signUp">登录</div>
<div v-if="userId" @click="signOut">退出登录</div>
</div>
</div>
</div>
@ -28,18 +29,23 @@ const store = useStore();
const router = useRouter();
const collapsed = computed(() => store.state.layout.display.left); //
const account = computed(() => store.getters['user/account']);
const userId = computed(() => store.getters['user/userId']);
// toggle left window display
function toggleCollapse() {
store.commit('layout/setDisplay', { prop: 'left', show: !collapsed.value });
}
function signUp() {
router.push({ path: '/user/signin' });
}
// 退
function signOut() {
store.commit('user/setUser', null);
store.commit('projects/setProject', null);
store.commit('task/setTaskDetail', null);
router.push({ path: '/experiment/user/signin' });
// store.commit('task/setTaskDetail', null);
// router.push({ path: '/experiment/user/signin' });
}
</script>

12
src/plugins/p-task-title.vue

@ -0,0 +1,12 @@
<template>
<!-- 任务名插件 -->
<div class="u-font-14">
{{ task.name }}
</div>
</template>
<script setup>
import { defineProps } from 'vue';
defineProps({ task: { type: Object, default: () => {} } });
</script>

12
src/routers/index.js

@ -1,14 +1,14 @@
import { createRouter, createWebHistory } from 'vue-router';
// 还有 createWebHashHistory 和 createMemoryHistory
export const user = [{ path: '/experiment/user/signIn', name: 'signIn', component: () => import('views/user/SignIn.vue') }];
export const user = [{ path: '/user/signIn', name: 'signIn', component: () => import('views/user/SignIn.vue') }];
export const routes = [
{
path: '/experiment/home',
path: '/home',
name: 'home',
redirect: '/experiment/home/test',
redirect: '/home/test',
component: () => import('views/home/Index.vue'),
children: [{ path: '/experiment/home/test', name: 'test', component: () => import('views/detail/Test.vue') }],
children: [{ path: '/home/test', name: 'test', component: () => import('views/detail/Test.vue') }],
},
];
@ -16,8 +16,8 @@ const router = createRouter({
history: createWebHistory(),
routes: [
{
path: '/experiment',
redirect: '/experiment/home',
path: '/',
redirect: '/home',
},
...user,
...routes,

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

@ -48,6 +48,11 @@ const mutations = {
sessionStorage.setItem('expreStartTime', data.startTime);
sessionStorage.setItem('expreEndTime', data.endTime);
},
// 设置唯一标识
setDeviceId(state, data) {
state.deviceId = data;
},
};
export default mutations;

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

@ -13,6 +13,7 @@ const state = {
subEndTime: '', // 子课题结束时间
expreStartTime: '', // 实验开始时间
expreEndTime: '', // 实验结束时间
deviceId: '', // 唯一标识
};
export default state;

11
src/store/tall/role/getters.js

@ -1,12 +1,11 @@
const getters = {
// 是不是负责人
isMine({ roleId, invisibleRoles, visibleRoles }) {
console.log(roleId, invisibleRoles, visibleRoles);
// if (!visibleRoles || !visibleRoles.length) return false;
// const visible = visibleRoles.find(visible => visible.id === roleId);
// if (visible) return visible.mine;
// const invisible = invisibleRoles.find(invisible => invisible.id === roleId);
// if (invisible) return visible.mine;
if (!visibleRoles || !visibleRoles.length) return false;
const visibleData = visibleRoles.find(visible => visible.id === roleId);
if (visibleData) return visibleData.mine;
const invisibleData = invisibleRoles.find(invisible => invisible.id === roleId);
if (invisibleData) return invisibleData.mine;
return false;
},
};

11
src/store/tall/role/mutations.js

@ -15,7 +15,16 @@ const mutations = {
*/
setVisibleRoles(state, data) {
state.visibleRoles = data || [];
sessionStorage.setItem('roles', JSON.stringify(data));
// sessionStorage.setItem('roles', JSON.stringify(data));
},
/**
* 设置当前角色信息
* @param {Object} state
* @param {string} roleId 当前正在展示的角色的id
*/
setRoleInfo(state, data) {
state.roleInfo = data;
},
/**

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

@ -2,6 +2,7 @@ const state = {
invisibleRoles: [], // 不展示的角色信息
visibleRoles: [], // 展示的角色信息
roleId: '', // 当前展示查看的角色id
roleInfo: null, // 当前展示查看的角色
members: [], // 项目下所有成员
};

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

@ -1,5 +1,4 @@
import { getByCode, submitAnswer, getIntellectual, getIntellectualList, submitIntellectual } from 'apis';
import { message } from 'ant-design-vue';
// import { message } from 'ant-design-vue';
export default {
namespaced: true,
@ -26,19 +25,10 @@ export default {
showSkeleton: false, // 定期任务骨架屏
newProjectInfo: {},
showScrollTo: false, // 是否可以设置时间轴自动滚动的位置
taskDetail: {}, // 当前点击任务信息
label: '', // 任务code
members: [], // 成员列表
globalHeight: 0, // 日常任务面板高度
detailId: '', // 子课题Id
subProjectInfo: null, // 当前子课题信息
experimentationId: '', // 实验ID
experimentationInfo: null, // 当前实验信息
regularTasks: [], // 定期任务
intellectualId: '', // 当前选择的知识产权ID
meetId: '', // 当前选择的会议ID
subMeetId: '', // 当前选择的子课题会议ID
question: null,
currLocationTaskId: '', // 前需要定位到的任务id
businessCode: '', // 当前打开的项目的所属服务
},
getters: {
@ -231,17 +221,7 @@ export default {
*/
setPermanents(state, tasks) {
state.permanents = tasks || [];
sessionStorage.setItem('permanents', JSON.stringify(tasks));
},
/**
* 定期任务
* @param {object} state
* @param {array} tasks 服务端查询到的定期任务书籍
*/
setRegularTasks(state, tasks) {
state.regularTasks = tasks || [];
sessionStorage.setItem('regularTasks', JSON.stringify(tasks));
// sessionStorage.setItem('permanents', JSON.stringify(tasks));
},
/**
@ -311,187 +291,33 @@ export default {
},
/**
* 设置当前点击的任务信息
*/
setTaskDetail(state, data) {
state.taskDetail = data;
if (data) {
sessionStorage.setItem('taskId', data.id);
sessionStorage.setItem('taskDetail', JSON.stringify(data));
} else {
sessionStorage.removeItem('taskId');
sessionStorage.removeItem('taskDetail');
}
},
/**
* 设置当前任务的code
*/
setLabel(state, data) {
state.label = data;
if (data) {
sessionStorage.setItem('label', data);
} else {
sessionStorage.removeItem('label');
}
},
/**
* 设置成员列表
*/
setMembers(state, data) {
state.members = data || [];
},
/**
* 设置日常任务面板高度
*/
setGlobalHeight(state, data) {
state.globalHeight = data;
sessionStorage.setItem('globalHeight', data);
},
/**
* 子课题Id
*/
sonDetailId(state, data) {
state.detailId = data;
},
/**
* 设置当前子课题信息
*/
setSubProjectInfo(state, data) {
state.subProjectInfo = data;
},
/**
* 实验Id
*/
sonExperimentationId(state, data) {
state.experimentationId = data;
},
/**
* 设置当前实验信息
*/
setExperimentationInfo(state, data) {
state.experimentationInfo = data;
},
/**
* 知识产权ID
* 定期任务
* @param {object} state
* @param {array} tasks 服务端查询到的定期任务书籍
*/
setIntellectualId(state, data) {
state.intellectualId = data;
setRegularTasks(state, tasks) {
state.regularTasks = tasks || [];
// sessionStorage.setItem('regularTasks', JSON.stringify(tasks));
},
/**
* 会议ID
* 当前需要定位到的任务id
* @param {Object} state
* @param {Object} data
*/
setMeetId(state, data) {
state.meetId = data;
setCurrLocationTaskId(state, data) {
state.currLocationTaskId = data;
},
/**
* 子课题会议ID
* 当前打开的项目的所属服务
* @param {Object} state
* @param {Object} data
*/
setSubMeetId(state, data) {
state.subMeetId = data;
},
setQuestion(state, data) {
state.question = data;
setBusinessCode(state, data) {
state.businessCode = data;
},
},
actions: {
/**
* 根据code查询所有试题
* @param {string} param.code
* @param {integer} param.projectId
* @param {integer} param.intellectualId
*/
async getByCode({ commit }, param) {
try {
const data = await getByCode(param);
commit('setQuestion', data || null);
return data;
} catch (error) {
message.info(error);
throw new Error(error);
}
},
/**
* 答案
* @param {string} param.code
* @param {integer} param.projectId
* @param {array} param.questionAndAnswerList
*/
async submitAnswer({ commit }, param) {
try {
const data = await submitAnswer(param);
commit('setQuestion', data || null);
message.info('保存成功');
return data;
} catch (error) {
message.info(error);
throw new Error(error);
}
},
/**
* 查询知识产权题目论文专利软著
* @param {string} param.code
* @param {integer} param.projectId
* @param {integer} param.intellectualId
*/
async getIntellectual({ commit }, param) {
try {
const data = await getIntellectual(param);
commit('setQuestion', data || null);
return data;
} catch (error) {
message.info(error);
throw new Error(error);
}
},
/**
* 查询知识产权列表 论文专利软著
* @param {string} param.code
* @param {integer} param.projectId
* @param {integer} param.intellectualId
*/
async getIntellectualList({ commit }, param) {
try {
const data = await getIntellectualList(param);
commit('setQuestion', data || null);
return data;
} catch (error) {
message.info(error);
throw new Error(error);
}
},
/**
* 提交知识产权信息论文专利软著
* @param {string} param.code
* @param {integer} param.projectId
* @param {integer} param.intellectualId
* @param {array} param.questionAndAnswerList
*/
async submitIntellectual({ commit }, param) {
try {
const data = await submitIntellectual(param);
commit('setQuestion', data || null);
message.info('保存成功');
return data;
} catch (error) {
message.info(error);
throw new Error(error);
}
},
},
actions: {},
};

26
src/store/tall/user/index.js

@ -1,10 +1,11 @@
import { getToken } from 'apis';
import { getNewToken, getToken } from 'apis';
import { message } from 'ant-design-vue';
export default {
namespaced: true,
state: { user: null },
state: { user: null, count: 3 },
getters: {
token({ user }) {
@ -37,6 +38,13 @@ export default {
sessionStorage.removeItem('user');
}
},
/**
* 设置token过期重新请求次数
*/
setCount(state, data) {
state.count = data;
},
},
actions: {
@ -55,5 +63,19 @@ export default {
throw new Error(error);
}
},
/**
* 根据refreshToken重新获取token
* @param {string} refreshToken
*/
async getTokenByRefreshToken(refreshToken) {
try {
const data = await getNewToken(refreshToken);
return data;
} catch (error) {
message.error(error);
throw new Error(error);
}
},
},
};

46
src/utils/axios.js

@ -1,6 +1,7 @@
import Axios from 'axios';
import { message } from 'ant-design-vue';
import store from 'store';
import router from './routers/index';
const baseUrl = '/gateway';
@ -12,10 +13,12 @@ const instance = Axios.create({
// request
instance.interceptors.request.use(
config => {
const token = store.getters['user/token'] || localStorage.getItem('token');
const token = store.getters['user/token'] || sessionStorage.getItem('token');
if (token) {
config.headers.Authorization = `Bearer ${token}`;
}
config.headers.appType = 0;
config.headers.deviceId = store.state.layout.deviceId;
return config;
},
error => {
@ -25,17 +28,52 @@ instance.interceptors.request.use(
// response
instance.interceptors.response.use(
response => {
async response => {
if (response.status !== 200 || !response.data) {
return Promise.reject(response.statusText);
}
const { code, data, msg } = response.data;
const { code, msg, tokenObj } = response.data;
const resData = response.data.data;
if (code === 200) {
return data;
if (tokenObj.token) {
sessionStorage.setItem('token', tokenObj.token);
sessionStorage.setItem('refreshToken', tokenObj.refreshToken);
}
return resData;
}
if (code === 49) {
// token过期
store.commit('user/setCount', store.state.user.count - 1);
// 2、刷新token
const refreshToken = sessionStorage.getItem('refreshToken');
const data = await store.dispatch('user/getTokenByRefreshToken', refreshToken);
// 3、重新请求当前api
const { config } = response;
config.headers.Authorization = `Bearer ${data.token}`;
const res = await Axios.request(config);
if (res.data.code === 200) {
store.commit('user/setCount', 3);
}
return res;
}
if (code === 401) {
// refreshToken过期
sessionStorage.removeItem('token');
sessionStorage.removeItem('refreshToken');
store.commit('user/setUser', null);
message.error(msg);
setTimeout(() => {
router.push({ path: '/user/signin' });
}, 1500);
return Promise.reject(msg);
}
return Promise.reject(msg);
},
error => {
console.log('error', error);
if (error.response && error.response.data) {
const msg = error.response.data.message;
message.error(msg);

186
src/views/detail/Test.vue

@ -4,162 +4,52 @@
<!-- {{ taskInfo.name }} -->
<div class="task-con-box">
<!-- <div>{{ label }}</div> -->
<!-- 课题 -->
<!-- 查看课题进展 -->
<CheckSubjectProgress v-if="label === 'KT_KTJZ'" />
<!-- 科研会议管理 -->
<MeetingManagement v-if="label === 'KT_KYHY'" />
<!-- 合同管理 -->
<ContractManagement v-if="label === 'KT_HTGL'" />
<!-- 数据追溯解锁 -->
<DataUnlock v-if="label === 'KT_SJZSJS'" />
<!-- 成员管理 -->
<MemberManagement v-if="label === 'KT_CYGL' || label === 'ZKT_CYGL'" />
<!-- 计划任务书 -->
<PlanAssignment v-if="label === 'KT_JHRWS'" />
<!-- 分配课题 -->
<AssignmentSubject v-if="label === 'KT_FPKT'" />
<!-- 提交中期检查报告 -->
<InterimInspection v-if="label === 'KT_ZQJC'" />
<!-- 项目结题报告 -->
<Conclusion v-if="label === 'KT_JTBG'" />
<!-- 项目验收报告 -->
<Result v-if="label === 'KT_YSZS'" />
<!-- 子课题 -->
<!-- 子课题进展 -->
<SubSubjectProgress v-if="label === 'ZKT_KTJZ'" />
<!-- 子课题科研会议管理 -->
<SubMeetingManagement v-if="label === 'ZKT_HYGL'" />
<!-- 发表论文 -->
<PublishThesis v-if="label === 'ZKT_LW'" />
<!-- 申请专利 -->
<PublishPatent v-if="label === 'ZKT_ZL'" />
<!-- 发表软件著作权 -->
<PublishWork v-if="label === 'ZKT_RZ'" />
<!-- 科研成果管理 -->
<ScientificPayoffs v-if="label === 'ZKT_KYCG'" />
<!-- 成员管理 -->
<!-- <SubMemberManagement v-if="label === 'ZKT_CYGL'" /> -->
<!-- 分配实验 -->
<AssignmentExperiment v-if="label === 'ZKT_FPSY'" />
<!-- 子课题中期检查报告 -->
<SubInterimInspection v-if="label === 'ZKT_ZQJC'" />
<!-- 子课题结题报告 -->
<SubConclusion v-if="label === 'ZKT_JTBG'" />
<!-- 子课题验收报告 -->
<SubResult v-if="label === 'ZKT_YSZS'" />
<!-- 实验 -->
<!-- 提交实验报告 -->
<LabReport v-if="label === 'SY_BG'" />
<!-- 记录实验过程 -->
<Procedure v-if="label === 'SY_GC'" />
<!-- 记录实验数据 -->
<ExperimentalData v-if="label === 'SY_SJ'" />
<!-- 实验程序代码 -->
<ExperimentalCode v-if="label === 'SY_DM'" />
<!-- 撰写实验结果 -->
<ExperimentalResult v-if="label === 'SY_JG'" />
</div>
<div class="task-con-list" v-if="isShowList">
<!-- <div class="task-con-list" v-if="isShowList">
<TaskConList />
</div>
</div> -->
</div>
</div>
</template>
<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 ContractManagement from 'components/tall/task/ContractManagement.vue'; //
import DataUnlock from 'components/tall/task/DataUnlock.vue'; //
import MemberManagement from 'components/tall/task/MemberManagement.vue'; //
import PlanAssignment from 'components/tall/task/PlanAssignment.vue'; //
import AssignmentSubject from 'components/tall/task/AssignmentSubject.vue'; //
import InterimInspection from 'components/tall/task/InterimInspection.vue'; //
import Conclusion from 'components/tall/task/Conclusion.vue'; //
import Result from 'components/tall/task/Result.vue'; //
import SubSubjectProgress from 'components/tall/task/SubSubjectProgress.vue'; //
import SubMeetingManagement from 'components/tall/task/SubMeetingManagement.vue'; //
import PublishThesis from 'components/tall/task/PublishThesis.vue'; //
import PublishPatent from 'components/tall/task/PublishPatent.vue'; //
import PublishWork from 'components/tall/task/PublishWork.vue'; //
import ScientificPayoffs from 'components/tall/task/ScientificPayoffs.vue'; //
// import SubMemberManagement from 'components/tall/task/SubMemberManagement.vue'; //
import AssignmentExperiment from 'components/tall/task/AssignmentExperiment.vue'; //
import SubInterimInspection from 'components/tall/task/SubInterimInspection.vue'; //
import SubConclusion from 'components/tall/task/SubConclusion.vue'; //
import SubResult from 'components/tall/task/SubResult.vue'; //
import LabReport from 'components/tall/task/LabReport.vue'; //
import Procedure from 'components/tall/task/Procedure.vue'; //
import ExperimentalData from 'components/tall/task/ExperimentalData.vue'; //
import ExperimentalCode from 'components/tall/task/ExperimentalCode.vue'; //
import ExperimentalResult from 'components/tall/task/ExperimentalResult.vue'; //
import TaskConList from 'components/tall/task/TaskConList.vue'; //
const store = useStore();
const taskDetail = computed(() => store.state.task.taskDetail); //
const label = ref(null);
const sessionTaskDetail = sessionStorage.getItem('taskDetail');
const isShowList = computed(() => store.state.layout.isShowListStatus); //
if (sessionTaskDetail) {
const taskInfo = JSON.parse(sessionTaskDetail);
if (taskInfo.plugins && taskInfo.plugins.length > 0) {
taskInfo.plugins.forEach(item => {
if (Number(item[0].pluginId) === 1) {
label.value = item[0].param;
store.commit('task/setLabel', label.value);
}
});
}
}
//
watch([taskDetail], () => {
if (!taskDetail.value) return;
const taskInfo = taskDetail.value;
if (taskInfo.plugins && taskInfo.plugins.length > 0) {
taskInfo.plugins.forEach(item => {
if (Number(item[0].pluginId) === 1) {
label.value = item[0].param;
store.commit('task/setLabel', label.value);
}
});
}
});
// import { computed, watch, ref } from 'vue';
// import { useStore } from 'vuex';
// const store = useStore();
// const taskDetail = computed(() => store.state.task.taskDetail); //
// const label = ref(null);
// const sessionTaskDetail = sessionStorage.getItem('taskDetail');
// const isShowList = computed(() => store.state.layout.isShowListStatus); //
// if (sessionTaskDetail) {
// const taskInfo = JSON.parse(sessionTaskDetail);
// if (taskInfo.plugins && taskInfo.plugins.length > 0) {
// taskInfo.plugins.forEach(item => {
// if (Number(item[0].pluginId) === 1) {
// label.value = item[0].param;
// store.commit('task/setLabel', label.value);
// }
// });
// }
// }
// //
// watch([taskDetail], () => {
// if (!taskDetail.value) return;
// const taskInfo = taskDetail.value;
// if (taskInfo.plugins && taskInfo.plugins.length > 0) {
// taskInfo.plugins.forEach(item => {
// if (Number(item[0].pluginId) === 1) {
// label.value = item[0].param;
// store.commit('task/setLabel', label.value);
// }
// });
// }
// });
</script>
<style scoped>

67
src/views/home/Index.vue

@ -1,32 +1,25 @@
<template>
<!-- <div class="task-detail">
<div class="task-con">任务详情页</div>
<div>
<router-view></router-view>
</div>
</div> -->
<a-config-provider :locale="locale">
<a-layout>
<a-layout-header style="background: #fff"> <TopNavbar /> </a-layout-header>
<a-layout>
<!-- 日历页-->
<!-- 日历页 -->
<a-layout-sider v-show="collapsed" style="background: #fff"><Left /></a-layout-sider>
<a-layout>
<Intro v-if="!projectId" />
<Intro v-if="!projectInfo.id" />
<a-layout-sider v-if="projectId" class="project-detail"><Center /></a-layout-sider>
<a-layout-sider v-if="projectInfo.id" class="project-detail"><Center /></a-layout-sider>
<a-layout v-if="taskId">
<!-- 导航栏-->
<a-layout v-if="taskDetail && taskDetail.id">
<!-- 导航栏 -->
<a-layout-header style="background: #fff">
<Navbar />
</a-layout-header>
<!-- 内容区-->
<!-- 内容区 -->
<a-layout-content><router-view></router-view></a-layout-content>
<!-- 脚部-->
<!-- <a-layout-footer>Footer</a-layout-footer>-->
<!-- 脚部 -->
<!-- <a-layout-footer>Footer</a-layout-footer> -->
</a-layout>
</a-layout>
</a-layout>
@ -35,7 +28,7 @@
</template>
<script setup>
import { computed, watch, ref } from 'vue';
import { computed } from 'vue';
import { useStore } from 'vuex';
// import { useRoute, useRouter } from 'vue-router';
import zhCN from 'ant-design-vue/es/locale/zh_CN';
@ -49,33 +42,29 @@ const locale = zhCN;
const store = useStore();
const collapsed = computed(() => store.state.layout.display.left); //
const projectId = ref(null);
const project = computed(() => store.state.projects.project); //
const sessionProjectId = sessionStorage.getItem('projectId');
projectId.value = sessionProjectId;
// const projectId = ref(null);
const projectInfo = computed(() => store.state.projects.project); //
const sessionProject = sessionStorage.getItem('project'); //
// const sessionProjectId = sessionStorage.getItem('projectId');
// projectId.value = sessionProjectId;
const taskId = ref(null);
const sessionTaskId = sessionStorage.getItem('taskId');
taskId.value = sessionTaskId;
// const taskId = ref(null);
const sessionTask = sessionStorage.getItem('taskDetail');
// taskId.value = sessionTaskId;
const taskDetail = computed(() => store.state.task.taskDetail); //
//
watch(project, () => {
if (project.value) {
projectId.value = project.value.id;
} else {
projectId.value = null;
}
});
if (sessionProject && !projectInfo.value.id) {
const info = JSON.parse(sessionProject);
console.log(`info`, info);
store.commit('projects/setProject', info);
store.commit('task/setBusinessCode', info.businessCode);
}
//
watch(taskDetail, () => {
if (taskDetail.value) {
taskId.value = taskDetail.value.id;
} else {
taskId.value = null;
}
});
if (sessionTask && !taskDetail.value.id) {
const info = JSON.parse(sessionTask);
console.log(info);
// store.commit('task/setTaskDetail', info);
}
// queryu token
// const route = useRoute();

180
src/views/user/SignIn.vue

@ -2,19 +2,42 @@
<div class="login-box relative flex justify-between" style="width: 100%; height: 100%">
<div class="login-wrap absolute right-0">
<div class="login-form relative">
<h1>科研实验管理云平台系统</h1>
<h1>PT</h1>
<!-- 用户名密码输入 -->
<a-form ref="signInFormRef" :model="signInForm" :rules="rules">
<a-form-item name="username">
<a-form-item name="phone" style="height: 114px; margin-bottom: 0">
<label style="font-size: 16px"><span style="color: #ff5353; margin-right: 5px">*</span>手机号</label>
<a-input v-model:value="signInForm.phone" placeholder="请输入手机号" />
</a-form-item>
<a-form-item name="verificationCodeValue" style="height: 114px; margin-bottom: 0">
<label style="font-size: 16px"><span style="color: #ff5353; margin-right: 5px">*</span>图形验证码</label>
<div class="relative">
<a-input v-model:value="signInForm.verificationCodeValue" placeholder="请输入计算结果" />
<div class="absolute top-px right-px inline-block">
<a-image :width="120" :preview="false" :src="signInForm.imageBase64" @click="getImage" />
</div>
</div>
</a-form-item>
<a-form-item name="smsCode" style="height: 114px; margin-bottom: 0">
<label style="font-size: 16px"><span style="color: #ff5353; margin-right: 5px">*</span>验证码</label>
<div class="flex justify-between items-center">
<a-input v-model:value="signInForm.smsCode" placeholder="请输入验证码" />
<a-button class="mt-0 ml-2" @click="getCode">{{ signInForm.showInterval ? signInForm.interval : '获取动态码' }}</a-button>
</div>
</a-form-item>
<!-- <a-form-item name="username">
<label style="font-size: 16px"><span style="color: #ff5353; margin-right: 5px">*</span>用户名</label>
<a-input v-model:value="signInForm.username" placeholder="请输入用户名" />
</a-form-item>
<a-form-item name="password">
<label style="font-size: 16px"><span style="color: #ff5353; margin-right: 5px">*</span>密码</label>
<a-input v-model:value="signInForm.password" type="password" placeholder="请输入密码" />
</a-form-item>
</a-form-item> -->
<a-form-item>
<a-button type="primary" html-type="submit" @click="handleSingIn">登录</a-button>
<a-button class="mt-4 w-full" style="font-size: 16px" type="primary" html-type="submit" @click="handleSingIn">登录</a-button>
</a-form-item>
</a-form>
</div>
@ -24,7 +47,7 @@
<script setup>
import { reactive, ref } from 'vue';
import { signIn } from 'apis';
import { signIn, getImageCode, getSmsCode } from 'apis';
import { useStore } from 'vuex';
import { useRouter } from 'vue-router';
import { message } from 'ant-design-vue';
@ -33,41 +56,155 @@ const store = useStore();
const router = useRouter();
const signInFormRef = ref(null);
const signInForm = reactive({
username: '',
password: '',
phone: '',
verificationCodeValue: '', //
smsCode: '', //
imageBase64: '', //
verificationCodeId: '', // id
showInterval: false, //
interval: 60, // 60s
// username: '',
// password: '',
});
const codeTimer = ref(null);
const validatePhone = async (_rule, value) => {
const reg = /^1(3|4|5|6|7|8|9)\d{9}$/;
if (value && !reg.test(value)) {
return Promise.reject(new Error('请输入正确的手机号'));
}
return Promise.resolve();
};
const validateNumber = async (_rule, value) => {
if (value && !Number(value)) {
return Promise.reject(new Error('验证码只能为数字'));
}
return Promise.resolve();
};
const rules = reactive({
username: [
phone: [
{
required: true,
message: '请输入账号',
message: '请输入手机号',
trigger: 'blur',
},
{
validator: validatePhone,
trigger: 'change',
},
],
verificationCodeValue: [
{
required: true,
message: '请输入图形验证码计算结果',
trigger: 'blur',
},
{
validator: validateNumber,
trigger: ['change', 'blur'],
},
],
password: [
smsCode: [
{
required: true,
message: '请输入密码',
message: '请输入验证码',
trigger: 'blur',
},
{
validator: validateNumber,
trigger: ['change', 'blur'],
},
],
// username: [
// {
// required: true,
// message: '',
// trigger: 'blur',
// },
// ],
// password: [
// {
// required: true,
// message: '',
// trigger: 'blur',
// },
// ],
});
//
getImage();
//
async function getImage() {
try {
const data = await getImageCode();
signInForm.imageBase64 = data.imageBase64 || '';
signInForm.verificationCodeId = data.verificationCodeId || '';
} catch (error) {
message.info(error);
}
}
async function getCode() {
if (signInForm.showInterval) return;
if (!signInForm.phone) {
message.info('请输入手机号');
return;
}
if (!signInForm.verificationCodeId || !signInForm.verificationCodeValue) {
message.info('请输入图形验证码');
return;
}
try {
const params = {
phone: signInForm.phone,
verificationCodeId: signInForm.verificationCodeId,
verificationCodeValue: signInForm.verificationCodeValue,
};
const data = await getSmsCode(params);
if (data) {
getCodeInterval();
}
} catch (error) {
message.info(error);
}
}
//
function getCodeInterval() {
signInForm.showInterval = true;
codeTimer.value = setInterval(() => {
if (signInForm.interval === 0) {
clearInterval(codeTimer.value);
codeTimer.value = null;
signInForm.showInterval = false;
signInForm.interval = 60;
return;
}
signInForm.interval -= 1;
}, 1000);
}
//
function handleSingIn() {
signInFormRef.value
.validate()
.then(async () => {
if (signInForm.username && signInForm.password) {
if (signInForm.phone && signInForm.smsCode) {
try {
const params = {
client: 1,
client: 0,
data: {
identifier: signInForm.username,
credential: signInForm.password,
identifier: signInForm.phone,
credential: signInForm.smsCode,
},
type: 3,
type: 1,
};
const resData = await signIn(params);
store.commit('user/setUser', resData);
@ -75,7 +212,7 @@ function handleSingIn() {
console.log('登录成功, 欢迎回来');
setTimeout(() => {
//
router.push({ path: '/experiment/home' });
router.push({ path: '/home' });
}, 1000);
} catch (error) {
message.info(error);
@ -84,7 +221,6 @@ function handleSingIn() {
}
})
.catch(error => {
message.info(error);
throw new Error(error);
});
}
@ -120,17 +256,17 @@ h1 {
font-weight: 600;
}
.ant-form label {
margin-bottom: 15px;
}
.ant-input {
height: 45px;
margin-top: 15px;
border-radius: 6px;
}
.ant-btn {
margin-top: 32px;
width: 100%;
height: 45px;
border-radius: 6px;
font-size: 18px;
}
</style>

8
vite.config.js

@ -1,17 +1,17 @@
import Components from 'unplugin-vue-components/vite';
import { AntDesignVueResolver } from 'unplugin-vue-components/resolvers';
import Components from 'unplugin-vue-components/vite';
import { VitePWA } from 'vite-plugin-pwa';
import WindiCSS from 'vite-plugin-windicss';
import { defineConfig } from 'vite';
import path from 'path';
import vue from '@vitejs/plugin-vue';
import viteCompression from 'vite-plugin-compression';
import { VitePWA } from 'vite-plugin-pwa';
import vue from '@vitejs/plugin-vue';
const resolve = dir => path.join(__dirname, dir);
// https://vitejs.dev/config/
export default defineConfig({
base: '/experiment',
base: '/',
plugins: [vue(), VitePWA(), WindiCSS(), Components({ resolvers: [AntDesignVueResolver()] }), viteCompression()],
resolve: {
alias: {

5
yarn.lock

@ -4800,6 +4800,11 @@
"resolved" "https://registry.npm.taobao.org/util-deprecate/download/util-deprecate-1.0.2.tgz?cache=0&other_urls=https%3A%2F%2Fregistry.npm.taobao.org%2Futil-deprecate%2Fdownload%2Futil-deprecate-1.0.2.tgz"
"version" "1.0.2"
"uuid@^8.3.2":
"integrity" "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg=="
"resolved" "https://registry.npmmirror.com/uuid/-/uuid-8.3.2.tgz"
"version" "8.3.2"
"v8-compile-cache@^2.0.3":
"integrity" "sha1-LeGWGMZtwkfc+2+ZM4A12CRaLO4="
"resolved" "https://registry.nlark.com/v8-compile-cache/download/v8-compile-cache-2.3.0.tgz"

Loading…
Cancel
Save