Przeglądaj źródła

Merge branch 'master' of http://git.ycjcjy.com/civilized_city/miniapp

fangmingyue 2 lat temu
rodzic
commit
96f87755e5
77 zmienionych plików z 2593 dodań i 329 usunięć
  1. 2
    2
      config/dev.js
  2. 10
    1
      src/app.config.js
  3. 2
    4
      src/app.less
  4. BIN
      src/assets/icons/avatar.png
  5. BIN
      src/assets/icons/icon1.png
  6. BIN
      src/assets/icons/icon10.png
  7. BIN
      src/assets/icons/icon11.png
  8. BIN
      src/assets/icons/icon2.png
  9. BIN
      src/assets/icons/icon3.png
  10. BIN
      src/assets/icons/icon4.png
  11. BIN
      src/assets/icons/icon5.png
  12. BIN
      src/assets/icons/icon6.png
  13. BIN
      src/assets/icons/icon7.png
  14. BIN
      src/assets/icons/icon8.png
  15. BIN
      src/assets/icons/icon9.png
  16. 19
    0
      src/components/DatePicker/index.jsx
  17. 36
    0
      src/components/Icon.jsx
  18. 59
    0
      src/components/IssueCard/index.jsx
  19. 64
    0
      src/components/IssueCard/style.module.less
  20. 48
    0
      src/components/MenuIcon.jsx
  21. 60
    0
      src/components/NavLoading/index.jsx
  22. 22
    0
      src/components/NavLoading/style.module.less
  23. 9
    0
      src/components/OrgPicker/index.jsx
  24. 1
    0
      src/components/Pickerful/style.module.less
  25. 64
    0
      src/components/PowerList/index.jsx
  26. 17
    0
      src/components/RatioView.jsx
  27. 26
    16
      src/components/Uploader/index.jsx
  28. 40
    12
      src/components/map/index.jsx
  29. 95
    105
      src/layouts/TabBar.jsx
  30. 15
    6
      src/layouts/index.jsx
  31. 11
    2
      src/layouts/layout.module.less
  32. 4
    0
      src/pages/apply/apply/index.config.js
  33. 118
    0
      src/pages/apply/apply/index.jsx
  34. 60
    0
      src/pages/apply/components/Issue.jsx
  35. 4
    0
      src/pages/apply/detail/index.config.js
  36. 89
    0
      src/pages/apply/detail/index.jsx
  37. 4
    0
      src/pages/apply/list/index.config.js
  38. 72
    0
      src/pages/apply/list/index.jsx
  39. 4
    0
      src/pages/apply/verify/index.config.js
  40. 129
    0
      src/pages/apply/verify/index.jsx
  41. 4
    1
      src/pages/home/components/Head.jsx
  42. 7
    15
      src/pages/home/components/head.module.less
  43. 45
    3
      src/pages/home/index.jsx
  44. 16
    0
      src/pages/home/index.less
  45. 0
    156
      src/pages/issue/add/index.jsx
  46. 266
    0
      src/pages/issue/components/Issue/index.jsx
  47. 55
    0
      src/pages/issue/edit/components/Assigned.jsx
  48. 98
    0
      src/pages/issue/edit/components/Cancel.jsx
  49. 52
    0
      src/pages/issue/edit/components/Edit.jsx
  50. 45
    0
      src/pages/issue/edit/components/Save.jsx
  51. 1
    0
      src/pages/issue/edit/index.config.js
  52. 95
    0
      src/pages/issue/edit/index.jsx
  53. 4
    0
      src/pages/issue/list/index.config.js
  54. 82
    0
      src/pages/issue/list/index.jsx
  55. 4
    0
      src/pages/issue/list2/index.config.js
  56. 72
    0
      src/pages/issue/list2/index.jsx
  57. 9
    1
      src/pages/login/components/Form.jsx
  58. 1
    5
      src/pages/login/index.jsx
  59. 46
    0
      src/pages/message/list/components/Card.jsx
  60. 8
    0
      src/pages/message/list/components/card.module.less
  61. 4
    0
      src/pages/message/list/index.config.js
  62. 22
    0
      src/pages/message/list/index.jsx
  63. 38
    0
      src/pages/org/components/issue-info/index.jsx
  64. 114
    0
      src/pages/org/issue/detail/components/Apply.jsx
  65. 34
    0
      src/pages/org/issue/detail/components/Update.jsx
  66. 4
    0
      src/pages/org/issue/detail/index.config.js
  67. 136
    0
      src/pages/org/issue/detail/index.jsx
  68. 4
    0
      src/pages/org/issue/list/index.config.js
  69. 58
    0
      src/pages/org/issue/list/index.jsx
  70. 30
    0
      src/services/sysorg.js
  71. 15
    0
      src/services/taissue.js
  72. 29
    0
      src/services/taissueapply.js
  73. 29
    0
      src/services/tamessage.js
  74. 30
    0
      src/services/taorgissue.js
  75. 32
    0
      src/utils/biz.js
  76. 16
    0
      src/utils/date.js
  77. 4
    0
      src/utils/user.js

+ 2
- 2
config/dev.js Wyświetl plik

@@ -3,8 +3,8 @@ module.exports = {
3 3
     NODE_ENV: '"development"'
4 4
   },
5 5
   defineConstants: {
6
-    // HOST: '"http://127.0.0.1:9087"'
7
-    HOST: '"http://192.168.1.2:9087"'
6
+    HOST: '"http://127.0.0.1:9087"'
7
+    // HOST: '"http://192.168.1.2:9087"'
8 8
   },
9 9
   mini: {},
10 10
   h5: {}

+ 10
- 1
src/app.config.js Wyświetl plik

