<template>
  <a-layout :class="$style.webflow">
    <div v-if="busy" :class="[$style.busy, 'ant-modal-mask']" />
    <sd-header>
      <!-- <div slot="left" :class="$style.logo">
        <h3 v-if="showLogo">{{ logoText }}</h3>
      </div> -->
      <div :class="$style.remindclock">
        <div v-if="FlowData.attrs.limitTimeInfo && FlowData.attrs.limitTimeInfo.flowTimeToDeadline">
          <a-tooltip>
            <template slot="title">
              {{ deadline(FlowData.attrs.limitTimeInfo.flowColor) }}:
              {{ FlowData.attrs.limitTimeInfo.flowTimeToDeadline }}
            </template>
            <a-tag :color="FlowData.attrs.limitTimeInfo.flowColor === 'red' ? 'red' : 'blue'">
              <a-icon type="sd-3-circle" :class="$style.remind" />全流程限时
            </a-tag>
          </a-tooltip>
        </div>
        <div v-if="FlowData.attrs.limitTimeInfo && FlowData.attrs.limitTimeInfo.stepTimeToDeadline">
          <a-tag :color="FlowData.attrs.limitTimeInfo.stepColor === 'red' ? 'red' : 'blue'">
            <a-tooltip>
              <template slot="title"> 本环节限时 </template>
              <a-icon type="exclamation-circle" :class="$style.remind" />{{
                deadline(FlowData.attrs.limitTimeInfo.stepColor)
              }}:
              <span>{{ FlowData.attrs.limitTimeInfo.stepTimeToDeadline }} </span>
            </a-tooltip>
          </a-tag>
        </div>
      </div>
      <span
        v-if="leftTime !== undefined"
        :class="$style.lefttime"
        :title="`您在并发理环节中，${leftTime}分钟后可能会失去该文件的处理权限`"
      >
        <a-icon type="clock-circle" />{{ leftTime }}分钟</span
      >
      <sd-webflow-button
        v-for="button in btnsByType.headerButtons"
        :key="button.buttonId"
        :button="button"
        :icons="buttonIconsInternal"
        :class-name="buttonClasses[button.buttonId] || buttonClasses[button.fakeId]"
        type="default"
        :loading="busyButtons[button.buttonId]"
        :disabled="disableActionAlert !== ''"
        :title="button.buttonDesc || disableActionAlert || button.buttonName"
        @click="buttonClick"
      />
    </sd-header>
    <a-layout-content :class="[$style.main, { [$style.foldMain]: fold }]">
      <div :class="[$style.form, { [$style.collapse]: fold }]">
        <sd-webflow-tabs :active-key="tabkey" :removed-tabs="removedTabs" @change="callback">
          <a-tab-pane
            v-for="{ caption, groupId } in doubleView ? [] : attachmentExTabs"
            :key="`sdAttachmentExTab${groupId}`"
          >
            <span
              slot="tab"
              :class="{
                [$style.error]:
                  tabsInvalid[`sdAttachmentExTab${groupId}`] &&
                  tabkey !== `sdAttachmentExTab${groupId}`,
              }"
              >{{ caption }}
            </span>
            <a-card size="small" :class="$style.attachmentex">
              <portal-target :name="`sd-attachment-ex${groupId}`" multiple slim />
              <div class="has-error"
                ><span class="ant-form-explain">{{ attachmentValidMessage }}</span></div
              >
            </a-card>
          </a-tab-pane>
          <a-tab-pane key="sdForm" force-render>
            <span slot="tab" :class="{ [$style.error]: tabsInvalid.sdForm && tabkey !== 'sdForm' }">
              基本信息
            </span>
            <div :class="$style.attForm">
              <a-button
                v-if="doubleView && tabkey === 'sdForm'"
                type="primary"
                @click="
                  foldPreview = !foldPreview
                  foldFlowForm = foldPreview ? false : foldFlowForm
                "
                >{{ !foldPreview ? '收起文件' : '展开文件' }}
              </a-button>
              <a-button
                v-if="doubleView && tabkey === 'sdForm'"
                type="primary"
                @click="
                  foldFlowForm = !foldFlowForm
                  foldPreview = foldFlowForm ? false : foldPreview
                "
                >{{ !foldFlowForm ? '收起表单' : '展开表单' }}
              </a-button>
              <div
                v-if="doubleView"
                :class="[$style.preview, { [$style['view-fold']]: foldPreview }]"
              >
                <a-card
                  v-for="{ caption, groupId } in attachmentExTabs"
                  :key="`sdAttachmentExTab${groupId}`"
                  size="small"
                  :class="$style.attachmentex"
                >
                  <portal-target :name="`sd-attachment-ex${groupId}`" multiple slim />
                  <div class="has-error">
                    <span class="ant-form-explain">{{ attachmentValidMessage }}</span>
                  </div>
                </a-card>
              </div>
              <div
                v-if="!foldFlowForm && !foldPreview && doubleView"
                :class="$style.separate"
              ></div>
              <a-card
                size="small"
                :class="[$style.flowform, { [$style['view-fold']]: foldFlowForm }]"
              >
                <sd-flow-form ref="form" :flow-data="FlowData">
                  <template v-slot="scope">
                    <formWatcher
                      :model="scope.model"
                      :fields="FlowData.attrs.conditionFields"
                      @change="debouncedGetPushButtons"
                    />
                    <!--
          @slot 详情表单内容
          @binding {scope} model 整个表单的数据，可用于 v-model 绑定
          @binding {scope} fields 后台返回的字段定义
          @binding {scope} FlowData 整个流程上下文信息
        -->
                    <slot name="form" v-bind="scope" />
                  </template>
                </sd-flow-form>
              </a-card>
            </div>
          </a-tab-pane>
          <a-tab-pane key="sdLog" ref="sdLog" tab="流程跟踪" :class="$style.processinfo">
            <sd-config-provider :get-popup-container="() => $refs.sdLog.$el">
              <div>
                <sd-sort-children>
                  <sd-card-collapse
                    v-model="flowProcessExpand.opinionLog"
                    title="审批意见"
                    :data-order="100"
                  >
                    <template slot="extra">
                      <div :class="$style.opinionextra">
                        <div v-if="FlowData.attrs.isSubflow" :class="$style.dispsign" @click.stop
                          >显示全流程：<a-switch v-model="logDispAll"
                        /></div>
                        <div ref="filterStepEL" :class="$style.opinionFilter" @click.stop></div>
                      </div>
                    </template>
                    <sd-opinion-log filter-steps :get-filter-container="() => $refs.filterStepEL" />
                  </sd-card-collapse>
                  <sd-card-collapse
                    v-model="flowProcessExpand.processLog"
                    title="流转记录"
                    :data-order="200"
                  >
                    <sd-process-info />
                  </sd-card-collapse>
                  <sd-card-collapse
                    v-model="flowProcessExpand.informLog"
                    title="知会记录"
                    :data-order="300"
                  >
                    <template slot="extra">
                      <span ref="filterUserEL" @click.stop></span>
                    </template>
                    <sd-inform-log filter-user :get-filter-container="() => $refs.filterUserEL" />
                  </sd-card-collapse>
                  <sd-card-collapse
                    v-model="flowProcessExpand.reminderLog"
                    title="催办记录"
                    :data-order="400"
                  >
                    <sd-reminder-log />
                  </sd-card-collapse>
                  <sd-card-collapse
                    v-model="flowProcessExpand.modifyLog"
                    title="修改记录"
                    :data-order="500"
                  >
                    <sd-modify-log />
                  </sd-card-collapse>
                  <sd-card-collapse
                    v-model="flowProcessExpand.invalidLog"
                    title="作废记录"
                    :data-order="500"
                  >
                    <sd-invalid-log />
                  </sd-card-collapse>
                  <!--
                @slot 流程跟踪Tab页内容区 <br>8.0.6 新增
                @binding {scope} FlowData 整个流程上下文信息
                @binding {boolean} actived 当前Tab页是否处于激活状态
              -->
                  <slot name="log-tab-bottom" :FlowData="FlowData" :actived="tabkey === 'sdLog'" />
                </sd-sort-children>
              </div>
            </sd-config-provider>
          </a-tab-pane>
          <a-tab-pane key="sdRelatedDoc">
            <a-card v-if="relateDoc" size="small" :data-order="100">
              <sd-relate-doc @update="($event) => (relateDocCount = $event)" />
            </a-card>
            <a-card size="small" title="被关联文件" :data-order="200">
              <sd-be-related-doc />
            </a-card>
            <!--
                @slot 关联文档Tab页内容区
                @binding {scope} FlowData 整个流程上下文信息
                @binding {boolean} actived 当前Tab页是否处于激活状态
              -->
            <slot
              name="related-doc-tab-bottom"
              :FlowData="FlowData"
              :actived="tabkey === 'sdRelatedDoc'"
            />
            <span slot="tab">
              关联文件 <span v-if="relateDocCount > 0">({{ relateDocCount }})</span></span
            >
          </a-tab-pane>
          <a-tab-pane key="sdFlowchart" tab="流程图" :class="$style.flowpic">
            <sd-flowchart
              :log-disp-all="logDispAll"
              toggle-view
              :root-inst-id="rootParentInfo ? rootParentInfo.id : FlowData.instId"
              :root-flow-id="rootParentInfo ? rootParentInfo.flowId : FlowData.processId"
              @changeView="(flag) => (logDispAll = flag)"
            />
          </a-tab-pane>
          <!--
          @slot 扩展的Tab页<br>8.0.3 新增
          @binding {scope} FlowData 整个流程上下文信息
        -->
          <slot name="extra-tabs" :FlowData="FlowData" />
        </sd-webflow-tabs>
      </div>
      <div :class="[$style.middleBtn, { [$style.shrink]: !isNote }]">
        <a-button type="primary" :class="[$style.fold]" @click="fold = !fold">
          <a-icon :type="fold ? 'left' : 'right'" />
        </a-button>
        <!-- 便签 -->
        <SdNote
          v-if="isNote"
          :flow-data="flowData"
          :btn-info="noteBtnInfo"
          :do-action="doAction"
        ></SdNote>
      </div>
      <!-- 填写意见 -->
      <div :class="[$style['operate-area'], { [$style.collapse]: fold }]">
        <a-card v-if="canWirteOpinion" :class="$style.writeOpinion">
          <template slot="title">
            <span class="ant-badge ant-badge-not-a-wrapper">
              <sup class="ant-scroll-number ant-badge-count" />
            </span>
            填写意见
          </template>
          <portal-target
            ref="approveOpinion"
            name="approve-opinion"
            :class="{ [$style['has-error']]: opinionValidMessage }"
          >
          </portal-target>
          <div class="has-error"
            ><span class="ant-form-explain">{{ opinionValidMessage }}</span></div
          >
        </a-card>
        <!-- 审核要素 -->
        <a-card v-if="FlowData.attrs.auditElement.length > 0" :class="$style.flowys">
          <template slot="title">
            <span class="ant-badge ant-badge-not-a-wrapper">
              <sup class="ant-scroll-number ant-badge-count" />
            </span>
            <span>审核要素</span>
            <a-tooltip>
              <template slot="title"> 需要全选才可提交流转 </template>
              <a-icon :class="$style.yshint" type="exclamation-circle" theme="twoTone" />
            </a-tooltip>
            <div :class="$style.checkallys">
              <a-checkbox
                :indeterminate="indeterminate"
                :checked="checkAllYS"
                @change="onCheckAllChange"
              >
                全选
              </a-checkbox>
            </div>
          </template>
          <div :class="$style.checkcontent">
            <a-checkbox-group
              v-model="checkedYSlist"
              option-label="name"
              :options="FlowData.attrs.auditElement"
              @change="onChange"
            >
              <span slot="label" slot-scope="option">{{ option.name }}</span>
            </a-checkbox-group>
          </div>
        </a-card>
        <a-card v-if="btnsByType.submitButtons.length > 0" :class="$style.comitbtns">
          <template slot="title">
            <span class="ant-badge ant-badge-not-a-wrapper">
              <sup class="ant-scroll-number ant-badge-count" />
            </span>
            提交流转
          </template>
          <div :class="$style.nodes">
            <sd-webflow-button
              v-for="button in btnsByType.submitButtons"
              :key="button.buttonId"
              size="large"
              type="primary"
              :class-name="buttonClasses[button.buttonId] || buttonClasses[button.fakeId]"
              :disabled="disableActionAlert !== ''"
              :title="disableActionAlert || button.buttonName"
              block
              :loading="busyButtons[button.buttonId]"
              :button="button"
              @click="buttonClick"
            />
          </div>
          <!--
                @slot 提交按钮下面的区域
                @binding {scope} FlowData 整个流程上下文信息
              -->
          <slot name="submit-area-after" :FlowData="FlowData"> </slot>
        </a-card>
        <a-card>
          <div :class="$style.actions">
            <sd-webflow-button
              v-for="button in btnsByType.actionButtons"
              :key="button.buttonId"
              :button="button"
              :class-name="buttonClasses[button.buttonId] || buttonClasses[button.fakeId]"
              :loading="busyButtons[button.buttonId]"
              :disabled="disableActionAlert !== ''"
              :title="button.buttonDesc || disableActionAlert || button.buttonName"
              @click="buttonClick"
            />
          </div>
        </a-card>
      </div>
    </a-layout-content>
    <component :is="actionComponent" ref="actionComponent">
      <template v-for="(_, slot) of $scopedSlots" v-slot:[slot]="scope">
        <slot :name="slot" v-bind="scope" />
      </template>
    </component>
  </a-layout>
