VUE(Ant Design Vue Pro)+springboot项目开发总结

2 篇文章 0 订阅
1 篇文章 0 订阅

一、前端VUE(Ant Design Vue Pro)

官网:https://www.antdv.com/docs/vue/introduce-cn/

1,table隔行换色
<s-table
      :rowClassName="setRowClassName"
    >
 methods: {
  setRowClassName(record, index) {
      return index % 2 === 1 ? 'ant-table-row-twoe' : 'ant-table-row-once'
    }
 }
// 添加全局样式
.ant-table-row-once {
  background-color: #ffffff;
}

.ant-table-row-twoe {
  background-color: #fafafa;
}
2,改变table行高
.ant-table-thead > tr > th,
.ant-table-tbody > tr > td {
  padding: 8px 8px;
}
3,vs-code setting.json默认配置和代码自动格式化配置
{
    "extensions.ignoreRecommendations": true,
    "eslint.enable": false,
    "editor.wordWrapColumn": 120,
    "[javascript]": {
        "editor.defaultFormatter": "esbenp.prettier-vscode"
    },
    "vetur.validation.template": false
}
{
    // vscode默认启用了根据文件类型自动设置tabsize的选项
    "editor.detectIndentation": false,
    // 重新设定tabsize
    "editor.tabSize": 4,
    // #值设置为true时,每次保存的时候自动格式化;值设置为false时,代码格式化请按shift+alt+F
    "editor.formatOnSave": false,
    // #每次保存的时候将代码按eslint格式进行修复
    "eslint.autoFixOnSave": true,
    // 添加 vue 支持
    "eslint.validate": [
        "javascript",
        "javascriptreact",
        {
            "language": "vue",
            "autoFix": true
        }
    ],
    //  #让prettier使用eslint的代码格式进行校验
    "prettier.eslintIntegration": true,
    //  #去掉代码结尾的分号
    "prettier.semi": false,
    //  #使用带引号替代双引号
    "prettier.singleQuote": true,
    "prettier.tabWidth": 4,
    //  #让函数(名)和后面的括号之间加个空格
    "javascript.format.insertSpaceBeforeFunctionParenthesis": true,
    // #这个按用户自身习惯选择
    "vetur.format.defaultFormatter.html": "js-beautify-html",
    // #让vue中的js按"prettier"格式进行格式化
    "vetur.format.defaultFormatter.js": "prettier",
    "vetur.format.defaultFormatterOptions": {
        "js-beautify-html": {
            // #vue组件中html代码格式化样式
            "wrap_attributes": "force-aligned", //也可以设置为“auto”,效果会不一样
            "wrap_line_length": 200,
            "end_with_newline": false,
            "semi": false,
            "singleQuote": true
        },
        "prettier": {
            "semi": false,
            "singleQuote": true
        }
    },
    "[jsonc]": {
        "editor.defaultFormatter": "esbenp.prettier-vscode"
    },
    // 格式化stylus, 需安装Manta's Stylus Supremacy插件
    "stylusSupremacy.insertColons": false, // 是否插入冒号
    "stylusSupremacy.insertSemicolons": false, // 是否插入分号
    "stylusSupremacy.insertBraces": false, // 是否插入大括号
    "stylusSupremacy.insertNewLineAroundImports": false, // import之后是否换行
    "stylusSupremacy.insertNewLineAroundBlocks": false,
    "prettier.useTabs": true,
    "files.autoSave": "off",
    "explorer.confirmDelete": false,
    "[javascript]": {
        "editor.defaultFormatter": "esbenp.prettier-vscode"
    },
    "[json]": {
        "editor.defaultFormatter": "esbenp.prettier-vscode"
    },
    "diffEditor.ignoreTrimWhitespace": false // 两个选择器中是否换行
}
5,全局共通函数定义
// 1,在util.js中添加函数
// 2,在main.js中添加引用
// 3,调用
/**
 * 依据code,返回常数代码对应的文本
 * @param value 常数代码code
 * @param codeType 常数代码
 */
export default {
  install(Vue) {
    Vue.prototype.getCodeText = function (value, codeType) {
      var text = ''
      this.$store.getters.items[codeType].forEach((item, index, arr) => {
        if (value != null && value.toString() === item.value) {
          text = item.text
        }
      })
      return text
    }
  }
}

import util from './utils/util'
Vue.use(util)

{
    title: '报告生成状态',
    dataIndex: 'generateStatus',
    customRender: value => {
        return this.getCodeText(value, 'code_rep_cre_code')
    }
},
6,使用filter

Vue.js 允许你自定义过滤器,可被用于一些常见的文本格式化。过滤器可以用在两个地方:双花括号插值和 v-bind 表达式 (后者从 2.1.0+ 开始支持)。过滤器应该被添加在 JavaScript 表达式的尾部,由“管道”符号指示:

<!-- 在双花括号中 -->
{{ message | capitalize }}

<!--`v-bind`-->
<div v-bind:id="rawId | formatId"></div>

你可以在一个组件的选项中定义本地的过滤器:
filters: {
  capitalize: function (value) {
    if (!value) return ''
    value = value.toString()
    return value.charAt(0).toUpperCase() + value.slice(1)
  }
}

或者在创建 Vue 实例之前全局定义过滤器
Vue.filter('capitalize', function (value) {
  if (!value) return ''
  value = value.toString()
  return value.charAt(0).toUpperCase() + value.slice(1)
})

过滤器可以串联:

{{ message | filterA | filterB }}
在这个例子中,filterA 被定义为接收单个参数的过滤器函数,表达式 message 的值将作为参数传入到函数中。然后继续调用同样被定义为接收单个参数的过滤器函数 filterB,将 filterA 的结果传递到 filterB 中。

过滤器是 JavaScript 函数,因此可以接收参数:

{{ message | filterA('arg1', arg2) }}
这里,filterA 被定义为接收三个参数的过滤器函数。其中 message 的值作为第一个参数,普通字符串 'arg1' 作为第二个参数,表达式 arg2 的值作为第三个参数。
Vue.filter('moment', function (dataStr, pattern = 'YYYY-MM-DD HH:mm:ss') {
  return moment(dataStr).format(pattern)
})

import './utils/filter'

{
    title: '最近一次生成时间',
    dataIndex: 'lastGenerateTime',
    scopedSlots: { customRender: 'lastGenerateTime' }
},
  
<span slot="lastGenerateTime" slot-scope="text">
    <span>{{ text | moment }}</span>
</span>
7,CSS

官网:

https://pro.loacg.com/docs/style

如何在 Vue 中优雅地使用 CSS Modules?

https://juejin.im/post/5ac5fd7f5188257cc20d854e

覆盖组件样式:

<template>
    <div class="test-wrapper">
        <a-select>
            <a-select-option value="1">Option 1</a-select-option>
            <a-select-option value="2">Option 2</a-select-option>
            <a-select-option value="3">Option 3</a-select-option>
        </a-select>
    </div>
</template>

<script>
export default {

}
</script>
<style lang="less" scoped>
// 使用 css 时可以用 >>> 进行样式穿透
.test-wrapper >>> .ant-select {
    font-size: 16px;
}

// 使用 scss, less 时,可以用 /deep/ 进行样式穿透
.test-wrapper /deep/ .ant-select {
    font-size: 16px;
}
</style>
8,跳转路由–带参数的跳转三种方式
<li v-for="article in articles" @click="getDescribe(article.id)">
  • 方案1 path带参数

    getDescribe(id) {
    // 直接调用$router.push 实现携带参数的跳转
    this.$router.push({
       path: `/describe/${id}`,
    })
    // 路由配置如下
    {
         path: '/describe/:id',
         name: 'Describe',
         component: Describe
       }e
    
  • 方案2 父组件中:通过路由属性中的name来确定匹配的路由,通过params来传递参数。

    this.$router.push({
              name: 'Describe',
              params: {
                id: id
              }
            })
    // 路由配置
    {
         path: '/describe',
         name: 'Describe',
         component: Describe
       }
    // 子组件中: 这样来获取参数
    this.$route.params.id
    

  • 方案3通过query来传递参数

     this.$router.push({
              path: '/describe',
              query: {
                id: id
              }
            })
    {
         path: '/describe',
         name: 'Describe',
         component: Describe
       }
    this.$route.query.id
    

2,ink跳转传参:

import Link from 'umi/link';
<Link to={{
     pathname: '/api-manage/create-sub-system',
      query: {
        id: 6,
        value: 'lala',
      }
    }}>
      <Button
        style={{ marginLeft: 8 }}
        type="primary"
      // onClick={this.onCreate}
      >
        <Icon type="plus" />
        创建
    </Button>
 </Link>
9,自定义校验

1)自己实现

<a-input allowClear placeholder="请输入分位值区间" @blur="checkTitle">
// 校验标题重复
    checkTitle(e) {
        const title = e.target.value
        // HrReptSetConstModelController /checkTitle/{templateId}/{constType}/{title}
          if (e.target.value) {
            const arr = [{
              message: '您输入的手机格式不正确!',
              field: 'constNameZH'
            }]
            this.form.setFields({ constNameZH: { value: e.target.value, errors: arr } })
          }
        console.log(title)
    },

2,利用vue自带方法

v-decorator="['constNameEN', {initialValue: mdl.constNameEN, rules: [{validator: checkTitle}]}]"

// 校验标题重复
    checkTitle(rule, value, callback) {
      if (value) {
        // /rept/consts/checkTitle/{templateId}/{constType}/{title}
        const field = rule.fullField
        let langMark = 'zh-CN'
        if (field.substring(field.length - 2) === 'EN') {
          langMark = 'en-US'
        } else if (field.substring(field.length - 2) === 'JP') {
          langMark = 'ja-JP'
        }
        // 新增校验url
        let url = '/rept/consts/checkTitle/' + this.templateId + '/' + 0 + '/' + value + '/' + langMark
        if (this.constId) {
          // 修改校验url
          url = '/rept/consts/checkTitleEdit/' + this.templateId + '/' + 0 + '/' + value + '/' + langMark
        }
        api.get(url).then(res => {
          if (res === 'NG') {
            const error = '该分位值区间已被使用,请重新输入'
            callback(error)
          } else {
            callback()
          }
        })
      } else {
        callback()
      }
    },
10,在光标处插入文本内容
<a-form-item label="正文描述:">
                <a-textarea ref="contentArea" rows="10" placeholder="请输入正文描述" v-decorator="[ 'guideDescribeZH', {initialValue: mdl.guideDescribe, rules: [{ max: 2000, message: '最大长度不能超过2000' }]}]"/>
                <div style="margin-bottom:-24px;"><a @click="handlerA('ZH')">有效评估人数</a><a style="margin-left:12px;" @click="handlerA('ZH')">活动开启时间</a><a style="margin-left:12px;" @click="handlerA('ZH')">活动结束时间</a></div>
              </a-form-item>

// 关于评估数据样本,点击链接字体时
    handlerA(type) {
      const elInput = this.$refs.contentArea.$el
      const startPos = elInput.selectionStart// input 第0个字符到选中的字符
      const endPos = elInput.selectionEnd // 选中的字符到最后的字符
      if (startPos === undefined || endPos === undefined) return
      // 将文字添加到选中的光标位置
      const innerText = '#' + event.target.text + '#'
      let sourceText = ''
      if (type === 'ZH') {
        sourceText = this.form.getFieldValue('guideDescribeZH')
      } else if (type === 'EN') {
        sourceText = this.form.getFieldValue('guideDescribeEN')
      } else {
        sourceText = this.form.getFieldValue('guideDescribeJP')
      }
      const result = sourceText.substring(0, startPos) + innerText + sourceText.substring(endPos)
      elInput.value = result
      // 重新定义光标位置
      elInput.focus()
      elInput.selectionStart = startPos + innerText.length
      elInput.selectionEnd = startPos + innerText.length

      if (type === 'ZH') {
        this.form.setFieldsValue({
          guideDescribeZH: result
        })
      } else if (type === 'EN') {
        this.form.setFieldsValue({
          guideDescribeEN: result
        })
      } else {
        this.form.setFieldsValue({
          guideDescribeJP: result
        })
      }
    },
11,model开发

src\views\evaluation\setting\modules\ModeForm.vue

