<template>
  <div class="code-wrap flex">
    <div class="left">
      <codemirror v-model="codeVal"
                  :options="cmOption"
                  class="code-mirror"
                  @ready="ready"
                  ref="codemirror"></codemirror>
      <div v-if="isSubmit"
           class="code-mask"></div>
      <div class="btns">
        <span class="el-icon-delete del"
              @click="clearCode"></span>
        <el-button v-if="modelIsShow"
                   class="btn"
                   type="warning"
                   @click="importModel">导入模型</el-button>
        <el-button class="run btn"
                   type="primary"
                   @click="runCode(false)"
                   :disabled="runEnable">运行</el-button>
      </div>
    </div>
    <div class="line"></div>
    <div class="code-right answer">
      <p :class="['text-wrapper', 'pic-num' + picSrcList.length]">{{ runResult }}</p>
      <div :class="['pic-wrap', {wrong: isError === 0}]"
           v-if="picSrcList.length">
        <div class="pic-item"
             v-for="(img, i) in picSrcList"
             :key="i">
          <el-image class="pic"
                    :src="img"
                    :preview-src-list="picSrcList">
          </el-image>
          <el-button class="download-btn btn"
                     type="primary"
                     size="mini"
                     @click="downloadPic(i)">下载图片</el-button>
          <a :ref="'picLink' + i"
             style="display: none;"
             download="运行结果.png"
             :href="img">下载图片</a>
        </div>
      </div>
      <div class="result-right t-color"
           v-show="isError">
        <img :src="require(`@/assets/images/system/${$config.defaultSystem}/yes.png`)"
             alt />运行成功
        <el-button class="tips-btn"
                   @click="exportResult">导出结果</el-button>
      </div>
      <div class="result-wrong"
           v-show="isError === 0">
        <img src="@/assets/images/error.png"
             alt />
        第{{errLine}}行出现错误
        <el-button class="tips-btn"
                   @click="getTips"
                   v-show="showTips">提示</el-button>
        <el-dialog title="答案提示"
                   center
                   :close-on-click-modal="false"
                   :visible.sync="tipsVisible">
          <el-tabs>
            <el-tab-pane label="参考答案">
              <div :class="['answer-wrap', {client: !fromManager}]"
                   v-html="answer"></div>
            </el-tab-pane>
          </el-tabs>
        </el-dialog>
      </div>
    </div>

    <!-- 导入模型 -->
    <el-dialog title="请选择要导入的模型"
               :visible.sync="modelVisible"
               width="400px"
               :close-on-click-modal="false"
               custom-class="model-dia">
      <el-select class="w-100"
                 v-model="modelType"
                 size="mini">
        <el-option v-for="item in modelOp"
                   :key="item.id"
                   :label="item.name"
                   :value="item.id">
        </el-option>
      </el-select>
      <!-- <el-radio-group v-model="modelType"
                      @change="changeModelType">
        <div style="margin-bottom: 10px;">
          <el-radio :label="0">本校模型</el-radio>
        </div>
        <div>
          <el-radio :label="1">内置模型</el-radio>
        </div>
      </el-radio-group> -->
      <!-- <el-divider></el-divider> -->
      <div class="model-wrap">
        <el-tree v-if="!modelType"
                 :data="schoolModel"
                 ref="tree"
                 default-expand-all
                 @check-change="treeCheckChange"
                 show-checkbox
                 :check-strictly="true"
                 node-key="id"
                 :props="{children: 'children', label: 'categoryName', isLeaf: 'leaf'}"
                 v-loading="modelLoading">
        </el-tree>
        <el-tree v-if="modelType"
                 :data="systemModel"
                 ref="tree"
                 default-expand-all
                 @check-change="treeCheckChange"
                 show-checkbox
                 :check-strictly="true"
                 node-key="id"
                 :props="{children: 'children', label: 'categoryName', isLeaf: 'leaf'}"
                 v-loading="modelLoading">
        </el-tree>
      </div>
      <span slot="footer"
            class="dialog-footer">
        <el-button size="small"
                   @click="modelVisible = false">取消</el-button>
        <el-button size="small"
                   type="primary"
                   @click="submit">导入</el-button>
      </span>
    </el-dialog>
  </div>
</template>

