Yansen 2 年之前
父節點
當前提交
b81542a4de

+ 24
- 14
src/components/Pickerful/index.jsx 查看文件

@@ -1,7 +1,13 @@
1 1
 import React from 'react';
2 2
 import { View, ScrollView } from '@tarojs/components';
3 3
 import { Button, Popup, Search, Cell, Icon } from '@antmjs/vantui';
4
-import style from './style.module.less';
4
+import VABC from '@/components/VABC';
5
+
6
+const wrapperStyle = {
7
+  width: '90vw',
8
+  height: '100vh',
9
+  paddingBottom: 'env(safe-area-inset-bottom)',
10
+}
5 11
 
6 12
 export default (props) => {
7 13
   const { show, value, keyValue, labelValue = 'name', dictAPI, onChange, onCancel } = props;
@@ -37,14 +43,22 @@ export default (props) => {
37 43
   
38 44
   return (
39 45
     <Popup position="right" show={show} onClose={onCancel}>
40
-      <View className={style['pickerful-box']}>
41
-        <Search
42
-          onChange={(e) => setSearchText(e.detail)}
43
-          placeholder="请输入搜索关键词"
44
-          onSearch={onSearch}
45
-          clearable
46
-        />
47
-        <View style={{ flex: 1, overflow: 'hidden' }}>
46
+      <VABC
47
+        header={(
48
+          <Search
49
+            onChange={(e) => setSearchText(e.detail)}
50
+            placeholder="请输入搜索关键词"
51
+            onSearch={onSearch}
52
+            clearable
53
+          />
54
+        )}
55
+        footer={(
56
+          <View style={{ padding: '8px 2em' }}>
57
+            <Button block type="primary" disabled={!checked} onClick={onClick}>确定</Button>
58
+          </View>
59
+        )}
60
+        style={wrapperStyle}
61
+      >
48 62
           <ScrollView scrollY style={{ height: '100%' }}>
49 63
             {
50 64
               list.map(item => (
@@ -61,11 +75,7 @@ export default (props) => {
61 75
               ))
62 76
             }
63 77
           </ScrollView>
64
-        </View>
65
-        <View style={{ padding: '8px 2em' }}>
66
-          <Button block type="primary" disabled={!checked} onClick={onClick}>确定</Button>
67
-        </View>
68
-      </View>
78
+      </VABC>
69 79
     </Popup>
70 80
   )
71 81
 }

+ 0
- 12
src/components/Pickerful/style.module.less 查看文件

@@ -1,12 +0,0 @@
1
-
2
-.pickerful-box {
3
-  width: 90vw;
4
-  height: 100%;
5
-  display: flex;
6
-  flex-direction: column;
7
-
8
-  & > view {
9
-    flex: none;
10
-  }
11
-}
12
-

+ 12
- 1
src/components/PowerList/index.jsx 查看文件

@@ -4,17 +4,26 @@ import { View } from '@tarojs/components';
4 4
 import { PowerScrollView } from '@antmjs/vantui';
5 5
 
6 6
 export default (props) => {
7
-  const { request, params, renderItem } = props;
7
+  const { request, params, renderItem, onLoadingChange } = props;
8 8
 
9 9
   const pageSize = 20;
10 10
   const pageNumRef = React.useRef(1);
11 11
   const listRef = React.useRef([]);
12
+  const [loading, setLoading] = React.useState(false);
12 13
   const [list, setList] = React.useState([]);
13 14
   const [finished, setFinished] = React.useState(true);
14 15
   listRef.current = list;
15 16
 
17
+  const changeLoading = (val) => {
18
+    setLoading(val);
19
+    if (onLoadingChange) {
20
+      onLoadingChange(val);
21
+    }
22
+  }
23
+
16 24
   const queryData = React.useCallback((options = {}) => {
17 25
     return new Promise((resolve, reject) => {
26
+      changeLoading(true);
18 27
       request({
19 28
         pageSize,
20 29
         ...(params || {}),
@@ -29,8 +38,10 @@ export default (props) => {
29 38
         }
30 39
 
31 40
         setFinished(current >= pages);
41
+        changeLoading(false);
32 42
         resolve();
33 43
       }).catch((err) => {
44
+        changeLoading(false);
34 45
         reject(err);
35 46
       });
36 47
     });

+ 30
- 0
src/components/VABC/index.jsx 查看文件

@@ -0,0 +1,30 @@
1
+import React from 'react';
2
+import Taro from '@tarojs/taro';
3
+import { View } from '@tarojs/components';
4
+import clsScope from './style.module.less';
5
+
6
+export default (props) => {
7
+  const { style, header, footer  } = props;
8
+  
9
+  return (
10
+    <View className={clsScope['v-abc-wrapper']} style={style}>
11
+      {
12
+        header && (
13
+          <View className={clsScope['v-abc-hd']}>
14
+            {header}
15
+          </View>
16
+        )
17
+      }
18
+      <View className={clsScope['v-abc-bd']}>
19
+        {props.children}
20
+      </View>
21
+      {
22
+        footer && (
23
+          <View className={clsScope['v-abc-ft']}>
24
+            {footer}
25
+          </View>
26
+        )
27
+      }
28
+    </View>
29
+  )
30
+}

+ 19
- 0
src/components/VABC/style.module.less 查看文件

@@ -0,0 +1,19 @@
1
+
2
+.v-abc-wrapper {
3
+  height: 100%;
4
+  display: flex;
5
+  flex-direction: column;
6
+
7
+  .v-abc-hd {
8
+    flex: none;
9
+  }
10
+
11
+  .v-abc-bd {
12
+    flex: 1;
13
+    overflow: hidden;
14
+  }
15
+
16
+  .v-abc-ft {
17
+    flex: none;
18
+  }
19
+}

+ 0
- 32
src/pages/check/loc/edit/components/FormTpl.jsx 查看文件

@@ -1,32 +0,0 @@
1
-import React from 'react';
2
-import Taro from '@tarojs/taro';
3
-import { View, ScrollView } from '@tarojs/components';
4
-
5
-const fullHeight = {
6
-  height: '100%',
7
-}
8
-
9
-const fmBdStyle = {
10
-  height: 'calc(100% - 80px)',
11
-}
12
-
13
-const fmActStyle = {
14
-  padding: 'var(--main-space)',
15
-  height: '80px',
16
-}
17
-
18
-export default (props) => {
19
-
20
-  const { action } = props;
21
-  
22
-  return (
23
-    <View style={fullHeight}>
24
-      <View style={fmBdStyle}>
25
-        <ScrollView scrollY style={fullHeight}>
26
-          {props.children}
27
-        </ScrollView>
28
-      </View>
29
-      <View style={fmActStyle}>{action}</View>
30
-    </View>
31
-  )
32
-}

+ 20
- 0
src/pages/check/loc/edit/components/FormTpl/index.jsx 查看文件

@@ -0,0 +1,20 @@
1
+import React from 'react';
2
+import Taro from '@tarojs/taro';
3
+import { View, ScrollView } from '@tarojs/components';
4
+import VABC from '@/components/VABC';
5
+
6
+export default (props) => {
7
+  const { action } = props;
8
+  
9
+  return (
10
+    <VABC
11
+      footer={(
12
+        <View style={{padding: 'var(--main-space)'}}>{action}</View>
13
+      )}
14
+    >
15
+      <ScrollView scrollY style={{ height: '100%' }}>
16
+        {props.children}
17
+      </ScrollView>
18
+    </VABC>
19
+  )
20
+}

+ 14
- 4
src/pages/check/loc/edit/components/LocForm.jsx 查看文件

@@ -4,18 +4,29 @@ import { View } from '@tarojs/components';
4 4
 import { CellGroup, Cell, Field, Button } from '@antmjs/vantui';
5 5
 import Map from '@/components/map';
6 6
 import mapIcon from '@/assets/icons/marker.png';
7
-import FormTpl from './FormTpl';
7
+import FormTpl from './FormTpl/index';
8 8
 
9 9
 export default (props) => {
10 10
 
11 11
   const { detail, onDetailChange, onClick } = props;
12
+
13
+  const [loc, setLoc] = React.useState();
12 14
   
13 15
   const setFieldChange = (key, val) => {
14 16
     onDetailChange({
15
-      ...detail,
17
+      ...(detail || {}),
16 18
       [key]: val,
17 19
     })
18 20
   }
21
+
22
+  React.useEffect(() => {
23
+    if (detail && loc) {
24
+      onDetailChange({
25
+        ...detail,
26
+        location: loc,
27
+      })
28
+    }
29
+  }, [detail, loc]);
19 30
   
20 31
   return (
21 32
     <FormTpl
@@ -23,10 +34,9 @@ export default (props) => {
23 34
         <Button block type="primary" onClick={onClick}>下一步</Button>
24 35
       )}
25 36
     >
26
-      <Map onLocChange={e => setFieldChange('location', e)} />
37
+      <Map onLocChange={setLoc} />
27 38
       <CellGroup>
28 39
         <Cell
29
-          isLink
30 40
           title="点位"
31 41
           value={detail?.name}
32 42
         />

+ 70
- 0
src/pages/check/loc/edit/components/QuSelect/index.jsx 查看文件

@@ -0,0 +1,70 @@
1
+import React from 'react';
2
+import Taro from '@tarojs/taro';
3
+import { View, Text } from '@tarojs/components';
4
+import { Popup, Cell, Icon } from '@antmjs/vantui';
5
+import RatioView from '@/components/RatioView';
6
+import style from './style.module.less';
7
+
8
+const Bar = (props) => {
9
+  const {cursor, total, onClick} =  props;
10
+
11
+  const handleClick = (e) => {
12
+    if (onClick) {
13
+      onClick(e);
14
+    }
15
+  }
16
+
17
+  return (
18
+    <Cell onClick={handleClick}>
19
+      <View className={style['qu-board-title']}>
20
+        <Icon name="weapp-nav" size={60} color="var(--main-bg-color)" />
21
+        <Text>{cursor + 1}</Text>
22
+        <Text> / </Text>
23
+        <Text>{total}</Text>
24
+      </View>
25
+    </Cell>
26
+  )
27
+}
28
+
29
+
30
+export default (props) => {
31
+  const { value = 0, list, onChange } = props;
32
+
33
+  const [show, setShow] = React.useState(false);
34
+
35
+  const handleClick = (e, inx) => {
36
+    e.stopPropagation();
37
+    setShow(false);
38
+
39
+    if (onChange) {
40
+      onChange(inx);
41
+    }
42
+  }
43
+  
44
+  return (
45
+    <View className={style['qu-board-bar']}>
46
+      <Bar cursor={value} total={list?.length} onClick={() => setShow(true)} />
47
+      <Popup position="bottom" show={show} onClose={() => setShow(false)}>
48
+        <View className={style['qu-board-wrapper']}>
49
+          <Bar cursor={value} total={list?.length} />
50
+          <View className={style['qu-board-bd']}>
51
+            {
52
+              list?.map((it, inx) => {
53
+                const cls = [
54
+                  style['qu-board-bd-item'],
55
+                  it?.finished ? style['qu-board-bd-active'] : false,
56
+                ].filter(Boolean).join('-')
57
+
58
+                return (
59
+                  <RatioView key={inx}>
60
+                    <View className={cls} onClick={e => handleClick(e, inx)}>{inx + 1}</View>
61
+                  </RatioView>
62
+                )
63
+              })
64
+            }
65
+          </View>
66
+        </View>
67
+      </Popup>
68
+    </View>
69
+  )
70
+}

+ 50
- 0
src/pages/check/loc/edit/components/QuSelect/style.module.less 查看文件

@@ -0,0 +1,50 @@
1
+.qu-board-bar {
2
+  background: #fff;
3
+  border-top: 1px solid rgba(0, 0, 0, 0.12);
4
+}
5
+
6
+.qu-board-title {
7
+  font-size: 28px;
8
+  color: #999;
9
+  font-weight: bold;
10
+  display: flex;
11
+  justify-content: flex-end;
12
+  align-items: center;
13
+
14
+  & > text {
15
+    &:first-of-type {
16
+      margin-left: 0.6em;
17
+    }
18
+  }
19
+}
20
+
21
+.qu-board-wrapper {
22
+  background: #fff;
23
+
24
+  .qu-board-bd {
25
+    padding: var(--main-space) calc(var(--main-space) * 2);
26
+    display: grid;
27
+    grid-template-columns: repeat(5, 1fr);
28
+    row-gap: 36px;
29
+    column-gap: 36px;
30
+
31
+    .qu-board-bd-item {
32
+      width: 100%;
33
+      height: 100%;
34
+      border-radius: 50%;
35
+      overflow: hidden;
36
+      display: grid;
37
+      place-items: center;
38
+      font-size: 30px;
39
+      color: #666;
40
+      font-weight: bold;
41
+      border: 2px solid var(--main-bg-color);
42
+
43
+      &.qu-board-bd-active {
44
+        color: #fff;
45
+        border: 2px solid transparent;
46
+        background-color: var(--main-bg-color);
47
+      }
48
+    }
49
+  }
50
+}

+ 115
- 0
src/pages/check/loc/edit/components/Question.jsx 查看文件

@@ -0,0 +1,115 @@
1
+import React from 'react';
2
+import Taro from '@tarojs/taro';
3
+import { View, RichText, Text } from '@tarojs/components';
4
+import { CellGroup, Cell, Field, Radio, RadioGroup, Button } from '@antmjs/vantui';
5
+import Uploader from '@/components/Uploader/index';
6
+import { getTaCheckAnswerItem } from '@/services/tacheckansweritem';
7
+import FormTpl from './FormTpl/index';
8
+
9
+const actStyle = {
10
+  display: 'flex',
11
+  width: '100%',
12
+}
13
+
14
+export default (props) => {
15
+
16
+  const { detail, readonly, cursor, total , onChange, onPrev, onNext } = props;
17
+
18
+  const [isPrev, isFinished] = React.useMemo(() => {
19
+    return [
20
+      cursor > 0,
21
+      cursor >= total - 1,
22
+    ]
23
+  }, [cursor, total])
24
+
25
+  const [answer, setAnswer] = React.useState();
26
+
27
+  const onSubmit = () => {
28
+    onChange(answer);
29
+    onNext();
30
+  }
31
+
32
+  const setFieldChange = (key, val) => {
33
+    if (readonly) return;
34
+
35
+    setAnswer({
36
+      ...(answer || {}),
37
+      [key]: val,
38
+    })
39
+  }
40
+
41
+  React.useEffect(() => {
42
+    if (detail?.quId) {
43
+      getTaCheckAnswerItem({ quId: detail.quId, isMine: true }).then(res => {
44
+        setAnswer(res && res.length ? res[0] : undefined)
45
+      })
46
+    } else {
47
+      setAnswer(undefined)
48
+    }
49
+  }, [detail]);
50
+  
51
+  return (
52
+    <FormTpl
53
+      action={(
54
+        <View style={actStyle}>
55
+          {
56
+            isPrev && <Button type="default" block style={{flex: 1}} onClick={onPrev}>上一题</Button>
57
+          }
58
+          <Button type="primary" block style={{flex: 1, marginLeft: isPrev ? '1em' : 0 }} onClick={onSubmit}>
59
+            {isFinished ? '提交' : '下一题'}
60
+          </Button>
61
+        </View>
62
+      )}
63
+    >
64
+      <RadioGroup value={answer?.answerCode}>
65
+        <CellGroup>
66
+          <Cell title="测评内容" />
67
+          <Cell
68
+            border={false}
69
+            renderTitle={(
70
+              <View>
71
+                <Text>{detail?.sortNo}</Text>
72
+                <Text>{detail?.title}</Text>
73
+              </View>
74
+            )}
75
+          />
76
+          {
77
+            (detail?.answerList || []).map((it) => {              
78
+              return (
79
+                <Cell
80
+                  key={it.answerId}
81
+                  title={it.answer}
82
+                  clickable
83
+                  border={false}
84
+                  onClick={() => setFieldChange('answerCode', it.answerCode)}
85
+                  renderRightIcon={<Radio checkedColor="var(--main-bg-color)" readonly={readonly} name={it.answerCode}></Radio>}
86
+                />
87
+              );
88
+            })
89
+          }
90
+        </CellGroup>
91
+      </RadioGroup>
92
+      <CellGroup style={{marginTop: '20px'}}>
93
+        <Cell title="测评标准" />
94
+        <Cell
95
+          renderTitle={
96
+            <RichText nodes={detail?.stand} />
97
+          }
98
+        />
99
+      </CellGroup>
100
+      <CellGroup style={{marginTop: '20px'}}>        
101
+        <Cell title="拍照或视频" border={false} />
102
+
103
+        <Cell
104
+          renderTitle={
105
+            <Uploader
106
+              disabled={readonly}
107
+              value={answer?.attachList}
108
+              onChange={e => setFieldChange('attachList',e)}
109
+            />
110
+          }
111
+        />
112
+      </CellGroup>
113
+    </FormTpl>
114
+  )
115
+}

+ 49
- 4
src/pages/check/loc/edit/index.jsx 查看文件

@@ -4,7 +4,10 @@ import { View } from '@tarojs/components';
4 4
 import Page from '@/layouts/index';
5 5
 import { getTaCheckItemById, preCheck } from '@/services/tacheckitem';
6 6
 import { getTaCheckItemQu } from '@/services/tacheckitemqu';
7
+import VABC from '@/components/VABC';
7 8
 import LocForm from './components/LocForm';
9
+import Question from './components/Question';
10
+import QuSelect from './components/QuSelect';
8 11
 
9 12
 export default (props) => {
10 13
 
@@ -12,14 +15,28 @@ export default (props) => {
12 15
   const { id } = router.params;
13 16
 
14 17
   const [loading, setLoading] = React.useState(false);
15
-  const [checkInfo, setCheckInfo] = React.useState();
18
+  const [checkItemInfo, setCheckItemInfo] = React.useState();
16 19
   const [quList, setQuList] = React.useState([]);
20
+  const [index, setIndex] = React.useState(-1);
21
+
22
+  const onPreFormSubmit = () => {
23
+    setIndex(0);
24
+  }
25
+
26
+  const onIndexChange = (t, n) => {
27
+    // eslint-disable-next-line no-restricted-globals
28
+    if (isNaN(t)) {
29
+      setIndex(index + n);
30
+    } else {
31
+      setIndex(t);
32
+    }
33
+  }
17 34
 
18 35
   React.useEffect(() => {
19 36
     setLoading(true);
20
-    preCheck(id).then(() => {
37
+    preCheck(id, { noSuccesTip: true }).then(() => {
21 38
       getTaCheckItemById(id).then(res => {
22
-        setCheckInfo(res);
39
+        setCheckItemInfo(res);
23 40
       });
24 41
   
25 42
       getTaCheckItemQu({pageSize: 200, itemId: id}).then(res => {
@@ -35,7 +52,35 @@ export default (props) => {
35 52
   
36 53
   return (
37 54
     <Page loading={loading}>
38
-      <LocForm />
55
+      <VABC
56
+        footer={
57
+          index > -1 && (
58
+            <QuSelect
59
+              value={index}
60
+              list={quList}
61
+              onChnage={onIndexChange}
62
+            />
63
+          )
64
+        }
65
+      >
66
+      {
67
+        index == -1 && <LocForm detail={checkItemInfo} onDetailChange={setCheckItemInfo} onClick={onPreFormSubmit} />
68
+      }
69
+      {
70
+        quList.map((x, inx) => (
71
+          index == inx && (
72
+            <Question
73
+              key={x.quId}
74
+              detail={x}
75
+              cursor={inx}
76
+              total={quList?.length}
77
+              onPrev={() => onIndexChange(null, -1)}
78
+              onNext={() => onIndexChange(null, 1)}
79
+            />
80
+          )
81
+        ))
82
+      }
83
+      </VABC>
39 84
     </Page>
40 85
   )
41 86
 }

+ 14
- 3
src/pages/check/loc/list/index.jsx 查看文件

@@ -1,7 +1,7 @@
1 1
 import React from 'react';
2 2
 import Taro from '@tarojs/taro';
3 3
 import { View } from '@tarojs/components';
4
-import { Cell } from '@antmjs/vantui';
4
+import { Cell, Icon } from '@antmjs/vantui';
5 5
 import Page from '@/layouts/index';
6 6
 import PowerList from '@/components/PowerList';
7 7
 import { getTaCheckItem } from '@/services/tacheckitem';
@@ -11,10 +11,13 @@ export default (props) => {
11 11
   const router = Taro.useRouter();
12 12
   const { checkId } = router.params;
13 13
 
14
+  const [loading, setLoading] = React.useState(false);
15
+
14 16
   const params = React.useMemo(() => ({
15 17
     checkId,
16 18
     itemType: 'loc',
17 19
     pageSize: 100,
20
+    mustValid: true,
18 21
   }), [checkId]);
19 22
 
20 23
   const onClick = (item) => {
@@ -24,13 +27,21 @@ export default (props) => {
24 27
   }
25 28
   
26 29
   return (
27
-    <Page>
30
+    <Page loading={loading}>
28 31
       <PowerList
29 32
         request={getTaCheckItem}
30 33
         params={params}
31 34
         renderItem={item => (
32
-          <Cell key={item.itemId} title={item.name} onClick={() => onClick(item)} />
35
+          <Cell
36
+            key={item.itemId}
37
+            title={item.name}
38
+            renderRightIcon={(
39
+              <Icon name={item.readonly ? 'link-o' : 'arrow'} />
40
+            )}
41
+            onClick={() => onClick(item)}
42
+          />
33 43
         )}
44
+        onLoadingChange={setLoading}
34 45
       />
35 46
     </Page>
36 47
   )

+ 29
- 0
src/services/tacheckansweritem.js 查看文件

@@ -0,0 +1,29 @@
1
+import request from '@/utils/request';
2
+
3
+/*
4
+ * 分页查询
5
+ */
6
+export const getTaCheckAnswerItem = (params) => request('/api/taCheckAnswerItem', { params });
7
+
8
+/*
9
+ * 新增数据
10
+ */
11
+export const postTaCheckAnswerItem = (data) => request('/api/taCheckAnswerItem', { data, method: 'post' });
12
+
13
+/*
14
+ * 通过ID查询单条数据
15
+ */
16
+export const getTaCheckAnswerItemById = (id) => request(`/api/taCheckAnswerItem/${id}`);
17
+
18
+/**
19
+ * 更新数据
20
+ * @param {*} id
21
+ * @param {*} data
22
+ * @returns
23
+ */
24
+export const putTaCheckAnswerItem = (id, data) => request(`/api/taCheckAnswerItem/${id}`, { data, method: 'put' });
25
+
26
+/*
27
+ * 通过主键删除数据
28
+ */
29
+export const deleteTaCheckAnswerItem = (id) => request(`/api/taCheckAnswerItem/${id}`, { method: 'delete' });

+ 1
- 1
src/services/tacheckitem.js 查看文件

@@ -28,4 +28,4 @@ export const putTaCheckItem = (id, data) => request(`/api/taCheckItem/${id}`, {
28 28
  */
29 29
 export const deleteTaCheckItem = (id) => request(`/api/taCheckItem/${id}`, { method: 'delete' });
30 30
 
31
-export const preCheck = (id) => request(`/api/taCheckItem/${id}/answer/pre-check`, { method: 'put' });
31
+export const preCheck = (id, opts = {}) => request(`/api/taCheckItem/${id}/answer/pre-check`, { method: 'put', ...opts });

+ 7
- 5
src/utils/request.js 查看文件

@@ -13,7 +13,7 @@ const getQueryStrByParams = params => {
13 13
 
14 14
 export default function request(api, options = {}) {
15 15
   return new Promise((resolve, reject) => {
16
-    const { method = 'GET', params, data = {}, header = {}, silent = false, ...cfgs } = options;
16
+    const { method = 'GET', params, data = {}, header = {}, silent = false, noSuccesTip, ...cfgs } = options;
17 17
     const queryStr = params ? getQueryStrByParams(params) : undefined;
18 18
     // eslint-disable-next-line no-undef
19 19
     const url = queryStr ? `${HOST}${api}?${queryStr}` : `${HOST}${api}`;
@@ -58,10 +58,12 @@ export default function request(api, options = {}) {
58 58
         }
59 59
 
60 60
         if (!silent && 'GET' !== method.toUpperCase()) {
61
-          Taro.showToast({
62
-            title: '操作成功',
63
-            icon: 'none',
64
-          })
61
+          if (!noSuccesTip) {
62
+            Taro.showToast({
63
+              title: '操作成功',
64
+              icon: 'none',
65
+            })
66
+          }
65 67
         }
66 68
 
67 69
         resolve(data.data);