wucl il y a 2 ans
Parent
commit
f03c957d44

+ 3 - 0
src/api/modules/dashboard.js

@@ -14,4 +14,7 @@ export default {
     getAnalysisListByCycleId(param) {
         return request.get(`/analysis/cycle/${param}`)
     },
+    getMergeData(){
+        return request.get(`/analysis/merge`)
+    }
 }

+ 6 - 1
src/api/modules/question.js

@@ -10,5 +10,10 @@ export default {
   add(params) {
     return request.post(`question`, params)
   },
-  
+  tree() {
+    return request.get(`/question/tree`)
+  },
+  dele(params) {
+    return request.delete(`/question/${params}`)
+  },
 }

+ 17 - 9
src/pages/Cycle.vue

@@ -7,15 +7,19 @@
     <y-page-list-layout :page-list="pageData" :page-para="listQuery" :getList="getList">
       <template slot="left">
         <el-input v-model="listQuery.itemName" placeholder="请输项目名称" clearable
-          style="margin-left: 20px;width: 320px;float: left;">
+          style="width: 320px;float: left;">
         </el-input>
-        <el-select v-model="listQuery.cycleName" placeholder="请选择轮数" clearable
+        <el-select v-model="listQuery.typeId" placeholder="请选择问卷类型" clearable
+          style="margin-left: 20px;width: 320px; float: left;">
+            <el-option v-for="(o, index) in options" :label="o.label" :value="o.id" />
+          </el-select>
+          <el-select v-model="listQuery.cycleName" placeholder="请选择轮数" clearable
           style="margin-left: 20px;width: 320px;float: left;">
           <el-option v-for="(c, index) in cycleNames" :label="c" :value="c" />
         </el-select>
-        <el-button class="filter-item" style="margin-left: 10px;float: left;" type="primary" @click="searchList" round>搜索
+        <el-button class="filter-item" style="margin-left: 10px;" type="primary" @click="searchList" round>搜索
         </el-button>
-        <el-button class="filter-item" style="float: left;" round type="warning" @click="resetSearch()">重置
+        <el-button class="filter-item"  round type="warning" @click="resetSearch()">重置
         </el-button>
       </template>
       <parentTable v-loading="listLoading" :data="pageData.records" slot="table" style="width: 100%;"
@@ -30,11 +34,6 @@
             <span>{{ row.type }}</span>
           </template>
         </el-table-column>
-        <el-table-column label="范围" align="center">
-          <template slot-scope="{row}">
-            <span>{{ row.scope }}</span>
-          </template>
-        </el-table-column>
         <el-table-column label="轮数" align="center">
           <template slot-scope="{row}">
             <span>{{ row.cycleName }}</span>
@@ -104,6 +103,7 @@ export default {
         size: 10,
         descs: 'id',
       },
+      options: [],
       listQueryKey: 'keyword',
       dialogVisible: false,
       cycleNames: ['第一轮', '第二轮', '第三轮', '第四轮', '第五轮', '第六轮', '第七轮', '第八轮', '第九轮', '第十轮'],
