wenbobowen %!s(int64=4) %!d(string=hai) anos
pai
achega
83594556c7
Modificáronse 37 ficheiros con 1314 adicións e 2 borrados
  1. BIN=BIN
      .DS_Store
  2. 5 1
      package.json
  3. 2 1
      src/app.js
  4. 19 0
      src/components/detialItemTitle/index.js
  5. 28 0
      src/components/detialItemTitle/style.module.less
  6. 29 0
      src/components/detialModalHeader/index.js
  7. 36 0
      src/components/detialModalHeader/style.module.less
  8. 26 0
      src/components/detialTable/index.js
  9. 17 0
      src/components/dividerBox/index.js
  10. 16 0
      src/components/dividerBox/style.module.less
  11. 81 0
      src/components/editTable/index.js
  12. 26 0
      src/components/editTable/style.module.less
  13. 72 0
      src/components/fromChooseItem/index.js
  14. 11 0
      src/components/fromChooseItem/style.module.less
  15. 32 0
      src/components/header/index.js
  16. 34 0
      src/components/header/style.less
  17. 30 0
      src/components/menuCard/index.js
  18. 55 0
      src/components/menuCard/style.less
  19. 14 0
      src/components/modalFooter/index.js
  20. 14 0
      src/components/modalFooter/style.module.less
  21. 24 0
      src/components/pageHeader/components/mainTitle/index.js
  22. 11 0
      src/components/pageHeader/components/mainTitle/style.less
  23. 50 0
      src/components/pageHeader/components/searchHeader/index.js
  24. 33 0
      src/components/pageHeader/components/searchHeader/style.module.less
  25. 35 0
      src/components/pageHeader/index.js
  26. 38 0
      src/components/pageHeader/style.less
  27. 73 0
      src/components/searchInput/components/searchPeople/index.js
  28. 28 0
      src/components/searchInput/components/searchPeople/style.module.less
  29. 196 0
      src/components/searchInput/index.js
  30. 24 0
      src/components/searchInput/style.module.less
  31. 1 0
      src/index.js
  32. 19 0
      src/request/common/api.js
  33. 38 0
      src/request/common/config/index.js
  34. 103 0
      src/request/common/request/index.js
  35. 41 0
      src/utils/index.js
  36. 27 0
      webpack/dev.config.js
  37. 26 0
      webpack/prod.config.js

BIN=BIN
.DS_Store


+ 5 - 1
package.json

