董国庆
2025-09-12 527efb36f35b471710e445972673abff45bacdac
laboratory/src/views/deliveryAssessment/taskList/components/AssessmentDialog.vue
@@ -1,244 +1,322 @@
<template>
    <el-dialog :visible.sync="dialogVisible" title="课题评定详情" width="70%" @close="handleClose">
        <el-form :model="form" inline label-position="top" :rules="rules" ref="formRef">
            <el-row :gutter="20">
                <el-col :xs="24" :sm="24" :md="12" :lg="12" :xl="12">
                    <el-form-item label="报告编号" prop="reportNo" required>
                        <el-input v-model="form.reportNo" disabled placeholder="自动生成" />
                    </el-form-item>
                </el-col>
                <el-col :xs="24" :sm="24" :md="12" :lg="12" :xl="12">
                    <el-form-item label="报告名称" prop="reportName">
                        <el-input v-model="form.reportName" placeholder="请输入" />
                    </el-form-item>
                </el-col>
            </el-row>
        </el-form>
        <div class="content-box">
            <el-row :gutter="16">
                <el-col :xs="24" :sm="24" :md="4" :lg="4" :xl="4">
                    <div class="content-box-left">
                        <div class="content-box-left-th">设立课题规则</div>
                        <div class="content-box-left-body" :style="{ height: `calc(${$baseTableHeight()}px - 40px)` }">
                            1、根据可研报告、产品构思设计的工艺研究路线,一条工艺路线设立一个课题。如果一个课题中有多个化合物需要开发研究,则每个化合物作为一个分题;分题归集到该课题中,最终形成课题报告。不同课题报告中的分题不能重复使用。
                            2、在可行研究阶段,工艺开发升级,重新规划工艺研究路线,则以新规划的工艺路线方案来设定课题</div>
                    </div>
                </el-col>
                <el-col style="margin-top: 5px;" :xs="24" :sm="24" :md="20" :lg="20" :xl="20">
                    <Table :total="0" :height="null" :data="criteriaList" show-summary :summary-method="getSummaries"
                        :span-method="arraySpanMethod">
                        <el-table-column type="index" label="序号" width="80" />
                        <el-table-column prop="criteria" label="创新型课题标准" />
                        <el-table-column prop="fullScore" label="满分值" width="100" />
                        <el-table-column label="评定分值" prop="score" width="200">
                            <template #default="{ row }">
                                <el-input-number v-model="row.score" :min="0" :max="row.fullScore" :step="1" />
                            </template>
                        </el-table-column>
                        <el-table-column prop="rule" label="创新型课题报告评分规则">
                            <template>
                                <div>
                                    <div>1、各分项评满分,应满足以下四项要求:</div>
                                    <div>①分项内容:清晰、系统、完整,结构逻辑清晰,无缺项;</div>
                                    <div>②团队工作运行顺畅、计划时间高效。</div>
                                    <div>③工作结果完成度高。</div>
                                    <div>④课题文档报告完成度高。</div>
                                    <div>2、某分项工作完成,但出现以下三种错误中的1种,则减1分:</div>
                                    <div>①有缺项、漏项;</div>
                                    <div>②或不完整清晰;</div>
                                    <div>③或工作效率人为拖延。</div>
                                    <div>3、某分项工作基本完成,但出现三种错误中的2-3种,则减2分:</div>
                                    <div> ①有缺项、漏项;</div>
                                    <div>②或不完整清晰;</div>
                                    <div>③或工作效率人为拖延。</div>
                                    <div>4、不能完成某分项的全部工作,或课题不涉及该分项内容,则该分项评0分。</div>
                                </div>
                            </template>
                        </el-table-column>
                    </Table>
                </el-col>
            </el-row>
        </div>
        <div class="assessed">
            <div>评定时间:2025-2-20 11:08:00</div>
            <div>评定人:张三</div>
        </div>
        <template #footer>
            <span class="dialog-footer select-member-footer">
                <el-button type="primary" @click="handleSubmit">提交评定结果</el-button>
            </span>
        </template>
    </el-dialog>
  <el-dialog
    :visible.sync="dialogVisible"
    title="课题评定详情"
    width="70%"
    @close="handleClose"
  >
    <el-form
      :model="form"
      inline
      label-position="top"
      :rules="rules"
      ref="formRef"
    >
      <el-row :gutter="20">
        <el-col :xs="24" :sm="24" :md="12" :lg="12" :xl="12">
          <el-form-item label="报告编号" prop="reportNo" required>
            <el-input v-model="form.reportNo" disabled placeholder="自动生成" />
          </el-form-item>
        </el-col>
        <el-col :xs="24" :sm="24" :md="12" :lg="12" :xl="12">
          <el-form-item label="报告名称" prop="reportName">
            <el-input v-model="form.reportName" disabled placeholder="请输入" />
          </el-form-item>
        </el-col>
      </el-row>
    </el-form>
    <div class="content-box">
      <el-row :gutter="16">
        <el-col :xs="24" :sm="24" :md="4" :lg="4" :xl="4">
          <div class="content-box-left">
            <div class="content-box-left-th">设立课题规则</div>
            <div
              class="content-box-left-body"
              :style="{ height: `calc(${$baseTableHeight()}px - 40px)` }"
            >
              1、根据可研报告、产品构思设计的工艺研究路线,一条工艺路线设立一个课题。如果一个课题中有多个化合物需要开发研究,则每个化合物作为一个分题;分题归集到该课题中,最终形成课题报告。不同课题报告中的分题不能重复使用。
              2、在可行研究阶段,工艺开发升级,重新规划工艺研究路线,则以新规划的工艺路线方案来设定课题
            </div>
          </div>
        </el-col>
        <el-col
          style="margin-top: 5px"
          :xs="24"
          :sm="24"
          :md="20"
          :lg="20"
          :xl="20"
        >
          <Table
            :total="0"
            :height="null"
            :data="criteriaList"
            show-summary
            :summary-method="getSummaries"
            :span-method="arraySpanMethod"
          >
            <el-table-column type="index" label="序号" width="80" />
            <el-table-column prop="criteria" label="创新型课题标准" />
            <el-table-column prop="fullScore" label="满分值" width="100" />
            <el-table-column label="评定分值" prop="score" width="200">
              <template #default="{ row }">
                <el-input-number
                  v-model="row.score"
                  :min="0"
                  :max="row.fullScore"
                  :step="1"
                  :disabled="type === 'detail'"
                />
              </template>
            </el-table-column>
            <el-table-column prop="rule" label="创新型课题报告评分规则">
              <template>
                <div>
                  <div>1、各分项评满分,应满足以下四项要求:</div>
                  <div>①分项内容:清晰、系统、完整,结构逻辑清晰,无缺项;</div>
                  <div>②团队工作运行顺畅、计划时间高效。</div>
                  <div>③工作结果完成度高。</div>
                  <div>④课题文档报告完成度高。</div>
                  <div>
                    2、某分项工作完成,但出现以下三种错误中的1种,则减1分:
                  </div>
                  <div>①有缺项、漏项;</div>
                  <div>②或不完整清晰;</div>
                  <div>③或工作效率人为拖延。</div>
                  <div>
                    3、某分项工作基本完成,但出现三种错误中的2-3种,则减2分:
                  </div>
                  <div>①有缺项、漏项;</div>
                  <div>②或不完整清晰;</div>
                  <div>③或工作效率人为拖延。</div>
                  <div>
                    4、不能完成某分项的全部工作,或课题不涉及该分项内容,则该分项评0分。
                  </div>
                </div>
              </template>
            </el-table-column>
          </Table>
        </el-col>
      </el-row>
    </div>
    <div class="assessed" v-if="evaluateInfo.status == 3">
      <div>评定时间:{{ evaluateInfo.evaluateTime || "" }}</div>
      <div>评定人:{{ evaluateInfo.evaluatePersonName || "" }}</div>
    </div>
    <template #footer>
      <span class="dialog-footer select-member-footer" v-if="type !== 'detail'">
        <el-button type="primary" @click="handleSubmit">提交评定结果</el-button>
      </span>
    </template>
  </el-dialog>
