Procházet zdrojové kódy

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

fangmingyue před 2 roky
rodič
revize
4d13fd2755
51 změnil soubory, kde provedl 1403 přidání a 144 odebrání
  1. 2
    1
      .eslintrc
  2. 1
    1
      babel.config.js
  3. 2
    1
      config/dev.js
  4. 1
    0
      config/index.js
  5. 1
    0
      package.json
  6. 8
    26
      src/app.config.js
  7. 3
    1
      src/app.js
  8. 4
    3
      src/app.less
  9. binární
      src/assets/icons/close.png
  10. binární
      src/assets/icons/marker.png
  11. binární
      src/assets/icons/uploader.png
  12. 28
    0
      src/components/Auth.jsx
  13. 69
    0
      src/components/Pickerful/index.jsx
  14. 12
    0
      src/components/Pickerful/style.module.less
  15. 83
    0
      src/components/Uploader/index.jsx
  16. 64
    0
      src/components/Uploader/style.modules.less
  17. 9
    0
      src/components/issueType/index.jsx
  18. 9
    0
      src/components/locType/index.jsx
  19. 31
    0
      src/components/map/index.jsx
  20. 15
    0
      src/components/map/style.module.less
  21. 137
    0
      src/layouts/TabBar.jsx
  22. 32
    11
      src/layouts/index.jsx
  23. 14
    1
      src/layouts/layout.module.less
  24. 10
    4
      src/pages/home/components/Head.jsx
  25. 28
    8
      src/pages/home/components/head.module.less
  26. 6
    2
      src/pages/home/index.jsx
  27. 3
    0
      src/pages/issue/add/index.config.js
  28. 156
    0
      src/pages/issue/add/index.jsx
  29. 77
    24
      src/pages/login/components/Form.jsx
  30. 63
    0
      src/pages/login/components/form.less
  31. 0
    36
      src/pages/login/components/form.module.less
  32. 22
    5
      src/pages/login/index.jsx
  33. 3
    7
      src/pages/reporting/detail/components/Map.jsx
  34. 0
    5
      src/pages/reporting/detail/components/map.module.less
  35. 1
    1
      src/pages/reporting/detail/index.jsx
  36. 102
    0
      src/pages/reset-password/components/Captcha.jsx
  37. 87
    0
      src/pages/reset-password/components/ResetPwd.jsx
  38. 3
    0
      src/pages/reset-password/index.config.js
  39. 44
    0
      src/pages/reset-password/index.jsx
  40. 50
    0
      src/pages/reset-password/index.less
  41. 6
    0
      src/services/comm.js
  42. 26
    0
      src/services/taissue.js
  43. 26
    0
      src/services/tdissuetype.js
  44. 29
    0
      src/services/tdloctype.js
  45. 16
    0
      src/services/wxma.js
  46. 24
    4
      src/store/user.js
  47. 20
    1
      src/utils/authorize.js
  48. 12
    0
      src/utils/message.js
  49. 39
    1
      src/utils/request.js
  50. 24
    0
      src/utils/user.js
  51. 1
    1
      yarn.lock

+ 2
- 1
.eslintrc Zobrazit soubor

@@ -3,6 +3,7 @@
3 3
   "rules": {
4 4
     "react/jsx-uses-react": "off",
5 5
     "react/react-in-jsx-scope": "off",
6
-    "no-unused-vars": "warn"
6
+    "no-unused-vars": "warn",
7
+    "jsx-quotes": ["warn", "prefer-double"]
7 8
   }
8 9
 }

+ 1
- 1
babel.config.js Zobrazit soubor

@@ -13,7 +13,7 @@ module.exports = {
13 13
       {
14 14
         libraryName: "@antmjs/vantui",
15 15
         libraryDirectory: "es",
16
-        style: true
16
+        style: (name) => `${name}/style/less`,
17 17
       },
18 18
       "@antmjs/vantui"
19 19
     ]

+ 2
- 1
config/dev.js Zobrazit soubor

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

+ 1
- 0
config/index.js Zobrazit soubor

@@ -16,6 +16,7 @@ const config = {
16 16
   },
17 17
   copy: {
18 18
     patterns: [
19
+      { from: 'src/assets/tabbar/', to: 'dist/assets/tabbar/' },
19 20
     ],
20 21
     options: {
21 22
     }

+ 1
- 0
package.json Zobrazit soubor

@@ -48,6 +48,7 @@
48 48
     "@tarojs/taro": "3.5.7",
49 49
     "@tarojs/taro-h5": "3.5.7",
50 50
     "@zjxpcyc/react-tiny-store": "^2.0.1",
51
+    "md5": "^2.3.0",
51 52
     "react": "^18.0.0",
52 53
     "react-dom": "^18.0.0"
53 54
   },

+ 8
- 26
src/app.config.js Zobrazit soubor

@@ -1,37 +1,16 @@
1
+// eslint-disable-next-line no-undef
1 2
 export default defineAppConfig({
2 3
   pages: [
3
-    'pages/login/index',
4 4
     'pages/home/index',
5
+    'pages/login/index',
5 6
     'pages/index3/index',
6 7
     'pages/index4/index',
7 8
     'pages/index5/index',
9
+    'pages/issue/add/index',
8 10
     'pages/reporting/index',
9 11
     'pages/reporting/detail/index',
12
+    'pages/reset-password/index',
10 13
   ],
11
-  tabBar: {
12
-    list: [
13
-      {
14
-        "pagePath": "pages/login/index",
15
-        "text": "首页"
16
-      },
17
-      {
18
-        "pagePath": "pages/home/index",
19
-        "text": "公告"
20
-      },
21
-      {
22
-        "pagePath": "pages/index3/index",
23
-        "text": "发布"
24
-      },
25
-      {
26
-        "pagePath": "pages/index4/index",
27
-        "text": "测评标准"
28
-      },
29
-      {
30
-        "pagePath": "pages/index5/index",
31
-        "text": "我的"
32
-      },
33
-    ],
34
-  },
35 14
   window: {
36 15
     // backgroundColor: '#1A7565',
37 16
     backgroundTextStyle: 'light',
@@ -46,5 +25,8 @@ export default defineAppConfig({
46 25
   },
47 26
   requiredPrivateInfos: [
48 27
     'getLocation'
49
-  ]
28
+  ],
29
+  setting: {
30
+    urlCheck: false,
31
+  }
50 32
 })

+ 3
- 1
src/app.js Zobrazit soubor

@@ -10,7 +10,9 @@ const App = (props) => {
10 10
       success: (res) => {
11 11
         store.getState('user').login(res.code);
12 12
       }
13
-    })
13
+    });
14
+
15
+    store.getState('user').current();
14 16
   });