<template>
  <a-modal
    :title="title"
    :width="width"
    :visible="visible"
    :confirmLoading="confirmLoading"
    :footer="null"
    :maskClosable="false"
    :afterClose="afterClose"
    @cancel="handleCancel"
    @ok="handleSubmit"
  >
    <a-spin :spinning="confirmLoading" class="parent">
      <a-tabs :activeKey="activeKey" @change="changeTab" :tabBarStyle="{ textAlign: 'right', borderBottom: 'set' }">
        <a-tab-pane tab="中文" key="zh-CN">
          <a-form :form="form" :label-col="formItemLayout.labelCol" :wrapper-col="formItemLayout.wrapperCol">
            <a-form-item label="标题:">
              <a-input allowClear placeholder="请输入标题" v-decorator="['guideTitleZH', {initialValue: mdl.guideTitle, rules: [{ required: true, message: '请输入标题' },{ max: 100, message: '最大长度不能超过100' }]}]" />
            </a-form-item>
            <div v-if="guideType === '2'">
              <a-form-item label="正文描述:">
                <a-textarea rows="10" placeholder="请输入正文描述" v-decorator="[ 'guideDescribeZH', {initialValue: mdl.guideDescribe, rules: [{ max: 2000, message: '最大长度不能超过2000' }]}]"/>
              </a-form-item>
            </div>
            <div v-if="guideType === '7'">
              <a-form-item label="正文描述:">
                <a-textarea ref="contentArea" rows="10" placeholder="请输入正文描述" v-decorator="[ 'guideDescribeZH', {initialValue: mdl.guideDescribe, rules: [{ max: 2000, message: '最大长度不能超过2000' }]}]"/>
                <div style="margin-bottom:-24px;"><a @click="handlerA('ZH')">有效评估人数</a><a style="margin-left:12px;" @click="handlerA('ZH')">活动开启时间</a><a style="margin-left:12px;" @click="handlerA('ZH')">活动结束时间</a></div>
              </a-form-item>
              <a-form-item label="作答统计:">
                <a-radio-group v-decorator="['isShow', {initialValue: mdl.isShow }]">
                  <a-radio :value="1">显示</a-radio>
                  <a-radio :value="0">不显示</a-radio>
                </a-radio-group>
              </a-form-item>
            </div>
            <div v-if="guideType === '8'">
              <a-form-item label="正文描述:" required :validate-status="validateStatusZH" :help="helpZH">
                <vue-ueditor-wrap v-model="guideDescribeZh" :config="myConfig"></vue-ueditor-wrap>
              </a-form-item>
            </div>
            <div v-if="guideType !== '8' && guideType !== '7' && guideType !== '2'">
              <a-form-item label="正文描述:">
                <a-textarea rows="10" placeholder="请输入正文描述" v-decorator="[ 'guideDescribeZH', {initialValue: mdl.guideDescribe, rules: [{ required: true, message: '请输入正文描述' },{ max: 2000, message: '最大长度不能超过2000' }]}]"/>
              </a-form-item>
            </div>
            <!-- 隐藏字段信息为了在页面初期化的时候将日文中文信息加载进来-->
            <!-- <div style="display: none;">
              <a-form-item label="标题:">
                <a-input allowClear placeholder="请输入标题" v-decorator="['guideTitleEN', {initialValue: mdlEN.guideTitle, rules: [{ required: true, message: '请输入标题' },{ max: 100, message: '最大长度不能超过100' }]}]" />
              </a-form-item>
              <div v-if="guideType === '2'">
                <a-form-item label="正文描述:">
                  <a-textarea rows="10" placeholder="请输入正文描述" v-decorator="[ 'guideDescribeEN', {initialValue: mdlEN.guideDescribe, rules: [{ max: 2000, message: '最大长度不能超过2000' }]}]"/>
                </a-form-item>
              </div>
              <div v-if="guideType === '7'">
                <a-form-item label="正文描述:">
                  <a-textarea rows="10" placeholder="请输入正文描述" v-decorator="[ 'guideDescribeEN', {initialValue: mdlEN.guideDescribe, rules: [{ max: 2000, message: '最大长度不能超过2000' }]}]"/>
                  <div style="margin-bottom:-24px;"><a @click="handlerA('EN')">有效评估人数</a><a style="margin-left:12px;" @click="handlerA('EN')">活动开启时间</a><a style="margin-left:12px;" @click="handlerA('EN')">活动结束时间</a></div>
                </a-form-item>
                <a-form-item label="作答统计:">
                  <a-radio-group disabled defaultValue="1" v-decorator="['isShow', {initialValue: mdl.isShow }]">
                    <a-radio :value="1">显示</a-radio>
                    <a-radio :value="0">不显示</a-radio>
                  </a-radio-group>
                </a-form-item>
              </div>
              <div v-if="guideType === '8'">
                <a-form-item label="正文描述:" required :validate-status="validateStatusEN" :help="helpEN">
                  <vue-ueditor-wrap v-model="guideDescribeEn" :config="myConfig"></vue-ueditor-wrap>
                </a-form-item>
              </div>
              <div v-if="guideType !== '8' && guideType !== '7' && guideType !== '2'">
                <a-form-item label="正文描述:">
                  <a-textarea rows="10" placeholder="请输入正文描述" v-decorator="[ 'guideDescribeEN', {initialValue: mdlEN.guideDescribe, rules: [{ required: true, message: '请输入正文描述' },{ max: 2000, message: '最大长度不能超过2000' }]}]"/>
                </a-form-item>
              </div>
              <a-form-item label="标题:">
                <a-input allowClear placeholder="请输入标题" v-decorator="['guideTitleJP', {initialValue: mdlJP.guideTitle, rules: [{ required: true, message: '请输入标题' },{ max: 100, message: '最大长度不能超过100' }]}]" />
              </a-form-item>
              <div v-if="guideType === '2'">
                <a-form-item label="正文描述:">
                  <a-textarea rows="10" placeholder="请输入正文描述" v-decorator="[ 'guideDescribeJP', {initialValue: mdlJP.guideDescribe, rules: [{ max: 2000, message: '最大长度不能超过2000' }]}]"/>
                </a-form-item>
              </div>
              <div v-if="guideType === '7'">
                <a-form-item label="正文描述:">
                  <a-textarea rows="10" placeholder="请输入正文描述" v-decorator="[ 'guideDescribeJP', {initialValue: mdlEN.guideDescribe, rules: [{ max: 2000, message: '最大长度不能超过2000' }]}]"/>
                  <div style="margin-bottom:-24px;"><a @click="handlerA('JP')">有效评估人数</a><a style="margin-left:12px;" @click="handlerA('JP')">活动开启时间</a><a style="margin-left:12px;" @click="handlerA('JP')">活动结束时间</a></div>
                </a-form-item>
                <a-form-item label="作答统计:">
                  <a-radio-group disabled defaultValue="1" v-decorator="['isShow', {initialValue: mdl.isShow }]">
                    <a-radio :value="1">显示</a-radio>
                    <a-radio :value="0">不显示</a-radio>
                  </a-radio-group>
                </a-form-item>
              </div>
              <div v-if="guideType === '8'">
                <a-form-item label="正文描述:" required :validate-status="validateStatusJP" :help="helpJP">
                  <vue-ueditor-wrap v-model="guideDescribeJp" :config="myConfig"></vue-ueditor-wrap>
                </a-form-item>
              </div>
              <div v-if="guideType !== '8' && guideType !== '7' && guideType !== '2'">
                <a-form-item label="正文描述:">
                  <a-textarea rows="10" placeholder="请输入正文描述" v-decorator="[ 'guideDescribeJP', {initialValue: mdlJP.guideDescribe, rules: [{ required: true, message: '请输入正文描述' },{ max: 2000, message: '最大长度不能超过2000' }]}]"/>
                </a-form-item>
              </div>
            </div> -->
          </a-form>

        </a-tab-pane>
        <!-- 英文 模块-->
        <!-- <a-tab-pane tab="英文" key="en-US">
          <a-form :form="form" :label-col="formItemLayout.labelCol" :wrapper-col="formItemLayout.wrapperCol">
            <a-form-item label="标题:">
              <a-input allowClear placeholder="请输入标题" v-decorator="['guideTitleEN', {initialValue: mdlEN.guideTitle, rules: [{ required: true, message: '请输入标题' },{ max: 100, message: '最大长度不能超过100' }]}]" />
            </a-form-item>
            <div v-if="guideType === '2'">
              <a-form-item label="正文描述:">
                <a-textarea rows="10" placeholder="请输入正文描述" v-decorator="[ 'guideDescribeEN', {initialValue: mdlEN.guideDescribe, rules: [{ max: 2000, message: '最大长度不能超过2000' }]}]"/>
              </a-form-item>
            </div>
            <div v-if="guideType === '7'">
              <a-form-item label="正文描述:">
                <a-textarea rows="10" placeholder="请输入正文描述" v-decorator="[ 'guideDescribeEN', {initialValue: mdlEN.guideDescribe, rules: [{ max: 2000, message: '最大长度不能超过2000' }]}]"/>
                <div style="margin-bottom:-24px;"><a @click="handlerA('EN')">有效评估人数</a><a style="margin-left:12px;" @click="handlerA('EN')">活动开启时间</a><a style="margin-left:12px;" @click="handlerA('EN')">活动结束时间</a></div>
              </a-form-item>
              <a-form-item label="作答统计:">
                <a-radio-group disabled defaultValue="1" v-decorator="['isShow', {initialValue: mdl.isShow }]">
                  <a-radio :value="1">显示</a-radio>
                  <a-radio :value="0">不显示</a-radio>
                </a-radio-group>
              </a-form-item>
            </div>
            <div v-if="guideType === '8'">
              <a-form-item label="正文描述:" required :validate-status="validateStatusEN" :help="helpEN">
                <vue-ueditor-wrap v-model="guideDescribeEn" :config="myConfig"></vue-ueditor-wrap>
              </a-form-item>
            </div>
            <div v-if="guideType !== '8' && guideType !== '7' && guideType !== '2'">
              <a-form-item label="正文描述:">
                <a-textarea rows="10" placeholder="请输入正文描述" v-decorator="[ 'guideDescribeEN', {initialValue: mdlEN.guideDescribe, rules: [{ required: true, message: '请输入正文描述' },{ max: 2000, message: '最大长度不能超过2000' }]}]"/>
              </a-form-item>
            </div>
          </a-form>
        </a-tab-pane> -->
        <!-- 日本语 模块-->
        <!-- <a-tab-pane tab="日本语" key="ja-JP">
          <a-form :form="form" :label-col="formItemLayout.labelCol" :wrapper-col="formItemLayout.wrapperCol">
            <a-form-item label="标题:">
              <a-input allowClear placeholder="请输入标题" v-decorator="['guideTitleJP', {initialValue: mdlJP.guideTitle, rules: [{ required: true, message: '请输入标题' },{ max: 100, message: '最大长度不能超过100' }]}]" />
            </a-form-item>
            <div v-if="guideType === '2'">
              <a-form-item label="正文描述:">
                <a-textarea rows="10" placeholder="请输入正文描述" v-decorator="[ 'guideDescribeJP', {initialValue: mdlJP.guideDescribe, rules: [{ max: 2000, message: '最大长度不能超过2000' }]}]"/>
              </a-form-item>
            </div>
            <div v-if="guideType === '7'">
              <a-form-item label="正文描述:">
                <a-textarea rows="10" placeholder="请输入正文描述" v-decorator="[ 'guideDescribeJP', {initialValue: mdlEN.guideDescribe, rules: [{ max: 2000, message: '最大长度不能超过2000' }]}]"/>
                <div style="margin-bottom:-24px;"><a @click="handlerA('JP')">有效评估人数</a><a style="margin-left:12px;" @click="handlerA('JP')">活动开启时间</a><a style="margin-left:12px;" @click="handlerA('JP')">活动结束时间</a></div>
              </a-form-item>
              <a-form-item label="作答统计:">
                <a-radio-group disabled defaultValue="1" v-decorator="['isShow', {initialValue: mdl.isShow }]">
                  <a-radio :value="1">显示</a-radio>
                  <a-radio :value="0">不显示</a-radio>
                </a-radio-group>
              </a-form-item>
            </div>
            <div v-if="guideType === '8'">
              <a-form-item label="正文描述:" required :validate-status="validateStatusJP" :help="helpJP">
                <vue-ueditor-wrap v-model="guideDescribeJp" :config="myConfig"></vue-ueditor-wrap>
              </a-form-item>
            </div>
            <div v-if="guideType !== '8' && guideType !== '7' && guideType !== '2'">
              <a-form-item label="正文描述:">
                <a-textarea rows="10" placeholder="请输入正文描述" v-decorator="[ 'guideDescribeJP', {initialValue: mdlJP.guideDescribe, rules: [{ required: true, message: '请输入正文描述' },{ max: 2000, message: '最大长度不能超过2000' }]}]"/>
              </a-form-item>
            </div>
          </a-form>
        </a-tab-pane> -->
      </a-tabs>
      <div style="text-align: center;padding-bottom: 24px;">
        <a-button icon="check-circle" type="primary" html-type="submit" @click="handleSubmit()">{{ this.global.BTN.SAVE }}</a-button>
        <a-button icon="close-circle" style="margin-left: 8px" @click="handleCancel()">{{ this.global.BTN.CANCEL }}</a-button>
      </div>
    </a-spin>
  </a-modal>
</template>

<script>
import { api } from '@/api/api'
import VueUeditorWrap from 'vue-ueditor-wrap'