</template>
<script>
import { evaluateDetail, evaluate } from "../service.js";
export default {
    name: 'AssessmentDialog',
    props: {
        modelValue: {
            type: Boolean,
            default: false
        },
        reportData: {
            type: Object,
            default: () => { }
        }
  name: "AssessmentDialog",
  props: {
    modelValue: {
      type: Boolean,
      default: false,
    },
    data() {
    id: {
      type: [String, Number],
      required: true,
    },
    type: {
      type: String,
      default: "assessment",
    },
  },
  data() {
    return {
      dialogVisible: false,
      form: {
        reportNo: "",
        reportName: "",
      },
      evaluateInfo: {},
      rules: {
        reportName: [
          { required: true, message: "请输入报告名称", trigger: "blur" },
        ],
      },
      criteriaList: [
        {
          criteria:
            "课题名称依准确性、报告内容完整性:研究目标是否完整、是否研究工作内容合理性、是否围绕目标展开开、是否具有逻辑性和条理性",
          fullScore: 3,
          score: 0,
        },
        {
          criteria: "文献资料调查:全面性、系统性编辑逻辑顺畅,表达规范",
          fullScore: 2,
          score: 0,
        },
        {
          criteria:
            "专业/技术路线与方法:合理性、可行性;实验设计科学、能完成研究目标、方法完整、完整、可靠;",
          fullScore: 3,
          score: 0,
        },
        {
          criteria:
            "实验验证工作可行性强,团队工作调度支持实验设备、材料等条件的准备良好,时间计划合理,能效按时完成",
          fullScore: 2,
          score: 0,
        },
        {
          criteria:
            "实验设计合理性:①对照设置,重复次数,样本量足够。②变量控制:自变量控制,因变量测量,干扰因素分析识别;确保结果的准确性③验证结果解释分析:符合科学原理、逻辑严密、不存在漏洞。具有实际应用价值,解决了实际问题或实现了研究目的。",
          fullScore: 3,
          score: 0,
        },
        {
          criteria:
            "课题报告完成度高,预期研究结果果是否具有技术价值和应用价值。风险识别:识别了潜在的技术风险、市场风险和管理风险。",
          fullScore: 2,
          score: 0,
        },
      ],
    };
  },
  watch: {
    modelValue: {
      handler(val) {
        this.dialogVisible = val;
        if (val && this.id) {
          this.fetchDetail();
        }
      },
      immediate: true,
    },
    id(val) {
      if (this.modelValue && val) {
        this.fetchDetail();
      }
    },
  },
  methods: {
    async fetchDetail() {
      try {
        const res = await evaluateDetail({ id: this.id });
        if (res) {
          this.evaluateInfo = { ...res };
          this.form.reportNo = res.reportCode || "";
          this.form.reportName = res.reportName || "";
          // 详情回显分数
          if (res.evaluateScore) {
            if (
              this.type === "detail" &&
              typeof res.evaluateScore === "string"
            ) {
              const scoreArr = res.evaluateScore
                .split(",")
                .map((s) => Number(s));
              this.criteriaList.forEach((item, idx) => {
                item.score = scoreArr[idx] || 0;
              });
            }
          }
        }
      } catch (e) {
        this.$message && this.$message.error("获取详情失败");
      }
    },
    handleClose() {
      this.$emit("update:modelValue", false);
      this.$emit("close");
    },
    async handleSubmit() {
      try {
        // 拼接分数
        const evaluateScore = this.criteriaList
          .map((item) => item.score)
          .join(",");
        const params = {
          id: this.id,
          evaluateScore,
        };
        evaluate(params).then((res) => {
          if (res) {
            this.$message && this.$message.success("评定成功");
            this.handleClose();
          }
        });
      } catch (error) {
        this.$message && this.$message.error("评定失败");
      }
    },
    getSummaries(param) {
      const { columns, data } = param;
      const sums = [];
      columns.forEach((column, index) => {
        if (index === 0) {
          sums[index] = "合计";
          return;
        }
        const values = data.map((item) => Number(item[column.property]));
        if (!values.every((value) => isNaN(value))) {
          sums[index] = values.reduce((prev, curr) => {
            const value = Number(curr);
            if (!isNaN(value)) {
              return prev + curr;
            } else {
              return prev;
            }
          }, 0);
          sums[index] += " 分";
        } else {
          sums[index] = "";
        }
      });
      return sums;
    },
    arraySpanMethod({ row, column, rowIndex, columnIndex }) {
      if (columnIndex === 4 && rowIndex == 0) {
        return {
            dialogVisible: false,
            form: {
                reportNo: '',
                reportName: '',
            },
            rules: {
                reportName: [
                    { required: true, message: '请输入报告名称', trigger: 'blur' }
                ],
            },
            criteriaList: [
                {
                    criteria: '课题名称依准确性、报告内容完整性:研究目标是否完整、是否研究工作内容合理性、是否围绕目标展开开、是否具有逻辑性和条理性',
                    fullScore: 3,
                    score: 0,
                },
                {
                    criteria: '文献资料调查:全面性、系统性编辑逻辑顺畅,表达规范',
                    fullScore: 2,
                    score: 0,
                },
                {
                    criteria: '专业/技术路线与方法:合理性、可行性;实验设计科学、能完成研究目标、方法完整、完整、可靠;',
                    fullScore: 3,
                    score: 0,
                },
                {
                    criteria: '实验验证工作可行性强,团队工作调度支持实验设备、材料等条件的准备良好,时间计划合理,能效按时完成',
                    fullScore: 2,
                    score: 0,
                },
                {
                    criteria: '实验设计合理性:①对照设置,重复次数,样本量足够。②变量控制:自变量控制,因变量测量,干扰因素分析识别;确保结果的准确性③验证结果解释分析:符合科学原理、逻辑严密、不存在漏洞。具有实际应用价值,解决了实际问题或实现了研究目的。',
                    fullScore: 3,
                    score: 0,
                },
                {
                    criteria: '课题报告完成度高,预期研究结果果是否具有技术价值和应用价值。风险识别:识别了潜在的技术风险、市场风险和管理风险。',
                    fullScore: 2,
                    score: 0,
                }
            ]
        }
          rowspan: 6,
          colspan: 1,
        };
      } else if (rowIndex !== 0 && columnIndex === 4) {
        return [0, 0];
      }
    },
    watch: {
        modelValue: {
            handler(val) {
                this.dialogVisible = val;
            },
            immediate: true
        },
        reportData: {
            handler(val) {
                if (val) {
                    this.form.reportNo = val.reportNo || '';
                    this.form.reportName = val.reportName || '';
                }
            },
            immediate: true
        }
    },
    methods: {
        handleClose() {
            this.$emit('update:modelValue', false);
        },
        async handleSubmit() {
            try {
                await this.$refs.formRef.validate();
                const totalScore = this.criteriaList.reduce((sum, item) => sum + (item.score || 0), 0);
                const assessmentData = {
                    ...this.form,
                    totalScore,
                    criteriaScores: this.criteriaList.map(item => ({
                        criteria: item.criteria,
                        score: item.score || 0
                    }))
                };
                this.$emit('submit', assessmentData);
                this.handleClose();
            } catch (error) {
                // 表单验证失败
            }
        },
        getSummaries(param) {
            const { columns, data } = param;
            const sums = [];
            columns.forEach((column, index) => {
                if (index === 0) {
                    sums[index] = '合计';
                    return;
                }
                const values = data.map(item => Number(item[column.property]));
                if (!values.every(value => isNaN(value))) {
                    sums[index] = values.reduce((prev, curr) => {
                        const value = Number(curr);
                        if (!isNaN(value)) {
                            return prev + curr;
                        } else {
                            return prev;
                        }
                    }, 0);
                    sums[index] += ' 分';
                } else {
                    sums[index] = '';
                }
            });
            return sums;
        },
        arraySpanMethod({ row, column, rowIndex, columnIndex }) {
            if (columnIndex === 4 && rowIndex == 0) {
                return {
                    rowspan: 6,
                    colspan: 1
                }
            } else if (rowIndex !== 0 && columnIndex === 4) {
                return [0, 0]
            }
        }
    }
}
  },
};
</script>
<style lang="less" scoped>
.content-box {
  &-left {
    margin-top: 5px;
    display: flex;
    flex-direction: column;
    border: 1px solid #ebeef5;
    border-radius: 8px 8px 0px 0px;
    font-size: 12px;
    &-left {
        margin-top: 5px;
        display: flex;
        flex-direction: column;
        border: 1px solid #EBEEF5;
        border-radius: 8px 8px 0px 0px;
        font-size: 12px;
        &-th {
            line-height: 40px;
            background: #FAFAFA !important;
            color: #909399;
            padding: 0 10px;
            font-weight: bold;
        }
        &-body {
            padding: 0 10px;
            line-height: 23px;
        }
    &-th {
      line-height: 40px;
      background: #fafafa !important;
      color: #909399;
      padding: 0 10px;
      font-weight: bold;
    }
    &-body {
      padding: 0 10px;
      line-height: 23px;
    }
  }
}
.assessed {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 25px;
    margin-top: 20px;
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 25px;
  margin-top: 20px;
}
</style>