<script>
import Cookie from 'js-cookie'
import newmain from "../util/newMain";
import { codemirror } from "vue-codemirror";
import "codemirror/theme/ambiance.css"; // 这里引入的是主题样式
import "codemirror/mode/javascript/javascript.js";
import "codemirror/mode/python/python.js";
import "codemirror/lib/codemirror.css";
// require active-line.js
import "codemirror/addon/selection/active-line.js";
// styleSelectedText
import "codemirror/addon/selection/mark-selection.js";
// hint
import "codemirror/addon/hint/show-hint.js";
import "codemirror/addon/hint/sql-hint.js";
import "codemirror/addon/hint/show-hint.css";
import "codemirror/addon/hint/javascript-hint.js";
// highlightSelectionMatches
import "codemirror/addon/scroll/annotatescrollbar.js";
import "codemirror/addon/search/matchesonscrollbar.js";
import "codemirror/addon/search/match-highlighter.js";
// keyMap
import "codemirror/mode/clike/clike.js";
import "codemirror/mode/sql/sql.js";
import "codemirror/addon/edit/matchbrackets.js";
import "codemirror/addon/comment/comment.js";
import "codemirror/addon/dialog/dialog.js";
import "codemirror/addon/dialog/dialog.css";
import "codemirror/addon/search/searchcursor.js";
import "codemirror/addon/search/search.js";
import "codemirror/keymap/sublime.js";
// foldGutter
import "codemirror/addon/fold/foldgutter.css";
import "codemirror/addon/fold/brace-fold.js";
import "codemirror/addon/fold/comment-fold.js";
import "codemirror/addon/fold/foldcode.js";
import "codemirror/addon/fold/foldgutter.js";
import "codemirror/addon/fold/indent-fold.js";
import "codemirror/addon/fold/markdown-fold.js";
import "codemirror/addon/fold/xml-fold.js";
// 编辑的主题文件
import "codemirror/theme/monokai.css";
import "codemirror/theme/base16-light.css";
import { Loading } from 'element-ui';
import axios from 'axios';
import util from '@/util'
import config from '@/config'
const CANCEL_TOKEN = axios.CancelToken // 用于input中中断请求
export default {
  props: ['judgmentId', 'code', 'codeId', 'projectId', 'systemId', 'retResult', 'modelIsShow'],
  data () {
    return {
      token: Cookie.get('admin-token'),
      assessmentId: Cookie.get('admin-assessmentId'), // 考核id
      fromManager: Cookie.get('admin-fromManager'), // 是否是从教师端进入
      courseId: Cookie.get('admin-courseId'), // 课程id
      showTips: false, // 显示隐藏提示按钮
      answer: '', // 正确答案
      codeVal: this.code,
      runResult: '', // 运行结果
      isError: false, // 运行正确与否
      errLine: '', // 错误代码的位置
      isSubmit: Cookie.get('admin-isSubmit') == 'true' ? true : false, // 是否提交的标识
      runEnable: false, // 是否禁用运行按钮
      tipsVisible: false, // 答案提示弹框显示标识
      picSrcList: [],
      loadIns: null, // loading实例
      cmOption: {
        scrollbarStyle: "native",
        tabSize: 2, // tab
        styleActiveLine: true, // 高亮选中行
        lineNumbers: true, // 显示行号
        styleSelectedText: true,
        line: true,
        foldGutter: true, // 块槽
        gutters: ["CodeMirror-linenumbers", "CodeMirror-foldgutter"],
        highlightSelectionMatches: { showToken: /\w/, annotateScrollbar: true }, // 可以启用该选项来突出显示当前选中的内容的所有实例
        mode: 'python',
        lineWrapping: true, //代码折叠
        autoCloseTags: true,// 自动闭合标签
        autoCloseBrackets: true,// 自动闭合括号
        // hint.js options
        hintOptions: {
          // 当匹配只有一项的时候是否自动补全
          completeSingle: false
        },
        // 快捷键 可提供三种模式 sublime、emacs、vim
        keyMap: "sublime",
        matchBrackets: true,
        showCursorWhenSelecting: true,
        theme: "monokai" // 主题
      },
      inputTextReg: /^((?!#).*?(,|\s?|\(|\[|\{)+)?input(?!\w)\(['|"]([\s\S]+?)['|"]\)/m, // 匹配input()
      requestList: [], // 有input的情况下，保存每个axios的对象，用于中断请求
      sourceCode: '', // 把input替换成exit函数后的代码
      requestTimer: null, // 用于中断请求的定时器
      modelVisible: false,
      modelLoading: false,
      modelImporting: false,
      schoolModel: [],
      systemModel: [],
      modelType: 1,
      modelOp: [
        {
          id: 0,
          name: '本校模型'
        },
        {
          id: 1,
          name: '内置模型'
        },
      ]
    };
  },
  components: {
    codemirror
  },
  watch: {
    codeVal (val) {
      this.$emit("update:code", val)
    }
  },
  mounted () {
    if (!this.assessmentId) this.showTips = true
    //兄弟组件传值
    newmain.$on("isSubmit", isSubmit => {
      this.isSubmit = isSubmit
    })
    this.dragSide()
  },

  methods: {
    // 页面加载完后重置编辑框大小
    ready () {
      const code = this.$refs.codemirror.codemirror
      code.setSize("auto", "calc(100vh - 167px)");

      // 查询粘贴状态
      this.$post(`${this.api.whetherCanPaste}?systemId=${this.systemId}`).then(res => {
        // 监听改变前的钩子，禁止粘贴(从学生端进入，并且老师端禁用了粘贴功能)
        res.data == 'false' && !this.fromManager && code.on('beforeChange', (istance, change) => {
          change.origin === 'paste' && change.cancel()
        })
      }).catch(err => { })
      code.on('keypress', function () {
        // 显示智能提示
        code.showHint()
      });
    },
    // 清屏
    clearCode () {
      this.codeVal = ''
      this.isError = ''
      this.runResult = ''
      this.$emit('update:codeId', '') // 更新coddeId
      this.$emit('update:answer', '') // 更新运行结果
      this.$emit('update:retResult', '') // 更新返回结果
    },
    // 导入模型
    importModel () {
      this.modelLoading = true
      this.modelVisible = true
      // 查询源模型分类
      this.$post(`${this.api.modelClassListByStudent}?systemId=${this.systemId}&founder=1`).then(res => {
        const promises = []
        const addType = (list, system) => {
          list.map(e => {
            e.disabled = true
            // 用promise储存以添加完后更新数据
            promises.push(new Promise((resolve, reject) => {
              // 系统内置跟本校的模型接口不一样
              this.$post(this.api[system ? 'studentModelListBySystem' : 'studentModelList'], {
                categoryId: e.id,
                pageNum: 1,
                pageSize: 10000,
                systemId: +this.systemId,
                founder: 1
              }).then(({ data }) => {
                let { records } = data
                records = records.filter(e => !e.isOpen) // 禁用的不显示
                records.map(n => {
                  n.categoryName = n.modelName
                })
                e.children = [...e.children, ...records]
                resolve()
              }).catch(res => {
                reject()
              })
            }))
            e.children && e.children.length && addType(e.children, system)
          })
        }
        addType(res.schoolClassification)
        addType(res.systemBuiltInClassification, 1)
        Promise.all(promises).then(_ => {
          this.schoolModel = res.schoolClassification
          this.systemModel = res.systemBuiltInClassification
          this.modelLoading = false
        }).catch(res => { })
      }).catch(res => { })
    },
    // 分类类型选择回调
    changeModelType (val) {
      // this.getType()
    },
    // 多选框选择回调
    treeCheckChange (data, checked, indeterminate) {
      const checkKey = this.$refs.tree.getCheckedKeys()
      // 这里要求单选，所以把多选框改成单选，选择了一个节点后把之前选的都给取消选择，再选中刚勾选的
      if (checkKey.length > 1 && checked) {
        this.$refs.tree.setCheckedNodes(checkKey, false)
        this.$refs.tree.setChecked(data.id, true)
      }
    },
    // 导入模型提交
    submit () {
      if (this.modelImporting) return false
      const id = this.$refs.tree.getCheckedKeys()
      if (!id.length) return this.$message.error('请选择模型！')
      this.modelImporting = true
      this.$post(`${this.api.referenceFindById}?id=${id[0]}`).then(res => {
        this.codeVal += (this.codeVal ? '\n\n' : '') + res.data.modelDemo // 空两行插入
        this.modelVisible = false
        this.$nextTick(() => {
          const codemirror = this.$refs.codemirror.codemirror
          codemirror.focus()
          codemirror.setCursor(codemirror.lineCount(), 0)
          setTimeout(() => {
            this.modelImporting = false
          }, 2000)
        })
      }).catch(res => { })
    },
    /**
     * python代码里如果有input函数的话，是做了单独的处理的，原理是先把所有input函数都替换成exit函数，再在exit函数里加上特定标识，再通过接口传给后端去执行
     * 因为exit函数是跟input有类似的效果，就是都会产生阻塞，所以python引擎一旦遇到exit，进程就会被停止，然后返回exit函数里面的值，而这个值，就是上面说的特定标识加上原本这个input函数里的值
     * 然后就可以通过这个返回的值来提示给用户，让用户继续输入
     * 下面这个函数就是递归执行这个input输入过程的函数
     */
    confirmInput (msg) {
      const receiveResult = msg.replace('validing:', '')
      this.runResult += receiveResult
      this.$prompt(receiveResult, '提示', {
        confirmButtonText: "确定"
      }).then(({ value }) => {
        this.runResult += `${value}\n`
        // 把exit函数替换成用户输入的值
        this.sourceCode = this.sourceCode.replace(`exit('validing:${receiveResult.replace(/[\r\n]*/g, '')}')`, _ => {
          return `'${value}'`
        })
        this.sourceCode = this.sourceCode.replace(`exit("validing:${receiveResult.replace(/[\r\n]*/g, '')}")`, _ => {
          return `'${value}'`
        })
        clearTimeout(this.requestTimer)

        // 一秒钟后还没请求成功的话，就直接中断请求，因为一般是陷入死循环了才会需要这么长时间的，中断了后就再次执行AnswerTips（运行按钮的执行函数）函数，再次进入循环
        this.requestTimer = setTimeout(() => {
          this.requestList.map(n => n('interrupt'))
        }, 1000)

        axios.post(config.host + this.api.runPythonCode, {
          code: this.sourceCode,
          bcId: this.judgmentId,
          cid: this.courseId,
          projectId: this.projectId
        }, {
          headers: {
            token: this.token
          },
          cancelToken: new CANCEL_TOKEN(c => { //强行中断请求要用到的，记录请求信息
            this.requestList.push(c)
          })
        }).then(response => {
          let res = response.data
          const data = res.code
          const result = data.runResult
          if (result.includes('File ')) {
            this.runResult = result
            this.errLine = parseInt(result.substring(result.indexOf('line') + 4, result.length))
            this.isError = data.retResult
          } else if (result.includes('validing:')) {
            this.isError = 1
            this.confirmInput(result)
          } else if (data.retResult) {
            this.isError = 1
            this.runResult += result
          }
          this.$emit('update:codeId', res.codeId) // 更新coddeId
          this.$emit('update:answer', this.runResult) // 更新运行结果
          this.$emit('update:retResult', data.retResult) // 更新返回结果
        }).catch(e => {
          if (e && e.message == 'interrupt') {
            this.runCode(true)
            this.requestList = []
          }
        })

      }).catch(err => { })
    },
    // 运行代码
    runCode (isWhile) { // isWhile为true表示代码里有while循环，右边的运行结果需要拼接展示，而不是直接覆盖
      if (!this.isSubmit) {
        let code = this.codeVal
        if (!code) {
          this.$message({
            message: "警告哦，内容为空不可运行",
            type: "warning"
          })
        } else {
          const { inputTextReg } = this
          const inputFuncReg = /input\(['|"]/g
          const bcId = this.judgmentId
          const cid = this.courseId
          const projectId = this.projectId
          // 该正则是验证代码里是否有input，如果有，就要另外做处理，而不是直接传给后端执行
          if (inputTextReg.test(code)) {
            code = code.replace(inputTextReg, val => {
              return val.replace(/\\n/g, "")
            })
            this.codeVal = code

            // 把input函数替换成exit函数，加上"validing:"作为特定标识，好方便后面的识别
            code = code.replace(inputFuncReg, val => {
              return `exit(${val[val.length - 1]}validing:`
            })
            this.sourceCode = code

            if (!isWhile) this.runResult = ''
            this.$post(this.api.runPythonCode, {
              code,
              bcId,
              cid,
              projectId
            }).then(res => {
              const data = res.code
              const result = data.runResult
              if (result.includes('File ')) {
                if (isWhile) {
                  this.runResult += result
                } else {
                  this.runResult = result
                }
                this.errLine = parseInt(result.substring(result.indexOf('line') + 4, result.length))
                this.isError = data.retResult
              } else if (result.includes('validing:')) {
                this.isError = 1
                this.confirmInput(result)
              }
              this.$emit('update:codeId', res.codeId) // 更新coddeId
              this.$emit('update:answer', this.runResult) // 更新运行结果
              this.$emit('update:retResult', data.retResult) // 更新返回结果
            }).catch(err => {
              this.loadIns.close()
            })
          } else {
            this.loadIns = Loading.service({
              text: '代码运行中请稍等几分钟',
              background: 'transparent'
            })
            // 如果代码有savefig(python里的保存图片的方法),则要把savefig里的图片名字加上个时间戳，防止后台保存图片到服务器的时候图片名字重复导致覆盖
            code = code.replace(/\.savefig\(([\u4e00-\u9fa5\w]*?['"])/mg, str => {
              return str + Date.now()
            })
            // 把代码传给后端，在后端运行Python代码
            this.$post(this.api.runPythonCode, {
              code,
              bcId,
              cid,
              projectId
            }).then(res => {
              const data = res.code
              const photo = data.photoUrl
              const result = data.runResult || ''
              this.$emit('cache') // 每次运行代码都要把代码传给后端做缓存
              this.loadIns.close()
              this.picSrcList = []
              if (photo) this.picSrcList = photo.split(',')
              this.$emit('update:codeId', res.codeId) // 更新coddeId
              this.$emit('update:answer', result) // 更新运行结果
              this.$emit('update:retResult', data.retResult) // 更新返回结果
              let imgList = ''
              let firtImg = ''
              try {
                imgList = eval(result)
              } catch (error) { }
              if (imgList && imgList.length) firtImg = imgList[0]
              // 如果是下载图片的代码，则要显示图片和运行结果，不用显示对错，换成显示下载图片
              if (photo) {
                this.isError = data.retResult // 对错隐藏
                const text = result.replace(photo, '') // 结果里包含了图片路径，所以要把图片路径给去掉
                this.runResult = text
                this.picSrcList = photo.split(',')
                this.errLine = parseInt(result.substring(result.indexOf("line") + 4, result.length))
              } else if (imgList instanceof Array && imgList.length && typeof firtImg === 'string' && (firtImg.includes('.jpg') || firtImg.includes('.png') || firtImg.includes('img'))) {
                /**
                 * 这段是为要下载图片的项目案例写的，后端会返回图片名称的数组，前端负责循环这个数组，然后下载下来
                 * 只有该系统有这段代码，因为其他7个系统没有下载图片的项目，后续如果加了，直接把这段代码复制过去即可
                 */
                imgList.map((n, i) => {
                  util.downloadFile(`${i + 1}.jpg`, n)
                })
                this.isError = 0
                this.runResult = '下载完成'
              } else {
                this.isError = data.retResult
                this.runResult = result
                this.errLine = parseInt(result.substring(result.indexOf("line") + 4, result.length))
              }
            }).catch(res => {
              this.isError = false
              this.runResult = ''
              this.picSrcList = []
              this.loadIns.close()
              res.status == 500 && this.$message.error('检测到代码里有非法代码，请检查是否有调用系统命令。')
            })
          }
        }
      } else {
        this.runEnable = true
        this.$message({
          message: "警告哦，已提交不可再运行",
          type: "warning"
        })
      }
    },
    // 下载图片
    downloadPic (i) {
      this.$refs['picLink' + i][0].click()
    },
    // 导出运行结果
    exportResult () {
      var FileSaver = require('file-saver');
      var blob = new Blob([this.runResult], { type: "text/plain;charset=utf-8" });
      FileSaver.saveAs(blob, 'result.csv')
    },
    // 获取正式答案
    getTips () {
      this.tipsVisible = true
      this.$get(this.api.queryBcJudgmentByBcId, {
        bcId: this.judgmentId
      }).then(res => {
        this.answer = res.experimentCode
      }).catch(err => { })
    },
    // 中间拖拽
    dragSide () {
      const left = document.querySelector('.left')
      const line = document.querySelector('.line')
      const right = document.querySelector('.code-right')
      // 移动区域鼠标移入事件
      line.onmousedown = e => {
        // 外面容器宽度
        const width = document.querySelector('.code-wrap').clientWidth
        document.onmousemove = e => {
          let x = e.clientX
          // 移动区间的范围
          if (x >= width * .1 && x <= width * .9) {
            line.style.left = x + 'px'
            left.style.width = x + 'px'
            right.style.width = document.querySelector('.code-wrap').clientWidth - (x + 5) + 'px'
          }
        }
        document.onmouseup = () => {
          document.onmousemove = null
          document.onmouseup = null
        }
      }
    }
  }
};
</script>

<style lang="scss" scoped>
.code-wrap {
  position: relative;
  .code-mirror {
    line-height: 30px;
  }
}
.left {
  position: relative;
  width: 60%;
}
.text-wrapper {
  white-space: pre-wrap;
}
/deep/.answer {
  .el-tab-pane {
    padding: 0 10px;
    height: 340px;
    overflow: hidden;
    overflow-y: auto;
    white-space: pre-wrap;
  }
  .el-dialog--center {
    width: 500px;
    height: 500px;
  }
  .el-dialog__title {
    font-size: 22px;
    font-weight: 500;
  }
  .el-tabs__nav-wrap::after {
    background-color: #333;
  }
  .el-tabs__active-bar {
    height: 0;
    background-color: #fff;
  }
  .el-tabs__header {
    background-color: #333;
  }
  .el-dialog--center .el-dialog__body {
    padding: 0;
  }
  .el-tabs__item {
    width: 80px;
    color: #fff;
  }
  .el-tabs--top .el-tabs__item.is-top:nth-child(2) {
    padding-left: 20px;
  }
  .el-tabs__item.is-active {
    color: #fff !important;
    background-color: #333 !important;
  }
  .tips-btn {
    margin-top: 10px;
    height: 40px;
    width: 90px;
    border: none;
    position: absolute;
    right: 0;
    background: rgba(255, 49, 49, 1);
    color: rgba(255, 255, 255, 1);
    &:hover,
    &:focus,
    &:active {
      background: rgba(255, 49, 49, 1);
      color: rgba(255, 255, 255, 1);
    }
  }
}
.del {
  width: 40px;
  margin-right: 10px;
  line-height: 40px;
  color: #fff;
  text-align: center;
  border-radius: 50%;
  background-color: #f00;
  cursor: pointer;
  &:hover {
    opacity: 0.9;
  }
}
.result-right {
  background-color: rgba(43, 40, 22, 1);
}
.result-wrong {
  background-color: rgba(43, 22, 22, 1);
  color: #f00;
}
.result-wrong,
.result-right {
  position: absolute;
  left: 20px;
  right: 20px;
  display: flex;
  bottom: 10px;
  padding: 0 10px;
  img {
    width: 40px;
    height: 40px;
    margin-top: 5px;
    margin-right: 10px;
  }
}
.line {
  z-index: 2;
  position: absolute;
  left: 60%;
  top: 0;
  width: 5px;
  height: 100%;
  cursor: e-resize;
}
.code-right {
  width: 40%;
  color: #fff;
  background: #1b1b1b;
  display: inline-block;
  position: relative;
  overflow-x: auto;
  p {
    font-size: 18px;
    margin: 10px;
    position: absolute;
    width: calc(100% - 14px);
    height: calc(100vh - 340px);
    overflow: auto;
  }
  .pic-num0 {
    height: calc(100vh - 247px);
  }
  .pic-num1 {
    height: calc(100vh - 520px);
  }
}
.pic-wrap {
  position: absolute;
  left: 0;
  right: 0;
  bottom: 5px;
  display: flex;
  max-width: calc(100% - 50px);
  margin: 0 auto;
  text-align: center;
  overflow: auto;
  &.wrong {
    bottom: 60px;
  }
  .pic-item {
    margin: 0 5px 5px;
    &:only-child {
      .pic {
        width: 80%;
      }
    }
  }
  .pic {
    display: block;
    width: 100px;
    margin: 0 auto 10px;
  }
}
.code-mask {
  z-index: 2;
  position: absolute;
  top: 0;
  left: 0;
  bottom: 0;
  right: 0;
}
.btns {
  z-index: 99;
  position: absolute;
  right: 50px;
  bottom: 15px;
  display: flex;
}
.run {
  width: 100px;
  color: #fff;
}
.download-btn {
  color: #fff;
}
/deep/.answer-wrap {
  &.client {
    user-select: none;
  }
  pre {
    width: 100%;
    white-space: pre-wrap;
  }
  img {
    max-width: 100%;
  }
}
/deep/.model-dia {
  .el-dialog__body {
    padding: 0 20px;
  }
  .el-divider--horizontal {
    margin: 15px 0;
  }
}
.model-wrap {
  max-height: 400px;
  overflow: auto;
}
</style>