</template>

<script>
import Vue from 'vue'
import { Message, Modal } from 'ant-design-vue'
import cloneDeep from 'lodash.clonedeep'
import debounce from 'lodash.debounce'
import scrollIntoView from 'scroll-into-view-if-needed'
import moment from 'moment'
import buttonHandlers from './button-handlers/index'
import components from './_import-components/sd-webflow-import'
import SdNote from './sd-note'
import FlowService from '@/webflow/flow-service'
import storeMixin from '@/common/store-mixin'
import sdFormPublicMethodsNew from '@/common/components/sd-form/sd-form-public-methods-new'
import crossWindowWatcher from '@/common/services/cross-window-watcher'
import axios from '@/common/services/axios-instance'
import { sdLocalStorage, sdSessionStorage } from '@/common/services/storage-service'
import ProgressBar from '@/common/services/progress-bar'
Vue.use(Message)
Vue.use(Modal)

let noticeWhenClose = true
let snap
/**
 * 流程表单
 * @displayName SdWebflow 流程表单
 */
export default {
  name: 'SdWebflow',
  components: {
    ...components,
    SdNote,
    formWatcher: {
      props: ['model', 'fields'],
      created() {
        if (Array.isArray(this.fields) && this.fields.length) {
          this.$watch(function() {
            return JSON.stringify(this.fields.map((field) => this.model[field]))
          }, this.callback)
        } else if (this.fields === null) {
          this.$watch('model', this.callback, {
            deep: true,
          })
        }
      },
      methods: {
        callback() {
          this.$emit('change')
        },
      },
      render(h) {
        return <span />
      },
    },
  },
  mixins: [storeMixin, sdFormPublicMethodsNew],
  props: {
    /**
     * 表单的自定义校验，适用于多个字段联合校验，返回值为Promise.resolve(true)通过，
     * Promise.resolve(false)校验失败
     */
    validateForm: {
      type: Function,
      default: () => Promise.resolve(true),
    },
    /**
     * 移除的tab列表
     * @since 8.0.3
     */
    removedTabs: {
      type: Array,
      default: () => [],
    },
    /**
     * 顶部区域的按钮
     * @since 8.0.4
     */
    headerButtons: {
      type: Array,
      default: () => [
        'admin-ops',
        'workflow$myCollection',
        'workflow$myCollectionCancel',
        'we-doc$print',
        'workflow$openTodo',
        'we-doc$close',
      ],
    },
    /**
     * 流程提交区域的按钮
     * @since 8.0.4
     */
    submitButtons: {
      type: Array,
      default: () => ['workflow-push'],
    },
    /**
     * 指定按钮的图标，用于顶部区域按钮的显示
     * @since 8.0.4
     */
    buttonIcons: {
      type: Object,
      default: () => ({}),
    },
    /**
     * 指定按钮的样式，用于自定义添加按钮样式
     * @since 8.1.0.23
     */
    buttonClasses: {
      type: Object,
      default: () => ({}),
    },
    /**
     * 启用正文一屏预览功能，前提是部署了wps预览服务，使用了indidoc控件才生效
     * @since 8.0.6
     */
    enableAttachmentExTab: {
      /**
       * 8.0.15（含）后该参数废弃，是否启用一屏预览仅靠附件组件的enableAttachmentExTab属性控制即可
       */
      type: Boolean,
      default: false,
    },
    /**
     * @ignore
     */
    flowData: {
      type: Object,
      required: true,
    },
    /**
     * 表单打印默认显示区域
     * @since 0.17
     */
    defaultPrintBlocks: {
      type: Array,
      default: () => [],
    },
  },
  data() {
    return {
      logoText: '',
      tabkey: undefined, // 切换页签获取a-tabs的key，目的每次点击关联文档页签时重新加载
      parentFlowData: {
        processLogInfo: [],
      },
      rootFlowData: {
        processLogInfo: [],
        processActiveLogInfo: [],
      },
      opinionValidMessage: '',
      fold: false,
      operationLogInfo: [],
      relateDoc: null,
      relateDocCount: undefined,
      customProps: undefined,
      busyButtons: {},
      log: {}, // 各种流转记录
      attachmentValidMessage: '',
      tabsInvalid: {},
      isNote: false,
      noteBtnInfo: {},
      actionComponent: undefined,
      activedAtt: {},
      attachmentExTabs: [], //
      leftTime: undefined,
      lockModal: undefined,
      busy: false,
      flowProcessExpand: {
        opinionLog: true,
        processLog: true,
        informLog: false,
        reminderLog: false,
        modifyLog: false,
        invalidLog: false,
      },
      foldPreview: false,
      foldFlowForm: false,
      rootParentInfo: undefined, // 顶级主流程信息
      logDispAll: false,
      // 审核要素相关
      indeterminate: false,
      checkedYSlist: [],
      flowYSList: [],
      checkAllYS: false,
    }
  },
  computed: {
    showLogo() {
      return window.name !== 'sdEasyflowIframe'
    },
    FlowData() {
      return Object.assign(this.flowData, this.log)
    },
    canWirteOpinion() {
      return (
        this.FlowData.processFormData?.processFormPropertyValues.find(
          (i) => i.dataType === 'opinion'
        )?.readonly === false
      )
    },
    opinionInfo() {
      return this.getOpinionInfo(this.FlowData?.processLogInfo, this.FlowData?.processActiveLogInfo)
    },
    parentOpinionInfo() {
      return this.getOpinionInfo(this.parentFlowData?.processLogInfo || [])
    },
    reminderInfo() {
      return [].concat.apply(
        [],
        this.FlowData.reminderLogInfo?.map((a) => a.reminderUserInfos)
      )
    },
    invalidInfo() {
      return this.FlowData.processLogInfo?.filter((item) => item.actionName === '作废')
    },
    buttonIconsInternal() {
      // 默认图标配置 + 用户自定义配置
      return {
        save: 'save',
        'we-doc$print': 'printer',
        'we-doc$close': 'close-circle',
        workflow$openTodo: 'share-alt',
        'we-doc$serialNumber': 'number',
        workflow$myCollection: 'sd-star',
        workflow$myCollectionCancel: 'sd-star',
        'admin-ops': 'tool',
        ...this.buttonIcons,
      }
    },
    btnsByType() {
      const adminBtnIds = [
        'workflow$delete',
        'workflow$suspend',
        'workflow$terminate',
        'workflow$interfereStep',
        'workflow$interfereAction',
        'workflow$resume',
      ]
      const hiddenBtnIds = [
        'workflow$dataMaint',
        'we-doc$note',
        'we-doc$redraw',
        'we-doc$huiqianYJDelete', // 会签意见删除按钮
      ]

      // 把所有按钮按区域分组
      const headerButtons = []
      const submitButtons = []
      const actionButtons = []
      const adminButtons = []
      // add by zzb 修改退出按钮名称
      this.FlowData.buttons.forEach((item) => {
        if (item.buttonId === 'we-doc$close') {
          item.buttonName = '关闭'
        }
      })
      const buttons = this.FlowData.buttons
        .map((m) => {
          m.buttonDesc = m.attrs?.buttonDesc || ''
          return m
        })
        .filter((btn) => {
          // 找到管理员操作按钮
          const isAdminBtn = adminBtnIds.indexOf(btn.buttonId) !== -1
          if (isAdminBtn) {
            adminButtons.push(btn)
          }
          const isHiddenBtn = hiddenBtnIds.indexOf(btn.buttonId) !== -1
          // 管理员按钮 + 隐藏的按钮，从原数组移除
          return !(isAdminBtn || isHiddenBtn)
        })
      // 有管理员操作的话，增加一个admin-ops按钮
      if (adminButtons.length > 0) {
        buttons.push({
          buttonId: 'admin-ops',
          buttonName: '管理员操作',
          children: adminButtons,
        })
      }
      if (this.FlowData?.saveButtons?.[0]) {
        buttons.unshift({ ...this.FlowData.saveButtons[0], type: 'save' })
      }
      buttons
        .concat(
          (this.FlowData.pushButtons || []).map((btn) => {
            return {
              type: 'push',
              ...btn,
            }
          })
        )
        .forEach((btn) => {
          let fakeId = btn.buttonId
          if (btn.type === 'push') {
            fakeId = 'workflow-push'
          } else if (btn.type === 'save') {
            fakeId = 'save'
          }
          btn.fakeId = fakeId
          if (this.headerButtons.includes(fakeId) || this.headerButtons.includes(btn.buttonId)) {
            headerButtons.push(btn)
          } else if (
            this.submitButtons.includes(fakeId) ||
            this.submitButtons.includes(btn.buttonId)
          ) {
            submitButtons.push(btn)
          } else {
            actionButtons.push(btn)
          }
        })
      headerButtons.sort((a, b) => {
        return this.headerButtons.indexOf(a.fakeId) - this.headerButtons.indexOf(b.fakeId)
      })
      submitButtons.sort((a, b) => {
        return this.submitButtons.indexOf(a.fakeId) - this.submitButtons.indexOf(b.fakeId)
      })
      return {
        headerButtons,
        submitButtons,
        actionButtons,
      }
    },
    deadline() {
      return function(type) {
        let deadline = ''
        switch (type) {
          case 'blue':
            deadline = '限时'
            break
          case 'orange':
            deadline = '快到期'
            break
          case 'red':
            deadline = '已超时'
            break
        }
        return deadline
      }
    },
    disableActionAlert() {
      return JSON.stringify(this.activedAtt).includes('true') ? '请先关闭正文或附件再操作' : ''
    },
    enableDoubleView() {
      // 是否开启了双览
      return sdSessionStorage.getItem('ApprovalMode') === '1'
    },
    doubleView() {
      // 开启了一屏预览且有预览的附件
      return this.enableDoubleView && this.attachmentExTabs.length > 0
    },
  },
  watch: {
    fold() {
      const event = document.createEvent('Event')
      event.initEvent('resize', true, true)
      window.dispatchEvent(event)
    },
    logDispAll() {
      this.resetlog()
    },
  },
  mounted() {
    // 如果当前流程已结束，则隐藏右侧功能区域 add by zzb
    if (this.FlowData.attrs.stepN === 9999) {
      this.fold = true
    }
    this.$nextTick(() => {
      this.sdEmit('firstMeaningfulPaint', { FlowData: this.FlowData })
      const totalCostTime = new Date().valueOf() - parseInt(window.performance.timeOrigin)
      // eslint-disable-next-line no-console
      console.log(`打开表单耗时:%c${totalCostTime}ms`, 'background-color: blue')
      if (this.FlowData.mode === 'EDIT') {
        snap = this.getFormSnap()
        window.addEventListener('beforeunload', (event) => {
          if ((this.getFormSnap() !== snap || this.disableActionAlert) && noticeWhenClose) {
            event.preventDefault()
            event.returnValue = '正在编辑文档或正文附件，确认是否离开此网站'
          }
        })
      }
    })
    window.addEventListener('unload', () => {
      FlowService.unLockWhenBrowerClose(this.FlowData.instId)
    })
    const oldfun = this.validateFields
    this.validateFields = function() {
      return oldfun
        .call(this, ...arguments)
        .then(this.validateForm)
        .then((flag) => {
          if (!flag) {
            return Promise.reject(flag)
          }
          if (this.relateDoc?.required && this.relateDocCount === 0) {
            Message.warn('请添加关联文档')
            this.tabkey = 'sdRelatedDoc'
            return Promise.resolve(false)
          }
          let attachments = []
          const attachmentFields = this.FlowData.processFormData.processFormPropertyValues.filter(
            (field) => field.dataType === 'attachment'
          )
          attachmentFields.forEach((field) => {
            attachments = attachments.concat(this.getFieldValue(field.name))
          })
          const isCompleted = attachments.every((item) => item.Completed === 1)
          if (attachments.length && !isCompleted) {
            Message.warn('您有文件还在上传中')
            return Promise.resolve(false)
          } else {
            return Promise.resolve(true)
          }
        })
        .then((flag) => {
          if (flag) {
            return Promise.resolve(flag)
          } else {
            return Promise.reject(flag) // 和原validateFields保持一致
          }
        })
        .catch((e) => {
          this.opinionValidate()
          this.attachValidate().then(() => {
            this.tabsValidate().then(() => {
              this.tabkey = Object.keys(this.tabsInvalid)[0] || this.tabkey
            })
          })
          return Promise.reject(e)
        })
    }
    this.old_validateField = this.validateField
    this.validateField = function(field) {
      if (field === 'approveOpinion') {
        this.opinionValidate()
      }
      return this.old_validateField(...arguments)
    }
    if (!this.tabkey) {
      this.tabkey = 'sdForm'
    }
    const lock = JSON.parse(sdLocalStorage.getItem(`Lock${this.FlowData?.instId}`) || '{}')
    if (lock.expire) {
      this.leftTime = moment.duration(lock.expire - lock.lastActive).minutes()
      Message.warn(`您在并发处理环节中，${this.leftTime}分钟后可能会失去该文件的处理权限`, 5)
      setInterval(() => {
        if (this.leftTime === undefined) {
          return
        }
        if (this.leftTime <= 0) {
          this.leftTime = 0
        } else {
          this.leftTime = this.leftTime - 1
        }
        if (this.leftTime === 3) {
          this.lockModal = Modal.info({
            title: '继续处理',
            content: `${this.leftTime}分钟后您对此文件处理权限将失效，请点击确认延长处理时间`,
            okText: '确认',
            onOk: () => {
              FlowService.getLock(this.FlowData?.instId, true).then((flag) => {
                if (flag) {
                  this.leftTime = moment.duration(lock.expire - lock.lastActive).minutes()
                  Message.success(`已延长处理时间,${this.leftTime}分钟后过期`)
                } else {
                  Message.success(`当前文件已被其他人处理，您已无法继续处理该文件`)
                }
              })
            },
          })
        }
        if (this.leftTime < 3) {
          this.lockModal.update({
            content:
              this.leftTime > 0
                ? `${this.leftTime}分钟后您对此文件处理权限将失效，请点击确认延长处理时间`
                : `您已失去此文件的处理权限，请点击确认重新获取权限`,
          })
        }
      }, 60 * 1000)
    }
  },
  created() {
    this.logoText = this.FlowData.processFormData.processFormPropertyValues.find(
      (field) => field.name === 'businessTypeName'
    )?.value
    this.relateDoc = this.FlowData.processFormData.processFormPropertyValues.find(
      (field) => field.dataType === 'relateddoc'
    )
    this.relateDocCount = JSON.parse(this.relateDoc?.attr?.relatedDoc || '[]').length
    if (
      this.FlowData.attrs.workflowStatus &&
      this.FlowData.attrs.workflowStatus !== 'ACTIVATED' &&
      window.location.href.includes('todo-pages')
    ) {
      Message.warn('流程已暂停或终止')
    }
    if (
      typeof this.FlowData.attrs.isRead !== 'undefined' &&
      !this.FlowData.attrs.isRead &&
      !this.FlowData.attrs.openRead
    ) {
      if (this.flowData.attrs.feedbackOpinionRequired) {
        Message.warn('此文档必须填写反馈信息')
      }
    }
    if (this.FlowData?.attrs?.processState === 3) {
      Message.warn('本文档上次提交有异常，请重新处理')
    }
    if (this.FlowData.attrs.openRead) {
      crossWindowWatcher.notifyChange(true)
    }
    // 便签按钮
    const btnNote = this.FlowData.buttons.find((k) => k.buttonId === 'we-doc$note')
    if (btnNote) {
      this.isNote = true
      this.noteBtnInfo = {
        buttonDesc: btnNote.attrs?.buttonDesc || '',
        buttonName: btnNote.buttonName,
      }
    } else {
      this.isNote = false
    }
    this.debouncedGetPushButtons = debounce(() => {
      if (this.FlowData.mode === 'EDIT') {
        this.updatePushButtons()
      }
    }, 500)
    // 如果是子流程 ，获取顶级流程信息
    if (this.FlowData.attrs.isSubflow && this.FlowData.attrs.parentFlowInstanceId) {
      FlowService.getRootFlowInstanceId(this.FlowData.attrs.parentFlowInstanceId).then((res) => {
        this.rootParentInfo = {}
        this.rootParentInfo.id = Number(res.id)
        this.rootParentInfo.flowId = res.flowId
      })
    }
  },
  methods: {
    onChange(checkedList) {
      this.indeterminate =
        !!checkedList.length && checkedList.length < this.FlowData.attrs.auditElement.length
      this.checkAllYS = checkedList.length === this.FlowData.attrs.auditElement.length
    },
    onCheckAllChange(e) {
      Object.assign(this, {
        checkedYSlist: e.target.checked
          ? this.FlowData.attrs.auditElement.map((item) => item.value)
          : [],
        indeterminate: false,
        checkAllYS: e.target.checked,
      })
    },
    updateAttachmentExTabs(groupId, flag) {
      if (flag) {
        // 添加附件页签
        const caption = this.FlowData.processFormData.processFormPropertyValues.find((item) => {
          return item.dataType === 'attachment' && item.value?.includes?.(groupId)
        }).caption
        if (this.attachmentExTabs.length === 0 && !this.enableDoubleView) {
          // 记录第一个附件页签，设置选中
          this.tabkey = `sdAttachmentExTab${groupId}`
        }
        if (!this.attachmentExTabs.some((item) => item.groupId === groupId)) {
          this.attachmentExTabs.push({
            caption,
            groupId,
          })
          this.attachmentExTabs = [...this.attachmentExTabs]
          return true
        }
      } else {
        // 卸载附件页签
        this.attachmentExTabs = this.attachmentExTabs.filter((item) => {
          return item.groupId !== groupId
        })
        if (this.attachmentExTabs.length && !this.enableDoubleView) {
          // 卸载后修改选中的页签
          this.tabkey = `sdAttachmentExTab${this.attachmentExTabs[0].groupId}`
        } else {
          this.tabkey = 'sdForm'
        }
      }
    },
    updatePushButtons() {
      if (this.ajaxRequest) this.ajaxRequest.cancel()
      this.ajaxRequest = axios.CancelToken.source()
      return axios
        .post(
          'api/framework/v1/process-drive/push-task-buttons',
          {
            instId: this.FlowData.instId,
            activeStepId: this.FlowData.activeStepId,
            inputs: this.getBackendValues(),
            flowId: this.FlowData.processId,
          },
          { cancelToken: this.ajaxRequest.token }
        )
        .then((res) => {
          this.FlowData.pushButtons = res.data
        })
        .catch((thrown) => {
          if (!axios.isCancel(thrown)) {
            // eslint-disable-next-line no-console
            console.error(thrown)
          }
        })
    },
    updateActivedAtt(code, statue) {
      this.activedAtt[code] = statue
      this.activedAtt = { ...this.activedAtt }
    },
    // 表单快照
    getFormSnap() {
      // 关闭页面时提示用户页面是否修改，对比修改的值不包含附件，附件会自动保存，而且它的值的更新是异步的
      const attachmentFields = this.FlowData.processFormData.processFormPropertyValues.filter(
        (item) => item.dataType === 'attachment'
      )
      const snap = this.getFieldsValue()
      Object.keys(snap).forEach((key) => {
        if (attachmentFields.some((item) => item.name === key)) delete snap[key]
      })
      return JSON.stringify(snap)
    },
    // 处理审批意见
    getOpinionInfo(processLogInfo, processActiveLogInfo) {
      let opinionInfo = []
      // 子流程 且 显示全流程：审批意见从顶层文档中获取
      if (this.FlowData.attrs.isSubflow && this.logDispAll) {
        if (this.rootFlowData.processLogInfo?.[0]) {
          opinionInfo = opinionInfo.concat(this.rootFlowData.processLogInfo)
        }
        if (this.rootFlowData.processActiveLogInfo?.[0]) {
          opinionInfo = opinionInfo.concat(this.rootFlowData.processActiveLogInfo)
        }
      } else {
        if (processLogInfo?.[0]) {
          opinionInfo = opinionInfo.concat(processLogInfo)
        }
        if (processActiveLogInfo?.[0]) {
          opinionInfo = opinionInfo.concat(processActiveLogInfo)
        }
      }
      opinionInfo = cloneDeep(opinionInfo)
      const filterOpinions = (opinions) => {
        return (opinions || []).filter((item) => {
          if (item.subFlowLogInfo?.length) {
            item.subFlowLogInfo = filterOpinions(item.subFlowLogInfo)
            if (item.subFlowLogInfo.length === 0) {
              delete item.subFlowLogInfo
            }
          }
          if (item.subFlowActiveLogInfos?.length) {
            item.subFlowActiveLogInfos = filterOpinions(item.subFlowActiveLogInfos)
            if (item.subFlowActiveLogInfos.length === 0) {
              delete item.subFlowActiveLogInfos
            }
          }
          return (
            item.opinion ||
            item.processLogAttachments?.length ||
            item.subFlowLogInfo?.length ||
            item.subFlowActiveLogInfos?.length
          )
        })
      }
      return filterOpinions(opinionInfo)
    },

    // 切换tab页签 检测key值变化
    callback(key) {
      this.tabkey = key
      this.tabsValidate()
    },
    getLog(type, forceUpdate = false) {
      // forceUpdate强制刷新数据
      // 获取记录reminder，inform，process,operation等
      if (this.FlowData[type + 'LogInfo'] && !forceUpdate) {
        // 对于意见，流转记录,作废这样的，用同一个请求返回值，且已经请求过数据了，就用已经有的值
        return Promise.resolve()
      }
      // 子流程 且 显示全流程：获取顶层主流程相关流程信息
      if (this.FlowData.attrs.isSubflow && this.logDispAll) {
        if (this['toppromise' + type] && !forceUpdate) {
          // 对于意见，流转记录，作废这样的，用同一个请求的,又都要发请求的，不重复发请求
          return this['toppromise' + type]
        }
        this['toppromise' + type] = FlowService.getRootLog(this.rootParentInfo, this.FlowData, type)
          .then((log) => {
            if (type === 'process') {
              this.rootFlowData.processLogInfo = log.processLogInfo
              this.rootFlowData.processActiveLogInfo = log.processActiveLogInfo
              // 为了能响应式所以这样赋值,this.log.processLogInfo
              this.$set(this.log, type + 'LogInfo', log.processLogInfo)
              this.$set(this.log, type + 'ActiveLogInfo', log.processActiveLogInfo)
            } else {
              this.$set(this.log, type + 'LogInfo', log)
            }
          })
          .finally(() => {
            this['toppromise' + type] = undefined
          })
        return this['toppromise' + type]
      } else {
        if (this['promise' + type] && !forceUpdate) {
          // 对于意见，流转记录，作废这样的，用同一个请求的,又都要发请求的，不重复发请求
          return this['promise' + type]
        }
        // 获取当前流程相关流程信息
        if (this.FlowData.instId) {
          // 获取关联文档相关参数
          const { openAuthType, openAuthParameter } = this.$route.query
          const config = {}
          if (openAuthType) {
            config.params = { openAuthType, openAuthParameter }
          }
          this['promise' + type] = FlowService.getLog(this.FlowData, type, config)
            .then((log) => {
              // 为了能响应式所以这样赋值,this.log.processLogInfo
              if (type === 'process') {
                this.$set(this.log, type + 'LogInfo', log.processLogInfo)
                this.$set(this.log, type + 'ActiveLogInfo', log.processActiveLogInfo)
              } else {
                this.$set(this.log, type + 'LogInfo', log)
              }
            })
            .finally(() => {
              this['promise' + type] = undefined
            })
          return this['promise' + type]
        }
        return Promise.resolve()
      }
    },
    updateFlow() {
      return this.sdEmit('requestUpdateFlowData').then(() => {
        if (this.FlowData.optimistic === true) {
          this.leftTime = undefined
        }
        // 更新流程后更新各种记录
        if (this.log.processLogInfo) {
          this.getLog('process', true)
        }
        if (this.log.reminderLogInfo) {
          this.getLog('reminder', true)
        }
        if (this.log.informLogInfo) {
          this.getLog('inform', true)
        }
        if (this.log.operationLogInfo) {
          this.getLog('operation', true)
        }
      })
    },
    buttonClick(button) {
      this.busyButtons = { ...this.busyButtons, [button.buttonId]: true }
      ProgressBar.start()
      this.busy = true
      let p = Promise.resolve()
      if (
        this.FlowData.activeStepId === 0 &&
        this.FlowData?.saveButtons?.[0] &&
        ![
          'we-doc$print',
          'we-doc$close',
          'save',
          'workflow-push',
          'workflow$manualNumber',
        ].includes(button.fakeId)
      ) {
        p = Promise.reject(Message.warn('因为当前是起草状态，需先保存当前流程再执行此操作'))
      }
      p.then(() => {
        return this.sdEmit('actionBtnClick', { button, FlowData: this.FlowData }).then((evt) => {
          this.FlowData.customProps = this.customProps
          const defaultPrevented = evt.defaultPrevented
          if (defaultPrevented) {
            return Promise.resolve()
          }
          return this.doAction(button)
        })
      }).finally(() => {
        this.busyButtons = { ...this.busyButtons, [button.buttonId]: false }
        ProgressBar.done()
        if ((button.type === 'save') | (button.fakeId === 'workflow-push')) {
          this.$emit('saveproject', this.FlowData)
        }
        this.busy = false
      })
      return
      // 为了生成文档
      /**
       * 操作按钮点击时触发
       *
       * @property {event} evt 事件对象，可以用来取消默认操作等
       * @property {object} context 点击的按钮和流程信息{button,FlowData}
       */
      this.$emit('actionBtnClick', {}, {}) // eslint-disable-line no-unreachable
    },
    doAction(button) {
      const id = (button.fakeId || button.buttonId).toLowerCase().replace(/\$|-|_/g, '')
      const serviceJs = buttonHandlers.js[id]
      const serviceVue = buttonHandlers.vue['sd' + id]
      if (serviceVue) {
        // vue组件
        this.actionComponent = serviceVue
        return serviceVue()
          .then(() => {
            return this.$nextTick()
          })
          .then(() => {
            return this.$refs.actionComponent.run({ button })
          })
      } else if (serviceJs) {
        return serviceJs().then((f) => {
          return f.default.bind(this)({ button })
        })
      } else {
        return Promise.resolve()
      }
    },

    opinionValidate({ atts, attskey, preview, validate = true } = {}) {
      // 预览意见
      const OPINION_FIELD = this.FlowData.attrs.OPINION_FIELD
      const trustUserInfo = this.FlowData.processFormData?.processFormPropertyValues.find(
        (i) => i.name === 'approveOpinion'
      ).attr.trustUserInfo

      //  预览意见签署后台给了
      const currentStepOpinion = this.FlowData.processFormData?.processFormPropertyValues.find(
        (i) => i.name === OPINION_FIELD
      )?.attr?.currentStepOpinion
      if (OPINION_FIELD && preview && currentStepOpinion) {
        const preYj = {
          preview: true,
          attskey,
          signContent: {
            signTime: new Date().getTime(),
            signContent: this.getFieldValue('approveOpinion'),
            signer: {
              ...currentStepOpinion.opinion.signContent.signer,
              ...{
                trustor:
                  trustUserInfo?.trustType === 1
                    ? { trustorName: trustUserInfo.trustorName }
                    : null,
              },
            },
            attachments: atts,
            attachment: JSON.parse(
              this.FlowData.processFormData?.processFormPropertyValues.find(
                (i) => i.name === 'approveOpinion'
              ).value
            ).value?.attrs?.attachment,
          },
        }
        const opinions = this.getFieldValue(OPINION_FIELD)
        if (opinions.find((item) => item.preview)) {
          // eslint-disable-next-line babel/no-unused-expressions
          opinions?.splice(
            opinions.findIndex((item) => item.preview),
            1
          )
        }
        if (this.getFieldValue('approveOpinion') || atts.length || attskey) {
          opinions.splice(currentStepOpinion.index, 0, preYj)
        }
        this.setFieldValue(OPINION_FIELD, opinions)
        this.$nextTick().then(() => {
          if (this.$el.querySelector('.preview_sd-opinion-view_webflow')) {
            scrollIntoView(this.$el.querySelector('.preview_sd-opinion-view_webflow'), {
              scrollMode: 'if-needed',
              block: 'center',
              skipOverflowHiddenElements: true,
            })
          }
        })
      }
      // 意见的校验，单独发布事件的方式处理，兼容传送门的情况,存粹是为了获取提示语
      if (validate) {
        this.old_validateField('approveOpinion')
          .then((success) => {
            this.opinionValidMessage = ''
          })
          .catch((errorMessage) => {
            this.opinionValidMessage = errorMessage
          })
      }
    },
    attachValidate() {
      // 一瓶预览后，读取附件校验状态
      return this.$nextTick().then(() => {
        this.attachmentValidMessage =
          document.querySelector('.for-print_sd-attachment-display_common .has-error')?.innerText ||
          ''
      })
    },
    tabsValidate() {
      // 找到有可见的has-error元素的页签
      this.tabsInvalid = {}
      return this.$nextTick().then(() => {
        document.querySelectorAll('.ant-tabs-tabpane').forEach((item) => {
          if (
            [...item.querySelectorAll('.has-error')].some(
              (errorElement) => errorElement.clientHeight !== 0 && errorElement.clientWidth !== 0
            )
          ) {
            this.tabsInvalid[item.getAttribute('data-key')] = true
            this.tabsInvalid = { ...this.tabsInvalid }
          }
        })
      })
    },
    getFormData() {
      return this.getBackendValues()
    },

    close(flag = false, notice = false) {
      if (
        notice &&
        this.FlowData.mode === 'EDIT' &&
        (this.getFormSnap() !== snap || this.disableActionAlert)
      ) {
        // 如果是代码触发的关闭，自己弹框确认是否关闭，不用onbeforunload是为了防止执行了解锁，页面却没有关闭
        if (!window.confirm('正在编辑文档或正文附件，确认是否离开此网站')) {
          return
        }
      }
      crossWindowWatcher.notifyChange(flag)
      return FlowService.unLock(this.FlowData.instId).finally(() => {
        // 页面提交后会自动关闭当前页面，不方便调试，在此加个debugger不影响正常提交，只会在调试模式下起作用
        noticeWhenClose = false // 代码触发的关闭不走onbeforunload
        // eslint-disable-next-line no-debugger
        debugger
        window.opener = window
        const win = window.open('', '_self')
        win.close()
      })
    },
    checkLock() {
      // 需要校验锁是否允许下一步操作
      if (!this.FlowData.optimistic && this.FlowData.mode === 'EDIT') {
        return FlowService.getLock(this.FlowData.instId).then((data) => {
          if (data) {
            this.FlowData.lockId = data
            return true
          } else {
            Message.warn('文档已被其他人处理')
            return Promise.reject(new Error('文档已被其他人处理'))
          }
        })
      }
      return Promise.resolve()
    },
    setCustomProps(value) {
      if (value && typeof value !== 'string') {
        throw new Error('调用setCustomProps扩展传给后台的prop扩展参数的值必须为字符串格式')
      }
      this.customProps = value
    },
    getCustomProps() {
      return this.customProps
    },
    // 切换流程显示方式
    resetlog() {
      if (this.log.processLogInfo) {
        this.getLog('process', true)
      }
      if (this.log.reminderLogInfo) {
        this.getLog('reminder', true)
      }
      if (this.log.informLogInfo) {
        this.getLog('inform', true)
      }
      if (this.log.operationLogInfo) {
        this.getLog('operation', true)
      }
    },
  },
  // 因为已经有了template，所以render函数不会被调用
  // btn-handler里的slot文档可以写在这里
  /**
   * 扩展的打印区域<br>0.17 新增
   * @slot print-content-bottom
   * @binding {scope} FlowData 整个流程上下文信息
   */
  render(h) {
    // btn-handler里的事件，可以在这里写文档
    return <span />
  },
  provide() {
    return {
      attachValidate: this.attachValidate,
      updateActivedAtt: this.updateActivedAtt,
      webflow: this,
    }
  },
}
</script>

