项目报表添加ORR分布、项目报表添加权限

main
wangxiaoshuang 2026-01-05 15:23:30 +08:00
parent 99955b2eff
commit d8712f26cd
4 changed files with 215 additions and 228 deletions

View File

@ -2,16 +2,16 @@
<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 && item.path !== '/trials/trials-panel/attachments'" :label="$t(item.LanguageMark)" :name="item.path">
<el-tabs v-if="!item.tabHiddn" v-model="trialsTabChild" @tab-click="clickTab" style="background-color: #f5f7fa;">
<el-tab-pane v-for="item of trialsRouter.children.find(v => { return v.name == 'TrialsPanel' }).children"
:key="`tab${item.path}`"
:disabled="TotalNeedSignTrialDocCount !== 0 && item.path !== '/trials/trials-panel/attachments'"
:label="$t(item.LanguageMark)" :name="item.path">
<el-tabs v-if="!item.tabHiddn" v-model="trialsTabChild" @tab-click="clickTab"
style="background-color: #f5f7fa;">
<template v-for="item1 of item.children">
<el-tab-pane
v-if="TrialConfig && isShow(item1.path)"
:key="`tab1${item1.path}`"
<el-tab-pane v-if="TrialConfig && isShow(item1.path)" :key="`tab1${item1.path}`"
:disabled="TotalNeedSignTrialDocCount !== 0 && item1.path !== '/trials/trials-panel/attachments/self-attachment' || (TrialStatusStr === 'Initializing' && (item1.path === '/trials/trials-panel/setting/personnel-manage' || item1.path === '/trials/trials-panel/setting/qc-question' || item1.path === '/trials/trials-panel/setting/reading-unit' || item1.path === '/trials/trials-panel/setting/medical-audit' || item1.path === '/trials/trials-panel/setting/email-manage'))"
:label="$t(item1.LanguageMark)"
:name="item1.path"
/>
:label="$t(item1.LanguageMark)" :name="item1.path" />
</template>
</el-tabs>
</el-tab-pane>
@ -83,7 +83,7 @@ export default {
getUserDocumentList({ TrialId: this.$route.query.trialId }).then(async res => {
var total = res.OtherInfo.NeedSignCount
var TrialStatusStr = res.OtherInfo.TrialStatusStr
this.IsAdditionalAssessment = res.OtherInfo.IsAdditionalAssessment
this.IsAdditionalAssessment = res.OtherInfo.IsAdditionalAssessment
this.TrialStatusStr = TrialStatusStr
await store.dispatch('user/setTotalNeedSignTrialDocCount', total)
await store.dispatch('user/setTrialStatusStr', TrialStatusStr)
@ -100,7 +100,7 @@ export default {
getUserDocumentList({ TrialId: this.$route.query.trialId }).then(async res => {
var total = res.OtherInfo.NeedSignCount
var TrialStatusStr = res.OtherInfo.TrialStatusStr
this.IsAdditionalAssessment = res.OtherInfo.IsAdditionalAssessment
this.IsAdditionalAssessment = res.OtherInfo.IsAdditionalAssessment
this.TrialStatusStr = TrialStatusStr
await store.dispatch('user/setTotalNeedSignTrialDocCount', total)
await store.dispatch('user/setTrialStatusStr', TrialStatusStr)
@ -117,8 +117,11 @@ export default {
}
},
methods: {
isShow: function(path) {
isShow: function (path) {
var isShow = true
let chartList = [
'/trials/trials-panel/trial-summary/report-forms'
]
var qualityList = [
'/trials/trials-panel/visit/crc-question',
'/trials/trials-panel/visit/qc-check',
@ -134,6 +137,9 @@ export default {
'/trials/trials-panel/trial-summary/image-inspect',
'/trials/trials-panel/trial-summary/push-record',
]
if (!this.$store.state.trials.config.IsExternalViewTrialChart && ~chartList.indexOf(path) && this.hasPermi(['role:cmm', 'role:cpm', 'role:ea', 'role:mc', 'role:smm', 'role:spm'])) {
isShow = false
}
if (this.TrialConfig.QCProcessEnum === 0 && ~qualityList.indexOf(path)) {
isShow = false
}
@ -205,56 +211,69 @@ export default {
}
</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: #428bca;
}
span{
margin-left: 3px;
font-size: 14px;
}
.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: #428bca;
}
.el-tabs__header{
margin-bottom: 3px;
}
.el-tabs__item.is-active{
font-weight: bold;
}
position: relative;
.el-input--medium .el-input__inner{
height: 44px;line-height: 44px;width: 280px;
border: none;
text-align: right;
span {
margin-left: 3px;
font-size: 14px;
}
}
.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;
.el-tabs__header {
margin-bottom: 3px;
}
.el-tabs__item.is-active {
font-weight: bold;
}
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

@ -12,226 +12,110 @@
<span>{{ $t('trials:externalStaff:table:phone') }}:</span>
<el-input v-model="listQuery.Phone" size="mini" class="mr" clearable />
<!-- 查询 -->
<el-button
type="primary"
size="mini"
icon="el-icon-search"
@click="handleSearch"
>
<el-button type="primary" size="mini" icon="el-icon-search" @click="handleSearch">
{{ $t('common:button:search') }}
</el-button>
<!-- 重置 -->
<el-button
size="mini"
type="primary"
style="margin-right: 10px"
icon="el-icon-refresh-left"
@click="handleReset"
>
<el-button size="mini" type="primary" style="margin-right: 10px" icon="el-icon-refresh-left"
@click="handleReset">
{{ $t('common:button:reset') }}
</el-button>
<!-- 权限配置 -->
<el-button
type="primary"
size="mini"
style="margin-left: auto"
:loading="assignLoadStatus"
@click="openPermission"
>
<el-button type="primary" size="mini" style="margin-left: auto" :loading="assignLoadStatus"
@click="openPermission">
{{ $t('trials:staff:button:permissionConfiguration') }}
</el-button>
<!-- 发送邮件 -->
<el-button
type="primary"
size="mini"
style="margin-left: 10px"
:disabled="selectArr.length === 0"
:loading="assignLoadStatus"
icon="el-icon-message"
@click="sendEmail"
>
<el-button type="primary" size="mini" style="margin-left: 10px" :disabled="selectArr.length === 0"
:loading="assignLoadStatus" icon="el-icon-message" @click="sendEmail">
{{ $t('trials:staff:button:sendEmail') }}
</el-button>
<!-- 添加 -->
<el-button
type="primary"
icon="el-icon-plus"
size="mini"
style="margin-left: 10px"
:loading="assignLoadStatus"
@click="handleAdd"
>
<el-button type="primary" icon="el-icon-plus" size="mini" style="margin-left: 10px" :loading="assignLoadStatus"
@click="handleAdd">
{{ $t('trials:staff:button:addExternalStaff') }}
</el-button>
</div>
</el-header>
<el-main>
<div class="data-table">
<el-table
v-loading="loading"
:data="list"
stripe
height="400px"
class="participant-table-list"
@selection-change="handleSelectChange"
@sort-change="handleSortByColumn"
>
<el-table-column
type="selection"
width="50"
:selectable="handleSelectable"
/>
<el-table v-loading="loading" :data="list" stripe height="400px" class="participant-table-list"
@selection-change="handleSelectChange" @sort-change="handleSortByColumn">
<el-table-column type="selection" width="50" :selectable="handleSelectable" />
<el-table-column type="index" width="50" />
<el-table-column
prop="UserRealName"
:label="$t('trials:externalStaff:table:name')"
show-overflow-tooltip
sortable="custom"
min-width="100"
>
<el-table-column prop="UserRealName" :label="$t('trials:externalStaff: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="UserType"
:label="$t('trials:externalStaff:table:userType')"
show-overflow-tooltip
sortable="custom"
min-width="120"
>
<el-table-column prop="UserType" :label="$t('trials:externalStaff:table:userType')" show-overflow-tooltip
sortable="custom" min-width="120">
<template slot-scope="scope">
{{
userTypeOptions.length > 0
? userTypeOptions.find((v) => {
return v.Id == scope.row.UserTypeId
}).UserTypeShortName
return v.Id == scope.row.UserTypeId
}).UserTypeShortName
: ''
}}
</template>
</el-table-column>
<el-table-column
prop="Phone"
:label="$t('trials:externalStaff:table:phone')"
show-overflow-tooltip
sortable="custom"
min-width="120"
/>
<el-table-column
prop="Email"
:label="$t('trials:externalStaff:table:email')"
show-overflow-tooltip
sortable="custom"
min-width="120"
/>
<el-table-column
prop="OrganizationName"
:label="$t('trials:externalStaff:table:organization')"
show-overflow-tooltip
sortable="custom"
min-width="120"
/>
<el-table-column prop="Phone" :label="$t('trials:externalStaff:table:phone')" show-overflow-tooltip
sortable="custom" min-width="120" />
<el-table-column prop="Email" :label="$t('trials:externalStaff:table:email')" show-overflow-tooltip
sortable="custom" min-width="120" />
<el-table-column prop="OrganizationName" :label="$t('trials:externalStaff:table:organization')"
show-overflow-tooltip sortable="custom" min-width="120" />
<el-table-column
prop="inviteState"
:label="$t('trials:externalStaff:table:status')"
fixed="right"
show-overflow-tooltip
sortable="custom"
min-width="120"
>
<el-table-column prop="inviteState" :label="$t('trials:externalStaff:table:status')" fixed="right"
show-overflow-tooltip sortable="custom" min-width="120">
<template slot-scope="scope">{{
$fd('IsJoin', scope.row.IsJoin)
}}</template>
</el-table-column>
<el-table-column
:label="$t('common:action:action')"
fixed="right"
min-width="150"
>
<el-table-column :label="$t('common:action:action')" fixed="right" min-width="150">
<template slot-scope="scope">
<el-button
circle
icon="el-icon-delete"
:disabled="scope.row.IsJoin"
:title="$t('trials:externalStaff:button:delete')"
@click="deleteTrialExternalUser(scope.row)"
/>
<el-button circle icon="el-icon-delete" :disabled="scope.row.IsJoin"
:title="$t('trials:externalStaff:button:delete')" @click="deleteTrialExternalUser(scope.row)" />
</template>
</el-table-column>
</el-table>
</div>
</el-main>
<StaffExternalAdd
ref="StaffExternalAdd"
:user-type-options="userTypeOptions"
@getList="refeshList"
/>
<StaffExternalAdd ref="StaffExternalAdd" :user-type-options="userTypeOptions" @getList="refeshList" />
<base-model v-if="model_config.visible" :config="model_config">
<template slot="dialog-body">
<el-form
:model="permission"
:rules="permissionRole"
ref="permissionForm"
label-width="230px"
class="demo-ruleForm"
>
<el-form-item
:label="$t('trials:externalStaff:form:IsSPMJoinReReadingApproval')"
prop="IsSPMJoinReReadingApproval"
>
<el-form :model="permission" :rules="permissionRole" ref="permissionForm" label-width="230px"
class="demo-ruleForm">
<el-form-item :label="$t('trials:externalStaff:form:IsSPMJoinReReadingApproval')"
prop="IsSPMJoinReReadingApproval">
<el-radio-group v-model="permission.IsSPMJoinReReadingApproval">
<el-radio
v-for="item in $d.YesOrNo"
:key="item.id"
:label="item.value"
>{{ item.label }}</el-radio
>
<el-radio v-for="item in $d.YesOrNo" :key="item.id" :label="item.value">{{ item.label }}</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item
:label="$t('trials:externalStaff:form:IsSPMJoinReviewerSelect')"
prop="IsSPMJoinReviewerSelect"
>
<el-form-item :label="$t('trials:externalStaff:form:IsSPMJoinReviewerSelect')" prop="IsSPMJoinReviewerSelect">
<el-radio-group v-model="permission.IsSPMJoinReviewerSelect">
<el-radio
v-for="item in $d.YesOrNo"
:key="item.id"
:label="item.value"
>{{ item.label }}</el-radio
>
<el-radio v-for="item in $d.YesOrNo" :key="item.id" :label="item.value">{{ item.label }}</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item
:label="$t('trials:externalStaff:form:IsSPMJoinSiteSurvey')"
prop="IsSPMJoinSiteSurvey"
>
<el-form-item :label="$t('trials:externalStaff:form:IsSPMJoinSiteSurvey')" prop="IsSPMJoinSiteSurvey">
<el-radio-group v-model="permission.IsSPMJoinSiteSurvey">
<el-radio
v-for="item in $d.YesOrNo"
:key="item.id"
:label="item.value"
>{{ item.label }}</el-radio
>
<el-radio v-for="item in $d.YesOrNo" :key="item.id" :label="item.value">{{ item.label }}</el-radio>
</el-radio-group>
</el-form-item>
<el-form-item :label="$t('trials:externalStaff:form:IsExternalViewTrialChart')" prop="IsSPMJoinSiteSurvey">
<el-radio-group v-model="permission.IsExternalViewTrialChart">
<el-radio v-for="item in $d.YesOrNo" :key="item.id" :label="item.value">{{ item.label }}</el-radio>
</el-radio-group>
</el-form-item>
</el-form>
</template>
<template slot="dialog-footer">
<el-button
:disabled="assignLoadStatus"
size="small"
type="primary"
@click="model_config.visible = false"
>
<el-button :disabled="assignLoadStatus" size="small" type="primary" @click="model_config.visible = false">
{{ $t('common:button:cancel') }}
</el-button>
<el-button
size="small"
type="primary"
:loading="assignLoadStatus"
@click="savePermission"
>
<el-button size="small" type="primary" :loading="assignLoadStatus" @click="savePermission">
{{ $t('common:button:save') }}
</el-button>
</template>
@ -286,6 +170,7 @@ export default {
IsSPMJoinReReadingApproval: false,
IsSPMJoinReviewerSelect: false,
IsSPMJoinSiteSurvey: false,
IsExternalViewTrialChart: false
},
permissionRole: {},
}
@ -491,24 +376,30 @@ export default {
<style lang="scss" scoped>
.participant-container {
height: 100%;
.el-header {
padding-right: 5px;
.filter-container {
display: flex;
align-items: center;
span {
font-size: 13px;
margin-right: 5px;
}
.mr {
margin-right: 5px;
width: 120px;
}
}
}
.el-main {
padding: 0px;
}
.el-footer {
padding: 0 20px;
}

View File

@ -123,7 +123,7 @@
show-overflow-tooltip sortable="custom" min-width="120" />
<el-table-column
v-if="hasPermi(['trials:trials-panel:subject:edit', 'trials:trials-panel:subject:status', 'trials:trials-panel:subject:delete', 'trials:trials-panel:subject:chart', 'trials:trials-panel:subject:report'])"
v-if="hasPermi(['trials:trials-panel:subject:edit', 'trials:trials-panel:subject:status', 'trials:trials-panel:subject:delete', 'trials:trials-panel:subject:report']) || (hasPermi(['trials:trials-panel:subject:chart']) && isShowChart)"
:label="$t('common:action:action')" width="240" fixed="right">
<template slot-scope="scope">
<!-- 编辑 -->
@ -140,8 +140,8 @@
<el-button v-hasPermi="['trials:trials-panel:subject:report']" icon="el-icon-message" circle
:title="$t('trials:subject:title:report')" @click="handleMessage(scope.row)" />
<!-- 图表 -->
<el-button v-hasPermi="['trials:trials-panel:subject:chart']" icon="el-icon-s-data" circle
:title="$t('trials:subject:title:report')" @click="handleopenSubjectChart(scope.row)" />
<el-button v-hasPermi="['trials:trials-panel:subject:chart']" v-if="isShowChart" icon="el-icon-s-data"
circle :title="$t('trials:subject:title:report')" @click="handleopenSubjectChart(scope.row)" />
</template>
</el-table-column>
</el-table>
@ -229,6 +229,11 @@ export default {
})
}
},
computed: {
isShowChart() {
return this.$store.state.trials.config.IsExternalViewTrialChart || !this.hasPermi(['role:cmm', 'role:cpm', 'role:ea', 'role:mc', 'role:smm', 'role:spm'])
}
},
mounted() {
const trialsRouter = this.$store.getters.routes.find(r => { return r.name === 'Trials' })
const trialsPanelRouter = trialsRouter.children.find(r => { return r.name === 'TrialsPanel' }).children

View File

@ -18,11 +18,16 @@
<div class="chartBox" :style="{ height: isTumor ? '50%' : '100%' }">
<div ref="chartContainer_right" style="width: 100%; height: 100%;" v-loading="loading_right"></div>
</div>
<div class="chartBox" :style="{ position: 'relative' }" v-if="isTumor">
<div ref="chartContainer_bottom" style="width: 100%; height: 100%;" v-loading="loading_bottom"></div>
<span v-if="ORR" style="position: absolute;top: 0;right:20px">ORR: {{ ORR }}</span>
</div>
<div class="chartBox" v-if="isTumor">
<div ref="chartContainer_bottom" style="width: 100%; height: 100%;" v-loading="loading_bottom"></div>
</div>
<div class="chartBox" :style="{ position: 'relative' }" v-if="isTumor">
<div ref="chartContainer_bottom_left" style="width: 100%; height: 100%;" v-loading="loading_bottom">
</div>
<span v-if="ORR" style="position: absolute;top: 0;right:20px">{{
$t("trials:reportForms:cjart:title:totleORR") }} {{ ORR }}</span>
</div>
<div class="chartBox" v-if="isTumor" style="width:100%">
<div ref="chartContainer_bottom_right" style="width: 100%; height: 100%;" v-loading="loading_bottom">
</div>
</div>
@ -76,6 +81,7 @@ export default {
loading_bottom: false,
chart_bottom: null,
chart_bottom_right: null,
chart_bottom_left: null,
// isHas_bottom_right: false,
ORR: null
}
@ -369,12 +375,26 @@ export default {
// this.isHas_bottom_right = true
OtherInfo.PDList.forEach(item => {
obj2.xAxisData.push(item.SubjectCode)
obj2.seriesData.push(item.DaysDiff)
obj2.seriesData.push(item.MonthsDiff)
})
obj2.medianValue = this.calculateMedian(obj2.seriesData)
}
let obj3 = {
xAxisData: [],
seriesData: [],
unit: this.$t("trials:reportForms:cjart:unit:ORRValue"),
titleText: this.$t("trials:reportForms:cjart:title:ORR"),
}
if (OtherInfo.SiteORRList && OtherInfo.SiteORRList.length > 0) {
// this.isHas_bottom_right = true
OtherInfo.SiteORRList.forEach(item => {
obj3.xAxisData.push(item.TrialSiteCode)
obj3.seriesData.push(item.OrrPercent.split("%")[0])
})
}
this.$nextTick(() => {
this.initChart_bottom_right(obj2)
this.initChart_bottom_left(obj3)
})
// else {
// this.isHas_bottom_right = false
@ -428,6 +448,57 @@ export default {
// 4. 使
this.chart_bottom.setOption(option);
},
initChart_bottom_left(obj) {
this.chart_bottom_left = echarts.init(this.$refs.chartContainer_bottom_left);
// ...
const option = {
title: {
text: obj.titleText
},
color: this.color,
tooltip: {
trigger: 'item',
formatter: `{b} : {c}%`
},
// tooltip: {
// trigger: 'axis',
// axisPointer: {
// type: 'shadow'
// }
// },
xAxis: {
type: 'category',
data: obj.xAxisData
// data: ['aaa', 'bbb', 'ccc']
},
yAxis: {
name: obj.unit,
type: 'value',
axisLabel: {
formatter: '{value}%'
}
},
series: {
label: {
show: true,
formatter: `{c}%`,
position: 'inside',
textStyle: {
fontSize: 14, // 14px
color: '#000', //
// fontWeight: 'bold' //
}
},
data: obj.seriesData,
// data: [1, 2, 3],
type: 'bar',
barWidth: 20,
}
};
// 4. 使
this.chart_bottom_left.setOption(option);
},
initChart_bottom_right(obj) {
this.chart_bottom_right = echarts.init(this.$refs.chartContainer_bottom_right);
// ...
@ -513,6 +584,7 @@ export default {
width: 100%;
height: 100%;
background-color: #fff;
overflow-y: auto;
.search {
padding: 5px;