意见反馈
continuous-integration/drone/push Build is passing Details

uat_us
DESKTOP-6C3NK6N\WXS 2024-08-01 15:23:58 +08:00
parent 96cb8a478c
commit b76303c63d
4 changed files with 303 additions and 100 deletions

View File

@ -3818,4 +3818,35 @@ export function getDicomModalityList(params) {
params
})
}
// 获取反馈列表
export function getUserFeedBackList(data) {
return request({
url: `/UserFeedBack/getUserFeedBackList`,
method: 'post',
data
})
}
// 更新反馈状态
export function batchUpdateFeedBackState(data) {
return request({
url: `/UserFeedBack/batchUpdateFeedBackState`,
method: 'put',
data
})
}
// 新增或修改意见反馈
export function addOrUpdateUserFeedBack(data) {
return request({
url: `/UserFeedBack/addOrUpdateUserFeedBack`,
method: 'post',
data
})
}
// 获取反馈详情
export function getUserFeedBackInfo(data) {
return request({
url: `/UserFeedBack/getUserFeedBackInfo`,
method: 'post',
data
})
}

View File

@ -12,7 +12,7 @@
{{ title }}
</div>
<!-- 项目详情 -->
<div class="trialsBox">
<div class="trialsBox" v-if="visitTaskId">
<el-form
label-position="right"
:model="form"
@ -20,22 +20,25 @@
class="trialsForm"
v-if="type === 'detail'"
>
<el-form-item :label="$t('feedBack:trials:code')">
<span>2024-06-28 15:00</span>
<el-form-item :label="$t('feedBack:trials:code')" style="width: 40%">
<span>{{ form.TrialCode }}</span>
</el-form-item>
<el-form-item :label="$t('feedBack:trials:name')">
<span>2024-06-28 15:00</span>
<el-form-item :label="$t('feedBack:trials:name')" style="width: 40%">
<span>{{ form.ExperimentName }}</span>
</el-form-item>
<el-form-item :label="$t('feedBack:trials:siteCode')">
<span>2024-06-28 15:00</span>
<el-form-item
:label="$t('feedBack:trials:siteCode')"
style="width: 40%"
>
<span>{{ form.TrialSiteCode }}</span>
</el-form-item>
<el-form-item :label="$t('feedBack:trials:visit')">
<span>2024-06-28 15:00</span>
<el-form-item :label="$t('feedBack:trials:visit')" style="width: 40%">
<span>{{ form.SubjectVisitName }}</span>
</el-form-item>
</el-form>
</div>
<el-form
ref="mfaForm"
ref="feedBackForm"
label-position="right"
:model="form"
:rules="rules"
@ -47,17 +50,17 @@
class="el-icon-warning-outline"
style="color: #f56c6c; font-size: 24px"
></i>
<span> 影像异常导致无法阅片</span>
<span>{{ $t("feedBack:imgfail:tip") }}</span>
</p>
<!-- 问题反馈 -->
<el-form-item
:label="$t('feedBack:form:feedBack')"
prop="feedBack"
prop="QuestionType"
v-if="type === 'feedback' && trialId"
>
<el-select v-model="form.type" style="width: 100%">
<el-select v-model="form.QuestionType" style="width: 100%">
<el-option
v-for="item in options"
v-for="item in QuestionTypeOptions"
:key="item.id"
:label="item.label"
:value="item.value"
@ -68,28 +71,34 @@
<!-- 问题反馈 -->
<el-form-item
:label="$t('feedBack:form:feedBack')"
prop="feedBack"
prop="QuestionType"
v-if="type === 'detail'"
>
<span>影像异常导致无法阅片</span>
<span>{{
QuestionTypeOptions.filter(
(item) => item.value === form.QuestionType
)[0].label
}}</span>
</el-form-item>
<!-- 问题描述 -->
<el-form-item
:label="$t('feedBack:form:description')"
prop="description"
prop="QuestionDescription"
>
<el-input
v-model="form.Code"
v-model="form.QuestionDescription"
type="textarea"
:rows="4"
:maxlength="500"
:disabled="type === 'detail'"
/>
</el-form-item>
<!-- 截图 -->
<el-form-item :label="$t('feedBack:form:screenshot')" prop="screenshot">
<uploadImage
:path.sync="form.HospitalLogoPath"
:path.sync="form.ScreenshotList"
:disabled="type === 'detail'"
ref="uploadImage"
/>
</el-form-item>
<!-- 反馈时间 -->
@ -98,23 +107,24 @@
prop="screenshot"
v-if="type === 'detail'"
>
<span>2024-06-28 15:00</span>
<span>{{ form.CreateTime }}</span>
</el-form-item>
<!-- 状态 -->
<el-form-item
:label="$t('feedBack:form:status')"
prop="screenshot"
v-if="type === 'detail'"
v-if="type === 'detail' && level > 7"
>
<el-switch
v-model="form.status"
v-model="form.State"
active-color="#13ce66"
inactive-color="#ff4949"
:active-value="true"
:inactive-value="false"
:active-text="$fd('FeedBackStatus', true)"
:inactive-text="$fd('FeedBackStatus', false)"
:disabled="level < 8"
:active-value="1"
:inactive-value="0"
:active-text="$fd('FeedBackStatus', 1)"
:inactive-text="$fd('FeedBackStatus', 0)"
:disabled="level < 8 || !isStateChange || form.State === 1"
@change="changeState"
>
</el-switch>
</el-form-item>
@ -139,6 +149,11 @@
</template>
<script>
import uploadImage from "./uploadImage.vue";
import {
addOrUpdateUserFeedBack,
getUserFeedBackInfo,
batchUpdateFeedBackState,
} from "@/api/trials.js";
export default {
name: "FB",
components: { uploadImage },
@ -152,33 +167,43 @@ export default {
trialId: null, // id
Id: null, // id
visitTaskId: null, // id
isStateChange: true, //
form: {
Code: null,
UserId: null,
EMail: null,
username: null,
HospitalLogoPath: [],
type: 0,
Id: null,
SubjectId: null,
SubjectVisitId: null,
QuestionType: 0,
QuestionDescription: null,
State: 0,
TrialSiteId: null,
TrialId: null,
VisitTaskId: null,
ScreenshotList: [],
ScreenshotListStr: null,
},
rules: {
Code: [
QuestionType: [
{
required: true,
message: this.$t("common:ruleMessage:specify"),
trigger: "blur",
},
],
EMail: [
QuestionDescription: [
{
required: true,
message: this.$t("common:ruleMessage:specify"),
trigger: "blur",
},
],
username: [
{
required: true,
message: this.$t("common:ruleMessage:specify"),
validator: (rule, value, callback) => {
if (value.length < 5) {
callback(
new Error(this.$t("feedBack:ruleMessage:lengthLimitMin5"))
);
}
callback();
},
trigger: "blur",
},
],
@ -198,6 +223,21 @@ export default {
}
return 0;
},
QuestionTypeOptions() {
if (this.level > 7) {
return [
...this.$d.FeedBackTypeToCRC.filter((item) => item.value > 0),
...this.$d.FeedBackTypeToIR,
];
}
if (this.hasPermi(["role:ir"])) {
return this.$d.FeedBackTypeToIR;
}
if (this.hasPermi(["role:crc"])) {
return this.$d.FeedBackTypeToCRC;
}
return [];
},
},
methods: {
open(data) {
@ -206,18 +246,81 @@ export default {
this.trialId = trialId;
this.Id = Id;
this.visitTaskId = visitTaskId;
this.visible = true;
this.setTypeOption();
if (!Id) {
this.title = this.setTitle();
}
this.setTypeOption();
if (Id || visitTaskId) {
this.getInfo(Id, visitTaskId);
}
this.visible = true;
},
cancel() {
this.visible = false;
this.$emit("closed");
},
async save() {
this.cancel();
try {
let validate = await this.$refs.feedBackForm.validate();
if (!validate) return;
if (this.trialId) {
this.form.TrialId = this.trialId;
}
if (this.visitTaskId) {
this.form.VisitTaskId = this.visitTaskId;
}
if (this.Id) {
this.form.Id = this.Id;
}
this.loading = true;
let res = await addOrUpdateUserFeedBack(this.form);
this.loading = false;
if (res.IsSuccess) {
this.$emit("success");
this.$message.success(this.$t("feedBack:save:success"));
this.cancel();
}
} catch (err) {
this.loading = false;
console.log(err);
}
},
//
async changeState() {
try {
let data = {
IdList: [this.Id],
State: 1,
};
let res = await batchUpdateFeedBackState(data);
if (res.IsSuccess) {
this.$message.success(this.$t("feedBack:changeState:success"));
this.$emit("success");
this.cancel();
}
} catch (err) {
console.log(err);
}
},
//
async getInfo(Id, visitTaskId) {
try {
let data = {
Id,
visitTaskId,
};
let res = await getUserFeedBackInfo(data);
if (res.IsSuccess && res.Result) {
this.form = res.Result;
this.Id = res.Result.Id;
if (res.Result.State > 0) {
this.isStateChange = false;
}
this.$refs.uploadImage.initFileList(res.Result.ScreenshotList);
}
} catch (err) {
console.log(err);
}
},
setTypeOption() {
if (!this.trialId) return (this.options = []);

View File

@ -12,7 +12,6 @@
:on-remove="remove"
:on-exceed="handleExceed"
accept=".png,.jpg,.jpeg"
v-if="!disabled"
list-type="picture-card"
>
<i slot="default" class="el-icon-plus"></i>
@ -66,9 +65,6 @@ export default {
btnDisabled: false,
};
},
mounted() {
this.initFileList();
},
methods: {
remove(file, fileList) {
// this.$emit("update:path", null);
@ -104,10 +100,11 @@ export default {
return false;
}
},
initFileList() {
if (this.path && this.path.length > 0) {
initFileList(list) {
console.log(list);
if (list && list.length > 0) {
this.fileList = [];
this.path.forEach((item, index) => {
list.forEach((item, index) => {
let name = item.split("/");
this.fileList.push({
name: name[name.length - 1],
@ -117,6 +114,7 @@ export default {
uid: `${name[name.length - 1]}${index}`,
});
});
console.log(this.fileList);
}
},
async uploadFile(param) {

View File

@ -5,7 +5,7 @@
<!--项目编号/实验名称-->
<el-form-item :label="$t('feedBack:search:trials')" v-if="level > 8">
<el-input
v-model="searchData.a"
v-model="searchData.TrialKeyInfo"
clearable
style="width: 150px"
></el-input>
@ -13,7 +13,7 @@
<!--中心-->
<el-form-item :label="$t('feedBack:search:site')" v-if="level > 7">
<el-input
v-model="searchData.a"
v-model="searchData.TrialSiteCode"
clearable
style="width: 150px"
></el-input>
@ -24,7 +24,7 @@
v-if="level > 7"
>
<el-input
v-model="searchData.a"
v-model="searchData.SubejctAndVisitKeyInfo"
clearable
style="width: 150px"
></el-input>
@ -32,12 +32,12 @@
<!--角色-->
<el-form-item :label="$t('feedBack:search:role')" v-if="level > 7">
<el-select
v-model="searchData.NoticeTypeEnum"
v-model="searchData.UserTypeEnum"
clearable
style="width: 150px"
>
<el-option
v-for="item of $d.NoteType"
v-for="item of UserTypeOptins"
:key="item.value"
:label="item.label"
:value="item.value"
@ -47,7 +47,7 @@
<!--反馈人-->
<el-form-item :label="$t('feedBack:search:user')" v-if="level > 7">
<el-input
v-model="searchData.a"
v-model="searchData.FeedBackKeyInfo"
clearable
style="width: 150px"
></el-input>
@ -55,12 +55,12 @@
<!--问题类型-->
<el-form-item :label="$t('feedBack:search:questionType')">
<el-select
v-model="searchData.NoticeTypeEnum"
v-model="searchData.QuestionType"
clearable
style="width: 150px"
>
<el-option
v-for="item of $d.NoteType"
v-for="item of QuestionTypeOptions"
:key="item.value"
:label="item.label"
:value="item.value"
@ -70,20 +70,16 @@
<!--问题描述-->
<el-form-item :label="$t('feedBack:search:description')">
<el-input
v-model="searchData.a"
v-model="searchData.QuestionDescription"
clearable
style="width: 150px"
></el-input>
</el-form-item>
<!--状态-->
<el-form-item :label="$t('feedBack:search:status')" v-if="level > 7">
<el-select
v-model="searchData.NoticeTypeEnum"
clearable
style="width: 150px"
>
<el-select v-model="searchData.State" clearable style="width: 150px">
<el-option
v-for="item of $d.NoteType"
v-for="item of $d.FeedBackStatus"
:key="item.value"
:label="item.label"
:value="item.value"
@ -135,6 +131,7 @@
@click="resolve"
style="float: right"
v-if="level > 7"
:disabled="tableSelectData.length <= 0"
>
{{ $t("feedBack:button:resolve") }}
</el-button>
@ -147,7 +144,7 @@
class="table"
@selection-change="handleSelectChange"
@sort-change="handleSortByColumn"
:default-sort="{ prop: 'StudyTime', order: 'descending' }"
:default-sort="{ prop: 'CreateTime', order: 'descending' }"
>
<el-table-column
type="selection"
@ -158,8 +155,8 @@
<el-table-column type="index" width="50" />
<el-table-column
:label="$t('feedBack:form:trialCode')"
prop="NoticeLevelEnum"
min-width="120"
prop="TrialCode"
min-width="80"
show-overflow-tooltip
sortable="custom"
v-if="level > 8"
@ -174,7 +171,7 @@
/>
<el-table-column
:label="$t('feedBack:form:siteCode')"
prop="NoticeContent"
prop="TrialSiteCode"
min-width="100"
show-overflow-tooltip
sortable="custom"
@ -182,60 +179,74 @@
/>
<el-table-column
:label="$t('feedBack:form:subjectVisit')"
prop="NoticeStateEnum"
min-width="100"
prop="SubjectVisitName"
min-width="120"
show-overflow-tooltip
sortable="custom"
v-if="level > 7"
/>
<el-table-column
:label="$t('feedBack:form:role')"
prop="ApplicableProjectEnum"
min-width="100"
prop="FeedBackUserName"
min-width="80"
show-overflow-tooltip
sortable="custom"
v-if="level > 7"
/>
<el-table-column
:label="$t('feedBack:form:user')"
prop="ApplicableProjectEnum"
min-width="100"
prop="FeedBackFullName"
min-width="80"
show-overflow-tooltip
sortable="custom"
v-if="level > 7"
/>
<el-table-column
:label="$t('feedBack:form:questionType')"
prop="NoticeUserTypeList"
min-width="80"
prop="QuestionType"
min-width="120"
show-overflow-tooltip
sortable="custom"
/>
>
<template slot-scope="scope">
<span>{{
QuestionTypeOptions.filter(
(item) => item.value === scope.row.QuestionType
)[0].label
}}</span>
</template>
</el-table-column>
<el-table-column
:label="$t('feedBack:form:description')"
prop="NoticeModeEnum"
prop="QuestionDescription"
min-width="100"
show-overflow-tooltip
sortable="custom"
/>
<el-table-column
:label="$t('feedBack:form:time')"
prop="StartDate"
prop="CreateTime"
min-width="100"
show-overflow-tooltip
sortable="custom"
/>
<el-table-column
:label="$t('feedBack:form:status')"
prop="PublishUserName"
min-width="100"
prop="State"
min-width="80"
show-overflow-tooltip
sortable="custom"
v-if="level > 7"
/>
>
<template slot-scope="scope">
<el-tag :type="['danger', 'success'][scope.row.State]">{{
$fd("FeedBackStatus", scope.row.State)
}}</el-tag>
</template>
</el-table-column>
<el-table-column
:label="$t('feedBack:form:uploadTime')"
prop="PublishedTime"
prop="UpdateTime"
min-width="100"
show-overflow-tooltip
sortable="custom"
@ -268,19 +279,23 @@
</template>
<script>
import Pagination from "@/components/Pagination";
import { batchUpdateFeedBackState, getUserFeedBackList } from "@/api/trials.js";
const searchDataDefault = () => {
return {
Asc: true,
SortField: "",
NoticeContent: null,
FileName: null,
NoticeTypeEnum: null,
NoticeLevelEnum: null,
ApplicableProjectEnum: null,
NoticeModeEnum: null,
NoticeStateEnum: null,
TrialKeyInfo: null,
SubejctAndVisitKeyInfo: null,
UserTypeEnum: null,
FeedBackKeyInfo: null,
QuestionDescription: null,
QuestionType: null,
State: null,
TrialSiteCode: null,
BeginCreatime: null,
EndCreatime: null,
PageIndex: 1,
PageSize: 20,
Asc: false,
SortField: "CreateTime",
};
};
export default {
@ -301,6 +316,10 @@ export default {
//
searchData: searchDataDefault(),
tableSelectData: [],
UserTypeOptins: [
{ label: "CRC", value: 2 },
{ label: "IR", value: 13 },
],
};
},
computed: {
@ -316,17 +335,47 @@ export default {
}
return 0;
},
QuestionTypeOptions() {
if (this.level > 7) {
return [
...this.$d.FeedBackTypeToCRC.filter((item) => item.value > 0),
...this.$d.FeedBackTypeToIR,
];
}
if (this.hasPermi(["role:ir"])) {
return this.$d.FeedBackTypeToIR;
}
if (this.hasPermi(["role:crc"])) {
return this.$d.FeedBackTypeToCRC;
}
return [];
},
},
created() {
this.getList();
},
mounted() {},
methods: {
getList() {},
async getList() {
try {
this.loading = true;
let res = await getUserFeedBackList(this.searchData);
this.loading = false;
if (res.IsSuccess) {
this.list = res.Result.CurrentPageData;
this.total = res.Result.TotalCount;
}
} catch (err) {
this.loading = false;
console.log(err);
}
},
handleDatetimeChange(val) {
if (val) {
this.searchData.StartTime = val[0];
this.searchData.EndTime = val[1];
this.searchData.BeginCreatime = val[0];
this.searchData.EndCreatime = val[1];
} else {
this.searchData.StartTime = "";
this.searchData.EndTime = "";
this.searchData.BeginCreatime = "";
this.searchData.EndCreatime = "";
}
},
//
@ -337,7 +386,26 @@ export default {
this.getList();
},
//
resolve() {},
async resolve() {
try {
let confirm = await this.$confirm(this.$t("feedBack:resolve:tip"), {
type: "warning",
distinguishCancelAndClose: true,
});
if (!confirm) return;
let data = {
IdList: this.tableSelectData.map((item) => item.Id),
state: 1,
};
let res = await batchUpdateFeedBackState(data);
if (res.IsSuccess) {
this.$message.success(this.$t("feedBack:resolve:success"));
this.getList();
}
} catch (err) {
console.log(err);
}
},
//
report() {},
//
@ -361,6 +429,9 @@ export default {
this.$FB({
type: "detail",
Id: row.Id,
callBack: () => {
this.getList();
},
});
},
},