export default {
  data() {
    return {
      formItemLayout: {
        labelCol: { xs: { span: 24 }, sm: { span: 5 } },
        wrapperCol: { xs: { span: 24 }, sm: { span: 16 } }
      },
      title: '新增',
      width: 640,
      visible: false,
      confirmLoading: false,
      modelFlag: '', // 父页面传递参数表示add,edit
      guideId: null, // 父页面传递参数:指南业务主键
      setModuleId: null, // 父页面传递参数:模块库系统主键
      guideType: null,
      activeKey: 'zh-CN',
      guideDescribeZh: '',
      guideDescribeEn: '',
      guideDescribeJp: '',
      validateStatusZH: 'validating',
      validateStatusEN: 'validating',
      validateStatusJP: 'validating',
      helpZH: '',
      helpEN: '',
      helpJP: '',
      mdl: {},
      mdlEN: {},
      mdlJP: {},
      form: this.$form.createForm(this),
      myConfig: {
        // 编辑器不自动被内容撑高
        autoHeightEnabled: false,
        // 初始容器高度
        initialFrameHeight: 240,
        enableAutoSave: false,
        maximumWords: 5000,
        // 初始容器宽度
        initialFrameWidth: '100%',
        imageActionName: 'uploadimage',
        imageAllowFiles: ['.png', '.jpg', '.jpeg', '.gif', '.bmp'],
        imageFieldName: 'file',
        imageUrlPrefix: process.env.VUE_APP_API_FILE_PREFIX_URL,
        // 上传文件接口(这个地址是我为了方便各位体验文件上传功能搭建的临时接口,请勿在生产环境使用!!!)
        serverUrl: '/files',
        /* toolbars: [
    ['fullscreen', 'source', 'undo', 'redo'],
    ['bold', 'italic', 'underline', 'fontborder', 'strikethrough', 'superscript', 'subscript', 'removeformat', 'formatmatch', 'autotypeset', 'blockquote', 'pasteplain', 'insertimage', '|', 'forecolor', 'backcolor', 'insertorderedlist', 'insertunorderedlist', 'selectall', 'cleardoc']
], */
        // UEditor 资源文件的存放路径,如果你使用的是 vue-cli 生成的项目,通常不需要设置该选项,vue-ueditor-wrap 会自动处理常见的情况,如果需要特殊配置,参考下方的常见问题2
        UEDITOR_HOME_URL: '/UEditor/'
      }
    }
  },
  methods: {
    // 接收父页面数据完成页面初期化
    // modelFlag标记操作add:添加,edit编辑
    // guide编辑的时候传递的行数据,包含指南的业务id和类型
    // setModuleId 指南业务主键
    init(modelFlag, guide, setModuleId) {
      this.visible = true
      this.modelFlag = modelFlag
      this.setModuleId = setModuleId
      // 修改:动态获取显示数据
      if (guide) {
        this.title = '编辑'
        this.guideId = guide.guideId
        this.guideType = guide.guideType
        // 关于能力模型,富文本
        if (this.guideType === '8') {
            this.width = 1048
        }
        let url = '/rept/guides/getGuide/' + this.guideId + '/' + guide.setModuleId
        if (this.modelFlag === 'libEdit') {
            url = '/rept/guides/getLibGuide/' + this.guideId
        }
        // HrReptSetGuideController
        api.get(url).then(res => {
            if (res) {
                this.mdl = res['zh-CN']
                this.mdlEN = res['en-US']
                this.mdlJP = res['ja-JP']
                if (this.guideType === '8') {
                    this.guideDescribeZh = this.mdl.guideDescribe
                    this.guideDescribeEn = this.mdlEN.guideDescribe
                    this.guideDescribeJp = this.mdlJP.guideDescribe
                }
            }
        })
      }
    },

    // 执行保存
    handleSubmit() {
      // 关于能力模型
      this.errorFlag = true
      if (this.guideType === '8') {
          // 校验中文
          if (!this.guideDescribeZh) {
            this.helpZH = '请输入正文描述'
            this.validateStatusZH = 'error'
            this.errorFlag = false
          } else {
              if (this.guideDescribeZh.length > 5000) {
                this.helpZH = '最大长度不能超过5000'
                this.validateStatusZH = 'error'
                this.errorFlag = false
              }
          }
          // 校验英文
          /* if (!this.guideDescribeEn) {
            this.helpEN = '请输入正文描述'
            this.validateStatusEN = 'error'
            this.errorFlag = false
          } else {
              if (this.guideDescribeEn.length > 5000) {
                this.helpEN = '最大长度不能超过5000'
                this.validateStatusEN = 'error'
                this.errorFlag = false
              }
          } */
          // 校验日文
          /* if (!this.guideDescribeJp) {
            this.helpJP = '请输入正文描述'
            this.validateStatusJP = 'error'
            this.errorFlag = false
          } else {
              if (this.guideDescribeJp.length > 5000) {
                this.helpJP = '最大长度不能超过5000'
                this.validateStatusJP = 'error'
                this.errorFlag = false
              }
          } */
      }
      this.form.validateFields((errors, values) => {
        if (!errors && this.errorFlag) {
            // 设置中文
            this.mdl.guideTitle = values.guideTitleZH
            this.mdl.guideDescribe = values.guideDescribeZH || this.guideDescribeZh
            // 设置英文
            // this.mdlEN.guideTitle = values.guideTitleEN
            // this.mdlEN.guideDescribe = values.guideDescribeEN || this.guideDescribeEn
            // 设置日文
            // this.mdlJP.guideTitle = values.guideTitleJP
            // this.mdlJP.guideDescribe = values.guideDescribeJP || this.guideDescribeJp

            // 关于评估数据样本
            if (this.guideType === '7') {
                this.mdl.isShow = values.isShow
                // this.mdlEN.isShow = values.isShow
                // this.mdlJP.isShow = values.isShow
            }
            // 执行添加操作时,设置默认参数
            if (this.modelFlag === 'add') {
                this.mdl.setModuleId = this.setModuleId
                this.mdl.langMark = 'zh-CN'
                this.mdl.isShow = 1
                /* this.mdlEN.setModuleId = this.setModuleId
                this.mdlEN.langMark = 'en-US'
                this.mdlEN.isShow = 1
                this.mdlJP.setModuleId = this.setModuleId
                this.mdlJP.langMark = 'ja-JP'
                this.mdlJP.isShow = 1 */
            }

            // 执行系统模板保存操作
            if (this.modelFlag === 'libEdit') {
                this.mdl.setModuleId = this.setModuleId
                // this.mdlEN.setModuleId = this.setModuleId
                // this.mdlJP.setModuleId = this.setModuleId
            }

            // 设置传入的中日英数组
            const guides = []
            guides[0] = this.mdl
            // guides[1] = this.mdlEN
            // guides[2] = this.mdlJP
            values['guides'] = guides
            values['guideId'] = this.guideId
            values['setModuleId'] = this.setModuleId
            this.confirmLoading = true

            // addLibguide系统模板,addOrUpdateGuide自定义
            let url = '/rept/guides/addOrUpdateGuide'
            if (this.modelFlag === 'libEdit') {
                url = '/rept/guides/addLibguide'
            }
            api.post(url, values).then(res => {
                this.$message.success(this.global.SAVE_SUCCESS)
                this.confirmLoading = false
                this.handleCancel()
                this.$emit('ok')
            })
        } else {
          // this.$message.error('请您将中英日三个模块内容填写完整,再进行保存操作。')
          this.confirmLoading = false
        }
      })
    },

    // 取消
    handleCancel() {
      this.visible = false
    },

    // 关于评估数据样本,点击链接字体时
    handlerA(type) {
      const elInput = this.$refs.contentArea.$el
      const startPos = elInput.selectionStart// input 第0个字符到选中的字符
      const endPos = elInput.selectionEnd // 选中的字符到最后的字符
      if (startPos === undefined || endPos === undefined) return
      // 将文字添加到选中的光标位置
      const innerText = '#' + event.target.text + '#'
      let sourceText = ''
      if (type === 'ZH') {
        sourceText = this.form.getFieldValue('guideDescribeZH')
      } else if (type === 'EN') {
        sourceText = this.form.getFieldValue('guideDescribeEN')
      } else {
        sourceText = this.form.getFieldValue('guideDescribeJP')
      }
      const result = sourceText.substring(0, startPos) + innerText + sourceText.substring(endPos)
      elInput.value = result
      // 重新定义光标位置
      elInput.focus()
      elInput.selectionStart = startPos + innerText.length
      elInput.selectionEnd = startPos + innerText.length

      if (type === 'ZH') {
        this.form.setFieldsValue({
          guideDescribeZH: result
        })
      } else if (type === 'EN') {
        this.form.setFieldsValue({
          guideDescribeEN: result
        })
      } else {
        this.form.setFieldsValue({
          guideDescribeJP: result
        })
      }
    },

    // 切换tab
    changeTab(key) {
      this.activeKey = key
    },

    // 窗口关闭后初期化控件参数
    afterClose() {
      this.form.resetFields()
      this.activeKey = 'zh-CN'
      this.guideId = null
      this.guideType = null
      this.width = 640
      this.guideDescribeZh = ''
      this.guideDescribeEn = ''
      this.guideDescribeJp = ''
      this.mdl = {}
      this.mdlEN = {}
      this.mdlJP = {}
    }
  },
  components: {
    VueUeditorWrap
  }
}
</script>

<style scoped>
  .parent >>> .edui-default .edui-toolbar {
    line-height: initial;
  }
  .parent >>> .edui-default .edui-toolbar .edui-combox .edui-combox-body {
    line-height: initial;
  }
  .parent >>> .ant-tabs-bar {
    border-bottom: 1px solid #fff;
  }
</style>

12,form表单元素,一行包含多个控件,有增加删除操作

在这里插入图片描述

<template>
  <a-modal
    :title="title"
    :width="800"
    :visible="visible"
    :confirmLoading="confirmLoading"
    :footer="null"
    :maskClosable="false"
    :afterClose="afterClose"
    @cancel="handleCancel"
    @ok="handleSubmit"
  >
    <a-spin :spinning="confirmLoading" class="parent">
      <a-tabs :activeKey="activeKey" @change="changeTab" :tabBarStyle="{ textAlign: 'right', borderBottom: 'set' }">
        <a-tab-pane tab="中文" key="zh-CN">
          <a-form :form="form" :label-col="formItemLayout.labelCol" :wrapper-col="formItemLayout.wrapperCol">
            <a-form-item label="认知结果:" :help="titleMsg" :validateStatus="titleErrorStatus">
              <a-input allowClear @change="checkTitle('zh-CN')" placeholder="请输入认知结果" v-decorator="['constNameZH', {initialValue: mdl.constNameZH, rules: [{ required: true, message: '请输入认知结果' },{ max: 20, message: '最大长度不能超过20' }]}]" />
            </a-form-item>
            <a-form-item label="说明:">
              <a-textarea rows="4" placeholder="请输入说明" v-decorator="[ 'constDescZH', {initialValue: mdl.constDescZH, rules: [{ max: 500, message: '最大长度不能超过500' }]}]"/>
            </a-form-item>
            <a-form-item label="计算规则" :required="true" :help="formErrMessage" :validateStatus="errorStatus">
              <div v-for="rule in constRules" :key="rule.showOrder">
                <a-select allowClear @change="checkRuleleft(rule)" placeholder="请选择" style="width:128px;margin-right:8px;" v-model="rule.ruleLeft">
                  <!-- <a-select-option v-for="item in evalRelation" :key="item.code" :value="item.code">{{ item.codeName }}</a-select-option> -->
                  <a-select-option value="0">问卷自评总分</a-select-option>
                  <a-select-option value="4">问卷他评总分</a-select-option>
                </a-select>
                <span>-</span>
                <a-select allowClear @change="checkRuleleft(rule)" placeholder="请选择" style="width:128px;margin-right:8px;margin-left:8px;" v-model="rule.ruleRight">
                  <!-- <a-select-option v-for="item in evalRelation" :key="item.code" :value="item.code">{{ item.codeName }}</a-select-option> -->
                  <a-select-option value="0">问卷自评总分</a-select-option>
                  <a-select-option value="4">问卷他评总分</a-select-option>
                </a-select>
                <a-select allowClear placeholder="请选择" style="width:88px;margin-right:8px;" v-model="rule.computeSign">
                  <a-select-option v-for="item in $store.getters.items.code_compute_sign" :key="item.code" :value="item.code">{{ item.codeName }}</a-select-option>
                </a-select>
                <a-input-number :precision="1" :min="0" :max="100" style=" width:65px;margin-right:5px;" v-model="rule.ruleValue"/>
                <span></span>
                <img @click="addRuleInterval(rule)" src="@/image/add.png" style="margin-left: 12px;width:11px; height:12px;"/>
                <img v-if="constRules.length !== 1" @click="removeRuleInterval(rule)" src="@/image/close.png" style="margin-left:12px;width:11px; height:12px;"/>
              </div>
            </a-form-item>
            <a-form-item label="规则间运算逻辑:">
              <a-select allowClear placeholder="请选择" v-decorator="['arithLogic', { initialValue: mdl.arithLogic, rules: [{ required: true, message: '请选择规则间运算逻辑' }] }]">
                <a-select-option v-for="item in this.$store.getters.items.code_arith_logic" :key="item.code" :value="item.code" >{{ item.codeName }}</a-select-option>
              </a-select>
            </a-form-item>
            <!-- <div style="display: none;">
              <a-form-item label="认知结果:">
                <a-input allowClear placeholder="请输入认知结果" v-decorator="['constNameEN', {initialValue: mdl.constNameEN}]" />
              </a-form-item>
              <a-form-item label="说明:">
                <a-textarea rows="4" placeholder="请输入说明" v-decorator="[ 'constDescEN', {initialValue: mdl.constDescEN}]"/>
              </a-form-item>
              <a-form-item label="认知结果:">
                <a-input allowClear placeholder="请输入认知结果" v-decorator="['constNameJP', {initialValue: mdl.constNameJP}]" />
              </a-form-item>
              <a-form-item label="说明:">
                <a-textarea rows="4" placeholder="请输入说明" v-decorator="[ 'constDescJP', {initialValue: mdl.constDescJP}]"/>
              </a-form-item>
            </div> -->
          </a-form>
        </a-tab-pane>
        <!-- <a-tab-pane tab="英文" key="en-US">
          <a-form :form="form" :label-col="formItemLayout.labelCol" :wrapper-col="formItemLayout.wrapperCol">
            <a-form-item label="认知结果:">
              <a-input allowClear placeholder="请输入认知结果" v-decorator="['constNameEN', {initialValue: mdl.constNameEN, rules: [{validator: checkTitle}]}]" />
            </a-form-item>
            <a-form-item label="说明:">
              <a-textarea rows="4" placeholder="请输入说明" v-decorator="[ 'constDescEN', {initialValue: mdl.constDescEN}]"/>
            </a-form-item>
            <a-form-item label="计算规则">
              <div>
                <a-select placeholder="请选择" disabled style="width:100px;margin-right:15px;"/>
                <span>-</span>
                <a-select placeholder="请选择" disabled style="width:100px;margin-right:15px;margin-left:8px;"/>
                <a-select placeholder="请选择" disabled style="width:88px;margin-right:15px;"/>
                <a-input-number disabled style=" width:60px;margin-right:15px;" />
                <span>分</span>
              </div>
            </a-form-item>
            <a-form-item label="规则间运算逻辑:">
              <a-select placeholder="请选择" disabled></a-select>
            </a-form-item>
          </a-form>
        </a-tab-pane>
        <a-tab-pane tab="日本语" key="ja-JP" visible>
          <a-form :form="form" :label-col="formItemLayout.labelCol" :wrapper-col="formItemLayout.wrapperCol">
            <a-form-item label="认知结果:">
              <a-input allowClear placeholder="请输入认知结果" v-decorator="['constNameJP', {initialValue: mdl.constNameJP, rules: [{validator: checkTitle}]}]" />
            </a-form-item>
            <a-form-item label="说明:">
              <a-textarea rows="4" placeholder="请输入说明" v-decorator="[ 'constDescJP', {initialValue: mdl.constDescJP}]"/>
            </a-form-item>
            <a-form-item label="计算规则">
              <div>
                <a-select placeholder="请选择" disabled style="width:100px;margin-right:15px;"/>
                <span>-</span>
                <a-select placeholder="请选择" disabled style="width:100px;margin-right:15px;margin-left:8px;"/>
                <a-select placeholder="请选择" disabled style="width:88px;margin-right:15px;"/>
                <a-input-number disabled style=" width:60px;margin-right:15px;"/>
                <span>分</span>
              </div>
            </a-form-item>
            <a-form-item label="规则间运算逻辑:">
              <a-select placeholder="请选择" disabled />
            </a-form-item>
          </a-form>
        </a-tab-pane> -->
      </a-tabs>
      <div style="text-align: center;padding-bottom: 24px;">
        <a-button icon="check-circle" type="primary" html-type="submit" @click="handleSubmit()">{{ this.global.BTN.SAVE }}</a-button>
        <a-button icon="close-circle" style="margin-left: 8px" @click="handleCancel()">{{ this.global.BTN.CANCEL }}</a-button>
      </div>
    </a-spin>
  </a-modal>
