From 68daed8ac76b42542e6ed3fbcac19a057c1dedf0 Mon Sep 17 00:00:00 2001
From: WXL <wl_5969728@163.com>
Date: 星期五, 15 五月 2026 09:22:17 +0800
Subject: [PATCH] 青岛opo维护
---
src/api/businessApi/ethicalReview.js | 28
src/views/business/ethicalReview/index.vue | 10
src/views/business/ethicalReview/ethicalReviewInfo.vue | 1543 ++++++++++++++++++++++++++++++++++++++++----------------
3 files changed, 1,116 insertions(+), 465 deletions(-)
diff --git a/src/api/businessApi/ethicalReview.js b/src/api/businessApi/ethicalReview.js
index de9780f..71139a8 100644
--- a/src/api/businessApi/ethicalReview.js
+++ b/src/api/businessApi/ethicalReview.js
@@ -8,7 +8,7 @@
data: data
})
}
-// 姝讳骸淇℃伅淇敼
+// 浼︾悊瀹℃煡淇℃伅淇敼
export function ethicalreviewadd(data) {
return request({
url: '/project/ethicalreviewinitiate/add',
@@ -16,7 +16,7 @@
data: data
})
}
-// 姝讳骸淇℃伅淇敼
+// 浼︾悊瀹℃煡淇℃伅淇敼
export function ethicalreviewedit(data) {
return request({
url: '/project/ethicalreviewinitiate/edit',
@@ -24,11 +24,33 @@
data: data
})
}
-// 姝讳骸淇℃伅璇︽儏
+// 瀹℃煡涓撳缁熻
+export function ethicalreExpertTotal(query) {
+ return request({
+ url: '/project/ethicalreviewopinions/expertTotal',
+ method: 'get',
+ params: query
+ })
+}
+// 浼︾悊瀹℃煡淇℃伅璇︽儏
export function ethicalreviewInfo(id) {
return request({
url: '/project/ethicalreviewinitiate/getInfo/' + id,
method: 'get'
})
}
+// 瀹℃煡鍗曠姸鎬佸彉鏇�
+export function ethicalreviewreceiveStatus(id) {
+ return request({
+ url: '/project/ethicalreviewopinions/receiveStatus',
+ method: 'get'
+ })
+}
+// // 浼︾悊瀹℃煡淇℃伅璇︽儏
+// export function ethicalreviewInfo(id) {
+// return request({
+// url: '/project/ethicalreviewinitiate/getInfo/' + id,
+// method: 'get'
+// })
+// }
diff --git a/src/views/business/ethicalReview/ethicalReviewInfo.vue b/src/views/business/ethicalReview/ethicalReviewInfo.vue
index 9e76ff2..1c4b908 100644
--- a/src/views/business/ethicalReview/ethicalReviewInfo.vue
+++ b/src/views/business/ethicalReview/ethicalReviewInfo.vue
@@ -12,9 +12,28 @@
</el-button>
<el-button
+ type="success"
+ @click="handleCompleteReview"
+ :disabled="form.status == '3' || form.status == '2'"
+ :loading="completeLoading"
+ >
+ 瀹℃煡瀹屾垚
+ </el-button>
+
+ <el-button
type="warning"
+ @click="handleSuspendReview"
+ :disabled="form.status == '2' || form.status == '3'"
+ :loading="suspendLoading"
+ >
+ 瀹℃煡涓
+ </el-button>
+
+ <el-button
+ type="danger"
@click="handleEndReview"
- :disabled="form.status === '2'"
+ :disabled="form.status == '2'"
+ :loading="endLoading"
>
缁撴潫瀹℃煡
</el-button>
@@ -36,9 +55,6 @@
<el-input v-model="form.initiatePerson" />
</el-form-item>
</el-col>
- </el-row>
-
- <el-row :gutter="20">
<el-col :span="8">
<el-form-item label="瀹℃煡鐘舵��" prop="status">
<el-select v-model="form.status" style="width: 100%">
@@ -51,54 +67,13 @@
</el-select>
</el-form-item>
</el-col>
- <el-col :span="8">
- <el-form-item label="鍙戣捣鏃堕棿" prop="startTime">
- <el-date-picker
- v-model="form.startTime"
- type="datetime"
- value-format="yyyy-MM-dd HH:mm:ss"
- style="width: 100%"
- />
- </el-form-item>
- </el-col>
- <el-col :span="8">
- <el-form-item label="鎴鏃堕棿" prop="cutOffTime">
- <el-date-picker
- v-model="form.cutOffTime"
- type="datetime"
- value-format="yyyy-MM-dd HH:mm:ss"
- style="width: 100%"
- >
- </el-date-picker>
- </el-form-item>
- </el-col>
</el-row>
<!-- 涓撳鐩稿叧淇℃伅 -->
- <el-row :gutter="20">
- <el-col :span="8">
- <el-form-item label="涓撳濮撳悕" prop="expertName">
- <el-input v-model="form.expertName" />
- </el-form-item>
- </el-col>
- <el-col :span="8">
- <el-form-item label="涓撳缂栧彿" prop="expertNo">
- <el-input v-model="form.expertNo" />
- </el-form-item>
- </el-col>
- <el-col :span="8">
- <el-form-item label="涓撳绫诲瀷" prop="expertType">
- <el-select v-model="form.expertType" style="width: 100%">
- <el-option label="鏅�氫笓瀹�" value="normal" />
- <el-option label="涓诲涓撳" value="chief" />
- </el-select>
- </el-form-item>
- </el-col>
- </el-row>
<el-row :gutter="20">
<el-col :span="8">
- <el-form-item label="涓撳缁撹" prop="expertConclusion">
+ <el-form-item label="浼︾悊瀹℃煡缁撹" prop="expertConclusion">
<el-select v-model="form.expertConclusion" style="width: 100%">
<el-option label="鍚屾剰" value="1" />
<el-option label="瀹℃煡涓�" value="2" />
@@ -107,7 +82,7 @@
</el-form-item>
</el-col>
<el-col :span="8">
- <el-form-item label="涓撳缁撹鏃堕棿" prop="expertTime">
+ <el-form-item label="浼︾悊瀹℃煡缁撹鏃堕棿" prop="expertTime">
<el-date-picker
v-model="form.expertTime"
type="datetime"
@@ -116,26 +91,16 @@
/>
</el-form-item>
</el-col>
- <el-col :span="8">
- <el-form-item label="涓撳鎺掗槦搴忓彿" prop="orderNo">
- <el-input-number
- v-model="form.orderNo"
- :min="1"
- :max="20"
- style="width: 100%"
- />
- </el-form-item>
- </el-col>
</el-row>
<el-row :gutter="20">
<el-col :span="24">
- <el-form-item label="涓撳鎰忚" prop="expertOpinion">
+ <el-form-item label="瀹℃煡鎰忚" prop="expertOpinion">
<el-input
type="textarea"
:rows="2"
v-model="form.expertOpinion"
- placeholder="璇疯緭鍏ヤ笓瀹舵剰瑙�"
+ placeholder="璇疯緭鍏ユ剰瑙�"
/>
</el-form-item>
</el-col>
@@ -160,9 +125,6 @@
<el-card class="attachment-card">
<div slot="header" class="clearfix">
<span class="detail-title">鐩稿叧闄勪欢</span>
- <!-- <el-button type="primary" size="mini" @click="openUploadDialog">
- 涓婁紶闄勪欢
- </el-button> -->
</div>
<!-- 浣跨敤 UploadAttachment 缁勪欢 -->
@@ -185,11 +147,7 @@
<div class="list-title">
宸蹭笂浼犻檮浠� ({{ form.annexfilesList.length }})
</div>
- <el-table
- :data="form.annexfilesList"
- style="width: 100%"
- size="small"
- >
+ <el-table :data="form.annexfilesList" style="width: 100%" size="small">
<el-table-column label="鏂囦欢鍚�" min-width="200">
<template slot-scope="scope">
<i
@@ -240,8 +198,14 @@
</div>
<!-- 绌虹姸鎬� -->
- <div v-if="!form.annexfilesList || form.annexfilesList.length === 0" class="empty-attachment">
- <i class="el-icon-folder-opened" style="font-size: 60px; color: #C0C4CC; margin-bottom: 20px;"></i>
+ <div
+ v-if="!form.annexfilesList || form.annexfilesList.length == 0"
+ class="empty-attachment"
+ >
+ <i
+ class="el-icon-folder-opened"
+ style="font-size: 60px; color: #C0C4CC; margin-bottom: 20px;"
+ ></i>
<p style="color: #909399; font-size: 14px;">鏆傛棤闄勪欢锛岃涓婁紶鐩稿叧鏂囦欢</p>
</div>
</el-card>
@@ -249,8 +213,11 @@
<!-- 涓撳瀹℃煡鎯呭喌 -->
<el-card class="expert-card">
<div slot="header" class="clearfix">
- <span class="detail-title">涓撳瀹℃煡鎯呭喌 (18浣嶄笓瀹� + 1浣嶄富濮斾笓瀹�)</span>
+ <span class="detail-title">涓撳瀹℃煡鎯呭喌</span>
<div style="float: right;">
+ <el-button size="mini" type="primary" @click="handleAddExpert">
+ 娣诲姞涓撳
+ </el-button>
<el-button
size="mini"
type="primary"
@@ -286,20 +253,22 @@
<el-row :gutter="20">
<el-col :span="6">
<div class="stat-item">
- <span class="stat-label">涓撳宸插悓鎰�:</span>
- <span class="stat-value">{{ approvedNormalExperts }}/18</span>
+ <span class="stat-label">鏅�氫笓瀹�:</span>
+ <span class="stat-value">{{ normalExpertsCount }}浜�</span>
</div>
</el-col>
<el-col :span="6">
<div class="stat-item">
- <span class="stat-label">涓诲涓撳鐘舵��:</span>
- <span class="stat-value">{{ chiefExpertStatus }}</span>
+ <span class="stat-label">涓诲涓撳:</span>
+ <span class="stat-value">{{ chiefExpertsCount }}浜�</span>
</div>
</el-col>
<el-col :span="6">
<div class="stat-item">
- <span class="stat-label">鎬诲畬鎴愯繘搴�:</span>
- <span class="stat-value">{{ completionRate }}%</span>
+ <span class="stat-label">宸插悓鎰�:</span>
+ <span class="stat-value"
+ >{{ approvedExpertsCount }}/{{ totalExpertsCount }}</span
+ >
</div>
</el-col>
<el-col :span="6">
@@ -317,10 +286,10 @@
<!-- 涓撳瀹℃煡琛ㄦ牸 -->
<el-table
- :data="expertReviews"
+ :data="ethicalreviewopinionsList"
v-loading="expertLoading"
style="width: 100%"
- height="800"
+ height="600"
:row-class-name="getExpertRowClassName"
>
<el-table-column label="搴忓彿" width="60" align="center" type="index" />
@@ -332,9 +301,14 @@
fixed="left"
>
<template slot-scope="scope">
- <span>{{ scope.row.expertName }}</span>
+ <span
+ class="expert-name-link"
+ @click="handleViewExpertHistory(scope.row)"
+ >
+ {{ scope.row.expertname }}
+ </span>
<el-tag
- v-if="scope.row.isChief"
+ v-if="scope.row.expertType == '1'"
size="mini"
type="danger"
style="margin-left: 5px;"
@@ -343,18 +317,31 @@
</template>
</el-table-column>
+ <el-table-column label="涓撳缂栧彿" width="120" align="center">
+ <template slot-scope="scope">
+ <span>{{ scope.row.expertNo || "-" }}</span>
+ </template>
+ </el-table-column>
+
<el-table-column label="涓撳绫诲瀷" width="100" align="center">
<template slot-scope="scope">
- <span :class="scope.row.isChief ? 'chief-expert' : 'normal-expert'">
- {{ scope.row.isChief ? "涓诲涓撳" : "涓撳" }}
+ <span
+ :class="
+ scope.row.expertType == '1' ? 'chief-expert' : 'normal-expert'
+ "
+ >
+ {{ getExpertTypeText(scope.row.expertType) }}
</span>
</template>
</el-table-column>
<el-table-column label="瀹℃煡鐘舵��" width="100" align="center">
<template slot-scope="scope">
- <el-tag :type="statusFilter(scope.row.reviewStatus)" size="small">
- {{ statusTextFilter(scope.row.reviewStatus) }}
+ <el-tag
+ :type="getReviewStatusFilter(scope.row.receiveStatus)"
+ size="small"
+ >
+ {{ getReviewStatusText(scope.row.receiveStatus) }}
</el-tag>
</template>
</el-table-column>
@@ -362,11 +349,11 @@
<el-table-column label="涓撳缁撹" width="120" align="center">
<template slot-scope="scope">
<el-tag
- v-if="scope.row.expertConclusion"
- :type="conclusionFilter(scope.row.expertConclusion)"
+ v-if="scope.row.expertconclusion"
+ :type="getConclusionFilter(scope.row.expertconclusion)"
size="small"
>
- {{ conclusionTextFilter(scope.row.expertConclusion) }}
+ {{ getConclusionText(scope.row.expertconclusion) }}
</el-tag>
<span v-else class="no-data">-</span>
</template>
@@ -374,23 +361,33 @@
<el-table-column label="瀹℃煡鎰忚" min-width="200" show-overflow-tooltip>
<template slot-scope="scope">
- <span :class="{ 'expert-opinion': scope.row.expertOpinion }">
- {{ scope.row.expertOpinion || "鏆傛棤鎰忚" }}
+ <span :class="{ 'expert-opinion': scope.row.expertopinion }">
+ {{ scope.row.expertopinion || "鏆傛棤鎰忚" }}
</span>
+ </template>
+ </el-table-column>
+
+ <el-table-column label="缁撹椤哄簭" width="100" align="center">
+ <template slot-scope="scope">
+ <span>{{ scope.row.conclusionorder || "-" }}</span>
</template>
</el-table-column>
<el-table-column label="瀹℃煡鏃堕棿" width="160" align="center">
<template slot-scope="scope">
<span>{{
- scope.row.reviewTime ? parseTime(scope.row.reviewTime) : "鏈鏌�"
+ scope.row.conclusiontime
+ ? formatDateTime(scope.row.conclusiontime)
+ : "鏈鏌�"
}}</span>
</template>
</el-table-column>
<el-table-column label="鍙戦�佹椂闂�" width="160" align="center">
<template slot-scope="scope">
<span>{{
- scope.row.sendTime ? parseTime(scope.row.sendTime) : "鏈彂閫�"
+ scope.row.startTime
+ ? formatDateTime(scope.row.startTime)
+ : "鏈彂閫�"
}}</span>
</template>
</el-table-column>
@@ -402,17 +399,29 @@
type="text"
icon="el-icon-s-promotion"
@click="handleSendToExpert(scope.row)"
- :disabled="scope.row.reviewStatus === 'submitted'"
- :class="{ 'sent-button': scope.row.reviewStatus === 'submitted' }"
+ :disabled="
+ scope.row.receiveStatus == '2' ||
+ scope.row.receiveStatus == '3' ||
+ scope.row.receiveStatus == '4'
+ "
+ :class="{
+ 'sent-button':
+ scope.row.receiveStatus == '2' ||
+ scope.row.receiveStatus == '3'
+ }"
>
- {{ scope.row.reviewStatus === "submitted" ? "宸插彂閫�" : "鍙戦��" }}
+ {{
+ scope.row.receiveStatus == "2" || scope.row.receiveStatus == "3"
+ ? "宸插彂閫�"
+ : "鍙戦��"
+ }}
</el-button>
<el-button
size="mini"
type="text"
icon="el-icon-edit"
@click="handleEditExpertReview(scope.row)"
- :disabled="scope.row.reviewStatus !== 'submitted'"
+ :disabled="!['2', '3'].includes(scope.row.receiveStatus)"
>
缂栬緫
</el-button>
@@ -424,28 +433,187 @@
>
璇︽儏
</el-button>
+ <el-button
+ size="mini"
+ type="text"
+ icon="el-icon-delete"
+ @click="handleDeleteExpertReview(scope.$index)"
+ style="color: #f56c6c;"
+ >
+ 鍒犻櫎
+ </el-button>
</template>
</el-table-column>
</el-table>
</el-card>
+ <!-- 娣诲姞涓撳瀵硅瘽妗� -->
+ <el-dialog
+ title="娣诲姞涓撳"
+ :visible.sync="expertDialogVisible"
+ width="800px"
+ @close="handleExpertDialogClose"
+ >
+ <div style="margin-bottom: 20px;">
+ <el-input
+ v-model="expertSearchQuery"
+ placeholder="璇疯緭鍏ヤ笓瀹跺鍚嶆垨缂栧彿鎼滅储"
+ style="width: 300px; margin-right: 10px;"
+ @keyup.enter.native="handleSearchExperts"
+ >
+ <el-button
+ slot="append"
+ icon="el-icon-search"
+ @click="handleSearchExperts"
+ ></el-button>
+ </el-input>
+ <el-select
+ v-model="filterExpertType"
+ placeholder="涓撳绫诲瀷"
+ style="width: 150px; margin-right: 10px;"
+ @change="handleSearchExperts"
+ >
+ <el-option label="鍏ㄩ儴" value=""></el-option>
+ <el-option label="鏅�氫笓瀹�" value="0"></el-option>
+ <el-option label="涓讳换濮斿憳" value="1"></el-option>
+ </el-select>
+ <el-button type="primary" @click="handleSearchExperts">鎼滅储</el-button>
+ <el-button @click="handleResetSearch">閲嶇疆</el-button>
+ </div>
+
+ <el-table
+ :data="expertList"
+ v-loading="expertListLoading"
+ style="width: 100%"
+ max-height="400"
+ @selection-change="handleExpertSelectionChange"
+ >
+ <el-table-column type="selection" width="55"></el-table-column>
+ <el-table-column
+ label="涓撳濮撳悕"
+ prop="username"
+ width="120"
+ ></el-table-column>
+ <el-table-column
+ label="涓撳缂栧彿"
+ prop="userno"
+ width="120"
+ ></el-table-column>
+ <el-table-column label="涓撳绫诲瀷" width="100">
+ <template slot-scope="scope">
+ <el-tag
+ size="small"
+ :type="getIsChiefExpert(scope.row) ? 'danger' : ''"
+ >
+ {{ getIsChiefExpert(scope.row) ? "涓讳换濮斿憳" : "鏅�氫笓瀹�" }}
+ </el-tag>
+ </template>
+ </el-table-column>
+ <el-table-column
+ label="鍗曚綅鍚嶇О"
+ prop="unitname"
+ show-overflow-tooltip
+ ></el-table-column>
+ <el-table-column
+ label="鑱岀О"
+ prop="title"
+ width="100"
+ ></el-table-column>
+ <el-table-column
+ label="鑱旂郴鐢佃瘽"
+ prop="telephone"
+ width="120"
+ ></el-table-column>
+ </el-table>
+
+ <div style="margin-top: 20px; text-align: center;">
+ <el-pagination
+ @size-change="handlePageSizeChange"
+ @current-change="handlePageChange"
+ :current-page="expertPage.pageNum"
+ :page-sizes="[10, 20, 50, 100]"
+ :page-size="expertPage.pageSize"
+ layout="total, sizes, prev, pager, next, jumper"
+ :total="expertTotal"
+ ></el-pagination>
+ </div>
+
+ <div slot="footer">
+ <el-button @click="expertDialogVisible = false">鍙栨秷</el-button>
+ <el-button
+ type="primary"
+ @click="handleConfirmAddExpert"
+ :disabled="selectedExperts.length == 0"
+ >纭畾娣诲姞</el-button
+ >
+ </div>
+ </el-dialog>
+
<!-- 鍙戦�佷笓瀹跺璇濇 -->
<el-dialog
- title="鍙戦�佷笓瀹跺鏌�"
+ :title="sendDialogTitle"
:visible.sync="sendDialogVisible"
width="500px"
>
<el-form :model="sendForm" ref="sendForm" label-width="100px">
<el-form-item label="涓撳绫诲瀷" prop="expertType">
- <el-radio-group v-model="sendForm.expertType">
- <el-radio label="normal">涓撳</el-radio>
+ <el-radio-group
+ v-model="sendForm.expertType"
+ @change="handleExpertTypeChange"
+ >
+ <el-radio label="normal">鏅�氫笓瀹�</el-radio>
<el-radio label="chief">涓诲涓撳</el-radio>
</el-radio-group>
</el-form-item>
+
+ <el-form-item label="鍙戦�佹椂闂�" prop="startTime" required>
+ <el-date-picker
+ v-model="sendForm.startTime"
+ type="datetime"
+ placeholder="璇烽�夋嫨鍙戦�佹椂闂�"
+ value-format="yyyy-MM-dd HH:mm:ss"
+ style="width: 100%"
+ />
+ </el-form-item>
+
+ <el-form-item
+ label="鎴鏃堕棿"
+ prop="endTime"
+ :required="sendForm.expertType !== 'chief'"
+ >
+ <el-date-picker
+ v-model="sendForm.endTime"
+ type="datetime"
+ placeholder="璇烽�夋嫨鎴鏃堕棿"
+ value-format="yyyy-MM-dd HH:mm:ss"
+ style="width: 100%"
+ :disabled="sendForm.expertType === 'chief'"
+ />
+ <div
+ v-if="sendForm.expertType === 'chief'"
+ style="font-size: 12px; color: #999; margin-top: 5px;"
+ >
+ 涓诲涓撳鏃犻渶璁剧疆鎴鏃堕棿
+ </div>
+ </el-form-item>
+
+ <el-form-item label="鍙戦�佹柟寮�" prop="sendType" required>
+ <el-select
+ v-model="sendForm.sendType"
+ placeholder="璇烽�夋嫨鍙戦�佹柟寮�"
+ style="width: 100%"
+ >
+ <el-option label="绯荤粺鍙戦��" value="0"></el-option>
+ <el-option label="閭欢鍙戦��" value="1"></el-option>
+ <el-option label="鐭俊鍙戦��" value="2"></el-option>
+ <el-option label="鍏朵粬鏂瑰紡" value="3"></el-option>
+ </el-select>
+ </el-form-item>
+
<el-form-item
label="閫夋嫨涓撳"
prop="expertIds"
- v-if="sendForm.expertType === 'normal'"
+ v-if="sendForm.expertType == 'normal'"
>
<el-select
v-model="sendForm.expertIds"
@@ -454,13 +622,18 @@
style="width: 100%"
>
<el-option
- v-for="expert in availableExperts"
- :key="expert.id"
- :label="expert.name"
- :value="expert.id"
+ v-for="expert in availableNormalExperts"
+ :key="getExpertKey(expert)"
+ :label="
+ `${expert.expertname}${
+ expert.expertNo ? '(' + expert.expertNo + ')' : ''
+ }`
+ "
+ :value="getExpertKey(expert)"
/>
</el-select>
</el-form-item>
+
<el-form-item label="鍙戦�佸唴瀹�" prop="content">
<el-input
type="textarea"
@@ -472,9 +645,122 @@
</el-form>
<div slot="footer">
<el-button @click="sendDialogVisible = false">鍙栨秷</el-button>
- <el-button type="primary" @click="handleSendConfirm"
+ <el-button type="primary" @click="handleSendConfirm" :loading="sending"
>纭鍙戦��</el-button
>
+ </div>
+ </el-dialog>
+
+ <!-- 涓撳鍘嗗彶瀹℃壒鎯呭喌瀵硅瘽妗� -->
+ <el-dialog
+ title="涓撳鍘嗗彶瀹℃壒鎯呭喌"
+ :visible.sync="expertHistoryDialogVisible"
+ width="600px"
+ >
+ <div v-loading="expertHistoryLoading">
+ <div v-if="expertHistoryData" style="padding: 20px;">
+ <el-row :gutter="20" style="margin-bottom: 20px;">
+ <el-col :span="12">
+ <div class="history-stat-item">
+ <div class="history-stat-label">涓撳濮撳悕</div>
+ <div class="history-stat-value">
+ {{ currentExpertInfo.expertname || "-" }}
+ </div>
+ </div>
+ </el-col>
+ <el-col :span="12">
+ <div class="history-stat-item">
+ <div class="history-stat-label">涓撳缂栧彿</div>
+ <div class="history-stat-value">
+ {{ currentExpertInfo.expertNo || "-" }}
+ </div>
+ </div>
+ </el-col>
+ </el-row>
+
+ <el-divider></el-divider>
+
+ <el-row :gutter="20">
+ <el-col :span="12">
+ <div class="history-stat-item">
+ <div class="history-stat-label">鎬诲鎵规暟閲�</div>
+ <div
+ class="history-stat-value"
+ style="font-size: 24px; color: #409EFF;"
+ >
+ {{ expertHistoryData.count || 0 }}
+ </div>
+ </div>
+ </el-col>
+ <el-col :span="12">
+ <div class="history-stat-item">
+ <div class="history-stat-label">宸叉帴鏀舵暟閲�</div>
+ <div class="history-stat-value">
+ {{ expertHistoryData.acceptCount || 0 }}
+ </div>
+ </div>
+ </el-col>
+ </el-row>
+
+ <el-row :gutter="20" style="margin-top: 20px;">
+ <el-col :span="12">
+ <div class="history-stat-item">
+ <div class="history-stat-label">鏈帴鏀舵暟閲�</div>
+ <div class="history-stat-value">
+ {{ expertHistoryData.notAcceptCount || 0 }}
+ </div>
+ </div>
+ </el-col>
+ <el-col :span="12">
+ <div class="history-stat-item">
+ <div class="history-stat-label">鏈夋剰瑙佹暟閲�</div>
+ <div class="history-stat-value">
+ {{ expertHistoryData.opinionCount || 0 }}
+ </div>
+ </div>
+ </el-col>
+ </el-row>
+
+ <el-row :gutter="20" style="margin-top: 20px;">
+ <el-col :span="12">
+ <div class="history-stat-item">
+ <div class="history-stat-label">鏃犳剰瑙佹暟閲�</div>
+ <div class="history-stat-value">
+ {{ expertHistoryData.notOpinionCount || 0 }}
+ </div>
+ </div>
+ </el-col>
+ <el-col :span="12">
+ <div class="history-stat-item">
+ <div class="history-stat-label">鏈夐檮浠舵暟閲�</div>
+ <div class="history-stat-value">
+ {{ expertHistoryData.annexCount || 0 }}
+ </div>
+ </div>
+ </el-col>
+ </el-row>
+
+ <el-row :gutter="20" style="margin-top: 20px;">
+ <el-col :span="12">
+ <div class="history-stat-item">
+ <div class="history-stat-label">鏃犻檮浠舵暟閲�</div>
+ <div class="history-stat-value">
+ {{ expertHistoryData.notAnnexCount || 0 }}
+ </div>
+ </div>
+ </el-col>
+ </el-row>
+ </div>
+ <div v-else style="text-align: center; padding: 40px 0;">
+ <i
+ class="el-icon-info"
+ style="font-size: 60px; color: #C0C4CC; margin-bottom: 20px;"
+ ></i>
+ <p style="color: #909399; font-size: 14px;">鏆傛棤鍘嗗彶瀹℃壒鏁版嵁</p>
+ </div>
+ </div>
+ <div slot="footer">
+ <el-button @click="expertHistoryDialogVisible = false">鍏抽棴</el-button>
</div>
</el-dialog>
@@ -494,8 +780,10 @@
reviewinitiateBaseInfoList,
ethicalreviewedit,
ethicalreviewadd,
- ethicalreviewInfo
+ ethicalreviewInfo,
+ ethicalreExpertTotal
} from "@/api/businessApi";
+import { listExternalperson } from "@/api/project/externalperson";
import CaseBasicInfo from "@/components/CaseBasicInfo";
import UploadAttachment from "@/components/UploadAttachment";
import FilePreviewDialog from "@/components/FilePreviewDialog";
@@ -504,7 +792,7 @@
export default {
name: "EthicsReviewDetail",
components: { CaseBasicInfo, UploadAttachment, FilePreviewDialog },
- dicts: ["sys_user_sex", "sys_ethical"],
+ dicts: ["sys_user_sex", "sys_ethical", "Review_status"],
data() {
return {
@@ -526,7 +814,7 @@
initiatePerson: "",
// 鐘舵�佸拰鏃堕棿
- status: "0", // 0:鏂板缓, 1:瀹℃煡涓�, 2:缁撴潫
+ status: "0", // 0:寰呭鏌�, 1:瀹℃煡涓�, 2:瀹℃煡涓, 3:瀹℃煡瀹屾垚
startTime: "",
cutOffTime: "",
endTime: "",
@@ -534,7 +822,7 @@
// 涓撳淇℃伅
expertName: "",
expertNo: "",
- expertType: "normal",
+ expertType: "0",
expertConclusion: "",
expertOpinion: "",
expertTime: "",
@@ -547,6 +835,9 @@
annexfilesList: [],
filePatch: "",
+ // 涓撳瀹℃煡鎰忚鍒楄〃
+ ethicalreviewopinionsList: [],
+
// 绯荤粺瀛楁
createBy: "",
createTime: "",
@@ -554,6 +845,7 @@
updateTime: "",
delFlag: "0"
},
+
// 琛ㄥ崟楠岃瘉瑙勫垯
rules: {
initiateTheme: [
@@ -571,12 +863,6 @@
status: [
{ required: true, message: "瀹℃煡鐘舵�佷笉鑳戒负绌�", trigger: "change" }
],
- startTime: [
- { required: true, message: "鍙戣捣鏃堕棿涓嶈兘涓虹┖", trigger: "change" }
- ],
- cutOffTime: [
- { required: true, message: "鎴鏃堕棿涓嶈兘涓虹┖", trigger: "change" }
- ],
expertName: [
{ max: 50, message: "闀垮害涓嶈兘瓒呰繃 50 涓瓧绗�", trigger: "blur" }
],
@@ -590,8 +876,13 @@
{ max: 500, message: "闀垮害涓嶈兘瓒呰繃 500 涓瓧绗�", trigger: "blur" }
]
},
+
// 淇濆瓨鍔犺浇鐘舵��
saveLoading: false,
+ completeLoading: false,
+ suspendLoading: false,
+ endLoading: false,
+ sending: false,
// 闄勪欢鐩稿叧
attachmentFileList: [],
@@ -601,305 +892,175 @@
currentPreviewFile: null,
// 涓撳瀹℃煡鏁版嵁
- expertReviews: [
- // 涓撳锛�18浣嶏級- 鍒濆鐘舵�佷负鐢宠涓�
- {
- id: 1,
- expertName: "闄舵槉",
- isChief: false,
- reviewStatus: "applying",
- expertConclusion: "",
- expertOpinion: "",
- reviewTime: "",
- sendTime: ""
- },
- {
- id: 2,
- expertName: "鍒樻枌",
- isChief: false,
- reviewStatus: "applying",
- expertConclusion: "",
- expertOpinion: "",
- reviewTime: "",
- sendTime: ""
- },
- {
- id: 3,
- expertName: "浜庢捣鍒� ",
- isChief: false,
- reviewStatus: "applying",
- expertConclusion: "",
- expertOpinion: "",
- reviewTime: "",
- sendTime: ""
- },
- {
- id: 4,
- expertName: "鐜嬬孩姊�",
- isChief: false,
- reviewStatus: "applying",
- expertConclusion: "",
- expertOpinion: "",
- reviewTime: "",
- sendTime: ""
- },
- {
- id: 5,
- expertName: "鐜嬫槬鍏�",
- isChief: false,
- reviewStatus: "applying",
- expertConclusion: "",
- expertOpinion: "",
- reviewTime: "",
- sendTime: ""
- },
- {
- id: 6,
- expertName: "鐜嬮潤",
- isChief: false,
- reviewStatus: "applying",
- expertConclusion: "",
- expertOpinion: "",
- reviewTime: "",
- sendTime: ""
- },
- {
- id: 7,
- expertName: "杈规枃瓒�",
- isChief: false,
- reviewStatus: "applying",
- expertConclusion: "",
- expertOpinion: "",
- reviewTime: "",
- sendTime: ""
- },
- {
- id: 8,
- expertName: "闂織鍕�",
- isChief: false,
- reviewStatus: "applying",
- expertConclusion: "",
- expertOpinion: "",
- reviewTime: "",
- sendTime: ""
- },
- {
- id: 9,
- expertName: "璁稿嚖",
- isChief: false,
- reviewStatus: "applying",
- expertConclusion: "",
- expertOpinion: "",
- reviewTime: "",
- sendTime: ""
- },
- {
- id: 10,
- expertName: "璁镐紶灞�",
- isChief: false,
- reviewStatus: "applying",
- expertConclusion: "",
- expertOpinion: "",
- reviewTime: "",
- sendTime: ""
- },
- {
- id: 11,
- expertName: "寮犵孩宀�",
- isChief: false,
- reviewStatus: "applying",
- expertConclusion: "",
- expertOpinion: "",
- reviewTime: "",
- sendTime: ""
- },
- {
- id: 12,
- expertName: "鏉ㄨ嫃姘�",
- isChief: false,
- reviewStatus: "applying",
- expertConclusion: "",
- expertOpinion: "",
- reviewTime: "",
- sendTime: ""
- },
- {
- id: 13,
- expertName: "瀹嬬帀寮�",
- isChief: false,
- reviewStatus: "applying",
- expertConclusion: "",
- expertOpinion: "",
- reviewTime: "",
- sendTime: ""
- },
- {
- id: 14,
- expertName: "鍛ㄤ紶鍒�",
- isChief: false,
- reviewStatus: "applying",
- expertConclusion: "",
- expertOpinion: "",
- reviewTime: "",
- sendTime: ""
- },
- {
- id: 15,
- expertName: "鑽嗗嚒娉�",
- isChief: false,
- reviewStatus: "applying",
- expertConclusion: "",
- expertOpinion: "",
- reviewTime: "",
- sendTime: ""
- },
- {
- id: 16,
- expertName: "鐭枃鎹�",
- isChief: false,
- reviewStatus: "applying",
- expertConclusion: "",
- expertOpinion: "",
- reviewTime: "",
- sendTime: ""
- },
- {
- id: 17,
- expertName: "钁i渿",
- isChief: false,
- reviewStatus: "applying",
- expertConclusion: "",
- expertOpinion: "",
- reviewTime: "",
- sendTime: ""
- },
- {
- id: 18,
- expertName: "钄¢噾璐�",
- isChief: false,
- reviewStatus: "applying",
- expertConclusion: "",
- expertOpinion: "",
- reviewTime: "",
- sendTime: ""
- },
- // 涓诲涓撳锛�1浣嶏級
- {
- id: 19,
- expertName: "瀛斿績娑�",
- isChief: true,
- reviewStatus: "applying",
- expertConclusion: "",
- expertOpinion: "",
- reviewTime: "",
- sendTime: ""
- }
- ],
+ expertReviews: [],
expertLoading: false,
attachmentLoading: false,
+
+ // 娣诲姞涓撳瀵硅瘽妗�
+ expertDialogVisible: false,
+ expertList: [],
+ expertListLoading: false,
+ expertSearchQuery: "",
+ filterExpertType: "",
+ expertPage: {
+ pageNum: 1,
+ pageSize: 50
+ },
+ expertTotal: 0,
+ selectedExperts: [],
// 鍙戦�佸璇濇
sendDialogVisible: false,
sendForm: {
expertType: "normal",
expertIds: [],
+ startTime: "",
+ endTime: "",
+ sendType: "0",
content: ""
},
- // 鍙敤涓撳鍒楄〃
- availableExperts: [
- { id: 1, name: "闄舵槉", type: "normal" },
- { id: 2, name: "鍒樻枌", type: "normal" },
- { id: 3, name: "浜庢捣鍒�", type: "normal" },
- { id: 4, name: "鐜嬬孩姊�", type: "normal" },
- { id: 5, name: "鐜嬫槬鍏�", type: "normal" },
- { id: 6, name: "鐜嬮潤", type: "normal" },
- { id: 7, name: "杈规枃瓒�", type: "normal" },
- { id: 8, name: "闂織鍕�", type: "normal" },
- { id: 9, name: "璁稿嚖", type: "normal" },
- { id: 10, name: "璁镐紶灞�", type: "normal" },
- { id: 11, name: "寮犵孩宀�", type: "normal" },
- { id: 12, name: "鏉ㄨ嫃姘�", type: "normal" },
- { id: 13, name: "瀹嬬帀寮�", type: "normal" },
- { id: 14, name: "鍛ㄤ紶鍒�", type: "normal" },
- { id: 15, name: "鑽嗗嚒娉�", type: "normal" },
- { id: 16, name: "鐭枃鎹�", type: "normal" },
- { id: 17, name: "钁i渿", type: "normal" },
- { id: 18, name: "钄¢噾璐�", type: "normal" },
- { id: 19, name: "瀛斿績娑�", type: "chief" }
- ]
+ // 涓撳鍘嗗彶瀹℃壒鎯呭喌
+ expertHistoryDialogVisible: false,
+ expertHistoryLoading: false,
+ expertHistoryData: null,
+ currentExpertInfo: {},
+
+ // 鍐呴儴鐘舵�佽窡韪�
+ internalExpertList: []
};
},
computed: {
- // 璁$畻灞炴�э細涓撳鍚屾剰鏁伴噺
- approvedNormalExperts() {
- return this.expertReviews.filter(
- expert => !expert.isChief && expert.expertConclusion === "approved"
+ // 璁$畻灞炴�э細鑾峰彇涓撳鍒楄〃锛堢‘淇濆搷搴斿紡锛�
+ ethicalreviewopinionsList() {
+ return this.form.ethicalreviewopinionsList || [];
+ },
+
+ // 璁$畻灞炴�э細鏅�氫笓瀹舵暟閲�
+ normalExpertsCount() {
+ return this.ethicalreviewopinionsList.filter(
+ expert => expert.expertType === "0"
).length;
},
- // 璁$畻灞炴�э細涓诲涓撳鐘舵��
- chiefExpertStatus() {
- const chiefExpert = this.expertReviews.find(expert => expert.isChief);
- return chiefExpert
- ? this.statusTextFilter(chiefExpert.reviewStatus)
- : "鏈垎閰�";
- },
- // 璁$畻灞炴�э細瀹屾垚杩涘害
- completionRate() {
- const totalExperts = this.expertReviews.length;
- const completedExperts = this.expertReviews.filter(
- expert => expert.reviewStatus === "submitted"
+
+ // 璁$畻灞炴�э細涓诲涓撳鏁伴噺
+ chiefExpertsCount() {
+ return this.ethicalreviewopinionsList.filter(
+ expert => expert.expertType === "1"
).length;
- return totalExperts > 0
- ? Math.round((completedExperts / totalExperts) * 100)
- : 0;
},
+
+ // 璁$畻灞炴�э細鎬讳笓瀹舵暟閲�
+ totalExpertsCount() {
+ return this.ethicalreviewopinionsList.length;
+ },
+
+ // 璁$畻灞炴�э細宸插悓鎰忎笓瀹舵暟閲�
+ approvedExpertsCount() {
+ return this.ethicalreviewopinionsList.filter(
+ expert => expert.expertconclusion === "1"
+ ).length;
+ },
+
// 璁$畻灞炴�э細鎬讳綋缁撹
overallConclusionText() {
- if (this.approvedNormalExperts >= 12) {
+ const total = this.totalExpertsCount;
+ const approved = this.approvedExpertsCount;
+
+ if (total === 0) return "鏈鏌�";
+ if (approved >= Math.ceil(total * 0.7)) {
+ // 瓒呰繃70%鍚屾剰
return "閫氳繃";
- } else if (this.approvedNormalExperts >= 9) {
+ } else if (approved >= Math.ceil(total * 0.5)) {
+ // 瓒呰繃50%鍚屾剰
return "淇敼鍚庨�氳繃";
} else {
return "涓嶉�氳繃";
}
},
+
overallConclusionFilter() {
- if (this.approvedNormalExperts >= 12) {
+ const total = this.totalExpertsCount;
+ const approved = this.approvedExpertsCount;
+
+ if (total === 0) return "info";
+ if (approved >= Math.ceil(total * 0.7)) {
return "success";
- } else if (this.approvedNormalExperts >= 9) {
+ } else if (approved >= Math.ceil(total * 0.5)) {
return "warning";
} else {
return "danger";
}
},
- // 鏄惁鍙互鍙戦�佺粰涓撳
+
+ // 鍙彂閫佺殑鏅�氫笓瀹�
+ availableNormalExperts() {
+ return this.ethicalreviewopinionsList.filter(
+ expert =>
+ expert.expertType === "0" &&
+ (!expert.receiveStatus ||
+ expert.receiveStatus === "0" ||
+ expert.receiveStatus === "1")
+ );
+ },
+
+ // 鍙彂閫佺殑涓诲涓撳
+ availableChiefExperts() {
+ return this.ethicalreviewopinionsList.filter(
+ expert =>
+ expert.expertType === "1" &&
+ (!expert.receiveStatus ||
+ expert.receiveStatus === "0" ||
+ expert.receiveStatus === "1")
+ );
+ },
+
+ // 鏄惁鍙互鍙戦�佺粰鏅�氫笓瀹�
canSendToNormalExperts() {
- return (
- this.expertReviews.filter(
- expert => !expert.isChief && expert.reviewStatus === "applying"
- ).length > 0
- );
+ return this.availableNormalExperts.length > 0;
},
- // 鏄惁鍙互鍙戦�佺粰涓诲涓撳锛堥渶瑕佽嚦灏�12涓笓瀹跺悓鎰忥級
+
+ // 鏄惁鍙互鍙戦�佺粰涓诲涓撳锛堥渶瑕佽嚦灏�12涓櫘閫氫笓瀹跺悓鎰忥級
canSendToChiefExpert() {
- return (
- this.approvedNormalExperts >= 12 &&
- this.expertReviews.filter(
- expert => expert.isChief && expert.reviewStatus === "applying"
- ).length > 0
- );
+ const normalApprovedCount = this.ethicalreviewopinionsList.filter(
+ expert => expert.expertType === "0" && expert.expertconclusion === "1"
+ ).length;
+ return this.availableChiefExperts.length > 0 && normalApprovedCount >= 12;
},
+
// 鏄惁鍙互鎵归噺鍙戦��
canBatchSend() {
return (
- this.expertReviews.filter(expert => expert.reviewStatus === "applying")
- .length > 0
+ this.availableNormalExperts.length > 0 ||
+ this.availableChiefExperts.length > 0
);
},
+
// 褰撳墠鐢ㄦ埛淇℃伅
currentUser() {
return JSON.parse(sessionStorage.getItem("user") || "{}");
+ },
+
+ // 鍙戦�佸璇濇鏍囬
+ sendDialogTitle() {
+ if (this.sendForm.expertType === "chief") {
+ return "鍙戦�佷富濮斾笓瀹跺鏌�";
+ } else if (this.sendForm.expertType === "normal") {
+ return "鍙戦�佹櫘閫氫笓瀹跺鏌�";
+ } else {
+ return "鍙戦�佷笓瀹跺鏌�";
+ }
+ }
+ },
+ watch: {
+ // 鐩戝惉琛ㄥ崟涓殑涓撳鍒楄〃鍙樺寲
+ "form.ethicalreviewopinionsList": {
+ handler(newVal) {
+ console.log("涓撳鍒楄〃鍙樺寲:", newVal);
+ // 寮哄埗瑙﹀彂璁$畻灞炴�ф洿鏂�
+ this.$forceUpdate();
+ },
+ deep: true
}
},
created() {
@@ -940,16 +1101,25 @@
// 瑙f瀽 filePatch 瀛楁
this.parseFilePatch(this.form.filePatch);
this.initAttachmentFileList();
+
+ // 濡傛灉涓撳瀹℃煡鎰忚鍒楄〃涓嶅瓨鍦紝鍒濆鍖栦负绌烘暟缁�
+ if (!this.form.ethicalreviewopinionsList) {
+ this.$set(this.form, "ethicalreviewopinionsList", []);
+ }
} else if (response.data && infoid) {
this.form = response.data[0];
// 瑙f瀽 filePatch 瀛楁
this.parseFilePatch(this.form.filePatch);
this.initAttachmentFileList();
- }
- console.log(this.form, "this.form ");
- this.infoid = detailData.infoid || this.infoid;
- this.caseNo = detailData.caseNo || "";
+ // 濡傛灉涓撳瀹℃煡鎰忚鍒楄〃涓嶅瓨鍦紝鍒濆鍖栦负绌烘暟缁�
+ if (!this.form.ethicalreviewopinionsList) {
+ this.$set(this.form, "ethicalreviewopinionsList", []);
+ }
+ }
+
+ // 璁剧疆 expertReviews 鐢ㄤ簬琛ㄦ牸鏄剧ず
+ this.expertReviews = this.form.ethicalreviewopinionsList;
this.$message.success("鏁版嵁鍔犺浇鎴愬姛");
} else {
@@ -982,7 +1152,11 @@
initAttachmentFileList() {
if (this.form.annexfilesList && this.form.annexfilesList.length > 0) {
this.attachmentFileList = this.form.annexfilesList.map(item => ({
- uid: item.id || Math.random().toString(36).substr(2, 9),
+ uid:
+ item.id ||
+ Math.random()
+ .toString(36)
+ .substr(2, 9),
name: item.fileName,
url: item.path || item.fileUrl,
status: "success"
@@ -1046,11 +1220,6 @@
handleUploadError({ file, fileList, error }) {
console.error("闄勪欢涓婁紶澶辫触:", error);
this.$message.error("鏂囦欢涓婁紶澶辫触锛岃閲嶈瘯");
- },
-
- // 鎵撳紑涓婁紶瀵硅瘽妗�
- openUploadDialog() {
- this.$refs.uploadAttachment.openUpload();
},
// 鏂囦欢棰勮
@@ -1129,53 +1298,80 @@
}
},
- // 鑾峰彇涓撳瀹℃煡鍒楄〃
- getExpertReviews(ethicsReviewId) {
- this.expertLoading = true;
- setTimeout(() => {
- this.expertLoading = false;
- }, 500);
+ // 鍒ゆ柇鏄惁涓轰富浠诲鍛�
+ getIsChiefExpert(expert) {
+ // 鑱岀О鍖呭惈"涓讳换濮斿憳"鎴栬�卐xpertType涓�"1"
+ return (
+ (expert.title && expert.title.includes("涓讳换濮斿憳")) ||
+ expert.expertType === "1"
+ );
},
- // 涓撳琛屾牱寮�
- getExpertRowClassName({ row }) {
- return row.isChief ? "chief-expert-row" : "normal-expert-row";
+ // 涓撳绫诲瀷鏂囨湰杞崲
+ getExpertTypeText(type) {
+ return type === "1" ? "涓诲涓撳" : "鏅�氫笓瀹�";
},
- // 鐘舵�佽繃婊ゅ櫒
- statusFilter(status) {
+ // 瀹℃煡鐘舵�佽繃婊ゅ櫒
+ getReviewStatusFilter(status) {
const statusMap = {
- applying: "info",
- submitted: "success"
+ "0": "info", // 寰呮帴鏀�
+ "1": "warning", // 鏈帴鏀�
+ "2": "success", // 宸叉帴鏀�
+ "3": "danger", // 瓒呮椂
+ "4": "danger", // 涓
+ "5": "success" // 瀹屾垚
};
return statusMap[status] || "info";
},
- statusTextFilter(status) {
+ getReviewStatusText(status) {
const statusMap = {
- applying: "鐢宠涓�",
- submitted: "宸叉彁浜�"
+ "0": "寰呮帴鏀�",
+ "1": "鏈帴鏀�",
+ "2": "宸叉帴鏀�",
+ "3": "瓒呮椂",
+ "4": "涓",
+ "5": "瀹屾垚"
};
return statusMap[status] || "鏈煡";
},
// 缁撹杩囨护鍣�
- conclusionFilter(conclusion) {
+ getConclusionFilter(conclusion) {
const conclusionMap = {
- approved: "success",
- approved_with_modifications: "warning",
- disapproved: "danger"
+ "1": "success", // 鍚屾剰
+ "2": "warning", // 瀹℃煡涓�
+ "0": "danger" // 涓嶅悓鎰�
};
return conclusionMap[conclusion] || "info";
},
- conclusionTextFilter(conclusion) {
+ getConclusionText(conclusion) {
const conclusionMap = {
- approved: "鍚屾剰",
- approved_with_modifications: "淇敼鍚庡悓鎰�",
- disapproved: "涓嶅悓鎰�"
+ "1": "鍚屾剰",
+ "2": "瀹℃煡涓�",
+ "0": "涓嶅悓鎰�"
};
return conclusionMap[conclusion] || "鏈煡";
+ },
+
+ // 涓撳琛屾牱寮�
+ getExpertRowClassName({ row }) {
+ return row.expertType === "1" ? "chief-expert-row" : "normal-expert-row";
+ },
+
+ // 鑾峰彇涓撳鍞竴鏍囪瘑
+ getExpertKey(expert) {
+ return expert.id || expert.expertNo || expert.expertname;
+ },
+
+ // 涓撳绫诲瀷鍙樻洿澶勭悊
+ handleExpertTypeChange() {
+ if (this.sendForm.expertType === "chief") {
+ // 涓诲涓撳鏃犻渶璁剧疆鎴鏃堕棿
+ this.sendForm.endTime = "";
+ }
},
// 淇濆瓨淇℃伅
@@ -1223,6 +1419,98 @@
});
},
+ // 瀹℃煡瀹屾垚
+ async handleCompleteReview() {
+ this.$confirm("纭畾瑕佸皢鏈浼︾悊瀹℃煡鐘舵�佽涓哄畬鎴愬悧锛�", "鎻愮ず", {
+ confirmButtonText: "纭畾",
+ cancelButtonText: "鍙栨秷",
+ type: "warning"
+ })
+ .then(async () => {
+ this.completeLoading = true;
+ try {
+ const updateData = {
+ ...this.form,
+ status: "3", // 瀹℃煡瀹屾垚
+ endTime: new Date()
+ .toISOString()
+ .replace("T", " ")
+ .substring(0, 19)
+ };
+
+ const response = await ethicalreviewedit(updateData);
+
+ if (response.code === 200) {
+ this.$message.success("瀹℃煡鐘舵�佸凡鏇存柊涓哄畬鎴�");
+ this.form.status = "3";
+ this.form.endTime = updateData.endTime;
+ } else {
+ this.$message.error("鎿嶄綔澶辫触锛�" + (response.msg || "鏈煡閿欒"));
+ }
+ } catch (error) {
+ console.error("鏇存柊瀹℃煡鐘舵�佸け璐�:", error);
+ this.$message.error("鏇存柊瀹℃煡鐘舵�佸け璐�");
+ } finally {
+ this.completeLoading = false;
+ }
+ })
+ .catch(() => {});
+ },
+
+ // 瀹℃煡涓
+ async handleSuspendReview() {
+ this.$confirm(
+ "纭畾瑕佷腑姝㈡湰娆′鸡鐞嗗鏌ュ悧锛熸墍鏈変笓瀹剁殑瀹℃煡鐘舵�佸皢鍙樹负涓銆�",
+ "鎻愮ず",
+ {
+ confirmButtonText: "纭畾",
+ cancelButtonText: "鍙栨秷",
+ type: "warning"
+ }
+ )
+ .then(async () => {
+ this.suspendLoading = true;
+ try {
+ // 鏇存柊鎵�鏈変笓瀹剁殑鎺ユ敹鐘舵�佷负涓
+ if (
+ this.form.ethicalreviewopinionsList &&
+ this.form.ethicalreviewopinionsList.length > 0
+ ) {
+ this.form.ethicalreviewopinionsList.forEach(expert => {
+ expert.receiveStatus = "4"; // 涓鐘舵��
+ expert.updateTime = new Date()
+ .toISOString()
+ .replace("T", " ")
+ .substring(0, 19);
+ });
+ }
+
+ const updateData = {
+ ...this.form,
+ status: "2" // 瀹℃煡涓
+ };
+
+ const response = await ethicalreviewedit(updateData);
+
+ if (response.code === 200) {
+ this.$message.success("瀹℃煡宸蹭腑姝紝鎵�鏈変笓瀹剁姸鎬佸凡鏇存柊");
+ this.form.status = "2";
+
+ // 瑙﹀彂璁$畻灞炴�ф洿鏂�
+ this.$forceUpdate();
+ } else {
+ this.$message.error("鎿嶄綔澶辫触锛�" + (response.msg || "鏈煡閿欒"));
+ }
+ } catch (error) {
+ console.error("涓瀹℃煡澶辫触:", error);
+ this.$message.error("涓瀹℃煡澶辫触");
+ } finally {
+ this.suspendLoading = false;
+ }
+ })
+ .catch(() => {});
+ },
+
// 缁撴潫瀹℃煡
async handleEndReview() {
this.$confirm(
@@ -1235,10 +1523,11 @@
}
)
.then(async () => {
+ this.endLoading = true;
try {
const updateData = {
...this.form,
- status: "2",
+ status: "2", // 瀹℃煡涓
endTime: new Date()
.toISOString()
.replace("T", " ")
@@ -1257,78 +1546,292 @@
} catch (error) {
console.error("缁撴潫瀹℃煡澶辫触:", error);
this.$message.error("缁撴潫瀹℃煡澶辫触");
+ } finally {
+ this.endLoading = false;
}
})
.catch(() => {});
},
- // 鍙戦�佺粰涓撳
- handleSendToNormalExperts() {
- const normalExperts = this.expertReviews.filter(
- expert => !expert.isChief && expert.reviewStatus === "applying"
+ // 鎵撳紑娣诲姞涓撳瀵硅瘽妗�
+ handleAddExpert() {
+ this.expertDialogVisible = true;
+ this.loadExperts();
+ },
+
+ // 鍔犺浇涓撳鍒楄〃
+ async loadExperts() {
+ try {
+ this.expertListLoading = true;
+ const params = {
+ usertype: "浼︾悊涓撳", // 浼︾悊涓撳
+ pageNum: this.expertPage.pageNum,
+ pageSize: this.expertPage.pageSize
+ };
+
+ if (this.expertSearchQuery) {
+ params.username = this.expertSearchQuery;
+ }
+
+ const response = await listExternalperson(params);
+ if (response.code === 200) {
+ this.expertList = response.rows;
+ this.expertTotal = response.total;
+ } else {
+ this.$message.error(
+ "鍔犺浇涓撳鍒楄〃澶辫触锛�" + (response.msg || "鏈煡閿欒")
+ );
+ }
+ } catch (error) {
+ console.error("鍔犺浇涓撳鍒楄〃澶辫触:", error);
+ this.$message.error("鍔犺浇涓撳鍒楄〃澶辫触");
+ } finally {
+ this.expertListLoading = false;
+ }
+ },
+
+ // 鎼滅储涓撳
+ handleSearchExperts() {
+ this.expertPage.pageNum = 1;
+ this.loadExperts();
+ },
+
+ // 閲嶇疆鎼滅储
+ handleResetSearch() {
+ this.expertSearchQuery = "";
+ this.filterExpertType = "";
+ this.expertPage.pageNum = 1;
+ this.loadExperts();
+ },
+
+ // 涓撳閫夋嫨鍙樺寲
+ handleExpertSelectionChange(selection) {
+ this.selectedExperts = selection;
+ },
+
+ // 纭娣诲姞涓撳
+ handleConfirmAddExpert() {
+ if (this.selectedExperts.length === 0) {
+ this.$message.warning("璇烽�夋嫨瑕佹坊鍔犵殑涓撳");
+ return;
+ }
+
+ // 纭繚ethicalreviewopinionsList瀛樺湪
+ if (!this.form.ethicalreviewopinionsList) {
+ this.$set(this.form, "ethicalreviewopinionsList", []);
+ }
+
+ // 杩囨护宸插瓨鍦ㄧ殑涓撳
+ const existingExpertIds = this.form.ethicalreviewopinionsList.map(
+ expert => expert.expertNo || expert.expertname
);
- this.sendForm.expertIds = normalExperts.map(expert => expert.id);
+ const newExperts = this.selectedExperts.filter(expert => {
+ return !existingExpertIds.includes(expert.userno || expert.username);
+ });
+
+ if (newExperts.length === 0) {
+ this.$message.warning("閫夋嫨鐨勪笓瀹跺凡瀛樺湪");
+ return;
+ }
+
+ // 娣诲姞涓撳鍒板垪琛�
+ newExperts.forEach(expert => {
+ // 鍒ゆ柇鏄惁涓轰富浠诲鍛�
+ const isChief = this.getIsChiefExpert(expert);
+
+ const expertReview = {
+ id: undefined,
+ infoid: this.infoid,
+ nitiateId: this.form.id || undefined,
+ caseNo: this.form.caseNo,
+ expertname: expert.username,
+ expertNo: expert.userno,
+ expertType: isChief ? "1" : "0", // 涓讳换濮斿憳璁剧疆涓轰富濮斾笓瀹�
+ deptName: expert.unitname || "",
+ title: expert.title || "",
+ telephone: expert.telephone || "",
+ receiveStatus: "0", // 寰呮帴鏀�
+ expertconclusion: "",
+ expertopinion: "",
+ conclusionannex: "",
+ conclusionorder: this.form.ethicalreviewopinionsList.length + 1,
+ conclusiontime: "",
+ startTime: "",
+ endTime: "",
+ sendType: "",
+ createBy: this.currentUser.username || "admin",
+ createTime: new Date()
+ .toISOString()
+ .replace("T", " ")
+ .substring(0, 19),
+ updateBy: this.currentUser.username || "admin",
+ updateTime: new Date()
+ .toISOString()
+ .replace("T", " ")
+ .substring(0, 19),
+ delFlag: "0"
+ };
+
+ // 浣跨敤Vue.set纭繚鍝嶅簲寮�
+ this.form.ethicalreviewopinionsList.push(expertReview);
+ });
+
+ // 瑙﹀彂璁$畻灞炴�ф洿鏂�
+ this.$forceUpdate();
+
+ console.log(
+ "娣诲姞涓撳鍚庯紝褰撳墠涓撳鍒楄〃:",
+ this.form.ethicalreviewopinionsList
+ );
+ console.log("鏅�氫笓瀹舵暟閲�:", this.normalExpertsCount);
+ console.log("涓诲涓撳鏁伴噺:", this.chiefExpertsCount);
+ console.log("鍙彂閫佹櫘閫氫笓瀹�:", this.availableNormalExperts);
+
+ this.$message.success(`鎴愬姛娣诲姞 ${newExperts.length} 浣嶄笓瀹禶);
+ this.expertDialogVisible = false;
+ this.selectedExperts = [];
+ },
+
+ // 娣诲姞涓撳瀵硅瘽妗嗗叧闂�
+ handleExpertDialogClose() {
+ this.selectedExperts = [];
+ this.expertSearchQuery = "";
+ this.filterExpertType = "";
+ this.expertPage.pageNum = 1;
+ },
+
+ // 椤电爜鍙樺寲
+ handlePageChange(pageNum) {
+ this.expertPage.pageNum = pageNum;
+ this.loadExperts();
+ },
+
+ // 姣忛〉鏉℃暟鍙樺寲
+ handlePageSizeChange(pageSize) {
+ this.expertPage.pageSize = pageSize;
+ this.expertPage.pageNum = 1;
+ this.loadExperts();
+ },
+
+ // 鍙戦�佺粰鏅�氫笓瀹�
+ handleSendToNormalExperts() {
+ this.sendForm.expertIds = this.availableNormalExperts.map(expert =>
+ this.getExpertKey(expert)
+ );
this.sendForm.expertType = "normal";
+ this.sendForm.endTime = ""; // 閲嶇疆鎴鏃堕棿
this.sendDialogVisible = true;
},
// 鍙戦�佺粰涓诲涓撳
handleSendToChiefExpert() {
- const chiefExpert = this.expertReviews.find(
- expert => expert.isChief && expert.reviewStatus === "applying"
+ this.sendForm.expertIds = this.availableChiefExperts.map(expert =>
+ this.getExpertKey(expert)
);
- if (chiefExpert) {
- this.sendForm.expertIds = [chiefExpert.id];
- this.sendForm.expertType = "chief";
- this.sendDialogVisible = true;
- }
+ this.sendForm.expertType = "chief";
+ this.sendForm.endTime = ""; // 涓诲涓撳鏃犻渶鎴鏃堕棿
+ this.sendDialogVisible = true;
},
// 鎵归噺鍙戦��
handleBatchSend() {
- const applyingExperts = this.expertReviews.filter(
- expert => expert.reviewStatus === "applying"
+ const allAvailableExperts = [
+ ...this.availableNormalExperts,
+ ...this.availableChiefExperts
+ ];
+ this.sendForm.expertIds = allAvailableExperts.map(expert =>
+ this.getExpertKey(expert)
);
- this.sendForm.expertIds = applyingExperts.map(expert => expert.id);
this.sendForm.expertType = "batch";
+ this.sendForm.endTime = ""; // 閲嶇疆鎴鏃堕棿
this.sendDialogVisible = true;
},
// 鍙戦�佺粰鍗曚釜涓撳
handleSendToExpert(expert) {
- this.sendForm.expertIds = [expert.id];
- this.sendForm.expertType = expert.isChief ? "chief" : "normal";
+ this.sendForm.expertIds = [this.getExpertKey(expert)];
+ this.sendForm.expertType = expert.expertType === "1" ? "chief" : "normal";
+ this.sendForm.endTime = expert.expertType === "1" ? "" : ""; // 涓诲涓撳鏃犻渶鎴鏃堕棿
this.sendDialogVisible = true;
},
// 纭鍙戦��
- handleSendConfirm() {
+ async handleSendConfirm() {
+ if (!this.sendForm.startTime) {
+ this.$message.warning("璇烽�夋嫨鍙戦�佹椂闂�");
+ return;
+ }
+
+ // 鏅�氫笓瀹堕渶瑕佹埅姝㈡椂闂达紝涓诲涓撳涓嶉渶瑕�
+ if (this.sendForm.expertType !== "chief" && !this.sendForm.endTime) {
+ this.$message.warning("璇烽�夋嫨鎴鏃堕棿");
+ return;
+ }
+
+ if (!this.sendForm.sendType) {
+ this.$message.warning("璇烽�夋嫨鍙戦�佹柟寮�");
+ return;
+ }
+
if (this.sendForm.expertIds.length === 0) {
this.$message.warning("璇烽�夋嫨瑕佸彂閫佺殑涓撳");
return;
}
- this.$message.success("鍙戦�佹垚鍔�");
- this.sendDialogVisible = false;
+ this.sending = true;
+ try {
+ // 妯℃嫙鍙戦�佽繃绋�
+ await new Promise(resolve => setTimeout(resolve, 1000));
- this.sendForm.expertIds.forEach(expertId => {
- const index = this.expertReviews.findIndex(
- expert => expert.id === expertId
- );
- if (index !== -1) {
- this.expertReviews[index].reviewStatus = "submitted";
- this.expertReviews[index].sendTime = new Date()
- .toISOString()
- .replace("T", " ")
- .substring(0, 19);
- }
- });
+ // 鏇存柊涓撳鐘舵��
+ this.sendForm.expertIds.forEach(expertKey => {
+ const index = this.form.ethicalreviewopinionsList.findIndex(
+ expert => this.getExpertKey(expert) === expertKey
+ );
+ if (index !== -1) {
+ this.form.ethicalreviewopinionsList[index].receiveStatus = "2"; // 宸叉帴鏀�
+ this.form.ethicalreviewopinionsList[
+ index
+ ].startTime = this.sendForm.startTime;
+ this.form.ethicalreviewopinionsList[
+ index
+ ].endTime = this.sendForm.endTime;
+ this.form.ethicalreviewopinionsList[
+ index
+ ].sendType = this.sendForm.sendType;
+ this.form.ethicalreviewopinionsList[index].updateTime = new Date()
+ .toISOString()
+ .replace("T", " ")
+ .substring(0, 19);
- this.sendForm = {
- expertType: "normal",
- expertIds: [],
- content: ""
- };
+ // 浣跨敤Vue.set纭繚鍝嶅簲寮�
+ this.$set(
+ this.form.ethicalreviewopinionsList,
+ index,
+ this.form.ethicalreviewopinionsList[index]
+ );
+ }
+ });
+
+ this.$message.success("鍙戦�佹垚鍔�");
+ this.sendDialogVisible = false;
+ this.sendForm = {
+ expertType: "normal",
+ expertIds: [],
+ startTime: "",
+ endTime: "",
+ sendType: "0",
+ content: ""
+ };
+
+ // 瑙﹀彂璁$畻灞炴�ф洿鏂�
+ this.$forceUpdate();
+ } catch (error) {
+ console.error("鍙戦�佸け璐�:", error);
+ this.$message.error("鍙戦�佸け璐ワ紝璇烽噸璇�");
+ } finally {
+ this.sending = false;
+ }
},
// 缂栬緫涓撳瀹℃煡
@@ -1336,7 +1839,8 @@
this.$prompt("璇疯緭鍏ュ鏌ユ剰瑙�", "缂栬緫涓撳瀹℃煡", {
confirmButtonText: "纭畾",
cancelButtonText: "鍙栨秷",
- inputValue: expert.expertOpinion || "",
+ inputValue: expert.expertopinion || "",
+ inputPlaceholder: "璇疯緭鍏ュ鏌ユ剰瑙�",
inputValidator: value => {
if (!value || value.trim() === "") {
return "瀹℃煡鎰忚涓嶈兘涓虹┖";
@@ -1345,42 +1849,134 @@
}
})
.then(({ value }) => {
- const index = this.expertReviews.findIndex(e => e.id === expert.id);
+ const index = this.form.ethicalreviewopinionsList.findIndex(
+ e => e.id === expert.id || e.expertNo === expert.expertNo
+ );
if (index !== -1) {
- this.expertReviews[index].expertOpinion = value;
+ this.form.ethicalreviewopinionsList[index].expertopinion = value;
+ this.form.ethicalreviewopinionsList[index].updateTime = new Date()
+ .toISOString()
+ .replace("T", " ")
+ .substring(0, 19);
+
+ // 浣跨敤Vue.set纭繚鍝嶅簲寮�
+ this.$set(
+ this.form.ethicalreviewopinionsList,
+ index,
+ this.form.ethicalreviewopinionsList[index]
+ );
+
this.$message.success("瀹℃煡鎰忚宸叉洿鏂�");
}
})
.catch(() => {});
},
+ // 鏌ョ湅涓撳鍘嗗彶瀹℃壒鎯呭喌
+ async handleViewExpertHistory(expert) {
+ console.log(12);
+
+ if (!expert.expertNo) {
+ this.$message.warning("璇ヤ笓瀹舵病鏈夌紪鍙凤紝鏃犳硶鏌ヨ鍘嗗彶瀹℃壒鎯呭喌");
+ return;
+ }
+
+ this.currentExpertInfo = expert;
+ this.expertHistoryLoading = true;
+ this.expertHistoryDialogVisible = true;
+
+ try {
+ const params = {
+ expertNo: expert.expertNo
+ };
+ console.log(11);
+
+ const response = await ethicalreExpertTotal(params);
+ console.log(response);
+
+ if (response) {
+ this.expertHistoryData = response[0];
+ } else {
+ this.$message.error(
+ "鏌ヨ涓撳鍘嗗彶瀹℃壒鎯呭喌澶辫触锛�" + (response.msg || "鏈煡閿欒")
+ );
+ this.expertHistoryData = null;
+ }
+ } catch (error) {
+ console.error("鏌ヨ涓撳鍘嗗彶瀹℃壒鎯呭喌澶辫触:", error);
+ this.$message.error("鏌ヨ涓撳鍘嗗彶瀹℃壒鎯呭喌澶辫触");
+ this.expertHistoryData = null;
+ } finally {
+ this.expertHistoryLoading = false;
+ }
+ },
+
// 鏌ョ湅涓撳瀹℃煡璇︽儏
handleViewExpertReview(expert) {
this.$alert(
`
- <div>
- <p><strong>涓撳濮撳悕锛�</strong>${expert.expertName}</p>
- <p><strong>涓撳绫诲瀷锛�</strong>${
- expert.isChief ? "涓诲涓撳" : "涓撳"
- }</p>
- <p><strong>瀹℃煡鐘舵�侊細</strong>${this.statusTextFilter(
- expert.reviewStatus
+ <div style="line-height: 1.6;">
+ <p><strong>涓撳濮撳悕锛�</strong>${expert.expertname}</p>
+ <p><strong>涓撳缂栧彿锛�</strong>${expert.expertNo || "-"}</p>
+ <p><strong>涓撳绫诲瀷锛�</strong>${this.getExpertTypeText(
+ expert.expertType
+ )}</p>
+ <p><strong>绉戝鍚嶇О锛�</strong>${expert.deptName || "-"}</p>
+ <p><strong>鑱岀О锛�</strong>${expert.title || "-"}</p>
+ <p><strong>鑱旂郴鐢佃瘽锛�</strong>${expert.telephone || "-"}</p>
+ <p><strong>瀹℃煡鐘舵�侊細</strong>${this.getReviewStatusText(
+ expert.receiveStatus
)}</p>
<p><strong>涓撳缁撹锛�</strong>${
- expert.expertConclusion
- ? this.conclusionTextFilter(expert.expertConclusion)
+ expert.expertconclusion
+ ? this.getConclusionText(expert.expertconclusion)
: "鏈彁浜�"
}</p>
- <p><strong>瀹℃煡鎰忚锛�</strong>${expert.expertOpinion || "鏃�"}</p>
- <p><strong>瀹℃煡鏃堕棿锛�</strong>${expert.reviewTime || "鏈鏌�"}</p>
+ <p><strong>瀹℃煡鎰忚锛�</strong>${expert.expertopinion || "鏃�"}</p>
+ <p><strong>缁撹椤哄簭锛�</strong>${expert.conclusionorder || "-"}</p>
+ <p><strong>瀹℃煡鏃堕棿锛�</strong>${expert.conclusiontime || "鏈鏌�"}</p>
+ <p><strong>鍙戦�佹椂闂达細</strong>${expert.startTime || "鏈彂閫�"}</p>
+ <p><strong>鎴鏃堕棿锛�</strong>${expert.endTime || "-"}</p>
+ <p><strong>鍙戦�佹柟寮忥細</strong>${
+ expert.sendType
+ ? expert.sendType === "0"
+ ? "绯荤粺鍙戦��"
+ : expert.sendType === "1"
+ ? "閭欢鍙戦��"
+ : expert.sendType === "2"
+ ? "鐭俊鍙戦��"
+ : "鍏朵粬鏂瑰紡"
+ : "-"
+ }</p>
</div>
`,
"涓撳瀹℃煡璇︽儏",
{
dangerouslyUseHTMLString: true,
- customClass: "expert-review-detail-dialog"
+ customClass: "expert-review-detail-dialog",
+ showConfirmButton: false,
+ showCancelButton: true,
+ cancelButtonText: "鍏抽棴"
}
);
+ },
+
+ // 鍒犻櫎涓撳瀹℃煡
+ handleDeleteExpertReview(index) {
+ this.$confirm("纭畾瑕佸垹闄よ涓撳鐨勫鏌ヨ褰曞悧锛�", "鎻愮ず", {
+ confirmButtonText: "纭畾",
+ cancelButtonText: "鍙栨秷",
+ type: "warning"
+ })
+ .then(() => {
+ this.form.ethicalreviewopinionsList.splice(index, 1);
+
+ // 瑙﹀彂璁$畻灞炴�ф洿鏂�
+ this.$forceUpdate();
+
+ this.$message.success("涓撳瀹℃煡璁板綍宸插垹闄�");
+ })
+ .catch(() => {});
},
// 鏃堕棿鏍煎紡鍖�
@@ -1403,8 +1999,6 @@
}
};
</script>
-
-
<style scoped>
.ethics-review-detail {
@@ -1516,6 +2110,40 @@
.sent-button {
color: #67c23a !important;
}
+
+/* 涓撳濮撳悕閾炬帴鏍峰紡 */
+.expert-name-link {
+ color: #409eff;
+ cursor: pointer;
+ text-decoration: none;
+ transition: color 0.3s;
+}
+
+.expert-name-link:hover {
+ color: #66b1ff;
+ text-decoration: underline;
+}
+
+/* 鍘嗗彶缁熻鏍峰紡 */
+.history-stat-item {
+ padding: 10px;
+ border-radius: 4px;
+ background-color: #f5f7fa;
+ text-align: center;
+}
+
+.history-stat-label {
+ font-size: 12px;
+ color: #909399;
+ margin-bottom: 5px;
+}
+
+.history-stat-value {
+ font-size: 18px;
+ font-weight: bold;
+ color: #303133;
+}
+
.form-section {
margin-bottom: 16px;
}
@@ -1576,6 +2204,7 @@
.case-info-card {
border-left: 4px solid #67c23a;
}
+
/* 鍝嶅簲寮忚璁� */
@media (max-width: 768px) {
.ethics-review-detail {
diff --git a/src/views/business/ethicalReview/index.vue b/src/views/business/ethicalReview/index.vue
index 887e77f..6b6a66e 100644
--- a/src/views/business/ethicalReview/index.vue
+++ b/src/views/business/ethicalReview/index.vue
@@ -127,7 +127,7 @@
prop="name"
width="100"
/>
-
+
<el-table-column label="鎬у埆" align="center" prop="sex" width="80">
<template slot-scope="scope">
<dict-tag
@@ -138,7 +138,7 @@
</el-table-column>
<el-table-column label="骞撮緞" align="center" prop="age" width="80" />
<!--
-
+
<el-table-column
label="妗堜緥缂栧彿"
align="center"
@@ -163,7 +163,7 @@
prop="idcardno"
width="180"
/>
-
+
<el-table-column
label="鍖荤枟鏈烘瀯"
align="center"
@@ -171,7 +171,7 @@
min-width="150"
show-overflow-tooltip
/>
-
+
<el-table-column
label="涓撳濮撳悕"
align="center"
@@ -286,7 +286,7 @@
export default {
name: "EthicsReviewList",
components: { Pagination },
- dicts: ["sys_user_sex", "sys_ethical",'expert_Conclusion'],
+ dicts: ["sys_user_sex", "ReviewForm_status",'sys_ethical','expert_Conclusion'],
data() {
return {
// 閬僵灞�
--
Gitblit v1.9.3