<style module lang="scss">
@use '@/common/design' as *;
$exclamation-light-color: #ccc;
.theme {
  color: $primary-color;
}
.actions {
  margin: 0 -0.5%;
  div {
    width: 24%;
    opacity: 0;
  }
}
.nodes {
  button {
    margin-top: 10px;
  }
}

.webflow {
  z-index: 1;
  height: 100vh;
}
.main {
  display: flex;
  justify-content: space-between;
  height: calc(100vh - 64px);
  padding: $padding-lg;
  &.fold-main {
    padding-right: 0;
  }
  .middle-btn {
    position: relative;
    width: 30px;
    &.shrink {
      width: 20px;
    }
  }
}
.form {
  flex: auto;
  float: left;
  width: calc(100% - 370px); // add by zzb 450px->370px
  height: 100%;
  &.collapse {
    width: calc(100% - 20px);
  }
  .attachmentex {
    :global(.ant-card-body) {
      padding: 0 12px 12px 12px;
    }
    :global(.has-error) {
      position: absolute;
      bottom: 0;
    }
  }
  > :global(.ant-tabs) {
    > :global(.ant-tabs-top-bar .ant-tabs-nav-wrap) {
      text-align: center;
    }

    height: 100%;
    > :global(.ant-tabs-content) {
      position: relative;
      height: calc(100% - 64px);
      overflow: auto;
      :global(.ant-tabs-tabpane.ant-tabs-tabpane-active) {
        display: flex;
        flex-direction: column;
        height: 100%;
        min-height: 100%;
        :global(.ant-card) {
          margin-bottom: $padding-lg;
          &.attachmentex {
            margin-bottom: 0;
            + .attachmentex {
              margin-top: 20px;
            }
          }
        }
        :global(> .ant-card:last-child) {
          flex: 1 0 auto;
          margin-bottom: 0;
        }
        .wpscard {
          :global(.ant-card-body) {
            position: absolute;
            width: 100%;
            height: 100%;
            overflow: hidden;
          }

          flex: 1;
          + .attachmentex {
            flex: none;
            height: auto;
          }
        }
      }
    }
    > :global(.ant-tabs-top-bar) {
      height: 48px;
      background-color: $body-background;
    }
  }

  :global(#logskeleton) {
    flex: 1 0 auto;
    margin-bottom: 0;
  }
}
.operate-area {
  position: relative;
  display: flex;
  flex-direction: column;
  float: right;
  width: 350px; // add by zzb 450px -> 350px
  height: 100%;
  overflow: auto;
  textarea {
    resize: none;
  }
  .write-opinion {
    flex: 0 0 auto;
  }
  :global(.ant-card) {
    margin-bottom: $padding-sm;
    // overflow: hidden;
  }
  :global(.ant-card:last-child) {
    flex: 1 1 auto;
    margin-bottom: 0;
  }
  :global(.ant-badge-count) {
    background-color: $primary-color;
  }
  :global(.ant-badge) {
    vertical-align: sub;
  }
  &.collapse {
    width: 0;
  }

  counter-reset: oindex;
  :global(sup.ant-scroll-number.ant-badge-count::after) {
    content: counter(oindex);
    counter-increment: oindex;
  }
}
.fold {
  position: absolute;
  top: calc(50% - 30px);
  right: 0;
  z-index: 2;
  width: 15px;
  height: 75px;
  padding: 0;
  border-radius: 10px 0 0 10px;
}
.notebtn {
  left: 20px;
}