</template>

<script>
import { api } from '@/api/api'
export default {
  data() {
    return {
      formItemLayout: {
        labelCol: { xs: { span: 24 }, sm: { span: 5 } },
        wrapperCol: { xs: { span: 24 }, sm: { span: 17 } }
      },
      title: '新增认知结果',
      visible: false,
      confirmLoading: false,
      activeKey: 'zh-CN',
      templateId: 0,
      constId: null,
      constRules: [{ showOrder: 0 }],
      mdl: {},
      form: this.$form.createForm(this),
      formErrMessage: null,
      errorStatus: null,
      titleMsg: null,
      titleErrorStatus: null,
      showOrderHome: 1
    }
  },

  computed: {
    evalRelation() {
        let arr = []
        if (this.$store.getters.items && this.$store.getters.items.code_eval_relation) {
            arr = this.$store.getters.items.code_eval_relation
        }
        return arr.filter(item => (item.code === '4' || item.code === '0'))
    }
  },

  methods: {
    // 接收父页面数据完成页面初期化
    init(templateId, constId) {
      this.templateId = templateId
      this.visible = true
      // 修改:动态获取显示数据
      if (constId) {
        this.title = '编辑认知结果'
        this.constId = constId
        // HrReptSetConstModelController
        // constType=0:关于认知结果
        api.get('/rept/consts/getConst/' + this.constId + '/' + this.templateId + '/0').then(res => {
            if (res) {
                this.mdl = {}
                this.mdl = res
                this.showOrderHome = res.showOrderHome
                this.constRules = res.constRules
            }
        })
      }
    },

    // 执行保存
    handleSubmit() {
      this.formErrMessage = null
      this.errorStatus = null
      let errorFlag = false
      // 校验计算规则
      this.constRules.forEach((val, index) => {
        if (!val.ruleLeft || !val.ruleRight || !val.computeSign || (!val.ruleValue && val.ruleValue !== 0)) {
          this.formErrMessage = '请输入完整的计算规则'
          this.errorStatus = 'error'
          errorFlag = true
          return false
        }

        if (val.ruleLeft && val.ruleRight && val.ruleLeft === val.ruleRight) {
          this.formErrMessage = '选项1和2不能重复'
          this.errorStatus = 'error'
          errorFlag = true
          return false
      }
      })

      this.form.validateFields((errors, values) => {
        if (!values['constNameZH']) {
          this.titleMsg = '请输入认知结果'
          this.titleErrorStatus = 'error'
        }
        if (!errors && !errorFlag && !this.titleErrorStatus) {
          values['constRules'] = this.constRules
          values['templateId'] = this.templateId
          values['constId'] = this.constId
          values['constType'] = '0' // 0:关于认知结果
          values['showOrderHome'] = this.showOrderHome
          this.confirmLoading = true
          // HrReptSetConstModelController
          api.post('/rept/consts/addOrUpdateConst', values).then(res => {
            this.$message.success(this.global.SAVE_SUCCESS)
            this.confirmLoading = false
            this.handleCancel()
            this.$emit('ok')
          })
        } else {
          this.confirmLoading = false
        }
      })
    },

    // 校验标题
    checkTitle(langMark) {
      this.titleMsg = null
      this.titleErrorStatus = null
      const value = event.target.value
      // { required: true, message: '请输入认知结果' },{ max: 20, message: '最大长度不能超过20' }
      if (value) {
        if (value.length > 20) {
            this.titleMsg = '最大长度不能超过20'
            this.titleErrorStatus = 'error'
            return false
        }
        // 校验查询条件集
        const values = {
          'templateId': this.templateId,
          'constId': this.constId,
          'langMark': langMark,
          'constType': '0',
          'constName': value
        }

        api.post('/rept/consts/checkTitle', values).then(res => {
          if (res === 'NG') {
            this.titleMsg = '该认知结果已被使用,请重新输入'
            this.titleErrorStatus = 'error'
          } else {
            this.titleMsg = null
            this.titleErrorStatus = null
          }
        })
      } else {
          this.titleMsg = '请输入认知结果'
          this.titleErrorStatus = 'error'
          return false
      }
    },

    checkRuleleft(rule) {
      this.formErrMessage = null
      this.errorStatus = null
      if (rule.ruleLeft && rule.ruleRight && rule.ruleLeft === rule.ruleRight) {
          this.formErrMessage = '选项1和2不能重复'
          this.errorStatus = 'error'
          return false
      }
    },

    // 取消
    handleCancel() {
      this.visible = false
    },

    // 切换tab
    changeTab(key) {
      this.activeKey = key
    },

    // 窗口关闭后初期化控件参数
    afterClose() {
      this.form.resetFields()
      this.formErrMessage = null
      this.errorStatus = null
      this.titleMsg = null
      this.titleErrorStatus = null
      this.activeKey = 'zh-CN'
      this.constRules = [{ showOrder: 0 }]
      this.mdl = {}
      this.constId = null
      this.showOrderHome = 1
      this.title = '新增认知结果'
    },

    // 添加元素
    addRuleInterval() {
      const len = this.constRules.length
      this.constRules.push({ showOrder: len })
    },

    // 删除元素
    removeRuleInterval(item) {
      const len = this.constRules.length
      if (len === 1) {
        return
      }
      this.constRules.splice(item.showOrder, 1)
      var newLen = this.constRules.length
      if (item.showOrder !== len - 1) {
        for (var i = 0; i < newLen; i++) {
           const constRules = this.constRules[i]
           constRules.showOrder = i
        }
      }
    }
  }
}
</script>

<style scoped>
.parent >>> .ant-select-disabled .ant-select-selection {
    background: white;
    cursor: not-allowed;
}
.parent >>> .ant-input-number-disabled .ant-input-number-input {
    background: white;
    cursor: not-allowed;
}
.parent >>> .ant-tabs-bar {
    border-bottom: 1px solid #fff;
}
</style>

13,使用a-table去除分页,返回值不需要是Promise
<a-table
style="margin: 0 30px 0 30px;"
ref="table"
:rowKey="record => record.id"
:columns="columns3"
:data-source="loadData3"
:pagination="false"
>
<span slot="computeRule3" slot-scope="text">
    <template>
    <span v-html="text"></span>
    </template>
</span>
<span slot="action3" slot-scope="text, record">
    <template>
    <a @click="$refs.cognitiveBias.create(parseInt(templateId),'edit',record,language)">编辑</a>
    <a style="margin-left:12px;" @click="deleteRecord(record.constId)">删除</a>
    </template>
</span>
</a-table>
columns3: [
        {
          dataIndex: 'constName',
          title: '认知偏差',
          width: '20%'
        },
        {
          dataIndex: 'constDesc',
          title: '说明',
          width: '30%'
        },
        {
          dataIndex: 'computeRule',
          title: '计算规则',
          width: '20%',
          scopedSlots: { customRender: 'computeRule3' }
        },
        {
          dataIndex: 'arithLogic',
          title: '规则间运算逻辑',
          width: '20%',
          customRender: value => {
            return this.getCodeText(value, 'code_arith_logic')
          }
        },
        {
          dataIndex: 'action',
          title: '操作',
          width: '10%',
          scopedSlots: { customRender: 'action3' }
        }
      ],
// 获取列表数据
getGridData() {
	api.get('/rept/consts/getGridData/' + this.templateId).then(res => {
		this.loadData0 = res.constType0
		this.loadData1 = res.constType1
		this.loadData2 = res.constType2
		this.loadData3 = res.constType3
		this.loadData4 = res.constType4
		this.chartData = res.chartList[0].chartData
		this.maxRuleValue = res.chartList[0].maxRuleValue
	})
},
14,表格列样式调整
{
    title: '模块',
    dataIndex: 'moduleName',
    width: '20%',
    customRender: (value, row, index) => {
      const obj = {
        children: value,
        attrs: {}
      }
      obj.attrs.rowSpan = row.normModuleCount
      return obj
    },
    customCell: (record, rowIndex) => {
      const cellStyle =
        'background: #ffffff;text-align: left;border:2px solid #ffffff '
      return { style: cellStyle }
    }
  },
15,九宫格图表展示

在这里插入图片描述

主页面调用

  <div v-if="this.chartData.length > 0" style="height:649px;margin: 41px 30px 0 30px;">
	<div style="margin: 0 0 0px 30px;font-weight: bold;">预览:</div>
	<div style="width:695px; height:592px;margin: 36px auto 0;">
	  <report-abstract-rects :chartData="chartData" :maxRuleValue="maxRuleValue"></report-abstract-rects>
	</div>
  </div>

坐标轴ehr-web\src\views\rept\ReportAbstractRects.vue

注意:表格动态展示,需要在代码中进行computed监控数据的变化

<template>
  <div>
    <div class="myRectangleChartRow">
      <!-- y轴 -->
      <div class="yLabelCol">
        <div class="yLabelT">他评</div>
        <div class="yLabelT" v-for="(item, index) in frames" :key="index">{{ item }}</div>
      </div>
      <!-- 矩阵 -->
      <report-abstract-rectangle-chart
        class="myRectangleChart"
        :chartData="chartData"
        :height="560"
        :width="630"
        :max="max"
        :min="min"
      ></report-abstract-rectangle-chart>
    </div>
    <!-- X轴 -->
    <div class="xLabelRow">
      <div class="xLabel">自评</div>
      <div class="xLabel" v-for="(item, index) in frames" :key="index">{{ item }}</div>
    </div>
  </div>
</template>

<script>
import ReportAbstractRectangleChart from '@/views/rept/ReportAbstractRectangleChart'
export default {
  name: 'ReportAbstractRects',
  components: {
    [ReportAbstractRectangleChart.name]: ReportAbstractRectangleChart
  },
  mounted() {},
  data() {
    return {
      min: 0
    }
  },
//   methods: {},
  computed: {
    // 坐标轴最大值
    max() {
      return this.maxRuleValue
    },
    // 循环坐标数据集
    frames() {
      const frames = []
      const interval = this.max / 5
      frames[0] = Math.round(interval * 100) / 100
      frames[1] = Math.round(interval * 2 * 100) / 100
      frames[2] = Math.round(interval * 3 * 100) / 100
      frames[3] = Math.round(interval * 4 * 100) / 100
      frames[4] = Math.round(this.max * 100) / 100
      return frames
    }
  },

  props: {
    chartData: { type: Array, required: true },
    maxRuleValue: { type: Number, required: true }
  }
}
</script>

<style lang="less" scoped>
.myRectangleChartRow {
  display: inline-flex;
  /* line-height: 30px; */
}
.yLabelCol {
  width: 40px;
  height: 558px;
  display: flex;
  flex-direction: column-reverse;
  justify-content: space-between;
  /* border: 2px solid transparent;
  border-right-color: #096dd9; */
  margin-top: -20px;
}
/* .yLabelCol:before{// 在最前面显示
  content: "\2022";
} */
.yLabelT {
  transform: rotate(-90deg);
}
.xLabelRow {
  width: 623px;
  display: inline-flex;
  justify-content: space-between;
  margin-left: 40px;
  /* border: 2px solid transparent;
  border-top-color: #096dd9; */
}
</style>

画格子src\views\rept\ReportAbstractRectangleChart.vue

实现的原理就是,循环结果集,依据结果集的坐标数据,进行计算div的高度和偏移量实现

