king
2023-02-13 32f1d2179f6d7ccb5b167aa40116a59e68851a90
2023-02-13
3个文件已修改
12个文件已添加
933 ■■■■■ 已修改文件
src/assets/img/file-excel-fill.png 补丁 | 查看 | 原始文档 | blame | 历史
src/assets/img/file-fill.png 补丁 | 查看 | 原始文档 | blame | 历史
src/assets/img/file-pdf-fill.png 补丁 | 查看 | 原始文档 | blame | 历史
src/assets/img/file-ppt-fill.png 补丁 | 查看 | 原始文档 | blame | 历史
src/assets/img/file-word-fill.png 补丁 | 查看 | 原始文档 | blame | 历史
src/assets/img/picture-fill.png 补丁 | 查看 | 原始文档 | blame | 历史
src/assets/img/rar.png 补丁 | 查看 | 原始文档 | blame | 历史
src/tabviews/custom/components/module/voucher/index.jsx 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/tabviews/custom/components/module/voucher/resetAttach/addAttach/fileupload/index.jsx 266 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/tabviews/custom/components/module/voucher/resetAttach/addAttach/fileupload/index.scss 29 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/tabviews/custom/components/module/voucher/resetAttach/addAttach/index.jsx 159 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/tabviews/custom/components/module/voucher/resetAttach/documents/index.jsx 83 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/tabviews/custom/components/module/voucher/resetAttach/documents/index.scss 补丁 | 查看 | 原始文档 | blame | 历史
src/tabviews/custom/components/module/voucher/resetAttach/index.jsx 196 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/tabviews/custom/components/module/voucher/resetAttach/index.scss 188 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/assets/img/file-excel-fill.png
src/assets/img/file-fill.png
src/assets/img/file-pdf-fill.png
src/assets/img/file-ppt-fill.png
src/assets/img/file-word-fill.png
src/assets/img/picture-fill.png
src/assets/img/rar.png
src/tabviews/custom/components/module/voucher/index.jsx
@@ -180,7 +180,9 @@
        })
      } else {
        this.setState({
          typeOptions: typeOptions
          typeOptions: typeOptions,
          orgcode: res.orgcode,
          orgname: res.orgname,
        })
      }
@@ -308,8 +310,8 @@
        charType: res.voucher_class,
        charName: res.voucher_char,
        charInt: res.voucher_char_int,
        orgcode: res.orgcode,
        orgname: res.orgname,
        // orgcode: res.orgcode,
        // orgname: res.orgname,
        tbdata: fromJS(data).toJS(),
        status: 'saved'
      })
