forked from TALL/tall3-pc-keti
60 changed files with 740 additions and 7928 deletions
@ -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 |
@ -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 |
|||
|
@ -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 |
|||
|
@ -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 |
|||
|
@ -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> |
@ -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> |
@ -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> |
@ -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> |
@ -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> |
@ -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> |
@ -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> |
@ -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> |
@ -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> |
@ -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> |
@ -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> |
@ -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> |
@ -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">格式:jpg、jpeg、rar、zip</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> |
@ -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> |
@ -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> |
@ -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> |
@ -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> |
@ -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> |
@ -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> |
@ -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> |
@ -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> |
@ -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> |
@ -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> |
@ -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">格式:jpg、jpeg、rar、zip</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> |
@ -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> |
@ -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> |
@ -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> |
@ -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> |
Loading…
Reference in new issue