<template>
  <div :style="{'height':height+'px','width':width+'px','position':'relative'}">
    <div
      v-for="(item, index) in chartData"
      :key="index"
      :style="{'width':getWidth(item),'height':getHeight(item),
               'top':(max-item[3])/(max-min)*height+1+'px','left':(item[0]-min)/(max-min)*width+1+'px','background-color':item[5]}"
      :class="['rectangle','rectangleImg'+index%5]"
      @touchstart="gtouchstart"
      @touchend="gtouchend"
    >
      <div class="rectangleText1">
        <a-dropdown>
          <a class="rectangleText1">{{ item[4] | areaTextEllipsis((item[1]-item[0])/(max-min)*width-8) }}</a>
          <a-menu slot="overlay">
            <a-menu-item>{{ item[4] }}</a-menu-item>
          </a-menu>
        </a-dropdown>
      </div>
      <!-- <div id="normItemCount" v-if="item[6] && item[7] && item[7].length > 0"> -->
      <div v-if="item[6] && item[7] && item[7].length > 0">
        <a-dropdown>
          <a class="rectangleText2">{{ item[6] }}<span style="font-size:16px"></span></a>
          <a-menu slot="overlay">
            <a-menu-item>{{ item[7] | normArrayEllipsis }}</a-menu-item>
          </a-menu>
        </a-dropdown>
      </div>
    </div>
    <!-- <a-modal
      :mask="true"
      :maskClosable="false"
      :title="areaName"
      :width="600"
      :visible="visible"
      :footer="null"
      @cancel="handleCancel"
    >
      <div class="normItem">
        <table width="100%">
          <tbody>
            <tr height="30px" v-if="normArray.length === 1">
              <td width="100%" align="center">{{ normArray[0] }}</td>
            </tr>
            <tr height="30px" v-if="normArray.length === 2">
              <td width="50%" align="center">{{ normArray[0] }}</td>
              <td width="50%" align="center">{{ normArray[1] }}</td>
            </tr>
            <tr height="30px" v-show="normArray.length >= 3" v-for="(trItem, trIndex) in trCount" :key="'norm' + trIndex">
              <td width="33.33%" align="center">{{ normArray[trIndex * 3 + 0] }}</td>
              <td width="33.33%" align="center">{{ normArray[trIndex * 3 + 1] }}</td>
              <td width="33.33%" align="center">{{ normArray[trIndex * 3 + 2] }}</td>
            </tr>
          </tbody>
        </table>
      </div>
    </a-modal> -->
  </div>
</template>

<script>
export default {
  name: 'ReportAbstractRectangleChart',
  components: {
  },
  props: {
    chartData: { type: Array, required: true },
    height: { type: Number, required: true },
    width: { type: Number, required: true },
    max: { type: Number, required: true },
    min: { type: Number, required: true }
  },
  data() {
    return {
      startX: 0,
      startY: 0
      // areaName: '',
      // normArray: [],
      // trCount: 0,
      // visible: false
    }
  },
  filters: {
    normArrayEllipsis(normArray) {
      var itemStr = ''
      normArray.forEach((item, index) => {
        itemStr = itemStr + item + ' | '
      })
      return itemStr.substring(0, itemStr.length - 2)
    },
    areaTextEllipsis(value, divWidth) {
      // 文字整体所占宽度
      var fontWidth = value.length * 16
      // 如果div宽度大于整体宽度则原值返回,否则缩进...
      if (divWidth >= fontWidth) {
        return value
      } else {
        // 计算文字缩进偏移量
        var offset = parseInt((fontWidth - divWidth) / 16) + 2
        return value.substring(0, value.length - offset) + '...'
      }
    }
  },
  methods: {
    gtouchstart(e) {
      this.startY = e.changedTouches[0].pageY
      this.startX = e.changedTouches[0].pageX
    },
    gtouchend(e) {
      if (
        this.startY === e.changedTouches[0].pageY &&
        this.startX === e.changedTouches[0].pageX
      ) {
        this.showDetailFunction()
      } else {

      }
    },
    showDetailFunction() {
      this.$parent.showDetailFunction() // TODO:
    },
    // showNorms (title, normArray) {
    //   if (normArray.length === 0) {
    //     return
    //   }
    //   this.trCount = parseInt(normArray.length / 3) + 1
    //   this.areaName = title
    //   this.normArray = normArray
    //   this.visible = true
    // },
    handleCancel() {
      this.visible = false
    },
    getWidth(item) {
      if ((item[1] - item[0]) <= 0) {
        return '4px'
      }
      const width = (item[1] - item[0]) / (this.max - this.min) * this.width - 8 + 'px'
      return width
    },
    getHeight(item) {
      if ((item[3] - item[2]) <= 0) {
        return '4px'
      }
      const height = (item[3] - item[2]) / (this.max - this.min) * this.height - 8 + 'px'
      return height
    }
  }
}
</script>
<style lang="less" scoped>
.rectangle {
  border-radius: 8px;
  position: absolute;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
}
.rectangleText1 {
  color: white;
  font-size: 16px;
  font-weight: bold;
  white-space: nowrap;
}
.rectangleText2 {
  color: white;
  font-size: 24px;
  line-height: 1;
  font-family: 'NeoSansStd-Light', 'Segoe UI', SegoeUI, "Helvetica Neue", Helvetica, Arial, sans-serif;
  font-weight: lighter;
  font-style: normal;
}
.rectangleImg5 {
  transform: rotate(180deg);
  position: absolute;
  opacity: 0.05;
  left: 10%;
  top: 5%;
}
.normItem {
  width: 90%;
  margin: 20px 26px;
  background-color: rgb(247, 248, 249);
}
// #normItemCount {
//   cursor: pointer;
// }
</style>
16,封面图
父页面
this.$router.push({
    name: 'coverInfo',
    query: {
    setModuleId: poolItem.setModuleId,
    templateId: this.templateId,
    isExistSubjective: this.isExistSubjective,
    modeId: this.modeId
    }
})

coverInfo

<template>
  <div style="background: #ffffff">
    <a-spin :spinning="confirmLoading">
      <div class="cover_title">
        <p style="margin-left: 22px;">参数设置</p>
      </div>
      <a-form :form="form">
        <a-form-item label="封面图片" :labelCol="labelCol" :wrapperCol="wrapperCol" style="left:9%;font-weight:bold">
          <div class="cover_tip">
            <p>(适用于PDF版本报告)</p>
          </div>
          <div>
            <img v-if="coverInfo.imgUrl" :src="coverInfo.imgUrl" alt="cover" style="width:380px;height:213px;" />
          </div>
          <div style="padding-left:148px;font-size:14px;font-weight:normal">
            <a @click="$refs.coverModal.edit(coverInfo)">重新选择图片</a>
          </div>
        </a-form-item>
        <a-form-item
          label="展示信息"
          :labelCol="labelCol"
          :wrapperCol="wrapperCol"
          style="left:9%;top:-20px;font-weight:bold"
        >
          <div style="padding-left: 140px;">
            <div v-for="(item, index) in coverExts" :key="index" style="line-height:10px;margin-top:14px;">
              <p>{{ item.showComment }}</p>
            </div>
            <div>
              <a @click="editShowInfo" style="padding-left: 8px;font-size:14px;font-weight:normal">展示编辑字段</a>
            </div>
          </div>
        </a-form-item>
        <a-divider style="min-width: 10%;width: 35%;margin: 24px 425px;"></a-divider>
        <a-form-item :wrapperCol="{ span: 24 }" style="text-align: center">
          <a-button htmlType="submit" icon="check-circle" type="primary" @click="this.handleSubmit">{{
            this.global.BTN.SAVE
          }}</a-button>
          <a-button style="margin-left: 8px" icon="close-circle" @click="this.handleCancel">{{
            this.global.BTN.CANCEL
          }}</a-button>
        </a-form-item>
      </a-form>
    </a-spin>
    <div>
      <edit-show-info ref="editShowInfo" @ok="ok" />
      <cover-modal ref="coverModal" @ok="coverOk"></cover-modal>
    </div>
  </div>
</template>
<script>
import { api } from '@/api/api'
import EditShowInfo from './EditShowInfo'
import CoverModal from './modules/CoverModal'
export default {
  data() {
    return {
      labelCol: {
        xs: { span: 24 },
        sm: { span: 7 }
      },
      wrapperCol: {
        xs: { span: 24 },
        sm: { span: 7 }
      },
      form: this.$form.createForm(this),
      confirmLoading: false,
      fileList: [],
      uploading: false,
      loading: false,
      imageUrl: '',
      setModuleId: '',
      coverInfo: {},
      coverExts: [],
      tempCoverExts: [],
      templateId: 0,
      isExistSubjective: 0,
      modeId: 0
    }
  },
  components: {
    EditShowInfo,
    CoverModal
  },
  created() {
    this.setModuleId = this.$route.query.setModuleId
    this.getCoverInfo()
    this.templateId = this.$route.query.templateId
    this.isExistSubjective = this.$route.query.isExistSubjective
    this.modeId = this.$route.query.modeId
  },
  methods: {
    handleSubmit() {
      const {
        form: { validateFields }
      } = this
      this.confirmLoading = true
      validateFields((errors, values) => {
        if (!errors) {
          //   const
          api.post('/rept/cover/add', this.coverInfo).then(res => {
            const cover = { id: this.coverInfo.id, setCoverExts: this.coverExts }
            api.post('rept/coverExt/add', cover).then(res => {
              this.$message.success(this.global.SAVE_SUCCESS)
              this.confirmLoading = false
              this.$router.replace({
                name: 'evaluationTemplateSettings',
                params: {
                  templateId: this.templateId,
                  modeId: this.modeId,
                  isExistSubjective: this.isExistSubjective,
                  tabKey: '4'
                }
              })
              this.$router.go(-1)
            })
          })
        } else {
          this.$message.error(this.global.SAVE_FAIL)
          this.confirmLoading = false
        }
      })
    },
    handleCancel() {
      this.$router.replace({
        name: 'evaluationTemplateSettings',
        params: {
          templateId: this.templateId,
          modeId: this.modeId,
          isExistSubjective: this.isExistSubjective,
          tabKey: '4'
        }
      })
      this.$router.go(-1)
    },
    editShowInfo() {
      this.tempCoverExts = this.coverExts.slice(0, this.coverExts.length - 1)
      const param = { coverInfo: this.coverInfo, coverExts: this.tempCoverExts }
      console.log(param)
      this.$refs.editShowInfo.edit(param)
    },
    ok(arr) {
      this.coverExts = Object.assign([], arr)
    },
    getCoverInfo() {
      api.get('/rept/cover/info/' + this.setModuleId).then(res => {
        this.coverInfo = res
        api.get('/rept/coverExt/list/' + res.id).then(res2 => {
          this.coverExts = res2
        })
      })
    },
    coverOk(imgUrl) {
      this.coverInfo.imgUrl = imgUrl
    }
  }
}
</script>
<style scoped>
.cover_title {
  font-size: 16px;
  font-weight: bold;
  text-align: left;
  color: #333333;
  margin-left: 54px;
  padding-top: 30px;
}
.cover_tip {
  background: #f2f5ff;
  width: 369px;
  /* font-size: 14px; */
  text-align: center;
  color: #666666;
  margin-left: 5px;
}
</style>

src\views\evaluation\setting\modules\CoverModal.vue

<template>

  <a-modal
    title="编辑封面"
    :visible="visible"
    :maskClosable="false"
    :confirmLoading="confirmLoading"
    :width="800"
    :footer="null"
    @cancel="cancelHandel">
    <a-row>
      <a-col :xs="24" :md="24" :style="{height: '400px'}">
        <vue-cropper
          ref="cropper"
          :img="options.img"
          :info="true"
          :autoCrop="options.autoCrop"
          :autoCropWidth="options.autoCropWidth"
          :autoCropHeight="options.autoCropHeight"
          :fixedBox="options.fixedBox"
          @realTime="realTime"
        >
        </vue-cropper>
      </a-col>
    </a-row>
    <br>
    <a-row>
      <a-col :lg="2" :md="2">
        <a-upload name="file" :beforeUpload="beforeUpload" :showUploadList="false">
          <a-button icon="upload">选择图片</a-button>
        </a-upload>
      </a-col>
      <a-col :lg="{span: 1, offset: 2}" :md="2">
        <a-button icon="plus" @click="changeScale(1)"/>
      </a-col>
      <a-col :lg="{span: 1, offset: 1}" :md="2">
        <a-button icon="minus" @click="changeScale(-1)"/>
      </a-col>
      <a-col :lg="{span: 1, offset: 1}" :md="2">
        <a-button icon="undo" @click="rotateLeft"/>
      </a-col>
      <a-col :lg="{span: 1, offset: 1}" :md="2">
        <a-button icon="redo" @click="rotateRight"/>
      </a-col>
      <a-col :lg="{span: 2, offset: 6}" :md="2">
        <a-button type="primary" @click="finish('blob')" :disabled="isDisable">保存</a-button>
      </a-col>
    </a-row>
  </a-modal>

</template>
<script>
import { VueCropper } from 'vue-cropper'
import { ossAxios } from '@/utils/request'