@@ -703,7 +705,7 @@
  }
  render() {
    const { type, status, loading, config, typeOptions, charType, charInt, data, vouDate, username, remark, attachments, title, attachlist } = this.state
    const { type, status, book, loading, config, orgcode, typeOptions, charType, charInt, data, vouDate, username, remark, attachments, title, attachlist } = this.state
    return (
      <div className="menu-voucher-wrap" style={config.style}>
@@ -736,7 +738,7 @@
            </div>
            <div className="voucher-affix">
              附单据 <InputNumber precision={0} value={attachments || 0} autoComplete="off" onChange={this.changeAttach}/> 张
              <ResetAttach attachlist={attachlist} onChange={(vals) => this.setState({attachlist: vals})}/>
              <ResetAttach config={config} book={book} orgcode={orgcode} attachlist={attachlist} onChange={(vals) => this.setState({attachlist: vals})}/>
              <ResetRemark remark={remark} ID={config.uuid + 'remark'} onChange={(val) => this.setState({remark: val})}/>
            </div>
          </div> : null}
src/tabviews/custom/components/module/voucher/resetAttach/addAttach/fileupload/index.jsx
New file
@@ -0,0 +1,266 @@
import React, {Component} from 'react'
import PropTypes from 'prop-types'
import { is, fromJS } from 'immutable'
import moment from 'moment'
import { Upload, Button, Progress, notification } from 'antd'
import { UploadOutlined } from '@ant-design/icons'
import SparkMD5 from 'spark-md5'
import Api from '@/api'
import './index.scss'
class FileUpload extends Component {
  static propTpyes = {
    config: PropTypes.object,  // 表单信息
    onChange: PropTypes.func,  // 表单变化
  }
  state = {
    percent: 0,
    accept: '',
    accepts: null,
    maxFile: null,
    showprogress: false,
    maxSize: 0,
    filelist: []
  }
  UNSAFE_componentWillMount () {
    const { config } = this.props
    let filelist = []
    let accept = ''
    let accepts = null
    this.setState({
      accept,
      accepts,
      filelist,
      maxSize: config.maxSize || 0,
      maxFile: config.maxfile
    })
  }
  shouldComponentUpdate (nextProps, nextState) {
    return !is(fromJS(this.state), fromJS(nextState))
  }
  onChange = ({ fileList }) => {
    fileList = fileList.map(item => {
      if (item.status === 'error' && /^<!DOCTYPE html>/.test(item.response)) {
        item.response = ''
      }
      return item
    })
    this.setState({filelist: fileList})
  }
  onRemove = file => {
    this.setState({filelist: []})
    this.props.onChange('')
  }
  onUpdate = (url, name) => {
    let filelist = fromJS(this.state.filelist).toJS()
    if (filelist[0] && url) {
      filelist[0].status = 'done'
      filelist[0].response = url
    }
    this.setState({filelist})
    this.props.onChange(url, name)
  }
  onFail = (msg) => {
    let filelist = this.state.filelist.map(item => {
      if (!item.url && !item.response && !item.status) {
        item.status = 'error'
      }
      return item
    })
    this.setState({filelist, showprogress: false, percent: 0})
    notification.warning({
      top: 92,
      message: msg || '文件上传失败!',
      duration: 5
    })
  }
  shardupload = (param, name) => {
    let form = new FormData()
    form.append('file', param.binary)
    form.append('fileMd5', param.fileMd5)
    form.append('shardingMd5', param.fileMd5)
    form.append('baseDomain', window.GLOB.baseurl)
    form.append('rootPath', 'Content/images/upload/')
    form.append('fileName', param.fileName)
    form.append('fileExt', param.fileType)
    form.append('shardingCnt', 1)
    form.append('shardingNo', 1)
    form.append('LoginUID', sessionStorage.getItem('LoginUID') || '')
    form.append('UserID', sessionStorage.getItem('UserID') || '')
    Api.getLargeFileUpload(form).then(res => {
      if (res.status) {
        if (res.urlPath) {
          this.onUpdate(res.urlPath, name)
        } else {
          this.onFail()
        }
        this.setState({
          percent: 100
        }, () => {
          setTimeout(() => {
            this.setState({
              showprogress: false,
              percent: 0
            })
          }, 200)
        })
      } else {
        this.onFail(res.message)
      }
    })
  }
  getuuid = () => {
    let uuid = []
    let _options = '0123456789abcdefghigklmnopqrstuv'
    for (let i = 0; i < 19; i++) {
      uuid.push(_options.substr(Math.floor(Math.random() * 0x20), 1))
    }
    uuid = uuid.join('')
    return uuid
  }
  beforeUpload = (file) => {
    const { accepts, maxSize } = this.state
    if (accepts && file.name) {
      let pass = false
      accepts.forEach(type => {
        if (new RegExp(type + '$', 'ig').test(file.name)) {
          pass = true
        }
      })
      if (!pass) {
        setTimeout(() => {
          this.onFail('文件格式错误!')
        }, 10)
        return false
      }
    }
    if (maxSize) {
      let fileSize = file.size / 1024 / 1024
      if (fileSize > maxSize) {
        setTimeout(() => {
          this.onFail(`文件大小不可超过${maxSize}M`)
        }, 10)
        return false
      }
    }
    this.setState({
      showprogress: true,
      percent: 0
    })
    // 兼容性的处理
    let spark = new SparkMD5.ArrayBuffer()         // 对arrayBuffer数据进行md5加密,产生一个md5字符串
    let totalFileReader = new FileReader()         // 用于计算出总文件的fileMd5
    let param = {}
    param.fileName = file.name.replace(/\.{1}[^.]*$/ig, '')  // 文件名(去除后缀名)
    param.fileType = file.name.replace(/^.*\.{1}/ig, '')     // 文件类型
    if (!/^[A-Za-z0-9]+$/.test(param.fileName)) {            // 文件名称含有英文及数字之外字符时,名称系统生成
      param.fileName = moment().format('YYYYMMDDHHmmss') + this.getuuid()
    }
    totalFileReader.readAsArrayBuffer(file)
    totalFileReader.onload = (e) => {   // 对整个totalFile生成md5
      spark.append(e.target.result)
      param.fileMd5 = spark.end()       // 计算整个文件的fileMd5
      param.binary = file
      let _param = new FormData()
      _param.append('fileMd5', param.fileMd5)
      Api.getFilePreUpload(_param).then(res => {
        if (res.status && res.urlPath) {
          this.onUpdate(res.urlPath, file.name)
          this.setState({
            percent: 100
          }, () => {
            setTimeout(() => {
              this.setState({
                showprogress: false,
                percent: 0
              })
            }, 200)
          })
        } else {
          this.shardupload(param, file.name)
        }
      })
    }
    totalFileReader.onerror = () => {
      this.onFail('文件读取失败!')
    }
    return false
  }
  /**
   * @description 组件销毁,清除state更新
   */
  componentWillUnmount () {
    this.setState = () => {
      return
    }
  }
  render() {
    const { showprogress, percent, filelist, accept } = this.state
    let uploadable = 'attach-form-container '
    if (filelist.length >= 1) {
      uploadable += 'limit-fileupload'
    }
    const props = {
      name: 'file',
      disabled: showprogress,
      listType: 'text',
      fileList: filelist,
      action: null,
      accept: accept,
      method: 'post',
      multiple: false,
      onChange: this.onChange,
      onRemove: this.onRemove,
      beforeUpload: this.beforeUpload,
      className: uploadable
    }
    return (
      <Upload {...props}>
        <Button>
          <UploadOutlined /> 点击上传
        </Button>
        {showprogress ? <Progress percent={percent} size="small" /> : null}
      </Upload>
    )
  }
}
export default FileUpload
src/tabviews/custom/components/module/voucher/resetAttach/addAttach/fileupload/index.scss
New file
@@ -0,0 +1,29 @@
.attach-form-container {
  .ant-progress-small.ant-progress-line {
    position: absolute;
    bottom: -20px;
    left: 0px;
  }
  a[href^="data"] {
    pointer-events: none;
    .anticon-eye-o {
      display: none;
    }
  }
}
.attach-form-container.limit-fileupload {
  > .ant-upload {
    display: inline;
    >.ant-upload {
      >input {
        display: none;
      }
      >button {
        display: none;
      }
    }
  }
  > .ant-upload-select-picture-card {
    display: none;
  }
}
src/tabviews/custom/components/module/voucher/resetAttach/addAttach/index.jsx
New file
@@ -0,0 +1,159 @@
import React, {Component} from 'react'
import { Form, Input, Select, Radio, Col } from 'antd'
import MKFileUpload from './fileupload'
const { TextArea } = Input
class AddAttach extends Component {
  state = {
    f_method: 'upload',
  }
  handleConfirm = () => {
    const { files } = this.props
    return new Promise((resolve, reject) => {
      this.props.form.validateFieldsAndScroll((err, values) => {
        if (err) return
        files.forEach(item => {
          if (item.data_code === values.data_code) {
            values.data_name = item.data_name
          }
        })
        if (values.f_method === 'input') {
          values.url = values.fileurl
        }
        resolve(values)
      })
    })
  }
  fileChange = (val, name) => {
    if (name) {
      this.props.form.setFieldsValue({attachments_title: name})
    }
  }
  render() {
    const { files } = this.props
    const { getFieldDecorator } = this.props.form
    const { f_method } = this.state
    const formItemLayout = {
      labelCol: {
        xs: { span: 24 },
        sm: { span: 6 }
      },
      wrapperCol: {
        xs: { span: 24 },
        sm: { span: 16 }
      }
    }
    return (
      <Form {...formItemLayout} onKeyDown={this.onEnterSubmit}>
        <Col span={12}>
          <Form.Item label="文件夹">
            {getFieldDecorator('data_code', {
              initialValue: '',
              rules: [
                {
                  required: true,
                  message: '请选择文件夹!'
                }
              ]
            })(<Select>
              {files.map((option, i) =>
                <Select.Option key={i} value={option.data_code}>{option.data_name}</Select.Option>
              )}
            </Select>)}
          </Form.Item>
        </Col>
        <Col span={12}>
          <Form.Item label="添加方式">
            {getFieldDecorator('f_method', {
              initialValue: 'upload',
              rules: [
                {
                  required: true,
                  message: '请选择添加方式!'
                }
              ]
            })(<Radio.Group onChange={(e) => this.setState({f_method: e.target.value})}>
                <Radio value="upload">上传</Radio>
                <Radio value="input">输入</Radio>
              </Radio.Group>)}
          </Form.Item>
        </Col>
        {f_method === 'upload' ? <Col span={12}>
          <Form.Item label="文件">
            {getFieldDecorator('url', {
              initialValue: '',
              rules: [
                {
                  required: true,
                  message: '请添加文件!'
                }
              ]
            })(<MKFileUpload config={{
                initval: '',
                compress: 'false',
                suffix: '',
                maxfile: 1,
                fileType: 'text'
              }} onChange={(val, name) => this.fileChange(val, name)} />)}
          </Form.Item>
        </Col> : <Col span={24}>
          <Form.Item label="文件地址">
            {getFieldDecorator('fileurl', {
              initialValue: '',
              rules: [
                {
                  required: true,
                  message: '请输入文件地址!'
                }
              ]
            })(<TextArea autoSize={{ minRows: 2 }}/>)}
          </Form.Item>
        </Col>}
        <Col span={12}>
          <Form.Item label="文件名">
            {getFieldDecorator('attachments_title', {
              initialValue: '',
              rules: [
                {
                  required: true,
                  message: '请输入文件名!'
                },
                {
                  max: 50,
                  message: '最大长度为50位!'
                }
              ]
            })(<Input />)}
          </Form.Item>
        </Col>
        <Col span={24}>
          <Form.Item label="备注">
            {getFieldDecorator('remark', {
              initialValue: '',
              rules: [
                {
                  max: 512,
                  message: '最大长度为512位!'
                }
              ]
            })(<TextArea autoSize={{ minRows: 2 }}/>)}
          </Form.Item>
        </Col>
        <div style={{clear: 'both'}}></div>
      </Form>
    )
  }
}
export default Form.create()(AddAttach)
src/tabviews/custom/components/module/voucher/resetAttach/documents/index.jsx
New file
@@ -0,0 +1,83 @@
import React, {Component} from 'react'
class Documents extends Component {
  state = {
    folders: [],
    docs: [],
    selectKey: [],
    actFolder: ''
  }
  UNSAFE_componentWillMount() {
    const { documents } = this.props
    let folders = []
    let actFolder = documents[0].folder
    let docs = []
    documents.forEach(item => {
      folders.push(item.folder)
      if (item.folder === actFolder) {
        docs.push(item)
      }
    })
    this.setState({selectKey: [], folders: Array.from(new Set(folders)), actFolder: actFolder, docs})
  }
  submit = () => {
    this.props.onChange()
  }
  checkfolder = (val) => {
    const { documents } = this.props
    const { actFolder } = this.state
    if (actFolder === val) return
    let docs = []
    documents.forEach(item => {
      if (item.folder === val) {
        docs.push(item)
      }
    })
    this.setState({actFolder: val, selectKey: [], docs})
  }
  render() {
    const { folders, docs, selectKey, actFolder } = this.state
    let checkAll = ''
    if (selectKey.length > 0) {
      checkAll = selectKey.length < docs.length ? ' half' : ' active'
    }
    return (
      <div className="document-wrap">
        <div className="document-title">
          <div className="folder-box">文件夹</div>
          <div className="folder">
            <span className={'square-select' + checkAll}></span>
            <span>文件</span>
          </div>
        </div>
        <div className="document-body">
          <div className="doc-name">
            {folders.map(folder => (<div className={actFolder === folder ? 'active' : ''} onClick={() => this.checkfolder(folder)} key={folder}>{folder}</div>))}
          </div>
          <div className="file-wrap">
            {docs.map(doc => {
              return <div className="file-item" key={doc.id}>
                <span className={'square-select' + (selectKey.indexOf(doc.id) > -1 ? ' active' : '')}></span>
                <img src={doc.icon} alt=""/>
                <span className="file-name">{doc.attachments_title}</span>
              </div>
            })}
          </div>
        </div>
      </div>
    )
  }
}
export default Documents
src/tabviews/custom/components/module/voucher/resetAttach/documents/index.scss
src/tabviews/custom/components/module/voucher/resetAttach/index.jsx
@@ -1,32 +1,170 @@
import React, {Component} from 'react'
import { fromJS } from 'immutable'
import { Button, Modal } from 'antd'
import { Button, Modal, notification } from 'antd'
import moment from 'moment'
import { DeleteOutlined } from '@ant-design/icons'
// import md5 from 'md5'
import Api from '@/api'
import AddAttach from './addAttach'
import Documents from './documents'
import './index.scss'
import wordImg from '@/assets/img/file-word-fill.png'
import excelImg from '@/assets/img/file-excel-fill.png'
import fileImg from '@/assets/img/file-fill.png'
import pdfImg from '@/assets/img/file-pdf-fill.png'
import pptImg from '@/assets/img/file-ppt-fill.png'
import picImg from '@/assets/img/picture-fill.png'
import rarImg from '@/assets/img/rar.png'
class ResetAttach extends Component {
  state = {
    visible: false,
    list: ''
    upVisible: false,
    docVisible: false,
    files: [{data_code: 'ddd', data_name: '凭证'}],
    list: [],
    documents: [
      {id: '1223', folder: '凭证附件', icon: excelImg, attachments_title: '主表20230130173553.xlsx', attachments_url: 'http://主表20230130173553.xlsx'},
      {id: '1224', folder: '凭证附件', icon: excelImg, attachments_title: '主表2sdfsafjifjiji.xlsx', attachments_url: 'http://主表2sdfsafjifjiji.xlsx'},
      {id: '1225', folder: '凭证附件', icon: excelImg, attachments_title: '主表20230sjiejgiej.xlsx', attachments_url: 'http://主表20230sjiejgiej.xlsx'},
      {id: '1227', folder: '回执', icon: excelImg, attachments_title: '主表2023sdfrgtgfgd.xlsx', attachments_url: 'http://主表2023sdfrgtgfgd.xlsx'},
    ],
    selectDocs: []
  }
  submit = () => {
    this.setState({remark: '', visible: false})
    this.props.onChange()
    this.setState({visible: false})
    this.props.onChange(this.state.list)
  }
  trigger = () => {
    const { attachlist } = this.props
    let list = fromJS(attachlist).toJS()
    list = list.map(item => {
      item.icon = this.getIcon(item.attachments_url)
      return item
    })
    this.setState({visible: true, list: fromJS(attachlist).toJS()})
    this.setState({visible: true, list: list})
  }
  triggerUpload = () => {
  upSubmit = () => {
    const { config, book, orgcode } = this.props
    if (!book) {
      notification.warning({
        top: 92,
        message: '请选择账套!',
        duration: 5
      })
      return
    }
    let ID = (() => {
      let uuid = []
      let options = '0123456789abcdefghigklmnopqrstuv'
      for (let i = 0; i < 19; i++) {
        uuid.push(options.substr(Math.floor(Math.random() * 0x20), 1))
      }
      uuid = moment().format('YYYYMMDDHHmmssSSS') + uuid.join('')
      return uuid.toUpperCase()
    })()
    this.formRef.handleConfirm().then(res => {
      let param = {
        func: 's_fcc_voucher_attachments_addupt',
        data_code: res.data_code,
        data_name: res.data_name,
        id: ID,
        orgcode: orgcode,
        voucher_at_lp: '',
        attachments_title: res.attachments_title,
        f_method: res.f_method,
        attachments_url: res.url,
        remark: res.remark,
        status: config.wrap.attachStatus !== 10 ? 0 : 10,
        statusname: config.wrap.attachStatus !== 10 ? '待审核' : '已审核',
        typename: config.name,
        UserName: sessionStorage.getItem('User_Name') || '',
        FullName: sessionStorage.getItem('Full_Name') || '',
        BID: book.id
      }
      Api.genericInterface(param).then(result => {
        if (!result.status) {
          notification.warning({
            top: 92,
            message: result.message,
            duration: 5
          })
          return
        }
        let list = fromJS(this.state.list).toJS()
        let item = {
          id: ID,
          data_code: res.data_code,
          data_name: res.data_name,
          attachments_title: res.attachments_title,
          attachments_url: res.url
        }
        item.icon = this.getIcon(res.url)
        list.push(item)
        this.setState({list: list})
      })
    })
  }
  deleteFile = (id) => {
    this.setState({list: this.state.list.filter(item => item.id !== id)})
  }
  getIcon = (url) => {
    let type = 'file'
    if (/(.png|.jpg|.gif|.jpeg)$/i.test(url)) {
      type = 'pic'
    } else if (/(.doc|.docx)$/i.test(url)) {
      type = 'word'
    } else if (/(.xls|.xlsx)$/i.test(url)) {
      type = 'excel'
    } else if (/(.zip|.rar)$/i.test(url)) {
      type = 'rar'
    } else if (/.pdf$/i.test(url)) {
      type = 'pdf'
    } else if (/.pptx$/i.test(url)) {
      type = 'ppt'
    }
    let icon = fileImg
    if (type === 'excel') {
      icon = excelImg
    } else if (type === 'word') {
      icon = wordImg
    } else if (type === 'pdf') {
      icon = pdfImg
    } else if (type === 'pic') {
      icon = picImg
    } else if (type === 'ppt') {
      icon = pptImg
    } else if (type === 'rar') {
      icon = rarImg
    }
    return icon
  }
  docSubmit = () => {
  }
  render() {
    const { visible } = this.state
    const { visible, upVisible, docVisible, files, list, documents } = this.state
    return (
      <>
@@ -42,13 +180,47 @@
          cancelText=""
          destroyOnClose
        >
          <Button type="link" className="attach-type-btn" onClick={this.triggerUpload}>上传新文件</Button>
          <Button type="link" className="attach-type-btn" onClick={this.trigger}>从会计电子档案选择</Button>
          <Button type="link" className="attach-type-btn" onClick={() => this.setState({upVisible: true})}>上传新文件</Button>
          <Button type="link" className="attach-type-btn" onClick={() => this.setState({docVisible: true, selectDocs: []})}>从会计电子档案选择</Button>
          <div className="attach-selected-list">
            {list.map(item => {
              return <div className="attach-item" key={item.id}>
                <img src={item.icon} alt=""/>
                <div className="attach-msg">
                  <div>{item.attachments_title}</div>
                  <div>{item.data_name ? item.data_name + ' / ' : ''}{item.attachments_url}</div>
                </div>
                <div>
                  <DeleteOutlined onClick={() => this.deleteFile(item.id)}/>
                </div>
              </div>
            })}
          </div>
        </Modal>
        <Modal
          title="添加附件"
          wrapClassName="voucher-attach-add-wrap"
          visible={upVisible}
          width={700}
          maskClosable={false}
          onOk={this.upSubmit}
          onCancel={() => { this.setState({ upVisible: false })}}
          destroyOnClose
        >
          <AddAttach files={files} wrappedComponentRef={(inst) => this.formRef = inst} submit={this.upSubmit}/>
        </Modal>
        <Modal
          title="电子档案"
          wrapClassName="voucher-attach-document-wrap"
          visible={docVisible}
          width={700}
          maskClosable={false}
          onOk={this.docSubmit}
          onCancel={() => { this.setState({ docVisible: false, selectDocs: [] })}}
          destroyOnClose
        >
          {docVisible ? <Documents documents={documents} onChange={(vals) => this.setState({selectDocs: vals})}/> : null}
        </Modal>
      </>
    )
  }
src/tabviews/custom/components/module/voucher/resetAttach/index.scss
@@ -14,6 +14,37 @@
      overflow-y: auto;
      border: 1px solid #d8d8d8;
      margin-top: 10px;
      .attach-item {
        display: flex;
        border-bottom: 1px solid #f1f1f1;
        img {
          width: 45px;
          height: 45px;
          padding: 9px;
          margin-top: 5px;
        }
        .attach-msg {
          flex: 1;
          width: calc(100% - 95px);
          padding: 5px 0px;
          div {
            color: #000000;
            overflow: hidden;
            text-overflow: ellipsis;
            white-space: nowrap;
          }
        }
        .attach-msg + div {
          width: 50px;
          line-height: 45px;
          margin-top: 5px;
          text-align: center;
          font-size: 16px;
          cursor: pointer;
          color: orangered;
        }
      }
    }
    .attach-selected-list::-webkit-scrollbar {
      width: 7px;
@@ -30,4 +61,161 @@
      background: rgba(0, 0, 0, 0);
    }
  }
}
.voucher-attach-add-wrap {
  .ant-form {
    > .ant-col {
      float: none;
      display: inline-block;
      vertical-align: top;
    }
    > .ant-col-24 {
      .ant-form-item-label {
        width: 12.5%;
      }
      .ant-form-item-control-wrapper {
        width: 84%;
      }
    }
  }
}
.voucher-attach-document-wrap {
  .ant-modal-body {
    padding: 15px;
  }
  .document-wrap {
    .document-title {
      display: flex;
      background: #e8e8e8;
      line-height: 38px;
      border: 1px solid #d8d8d8;
      border-bottom: none;
      .folder-box {
        width: 200px;
        text-align: center;
        border-right: 1px solid #d8d8d8;
      }
      .folder {
        flex: 1;
        .square-select {
          top: 3px;
          margin: 0 10px;
        }
      }
    }
    .document-body {
      border: 1px solid #d8d8d8;
      height: 300px;
      display: flex;
      .doc-name {
        width: 200px;
        text-align: center;
        border-right: 1px solid #d8d8d8;
        height: 100%;
        overflow-y: auto;
        div {
          border-bottom: 1px solid #e8e8e8;
          height: 40px;
          line-height: 40px;
        }
        .active {
          background: var(--mk-sys-color1);
        }
      }
      .file-wrap {
        flex: 1;
        width: calc(100% - 200px);
        height: 100%;
        overflow-y: auto;
        .file-item {
          border-bottom: 1px solid #e8e8e8;
          height: 40px;
          line-height: 40px;
          .square-select {
            top: 4px;
            margin: 0 10px;
          }
          img {
            width: 20px;
            height: 20px;
            margin-top: -2px;
            margin-right: 5px;
          }
          .file-name {
            display: inline-block;
            overflow: hidden;
            text-overflow: ellipsis;
            white-space: nowrap;
            vertical-align: top;
            width: calc(100% - 90px);
          }
        }
      }
    }
    .doc-name::-webkit-scrollbar, .file-wrap::-webkit-scrollbar {
      width: 7px;
    }
    .doc-name::-webkit-scrollbar-thumb, .file-wrap::-webkit-scrollbar-thumb {
      border-radius: 5px;
      box-shadow: inset 0 0 5px rgba(0, 0, 0, 0.13);
      background: rgba(0, 0, 0, 0.13);
    }
    .doc-name::-webkit-scrollbar-track, .file-wrap::-webkit-scrollbar-track {
      box-shadow: inset 0 0 5px rgba(0, 0, 0, 0.05);
      border-radius: 3px;
      border: 1px solid rgba(0, 0, 0, 0.07);
      background: rgba(0, 0, 0, 0);
    }
    .square-select {
      position: relative;
      display: inline-block;
      width: 16px;
      height: 16px;
      border: 1px solid #cccccc;
      box-sizing: content-box;
      margin: auto;
      margin-right: 5px;
      background-color: #ffffff;
      transition: border-color 0.2s;
      cursor: pointer;
    }
    .square-select.active {
      border-color: var(--mk-sys-color);
      background: var(--mk-sys-color);
    }
    .square-select.half {
      border-color: var(--mk-sys-color);
    }
    .square-select.half::before {
      display: none;
    }
    .square-select.half::after {
      position: absolute;
      top: 4px;
      left: 4px;
      content: ' ';
      display: block;
      width: 8px;
      height: 8px;
      background: var(--mk-sys-color);
    }
    .square-select::before {
      position: relative;
      top: 1px;
      left: 6px;
      content: ' ';
      display: block;
      width: 5px;
      height: 11px;
      border-style: solid;
      border-width: 0 2px 2px 0;
      border-color: #ffffff;
      transform: rotate(45deg);
    }
  }
}