@@ -3,10 +3,19 @@ export default defineAppConfig({
3 3
   pages: [
4 4
     'pages/home/index',
5 5
     'pages/login/index',
6
+    'pages/issue/list/index',
7
+    'pages/issue/list2/index',
8
+    'pages/issue/edit/index',
9
+    'pages/org/issue/list/index',
10
+    'pages/org/issue/detail/index',
11
+    'pages/apply/list/index',
12
+    'pages/apply/apply/index',
13
+    'pages/apply/verify/index',
14
+    'pages/apply/detail/index',
15
+    'pages/message/list/index',
6 16
     'pages/index3/index',
7 17
     'pages/index4/index',
8 18
     'pages/index5/index',
9
-    'pages/issue/add/index',
10 19
     'pages/reporting/index',
11 20
     'pages/reporting/detail/index',
12 21
     'pages/reset-password/index',

+ 2
- 4
src/app.less Wyświetl plik

@@ -12,12 +12,10 @@ page {
12 12
   --button-primary-background-color: var(--main-bg-color);
13 13
   // 菊花转主色
14 14
   --loading-spinner-color: var(--main-bg-color);
15
+  // tab 划线颜色
16
+  --tabs-bottom-bar-color: var(--main-bg-color);
15 17
 }
16 18
 
17 19
 view {
18 20
   box-sizing: border-box;
19 21
 }
20
-
21
-.page-wrapper {
22
-
23
-}

BIN
src/assets/icons/avatar.png Wyświetl plik


BIN
src/assets/icons/icon1.png Wyświetl plik


BIN
src/assets/icons/icon10.png Wyświetl plik


BIN
src/assets/icons/icon11.png Wyświetl plik


BIN
src/assets/icons/icon2.png Wyświetl plik


BIN
src/assets/icons/icon3.png Wyświetl plik


BIN
src/assets/icons/icon4.png Wyświetl plik


BIN
src/assets/icons/icon5.png Wyświetl plik


BIN
src/assets/icons/icon6.png Wyświetl plik


BIN
src/assets/icons/icon7.png Wyświetl plik


BIN
src/assets/icons/icon8.png Wyświetl plik


BIN
src/assets/icons/icon9.png Wyświetl plik


+ 19
- 0
src/components/DatePicker/index.jsx Wyświetl plik

@@ -0,0 +1,19 @@
1
+import React from 'react';
2
+import Taro from '@tarojs/taro';
3
+import { View } from '@tarojs/components';
4
+import { Popup, DatetimePicker } from '@antmjs/vantui';
5
+
6
+export default (props) => {
7
+  const { show, onCancel, onChange, ...leftProps } = props;
8
+
9
+  const onConfirm = (e) => {
10
+    const dt = new Date(e.detail.value);
11
+    onChange(dt);
12
+  }
13
+  
14
+  return (
15
+    <Popup show={show} position="bottom" onClose={onCancel}>
16
+      <DatetimePicker {...leftProps} onConfirm={onConfirm} />
17
+    </Popup>
18
+  )
19
+}

+ 36
- 0
src/components/Icon.jsx Wyświetl plik

@@ -0,0 +1,36 @@
1
+import React from 'react';
2
+import Taro from '@tarojs/taro';
3
+import { Image } from '@tarojs/components';
4
+import icon1 from '@/assets/icons/icon1.png';
5
+import icon2 from '@/assets/icons/icon2.png';
6
+import icon3 from '@/assets/icons/icon3.png';
7
+import icon4 from '@/assets/icons/icon4.png';
8
+import icon5 from '@/assets/icons/icon5.png';
9
+import icon6 from '@/assets/icons/icon6.png';
10
+import icon7 from '@/assets/icons/icon7.png';
11
+import icon8 from '@/assets/icons/icon8.png';
12
+import icon9 from '@/assets/icons/icon9.png';
13
+import icon10 from '@/assets/icons/icon10.png';
14
+import icon11 from '@/assets/icons/icon11.png';
15
+
16
+const icons = {
17
+  icon1,
18
+  icon2,
19
+  icon3,
20
+  icon4,
21
+  icon5,
22
+  icon6,
23
+  icon7,
24
+  icon8,
25
+  icon9,
26
+  icon10,
27
+  icon11,
28
+}
29
+
30
+export default (props) => {
31
+  const { className, style, name } = props;
32
+  
33
+  return (
34
+    <Image src={icons[name]} className={className} style={style} />
35
+  )
36
+}

+ 59
- 0
src/components/IssueCard/index.jsx Wyświetl plik

@@ -0,0 +1,59 @@
1
+import React from 'react';
2
+import Taro from '@tarojs/taro';
3
+import dayjs from 'dayjs';
4
+import { View, ScrollView, RichText, Image, Text } from '@tarojs/components';
5
+import { getIssueStatus } from '@/utils/biz';
6
+import icon from '@/assets/icons/marker.png';
7
+import style from './style.module.less';
8
+
9
+const colors = [
10
+  ['rgba(130, 176, 254, 0.08)', 'rgba(52, 121, 237, 1)'], // 未交办
11
+  ['rgba(51, 218, 156, 0.08)', 'rgba(26, 117, 101, 1)'], // 已交办
12
+  ['rgba(251, 157, 75, 0.08)', 'rgba(232, 116, 16, 1)'], // 已办结
13
+  ['rgba(255, 245, 245, 1)', 'rgba(255, 76, 76, 1)'], // 已逾期
14
+  ['transparent', 'rgba(159, 159, 159, 1)'], // 已打回
15
+]
16
+
17
+export default (props) => {
18
+
19
+  const { detail, onClick, color, stText } = props;
20
+
21
+  const [
22
+    content,
23
+    createDate,
24
+    styleColor,
25
+    statusTxt,
26
+  ] = React.useMemo(() => {
27
+    if (!detail) return [];
28
+
29
+    const {value : bizStatus = 0, label : statusText} = getIssueStatus(detail);
30
+
31
+    return [
32
+      (detail.issueContent || detail.content || '').replace('\r\n', '<br>').replace('\n', '<br>'),
33
+      dayjs(detail.createDate).format('YYYY-MM-DD HH:mm'),
34
+      colors[color ?? bizStatus],
35
+      stText || statusText,
36
+    ];
37
+  }, [detail, color, stText]);
38
+  
39
+  return (
40
+    <View className={style['issue-card-wrapper']} onClick={onClick}>
41
+      <View className={style['issue-card-header']}>
42
+        <View>问题:</View>
43
+        <View>{createDate}</View>
44
+      </View>
45
+      <View className={style['issue-card-body']} style={{ backgroundColor: styleColor[0] }}>
46
+        <ScrollView scrollY style={{ height: '100%' }}>
47
+          <RichText nodes={content} />
48
+        </ScrollView>
49
+      </View>
50
+      <View className={style['issue-card-footer']}>
51
+        <View>
52
+          <Image src={icon} />
53
+          <Text>{detail?.issueAddr || detail?.addr || ''}</Text>
54
+        </View>
55
+        <View style={{ color: styleColor[1] }}>{statusTxt}</View>
56
+      </View>
57
+    </View>
58
+  )
59
+}

+ 64
- 0
src/components/IssueCard/style.module.less Wyświetl plik

@@ -0,0 +1,64 @@
1
+
2
+.issue-card-wrapper {
3
+  width: calc(100% - var(--main-space) * 2);
4
+  margin: var(--main-space);
5
+  background: #FFFFFF;
6
+  box-shadow: 0px 18px 22px 1px rgba(0,0,0,0.06);
7
+  border-radius: 8px;
8
+  padding: 20px 20px 40px 20px;
9
+
10
+  .issue-card-header {
11
+    display: flex;
12
+    align-items: center;
13
+    justify-content: space-between;
14
+
15
+    & > view {
16
+      flex: 1;
17
+      font-size: 24px;
18
+      line-height: 42px;
19
+      color: #202020;
20
+
21
+      &:last-child {
22
+        flex: none;
23
+        font-size: 20px;
24
+        color: #222;
25
+      }
26
+    }
27
+  }
28
+
29
+  .issue-card-body {
30
+    padding: 20px;
31
+    font-size: 24px;
32
+    color: #333;
33
+    line-height: 54px;
34
+    height: 252px;
35
+    margin-top: calc(var(--main-space) / 2);
36
+  }
37
+
38
+  .issue-card-footer {
39
+    display: flex;
40
+    align-items: center;
41
+    justify-content: space-between;
42
+    
43
+    font-size: 24px;
44
+    line-height: 32px;
45
+    color: #202020;
46
+    margin-top: var(--main-space);
47
+
48
+    image {
49
+      display: inline-block;
50
+      width: 25px;
51
+      height: 29px;
52
+      margin-right: 10px;
53
+      vertical-align: middle;
54
+    }
55
+    
56
+    & > view {
57
+      flex: 1;
58
+
59
+      &:last-child {
60
+        flex: none;
61
+      }
62
+    }
63
+  }
64
+}

+ 48
- 0
src/components/MenuIcon.jsx Wyświetl plik

@@ -0,0 +1,48 @@
1
+import React from 'react';
2
+import Taro from '@tarojs/taro';
3
+import { View } from '@tarojs/components';
4
+import RatioView from './RatioView';
5
+import Icon from './Icon';
6
+
7
+const wrapperStyle = {
8
+  display: 'grid',
9
+  placeItems: 'center',
10
+  width: '100%',
11
+  height: '100%',
12
+}
13
+
14
+const iconStyle = {
15
+  width: '100rpx',
16
+  height: '100rpx',
17
+  margin: 'auto',
18
+  display: 'block',
19
+}
20
+
21
+const txtStyle = {
22
+  marginTop: '20rpx',
23
+  textAlign: 'center',
24
+  fontSize: '30rpx',
25
+  color: '#202020',
26
+}
27
+
28
+export default (props) => {
29
+  
30
+  const { icon, text, link } = props;
31
+
32
+  const onClick = () => {
33
+    Taro.navigateTo({
34
+      url: link
35
+    })
36
+  }
37
+
38
+  return (
39
+    <RatioView>
40
+      <View style={wrapperStyle} onClick={onClick}>
41
+        <View>
42
+          <Icon name={icon} style={iconStyle} />
43
+          <View style={txtStyle}>{text}</View>
44
+        </View>
45
+      </View>
46
+    </RatioView>
47
+  )
48
+}

+ 60
- 0
src/components/NavLoading/index.jsx Wyświetl plik

@@ -0,0 +1,60 @@
1
+import React from 'react';
2
+import Taro from '@tarojs/taro';
3
+import { View } from '@tarojs/components';
4
+import style from './style.module.less';
5
+
6
+export default (props) => {
7
+
8
+  const { loading } = props;
9
+  const [percent, setPercent] = React.useState(0);
10
+  const percentRef = React.useRef(0);
11
+  const tickerRef = React.useRef();
12
+  const startRef = React.useRef(false);
13
+
14
+  React.useEffect(() => {
15
+    if (loading) {
16
+      startRef.current = true;
17
+      
18
+      // 先前进一点, 大约 3s 内走 94%
19
+      const firstS = 94;
20
+      const startS = 4;
21
+      percentRef.current = startS;
22
+      setPercent(percentRef.current);
23
+
24
+      const step = (firstS - startS) / 300;
25
+      tickerRef.current = setInterval(() => {
26
+        percentRef.current += step;
27
+        setPercent(percentRef.current);
28
+
29
+        if (percentRef.current >= 94) {
30
+          clearInterval(tickerRef.current);
31
+          tickerRef.current = null;
32
+        }
33
+      }, 100);
34
+    } else {
35
+      // 还没开始
36
+      if (!startRef.current) return ;
37
+
38
+      // 剩下的在 200ms 内走完
39
+      clearInterval(tickerRef.current);
40
+      const leftS = 100 - percentRef.current;      
41
+      const step = leftS / 20;
42
+      tickerRef.current = setInterval(() => {  
43
+        percentRef.current += step;
44
+        setPercent(percentRef.current);
45
+
46
+        if (percentRef.current >= 100) {
47
+          clearInterval(tickerRef.current);
48
+          tickerRef.current = null;
49
+          startRef.current = false;
50
+        }
51
+      }, 10);
52
+    }
53
+  }, [loading]);
54
+  
55
+  return (
56
+    <View className={`${style['nav-loading']} ${percent >= 100 ? style.hidden : ''}`}>
57
+      <View className={style['nav-loading-bar']} style={{ width: `${percent}%` }}></View>
58
+    </View>
59
+  )
60
+}

+ 22
- 0
src/components/NavLoading/style.module.less Wyświetl plik

@@ -0,0 +1,22 @@
1
+
2
+.nav-loading {
3
+  position: fixed;
4
+  z-index: 1000;
5
+  top: 0;
6
+  left: 0;
7
+  width: 100%;
8
+  height: 6px;
9
+  background: transparent;
10
+  opacity: 1;
11
+  transition: opacity 200ms;
12
+
13
+  &.hidden {
14
+    opacity: 0;
15
+  }
16
+
17
+  .nav-loading-bar {
18
+    width: 0%;
19
+    height: 100%;
20
+    background-color: var(--main-bg-color);
21
+  }
22
+}

+ 9
- 0
src/components/OrgPicker/index.jsx Wyświetl plik

@@ -0,0 +1,9 @@
1
+import React from 'react';
2
+import Pickerful from '@/components/Pickerful';
3
+import { getSysOrg } from '@/services/sysorg';
4
+
5
+export default (props) => {
6
+  return (
7
+    <Pickerful {...props} keyValue="orgId" dictAPI={getSysOrg} />
8
+  )
9
+}

+ 1
- 0
src/components/Pickerful/style.module.less Wyświetl plik

@@ -2,6 +2,7 @@
2 2
 .pickerful-box {
3 3
   width: 90vw;
4 4
   height: 100vh;
5
+  padding-bottom: env(safe-area-inset-bottom);
5 6
   display: flex;
6 7
   flex-direction: column;
7 8
 

+ 64
- 0
src/components/PowerList/index.jsx Wyświetl plik

@@ -0,0 +1,64 @@
1
+import React from 'react';
2
+import Taro from '@tarojs/taro';
3
+import { View } from '@tarojs/components';
4
+import { PowerScrollView } from '@antmjs/vantui';
5
+
6
+export default (props) => {
7
+  const { request, params, renderItem } = props;
8
+
9
+  const pageSize = 20;
10
+  const pageNumRef = React.useRef(1);
11
+  const listRef = React.useRef([]);
12
+  const [list, setList] = React.useState([]);
13
+  const [finished, setFinished] = React.useState(true);
14
+  listRef.current = list;
15
+
16
+  const queryData = React.useCallback((options = {}) => {
17
+    return new Promise((resolve, reject) => {
18
+      request({
19
+        pageSize,
20
+        ...(params || {}),
21
+        ...options
22
+      }).then((res) => {
23
+        const { records, current, pages } = res;
24
+
25
+        if (current == 1) {
26
+          setList(records || []);
27
+        } else {
28
+          setList(listRef.current.concat(records || []));
29
+        }
30
+
31
+        setFinished(current >= pages);
32
+        resolve();
33
+      }).catch((err) => {
34
+        reject(err);
35
+      });
36
+    });
37
+  }, [request, params]);
38
+
39
+  const onScrollToLower = React.useCallback((event = 0, isRefresh = false) => {
40
+    pageNumRef.current += 1;
41
+    return queryData({pageNum : pageNumRef.current});
42
+  }, [queryData]);
43
+
44
+  React.useEffect(() => {
45
+    pageNumRef.current = 1;
46
+    queryData({pageNum : pageNumRef.current});
47
+  }, [queryData])
48
+  
49
+  return (
50
+    <PowerScrollView
51
+      scrollY
52
+      finishedText="没有更多了"
53
+      refresherEnabled={false}
54
+      current={list.length}
55
+      pageSize={pageSize}
56
+      finished={finished}
57
+      onScrollToLower={onScrollToLower}
58
+    >
59
+      {
60
+        list.map(renderItem)
61
+      }
62
+    </PowerScrollView>
63
+  )
64
+}

+ 17
- 0
src/components/RatioView.jsx Wyświetl plik

@@ -0,0 +1,17 @@
1
+import React from 'react';
2
+import Taro from '@tarojs/taro';
3
+import { View } from '@tarojs/components';
4
+
5
+export default (props) => {
6
+  const { className, style, ratio = 100 } = props;
7
+  
8
+  return (
9
+    <View className={className} style={style}>
10
+      <View style={{ paddingBottom: `${ratio}%`, position: 'relative', overflow: 'hidden' }}>
11
+        <View style={{ width: '100%', height: '100%', position: 'absolute', left: 0, top: 0 }}>
12
+          {props.children}
13
+        </View>
14
+      </View>
15
+    </View>
16
+  )
17
+}

+ 26
- 16
src/components/Uploader/index.jsx Wyświetl plik

@@ -10,13 +10,15 @@ import style from './style.modules.less';
10 10
 export default (props) => {
11 11
   // value 为数组
12 12
   // 单个元素结构为 { url, fileType }
13
-  const { value = [], onChange } = props;
13
+  const { value, disabled = false, onChange } = props;
14 14
   const [loading, setLoading] = React.useState(false);
15 15
 
16 16
   const onAdd = (e) => {
17 17
     e.preventDefault();
18 18
     e.stopPropagation();
19 19
 
20
+    if (disabled) return;
21
+
20 22
     setLoading(true);
21 23
     Taro.chooseMedia({
22 24
       maxDuration: 60,
@@ -25,7 +27,7 @@ export default (props) => {
25 27
         const { tempFiles } = res;
26 28
         uploadFiles(tempFiles).then(resp => {
27 29
           setLoading(false);          
28
-          onChange(value.concat(resp));
30
+          onChange((value || []).concat(resp));
29 31
         }).catch((err) => {
30 32
           console.error(err);
31 33
           setLoading(false);
@@ -53,27 +55,35 @@ export default (props) => {
53 55
   return (
54 56
     <View className={style['uploader-wrapper']}>
55 57
       <View className={style['uploader-box']}>
56
-        <View className={style['uploader-add']}>
57
-          <Image src={icon} onClick={onAdd} />
58
-          {
59
-            loading && (
60
-              <View>
61
-                <Loading color="#fff" />
62
-              </View>
63
-            )
64
-          }
65
-        </View>
66 58
         {
67
-          value.map((item, inx) => (
59
+          !disabled && (
60
+            <View className={style['uploader-add']}>
61
+              <Image src={icon} onClick={onAdd} />
62
+              {
63
+                loading && (
64
+                  <View>
65
+                    <Loading color="#fff" />
66
+                  </View>
67
+                )
68
+              }
69
+            </View>
70
+          )
71
+        }
72
+        {
73
+          (value || []).map((item, inx) => (
68 74
             <View key={item.url} className={style['uploader-item']}>
69 75
               {
70 76
                 item.fileType == 'image' ?
71 77
                   <Image src={item.url} onClick={() => onPreview(inx)} /> :
72 78
                   <Video src={item.url} controls={false} onClick={() => onPreview(inx)} />
73 79
               }
74
-              <View onClick={() => onDelete(item)}>
75
-                <Image src={closeIcon} />
76
-              </View>
80
+              {
81
+                !disabled && (
82
+                  <View onClick={() => onDelete(item)}>
83
+                    <Image src={closeIcon} />
84
+                  </View>
85
+                )
86
+              }
77 87
             </View>
78 88
           ))
79 89
         }

+ 40
- 12
src/components/map/index.jsx Wyświetl plik

@@ -1,29 +1,57 @@
1 1
 import React from 'react';
2
-import { View, Map, Marker } from '@tarojs/components';
2
+import Taro from '@tarojs/taro';
3
+import { View, Map } from '@tarojs/components';
4
+import { getLocation } from '@/utils/authorize';
3 5
 import iconPath from '@/assets/icons/marker.png';
4 6
 import style from './style.module.less';
5 7
 
6 8
 export default (props) => {
7
-  const { location = '' } = props;
8
-  const {lng, lat} = location.split(',');
9
+  const { location } = props;
10
+
11
+  const [currentPos, setCurPos] = React.useState();
12
+  const [lngLat, setLngLat] = React.useState([,]);
9 13
 
10 14
   const markers = React.useMemo(() => {
11
-    if (lng == undefined || lat == undefined) return [];
12
-  
15
+    if (!location && !currentPos) return [];
16
+    
17
+    let longitude, latitude;
18
+    if (location) {
19
+      const [lng, lat] = location.split(',');
20
+      longitude = lng - 0;
21
+      latitude = lat - 0;
22
+    } else {
23
+      longitude = currentPos.longitude;
24
+      latitude = currentPos.latitude;
25
+    }
26
+
27
+    setLngLat([longitude, latitude]);
28
+
13 29
     return [{
14
-      longitude: lng - 0,
15
-      latitude: lat - 0,
30
+      id: 1,
31
+      longitude,
32
+      latitude,
16 33
       iconPath,
17
-      width: 50,
18
-      height: 58
34
+      width: 17,
35
+      height: 20
19 36
     }];
20
-  }, [lng, lat]);
37
+  }, [location, currentPos]);
38
+  
39
+  React.useEffect(() => {
40
+    getLocation().then((res) => {
41
+      setCurPos(res);
42
+    }).catch((err) => {
43
+      Taro.showToast({
44
+        title: '定位失败',
45
+        icon: 'none',
46
+      })
47
+    });
48
+  }, []);
21 49
   
22 50
   return (
23 51
     <View className={style['map-wrapper']}>
24 52
       <Map
25
-        longitude={lng}
26
-        latitude={lat}
53
+        longitude={lngLat[0]}
54
+        latitude={lngLat[1]}
27 55
         markers={markers}
28 56
       ></Map>
29 57
     </View>

+ 95
- 105
src/layouts/TabBar.jsx Wyświetl plik

@@ -2,6 +2,14 @@ import React from 'react';
2 2
 import Taro from '@tarojs/taro';
3 3
 import { Image } from '@tarojs/components';
4 4
 import { Tabbar, TabbarItem } from '@antmjs/vantui';
5
+import {
6
+  ROLE_INSPECTOR,
7
+  ROLE_MANAGER,
8
+  ROLE_ORG_USER,
9
+  ROLE_ORG_MANAGER,
10
+  ROLE_QUERY_PERSON,
11
+  ROLE_CITIZEN
12
+} from '@/utils/user';
5 13
 import homeIcon from '@/assets/tabbar/page.png';
6 14
 import homeActiveIcon from '@/assets/tabbar/page click.png';
7 15
 import noticeIcon from '@/assets/tabbar/notice.png';
@@ -13,21 +21,74 @@ import checkActiveIcon from '@/assets/tabbar/evaluation click.png';
13 21
 import mineIcon from '@/assets/tabbar/my.png';
14 22
 import mineActiveIcon from '@/assets/tabbar/my click.png';
15 23
 
16
-const tabPages = {
17
-  home: '/pages/home/index',
18
-  notice: '/pages/index3/index',
19
-  issue: '/pages/issue/add/index',
20
-  check: '/pages/index4/index',
21
-  mine: '/pages/index5/index',
24
+const home = {
25
+  name: 'home',
26
+  label: '首页',
27
+  icon: homeIcon,
28
+  activeIcon: homeActiveIcon,
29
+  page: '/pages/home/index'
30
+}
31
+const notice = {
32
+  name: 'notice',
33
+  label: '公告',
34
+  icon: noticeIcon,
35
+  activeIcon: noticeActiveIcon,
36
+  page: '/pages/index3/index'
37
+}
38
+const issue = {
39
+  name: 'issue',
40
+  label: '发布',
41
+  icon: issueIcon,
42
+  activeIcon: issueActiveIcon,
43
+  page: '/pages/issue/edit/index'
44
+}
45
+const check = {
46
+  name: 'check',
47
+  label: '测评标准',
48
+  icon: checkIcon,
49
+  activeIcon: checkActiveIcon,
50
+  page: '/pages/index4/index'
51
+}
52
+const mine = {
53
+  name: 'mine',
54
+  label: '我的',
55
+  icon: mineIcon,
56
+  activeIcon: mineActiveIcon,
57
+  page: '/pages/index5/index'
22 58
 }
23 59
 
24 60
 export default (props) => {
25
-  const { active = -1, className } = props;
61
+  const { active = -1, className, user } = props;
62
+
63
+  const tabItems = React.useMemo(() => {
64
+    if (!user) return [];
65
+    
66
+    if (user.dutyList.indexOf(ROLE_INSPECTOR) > -1 // 督察员
67
+      || user.dutyList.indexOf(ROLE_MANAGER) > -1 // 管理员
68
+    ) {
69
+      return [
70
+        home,
71
+        notice,
72
+        issue,
73
+        check,
74
+        mine
75
+      ]
76
+    }
77
+
78
+    if (user.dutyList.indexOf(ROLE_ORG_USER) > -1) {
79
+      return [
80
+        home,
81
+        notice,
82
+        mine,
83
+      ]
84
+    }
85
+
86
+    return [];
87
+  }, [user]);
26 88
 
27 89
   const onChange = (e) => {
28
-    Taro.reLaunch({
29
-      url: tabPages[e.detail]
30
-    })
90
+    const url = tabItems.filter(x => x.name === e.detail)[0].page;
91
+    Taro.reLaunch({ url })
31 92
   }
32 93
 
33 94
   return (
@@ -37,101 +98,30 @@ export default (props) => {
37 98
       active={active}
38 99
       onChange={onChange}
39 100
     >
40
-      <TabbarItem
41
-        name="home"
42
-        renderIcon={
43
-          <Image
44
-            src={homeIcon}
45
-            mode="aspectFit"
46
-            style="width: 30px; height: 18px;"
47
-          ></Image>
48
-        }
49
-        renderIconActive={
50
-          <Image
51
-            src={homeActiveIcon}
52
-            mode="aspectFit"
53
-            style="width: 30px; height: 18px;"
54
-          ></Image>
55
-        }
56
-      >
57
-        首页
58
-      </TabbarItem>
59
-      <TabbarItem
60
-        name="notice"
61
-        renderIcon={
62
-          <Image
63
-            src={noticeIcon}
64
-            mode="aspectFit"
65
-            style="width: 30px; height: 18px;"
66
-          ></Image>
67
-        }
68
-        renderIconActive={
69
-          <Image
70
-            src={noticeActiveIcon}
71
-            mode="aspectFit"
72
-            style="width: 30px; height: 18px;"
73
-          ></Image>
74
-        }
75
-      >
76
-        公告
77
-      </TabbarItem>
78
-      <TabbarItem
79
-        name="issue"
80
-        renderIcon={
81
-          <Image
82
-            src={issueIcon}
83
-            mode="aspectFit"
84
-            style="width: 30px; height: 18px;"
85
-          ></Image>
86
-        }
87
-        renderIconActive={
88
-          <Image
89
-            src={issueActiveIcon}
90
-            mode="aspectFit"
91
-            style="width: 30px; height: 18px;"
92
-          ></Image>
93
-        }
94
-      >
95
-        发布
96
-      </TabbarItem>
97
-      <TabbarItem
98
-        name="check"
99
-        renderIcon={
100
-          <Image
101
-            src={checkIcon}
102
-            mode="aspectFit"
103
-            style="width: 30px; height: 18px;"
104
-          ></Image>
105
-        }
106
-        renderIconActive={
107
-          <Image
108
-            src={checkActiveIcon}
109
-            mode="aspectFit"
110
-            style="width: 30px; height: 18px;"
111
-          ></Image>
112
-        }
113
-      >
114
-        测评标准
115
-      </TabbarItem>
116
-      <TabbarItem
117
-        name="mine"
118
-        renderIcon={
119
-          <Image
120
-            src={mineIcon}
121
-            mode="aspectFit"
122
-            style="width: 30px; height: 18px;"
123
-          ></Image>
124
-        }
125
-        renderIconActive={
126
-          <Image
127
-            src={mineActiveIcon}
128
-            mode="aspectFit"
129
-            style="width: 30px; height: 18px;"
130
-          ></Image>
131
-        }
132
-      >
133
-        我的
134
-      </TabbarItem>
101
+      {
102
+        tabItems.map(item => (
103
+          <TabbarItem
104
+            key={item.name}
105
+            name={item.name}
106
+            renderIcon={
107
+              <Image
108
+                src={item.icon}
109
+                mode="aspectFit"
110
+                style="width: 30px; height: 18px;"
111
+              ></Image>
112
+            }
113
+            renderIconActive={
114
+              <Image
115
+                src={item.activeIcon}
116
+                mode="aspectFit"
117
+                style="width: 30px; height: 18px;"
118
+              ></Image>
119
+            }
120
+          >
121
+            {item.label}
122
+          </TabbarItem>
123
+        ))
124
+      }
135 125
     </Tabbar>
136 126
   )
137 127
 }

+ 15
- 6
src/layouts/index.jsx Wyświetl plik

@@ -2,17 +2,18 @@ import React from 'react';
2 2
 import Taro from '@tarojs/taro';
3 3
 import { View, Image } from '@tarojs/components';
4 4
 import { useModel } from '@/store';
5
-import { Loading } from '@antmjs/vantui';
5
+import { Loading, Notify } from '@antmjs/vantui';
6
+import NavLoading from '@/components/NavLoading';
6 7
 import Auth from '@/components/Auth';
7 8
 import TabBar from './TabBar';
8 9
 import laySty from './layout.module.less';
9 10
 
10 11
 export default (props) => {
11
-  const { className, style, roles, tabBar = false } = props;
12
+  const { className, style, roles, tabBar = false, loading } = props;
12 13
 
13 14
   const { person, user } = useModel('user');
14 15
 
15
-  const classNames = `${laySty['page-wrapper']} ${className}`;
16
+  const conatinerClass = `${laySty['page-conatiner']} ${tabBar ? laySty['with-tabbar'] : ''} ${className}`;
16 17
 
17 18
   React.useEffect(() => {
18 19
     if (person && !user) {
@@ -26,8 +27,10 @@ export default (props) => {
26 27
   }, [person, user]);
27 28
 
28 29
   return (
29
-    <View className={classNames} style={style}>
30
-      <View className={laySty['page-conatiner']}>
30
+    <View className={laySty['page-wrapper']}>
31
+      <Notify id="vanNotify" />
32
+      <NavLoading loading={loading} />
33
+      <View className={conatinerClass} style={style}>
31 34
         {
32 35
           !person && (
33 36
             <View className={laySty.loading}>
@@ -40,9 +43,15 @@ export default (props) => {
40 43
         <Auth roles={roles}>
41 44
           {props.children}
42 45
         </Auth>
46
+
47
+        {
48
+          !tabBar && (
49
+            <View className={laySty['pdm-space']}></View>
50
+          )
51
+        }
43 52
       </View>
44 53
       {
45
-        tabBar && <TabBar className={laySty['page-tabbar']} active={tabBar} />
54
+        tabBar && <TabBar className={laySty['page-tabbar']} active={tabBar} user={user} />
46 55
       }
47 56
     </View>
48 57
   )

+ 11
- 2
src/layouts/layout.module.less Wyświetl plik

@@ -8,8 +8,17 @@
8 8
 
9 9
   .page-conatiner {
10 10
     width: 100%;
11
-    height: calc(100% - var(--tabbar-height, 100rpx));
12
-    overflow: hidden;
11
+    height: 100%;
12
+
13
+    &.with-tabbar {
14
+      overflow-y: scroll;
15
+      // height: calc(100% - var(--tabbar-height, 100rpx));
16
+      padding-bottom: var(--tabbar-height, 100rpx);
17
+    }
18
+
19
+    .pdm-space {
20
+      height: var(--main-space);
21
+    }
13 22
   }
14 23
 
15 24
   .page-tabbar {

+ 4
- 0
src/pages/apply/apply/index.config.js Wyświetl plik

@@ -0,0 +1,4 @@
1
+// eslint-disable-next-line no-undef
2
+export default definePageConfig({
3
+  navigationBarTitleText: '问题单申请'
4
+})

+ 118
- 0
src/pages/apply/apply/index.jsx Wyświetl plik

@@ -0,0 +1,118 @@
1
+import React from 'react';
2
+import Taro from '@tarojs/taro';
3
+import { View } from '@tarojs/components';
4
+import Page from '@/layouts/index';
5
+import { Cell, CellGroup, Field, Button } from '@antmjs/vantui';
6
+import DatePicker from '@/components/DatePicker';
7
+import { getDateStr } from '@/utils/date';
8
+import { postTaIssueApply } from '@/services/taissueapply';
9
+import { warn } from '@/utils/message';
10
+import Issue from '../components/Issue';
11
+
12
+const today = new Date();
13
+export default (props) => {
14
+
15
+  const router = Taro.useRouter();
16
+  const { issueId, applyType } = router.params;
17
+
18
+  const [loading, setLoading] = React.useState(false);
19
+
20
+  React.useMemo(() => {
21
+    switch (applyType) {
22
+      case 'delay':
23
+        Taro.setNavigationBarTitle('申请延期');
24
+        break;
25
+      case 'reject':
26
+        Taro.setNavigationBarTitle('申请驳回');
27
+        break;
28
+      case 'verify':
29
+        Taro.setNavigationBarTitle('申请审核');
30
+        break;
31
+      case 'end':
32
+        Taro.setNavigationBarTitle('申请办结');
33
+        break;
34
+    }
35
+  }, [applyType]);
36
+
37
+  
38
+  const [submitting, setSubmitting] = React.useState(false);
39
+  const [formData, setFormData] = React.useState({});
40
+  const [showDatePicker, setShowDatePicker] = React.useState(false);
41
+
42
+  const setFieldValue = (key, value) => {
43
+    setFormData({
44
+      ...formData,
45
+      [key]: value,
46
+    })
47
+  }
48
+  
49
+  const onDateChange = (dt) => {
50
+    const date = getDateStr(dt);
51
+    setFieldValue('content', date);
52
+    setShowDatePicker(false);
53
+  }
54
+
55
+  const onSubmit = () => {
56
+    try {
57
+      if (applyType == 'delay') {
58
+        warn(!formData.content, '请选择延期时间');
59
+      }
60
+      warn(!formData.remark, '请填写申请说明');
61
+    } catch (e) {
62
+      return ;
63
+    }
64
+
65
+    const data = {
66
+      issueId,
67
+      applyType,
68
+      ...formData
69
+    }
70
+
71
+    setSubmitting(true);
72
+    postTaIssueApply(data).then(() => {
73
+      Taro.navigateBack({delta: 1});
74
+      setSubmitting(false);
75
+    }).catch(() => {
76
+      setSubmitting(false);
77
+    })
78
+  }
79
+  
80
+  
81
+  return (
82
+    <Page loading={loading}>
83
+      <Issue issueId={issueId} onLoading={setLoading} />
84
+      
85
+      <DatePicker
86
+        type="date"
87
+        minDate={today}
88
+        show={showDatePicker}
89
+        value={formData.content}
90
+        onCancel={() => setShowDatePicker(false)}
91
+        onChange={onDateChange}
92
+      />
93
+
94
+      <CellGroup style={{marginTop: '20px'}}>
95
+        {
96
+          applyType == 'delay' && (
97
+            <Cell
98
+              isLink
99
+              title="延期时间"
100
+              value={formData.content}
101
+              onClick={() => setShowDatePicker(true)}
102
+            />
103
+          )
104
+        }
105
+        <Cell title="申请说明" />
106
+        <Field
107
+          type="textarea"
108
+          autosize={{ minHeight: '120px' }}
109
+          value={formData.remark}
110
+          onChange={e => setFieldValue('remark', e.detail)}
111
+        />
112
+      </CellGroup>
113
+      <View style={{marginTop: '20px', padding: 'var(--main-space)'}}>
114
+        <Button block type="primary" loading={submitting} onClick={onSubmit}>提交申请</Button>
115
+      </View>
116
+    </Page>
117
+  )
118
+}

+ 60
- 0
src/pages/apply/components/Issue.jsx Wyświetl plik

@@ -0,0 +1,60 @@
1
+import React from 'react';
2
+import Taro from '@tarojs/taro';
3
+import { View } from '@tarojs/components';
4
+import { Cell, CellGroup, Field, Button } from '@antmjs/vantui';
5
+import Uploader from '@/components/Uploader/index';
6
+import { getTaIssueById } from '@/services/taissue';
7
+import { getDtStr } from '@/utils/date';
8
+
9
+export default (props) => {
10
+  const { issueId, onLoading } = props;
11
+
12
+  const [issue, setIssue] = React.useState({});
13
+
14
+  const setLoading = (val) => {
15
+    if (onLoading) {
16
+      onLoading(val);
17
+    }
18
+  }
19
+
20
+  React.useEffect(() => {
21
+    if (issueId) {
22
+      setLoading(true);
23
+      getTaIssueById(issueId).then((res) => {
24
+        setIssue(res);
25
+        setLoading(false);
26
+      }).catch(() => {
27
+        setLoading(false);
28
+      });
29
+    }
30
+  }, [issueId]);
31
+
32
+  return (
33
+    <>
34
+      <CellGroup>
35
+        <Cell title="上报时间" value={getDtStr(issue?.createDate)} />
36
+        <Cell title="办结时间" value={issue?.expireDate} />
37
+        <Cell title="交办次数" value={issue?.processNum || 1} />
38
+      </CellGroup>
39
+      <CellGroup style={{marginTop: '20px'}}>
40
+        <Cell title="抽检位置" value={issue?.locName} />
41
+        <Cell title="定 位 点" value={issue?.addr} />
42
+        <Cell title="问题详情" />        
43
+        <Field
44
+          readonly
45
+          type="textarea"
46
+          autosize={{ minHeight: '120px' }}
47
+          value={issue?.content}
48
+        /> 
49
+        <Cell
50
+          renderTitle={
51
+            <Uploader
52
+              disabled
53
+              value={issue?.attachList}
54
+            />
55
+          }
56
+        />
57
+      </CellGroup>
58
+    </>
59
+  )
60
+}

+ 4
- 0
src/pages/apply/detail/index.config.js Wyświetl plik

@@ -0,0 +1,4 @@
1
+// eslint-disable-next-line no-undef
2
+export default definePageConfig({
3
+  navigationBarTitleText: '审批详情'
4
+})

+ 89
- 0
src/pages/apply/detail/index.jsx Wyświetl plik

@@ -0,0 +1,89 @@
1
+import React from 'react';
2
+import Taro from '@tarojs/taro';
3
+import { View } from '@tarojs/components';
4
+import Page from '@/layouts/index';
5
+import { Cell, CellGroup, Field, Radio, RadioGroup } from '@antmjs/vantui';
6
+import { getTaIssueApplyById } from '@/services/taissueapply';
7
+import { getDtStr } from '@/utils/date';
8
+import Issue from '../components/Issue';
9
+
10
+// const today = new Date();
11
+export default (props) => {
12
+
13
+  const router = Taro.useRouter();
14
+  const { id, applyType, issueId } = router.params;
15
+
16
+  const [loading, setLoading] = React.useState(false);
17
+  const [applyInfo, setApplyInfo] = React.useState({});
18
+
19
+  React.useMemo(() => {
20
+    switch (applyType) {
21
+      case 'delay':
22
+        Taro.setNavigationBarTitle('延期申请');
23
+        break;
24
+      case 'reject':
25
+        Taro.setNavigationBarTitle('驳回申请');
26
+        break;
27
+      case 'verify':
28
+        Taro.setNavigationBarTitle('审核申请');
29
+        break;
30
+      case 'end':
31
+        Taro.setNavigationBarTitle('办结申请');
32
+        break;
33
+    }
34
+  }, [applyType]);
35
+  
36
+  React.useEffect(() => {
37
+    if (id) {
38
+      setLoading(true);
39
+      getTaIssueApplyById(id).then(res => {
40
+        setApplyInfo(res);
41
+        setLoading(false);
42
+      }).catch(() => {
43
+        setLoading(false);
44
+      });
45
+    }
46
+  }, [id]);
47
+  
48
+  return (
49
+    <Page loading={loading}>
50
+      <Issue issueId={issueId} />
51
+     
52
+      <CellGroup style={{marginTop: '20px'}}>
53
+        {
54
+          applyInfo.applyType == 'delay' && (
55
+            <Cell
56
+              title="延期日期"
57
+              value={applyInfo.content}
58
+            />
59
+          )
60
+        }
61
+        <Cell title="申请说明" />
62
+        <Field
63
+          readonly
64
+          type="textarea"
65
+          autosize={{ minHeight: '120px' }}
66
+          value={applyInfo.remark}
67
+        />
68
+      </CellGroup>
69
+     
70
+      <CellGroup style={{marginTop: '20px'}}>
71
+        <Cell
72
+          title="审批结果"
73
+          value={applyInfo.verifyStatus == 'pass' ? '通过' : '驳回'}
74
+        />
75
+        <Cell title="审批说明" />
76
+        <Field
77
+          readonly
78
+          type="textarea"
79
+          autosize={{ minHeight: '120px' }}
80
+          value={applyInfo.verifyDesc}
81
+        />
82
+        <Cell
83
+          title="审核日期"
84
+          value={getDtStr(applyInfo.verifyDate)}
85
+        />
86
+      </CellGroup>
87
+    </Page>
88
+  )
89
+}

+ 4
- 0
src/pages/apply/list/index.config.js Wyświetl plik

@@ -0,0 +1,4 @@
1
+// eslint-disable-next-line no-undef
2
+export default definePageConfig({
3
+  navigationBarTitleText: '申请列表'
4
+})

+ 72
- 0
src/pages/apply/list/index.jsx Wyświetl plik

@@ -0,0 +1,72 @@
1
+import React from 'react';
2
+import Taro from '@tarojs/taro';
3
+import { View } from '@tarojs/components';
4
+import { Tab, Tabs } from '@antmjs/vantui';
5
+import Page from '@/layouts/index';
6
+import PowerList from '@/components/PowerList';
7
+import Card from '@/components/IssueCard';
8
+import { getTaIssueApply } from '@/services/taissueapply';
9
+import { ROLE_MANAGER } from '@/utils/user';
10
+import { useModel } from '@/store';
11
+import { getIssueStatus } from '@/utils/biz';
12
+
13
+export default (props) => {
14
+
15
+  const router = Taro.useRouter();
16
+  const { title, applyType = '' } = router.params;
17
+
18
+  React.useMemo(() => {
19
+    if (title) {
20
+      Taro.setNavigationBarTitle({ title });
21
+    } else {
22
+      Taro.setNavigationBarTitle({ title: '申请列表' });
23
+    }
24
+  }, [title]);
25
+
26
+  const { user } = useModel('user');
27
+
28
+  const onClick = (item) => {
29
+    if (!user) return ;
30
+
31
+    if (user.dutyList.indexOf(ROLE_MANAGER) > -1) {
32
+      if (item.verifyStatus) {
33
+        Taro.navigateTo({
34
+          url: `/pages/apply/detail/index?id=${item.applyId}&issueId=${item.issueId}&applyType=${item.applyType}`
35
+        })
36
+      } else {
37
+        Taro.navigateTo({
38
+          url: `/pages/apply/verify/index?id=${item.applyId}&issueId=${item.issueId}&applyType=${item.applyType}`
39
+        })
40
+      }
41
+    } else {
42
+      Taro.navigateTo({
43
+        url: `/pages/apply/detail/index?id=${item.applyId}&issueId=${item.issueId}&applyType=${item.applyType}`
44
+      })
45
+    }
46
+  }
47
+  
48
+  return (
49
+    <Page>
50
+      <Tabs sticky>
51
+        <Tab title="监督员">
52
+          <PowerList
53
+            request={getTaIssueApply}
54
+            params={{applyType, sourceType: 'inspector'}}
55
+            renderItem={(item) => (
56
+              <Card key={item.issueId} detail={item} stText={title} onClick={() => onClick(item)} />
57
+            )}
58
+          />
59
+        </Tab>
60
+        <Tab title="市民">
61
+          <PowerList
62
+            request={getTaIssueApply}
63
+            params={{applyType, sourceType: 'feedback'}}
64
+            renderItem={(item) => (
65
+              <Card key={item.issueId} detail={item} stText={title} onClick={() => onClick(item)} />
66
+            )}
67
+          />
68
+        </Tab>
69
+      </Tabs>
70
+    </Page>
71
+  )
72
+}

+ 4
- 0
src/pages/apply/verify/index.config.js Wyświetl plik

@@ -0,0 +1,4 @@
1
+// eslint-disable-next-line no-undef
2
+export default definePageConfig({
3
+  navigationBarTitleText: '问题单审批'
4
+})

+ 129
- 0
src/pages/apply/verify/index.jsx Wyświetl plik

@@ -0,0 +1,129 @@
1
+import React from 'react';
2
+import Taro from '@tarojs/taro';
3
+import { View } from '@tarojs/components';
4
+import Page from '@/layouts/index';
5
+import { Cell, CellGroup, Field, Button, Radio, RadioGroup } from '@antmjs/vantui';
6
+import { putTaIssueApply, getTaIssueApplyById } from '@/services/taissueapply';
7
+import { warn } from '@/utils/message';
8
+import Issue from '../components/Issue';
9
+
10
+// const today = new Date();
11
+export default (props) => {
12
+
13
+  const router = Taro.useRouter();
14
+  const { id, applyType, issueId } = router.params;
15
+
16
+  const [loading, setLoading] = React.useState(false);
17
+  const [applyInfo, setApplyInfo] = React.useState({});
18
+
19
+  React.useMemo(() => {
20
+    switch (applyType) {
21
+      case 'delay':
22
+        Taro.setNavigationBarTitle('延期审批');
23
+        break;
24
+      case 'reject':
25
+        Taro.setNavigationBarTitle('驳回审批');
26
+        break;
27
+      case 'verify':
28
+        Taro.setNavigationBarTitle('审核审批');
29
+        break;
30
+      case 'end':
31
+        Taro.setNavigationBarTitle('办结审批');
32
+        break;
33
+    }
34
+  }, [applyType]);
35
+
36
+  
37
+  const [submitting, setSubmitting] = React.useState(false);
38
+  const [formData, setFormData] = React.useState({});
39
+
40
+  const setFieldValue = (key, value) => {
41
+    setFormData({
42
+      ...formData,
43
+      [key]: value,
44
+    })
45
+  }
46
+  
47
+  const onSubmit = () => {
48
+    try {
49
+      warn(!formData.verifyStatus, '请选择审批结果');
50
+      warn(!formData.verifyDesc, '请填写审批说明');
51
+    } catch (e) {
52
+      return ;
53
+    }
54
+
55
+    const data = {
56
+      ...applyInfo,
57
+      ...formData
58
+    }
59
+
60
+    setSubmitting(true);
61
+    putTaIssueApply(data.applyId, data).then(() => {
62
+      Taro.navigateBack({delta: 1});
63
+      setSubmitting(false);
64
+    }).catch(() => {
65
+      setSubmitting(false);
66
+    })
67
+  }
68
+  
69
+  React.useEffect(() => {
70
+    if (id) {
71
+      setLoading(true);
72
+      getTaIssueApplyById(id).then(res => {
73
+        setApplyInfo(res);
74
+        setLoading(false);
75
+      }).catch(() => {
76
+        setLoading(false);
77
+      });
78
+    }
79
+  }, [id]);
80
+  
81
+  return (
82
+    <Page loading={loading}>
83
+      <Issue issueId={issueId} />
84
+     
85
+      <CellGroup style={{marginTop: '20px'}}>
86
+        {
87
+          applyInfo.applyType == 'delay' && (
88
+            <Cell
89
+              title="延期日期"
90
+              value={applyInfo.content}
91
+            />
92
+          )
93
+        }
94
+        <Cell title="申请说明" />
95
+        <Field
96
+          readonly
97
+          type="textarea"
98
+          autosize={{ minHeight: '120px' }}
99
+          value={applyInfo.remark}
100
+        />
101
+      </CellGroup>
102
+     
103
+      <CellGroup style={{marginTop: '20px'}}>
104
+        <Cell
105
+          title="审批结果"
106
+        >
107
+          <RadioGroup
108
+            direction="horizontal"
109
+            value={formData.verifyStatus}
110
+            onChange={(e) => setFieldValue('verifyStatus', e.detail)}
111
+          >
112
+            <Radio name="pass" checkedColor="var(--main-bg-color)">通过</Radio>
113
+            <Radio name="reject" checkedColor="red">驳回</Radio>
114
+          </RadioGroup>
115
+        </Cell>
116
+        <Cell title="审批说明" />
117
+        <Field
118
+          type="textarea"
119
+          autosize={{ minHeight: '120px' }}
120
+          value={formData.verifyDesc}
121
+          onChange={(e) => setFieldValue('verifyDesc', e.detail)}
122
+        />
123
+      </CellGroup>
124
+      <View style={{marginTop: '20px', padding: 'var(--main-space)'}}>
125
+        <Button block type="primary" loading={submitting} onClick={onSubmit}>提交审批</Button>
126
+      </View>
127
+    </Page>
128
+  )
129
+}

+ 4
- 1
src/pages/home/components/Head.jsx Wyświetl plik

@@ -2,6 +2,7 @@ import React from 'react';
2 2
 import { View, Image } from '@tarojs/components';
3 3
 import { ROLES, ROLE_CITIZEN } from '@/utils/user';
4 4
 import logo from '@/assets/image/logo.png';
5
+import avatar from '@/assets/icons/avatar.png';
5 6
 import style from './head.module.less';
6 7
 
7 8
 export default (props) => {
@@ -14,7 +15,9 @@ export default (props) => {
14 15
       <View className={style.profile}>
15 16
         <View style={{ letterSpacing: '2px' }}>Hi, {user.name}!</View>
16 17
         <View className={style.badge}>
17
-          <View className={style.icon}></View>
18
+          <View className={style.icon}>
19
+            <Image src={avatar} />
20
+          </View>
18 21
           <View>{role}</View>
19 22
         </View>
20 23
       </View>

+ 7
- 15
src/pages/home/components/head.module.less Wyświetl plik

@@ -27,25 +27,17 @@
27 27
       text-align: center;
28 28
 
29 29
       .icon {
30
-        top: -12px;
31
-        left: -12px;
30
+        top: -8px;
31
+        left: -8px;
32 32
         position: absolute;
33
-        width: 56px;
34
-        height: 56px;
35
-        border: 8px solid rgba(251, 239, 121, 0.6);
33
+        width: 64px;
34
+        height: 64px;
36 35
         border-radius: 50%;
37 36
         box-sizing: content-box;
38 37
 
39
-        &::before {
40
-          content: '';
41
-          position: absolute;
42
-          top: 0;
43
-          left: 0;
44
-          width: 56px;
45
-          height: 56px;
46
-          background: linear-gradient(0, #E36A00, #FFA658);
47
-          border-radius: 50%;
48
-          overflow: hidden;
38
+        image {
39
+          width: 100%;
40
+          height: 100%;
49 41
         }
50 42
       }
51 43
     }

+ 45
- 3
src/pages/home/index.jsx Wyświetl plik

@@ -2,20 +2,62 @@ import React from 'react'
2 2
 import { View, Text, Image } from '@tarojs/components'
3 3
 import Page from '@/layouts/index';
4 4
 import { useModel } from '@/store';
5
+import MenuIcon from '@/components/MenuIcon';
6
+import { ROLE_INSPECTOR, ROLE_MANAGER, ROLE_ORG_USER } from '@/utils/user';
5 7
 import Head from './components/Head';
6 8
 import BannerCard from './components/BannerCard';
7
-import MenuCard from './components/MenuCard';
8 9
 import './index.less';
9 10
 
11
+const menus = {
12
+  [ROLE_INSPECTOR]: [
13
+    { icon: 'icon9', text: '我的上报', link: '/pages/issue/list/index' },
14
+    { icon: 'icon11', text: '消息通知', link: '' },
15
+    { icon: 'icon10', text: '模拟测评', link: '' },
16
+  ],
17
+
18
+  [ROLE_MANAGER]: [
19
+    { icon: 'icon1', text: '待 交 办', link: '/pages/issue/list2/index?title=待交办&bizStatus=start' },
20
+    { icon: 'icon2', text: '已 交 办', link: '/pages/issue/list2/index?title=已交办&bizStatus=assigned' },
21
+    { icon: 'icon3', text: '已 办 结', link: '/pages/issue/list2/index?title=已办结报&bizStatus=end' },
22
+    { icon: 'icon4', text: '消单申请', link: '/pages/apply/list/index?title=消单申请&applyType=end' },
23
+    { icon: 'icon5', text: '逾期警告', link: '/pages/issue/list2/index?title=逾期警告&bizStatus=expired' },
24
+    { icon: 'icon6', text: '延期申请', link: '/pages/apply/list/index?title=延期申请&applyType=delay' },
25
+    { icon: 'icon7', text: '统计查询', link: '' },
26
+    { icon: 'icon9', text: '我的上报', link: '/pages/issue/list2/index?title=我的上报&mine=true' },
27
+    { icon: 'icon11', text: '消息通知', link: '' },
28
+  ],
29
+
30
+  [ROLE_ORG_USER]: [
31
+    { icon: 'icon2', text: '处 理 中', link: '/pages/org/issue/list/index?title=处理中&bizStatus=assigned' },
32
+    { icon: 'icon3', text: '已 办 结', link: '/pages/org/issue/list/index?title=已办结报&bizStatus=end' },
33
+    { icon: 'icon5', text: '已 逾 期', link: '' },
34
+    { icon: 'icon7', text: '统计查询', link: '' },
35
+    { icon: 'icon11', text: '消息通知', link: '/pages/message/list/index' },
36
+  ]
37
+}
38
+
10 39
 export default (props) => {
11 40
 
12 41
   const { user } = useModel('user');
13 42
 
43
+  const menuArr = React.useMemo(() => {
44
+    if (!user || !user.dutyList) return [];
45
+
46
+    return menus[user.dutyList[0]];
47
+  }, [user])
48
+
14 49
   return (
15
-    <Page tabBar="home" className='index'>
50
+    <Page tabBar="home" className='home-page'>
16 51
       <Head user={user} />
17 52
       <BannerCard />
18
-      <MenuCard />
53
+      
54
+      <View className="menu-icons">
55
+        {
56
+          menuArr.map(x => (
57
+            <MenuIcon key={x.text} {...x} />
58
+          ))
59
+        }
60
+      </View>
19 61
     </Page>
20 62
   );
21 63
 }

+ 16
- 0
src/pages/home/index.less Wyświetl plik

@@ -0,0 +1,16 @@
1
+
2
+.home-page {
3
+  .menu-icons {
4
+    display: grid;
5
+    grid-template-columns: repeat(3, 1fr);
6
+    grid-column-gap: var(--main-space);
7
+    grid-row-gap: var(--main-space);
8
+    padding: var(--main-space);
9
+
10
+    & > view {
11
+      background: #FFFFFF;
12
+      box-shadow: 0px 0px 20px 0px rgba(0,0,0,0.06);
13
+      border-radius: 8px;
14
+    }
15
+  }
16
+}

+ 0
- 156
src/pages/issue/add/index.jsx Wyświetl plik

@@ -1,156 +0,0 @@
1
-import React from 'react';
2
-import Taro from '@tarojs/taro';
3
-import { View } from '@tarojs/components';
4
-import { Button, Notify, Field, Cell, CellGroup } from '@antmjs/vantui';
5
-import Page from '@/layouts/index';
6
-import LocType from '@/components/locType';
7
-import IssueType from '@/components/issueType';
8
-import Map from '@/components/map';
9
-import Uploader from '@/components/Uploader/index';
10
-import { getLocation } from '@/utils/authorize';
11
-import { ROLE_INSPECTOR } from '@/utils/user';
12
-import mapIcon from '@/assets/icons/marker.png';
13
-import { postTaIssue } from '@/services/taissue';
14
-import { warn } from '@/utils/message';
15
-
16
-export default (props) => {
17
-
18
-  const [formData, setFormData] = React.useState({
19
-    typeId: undefined,
20
-    typeName: undefined,
21
-    locId: undefined,
22
-    locName: undefined,
23
-    location: undefined,
24
-    addr: undefined,
25
-    content: undefined,
26
-    attachList: [],
27
-  });
28
-  const [showLocType, setShowLocType] = React.useState(false);
29
-  const [showIssueType, setShowIssueType] = React.useState(false);
30
-  const [loading, setLoading] = React.useState(false);
31
-
32
-  const onLocTypeChange = (_, it) => {
33
-    setFormData({
34
-      ...formData,
35
-      locId: it.typeId,
36
-      locName: it.name,
37
-    });
38
-    setShowLocType(false);
39
-  }
40
-
41
-  const onIssueTypeChange = (_, it) => {
42
-    setFormData({
43
-      ...formData,
44
-      typeId: it.typeId,
45
-      typeName: it.name,
46
-    });
47
-    setShowIssueType(false);
48
-  }
49
-
50
-  const onFieldChange = (field, value) => {
51
-    setFormData({
52
-      ...formData,
53
-      [field]: value,
54
-    })
55
-  }
56
-
57
-  const onSubmit = () => {
58
-    try {
59
-      warn(!formData.addr, '请填写地址')
60
-      warn(!formData.locId, '请选择点位')
61
-      warn(!formData.content, '请填写问题描述')
62
-      warn(!formData.typeId, '请选择问题分类')
63
-      warn(!formData.attachList || formData.attachList.length < 1, '请上传照片')
64
-    } catch (e) {
65
-      return;
66
-    }
67
-
68
-    setLoading(true)
69
-    postTaIssue(formData).then(() => {
70
-      setLoading(false);
71
-      Taro.navigateBack({delta: 1});
72
-    }).catch(() => {
73
-      setLoading(false);
74
-    })
75
-  }
76
-  
77
-  React.useMemo(() => {
78
-    getLocation().then((res) => {
79
-      onFieldChange('location', `${res.longitude},${res.latitude}`);
80
-    }).catch((err) => {
81
-      Notify.show({
82
-        message: '获取位置失败, 请退出重试',
83
-        type: 'warning',
84
-      })
85
-    });
86
-  }, []);
87
-
88
-  return (
89
-    <Page roles={[ROLE_INSPECTOR]}>
90
-      <LocType
91
-        show={showLocType}
92
-        value={formData.addr}
93
-        onCancel={() => setShowLocType(false)}
94
-        onChange={onLocTypeChange}
95
-      />
96
-
97
-      <IssueType
98
-        show={showIssueType}
99
-        value={formData.typeName}
100
-        onCancel={() => setShowIssueType(false)}
101
-        onChange={onIssueTypeChange}
102
-      />
103
-
104
-      <Map location={formData.location} />
105
-
106
-      <Field
107
-        value={formData.addr}
108
-        leftIcon={mapIcon}
109
-        placeholder="请输入地址"
110
-        onChange={e => onFieldChange('addr', e.detail)}
111
-      />
112
-
113
-      <CellGroup style={{marginTop: '20px'}}>
114
-        <Cell
115
-          isLink
116
-          title="点位"
117
-          value={formData.locName}
118
-          onClick={() => setShowLocType(true)}
119
-        />
120
-
121
-        <Cell title="问题描述" border={false} />
122
-
123
-        <Field
124
-          type="textarea"
125
-          placeholder="请输入问题描述"
126
-          autosize={{ minHeight: '120px' }}
127
-          value={formData.content}
128
-          onChange={e => onFieldChange('content', e.detail)}
129
-        />
130
-      </CellGroup>
131
-      
132
-      <Cell
133
-        isLink
134
-        title="问题分类"
135
-        style={{marginTop: '20px'}}
136
-        value={formData.typeName}
137
-        onClick={() => setShowIssueType(true)}
138
-      />
139
-
140
-      
141
-      <CellGroup style={{marginTop: '20px'}}>        
142
-        <Cell title="拍照" border={false} />
143
-
144
-        <Cell
145
-          renderTitle={
146
-            <Uploader value={formData.attachList} onChange={e => onFieldChange('attachList',e)} />
147
-          }
148
-        />
149
-      </CellGroup>
150
-
151
-      <View style={{margin: '20px auto', padding: '0 1em'}}>
152
-        <Button block type="primary" loading={loading} onClick={onSubmit}>提交</Button>
153
-      </View>
154
-    </Page>
155
-  )
156
-}

+ 266
- 0
src/pages/issue/components/Issue/index.jsx Wyświetl plik

@@ -0,0 +1,266 @@
1
+import React from 'react';
2
+import Taro from '@tarojs/taro';
3
+import { View } from '@tarojs/components';
4
+import { Button, Notify, Field, Cell, CellGroup } from '@antmjs/vantui';
5
+import LocType from '@/components/LocType';
6
+import IssueType from '@/components/IssueType';
7
+import OrgPicker from '@/components/OrgPicker';
8
+import DatePicker from '@/components/DatePicker';
9
+import Map from '@/components/map';
10
+import Uploader from '@/components/Uploader/index';
11
+import { getIssueStatus } from '@/utils/biz';
12
+import { getDateStr } from '@/utils/date';
13
+import mapIcon from '@/assets/icons/marker.png';
14
+import { getIssueProcess } from '@/services/taissue';
15
+
16
+const today = new Date();
17
+
18
+export default (props) => {
19
+  const {
20
+    issue,
21
+    readOnly,
22
+    showOrg,
23
+    showExpireDate,
24
+    renderFields,
25
+    renderAction
26
+  } = props;
27
+
28
+  const [formData, setFormData] = React.useState({
29
+    typeId: undefined,
30
+    typeName: undefined,
31
+    locId: undefined,
32
+    locName: undefined,
33
+    location: undefined,
34
+    addr: undefined,
35
+    content: undefined,
36
+    attachList: [],
37
+  });
38
+  const [showLocType, setShowLocType] = React.useState(false);
39
+  const [showIssueType, setShowIssueType] = React.useState(false);
40
+  const [showOrgPicker, setShowOrgPicker] = React.useState(false);
41
+  const [showDatePicker, setShowDatePicker] = React.useState(false);
42
+  const [bizStatus, setBizStatus] = React.useState();
43
+  const [issueProcess, setIssueProcess] = React.useState([]);
44
+  
45
+  const issueReject = React.useMemo(() => {
46
+    if (!issueProcess || !issueProcess.length) return {};
47
+
48
+    return issueProcess.filter(x => x.processStatus === 'reject')[0] || {};
49
+  }, [issueProcess]);
50
+    
51
+  const onLocTypeChange = (_, it) => {
52
+    setFormData({
53
+      ...formData,
54
+      locId: it.typeId,
55
+      locName: it.name,
56
+    });
57
+    setShowLocType(false);
58
+  }
59
+
60
+  const onIssueTypeChange = (_, it) => {
61
+    setFormData({
62
+      ...formData,
63
+      typeId: it.typeId,
64
+      typeName: it.name,
65
+    });
66
+    setShowIssueType(false);
67
+  }
68
+
69
+  const onOrgChange = (_, it) => {
70
+    setFormData({
71
+      ...formData,
72
+      orgId: it.orgId,
73
+      orgName: it.name,
74
+    });
75
+    setShowOrgPicker(false);
76
+  }
77
+
78
+  const onDateChange = (dt) => {
79
+    const date = getDateStr(dt);
80
+    setFormData({
81
+      ...formData,
82
+      expireDate: date,
83
+    });
84
+    setShowDatePicker(false);
85
+  }
86
+
87
+  const setFieldChange = (field, value) => {
88
+    setFormData({
89
+      ...formData,
90
+      [field]: value,
91
+    })
92
+  }
93
+
94
+  React.useEffect(() => {
95
+    if (issue) {
96
+      setFormData(issue);
97
+      setBizStatus(getIssueStatus(issue));
98
+      
99
+      if (issue.processStatus == 'reject') {
100
+        getIssueProcess({pageSize: 100, issueId: issue.issueId}).then(res => {
101
+          setIssueProcess(res.records || []);
102
+        })
103
+      }
104
+    }
105
+  }, [issue]);
106
+  
107
+  return (
108
+    <View>
109
+      <LocType
110
+        show={showLocType}
111
+        value={formData.addr}
112
+        onCancel={() => setShowLocType(false)}
113
+        onChange={onLocTypeChange}
114
+      />
115
+
116
+      <IssueType
117
+        show={showIssueType}
118
+        value={formData.typeName}
119
+        onCancel={() => setShowIssueType(false)}
120
+        onChange={onIssueTypeChange}
121
+      />
122
+
123
+      <OrgPicker
124
+        show={showOrgPicker}
125
+        value={formData.orgName}
126
+        onCancel={() => setShowOrgPicker(false)}
127
+        onChange={onOrgChange}
128
+      />
129
+
130
+      <DatePicker
131
+        type="date"
132
+        minDate={today}
133
+        show={showDatePicker}
134
+        value={formData.expireDate}
135
+        onCancel={() => setShowDatePicker(false)}
136
+        onChange={onDateChange}
137
+      />
138
+
139
+      <Map location={formData.location} />
140
+
141
+      <CellGroup>
142
+        <Cell
143
+          title="点位"
144
+          isLink={!readOnly}
145
+          value={formData.locName}
146
+          onClick={() => !readOnly && setShowLocType(true)}
147
+        />
148
+        <Field
149
+          placeholder="请输入地址"
150
+          value={formData.addr}
151
+          leftIcon={mapIcon}
152
+          readonly={readOnly}
153
+          onChange={e => setFieldChange('addr', e.detail)}
154
+        />
155
+      </CellGroup>
156
+
157
+      {
158
+        readOnly && bizStatus && (
159
+          <CellGroup style={{marginTop: '20px'}}>
160
+            <Cell
161
+              title="状态"
162
+              value={bizStatus.label}
163
+            />
164
+
165
+          {
166
+            issueReject.processId && (
167
+              <>
168
+                <Cell title="驳回原因" />        
169
+                <Field
170
+                  readonly
171
+                  type="textarea"
172
+                  autosize={{ minHeight: '120px' }}
173
+                  value={issueReject.processResult}
174
+                />
175
+              </>
176
+            )
177
+          }
178
+          </CellGroup>
179
+        )
180
+      }
181
+      
182
+
183
+      <CellGroup style={{marginTop: '20px'}}>
184
+        <Cell
185
+          title="问题分类"
186
+          isLink={!readOnly}
187
+          style={{marginTop: '20px'}}
188
+          value={formData.typeName}
189
+          onClick={() => !readOnly && setShowIssueType(true)}
190
+        />
191
+        <Cell title="问题描述" border={false} />
192
+
193
+        <Field
194
+          type="textarea"
195
+          placeholder="请输入问题描述"
196
+          readonly={readOnly}
197
+          autosize={{ minHeight: '120px' }}
198
+          value={formData.content}
199
+          onChange={e => setFieldChange('content', e.detail)}
200
+        />
201
+      </CellGroup>
202
+            
203
+      <CellGroup style={{marginTop: '20px'}}>        
204
+        <Cell title="拍照或视频" border={false} />
205
+
206
+        <Cell
207
+          renderTitle={
208
+            <Uploader
209
+              value={formData.attachList}
210
+              disabled={readOnly}
211
+              onChange={e => setFieldChange('attachList',e)}
212
+            />
213
+          }
214
+        />
215
+      </CellGroup>
216
+
217
+      {
218
+        (showOrg || showExpireDate) && (
219
+          <CellGroup style={{marginTop: '20px'}}>
220
+            {
221
+              showOrg && (
222
+                <Cell
223
+                  title="交办单位"
224
+                  isLink={!readOnly}
225
+                  value={formData.orgName}
226
+                  onClick={() => !readOnly && setShowOrgPicker(true)}
227
+                />
228
+              )
229
+            }
230
+            {
231
+              showExpireDate && (
232
+                <Cell
233
+                  title="办结时间"
234
+                  isLink={!readOnly}
235
+                  value={formData.expireDate}
236
+                  onClick={() => !readOnly && setShowDatePicker(true)}
237
+                />
238
+              )
239
+            }
240
+          </CellGroup>
241
+        )
242
+      }
243
+
244
+      {
245
+        renderFields ?
246
+          renderFields(
247
+            formData,
248
+            {
249
+              setFieldChange,
250
+              setFormData,
251
+              showOrgPicker,
252
+              setShowOrgPicker,
253
+            }
254
+          ) : null
255
+      }
256
+
257
+      {
258
+        renderAction ? (
259
+          <View style={{margin: '20px auto'}}>
260
+            {renderAction(formData)}
261
+          </View>
262
+        ) : null
263
+      }
264
+    </View>
265
+  )
266
+}

+ 55
- 0
src/pages/issue/edit/components/Assigned.jsx Wyświetl plik

@@ -0,0 +1,55 @@
1
+import React from 'react';
2
+import Taro from '@tarojs/taro';
3
+import { View } from '@tarojs/components';
4
+import { Button } from '@antmjs/vantui';
5
+import { postIssueAssigned, putTaIssue } from '@/services/taissue';
6
+import { warn } from '@/utils/message';
7
+
8
+export default (props) => {
9
+
10
+  const { issue, formData } = props;
11
+
12
+  const [loading1, setLoading1] = React.useState(false);
13
+  
14
+  // 交办
15
+  const onAssigned = () => {
16
+    try {
17
+      warn(!formData.addr, '请填写地址')
18
+      warn(!formData.locId, '请选择点位')
19
+      warn(!formData.content, '请填写问题描述')
20
+      warn(!formData.typeId, '请选择问题分类')
21
+      warn(!formData.attachList || formData.attachList.length < 1, '请上传照片')
22
+      warn(!formData.orgId, '请选择交办单位')
23
+      warn(!formData.expireDate, '请选择办结时间')
24
+    } catch (e) {
25
+      return;
26
+    }
27
+
28
+    setLoading1(true)
29
+
30
+    const data = { ...issue, ... formData };
31
+
32
+    // 先提交修改
33
+    putTaIssue(issue.issueId, data).then(() => {
34
+      // 再提交交办
35
+      postIssueAssigned(data).then(() => {
36
+        setLoading1(false);
37
+        Taro.navigateBack({delta: 1});
38
+      }).catch(() => {
39
+        setLoading1(false);
40
+      })
41
+    }).catch(() => {
42
+      setLoading1(false);
43
+    })
44
+  }
45
+ 
46
+  return (
47
+    <Button
48
+      block
49
+      type="primary"
50
+      loading={loading1}
51
+      onClick={onAssigned}
52
+      style={{marginTop: '20px'}}
53
+    >交办</Button>
54
+  )
55
+}

+ 98
- 0
src/pages/issue/edit/components/Cancel.jsx Wyświetl plik

@@ -0,0 +1,98 @@
1
+import React from 'react';
2
+import Taro from '@tarojs/taro';
3
+import { View } from '@tarojs/components';
4
+import { Button, Popup, CellGroup, Cell, Field } from '@antmjs/vantui';
5
+import { deleteTaIssue, postIssueRejct } from '@/services/taissue';
6
+import { warn } from '@/utils/message';
7
+
8
+export default (props) => {
9
+
10
+  const { issue } = props;
11
+
12
+  const [loading2, setLoading2] = React.useState(false);
13
+  const [loading3, setLoading3] = React.useState(false);
14
+  const [show, setShow] = React.useState(false);
15
+  const [processResult, setProcessResult] = React.useState();
16
+  
17
+  const onDelete = () => {
18
+    Taro.showModal({
19
+      title: '删除',
20
+      content: '确定进行删除操作?',
21
+      success (res) {
22
+        if (res.confirm) {
23
+          setLoading2(true)
24
+          deleteTaIssue(issue.issueId).then(() => {
25
+            setLoading2(false);
26
+            Taro.navigateBack({delta: 1});
27
+          }).catch(() => {
28
+            setLoading2(false);
29
+          })
30
+        }
31
+      }
32
+    })
33
+  }
34
+
35
+  const onReject = () => {
36
+    try {
37
+      warn(!processResult, '请填写退单理由')
38
+    } catch (e) {
39
+      return;
40
+    }
41
+
42
+    setShow(false);
43
+
44
+    const data = {
45
+      processResult,
46
+      issueId: issue.issueId,
47
+    }
48
+
49
+    setLoading3(true)
50
+    postIssueRejct(data).then(() => {
51
+      setLoading3(false);
52
+      Taro.navigateBack({delta: 1});
53
+    }).catch(() => {
54
+      setLoading3(false);
55
+    })
56
+  }
57
+  
58
+  return (
59
+    <>
60
+      <Popup position="bottom" show={show} onClose={() => setShow(false)}>
61
+        <CellGroup>
62
+          <Cell title="退单理由" />
63
+          <Field
64
+            type="textarea"
65
+            placeholder="请输入退单理由"
66
+            value={processResult}
67
+            autosize={{ minHeight: '240px' }}
68
+            onChange={e => setProcessResult(e.detail)}
69
+          />
70
+          <View style={{display: 'flex', justifyContent: 'space-around'}}>
71
+            <Button plain type="default" onClick={() => setShow(false)}>取消</Button>
72
+            <Button plain type="primary" onClick={onReject}>确定</Button>
73
+          </View>
74
+        </CellGroup>
75
+      </Popup>
76
+      <View style={{marginTop: '20px', display: 'flex'}}>
77
+        <Button
78
+          plain
79
+          hairline
80
+          type="danger"
81
+          loading={loading2}
82
+          disabled={loading3}
83
+          style={{ flex: 1 }}
84
+          onClick={onDelete}
85
+        >删除</Button>
86
+        <Button
87
+          plain
88
+          hairline
89
+          type="info"
90
+          loading={loading3}
91
+          disabled={loading2}
92
+          style={{ flex: 1, marginLeft: 'var(--main-space)' }}
93
+          onClick={() => setShow(true)}
94
+        >退单</Button>
95
+      </View>
96
+    </>
97
+  )
98
+}

+ 52
- 0
src/pages/issue/edit/components/Edit.jsx Wyświetl plik

@@ -0,0 +1,52 @@
1
+import React from 'react';
2
+import Taro from '@tarojs/taro';
3
+import { View } from '@tarojs/components';
4
+import { Button } from '@antmjs/vantui';
5
+import { putTaIssue } from '@/services/taissue';
6
+import { warn } from '@/utils/message';
7
+
8
+export default (props) => {
9
+
10
+  const { issue, formData } = props;
11
+
12
+  const [loading, setLoading] = React.useState(false);
13
+  
14
+  // 新增问题单
15
+  const onSubmit = () => {
16
+    try {
17
+      warn(!formData.addr, '请填写地址')
18
+      warn(!formData.locId, '请选择点位')
19
+      warn(!formData.content, '请填写问题描述')
20
+      warn(!formData.typeId, '请选择问题分类')
21
+      warn(!formData.attachList || formData.attachList.length < 1, '请上传照片')
22
+    } catch (e) {
23
+      return;
24
+    }
25
+
26
+    setLoading(true)
27
+    putTaIssue(issue.issueId, { ...issue, formData }).then(() => {
28
+      setLoading(false);
29
+      Taro.navigateBack({
30
+        delta: 1,
31
+        fail: () => {
32
+          Taro.reLaunch({
33
+            url: 'pages/home/index'
34
+          })
35
+        } 
36
+      });
37
+    }).catch(() => {
38
+      setLoading(false);
39
+    })
40
+  }
41
+  
42
+  return (
43
+    <Button
44
+      block
45
+      plain
46
+      hairline
47
+      type="primary"
48
+      loading={loading}
49
+      onClick={onSubmit}
50
+    >修改</Button>
51
+  )
52
+}

+ 45
- 0
src/pages/issue/edit/components/Save.jsx Wyświetl plik

@@ -0,0 +1,45 @@
1
+import React from 'react';
2
+import Taro from '@tarojs/taro';
3
+import { View } from '@tarojs/components';
4
+import { Button } from '@antmjs/vantui';
5
+import { postTaIssue } from '@/services/taissue';
6
+import { warn } from '@/utils/message';
7
+
8
+export default (props) => {
9
+
10
+  const { formData } = props;
11
+
12
+  const [loading, setLoading] = React.useState(false);
13
+  
14
+  // 新增问题单
15
+  const onSubmit = () => {
16
+    try {
17
+      warn(!formData.addr, '请填写地址')
18
+      warn(!formData.locId, '请选择点位')
19
+      warn(!formData.content, '请填写问题描述')
20
+      warn(!formData.typeId, '请选择问题分类')
21
+      warn(!formData.attachList || formData.attachList.length < 1, '请上传照片')
22
+    } catch (e) {
23
+      return;
24
+    }
25
+
26
+    setLoading(true)
27
+    postTaIssue(formData).then(() => {
28
+      setLoading(false);
29
+      Taro.navigateBack({
30
+        delta: 1,
31
+        fail: () => {
32
+          Taro.reLaunch({
33
+            url: '/pages/home/index'
34
+          });
35
+        }
36
+      });
37
+    }).catch(() => {
38
+      setLoading(false);
39
+    })
40
+  }
41
+  
42
+  return (
43
+    <Button block type="primary" loading={loading} onClick={onSubmit}>提交</Button>
44
+  )
45
+}

src/pages/issue/add/index.config.js → src/pages/issue/edit/index.config.js Wyświetl plik

@@ -1,3 +1,4 @@
1
+// eslint-disable-next-line no-undef
1 2
 export default definePageConfig({
2 3
   navigationBarTitleText: '我要上报'
3 4
 })

+ 95
- 0
src/pages/issue/edit/index.jsx Wyświetl plik

@@ -0,0 +1,95 @@
1
+import React from 'react';
2
+import Taro from '@tarojs/taro';
3
+import { View } from '@tarojs/components';
4
+import { CellGroup, Cell } from '@antmjs/vantui';
5
+import Page from '@/layouts/index';
6
+import { useModel } from '@/store';
7
+import { ROLE_INSPECTOR, ROLE_MANAGER } from '@/utils/user';
8
+import { getTaIssueById } from '@/services/taissue';
9
+import IssueForm from '../components/Issue';
10
+import Save from './components/Save';
11
+import Edit from './components/Edit';
12
+import Assigned from './components/Assigned';
13
+import Cancel from './components/Cancel';
14
+
15
+export default (props) => {
16
+
17
+  const router = Taro.useRouter();
18
+  const { id, act } = router.params;
19
+
20
+  const { user } = useModel('user');
21
+
22
+  const [loading, setLoading] = React.useState(false);
23
+  const [issue, setIssue] = React.useState();
24
+
25
+  // 各按钮状态
26
+  const [
27
+    readOnly,
28
+    canEdit,
29
+    canAssigned,
30
+    canCancel,
31
+  ] = React.useMemo(() => {
32
+    if (!issue) return [];
33
+    if (!user) return [];
34
+
35
+    // 如果是督察员
36
+    if (user.dutyList.indexOf(ROLE_INSPECTOR) > -1) {
37
+      return [
38
+        issue.processNode != 'start' && issue.processStatus != 'reject',
39
+        issue.processNode == 'start' || issue.processStatus == 'reject',
40
+        false,
41
+        false,
42
+      ]
43
+    }
44
+
45
+    // 如果是管理员
46
+    if (user?.dutyList.indexOf(ROLE_MANAGER) > -1) {
47
+      return [
48
+        issue.processNode != 'start',
49
+        false,
50
+        issue.processNode == 'start',
51
+        issue.processNode == 'start',
52
+      ]
53
+    }
54
+
55
+    return [];
56
+  }, [issue, user]);
57
+
58
+  const onIssueChange = (val = {}) => {
59
+    setIssue({
60
+      ...(issue || {}),
61
+      ...val,
62
+    })
63
+  }
64
+  
65
+  React.useEffect(() => {
66
+    if (id) {
67
+      setLoading(true)
68
+      getTaIssueById(id).then(res => {
69
+        setLoading(false);
70
+        setIssue(res);
71
+      }).catch(() => {
72
+        setLoading(false);
73
+      });
74
+    }
75
+  }, [id]);
76
+
77
+  return (
78
+    <Page roles={[ROLE_INSPECTOR, ROLE_MANAGER]} loading={loading}>
79
+      <IssueForm
80
+        issue={issue}
81
+        readOnly={readOnly}
82
+        showOrg={canAssigned}
83
+        showExpireDate={canAssigned}
84
+        renderAction={(formData) => (
85
+          <View style={{ padding: 'var(--main-space)', background: '#fff' }}>
86
+            {!id && <Save formData={formData} />}
87
+            {canEdit && <Edit issue={issue} formData={formData} />}
88
+            {canAssigned && <Assigned issue={issue} formData={formData} />}
89
+            {canCancel && <Cancel issue={issue} />}
90
+          </View>
91
+        )}
92
+      />
93
+    </Page>
94
+  )
95
+}

+ 4
- 0
src/pages/issue/list/index.config.js Wyświetl plik

@@ -0,0 +1,4 @@
1
+// eslint-disable-next-line no-undef
2
+export default definePageConfig({
3
+  navigationBarTitleText: '我的上报'
4
+})

+ 82
- 0
src/pages/issue/list/index.jsx Wyświetl plik

@@ -0,0 +1,82 @@
1
+import React from 'react';
2
+import Taro from '@tarojs/taro';
3
+import { View } from '@tarojs/components';
4
+import { Tab, Tabs } from '@antmjs/vantui';
5
+import Page from '@/layouts/index';
6
+import PowerList from '@/components/PowerList';
7
+import Card from '@/components/IssueCard';
8
+import { getTaIssue } from '@/services/taissue';
9
+import { ROLE_INSPECTOR, ROLE_MANAGER } from '@/utils/user';
10
+import { useModel } from '@/store';
11
+import { getIssueStatus } from '@/utils/biz';
12
+
13
+// 只有督察员能看到当前页面
14
+export default (props) => {
15
+  // const { user } = useModel('user');
16
+
17
+  const onClick = (item) => {
18
+    Taro.navigateTo({
19
+      url: `/pages/issue/edit/index?id=${item.issueId}`
20
+    })
21
+  }
22
+  
23
+  return (
24
+    <Page roles={[ROLE_INSPECTOR]}>
25
+      <Tabs sticky>
26
+        <Tab title="全部">
27
+          <PowerList
28
+            request={getTaIssue}
29
+            renderItem={(item) => (
30
+              <Card key={item.issueId} detail={item} onClick={() => onClick(item)} />
31
+            )}
32
+          />
33
+        </Tab>
34
+        <Tab title="未交办">
35
+          <PowerList
36
+            request={getTaIssue}
37
+            params={{bizStatus: 'start'}}
38
+            renderItem={(item) => (
39
+              <Card key={item.issueId} detail={item} onClick={() => onClick(item)} />
40
+            )}
41
+          />
42
+        </Tab>
43
+        <Tab title="已交办">
44
+          <PowerList
45
+            request={getTaIssue}
46
+            params={{bizStatus: 'assigned'}}
47
+            renderItem={(item) => (
48
+              <Card key={item.issueId} detail={item} onClick={() => onClick(item)} />
49
+            )}
50
+          />
51
+        </Tab>
52
+        <Tab title="已办结">
53
+          <PowerList
54
+            request={getTaIssue}
55
+            params={{bizStatus: 'end'}}
56
+            renderItem={(item) => (
57
+              <Card key={item.issueId} detail={item} onClick={() => onClick(item)} />
58
+            )}
59
+          />
60
+        </Tab>
61
+        <Tab title="已逾期">
62
+          <PowerList
63
+            request={getTaIssue}
64
+            params={{bizStatus: 'expired'}}
65
+            renderItem={(item) => (
66
+              <Card key={item.issueId} detail={item} onClick={() => onClick(item)} />
67
+            )}
68
+          />
69
+        </Tab>
70
+        <Tab title="已打回">
71
+          <PowerList
72
+            request={getTaIssue}
73
+            params={{bizStatus: 'reject'}}
74
+            renderItem={(item) => (
75
+              <Card key={item.issueId} detail={item} onClick={() => onClick(item)} />
76
+            )}
77
+          />
78
+        </Tab>
79
+      </Tabs>
80
+    </Page>
81
+  )
82
+}

+ 4
- 0
src/pages/issue/list2/index.config.js Wyświetl plik

@@ -0,0 +1,4 @@
1
+// eslint-disable-next-line no-undef
2
+export default definePageConfig({
3
+  navigationBarTitleText: '问题列表'
4
+})

+ 72
- 0
src/pages/issue/list2/index.jsx Wyświetl plik

@@ -0,0 +1,72 @@
1
+import React from 'react';
2
+import Taro from '@tarojs/taro';
3
+import { View } from '@tarojs/components';
4
+import { Tab, Tabs } from '@antmjs/vantui';
5
+import Page from '@/layouts/index';
6
+import PowerList from '@/components/PowerList';
7
+import Card from '@/components/IssueCard';
8
+import { getTaIssue } from '@/services/taissue';
9
+import { ROLE_MANAGER } from '@/utils/user';
10
+import { useModel } from '@/store';
11
+import { getIssueStatus } from '@/utils/biz';
12
+
13
+export default (props) => {
14
+
15
+  const router = Taro.useRouter();
16
+  const { title, bizStatus = '', mine = '' } = router.params;
17
+
18
+  React.useMemo(() => {
19
+    if (title) {
20
+      Taro.setNavigationBarTitle({ title });
21
+    } else {
22
+      Taro.setNavigationBarTitle({ title: '问题列表' });
23
+    }
24
+  }, [title]);
25
+
26
+  const { user } = useModel('user');
27
+
28
+  const onClick = (item) => {
29
+
30
+    let act = '';
31
+    const status = getIssueStatus(item);
32
+
33
+    // 如果是管理员
34
+    if (user.dutyList.indexOf(ROLE_MANAGER) > -1) {
35
+      // 如果是待交办
36
+      if (status.value === 0) {
37
+        act = 'assigned';
38
+      }
39
+    } else {
40
+      
41
+    }
42
+
43
+    Taro.navigateTo({
44
+      url: `/pages/issue/edit/index?id=${item.issueId}&act=${act}`
45
+    })
46
+  }
47
+  
48
+  return (
49
+    <Page>
50
+      <Tabs sticky>
51
+        <Tab title="监督员">
52
+          <PowerList
53
+            request={getTaIssue}
54
+            params={{bizStatus, mine, sourceType: 'inspector'}}
55
+            renderItem={(item) => (
56
+              <Card key={item.issueId} detail={item} onClick={() => onClick(item)} />
57
+            )}
58
+          />
59
+        </Tab>
60
+        <Tab title="市民">
61
+          <PowerList
62
+            request={getTaIssue}
63
+            params={{bizStatus, mine, sourceType: 'feedback'}}
64
+            renderItem={(item) => (
65
+              <Card key={item.issueId} detail={item} onClick={() => onClick(item)} />
66
+            )}
67
+          />
68
+        </Tab>
69
+      </Tabs>
70
+    </Page>
71
+  )
72
+}

+ 9
- 1
src/pages/login/components/Form.jsx Wyświetl plik

@@ -14,6 +14,7 @@ export default (props) => {
14 14
   const form = Form.useForm();
15 15
   const [accErr, setAccErr] = React.useState();
16 16
   const [pwdErr, setPwdErr] = React.useState();
17
+  const [loading, setLoading] = React.useState(false);
17 18
 
18 19
   const onFinish = (errs, res) => {
19 20
     if (!res.account) {
@@ -29,7 +30,13 @@ export default (props) => {
29 30
       setPwdErr();
30 31
     }
31 32
 
32
-    signin(res).then(onSuccess);
33
+    setLoading(true);
34
+    signin(res).then(() => {
35
+      onSuccess();
36
+      setLoading(false);
37
+    }).catch(err => {
38
+      setLoading(false);
39
+    })
33 40
   }
34 41
 
35 42
   const onForgetPwd = () => {
@@ -73,6 +80,7 @@ export default (props) => {
73 80
             block
74 81
             type="primary"
75 82
             formType="submit"
83
+            loading={loading}
76 84
           >
77 85
             登录
78 86
           </Button>

+ 1
- 5
src/pages/login/index.jsx Wyświetl plik

@@ -9,10 +9,6 @@ import './index.less'
9 9
 
10 10
 export default (props) => {
11 11
 
12
-  React.useMemo(() => {  
13
-    Taro.hideTabBar();
14
-  }, []);
15
-
16 12
   const onSuccess = () => {
17 13
     Taro.navigateBack({
18 14
       delta: 1,
@@ -25,7 +21,7 @@ export default (props) => {
25 21
   }
26 22
 
27 23
   return (
28
-    <Page className="index">
24
+    <Page>
29 25
       <View className="login-box">
30 26
         <Head />
31 27
         <Form onSuccess={onSuccess} />

+ 46
- 0
src/pages/message/list/components/Card.jsx Wyświetl plik

@@ -0,0 +1,46 @@
1
+import React from 'react';
2
+import Taro from '@tarojs/taro';
3
+import { View } from '@tarojs/components';
4
+import { Cell } from '@antmjs/vantui';
5
+import { getDtStr } from '@/utils/date';
6
+import style from './card.module.less';
7
+
8
+export default (props) => {
9
+  const { item } = props;
10
+
11
+  const url = React.useMemo(() => {
12
+    if (item.targetType == 'issue') {
13
+
14
+    } else if (item.targetType == 'apply') {
15
+
16
+    }
17
+
18
+    return '';
19
+  }, [item])
20
+  
21
+  return (
22
+    <View className={style['msg-card-wrapper']}>
23
+      <Cell
24
+        isLink
25
+        url={url}
26
+        renderTitle={(
27
+          <View style={{ display: 'flex', justifyContent: 'space-between' }}>
28
+            <View>
29
+              {item?.title}
30
+            </View>
31
+            <View>
32
+              {getDtStr(item?.createDate)}
33
+            </View>
34
+          </View>
35
+        )}
36
+        renderLabel={(
37
+          <View style={{ width: '100%' }}>
38
+            <View>
39
+              {item?.content}
40
+            </View>
41
+          </View>
42
+        )}
43
+      />
44
+    </View>
45
+  )
46
+}

+ 8
- 0
src/pages/message/list/components/card.module.less Wyświetl plik

@@ -0,0 +1,8 @@
1
+
2
+.msg-card-wrapper {
3
+  background: #FFFFFF;
4
+  box-shadow: 0px 9px 11px 0px rgba(0,0,0,0.06);
5
+  border-radius: 8px;
6
+  width: calc(100% - var(--main-space) * 2);
7
+  margin: var(--main-space);
8
+}

+ 4
- 0
src/pages/message/list/index.config.js Wyświetl plik

@@ -0,0 +1,4 @@
1
+// eslint-disable-next-line no-undef
2
+export default definePageConfig({
3
+  navigationBarTitleText: '消息列表'
4
+})

+ 22
- 0
src/pages/message/list/index.jsx Wyświetl plik

@@ -0,0 +1,22 @@
1
+import React from 'react';
2
+import Taro from '@tarojs/taro';
3
+import { View } from '@tarojs/components';
4
+import Page from '@/layouts/index';
5
+import PowerList from '@/components/PowerList';
6
+import { getTaMessage } from '@/services/tamessage';
7
+import Card from './components/Card';
8
+
9
+export default (props) => {
10
+
11
+  
12
+  return (
13
+    <Page>
14
+      <PowerList
15
+        request={getTaMessage}
16
+        renderItem={(item) => (
17
+          <Card key={item.msgId} item={item} />
18
+        )}
19
+      />
20
+    </Page>
21
+  )
22
+}

+ 38
- 0
src/pages/org/components/issue-info/index.jsx Wyświetl plik

@@ -0,0 +1,38 @@
1
+import React from 'react';
2
+import Taro from '@tarojs/taro';
3
+import { View } from '@tarojs/components';
4
+import { Cell, CellGroup, Field, Button } from '@antmjs/vantui';
5
+import Uploader from '@/components/Uploader/index';
6
+import { getDtStr } from '@/utils/date';
7
+
8
+export default (props) => {
9
+  const { issue } = props;
10
+  return (
11
+    <>
12
+      <CellGroup>
13
+        <Cell title="上报时间" value={getDtStr(issue?.createDate)} />
14
+        <Cell title="办结时间" value={issue?.expireDate} />
15
+        <Cell title="交办次数" value={issue?.processNum || 1} />
16
+      </CellGroup>
17
+      <CellGroup style={{marginTop: '20px'}}>
18
+        <Cell title="抽检位置" value={issue?.locName} />
19
+        <Cell title="定 位 点" value={issue?.addr} />
20
+        <Cell title="问题详情" />        
21
+        <Field
22
+          readonly
23
+          type="textarea"
24
+          autosize={{ minHeight: '120px' }}
25
+          value={issue?.content}
26
+        /> 
27
+        <Cell
28
+          renderTitle={
29
+            <Uploader
30
+              disabled
31
+              value={issue?.attachList}
32
+            />
33
+          }
34
+        />
35
+      </CellGroup>
36
+    </>
37
+  )
38
+}

+ 114
- 0
src/pages/org/issue/detail/components/Apply.jsx Wyświetl plik

@@ -0,0 +1,114 @@
1
+import React from 'react';
2
+import Taro from '@tarojs/taro';
3
+import { View } from '@tarojs/components';
4
+import { Cell, CellGroup, Field, Button, Popup } from '@antmjs/vantui';
5
+import DatePicker from '@/components/DatePicker';
6
+import { getDateStr } from '@/utils/date';
7
+import { postTaIssueApply } from '@/services/taissueapply';
8
+import { warn } from '@/utils/message';
9
+
10
+const today = new Date();
11
+export default (props) => {
12
+
13
+  const { orgIssue, applyType, ...leftProps } = props;
14
+
15
+  const [show, setShow] = React.useState(false);
16
+  const [submitting, setSubmitting] = React.useState(false);
17
+  const [formData, setFormData] = React.useState({});
18
+  const [showDatePicker, setShowDatePicker] = React.useState(false);
19
+
20
+  const setFieldValue = (key, value) => {
21
+    setFormData({
22
+      ...formData,
23
+      [key]: value,
24
+    })
25
+  }
26
+
27
+  const onDateClick = () => {
28
+    setShow(false);
29
+    setShowDatePicker(true);
30
+  }
31
+  
32
+  const onDateChange = (dt) => {
33
+    const date = getDateStr(dt);
34
+    setFieldValue('content', date);
35
+    setShowDatePicker(false);
36
+    setShow(true);
37
+  }
38
+
39
+  const onClick = () => {
40
+    if (applyType == 'org-verify' || applyType == 'end') {
41
+      onSubmit();
42
+    } else {
43
+      setShow(true);
44
+    }
45
+  }
46
+
47
+  const onSubmit = () => {
48
+    try {
49
+      warn(!orgIssue || !orgIssue.issueId, '未找到问题单');
50
+      if (applyType === 'delay') {
51
+        warn(!formData.content, '请选择延期时间');
52
+      }
53
+
54
+      if (applyType == 'delay' || applyType == 'reject') {
55
+        warn(!formData.remark, '请填写理由说明');
56
+      }
57
+    } catch (e) {
58
+      return ;
59
+    }
60
+
61
+    const data = {
62
+      applyType,
63
+      issueId: orgIssue.issueId,
64
+      ...formData
65
+    }
66
+
67
+    setSubmitting(true);
68
+    postTaIssueApply(data).then(() => {
69
+      Taro.navigateBack({delta: 1});
70
+      setSubmitting(false);
71
+    }).catch(() => {
72
+      setSubmitting(false);
73
+    })
74
+  }
75
+  
76
+  return (
77
+    <>
78
+      <DatePicker
79
+        type="date"
80
+        minDate={today}
81
+        show={showDatePicker}
82
+        value={formData.content}
83
+        onCancel={() => setShowDatePicker(false)}
84
+        onChange={onDateChange}
85
+      />
86
+      <Popup position="bottom" show={show} onClose={() => setShow(false)}>
87
+        <CellGroup>
88
+          {
89
+            applyType == 'delay' && (
90
+              <Cell
91
+                isLink
92
+                title="延期时间"
93
+                value={formData.content}
94
+                onClick={onDateClick}
95
+              />
96
+            )
97
+          }
98
+          <Cell title="理由说明" />
99
+          <Field
100
+            type="textarea"
101
+            autosize={{ minHeight: '120px' }}
102
+            value={formData.remark}
103
+            onChange={e => setFieldValue('remark', e.detail)}
104
+          />
105
+          <View style={{display: 'flex'}}>
106
+            <Button block plain type="default" onClick={() => setShow(false)}>取消</Button>
107
+            <Button block plain type="primary" loading={submitting} onClick={onSubmit}>确定</Button>
108
+          </View>
109
+        </CellGroup>
110
+      </Popup>
111
+      <Button {...leftProps} onClick={onClick}>{props.children}</Button>
112
+    </>
113
+  )
114
+}

+ 34
- 0
src/pages/org/issue/detail/components/Update.jsx Wyświetl plik

@@ -0,0 +1,34 @@
1
+import React from 'react';
2
+import Taro from '@tarojs/taro';
3
+import { View } from '@tarojs/components';
4
+import { Button } from '@antmjs/vantui';
5
+import { putTaOrgIssue } from '@/services/taorgissue';
6
+import { warn } from '@/utils/message';
7
+
8
+export default (props) => {
9
+  const { orgIssue } = props;
10
+  
11
+  const [submitting, setSubmitting] = React.useState(false);
12
+  
13
+  const onSubmit = () => {
14
+    try {
15
+      warn(!orgIssue || !orgIssue.issueId, '未找打问题单');
16
+      warn(!orgIssue.result, '请填写整改结果');
17
+      warn(!orgIssue.attachList || !orgIssue.attachList, '请上传照片或视频');
18
+    } catch (e) {
19
+      return ;
20
+    }
21
+
22
+    setSubmitting(true);
23
+    putTaOrgIssue(orgIssue.orgIssueId, orgIssue).then(() => {
24
+      Taro.navigateBack({delta: 1});
25
+      setSubmitting(false);
26
+    }).catch(() => {
27
+      setSubmitting(false);
28
+    })
29
+  }
30
+
31
+  return (
32
+    <Button block type="primary" loading={submitting} onClick={onSubmit}>保存修改</Button>
33
+  )
34
+}

+ 4
- 0
src/pages/org/issue/detail/index.config.js Wyświetl plik

@@ -0,0 +1,4 @@
1
+// eslint-disable-next-line no-undef
2
+export default definePageConfig({
3
+  navigationBarTitleText: '问题详情'
4
+})

+ 136
- 0
src/pages/org/issue/detail/index.jsx Wyświetl plik

@@ -0,0 +1,136 @@
1
+import React from 'react';
2
+import Taro from '@tarojs/taro';
3
+import { View } from '@tarojs/components';
4
+import { Cell, CellGroup, Field, Button } from '@antmjs/vantui';
5
+import Page from '@/layouts/index';
6
+import Uploader from '@/components/Uploader/index';
7
+import { getTaIssueById } from '@/services/taissue';
8
+import { getTaOrgIssueByIssueId } from '@/services/taorgissue';
9
+
10
+import IssueInfo from '../../components/issue-info';
11
+import Apply from './components/Apply';
12
+
13
+const status = {
14
+  delay: '延期申请中',
15
+  reject: '驳回申请中',
16
+  end: '办结申请中',
17
+}
18
+
19
+export default (props) => {
20
+
21
+  const router = Taro.useRouter();
22
+  const { id, act } = router.params;
23
+
24
+  const [loading, setLoading] = React.useState(false);
25
+  const [issue, setIssue] = React.useState();
26
+  const [orgIssue, setOrgIssue] = React.useState({});
27
+
28
+  const [
29
+    readOnly,
30
+    statusTxt,
31
+    showEditBtn,
32
+  ] = React.useMemo(() => {
33
+    if (!orgIssue.issueId) return [];
34
+
35
+    return [
36
+      issue?.applyId,
37
+      status[orgIssue.result],
38
+      orgIssue.processStatus == 'start' && !issue?.applyId
39
+    ]
40
+
41
+  }, [issue, orgIssue.issueId]);
42
+
43
+  const setFormData = (key, value) => {
44
+    setOrgIssue({
45
+      ...(orgIssue || {}),
46
+      [key]: value,
47
+    })
48
+  }
49
+
50
+  React.useEffect(() => {
51
+    if (id) {
52
+      setLoading(true)
53
+      getTaIssueById(id).then(res => {
54
+        setLoading(false);
55
+        setIssue(res);
56
+      }).catch(() => {
57
+        setLoading(false);
58
+      });
59
+
60
+      getTaOrgIssueByIssueId(id).then(res => setOrgIssue(res));
61
+    }
62
+  }, [id]);
63
+  
64
+  return (
65
+    <Page loading={loading}>
66
+      <IssueInfo issue={issue} />
67
+      <CellGroup style={{marginTop: '20px'}}>
68
+        <Cell title="整改结果" />
69
+        <Field
70
+          type="textarea"
71
+          readonly={readOnly}
72
+          autosize={{ minHeight: '120px' }}
73
+          value={orgIssue.result}
74
+          onChange={e => setFormData('result', e.detail)}
75
+        />
76
+      </CellGroup>
77
+      <CellGroup style={{marginTop: '20px'}}>        
78
+        <Cell title="拍照或视频" border={false} />
79
+
80
+        <Cell
81
+          renderTitle={
82
+            <Uploader disabled={readOnly} value={orgIssue.attachList} onChange={e => setFormData('attachList',e)} />
83
+          }
84
+        />
85
+      </CellGroup>
86
+
87
+      {
88
+        readOnly && (
89
+          <CellGroup style={{marginTop: '20px'}}>
90
+            <Cell title="状态" value={statusTxt} />
91
+          </CellGroup>
92
+        )
93
+      }
94
+
95
+      {
96
+        !readOnly && (
97
+          <View style={{marginTop: '20px', padding: 'var(--main-space)'}}>
98
+            <View>
99
+              {
100
+                showEditBtn ?
101
+                  <Button block type="primary">保存修改</Button> :
102
+                  <Apply block type="primary" applyType="end" orgIssue={orgIssue}>申请办结</Apply>
103
+              }
104
+            </View>
105
+            {
106
+              !showEditBtn && (
107
+                <View style={{marginTop: '20px'}}>
108
+                  <Apply block type="primary" applyType="org-verify" orgIssue={orgIssue}>申请审核</Apply>
109
+                </View>
110
+              )
111
+            }
112
+            <View style={{marginTop: '20px', display: 'flex' }}>
113
+              <Apply
114
+                block
115
+                plain
116
+                hairline
117
+                type="info"
118
+                applyType="delay"
119
+                orgIssue={orgIssue}
120
+              >申请延期</Apply>
121
+              <Apply
122
+                block
123
+                plain
124
+                hairline
125
+                type="danger"
126
+                applyType="reject"
127
+                orgIssue={orgIssue}
128
+                style={{marginLeft: 'var(--main-space)'}}
129
+              >申请驳回</Apply>
130
+            </View>
131
+          </View>
132
+        )
133
+      }
134
+    </Page>
135
+  )
136
+}

+ 4
- 0
src/pages/org/issue/list/index.config.js Wyświetl plik

@@ -0,0 +1,4 @@
1
+// eslint-disable-next-line no-undef
2
+export default definePageConfig({
3
+  navigationBarTitleText: '问题列表'
4
+})

+ 58
- 0
src/pages/org/issue/list/index.jsx Wyświetl plik

@@ -0,0 +1,58 @@
1
+import React from 'react';
2
+import Taro from '@tarojs/taro';
3
+import { View } from '@tarojs/components';
4
+import { Tab, Tabs } from '@antmjs/vantui';
5
+import Page from '@/layouts/index';
6
+import PowerList from '@/components/PowerList';
7
+import Card from '@/components/IssueCard';
8
+import { getTaOrgIssue } from '@/services/taorgissue';
9
+import { ROLE_MANAGER } from '@/utils/user';
10
+import { useModel } from '@/store';
11
+import { getIssueStatus } from '@/utils/biz';
12
+
13
+export default (props) => {
14
+
15
+  const router = Taro.useRouter();
16
+  const { title, bizStatus, color } = router.params;
17
+
18
+  React.useMemo(() => {
19
+    if (title) {
20
+      Taro.setNavigationBarTitle({ title });
21
+    } else {
22
+      Taro.setNavigationBarTitle({ title: '问题列表' });
23
+    }
24
+  }, [title]);
25
+
26
+  // const { user } = useModel('user');
27
+
28
+  const onClick = (item) => {
29
+    Taro.navigateTo({
30
+      url: `/pages/org/issue/detail/index?id=${item.issueId}`
31
+    })
32
+  }
33
+  
34
+  return (
35
+    <Page>
36
+      <Tabs sticky>
37
+        <Tab title="监督员">
38
+          <PowerList
39
+            request={getTaOrgIssue}
40
+            params={{bizStatus, sourceType: 'inspector'}}
41
+            renderItem={(item) => (
42
+              <Card key={item.issueId} detail={item} stText={title} color={color} onClick={() => onClick(item)} />
43
+            )}
44
+          />
45
+        </Tab>
46
+        <Tab title="市民">
47
+          <PowerList
48
+            request={getTaOrgIssue}
49
+            params={{bizStatus, sourceType: 'feedback'}}
50
+            renderItem={(item) => (
51
+              <Card key={item.issueId} detail={item} stText={title} color={color} onClick={() => onClick(item)} />
52
+            )}
53
+          />
54
+        </Tab>
55
+      </Tabs>
56
+    </Page>
57
+  )
58
+}

+ 30
- 0
src/services/sysorg.js Wyświetl plik

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

+ 15
- 0
src/services/taissue.js Wyświetl plik

@@ -24,3 +24,18 @@ export const putTaIssue = (id, data) => request(`/api/taIssue/${id}`, { data, me
24 24
  * 通过主键删除数据
25 25
  */
26 26
 export const deleteTaIssue = (id) => request(`/api/taIssue/${id}`, { method: 'delete' });
27
+
28
+/*
29
+ * 交办
30
+ */
31
+export const postIssueAssigned = (data) => request('/api/taIssueProcess/assigned', { data, method: 'post' });
32
+
33
+/*
34
+ * 驳回
35
+ */
36
+export const postIssueRejct = (data) => request('/api/taIssueProcess/reject', { data, method: 'post' });
37
+
38
+/*
39
+ * 获取所有流程节点
40
+ */
41
+export const getIssueProcess = (params) => request('/api/taIssueProcess', { params });

+ 29
- 0
src/services/taissueapply.js Wyświetl plik

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

+ 29
- 0
src/services/tamessage.js Wyświetl plik

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

+ 30
- 0
src/services/taorgissue.js Wyświetl plik

@@ -0,0 +1,30 @@
1
+import request from '@/utils/request';
2
+
3
+/*
4
+ * 分页查询
5
+ */
6
+export const getTaOrgIssue = (params) => request('/api/taOrgIssue', { params });
7
+
8
+/*
9
+ * 新增数据
10
+ */
11
+export const postTaOrgIssue = (data) => request('/api/taOrgIssue', { data, method: 'post' });
12
+
13
+/*
14
+ * 通过ID查询单条数据
15
+ */
16
+export const getTaOrgIssueById = (id) => request(`/api/taOrgIssue/${id}`);
17
+/*
18
+ * 通过ISSUE_ID查询单条数据
19
+ */
20
+export const getTaOrgIssueByIssueId = (issueId) => request(`/api/taIssue/${issueId}/orgIssue`);
21
+
22
+/*
23
+ * 更新数据
24
+ */
25
+export const putTaOrgIssue = (id, data) => request(`/api/taOrgIssue/${id}`, { data, method: 'put' });
26
+
27
+/*
28
+ * 通过主键删除数据
29
+ */
30
+export const deleteTaOrgIssue = (id) => request(`/api/taOrgIssue/${id}`, { method: 'delete' });

+ 32
- 0
src/utils/biz.js Wyświetl plik

@@ -0,0 +1,32 @@
1
+import dayjs from 'dayjs';
2
+
3
+export function getIssueStatus(taIssue) {
4
+  if (taIssue.processStatus === 'reject') {
5
+    return {
6
+      value: 4,
7
+      label: '已打回',
8
+    };
9
+  } else if (taIssue.processNode === 'start') {
10
+    return {
11
+      value: 0,
12
+      label: '未交办',
13
+    }
14
+  } else if (taIssue.processNode === 'end') {
15
+    return {
16
+      value: 2,
17
+      label: '已办结',
18
+    };
19
+  } else if (taIssue.processNode !== 'end' && taIssue.expireDate <= dayjs().format('YYYY-MM-DD')) {
20
+    return {
21
+      value: 3,
22
+      label: '已逾期',
23
+    };
24
+  } else if (taIssue.processNode === 'assigned') {
25
+    return {
26
+      value: 1,
27
+      label: '已交办',
28
+    };
29
+  }
30
+
31
+  return {};
32
+}

+ 16
- 0
src/utils/date.js Wyświetl plik

@@ -0,0 +1,16 @@
1
+
2
+function fill0(n) {
3
+  return n > 9 ? `${n}` : `0${n}`
4
+}
5
+
6
+export function getDateStr(dt) {
7
+  const y = dt.getFullYear();
8
+  const m = dt.getMonth() + 1;
9
+  const d = dt.getDate();
10
+
11
+  return `${y}-${fill0(m)}-${fill0(d)}`
12
+}
13
+
14
+export function getDtStr(dtStr) {
15
+  return !dtStr ? '' : dtStr.replace('T', ' ').substring(0, 16);
16
+}

+ 4
- 0
src/utils/user.js Wyświetl plik

@@ -8,6 +8,9 @@ export const ROLE_MANAGER = 'manager';
8 8
 // 单位人员
9 9
 export const ROLE_ORG_USER = 'org_user';
10 10
 
11
+// 单位管理员
12
+export const ROLE_ORG_MANAGER = 'org_manager';
13
+
11 14
 // 查询员
12 15
 export const ROLE_QUERY_PERSON = 'query_person';
13 16
 
@@ -19,6 +22,7 @@ export const ROLES = {
19 22
   [ROLE_INSPECTOR]: '督察员',
20 23
   [ROLE_MANAGER]: '管理员',
21 24
   [ROLE_ORG_USER]: '单位人员',
25
+  [ROLE_ORG_MANAGER]: '单位管理员',
22 26
   [ROLE_QUERY_PERSON]: '查询员',
23 27
   [ROLE_CITIZEN]: '市民',
24 28
 }