Yansen 11 months ago
parent
commit
333b8de261

+ 1
- 0
package.json View File

@@ -50,6 +50,7 @@
50 50
     "@tarojs/taro": "3.6.25",
51 51
     "@zjxpcyc/js_event": "^1.0.0",
52 52
     "@zjxpcyc/react-tiny-store": "^2.0.1",
53
+    "classnames": "^2.5.1",
53 54
     "echarts": "^5.4.1",
54 55
     "echarts-for-weixin": "^1.0.2",
55 56
     "gcoord": "^1.0.6",

+ 1
- 0
src/app.config.js View File

@@ -28,6 +28,7 @@ export default defineAppConfig({
28 28
     "pages/feedback/issuelist/index",
29 29
     "pages/user/list/index",
30 30
     "pages/user/add/index",
31
+    "pages/test/index",
31 32
   ],
32 33
   subpackages: [
33 34
     {

+ 71
- 0
src/components/OrgTree/index.jsx View File

@@ -0,0 +1,71 @@
1
+import React from 'react';
2
+import Taro from '@tarojs/taro';
3
+import { Popup, Button, Cell, Icon } from '@antmjs/vantui';
4
+import { ScrollView, View } from '@tarojs/components';
5
+import { getSysOrg } from '@/services/sysorg';
6
+import VABC from '@/components/VABC';
7
+import Tree from '@/components/Tree';
8
+import { arr2Tree } from '@/utils/array';
9
+
10
+const wrapperStyle = {
11
+  width: '90vw',
12
+  height: '100vh',
13
+  paddingBottom: 'env(safe-area-inset-bottom)',
14
+}
15
+
16
+export default function OrgTree(props) {
17
+  const { show, onCancel, onChange } = props;
18
+
19
+  const [dict, setDict] = React.useState([]);
20
+  const [checked, setChecked] = React.useState();
21
+  
22
+  const onSubmit = () => {
23
+    if (!checked) return;
24
+    onChange(checked?.orgId, checked)
25
+  }
26
+
27
+  const onCheck = (e, it) => {
28
+    // e.stopPropagation();
29
+    // e.preventDefault();
30
+    setChecked(it);
31
+  }
32
+  
33
+  React.useEffect(() => {
34
+    getSysOrg({pageSize: 500, isResponsible: 1, parentId: props.parentId }).then(res => {
35
+      const [treeData] = arr2Tree(res.records || [], 'orgPId', 'orgId');
36
+      console.log('-------treeData-------', treeData)
37
+      setDict(treeData);
38
+    })
39
+  }, [props.parentId]);
40
+
41
+  return (
42
+    <Popup position="right" show={show} onClose={onCancel} >
43
+      <VABC
44
+        footer={(
45
+          <View style={{ padding: '8px 2em' }}>
46
+            <Button block type="primary" disabled={!checked} onClick={onSubmit}>确定</Button>
47
+          </View>
48
+        )}
49
+        style={wrapperStyle}
50
+      >
51
+        <ScrollView scrollY style={{ height: '100%' }}>
52
+          <Tree
53
+            data={dict}
54
+            renderTitle={(it) => (
55
+              <Cell
56
+                title={it.name}
57
+                value={(
58
+                  <View>
59
+                    { checked?.orgId == it.orgId ? <Icon name="success" color="#1A7565" /> : <View style={{ width: '100%', minHeight: '10px' }} /> }
60
+                  </View>
61
+                )}
62
+                renderIcon={it.children?.length > 0 ? <Icon name="arrow" /> : <View style={{ minWidth: '16px' }} />}
63
+                onClick={e => onCheck(e, it)}
64
+              />
65
+            )}
66
+          />
67
+        </ScrollView>
68
+      </VABC>
69
+    </Popup>
70
+  );
71
+}

+ 75
- 0
src/components/Tree/TreeItem.jsx View File

@@ -0,0 +1,75 @@
1
+import React from 'react';
2
+import Taro from '@tarojs/taro';
3
+import classNames from 'classnames';
4
+import { View } from '@tarojs/components';
5
+import cls from './style.module.less';
6
+
7
+export default function TreeItem(props) {
8
+  const { data, expanded, onClick, onToggle, renderTitle, onHeight } = props;
9
+  const ctt = React.useMemo(() => `ctt-${Math.random().toString(36).substring(2, 7)}`, []);
10
+  const [current, setCurrent] = React.useState(-1);
11
+  const [height, setHeight] = React.useState(0);
12
+  const expandedRef = React.useRef(false);
13
+  expandedRef.current = expanded;
14
+
15
+  const style = {
16
+    maxHeight: expanded ? `${height + 10}px` : '0px',
17
+  }
18
+  
19
+  const handleToggle = (e) => {
20
+    e.stopPropagation();
21
+    e.preventDefault();
22
+    const checked = !expandedRef.current;
23
+    onToggle && onToggle(checked);
24
+    onClick && onClick(data);
25
+    onHeight && onHeight();
26
+  }
27
+
28
+  const handleChildToggle = (e, inx) => {
29
+    setCurrent(e ? inx : -1);
30
+  }
31
+
32
+  const calcHeight = React.useCallback(() => {
33
+    Taro.nextTick(() => {
34
+      Taro.createSelectorQuery()
35
+      .select(`.${ctt}`)
36
+      .boundingClientRect(res => {
37
+        // console.log('-----rect------', ctt, res?.height)
38
+        setHeight(res?.height || 0);
39
+      })
40
+      .exec();
41
+    })
42
+  }, []);
43
+
44
+  React.useEffect(() => {
45
+    calcHeight();
46
+  }, []);
47
+
48
+  return (
49
+    <View className={classNames(cls.item, {[cls.expanded]: expanded})} onClick={handleToggle}>
50
+      {/* <View className={cls.toggle} onClick={handleToggle}></View> */}
51
+      {renderTitle ? renderTitle(data) : null}
52
+      {
53
+        data?.children?.length > 0 && (
54
+          <View class={classNames(cls.children)} style={style}>
55
+            <View className={ctt}>
56
+            {
57
+              data.children.map((it, inx) => (
58
+                <TreeItem
59
+                  key={inx}
60
+                  data={it}
61
+                  expanded={current == inx}
62
+                  onClick={onClick}
63
+                  onToggle={(e) => handleChildToggle(e, inx)}
64
+                  onHeight={onHeight || calcHeight}
65
+                  renderTitle={renderTitle}
66
+                />
67
+              ))
68
+            }
69
+            </View>
70
+          </View>
71
+        )
72
+      }
73
+    </View>
74
+  );
75
+}

+ 28
- 0
src/components/Tree/index.jsx View File

@@ -0,0 +1,28 @@
1
+import React from 'react';
2
+import classNames from 'classnames';
3
+import Taro from '@tarojs/taro';
4
+import { View } from '@tarojs/components';
5
+import TreeItem from './TreeItem';
6
+import cls from './style.module.less';
7
+
8
+export default function Tree(props) {
9
+  const { data, renderTitle, onClick } = props;
10
+  const [current, setCurrent] = React.useState(0);
11
+  
12
+  return (
13
+    <View class={classNames(cls.tree, cls.box)}>
14
+      {
15
+        data.map((it, inx) => (
16
+          <TreeItem
17
+            key={inx}
18
+            data={it}
19
+            expanded={current == inx}
20
+            onToggle={(x) => setCurrent(x ? inx : -1)}
21
+            onClick={onClick}
22
+            renderTitle={renderTitle}
23
+          />
24
+        ))
25
+      }
26
+    </View>
27
+  );
28
+}

+ 58
- 0
src/components/Tree/style.module.less View File

@@ -0,0 +1,58 @@
1
+.tree {
2
+  list-style-type: none;
3
+  padding: 0;
4
+}
5
+
6
+.tree .item {
7
+  position: relative;
8
+  margin-left: 20px;
9
+  cursor: pointer;
10
+}
11
+
12
+.toggle {
13
+  display: inline-block;
14
+  cursor: pointer;
15
+  min-width: 1em;
16
+  min-height: 1em;
17
+  background-color: red;
18
+}
19
+
20
+.children {
21
+  display: block;
22
+}
23
+
24
+/* 初始状态下的连接线 */
25
+.tree .item:before {
26
+  // content: "";
27
+  // position: absolute;
28
+  // top: 0;
29
+  // left: -20px;
30
+  // width: 15px;
31
+  // border-left: 1px solid #ccc;
32
+  // height: 100%;
33
+}
34
+
35
+/* 添加子级的连接线 */
36
+.tree .item > .children:before {
37
+  // content: "";
38
+  // position: absolute;
39
+  // top: 0;
40
+  // left: -17px;
41
+  // border-top: 1px solid #ccc;
42
+  // width: 10px;
43
+  // height: 15px;
44
+  // transform: rotate(90deg);
45
+  // transform-origin: bottom left;
46
+}
47
+
48
+/* 展开时的子级列表 */
49
+.tree .children {
50
+  overflow: hidden;
51
+  max-height: 0;
52
+  transition: all .3s ease-in-out;
53
+}
54
+
55
+/* 使用JavaScript动态添加的展开/收缩样式 */
56
+.tree .expanded > .children {
57
+  max-height: 400px;
58
+}

+ 5
- 4
src/pages/issue/components/Issue/index.jsx View File

@@ -4,7 +4,8 @@ import { Textarea, View } from "@tarojs/components";
4 4
 import { Field, Cell, CellGroup } from "@antmjs/vantui";
5 5
 import LocType from "@/components/LocType";
6 6
 import IssueType from "@/components/IssueType";
7
-import OrgPicker from "@/components/OrgPicker";
7
+// import OrgPicker from "@/components/OrgPicker";
8
+import OrgTree from "@/components/OrgTree";
8 9
 import DatePicker from "@/components/DatePicker";
9 10
 import Map from "@/components/map";
10 11
 import Uploader from "@/components/Uploader/index";
@@ -102,8 +103,8 @@ export default (props) => {
102 103
   };
103 104
 
104 105
   const setFieldChange = (field, value) => {
105
-    console.log("field->>>>>>>>>>>>>>>>",field)
106
-    console.log("value->>>>>>>>>>>>>>>>",value)
106
+    // console.log("field->>>>>>>>>>>>>>>>",field)
107
+    // console.log("value->>>>>>>>>>>>>>>>",value)
107 108
     const data = {
108 109
       ...fmRef.current,
109 110
       [field]: value,
@@ -166,7 +167,7 @@ export default (props) => {
166 167
         onChange={onIssueTypeChange}
167 168
       />
168 169
 
169
-      <OrgPicker
170
+      <OrgTree
170 171
         show={showOrgPicker}
171 172
         value={formData.orgName}
172 173
         onCancel={() => setShowOrgPicker(false)}

+ 3
- 2
src/pages/org/issue/detail/components/Assigned.jsx View File

@@ -2,7 +2,8 @@ import React from 'react';
2 2
 import Taro from '@tarojs/taro';
3 3
 import { View } from '@tarojs/components';
4 4
 import { Button, Dialog } from '@antmjs/vantui';
5
-import OrgPicker from "@/components/OrgPicker";
5
+// import OrgPicker from "@/components/OrgPicker";
6
+import OrgTree from "@/components/OrgTree";
6 7
 import { postIssueAssignedAgain, putTaIssue, copyTaIssue } from '@/services/taissue';
7 8
 import { warn } from '@/utils/message';
8 9
 
@@ -62,7 +63,7 @@ export default (props) => {
62 63
   return (
63 64
     <View style={{ marginTop: '20px', display: 'flex' }}>
64 65
       <Dialog id={dialogId} />
65
-      <OrgPicker
66
+      <OrgTree
66 67
         parentId={issue.orgId}
67 68
         show={showOrgPicker}
68 69
         value={formDataRef.current?.orgName}

+ 3
- 0
src/pages/test/index.config.js View File

@@ -0,0 +1,3 @@
1
+export default definePageConfig({
2
+  navigationBarTitleText: '测试'
3
+})

+ 18
- 0
src/pages/test/index.jsx View File

@@ -0,0 +1,18 @@
1
+import React from 'react';
2
+import Taro from '@tarojs/taro';
3
+import { Button } from '@tarojs/components';
4
+import OrgTree from '@/components/OrgTree';
5
+
6
+export default function Test(props) {
7
+
8
+  const [show, setShow] = React.useState(false);
9
+  
10
+  return (
11
+    <>
12
+    <Button onClick={() => setShow(true)}>
13
+      ABNCD
14
+    </Button>
15
+    <OrgTree show={show} onCancel={() => setShow(false)} onChange={console.log} />
16
+    </>
17
+  );
18
+}

+ 3
- 2
src/subpkg1/pages/components/orgStat/index.jsx View File

@@ -4,7 +4,8 @@ import { View } from '@tarojs/components';
4 4
 import { CellGroup, Cell, Popup } from '@antmjs/vantui';
5 5
 import Card from '@/components/Card';
6 6
 import Chart, { getLinearGradient } from '@/subpkg1/components/chart';
7
-import OrgPicker from '@/components/OrgPicker';
7
+// import OrgPicker from '@/components/OrgPicker';
8
+import OrgTree from '@/components/OrgTree';
8 9
 import { getIssueOrg } from '@/services/stat';
9 10
 
10 11
 export default (props) => {
@@ -74,7 +75,7 @@ export default (props) => {
74 75
     <Card>
75 76
       <Cell title="交办统计" isLink value={valStr} onClick={() => setShow(true)} />
76 77
       <Chart option={option} style={{ height: '30vh'}} />
77
-      <OrgPicker show={show} onChange={onOrgChange} onClose={onCancel} />
78
+      <OrgTree show={show} onChange={onOrgChange} onClose={onCancel} />
78 79
     </Card>
79 80
   )
80 81
 }

+ 64
- 0
src/utils/array.js View File

@@ -0,0 +1,64 @@
1
+/**
2
+ * 数组 转 Tree
3
+ * @param {*} arr
4
+ * @param {*} parent
5
+ * @param {*} key
6
+ * @returns
7
+ */
8
+export function arr2Tree(arr = [], parent = "parentId", key = "key") {
9
+  // 转换为字典
10
+  const dict = arr.reduce((acc, item) => {
11
+    return {
12
+      ...acc,
13
+      [item[key]]: {
14
+        ...item,
15
+        children: [],
16
+      },
17
+    };
18
+  }, {});
19
+
20
+  // // 查找顶级结点
21
+  // const getRootNode = (node) => {
22
+  //   const found = arr.filter((x) => node[parentId] === x[key])[0];
23
+
24
+  //   return !found ? dict[node[key]] : getRootNode(found);
25
+  // };
26
+
27
+  // 挂载父子节点
28
+  const tree = [];
29
+  for (let item of arr) {
30
+    const it = dict[item[key]];
31
+    const parentNodeId = it[parent];
32
+    const parentNode = dict[parentNodeId];
33
+
34
+    if (!parentNode) {
35
+      tree.push(it);
36
+    } else {
37
+      dict[parentNodeId].children.push(it);
38
+    }
39
+  }
40
+  return [tree, dict];
41
+}
42
+
43
+/**
44
+ * 深度展平数组
45
+ * @param {*} arr
46
+ * @returns
47
+ */
48
+export function flatten(arr = []) {
49
+  return arr.reduce((acc, it) => {
50
+    const list = Array.isArray(it) ? flatten(it) : it;
51
+    return acc.concat(list);
52
+  }, []);
53
+}
54
+
55
+/**
56
+ * 深度数组去重
57
+ * @param {*} arr
58
+ */
59
+export function uniq(arr = []) {
60
+  const list = flatten(arr);
61
+  return list.reduce((acc, it) => {
62
+    return acc.indexOf(it) > -1 ? acc : acc.concat(it);
63
+  }, []);
64
+}

+ 14828
- 24
yarn-error.log
File diff suppressed because it is too large
View File


+ 5
- 0
yarn.lock View File

@@ -4396,6 +4396,11 @@ classnames@^2.2.5, classnames@^2.2.6, classnames@^2.3.1:
4396 4396
   resolved "https://registry.npmmirror.com/classnames/-/classnames-2.5.1.tgz"
4397 4397
   integrity sha512-saHYOzhIQs6wy2sVxTM6bUDsQO4F50V9RQ22qBpEdCW+I+/Wmke2HOl6lS6dTpdxVhb88/I6+Hs+438c3lfUow==
4398 4398
 
4399
+classnames@^2.5.1:
4400
+  version "2.5.1"
4401
+  resolved "https://registry.npmmirror.com/classnames/-/classnames-2.5.1.tgz#ba774c614be0f016da105c858e7159eae8e7687b"
4402
+  integrity sha512-saHYOzhIQs6wy2sVxTM6bUDsQO4F50V9RQ22qBpEdCW+I+/Wmke2HOl6lS6dTpdxVhb88/I6+Hs+438c3lfUow==
4403
+
4399 4404
 clean-css@^4.2.1, clean-css@^4.2.3:
4400 4405
   version "4.2.4"
4401 4406
   resolved "https://registry.npmmirror.com/clean-css/-/clean-css-4.2.4.tgz"