From dc082351978a1e9f75d7a1471a0ca7ebeac552a5 Mon Sep 17 00:00:00 2001
From: WXL <wl_5969728@163.com>
Date: 星期一, 01 六月 2026 11:07:50 +0800
Subject: [PATCH] opo维护
---
src/views/business/course/components/EthicalReviewStage.vue | 2299 ++++++++++++++++++++++++++++++++++++++++++++++++++++++----
1 files changed, 2,142 insertions(+), 157 deletions(-)
diff --git a/src/views/business/course/components/EthicalReviewStage.vue b/src/views/business/course/components/EthicalReviewStage.vue
index 47e4877..3192819 100644
--- a/src/views/business/course/components/EthicalReviewStage.vue
+++ b/src/views/business/course/components/EthicalReviewStage.vue
@@ -1,206 +1,2191 @@
<template>
- <base-stage :stage-data="stageData" :case-info="caseInfo">
- <template #header>
- <el-alert
- title="浼︾悊瀹℃煡闃舵"
- :type="getAlertType()"
- :description="getAlertDescription()"
- show-icon
- :closable="false"
- />
- </template>
+ <div class="ethics-review-detail">
- <el-row :gutter="20" style="margin-top: 20px;">
- <el-col :span="12">
- <el-card>
- <div slot="header" class="card-header">
- <span>瀹℃煡濮斿憳浼氫俊鎭�</span>
- </div>
- <el-descriptions :column="1" border>
- <el-descriptions-item label="濮斿憳浼氬悕绉�">
- {{ reviewCommittee.name }}
- </el-descriptions-item>
- <el-descriptions-item label="瀹℃煡浼氳鏃堕棿">
- {{ formatTime(reviewCommittee.meetingTime) }}
- </el-descriptions-item>
- <el-descriptions-item label="鍙備細濮斿憳">
- {{ reviewCommittee.members.length }} 浜�
- </el-descriptions-item>
- <el-descriptions-item label="瀹℃煡缁撹">
- <el-tag :type="reviewCommittee.conclusion ? 'success' : 'warning'">
- {{ reviewCommittee.conclusion ? '瀹℃煡閫氳繃' : '瀹℃煡涓�' }}
- </el-tag>
- </el-descriptions-item>
- <el-descriptions-item label="涓诲腑绛惧瓧">
- {{ reviewCommittee.chairman }}
- </el-descriptions-item>
- </el-descriptions>
- </el-card>
- </el-col>
+ <el-card class="detail-card">
+ <!-- 浼︾悊瀹℃煡鍩烘湰淇℃伅 -->
+ <div slot="header" class="clearfix">
+ <span class="detail-title">浼︾悊瀹℃煡鍩烘湰淇℃伅</span>
+ <div style="float: right;">
+ <el-button type="primary" @click="handleSave" :loading="saveLoading">
+ 淇濆瓨
+ </el-button>
- <el-col :span="12">
- <el-card>
- <div slot="header" class="card-header">
- <span>瀹℃煡娴佺▼杩涘害</span>
- </div>
- <el-steps direction="vertical" :active="reviewProgress.active" space="80px">
- <el-step
- v-for="step in reviewProgress.steps"
- :key="step.title"
- :title="step.title"
- :description="step.description"
- :status="step.status"
- />
- </el-steps>
- </el-card>
- </el-col>
- </el-row>
+ <el-button
+ type="success"
+ @click="handleCompleteReview"
+ :disabled="form.status == '3' || form.status == '2'"
+ :loading="completeLoading"
+ >
+ 瀹℃煡瀹屾垚
+ </el-button>
- <el-card style="margin-top: 20px;">
- <div slot="header" class="card-header">
- <span>濮斿憳瀹℃煡鎰忚</span>
+ <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'"
+ :loading="endLoading"
+ >
+ 缁撴潫瀹℃煡
+ </el-button>
+ </div>
</div>
- <el-table :data="reviewComments" border>
- <el-table-column label="濮斿憳濮撳悕" prop="memberName" width="120" />
- <el-table-column label="涓撲笟棰嗗煙" prop="specialty" width="120" />
- <el-table-column label="瀹℃煡鎰忚" prop="comment" min-width="200" />
- <el-table-column label="鎶曠エ缁撴灉" width="100">
+
+ <el-form :model="form" ref="form" :rules="rules" label-width="120px">
+ <el-row :gutter="20">
+ <el-col :span="8">
+ <el-form-item label="鍙戣捣涓婚" prop="initiateTheme">
+ <el-input
+ v-model="form.initiateTheme"
+ placeholder="璇疯緭鍏ュ彂璧蜂富棰�"
+ />
+ </el-form-item>
+ </el-col>
+ <el-col :span="8">
+ <el-form-item label="鍙戣捣浜�" prop="initiatePerson">
+ <el-input v-model="form.initiatePerson" />
+ </el-form-item>
+ </el-col>
+ <el-col :span="8">
+ <el-form-item label="瀹℃煡鐘舵��" prop="status">
+ <el-select v-model="form.status" style="width: 100%">
+ <el-option
+ v-for="dict in dict.type.sys_ethical"
+ :key="dict.value"
+ :label="dict.label"
+ :value="dict.value"
+ />
+ </el-select>
+ </el-form-item>
+ </el-col>
+ </el-row>
+
+ <!-- 涓撳鐩稿叧淇℃伅 -->
+
+ <el-row :gutter="20">
+ <el-col :span="8">
+ <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" />
+ <el-option label="涓嶅悓鎰�" value="0" />
+ </el-select>
+ </el-form-item>
+ </el-col>
+ <el-col :span="8">
+ <el-form-item label="浼︾悊瀹℃煡缁撹鏃堕棿" prop="expertTime">
+ <el-date-picker
+ v-model="form.expertTime"
+ type="datetime"
+ value-format="yyyy-MM-dd HH:mm:ss"
+ 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-input
+ type="textarea"
+ :rows="2"
+ v-model="form.expertOpinion"
+ placeholder="璇疯緭鍏ユ剰瑙�"
+ />
+ </el-form-item>
+ </el-col>
+ </el-row>
+
+ <el-row :gutter="20">
+ <el-col :span="24">
+ <el-form-item label="澶囨敞" prop="remark">
+ <el-input
+ type="textarea"
+ :rows="3"
+ v-model="form.remark"
+ placeholder="璇疯緭鍏ュ娉ㄤ俊鎭�"
+ />
+ </el-form-item>
+ </el-col>
+ </el-row>
+ </el-form>
+ </el-card>
+
+ <!-- 闄勪欢涓婁紶 -->
+ <el-card class="attachment-card">
+ <div slot="header" class="clearfix">
+ <span class="detail-title">鐩稿叧闄勪欢</span>
+ </div>
+
+ <!-- 浣跨敤 UploadAttachment 缁勪欢 -->
+ <UploadAttachment
+ ref="uploadAttachment"
+ :file-list="attachmentFileList"
+ :limit="10"
+ accept=".pdf,.jpg,.jpeg,.png,.doc,.docx,.xls,.xlsx"
+ @change="handleAttachmentChange"
+ @upload-success="handleUploadSuccess"
+ @upload-error="handleUploadError"
+ @remove="handleAttachmentRemove"
+ />
+
+ <!-- 闄勪欢鍒楄〃 -->
+ <div
+ class="attachment-list"
+ v-if="form.annexfilesList && form.annexfilesList.length > 0"
+ >
+ <div class="list-title">
+ 宸蹭笂浼犻檮浠� ({{ form.annexfilesList.length }})
+ </div>
+ <el-table :data="form.annexfilesList" style="width: 100%" size="small">
+ <el-table-column label="鏂囦欢鍚�" min-width="200">
+ <template slot-scope="scope">
+ <i
+ class="el-icon-document"
+ style="margin-right: 8px; color: #409EFF;"
+ ></i>
+ <span class="file-name">{{ scope.row.fileName }}</span>
+ </template>
+ </el-table-column>
+ <el-table-column label="鏂囦欢绫诲瀷" width="100">
+ <template slot-scope="scope">
+ <el-tag size="small">{{
+ getFileType(scope.row.fileName)
+ }}</el-tag>
+ </template>
+ </el-table-column>
+ <el-table-column label="鍒涘缓鏃堕棿" width="160">
+ <template slot-scope="scope">
+ <span>{{ formatDateTime(scope.row.createTime) }}</span>
+ </template>
+ </el-table-column>
+ <el-table-column label="鎿嶄綔" width="266">
+ <template slot-scope="scope">
+ <el-button
+ size="mini"
+ type="primary"
+ @click="handlePreview(scope.row)"
+ >
+ 棰勮
+ </el-button>
+ <el-button
+ size="mini"
+ type="success"
+ @click="handleDownload(scope.row)"
+ >
+ 涓嬭浇
+ </el-button>
+ <el-button
+ size="mini"
+ type="danger"
+ @click="handleRemoveAttachment(scope.$index)"
+ >
+ 鍒犻櫎
+ </el-button>
+ </template>
+ </el-table-column>
+ </el-table>
+ </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>
+ <p style="color: #909399; font-size: 14px;">鏆傛棤闄勪欢锛岃涓婁紶鐩稿叧鏂囦欢</p>
+ </div>
+ </el-card>
+
+ <!-- 涓撳瀹℃煡鎯呭喌 -->
+ <el-card class="expert-card">
+ <div slot="header" class="clearfix">
+ <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"
+ @click="handleSendToNormalExperts"
+ :disabled="!canSendToNormalExperts"
+ >
+ 鍙戦�佷笓瀹�
+ </el-button>
+ <el-button
+ size="mini"
+ type="success"
+ @click="handleSendToChiefExpert"
+ :disabled="!canSendToChiefExpert"
+ >
+ 鍙戦�佷富濮斾笓瀹�
+ </el-button>
+ </div>
+ </div>
+
+ <!-- 涓撳缁熻淇℃伅 -->
+ <div
+ class="expert-stats"
+ style="margin-top: 20px; padding: 15px; background: #f5f7fa; border-radius: 4px;"
+ >
+ <el-row :gutter="20">
+ <el-col :span="6">
+ <div class="stat-item">
+ <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">{{ chiefExpertsCount }}浜�</span>
+ </div>
+ </el-col>
+ <el-col :span="6">
+ <div class="stat-item">
+ <span class="stat-label">宸插悓鎰�:</span>
+ <span class="stat-value"
+ >{{ approvedExpertsCount }}/{{ totalExpertsCount }}</span
+ >
+ </div>
+ </el-col>
+ <el-col :span="6">
+ <div class="stat-item">
+ <span class="stat-label">瀹℃煡缁撴灉:</span>
+ <span class="stat-value">
+ <el-tag :type="overallConclusionFilter">
+ {{ overallConclusionText }}
+ </el-tag>
+ </span>
+ </div>
+ </el-col>
+ </el-row>
+ </div>
+
+ <!-- 涓撳瀹℃煡琛ㄦ牸 -->
+ <el-table
+ :data="ethicalreviewopinionsList"
+ v-loading="expertLoading"
+ style="width: 100%"
+ height="600"
+ :row-class-name="getExpertRowClassName"
+ >
+ <el-table-column label="搴忓彿" width="60" align="center" type="index" />
+
+ <el-table-column
+ label="涓撳濮撳悕"
+ width="120"
+ align="center"
+ fixed="left"
+ >
<template slot-scope="scope">
- <el-tag :type="scope.row.vote === '鍚屾剰' ? 'success' : 'danger'">
- {{ scope.row.vote }}
+ <span
+ class="expert-name-link"
+ @click="handleViewExpertHistory(scope.row)"
+ >
+ {{ scope.row.expertname }}
+ </span>
+ <el-tag
+ v-if="scope.row.expertType == '1'"
+ size="mini"
+ type="danger"
+ style="margin-left: 5px;"
+ >涓诲</el-tag
+ >
+ </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.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="getReviewStatusFilter(scope.row.receiveStatus)"
+ size="small"
+ >
+ {{ getReviewStatusText(scope.row.receiveStatus) }}
</el-tag>
</template>
</el-table-column>
- <el-table-column label="瀹℃煡鏃堕棿" width="160">
+
+ <el-table-column label="涓撳缁撹" width="120" align="center">
<template slot-scope="scope">
- {{ formatTime(scope.row.reviewTime) }}
+ <el-tag
+ v-if="scope.row.expertconclusion"
+ :type="getConclusionFilter(scope.row.expertconclusion)"
+ size="small"
+ >
+ {{ getConclusionText(scope.row.expertconclusion) }}
+ </el-tag>
+ <span v-else class="no-data">-</span>
+ </template>
+ </el-table-column>
+
+ <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>
+ </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.endTime ? formatDateTime(scope.row.endTime) : "鏈缃�"
+ }}</span>
+ </template>
+ </el-table-column>
+
+ <el-table-column label="瀹℃煡鏃堕棿" width="160" align="center">
+ <template slot-scope="scope">
+ <span>{{
+ 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.startTime
+ ? formatDateTime(scope.row.startTime)
+ : "鏈彂閫�"
+ }}</span>
+ </template>
+ </el-table-column>
+
+ <el-table-column label="鎿嶄綔" width="180" align="center" fixed="right">
+ <template slot-scope="scope">
+ <el-button
+ size="mini"
+ type="text"
+ icon="el-icon-s-promotion"
+ @click="handleSendToExpert(scope.row)"
+ :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.receiveStatus == "2" || scope.row.receiveStatus == "3"
+ ? "宸插彂閫�"
+ : "鍙戦��"
+ }}
+ </el-button>
+ <el-button
+ v-if="scope.row.receiveStatus == 0"
+ size="mini"
+ type="text"
+ icon="el-icon-delete"
+ @click="handleDeleteExpertReview(scope.row, scope.$index)"
+ style="color: #f56c6c;"
+ >
+ 鍒犻櫎
+ </el-button>
</template>
</el-table-column>
</el-table>
</el-card>
- <el-card style="margin-top: 20px;">
- <div slot="header" class="card-header">
- <span>瀹℃煡鍐宠鏂囦欢</span>
- <el-button type="primary" size="small" @click="handleViewResolution">
- 鏌ョ湅鍐宠鏂囦欢
- </el-button>
+ <!-- 娣诲姞涓撳瀵硅瘽妗� -->
+ <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>
- <div class="resolution-content">
- <p><strong>浼︾悊瀹℃煡鍐宠锛�</strong></p>
- <p>{{ resolutionContent }}</p>
- <el-divider />
- <div class="signature-area">
- <p>浼︾悊濮斿憳浼氫富甯細{{ reviewCommittee.chairman }}</p>
- <p>鏃ユ湡锛歿{ formatTime(reviewCommittee.meetingTime) }}</p>
+
+ <el-table
+ :data="filteredExpertList"
+ 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="filteredExpertTotal"
+ ></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="sendDialogTitle"
+ :visible.sync="sendDialogVisible"
+ width="500px"
+ @close="handleSendDialogClose"
+ >
+ <el-form :model="sendForm" ref="sendForm" label-width="100px">
+ <el-form-item label="涓撳绫诲瀷" prop="expertType">
+ <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="margin-top: 5px;">
+ <el-button-group>
+ <el-button size="mini" @click="setEndTime(0.5)"
+ >鍗婂皬鏃跺悗</el-button
+ >
+ <el-button size="mini" @click="setEndTime(1)">涓�灏忔椂鍚�</el-button>
+ <el-button size="mini" @click="setEndTime(2)">涓ゅ皬鏃跺悗</el-button>
+ <el-button size="mini" @click="setEndTime(24)">涓�澶╁悗</el-button>
+ </el-button-group>
+ </div>
+ <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="title" required>
+ <el-input v-model="sendForm.title" placeholder="璇疯緭鍏ュ彂閫佹爣棰�" />
+ </el-form-item>
+
+ <el-form-item label="鍙戦�佸唴瀹�" prop="content" required>
+ <el-input
+ type="textarea"
+ :rows="4"
+ v-model="sendForm.content"
+ placeholder="璇疯緭鍏ュ彂閫佺粰涓撳鐨勫鏌ュ唴瀹硅鏄�"
+ />
+ </el-form-item>
+
+ <el-form-item label="璺宠浆閾炬帴" prop="url">
+ <el-input
+ v-model="sendForm.url"
+ placeholder="璇疯緭鍏ヨ烦杞摼鎺ワ紙鍙�夛級"
+ />
+ </el-form-item>
+ </el-form>
+ <div slot="footer">
+ <el-button @click="sendDialogVisible = false">鍙栨秷</el-button>
+ <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>
- </el-card>
- </base-stage>
+ <div slot="footer">
+ <el-button @click="expertHistoryDialogVisible = false">鍏抽棴</el-button>
+ </div>
+ </el-dialog>
+
+ <!-- 鏂囦欢棰勮寮圭獥 -->
+ <FilePreviewDialog
+ :visible="previewVisible"
+ :file="currentPreviewFile"
+ @close="previewVisible = false"
+ @download="handleDownload"
+ />
+ </div>
</template>
<script>
-import BaseStage from './BaseStage.vue';
+import { getToken } from "@/utils/auth";
+import {
+ ethicalreviewgetInfoID,
+ ethicalreviewedit,
+ ethicalreviewadd,
+ ethicalreviewInfo,
+ ethicalreExpertTotal,
+ sendNotification
+} from "@/api/businessApi";
+import { listExternalperson } from "@/api/project/externalperson";
+import CaseBasicInfo from "@/components/CaseBasicInfo";
+import UploadAttachment from "@/components/UploadAttachment";
+import FilePreviewDialog from "@/components/FilePreviewDialog";
+import dayjs from "dayjs";
export default {
- name: 'EthicalReviewStage',
- components: { BaseStage },
- props: {
- stageData: {
- type: Object,
- default: () => ({})
- },
- caseInfo: {
- type: Object,
- default: () => ({})
+ name: "EthicsReviewDetail",
+ components: { CaseBasicInfo, UploadAttachment, FilePreviewDialog },
+ dicts: ["sys_user_sex", "sys_ethical", "Review_status"],
+ props: {
+ infoid: {
+ type: String,
+ default: true
}
},
data() {
return {
- reviewCommittee: {
- name: '鍖婚櫌浼︾悊瀹℃煡濮斿憳浼�',
- meetingTime: '2023-12-03 15:20:00',
- members: ['寮犳暀鎺�', '鏉庝富浠�', '鐜嬪尰鐢�', '璧靛鍛�', '閽变笓瀹�'],
- conclusion: true,
- chairman: '寮犳暀鎺�'
+ // 椤甸潰妯″紡
+ isEdit: false,
+ // 鍩烘湰淇℃伅
+ id: undefined,
+ caseId: null,
+ caseNo: "",
+
+ // 琛ㄥ崟鏁版嵁
+ form: {
+ // 鍩虹淇℃伅
+ id: undefined,
+ infoid: undefined,
+ caseNo: "",
+ initiateTheme: "",
+ initiatePerson: "",
+
+ // 鐘舵�佸拰鏃堕棿
+ status: "0", // 0:寰呭鏌�, 1:瀹℃煡涓�, 2:瀹℃煡涓, 3:瀹℃煡瀹屾垚
+ startTime: "",
+ cutOffTime: "",
+ endTime: "",
+
+ // 涓撳淇℃伅
+ expertName: "",
+ expertNo: "",
+ expertType: "0",
+ expertConclusion: "",
+ expertOpinion: "",
+ expertTime: "",
+ orderNo: 1,
+
+ // 澶囨敞
+ remark: "",
+
+ // 闄勪欢淇℃伅
+ annexfilesList: [],
+ filePatch: "",
+
+ // 涓撳瀹℃煡鎰忚鍒楄〃
+ ethicalreviewopinionsList: [],
+
+ // 绯荤粺瀛楁
+ createBy: "",
+ createTime: "",
+ updateBy: "",
+ updateTime: "",
+ delFlag: "0"
},
- reviewProgress: {
- active: 4,
- steps: [
+
+ // 琛ㄥ崟楠岃瘉瑙勫垯
+ rules: {
+ initiateTheme: [
+ { required: true, message: "鍙戣捣涓婚涓嶈兘涓虹┖", trigger: "blur" },
{
- title: '鏉愭枡鍒濆',
- description: '鐢宠鏉愭枡瀹屾暣鎬у鏌�',
- status: 'finish'
- },
- {
- title: '濮斿憳璇勫',
- description: '鍚勫鍛樼嫭绔嬪鏌�',
- status: 'finish'
- },
- {
- title: '浼氳璁ㄨ',
- description: '濮斿憳浼氶泦浣撹璁�',
- status: 'finish'
- },
- {
- title: '褰㈡垚鍐宠',
- description: '鎶曠エ褰㈡垚鏈�缁堝喅璁�',
- status: 'finish'
+ min: 2,
+ max: 100,
+ message: "闀垮害鍦� 2 鍒� 100 涓瓧绗�",
+ trigger: "blur"
}
+ ],
+ initiatePerson: [
+ { required: true, message: "鍙戣捣浜轰笉鑳戒负绌�", trigger: "blur" }
+ ],
+ status: [
+ { required: true, message: "瀹℃煡鐘舵�佷笉鑳戒负绌�", trigger: "change" }
+ ],
+ expertName: [
+ { max: 50, message: "闀垮害涓嶈兘瓒呰繃 50 涓瓧绗�", trigger: "blur" }
+ ],
+ expertNo: [
+ { max: 50, message: "闀垮害涓嶈兘瓒呰繃 50 涓瓧绗�", trigger: "blur" }
+ ],
+ expertConclusion: [
+ { max: 2, message: "闀垮害涓嶈兘瓒呰繃 2 涓瓧绗�", trigger: "change" }
+ ],
+ remark: [
+ { max: 500, message: "闀垮害涓嶈兘瓒呰繃 500 涓瓧绗�", trigger: "blur" }
]
},
- reviewComments: [
- {
- memberName: '寮犳暀鎺�',
- specialty: '鍖诲浼︾悊',
- comment: '鎹愮尞绋嬪簭绗﹀悎浼︾悊瑙勮寖锛屽悓鎰忛�氳繃',
- vote: '鍚屾剰',
- reviewTime: '2023-12-03 14:30:00'
- },
- {
- memberName: '鏉庝富浠�',
- specialty: '涓村簥鍖诲',
- comment: '鍖荤枟绋嬪簭瑙勮寖锛屾棤浼︾悊闂',
- vote: '鍚屾剰',
- reviewTime: '2023-12-03 14:45:00'
- },
- {
- memberName: '鐜嬪尰鐢�',
- specialty: '娉曞緥鍖诲',
- comment: '娉曞緥鏂囦欢榻愬叏锛岀▼搴忓悎娉�',
- vote: '鍚屾剰',
- reviewTime: '2023-12-03 15:00:00'
- }
- ],
- resolutionContent: '缁忎鸡鐞嗗鏌ュ鍛樹細瀹℃煡锛岃鍣ㄥ畼鎹愮尞妗堜緥绗﹀悎鍖诲浼︾悊瑕佹眰锛屾崘鐚▼搴忚鑼冿紝瀹跺睘鎰忔効鐪熷疄鏈夋晥锛屽悓鎰忚繘琛屽櫒瀹樻崘鐚��'
+
+ // 淇濆瓨鍔犺浇鐘舵��
+ saveLoading: false,
+ completeLoading: false,
+ suspendLoading: false,
+ endLoading: false,
+ sending: false,
+
+ // 闄勪欢鐩稿叧
+ attachmentFileList: [],
+
+ // 棰勮鐩稿叧
+ previewVisible: false,
+ currentPreviewFile: null,
+
+ // 涓撳瀹℃煡鏁版嵁
+ 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",
+ title: "浼︾悊瀹℃煡浠诲姟閫氱煡",
+ content: "",
+ url: ""
+ },
+
+ // 涓撳鍘嗗彶瀹℃壒鎯呭喌
+ expertHistoryDialogVisible: false,
+ expertHistoryLoading: false,
+ expertHistoryData: null,
+ currentExpertInfo: {},
+
+ // 褰撳墠鍙戦�佺殑涓撳
+ currentSendExperts: []
};
},
+ computed: {
+ // 璁$畻灞炴�э細鑾峰彇涓撳鍒楄〃锛堢‘淇濆搷搴斿紡锛�
+ ethicalreviewopinionsList() {
+ return this.form.ethicalreviewopinionsList || [];
+ },
+
+ // 璁$畻灞炴�э細鏅�氫笓瀹舵暟閲�
+ normalExpertsCount() {
+ return this.ethicalreviewopinionsList.filter(
+ expert => expert.expertType === "0"
+ ).length;
+ },
+
+ // 璁$畻灞炴�э細涓诲涓撳鏁伴噺
+ chiefExpertsCount() {
+ return this.ethicalreviewopinionsList.filter(
+ expert => expert.expertType === "1"
+ ).length;
+ },
+
+ // 璁$畻灞炴�э細鎬讳笓瀹舵暟閲�
+ totalExpertsCount() {
+ return this.ethicalreviewopinionsList.length;
+ },
+
+ // 璁$畻灞炴�э細宸插悓鎰忎笓瀹舵暟閲�
+ approvedExpertsCount() {
+ return this.ethicalreviewopinionsList.filter(
+ expert => expert.expertconclusion === "1"
+ ).length;
+ },
+
+ // 璁$畻灞炴�э細鎬讳綋缁撹
+ overallConclusionText() {
+ const total = this.totalExpertsCount;
+ const approved = this.approvedExpertsCount;
+
+ if (total === 0) return "鏈鏌�";
+ if (approved >= Math.ceil(total * 0.7)) {
+ // 瓒呰繃70%鍚屾剰
+ return "閫氳繃";
+ } else if (approved >= Math.ceil(total * 0.5)) {
+ // 瓒呰繃50%鍚屾剰
+ return "淇敼鍚庨�氳繃";
+ } else {
+ return "涓嶉�氳繃";
+ }
+ },
+
+ overallConclusionFilter() {
+ const total = this.totalExpertsCount;
+ const approved = this.approvedExpertsCount;
+
+ if (total === 0) return "info";
+ if (approved >= Math.ceil(total * 0.7)) {
+ return "success";
+ } 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.availableNormalExperts.length > 0;
+ },
+
+ // 鏄惁鍙互鍙戦�佺粰涓诲涓撳锛堥渶瑕佽嚦灏�12涓櫘閫氫笓瀹跺悓鎰忥級
+ canSendToChiefExpert() {
+ const normalApprovedCount = this.ethicalreviewopinionsList.filter(
+ expert => expert.expertType === "0" && expert.expertconclusion === "1"
+ ).length;
+ return this.availableChiefExperts.length > 0 && normalApprovedCount >= 12;
+ },
+
+ // 宸插瓨鍦ㄧ殑涓撳缂栧彿鍒楄〃
+ existingExpertNos() {
+ return this.ethicalreviewopinionsList
+ .map(expert => expert.expertNo)
+ .filter(no => no);
+ },
+
+ // 宸插瓨鍦ㄧ殑涓撳濮撳悕鍒楄〃
+ existingExpertNames() {
+ return this.ethicalreviewopinionsList
+ .map(expert => expert.expertname)
+ .filter(name => name);
+ },
+
+ // 杩囨护鍚庣殑涓撳鍒楄〃锛堟帓闄ゅ凡瀛樺湪鐨勪笓瀹讹級
+ filteredExpertList() {
+ if (!this.expertList.length) return [];
+
+ return this.expertList.filter(expert => {
+ // 濡傛灉涓撳鏈夌紪鍙凤紝妫�鏌ョ紪鍙锋槸鍚﹀凡瀛樺湪
+ if (expert.userno && this.existingExpertNos.includes(expert.userno)) {
+ return false;
+ }
+ // 濡傛灉涓撳鏈夊鍚嶏紝妫�鏌ュ鍚嶆槸鍚﹀凡瀛樺湪
+ if (
+ expert.username &&
+ this.existingExpertNames.includes(expert.username)
+ ) {
+ return false;
+ }
+ return true;
+ });
+ },
+
+ // 杩囨护鍚庣殑涓撳鎬绘暟
+ filteredExpertTotal() {
+ return this.filteredExpertList.length;
+ },
+
+ // 褰撳墠鐢ㄦ埛淇℃伅
+ 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);
+ },
+ deep: true
+ }
+ },
+ created() {
+
+ this.caseId = this.infoid;
+ this.getDetail(this.infoid, this.id);
+ },
methods: {
- getAlertType() {
- const status = this.stageData.status;
- return status === 'completed' ? 'success' :
- status === 'in_progress' ? 'warning' : 'info';
+ // 鍒濆鍖栨柊澧炴暟鎹�
+ initNewData() {
+ this.form.infoid = this.infoid;
+ this.form.caseNo = this.$route.query.caseNo || "";
+ this.form.initiatePerson = this.currentUser.username || "褰撳墠鐢ㄦ埛";
+ this.form.startTime = new Date()
+ .toISOString()
+ .replace("T", " ")
+ .substring(0, 19);
+ this.form.createBy = this.currentUser.username || "admin";
},
- getAlertDescription() {
- const status = this.stageData.status;
- return status === 'completed' ? '浼︾悊瀹℃煡宸查�氳繃锛屽彲浠ヨ繘琛屽櫒瀹樺垎閰�' :
- status === 'in_progress' ? '浼︾悊瀹℃煡娴佺▼姝e湪杩涜涓�' : '绛夊緟寮�濮嬩鸡鐞嗗鏌ユ祦绋�';
+
+ // 鑾峰彇璇︽儏
+ async getDetail(infoid, id) {
+ try {
+ this.expertLoading = true;
+ let response = {};
+ if (id) {
+ response = await ethicalreviewInfo(id);
+ } else if (infoid) {
+ response = await ethicalreviewgetInfoID({ InfoId: infoid });
+ }
+
+ if (response.code === 200) {
+ let detailData = {};
+
+ if (response.data && id) {
+ this.form = response.data;
+ // 瑙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;
+ // 瑙f瀽 filePatch 瀛楁
+ this.parseFilePatch(this.form.filePatch);
+ this.initAttachmentFileList();
+
+ // 濡傛灉涓撳瀹℃煡鎰忚鍒楄〃涓嶅瓨鍦紝鍒濆鍖栦负绌烘暟缁�
+ if (!this.form.ethicalreviewopinionsList) {
+ this.$set(this.form, "ethicalreviewopinionsList", []);
+ }
+ }
+
+ // 璁剧疆 expertReviews 鐢ㄤ簬琛ㄦ牸鏄剧ず
+ this.expertReviews = this.form.ethicalreviewopinionsList;
+
+ this.$message.success("鏁版嵁鍔犺浇鎴愬姛");
+ } else {
+ this.$message.error("鑾峰彇璇︽儏澶辫触锛�" + (response.msg || "鏈煡閿欒"));
+ }
+ } catch (error) {
+ console.error("鑾峰彇浼︾悊瀹℃煡璇︽儏澶辫触:", error);
+ this.$message.error("鏁版嵁鍔犺浇澶辫触");
+ } finally {
+ this.expertLoading = false;
+ }
},
- handleViewResolution() {
- this.$message.info('鏌ョ湅浼︾悊瀹℃煡鍐宠鏂囦欢鍔熻兘');
+
+ // 瑙f瀽 filePatch 瀛楁
+ parseFilePatch(filePatch) {
+ if (!filePatch) {
+ this.form.annexfilesList = [];
+ return;
+ }
+
+ try {
+ this.form.annexfilesList = JSON.parse(filePatch);
+ } catch (error) {
+ console.error("瑙f瀽 filePatch 瀛楁澶辫触:", error);
+ this.form.annexfilesList = [];
+ }
+ },
+
+ // 鍒濆鍖栭檮浠舵枃浠跺垪琛�
+ 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),
+ name: item.fileName,
+ url: item.path || item.fileUrl,
+ status: "success"
+ }));
+ } else {
+ this.attachmentFileList = [];
+ }
+ },
+
+ // 鏋勫缓 filePatch 瀛楁
+ buildFilePatch() {
+ if (!this.form.annexfilesList || this.form.annexfilesList.length === 0) {
+ return "";
+ }
+ return JSON.stringify(this.form.annexfilesList);
+ },
+
+ // 闄勪欢鍙樺寲澶勭悊
+ handleAttachmentChange(fileList) {
+ this.attachmentFileList = fileList;
+ },
+
+ // 闄勪欢绉婚櫎澶勭悊
+ handleAttachmentRemove(file) {
+ if (file.url) {
+ const index = this.form.annexfilesList.findIndex(
+ item => item.path === file.url || item.fileUrl === file.url
+ );
+ if (index > -1) {
+ this.form.annexfilesList.splice(index, 1);
+ }
+ }
+ },
+
+ // 鎵嬪姩鍒犻櫎闄勪欢
+ handleRemoveAttachment(index) {
+ this.form.annexfilesList.splice(index, 1);
+ this.attachmentFileList.splice(index, 1);
+ this.$message.success("闄勪欢鍒犻櫎鎴愬姛");
+ },
+
+ // 涓婁紶鎴愬姛澶勭悊
+ handleUploadSuccess({ file, fileList, response }) {
+ if (response.code === 200) {
+ const attachmentObj = {
+ fileName: file.name,
+ path: response.data || file.url,
+ fileUrl: response.data || file.url,
+ type: this.getFileExtension(file.name),
+ createTime: dayjs().format("YYYY-MM-DD HH:mm:ss"),
+ infoid: this.infoid,
+ delFlag: 0
+ };
+
+ this.form.annexfilesList.push(attachmentObj);
+ this.$message.success("鏂囦欢涓婁紶鎴愬姛");
+ }
+ },
+
+ // 涓婁紶閿欒澶勭悊
+ handleUploadError({ file, fileList, error }) {
+ console.error("闄勪欢涓婁紶澶辫触:", error);
+ this.$message.error("鏂囦欢涓婁紶澶辫触锛岃閲嶈瘯");
+ },
+
+ // 鏂囦欢棰勮
+ handlePreview(file) {
+ this.currentPreviewFile = {
+ fileName: file.fileName,
+ fileUrl: file.path || file.fileUrl,
+ fileType: this.getFileType(file.fileName)
+ };
+ this.previewVisible = true;
+ },
+
+ // 鏂囦欢涓嬭浇
+ handleDownload(file) {
+ const fileUrl = file.path || file.fileUrl;
+ const fileName = file.fileName;
+
+ if (fileUrl) {
+ const link = document.createElement("a");
+ link.href = fileUrl;
+ link.download = fileName;
+ link.style.display = "none";
+ document.body.appendChild(link);
+ link.click();
+ document.body.removeChild(link);
+ this.$message.success("寮�濮嬩笅杞芥枃浠�");
+ } else {
+ this.$message.warning("鏂囦欢璺緞涓嶅瓨鍦紝鏃犳硶涓嬭浇");
+ }
+ },
+
+ // 鑾峰彇鏂囦欢绫诲瀷
+ getFileType(fileName) {
+ if (!fileName) return "other";
+
+ const extension = fileName
+ .split(".")
+ .pop()
+ .toLowerCase();
+ const imageTypes = ["jpg", "jpeg", "png", "gif", "bmp", "webp"];
+ const pdfTypes = ["pdf"];
+ const officeTypes = ["doc", "docx", "xls", "xlsx", "ppt", "pptx"];
+
+ if (imageTypes.includes(extension)) return "image";
+ if (pdfTypes.includes(extension)) return "pdf";
+ if (officeTypes.includes(extension)) return "office";
+ return "other";
+ },
+
+ // 鑾峰彇鏂囦欢鎵╁睍鍚�
+ getFileExtension(filename) {
+ return filename
+ .split(".")
+ .pop()
+ .toLowerCase();
+ },
+
+ // 鏃ユ湡鏃堕棿鏍煎紡鍖�
+ formatDateTime(dateTime) {
+ if (!dateTime) return "";
+
+ try {
+ const date = new Date(dateTime);
+ if (isNaN(date.getTime())) return dateTime;
+
+ const year = date.getFullYear();
+ const month = String(date.getMonth() + 1).padStart(2, "0");
+ const day = String(date.getDate()).padStart(2, "0");
+ const hours = String(date.getHours()).padStart(2, "0");
+ const minutes = String(date.getMinutes()).padStart(2, "0");
+ const seconds = String(date.getSeconds()).padStart(2, "0");
+
+ return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`;
+ } catch (error) {
+ return dateTime;
+ }
+ },
+
+ // 鍒ゆ柇鏄惁涓轰富浠诲鍛�
+ getIsChiefExpert(expert) {
+ // 鑱岀О鍖呭惈"涓讳换濮斿憳"鎴栬�卐xpertType涓�"1"
+ return (
+ (expert.title && expert.title.includes("涓讳换濮斿憳")) ||
+ expert.expertType === "1"
+ );
+ },
+
+ // 涓撳绫诲瀷鏂囨湰杞崲
+ getExpertTypeText(type) {
+ return type === "1" ? "涓诲涓撳" : "鏅�氫笓瀹�";
+ },
+
+ // 瀹℃煡鐘舵�佽繃婊ゅ櫒
+ getReviewStatusFilter(status) {
+ const statusMap = {
+ "0": "info", // 寰呮帴鏀�
+ "1": "warning", // 鏈帴鏀�
+ "2": "success", // 宸叉帴鏀�
+ "3": "danger", // 瓒呮椂
+ "4": "danger", // 涓
+ "5": "success" // 瀹屾垚
+ };
+ return statusMap[status] || "info";
+ },
+
+ getReviewStatusText(status) {
+ const statusMap = {
+ "0": "寰呮帴鏀�",
+ "1": "鏈帴鏀�",
+ "2": "宸叉帴鏀�",
+ "3": "瓒呮椂",
+ "4": "涓",
+ "5": "瀹屾垚"
+ };
+ return statusMap[status] || "鏈煡";
+ },
+
+ // 缁撹杩囨护鍣�
+ getConclusionFilter(conclusion) {
+ const conclusionMap = {
+ "1": "success", // 鍚屾剰
+ "2": "warning", // 瀹℃煡涓�
+ "0": "danger" // 涓嶅悓鎰�
+ };
+ return conclusionMap[conclusion] || "info";
+ },
+
+ getConclusionText(conclusion) {
+ const conclusionMap = {
+ "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 = "";
+ } else {
+ // 鏅�氫笓瀹堕噸缃埅姝㈡椂闂翠负褰撳墠鏃堕棿
+ this.sendForm.endTime = "";
+ }
+ },
+
+ // 璁剧疆鎴鏃堕棿蹇嵎閿�
+ setEndTime(hours) {
+ const now = new Date();
+ const endTime = new Date(now.getTime() + hours * 60 * 60 * 1000);
+ this.sendForm.endTime = endTime
+ .toISOString()
+ .replace("T", " ")
+ .substring(0, 19);
+ },
+
+ // 淇濆瓨淇℃伅
+ async handleSave() {
+ this.$refs.form.validate(async valid => {
+ if (valid) {
+ this.saveLoading = true;
+ // 淇濆瓨娓呯┖id渚夸簬鍚庣鏁翠綋鍒犻櫎鏂板
+ this.form.ethicalreviewopinionsList.forEach(item=>{
+ item.id=null
+ })
+ try {
+ const submitData = {
+ ...this.form,
+ // 纭繚蹇呰瀛楁
+ infoid: this.infoid,
+ caseNo: this.caseNo,
+ // 鏋勫缓 filePatch 瀛楁
+ filePatch: this.buildFilePatch()
+ };
+
+ let response = null;
+
+ if (submitData.id) {
+ response = await ethicalreviewedit(submitData);
+ } else {
+ response = await ethicalreviewadd(submitData);
+ }
+
+ if (response.code === 200) {
+ this.$message.success("淇濆瓨鎴愬姛");
+ this.isEdit = false;
+ if (!this.form.id && response.data && response.data.id) {
+ this.form.id = response.data.id;
+ this.$router.replace({
+ query: { ...this.$route.query, id: this.form.id }
+ });
+ }
+ } else {
+ this.$message.error("淇濆瓨澶辫触锛�" + (response.msg || "鏈煡閿欒"));
+ }
+ } catch (error) {
+ console.error("淇濆瓨澶辫触:", error);
+ this.$message.error("淇濆瓨澶辫触锛岃閲嶈瘯");
+ } finally {
+ this.saveLoading = false;
+ }
+ }
+ });
+ },
+
+ // 瀹℃煡瀹屾垚
+ 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";
+ } else {
+ this.$message.error("鎿嶄綔澶辫触锛�" + (response.msg || "鏈煡閿欒"));
+ }
+ } catch (error) {
+ console.error("涓瀹℃煡澶辫触:", error);
+ this.$message.error("涓瀹℃煡澶辫触");
+ } finally {
+ this.suspendLoading = false;
+ }
+ })
+ .catch(() => {});
+ },
+
+ // 缁撴潫瀹℃煡
+ async handleEndReview() {
+ this.$confirm(
+ "纭畾瑕佺粨鏉熸湰娆′鸡鐞嗗鏌ュ悧锛熺粨鏉熷悗灏嗘棤娉曚慨鏀逛笓瀹跺鏌ョ粨鏋溿��",
+ "鎻愮ず",
+ {
+ confirmButtonText: "纭畾",
+ cancelButtonText: "鍙栨秷",
+ type: "warning"
+ }
+ )
+ .then(async () => {
+ this.endLoading = true;
+ try {
+ const updateData = {
+ ...this.form,
+ status: "2", // 瀹℃煡涓
+ endTime: new Date()
+ .toISOString()
+ .replace("T", " ")
+ .substring(0, 19)
+ };
+
+ const response = await ethicalreviewedit(updateData);
+
+ if (response.code === 200) {
+ this.$message.success("瀹℃煡宸茬粨鏉�");
+ this.form.status = "2";
+ this.form.endTime = updateData.endTime;
+ } else {
+ this.$message.error("鎿嶄綔澶辫触锛�" + (response.msg || "鏈煡閿欒"));
+ }
+ } catch (error) {
+ console.error("缁撴潫瀹℃煡澶辫触:", error);
+ this.$message.error("缁撴潫瀹℃煡澶辫触");
+ } finally {
+ this.endLoading = false;
+ }
+ })
+ .catch(() => {});
+ },
+
+ // 鎵撳紑娣诲姞涓撳瀵硅瘽妗�
+ 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 || 0;
+ } 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", []);
+ }
+
+ // 娣诲姞涓撳鍒板垪琛�
+ this.selectedExperts.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 || "",
+ deptname: 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"
+ };
+
+ // 浣跨敤push娣诲姞锛岀‘淇濆搷搴斿紡
+ this.form.ethicalreviewopinionsList.push(expertReview);
+ });
+
+ this.$message.success(`鎴愬姛娣诲姞 ${this.selectedExperts.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.currentSendExperts = this.availableNormalExperts;
+ this.sendForm.expertType = "normal";
+ this.sendForm.endTime = ""; // 閲嶇疆鎴鏃堕棿
+ this.sendDialogVisible = true;
+ },
+
+ // 鍙戦�佺粰涓诲涓撳
+ handleSendToChiefExpert() {
+ this.currentSendExperts = this.availableChiefExperts;
+ this.sendForm.expertType = "chief";
+ this.sendForm.endTime = ""; // 涓诲涓撳鏃犻渶鎴鏃堕棿
+ this.sendDialogVisible = true;
+ },
+
+ // 鍙戦�佺粰鍗曚釜涓撳
+ handleSendToExpert(expert) {
+ this.currentSendExperts = [expert];
+ this.sendForm.expertType = expert.expertType === "1" ? "chief" : "normal";
+ this.sendForm.endTime = expert.expertType === "1" ? "" : ""; // 涓诲涓撳鏃犻渶鎴鏃堕棿
+ this.sendDialogVisible = true;
+ },
+
+ // 鍙戦�佸璇濇鍏抽棴
+ handleSendDialogClose() {
+ this.sendForm = {
+ expertType: "normal",
+ expertIds: [],
+ startTime: "",
+ endTime: "",
+ sendType: "0",
+ title: "浼︾悊瀹℃煡浠诲姟閫氱煡",
+ content: "",
+ url: ""
+ };
+ this.currentSendExperts = [];
+ },
+
+ // 纭鍙戦��
+ 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.title) {
+ this.$message.warning("璇疯緭鍏ュ彂閫佹爣棰�");
+ return;
+ }
+
+ if (!this.sendForm.content) {
+ this.$message.warning("璇疯緭鍏ュ彂閫佸唴瀹�");
+ return;
+ }
+
+ if (this.currentSendExperts.length === 0) {
+ this.$message.warning("娌℃湁鎵惧埌鍙彂閫佺殑涓撳");
+ return;
+ }
+
+ this.sending = true;
+ try {
+ // 鍙戦�佺粰姣忎釜涓撳
+ const sendPromises = this.currentSendExperts.map(async expert => {
+ try {
+ // 鏋勫缓鍙戦�佹暟鎹�
+ const sendData = {
+ number: expert.deptname || "", // 鐢ㄦ埛鎵嬫満鍙�
+ title: this.sendForm.title,
+ url: this.sendForm.url || "",
+
+ createTime: new Date()
+ .toISOString()
+ .replace("T", " ")
+ .substring(0, 19)
+ };
+
+ // 璋冪敤鍙戦�侀�氱煡鎺ュ彛
+ const response = await sendNotification(sendData);
+
+ if (response.code === 200) {
+ // 鏇存柊涓撳鐘舵��
+ const index = this.form.ethicalreviewopinionsList.findIndex(
+ e =>
+ e.expertNo === expert.expertNo ||
+ e.expertname === expert.expertname
+ );
+
+ if (index !== -1) {
+ this.form.ethicalreviewopinionsList[index].receiveStatus = "1"; // 宸叉帴鏀�
+ 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);
+
+ // 浣跨敤Vue.set纭繚鍝嶅簲寮忔洿鏂�
+ this.$set(
+ this.form.ethicalreviewopinionsList,
+ index,
+ this.form.ethicalreviewopinionsList[index]
+ );
+ }
+
+ return { success: true, expert: expert.expertname };
+ } else {
+ return {
+ success: false,
+ expert: expert.expertname,
+ error: response.msg
+ };
+ }
+ } catch (error) {
+ console.error(`鍙戦�佺粰涓撳 ${expert.expertname} 澶辫触:`, error);
+ return {
+ success: false,
+ expert: expert.expertname,
+ error: error.message
+ };
+ }
+ });
+
+ // 绛夊緟鎵�鏈夊彂閫佸畬鎴�
+ const results = await Promise.all(sendPromises);
+
+ // 缁熻鍙戦�佺粨鏋�
+ const successCount = results.filter(r => r.success).length;
+ const failCount = results.filter(r => !r.success).length;
+
+ if (failCount === 0) {
+ this.$message.success(`鎴愬姛鍙戦�佺粰 ${successCount} 浣嶄笓瀹禶);
+ } else if (successCount > 0) {
+ this.$message.warning(
+ `鎴愬姛鍙戦�佺粰 ${successCount} 浣嶄笓瀹讹紝澶辫触 ${failCount} 浣峘
+ );
+ } else {
+ this.$message.error("鍙戦�佸け璐ワ紝璇风◢鍚庨噸璇�");
+ }
+
+ this.sendDialogVisible = false;
+ this.sendForm = {
+ expertType: "normal",
+ expertIds: [],
+ startTime: "",
+ endTime: "",
+ sendType: "0",
+ title: "浼︾悊瀹℃煡浠诲姟閫氱煡",
+ content: "",
+ url: ""
+ };
+ this.currentSendExperts = [];
+ } catch (error) {
+ console.error("鍙戦�佸け璐�:", error);
+ this.$message.error("鍙戦�佸け璐ワ紝璇烽噸璇�");
+ } finally {
+ this.sending = false;
+ }
+ },
+
+ // 鍒犻櫎涓撳瀹℃煡
+ handleDeleteExpertReview(expert, index) {
+ this.$confirm("纭畾瑕佸垹闄よ涓撳鐨勫鏌ヨ褰曞悧锛�", "鎻愮ず", {
+ confirmButtonText: "纭畾",
+ cancelButtonText: "鍙栨秷",
+ type: "warning"
+ })
+ .then(() => {
+ // 浠庢暟缁勪腑鍒犻櫎涓撳
+ this.form.ethicalreviewopinionsList.splice(index, 1);
+ this.$message.success("涓撳瀹℃煡璁板綍宸插垹闄�");
+ })
+ .catch(() => {});
+ },
+
+ // 鏌ョ湅涓撳鍘嗗彶瀹℃壒鎯呭喌
+ async handleViewExpertHistory(expert) {
+ if (!expert.expertNo) {
+ this.$message.warning("璇ヤ笓瀹舵病鏈夌紪鍙凤紝鏃犳硶鏌ヨ鍘嗗彶瀹℃壒鎯呭喌");
+ return;
+ }
+
+ this.currentExpertInfo = expert;
+ this.expertHistoryLoading = true;
+ this.expertHistoryDialogVisible = true;
+
+ try {
+ const params = {
+ expertNo: expert.expertNo
+ };
+
+ const response = await ethicalreExpertTotal(params);
+
+ if (response && response.code === 200) {
+ this.expertHistoryData = response.data || response[0] || null;
+ } else {
+ this.$message.error(
+ "鏌ヨ涓撳鍘嗗彶瀹℃壒鎯呭喌澶辫触锛�" + (response?.msg || "鏈煡閿欒")
+ );
+ this.expertHistoryData = null;
+ }
+ } catch (error) {
+ console.error("鏌ヨ涓撳鍘嗗彶瀹℃壒鎯呭喌澶辫触:", error);
+ this.$message.error("鏌ヨ涓撳鍘嗗彶瀹℃壒鎯呭喌澶辫触");
+ this.expertHistoryData = null;
+ } finally {
+ this.expertHistoryLoading = false;
+ }
+ },
+
+ // 鏃堕棿鏍煎紡鍖�
+ parseTime(time) {
+ if (!time) return "";
+ const date = new Date(time);
+ if (isNaN(date.getTime())) return time;
+
+ return `${date.getFullYear()}-${(date.getMonth() + 1)
+ .toString()
+ .padStart(2, "0")}-${date
+ .getDate()
+ .toString()
+ .padStart(2, "0")} ${date
+ .getHours()
+ .toString()
+ .padStart(2, "0")}:${date
+ .getMinutes()
+ .toString()
+ .padStart(2, "0")}`;
}
}
};
</script>
<style scoped>
-.resolution-content {
+.ethics-review-detail {
padding: 20px;
- line-height: 1.8;
+ background-color: #f5f7fa;
}
-.signature-area {
+.detail-card {
+ margin-bottom: 20px;
+ border-radius: 8px;
+ box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
+}
+
+.expert-card {
+ margin-bottom: 20px;
+ border-radius: 8px;
+ box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
+}
+
+.attachment-card {
+ margin-bottom: 20px;
+ border-radius: 8px;
+ box-shadow: 0 2px 12px 0 rgba(0, 0, 0, 0.1);
+}
+
+.detail-title {
+ font-size: 18px;
+ font-weight: 600;
+ color: #303133;
+}
+
+.expert-stats {
+ background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
+ color: rgb(43, 181, 245);
+ border-radius: 8px;
+ margin-bottom: 20px;
+}
+
+.stat-item {
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ padding: 10px;
+}
+
+.stat-label {
+ font-size: 12px;
+ opacity: 0.9;
+ margin-bottom: 5px;
+}
+
+.stat-value {
+ font-size: 18px;
+ font-weight: bold;
+}
+
+.file-info {
+ display: flex;
+ align-items: center;
+}
+
+.empty-attachment {
+ text-align: center;
+ padding: 40px 0;
+ color: #909399;
+}
+
+/* 涓撳绫诲瀷鏍峰紡 */
+.normal-expert {
+ color: #409eff;
+ font-weight: 500;
+}
+
+.chief-expert {
+ color: #f56c6c;
+ font-weight: 600;
+}
+
+/* 涓撳琛屾牱寮� */
+:deep(.normal-expert-row) {
+ background-color: #fafafa;
+}
+
+:deep(.chief-expert-row) {
+ background-color: #fff7e6;
+}
+
+:deep(.normal-expert-row:hover) {
+ background-color: #f0f7ff;
+}
+
+:deep(.chief-expert-row:hover) {
+ background-color: #ffecc2;
+}
+
+/* 鏃犳暟鎹牱寮� */
+.no-data {
+ color: #909399;
+ font-style: italic;
+}
+
+/* 涓撳鎰忚鏍峰紡 */
+.expert-opinion {
+ color: #303133;
+ line-height: 1.5;
+}
+
+/* 宸插彂閫佹寜閽牱寮� */
+.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;
+}
+
+.section-header {
+ display: flex;
+ align-items: center;
+ font-weight: bold;
+ color: #303133;
+}
+
+.dialog-footer {
text-align: right;
- margin-top: 30px;
+ padding: 20px 0 0;
+}
+
+.attachment-section {
+ margin-bottom: 16px;
+}
+
+.attachment-header {
+ display: flex;
+ align-items: center;
+ margin-bottom: 16px;
+ padding: 8px 0;
+ border-bottom: 1px solid #ebeef5;
+}
+
+.attachment-title {
+ font-weight: bold;
+ margin: 0 8px;
+}
+
+.attachment-tip {
+ font-size: 12px;
+ color: #909399;
+}
+
+.attachment-list {
+ margin-top: 16px;
+}
+
+.list-title {
+ font-weight: bold;
+ margin-bottom: 12px;
+ color: #303133;
+}
+
+.file-name {
+ font-size: 13px;
+}
+
+/* 妗堜緥淇℃伅灞曠ず鏍峰紡 */
+.selected-case-info {
+ margin-bottom: 20px;
+}
+
+.case-info-card {
+ border-left: 4px solid #67c23a;
+}
+
+/* 鍝嶅簲寮忚璁� */
+@media (max-width: 768px) {
+ .ethics-review-detail {
+ padding: 10px;
+ }
+
+ .expert-stats .el-col {
+ margin-bottom: 10px;
+ }
}
</style>
--
Gitblit v1.9.3