.has-error {
  textarea {
    &:hover,
    &:focus {
      border-color: $alert-error-icon-color;
      box-shadow: none;
    }

    border-color: $alert-error-icon-color;
  }
}
.comitbtns {
  flex: 0 0 auto;
  :global(.ant-card-body) {
    padding: 16px 48px;
  }
}

.candidate {
  :global(.ant-modal-body) {
    padding: 12px;
  }
}
.exclamation {
  color: $exclamation-light-color;
}
.current-operator {
  :global(.ant-checkbox-wrapper:first-child) {
    margin-left: 8px;
  }
}
:global(.browser-ie) {
  .wpscard {
    :global(.ant-card-body) {
      height: calc(100vh - 170px);
      background: white;
    }
  }
}
.error {
  color: $error-color;
}
.remindclock {
  position: absolute;
  top: 0;
  left: 50%;
  width: 372px;
  margin-left: -186px;
  text-align: center;
  .remind {
    margin-right: 6px;
  }
  div {
    display: inline-block;
  }
  :global(.ant-tag) {
    padding: 10px;
    font-size: $font-size-base;
  }
}
.logo {
  h3 {
    color: $white;
  }
}

.lefttime {
  position: relative;
  color: $warning-color;
}

.busy {
  z-index: 900;
  :global(#nprogress .peg) {
    box-shadow: 0 0 10px #fff, 0 0 5px #fff;
  }
  :global(#nprogress .spinner-icon) {
    border-top-color: $primary-4;
    border-left-color: $primary-4;
  }
  :global(#nprogress .bar) {
    z-index: 999;
  }
  :global(#nprogress .spinner) {
    z-index: 999;
  }
}
.opinion-filter {
  display: inline-block;
  margin-top: -5px;
}
.att-form {
  display: flex;
  justify-content: space-between;
  width: 100%;
  height: 100%;
  overflow: hidden;
  .preview {
    flex: 1;
    overflow: auto;
    &.view-fold {
      display: none;
    }
  }
  .separate {
    width: 15px;
  }
  .flowform {
    flex: 1;
    height: 100%;
    overflow: auto;
    &.view-fold {
      display: none;
    }
  }

  > button {
    position: fixed;
    top: 350px;
    z-index: 10;
    width: 15px;
    height: 75px;
    padding: 0;
    font-size: 0.8em;
    line-height: 1.3em;
    white-space: normal;
    transition: none;
  }
  > button:first-child {
    left: 5px;
    border-radius: 10px 0 0 10px;
  }
  > button:nth-child(2) {
    left: calc(100% - 470px - 28px);
    border-radius: 0 10px 10px 0;
  }
}
.collapse .att-form > button:nth-child(2) {
  left: calc(100% - 28px);
}
.dispsign {
  display: inline-block;
  margin-right: 15px;
}
.opinionextra {
  display: flex;
}
.flowys {
  .yshint {
    margin-left: 10px;
    cursor: pointer;
  }
  .checkallys {
    float: right;
  }
}
.checkcontent {
  :global(.ant-checkbox-group-item) {
    display: flex;
    :global(.ant-checkbox) {
      display: inline-block;
      height: 100%;
      margin-top: 7px;
    }
  }
}
</style>