@@ -114,8 +114,16 @@ export default {
   },
   created() {
     this.getList();
+    this.getQuestionType();
   },
   methods: {
+    getQuestionType(){
+        this.$api.question.simpleAll().then(res=>{
+          if (res.code === 200){
+              this.options = res.data;
+          }
+        })
+    },
     remove(row) {
       if (row.id) {
         this.$confirm('此操作将删除专家评分, 是否继续?', '提示', {

+ 118 - 61
src/pages/DashBoard.vue

@@ -1,24 +1,70 @@
 <template>
     <div class="board-container">
         <div class="header">
-            <img src="../assets/logo.png" style="margin-left: 10px; margin-top: 10px;" >
+            <img src="../assets/logo.png" style="margin-left: 10px; margin-top: 10px;">
         </div>
         <div class="title_info">
             <el-card shadow="always" style="background-color: rgb(201,235,251); border-radius: 5px;">
                 <p class="item_name">{{ itemName }}</p>
-                <p class="scope_cycleName">{{type}} {{ scope }} 类 {{ cycleName }} 专家评分公示</p>
+                <p class="scope_cycleName">{{ type }} {{ scope }} 类 {{ cycleName }} 专家评分公示</p>
             </el-card>
         </div>
         <div class="analysis_collect">
             <span class="name"><i class="el-icon-s-data"></i>评分项平均得分</span>
-            <el-table :data="analysisData" border style="width: 100% ;" stripe highlight-current-row>
-                <el-table-column prop="questionName" label="评分项" align="center">
-                </el-table-column>
-                <el-table-column prop="avgScore" label="平均得分" align="center">
-                </el-table-column>
+            <!-- <el-row :gutter="20">
+                <el-col :span="6" v-for="(item, index ) in analysisData">
+                    <el-card shadow="never">
+                        <span style="font-weight: 900; letter-spacing: 3px;">
+                            {{ item.questionName }}
+                        </span>
+                        <span style="float: right; color: red;">
+                            {{ item.avgScore }}
+                        </span>
+                    </el-card>
+                </el-col>
+            </el-row> -->
+            <el-table :data="analysisData" :span-method="objectSpanMethod"
+                style="width: 100% ;letter-spacing: 2px; color: black; " border="border">
+                <el-table-column v-for="item in tableColumn" :key="item.prop" :prop="item.prop" :label="item.label"
+                    align="center" min-width="180" />
             </el-table>
+            <!-- <y-page-list-layout>
+                <el-table :data="analysisData" slot="table" style="width: 100%;" border="border">
+                    <el-table-column v-if="analysisData != null && analysisData.length > 0" label="名称" align="center" width="150px">
+                        <template slot-scope="{row}">
+                            <span style=" letter-spacing: 3px;">{{ row.questionName }}-{{ row.avgScore }}</span>
+                        </template>
+                    </el-table-column>
+                    <el-table-column v-if="analysisData != null && analysisData.length > 0" label="定级因素" align="center">
+                        <template slot-scope="{row}">
+                            <div style="margin-bottom: 10px; letter-spacing: 3px;" v-for="(item, index) in row.children">{{ item.questionName }}-{{ item.avgScore }}</div>
+                        </template>
+                    </el-table-column>
+                    <el-table-column v-if="analysisData != null && analysisData.length > 0" label="一级因子" align="center">
+                        <template slot-scope="{row}">
+                            <div  v-for="(item, index) in row.children">
+                                <div style=" margin-bottom: 10px;  letter-spacing: 3px;" v-for="(item1, index) in item.children">
+                                    {{ item1.questionName }}-{{ item1.avgScore }}
+                                </div>
+                            </div>
+                        </template>
+                    </el-table-column>
+                    <el-table-column v-if="analysisData != null && analysisData.length > 0" label="二级因子" align="center">
+                        <template slot-scope="{row}">
+                            <div v-for="(item, index) in row.children">
+                                <div v-for="(item1, index) in item.children">
+                                    <div style=" margin-bottom: 10px;  letter-spacing: 3px;" v-for="(item2, index) in item1.children">
+                                        {{ item2.questionName }}-{{ item2.avgScore }}
+                                    </div>
+                                </div>
+                            </div>
+                        </template>
+                    </el-table-column>
+                </el-table>
+            </y-page-list-layout> -->
+
         </div>
-        <div class="origin_collect">
+        <!-- <div class="origin_collect">
             <span class="name"><i class="el-icon-s-data"></i>专家评分值</span>
             <el-table :data="originData" border style="width: 100%" stripe highlight-current-row>
                 <el-table-column prop="professorNo" label="专家编号" align="center">
@@ -30,11 +76,11 @@
                 <el-table-column prop="created" label="专家评分时间" align="center">
                 </el-table-column>
             </el-table>
-        </div>
+        </div> -->
         <div class="user_size">
-           <span v-for="(u,index) in userSize">
+            <span v-for="(u, index) in userSize">
                 <i class="el-icon-s-custom" style="color: green; font-size: xx-large;"></i>
-           </span>
+            </span>
         </div>
     </div>
 </template>
@@ -42,11 +88,13 @@
 <script>
 import YPageListLayout from '@/components/custom/YPageListLayout'
 import WebSocketClient from '../utils/WebSocketClient.js'
+import { getMergeCells } from '../utils/table.js'
 
 export default {
     name: 'Dashboard',
     components: {
         YPageListLayout,
+        getMergeCells
     },
     data() {
         return {
@@ -56,49 +104,66 @@ export default {
             itemName: "",
             scope: "",
             cycleName: "",
-            type:null,
+            type: null,
             messages: [],
             error: null,
             client: {
-                messages:[
-                    {data:"*"},
-                    {userSize:0}
+                messages: [
+                    { data: "*" },
+                    { userSize: 0 }
                 ]
             },
-            userSize:0
-
+            userSize: 0,
+            tableColumn: [
+                { prop: 'name', label: '名称' },
+                { prop: 'element', label: '定级因素' },
+                { prop: 'element1', label: '一级因子' },
+                { prop: 'element2', label: '二级因子' },
+            ],
         }
     },
-    watch:{
-        client:{
+    computed: {
+        // 获取所有单元格合并数据
+        spanArr() {
+            if (!this.tableColumn.length) return []
+            const mergeCols = ['name', 'element', 'element1', 'element2'] // 需要合并的列(字段)
+            return getMergeCells(this.analysisData, this.tableColumn, mergeCols)
+        }
+    },
+    watch: {
+        client: {
             deep: true,
-            handler(newValue,oldValue){
-                let data = newValue.messages[(newValue.messages.length)-1]
-                if (data != undefined){
-                    if (data.data !=null){
+            handler(newValue, oldValue) {
+                let data = newValue.messages[(newValue.messages.length) - 1]
+                if (data != undefined) {
+                    if (data.data != null) {
                         this.getAnalysisList();
                         this.getOriginList();
                     }
-                    if (data.userSize != undefined){
-                        this.userSize = (data.userSize)-1;
+                    if (data.userSize != undefined) {
+                        this.userSize = (data.userSize) - 1;
                     }
                 }
-               
+
             }
         }
     },
-    mounted(){
+    mounted() {
         this.init();
     },
     created() {
         this.getAnalysisList();
-        this.getOriginList();
+        // this.getOriginList();
     },
     methods: {
-        init(){
-            if (typeof(WebSocket) === "undefined"){
+        objectSpanMethod({ row, column, rowIndex, columnIndex }) {
+            return this.spanArr[rowIndex][columnIndex]
+        },
+
+        init() {
+            if (typeof (WebSocket) === "undefined") {
                 console.log("您的浏览器不支持socket")
-            }else{
+            } else {
                 // 创建 WebSocketClient 实例并连接到服务端 API 接口
                 this.client = new WebSocketClient('wss://kps.scdayou.com/ws/apo/ws')
                 this.client.connect()
@@ -106,16 +171,12 @@ export default {
         },
         getAnalysisList() {
             const that = this
-            this.$api.dashboard.getAnalysisList()
+            this.$api.dashboard.getMergeData()
                 .then((res) => {
-                    let tScore = res.data[0].avgScore
-                    if (tScore==null){
-                        that.analysisData = null;
-                    }else{
-                        that.analysisData = res.data
-                    }
-                    setTimeout(() => {
-                    }, 200)
+                    that.analysisData = res.data.analysisData;
+                    that.itemName = res.data.itemName;
+                    that.cycleName = res.data.cycleName;
+                    that.type = res.data.typeName;
                 })
                 .catch(() => {
                     that.listLoading = false
@@ -125,15 +186,11 @@ export default {
             const that = this
             this.$api.dashboard.getOriginList()
                 .then((res) => {
-                    that.itemName = res.data[0].itemName;
-                    that.scope = res.data[0].scope;
-                    that.cycleName = res.data[0].cycleName;
-                    that.type = res.data[0].type;
-                    if (res.data[0].score ==null){
+                    if (res.data[0].score == null) {
                         that.originData = null;
-                    }else{
+                    } else {
                         that.originData = res.data;
-                    
+
                     }
                     setTimeout(() => {
                     }, 200)
@@ -144,11 +201,11 @@ export default {
         },
     },
     destoryed() {
-    // 在组件销毁前断开 WebSocket 连接
+        // 在组件销毁前断开 WebSocket 连接
         this.client.disconnect()
         console.log("websocket 断开")
     },
-    beforeDestory(){
+    beforeDestory() {
         this.client.disconnect()
         console.log("websocket 断开")
     }
@@ -159,13 +216,12 @@ export default {
 .header {
     height: 80px;
     padding: 0;
-    background-color:  #ffffff;
+    background-color: #ffffff;
     vertical-align: middle;
     display: flex;
     position: relative;
     width: 100%;
 }
-
 .left {
     margin: 0;
     padding-left: 10px;
@@ -180,8 +236,7 @@ export default {
     overflow: auto; */
     top: -10;
     background-image: linear-gradient(to top, #ace0f9 0%, #ffffff 100%);
-    height: 1200px;
-    overflow:auto;
+    overflow: auto;
 }
 
 .item_name {
@@ -195,7 +250,7 @@ export default {
     letter-spacing: 1px;
     font-weight: bold;
     color: red;
-   
+
 }
 
 .title_info {
@@ -206,9 +261,10 @@ export default {
 }
 
 .analysis_collect {
-    margin-top: 2%;
+    margin-top: 1%;
     width: 70%;
     margin-left: 15%;
+    height: 1200px;
 }
 
 .origin_collect {
@@ -217,18 +273,19 @@ export default {
     margin-bottom: 2%;
     margin-left: 15%;
 }
-.name{
+
+.name {
     /* font-family: 'Courier New', Courier, monospace; */
     margin-bottom: 5px;
     letter-spacing: 1px;
     font-weight: bold;
     font-size: small;
 }
-.user_size{
-    position:absolute;
-    right:30px;
-    top:1%;
+
+.user_size {
+    position: absolute;
+    right: 30px;
+    top: 1%;
     height: 40px;
 }
-
 </style>

+ 17 - 95
src/pages/Document.vue

@@ -17,43 +17,22 @@
       </template>
       <parentTable v-loading="listLoading" :data="pageData.records" slot="table" style="width: 100%;"
         :showSummary="false">
-        <el-table-column type="expand">
-          <template slot-scope="{row}">
-            <parentTable style="font-size: 5px;color: #8c939d" v-loading="listLoading" inline :data="row.questions">
-              <el-table-column label="题目名称" align="center">
-                <template slot-scope="{row}">
-                  <span>{{ row.name }}</span>
-                </template>
-              </el-table-column>
-              <el-table-column label="创建时间" align="center" width="550">
-                <template slot-scope="{row}">
-                  <span>{{ row.created }}</span>
-                </template>
-              </el-table-column>
-            </parentTable>
-          </template>
-        </el-table-column>
-        <el-table-column label="项目名称" align="center">
+        <el-table-column label="项目名称" align="center" width="700">
           <template slot-scope="{row}">
             <span>{{ row.itemName }}</span>
           </template>
         </el-table-column>
-        <el-table-column label="类型" align="center" width="200">
+        <el-table-column label="类型" align="center" >
           <template slot-scope="{row}">
             <span>{{ row.type }}</span>
           </template>
         </el-table-column>
-        <el-table-column label="范围" align="center" width="200">
-          <template slot-scope="{row}">
-            <span>{{ row.scope }}</span>
-          </template>
-        </el-table-column>
-        <el-table-column label="创建时间" align="center" width="200">
+        <el-table-column label="创建时间" align="center" >
           <template slot-scope="{row}">
             <span>{{ row.created }}</span>
           </template>
         </el-table-column>
-        <el-table-column label="操作" align="center" width="200">
+        <el-table-column label="操作" align="center">
           <template slot-scope="{row}">
             <el-button type="success" @click="openCycleDiglog(row)">下发问卷</el-button>
           </template>
@@ -65,21 +44,11 @@
         <el-form-item label="项目名称" prop="itemName">
           <el-input v-model.trim="documentForm.itemName"></el-input>
         </el-form-item>
-        <el-form-item label="问卷类型" prop="type">
-          <el-select v-model="documentForm.type" placeholder="请选择类型" @change="selectScope">
-            <el-option v-for="(o, index) in options" :label="o.type" :value="o.type" />
-          </el-select>
-        </el-form-item>
-        <el-form-item label="范围" prop="scope">
-          <el-select v-model="documentForm.scope" placeholder="请选择范围">
-            <el-option v-for="(s, index) in scopes" :label="s" :value="s"></el-option>
+        <el-form-item label="问卷类型" prop="typeId">
+          <el-select v-model="documentForm.typeId" placeholder="请选择类型">
+            <el-option v-for="(o, index) in options" :label="o.label" :value="o.id" />
           </el-select>
         </el-form-item>
-        <el-form-item label="选择题目" prop="questions">
-          <el-transfer filterable :filter-method="filterMethod" filter-placeholder="请输入题目名称"
-            v-model="documentForm.questions" :data="questionList">
-          </el-transfer>
-        </el-form-item>
       </el-form>
       <span slot="footer" class="dialog-footer">
         <el-button @click="dialogVisible = false">取 消</el-button>
@@ -124,48 +93,7 @@ export default {
       dialogVisible: false,
       dialogVisibleX: false,
       documentForm: {},
-      options: [
-        {
-          type: "城镇土地综合定级因素表",
-          scope: ["名称", "定级因素", "一级因子", "二级因子"]
-        },
-        {
-          type: "城镇商服用地定级因素表",
-          scope: ["名称", "定级因素", "一级因子", "二级因子"]
-        },
-        {
-          type: "城镇住宅用地定级因素表",
-          scope: ["名称", "定级因素", "一级因子", "二级因子"]
-        },
-        {
-          type: "城镇工业用地定级因素表",
-          scope: ["名称", "定级因素", "一级因子", "二级因子"]
-        },
-        {
-          type: "城镇公共管理与公共服务用地定级因素表",
-          scope: ["名称", "定级因素", "一级因子", "二级因子"]
-        },
-        {
-          type: "集体建设用地定级因素表",
-          scope: ["名称", "定级因素", "一级因子", "二级因子"]
-        },
-        {
-          type: "耕地定级指标体系表",
-          scope: ["因素层", "因子层"]
-        },
-        {
-          type: "园地定级指标体系表",
-          scope: ["因素", "因子", "评价指标"]
-        },
-        {
-          type: "林地定级指标体系表",
-          scope: ["林地类型", "因素", "因子", "评价指标"]
-        },
-        {
-          type: "草地定级指标体系表",
-          scope: ["因素", "因子", "评价指标", "天然牧草地", "人工牧草地", "其他草地"]
-        },
-      ],
+      options: [],
       scopes: [],
       questionList: [],
       filterMethod(query, item) {
@@ -178,12 +106,6 @@ export default {
         type: [
           { required: true, message: '请选择问卷类型', trigger: 'blur' }
         ],
-        scope: [
-          { required: true, message: '请选择问卷范围', trigger: 'blur' }
-        ],
-        questions: [
-          { required: true, message: '请选择题目', trigger: 'blur' }
-        ]
       },
       cycleNames: ['第一轮', '第二轮', '第三轮', '第四轮', '第五轮', '第六轮', '第七轮', '第八轮', '第九轮', '第十轮'],
       cycleForm: {
@@ -194,8 +116,16 @@ export default {
   },
   created() {
     this.getList();
+    this.getQuestionType();
   },
   methods: {
+    getQuestionType(){
+        this.$api.question.simpleAll().then(res=>{
+          if (res.code === 200){
+              this.options = res.data;
+          }
+        })
+    },
     addCycle() {
       if (this.cycleForm.documentId) {
         if (this.cycleForm.cycleName) {
@@ -233,15 +163,7 @@ export default {
         }
       })
     },
-    selectScope(value) {
-      const options = this.options
-      for (let i in options) {
-        if (options[i].type === value) {
-          this.scopes = options[i].scope
-          break
-        }
-      }
-    },
+    
     resetSearch() {
       this.$router.push({ query: {} });
       this.listQuery = {

+ 255 - 81
src/pages/Question.vue

@@ -1,55 +1,76 @@
 <template>
   <div class="app-container">
-    <!-- <div class="title-container">
-        <breadcrumb id="breadcrumb-container" class="breadcrumb-container"/>
-      </div> -->
-
-    <y-page-list-layout :page-list="pageData" :page-para="listQuery" :getList="getList">
-      <template slot="left">
-        <el-button type="primary" round style="float: left" @click="dialogVisible = true">新增</el-button>
-        <el-input v-model="listQuery.itemName" placeholder="请输题目名称" clearable
-          style="margin-left: 20px;width: 320px;float: left;">
-        </el-input>
-        <el-button class="filter-item" style="margin-left: 10px;float: left;" type="primary" @click="searchList" round>搜索
-        </el-button>
-        <el-button class="filter-item" style="float: left;" round type="warning" @click="resetSearch()">重置
-        </el-button>
-      </template>
-      <parentTable v-loading="listLoading" :data="pageData.records" slot="table" style="width: 100%;"
-        :showSummary="false">
-        <el-table-column label="题目名称" align="center">
-          <template slot-scope="{row}">
-            <span>{{ row.name }}</span>
-          </template>
-        </el-table-column>
-        <el-table-column label="创建时间" align="center">
-          <template slot-scope="{row}">
-            <span>{{ row.created }}</span>
-          </template>
-        </el-table-column>
-      </parentTable>
-    </y-page-list-layout>
-    <el-dialog title="新增题目" :visible.sync="dialogVisible" width="30%" center>
-      <el-form :model="questionForm" label-width="100px" class="demo-ruleForm">
-        <el-form-item label="题目名称" prop="name">
-          <el-input v-model.trim="questionForm.name"></el-input>
+    <div class="content">
+      <el-tabs addable @edit="handleTabsEdit" v-model="editableTabsValue" @tab-click="clickTab" type="border-card">
+        <el-tab-pane  :key="item.name" v-for="(item, index) in questionTypeTree" :label="item.title" :name="item.name">
+          <el-card shadow="always" class="tree">
+            <el-tree :data="item.questionTree" show-checkbox node-key="id" default-expand-all
+              :expand-on-click-node="false">
+              <span class="custom-tree-node" slot-scope="{ node, data }">
+                <span @click="show(node)">{{ node.label }}</span>
+                <span>
+                  <el-button type="text" size="mini" @click="() => append(node, data)">
+                    添加
+                  </el-button>
+                  <el-button type="text" size="mini" @click="() => remove(node, data)">
+                    删除
+                  </el-button>
+                </span>
+              </span>
+            </el-tree>
+          </el-card>
+        </el-tab-pane>
+      </el-tabs>
+      <div class="panel">
+          <el-card shadow="always" >
+            <el-form label-position="left" label-width="120px" :model="questionForm" class="form">
+              <el-form-item label="上级评价项:">
+                <el-input v-model="questionForm.parentLabel" readonly disabled></el-input>
+              </el-form-item>
+              <el-form-item label="范围:">
+                <el-select v-model="questionForm.scope" placeholder="请选择评价项所属范围" style="width:100%">
+                  <el-option label="名称" value="名称"></el-option>
+                  <el-option label="定级因素" value="定级因素"></el-option>
+                  <el-option label="一级因子" value="一级因子"></el-option>
+                  <el-option label="二级因子" value="二级因子"></el-option>
+                </el-select>
+              </el-form-item>
+              <el-form-item label="评价项:">
+                <el-input v-model="questionForm.label"></el-input>
+              </el-form-item>
+              <el-form-item label="分值范围:">
+                <el-col :span="11">
+                  <el-input v-model.number="questionForm.minScore"></el-input>
+                </el-col>
+                <el-col class="line" :span="2">--</el-col>
+                <el-col :span="11">
+                  <el-input v-model.number="questionForm.maxScore"></el-input>
+                </el-col>
+              </el-form-item>
+              <el-form-item>
+                <el-button type="success" @click="save">保存</el-button>
+              </el-form-item>
+            </el-form>
+          </el-card>
+        </div>
+    </div>
+    <el-dialog title="新增类型" :visible.sync="dialogVisible" width="20%" center>
+      <el-form label-width="100px">
+        <el-form-item label="类型名称">
+          <el-input v-model="typeName"></el-input>
         </el-form-item>
       </el-form>
       <span slot="footer" class="dialog-footer">
         <el-button @click="dialogVisible = false">取 消</el-button>
-        <el-button type="primary" @click="addQuestion">确 定</el-button>
+        <el-button type="primary" @click="addType">确 定</el-button>
       </span>
     </el-dialog>
   </div>
 </template>
 <script>
-import YPageListLayout from '@/components/custom/YPageListLayout'
-
 export default {
   name: 'Question',
-  components: {
-    YPageListLayout,
-  },
+
   data() {
     return {
       pageData: {},
@@ -63,49 +84,74 @@ export default {
       listQueryKey: 'keyword',
       dialogVisible: false,
       questionForm: {
-        name: null
-      }
+        parentId: null,
+        parentLabel: null,
+        id: null,
+        label: null,
+        scope: null,
+        minScore: null,
+        maxScore: null
+      },
+      dialogVisible: false,
+      typeName: null,
+      questionTypeTree: [
+ 
+      ],
+      editableTabsValue:null
     }
   },
   created() {
-    this.getList();
+    //this.getTableTypes();
+    this.getTree();
   },
   methods: {
-    resetSearch() {
-      this.$router.push({ query: {} });
-      this.listQuery = {
-        current: 1,
-        size: 10,
-        descs: 'id',
-      }
-      this.getList()
+    clickTab(e){
+        this.editableTabsValue = e.name;
     },
-    searchList() {
-      // 重置分页
-      this.listQuery.page = 1
-      this.listQuery.size = 10
-      this.getList()
+    addType() {
+      let typeName = this.typeName;
+      if (!typeName) {
+        this.$notify.error({
+          title: '错误',
+          message: '类型名称不能为空'
+        });
+        return;
+      }
+      this.$api.question.add({ "label": typeName, "scope": "类型" }).then(res => {
+        if (res.code === 200) {
+          // this.questionTypeTree.push({
+          //   title: typeName,
+          //   name: typeName,
+          //   content: typeName
+          // });
+          this.editableTabsValue = res.data+"";
+        }
+      })
+      this.dialogVisible = false;
+      this.getTree();
     },
-    getList() {
-      const that = this
-      that.listLoading = true
-      const key = {}
-      key[that.listQueryKey] = that.listQuery.description
-      console.log(this.$api)
-      this.$api.question.list(Object.assign({}, that.listQuery, key))
-        .then((res) => {
-          that.pageData = res.data
-          setTimeout(() => {
-            that.listLoading = false
-          }, 200)
-        })
-        .catch(() => {
-          that.listLoading = false
+    dele(id) {
+      if (id) {
+        this.$api.question.dele(id).then(res => {
+          if (res.code === 200) {
+            this.$notify({
+              title: '成功',
+              message: '删除成功',
+              type: 'success'
+            });
+            this.getTree();
+          } else {
+            this.$notify.error({
+              title: '错误',
+              message: '删除失败'
+            });
+          }
         })
+      }
     },
-    addQuestion() {
-      let questionName = this.questionForm.name;
-      if (questionName) {
+    save() {
+      let questionLabel = this.questionForm.label;
+      if (questionLabel) {
         this.$api.question.add(this.questionForm).then(res => {
           if (res.code === 200) {
             this.$notify({
@@ -113,28 +159,156 @@ export default {
               message: '新增成功',
               type: 'success'
             });
-            this.questionName = null;
-            this.dialogVisible = false;
-            this.getList();
+            this.getTree();
           } else {
             this.$notify.error({
               title: '错误',
               message: '新增失败'
             });
           }
-          this.questionName = null;
-          this.dialogVisible = false;
-          this.getList();
         })
       } else {
         this.$notify.error({
           title: '错误',
-          message: '题目名称不能为空'
+          message: '评价项名称不能为空'
         });
       }
-    }
+    },
+    show(node) {
+      this.questionForm.id = node.data.id;
+      this.questionForm.scope = node.data.scope;
+      this.questionForm.label = node.data.label;
+      this.questionForm.minScore = node.data.minScore;
+      this.questionForm.maxScore = node.data.maxScore;
+      this.questionForm.parentLabel = node.data.parentLabel;
+      this.questionForm.parentId = node.data.parentId;
+    },
+    handleTabsEdit(targetName, action) {
+      if (action === 'add') {
+        this.dialogVisible = true;
+      }
+      // if (action === 'remove') {
+      //   let tabs = this.questionTypeTree;
+      //   let activeName = this.editableTabsValue;
+      //   if (activeName === targetName) {
+      //     tabs.forEach((tab, index) => {
+      //       if (tab.name === targetName) {
+      //         let nextTab = tabs[index + 1] || tabs[index - 1];
+      //         if (nextTab) {
+      //           activeName = nextTab.name;
+      //         }
+      //       }
+      //     });
+      //   }
+
+      //   this.editableTabsValue = activeName;
+      //   this.questionTypeTree = tabs.filter(tab => tab.name !== targetName);
+      // }
+    },
+    append(node, data) {
+      const newChild = { id: null, label: '待定义', parentLabel: node.data.label, parentId: node.data.id, children: [] };
+      console.log(node)
+      if (node.level === 5){
+        this.$notify.error({
+              title: '警告',
+              message: '二级因子下暂不支持添加下级'
+            });
+            return ;
+      }
+      if (!data.children) {
+        this.$set(data, 'children', []);
+      }
+      data.children.push(newChild);
+    },
+
+    remove(node, data) {
+      this.$confirm('请确认是否删除此评价项', '提示', {
+        confirmButtonText: '确定',
+        cancelButtonText: '取消',
+        type: 'warning'
+      }).then(() => {
+        const parent = node.parent;
+        console.log(node.data)
+        if (node.data.children!=null) {
+          this.$confirm('此评价项下还存在子评价项,若删除子评价项也将删除', '提示', {
+            confirmButtonText: '确定',
+            cancelButtonText: '取消',
+            type: 'warning'
+          }).then(() => {
+            const children = parent.data.children || parent.data;
+            const index = children.findIndex(d => d.id === data.id);
+            children.splice(index, 1);
+            this.dele(node.data.id)
+          })
+        }else {
+          const children = parent.data.children || parent.data;
+            const index = children.findIndex(d => d.id === data.id);
+            children.splice(index, 1);
+            this.dele(node.data.id)
+        }
+
+      })
+
+    },
+    getTree() {
+      const that = this
+      that.questionTypeTree = [];
+      that.listLoading = true
+      this.$api.question.tree()
+        .then((res) => {
+          for (let i in res.data) {
+            let parentItem = {label:res.data[i].label,id:res.data[i].id,scope:res.data[i].scope,children:res.data[i].children}
+            let item = {
+              title: res.data[i].label,
+              name: res.data[i].id + "",
+              content: null,
+              questionTree: [parentItem]
+            }
+            that.questionTypeTree.push(item)
+          }
+          if (that.editableTabsValue==="0"){
+            that.editableTabsValue = that.questionTypeTree[0].name
+          }
+          setTimeout(() => {
+            that.listLoading = false
+          }, 200)
+        })
+        .catch(() => {
+          that.listLoading = false
+        })
+    },
   },
 }
 </script>
+<style scoped>
+.tree {
+  width: 35%;
+}
+
+.panel {
+  width: 45%;
+  height: 800px;
+  flex: 1;
+  position: absolute;
+  top: 15.2%;
+  left: 50%;
+}
+
+.custom-tree-node {
+  flex: 1;
+  display: flex;
+  align-items: center;
+  justify-content: space-between;
+  font-size: 14px;
+  padding-right: 8px;
+}
 
+.form {
+  width: 55%;
+  margin-left: 23%;
+  margin-top: 10%;
+  font-size: medium;
+  font-weight: bold;
+}
+</style>
   

+ 50 - 0
src/utils/table.js

@@ -0,0 +1,50 @@
+/**
+ * 分析每一列,找出所有【列】可合并(数据相同)的单元格
+ * @param {Array} tableData 表数据
+ * @param {Array} tableColumn 表字段/表头
+ * @param {Array} mergeCols 指定合并哪些列(字段)
+ * @returns
+ */
+export const getMergeCells = (tableData = [], tableColumn = [], mergeCols = []) => {
+    const fields = tableColumn?.map(v => v.prop)
+    const array = []
+    
+    if (!tableData?.length || !tableColumn?.length || !mergeCols?.length) return
+   
+    // 倒叙遍历行(方便统计合并列单元格数至最上方,避免表格塌陷)
+    for (let row = tableData.length - 1; row >= 0; row--) {
+      array[row] = []
+      for (let col = 0; col < fields.length; col++) {
+         // 1.最后一行单元格不合并(初始无可对比数据)
+         // 2.不在指定列(mergeCols)的单元格不合并
+         // 3.空值不合并
+        if (row === tableData.length - 1 || !mergeCols.includes(fields[col]) || !tableData[row][fields[col]]) {
+          array[row][col] = [1, 1]
+          continue
+        }
+  
+        // 4.数据相同但所属父级不一致的单元格不合并
+        const parentFields = mergeCols.slice(0, col) // 在指定合并列中找出所有父级
+        if (mergeCols.includes(fields[col]) && parentFields?.includes(fields[col - 1])) {
+          const currentParents = parentFields.map(field => tableData[row][field]) // 当前单元格所有父级
+          const nextRowParents = parentFields.map(field => tableData[row + 1][field]) // 下一行单元格所有父级
+          if (currentParents?.toString() !== nextRowParents?.toString()) {
+            array[row][col] = [1, 1]
+            continue
+          }
+        }
+  
+        // 5.合并相同数据的单元格
+        if (tableData[row][fields[col]] === tableData[row + 1][fields[col]]) {
+          const beforeCell = array[row + 1][col]
+          array[row][col] = [1 + beforeCell[0], 1]
+          beforeCell[0] = 0
+          beforeCell[1] = 0
+        } else {
+          array[row][col] = [1, 1] // 否则不合并
+        }
+      }
+    }
+    // console.log(array, 'array')
+    return array
+  }