15 17
 
16 18
   return (

+ 4
- 3
src/app.less Zobrazit soubor

@@ -7,10 +7,11 @@ page {
7 7
   --main-space: 4vw;
8 8
 
9 9
   // antm-js 样式修改
10
-  // 按钮主色
11
-  --buttonPrimaryBackgroundColor: #1A7565;
10
+  // 按钮
11
+  --button-primary-color: #FFFFFF;
12
+  --button-primary-background-color: var(--main-bg-color);
12 13
   // 菊花转主色
13
-  --loading-spinner-color: #1A7565;
14
+  --loading-spinner-color: var(--main-bg-color);
14 15
 }
15 16
 
16 17
 view {

binární
src/assets/icons/close.png Zobrazit soubor


binární
src/assets/icons/marker.png Zobrazit soubor


binární
src/assets/icons/uploader.png Zobrazit soubor


+ 28
- 0
src/components/Auth.jsx Zobrazit soubor

@@ -0,0 +1,28 @@
1
+import React from 'react';
2
+import { View } from '@tarojs/components';
3
+import { useModel } from '@/store';
4
+
5
+export default (props) => {
6
+  const { roles = [] } = props;
7
+  const { user } = useModel('user');
8
+
9
+  const targets = user?.dutyList || [];
10
+
11
+  let hasRights = false;
12
+  for(let role of roles) {
13
+    if (hasRights) {
14
+      continue;
15
+    }
16
+
17
+    const found = targets.indexOf(role) > -1;
18
+    if (found) {
19
+      hasRights = found;
20
+    }
21
+  }
22
+
23
+  return !roles.length || hasRights ? props.children : (
24
+    <View style={{width: '100%', height: '100%', display: 'grid', placeItems: 'center'}}>
25
+      <View>暂无权限</View>
26
+    </View>
27
+  );
28
+}

+ 69
- 0
src/components/Pickerful/index.jsx Zobrazit soubor

@@ -0,0 +1,69 @@
1
+import React from 'react';
2
+import { View, ScrollView } from '@tarojs/components';
3
+import { Button, Popup, Search, Cell, Icon } from '@antmjs/vantui';
4
+import style from './style.module.less';
5
+
6
+export default (props) => {
7
+  const { show, value, keyValue, labelValue = 'name', dictAPI, onChange, onCancel } = props;
8
+
9
+  const [dict, setDict] = React.useState([]);
10
+  const [checked, setChecked] = React.useState();
11
+  const [searchText, setSearchText] = React.useState('')
12
+  const onSearch = () => {
13
+    // todo
14
+  }
15
+
16
+  const list = React.useMemo(() => {
17
+    if (!searchText) return dict;
18
+    return dict.filter(x => x.name.indexOf(searchText) > -1);
19
+  }, [searchText, dict]);
20
+
21
+  const onClick = () => {
22
+    if (!checked) return;
23
+    onChange(checked[keyValue], checked)
24
+  }
25
+
26
+  React.useEffect(() => {
27
+    dictAPI({pageSize: 500}).then(res => {
28
+      setDict(res.records || []);
29
+    })
30
+  }, []);
31
+
32
+  React.useEffect(() => {
33
+    if (value && dict) {
34
+      setChecked(dict.filter(x => x[keyValue] == value)[0]);
35
+    }
36
+  }, [value, dict]);
37
+  
38
+  return (
39
+    <Popup position="right" show={show} onClose={onCancel}>
40
+      <View className={style['pickerful-box']}>
41
+        <Search
42
+          onChange={(e) => setSearchText(e.detail)}
43
+          placeholder="请输入搜索关键词"
44
+          onSearch={onSearch}
45
+          clearable
46
+        />
47
+        <ScrollView scrollY style={{ flex: 1 }}>
48
+          {
49
+            list.map(item => (
50
+              <Cell
51
+                key={item[keyValue]}
52
+                title={item[labelValue]}
53
+                renderRightIcon={
54
+                  checked && checked[keyValue] == item[keyValue] ?
55
+                  <Icon name="success" color="#1A7565" /> :
56
+                  null
57
+                }
58
+                onClick={() => setChecked(item)}
59
+              />
60
+            ))
61
+          }
62
+        </ScrollView>
63
+        <View style={{ padding: '8px 2em' }}>
64
+          <Button block type="primary" disabled={!checked} onClick={onClick}>确定</Button>
65
+        </View>
66
+      </View>
67
+    </Popup>
68
+  )
69
+}

+ 12
- 0
src/components/Pickerful/style.module.less Zobrazit soubor

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

+ 83
- 0
src/components/Uploader/index.jsx Zobrazit soubor

@@ -0,0 +1,83 @@
1
+import React from 'react';
2
+import Taro from '@tarojs/taro';
3
+import { View, Image, Video } from '@tarojs/components';
4
+import { Loading } from '@antmjs/vantui';
5
+import { uploadFiles } from '@/utils/request';
6
+import icon from '@/assets/icons/uploader.png';
7
+import closeIcon from '@/assets/icons/close.png';
8
+import style from './style.modules.less';
9
+
10
+export default (props) => {
11
+  // value 为数组
12
+  // 单个元素结构为 { url, fileType }
13
+  const { value = [], onChange } = props;
14
+  const [loading, setLoading] = React.useState(false);
15
+
16
+  const onAdd = (e) => {
17
+    e.preventDefault();
18
+    e.stopPropagation();
19
+
20
+    setLoading(true);
21
+    Taro.chooseMedia({
22
+      maxDuration: 60,
23
+      sizeType: 'compressed',
24
+      success: (res) => {
25
+        const { tempFiles } = res;
26
+        uploadFiles(tempFiles).then(resp => {
27
+          setLoading(false);          
28
+          onChange(value.concat(resp));
29
+        }).catch((err) => {
30
+          console.error(err);
31
+          setLoading(false);
32
+        })
33
+      },
34
+      fail: (err) => {
35
+        console.error(err);
36
+        setLoading(false);
37
+      }
38
+    })
39
+  }
40
+
41
+  const onDelete = (item) => {
42
+    const newVal = value.filter(x => x.url != item.url);
43
+    onChange(newVal);
44
+  }
45
+
46
+  const onPreview = (inx) => {
47
+    Taro.previewMedia({
48
+      sources: value.map(x => ({ url: x.url, type: x.fileType })),
49
+      current: inx,
50
+    })
51
+  }
52
+
53
+  return (
54
+    <View className={style['uploader-wrapper']}>
55
+      <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
+        {
67
+          value.map((item, inx) => (
68
+            <View key={item.url} className={style['uploader-item']}>
69
+              {
70
+                item.fileType == 'image' ?
71
+                  <Image src={item.url} onClick={() => onPreview(inx)} /> :
72
+                  <Video src={item.url} controls={false} onClick={() => onPreview(inx)} />
73
+              }
74
+              <View onClick={() => onDelete(item)}>
75
+                <Image src={closeIcon} />
76
+              </View>
77
+            </View>
78
+          ))
79
+        }
80
+      </View>
81
+    </View>
82
+  )
83
+}

+ 64
- 0
src/components/Uploader/style.modules.less Zobrazit soubor

@@ -0,0 +1,64 @@
1
+
2
+.uploader-wrapper {
3
+  display: inline-block;
4
+  .uploader-box {
5
+    display: flex;
6
+
7
+    & > view {
8
+      flex: none;
9
+      width: 160px;
10
+      height: 160px;
11
+      position: relative;
12
+
13
+      image {
14
+        width: 100%;
15
+        height: 100%;
16
+      }
17
+
18
+      video {
19
+        width: 100%;
20
+        height: 100%;
21
+      }
22
+
23
+      & + view {
24
+        margin-left: 20px;
25
+      }
26
+    }
27
+
28
+    .uploader-add {
29
+      & > view {
30
+        position: absolute;
31
+        z-index: 2;
32
+        width: 100%;
33
+        height: 100%;
34
+        left: 0;
35
+        top: 0;
36
+
37
+        display: grid;
38
+        place-items: center;
39
+        background-color: rgba(0, 0, 0, .75);
40
+      }
41
+    }
42
+
43
+    .uploader-item {
44
+      background-color: rgba(0, 0, 0, .05);
45
+
46
+      & > view {
47
+        position: absolute;
48
+        z-index: 2;
49
+        width: 40%;
50
+        height: 40%;
51
+        top: 0;
52
+        right: 0;
53
+
54
+        & > image {
55
+          position: absolute;
56
+          width: 60%;
57
+          height: 60%;
58
+          top: -10px;
59
+          right: -10px;
60
+        }
61
+      }
62
+    }
63
+  }
64
+}

+ 9
- 0
src/components/issueType/index.jsx Zobrazit soubor

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

+ 9
- 0
src/components/locType/index.jsx Zobrazit soubor

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

+ 31
- 0
src/components/map/index.jsx Zobrazit soubor

@@ -0,0 +1,31 @@
1
+import React from 'react';
2
+import { View, Map, Marker } from '@tarojs/components';
3
+import iconPath from '@/assets/icons/marker.png';
4
+import style from './style.module.less';
5
+
6
+export default (props) => {
7
+  const { location = '' } = props;
8
+  const {lng, lat} = location.split(',');
9
+
10
+  const markers = React.useMemo(() => {
11
+    if (lng == undefined || lat == undefined) return [];
12
+  
13
+    return [{
14
+      longitude: lng - 0,
15
+      latitude: lat - 0,
16
+      iconPath,
17
+      width: 50,
18
+      height: 58
19
+    }];
20
+  }, [lng, lat]);
21
+  
22
+  return (
23
+    <View className={style['map-wrapper']}>
24
+      <Map
25
+        longitude={lng}
26
+        latitude={lat}
27
+        markers={markers}
28
+      ></Map>
29
+    </View>
30
+  )
31
+}

+ 15
- 0
src/components/map/style.module.less Zobrazit soubor

@@ -0,0 +1,15 @@
1
+
2
+.map-wrapper {
3
+  width: 100%;
4
+  height: 0;
5
+  padding-bottom: 50%;
6
+  position: relative;
7
+
8
+  map {
9
+    width: 100%;
10
+    height: 100%;
11
+    position: absolute;
12
+    top: 0;
13
+    left: 0;
14
+  }
15
+}

+ 137
- 0
src/layouts/TabBar.jsx Zobrazit soubor

@@ -0,0 +1,137 @@
1
+import React from 'react';
2
+import Taro from '@tarojs/taro';
3
+import { Image } from '@tarojs/components';
4
+import { Tabbar, TabbarItem } from '@antmjs/vantui';
5
+import homeIcon from '@/assets/tabbar/page.png';
6
+import homeActiveIcon from '@/assets/tabbar/page click.png';
7
+import noticeIcon from '@/assets/tabbar/notice.png';
8
+import noticeActiveIcon from '@/assets/tabbar/notice click.png';
9
+import issueIcon from '@/assets/tabbar/release.png';
10
+import issueActiveIcon from '@/assets/tabbar/release click.png';
11
+import checkIcon from '@/assets/tabbar/evaluation.png';
12
+import checkActiveIcon from '@/assets/tabbar/evaluation click.png';
13
+import mineIcon from '@/assets/tabbar/my.png';
14
+import mineActiveIcon from '@/assets/tabbar/my click.png';
15
+
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',
22
+}
23
+
24
+export default (props) => {
25
+  const { active = -1, className } = props;
26
+
27
+  const onChange = (e) => {
28
+    Taro.reLaunch({
29
+      url: tabPages[e.detail]
30
+    })
31
+  }
32
+
33
+  return (
34
+    <Tabbar
35
+      activeColor="#1A7565"
36
+      className={className}
37
+      active={active}
38
+      onChange={onChange}
39
+    >
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>
135
+    </Tabbar>
136
+  )
137
+}

+ 32
- 11
src/layouts/index.jsx Zobrazit soubor

@@ -1,28 +1,49 @@
1 1
 import React from 'react';
2
-import { View } from '@tarojs/components';
2
+import Taro from '@tarojs/taro';
3
+import { View, Image } from '@tarojs/components';
3 4
 import { useModel } from '@/store';
4 5
 import { Loading } from '@antmjs/vantui';
6
+import Auth from '@/components/Auth';
7
+import TabBar from './TabBar';
5 8
 import laySty from './layout.module.less';
6 9
 
7 10
 export default (props) => {
8
-  const { className, style } = props;
11
+  const { className, style, roles, tabBar = false } = props;
9 12
 
10
-  const { person } = useModel('user');
13
+  const { person, user } = useModel('user');
11 14
 
12 15
   const classNames = `${laySty['page-wrapper']} ${className}`;
13 16
 
17
+  React.useEffect(() => {
18
+    if (person && !user) {
19
+      const currentPage = Taro.getCurrentPages().slice().pop();
20
+      if ('pages/login/index' !== currentPage.route) {
21
+        Taro.navigateTo({
22
+          url: '/pages/login/index',
23
+        })
24
+      }
25
+    }
26
+  }, [person, user]);
27
+
14 28
   return (
15 29
     <View className={classNames} style={style}>
30
+      <View className={laySty['page-conatiner']}>
31
+        {
32
+          !person && (
33
+            <View className={laySty.loading}>
34
+              <Loading size="32px" vertical>
35
+                加载中...
36
+              </Loading>
37
+            </View>
38
+          )
39
+        }
40
+        <Auth roles={roles}>
41
+          {props.children}
42
+        </Auth>
43
+      </View>
16 44
       {
17
-        !person && (
18
-          <View className={laySty.loading}>
19
-            <Loading size='32px' vertical>
20
-              加载中...
21
-            </Loading>
22
-          </View>
23
-        )
45
+        tabBar && <TabBar className={laySty['page-tabbar']} active={tabBar} />
24 46
       }
25
-      {props.children}
26 47
     </View>
27 48
   )
28 49
 }

+ 14
- 1
src/layouts/layout.module.less Zobrazit soubor

@@ -1,7 +1,20 @@
1 1
 
2 2
 .page-wrapper {
3 3
   position: relative;
4
-  height: 100%;
4
+  width: 100%;
5
+  box-sizing: content-box;
6
+  height: calc(100% - env(safe-area-inset-bottom));
7
+  padding-bottom: env(safe-area-inset-bottom);
8
+
9
+  .page-conatiner {
10
+    width: 100%;
11
+    height: calc(100% - var(--tabbar-height, 100rpx));
12
+    overflow: hidden;
13
+  }
14
+
15
+  .page-tabbar {
16
+    flex: none;
17
+  }
5 18
 
6 19
   .loading {
7 20
     position: fixed;

+ 10
- 4
src/pages/home/components/Head.jsx Zobrazit soubor

@@ -1,19 +1,25 @@
1 1
 import React from 'react';
2
-import { View } from '@tarojs/components';
2
+import { View, Image } from '@tarojs/components';
3
+import { ROLES, ROLE_CITIZEN } from '@/utils/user';
4
+import logo from '@/assets/image/logo.png';
3 5
 import style from './head.module.less';
4 6
 
5 7
 export default (props) => {
8
+  const { user = {} } = props;
9
+  const { dutyList = [] } = user;
10
+  const role = ROLES[dutyList[0] || ROLE_CITIZEN];
11
+
6 12
   return (
7 13
     <View className={style.head}>
8 14
       <View className={style.profile}>
9
-        <View>Hi, 张三!</View>
15
+        <View style={{ letterSpacing: '2px' }}>Hi, {user.name}!</View>
10 16
         <View className={style.badge}>
11 17
           <View className={style.icon}></View>
12
-          <View>督察员</View>
18
+          <View>{role}</View>
13 19
         </View>
14 20
       </View>
15 21
       <View className={style.avatar}>
16
-
22
+        <Image src={user.avatar || logo}></Image>
17 23
       </View>
18 24
     </View>
19 25
   )

+ 28
- 8
src/pages/home/components/head.module.less Zobrazit soubor

@@ -14,24 +14,39 @@
14 14
     font-size: 42px;
15 15
 
16 16
     .badge {
17
+      display: inline-block;
17 18
       position: relative;
18
-      padding-left: 64px;
19
+      padding-right: 24px;
20
+      padding-left: 72px;
19 21
       margin-top: var(--main-space);
20 22
       height: 48px;
21
-      line-height: 48px;
23
+      line-height: 46px; // 向上偏移2像素
22 24
       background: linear-gradient(0, #E36A00, #FFA658);
23 25
       border-radius: 22px;
24 26
       font-size: 24px;
27
+      text-align: center;
25 28
 
26 29
       .icon {
27
-        top: -8px;
28
-        left: -8px;
30
+        top: -12px;
31
+        left: -12px;
29 32
         position: absolute;
30
-        width: 64px;
31
-        height: 64px;
32
-        background: linear-gradient(0, #E36A00, #FFA658);
33
+        width: 56px;
34
+        height: 56px;
35
+        border: 8px solid rgba(251, 239, 121, 0.6);
33 36
         border-radius: 50%;
34
-        border: 8px solid rgba(251, 239, 121, 0.2);
37
+        box-sizing: content-box;
38
+
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;
49
+        }
35 50
       }
36 51
     }
37 52
   }
@@ -42,5 +57,10 @@
42 57
     height: 16vw;
43 58
     border-radius: 50%;
44 59
     border: 4px solid #fff;
60
+
61
+    image {
62
+      width: 100%;
63
+      height: 100%;
64
+    }
45 65
   }
46 66
 }

+ 6
- 2
src/pages/home/index.jsx Zobrazit soubor

@@ -1,15 +1,19 @@
1 1
 import React from 'react'
2 2
 import { View, Text, Image } from '@tarojs/components'
3 3
 import Page from '@/layouts/index';
4
+import { useModel } from '@/store';
4 5
 import Head from './components/Head';
5 6
 import BannerCard from './components/BannerCard';
6 7
 import MenuCard from './components/MenuCard';
7 8
 import './index.less';
8 9
 
9 10
 export default (props) => {
11
+
12
+  const { user } = useModel('user');
13
+
10 14
   return (
11
-    <Page className='index'>
12
-      <Head />
15
+    <Page tabBar="home" className='index'>
16
+      <Head user={user} />
13 17
       <BannerCard />
14 18
       <MenuCard />
15 19
     </Page>

+ 3
- 0
src/pages/issue/add/index.config.js Zobrazit soubor

@@ -0,0 +1,3 @@
1
+export default definePageConfig({
2
+  navigationBarTitleText: '我要上报'
3
+})

+ 156
- 0
src/pages/issue/add/index.jsx Zobrazit soubor

@@ -0,0 +1,156 @@
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
+}

+ 77
- 24
src/pages/login/components/Form.jsx Zobrazit soubor

@@ -1,30 +1,83 @@
1 1
 import React from 'react';
2
-import { View, Image, Input } from '@tarojs/components';
3
-import { Form, Button } from '@antmjs/vantui'
4
-import user from '../../../assets/image/user.png';
5
-import password from '../../../assets/image/password.png';
6
-import style from './form.module.less';
2
+import Taro from '@tarojs/taro';
3
+import { View } from '@tarojs/components';
4
+import { Form, FormItem, Field, Button } from '@antmjs/vantui'
5
+import { useModel } from '@/store';
6
+import user from '@/assets/image/user.png';
7
+import password from '@/assets/image/password.png';
8
+import './form.less';
7 9
 
8 10
 export default (props) => {
11
+  const { onSuccess } = props;
12
+
13
+  const { signin } = useModel('user');
14
+  const form = Form.useForm();
15
+  const [accErr, setAccErr] = React.useState();
16
+  const [pwdErr, setPwdErr] = React.useState();
17
+
18
+  const onFinish = (errs, res) => {
19
+    if (!res.account) {
20
+      setAccErr('账户不能为空');
21
+      return;
22
+    } else {
23
+      setAccErr();
24
+    }
25
+    if (!res.password) {
26
+      setPwdErr('密码不能为空');
27
+      return;
28
+    } else {
29
+      setPwdErr();
30
+    }
31
+
32
+    signin(res).then(onSuccess);
33
+  }
34
+
35
+  const onForgetPwd = () => {
36
+    Taro.navigateTo({
37
+      url: '/pages/reset-password/index'
38
+    });
39
+  }
40
+
9 41
   return (
10
-    <Form className={style.form}>
11
-      <View className={style['input-box']}>
12
-        <Image src={user} className={style['img-icon']} />账号<Input className={style.input} placeholder='请输入您的登录账号' />
13
-      </View>
14
-      <View className={style['input-box']}>
15
-        <Image src={password} className={style['img-icon']} />密码<Input className={style.input} placeholder='请输入您的登录密码' />
16
-      </View>
17
-      <View
18
-        className={style['forget-pwd']}
19
-      >
20
-        忘记密码?
21
-      </View>
22
-      <Button
23
-        type='primary'
24
-        className={style.submit}
25
-      >
26
-        登录
27
-      </Button>
28
-    </Form>
42
+    <View className="login-form">
43
+      <Form form={form} onFinish={onFinish}>
44
+        <FormItem
45
+          name="account"
46
+          valueFormat={(e) => e.detail}
47
+        >
48
+          <Field
49
+            border
50
+            label="账号"
51
+            leftIcon={user}
52
+            errorMessage={accErr}
53
+            placeholder="请输入您的登录账号"
54
+          ></Field>
55
+        </FormItem>
56
+        <FormItem
57
+          name="password"
58
+          valueFormat={(e) => e.detail}
59
+        >
60
+          <Field
61
+            password
62
+            label="密码"
63
+            leftIcon={password}
64
+            errorMessage={pwdErr}
65
+            placeholder="请输入您的登录密码"
66
+          ></Field>
67
+        </FormItem>
68
+        <View className="forget-password" onClick={onForgetPwd}>
69
+          忘记密码?
70
+        </View>
71
+        <View>
72
+          <Button
73
+            block
74
+            type="primary"
75
+            formType="submit"
76
+          >
77
+            登录
78
+          </Button>
79
+        </View>
80
+      </Form>
81
+    </View>
29 82
   )
30 83
 }

+ 63
- 0
src/pages/login/components/form.less Zobrazit soubor

@@ -0,0 +1,63 @@
1
+.login-form {
2
+  width: 100%;
3
+  box-sizing: border-box;
4
+  padding: 0 var(--main-space);
5
+
6
+  .vant-form-formItem,
7
+  .vant-form-formItem-wrapper,
8
+  .vant-form-formItem-controll {
9
+    margin: 0;
10
+    padding: 0;
11
+    background-color: transparent;
12
+  }
13
+
14
+  .vant-form-formItem-wrapper {
15
+    & + .vant-form-formItem-wrapper {
16
+      margin-top: var(--main-space);
17
+    }
18
+  }
19
+
20
+  .vant-form-label {
21
+    width: 0;
22
+  }
23
+
24
+  .field-wrapper {
25
+    width: 100%;
26
+    padding: 0 var(--main-space);
27
+  }
28
+
29
+  .van-cell {
30
+    background-color: transparent;
31
+
32
+    &::after {
33
+      border-bottom-color: rgba(0,0,0,0.12);
34
+    }
35
+  }
36
+
37
+  .van-icon__image {
38
+    width: 42px;
39
+    height: 42px;
40
+    vertical-align: middle;
41
+  }
42
+
43
+  .van-cell__title {
44
+    margin-left: 20px;
45
+    font-size: 16px;
46
+    vertical-align: middle;
47
+    border-right: 1px solid #000;
48
+  }
49
+
50
+  .van-field__label {
51
+    font-size: 32px;
52
+  }
53
+
54
+  .forget-password {
55
+    text-align: right;
56
+    font-size: 28px;
57
+    font-weight: 400;
58
+    color: var(--main-bg-color);
59
+    height: 148px;
60
+    line-height: 148px;
61
+    padding: 0 var(--main-space);
62
+  }
63
+}

+ 0
- 36
src/pages/login/components/form.module.less Zobrazit soubor

@@ -1,36 +0,0 @@
1
-.form {
2
-  width: 620px;
3
-  .input-box {
4
-    display: flex;
5
-    border-bottom: 1px solid #e9eaea;
6
-    width: 600px;
7
-    margin: 26px auto;
8
-    padding-bottom: 30px;
9
-    margin-top: 60px;
10
-    .img-icon {
11
-      margin-right: 30px;
12
-      width: 42px;
13
-      height: 44px;
14
-    }
15
-    .input {
16
-      color: rgb(174, 174, 174);
17
-      margin-left: 15px;
18
-      padding-left: 20px;
19
-      flex-direction: column;
20
-      border-left: 1px solid black;
21
-    }
22
-  }
23
-  .forget-pwd {
24
-    font-size: 28px;
25
-    color: var(--main-bg-color);
26
-    float: right;
27
-    margin-right: 72px;
28
-    margin-bottom: 104px;
29
-  }
30
-  .submit {
31
-    width: 666px;
32
-    margin: 80px auto;
33
-    background-color: var(--main-bg-color);
34
-    color: #fff;
35
-  }
36
-}

+ 22
- 5
src/pages/login/index.jsx Zobrazit soubor

@@ -1,19 +1,36 @@
1
-import Page from '@/layouts/index';
1
+import React from 'react';
2
+import Taro from '@tarojs/taro';
2 3
 import { View } from '@tarojs/components';
4
+import Page from '@/layouts/index';
3 5
 import Head from "./components/Head";
4 6
 import Form from "./components/Form";
5 7
 import Bottom from "./components/Bottom";
6 8
 import './index.less'
7 9
 
8 10
 export default (props) => {
11
+
12
+  React.useMemo(() => {  
13
+    Taro.hideTabBar();
14
+  }, []);
15
+
16
+  const onSuccess = () => {
17
+    Taro.navigateBack({
18
+      delta: 1,
19
+      fail: () => {
20
+        Taro.reLaunch({
21
+          url: '/pages/home/index'
22
+        })
23
+      }
24
+    });
25
+  }
26
+
9 27
   return (
10
-    <Page className='index'>
11
-      <View className='login-box'>
28
+    <Page className="index">
29
+      <View className="login-box">
12 30
         <Head />
13
-        <Form />
31
+        <Form onSuccess={onSuccess} />
14 32
         <Bottom />
15 33
       </View>
16 34
     </Page>
17
-
18 35
   )
19 36
 }

+ 3
- 7
src/pages/reporting/detail/components/Map.jsx Zobrazit soubor

@@ -1,18 +1,14 @@
1 1
 import React from 'react';
2
-import { View, Map } from '@tarojs/components';
2
+import { View } from '@tarojs/components';
3
+import Map from '@/components/map';
3 4
 import style from './map.module.less';
4 5
 
5 6
 export default (props) => {
6 7
   const { location } = props;
7
-  const loc = location ? location.split(',') : [,];
8 8
   
9 9
   return (
10 10
     <View className={style['map-box']}>
11
-      <Map
12
-        className={style.map}
13
-        longitude={loc[0]}
14
-        latitude={loc[1]}
15
-      ></Map>
11
+      <Map location={location} />
16 12
       <View className={style.address}>
17 13
         阳光丽景小区
18 14
       </View>

+ 0
- 5
src/pages/reporting/detail/components/map.module.less Zobrazit soubor

@@ -2,11 +2,6 @@
2 2
 .map-box {
3 3
   width: 100vw;
4 4
 
5
-  .map {
6
-    width: 100%;
7
-    height: 50vw;
8
-  }
9
-
10 5
   .address {
11 6
     width: 100%;
12 7
     background: #fff;

+ 1
- 1
src/pages/reporting/detail/index.jsx Zobrazit soubor

@@ -1,9 +1,9 @@
1 1
 import React from 'react';
2 2
 import Taro from '@tarojs/taro';
3 3
 import { View } from '@tarojs/components';
4
+import { Notify } from '@antmjs/vantui';
4 5
 import Page from '@/layouts/index';
5 6
 import getAuthorize from '@/utils/authorize';
6
-import { Notify } from '@antmjs/vantui';
7 7
 import Map from './components/Map';
8 8
 import Issue from './components/Issue';
9 9
 

+ 102
- 0
src/pages/reset-password/components/Captcha.jsx Zobrazit soubor

@@ -0,0 +1,102 @@
1
+import React from 'react';
2
+import { View } from '@tarojs/components';
3
+import { Field, Button } from '@antmjs/vantui';
4
+import { getCaptcha } from '@/services/comm';
5
+
6
+export default (props) => {
7
+  const { onNext } = props;
8
+
9
+  const [loading, setLoading] = React.useState(false);
10
+  const [phone, setPhone] = React.useState();
11
+  const [phoneErr, setPhoneErr] = React.useState();
12
+  const [captcha, setCaptcha] = React.useState();
13
+  const [captchaErr, setCaptchaErr] = React.useState();
14
+  const [disabled, setDisabled] = React.useState(false);
15
+  const [sec, setSec] = React.useState(0);
16
+
17
+  const btnText = disabled ? `剩余 ${sec} 秒` : '获取验证码';
18
+
19
+  const onCaptcha = () => {
20
+    if (!phone || phone.length != 11) {
21
+      setPhoneErr('请输入正确的手机号')
22
+      return;
23
+    } else {
24
+      setPhoneErr();
25
+    }
26
+    
27
+    setLoading(true);
28
+    getCaptcha(phone).then(() => {
29
+      setLoading(false);
30
+      let left = 60;
31
+      
32
+      setDisabled(true);
33
+      setSec(left --);
34
+      const ticker = setInterval(() => {
35
+        left -= 1;
36
+        setSec(left);
37
+  
38
+        if (left <= 1) {
39
+          setDisabled(false);
40
+          clearInterval(ticker);
41
+        }
42
+      }, 1000);
43
+    }).catch(() => {
44
+      setLoading(false);
45
+    });
46
+  }
47
+
48
+  const handleNext = () => {
49
+    if (!phone) {
50
+      setPhoneErr('请输入手机号');
51
+    }
52
+
53
+    if (!captcha) {
54
+      setCaptchaErr('请输入验证码')
55
+    }
56
+
57
+    if (!phone || !captcha) {
58
+      return;
59
+    }
60
+
61
+    onNext({phone, captcha});
62
+  }
63
+  
64
+  return (
65
+    <View>
66
+      <View className="title">找回密码</View>
67
+      <View className="form-box">
68
+        <Field
69
+          value={phone}
70
+          loading={loading}
71
+          placeholder="请输入注册手机号"
72
+          onChange={e => setPhone(e.detail)}
73
+          errorMessage={phoneErr}
74
+          renderButton={
75
+            <Button
76
+              round
77
+              size="small"
78
+              type="primary"
79
+              disabled={disabled}
80
+              onClick={onCaptcha}
81
+            >
82
+              {btnText}
83
+            </Button>
84
+          }
85
+        ></Field>
86
+        <Field
87
+          value={captcha}
88
+          placeholder="请输入验证码"
89
+          errorMessage={captchaErr}
90
+          onChange={e => setCaptcha(e.detail)}
91
+        ></Field>
92
+      </View>
93
+      <View className="form-act">
94
+        <View>
95
+        </View>
96
+        <View>
97
+          <Button block type="primary" size="small" onClick={handleNext}>下一步</Button>
98
+        </View>
99
+      </View>
100
+    </View>
101
+  )
102
+}

+ 87
- 0
src/pages/reset-password/components/ResetPwd.jsx Zobrazit soubor

@@ -0,0 +1,87 @@
1
+import React from 'react';
2
+import Taro from '@tarojs/taro';
3
+import { View } from '@tarojs/components';
4
+import { Field, Button } from '@antmjs/vantui';
5
+import md5 from 'md5';
6
+
7
+export default (props) => {
8
+  const { onPrev, onSubmit } = props;
9
+
10
+  const [loading, setLoading] = React.useState(false);
11
+  const [pwd1, setPwd1] = React.useState();
12
+  const [pwd1Err, setPwd1Err] = React.useState();
13
+  const [pwd2, setPwd2] = React.useState();
14
+  const [pwd2Err, setPwd2Err] = React.useState();
15
+  
16
+  const handlePrev = () => {
17
+    onPrev();
18
+  }
19
+
20
+  const handleSubmit = () => {
21
+    if (!pwd1) {
22
+      setPwd1Err('请输入密码');
23
+    } else{
24
+      setPwd1Err();
25
+    }
26
+    if (!pwd2) {
27
+      setPwd2Err('请确认密码');
28
+    } else {
29
+      setPwd2Err();
30
+    }
31
+
32
+    if (!pwd1 || !pwd2) {
33
+      return;
34
+    }
35
+
36
+    if (pwd1 != pwd2) {
37
+      setPwd2Err('两次密码不一致');
38
+      return;
39
+    } else {
40
+      setPwd2Err();
41
+    }
42
+
43
+    setLoading(true)
44
+    onSubmit({ password: md5(pwd1) }).then(() => {
45
+      setLoading(false);
46
+
47
+      Taro.navigateBack({
48
+        delta: 1,
49
+        fail: () => {
50
+          Taro.reLaunch({
51
+            url: '/pages/login/index',
52
+          })
53
+        }
54
+      })
55
+    }).catch(() => {
56
+      setLoading(false);
57
+    });
58
+  }
59
+  
60
+  return (
61
+    <View>
62
+      <View className="title">重置密码</View>
63
+      <View className="form-box">
64
+        <Field
65
+          value={pwd1}
66
+          placeholder="请设置新密码"
67
+          errorMessage={pwd1Err}
68
+          onChange={e => setPwd1(e.detail)}
69
+        ></Field>
70
+        <Field
71
+          value={pwd2}
72
+          placeholder="再次确认新密码"
73
+          errorMessage={pwd2Err}
74
+          onChange={e => setPwd2(e.detail)}
75
+        ></Field>
76
+      </View>
77
+      <View className="form-act">
78
+        <View>
79
+          <Button block hairline plain type="primary" size="small" onClick={handlePrev}>上一步</Button>
80
+        </View>
81
+        <View>
82
+          <Button block type="primary" size="small" loading={loading} onClick={handleSubmit}>确定</Button>
83
+        </View>
84
+      </View>
85
+    </View>
86
+  )
87
+}

+ 3
- 0
src/pages/reset-password/index.config.js Zobrazit soubor

@@ -0,0 +1,3 @@
1
+export default definePageConfig({
2
+  navigationBarTitleText: '找回密码'
3
+})

+ 44
- 0
src/pages/reset-password/index.jsx Zobrazit soubor

@@ -0,0 +1,44 @@
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 { changePwd } from '@/services/wxma'; 
6
+import Captcha from './components/Captcha';
7
+import ResetPwd from './components/ResetPwd';
8
+import './index.less';
9
+
10
+export default (props) => {
11
+
12
+  const [direct, setDirect] = React.useState();
13
+  const transClass = direct == 'right' ? 'form-wrapper to-right' : 'form-wrapper';
14
+  const formData = React.useRef();
15
+
16
+  const onNext = (data) => {
17
+    formData.current = data;
18
+    setDirect('right');
19
+  }
20
+
21
+  const onSubmit = (data) => {
22
+    formData.current = {
23
+      ...formData.current,
24
+      ...data,
25
+    }
26
+
27
+    return changePwd(formData.current)
28
+  }
29
+  
30
+  return (
31
+    <Page>
32
+      <View className="reset-password-box">
33
+        <View className={transClass}>
34
+          <View>
35
+            <Captcha onNext={onNext} />
36
+          </View>
37
+          <View>
38
+            <ResetPwd onPrev={() => setDirect()} onSubmit={onSubmit} />
39
+          </View>
40
+        </View>
41
+      </View>
42
+    </Page>
43
+  )
44
+}

+ 50
- 0
src/pages/reset-password/index.less Zobrazit soubor

@@ -0,0 +1,50 @@
1
+.reset-password-box {
2
+  width: 100%;
3
+  padding: 80px 0;
4
+  overflow: hidden;
5
+
6
+  .title {
7
+    font-size: 36px;
8
+    font-weight: 500;
9
+    color: #000000;
10
+  }
11
+
12
+  .form-box {
13
+    margin-top: 80px;
14
+
15
+    .van-cell {
16
+      background: transparent;
17
+
18
+      &::after {
19
+        border-bottom-color: rgba(0, 0, 0, 0.12);
20
+      }
21
+    }
22
+  }
23
+
24
+  .form-act {
25
+    margin-top: 80px;
26
+    display: flex;
27
+    align-items: center;
28
+    justify-content: space-between;
29
+
30
+    & > view {
31
+      width: 180px;
32
+      flex: none;
33
+    }
34
+  }
35
+
36
+  .form-wrapper {
37
+    width: 200%;
38
+    transition: transform 200ms;
39
+
40
+    & > view {
41
+      padding: 0 60px;
42
+      display: inline-block;
43
+      width: 50%;
44
+    }
45
+
46
+    &.to-right {
47
+      transform: translate(-50%, 0);
48
+    }
49
+  }
50
+}

+ 6
- 0
src/services/comm.js Zobrazit soubor

@@ -0,0 +1,6 @@
1
+import request from '@/utils/request';
2
+
3
+/*
4
+ * 获取验证码
5
+ */
6
+export const getCaptcha = (phone) => request(`/api/ma/captcha?phone=${phone}`);

+ 26
- 0
src/services/taissue.js Zobrazit soubor

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

+ 26
- 0
src/services/tdissuetype.js Zobrazit soubor

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

+ 29
- 0
src/services/tdloctype.js Zobrazit soubor

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

+ 16
- 0
src/services/wxma.js Zobrazit soubor

@@ -14,3 +14,19 @@ export const authUser = (id, data) => request(`/api/ma/auth-user`, { data, metho
14 14
  * 小程序登录
15 15
  */
16 16
 export const login = (code) => request(`/api/ma/login?code=${code}`, { method: 'post', silent: true });
17
+
18
+/*
19
+ * 小程序登录
20
+ */
21
+export const signin = (data) => request(`/api/ma/signin`, { method: 'post', data });
22
+
23
+/*
24
+ * 获取当前人员
25
+ */
26
+export const currentUser = () => request(`/api/ma/current`);
27
+
28
+
29
+/*
30
+ * 修改密码
31
+ */
32
+export const changePwd = (data) => request(`/api/ma/change-password`, { method: 'post', data });

+ 24
- 4
src/store/user.js Zobrazit soubor

@@ -1,22 +1,42 @@
1 1
 import React from 'react';
2 2
 import Taro from '@tarojs/taro';
3
-import { login } from '@/services/wxma';
3
+import md5 from 'md5';
4
+import { login, signin, currentUser } from '@/services/wxma';
4 5
 
5 6
 export default function useUser() {
6
-  const [person, setPerson] = React.useState({});
7
-  const [user, setUser] = React.useState({});
7
+  const [person, setPerson] = React.useState();
8
+  const [user, setUser] = React.useState();
8 9
 
9 10
   const maLogin = (code) => {
10 11
     login(code).then(res => {
11 12
       setPerson(res.person);
12
-      setUser(res.user);
13
+      Taro.setStorage({ key: 'personId', data: res.person.personId })
13 14
       Taro.setStorage({ key: 'sessionKey', data: res.sessionKey })
14 15
     });
15 16
   }
16 17
 
18
+  const maSignIn = (params) => {
19
+    const data = {
20
+      ...params,
21
+      password: md5(params.password)
22
+    }
23
+
24
+    return signin(data).then(res => {
25
+      setUser(res.user);
26
+    })
27
+  }
28
+
29
+  const current = () => {
30
+    currentUser().then(res => {
31
+      setUser(res.user);
32
+    })
33
+  }
34
+
17 35
   return {
18 36
     user,
19 37
     person,
38
+    current,
39
+    signin: maSignIn,
20 40
     login: maLogin,
21 41
   }
22 42
 }

+ 20
- 1
src/utils/authorize.js Zobrazit soubor

@@ -1,6 +1,6 @@
1 1
 import Taro from '@tarojs/taro';
2 2
 
3
-export default (scope) => {
3
+export default function getAuthorize(scope) {
4 4
   return new Promise((resolve, reject) => {
5 5
     Taro.getSetting({
6 6
       success(res) {
@@ -24,3 +24,22 @@ export default (scope) => {
24 24
     });
25 25
   });
26 26
 }
27
+
28
+export const getLocation = () => {
29
+  return new Promise((resolve, reject) => {
30
+    getAuthorize('scope.userLocation').then(() => {
31
+      Taro.getLocation({
32
+        success(res) {
33
+          resolve(res);
34
+        },
35
+        fail(err) {
36
+          console.error(err);
37
+          reject(err);
38
+        }
39
+      });
40
+    }).catch((err) => {
41
+      console.error(err);
42
+      reject(err);
43
+    });
44
+  });
45
+}

+ 12
- 0
src/utils/message.js Zobrazit soubor

@@ -0,0 +1,12 @@
1
+
2
+import { Notify } from '@antmjs/vantui';
3
+
4
+export function warn(predicate, message) {
5
+  if (predicate) {
6
+    Notify.show({
7
+      message,
8
+      type: 'warning',
9
+    })
10
+    throw new Error(message);
11
+  }
12
+}

+ 39
- 1
src/utils/request.js Zobrazit soubor

@@ -16,7 +16,8 @@ export default function request(api, options = {}) {
16 16
     const { method = 'GET', params, data = {}, header = {}, silent = false, ...cfgs } = options;
17 17
     const queryStr = params ? getQueryStrByParams(params) : undefined;
18 18
     // eslint-disable-next-line no-undef
19
-    const url = queryStr ? `${HOST}${api}?${queryStr}` : `${HOST}${api}`
19
+    const url = queryStr ? `${HOST}${api}?${queryStr}` : `${HOST}${api}`;
20
+    const personId = Taro.getStorageSync('personId');
20 21
     
21 22
     const onError = (err) => {
22 23
       console.error(err);
@@ -39,6 +40,7 @@ export default function request(api, options = {}) {
39 40
       data,
40 41
       method,
41 42
       header: {
43
+        'x-personId': personId,
42 44
         'content-type': 'application/json',
43 45
         'authorization': getToken(),
44 46
         ...header,
@@ -70,3 +72,39 @@ export default function request(api, options = {}) {
70 72
 
71 73
   });
72 74
 }
75
+
76
+
77
+export const uploadFiles = async (files, url) => {
78
+  const uploads = []
79
+  const token = getToken();
80
+  for (var i = 0; i < files.length; i++) {
81
+    uploads[i] = new Promise((resolve, reject) => {
82
+      Taro.uploadFile({
83
+        // eslint-disable-next-line no-undef
84
+        url: url || `${HOST}/api/ma/file`,
85
+        filePath: files[i].tempFilePath,
86
+        header: {
87
+          'authorization': token,
88
+        },
89
+        name: 'file',
90
+        formData: {
91
+          fileType: files[i].fileType,
92
+        },
93
+        success: function (res) {
94
+          // debugger
95
+          const _data = JSON.parse(res.data)
96
+          // if (_data.code !== CODE_SUCCESS) {
97
+          //   reject(new Error(_data.message))
98
+          // }
99
+
100
+          resolve(_data.data)
101
+        },
102
+        fail(err) {
103
+          reject(err)
104
+        }
105
+      })
106
+    })
107
+  }
108
+
109
+  return Promise.all(uploads)
110
+}

+ 24
- 0
src/utils/user.js Zobrazit soubor

@@ -0,0 +1,24 @@
1
+
2
+// 督察员
3
+export const ROLE_INSPECTOR = 'inspector';
4
+
5
+// 管理员
6
+export const ROLE_MANAGER = 'manager';
7
+
8
+// 单位人员
9
+export const ROLE_ORG_USER = 'org_user';
10
+
11
+// 查询员
12
+export const ROLE_QUERY_PERSON = 'query_person';
13
+
14
+// 市民
15
+export const ROLE_CITIZEN = 'citizen';
16
+
17
+
18
+export const ROLES = {
19
+  [ROLE_INSPECTOR]: '督察员',
20
+  [ROLE_MANAGER]: '管理员',
21
+  [ROLE_ORG_USER]: '单位人员',
22
+  [ROLE_QUERY_PERSON]: '查询员',
23
+  [ROLE_CITIZEN]: '市民',
24
+}

+ 1
- 1
yarn.lock Zobrazit soubor

@@ -8180,7 +8180,7 @@ md5.js@^1.3.4:
8180 8180
 
8181 8181
 md5@^2.3.0:
8182 8182
   version "2.3.0"
8183
-  resolved "https://registry.npmmirror.com/md5/-/md5-2.3.0.tgz#c3da9a6aae3a30b46b7b0c349b87b110dc3bda4f"
8183
+  resolved "https://registry.yarnpkg.com/md5/-/md5-2.3.0.tgz#c3da9a6aae3a30b46b7b0c349b87b110dc3bda4f"
8184 8184
   integrity sha512-T1GITYmFaKuO91vxyoQMFETst+O71VUPEU3ze5GNzDm0OWdP8v1ziTaAEPUr/3kLsY3Sftgz242A1SetQiDL7g==
8185 8185
   dependencies:
8186 8186
     charenc "0.0.2"