熊飞 2023-12-25 13:56:19 +08:00
parent cc16889f44
commit 3e3e54ed8f
7945 changed files with 4743363 additions and 0 deletions

5
.banelrc Normal file
View File

@ -0,0 +1,5 @@
{
"compact": false,
"presets": ["env", "stage-0"],
"plugins": ["transform-runtime"]
}

14
.editorconfig Normal file
View File

@ -0,0 +1,14 @@
# http://editorconfig.org
root = true
[*]
charset = utf-8
indent_style = space
indent_size = 2
end_of_line = lf
insert_final_newline = true
trim_trailing_whitespace = true
[*.md]
insert_final_newline = false
trim_trailing_whitespace = false

10
.env Normal file
View File

@ -0,0 +1,10 @@
VUE_FILE_PATH = 'https://zyypacs-code.oss-cn-shanghai.aliyuncs.com'
OSS_REGION = 'oss-cn-shanghai'
OSS_BUCKET = 'zyypacs-code'
OSS_ACCESS_KEY_ID = 'mpXG7Nu6zTpsDrI1'
OSS_ACCESS_KEY_SECRET = 'yNINcEb099SkNfF6vYKaoP8TZNI3xZ'

48
.env.development Normal file
View File

@ -0,0 +1,48 @@
ENV = 'development'
NODE_ENV = 'development'
# base public path
VUE_APP_BASE_PATH = '/'
VUE_APP_IS_TEST = false
# base api
VUE_APP_BASE_API = 'http://123.56.94.154:7000'
VUE_CLI_BABEL_TRANSPILE_MODULES = false
# 是否开启登陆限制 true:是 false:否
VUE_APP_LOGIN_FOR_PERMISSION = false
# 是否开启长时间无操作锁定弹框 true:是 false:否
VUE_APP_LOCK_FOR_PERMISSION = false
# 无操作锁定弹框判断时间 单位:秒
VUE_APP_LOCK_FOR_TIME = 900
# 是否开启长时间无操作登出 true:是 false:否
VUE_APP_LOGOUT_FOR_PERMISSION = false
# 无操作锁定弹框判断时间 单位:秒
VUE_APP_LOGOUT_FOR_TIME = 1800
# 是否开启密码正则验证 true:是 false:否
VUE_APP_PASSWORD_FOR_PERMISSION = true
# 密码校验规则
VUE_APP_PASSWORD_FOR_REGULAR = ^(?=.*[a-z])(?=.*[A-Z])(?=.*[0-9])(?=.*[-_.@^+\$!%#*?&\$~])[A-Za-z0-9-~_.@^+\$~!%#*?&]{8,32}$
# 是否开启文档签署验证 true:是 false:否
VUE_APP_WORD_FOR_PERMISSION = false
# dicom文件地址
VUE_APP_DICOM_PATH = 'https://zyypacs.oss-cn-shanghai.aliyuncs.com'
VUE_APP_DICOM_USA_PATH = 'https://zyypacs-usa.oss-us-west-1.aliyuncs.com'
VUE_APP_OSS_CONFIG_REGION = 'oss-cn-shanghai'
VUE_APP_OSS_CONFIG_BUCKET = 'zyypacs'
VUE_APP_OSS_PATH = '/dev/dist'

41
.env.prod Normal file
View File

@ -0,0 +1,41 @@
# just a flag
ENV = 'prod'
NODE_ENV = 'prod'
#process.env.NODE_ENV
# base public path
VUE_APP_BASE_PATH = '/'
# 是否开启登陆限制 true:是 false:否
VUE_APP_LOGIN_FOR_PERMISSION = true
# 是否开启长时间无操作锁定弹框 true:是 false:否
VUE_APP_LOCK_FOR_PERMISSION = true
# 无操作锁定弹框判断时间 单位:秒
VUE_APP_LOCK_FOR_TIME = 900
# 是否开启长时间无操作登出 true:是 false:否
VUE_APP_LOGOUT_FOR_PERMISSION = true
# 无操作锁定弹框判断时间 单位:秒
VUE_APP_LOGOUT_FOR_TIME = 1800
# 是否开启密码正则验证 true:是 false:否
VUE_APP_PASSWORD_FOR_PERMISSION = true
# 是否开启密码正则验证 true:是 false:否
VUE_APP_PASSWORD_FOR_REGULAR = ^(?=.*[a-z])(?=.*[A-Z])(?=.*[0-9])(?=.*[-_.@^+\$!%#*?&\$~])[A-Za-z0-9-~_.@^+\$~!%#*?&]{8,32}$
# 是否开启文档签署验证 true:是 false:否
VUE_APP_WORD_FOR_PERMISSION = true
# dicom文件地址
# VUE_APP_DICOM_PATH = 'http://101.132.193.237:7080'
VUE_APP_DICOM_PATH = 'https://zyypacs-prod.oss-cn-shanghai.aliyuncs.com'
VUE_APP_OSS_CONFIG_REGION = 'oss-cn-shanghai'
VUE_APP_OSS_CONFIG_BUCKET = 'zyypacs-prod'
VUE_APP_OSS_PATH = '/prod/dist'

46
.env.production Normal file
View File

@ -0,0 +1,46 @@
# just a flag
ENV = 'production'
NODE_ENV = 'production'
# base public path
VUE_APP_BASE_PATH = '/'
VUE_APP_IS_TEST = true
# 是否开启登陆限制 true:是 false:否
VUE_APP_LOGIN_FOR_PERMISSION = false
# 是否开启长时间无操作锁定弹框 true:是 false:否
VUE_APP_LOCK_FOR_PERMISSION = false
# 无操作锁定弹框判断时间 单位:秒
VUE_APP_LOCK_FOR_TIME = 900
# 是否开启长时间无操作登出 true:是 false:否
VUE_APP_LOGOUT_FOR_PERMISSION = false
# 无操作锁定弹框判断时间 单位:秒
VUE_APP_LOGOUT_FOR_TIME = 1800
# 是否开启密码正则验证 true:是 false:否
VUE_APP_PASSWORD_FOR_PERMISSION = true
# 是否开启密码正则验证 true:是 false:否
VUE_APP_PASSWORD_FOR_REGULAR = ^(?=.*[a-z])(?=.*[A-Z])(?=.*[0-9])(?=.*[-_.@^+\$!%#*?&\$~])[A-Za-z0-9-~_.@^+\$~!%#*?&]{8,32}$
# 是否开启文档签署验证 true:是 false:否
VUE_APP_WORD_FOR_PERMISSION = false
# dicom文件地址
VUE_APP_DICOM_PATH = 'https://zyypacs.oss-cn-shanghai.aliyuncs.com'
#VUE_APP_DICOM_PATH = 'http://123.56.94.154:7000'
#VUE_APP_DICOM_PATH = 'https://zyypacs-mdc.oss-cn-shanghai.aliyuncs.com'
VUE_APP_DICOM_USA_PATH = 'https://zyypacs-usa.oss-us-west-1.aliyuncs.com'
VUE_APP_OSS_CONFIG_REGION = 'oss-cn-shanghai'
VUE_APP_OSS_CONFIG_BUCKET = 'zyypacs'
VUE_APP_OSS_PATH = '/test/dist'

34
.env.prop Normal file
View File

@ -0,0 +1,34 @@
# just a flag
ENV = 'prop'
NODE_ENV = 'prop'
# 是否开启登陆限制 true:是 false:否
VUE_APP_LOGIN_FOR_PERMISSION = true
# 是否开启长时间无操作锁定弹框 true:是 false:否
VUE_APP_LOCK_FOR_PERMISSION = true
# 无操作锁定弹框判断时间 单位:秒
VUE_APP_LOCK_FOR_TIME = 900
# 是否开启长时间无操作登出 true:是 false:否
VUE_APP_LOGOUT_FOR_PERMISSION = true
# 无操作锁定弹框判断时间 单位:秒
VUE_APP_LOGOUT_FOR_TIME = 1800
# 是否开启密码正则验证 true:是 false:否
VUE_APP_PASSWORD_FOR_PERMISSION = true
# 是否开启密码正则验证 true:是 false:否
VUE_APP_PASSWORD_FOR_REGULAR = ^(?=.*[a-z])(?=.*[A-Z])(?=.*[0-9])(?=.*[-_.@^+\$!%#*?&\$~])[A-Za-z0-9-~_.@^+\$~!%#*?&]{8,32}$
# 是否开启文档签署验证 true:是 false:否
VUE_APP_WORD_FOR_PERMISSION = true
# dicom文件地址
VUE_APP_DICOM_PATH = 'http://123.56.94.154:7020'
VUE_APP_OSS_CONFIG_REGION = 'oss-cn-shanghai'
VUE_APP_OSS_CONFIG_BUCKET = 'zyypacs'

41
.env.uat Normal file
View File

@ -0,0 +1,41 @@
# just a flag
ENV = 'production'
NODE_ENV = 'production'
# base public path
VUE_APP_BASE_PATH = '/'
# base public path
VUE_APP_BASE_PATH = '/'
# 是否开启登陆限制 true:是 false:否
VUE_APP_LOGIN_FOR_PERMISSION = true
# 是否开启长时间无操作锁定弹框 true:是 false:否
VUE_APP_LOCK_FOR_PERMISSION = true
# 无操作锁定弹框判断时间 单位:秒
VUE_APP_LOCK_FOR_TIME = 900
# 是否开启长时间无操作登出 true:是 false:否
VUE_APP_LOGOUT_FOR_PERMISSION = true
# 无操作锁定弹框判断时间 单位:秒
VUE_APP_LOGOUT_FOR_TIME = 1800
# 是否开启密码正则验证 true:是 false:否
VUE_APP_PASSWORD_FOR_PERMISSION = true
# 是否开启密码正则验证 true:是 false:否
VUE_APP_PASSWORD_FOR_REGULAR = ^(?=.*[a-z])(?=.*[A-Z])(?=.*[0-9])(?=.*[-_.@^+\$!%#*?&\$~])[A-Za-z0-9-~_.@^+\$~!%#*?&]{8,32}$
# 是否开启文档签署验证 true:是 false:否
VUE_APP_WORD_FOR_PERMISSION = true
# dicom文件地址
VUE_APP_DICOM_PATH = 'https://zyypacs-uat.oss-cn-shanghai.aliyuncs.com'
VUE_APP_OSS_CONFIG_REGION = 'oss-cn-shanghai'
VUE_APP_OSS_CONFIG_BUCKET = 'zyypacs-uat'
VUE_APP_OSS_PATH = '/uat/dist'

40
.env.usa Normal file
View File

@ -0,0 +1,40 @@
# just a flag
ENV = 'production'
NODE_ENV = 'production'
# base public path
VUE_APP_BASE_PATH = '/'
# 是否开启登陆限制 true:是 false:否
VUE_APP_LOGIN_FOR_PERMISSION = false
# 是否开启长时间无操作锁定弹框 true:是 false:否
VUE_APP_LOCK_FOR_PERMISSION = false
# 无操作锁定弹框判断时间 单位:秒
VUE_APP_LOCK_FOR_TIME = 900
# 是否开启长时间无操作登出 true:是 false:否
VUE_APP_LOGOUT_FOR_PERMISSION = false
# 无操作锁定弹框判断时间 单位:秒
VUE_APP_LOGOUT_FOR_TIME = 1800
# 是否开启密码正则验证 true:是 false:否
VUE_APP_PASSWORD_FOR_PERMISSION = false
# 是否开启密码正则验证 true:是 false:否
VUE_APP_PASSWORD_FOR_REGULAR = ^(?=.*[a-z])(?=.*[A-Z])(?=.*[0-9])(?=.*[-_.@^+\$!%#*?&\$~])[A-Za-z0-9-~_.@^+\$~!%#*?&]{8,32}$
# 是否开启文档签署验证 true:是 false:否
VUE_APP_WORD_FOR_PERMISSION = true
# dicom文件地址
VUE_APP_DICOM_PATH = 'https://zyypacs-usa.oss-us-west-1.aliyuncs.com'
VUE_APP_DICOM_USA_PATH = 'https://zyypacs-usa.oss-us-west-1.aliyuncs.com'
VUE_APP_OSS_CONFIG_REGION = 'oss-us-west-1'
VUE_APP_OSS_CONFIG_BUCKET = 'zyypacs-usa'
VUE_APP_OSS_PATH = '/usa/dist'

42
.eslintignore Normal file
View File

@ -0,0 +1,42 @@
build/*.js
src/assets
src/utils/*
public
dist
src/*
src/views/dictionary/checkConfig/*
src/views/trials/trials-panel/trial-summary/*
src/main.js
src/views/trials/trials-panel/reading/reading-tracking/*
src/views/trials/trials-panel/reading/pm-medical-feedback/*
src/views/trials/trials-panel/reading/consistency-analysis/*
src/views/trials/trials-panel/reading/referee-allocation/*
src/views/trials/trials-list/components/TrialForm
src/views/trials/trials-panel/reading/read-task-allocation/*
src/views/blindResumeInfo/*
src/views/trials/trials-panel/visit/crc-upload/*
src/views/trials/trials-panel/reading/dicoms/customize/*
src/views/trials/trials-panel/subject/brain-metastasis/*
src/views/trials/trials-panel/setting/reading-unit/components/*
src/cornerstonejs/*
src/components/clinicalDataQuestions/*
src/views/dictionary/template/*
src/views/trials/trials-panel/reading/reading-tracking/components/*
src/views/trials/trials-panel/trial-summary/audit-record/*
src/views/dictionary/template/components/*
src/views/trials/trials-panel/reading/task-allocation/*
src/views/trials/trials-panel/clinical-data/*
src/views/trials/trials-panel/visit/qc-check/components/clinicalData/*
src/views/trials/trials-panel/subject/reading-period/*
src/views/dictionary/template/email/*
src/views/trials/trials-panel/enrolled-reviewers/stats/components/AddWorkload.vue
src/views/trials/trials-workbench/components/clinicalDataPM.vue
src/views/trials/trials-workbench/*
src/components/*
src/views/resumeInfo/*
src/layout/*
src/views/trials/trials-panel/setting/*
src/views/reviewers/*
src/api/reviewers
src/views/trials/trials-panel/setting/trial-config/*

278
.eslintrc.js Normal file
View File

@ -0,0 +1,278 @@
module.exports = {
root: true,
parserOptions: {
parser: 'babel-eslint',
sourceType: 'module'
},
globals: {
"zzSessionStorage": true
},
env: {
browser: true,
node: true,
es6: true
},
extends: ["plugin:vue/recommended"],
// add your custom rules here
//it is base on https://github.com/vuejs/eslint-config-vue
rules: {
"vue/max-attributes-per-line": [
2,
{
singleline: 10,
multiline: {
max: 1,
allowFirstLine: false
}
}
],
"vue/singleline-html-element-content-newline": "off",
"vue/multiline-html-element-content-newline": "off",
"vue/name-property-casing": ["error", "PascalCase"],
"vue/no-v-html": "off",
"accessor-pairs": 2,
"arrow-spacing": [
2,
{
before: true,
after: true
}
],
"block-spacing": [2, "always"],
"brace-style": [
2,
"1tbs",
{
allowSingleLine: true
}
],
camelcase: [
0,
{
properties: "always"
}
],
"comma-dangle": [2, "never"],
"comma-spacing": [
2,
{
before: false,
after: true
}
],
"comma-style": [2, "last"],
"constructor-super": 2,
curly: [2, "multi-line"],
"dot-location": [2, "property"],
"eol-last": 2,
eqeqeq: ["error", "always", { null: "ignore" }],
"generator-star-spacing": [
2,
{
before: true,
after: true
}
],
"handle-callback-err": [2, "^(err|error)$"],
indent: [
2,
2,
{
SwitchCase: 1
}
],
"jsx-quotes": [2, "prefer-single"],
"key-spacing": [
2,
{
beforeColon: false,
afterColon: true
}
],
"keyword-spacing": [
2,
{
before: true,
after: true
}
],
"new-cap": [
2,
{
newIsCap: true,
capIsNew: false
}
],
"new-parens": 2,
"no-array-constructor": 2,
"no-caller": 2,
"no-console": "off",
"no-class-assign": 2,
"no-cond-assign": 2,
"no-const-assign": 2,
"no-control-regex": 0,
"no-delete-var": 2,
"no-dupe-args": 2,
"no-dupe-class-members": 2,
"no-dupe-keys": 2,
"no-duplicate-case": 2,
"no-empty-character-class": 2,
"no-empty-pattern": 2,
"no-eval": 2,
"no-ex-assign": 2,
"no-extend-native": 2,
"no-extra-bind": 2,
"no-extra-boolean-cast": 2,
"no-extra-parens": [2, "functions"],
"no-fallthrough": 2,
"no-floating-decimal": 2,
"no-func-assign": 2,
"no-implied-eval": 2,
"no-inner-declarations": [2, "functions"],
"no-invalid-regexp": 2,
"no-irregular-whitespace": 2,
"no-iterator": 2,
"no-label-var": 2,
"no-labels": [
2,
{
allowLoop: false,
allowSwitch: false
}
],
"no-lone-blocks": 2,
"no-mixed-spaces-and-tabs": 2,
"no-multi-spaces": 2,
"no-multi-str": 2,
"no-multiple-empty-lines": [
2,
{
max: 1
}
],
"no-native-reassign": 2,
"no-negated-in-lhs": 2,
"no-new-object": 2,
"no-new-require": 2,
"no-new-symbol": 2,
"no-new-wrappers": 2,
"no-obj-calls": 2,
"no-octal": 2,
"no-octal-escape": 2,
"no-path-concat": 2,
"no-proto": 2,
"no-redeclare": 2,
"no-regex-spaces": 2,
"no-return-assign": [2, "except-parens"],
"no-self-assign": 2,
"no-self-compare": 2,
"no-sequences": 2,
"no-shadow-restricted-names": 2,
"no-spaced-func": 2,
"no-sparse-arrays": 2,
"no-this-before-super": 2,
"no-throw-literal": 2,
"no-trailing-spaces": 2,
"no-undef": 2,
"no-undef-init": 2,
"no-unexpected-multiline": 2,
"no-unmodified-loop-condition": 2,
"no-unneeded-ternary": [
2,
{
defaultAssignment: false
}
],
"no-unreachable": 2,
"no-unsafe-finally": 2,
"no-unused-vars": [
2,
{
vars: "all",
args: "none"
}
],
"no-useless-call": 2,
"no-useless-computed-key": 2,
"no-useless-constructor": 2,
"no-useless-escape": 0,
"no-whitespace-before-property": 2,
"no-with": 2,
"one-var": [
2,
{
initialized: "never"
}
],
"operator-linebreak": [
2,
"after",
{
overrides: {
"?": "before",
":": "before"
}
}
],
"padded-blocks": [2, "never"],
quotes: [
2,
"single",
{
avoidEscape: true,
allowTemplateLiterals: true
}
],
semi: [2, "never"],
"semi-spacing": [
2,
{
before: false,
after: true
}
],
"space-before-blocks": [2, "always"],
"space-before-function-paren": [2, "never"],
"space-in-parens": [2, "never"],
"space-infix-ops": 2,
"space-unary-ops": [
2,
{
words: true,
nonwords: false
}
],
"spaced-comment": [
2,
"always",
{
markers: [
"global",
"globals",
"eslint",
"eslint-disable",
"*package",
"!",
","
]
}
],
"template-curly-spacing": [2, "never"],
"use-isnan": 2,
"valid-typeof": 2,
"wrap-iife": [2, "any"],
"yield-star-spacing": [2, "both"],
yoda: [2, "never"],
"prefer-const": 2,
//"no-debugger": process.env.NODE_ENV === "production" ? 2 : 0,
"object-curly-spacing": [
2,
"always",
{
objectsInObjects: false
}
],
"array-bracket-spacing": [2, "never"]
}
};

16
.gitignore vendored Normal file
View File

@ -0,0 +1,16 @@
.DS_Store
node_modules/
dist/
npm-debug.log*
yarn-debug.log*
yarn-error.log*
package-lock.json
tests/**/coverage/
# Editor directories and files
.idea
.vscode
*.suo
*.ntvs*
*.njsproj
*.sln

1
.svn/entries Normal file
View File

@ -0,0 +1 @@
12

1
.svn/format Normal file
View File

@ -0,0 +1 @@
12

View File

@ -0,0 +1,93 @@
{
"name": "IRCIS",
"license": "MIT",
"scripts": {
"dev": "vue-cli-service serve --open",
"build": "vue-cli-service build",
"build:prod": "vue-cli-service build --mode prod",
"build:uat": "vue-cli-service build --mode uat",
"build:usa": "vue-cli-service build --mode usa",
"pre": "vue-cli-service build --mode pre",
"i18n": "npm run i18n:zh && npm run i18n:en",
"i18n:zh": "node i18nGenerate.js lang=zh keyCol=5 valCol=6",
"i18n:en": "node i18nGenerate.js lang=en keyCol=5 valCol=7"
},
"dependencies": {
"@ffmpeg/core": "^0.10.0",
"@ffmpeg/ffmpeg": "^0.10.1",
"@microsoft/signalr": "^6.0.8",
"@riophae/vue-treeselect": "0.4.0",
"ali-oss": "^6.17.1",
"axios": "0.18.1",
"copy-webpack-plugin": "^4.5.2",
"cornerstone-core": "^2.3.0",
"cornerstone-math": "^0.1.8",
"cornerstone-tools": "^6.0.8",
"cornerstone-wado-image-loader": "^3.1.2",
"dicom-parser": "^1.8.7",
"echarts": "^4.8.0",
"element-ui": "^2.15.8",
"exceljs": "^4.1.0",
"file-saver": "^2.0.5",
"hammerjs": "^2.0.8",
"js-md5": "^0.7.3",
"jszip": "^3.7.1",
"moment": "^2.27.0",
"normalize.css": "7.0.0",
"nprogress": "0.2.0",
"path-to-regexp": "2.4.0",
"pdfobject": "^2.2.8",
"popper.js": "^1.16.1",
"screenfull": "^4.2.0",
"sortablejs": "^1.15.0",
"v-viewer": "^1.6.4",
"vue": "2.6.10",
"vue-clipboard2": "^0.3.1",
"vue-contextmenujs": "^1.3.13",
"vue-count-to": "^1.0.13",
"vue-i18n": "^8.7.0",
"vue-pdf": "^4.3.0",
"vue-router": "3.0.6",
"vue-seamless-scroll": "^1.1.21",
"vuedraggable": "^2.24.3",
"vuex": "3.1.0",
"webpack-aliyun-oss": "^0.5.9"
},
"devDependencies": {
"@babel/core": "7.0.0",
"@babel/register": "7.0.0",
"@vue/cli-plugin-babel": "3.6.0",
"@vue/cli-plugin-eslint": "^3.9.1",
"@vue/cli-plugin-unit-jest": "3.6.3",
"@vue/cli-service": "3.6.0",
"@vue/test-utils": "1.0.0-beta.29",
"autoprefixer": "^9.5.1",
"babel-core": "7.0.0-bridge.0",
"babel-eslint": "7.2.3",
"babel-jest": "23.6.0",
"chalk": "2.4.2",
"connect": "3.6.6",
"eslint": "5.15.3",
"eslint-plugin-vue": "5.2.2",
"html-webpack-plugin": "3.2.0",
"mockjs": "1.0.1-beta3",
"node-sass": "^4.9.0",
"node-xlsx": "^0.21.0",
"runjs": "^4.3.2",
"sass-loader": "^7.1.0",
"script-ext-html-webpack-plugin": "2.1.3",
"script-loader": "0.7.2",
"serve-static": "^1.13.2",
"svg-sprite-loader": "4.1.3",
"svgo": "1.2.2",
"vue-template-compiler": "2.6.10"
},
"engines": {
"node": ">=8.9",
"npm": ">= 3.0.0"
},
"browserslist": [
"> 1%",
"last 2 versions"
]
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,420 @@
<template>
<el-form
ref="clinicalDataForm"
v-loading="loading"
:model="form"
size="small"
:rules="rules"
:disabled="type === 'look'"
label-width="130px"
>
<div class="base-dialog-body">
<el-form-item label="类型" prop="ClinicalQuestionType">
<el-select v-model="form.ClinicalQuestionType" clearable @change="((val)=>{typeChange(val, form)})">
<el-option
v-for="item of $d.ClinicalQuestionType"
:key="item.value"
:value="item.value"
:label="item.label"
/>
</el-select>
</el-form-item>
<el-form-item
v-if="form.ClinicalQuestionType === 'group'"
label="分组名称"
prop="GroupName"
>
<el-input v-model="form.GroupName" />
</el-form-item>
<el-form-item
v-if="form.ClinicalQuestionType === 'group'"
label="分组名称EN"
prop="GroupEnName"
>
<el-input v-model="form.GroupEnName" />
</el-form-item>
<el-form-item
v-if="form.ClinicalQuestionType !== 'group'"
label="问题名称"
prop="QuestionName"
>
<el-input v-model="form.QuestionName" />
</el-form-item>
<el-form-item
v-if="form.ClinicalQuestionType !== 'group'"
label="问题名称EN"
prop="QuestionEnName"
>
<el-input v-model="form.QuestionEnName" />
</el-form-item>
<el-form-item
v-if="form.Type === 'select' || form.Type === 'radio'"
label="选项"
prop="TypeValue"
>
<el-input
v-model="form.TypeValue"
placeholder="选项请用‘|’分割多个选项"
/>
</el-form-item>
<el-form-item v-if="form.ClinicalQuestionType !== 'group'" label="是否显示" prop="ShowQuestion">
<el-radio-group
v-model="form.ShowQuestion"
@change="((val)=>{isShowQuestionChange(val, form)})"
>
<el-radio
v-for="item of $d.ShowQuestion"
:key="`ShowQuestion${item.value}`"
:label="item.value"
>
{{ item.label }}
</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item
v-if="form.ClinicalQuestionType !== 'group' && form.ShowQuestion===1"
label="显示依赖父问题"
prop="ParentId"
>
<el-select
v-model="form.ParentId"
clearable
@change="((val)=>{parentQuestionChange(val, form)})"
>
<el-option
v-for="item of parentOptions"
:key="`ParentId${item.QuestionId}`"
:label="item.QuestionName"
:value="item.QuestionId"
/>
</el-select>
</el-form-item>
<!-- 显示时依赖父问题触发值 -->
<el-form-item v-if="form.ParentId && form.ShowQuestion===1" label="显示触发值" prop="ParentTriggerValue">
<el-select v-model="form.ParentTriggerValue" clearable>
<el-option
v-for="item of parentTriggerValOptions"
:key="item.id"
:label="item.label"
:value="item.value.toString()"
/>
</el-select>
</el-form-item>
<el-form-item v-if="form.ClinicalQuestionType !== 'group' && form.ClinicalQuestionType !== 'table'" label="是否必填" prop="IsRequired">
<el-radio-group
v-model="form.IsRequired"
>
<el-radio
v-for="item of $d.YesOrNo"
:key="`QuestionRequired${item.value}`"
:label="item.value"
>
{{ item.label }}
</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item v-if="form.ClinicalQuestionType !== 'group'" label="问题分组">
<el-select v-model="form.GroupId" clearable :disabled="isParentExistGroup">
<el-option
v-for="group of groupOptions"
:key="group.GroupId"
:label="group.GroupName"
:value="group.GroupId"
/>
</el-select>
</el-form-item>
<el-form-item v-if="form.ClinicalQuestionType === 'upload'" label="最大上传个数">
<el-input-number v-model="form.ImageCount" controls-position="right" :min="1" :max="10" />
</el-form-item>
<el-form-item label="序号" prop="ShowOrder">
<el-input-number
v-model="form.ShowOrder"
controls-position="right"
:min="0"
/>
</el-form-item>
</div>
<div class="base-dialog-footer" style="text-align:right;margin-top:10px;">
<el-form-item>
<!-- 取消 -->
<el-button
:disabled="btnLoading"
size="small"
type="primary"
@click="close"
>
{{ $t('common:button:cancel') }}
</el-button>
<!-- 保存 -->
<el-button size="small" type="primary" :loading="btnLoading" @click="save">
{{ $t('common:button:save') }}
</el-button>
</el-form-item>
</div>
</el-form>
</template>
<script>
import { addOrUpdateSystemClinicalQuestion, getSystemClinicalOtherQuestionList, getSystemClinicalGroupQuestionList } from '@/api/dictionary'
export default {
name: 'AddOrUpdateClinicalData',
props: {
clinicalId: {
type: String,
default() { return '' }
},
data: {
type: Object,
default() { return {} }
},
type: {
type: String,
default() { return '' }
}
},
data() {
var validateTypeVal = (rule, value, callback) => {
if (value === '') {
callback(new Error(this.$t('common:ruleMessage:specify')))
} else {
var arr = value.split('|')
if (new Set(arr).size !== arr.length) {
callback(new Error('选项不允许存在相同值'))
} else {
callback()
}
}
}
return {
validateTypeVal: validateTypeVal,
form: {
Id: '',
QuestionName: '',
QuestionEnName: '',
ClinicalQuestionType: null,
ValueType: '',
Unit: '',
QuestionGenre: '',
TypeValue: '',
DictionaryCode: '',
ParentId: '',
ParentTriggerValue: '',
ShowOrder: 0,
ShowQuestion: 0,
IsRequired: 2,
IsJudgeQuestion: false,
GroupName: '',
GroupEnName: '',
Remark: '',
ImageCount: 1,
RelevanceId: '',
RelevanceValue: '',
LesionType: '',
QuestionType: null,
IsShowInDicom: false,
OrderMark: '',
MaxQuestionCount: 0,
GlobalReadingShowType: 3,
DefaultValue: null,
LimitEdit: 0,
GroupId: null,
ConvertShowType: 0
},
rules: {
Type: [
{ required: true, message: '请选择', trigger: 'blur' }
],
LesionType: [
{ required: true, message: '请选择', trigger: 'blur' }
],
QuestionName: [{ required: true, message: '请注明', trigger: 'blur' },
{ max: 300, message: '最大长度为 300' }],
QuestionGenre: [{ required: true, message: '请选择', trigger: 'blur' }],
TypeValue: [{ required: true, message: '请注明', trigger: 'blur' },
{ validator: validateTypeVal, trigger: 'blur' },
{ max: 500, message: '最大长度为 500' }],
DictionaryCode: [{ required: true, message: '请选择', trigger: 'blur' }],
ShowQuestion: [{ required: true, message: '请选择', trigger: 'blur' }],
IsRequired: [{ required: true, message: '请选择', trigger: 'blur' }],
ParentId: [{ required: true, message: '请选择', trigger: 'blur' }],
ParentTriggerValue: [
{ required: true, message: '请选择', trigger: 'blur' }
],
RelevanceId: [{ required: true, message: '请选择', trigger: 'blur' }],
RelevanceValue: [{ required: true, message: '请选择', trigger: 'blur' }],
GroupName: [{ required: true, message: '请注明', trigger: 'blur' },
{ max: 50, message: '最大长度为 50' }]
},
loading: false,
btnLoading: false,
parentOptions: [],
parentTriggerValOptions: [],
groupOptions: [],
isParentExistGroup: false,
lesionTypes: [],
dicList: [],
CriterionDictionaryList: []
}
},
mounted() {
this.initForm()
},
methods: {
async initForm() {
await this.getGroupOptions()
await this.getParentQuestions()
await this.getBasicConfigSelect()
if (Object.keys(this.data).length > 0) {
for (const k in this.form) {
if (this.data.hasOwnProperty(k)) {
this.form[k] = this.data[k]
}
}
if (this.form.ParentId !== '' && this.form.ParentId !== null && this.form.GroupName !== '') {
this.isParentExistGroup = true
}
if (this.form.ParentId) {
var index = this.parentOptions.findIndex(item => {
return item.QuestionId === this.form.ParentId
})
if (index !== -1) {
if (this.parentOptions[index].QuestionGenre === 3) {
this.parentTriggerValOptions = this.$d[this.parentOptions[index].DictionaryCode]
} else {
const options = []
this.parentOptions[index].TypeValue.split('|').forEach((item, index) => {
options.push({ id: index, label: item, value: item })
})
this.parentTriggerValOptions = options
}
}
}
if (this.form.RelevanceId) {
var i = this.parentOptions.findIndex(item => {
return item.QuestionId === this.form.RelevanceId
})
if (i !== -1) {
if (this.parentOptions[index].QuestionGenre === 3) {
this.reParentTriggerValOptions = this.$d[this.parentOptions[index].DictionaryCode]
} else {
const options = []
this.parentOptions[index].TypeValue.split('|').forEach((item, index) => {
options.push({ id: index, label: item, value: item })
})
this.reParentTriggerValOptions = options
}
}
}
}
},
save() {
this.$refs.clinicalDataForm.validate(valid => {
if (!valid) return
this.btnLoading = true
if (this.form.ClinicalQuestionType !== 'upload') {
this.form.ImageCount = 0
}
addOrUpdateSystemClinicalQuestion(this.form).then(res => {
this.btnLoading = false
this.$emit('getList')
this.$emit('close')
this.$message.success(this.$t('common:message:savedSuccessfully'))
})
.catch(() => {
this.btnLoading = false
})
})
},
getBasicConfigSelect() {
this.dicList = this.$d
},
getGroupOptions() {
return new Promise((resolve, reject) => {
this.loading = true
var param = {
SystemClinicalId: this.clinicalId
}
getSystemClinicalGroupQuestionList(param).then(res => {
this.groupOptions = res.Result
this.loading = false
resolve()
}).catch(() => {
this.loading = false
reject()
})
})
},
getParentQuestions() {
return new Promise((resolve, reject) => {
this.loading = true
var param = {
SystemClinicalId: this.clinicalId
}
getSystemClinicalOtherQuestionList(param).then(res => {
this.parentOptions = res.Result
this.loading = false
resolve()
}).catch(() => {
this.loading = false
reject()
})
})
},
typeChange(val, data) {
if (val !== 'select' && val !== 'radio') {
}
if (val === 'table') {
data.IsRequired = 2
} else {
data.LesionType = null
}
data.TypeValue = ''
data.ValueType = ''
data.Unit = ''
},
isShowQuestionChange(val, form) {
if (val === 2) {
form.IsRequired = 2
form.ParentId = ''
form.ParentTriggerValue = ''
} else {
form.ParentId = ''
form.ParentTriggerValue = ''
}
},
parentQuestionChange(val, form) {
this.isParentExistGroup = false
if (val) {
var index = this.parentOptions.findIndex(item => {
return item.QuestionId === val
})
if (index !== -1) {
if (this.parentOptions[index].QuestionGenre === 3) {
this.parentTriggerValOptions = this.$d[this.parentOptions[index].DictionaryCode]
} else {
var options = []
this.parentOptions[index].TypeValue.split('|').forEach((item, index) => {
options.push({ id: index, label: item, value: item })
})
this.parentTriggerValOptions = options
}
if (this.parentOptions[index].GroupName) {
this.isParentExistGroup = true
form.GroupName = this.parentOptions[index].GroupName
}
}
} else {
form.GroupName = ''
form.GroupEnName = ''
}
form.ParentTriggerValue = ''
},
close() {
this.$emit('close')
}
}
}
</script>
<style lang="scss" scoped>
</style>

View File

@ -0,0 +1,995 @@
/* eslint-disable */
<template>
<BaseContainer>
<template slot="search-container">
<el-form :inline="true">
<el-form-item :label="$t('trials:consistencyCheck:table:siteId')">
<el-select v-model="searchData.SiteId" clearable filterable style="width:120px;">
<el-option
v-for="(item,index) of siteOptions"
:key="index"
:label="item.TrialSiteCode"
:value="item.SiteId"
/>
</el-select>
</el-form-item>
<!-- 受试者编号 -->
<el-form-item :label="$t('trials:consistencyCheck:table:subjectId')">
<el-input v-model="searchData.SubjectCode" style="width:100px;" />
</el-form-item>
<el-form-item style="margin-bottom:10px" label="访视/阅片期名称">
<el-input
v-model="searchData.TaskName"
style="width:100px;"
clearable
/>
</el-form-item>
<el-form-item style="margin-bottom:10px" label="阅片人">
<el-select v-model="searchData.DoctorUserId" clearable style="width:120px;">
<el-option v-for="item of DoctorUserList" :key="'DoctorUserId' + item.DoctorUserId" :value="item.DoctorUserId" :label="item.UserName" />
</el-select>
</el-form-item>
<el-form-item style="margin-bottom:10px" label="是否加急">
<el-select v-model="searchData.IsUrgent" clearable style="width:120px;">
<el-option v-for="item of $d.YesOrNo" :key="'IsUrgent' + item.label" :value="item.value" :label="item.label" />
</el-select>
</el-form-item>
<el-form-item style="margin-bottom:10px" label="任务状态">
<el-select v-model="searchData.TaskState" clearable style="width:120px;">
<el-option v-for="item of $d.TaskState" :key="'TaskState' + item.label" :value="item.value" :label="item.label" />
</el-select>
</el-form-item>
<el-form-item style="margin-bottom:10px" label="任务类型">
<el-select v-model="searchData.ReadingCategory" clearable style="width:120px;">
<el-option v-for="item of $d.ReadingCategory" :key="'ReadingCategory' + item.label" :value="item.value" :label="item.label" />
</el-select>
</el-form-item>
<el-form-item style="margin-bottom:10px" label="分配状态">
<el-select v-model="searchData.TaskAllocationState" clearable style="width:120px;">
<el-option v-for="item of $d.TaskAllocationState" :key="'TaskAllocationState' + item.label" :value="item.value" :label="item.label" />
</el-select>
</el-form-item>
<el-form-item style="margin-bottom:10px" label="分配时间">
<el-date-picker
v-model="timeList"
value-format="yyyy-MM-dd HH:mm:ss"
type="datetimerange"
range-separator="至"
start-placeholder="开始日期"
end-placeholder="结束日期"
@change="changeTimeList"
/>
</el-form-item>
<el-form-item style="margin-bottom:10px">
<el-button type="primary" icon="el-icon-search" @click="handleSearch">
{{ $t('common:button:search') }}
</el-button>
<el-button type="primary" icon="el-icon-refresh-left" @click="handleReset">
{{ $t('common:button:reset') }}
</el-button>
</el-form-item>
</el-form>
</template>
<template slot="main-container">
<el-table
v-adaptive="{bottomOffset:60}"
v-loading="loading"
:data="list"
stripe
height="100"
@sort-change="handleSortChange"
>
<el-table-column
prop="OriginalReReadingTask.IsUrgent"
:label="$t('trials:consistencyCheck:table:isUrgent')"
show-overflow-tooltip
min-width="100"
sortable="custom"
>
<template slot-scope="scope">
<el-tag v-if="scope.row.IsUrgent" type="danger">{{ $fd('YesOrNo', scope.row.OriginalReReadingTask.IsUrgent) }}</el-tag>
<el-tag v-else type="primary">{{ $fd('YesOrNo', scope.row.OriginalReReadingTask.IsUrgent) }}</el-tag>
</template>
</el-table-column>
<el-table-column
prop="OriginalReReadingTask.TaskCode"
label="任务编号"
min-width="100"
sortable="custom"
show-overflow-tooltip
/>
<el-table-column
prop="OriginalReReadingTask.TrialSiteCode"
label="中心编号"
min-width="100"
sortable="custom"
show-overflow-tooltip
/>
<el-table-column
prop="OriginalReReadingTask.SubjectCode"
label="受试者编号"
min-width="120"
sortable="custom"
show-overflow-tooltip
/>
<el-table-column
prop="OriginalReReadingTask.TaskName"
label="访视/阅片期名称"
min-width="160"
sortable="custom"
show-overflow-tooltip
/>
<el-table-column
prop="OriginalReReadingTask.TaskBlindName"
label="访视盲态名称"
min-width="140"
sortable="custom"
show-overflow-tooltip
/>
<el-table-column
prop="OriginalReReadingTask.CreateTime"
label="任务创建时间"
min-width="140"
sortable="custom"
show-overflow-tooltip
/>
<el-table-column
prop="OriginalReReadingTask.TaskState"
label="任务状态"
min-width="100"
sortable="custom"
show-overflow-tooltip
>
<template slot-scope="scope">
<el-tag v-if="scope.row.OriginalReReadingTask.TaskState === 0" type="primary">{{ $fd('TaskState', scope.row.OriginalReReadingTask.TaskState) }}</el-tag>
<el-tag v-if="scope.row.OriginalReReadingTask.TaskState === 1" type="info">{{ $fd('TaskState', scope.row.OriginalReReadingTask.TaskState) }}</el-tag>
<el-tag v-if="scope.row.OriginalReReadingTask.TaskState === 3" type="danger">{{ $fd('TaskState', scope.row.OriginalReReadingTask.TaskState) }}</el-tag>
<el-tag v-if="scope.row.OriginalReReadingTask.TaskState === 4" type="warning">{{ $fd('TaskState', scope.row.OriginalReReadingTask.TaskState) }}</el-tag>
</template>
</el-table-column>
<el-table-column
prop="OriginalReReadingTask.ReadingCategory"
label="任务类型"
min-width="100"
sortable="custom"
show-overflow-tooltip
>
<template slot-scope="scope">
<el-tag v-if="scope.row.OriginalReReadingTask.ReadingCategory === 1" type="primary">{{ $fd('ReadingCategory', scope.row.OriginalReReadingTask.ReadingCategory) }}</el-tag>
<el-tag v-if="scope.row.OriginalReReadingTask.ReadingCategory === 2" type="info">{{ $fd('ReadingCategory', scope.row.OriginalReReadingTask.ReadingCategory) }}</el-tag>
<el-tag v-if="scope.row.OriginalReReadingTask.ReadingCategory === 4" type="danger">{{ $fd('ReadingCategory', scope.row.OriginalReReadingTask.ReadingCategory) }}</el-tag>
<el-tag v-if="scope.row.OriginalReReadingTask.ReadingCategory === 5" type="warning">{{ $fd('ReadingCategory', scope.row.OriginalReReadingTask.ReadingCategory) }}</el-tag>
</template>
</el-table-column>
<el-table-column
prop="OriginalReReadingTask.ArmEnum"
label="角色"
min-width="80"
show-overflow-tooltip
>
<template slot-scope="scope">
<el-tag v-if="scope.row.OriginalReReadingTask.ArmEnum === 1" type="primary">{{ $fd('ArmEnum', scope.row.OriginalReReadingTask.ArmEnum) }}</el-tag>
<el-tag v-if="scope.row.OriginalReReadingTask.ArmEnum === 2" type="success">{{ $fd('ArmEnum', scope.row.OriginalReReadingTask.ArmEnum) }}</el-tag>
<el-tag v-if="scope.row.OriginalReReadingTask.ArmEnum === 3" type="info">{{ $fd('ArmEnum', scope.row.OriginalReReadingTask.ArmEnum) }}</el-tag>
<el-tag v-if="scope.row.OriginalReReadingTask.ArmEnum === 4" type="danger">{{ $fd('ArmEnum', scope.row.OriginalReReadingTask.ArmEnum) }}</el-tag>
<el-tag v-if="scope.row.OriginalReReadingTask.ArmEnum === 5" type="warning">{{ $fd('ArmEnum', scope.row.OriginalReReadingTask.ArmEnum) }}</el-tag>
</template>
</el-table-column>
<el-table-column
prop="OriginalReReadingTask.UserName"
label="阅片人"
min-width="100"
sortable="custom"
show-overflow-tooltip
>
<template slot-scope="scope">
{{ scope.row.OriginalReReadingTask.UserName }}({{ scope.row.OriginalReReadingTask.FullName }})
</template>
</el-table-column>
<el-table-column
prop="OriginalReReadingTask.SignTime"
label="阅片完成时间"
min-width="180"
sortable="custom"
show-overflow-tooltip
/>
<el-table-column
prop="OriginalReReadingTask.RequestReReadingType"
label="重阅申请类型"
min-width="140"
sortable="custom"
show-overflow-tooltip
>
<template slot-scope="scope">
<div v-if="scope.row.RequestReReadingType">
<el-tag v-if="scope.row.RequestReReadingType === 2" type="danger">{{ $fd('RequestReReadingType', scope.row.RequestReReadingType) }}</el-tag>
<el-tag v-else type="primary">{{ $fd('RequestReReadingType', scope.row.RequestReReadingType) }}</el-tag>
</div>
</template>
</el-table-column>
<el-table-column
prop="RequestReReadingTime"
label="申请时间"
min-width="100"
sortable="custom"
show-overflow-tooltip
/>
<el-table-column
prop="RequestReReadingReason"
label="申请原因"
min-width="100"
sortable="custom"
show-overflow-tooltip
/>
<el-table-column
prop="AllocateTime"
label="审批结果"
min-width="100"
sortable="custom"
show-overflow-tooltip
>
<template slot-scope="scope">
<div v-if="scope.row.RequestReReadingResultEnum">
<el-tooltip v-if="scope.row.RequestReReadingResultEnum === 2" class="item" effect="dark" placement="top">
<div slot="content">
{{ scope.row.RequestReReadingRejectReason }}
</div>
<el-tag type="danger">{{ $fd('RequestReReadingResult', scope.row.RequestReReadingResultEnum) }}</el-tag>
</el-tooltip>
<el-tag v-else type="primary">{{ $fd('RequestReReadingResult', scope.row.RequestReReadingResultEnum) }}</el-tag>
</div>
</template>
</el-table-column>
<el-table-column
prop="ReReadingNewTaskCode"
label="新任务编号"
min-width="120"
sortable="custom"
show-overflow-tooltip
/>
<el-table-column
fixed="right"
label="操作"
width="210"
>
<template slot-scope="scope">
<el-button
:disabled="!!scope.row.RequestReReadingResultEnum || scope.row.RequestReReadingType === 1"
icon="el-icon-check"
circle
title="同意"
@click="openReReadingOrBackList(scope.row,1)"
/>
<el-button
:disabled="!!scope.row.RequestReReadingResultEnum || scope.row.RequestReReadingType === 1"
icon="el-icon-close"
circle
title="拒绝"
@click="confirmReReading(scope.row,2, true)"
/>
</template>
</el-table-column>
</el-table>
<!-- 分页组件 -->
<pagination class="page" :total="total" :page.sync="searchData.PageIndex" :limit.sync="searchData.PageSize" @pagination="getList" />
<el-dialog
v-if="ConfirmReReadingVisible"
title="驳回"
:visible.sync="ConfirmReReadingVisible"
width="600px"
append-to-body
:close-on-click-modal="false"
custom-class="base-dialog-wrapper"
>
<div style="padding:10px;border: 1px solid #e0e0e0;max-height:650px;overflow-y: auto;">
<el-form
ref="reasonForm"
:rules="rules"
:model="ConfirmReReadingForm"
class="demo-ruleForm"
size="small"
label-width="120px"
>
<el-form-item label="驳回原因" prop="RequestReReadingRejectReason">
<el-input
v-model="ConfirmReReadingForm.RequestReReadingRejectReason"
type="textarea"
:autosize="{ minRows: 2, maxRows: 4}"
:placeholder="$t('common:ruleMessage:specify')"
maxlength="500"
show-word-limit
/>
</el-form-item>
</el-form>
</div>
<div slot="footer" class="dialog-footer">
<!-- 取消 -->
<el-button :disabled="btnLoading" size="small" type="primary" @click="ConfirmReReadingVisible = false;ConfirmReReadingForm = { RequestReReadingRejectReason: null }">
{{ $t('common:button:cancel') }}
</el-button>
<!-- 保存 -->
<el-button :loading="btnLoading" size="small" type="primary" @click="confirmReReading(rowData, 2)">
{{ $t('common:button:save') }}
</el-button>
</div>
</el-dialog>
<el-dialog
v-if="ReReadingOrBackVisible"
title="重阅影响任务列表"
:visible.sync="ReReadingOrBackVisible"
width="1360px"
append-to-body
:close-on-click-modal="false"
custom-class="base-dialog-wrapper"
>
<div class="base-dialog-body">
<el-table
v-loading="loading"
:data="InfluenceTaskList"
stripe
height="100"
style="min-height: 400px;"
>
<el-table-column
prop="TaskCode"
label="任务编号"
min-width="100"
show-overflow-tooltip
/>
<el-table-column
prop="TrialSiteCode"
label="中心编号"
min-width="100"
show-overflow-tooltip
/>
<el-table-column
prop="SubjectCode"
label="受试者编号"
min-width="120"
show-overflow-tooltip
/>
<el-table-column
prop="VisitTaskNum"
label="访视/阅片期名称"
min-width="160"
sortable="custom"
show-overflow-tooltip
>
<template slot-scope="scope">
{{scope.row.TaskName}}
</template>
</el-table-column>
<el-table-column
prop="TaskBlindName"
label="访视盲态名称"
min-width="140"
show-overflow-tooltip
/>
<el-table-column
prop="TaskBlindName"
label="访视盲态名称"
min-width="140"
show-overflow-tooltip
/>
<el-table-column
prop="TaskState"
label="任务状态"
min-width="100"
sortable="custom"
show-overflow-tooltip
>
<template slot-scope="scope">
<el-tag v-if="scope.row.TaskState === 0" type="primary">{{ $fd('TaskState', scope.row.TaskState) }}</el-tag>
<el-tag v-if="scope.row.TaskState === 1" type="info">{{ $fd('TaskState', scope.row.TaskState) }}</el-tag>
<el-tag v-if="scope.row.TaskState === 3" type="danger">{{ $fd('TaskState', scope.row.TaskState) }}</el-tag>
<el-tag v-if="scope.row.TaskState === 4" type="warning">{{ $fd('TaskState', scope.row.TaskState) }}</el-tag>
</template>
</el-table-column>
<el-table-column
prop="ReadingCategory"
min-width="100"
label="任务类型"
show-overflow-tooltip
>
<template slot-scope="scope">
<el-tag v-if="scope.row.ReadingCategory === 1" type="primary">{{ $fd('ReadingCategory', scope.row.ReadingCategory) }}</el-tag>
<el-tag v-if="scope.row.ReadingCategory === 2" type="info">{{ $fd('ReadingCategory', scope.row.ReadingCategory) }}</el-tag>
<el-tag v-if="scope.row.ReadingCategory === 4" type="danger">{{ $fd('ReadingCategory', scope.row.ReadingCategory) }}</el-tag>
<el-tag v-if="scope.row.ReadingCategory === 5" type="warning">{{ $fd('ReadingCategory', scope.row.ReadingCategory) }}</el-tag>
</template>
</el-table-column>
<el-table-column
prop="OptType"
label="生效后任务状态"
min-width="160"
show-overflow-tooltip
>
<template slot-scope="scope">
<el-tag v-if="scope.row.OptType === 0" type="primary">{{ $fd('TaskState', scope.row.OptType) }}</el-tag>
<el-tag v-if="scope.row.OptType === 1" type="info">{{ $fd('TaskState', scope.row.OptType) }}</el-tag>
<el-tag v-if="scope.row.OptType === 3" type="danger">{{ $fd('TaskState', scope.row.OptType) }}</el-tag>
<el-tag v-if="scope.row.OptType === 4" type="warning">{{ $fd('TaskState', scope.row.OptType) }}</el-tag>
</template>
</el-table-column>
<el-table-column
prop="UserName"
label="阅片人"
min-width="160"
show-overflow-tooltip
>
<template v-if="scope.row.DoctorUser.UserName" slot-scope="scope">
{{ scope.row.DoctorUser.UserName }}({{ scope.row.DoctorUser.FullName }})
</template>
</el-table-column>
</el-table>
</div>
<div slot="footer" class="dialog-footer">
<!-- 取消 -->
<el-button :disabled="btnLoading" size="small" type="primary" @click="ReReadingOrBackVisible = false">
{{ $t('common:button:cancel') }}
</el-button>
<!-- 保存 -->
<el-button :loading="btnLoading" size="small" type="primary" @click="confirmReReading(rowData, 1)">
确定
</el-button>
</div>
</el-dialog>
</template>
</BaseContainer>
</template>
<script>
import { getTrialSiteSelect } from '@/api/trials'
import { getReReadingOrBackInfluenceTaskList, applyReReading, confirmReReading, getSPMReReadingTaskList, assignSubjectTaskToDoctor, manualAssignDoctorApplyTask, autoSubjectAssignDoctor, getTaskAllocationRuleList, getSubjectAssignList, assignSubjectDoctor, cancelSubjectAssignDoctor, deleteTaskAllocationRule, configTrialTaskInfo, getDoctorUserSelectList } from '@/api/trials/reading'
import BaseContainer from '@/components/BaseContainer'
import Pagination from '@/components/Pagination'
import { getToken } from '@/utils/auth'
import { changeURLStatic } from '@/utils/history.js'
const searchDataDefault = () => {
return {
PageIndex: 1,
PageSize: 20,
Asc: true,
SortField: '',
TrialId: null,
SiteId: null,
SubjectId: null,
SubjectCode: null,
IsUrgent: null,
TaskName: null,
TaskState: null,
DoctorUserId: null,
ReadingCategory: null,
TaskAllocationState: null,
BeginAllocateDate: null,
EndAllocateDate: null,
RootReReadingTaskId: null
}
}
export default {
name: 'TrialsNotice',
components: { BaseContainer, Pagination },
data() {
return {
searchData: searchDataDefault(),
ReReadingOrBackVisible: false,
InfluenceTaskList: [],
maxLength: 0,
list: [],
siteOptions: [],
total: 0,
SubjectAssignListTotal: 0,
loading: false,
AllocationConfigVisible: false,
ConfirmReReadingForm: {
RequestReReadingRejectReason: null
},
rules: {
RequestReReadingRejectReason: [{ required: true, message: this.$t('common:ruleMessage:specify'), trigger: 'blur' }]
},
ConfirmReReadingVisible: false,
taskAllocationRuleVisible: false,
AutoAlloctionVisible: false,
manualAllocationVisible: false,
trialId: this.$route.query.trialId,
currentData: {},
param: {},
DoctorUserList: [],
TaskAllocationRuleList: [],
btnLoading: false,
value: true,
value1: true,
TrialTaskInfo: {
TrialId: null,
TaskAllocateObjEnum: 0,
FollowVisitAutoAssignDefaultState: 0,
FollowGlobalVisitAutoAssignDefaultState: 0,
TaskAllocateDefaultState: 1
},
title: '',
rowData: {},
timeList: [],
SubjectAssignList: [],
SubjectAssignSelectList: [],
TaskAllocationRuleSelectList: [],
readVisible: false,
TaskOptType: null,
OtherInfo: null,
ReadingType: null
}
},
mounted() {
if (this.$route.query.SiteId) {
this.searchData.SiteId = this.$route.query.SiteId
this.searchData.SubjectCode = this.$route.query.SubjectCode
this.searchData.TaskName = this.$route.query.TaskName
this.searchData.DoctorUserId = this.$route.query.DoctorUserId
changeURLStatic('SiteId', '')
changeURLStatic('SubjectCode', '')
changeURLStatic('TaskName', '')
changeURLStatic('DoctorUserId', '')
}
this.getList()
this.getSite()
this.getDoctorUserSelectList()
},
methods: {
openReReadingOrBackList(row) {
this.loading = true
this.rowData = {...row}
getReReadingOrBackInfluenceTaskList(row.OriginalReReadingTaskId, true, row.Id).then(res => {
this.InfluenceTaskList = res.Result
this.ReReadingOrBackVisible = true
this.loading = false
}).catch(() => {this.loading = false})
},
lookReadingResults(row) {
var token = getToken()
const routeData = this.$router.resolve({
path: `/noneDicomReading?subjectId=${row.SubjectId}&trialId=${row.TrialId}&visitTaskId=${row.NewReReadingTaskId}&TokenKey=${token}`
})
window.open(routeData.href, '_blank')
},
// 申请重阅
applyReReading(row) {
this.$confirm('确定要申请重阅?').then(() => {
this.loading = true
this.btnLoading = true
var params = {
TaskIdList: [
row.NewReReadingTaskId
],
TrialId: this.$route.query.trialId,
RequestReReadingType: 2,
RequestReReadingReason: ''
}
applyReReading(params).then(() => {
this.loading = false
this.btnLoading = false
this.$message.success('申请重阅成功')
this.getList()
}).catch(() => {
this.loading = false
this.btnLoading = false
})
})
},
confirmReReading(row, type, open) {
if (open) {
this.rowData = { ...row }
this.ConfirmReReadingVisible = true
this.ConfirmReReadingForm = {
RequestReReadingRejectReason: null
}
return
}
if (type === 2) {
this.$refs.reasonForm.validate((valid) => {
if (!valid) return
var params = {
ConfirmReReadingList: [
{
Id: row.Id,
OriginalReReadingTaskId: row.OriginalReReadingTaskId,
RootReReadingTaskId: row.RootReReadingTaskId,
NewReReadingTaskId: row.NewReReadingTaskId
}
],
RequestReReadingRejectReason: this.ConfirmReReadingForm.RequestReReadingRejectReason,
TrialId: this.$route.query.trialId,
RequestReReadingResultEnum: type
}
this.loading = true
this.btnLoading = true
confirmReReading(params).then(() => {
this.loading = false
this.btnLoading = false
this.$message.success(`${type === 1 ? '同意' : '拒绝'}申请成功`)
this.getList()
this.ConfirmReReadingVisible = false
}).catch(() => {
this.loading = false
this.btnLoading = false
})
})
return
}
this.$confirm(`确定${type === 1 ? '同意' : '拒绝'}该重阅申请?`).then(() => {
var params = {
ConfirmReReadingList: [
{
Id: row.Id,
OriginalReReadingTaskId: row.OriginalReReadingTaskId,
RootReReadingTaskId: row.RootReReadingTaskId,
NewReReadingTaskId: row.NewReReadingTaskId
}
],
TrialId: this.$route.query.trialId,
RequestReReadingResultEnum: type
}
this.loading = true
this.btnLoading = true
confirmReReading(params).then(() => {
this.loading = false
this.btnLoading = false
this.$message.success(`${type === 1 ? '同意' : '拒绝'}申请成功`)
this.ReReadingOrBackVisible = false
this.getList()
}).catch(() => {
this.loading = false
this.btnLoading = false
})
})
},
assignSubjectTaskToDoctor(row, type) {
this.rowData = { ...row }
this.rowData.TaskOptType = type
if (type === 1 || type === 2) {
this.readVisible = true
} else if (type === 3) {
this.$confirm('是否确定分配').then(() => {
this.btnLoading = true
this.loading = true
var params = Object.assign({}, this.rowData)
assignSubjectTaskToDoctor(params).then(() => {
this.$message.success('保存成功')
this.btnLoading = false
this.loading = false
this.readVisible = false
this.getList()
}).catch(() => {
this.btnLoading = false
this.loading = false
})
})
} else if (type === 4) {
this.$confirm('是否确定取消分配').then(() => {
this.btnLoading = true
this.loading = true
var params = Object.assign({}, this.rowData)
assignSubjectTaskToDoctor(params).then(() => {
this.$message.success('保存成功')
this.btnLoading = false
this.loading = false
this.readVisible = false
this.getList()
}).catch(() => {
this.btnLoading = false
this.loading = false
})
})
}
},
assignSubjectTaskToDoctorSave() {
this.$refs.readDataForm.validate(valid => {
if (!valid) return
this.btnLoading = true
this.loading = true
var params = Object.assign({}, this.rowData)
assignSubjectTaskToDoctor(params).then(() => {
this.$message.success('保存成功')
this.btnLoading = false
this.loading = false
this.readVisible = false
this.getList()
}).catch(() => {
this.btnLoading = false
this.loading = false
})
})
},
manualAssignDoctorApplyTask(row) {
console.log(row)
var params = {
TrialId: this.trialId
}
if (row) {
params.SubjectDoctorUserList = [
row
]
}
this.$confirm('确定将当前已分配阅片人应用到任务?').then(() => {
this.loading = true
this.btnLoading = true
manualAssignDoctorApplyTask(params).then(() => {
this.loading = false
this.btnLoading = false
this.$message.success('应用成功')
this.getList()
this.openAutoAlloction()
}).catch(() => {
this.loading = false
this.btnLoading = false
})
})
},
autoSubjectAssignDoctor() {
this.$confirm('确定执行自动分配?').then(() => {
this.loading = true
this.btnLoading = true
autoSubjectAssignDoctor({ TrialId: this.trialId }).then(() => {
this.loading = false
this.btnLoading = false
this.$message.success('自动分配成功')
this.getList()
this.openAutoAlloction()
}).catch(() => {
this.loading = false
this.btnLoading = false
})
})
},
handleSaveTaskAllocationRuleSelectList() {
if (this.TaskAllocationRuleSelectList.length === 0) {
this.$message.error('请选择阅片人')
return
}
var num
if (this.ReadingType === 2) {
num = 2
} else {
num = 1
}
if (this.TaskAllocationRuleSelectList.length !== num) {
this.$message.error(`当前阅片模式为${this.$fd('ReadingMethod', this.ReadingType)},请选择正确阅片人数量`)
return
}
this.loading = true
this.btnLoading = true
var params = {
DoctorUserIdArmList: this.TaskAllocationRuleSelectList.map((v, i) => { return { DoctorUserId: v.DoctorUserId, ArmEnum: this.ReadingType === 1 ? 1 : i + 1 } }),
SubjectIdList: this.SubjectAssignSelectList.map(v => v.SubjectId),
TrialId: this.trialId,
IsReAssign: true
}
assignSubjectDoctor(params).then(() => {
this.loading = false
this.btnLoading = false
this.openAutoAlloction()
this.TaskAllocationRuleSelectList = []
this.SubjectAssignSelectList = []
this.getDoctorUserSelectList()
this.$message.success('分配成功')
this.manualAllocationVisible = false
}).catch(() => {
this.loading = false
this.btnLoading = false
})
},
selectable(row) {
var num
if (this.ReadingType === 2) {
num = 2
} else {
num = 1
}
if (this.TaskAllocationRuleSelectList.length === num && !this.TaskAllocationRuleSelectList.find(v => { return v.UserName === row.UserName })) {
return false
} else {
return true
}
},
cancelSubjectAssignDoctor(row) {
if (row) {
this.$confirm('确定要清空该受试者的阅片人吗?').then(() => {
var params = {
SubjectIdList: [row.SubjectId],
TrialId: this.trialId
}
this.loading = true
this.btnLoading = true
cancelSubjectAssignDoctor(params).then(() => {
this.$message.success('清空成功')
this.loading = false
this.btnLoading = false
this.openAutoAlloction()
}).catch(() => {
this.loading = false
this.btnLoading = false
})
})
return
}
if (this.SubjectAssignSelectList.length === 0) {
this.$message.error('请勾选要清空阅片人的受试者')
return
}
this.$confirm('确定要清空该受试者的阅片人吗?').then(() => {
var params = {
SubjectIdList: this.SubjectAssignSelectList.map(v => v.SubjectId),
TrialId: this.trialId
}
this.loading = true
this.btnLoading = true
cancelSubjectAssignDoctor(params).then(() => {
this.$message.success('清空成功')
this.loading = false
this.btnLoading = false
this.openAutoAlloction()
}).catch(() => {
this.loading = false
this.btnLoading = false
})
})
},
manualAllocation(row) {
if (row) {
this.SubjectAssignSelectList = [row]
}
if (this.SubjectAssignSelectList.length === 0) {
this.$message.error('请勾选要分配的受试者')
return
}
this.manualAllocationVisible = true
},
handleTaskAllocationRuleListSelectChange(e) {
this.TaskAllocationRuleSelectList = e
},
handleSubjectAssignListSelectChange(e) {
this.SubjectAssignSelectList = e
},
changeTimeList() {
if (this.timeList) {
this.searchData.BeginAllocateDate = this.timeList[0]
this.searchData.EndAllocateDate = this.timeList[1]
} else {
this.searchData.BeginAllocateDate = null
this.searchData.EndAllocateDate = null
}
},
handleCancel() {
this.AllocationConfigVisible = false
},
handleSave() {
this.btnLoading = true
this.loading = true
var params = Object.assign({}, this.TrialTaskInfo)
params.TrialId = this.$route.query.trialId
configTrialTaskInfo(params).then(() => {
this.btnLoading = false
this.loading = false
this.AllocationConfigVisible = false
this.getList()
this.$message.success('保存成功')
}).catch(() => {
this.btnLoading = false
this.loading = false
})
},
handleDelete(row) {
this.$confirm(`确定要删除${row.UserName}的任务分配规则?`, {
type: 'warning',
distinguishCancelAndClose: true
}).then(() => {
this.loading = true
deleteTaskAllocationRule(row.Id).then(() => {
this.loading = false
this.$message.success('删除成功')
this.openAllocationConfigVisible()
}).catch(() => { this.loading = false })
})
},
addOrEdit(row) {
if (row) {
this.rowData = { ...row }
this.title = '编辑'
} else {
this.rowData = {}
this.title = '新增'
}
this.taskAllocationRuleVisible = true
},
openAllocationConfigVisible() {
this.loading = true
getTaskAllocationRuleList({
TrialId: this.$route.query.trialId
}).then(res => {
this.loading = false
this.TaskAllocationRuleList = res.Result
this.TrialTaskInfo = Object.assign({}, res.OtherInfo)
this.AllocationConfigVisible = true
this.taskAllocationRuleVisible = false
}).catch(() => { this.loading = false })
},
openAutoAlloction() {
this.loading = true
getTaskAllocationRuleList({
TrialId: this.$route.query.trialId
}).then(async(res) => {
this.taskSearchData.TrialId = this.$route.query.trialId
var params = Object.assign({}, this.taskSearchData)
var SubjectAssignListRes = await getSubjectAssignList(params)
this.SubjectAssignList = SubjectAssignListRes.Result.CurrentPageData
this.SubjectAssignListTotal = SubjectAssignListRes.Result.TotalCount
this.loading = false
this.TaskAllocationRuleList = res.Result
this.TrialTaskInfo = Object.assign({}, res.OtherInfo)
this.AutoAlloctionVisible = true
}).catch(() => { this.loading = false })
},
getDoctorUserSelectList() {
getDoctorUserSelectList(this.$route.query.trialId).then(res => {
this.DoctorUserList = res.Result
if (this.DoctorUserList.length > 0) {
this.ReadingType = this.DoctorUserList[0].ReadingType
} else {
this.$alert('请先去选择项目阅片人')
}
})
},
getList() {
this.searchData.TrialId = this.$route.query.trialId
this.loading = true
getSPMReReadingTaskList(this.searchData).then(res => {
this.loading = false
this.OtherInfo = res.OtherInfo
this.list = res.Result.CurrentPageData
this.total = res.Result.TotalCount
}).catch(() => { this.loading = false })
},
handleSearch() {
this.searchData.PageIndex = 1
this.getList()
},
handleReset() {
this.searchData = searchDataDefault()
this.getList()
},
getSite() {
getTrialSiteSelect(this.trialId).then(res => {
this.siteOptions = res.Result
})
},
// 排序
handleSortChange(column) {
if (column.order === 'ascending') {
this.searchData.Asc = true
} else {
this.searchData.Asc = false
}
this.searchData.SortField = column.prop
this.searchData.PageIndex = 1
this.getList()
}
}
}
</script>
<style lang="scss" scoped>
>>>.hidden-row{
display: none;
}
>>>.el-dialog__body {
padding: 15px 20px;
}
>>>.el-tag--danger.el-tag--dark {
// background-color: #f56c6c!important;
border-color: none!important;
// color: #fff!important;
}
>>>#TaskAllocationRuleList thead .el-checkbox__inner{
display: none;
}
>>>.el-descriptions-item__label.has-colon:after{
display: none;
}
</style>

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,214 @@
<template>
<div>
<div class="search-form" style="display:flex;justify-content: space-between;">
<div>
</div>
<div>
<el-button
size="mini"
type="primary"
@click="handleAdd"
style="margin-right: 10px;"
>
配置
</el-button>
</div>
</div>
<el-table
v-loading="loading"
v-adaptive="{bottomOffset:0}"
height="100"
style="width: 100%"
:data="list"
stripe
>
<el-table-column
prop=""
label="序号"
width="50"
>
<template slot-scope="scope">
{{scope.$index + 1}}
</template>
</el-table-column>
<el-table-column
prop="Code"
label="键值"
show-overflow-tooltip
/>
<el-table-column
prop="ValueCN"
label="中文值"
show-overflow-tooltip
>
</el-table-column>
<el-table-column
prop="Value"
label="英文值"
show-overflow-tooltip
>
</el-table-column>
<el-table-column
prop="Description"
label="描述"
show-overflow-tooltip
>
</el-table-column>
</el-table>
<el-dialog
v-if="config.visible"
:visible.sync="config.visible"
:close-on-click-modal="false"
:title="config.title"
width="90%"
append-to-body
>
<div>
<div style="text-align: right;">
<el-button
size="mini"
type="primary"
@click="handleSave"
v-loading="loading"
>
保存
</el-button>
</div>
<el-table
v-loading="loading"
v-adaptive="{bottomOffset:100}"
height="100"
ref="multipleTable"
:data="$d[parentCode]"
stripe
@selection-change="handleSelectionChange"
>
<el-table-column
type="selection"
width="55">
</el-table-column>
<el-table-column
prop="raw.Code"
label="键值"
show-overflow-tooltip
>
</el-table-column>
<el-table-column
prop="raw.ValueCN"
label="中文值"
show-overflow-tooltip
>
</el-table-column>
<el-table-column
prop="raw.Value"
label="英文值"
show-overflow-tooltip
>
</el-table-column>
<el-table-column
prop="raw.Description"
label="描述"
show-overflow-tooltip
>
</el-table-column>
</el-table>
</div>
</el-dialog>
</div>
</template>
<script>
import { getAssessType, setCriterionDictionary, setDictionaryBaseLineUse, setDictionaryFollowVisitUse } from '@/api/dictionary'
export default {
props: {
criterionId: {
type: String,
required: true
},
parentCode: {
type: String,
required: true
}
},
data() {
return {
list: [],
total: 0,
loading: false,
rowData: {},
activeName: '0',
addOrEdit: { visible: false, title: '' },
preview: { visible: false, title: 'eCRF预览' },
config: { visible: false, title: '标准字典值' },
selectedList: []
}
},
mounted() {
this.getList()
console.log(this.$d[this.parentCode])
},
methods: {
toggleSelection(rows) {
console.log(this.$refs.multipleTable)
if (rows) {
rows.forEach(row => {
this.$refs.multipleTable.toggleRowSelection(row);
});
} else {
this.$refs.multipleTable.clearSelection();
}
},
handleSelectionChange(val) {
this.selectedList = val
},
getList() {
this.loading = true
getAssessType({
CriterionId: this.criterionId,
ParentCode: this.parentCode
}).then(res => {
this.loading = false
this.list = res.Result
})
},
handleAdd() {
this.config.visible = true
this.$nextTick(() => {
var a = this.$d[this.parentCode].filter(v => {
return !!this.list.find(v1 => {
return v1.DictionaryId === v.id
})
})
this.toggleSelection(a)
})
},
handleSave() {
this.loading = true
var params = {
CriterionId: this.criterionId,
ParentCode: this.parentCode,
DictionaryIds: this.selectedList.map(v => v.id),
isSystemCriterion: true
}
setCriterionDictionary(params).then(res => {
this.loading = false
this.$message.success('保存成功')
this.config.visible = false
this.selectedList = []
this.getList()
this.$emit('getList')
}).catch(() => {
})
},
handleDelete() {
}
}
}
</script>
<style scoped>
>>>.el-form-item__content{
width: calc(100% - 110px);
}
</style>

View File

@ -0,0 +1,133 @@
<template>
<div>
<el-form-item
v-if="(questionForm[question.ParentId] === question.ParentTriggerValue) || question.ParentId===''||question.ParentId===null"
:label="`${question.QuestionName}`"
:prop="question.ReadingQuestionTrialId"
:rules="[
{ required: question.IsRequired && question.Type!=='group' && question.Type!=='summary',
message: '请注明', trigger: ['blur', 'change']},
]"
:class="[question.Type==='group'?'mb':'']"
>
<div
v-if="question.Type==='group'"
style="font-weight: bold;font-size: 16px;"
>
{{ question.GroupName }}
</div>
<!-- 输入框 -->
<el-input
v-if="question.Type==='input'"
v-model="questionForm[question.ReadingQuestionTrialId]"
:disabled="readingTaskState >= 2"
/>
<!-- 多行文本输入框 -->
<el-input
v-if="question.Type==='textarea'"
v-model="questionForm[question.ReadingQuestionTrialId]"
type="textarea"
:autosize="{ minRows: 2, maxRows: 4}"
:disabled="readingTaskState >= 2"
/>
<!-- 下拉框 -->
<el-select
v-if="question.Type==='select'"
v-model="questionForm[question.ReadingQuestionTrialId]"
:disabled="readingTaskState >= 2"
@change="((val)=>{formItemChange(val, question)})"
>
<el-option
v-for="val in question.TypeValue.split('|')"
:key="val"
:label="val"
:value="val"
/>
</el-select>
<!-- 单选 -->
<el-radio-group
v-if="question.Type==='radio'"
v-model="questionForm[question.ReadingQuestionTrialId]"
:disabled="readingTaskState >= 2"
@change="((val)=>{formItemChange(val, question)})"
>
<el-radio v-for="val in question.TypeValue.split('|')" :key="val" :label="val">{{ val }}</el-radio>
</el-radio-group>
<!-- 复选框 -->
<el-checkbox-group
v-if="question.Type==='checkbox'"
v-model="questionForm[question.ReadingQuestionTrialId]"
:disabled="readingTaskState >= 2"
>
<el-checkbox v-for="val in question.TypeValue.split('|')" :key="val" :label="val">{{ val }}</el-checkbox>
</el-checkbox-group>
</el-form-item>
<FormItem
v-for="(item) in question.Childrens"
:key="item.ReadingQuestionTrialId"
:question="item"
:reading-task-state="readingTaskState"
:question-form="questionForm"
/>
</div>
</template>
<script>
export default {
name: 'FormItem',
props: {
questionForm: {
type: Object,
default() {
return {}
}
},
question: {
type: Object,
default() {
return []
}
},
readingTaskState: {
type: Number,
required: true
}
},
data() {
return {
}
},
watch: {
questionForm: {
deep: true,
immediate: true,
handler(v) {
// console.log(v)
}
}
},
mounted() {
},
methods: {
formItemChange(v, question) {
if (question.Childrens.length > 0) {
this.resetChild(question.Childrens)
}
},
resetChild(obj) {
obj.forEach(i => {
this.$emit('resetFormItemData', i.ReadingQuestionTrialId)
if (i.Childrens && i.Childrens.length > 0) {
this.resetChild(i.Childrens)
}
})
}
}
}
</script>
<style lang="scss" scoped>
.mb{
margin-bottom: 0px;
}
</style>

View File

@ -0,0 +1,272 @@
<template>
<div v-loading="loading">
<div style="padding: 0 20px;">
<div class="info-content" v-html="crcMessageInfo.TalkContent" />
</div>
<div>
<div style="padding: 10px;background: #f3f3f3;border-radius: 10px;margin-top: 10px">
<div style="display:flex;margin-bottom: 10px;justify-content: space-between;align-items: center">
<div>
请准确核实后录入当前访视的实际影像检查记录:
</div>
<el-button type="primary" size="mini" @click="addMsg">添加</el-button>
</div>
<el-table
:data="msgList">
<el-table-column type="index" width="40" />
<el-table-column
prop=""
label="检查日期"
show-overflow-tooltip
>
<template slot-scope="scope">
{{scope.row.StudyDate}}
</template>
</el-table-column>
<el-table-column
prop=""
label="检查类型"
show-overflow-tooltip
>
<template slot-scope="scope">
{{scope.row.Modality}}
</template>
</el-table-column>
<el-table-column
prop=""
label="已提交"
show-overflow-tooltip
>
<template slot-scope="scope">
{{$fd('YesOrNo', scope.row.IsJoin)}}
</template>
</el-table-column>
<el-table-column
prop=""
label="实际存在"
show-overflow-tooltip
>
<template slot-scope="scope">
<el-switch
:disabled="!scope.row.IsJoin"
v-model="scope.row.IsCheck"
@change="compareStudy"
>
</el-switch>
</template>
</el-table-column>
<el-table-column
prop=""
width="100px"
label="操作"
fixed="right"
>
<template slot-scope="scope">
<el-button :disabled="scope.row.IsJoin" @click="moveMsg(scope.$index)" size="small" type="text">删除</el-button>
</template>
</el-table-column>
</el-table>
<el-form ref="revenusForm"
size="small"
label-width="400px"
:label-position="'top'"
:rules="rules"
:model="form">
<el-form-item v-if="isLackOf" label="IRC与实际情况影像检查不一致的原因为" prop="reason">
<el-checkbox-group v-model="form.reason" style="display: flex;flex-direction: column">
<el-checkbox style="display: block" label="EDC按疗效评估数据记录影像检查信息;">EDC按疗效评估数据记录影像检查信息</el-checkbox>
<el-checkbox style="display: block" label="EDC录入错误;">EDC录入错误</el-checkbox>
<el-checkbox style="display: block" label="IRC数据上传错误申请回退并重新上传;">IRC数据上传错误申请回退并重新上传</el-checkbox>
</el-checkbox-group>
</el-form-item>
</el-form>
</div>
</div>
<div class="function">
<!-- 发送 -->
<el-button
type="primary"
@click="handleReply"
>
{{ $t('trials:consistencyCheck:button:sendMessage') }}
</el-button>
</div>
<el-dialog
v-if="studyVisible"
:visible.sync="studyVisible"
v-dialogDrag
:close-on-click-modal="false"
append-to-body
custom-class="base-dialog-wrapper"
:width="'500px'"
title="添加检查"
>
<el-form>
<div class="base-dialog-body">
<el-form-item label-width="80px" label="检查日期">
<el-date-picker
v-model="typeInfo.StudyDate"
style="width: 100%"
type="date"
value-format="yyyy-MM-dd"
placeholder="选择日期">
</el-date-picker>
</el-form-item>
<el-form-item label-width="80px" label="检查类型">
<el-select
v-model="typeInfo.Modality"
style="width: 100%"
>
<el-option v-for="item of $d.Modality" :label="item.raw.Value" :value="item.value">
</el-option>
</el-select>
</el-form-item>
</div>
<div class="base-dialog-footer" style="text-align:right;margin-top:10px;">
<el-form-item style="text-align:right;">
<el-button size="small" type="primary" @click="handleCancel"> {{ $t('common:button:cancel') }}</el-button>
<el-button size="small" type="primary" @click="handleSave">
{{ $t('common:button:save') }}
</el-button>
</el-form-item>
</div>
</el-form>
</el-dialog>
</div>
</template>
<script>
export default {
name: 'CrcSendMessage',
props: {
crcMessageInfo: {
type: Object,
default() {
return {
TalkContent: null,
}
}
}
},
data() {
return {
loading: false,
msgList: [],
studyVisible: false,
typeInfo: {
StudyDate: null,
Modality: null,
IsJoin: false,
IsCheck: true
},
isLackOf: false,
EDCList: [],
IRCList: [],
form: {
reason: []
},
rules: {
reason: [{ required: true, message: this.$t('common:ruleMessage:select'), trigger: ['blur', 'change'] }]
}
}
},
mounted() {
this.crcMessageInfo.ParamInfoList.forEach(v => {
this.msgList.push({...v, IsJoin: true, Ischeck: false})
})
this.compareStudy()
},
methods: {
compareStudy() {
var isLackOf = false
this.IRCList = []
this.msgList.forEach(v => {
if (v.IsJoin !== v.IsCheck) {
isLackOf = true
this.IRCList.push(v)
}
})
this.isLackOf = isLackOf
},
handleCancel() {
this.studyVisible = false
this.typeInfo = {
StudyDate: null,
Modality: null
}
},
handleSave() {
if (!this.typeInfo.StudyDate || !this.typeInfo.Modality) {
this.$message.error('请选择检查日期和检查类型')
return
}
this.msgList.push({...this.typeInfo})
this.compareStudy()
this.studyVisible = false
this.typeInfo = {
StudyDate: null,
Modality: null,
IsJoin: false,
IsCheck: true
}
},
closeLoading() {
this.loading = false
},
addMsg() {
this.studyVisible = true
},
moveMsg(index) {
this.$confirm('确定删除该条检查记录?').then(res => {
this.msgList.splice(index, 1)
this.compareStudy()
})
},
handleReply() {
if (this.msgList.length === 0) {
this.$message.error('请录入真实的检查情况')
return
}
this.$refs.revenusForm.validate((valid) => {
if (!valid) return
var TalkContent = ''
TalkContent += '经核实,该受试者当前访视的实际影像检查如下:<br>'
this.msgList.forEach((v, i) => {
TalkContent += `${i+1}.${v.StudyDate}的${v.Modality}影像检查<br>`
})
if (this.isLackOf) {
TalkContent += '<br>'
TalkContent += 'IRC与实际情况不一致影像检查如下<br>'
this.IRCList.forEach((v, i) => {
TalkContent += `${i+1}.${v.StudyDate}的${v.Modality}影像检查(${v.IsJoin && !v.IsCheck ? 'IRC多余': 'IRC缺少'}<br>`
})
if (this.form.reason.length > 0) {
TalkContent += '<br>'
TalkContent += 'IRC与实际情况影像检查不一致的原因为<br>'
this.form.reason.forEach((v, i) => {
TalkContent += `${i+1}.${v}<br>`
})
}
} else {
TalkContent += `<br>IRC与实际情况影像检查一致`
}
console.log(TalkContent)
this.$emit('sendMessage', TalkContent, this.closeLoading)
})
}
}
}
</script>
<style scoped>
.function{
margin-top: 10px;
text-align: right;
}
>>>.el-form--label-top .el-form-item__label{
padding-bottom: 0;
padding-top: 20px;
}
</style>

View File

@ -0,0 +1,33 @@
const getDefaultState = () => {
return {
reviewersQuery: null,
isEnglish: true
}
}
const state = getDefaultState
const mutations = {
RESET_STATE: (state) => {
Object.assign(state, getDefaultState())
},
SET_QUERYPARAM: (state, query) => {
state.reviewersQuery = query
},
SET_ISENGLISH: (state, isEnglish) => {
state.isEnglish = isEnglish
}
}
const actions = {
resetState({ commit }) {
commit('RESET_STATE')
}
}
export default {
namespaced: true,
state,
mutations,
actions
}

View File

@ -0,0 +1,268 @@
<template>
<el-form
ref="clinicalDataForm"
v-loading="loading"
:model="form"
size="small"
:rules="rules"
label-width="110px"
>
<div class="base-dialog-body">
<el-form-item label="类型" prop="Type">
<el-select v-model="form.Type">
<el-option
v-for="item of $d.Criterion_Question_Type"
:key="item.value"
:value="item.value"
:label="item.label"
/>
</el-select>
</el-form-item>
<el-form-item
v-if="form.Type === 'group'"
label="分组名称"
prop="GroupName"
>
<el-input v-model="form.GroupName" />
</el-form-item>
<el-form-item
v-if="form.Type !== 'group'"
label="审核问题"
prop="QuestionName"
>
<el-input v-model="form.QuestionName" />
</el-form-item>
<el-form-item
v-if="form.Type !== 'textarea' && form.Type !== 'input' && form.Type !== 'group' && form.Type !== 'summary'"
label="选项"
prop="TypeValue"
>
<el-input v-model="form.TypeValue" placeholder="选项请用‘|’分割多个选项" />
</el-form-item>
<!-- 父问题 -->
<el-form-item v-if="form.Type !== 'group'" label="父问题">
<el-select v-model="form.ParentId" clearable @change="((val)=>{parentQuestionChange(val, form)})">
<el-option
v-for="item of parentOptions"
:key="item.QuestionId"
:label="item.QuestionName"
:value="item.QuestionId"
/>
</el-select>
</el-form-item>
<!-- 父问题触发值 -->
<el-form-item v-if="form.ParentId" label="父问题触发值" prop="ParentTriggerValue">
<el-select v-model="form.ParentTriggerValue">
<el-option
v-for="item of parentTriggerValOptions"
:key="item"
:label="item"
:value="item"
/>
</el-select>
</el-form-item>
<el-form-item v-if="form.Type !== 'group'" label="问题分组">
<el-select v-model="form.GroupName" clearable :disabled="isParentExistGroup">
<el-option
v-for="group of groupOptions"
:key="group"
:label="group"
:value="group"
/>
</el-select>
</el-form-item>
<el-form-item v-if="form.Type !== 'group'" label="是否必填">
<el-radio-group v-model="form.IsRequired">
<el-radio :label="true">是</el-radio>
<el-radio :label="false">否</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item v-if="form.Type !== 'group'" label="是否裁判问题">
<el-radio-group v-model="form.IsJudgeQuestion">
<el-radio :label="true">是</el-radio>
<el-radio :label="false">否</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item v-if="form.Type !== 'group'" label="注释">
<el-input v-model="form.Remark" />
</el-form-item>
<!-- <el-form-item label="启用状态: ">
<el-radio-group v-model="form.IsEnable">
<el-radio :label="true">启用</el-radio>
<el-radio :label="false">停用</el-radio>
</el-radio-group>
</el-form-item> -->
<el-form-item label="序号: " prop="ShowOrder">
<el-input-number v-model="form.ShowOrder" controls-position="right" :min="0" />
</el-form-item>
</div>
<div class="base-dialog-footer" style="text-align:right;margin-top:10px;">
<el-form-item>
<!-- 取消 -->
<el-button
:disabled="btnLoading"
size="small"
type="primary"
@click="close"
>
{{ $t('common:button:cancel') }}
</el-button>
<!-- 保存 -->
<el-button size="small" type="primary" :loading="btnLoading" @click="save">
{{ $t('common:button:save') }}
</el-button>
</el-form-item>
</div>
</el-form>
</template>
<script>
import { addOrUpdateReadingQuestionTrial, getTrialCriterionOtherQuestion, getTrialGroupNameList } from '@/api/trials'
export default {
name: 'AddOrUpdateClinicalData',
props: {
data: {
type: Object,
default() { return {} }
}
},
data() {
return {
form: {
Id: '',
ReadingQuestionCriterionTrialId: '',
QuestionName: '',
Type: 'select',
TypeValue: '',
ParentId: '',
ParentTriggerValue: '',
ShowOrder: 0,
IsRequired: true,
IsJudgeQuestion: false,
GroupName: '',
Remark: ''
// IsEnable: true
},
rules: {
Type: [
{ required: true, message: '请选择', trigger: 'blur' }
],
QuestionName: [{ required: true, message: '请注明', trigger: 'blur' },
{ max: 50, message: '最大长度为 50' }],
TypeValue: [{ required: true, message: '请注明', trigger: 'blur' },
{ max: 200, message: '最大长度为 200' }],
ParentTriggerValue: [
{ required: true, message: '请选择', trigger: 'blur' }
],
GroupName: [{ required: true, message: '请注明', trigger: 'blur' },
{ max: 50, message: '最大长度为 50' }]
},
loading: false,
btnLoading: false,
parentOptions: [],
parentTriggerValOptions: [],
groupOptions: [],
isParentExistGroup: false
}
},
mounted() {
this.initForm()
},
methods: {
async initForm() {
await this.getGroupOptions()
await this.getParentQuestions()
if (Object.keys(this.data).length > 0) {
for (const k in this.form) {
if (this.data.hasOwnProperty(k)) {
this.form[k] = this.data[k]
}
}
if (this.form.ParentId !== '' && this.form.ParentId !== null && this.form.GroupName !== '') {
this.isParentExistGroup = true
}
}
},
save() {
this.$refs.clinicalDataForm.validate(valid => {
if (!valid) return
this.btnLoading = true
addOrUpdateReadingQuestionTrial(this.form).then(res => {
this.btnLoading = false
this.$emit('getList')
this.$emit('close')
this.$message.success(this.$t('common:message:savedSuccessfully'))
})
.catch(() => {
this.btnLoading = false
})
})
},
getGroupOptions() {
return new Promise((resolve, reject) => {
this.loading = true
var param = {
criterionId: this.data.ReadingQuestionCriterionTrialId
}
getTrialGroupNameList(param).then(res => {
this.groupOptions = res.Result
this.loading = false
resolve()
}).catch(() => {
this.loading = false
reject()
})
})
},
getParentQuestions() {
return new Promise((resolve, reject) => {
this.loading = true
var param = {
ReadingQuestionCriterionTrialId: this.data.ReadingQuestionCriterionTrialId,
id: this.data.Id
}
getTrialCriterionOtherQuestion(param).then(res => {
this.parentOptions = res.Result
this.loading = false
resolve()
}).catch(() => {
this.loading = false
reject()
})
})
},
parentQuestionChange(val, form) {
this.isParentExistGroup = false
if (val) {
var index = this.parentOptions.findIndex(item => {
return item.QuestionId === val
})
if (index !== -1) {
this.parentTriggerValOptions = this.parentOptions[index].TypeValue.split('|')
if (this.parentOptions[index].GroupName) {
this.isParentExistGroup = true
form.GroupName = this.parentOptions[index].GroupName
}
}
} else {
form.ParentTriggerValue = ''
form.GroupName = ''
}
},
close() {
this.$emit('close')
}
}
}
</script>
<style lang="scss" scoped>
</style>

View File

@ -0,0 +1,140 @@
<template>
<div v-loading="loading" class="sign-form-wrapper">
<div class="sign-form-body">
<h4 v-if="signText" style="color:red;white-space: pre-line;">* {{ signText }}</h4>
<el-form
ref="signForm"
:model="signForm"
size="small"
label-width="100px"
>
<!-- 用户名 -->
<el-form-item
:label="$t('common:form:sign:userName')"
prop="userName"
:rules="[
{ required: true, message: $t('common:ruleMessage:specify'), trigger: 'blur' }
]"
>
<el-input v-model="signForm.userName" />
</el-form-item>
<!-- 密码 -->
<el-form-item
:label="$t('common:form:sign:password')"
prop="password"
:rules="[
{ required: true, message: $t('common:ruleMessage:specify'), trigger: 'blur' }
]"
>
<el-input v-model="signForm.password" show-password auto-complete="new-password" />
</el-form-item>
</el-form>
</div>
<div slot="footer" class="dialog-footer sign-form-footer">
<el-button :disabled="btnLoading" size="small" type="primary" @click="handleclose">
{{ $t('common:button:cancel') }}
</el-button>
<el-button :loading="btnLoading" :disabled="unsigned" size="small" type="primary" @click="handleVerifySignature">
{{ $t('common:button:sign') }}
</el-button>
</div>
</div>
</template>
<script>
// import { verifySignature } from '@/api/trials'
import md5 from 'js-md5'
// import const_ from '@/const/sign-code'
export default {
name: 'SignForm',
props: {
signCodeEnum: {
type: Number,
required: true
},
subjectVisitId: {
type: String,
default: ''
}
},
data() {
return {
signForm: {
userName: '',
password: ''
},
signText: '',
signCodeId: '',
signCode: '',
btnLoading: false,
loading: false,
unsigned: false
}
},
mounted() {
this.loading = true
this.$store.dispatch('trials/getSignInfo', { signCode: this.signCodeEnum })
.then((res) => {
this.unsigned = false
this.loading = false
this.signText = res.SignText
this.signCode = res.SignCode
this.signCodeId = res.SignCodeId
}).catch(() => {
this.loading = false
this.unsigned = true
})
},
methods: {
handleVerifySignature() {
const currentUser = zzSessionStorage.getItem('userName').toLocaleLowerCase()
this.$refs.signForm.validate((valid) => {
if (!valid) return
if (this.signForm.userName.trim().toLocaleLowerCase() !== currentUser) {
// 用户名输入错误!
this.$alert(this.$t('common:message:signWarning'))
this.loading = false
return
}
this.btnLoading = true
const param = {
UserName: this.signForm.userName,
PassWord: md5(this.signForm.password),
TrialId: this.$route.query.trialId,
SignCode: this.signCode,
SignText: this.signText,
SignCodeId: this.signCodeId,
SubjectVisitId: this.subjectVisitId
}
this.$emit('closeDialog', true, param)
})
},
handleclose() {
this.$emit('closeDialog', false)
}
}
}
</script>
<style lang="scss" scoped>
.sign-form-wrapper{
.sign-form-body{
padding:5px 10px 10px 10px;
// border: 1px solid #e0e0e0;
max-height:650px;
overflow-y: auto;
// >>>.el-form-item__label{
// color: #fff;
// }
}
.sign-form-footer{
padding:10px;
margin-top: 10px;
text-align: right;
}
}
</style>
<style>
.el-dialog__body .sign-form-body h4{
word-break: normal!important;
}
</style>

View File

@ -0,0 +1,449 @@
<template>
<box-content v-loading="loading" style="height: 100%;background: #fff;">
<!-- 搜索框 -->
<div class="search">
<el-form
:inline="true"
class="base-search-form"
>
<!-- 审核问题 -->
<el-form-item
:label="$t('trials:qcCfg:table:questionName')"
>
<el-input
v-model="searchData.QuestionName"
clearable
style="width:120px;"
/>
</el-form-item>
<!-- 类型 -->
<el-form-item
:label="$t('trials:qcCfg:table:type')"
>
<el-select
v-model="searchData.Type"
clearable
>
<el-option
v-for="item of $d.QcType"
:key="item.value"
:value="item.value"
:label="item.label"
/>
</el-select>
</el-form-item>
<el-form-item
:label="$t('trials:qcCfg:table:isEnable')"
>
<el-select
v-model="searchData.IsEnable"
clearable
>
<el-option
v-for="item of $d.IsEnable"
:key="item.value"
:value="item.value"
:label="item.label"
/>
</el-select>
</el-form-item>
<el-form-item
:label="$t('trials:qcCfg:table:isRequired')"
>
<el-select
v-model="searchData.IsRequired"
clearable
>
<el-option
v-for="item of $d.YesOrNo"
:key="`IsRequired${item.value}`"
:value="item.value"
:label="item.label"
/>
</el-select>
</el-form-item>
<el-form-item>
<el-button
type="primary"
icon="el-icon-search"
@click="handleSearch"
>
{{ $t('common:button:search') }}
</el-button>
<el-button
type="primary"
icon="el-icon-refresh-left"
@click="handleReset"
>
{{ $t('common:button:reset') }}
</el-button>
</el-form-item>
</el-form>
<span style="margin-left:auto">
<el-button
:disabled="otherInfo.QuestionCount===0"
type="primary"
icon="el-icon-view"
@click="preview.visible = true"
>
预览
</el-button>
<el-button
v-if="!otherInfo.IsConfirmMedicineQuestion && hasPermi(['trials:trials-panel:setting:medical-audit:confirm'])"
type="primary"
icon="el-icon-plus"
@click="handleAdd"
>
{{ $t('trials:qcCfg:button:default') }}
</el-button>
<el-button
v-if="!otherInfo.IsConfirmMedicineQuestion && hasPermi(['trials:trials-panel:setting:medical-audit:confirm'])"
type="primary"
icon="el-icon-plus"
@click="handleCustomQS"
>
{{ $t('trials:qcCfg:button:custom') }}
</el-button>
<el-button
v-if="!otherInfo.IsConfirmMedicineQuestion && hasPermi(['trials:trials-panel:setting:medical-audit:confirm'])"
:disabled="otherInfo.QuestionCount===0"
type="danger"
icon="el-icon-circle-check"
@click="handleConfirm"
>
{{ $t('trials:qcCfg:button:confirm') }}
</el-button>
</span>
</div>
<el-table
v-adaptive="{bottomOffset:45}"
:data="list"
stripe
height="100"
>
<!-- <el-table-column type="index" width="60" /> -->
<!-- 序号 -->
<el-table-column
prop="ShowOrder"
:label="$t('trials:qcCfg:table:order')"
width="60"
/>
<!-- 审核问题 -->
<el-table-column
prop="QuestionName"
:label="$t('trials:qcCfg:table:questionName')"
show-overflow-tooltip
/>
<!-- 类型 -->
<el-table-column
prop="Type"
:label="$t('trials:qcCfg:table:type')"
show-overflow-tooltip
>
<template slot-scope="scope">
{{ $fd('QcType', scope.row.Type) }}
</template>
</el-table-column>
<!-- 选项 -->
<el-table-column
prop="TypeValue"
:label="$t('trials:qcCfg:table:typeValue')"
show-overflow-tooltip
/>
<!-- 任务类型 -->
<el-table-column
prop="ReadingCategory"
label="任务类型"
show-overflow-tooltip
>
<template slot-scope="scope">
<el-tag v-if="scope.row.ReadingCategory === 1" type="primary">
{{ $fd('ReadingCategory', scope.row.ReadingCategory) }}
</el-tag>
<el-tag v-if="scope.row.ReadingCategory === 2" type="info">
{{ $fd('ReadingCategory', scope.row.ReadingCategory) }}
</el-tag>
<el-tag v-if="scope.row.ReadingCategory === 4" type="danger">
{{ $fd('ReadingCategory', scope.row.ReadingCategory) }}
</el-tag>
<el-tag v-if="scope.row.ReadingCategory === 5" type="warning">
{{ $fd('ReadingCategory', scope.row.ReadingCategory) }}
</el-tag>
</template>
</el-table-column>
<!-- 父问题 -->
<el-table-column
prop="ParentShowOrder"
:label="$t('trials:qcCfg:table:parentQs')"
show-overflow-tooltip
/>
<!-- 父问题触发值 -->
<el-table-column
prop="ParentTriggerValue"
:label="$t('trials:qcCfg:table:parentTriggerValue')"
show-overflow-tooltip
/>
<!-- 是否必填 -->
<el-table-column
prop="IsRequired"
:label="$t('trials:qcCfg:table:isRequired')"
>
<template slot-scope="scope">
{{ $fd('YesOrNo', scope.row.IsRequired) }}
</template>
</el-table-column>
<!-- 启用状态 -->
<el-table-column
prop="IsEnable"
:label="$t('trials:qcCfg:table:isEnable')"
>
<template slot-scope="scope">
{{ $fd('IsEnable', scope.row.IsEnable) }}
</template>
</el-table-column>
<el-table-column
v-if="!otherInfo.IsConfirmMedicineQuestion && hasPermi(['trials:trials-panel:setting:medical-audit:confirm'])"
:label="$t('common:action:action')"
width="150"
>
<template slot-scope="scope">
<el-button
circle
:title="$t('common:button:edit')"
icon="el-icon-edit-outline"
@click="handleEdit(scope.row)"
/>
<el-button
circle
:title="$t('common:button:delete')"
icon="el-icon-delete"
@click="handleDelete(scope.row)"
/>
</template>
</el-table-column>
</el-table>
<!-- 新增默认问题 -->
<el-dialog
v-if="addVisible"
:visible.sync="addVisible"
width="1100px"
:close-on-click-modal="false"
custom-class="base-dialog-wrapper"
:title="$t('trials:qcCfg:button:default')"
>
<div class="base-dialog-body">
<DefaultQS @getList="getList" @close="addVisible = false" />
</div>
</el-dialog>
<!-- 新增/编辑问题 -->
<el-dialog
v-if="qsForm.visible"
:visible.sync="qsForm.visible"
:close-on-click-modal="false"
custom-class="base-dialog-wrapper"
:title="qsForm.title"
width="600px"
>
<QSForm :data="currentRow" @getList="getList" @close="qsForm.visible = false" />
</el-dialog>
<!-- 预览模板 -->
<el-dialog
v-if="preview.visible"
:visible.sync="preview.visible"
:close-on-click-modal="false"
custom-class="base-dialog-wrapper"
:title="preview.title"
width="600px"
>
<div class="base-dialog-body">
<QuestionsPreview />
</div>
</el-dialog>
<!--签名框 -->
<el-dialog
v-if="signVisible"
:visible.sync="signVisible"
:close-on-click-modal="false"
width="600px"
custom-class="base-dialog-wrapper"
>
<div slot="title">
<span
style="font-size:18px;"
>
{{ $t('common:dialogTitle:sign') }}
</span>
<span
style="font-size:12px;margin-left:5px"
>
{{ `(${$t('common:label:sign')}${ currentUser })` }}
</span>
</div>
<SignForm
ref="signForm"
:sign-code-enum="signCode"
@closeDialog="closeSignDialog"
/>
</el-dialog>
</box-content>
</template>
<script>
import { getReadingMedicineTrialQuestionList, deleteReadingMedicineTrialQuestion, confirmReadingMedicineQuestion } from '@/api/trials'
import BoxContent from '@/components/BoxContent'
import DefaultQS from './components/DefaultQS'
import QSForm from './components/QSForm'
import QuestionsPreview from './components/QuestionsPreview'
import SignForm from '@/views/trials/components/newSignForm'
import const_ from '@/const/sign-code'
const searchDataDefault = () => {
return {
QuestionName: '',
Type: '',
IsEnable: null
}
}
export default {
name: 'MedicalAuditQS',
components: { BoxContent, DefaultQS, QSForm, QuestionsPreview, SignForm },
data() {
return {
searchData: searchDataDefault(),
list: [],
total: 0,
loading: false,
addVisible: false,
btnLoading: false,
signText: '',
signCode: null,
currentUser: zzSessionStorage.getItem('userName'),
signVisible: false,
userTypeEnumInt: zzSessionStorage.getItem('userTypeEnumInt') * 1,
otherInfo: {
IsConfirmMedicineQuestion: true
},
trialId: '',
qsForm: { visible: false, title: '' },
preview: { visible: false, title: '预览' },
currentRow: {}
}
},
mounted() {
this.trialId = this.$route.query.trialId
this.getList()
},
methods: {
// 获取qc模板信息
getList() {
this.loading = true
this.searchData.TrialId = this.trialId
getReadingMedicineTrialQuestionList(this.searchData).then(res => {
this.loading = false
this.list = res.Result
this.otherInfo = res.OtherInfo
this.otherInfo.IsConfirmMedicineQuestion = false
}).catch(() => { this.loading = false })
},
// 打开系统模板配置项
handleAdd() {
this.addVisible = true
},
handleEdit(row) {
this.currentRow = { ...row }
this.qsForm.title = '编辑'
this.qsForm.visible = true
},
// 删除qc问题配置项
handleDelete(row) {
// 判断该问题是否存在子问题,如果有则不允许删除
const i = this.list.findIndex(item => item.ParentId === row.Id)
if (i !== -1) {
this.$message.error(this.$t('trials:qcCfg:message:deleteWarning'))
} else {
this.$confirm(this.$t('trials:qcCfg:message:delete'), {
type: 'warning',
distinguishCancelAndClose: true
})
.then(() => {
this.loading = true
var param = {
trialId: this.trialId,
id: row.Id
}
deleteReadingMedicineTrialQuestion(param)
.then(res => {
this.loading = false
if (res.IsSuccess) {
this.getList()
this.$message.success(this.$t('common:message:deletedSuccessfully'))
}
}).catch(() => { this.loading = false })
})
}
},
// 打开自定义qc问题配置框
handleCustomQS() {
this.currentRow = {}
this.qsForm.title = '新增'
this.qsForm.visible = true
},
// 查询
handleSearch() {
this.getList()
},
// 重置
handleReset() {
this.searchData = searchDataDefault()
this.getList()
},
// 确认前签名
handleConfirm() {
if (this.otherInfo.QuestionCount === 0) {
this.$message.error('请先配置医学审核问题再进行确认!')
return
}
const { MedicalAuditConfirmation } = const_.processSignature
this.signCode = MedicalAuditConfirmation
this.signVisible = true
},
// 关闭签名框并设置确认状态
closeSignDialog(isSign, signInfo) {
if (isSign) {
this.signConfirm(signInfo)
} else {
this.signVisible = false
}
},
// 签名确认
signConfirm(signInfo) {
this.loading = true
const params = {
data: {
trialId: this.trialId
},
signInfo: signInfo
}
confirmReadingMedicineQuestion(params).then(res => {
this.loading = false
if (res.IsSuccess) {
this.$message.success(this.$t('common:message:savedSuccessfully'))
this.$refs['signForm'].btnLoading = false
this.signVisible = false
this.getList()
}
}).catch(_ => {
this.loading = false
this.$refs['signForm'].btnLoading = false
})
}
}
}
</script>

View File

@ -0,0 +1,899 @@
<template>
<div
id="canvas"
ref="canvas"
v-loading="loading"
element-loading-text="Loading..."
element-loading-background="rgba(0, 0, 0, 0.8)"
style="position:relative;"
class="cornerstone-element"
>
<div v-show="dicomInfo.series" class="info-series">
<h2 style="color:#f44336;padding: 5px 0px;margin: 0;">{{ stack.taskBlindName }}</h2>
<div>Series #{{ dicomInfo.series }}</div>
<div>Image #{{ dicomInfo.frame }}</div>
<div>{{ dicomInfo.modality }}</div>
</div>
<div v-show="dicomInfo.series" class="info-image">
<div>
P {{ dicomInfo.size }}
<span v-show="dicomInfo.pixel">{{ dicomInfo.pixel }}mm</span>
</div>
<div v-show="dicomInfo.thick">Slice Thickness {{ dicomInfo.thick }}mm</div>
<div>WW/WC {{ dicomInfo.wwwc }}</div>
<div>Zoom {{ dicomInfo.zoom }}</div>
<div v-show="dicomInfo.location">Location {{ dicomInfo.location }}mm</div>
<!-- <div v-show="toolState.clipPlaying">FPS {{ dicomInfo.fps }}</div> -->
</div>
<div class="info-subject">
<!-- <div>{{ dicomInfo.hospital }}</div> -->
<div v-show="dicomInfo.pid">{{ dicomInfo.pid }}</div>
<div>{{ dicomInfo.name }}</div>
<!-- <div>{{ dicomInfo.sex }} {{ dicomInfo.age }}</div> -->
<!-- <div v-show="dicomInfo.acc">ACC {{ dicomInfo.acc }}</div> -->
<div>{{ dicomInfo.time }}</div>
</div>
<!-- <div v-show="stack.firstImageLoading" class="load-indicator">
Loading Series #{{ stack.seriesNumber }}...
</div>-->
</div>
</template>
<script>
import Store from './Store'
import Vue from 'vue'
import Contextmenu from 'vue-contextmenujs'
Vue.use(Contextmenu)
import * as cornerstone from 'cornerstone-core'
import * as cornerstoneMath from 'cornerstone-math'
import * as cornerstoneTools from 'cornerstone-tools'
const scroll = cornerstoneTools.import('util/scrollToIndex')
// const drawTextBox = cornerstoneTools.import('drawing/drawTextBox')
// const { getNewContext } = cornerstoneTools.import('drawing/index')
// const drawLinkedTextBox = cornerstoneTools.import('drawing/drawLinkedTextBox')
import Hammer from 'hammerjs'
import LengthTool from '@/views/trials/trials-panel/reading/dicoms/tools/Length/LengthTool'
import BidirectionalTool from '@/views/trials/trials-panel/reading/dicoms/tools/Bidirectional/BidirectionalTool'
import ArrowAnnotateTool from '@/views/trials/trials-panel/reading/dicoms/tools/ArrowAnnotate/ArrowAnnotateTool'
cornerstoneTools.external.cornerstone = cornerstone
cornerstoneTools.external.Hammer = Hammer
cornerstoneTools.external.cornerstoneMath = cornerstoneMath
cornerstoneTools.toolStyle.setToolWidth(2)
cornerstoneTools.toolColors.setToolColor('rgb(255, 255, 0)')
cornerstoneTools.toolColors.setActiveColor('rgb(0, 255, 0)')
// cornerstoneTools.init({ globalToolSyncEnabled: true })
cornerstoneTools.init()
export default {
name: 'DicomCanvas',
props: {
isCurrentTask: {
type: Boolean,
required: true
},
isActive: {
type: Boolean,
required: true
}
},
data() {
return {
loading: false,
canvas: {},
stack: {
studyId: '',
seriesId: '',
instanceId: '',
seriesNumber: '',
imageIds: [],
currentImageIdIndex: 0,
firstImageLoading: false,
visitTaskId: '',
taskBlindName: ''
// preventCache: true
},
dicomInfo: {
hospital: '',
pid: '',
name: '',
sex: '',
age: '',
acc: '',
modality: '',
time: '',
series: '',
frame: '',
size: '',
pixel: 0,
thick: 0,
wwwc: '',
zoom: 0,
fps: 5
},
toolState: {
initialized: false,
activeTool: 'none',
dicomInfoVisible: false,
clipPlaying: false,
viewportInvert: false
},
loadImagePromise: null,
AnnotationSync: null,
allROIToolData: {},
type: '',
series: '',
ToolStateManager: null,
renderedMeasured: [],
measuredTools: ['Length', 'Bidirectional', 'ArrowAnnotate'],
measuredData: []
}
},
mounted() {
this.type = this.$router.currentRoute.query.type ? this.$router.currentRoute.query.type : ''
this.canvas = this.$refs.canvas
this.canvas.addEventListener('cornerstonenewimage', this.onNewImage)
this.canvas.addEventListener(
'cornerstoneimagerendered',
this.onImageRendered
)
this.canvas.addEventListener(
'cornerstoneimageloaded',
this.onImageLoaded
)
this.canvas.addEventListener(
'cornerstonetoolsclipstopped',
this.onClipStopped
)
// this.canvas.addEventListener(
// 'cornerstonetoolsmeasurementcompleted',
// this.onMeasurementcompleted
// )
// this.canvas.addEventListener(
// 'cornerstonetoolsmeasurementmodified',
// this.onMeasurementmodified
// )
// this.canvas.addEventListener(
// 'cornerstonetoolsmeasurementremoved',
// this.onMeasurementremoved
// )
this.canvas.addEventListener('cornerstonetoolsmouseup', this.mouseUp)
// this.canvas.addEventListener('cornerstonetoolsmouseup', this.mouseDown)
this.canvas.addEventListener('cornerstonetoolsmouseclick', this.mouseClick)
},
methods: {
mouseDown(e) {
return false
},
mouseUp(e) {
console.log('mouseUp')
const { element, currentPoints, image } = e.detail
var imageId = image.imageId
var instanceId = imageId.split('/')[imageId.split('/').length - 1]
const ToolStateManager = cornerstoneTools.globalImageIdSpecificToolStateManager
this.measuredTools.map(toolType => {
const toolState = ToolStateManager.getImageIdToolState(e.detail.image.imageId, toolType)
if (!toolState) return
var toolObj = new cornerstoneTools[`${toolType}Tool`]()
var i = toolState.data.findIndex(data => toolObj.pointNearTool(element, data, currentPoints.canvas, 'mouse'))
if (i > -1) {
const measureData = {}
measureData.studyId = this.stack.studyId
measureData.seriesId = this.stack.seriesId
measureData.instanceId = instanceId
measureData.data = toolState.data[i]
measureData.type = toolType
measureData.thick = this.dicomInfo.thick
var uuid = toolState.data[i].uuid
var idx = this.measuredData.findIndex(item => item.MeasureData && item.MeasureData.data.uuid === uuid)
if (idx > -1) {
var questionInfo = this.measuredData[idx]
this.$emit('modifyMeasureData', { measureData, questionInfo })
} else {
this.$emit('setMeasureData', measureData)
}
cornerstoneTools.setToolPassiveForElement(this.canvas, e.detail.toolName)
}
})
},
mouseClick(e) {
const { element, currentPoints } = e.detail
const ToolStateManager = cornerstoneTools.globalImageIdSpecificToolStateManager
this.measuredTools.map(toolType => {
const toolState = ToolStateManager.getImageIdToolState(e.detail.image.imageId, toolType)
if (!toolState) return
var toolObj = new cornerstoneTools[`${toolType}Tool`]()
// toolState.data.forEach(data => {
// // data.color = ''
// })
var i = toolState.data.findIndex(data => toolObj.pointNearTool(element, data, currentPoints.canvas, 'mouse'))
if (i > -1) {
// 设置鼠标选中颜色rgb(0, 255, 0)
// toolState.data[i].color = ''
var idx = this.measuredData.findIndex(item => item.MeasureData && item.MeasureData.data && item.MeasureData.data.uuid === toolState.data[i].uuid)
if (idx > -1) {
Store.$emit('setCollapseActive', this.measuredData[idx])
}
// toolState.data[i].active = true
// cornerstone.updateImage(element)
}
})
},
loadImageStack(dicomSeries) {
this.series = dicomSeries
this.stack.seriesId = dicomSeries.seriesId
this.stack.studyId = dicomSeries.studyId
this.stack.seriesNumber = dicomSeries.seriesNumber
this.stack.imageIds = dicomSeries.imageIds
this.stack.currentImageIdIndex = dicomSeries.imageIdIndex ? dicomSeries.imageIdIndex : 0
this.stack.firstImageLoading = true
this.stack.visitTaskId = dicomSeries.visitTaskId
this.stack.taskBlindName = dicomSeries.taskBlindName
this.measuredData = dicomSeries.measuredData
const imageId = this.stack.imageIds[this.stack.currentImageIdIndex]
this.stack.instanceId = imageId.split('/')[imageId.split('/').length - 1]
const ToolStateManager = cornerstoneTools.globalImageIdSpecificToolStateManager
ToolStateManager.clearImageIdToolState(dicomSeries.imageIds)
if (this.toolState.clipPlaying) this.toggleClipPlay()
this.toolState.viewportInvert = false
this.toolState.dicomInfoVisible = false
const element = this.$refs.canvas
cornerstone.enable(element)
var scope = this
// cornerstone.imageCache.setMaximumSizeBytes(100000)
// this.loading = true
const loading = this.$loading({ fullscreen: true })
// cornerstone.loadImage(this.stack.imageIds[this.stack.currentImageIdIndex])
cornerstone.loadAndCacheImage(this.stack.imageIds[this.stack.currentImageIdIndex])
.then(image => {
// this.loading = false
loading.close()
scope.onFirstImageLoaded(image)
}).catch((error) => {
// this.loading = false
loading.close()
if (error.error && error.error.message) {
this.$message.error(error.error.message)
}
})
},
onFirstImageLoaded(image) {
const element = this.$refs.canvas
var viewport = cornerstone.getDefaultViewportForImage(this.canvas, image)
cornerstone.displayImage(this.canvas, image, viewport)
if (!this.toolState.initialized) {
this.toolState.initialized = true
const toolButtons = document.querySelectorAll('[data-tool]')
// const scope = this
Array.from(toolButtons).forEach((toolBtn) => {
// Add the tool
const toolName = toolBtn.getAttribute('data-tool')
const apiTool = cornerstoneTools[`${toolName}Tool`]
if (apiTool) {
const toolAlreadyAddedToElement = cornerstoneTools.getToolForElement(
element,
apiTool
)
if (!toolAlreadyAddedToElement) {
if (toolName === 'Length') {
cornerstoneTools.addToolForElement(element, LengthTool)
} else if (toolName === 'Bidirectional') {
cornerstoneTools.addToolForElement(element, BidirectionalTool)
} else if (toolName === 'ArrowAnnotate') {
cornerstoneTools.addToolForElement(element, ArrowAnnotateTool, { configuration: { allowEmptyLabel: true }})
} else {
cornerstoneTools.addToolForElement(element, apiTool)
}
}
}
})
if (
!cornerstoneTools.getToolForElement(
element,
cornerstoneTools.StackScrollMouseWheelTool
)
) {
cornerstoneTools.addToolForElement(
element,
cornerstoneTools.StackScrollMouseWheelTool
)
}
cornerstoneTools.setToolActiveForElement(
element,
'StackScrollMouseWheel',
{}
)
if (
!cornerstoneTools.getToolForElement(
element,
cornerstoneTools.ScaleOverlayTool
)
) {
cornerstoneTools.addToolForElement(
element,
cornerstoneTools.ScaleOverlayTool
)
}
cornerstoneTools.setToolActiveForElement(element, 'ScaleOverlay', {})
if (!cornerstoneTools.getToolForElement(element, cornerstoneTools.OrientationMarkersTool)) {
cornerstoneTools.addToolForElement(
element,
cornerstoneTools.OrientationMarkersTool
)
}
cornerstoneTools.setToolActiveForElement(
element,
'OrientationMarkers',
{}
)
}
cornerstoneTools.addStackStateManager(this.canvas, ['stack', 'stackPrefetch', 'playClip'])
cornerstoneTools.addToolState(this.canvas, 'stack', this.stack)
cornerstoneTools.stackPrefetch.enable(this.canvas)
// cornerstoneTools.setElementToolStateManager(this.toolStateManager)
cornerstone.updateImage(element, true)
// cornerstoneTools.stackPrefetch.setConfiguration({ maxImagesToPrefetch: Infinity,
// preserveExistingPool: true })
// cornerstoneTools.stackPrefetch.enable(this.canvas)
// this.stack.imageIds.forEach((item, index) => {
// if (index === 0) {
// return
// }
// cornerstone.loadImage(item)
// })
this.stack.firstImageLoading = false
this.toolState.dicomInfoVisible = true
this.stack.instanceId = image.imageId.split('/')[image.imageId.split('/').length - 1]
},
onNewImage(e) {
e.detail.enabledElement.options = {}
var data = e.detail.image.data
this.dicomInfo.hospital = data.string('x00080080')
// this.dicomInfo.pid = data.string('x00100020')
this.dicomInfo.pid = data.string('x00120040')
this.dicomInfo.name = data.string('x00100010')
this.dicomInfo.age = data.string('x00101010')
this.dicomInfo.sex = data.string('x00100040')
this.dicomInfo.acc = data.string('x00080050') // 登记号
this.dicomInfo.modality = data.string('x00080060')
this.dicomInfo.time = this.formatDicomDateTime(
data.string('x00080020'),
data.string('x00080030')
)
this.dicomInfo.series = data.string('x00200011')
this.dicomInfo.frame = `${this.stack.currentImageIdIndex + 1}/${
this.stack.imageIds.length
}`
this.dicomInfo.size = `${data.uint16('x00280010')}x${data.uint16(
'x00280011'
)}`
// var pixel = data.floatString('x00280030')
if (this.dicomInfo.pixel) {
this.dicomInfo.pixel = this.dicomInfo.pixel.toFixed(2)
}
this.dicomInfo.thick = data.floatString('x00180050') // 切片厚度
if (this.dicomInfo.thick) {
this.dicomInfo.thick = this.dicomInfo.thick.toFixed(2)
}
},
formatDicomDateTime(date, time) {
if (date) {
date = `${date.substr(0, 4)}-${date.substr(4, 2)}-${date.substr(6, 2)}`
}
if (time) { time = `${time.substr(0, 2)}:${time.substr(2, 2)}:${time.substr(4, 2)}` }
return time ? `${date} ${time}` : `${date} 00:00:00`
},
onImageLoaded(e) {
console.log('onImageLoaded')
},
onImageRendered(e) {
console.log('onImageRendered')
var imageId = e.detail.image.imageId
var instanceId = imageId.split('/')[imageId.split('/').length - 1]
this.stack.instanceId = instanceId
var viewport = e.detail.viewport
this.dicomInfo.wwwc = `${Math.round(
viewport.voi.windowWidth
)}/${Math.round(viewport.voi.windowCenter)}` // 窗位
this.dicomInfo.zoom = viewport.scale.toFixed(2)
var data = e.detail.image.data
const position = data.string('x00200032')
this.dicomInfo.location = position ? Number(
position.split('\\')[position.split('\\').length - 1]
).toFixed(2) : ''
const ToolStateManager = cornerstoneTools.globalImageIdSpecificToolStateManager
this.measuredData.forEach(data => {
if (data.SeriesId === this.stack.seriesId && data.InstanceId === this.stack.instanceId && data.MeasureData) {
const toolState = ToolStateManager.getImageIdToolState(e.detail.image.imageId, data.MeasureData.type)
if (toolState && toolState.data.length > 0) {
var idx = toolState.data.findIndex(item => item.uuid === data.MeasureData.data.uuid)
if (idx === -1) {
ToolStateManager.addImageIdToolState(e.detail.image.imageId, data.MeasureData.type, data.MeasureData.data)
}
} else {
ToolStateManager.addImageIdToolState(e.detail.image.imageId, data.MeasureData.type, data.MeasureData.data)
}
}
})
this.measuredTools.forEach(tool => {
if (!this.isCurrentTask) {
cornerstoneTools.setToolEnabledForElement(this.canvas, tool)
} else {
cornerstoneTools.setToolPassiveForElement(this.canvas, tool)
}
})
},
onMeasurementcompleted(e) {
// 测量完成
console.log('completed')
const { image } = e.detail
var imageId = image.imageId
var instanceId = imageId.split('/')[imageId.split('/').length - 1]
if (e.detail.toolName === 'Length' || e.detail.toolName === 'Bidirectional' || e.detail.toolName === 'ArrowAnnotate') {
const measureData = {}
measureData.studyId = this.stack.studyId
measureData.seriesId = this.stack.seriesId
measureData.instanceId = instanceId
measureData.data = e.detail.measurementData
measureData.type = e.detail.toolName
measureData.thick = this.dicomInfo.thick
this.$emit('setMeasureData', measureData)
cornerstoneTools.setToolPassiveForElement(this.canvas, e.detail.toolName)
}
},
onMeasurementremoved(e) {
if (e.detail.toolName === 'Length' || e.detail.toolName === 'Bidirectional') {
this.$emit('removeMeasureData', e.detail.measurementData.uuid)
}
},
onMeasurementmodified(e) {
// 移动
console.log('modified')
const { image } = e.detail
var imageId = image.imageId
var instanceId = imageId.split('/')[imageId.split('/').length - 1]
if (e.detail.toolName === 'Length' || e.detail.toolName === 'Bidirectional' || e.detail.toolName === 'ArrowAnnotate') {
const measureData = {}
measureData.studyId = this.stack.studyId
measureData.seriesId = this.stack.seriesId
measureData.instanceId = instanceId
measureData.data = e.detail.measurementData
measureData.type = e.detail.toolName
measureData.thick = this.dicomInfo.thick
var uuid = e.detail.measurementData.uuid
var idx = this.measuredData.findIndex(item => item.MeasureData && item.MeasureData.data.uuid === uuid)
var questionInfo = idx > -1 ? this.measuredData[idx] : ''
this.$emit('modifyMeasureData', { measureData, questionInfo })
cornerstoneTools.setToolPassiveForElement(this.canvas, e.detail.toolName)
// this.canvas.removeEventListener('mouseup', this.measurementMouseUpHandler)
}
},
measurementMouseUpHandler(e) {
console.log('measurementMouseUp')
},
updateImage(obj) {
const element = this.$refs.canvas
this.measuredData = obj.measuredData
const ToolStateManager = cornerstoneTools.globalImageIdSpecificToolStateManager
var imageId = `wadouri:/api/instance/content/${obj.instanceId}`
ToolStateManager.clearImageIdToolState(imageId)
cornerstone.updateImage(element, true)
},
removeToolState(measuredData) {
var idx = this.measuredData.findIndex(item => item.MeasureData && item.MeasureData.data.uuid === measuredData.data.uuid)
if (idx > -1) {
this.measuredData.splice(idx, 1)
const element = this.$refs.canvas
const ToolStateManager = cornerstoneTools.globalImageIdSpecificToolStateManager
var imageId = `wadouri:/api/instance/content/${measuredData.instanceId}`
ToolStateManager.clearImageIdToolState(imageId)
cornerstone.updateImage(element, true)
}
},
getToolForElement(toolName) {
var isExist = false
for (var i = 0; i < cornerstoneTools.store.state.tools.length; i++) {
if (cornerstoneTools.store.state.tools[i].name === toolName) {
isExist = true
break
}
}
return isExist
},
onClipStopped() {
this.toolState.clipPlaying = false
},
onKeyPress(e) {
var key = e.detail.keyCode
var keys = { PageUp: 33, PageDown: 34, End: 35, Home: 36 } // Left: 37, Up: 38, Right: 39, Down: 40
if (key < keys.PageUp || key > keys.Home) return
if (key === keys.Home) this.scrollPage(-9999)
else if (key === keys.PageUp) this.scrollPage(-1)
else if (key === keys.PageDown) this.scrollPage(1)
else if (key === keys.End) this.scrollPage(9999)
},
resizeCanvas() {
cornerstone.resize(this.canvas, true)
},
activateCanvas() {
this.canvas.tabIndex = 0
this.canvas.focus()
},
resetViewport() {
this.toolState.viewportInvert = false
var image = cornerstone.getImage(this.canvas)
cornerstone.setViewport(
this.canvas,
cornerstone.getDefaultViewportForImage(this.canvas, image)
)
},
toggleDicomInfo() {
this.toolState.dicomInfoVisible = !this.toolState.dicomInfoVisible
if (this.toolState.dicomInfoVisible) {
cornerstoneTools.orientationMarkers.enable(this.canvas)
} else cornerstoneTools.orientationMarkers.disable(this.canvas)
cornerstone.updateImage(this.canvas)
},
setColormap(colormap) {
const viewport = cornerstone.getViewport(this.canvas)
viewport.colormap = colormap
cornerstone.setViewport(this.canvas, viewport)
cornerstone.updateImage(this.canvas, true)
if (!colormap) {
this.resetRenderCanvase(this.canvas)
}
},
resetRenderCanvase(element) {
const enabledElement = cornerstone.getEnabledElement(element)
enabledElement.renderingTools.colormapId = undefined
enabledElement.renderingTools.colorLut = undefined
const renderCanvas = enabledElement.renderingTools.renderCanvas
const canvasContext = renderCanvas.getContext('2d')
canvasContext.fillStyle = 'white'
canvasContext.fillRect(0, 0, renderCanvas.width, renderCanvas.height)
const renderCanvasData = canvasContext.getImageData(
0,
0,
renderCanvas.width,
renderCanvas.height
)
enabledElement.renderingTools.renderCanvasContext = canvasContext
enabledElement.renderingTools.renderCanvasData = renderCanvasData
},
scrollPage(offset) {
var index = this.stack.currentImageIdIndex + offset
if (index < 0) index = 0
else if (index >= this.stack.imageIds.length) {
index = this.stack.imageIds.length - 1
}
if (index !== this.stack.currentImageIdIndex) {
scroll(this.canvas, index)
}
},
toggleClipPlay() {
if (this.toolState.clipPlaying) {
cornerstoneTools.stopClip(this.canvas)
this.toolState.clipPlaying = false
return
}
this.toolState.clipPlaying = true
cornerstoneTools.playClip(this.canvas, this.dicomInfo.fps)
cornerstoneTools.getToolState(
this.canvas,
'playClip'
).data[0].loop = false
},
setFps(fps) {
this.dicomInfo.fps = fps
},
resetWwwc() {
this.toolState.viewportInvert = false
var viewport = cornerstone.getViewport(this.canvas)
viewport.invert = false
var image = cornerstone.getImage(this.canvas)
viewport.voi.windowWidth = image.windowWidth
viewport.voi.windowCenter = image.windowCenter
cornerstone.setViewport(this.canvas, viewport)
},
setWwwc(ww, wc) {
var viewport = cornerstone.getViewport(this.canvas)
viewport.voi.windowWidth = ww
viewport.voi.windowCenter = wc
cornerstone.setViewport(this.canvas, viewport)
},
toggleInvert() {
this.toolState.viewportInvert = !this.toolState.viewportInvert
var viewport = cornerstone.getViewport(this.canvas)
viewport.invert = this.toolState.viewportInvert
cornerstone.setViewport(this.canvas, viewport)
},
activateZoom() {
cornerstoneTools.addTool(cornerstoneTools.ZoomTool, {
configuration: {
invert: false,
preventZoomOutsideImage: false,
minScale: 0.1,
maxScale: 20.0
}
})
cornerstoneTools.setToolActive('Zoom', { mouseButtonMask: 1 })
this.toolState.activeTool = 'zoom'
},
resetRotate() {
var viewport = cornerstone.getViewport(this.canvas)
viewport.hflip = false
viewport.vflip = false
viewport.rotation = 0
cornerstone.setViewport(this.canvas, viewport)
},
setRotate(hflip, vflip, angle) {
var viewport = cornerstone.getViewport(this.canvas)
if (hflip) viewport.hflip = !viewport.hflip
if (vflip) viewport.vflip = !viewport.vflip
if (angle !== 0) viewport.rotation += angle
cornerstone.setViewport(this.canvas, viewport)
},
saveImage() {
var uid = cornerstone.getImage(this.canvas).data.string('x00080018')
cornerstoneTools.SaveAs(this.canvas, `${uid}.png`)
},
fitToWindow() {
if (this.stack.seriesNumber) {
cornerstone.fitToWindow(this.canvas)
}
},
fitToImage() {
if (this.stack.seriesNumber) {
const enabledElement = cornerstone.getEnabledElement(this.canvas)
enabledElement.viewport.scale = 1
cornerstone.updateImage(this.canvas)
}
},
removeLabel(item) {
const promise = scroll(this.canvas, item.data.imageIdIndex)
const scope = this
Promise.all([promise]).then(res => {
cornerstoneTools.removeToolState(scope.canvas, item.type, item.data)
cornerstone.updateImage(scope.canvas)
})
},
setToolPassive(toolName) {
// 它们的数据可以被操作,但不能创建。
cornerstoneTools.setToolPassiveForElement(this.canvas, toolName)
},
setToolActive(toolName) {
cornerstoneTools.setToolActiveForElement(this.canvas, toolName, {
mouseButtonMask: 1
})
},
setToolEnabled(toolName) {
// 工具将呈现,但不会对输入作出反应
cornerstoneTools.setToolEnabledForElement(this.canvas, toolName)
},
setAllToolsPassive() {
cornerstoneTools.store.state.tools.forEach((tool) => {
cornerstoneTools.setToolPassiveForElement(this.canvas, tool.name)
})
},
addTargetElement(synchronizer) {
synchronizer.addTarget(this.$refs.canvas)
},
removeTarget(synchronizer) {
synchronizer.removeTarget(this.$refs.canvas)
},
addSourceElement(synchronizer) {
synchronizer.addSource(this.$refs.canvas)
},
removeSource(synchronizer) {
synchronizer.removeSource(this.$refs.canvas)
},
activeReferenceLine(synchronizer) {
if (
!cornerstoneTools.getToolForElement(
this.canvas,
cornerstoneTools.ReferenceLinesTool
)
) {
cornerstoneTools.addToolForElement(
this.canvas,
cornerstoneTools.ReferenceLinesTool
)
}
cornerstoneTools.setToolEnabledForElement(this.canvas, 'ReferenceLines', {
synchronizationContext: synchronizer
})
// cornerstoneTools.addTool(cornerstoneTools.CrosshairsTool)
// cornerstoneTools.setToolActive('Crosshairs', {
// mouseButtonMask: 1,
// synchronizationContext: synchronizer
// })
},
disabledReferenceLine(synchronizer) {
synchronizer.enabled = false
synchronizer.remove(this.canvas)
cornerstoneTools.setToolDisabledForElement(this.canvas, 'ReferenceLines')
// cornerstoneTools.setToolDisabledForElement(this.canvas, 'Crosshairs')
},
activeViewPortToolSync(synchronizer, toolName) {
synchronizer.add(this.canvas)
synchronizer.enabled = true
if (
!cornerstoneTools.getToolForElement(
this.canvas,
cornerstoneTools[`${toolName}Tool`]
)
) {
cornerstoneTools.addToolForElement(
this.canvas,
cornerstoneTools[`${toolName}Tool`]
)
}
cornerstoneTools.setToolActiveForElement(this.canvas, toolName, {
mouseButtonMask: 1,
synchronizationContext: synchronizer
})
},
disabledViewPortToolSync(synchronizer, toolName) {
synchronizer.enabled = false
synchronizer.remove(this.canvas)
cornerstoneTools.setToolDisabledForElement(this.canvas, toolName)
},
activeImageSync(synchronizer) {
synchronizer.add(this.$refs.canvas)
synchronizer.enabled = true
return false
},
disabledImageSync(synchronizer) {
synchronizer.remove(this.$refs.canvas)
synchronizer.enabled = false
return false
},
activeAnnotationSync(synchronizer) {
this.AnnotationSync = synchronizer
synchronizer.add(this.$refs.canvas)
synchronizer.enabled = true
},
disabledAnnotationSync(synchronizer) {
this.AnnotationSync = null
synchronizer.enabled = false
synchronizer.remove(this.$refs.canvas)
this.setAllToolsPassive()
},
getToolSate() {
const toolROITypes = [
'Probe',
'EllipticalRoi',
'RectangleRoi',
'ArrowAnnotate',
'Length',
'CobbAngle',
'Angle',
'Bidirectional',
'FreehandRoi'
]
for (let i = 0; i < toolROITypes.length; i++) {
const toolROIType = toolROITypes[i]
const toolROIData = JSON.stringify(
cornerstoneTools.getToolState(this.canvas, toolROIType)
)
if (toolROIData !== undefined) {
this.allROIToolData[toolROITypes[i]] = JSON.parse(toolROIData)
}
}
}
}
}
</script>
<style scoped>
.info-series {
position: absolute;
left: 10px;
top: 10px;
text-align: left;
color: #ddd;
font-size: 12px;
/* z-index: 1; */
}
.info-image {
position: absolute;
left: 10px;
bottom: 10px;
text-align: left;
color: #ddd;
font-size: 12px;
/* z-index: 1; */
}
.info-subject {
position: absolute;
right: 10px;
top: 10px;
text-align: right;
color: #ddd;
font-size: 12px;
/* z-index: 1; */
}
.load-indicator {
position: absolute;
left: 10px;
top: 10px;
text-align: left;
/* z-index: 1; */
}
.magnifyTool {
border: 2px solid #ffffff;
border-radius: 50%;
display: none;
cursor: none;
}
.menu__item {
display: block;
line-height: 20px;
text-align: center;
margin: 10px;
cursor: default;
}
.menu__item:hover {
color: #ff0000;
}
.menu {
height: auto;
width: auto;
position: absolute;
font-size: 14px;
text-align: left;
border-radius: 10px;
border: 1px solid #c21111;
background-color: #ffffff;
}
li:hover {
background-color: #e0e0e2;
color: white;
}
</style>

View File

@ -0,0 +1,953 @@
<template>
<div class="measurement-wrapper" :style="{'height':height+10+'px'}">
<div class="container" :style="{'height':height+'px'}">
<h3 style="color: #ddd;padding: 5px 0px;margin: 0;">
{{ taskBlindName }}
</h3>
<!-- 非测量问题 -->
<div class="lesions">
<Questions ref="ecrf" />
</div>
<!-- 测量问题 -->
<template v-if="questions.length > 0 && CriterionType !== 10">
<div v-for="(qs,index) in questions" :key="index" v-loading="loading" class="lesions lesions_wrapper">
<h4 v-if="qs.Type === 'group'" style="color: #ddd;padding: 5px 0px;margin: 0;">
{{ qs.GroupName }}
</h4>
<div class="lesion_list">
<div v-for="item in qs.Childrens" v-show="!(isBaseLineTask && item.LesionType === 2)" :key="item.Id">
<div v-if="item.Type === 'table'" class="flex-row">
<div class="title">{{ item.QuestionName }}</div>
<div v-if="readingTaskState<2 && (isBaseLineTask || item.LesionType === 2)" class="add-icon" @click.prevent="handleAdd(item)">
<i class="el-icon-plus" />
</div>
</div>
<!-- @change="handleCollapseChange(qs.Childrens,item)" -->
<el-collapse
v-if="item.Type === 'table' && item.TableQuestions"
v-model="activeName"
accordion
@change="handleCollapseChange"
>
<el-collapse-item
v-for="(q,i) in item.TableQuestions.Answers"
:key="i"
:name="`${item.Id}_${q.RowIndex}`"
>
<template slot="title">
<div style="width:300px;position: relative;" :style="{color:(activeName===item.Id+q.RowIndex?'#ffeb3b':'#fff')}">
<el-tooltip v-show="readingTaskState<2 && !!item.TableQuestions.Answers[i].RowId && !isBaseLineTask && item.TableQuestions.Answers[i].isDicomReading !== false" class="item" content="分裂" placement="left">
<i class="el-icon-plus" style="color:#fff;font-size: 16px;" @click.stop="handleSplit(item.TableQuestions.Answers[i].RowId,item.Id)" />
</el-tooltip>
{{ getLesionName(item.OrderMark,q.RowIndex) }}
<el-tooltip v-if="readingTaskState<2 && parseInt(item.TableQuestions.Answers[i].saveTypeEnum) === 0" class="item" effect="dark" content="未保存" placement="bottom">
<i class="el-icon-warning" style="color:red" />
</el-tooltip>
<el-tooltip v-if="readingTaskState<2 && parseInt(item.TableQuestions.Answers[i].saveTypeEnum) ===1" class="item" effect="dark" content="信息不完整" placement="bottom">
<i class="el-icon-warning" style="color:#ff9800" />
</el-tooltip>
<div style="position: absolute;left: 60px;top: 2px;">
<div style="display: flex;flex-direction: row;font-size: 10px;width:200px;">
<div v-if="item.TableQuestions.Answers[i].lesionPart" style="margin-left:10px;margin-bottom:5px;">
{{ item.TableQuestions.Answers[i].lesionPart }}
</div>
<div v-if="item.TableQuestions.Answers[i].lesionState" style="margin-left:10px;margin-bottom:5px;">
<span v-if="item.LesionType === 0">
{{ $fd('TargetState',parseInt(item.TableQuestions.Answers[i].lesionState)) }}
</span>
<span v-else-if="item.LesionType === 1">
{{ $fd('NoTargetState',parseInt(item.TableQuestions.Answers[i].lesionState)) }}
</span>
<span v-else>
{{ $fd('NewLesionState',parseInt(item.TableQuestions.Answers[i].lesionState)) }}
</span>
</div>
<div style="margin-left:10px;margin-bottom:5px;">
<template v-if="item.TableQuestions.Answers[i].isLymphLesion && item.TableQuestions.Answers[i].lesionShort">
{{ item.TableQuestions.Answers[i].lesionShort }}mm
</template>
<template v-else-if="!item.TableQuestions.Answers[i].isLymphLesion && item.TableQuestions.Answers[i].lesionLength">
{{ item.TableQuestions.Answers[i].lesionLength }}mm
</template>
<!-- <span v-if="item.TableQuestions.Answers[i].lesionLength && item.TableQuestions.Answers[i].lesionShort">
{{ item.TableQuestions.Answers[i].lesionShort }}mm
</span>
<span v-else>
{{ item.TableQuestions.Answers[i].lesionLength? `${item.TableQuestions.Answers[i].lesionLength}mm`:'' }}
</span> -->
</div>
</div>
</div>
</div>
</template>
<MeasurementForm
:ref="`${item.Id}_${q.RowIndex}`"
:questions="item.TableQuestions.Questions"
:answers="item.TableQuestions.Answers[i]"
:lesion-type="item.LesionType"
:order-mark="item.OrderMark"
:table-questions="tableQuestions"
:row-index="String(q.RowIndex)"
:question-name="item.QuestionName"
:parent-qs-id="item.Id"
:visit-task-id="visitTaskId"
:is-current-task="isCurrentTask"
:reading-task-state="readingTaskState"
:is-base-line-task="isBaseLineTask"
@getReadingQuestionAndAnswer="getReadingQuestionAndAnswer"
@determineExistsUnsavedLession="determineExistsUnsavedLession"
@resetQuestions="resetQuestions"
@close="close"
/>
</el-collapse-item>
</el-collapse>
</div>
</div>
</div>
</template>
<template v-if="questions.length > 0 && CriterionType === 10">
<PCWG3Questions ref="pcwg3" :questions="questions" :reading-task-state-parent="readingTaskState" />
</template>
</div>
</div>
</template>
<script>
import { splitLesion } from '@/api/trials'
import DicomEvent from './DicomEvent'
import PCWG3Questions from './questions/PCWG3Questions'
import store from '@/store'
import { mapGetters } from 'vuex'
import Questions from './Questions'
import MeasurementForm from './MeasurementForm'
export default {
name: 'MeasurementList',
components: {
Questions,
MeasurementForm,
PCWG3Questions
},
data() {
return {
height: window.innerHeight - 140,
questions: [],
activeName: '',
activeItem: {
activeRowIndex: null,
activeCollapseId: null
},
visitTaskId: '',
isCurrentTask: false,
loading: false,
unSaveTargets: [],
temporaryLesions: [],
readingTaskState: 2,
isBaseLineTask: false,
taskBlindName: '',
tableQuestions: [],
isFirstRender: false,
CriterionType: null
}
},
computed: {
...mapGetters(['visitTaskList'])
},
mounted() {
window.addEventListener('message', this.receiveMsg)
this.CriterionType = parseInt(this.$route.query.CriterionType)
DicomEvent.$on('loadMeasurementList', async obj => {
if (this.visitTaskId !== obj.visitTaskId) {
this.visitTaskId = obj.visitTaskId
this.taskBlindName = obj.taskBlindName
this.activeName = ''
this.activeItem.activeRowIndex = null
this.activeItem.activeCollapseId = null
this.$refs['ecrf'].getQuestions(obj.visitTaskId)
}
var idx = this.visitTaskList.findIndex(i => i.VisitTaskId === obj.visitTaskId)
if (idx > -1) {
if (this.visitTaskList[idx].ReadingQuestions.length > 0) {
this.questions = this.getQuestions(this.visitTaskList[idx].ReadingQuestions)
}
this.readingTaskState = this.visitTaskList[idx].ReadingTaskState
this.isBaseLineTask = this.visitTaskList[idx].IsBaseLineTask
this.isCurrentTask = this.visitTaskList[idx].IsCurrentTask
this.getTableQuestions()
if (this.isFirstRender) {
this.$nextTick(() => {
this.tableQuestions.forEach(item => {
item.TableQuestions.Answers.forEach(i => {
var refName = `${item.Id}_${i.RowIndex}`
this.$refs[refName][0].initForm()
})
})
})
}
}
if (!this.isFirstRender) {
this.isFirstRender = true
}
console.log('loadMeasurementList')
})
DicomEvent.$on('setCollapseActive', measureData => {
this.setCollapseActive(measureData)
console.log('setCollapseActive')
})
DicomEvent.$on('changeLesionType', (questionsObj) => {
this.changeLesionType(questionsObj)
console.log('changeLesionType')
})
DicomEvent.$on('getAllUnSaveLesions', (callback) => {
var list = this.getAllUnSaveLesions()
callback(list)
console.log('getAllUnSaveLesions')
})
DicomEvent.$on('split', measureData => {
this.handleSplit(measureData.RowId, measureData.QuestionId)
})
DicomEvent.$on('setReadingState', readingTaskState => {
this.readingTaskState = readingTaskState
})
window.addEventListener('resize', this.setHeight)
},
beforeDestroy() {
DicomEvent.$off('loadMeasurementList')
DicomEvent.$off('setCollapseActive')
DicomEvent.$off('changeLesionType')
DicomEvent.$off('getUnSaveTarget')
DicomEvent.$off('setReadingState')
},
methods: {
initPage(obj) {
if (this.visitTaskId !== obj.visitTaskId) {
this.visitTaskId = obj.visitTaskId
this.taskBlindName = obj.taskBlindName
this.activeName = ''
this.activeItem.activeRowIndex = null
this.activeItem.activeCollapseId = null
this.$refs['ecrf'].getQuestions(obj.visitTaskId)
}
var idx = this.visitTaskList.findIndex(i => i.VisitTaskId === obj.visitTaskId)
if (idx > -1) {
if (this.visitTaskList[idx].ReadingQuestions.length > 0) {
this.questions = this.visitTaskList[idx].ReadingQuestions
}
this.readingTaskState = this.visitTaskList[idx].ReadingTaskState
this.isBaseLineTask = this.visitTaskList[idx].IsBaseLineTask
this.isCurrentTask = this.visitTaskList[idx].IsCurrentTask
this.getTableQuestions()
if (this.isFirstRender) {
this.$nextTick(() => {
this.tableQuestions.forEach(item => {
item.TableQuestions.Answers.forEach(i => {
var refName = `${item.Id}_${i.RowIndex}`
this.$refs[refName][0].initForm()
})
})
})
}
}
if (!this.isFirstRender) {
this.isFirstRender = true
}
if (this.CriterionType === 10) {
this.$nextTick(() => {
console.log(this.$refs)
console.log(this.$refs.pcwg3)
console.log(this.$refs.pcwg3.initPage(obj))
})
}
},
async resetQuestions(obj) {
this.setQuestions(this.questions, obj)
console.log(this.questions)
await store.dispatch('reading/setReadingQuestionAndAnswer', { questions: this.questions, visitTaskId: this.visitTaskId })
this.getTableQuestions()
},
setQuestions(questions, obj) {
questions.forEach(item => {
if (item.Type === 'table' && item.Id === obj.questionId) {
var idx = item.TableQuestions.Answers.findIndex(i => i.RowIndex === obj.rowIndex)
item.TableQuestions.Answers[idx].isLymphLesion = obj.isLymphLesion
item.TableQuestions.Answers[idx].loctation = obj.lesionOrgan
item.TableQuestions.Answers[idx].lesionPart = obj.lesionPart
item.TableQuestions.Answers[idx].lesionLength = obj.lesionLength
item.TableQuestions.Answers[idx].lesionShort = obj.lesionShort
item.TableQuestions.Answers[idx].lesionState = obj.lesionState
item.TableQuestions.Answers[idx].saveTypeEnum = obj.saveTypeEnum
for (const i in obj.anwsers) {
if (i === 'MeasureData' && obj.anwsers[i]) {
item.TableQuestions.Answers[idx].InstanceId = obj.anwsers[i].instanceId
item.TableQuestions.Answers[idx].SeriesId = obj.anwsers[i].seriesId
item.TableQuestions.Answers[idx][i] = JSON.stringify(obj.anwsers[i])
item.TableQuestions.Answers[idx].isDicomReading = !(obj.anwsers[i].IsDicomReading === false || obj.anwsers[i].IsDicomReading === 'False')
} else {
item.TableQuestions.Answers[idx][i] = String(obj.anwsers[i])
}
}
}
if (item.Childrens.length > 0) {
this.setQuestions(item.Childrens, obj)
}
})
},
getQuestions(questions) {
questions.forEach(item => {
if (item.Type === 'table' && item.TableQuestions && item.TableQuestions.Answers.length > 0) {
item.TableQuestions.Answers.forEach(answerObj => {
this.$set(answerObj, 'loctation', this.getQuestionAnswer(item.TableQuestions.Questions, 6, answerObj))
this.$set(answerObj, 'lesionLength', this.getQuestionAnswer(item.TableQuestions.Questions, 0, answerObj))
this.$set(answerObj, 'lesionShort', this.getQuestionAnswer(item.TableQuestions.Questions, 1, answerObj))
var isLymphLesion = this.getQuestionAnswer(item.TableQuestions.Questions, 2, answerObj)
isLymphLesion = isLymphLesion ? parseInt(isLymphLesion) : null
this.$set(answerObj, 'isLymphLesion', isLymphLesion)
var lesionState = this.getQuestionAnswer(item.TableQuestions.Questions, 7, answerObj)
lesionState = lesionState ? parseInt(lesionState) : ''
this.$set(answerObj, 'lesionState', lesionState)
if (answerObj.RowId) {
this.$set(answerObj, 'saveTypeEnum', isNaN(parseInt(lesionState)) ? 1 : 2)
} else {
this.$set(answerObj, 'saveTypeEnum', 0)
}
})
}
if (item.Childrens.length > 0) {
this.getQuestions(item.Childrens)
}
})
return questions
},
getTableQuestions() {
this.tableQuestions = []
this.questions.map(item => {
if (item.Type === 'table') {
this.tableQuestions.push(item)
}
if (item.Childrens.length > 0) {
this.getTableQuestionsChild(item.Childrens)
}
})
},
getTableQuestionsChild(obj) {
obj.map(item => {
if (item.Type === 'table') {
this.tableQuestions.push(item)
}
if (item.Childrens.length > 0) {
this.getTableQuestionsChild(item.Childrens)
}
})
},
refreshReadingQuestionAndAnswer(type) {
if (type === 0) {
// 删除
this.activeName = ''
this.activeItem.activeRowIndex = null
this.activeItem.activeCollapseId = null
}
this.getReadingQuestionAndAnswer(this.visitTaskId)
},
getReadingQuestionAndAnswer() {
return new Promise(async resolve => {
const loading = this.$loading({ fullscreen: true })
await store.dispatch('reading/refreshReadingQuestionAndAnswer', { trialId: this.$router.currentRoute.query.trialId, visitTaskId: this.visitTaskId }).then(() => {
var idx = this.visitTaskList.findIndex(i => i.VisitTaskId === this.visitTaskId)
if (idx > -1) {
if (this.visitTaskList[idx].ReadingQuestions.length > 0) {
this.questions = this.visitTaskList[idx].ReadingQuestions
}
this.readingTaskState = this.visitTaskList[idx].ReadingTaskState
this.isBaseLineTask = this.visitTaskList[idx].IsBaseLineTask
this.isCurrentTask = this.visitTaskList[idx].IsCurrentTask
}
this.getTableQuestions()
this.$nextTick(() => {
this.tableQuestions.forEach(item => {
item.TableQuestions.Answers.forEach(i => {
var refName = `${item.Id}_${i.RowIndex}`
this.$refs[refName][0].initForm()
})
})
})
})
await store.dispatch('reading/refreshMeasuredData', this.visitTaskId)
DicomEvent.$emit('getMeasureData')
loading.close()
resolve()
})
},
setHeight() {
this.height = window.innerHeight - 140
},
getQuestionAnswer(questions, questionMark, answers) {
var idx = questions.findIndex(i => i.QuestionMark === questionMark)
if (idx > -1) {
var questionId = questions[idx].Id
return answers[questionId]
} else {
return ''
}
},
// 分裂
handleSplit(rowId, questionId) {
var arr = []
this.getUnSaveTarget()
if (this.unSaveTargets.length > 0) {
this.unSaveTargets.forEach(item => {
arr.push(item.lessionName)
})
}
var unSavedList = this.getAllUnSaveLesions()
unSavedList.forEach(item => {
arr.push(item.lessionName)
})
if (arr.length > 0) {
var message = arr.join('、')
this.$confirm(`病灶${message},未保存。如果分裂,信息将被还原,是否继续?`, {
type: 'warning',
distinguishCancelAndClose: true
})
.then(() => {
this.split(rowId, questionId)
})
.catch(() => {})
} else {
this.$confirm('是否确认分裂?', {
type: 'warning',
distinguishCancelAndClose: true
})
.then(() => {
this.split(rowId, questionId)
})
.catch(() => {})
}
},
split(rowId, questionId) {
const loading = this.$loading({ fullscreen: true })
var params = {
visitTaskId: this.visitTaskId,
questionId: questionId,
rowId: rowId
}
splitLesion(params).then(async res => {
loading.close()
await this.getReadingQuestionAndAnswer()
this.$nextTick(() => {
this.tableQuestions.forEach(item => {
item.TableQuestions.Answers.forEach(i => {
var refName = `${item.Id}_${i.RowIndex}`
this.$refs[refName][0].initForm()
})
})
})
})
},
isCanActiveTool(toolName) {
if (this.CriterionType === 10) {
return this.$refs['pcwg3'].isCanActiveTool(toolName)
} else {
this.getUnSaveTarget()
if (this.unSaveTargets.length > 0) {
var rowIndex = String(this.unSaveTargets[0].rowIndex)
var questionId = this.unSaveTargets[0].questionId
const refName = `${this.activeItem.activeCollapseId}_${this.activeItem.activeRowIndex}`
if (rowIndex === this.activeItem.activeRowIndex && questionId === this.activeItem.activeCollapseId && !this.$refs[refName][0].questionForm.MeasureData) {
return this.checkToolCanActive(toolName)
} else {
return { isCanActiveTool: false, reason: `病灶${this.unSaveTargets[0].lessionName}信息未保存,不允许继续添加!` }
}
} else {
if (this.activeItem.activeCollapseId) {
return this.checkToolCanActive(toolName)
} else {
if (toolName === 'ArrowAnnotate' || toolName === 'Bidirectional' || toolName === 'Length') {
return { isCanActiveTool: true, reason: '' }
} else {
return { isCanActiveTool: false, reason: '' }
}
}
}
}
},
checkToolCanActive(toolName) {
const refName = `${this.activeItem.activeCollapseId}_${this.activeItem.activeRowIndex}`
var isLymphNodes = this.$refs[refName][0].getQuestionVal(2)
isLymphNodes = !isNaN(parseInt(isLymphNodes)) ? parseInt(isLymphNodes) : null
if (!this.$refs[refName][0].questionForm.MeasureData) {
if (this.isBaseLineTask) {
if (this.$refs[refName][0].lesionType === 0 && isLymphNodes === 1 && toolName !== 'Bidirectional') {
return { isCanActiveTool: false, reason: '淋巴结靶病灶需使用长短径测量工具' }
} else if (this.$refs[refName][0].lesionType === 0 && isLymphNodes === 0 && toolName !== 'Length') {
return { isCanActiveTool: false, reason: '非淋巴结靶病灶需使用直径测量工具' }
} else if (this.$refs[refName][0].lesionType === 1 && isLymphNodes === 1 && toolName !== 'Bidirectional' && toolName !== 'ArrowAnnotate') {
return { isCanActiveTool: false, reason: '淋巴结非靶病灶需使用长短径测量工具或箭头工具' }
} else if (this.$refs[refName][0].lesionType === 1 && isLymphNodes === 0 && toolName !== 'Length' && toolName !== 'ArrowAnnotate') {
return { isCanActiveTool: false, reason: '非淋巴结非靶病灶需使用直径测量工具或箭头工具' }
} else {
return { isCanActiveTool: true, reason: '' }
}
} else {
if (this.$refs[refName][0].lesionType === 0 && isLymphNodes === 1 && toolName !== 'Bidirectional' && toolName !== 'ArrowAnnotate') {
return { isCanActiveTool: false, reason: '淋巴结靶病灶需使用长短径测量工具或箭头工具' }
} else if (this.$refs[refName][0].lesionType === 0 && isLymphNodes === 0 && toolName !== 'Length' && toolName !== 'ArrowAnnotate') {
return { isCanActiveTool: false, reason: '淋巴结非靶病灶需使用直径测量工具或箭头工具' }
} else if (this.$refs[refName][0].lesionType === 1 && isLymphNodes === 1 && toolName !== 'Bidirectional' && toolName !== 'ArrowAnnotate') {
return { isCanActiveTool: false, reason: '淋巴结非靶病灶需使用长短径测量工具或箭头工具' }
} else if (this.$refs[refName][0].lesionType === 1 && isLymphNodes === 0 && toolName !== 'Length' && toolName !== 'ArrowAnnotate') {
return { isCanActiveTool: false, reason: '非淋巴结非靶病灶需使用直径测量工具或箭头工具' }
} else if (this.$refs[refName][0].lesionType === 2 && isLymphNodes === 1 && toolName !== 'Bidirectional' && toolName !== 'ArrowAnnotate') {
return { isCanActiveTool: false, reason: '淋巴结新病灶需使用长短径测量工具或箭头工具' }
} else if (this.$refs[refName][0].lesionType === 2 && isLymphNodes === 0 && toolName !== 'Length' && toolName !== 'ArrowAnnotate') {
return { isCanActiveTool: false, reason: '非淋巴结新病灶需使用直径测量工具或箭头工具' }
} else {
return { isCanActiveTool: true, reason: '' }
}
}
} else {
return { isCanActiveTool: true, reason: '' }
}
},
async handleAdd(qs) {
// 判断是否有未保存的病灶
this.getUnSaveTarget()
if (this.unSaveTargets.length > 0) {
this.$confirm(`病灶${this.unSaveTargets[0].lessionName}信息未保存,不允许继续添加!`, {
type: 'warning',
showCancelButton: false,
callback: action => {}
})
return
}
if (!!qs.MaxQuestionCount && qs.MaxQuestionCount <= qs.TableQuestions.Answers.length) {
this.$confirm(`${qs.QuestionName}个数最多不能超过${qs.MaxQuestionCount}个`, {
type: 'warning',
showCancelButton: false,
callback: action => {}
})
} else {
// saveTypeEnum: 0
var obj = { saveTypeEnum: 0 }
var questions = qs.TableQuestions.Questions.find(item => item.QuestionMark === 3)
if (questions) {
var maxIndex = this.getMaxRowIndex(qs.TableQuestions.Answers)
obj.RowIndex = `${maxIndex + 1}.00`
obj[questions.Id] = `${qs.OrderMark}${String(maxIndex + 1).padStart(2, '0')}`
}
obj.IsDicomReading = true
await store.dispatch('reading/addReadingQuestionAndAnswer', { lesionType: qs.LesionType, visitTaskId: this.visitTaskId, lesionObj: obj })
this.activeItem.activeRowIndex = String(obj.RowIndex)
this.activeItem.activeCollapseId = qs.Id
this.activeName = `${this.activeItem.activeCollapseId}_${this.activeItem.activeRowIndex}`
}
},
getMaxRowIndex(answerList) {
if (answerList.length > 0) {
var arr = []
answerList.forEach(item => {
var rowIndex = item.RowIndex
arr.push(parseInt(rowIndex))
})
var max = Math.max.apply(null, arr)
return max
} else {
return 0
}
},
determineExistsUnsavedLession(callback) {
// const loading = this.$loading({ fullscreen: true })
this.getUnSaveTarget()
if (this.unSaveTargets.length > 0) {
var rowIndex = String(this.unSaveTargets[0].rowIndex)
var questionId = this.unSaveTargets[0].questionId
if (rowIndex === String(this.activeItem.activeRowIndex) && questionId === this.activeItem.activeCollapseId) {
callback(true)
// loading.close()
return
} else {
this.$confirm(`病灶${this.unSaveTargets[0].lessionName}信息未保存,不允许执行此操作!`, {
type: 'warning',
showCancelButton: false,
callback: action => { }
})
callback(false)
// loading.close()
return
}
}
callback(true)
// loading.close()
return
},
getUnSaveTarget() {
this.unSaveTargets = []
this.tableQuestions.map(item => {
if (item.TableQuestions && item.TableQuestions.Answers) {
item.TableQuestions.Answers.map(t => {
if (!t.RowId) {
var lessionName = this.getLesionName(item.OrderMark, t.RowIndex)
this.unSaveTargets.push({ lessionName: lessionName, rowIndex: t.RowIndex, questionId: item.Id })
}
})
}
})
// console.log(this.unSaveTargets)
return this.unSaveTargets
},
getAllUnSaveLesions() {
var arr = []
this.tableQuestions.map(item => {
if (item.TableQuestions && item.TableQuestions.Answers) {
item.TableQuestions.Answers.map(t => {
const refName = `${item.Id}_${t.RowIndex}`
if (this.$refs[refName] && this.$refs[refName][0] && this.$refs[refName][0].questionForm.saveTypeEnum !== 2) {
var lessionName = this.getLesionName(item.OrderMark, t.RowIndex)
arr.push({ lessionName: lessionName, rowIndex: t.RowIndex, questionId: item.Id })
}
})
}
})
return arr
},
getLesionName(orderMark, rowIndex) {
var lessionName = ''
var rowIndexArr = rowIndex.split('.')
var x = parseInt(rowIndexArr[0])
var y = parseInt(rowIndexArr[1])
if (y > 0) {
y = String.fromCharCode(parseInt(rowIndexArr[1]) - 1 + 65 + 32)
lessionName = `${orderMark}${String(x).padStart(2, '0')}${y}`
} else {
lessionName = `${orderMark}${String(x).padStart(2, '0')}`
}
return lessionName
},
handleCollapseChange(val) {
if (this.activeName) {
var arr = this.activeName.split('_')
this.activeItem.activeRowIndex = arr[1]
this.activeItem.activeCollapseId = arr[0]
this.$nextTick(() => {
const refName = `${this.activeItem.activeCollapseId}_${this.activeItem.activeRowIndex}`
if (this.$refs[refName][0].questionForm.IsDicomReading !== false) {
DicomEvent.$emit('imageLocation', { questionId: this.activeItem.activeCollapseId, rowIndex: this.activeItem.activeRowIndex, visitTaskId: this.visitTaskId })
}
})
} else {
this.activeItem.activeRowIndex = null
this.activeItem.activeCollapseId = null
}
},
// 更改病灶类型
async changeLesionType(questionsObj) {
await store.dispatch('reading/removeReadingQuestionAndAnswer', { lesionType: questionsObj.oldLesionType, rowIndex: questionsObj.rowIndex, visitTaskId: this.visitTaskId })
// saveTypeEnum: 0
var lesionObj = { }
var questionObj = questionsObj.questionForm
// 切换到新的病灶集合
var targetObj = this.tableQuestions.find(item => item.LesionType === questionsObj.newLesionType)
var lesionMark = ''
targetObj.TableQuestions.Questions.forEach(i => {
if (i.QuestionMark === 3) {
var maxIndex = this.getMaxRowIndex(targetObj.TableQuestions.Answers)
lesionObj.RowIndex = `${maxIndex + 1}.00`
lesionObj[i.Id] = `${targetObj.OrderMark}${String(maxIndex + 1).padStart(2, '0')}`
lesionMark = lesionObj[i.Id]
} else {
var obj = questionObj.find(v => v.QuestionMark === i.QuestionMark)
if (obj) {
lesionObj[i.Id] = obj.Answer
}
}
})
if (questionsObj.measureData) {
await store.dispatch('reading/removeMeasuredData', { visitTaskId: this.visitTaskId, measureData: questionsObj.measureData, questionId: questionsObj.questionId, rowIndex: questionsObj.rowIndex })
questionsObj.measureData.data.remark = lesionMark
lesionObj.MeasureData = JSON.stringify(questionsObj.measureData)
}
await store.dispatch('reading/addReadingQuestionAndAnswer', { lesionType: questionsObj.newLesionType, visitTaskId: this.visitTaskId, lesionObj: lesionObj })
this.activeItem.activeRowIndex = String(lesionObj.RowIndex)
this.activeItem.activeCollapseId = targetObj.Id
this.activeName = `${this.activeItem.activeCollapseId}_${this.activeItem.activeRowIndex}`
const refName = `${this.activeItem.activeCollapseId}_${this.activeItem.activeRowIndex}`
this.$nextTick(() => {
this.$refs[refName][0].setMeasureData(questionsObj.measureData)
})
},
removeQuestionAnswer(questionsObj) {
// 移除当前病灶
var currentObj = this.tableQuestions.find(item => item.LesionType === questionsObj.oldLesionType)
if (currentObj) {
var i = currentObj.TableQuestions.Answers.findIndex(i => String(i.RowIndex) === String(questionsObj.rowIndex))
if (i === -1) return
currentObj.TableQuestions.Answers.splice(i, 1)
}
},
// 创建新病灶
createNLTarget(measureData) {
var idx = this.tableQuestions.findIndex(item => item.LesionType === 2)
if (idx > -1) {
this.addTarget(this.tableQuestions[idx], measureData)
}
},
// 创建靶病灶
createTTarget(measureData) {
var idx = this.tableQuestions.findIndex(item => item.LesionType === 0)
if (idx > -1) {
this.addTarget(this.tableQuestions[idx], measureData)
}
},
async addTarget(target, measureData) {
// saveTypeEnum: 0
var obj = {}
// obj.RowIndex = target.TableQuestions.Answers.length + 1
var questions = target.TableQuestions.Questions.find(item => item.QuestionMark === 3)
if (questions) {
var maxIndex = this.getMaxRowIndex(target.TableQuestions.Answers)
obj.RowIndex = `${maxIndex + 1}.00`
obj[questions.Id] = `${target.OrderMark}${String(maxIndex + 1).padStart(2, '0')}`
// obj[questions.Id] = `${target.OrderMark}${String(obj.RowIndex).padStart(2, '0')}`
}
// target.TableQuestions.Answers.push(obj)
obj.IsDicomReading = !(measureData.isDicomReading === false || measureData.isDicomReading === 'False')
obj.measureObj = measureData
await store.dispatch('reading/addReadingQuestionAndAnswer', { lesionType: target.LesionType, visitTaskId: this.visitTaskId, lesionObj: obj })
this.activeItem.activeRowIndex = String(obj.RowIndex)
this.activeItem.activeCollapseId = target.Id
this.activeName = `${this.activeItem.activeCollapseId}_${this.activeItem.activeRowIndex}`
// const refName = `${this.activeItem.activeCollapseId}_${this.activeItem.activeRowIndex}`
// this.$nextTick(() => {
// this.$refs[refName][0].setMeasureData(measureData)
// })
},
createNTTarget(measureData) {
var idx = this.tableQuestions.findIndex(item => item.LesionType === 1)
if (idx > -1) {
this.addTarget(this.tableQuestions[idx], measureData)
}
},
setCollapseActive(measureData) {
if (measureData) {
if (this.activeItem.activeRowIndex === measureData.RowIndex && this.activeItem.activeCollapseId === measureData.QuestionId) {
return
} else {
this.activeItem.activeCollapseId = measureData.QuestionId
this.activeItem.activeRowIndex = measureData.RowIndex
this.activeName = `${this.activeItem.activeCollapseId}_${this.activeItem.activeRowIndex}`
}
}
},
modifyMeasuredData(measureObj) {
if (this.CriterionType !== 10) {
if (measureObj.questionInfo) {
this.activeItem.activeCollapseId = measureObj.questionInfo.QuestionId
this.activeItem.activeRowIndex = String(measureObj.questionInfo.RowIndex)
this.activeName = `${this.activeItem.activeCollapseId}_${this.activeItem.activeRowIndex}`
const refName = `${this.activeItem.activeCollapseId}_${this.activeItem.activeRowIndex}`
this.$refs[refName][0].setMeasureData(measureObj.measureData)
}
} else {
this.$refs['pcwg3'].modifyMeasuredData(measureObj)
}
},
// 设置测量数据
setMeasuredData(measureData) {
if (this.CriterionType !== 10) {
if (this.activeItem.activeCollapseId) {
// 判断是否存在测量数据
this.$nextTick(() => {
const refName = `${this.activeItem.activeCollapseId}_${this.activeItem.activeRowIndex}`
if ((this.$refs[refName][0].questionForm && this.$refs[refName][0].questionForm.MeasureData && measureData.data.uuid === this.$refs[refName][0].questionForm.MeasureData.data.uuid) || !this.$refs[refName][0].questionForm.MeasureData) {
this.$refs[refName][0].setMeasureData(measureData)
} else {
if (this.isBaseLineTask) {
var idx = this.tableQuestions.findIndex(item => item.LesionType === 0)
if (this.tableQuestions[idx].TableQuestions.Answers.length < this.tableQuestions[idx].MaxQuestionCount && (measureData.type === 'Length' || measureData.type === 'Bidirectional')) {
this.createTTarget(measureData)
} else {
this.createNTTarget(measureData)
}
} else {
this.createNLTarget(measureData)
}
}
})
} else {
if (this.isBaseLineTask) {
var idx = this.tableQuestions.findIndex(item => item.LesionType === 0)
if (this.tableQuestions[idx].TableQuestions.Answers.length < this.tableQuestions[idx].MaxQuestionCount && (measureData.type === 'Length' || measureData.type === 'Bidirectional')) {
this.createTTarget(measureData)
} else {
this.createNTTarget(measureData)
}
} else {
this.createNLTarget(measureData)
}
}
} else {
this.$refs['pcwg3'].setMeasuredData(measureData)
}
},
async close(questionsObj) {
if (questionsObj) {
this.getReadingQuestionAndAnswer(questionsObj.visitTaskId)
}
this.activeItem.activeRowIndex = null
this.activeItem.activeCollapseId = null
this.activeName = ''
},
getECRFQuestions(obj) {
this.$refs['ecrf'].getQuestions(obj.visitTaskId)
},
receiveMsg(event) {
if (event.data.type === 'isCanActiveNoneDicomTool') {
this.getUnSaveTarget()
var obj = {}
if (this.unSaveTargets.length > 0) {
var rowIndex = String(this.unSaveTargets[0].rowIndex)
var questionId = this.unSaveTargets[0].questionId
const refName = `${this.activeItem.activeCollapseId}_${this.activeItem.activeRowIndex}`
if (rowIndex === this.activeItem.activeRowIndex && questionId === this.activeItem.activeCollapseId && !this.$refs[refName][0].questionForm.MeasureData) {
// 判断是否是靶病灶
// questions.findIndex(i => i.QuestionMark === questionMark)
var lesionType = this.$refs[refName][0].questionForm.LesionType
if (lesionType === 0) {
obj = { isCanActiveTool: false, lesionName: '', reason: '当前病灶为靶病灶,不允许添加标记!' }
} else {
obj = { isCanActiveTool: true, lesionName: this.$refs[refName][0].lesionMark, reason: '' }
}
} else {
obj = { isCanActiveTool: false, lesionName: '', reason: `病灶${this.unSaveTargets[0].lessionName}信息未保存,不允许添加!` }
}
} else {
if (this.activeItem.activeCollapseId) {
// 判断是否是靶病灶
const refName = `${this.activeItem.activeCollapseId}_${this.activeItem.activeRowIndex}`
obj = { isCanActiveTool: true, lesionName: this.$refs[refName][0].lesionMark, reason: '' }
} else {
// 获取非靶病灶/新病灶的长度及标识
var qsObj = null
var lesionName = ''
if (this.isBaseLineTask) {
const idx = this.tableQuestions.findIndex(item => item.LesionType === 1)
if (idx > -1) {
qsObj = this.tableQuestions[idx]
}
} else {
const idx = this.tableQuestions.findIndex(item => item.LesionType === 2)
if (idx > -1) {
qsObj = this.tableQuestions[idx]
}
}
var questions = qsObj.TableQuestions.Questions.find(item => item.QuestionMark === 3)
if (questions) {
var maxIndex = this.getMaxRowIndex(qsObj.TableQuestions.Answers)
lesionName = `${qsObj.OrderMark}${String(maxIndex + 1).padStart(2, '0')}`
}
obj = { isCanActiveTool: true, lesionName: lesionName, reason: '' }
}
}
DicomEvent.$emit('isCanActiveNoneDicomTool', obj)
} else if (event.data.type === 'setMeasurement') {
this.setMeasuredData(event.data.data)
}
}
}
}
</script>
<style lang="scss" scoped>
.measurement-wrapper{
overflow-y: auto;
// overflow: hidden;
.container{
padding: 10px;
}
.title{
padding: 5px;
font-weight: bold;
color: #ddd;
font-size: 15px;
}
.add-icon{
padding: 5px;
font-weight: bold;
color: #ddd;
font-size: 15px;
border: 1px solid #938b8b;
margin-bottom: 2px;
cursor: pointer;
}
.add-icon:hover{
background-color: #607d8b;
}
.flex-row{
display: flex;
flex-direction: row;
justify-content: space-between;
background-color: #424242;
}
.lesion_list{
position: relative;
}
.el-collapse{
border-bottom:none;
>>>.el-collapse-item{
background-color: #000!important;
color: #ddd;
}
>>>.el-collapse-item__header{
background-color: #000!important;
color: #ddd;
border-bottom-color:#5a5a5a;
padding-left: 5px;
height: 35px;
line-height: 35px;
}
>>>.el-collapse-item__wrap{
background-color: #000!important;
color: #ddd;
}
>>>.el-collapse-item__content{
width:260px;
position: absolute;
top: 0px;
right: 0px;
border: 1px solid #ffeb3b;
z-index: 1;
color: #ddd;
padding: 5px;
background-color:#1e1e1e;
}
}
}
</style>

View File

@ -0,0 +1,21 @@
<template>
<QuestionsPreview v-if="trialCriterionId" :criterion-id="trialCriterionId" :is-system-criterion="isSystemCriterion" :form-type="1" style="padding:10px;" />
</template>
<script>
import QuestionsPreview from './components/QuestionsPreview'
export default {
name: 'CriterionQuestions',
components: { QuestionsPreview },
data() {
return {
trialCriterionId: '',
isSystemCriterion: ''
}
},
mounted() {
console.log(this.$route.query)
this.trialCriterionId = this.$route.query.TrialReadingCriterionId
this.isSystemCriterion = this.$route.query.isSystemCriterion === 'true'
}
}
</script>

View File

@ -0,0 +1,548 @@
<template>
<div class="dicom-viewer-wrapper">
<div class="dicom-viewer-container">
<div class="dicom-tools">
<div class="tool-wrapper">
<div class="dropdown">
<div class="icon">
<svg-icon icon-class="layout" class="svg-icon" />
</div>
<div class="text">布局<i class="el-icon-caret-bottom" /></div>
<div class="dropdown-content">
<ul style="width:40px">
<li v-for="layout in layouts" :key="layout.index">
<a href="#" @click.prevent="changeLayout(layout)">{{ layout.row }}*{{ layout.col }}</a>
</li>
</ul>
</div>
</div>
</div>
<div class="tool-wrapper">
<div
class="icon"
:class="[activeTool==='Zoom'?'tool_active':'']"
data-tool="Zoom"
@click.prevent="setToolActive('Zoom',false)"
>
<svg-icon icon-class="magnifier" class="svg-icon" />
</div>
<div class="text">缩放</div>
</div>
<div class="tool-wrapper">
<div
class="icon"
:class="[activeTool==='Pan'?'tool_active':'']"
data-tool="Pan"
@click.prevent="setToolActive('Pan',false)"
>
<svg-icon icon-class="move" class="svg-icon" />
</div>
<div class="text">移动</div>
</div>
<div class="tool-wrapper">
<div
class="icon"
@click.prevent="fitToType('fitToWindow')"
>
<svg-icon icon-class="fitToWindow" class="svg-icon" />
</div>
<div class="text">适应窗口</div>
</div>
<div class="tool-wrapper">
<div
class="icon"
@click.prevent="fitToType('fitToImage')"
>
<svg-icon icon-class="fitToImage" class="svg-icon" />
</div>
<div class="text">适应图像</div>
</div>
<div class="tool-wrapper">
<div
class="icon"
:class="[activeTool==='Length'?'tool_active':'']"
data-tool="Length"
@click.prevent="setToolActive('Length',true)"
>
<svg-icon icon-class="length" class="svg-icon" />
</div>
<div class="text">长度测量</div>
</div>
<div class="tool-wrapper">
<div
class="icon"
:class="[activeTool==='Bidirectional'?'tool_active':'']"
data-tool="Bidirectional"
@click.prevent="setToolActive('Bidirectional',true)"
>
<svg-icon icon-class="bidirection" class="svg-icon" />
</div>
<div class="text">十字线</div>
</div>
<div class="tool-wrapper">
<div
class="icon"
:class="[activeTool==='ArrowAnnotate'?'tool_active':'']"
data-tool="ArrowAnnotate"
@click.prevent="setToolActive('ArrowAnnotate',true)"
>
<svg-icon icon-class="label" class="svg-icon" />
</div>
<div class="text">文字标注</div>
</div>
<!-- <div class="tool-wrapper">
<div
class="icon"
:class="[activeTool==='Eraser'?'tool_active':'']"
data-tool="Eraser"
@click.prevent="setToolActive('Eraser')"
>
<svg-icon icon-class="clear" class="svg-icon" />
</div>
<div class="text">清除标记</div>
</div> -->
<div class="tool-wrapper">
<div class="dropdown">
<div class="icon">
<svg-icon icon-class="more" class="svg-icon" />
</div>
<div class="text">更多<i class="el-icon-caret-bottom" /></div>
<div class="dropdown-content">
<ul style="width:90px;text-align: left;">
<li>
<a href="#">
<svg-icon icon-class="angle" />
角度测量
</a>
</li>
<li>
<a href="#">
<svg-icon icon-class="cobb" />
Cobb测量
</a>
</li>
<li>
<a href="#">
<svg-icon icon-class="oval" />
椭圆测量
</a>
</li>
<li>
<a href="#">
<svg-icon icon-class="rectangle" />
矩形测量
</a>
</li>
<li>
<a href="#">
<svg-icon icon-class="image" />
导出Png图像
</a>
</li>
</ul>
</div>
</div>
</div>
</div>
<div class="dicom-viewers">
<div ref="container" class="viewer-container" :class="['box', `box_${layoutRow}_${layoutCol}`]">
<div
v-for="i in maxCanvas"
:key="i"
:class="['item', i-1===currentDicomCanvasIndex?'item_active':'']"
:data-index="i-1"
@click="activateDicomCanvas(i-1)"
>
<dicom-canvas
v-if="canvasW"
:ref="`dicomCanvas${i-1}`"
:style="{width:canvasW,height: canvasH}"
:is-current-task="isCurrentTask"
:is-active="i-1===currentDicomCanvasIndex"
@setMeasureData="setMeasureData"
@removeMeasureData="removeMeasureData"
@modifyMeasureData="modifyMeasureData"
/>
</div>
</div>
<div ref="form-container" class="form-container">
<MeasurementList
v-if="visitTaskId"
ref="measurementList"
:is-current-task="isCurrentTask"
/>
</div>
</div>
</div>
</div>
</template>
<script>
import * as cornerstone from 'cornerstone-core'
import * as cornerstoneMath from 'cornerstone-math'
import * as cornerstoneTools from 'cornerstone-tools'
import Hammer from 'hammerjs'
cornerstoneTools.external.cornerstone = cornerstone
cornerstoneTools.external.Hammer = Hammer
cornerstoneTools.external.cornerstoneMath = cornerstoneMath
import DicomCanvas from './DicomCanvas'
import MeasurementList from './MeasurementList'
import Store from './Store'
export default {
name: 'DicomViewer',
components: { DicomCanvas, MeasurementList },
props: {
visitTaskId: {
type: String,
default: ''
},
isCurrentTask: {
type: Boolean,
required: true
},
readingTaskState: {
type: Number,
required: true
}
},
data() {
return {
layouts: [
{ index: 0, row: 1, col: 1 },
{ index: 1, row: 1, col: 2 },
{ index: 2, row: 2, col: 1 },
{ index: 3, row: 2, col: 2 }
],
maxCanvas: 1,
layoutRow: 1,
layoutCol: 1,
currentDicomCanvasIndex: 0,
currentDicomCanvas: {
toolState: {
clipPlaying: false
}
},
colormapsList: [],
rotateList: [],
colorList: [],
wwwcList: [],
canvasW: null,
canvasH: null,
activeTool: '',
measuredTools: ['Length', 'Bidirectional']
}
},
mounted() {
this.rotateList[0] = '1'
this.colorList[0] = ''
this.wwwcList[0] = '1'
this.colormapsList = cornerstone.colors.getColormapsList()
this.currentDicomCanvas = this.$refs['dicomCanvas0'] ? this.$refs['dicomCanvas0'][0] : ''
this.setCanvasStyle()
window.addEventListener('resize', this.setCanvasStyle)
Store.$on('updateImage', (measureData) => {
for (let i = 0; i < this.maxCanvas; i++) {
var stack = this.$refs[`dicomCanvas${this.currentDicomCanvasIndex}`][0].stack
if (stack.studyId) {
this.$refs[`dicomCanvas${this.currentDicomCanvasIndex}`][0].updateImage(measureData)
}
}
console.log('updateImage')
})
},
beforeDestroy() {
Store.$off('updateImage')
},
methods: {
loadImageStack(dicomSeries) {
if (this.activeTool) {
this.$refs[`dicomCanvas${this.currentDicomCanvasIndex}`][0].setToolPassive(this.activeTool)
this.activeTool = ''
}
this.$refs[`dicomCanvas${this.currentDicomCanvasIndex}`][0].loadImageStack(dicomSeries)
},
// 设置画布大小
setCanvasStyle() {
this.canvasW = (window.innerWidth - 520) / this.layoutCol + 'px'
this.canvasH = (window.innerHeight - 130) / this.layoutRow + 'px'
},
// 切换布局
changeLayout(layout) {
this.layoutRow = layout.row
this.layoutCol = layout.col
this.setCanvasStyle()
this.maxCanvas = layout.row * layout.col
this.$nextTick(() => {
const elements = document.querySelectorAll('.cornerstone-element')
Array.from(elements).forEach((element) => {
cornerstone.enable(element)
cornerstone.resize(element)
})
})
},
// 激活画布
activateDicomCanvas(index) {
if (index !== this.currentDicomCanvasIndex) {
if (this.activeTool) {
this.$refs[`dicomCanvas${this.currentDicomCanvasIndex}`][0].setToolPassive(this.activeTool)
this.activeTool = ''
}
this.currentDicomCanvasIndex = index
this.currentDicomCanvas = this.$refs[`dicomCanvas${index}`]
this.currentDicomCanvas.tabIndex = 0
if (!this.rotateList[this.currentDicomCanvasIndex]) {
this.rotateList[this.currentDicomCanvasIndex] = '1'
}
if (!this.colorList[this.currentDicomCanvasIndex]) {
this.colorList[this.currentDicomCanvasIndex] = ''
}
if (!this.wwwcList[this.currentDicomCanvasIndex]) {
this.wwwcList[this.currentDicomCanvasIndex] = '1'
}
var stack = this.$refs[`dicomCanvas${this.currentDicomCanvasIndex}`][0].stack
if (stack.studyId) {
Store.$emit('toggleVisitList', stack)
}
}
},
setToolActive(toolName, isMeasuredTool) {
console.log((!this.isCurrentTask && isMeasuredTool) || this.readingTaskState >= 2)
if ((!this.isCurrentTask && isMeasuredTool) || this.readingTaskState >= 2) {
this.activeTool = ''
this.$refs[`dicomCanvas${this.currentDicomCanvasIndex}`][0].setToolEnabled(toolName)
return
}
if (this.activeTool === toolName) {
this.$refs[`dicomCanvas${this.currentDicomCanvasIndex}`][0].setToolEnabled(toolName)
this.activeTool = ''
} else {
if (this.measuredTools.includes(toolName)) {
var isCanActiveTool = this.$refs['measurementList'].isCanActiveTool()
if (isCanActiveTool) {
this.activeTool = toolName
this.$refs[`dicomCanvas${this.currentDicomCanvasIndex}`][0].setToolActive(toolName)
}
}
}
},
setToolEnabled() {
if (!this.isCurrentTask) {
this.$refs[`dicomCanvas${this.currentDicomCanvasIndex}`][0].setToolEnabled(this.activeTool)
this.activeTool = ''
return
}
if (this.activeTool) {
this.$refs[`dicomCanvas${this.currentDicomCanvasIndex}`][0].setToolEnabled(this.activeTool)
this.activeTool = ''
}
},
fitToType(toolName) {
if (this.activeTool !== 'fitToWindow' && this.activeTool !== 'fitToImage') {
this.$refs[`dicomCanvas${this.currentDicomCanvasIndex}`][0].setToolPassive(this.activeTool)
}
if (toolName === 'fitToWindow') {
this.$refs[`dicomCanvas${this.currentDicomCanvasIndex}`][0].fitToWindow()
} else if (toolName === 'fitToImage') {
this.$refs[`dicomCanvas${this.currentDicomCanvasIndex}`][0].fitToImage()
}
this.activeTool = toolName
},
setMeasureData(data) {
this.$refs['measurementList'].setMeasuredData(data)
this.activeTool = ''
},
removeMeasureData(uuid) {
console.log(uuid)
},
modifyMeasureData(data) {
this.$refs['measurementList'].modifyMeasuredData(data)
this.activeTool = ''
}
}
}
</script>
<style lang="scss" scoped>
.dicom-viewer-wrapper{
height: 100%;
padding: 5px 0px 5px 5px;
box-sizing: border-box;
.dicom-viewer-container{
display:flex;
flex-direction: column;
height: 100%;
}
::-webkit-scrollbar {
width: 5px;
height: 5px;
}
::-webkit-scrollbar-thumb {
border-radius: 10px;
background: #d0d0d0;
}
.dicom-tools{
box-sizing: border-box;
width: 100%;
height: 80px;
padding: 0 5px;
border: 1px solid #727272;
display: flex;
flex-direction: row;
justify-content: flex-start;
align-items: center;
.tool-wrapper{
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
margin-right: 30px;
.icon{
padding: 5px;
border: 1px solid #404040;
cursor: pointer;
text-align: center;
.svg-icon{
font-size:20px;
color:#ddd;
}
}
.text{
position: relative;
font-size: 12px;
margin-top: 5px;
color: #d0d0d0;
}
}
.tool_active{
background-color: #607d8b;
}
.icon:hover{
background-color: #607d8b;
}
.dropdown {
position: relative;
display: inline-block;
.text{
text-align: center;
}
}
.dropdown-content {
display: none;
position: absolute;
background-color: #383838;
color: #fff;
box-shadow: 0px 8px 16px 0px rgba(0,0,0,0.2);
z-index: 1;
font-size: 12px;
ul{
list-style: none;
margin: 0;
padding: 0;
text-align: center;
li{
a{
display: block;
padding: 5px 0px;
}
}
}
ul li:hover a{
background-color: #727272;
}
}
.dropdown:hover .dropdown-content {
display: block;
}
}
.dicom-viewers{
box-sizing: border-box;
flex: 1;
// width: 100%;
margin-top: 5px;
height: 100%;
display: flex;
flex-direction: row;
justify-content: flex-start;
.viewer-container{
box-sizing: border-box;
flex: 1;
height: 100%;
border: 1px solid #727272;
}
.form-container{
// box-sizing: border-box;
width: 300px;
height: 100%;
border: 1px solid #727272;
// overflow-y: auto;
}
.measurement-container{
// height: 100%;
overflow-y: auto;
}
.box{
display: grid;
box-sizing: border-box;
height: 100%;
padding: 0;
.item{
box-sizing: border-box;
position: relative;
border: 1px solid rgba(255, 255, 255, 0.21);
position: relative;
&_active{
border: 1px solid #5297b9;
}
}
}
.box_1_1{
grid-template-columns: repeat(1, 100%); //1列占100%
grid-template-rows: repeat(1, 100%); //1行占100%
}
.box_1_2{
grid-template-columns: repeat(2, 50%); //1列占50%
grid-template-rows: repeat(1, 100%); //1行占100%
}
.box_2_1{
grid-template-columns: repeat(1, 100%); //1列占100%
grid-template-rows: repeat(2, 50%); //1行占50%
}
.box_2_2{
grid-template-columns: repeat(2, 50%); //1列占50%
grid-template-rows: repeat(2, 50%); //1行占50%
}
// .box_3_1{
// grid-template-columns: repeat(3, 100%); //1列占100%
// grid-template-rows: repeat(3, 33.33%); //1行占100%
// }
// .box_3_2{
// grid-template-columns: repeat(3, 50%); //1列占100%
// grid-template-rows: repeat(3, 50%); //1行占100%
// }
// .box_3_3{
// grid-template-columns: repeat(3, 33.33%); //1列占100%
// grid-template-rows: repeat(3, 33.33%); //1行占100%
// }
}
}
</style>

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,221 @@
<template>
<el-form
ref="taskAllocationRuleDataForm"
v-loading="loading"
:model="form"
:rules="rules"
size="small"
>
<div v-if="list.length > 0">
<div style="text-align: right">
<el-button size="mini" type="primary" style="position:relative;top: -10px" @click="clearAll">
<!-- 清空全部未读阅片人 -->
{{$t('trials:reviewAssign:clearReader:title:clearAll')}}
</el-button>
</div>
<div v-if="readingType === 1" style="line-height: 32px;margin-bottom: 10px;">
<!-- 阅片人R1 -->
{{$t('trials:reviewAssign:clearReader:title:r1')}}
<el-tag v-if="findDoctorUser(list, 0).DoctorUser && !findDoctorUser(list, 0).IsCancelAssign" :closable="!findDoctorUser(list, 0).IsHaveReading" @close="myClose(findDoctorUser(list, 0))">
{{findDoctorUser(list, 0).DoctorUser.UserName}}({{findDoctorUser(list, 0).DoctorUser.FullName}})
</el-tag>
</div>
<div v-if="readingType === 2" style="line-height: 32px;margin-bottom: 10px;">
<!-- 阅片人R1 -->
{{$t('trials:reviewAssign:clearReader:title:r1')}}
<el-tag v-if="findDoctorUser(list, 1).DoctorUser && !findDoctorUser(list, 1).IsCancelAssign" :closable="!findDoctorUser(list, 1).IsHaveReading" @close="myClose(findDoctorUser(list, 1))">
{{findDoctorUser(list, 1).DoctorUser.UserName}}({{findDoctorUser(list, 1).DoctorUser.FullName}})
</el-tag>
</div>
<div v-if="readingType === 2" style="line-height: 32px;margin-bottom: 10px;">
<!-- 阅片人R2 -->
{{$t('trials:reviewAssign:clearReader:title:r2')}}
<el-tag v-if="findDoctorUser(list, 2).DoctorUser && !findDoctorUser(list, 2).IsCancelAssign" :closable="!findDoctorUser(list, 2).IsHaveReading" @close="myClose(findDoctorUser(list, 2))">
{{findDoctorUser(list, 2).DoctorUser.UserName}}({{findDoctorUser(list, 2).DoctorUser.FullName}})
</el-tag>
</div>
<div v-if="OtherInfo && OtherInfo.IsArbitrationReading && readingType === 2" style="line-height: 32px;margin-bottom: 10px;">
<!-- 裁判AD -->
{{$t('trials:reviewAssign:clearReader:title:ad')}}
<el-tag v-if="findDoctorUser(list, 3).DoctorUser && !findDoctorUser(list, 3).IsCancelAssign" :closable="!findDoctorUser(list, 3).IsHaveReading" @close="myClose(findDoctorUser(list, 3))">
{{findDoctorUser(list, 3).DoctorUser.UserName}}({{findDoctorUser(list, 3).DoctorUser.FullName}})
</el-tag>
</div>
<div style="line-height: 32px;margin-bottom: 10px;" v-if="OtherInfo && OtherInfo.IsOncologyReading">
<!-- 肿瘤阅片人O1 -->
{{$t('trials:reviewAssign:clearReader:title:O1')}}
<el-tag v-if="findDoctorUser(list, 4).DoctorUser && !findDoctorUser(list, 4).IsCancelAssign" :closable="!findDoctorUser(list, 4).IsHaveReading" @close="myClose(findDoctorUser(list, 4))">
{{findDoctorUser(list, 4).DoctorUser.UserName}}({{findDoctorUser(list, 4).DoctorUser.FullName}})
</el-tag>
</div>
<div style="line-height: 32px;margin-bottom: 10px;font-size: 12px;color:#f66;">
<i class="el-icon-info"></i>
<!-- 如果分配的阅片人已开始读片则不能清空 -->
{{$t('trials:reviewAssign:clearReader:title:msg1')}}
</div>
<div>
<!-- 原因 -->
<el-form-item label-width="80px" :label="$t('trials:reviewAssign:clearReader:title:reason')" prop="Note">
<el-input v-model="form.Note" type="textarea" maxlength="500" show-word-limit></el-input>
</el-form-item>
</div>
</div>
<!-- 保存 -->
<div class="base-dialog-footer" style="text-align:right;margin-top:10px;">
<el-form-item>
<!-- 取消 -->
<el-button
:disabled="loading"
size="small"
type="primary"
@click="close"
>
{{ $t('common:button:cancel') }}
</el-button>
<el-button size="small" type="primary" :loading="loading" style="margin-top: 10px;" @click="save">
{{ $t('common:button:save') }}
</el-button>
</el-form-item>
</div>
</el-form>
</template>
<script>
import { getSubjectAssignedDoctorList, cancelSubjectAssignedDoctor, addSubjectCancelDoctorNote } from '@/api/trials/reading'
export default {
name: 'ManualAllocationTable',
props: {
TrialReadingCriterionId: {
type: String,
default() {
return ''
}
},
data: {
type: Object,
default() {
return {}
}
},
readingType: {
type: Number,
default() {
return 0
}
}
},
data() {
return {
TrialId: this.$route.query.trialId,
list: [],
loading: false,
btnLoading: false,
form: {
Note: ''
},
readingType: null,
rules: {
Note: [{ required: true, message: this.$t('common:ruleMessage:specify'), trigger: 'blur' }],
},
OtherInfo: null
}
},
mounted() {
this.TrialId = this.$route.query.trialId
this.loading = true
getSubjectAssignedDoctorList(this.data.SubjectId, this.TrialReadingCriterionId).then(res => {
this.list = res.Result
this.readingType = res.OtherInfo.ReadingType
this.OtherInfo = Object.assign({}, res.OtherInfo)
this.loading = false
}).catch(() => {
this.loading = false
})
},
methods: {
clearAll () {
var is = false
this.form.Note = ''
this.list.forEach((v,i) => {
if (!v.IsHaveReading) {
this.$set(v, 'IsCancelAssign', true)
if (i + 1 === this.list.length) {
// this.form.Note += `取消${this.$fd('ArmEnum', v.ArmEnum)}阅片人${v.DoctorUser.UserName}(${v.DoctorUser.FullName});`
trials:reviewAssign:clearReader:title:cancelReader
this.form.Note += `${this.$t('trials:reviewAssign:clearReader:title:cancelReader')}(${this.$fd('ArmEnum', v.ArmEnum)} ${v.DoctorUser.UserName}(${v.DoctorUser.FullName});`
} else {
// this.form.Note += `取消${this.$fd('ArmEnum', v.ArmEnum)}阅片人${v.DoctorUser.UserName}(${v.DoctorUser.FullName});\n`
this.form.Note += `${this.$t('trials:reviewAssign:clearReader:title:cancelReader')}(${this.$fd('ArmEnum', v.ArmEnum)} ${v.DoctorUser.UserName}(${v.DoctorUser.FullName});\n`
}
is = true
}
})
if (is) {
// '已全部清空,请点击保存应用'
this.$message.success(this.$t('trials:reviewAssign:clearReader:title:msg2'))
} else {
// '没有可清空的阅片人'
this.$message.error(this.$t('trials:reviewAssign:clearReader:title:msg3'))
}
},
myClose(o) {
this.$set(o, 'IsCancelAssign', true)
if (this.form.Note) {
// this.form.Note += `\n取消${this.$fd('ArmEnum', o.ArmEnum)}阅片人${o.DoctorUser.UserName}(${o.DoctorUser.FullName});`
this.form.Note += `\n${this.$t('trials:reviewAssign:clearReader:title:cancelReader')}(${this.$fd('ArmEnum', o.ArmEnum)} ${o.DoctorUser.UserName}(${o.DoctorUser.FullName});`
} else {
// this.form.Note += `取消${this.$fd('ArmEnum', o.ArmEnum)}阅片人${o.DoctorUser.UserName}(${o.DoctorUser.FullName});`
this.form.Note += `${this.$t('trials:reviewAssign:clearReader:title:cancelReader')}(${this.$fd('ArmEnum', o.ArmEnum)} ${o.DoctorUser.UserName}(${o.DoctorUser.FullName});`
}
},
findDoctorUser(list, type) {
var a
if (list.length > 0) {
a = list.find(v => {
return v.ArmEnum === type
}) ? list.find(v => {
return v.ArmEnum === type
}) : {
DoctorUser: null
}
}
return a
},
save() {
this.$refs.taskAllocationRuleDataForm.validate(valid => {
if (!valid) return
this.loading = true
var params = this.list.map(v => {
return {
Id: v.Id,
SubjectId: this.data.SubjectId,
DoctorUserId: v.DoctorUserId,
ArmEnum: v.ArmEnum,
IsCancelAssign: v.IsCancelAssign
}
})
cancelSubjectAssignedDoctor({
CancelList: params,
Note: this.form.Note,
TrialReadingCriterionId: this.TrialReadingCriterionId,
TrialId: this.TrialId
}).then(() => {
this.loading = false
// '保存成功'
this.$message.success(this.$t('common:message:savedSuccessfully'))
this.$emit('getList')
this.close()
}).catch(() => {
this.loading = false
})
})
},
close() { this.$emit('close') }
}
}
</script>
<style scoped>
</style>

View File

@ -0,0 +1,684 @@
<template>
<el-form
ref="clinicalDataForm"
v-loading="loading"
:model="form"
size="small"
:rules="rules"
:disabled="type === 'look'"
label-width="130px"
>
<div class="base-dialog-body">
<el-form-item label="类型" prop="ClinicalQuestionType">
<el-select v-model="form.ClinicalQuestionType" clearable @change="((val)=>{typeChange(val, form)})">
<el-option
v-for="item of $d.ClinicalQuestionType"
:key="item.value"
:value="item.value"
:label="item.label"
/>
</el-select>
</el-form-item>
<el-form-item
label="问题名称"
prop="QuestionName"
>
<el-input v-model="form.QuestionName" />
</el-form-item>
<el-form-item
label="问题名称EN"
prop="QuestionEnName"
>
<el-input v-model="form.QuestionEnName" />
</el-form-item>
<el-form-item
v-if="form.ClinicalQuestionType === 'select' || form.ClinicalQuestionType === 'radio' || form.ClinicalQuestionType === 'checkbox'"
label="选项"
prop="TypeValue"
>
<el-input
v-model="form.TypeValue"
placeholder="选项请用‘|’分割多个选项"
/>
</el-form-item>
<el-form-item
v-if="form.ClinicalQuestionType === 'number'"
label="小数位数"
prop="DigitPlaces"
>
<el-radio-group
v-model="form.DigitPlaces"
style="line-height: 40px;"
>
<el-radio
v-for="item of $d.DigitPlaces"
:key="`DigitPlaces${item.value}`"
:label="item.value"
>
{{ item.label }}
</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item
v-if="form.ClinicalQuestionType === 'number'"
label="单位"
prop="Unit"
>
<el-input
v-model="form.Unit"
/>
</el-form-item>
<div v-if="form.ClinicalQuestionType === 'number'" style="background: #f8f8f8;padding: 10px 0px;border-radius: 10px;position: relative;margin-bottom: 10px;">
<!-- 运算类型 -->
<el-form-item
:label="$t('trials:readingUnit:qsList:title:customCalculateMark')"
prop="CustomCalculateMark"
>
<el-radio-group
v-model="form.CustomCalculateMark"
style="line-height: 40px;"
@change="(v) => CustomCalculateMarkChange(v)"
>
<div>
<el-radio
v-for="item of $d.ClinicalCustomCalculateMark"
v-show="item.value <= 4"
:key="`CustomCalculateMark${item.value}`"
:label="item.value"
>
{{ item.label }}
</el-radio>
</div>
<div>
<el-radio
v-for="item of $d.ClinicalCustomCalculateMark"
v-show="item.value > 4"
:key="`CustomCalculateMark${item.value}`"
:label="item.value"
>
{{ item.label }}
</el-radio>
</div>
</el-radio-group>
</el-form-item>
<el-table
v-if="isShow"
ref="CalculateTable"
:data="form.CalculateQuestions"
style="margin: 10px;width: calc(100% - 20px)"
size="small"
>
<!-- 序号 -->
<el-table-column
:label="$t('trials:readingUnit:qsList:title:order')"
min-width="70"
>
<template slot-scope="scope">
{{ scope.$index + 1 }}
</template>
</el-table-column>
<!-- 运算类型 -->
<el-table-column
:label="$t('trials:readingUnit:qsList:title:customCalculateMark')"
show-overflow-tooltip
min-width="70"
>
<template slot-scope="scope">
{{ $fd('ClinicalCustomCalculateMark', form.CustomCalculateMark) }}
</template>
</el-table-column>
<!-- 问题名称 -->
<el-table-column
:label="$t('trials:readingUnit:qsList:title:qsName')"
prop="TableQuestionId"
show-overflow-tooltip
min-width="180"
>
<template slot-scope="scope">
<el-select v-model="scope.row.TableQuestionId" clearable>
<el-option v-for="item of Questions" :key="item.QuestionId" :label="item.QuestionName" :value="item.QuestionId" />
</el-select>
</template>
</el-table-column>
<el-table-column
v-if="form.CustomCalculateMark <= 4"
:label="$t('common:action:action')"
prop="TableQuestionId"
show-overflow-tooltip
>
<template slot-scope="scope">
<!-- 添加 -->
<el-button
icon="el-icon-plus"
size="mini"
circle
:title="$t('trials:readingUnit:qsList:title:add')"
@click="addCustomCalculateMark(scope.$index)"
/>
<!-- 删除 -->
<el-button
icon="el-icon-minus"
size="mini"
circle
:disabled="form.CalculateQuestions.length <= 2"
:title="$t('trials:readingUnit:qsList:title:delete')"
@click="deleteCustomCalculateMark(scope.$index)"
/>
</template>
</el-table-column>
</el-table>
<div style="font-size: 12px;color:#666;padding: 0 15px 0;text-align: right" v-if="form.CustomCalculateMark !== -1">
<!-- '请选择同级问题!': '请选择表格问题下的子问题!' -->
<i class="el-icon-info" />{{ [1, 2, 3, 4, 9, -1].includes(form.CustomCalculateMark) ? $t('trials:readingUnit:qsList:message:msg1') : $t('trials:readingUnit:qsList:message:msg2') }}
</div>
</div>
<el-form-item v-if="form.ClinicalQuestionType !== 'group'" label="是否显示" prop="ClinicalQuestionShowEnum">
<el-radio-group
v-model="form.ClinicalQuestionShowEnum"
@change="((val)=>{isShowQuestionChange(val, form)})"
>
<el-radio
v-for="item of $d.ClinicalQuestionShow"
:key="`ClinicalQuestionShowEnum${item.value}`"
:label="item.value"
>
{{ item.label }}
</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item
v-if="form.ClinicalQuestionType !== 'group' && form.ClinicalQuestionShowEnum===1"
label="显示依赖父问题"
prop="ParentId"
>
<el-select
v-model="form.ParentId"
clearable
@change="((val)=>{parentQuestionChange(val, form)})"
>
<el-option
v-for="item of parentOptions"
:key="`ParentId${item.QuestionId}`"
:label="item.QuestionName"
:value="item.Id"
/>
</el-select>
</el-form-item>
<!-- 显示时依赖父问题触发值 -->
<el-form-item v-if="form.ParentId && form.ClinicalQuestionShowEnum===1" label="显示触发值" prop="ParentTriggerValue">
<el-select v-model="form.ParentTriggerValue" clearable>
<el-option
v-for="item of parentTriggerValOptions"
:key="item.id"
:label="item.label"
:value="item.value.toString()"
/>
</el-select>
</el-form-item>
<el-form-item v-if="form.ClinicalQuestionType !== 'group' && form.ClinicalQuestionType !== 'table'" :label="$t('trials:readingUnit:qsList:title:isRequired')" prop="IsRequired">
<el-radio-group
v-model="form.IsRequired"
:disabled="form.IsJudgeQuestion===true || form.ShowQuestion===2"
@change="((val)=>{isRequiredChange(val, form)})"
>
<el-radio
v-for="item of $d.QuestionRequired"
:key="`QuestionRequired${item.value}`"
:label="item.value"
>
{{ item.label }}
</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item
v-if="form.ClinicalQuestionType !== 'group' && form.IsRequired === 1"
:label="$t('trials:readingUnit:qsList:title:relevanceId')"
prop="RelevanceId"
>
<el-select
v-model="form.RelevanceId"
clearable
@change="((val)=>{relevanceQuestionChange(val, form)})"
>
<el-option
v-for="item of parentOptions"
:key="`RelevanceId${item.QuestionId}`"
:label="item.QuestionName"
:value="item.Id"
/>
</el-select>
</el-form-item>
<!-- 必填触发值 -->
<el-form-item
v-if="form.RelevanceId && form.IsRequired === 1"
:label="$t('trials:readingUnit:qsList:title:relevanceValueList')"
prop="RelevanceValue"
>
<el-select v-model="form.RelevanceValue" clearable>
<el-option
v-for="item of reParentTriggerValOptions"
:key="item.id"
:label="item.label"
:value="item.value"
/>
</el-select>
</el-form-item>
<el-form-item v-if="form.ClinicalQuestionType !== 'group'" prop="GroupId" label="问题分组">
<el-select v-model="form.GroupId" clearable :disabled="isParentExistGroup">
<el-option
v-for="group of groupOptions"
:key="group.Id"
:label="group.QuestionName"
:value="group.Id"
/>
</el-select>
</el-form-item>
<el-form-item v-if="form.ClinicalQuestionType === 'time' && [2, 3].includes(clinicalInfo.ClinicalDataLevel)" label="是否为检查日期" prop="IsCheckDate">
<el-radio-group
v-model="form.IsCheckDate"
>
<el-radio
v-for="item of $d.YesOrNo"
:key="`IsCheckDate${item.value}`"
:label="item.value"
>
{{ item.label }}
</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item v-if="form.ClinicalQuestionType !== 'table' && form.ClinicalQuestionType !== 'group' && form.ClinicalQuestionType !== 'number'" label="最大长度">
<el-input-number
v-model="form.MaxAnswerLength"
controls-position="right"
:min="0"
:max="2000"
/>
</el-form-item>
<el-form-item v-if="form.ClinicalQuestionType === 'table'" label="最大行数">
<el-input-number
v-model="form.MaxQuestionCount"
controls-position="right"
:min="0"
:max="10"
/>
</el-form-item>
<el-form-item v-if="form.ClinicalQuestionType === 'upload'" label="最大上传个数">
<el-input-number v-model="form.ImageCount" controls-position="right" :min="1" :max="10" />
</el-form-item>
<el-form-item label="序号" prop="ShowOrder">
<el-input-number
v-model="form.ShowOrder"
controls-position="right"
:min="0"
/>
</el-form-item>
</div>
<div class="base-dialog-footer" style="text-align:right;margin-top:10px;">
<el-form-item>
<!-- 取消 -->
<el-button
:disabled="btnLoading"
size="small"
type="primary"
@click="close"
>
{{ $t('common:button:cancel') }}
</el-button>
<!-- 保存 -->
<el-button size="small" type="primary" :loading="btnLoading" @click="save">
{{ $t('common:button:save') }}
</el-button>
</el-form-item>
</div>
</el-form>
</template>
<script>
import { addOrUpdateTrialClinicalQuestion, getTrialClinicalOtherQuestionList, getTrialClinicalGroupQuestionList, getClinicalCalculateQuestions } from '@/api/dictionary'
export default {
name: 'AddOrUpdateClinicalData',
props: {
clinicalId: {
type: String,
default() { return '' }
},
clinicalInfo: {
type: Object,
default() { return {} }
},
list: {
type: Array,
default() { return [] }
},
data: {
type: Object,
default() { return {} }
},
type: {
type: String,
default() { return '' }
}
},
data() {
var validateTypeVal = (rule, value, callback) => {
if (value === '') {
callback(new Error(this.$t('common:ruleMessage:specify')))
} else {
var arr = value.split('|')
if (new Set(arr).size !== arr.length) {
callback(new Error('选项不允许存在相同值'))
} else {
callback()
}
}
}
return {
validateTypeVal: validateTypeVal,
form: {
Id: '',
TrialClinicalId: null,
DigitPlaces: null,
QuestionName: '',
QuestionEnName: '',
ClinicalQuestionType: null,
ValueType: '',
Unit: '',
TypeValue: '',
ParentId: '',
ParentTriggerValue: '',
ShowOrder: 1,
ClinicalQuestionShowEnum: 0,
CalculateQuestions: [],
IsRequired: 2,
GroupName: '',
GroupEnName: '',
Remark: '',
ImageCount: 1,
QuestionType: null,
MaxQuestionCount: 0,
MaxAnswerLength: 0,
IsCheckDate: false,
LimitEdit: 0,
GroupId: null,
CustomCalculateMark: -1,
RelevanceId: '',
RelevanceValue: ''
},
rules: {
ClinicalQuestionType: [
{ required: true, message: '请选择', trigger: 'blur' }
],
GroupId: [
{ required: true, message: '请选择', trigger: 'blur' }
],
LesionType: [
{ required: true, message: '请选择', trigger: 'blur' }
],
QuestionName: [{ required: true, message: '请注明', trigger: 'blur' },
{ max: 300, message: '最大长度为 300' }],
QuestionGenre: [{ required: true, message: '请选择', trigger: 'blur' }],
TypeValue: [{ required: true, message: '请注明', trigger: 'blur' },
{ validator: validateTypeVal, trigger: 'blur' },
{ max: 500, message: '最大长度为 500' }],
DictionaryCode: [{ required: true, message: '请选择', trigger: 'blur' }],
IsCheckDate: [{ required: true, message: '请选择', trigger: 'blur' }],
ClinicalQuestionShowEnum: [{ required: true, message: '请选择', trigger: 'blur' }],
IsRequired: [{ required: true, message: '请选择', trigger: 'blur' }],
DigitPlaces: [{ required: true, message: '请选择', trigger: 'blur' }],
ParentId: [{ required: true, message: '请选择', trigger: 'blur' }],
ParentTriggerValue: [
{ required: true, message: '请选择', trigger: 'blur' }
],
RelevanceId: [{ required: true, message: '请选择', trigger: 'blur' }],
RelevanceValue: [{ required: true, message: '请选择', trigger: 'blur' }],
GroupName: [{ required: true, message: '请注明', trigger: 'blur' },
{ max: 50, message: '最大长度为 50' }]
},
Questions: [],
loading: false,
btnLoading: false,
parentOptions: [],
parentTriggerValOptions: [],
groupOptions: [],
isParentExistGroup: false,
lesionTypes: [],
dicList: [],
CriterionDictionaryList: [],
isShow: false,
}
},
mounted() {
this.initForm()
this.getClinicalCalculateQuestions()
},
methods: {
getClinicalCalculateQuestions() {
getClinicalCalculateQuestions({
TrialClinicalId: this.clinicalId,
ClinicalQuestionType: 'time'
}).then(res => {
this.Questions = res.Result
})
},
CustomCalculateMarkChange() {
this.isShow = false
console.log(this.form.CustomCalculateMark)
if (this.form.CustomCalculateMark < 0) {
this.form.CalculateQuestions = []
return
}
if (this.form.CustomCalculateMark === 10) {
this.form.CalculateQuestions = [
{
IsTable: false,
QuestionId: null,
TableQuestionId: null
}]
this.$nextTick(() => {
this.isShow = true
})
return
}
if (this.form.CustomCalculateMark <= 4) {
this.form.CalculateQuestions = [
{
IsTable: false,
QuestionId: null,
TableQuestionId: null
}, {
IsTable: false,
QuestionId: null,
TableQuestionId: null
}
]
} else {
this.form.CalculateQuestions = [
{
IsTable: true,
QuestionId: null,
TableQuestionId: null
}
]
}
this.$nextTick(() => {
this.isShow = true
})
},
isRequiredChange(val, form) {
if (val !== 1) {
form.RelevanceId = ''
form.RelevanceValue = ''
}
},
async initForm() {
await this.getGroupOptions()
await this.getParentQuestions()
await this.getBasicConfigSelect()
if (Object.keys(this.data).length > 0) {
for (const k in this.form) {
if (k === 'CalculateQuestions' && this.data[k] !== undefined) {
this.form[k] = JSON.parse(this.data[k])
this.isShow = true
} else {
if (this.data.hasOwnProperty(k)) {
this.form[k] = this.data[k]
}
}
}
if (this.form.ParentId !== '' && this.form.ParentId !== null && this.form.GroupName !== '') {
this.isParentExistGroup = true
}
if (this.form.ParentId) {
var index = this.parentOptions.findIndex(item => {
return item.Id === this.form.ParentId
})
if (index !== -1) {
const options = []
this.parentOptions[index].TypeValue.split('|').forEach((item, index) => {
options.push({ id: index, label: item, value: item })
})
this.parentTriggerValOptions = options
}
}
if (this.form.RelevanceId) {
var i = this.parentOptions.findIndex(item => {
return item.Id === this.form.RelevanceId
})
if (i !== -1) {
const options = []
this.parentOptions[index].TypeValue.split('|').forEach((item, index) => {
options.push({ id: index, label: item, value: item })
})
this.reParentTriggerValOptions = options
}
}
}
if (!this.form.Id) {
if (this.list.length > 0) {
let max = Math.max(...this.list.map(item=>item.ShowOrder))
this.form.ShowOrder = max + 1
}
}
},
save() {
this.$refs.clinicalDataForm.validate(valid => {
if (!valid) return
this.btnLoading = true
this.form.TrialClinicalId = this.clinicalId
if (this.form.ClinicalQuestionType !== 'upload') {
this.form.ImageCount = 0
}
var params = Object.assign({}, this.form)
params.CalculateQuestions = JSON.stringify(this.form.CalculateQuestions)
addOrUpdateTrialClinicalQuestion(params).then(res => {
this.btnLoading = false
this.$emit('getList')
this.$emit('close')
this.$message.success(this.$t('common:message:savedSuccessfully'))
})
.catch(() => {
this.btnLoading = false
})
})
},
getBasicConfigSelect() {
this.dicList = this.$d
},
getGroupOptions() {
return new Promise((resolve, reject) => {
this.loading = true
var param = {
TrialClinicalId: this.clinicalId
}
getTrialClinicalGroupQuestionList(param).then(res => {
this.groupOptions = res.Result
this.loading = false
resolve()
}).catch(() => {
this.loading = false
reject()
})
})
},
getParentQuestions() {
return new Promise((resolve, reject) => {
this.loading = true
var param = {
TrialClinicalId: this.clinicalId
}
getTrialClinicalOtherQuestionList(param).then(res => {
this.parentOptions = res.Result
this.loading = false
resolve()
}).catch(() => {
this.loading = false
reject()
})
})
},
typeChange(val, data) {
if (val !== 'select' && val !== 'radio') {
}
if (val === 'table') {
data.IsRequired = 2
} else {
data.LesionType = null
}
data.TypeValue = ''
data.ValueType = ''
data.Unit = ''
},
isShowQuestionChange(val, form) {
if (val === 2) {
form.IsRequired = 2
form.ParentId = ''
form.ParentTriggerValue = ''
} else {
form.ParentId = ''
form.ParentTriggerValue = ''
}
},
parentQuestionChange(val, form) {
console.log(val)
if (val) {
var index = this.parentOptions.findIndex(item => {
return item.Id === val
})
if (index !== -1) {
var options = []
this.parentOptions[index].TypeValue.split('|').forEach((item, index) => {
options.push({ id: index, label: item, value: item })
})
this.parentTriggerValOptions = options
}
} else {
form.GroupName = ''
form.GroupEnName = ''
}
form.ParentTriggerValue = ''
},
relevanceQuestionChange(val, form) {
if (val) {
var index = this.parentOptions.findIndex(item => {
return item.Id === val
})
if (index !== -1) {
var options = []
this.parentOptions[index].TypeValue.split('|').forEach((item, index) => {
options.push({ id: index, label: item, value: item })
})
this.reParentTriggerValOptions = options
}
}
form.RelevanceValue = ''
},
close() {
this.$emit('close')
}
}
}
</script>
<style lang="scss" scoped>
</style>

View File

@ -0,0 +1,716 @@
<template>
<div>
<div v-for="(qs,index) in questions" :key="index" v-loading="loading" class="lesions lesions_wrapper">
<h4 v-if="qs.Type === 'group'" style="color: #ddd;padding: 5px 0px;margin: 0;">
{{ qs.GroupName }}
</h4>
<div class="lesion_list">
<div v-for="item in qs.Childrens" v-show="!(isBaseLineTask && item.LesionType === 2) && !(isBaseLineTask && item.LesionType === 3)" :key="item.Id">
<!-- <div v-for="item in qs.Childrens" :key="item.Id">-->
<div v-if="item.Type === 'table'" class="flex-row">
<div class="title">{{ item.QuestionName }}</div>
<div v-if="readingTaskState<2 && (isBaseLineTask || item.LesionType === 2)" class="add-icon" @click.prevent="handleAdd(item)">
<i class="el-icon-plus" />
</div>
</div>
<!-- @change="handleCollapseChange(qs.Childrens,item)" -->
<el-collapse
v-if="item.Type === 'table' && item.TableQuestions"
v-model="activeName"
accordion
@change="handleCollapseChange"
>
<el-collapse-item
v-for="(q,i) in item.TableQuestions.Answers"
:key="i"
:name="`${item.Id}_${q.RowIndex}`"
>
<template slot="title">
<div style="width:300px;position: relative;" :style="{color:(activeName===item.Id+q.RowIndex?'#ffeb3b':'#fff')}">
{{ getLesionName(item.OrderMark,q.RowIndex, qs.Childrens) }}
<!-- <el-tooltip v-if="!item.TableQuestions.Answers[i].isLesionSaved" class="item" effect="dark" content="未保存" placement="bottom">-->
<!-- <i class="el-icon-warning" style="color:red" />-->
<!-- </el-tooltip>-->
<el-tooltip v-if="readingTaskState<2 && parseInt(item.TableQuestions.Answers[i].saveTypeEnum) === 0" class="item" effect="dark" content="未保存" placement="bottom">
<i class="el-icon-warning" style="color:red" />
</el-tooltip>
<el-tooltip v-if="readingTaskState<2 && parseInt(item.TableQuestions.Answers[i].saveTypeEnum) ===1 && item.LesionType !== 4" class="item" effect="dark" content="已更新,未保存" placement="bottom">
<i class="el-icon-warning" style="color:#ff9800" />
</el-tooltip>
<div style="position: absolute;left: 60px;top: 2px;">
<div style="display: flex;flex-direction: row;font-size: 10px;width:200px;">
<div v-if="item.TableQuestions.Answers[i].lesionPart" style="margin-left:10px;margin-bottom:5px;">
{{ item.TableQuestions.Answers[i].lesionPart }}
</div>
<div v-if="item.TableQuestions.Answers[i].lesionState" style="margin-left:10px;margin-bottom:5px;">
{{ $fd('EvaluationOfState',parseInt(item.TableQuestions.Answers[i].lesionState)) }}
</div>
</div>
</div>
</div>
</template>
<PCWG3QuestionsForm
:ref="`${item.Id}_${q.RowIndex}`"
:questions="item.TableQuestions.Questions"
:answers="item.TableQuestions.Answers[i]"
:lesion-type="item.LesionType"
:order-mark="item.OrderMark"
:table-questions="tableQuestions"
:row-index="String(q.RowIndex)"
:question-name="item.QuestionName"
:parent-qs-id="item.Id"
:visit-task-id="visitTaskId"
:is-current-task="isCurrentTask"
:reading-task-state="readingTaskState"
:is-base-line-task="isBaseLineTask"
@getReadingQuestionAndAnswer="getReadingQuestionAndAnswer"
@determineExistsUnsavedLession="determineExistsUnsavedLession"
@resetQuestions="resetQuestions"
@close="close"
/>
</el-collapse-item>
</el-collapse>
</div>
</div>
</div>
</div>
</template>
<script>
import DicomEvent from '../DicomEvent'
import PCWG3QuestionsForm from './PCWG3QuestionsForm'
import store from '@/store'
import { mapGetters } from 'vuex'
export default {
name: 'PCWG3Questions',
components: {
PCWG3QuestionsForm
},
props: {
questions: {
type: Array,
default() { return [] }
},
readingTaskStateParent: {
type: Number,
default() { return 0 }
}
},
data() {
return {
loading: false,
activeName: null,
isBaseLineTask: false,
readingTaskState: 0,
tableQuestions: [],
unSaveTargets: [],
activeItem: {
activeRowIndex: null,
activeCollapseId: null
},
visitTaskId: '',
isFirstRender: false,
isCurrentTask: false,
TaskNum: null,
BlindName: null,
isMax: false
}
},
computed: {
...mapGetters(['visitTaskList'])
},
watch: {
readingTaskStateParent(v) {
this.readingTaskState = v
},
activeName(v) {
if (v) {
const refName = `${this.activeItem.activeCollapseId}_${this.activeItem.activeRowIndex}`
this.$nextTick(() => {
if (this.$refs[refName][0].questionForm && !this.$refs[refName][0].questionForm.MeasureData) {
DicomEvent.$emit('PGWC3Active')
}
})
}
}
},
mounted() {
DicomEvent.$on('loadMeasurementList', async obj => {
this.initPage(obj)
})
DicomEvent.$on('getAllUnSaveLesions', (callback) => {
var list = this.getAllUnSaveLesions()
callback(list)
})
DicomEvent.$on('setCollapseActive', measureData => {
this.setCollapseActive(measureData)
})
},
methods: {
modifyMeasuredData(measureObj) {
if (measureObj.questionInfo) {
this.activeItem.activeCollapseId = measureObj.questionInfo.QuestionId
this.activeItem.activeRowIndex = String(measureObj.questionInfo.RowIndex)
this.activeName = `${this.activeItem.activeCollapseId}_${this.activeItem.activeRowIndex}`
const refName = `${this.activeItem.activeCollapseId}_${this.activeItem.activeRowIndex}`
this.$refs[refName][0].setMeasureData(measureObj.measureData)
}
},
setMeasuredData(measureData) {
if (this.activeItem.activeCollapseId) {
// 判断是否存在测量数据
this.$nextTick(() => {
const refName = `${this.activeItem.activeCollapseId}_${this.activeItem.activeRowIndex}`
if ((this.$refs[refName][0].questionForm && this.$refs[refName][0].questionForm.MeasureData && measureData.data.uuid === this.$refs[refName][0].questionForm.MeasureData.data.uuid) || !this.$refs[refName][0].questionForm.MeasureData) {
this.$refs[refName][0].setMeasureData(measureData)
} else {
if (this.isBaseLineTask) {
this.createNTTarget(measureData)
} else {
this.createNLTarget(measureData)
}
}
})
} else {
if (this.isBaseLineTask) {
this.createNTTarget(measureData)
} else {
this.createNLTarget(measureData)
}
}
},
createNTTarget(measureData) {
var idx = this.tableQuestions.findIndex(item => item.LesionType === 4)
if (idx > -1) {
this.addTarget(this.tableQuestions[idx], measureData)
}
},
setCollapseActive(measureData) {
if (measureData) {
if (this.activeItem.activeRowIndex === measureData.RowIndex && this.activeItem.activeCollapseId === measureData.QuestionId) {
return
} else {
this.activeItem.activeCollapseId = measureData.QuestionId
this.activeItem.activeRowIndex = measureData.RowIndex
this.activeName = `${this.activeItem.activeCollapseId}_${this.activeItem.activeRowIndex}`
}
}
},
// 创建新病灶
createNLTarget(measureData) {
var idx = this.tableQuestions.findIndex(item => item.LesionType === 2)
var idx2 = this.tableQuestions.findIndex(item => item.LesionType === 3)
if (idx > -1) {
this.addTarget(this.tableQuestions[idx], measureData, this.tableQuestions[idx2])
}
},
async addTarget(target, measureData, atarget) {
var obj = { saveTypeEnum: 0 }
// obj.RowIndex = target.TableQuestions.Answers.length + 1
// var questions = target.TableQuestions.Questions.find(item => item.QuestionMark === 3)
var questions2 = target.TableQuestions.Questions.find(item => item.QuestionMark === 9)
var questions3 = target.TableQuestions.Questions.find(item => item.QuestionMark === 7)
questions3 && (obj[questions3.Id] = 2)
// if (questions) {
// var maxIndex = this.getMaxRowIndex(target.TableQuestions.Answers)
// if (maxIndex < 1 && !this.isBaseLineTask) {
// maxIndex = this.getMaxRowIndex(atarget.TableQuestions.Answers)
// }
// obj.RowIndex = `${maxIndex + 1}.00`
// obj[questions.Id] = `${target.OrderMark}${String(maxIndex + 1).padStart(2, '0')}`
// }
var maxIndex = this.getMaxRowIndex(target.TableQuestions.Answers)
if (maxIndex < 1 && !this.isBaseLineTask) {
maxIndex = this.getMaxRowIndex(atarget.TableQuestions.Answers)
}
obj.RowIndex = `${maxIndex + 1}.00`
if (questions2) {
obj[questions2.Id] = this.BlindName
obj['BlindName'] = this.BlindName
obj['TaskNum'] = this.TaskNum
}
// target.TableQuestions.Answers.push(obj)
await store.dispatch('reading/addReadingQuestionAndAnswer', { lesionType: target.LesionType, visitTaskId: this.visitTaskId, lesionObj: obj })
this.activeItem.activeRowIndex = String(obj.RowIndex)
this.activeItem.activeCollapseId = target.Id
this.activeName = `${this.activeItem.activeCollapseId}_${this.activeItem.activeRowIndex}`
const refName = `${this.activeItem.activeCollapseId}_${this.activeItem.activeRowIndex}`
this.$nextTick(() => {
this.$refs[refName][0].setMeasureData(measureData)
})
},
determineExistsUnsavedLession(callback) {
// const loading = this.$loading({ fullscreen: true })
this.getUnSaveTarget()
if (this.unSaveTargets.length > 0) {
var rowIndex = String(this.unSaveTargets[0].rowIndex)
var questionId = this.unSaveTargets[0].questionId
if (rowIndex === String(this.activeItem.activeRowIndex) && questionId === this.activeItem.activeCollapseId) {
callback(true)
// loading.close()
return
} else {
this.$confirm(`病灶${this.unSaveTargets[0].lessionName}信息未保存,不允许执行此操作!`, {
type: 'warning',
showCancelButton: false,
callback: action => { }
})
callback(false)
// loading.close()
return
}
}
callback(true)
// loading.close()
return
},
getReadingQuestionAndAnswer() {
return new Promise(async resolve => {
const loading = this.$loading({ fullscreen: true })
await store.dispatch('reading/refreshReadingQuestionAndAnswer', { trialId: this.$router.currentRoute.query.trialId, visitTaskId: this.visitTaskId }).then(() => {
var idx = this.visitTaskList.findIndex(i => i.VisitTaskId === this.visitTaskId)
if (idx > -1) {
if (this.visitTaskList[idx].ReadingQuestions.length > 0) {
this.questions = this.visitTaskList[idx].ReadingQuestions
}
this.readingTaskState = this.visitTaskList[idx].ReadingTaskState
this.isBaseLineTask = this.visitTaskList[idx].IsBaseLineTask
this.isCurrentTask = this.visitTaskList[idx].IsCurrentTask
}
this.getTableQuestions()
this.$nextTick(() => {
this.tableQuestions.forEach(item => {
item.TableQuestions.Answers.forEach(i => {
var refName = `${item.Id}_${i.RowIndex}`
this.$refs[refName][0].initForm()
})
})
})
})
await store.dispatch('reading/refreshMeasuredData', this.visitTaskId)
DicomEvent.$emit('getMeasureData')
loading.close()
resolve()
})
},
// getReadingQuestionAndAnswer() {
// return new Promise(async resolve => {
// const loading = this.$loading({ fullscreen: true })
// await store.dispatch('reading/getReadingQuestionAndAnswer', { trialId: this.$router.currentRoute.query.trialId, visitTaskId: this.visitTaskId }).then(() => {
// var idx = this.visitTaskList.findIndex(i => i.VisitTaskId === this.visitTaskId)
// if (idx > -1) {
// if (this.visitTaskList[idx].ReadingQuestions.length > 0) {
// this.questions = this.visitTaskList[idx].ReadingQuestions
// }
// this.readingTaskState = this.visitTaskList[idx].ReadingTaskState
// this.isBaseLineTask = this.visitTaskList[idx].IsBaseLineTask
// this.isCurrentTask = this.visitTaskList[idx].IsCurrentTask
// }
// this.getTableQuestions()
// })
// await store.dispatch('reading/getMeasuredData', this.visitTaskId)
// DicomEvent.$emit('getMeasureData')
// loading.close()
// resolve()
// })
// },
getUnSaveTarget() {
this.unSaveTargets = []
this.isMax = false
this.tableQuestions.map(item => {
if (item.TableQuestions && item.TableQuestions.Answers && item.TableQuestions.Answers.length < item.MaxQuestionCount) {
item.TableQuestions.Answers.map(t => {
if (!t.RowId) {
var lessionName = this.getLesionName(item.OrderMark, t.RowIndex)
this.unSaveTargets.push({ lessionName: lessionName, rowIndex: t.RowIndex, questionId: item.Id })
}
})
} else {
this.isMax = true
if (this.isBaseLineTask) {
this.isMax = false
}
if (item.MaxQuestionCount === null) {
this.isMax = false
}
}
})
return this.unSaveTargets
},
async resetQuestions(obj) {
this.setQuestions(this.questions, obj)
await store.dispatch('reading/setReadingQuestionAndAnswer', { questions: this.questions, visitTaskId: this.visitTaskId })
this.getTableQuestions()
},
setQuestions(questions, obj) {
questions.forEach(item => {
if (item.Type === 'table' && item.Id === obj.questionId) {
var idx = item.TableQuestions.Answers.findIndex(i => i.RowIndex === obj.rowIndex)
item.TableQuestions.Answers[idx].isLymphLesion = obj.isLymphLesion
item.TableQuestions.Answers[idx].loctation = obj.lesionOrgan
item.TableQuestions.Answers[idx].lesionPart = obj.lesionPart
item.TableQuestions.Answers[idx].lesionLength = obj.lesionLength
item.TableQuestions.Answers[idx].lesionShort = obj.lesionShort
item.TableQuestions.Answers[idx].lesionState = obj.lesionState
item.TableQuestions.Answers[idx].saveTypeEnum = obj.saveTypeEnum
for (const i in obj.anwsers) {
if (i === 'MeasureData' && obj.anwsers[i]) {
item.TableQuestions.Answers[idx].InstanceId = obj.anwsers[i].instanceId
item.TableQuestions.Answers[idx].SeriesId = obj.anwsers[i].seriesId
item.TableQuestions.Answers[idx][i] = JSON.stringify(obj.anwsers[i])
item.TableQuestions.Answers[idx].isDicomReading = !(obj.anwsers[i].IsDicomReading === false || obj.anwsers[i].IsDicomReading === 'False')
} else {
item.TableQuestions.Answers[idx][i] = String(obj.anwsers[i])
}
}
}
if (item.Childrens.length > 0) {
this.setQuestions(item.Childrens, obj)
}
})
},
getQuestions(questions) {
questions.forEach(item => {
if (item.Type === 'table' && item.TableQuestions && item.TableQuestions.Answers.length > 0) {
item.TableQuestions.Answers.forEach(answerObj => {
this.$set(answerObj, 'loctation', this.getQuestionAnswer(item.TableQuestions.Questions, 6, answerObj))
this.$set(answerObj, 'lesionLength', this.getQuestionAnswer(item.TableQuestions.Questions, 0, answerObj))
this.$set(answerObj, 'lesionShort', this.getQuestionAnswer(item.TableQuestions.Questions, 1, answerObj))
var isLymphLesion = this.getQuestionAnswer(item.TableQuestions.Questions, 2, answerObj)
isLymphLesion = isLymphLesion ? parseInt(isLymphLesion) : null
this.$set(answerObj, 'isLymphLesion', isLymphLesion)
this.$set(answerObj, 'isLesionSaved', true)
var lesionState = this.getQuestionAnswer(item.TableQuestions.Questions, 7, answerObj)
lesionState = !isNaN(parseInt(lesionState)) ? parseInt(lesionState) : ''
this.$set(answerObj, 'lesionState', lesionState)
if (answerObj.RowId) {
this.$set(answerObj, 'saveTypeEnum', isNaN(parseInt(lesionState)) ? 1 : 2)
} else {
this.$set(answerObj, 'saveTypeEnum', 0)
}
})
}
if (item.Childrens.length > 0) {
this.getQuestions(item.Childrens)
}
})
return questions
},
getTableQuestions() {
this.tableQuestions = []
this.questions.map(item => {
if (item.Type === 'table') {
this.tableQuestions.push(item)
}
if (item.Childrens.length > 0) {
this.getTableQuestionsChild(item.Childrens)
}
})
},
getTableQuestionsChild(obj) {
obj.map(item => {
if (item.Type === 'table') {
this.tableQuestions.push(item)
}
if (item.Childrens.length > 0) {
this.getTableQuestionsChild(item.Childrens)
}
})
},
getLesionName(orderMark, rowIndex, qs) {
var lessionName = ''
var rowIndexArr = rowIndex.split('.')
var x = parseInt(rowIndexArr[0])
var y = parseInt(rowIndexArr[1])
if (y > 0) {
y = String.fromCharCode(parseInt(rowIndexArr[1]) - 1 + 65 + 32)
lessionName = `${orderMark}${String(x).padStart(2, '0')}${y}`
} else {
lessionName = `${orderMark}${String(x).padStart(2, '0')}`
}
return lessionName
},
handleAdd(qs) {
this.getUnSaveTarget()
if (this.unSaveTargets.length > 0) {
this.$confirm(`病灶${this.unSaveTargets[0].lessionName}信息未保存,不允许继续添加!`, {
type: 'warning',
showCancelButton: false,
callback: action => {}
})
return
}
if (!!qs.MaxQuestionCount && qs.MaxQuestionCount <= qs.TableQuestions.Answers.length) {
this.$confirm(`${qs.QuestionName}个数最多不能超过${qs.MaxQuestionCount}个`, {
type: 'warning',
showCancelButton: false,
callback: action => {}
})
} else {
var obj = { saveTypeEnum: 0 }
var idx2 = this.tableQuestions.findIndex(item => item.LesionType === 3)
var maxIndex = this.getMaxRowIndex(qs.TableQuestions.Answers)
if (maxIndex < 1) {
maxIndex = this.getMaxRowIndex(this.tableQuestions[idx2].TableQuestions.Answers)
}
obj.RowIndex = `${maxIndex + 1}.00`
qs.TableQuestions.Questions.forEach(item => {
if (item.QuestionMark === 9) {
obj[item.Id] = this.BlindName
obj['BlindName'] = this.BlindName
obj['TaskNum'] = this.TaskNum
} else if (item.QuestionMark === 7) {
console.log('状态')
obj[item.Id] = 2
} else {
obj[item.Id] = ''
}
})
qs.TableQuestions.Answers.push(obj)
this.activeItem.activeRowIndex = String(obj.RowIndex)
this.activeItem.activeCollapseId = qs.Id
this.activeName = `${this.activeItem.activeCollapseId}_${this.activeItem.activeRowIndex}`
}
},
getMaxRowIndex(answerList) {
if (answerList.length > 0) {
var arr = []
answerList.forEach(item => {
var rowIndex = item.RowIndex
arr.push(parseInt(rowIndex))
})
var max = Math.max.apply(null, arr)
return max
} else {
return 0
}
},
handleCollapseChange(val) {
console.log(this.activeName)
if (this.activeName) {
var arr = this.activeName.split('_')
this.activeItem.activeRowIndex = arr[1]
this.activeItem.activeCollapseId = arr[0]
this.$nextTick(() => {
const refName = `${this.activeItem.activeCollapseId}_${this.activeItem.activeRowIndex}`
if (this.$refs[refName][0].questionForm.IsDicomReading !== false) {
DicomEvent.$emit('imageLocation', { questionId: this.activeItem.activeCollapseId, rowIndex: this.activeItem.activeRowIndex, visitTaskId: this.visitTaskId, lesionName: this.$refs[refName][0].lesionName, lesionType: this.$refs[refName][0].lesionType })
}
})
} else {
this.activeItem.activeRowIndex = null
this.activeItem.activeCollapseId = null
}
},
getAllUnSaveLesions() {
var arr = []
console.log(this.tableQuestions)
this.tableQuestions.map(item => {
if (item.TableQuestions && item.TableQuestions.Answers) {
item.TableQuestions.Answers.map(t => {
const refName = `${item.Id}_${t.RowIndex}`
if (this.$refs[refName] && this.$refs[refName][0] && this.$refs[refName][0].questionForm.saveTypeEnum !== 2) {
var lessionName = this.getLesionName(item.OrderMark, t.RowIndex)
arr.push({ lessionName: lessionName, rowIndex: t.RowIndex, questionId: item.Id })
}
})
}
})
return arr
},
initPage(obj) {
console.log(this.questions)
if (this.visitTaskId !== obj.visitTaskId) {
this.visitTaskId = obj.visitTaskId
this.taskBlindName = obj.taskBlindName
this.activeName = ''
this.activeItem.activeRowIndex = null
this.activeItem.activeCollapseId = null
// this.$refs['ecrf'].getQuestions(obj.visitTaskId)
this.$emit('getECRFQuestions', obj)
}
var idx = this.visitTaskList.findIndex(i => i.VisitTaskId === obj.visitTaskId)
if (idx > -1) {
if (this.visitTaskList[idx].ReadingQuestions.length > 0) {
this.questions = this.visitTaskList[idx].ReadingQuestions
}
this.readingTaskState = this.visitTaskList[idx].ReadingTaskState
this.isBaseLineTask = this.visitTaskList[idx].IsBaseLineTask
this.isCurrentTask = this.visitTaskList[idx].IsCurrentTask
this.TaskNum = this.visitTaskList[idx].VisitTaskNum
this.BlindName = this.visitTaskList[idx].TaskBlindName
this.getTableQuestions()
if (this.isFirstRender) {
this.$nextTick(() => {
this.tableQuestions.forEach(item => {
item.TableQuestions.Answers.forEach(i => {
var refName = `${item.Id}_${i.RowIndex}`
this.$refs[refName][0].initForm()
})
})
})
}
}
if (!this.isFirstRender) {
this.isFirstRender = true
}
},
isCanActiveTool(toolName) {
this.getUnSaveTarget()
if (!this.activeName) {
if (this.isMax) {
return { isCanActiveTool: false, reason: `新病灶已到达最大数,无法添加` }
}
} else {
const refName = `${this.activeItem.activeCollapseId}_${this.activeItem.activeRowIndex}`
if (this.$refs[refName][0].questionForm.MeasureData && this.isMax) {
return { isCanActiveTool: false, reason: `新病灶已到达最大数,无法添加` }
}
}
if (this.unSaveTargets.length > 0) {
var rowIndex = String(this.unSaveTargets[0].rowIndex)
var questionId = this.unSaveTargets[0].questionId
const refName = `${this.activeItem.activeCollapseId}_${this.activeItem.activeRowIndex}`
if (rowIndex === this.activeItem.activeRowIndex && questionId === this.activeItem.activeCollapseId && !this.$refs[refName][0].questionForm.MeasureData) {
return this.checkToolCanActive(toolName)
} else {
return { isCanActiveTool: false, reason: `病灶${this.unSaveTargets[0].lessionName}信息未保存,不允许继续添加!` }
}
} else {
if (this.activeItem.activeCollapseId) {
return this.checkToolCanActive(toolName)
} else {
if (toolName === 'ArrowAnnotate' || toolName === 'Bidirectional' || toolName === 'Length') {
return { isCanActiveTool: true, reason: '' }
} else {
return { isCanActiveTool: false, reason: '' }
}
}
}
},
checkToolCanActive(toolName) {
const refName = `${this.activeItem.activeCollapseId}_${this.activeItem.activeRowIndex}`
var isLymphNodes = this.$refs[refName][0].getQuestionVal(2)
isLymphNodes = !isNaN(parseInt(isLymphNodes)) ? parseInt(isLymphNodes) : null
if (!this.$refs[refName][0].questionForm.MeasureData) {
if (this.isBaseLineTask) {
if (this.$refs[refName][0].lesionType === 0 && isLymphNodes === 1 && toolName !== 'Bidirectional') {
return { isCanActiveTool: false, reason: '淋巴结靶病灶需使用长短径测量工具' }
} else if (this.$refs[refName][0].lesionType === 0 && isLymphNodes === 0 && toolName !== 'Length') {
return { isCanActiveTool: false, reason: '非淋巴结靶病灶需使用长径测量工具' }
} else if (this.$refs[refName][0].lesionType === 1 && isLymphNodes === 1 && toolName !== 'Bidirectional' && toolName !== 'ArrowAnnotate') {
return { isCanActiveTool: false, reason: '淋巴结非靶病灶需使用长短径测量工具或标注工具' }
} else if (this.$refs[refName][0].lesionType === 1 && isLymphNodes === 0 && toolName !== 'Length' && toolName !== 'ArrowAnnotate') {
return { isCanActiveTool: false, reason: '非淋巴结非靶病灶需使用长径测量工具或标注工具' }
} else {
return { isCanActiveTool: true, reason: '' }
}
} else {
if (this.$refs[refName][0].lesionType === 0 && isLymphNodes === 1 && toolName !== 'Bidirectional' && toolName !== 'ArrowAnnotate') {
return { isCanActiveTool: false, reason: '淋巴结靶病灶需使用长短径测量工具或标注工具' }
} else if (this.$refs[refName][0].lesionType === 0 && isLymphNodes === 0 && toolName !== 'Length' && toolName !== 'ArrowAnnotate') {
return { isCanActiveTool: false, reason: '淋巴结f非靶病灶需使用长径测量工具或标注工具' }
} else if (this.$refs[refName][0].lesionType === 1 && isLymphNodes === 1 && toolName !== 'Bidirectional' && toolName !== 'ArrowAnnotate') {
return { isCanActiveTool: false, reason: '淋巴结非靶病灶需使用长短径测量工具或标注工具' }
} else if (this.$refs[refName][0].lesionType === 1 && isLymphNodes === 0 && toolName !== 'Length' && toolName !== 'ArrowAnnotate') {
return { isCanActiveTool: false, reason: '非淋巴结非靶病灶需使用长径测量工具或标注工具' }
} else if (this.$refs[refName][0].lesionType === 2 && isLymphNodes === 1 && toolName !== 'Bidirectional' && toolName !== 'ArrowAnnotate') {
return { isCanActiveTool: false, reason: '淋巴结新病灶需使用长短径测量工具或标注工具' }
} else if (this.$refs[refName][0].lesionType === 2 && isLymphNodes === 0 && toolName !== 'Length' && toolName !== 'ArrowAnnotate') {
return { isCanActiveTool: false, reason: '非淋巴结新病灶需使用长径测量工具或标注工具' }
} else {
return { isCanActiveTool: true, reason: '' }
}
}
} else {
return { isCanActiveTool: true, reason: '' }
}
},
async close(questionsObj) {
if (questionsObj) {
this.getReadingQuestionAndAnswer(questionsObj.visitTaskId)
}
this.activeItem.activeRowIndex = null
this.activeItem.activeCollapseId = null
this.activeName = ''
}
}
}
</script>
<style lang="scss" scoped>
.measurement-wrapper{
overflow-y: auto;
// overflow: hidden;
.container{
padding: 10px;
}
.title{
padding: 5px;
font-weight: bold;
color: #ddd;
font-size: 15px;
}
.add-icon{
padding: 5px;
font-weight: bold;
color: #ddd;
font-size: 15px;
border: 1px solid #938b8b;
margin-bottom: 2px;
cursor: pointer;
}
.add-icon:hover{
background-color: #607d8b;
}
.flex-row{
display: flex;
flex-direction: row;
justify-content: space-between;
background-color: #424242;
}
.lesion_list{
position: relative;
}
.el-collapse{
border-bottom:none;
>>>.el-collapse-item{
background-color: #000!important;
color: #ddd;
}
>>>.el-collapse-item__header{
background-color: #000!important;
color: #ddd;
border-bottom-color:#5a5a5a;
padding-left: 5px;
height: 35px;
line-height: 35px;
}
>>>.el-collapse-item__wrap{
background-color: #000!important;
color: #ddd;
}
>>>.el-collapse-item__content{
width:260px;
position: absolute;
top: 0px;
right: 0px;
border: 1px solid #ffeb3b;
z-index: 1;
color: #ddd;
padding: 5px;
background-color:#1e1e1e;
}
}
}
</style>

View File

@ -0,0 +1,301 @@
<template>
<div id="tags-view-container" class="tags-view-container">
<scroll-pane ref="scrollPane" class="tags-view-wrapper">
<router-link
v-for="tag in visitedViews"
ref="tag"
:key="tag.path"
:class="isActive(tag)?'active':''"
:to="{ path: tag.path, query: tag.query, fullPath: tag.fullPath }"
tag="span"
class="tags-view-item"
@click.middle.native="!isAffix(tag)?closeSelectedTag(tag):''"
@contextmenu.prevent.native="openMenu(tag,$event)"
>
{{ tag.title }}
<span
v-if="!isAffix(tag)"
class="el-icon-close"
@click.prevent.stop="closeSelectedTag(tag)"
/>
</router-link>
</scroll-pane>
<ul v-show="visible" :style="{left:left+'px',top:top+'px'}" class="contextmenu">
<li @click="refreshSelectedTag(selectedTag)">Refresh</li>
<li v-if="!isAffix(selectedTag)" @click="closeSelectedTag(selectedTag)">Close</li>
<li @click="closeOthersTags">Close Others</li>
<li @click="closeAllTags(selectedTag)">Close All</li>
</ul>
</div>
</template>
<script>
import ScrollPane from './ScrollPane'
import path from 'path'
export default {
components: { ScrollPane },
data() {
return {
visible: false,
top: 0,
left: 0,
selectedTag: {},
affixTags: []
}
},
computed: {
visitedViews() {
return this.$store.state.tagsView.visitedViews
}
// routes() {
// return this.$store.state.permission.routes
// }
},
watch: {
$route() {
this.addTags()
this.moveToCurrentTag()
},
visible(value) {
if (value) {
document.body.addEventListener('click', this.closeMenu)
} else {
document.body.removeEventListener('click', this.closeMenu)
}
}
},
mounted() {
this.initTags()
this.addTags()
},
methods: {
isActive(route) {
return route.path === this.$route.path
},
isAffix(tag) {
return tag.meta && tag.meta.affix
},
filterAffixTags(routes, basePath = '/') {
let tags = []
// 判断是否为空,否则控制台会报错
if (this.routes) {
routes.forEach(route => {
if (route.meta && route.meta.affix) {
const tagPath = path.resolve(basePath, route.path)
tags.push({
fullPath: tagPath,
path: tagPath,
name: route.name,
meta: { ...route.meta }
})
}
if (route.children) {
const tempTags = this.filterAffixTags(route.children, route.path)
if (tempTags.length >= 1) {
tags = [...tags, ...tempTags]
}
}
})
}
return tags
},
initTags() {
const affixTags = (this.affixTags = this.filterAffixTags(this.routes))
for (const tag of affixTags) {
// Must have tag name
if (tag.name) {
this.$store.dispatch('tagsView/addVisitedView', tag)
}
}
},
addTags() {
const { name } = this.$route
if (name) {
this.$store.dispatch('tagsView/addView', this.$route)
}
return false
},
moveToCurrentTag() {
const tags = this.$refs.tag
this.$nextTick(() => {
for (const tag of tags) {
if (tag.to.path === this.$route.path) {
this.$refs.scrollPane.moveToTarget(tag)
// when query is different then update
if (tag.to.fullPath !== this.$route.fullPath) {
this.$store.dispatch('tagsView/updateVisitedView', this.$route)
}
break
}
}
})
},
refreshSelectedTag(view) {
this.$store.dispatch('tagsView/delCachedView', view).then(() => {
const { fullPath } = view
this.$nextTick(() => {
this.$router.replace({
path: '/redirect' + fullPath
})
})
})
},
closeSelectedTag(view) {
this.$store
.dispatch('tagsView/delView', view)
.then(({ visitedViews }) => {
if (this.isActive(view)) {
this.toLastView(visitedViews, view)
}
})
},
closeOthersTags() {
this.$router.push(this.selectedTag)
this.$store
.dispatch('tagsView/delOthersViews', this.selectedTag)
.then(() => {
this.moveToCurrentTag()
})
},
closeAllTags(view) {
this.$store.dispatch('tagsView/delAllViews').then(({ visitedViews }) => {
if (this.affixTags.some(tag => tag.path === view.path)) {
return
}
this.toLastView(visitedViews, view)
})
},
toLastView(visitedViews, view) {
const latestView = visitedViews.slice(-1)[0]
if (latestView) {
this.$router.push(latestView.fullPath)
} else {
// now the default is to redirect to the home page if there is no tags-view,
// you can adjust it according to your needs.
if (view.name === 'Dashboard') {
// to reload home page
this.$router.replace({ path: '/redirect' + view.fullPath })
} else {
this.$router.push('/')
}
}
},
openMenu(tag, e) {
const menuMinWidth = 105
const offsetLeft = this.$el.getBoundingClientRect().left // container margin left
const offsetWidth = this.$el.offsetWidth // container width
const maxLeft = offsetWidth - menuMinWidth // left boundary
const left = e.clientX - offsetLeft + 15 // 15: margin right
if (left > maxLeft) {
this.left = maxLeft
} else {
this.left = left
}
this.top = e.clientY
this.visible = true
this.selectedTag = tag
},
closeMenu() {
this.visible = false
}
}
}
</script>
<style lang="scss" scoped>
.tags-view-container {
height: 34px;
width: 100%;
background: #fff;
border-bottom: 1px solid #d8dce5;
box-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.12), 0 0 3px 0 rgba(0, 0, 0, 0.04);
.tags-view-wrapper {
.tags-view-item {
display: inline-block;
position: relative;
cursor: pointer;
height: 26px;
line-height: 26px;
border: 1px solid #d8dce5;
color: #495060;
background: #fff;
padding: 0 8px;
font-size: 12px;
margin-left: 5px;
margin-top: 4px;
&:first-of-type {
margin-left: 15px;
}
&:last-of-type {
margin-right: 15px;
}
&.active {
background-color: #42b983;
color: #fff;
border-color: #42b983;
&::before {
content: "";
background: #fff;
display: inline-block;
width: 8px;
height: 8px;
border-radius: 50%;
position: relative;
margin-right: 2px;
}
}
}
}
.contextmenu {
margin: 0;
background: #fff;
z-index: 3000;
position: absolute;
list-style-type: none;
padding: 5px 0;
border-radius: 4px;
font-size: 12px;
font-weight: 400;
color: #333;
box-shadow: 2px 2px 3px 0 rgba(0, 0, 0, 0.3);
li {
margin: 0;
padding: 7px 16px;
cursor: pointer;
&:hover {
background: #eee;
}
}
}
}
</style>
<style lang="scss">
//reset element css of el-icon-close
.tags-view-wrapper {
.tags-view-item {
.el-icon-close {
width: 16px;
height: 16px;
vertical-align: 2px;
border-radius: 50%;
text-align: center;
transition: all 0.3s cubic-bezier(0.645, 0.045, 0.355, 1);
transform-origin: 100% 50%;
&:before {
transform: scale(0.6);
display: inline-block;
vertical-align: -3px;
}
&:hover {
background-color: #b4bccc;
color: #fff;
}
}
}
}
</style>

View File

@ -0,0 +1,407 @@
<template>
<el-form
ref="taskAllocationRuleDataForm"
v-loading="loading"
:model="form"
size="small"
>
<div>
<div style="line-height: 32px;margin-bottom: 10px;">
当前项目阅片模式为:
<el-tag type="primary">{{ $fd('ReadingMethod', readingType) }}</el-tag>
</div>
<div v-if="readingType === 1" style="line-height: 32px;margin-bottom: 10px;">
阅片人R1
<el-select v-model="form.R1" clearable @change="$forceUpdate()" :disabled="!!q.find(v => v.ArmEnum === 0)" size="mini">
<el-option v-for="item of DoctorSelectList.R1" :key="`R1${item.DoctorUserId}`" v-if="form.O1 !== item.DoctorUserId && form.AD !== item.DoctorUserId && form.R2 !== item.DoctorUserId && !q.find(v => v.DoctorUser.UserId === item.DoctorUserId) || !!q.find(v => v.ArmEnum === 0)" :label="`${item.UserName}(${item.FullName})`" :value="item.DoctorUserId" />
</el-select>
</div>
<div v-if="readingType === 2" style="line-height: 32px;margin-bottom: 10px;">
阅片人R1
<el-select v-model="form.R1" clearable @change="$forceUpdate()" :disabled="!!q.find(v => v.ArmEnum === 1)" size="mini">
<el-option v-for="item of DoctorSelectList.R1" :key="`R1${item.DoctorUserId}`" v-if="form.O1 !== item.DoctorUserId && form.AD !== item.DoctorUserId && form.R2 !== item.DoctorUserId && !q.find(v => v.DoctorUser.UserId === item.DoctorUserId) || !!q.find(v => v.ArmEnum === 1)" :label="`${item.UserName}(${item.FullName})`" :value="item.DoctorUserId" />
</el-select>
</div>
<div v-if="readingType === 2" style="line-height: 32px;margin-bottom: 10px;">
阅片人R2
<el-select v-model="form.R2" clearable @change="$forceUpdate()" :disabled="!!q.find(v => v.ArmEnum === 2)" size="mini">
<el-option v-for="item of DoctorSelectList.R2" :key="`R2${item.DoctorUserId}`" v-if="form.O1 !== item.DoctorUserId && form.R1 !== item.DoctorUserId && form.AD !== item.DoctorUserId && !q.find(v => v.DoctorUser.UserId === item.DoctorUserId) || !!q.find(v => v.ArmEnum === 2)" :label="`${item.UserName}(${item.FullName})`" :value="item.DoctorUserId" />
</el-select>
</div>
<div style="line-height: 32px;margin-bottom: 10px;">
裁判AD
<el-select v-model="form.AD" clearable @change="$forceUpdate()" :disabled="!!q.find(v => v.ArmEnum === 3)" size="mini">
<el-option v-for="item of DoctorSelectList.AD" :key="`AD${item.DoctorUserId}`" v-if="form.O1 !== item.DoctorUserId && form.R1 !== item.DoctorUserId && form.R2 !== item.DoctorUserId && !q.find(v => v.DoctorUser.UserId === item.DoctorUserId) || !!q.find(v => v.ArmEnum === 3)" :label="`${item.UserName}(${item.FullName})`" :value="item.DoctorUserId" />
</el-select>
</div>
<div style="line-height: 32px;margin-bottom: 10px;">
肿瘤阅片人O1
<el-select v-model="form.O1" clearable @change="$forceUpdate()" :disabled="!!q.find(v => v.ArmEnum === 4)" size="mini">
<el-option v-for="item of DoctorSelectList.O1" :key="`O1${item.DoctorUserId}`" v-if="form.AD !== item.DoctorUserId && form.R1 !== item.DoctorUserId && form.R2 !== item.DoctorUserId && !q.find(v => v.DoctorUser.UserId === item.DoctorUserId) || !!q.find(v => v.ArmEnum === 4)" :label="`${item.UserName}(${item.FullName})`" :value="item.DoctorUserId" />
</el-select>
</div>
</div>
<el-table
border
:data="list"
stripe
>
<el-table-column
prop="UserName"
label="阅片人"
min-width="180"
show-overflow-tooltip
>
<template slot-scope="scope">
{{ `${scope.row.DoctorUser.UserName}(${scope.row.DoctorUser.FullName})` }}
</template>
</el-table-column>
<el-table-column
prop="PlanSubjectCount"
label="计划受试者数量"
min-width="130"
show-overflow-tooltip
>
</el-table-column>
<el-table-column
prop="AssignedSubjectCount"
label="已分配受试者数量"
min-width="140"
show-overflow-tooltip
>
</el-table-column>
<el-table-column
prop="UserName"
label="阅片类型"
min-width="260"
show-overflow-tooltip
>
<template slot-scope="scope">
<span v-for="item of scope.row.ReadingCategoryList">
<el-tag v-if="item === 1" type="primary">{{ $fd('ReadingCategory', item) + '&' + $fd('ReadingCategory', 2) }}</el-tag>
<el-tag v-if="item === 4" type="danger">{{ $fd('ReadingCategory', item) }}</el-tag>
<el-tag v-if="item === 5" type="warning">{{ $fd('ReadingCategory', item) }}</el-tag>
</span>
</template>
</el-table-column>
<el-table-column
prop="WaitDealAllTaskCount"
label="待处理任务量(全平台)"
min-width="180"
show-overflow-tooltip
>
</el-table-column>
<el-table-column
prop="WaitDealTrialTaskCount"
label="待处理任务量(项目)"
min-width="180"
show-overflow-tooltip
>
</el-table-column>
<el-table-column
prop="UserName"
label="访视平均阅片时间(去除离群值后的)"
min-width="260"
show-overflow-tooltip
>
</el-table-column>
<el-table-column
prop="IsEnable"
label="状态"
min-width="100"
show-overflow-tooltip
>
<template slot-scope="scope">
<el-tag v-if="!scope.row.IsEnable" type="danger">{{ $fd('IsEnable', scope.row.IsEnable) }}</el-tag>
<el-tag v-else>{{ $fd('IsEnable', scope.row.IsEnable) }}</el-tag>
</template>
</el-table-column>
<el-table-column
prop="Note"
label="备注"
min-width="100"
show-overflow-tooltip
/>
</el-table>
<div class="base-dialog-footer" style="text-align:right;margin-top:10px;">
<el-form-item>
<!-- 取消 -->
<el-button
:disabled="loading"
size="small"
type="primary"
@click="close"
>
关闭
</el-button>
<el-button size="small" type="primary" :loading="loading" style="margin-top: 10px;" @click="save">
{{ $t('common:button:save') }}
</el-button>
</el-form-item>
</div>
</el-form>
</template>
<script>
import { getSubjectAssignAndTaskStatList, batchAssignDoctorToSubject, getDoctorSubjectStat, getDoctorSelectList} from '@/api/trials/reading'
export default {
name: 'ManualAllocationTable',
props: {
TrialReadingCriterionId: {
type: String,
default() {
return []
}
},
data: {
type: Object,
default() {
return {}
}
},
SubjectAssignSelectList: {
type: Array,
default() {
return []
}
},
// readingType: {
// type: Number,
// default() {
// return 0
// }
// }
},
data() {
return {
TrialId: this.$route.query.trialId,
form: {
R0: null,
R1: null,
R2: null,
AD: null,
O1: null
},
DoctorSelectList: {
R1: [],
R2: [],
AD: [],
O1: [],
},
list: [],
loading: false,
btnLoading: false,
q: [],
readingType: null,
isShowInfo: {}
}
},
mounted() {
this.TrialId = this.$route.query.trialId
this.getDoctorSubjectStat()
var arr = []
arr.push(this.getDoctorSelectList(1))
arr.push(this.getDoctorSelectList(2))
arr.push(this.getDoctorSelectList(4))
arr.push(this.getDoctorSelectList(5))
var q = []
if (Object.keys(this.data).length && this.data.SubjectId) {
} else {
this.SubjectAssignSelectList.forEach(v => {
v.NowDoctorUserList.forEach(v1 => {
q.push(v1)
})
this.q = q
})
}
console.log(q)
Promise.all(arr).then(() => {
if (Object.keys(this.data).length && this.data.SubjectId) {
this.form.R0 = this.findDoctorUser(this.data.NowDoctorUserList, 0).DoctorUser ? this.findDoctorUser(this.data.NowDoctorUserList, 0).DoctorUser.UserId : null
this.form.R1 = this.findDoctorUser(this.data.NowDoctorUserList, 1).DoctorUser ? this.findDoctorUser(this.data.NowDoctorUserList, 1).DoctorUser.UserId : null
this.form.R2 = this.findDoctorUser(this.data.NowDoctorUserList, 2).DoctorUser ? this.findDoctorUser(this.data.NowDoctorUserList, 2).DoctorUser.UserId : null
this.form.AD = this.findDoctorUser(this.data.NowDoctorUserList, 3).DoctorUser ? this.findDoctorUser(this.data.NowDoctorUserList, 3).DoctorUser.UserId : null
this.form.O1 = this.findDoctorUser(this.data.NowDoctorUserList, 4).DoctorUser ? this.findDoctorUser(this.data.NowDoctorUserList, 4).DoctorUser.UserId : null
this.data.NowDoctorUserList.forEach(v1 => {
q.push(v1)
})
this.q = q
} else {
var userList = {
R0: [],
R1: [],
R2: [],
AD: [],
O1: []
}
this.SubjectAssignSelectList.forEach(v1 => {
v1.NowDoctorUserList.forEach(v => {
if (v.ArmEnum === 0) {
userList.R0.push(v)
}
if (v.ArmEnum === 1) {
userList.R1.push(v)
}
if (v.ArmEnum === 2) {
userList.R2.push(v)
}
if (v.ArmEnum === 3) {
userList.AD.push(v)
}
if (v.ArmEnum === 4) {
userList.O1.push(v)
}
})
})
var isShowInfo = {}
for (var key in userList) {
var itemList = userList[key]
var id
isShowInfo[key] = this.checkArrayObjectValueRepeat(itemList, 'DoctorUserId')
}
this.isShowInfo = isShowInfo
if (!isShowInfo.R0) {
this.form.R0 = this.findDoctorUser(this.SubjectAssignSelectList[0].NowDoctorUserList, 0).DoctorUser ? this.findDoctorUser(this.SubjectAssignSelectList[0].NowDoctorUserList, 0).DoctorUser.UserId : null
}
if (!isShowInfo.R1) {
this.form.R1 = this.findDoctorUser(this.SubjectAssignSelectList[0].NowDoctorUserList, 1).DoctorUser ? this.findDoctorUser(this.SubjectAssignSelectList[0].NowDoctorUserList, 1).DoctorUser.UserId : null
}
if (!isShowInfo.R2) {
this.form.R2 = this.findDoctorUser(this.SubjectAssignSelectList[0].NowDoctorUserList, 2).DoctorUser ? this.findDoctorUser(this.SubjectAssignSelectList[0].NowDoctorUserList, 2).DoctorUser.UserId : null
}
if (!isShowInfo.AD) {
this.form.AD = this.findDoctorUser(this.SubjectAssignSelectList[0].NowDoctorUserList, 3).DoctorUser ? this.findDoctorUser(this.SubjectAssignSelectList[0].NowDoctorUserList, 3).DoctorUser.UserId : null
}
if (!isShowInfo.O1) {
this.form.O1 = this.findDoctorUser(this.SubjectAssignSelectList[0].NowDoctorUserList, 4).DoctorUser ? this.findDoctorUser(this.SubjectAssignSelectList[0].NowDoctorUserList, 4).DoctorUser.UserId : null
}
}
})
},
methods: {
checkArrayObjectValueRepeat(dataArr, key) {
console.log(dataArr)
var a = null
var b = false
console.log(this.SubjectAssignSelectList.length)
if (this.SubjectAssignSelectList.length === 1) {
return false
}
dataArr.forEach((v,i) => {
if(i === 0){
a = v[key]
}
if (v[key] !== a) {
b = true
}
if (dataArr.length === 1) {
b = true
}
})
return b
},
findDoctorUser(list, type) {
var a
if (list.length > 0) {
a = list.find(v => {
return v.ArmEnum === type
}) ? list.find(v => {
return v.ArmEnum === type
}) : {
DoctorUser: null
}
}
return a
},
getDoctorSelectList(ReadingCategory) {
return new Promise(resolve => {
getDoctorSelectList({
TrialId: this.$route.query.trialId,
ReadingCategory: ReadingCategory,
TrialReadingCriterionId: this.TrialReadingCriterionId
}).then(res => {
switch (ReadingCategory) {
case 1:
this.$set(this.DoctorSelectList, 'R1', res.Result)
break;
case 2:
this.$set(this.DoctorSelectList, 'R2', res.Result)
break;
case 4:
this.$set(this.DoctorSelectList, 'AD', res.Result)
break;
case 5:
this.$set(this.DoctorSelectList, 'O1', res.Result)
break;
}
resolve()
})
})
},
getDoctorSubjectStat() {
this.loading = true
getDoctorSubjectStat(this.TrialId, this.TrialReadingCriterionId).then(res => {
this.readingType = res.OtherInfo.ReadingType
this.list = res.Result
this.loading = false
}).catch(() => {
this.loading = false
})
},
save() {
var DoctorArmList = []
if (this.form.R0) {
DoctorArmList.push({
ArmEnum: 0,
DoctorUserId: this.form.R0
})
}
if (this.form.R1) {
DoctorArmList.push({
ArmEnum: 1,
DoctorUserId: this.form.R1
})
}
if (this.form.R2) {
DoctorArmList.push({
ArmEnum: 2,
DoctorUserId: this.form.R2
})
}
if (this.form.AD) {
DoctorArmList.push({
ArmEnum: 3,
DoctorUserId: this.form.AD
})
}
if (this.form.O1) {
DoctorArmList.push({
ArmEnum: 4,
DoctorUserId: this.form.O1
})
}
var params = {
TrialId: this.TrialId,
TrialReadingCriterionId: this.TrialReadingCriterionId,
SubjectIdList: this.SubjectAssignSelectList.map(v => v.SubjectId),
DoctorArmList: DoctorArmList
}
this.loading = true
batchAssignDoctorToSubject(params).then(res => {
this.$message.success('保存成功')
this.loading = false
this.$emit('getList')
this.close()
}).catch(() => {
this.loading = false
})
},
close() { this.$emit('close') }
}
}
</script>
<style scoped>
</style>

View File

@ -0,0 +1,816 @@
<template>
<div class="criterion-form-item">
<div
v-if="!!question.GroupName && question.Type==='group'"
style="font-weight: bold;font-size: 16px;margin: 5px 0px;color:#fff;"
>
{{ question.GroupName }}
</div>
<div
v-if=" question.Type==='table'"
style="font-weight: bold;font-size: 14px;margin: 5px 0px;"
>
<div style="display: flex;justify-content: space-between;align-items: center;color:#fff;margin: 10px 0 5px">
<span>{{ question.QuestionName }}</span>
<el-button size="mini" v-if="readingTaskState<2" @click="openAddTableCol(question)">
添加
</el-button>
</div>
<el-table
:data="questionForm[question.Id]">
<el-table-column
:prop="item.Id"
:label="item.QuestionName"
:key="item.Id"
v-for="item of question.TableQuestions.Questions"
show-overflow-tooltip
:render-header="renderHeader"
>
<template slot-scope="scope">
<span v-if="item.Type === 'upload'">
{{scope.row[item.Id] === '' ? '' : scope.row[item.Id].split('|').length}}
</span>
<span v-else-if="item.Type === 'number'">
{{scope.row[item.Id] ? parseFloat(scope.row[item.Id]).toFixed(digitPlaces) : null}}
</span>
<span v-else>
{{scope.row[item.Id]}}
</span>
</template>
</el-table-column>
<el-table-column
label="操作"
show-overflow-tooltip
width="100px"
v-if="readingTaskState < 2"
fixed="right"
>
<template slot-scope="scope">
<el-button type="text" size="mini" @click="openAddTableCol(question, scope.$index)">
编辑
</el-button>
<el-button type="text" size="mini" v-if="scope.row.IsCurrentTaskAdd === 'True' || !question.IsCopyLesions || IsBaseline" @click="deleteTableCol(question, scope.$index)">
删除
</el-button>
</template>
</el-table-column>
</el-table>
</div>
<template v-else>
<el-form-item
v-if="(question.ShowQuestion===1 && question.ParentTriggerValueList.includes(questionForm[question.ParentId])) || question.ShowQuestion===0"
:label="`${question.QuestionName}`"
:prop="question.Id"
:rules="[
{ required: (question.IsRequired === 0 || (question.IsRequired ===1 && question.RelevanceId && (question.RelevanceValueList.includes(questionForm[question.RelevanceId])))) && question.Type!=='group' && question.Type!=='summary',
message: '请注明', trigger: ['blur', 'change']},
]"
:class="[question.Type==='group'?'mb':question.Type==='upload'?'uploadWrapper':'']"
>
<!-- 输入框 -->
<el-input
v-if="question.Type==='input'"
v-model="questionForm[question.Id]"
:disabled="question.TableQuestionType === 2"
/>
<!-- 多行文本输入框 -->
<el-input
v-if="question.Type==='textarea'"
v-model="questionForm[question.Id]"
type="textarea"
:autosize="{ minRows: 2, maxRows: 4}"
/>
<!-- 下拉框 -->
<el-select
v-if="question.Type==='select'"
v-model="questionForm[question.Id]"
clearable
:disabled="(question.TableQuestionType === 2 || question.QuestionGenre === 2) && !!question.DictionaryCode"
@change="((val)=>{formItemChange(val, question)})"
>
<template v-if="question.TableQuestionType === 1">
<el-option
v-for="item in organList"
:key="item.Id"
:label="item[question.DataTableColumn]"
:value="item[question.DataTableColumn]"
/>
</template>
<template v-else-if="question.TableQuestionType === 3 || question.QuestionGenre === 3">
<el-option
v-for="item of $d[question.DictionaryCode]"
:key="item.id"
:value="item.value"
:label="item.label"
/>
</template>
<template v-else-if="(question.TableQuestionType === 2 || question.QuestionGenre === 2) && question.DictionaryCode">
<el-option
v-for="item of $d[question.DictionaryCode]"
:key="item.id"
:value="item.value"
:label="item.label"
/>
</template>
<template v-else>
<el-option
v-for="val in question.TypeValue.split('|')"
:key="val"
:label="val"
:value="val"
/>
</template>
</el-select>
<!-- 单选 -->
<el-radio-group
v-if="question.Type==='radio'"
v-model="questionForm[question.Id]"
@change="((val)=>{formItemChange(val, question)})"
>
<el-radio
v-for="val in question.TypeValue.split('|')"
:key="val"
:label="val"
>
{{ val }}
</el-radio>
</el-radio-group>
<!-- 复选框 -->
<el-checkbox-group
v-if="question.Type==='checkbox'"
v-model="questionForm[question.Id]"
>
<el-checkbox
v-for="val in question.TypeValue.split('|')"
:key="val"
:label="val"
>
{{ val }}
</el-checkbox>
</el-checkbox-group>
<!-- 自动计算 -->
<!-- :precision="2" :step="0.1" :max="10" -->
<el-input
v-if="question.Type==='calculation'"
v-model="questionForm[question.Id]"
@input="value=value.replace(/^\D*(\d*(?:.\d{0,2})?).*$/g, '$1')"
disabled
/>
<!-- 自增 -->
<el-input
v-if="question.Type==='increment'"
v-model="questionForm[question.Id]"
disabled
/>
<!-- 数值 -->
<!-- :precision="2" :step="0.1" :max="10" -->
<el-input
type="number"
v-if="question.Type === 'number' && question.DataSource !== 1"
@change="((val)=>{formItemNumberChange(val, question)})"
onblur="value=parseInt(value).toFixed(parseInt(localStorage.getItem('digitPlaces')))"
v-model="questionForm[question.Id]"
>
<template slot="append" v-if="question.Unit !== 0">{{question.Unit !== 4 ? $fd('ValueUnit', question.Unit) : question.CustomUnit}}</template>
<template slot="append" v-else-if="question.ValueType === 2">%</template>
</el-input>
<el-input
type="number"
v-if="question.Type === 'number' && question.DataSource === 1"
onblur="value=parseInt(value).toFixed(parseInt(localStorage.getItem('digitPlaces')))"
:disabled="question.DataSource === 1"
v-model="questionForm[question.Id]"
>
<template slot="append" v-if="question.Unit !== 0">{{question.Unit !== 4 ? $fd('ValueUnit', question.Unit) : question.CustomUnit}}</template>
<template slot="append" v-else-if="question.ValueType === 2">%</template>
</el-input>
<!-- 上传图像 -->
<el-upload
v-if="question.Type==='upload'"
:action="accept"
:limit="question.ImageCount"
:on-preview="handlePictureCardPreview"
:before-upload="handleBeforeUpload"
:http-request="uploadScreenshot"
list-type="picture-card"
:on-remove="handleRemove"
:file-list="fileList"
:class="{disabled:fileList.length >= question.ImageCount}"
>
<i slot="default" class="el-icon-plus" />
<div slot="file" slot-scope="{file}">
<img
class="el-upload-list__item-thumbnail"
:src="file.url"
alt=""
>
<span class="el-upload-list__item-actions">
<span
class="el-upload-list__item-preview"
@click="handlePictureCardPreview(file)"
>
<i class="el-icon-zoom-in" />
</span>
<span
v-if="readingTaskState < 2"
class="el-upload-list__item-delete"
@click="handleRemove(file)"
>
<i class="el-icon-delete" />
</span>
</span>
</div>
</el-upload>
<el-dialog
v-if="question.Type==='upload'"
append-to-body
:visible.sync="imgVisible"
width="600px"
>
<el-image :src="imageUrl" width="100%">
<div slot="placeholder" class="image-slot">
加载中<span class="dot">...</span>
</div>
</el-image>
</el-dialog>
</el-form-item>
</template>
<template v-if="question.Childrens && question.Childrens.length>0 && question.Type !== 'table'">
<CustomizeQuestionFormItem
v-for="(item) in question.Childrens"
:key="item.Id"
:question="item"
:IsBaseline="IsBaseline"
:reading-task-state="readingTaskState"
:question-form="questionForm"
:visit-task-id="visitTaskId"
:criterion-id="criterionId"
:CalculationList="CalculationList"
@formItemNumberChange="formItemNumberChange"
@setFormItemData="setFormItemData"
@resetFormItemData="resetFormItemData"
/>
</template>
<base-model :config="addOrEdit"
class="my_dialog"
:close-on-click-modal="false"
width="400px"
append-to-body
>
<template slot="dialog-body">
<el-form
ref="tableQsForm"
:model="QuestionsForm"
v-loading="loading"
size="small"
>
<QuestionTableFormItem
v-for="(item) in QuestionsList"
:key="item.Id"
:question="item"
:IsBaseline="IsBaseline"
:reading-task-state="readingTaskState"
:question-form="QuestionsForm"
:visit-task-id="visitTaskId"
:criterion-id="criterionId"
:type="addOrEdit.type"
:CalculationList="CalculationList"
@formItemTableNumberChange="formItemTableNumberChange"
@setFormItemData="setFormItemData"
@resetFormItemData="resetFormItemData"
@setFormTableItemData="setFormTableItemData"
/>
</el-form>
</template>
<template slot="dialog-footer">
<el-button
size="small"
@click="addOrEdit.visible = false"
>
{{ $t('common:button:cancel') }}
</el-button>
<!-- 保存 -->
<el-button size="small" class="my_upload_btn" @click="handleSave">
{{ $t('common:button:save') }}
</el-button>
</template>
</base-model>
</div>
</template>
<script>
import { uploadReadingAnswerImage, getTrialOrganList, deleteReadingRowAnswer, getCustomTableQuestionPreview, getQuestionCalculateRelation, submitTableQuestion } from '@/api/trials'
import QuestionTableFormItem from './CustomizeQuestionTableFormItem'
import BaseModel from '@/components/BaseModel'
import DicomEvent from './../components/DicomEvent'
export default {
name: 'CustomizeQuestionFormItem',
components: { QuestionTableFormItem, BaseModel },
props: {
IsBaseline: {
type: Boolean,
required: true
},
questionForm: {
type: Object,
default() {
return {}
}
},
question: {
type: Object,
default() {
return []
}
},
criterionId: {
type: String,
required: true
},
readingTaskState: {
type: Number,
required: true
},
visitTaskId: {
type: String,
default: ''
},
CalculationList: {
type: Array,
default: []
}
},
data() {
return {
addOrEdit: { visible: false, title: '', width: '400px' },
fileList: [],
accept: '.png,.jpg,.jpeg',
imgVisible: false,
imageUrl: '',
urls: [],
organList: [],
QuestionsList: [],
QuestionsForm: {},
AnswersList: [],
loading: false,
RowIndex: 0,
RowId: null,
digitPlaces: 0
}
},
watch: {
questionForm: {
deep: true,
immediate: true,
handler(v, oldv) {
try {
if (!v[this.question.Id] || !oldv[this.question.Id]) return
} catch (e) {
}
this.formItemNumberChange(this.question.Id, false)
}
},
},
mounted() {
this.digitPlaces = localStorage.getItem('digitPlaces') ? parseInt(localStorage.getItem('digitPlaces')) : 0
if (this.question.Type === 'upload') {
if (this.questionForm[this.question.Id]) {
this.urls = this.questionForm[this.question.Id].split('|')
this.fileList = []
this.urls.map(url => {
this.fileList.push({ name: '', url: `/api/${url}` })
})
}
}
if (this.question.Type === 'table') {
// this.getQuestionCalculateRelation()
if (this.questionForm[this.question.Id]) {
this.QuestionsForm = {}
this.question.TableQuestions.Questions.forEach(v => {
if (v.Type === 'number') {
this.$set(this.QuestionsForm, v.Id, 0)
} else {
this.$set(this.QuestionsForm, v.Id, '')
}
})
}
}
},
methods: {
deleteTableCol(row, index) {
console.log(row)
this.$confirm('确定要删除该行表格问题吗?').then(() => {
const loading = this.$loading({ fullscreen: true })
var param = {
visitTaskId: this.visitTaskId,
questionId: row.Id,
rowId: this.questionForm[row.Id][index].RowId
}
deleteReadingRowAnswer(param)
.then(async res => {
if (res.IsSuccess) {
this.$message.success('删除成功')
DicomEvent.$emit('reGetQuestionAnswer')
}
loading.close()
}).catch(() => {
loading.close()
})
})
},
setFormTableItemData(id, url) {
this.$set(this.QuestionsForm, id, url)
},
handleSave() {
console.log(this.QuestionsForm)
this.$refs.tableQsForm.validate(valid => {
if (!valid) return
const loading = this.$loading({ fullscreen: true })
var answers = []
var reg = new RegExp(/^[0-9a-zA-Z]{8}-[0-9a-zA-Z]{4}-[0-9a-zA-Z]{4}-[0-9a-zA-Z]{4}-[0-9a-zA-Z]{12}$/)
for (const k in this.QuestionsForm) {
if (reg.test(k)) {
if (answers.findIndex(i => i.tableQuestionId === k) === -1) {
console.log(this.QuestionsForm)
answers.push({ tableQuestionId: k, answer: this.QuestionsForm[k] })
}
}
}
if (this.addOrEdit.type === 'edit') {
var params = {
questionId: this.question.Id,
RowIndex: parseInt(this.QuestionsForm.RowIndex),
RowId: this.QuestionsForm.RowId,
visitTaskId: this.visitTaskId,
trialId: this.$route.query.trialId,
answerList: answers,
}
} else {
var params = {
questionId: this.question.Id,
RowIndex: this.questionForm[this.question.Id].length + 1,
visitTaskId: this.visitTaskId,
trialId: this.$route.query.trialId,
answerList: answers,
}
this.QuestionsForm.RowIndex = this.questionForm[this.question.Id].length + 1
}
submitTableQuestion(params).then(async res => {
this.$message.success('保存成功!')
this.QuestionsForm.RowId = res.Result.RowId
this.save()
loading.close()
}).catch(() => { loading.close() })
})
},
renderHeader(h, obj) {
let span = document.createElement('span')
span.style.fontSize = '14px'
span.innerText = obj.column.label
document.body.appendChild(span)
obj.column.realWidth = span.getBoundingClientRect().width + 40
document.body.removeChild(span)
return h('span', obj.column.label)
},
getQuestionCalculateRelation() {
getQuestionCalculateRelation({
TrialReadingCriterionId: this.criterionId,
ReadingQuestionId: this.question.Id
}).then(res => {
this.CalculationList = res.Result
})
},
save() {
console.log(this.$refs.tableQsForm, this.QuestionsForm)
this.$refs['tableQsForm'].validate((valid) => {
console.log(valid)
if (!valid) return
if (this.addOrEdit.type === 'add') {
this.AnswersList.push(this.QuestionsForm)
} else {
var index = this.AnswersList.findIndex(v => v.RowId === this.QuestionsForm.RowId)
this.AnswersList.splice(index, 1, this.QuestionsForm)
}
this.$emit('setFormItemData', {key: this.question.Id, val: this.AnswersList})
this.formItemNumberChange(this.question.Id, true)
this.addOrEdit.visible = false
})
},
logic(rules, num = 0) {
try {
if (rules.CalculateQuestionList.length === 0) {
return false
}
rules.CalculateQuestionList.forEach((o, i) => {
if (i === 0) {
if (rules.CustomCalculateMark > 4) {
switch (rules.CustomCalculateMark) {
case 5:
this.questionForm[o.QuestionId].forEach((q, qi) => {
if (qi === 0) {
num = parseFloat(q[o.TableQuestionId])
} else {
num *= parseFloat(q[o.TableQuestionId])
}
})
break;
case 6:
this.questionForm[o.QuestionId].forEach((q, qi) => {
if (qi === 0) {
num = parseFloat(q[o.TableQuestionId])
} else {
num += parseFloat(q[o.TableQuestionId])
}
})
break;
case 7:
this.questionForm[o.QuestionId].forEach((q, qi) => {
if (qi === 0) {
num = parseFloat(q[o.TableQuestionId])
} else {
num += parseFloat(q[o.TableQuestionId])
}
})
num = this.questionForm[o.QuestionId].length === 0 ? 0 : num / this.questionForm[o.QuestionId].length
break;
case 8:
var arr = []
this.questionForm[o.QuestionId].forEach(q => {
arr.push(q[o.TableQuestionId])
})
num = arr.length === 0 ? 0 : Math.max(...arr)
break;
case 9:
var arr = []
this.questionForm[o.QuestionId].forEach(q => {
arr.push(q[o.TableQuestionId])
})
num = arr.length === 0 ? 0 : Math.min(...arr)
break;
}
} else {
num = parseFloat(this.questionForm[o.TableQuestionId])
}
} else {
switch (rules.CustomCalculateMark) {
case 1:
num += parseFloat(this.questionForm[o.TableQuestionId])
break;
case 2:
num -= parseFloat(this.questionForm[o.TableQuestionId])
break;
case 3:
num *= parseFloat(this.questionForm[o.TableQuestionId])
break;
case 4:
if (parseFloat(this.questionForm[o.TableQuestionId]) === 0) {
num = 0
} else {
num /= parseFloat(this.questionForm[o.TableQuestionId])
}
break;
}
}
})
} catch (e) {
console.log(e)
}
var digitPlaces = parseInt(localStorage.getItem('digitPlaces'))
if (rules.ValueType === 2) {
num = num * 100
}
return num.toFixed(digitPlaces)
},
formItemNumberChange(questionId, isTable) {
if (isTable) {
this.CalculationList.forEach((v, i) => {
var find = v.CalculateQuestionList.filter(o => {
return o.QuestionId === questionId
})
// find的自动计算值number
if (find) {
var num = this.logic(v)
if (num !== false) {
this.$emit('setFormItemData', { key: v.QuestionId, val: num })
}
}
})
} else {
this.CalculationList.forEach(v => {
var find = v.CalculateQuestionList.filter(o => {
return o.TableQuestionId === questionId
})
// find的自动计算值number
if (find) {
var num = this.logic(v)
if (num !== false) {
this.$emit('setFormItemData', { key: v.QuestionId, val: num })
}
}
})
}
// this.$emit('formItemNumberChange')
},
formItemTableNumberChange() {
this.question.TableQuestions.Questions.forEach(v => {
if (v.Type === 'number' && v.DataSource === 1) {
var CalculateQuestions = JSON.parse(v.CalculateQuestions)
var num
CalculateQuestions.forEach((o, i) => {
if (i === 0) {
num = this.QuestionsForm[o.TableQuestionId]
} else {
switch (v.CustomCalculateMark) {
case 1:
num += this.QuestionsForm[o.TableQuestionId]
break;
case 2:
num -= this.QuestionsForm[o.TableQuestionId]
break;
case 3:
num *= this.QuestionsForm[o.TableQuestionId]
break;
case 4:
num /= this.QuestionsForm[o.TableQuestionId]
break;
}
}
})
this.$set(this.QuestionsForm, v.Id, num.toString())
}
})
},
openAddTableCol(row, index) {
this.addOrEdit.visible = true
this.addOrEdit.title = row.QuestionName + '表格问题'
this.QuestionsList = row.TableQuestions.Questions
this.AnswersList = row.TableQuestions.Answers
if (!index && index !== 0) {
this.addOrEdit.type = 'add'
this.QuestionsForm = {}
} else {
this.addOrEdit.type = 'edit'
console.log(this.questionForm)
this.QuestionsForm = Object.assign({}, this.questionForm[row.Id][index])
}
},
getOrganInfoList() {
var param = {
trialId: this.$route.query.trialId,
lesionType: this.question.LesionType,
// systemCriterionId: this.criterionId,
isEnable: true
}
getTrialOrganList(param).then(res => {
this.organList = res.Result
})
},
formNumberItemChange(v) {
this.$emit('setFormItemData', { key: v.QuestionId, val: num })
},
formItemChange(v, question) {
if (question.Childrens.length > 0) {
this.resetChild(question.Childrens)
}
if (question.TableQuestionType === 1 && question.RelationQuestions.length > 0) {
var index = this.organList.findIndex(item => item[question.DataTableColumn] === v)
if (index < 0) return
var selected = this.organList[index]
this.question.RelationQuestions.map(qs => {
var val = selected[qs.DataTableColumn]
// if (typeof val === 'boolean') {
// // val = String(val)
// }
this.$emit('setFormItemData', { key: qs.Id, val: val })
})
}
},
resetChild(obj) {
obj.forEach(i => {
this.$emit('resetFormItemData', i.Id)
if (i.Childrens && i.Childrens.length > 0) {
this.resetChild(i.Childrens)
}
})
},
resetFormItemData(v) {
this.$emit('resetFormItemData', v)
},
setFormItemData(obj) {
this.$emit('setFormItemData', obj)
},
uploadScreenshot(param) {
if (!this.visitTaskId) return
const loading = this.$loading({
target: document.querySelector('.ecrf-wrapper'),
fullscreen: false,
lock: true,
text: 'Loading',
spinner: 'el-icon-loading'
})
const formData = new FormData()
formData.append('file', param.file)
uploadReadingAnswerImage(this.$route.query.trialId, this.visitTaskId, formData).then(res => {
if (res.IsSuccess) {
this.fileList.push({ url: `/api/${res.Result.Path}` })
this.urls.push(res.Result.Path)
this.$emit('setFormItemData', { key: this.question.Id, val: this.urls.length > 0 ? this.urls.join('|') : '' })
}
loading.close()
}).catch(() => {
loading.close()
})
},
handleBeforeUpload(file) {
// 检测文件类型是否符合要求
if (this.checkFileSuffix(file.name)) {
// this.fileList = []
return true
} else {
this.$alert(`必须是 ${this.accept} 格式`)
return false
}
},
checkFileSuffix(fileName) {
var index = fileName.lastIndexOf('.')
var suffix = fileName.substring(index + 1, fileName.length)
if (this.accept.toLocaleLowerCase().search(suffix.toLocaleLowerCase()) === -1) {
return false
} else {
return true
}
},
// 预览图片
handlePictureCardPreview(file) {
this.imageUrl = file.url
this.imgVisible = true
},
// 删除图片
handleRemove(file, fileList) {
this.imageUrl = ''
this.fileList.splice(this.fileList.findIndex(f => f.url === file.url), 1)
this.urls.splice(this.fileList.findIndex(f => f === file.url), 1)
this.$emit('setFormItemData', { key: this.question.Id, val: this.urls.length > 0 ? this.urls.join('|') : '' })
}
}
}
</script>
<style lang="scss" scoped>
.my_dialog{
.criterion-form-item{
width: 100%;
>>>.el-form-item__content{
width: auto;
}
}
}
.criterion-form-item{
>>>.criterion-form-item .el-form-item{
display: block;
.el-form-item__label{
display: block;
color: #c8c8c8;
float: none;
text-align: left;
}
.el-input--small .el-input__inner{
display: block;
}
}
.el-form-item{
display: flex;
flex-direction: row;
align-items: flex-start;
}
>>>.el-form-item__content{
}
.el-input{
width:100%;
}
.mb{
margin-bottom: 0px;
}
.disabled{
>>>.el-upload--picture-card {
display: none;
}
}
.uploadWrapper{
display: flex;
flex-direction: column;
align-items: flex-start;
}
}
>>>.el-table__body-wrapper::-webkit-scrollbar{
height: 10px!important;
}
>>>.el-table__fixed-right::before{
display: none;
}
</style>

View File

@ -0,0 +1,162 @@
<template>
<div style="display: flex;justify-content: center">
<div style="width: 600px;text-align: center;border: 1px solid #e6e6e6;margin-top:40px;padding:10px;">
<div class="trial-myinfo-head" style="font-size: 30px;line-height: 120px;">
<!-- 首次登录修改密码 -->
{{ $t('recompose:title:init') }}
</div>
<el-form ref="passwordForm" v-loading="loading" label-position="right" :model="password" :rules="passwordFormRules" label-width="120px">
<!-- 用户名 -->
<el-form-item :label="$t('recompose:form:userName')" prop="NewUserName">
<el-input v-model="password.NewUserName" />
</el-form-item>
<!-- 旧密码 -->
<el-form-item :label="$t('recompose:form:oldPassword')" prop="OldPassWord">
<el-input v-model="password.OldPassWord" type="password" show-password auto-complete="new-password" />
</el-form-item>
<!-- 新密码 -->
<el-form-item :label="$t('recompose:form:newPassword')" prop="NewPassWord">
<el-input v-model="password.NewPassWord" type="password" show-password auto-complete="new-password" />
</el-form-item>
<!-- 确认密码 -->
<el-form-item :label="$t('recompose:form:confirmPassword')" prop="ConfirmPassWord">
<el-input v-model="password.ConfirmPassWord" type="password" show-password auto-complete="new-password" />
</el-form-item>
<el-form-item style="text-align:right">
<!-- 取消 -->
<el-button
size="small"
@click="cancel"
>
{{ $t('recompose:button:cancel') }}
</el-button>
<!-- 保存 -->
<el-button
type="primary"
size="small"
@click="save"
>
{{ $t('recompose:button:save') }}
</el-button>
</el-form-item>
</el-form>
</div>
</div>
</template>
<script>
import { modifyPassword } from '@/api/admin.js'
import md5 from 'js-md5'
import {mapMutations} from "vuex";
export default {
data() {
return {
password: {
NewUserName: null
},
passwordFormRules: {
NewUserName: [{ required: true, message: this.$t('common:ruleMessage:specify'), trigger: 'blur' }],
OldPassWord: [{ required: true, message: this.$t('common:ruleMessage:specify'), trigger: 'blur' }],
NewPassWord: [
{ required: true, message: this.$t('common:ruleMessage:specify'), trigger: 'blur' },
{
required: true,
trigger: 'blur',
validator: this.$validatePassword
},
],
ConfirmPassWord: [
{ required: true, message: this.$t('common:ruleMessage:specify'), trigger: 'blur' },
]
},
userId: null,
loading: false
}
},
mounted() {
this.$set(this.password, 'NewUserName', this.$route.query.userName)
if (!this.password.NewUserName) {
// 该页面只提供给首次进入系统的用户使用
this.$alert(this.$t('recompose:message:warning'))
}
},
methods: {
...mapMutations({ setLanguage: 'lang/setLanguage' }),
async logout() {
var loginType = zzSessionStorage.getItem('loginType')
await this.$store.dispatch('user/logout')
if (loginType) {
this.$router.push(`/login?loginType=${loginType}`)
} else {
this.$router.push(`/login`)
}
this.$i18n.locale = 'zh'
this.setLanguage('zh')
this.$updateDictionary()
},
save() {
this.$refs.passwordForm.validate(valid => {
if (valid) {
if (this.password.NewPassWord !== this.password.ConfirmPassWord) {
// 两次密码输入不一致
this.$alert(this.$t('recompose:message:passwordDiffer'))
return
}
const param = {
NewUserName: this.password.NewUserName,
NewPassWord: md5(this.password.NewPassWord),
OldPassWord: md5(this.password.OldPassWord)
}
this.loading = true
modifyPassword(param).then(res => {
this.loading = false
if (res.IsSuccess) {
// 修改成功,请重新登录账号
this.$message.success(this.$t('recompose:message:updatedSuccessfully'))
setTimeout(() => {
this.logout()
}, 500)
}
}).catch(() => { this.loading = false })
}
})
},
cancel() {
this.$refs['passwordForm'].resetFields()
}
}
}
</script>
<style>
.reset-wrapper {
padding: 20px;
}
.reset-wrapper .el-page-header {
line-height: 50px;
border: 1px solid #ebeef5;
border-radius: 4px;
background-color: #fff;
}
.reset-wrapper .box-wrapper {
width: 60%;
margin: 20px auto;
padding: 10px;
color: #303133;
}
</style>
<style scoped>
>>>.is-error{
margin-bottom: 40px;
}
input:-webkit-autofill {
-webkit-text-fill-color: #ededed !important;
box-shadow: 0 0 0px 1000px transparent inset !important;
background-color:transparent;
background-image: none;
transition: background-color 50000s ease-in-out 0s;
}
input {
background-color:transparent;
caret-color: #fff;
}
</style>

View File

@ -0,0 +1,261 @@
<template>
<el-form
ref="sysTemplateFrom"
v-loading="loading"
:model="form"
label-width="90px"
size="small"
:rules="rules"
class="upload-temporary-file"
>
<div class="base-dialog-body">
<el-form-item label="Code" prop="Code">
<el-input v-model="form.Code" />
</el-form-item>
<el-form-item label="业务场景: " prop="BusinessScenarioEnum">
<el-select
v-model="form.BusinessScenarioEnum"
style="width:100%;"
size="small"
filterable
disabled
>
<el-option
v-for="item of $d.Common_File_BusinessScenario"
:key="item.id"
:label="item.label"
:value="item.value"
/>
</el-select>
</el-form-item>
<el-form-item label="文件: ">
<div class="upload-container">
<el-upload
class="upload-demo"
action
accept=".xls,.xlsx,.doc,.docx"
:before-upload="beforeUpload"
:http-request="handleUploadFile"
:on-preview="handlePreview"
:on-remove="handleRemoveFile"
:show-file-list="true"
:file-list="fileList"
:limit="1"
:on-exceed="handleExceed"
:disabled="!form.FileTypeEnum || !form.BusinessScenarioEnum"
>
<el-button size="small" type="primary" :disabled="!form.FileTypeEnum || !form.BusinessScenarioEnum">Select</el-button>
<span
slot="tip"
style="margin-left:10px;"
class="el-upload__tip"
>
(must be in xlsx/xls/doc/docx format)
</span>
</el-upload>
</div>
</el-form-item>
<el-form-item label="文件名" prop="Name">
<el-input v-model="form.Name" />
</el-form-item>
<el-form-item label="阅片标准: " prop="CriterionTypeEnum">
<el-select
v-model="form.CriterionTypeEnum"
style="width:100%;"
size="small"
filterable
clearable
>
<el-option
v-for="item of $d.CriterionType"
:key="item.id"
:label="item.label"
:value="item.value"
/>
</el-select>
</el-form-item>
<el-form-item v-if="form.Id !== ''" label="是否废除: ">
<el-radio-group v-model="form.IsDeleted">
<el-radio v-for="item of $d.YesOrNo" :label="item.value">{{ item.label }}</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item label="描述">
<el-input
v-model="form.Description"
type="textarea"
:autosize="{ minRows: 2, maxRows: 4}"
placeholder="请输入内容"
/>
</el-form-item>
</div>
<div class="base-dialog-footer" style="text-align:right;margin-top:10px;">
<el-form-item style="text-align:right;">
<el-button size="small" type="primary" :disabled="!form.FileTypeEnum || !form.BusinessScenarioEnum" @click="handleSave">Save</el-button>
</el-form-item>
</div>
</el-form>
</template>
<script>
import { addOrUpdateCommonDocument, uploadCommonDoc } from '@/api/dictionary'
import { getBasicDataSelects } from '@/api/dictionary/dictionary'
export default {
name: 'TemplateForm',
props: {
data: {
type: Object,
default() { return {} }
},
isDistinguishCriteria: {
type: Boolean,
required: true
}
},
data() {
return {
form: {
Id: '',
Code: '',
FileTypeEnum: null,
BusinessScenarioEnum: null,
CriterionTypeEnum: null,
Name: '',
Path: '',
Description: '',
IsDeleted: false
},
rules: {
Code: [{ required: true, message: 'Please specify', trigger: ['blur'] }],
Name: [{ required: true, message: 'Please specify', trigger: ['blur'] }],
CriterionTypeEnum: [{ required: this.isDistinguishCriteria, message: 'Please select', trigger: ['blur'] }],
BusinessScenarioEnum: [{ required: true, message: 'Please select', trigger: ['blur'] }]
},
fileList: [],
loading: false,
dictionaryList: {}
}
},
mounted() {
this.initForm()
},
methods: {
async initForm() {
await this.getDicData()
if (Object.keys(this.data).length > 0) {
if (this.data.Path) {
this.fileList = [
{
name: this.data.Name,
path: this.data.Path,
type: this.data.Type
}
]
}
for (const k in this.form) {
if (this.data.hasOwnProperty(k)) {
this.form[k] = this.data[k]
}
}
}
},
// 获取文件类型下拉框
getDicData() {
this.loading = true
getBasicDataSelects(['Common_File_ModuleType', 'Common_File_Type']).then(res => {
this.dictionaryList = { ...res.Result }
this.loading = false
}).catch(() => {
this.loading = false
})
},
beforeUpload(file) {
// 检测文件类型是否符合要求
if (this.checkFileSuffix(file.name)) {
this.fileList = []
return true
} else {
this.$alert('必须是word/excel格式')
return false
}
},
handleUploadFile(param) {
this.loading = true
uploadCommonDoc(param.file).then(res => {
this.loading = false
if (res.IsSuccess) {
this.form.Path = res.Result.FilePath
this.form.Name = param.file.name
const file = { name: this.form.Name, path: this.form.Path }
this.fileList.push(file)
}
}).catch(() => {
this.loading = false
})
},
handleSave() {
this.$refs.sysTemplateFrom.validate(valid => {
if (!valid) return
if (!this.form.Name) {
this.$alert('Please select file.')
return
}
this.loading = true
addOrUpdateCommonDocument(this.form).then(res => {
this.loading = false
this.$emit('closeDialog')
this.$emit('getList')
this.$message.success('保存成功!')
}).catch(() => {
this.loading = false
})
})
},
handleRemoveFile() {
this.fileList = []
this.form.Path = ''
this.form.Name = ''
},
handlePreview(file) {
if (file.fullPath) {
window.open(file.fullPath, '_blank')
}
},
handleExceed(files, fileList) {
this.$message.warning(`Upload is currently limited to 1 file`)
},
checkFileSuffix(fileName) {
var typeArr = ['xls', 'xlsx', 'doc', 'docx']
var extendName = fileName.substring(fileName.lastIndexOf('.') + 1).toLocaleLowerCase()
if (typeArr.indexOf(extendName) !== -1) {
return true
} else {
return false
}
}
}
}
</script>
<style lang="scss">
.upload-temporary-file{
.upload-container .el-upload--text {
border: none;
width: 80px;
height: 40px;
}
.upload-container .el-input--small {
margin-bottom: 5px;
}
.upload-container .el-icon-circle-check {
color: #428bca;
font-size: 13px;
}
.account_item_clear{
.el-tag__close{
display: none !important;
}
}
}
</style>

View File

@ -0,0 +1,499 @@
<template>
<BaseContainer>
<template slot="search-container">
<el-form :inline="true">
<!-- 受试者编号 -->
<el-form-item label="受试者编号">
<el-input
v-model="searchData.SubjectCode"
style="width:130px;"
clearable
/>
</el-form-item>
<el-form-item style="margin-bottom:10px" label="任务状态">
<el-select v-model="searchData.TaskState" clearable style="width:120px;">
<el-option v-for="item of $d.TaskState" :key="'TaskState' + item.label" :value="item.value" :label="item.label" />
</el-select>
</el-form-item>
<el-form-item style="margin-bottom:10px" label="任务类型">
<el-select v-model="searchData.ReadingCategory" clearable style="width:120px;">
<el-option v-for="item of $d.ReadingCategory" :key="'ReadingCategory' + item.label" :value="item.value" :label="item.label" />
</el-select>
</el-form-item>
<el-form-item>
<!-- 查询 -->
<el-button type="primary" icon="el-icon-search" @click="handleSearch">
{{ $t('common:button:search') }}
</el-button>
<!-- 重置 -->
<el-button type="primary" icon="el-icon-refresh-left" @click="handleReset">
{{ $t('common:button:reset') }}
</el-button>
</el-form-item>
</el-form>
</template>
<template slot="main-container">
<el-table
v-adaptive="{bottomOffset:60}"
v-loading="loading"
:data="list"
stripe
height="100"
@sort-change="handleSortChange"
>
<el-table-column type="index" width="40" align="left" />
<!-- 是否加急 -->
<el-table-column
prop="IsUrgent"
min-width="100"
label="是否加急"
sortable="custom"
show-overflow-tooltip
>
<template slot-scope="scope">
<el-tag v-if="scope.row.IsUrgent" type="danger">{{ $fd('YesOrNo', scope.row.IsUrgent) }}</el-tag>
<el-tag v-else type="primary">{{ $fd('YesOrNo', scope.row.IsUrgent) }}</el-tag>
</template>
</el-table-column>
<!-- 任务编号 -->
<el-table-column
prop="TaskCode"
min-width="100"
label="任务编号"
sortable="custom"
show-overflow-tooltip
/>
<!-- 受试者编号 -->
<el-table-column
prop="SubjectCode"
min-width="100"
label="受试者编号"
sortable="custom"
show-overflow-tooltip
/>
<!-- 盲态访视名称 -->
<el-table-column
prop="TaskBlindName"
min-width="100"
label="盲态访视名称"
sortable="custom"
show-overflow-tooltip
/>
<el-table-column
prop="TaskState"
label="任务状态"
min-width="100"
sortable="custom"
show-overflow-tooltip
>
<template slot-scope="scope">
<el-tag v-if="scope.row.TaskState === 0" type="primary">{{ $fd('TaskState', scope.row.TaskState) }}</el-tag>
<el-tag v-if="scope.row.TaskState === 1" type="info">{{ $fd('TaskState', scope.row.TaskState) }}</el-tag>
<el-tag v-if="scope.row.TaskState === 3" type="danger">{{ $fd('TaskState', scope.row.TaskState) }}</el-tag>
<el-tag v-if="scope.row.TaskState === 4" type="warning">{{ $fd('TaskState', scope.row.TaskState) }}</el-tag>
</template>
</el-table-column>
<!-- 任务类型 -->
<el-table-column
prop="ReadingCategory"
min-width="100"
label="任务类型"
sortable="custom"
show-overflow-tooltip
>
<template slot-scope="scope">
<el-tag v-if="scope.row.ReadingCategory === 1" type="primary">{{ $fd('ReadingCategory', scope.row.ReadingCategory) }}</el-tag>
<el-tag v-if="scope.row.ReadingCategory === 2" type="info">{{ $fd('ReadingCategory', scope.row.ReadingCategory) }}</el-tag>
<el-tag v-if="scope.row.ReadingCategory === 4" type="danger">{{ $fd('ReadingCategory', scope.row.ReadingCategory) }}</el-tag>
<el-tag v-if="scope.row.ReadingCategory === 5" type="warning">{{ $fd('ReadingCategory', scope.row.ReadingCategory) }}</el-tag>
</template>
</el-table-column>
<!-- 阅片状态 -->
<!-- <el-table-column
prop="TaskAllocationState"
min-width="100"
label="阅片状态"
show-overflow-tooltip
>
<template slot-scope="scope">
<el-tag v-if="scope.row.ReadingTaskState === 2" type="primary">{{ $fd('ReadingTaskState', scope.row.ReadingTaskState) }}</el-tag>
<el-tag v-else type="danger">{{ $fd('ReadingTaskState', scope.row.ReadingTaskState) }}</el-tag>
</template>
</el-table-column> -->
<!-- 分配时间 -->
<el-table-column
prop="AllocateTime"
min-width="100"
label="分配时间"
sortable="custom"
show-overflow-tooltip
/>
<!-- 签名时间 -->
<el-table-column
prop="SignTime"
min-width="100"
label="签名时间"
sortable="custom"
show-overflow-tooltip
/>
<el-table-column
prop="ReReadingApplyState"
label="申请状态"
min-width="140"
sortable="custom"
show-overflow-tooltip
>
<template slot-scope="scope">
<el-tag v-if="scope.row.ReReadingApplyState === 4 || scope.row.ReReadingApplyState === 5" type="danger">{{ $fd('ReReadingApplyState', scope.row.ReReadingApplyState) }}</el-tag>
<el-tag v-if="scope.row.ReReadingApplyState === 2" type="primary">{{ $fd('ReReadingApplyState', scope.row.ReReadingApplyState) }}</el-tag>
<el-tag v-if="scope.row.ReReadingApplyState === 3" type="warning">{{ $fd('ReReadingApplyState', scope.row.ReReadingApplyState) }}</el-tag>
</template>
</el-table-column>
<el-table-column
:label="$t('common:action:action')"
width="250"
fixed="right"
>
<template slot-scope="scope">
<!-- 查看 -->
<el-button
circle
title="查看"
icon="el-icon-view"
@click="handleReadImage(scope.row)"
/>
<!-- 申请重阅 -->
<el-button
:disabled="scope.row.TaskState !== 0 || scope.row.ReadingTaskState !== 2 || scope.row.ReReadingApplyState === 2 || scope.row.ReReadingApplyState === 4 || scope.row.ReReadingApplyState === 5"
icon="el-icon-collection"
circle
title="申请重阅"
@click="openApplyReReading(scope.row)"
/>
<el-button
:disabled="!scope.row.ReReadingApplyState"
icon="el-icon-toilet-paper"
circle
title="退回重阅历史"
@click="reReadingHistory(scope.row, 1)"
/>
</template>
</el-table-column>
</el-table>
<!-- 分页组件 -->
<pagination class="page" :total="total" :page.sync="searchData.PageIndex" :limit.sync="searchData.PageSize" @pagination="getList" />
</template>
<el-dialog
v-if="ApplyforReasonVisible"
title="申请重阅"
:visible.sync="ApplyforReasonVisible"
width="1460px"
append-to-body
:close-on-click-modal="false"
custom-class="base-dialog-wrapper"
>
<el-form
ref="reasonForm"
:rules="rules"
:model="ApplyforReasonForm"
class="demo-ruleForm"
size="small"
label-width="150px"
>
<el-divider content-position="left">申请原因</el-divider>
<el-form-item
label="申请原因"
prop="Type"
:rules="[
{ required: true, message: $t('common:ruleMessage:select')},
]"
>
<el-radio-group v-model="ApplyforReasonForm.Type">
<!-- 问题已解决 -->
<el-radio :label="1">阅片记录错误</el-radio>
<!-- 问题无法解决强制关闭质疑 -->
<el-radio :label="2">其他</el-radio>
</el-radio-group>
</el-form-item>
<!-- 原因 -->
<el-form-item v-if="ApplyforReasonForm.Type === 2" label="备注" prop="RequestReReadingReason">
<el-input
v-model="ApplyforReasonForm.RequestReReadingReason"
type="textarea"
style="width: 400px"
:autosize="{ minRows: 2, maxRows: 4}"
:placeholder="$t('common:ruleMessage:specify')"
maxlength="500"
show-word-limit
/>
</el-form-item>
<el-form-item
label="是否复制阅片表单"
prop="IsCopyOrigenalForms"
:rules="[
{ required: true, message: $t('common:ruleMessage:select')},
]"
>
<el-radio-group v-model="ApplyforReasonForm.IsCopyOrigenalForms">
<!-- 问题已解决 -->
<el-radio :label="true">是</el-radio>
<!-- 问题无法解决强制关闭质疑 -->
<el-radio :label="false">否</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item
v-if="ApplyforReasonForm.IsCopyOrigenalForms"
label="是否复制后续表单"
prop="IsCopyFollowForms"
:rules="[
{ required: true, message: $t('common:ruleMessage:select')},
]"
>
<el-radio-group v-model="ApplyforReasonForm.IsCopyFollowForms">
<!-- 问题已解决 -->
<el-radio :label="true">是</el-radio>
<!-- 问题无法解决强制关闭质疑 -->
<el-radio :label="false">否</el-radio>
</el-radio-group>
</el-form-item>
</el-form>
<el-divider content-position="left">影响的任务列表</el-divider>
<div style="padding:10px;border: 1px solid #e0e0e0;max-height:650px;overflow-y: auto;">
<el-table
v-loading="loading"
:data="InfluenceTaskList"
stripe
height="100"
style="min-height: 400px;"
>
<el-table-column
prop="TaskCode"
label="任务编号"
min-width="100"
show-overflow-tooltip
/>
<el-table-column
prop="TrialSiteCode"
label="中心编号"
min-width="100"
show-overflow-tooltip
/>
<el-table-column
prop="SubjectCode"
label="受试者编号"
min-width="120"
show-overflow-tooltip
/>
<el-table-column
prop="TaskBlindName"
label="访视盲态名称"
min-width="140"
show-overflow-tooltip
/>
<el-table-column
prop="TaskState"
label="任务状态"
min-width="100"
show-overflow-tooltip
>
<template slot-scope="scope">
<el-tag v-if="scope.row.TaskState === 0" type="primary">{{ $fd('TaskState', scope.row.TaskState) }}</el-tag>
<el-tag v-if="scope.row.TaskState === 1" type="info">{{ $fd('TaskState', scope.row.TaskState) }}</el-tag>
<el-tag v-if="scope.row.TaskState === 3" type="danger">{{ $fd('TaskState', scope.row.TaskState) }}</el-tag>
<el-tag v-if="scope.row.TaskState === 4" type="warning">{{ $fd('TaskState', scope.row.TaskState) }}</el-tag>
</template>
</el-table-column>
<el-table-column
prop="ReadingCategory"
min-width="100"
label="任务类型"
show-overflow-tooltip
>
<template slot-scope="scope">
<el-tag v-if="scope.row.ReadingCategory === 1" type="primary">{{ $fd('ReadingCategory', scope.row.ReadingCategory) }}</el-tag>
<el-tag v-if="scope.row.ReadingCategory === 2" type="info">{{ $fd('ReadingCategory', scope.row.ReadingCategory) }}</el-tag>
<el-tag v-if="scope.row.ReadingCategory === 4" type="danger">{{ $fd('ReadingCategory', scope.row.ReadingCategory) }}</el-tag>
<el-tag v-if="scope.row.ReadingCategory === 5" type="warning">{{ $fd('ReadingCategory', scope.row.ReadingCategory) }}</el-tag>
</template>
</el-table-column>
<el-table-column
prop="ReadingTaskState"
label="阅片状态"
min-width="100"
show-overflow-tooltip
>
<template slot-scope="scope">
<el-tag v-if="scope.row.ReadingTaskState === 2" type="primary">{{ $fd('ReadingTaskState', scope.row.ReadingTaskState) }}</el-tag>
<el-tag v-else type="danger">{{ $fd('ReadingTaskState', scope.row.ReadingTaskState) }}</el-tag>
</template>
</el-table-column>
<el-table-column
prop="OptType"
label="生效后影响"
min-width="160"
show-overflow-tooltip
>
<template slot-scope="scope">
<el-tag v-if="scope.row.OptType === 0" type="primary">{{ $fd('ReReadingOrBackOptType', scope.row.OptType) }}</el-tag>
<el-tag v-if="scope.row.OptType === 1" type="danger">{{ $fd('ReReadingOrBackOptType', scope.row.OptType) }}</el-tag>
<el-tag v-if="scope.row.OptType === 2" type="warning">{{ $fd('ReReadingOrBackOptType', scope.row.OptType) }}</el-tag>
</template>
</el-table-column>
<el-table-column
prop="UserName"
label="阅片人"
min-width="160"
show-overflow-tooltip
>
<template v-if="scope.row.DoctorUser" slot-scope="scope">
{{ scope.row.DoctorUser.UserName }}({{ scope.row.DoctorUser.FullName }})
</template>
</el-table-column>
</el-table>
</div>
<div slot="footer" class="dialog-footer">
<!-- 取消 -->
<el-button :disabled="btnLoading" size="small" type="primary" @click="ApplyforReasonVisible = false;ApplyforReasonForm = { Type: null, RequestReReadingReason: null }">
{{ $t('common:button:cancel') }}
</el-button>
<!-- 保存 -->
<el-button :loading="btnLoading" size="small" type="primary" @click="applyReReading">
{{ $t('common:button:save') }}
</el-button>
</div>
</el-dialog>
</BaseContainer>
</template>
<script>
import { getIRHaveReadTaskList } from '@/api/trials'
import { applyReReading, getReReadingOrBackInfluenceTaskList } from '@/api/trials/reading'
import BaseContainer from '@/components/BaseContainer'
import Pagination from '@/components/Pagination'
import { getToken } from '@/utils/auth'
const searchDataDefault = () => {
return {
SubjectCode: '',
SortField: '',
PageIndex: 1,
PageSize: 20,
TaskState: null,
ReadingCategory: null
}
}
export default {
name: 'ReadTask',
components: { BaseContainer, Pagination },
data() {
return {
searchData: searchDataDefault(),
list: [],
total: 0,
loading: false,
btnLoading: false,
trialId: '',
ApplyforReasonVisible: false,
ApplyforReasonForm: {
Type: null,
RequestReReadingReason: null,
IsCopyOrigenalForms: null,
IsCopyFollowForms: null
},
InfluenceTaskList: [],
rules: {
Type: [{ required: true, message: this.$t('common:ruleMessage:specify'), trigger: 'blur' }],
IsCopyOrigenalForms: [{ required: true, message: this.$t('common:ruleMessage:specify'), trigger: 'blur' }],
RequestReReadingReason: [{ required: true, message: this.$t('common:ruleMessage:specify'), trigger: 'blur' }]
}
}
},
mounted() {
this.trialId = this.$route.query.trialId
this.getList()
},
methods: {
// 重阅历史
reReadingHistory(row) {
this.$router.push({ path: `/trials/trials-panel/reading/rereadTask?trialId=${this.$route.query.trialId}&trialCode=${this.$route.query.trialCode}&researchProgramNo=${this.$route.query.researchProgramNo}&SiteId=${row.SiteId}&SubjectCode=${row.SubjectCode}&TaskName=${row.TaskName}` })
},
getList() {
this.loading = true
this.searchData.TrialId = this.trialId
getIRHaveReadTaskList(this.searchData).then(res => {
this.list = res.Result.CurrentPageData
this.total = res.Result.TotalCount
this.loading = false
}).catch(() => { this.loading = false })
},
handleSearch() {
this.searchData.PageIndex = 1
this.getList()
},
handleReset() {
this.searchData = searchDataDefault()
this.getList()
},
handleReadImage(row) {
var token = getToken()
const routeData = this.$router.resolve({
path: `/noneDicomReading?subjectId=${row.SubjectId}&trialId=${row.TrialId}&visitTaskId=${row.Id}&TokenKey=${token}`
})
window.open(routeData.href, '_blank')
},
openApplyReReading(row) {
this.rowData = { ...row }
this.loading = true
getReReadingOrBackInfluenceTaskList(row.Id, true).then(res => {
this.InfluenceTaskList = res.Result
this.ApplyforReasonVisible = true
this.ApplyforReasonForm = {
Type: null,
Remake: null
}
this.loading = false
}).catch(() => { this.loading = false })
},
// 申请重阅
applyReReading() {
this.$refs.reasonForm.validate((valid) => {
if (!valid) return
this.loading = true
this.btnLoading = true
var params = {
TaskIdList: [
this.rowData.Id
],
TrialId: this.$route.query.trialId,
IsCopyOrigenalForms: this.ApplyforReasonForm.IsCopyOrigenalForms,
IsCopyFollowForms: this.ApplyforReasonForm.IsCopyFollowForms,
RequestReReadingType: 1,
RequestReReadingReason: this.ApplyforReasonForm.Type === 2 ? this.ApplyforReasonForm.RequestReReadingReason : '阅片记录错误'
}
applyReReading(params).then(res => {
this.loading = false
this.btnLoading = false
this.$message.success('申请重阅成功')
this.getList()
this.ApplyforReasonVisible = false
}).catch(() => {
this.loading = false
this.btnLoading = false
})
})
},
// 排序
handleSortChange(column) {
if (column.order === 'ascending') {
this.searchData.Asc = true
} else {
this.searchData.Asc = false
}
this.searchData.SortField = column.prop
this.searchData.PageIndex = 1
this.getList()
}
}
}
</script>

View File

@ -0,0 +1,427 @@
<template>
<el-form v-if="globalForm.taskList.length > 0" ref="globalRuleForm" :model="globalForm">
<el-table
v-loading="loading"
:data="globalForm.taskList"
>
<!-- 访视名称 -->
<el-table-column
prop="BlindName"
:label="$t('trials:globalReview:table:visitName')"
show-overflow-tooltip
width="150"
/>
<!-- 评估结果 -->
<el-table-column
:label="$t('trials:globalReview:table:evaluationRes')"
align="center"
prop=""
>
<template>
<el-table-column
v-for="(qs,index) in globalInfo.evaluationQsList"
:key="index"
prop=""
:label="qs"
show-overflow-tooltip
width="150"
>
<template slot-scope="scope">
<div v-if="scope.row.BeforeQuestionList.length>index && scope.row.BeforeQuestionList[index].Answer" :style="{color: scope.row.BeforeQuestionList[index].IsGlobalAnswer ? '#f66' : null}">
<span v-if="scope.row.BeforeQuestionList[index].DictionaryCode">
{{ $fd(scope.row.BeforeQuestionList[index].DictionaryCode,parseInt(scope.row.BeforeQuestionList[index].Answer)) }}
</span>
<span v-else>{{ scope.row.BeforeQuestionList[index].Answer }}</span>
</div>
</template>
</el-table-column>
</template>
</el-table-column>
<!-- 是否同意访视结果 -->
<el-table-column
prop=""
:label="$t('trials:globalReview:table:isAgreeEvaluationRes')"
show-overflow-tooltip
width="170"
>
<template slot-scope="scope">
<div v-if="readingTaskState<2 ">
<el-form-item
:prop="`${scope.$index}${scope.row.AgreeOrNot[0].GlobalAnswerType}`"
label=""
:rules="[
{ required:true,message: $t('common:ruleMessage:select'), trigger: ['change','blur']},
]"
>
<el-radio-group
v-model="globalForm[`${scope.$index}${scope.row.AgreeOrNot[0].GlobalAnswerType}`]"
@change="handleAgreeOrNotChange(scope.$index,scope.row.AgreeOrNot[0].GlobalAnswerType)"
>
<el-radio
v-for="item of $d.ReadingYesOrNo"
:key="'AgreeOrNot' + item.value"
:label="String(item.value)"
>
{{ item.label }}
</el-radio>
</el-radio-group>
</el-form-item>
</div>
<el-tag v-else-if="scope.row.AgreeOrNot.length > 0 && parseInt(scope.row.AgreeOrNot[0].Answer) === 1" type="primary">
{{ $fd('ReadingYesOrNo',parseInt(scope.row.AgreeOrNot[0].Answer)) }}
</el-tag>
<el-tag v-else-if="scope.row.AgreeOrNot.length > 0 && parseInt(scope.row.AgreeOrNot[0].Answer) === 0" type="danger"> {{ $fd('ReadingYesOrNo',parseInt(scope.row.AgreeOrNot[0].Answer)) }}</el-tag>
<span v-else />
</template>
</el-table-column>
<!-- 调整后结果 -->
<el-table-column
:label="$t('trials:globalReview:table:adjustedRes')"
align="center"
prop=""
>
<template>
<el-table-column
v-for="(qs,index) in globalInfo.adjustedQsList"
:key="index"
prop=""
:label="qs"
show-overflow-tooltip
:min-width="index === 3 ? '200' : '200'"
>
<template slot-scope="scope">
<div v-if="readingTaskState<2 ">
<span v-if="(scope.row.IsBaseLine && scope.row.AfterQuestionList[index].LimitEdit === 2) || (!scope.row.IsBaseLine && scope.row.AfterQuestionList[index].LimitEdit === 1)">
{{ $fd(scope.row.AfterQuestionList[index].DictionaryCode, parseInt(scope.row.AfterQuestionList[index].VisitAnswer)) }}
</span>
<el-form-item
v-else
style="margin-bottom: 0;"
:prop="`${scope.$index}${scope.row.AfterQuestionList[index].QuestionId?scope.row.AfterQuestionList[index].QuestionId:scope.row.AfterQuestionList[index].GlobalAnswerType}`"
label=""
:rules="[
{ required:parseInt(globalForm[`${scope.$index}${scope.row.AgreeOrNot[0].GlobalAnswerType}`]) === 0,message: $t('common:ruleMessage:specify'), trigger: ['change','blur']},
]"
>
<label v-if="parseInt(globalForm[`${scope.$index}${scope.row.AgreeOrNot[0].GlobalAnswerType}`]) === 0" />
<!-- 裁判问题 -->
<template v-if="scope.row.AfterQuestionList[index].GlobalAnswerType === 0 ">
<el-tooltip class="item" effect="dark" v-if="getText(globalForm[`${scope.$index}${scope.row.AfterQuestionList[index].QuestionId?scope.row.AfterQuestionList[index].QuestionId:String(scope.row.AfterQuestionList[index].GlobalAnswerType)}`], scope.row.AfterQuestionList[index])" :content="getText(globalForm[`${scope.$index}${scope.row.AfterQuestionList[index].QuestionId?scope.row.AfterQuestionList[index].QuestionId:String(scope.row.AfterQuestionList[index].GlobalAnswerType)}`], scope.row.AfterQuestionList[index])" placement="top-start">
<el-select
v-model="globalForm[`${scope.$index}${scope.row.AfterQuestionList[index].QuestionId?scope.row.AfterQuestionList[index].QuestionId:String(scope.row.AfterQuestionList[index].GlobalAnswerType)}`]"
style="width:90%;"
:disabled="parseInt(globalForm[`${scope.$index}${scope.row.AgreeOrNot[0].GlobalAnswerType}`]) !== 0"
>
<template v-if="scope.row.AfterQuestionList[index].TypeValue">
<el-option
v-for="val in scope.row.AfterQuestionList[index].TypeValue.split('|')"
:key="val"
:label="val"
:value="val"
/>
</template>
<template v-else-if="scope.row.AfterQuestionList[index].DictionaryCode && scope.row.AfterQuestionList[index].QuestionType === 13">
<el-option
v-for="item of $d[scope.row.AfterQuestionList[index].DictionaryCode]"
v-show="item.value!==-1"
:key="item.id"
:value="String(item.value)"
:label="item.label"
:disabled="String(getBeforeAnswer(scope.row.AfterQuestionList[index].QuestionName,scope.row)) === String(item.value)"
/>
</template>
<template v-else-if="scope.row.AfterQuestionList[index].DictionaryCode">
<el-option
v-for="item of $d[scope.row.AfterQuestionList[index].DictionaryCode]"
:key="item.id"
:value="String(item.value)"
:disabled="String(getBeforeAnswer(scope.row.AfterQuestionList[index].QuestionName,scope.row)) === String(item.value)"
:label="item.label"
/>
</template>
</el-select>
</el-tooltip>
<el-select
v-model="globalForm[`${scope.$index}${scope.row.AfterQuestionList[index].QuestionId?scope.row.AfterQuestionList[index].QuestionId:String(scope.row.AfterQuestionList[index].GlobalAnswerType)}`]"
style="width:90%;"
v-else
:disabled="parseInt(globalForm[`${scope.$index}${scope.row.AgreeOrNot[0].GlobalAnswerType}`]) !== 0"
>
<template v-if="scope.row.AfterQuestionList[index].TypeValue">
<el-option
v-for="val in scope.row.AfterQuestionList[index].TypeValue.split('|')"
:key="val"
:label="val"
:value="val"
/>
</template>
<template v-else-if="scope.row.AfterQuestionList[index].DictionaryCode && scope.row.AfterQuestionList[index].QuestionType === 13">
<el-option
v-for="item of $d[scope.row.AfterQuestionList[index].DictionaryCode]"
v-show="item.value!==-1"
:key="item.id"
:value="String(item.value)"
:label="item.label"
:disabled="String(getBeforeAnswer(scope.row.AfterQuestionList[index].QuestionName,scope.row)) === String(item.value)"
/>
</template>
<template v-else-if="scope.row.AfterQuestionList[index].DictionaryCode">
<el-option
v-for="item of $d[scope.row.AfterQuestionList[index].DictionaryCode]"
:key="item.id"
:value="String(item.value)"
:disabled="String(getBeforeAnswer(scope.row.AfterQuestionList[index].QuestionName,scope.row)) === String(item.value)"
:label="item.label"
/>
</template>
</el-select>
</template>
<!-- 评估更新类型 GlobalAnswerType:3 -->
<template v-if="scope.row.AfterQuestionList[index].GlobalAnswerType === 3">
<el-tooltip class="item" effect="dark" v-if="getText(globalForm[`${scope.$index}${scope.row.AfterQuestionList[index].GlobalAnswerType}`], scope.row.AfterQuestionList[index], scope.row.AfterQuestionList[index].GlobalAnswerType)" :content="getText(globalForm[`${scope.$index}${scope.row.AfterQuestionList[index].GlobalAnswerType}`], scope.row.AfterQuestionList[index], scope.row.AfterQuestionList[index].GlobalAnswerType)" placement="top-start">
<el-select
v-model="globalForm[`${scope.$index}${scope.row.AfterQuestionList[index].GlobalAnswerType}`]"
style="width:90%;"
:disabled="parseInt(globalForm[`${scope.$index}${scope.row.AgreeOrNot[0].GlobalAnswerType}`]) !== 0"
>
<el-option
v-for="val in globalInfo.assessTypeList"
v-show="(scope.row.IsBaseLine && val.IsBaseLineUse) || (!scope.row.IsBaseLine && !val.IsBaseLineUse)"
:key="val.Code"
:label="language === 'en'?val.Value:val.ValueCN"
:value="val.Code"
/>
</el-select>
</el-tooltip>
<el-select
v-else
v-model="globalForm[`${scope.$index}${scope.row.AfterQuestionList[index].GlobalAnswerType}`]"
style="width:90%;"
:disabled="parseInt(globalForm[`${scope.$index}${scope.row.AgreeOrNot[0].GlobalAnswerType}`]) !== 0"
>
<el-option
v-for="val in globalInfo.assessTypeList"
v-show="(scope.row.IsBaseLine && val.IsBaseLineUse) || (!scope.row.IsBaseLine && !val.IsBaseLineUse)"
:key="val.Code"
:label="language === 'en'?val.Value:val.ValueCN"
:value="val.Code"
/>
</el-select>
</template>
<el-input
v-if="scope.row.AfterQuestionList[index].GlobalAnswerType === 1"
v-model="globalForm[`${scope.$index}${scope.row.AfterQuestionList[index].QuestionId?scope.row.AfterQuestionList[index].QuestionId:String(scope.row.AfterQuestionList[index].GlobalAnswerType)}`]"
type="textarea"
maxlength="100"
show-word-limit
style="width:90%;"
:autosize="{ minRows: 2 }"
:disabled="parseInt(globalForm[`${scope.$index}${scope.row.AgreeOrNot[0].GlobalAnswerType}`]) !== 0"
/>
</el-form-item>
</div>
<div v-else-if="scope.row.AfterQuestionList.length>index && scope.row.AfterQuestionList[index].Answer">
<span v-if="scope.row.AfterQuestionList[index].GlobalAnswerType === 3">
{{ getAssessType(scope.row.AfterQuestionList[index].Answer) }}
</span>
<span v-else-if="scope.row.AfterQuestionList[index].DictionaryCode">
{{ $fd(scope.row.AfterQuestionList[index].DictionaryCode,parseInt(scope.row.AfterQuestionList[index].Answer)) }}
</span>
<span v-else>{{ scope.row.AfterQuestionList[index].Answer }}</span>
</div>
</template>
</el-table-column>
</template>
</el-table-column>
<el-table-column
:label="$t('common:action:action')"
width="100"
>
<template slot-scope="scope">
<el-button
circle
:title="$t('trials:globalReview:table:view')"
icon="el-icon-view"
@click="handleView(scope.row)"
/>
</template>
</el-table-column>
</el-table>
</el-form>
</template>
<script>
import { getToken } from '@/utils/auth'
import { batchSubmitGlobalReadingInfo } from '@/api/trials'
import { mapGetters } from 'vuex'
export default {
props: {
globalInfo: {
type: Object,
default() {
return {}
}
},
globalForm: {
type: Object,
default() {
return {}
}
},
readingTaskState: {
type: Number,
required: true
}
},
data() {
return {
loading: false,
assessTypes: []
}
},
computed: {
...mapGetters(['language'])
},
mounted() {
this.assessTypes = this.globalInfo.assessTypeList.filter(i => i.IsBaseLineUse === this.isBaseline)
},
methods: {
getText(val, row, type) {
var text
if (type === 3) {
var o = this.globalInfo.assessTypeList.find(v => {
return parseInt(v.Code) === parseInt(val)
})
if (!o) {
return ''
}
return this.language === 'en' ? o.Value : o.ValueCN
} else if (row.TypeValue) {
return val
} else if (row.DictionaryCode) {
return this.$fd(row.DictionaryCode, parseInt(val))
}
},
handleView(row) {
var token = getToken()
var visitTaskId = row.VisitTaskId
var criterionType = this.$router.currentRoute.query.criterionType
var readingTool = this.$router.currentRoute.query.readingTool
readingTool = isNaN(parseInt(readingTool)) ? null : parseInt(readingTool)
var isReadingTaskViewInOrder = this.$router.currentRoute.query.isReadingTaskViewInOrder
var path = ''
if (readingTool === 0) {
path = `/readingDicoms?trialId=${this.globalInfo.trialId}&subjectCode=${this.globalInfo.subjectCode}&subjectId=${this.globalInfo.subjectId}&visitTaskId=${visitTaskId}&isReadingTaskViewInOrder=${isReadingTaskViewInOrder}&criterionType=${criterionType}&readingTool=${readingTool}&TokenKey=${token}`
} else {
path = `/noneDicomReading?trialId=${this.globalInfo.trialId}&subjectCode=${this.globalInfo.subjectCode}&subjectId=${this.globalInfo.subjectId}&visitTaskId=${visitTaskId}&isReadingTaskViewInOrder=${isReadingTaskViewInOrder}&criterionType=${criterionType}&readingTool=${readingTool}&TokenKey=${token}`
}
const routeData = this.$router.resolve({ path })
window.open(routeData.href, '_blank')
},
handleSave(isPrompt = true) {
return new Promise((resolve, reject) => {
this.$refs['globalRuleForm'].validate(valid => {
if (valid) {
this.loading = true
var visitTaskAnswerList = []
this.globalForm.taskList.forEach((item, index) => {
var answerList = []
item.AfterQuestionList.map(i => {
var obj = {}
if (i.QuestionId) {
obj.questionId = i.QuestionId
obj.globalAnswerType = i.GlobalAnswerType
obj.answer = this.globalForm[ `${index}${i.QuestionId}`]
} else {
obj.questionId = ''
obj.globalAnswerType = i.GlobalAnswerType
obj.answer = this.globalForm[ `${index}${i.GlobalAnswerType}`]
}
answerList.push(obj)
})
answerList.push({ questionId: '', globalAnswerType: item.AgreeOrNot[0].GlobalAnswerType,
answer: this.globalForm[ `${index}${item.AgreeOrNot[0].GlobalAnswerType}`] })
visitTaskAnswerList.push({ visitTaskId: item.VisitTaskId, answerList: answerList })
})
var params = {
globalTaskId: this.globalInfo.visitTaskId,
subjectId: this.globalInfo.subjectId,
trialId: this.globalInfo.trialId,
visitTaskAnswerList
}
batchSubmitGlobalReadingInfo(params).then(res => {
this.loading = false
if (isPrompt) {
console.log(isPrompt)
this.$message.success(this.$t('common:message:savedSuccessfully'))
}
this.$emit('getGlInfo')
resolve()
}).catch(() => {
this.loading = false
reject()
})
} else {
reject()
}
})
})
},
getBeforeAnswer(qsId, row) {
var i = row.BeforeQuestionList.findIndex(item => item.QuestionName === qsId)
if (i > -1 && row.BeforeQuestionList[i].Answer) {
var answer = ''
if (row.BeforeQuestionList[i].DictionaryCode) {
answer = row.BeforeQuestionList[i].Answer
} else {
answer = row.BeforeQuestionList[i].Answer
}
return answer
} else {
return ''
}
},
handleAgreeOrNotChange(index, globalAnswerType) {
var agreeOrNot = parseInt(this.globalForm[`${index}${globalAnswerType}`])
if (agreeOrNot === 1) {
var qsList = this.globalForm.taskList[index].AfterQuestionList
qsList.map(v => {
if (v.QuestionId) {
this.globalForm[`${index}${v.QuestionId}`] = ''
} else if (v.GlobalAnswerType !== globalAnswerType) {
this.globalForm[`${index}${v.GlobalAnswerType}`] = ''
}
})
}
},
getAssessType(v) {
console.log(this.language)
var i = this.globalInfo.assessTypeList.findIndex(i => String(i.Code) === String(v))
if (i > -1) {
return this.language === 'en' ? this.globalInfo.assessTypeList[i].Value : this.globalInfo.assessTypeList[i].ValueCN
} else {
return ''
}
}
}
}
</script>
<style lang="scss" scoped>
>>>.el-form-item label:before {
content: '*';
color: #F56C6C;
margin-right: 4px;
}
>>>.el-textarea .el-input__count{
background: rgba(0,0,0,0);
line-height: normal;
}
>>>.el-form-item{
margin-bottom: 0px;
}
</style>

View File

@ -0,0 +1,439 @@
<template>
<BaseContainer>
<el-tabs v-model="TrialReadingCriterionId" type="border-card">
<el-tab-pane v-for="i of trialCriterionList" :key="i.TrialReadingCriterionId" :label="i.TrialReadingCriterionName" :name="i.TrialReadingCriterionId">
<div v-if="TrialReadingCriterionId === i.TrialReadingCriterionId">
<div slot="search-container">
<el-form :inline="true">
<!-- 是否加急 -->
<el-form-item
style="margin-bottom:10px"
:label="$t('trials:medicalFeedback:table:isUrgent')"
>
<el-select
v-model="searchData.IsUrgent"
clearable
style="width:120px;"
>
<el-option
v-for="item of $d.YesOrNo"
:key="'IsUrgent' + item.label"
:value="item.value"
:label="item.label"
/>
</el-select>
</el-form-item>
<!-- 受试者编号 -->
<el-form-item :label="$t('trials:medicalFeedback:table:subjectCode')">
<el-input
v-model="searchData.SubjectCode"
style="width:130px;"
clearable
/>
</el-form-item>
<!-- 盲态任务标识 -->
<el-form-item
style="margin-bottom:10px"
:label="$t('trials:medicalFeedback:table:taskBlindName')"
>
<el-input
v-model="searchData.TaskBlindName"
style="width:100px;"
clearable
/>
</el-form-item>
<!-- 任务类型 -->
<el-form-item
style="margin-bottom:10px"
:label="$t('trials:medicalFeedback:table:readingCategory')"
>
<el-select
v-model="searchData.ReadingCategory"
clearable
style="width:120px;"
>
<el-option
v-for="item of $d.ReadingCategory"
:key="'ReadingCategory' + item.label"
:value="item.value"
:label="item.label"
/>
</el-select>
</el-form-item>
<!-- 审核状态 -->
<el-form-item
style="margin-bottom:10px"
:label="$t('trials:medicalFeedback:table:auditState')"
>
<el-select
v-model="searchData.AuditState"
clearable
style="width:120px;"
>
<el-option
v-for="item of $d.MedicalReviewAuditState"
:key="'AuditState' + item.label"
:value="item.value"
:label="item.label"
/>
</el-select>
</el-form-item>
<!-- 医学审核建议 -->
<el-form-item
style="margin-bottom:10px"
:label="$t('trials:medicalFeedback:table:auditAdvice')"
>
<el-select
v-model="searchData.AuditAdviceEnum"
clearable
style="width:120px;"
>
<el-option
v-for="item of $d.AuditAdvice"
:key="'AuditAdvice' + item.label"
:value="item.value"
:label="item.label"
/>
</el-select>
</el-form-item>
<!-- 是否认可 -->
<el-form-item
style="margin-bottom:10px"
:label="$t('trials:medicalFeedback:table:doctorUserIdea')"
>
<el-select
v-model="searchData.DoctorUserIdeaEnum"
clearable
style="width:120px;"
>
<el-option
v-for="item of $d.MedicalReviewDoctorUserIdea"
:key="'MedicalReviewDoctorUserIdea' + item.label"
:value="item.value"
:label="item.label"
/>
</el-select>
</el-form-item>
<el-form-item>
<!-- 查询 -->
<el-button type="primary" icon="el-icon-search" @click="handleSearch">
{{ $t('common:button:search') }}
</el-button>
<!-- 重置 -->
<el-button type="primary" icon="el-icon-refresh-left" @click="handleReset">
{{ $t('common:button:reset') }}
</el-button>
</el-form-item>
</el-form>
</div>
<div slot="main-container">
<el-table
v-adaptive="{bottomOffset:75}"
v-loading="loading"
:data="list"
stripe
height="100"
>
<el-table-column type="index" width="40" align="left" />
<!-- 是否加急 -->
<el-table-column
prop="IsUrgent"
min-width="100"
:label="$t('trials:medicalFeedback:table:isUrgent')"
show-overflow-tooltip
sortable="custom"
>
<template slot-scope="scope">
<el-tag v-if="scope.row.IsUrgent" type="danger">{{ $fd('YesOrNo', scope.row.IsUrgent) }}</el-tag>
<el-tag v-else type="primary">{{ $fd('YesOrNo', scope.row.IsUrgent) }}</el-tag>
</template>
</el-table-column>
<!-- 受试者编号 -->
<el-table-column
prop="SubjectCode"
min-width="100"
:label="$t('trials:medicalFeedback:table:subjectCode')"
show-overflow-tooltip
sortable="custom"
/>
<!-- 盲态任务标识 -->
<el-table-column
prop="TaskBlindName"
min-width="100"
:label="$t('trials:medicalFeedback:table:taskBlindName')"
show-overflow-tooltip
sortable="custom"
/>
<!-- 阅片标准 -->
<el-table-column
prop="TrialReadingCriterionName"
:label="$t('trials:medicalFeedback:table:criterionName')"
min-width="140"
show-overflow-tooltip
sortable="custom"
/>
<!-- 任务类型 -->
<el-table-column
prop="ReadingCategory"
:label="$t('trials:medicalFeedback:table:readingCategory')"
min-width="100"
sortable="custom"
show-overflow-tooltip
>
<template slot-scope="scope">
<el-tag v-if="scope.row.ReadingCategory === 1" type="primary">
{{ $fd('ReadingCategory', scope.row.ReadingCategory) }}
</el-tag>
<el-tag v-if="scope.row.ReadingCategory === 2" type="info">
{{ $fd('ReadingCategory', scope.row.ReadingCategory) }}
</el-tag>
<el-tag v-if="scope.row.ReadingCategory === 4" type="danger">
{{ $fd('ReadingCategory', scope.row.ReadingCategory) }}
</el-tag>
<el-tag v-if="scope.row.ReadingCategory === 5" type="warning">
{{ $fd('ReadingCategory', scope.row.ReadingCategory) }}
</el-tag>
</template>
</el-table-column>
<!-- 阅片任务状态 -->
<el-table-column
prop="TaskState"
:label="$t('trials:medicalFeedback:table:taskState')"
min-width="100"
show-overflow-tooltip
sortable="custom"
>
<template slot-scope="scope">
<el-tag v-if="scope.row.TaskState === 0" type="primary">{{ $fd('TaskState', scope.row.TaskState) }}</el-tag>
<el-tag v-if="scope.row.TaskState === 1" type="info">{{ $fd('TaskState', scope.row.TaskState) }}</el-tag>
<el-tag v-if="scope.row.TaskState === 3" type="danger">{{ $fd('TaskState', scope.row.TaskState) }}</el-tag>
<el-tag v-if="scope.row.TaskState === 4" type="warning">{{ $fd('TaskState', scope.row.TaskState) }}</el-tag>
</template>
</el-table-column>
<!-- 审核状态 -->
<el-table-column
prop="AuditState"
:label="$t('trials:medicalFeedback:table:auditState')"
min-width="100"
sortable="custom"
show-overflow-tooltip
>
<template slot-scope="scope">
<el-tag v-if="scope.row.AuditState === 0" type="warning">
{{ $fd('MedicalReviewAuditState', scope.row.AuditState) }}
</el-tag>
<el-tag v-if="scope.row.AuditState === 1" type="primary">
{{ $fd('MedicalReviewAuditState', scope.row.AuditState) }}
</el-tag>
<el-tag v-if="scope.row.AuditState === 2" type="danger">
{{ $fd('MedicalReviewAuditState', scope.row.AuditState) }}
</el-tag>
</template>
</el-table-column>
<!-- 首次质询时间 -->
<el-table-column
prop="FirstReplyTime"
min-width="100"
:label="$t('trials:medicalFeedback:table:firstReplyTime')"
show-overflow-tooltip
sortable="custom"
/>
<!-- 最近回复时间 -->
<el-table-column
prop="LastReplyTime"
min-width="100"
:label="$t('trials:medicalFeedback:table:lastReplyTime')"
show-overflow-tooltip
sortable="custom"
/>
<!-- 医学审核建议 -->
<el-table-column
prop="AuditAdviceEnum"
:label="$t('trials:medicalFeedback:table:auditAdvice')"
min-width="120"
sortable="custom"
show-overflow-tooltip
>
<template slot-scope="scope">
{{ $fd('AuditAdvice',scope.row.AuditAdviceEnum) }}
</template>
</el-table-column>
<!-- 是否认可 -->
<el-table-column
prop="DoctorUserIdeaEnum"
min-width="120"
:label="$t('trials:medicalFeedback:table:doctorUserIdea')"
show-overflow-tooltip
sortable="custom"
>
<template slot-scope="scope">
{{ $fd('MedicalReviewDoctorUserIdea',scope.row.DoctorUserIdeaEnum) }}
</template>
</el-table-column>
<el-table-column
:label="$t('common:action:action')"
width="200"
fixed="right"
>
<template slot-scope="scope">
<!-- 查看 -->
<el-button
:disabled="!scope.row.IsHaveQuestion || scope.row.IsInvalid"
circle
:title="$t('trials:medicalFeedback:table:view')"
icon="el-icon-edit-outline"
@click="handleReadMecialAudit(scope.row)"
/>
<!-- 回复 -->
<el-button
:disabled="!scope.row.IsHaveQuestion || scope.row.IsInvalid"
circle
:title="$t('trials:medicalFeedback:table:reply')"
icon="el-icon-chat-dot-round"
@click="handleReply(scope.row)"
/>
</template>
</el-table-column>
</el-table>
<!-- 分页组件 -->
<pagination class="page" :total="total" :page.sync="searchData.PageIndex" :limit.sync="searchData.PageSize" @pagination="getList" />
</div>
</div>
</el-tab-pane>
</el-tabs>
<el-dialog
v-if="chatForm.visible"
:visible.sync="chatForm.visible"
:close-on-click-modal="false"
custom-class="base-dialog-wrapper"
:title="chatForm.title"
width="600px"
>
<ChatForm
:task-medical-review-id="currentRow.Id"
:visit-task-id="currentRow.VisitTaskId"
@getList="getList"
@close="chatForm.visible = false"
/>
</el-dialog>
<!-- 医学审核详情 -->
<el-dialog
v-if="auditVisible"
:visible.sync="auditVisible"
:close-on-click-modal="false"
:fullscreen="true"
:title="$t('trials:medicalFeedback:title:mfDetail')"
>
<MedicalAudit
:task-medical-review-id="currentRow.Id"
:visit-task-id="currentRow.VisitTaskId"
:row-data="currentRow"
@getList="getList"
@close="auditVisible=false"
/>
</el-dialog>
</BaseContainer>
</template>
<script>
import { getIRMedicalFeedbackList } from '@/api/trials'
import { getTrialCriterionList } from '@/api/trials/reading'
import BaseContainer from '@/components/BaseContainer'
import Pagination from '@/components/Pagination'
import ChatForm from './components/ChatForm'
import MedicalAudit from '@/views/trials/trials-panel/reading/mim-medical-audit/components/MedicalAudit'
const searchDataDefault = () => {
return {
IsUrgent: null,
SubjectCode: '',
TaskBlindName: '',
ReadingCategory: null,
AuditState: null,
AuditAdviceEnum: null,
DoctorUserIdeaEnum: null,
PageIndex: 1,
PageSize: 20
}
}
export default {
name: 'ReadTask',
components: { BaseContainer, Pagination, ChatForm, MedicalAudit },
data() {
return {
searchData: searchDataDefault(),
list: [],
total: 0,
loading: false,
trialId: '',
currentRow: {},
chatForm: { visible: false, title: '' }, // 质询记录
auditVisible: false,
trialCriterionList: [],
TrialReadingCriterionId: '0'
}
},
watch: {
TrialReadingCriterionId(v) {
if (v) {
this.getList()
}
}
},
mounted() {
this.trialId = this.$route.query.trialId
this.getTrialCriterionList()
},
methods: {
getTrialCriterionList() {
getTrialCriterionList(this.trialId).then(res => {
this.trialCriterionList = res.Result
this.TrialReadingCriterionId = this.trialCriterionList[0].TrialReadingCriterionId
}).catch(() => {})
},
getList() {
this.loading = true
this.searchData.TrialId = this.trialId
this.searchData.TrialReadingCriterionId = this.TrialReadingCriterionId
getIRMedicalFeedbackList(this.searchData).then(res => {
this.list = res.Result.CurrentPageData
this.total = res.Result.TotalCount
this.loading = false
}).catch(() => { this.loading = false })
},
handleSearch() {
this.searchData.PageIndex = 1
this.getList()
},
handleReset() {
this.searchData = searchDataDefault()
this.getList()
},
handleReadMecialAudit(row) {
this.currentRow = { ...row }
this.auditVisible = true
},
handleReply(row) {
this.currentRow = { ...row }
this.chatForm.title = `${this.$t('trials:medicalFeedback:title:qaRecord')} (${row.SubjectCode} ${row.TaskBlindName})`
this.chatForm.visible = true
},
// 排序
handleSortChange(column) {
if (column.order === 'ascending') {
this.searchData.Asc = true
} else {
this.searchData.Asc = false
}
this.searchData.SortField = column.prop
this.searchData.PageIndex = 1
this.getList()
}
}
}
</script>

View File

@ -0,0 +1,29 @@
<script>
export default {
name: 'MenuItem',
functional: true,
props: {
icon: {
type: String,
default: ''
},
title: {
type: String,
default: ''
}
},
render(h, context) {
const { icon, title } = context.props
const vnodes = []
if (icon) {
vnodes.push(<svg-icon icon-class={icon}/>)
}
if (title) {
vnodes.push(<span slot='title'>{(title)}</span>)
}
return vnodes
}
}
</script>

View File

@ -0,0 +1,201 @@
<template>
<div class="trialsTab">
<el-tabs v-model="trialsTab" @tab-click="clickTab">
<el-tab-pane v-for="item of trialsRouter.children.find(v => {return v.name == 'TrialsPanel'}).children" :key="`tab${item.path}`" :disabled="TotalNeedSignTrialDocCount !== 0" :label="$t(item.LanguageMark)" :name="item.path">
<el-tabs v-if="!item.tabHiddn" v-model="trialsTabChild" @tab-click="clickTab">
<el-tab-pane
v-for="item1 of item.children"
:key="`tab1${item1.path}`"
:disabled="TotalNeedSignTrialDocCount !== 0 || (TrialStatusStr === 'Initializing' && (item1.path === '/trials/trials-panel/setting/personnel-manage' || item1.path === '/trials/trials-panel/setting/qc-question'))"
:label="$t(item1.LanguageMark)"
:name="item1.path"
/>
</el-tabs>
</el-tab-pane>
</el-tabs>
<div style="position: absolute;top: 0;right: 0;display: flex;justify-content: space-between;align-items: center">
<trials-select />
<div>
<i class="iconfont" style="font-size: 30px;color:#ccc">&#xe680;</i>
</div>
<div class="my_icon_box" title="返回项目列表" @click="goBack">
<i class="iconfont">&#xe670;</i>
<span>返回</span>
</div>
</div>
</div>
</template>
<script>
import { mapGetters } from 'vuex'
import { getTrialSelect } from '@/api/trials'
import TrialsSelect from '@/components/TrialsSelect/index.vue'
import { getUserDocumentList } from '@/api/trials'
import store from '@/store'
export default {
name: 'TrialsTab',
components: {
TrialsSelect
},
data() {
return {
trialsRouter: this.$store.getters.routes.find(v => { return v.name === 'Trials' }),
trialsTab: null,
trialsTabChild: null,
tabTrialsId: null,
options: [],
TrialStatusStr: null
}
},
computed: {
...mapGetters(['TotalNeedSignTrialDocCount'])
},
watch: {
$route(v, ov) {
this.selectedTab()
var query = ``
var url = window.location.href
if (~url.indexOf('?')) {
query = url.split('?')[1]
}
zzSessionStorage.setItem('lastWorkbench', `${v.path}${query ? '?' : ''}${query}`)
var firstGoIn = this.trialsRouter.children.find(v => { return v.name === 'TrialsPanel' }).children[0]
if (this.trialsTab === '/trials/trials-panel') {
this.$router.replace({ path: `${firstGoIn.path}${query ? '?' : ''}${query}` })
if (firstGoIn.children && firstGoIn.children.length > 0) {
this.$router.replace({ path: `${firstGoIn.children[0].path}${query ? '?' : ''}${query}` })
}
}
}
},
mounted() {
var query = ``
var url = window.location.href
if (~url.indexOf('?')) {
query = url.split('?')[1]
}
getUserDocumentList({ TrialId: this.$route.query.trialId }).then(async res => {
var total = res.OtherInfo.NeedSignCount
var TrialStatusStr = res.OtherInfo.TrialStatusStr
this.TrialStatusStr = TrialStatusStr
await store.dispatch('user/setTotalNeedSignTrialDocCount', total)
await store.dispatch('user/setTrialStatusStr', TrialStatusStr)
}).catch(() => {
})
this.selectedTab()
this.getTrialList()
var firstGoIn = this.trialsRouter.children.find(v => { return v.name === 'TrialsPanel' }).children[0]
if (this.trialsTab === '/trials/trials-panel') {
this.$router.replace({ path: `${firstGoIn.path}${query ? '?' : ''}${query}` })
if (firstGoIn.children && firstGoIn.children.length > 0) {
this.$router.replace({ path: `${firstGoIn.children[0].path}${query ? '?' : ''}${query}` })
}
}
},
methods: {
goBack() {
this.$router.push({ path: '/trials/trials-list' })
},
selectTrials(v) {
var trial = this.options.find(o => {
return o.Id === v
})
this.$router.push({ path: `/trials/trials-panel?trialId=${trial.Id}&trialCode=${trial.TrialCode}` })
},
getTrialList() {
getTrialSelect()
.then(res => {
this.options = res.Result
this.tabTrialsId = this.$route.query.trialId
})
.catch(() => {
})
},
selectedTab() {
var path = this.$route.path
if (this.$route.path.split('/').length > 4) {
this.trialsTab = this.getValue(path)
this.trialsTabChild = path
} else {
this.trialsTab = path
}
},
getValue(url) {
var site = url.lastIndexOf('\/')
return url.substring(0, site)
},
clickTab(v) {
try {
var query = ``
var url = window.location.href
if (~url.indexOf('?')) {
query = url.split('?')[1]
}
this.$router.push({ path: `${v.name}${query ? '?' : ''}${query}` })
var trialsPanelList = this.trialsRouter.children.find(v => { return v.name === 'TrialsPanel' }).children
var isHasChild = trialsPanelList.find(c => { return c.path === v.name }) && trialsPanelList.find(c => { return c.path === v.name }).children
if (trialsPanelList.find(c => { return c.path === v.name }) && trialsPanelList.find(c => { return c.path === v.name }).tabHiddn) {
return
}
if (isHasChild && isHasChild.length > 0) {
this.trialsTabChild = isHasChild[0].path
this.$router.push({ path: `${this.trialsTabChild}${query ? '?' : ''}${query}` })
}
} catch (e) {
console.log(e)
}
}
}
}
</script>
<style lang="scss">
.trialsTab{
.my_icon_box{
padding:0 20px 0 0;
transition: color,transform .3s;
cursor: pointer;
color: #666;
display: flex;
justify-content: center;
align-items: center;
&:hover{
color: #00d1b2;
}
span{
margin-left: 3px;
font-size: 14px;
}
}
.el-tabs__header{
margin-bottom: 3px;
}
position: relative;
.el-input--medium .el-input__inner{
height: 44px;line-height: 44px;width: 280px;
border: none;
text-align: right;
}
}
.base-model-wrapper{
.el-dialog__header{
padding: 10px;
.el-dialog__headerbtn{
top: 10px;
right: 10px;
}
}
.el-dialog__body{
padding:10px 10px 10px 10px;
.base-modal-body{
min-height: 100px;
max-height:650px;
overflow-y: auto;
padding: 10px;
border: 1px solid #e0e0e0;
}
}
.el-dialog__footer{
padding: 10px;
}
}
</style>

View File

@ -0,0 +1,446 @@
<template>
<div class="report-wrapper">
<el-card v-loading="loading" shadow="never">
<div slot="header" class="clearfix report-header">
<h3 style="margin:0;padding:0;">电子影像病例报告表eICRF</h3>
<div v-if="readingTaskState<2" style="margin-left:auto">
<el-button type="primary" size="small" @click="getReportInfo">刷新</el-button>
<el-button type="primary" size="small" @click="handleSave(true)">保存</el-button>
<el-button type="primary" size="small" @click="handleConfirm">确认</el-button>
</div>
</div>
<div style="padding:5px">
<el-table
ref="reportList"
v-adaptive="{bottomOffset:60}"
:data="taskQuestions"
row-key="Id"
border
default-expand-all
height="100"
:tree-props="{children: 'Childrens', hasChildren: 'hasChildren'}"
size="mini"
>
<el-table-column
prop=""
label=""
show-overflow-tooltip
width="350px"
>
<template slot-scope="scope">
<span v-if="scope.row.QuestionName">{{ scope.row.QuestionName }}</span>
<span
v-else
style="font-weight: bold;font-size: 16px;color: #f44336;"
>
{{ scope.row.GroupName }}
</span>
</template>
</el-table-column>
<el-table-column
v-for="task in visitTaskList"
:key="task.VisitTaskId"
prop="date"
:label="task.BlindName"
show-overflow-tooltip
width="200px"
>
<template slot-scope="scope">
<template v-if="task.VisitTaskId === visitTaskId && !task.IsBaseLine && readingTaskState < 2 && scope.row.QuestionType=== 13">
<!-- 输入框 -->
<el-input
v-if="scope.row.Type==='input'"
v-model="scope.row.Answers[task.VisitTaskId]"
size="mini"
/>
<el-select
v-else-if="scope.row.Type==='select' && scope.row.DictionaryCode"
v-model="scope.row.Answers[task.VisitTaskId]"
size="mini"
@change="handleEvaluateResultChange"
>
<el-option
v-for="item of $d[ scope.row.DictionaryCode]"
:key="item.id"
:value="item.value"
:label="item.label"
/>
</el-select>
<span v-else>{{ scope.row.Answers && scope.row.Answers.hasOwnProperty(task.VisitTaskId)? scope.row.Answers[task.VisitTaskId]:'' }}</span>
</template>
<template v-else-if="tumorEvaluate && task.VisitTaskId === visitTaskId && readingTaskState < 2 && scope.row.QuestionType=== 14 && scope.row.Answers && currentEvaluateResult !== tumorEvaluate">
<!-- 输入框 -->
<el-input
v-if="scope.row.Type==='input'"
v-model="scope.row.Answers[task.VisitTaskId]"
size="mini"
@change="evaluateReasonChange"
/>
<p style="width: 180px;white-space: normal;word-break: break-all;word-wrap: break-word;">系统评估结果为:<span style="color:red">{{ $fd('OverallAssessment',tumorEvaluate) }}</span>,与当前调整的结果不一致,请填写调整原因</p>
</template>
<template v-else-if="scope.row.DictionaryCode">
{{ $fd(scope.row.DictionaryCode, scope.row.Answers[task.VisitTaskId]) }}
</template>
<template v-else-if="scope.row.Answers && scope.row.Answers.hasOwnProperty(task.VisitTaskId)">
{{ isNaN(parseInt(scope.row.Answers[task.VisitTaskId]))?scope.row.Answers[task.VisitTaskId]:parseInt(scope.row.Answers[task.VisitTaskId])>0?`${scope.row.Answers[task.VisitTaskId]}mm`:'' }}
<!-- isNaN(parseInt(scope.row.Answers[task.VisitTaskId]))?'':`${scope.row.Answers[task.VisitTaskId]}mm` -->
<!-- {{ scope.row.Answers && scope.row.Answers.hasOwnProperty(task.VisitTaskId)? scope.row.Answers[task.VisitTaskId]:'' }} -->
</template>
</template>
</el-table-column>
</el-table>
</div>
</el-card>
<!-- 签名框 -->
<el-dialog
v-if="signVisible"
:visible.sync="signVisible"
:close-on-click-modal="false"
width="600px"
custom-class="base-dialog-wrapper"
>
<div slot="title">
<span style="font-size:18px;">{{ $t('common:dialogTitle:sign') }}</span>
<span style="font-size:12px;margin-left:5px">{{ `(${$t('common:label:sign')}${ currentUser })` }}</span>
</div>
<SignForm ref="signForm" :sign-code-enum="signCode" @closeDialog="closeSignDialog" />
</el-dialog>
</div>
</template>
<script>
import { getReadingReportEvaluation, changeDicomReadingQuestionAnswer, submitDicomVisitTask, verifyVisitTaskQuestions } from '@/api/trials'
import Store from './Store'
import const_ from '@/const/sign-code'
import SignForm from '@/views/trials/components/newSignForm'
import { getToken } from '@/utils/auth'
export default {
name: 'ReportPage',
components: { SignForm },
props: {
visitTaskId: {
type: String,
required: true
}
},
data() {
return {
currentUser: zzSessionStorage.getItem('userName'),
signVisible: false,
signCode: null,
visitTaskList: [],
taskQuestions: [],
loading: false,
answers: [],
readingTaskState: 2,
tumorEvaluate: '',
currentEvaluateResult: '',
answerArr: []
}
},
watch: {
taskQuestions() {
this.$nextTick(() => {
this.$refs.reportList.doLayout()
})
}
},
mounted() {
window.addEventListener('resize', this.handleResize)
Store.$on('getReportInfo', isRefresh => {
if (!isRefresh) return
this.getReportInfo()
})
this.getReportInfo()
},
beforeDestroy() {
Store.$off('getReportInfo')
},
methods: {
getReportInfo() {
this.loading = true
var params = {
visitTaskId: this.visitTaskId,
trialId: this.$router.currentRoute.query.trialId
}
getReadingReportEvaluation(params).then(res => {
this.readingTaskState = res.Result.ReadingTaskState
this.tumorEvaluate = res.Result.TumorEvaluate ? parseInt(res.Result.TumorEvaluate) : null
var questions = []
this.answerArr = []
res.Result.TaskQuestions.forEach((item, index) => {
if (!([3, 4, 5, 6, 2].includes(item.QuestionMark))) {
const obj = item
obj.Answers = {}
item.Answer.forEach(i => {
if (item.DictionaryCode) {
obj.Answers[i.VisitTaskId] = i.Answer ? parseInt(i.Answer) : null
} else {
obj.Answers[i.VisitTaskId] = i.Answer
}
})
if (item.QuestionType === 13) {
this.currentEvaluateResult = obj.Answers[this.visitTaskId]
this.answerArr.push({ id: item.QuestionId, answer: obj.Answers[this.visitTaskId], questionType: 14 })
}
if (item.QuestionType === 14) {
this.answerArr.push({ id: item.QuestionId, answer: obj.Answers[this.visitTaskId], questionType: 14 })
}
if (item.Childrens && item.Childrens.length > 0) {
obj.Childrens = this.getQuestions(item.Childrens, obj.IsShowInDicom)
}
questions.push(obj)
}
})
this.taskQuestions = questions
this.visitTaskList = res.Result.VisitTaskList
this.loading = false
}).catch(() => { this.loading = false })
},
getQuestions(questions, isShowInDicom) {
const arr = []
if (questions.length !== 0) {
questions.forEach((item) => {
if (!([3, 4, 5, 6, 2].includes(item.QuestionMark))) {
const obj = item
obj.IsShowInDicom = isShowInDicom
obj.Answers = {}
if (item.RowIndex > 0) {
var idx = item.Childrens.findIndex(i => i.QuestionMark === 4)
if (idx > -1) {
if (item.Childrens[idx].Answer.length > 0) {
var k = item.Childrens[idx].Answer.findIndex(v => v.Answer !== '')
// var splitOrMergeType = isNaN(parseInt(item.SplitOrMergeType)) ? '' : parseInt(item.SplitOrMergeType)
if (item.SplitOrMergeLesionName && k > -1) {
obj.QuestionName = `${obj.QuestionName} -- ${item.Childrens[idx].Answer[k].Answer} (Split from ${item.SplitOrMergeLesionName})`
// obj.QuestionName = `${obj.QuestionName} `
} else if (!item.SplitOrMergeLesionName && k > -1) {
obj.QuestionName = `${obj.QuestionName} -- ${item.Childrens[idx].Answer[k].Answer}`
// obj.QuestionName = `${obj.QuestionName} `
} else {
obj.QuestionName = `${obj.QuestionName} `
}
}
}
}
item.Answer.forEach(i => {
if (item.DictionaryCode) {
obj.Answers[i.VisitTaskId] = i.Answer ? parseInt(i.Answer) : null
} else {
obj.Answers[i.VisitTaskId] = i.Answer
}
})
if (item.QuestionType === 13) {
this.currentEvaluateResult = obj.Answers[this.visitTaskId]
this.answerArr.push({ id: item.QuestionId, answer: obj.Answers[this.visitTaskId], questionType: 13 })
}
if (item.QuestionType === 14) {
this.answerArr.push({ id: item.QuestionId, answer: obj.Answers[this.visitTaskId], questionType: 14 })
}
if (item.Childrens.length >= 1) {
obj.Childrens = this.getQuestions(item.Childrens, isShowInDicom)
}
arr.push(obj)
}
})
}
return arr
},
handleEvaluateResultChange(val) {
this.currentEvaluateResult = parseInt(val)
var idx = this.answerArr.findIndex(i => i.questionType === 13)
this.answerArr[idx].answer = val
},
evaluateReasonChange(val) {
var idx = this.answerArr.findIndex(i => i.questionType === 14)
this.answerArr[idx].answer = val
},
async handleConfirm() {
await this.handleSave(false)
await this.verifyVisitTaskQuestions()
const { ImageAssessmentReportConfirmation } = const_.processSignature
this.signCode = ImageAssessmentReportConfirmation
this.signVisible = true
},
verifyVisitTaskQuestions() {
return new Promise((resolve, reject) => {
this.loading = true
verifyVisitTaskQuestions({ visitTaskId: this.visitTaskId }).then(res => {
this.loading = false
resolve()
}).catch(() => {
this.loading = false
reject()
})
})
},
handleResize() {
this.$nextTick(() => {
this.$refs.reportList.doLayout()
})
},
// 关闭签名框
closeSignDialog(isSign, signInfo) {
if (isSign) {
this.signConfirm(signInfo)
} else {
this.signVisible = false
}
},
// 签名并确认
signConfirm(signInfo) {
this.loading = true
var params = {
data: {
visitTaskId: this.visitTaskId
},
signInfo: signInfo
}
submitDicomVisitTask(params).then(res => {
if (res.IsSuccess) {
this.$message.success(this.$t('common:message:savedSuccessfully'))
this.$refs['signForm'].btnLoading = false
this.signVisible = false
// window.location.reload()
// window.opener.postMessage('refreshTaskList', window.location)
window.opener.postMessage('refreshTaskList', window.location)
var token = getToken()
var subjectCode = this.$router.currentRoute.query.subjectCode
var subjectId = this.$router.currentRoute.query.subjectId
var trialId = this.$router.currentRoute.query.trialId
this.$router.push({
path: `/readingPage?subjectCode=${subjectCode}&subjectId=${subjectId}&trialId=${trialId}&TokenKey=${token}`
})
}
this.loading = false
}).catch(_ => {
this.loading = false
this.$refs['signForm'].btnLoading = false
})
},
handleSave(isPrompt) {
return new Promise((resolve, reject) => {
var evaluateResult = ''
var evaluateAjustReason = ''
this.answers = []
var isExistEvaluateResult = false
this.answerArr.map(item => {
if (item.questionType === 13) {
evaluateResult = item.answer
isExistEvaluateResult = true
}
if (item.questionType === 14) {
evaluateAjustReason = item.answer
}
this.answers.push({ id: item.id, answer: item.answer })
})
if (isExistEvaluateResult && evaluateResult === null) {
this.$confirm('请将疗效评估信息填写完整', {
type: 'warning',
showCancelButton: false,
callback: action => {}
})
reject()
return
}
if (isExistEvaluateResult && (evaluateResult !== this.tumorEvaluate) && !evaluateAjustReason) {
this.$confirm('请填写整体评估调整原因', {
type: 'warning',
showCancelButton: false,
callback: action => {}
})
reject()
return
}
this.loading = true
var params = {
visitTaskId: this.visitTaskId,
answers: this.answers
}
changeDicomReadingQuestionAnswer(params).then(res => {
if (isPrompt) {
this.$message.success(this.$t('common:message:savedSuccessfully'))
}
this.loading = false
resolve()
}).catch(() => {
this.loading = false
reject()
})
})
}
}
}
</script>
<style lang="scss" scoped>
.report-wrapper{
height: 100%;
// background-color: #fff;
background-color: #000;
::-webkit-scrollbar {
width: 7px;
height: 7px;
}
::-webkit-scrollbar-thumb {
border-radius: 10px;
background: #d0d0d0;
}
.report-header{
display: flex;
}
.el-card{
background-color: #000;
color: #ffffff;
border:none;
}
// >>>.el-table__cell{
// background-color: #000;
// color: #ffffff;
// }
// >>>.el-table{
// background-color: #000;
// color: #ffffff;
// }
// >>>.el-table__cell{
// background-color: #000;
// color: #ffffff;
// }
>>>.el-table, .el-table__expanded-cell {
background-color: #000;
color: #fff;
border-color:#444444;
}
>>>.el-table th, .el-table tr {
background-color: #000;
color: #fff;
border-color:#444444;
}
>>>.el-table__body tr > td{
background-color:#000 !important;
color: #fff;
border-color:#444444;
}
>>>.el-table__body tr:hover > td{
background-color:#858282 !important;
color: #fff;
border-color:#444444;
}
>>>.el-table--border th.gutter:last-of-type{
border: none;
}
>>>.el-card__header{
border: none;
padding: 10px;
}
}
</style>

View File

@ -0,0 +1,153 @@
<template>
<div v-loading="loading" class="ecrf-wrapper">
<el-form
v-if="questions.length > 0"
ref="questions"
size="small"
:model="questionForm"
>
<QuestionItem
v-for="question of questions"
:key="question.Id"
:question="question"
:question-form="questionForm"
:reading-task-state="readingTaskState"
:visit-task-id="visitTaskId"
@setFormItemData="setFormItemData"
@resetFormItemData="resetFormItemData"
/>
<el-form-item v-if="readingTaskState < 2">
<div style="text-align:right">
<el-button size="mini" @click="handleSave">{{ $t('common:button:save') }}</el-button>
</div>
</el-form-item>
</el-form>
</div>
</template>
<script>
import { saveImageQuality } from '@/api/trials'
import QuestionItem from './QuestionItem'
import DicomEvent from './DicomEvent'
import { mapGetters } from 'vuex'
export default {
name: 'ECRF',
components: {
QuestionItem
},
data() {
return {
loading: false,
questions: [],
questionForm: {},
readingTaskState: 2,
visitTaskId: ''
}
},
computed: {
...mapGetters(['visitTaskList'])
},
mounted() {
DicomEvent.$on('setReadingState', readingTaskState => {
this.readingTaskState = readingTaskState
})
},
beforeDestroy() {
DicomEvent.$off('setReadingState')
},
methods: {
async getQuestions(visitTaskId) {
this.visitTaskId = visitTaskId
// const loading = this.$loading({ fullscreen: true })
var idx = this.visitTaskList.findIndex(i => i.VisitTaskId === visitTaskId)
if (idx > -1) {
this.readingTaskState = this.visitTaskList[idx].ReadingTaskState
var questions = this.visitTaskList[idx].Questions
questions.map((v) => {
if (v.Type === 'group' && v.Childrens.length === 0) return
if (!v.IsPage && v.Type !== 'group' && v.Type !== 'summary') {
this.$set(this.questionForm, v.Id, v.Answer ? v.Answer : null)
}
if (v.Childrens.length > 0) {
this.setChild(v.Childrens)
}
})
this.questions = questions
}
// loading.close()
},
setChild(obj) {
obj.forEach(i => {
if (i.Type !== 'group' && i.Type !== 'summary' && i.Id) {
this.$set(this.questionForm, i.Id, i.Answer ? i.Answer : null)
}
if (i.Childrens && i.Childrens.length > 0) {
this.setChild(i.Childrens)
}
})
},
handleSave() {
this.$refs['questions'].validate((valid) => {
if (!valid) return
this.loading = true
var answers = []
for (const k in this.questionForm) {
answers.push({ id: k, answer: this.questionForm[k] })
}
var params = {
visitTaskId: this.visitTaskId,
answers: answers
}
saveImageQuality(params).then(res => {
this.$message.success(this.$t('common:message:savedSuccessfully'))
this.loading = false
DicomEvent.$emit('getReportInfo', true)
DicomEvent.$emit('refreshVisitList', this.visitTaskId)
}).catch(() => {
this.loading = false
})
})
},
resetFormItemData(v) {
this.questionForm[v] = null
},
setFormItemData(obj) {
this.questionForm[obj.key] = obj.val
}
}
}
</script>
<style lang="scss" scoped>
.ecrf-wrapper{
>>>.el-form-item__label{
color: #c3c3c3;
}
>>>.el-input__inner{
background-color: transparent;
color: #ddd;
border: 1px solid #5e5e5e;
}
>>>.el-form-item{
display: flex;
flex-direction: row;
justify-content: flex-start;
}
>>>.el-form-item__content{
flex: 1;
}
>>>.el-button--mini, .el-button--mini.is-round {
padding: 7px 10px;
}
.el-form-item__content
.el-select{
width: 100%;
}
}
</style>

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,708 @@
/* eslint-disable vue/this-in-template */
<template>
<div v-loading="loading" class="viewerContainer">
<!-- <div class="viewerBanner" /> -->
<div class="viewerContentWrapper">
<div class="viewerLeftSidePanel">
<div id="listWrapper" class="sidePanelBody">
<div class="sidePanelThumbs">
<el-tabs v-model="activeName" type="border-card">
<el-tab-pane :label="$t('trials:dicom-show:nowStudy')" name="first">
<div class="studyDesc">
{{ studyCode }}
</div>
<div class="studyDesc">
{{ description }}
</div>
<div v-if="seriesCount" class="studyDesc">
{{ modality }} : {{ seriesCount }} Series
</div>
<div class="viewerSidethumbs ps" style="position: relative;">
<div class="viewerSidethumbinner">
<div
v-for="(item, index) in seriesList"
:key="index"
:class="{'viewerSideActive': index==0}"
style="position: relative;margin-bottom:5px;border-radius: 2px;border: 1px solid #404040;"
series-type="current"
@click="showSeriesImage($event,index,item)"
>
<div class="viewernavigatorwrapper">
<el-image
class="image-preview"
style="height:72px;width:72px;"
:src="item.previewImageUrl"
fit="fill"
/>
<div class="viewernavitextwrapper">
<div v-if="item.keySeries" style="padding: 1px;color:red">
Key Images
</div>
<div v-else style="padding: 1px;">
#{{ item.seriesNumber }}
</div>
<div v-show="item.instanceCount" style="padding: 1px;">
{{ item.modality }}: {{ item.instanceCount }} image
</div>
<div v-show="!item.keySeries && item.sliceThickness" style="padding: 1px;">
T: {{ item.sliceThickness }}
</div>
<div v-show="!item.keySeries &&item.description" style="width: 120px;white-space: nowrap;overflow: hidden;text-overflow: ellipsis;padding: 1x;">
{{ item.description }}
</div>
<div v-if="!item.keySeries" style="padding: 1px;">
{{ item.prefetchInstanceCount }}/{{ item.instanceCount }}
</div>
</div>
</div>
<div v-if="showDelete" @click.stop="">
<span style="font-size: 12px;">{{ $t('trials:audit:table:isReading') }}</span>
<el-switch
v-model="item.isReading"
size="mini"
@change="changeReadingStatus($event, item)"
/>
<span style="font-size: 12px;">{{ $t('trials:audit:table:isDelete') }}</span>
<el-switch
v-model="item.isDeleted"
size="mini"
@change="changeDeleteStatus($event, item)"
/>
</div>
<div v-if="item.prefetchInstanceCount>0 && item.prefetchInstanceCount<item.instanceCount">
<el-progress :percentage="parseInt(((item.prefetchInstanceCount/item.instanceCount)*100).toFixed(2))" />
</div>
</div>
</div>
</div>
</el-tab-pane>
</el-tabs>
</div>
</div>
</div>
<div class="viewerContent">
<dicom-viewer id="dicomViewer" ref="dicomViewer" style="height:100%" />
</div>
<!-- <div class="viewerRightSidePanel">
<dicom-tools />
</div> -->
</div>
</div>
</template>
<script>
import * as dicomParser from 'dicom-parser'
import * as cornerstone from 'cornerstone-core'
import * as cornerstoneWADOImageLoader from 'cornerstone-wado-image-loader'
import dicomViewer from '@/components/Dicom/DicomViewer'
import { getStudyInfo, getSeriesList } from '@/api/reading'
import { getInstanceList, setSeriesStatus } from '@/api/trials'
import requestPoolManager from '@/utils/request-pool'
import store from '@/store'
import { changeURLStatic } from '@/utils/history.js'
cornerstoneWADOImageLoader.external.dicomParser = dicomParser
cornerstoneWADOImageLoader.external.cornerstone = cornerstone
const maximumSizeInBytes = 1024 * 1024 * 1024 // 1 GB
export default {
components: {
'dicom-viewer': dicomViewer
},
data: function() {
return {
trialId: '',
studyId: '',
tpCode: '',
baseLine: null,
studyCode: '',
modality: '',
description: '',
seriesCount: 0,
seriesList: [],
currentSeriesIndex: -1,
arr: [],
activeName: 'first',
tpList: [],
visitNum: 0,
labels: {},
type: '',
currentRelationIndex: [],
cachedImages: [],
isLoadedAll: false,
studyList: {},
currentStudy: '',
previewImageCount: 0,
relationStudyList: [],
isStartLoad: false,
firstInstanceId: '',
showDelete: false,
loading: false,
imageList: []
}
},
created: function() {
this.type = this.$router.currentRoute.query.type ? this.$router.currentRoute.query.type : ''
this.visitNum = this.$router.currentRoute.query.visitNum ? parseInt(this.$router.currentRoute.query.visitNum) : 0
cornerstone.events.addEventListener('cornerstoneimageloaded', this.cornerstoneImageLoaded)
// cornerstone.events.addEventListener('datasetscachechanged', this.datasetsCacheChanged)
if (this.$router.currentRoute.query.TokenKey) {
store.dispatch('user/setToken', this.$router.currentRoute.query.TokenKey)
changeURLStatic('TokenKey', '')
}
this.studyId = this.$router.currentRoute.query.studyId
if (this.type === 'Series') {
// this.initStudy()
this.showDelete = parseInt(this.$router.currentRoute.query.showDelete)
this.initSeries()
} else if (this.type === 'Study') {
this.showDelete = parseInt(this.$router.currentRoute.query.showDelete)
this.loadStudy()
} else if (this.type === 'Share') {
this.loadStudy()
}
},
beforeDestroy() {
requestPoolManager.stopTaskTimer()
},
methods: {
loadStudy() {
var scope = this
getStudyInfo(scope.studyId).then(data => {
if (data.IsSuccess) {
scope.studyCode = data.Result.StudyCode
scope.modality = data.Result.Modalities
scope.seriesCount = data.Result.SeriesCount
scope.description = data.Result.Description
var url = `/series/list/${scope.studyId}`
scope.getSeriesList(url)
}
})
},
getSeriesList(url) {
var scope = this
getSeriesList(url).then(data => {
if (data.IsSuccess) {
const { Result } = data
var seriesList = []
Result.forEach(function(item) {
const imageIds = []
item.InstancePathList.forEach((path) => {
imageIds.push(`wadouri:${localStorage.getItem('location') !== 'USA' ? scope.OSSclientConfig.basePath : scope.OSSclientConfig.basePath}${path}`)
})
var subjectVisitId = scope.$router.currentRoute.query.subjectVisitId
var studyId = scope.$router.currentRoute.query.studyId
var trialId = scope.$router.currentRoute.query.trialId
seriesList.push({
trialId,
subjectVisitId,
studyId,
imageIds: imageIds,
seriesId: item.Id,
seriesUid: item.SeriesInstanceUid,
seriesNumber: item.SeriesNumber,
sliceThickness: item.SliceThickness,
modality: item.Modality,
description: item.Description,
isReading: item.IsReading,
isDeleted: item.IsDeleted,
previewImageUrl: item.ImageResizePath ? scope.OSSclientConfig.basePath + item.ImageResizePath : `/api/series/preview/${item.Id}`,
instanceCount: item.InstanceCount,
prefetchInstanceCount: 0,
hasLabel: item.HasLabel,
keySeries: item.KeySeries,
tpCode: scope.tpCode,
loadStatus: false,
imageloadedArr: []
})
})
scope.seriesList = seriesList
if (scope.seriesList.length > 0) {
scope.$refs.dicomViewer.loadImageStack(scope.seriesList[0], scope.labels[scope.tpCode])
scope.firstInstanceId = scope.seriesList[0].imageIds[0]
}
}
})
},
initSeries() {
this.studyCode = this.$router.currentRoute.query.studyCode
this.modality = this.$router.currentRoute.query.modality
this.seriesCount = 1
this.description = this.$router.currentRoute.query.description
var seriesId = this.$router.currentRoute.query.seriesId
var seriesNumber = this.$router.currentRoute.query.seriesNumber
var subjectVisitId = this.$router.currentRoute.query.subjectVisitId
var studyId = this.$router.currentRoute.query.studyId
var trialId = this.$router.currentRoute.query.trialId
getInstanceList(seriesId).then(res => {
if (!res.Result || (res.Result && res.Result.length === 0)) return
var seriesInstanceUid = res.Result[0].SeriesInstanceUid
var sliceLocation = res.Result[0].SliceLocation
var isReading = res.Result[0].IsReading
var isDeleted = res.Result[0].IsDeleted
var seriesList = []
var imageIds = []
res.Result.forEach(instance => {
imageIds.push(`wadouri:${localStorage.getItem('location') !== 'USA' ? scope.OSSclientConfig.basePath : scope.OSSclientConfig.basePath}${path}`)
})
seriesList.push({
trialId,
subjectVisitId,
studyId,
imageIds: imageIds,
seriesId: seriesId,
seriesUid: seriesInstanceUid,
seriesNumber: seriesNumber,
sliceThickness: sliceLocation,
modality: this.modality,
description: this.description,
isReading,
isDeleted,
previewImageUrl: res.OtherInfo.ImageResizePath ? scope.OSSclientConfig.basePath + res.OtherInfo.ImageResizePath : res.OtherInfo.ImageResizePath,
instanceCount: res.Result.length,
prefetchInstanceCount: 0,
loadStatus: false,
imageloadedArr: []
})
this.seriesList = seriesList
if (this.seriesList.length > 0) {
this.$nextTick(() => {
this.loadAllImages()
this.$refs.dicomViewer.loadImageStack(this.seriesList[0])
this.firstInstanceId = this.seriesList[0].imageIds[0]
})
}
})
},
showSeriesImage(e, seriesIndex, series) {
// if (seriesIndex === this.currentSeriesIndex) return
const element = e.currentTarget
const elements = document.querySelectorAll('[series-type]')
Array.from(elements).forEach((e) => {
e.classList.remove('viewerSideActive')
})
element.classList.add('viewerSideActive')
this.currentSeriesIndex = seriesIndex
this.$refs.dicomViewer.loadImageStack(this.seriesList[seriesIndex])
if (!series.loadStatus) {
series.isLoading = true
var p = new Date().getTime()
series.imageIds.map((imageId, i) => {
var priority = ''
if (i === 0) {
priority = new Date(new Date().setHours(23, 59, 59, 999)).getTime()
} else {
priority = p - 1
}
this.imageList.push({ imageId: imageId, seriesId: series.seriesId, priority })
})
}
},
initStudy() {
this.studyCode = this.$router.currentRoute.query.studyCode
this.modality = this.$router.currentRoute.query.modality
this.seriesCount = 1
this.description = this.$router.currentRoute.query.description
if (this.$router.currentRoute.query.series) {
const seriesInfo = JSON.parse(this.$router.currentRoute.query.series)
var seriesList = []
const imageIds = []
seriesInfo.InstancePathList.forEach((path) => {
imageIds.push(`wadouri:${localStorage.getItem('location') !== 'USA' ? this.OSSclientConfig.basePath : this.OSSclientConfig.basePath}${path}`)
})
seriesList.push({
imageIds: imageIds,
seriesId: seriesInfo.Id,
seriesUid: seriesInfo.SeriesInstanceUid,
seriesNumber: seriesInfo.SeriesNumber,
sliceThickness: seriesInfo.SliceThickness,
modality: seriesInfo.Modality,
description: seriesInfo.Description,
previewImageUrl: seriesInfo.ImageResizePath ? this.OSSclientConfig.basePath + seriesInfo.ImageResizePath : `/api/series/preview/${seriesInfo.Id}`,
instanceCount: seriesInfo.InstanceCount,
prefetchInstanceCount: 0,
hasLabel: seriesInfo.HasLabel,
keySeries: seriesInfo.KeySeries,
loadStatus: false,
imageloadedArr: []
})
this.seriesList = seriesList
if (this.seriesList.length > 0) {
this.$nextTick(() => {
this.loadAllImages()
this.$refs.dicomViewer.loadImageStack(this.seriesList[0])
this.firstInstanceId = this.seriesList[0].imageIds[0]
})
}
}
},
deleteSeries() {
this.loading = true
var subjectVisitId = this.$router.currentRoute.query.subjectVisitId
var studyId = this.$router.currentRoute.query.studyId
var seriesId = this.$router.currentRoute.query.seriesId
// trialId, subjectVisitId, studyId, seriesId, state
setSeriesStatus(this.trialId, subjectVisitId, studyId, seriesId, 5).then(res => {
this.loading = false
if (res.IsSuccess) {
this.$message.success(this.$t('common:message:savedSuccessfully'))
window.opener.postMessage({ type: 'refreshSeriesList', data: '' }, window.location)
}
}).catch(() => { this.loading = false })
},
changeReadingStatus(callback, data) {
let statusStr = ''
if (callback) {
statusStr = this.$t('trials:audit:label:setSeriesReading')
data.isReading = false
} else {
statusStr = this.$t('trials:audit:label:setSeriesNotReading')
data.isReading = true
}
var message = this.$t('trials:audit:message:changeSeriesStatus').replace('xxx', statusStr)
this.$confirm(message, {
distinguishCancelAndClose: true,
type: 'warning'
}).then(() => {
const state = data.isReading ? 1 : 2
this.loading = true
setSeriesStatus(data.trialId, data.subjectVisitId, data.studyId, data.seriesId, state).then(res => {
this.loading = false
if (res.IsSuccess) {
data.isReading = !data.isReading
this.$message.success(this.$t('common:message:savedSuccessfully'))
window.opener.postMessage({ type: 'refreshSeriesList', data: '' }, window.location)
}
}).catch(() => { this.loading = false })
}).catch(() => {})
},
changeDeleteStatus(callback, data) {
let statusStr = ''
if (callback) {
statusStr = this.$t('trials:audit:label:setSeriesDeleted')
data.isDeleted = false
} else {
statusStr = this.$t('trials:audit:label:setSeriesNotDelete')
data.isDeleted = true
}
var message = this.$t('trials:audit:message:changeSeriesStatus').replace('xxx', statusStr)
this.$confirm(message, {
distinguishCancelAndClose: true,
type: 'warning'
}).then(() => {
const state = data.isDeleted ? 5 : 4
this.loading = true
// var trialId = this.$router.currentRoute.query.trialId
// var subjectVisitId = this.$router.currentRoute.query.subjectVisitId
// var studyId = this.$router.currentRoute.query.studyId
// var seriesId = this.$router.currentRoute.query.seriesId
setSeriesStatus(data.trialId, data.subjectVisitId, data.studyId, data.seriesId, state).then(res => {
this.loading = false
if (res.IsSuccess) {
data.isDeleted = !data.isDeleted
this.$message.success(this.$t('common:message:savedSuccessfully'))
window.opener.postMessage({ type: 'refreshSeriesList', data: '' }, window.location)
}
}).catch(() => { this.loading = false })
}).catch(() => {})
},
loadAllImages() {
var priority = new Date().getTime()
this.seriesList.forEach(series => {
series.imageIds.forEach(imageId => {
priority--
// this.load(imageId, series.seriesId, priority)
this.imageList.push({ imageId: imageId, seriesId: series.seriesId, priority })
})
})
if (this.imageList.length > 0) {
this.loopLoad()
}
},
loopLoad() {
if (this.imageList.length > 0) {
requestPoolManager.startTaskTimer()
this.imageList.map(image => {
requestPoolManager.loadAndCacheImagePlus(image.imageId, image.seriesId, image.priority).then(res => {
this.imageLoaded(image, res.data.string('x0020000e'))
})
})
this.imageList = []
}
},
load(imageId, seriesId, priority = 999) {
return new Promise((resolve, reject) => {
requestPoolManager.loadAndCacheImagePlus(imageId, seriesId, priority).then(res => {
if (!res) return
this.imageLoaded(imageId, res.data.string('x0020000e'))
resolve(res)
}).catch(e => {
reject(e)
})
})
},
imageLoaded(imageId, seriesUid) {
var seriesIndex = -1
for (let i = 0; i < this.seriesList.length; ++i) {
if (this.seriesList[i].seriesUid === seriesUid) {
seriesIndex = i
break
}
}
if (seriesIndex < 0) return
const imageIdIndex = this.seriesList[seriesIndex].imageIds.indexOf(imageId)
if (imageIdIndex < 0) return
if (this.seriesList[seriesIndex].imageloadedArr.indexOf(imageId) < 0) {
++this.seriesList[seriesIndex].prefetchInstanceCount
this.seriesList[seriesIndex].imageloadedArr.push(imageId)
if (this.seriesList[seriesIndex].prefetchInstanceCount >= this.seriesList[seriesIndex].instanceCount) {
this.seriesList[seriesIndex].prefetchInstanceCount = this.seriesList[seriesIndex].instanceCount
// 设置当前序列状态为已下载完成
this.seriesList[seriesIndex].loadStatus = true
// if (!this.isLoadedAll) {
// this.loadAllImages()
// }
}
}
},
datasetsCacheChanged(e) {
// const uri = e.detail.uri
const cacheInfo = e.detail.cacheInfo
const cacheSizeInBytes = cacheInfo.cacheSizeInBytes
if (cacheSizeInBytes >= maximumSizeInBytes) {
// this.unload()
}
},
unload() {
this.cachedImages.sort(this.compare)
cornerstoneWADOImageLoader.wadouri.dataSetCacheManager.unload(this.cachedImages[0].uri)
this.cachedImages.splice(0, 1)
},
compare(a, b) {
if (a.timeStamp > b.timeStamp) {
return -1
}
if (a.timeStamp < b.timeStamp) {
return 1
}
return 0
},
cornerstoneImageLoaded(e) {
console.log('dicom-study')
if (this.firstInstanceId === e.detail.image.imageId && !this.isStartLoad) {
// 初始化图像加载完成时,自动下载
this.loadAllImages()
this.isStartLoad = true
// requestPoolManager.executeTask()
}
const uri = e.detail.image.sharedCacheKey
const index = this.cachedImages.findIndex(item => item.uri === uri)
if (index === -1) {
this.cachedImages.push({ uri: uri, timestamp: new Date().getTime() })
} else {
this.cachedImages[index].timestamp = new Date().getTime()
}
// loadedDataSets[uri].dataSet.byteArray.length
// console.log(this.cachedImages.length)
// console.log(cornerstoneWADOImageLoader.wadouri.dataSetCacheManager.getInfo().cacheSizeInBytes)
// const imageId = e.detail.image.imageId
var imageId = e.detail.image.imageId
var seriesUid = e.detail.image.data.string('x0020000e')
var seriesIndex = -1
for (let i = 0; i < this.seriesList.length; ++i) {
if (this.seriesList[i].seriesUid === seriesUid) {
seriesIndex = i
break
}
}
if (seriesIndex < 0) return
const imageIdIndex = this.seriesList[seriesIndex].imageIds.indexOf(imageId)
if (imageIdIndex < 0) return
if (this.seriesList[seriesIndex].imageloadedArr.indexOf(imageId) < 0) {
++this.seriesList[seriesIndex].prefetchInstanceCount
this.seriesList[seriesIndex].imageloadedArr.push(imageId)
if (this.seriesList[seriesIndex].prefetchInstanceCount >= this.seriesList[seriesIndex].instanceCount) {
this.seriesList[seriesIndex].prefetchInstanceCount = this.seriesList[seriesIndex].instanceCount
// 设置当前序列状态为已下载完成
this.seriesList[seriesIndex].loadStatus = true
// if (!this.isLoadedAll) {
// this.loadAllImages()
// }
}
}
}
}
}
</script>
<style>
.viewerContainer .el-tabs--border-card{
background:none;
border: none;
}
.viewerContainer .el-tabs__item{
height: 35px;
line-height: 35px;
}
.viewerContainer .el-tabs--border-card>.el-tabs__content{
padding: 5px 0px 0px 0px;
}
.viewerContainer ::-webkit-scrollbar {
width: 7px;
height: 7px;
}
.viewerContainer ::-webkit-scrollbar-thumb {
border-radius: 10px;
background: #d0d0d0;
}
.viewerContainer .relationVisit{
line-height: 20px;
cursor: pointer;
}
.viewerContainer{
display: block;
height: 100%;
margin-left: auto;
margin-right: auto;
background-color: #444;
overflow: hidden;
}
.viewerContainer .viewerBanner {
background: linear-gradient(0,#444,#222);
min-height: 28px;
font-size: 16px;
margin: 0;
height: 30px;
padding-top: 0;
padding-left: 5px;
padding-right: 5px;
font-weight: bold;
}
.viewerContainer .viewerContentWrapper {
display: flex;
flex-direction: row;
width: 100%;
padding: 5px;
/* height: 95%; */
height: 99%;
overflow: hidden;
text-overflow: clip;
white-space: nowrap;
}
.viewerContainer .viewerContentWrapper>div {
display: inline-block;
white-space: normal;
}
.viewerContainer .viewerLeftSidePanel {
width: 215px;
background-color: #323232;
box-sizing: border-box;
margin: 0;
padding: 0;
margin-right: 2px;
color: #D0D0D0;
overflow-y: auto;
}
.viewerContainer .viewerContentWrapper>div>.sidePanelBody {
background: rgba(50,50,50,1);
word-break: break-all;
display: table;
width: 100%;
border: 1px solid #3e3f3a;
}
.viewerContainer .viewerContentWrapper>div>div.sidePanelBody>div {
width: 100%;
height: 100%;
}
.viewerContainer .studyDesc{
font-weight: bold;
font-size: 13px;
text-align: center;
background: rgb(88 84 83);
color: #d0d0d0;
padding: 2px;
}
.viewerContainer .ps {
overflow: hidden !important;
overflow-anchor: none;
-ms-overflow-style: none;
touch-action: auto;
-ms-touch-action: auto;
}
.viewerContainer .viewerLeftSidePanel .viewernavigatorwrapper {
display: flex;
width: 210px;
height: 84px;
padding: 1px 2px 1px 8px;
margin: 6px 0 6px 1px;
/* border-radius: 2px;
border: 1px solid #404040; */
}
.viewerContainer .viewernavigatorwrapper .el-progress__text{
display: none;
}
.viewerContainer .viewernavigatorwrapper .el-progress-bar{
padding-right:0px;
}
.viewerContainer .ui-draggable-handle {
-ms-touch-action: none;
touch-action: none;
}
.viewerContainer .viewerLeftSidePanel .image-preview {
border: 2px solid #252525;
cursor: pointer;
}
.viewerContainer .viewerLeftSidePanel .viewernavitextwrapper {
/* width: 120px;
height: 80px; */
flex:1;
padding: 3px 1px 3px 4px;
vertical-align: top;
font-size: 12px;
}
.viewerContainer .viewerLeftSidePanel .viewerlabelwrapper {
width: 20px;
font-size: 12px;
}
.viewerContainer .viewerSideActive {
background: #16477b;
background: #16477b80;
border: 1px solid #23527b;
}
.viewerContainer .viewerContent {
flex: 1;
height: 100%;
display: block;
background-color: black;
color: #D0D0D0;
font-size: 13px;
}
/* .viewerRightSidePanel {
width: 300px;
height: 100%;
background-color: #323232;
margin: 0;
padding: 0;
margin-left: 2px;
border: 1px solid blueviolet;
color: #D0D0D0;
font-size: 13px;
} */
</style>

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,419 @@
<template>
<div class="viewerContainer">
<div class="viewerContentWrapper">
<div class="viewerLeftSidePanel">
<div id="listWrapper" class="sidePanelBody">
<div class="sidePanelThumbs">
<el-tabs v-model="activeName" type="border-card" @tab-click="handleTabClick">
<el-tab-pane label="当前序列" name="current-study">
<div class="text-desc">
{{ studyCode }}
</div>
<div class="text-desc">
{{ description }}
</div>
<div v-if="seriesCount" class="text-desc">
{{ modality }} : {{ seriesCount }} Series
</div>
<div class="viewerSidethumbs ps" style="position: relative;">
<div class="viewerSidethumbinner">
<div
v-for="(item, index) in seriesList"
:key="index"
class="viewernavigatorwrapper"
:class="{'viewerSideActive': index==0}"
style="position: relative;"
series-type="current"
@click="showSeriesImage($event,index)"
>
<el-image
class="image-preview"
style="height:72px;width:72px;"
:src="item.previewImageUrl"
fit="fill"
/>
<div class="viewernavitextwrapper">
<div style="padding: 1px;">
#{{ item.seriesNumber }}
</div>
<div v-show="item.instanceCount" style="padding: 1px;">
{{ item.modality }}: {{ item.instanceCount }} image
</div>
<div v-show=" item.sliceThickness" style="padding: 1px;">
T: {{ item.sliceThickness }}
</div>
<div v-show="item.description" style="padding: 1x;">
{{ item.description }}
</div>
<div style="padding: 1px;">
{{ item.prefetchInstanceCount }}/{{ item.instanceCount }}
</div>
</div>
<div style="position: absolute;bottom: -10px;left: 0;width: 100%;">
<el-progress v-if="item.prefetchInstanceCount>0 && item.prefetchInstanceCount<item.instanceCount" :percentage="Number(item.prefetchInstanceCount/item.instanceCount)*100" />
</div>
</div>
</div>
</div>
</el-tab-pane>
</el-tabs>
</div>
</div>
</div>
<div class="viewerContent">
<dicom-viewer id="dicomViewer" ref="dicomViewer" style="height:100%" />
</div>
</div>
</div>
</template>
<script>
import * as dicomParser from 'dicom-parser'
import * as cornerstone from 'cornerstone-core'
import * as cornerstoneWADOImageLoader from 'cornerstone-wado-image-loader'
import dicomViewer from '@/components/Dicom/DicomViewer'
import requestPoolManager from '@/utils/request-pool'
import store from '@/store'
cornerstoneWADOImageLoader.external.dicomParser = dicomParser
cornerstoneWADOImageLoader.external.cornerstone = cornerstone
// cornerstoneWADOImageLoader.webWorkerManager.initialize({
// webWorkerPath: './webWorker.js',
// taskConfiguration: {
// 'decodeTask': {
// codecsPath: './dicomCodecs.js'
// }
// }
// })
const maximumSizeInBytes = 1024 * 1024 * 1024 // 1 GB
export default {
name: 'DicomsOfSeries',
components: {
'dicom-viewer': dicomViewer
},
data() {
return {
studyCode: '',
modality: '',
seriesCount: 0,
description: '',
seriesList: [],
cachedImages: [],
isStartLoad: false,
firstInstanceId: ''
}
},
created() {
store.dispatch('user/setToken', this.$router.currentRoute.query.token)
cornerstone.events.addEventListener('cornerstoneimageloaded', this.cornerstoneImageLoaded)
this.initStudy()
},
beforeDestroy() {
requestPoolManager.stopTaskTimer()
},
methods: {
// 格式化序列信息
initStudy() {
this.studyCode = this.$router.currentRoute.query.studyCode
this.modality = this.$router.currentRoute.query.modality
this.seriesCount = 1
this.description = this.$router.currentRoute.query.description
if (this.$router.currentRoute.query.series) {
const seriesInfo = JSON.parse(this.$router.currentRoute.query.series)
var seriesList = []
const imageIds = []
// seriesInfo.InstanceList.forEach(function(item) {
// imageIds.push(`wadouri:/api/instance/content/${item}`)
// })
seriesInfo.InstanceList.forEach((id) => {
imageIds.push(`wadouri:http://123.56.94.154:7000/instance/content/${id}`)
})
seriesList.push({
imageIds: imageIds,
seriesId: seriesInfo.Id,
seriesUid: seriesInfo.SeriesInstanceUid,
seriesNumber: seriesInfo.SeriesNumber,
sliceThickness: seriesInfo.SliceThickness,
modality: seriesInfo.Modality,
description: seriesInfo.Description,
previewImageUrl: `/api/series/preview/${seriesInfo.Id}`,
instanceCount: seriesInfo.InstanceCount,
prefetchInstanceCount: 0,
loadStatus: false,
imageloadedArr: []
})
this.seriesList = seriesList
if (this.seriesList.length > 0) {
this.$nextTick(() => {
this.$refs.dicomViewer.loadImageStack(this.seriesList[0])
})
}
}
},
showSeriesImage(e, seriesIndex) {
const element = e.currentTarget
const elements = document.querySelectorAll('[series-type]')
Array.from(elements).forEach((e) => {
e.classList.remove('viewerSideActive')
})
element.classList.add('viewerSideActive')
this.currentSeriesIndex = seriesIndex
this.$refs.dicomViewer.loadImageStack(this.seriesList[seriesIndex])
},
loadAllImages() {
this.seriesList.forEach(series => {
series.imageIds.forEach(imageId => {
this.load(imageId, series.seriesId)
})
})
},
// load(imageId, priority = 999) {
// return new Promise((resolve, reject) => {
// const imageTask = this.buildImageRequestTask(imageId, { priority })
// requestPoolManager.addTaskIntoPool(imageTask).then((res) => {
// resolve(res)
// }).catch(e => {
// reject(e)
// console.log(e)
// })
// })
// },
load(imageId, seriesId, priority = 999) {
return new Promise((resolve, reject) => {
requestPoolManager.loadAndCacheImagePlus(imageId, seriesId, priority).then(res => {
if (!res) return
this.imageLoaded(imageId, res.data.string('x0020000e'))
resolve(res)
}).catch(e => {
reject(e)
})
})
},
imageLoaded(imageId, seriesUid) {
var seriesIndex = -1
for (let i = 0; i < this.seriesList.length; ++i) {
if (this.seriesList[i].seriesUid === seriesUid) {
seriesIndex = i
break
}
}
if (seriesIndex < 0) return
const imageIdIndex = this.seriesList[seriesIndex].imageIds.indexOf(imageId)
if (imageIdIndex < 0) return
if (this.seriesList[seriesIndex].imageloadedArr.indexOf(imageId) < 0) {
++this.seriesList[seriesIndex].prefetchInstanceCount
this.seriesList[seriesIndex].imageloadedArr.push(imageId)
if (this.seriesList[seriesIndex].prefetchInstanceCount >= this.seriesList[seriesIndex].instanceCount) {
this.seriesList[seriesIndex].prefetchInstanceCount = this.seriesList[seriesIndex].instanceCount
// 设置当前序列状态为已下载完成
this.seriesList[seriesIndex].loadStatus = true
}
}
},
buildImageRequestTask(imageId, config = {}) {
return {
key: imageId,
...config,
execute: () => {
return cornerstone.loadAndCacheImage(imageId)
}
}
},
datasetsCacheChanged(e) {
const cacheInfo = e.detail.cacheInfo
const cacheSizeInBytes = cacheInfo.cacheSizeInBytes
if (cacheSizeInBytes >= maximumSizeInBytes) {
// 清理影像缓存
}
},
// instance下载成功回调
cornerstoneImageLoaded(e) {
if (this.firstInstanceId === e.detail.image.imageId && !this.isStartLoad) {
// 初始化图像加载完成时,自动下载
this.loadAllImages()
this.isStartLoad = true
requestPoolManager.executeTask()
}
const uri = e.detail.image.sharedCacheKey
const index = this.cachedImages.findIndex(item => item.uri === uri)
if (index === -1) {
this.cachedImages.push({ uri: uri, timestamp: new Date().getTime() })
} else {
this.cachedImages[index].timestamp = new Date().getTime()
}
var imageId = e.detail.image.imageId
var seriesUid = e.detail.image.data.string('x0020000e')
var seriesIndex = -1
for (let i = 0; i < this.seriesList.length; ++i) {
if (this.seriesList[i].seriesUid === seriesUid) {
seriesIndex = i
break
}
}
if (seriesIndex < 0) return
const imageIdIndex = this.seriesList[seriesIndex].imageIds.indexOf(imageId)
if (imageIdIndex < 0) return
if (this.seriesList[seriesIndex].imageloadedArr.indexOf(imageId) < 0) {
++this.seriesList[seriesIndex].prefetchInstanceCount
this.seriesList[seriesIndex].imageloadedArr.push(imageId)
if (this.seriesList[seriesIndex].prefetchInstanceCount >= this.seriesList[seriesIndex].instanceCount) {
this.seriesList[seriesIndex].prefetchInstanceCount = this.seriesList[seriesIndex].instanceCount
// 设置当前序列状态为已下载完成
this.seriesList[seriesIndex].loadStatus = true
}
}
}
}
}
</script>
<style lang="scss">
.viewerContainer{
display: block;
height: 100%;
margin-left: auto;
margin-right: auto;
background-color: #444;
overflow: hidden;
.el-tabs--border-card{
background:none;
border: none;
}
.el-tabs--border-card>.el-tabs__content{
padding: 5px 0px 0px 0px;
}
.el-tabs__item{
height: 35px;
line-height: 35px;
}
::-webkit-scrollbar {
width: 7px;
height: 7px;
}
::-webkit-scrollbar-thumb {
border-radius: 10px;
background: #d0d0d0;
}
.relationVisit{
line-height: 20px;
cursor: pointer;
}
.viewerBanner {
background: linear-gradient(0,#444,#222);
min-height: 28px;
font-size: 16px;
margin: 0;
height: 30px;
padding-top: 0;
padding-left: 5px;
padding-right: 5px;
font-weight: bold;
}
.viewerContentWrapper {
display: flex;
flex-direction: row;
width: 100%;
padding: 5px;
/* height: 95%; */
height: 99%;
overflow: hidden;
text-overflow: clip;
white-space: nowrap;
}
.viewerContentWrapper>div {
display: inline-block;
white-space: normal;
}
.viewerLeftSidePanel {
width: 215px;
background-color: #323232;
box-sizing: border-box;
margin: 0;
padding: 0;
margin-right: 2px;
color: #D0D0D0;
overflow-y: auto;
}
.viewerContentWrapper>div>.sidePanelBody {
background: rgba(50,50,50,1);
word-break: break-all;
display: table;
width: 100%;
border: 1px solid #3e3f3a;
}
.viewerContentWrapper>div>div.sidePanelBody>div {
width: 100%;
height: 100%;
}
.text-desc{
font-weight: bold;
font-size: 13px;
text-align: center;
// background: rgb(88 84 83);
color: #d0d0d0;
padding: 2px;
}
.ps {
overflow: hidden !important;
overflow-anchor: none;
-ms-overflow-style: none;
touch-action: auto;
-ms-touch-action: auto;
}
.viewerLeftSidePanel .viewernavigatorwrapper {
display: flex;
width: 210px;
height: 84px;
padding: 1px 2px 1px 8px;
margin: 6px 0 6px 1px;
border-radius: 2px;
border: 1px solid #404040;
}
.viewernavigatorwrapper .el-progress__text{
display: none;
}
.viewernavigatorwrapper .el-progress-bar{
padding-right:0px;
}
.ui-draggable-handle {
-ms-touch-action: none;
touch-action: none;
}
.viewerLeftSidePanel .image-preview {
border: 2px solid #252525;
cursor: pointer;
}
.viewerLeftSidePanel .viewernavitextwrapper {
/* width: 120px;
height: 80px; */
flex:1;
padding: 3px 1px 3px 4px;
vertical-align: top;
font-size: 12px;
}
.viewerLeftSidePanel .viewerlabelwrapper {
width: 20px;
font-size: 12px;
}
.viewerSideActive {
background: #16477b;
background: #16477b80;
border: 1px solid #23527b;
}
.viewerContent {
flex: 1;
height: 100%;
display: block;
background-color: black;
color: #D0D0D0;
font-size: 13px;
}
}
</style>

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,690 @@
<template>
<el-form
ref="clinicalDataForm"
v-loading="loading"
:model="form"
size="small"
:rules="rules"
label-width="130px"
>
<div class="base-dialog-body">
<el-form-item label="类型" prop="Type">
<el-select clearable v-model="form.Type" @change="((val)=>{typeChange(val, form)})">
<el-option
v-for="item of $d.Criterion_Question_Type"
:key="item.value"
:value="item.value"
:label="item.label"
/>
</el-select>
</el-form-item>
<!-- 数值类型 -->
<el-form-item
v-if="form.Type === 'number' || form.Type === 'calculation'"
label="数值类型"
prop="ValueType"
:rules="[
{ required: true, message: '请选择'}
]"
>
<el-radio-group
v-model="form.ValueType"
>
<el-radio
v-for="item of $d.ValueType"
:key="item.id"
:label="item.value"
>
{{ item.label }}
</el-radio>
</el-radio-group>
</el-form-item>
<!-- 数值单位 -->
<el-form-item
v-if="form.Type === 'number' || form.Type === 'calculation'"
label="单位"
>
<el-radio-group
v-model="form.Unit"
>
<el-radio
v-for="item of $d.ValueUnit"
:key="item.id"
:label="item.value"
>
{{ item.label }}
</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item
v-if="form.Type === 'table'"
label="表格类型标识"
prop="LesionType"
>
<el-select
clearable
v-model="form.LesionType"
>
<el-option
v-for="item of lesionTypes"
:key="item.Code"
:value="parseInt(item.Code)"
:label="$fd('LesionType',parseInt(item.Code))"
/>
</el-select>
</el-form-item>
<el-form-item
v-if="form.Type === 'group'"
label="分组名称"
prop="GroupName"
>
<el-input v-model="form.GroupName" />
</el-form-item>
<el-form-item
v-if="form.Type !== 'group'"
label="问题名称"
prop="QuestionName"
>
<el-input v-model="form.QuestionName" />
</el-form-item>
<!-- <el-form-item
v-if="form.Type === 'select' || form.Type === 'radio'"
label="选项"
prop="TypeValue"
>
<el-input
v-model="form.TypeValue"
placeholder="选项请用‘|’分割多个选项"
/>
</el-form-item> -->
<!-- 选项类型 -->
<el-form-item
v-if="form.Type === 'select' || form.Type === 'radio' || form.Type === 'calculation'"
label="选项类型"
prop="QuestionGenre"
:rules="[
{ required: form.Type !== 'calculation', message: '请选择'}
]"
>
<el-radio-group
v-model="form.QuestionGenre"
@change="((val)=>{questionGenreChange(val, form)})"
>
<el-radio
v-for="item of $d.TableQuestionType"
v-show="item.value===0 || item.value===3"
:key="item.id"
:label="item.value"
>
{{ item.label }}
</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item
v-if="form.QuestionGenre === 0"
label="选项"
prop="TypeValue"
>
<el-input
v-model="form.TypeValue"
placeholder="选项请用‘|’分割多个选项"
/>
</el-form-item>
<el-form-item
v-if="form.QuestionGenre === 3 "
label="关联字典"
prop="DictionaryCode"
>
<el-select clearable v-model="form.DictionaryCode" @change="() => {form.DefaultValue = null}">
<el-option
v-for="item of dicList"
:key="item.Id"
:label="item.Code"
:value="item.Code"
/>
</el-select>
</el-form-item>
<el-form-item
v-if="form.QuestionGenre === 3 "
label="默认值"
>
<el-select clearable v-model="form.DefaultValue">
<el-option
v-for="item of form.DictionaryCode ? $d[form.DictionaryCode] : []"
:key="item.id"
:label="item.label"
:value="item.value"
/>
</el-select>
</el-form-item>
<el-form-item
v-if="form.QuestionGenre === 0 "
label="默认值"
>
<el-select clearable v-model="form.DefaultValue">
<el-option
v-for="item of form.TypeValue ? form.TypeValue.split('|') : []"
:key="item"
:label="item"
:value="item"
/>
</el-select>
</el-form-item>
<el-form-item v-if="form.Type !== 'group'" label="是否显示" prop="ShowQuestion">
<el-radio-group
v-model="form.ShowQuestion"
@change="((val)=>{isShowQuestionChange(val, form)})"
>
<el-radio
v-for="item of $d.ShowQuestion"
:key="`ShowQuestion${item.value}`"
:label="item.value"
>
{{ item.label }}
</el-radio>
</el-radio-group>
</el-form-item>
<!-- 显示时依赖父问题 -->
<el-form-item
v-if="form.Type !== 'group' && form.ShowQuestion===1"
label="显示依赖父问题"
prop="ParentId"
>
<el-select
v-model="form.ParentId"
clearable
@change="((val)=>{parentQuestionChange(val, form)})"
>
<el-option
v-for="item of parentOptions"
:key="`ParentId${item.QuestionId}`"
:label="item.QuestionName"
:value="item.QuestionId"
/>
</el-select>
</el-form-item>
<!-- 显示时依赖父问题触发值 -->
<el-form-item v-if="form.ParentId && form.ShowQuestion===1" label="显示触发值" prop="ParentTriggerValue">
<el-select clearable v-model="form.ParentTriggerValue">
<el-option
v-for="item of parentTriggerValOptions"
:key="item"
:label="item"
:value="item"
/>
</el-select>
</el-form-item>
<el-form-item v-if="form.Type === 'select' || form.Type === 'radio' || form.Type === 'calculation'" label="是否裁判问题">
<el-radio-group v-model="form.IsJudgeQuestion" @change="((val)=>{isJudgeQuestionChange(val, form)})">
<el-radio :label="true">是</el-radio>
<el-radio :label="false">否</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item v-if="form.Type !== 'group' && form.Type !== 'table'" label="是否必填" prop="IsRequired">
<el-radio-group
v-model="form.IsRequired"
:disabled="form.IsJudgeQuestion===true || form.ShowQuestion===2"
@change="((val)=>{isRequiredChange(val, form)})"
>
<el-radio
v-for="item of $d.QuestionRequired"
:key="`QuestionRequired${item.value}`"
:label="item.value"
>
{{ item.label }}
</el-radio>
</el-radio-group>
</el-form-item>
<!-- 父问题 -->
<el-form-item
v-if="form.Type !== 'group' && form.IsRequired === 1"
label="必填依赖父问题"
prop="RelevanceId"
>
<el-select
v-model="form.RelevanceId"
clearable
@change="((val)=>{relevanceQuestionChange(val, form)})"
>
<el-option
v-for="item of parentOptions"
:key="`RelevanceId${item.QuestionId}`"
:label="item.QuestionName"
:value="item.QuestionId"
/>
</el-select>
</el-form-item>
<!-- 必填依赖的父问题触发值 -->
<el-form-item
v-if="form.RelevanceId && form.IsRequired === 1"
label="必填触发值"
prop="RelevanceValue"
>
<el-select clearable v-model="form.RelevanceValue">
<el-option
v-for="item of reParentTriggerValOptions"
:key="item"
:label="item"
:value="item"
/>
</el-select>
</el-form-item>
<!-- 父问题 -->
<!-- <el-form-item v-if="form.Type !== 'group'" label="父问题">
<el-select
v-model="form.ParentId"
clearable
@change="((val)=>{parentQuestionChange(val, form)})"
>
<el-option
v-for="item of parentOptions"
:key="item.QuestionId"
:label="item.QuestionName"
:value="item.QuestionId"
/>
</el-select>
</el-form-item> -->
<!-- 父问题触发值 -->
<!-- <el-form-item v-if="form.ParentId" label="父问题触发值" prop="ParentTriggerValue">
<el-select v-model="form.ParentTriggerValue">
<el-option
v-for="item of parentTriggerValOptions"
:key="item"
:label="item"
:value="item"
/>
</el-select>
</el-form-item> -->
<el-form-item v-if="form.Type !== 'group'" label="问题分组">
<el-select v-model="form.GroupName" clearable :disabled="isParentExistGroup">
<el-option
v-for="group of groupOptions"
:key="group"
:label="group"
:value="group"
/>
</el-select>
</el-form-item>
<el-form-item label="全局阅片是否显示">
<el-radio-group v-model="form.GlobalReadingShowType">
<el-radio v-for="item of $d.GlobalReadingShowType" :label="item.value">{{item.label}}</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item
label="问题标识"
prop="QuestionType"
>
<el-select
v-model="form.QuestionType"
clearable
>
<el-option
v-for="item of $d.QuestionType"
:key="item.id"
:value="item.value"
:label="item.label"
/>
</el-select>
</el-form-item>
<!-- <el-form-item v-if="form.Type !== 'group'" label="是否必填">
<el-radio-group v-model="form.IsRequired" :disabled="form.IsJudgeQuestion===true">
<el-radio :label="true">是</el-radio>
<el-radio :label="false">否</el-radio>
</el-radio-group>
</el-form-item> -->
<el-form-item v-if="form.Type === 'upload'" label="最大上传个数">
<el-input-number v-model="form.ImageCount" controls-position="right" :min="1" :max="10" />
</el-form-item>
<el-form-item v-if="form.Type !== 'group' && form.Type !== 'summary'" label="注释">
<el-input v-model="form.Remark" />
</el-form-item>
<el-form-item label="序号" prop="ShowOrder">
<el-input-number
v-model="form.ShowOrder"
controls-position="right"
:min="0"
/>
</el-form-item>
<el-form-item v-if="form.Type === 'table'" label="自增序号前缀">
<el-input v-model="form.OrderMark" />
</el-form-item>
<el-form-item v-if="form.Type === 'table'" label="最大行数">
<el-input-number
v-model="form.MaxQuestionCount"
controls-position="right"
/>
</el-form-item>
<el-form-item label="是否在阅片显示">
<el-radio-group v-model="form.IsShowInDicom">
<el-radio :label="true">是</el-radio>
<el-radio :label="false">否</el-radio>
</el-radio-group>
</el-form-item>
</div>
<div class="base-dialog-footer" style="text-align:right;margin-top:10px;">
<el-form-item>
<!-- 取消 -->
<el-button
:disabled="btnLoading"
size="small"
type="primary"
@click="close"
>
{{ $t('common:button:cancel') }}
</el-button>
<!-- 保存 -->
<el-button size="small" type="primary" :loading="btnLoading" @click="save">
{{ $t('common:button:save') }}
</el-button>
</el-form-item>
</div>
</el-form>
</template>
<script>
import { addOrUpdateReadingQuestionSystem, getSystemCriterionOtherQuestion, getSystemGroupNameList } from '@/api/dictionary'
import { getCriterionLesionType } from '@/api/trials'
import { getBasicConfigSelect } from '@/api/dictionary'
export default {
name: 'AddOrUpdateClinicalData',
props: {
data: {
type: Object,
default() { return {} }
}
},
data() {
var validateTypeVal = (rule, value, callback) => {
if (value === '') {
callback(new Error(this.$t('common:ruleMessage:specify')))
} else {
var arr = value.split('|')
if (new Set(arr).size !== arr.length) {
callback(new Error('选项不允许存在相同值'))
} else {
callback()
}
}
}
return {
validateTypeVal: validateTypeVal,
form: {
Id: '',
ReadingQuestionCriterionSystemId: '',
QuestionName: '',
Type: 'input',
ValueType: '',
Unit: '',
QuestionGenre: '',
TypeValue: '',
DictionaryCode: '',
ParentId: '',
ParentTriggerValue: '',
ShowOrder: 0,
ShowQuestion: 0,
IsRequired: 2,
IsJudgeQuestion: false,
GroupName: '',
Remark: '',
ImageCount: 1,
RelevanceId: '',
RelevanceValue: '',
LesionType: '',
QuestionType: null,
IsShowInDicom: false,
OrderMark: '',
MaxQuestionCount: 0,
GlobalReadingShowType: 3,
DefaultValue: null
},
rules: {
Type: [
{ required: true, message: '请选择', trigger: 'blur' }
],
LesionType: [
{ required: true, message: '请选择', trigger: 'blur' }
],
QuestionName: [{ required: true, message: '请注明', trigger: 'blur' },
{ max: 50, message: '最大长度为 50' }],
QuestionGenre: [{ required: true, message: '请选择', trigger: 'blur' }],
TypeValue: [{ required: true, message: '请注明', trigger: 'blur' },
{ validator: validateTypeVal, trigger: 'blur' },
{ max: 500, message: '最大长度为 500' }],
DictionaryCode: [{ required: true, message: '请选择', trigger: 'blur' }],
ShowQuestion: [{ required: true, message: '请选择', trigger: 'blur' }],
IsRequired: [{ required: true, message: '请选择', trigger: 'blur' }],
ParentId: [{ required: true, message: '请选择', trigger: 'blur' }],
ParentTriggerValue: [
{ required: true, message: '请选择', trigger: 'blur' }
],
RelevanceId: [{ required: true, message: '请选择', trigger: 'blur' }],
RelevanceValue: [{ required: true, message: '请选择', trigger: 'blur' }],
GroupName: [{ required: true, message: '请注明', trigger: 'blur' },
{ max: 50, message: '最大长度为 50' }]
},
loading: false,
btnLoading: false,
parentOptions: [],
parentTriggerValOptions: [],
groupOptions: [],
isParentExistGroup: false,
lesionTypes: [],
dicList: []
}
},
mounted() {
this.initForm()
},
methods: {
async initForm() {
await this.getGroupOptions()
await this.getParentQuestions()
await this.getLesionType()
await this.getBasicConfigSelect()
if (Object.keys(this.data).length > 0) {
for (const k in this.form) {
if (this.data.hasOwnProperty(k)) {
this.form[k] = this.data[k]
}
}
if (this.form.ParentId !== '' && this.form.ParentId !== null && this.form.GroupName !== '') {
this.isParentExistGroup = true
}
if (this.form.ParentId) {
var index = this.parentOptions.findIndex(item => {
return item.QuestionId === this.form.ParentId
})
if (index !== -1) {
this.parentTriggerValOptions = this.parentOptions[index].TypeValue.split('|')
}
}
if (this.form.RelevanceId) {
var i = this.parentOptions.findIndex(item => {
return item.QuestionId === this.form.RelevanceId
})
if (i !== -1) {
this.reParentTriggerValOptions = this.parentOptions[i].TypeValue.split('|')
}
}
}
},
save() {
this.$refs.clinicalDataForm.validate(valid => {
if (!valid) return
this.btnLoading = true
if (this.form.Type !== 'upload') {
this.form.ImageCount = 0
}
addOrUpdateReadingQuestionSystem(this.form).then(res => {
this.btnLoading = false
this.$emit('getList')
this.$emit('close')
this.$message.success(this.$t('common:message:savedSuccessfully'))
})
.catch(() => {
this.btnLoading = false
})
})
},
getBasicConfigSelect() {
getBasicConfigSelect('Reading_eCRF').then(res => {
this.dicList = res.Result
})
},
getGroupOptions() {
return new Promise((resolve, reject) => {
this.loading = true
var param = {
criterionId: this.data.ReadingQuestionCriterionSystemId
}
getSystemGroupNameList(param).then(res => {
this.groupOptions = res.Result
this.loading = false
resolve()
}).catch(() => {
this.loading = false
reject()
})
})
},
getParentQuestions() {
return new Promise((resolve, reject) => {
this.loading = true
var param = {
readingQuestionCriterionSystemId: this.data.ReadingQuestionCriterionSystemId,
id: this.data.Id
}
getSystemCriterionOtherQuestion(param).then(res => {
this.parentOptions = res.Result
this.loading = false
resolve()
}).catch(() => {
this.loading = false
reject()
})
})
},
questionGenreChange(val, form) {
form.DictionaryCode = ''
form.TypeValue = ''
},
// parentQuestionChange(val, form) {
// this.isParentExistGroup = false
// if (val) {
// var index = this.parentOptions.findIndex(item => {
// return item.QuestionId === val
// })
// if (index !== -1) {
// this.parentTriggerValOptions = this.parentOptions[index].TypeValue.split('|')
// if (this.parentOptions[index].GroupName) {
// this.isParentExistGroup = true
// form.GroupName = this.parentOptions[index].GroupName
// }
// }
// } else {
// form.ParentTriggerValue = ''
// form.GroupName = ''
// }
// },
typeChange(val, data) {
if (val !== 'select' && val !== 'radio') {
data.IsJudgeQuestion = false
}
if (val === 'table') {
data.IsRequired = 2
} else {
data.LesionType = null
}
data.DictionaryCode = ''
data.TypeValue = ''
data.ValueType = ''
data.Unit = ''
},
isShowQuestionChange(val, form) {
console.log(val)
if (val === 2) {
form.IsRequired = 2
form.ParentId = ''
form.ParentTriggerValue = ''
} else {
form.ParentId = ''
form.ParentTriggerValue = ''
}
},
isRequiredChange(val, form) {
if (val !== 1) {
form.RelevanceId = ''
form.RelevanceValue = ''
}
},
parentQuestionChange(val, form) {
this.isParentExistGroup = false
if (val) {
var index = this.parentOptions.findIndex(item => {
return item.QuestionId === val
})
if (index !== -1) {
this.parentTriggerValOptions = this.parentOptions[index].TypeValue.split('|')
if (this.parentOptions[index].GroupName) {
this.isParentExistGroup = true
form.GroupName = this.parentOptions[index].GroupName
}
}
} else {
form.GroupName = ''
}
form.ParentTriggerValue = ''
},
relevanceQuestionChange(val, form) {
if (val) {
var index = this.parentOptions.findIndex(item => {
return item.QuestionId === val
})
if (index !== -1) {
this.reParentTriggerValOptions = this.parentOptions[index].TypeValue.split('|')
}
}
form.RelevanceValue = ''
},
isJudgeQuestionChange(val, form) {
if (val) {
form.IsRequired = 0
}
},
getLesionType() {
return new Promise((resolve, reject) => {
this.loading = true
var param = {
criterionId: this.data.ReadingQuestionCriterionSystemId
}
getCriterionLesionType(param).then(res => {
this.lesionTypes = res.Result
this.loading = false
resolve()
}).catch(() => {
this.loading = false
reject()
})
})
},
close() {
this.$emit('close')
}
}
}
</script>
<style lang="scss" scoped>
</style>

View File

@ -0,0 +1,256 @@
<template>
<div class="criterion-form-item">
<el-form-item
:label="`${question.QuestionName}`"
:prop="question.Id"
:rules="[
{ required: (question.IsRequired === 0 || (question.IsRequired ===1 && question.RelevanceId && !!~question.RelevanceValueList.indexOf(questionForm[question.RelevanceId]))) && question.Type!=='group',
message: $t('common:ruleMessage:specify'), trigger: ['blur']},
]"
:class="[question.ClinicalTableQuestionType==='group'?'mb':question.ClinicalTableQuestionType==='upload'?'uploadWrapper':'']"
>
<el-input
v-if="question.ClinicalTableQuestionType==='input'"
v-model="questionForm[question.Id]"
:disabled="question.TableQuestionType === 2"
/>
<!-- 多行文本输入框 -->
<el-input
v-if="question.ClinicalTableQuestionType==='textarea'"
v-model="questionForm[question.Id]"
type="textarea"
:autosize="{ minRows: 2, maxRows: 4}"
/>
<!-- 下拉框 -->
<el-select
v-if="question.ClinicalTableQuestionType==='select'"
v-model="questionForm[question.Id]"
clearable
:disabled="(question.TableQuestionType === 2 || question.QuestionGenre === 2) && !!question.DictionaryCode"
>
<template v-if="question.TableQuestionType === 1">
<el-option
v-for="item in organList"
:key="item.Id"
:label="item[question.DataTableColumn]"
:value="item[question.DataTableColumn]"
/>
</template>
<template v-else-if="question.TableQuestionType === 3 || question.QuestionGenre === 3">
<el-option
v-for="item of $d[question.DictionaryCode]"
:key="item.id"
:value="item.value"
:label="item.label"
/>
</template>
<template v-else-if="(question.TableQuestionType === 2 || question.QuestionGenre === 2) && question.DictionaryCode">
<el-option
v-for="item of $d[question.DictionaryCode]"
:key="item.id"
:value="item.value"
:label="item.label"
/>
</template>
<template v-else>
<el-option
v-for="val in question.TypeValue.split('|')"
:key="val"
:label="val"
:value="val"
/>
</template>
</el-select>
<!-- 单选 -->
<el-date-picker
v-if="question.ClinicalTableQuestionType === 'time'"
v-model="questionForm[question.Id]"
format="yyyy-MM-dd"
value-format="yyyy-MM-dd"
style="width: 200px"
align="right"
type="date"
placeholder="选择日期"
>
</el-date-picker>
<!-- 单选 -->
<el-radio-group
v-if="question.ClinicalTableQuestionType==='radio'"
v-model="questionForm[question.Id]"
@change="((val)=>{formItemChange(val, question)})"
>
<template v-if="question.DictionaryCode">
<el-radio
v-for="item of $d[question.DictionaryCode]"
:key="item.id"
:label="item.value"
>
{{ item.label }}
</el-radio>
</template>
<template v-if="question.TypeValue">
<el-radio
v-for="val in question.TypeValue.split('|')"
:key="val"
:label="val"
>
{{ val }}
</el-radio>
</template>
</el-radio-group>
<!-- 复选框 -->
<el-checkbox-group
v-if="question.ClinicalTableQuestionType==='checkbox'"
v-model="questionForm[question.Id]"
>
<el-checkbox
v-for="val in question.TypeValue.split('|')"
:key="val"
:label="val"
>
{{ val }}
</el-checkbox>
</el-checkbox-group>
<el-input
type="number"
v-if="question.ClinicalTableQuestionType === 'number'"
style="width: 200px"
v-model="questionForm[question.Id]"
>
<template slot="append" v-if="question.Unit">{{question.Unit}}</template>
</el-input>
</el-form-item>
</div>
</template>
<script>
import { uploadReadingAnswerImage, getTrialOrganList, getCustomTableQuestionPreview } from '@/api/trials'
export default {
name: 'QuestionFormItem',
props: {
questionForm: {
type: Object,
default() {
return {}
}
},
question: {
type: Object,
default() {
return []
}
},
},
data() {
return {
addOrEdit: { visible: false, title: '' },
fileList: [],
accept: '.png,.jpg,.jpeg',
imgVisible: false,
imageUrl: '',
urls: [],
organList: [],
QuestionsList: [],
QuestionsForm: {}
}
},
computed: {
calculationValue: {
get() {
return this.questionForm[this.question.Id]
},
set() {
}
}
},
watch: {
questionForm: {
deep: true,
immediate: true,
handler(v) {
}
}
},
mounted() {
console.log(this.question)
if (this.question.ClinicalTableQuestionType === 'number') {
this.$set(this.questionForm, this.question.Id, 0)
} else if (this.question.ClinicalTableQuestionType === 'time') {
this.$set(this.questionForm, this.question.Id, null)
} else if (this.question.ClinicalTableQuestionType === 'checkbox') {
this.$set(this.questionForm, this.question.Id, [])
} else{
this.$set(this.questionForm, this.question.Id, '')
}
},
methods: {
save() {
},
openAddTableCol(row) {
this.addOrEdit.visible = true
this.addOrEdit.title = row.QuestionName + '表格问题'
this.QuestionsList = row.TableQuestions.Questions
this.AnswersList = row.TableQuestions.Answers
this.questionForm = {}
console.log(this.questionForm)
},
formItemChange(v, question) {
},
formItemNumberChange(v, question) {
this.$emit('formItemTableNumberChange', v, question)
},
resetChild(obj) {
obj.forEach(i => {
this.$emit('resetFormItemData', i.Id)
if (i.Childrens && i.Childrens.length > 0) {
this.resetChild(i.Childrens)
}
})
},
resetFormItemData(v) {
this.$emit('resetFormItemData', v)
},
setFormItemData(obj) {
this.$emit('setFormItemData', obj)
},
}
}
</script>
<style lang="scss" scoped>
.my_dialog{
.criterion-form-item{
>>>.el-form-item__content{
width: auto;
}
}
}
.criterion-form-item{
.el-form-item{
display: flex;
flex-direction: row;
align-items: flex-start;
}
>>>.el-form-item__content{
width: 500px;
}
.el-input{
width:100%;
}
.mb{
margin-bottom: 0px;
}
.disabled{
>>>.el-upload--picture-card {
display: none;
}
}
.uploadWrapper{
display: flex;
flex-direction: column;
align-items: flex-start;
}
}
</style>

View File

@ -0,0 +1,11 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
xmlns="http://www.w3.org/2000/svg"
width="40"
height="40"
viewBox="0 0 40 40">
<path
d="M 1.5006714,23.536225 6.8925879,18.994244 14.585721,26.037937 34.019683,4.5410479 38.499329,9.2235032 14.585721,35.458952 z"
id="path4"
style="fill:#ffff00;fill-opacity:1;stroke:#000000;stroke-width:1.25402856;stroke-opacity:1" />
</svg>

After

Width:  |  Height:  |  Size: 415 B

View File

@ -0,0 +1,380 @@
<template>
<el-form v-if="globalForm.taskList.length > 0" ref="globalRuleForm" :model="globalForm">
<el-table
v-loading="loading"
:data="globalForm.taskList"
>
<!-- 访视名称 -->
<el-table-column
prop="BlindName"
:label="$t('trials:globalReview:table:visitName')"
show-overflow-tooltip
width="150"
/>
<!-- 评估结果 -->
<el-table-column
:label="$t('trials:globalReview:table:evaluationRes')"
align="center"
prop=""
>
<template>
<el-table-column
v-for="(qs,index) in globalInfo.evaluationQsList"
:key="index"
prop=""
:label="qs"
show-overflow-tooltip
width="150"
>
<template slot-scope="scope">
<div v-if="scope.row.BeforeQuestionList.length>index && scope.row.BeforeQuestionList[index].Answer" :style="{color: scope.row.BeforeQuestionList[index].IsGlobalAnswer ? '#f66' : null}">
<span v-if="scope.row.BeforeQuestionList[index].DictionaryCode">
{{ $fd(scope.row.BeforeQuestionList[index].DictionaryCode,parseInt(scope.row.BeforeQuestionList[index].Answer)) }}
</span>
<span v-else>{{ scope.row.BeforeQuestionList[index].Answer }}</span>
</div>
</template>
</el-table-column>
</template>
</el-table-column>
<!-- 是否同意访视结果 -->
<el-table-column
prop=""
:label="$t('trials:globalReview:table:isAgreeEvaluationRes')"
show-overflow-tooltip
width="170"
>
<template slot-scope="scope">
<div v-if="readingTaskState<2 ">
<el-form-item
:prop="`${scope.$index}${scope.row.AgreeOrNot[0].GlobalAnswerType}`"
label=""
:rules="[
{ required:true,message: $t('common:ruleMessage:select'), trigger: ['change','blur']},
]"
>
<el-radio-group
v-model="globalForm[`${scope.$index}${scope.row.AgreeOrNot[0].GlobalAnswerType}`]"
@change="handleAgreeOrNotChange(scope.$index,scope.row.AgreeOrNot[0].GlobalAnswerType)"
>
<el-radio
v-for="item of $d.ReadingYesOrNo"
:key="'AgreeOrNot' + item.value"
:label="String(item.value)"
>
{{ item.label }}
</el-radio>
</el-radio-group>
</el-form-item>
</div>
<el-tag v-else-if="scope.row.AgreeOrNot.length > 0 && parseInt(scope.row.AgreeOrNot[0].Answer) === 1" type="primary">
{{ $fd('ReadingYesOrNo',parseInt(scope.row.AgreeOrNot[0].Answer)) }}
</el-tag>
<el-tag v-else-if="scope.row.AgreeOrNot.length > 0 && parseInt(scope.row.AgreeOrNot[0].Answer) === 0" type="danger"> {{ $fd('ReadingYesOrNo',parseInt(scope.row.AgreeOrNot[0].Answer)) }}</el-tag>
<span v-else />
</template>
</el-table-column>
<!-- 调整后结果 -->
<el-table-column
:label="$t('trials:globalReview:table:adjustedRes')"
align="center"
prop=""
>
<template>
<el-table-column
v-for="(qs,index) in globalInfo.adjustedQsList"
:key="index"
prop=""
:label="qs"
show-overflow-tooltip
:width="index === 3 ? '300' : '150'"
>
<template slot-scope="scope">
<div v-if="readingTaskState<2 ">
<span v-if="(scope.row.IsBaseLine && scope.row.AfterQuestionList[index].LimitEdit === 2) || (!scope.row.IsBaseLine && scope.row.AfterQuestionList[index].LimitEdit === 1)">
{{$fd(scope.row.AfterQuestionList[index].DictionaryCode, parseInt(scope.row.AfterQuestionList[index].VisitAnswer))}}
</span>
<el-form-item
style="margin-bottom: 0;"
v-else
:prop="`${scope.$index}${scope.row.AfterQuestionList[index].QuestionId?scope.row.AfterQuestionList[index].QuestionId:scope.row.AfterQuestionList[index].GlobalAnswerType}`"
label=""
:rules="[
{ required:parseInt(globalForm[`${scope.$index}${scope.row.AgreeOrNot[0].GlobalAnswerType}`]) === 0,message: $t('common:ruleMessage:specify'), trigger: ['change','blur']},
]"
>
<label v-if="parseInt(globalForm[`${scope.$index}${scope.row.AgreeOrNot[0].GlobalAnswerType}`]) === 0" />
<!-- 裁判问题 -->
<template v-if="scope.row.AfterQuestionList[index].GlobalAnswerType === 0 ">
<el-tooltip class="item" effect="dark" :content="getText(globalForm[`${scope.$index}${scope.row.AfterQuestionList[index].QuestionId?scope.row.AfterQuestionList[index].QuestionId:String(scope.row.AfterQuestionList[index].GlobalAnswerType)}`], scope.row.AfterQuestionList[index])" placement="top-start">
<el-select
v-model="globalForm[`${scope.$index}${scope.row.AfterQuestionList[index].QuestionId?scope.row.AfterQuestionList[index].QuestionId:String(scope.row.AfterQuestionList[index].GlobalAnswerType)}`]"
style="width:90%;"
:disabled="parseInt(globalForm[`${scope.$index}${scope.row.AgreeOrNot[0].GlobalAnswerType}`]) !== 0"
>
<template v-if="scope.row.AfterQuestionList[index].TypeValue">
<el-option
v-for="val in scope.row.AfterQuestionList[index].TypeValue.split('|')"
:key="val"
:label="val"
:value="val"
/>
</template>
<template v-else-if="scope.row.AfterQuestionList[index].DictionaryCode && scope.row.AfterQuestionList[index].QuestionType === 13">
<el-option
v-for="item of $d[scope.row.AfterQuestionList[index].DictionaryCode]"
v-show="item.value!==-1"
:key="item.id"
:value="String(item.value)"
:label="item.label"
:disabled="String(getBeforeAnswer(scope.row.AfterQuestionList[index].QuestionName,scope.row)) === String(item.value)"
/>
</template>
<template v-else-if="scope.row.AfterQuestionList[index].DictionaryCode">
<el-option
v-for="item of $d[scope.row.AfterQuestionList[index].DictionaryCode]"
:key="item.id"
:value="String(item.value)"
:disabled="String(getBeforeAnswer(scope.row.AfterQuestionList[index].QuestionName,scope.row)) === String(item.value)"
:label="item.label"
/>
</template>
</el-select>
</el-tooltip>
</template>
<!-- 评估更新类型 GlobalAnswerType:3 -->
<template v-if="scope.row.AfterQuestionList[index].GlobalAnswerType === 3">
<el-tooltip class="item" effect="dark" :content="getText(globalForm[`${scope.$index}${scope.row.AfterQuestionList[index].GlobalAnswerType}`], scope.row.AfterQuestionList[index], scope.row.AfterQuestionList[index].GlobalAnswerType)" placement="top-start">
<el-select
v-model="globalForm[`${scope.$index}${scope.row.AfterQuestionList[index].GlobalAnswerType}`]"
style="width:90%;"
:disabled="parseInt(globalForm[`${scope.$index}${scope.row.AgreeOrNot[0].GlobalAnswerType}`]) !== 0"
>
<el-option
v-for="val in globalInfo.assessTypeList"
v-show="(scope.row.IsBaseLine && val.IsBaseLineUse) || (!scope.row.IsBaseLine && !val.IsBaseLineUse)"
:key="val.Code"
:label="language === 'en'?val.Value:val.ValueCN"
:value="val.Code"
/>
</el-select>
</el-tooltip>
</template>
<el-input
v-if="scope.row.AfterQuestionList[index].GlobalAnswerType === 1"
v-model="globalForm[`${scope.$index}${scope.row.AfterQuestionList[index].QuestionId?scope.row.AfterQuestionList[index].QuestionId:String(scope.row.AfterQuestionList[index].GlobalAnswerType)}`]"
type="textarea"
maxlength="100"
show-word-limit
style="width:90%;"
:autosize="{ minRows: 2 }"
:disabled="parseInt(globalForm[`${scope.$index}${scope.row.AgreeOrNot[0].GlobalAnswerType}`]) !== 0"
/>
</el-form-item>
</div>
<div v-else-if="scope.row.AfterQuestionList.length>index && scope.row.AfterQuestionList[index].Answer">
<span v-if="scope.row.AfterQuestionList[index].GlobalAnswerType === 3">
{{ getAssessType(scope.row.AfterQuestionList[index].Answer) }}
</span>
<span v-else-if="scope.row.AfterQuestionList[index].DictionaryCode">
{{ $fd(scope.row.AfterQuestionList[index].DictionaryCode,parseInt(scope.row.AfterQuestionList[index].Answer)) }}
</span>
<span v-else>{{ scope.row.AfterQuestionList[index].Answer }}</span>
</div>
</template>
</el-table-column>
</template>
</el-table-column>
<el-table-column
:label="$t('common:action:action')"
width="200"
>
<template slot-scope="scope">
<el-button
circle
:title="$t('trials:globalReview:table:view')"
icon="el-icon-view"
@click="handleView(scope.row)"
/>
</template>
</el-table-column>
</el-table>
</el-form>
</template>
<script>
import { getToken } from '@/utils/auth'
import { batchSubmitGlobalReadingInfo } from '@/api/trials'
import { mapGetters } from 'vuex'
export default {
props: {
globalInfo: {
type: Object,
default() {
return {}
}
},
globalForm: {
type: Object,
default() {
return {}
}
},
readingTaskState: {
type: Number,
required: true
}
},
data() {
return {
loading: false,
assessTypes: []
}
},
computed: {
...mapGetters(['language'])
},
mounted() {
this.assessTypes = this.globalInfo.assessTypeList.filter(i => i.IsBaseLineUse === this.isBaseline)
},
methods: {
getText(val, row, type) {
var text
if (type === 3) {
var o = this.globalInfo.assessTypeList.find(v => {
return parseInt(v.Code) === parseInt(val)
})
if (!o) {
return ''
}
return this.language === 'en' ? o.Value : o.ValueCN
} else if (row.TypeValue) {
return val
} else if (row.DictionaryCode){
return this.$fd(row.DictionaryCode, parseInt(val))
}
},
handleView(row) {
var token = getToken()
var visitTaskId = row.VisitTaskId
var criterionType = this.$router.currentRoute.query.criterionType
var readingTool = this.$router.currentRoute.query.readingTool
readingTool = isNaN(parseInt(readingTool)) ? null : parseInt(readingTool)
var isReadingTaskViewInOrder = this.$router.currentRoute.query.isReadingTaskViewInOrder
var path = ''
if (readingTool === 0) {
path = `/readingDicoms?trialId=${this.globalInfo.trialId}&subjectCode=${this.globalInfo.subjectCode}&subjectId=${this.globalInfo.subjectId}&visitTaskId=${visitTaskId}&isReadingTaskViewInOrder=${isReadingTaskViewInOrder}&criterionType=${criterionType}&readingTool=${readingTool}&TokenKey=${token}`
} else {
path = `/noneDicomReading?trialId=${this.globalInfo.trialId}&subjectCode=${this.globalInfo.subjectCode}&subjectId=${this.globalInfo.subjectId}&visitTaskId=${visitTaskId}&isReadingTaskViewInOrder=${isReadingTaskViewInOrder}&criterionType=${criterionType}&readingTool=${readingTool}&TokenKey=${token}`
}
const routeData = this.$router.resolve({ path })
window.open(routeData.href, '_blank')
},
handleSave(isPrompt = true) {
return new Promise((resolve, reject) => {
this.$refs['globalRuleForm'].validate(valid => {
if (valid) {
this.loading = true
var visitTaskAnswerList = []
this.globalForm.taskList.forEach((item, index) => {
var answerList = []
item.AfterQuestionList.map(i => {
var obj = {}
if (i.QuestionId) {
obj.questionId = i.QuestionId
obj.globalAnswerType = i.GlobalAnswerType
obj.answer = this.globalForm[ `${index}${i.QuestionId}`]
} else {
obj.questionId = ''
obj.globalAnswerType = i.GlobalAnswerType
obj.answer = this.globalForm[ `${index}${i.GlobalAnswerType}`]
}
answerList.push(obj)
})
answerList.push({ questionId: '', globalAnswerType: item.AgreeOrNot[0].GlobalAnswerType,
answer: this.globalForm[ `${index}${item.AgreeOrNot[0].GlobalAnswerType}`] })
visitTaskAnswerList.push({ visitTaskId: item.VisitTaskId, answerList: answerList })
})
var params = {
globalTaskId: this.globalInfo.visitTaskId,
subjectId: this.globalInfo.subjectId,
trialId: this.globalInfo.trialId,
visitTaskAnswerList
}
batchSubmitGlobalReadingInfo(params).then(res => {
this.loading = false
if (isPrompt) {
console.log(isPrompt)
this.$message.success(this.$t('common:message:savedSuccessfully'))
}
this.$emit('getGlInfo')
resolve()
}).catch(() => {
this.loading = false
reject()
})
} else {
reject()
}
})
})
},
getBeforeAnswer(qsId, row) {
var i = row.BeforeQuestionList.findIndex(item => item.QuestionName === qsId)
if (i > -1 && row.BeforeQuestionList[i].Answer) {
var answer = ''
if (row.BeforeQuestionList[i].DictionaryCode) {
answer = row.BeforeQuestionList[i].Answer
} else {
answer = row.BeforeQuestionList[i].Answer
}
return answer
} else {
return ''
}
},
handleAgreeOrNotChange(index, globalAnswerType) {
var agreeOrNot = parseInt(this.globalForm[`${index}${globalAnswerType}`])
if (agreeOrNot === 1) {
var qsList = this.globalForm.taskList[index].AfterQuestionList
qsList.map(v => {
if (v.QuestionId) {
this.globalForm[`${index}${v.QuestionId}`] = ''
} else if (v.GlobalAnswerType !== globalAnswerType) {
this.globalForm[`${index}${v.GlobalAnswerType}`] = ''
}
})
}
},
getAssessType(v) {
console.log(this.language)
var i = this.globalInfo.assessTypeList.findIndex(i => String(i.Code) === String(v))
if (i > -1) {
return this.language === 'en' ? this.globalInfo.assessTypeList[i].Value : this.globalInfo.assessTypeList[i].ValueCN
} else {
return ''
}
}
}
}
</script>
<style lang="scss" scoped>
>>>.el-form-item label:before {
content: '*';
color: #F56C6C;
margin-right: 4px;
}
>>>.el-textarea .el-input__count{
background: rgba(0,0,0,0);
line-height: normal;
}
>>>.el-form-item{
margin-bottom: 0px;
}
</style>

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,245 @@
<template>
<BaseContainer>
<template slot="search-container">
<div style="margin-left:auto;">
<!-- 提交 -->
<el-button
type="primary"
:disabled="submitIdArr.length==0"
icon="el-icon-check"
:loading="loading"
@click="handleSubmit"
>
{{ $t('trials:seletctedReviews:button:submit') }}
</el-button>
<!-- 下载所有 -->
<el-button
v-if="hasPermi(['role:pm'])"
type="primary"
icon="el-icon-download"
:disabled="!total"
:loading="loading"
@click="handleDownloadAll"
>
{{ $t('trials:seletctedReviews:button:submissionDownAll') }}
</el-button>
</div>
</template>
<template slot="main-container">
<el-table
v-loading="listLoading"
v-adaptive="{bottomOffset:65}"
height="100"
:data="list"
class="table"
:row-class-name="handleHighLighRow"
@sort-change="handleSortChange"
@selection-change="handleSelectionChange"
>
<el-table-column type="selection" :selectable="handleSelectTable" />
<el-table-column type="index" width="40" />
<!-- Name -->
<el-table-column
prop="LastName"
:label="$t('trials:seletctedReviews:table:name')"
show-overflow-tooltip
sortable="custom"
min-width="120"
>
<template slot-scope="scope">
<router-link
style="color: #428bca;"
tag="a"
:to="{
path: `/trialsResume?doctorId=${scope.row.Id}&token=${token}`,
}"
target="_blank"
>{{ scope.row.LastName }} / {{ scope.row.FirstName }}</router-link>
</template>
</el-table-column>
<!-- Name CN -->
<el-table-column
prop="ChineseName"
:label="$t('trials:seletctedReviews:table:nameCN')"
show-overflow-tooltip
sortable="custom"
min-width="120"
/>
<!-- ID -->
<el-table-column
prop="Code"
:label="$t('trials:seletctedReviews:table:id')"
show-overflow-tooltip
sortable="custom"
min-width="100"
/>
<!-- Status -->
<el-table-column
prop="DoctorTrialState"
:label="$t('trials:seletctedReviews:table:status')"
min-width="100"
>
<template slot-scope="scope">
<el-tag v-if="scope.row.DoctorTrialState === 5" type="primary">{{ $fd('DoctorTrialState', scope.row.DoctorTrialState) }}</el-tag>
<el-tag v-else type="danger">{{ $fd('DoctorTrialState', 16) }}</el-tag>
</template>
</el-table-column>
<!-- Submitter -->
<el-table-column
prop="OptUserName"
:label="$t('trials:seletctedReviews:table:submitter')"
min-width="120"
/>
<!-- Submission Time -->
<el-table-column
prop="OptTimeStr"
:label="$t('trials:seletctedReviews:table:submissionTime')"
min-width="150"
/>
<el-table-column
:label="$t('common:action:action')"
fixed="right"
width="200"
>
<template slot-scope="scope">
<!-- <el-button type="primary" icon="el-icon-view" @click="handleDetail(scope.row)">Detail</el-button> -->
<el-button
icon="el-icon-download"
circle
:title="$t('trials:seletctedReviews:button:submissionDown')"
@click="handleDownload(scope.row)"
/>
</template>
</el-table-column>
</el-table>
<pagination class="page" :total="total" :page.sync="listQuery.PageIndex" :limit.sync="listQuery.PageSize" @pagination="getList" />
<el-dialog
:title="$t('trials:seletctedReviews:title:language')"
:visible.sync="dialogVisible"
width="30%"
>
<!-- 语言 -->
<span>{{ $t('trials:seletctedReviews:label:language') }}: </span>
<el-radio-group v-model="language">
<el-radio :label="2">English</el-radio>
<el-radio :label="1">中文</el-radio>
</el-radio-group>
<span slot="footer" class="dialog-footer">
<el-button @click="dialogVisible = false">{{ $t('common:button:cancel') }}</el-button>
<el-button type="primary" @click="handleDownloadResumes">{{ $t('common:button:save') }}</el-button>
</span>
</el-dialog>
</template>
</BaseContainer>
</template>
<script>
import BaseContainer from '@/components/BaseContainer'
import Pagination from '@/components/Pagination'
import { getSubmissionOrApprovalReviewerList, submitReviewer, downloadResume } from '@/api/trials'
import store from '@/store'
const enrollState = 1
export default {
name: 'Submission',
components: { BaseContainer, Pagination },
data() {
return {
list: [],
listQuery: {
TrialId: '',
IntoGroupSearchState: enrollState,
PageIndex: 1,
PageSize: 20,
Asc: false,
SortField: ''
},
loading: false,
total: 0,
listLoading: false,
submitIdArr: [],
dialogVisible: false,
language: 2,
token: store.getters.token
}
},
created() { this.initPage() },
methods: {
initPage() {
this.getList()
},
getList() {
this.listLoading = true
this.listQuery.TrialId = this.$route.query.trialId
getSubmissionOrApprovalReviewerList(this.listQuery).then(res => {
this.listLoading = false
this.list = res.Result.CurrentPageData
this.total = res.Result.TotalCount
// eslint-disable-next-line handle-callback-err
}).catch(() => { this.listLoading = false })
},
handleSubmit() {
// 是否确认提交?
this.$confirm(this.$t('trials:seletctedReviews:message:msg2'), {
type: 'warning'
}).then(() => {
this.loading = true
const trialId = this.$route.query.trialId
submitReviewer(trialId, this.submitIdArr, 1).then(res => {
this.loading = false
if (res.IsSuccess) {
this.getList()
// 保存成功
this.$message.success(this.$t('common:message:savedSuccessfully'))
this.$emit('nextStep', 'approval')
}
}).catch(() => {
this.loading = false
})
})
},
handleSelectionChange(val) {
const arr = []
for (let index = 0; index < val.length; index++) {
arr.push(val[index].Id)
}
this.submitIdArr = arr
},
handleSortChange(column) {
if (column.order === 'ascending') {
this.listQuery.Asc = true
} else {
this.listQuery.Asc = false
}
this.listQuery.SortField = column.prop
this.listQuery.PageIndex = 1
this.getList()
},
handleHighLighRow({ row, rowIndex }) {
if (row.DoctorTrialState === 5) {
return 'selected'
}
},
handleSelectTable(row) { return row.DoctorTrialState !== 5 },
handleDetail(row) {
const { href } = this.$router.resolve({ path: `/trialsResume?doctorId=${row.Id}` })
window.open(href, '_blank')
},
handleDownloadAll() {
this.dialogVisible = true
},
handleDownloadResumes() {
this.dialogVisible = false
const arr = []
for (let index = 0; index < this.list.length; index++) {
arr.push(this.list[index].Id)
}
this.downloadResume(arr)
},
downloadResume(arr) {
downloadResume(this.$route.query.trialId, this.language, arr).then(res => { window.open(res.Result) })
},
handleDownload(row) { this.downloadResume([row.Id]) }
}
}
</script>

View File

@ -0,0 +1,108 @@
<template>
<div />
</template>
<script>
import { getNextTask, getReadingTool } from '@/api/trials'
import store from '@/store'
import { changeURLStatic } from '@/utils/history.js'
export default {
name: 'ReadPage',
data() {
return {
trialId: '',
subjectCode: '',
subjectId: '',
visitTaskId: '',
readingCategory: 2,
taskBlindName: '',
isReadingShowSubjectInfo: false,
isReadingShowPreviousResults: false,
TrialReadingCriterionId: null,
isExistsClinicalData: false,
tabName: '',
isExistsNoDicomFile: false
}
},
mounted() {
if (this.$router.currentRoute.query.TokenKey) {
store.dispatch('user/setToken', this.$router.currentRoute.query.TokenKey)
changeURLStatic('TokenKey', '')
}
this.tabName = this.$router.currentRoute.query.tabName ? this.$router.currentRoute.query.tabName : 'read'
this.trialId = this.$router.currentRoute.query.trialId
this.subjectCode = this.$router.currentRoute.query.subjectCode
this.subjectId = this.$router.currentRoute.query.subjectId ? this.$router.currentRoute.query.subjectId : ''
this.visitTaskId = this.$router.currentRoute.query.visitTaskId ? this.$router.currentRoute.query.visitTaskId : ''
// this.TrialReadingCriterionId = this.$router.currentRoute.query.trialReadingCriterionId
this.TrialReadingCriterionId = localStorage.getItem('TrialReadingCriterionId')
// this.isReadingShowPreviousResults = this.$router.currentRoute.query.isReadingShowPreviousResults
this.getTaskInfo()
},
methods: {
getTaskInfo() {
const loading = this.$loading({ fullscreen: true })
var param = {
subjectId: this.subjectId,
trialId: this.trialId,
visistTaskId: this.visitTaskId,
subjectCode: this.subjectCode,
TrialReadingCriterionId: this.TrialReadingCriterionId
}
getNextTask(param).then(async res => {
this.readingCategory = res.Result.ReadingCategory
this.subjectId = res.Result.SubjectId
this.visitTaskId = res.Result.VisitTaskId
this.subjectCode = res.Result.SubjectCode
this.taskBlindName = res.Result.TaskBlindName
this.isReadingShowSubjectInfo = res.Result.IsReadingShowSubjectInfo
this.isExistsClinicalData = res.Result.IsExistsClinicalData
if (this.$router.currentRoute.query.isReadingShowPreviousResults !== undefined && this.$router.currentRoute.query.isReadingShowPreviousResults !== null) {
this.isReadingShowPreviousResults = this.$router.currentRoute.query.isReadingShowPreviousResults
} else {
this.isReadingShowPreviousResults = res.Result.IsReadingShowPreviousResults
}
this.isExistsNoDicomFile = res.Result.IsExistsNoDicomFile
loading.close()
if (res.Result.ReadingCategory === 1) {
// 访视阅片
this.getCriterionConfig()
} else if (res.Result.ReadingCategory === 2) {
// 全局阅片
this.$router.push({
path: `/globalReview?trialId=${this.trialId}&subjectCode=${this.subjectCode}&subjectId=${this.subjectId}&visitTaskId=${this.visitTaskId}&readingCategory=${this.readingCategory}&taskBlindName=${this.taskBlindName}&isReadingShowSubjectInfo=${this.isReadingShowSubjectInfo}&isReadingShowPreviousResults=${this.isReadingShowPreviousResults}`
})
} else if (res.Result.ReadingCategory === 4) {
// 裁判阅片
this.$router.push({
path: `/adReview?trialId=${this.trialId}&subjectCode=${this.subjectCode}&subjectId=${this.subjectId}&visitTaskId=${this.visitTaskId}&readingCategory=${this.readingCategory}&taskBlindName=${this.taskBlindName}&isReadingShowSubjectInfo=${this.isReadingShowSubjectInfo}&isReadingShowPreviousResults=${this.isReadingShowPreviousResults}`
})
} else if (res.Result.ReadingCategory === 5) {
// 肿瘤学阅片
this.$router.push({
path: `/oncologyReview?trialId=${this.trialId}&subjectCode=${this.subjectCode}&subjectId=${this.subjectId}&visitTaskId=${this.visitTaskId}&readingCategory=${this.readingCategory}&taskBlindName=${this.taskBlindName}&isReadingShowSubjectInfo=${this.isReadingShowSubjectInfo}&isReadingShowPreviousResults=${this.isReadingShowPreviousResults}`
})
}
}).catch(() => { loading.close() })
},
getCriterionConfig() {
const loading = this.$loading({ fullscreen: true })
getReadingTool({ visitTaskId: this.visitTaskId }).then(res => {
loading.close()
if (res.Result.ReadingTool === 0) {
window.location.href = `/readingDicoms?TrialReadingCriterionId=${res.Result.TrialReadingCriterionId}&CriterionType=${res.Result.CriterionType}&trialId=${this.trialId}&subjectCode=${this.subjectCode}&subjectId=${this.subjectId}&visitTaskId=${this.visitTaskId}&readingCategory=${this.readingCategory}&taskBlindName=${this.taskBlindName}&isReadingShowSubjectInfo=${this.isReadingShowSubjectInfo}&isReadingShowPreviousResults=${this.isReadingShowPreviousResults}&tabName=${this.tabName}&isExistsClinicalData=${this.isExistsClinicalData}&isExistsNoDicomFile=${this.isExistsNoDicomFile}`
// this.$router.push({
// path: `/readingDicoms?TrialReadingCriterionId=${res.Result.TrialReadingCriterionId}&CriterionType=${res.Result.CriterionType}&trialId=${this.trialId}&subjectCode=${this.subjectCode}&subjectId=${this.subjectId}&visitTaskId=${this.visitTaskId}&readingCategory=${this.readingCategory}&taskBlindName=${this.taskBlindName}&isReadingShowSubjectInfo=${this.isReadingShowSubjectInfo}&isReadingShowPreviousResults=${this.isReadingShowPreviousResults}&tabName=${this.tabName}&isExistsClinicalData=${this.isExistsClinicalData}`
// })
} else {
// if (res.Result.ReadingTool === 1)
this.$router.push({
path: `/noneDicomReading?trialId=${this.trialId}&subjectCode=${this.subjectCode}&subjectId=${this.subjectId}&visitTaskId=${this.visitTaskId}&readingCategory=${this.readingCategory}&taskBlindName=${this.taskBlindName}&isReadingShowSubjectInfo=${this.isReadingShowSubjectInfo}&isReadingShowPreviousResults=${this.isReadingShowPreviousResults}&isExistsClinicalData=${this.isExistsClinicalData}`
})
}
}).catch(() => { loading.close() })
}
}
}
</script>

View File

@ -0,0 +1,32 @@
import createImageIdsAndCacheMetaData from './createImageIdsAndCacheMetaData';
import wadoURICreateImageIds from './WADOURICreateImageIds';
import initDemo from './initDemo';
import setCtTransferFunctionForVolumeActor, {
ctVoiRange,
} from './setCtTransferFunctionForVolumeActor';
import setPetTransferFunctionForVolumeActor from './setPetTransferFunctionForVolumeActor';
import setPetColorMapTransferFunctionForVolumeActor from './setPetColorMapTransferFunctionForVolumeActor';
import setTitleAndDescription from './setTitleAndDescription';
import addButtonToToolbar from './addButtonToToolbar';
import addCheckboxToToolbar from './addCheckboxToToolbar';
import addToggleButtonToToolbar from './addToggleButtonToToolbar';
import addDropdownToToolbar from './addDropdownToToolbar';
import addSliderToToolbar from './addSliderToToolbar';
import camera from './camera';
export {
createImageIdsAndCacheMetaData,
wadoURICreateImageIds,
initDemo,
setTitleAndDescription,
addButtonToToolbar,
addCheckboxToToolbar,
addDropdownToToolbar,
addSliderToToolbar,
addToggleButtonToToolbar,
setPetColorMapTransferFunctionForVolumeActor,
setPetTransferFunctionForVolumeActor,
setCtTransferFunctionForVolumeActor,
ctVoiRange,
camera,
};

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,48 @@
<template>
<div class="comment-info">
<el-form :model="statusList" class="demo-form-inline" size="small">
<el-form-item label="Comment:">
<el-input
v-model="statusList.AdminComment"
type="textarea"
autosize
readonly
/>
</el-form-item>
</el-form>
</div>
</template>
<script>
export default {
props: {
statusList: {
type: Object,
default() {
return {}
}
}
}
}
</script>
<style lang="scss">
.comment-info{
padding:5px 15px;
font-size:13px;
.el-form-item--mini.el-form-item{
margin-bottom: 0px;
}
.el-form-item--small.el-form-item{
margin-bottom: 0px;
}
.el-form-item__content{
font-size: 13px;
}
.el-form-item__label{
font-size: 13px;
color: #303133;
font-weight: bold;
}
}
</style>

View File

@ -0,0 +1,434 @@
<template>
<BaseContainer>
<el-tabs v-model="TrialReadingCriterionId" type="border-card">
<el-tab-pane v-for="i of trialCriterionList" :key="i.TrialReadingCriterionId" :label="i.TrialReadingCriterionName" :name="i.TrialReadingCriterionId">
<div v-if="TrialReadingCriterionId === i.TrialReadingCriterionId">
<div slot="search-container">
<el-form :inline="true">
<el-form-item
style="margin-bottom:10px"
label="是否加急"
>
<el-select
v-model="searchData.IsUrgent"
clearable
style="width:120px;"
>
<el-option
v-for="item of $d.YesOrNo"
:key="'IsUrgent' + item.label"
:value="item.value"
:label="item.label"
/>
</el-select>
</el-form-item>
<!-- 受试者编号 -->
<el-form-item label="受试者编号">
<el-input
v-model="searchData.SubjectCode"
style="width:130px;"
clearable
/>
</el-form-item>
<!-- 盲态访视名称 -->
<el-form-item
style="margin-bottom:10px"
label="盲态访视名称"
>
<el-input
v-model="searchData.TaskBlindName"
style="width:100px;"
clearable
/>
</el-form-item>
<!-- 任务类型 -->
<el-form-item
style="margin-bottom:10px"
label="任务类型"
>
<el-select
v-model="searchData.ReadingCategory"
clearable
style="width:120px;"
>
<el-option
v-for="item of $d.ReadingCategory"
:key="'ReadingCategory' + item.label"
:value="item.value"
:label="item.label"
/>
</el-select>
</el-form-item>
<!-- 审核状态 -->
<el-form-item
style="margin-bottom:10px"
label="审核状态"
>
<el-select
v-model="searchData.AuditState"
clearable
style="width:120px;"
>
<el-option
v-for="item of $d.MedicalReviewAuditState"
:key="'AuditState' + item.label"
:value="item.value"
:label="item.label"
/>
</el-select>
</el-form-item>
<!-- 医学审核建议 -->
<el-form-item
style="margin-bottom:10px"
label="医学审核建议"
>
<el-select
v-model="searchData.AuditAdviceEnum"
clearable
style="width:120px;"
>
<el-option
v-for="item of $d.AuditAdvice"
:key="'AuditAdvice' + item.label"
:value="item.value"
:label="item.label"
/>
</el-select>
</el-form-item>
<!-- 是否认可 -->
<el-form-item
style="margin-bottom:10px"
label="是否认可"
>
<el-select
v-model="searchData.DoctorUserIdeaEnum"
clearable
style="width:120px;"
>
<el-option
v-for="item of $d.MedicalReviewDoctorUserIdea"
:key="'MedicalReviewDoctorUserIdea' + item.label"
:value="item.value"
:label="item.label"
/>
</el-select>
</el-form-item>
<el-form-item>
<!-- 查询 -->
<el-button type="primary" icon="el-icon-search" @click="handleSearch">
{{ $t('common:button:search') }}
</el-button>
<!-- 重置 -->
<el-button type="primary" icon="el-icon-refresh-left" @click="handleReset">
{{ $t('common:button:reset') }}
</el-button>
</el-form-item>
</el-form>
</div>
<div slot="main-container">
<el-table
v-adaptive="{bottomOffset:75}"
v-loading="loading"
:data="list"
stripe
height="100"
>
<el-table-column type="index" width="40" align="left" />
<!-- 是否加急 -->
<el-table-column
prop="IsUrgent"
min-width="100"
label="是否加急"
show-overflow-tooltip
sortable="custom"
>
<template slot-scope="scope">
<el-tag v-if="scope.row.IsUrgent" type="danger">{{ $fd('YesOrNo', scope.row.IsUrgent) }}</el-tag>
<el-tag v-else type="primary">{{ $fd('YesOrNo', scope.row.IsUrgent) }}</el-tag>
</template>
</el-table-column>
<!-- 受试者编号 -->
<el-table-column
prop="SubjectCode"
min-width="100"
label="受试者编号"
show-overflow-tooltip
sortable="custom"
/>
<!-- 盲态访视名称 -->
<el-table-column
prop="TaskBlindName"
min-width="100"
label="盲态访视名称"
show-overflow-tooltip
sortable="custom"
/>
<el-table-column
prop="TrialReadingCriterionName"
label="阅片标准"
min-width="140"
show-overflow-tooltip
sortable="custom"
/>
<!-- 任务类型 -->
<el-table-column
prop="ReadingCategory"
label="任务类型"
min-width="100"
sortable="custom"
show-overflow-tooltip
>
<template slot-scope="scope">
<el-tag v-if="scope.row.ReadingCategory === 1" type="primary">
{{ $fd('ReadingCategory', scope.row.ReadingCategory) }}
</el-tag>
<el-tag v-if="scope.row.ReadingCategory === 2" type="info">
{{ $fd('ReadingCategory', scope.row.ReadingCategory) }}
</el-tag>
<el-tag v-if="scope.row.ReadingCategory === 4" type="danger">
{{ $fd('ReadingCategory', scope.row.ReadingCategory) }}
</el-tag>
<el-tag v-if="scope.row.ReadingCategory === 5" type="warning">
{{ $fd('ReadingCategory', scope.row.ReadingCategory) }}
</el-tag>
</template>
</el-table-column>
<!-- 任务状态 -->
<el-table-column
prop="TaskState"
label="阅片任务状态"
min-width="100"
show-overflow-tooltip
sortable="custom"
>
<template slot-scope="scope">
<el-tag v-if="scope.row.TaskState === 0" type="primary">{{ $fd('TaskState', scope.row.TaskState) }}</el-tag>
<el-tag v-if="scope.row.TaskState === 1" type="info">{{ $fd('TaskState', scope.row.TaskState) }}</el-tag>
<el-tag v-if="scope.row.TaskState === 3" type="danger">{{ $fd('TaskState', scope.row.TaskState) }}</el-tag>
<el-tag v-if="scope.row.TaskState === 4" type="warning">{{ $fd('TaskState', scope.row.TaskState) }}</el-tag>
</template>
</el-table-column>
<el-table-column
prop="AuditState"
label="审核状态"
min-width="100"
sortable="custom"
show-overflow-tooltip
>
<template slot-scope="scope">
<el-tag v-if="scope.row.AuditState === 0" type="warning">
{{ $fd('MedicalReviewAuditState', scope.row.AuditState) }}
</el-tag>
<el-tag v-if="scope.row.AuditState === 1" type="primary">
{{ $fd('MedicalReviewAuditState', scope.row.AuditState) }}
</el-tag>
<el-tag v-if="scope.row.AuditState === 2" type="danger">
{{ $fd('MedicalReviewAuditState', scope.row.AuditState) }}
</el-tag>
</template>
</el-table-column>
<!-- 首次质询时间 -->
<el-table-column
prop="FirstReplyTime"
min-width="100"
label="首次质询时间"
show-overflow-tooltip
sortable="custom"
/>
<!-- 最近回复时间 -->
<el-table-column
prop="LastReplyTime"
min-width="100"
label="最近回复时间"
show-overflow-tooltip
sortable="custom"
/>
<!-- 医学审核建议 -->
<el-table-column
prop="AuditAdviceEnum"
label="医学审核建议"
min-width="120"
sortable="custom"
show-overflow-tooltip
>
<template slot-scope="scope">
{{ $fd('AuditAdvice',scope.row.AuditAdviceEnum) }}
</template>
</el-table-column>
<!-- 是否认可 -->
<el-table-column
prop="DoctorUserIdeaEnum"
min-width="100"
label="是否认可"
show-overflow-tooltip
sortable="custom"
>
<template slot-scope="scope">
{{ $fd('MedicalReviewDoctorUserIdea',scope.row.DoctorUserIdeaEnum) }}
</template>
</el-table-column>
<el-table-column
:label="$t('common:action:action')"
width="200"
fixed="right"
>
<template slot-scope="scope">
<!-- 阅片 -->
<el-button
:disabled="!scope.row.IsHaveQuestion || scope.row.IsInvalid"
circle
title="查看"
icon="el-icon-edit-outline"
@click="handleReadMecialAudit(scope.row)"
/>
<el-button
:disabled="!scope.row.IsHaveQuestion || scope.row.IsInvalid"
circle
title="回复"
icon="el-icon-chat-dot-round"
@click="handleReply(scope.row)"
/>
</template>
</el-table-column>
</el-table>
<!-- 分页组件 -->
<pagination class="page" :total="total" :page.sync="searchData.PageIndex" :limit.sync="searchData.PageSize" @pagination="getList" />
</div>
</div>
</el-tab-pane>
</el-tabs>
<el-dialog
v-if="chatForm.visible"
:visible.sync="chatForm.visible"
:close-on-click-modal="false"
custom-class="base-dialog-wrapper"
:title="chatForm.title"
width="600px"
>
<ChatForm
:task-medical-review-id="currentRow.Id"
:visit-task-id="currentRow.VisitTaskId"
@getList="getList"
@close="chatForm.visible = false"
/>
</el-dialog>
<!-- 审核 -->
<el-dialog
v-if="auditVisible"
:visible.sync="auditVisible"
:close-on-click-modal="false"
:fullscreen="true"
title="医学审核详情"
>
<MedicalAudit
:task-medical-review-id="currentRow.Id"
:visit-task-id="currentRow.VisitTaskId"
:row-data="currentRow"
@getList="getList"
@close="auditVisible=false"
/>
</el-dialog>
</BaseContainer>
</template>
<script>
import { getIRMedicalFeedbackList } from '@/api/trials'
import { getTrialCriterionList } from '@/api/trials/reading'
import BaseContainer from '@/components/BaseContainer'
import Pagination from '@/components/Pagination'
import ChatForm from './components/ChatForm'
import MedicalAudit from '@/views/trials/trials-panel/reading/mim-medical-audit/components/MedicalAudit'
const searchDataDefault = () => {
return {
IsUrgent: null,
SubjectCode: '',
TaskBlindName: '',
ReadingCategory: null,
AuditState: null,
AuditAdviceEnum: null,
DoctorUserIdeaEnum: null,
PageIndex: 1,
PageSize: 20
}
}
export default {
name: 'ReadTask',
components: { BaseContainer, Pagination, ChatForm, MedicalAudit },
data() {
return {
searchData: searchDataDefault(),
list: [],
total: 0,
loading: false,
trialId: '',
currentRow: {},
chatForm: { visible: false, title: '质询记录' },
auditVisible: false,
trialCriterionList: [],
TrialReadingCriterionId: '0'
}
},
watch: {
TrialReadingCriterionId(v) {
if (v) {
this.getList()
}
}
},
mounted() {
this.trialId = this.$route.query.trialId
this.getTrialCriterionList()
},
methods: {
getTrialCriterionList() {
getTrialCriterionList(this.trialId).then(res => {
this.trialCriterionList = res.Result
this.TrialReadingCriterionId = this.trialCriterionList[0].TrialReadingCriterionId
}).catch(() => {})
},
getList() {
this.loading = true
this.searchData.TrialId = this.trialId
this.searchData.TrialReadingCriterionId = this.TrialReadingCriterionId
getIRMedicalFeedbackList(this.searchData).then(res => {
this.list = res.Result.CurrentPageData
this.total = res.Result.TotalCount
this.loading = false
}).catch(() => { this.loading = false })
},
handleSearch() {
this.searchData.PageIndex = 1
this.getList()
},
handleReset() {
this.searchData = searchDataDefault()
this.getList()
},
handleReadMecialAudit(row) {
this.currentRow = { ...row }
this.auditVisible = true
},
handleReply(row) {
this.currentRow = { ...row }
this.chatForm.visible = true
},
// 排序
handleSortChange(column) {
if (column.order === 'ascending') {
this.searchData.Asc = true
} else {
this.searchData.Asc = false
}
this.searchData.SortField = column.prop
this.searchData.PageIndex = 1
this.getList()
}
}
}
</script>

View File

@ -0,0 +1,111 @@
/* eslint-disable no-unused-vars */
import dcmjs from 'dcmjs'
import { calculateSUVScalingFactors } from '@cornerstonejs/calculate-suv'
import { getPTImageIdInstanceMetadata } from './getPTImageIdInstanceMetadata'
import { utilities } from '@cornerstonejs/core'
// import cornerstoneWADOImageLoader from 'cornerstone-wado-image-loader'
import cornerstoneDICOMImageLoader from '@cornerstonejs/dicom-image-loader'
import ptScalingMetaDataProvider from './ptScalingMetaDataProvider'
import getPixelSpacingInformation from './getPixelSpacingInformation'
import { convertMultiframeImageIds, prefetchMetadataInformation } from './convertMultiframeImageIds'
import removeInvalidTags from './removeInvalidTags'
const { calibratedPixelSpacingMetadataProvider } = utilities
function parseImageId(imageId) {
// build a url by parsing out the url scheme and frame index from the imageId
const firstColonIndex = imageId.indexOf(':')
let url = imageId.substring(firstColonIndex + 1)
const frameIndex = url.indexOf('frame=')
let frame
if (frameIndex !== -1) {
const frameStr = url.substr(frameIndex + 6)
frame = parseInt(frameStr, 10)
url = url.substr(0, frameIndex - 1)
}
return {
scheme: imageId.substr(0, firstColonIndex),
url,
frame
}
}
async function createImageIdsAndCacheMetaData({
modality,
imageIds
}) {
await prefetchMetadataInformation(imageIds)
imageIds = convertMultiframeImageIds(imageIds)
imageIds.forEach((imageId) => {
const parseImage = parseImageId(imageId)
const imageData =
cornerstoneDICOMImageLoader.wadouri.dataSetCacheManager.get(parseImage.url)
const arrayBuffer = imageData.byteArray.buffer
var dicomData = dcmjs.data.DicomMessage.readFile(arrayBuffer)
var dataset = dcmjs.data.DicomMetaDictionary.naturalizeDataset(
dicomData.dict
)
dataset._meta = dcmjs.data.DicomMetaDictionary.namifyDataset(
dicomData.meta
)
var instanceMetaData = dataset
instanceMetaData = removeInvalidTags(instanceMetaData)
if (instanceMetaData) {
const pixelSpacing = getPixelSpacingInformation(instanceMetaData)
if (pixelSpacing) {
calibratedPixelSpacingMetadataProvider.add(
imageId,
// pixelSpacing.map((s) => parseFloat(s))
{ rowPixelSpacing: parseFloat(pixelSpacing[0]),
columnPixelSpacing: parseFloat(pixelSpacing[1])
}
)
}
}
})
if (modality === 'PT') {
const InstanceMetadataArray = []
imageIds.forEach((imageId) => {
// var parseImage = parseImageId(imageId)
// imageId,
const instanceMetadata = getPTImageIdInstanceMetadata(imageId)
if (typeof instanceMetadata.CorrectedImage === 'string') {
instanceMetadata.CorrectedImage =
instanceMetadata.CorrectedImage.split('\\')
}
if (instanceMetadata) {
InstanceMetadataArray.push(instanceMetadata)
}
})
if (InstanceMetadataArray.length) {
// const suvScalingFactors = calculateSUVScalingFactors(
// InstanceMetadataArray
// )
// InstanceMetadataArray.forEach((instanceMetadata, index) => {
// ptScalingMetaDataProvider.addInstance(
// imageIds[index],
// suvScalingFactors[index]
// )
// })
try {
const suvScalingFactors = calculateSUVScalingFactors(
InstanceMetadataArray
)
InstanceMetadataArray.forEach((instanceMetadata, index) => {
ptScalingMetaDataProvider.addInstance(
imageIds[index],
suvScalingFactors[index]
)
})
} catch (error) {
console.log(error)
}
}
}
return imageIds
}
export { createImageIdsAndCacheMetaData }

View File

@ -0,0 +1,269 @@
<template>
<div class="measurement-wrapper" :style="{'height':height+10+'px'}" style="position: relative">
<div class="container" :style="{'height':height+'px'}" style="padding-bottom: 50px;overflow-y: auto;">
<h3 style="color: #ddd;padding: 5px 0px;margin: 0;" v-if="isReadingShowSubjectInfo">
<span v-if="subjectCode">{{ subjectCode }} </span>
<span style="margin-left:5px;">{{ taskBlindName }}</span>
</h3>
<QuestionsPreview
ref="QuestionsPreview"
v-if="ecrfShow"
:visitTaskId="visitTaskId"
:criterionId="TrialReadingCriterionId">
</QuestionsPreview>
</div>
<div style="position: absolute;bottom:0;left: 0;z-index: 10;background: #000;width: 100%;display: flex;justify-content: right;align-items: center;height: 50px">
<el-button size="mini" v-if="readingTaskState<2" style="margin-right: 10px" @click="handleSave(true)">
保存
</el-button>
<el-button size="mini" style="margin-right: 10px" v-if="readingTaskState<2 && IseCRFShowInDicomReading" @click="handleConfirm">提交</el-button>
</div>
<!-- 签名框 -->
<el-dialog
v-if="signVisible"
:visible.sync="signVisible"
:close-on-click-modal="false"
width="600px"
custom-class="base-dialog-wrapper"
>
<div slot="title">
<span style="font-size:18px;">{{ $t('common:dialogTitle:sign') }}</span>
<span style="font-size:12px;margin-left:5px">{{ `(${$t('common:label:sign')}${ currentUser })` }}</span>
</div>
<SignForm ref="signForm" :sign-code-enum="signCode" @closeDialog="closeSignDialog" />
</el-dialog>
</div>
</template>
<script>
import { submitDicomVisitTask } from '@/api/trials'
import DicomEvent from '../components/DicomEvent'
import SignForm from '@/views/trials/components/newSignForm'
import QuestionsPreview from './CustomizeQuestionsPreview'
import const_ from '@/const/sign-code'
import store from '@/store'
import { mapGetters } from 'vuex'
export default {
name: 'CustomizeMeasurementList',
components: {
QuestionsPreview, SignForm
},
props: {
isReadingShowSubjectInfo: {
type: Boolean,
required: true
},
IseCRFShowInDicomReading: {
type: Boolean,
required: true
}
},
data() {
return {
currentUser: zzSessionStorage.getItem('userName'),
height: window.innerHeight - 140,
visitTaskId: '',
loading: false,
CriterionType: null,
subjectCode: '',
taskBlindName: '',
TrialReadingCriterionId: null,
ecrfShow: false,
signCode: null,
signVisible: false,
readingTaskState: null
}
},
computed: {
...mapGetters(['visitTaskList'])
},
mounted() {
this.subjectCode = this.$router.currentRoute.query.subjectCode
this.CriterionType = parseInt(localStorage.getItem('CriterionType'))
this.TrialReadingCriterionId = this.$router.currentRoute.query.TrialReadingCriterionId
DicomEvent.$on('loadMeasurementList', async obj => {
console.log(11111, this.$refs['QuestionsPreview'])
if (this.visitTaskId !== obj.visitTaskId) {
this.visitTaskId = obj.visitTaskId
this.taskBlindName = obj.taskBlindName
this.$refs['QuestionsPreview'].getCustomTableQuestionAnswer(obj.visitTaskId)
}
})
DicomEvent.$on('getCustomTableQuestionAnswer', (visitTaskId) => {
this.$refs['QuestionsPreview'].getCustomTableQuestionAnswer(visitTaskId)
})
DicomEvent.$on('setCollapseActive', measureData => {
})
DicomEvent.$on('changeLesionType', (questionsObj) => {
})
DicomEvent.$on('getAllUnSaveLesions', (callback) => {
})
DicomEvent.$on('split', measureData => {
})
DicomEvent.$on('setReadingState', readingTaskState => {
this.readingTaskState = readingTaskState
console.log(this.readingTaskState)
})
window.addEventListener('resize', this.setHeight)
},
beforeDestroy() {
DicomEvent.$off('loadMeasurementList')
DicomEvent.$off('setCollapseActive')
DicomEvent.$off('changeLesionType')
DicomEvent.$off('getUnSaveTarget')
DicomEvent.$off('setReadingState')
DicomEvent.$off('getCustomTableQuestionAnswer')
},
methods: {
// 关闭签名框
closeSignDialog(isSign, signInfo) {
if (isSign) {
this.signConfirm(signInfo)
} else {
this.signVisible = false
}
},
// 签名并确认
signConfirm(signInfo) {
this.loading = true
var params = {
data: {
visitTaskId: this.visitTaskId
},
signInfo: signInfo
}
this.signVisible = false
submitDicomVisitTask(params).then(res => {
if (res.IsSuccess) {
try {
this.$message.success(this.$t('common:message:savedSuccessfully'))
if (this.$refs['signForm']) {
this.$refs['signForm'].btnLoading = false
}
this.signVisible = false
// 设置当前任务阅片状态为已读
this.readingTaskState = 2
store.dispatch('reading/setVisitTaskReadingTaskState', { visitTaskId: this.visitTaskId, readingTaskState: 2 })
DicomEvent.$emit('setReadingState', 2)
window.opener.postMessage('refreshTaskList', window.location)
this.$confirm('当前阅片任务已完成,是否进入下一个阅片任务?', {
type: 'warning',
distinguishCancelAndClose: true
})
.then(() => {
try {DicomEvent.$emit('getNextTask')} catch (e) {console.log(e)}
})
.catch(action => {
})
} catch (e) {
console.log(e)
}
}
this.loading = false
}).catch(() => {
this.loading = false
if (this.$refs['signForm'] && this.$refs['signForm'].btnLoading) {
this.$refs['signForm'].btnLoading = false
}
})
},
async handleConfirm() {
var res = await this.$refs['QuestionsPreview'].handleSave(false)
if (res) {
const { ImageAssessmentReportConfirmation } = const_.processSignature
this.signCode = ImageAssessmentReportConfirmation
this.signVisible = true
}
},
handleSave(isMsg) {
this.$refs['QuestionsPreview'].handleSave(isMsg)
},
setHeight() {
this.height = window.innerHeight - 140
},
initPage(obj) {
if (this.visitTaskId !== obj.visitTaskId) {
this.visitTaskId = obj.visitTaskId
this.taskBlindName = obj.taskBlindName
this.activeName = ''
this.ecrfShow = true
}
}
}
}
</script>
<style lang="scss" scoped>
.measurement-wrapper{
overflow-y: auto;
// overflow: hidden;
.container{
padding: 10px;
}
.title{
padding: 5px;
font-weight: bold;
color: #ddd;
font-size: 15px;
}
.add-icon{
padding: 5px;
font-weight: bold;
color: #ddd;
font-size: 15px;
border: 1px solid #938b8b;
margin-bottom: 2px;
cursor: pointer;
}
.add-icon:hover{
background-color: #607d8b;
}
.flex-row{
display: flex;
flex-direction: row;
justify-content: space-between;
background-color: #424242;
}
.lesion_list{
position: relative;
}
.el-collapse{
border-bottom:none;
>>>.el-collapse-item{
background-color: #000!important;
color: #ddd;
}
>>>.el-collapse-item__header{
background-color: #000!important;
color: #ddd;
border-bottom-color:#5a5a5a;
padding-left: 5px;
height: 35px;
line-height: 35px;
}
>>>.el-collapse-item__wrap{
background-color: #000!important;
color: #ddd;
}
>>>.el-collapse-item__content{
width:260px;
position: absolute;
top: 0px;
right: 0px;
// border: 1px solid #ffeb3b;
border: 1px solid #fff;
z-index: 1;
color: #ddd;
padding: 5px;
background-color:#1e1e1e;
}
}
}
</style>

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,318 @@
<template>
<box-content style="height: 100%;background: #fff;">
<!-- 搜索框 -->
<div class="search">
<el-form :inline="true" class="base-search-form">
<!-- 文件类型 -->
<el-form-item :label="$t('trials:attachment:table:fileType')">
<el-select
v-model="searchData.FileTypeId"
style="width:150px;"
>
<el-option
v-for="item of $d.Trial_Document"
:key="item.value"
:label="item.label"
:value="item.value"
/>
</el-select>
</el-form-item>
<!-- 文件名称 -->
<el-form-item :label="$t('trials:attachment:table:fileName')">
<el-input v-model="searchData.Name" style="width:100px;" />
</el-form-item>
<el-form-item>
<!-- 查询 -->
<el-button type="primary" icon="el-icon-search" @click="handleSearch">
{{ $t('common:button:search') }}
</el-button>
<!-- 重置 -->
<el-button type="primary" icon="el-icon-refresh-left" @click="handleReset">
{{ $t('common:button:reset') }}
</el-button>
</el-form-item>
</el-form>
<span style="margin-left:auto;">
<!-- 新增 -->
<el-button
v-hasPermi="['trials:trials-panel:setting:attachment:add']"
type="primary"
icon="el-icon-plus"
@click="handleAdd"
>
{{ $t('common:button:add') }}
</el-button>
</span>
</div>
<!-- 系统文件列表 -->
<el-table
ref="attachmentList"
v-loading="loading"
v-adaptive="{bottomOffset:60}"
:data="list"
stripe
height="100"
@sort-change="handleSortByColumn"
>
<el-table-column type="index" width="40" />
<!-- 文件类型 -->
<el-table-column
prop="FileType"
:label="$t('trials:attachment:table:fileType')"
show-overflow-tooltip
sortable="custom"
min-width="120"
/>
<!-- 文件名称 -->
<el-table-column
prop="Name"
:label="$t('trials:attachment:table:fileName')"
show-overflow-tooltip
sortable="custom"
min-width="120"
/>
<!-- 查看最短时间(分钟) -->
<el-table-column
prop="SignViewMinimumMinutes"
:label="$t('trials:attachment:table:min')"
show-overflow-tooltip
sortable="custom"
min-width="170"
/>
<!-- 是否废除 -->
<el-table-column
prop="IsDeleted"
:label="$t('trials:attachment:table:isDeleted')"
show-overflow-tooltip
sortable="custom"
min-width="100"
>
<template slot-scope="scope">
<el-tag v-if="scope.row.IsDeleted" type="danger">{{ $fd('YesOrNo', scope.row.IsDeleted) }}</el-tag>
<el-tag v-else type="primary">{{ $fd('YesOrNo', scope.row.IsDeleted) }}</el-tag>
</template>
</el-table-column>
<!-- 需要签署的用户类型 -->
<el-table-column
prop="NeedConfirmedUserTypes"
:label="$t('trials:attachment:table:userType')"
show-overflow-tooltip
min-width="160"
>
<template slot-scope="scope">
{{ scope.row.NeedConfirmedUserTypes?scope.row.NeedConfirmedUserTypes.join(', '):'' }}
</template>
</el-table-column>
<!-- 描述 -->
<el-table-column
prop="Description"
:label="$t('trials:attachment:table:description')"
show-overflow-tooltip
min-width="100"
/>
<!-- 更新时间 -->
<el-table-column
prop="UpdateTime"
:label="$t('trials:attachment:table:updateTime')"
show-overflow-tooltip
sortable="custom"
min-width="150"
/>
<el-table-column
:label="$t('common:action:action')"
width="400"
fixed="right"
>
<template slot-scope="scope">
<!-- 预览 -->
<el-button
icon="el-icon-view"
circle
:title="$t('trials:attachment:action:preview')"
@click="handlePreview(scope.row)"
/>
<!-- 编辑 -->
<el-button
v-hasPermi="['trials:trials-panel:setting:attachment:edit']"
circle
:title="$t('trials:attachment:action:edit')"
icon="el-icon-edit-outline"
:disabled="scope.row.IsSomeUserSigned || scope.row.IsDeleted"
@click="handleEdit(scope.row)"
/>
<!-- 废除 -->
<el-button
v-hasPermi="['trials:trials-panel:setting:attachment:abolish']"
:disabled="scope.row.IsDeleted"
circle
:title="$t('trials:attachment:action:apolish')"
icon="el-icon-delete"
@click="handleRepeal(scope.row)"
/>
</template>
</el-table-column>
</el-table>
<!-- 分页组件 -->
<pagination class="page" :total="total" :page.sync="searchData.PageIndex" :limit.sync="searchData.PageSize" @pagination="getList" />
<!-- 新增/编辑 -->
<el-dialog
v-if="editVisible"
:visible.sync="editVisible"
:close-on-click-modal="false"
:title="title"
width="800px"
custom-class="base-dialog-wrapper"
>
<AttachmentForm :data="currentRow" @closeDialog="closeDialog" @getList="getList" />
</el-dialog>
<!-- 预览文件 -->
<el-dialog
v-if="previewVisible"
:visible.sync="previewVisible"
:title="$t('trials:attachment:dialogTitle:preview')"
:fullscreen="true"
append-to-body
custom-class="base-dialog-wrapper"
>
<div class="base-modal-body" style="border:2px solid #ccc;padding: 10px">
<PreviewFile v-if="previewVisible" :file-path="currentPath" :file-type="currentType" />
</div>
</el-dialog>
</box-content>
</template>
<script>
import { getTrialDocumentList, userAbandonDoc } from '@/api/trials'
import BoxContent from '@/components/BoxContent'
import Pagination from '@/components/Pagination'
import AttachmentForm from './components/attachmentForm'
import PreviewFile from './components/previewFile'
const searchDataDefault = () => {
return {
FileTypeId: '',
Name: '',
PageIndex: 1,
PageSize: 20,
SortField: '',
Asc: false
}
}
export default {
name: 'TrialAttachmentList',
components: { BoxContent, Pagination, AttachmentForm, PreviewFile },
dicts: ['Trial_Document'],
data() {
return {
searchData: searchDataDefault(),
loading: false,
list: [],
total: 0,
editVisible: false,
previewVisible: false,
title: '',
currentRow: {},
currentPath: '',
currentType: '',
trialId: ''
}
},
mounted() {
this.trialId = this.$route.query.trialId
this.getList()
},
methods: {
// 获取系统文件数据
getList() {
this.loading = true
this.searchData.TrialId = this.trialId
getTrialDocumentList(this.searchData).then(res => {
this.loading = false
const { CurrentPageData, TotalCount } = res.Result
CurrentPageData.forEach(item => {
item.NeedConfirmedUserTypes = []
item.NeedConfirmedUserTypeeIds = []
item.NeedConfirmedUserTypeList.forEach((i) => {
item.NeedConfirmedUserTypes.push(i.UserTypeShortName)
item.NeedConfirmedUserTypeeIds.push(i.NeedConfirmUserTypeId)
})
})
this.list = CurrentPageData
this.total = TotalCount
}).catch(() => {
this.loading = false
})
},
// 新增
handleAdd() {
this.title = this.$t('common:button:add')
this.currentRow = {}
this.editVisible = true
},
// 预览
handlePreview(row) {
const { Name, FullFilePath } = row
this.currentPath = FullFilePath
this.currentType = row.Name ? Name.substring(Name.lastIndexOf('.') + 1).toLocaleLowerCase() : ''
this.previewVisible = true
},
// 编辑
handleEdit(row) {
this.title = this.$t('common:button:edit')
this.currentRow = { ...row }
this.editVisible = true
},
// 废除
handleRepeal(row) {
this.$confirm(this.$t('trials:attachment:message:abolish'), {
type: 'warning',
distinguishCancelAndClose: true
})
.then(() => {
this.loading = true
userAbandonDoc(row.Id, false)
.then(res => {
this.loading = false
if (res.IsSuccess) {
this.getList()
this.$message.success(this.$t('trials:trials-list:message:abolitionSuccessfully'))
}
}).catch(() => { this.loading = false })
}).catch(() => {})
},
// 关闭编辑弹窗
closeDialog() {
this.editVisible = false
},
// 重置
handleReset() {
this.searchData = searchDataDefault()
this.getList()
this.$nextTick(() => {
this.$refs.attachmentList.clearSort()
})
},
// 查询
handleSearch() {
this.searchData.PageIndex = 1
this.getList()
},
// 排序
handleSortByColumn(column) {
if (column.order === 'ascending') {
this.searchData.Asc = true
} else {
this.searchData.Asc = false
}
this.searchData.SortField = column.prop
this.searchData.PageIndex = 1
this.getList()
}
}
}
</script>

View File

@ -0,0 +1,808 @@
<template>
<div v-loading="loading" class="study-wrapper">
<h4 v-if="isReadingShowSubjectInfo" style="color: #ddd;padding: 5px 0px;margin: 0;text-align: center;background-color: #4c4c4c;">
{{ subjectCode }}
</h4>
<h4 v-if="isReadingShowSubjectInfo" style="color: #ddd;padding: 5px 0px;margin: 0;text-align: center;background-color: #4c4c4c;margin-bottom: 5px;">
{{ taskBlindName }}
</h4>
<div class="ps">
<el-collapse v-model="activeNames">
<el-collapse-item v-for="(study, index) in studyList" :key="`${study.StudyId}`" :name="`${study.StudyId}`">
<template slot="title">
<div v-if="study.IsCriticalSequence" class="dicom-desc">
<!-- 关键序列 -->
{{ $t('trials:reading:title:keySeries') }}
</div>
<div
v-else
class="dicom-desc"
style="width: 150px;white-space: nowrap;overflow: hidden;text-overflow: ellipsis;"
>
<el-tooltip class="item" effect="dark" :content="`${study.StudyCode} ${study.Description?study.Description:''} ${study.Modalities} (${study.SeriesCount})`" placement="right">
<div>
<span>{{ study.StudyCode }} {{ study.Description }}</span>
<span> {{ study.Modalities }} ({{ study.SeriesCount }})</span>
</div>
</el-tooltip>
</div>
</template>
<div class="series">
<div
v-for="(series, i) in study.SeriesList"
:key="i"
style="position:relative;margin:10px 0px;"
series-type="current"
@click="showSeriesImage(index,i,series)"
>
<div
:class="{'series-active': i==seriesIndex && index === studyIndex}"
class="series-wrapper"
>
<el-image
class="image-preview"
:src="series.previewImageUrl"
fit="fill"
/>
<div class="image-desc">
<div class="flex-div">
<div>#{{ series.seriesNumber }} </div>
<div v-if="series.isDicom && series.prefetchInstanceCount<series.instanceCount">
<!-- 下载 -->
<el-tooltip v-if="!series.isLoading" class="item" effect="dark" :content="$t('trials:reading:button:download')" placement="bottom">
<i class="el-icon-video-play" style="font-size: 18px;margin-right: 5px;color: #ffeb3b;cursor: pointer;" @click.stop="loadSeries(series,index,i)" />
</el-tooltip>
<!-- 暂停 -->
<el-tooltip v-else class="item" effect="dark" :content="$t('trials:reading:button:pause')" placement="bottom">
<i class="el-icon-video-pause" style="font-size: 18px;margin-right: 5px;color: #ffeb3b;cursor: pointer;" @click.stop="stopLoadSeries(series,index,i)" />
</el-tooltip>
</div>
<el-tooltip v-else-if="series.prefetchInstanceCount === 0" class="item" effect="dark" :content="$t('trials:reading:button:download')" placement="bottom">
<i class="el-icon-video-play" style="font-size: 18px;margin-right: 5px;color: #ffeb3b;cursor: pointer;" @click.stop="loadSeries(series,index,i)" />
</el-tooltip>
</div>
<p v-show="series.description">
<el-tooltip class="item" effect="dark" :content="series.description" placement="right">
<div style="">{{ series.description }}</div>
</el-tooltip>
</p>
<p v-show="series.sliceThickness">
T: {{ series.sliceThickness }}
</p>
<p v-show="series.instanceCount">
{{ series.modality }}: {{ series.instanceCount }} image
</p>
<div class="flex-div">
<div v-if="measureData.findIndex(v=>v.SeriesId === series.seriesId) > -1">
<!-- 有标注 -->
<el-tooltip class="item" effect="dark" :content="$t('trials:reading:button:marked')" placement="right">
<i class="el-icon-star-on" style="font-size: 16px;color: #ff5722;" />
</el-tooltip>
</div>
</div>
</div>
</div>
<div v-if="series.isDicom && series.prefetchInstanceCount>0 && series.prefetchInstanceCount<series.instanceCount" style="width: 100%;">
<el-progress
:percentage="parseInt(((series.prefetchInstanceCount/series.instanceCount)*100).toFixed(2))"
/>
</div>
</div>
</div>
</el-collapse-item>
</el-collapse>
</div>
</div>
</template>
<script>
import * as dicomParser from 'dicom-parser'
import * as cornerstone from 'cornerstone-core'
import * as cornerstoneWADOImageLoader from 'cornerstone-wado-image-loader'
import requestPoolManager from '@/utils/request-pool'
import DicomEvent from './DicomEvent'
import { mapGetters } from 'vuex'
import store from '@/store'
cornerstoneWADOImageLoader.external.dicomParser = dicomParser
cornerstoneWADOImageLoader.external.cornerstone = cornerstone
// const maximumSizeInBytes = 1024 * 1024 * 1024 // 1 GB
export default {
name: 'StudyList',
props: {
trialId: {
type: String,
required: true
},
subjectVisitId: {
type: String,
required: true
},
visitTaskId: {
type: String,
required: true
},
isReading: {
type: Number,
default: 0
},
taskBlindName: {
type: String,
default: ''
},
isReadingShowSubjectInfo: {
type: Boolean,
required: true
}
},
data() {
return {
studyList: [],
cachedImages: [],
studyIndex: null,
seriesIndex: null,
loading: false,
measureData: [],
isRender: false,
subjectCode: '',
activeNames: [],
seriesArr: [],
imageList: [],
loopLoadStatus: 0 // -1暂停下载 0未下载完成 1下载完成
}
},
computed: {
...mapGetters(['visitTaskList', 'currentTaskId', 'activeSeries'])
},
watch: {
visitTaskList: {
immediate: true,
handler(val) {
var idx = this.visitTaskList.findIndex(i => i.VisitTaskId === this.visitTaskId)
if (idx === -1) return
this.measureData = this.visitTaskList[idx].MeasureData
}
}
// activeSeries: {
// immediate: true,
// handler(val) {
// console.log('activeSeries', val)
// this.studyIndex = val.studyIndex
// this.seriesIndex = val.seriesIndex
// }
// }
},
mounted() {
this.subjectCode = this.$router.currentRoute.query.subjectCode
// DicomEvent.$on('getMeasureData', () => {
// var idx = this.visitTaskList.findIndex(i => i.VisitTaskId === this.visitTaskId)
// this.measureData = this.visitTaskList[idx].MeasureData
// })
// DicomEvent.$on('setReadingState', readingTaskState => {
// var idx = this.visitTaskList.findIndex(i => i.VisitTaskId === this.visitTaskId)
// if (idx > -1) {
// this.studyList = this.visitTaskList[idx].StudyList
// }
// })
cornerstone.events.addEventListener('cornerstoneimageloaded', this.cornerstoneImageLoaded)
},
beforeDestroy() {
cornerstone.imageCache.purgeCache()
// DicomEvent.$off('getMeasureData')
},
methods: {
initStudyInfo() {
const loading = this.$loading({ fullscreen: true })
// 初始化待渲染序列
this.getInitSeries().then((res) => {
requestPoolManager.startTaskTimer()
res.map((item) => {
this.loadInitialImage(item)
})
var i = res.findIndex(s => s.isCurrentTask)
if (i > -1) {
var p = this.visitTaskId === this.currentTaskId ? parseInt(new Date().getTime()) : 999 // 非当前任务,符合自动下载的权重次之
var idx = this.visitTaskList.findIndex(i => i.VisitTaskId === this.visitTaskId)
if (idx > -1) {
this.measureData = this.visitTaskList[idx].MeasureData
this.studyList = this.visitTaskList[idx].StudyList
this.studyList.map((study, studyIndex) => {
study.SeriesList.map((series, seriesIndex) => {
var sliceThickness = isNaN(parseInt(series.sliceThickness)) ? null : parseInt(series.sliceThickness)
if (sliceThickness === 5 || series.instanceCount <= 100) {
series.imageIds.map(image => {
let priority = 0
if (series.seriesId === res[i].seriesId) {
priority = parseInt(new Date().getTime()) * 10
} else {
priority = --p
}
this.imageList.push({ imageId: image, seriesId: series.seriesId, studyIndex: studyIndex, seriesIndex: seriesIndex, visitTaskId: series.visitTaskId, priority })
})
}
})
})
this.loopLoad()
}
}
DicomEvent.$emit('loadImageStacks', res)
loading.close()
this.isRender = true
}).catch(() => {
loading.close()
})
},
// 获取初始化序列信息
loadInitialImage(seriesInfo) {
var p = parseInt(new Date().getTime())
var imageId = seriesInfo.imageIds[seriesInfo.imageIdIndex]
requestPoolManager.loadAndCacheImagePlus(imageId, seriesInfo.seriesId, p * 100).then(res => {
this.imageLoaded(seriesInfo, res.data.string('x0020000e'))
})
},
getStudyList() {
if (!this.isRender) {
var idx = this.visitTaskList.findIndex(i => i.VisitTaskId === this.visitTaskId)
if (idx > -1 && this.visitTaskList[idx].StudyList && this.visitTaskList[idx].StudyList.length > 0) {
this.measureData = this.visitTaskList[idx].MeasureData
this.studyList = this.visitTaskList[idx].StudyList
var sIdx = this.studyList.findIndex(s => s.IsDicom)
if (sIdx > -1) {
this.studyIndex = sIdx
this.seriesIndex = 0
this.activeNames = [`${this.studyList[sIdx].StudyId}`]
this.loadImages(this.visitTaskList[idx])
}
}
this.isRender = true
}
},
async getInitSeries() {
var seriesList = []
var isReadingTaskViewInOrder = JSON.parse(this.$router.currentRoute.query.isReadingTaskViewInOrder)
var idx = this.visitTaskList.findIndex(i => i.VisitTaskId === this.visitTaskId)
this.studyList = this.visitTaskList[idx].StudyList
if (this.visitTaskList[idx].IsBaseLineTask || !isReadingTaskViewInOrder) {
// 基线
const obj = this.getFirstMarkedSeries(this.visitTaskList[idx].MeasureData, [...this.visitTaskList[idx].StudyList])
if (Object.keys(obj).length !== 0) {
this.studyIndex = obj.studyIndex
this.seriesIndex = obj.seriesIndex
seriesList.push(obj.series)
this.activeNames = [`${this.studyList[ this.studyIndex].StudyId}`]
this.studyList[ obj.studyIndex].SeriesList[obj.seriesIndex].isFirstRender = true
} else {
// 初始化问题表单
if (this.studyList.length > 0) {
this.activeNames = [`${this.studyList[0].StudyId}`]
}
// DicomEvent.$emit('loadMeasurementList', { visitTaskId: this.visitTaskList[idx].VisitTaskId, taskBlindName: this.visitTaskList[idx].TaskBlindName })
}
} else {
// 随访
var bsIdx = null
var criterionType = parseInt(localStorage.getItem('CriterionType'))
if (criterionType === 10) {
const i = this.visitTaskList.findIndex(i => i.IsCurrentTask)
bsIdx = i - 1
} else {
bsIdx = this.visitTaskList.findIndex(i => i.IsBaseLineTask)
}
if (bsIdx > -1) {
var trialId = this.$router.currentRoute.query.trialId
await store.dispatch('reading/getMeasuredData', this.visitTaskList[bsIdx].VisitTaskId)
await store.dispatch('reading/getStudyInfo', { trialId: trialId, subjectVisitId: this.visitTaskList[bsIdx].VisitId, visitTaskId: this.visitTaskList[bsIdx].VisitTaskId, taskBlindName: this.visitTaskList[bsIdx].TaskBlindName })
const firstObj = this.getFirstMarkedSeries(this.visitTaskList[bsIdx].MeasureData, [...this.visitTaskList[bsIdx].StudyList])
seriesList.push(firstObj.series)
const secondObj = this.getSecondMarkedSeries(firstObj, { ...this.visitTaskList[idx] })
this.studyIndex = secondObj.studyIndex
this.seriesIndex = secondObj.seriesIndex
seriesList.push(secondObj.series)
this.studyList[ secondObj.studyIndex].SeriesList[secondObj.seriesIndex].isFirstRender = true
this.studyIndex = secondObj.studyIndex
this.seriesIndex = secondObj.seriesIndex
this.activeNames = [`${this.studyList[secondObj.studyIndex].StudyId}`]
} else {
const sIdx = this.studyList.findIndex(s => s.IsDicom)
const series = this.studyList[sIdx].SeriesList[0]
series.imageIdIndex = Math.floor(series.imageIds.length / 2)
seriesList.push(series)
this.studyIndex = sIdx
this.seriesIndex = 0
this.activeNames = [`${this.studyList[sIdx].StudyId}`]
}
}
return seriesList
},
getSecondMarkedSeries(baseObj, visitTaskInfo) {
var obj = {}
var studyList = visitTaskInfo.StudyList
var measureDatas = visitTaskInfo.MeasureData
if (baseObj.isMarked) {
var i = measureDatas.findIndex(i => Object.keys(i.MeasureData).length > 0 && i.OrderMarkName === baseObj.measureData.OrderMarkName)
if (i !== -1) {
const sdIndx = studyList.findIndex(sd => sd.StudyId === measureDatas[i].StudyId)
const seriesList = studyList[sdIndx].SeriesList
const srIdx = seriesList.findIndex(sr => sr.seriesId === measureDatas[i].SeriesId)
// const instanceList = seriesList[srIdx].imageIds
const instanceList = seriesList[srIdx].instanceList
const isIdx = instanceList.findIndex(is => is.includes(measureDatas[i].InstanceId))
const series = seriesList[srIdx]
series.imageIdIndex = isIdx
obj.studyIndex = sdIndx
obj.seriesIndex = srIdx
obj.series = series
obj.seriesId = series.seriesId
return obj
}
}
var seriesInfo = null
var seriesList = studyList.map(s => s.SeriesList).flat()
var similarArr = seriesList.map((i, index) => {
return { similar: this.strSimilarity2Percent(i.description, baseObj.series.description), index }
})
similarArr = similarArr.sort((a, b) => {
return b.similar - a.similar
})
var idx = similarArr[0] && similarArr[0].similar > 0.85 ? similarArr[0].index : -1
if (idx > -1) {
seriesInfo = seriesList[idx]
}
// const idx = seriesList.findIndex(series => series.description === baseSeries.description)
// idx > -1 ? obj = seriesList[idx] : ''
if (!seriesInfo) {
// 描述不一致,则筛选层厚一致
const idx = seriesList.findIndex(series => series.sliceThickness === baseObj.series.sliceThickness)
idx > -1 ? seriesInfo = seriesList[idx] : ''
}
if (!seriesInfo) {
// 层厚不一致,则筛选图像数量一致
const idx = seriesList.findIndex(series => series.instanceCount === baseObj.series.instanceCount)
idx > -1 ? seriesInfo = seriesList[idx] : ''
}
if (!seriesInfo) {
// 都不符合要求, 判断是否存在层厚为5的序列否则显示第一个检查的第一个序列
const idx = seriesList.findIndex(series => series.isDicom && parseInt(series.instanceCount) === 5)
idx > -1 ? seriesInfo = seriesList[idx] : seriesInfo = seriesList[0]
}
if (seriesInfo) {
seriesInfo.imageIdIndex = Math.floor(seriesInfo.imageIds.length * (baseObj.series.imageIdIndex / baseObj.series.instanceCount))
obj.studyIndex = seriesInfo.studyIndex
obj.seriesIndex = seriesInfo.seriesIndex
obj.series = seriesInfo
obj.seriesId = seriesInfo.seriesId
}
return obj
},
findSerialNumberClosest(arr, n) {
var absArr = []
for (let i = 0; i < arr.length; i++) {
const m = n - arr[i].seriesNumber
absArr.push({ index: i, n: Math.abs(m) })
}
const num = Math.min.apply(null, absArr.map(item => item.n))
var mIdx = absArr.findIndex(item => item.n === num)
return mIdx
},
getFirstMarkedSeries(measureDatas, studyList) {
var obj = {}
var mIdx = measureDatas.findIndex(m => Object.keys(m.MeasureData).length > 0)
if (mIdx !== -1) {
// 有标记时,显示带标记影像所在序列
const sdIndx = studyList.findIndex(sd => sd.StudyId === measureDatas[mIdx].StudyId && sd.IsDicom)
if (sdIndx > -1) {
const seriesList = studyList[sdIndx].SeriesList
const srIdx = seriesList.findIndex(sr => sr.seriesId === measureDatas[mIdx].SeriesId)
// const instanceList = seriesList[srIdx].imageIds
const instanceList = seriesList[srIdx].instanceList
const isIdx = instanceList.findIndex(is => is.includes(measureDatas[mIdx].InstanceId))
const series = seriesList[srIdx]
series.imageIdIndex = isIdx
obj.studyIndex = sdIndx
obj.seriesIndex = srIdx
obj.series = series
obj.seriesId = series.seriesId
obj.isMarked = true
obj.measureData = measureDatas[mIdx]
}
} else {
var seriesObj = null
for (let i = 0; i < studyList.length; i++) {
const seriesIdx = studyList[i].SeriesList.findIndex(s => s.sliceThickness && parseInt(s.sliceThickness) === 5)
if (seriesIdx > -1) {
seriesObj = { studyIndex: i, seriesIdx, series: studyList[i].SeriesList[seriesIdx] }
break
}
}
if (seriesObj) {
obj.studyIndex = seriesObj.studyIndex
obj.seriesIndex = seriesObj.seriesIdx
seriesObj.series.imageIdIndex = Math.floor(seriesObj.series.imageIds.length / 2)
obj.series = seriesObj.series
obj.seriesId = seriesObj.series.seriesId
obj.isMarked = false
} else {
const sIdx = studyList.findIndex(s => s.IsDicom)
if (sIdx > -1) {
// 判断是否存在层厚为5的序列否则显示第一个检查的第一个序列
const series = studyList[sIdx].SeriesList[0]
var imageIdIndex = Math.floor(series.imageIds.length / 2)
obj.studyIndex = sIdx
obj.seriesIndex = 0
obj.series = series
obj.series.imageIdIndex = imageIdIndex
obj.seriesId = series.seriesId
obj.isMarked = false
}
}
}
return obj
},
strSimilarity2Number(s, t) {
var n = s.length; var m = t.length; var d = []
var i, j, s_i, t_j, cost
if (n === 0) return m
if (m === 0) return n
for (i = 0; i <= n; i++) {
d[i] = []
d[i][0] = i
}
for (j = 0; j <= m; j++) {
d[0][j] = j
}
for (i = 1; i <= n; i++) {
s_i = s.charAt(i - 1)
for (j = 1; j <= m; j++) {
t_j = t.charAt(j - 1)
if (s_i === t_j) {
cost = 0
} else {
cost = 1
}
d[i][j] = this.Minimum(d[i - 1][j] + 1, d[i][j - 1] + 1, d[i - 1][j - 1] + cost)
}
}
return d[n][m]
},
// 两个字符串的相似程度,并返回相似度百分比
strSimilarity2Percent(s, t) {
var l = s.length > t.length ? s.length : t.length
var d = this.strSimilarity2Number(s, t)
return Number((1 - d / l).toFixed(4))
},
Minimum(a, b, c) {
return a < b ? (a < c ? a : c) : (b < c ? b : c)
},
showSeriesImage(studyIndex, seriesIndex, series) {
if (series.isDicom) {
this.studyIndex = studyIndex
this.seriesIndex = seriesIndex
this.studyList[studyIndex].SeriesList[seriesIndex].measureData = this.measureData
var dicomStatck = this.studyList[studyIndex].SeriesList[seriesIndex]
this.$emit('loadImageStack', dicomStatck)
if (!series.loadStatus) {
this.loopLoadStatus = -1
series.isLoading = true
var p = parseInt(new Date().getTime())
series.imageIds.map((imageId, i) => {
var priority = ''
if (i === 0) {
priority = parseInt(new Date().getTime()) * 10
} else {
priority = --p
}
this.imageList.push({ imageId: imageId, seriesId: series.seriesId, studyIndex: studyIndex, seriesIndex: seriesIndex, visitTaskId: series.visitTaskId, priority })
})
if (this.imageList.length > 0) {
this.loopLoadStatus = 0
this.loopLoad()
}
}
DicomEvent.$emit('loadMeasurementList', { visitTaskId: this.visitTaskId, taskBlindName: this.taskBlindName })
} else {
// 非Dicom预览
this.$emit('previewNoneDicoms', { visitTaskId: this.visitTaskId })
}
store.dispatch('reading/setActiveSeries', series)
},
setSeriesActive(obj) {
var idx = this.visitTaskList.findIndex(i => i.VisitTaskId === this.visitTaskId)
if (idx === -1) return
this.studyList = this.visitTaskList[idx].StudyList
this.studyIndex = obj.studyIndex
this.seriesIndex = obj.seriesIndex
var activeNames = `${this.studyList[obj.studyIndex].StudyId}`
if (this.activeNames.includes(activeNames)) return
this.activeNames.push(activeNames)
this.loadImages(this.visitTaskList[idx])
},
selectSeries(obj) {
var seriseList = this.studyList.map(s => s.SeriesList).flat()
var i = seriseList.findIndex(i => i.seriesId === obj.seriesId && i.studyId === obj.studyId)
if (i === -1) return
var idx = i + obj.offset
if (idx < 0) {
idx = 0
} else if (idx >= seriseList.length) {
idx = seriseList.length - 1
}
if (seriseList[idx].seriesIndex === this.seriesIndex && seriseList[idx].studyIndex === this.studyIndex) return
this.showSeriesImage(seriseList[idx].studyIndex, seriseList[idx].seriesIndex, seriseList[idx])
this.handleActiveSeries(seriseList[idx])
},
handleActiveSeries(series) {
this.studyIndex = series.studyIndex
this.seriesIndex = series.seriesIndex
var idx = this.visitTaskList.findIndex(i => i.VisitTaskId === series.visitTaskId)
if (idx === -1) return
this.studyList = this.visitTaskList[idx].StudyList
var activeNames = `${this.studyList[series.studyIndex].StudyId}`
if (this.activeNames.includes(activeNames)) return
this.activeNames.push(activeNames)
this.loadImages(this.visitTaskList[idx])
store.dispatch('reading/setActiveSeries', series)
},
loadImages(taskInfo) {
var priority = taskInfo.IsCurrentTask ? parseInt(new Date().getTime()) : 999
this.studyList.map((study, studyIndex) => {
study.SeriesList.map((series, seriesIndex) => {
if (!series.loadStatus) {
var sliceThickness = isNaN(parseInt(series.sliceThickness)) ? null : parseInt(series.sliceThickness)
if (series.isBeMark || sliceThickness === 5 || series.instanceCount <= 100) {
series.imageIds.map(image => {
priority = priority - 1
this.imageList.push({ imageId: image, seriesId: series.seriesId, studyIndex: studyIndex, seriesIndex: seriesIndex, visitTaskId: series.visitTaskId, priority })
})
}
}
})
})
this.loopLoad()
},
loadImages2(taskInfo) {
var priority = parseInt(new Date().getTime())
// 当前任务只自动下载层厚为5mm 或者影像数量小于 100张
if (taskInfo.IsCurrentTask) {
this.studyList.map((study, studyIndex) => {
study.SeriesList.map((series, seriesIndex) => {
var sliceThickness = isNaN(parseInt(series.sliceThickness)) ? null : parseInt(series.sliceThickness)
if (sliceThickness === 5 || series.instanceCount <= 100) {
series.imageIds.map(image => {
priority = priority - 1
this.imageList.push({ imageId: image, seriesId: series.seriesId, studyIndex: studyIndex, seriesIndex: seriesIndex, visitTaskId: series.visitTaskId, priority })
})
}
})
})
this.loopLoad()
} else {
// 非当前任务只自动下载关键序列
var idx = this.studyList.findIndex(study => study.IsCriticalSequence)
if (idx === -1) return
this.studyList[idx].SeriesList.forEach((series, seriesIndex) => {
if (!series.loadStatus) {
series.imageIds.map(image => {
priority--
this.imageList.push({ imageId: image, seriesId: series.seriesId, studyIndex: idx, seriesIndex: seriesIndex, visitTaskId: series.visitTaskId, priority })
})
}
})
this.loopLoad()
}
},
loopLoad() {
if (this.imageList.length > 0) {
requestPoolManager.startTaskTimer()
this.imageList.map(image => {
requestPoolManager.loadAndCacheImagePlus(image.imageId, image.seriesId, image.priority).then(res => {
this.imageLoaded(image, res.data.string('x0020000e'))
})
})
this.imageList = []
}
},
// 启动下载序列
loadSeries(series, studyIndex, seriesIndex) {
console.log('loadSeries')
this.loopLoadStatus = -1
this.$set(this.studyList[studyIndex].SeriesList[seriesIndex], 'isLoading', true)
const priority = parseInt(new Date().getTime())
series.imageIds.map(image => {
if (series.imageloadedArr.indexOf(image) === -1) {
this.imageList.push({ imageId: image, seriesId: series.seriesId, studyIndex: studyIndex, seriesIndex: seriesIndex, visitTaskId: series.visitTaskId, priority })
}
})
if (this.imageList.length > 0) {
this.loopLoadStatus = 0
this.loopLoad()
}
},
// 暂停下载
stopLoadSeries(series, studyIndex, seriesIndex) {
console.log('stopLoadSeries')
requestPoolManager.removeTask(series.seriesId)
this.$set(this.studyList[studyIndex].SeriesList[seriesIndex], 'isLoading', false)
},
async imageLoaded(image, seriesUid) {
await store.dispatch('reading/updateStudyList', { visitTaskId: image.visitTaskId, imageId: image.imageId, seriesUid })
},
// instance下载成功回调
async cornerstoneImageLoaded(e) {
await store.dispatch('reading/updateStudyList', { visitTaskId: this.visitTaskId, imageId: e.detail.image.imageId, seriesUid: e.detail.image.data.string('x0020000e') })
var idx = this.visitTaskList.findIndex(i => i.VisitTaskId === this.visitTaskId)
this.studyList = this.visitTaskList[idx].StudyList
const uri = e.detail.image.sharedCacheKey
const index = this.cachedImages.findIndex(item => item.uri === uri)
if (index === -1) {
this.cachedImages.push({ uri: uri, timestamp: new Date().getTime() })
} else {
this.cachedImages[index].timestamp = new Date().getTime()
}
var imageId = e.detail.image.imageId
var seriesUid = e.detail.image.data.string('x0020000e')
var studyIndex = -1
var seriesIndex = -1
for (let i = 0; i < this.studyList.length; ++i) {
for (let j = 0; j < this.studyList[i].SeriesList.length; ++j) {
if (this.studyList[i].SeriesList[j].seriesUid === seriesUid) {
studyIndex = i
seriesIndex = j
break
}
}
if (studyIndex > 0) break
}
if (seriesIndex < 0) return
const imageIdIndex = this.studyList[studyIndex].SeriesList[seriesIndex].imageIds.indexOf(imageId)
if (imageIdIndex < 0) return
if (this.studyList[studyIndex].SeriesList[seriesIndex].imageloadedArr.indexOf(imageId) < 0) {
if (this.studyList[studyIndex].SeriesList[seriesIndex].prefetchInstanceCount >= this.studyList[studyIndex].SeriesList[seriesIndex].instanceCount) {
// 设置当前序列状态为已下载完成
this.studyList[studyIndex].SeriesList[seriesIndex].loadStatus = true
}
}
}
}
}
</script>
<style lang="scss" scoped>
.study-wrapper{
>>>.el-progress-bar__inner{
transition: width 0s ease;
}
width:100%;
height: 100%;
overflow-y: auto;
.dicom-desc{
font-weight: bold;
font-size: 13px;
text-align: left;
color: #d0d0d0;
padding: 2px;
}
.ps {
overflow-anchor: none;
touch-action: auto;
}
.series-active {
background-color: #607d8b!important;
border: 1px solid #607d8b!important;
}
>>>.el-progress__text{
color: #ccc;
font-size: 12px;
}
.series{
width: 100%;
display: flex;
flex-direction: column;
justify-content: flex-start;
.series-wrapper {
display: flex;
flex-direction: row;
justify-content: space-between;
align-items: center;
width: 100%;
height: 95px;
// padding: 1px 2px 1px 8px;
// margin: 10px 0px;
border-radius: 2px;
border: 1px solid #404040;
background-color: #3a3a3a;
.el-progress__text{
display: none;
}
.el-progress-bar{
padding-right:0px;
}
.image-preview {
height: 60px;
width: 60px;
border: 2px solid #252525;
cursor: pointer;
}
.image-desc {
vertical-align: top;
p{
width: 100px;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
font-size: 12px;
color: #ddd;
margin: 0px;
line-height: 1.5;
div{
width: 100px;
}
}
}
.flex-div{
display: flex;
flex-direction: row;
justify-content: space-between;
padding: 1px;
width: 100px;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
font-size: 12px;
color: #ddd;
padding: 1px;
margin: 0px;
}
}
}
.el-collapse{
border: none;
>>>.el-collapse-item{
background-color: #000!important;
color: #ddd;
}
>>>.el-collapse-item__content{
padding-bottom:10px;
background-color: #000!important;
}
>>>.el-collapse-item__header{
background-color: #000!important;
color: #ddd;
border-bottom-color:#5a5a5a;
padding-left: 5px;
height: 40px;
line-height: 20px;
}
}
}
</style>

View File

@ -0,0 +1,785 @@
/* eslint-disable */
<template>
<BaseContainer>
<template slot="search-container">
<el-form :inline="true">
<el-form-item :label="$t('trials:consistencyCheck:table:siteId')">
<el-select v-model="searchData.TrialSiteCode" clearable filterable style="width:120px;">
<el-option
v-for="(item,index) of siteOptions"
:key="index"
:label="item"
:value="item"
/>
</el-select>
</el-form-item>
<!-- 受试者编号 -->
<el-form-item :label="$t('trials:consistencyCheck:table:subjectId')">
<el-input v-model="searchData.SubjectCode" style="width:100px;" />
</el-form-item>
<el-form-item style="margin-bottom:10px" label="访视/阅片期名称">
<el-input
v-model="searchData.TaskName"
style="width:100px;"
clearable
/>
</el-form-item>
<el-form-item style="margin-bottom:10px" label="角色">
<el-select v-model="searchData.ArmEnum" clearable style="width:120px;">
<el-option v-for="item of $d.ArmEnum" :key="'ArmEnum' + item.label" :value="item.value" :label="item.label" />
</el-select>
</el-form-item>
<el-form-item style="margin-bottom:10px" label="阅片人">
<el-select v-model="searchData.DoctorUserId" clearable style="width:120px;">
<el-option v-for="item of DoctorUserList" :key="'DoctorUserId' + item.DoctorUserId" :value="item.DoctorUserId" :label="`${item.UserName}(${item.FullName})`" />
</el-select>
</el-form-item>
<el-form-item style="margin-bottom:10px" label="是否加急">
<el-select v-model="searchData.IsUrgent" clearable style="width:120px;">
<el-option v-for="item of $d.YesOrNo" :key="'IsUrgent' + item.label" :value="item.value" :label="item.label" />
</el-select>
</el-form-item>
<el-form-item style="margin-bottom:10px" label="任务状态">
<el-select v-model="searchData.TaskState" clearable style="width:120px;">
<el-option v-for="item of $d.TaskState" :key="'TaskState' + item.label" :value="item.value" :label="item.label" />
</el-select>
</el-form-item>
<el-form-item style="margin-bottom:10px" label="任务类型">
<el-select v-model="searchData.ReadingCategory" clearable style="width:120px;">
<el-option v-for="item of $d.ReadingCategory" :key="'ReadingCategory' + item.label" :value="item.value" :label="item.label" />
</el-select>
</el-form-item>
<el-form-item style="margin-bottom:10px" label="一致性分析类别">
<el-select v-model="searchData.IsSelfAnalysis" clearable style="width:120px;">
<el-option v-for="item of $d.IsSelfAnalysis" :key="'IsSelfAnalysis' + item.label" :value="item.value" :label="item.label" />
</el-select>
</el-form-item>
<el-form-item style="margin-bottom:10px" label="分配状态">
<el-select v-model="searchData.TaskAllocationState" clearable style="width:120px;">
<el-option v-for="item of $d.TaskAllocationState" :key="'TaskAllocationState' + item.label" :value="item.value" :label="item.label" />
</el-select>
</el-form-item>
<el-form-item style="margin-bottom:10px" label="分配时间">
<el-date-picker
v-model="timeList"
value-format="yyyy-MM-dd HH:mm:ss"
type="datetimerange"
range-separator="至"
start-placeholder="开始日期"
end-placeholder="结束日期"
@change="changeTimeList"
/>
</el-form-item>
<el-form-item style="margin-bottom:10px">
<el-button type="primary" icon="el-icon-search" @click="handleSearch">
{{ $t('common:button:search') }}
</el-button>
<el-button type="primary" icon="el-icon-refresh-left" @click="handleReset">
{{ $t('common:button:reset') }}
</el-button>
<el-button type="primary" @click="openTaskConsistentRule(true)">
自身一致性分析
</el-button>
<el-button type="primary" @click="openTaskConsistentRule(false)">
组间一致性分析
</el-button>
</el-form-item>
</el-form>
</template>
<template slot="main-container">
<el-table
v-adaptive="{bottomOffset:60}"
v-loading="loading"
:data="list"
stripe
height="100"
@sort-change="handleSortChange"
>
<el-table-column
prop="IsUrgent"
:label="$t('trials:consistencyCheck:table:isUrgent')"
show-overflow-tooltip
min-width="100"
sortable="custom"
>
<template slot-scope="scope">
<el-tag v-if="scope.row.IsUrgent" type="danger">{{ $fd('YesOrNo', scope.row.IsUrgent) }}</el-tag>
<el-tag v-else type="primary">{{ $fd('YesOrNo', scope.row.IsUrgent) }}</el-tag>
</template>
</el-table-column>
<el-table-column
prop="TaskCode"
label="任务编号"
min-width="100"
sortable="custom"
show-overflow-tooltip
/>
<el-table-column
prop="TrialSiteCode"
label="中心编号"
min-width="100"
sortable="custom"
show-overflow-tooltip
/>
<el-table-column
prop="SubjectCode"
label="受试者编号"
min-width="120"
sortable="custom"
show-overflow-tooltip
/>
<el-table-column
prop="VisitTaskNum"
label="访视/阅片期名称"
min-width="160"
sortable="custom"
show-overflow-tooltip
>
<template slot-scope="scope">
{{scope.row.TaskName}}
</template>
</el-table-column>
<el-table-column
prop="TaskBlindName"
label="访视盲态名称"
min-width="140"
sortable="custom"
show-overflow-tooltip
/>
<el-table-column
prop="ReadingCategory"
label="任务类型"
min-width="100"
sortable="custom"
show-overflow-tooltip
>
<template slot-scope="scope">
<el-tag v-if="scope.row.ReadingCategory === 1" type="primary">{{ $fd('ReadingCategory', scope.row.ReadingCategory) }}</el-tag>
<el-tag v-if="scope.row.ReadingCategory === 2" type="info">{{ $fd('ReadingCategory', scope.row.ReadingCategory) }}</el-tag>
<el-tag v-if="scope.row.ReadingCategory === 4" type="danger">{{ $fd('ReadingCategory', scope.row.ReadingCategory) }}</el-tag>
<el-tag v-if="scope.row.ReadingCategory === 5" type="warning">{{ $fd('ReadingCategory', scope.row.ReadingCategory) }}</el-tag>
</template>
</el-table-column>
<el-table-column
prop="IsSelfAnalysis"
label="一致性分析类别"
min-width="160"
sortable="custom"
show-overflow-tooltip
>
<template slot-scope="scope">
<el-tag v-if="scope.row.IsSelfAnalysis" type="primary">{{ $fd('IsSelfAnalysis', scope.row.IsSelfAnalysis) }}</el-tag>
<el-tag v-else type="warning">{{ $fd('IsSelfAnalysis', scope.row.IsSelfAnalysis) }}</el-tag>
</template>
</el-table-column>
<el-table-column
prop="ArmEnum"
label="角色"
min-width="80"
sortable="custom"
show-overflow-tooltip
>
<template slot-scope="scope">
<el-tag v-if="scope.row.ArmEnum === 1" type="primary">{{$fd('ArmEnum', scope.row.ArmEnum)}}</el-tag>
<el-tag v-if="scope.row.ArmEnum === 2" type="success">{{$fd('ArmEnum', scope.row.ArmEnum)}}</el-tag>
<el-tag v-if="scope.row.ArmEnum === 3" type="info">{{$fd('ArmEnum', scope.row.ArmEnum)}}</el-tag>
<el-tag v-if="scope.row.ArmEnum === 4" type="danger">{{$fd('ArmEnum', scope.row.ArmEnum)}}</el-tag>
<el-tag v-if="scope.row.ArmEnum === 5" type="warning">{{$fd('ArmEnum', scope.row.ArmEnum)}}</el-tag>
<el-tag v-if="scope.row.ArmEnum === 8" style="border-color: #4c2791;color:#4c2791;">{{$fd('ArmEnum', scope.row.ArmEnum)}}</el-tag>
</template>
</el-table-column>
<el-table-column
prop="UserName"
label="阅片人"
min-width="100"
sortable="custom"
show-overflow-tooltip
>
<template slot-scope="scope">
{{scope.row.UserName}}({{scope.row.FullName}})
</template>
</el-table-column>
<el-table-column
prop="TaskState"
label="任务状态"
min-width="100"
sortable="custom"
show-overflow-tooltip
>
<template slot-scope="scope">
<el-tag v-if="scope.row.TaskState === 0" type="primary">{{ $fd('TaskState', scope.row.TaskState) }}</el-tag>
<el-tag v-if="scope.row.TaskState === 1" type="info">{{ $fd('TaskState', scope.row.TaskState) }}</el-tag>
<el-tag v-if="scope.row.TaskState === 3" type="danger">{{ $fd('TaskState', scope.row.TaskState) }}</el-tag>
<el-tag v-if="scope.row.TaskState === 4" type="warning">{{ $fd('TaskState', scope.row.TaskState) }}</el-tag>
</template>
</el-table-column>
<el-table-column
prop="AllocateTime"
label="分配时间"
min-width="140"
sortable="custom"
show-overflow-tooltip
>
</el-table-column>
<el-table-column
prop="SuggesteFinishedTime"
label="建议完成时间"
min-width="140"
sortable="custom"
show-overflow-tooltip
/>
<el-table-column
prop="ReadingTaskState"
label="阅片状态"
min-width="120"
sortable="custom"
show-overflow-tooltip
>
<template slot-scope="scope">
<el-tag v-if="scope.row.ReadingTaskState === 2" type="primary">{{ $fd('ReadingTaskState', scope.row.ReadingTaskState) }}</el-tag>
<el-tag v-else type="danger">{{ $fd('ReadingTaskState', scope.row.ReadingTaskState) }}</el-tag>
</template>
</el-table-column>
<el-table-column
prop="SignTime"
label="阅片完成时间"
min-width="140"
sortable="custom"
show-overflow-tooltip
/>
<el-table-column
prop="ReReadingApplyState"
label="申请状态"
min-width="140"
sortable="custom"
show-overflow-tooltip
>
<template slot-scope="scope">
<el-tag v-if="scope.row.ReReadingApplyState === 1" type="danger">{{ $fd('ReReadingApplyState', scope.row.ReReadingApplyState) }}</el-tag>
<el-tag v-if="scope.row.ReReadingApplyState === 2" type="primary">{{ $fd('ReReadingApplyState', scope.row.ReReadingApplyState) }}</el-tag>
<el-tag v-if="scope.row.ReReadingApplyState === 3" type="warning">{{ $fd('ReReadingApplyState', scope.row.ReReadingApplyState) }}</el-tag>
</template>
</el-table-column>
<el-table-column
prop="IsReReadingOrBackInfluenceAnalysis"
label="是否受到退回影响"
min-width="160"
sortable="custom"
show-overflow-tooltip>
<template slot-scope="scope">
<el-tag v-if="!scope.row.IsReReadingOrBackInfluenceAnalysis" type="danger">{{ $fd('YesOrNo', scope.row.IsReReadingOrBackInfluenceAnalysis) }}</el-tag>
<el-tag v-if="scope.row.IsReReadingOrBackInfluenceAnalysis" type="primary">{{ $fd('YesOrNo', scope.row.IsReReadingOrBackInfluenceAnalysis) }}</el-tag>
</template>
</el-table-column>
<el-table-column
fixed="right"
label="操作"
width="110"
>
<template slot-scope="scope">
<el-button
:disabled="scope.row.ReadingTaskState !== 2"
icon="el-icon-view"
circle
title="查看阅片结果"
@click="lookReadingResults(scope.row)"
/>
<el-button
:disabled="!scope.row.IsReReadingOrBackInfluenceAnalysis || scope.row.TaskState === 1"
icon="el-icon-close"
circle
title="设置为失效"
@click="setAnalysisTaskInvalid(scope.row)"
/>
<!-- <el-button-->
<!-- :disabled="scope.row.TaskState !== 0 || scope.row.ReadingTaskState !== 2 || scope.row.ReReadingApplyState === 2 || scope.row.ReReadingApplyState === 1"-->
<!-- icon="el-icon-collection"-->
<!-- circle-->
<!-- title="申请重阅"-->
<!-- @click="openApplyReReading(scope.row)"-->
<!-- />-->
<!-- <el-button-->
<!-- :disabled="!scope.row.ReReadingApplyState"-->
<!-- icon="el-icon-toilet-paper"-->
<!-- circle-->
<!-- title="退回重阅历史"-->
<!-- @click="reReadingHistory(scope.row, 1)"-->
<!-- />-->
</template>
</el-table-column>
</el-table>
<!-- 分页组件 -->
<pagination class="page" :total="total" :page.sync="searchData.PageIndex" :limit.sync="searchData.PageSize" @pagination="getList" />
<el-dialog
v-if="ReaderRulesVisible"
:title="title"
:visible.sync="ReaderRulesVisible"
width="1200px"
append-to-body
:close-on-click-modal="false"
custom-class="base-dialog-wrapper"
>
<el-button size="small" type="primary" style="position: absolute;right: 20px;top: 60px;z-index: 100;" :loading="btnLoading" @click="addReaderRulesForm()">
配置{{ title }}规则
</el-button>
<div class="base-dialog-body" v-if="IsSelfAnalysis" style="margin-top: 32px;">
<el-table
v-loading="loading"
:data="TaskConsistentRuleList"
stripe
>
<el-table-column
prop="TaskCode"
label="阅片人"
min-width="140"
show-overflow-tooltip
>
<template slot-scope="scope" v-if="scope.row.AnalysisDoctorUser">
{{scope.row.AnalysisDoctorUser.UserName}}({{scope.row.AnalysisDoctorUser.FullName}})
</template>
</el-table-column>
<el-table-column
prop="PlanVisitCount"
label="访视数"
min-width="90"
show-overflow-tooltip
>
</el-table-column>
<el-table-column
prop="IntervalWeeks"
label="间隔周期"
min-width="100"
show-overflow-tooltip
v-if="IsSelfAnalysis"
>
</el-table-column>
<el-table-column
prop="IsHaveReadingPeriod"
label="所选访视是否有阅片期"
min-width="180"
show-overflow-tooltip
>
<template slot-scope="scope">
<el-tag v-if="scope.row.IsHaveReadingPeriod" size="success">{{$fd('YesOrNo', scope.row.IsHaveReadingPeriod)}}</el-tag>
<el-tag v-else size="danger">{{$fd('YesOrNo', scope.row.IsHaveReadingPeriod)}}</el-tag>
</template>
</el-table-column>
<el-table-column
prop="IsGenerateGlobalTask"
label="是否生成全局阅片任务"
min-width="180"
show-overflow-tooltip
>
<template slot-scope="scope">
<el-tag v-if="scope.row.IsGenerateGlobalTask" size="success">{{$fd('YesOrNo', scope.row.IsGenerateGlobalTask)}}</el-tag>
<el-tag v-else size="danger">{{$fd('YesOrNo', scope.row.IsGenerateGlobalTask)}}</el-tag>
</template>
</el-table-column>
<el-table-column
prop="PlanSubjectCount"
label="计划受试者数"
min-width="120"
show-overflow-tooltip
>
</el-table-column>
<el-table-column
prop="GeneratedSubjectCount"
label="已生成受试者数"
min-width="130"
show-overflow-tooltip
>
</el-table-column>
<el-table-column
fixed="right"
label="操作"
width="110"
>
<template slot-scope="scope">
<el-button
circle
icon="el-icon-s-check"
title="选择"
@click="openDoctorConsistentRuleSubjectTable(scope.row)"
/>
</template>
</el-table-column>
</el-table>
</div>
<div slot="footer" class="dialog-footer">
<!-- 取消 -->
<el-button :disabled="btnLoading" size="small" type="primary" @click="ReaderRulesVisible = false">
{{ $t('common:button:cancel') }}
</el-button>
<!-- 保存 -->
</div>
</el-dialog>
<el-dialog
v-if="ReaderRulesVisible2"
:title="title"
:visible.sync="ReaderRulesVisible2"
width="1200px"
append-to-body
:close-on-click-modal="false"
custom-class="base-dialog-wrapper"
>
<el-button size="small" type="primary" style="position: absolute;right: 20px;top: 60px;z-index: 100;" :loading="btnLoading" @click="addReaderRulesForm('g')">
配置{{ title }}规则
</el-button>
<GroupConsistentRuleSubjectTable ref="GroupConsistentRuleSubjectTable" style="margin-top: 40px" :IsSelfAnalysis="IsSelfAnalysis" :changeNum="changeNum" @close="() => {ReaderRulesVisible2 = false; ; rowData = {}}" @getList="getList()" />
</el-dialog>
<el-dialog
v-if="ReaderRulesFormVisible"
:title=" `配置${title}规则`"
:visible.sync="ReaderRulesFormVisible"
width="480px"
:close-on-click-modal="false"
>
<ReaderRulesForm :isDisable="isDisable" :data="rowData" :select-list="SelectList" :doctor-user-list="DoctorUserList" :is-self-analysis="IsSelfAnalysis" @close="() => {ReaderRulesFormVisible = false; rowData = {}}" @getList="openTaskConsistentRule(IsSelfAnalysis)" />
</el-dialog>
<el-dialog
v-if="DoctorConsistentRuleSubjectTableVisible"
title="选择受试者"
:visible.sync="DoctorConsistentRuleSubjectTableVisible"
width="1000px"
:close-on-click-modal="false"
>
<DoctorConsistentRuleSubjectTable :data="rowData" @close="() => {DoctorConsistentRuleSubjectTableVisible = false; ; rowData = {}}" @getList="openTaskConsistentRule(IsSelfAnalysis)" />
</el-dialog>
<el-dialog
v-if="ApplyforReasonVisible"
title="申请重阅"
:visible.sync="ApplyforReasonVisible"
width="600px"
append-to-body
:close-on-click-modal="false"
custom-class="base-dialog-wrapper"
>
<div style="padding:10px;border: 1px solid #e0e0e0;max-height:650px;overflow-y: auto;">
<el-form
ref="reasonForm"
:rules="rules"
:model="ApplyforReasonForm"
class="demo-ruleForm"
size="small"
label-width="120px"
>
<el-form-item
label="申请原因"
prop="Type"
:rules="[
{ required: true, message: $t('common:ruleMessage:select')},
]"
>
<el-radio-group v-model="ApplyforReasonForm.Type">
<!-- 问题已解决 -->
<el-radio :label="1">图像重传</el-radio>
<!-- 问题无法解决强制关闭质疑 -->
<el-radio :label="2">其他</el-radio>
</el-radio-group>
</el-form-item>
<!-- 原因 -->
<el-form-item v-if="ApplyforReasonForm.Type === 2" label="备注" prop="RequestReReadingReason">
<el-input
v-model="ApplyforReasonForm.RequestReReadingReason"
type="textarea"
:autosize="{ minRows: 2, maxRows: 4}"
:placeholder="$t('common:ruleMessage:specify')"
maxlength="500"
show-word-limit
/>
</el-form-item>
</el-form>
</div>
<div slot="footer" class="dialog-footer">
<!-- 取消 -->
<el-button :disabled="btnLoading" size="small" type="primary" @click="ApplyforReasonVisible = false;ApplyforReasonForm = { Type: null, RequestReReadingReason: null }">
{{ $t('common:button:cancel') }}
</el-button>
<!-- 保存 -->
<el-button :loading="btnLoading" size="small" type="primary" @click="applyReReading">
{{ $t('common:button:save') }}
</el-button>
</div>
</el-dialog>
</template>
</BaseContainer>
</template>
<script>
import { getSelfConsistentDoctorStatList, setAnalysisTaskInvalid, getTrialSiteCodeSelect, getDoctorUserSelectList, getAnalysisTaskList, applyReReading, getDoctorConsistentRuleSubjectList, confirmGenerateConsistentTask, getTaskConsistentRuleList, addOrUpdateTaskConsistentRule, deleteTaskConsistentRule } from '@/api/trials/reading'
import ReaderRulesForm from './components/ReaderRulesForm'
import DoctorConsistentRuleSubjectTable from './components/DoctorConsistentRuleSubjectTable'
import GroupConsistentRuleSubjectTable from './components/GroupConsistentRuleSubjectTable'
import BaseContainer from '@/components/BaseContainer'
import Pagination from '@/components/Pagination'
import { getToken } from '@/utils/auth'
const searchDataDefault = () => {
return {
PageIndex: 1,
PageSize: 20,
Asc: true,
SortField: '',
TrialId: null,
SiteId: null,
SubjectId: null,
SubjectCode: null,
TrialSiteCode: null,
IsUrgent: null,
TaskName: null,
TaskState: null,
DoctorUserId: null,
ReadingCategory: null,
TaskAllocationState: null,
BeginAllocateDate: null,
EndAllocateDate: null
}
}
export default {
name: 'TrialsNotice',
components: { BaseContainer, Pagination, ReaderRulesForm, DoctorConsistentRuleSubjectTable, GroupConsistentRuleSubjectTable },
data() {
return {
searchData: searchDataDefault(),
maxLength: 0,
list: [],
siteOptions: [],
total: 0,
loading: false,
ReaderRulesVisible: false,
DoctorConsistentRuleSubjectTableVisible: false,
trialId: this.$route.query.trialId,
ReaderRulesVisible2: false,
currentData: {},
param: {},
DoctorUserList: [],
btnLoading: false,
rowData: {},
OtherInfo: null,
title: null,
SelectList: [],
TaskConsistentRuleList: [],
isDisable: false,
ReaderRulesFormVisible: false,
IsSelfAnalysis: true,
timeList: [],
ApplyforReasonVisible: false,
ApplyforReasonForm: {
Type: null,
RequestReReadingReason: null
},
rules: {
Type: [{ required: true, message: this.$t('common:ruleMessage:specify'), trigger: 'blur' }],
RequestReReadingReason: [{ required: true, message: this.$t('common:ruleMessage:specify'), trigger: 'blur' }]
},
changeNum: 0
}
},
mounted() {
this.getList()
this.getSite()
this.getDoctorUserSelectList()
},
methods: {
setAnalysisTaskInvalid(row) {
this.$confirm('确定要将该一致性任务设置成失效吗?').then(() => {
this.loading = true
setAnalysisTaskInvalid([
row.Id
]).then(res => {
this.$message.success('设置成功')
this.getList()
this.loading = false
}).catch(() => {
this.loading = false
})
})
},
changeTimeList() {
if (this.timeList) {
this.searchData.BeginAllocateDate = this.timeList[0]
this.searchData.EndAllocateDate = this.timeList[1]
} else {
this.searchData.BeginAllocateDate = null
this.searchData.EndAllocateDate = null
}
},
// 重阅历史
reReadingHistory(row) {
this.$router.push({ path: `/trials/trials-panel/reading/reReadingTracking?trialId=${this.$route.query.trialId}&trialCode=${this.$route.query.trialCode}&researchProgramNo=${this.$route.query.researchProgramNo}&SiteId=${row.SiteId}&SubjectCode=${row.SubjectCode}&TaskName=${row.TaskName}&DoctorUserId=${row.DoctorUserId}` })
},
// 申请重阅
applyReReading() {
this.$refs.reasonForm.validate((valid) => {
if (!valid) return
this.loading = true
this.btnLoading = true
var params = {
TaskIdList: [
this.rowData.Id
],
TrialId: this.$route.query.trialId,
RequestReReadingType: 2,
RequestReReadingReason: this.ApplyforReasonForm.Type === 2 ? this.ApplyforReasonForm.RequestReReadingReason : '图像重传'
}
applyReReading(params).then(res => {
this.loading = false
this.btnLoading = false
this.$message.success('申请重阅成功')
this.getList()
this.ApplyforReasonVisible = false
}).catch(() => {
this.loading = false
this.btnLoading = false
})
})
},
lookReadingResults(row) {
var token = getToken()
const routeData = this.$router.resolve({
path: `/noneDicomReading?subjectId=${row.SubjectId}&trialId=${row.TrialId}&visitTaskId=${row.Id}&TokenKey=${token}`
})
window.open(routeData.href, '_blank')
},
openApplyReReading(row) {
this.rowData = { ...row }
this.ApplyforReasonVisible = true
this.ApplyforReasonForm = {
Type: null,
Remake: null
}
},
editReaderRulesForm(row) {
this.rowData = {...row}
this.ReaderRulesFormVisible = true
},
openDoctorConsistentRuleSubjectTable(row) {
this.rowData = {...row}
this.DoctorConsistentRuleSubjectTableVisible = true
},
addReaderRulesForm(isg) {
this.isDisable = false
if (isg) {
if (this.$refs['GroupConsistentRuleSubjectTable'].list) {
this.$refs['GroupConsistentRuleSubjectTable'].list.forEach(v => {
if (v.IsHaveGeneratedTask) {
this.isDisable = true
}
})
}
} else {
if (this.TaskConsistentRuleList.length > 0) {
this.rowData = {...this.TaskConsistentRuleList[0]}
this.TaskConsistentRuleList.forEach(v => {
if (v.GeneratedSubjectCount) {
this.isDisable = true
}
})
} else {
this.rowData = {}
}
}
this.ReaderRulesFormVisible = true
},
openTaskConsistentRule(IsSelfAnalysis) {
this.loading = true
if (IsSelfAnalysis) {
this.title = '自身一致性分析'
getSelfConsistentDoctorStatList({TrialId: this.trialId}).then(res => {
this.ReaderRulesFormVisible = false
this.DoctorConsistentRuleSubjectTableVisible = false
this.TaskConsistentRuleList = res.Result
this.IsSelfAnalysis = IsSelfAnalysis
this.ReaderRulesVisible = true
this.getList()
this.loading = false
}).catch(() => {
this.loading = false
})
} else {
this.changeNum ++
this.ReaderRulesFormVisible = false
this.title = '组间一致性分析'
this.IsSelfAnalysis = IsSelfAnalysis
this.ReaderRulesVisible2 = true
this.loading = false
}
// getTaskConsistentRuleList({
// IsSelfAnalysis: IsSelfAnalysis,
// TrialId: this.trialId
// }).then(res => {
// this.ReaderRulesFormVisible = false
// this.DoctorConsistentRuleSubjectTableVisible = false
// this.TaskConsistentRuleList = res.Result
// this.IsSelfAnalysis = IsSelfAnalysis
// this.ReaderRulesVisible = true
// })
},
getDoctorUserSelectList() {
getDoctorUserSelectList(this.$route.query.trialId).then(res => {
this.DoctorUserList = res.Result
if (this.DoctorUserList.length > 0) {
this.ReadingType = this.DoctorUserList[0].ReadingType
} else {
this.$alert('请先去选择项目阅片人')
}
})
},
getList() {
this.searchData.TrialId = this.$route.query.trialId
this.loading = true
getAnalysisTaskList(this.searchData).then(res => {
this.loading = false
this.OtherInfo = res.OtherInfo
this.list = res.Result.CurrentPageData
this.total = res.Result.TotalCount
this.ReaderRulesVisible2 = false
this.AssignMedicalReviewTaskVisible = false
}).catch(() => { this.loading = false })
},
handleSearch() {
this.searchData.PageIndex = 1
this.getList()
},
handleReset() {
this.searchData = searchDataDefault()
this.getList()
},
getSite() {
getTrialSiteCodeSelect(this.trialId).then(res => {
this.siteOptions = res.Result
})
},
// 排序
handleSortChange(column) {
if (column.order === 'ascending') {
this.searchData.Asc = true
} else {
this.searchData.Asc = false
}
this.searchData.SortField = column.prop
this.searchData.PageIndex = 1
this.getList()
}
}
}
</script>
<style lang="scss" scoped>
>>>.hidden-row{
display: none;
}
>>>.el-dialog__body {
padding: 15px 20px;
}
>>>.el-tag--danger.el-tag--dark {
// background-color: #f56c6c!important;
border-color: none!important;
// color: #fff!important;
}
>>>#TaskAllocationRuleList thead .el-checkbox__inner{
display: none;
}
>>>.el-descriptions-item__label.has-colon:after{
display: none;
}
</style>

View File

@ -0,0 +1,350 @@
<template>
<BaseContainer v-loading="listLoading">
<template slot="search-container">
<span style="margin-left:auto;">
<!-- Add -->
<el-button
v-hasPermi="['trials:trials-panel:setting:visit-plan:add']"
type="primary"
icon="el-icon-plus"
size="small"
@click="handleAdd"
>
{{ $t('common:button:add') }}
</el-button>
<!-- 盲态标识 -->
<el-button
v-hasPermi="['trials:trials-panel:setting:visit-plan:add']"
type="primary"
icon="el-icon-setting"
size="small"
@click="handleConfig"
>
{{ $t('trials:visitPlan:button:blindFlag') }}
</el-button>
<!-- Confirm -->
<el-button
v-hasPermi="['trials:trials-panel:setting:visit-plan:confirm']"
type="primary"
icon="el-icon-check"
:disabled="!isConfirm"
:loading="confirmLoading"
size="small"
@click="handleConfirm(true)"
>
{{ $t('trials:visitPlan:button:confirm') }}
</el-button>
<!-- 调整记录 -->
<el-button
v-hasPermi="['trials:trials-panel:setting:visit-plan:adjust-record']"
type="primary"
size="small"
@click="adjustVisible = true"
>
{{ $t('trials:visitPlan:button:adjustRecord') }}
</el-button>
</span>
</template>
<template slot="main-container">
<el-table
v-adaptive="{bottomOffset:60}"
:data="list"
height="100"
stripe
>
<!-- Visit Name -->
<el-table-column
prop="VisitName"
:label="$t('trials:visitPlan:table:visitName')"
show-overflow-tooltip
min-width="60"
/>
<!-- 是否基线 -->
<el-table-column
prop="IsBaseLine"
:label="$t('trials:visitPlan:table:isBaseLine')"
show-overflow-tooltip
min-width="60"
>
<template slot-scope="scope">
{{ $fd('YesOrNo', scope.row.IsBaseLine) }}
</template>
</el-table-column>
<!-- 盲态任务标识 -->
<!-- <el-table-column
prop="BlindName"
:label="$t('trials:visitPlan:table:blindName')"
show-overflow-tooltip
min-width="80"
/> -->
<!-- Visit Num -->
<el-table-column
prop="VisitNum"
:label="$t('trials:visitPlan:table:viistNum')"
show-overflow-tooltip
min-width="60"
/>
<!-- 访视间隔 -->
<el-table-column
prop="VisitDay"
:label="$t('trials:visitPlan:table:viistDay')"
show-overflow-tooltip
min-width="60"
/>
<!-- 窗口 -->
<el-table-column
v-if="isHaveFirstGiveMedicineDate"
prop="VisitWindowLeft"
:label="$t('trials:visitPlan:table:window')"
show-overflow-tooltip
min-width="60"
>
<template slot-scope="scope">
<span v-show="scope.row.VisitWindowLeft!==0 || scope.row.VisitWindowRight!==0">
{{ `${scope.row.VisitWindowLeft}${$t('trials:visitPlan:table:day')} ~ ${scope.row.VisitWindowRight}${$t('trials:visitPlan:table:day')}` }}
</span>
</template>
</el-table-column>
<!-- 是否确认 -->
<el-table-column
prop="IsConfirmed"
:label="$t('trials:visitPlan:table:isConfirm')"
show-overflow-tooltip
min-width="80"
>
<template slot-scope="scope">
<el-tag v-if="scope.row.IsConfirmed" type="primary">{{ $fd('YesOrNo', scope.row.IsConfirmed) }}</el-tag>
<el-tag v-else type="danger">{{ $fd('YesOrNo', scope.row.IsConfirmed) }}</el-tag>
</template>
</el-table-column>
<!-- 是否废除 -->
<el-table-column
prop="IsDeleted"
:label="$t('trials:visitPlan:table:isDeleted')"
show-overflow-tooltip
min-width="80"
>
<template slot-scope="scope">
<el-tag v-if="scope.row.IsDeleted">{{ $fd('YesOrNo', scope.row.IsDeleted) }}</el-tag>
<el-tag v-else type="danger">{{ $fd('YesOrNo', scope.row.IsDeleted) }}</el-tag>
</template>
</el-table-column>
<!-- Description -->
<el-table-column
prop="Description"
:label="$t('trials:visitPlan:table:description')"
show-overflow-tooltip
min-width="80"
/>
<!-- Time Created -->
<el-table-column
prop="CreateTime"
:label="$t('trials:visitPlan:table:createTime')"
show-overflow-tooltip
min-width="80"
/>
<el-table-column
v-if="hasPermi(['trials:trials-panel:setting:visit-plan:edit'])"
:label="$t('common:action:action')"
min-width="80"
>
<template slot-scope="scope">
<!-- Edit -->
<el-button
v-hasPermi="['trials:trials-panel:setting:visit-plan:edit']"
icon="el-icon-edit-outline"
circle
:title="$t('common:button:edit')"
@click="handleEdit(scope.row)"
/>
</template>
</el-table-column>
</el-table>
</template>
<base-model v-if="visit_model.visible" :config="visit_model">
<template slot="dialog-body">
<VisitPlanForm :row="row" :is-have-first-give-medicine-date="isHaveFirstGiveMedicineDate" @closeDialog="closeDialog" @getList="getList" />
</template>
</base-model>
<!-- 调整记录 -->
<el-dialog
v-if="adjustVisible"
:visible.sync="adjustVisible"
:close-on-click-modal="false"
width="800px"
:title="$t('trials:visitPlan:dialogTitle:record')"
>
<VisitPlanAdjust />
</el-dialog>
<!-- 配置 -->
<el-dialog
v-if="config_model.visible"
:visible.sync="config_model.visible"
:close-on-click-modal="false"
width="500px"
:title="config_model.title"
custom-class="base-dialog-wrapper"
>
<el-form
ref="blindNameForm"
v-loading="formLoading"
:model="form"
:rules="rules"
size="small"
label-width="140px"
>
<div class="base-dialog-body">
<!-- 基线盲态标识 -->
<el-form-item :label="$t('trials:visitPlan:button:bsBlindFlag')" prop="BlindBaseLineName">
<el-input
v-model="form.BlindBaseLineName"
:disabled="isHaveGeneratedTask"
/>
</el-form-item>
<!-- 随访盲态标识前缀 -->
<el-form-item :label="$t('trials:visitPlan:button:visitBlindFlag')" prop="BlindFollowUpPrefix">
<el-input
v-model="form.BlindFollowUpPrefix"
:disabled="isHaveGeneratedTask"
/>
</el-form-item>
</div>
<div v-if="!isHaveGeneratedTask" class="base-dialog-footer" style="text-align:right;margin-top:10px;">
<el-form-item>
<el-button v-hasPermi="['trials:trials-panel:setting:visit-plan:add']" type="primary" @click="handleSaveBlindName">
{{ $t('common:button:save') }}
</el-button>
</el-form-item>
</div>
</el-form>
</el-dialog>
</BaseContainer>
</template>
<script>
import { getVisitStageList, confirmTrialVisitPlan, updateVisitBlindName } from '@/api/trials'
import BaseContainer from '@/components/BaseContainer'
import BaseModel from '@/components/BaseModel'
import VisitPlanForm from './components/visitPlanForm'
import VisitPlanAdjust from './components/visitPlanAdjust'
export default {
name: 'VisitPlan',
components: { BaseContainer, BaseModel, VisitPlanForm, VisitPlanAdjust },
data() {
return {
list: [],
trialId: '',
listLoading: false,
row: {},
visitPlanConfirmed: true,
subjectHasAdded: true,
isHaveFirstGiveMedicineDate: false,
adjustVisible: false,
confirmLoading: false,
visit_model: { visible: false, title: '', width: '600px' },
userTypeEnumInt: zzSessionStorage.getItem('userTypeEnumInt') * 1,
isConfirm: false,
form: { TrialId: '', BlindBaseLineName: '', BlindFollowUpPrefix: '' },
rules: {
BlindBaseLineName: [
{ required: true, message: this.$t('common:ruleMessage:specify'), trigger: 'blur' }
],
BlindFollowUpPrefix: [
{ required: true, message: this.$t('common:ruleMessage:specify'), trigger: 'blur' }
]
},
config_model: { visible: false, title: this.$t('trials:visitPlan:button:blindFlag') }, // '盲态标识'
formLoading: false,
isHaveGeneratedTask: false
}
},
mounted() {
this.trialId = this.$route.query.trialId
this.getList()
},
methods: {
getList() {
this.listLoading = true
getVisitStageList(this.trialId)
.then((res) => {
this.listLoading = false
this.list = res.Result.VisitPlanList
this.visitPlanConfirmed = res.Result.VisitPlanConfirmed
this.isHaveFirstGiveMedicineDate = res.Result.IsHaveFirstGiveMedicineDate
this.isHaveGeneratedTask = res.Result.IsHaveGeneratedTask
this.form.BlindBaseLineName = res.Result.BlindBaseLineName
this.form.BlindFollowUpPrefix = res.Result.BlindFollowUpPrefix
this.isConfirm = res.Result.VisitPlanList.some(item => { return item.IsConfirmed === false })
})
.catch(() => {
this.listLoading = false
})
},
handleAdd() {
this.row = { TrialId: this.trialId, Id: '', IsBaseLine: false, BlindName: '', VisitWindowLeft: 0, VisitWindowRight: 0, Description: '' }
this.timer = new Date().getTime()
if (this.list.length === 0) {
this.row.VisitName = 'Baseline'
this.row.VisitNum = 0
this.row.VisitDay = 1
} else if (this.list.length === 1) {
this.row.VisitName = 'Visit ' + (this.list[0].VisitNum + 1)
this.row.VisitNum = this.list[0].VisitNum + 1
} else {
console.log(1)
this.row.VisitName = this.list[this.list.length - 1].VisitName.split(' ')[0] + ' ' + (this.list[this.list.length - 1].VisitNum + 1)
this.row.VisitNum = this.list[this.list.length - 1].VisitNum + 1
this.row.VisitDay = this.list[this.list.length - 1].VisitDay - this.list[this.list.length - 2].VisitDay + this.list[this.list.length - 1].VisitDay
}
this.visit_model.title = this.$t('common:button:add')
this.visit_model.visible = true
},
handleEdit(row) {
this.row = row
this.visit_model.title = this.$t('common:button:edit')
this.visit_model.visible = true
},
handleConfirm() {
const msg = this.$t('trials:adjustRecord:message:confirm')
this.$confirm(msg, {
type: 'warning',
distinguishCancelAndClose: true
}).then(() => {
this.confirmLoading = true
confirmTrialVisitPlan(this.trialId).then((res) => {
this.confirmLoading = false
if (res.IsSuccess) {
this.getList()
}
}).catch(() => { this.confirmLoading = false })
}).catch(() => {})
},
closeDialog() {
this.visit_model.visible = false
},
handleSaveBlindName() {
this.$refs.blindNameForm.validate((valid) => {
if (valid) {
this.formLoading = true
this.form.TrialId = this.trialId
updateVisitBlindName(this.form).then((res) => {
this.formLoading = false
this.config_model.visible = false
this.$message.success(this.$t('common:message:savedSuccessfully'))
}).catch(() => {
this.formLoading = false
})
}
})
},
handleConfig() {
this.config_model.visible = true
}
}
}
</script>

View File

@ -0,0 +1,496 @@
<template>
<div class="reading-rule-config-form">
<!-- 配置信息表单 -->
<el-form
ref="readingRulesForm"
v-loading="loading"
:model="form"
style="width:800px;"
:rules="rules"
label-width="260px"
size="small"
>
<!-- 阅片平台 -->
<el-form-item
:label="$t('trials:processCfg:form:imagePlatform')"
prop="ImagePlatform"
>
<el-radio-group
v-model="form.ImagePlatform"
:disabled="isConfirm || (!hasPermi(['trials:trials-panel:setting:reading-unit:edit']))"
>
<el-radio
v-for="item of $d.ImagePlatform"
:key="item.id"
:label="item.value"
>
{{ item.label }}
</el-radio>
</el-radio-group>
</el-form-item>
<!-- 阅片工具 -->
<el-form-item
v-if="form.ImagePlatform === 3"
:label="$t('trials:readingUnit:readingRules:title:readingTool')"
prop="ReadingTool"
>
<el-radio-group
v-model="form.ReadingTool"
:disabled="isConfirm || (!hasPermi(['trials:trials-panel:setting:reading-unit:edit']))"
>
<el-radio
v-for="item of $d.ReadingTool"
:key="item.id"
:label="item.value"
>
{{ item.label }}
</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item
:label="$t('trials:processCfg:form:readingType')"
prop="ReadingType"
>
<el-radio-group
v-model="form.ReadingType"
:disabled="isConfirm || (!hasPermi(['trials:trials-panel:setting:reading-unit:edit']))"
>
<el-radio
v-for="item of $d.ReadingMethod"
:key="item.id"
:label="item.value"
>
{{ item.label }}
</el-radio>
</el-radio-group>
</el-form-item>
<!-- 是否阅片期 -->
<el-form-item
:label="$t('trials:readingUnit:readingRules:title:isReadingPeriod')"
prop="IsReadingPeriod"
>
<el-radio-group
v-model="form.IsReadingPeriod"
:disabled="isConfirm || (!hasPermi(['trials:trials-panel:setting:reading-unit:edit']))"
>
<el-radio
v-for="item of $d.YesOrNo"
:key="`IsReadingPeriod${item.value}`"
:label="item.value"
>
{{ item.label }}
</el-radio>
</el-radio-group>
</el-form-item>
<!-- 是否阅片期 -->
<el-form-item
label="是否自动生成任务"
prop="IsAutoCreate"
>
<el-radio-group
v-model="form.IsAutoCreate"
:disabled="isConfirm || (!hasPermi(['trials:trials-panel:setting:reading-unit:edit']))"
>
<el-radio
v-for="item of $d.YesOrNo"
:key="`IsAutoCreate${item.value}`"
:label="item.value"
>
{{ item.label }}
</el-radio>
</el-radio-group>
</el-form-item>
<!-- 是否阅片期 -->
<el-form-item
label="是否附加评估"
prop="IsAdditionalAssessment"
>
<el-radio-group
v-model="form.IsAdditionalAssessment"
:disabled="isConfirm || (!hasPermi(['trials:trials-panel:setting:reading-unit:edit']))"
>
<el-radio
v-for="item of $d.YesOrNo"
:key="`IsAdditionalAssessment${item.value}`"
:label="item.value"
>
{{ item.label }}
</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item
:label="$t('trials:processCfg:form:global')"
prop="IsGlobalReading"
>
<el-radio-group
v-model="form.IsGlobalReading"
:disabled="IsMustGlobalReading || isConfirm || (!hasPermi(['trials:trials-panel:setting:reading-unit:edit']))"
>
<el-radio
v-for="item of $d.YesOrNo"
:key="`IsGlobalReading${item.value}`"
:label="item.value"
>
{{ item.label }}
</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item
v-if="form.ReadingType === 2"
:label="$t('trials:processCfg:form:ad')"
prop="IsArbitrationReading"
>
<el-radio-group
v-model="form.IsArbitrationReading"
:disabled="isConfirm || (!hasPermi(['trials:trials-panel:setting:reading-unit:edit']))"
>
<el-radio
v-for="item of $d.YesOrNo"
:key="`IsArbitrationReading${item.value}`"
:label="item.value"
>
{{ item.label }}
</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item
:label="$t('trials:processCfg:form:clinical')"
prop="IsOncologyReading"
>
<el-radio-group
v-model="form.IsOncologyReading"
:disabled="form.IsSystemCriterion || isConfirm || !hasPermi(['trials:trials-panel:setting:reading-unit:edit'])"
>
<el-radio
v-for="item of $d.YesOrNo"
:key="`IsOncologyReading${item.value}`"
:label="item.value"
>
{{ item.label }}
</el-radio>
</el-radio-group>
</el-form-item>
<!-- 任务展示方式 -->
<el-form-item
:label="$t('trials:readingUnit:readingRules:title:readingTaskViewInOrder')"
prop="IsReadingTaskViewInOrder"
>
<el-radio-group
v-model="form.IsReadingTaskViewInOrder"
:disabled="isConfirm || (!hasPermi(['trials:trials-panel:setting:reading-unit:edit']))"
@change="(v) => {
if (v) {
form.IsReadingShowSubjectInfo = true
form.IsReadingShowPreviousResults = true
IsNotReadingShowSubjectInfoChange = true
form.IseCRFShowInDicomReading = false
} else {
IsNotReadingShowSubjectInfoChange = false
}
}"
>
<el-radio
v-for="item of $d.IsReadingTaskViewInOrder"
:key="'IsReadingTaskViewInOrder' + item.value"
:label="item.value"
>
{{ item.label }}
</el-radio>
</el-radio-group>
</el-form-item>
<!-- 任务组织级别 -->
<el-form-item
:label="$t('trials:readingUnit:readingRules:title:readingTaskViewEnum')"
prop="ReadingTaskViewEnum"
>
<el-radio-group
v-model="form.ReadingTaskViewEnum"
:disabled="isConfirm || !isReadingTaskViewInOrder|| (!hasPermi(['trials:trials-panel:setting:reading-unit:edit']))"
>
<el-radio
v-for="item of $d.ReadingTaskViewEnum"
:key="item.id"
:label="item.value"
>
{{ item.label }}
</el-radio>
</el-radio-group>
</el-form-item>
<!-- 影像是否有标注 -->
<!-- <el-form-item
label="影像是否有标注"
prop="IsImageLabeled"
>
<el-radio-group
v-model="form.IsImageLabeled"
:disabled="isConfirm"
>
<el-radio
v-for="item of $d.YesOrNo"
:key="'IsImageLabeled'+item.value"
:label="item.value"
>
{{ item.label }}
</el-radio>
</el-radio-group>
</el-form-item> -->
<!-- IR阅片页面是否显示受试者信息 -->
<el-form-item
:label="$t('trials:readingUnit:readingRules:title:isReadingShowSubjectInfo')"
prop="IsReadingShowSubjectInfo"
>
<el-radio-group
v-model="form.IsReadingShowSubjectInfo"
:disabled="isConfirm || (!hasPermi(['trials:trials-panel:setting:reading-unit:edit'])) || form.IsReadingTaskViewInOrder"
>
<el-radio
v-for="item of $d.YesOrNo"
:key="'IsReadingShowSubjectInfo'+item.value"
:label="item.value"
>
{{ item.label }}
</el-radio>
</el-radio-group>
</el-form-item>
<!-- IR阅片页面是否可以查看既往任务结果 -->
<el-form-item
:label="$t('trials:readingUnit:readingRules:title:isReadingShowPreviousResults')"
prop="IsReadingShowPreviousResults"
>
<el-radio-group
v-model="form.IsReadingShowPreviousResults"
:disabled="isConfirm || (!hasPermi(['trials:trials-panel:setting:reading-unit:edit'])) || form.IsReadingTaskViewInOrder"
>
<el-radio
v-for="item of $d.YesOrNo"
:key="'IsReadingShowPreviousResults'+item.value"
:label="item.value"
>
{{ item.label }}
</el-radio>
</el-radio-group>
</el-form-item>
<!-- 修约小数位数 -->
<el-form-item
:label="$t('trials:processCfg:form:digitPlaces')"
prop="DigitPlaces"
>
<el-radio-group
v-model="form.DigitPlaces"
:disabled="isConfirm || (!hasPermi(['trials:trials-panel:setting:reading-unit:edit']))"
>
<el-radio
v-for="item of $d.DigitPlaces"
:key="item.id"
:label="item.value"
>
{{ item.label }}
</el-radio>
</el-radio-group>
</el-form-item>
<!-- eCRF是否显示在影像阅片页面 -->
<el-form-item
:label="$t('trials:readingUnit:readingRules:title:isECRFShowInDicomReading')"
prop="IseCRFShowInDicomReading"
>
<el-radio-group
v-model="form.IseCRFShowInDicomReading"
:disabled="form.IsSystemCriterion || isConfirm || (!hasPermi(['trials:trials-panel:setting:reading-unit:edit'])) || form.IsReadingTaskViewInOrder"
>
<el-radio
v-for="item of $d.YesOrNo"
:key="item.label"
:label="item.value"
>
{{ item.label }}
</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item v-if="!isConfirm && hasPermi(['trials:trials-panel:setting:reading-unit:edit'])">
<!-- 保存 -->
<el-button
type="primary"
@click="handleSave(true)"
>
{{ $t('common:button:save') }}
</el-button>
</el-form-item>
</el-form>
</div>
</template>
<script>
import { getCriterionReadingInfo, setCriterionReadingInfo } from '@/api/trials'
export default {
name: 'ReadingRules',
props: {
trialReadingCriterionId: {
type: String,
default() {
return ''
}
}
},
data() {
return {
form: {
TrialId: '',
ImagePlatform: null,
ReadingTool: 0,
ReadingTaskViewEnum: null,
IsImageLabeled: null,
IsReadingShowSubjectInfo: null,
IsReadingShowPreviousResults: null,
ReadingType: 1,
IsGlobalReading: false,
IsArbitrationReading: false,
IsOncologyReading: 0,
IsReadingTaskViewInOrder: false,
IsReadingPeriod: false,
// IsReadingTaskViewInOrder: null,
ArbitrationRule: null,
GlobalUpdateType: null,
IsSystemCriterion: true,
DigitPlaces: null,
IseCRFShowInDicomReading: false,
IsAutoCreate: null,
IsAdditionalAssessment: null
},
rules: {
ImagePlatform: [
{ required: true, message: this.$t('common:ruleMessage:select'), trigger: ['blur', 'change'] }
],
ReadingTool: [
{ required: true, message: this.$t('common:ruleMessage:select'), trigger: ['blur', 'change'] }
],
ReadingTaskViewEnum: [
{ required: true, message: this.$t('common:ruleMessage:select'), trigger: ['blur', 'change'] }
],
IsImageLabeled: [
{ required: true, message: this.$t('common:ruleMessage:select'), trigger: ['blur', 'change'] }
],
IsReadingShowSubjectInfo: [
{ required: true, message: this.$t('common:ruleMessage:select'), trigger: ['blur', 'change'] }
],
IsReadingShowPreviousResults: [
{ required: true, message: this.$t('common:ruleMessage:select'), trigger: ['blur', 'change'] }
],
ReadingType: [
{ required: true, message: this.$t('common:ruleMessage:select'), trigger: ['blur', 'change'] }
],
IsReadingTaskViewInOrder: [
{ required: true, message: this.$t('common:ruleMessage:select'), trigger: ['blur', 'change'] }
],
IsGlobalReading: [
{ required: true, message: this.$t('common:ruleMessage:select'), trigger: ['blur', 'change'] }
],
IsArbitrationReading: [
{ required: true, message: this.$t('common:ruleMessage:select'), trigger: ['blur', 'change'] }
],
IsOncologyReading: [
{ required: true, message: this.$t('common:ruleMessage:select'), trigger: ['blur', 'change'] }
],
DigitPlaces: [
{ required: true, message: this.$t('common:ruleMessage:select'), trigger: ['blur', 'change'] }
],
IseCRFShowInDicomReading: [
{ required: true, message: this.$t('common:ruleMessage:select'), trigger: ['blur', 'change'] }
],
IsReadingPeriod: [
{ required: true, message: this.$t('common:ruleMessage:select'), trigger: ['blur', 'change'] }
]
// IsReadingTaskViewInOrder: [
// { required: true, message: this.$t('common:ruleMessage:select'), trigger: ['blur', 'change'] }
// ]
},
loading: false,
readingInfo: {},
isConfirm: true,
isReadingTaskViewInOrder: null,
IsMustGlobalReading: false,
IsNotReadingShowSubjectInfoChange: false
}
},
mounted() {
this.initPage()
console.log(this.$d.GlobalAssessType)
},
methods: {
initPage() {
this.loading = true
const trialId = this.$route.query.trialId
getCriterionReadingInfo({ trialId, TrialReadingCriterionId: this.trialReadingCriterionId }).then(res => {
this.loading = false
for (const k in this.form) {
if (res.Result.hasOwnProperty(k)) {
this.form[k] = res.Result[k]
}
}
this.isReadingTaskViewInOrder = res.Result.IsReadingTaskViewInOrder
this.isConfirm = res.Result.IsSign
this.IsMustGlobalReading = res.Result.IsMustGlobalReading
this.$emit('setConfirm', res.Result.IsSign)
this.$emit('setArbitrationReading', res.Result.IsArbitrationReading)
this.$emit('setIsClinicalReading', res.Result.IsClinicalReading)
this.$emit('setGlobalReading', res.Result.IsGlobalReading)
this.$emit('setOncologyReading', res.Result.IsOncologyReading)
this.$emit('setDigitPlaces', res.Result.DigitPlaces)
if (res.Result.ReadingType === 1) {
this.$emit('setArbitrationReading', false)
}
}).catch(() => {
this.loading = false
})
},
// 配置信息保存
handleSave(isPrompt = true) {
return new Promise((resolve, reject) => {
this.$refs['readingRulesForm'].validate((valid) => {
if (!valid) {
resolve(false)
} else {
this.loading = true
if (this.form.ReadingType === 1) {
this.form.IsArbitrationReading = false
}
// 保存配置信息
this.form.TrialReadingCriterionId = this.trialReadingCriterionId
setCriterionReadingInfo(this.form).then(res => {
this.loading = false
this.$emit('reloadArbitrationRules')
this.$emit('setArbitrationReading', this.form.IsArbitrationReading)
this.$emit('setIsClinicalReading', this.form.IsClinicalReading)
this.$emit('setGlobalReading', this.form.IsGlobalReading)
this.$emit('setOncologyReading', this.form.IsOncologyReading)
this.$emit('setDigitPlaces', this.form.DigitPlaces)
if (this.form.ReadingType === 1) {
this.$emit('setArbitrationReading', false)
}
if (res.IsSuccess && isPrompt) {
this.$message.success(this.$t('common:message:savedSuccessfully'))
}
resolve(true)
}).catch(_ => {
this.loading = false
resolve(false)
})
}
})
})
}
}
}
</script>
<style lang="scss" scoped>
.reading-rule-config-form{
.fontColor{
color: red;
}
}
</style>

View File

@ -0,0 +1,567 @@
<template>
<div ref="container" style="width:100%;height:100%" class="dicom-container">
<!-- 访视阅片 -->
<div v-if="(isReadingTaskViewInOrder || (!isReadingTaskViewInOrder && isShow)) && readingCategory=== 1 && CriterionType !== 0" class="reading-wrapper">
<el-tabs v-model="activeName" v-loading="loading" :before-leave="beforeLeave">
<!-- 阅片 -->
<el-tab-pane :label="$t('trials:reading:tabTitle:review')" name="read">
<ReadPage
v-if="tabs.includes('read')"
:trial-id="trialId"
:visit-task-id="visitTaskId"
:subject-id="subjectId"
:subject-code="subjectCode"
:is-show="isShow"
:question-form-change-state="questionFormChangeState"
:question-form-change-num="questionFormChangeNum"
:is-reading-show-subject-info="isReadingShowSubjectInfo"
:is-reading-task-view-in-order="isReadingTaskViewInOrder"
:is-exists-clinical-data="isExistsClinicalData"
:is-exists-no-dicom-file="isExistsNoDicomFile"
:is-exists-manual="isExistsManual"
@previewCD="previewCD"
/>
</el-tab-pane>
<!-- 报告 -->
<el-tab-pane :label="$t('trials:reading:tabTitle:report')" name="report">
<ReportPage v-if="tabs.includes('report') && isShow" ref="reportPage" :question-form-change-state="questionFormChangeState" :visit-task-id="visitTaskId" />
</el-tab-pane>
</el-tabs>
</div>
<div v-if="(isReadingTaskViewInOrder || (!isReadingTaskViewInOrder && isShow)) && readingCategory=== 1 && CriterionType === 0" class="reading-wrapper">
<el-tabs v-model="activeName" v-loading="loading" :before-leave="beforeLeaveCustomize">
<!-- 阅片 -->
<el-tab-pane :label="$t('trials:reading:tabTitle:review')" name="read">
<CustomizeReadPage
v-if="tabs.includes('read')"
:trial-id="trialId"
:visit-task-id="visitTaskId"
:subject-id="subjectId"
:subject-code="subjectCode"
:is-show="isShow"
:is-reading-show-subject-info="isReadingShowSubjectInfo"
:is-reading-task-view-in-order="isReadingTaskViewInOrder"
:ise-c-r-f-show-in-dicom-reading="IseCRFShowInDicomReading"
:is-exists-clinical-data="isExistsClinicalData"
:is-exists-no-dicom-file="isExistsNoDicomFile"
:is-exists-manual="isExistsManual"
@previewCD="previewCD"
/>
</el-tab-pane>
<!-- 报告 -->
<el-tab-pane v-if="!IseCRFShowInDicomReading" :label="$t('trials:reading:tabTitle:report')" name="report">
<CustomizeReportPage v-if="isShow" ref="reportPage" :visit-task-id="visitTaskId" />
</el-tab-pane>
</el-tabs>
</div>
<!-- 全局阅片 -->
<GlobalReview
v-else-if="isShow && readingCategory === 2"
:trial-id="trialId"
:subject-id="subjectId"
:visit-task-id="visitTaskId"
:reading-category="readingCategory"
:subject-code="subjectCode"
:task-blind-name="taskBlindName"
:is-reading-show-subject-info="isReadingShowSubjectInfo"
:is-reading-show-previous-results="isReadingShowPreviousResults"
:is-exists-clinical-data="isExistsClinicalData"
/>
<!-- 裁判阅片 -->
<AdReview
v-else-if="isShow && readingCategory === 4"
:trial-id="trialId"
:subject-id="subjectId"
:visit-task-id="visitTaskId"
:reading-category="readingCategory"
:subject-code="subjectCode"
:task-blind-name="taskBlindName"
:is-reading-show-subject-info="isReadingShowSubjectInfo"
:is-reading-show-previous-results="isReadingShowPreviousResults"
:is-exists-clinical-data="isExistsClinicalData"
/>
<!-- 肿瘤学阅片 -->
<OncologyReview
v-else-if="isShow && readingCategory === 5"
:trial-id="trialId"
:subject-id="subjectId"
:visit-task-id="visitTaskId"
:reading-category="readingCategory"
:subject-code="subjectCode"
:task-blind-name="taskBlindName"
:is-reading-show-subject-info="isReadingShowSubjectInfo"
:is-reading-show-previous-results="isReadingShowPreviousResults"
:is-exists-clinical-data="isExistsClinicalData"
/>
<el-dialog
:visible.sync="dialogVisible"
:custom-class="isFullscreen?'full-dialog-container':'dialog-container'"
:show-close="false"
:close-on-click-modal="false"
:fullscreen="isFullscreen"
>
<span slot="title" class="dialog-footer">
<!-- 当前阅片任务存在临床数据,请查看。若已查看,请点击“确认” -->
<span v-if="!closeCDVisible">{{ $t('trials:reading:dagTitle:msg1') }}</span>
<div style="position: absolute;right: 20px;top: 10px;">
<svg-icon :icon-class="isFullscreen?'exit-fullscreen':'fullscreen'" style="cursor: pointer;font-size: 20px;" @click="isFullscreen=!isFullscreen" />
<svg-icon v-if="closeCDVisible" icon-class="dClose" style="cursor: pointer;font-size: 25px;margin-left: 10px;" @click="dialogVisible = false" />
</div>
</span>
<div style="height: 100%;margin:0;">
<ClinicalData
v-if="dialogVisible"
:trial-id="trialId"
:subject-id="subjectId"
:visit-task-id="visitTaskId"
/>
</div>
<span v-if="!closeCDVisible" slot="footer" class="dialog-footer">
<!-- 确认 -->
<el-button type="primary" @click="handleConfirmCD">{{ $t('trials:reading:button:confirm') }}</el-button>
</span>
</el-dialog>
<el-dialog
:visible.sync="tipVisible"
:show-close="false"
:close-on-click-modal="false"
width="500px"
>
<span slot="title" class="dialog-footer">
<!-- 提示 -->
{{ $t('trials:iRecist:warnning:msg44') }}
</span>
<div>
<div>
<!-- 您确定当前访视的整体评估结果为PD。根据《独立影像评估章程》该访视进入iRECIST阅片环节。请对新病灶进行重新评估 -->
{{ $t('trials:iRecist:warnning:msg45') }}
</div>
<div style="color: #f44336;font-size: 12px;margin: 5px;">
<!-- 1新病灶可转为新靶病灶、新非靶病灶、其它既往新病灶。 -->
{{ $t('trials:iRecist:warnning:msg46') }}
</div>
<div style="color: #f44336;font-size: 12px;margin: 5px;">
<!-- 2新靶病灶状态为明确的新病灶才能转换为新靶病灶。新靶病灶的选择规则与RECIST1.1相同;如果当前病灶已有标记,但不符合新靶病灶的要求,请清除标记,重新标注。 -->
{{ $t('trials:iRecist:warnning:msg47') }}
</div>
<div style="color: #f44336;font-size: 12px;margin: 5px;">
<!-- 3新非靶病灶状态为明确的新病灶才能转换为新非靶病灶。新靶病灶的选择规则与RECIST1.1相同。 -->
{{ $t('trials:iRecist:warnning:msg48') }}
</div>
<div style="color: #f44336;font-size: 12px;margin: 5px;">
<!-- 4其它既往新病灶系统会自动将状态为疑似、消失的新病灶转换为其它既往新病灶无需修改。 -->
{{ $t('trials:iRecist:warnning:msg49') }}
</div>
</div>
<span slot="footer" class="dialog-footer">
<!-- 确认 -->
<el-button type="primary" @click="tipVisible = false">{{ $t('trials:reading:button:confirm') }}</el-button>
</span>
</el-dialog>
</div>
</template>
<script>
import { getNextTask, readClinicalData, verifyDefaultQuestionBeAnswer } from '@/api/trials'
import ReadPage from './components/ReadPage'
import CustomizeReadPage from './customize/CustomizeReadPage'
import ReportPage from './components/ReportPage'
import CustomizeReportPage from './customize/CustomizeReportPage'
import GlobalReview from '@/views/trials/trials-panel/reading/global-review'
import AdReview from '@/views/trials/trials-panel/reading/ad-review'
import OncologyReview from '@/views/trials/trials-panel/reading/oncology-review'
import ClinicalData from '@/views/trials/trials-panel/reading/clinical-data'
import DicomEvent from './components/DicomEvent'
import store from '@/store'
import { getToken } from '@/utils/auth'
import { changeURLStatic } from '@/utils/history.js'
export default {
name: 'Reading',
components: {
ReadPage,
ReportPage,
GlobalReview,
AdReview,
OncologyReview,
ClinicalData,
CustomizeReadPage,
CustomizeReportPage
},
data() {
return {
isClinicalDataShow: false,
activeName: '',
tabs: [],
trialId: '',
subjectCode: '',
subjectId: '',
visitTaskId: '',
loading: false,
readingCategory: null,
isReadingShowSubjectInfo: false,
isReadingShowPreviousResults: false,
isExistsClinicalData: false,
isExistsNoDicomFile: false,
isNeedReadClinicalData: false,
isReadClinicalData: false,
digitPlaces: 2,
isReadingTaskViewInOrder: false,
firstTaskReadingCategory: null,
criterionType: null,
readingTool: null,
dialogVisible: false,
TrialReadingCriterionId: null,
isNewSubject: null,
isShow: false,
IseCRFShowInDicomReading: false,
isQualityIssueSaved: false,
questionFormChangeState: false,
questionFormChangeNum: 0,
isFullscreen: false,
tipVisible: false,
closeCDVisible: false
}
},
mounted() {
DicomEvent.$on('questionFormChange', (v) => {
if (this.questionFormChangeNum !== 0) {
this.questionFormChangeState = v
}
this.questionFormChangeNum++
})
DicomEvent.$on('getNextTask', () => {
store.dispatch('reading/setActiveSeries', {})
store.dispatch('reading/setLastCanvasTaskId', '')
this.isQualityIssueSaved = false
this.firstTaskReadingCategory = this.readingCategory
if (!this.isReadingTaskViewInOrder) {
// 清除缓存visitTaskList
store.dispatch('reading/resetVisitTasks')
}
this.getTaskInfo(1)
})
this.activeName = this.$router.currentRoute.query.tabName ? this.$router.currentRoute.query.tabName : 'read'
this.tabs.push(this.activeName)
this.trialId = this.$router.currentRoute.query.trialId
this.subjectCode = this.$router.currentRoute.query.subjectCode
this.subjectId = this.$router.currentRoute.query.subjectId
this.visitTaskId = this.$router.currentRoute.query.visitTaskId
this.isReadingTaskViewInOrder = JSON.parse(this.$router.currentRoute.query.isReadingTaskViewInOrder)
this.criterionType = this.$router.currentRoute.query.criterionType
this.readingTool = this.$router.currentRoute.query.readingTool
this.TrialReadingCriterionId = this.$router.currentRoute.query.TrialReadingCriterionId
this.isNewSubject = this.$router.currentRoute.query.isNewSubject
if (this.isNewSubject && this.isReadingTaskViewInOrder) {
// 已开始受试者${this.subjectCode}阅片任务
var msg = this.$t('trials:reading:warnning:msg1')
msg = msg.replace('xxx', this.subjectCode)
this.$message.success(msg)
changeURLStatic('isNewSubject', '')
}
if (this.$router.currentRoute.query.TokenKey) {
store.dispatch('user/setToken', this.$router.currentRoute.query.TokenKey)
changeURLStatic('TokenKey', '')
}
store.dispatch('reading/resetVisitTasks')
this.getTaskInfo()
window.addEventListener('beforeunload', this.handleWindowClose)
},
beforeDestroy() {
DicomEvent.$off('getNextTask')
DicomEvent.$emit('resetOpenWindow')
window.removeEventListener('beforeunload', this.handleWindowClose)
},
methods: {
getTaskInfo(flag = 0) {
this.loading = true
var param = {
subjectId: this.subjectId,
trialId: this.trialId,
subjectCode: this.subjectCode,
visitTaskId: this.$router.currentRoute.query.visitTaskId,
trialReadingCriterionId: this.$router.currentRoute.query.TrialReadingCriterionId
}
getNextTask(param).then(async res => {
this.isShow = false
this.visitTaskId = res.Result.VisitTaskId
// DicomEvent.$emit('getCustomTableQuestionAnswer', res.Result.VisitTaskId)
if (this.subjectId !== res.Result.SubjectId) {
store.dispatch('reading/resetVisitTasks')
var token = getToken()
window.location.href = `/readingDicoms?TrialReadingCriterionId=${this.TrialReadingCriterionId}&trialId=${this.trialId}&subjectCode=${res.Result.SubjectCode}&subjectId=${res.Result.SubjectId}&isReadingTaskViewInOrder=${this.isReadingTaskViewInOrder}&criterionType=${this.criterionType}&readingTool=${this.readingTool}&TokenKey=${token}&isNewSubject=1`
} else if (!this.isReadingTaskViewInOrder || res.Result.ReadingCategory !== 1) {
store.dispatch('reading/resetVisitTasks')
}
this.subjectId = res.Result.SubjectId
this.subjectCode = res.Result.SubjectCode
this.taskBlindName = res.Result.TaskBlindName
this.isReadingShowSubjectInfo = res.Result.IsReadingShowSubjectInfo
this.isReadingShowPreviousResults = res.Result.IsReadingShowPreviousResults
this.isExistsNoDicomFile = res.Result.IsExistsNoDicomFile
this.isExistsClinicalData = res.Result.IsExistsClinicalData
this.isExistsManual = res.Result.ExistsManual
this.isReadClinicalData = res.Result.IsReadClinicalData
this.isNeedReadClinicalData = res.Result.IsNeedReadClinicalData
this.CriterionType = res.Result.CriterionType
this.digitPlaces = res.Result.DigitPlaces
this.IseCRFShowInDicomReading = res.Result.IseCRFShowInDicomReading
localStorage.setItem('CriterionType', res.Result.CriterionType)
localStorage.setItem('digitPlaces', res.Result.DigitPlaces)
this.readingCategory = res.Result.ReadingCategory
this.questionFormChangeState = false
this.questionFormChangeNum = 0
if (this.isExistsClinicalData && this.isNeedReadClinicalData && !this.isReadClinicalData) {
this.isFullscreen = false
this.dialogVisible = true
}
if (res.Result.ReadingCategory === 1) {
this.activeName = 'read'
this.tabs = [this.activeName]
}
if (this.firstTaskReadingCategory === 1 && res.Result.ReadingCategory === 1 && this.isReadingTaskViewInOrder && flag) {
this.activeName = 'read'
this.tabs = [this.activeName]
this.$nextTick(() => {
DicomEvent.$emit('getNextVisitInfo')
DicomEvent.$emit('getReportInfo')
DicomEvent.$emit('resetOpenWindow')
})
}
this.$nextTick(() => {
this.isShow = true
this.loading = false
if (res.Result.IsFirstChangeTask && res.Result.ReadingTaskState === 0) {
this.tipVisible = true
}
})
}).catch(() => {
store.dispatch('reading/setCurrentReadingTaskState', 2)
this.loading = false
})
},
beforeLeaveCustomize(activeName, oldActiveName) {
if (oldActiveName === 'read') {
this.isShow = true
DicomEvent.$emit('getReportInfo', true)
this.$nextTick(() => {
this.$refs.reportPage.setScrollTop(1)
})
} else {
this.isShow = false
}
return Promise.resolve(true)
},
myConfirm(msg) {
return new Promise(resolve => {
this.$confirm(msg, {
type: 'warning',
showCancelButton: true
}).then(() => {
// this.tabs.push(activeName)
// this.$nextTick(() => {
// this.$refs.reportPage.setScrollTop(1)
// })
resolve(true)
}).catch(() => {
resolve(false)
})
})
},
async beforeLeave(activeName, oldActiveName) {
if (oldActiveName === 'read') {
this.tabs.push(activeName)
this.$nextTick(() => {
this.$refs.reportPage.setScrollTop(1)
})
return Promise.resolve(true)
// if (this.questionFormChangeState) {
// try {
// var msg = this.$t('trials:reading:warnning:msg201')
// var isgo = await this.myConfirm(msg)
// if (isgo) {
// // console.log(this.$refs['questions'])
// // this.$refs['questions'].handleSave()
// this.tabs.push(activeName)
// this.$nextTick(() => {
// this.$refs.reportPage.setScrollTop(1)
// })
// return Promise.resolve(true)
// } else {
// return Promise.reject(false)
// }
// } catch (e) {
// console.log(e)
// }
// } else {
// this.tabs.push(activeName)
// return Promise.resolve(true)
// }
// var list = null
// DicomEvent.$emit('getAllUnSaveLesions', val => {
// list = val
// })
// if (list.length > 0) {
// var arr = []
// list.map(i => {
// arr.push(i.lessionName)
// })
// // 请先保存${arr.join('、')}病灶信息!
// var msg = this.$t('trials:reading:warnning:msg2')
// msg = msg.replace('xxx', arr.join('、'))
// var isgo = await this.myConfirm(msg)
// if (isgo) {
// this.tabs.push(activeName)
// this.$nextTick(() => {
// this.$refs.reportPage.setScrollTop(1)
// })
// return Promise.resolve(true)
// } else {
// return Promise.reject(false)
// }
// } else {
// this.tabs.push(activeName)
// return Promise.resolve(true)
// }
// this.tabs.push(activeName)
// return Promise.resolve(true)
} else {
console.log(activeName)
if (!this.tabs.includes(activeName)) {
this.tabs.push(activeName)
}
// return true
return Promise.resolve(true)
}
},
verifyVisitTaskQuestions() {
return new Promise((resolve, reject) => {
this.loading = true
verifyDefaultQuestionBeAnswer({ visitTaskId: this.visitTaskId }).then(res => {
this.loading = false
this.isQualityIssueSaved = true
resolve()
}).catch(() => {
this.loading = false
this.isQualityIssueSaved = false
reject()
})
})
},
handleConfirmCD() {
this.loading = true
var visitTaskId = this.visitTaskId
readClinicalData({ visitTaskId }).then(res => {
this.loading = false
this.dialogVisible = false
this.isReadClinicalData = true
}).catch(() => { this.loading = false })
},
previewCD() {
this.closeCDVisible = true
this.isFullscreen = false
this.dialogVisible = true
},
handleWindowClose() {
DicomEvent.$emit('resetOpenWindow')
}
}
}
</script>
<style lang="scss" scoped>
>>>.el-message-box__headerbtn{
display: none;
}
.dicom-container{
.reading-wrapper{
width: 100%;
height: 100%;
padding: 0 5px;
box-sizing: border-box;
background-color: #000;
.el-tabs{
box-sizing: border-box;
height: 100%;
display: flex;
flex-direction: column;
.el-tabs__item{
color: #fff;
}
>>>.el-tabs__header{
height: 50px;
margin:0px;
box-sizing: border-box;
}
>>>.el-tabs__content{
flex: 1;
margin:0px;
box-sizing: border-box;
}
>>>.el-tabs__item{
color: #fff;
}
>>>.el-tab-pane{
height: 100%;
}
}
>>>.hot-keys-label{
color: #dfdfdf !important;
}
>>>.shortcut-key-input span{
color: #dfdfdf !important;
}
// >>>.el-dialog{
// background: #1e1e1e;
// >>>.el-dialog__body{
// padding: 10px;
// }
// }
>>>.el-dialog{
background: #1e1e1e;
border: 1px solid #ddd;
color: #ddd;
.el-dialog__title{
color:#fff;
}
.el-input .el-input__inner{
background-color: transparent;
color: #ddd;
border: 1px solid #5e5e5e;
}
.el-input.is-disabled .el-input__inner{
background-color: #646464a1;
}
.el-form-item__label{
color: #dfdfdf
}
}
}
>>>.dialog-container{
margin-top: 50px !important;
width:75%;
height:80%;
}
>>>.el-dialog__body{
padding: 10px;
height: calc(100% - 120px);
}
.el-dialog__header{
position: relative;
}
}
.full-dialog-container{
>>>.is-fullscreen .el-dialog__body{
height: calc(100% - 120px);
}
}
</style>

View File

@ -0,0 +1,65 @@
/* eslint-disable */
export default {
'components:uploadvideo:message:xf1': `上传成功`,
'components:uploadvideo:message:xf2': `选择的视频不能在浏览器环境播放,请点击转码,等待转码成功之后再进行上传操作`,
'components:uploadvideo:message:xf3': `页面未在安全上下文中不能进行转码请联系开发人员配置SSL证书`,
'components:uploadvideo:message:xf4': `nginx未设置同源隔离不能进行转码请联系开发人员配置Nginx请求头`,
'components:uploadvideo:message:xf5': `开始转码,请勿关闭窗口或页面`,
'components:uploadvideo:message:xf6': `转码成功`,
'components:uploadvideo:message:xf7': `文件读取成功,请上传`,
'trials:uploadNonDicoms:action:xfuploadvideo': `上传视频文件`,
'trials:uploadNonDicoms:title:xfuploadvideo': `上传视频文件`,
'trials:uploadNonDicoms:title:xflookvideo': `查看视频文件`,
'trials:check:radio:passreason1': `线上质疑解决核查通过`,
'trials:check:radio:passreason2': `线下核查通过`,
'trials:check:radio:passreason3': `实时阅片跳过核查`,
'trials:check:radio:passreason4': `其它`,
'trials:check:radio:closereason1': `问题已解决`,
'trials:check:radio:closereason2': `问题无法解决强制关闭质疑`,
'trials:check:radio:closereason3': `其他`,
'trials:qcQuality:label:passReason': `通过原因`,
'trials:qcQuality:label:closeReason': `关闭原因`,
'trials:consistencyCheck:dialogTitle:xfpassManually': `一致性核查通过`,
'trials:consistencyCheck:dialogTitle:xfclose': `关闭一致性核查`,
'recompose:form:email': `邮箱`,
'recompose:form:role': `角色`,
'trials:consistencyCheck:label:passreason': `原因`,
'trials:consistencyCheck:label:closereason': `备注`,
'trials:trials-panel:taskAllocation': `访视任务分配`,
'trials:trials-panel:reading': `阅片`,
'trials:trials-panel:refereeAllocation': `裁判任务分配`,
'trials:trials-panel:readingTracking': `阅片跟踪`,
'trials:trials-panel:reReadingTracking': `重阅跟踪`,
'trials:trials-panel:tab:pmMedicalFeedback': `医学审核`,
'trials:trials-panel:reading:consistency-analysis': `一致性分析`,
'trials:trials-panel:attachments:reReadingTracking': `重阅审批`,
'trials:trials-panel:message:notIsHasDicom': `当前文件夹没有DICOM文件,请检查文件夹是否正确!`,
'trials:trials-panel:reading:readTaskAllocation': `阅片任务分配`,
'trials:trials-panel:attachments:spm-enrollment': `阅片人筛选`,
'trials:seletctedReviews:title:language': `语言`,
'trials:enrolledReviews:button:sow': `既往阅片情况声明`,
'trials:enrolledReviews:button:asow': `入项资格确认书`,
'trials:enrolledReviews:button:tackNum': `任务量(未读/已读/总数)`,
'DicomViewer:data:customWwc': `自定义调窗`,
'CustomWwwcForm:form:label:ww': `窗宽`,
'CustomWwwcForm:form:label:wl': `窗位`,
'trials:trials-panel:readTaskAllocation:search:ReadPeople': `阅片人`,
'trials:trials-panel:readTaskAllocation:button:AllocationRule': `分配规则`,
'trials:trials-panel:readTaskAllocation:button:ManualDistribution': `手动分配`,
'trials:trials-panel:readTaskAllocation:button:vgoTask': `访视/全局/肿瘤学阅片任务`,
'trials:trials-panel:readTaskAllocation:button:adTask': `裁判阅片任务`,
'trials:trials-panel:readTaskAllocation:table:TrialSiteCode': `中心编号`,
'trials:trials-panel:readTaskAllocation:table:SubjectCode': `受试者编号`,
'trials:trials-panel:readTaskAllocation:table:VisitTaskTypeCount': `访视`,
'trials:trials-panel:readTaskAllocation:table:GlobalTaskTypeCount': `全局`,
'trials:trials-panel:readTaskAllocation:table:JudgeTaskTypeCount': `裁判`,
'trials:trials-panel:readTaskAllocation:table:OncologyTaskTypeCount': `肿瘤学阅片`,
'trials:trials-panel:readTaskAllocation:table:R1': `阅片人(R1)`,
'trials:trials-panel:readTaskAllocation:table:R2': `阅片人(R2)`,
'trials:trials-panel:readTaskAllocation:table:AD': `裁判(AD)`,
'trials:trials-panel:readTaskAllocation:table:O1': `肿瘤阅片人(O1)`,
'trials:trials-panel:readTaskAllocation:table:operation': `操作`,
'trials:trials-panel:readTaskAllocation:table:ManualDistribution': `手动分配`,
'trials:trials-panel:readTaskAllocation:table:operation': `清除阅片人`,
'trials:trials-panel:readTaskAllocation:table:operation': `查看取消历史`,
}

View File

@ -0,0 +1,267 @@
<template>
<div ref="monthly_reading_stats" style="width:100%;height:100%;" />
</template>
<script>
import echarts from 'echarts'
import { getReadingDataByMonth } from '@/api/dashboard'
import { fontSize } from 'utils/fontsize'
const Count = 6
export default {
props: {
area: { type: String, default: '' }
},
data() {
return {
myChart: ''
}
},
watch: {
area() {
const that = this
this.$nextTick(function() {
if (that.myChart) {
that.myChart.resize()
}
})
}
},
mounted() {
this.getData()
},
methods: {
initChart(xData, tpArr, adjArr, gArr, totalArr) {
const option = {
// backgroundColor: '#344b58',
title: {
text: 'Workload of Past 6 Months',
x: '4%',
top: 5,
textStyle: {
color: '#fff',
fontSize: fontSize(0.16)
}
},
tooltip: {
trigger: 'axis',
axisPointer: {
type: 'shadow',
textStyle: {
color: '#999'
}
}
},
grid: {
// borderWidth: 0,
top: '33%',
left: 60,
bottom: 25,
textStyle: {
color: '#999'
}
},
legend: {
x: '4%',
top: '13%',
icon: 'circle',
itemWidth: fontSize(0.1),
textStyle: {
color: '#fff',
fontSize: fontSize(0.14)
},
data: ['TP', 'AD', 'GL', 'Total']
},
xAxis: [
{
axisLine: {
show: true,
lineStyle: {
color: 'rgba(255,255,255,.1)',
// width: 1,
type: 'solid'
}
},
axisTick: {
show: false
},
axisLabel: {
interval: 1,
show: true,
// splitNumber: 15,
textStyle: {
color: '#fff',
fontSize: fontSize(0.14)
}
},
data: xData
}
],
yAxis: [
{
type: 'value',
axisLabel: {
show: true,
textStyle: {
color: '#fff',
fontSize: fontSize(0.14)
}
},
axisTick: {
show: false
},
axisLine: {
show: true,
lineStyle: {
color: 'rgba(255,255,255,.1 )',
// width: 1,
type: 'solid'
}
},
splitLine: {
lineStyle: {
color: 'rgba(255,255,255,.1)'
}
}
}
],
series: [
{
name: 'TP',
type: 'bar',
stack: 'Total',
barMaxWidth: fontSize(0.4),
itemStyle: {
normal: {
color: '#2fA2FF',
label: {
show: true,
textStyle: {
color: '#fff',
fontSize: fontSize(0.14)
},
position: 'inside',
formatter: function(p) {
return p.value > 0 ? p.value : ''
}
}
}
},
data: tpArr
},
{
name: 'AD',
type: 'bar',
stack: 'Total',
itemStyle: {
normal: {
color: '#27d08a',
barBorderRadius: 0,
label: {
show: true,
position: 'inside',
textStyle: {
color: '#fff',
fontSize: fontSize(0.14)
},
formatter: function(p) {
return p.value > 0 ? p.value : ''
}
}
}
},
data: adjArr
},
{
name: 'GL',
type: 'bar',
stack: 'Total',
itemStyle: {
normal: {
color: '#2f89cf',
barBorderRadius: 0,
textStyle: {
color: '#fff',
fontSize: fontSize(0.14)
},
label: {
show: true,
position: 'inside',
formatter: function(p) {
return p.value > 0 ? p.value : ''
}
}
}
},
data: gArr
},
{
name: 'Total',
type: 'line',
// "stack": "Total",
smooth: true, // 平滑曲线显示
symbolSize: 10,
symbol: 'circle',
itemStyle: {
normal: {
color: 'rgba(252,230,48,1)',
// "barBorderRadius": 0,
label: {
// position: 'inside',
textStyle: {
color: '#fff',
fontSize: fontSize(0.14)
},
show: true,
position: 'top',
formatter: function(p) {
return p.value > 0 ? p.value : ''
}
}
}
},
data: totalArr
}
],
animationEasing: 'elasticOut',
animationDelayUpdate: function(idx) {
return idx * 5
}
}
if (this.$refs.monthly_reading_stats) {
this.myChart = echarts.init(this.$refs.monthly_reading_stats)
this.myChart.setOption(option)
}
},
getData() {
getReadingDataByMonth(Count).then(res => {
if (res.IsSuccess) {
if (res.Result) {
const xData = []
const tpArr = []
const adjArr = []
const gArr = []
const totalArr = []
const result = res.Result.reverse()
for (let i = 0; i < result.length; i++) {
xData.push(result[i].Month)
tpArr.push(result[i].Timepoint)
adjArr.push(result[i].Adjudication)
gArr.push(result[i].Global)
const total = result[i].Timepoint + result[i].Adjudication + result[i].Global
totalArr.push(total)
}
this.initChart(xData, tpArr, adjArr, gArr, totalArr)
}
} else {
this.$message({ message: res.ErrorMessage, type: 'error' })
}
})
// .catch(error => {
// this.$message({ message: error, type: 'error' })
// })
}
}
}
</script>

View File

@ -0,0 +1,445 @@
<template>
<BaseContainer>
<!-- 搜索框 -->
<template slot="search-container">
<el-form :inline="true" size="mini">
<el-form-item label="器官类型">
<el-select v-model="searchData.OrganType" clearable style="width:130px;">
<el-option v-for="item of $d.OrganType" :key="item.id" :value="item.value" :label="item.label" />
</el-select>
</el-form-item>
<el-form-item label="部位">
<el-input v-model="searchData.Part" clearable style="width:130px;"/>
</el-form-item>
<el-form-item label="器官">
<el-input v-model="searchData.TULOC" clearable style="width:130px;"/>
</el-form-item>
<el-form-item label="是否淋巴结">
<el-select v-model="searchData.IsLymphNodes" clearable style="width:130px;">
<el-option v-for="item of $d.YesOrNo" :key="item.id" :value="item.value" :label="item.label" />
</el-select>
</el-form-item>
<el-form-item>
<!-- 查询 -->
<el-button type="primary" icon="el-icon-search" size="mini" @click="handleSearch">
{{ $t('common:button:search') }}
</el-button>
<!-- 重置 -->
<el-button type="primary" icon="el-icon-refresh-left" size="mini" @click="handleReset">
{{ $t('common:button:reset') }}
</el-button>
</el-form-item>
</el-form>
<span style="margin-left:auto">
<!-- <el-button
type="primary"
size="mini"
@click="lesionType.visible = true"
>
病灶类型配置
</el-button> -->
<el-button
type="primary"
size="mini"
@click="lesionType.visible = true"
>
病灶关联配置
</el-button>
<!-- <el-button-->
<!-- v-if="!isCompleteConfig"-->
<!-- type="primary"-->
<!-- size="mini"-->
<!-- @click="handleSet"-->
<!-- >-->
<!-- 默认数据配置-->
<!-- </el-button>-->
<el-button
v-if="!isCompleteConfig"
type="primary"
size="mini"
@click="handleAdd"
>
{{ $t('common:button:new') }}
</el-button>
</span>
</template>
<template slot="main-container">
<!-- 受试者列表 -->
<el-table
ref="organList"
v-loading="loading"
v-adaptive="{bottomOffset:60}"
:data="list"
stripe
height="100"
@sort-change="handleSortByColumn"
>
<el-table-column type="index" width="90" />
<!-- 类型 -->
<el-table-column
prop="OrganType"
label="器官类型"
show-overflow-tooltip
sortable="custom"
>
<template slot-scope="scope">
{{ $fd('OrganType',scope.row.OrganType) }}
</template>
</el-table-column>
<!-- 部位 -->
<el-table-column
prop="Part"
label="部位"
show-overflow-tooltip
sortable="custom"
/>
<el-table-column
prop="PartEN"
label="部位(英文)"
show-overflow-tooltip
sortable="custom"
/>
<el-table-column
prop="TULOC"
label="器官"
show-overflow-tooltip
sortable="custom"
/>
<el-table-column
prop="TULOCEN"
label="器官(英文)"
show-overflow-tooltip
sortable="custom"
/>
<el-table-column
prop="TULAT"
label="位置"
show-overflow-tooltip
sortable="custom"
/>
<el-table-column
prop="TULATEN"
label="位置(英文)"
show-overflow-tooltip
sortable="custom"
/>
<el-table-column
prop="IsLymphNodes"
label="是否淋巴结"
show-overflow-tooltip
sortable="custom"
>
<template slot-scope="scope">
<el-tag v-if="scope.row.IsLymphNodes" type="danger">
{{ $fd('ReadingYesOrNo', scope.row.IsLymphNodes) }}
</el-tag>
<el-tag v-else type="primary">{{ $fd('ReadingYesOrNo', scope.row.IsLymphNodes) }}</el-tag>
</template>
</el-table-column>
<el-table-column
prop="IsCanEditPosition"
label="是否可以编辑"
show-overflow-tooltip
sortable="custom"
>
<template slot-scope="scope">
<el-tag v-if="scope.row.IsCanEditPosition" type="danger">
{{ $fd('YesOrNo', scope.row.IsCanEditPosition) }}
</el-tag>
<el-tag v-else type="primary">{{ $fd('YesOrNo', scope.row.IsCanEditPosition) }}</el-tag>
</template>
</el-table-column>
<el-table-column
prop="Remark"
label="备注"
show-overflow-tooltip
/>
<el-table-column
v-if="!isCompleteConfig"
:label="$t('common:action:action')"
width="200"
fixed="right"
>
<template slot-scope="scope">
<el-button
circle
title="编辑"
icon="el-icon-edit-outline"
@click="handleEdit(scope.row)"
/>
<el-button
circle
title="删除"
icon="el-icon-delete"
@click="handleDelete(scope.row)"
/>
</template>
</el-table-column>
</el-table>
<!-- 分页组件 -->
<pagination class="page" :total="total" :page.sync="searchData.PageIndex" :limit.sync="searchData.PageSize" @pagination="getList" />
</template>
<!-- 新增/编辑 -->
<el-dialog
v-if="editDialog.visible"
:visible.sync="editDialog.visible"
:close-on-click-modal="false"
:title="editDialog.title"
width="500px"
custom-class="base-dialog-wrapper"
append-to-body
>
<OrganForm
:data="rowData"
:criterion-id="criterionId"
@close="editDialog.visible = false"
@getList="getList"
/>
</el-dialog>
<!-- 病灶类型 -->
<el-dialog
v-if="lesionType.visible"
:visible.sync="lesionType.visible"
:close-on-click-modal="false"
:title="lesionType.title"
width="800px"
custom-class="base-dialog-wrapper"
append-to-body
>
<LesionTypeTbl
:criterion-id="criterionId"
:is-complete-config="isCompleteConfig"
@close="lesionType.visible = false"
/>
</el-dialog>
<!-- 选择标准 -->
<el-dialog
v-if="criterion.visible"
:visible.sync="criterion.visible"
:close-on-click-modal="false"
:title="criterion.title"
width="500px"
custom-class="base-dialog-wrapper"
append-to-body
>
<el-form
ref="criterionForm"
v-loading="formLoading"
:model="form"
size="small"
>
<div class="base-dialog-body">
<!-- 类型 -->
<el-form-item
label="标准"
prop="CriterionId"
:rules="[
{ required: true, message: this.$t('common:ruleMessage:select'), trigger: ['blur'] },
]"
>
<el-select
v-model="form.CriterionId"
>
<el-option
v-for="item of criterions"
:key="item.CriterionId"
:label="item.CriterionName"
:value="item.CriterionId"
:disabled="criterionId === item.CriterionId"
/>
</el-select>
</el-form-item>
</div>
<div class="base-dialog-footer" style="text-align:right;margin-top:10px;">
<el-form-item style="text-align:right;">
<el-button type="primary" @click="criterion.visible = false"> {{ $t('common:button:cancel') }}</el-button>
<el-button size="small" type="primary" @click="handleSave">
{{ $t('common:button:save') }}
</el-button>
</el-form-item>
</div>
</el-form>
</el-dialog>
</BaseContainer>
</template>
<script>
import { getOrganPageList, deleteOrganInfo, getCriterionNidusList, copySystemCriterionData } from '@/api/dictionary'
import { getSystemCriterionList, synchronizeSystemOrganToTrial } from '@/api/trials'
import BaseContainer from '@/components/BaseContainer'
import Pagination from '@/components/Pagination'
import OrganForm from './OrganForm'
import LesionTypeTbl from './LesionTypeTbl'
const searchDataDefault = () => {
return {
SystemCriterionId: '',
OrganType: null,
PageIndex: 1,
PageSize: 20,
TULOC: null,
TULAT: null,
IsLymphNodes: null,
}
}
export default {
name: 'OrgansList',
components: { BaseContainer, Pagination, OrganForm, LesionTypeTbl },
props: {
criterionId: {
type: String,
required: true
},
isCompleteConfig: {
type: Boolean,
required: true
}
},
data() {
return {
searchData: searchDataDefault(),
loading: false,
list: [],
total: 0,
rowData: {},
editDialog: { title: '', visible: false },
lesionType: { title: '病灶器官关联关系', visible: false },
// lesionTypes: [],
criterion: { title: '默认数据配置', visible: false },
formLoading: false,
form: { CriterionId: '' },
criterions: []
}
},
watch: {
list() {
this.$nextTick(() => {
this.$refs.organList.doLayout()
})
}
},
mounted() {
this.getCriterions()
this.getList()
},
methods: {
synchronizeSystemOrganToTrial() {
this.loading = true
synchronizeSystemOrganToTrial({
TrialReadingCriterionId: this.criterionId,
}).then(res => {
this.loading = false
this.$message.success('同步成功')
}).catch(() => {
this.loading = false
})
},
// 获取受试者列表
getList() {
this.loading = true
this.searchData.SystemCriterionId = this.criterionId
getOrganPageList(this.searchData).then(res => {
this.loading = false
// this.list = res.Result
this.list = res.Result.CurrentPageData
this.total = res.Result.TotalCount
}).catch(() => { this.loading = false })
},
getLesionTypes() {
return new Promise((resolve, reject) => {
getCriterionNidusList({ criterionId: this.criterionId }).then(res => {
this.lesionTypes = res.Result
resolve()
}).catch(() => {
reject()
})
})
},
// 新增
handleAdd() {
this.rowData = { SystemCriterionId: this.criterionId }
this.editDialog.title = '新增'
this.editDialog.visible = true
},
// 编辑
handleEdit(row) {
this.rowData = { ...row }
this.editDialog.title = '编辑'
this.editDialog.visible = true
},
// 删除
handleDelete(row) {
this.$confirm('是否确认删除', {
type: 'warning',
distinguishCancelAndClose: true
})
.then(() => {
this.loading = true
deleteOrganInfo(row.Id)
.then(res => {
this.loading = false
if (res.IsSuccess) {
this.getList()
this.$message.success('删除成功!')
}
}).catch(() => { this.loading = false })
})
},
getCriterions() {
getSystemCriterionList().then(res => {
this.criterions = res.Result
})
},
// 查询
handleSearch() {
this.searchData.PageIndex = 1
this.getList()
},
// 重置
handleReset() {
this.searchData = searchDataDefault()
this.getList()
},
// 排序
handleSortByColumn(column) {
if (column.order === 'ascending') {
this.searchData.Asc = true
} else {
this.searchData.Asc = false
}
this.searchData.SortField = column.prop
this.searchData.PageIndex = 1
this.getList()
},
handleSet() {
this.criterion.visible = true
this.form.CriterionId = ''
},
handleSave() {
this.$refs['criterionForm'].validate((valid) => {
if (!valid) return
this.formLoading = true
var param = {
sourceSystemCriterionId: this.form.CriterionId,
newSystemCriterionId: this.criterionId,
isCopyQuestion: false
}
copySystemCriterionData(param).then(res => {
this.getList()
this.criterion.visible = false
this.formLoading = false
}).catch(() => {
this.formLoading = false
})
})
}
}
}
</script>

View File

@ -0,0 +1,222 @@
<template>
<box-content v-loading="loading">
<!-- 搜索框 -->
<div class="search">
<el-form :inline="true" size="mini" class="base-search-form">
<!-- 审核问题 -->
<el-form-item :label="$t('trials:qcCfg:table:questionName')">
<el-input v-model="searchData.QuestionName" clearable style="width:120px;" />
</el-form-item>
<!-- 语言类型 -->
<el-form-item
:label="$t('common:title:languageType')"
>
<el-select
v-model="searchData.LanguageType"
style="width:120px;"
clearable
>
<el-option
v-for="item of $d.LanguageType"
:key="item.value"
:value="item.value"
:label="item.label"
/>
</el-select>
</el-form-item>
<!-- 类型 -->
<el-form-item :label="$t('trials:qcCfg:table:type')">
<el-select v-model="searchData.Type" clearable style="width:120px;">
<el-option v-for="item of $d.QcType" :key="item.label" :value="item.value" :label="item.label" />
</el-select>
</el-form-item>
<!-- 级别: -->
<el-form-item :label="$t('trials:qcCfg:table:level')">
<el-select v-model="searchData.IsDefeaultViewParent" clearable style="width:120px;">
<el-option v-for="item of $d.IsDefeaultViewParentLevel" :key="item.id" :value="item.value" :label="item.label" />
</el-select>
</el-form-item>
<el-form-item>
<el-button type="primary" @click="handleSearch">{{ $t('common:button:search') }}</el-button>
<el-button type="primary" @click="handleReset">{{ $t('common:button:reset') }}</el-button>
</el-form-item>
</el-form>
<span style="margin-left:auto">
<el-button type="primary" size="mini" :disabled="selectArr.length<=0" @click="handleSubmit">
{{ $t('common:button:submit') }}
</el-button>
</span>
</div>
<el-table
ref="qsList"
:data="list"
stripe
size="small"
height="400"
@selection-change="handleSelectChange"
@select="handleselect"
>
<el-table-column type="selection" align="left" width="45" :selectable="handleSelectTable" />
<!-- 序号 -->
<el-table-column
prop="ShowOrder"
:label="$t('trials:qcCfg:table:order')"
width="60"
/>
<!-- 审核问题 -->
<el-table-column
prop="QuestionName"
:label="$t('trials:qcCfg:table:questionName')"
show-overflow-tooltip
/>
<!-- 审核问题 -->
<el-table-column
prop="LanguageType"
:label="$t('common:title:languageType')"
show-overflow-tooltip
>
<template slot-scope="scope">
{{ $fd('LanguageType', scope.row.LanguageType) }}
</template>
</el-table-column>
<!-- 类型 -->
<el-table-column
prop="Type"
:label="$t('trials:qcCfg:table:type')"
show-overflow-tooltip
>
<template slot-scope="scope">
{{ $fd('QcType', scope.row.Type) }}
</template>
</el-table-column>
<!-- 选项 -->
<el-table-column
prop="TypeValue"
:label="$t('trials:qcCfg:table:typeValue')"
show-overflow-tooltip
min-width="110"
/>
<!-- 父问题 -->
<el-table-column
prop="ParentShowOrder"
:label="$t('trials:qcCfg:table:parentQs')"
show-overflow-tooltip
width="120"
/>
<!-- 父问题触发值 -->
<el-table-column
prop="ParentTriggerValue"
:label="$t('trials:qcCfg:table:parentTriggerValue')"
show-overflow-tooltip
/>
<!-- 是否必填 -->
<el-table-column
prop="IsRequired"
:label="$t('trials:qcCfg:table:isRequired')"
min-width="90"
>
<template slot-scope="scope">
{{ $fd('YesOrNo', scope.row.IsRequired) }}
</template>
</el-table-column>
<!-- 启用状态 -->
<el-table-column
prop="IsEnable"
:label="$t('trials:qcCfg:table:isEnable')"
min-width="120"
>
<template slot-scope="scope">
{{ $fd('YesOrNo', scope.row.IsEnable) }}
</template>
</el-table-column>
</el-table>
<Pagination style="margin-top: 10px" class="page" :total="total" :page.sync="searchData.PageIndex" :limit.sync="searchData.PageSize" @pagination="getList" />
</box-content>
</template>
<script>
import { getQCQuestionConfigureList } from '@/api/dictionary'
import { batchAddTrialQCQuestionConfigure } from '@/api/trials'
import BoxContent from '@/components/BoxContent'
import Pagination from '@/components/Pagination'
const searchDataDefault = () => {
return {
Asc: true,
SortField: 'ShowOrder',
QuestionName: '',
LanguageType: null,
Type: '',
IsEnable: true,
IsDefeaultViewParent: false,
PageIndex: 1,
PageSize: 20
}
}
export default {
name: 'QcQuestions',
components: { BoxContent, Pagination },
data() {
return {
trialId: '',
searchData: searchDataDefault(),
list: [],
total: 0,
loading: false,
selectArr: []
}
},
mounted() {
this.trialId = this.$route.query.trialId
this.getList()
},
methods: {
// 获取受试者列表信息
getList() {
this.loading = true
this.searchData.TrialId = this.trialId
getQCQuestionConfigureList(this.searchData).then(res => {
this.loading = false
this.list = res.Result.CurrentPageData
this.total = res.Result.TotalCount
}).catch(() => { this.loading = false })
},
handleSelectChange(val) {
this.selectArr = val
},
handleselect(selection, row) {
const selected = selection.length && selection.indexOf(row) !== -1
console.log(selection)
var arr = this.list.filter(i => i.ParentId === row.Id)
arr.map(item => {
this.$refs.qsList.toggleRowSelection(item, selected)
})
},
handleSubmit() {
this.loading = true
batchAddTrialQCQuestionConfigure(this.trialId, this.selectArr).then(res => {
this.loading = false
if (res.IsSuccess) {
this.$message.success(this.$t('common:message:savedSuccessfully'))
this.$emit('getList')
this.$emit('close')
}
}).catch(() => { this.loading = false })
},
handleSelectTable(row) {
return !row.ParentId
},
// 查询
handleSearch() {
this.getList()
},
// 重置
handleReset() {
this.searchData = searchDataDefault()
this.getList()
}
}
}
</script>

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,690 @@
/* eslint-disable */
<template>
<BaseContainer>
<template slot="search-container">
<el-form :inline="true">
<!-- 中心编号 -->
<el-form-item :label="$t('trials:pmMedicalReview:table:siteCode')">
<el-select v-model="searchData.TrialSiteCode" clearable filterable style="width:120px;">
<el-option
v-for="(item,index) of siteOptions"
:key="index"
:label="item"
:value="item"
/>
</el-select>
</el-form-item>
<!-- 受试者编号 -->
<el-form-item :label="$t('trials:pmMedicalReview:table:subjectCode')">
<el-input v-model="searchData.SubjectCode" style="width:100px;" />
</el-form-item>
<!-- 任务名称 -->
<el-form-item style="margin-bottom:10px" :label="$t('trials:pmMedicalReview:table:taskName')">
<el-input
v-model="searchData.TaskName"
style="width:100px;"
clearable
/>
</el-form-item>
<!-- 阅片人 -->
<el-form-item style="margin-bottom:10px" :label="$t('trials:pmMedicalReview:table:reader')">
<el-select v-model="searchData.DoctorUserId" clearable style="width:120px;">
<el-option v-for="item of DoctorUserList" :key="'DoctorUserId' + item.DoctorUserId" :value="item.DoctorUserId" :label="`${item.UserName}(${item.FullName})`" />
</el-select>
</el-form-item>
<!-- 阅片标准 -->
<el-form-item style="margin-bottom:10px" :label="$t('trials:pmMedicalReview:table:criterionName')">
<el-select v-model="searchData.TrialReadingCriterionId" clearable style="width:120px;">
<el-option v-for="item of trialCriterionList" :key="'TrialReadingCriterionId' + item.TrialReadingCriterionId" :value="item.TrialReadingCriterionId" :label="item.TrialReadingCriterionName" />
</el-select>
</el-form-item>
<!-- 是否加急 -->
<el-form-item style="margin-bottom:10px" :label="$t('trials:pmMedicalReview:table:isUrgent')">
<el-select v-model="searchData.IsUrgent" clearable style="width:120px;">
<el-option v-for="item of $d.YesOrNo" :key="'IsUrgent' + item.label" :value="item.value" :label="item.label" />
</el-select>
</el-form-item>
<!-- 阅片任务状态 -->
<el-form-item style="margin-bottom:10px" :label="$t('trials:pmMedicalReview:table:taskState')">
<el-select v-model="searchData.TaskState" clearable style="width:120px;">
<el-option v-for="item of $d.TaskState" :key="'TaskState' + item.label" :value="item.value" :label="item.label" />
</el-select>
</el-form-item>
<!-- 任务类型 -->
<el-form-item style="margin-bottom:10px" :label="$t('trials:pmMedicalReview:table:taskType')">
<el-select v-model="searchData.ReadingCategory" clearable style="width:120px;">
<el-option v-for="item of $d.ReadingCategory" :key="'ReadingCategory' + item.label" :value="item.value" :label="item.label" />
</el-select>
</el-form-item>
<!-- 阅片状态 -->
<!-- <el-form-item style="margin-bottom:10px" label="阅片状态">
<el-select v-model="searchData.TaskAllocationState" clearable style="width:120px;">
<el-option v-for="item of $d.TaskAllocationState" :key="'TaskAllocationState' + item.label" :value="item.value" :label="item.label" />
</el-select>
</el-form-item> -->
<!-- 审核状态 -->
<el-form-item style="margin-bottom:10px" :label="$t('trials:pmMedicalReview:table:auditState')">
<el-select v-model="searchData.AuditState" clearable style="width:120px;">
<el-option v-for="item of $d.MedicalReviewAuditState" :key="'AuditState' + item.label" :value="item.value" :label="item.label" />
</el-select>
</el-form-item>
<el-form-item style="margin-bottom:10px">
<el-button type="primary" icon="el-icon-search" @click="handleSearch">
{{ $t('common:button:search') }}
</el-button>
<el-button type="primary" icon="el-icon-refresh-left" @click="handleReset">
{{ $t('common:button:reset') }}
</el-button>
<!--导出-->
<el-button type="primary" icon="el-icon-download" @click="handleExport">
{{ $t('common:button:export') }}
</el-button>
<!-- 筛选 -->
<el-button type="primary" v-if="hasPermi(['trials:trials-panel:tab:pmMedicalFeedback:edit'])" @click="openFilter">
{{$t('trials:pmMedicalReview:button:select')}}
</el-button>
<!-- 设置为失效 -->
<el-button type="primary" v-if="hasPermi(['trials:trials-panel:tab:pmMedicalFeedback:edit'])" @click="setMedicalReviewInvalid()">
{{$t('trials:pmMedicalReview:button:setInvalid')}}
</el-button>
</el-form-item>
</el-form>
</template>
<template slot="main-container">
<el-table
v-adaptive="{bottomOffset:60}"
v-loading="loading"
:data="list"
stripe
height="100"
@sort-change="handleSortChange"
@selection-change="handleListSelectChange"
>
<el-table-column type="selection" :selectable="MedicalReviewSelectable" align="left" width="45" />
<!-- 是否加急 -->
<el-table-column
prop="IsUrgent"
:label="$t('trials:pmMedicalReview:table:isUrgent')"
show-overflow-tooltip
width="120"
sortable="custom"
>
<template slot-scope="scope">
<el-tag v-if="scope.row.IsUrgent" type="danger">{{ $fd('YesOrNo', scope.row.IsUrgent) }}</el-tag>
<el-tag v-else type="primary">{{ $fd('YesOrNo', scope.row.IsUrgent) }}</el-tag>
</template>
</el-table-column>
<!-- 任务编号 -->
<el-table-column
prop="TaskCode"
:label="$t('trials:pmMedicalReview:table:taskCode')"
width="120"
sortable="custom"
show-overflow-tooltip
/>
<!-- 中心编号 -->
<el-table-column
prop="TrialSiteCode"
:label="$t('trials:pmMedicalReview:table:siteCode')"
width="100"
sortable="custom"
show-overflow-tooltip
/>
<!-- 受试者编号 -->
<el-table-column
prop="SubjectCode"
:label="$t('trials:pmMedicalReview:table:subjectCode')"
width="120"
sortable="custom"
show-overflow-tooltip
/>
<!-- 任务名称 -->
<el-table-column
prop="VisitTaskNum"
:label="$t('trials:pmMedicalReview:table:taskName')"
width="130"
sortable="custom"
show-overflow-tooltip
>
<template slot-scope="scope">
{{scope.row.TaskName}}
</template>
</el-table-column>
<!-- 盲态任务标识 -->
<el-table-column
prop="TaskBlindName"
:label="$t('trials:pmMedicalReview:table:blindName')"
width="160"
sortable="custom"
show-overflow-tooltip
/>
<!-- 任务类型 -->
<el-table-column
prop="ReadingCategory"
:label="$t('trials:pmMedicalReview:table:taskType')"
width="140"
sortable="custom"
show-overflow-tooltip
>
<template slot-scope="scope">
<el-tag v-if="scope.row.ReadingCategory === 1" type="primary">{{ $fd('ReadingCategory', scope.row.ReadingCategory) }}</el-tag>
<el-tag v-if="scope.row.ReadingCategory === 2" type="info">{{ $fd('ReadingCategory', scope.row.ReadingCategory) }}</el-tag>
<el-tag v-if="scope.row.ReadingCategory === 4" type="danger">{{ $fd('ReadingCategory', scope.row.ReadingCategory) }}</el-tag>
<el-tag v-if="scope.row.ReadingCategory === 5" type="warning">{{ $fd('ReadingCategory', scope.row.ReadingCategory) }}</el-tag>
</template>
</el-table-column>
<!-- 阅片任务状态 -->
<el-table-column
prop="TaskState"
:label="$t('trials:pmMedicalReview:table:taskState')"
width="160"
sortable="custom"
show-overflow-tooltip
>
<template slot-scope="scope">
<el-tag v-if="scope.row.TaskState === 0" type="primary">{{ $fd('TaskState', scope.row.TaskState) }}</el-tag>
<el-tag v-if="scope.row.TaskState === 1" type="info">{{ $fd('TaskState', scope.row.TaskState) }}</el-tag>
<el-tag v-if="scope.row.TaskState === 3" type="danger">{{ $fd('TaskState', scope.row.TaskState) }}</el-tag>
<el-tag v-if="scope.row.TaskState === 4" type="warning">{{ $fd('TaskState', scope.row.TaskState) }}</el-tag>
<el-tag v-if="scope.row.TaskState === 5" type="danger">{{ $fd('TaskState', scope.row.TaskState) }}</el-tag>
</template>
</el-table-column>
<!-- 角色 -->
<el-table-column
prop="ArmEnum"
:label="$t('trials:pmMedicalReview:table:role')"
width="100"
sortable="custom"
show-overflow-tooltip
>
<template slot-scope="scope">
<el-tag v-if="scope.row.ArmEnum === 1" type="primary">{{$fd('ArmEnum', scope.row.ArmEnum)}}</el-tag>
<el-tag v-if="scope.row.ArmEnum === 2" type="success">{{$fd('ArmEnum', scope.row.ArmEnum)}}</el-tag>
<el-tag v-if="scope.row.ArmEnum === 3" type="info">{{$fd('ArmEnum', scope.row.ArmEnum)}}</el-tag>
<el-tag v-if="scope.row.ArmEnum === 4" type="danger">{{$fd('ArmEnum', scope.row.ArmEnum)}}</el-tag>
<el-tag v-if="scope.row.ArmEnum === 5" type="warning">{{$fd('ArmEnum', scope.row.ArmEnum)}}</el-tag>
</template>
</el-table-column>
<!-- 阅片人 -->
<el-table-column
prop="DoctorUser.UserName"
:label="$t('trials:pmMedicalReview:table:reader')"
width="120"
sortable="custom"
show-overflow-tooltip
>
<template slot-scope="scope" v-if="scope.row.DoctorUser.UserName">
{{scope.row.DoctorUser.UserName}}({{scope.row.DoctorUser.FullName}})
</template>
</el-table-column>
<!-- 阅片标准 -->
<el-table-column
prop="TrialReadingCriterionName"
:label="$t('trials:pmMedicalReview:table:criterionName')"
width="180"
sortable="custom"
show-overflow-tooltip
/>
<!-- 阅片完成时间 -->
<el-table-column
prop="SignTime"
:label="$t('trials:pmMedicalReview:table:signTime')"
width="210"
sortable="custom"
show-overflow-tooltip
/>
<!-- 医学经理 -->
<el-table-column
prop="MedicalManagerUser.UserName"
:label="$t('trials:pmMedicalReview:table:mim')"
width="120"
sortable="custom"
show-overflow-tooltip
>
<template slot-scope="scope" v-if="scope.row.MedicalManagerUser">
{{scope.row.MedicalManagerUser.UserName}}({{scope.row.MedicalManagerUser.FullName}})
</template>
</el-table-column>
<!-- 审核分配时间 -->
<el-table-column
prop="AllocateTime"
:label="$t('trials:pmMedicalReview:table:allocateTime')"
width="180"
sortable="custom"
show-overflow-tooltip
>
</el-table-column>
<!-- 任务状态 -->
<el-table-column
prop="IsInvalid"
width="120"
:label="$t('trials:pmMedicalReview:table:isInvalid')"
sortable="custom"
show-overflow-tooltip
>
<template slot-scope="scope">
<el-tag v-if="scope.row.IsInvalid" type="danger">
{{$fd('IsTakeEffect', scope.row.IsInvalid)}}
</el-tag>
<el-tag v-else type="primary">
{{$fd('IsTakeEffect', scope.row.IsInvalid)}}
</el-tag>
</template>
</el-table-column>
<!-- 审核状态 -->
<el-table-column
prop="AuditState"
:label="$t('trials:pmMedicalReview:table:auditState')"
width="100"
sortable="custom"
show-overflow-tooltip
>
<template slot-scope="scope">
<el-tag type="warning" v-if="scope.row.AuditState === 0">{{$fd('MedicalReviewAuditState', scope.row.AuditState)}}</el-tag>
<el-tag type="primary" v-if="scope.row.AuditState === 1">{{$fd('MedicalReviewAuditState', scope.row.AuditState)}}</el-tag>
<el-tag type="danger" v-if="scope.row.AuditState === 2">{{$fd('MedicalReviewAuditState', scope.row.AuditState)}}</el-tag>
</template>
</el-table-column>
<!-- 审核建议 -->
<el-table-column
prop="AuditAdviceEnum"
:label="$t('trials:pmMedicalReview:table:auditAdvice')"
width="120"
sortable="custom"
show-overflow-tooltip
>
<template slot-scope="scope">
{{$fd('AuditAdvice', scope.row.AuditAdviceEnum)}}
</template>
</el-table-column>
<!-- 阅片人是否认同 -->
<el-table-column
prop="DoctorUserIdeaEnum"
:label="$t('trials:pmMedicalReview:table:whetherAgree')"
width="180"
sortable="custom"
show-overflow-tooltip
>
<template slot-scope="scope">
<el-tag type="primary" v-if="scope.row.DoctorUserIdeaEnum === 1"> {{$fd('MedicalReviewDoctorUserIdea', scope.row.DoctorUserIdeaEnum)}} </el-tag>
<el-tag type="danger" v-if="scope.row.DoctorUserIdeaEnum === 2"> {{$fd('MedicalReviewDoctorUserIdea', scope.row.DoctorUserIdeaEnum)}} </el-tag>
</template>
</el-table-column>
<!-- 审核签名时间 -->
<el-table-column
prop="AuditSignTime"
:label="$t('trials:pmMedicalReview:table:auditSignTime')"
width="140"
sortable="custom"
show-overflow-tooltip
/>
<el-table-column
:label="$t('common:action:action')"
width="240"
fixed="right"
>
<template slot-scope="scope">
<!-- 查看 -->
<el-button
circle
:title="$t('trials:pmMedicalReview:action:view')"
:disabled="scope.row.IsInvalid || scope.row.AuditState !== 2"
icon="el-icon-view"
@click="handleReadMecialAudit(scope.row)"
/>
<!-- 设置为失效 -->
<el-button
circle
:title="$t('trials:pmMedicalReview:action:failure')"
:disabled="scope.row.IsInvalid || !!scope.row.SignTime"
icon="el-icon-close"
@click="setMedicalReviewInvalid(scope.row)"
/>
<el-button
circle
:title="$t('trials:pmMedicalReview:action:assign')"
:disabled="!!scope.row.MedicalManagerUser || scope.row.IsInvalid || !!scope.row.SignTime"
icon="el-icon-thumb"
@click="assignMedicalReviewTask(scope.row, 1)"
/>
<el-button
circle
:title="$t('trials:pmMedicalReview:action:cancelAssign')"
:disabled="!scope.row.MedicalManagerUser || scope.row.IsInvalid || !!scope.row.SignTime"
icon="el-icon-refresh-left"
@click="assignMedicalReviewTask(scope.row, 3)"
/>
</template>
</el-table-column>
</el-table>
<!-- 分页组件 -->
<pagination class="page" :total="total" :page.sync="searchData.PageIndex" :limit.sync="searchData.PageSize" @pagination="getList" />
<!-- 筛选 -->
<el-dialog
v-if="FilterVisible"
:title="$t('trials:pmMedicalReview:button:select')"
:visible.sync="FilterVisible"
width="1200px"
:fullscreen="true"
append-to-body
:close-on-click-modal="false"
custom-class="base-dialog-wrapper"
>
<GenerateMedicalReviewTaskList :trial-criterion-list="trialCriterionList" @getList="getList"></GenerateMedicalReviewTaskList>
</el-dialog>
<!-- <el-dialog
v-if="AddOrEditFilterVisible"
:title="title"
:visible.sync="AddOrEditFilterVisible"
width="500px"
:close-on-click-modal="false"
>
<AddOrEditFilter :data="rowData" :doctor-user-list="DoctorUserList" @close="() => {AddOrEditFilterVisible = false}" @getList="openFilter" />
</el-dialog> -->
<!-- 分配医学经理 -->
<el-dialog
v-if="AssignMedicalReviewTaskVisible"
:title="$t('trials:pmMedicalReview:allocation:aliocationMIM')"
:visible.sync="AssignMedicalReviewTaskVisible"
width="500px"
:close-on-click-modal="false"
>
<ReAssignMedicalReviewTask :data="rowData" :select-list="SelectList" :doctor-user-list="MIMOptions" @close="() => {AssignMedicalReviewTaskVisible = false}" @getList="getList" />
</el-dialog>
<!-- 医学审核详情 -->
<el-dialog
v-if="auditVisible"
:visible.sync="auditVisible"
:close-on-click-modal="false"
:fullscreen="true"
:title="$t('trials:pmMedicalReview:dialog:medicalReview')"
>
<MedicalAudit :visit-task-id="currentRow.VisitTaskId" :task-medical-review-id="currentRow.Id" @getList="getList" @close="auditVisible=false" :rowData="currentRow"/>
</el-dialog>
</template>
</BaseContainer>
</template>
<script>
import { getTrialCriterionList, setMedicalReviewInvalid, assignMedicalReviewTask, getTrialSiteCodeSelect, getMedicalReviewTaskList, getDoctorUserSelectList, getMIMUserList, generateMedicalReviewTask, deleteTaskMedicalReviewRule } from '@/api/trials/reading'
import { getMedicalReviewTaskList_Export } from '@/api/export'
import BaseContainer from '@/components/BaseContainer'
import Pagination from '@/components/Pagination'
import AddOrEditFilter from './components/AddOrEditFilter'
import AssignMedicalReviewTask from './components/AssignMedicalReviewTask'
import ReAssignMedicalReviewTask from './components/ReAssignMedicalReviewTask'
import GenerateMedicalReviewTaskList from './components/GenerateMedicalReviewTaskList'
import MedicalAudit from '@/views/trials/trials-panel/reading/mim-medical-audit/components/MedicalAudit'
const searchDataDefault = () => {
return {
PageIndex: 1,
PageSize: 20,
TrialSiteCode: null,
Asc: true,
SortField: '',
TrialId: null,
SiteId: null,
SubjectId: null,
SubjectCode: null,
IsUrgent: null,
TaskName: null,
TaskState: null,
DoctorUserId: null,
ReadingCategory: null,
TaskAllocationState: null,
BeginAllocateDate: null,
EndAllocateDate: null,
AuditState: null,
TrialReadingCriterionId: null
}
}
export default {
name: 'TrialsNotice',
components: { BaseContainer, Pagination, AddOrEditFilter, AssignMedicalReviewTask, GenerateMedicalReviewTaskList, MedicalAudit, ReAssignMedicalReviewTask },
data() {
return {
auditVisible: null,
searchData: searchDataDefault(),
maxLength: 0,
list: [],
siteOptions: [],
total: 0,
loading: false,
FilterVisible: false,
AssignMedicalReviewTaskVisible: false,
AddOrEditFilterVisible: false,
trialId: this.$route.query.trialId,
currentData: {},
param: {},
DoctorUserList: [],
btnLoading: false,
rowData: {},
OtherInfo: null,
MIMOptions: [],
FilterList: [],
title: null,
SelectList: [],
SelectMedicalList: [],
distributionInfo: {},
currentRow: {},
trialCriterionList:[]
}
},
mounted() {
this.getList()
this.getSite()
this.getDoctorUserSelectList()
this.getMIMUserList()
this.getTrialCriterionList()
},
methods: {
handleExport() {
getMedicalReviewTaskList_Export(this.searchData).then(res => {
}).catch(() => { this.loading = false })
},
getTrialCriterionList() {
getTrialCriterionList(this.trialId).then(res => {
this.trialCriterionList = res.Result
}).catch(() => {})
},
MedicalReviewSelectable(row) {
if (row.IsInvalid || row.SignTime) {
return false
} else {
return true
}
},
handleListSelectChange(e) {
this.SelectMedicalList = e
},
setMedicalReviewInvalid(row) {
var params = {}
if (row) {
params.MedicalReviewIdList = [row.Id]
} else {
if (this.SelectMedicalList.length === 0) {
// '请先勾选医学审核任务'
this.$alert(this.$t('trials:pmMedicalReview:message:msg01'))
return
}
params.MedicalReviewIdList = this.SelectMedicalList.map(v => v.Id)
}
// 确定将该医学审核任务设置为失效吗?
this.$confirm(this.$t('trials:pmMedicalReview:message:msg02')).then(() => {
this.loading = true
params.TrialId = this.trialId
setMedicalReviewInvalid(params).then(res => {
// '设置成功'
this.$message.success(this.$t('trials:pmMedicalReview:message:msg03'))
this.getList()
this.loading = false
}).catch(() => {
this.loading = false
})
})
},
openAssignMedicalReviewTask() {
this.rowData = {}
if (this.SelectList.length === 0) {
// 请先选择需要分配的医学审核任务
this.$alert(this.$t('trials:pmMedicalReview:message:msg04'))
return
}
this.AssignMedicalReviewTaskVisible = true
},
handleReadMecialAudit(row) {
this.currentRow = { ...row }
this.auditVisible = true
},
assignMedicalReviewTask(row, type) {
var params = {
IdList: [
row.Id
],
taskOptType: type
}
if (type === 3) {
// '确定要取消分配吗?'
this.$confirm(this.$t('trials:pmMedicalReview:message:msg05')).then(() => {
this.loading = true
this.btnLoading = true
assignMedicalReviewTask(params).then(() => {
// 取消分配成功
this.$message.success(this.$t('trials:pmMedicalReview:message:msg06'))
this.getList()
this.loading = false
this.btnLoading = false
}).catch(() => {
this.loading = false
this.btnLoading = false
})
})
} else {
this.rowData = { ...row }
this.AssignMedicalReviewTaskVisible = true
}
},
selectable(row) {
if (row.MedicalManagerUserId) {
return false
} else {
return true
}
},
generateMedicalReviewTask() {
this.$confirm('确定要应用吗?').then(() => {
this.loading = true
this.btnLoading = true
generateMedicalReviewTask({
trialId: this.trialId
}).then(res => {
this.$message.success('应用成功')
this.loading = false
this.getList()
this.btnLoading = false
this.FilterVisible = false
}).catch(() => {
this.loading = false
this.btnLoading = false
})
})
},
addOrEdit(row) {
if (row) {
this.rowData = { ...row }
this.title = '编辑'
} else {
this.rowData = {}
this.title = '新增'
}
this.AddOrEditFilterVisible = true
},
openFilter() {
this.FilterVisible = true
this.AddOrEditFilterVisible = false
},
getDoctorUserSelectList() {
getDoctorUserSelectList(this.$route.query.trialId).then(res => {
this.DoctorUserList = res.Result
if (this.DoctorUserList.length > 0) {
this.ReadingType = this.DoctorUserList[0].ReadingType
} else {
}
})
},
getList() {
this.searchData.TrialId = this.$route.query.trialId
this.loading = true
getMedicalReviewTaskList(this.searchData).then(res => {
this.loading = false
this.OtherInfo = res.OtherInfo
this.list = res.Result.CurrentPageData
this.total = res.Result.TotalCount
this.AssignMedicalReviewTaskVisible = false
}).catch(() => { this.loading = false })
},
handleSearch() {
this.searchData.PageIndex = 1
this.getList()
},
handleReset() {
this.searchData = searchDataDefault()
this.getList()
},
getSite() {
getTrialSiteCodeSelect(this.trialId).then(res => {
this.siteOptions = res.Result
})
},
getMIMUserList() {
getMIMUserList(this.trialId).then(res => {
this.MIMOptions = res.Result
})
},
handleDelete(row) {
this.$confirm(`确定要删除${row.DoctorUser.UserName}(${row.DoctorUser.FullName})的核查计划吗?`, {
type: 'warning',
distinguishCancelAndClose: true
}).then(() => {
this.loading = true
deleteTaskMedicalReviewRule(row.Id).then(res => {
this.loading = false
this.$message.success('删除成功')
this.openFilter()
}).catch(() => { this.loading = false })
})
},
// 排序
handleSortChange(column) {
if (column.order === 'ascending') {
this.searchData.Asc = true
} else {
this.searchData.Asc = false
}
this.searchData.SortField = column.prop
this.searchData.PageIndex = 1
this.getList()
}
}
}
</script>
<style lang="scss" scoped>
>>>.hidden-row{
display: none;
}
>>>.el-dialog__body {
padding: 15px 20px;
}
>>>.el-tag--danger.el-tag--dark {
// background-color: #f56c6c!important;
border-color: none!important;
// color: #fff!important;
}
>>>#TaskAllocationRuleList thead .el-checkbox__inner{
display: none;
}
>>>.el-descriptions-item__label.has-colon:after{
display: none;
}
</style>

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,403 @@
<template>
<BaseContainer>
<!-- 搜索框 -->
<template slot="search-container">
<el-form :inline="true">
<!-- 中心编号 -->
<el-form-item :label="$t('trials:researchStaff:table:siteId')">
<el-select v-model="searchData.SiteId" clearable filterable style="width:120px;">
<el-option
v-for="(item,index) of siteOptions"
:key="index"
:label="item.TrialSiteCode"
:value="item.SiteId"
/>
</el-select>
</el-form-item>
<!-- 姓名 -->
<el-form-item :label="$t('trials:researchStaff:table:Name')">
<el-input v-model="searchData.UserName" class="mr" clearable style="width:120px;" />
</el-form-item>
<!-- 角色 -->
<el-form-item :label="$t('trials:researchStaff:table:role')">
<el-select
v-model="searchData.TrialRoleNameId"
style="width:120px;"
clearable
>
<el-option
v-for="item of dict.type.SiteSurvey_UserRoles"
:key="item.value"
:label="item.label"
:value="item.value"
/>
</el-select>
</el-form-item>
<!-- 单位 -->
<el-form-item :label="$t('trials:researchStaff:table:organization')">
<el-input v-model="searchData.OrganizationName" class="mr" clearable style="width:120px;" />
</el-form-item>
<!-- 是否生成账号 -->
<el-form-item :label="$t('trials:researchStaff:table:isGenerateAccount')">
<el-select
v-model="searchData.IsGenerateAccount"
style="width:120px;"
clearable
>
<el-option v-for="item of $d.YesOrNo" :key="`IsGenerateAccount${item.value}`" :label="item.label" :value="item.value" />
</el-select>
</el-form-item>
<!-- 用户类型 -->
<el-form-item :label="$t('trials:researchStaff:table:userType')">
<el-select
v-model="searchData.UserTypeId"
style="width:120px;"
clearable
>
<el-option
v-for="item of userTypeOptions"
:key="item.Id"
:label="item.UserTypeShortName"
:value="item.Id"
>
<span>{{ item.UserType }}</span>
</el-option>
</el-select>
</el-form-item>
<!-- 状态 -->
<!-- <el-form-item :label="$t('trials:researchStaff:table:status')">
<el-select v-model="searchData.State" clearable filterable style="width:120px;">
<el-option
v-for="(item,index) of researchUserState"
:key="index"
:label="item.label"
:value="item.value"
/>
</el-select>
</el-form-item> -->
<el-form-item>
<!-- 查询 -->
<el-button type="primary" icon="el-icon-search" @click="handleSearch">
{{ $t('common:button:search') }}
</el-button>
</el-form-item>
<!-- 重置 -->
<el-form-item>
<el-button type="primary" icon="el-icon-refresh-left" @click="handleReset">
{{ $t('common:button:reset') }}
</el-button>
</el-form-item>
<!-- 发邮件 -->
<el-form-item>
<el-button
v-hasPermi="['trials:trials-panel:attachments:site-research:summary-record:sendEmail']"
type="primary"
icon="el-icon-message"
:loading="sendEmailLoading"
:disabled="selectArr.length === 0"
@click="handleSendEmail"
>
{{ $t('trials:researchRecord:dialogButton:sendEmail') }}
</el-button>
</el-form-item>
<!-- 导出 -->
<el-form-item>
<el-button
type="primary"
icon="el-icon-download"
:disabled="list.length === 0"
@click="handleExport"
>
{{ $t('common:button:export') }}
</el-button>
</el-form-item>
</el-form>
</template>
<template slot="main-container">
<!-- 系统文件列表 -->
<el-table
ref="researchUsers"
v-loading="loading"
v-adaptive="{bottomOffset:70}"
:data="list"
stripe
height="100"
@selection-change="handleSelectChange"
@sort-change="handleSortByColumn"
>
<el-table-column
type="selection"
width="50"
:selectable="handleSelectTable"
/>
<el-table-column type="index" width="50" />
<!-- 中心编号 -->
<el-table-column
prop="TrialSiteSurvey.TrialSiteCode"
:label="$t('trials:researchStaff:table:siteId')"
show-overflow-tooltip
sortable="custom"
min-width="100"
>
<template slot-scope="scope">{{ `${scope.row.TrialSiteSurvey.TrialSiteCode}` }}</template>
</el-table-column>
<!-- 中心名称 -->
<el-table-column
prop="TrialSiteSurvey.SiteName"
:label="$t('trials:researchStaff:table:siteName')"
show-overflow-tooltip
sortable="custom"
min-width="120"
>
<template slot-scope="scope">{{ `${scope.row.TrialSiteSurvey.SiteName}` }}</template>
</el-table-column>
<!-- 姓名 -->
<el-table-column
prop="LastName"
:label="$t('trials:researchStaff:table:Name')"
show-overflow-tooltip
sortable="custom"
min-width="100"
>
<template slot-scope="scope">{{ `${scope.row.LastName} / ${scope.row.FirstName}` }}</template>
</el-table-column>
<!-- 角色 -->
<el-table-column
prop="TrialRoleName"
:label="$t('trials:researchStaff:table:role')"
show-overflow-tooltip
sortable="custom"
min-width="100"
/>
<!-- 电话号码 -->
<el-table-column
prop="Phone"
:label="$t('trials:researchStaff:table:phone')"
show-overflow-tooltip
sortable="custom"
min-width="120"
/>
<!-- 邮箱 -->
<el-table-column
prop="Email"
:label="$t('trials:researchStaff:table:email')"
show-overflow-tooltip
sortable="custom"
min-width="150"
/>
<!-- 单位 -->
<el-table-column
prop="OrganizationName"
:label="$t('trials:researchStaff:table:organization')"
show-overflow-tooltip
sortable="custom"
min-width="100"
/>
<!-- 是否生成账号 -->
<el-table-column
prop="IsGenerateAccount"
:label="$t('trials:researchStaff:table:isGenerateAccount')"
show-overflow-tooltip
sortable="custom"
min-width="130"
>
<template slot-scope="scope">
{{ $fd('YesOrNo', scope.row.IsGenerateAccount) }}
</template>
</el-table-column>
<!-- <el-table-column-->
<!-- prop="State"-->
<!-- :label="$t('trials:researchStaff:table:status')"-->
<!-- show-overflow-tooltip-->
<!-- sortable="custom"-->
<!-- min-width="130"-->
<!-- >-->
<!-- <template slot-scope="scope">-->
<!-- {{ $fd('researchUserState', scope.row.State) }}-->
<!-- </template>-->
<!-- </el-table-column>-->
<!-- 用户类型 -->
<el-table-column
prop="UserType"
:label="$t('trials:researchStaff:table:userType')"
show-overflow-tooltip
sortable="custom"
min-width="100"
/>
<!-- 是否加入 -->
<el-table-column
prop="IsJoin"
:label="$t('trials:researchStaff:table:isJoin')"
show-overflow-tooltip
sortable="custom"
min-width="100"
>
<template slot-scope="scope">
<el-tag v-if="scope.row.IsJoin" type="info">{{ $fd('IsJoin', scope.row.IsJoin) }}</el-tag>
<el-tag v-else type="danger">{{ $fd('IsJoin', scope.row.IsJoin) }}</el-tag>
</template>
</el-table-column>
</el-table>
<!-- 分页组件 -->
<pagination class="page" :total="total" :page.sync="searchData.PageIndex" :limit.sync="searchData.PageSize" @pagination="getList" />
</template>
</BaseContainer>
</template>
<script>
import { trialSiteSurveyUserList, getTrialSiteSelect, getUserTypeList, trialSiteUserSummaryListExport, sendSiteSurveyUserJoinEmail } from '@/api/trials'
import BaseContainer from '@/components/BaseContainer'
import Pagination from '@/components/Pagination'
const searchDataDefault = () => {
return {
SortField: '',
Asc: true,
PageIndex: 1,
PageSize: 20,
SiteId: '',
UserTypeId: '',
IsGenerateAccount: null,
TrialRoleNameId: '',
UserName: '',
OrganizationName: '',
FormWriterKeyInfo: '',
State: ''
}
}
export default {
name: 'SiteResearchList',
components: { BaseContainer, Pagination },
dicts: ['SiteSurvey_UserRoles'],
data() {
return {
searchData: searchDataDefault(),
loading: false,
list: [],
total: 0,
trialId: this.$route.query.trialId,
siteOptions: [],
userTypeOptions: [],
selectArr: [],
sendEmailLoading: false
// researchUserState: [
// { label: '未邀请', value: 0 },
// { label: '已邀请', value: 1 },
// { label: '已确认', value: 2 },
// { label: '已拒绝', value: 3 },
// { label: '超时', value: 4 }
// ]
}
},
mounted() {
this.getList()
this.getSite()
this.getUserType()
},
methods: {
// 获取系统文件数据
getList() {
this.loading = true
this.searchData.TrialId = this.trialId
trialSiteSurveyUserList(this.searchData).then(res => {
this.loading = false
this.list = res.Result.CurrentPageData
this.total = res.Result.TotalCount
}).catch(() => {
this.loading = false
})
},
// 获取调查表用户已勾选人员信息
handleSelectChange(val) {
this.selectArr = val
},
// handleSelectable(row) {
// // 已超时||已拒绝
// // if (row.State === 3 || row.State === 4) {
// // return true
// // } else {
// // return false
// // }
// return true
// },
// 发送邮件
handleSendEmail() {
this.sendEmailLoading = true
const param = {
TrialId: this.trialId,
TrialSiteSurveyId: this.selectArr[0].TrialSiteSurveyId,
BaseUrl: `${location.protocol}//${location.host}/login`,
RouteUrl: `${location.protocol}//${location.host}/email-recompose`,
UserList: this.selectArr
}
sendSiteSurveyUserJoinEmail(param).then(() => {
this.sendEmailLoading = false
// 发送成功
this.$message.success(this.$t('trials:researchStaff:message:sendEmalil'))
this.getList()
}).catch(() => { this.sendEmailLoading = false })
// var joinUrl = `${location.protocol}//${location.host}/#/joinVerify`
// var sendEmailUserArr = this.selectArr.map((v) => { return { Id: v.Id, Email: v.Email, IsSystemUser: v.IsSystemUser, SystemUserId: v.SystemUserId } })
// sendInviteEmail({
// TrialId: this.trialId,
// SendUsers: sendEmailUserArr,
// BaseUrl: BaseUrl,
// RouteUrl: joinUrl
// }).then(() => {
// this.sendEmailLoading = false
// this.$message.success('Saved successfully!')
// this.getResearchUserList()
// }).catch(() => { this.sendEmailLoading = false })
},
// 导出
handleExport() {
this.searchData.TrialId = this.trialId
this.loading = true
// eslint-disable-next-line no-unused-vars
const { SortField, Asc, PageIndex, PageSize, ...param } = { ...this.searchData }
trialSiteUserSummaryListExport({ ...param }).then(data => {
this.loading = false
}).catch(() => { this.loading = false })
},
// 获取site下拉框数据
getSite() {
getTrialSiteSelect(this.trialId).then(res => {
this.siteOptions = res.Result
})
},
// 获取系统用户类型
getUserType() {
getUserTypeList(3).then(res => {
this.userTypeOptions = res.Result
})
},
handleSelectTable(row) {
return row.IsGenerateSuccess && !row.IsJoin
},
// 重置
handleReset() {
this.searchData = searchDataDefault()
this.getList()
this.$nextTick(() => {
this.$refs.researchUsers.clearSort()
})
},
// 查询
handleSearch() {
this.getList()
},
// 排序
handleSortByColumn(column) {
if (column.order === 'ascending') {
this.searchData.Asc = true
} else {
this.searchData.Asc = false
}
this.searchData.SortField = column.prop
this.getList()
}
}
}
</script>

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,689 @@
/* eslint-disable */
<template>
<BaseContainer>
<template slot="search-container">
<el-form :inline="true">
<el-form-item :label="$t('trials:consistencyCheck:table:siteId')">
<el-select v-model="searchData.SiteId" clearable filterable style="width:120px;">
<el-option
v-for="(item,index) of siteOptions"
:key="index"
:label="item.TrialSiteCode"
:value="item.SiteId"
/>
</el-select>
</el-form-item>
<!-- 受试者编号 -->
<el-form-item :label="$t('trials:consistencyCheck:table:subjectId')">
<el-input v-model="searchData.SubjectCode" style="width:100px;" />
</el-form-item>
<el-form-item style="margin-bottom:10px" label="访视/阅片期名称">
<el-input
v-model="searchData.TaskName"
style="width:100px;"
clearable
/>
</el-form-item>
<el-form-item style="margin-bottom:10px" label="阅片人">
<el-select v-model="searchData.DoctorUserId" clearable style="width:120px;">
<el-option v-for="item of DoctorUserList" :key="'DoctorUserId' + item.DoctorUserId" :value="item.DoctorUserId" :label="item.UserName" />
</el-select>
</el-form-item>
<el-form-item style="margin-bottom:10px" label="是否加急">
<el-select v-model="searchData.IsUrgent" clearable style="width:120px;">
<el-option v-for="item of $d.YesOrNo" :key="'IsUrgent' + item.label" :value="item.value" :label="item.label" />
</el-select>
</el-form-item>
<el-form-item style="margin-bottom:10px" label="任务类型">
<el-select v-model="searchData.ReadingCategory" clearable style="width:120px;">
<el-option v-for="item of $d.ReadingCategory" :key="'ReadingCategory' + item.label" :value="item.value" :label="item.label" />
</el-select>
</el-form-item>
<el-form-item style="margin-bottom:10px" label="分配状态">
<el-select v-model="searchData.TaskAllocationState" clearable style="width:120px;">
<el-option v-for="item of $d.TaskAllocationState" :key="'TaskAllocationState' + item.label" :value="item.value" :label="item.label" />
</el-select>
</el-form-item>
<el-form-item style="margin-bottom:10px" label="分配时间">
<el-date-picker
v-model="timeList"
value-format="yyyy-MM-dd HH:mm:ss"
type="datetimerange"
range-separator="至"
start-placeholder="开始日期"
end-placeholder="结束日期"
@change="changeTimeList"
/>
</el-form-item>
<el-form-item style="margin-bottom:10px">
<el-button type="primary" icon="el-icon-search" @click="handleSearch">
{{ $t('common:button:search') }}
</el-button>
<el-button type="primary" icon="el-icon-refresh-left" @click="handleReset">
{{ $t('common:button:reset') }}
</el-button>
<el-button type="primary" @click="openTaskConsistentRule(true)">
自身一致性分析
</el-button>
<el-button type="primary" @click="openTaskConsistentRule(false)">
组间一致性分析
</el-button>
</el-form-item>
</el-form>
</template>
<template slot="main-container">
<el-table
v-adaptive="{bottomOffset:60}"
v-loading="loading"
:data="list"
stripe
height="100"
@sort-change="handleSortChange"
>
<el-table-column
prop="IsUrgent"
:label="$t('trials:consistencyCheck:table:isUrgent')"
show-overflow-tooltip
min-width="100"
sortable="custom"
>
<template slot-scope="scope">
<el-tag v-if="scope.row.IsUrgent" type="danger">{{ $fd('YesOrNo', scope.row.IsUrgent) }}</el-tag>
<el-tag v-else type="primary">{{ $fd('YesOrNo', scope.row.IsUrgent) }}</el-tag>
</template>
</el-table-column>
<el-table-column
prop="TaskCode"
label="任务编号"
min-width="100"
sortable="custom"
show-overflow-tooltip
/>
<el-table-column
prop="TrialSiteCode"
label="中心编号"
min-width="100"
sortable="custom"
show-overflow-tooltip
/>
<el-table-column
prop="SubjectCode"
label="受试者编号"
min-width="120"
sortable="custom"
show-overflow-tooltip
/>
<el-table-column
prop="TaskName"
label="访视/阅片期名称"
min-width="160"
sortable="custom"
show-overflow-tooltip
/>
<el-table-column
prop="TaskBlindName"
label="访视盲态名称"
min-width="140"
sortable="custom"
show-overflow-tooltip
/>
<el-table-column
prop="ReadingCategory"
label="阅片类别"
min-width="100"
sortable="custom"
show-overflow-tooltip
>
<template slot-scope="scope">
<el-tag v-if="scope.row.ReadingCategory === 1" type="primary">{{ $fd('ReadingCategory', scope.row.ReadingCategory) }}</el-tag>
<el-tag v-if="scope.row.ReadingCategory === 2" type="info">{{ $fd('ReadingCategory', scope.row.ReadingCategory) }}</el-tag>
<el-tag v-if="scope.row.ReadingCategory === 4" type="danger">{{ $fd('ReadingCategory', scope.row.ReadingCategory) }}</el-tag>
<el-tag v-if="scope.row.ReadingCategory === 5" type="warning">{{ $fd('ReadingCategory', scope.row.ReadingCategory) }}</el-tag>
</template>
</el-table-column>
<el-table-column
prop="TaskBlindName"
label="一致性分析类别"
min-width="160"
sortable="custom"
show-overflow-tooltip
>
<template slot-scope="scope">
<el-tag v-if="scope.row.IsSelfAnalysis" type="primary">{{ $fd('IsSelfAnalysis', scope.row.IsSelfAnalysis) }}</el-tag>
<el-tag v-else type="warning">{{ $fd('IsSelfAnalysis', scope.row.IsSelfAnalysis) }}</el-tag>
</template>
</el-table-column>
<el-table-column
prop="role"
label="角色"
min-width="80"
show-overflow-tooltip
>
<template slot-scope="scope">
<el-tag v-if="scope.row.ArmEnum === 1" type="primary">{{$fd('ArmEnum', scope.row.ArmEnum)}}</el-tag>
<el-tag v-if="scope.row.ArmEnum === 2" type="success">{{$fd('ArmEnum', scope.row.ArmEnum)}}</el-tag>
<el-tag v-if="scope.row.ArmEnum === 3" type="info">{{$fd('ArmEnum', scope.row.ArmEnum)}}</el-tag>
<el-tag v-if="scope.row.ArmEnum === 4" type="danger">{{$fd('ArmEnum', scope.row.ArmEnum)}}</el-tag>
<el-tag v-if="scope.row.ArmEnum === 5" type="warning">{{$fd('ArmEnum', scope.row.ArmEnum)}}</el-tag>
</template>
</el-table-column>
<el-table-column
prop="DoctorUser.UserName"
label="阅片人"
min-width="100"
sortable="custom"
show-overflow-tooltip
>
<template slot-scope="scope">
{{scope.row.UserName}}({{scope.row.FullName}})
</template>
</el-table-column>
<el-table-column
prop="TaskState"
label="任务状态"
min-width="100"
sortable="custom"
show-overflow-tooltip
>
<template slot-scope="scope">
<el-tag v-if="scope.row.TaskState === 0" type="primary">{{ $fd('TaskState', scope.row.TaskState) }}</el-tag>
<el-tag v-if="scope.row.TaskState === 1" type="info">{{ $fd('TaskState', scope.row.TaskState) }}</el-tag>
<el-tag v-if="scope.row.TaskState === 3" type="danger">{{ $fd('TaskState', scope.row.TaskState) }}</el-tag>
<el-tag v-if="scope.row.TaskState === 4" type="warning">{{ $fd('TaskState', scope.row.TaskState) }}</el-tag>
</template>
</el-table-column>
<el-table-column
prop="AllocateTime"
label="分配时间"
min-width="140"
sortable="custom"
show-overflow-tooltip
>
</el-table-column>
<el-table-column
prop="SuggesteFinishedTime"
label="建议完成时间"
min-width="140"
sortable="custom"
show-overflow-tooltip
/>
<el-table-column
prop="ReadingTaskState"
label="阅片状态"
min-width="120"
sortable="custom"
show-overflow-tooltip
>
<template slot-scope="scope">
<el-tag v-if="scope.row.ReadingTaskState === 2" type="primary">{{ $fd('ReadingTaskState', scope.row.ReadingTaskState) }}</el-tag>
<el-tag v-else type="danger">{{ $fd('ReadingTaskState', scope.row.ReadingTaskState) }}</el-tag>
</template>
</el-table-column>
<el-table-column
prop="SignTime"
label="阅片完成时间"
min-width="140"
sortable="custom"
show-overflow-tooltip
/>
<el-table-column
prop="ReReadingApplyState"
label="申请状态"
min-width="140"
sortable="custom"
show-overflow-tooltip
>
<template slot-scope="scope">
<el-tag v-if="scope.row.ReReadingApplyState === 1" type="danger">{{ $fd('ReReadingApplyState', scope.row.ReReadingApplyState) }}</el-tag>
<el-tag v-if="scope.row.ReReadingApplyState === 2" type="primary">{{ $fd('ReReadingApplyState', scope.row.ReReadingApplyState) }}</el-tag>
<el-tag v-if="scope.row.ReReadingApplyState === 3" type="warning">{{ $fd('ReReadingApplyState', scope.row.ReReadingApplyState) }}</el-tag>
</template>
</el-table-column>
<el-table-column
fixed="right"
label="操作"
width="210"
>
<template slot-scope="scope">
<el-button
:disabled="scope.row.ReadingTaskState !== 2"
icon="el-icon-view"
circle
title="查看阅片结果"
@click="lookReadingResults(scope.row)"
/>
<el-button
:disabled="scope.row.TaskState !== 0 || scope.row.ReadingTaskState !== 2 || scope.row.ReReadingApplyState === 2 || scope.row.ReReadingApplyState === 1"
icon="el-icon-collection"
circle
title="申请重阅"
@click="openApplyReReading(scope.row)"
/>
<el-button
:disabled="!scope.row.ReReadingApplyState"
icon="el-icon-toilet-paper"
circle
title="退回重阅历史"
@click="reReadingHistory(scope.row, 1)"
/>
</template>
</el-table-column>
</el-table>
<!-- 分页组件 -->
<pagination class="page" :total="total" :page.sync="searchData.PageIndex" :limit.sync="searchData.PageSize" @pagination="getList" />
<el-dialog
v-if="ReaderRulesVisible"
:title="title"
:visible.sync="ReaderRulesVisible"
width="1200px"
append-to-body
:close-on-click-modal="false"
custom-class="base-dialog-wrapper"
>
<el-button size="small" type="primary" style="position: absolute;right: 20px;top: 60px;z-index: 100;" :loading="btnLoading" @click="addReaderRulesForm">
配置{{ title }}规则
</el-button>
<div class="base-dialog-body" style="margin-top: 32px;">
<el-table
v-loading="loading"
:data="TaskConsistentRuleList"
stripe
>
<el-table-column
prop="TaskCode"
label="阅片人"
min-width="140"
show-overflow-tooltip
>
<template slot-scope="scope" v-if="scope.row.AnalysisDoctorUser">
{{scope.row.AnalysisDoctorUser.UserName}}({{scope.row.AnalysisDoctorUser.FullName}})
</template>
</el-table-column>
<el-table-column
prop="PlanVisitCount"
label="访视数"
min-width="90"
show-overflow-tooltip
>
</el-table-column>
<el-table-column
prop="IntervalWeeks"
label="间隔周期"
min-width="100"
show-overflow-tooltip
v-if="IsSelfAnalysis"
>
</el-table-column>
<el-table-column
prop="IsHaveReadingPeriod"
label="所选访视是否有阅片期"
min-width="180"
show-overflow-tooltip
>
<template slot-scope="scope">
<el-tag v-if="scope.row.IsHaveReadingPeriod" size="success">{{$fd('YesOrNo', scope.row.IsHaveReadingPeriod)}}</el-tag>
<el-tag v-else size="danger">{{$fd('YesOrNo', scope.row.IsHaveReadingPeriod)}}</el-tag>
</template>
</el-table-column>
<el-table-column
prop="IsGenerateGlobalTask"
label="是否生成全局阅片任务"
min-width="180"
show-overflow-tooltip
>
<template slot-scope="scope">
<el-tag v-if="scope.row.IsGenerateGlobalTask" size="success">{{$fd('YesOrNo', scope.row.IsGenerateGlobalTask)}}</el-tag>
<el-tag v-else size="danger">{{$fd('YesOrNo', scope.row.IsGenerateGlobalTask)}}</el-tag>
</template>
</el-table-column>
<el-table-column
prop="PlanSubjectCount"
label="计划受试者数"
min-width="120"
show-overflow-tooltip
>
</el-table-column>
<el-table-column
prop="GeneratedSubjectCount"
label="已生成受试者数"
min-width="130"
show-overflow-tooltip
>
</el-table-column>
<el-table-column
fixed="right"
label="操作"
width="110"
>
<template slot-scope="scope">
<el-button
circle
icon="el-icon-s-check"
title="选择"
@click="openDoctorConsistentRuleSubjectTable(scope.row)"
/>
</template>
</el-table-column>
</el-table>
</div>
<div slot="footer" class="dialog-footer">
<!-- 取消 -->
<el-button :disabled="btnLoading" size="small" type="primary" @click="ReaderRulesVisible = false">
{{ $t('common:button:cancel') }}
</el-button>
<!-- 保存 -->
</div>
</el-dialog>
<el-dialog
v-if="ReaderRulesFormVisible"
:title=" `配置${title}规则`"
:visible.sync="ReaderRulesFormVisible"
width="480px"
:close-on-click-modal="false"
>
<ReaderRulesForm :data="rowData" :select-list="SelectList" :doctor-user-list="DoctorUserList" :is-self-analysis="IsSelfAnalysis" @close="() => {ReaderRulesFormVisible = false; rowData = {}}" @getList="openTaskConsistentRule(IsSelfAnalysis)" />
</el-dialog>
<el-dialog
v-if="DoctorConsistentRuleSubjectTableVisible"
title="选择受试者"
:visible.sync="DoctorConsistentRuleSubjectTableVisible"
width="1000px"
:close-on-click-modal="false"
>
<DoctorConsistentRuleSubjectTable :data="rowData" @close="() => {DoctorConsistentRuleSubjectTableVisible = false; ; rowData = {}}" @getList="openTaskConsistentRule(IsSelfAnalysis)" />
</el-dialog>
<el-dialog
v-if="ApplyforReasonVisible"
title="申请重阅"
:visible.sync="ApplyforReasonVisible"
width="600px"
append-to-body
:close-on-click-modal="false"
custom-class="base-dialog-wrapper"
>
<div style="padding:10px;border: 1px solid #e0e0e0;max-height:650px;overflow-y: auto;">
<el-form
ref="reasonForm"
:rules="rules"
:model="ApplyforReasonForm"
class="demo-ruleForm"
size="small"
label-width="120px"
>
<el-form-item
label="申请原因"
prop="Type"
:rules="[
{ required: true, message: $t('common:ruleMessage:select')},
]"
>
<el-radio-group v-model="ApplyforReasonForm.Type">
<!-- 问题已解决 -->
<el-radio :label="1">图像重传</el-radio>
<!-- 问题无法解决强制关闭质疑 -->
<el-radio :label="2">其他</el-radio>
</el-radio-group>
</el-form-item>
<!-- 原因 -->
<el-form-item v-if="ApplyforReasonForm.Type === 2" label="备注" prop="RequestReReadingReason">
<el-input
v-model="ApplyforReasonForm.RequestReReadingReason"
type="textarea"
:autosize="{ minRows: 2, maxRows: 4}"
:placeholder="$t('common:ruleMessage:specify')"
maxlength="500"
show-word-limit
/>
</el-form-item>
</el-form>
</div>
<div slot="footer" class="dialog-footer">
<!-- 取消 -->
<el-button :disabled="btnLoading" size="small" type="primary" @click="ApplyforReasonVisible = false;ApplyforReasonForm = { Type: null, RequestReReadingReason: null }">
{{ $t('common:button:cancel') }}
</el-button>
<!-- 保存 -->
<el-button :loading="btnLoading" size="small" type="primary" @click="applyReReading">
{{ $t('common:button:save') }}
</el-button>
</div>
</el-dialog>
</template>
</BaseContainer>
</template>
<script>
import { getTrialSiteSelect } from '@/api/trials'
import { getSelfConsistentDoctorStatList, getDoctorUserSelectList, getAnalysisTaskList, applyReReading, getDoctorConsistentRuleSubjectList, confirmGenerateConsistentTask, getTaskConsistentRuleList, addOrUpdateTaskConsistentRule, deleteTaskConsistentRule } from '@/api/trials/reading'
import ReaderRulesForm from './components/ReaderRulesForm'
import DoctorConsistentRuleSubjectTable from './components/DoctorConsistentRuleSubjectTable'
import BaseContainer from '@/components/BaseContainer'
import Pagination from '@/components/Pagination'
const searchDataDefault = () => {
return {
PageIndex: 1,
PageSize: 20,
Asc: true,
SortField: '',
TrialId: null,
SiteId: null,
SubjectId: null,
SubjectCode: null,
IsUrgent: null,
TaskName: null,
DoctorUserId: null,
ReadingCategory: null,
TaskAllocationState: null,
BeginAllocateDate: null,
EndAllocateDate: null
}
}
export default {
name: 'TrialsNotice',
components: { BaseContainer, Pagination, ReaderRulesForm, DoctorConsistentRuleSubjectTable },
data() {
return {
searchData: searchDataDefault(),
maxLength: 0,
list: [],
siteOptions: [],
total: 0,
loading: false,
ReaderRulesVisible: false,
DoctorConsistentRuleSubjectTableVisible: false,
trialId: this.$route.query.trialId,
currentData: {},
param: {},
DoctorUserList: [],
btnLoading: false,
rowData: {},
OtherInfo: null,
title: null,
SelectList: [],
TaskConsistentRuleList: [],
ReaderRulesFormVisible: false,
IsSelfAnalysis: true,
timeList: [],
ApplyforReasonVisible: false,
ApplyforReasonForm: {
Type: null,
RequestReReadingReason: null
},
rules: {
Type: [{ required: true, message: this.$t('common:ruleMessage:specify'), trigger: 'blur' }],
RequestReReadingReason: [{ required: true, message: this.$t('common:ruleMessage:specify'), trigger: 'blur' }]
},
}
},
mounted() {
this.getList()
this.getSite()
this.getDoctorUserSelectList()
},
methods: {
changeTimeList() {
if (this.timeList) {
this.searchData.BeginAllocateDate = this.timeList[0]
this.searchData.EndAllocateDate = this.timeList[1]
} else {
this.searchData.BeginAllocateDate = null
this.searchData.EndAllocateDate = null
}
},
// 重阅历史
reReadingHistory(row) {
this.$router.push({ path: `/trials/trials-panel/reading/reReadingTracking?trialId=${this.$route.query.trialId}&trialCode=${this.$route.query.trialCode}&researchProgramNo=${this.$route.query.researchProgramNo}&SiteId=${row.SiteId}&SubjectCode=${row.SubjectCode}&TaskName=${row.TaskName}&DoctorUserId=${row.DoctorUserId}` })
},
// 申请重阅
applyReReading() {
this.$refs.reasonForm.validate((valid) => {
if (!valid) return
this.loading = true
this.btnLoading = true
var params = {
TaskIdList: [
this.rowData.Id
],
TrialId: this.$route.query.trialId,
RequestReReadingType: 2,
RequestReReadingReason: this.ApplyforReasonForm.Type === 2 ? this.ApplyforReasonForm.RequestReReadingReason : '图像重传'
}
applyReReading(params).then(res => {
this.loading = false
this.btnLoading = false
this.$message.success('申请重阅成功')
this.getList()
this.ApplyforReasonVisible = false
}).catch(() => {
this.loading = false
this.btnLoading = false
})
})
},
lookReadingResults(row) {
var token = getToken()
const routeData = this.$router.resolve({
path: `/noneDicomReading?subjectId=${row.SubjectId}&trialId=${row.TrialId}&visitTaskId=${row.Id}&TokenKey=${token}`
})
window.open(routeData.href, '_blank')
},
openApplyReReading(row) {
this.rowData = { ...row }
this.ApplyforReasonVisible = true
this.ApplyforReasonForm = {
Type: null,
Remake: null
}
},
editReaderRulesForm(row) {
this.rowData = {...row}
this.ReaderRulesFormVisible = true
},
openDoctorConsistentRuleSubjectTable(row) {
this.rowData = {...row}
this.DoctorConsistentRuleSubjectTableVisible = true
},
addReaderRulesForm() {
if (this.TaskConsistentRuleList.length > 0) {
this.rowData = {...this.TaskConsistentRuleList[0]}
} else {
this.rowData = {}
}
this.ReaderRulesFormVisible = true
},
openTaskConsistentRule(IsSelfAnalysis) {
this.loading = true
if (IsSelfAnalysis) {
this.title = '自身一致性分析'
getSelfConsistentDoctorStatList({TrialId: this.trialId}).then(res => {
this.ReaderRulesFormVisible = false
this.DoctorConsistentRuleSubjectTableVisible = false
this.TaskConsistentRuleList = res.Result
this.IsSelfAnalysis = IsSelfAnalysis
this.ReaderRulesVisible = true
this.loading = false
}).catch(() => {
this.loading = false
})
} else {
this.title = '组间一致性分析'
this.loading = false
}
// getTaskConsistentRuleList({
// IsSelfAnalysis: IsSelfAnalysis,
// TrialId: this.trialId
// }).then(res => {
// this.ReaderRulesFormVisible = false
// this.DoctorConsistentRuleSubjectTableVisible = false
// this.TaskConsistentRuleList = res.Result
// this.IsSelfAnalysis = IsSelfAnalysis
// this.ReaderRulesVisible = true
// })
},
getDoctorUserSelectList() {
getDoctorUserSelectList(this.$route.query.trialId).then(res => {
this.DoctorUserList = res.Result
if (this.DoctorUserList.length > 0) {
this.ReadingType = this.DoctorUserList[0].ReadingType
} else {
this.$alert('请先去选择项目阅片人')
}
})
},
getList() {
this.searchData.TrialId = this.$route.query.trialId
this.loading = true
getAnalysisTaskList(this.searchData).then(res => {
this.loading = false
this.OtherInfo = res.OtherInfo
this.list = res.Result.CurrentPageData
this.total = res.Result.TotalCount
this.AssignMedicalReviewTaskVisible = false
}).catch(() => { this.loading = false })
},
handleSearch() {
this.searchData.PageIndex = 1
this.getList()
},
handleReset() {
this.searchData = searchDataDefault()
this.getList()
},
getSite() {
getTrialSiteSelect(this.trialId).then(res => {
this.siteOptions = res.Result
})
},
// 排序
handleSortChange(column) {
if (column.order === 'ascending') {
this.searchData.Asc = true
} else {
this.searchData.Asc = false
}
this.searchData.SortField = column.prop
this.searchData.PageIndex = 1
this.getList()
}
}
}
</script>
<style lang="scss" scoped>
>>>.hidden-row{
display: none;
}
>>>.el-dialog__body {
padding: 15px 20px;
}
>>>.el-tag--danger.el-tag--dark {
// background-color: #f56c6c!important;
border-color: none!important;
// color: #fff!important;
}
>>>#TaskAllocationRuleList thead .el-checkbox__inner{
display: none;
}
>>>.el-descriptions-item__label.has-colon:after{
display: none;
}
</style>

View File

@ -0,0 +1,103 @@
/* eslint-disable */
<template>
<el-form
ref="MedicalManagerDataForm"
v-loading="loading"
:model="form"
size="small"
:rules="rules"
label-width="140px"
>
<div class="base-dialog-body">
<el-form-item label="阅片人" prop="MedicalManagerUserId">
<el-select v-model="form.MedicalManagerUserId" clearable>
<el-option v-for="item of doctorUserList" :label="`${item.UserName}(${item.FullName})`" :value="item.UserId" />
</el-select>
</el-form-item>
</div>
<div class="base-dialog-footer" style="text-align:right;margin-top:10px;">
<el-form-item>
<!-- 取消 -->
<el-button
:disabled="btnLoading"
size="small"
type="primary"
@click="close"
>
{{ $t('common:button:cancel') }}
</el-button>
<!-- 保存 -->
<el-button size="small" type="primary" :loading="btnLoading" @click="save">
{{ $t('common:button:save') }}
</el-button>
</el-form-item>
</div>
</el-form>
</template>
<script>
import { assignMedicalReviewTask } from '@/api/trials/reading'
export default {
name: 'AddOrUpdateTaskAllocationRuleData',
props: {
data: {
type: Object,
default() {
return {}
}
},
doctorUserList: {
type: Array,
default() {
return []
}
},
selectList: {
type: Array,
default() {
return []
}
}
},
data() {
return {
form: {
IdList: [],
MedicalManagerUserId: null,
TaskOptType: 1
},
rules: {
MedicalManagerUserId: [{ required: true, message: this.$t('common:ruleMessage:select'), trigger: 'blur' }],
},
loading: false,
btnLoading: false
}
},
mounted() {
this.form.TrialId = this.$route.query.trialId
this.form.IdList = Object.assign([], this.selectList.map(v => v.Id))
},
methods: {
save() {
this.$refs.MedicalManagerDataForm.validate(valid => {
console.log(valid)
if (!valid) return
this.btnLoading = true
this.loading = true
assignMedicalReviewTask(this.form).then(res => {
this.loading = false
this.btnLoading = false
this.$message.success('保存成功')
this.$emit('getList')
}).catch(() => {
this.loading = false
this.btnLoading = false
})
})
},
close() { this.$emit('close') }
}
}
</script>
<style lang="scss" scoped>
</style>

View File

@ -0,0 +1,856 @@
/* eslint-disable */
<template>
<BaseContainer>
<template slot="search-container">
<el-form :inline="true">
<el-form-item :label="$t('trials:consistencyCheck:table:siteId')">
<el-select v-model="searchData.SiteId" clearable filterable style="width:120px;">
<el-option
v-for="(item,index) of siteOptions"
:key="'taskSearchData' + index"
:label="item.TrialSiteCode"
:value="item.SiteId"
/>
</el-select>
</el-form-item>
<!-- 受试者编号 -->
<el-form-item :label="$t('trials:consistencyCheck:table:subjectId')">
<el-input v-model="searchData.SubjectCode" style="width:100px;" />
</el-form-item>
<el-form-item :label="$t('trials:seletctedReviews:title:ReadPeople')">
<el-select v-model="searchData.DoctorUserId" clearable style="width:120px;">
<el-option v-for="item of DoctorUserList" :key="'taskSearchData' + item.DoctorUserId" :value="item.DoctorUserId" :label="`${item.UserName}(${item.FullName})`" />
</el-select>
</el-form-item>
<el-form-item>
<el-button type="primary" icon="el-icon-search" @click="getList">
{{ $t('common:button:search') }}
</el-button>
<el-button type="primary" icon="el-icon-refresh-left" @click="handleReset">
{{ $t('common:button:reset') }}
</el-button>
<el-button type="primary" icon="el-icon-edit-outline" v-hasPermi="['trials:trials-panel:reading:readTaskAllocation:edit']" @click="openAllocationConfigVisible">
分配规则
</el-button>
<el-button type="primary" :loading="btnLoading" v-hasPermi="['trials:trials-panel:reading:readTaskAllocation:edit']" @click="manualAllocation()">
手动分配
</el-button>
<el-button type="primary" :loading="btnLoading" @click="openAllocation('task')">
访视/全局/肿瘤学阅片任务
</el-button>
<el-button type="primary" :loading="btnLoading" @click="openAllocation('referee')">
裁判阅片任务
</el-button>
</el-form-item>
</el-form>
</template>
<template slot="main-container">
<el-table
v-loading="loading"
v-adaptive="{bottomOffset:70}"
border
:data="list"
stripe
@selection-change="handleSubjectAssignListSelectChange"
>
<el-table-column type="selection" align="left" width="45" />
<el-table-column
prop="TrialSiteCode"
label="中心编号"
width="140"
show-overflow-tooltip
/>
<el-table-column
prop="SubjectCode"
label="受试者编号"
width="140"
show-overflow-tooltip
/>
<el-table-column
prop="VisitTaskTypeCount"
label="访视"
width="140"
show-overflow-tooltip
/>
<el-table-column
prop="GlobalTaskTypeCount"
label="全局"
width="140"
show-overflow-tooltip
/>
<el-table-column
prop="JudgeTaskTypeCount"
label="裁判"
width="140"
show-overflow-tooltip
/>
<el-table-column
prop="OncologyTaskTypeCount"
label="肿瘤学阅片"
width="140"
show-overflow-tooltip
/>
<el-table-column
prop="SubjectCode"
label="阅片人(R1)"
min-width="100"
show-overflow-tooltip
>
<template slot-scope="scope">
<div v-if="scope.row.NowDoctorUserList.length" style="display: flex;justify-content: left;align-items: center">
<el-tag v-if="findDoctorUser(scope.row.NowDoctorUserList, 1).DoctorUser">
{{ `${findDoctorUser(scope.row.NowDoctorUserList, 1).DoctorUser.UserName}(${findDoctorUser(scope.row.NowDoctorUserList, 1).DoctorUser.FullName})` }}
</el-tag>
</div>
</template>
</el-table-column>
<el-table-column
prop="SubjectCode"
label="阅片人(R2)"
min-width="100"
show-overflow-tooltip
>
<template slot-scope="scope">
<div v-if="scope.row.NowDoctorUserList.length" style="display: flex;justify-content: left;align-items: center">
<el-tag v-if="findDoctorUser(scope.row.NowDoctorUserList, 2).DoctorUser">
{{ `${findDoctorUser(scope.row.NowDoctorUserList, 2).DoctorUser.UserName}(${findDoctorUser(scope.row.NowDoctorUserList, 2).DoctorUser.FullName})` }}
</el-tag>
</div>
</template>
</el-table-column>
<el-table-column
prop="SubjectCode"
label="裁判(AD)"
min-width="100"
show-overflow-tooltip
>
<template slot-scope="scope">
<div v-if="scope.row.NowDoctorUserList.length" style="display: flex;justify-content: left;align-items: center">
<el-tag v-if="findDoctorUser(scope.row.NowDoctorUserList, 3).DoctorUser">
{{ `${findDoctorUser(scope.row.NowDoctorUserList, 3).DoctorUser.UserName}(${findDoctorUser(scope.row.NowDoctorUserList, 3).DoctorUser.FullName})` }}
</el-tag>
</div>
</template>
</el-table-column>
<el-table-column
prop="SubjectCode"
label="肿瘤阅片人(O1)"
min-width="100"
show-overflow-tooltip
>
<template slot-scope="scope">
<div v-if="scope.row.NowDoctorUserList.length" style="display: flex;justify-content: left;align-items: center">
<el-tag v-if="findDoctorUser(scope.row.NowDoctorUserList, 4).DoctorUser">
{{ `${findDoctorUser(scope.row.NowDoctorUserList, 4).DoctorUser.UserName}(${findDoctorUser(scope.row.NowDoctorUserList, 4).DoctorUser.FullName})` }}
</el-tag>
</div>
</template>
</el-table-column>
<el-table-column
prop=""
label="操作"
width="180"
show-overflow-tooltip
v-if="hasPermi(['trials:trials-panel:reading:readTaskAllocation:edit'])"
>
<template slot-scope="scope">
<el-button
circle
:disabled="scope.row.NowDoctorUserList.length === 4"
icon="el-icon-thumb"
title="手动分配"
@click="manualAllocation(scope.row)"
/>
<el-button
circle
:disabled="scope.row.NowDoctorUserList.length === 0"
icon="el-icon-close"
title="清除阅片人"
@click="openClearAudience(scope.row)"
/>
<el-button
circle
icon="el-icon-document-copy"
title="查看取消历史"
@click="openSubjectCancelDoctorHistoryList(scope.row)"
/>
</template>
</el-table-column>
</el-table>
<pagination style="text-align: right;margin-top: 10px;" class="page" :total="total" :page.sync="searchData.PageIndex" :limit.sync="searchData.PageSize" @pagination="getList" />
<!-- 详情 -->
<el-dialog
title="分配规则"
:visible.sync="AllocationConfigVisible"
width="1200px"
:close-on-click-modal="false"
custom-class="base-dialog-wrapper"
>
<div class="base-dialog-body" style="position: relative;">
<el-divider content-position="left" style="margin-bottom: 40px;">阅片人列表</el-divider>
<el-form
style="margin-top: 40px"
ref="signForm"
size="small"
label-width="0px"
>
<el-form-item>
<el-table
v-loading="loading"
border
:data="TaskAllocationRuleList"
stripe
>
<el-table-column
prop="DoctorUser.UserName"
label="阅片人"
min-width="180"
show-overflow-tooltip
>
<template slot-scope="scope">
{{ `${scope.row.DoctorUser.UserName}(${scope.row.DoctorUser.FullName})` }}
</template>
</el-table-column>
<el-table-column
prop="PlanSubjectCount"
label="计划受试者数量"
min-width="130"
show-overflow-tooltip
/>
<el-table-column
prop="UserName"
label="阅片类型"
min-width="280"
show-overflow-tooltip
>
<template slot-scope="scope">
<span v-for="item of scope.row.ReadingCategoryList">
<el-tag v-if="item === 1" type="primary">{{ $fd('ReadingCategory', item) + '&' + $fd('ReadingCategory', 2) }}</el-tag>
<el-tag v-if="item === 4" type="danger">{{ $fd('ReadingCategory', item) }}</el-tag>
<el-tag v-if="item === 5" type="warning">{{ $fd('ReadingCategory', item) }}</el-tag>
</span>
</template>
</el-table-column>
<el-table-column
prop="IsEnable"
label="状态"
min-width="80"
show-overflow-tooltip
>
<template slot-scope="scope">
<el-tag v-if="!scope.row.IsEnable" type="danger">{{ $fd('IsEnable', scope.row.IsEnable) }}</el-tag>
<el-tag v-else>{{ $fd('IsEnable', scope.row.IsEnable) }}</el-tag>
</template>
</el-table-column>
<el-table-column
prop="Note"
label="备注"
min-width="100"
show-overflow-tooltip
/>
<el-table-column
fixed="right"
label="操作"
width="180"
>
<template slot-scope="scope">
<el-button
circle
icon="el-icon-edit-outline"
title="编辑"
@click="addOrEdit(scope.row)"
/>
</template>
</el-table-column>
</el-table>
</el-form-item>
<el-divider content-position="left">后续分配规则</el-divider>
<el-descriptions class="margin-top" :column="1">
<el-descriptions-item>
<template slot="label">
<el-checkbox v-model="TrialTaskInfo.IsFollowVisitAutoAssign" disabled>同一病例随访阅片任务自动分配(在拟分配的阅片人未标记为退出项目状态时)默认状态 {{$fd('AutoAssignDefaultState', TrialTaskInfo.FollowVisitAutoAssignDefaultState)}} </el-checkbox>
</template>
<div style="margin-left: 10px;">
</div>
</el-descriptions-item>
<el-descriptions-item label="同一病例随访阅片任务自动分配(在拟分配的阅片人未标记为退出项目状态时)">
<template slot="label">
<el-checkbox v-model="TrialTaskInfo.IsFollowGlobalVisitAutoAssign" disabled>同一病例全局任务自动分配(在拟分配的阅片人未标记为退出项目状态时 默认状态 {{$fd('AutoAssignDefaultState', TrialTaskInfo.FollowGlobalVisitAutoAssignDefaultState)}} </el-checkbox>
</template>
<div style="margin-left: 10px;">
</div>
</el-descriptions-item>
<el-descriptions-item>
<template slot="label">
<el-checkbox v-model="TrialTaskInfo.IsFollowJudgeTaskAutoAssign" disabled>同一病例第二次裁判任务自动分配(在拟分配的阅片人未标记为退出项目状态时)默认状态 {{$fd('AutoAssignDefaultState', TrialTaskInfo.FollowJudgeTaskAutoAssignDefaultState)}} </el-checkbox>
</template>
<div style="margin-left: 10px;" />
</el-descriptions-item>
</el-descriptions>
</el-form>
</div>
<div class="base-dialog-footer" style="text-align:right;margin-top:10px;">
<!-- 取消 -->
<el-button
:disabled="btnLoading"
size="small"
type="primary"
@click="handleCancel"
>
{{ $t('common:button:cancel') }}
</el-button>
<!-- 保存 -->
<el-button size="small" type="primary" :loading="btnLoading" @click="handleSave">
{{ $t('common:button:save') }}
</el-button>
</div>
</el-dialog>
<el-dialog
v-if="taskAllocationRuleVisible"
:title="title"
:visible.sync="taskAllocationRuleVisible"
width="600px"
:close-on-click-modal="false"
>
<AddOrEditTaskAllocationRule :data="rowData" :TaskAllocationRuleList="TaskAllocationRuleList" :doctor-user-list="DoctorUserList" @close="() => {taskAllocationRuleVisible = false}" @getList="openAllocationConfigVisible" />
</el-dialog>
<el-dialog
v-if="manualAllocationVisible"
title="分配阅片人"
:visible.sync="manualAllocationVisible"
width="1580px"
:close-on-click-modal="false"
>
<ManualAllocationTable :data="rowData" :reading-type="ReadingType" :SubjectAssignSelectList="SubjectAssignSelectList" @close="() => {manualAllocationVisible = false}" @getList="getList"></ManualAllocationTable>
</el-dialog>
<el-dialog
v-if="ClearAudienceVisible"
title="清除阅片人"
:visible.sync="ClearAudienceVisible"
width="500px"
:close-on-click-modal="false"
>
<ClearAudienceList :data="rowData" :reading-type="ReadingType" @close="() => {ClearAudienceVisible = false}" @getList="getList"></ClearAudienceList>
</el-dialog>
<el-dialog
v-if="AllocationVisible"
:title="AllocationType === 'task' ? ' 访视/全局/肿瘤学阅片任务' : ' 裁判阅片任务'"
:visible.sync="AllocationVisible"
:fullscreen="true"
:close-on-click-modal="false"
>
<taskAllacation v-if="AllocationType === 'task'"></taskAllacation>
<refereeAllacation v-else></refereeAllacation>
</el-dialog>
<el-dialog
v-if="SubjectCancelDoctorHistoryVisible"
title="取消历史"
:visible.sync="SubjectCancelDoctorHistoryVisible"
width="800px"
:close-on-click-modal="false"
>
<el-table
border
:data="SubjectCancelDoctorHistoryList"
stripe
>
<el-table-column
prop="CreateTime"
label="取消时间"
width="180"
show-overflow-tooltip
/>
<el-table-column
prop="Note"
label="原因"
show-overflow-tooltip
/>
</el-table>
</el-dialog>
</template>
</BaseContainer>
</template>
<script>
import { getTrialSiteSelect } from '@/api/trials'
import { getSubjectCancelDoctorHistoryList, getSubjectAssignAndTaskStatList, getDoctorPlanAllocationRuleList, getSubjectApplyDoctorTaskStatList, assignSubjectTaskToDoctor, manualAssignDoctorApplyTask, autoSubjectAssignDoctor, getTaskAllocationRuleList, getSubjectAssignList, assignSubjectDoctor, cancelSubjectAssignDoctor, deleteTaskAllocationRule, configTrialTaskInfo, getDoctorUserSelectList } from '@/api/trials/reading'
import AddOrEditTaskAllocationRule from './components/AddOrEditTaskAllocationRule'
import ManualAllocationTable from './components/ManualAllocationTable'
import ClearAudienceList from './components/ClearAudienceList'
import BaseContainer from '@/components/BaseContainer'
import Pagination from '@/components/Pagination'
import taskAllacation from '../task-allocation/index'
import refereeAllacation from '../referee-allocation/index'
const searchDataDefault = () => {
return {
PageIndex: 1,
PageSize: 20,
Asc: true,
SortField: '',
TrialId: null,
SiteId: null,
SubjectId: null,
SubjectCode: null,
DoctorUserId: null,
IsHaveAssigned: null,
IsAssignConfirmed: null
}
}
const taskSearchDataDefault = () => {
return {
PageIndex: 1,
PageSize: 20,
Asc: true,
SortField: '',
TrialId: null,
SiteId: null,
SubjectId: null,
SubjectCode: null,
IsUrgent: null,
TaskName: null,
TaskState: null,
DoctorUserId: null,
ReadingCategory: null,
TaskAllocationState: null,
BeginAllocateDate: null,
EndAllocateDate: null
}
}
export default {
name: 'TrialsNotice',
components: { BaseContainer, Pagination, AddOrEditTaskAllocationRule, ManualAllocationTable, ClearAudienceList, taskAllacation, refereeAllacation },
data() {
return {
searchData: searchDataDefault(),
taskSearchData: taskSearchDataDefault(),
maxLength: 0,
list: [],
siteOptions: [],
total: 0,
SubjectAssignListTotal: 0,
loading: false,
AllocationVisible: false,
AllocationConfigVisible: false,
taskAllocationRuleVisible: false,
AutoAlloctionVisible: false,
ClearAudienceVisible: false,
WaitApplyVisible: false,
applyVisible: false,
manualAllocationVisible: false,
SubjectCancelDoctorHistoryVisible: false,
trialId: this.$route.query.trialId,
AllocationType: null,
currentData: {},
param: {},
DoctorUserList: [],
TaskAllocationRuleList: [],
btnLoading: false,
value: true,
value1: true,
TrialTaskInfo: {
TrialId: null,
TaskAllocateObjEnum: 0,
FollowVisitAutoAssignDefaultState: 0,
FollowGlobalVisitAutoAssignDefaultState: 0,
TaskAllocateDefaultState: 1,
IsFollowVisitAutoAssign: true,
IsFollowGlobalVisitAutoAssign: true
},
ReadingType: null,
rules: {
DoctorUserId: [{ required: true, message: this.$t('common:ruleMessage:select'), trigger: 'blur' }]
},
title: '',
rowData: {},
ApplyTaskInfo: {},
timeList: [],
WaitApplyList: [],
SubjectAssignList: [],
SubjectAssignSelectList: [],
TaskAllocationRuleSelectList: [],
readVisible: false,
TaskOptType: null,
OtherInfo: null,
SubjectCancelDoctorHistoryList: [],
}
},
mounted() {
this.getDoctorUserSelectList()
this.getSite()
this.getList()
},
methods: {
openSubjectCancelDoctorHistoryList(row) {
getSubjectCancelDoctorHistoryList(row.SubjectId).then((res) => {
this.SubjectCancelDoctorHistoryVisible = true
this.SubjectCancelDoctorHistoryList = res.Result
})
},
openAllocation(AllocationType) {
this.AllocationVisible = true
this.AllocationType = AllocationType
},
openClearAudience(row) {
this.rowData = {...row}
this.ClearAudienceVisible = true
},
findDoctorUser(list, type) {
var a
if (list.length > 0) {
a = list.find(v => {
return v.ArmEnum === type
}) ? list.find(v => {
return v.ArmEnum === type
}) : {
DoctorUser: null
}
}
return a
},
assignSubjectTaskToDoctor(row, type) {
this.rowData = { ...row }
this.rowData.TaskOptType = type
if (type === 1 || type === 2) {
this.readVisible = true
} else if (type === 3) {
this.$confirm('是否确定分配').then(() => {
this.btnLoading = true
this.loading = true
var params = Object.assign({}, this.rowData)
assignSubjectTaskToDoctor(params).then(res => {
this.$message.success('保存成功')
this.btnLoading = false
this.loading = false
this.readVisible = false
this.getList()
}).catch(() => {
this.btnLoading = false
this.loading = false
})
})
} else if (type === 4) {
this.$confirm('是否确定取消分配').then(() => {
this.btnLoading = true
this.loading = true
var params = Object.assign({}, this.rowData)
assignSubjectTaskToDoctor(params).then(res => {
this.$message.success('保存成功')
this.btnLoading = false
this.loading = false
this.readVisible = false
this.getList()
}).catch(() => {
this.btnLoading = false
this.loading = false
})
})
}
},
assignSubjectTaskToDoctorSave() {
this.$refs.readDataForm.validate(valid => {
if (!valid) return
this.btnLoading = true
this.loading = true
var params = Object.assign({}, this.rowData)
assignSubjectTaskToDoctor(params).then(res => {
this.$message.success('保存成功')
this.btnLoading = false
this.loading = false
this.readVisible = false
this.getList()
}).catch(() => {
this.btnLoading = false
this.loading = false
})
})
},
manualAssignDoctorApplyTaskSave() {
this.loading = true
this.btnLoading = true
manualAssignDoctorApplyTask(this.ApplyTaskInfo).then(res => {
this.loading = false
this.btnLoading = false
this.$message.success('生效成功')
this.WaitApplyVisible = false
this.getList()
this.openAutoAlloction()
}).catch(() => {
this.loading = false
this.btnLoading = false
})
},
manualAssignDoctorApplyTask(row) {
var params = {
TrialId: this.trialId,
SubjectIdList: Object.assign([], this.SubjectAssignSelectList.map(v => v.SubjectId))
}
this.ApplyTaskInfo = {
TrialId: this.trialId,
SubjectDoctorUserList: Object.assign([], this.SubjectAssignSelectList.map(v => {
v.DoctorUserIdArmList = v.DoctorUserList
return v
}))
}
if (row) {
params.SubjectIdList = [row.SubjectId]
var param = Object.assign({}, row)
param.DoctorUserIdArmList = param.DoctorUserList
this.ApplyTaskInfo = Object.assign({}, row)
delete this.ApplyTaskInfo.DoctorUserList
delete this.ApplyTaskInfo.DoctorUserIdList
this.ApplyTaskInfo.SubjectDoctorUserList = [
param
]
}
this.loading = true
this.btnLoading = true
getSubjectApplyDoctorTaskStatList(params).then(res => {
if (res.Result.length === 0) {
this.$message.error('该项目没有有效阅片人')
return
}
this.WaitApplyList = res.Result
this.WaitApplyVisible = true
this.loading = false
this.btnLoading = false
}).catch(() => {
this.loading = false
this.btnLoading = false
})
},
autoSubjectAssignDoctor() {
this.$confirm('确定执行自动分配?').then(() => {
this.loading = true
this.btnLoading = true
autoSubjectAssignDoctor({ TrialId: this.trialId }).then(res => {
this.loading = false
this.btnLoading = false
this.$message.success('自动分配成功')
this.getList()
this.openAutoAlloction()
}).catch(() => {
this.loading = false
this.btnLoading = false
})
})
},
handleSaveTaskAllocationRuleSelectList() {
if (this.TaskAllocationRuleSelectList.length === 0) {
this.$message.error('请选择阅片人')
return
}
var num
if (this.ReadingType === 2) {
num = 2
} else {
num = 1
}
if (this.TaskAllocationRuleSelectList.length !== num) {
this.$message.error(`当前阅片模式为${this.$fd('ReadingMethod', this.ReadingType)},请选择正确阅片人数量`)
return
}
this.loading = true
this.btnLoading = true
var params = {
DoctorUserIdArmList: this.TaskAllocationRuleSelectList.map((v, i) => { return { DoctorUserId: v.DoctorUserId, ArmEnum: this.ReadingType === 1 ? 1 : i + 1 } }),
SubjectIdList: this.SubjectAssignSelectList.map(v => v.SubjectId),
TrialId: this.trialId,
IsReAssign: true
}
assignSubjectDoctor(params).then(v => {
this.loading = false
this.btnLoading = false
this.openAutoAlloction()
this.TaskAllocationRuleSelectList = []
this.SubjectAssignSelectList = []
this.getDoctorUserSelectList()
this.$message.success('分配成功')
this.manualAllocationVisible = false
}).catch(() => {
this.loading = false
this.btnLoading = false
})
},
selectable(row) {
var num
if (this.ReadingType === 2) {
num = 2
} else {
num = 1
}
if (this.TaskAllocationRuleSelectList.length === num && !this.TaskAllocationRuleSelectList.find(v => { return v.UserName === row.UserName })) {
return false
} else {
return true
}
},
manualAllocation(row) {
this.rowData = {}
if (row) {
this.rowData = { ...row }
this.SubjectAssignSelectList = [row]
}
if (this.SubjectAssignSelectList.length === 0) {
this.$message.error('请勾选要分配的受试者')
return
}
this.manualAllocationVisible = true
},
handleTaskAllocationRuleListSelectChange(e) {
this.TaskAllocationRuleSelectList = e
},
handleSubjectAssignListSelectChange(e) {
this.SubjectAssignSelectList = e
},
changeTimeList() {
if (this.timeList) {
this.searchData.BeginAllocateDate = this.timeList[0]
this.searchData.EndAllocateDate = this.timeList[1]
} else {
this.searchData.BeginAllocateDate = null
this.searchData.EndAllocateDate = null
}
},
handleCancel() {
this.AllocationConfigVisible = false
},
handleSave() {
this.btnLoading = true
this.loading = true
var params = Object.assign({}, this.TrialTaskInfo)
params.TrialId = this.$route.query.trialId
configTrialTaskInfo(params).then(res => {
this.btnLoading = false
this.loading = false
this.AllocationConfigVisible = false
this.getList()
this.$message.success('保存成功')
}).catch(() => {
this.btnLoading = false
this.loading = false
})
},
handleDelete(row) {
this.$confirm(`确定要删除${row.DoctorUser.UserName}(${row.DoctorUser.FullName})的任务分配规则?`, {
type: 'warning',
distinguishCancelAndClose: true
}).then(() => {
this.loading = true
deleteTaskAllocationRule(row.Id, false).then(res => {
this.loading = false
this.$message.success('删除成功')
this.openAllocationConfigVisible()
}).catch(() => { this.loading = false })
})
},
addOrEdit(row) {
if (row) {
this.rowData = { ...row }
this.title = '编辑'
} else {
this.rowData = {}
this.title = '新增'
}
this.taskAllocationRuleVisible = true
},
openAllocationConfigVisible() {
this.loading = true
getDoctorPlanAllocationRuleList({
TrialId: this.$route.query.trialId
}).then(res => {
this.loading = false
this.TaskAllocationRuleList = res.Result
this.TrialTaskInfo = Object.assign({}, res.OtherInfo)
this.AllocationConfigVisible = true
this.taskAllocationRuleVisible = false
}).catch(() => { this.loading = false })
},
openAutoAlloction() {
this.loading = true
getTaskAllocationRuleList({
TrialId: this.$route.query.trialId
}).then(async(res) => {
this.taskSearchData.TrialId = this.$route.query.trialId
var params = Object.assign({}, this.taskSearchData)
var SubjectAssignListRes = await getSubjectAssignList(params)
this.SubjectAssignList = SubjectAssignListRes.Result.CurrentPageData
this.SubjectAssignListTotal = SubjectAssignListRes.Result.TotalCount
this.loading = false
this.TaskAllocationRuleList = res.Result
this.TrialTaskInfo = Object.assign({}, res.OtherInfo)
this.AutoAlloctionVisible = true
}).catch(() => { this.loading = false })
},
getDoctorUserSelectList() {
getDoctorUserSelectList(this.$route.query.trialId).then(res => {
this.DoctorUserList = res.Result
if (this.DoctorUserList.length > 0) {
this.ReadingType = this.DoctorUserList[0].ReadingType
} else {
}
})
},
getList() {
this.searchData.TrialId = this.$route.query.trialId
this.loading = true
getSubjectAssignAndTaskStatList(this.searchData).then(res => {
this.loading = false
this.OtherInfo = res.OtherInfo
this.list = res.Result.CurrentPageData
this.total = res.Result.TotalCount
this.manualAllocationVisible = false
this.ClearAudienceVisible = false
this.SubjectAssignSelectList = []
}).catch(() => { this.loading = false })
},
handleSearch() {
this.searchData.PageIndex = 1
this.getList()
},
handleReset() {
this.searchData = searchDataDefault()
this.getList()
},
handleReset2() {
this.taskSearchData = taskSearchDataDefault()
this.openAutoAlloction()
},
getSite() {
getTrialSiteSelect(this.trialId).then(res => {
this.siteOptions = res.Result
})
},
// 排序
handleSortChange(column) {
if (column.order === 'ascending') {
this.searchData.Asc = true
} else {
this.searchData.Asc = false
}
this.searchData.SortField = column.prop
this.searchData.PageIndex = 1
this.getList()
}
}
}
</script>
<style lang="scss" scoped>
>>>.hidden-row{
display: none;
}
>>>.el-dialog__body {
padding: 15px 20px;
}
>>>.el-tag--danger.el-tag--dark {
// background-color: #f56c6c!important;
border-color: none!important;
// color: #fff!important;
}
>>>#TaskAllocationRuleList thead .el-checkbox__inner{
display: none;
}
>>>.el-descriptions-item__label.has-colon:after{
display: none;
}
</style>

View File

@ -0,0 +1,364 @@
<template>
<div v-loading="loading" class="chat-wrapper">
<div class="chat-content">
<div v-for="(record,index) in recordContent" :key="index">
<div :class="[!record.IsCurrentUser?'word':'word-my']">
<img v-if="record.UserTypeEnumInt*1 === 14" :src="mimAvatar" alt="MIM">
<img v-else :src="irAvatar" alt="IR">
<div class="info">
<p class="user-info">
<span style="font-weight:700;">{{ record.CreateUserName }} </span>
<span>({{ record.CreateTime }}) </span>
</p>
<div class="info-content">
<template v-if="record.UserTypeEnumInt === 14 && record.Content">
<div v-if="record.MedicalDialogCloseEnum!== null">
{{ $t('trials:medicalFeedback:title:closeReasonEnum') }} {{ $fd('MedicalDialogCloseEnum',record.MedicalDialogCloseEnum) }}
</div>
<div>{{ record.Content }}</div>
</template>
<template v-else-if="record.UserTypeEnumInt === 14 && !record.Content">
<div>
<!-- 您好,根据医学审核反馈,该阅片任务的评估有如下问题需要您确认或澄清: -->
{{ $t('trials:medicalFeedback:message:msg1') }}
<ol>
{{ record.Questioning }}
</ol>
</div>
<div v-if="record.FileList && record.FileList.length > 0" style="margin-top:5px;">
<!-- 本次医学审核相关截图(如适用)如下: -->
{{ $t('trials:medicalFeedback:message:msg2') }}
<ol>
<li v-for="file in record.FileList" :key="file.ImagePath" style="list-style: none;">
<el-button
type="text"
size="mini"
@click="previewImage(file.ImagePath)"
>{{ file.FileName }}</el-button>
</li>
</ol>
</div>
<div style="margin-top:5px;">
<!-- 根据本项目《独立影像评估章程》的相关约定,该类问题医学审核建议为: -->
{{ $t('trials:medicalFeedback:message:msg3') }}
<ol>
<span style="color:red">{{ $fd('AuditAdvice', record.AuditAdviceEnum) }}</span>
</ol>
</div>
<div style="margin-top:5px;">
<!-- 为高效解决/处理以上全部医学反馈问题,麻烦您在对该阅片任务的评估准确复核后,明确是否认可医学反馈问题及结论。如不认可医学反馈,请逐条准确完整回复不认可的原因,非常感谢。 -->
{{ $t('trials:medicalFeedback:message:msg4') }}
</div>
</template>
<template v-else>
<div>
{{ $t('trials:medicalFeedback:title:isEndorse') }}{{ $fd('MedicalReviewDoctorUserIdea', record.DoctorUserIdeaEnum) }}
</div>
<div v-if="record.DoctorUserIdeaEnum===2"> {{ $t('trials:medicalFeedback:title:reason') }}{{ record.DisagreeReason }}</div>
<div v-if="record.MedicalDialogCloseEnum!== null">
<!-- 是否申请重阅: -->
{{ $t('trials:medicalFeedback:title:isRequestReread') }}{{ $fd('YesOrNo', record.IsApplyHeavyReading) }}
</div>
<div v-if="record.FileList && record.FileList.length > 0" style="margin-top:5px;">
<!-- 本次医学审核相关截图(如适用)如下: -->
{{ $t('trials:medicalFeedback:message:msg5') }}
<ol>
<li v-for="file in record.FileList" :key="file.ImagePath" style="list-style: none;">
<el-button
type="text"
size="mini"
@click="previewImage(file.ImagePath)"
>{{ file.FileName }}</el-button>
</li>
</ol>
</div>
</template>
</div>
</div>
</div>
</div>
</div>
<div
v-if="otherInfo && !otherInfo.IsClosedDialog && userTypeEnumInt === 14"
class="chat-message"
>
<div class="message">
<el-input
v-model="newMessage"
type="textarea"
:rows="2"
/>
</div>
<div class="function">
<!-- 回复 -->
<el-button
:disabled="newMessage===''"
type="primary"
:loading="btnLoading"
@click="handleReply"
>
{{ $t('trials:medicalFeedback:button:reply') }}
</el-button>
</div>
</div>
<div v-if="otherInfo && !otherInfo.IsClosedDialog && userTypeEnumInt === 13" class="chat-message">
<div class="function">
<!-- 回复 -->
<el-button
type="primary"
:loading="btnLoading"
@click="handleIRReply"
>
{{ $t('trials:medicalFeedback:button:reply') }}
</el-button>
</div>
</div>
<el-dialog
append-to-body
:visible.sync="previewDialog"
width="600px"
>
<!-- <img width="100%" :src="imagePath" alt="图片未找到"> -->
<el-image :src="imagePath" width="100%">
<div slot="placeholder" class="image-slot">
{{ $t('trials:medicalFeedback:message:loading') }}<span class="dot">...</span>
</div>
</el-image>
</el-dialog>
<el-dialog
v-if="irFeedbackForm.visible"
:visible.sync="irFeedbackForm.visible"
:close-on-click-modal="false"
custom-class="base-dialog-wrapper"
:title="irFeedbackForm.title"
width="1200px"
append-to-body
>
<FeedbackForm
:task-medical-review-id="taskMedicalReviewId"
:visit-task-id="visitTaskId"
@getList="getMessageList"
@close="irFeedbackForm.visible = false"
/>
</el-dialog>
</div>
</template>
<script>
import { getMedicalReviewDialog, sendMedicalReviewDialog } from '@/api/trials'
import FeedbackForm from '@/views/trials/trials-panel/reading/medical-feedback/components/FeedbackForm'
import mimAvatar from '@/assets/MIM.png'
import irAvatar from '@/assets/IR.png'
export default {
name: 'ChatForm',
components: {
FeedbackForm
},
props: {
taskMedicalReviewId: {
type: String,
required: true
},
visitTaskId: {
type: String,
required: true
}
},
data() {
return {
recordContent: [],
newMessage: '',
showNewMessage: false,
loading: false,
btnLoading: false,
userId: zzSessionStorage.getItem('userId'),
mimAvatar,
irAvatar,
userTypeEnumInt: zzSessionStorage.getItem('userTypeEnumInt') * 1,
ReuploadEnum: 0,
trialId: this.$route.query.trialId,
currentUserId: zzSessionStorage.getItem('userId'),
otherInfo: {},
previewDialog: false,
imagePath: '',
irFeedbackForm: { visible: false, title: '' }
}
},
mounted() {
this.getMessageList()
},
methods: {
// 初始化聊天信息
getMessageList() {
this.loading = true
var param = {
taskMedicalReviewId: this.taskMedicalReviewId
}
getMedicalReviewDialog(param).then(res => {
this.otherInfo = res.OtherInfo
this.recordContent = res.Result
this.setScrollHeight()
this.loading = false
}).catch(() => {
this.loading = false
})
},
setScrollHeight() {
setTimeout(() => {
var container = document.querySelectorAll('.chat-content')[0]
container.scrollTop = container.scrollHeight
}, 100)
},
// 回复质疑
handleReply() {
if (!this.newMessage) return
var param = {
taskMedicalReviewId: this.taskMedicalReviewId,
content: this.newMessage
}
this.loading = true
sendMedicalReviewDialog(param).then(res => {
this.loading = false
if (res.IsSuccess) {
this.newMessage = ''
this.getMessageList()
}
}).catch(() => { this.loading = false })
},
previewImage(path) {
this.imagePath = `/api/${path}`
this.previewDialog = true
},
handleIRReply() {
// '反馈'
this.irFeedbackForm.title = this.$t('trials:medicalFeedback:title:feedback')
this.irFeedbackForm.visible = true
}
}
}
</script>
<style lang="scss">
.chat-wrapper{
::-webkit-scrollbar {
width: 7px;
height: 7px;
}
::-webkit-scrollbar-thumb {
border-radius: 10px;
background: #d0d0d0;
}
.chat-content{
width:100%;
padding: 20px;
height: 500px;
overflow-y: auto;
.word{
display: flex;
margin-bottom: 20px;
img{
width:40px;
height: 40px;
border-radius: 50%;
}
.info{
margin-left: 10px;
.user-info{
font-size: 12px;
color:rgba(51,51,51,0.8);
margin:0;
height: 20px;
line-height: 20px;
margin-top: -5px;
}
.info-content{
padding: 10px;
font-size: 14px;
background-color: #ebeef5;
position: relative;
margin-top: 8px;
}
.info-content::before{
position: absolute;
left: -8px;
top: 8px;
content: '';
border-right: 10px solid #ebeef5;
border-top: 8px solid transparent;
border-bottom: 8px solid transparent;
}
}
}
.word-my{
display: flex;
justify-content: flex-start;
flex-direction: row-reverse;
margin-bottom: 20px;
img{
width: 40px;
height: 40px;
border-radius: 50%;
}
.info{
width: 90%;
margin-left: 10px;
text-align: right;
.user-info{
font-size: 12px;
color: rgba(51,51,51,0.8);
margin: 0;
height: 20px;
line-height: 20px;
margin-top: -5px;
margin-right: 10px;
}
.info-content{
position: relative;
max-width: 70%;
padding: 10px;
font-size: 14px;
float: right;
margin-right: 10px;
margin-top: 8px;
background-color: #7574d9;
color: #fff;
text-align: left;
}
.info-content::after{
position: absolute;
right: -8px;
top: 8px;
content: '';
border-left: 10px solid #7574d9;
border-top: 8px solid transparent;
border-bottom: 8px solid transparent;
}
}
}
}
.chat-message{
padding: 0 50px;
.function{
margin-top: 10px;
text-align: right;
}
}
ol{
margin: 5px;
padding: 0px 10px;
counter-reset: num;
li{
counter-increment: num;
// list-style:none;
}
}
ol.example{counter-reset: num;}
ol.example li{counter-increment: num; list-style:none;}
}
</style>

View File

@ -0,0 +1,68 @@
/* eslint-disable */
export default {
'components:uploadvideo:message:xf1': `上传成功`,
'components:uploadvideo:message:xf2': `选择的视频不能在浏览器环境播放,请点击转码,等待转码成功之后再进行上传操作`,
'components:uploadvideo:message:xf3': `页面未在安全上下文中不能进行转码请联系开发人员配置SSL证书`,
'components:uploadvideo:message:xf4': `nginx未设置同源隔离不能进行转码请联系开发人员配置Nginx请求头`,
'components:uploadvideo:message:xf5': `开始转码,请勿关闭窗口或页面`,
'components:uploadvideo:message:xf6': `转码成功`,
'components:uploadvideo:message:xf7': `文件读取成功,请上传`,
'passwordReset:form:passwordCentent': `密码必须包含18 32 个字符2至少1个数字3) 至少1个大写字母4至少1个小写字母5至少1个特殊字符 (~!-@#$%^&*_+?)`,
'trials:uploadNonDicoms:action:xfuploadvideo': `上传视频文件`,
'trials:uploadNonDicoms:title:xfuploadvideo': `上传视频文件`,
'trials:uploadNonDicoms:title:xflookvideo': `查看视频文件`,
'trials:check:radio:passreason1': `线上质疑解决核查通过`,
'trials:check:radio:passreason2': `线下核查通过`,
'trials:check:radio:passreason3': `实时阅片跳过核查`,
'trials:check:radio:passreason4': `其它`,
'trials:check:radio:closereason1': `问题已解决`,
'trials:check:radio:closereason2': `问题无法解决强制关闭质疑`,
'trials:check:radio:closereason3': `其他`,
'trials:qcQuality:label:passReason': `通过原因`,
'trials:qcQuality:label:closeReason': `关闭原因`,
'trials:consistencyCheck:dialogTitle:xfpassManually': `一致性核查通过`,
'trials:consistencyCheck:dialogTitle:xfclose': `关闭一致性核查`,
'recompose:form:email': `邮箱`,
'recompose:form:role': `角色`,
'trials:consistencyCheck:label:passreason': `原因`,
'trials:consistencyCheck:label:closereason': `备注`,
'trials:trials-panel:taskAllocation': `访视任务分配`,
'trials:trials-panel:reading': `阅片`,
'trials:trials-panel:refereeAllocation': `裁判任务分配`,
'trials:trials-panel:readingTracking': `阅片跟踪`,
'trials:trials-panel:reReadingTracking': `重阅跟踪`,
'trials:trials-panel:tab:pmMedicalFeedback': `医学审核`,
'trials:trials-panel:reading:consistency-analysis': `一致性分析`,
'trials:trials-panel:attachments:reReadingTracking': `重阅审批`,
'trials:trials-panel:message:notIsHasDicom': `当前文件夹没有DICOM文件,请检查文件夹是否正确!`,
'trials:trials-panel:reading:readTaskAllocation': `阅片任务分配`,
'trials:trials-panel:attachments:spm-enrollment': `阅片人筛选`,
'trials:seletctedReviews:title:language': `语言`,
'trials:enrolledReviews:button:sow': `既往阅片情况声明`,
'trials:enrolledReviews:button:asow': `入项资格确认书`,
'trials:enrolledReviews:button:tackNum': `任务量(未读/已读/总数)`,
'DicomViewer:data:customWwc': `自定义调窗`,
'CustomWwwcForm:form:label:ww': `窗宽`,
'CustomWwwcForm:form:label:wl': `窗位`,
'trials:trials-panel:readTaskAllocation:search:ReadPeople': `阅片人`,
'trials:trials-panel:readTaskAllocation:button:AllocationRule': `分配规则`,
'trials:trials-panel:readTaskAllocation:button:ManualDistribution': `手动分配`,
'trials:trials-panel:readTaskAllocation:button:vgoTask': `访视/全局/肿瘤学阅片任务`,
'trials:trials-panel:readTaskAllocation:button:adTask': `裁判阅片任务`,
'trials:trials-panel:readTaskAllocation:table:TrialSiteCode': `中心编号`,
'trials:trials-panel:readTaskAllocation:table:SubjectCode': `受试者编号`,
'trials:trials-panel:readTaskAllocation:table:VisitTaskTypeCount': `访视`,
'trials:trials-panel:readTaskAllocation:table:GlobalTaskTypeCount': `全局`,
'trials:trials-panel:readTaskAllocation:table:JudgeTaskTypeCount': `裁判`,
'trials:trials-panel:readTaskAllocation:table:OncologyTaskTypeCount': `肿瘤学阅片`,
'trials:trials-panel:readTaskAllocation:table:R1': `阅片人(R1)`,
'trials:trials-panel:readTaskAllocation:table:R2': `阅片人(R2)`,
'trials:trials-panel:readTaskAllocation:table:AD': `裁判(AD)`,
'trials:trials-panel:readTaskAllocation:table:O1': `肿瘤阅片人(O1)`,
'trials:trials-panel:readTaskAllocation:table:operation': `操作`,
'trials:trials-panel:readTaskAllocation:table:ManualDistribution': `手动分配`,
'trials:trials-panel:readTaskAllocation:table:clearReview': `清除阅片人`,
'trials:trials-panel:readTaskAllocation:table:lookCancelHistory': `查看取消历史`,
'trials:pendingReadingTasks:button:ExistReadingApply': `存在重阅申请(项目组申请 | 独立阅片人申请)还未处理完毕,当前标准未读任务不允许开始读片`,
'trials:readingPeriod:dialogTitle:EditSubjectPR': `编辑阅片期`,
}

View File

@ -0,0 +1,145 @@
<template>
<div>
<div
v-if="!!question.GroupName && question.Type==='group'"
>
<h4 style="color: #ddd;padding: 5px 0px;margin: 0;font-size: 15px;">
{{ language==='en'?question.GroupEnName:question.GroupName }}
</h4>
</div>
<template v-else>
<el-form-item
:label="`${language==='en'?question.QuestionEnName:question.QuestionName}`"
:prop="question.Id"
:rules="[
{ required: true,
message: $t('common:ruleMessage:select'), trigger: ['blur', 'change']},
]"
:class="[question.Type==='group'?'mb':'']"
>
<!-- 下拉框 -->
<el-select
v-if="question.Type==='select'"
v-model="questionForm[question.Id]"
clearable
@change="((val)=>{formItemChange(val, question)})"
>
<template v-if="question.DictionaryCode">
<el-option
v-for="item of $d[question.DictionaryCode]"
:key="item.id"
:value="String(item.value)"
:label="item.label"
/>
</template>
<template v-else>
<el-option
v-for="val in question.TypeValue.split('|')"
:key="val"
:label="val"
:value="val"
/>
</template>
</el-select>
<!-- 单选 -->
<el-radio-group
v-if="question.Type==='radio'"
v-model="questionForm[question.Id]"
@change="((val)=>{formItemChange(val, question)})"
>
<template v-if="question.DictionaryCode">
<el-radio
v-for="item of $d[question.DictionaryCode]"
:key="item.id"
:label="String(item.value)"
>
{{ item.label }}
</el-radio>
</template>
<template v-else-if="question.TypeValue">
<el-radio
v-for="val in question.TypeValue.split('|')"
:key="val"
:label="val"
>
{{ val }}
</el-radio>
</template>
</el-radio-group>
</el-form-item>
</template>
<AssessmentFormItem
v-for="(item) in question.Childrens"
:key="item.Id"
:question="item"
:question-form="questionForm"
@setFormItemData="setFormItemData"
@resetFormItemData="resetFormItemData"
/>
</div>
</template>
<script>
import { mapGetters } from 'vuex'
export default {
name: 'AssessmentFormItem',
props: {
questionForm: {
type: Object,
default() {
return {}
}
},
question: {
type: Object,
default() {
return []
}
}
},
data() {
return {
}
},
computed: {
...mapGetters(['language'])
},
watch: {
questionForm: {
deep: true,
immediate: true,
handler(v) {
// console.log(v)
}
}
},
mounted() {
},
methods: {
formItemChange(v, question) {
if (question.Childrens.length > 0) {
this.resetChild(question.Childrens)
}
},
resetChild(obj) {
obj.forEach(i => {
this.$emit('resetFormItemData', i.Id)
if (i.Childrens && i.Childrens.length > 0) {
this.resetChild(i.Childrens)
}
})
},
resetFormItemData(v) {
this.$emit('resetFormItemData', v)
},
setFormItemData(obj) {
this.$emit('setFormItemData', obj)
}
}
}
</script>
<style lang="scss" scoped>
.mb{
margin-bottom: 0px;
}
</style>

View File

@ -0,0 +1,822 @@
<template>
<BaseContainer>
<!-- <template slot="search-container">
<el-button
v-hasPermi="['trials:trials-panel:enrolled-reviewers:list:enroll']"
icon="el-icon-plus"
type="primary"
style="margin-left: auto"
@click="handleEnroll"
>Enroll</el-button>
</template> -->
<template slot="main-container">
<!-- 入组医生列表 -->
<el-table
ref="myTable"
v-loading="listLoading"
v-adaptive="{bottomOffset:55}"
:data="list"
stripe
height="100"
@sort-change="handleSortByColumn"
>
<el-table-column type="index" width="40" />
<!-- Name -->
<el-table-column
:label="$t('trials:enrolledReviews:table:name')"
show-overflow-tooltip
width="150"
prop="FirstName"
sortable="custom"
>
<template slot-scope="scope">
<router-link
style="color: #428bca;"
tag="a"
:to="{
path: `/trialsResume?doctorId=${scope.row.DoctorId}&token=${token}`,
}"
target="_blank"
>{{ scope.row.LastName }} / {{ scope.row.FirstName }}</router-link>
</template>
</el-table-column>
<!-- Name CN -->
<el-table-column
prop="ChineseName"
sortable="custom"
:label="$t('trials:enrolledReviews:table:nameCN')"
width="120"
show-overflow-tooltip
/>
<!-- 用户名 -->
<el-table-column
prop="UserName"
:label="$t('trials:enrolledReviews:table:userName')"
width="150"
sortable="custom"
show-overflow-tooltip
/>
<!-- 入组时间 -->
<el-table-column
prop="EnrollTimeStr"
:label="$t('trials:enrolledReviews:table:enrollmentTime')"
width="200"
sortable="custom"
show-overflow-tooltip
>
<template slot-scope="scope">
<span v-if="scope.row.EnrollTime">
{{ `${scope.row.EnrollTime}` }}
</span>
</template>
</el-table-column>
<el-table-column
prop="EnrollTimeStr"
:label="$t('trials:enrolledReviews:table:status')"
width="100"
sortable="custom"
>
<template slot-scope="scope">
<div>
<el-switch
v-model="scope.row.IsEnable"
:active-value="true"
:inactive-value="false"
@change="(v) => {return isEnableChange(scope.row, v)}"
/>
<span>{{$fd('IsEnable', scope.row.IsEnable)}}</span>
</div>
</template>
</el-table-column>
<el-table-column
v-for="ite of TrialReadingCriterionList"
:label="ite.TrialReadingCriterionName"
width="360"
header-align="center"
show-overflow-tooltip
>
<el-table-column
:label="$t('trials:enrolledReviews:table:readingType')"
width="300"
show-overflow-tooltip
>
<template slot-scope="scope">
<span v-for="item of scope.row.CriterionCategoryList.find(v => {return v.TrialReadingCriterionId === ite.TrialReadingCriterionId}) ? scope.row.CriterionCategoryList.find(v => {return v.TrialReadingCriterionId === ite.TrialReadingCriterionId}).ReadingCategorys : []">
<el-tag v-if="item === 1" type="primary">{{ $fd('ReadingCategory', item) + ' & ' + $fd('ReadingCategory', 2) }}</el-tag>
<!-- <el-tag v-if="item === 2" type="info">{{ $fd('ReadingCategory', item) }}</el-tag>-->
<el-tag v-if="item === 4" type="danger">{{ $fd('ReadingCategory', item) }}</el-tag>
<el-tag v-if="item === 5" type="warning">{{ $fd('ReadingCategory', item) }}</el-tag>
</span>
<el-button
v-hasPermi="['trials:trials-panel:enrolled-reviewers:list:edit']"
type="text"
@click="openSetEnrollReadingCategory(scope.row, ite.TrialReadingCriterionId)"
>
{{ $t('trials:enrolledReviews:button:config') }}
</el-button>
</template>
</el-table-column>
<el-table-column
:label="$t('trials:enrolledReviews:message:SOW')"
width="160"
show-overflow-tooltip
>
<template slot-scope="scope">
<div v-if="scope.row.CriterionCategoryList.find(v => {return v.TrialReadingCriterionId === ite.TrialReadingCriterionId}).StatementCriterionFileList.length > 0">
<el-button type="text" @click="windowOpen(scope.row.CriterionCategoryList.find(v => {return v.TrialReadingCriterionId === ite.TrialReadingCriterionId}).StatementCriterionFileList[0].FilePath)">View</el-button>
</div>
<div v-else>
<span>{{ $t('trials:enrolledReviews:label:notUpload') }}</span>
<!-- 上传 -->
<el-button
v-if="ite.CriterionType === 0"
v-hasPermi="['trials:trials-panel:enrolled-reviewers:list:edit']"
type="text"
style="margin-left: 10px"
@click="addCol(0, scope.row, ite, $t('trials:enrolledReviews:message:SOW'))"
>
{{ $t('trials:enrolledReviews:button:upload') }}
</el-button>
</div>
</template>
</el-table-column>
<el-table-column
:label="$t('trials:enrolledReviews:message:EQC')"
width="160"
show-overflow-tooltip
>
<template slot-scope="scope">
<div v-if="scope.row.CriterionCategoryList.find(v => {return v.TrialReadingCriterionId === ite.TrialReadingCriterionId}).AcknowledgementCriterionFileList.length > 0">
<el-button type="text" @click="windowOpen(scope.row.CriterionCategoryList.find(v => {return v.TrialReadingCriterionId === ite.TrialReadingCriterionId}).AcknowledgementCriterionFileList[0].FilePath)">View</el-button>
</div>
<div v-else>
<span>{{ $t('trials:enrolledReviews:label:notUpload') }}</span>
<!-- 上传 -->
<el-button
v-if="ite.CriterionType === 0"
v-hasPermi="['trials:trials-panel:enrolled-reviewers:list:edit']"
type="text"
style="margin-left: 10px"
@click="addCol(1, scope.row, ite, $t('trials:enrolledReviews:message:SOW'))"
>
{{ $t('trials:enrolledReviews:button:upload') }}
</el-button>
</div>
</template>
</el-table-column>
<el-table-column
:label="$t('trials:enrolledReviews:button:tackNum')"
width="300"
show-overflow-tooltip
>
<template slot-scope="scope">
<div>
{{ scope.row.CriterionCategoryList.find(v => {return v.TrialReadingCriterionId === ite.TrialReadingCriterionId}).PendingCount }}
/
{{ scope.row.CriterionCategoryList.find(v => {return v.TrialReadingCriterionId === ite.TrialReadingCriterionId}).ComplectedCount }}
/
{{ scope.row.CriterionCategoryList.find(v => {return v.TrialReadingCriterionId === ite.TrialReadingCriterionId}).TotalCount }}
</div>
</template>
</el-table-column>
</el-table-column>
</el-table>
</template>
<!-- 上传SOW -->
<base-model :config="sow_model">
<template slot="dialog-body">
<upload-acksow
ref="uploadAckSow"
:trial-id="listQuery.TrialId"
:doctor-id="doctorId"
@getFileList="getFileList"
/>
</template>
</base-model>
<!-- 修改 Reading Type -->
<base-model :config="readingType_model">
<template slot="dialog-body">
<!-- Reading Type -->
<label>{{ $t('trials:enrolledReviews:table:readingType') }}: </label>
<el-select
v-model="readingType"
filterable
allow-create
default-first-option
style="width: 70%"
>
<el-option :value="0" label="TP&GL&AD" />
<el-option :value="1" label="TP&GL" />
<el-option :value="2" label="AD" />
</el-select>
</template>
<template slot="dialog-footer">
<el-button
:disabled="readingType_model.btnLoading"
type="primary"
@click="readingType_model.visible = false"
>
{{ $t('common:button:cancel') }}
</el-button>
<el-button
type="primary"
:loading="readingType_model.btnLoading"
@click="handleUpdateReadingType"
>
{{ $t('common:button:save') }}
</el-button>
</template>
</base-model>
<!-- 修改 readingCategory -->
<base-model :config="readingCategory_model">
<template slot="dialog-body">
<!-- Reading Type -->
<label>{{ $t('trials:enrolledReviews:table:readingType') }}: </label>
<el-select
v-model="ReadingCategorys"
filterable
allow-create
multiple
default-first-option
style="width: 70%"
>
<el-option v-for="item of $d.ReadingCategory" v-if="item.value === 1" :key="item.id" :value="1" :label="$fd('ReadingCategory', 1) + ' & ' + $fd('ReadingCategory', 2)" />
<el-option v-for="item of $d.ReadingCategory" v-if="item.value === 4 && IsArbitrationReading" :key="item.id" :value="item.value" :label="$fd('ReadingCategory', 4)" />
<el-option v-for="item of $d.ReadingCategory" v-if="item.value === 5 && IsOncologyReading" :key="item.id" :value="item.value" :label="$fd('ReadingCategory', 5)" />
</el-select>
</template>
<template slot="dialog-footer">
<el-button
:disabled="readingCategory_model.btnLoading"
type="primary"
@click="readingCategory_model.visible = false"
>
{{ $t('common:button:cancel') }}
</el-button>
<el-button
type="primary"
:loading="readingCategory_model.btnLoading"
@click="handleUpdateReadingCategory"
>
{{ $t('common:button:save') }}
</el-button>
</template>
</base-model>
<!-- 修改医生状态 -->
<base-model :config="reviewerStatus_model">
<template slot="dialog-body">
<el-form
ref="updateStatusForm"
:model="updateStatusForm"
:rules="updateStatusFormrules"
>
<el-form-item :label="$t('trials:enrolledReviews:label:enrollmentStatus')" prop="Status">
<el-select
v-model="updateStatusForm.Status"
style="width:60%;"
>
<!-- 0回退1出组 -->
<el-option v-for="item of $d.OutOrInEnrollment" :key="`OutOrInEnrollment${item.value}`" :value="item.value" :label="item.label" :disabled="item.value === 0 && currentWorkload>0" />
</el-select>
</el-form-item>
<el-form-item
v-if="updateStatusForm.Status== 1"
:label="$t('trials:enrolledReviews:label:outOfEnrollmentTime')"
prop="OutEnrollmentTime"
>
<el-date-picker
v-model="updateStatusForm.OutEnrollmentTime"
type="date"
value-format="yyyy-MM-dd"
format="yyyy-MM-dd"
/>
</el-form-item>
</el-form>
</template>
<template slot="dialog-footer">
<el-button
:disabled="reviewerStatus_model.btnLoading"
type="primary"
@click="reviewerStatus_model.visible = false"
>
{{ $t('common:button:cancel') }}
</el-button>
<el-button
type="primary"
:loading="reviewerStatus_model.btnLoading"
@click="handleUpdateReviewerStatus"
>
{{ $t('common:button:save') }}
</el-button>
</template>
</base-model>
<BaseModel :config="model_cfg">
<template slot="dialog-body">
<el-form
ref="DictionaryTypeConfigForm"
:model="form"
label-width="120px"
size="small"
>
<el-form-item label="Criterion Type " prop="CriterionType">
<el-input v-model="form.CriterionName" disabled />
<!-- <el-select v-model="form.CriterionType">-->
<!-- <el-option-->
<!-- v-for="item of $d.CriterionType"-->
<!-- :key="item.id"-->
<!-- :value="item.value"-->
<!-- :label="item.label"-->
<!-- />-->
<!-- </el-select>-->
</el-form-item>
<el-form-item label="File" prop="FilePath">
<el-upload
class="upload-demo"
action
:before-upload="beforeUpload"
:http-request="handleUploadFile"
:on-preview="handlePreview2"
:on-remove="handleRemoveFile2"
:show-file-list="true"
:limit="1"
:file-list="fileList"
>
<el-button size="small" type="primary" :disabled="fileList.length > 0">点击上传</el-button>
</el-upload>
</el-form-item>
</el-form>
</template>
<template slot="dialog-footer">
<el-button :disabled="btnLoading" size="small" type="primary" @click="handleCancle">Cancel</el-button>
<el-button size="small" type="primary" :loading="btnLoading" @click="handleSave">Save</el-button>
</template>
</BaseModel>
</BaseContainer>
</template>
<script>
import {
getTrialEnrollmentWorkloadStats,
deleteReviewerAckSOW,
updateReviewerReadingType,
enrollBackOrOut,
setEnrollReadingCategory
} from '@/api/trials'
import { addDoctorCriterionFile } from '@/api/reviewers'
import { uploadFile } from '@/api/attachment'
import { updateTrialReviewerState } from '@/api/trials/reading'
import UploadAcksow from './components/UploadAcksow'
import store from '@/store'
import BaseContainer from '@/components/BaseContainer'
import BaseModel from '@/components/BaseModel'
export default {
components: { UploadAcksow, BaseContainer, BaseModel },
data() {
return {
list: [],
TrialReadingCriterionId: null,
listQuery: {
TrialId: '',
PageIndex: 1,
PageSize: 100,
Asc: false,
SortField: ''
},
doctorId: '',
currentRow: {},
listLoading: false,
total: 0,
trialId: '',
currentClickRowData: '',
btnLoading: false,
readingType: '',
model_cfg: { visible: false, showClose: true, width: '600px', title: '', appendToBody: true },
sow_model: {
visible: false,
title: this.$t('trials:enrolledReviews:dialogTitle:signedAckOfSow'),
showClose: true,
width: '500px'
},
readingType_model: {
visible: false,
title: this.$t('trials:enrolledReviews:dialogTitle:changeReadingType'),
showClose: true,
width: '500px',
btnLoading: false
},
readingCategory_model: {
visible: false,
title: this.$t('trials:enrolledReviews:dialogTitle:changeReadingType'),
showClose: true,
width: '500px',
btnLoading: false
},
reviewerStatus_model: {
visible: false,
title: this.$t('trials:enrolledReviews:dialogTitle:changeStatus'),
showClose: true,
width: '500px',
btnLoading: false
},
updateStatusForm: {
Status: '',
OutEnrollmentTime: ''
},
updateStatusFormrules: {
Status: [{ required: true, message: this.$t('common:ruleMessage:select'), trigger: ['blur', 'change'] }],
OutEnrollmentTime: [{ required: true, message: this.$t('common:ruleMessage:select'), trigger: ['blur', 'change'] }]
},
token: store.getters.token,
ReadingCategorys: [],
apiReadingCategorys: [],
TrialReadingCriterionList: [],
IsArbitrationReading: true,
IsOncologyReading: true,
fileList: [],
form: {}
}
},
computed: {
currentWorkload() {
if (Object.keys(this.currentRow).length === 0) return 0
var total = this.currentRow.Adjudication + this.currentRow.AdjudicationIn24H + this.currentRow.AdjudicationIn48H + this.currentRow.Timepoint + this.currentRow.TimepointIn24H + this.currentRow.TimepointIn48H + this.currentRow.TrainingTimes + this.currentRow.RefresherTraining + this.currentRow.Global + this.currentRow.Downtime
return total
}
},
mounted() {
this.trialId = this.$route.query.trialCode
this.listQuery.TrialId = this.$route.query.trialId
this.initPage()
},
methods: {
beforeUpload() {
if (this.fileList.length > 0) {
// 只允许上传1个文件
this.$alert(this.$t('trials:enrolledReviews:message:uploadSowWaring1'))
return
}
},
handleSave() {
this.$refs['DictionaryTypeConfigForm'].validate(valid => {
if (!valid) return
addDoctorCriterionFile(this.form).then(res => {
// 保存成功
this.$message.success(this.$t('common:message:savedSuccessfully'))
this.initPage()
this.handleCancle()
})
})
},
handlePreview3(row) {
if (row.FilePath) {
window.open(row.FilePath, '_blank')
}
},
handlePreview2(row, r2) {
if (row.fullPath) {
window.open(row.fullPath, '_blank')
}
},
handleUploadFile(param) {
this.btnLoading = true
const formData = new FormData()
formData.append('file', param.file)
this.form.FileName = param.file.name
// this.form.FileType === 0 ? '既往阅片情况声明' : '入项资格确认书'
uploadFile(param.file, this.form.FileType === 0 ? this.$t('trials:enrolledReviews:message:SOW') : this.$t('trials:enrolledReviews:message:EQC'), this.form.DoctorId).then(res => {
this.form.FilePath = res.Result.FilePath
this.btnLoading = false
this.fileList[0] = { name: param.file.name, path: res.Result.FilePath, fullPath: res.Result.FullFilePath }
})
},
handleCancle() {
this.model_cfg.visible = false
},
handleRemoveFile2() {
this.form.FileName = null
this.form.FilePath = null
this.fileList = []
},
addCol(fileType, row, item) {
this.model_cfg.visible = true
this.form = {
Remark: null,
FileType: fileType,
FileName: null,
FilePath: null,
IsEnable: true,
TrialId: this.listQuery.TrialId,
DoctorId: row.DoctorId,
CriterionType: item.CriterionType,
CriterionName: item.TrialReadingCriterionName,
TrialReadingCriterionId: item.TrialReadingCriterionId
}
this.handleRemoveFile2()
this.model_cfg.title = fileType === 0 ? this.$t('trials:enrolledReviews:message:SOW') : this.$t('trials:enrolledReviews:message:EQC')// '既往阅片情况说明' : '入项资格确认书'
},
windowOpen(url) {
console.log(url)
window.open(url)
},
isEnableChange(row, v) {
console.log(row)
this.listLoading = true
updateTrialReviewerState({
TrialId: this.listQuery.TrialId,
IsEnable: v,
EnrollId: row.EnrollId
}).then(res => {
// '保存成功'
this.$message.success(this.$t('common:message:savedSuccessfully'))
this.listLoading = false
}).catch(() => {
this.listLoading = false
})
},
handleUpdateReadingCategory() {
this.readingCategory_model.btnLoading = true
this.readingCategory_model.showClose = false
var params = {
TrialId: this.$route.query.trialId,
EnrollId: this.currentRow.EnrollId,
ReadingCategorys: Object.assign([], this.ReadingCategorys),
TrialReadingCriterionId: this.TrialReadingCriterionId
}
if (~this.ReadingCategorys.indexOf(1)) {
params.ReadingCategorys.push(2)
}
setEnrollReadingCategory(params)
.then(res => {
this.readingCategory_model.visible = false
this.readingCategory_model.btnLoading = false
this.readingCategory_model.showClose = true
this.initPage()
this.$message.success(this.$t('common:message:savedSuccessfully'))
}).catch(_ => {
this.readingCategory_model.btnLoading = false
this.readingCategory_model.showClose = true
})
},
openSetEnrollReadingCategory(row, TrialReadingCriterionId) {
// this.ReadingCategorys = []
console.log(row)
this.currentRow = row
this.TrialReadingCriterionId = TrialReadingCriterionId
this.readingCategory_model.visible = true
var o = row.CriterionCategoryList.find(v => {
return v.TrialReadingCriterionId === TrialReadingCriterionId
}) ? row.CriterionCategoryList.find(v => {
return v.TrialReadingCriterionId === TrialReadingCriterionId
}) : null
var i = row.TrialReadingCriterionList.find(v => {
return v.TrialReadingCriterionId === TrialReadingCriterionId
}) ? row.TrialReadingCriterionList.find(v => {
return v.TrialReadingCriterionId === TrialReadingCriterionId
}) : null
if (i) {
this.IsArbitrationReading = i.IsArbitrationReading
this.IsOncologyReading = i.IsOncologyReading
}
// index不能等于-1(删最后一个元素)和空(删第一个元素)
this.ReadingCategorys = Object.assign([], o.ReadingCategorys)
this.currentRow.TrialReadingCriterionId = TrialReadingCriterionId
var index = o ? o.ReadingCategorys.indexOf(2) : -1
if (index >= 0) {
this.ReadingCategorys.splice(index, 1)
}
},
// 获取入组医生列表数据
initPage() {
this.listLoading = true
getTrialEnrollmentWorkloadStats(this.listQuery).then((res) => {
this.listLoading = false
this.list = res.Result.CurrentPageData
if (this.list.length > 0) {
this.TrialReadingCriterionList = this.list[0].TrialReadingCriterionList
}
this.total = res.Result.TotalCount
this.$nextTick(() => {
// myTable是表格的ref属性值
if (this.$refs.myTable && this.$refs.myTable.doLayout) {
this.$refs.myTable.doLayout()
}
})
}).catch(() => {
this.listLoading = false
})
},
// Enroll按钮回调
handleEnroll() {
this.$router.push({
path: `/trials/trials-panel/enrolled-reviewers/enroll?trialId=${this.listQuery.TrialId}&trialCode=${this.trialId}`
})
},
// 打开修改状态模态框
changeStatus(row) {
this.reviewerStatus_model.visible = true
this.currentRow = row
},
// 更新医生状态信息
handleUpdateReviewerStatus() {
this.$refs.updateStatusForm.validate(valid => {
if (!valid) return
this.reviewerStatus_model.btnLoading = true
this.reviewerStatus_model.showClose = false
enrollBackOrOut(this.listQuery.TrialId, this.currentRow.DoctorId, this.updateStatusForm.Status)
.then(res => {
this.$nextTick(() => {
this.$refs['updateStatusForm'].resetFields()
})
this.reviewerStatus_model.btnLoading = false
this.reviewerStatus_model.visible = false
this.reviewerStatus_model.showClose = true
this.initPage()
this.$message.success(this.$t('common:message:savedSuccessfully'))
})
.catch(() => {
this.reviewerStatus_model.btnLoading = false
this.reviewerStatus_model.showClose = true
})
})
},
// 打开修改reading type模态框
handleConfigReadingType(row) {
this.readingType_model.visible = true
const { ReviewerReadingType } = row
this.readingType = ReviewerReadingType
this.currentRow = row
},
// 更新 reading type
handleUpdateReadingType() {
this.readingType_model.btnLoading = true
this.readingType_model.showClose = false
updateReviewerReadingType(this.listQuery.TrialId, this.currentRow.DoctorId, this.readingType)
.then(res => {
this.readingType_model.visible = false
this.readingType_model.btnLoading = false
this.readingType_model.showClose = true
this.initPage()
this.$message.success(this.$t('common:message:savedSuccessfully'))
}).catch(_ => {
this.readingType_model.btnLoading = false
this.readingType_model.showClose = true
})
},
// 排序
handleSortByColumn(column) {
if (column.order === 'ascending') {
this.listQuery.Asc = true
} else {
this.listQuery.Asc = false
}
column.prop === 'EnrollTimeStr'
? (this.listQuery.SortField = 'EnrollTime')
: (this.listQuery.SortField = column.prop)
this.listQuery.PageIndex = 1
this.initPage()
},
// 医生详情页跳转
handleReviewerDetail(row) {
const { href } = this.$router.resolve({ path: `/trials/trials-panel/enrolled-reviewers/resume?doctorId=${row.DoctorId}` })
window.open(href, '_blank')
},
// 上传SOW
handleUploadAckSow(row) {
this.doctorId = row.DoctorId
this.currentRow = row
this.sow_model.visible = true
let fileList = []
if (row.AgreementId !== '00000000-0000-0000-0000-000000000000') {
fileList = [
{
DoctorId: row.DoctorId,
name: row.AgreementFileName,
FileName: row.AgreementFileName,
Path: row.AgreementPath,
FullPath: row.AgreementFullPath,
Type: 'ACK of SOW'
}
]
}
this.$nextTick(function() {
this.$refs.uploadAckSow.initFileList(fileList)
})
},
// 获取上传控件种SOW文件列表
getFileList(fileList) {
this.currentRow.AgreementId =
fileList.length > 0
? fileList[0].Id
: '00000000-0000-0000-0000-000000000000'
this.currentRow.AgreementFileName =
fileList.length > 0 ? fileList[0].FileName : ''
this.currentRow.AgreementFullPath =
fileList.length > 0 ? fileList[0].FullPath : ''
this.currentRow.AgreementPath =
fileList.length > 0 ? fileList[0].Path : ''
},
// 预览SOW
handleViewAckSow(row) {
row.AgreementFullPath ? window.open(row.AgreementFullPath, '_blank') : ''
},
// 删除SOW文件
handleDeleteAckSOW(row) {
this.$confirm(this.$t('trials:enrolledReviews:message:deleteSOWWarning'), {
type: 'warning',
distinguishCancelAndClose: true
})
.then(() => {
deleteReviewerAckSOW(
this.listQuery.TrialId,
row.DoctorId,
row.AgreementId
).then((res) => {
row.AgreementId = '00000000-0000-0000-0000-000000000000'
row.AgreementFileName = ''
row.AgreementFullPath = ''
row.AgreementPath = ''
this.$message.success(this.$t('common:message:savedSuccessfully'))
})
})
.catch((action) => {})
},
// 跳转工作量统计页
handleStats(row) {
this.currentClickRowData = row
if (row.AgreementId === '00000000-0000-0000-0000-000000000000') {
// 'Ack-Sow is not uploaded, continue'
this.$confirm(this.$t('trials:enrolledReviews:message:statsWarning'), {
type: 'warning',
distinguishCancelAndClose: true
})
.then(() => {
this.handleTipCloseOK()
})
.catch(_ => {})
} else {
this.handleTipCloseOK()
}
},
handleTipCloseOK() {
zzSessionStorage.setItem(
'ReviewerName',
this.currentClickRowData.FirstName +
' ' +
this.currentClickRowData.LastName
)
this.$router.push({
path: `/trials/trials-panel/enrolled-reviewers/stats?doctorId=${this.currentClickRowData.DoctorId}&trialId=${this.listQuery.TrialId}&trialCode=${this.trialId}&doctorName=${this.currentClickRowData.LastName}/${this.currentClickRowData.FirstName}`
})
},
// 合计行
getSummaries(param) {
const { columns, data } = param
const sums = []
columns.forEach((column, index) => {
if (index === 1) {
sums[index] = 'Total (Current Page)'
return
}
const values = data.map((item) => Number(item[column.property]))
if (!values.every((value) => isNaN(value))) {
sums[index] = values.reduce((prev, curr) => {
const value = Number(curr)
if (!isNaN(value)) {
return prev + curr
} else {
return prev
}
}, 0)
}
})
return sums
}
}
}
</script>
<style lang="scss">
.enroll-list {
height: 100%;
.filter-box {
display: flex;
align-items: center;
padding: 5px 0px;
}
}
</style>

View File

@ -0,0 +1,354 @@
<template>
<BaseContainer>
<!-- 搜索框 -->
<template slot="search-container">
<el-form :inline="true">
<el-form-item label="器官类型">
<el-select v-model="searchData.OrganType" clearable style="width:130px;">
<el-option v-for="item of $d.OrganType" :key="item.id" :value="item.value" :label="item.label" />
</el-select>
</el-form-item>
<el-form-item label="部位">
<el-input v-model="searchData.Part" clearable style="width:130px;"/>
</el-form-item>
<el-form-item label="器官">
<el-input v-model="searchData.TULOC" clearable style="width:130px;"/>
</el-form-item>
<el-form-item label="是否淋巴结">
<el-select v-model="searchData.IsLymphNodes" clearable style="width:130px;">
<el-option v-for="item of $d.YesOrNo" :key="item.id" :value="item.value" :label="item.label" />
</el-select>
</el-form-item>
<el-form-item>
<el-button type="primary" icon="el-icon-search" size="small" @click="handleSearch">
{{ $t('common:button:search') }}
</el-button>
<el-button type="primary" icon="el-icon-refresh-left" size="small" @click="handleReset">
{{ $t('common:button:reset') }}
</el-button>
</el-form-item>
</el-form>
<span style="margin-left:auto;">
<el-button
v-if="!isConfirm && !isFromSystem"
type="primary"
icon="el-icon-plus"
size="small"
@click="handleSet"
>
设置
</el-button>
</span>
</template>
<template slot="main-container">
<!-- 受试者列表 -->
<el-table
ref="organList"
v-loading="loading"
v-adaptive="{bottomOffset:60}"
:data="list"
stripe
height="100"
>
<el-table-column type="index" width="90" />
<!-- 类型 -->
<el-table-column
prop="OrganType"
label="器官类型"
show-overflow-tooltip
>
<template slot-scope="scope">
{{ $fd('OrganType',scope.row.OrganType) }}
</template>
</el-table-column>
<!-- 部位 -->
<el-table-column
prop="Part"
label="部位"
show-overflow-tooltip
/>
<el-table-column
prop="PartEN"
label="部位(英文)"
show-overflow-tooltip
/>
<el-table-column
prop="TULOC"
label="器官"
show-overflow-tooltip
/>
<el-table-column
prop="TULOCEN"
label="器官(英文)"
show-overflow-tooltip
/>
<el-table-column
prop="TULAT"
label="位置"
show-overflow-tooltip
/>
<el-table-column
prop="TULATEN"
label="位置(英文)"
show-overflow-tooltip
/>
<el-table-column
prop="IsLymphNodes"
label="是否淋巴结"
show-overflow-tooltip
>
<template slot-scope="scope">
<el-tag v-if="scope.row.IsLymphNodes" type="danger">{{ $fd('ReadingYesOrNo', scope.row.IsLymphNodes) }}</el-tag>
<el-tag v-else type="primary">{{ $fd('ReadingYesOrNo', scope.row.IsLymphNodes) }}</el-tag>
</template>
</el-table-column>
<el-table-column
prop="IsCanEditPosition"
label="是否可以编辑"
show-overflow-tooltip
sortable="custom"
>
<template slot-scope="scope">
<el-tag v-if="scope.row.IsCanEditPosition" type="danger">
{{ $fd('YesOrNo', scope.row.IsCanEditPosition) }}
</el-tag>
<el-tag v-else type="primary">{{ $fd('YesOrNo', scope.row.IsCanEditPosition) }}</el-tag>
</template>
</el-table-column>
<el-table-column
prop="Remark"
label="备注"
show-overflow-tooltip
/>
<el-table-column
prop="IsEnable"
label="是否显示"
show-overflow-tooltip
>
<template slot-scope="scope">
<el-switch
v-if="!isConfirm"
v-model="scope.row.IsEnable"
@change="changeEnableStatus($event, scope.row)"
/>
<span v-else>{{ $fd('YesOrNo',scope.row.IsEnable) }}</span>
</template>
</el-table-column>
</el-table>
</template>
<!-- 选择标准 -->
<el-dialog
v-if="criterion.visible"
:visible.sync="criterion.visible"
:close-on-click-modal="false"
:title="criterion.title"
width="500px"
custom-class="base-dialog-wrapper"
append-to-body
>
<el-form
ref="criterionForm"
v-loading="formLoading"
:model="form"
size="small"
>
<div class="base-dialog-body">
<!-- 类型 -->
<el-form-item
label="标准"
prop="CriterionId"
:rules="[
{ required: true, message: this.$t('common:ruleMessage:select'), trigger: ['blur'] },
]"
>
<el-select
v-model="form.CriterionId"
>
<el-option
v-for="item of criterions"
:key="item.CriterionId"
:label="item.CriterionName"
:value="item.CriterionId"
/>
</el-select>
</el-form-item>
</div>
<div class="base-dialog-footer" style="text-align:right;margin-top:10px;">
<el-form-item style="text-align:right;">
<el-button type="primary" @click="criterion.visible = false"> {{ $t('common:button:cancel') }}</el-button>
<el-button size="small" type="primary" @click="handleSave">
{{ $t('common:button:save') }}
</el-button>
</el-form-item>
</div>
</el-form>
</el-dialog>
</BaseContainer>
</template>
<script>
import { getTrialOrganList, deleteTrialOrganInfo, getSystemCriterionList, synchronizeSystemOrganToTrial, setOrganIsEnable } from '@/api/trials'
import BaseContainer from '@/components/BaseContainer'
const searchDataDefault = () => {
return {
OrganType: null,
TULOC: null,
TULAT: null,
IsLymphNodes: null,
}
}
export default {
name: 'OrgansList',
components: { BaseContainer },
props: {
TrialReadingCriterionId: {
type: String,
required: true
},
isFromSystem: {
type: Boolean,
required: true
},
isConfirm: {
type: Boolean,
required: true
}
},
data() {
return {
searchData: searchDataDefault(),
loading: false,
list: [],
total: 0,
trialId: '',
criterion: { title: '设置', visible: false },
formLoading: false,
form: { CriterionId: '' },
criterions: []
}
},
watch: {
list() {
this.$nextTick(() => {
this.$refs.organList.doLayout()
})
}
},
mounted() {
this.getCriterions()
this.getList()
},
methods: {
// 获取受试者列表
getList() {
this.loading = true
this.searchData.TrialId = this.$route.query.trialId
this.searchData.TrialReadingCriterionId = this.TrialReadingCriterionId
getTrialOrganList(this.searchData).then(res => {
this.loading = false
this.list = res.Result
// this.list = res.Result.CurrentPageData
// this.total = res.Result.TotalCount
}).catch(() => { this.loading = false })
},
// 新增
handleAdd() {
this.addDialog.visible = true
},
changeEnableStatus(callback, row) {
if (callback) {
row.IsEnable = false
} else {
row.IsEnable = true
}
var message = '是否确认更改?'
this.$confirm(message, {
distinguishCancelAndClose: true,
type: 'warning'
}).then(() => {
const state = !row.IsEnable
this.loading = true
var params = {
isEnable: state,
ids: [row.Id]
}
setOrganIsEnable(params).then(res => {
this.loading = false
if (res.IsSuccess) {
this.$message.success(this.$t('common:message:savedSuccessfully'))
this.getList()
}
}).catch(() => { this.loading = false })
}).catch(() => {})
},
// 删除
handleDelete(row) {
this.$confirm('是否确认删除', {
type: 'warning',
distinguishCancelAndClose: true
})
.then(() => {
this.loading = true
deleteTrialOrganInfo(row.Id)
.then(res => {
this.loading = false
if (res.IsSuccess) {
this.getList()
this.$message.success('删除成功!')
}
}).catch(() => { this.loading = false })
})
},
// 查询
handleSearch() {
// this.searchData.PageIndex = 1
this.getList()
},
// 重置
handleReset() {
this.searchData = searchDataDefault()
this.getList()
},
// 排序
handleSortByColumn(column) {
if (column.order === 'ascending') {
this.searchData.Asc = true
} else {
this.searchData.Asc = false
}
this.searchData.SortField = column.prop
this.searchData.PageIndex = 1
this.getList()
},
getCriterions() {
getSystemCriterionList().then(res => {
this.criterions = res.Result
})
},
handleSet() {
this.criterion.visible = true
this.form.CriterionId = ''
},
handleSave() {
this.$refs['criterionForm'].validate((valid) => {
if (!valid) return
this.formLoading = true
var param = {
trialId: this.$route.query.trialId,
systemCriterionId: this.form.CriterionId
}
synchronizeSystemOrganToTrial(param).then(res => {
this.getList()
this.criterion.visible = false
this.formLoading = false
}).catch(() => {
this.formLoading = false
})
})
}
}
}
</script>

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,392 @@
<template>
<div v-loading="loading">
<div
class="search-form"
style="text-align: left;"
>
<!-- 新增 -->
<el-button
v-if="!isConfirm && hasPermi(['trials:trials-panel:setting:reading-unit:edit']) && !isFromSystem"
size="mini"
type="primary"
@click="handleAdd"
>
{{ $t('common:button:add') }}
</el-button>
<!-- 预览 -->
<el-button
v-if="isPreview"
:disabled="tblList.length===0"
size="mini"
type="primary"
@click="handlePreview"
>
{{ $t('common:button:preview') }}
</el-button>
</div>
<el-table
:data="tblList"
size="small"
>
<el-table-column
prop="ShowOrder"
label=""
width="50"
/>
<!-- 分组 -->
<el-table-column
prop="GroupName"
v-if="$i18n.locale === 'zh'"
:label="$t('trials:readingUnit:qsList:title:groupNameEn')"
show-overflow-tooltip
/>
<!-- 分组(EN) -->
<el-table-column
prop="GroupEnName"
v-if="$i18n.locale === 'en'"
:label="$t('trials:readingUnit:qsList:title:groupNameEn')"
show-overflow-tooltip
/>
<!-- 名称 -->
<el-table-column
prop="QuestionName"
v-if="$i18n.locale === 'zh'"
:label="$t('trials:readingUnit:qsList:title:qsNameEn')"
show-overflow-tooltip
/>
<!-- 名称(EN) -->
<el-table-column
prop="QuestionEnName"
v-if="$i18n.locale === 'en'"
:label="$t('trials:readingUnit:qsList:title:qsNameEn')"
show-overflow-tooltip
/>
<!-- 题型 -->
<el-table-column
prop="Type"
:label="$t('trials:readingUnit:qsList:title:type')"
show-overflow-tooltip
>
<template slot-scope="scope">
{{ $fd('Criterion_Question_Type',scope.row.Type) }}
</template>
</el-table-column>
<!-- 是否必填 -->
<el-table-column
prop="IsRequired"
:label="$t('trials:readingUnit:qsList:title:isRequired')"
show-overflow-tooltip
>
<template slot-scope="scope">
{{ $fd('QuestionRequired',scope.row.IsRequired) }}
</template>
</el-table-column>
<!-- 是否显示 -->
<el-table-column
prop="ShowQuestion"
:label="$t('trials:readingUnit:qsList:title:isShow')"
show-overflow-tooltip
>
<template slot-scope="scope">
{{ $fd('ShowQuestion',scope.row.ShowQuestion) }}
</template>
</el-table-column>
<!-- 是否裁判问题 -->
<el-table-column
prop="IsJudgeQuestion"
:label="$t('trials:readingUnit:qsList:title:isJudgeQuestion')"
width="120"
show-overflow-tooltip
>
<template slot-scope="scope">
{{ $fd('YesOrNo', scope.row.IsJudgeQuestion) }}
</template>
</el-table-column>
<!-- 是否在阅片页面显示 -->
<el-table-column
prop="IsShowInDicom"
:label="$t('trials:readingUnit:qsList:title:isShowInDicom')"
width="140"
show-overflow-tooltip
>
<template slot-scope="scope">
{{ $fd('YesOrNo', scope.row.IsShowInDicom) }}
</template>
</el-table-column>
<!-- 是否在全局阅片显示 -->
<el-table-column
prop="GlobalReadingShowType"
:label="$t('trials:readingUnit:qsList:title:globalReadingShowType')"
width="160"
show-overflow-tooltip
>
<template slot-scope="scope">
{{ $fd('GlobalReadingShowType', scope.row.GlobalReadingShowType) }}
</template>
</el-table-column>
<!-- 限制编辑 -->
<el-table-column
prop="LimitEdit"
:label="$t('trials:readingUnit:qsList:title:limitEdit')"
width="160"
show-overflow-tooltip
>
<template slot-scope="scope">
{{ $fd('LimitEdit', scope.row.LimitEdit) }}
</template>
</el-table-column>
<!-- 注释 -->
<el-table-column
prop="Remark"
:label="$t('trials:readingUnit:qsList:title:Remark')"
width="140"
show-overflow-tooltip
/>
<el-table-column
prop=""
:label="$t('common:action:action')"
width="280"
show-overflow-tooltip
>
<template slot-scope="scope">
<!-- 编辑 -->
<el-button
v-if="!isConfirm && !isFromSystem && hasPermi(['trials:trials-panel:setting:reading-unit:edit'])"
type="primary"
size="mini"
@click="handleEdit(scope.row)"
>
{{ $t('trials:readingUnit:qsList:title:edit') }}
</el-button>
<!-- 查看 -->
<el-button
v-else
type="primary"
size="mini"
@click="handlelook(scope.row)"
>
{{ $t('trials:readingUnit:qsList:title:view') }}
</el-button>
<!-- 表格问题 -->
<el-button
v-if="scope.row.Type === 'table'"
type="primary"
size="mini"
@click="handleConfig(scope.row)"
>
{{ $t('trials:readingUnit:qsList:title:tableQs') }}
</el-button>
<!-- 删除 -->
<el-button
v-if="!isConfirm && !isFromSystem && hasPermi(['trials:trials-panel:setting:reading-unit:edit'])"
type="danger"
size="mini"
@click="handleDelete(scope.row)"
>
{{ $t('trials:readingUnit:qsList:title:delete') }}
</el-button>
</template>
</el-table-column>
</el-table>
<el-dialog
v-if="addOrEdit.visible"
:visible.sync="addOrEdit.visible"
:close-on-click-modal="false"
:title="addOrEdit.title"
width="800px"
append-to-body
custom-class="base-dialog-wrapper"
>
<QuestionsForm
ref="addOrEdit"
:data="rowData"
:trial-criterion-id="trialCriterionId"
:is-from-system="isFromSystem"
:digit-places="digitPlaces"
:list="tblList"
:is-look="isLook"
:is-system-criterion="isSystemCriterion"
@close="addOrEdit.visible = false"
@getList="getList"
@reloadArbitrationRules="reloadArbitrationRules"
/>
</el-dialog>
<el-dialog
v-if="preview.visible"
:visible.sync="preview.visible"
:close-on-click-modal="false"
:title="preview.title"
:fullscreen="true"
>
<QuestionsPreview :criterion-id="trialCriterionId" :form-type="1" />
</el-dialog>
<el-dialog
v-if="config.visible"
:visible.sync="config.visible"
:close-on-click-modal="false"
:title="config.title"
:fullscreen="true"
>
<TableQsList
:digit-places="digitPlaces"
:reading-question-id="rowData.Id"
:is-from-system="isFromSystem"
:is-confirm="isConfirm"
:criterion-id="trialCriterionId"
@close="config.visible = false"
/>
</el-dialog>
</div>
</template>
<script>
import { getReadingQuestionTrialList, deleteReadingQuestionTrial } from '@/api/trials'
import QuestionsForm from './QuestionsForm'
import QuestionsPreview from './QuestionsPreview'
import TableQsList from './TableQsList'
import { mapGetters } from 'vuex'
const searchDataDefault = () => {
return {
QuestionName: ''
}
}
export default {
name: 'QcQuestions',
components: { QuestionsForm, QuestionsPreview, TableQsList },
props: {
isSystemCriterion: {
type: Boolean,
default() { return false }
},
digitPlaces: {
type: Number,
default() { return 0 }
},
list: {
type: Array,
default() { return [] }
},
trialCriterionId: {
type: String,
required: true
},
readingCriterionPageId: {
type: String,
default: ''
},
isConfirm: {
type: Boolean,
default: true
},
isPreview: {
type: Boolean,
default: true
},
isFromSystem: {
type: Boolean,
default: true
}
},
data() {
return {
searchData: searchDataDefault(),
// list: [],
isLook: null,
tblList: [],
total: 0,
loading: false,
rowData: {},
addOrEdit: { visible: false, title: '' },
preview: { visible: false, title: this.$t('trials:readingUnit:qsList:title:ecrfPreview') }, // 'eCRF预览'
config: { visible: false, title: '' }
}
},
computed: {
...mapGetters(['language'])
},
mounted() {
this.tblList = this.list
},
methods: {
getList() {
this.loading = true
this.searchData.ReadingQuestionCriterionTrialId = this.trialCriterionId
this.searchData.ReadingCriterionPageId = this.readingCriterionPageId
getReadingQuestionTrialList(this.searchData).then(res => {
this.loading = false
this.tblList = res.Result
}).catch(() => { this.loading = false })
},
handleAdd() {
this.rowData = {
ReadingQuestionCriterionTrialId: this.trialCriterionId,
ReadingCriterionPageId: this.readingCriterionPageId,
Id: '' }
this.isLook = false
this.addOrEdit.title = this.$t('trials:readingUnit:qsList:title:add')// '添加'
this.addOrEdit.visible = true
},
handlelook(row) {
this.rowData = { ...row }
this.isLook = true
this.rowData.ReadingQuestionCriterionTrialId = this.trialCriterionId
this.rowData.ReadingCriterionPageId = this.readingCriterionPageId
this.addOrEdit.title = this.$t('trials:readingUnit:qsList:title:view')// '查看'
this.addOrEdit.visible = true
},
handleEdit(row) {
this.rowData = { ...row }
this.isLook = false
this.rowData.ReadingQuestionCriterionTrialId = this.trialCriterionId
this.rowData.ReadingCriterionPageId = this.readingCriterionPageId
this.addOrEdit.title = this.$t('trials:readingUnit:qsList:title:edit')// '编辑'
this.addOrEdit.visible = true
},
handleDelete(row) {
// '是否确认删除?'
this.$confirm(this.$t('trials:readingUnit:qsList:message:deleteConfirm'), {
type: 'warning',
distinguishCancelAndClose: true
})
.then(() => {
this.loading = true
deleteReadingQuestionTrial(row.Id)
.then(res => {
this.loading = false
if (res.IsSuccess) {
this.getList()
// 删除成功!
this.$message.success(this.$t('trials:readingUnit:qsList:message:deleteSuccessfully'))
}
}).catch(() => { this.loading = false })
})
},
handlePreview() {
this.preview.visible = true
},
handleConfig(row) {
this.rowData = { ...row }
this.config.title = `${this.language === 'en' && row.QuestionEnName ? row.QuestionEnName : row.QuestionName}${this.$t('trials:readingUnit:qsList:title:tableQs')}`// 表格问题
this.config.visible = true
},
// 查询
handleSearch() {
this.getList()
},
// 重置
handleReset() {
this.searchData = searchDataDefault()
this.getList()
},
reloadArbitrationRules() {
this.$emit('reloadArbitrationRules')
}
}
}
</script>
<style lang="scss" scoped>
</style>

View File

@ -0,0 +1,252 @@
<template>
<el-form
ref="globalReadingForm"
v-loading="loading"
:model="form"
size="small"
:rules="rules"
label-width="110px"
>
<el-form-item label="评估结果" prop="GlobalAssessTypes">
<div style="width: 700px;display: flex">
<el-table
min-height="100"
style="width: 600px;"
ref="multipleTable"
:data="form.GlobalAssessTypes"
stripe
>
<el-table-column
prop="ValueCN"
label="中文值"
show-overflow-tooltip
>
</el-table-column>
<el-table-column
prop="Value"
label="英文值"
show-overflow-tooltip
>
</el-table-column>
<el-table-column
prop="IsBaseLineUse"
label="是否基线评估"
show-overflow-tooltip
>
<template slot-scope="scope">
<el-switch
v-model="scope.row.IsBaseLineUse"
>
</el-switch>
<span>{{$fd('YesOrNo', scope.row.IsBaseLineUse)}}</span>
</template>
</el-table-column>
<el-table-column
prop="IsFollowVisitUse"
label="是否随访评估"
show-overflow-tooltip
>
<template slot-scope="scope">
<el-switch
v-model="scope.row.IsFollowVisitUse"
>
</el-switch>
<span>{{$fd('YesOrNo', scope.row.IsFollowVisitUse)}}</span>
</template>
</el-table-column>
</el-table>
<div style="display: flex;align-items: center;padding-left: 10px">
<el-button
:disabled="form.IsSystemCriterion || isSign || !hasPermi(['trials:trials-panel:setting:reading-unit:edit'])"
icon="el-icon-plus"
circle
@click="handleSetCriterion"
/>
</div>
</div>
</el-form-item>
<el-form-item v-if="!isSign && hasPermi(['trials:trials-panel:setting:reading-unit:edit'])">
<!-- 保存 -->
<el-button
size="small"
type="primary"
@click="handleSave(true)"
>
{{ $t('common:button:save') }}
</el-button>
</el-form-item>
<el-dialog
v-if="config.visible"
:visible.sync="config.visible"
:close-on-click-modal="false"
:title="config.title"
width="90%"
append-to-body
>
<div style="text-align: right">
<el-button
size="mini"
type="primary"
@click="handleTypeSave"
v-loading="loading"
>
保存
</el-button>
</div>
<el-table
v-loading="loading"
v-adaptive="{bottomOffset:80}"
height="100"
ref="multipleTable"
:data="$d.GlobalAssessType"
stripe
@selection-change="handleSelectionChange"
>
<el-table-column
type="selection"
width="55">
</el-table-column>
<el-table-column
prop="raw.ValueCN"
label="中文值"
show-overflow-tooltip
>
</el-table-column>
<el-table-column
prop="raw.Value"
label="英文值"
show-overflow-tooltip
>
</el-table-column>
</el-table>
</el-dialog>
</el-form>
</template>
<script>
import { getTrialConfigGlobalReadingInfo, setGlobalReadingInfo } from '@/api/trials'
import { getAssessType } from '@/api/dictionary'
export default {
name: "GlobalReading",
props: {
TrialReadingCriterionId: {
type: String,
default() {
return ''
}
}
},
data() {
return {
form: {
TrialId: '',
GlobalAssessTypes: [],
IsSystemCriterion: true
},
rules: {
GlobalAssessTypes: [{ required: true, message: this.$t('common:ruleMessage:specify'), trigger: ['blur'] }]
},
loading: false,
config: { visible: false, title: '全局阅片' },
isSign: true,
selectedList: []
}
},
mounted() {
this.initForm()
},
methods: {
handleSelectionChange(val) {
this.selectedList = val
},
toggleSelection(rows) {
console.log(this.$refs.multipleTable)
if (rows) {
rows.forEach(row => {
this.$refs.multipleTable.toggleRowSelection(row);
});
} else {
this.$refs.multipleTable.clearSelection();
}
},
handleSetCriterion() {
this.config.visible = true
this.$nextTick(() => {
var a = this.$d.GlobalAssessType.filter(v => {
return !!this.form.GlobalAssessTypes.find(v1 => {
return v1.DictionaryId === v.id
})
})
this.toggleSelection(a)
})
},
handleTypeSave() {
this.form.GlobalAssessTypes = this.selectedList.map(v => {
var o = this.form.GlobalAssessTypes.find(v1 => v1.DictionaryId === v.id)
if (o) {
return o
} else {
return {
DictionaryId: v.id,
ValueCN: v.raw.ValueCN,
Value: v.raw.Value,
IsBaseLineUse: false,
IsFollowVisitUse: false
}
}
})
this.config.visible = false
},
initForm() {
this.loading = true
getTrialConfigGlobalReadingInfo({ trialId: this.$route.query.trialId, TrialReadingCriterionId: this.TrialReadingCriterionId }).then(res => {
if (Object.keys(res.Result).length > 0) {
for (const k in res.Result) {
if (this.form.hasOwnProperty(k)) {
this.form[k] = res.Result[k]
}
}
this.isSign = res.Result.IsSign
}
this.form.GlobalAssessTypeIdsStr = this.form.GlobalAssessTypeIds.map(v => this.$fd('GlobalAssessType', v, 'id')).toString()
this.loading = false
}).catch(() => {
this.loading = false
})
},
handleSave(isPrompt) {
return new Promise((resolve, reject) => {
this.$refs['globalReadingForm'].validate((valid) => {
if (!valid) {
reject(false)
} else {
this.loading = true
// 保存配置信息
this.form.TrialId = this.$route.query.trialId
this.form.TrialReadingCriterionId = this.TrialReadingCriterionId
var params = {
GlobalAssessList: this.form.GlobalAssessTypes,
TrialReadingCriterionId: this.TrialReadingCriterionId,
TrialId: this.$route.query.trialId
}
setGlobalReadingInfo(params).then(res => {
this.loading = false
if (res.IsSuccess && isPrompt) {
this.$message.success(this.$t('common:message:savedSuccessfully'))
}
resolve(true)
}).catch(_ => {
this.loading = false
reject(false)
})
}
})
})
}
}
}
</script>
<style scoped>
</style>

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,257 @@
<template>
<el-form
ref="sysTemplateFrom"
v-loading="loading"
:model="form"
label-width="90px"
size="small"
:rules="rules"
class="upload-temporary-file"
>
<div class="base-dialog-body">
<el-form-item label="Code" prop="Code">
<el-input v-model="form.Code" />
</el-form-item>
<el-form-item label="业务场景: " prop="BusinessScenarioEnum">
<el-select
v-model="form.BusinessScenarioEnum"
style="width:100%;"
size="small"
filterable
disabled
>
<el-option
v-for="item of $d.Common_File_BusinessScenario"
:key="item.id"
:label="item.label"
:value="item.value"
/>
</el-select>
</el-form-item>
<el-form-item label="文件: ">
<div class="upload-container">
<el-upload
class="upload-demo"
action
accept=".xls,.xlsx,.doc,.docx"
:before-upload="beforeUpload"
:http-request="handleUploadFile"
:on-preview="handlePreview"
:on-remove="handleRemoveFile"
:show-file-list="true"
:file-list="fileList"
:limit="1"
:on-exceed="handleExceed"
:disabled="!form.FileTypeEnum || !form.BusinessScenarioEnum"
>
<el-button size="small" type="primary" :disabled="!form.FileTypeEnum || !form.BusinessScenarioEnum">Select</el-button>
<span
slot="tip"
style="margin-left:10px;"
class="el-upload__tip"
>
(must be in xlsx/xls/doc/docx format)
</span>
</el-upload>
</div>
</el-form-item>
<el-form-item label="阅片标准: " prop="CriterionTypeEnum">
<el-select
v-model="form.CriterionTypeEnum"
style="width:100%;"
size="small"
filterable
clearable
>
<el-option
v-for="item of $d.CriterionType"
:key="item.id"
:label="item.label"
:value="item.value"
/>
</el-select>
</el-form-item>
<el-form-item v-if="form.Id !== ''" label="是否废除: ">
<el-radio-group v-model="form.IsDeleted">
<el-radio v-for="item of $d.YesOrNo" :label="item.value">{{ item.label }}</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item label="描述">
<el-input
v-model="form.Description"
type="textarea"
:autosize="{ minRows: 2, maxRows: 4}"
placeholder="请输入内容"
/>
</el-form-item>
</div>
<div class="base-dialog-footer" style="text-align:right;margin-top:10px;">
<el-form-item style="text-align:right;">
<el-button size="small" type="primary" :disabled="!form.FileTypeEnum || !form.BusinessScenarioEnum" @click="handleSave">Save</el-button>
</el-form-item>
</div>
</el-form>
</template>
<script>
import { addOrUpdateCommonDocument, uploadCommonDoc } from '@/api/dictionary'
import { getBasicDataSelects } from '@/api/dictionary/dictionary'
export default {
name: 'TemplateForm',
props: {
data: {
type: Object,
default() { return {} }
},
isDistinguishCriteria: {
type: Boolean,
required: true
}
},
data() {
return {
form: {
Id: '',
Code: '',
FileTypeEnum: null,
BusinessScenarioEnum: null,
CriterionTypeEnum: null,
Name: '',
Path: '',
Description: '',
IsDeleted: false
},
rules: {
Code: [{ required: true, message: 'Please specify', trigger: ['blur'] }],
CriterionTypeEnum: [{ required: this.isDistinguishCriteria, message: 'Please select', trigger: ['blur'] }],
BusinessScenarioEnum: [{ required: true, message: 'Please select', trigger: ['blur'] }]
},
fileList: [],
loading: false,
dictionaryList: {}
}
},
mounted() {
this.initForm()
},
methods: {
async initForm() {
await this.getDicData()
if (Object.keys(this.data).length > 0) {
if (this.data.Path) {
this.fileList = [
{
name: this.data.Name,
path: this.data.Path,
type: this.data.Type
}
]
}
for (const k in this.form) {
if (this.data.hasOwnProperty(k)) {
this.form[k] = this.data[k]
}
}
}
},
// 获取文件类型下拉框
getDicData() {
this.loading = true
getBasicDataSelects(['Common_File_ModuleType', 'Common_File_Type']).then(res => {
this.dictionaryList = { ...res.Result }
this.loading = false
}).catch(() => {
this.loading = false
})
},
beforeUpload(file) {
// 检测文件类型是否符合要求
if (this.checkFileSuffix(file.name)) {
this.fileList = []
return true
} else {
this.$alert('必须是word/excel格式')
return false
}
},
handleUploadFile(param) {
this.loading = true
uploadCommonDoc(param.file).then(res => {
this.loading = false
if (res.IsSuccess) {
this.form.Path = res.Result.FilePath
this.form.Name = param.file.name
const file = { name: this.form.Name, path: this.form.Path }
this.fileList.push(file)
}
}).catch(() => {
this.loading = false
})
},
handleSave() {
this.$refs.sysTemplateFrom.validate(valid => {
if (!valid) return
if (!this.form.Name) {
this.$alert('Please select file.')
return
}
this.loading = true
addOrUpdateCommonDocument(this.form).then(res => {
this.loading = false
this.$emit('closeDialog')
this.$emit('getList')
this.$message.success('保存成功!')
}).catch(() => {
this.loading = false
})
})
},
handleRemoveFile() {
this.fileList = []
this.form.Path = ''
this.form.Name = ''
},
handlePreview(file) {
if (file.fullPath) {
window.open(file.fullPath, '_blank')
}
},
handleExceed(files, fileList) {
this.$message.warning(`Upload is currently limited to 1 file`)
},
checkFileSuffix(fileName) {
var typeArr = ['xls', 'xlsx', 'doc', 'docx']
var extendName = fileName.substring(fileName.lastIndexOf('.') + 1).toLocaleLowerCase()
if (typeArr.indexOf(extendName) !== -1) {
return true
} else {
return false
}
}
}
}
</script>
<style lang="scss">
.upload-temporary-file{
.upload-container .el-upload--text {
border: none;
width: 80px;
height: 40px;
}
.upload-container .el-input--small {
margin-bottom: 5px;
}
.upload-container .el-icon-circle-check {
color: #428bca;
font-size: 13px;
}
.account_item_clear{
.el-tag__close{
display: none !important;
}
}
}
</style>

Some files were not shown because too many files have changed in this diff Show More