export default {
  components: {
    VueCropper
  },
  data () {
    return {
      visible: false,
      id: null,
      confirmLoading: false,
      fileList: [],
      uploading: false,
      options: {
        // img: 'https://zos.alipayobjects.com/rmsportal/jkjgkEfvpUPVyRjUImniVslZfWPnJuuZ.png',
        img: '',
        autoCrop: true,
        autoCropWidth: 600,
        autoCropHeight: 400,
        fixedBox: false
      },
      previews: {},
      fileName: '',
      isDisable: false
    }
  },
  methods: {
    edit (id) {
      this.visible = true
      this.id = id
      this.isDisable = false
      /* 获取原始头像 */
      this.options.img = null
    },
    close () {
      this.id = null
      this.visible = false
    },
    cancelHandel () {
      this.close()
    },
    changeScale (num) {
      num = num || 1
      this.$refs.cropper.changeScale(num)
    },
    rotateLeft () {
      this.$refs.cropper.rotateLeft()
    },
    rotateRight () {
      this.$refs.cropper.rotateRight()
    },
    beforeUpload (file) {
      const isJpgOrPng = file.type === 'image/jpeg' || file.type === 'image/png'
      if (!isJpgOrPng) {
        this.$message.error('只能上传图片!')
        return
      }
      this.fileName = file.name
      const reader = new FileReader()
      // 把Array Buffer转化为blob 如果是base64不需要
      // 转化为base64
      reader.readAsDataURL(file)
      reader.onload = () => {
        this.options.img = reader.result
      }
      // 转化为blob
      // reader.readAsArrayBuffer(file)

      return false
    },

    // 上传图片(点击上传按钮)
    finish (type) {
      if (!this.options.img) {
        this.$message.error('请选择上传图片!')
        return
      }
      this.isDisable = true
      console.log('finish')
      const formData = new FormData()
      const _this = this
      // 输出
      if (type === 'blob') {
        this.$refs.cropper.getCropBlob((data) => {
          const img = window.URL.createObjectURL(data)
          this.model = true
          this.modelSrc = img
          formData.append('file', data, this.fileName)
          ossAxios.post('', formData).then(res => {
            const data = res.data
            if (data.code === 200) {
              _this.$message.success('上传成功')
              _this.$emit('ok', '/files/' + data.data.fileId + '/preview/img')
              _this.visible = false
            } else {
              this.isDisable = false
              this.uploading = false
              this.$message.error('上传失败')
            }
          })
        })
      } else {
        this.$refs.cropper.getCropData((data) => {
          this.model = true
          this.modelSrc = data
        })
      }
    },
    okHandel () {
      const vm = this

      vm.confirmLoading = true
      setTimeout(() => {
        vm.confirmLoading = false
        vm.close()
        vm.$message.success('上传头像成功')
      }, 2000)
    },

    realTime (data) {
      this.previews = data
    }
  }
}
</script>

<style lang="less" scoped>

  .avatar-upload-preview {
    position: absolute;
    top: 50%;
    transform: translate(50%, -50%);
    width: 180px;
    height: 180px;
    border-radius: 50%;
    box-shadow: 0 0 4px #ccc;
    overflow: hidden;

    img {
      width: 100%;
      height: 100%;
    }
  }
</style>

17,Vue 中使用$router在新页面打开
<router-link target="_blank" />
let href = this.$router.resolve({name: '', params: {}}) window.open(href.href, '_blank');
showPersonalReport(record) {
    var releaseTime = record.releaseTime
    if (releaseTime === undefined || releaseTime === null) {
    releaseTime = parseInt((new Date()).getTime())
    }
    const routeData = this.$router.resolve({
    name: 'PersonalReport',
    params: {
        reportDetailsId: record.detailsId,
        reportReleaseTime: releaseTime,
        searchType: '1'
    }
    })
    window.open(routeData.href, '_blank')
},
18,动态加载CSS
<div style="width:305px;" :class="{'guide_desc_btn': guide.guideType !== '8'}">
  <a class="guide_a" @click="$refs.type1.init('edit', guide, setModuleId)">编辑</a>
  <a class="guide_a" @click="delGuide(guide.guideId)">删除</a>
  <a v-show="index < guides.length - 1" class="guide_a" @click="moveDown(index)">下移</a>
  <a v-show="index > 0" class="guide_a" @click="moveUp(index)">上移</a>
</div>
19,文本域显示

在这里插入图片描述

<div v-if="guide.guideType !== '8'" style="width:100%; overflow: hidden; word-wrap:break-word;">
<a-textarea
    style="overflow-y:hidden;border: none;resize: none;cursor: pointer;box-shadow: none;padding:0px 0px 0px 0px;font-weight:normal;font-family:PingFang SC Medium;font-size:14px;background: #f7f8f9;color:#666666"
    readonly
    :autosize="true"
    :value="guide.guideDescribe"
/>
</div>
<div v-else style="width:100%; overflow: hidden; word-wrap:break-word;">
<span v-html="changeCentent(guide.guideDescribe)"></span>
</div>

完整vue

<template>
  <div class="parent_div">
    <a-dropdown style="position: relative; top: 31px;width: 112px;z-index:1">
      <a-button type="primary" icon="plus">新建内容<a-icon type="down"/></a-button>
      <a-menu slot="overlay" @click="handleMenuClick">
        <a-menu-item key="1"><a style="margin:0 auto;">系统预置模块</a></a-menu-item>
        <a-menu-item key="2"><a style="margin:0 auto;">自定义模块</a></a-menu-item>
      </a-menu>
    </a-dropdown>
    <a-tabs defaultActiveKey="1" :tabBarStyle="{ textAlign: 'right', borderBottom: 'set' }">
      <a-tab-pane tab="中文" key="1">
        <div style="margin-bottom: 20px;">
          <img class="image_div" src="@/image/pen.png"/>
          <span class="image_title">阅读指南参数设置</span>
        </div>
        <div class="guide" v-for="(guide, index) in guides" :key="index">
          <div class="guide_title">
            <span>{{ guide.guideTitle }}</span>
          </div>
          <div class="guide_desc">
            <div style="display: flex;">
              <div v-if="guide.guideType !== '8'" style="width:100%; overflow: hidden; word-wrap:break-word;">
                <a-textarea
                  style="overflow-y:hidden;border: none;resize: none;cursor: pointer;box-shadow: none;padding:0px 0px 0px 0px;font-weight:normal;font-family:PingFang SC Medium;font-size:14px;background: #f7f8f9;color:#666666"
                  readonly
                  :autosize="true"
                  :value="guide.guideDescribe"
                />
              </div>
              <div v-else style="width:100%; overflow: hidden; word-wrap:break-word;">
                <span v-html="changeCentent(guide.guideDescribe)"></span>
              </div>
              <div style="width:305px;" :class="{'guide_desc_btn': guide.guideType !== '8'}">
                <a class="guide_a" @click="$refs.type1.init('edit', guide, setModuleId)">编辑</a>
                <a class="guide_a" @click="delGuide(guide.guideId)">删除</a>
                <a v-show="index < guides.length - 1" class="guide_a" @click="moveDown(index)">下移</a>
                <a v-show="index > 0" class="guide_a" @click="moveUp(index)">上移</a>
              </div>
            </div>
            <div v-if="guide.guideType === '7' && guide.isShow && guide.isShow===1" style="width:75%;">
              <a-table :rowKey="record => record.id" :columns="columns" :data-source="loadData" :pagination="false" bordered></a-table>
            </div>
          </div>
        </div>
      </a-tab-pane>
    </a-tabs>
    <modify-guide ref="type1" @ok="handleOk"></modify-guide>
    <lib-guide ref="type2" @libGuide="libGuide"></lib-guide>
  </div>
</template>
<script>
import { api } from '@/api/api'
import ModifyGuide from '@/views/evaluation/setting/modules/ModifyGuide'
import LibGuide from '@/views/evaluation/setting/modules/LibGuide'
import VueUeditorWrap from 'vue-ueditor-wrap'

export default {
  data() {
    return {
      setModuleId: 0,
      guides: [],
      guidesJP: [],
      guidesUS: [],
      loadData: [],
      columns: [
        {
          dataIndex: 'cell1',
          title: '评价角色'
        },
        {
          dataIndex: 'cell2',
          title: '发送评价邀请数'
        },
        {
          dataIndex: 'cell3',
          title: '有效作答数'
        },
        {
          dataIndex: 'cell4',
          title: '有效作答占比'
        }
      ]
    }
  },
  created() {
    this.setModuleId = this.$route.query.setModuleId
    this.initMethod()
    this.loadData = [
        { 'id': 1, 'cell1': '自己', 'cell2': '', 'cell3': '', 'cell4': '' },
        { 'id': 2, 'cell1': '上级', 'cell2': '', 'cell3': '', 'cell4': '' },
        { 'id': 3, 'cell1': '同级', 'cell2': '', 'cell3': '', 'cell4': '' },
        { 'id': 4, 'cell1': '下级', 'cell2': '', 'cell3': '', 'cell4': '' }
    ]
  },
  methods: {
    initMethod() {
      // HrReptSetGuideController
      api.get('/rept/guides/' + this.setModuleId).then(res => {
        if (!res) {
          this.guides = []
          this.guidesJP = []
          this.guidesUS = []
        } else {
          this.guides = res['zh-CN']
          this.guidesJP = res['ja-JP']
          this.guidesUS = res['en-US']
        }
      })
    },

    // 替换换行符
    changeCentent(content) {
      if (!content) {
        return content
      }
      return content.replace(/\n/g, '<br>')
    },

    // 向上移动
    moveUp(index) {
        const guide = this.guides[index]
        const nestGuide = this.guides[index - 1]
        // /moveDown/{guideId}/{guideOrder}/{nextGuideId}/{nextGuideOrder}
        api.put('/rept/guides/moveUp/' + guide.guideId + '/' + guide.guideOrder + '/' +
        nestGuide.guideId + '/' + nestGuide.guideOrder + '/' + this.setModuleId).then(res => {
            this.initMethod()
        })
    },

    // 向下移动
    moveDown(index) {
        const guide = this.guides[index]
        const nestGuide = this.guides[index + 1]
        api.put('/rept/guides/moveDown/' + guide.guideId + '/' + guide.guideOrder + '/' +
         nestGuide.guideId + '/' + nestGuide.guideOrder + '/' + this.setModuleId).then(res => {
            this.initMethod()
        })
    },

    // 删除操作
    delGuide(guideId) {
      this.$confirm({
        title: this.global.PROMPT,
        content: this.global.DEL_CONFIRM,
        okText: this.global.BTN.OK,
        cancelText: this.global.BTN.CANCEL,
        onOk: () => {
          api.delete('/rept/guides/delete/' + guideId + '/' + this.setModuleId).then(res => {
            this.$message.success(this.global.DEL_SUCCESS)
            this.initMethod()
          })
        },
        onCancel() {}
      })
    },

    // 点击菜单
    handleMenuClick(data) {
      if (data.key === '1') { // 系统模板
        this.$refs.type2.init(this.setModuleId)
      } else {
        this.$refs.type1.init('add', null, this.setModuleId)
      }
    },

    // 子页面回调方法
    handleOk () {
      this.initMethod()
    },

    // 系统调用模板下一步返回参数
    libGuide (libguide) {
      this.$refs.type1.init('libEdit', libguide, this.setModuleId)
    }
  },

  components: {
    ModifyGuide,
    LibGuide,
    VueUeditorWrap
  }
}
</script>
<style scoped>
  * {
    padding: 0;
    margin:0;
  }
  .parent_div {
    width: 98%;
    position: relative;
    left: 1%;
    top: -13px;
  }
  .parent_div >>> .ant-tabs-bar {
    border-bottom: 1px solid #fff;
  }
  .image_div {
    width: 23px;
    height: 23px;
  }
  .image_title {
    font-size: 16px;
    font-weight: bold;
    color: #333333;
    position: relative;
    left: 10px;
    top: 4px;
  }
  .guide{
    border-left: 4px solid #E9E9EB;
    padding: 20px 14px 20px 32px;
    background-color: #F7F8F9;
    margin-bottom: 8px;
    display: flex;
  }
  .guide_title{
    font-size: 15px;
    color: #666666;
    font-weight: bold;
    width:200px;
  }
  .guide_desc{
    width:100%;
    overflow: hidden;
  }
  .guide_a{
    margin-left: 25px;
    font-size: 15px;
    color: #3B8FDA;
  }
  .parent_div a:hover{
    cursor:pointer
  }
  .guide_desc_btn{
    display: flex;
    align-items: center;
  }
</style>

二、springboot后端

1,解析公式(后缀式栈)

实现如下功能:
// 执行公式,计算分数:如4.5+2 => 6.5

ArithUtils.java

package com.neusoft.ehr.eval.util;

/**
 * 精确计算工具类
 * 
 * @author shao.hw
 * @since 2020-04-18
 */
public class ArithUtils {
    // 默认除法运算精度
    private static final int DEF_DIV_SCALE = 16;

    // 这个类不能实例化
    private ArithUtils() {
    }

    /**
     * 提供精确的加法运算。
     *
     * @param v1 被加数
     * @param v2 加数
     * @return 两个参数的和
     */

    public static double add(double v1, double v2) {
        java.math.BigDecimal b1 = new java.math.BigDecimal(Double.toString(v1));
        java.math.BigDecimal b2 = new java.math.BigDecimal(Double.toString(v2));
        return b1.add(b2).doubleValue();
    }

    public static double add(String v1, String v2) {
        java.math.BigDecimal b1 = new java.math.BigDecimal(v1);
        java.math.BigDecimal b2 = new java.math.BigDecimal(v2);
        return b1.add(b2).doubleValue();
    }

    /**
     * 提供精确的减法运算。
     *
     * @param v1 被减数
     * @param v2 减数
     * @return 两个参数的差
     */

    public static double sub(double v1, double v2) {
        java.math.BigDecimal b1 = new java.math.BigDecimal(Double.toString(v1));
        java.math.BigDecimal b2 = new java.math.BigDecimal(Double.toString(v2));
        return b1.subtract(b2).doubleValue();
    }

    public static double sub(String v1, String v2) {
        java.math.BigDecimal b1 = new java.math.BigDecimal(v1);
        java.math.BigDecimal b2 = new java.math.BigDecimal(v2);
        return b1.subtract(b2).doubleValue();
    }

    /**
     * 提供精确的乘法运算。
     *
     * @param v1
     *            被乘数
     * @param v2
     *            乘数
     * @return 两个参数的积
     */