@@ -17,7 +17,8 @@
     "dev": "webpack-dev-server"
   },
   "dependencies": {
-    "antd": "^3.10.3"
+    "antd": "^3.10.3",
+    "clsx": "^1.1.1"
   },
   "devDependencies": {
     "@babel/core": "7.4.5",
@@ -25,6 +26,7 @@
     "@babel/preset-env": "7.4.5",
     "@babel/preset-react": "^7.0.0",
     "babel-loader": "^8.0.5",
+    "axios": "^0.21.1",
     "clean-webpack-plugin": "^3.0.0",
     "css-loader": "^2.1.1",
     "html-webpack-plugin": "^3.2.0",
@@ -33,6 +35,8 @@
     "react-dom": "^16.6.0",
     "sass-loader": "^9.0.2",
     "style-loader": "^0.23.1",
+    "less": "^3.10.3",
+    "less-loader": "^8.0.0",
     "url-loader": "^1.1.2",
     "webpack": "^4.29.6",
     "webpack-cli": "^3.2.3",

+ 2 - 1
src/app.js

@@ -1,6 +1,7 @@
 import React from 'react';
 import ReactDOM from 'react-dom';
 import AgileTCEditor from './kityminderEditor';
+import { FromChooseItem } from './index'
 
 // 本地调试DEMO
 class App extends React.Component {
@@ -22,7 +23,7 @@ class App extends React.Component {
     if (tab === 1) {
       content = <AgileTCEditor />;
     } else {
-      content = <div>sss</div>;
+      content = <FromChooseItem />;
     }
     return (
       <div>

+ 19 - 0
src/components/detialItemTitle/index.js

@@ -0,0 +1,19 @@
+import React from 'react'
+import styles from './style.module.less'
+export default function DetialTable(props) {
+  const { title, children, openBtn = false } = props
+  return (
+    <div className={styles.headerTitle}>
+      <div className={styles.titleLeftIcon} />
+      <div className={styles.titleLeftName}>{ title }</div>
+      {
+        openBtn && 
+        <div className={styles.editBtn}>
+          {/* <i /> */}
+          {/* <slot name="handleSlot" /> */}
+          {children}
+        </div>
+      }
+    </div>
+  );
+}

+ 28 - 0
src/components/detialItemTitle/style.module.less

@@ -0,0 +1,28 @@
+.headerTitle {
+  align-items: center;
+  .titleLeftIcon {
+    width: 4px;
+    height: 17px;
+    background: var(--brand-1);
+    border-radius: 1px;
+    display: inline-block;
+    vertical-align: text-top;
+  }
+  .titleLeftName {
+    display: inline-block;
+    width: auto;
+    height: 20px;
+    line-height: 20px;
+    font-size: 16px;
+    // font-family: Helvetica Neue,Helvetica,PingFang SC,Hiragino Sans GB,Microsoft YaHei,"\5FAE\8F6F\96C5\9ED1",Arial,sans-serif;
+    color: var(--font-1);
+    margin-left: 6px;
+  }
+  // .editBtn {
+  //   display: inline-block;
+  //   .el-icon-edit {
+  //     color: #1890FF;
+  //     margin-left: 6px;
+  //   }
+  // }
+}

+ 29 - 0
src/components/detialModalHeader/index.js

@@ -0,0 +1,29 @@
+import React from 'react'
+import clsx from 'clsx'
+import { Icon, Input, Select } from 'antd'
+import styles from './style.module.less'
+
+const { Option } = Select
+
+export default function DetialTable(props) {
+  const { title, openBtn = false, statusRender = null, iconListRender = null, subTitle = '', onChange, onClose } = props
+
+  return (
+    <div className={styles.detialModalHeader}>
+      <header className={clsx(styles.header, 'flex-center-between')}>
+        <div>
+          {subTitle && <span className={styles.subTitle}>bug-123</span>}
+          {statusRender}
+        </div>
+        <div className='flex-center-between'>
+          {iconListRender}
+          <Icon type="close" className={styles.iconClose} onClick={() => onClose()}/>
+        </div>
+      </header>
+      <div className={styles.title}>
+        <Input value={title} placeholder="Basic usage" className={styles.input} onChange={(e) => onChange({'title': e.target.value})} />
+        <div className={styles.inputText}>{title}</div>
+      </div>
+    </div>
+  );
+}

+ 36 - 0
src/components/detialModalHeader/style.module.less

@@ -0,0 +1,36 @@
+.detialModalHeader {
+  margin-bottom: 35px;
+  .subTitle {
+    font-size: 16px;
+    color: var(--font-3);
+    margin-right: 20px;
+  }
+  .iconClose {
+    margin-left: 20px;
+  }
+  .title {
+    margin-top: 5px;
+    // margin-left: -11px;
+    position: relative;
+    // padding: 5px;
+    .input, .inputText {
+      font-size: 20px;
+      color: var(--font-1);
+    }
+    .inputText {
+      display: block;
+    }
+    .input {
+      display: none;
+    }
+    
+    &:hover {
+      .inputText {
+        display: none;
+      }
+      .input {
+        display: block;
+      }
+    }
+  }
+}

+ 26 - 0
src/components/detialTable/index.js

@@ -0,0 +1,26 @@
+import React from 'react';
+import { Table } from 'antd';
+
+export default function DetialTable(props) {
+  const { columns, data = [], scroll, onChange, total = 0, curIndex, pageSize = 15 } = props
+  return (
+    <div>
+      <Table
+        className="detialTable"
+        size="small"
+        columns={columns}
+        dataSource={data}
+        rowKey="id"
+        pagination= {{
+          size: "default",
+          showQuickJumper: true,
+          current: curIndex,
+          pageSize,
+          total,
+        }}
+        scroll={scroll}
+        onChange={(e)=> onChange(e)}
+      />
+    </div>
+  );
+}

+ 17 - 0
src/components/dividerBox/index.js

@@ -0,0 +1,17 @@
+import React from 'react';
+import styles from './style.module.less'
+
+export default function DividerBox(props) {
+  const { title, children } = props
+
+  return (
+    <div className={styles.dividerBox}>
+      <div className={styles.title}>
+        {title}
+      </div>
+      <div className={styles.content}>
+        {children}
+      </div>
+    </div>
+  );
+}

+ 16 - 0
src/components/dividerBox/style.module.less

@@ -0,0 +1,16 @@
+.dividerBox {
+  border: 1px solid var( --gray-3);
+  padding: 16px 22px;
+  position: relative;
+  border-radius: 4px;
+  .title {
+    display: inline-block;
+    background: #fff;
+    position: absolute;
+    font-size: 12px;
+    top: -12px;
+    margin-left: 16px;
+    padding: 1px 8px;
+    border-radius: 2px 2px 0 0;
+  }
+}

+ 81 - 0
src/components/editTable/index.js

@@ -0,0 +1,81 @@
+import React, { useEffect, useState, useCallback } from 'react';
+import clsx from 'clsx';
+import { Input, Radio, Spin, Select, Icon } from 'antd';
+import { getDeepValue  } from '../../utils'
+import _, { add } from 'lodash';
+import styles from './style.module.less'
+const { TextArea } = Input
+const { Option } = Select
+
+export default function EditTable(props) {
+  const { renderFormData, value = [{}], onChange } = props
+  // const ss = value || [{}]
+  // const [ valueList, setValueList ] = useState(value)
+  function onChangeTableValue(idx, key, val) {
+    let list = _.clone(value)
+    list[idx][key] = val
+    onChange(renderFormData.key, list)
+  }
+  function add(idx) {
+    let list = _.clone(value)
+    list.splice(idx + 1, 0, {})
+    onChange(renderFormData.key, list)
+  }
+  function del(idx) {
+    let list = _.clone(value)
+    list.splice(idx, 1)
+    onChange(renderFormData.key, list)
+  }
+  return (
+    <div className={styles.EditTable}>
+      <table className={styles.table}>
+        <thead>
+          <tr>
+            {
+              renderFormData.tableHeaderRender.map(t => (
+                <th className={styles.head} key={t.name}>{t.name}</th>
+              ))
+            }
+            {/* <th className=""></th> */}
+            {/* <th class="">模块</th>
+            <th>模块名</th> */}
+          </tr>
+        </thead>
+        <tbody>
+            {
+              // map每一行
+              value.map((h, idx) => (
+                // map每一列
+                <tr key={idx} style={{ position: 'relative' }}>
+                {renderFormData.tableBodyRender.map(t => (
+                  <td className={styles.body} key={`${t.key}_${idx}`}>
+                    <Input
+                      placeholder={t.placeholder}
+                      value={h[t.key] || ''}
+                      disabled={renderFormData.disabled}
+                      onChange={(e) => onChangeTableValue(idx, t.key, e.target.value)}
+                    />
+                  </td>
+                ))}
+                  <td className={styles.btnGroup}>
+                    <Icon
+                      className={styles.icon}
+                      type="plus-square"
+                      onClick={() => add(idx)}
+                    />
+                    {value.length > 1
+                    &&
+                    <Icon
+                      className={styles.icon}
+                      type="minus-square"
+                      onClick={() => del(idx)}
+                    />}
+                  </td>
+                </tr>
+              ))
+            }
+        </tbody>
+      </table>
+    </div>
+  );
+}

+ 26 - 0
src/components/editTable/style.module.less

@@ -0,0 +1,26 @@
+.EditTable {
+  .table {
+    width: 100%;
+    .head, .body {
+      border-radius: 4px;
+      border: 1px solid var(--gray-2);
+      padding: 16px 16px;
+    }
+    .head {
+      background: var(--gray-4);
+      text-align: center;
+      line-height: 1.5;
+    }
+    .btnGroup {
+      position: absolute;
+      width: 55px;
+      text-align: center;
+      padding: 32px 0px 0px 0px;
+      .icon {
+        margin: 0 5px;
+        font-size: 14px;
+        color: var(--brand-1);
+      }
+    }
+  }
+}

+ 72 - 0
src/components/fromChooseItem/index.js

@@ -0,0 +1,72 @@
+import React from 'react';
+import clsx from 'clsx'
+import SearchInput from '../searchInput'
+import { Row, Col } from 'antd'
+import styles from './style.module.less'
+
+/**
+ *  name: '成员', // 名称
+    colSpan: 24, // 一行占用多宽
+    key: 'members', // 枚举
+    type: 'radio',
+    disabled: true, // 是否禁用
+    required: true, // 是否必填
+    nameKey: '' // 编辑现实的文案
+    childrenRoute: '' // 原路径是key下面的字符串 编辑时候变成obj 层级下,默认title  eg: in: '' ,in:{'title':''}
+    textInput: true, // 是否查看状态下 true是展示文本不可编辑
+    textInputName: 'xx' // 编辑状态下name名称
+    multiple: true/false // 是否可以多选
+    textInputRender: (d) =>  (<div><span>123123</span></div>), // 特殊渲染
+    options: isOpenExternalNetworkEnum //select情况下的option渲染
+    optionRender: optionRender: (d) =>  (<div><span>{d.title}{d.name}123</span></div>) // 特殊渲染option
+*/
+
+export default function FromChooseItem(props) {
+  const { 
+    viewInEdit = false, // 编辑状态
+    viewInEditChange,  // 修改编辑状态 有该方法就默认为查看编辑状态
+    renderFormList, // 渲染form表单list
+    labelWidth = null,  // label的宽度
+    valueData = {}, // 数据
+    horizontalSpacing = 16, 
+    verticalSpacing = 20, 
+    onChange, 
+    children 
+  } = props
+  return (
+    <div className={styles.fromChooseItem}>
+      <Row gutter={[ horizontalSpacing, verticalSpacing]}>
+        {
+          renderFormList.map(t => (
+            <div key={t.name}>
+              <Col span={t.colSpan} className={styles.colBox}>
+                <span
+                  className={styles.name}
+                  style={{ width: labelWidth || '88px' }}
+                >
+                  {
+                    t.required && !viewInEditChange ?
+                    <span className={clsx(styles.required, styles.width8)}>*</span>
+                    :
+                    <span className={styles.width8}></span>
+                  }
+                  {viewInEditChange ? `${t.textInputName || t.name}:` : t.name}
+                </span>
+                <div className={styles.input}>
+                  <SearchInput
+                    renderFormData={t}
+                    valueData={valueData}
+                    viewInEdit={viewInEdit}
+                    viewInEditChange={viewInEditChange}
+                    onChange={(key, e) => onChange(key, e)}
+                  />
+                </div>
+              </Col>
+              {children}
+            </div>
+          ))
+        }
+      </Row>
+    </div>
+  );
+}

+ 11 - 0
src/components/fromChooseItem/style.module.less

@@ -0,0 +1,11 @@
+@import '../../assets/css/mixin.less';
+.fromChooseItem {
+  .fromItem();
+  .required {
+    color: var(--red-1)
+  }
+  .width8 {
+    display: inline-block;
+    width: 8px;
+  }
+}

+ 32 - 0
src/components/header/index.js

@@ -0,0 +1,32 @@
+import React from 'react';
+import './style.less'
+
+export default function Header(props) {
+  const { name, goto } = props
+  return (
+    <div className='com-headerBox flex-center-between'>
+      <div className="logo-box flex-center-start">
+        <img
+          className="img-logo"
+          src="//pt-starimg.didistatic.com/static/starimg/node/AYdog3q0TQ1612337938928.png"
+          onClick={() => goto()}
+        />
+        <span className="menu">
+          质惠·{name}
+        </span>
+      </div>
+      <div className="utils-box flex-center-end">
+        <div className="item">
+          <i className='iconfont icon'>&#xe60d;</i>
+        </div>
+        <div className="item">
+          <i className='iconfont icon'>&#xe685;</i>
+        </div>
+        <img
+          className="img-avart item"
+          src="//pt-starimg.didistatic.com/static/starimg/node/AYdog3q0TQ1612337938928.png"
+        />
+      </div>
+    </div>
+  );
+}

+ 34 - 0
src/components/header/style.less

@@ -0,0 +1,34 @@
+.com-headerBox {
+  background-color: var(--brand-1);
+  height: 60px;
+  padding: 0 10px;
+  line-height: 60px;
+  .logo-box {
+    .img-logo {
+      width: 40px;
+      vertical-align: middle;
+    }
+    .menu {
+      display: inline-block;
+      margin-left: 10px;
+      color: #fff;
+      font-size: 18px;
+      font-weight: 500;
+    }
+  }
+  .utils-box {
+    .item {
+      margin-right: 20px;
+      .icon {
+        font-size: 24px;
+        color: #fff;
+      }
+    }
+    .img-avart {
+      width: 30px;
+      height: 30px;
+      border: 1px solid transparent;
+      border-radius: 15px;
+    }
+  }
+}

+ 30 - 0
src/components/menuCard/index.js

@@ -0,0 +1,30 @@
+import React from 'react';
+import { Button } from 'antd'
+import './style.less'
+
+export default function MenuCard(props) {
+  // constructor(props) {
+  //   super(props);
+  //   this.state = {}
+  // }
+  // componentDidMount() {
+  // }
+  // render() {
+  const { brandColor, name, desc } = props.data;
+  return (
+    <div className="com-menuCard" style={{ borderLeftColor: `${brandColor}` }}>
+      <div className="logo">
+        <div className="bg" />
+        <i className='iconfont icon'>&#xe60d;</i>
+      </div>
+      <div className="content">
+        <div className="name">{name}</div>
+        <div className="desc">{desc}</div>
+        <div className="btn">
+          <Button style={{ width: '96px' }} type="primary" onClick={() => this.props.goto()}>进入</Button>
+        </div>
+      </div>
+    </div>
+  )
+  // }
+}

+ 55 - 0
src/components/menuCard/style.less

@@ -0,0 +1,55 @@
+.com-menuCard {
+  text-align: center;
+  display: flex;
+  padding: 14px 22px;
+  background: #FFFFFF;
+  box-shadow: 0px 6px 12px rgba(0, 0, 0, 0.16);
+  opacity: 1;
+  border-radius: 0px 4px 4px 0px;
+  border-left: 2px solid #000;
+  .logo {
+    width: 94px;
+    position: relative;
+    .bg{
+      height: 58px;
+      width: 58px;
+      border-radius: 20px;
+      opacity: 0.14;
+      background: var(--brand-1);
+      line-height: 58px;
+    }
+    .icon {
+      font-size: 26px;
+      color: var(--brand-1);
+      position: absolute;
+      top: 10px;
+      left: 16px;
+    }
+  }
+  .content {
+    flex: 1;
+    text-align: left;
+    .name {
+      color: var(--font-1);
+      margin: 18px 0;
+      font-size: 16px;
+      font-weight: 500;
+    }
+    .desc {
+      color: var(--font-2);
+      font-size: 12px;
+      height: 54px;
+      overflow: hidden;
+      text-overflow: ellipsis;
+      display: -webkit-box;
+      -webkit-line-clamp: 3; //(行数)
+      -webkit-box-orient: vertical;
+    }
+    .btn {
+      text-align: center;
+      margin-top: 24px;
+      width: 100%;
+      margin-left: -50px;
+    }
+  }
+}

+ 14 - 0
src/components/modalFooter/index.js

@@ -0,0 +1,14 @@
+import React from 'react';
+import clsx from 'clsx'
+import styles from './style.module.less'
+
+export default function ModalFooter(props) {
+  const { ButtonList, mode = 'default' } = props
+  return (
+    <div className={clsx(styles.footerBox, styles['default'])}>
+      {
+        ButtonList.map(t => (t))
+      }
+    </div>
+  );
+}

+ 14 - 0
src/components/modalFooter/style.module.less

@@ -0,0 +1,14 @@
+.footerBox {
+  &.abs{
+    position: absolute;
+    bottom: 16px;
+    right: 20px;
+  }
+  &.default{
+    text-align: right;
+    padding: 16px 20px;
+  }
+  :global(button) {
+    margin-left: 20px;
+  }
+}

+ 24 - 0
src/components/pageHeader/components/mainTitle/index.js

@@ -0,0 +1,24 @@
+import React from 'react';
+import { Button, Divider } from 'antd'
+
+import './style.less'
+
+export default function MainTitle(props) {
+  const { title, btnText, btnClick } = props
+  return (
+    <div className="com-mainTile">
+      <div className="spaceBetween">
+        <div className="titleBox">
+          <span className="title">{title}</span>
+        </div>
+        {
+          btnText &&
+          <div>
+            <Button type="primary" size="default" onClick={() => btnClick()}>{ btnText }</Button>
+          </div>
+        }
+      </div>
+      <Divider className="marginTop10 marginBottom10"/>
+    </div>
+  );
+}

+ 11 - 0
src/components/pageHeader/components/mainTitle/style.less

@@ -0,0 +1,11 @@
+.com-mainTile {
+  .titleBox {
+    .title {
+      font-size: 22px;
+      letter-spacing: 1px;
+      font-weight: 600;
+      color: rgb(51, 59, 74);
+      padding-left: 16px;
+    }
+  }
+}

+ 50 - 0
src/components/pageHeader/components/searchHeader/index.js

@@ -0,0 +1,50 @@
+import React, { useState } from 'react';
+import FromChooseItem from '../fromChooseItem'
+import clsx from 'clsx'
+import { Button } from 'antd'
+import styles from './style.module.less'
+export default function SearchHeader(props) {
+  const { searchList, valueData, onChange, onSearch } = props
+  const [ showMore, setShowMore ] = useState(false)
+  return (
+    <div className={styles.searchHeader}>
+      <div className={clsx(styles.defaultBox, styles.spaceBetween)}>
+        <div className={styles.content}>
+          <FromChooseItem
+            renderFormList={searchList.default}
+            valueData={valueData}
+            labelWidth={'70px'}
+            onChange={(key, e) => onChange(key, e, true)}
+          />
+        </div>
+        <div className={styles.btn}>
+          <Button type="link" onClick={() => setShowMore(!showMore)}>
+            { showMore? '收起筛选' : '更多筛选' }
+          </Button>
+        </div>
+      </div>
+      {
+        showMore && (
+          <div className={styles.advBox}>
+            <div className={styles.content}>
+              <FromChooseItem
+                renderFormList={searchList.adv}
+                valueData={valueData}
+                labelWidth={'70px'}
+                onChange={(key, e) => onChange(key, e)}
+              />
+            </div>
+            <div className={styles.btnList}>
+              <Button type="primary" onClick={() => onSearch()}>
+                筛选
+              </Button>
+              <Button className={styles.marginLeft} onClick={() => setShowMore(!showMore)}>
+                重置
+              </Button>
+            </div>
+          </div>
+        )
+      }
+    </div>
+  );
+}

+ 33 - 0
src/components/pageHeader/components/searchHeader/style.module.less

@@ -0,0 +1,33 @@
+.searchHeader {
+  .defaultBox {
+    padding-top: 5px;
+    display: flex;
+    .content {
+      padding-left: 8px;
+      flex: 1;
+      margin-right: 10px;
+    }
+    .btn {
+      width: 88px; 
+    }
+  }
+  .advBox {
+    background: #fcfcfc;
+    border-radius: 4px;
+    padding: 16px;
+    padding-left: 8px;
+    // min-height: 100px;
+    margin-top: 22px;
+    border: 1px solid #eee;
+    width: 100%;
+    .content {
+      width: calc(100% - 78px);
+    }
+    .btnList {
+      text-align: end;
+    }
+  }
+}
+.marginLeft {
+  margin-left: 10px;
+}

+ 35 - 0
src/components/pageHeader/index.js

@@ -0,0 +1,35 @@
+import React, { useState } from 'react';
+import MainTitle from './components/mainTitle'
+import SearchHeader from './components/searchHeader'
+import _ from 'lodash';
+// import './index.less'
+
+let timeout = null
+export default function PageHeader(props) {
+  const { title, btnText, btnClick, searchList, onSearch } = props
+  const [data, setData] = useState({})
+  const onChange = (key, value, hot) => {
+    setData({...data, [key]: value })
+    if(hot) {
+      if(timeout) clearTimeout(timeout)
+      timeout = setTimeout(() => {
+        onSearch({...data, [key]: value })
+      }, 500)
+    }
+  }
+  return (
+    <div className='com-pageHeader'>
+      <MainTitle
+        title={title}
+        btnText={btnText}
+        btnClick={() => btnClick()}
+      />
+      <SearchHeader
+        searchList={searchList}
+        valueData={data}
+        onChange={(key, e, hot) => onChange(key, e, hot)}
+        onSearch={() => onSearch(data)}
+      />
+    </div>
+  );
+}

+ 38 - 0
src/components/pageHeader/style.less

@@ -0,0 +1,38 @@
+.com-pageHeader {
+  .titleBox {
+    display: flex;
+    
+    .radio {
+      margin-left: 18px;
+      // ::v-deep.el-radio-button__inner {
+      //   &:hover {
+      //     color: #606266;
+      //   }
+      // }
+      // .is-active {
+      //   ::v-deep.el-radio-button__inner {
+      //     &:hover {
+      //       color: #fff;
+      //     }
+      //   }
+      //   /deep/.el-radio-button__inner:hover {
+      //     background: #fff;
+      //     color: #409EFF;
+      //   }
+      //   /deep/.el-radio-button__orig-radio:checked+.el-radio-button__inner {
+      //     background: #fff;
+      //     color: #409EFF;
+      //   }
+      // }
+    }
+  }
+  .dividerLine {
+    margin: 10px 0;
+    color: #eef0f5;
+  }
+  .stylus-title {
+    display: flex;
+    justify-content: space-between;
+    align-items: center;
+  }
+}

+ 73 - 0
src/components/searchInput/components/searchPeople/index.js

@@ -0,0 +1,73 @@
+import React, { useEffect, useState, useCallback } from 'react';
+import clsx from 'clsx';
+import { Input, Radio, Spin, Select, Icon } from 'antd';
+import { getDeepValue, isChildOf  } from '../../../../utils'
+import { getPerson } from '../../../../request/common/api'
+import _ from 'lodash';
+import styles from './style.module.less'
+const { TextArea } = Input
+const { Option } = Select
+
+export default function searchPeople(props) {
+  //viewInEdit 为false 控制外层是否改调用的update
+  const [options, setOptions] = useState([])
+  const [fetching, setFetching] = useState(false)
+  const { renderFormData, value, onChange } = props
+  
+  const onSearch = _.debounce((val) => {
+    setFetching(true)
+    getPerson({ memberIDAP: val })
+      .then(res => {
+        setOptions(res.data || [])
+        setFetching(false)
+      }).catch(err => {
+        console.log(err)
+        setFetching(false)
+      })
+  }, 500)
+
+  function resetValue(data) {
+    let newData = renderFormData.multiple ? [] : {}
+    if (renderFormData.multiple){
+      newData = data.map(t => (
+        {
+          key: t.key,
+          label: t.label.props ? t.label.props.children[1].props.children : t.label
+        }
+      ))
+    } else {
+      newData = {
+        key: data.key,
+        label: t.label.props ? data.label.props.children[1].props.children : t.label
+      }
+    }
+    onChange(newData)
+  }
+  
+  return (
+    <div className={styles.searchPeople}>
+      <Select
+        mode={renderFormData.multiple && 'multiple'}
+        showSearch
+        value={value || (renderFormData.multiple ? [] : {})}
+        placeholder={renderFormData.placeholder}
+        notFoundContent={fetching ? <Spin size="small" /> : null}
+        labelInValue
+        style={{ width: '100%' }}
+        disabled={renderFormData.disabled}
+        onSearch={(e) => onSearch(e)}
+        onChange={(e) => resetValue(e)}
+      >
+        {options.map(d => (
+          <Option key={d.idap}>
+            <div className={styles.personItemStyle}>
+              <div className={styles.personItemDetail}>{ d.deptName }</div>
+              <div className={styles.personItemName}>{ d.name }</div>
+              <div className={styles.personItemDetail}>{ d.idap }</div>
+            </div>
+          </Option>
+        ))}
+      </Select>
+    </div>
+  );
+}

+ 28 - 0
src/components/searchInput/components/searchPeople/style.module.less

@@ -0,0 +1,28 @@
+.searchPeople {
+  :global(.ant-select-selection__rendered .ant-select-selection__choice__content) {
+    .personItemStyle {
+      .personItemDetail {
+        display: none;
+      }
+      .personItemName {
+        min-width: auto;
+      }
+    }
+  }
+  
+}
+.personItemStyle {
+  display: flex;
+  justify-content: flex-start;
+  .personItemDetail {
+    display: inline-block;
+    min-width:100px;
+    color: #8492a6;
+    font-size: 13px;
+    overflow:hidden
+  }
+  .personItemName {
+    min-width: 80px;
+  }
+}
+

+ 196 - 0
src/components/searchInput/index.js

@@ -0,0 +1,196 @@
+import React, { useEffect, useState, useCallback } from 'react';
+import clsx from 'clsx';
+import { Input, Radio, Spin, Select, Icon } from 'antd';
+import { getDeepValue, isChildOf  } from '../../utils'
+import SearchPeople from './components/searchPeople'
+import EditTable from '../editTable'
+import _ from "lodash";
+import styles from './style.module.less'
+const { TextArea } = Input
+const { Option } = Select
+
+export default function SearchInput(props) {
+  const [edit, setEdit] = useState(false)
+  //viewInEdit 为false 控制外层是否改调用的update
+  const { viewInEdit, viewInEditChange, renderFormData, valueData = {}, fetching = false, onChange } = props
+  function getInput() {
+    const value = getDeepValue(renderFormData.key, valueData, viewInEditChange && 'name')
+    console.log(111111, value)
+    switch(renderFormData.type) {
+      case 'radio': 
+        return (
+          <Radio.Group
+            value={value}
+            disabled={renderFormData.disabled}
+            onChange={(e) => onChange(renderFormData.key, e.target.value)}
+          >
+            {
+              renderFormData.options.map(t => (
+              <Radio value={t.key} key={t.key}>{t.label}</Radio>
+              ))
+            }
+          </Radio.Group>
+        )
+      case 'textarea':
+        return (
+          <TextArea
+            maxLength={500}
+            allowClear
+            disabled={renderFormData.disabled}
+            placeholder={renderFormData.placeholder}
+            value={getDeepValue(renderFormData.key, valueData)}
+            onChange={(e) => onChange(renderFormData.key, e.target.value)}
+          />
+        )
+      case 'select':
+        return (
+          <Select
+            mode={renderFormData.multiple && 'multiple'}
+            // labelInValue
+            // value={value}
+            placeholder={renderFormData.placeholder}
+            notFoundContent={fetching ? <Spin size="small" /> : null}
+            // filterOption={false}
+            // onSearch={this.fetchUser}
+            // onChange={this.handleChange}
+            style={{ width: '100%' }}
+            disabled={renderFormData.disabled}
+            value={value}
+            onChange={(e) => onChange(renderFormData.key, e)}
+          >
+            {renderFormData.options.map(d => (
+              <Option key={d.key}>{(renderFormData.optionRender && renderFormData.optionRender(d)) || d.label}</Option>
+            ))}
+          </Select>
+        )
+      case 'selectAtWill':
+        return (
+          <Select
+            mode="tags"
+            style={{ width: '100%' }}
+            placeholder={renderFormData.placeholder}
+            value={
+              renderFormData.multiple
+              ?
+              value || []
+              :
+              value ? [value] : []
+            }
+            onChange={(e) => selectAtWillChangeValue(renderFormData.key, e, renderFormData.multiple)}
+          >
+            {renderFormData.options.map(d => (
+              <Option key={d.key}>{d.label}</Option>
+            ))}
+          </Select>
+        )
+      case 'searchPeople':
+        return (
+          <SearchPeople
+            renderFormData={renderFormData}
+            value={getDeepValue(renderFormData.key, valueData)}
+            onChange={(e) => onChange(renderFormData.key, e)}
+          />
+        )
+      case 'table':
+        return (
+          <EditTable
+            renderFormData={renderFormData}
+            value={value}
+            onChange={(key, value) => onChange(key, value)}
+          />
+        )
+      default:
+        return (
+          <Input
+            placeholder={renderFormData.placeholder}
+            value={getDeepValue(renderFormData.key, valueData)}
+            disabled={renderFormData.disabled}
+            onChange={(e) => onChange(renderFormData.key, e.target.value)}
+          />
+        )
+    } 
+  }
+
+  function selectAtWillChangeValue(key, value, multiple) {
+    let val = value
+    // 不是多选
+    if(!multiple) {
+      // 如果有一个以上就取第二个 字符串
+      if (val.length > 1) {
+        val = value[1]
+      } else {
+        val = value[0]
+      }
+    }
+    onChange(key, val) 
+  }
+
+  function onEditChange(e) {
+    e.stopPropagation()
+    // callback()
+    // 先更新外层是打开编辑状态
+    // 在更新内层为编辑状态
+    setEdit(true)
+    viewInEditChange(true)
+  }
+  function getText() {
+    return (
+      <div>
+        {renderFormData.textInputRender
+        ?
+        renderFormData.textInputRender(valueData)
+        :
+        <span className={styles.text}>
+          {
+            getDeepValue(renderFormData.nameKey || renderFormData.key, valueData, renderFormData.childrenRoute || '')
+          }
+        </span>
+        }
+      </div>
+    )
+  }
+
+ // 接收外层是否更改编辑状态,更新完成会更改状态
+  useEffect(() => {
+    // 如果外层为关闭编辑状态 内层为开启编辑状态,则更新为关闭
+    if (edit && !viewInEdit) {
+      setEdit(false)
+    }
+  },[viewInEdit])
+  return (
+    //  onClick={(e) => e.stopPropagation()}
+    <div className={styles.searchInput}>
+      
+      { (viewInEditChange && !edit) ? (
+          // textInput为true可以编辑 
+          // 
+          renderFormData.textInput ? (
+          <div style={{ cursor: 'pointer' }}>
+            {renderFormData.textInputRender
+              ?
+              renderFormData.textInputRender(valueData)
+              :
+              <span className={clsx(styles.inputText, styles.text)}>
+                {
+                  getDeepValue(renderFormData.nameKey || renderFormData.key, valueData, renderFormData.childrenRoute || '')
+                  ||
+                  <span className={styles.empty}>请填写</span>
+                }
+              </span>
+            }
+            <Icon id={`${renderFormData.key}Icon`} className={styles.icon} type="edit" onClick={(e) => onEditChange(e)}/>
+          </div>
+          )
+          :
+          (
+            getText()
+          )
+        ) : (
+          <div onClick={(e) => e.stopPropagation()}>
+            {getInput()}
+          </div>
+        )
+      }
+    </div>
+  );
+}

+ 24 - 0
src/components/searchInput/style.module.less

@@ -0,0 +1,24 @@
+.searchInput {
+  display: inline-block;
+  width: 100%;
+  color: var(--font-1);
+  .text {
+    color: var(--font-1);
+  }
+  .icon{
+    display: none;
+    margin-left: 5px;
+  }
+  &:hover {
+    .icon{
+      display: inline-block;
+      color: var(--brand-1);
+    }
+    .inputText {
+      color: var(--brand-1);
+    }
+  }
+  .empty {
+    color: var(--gray-3);
+  }
+}

+ 1 - 0
src/index.js

@@ -1,2 +1,3 @@
 import AgileTCEditor from "./kityminderEditor";
 export default AgileTCEditor;
+export { FromChooseItem } from './components/fromChooseItem';

+ 19 - 0
src/request/common/api.js

@@ -0,0 +1,19 @@
+import request from './request/index'
+import { projectManagement } from './config/index'
+
+export function getPerson(data) {
+  return request({
+    url: projectManagement + `/member/queryMemberInfoByIDAPorName`,
+    method: 'post',
+    timeout: '10000',
+    data
+  })
+}
+// { memberIDAPs: []}
+export function getListPerson(data) {
+  return request({
+    url: projectManagement + `/member/getMembersInfoByLdaps`,
+    method: 'post',
+    data
+  })
+}

+ 38 - 0
src/request/common/config/index.js

@@ -0,0 +1,38 @@
+export let host = 'http://zhihui.xiaojukeji.com'
+export let hostPre = 'http://zhihui-pre.intra.xiaojukeji.com'
+export let hostTest = 'http://zhihui-test.intra.xiaojukeji.com'
+
+export let loginUrl = host + '/sso/login?jumpto=' + 'http://zhihui-test.intra.xiaojukeji.com' + location.pathname
+export let logoutUrl = host + '/sso/logout?jumpto=' + 'http://zhihui-test.intra.xiaojukeji.com' + location.pathname
+
+
+export function getEnv() {
+  let env = 'test'
+  if (location.host.indexOf('localhost') < 0) {
+    host = 'http://' + location.host
+    // requestIp = host + '/zuul'
+    loginUrl = host + '/sso/login?jumpto=' + location.href
+    logoutUrl = host + '/sso/logout?jumpto=' + location.href
+    if (host.indexOf('zhihui-pre.intra.xiaojukeji.com') >= 0) {
+      env = 'pre'
+    } else if (host.indexOf('zhihui.xiaojukeji.com') >= 0) {
+      env = 'online'
+    }
+  }
+  return env
+}
+
+export const requestIp =
+  getEnv() === 'test' ?
+    hostTest + '/zuul'
+    :
+    getEnv() === 'pre' ?
+      hostPre + '/zuul'
+      :
+      host + '/zuul';
+
+export const projectManagement = requestIp + '/project-management'
+export const envManagement = requestIp + '/zhihui-env'
+// export const httpMock = requestIp + '/mock' // mock
+
+

+ 103 - 0
src/request/common/request/index.js

@@ -0,0 +1,103 @@
+import axios from 'axios'
+// import store from '@/store'
+// import { getToken } from '@/utils/auth'
+import { loginUrl } from '../config'
+// import { Encrypt } from '@/utils/crypto-js.js'
+import { message } from 'antd';
+// import router from '@/router/index.js'
+// import store from '@/store/index.js'
+
+// create an axios instance
+const service = axios.create({
+  baseURL: '/apis', // url = base url + request url
+  timeout: 5000, // request timeout
+  withCredentials: true
+})
+
+service.interceptors.request.use(
+  config => {
+    // config.headers['secret'] = Encrypt()
+    // config.retry = 2// 如果请求超时,重试次数
+    // config.retryInterval = 1000 // 重试间隔
+    return config
+  },
+  error => {
+    message.error({
+      content: res.msg || 'Error',
+      duration: 1
+    })
+    return Promise.reject(error)
+  }
+)
+service.interceptors.response.use(
+  response => {
+    const res = response.data
+    return res
+    if(res.code === 200) {
+      return res
+    } else if (typeof res.code !== 'undefined' && res.code !== 200 && res.code !== 0) {
+      message.error({
+        content: res.msg || 'Error',
+        duration: 1
+      })
+    } else if (typeof res.retCode !== 'undefined' && res.retCode !== 0) {
+      message.error({
+        content: res.msg || 'Error',
+        duration: 1
+      })
+    } else {
+      message.warning({
+        content: '请稍后再试或联系管理员',
+        duration: 1
+      })
+    }
+    // return res
+  },
+  error => {
+    if (error && error.response) {
+      switch (error.response.status) {
+        case 400:
+          error.message = '错误请求'
+          break
+        case 401:
+          error.message = '未授权,请重新登录'
+          break
+        case 404:
+          error.message = '请求错误,未找到该资源'
+          break
+        case 405:
+          error.message = '请求方法未允许'
+          break
+        default:
+          error.message = '服务出现异常,请稍后再试或联系管理员'
+      }
+      if (error.response.status !== 403) {
+        // message({
+        //   message: error.message,
+        //   type: 'warning',
+        //   duration: 5 * 1000
+        // })
+        message.warning({
+          content: error.message,
+          duration: 1
+        })
+      }
+      if (error.response.status === 401) {
+        // location.href = loginUrl
+      } else if (error.response.status === 403) {
+        console.log('403')
+        // store.dispatch('global/setBizId', -1)
+        // store.dispatch('global/setBizName', '')
+        // router.push({ name: 'notAccess' })
+      } else {
+        message.warning({
+          content: error.message,
+          duration: 1
+        })
+      }
+    }
+    return Promise.reject(error)
+  }
+)
+
+export default service

+ 41 - 0
src/utils/index.js

@@ -0,0 +1,41 @@
+export function isEmptyObject(value) {
+  if (JSON.stringify(value) == "{}") {
+    return true
+  } else {
+    return false
+  }
+}
+
+export function getDeepValue(key, valueData, type = '') {
+  if (key.includes('.')) {
+    const keyList = key.split('.')
+    if (valueData[keyList[0]]) {
+      if (type && valueData[keyList[0]]) {
+        return valueData[keyList[0]][keyList[1]][type]
+      } else {
+        return valueData[keyList[0]][keyList[1]]
+      }
+    } else {
+      return undefined
+    }
+  }
+  if (type && valueData[key]) {
+    return valueData[key][type] || undefined
+  } else {
+    return valueData[key] || undefined
+  }
+}
+
+export function isChildOf(child, parent) {
+  let parentNode;
+  if (child && parent) {
+    parentNode = child.parentNode;
+    while (parentNode) {
+      if (parent === parentNode) {
+        return true;
+      }
+      parentNode = parentNode.parentNode;
+    }
+  }
+  return false;
+}

+ 27 - 0
webpack/dev.config.js

@@ -34,6 +34,33 @@ module.exports = {
           'sass-loader',
         ],
       },
+      {
+        test: /\.less$/,
+        exclude: /\.module\.less$/,
+        loader: [
+          'style-loader', 
+          'css-loader',
+          // 'css-loader?modules',
+          'less-loader'
+        ]
+      },
+      {
+        test: /\.module\.less$/,
+        loader: [
+          'style-loader', 
+          // 'css-loader',
+          {
+            loader: 'css-loader',
+            options: {
+              modules: {
+                localIdentName: '[local]_[hash:base64:5]',
+              }
+            }
+          },
+          // 'css-loader?modules',
+          'less-loader'
+        ]
+      },
       {
         test: /\.(mp3|mp4)$/,
         loader: 'url-loader',

+ 26 - 0
webpack/prod.config.js

@@ -21,6 +21,32 @@ module.exports = {
         test: /\.css$/,
         use: ['style-loader', 'css-loader'],
       },
+      // 抽离 less
+      {
+        test: /\.less$/,
+        exclude: /\.module\.less$/,
+        loader: [
+          MiniCssExtractPlugin.loader,  // 注意,这里不再用 style-loader
+          'css-loader', 
+          'less-loader'
+        ]
+      },
+      {
+        test: /\.module\.less$/,
+        loader: [
+          'style-loader', 
+          {
+            loader: 'css-loader',
+            options: {
+              modules: {
+                localIdentName: '[local]_[hash:base64:5]',
+              }
+            }
+          },
+          // 'css-loader?modules',
+          'less-loader'
+        ]
+      },
       {
         test: /\.s[ac]ss$/i,
         use: [