    public static double mul(double v1, double v2) {
        java.math.BigDecimal b1 = new java.math.BigDecimal(Double.toString(v1));
        java.math.BigDecimal b2 = new java.math.BigDecimal(Double.toString(v2));
        return b1.multiply(b2).doubleValue();
    }

    public static double mul(String v1, String v2) {
        java.math.BigDecimal b1 = new java.math.BigDecimal(v1);
        java.math.BigDecimal b2 = new java.math.BigDecimal(v2);
        return b1.multiply(b2).doubleValue();
    }

    /**
     * 提供(相对)精确的除法运算,当发生除不尽的情况时,精确到 小数点以后10位,以后的数字四舍五入。
     *
     * @param v1
     *            被除数
     * @param v2
     *            除数
     * @return 两个参数的商
     */

    public static double div(double v1, double v2) {
        return div(v1, v2, DEF_DIV_SCALE);
    }

    public static double div(String v1, String v2) {
        java.math.BigDecimal b1 = new java.math.BigDecimal(v1);
        java.math.BigDecimal b2 = new java.math.BigDecimal(v2);
        return b1.divide(b2, DEF_DIV_SCALE, java.math.BigDecimal.ROUND_HALF_UP).doubleValue();
    }

    /**
     * 提供(相对)精确的除法运算。当发生除不尽的情况时,由scale参数指 定精度,以后的数字四舍五入。
     *
     * @param v1 被除数
     * @param v2 除数
     * @param scale 表示表示需要精确到小数点以后几位。
     * @return 两个参数的商
     */

    public static double div(double v1, double v2, int scale) {
        if (scale < 0) {
            throw new IllegalArgumentException("The   scale   must   be   a   positive   integer   or   zero");
        }
        java.math.BigDecimal b1 = new java.math.BigDecimal(Double.toString(v1));
        java.math.BigDecimal b2 = new java.math.BigDecimal(Double.toString(v2));
        return b1.divide(b2, scale, java.math.BigDecimal.ROUND_HALF_UP).doubleValue();
    }

    /**
     * 提供精确的小数位四舍五入处理。
     *
     * @param v 需要四舍五入的数字
     * @param scale 小数点后保留几位
     * @return 四舍五入后的结果
     */

    public static double round(double v, int scale) {
        if (scale < 0) {
            throw new IllegalArgumentException("The   scale   must   be   a   positive   integer   or   zero");
        }
        java.math.BigDecimal b = new java.math.BigDecimal(Double.toString(v));
        java.math.BigDecimal one = new java.math.BigDecimal("1");
        return b.divide(one, scale, java.math.BigDecimal.ROUND_HALF_UP).doubleValue();
    }

    public static double round(String v, int scale) {
        if (scale < 0) {
            throw new IllegalArgumentException("The   scale   must   be   a   positive   integer   or   zero");
        }
        java.math.BigDecimal b = new java.math.BigDecimal(v);
        java.math.BigDecimal one = new java.math.BigDecimal("1");
        return b.divide(one, scale, java.math.BigDecimal.ROUND_HALF_UP).doubleValue();
    }
}

CalculatorUtils.java

package com.neusoft.ehr.eval.util;

import java.math.BigDecimal;
import java.util.Collections;
import java.util.Stack;

/**
 *  算数表达式求值 
 *  直接调用Calculator的类方法conversion() 
 *  传入算数表达式,将返回一个BigDecimal结果,保留四位小数
 *  如果计算过程错误,将返回 -1
 *  
 *  @author shao.hw
 *  @since 2020-04-18
 */
public class CalculatorUtils {
    private Stack<String> postfixStack = new Stack<String>();// 后缀式栈
    private Stack<Character> opStack = new Stack<Character>();// 运算符栈
    private int[] operatPriority = new int[] { 0, 3, 2, 1, -1, 1, 0, 2 };// 运用运算符ASCII码-40做索引的运算符优先级

    public static BigDecimal conversion(String expression) {
        BigDecimal result = BigDecimal.ZERO;
        CalculatorUtils cal = new CalculatorUtils();
        try {
            result = cal.calculate(expression);
        } catch (Exception e) {
            // e.printStackTrace();
            // 运算错误返回-1
            return BigDecimal.valueOf(-1);
        }
        return result.setScale(4,BigDecimal.ROUND_HALF_UP);
    }

    /**
     * 按照给定的表达式计算
     *
     * @param expression
     *            要计算的表达式例如:5+12*(3+5)/7
     * @return
     */
    public BigDecimal calculate(String expression) {
        Stack<String> resultStack = new Stack<String>();
        prepare(expression);
        Collections.reverse(postfixStack);// 将后缀式栈反转
        String firstValue, secondValue, currentValue;// 参与计算的第一个值,第二个值和算术运算符
        while (!postfixStack.isEmpty()) {
            currentValue = postfixStack.pop();
            if (!isOperator(currentValue.charAt(0))) {// 如果不是运算符则存入操作数栈中
                currentValue = currentValue.replace("~", "-");
                resultStack.push(currentValue);
            } else {// 如果是运算符则从操作数栈中取两个值和该数值一起参与运算
                secondValue = resultStack.pop();
                firstValue = resultStack.pop();

                // 将负数标记符改为负号
                firstValue = firstValue.replace("~", "-");
                secondValue = secondValue.replace("~", "-");

                String tempResult = calculate(firstValue, secondValue, currentValue.charAt(0));
                resultStack.push(tempResult);
            }
        }
        
        return BigDecimal.valueOf(Double.valueOf(resultStack.pop()));
    }

    /**
     * 数据准备阶段将表达式转换成为后缀式栈
     * 
     * @param expression
     */
    private void prepare(String expression) {
        opStack.push(',');// 运算符放入栈底元素逗号,此符号优先级最低
        char[] arr = expression.toCharArray();
        int currentIndex = 0;// 当前字符的位置
        int count = 0;// 上次算术运算符到本次算术运算符的字符的长度便于或者之间的数值
        char currentOp, peekOp;// 当前操作符和栈顶操作符
        for (int i = 0; i < arr.length; i++) {
            currentOp = arr[i];
            if (isOperator(currentOp)) {// 如果当前字符是运算符
                if (count > 0) {
                    postfixStack.push(new String(arr, currentIndex, count));// 取两个运算符之间的数字
                }
                peekOp = opStack.peek();
                if (currentOp == ')') {// 遇到反括号则将运算符栈中的元素移除到后缀式栈中直到遇到左括号
                    while (opStack.peek() != '(') {
                        postfixStack.push(String.valueOf(opStack.pop()));
                    }
                    opStack.pop();
                } else {
                    while (currentOp != '(' && peekOp != ',' && compare(currentOp, peekOp)) {
                        postfixStack.push(String.valueOf(opStack.pop()));
                        peekOp = opStack.peek();
                    }
                    opStack.push(currentOp);
                }
                count = 0;
                currentIndex = i + 1;
            } else {
                count++;
            }
        }
        if (count > 1 || (count == 1 && !isOperator(arr[currentIndex]))) {// 最后一个字符不是括号或者其他运算符的则加入后缀式栈中
            postfixStack.push(new String(arr, currentIndex, count));
        }

        while (opStack.peek() != ',') {
            postfixStack.push(String.valueOf(opStack.pop()));// 将操作符栈中的剩余的元素添加到后缀式栈中
        }
    }

    /**
     * 判断是否为算术符号
     *
     * @param c
     * @return
     */
    private boolean isOperator(char c) {
        return c == '+' || c == '-' || c == '*' || c == '/' || c == '(' || c == ')';
    }

    /**
     * 利用ASCII码-40做下标去算术符号优先级
     *
     * @param cur
     * @param peek
     * @return
     */
    public boolean compare(char cur, char peek) {// 如果是peek优先级高于cur,返回true,默认都是peek优先级要低
        boolean result = false;
        if (operatPriority[(peek) - 40] >= operatPriority[(cur) - 40]) {
            result = true;
        }
        return result;
    }

    /**
     * 按照给定的算术运算符做计算
     *
     * @param firstValue
     * @param secondValue
     * @param currentOp
     * @return
     */
    private String calculate(String firstValue, String secondValue, char currentOp) {
        String result = "";
        switch (currentOp) {
        case '+':
            result = String.valueOf(ArithUtils.add(firstValue, secondValue));
            break;
        case '-':
            result = String.valueOf(ArithUtils.sub(firstValue, secondValue));
            break;
        case '*':
            result = String.valueOf(ArithUtils.mul(firstValue, secondValue));
            break;
        case '/':
            result = String.valueOf(ArithUtils.div(firstValue, secondValue));
            break;
        }
        return result;
    }
}

// 执行公式,计算分数:如4.5+2 => 6.5
BigDecimal realityScore = CalculatorUtils.conversion(calcFormula);
if(BigDecimal.valueOf(-1).compareTo(realityScore) == 0) {
   log.error("指标计算出错:"+ map.getKey() + ";normId:"+normVo.getNormId()+";计算公式:"+normVo.getCalcFormula() +"calcFormula:"+ calcFormula + ";realityScore:"+realityScore);
}
2,StrSubstitutor模板字符替换
  1. 直接替换系统属性值

    StrSubstitutor.replaceSystemProperties(
        "You are running with java.version = ${java.version} and os.name = ${os.name}.");
    

  2. 使用Map替换字符串中的占位符

    Map valuesMap = HashMap();
    valuesMap.put("animal", "quick brown fox");
    valuesMap.put("target", "lazy dog");
    String templateString = "The ${animal} jumped over the ${target}.";
    
    StrSubstitutor sub = new StrSubstitutor(valuesMap);
    String resolvedString = sub.replace(templateString);
    
    // resolvedString的結果:The quick brown fox jumped over the lazy dog.
    

  3. StrSubstitutor会递归地替换变量,比如

    Map<String, Object> params = Maps.newHashMap();
    params.put("name", "${x}");
    params.put("x", "y");
    StrSubstitutor strSubstitutor = new StrSubstitutor(params);
    
    String hello2 = "${name}";
    System.out.println(strSubstitutor.replace(hello2));
    // 最后会输出:y
    

  4. 有时变量内还嵌套其它变量,这个StrSubstitutor也是支持的,要调用setEnableSubstitutionInVariables

    Map<String, Object> params = Maps.newHashMap();
    params.put("jre-1.8", "java-version-1.8");
    params.put("java.specification.version", "1.8");
    StrSubstitutor strSubstitutor = new StrSubstitutor(params);
               
    strSubstitutor.setEnableSubstitutionInVariables(true);
    System.out.println(strSubstitutor.replace("${jre-${java.specification.version}}"));
    // java-version-1.83,自动生成代码配置文件
    
3,自动生成代码配置文件
<!--         <dependency> -->
<!--             <groupId>com.baomidou</groupId> -->
<!--             <artifactId>mybatis-plus-generator</artifactId> -->
<!--             <version>3.1.2</version> -->
<!--         </dependency> -->
<!--    <dependency> -->
<!--       <groupId>org.freemarker</groupId> -->
<!--        <artifactId>freemarker</artifactId> -->
<!--   </dependency> -->
4,mybatis使用
1)正则表达式

在这里插入图片描述

匹配数字:

AND t1.score REGEXP '^[0-9]*.?[0-9]$'
2)查询map
@MapKey("sumLogic")
Map<Integer, Map<String, Integer>> getSumLogicMap(@Param("params") Map<String, Object> basicInfo);

xml

<select id="getSumLogicMap" resultType="java.util.HashMap">
        SELECT
            t.logic_type sumLogic,
            t.mode_type sumMode
        FROM
            hr_eval_activity_calc t
        WHERE
            t.is_deleted = 0
        AND t.template_id = #{params.templateId}
        AND t.rule_id = #{params.ruleId} 
        AND t.activity_id = #{params.activityId}
        <!-- 0:各层级得分汇总逻辑,1:他评汇总逻辑 -->
        AND t.logic_type in (0, 1)
    </select>
3)xml中条件判断
<if test="param.dimension != null and param.dimension != ''">
    and t.dimension_id = #{param.dimension}
</if>

<include refid="selectSql"/>

<when test="type!= null and type=='module'"><choose>
            <when test="type!= null and type=='template'">
                union all
                select '评估模板' title, 'templateName' dataIndex from dual
            </when>
            <when test="type!= null and type=='module'">
                union all
                select '评估模板' title, 'templateName' dataIndex from dual
                union all
                select '评估模块' title, 'moduleName' dataIndex from dual
            </when>
            <when test="type!= null and type=='dimension'">
                union all
                select '评估模板' title, 'templateName' dataIndex from dual
                union all
                select '评估模块' title, 'moduleName' dataIndex from dual
                union all
                select '评价维度' title, 'dimensionName' dataIndex from dual
            </when>
            <otherwise>
                union all
                select '评估模板' title, 'templateName' dataIndex from dual
                union all
                select '评估模块' title, 'moduleName' dataIndex from dual
                union all
                select '评价维度' title, 'dimensionName' dataIndex from dual
                union all
                select '指标名' title, 'normName' dataIndex from dual
            </otherwise>
        </choose>
<!-- 截取字符串 -->
  convert(t.score_tp,decimal(10,1)) scoreTp,
4)xml中传入参数为list
<foreach collection="param.leaderLevels" item="item" open="and appraisee.leader_level in (" close=")" separator=",">
    #{item}
</foreach>
5)字符串切割域合并
concat('ACT_', a.activity_id, '_', substring_index(a.assessor_mail,'@',1)) redisKey,
6)返回结果集中包含list
<resultMap id="EvalConstVO" type="com.neusoft.ehr.eval.vo.HrEvalConstVO">
    <id column="id" property="id"/>
    <result column="templateId" property="templateId"/>
    <result column="constId" property="constId"/>
    <result column="constType" property="constType"/>
    <result column="constNameZH" property="constNameZH"/>
    <result column="constNameEN" property="constNameEN"/>
    <result column="constNameJP" property="constNameJP"/>
    <result column="constDescZH" property="constDescZH"/>
    <result column="constDescEN" property="constDescEN"/>
    <result column="constDescJP" property="constDescJP"/>
    <result column="arithLogic" property="arithLogic"/>
    <result column="setColor" property="setColor"/>
    <result column="showOrderHome" property="showOrderHome" />
    <collection property="constRules" ofType="com.neusoft.ehr.eval.po.HrReptSetConstRulePO">
        <result column="ruleLeft" property="ruleLeft"/>
        <result column="ruleRight" property="ruleRight"/>
        <result column="computeSign" property="computeSign"/>
        <result column="ruleValue" property="ruleValue"/>
        <result column="showOrder" property="showOrder"/>
    </collection>
</resultMap>

<select id="getConst" resultMap="EvalConstVO"></select>

HrEvalConstVO

public class HrEvalConstVO {
    
    @ApiModelProperty(value = "model主键")
    @JsonSerialize(using = ToStringSerializer.class)
    private Long id;
    
    @ApiModelProperty(value = "模板业务主键")
    @JsonSerialize(using = ToStringSerializer.class)
    private Long templateId;

    @ApiModelProperty(value = "报告常量模型业务主键")
    @JsonSerialize(using = ToStringSerializer.class)
    private Long constId;
    
    @ApiModelProperty(value = "报告常量模型")
    private String constType;

    @ApiModelProperty(value = "名称中文")
    private String constNameZH;
    @ApiModelProperty(value = "名称英文")
    private String constNameEN;
    @ApiModelProperty(value = "名称日文")
    private String constNameJP;

    @ApiModelProperty(value = "说明中文")
    private String constDescZH;
    @ApiModelProperty(value = "说明英文")
    private String constDescEN;
    @ApiModelProperty(value = "说明日文")
    private String constDescJP;

    @ApiModelProperty(value = "规则间运算逻辑(常用代码:code_arith_logic)")
    private String arithLogic;

    @ApiModelProperty(value = "配色")
    private String setColor;

    @ApiModelProperty(value = "显示顺序")
    private Integer showOrderHome;
    
    @ApiModelProperty(value = "计算规则部分")
    List<HrReptSetConstRulePO> constRules = new ArrayList<>();
}
5,map,list循环
resultTotalDTOMap.forEach((k, v) -> {            
});
evalCalculateScorePOList.forEach(evalCalculateScorePO -> {
});
6,匹配字符串的汉字并替换
String string = "2我们1";
	    String tempStr = "";
	    String targetStr = "";
	    for(int i=0; i<string.length(); i++) {
	        String s = String.valueOf(string.charAt(i));
	        if(s.matches(regex)
	                || (i>0 && s.matches("[0-9]") && String.valueOf(string.charAt(i-1)).matches(regex))
	                || (i>0 && s.matches(regex) && String.valueOf(string.charAt(i-1)).matches("[0-9]") )) {
	            tempStr = tempStr + string.charAt(i);
	            if (i == string.length() -1) {
                    targetStr = targetStr + "${" + tempStr + "}";
                }
                continue;
            }
	        if (!StringUtils.isEmpty(tempStr)) {
	            targetStr = targetStr + "${" + tempStr + "}" + string.charAt(i);
            } else if (i<string.length()-1 && s.matches("[0-9]") && String.valueOf(string.charAt(i+1)).matches(regex) ) {
                // 2正念
                tempStr = s;
                continue;
            }else {
                targetStr = targetStr + string.charAt(i);
            }
	        
	        tempStr = "";
	    }
	    System.out.println(targetStr);
	    
	    Map<String, String> valuesMap = new HashMap<>();
	    valuesMap.put("正念", "(1+1)");
	    valuesMap.put("我们", "(1+2)");
	    StrSubstitutor sub = new StrSubstitutor(valuesMap);
	    String calcFormula = sub.replace(targetStr);
	    System.out.println("----------" + calcFormula);
        // 执行公式,计算分数:如4.5+2 => 6.5
        BigDecimal realityScore = CalculatorUtils.conversion(calcFormula);
        System.out.println("realityScore:" + realityScore);
        if(BigDecimal.valueOf(-1).compareTo(realityScore) == 0) {
            System.out.println("-------------->");
        }
7,mysql更新连续数字
SELECT @i:=0;
UPDATE hr_rept_set_const_model t set t.show_order = @i:=@i+1 where t.template_id = 130 and t.is_deleted = 0 and t.const_type=4
8,mybatisPlus使用
// 查询
LambdaQueryWrapper<HrReptSetConstModelPO> queryAllWrapper = new LambdaQueryWrapper<>();
queryAllWrapper.eq(HrReptSetConstModelPO::getTemplateId, constUpdateVo.getTemplateId());
queryAllWrapper.eq(HrReptSetConstModelPO::getConstType, constUpdateVo.getConstType());
queryAllWrapper.orderByDesc(HrReptSetConstModelPO::getShowOrder);
List<HrReptSetConstModelPO> showOrders = this.getEhrBaseMapper().selectList(queryAllWrapper);
showOrder = showOrders.size() > CommonConstants.ZERO ?
        showOrders.get(0).getShowOrder() + CommonConstants.ONE : CommonConstants.ONE;
// 更新
public String move(String direction, Long guideId, Integer guideOrder, Long nextGuideId, Integer nextGuideOrder) {

    // 移动操作将当前guideId的guideOrder与nextGuideId的nextGuideOrder互换
    // 操作当前条数据
    HrReptSetGuidePO po = new HrReptSetGuidePO();
    po.setGuideOrder(nextGuideOrder);
    LambdaUpdateWrapper<HrReptSetGuidePO> updateWrapper = new LambdaUpdateWrapper<>();
    updateWrapper.eq(HrReptSetGuidePO::getGuideId, guideId);
    this.update(po, updateWrapper);

    // 操作下一条数据
    po.setGuideOrder(guideOrder);
    LambdaUpdateWrapper<HrReptSetGuidePO> nextUpdateWrapper = new LambdaUpdateWrapper<>();
    nextUpdateWrapper.eq(HrReptSetGuidePO::getGuideId, nextGuideId);
    this.update(po, nextUpdateWrapper);
    
    return "OK";
}

// 删除
public String deleteConstModel(Long constId) {
    LambdaQueryWrapper<HrReptSetConstModelPO> queryWrapper = new LambdaQueryWrapper<>();
    queryWrapper.eq(HrReptSetConstModelPO::getConstId,constId);
    this.getEhrBaseMapper().delete(queryWrapper);
    return "success";
}
9,lombok
@Slf4j
@RequiredArgsConstructor(onConstructor = @__(@Autowired), access = AccessLevel.PUBLIC)

private final HrReptSetConstModelService cms;

@GetMapping(value = "/getGridData/{templateId}", produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
public R<Map<String, List<HrEvalConstHomeVO>>> getGridData(@PathVariable Long templateId){}

@PostMapping(value = "/addOrUpdateConst", produces = MediaType.APPLICATION_JSON_UTF8_VALUE)
public BaseResponse addOrUpdateConst(@RequestBody HrEvalConstVO constUpdateVo) {}
10,list排序
// 多个元素排序
// 将集合数据按照计算规则和显示顺序进行升序排序
Collections.sort(values, (s1, s2) -> {
    if (Integer.valueOf(s1.getRuleLeft()) - Integer.valueOf(s2.getRuleLeft()) >= 0) {
        if (s1.getShowOrder() - s2.getShowOrder() >= 0) {
            return 1;
        } else {
            return -1;
        }
    } else {
        return -1;
    }
});

// 单个元素排序
Collections.sort(list3, (s1,s2) -> (s1.getShowOrderHome() - s2.getShowOrderHome()));
11,区间校验,不能重复。如25<得分<=50 50<得分<75

区间校验推荐使用guava来实现简单,如下方式自己手动实现方式

@Override
public String checkRepeat(HrReptConstCheckVO checkVO) {

	log.info("hrReptConstCheckVO:" + ToStringBuilder.reflectionToString(checkVO));
	List<HrReptConstCheckVO> results = this.baseMapper.getRuleValueCount(checkVO);
	List<HrReptConstCheckVO> newResults = new ArrayList<>();
	for (int i=0; i<results.size(); i = i + 2) {
		HrReptConstCheckVO newResult = new HrReptConstCheckVO();
		newResult.setConstId(results.get(i).getConstId());
		// 当前条设置左边分数和计算符号
		newResult.setLeftRuleValue(results.get(i).getRuleValue());
		newResult.setLeftComputeSign(results.get(i).getComputeSign());
		// 下一条设置右边分数和计算符号
		newResult.setRightRuleValue(results.get(i+1).getRuleValue());
		newResult.setRightComputeSign(results.get(i+1).getComputeSign());

		newResults.add(newResult);
	}

	Integer leftRuleValue, rightRuleValue;
	String leftComputeSign, rightComputeSign;
	if (newResults != null && newResults.size() > 0) {
		for (HrReptConstCheckVO result : newResults) {
			if (checkVO.getConstId().equals(result.getConstId())) {
				continue;
			}

			leftRuleValue = result.getLeftRuleValue();
			rightRuleValue = result.getRightRuleValue();
			leftComputeSign = result.getLeftComputeSign();
			rightComputeSign = result.getRightComputeSign();
			// ComputeSign: 2:<; 3:≤
			if ("2".equals(leftComputeSign) && "2".equals(rightComputeSign)) {
				// 10 < 得分 < 20 -> 合法:左边>=20 || 右边<=10
				if (!(checkVO.getLeftRuleValue() >= rightRuleValue || checkVO.getRightRuleValue() <= leftRuleValue)) {
					return "NG";
				}
			} else if ("2".equals(leftComputeSign) && "3".equals(rightComputeSign)) {
				// 10 < 得分 <= 20 -> 合法:左边>20 || 右边<=10 || (左边值=20 && 左边符号<)
				if (!(checkVO.getLeftRuleValue() > rightRuleValue || checkVO.getRightRuleValue() <= leftRuleValue
						|| (checkVO.getLeftRuleValue() == rightRuleValue && "2".equals(checkVO.getLeftComputeSign())))) {
					return "NG";
				}
			} else if ("3".equals(leftComputeSign) && "2".equals(rightComputeSign)) {
				// 10 <= 得分 < 20 -> 合法:左边>=20 || 右边<10 || (右边值=10 && 右边符号<)
				if (!(checkVO.getLeftRuleValue() >= rightRuleValue || checkVO.getRightRuleValue() < leftRuleValue
						|| (checkVO.getRightRuleValue() == leftRuleValue && "2".equals(checkVO.getRightComputeSign())))) {
					return "NG";
				}
			} else {
				// 10 <= 得分 <= 20 -> 合法:左边>20 || 右边<10 || (左边值=20 && 左边符号<) || (右边值=10 && 右边符号<)
				if (!(checkVO.getLeftRuleValue() > rightRuleValue || checkVO.getRightRuleValue() < leftRuleValue
						|| (checkVO.getLeftRuleValue() == rightRuleValue && "2".equals(checkVO.getLeftComputeSign()))
						|| (checkVO.getRightRuleValue() == leftRuleValue && "2".equals(checkVO.getRightComputeSign()))
				)) {
					return "NG";
				}
			}
		}
	}
	return "OK";
}
12,Long类型数据在前台传入后台时丢失精度
@JsonSerialize(using = ToStringSerializer.class)
private Long constId;
13,ID生成器

参照IdWorker

package com.neusoft.ehr.eval.util;

import java.net.InetAddress;
import java.net.UnknownHostException;
import java.time.LocalDate;
import java.time.ZoneId;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class IdWorker {
    private static final Pattern PATTERN_LONG_ID = Pattern.compile("^([0-9]{15})([0-9a-f]{32})([0-9a-f]{3})$");

    private static final Pattern PATTERN_HOSTNAME = Pattern.compile("^.*\\D+([0-9]+)$");

    private static final long OFFSET = LocalDate.of(2000, 1, 1).atStartOfDay(ZoneId.of("Z")).toEpochSecond();

    private static final long MAX_NEXT = 0b11111_11111111_111L;

    private static final long SHARD_ID = getServerIdAsLong();

    private static long offset = 0;

    private static long lastEpoch = 0;

    public static long getNextId() {
        return nextId(System.currentTimeMillis() / 1000);
    }

    private static synchronized long nextId(long epochSecond) {
        if (epochSecond < lastEpoch) {
            epochSecond = lastEpoch;
        }
        if (lastEpoch != epochSecond) {
            lastEpoch = epochSecond;
            reset();
        }
        offset++;
        long next = offset & MAX_NEXT;
        if (next == 0) {
            return nextId(epochSecond + 1);
        }
        return generateId(epochSecond, next, SHARD_ID);
    }

    private static void reset() {
        offset = 0;
    }

    private static long generateId(long epochSecond, long next, long shardId) {
        return ((epochSecond - OFFSET) << 21) | (next << 5) | shardId;
    }

    private static long getServerIdAsLong() {
        try {
            String hostname = InetAddress.getLocalHost().getHostName();
            Matcher matcher = PATTERN_HOSTNAME.matcher(hostname);
            if (matcher.matches()) {
                long n = Long.parseLong(matcher.group(1));
                if (n >= 0 && n < 8) {
                    return n;
                }
            }
        } catch (UnknownHostException e) {

        }
        return 0;
    }

    public static void main(String[] args) {
      System.out.println(getNextId());
    }
}

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值