Procházet zdrojové kódy

feat: 页面开发

szr190 před 2 měsíci
rodič
revize
5ee48efa28

+ 0 - 63
src/base/components/Captcha.vue

@@ -1,63 +0,0 @@
1
-<!--
2
- * @Author: wyd
3
- * @Date: 2024-03
4
- * @LastEditors: wyd
5
- * @LastEditTime: 2024-04
6
- * @Description: 验证码
7
--->
8
-
9
-<script setup lang="ts">
10
-import { watch, ref, onMounted } from 'vue'
11
-import { getCaptchaImageApi } from '@/api/login'
12
-const props = defineProps({
13
-  modelValue: String
14
-})
15
-const emit = defineEmits(['getCaptchaInfo', 'update:modelValue'])
16
-const form = {
17
-  code: '',
18
-  uuid: '',
19
-  captchaEnabled: true
20
-}
21
-watch(
22
-  () => props.modelValue,
23
-  (val, pre) => {
24
-    form.code = props.modelValue || ''
25
-  }
26
-)
27
-// 更新code
28
-const codeChange = () => {
29
-  emit('update:modelValue', form.code)
30
-  emit('getCaptchaInfo', form)
31
-}
32
-const imgPath = ref('')
33
-// 获取验证码图片
34
-const getCaptchaImage = () => {
35
-  getCaptchaImageApi({ isWeb: false }).then((res) => {
36
-    form.captchaEnabled = res.data.captchaEnabled
37
-    form.uuid = res.data.uuid
38
-    if (res.data.captchaEnabled) {
39
-      imgPath.value = 'data:image/jpeg;base64,' + res.data.img
40
-    }
41
-    emit('getCaptchaInfo', form)
42
-  })
43
-}
44
-onMounted(() => {
45
-  getCaptchaImage()
46
-})
47
-defineExpose({ getCaptchaImage })
48
-</script>
49
-
50
-<template>
51
-  <view class="flex j-sb a-center">
52
-    <uni-easyinput type="text" v-model="form.code" placeholder="请输入验证码" @change="codeChange" />
53
-    <image :src="imgPath" mode="aspectFill" class="capcha-img ml10" @tap="getCaptchaImage" />
54
-  </view>
55
-</template>
56
-
57
-<style lang="scss" scoped>
58
-.capcha-img {
59
-  width: 100px;
60
-  height: 35px;
61
-  border-radius: 4px;
62
-}
63
-</style>

Diff nebyl zobrazen, protože je příliš veliký
+ 0 - 238
src/base/components/Editors/editor-icon.css


binární
src/base/components/Editors/iconfont.ttf


+ 0 - 286
src/base/components/Editors/index.vue

@@ -1,286 +0,0 @@
1
-<template>
2
-  <!-- <view class="container">
3
-    <view class="page-body"> -->
4
-  <view class="wrapper">
5
-    <view class="toolbar" @tap="format" style="height: 320rpx; overflow-y: auto">
6
-      <view :class="formats.bold ? 'ql-active' : ''" class="iconfont icon-zitijiacu" data-name="bold"></view>
7
-      <view :class="formats.italic ? 'ql-active' : ''" class="iconfont icon-zitixieti" data-name="italic"></view>
8
-      <view :class="formats.underline ? 'ql-active' : ''" class="iconfont icon-zitixiahuaxian" data-name="underline"></view>
9
-      <view :class="formats.strike ? 'ql-active' : ''" class="iconfont icon-zitishanchuxian" data-name="strike"></view>
10
-      <!-- #ifndef MP-BAIDU -->
11
-      <view :class="formats.align === 'left' ? 'ql-active' : ''" class="iconfont icon-zuoduiqi" data-name="align" data-value="left"></view>
12
-      <!-- #endif -->
13
-      <view :class="formats.align === 'center' ? 'ql-active' : ''" class="iconfont icon-juzhongduiqi" data-name="align" data-value="center"></view>
14
-      <view :class="formats.align === 'right' ? 'ql-active' : ''" class="iconfont icon-youduiqi" data-name="align" data-value="right"></view>
15
-      <view :class="formats.align === 'justify' ? 'ql-active' : ''" class="iconfont icon-zuoyouduiqi" data-name="align" data-value="justify"></view>
16
-      <!-- #ifndef MP-BAIDU -->
17
-      <view :class="formats.lineHeight ? 'ql-active' : ''" class="iconfont icon-line-height" data-name="lineHeight" data-value="2"></view>
18
-      <view
19
-        :class="formats.letterSpacing ? 'ql-active' : ''"
20
-        class="iconfont icon-Character-Spacing"
21
-        data-name="letterSpacing"
22
-        data-value="2em"
23
-      ></view>
24
-      <view :class="formats.marginTop ? 'ql-active' : ''" class="iconfont icon-722bianjiqi_duanqianju" data-name="marginTop" data-value="20px"></view>
25
-      <view
26
-        :class="formats.marginBottom ? 'ql-active' : ''"
27
-        class="iconfont icon-723bianjiqi_duanhouju"
28
-        data-name="marginBottom"
29
-        data-value="20px"
30
-      ></view>
31
-      <!-- #endif -->
32
-
33
-      <view class="iconfont icon-clearedformat" @tap="removeFormat"></view>
34
-
35
-      <!-- #ifndef MP-BAIDU -->
36
-      <view :class="formats.fontFamily ? 'ql-active' : ''" class="iconfont icon-font" data-name="fontFamily" data-value="Pacifico"></view>
37
-      <view :class="formats.fontSize === '24px' ? 'ql-active' : ''" class="iconfont icon-fontsize" data-name="fontSize" data-value="24px"></view>
38
-      <!-- #endif -->
39
-      <view :class="formats.color === '#0000ff' ? 'ql-active' : ''" class="iconfont icon-text_color" data-name="color" data-value="#0000ff"></view>
40
-      <view
41
-        :class="formats.backgroundColor === '#00ff00' ? 'ql-active' : ''"
42
-        class="iconfont icon-fontbgcolor"
43
-        data-name="backgroundColor"
44
-        data-value="#00ff00"
45
-      ></view>
46
-      <view class="iconfont icon-date" @tap="insertDate"></view>
47
-      <view class="iconfont icon--checklist" data-name="list" data-value="check"></view>
48
-      <view :class="formats.list === 'ordered' ? 'ql-active' : ''" class="iconfont icon-youxupailie" data-name="list" data-value="ordered"></view>
49
-      <view :class="formats.list === 'bullet' ? 'ql-active' : ''" class="iconfont icon-wuxupailie" data-name="list" data-value="bullet"></view>
50
-
51
-      <view class="iconfont icon-undo" @tap="undo"></view>
52
-      <view class="iconfont icon-redo" @tap="redo"></view>
53
-
54
-      <view class="iconfont icon-outdent" data-name="indent" data-value="-1"></view>
55
-      <view class="iconfont icon-indent" data-name="indent" data-value="+1"></view>
56
-      <view class="iconfont icon-fengexian" @tap="insertDivider"></view>
57
-      <view class="iconfont icon-charutupian" @tap="insertImage"></view>
58
-      <view :class="formats.header === 1 ? 'ql-active' : ''" class="iconfont icon-format-header-1" data-name="header" :data-value="1"></view>
59
-      <view :class="formats.script === 'sub' ? 'ql-active' : ''" class="iconfont icon-zitixiabiao" data-name="script" data-value="sub"></view>
60
-      <view :class="formats.script === 'super' ? 'ql-active' : ''" class="iconfont icon-zitishangbiao" data-name="script" data-value="super"></view>
61
-
62
-      <view class="iconfont icon-shanchu" @tap="clear"></view>
63
-
64
-      <view :class="formats.direction === 'rtl' ? 'ql-active' : ''" class="iconfont icon-direction-rtl" data-name="direction" data-value="rtl"></view>
65
-    </view>
66
-    <view class="editor-wrapper">
67
-      <editor
68
-        id="editor"
69
-        class="ql-container"
70
-        placeholder="请输入..."
71
-        show-img-size
72
-        show-img-toolbar
73
-        show-img-resize
74
-        @statuschange="onStatusChange"
75
-        :read-only="readOnly"
76
-        @ready="onEditorReady"
77
-        @input="onInput"
78
-      >
79
-      </editor>
80
-    </view>
81
-  </view>
82
-  <!-- </view>
83
-  </view> -->
84
-</template>
85
-
86
-<script>
87
-export default {
88
-  props: {
89
-    modelValue: {
90
-      type: String,
91
-      default: ''
92
-    }
93
-  },
94
-  data() {
95
-    return {
96
-      readOnly: false,
97
-      formats: {},
98
-      curContent: ''
99
-    }
100
-  },
101
-  watch: {
102
-    modelValue: {
103
-      handler(val) {
104
-        this.curContent = val
105
-      },
106
-      deep: true,
107
-      immediate: true
108
-    }
109
-  },
110
-  onLoad() {
111
-    // #ifndef MP-BAIDU
112
-    uni.loadFontFace({
113
-      family: 'Pacifico',
114
-      source: 'url("https://sungd.github.io/Pacifico.ttf")'
115
-    })
116
-    // #endif
117
-  },
118
-  methods: {
119
-    readOnlyChange() {
120
-      this.readOnly = !this.readOnly
121
-    },
122
-    onEditorReady() {
123
-      // #ifdef MP-BAIDU
124
-      this.editorCtx = requireDynamicLib('editorLib').createEditorContext('editor')
125
-      // #endif
126
-
127
-      // #ifdef APP-PLUS || MP-WEIXIN || H5
128
-      uni
129
-        .createSelectorQuery()
130
-        .in(this)
131
-        .select('#editor')
132
-        .context((res) => {
133
-          this.editorCtx = res.context
134
-
135
-          // setTimeout(() => {
136
-          //   this.editorCtx.setContents({
137
-          //     html: this.curContent
138
-          //   })
139
-          //   uni.pageScrollTo({
140
-          //     scrollTop: 0,
141
-          //     duration: 300
142
-          //   })
143
-          // }, 100)
144
-        })
145
-        .exec()
146
-      // #endif
147
-    },
148
-    undo() {
149
-      this.editorCtx.undo()
150
-    },
151
-    redo() {
152
-      this.editorCtx.redo()
153
-    },
154
-    format(e) {
155
-      let { name, value } = e.target.dataset
156
-      if (!name) return
157
-      this.editorCtx.format(name, value)
158
-    },
159
-    onStatusChange(e) {
160
-      const formats = e.detail
161
-      this.formats = formats
162
-      this.curContent = e.detail.html
163
-      this.$emit('update:content', e.detail.html)
164
-    },
165
-    onInput(e) {
166
-      this.curContent = e.detail.html
167
-      this.$emit('update:content', e.detail.html)
168
-    },
169
-    insertDivider() {
170
-      this.editorCtx.insertDivider({
171
-        success: function () {
172
-          console.log('insert divider success')
173
-        }
174
-      })
175
-    },
176
-    clear() {
177
-      uni.showModal({
178
-        title: '清空编辑器',
179
-        content: '确定清空编辑器全部内容?',
180
-        success: (res) => {
181
-          if (res.confirm) {
182
-            this.editorCtx.clear({
183
-              success: function (res) {
184
-                console.log('clear success')
185
-              }
186
-            })
187
-          }
188
-        }
189
-      })
190
-    },
191
-    removeFormat() {
192
-      this.editorCtx.removeFormat()
193
-    },
194
-    insertDate() {
195
-      const date = new Date()
196
-      const formatDate = `${date.getFullYear()}/${date.getMonth() + 1}/${date.getDate()}`
197
-      this.editorCtx.insertText({
198
-        text: formatDate
199
-      })
200
-    },
201
-    insertImage() {
202
-      uni.chooseImage({
203
-        count: 1,
204
-        success: (res) => {
205
-          uni.uploadFile({
206
-            url: '/ueditor?action=uploadimage&encode=utf-8',
207
-            name: 'file',
208
-            fileType: 'image',
209
-            filePath: res.tempFilePaths[0],
210
-            success: (result) => {
211
-              const params = JSON.parse(result.data)
212
-              if (params.state === 'SUCCESS') {
213
-                this.editorCtx.insertImage({
214
-                  src: params.url,
215
-                  alt: '图像',
216
-                  success: function () {
217
-                    console.log('insert image success')
218
-                  }
219
-                })
220
-              } else {
221
-                uni.showToast({ icon: 'none', title: '上传失败请重试' })
222
-              }
223
-            }
224
-          })
225
-        }
226
-      })
227
-    }
228
-  }
229
-}
230
-</script>
231
-
232
-<style>
233
-@import './editor-icon.css';
234
-
235
-.page-body {
236
-  /* height: calc(100vh - var(--window-top) - var(--status-bar-height)); */
237
-  height: 1000rpx;
238
-}
239
-
240
-.wrapper {
241
-  height: 1000rpx;
242
-}
243
-
244
-.editor-wrapper {
245
-  /* 		height: calc(100vh - var(--window-top) - var(--status-bar-height) - 140px);*/
246
-  background: #fff;
247
-  height: 680rpx;
248
-}
249
-
250
-.iconfont {
251
-  display: inline-block;
252
-  padding: 8px 8px;
253
-  width: 24px;
254
-  height: 24px;
255
-  cursor: pointer;
256
-  font-size: 20px;
257
-}
258
-
259
-.toolbar {
260
-  /* box-sizing: border-box; */
261
-  border-bottom: 0;
262
-  font-family: 'Helvetica Neue', 'Helvetica', 'Arial', sans-serif;
263
-}
264
-
265
-.ql-container {
266
-  /* box-sizing: border-box; */
267
-  padding: 12px 15px;
268
-  width: 100%;
269
-  /* min-height: 30vh;
270
-  height: 100%; */
271
-  /* margin-top: 20px; */
272
-  font-size: 28rpx;
273
-  line-height: 1.5;
274
-  height: 680rpx !important;
275
-  min-height: 680rpx !important;
276
-}
277
-
278
-.ql-active {
279
-  color: #06c;
280
-}
281
-
282
-/* editor {
283
-  height: 1000rpx !important;
284
-  min-height: 1000rpx !important;
285
-} */
286
-</style>

+ 0 - 181
src/base/forget.vue

@@ -1,181 +0,0 @@
1
-<!--
2
- * @Author: wyd
3
- * @Date: 2024-03
4
- * @LastEditors: wyd
5
- * @LastEditTime: 2024-03
6
- * @Description: 忘记密码
7
--->
8
-<script setup lang="ts">
9
-import { ref } from 'vue'
10
-import { onLoad, onPullDownRefresh, onReady } from '@dcloudio/uni-app'
11
-import { system } from '@/stores/modules/system'
12
-import { useGlobal } from '@/composables'
13
-import { validatorMobile, verifyPwd } from '@/utils/verification'
14
-import { getSmsApi, forgetApi } from '@/api/login'
15
-
16
-const systemStore = system()
17
-// 表单信息
18
-const form = ref({
19
-  passwordAgain: '',
20
-  password: '',
21
-  phonenumber: '',
22
-  smsCode: ''
23
-})
24
-// 表单校验
25
-const rules: UniHelper.UniFormsRules = {
26
-  password: {
27
-    rules: [
28
-      {
29
-        required: true,
30
-        errorMessage: '请输入密码'
31
-      },
32
-      {
33
-        validateFunction: verifyPwd
34
-      }
35
-    ]
36
-  },
37
-  passwordAgain: {
38
-    rules: [
39
-      {
40
-        required: true,
41
-        errorMessage: '请再次输入密码'
42
-      },
43
-      {
44
-        validateFunction: function (rule, value, data, callback) {
45
-          if (value !== data.password) {
46
-            callback('两次输入密码不一致')
47
-          }
48
-          return true
49
-        }
50
-      }
51
-    ]
52
-  },
53
-  phonenumber: {
54
-    rules: [
55
-      {
56
-        required: true,
57
-        errorMessage: '请输入手机号'
58
-      },
59
-      {
60
-        validateFunction: validatorMobile
61
-      }
62
-    ]
63
-  },
64
-  smsCode: {
65
-    rules: [
66
-      {
67
-        required: true,
68
-        errorMessage: '请输入验证码'
69
-      }
70
-    ]
71
-  }
72
-}
73
-
74
-// 短信验证码
75
-let isDisabled = ref(false)
76
-let countdown = ref(0)
77
-const getSmsCode = () => {
78
-  getSmsApi({ phonenumber: form.value.phonenumber, type: 2 }).then(() => {
79
-    uni.showToast({
80
-      title: '操作成功',
81
-      icon: 'none'
82
-    })
83
-    isDisabled.value = true
84
-    countdown.value = 60
85
-    const timerInterval = setInterval(() => {
86
-      if (countdown.value > 1) {
87
-        countdown.value--
88
-      } else {
89
-        isDisabled.value = false
90
-        clearInterval(timerInterval)
91
-      }
92
-    }, 1000)
93
-  })
94
-}
95
-
96
-// 表单组件实例
97
-const formRef = ref<UniHelper.UniFormsInstance>()
98
-
99
-const handleSubmit = async () => {
100
-  formRef.value?.validate?.((err, formData) => {
101
-    if (!err) {
102
-      uni.showLoading()
103
-      forgetApi(form.value).then(() => {
104
-        // 注册成功,返回上一页
105
-        uni.showToast({
106
-          title: '操作成功',
107
-          icon: 'none'
108
-        })
109
-        setTimeout(() => {
110
-          uni.navigateBack()
111
-        }, 1000)
112
-      })
113
-    }
114
-  })
115
-}
116
-onLoad(async () => {
117
-  await systemStore.getConfigData()
118
-})
119
-onReady(() => {
120
-  formRef.value?.setRules?.(rules)
121
-})
122
-// 下拉刷新
123
-onPullDownRefresh(async () => {
124
-  await systemStore.getConfigData()
125
-  uni.stopPullDownRefresh()
126
-})
127
-</script>
128
-
129
-<template>
130
-  <view class="login-box">
131
-    <view class="center-box p15 shadow">
132
-      <view class="tc f18 weight-600 black-color mb20">重置密码</view>
133
-      <uni-forms ref="formRef" :model="form" :rules="rules">
134
-        <uni-forms-item name="phonenumber">
135
-          <uni-easyinput type="text" v-model="form.phonenumber" placeholder="请输入手机号" />
136
-        </uni-forms-item>
137
-        <uni-forms-item name="password">
138
-          <uni-easyinput type="password" v-model="form.password" placeholder="请输入密码" />
139
-        </uni-forms-item>
140
-        <uni-forms-item name="passwordAgain">
141
-          <uni-easyinput type="password" v-model="form.passwordAgain" placeholder="请再次输入密码" />
142
-        </uni-forms-item>
143
-        <uni-forms-item name="smsCode">
144
-          <view class="flex j-sb a-center w100">
145
-            <uni-easyinput type="text" v-model="form.smsCode" placeholder="请输入验证码" />
146
-            <button class="ml10 sms-btn success-bg" :disabled="isDisabled" type="primary" size="mini" @tap="getSmsCode">
147
-              {{ isDisabled ? countdown + 's后重新获取' : '获取验证码' }}
148
-            </button>
149
-          </view>
150
-        </uni-forms-item>
151
-      </uni-forms>
152
-      <view class="btn-b warning-bg" @tap="handleSubmit">确 定</view>
153
-    </view>
154
-  </view>
155
-</template>
156
-
157
-<style lang="scss" scoped>
158
-.login-box {
159
-  width: 100vw;
160
-  height: 100vh;
161
-  background: url('http://1.94.6.75:9000/zlhy-app/bg_login.jpg') center no-repeat;
162
-  background-size: 100% 100%;
163
-  position: relative;
164
-  .center-box {
165
-    width: calc(100vw - 60px);
166
-    min-height: 200px;
167
-    border-radius: 10rpx;
168
-    background: rgba($color: #fff, $alpha: 0.8);
169
-    position: absolute;
170
-    left: 15px;
171
-    top: 20%;
172
-    .btn-b {
173
-      width: 100% !important;
174
-    }
175
-    .sms-btn {
176
-      height: 35px;
177
-      line-height: 35px;
178
-    }
179
-  }
180
-}
181
-</style>

+ 0 - 791
src/base/login.vue

@@ -1,791 +0,0 @@
1
-<script setup lang="ts">
2
-import { ref } from 'vue'
3
-import { onLoad, onReady } from '@dcloudio/uni-app'
4
-import { system } from '@/stores/modules/system'
5
-import { user } from '@/stores/modules/user'
6
-import { useGlobal } from '@/composables'
7
-import { validatorMobile, verifyPwd, validatorLoginMobile } from '@/utils/verification'
8
-import { loginApi, getSmsApi, weixinApi, getUserInfoApi, registerApi, forgetApi, phoneLoginApi } from '@/api/login'
9
-import Captcha from '@/base/components/Captcha.vue'
10
-import type { captchaInfo } from '@/types/login'
11
-import { getEnterpriseAPI } from '@/api/system'
12
-import type { useInfoResult } from '@/types/login'
13
-
14
-const userStore = user()
15
-const systemStore = system()
16
-// 表单信息
17
-const form = ref({
18
-  username: '',
19
-  password: '',
20
-  code: '',
21
-  phonenumber: '',
22
-  smsCode: '',
23
-  type: 2,
24
-  uuid: '',
25
-  openId: ''
26
-})
27
-
28
-// 表单校验
29
-const rules: UniHelper.UniFormsRules = {
30
-  username: {
31
-    rules: [
32
-      {
33
-        required: true,
34
-        errorMessage: '请输入手机号'
35
-      },
36
-      {
37
-        maxLength: useGlobal().textMax,
38
-        errorMessage: '限制{maxLength}个字符'
39
-      },
40
-      {
41
-        validateFunction: validatorLoginMobile
42
-      }
43
-    ]
44
-  },
45
-  password: {
46
-    rules: [
47
-      {
48
-        required: true,
49
-        errorMessage: '请输入密码'
50
-      }
51
-    ]
52
-  },
53
-  code: {
54
-    rules: [
55
-      {
56
-        required: true,
57
-        errorMessage: '请输入验证码'
58
-      }
59
-    ]
60
-  },
61
-  phonenumber: {
62
-    rules: [
63
-      {
64
-        required: true,
65
-        errorMessage: '请输入手机号'
66
-      },
67
-      {
68
-        validateFunction: validatorMobile
69
-      }
70
-    ]
71
-  },
72
-  smsCode: {
73
-    rules: [
74
-      {
75
-        required: true,
76
-        errorMessage: '请输入验证码'
77
-      }
78
-    ]
79
-  }
80
-}
81
-// 表单组件实例
82
-const formRef = ref<UniHelper.UniFormsInstance>()
83
-// 登录方式 1短信验证码登录 2密码登录
84
-const loginTypeChange = () => {
85
-  form.value.type = form.value.type === 1 ? 2 : 1
86
-}
87
-// 是否展示验证码
88
-let isShowCaptcha: boolean = true
89
-let uuid: string = ''
90
-const captchaRef = ref()
91
-const getCaptchaInfo = (val: captchaInfo) => {
92
-  isShowCaptcha = val.captchaEnabled
93
-  if (type.value === 1) {
94
-    uuid = val.uuid
95
-    form.value.code = val.code
96
-  } else {
97
-    registerForm.value.uuid = val.uuid
98
-    registerForm.value.code = val.code
99
-  }
100
-}
101
-// 短信验证码
102
-let isDisabled = ref(false)
103
-let countdown = ref(0)
104
-const getSmsCode = (type: number) => {
105
-  if (type === 1 && !form.value.phonenumber) {
106
-    useGlobal().tips('请输入手机号')
107
-    return
108
-  }
109
-  if (type === 2 && !pwdForm.value.phonenumber) {
110
-    useGlobal().tips('请输入手机号')
111
-    return
112
-  }
113
-  getSmsApi({ phonenumber: type === 1 ? form.value.phonenumber : pwdForm.value.phonenumber, type }).then(() => {
114
-    uni.showToast({
115
-      title: '操作成功',
116
-      icon: 'none'
117
-    })
118
-    isDisabled.value = true
119
-    countdown.value = 60
120
-    const timerInterval = setInterval(() => {
121
-      if (countdown.value > 1) {
122
-        countdown.value--
123
-      } else {
124
-        isDisabled.value = false
125
-        clearInterval(timerInterval)
126
-      }
127
-    }, 1000)
128
-  })
129
-}
130
-// 存储用户信息
131
-const refreshUserInfo = () => {
132
-  // 获取用户信息
133
-  getUserInfoApi().then((response) => {
134
-    const result = {
135
-      ...response?.data?.user,
136
-      secretary: 1
137
-    }
138
-    // 党员是否为书记
139
-    if (result.type & 32) {
140
-      result.secretary = response?.data?.secretary
141
-    }
142
-    // 存储用户信息
143
-    userStore.setUserInfo(result as useInfoResult)
144
-    // 存储权限
145
-    let permissions = []
146
-    if (response?.data?.permissions) {
147
-      for (let item of response.data.permissions) {
148
-        permissions.push(item)
149
-      }
150
-    }
151
-    userStore.setPermissions(permissions)
152
-    // 登录成功返回上一页
153
-    uni.navigateBack()
154
-  }).catch(() => {
155
-    uni.navigateBack()
156
-  })
157
-}
158
-// 登录
159
-const handleSubmit = async () => {
160
-  formRef.value?.validate?.((err, formData) => {
161
-    if (!err) {
162
-      let params = ref()
163
-      form.value.uuid = isShowCaptcha && form.value.type === 2 ? uuid : ''
164
-      if (isBind.value === 1) {
165
-        // 微信绑定
166
-        params.value = {
167
-          username: form.value.username,
168
-          password: form.value.password,
169
-          uuid: form.value.uuid,
170
-          openId: openId.value,
171
-          platform: 'pc'
172
-        }
173
-      } else {
174
-        params.value = form.value
175
-      }
176
-      uni.showLoading()
177
-      loginApi(params.value, isBind.value)
178
-        .then((res) => {
179
-          // 存储token
180
-          userStore.setToken(res?.data?.token)
181
-          setTimeout(() => {
182
-            refreshUserInfo()
183
-          }, 100)
184
-        })
185
-        .catch((err) => {
186
-          captchaRef.value?.getCaptchaImage()
187
-        })
188
-    }
189
-  })
190
-  // try {
191
-  //   // 表单校验
192
-  //   await formRef.value?.validate?.()
193
-  //   // 校验通过后再发送请求
194
-  // } catch (error) {
195
-  //   //
196
-  // }
197
-}
198
-// 清空密码
199
-const handleClear = () => {
200
-  form.value.password = ''
201
-}
202
-// 注册
203
-const handleRegister = () => {
204
-  uni.navigateTo({ url: '/base/register' })
205
-}
206
-// 忘记密码
207
-const handleForget = () => {
208
-  uni.navigateTo({ url: '/base/forget' })
209
-}
210
-// 微信登录
211
-let openId = ref('')
212
-// 微信是否绑定系统用户 1未绑定 0已绑定
213
-let isBind = ref(0)
214
-const handleUserProfile = () => {
215
-  uni.showLoading()
216
-  uni.login({
217
-    provider: 'weixin',
218
-    success: function (res) {
219
-      weixinApi({ code: res.code }).then((result) => {
220
-        console.log(result, 'res.code')
221
-        if (result.code === 409) {
222
-          // 用户不存在需要用户手动绑定
223
-          uni.showToast({
224
-            title: '微信账号未绑定系统用户,请先绑定',
225
-            icon: 'none',
226
-            duration: 3000
227
-          })
228
-          isBind.value = 1
229
-          openId.value = result.msg
230
-        } else {
231
-          isBind.value = 0
232
-          // 存储token
233
-          userStore.setToken(result?.data?.token)
234
-          // 返回用户token,正常存储登录
235
-          setTimeout(() => {
236
-            refreshUserInfo()
237
-          }, 100)
238
-        }
239
-      })
240
-    },
241
-    complete: function () {
242
-      uni.hideLoading()
243
-    }
244
-  })
245
-}
246
-
247
-// 注册
248
-// 表单校验
249
-const registerRules: UniHelper.UniFormsRules = {
250
-  // username: {
251
-  //   rules: [
252
-  //     {
253
-  //       required: true,
254
-  //       errorMessage: '请输入用户名'
255
-  //     },
256
-  //     {
257
-  //       maxLength: useGlobal().textMax,
258
-  //       errorMessage: '限制{maxLength}个字符'
259
-  //     }
260
-  //   ]
261
-  // },
262
-  realname: {
263
-    rules: [
264
-      {
265
-        required: true,
266
-        errorMessage: '请输入姓名'
267
-      },
268
-      {
269
-        maxLength: useGlobal().textMax,
270
-        errorMessage: '限制{maxLength}个字符'
271
-      }
272
-    ]
273
-  },
274
-  password: {
275
-    rules: [
276
-      {
277
-        required: true,
278
-        errorMessage: '请输入密码'
279
-      },
280
-      {
281
-        validateFunction: verifyPwd
282
-      }
283
-    ]
284
-  },
285
-  passwordAgain: {
286
-    rules: [
287
-      {
288
-        required: true,
289
-        errorMessage: '请再次输入密码'
290
-      },
291
-      {
292
-        validateFunction: function (rule, value, data, callback) {
293
-          if (value !== data.password) {
294
-            callback('两次输入密码不一致')
295
-          }
296
-          return true
297
-        }
298
-      }
299
-    ]
300
-  },
301
-  code: {
302
-    rules: [
303
-      {
304
-        required: true,
305
-        errorMessage: '请输入验证码'
306
-      }
307
-    ]
308
-  },
309
-  mobile: {
310
-    rules: [
311
-      {
312
-        required: true,
313
-        errorMessage: '请输入手机号'
314
-      },
315
-      {
316
-        validateFunction: validatorMobile
317
-      }
318
-    ]
319
-  }
320
-}
321
-// 表单组件实例
322
-const registerRef = ref<UniHelper.UniFormsInstance>()
323
-// 表单信息
324
-const registerForm = ref({
325
-  // username: '',
326
-  realname: '',
327
-  password: '',
328
-  mobile: '',
329
-  uuid: '',
330
-  code: '',
331
-  passwordAgain: ''
332
-})
333
-
334
-const getRegisterCaptchaInfo = (val: captchaInfo) => {
335
-  isShowCaptcha = val.captchaEnabled
336
-  registerForm.value.uuid = val.uuid
337
-  registerForm.value.code = val.code
338
-}
339
-
340
-const handleRegisterSubmit = () => {
341
-  registerRef.value?.validate?.((err, formData) => {
342
-    if (!err) {
343
-      uni.showLoading()
344
-      registerApi(registerForm.value)
345
-        .then(() => {
346
-          // 注册成功,返回登录
347
-          uni.showToast({
348
-            title: '操作成功',
349
-            icon: 'none'
350
-          })
351
-          type.value = 1
352
-        })
353
-        .catch(() => {
354
-          captchaRef.value?.getCaptchaImage()
355
-        })
356
-    }
357
-  })
358
-}
359
-
360
-// 忘记密码
361
-// 表单信息
362
-const pwdForm = ref({
363
-  passwordAgain: '',
364
-  password: '',
365
-  phonenumber: '',
366
-  smsCode: ''
367
-})
368
-// 表单校验
369
-const pwdRules: UniHelper.UniFormsRules = {
370
-  password: {
371
-    rules: [
372
-      {
373
-        required: true,
374
-        errorMessage: '请输入密码'
375
-      },
376
-      {
377
-        validateFunction: verifyPwd
378
-      }
379
-    ]
380
-  },
381
-  passwordAgain: {
382
-    rules: [
383
-      {
384
-        required: true,
385
-        errorMessage: '请再次输入密码'
386
-      },
387
-      {
388
-        validateFunction: function (rule, value, data, callback) {
389
-          if (value !== data.password) {
390
-            callback('两次输入密码不一致')
391
-          }
392
-          return true
393
-        }
394
-      }
395
-    ]
396
-  },
397
-  phonenumber: {
398
-    rules: [
399
-      {
400
-        required: true,
401
-        errorMessage: '请输入手机号'
402
-      },
403
-      {
404
-        validateFunction: validatorMobile
405
-      }
406
-    ]
407
-  },
408
-  smsCode: {
409
-    rules: [
410
-      {
411
-        required: true,
412
-        errorMessage: '请输入验证码'
413
-      }
414
-    ]
415
-  }
416
-}
417
-const pwdFormRef = ref<UniHelper.UniFormsInstance>()
418
-const handlePwdSubmit = async () => {
419
-  pwdFormRef.value?.validate?.((err, formData) => {
420
-    if (!err) {
421
-      uni.showLoading()
422
-      forgetApi(form.value).then(() => {
423
-        // 注册成功,返回登录
424
-        uni.showToast({
425
-          title: '操作成功',
426
-          icon: 'none'
427
-        })
428
-        type.value = 1
429
-      })
430
-    }
431
-  })
432
-}
433
-
434
-let type = ref(1) // 1登录 2注册
435
-const changeType = (index: number) => {
436
-  type.value = index
437
-  if (index === 2) {
438
-    registerForm.value = {
439
-      // username: '',
440
-      realname: '',
441
-      password: '',
442
-      mobile: '',
443
-      uuid: '',
444
-      code: ''
445
-    }
446
-    registerRef.value?.setRules?.(registerRules)
447
-    registerRef.value?.clearValidate()
448
-  } else if (type.value === 3) {
449
-    pwdForm.value = {
450
-      passwordAgain: '',
451
-      password: '',
452
-      phonenumber: '',
453
-      smsCode: ''
454
-    }
455
-    pwdFormRef.value?.setRules?.(pwdRules)
456
-    pwdFormRef.value?.clearValidate()
457
-  } else {
458
-    form.value = {
459
-      username: '',
460
-      password: '',
461
-      code: '',
462
-      phonenumber: '',
463
-      smsCode: '',
464
-      type: 2,
465
-      uuid: '',
466
-      openId: ''
467
-    }
468
-    formRef.value?.clearValidate()
469
-  }
470
-}
471
-// 获取手机号
472
-const getphonenumber = (val: { detail: { code: string } }) => {
473
-  if (val.detail && val.detail.code) {
474
-    uni.showLoading()
475
-    phoneLoginApi({ code: val.detail.code }).then((res) => {
476
-      // 存储token
477
-      userStore.setToken(res?.data?.token)
478
-      setTimeout(() => {
479
-        refreshUserInfo()
480
-      }, 100)
481
-    })
482
-  }
483
-}
484
-// 取消绑定
485
-const cancelBind = () => {
486
-  isBind.value = 0
487
-}
488
-
489
-onLoad(async () => {
490
-  await systemStore.getConfigData()
491
-})
492
-onReady(() => {
493
-  formRef.value?.setRules?.(rules)
494
-})
495
-// 下拉刷新
496
-// onPullDownRefresh(async () => {
497
-//   await systemStore.getConfigData()
498
-//   uni.stopPullDownRefresh()
499
-// })
500
-</script>
501
-
502
-<template>
503
-  <view class="login-box">
504
-    <view class="login-top flex a-ccenter f20 white-color f-wrap pl20">
505
-      <view class="w100">您好,</view>
506
-      <view>欢迎使用{{ systemStore.config.name || '智慧园区管理系统' }}</view>
507
-    </view>
508
-    <view class="flex j-sb a-c w100 login-action">
509
-      <view v-if="type < 3" class="w50 tc f14" :class="[type === 1 && 'weight-600']" @click="changeType(1)">登录</view>
510
-      <view v-if="type < 3 && systemStore.config.register === 'true'" class="w50 tc f14" :class="[type === 2 && 'weight-600']" @click="changeType(2)"
511
-        >注册</view
512
-      >
513
-      <view v-if="type === 3" class="w50 tc f14 weight-600">忘记密码</view>
514
-    </view>
515
-    <view v-if="type === 1 || type === 3" class="triangle triangle-left"></view>
516
-    <view v-if="type === 2" class="triangle triangle-right"></view>
517
-    <view class="center-box">
518
-      <!-- 登录 -->
519
-      <uni-forms v-if="type === 1" ref="formRef" :model="form" :rules="rules" label-position="top" label-width="0">
520
-        <!-- 短信登录 -->
521
-        <template v-if="form.type === 1">
522
-          <uni-forms-item name="phonenumber">
523
-            <template #label>
524
-              <view class="form-title">
525
-                <uni-icons fontFamily="iconfont" color="#2E62AF" size="14">&#xe6d9;</uni-icons>
526
-                手机号
527
-              </view>
528
-            </template>
529
-            <uni-easyinput type="text" v-model="form.phonenumber" placeholder="请输入手机号" />
530
-          </uni-forms-item>
531
-          <uni-forms-item name="smsCode">
532
-            <template #label>
533
-              <view class="form-title">
534
-                <uni-icons fontFamily="iconfont" color="#2E62AF" size="14">&#xe619;</uni-icons>
535
-                验证码
536
-              </view>
537
-            </template>
538
-            <view class="flex j-sb a-center w100">
539
-              <uni-easyinput type="text" v-model="form.smsCode" placeholder="请输入验证码" />
540
-              <view v-if="isDisabled" class="sms-btn weight-600 gray-color">
541
-                {{ countdown + 's后重新获取' }}
542
-              </view>
543
-              <view v-else class="sms-btn weight-600 primary-color" @tap="getSmsCode(1)"> 获取验证码 </view>
544
-            </view>
545
-          </uni-forms-item>
546
-        </template>
547
-        <!-- 密码登录|微信绑定 -->
548
-        <template v-if="form.type === 2 || isBind">
549
-          <uni-forms-item name="username">
550
-            <template #label>
551
-              <view class="form-title">
552
-                <uni-icons fontFamily="iconfont" color="#2E62AF" size="14">&#xe6d9;</uni-icons>
553
-                手机号
554
-              </view>
555
-            </template>
556
-            <uni-easyinput type="text" v-model="form.username" placeholder="请输入手机号" @clear="handleClear" />
557
-          </uni-forms-item>
558
-          <uni-forms-item name="password">
559
-            <template #label>
560
-              <view class="form-title">
561
-                <uni-icons fontFamily="iconfont" color="#2E62AF" size="14">&#xe638;</uni-icons>
562
-                密码
563
-              </view>
564
-            </template>
565
-            <uni-easyinput type="password" v-model="form.password" placeholder="请输入密码" />
566
-          </uni-forms-item>
567
-          <uni-forms-item v-if="isShowCaptcha && systemStore.config.captcha === 'true' && !isBind" name="code">
568
-            <template #label>
569
-              <view class="form-title">
570
-                <uni-icons fontFamily="iconfont" color="#2E62AF" size="14">&#xe619;</uni-icons>
571
-                验证码
572
-              </view>
573
-            </template>
574
-            <Captcha ref="captchaRef" @getCaptchaInfo="getCaptchaInfo" class="w100" />
575
-          </uni-forms-item>
576
-        </template>
577
-      </uni-forms>
578
-      <!-- 注册 -->
579
-      <uni-forms v-if="type === 2" ref="registerRef" :model="registerForm" :rules="registerRules" label-position="top" label-width="0">
580
-        <uni-forms-item name="mobile">
581
-          <template #label>
582
-            <view class="form-title">
583
-              <uni-icons fontFamily="iconfont" color="#2E62AF" size="14">&#xe6d9;</uni-icons>
584
-              手机号
585
-            </view>
586
-          </template>
587
-          <uni-easyinput type="text" v-model="registerForm.mobile" placeholder="请输入手机号" />
588
-        </uni-forms-item>
589
-        <!-- <uni-forms-item name="username">
590
-          <template #label>
591
-            <view class="form-title">
592
-              <uni-icons type="person" color="#2E62AF" size="14" />
593
-              用户名
594
-            </view>
595
-          </template>
596
-          <uni-easyinput type="text" v-model="registerForm.username" placeholder="请输入用户名" />
597
-        </uni-forms-item> -->
598
-        <uni-forms-item name="realname">
599
-          <template #label>
600
-            <view class="form-title">
601
-              <uni-icons type="person-filled" color="#2E62AF" size="14" />
602
-              姓名
603
-            </view>
604
-          </template>
605
-          <uni-easyinput type="text" v-model="registerForm.realname" placeholder="请输入姓名" />
606
-        </uni-forms-item>
607
-        <uni-forms-item name="password">
608
-          <template #label>
609
-            <view class="form-title">
610
-              <uni-icons fontFamily="iconfont" color="#2E62AF" size="14">&#xe638;</uni-icons>
611
-              密码
612
-            </view>
613
-          </template>
614
-          <uni-easyinput type="password" v-model="registerForm.password" placeholder="请输入密码" />
615
-        </uni-forms-item>
616
-        <uni-forms-item name="passwordAgain">
617
-          <template #label>
618
-            <view class="form-title">
619
-              <uni-icons fontFamily="iconfont" color="#2E62AF" size="14">&#xe638;</uni-icons>
620
-              确认密码
621
-            </view>
622
-          </template>
623
-          <uni-easyinput type="password" v-model="registerForm.passwordAgain" placeholder="请再次输入密码" />
624
-        </uni-forms-item>
625
-        <uni-forms-item v-if="isShowCaptcha && systemStore.config.captcha === 'true'" name="code">
626
-          <template #label>
627
-            <view class="form-title">
628
-              <uni-icons fontFamily="iconfont" color="#2E62AF" size="14">&#xe619;</uni-icons>
629
-              验证码
630
-            </view>
631
-          </template>
632
-          <Captcha ref="captchaRef" @getCaptchaInfo="getCaptchaInfo" class="w100" />
633
-        </uni-forms-item>
634
-      </uni-forms>
635
-      <!-- 忘记密码 -->
636
-      <uni-forms v-if="type === 3" ref="pwdFormRef" :model="pwdForm" :rules="pwdRules" label-position="top" label-width="0">
637
-        <uni-forms-item name="phonenumber">
638
-          <template #label>
639
-            <view class="form-title">
640
-              <uni-icons fontFamily="iconfont" color="#2E62AF" size="14">&#xe6d9;</uni-icons>
641
-              手机号
642
-            </view>
643
-          </template>
644
-          <uni-easyinput type="text" v-model="pwdForm.phonenumber" placeholder="请输入手机号" />
645
-        </uni-forms-item>
646
-        <uni-forms-item name="smsCode">
647
-          <template #label>
648
-            <view class="form-title">
649
-              <uni-icons fontFamily="iconfont" color="#2E62AF" size="14">&#xe619;</uni-icons>
650
-              验证码
651
-            </view>
652
-          </template>
653
-          <view class="flex j-sb a-center w100">
654
-            <uni-easyinput type="text" v-model="pwdForm.smsCode" placeholder="请输入验证码" />
655
-            <view v-if="isDisabled" class="sms-btn weight-600 gray-color">
656
-              {{ countdown + 's后重新获取' }}
657
-            </view>
658
-            <view v-else class="sms-btn weight-600 primary-color" @tap="getSmsCode(2)"> 获取验证码 </view>
659
-          </view>
660
-        </uni-forms-item>
661
-        <uni-forms-item name="password">
662
-          <template #label>
663
-            <view class="form-title">
664
-              <uni-icons fontFamily="iconfont" color="#2E62AF" size="14">&#xe638;</uni-icons>
665
-              新密码
666
-            </view>
667
-          </template>
668
-          <uni-easyinput type="password" v-model="pwdForm.password" placeholder="请输入密码" />
669
-        </uni-forms-item>
670
-        <uni-forms-item name="passwordAgain">
671
-          <template #label>
672
-            <view class="form-title">
673
-              <uni-icons fontFamily="iconfont" color="#2E62AF" size="14">&#xe638;</uni-icons>
674
-              确认密码
675
-            </view>
676
-          </template>
677
-          <uni-easyinput type="password" v-model="pwdForm.passwordAgain" placeholder="请再次输入新密码" />
678
-        </uni-forms-item>
679
-      </uni-forms>
680
-      <!-- 登录操作 -->
681
-      <template v-if="type === 1">
682
-        <view v-if="systemStore.config.sms === 'true'" class="f12 mb20 mt10 primary-color weight-600 flex a-c j-sb">
683
-          <text @tap="loginTypeChange">{{ form.type === 2 ? '短信验证码登录' : '密码登录' }}</text>
684
-          <!-- <text @tap="handleForget">忘记密码</text> -->
685
-          <text @tap="changeType(3)">忘记密码</text>
686
-        </view>
687
-        <view class="btn-b gradual-primary" @tap="handleSubmit">{{ isBind ? '绑 定' : '登 录' }}</view>
688
-        <!-- #ifdef MP-WEIXIN -->
689
-        <view v-if="!isBind" class="tc m10 gray-color f12">OR</view>
690
-        <view v-if="!isBind" class="btn-b plain-bg primary-color mb10" @tap="handleUserProfile">微信一键登录</view>
691
-        <!-- #endif -->
692
-        <button v-if="!isBind" type="primary" open-type="getPhoneNumber" @getphonenumber="getphonenumber" class="btn-b plain-bg primary-color">
693
-          手机号登录
694
-        </button>
695
-        <view v-if="isBind" class="f12 mt20 primary-color weight-600" @click="cancelBind"
696
-          ><uni-icons type="left" size="12" color="#2e62af"></uni-icons>返回</view
697
-        >
698
-      </template>
699
-      <!-- 注册 -->
700
-      <view v-if="type === 2" class="btn-b gradual-primary" @tap="handleRegisterSubmit">确 定</view>
701
-      <!-- 忘记密码 -->
702
-      <template v-if="type === 3">
703
-        <view class="f12 mb20 mt10 primary-color weight-600 flex a-c j-sb">
704
-          <text @tap="changeType(1)">返回</text>
705
-        </view>
706
-        <view class="btn-b gradual-primary" @tap="handlePwdSubmit">确 定</view>
707
-      </template>
708
-    </view>
709
-    <!-- <view v-if="type === 1" class="gray-color f12 tc w100 agreement"> 登录即代表已阅读并同意<text class="primary-color">《用户协议》</text></view> -->
710
-  </view>
711
-</template>
712
-
713
-<style lang="scss" scoped>
714
-.login-box {
715
-  width: 100vw;
716
-  height: 100vh;
717
-  position: relative;
718
-  background: #fff;
719
-  .login-top {
720
-    height: 550rpx;
721
-    background: url('http://1.94.6.75:9000/zlhy-app/bg_activity.jpg') center no-repeat;
722
-    background-size: 100% 100%;
723
-  }
724
-  .login-action {
725
-    position: absolute;
726
-    left: 0;
727
-    top: 400rpx;
728
-    color: #fff;
729
-  }
730
-  :deep(.center-box) {
731
-    // width: calc(100vw - 100rpx);
732
-    width: 100%;
733
-    min-height: 200px;
734
-    border-radius: 50rpx;
735
-    background: #fff;
736
-    position: absolute;
737
-    left: 0;
738
-    top: 480rpx;
739
-    padding: 50rpx;
740
-    .btn-b {
741
-      width: 100% !important;
742
-    }
743
-    .is-input-border {
744
-      border-radius: 0;
745
-      border: 0;
746
-      border-bottom: 1px solid #dcdfe6;
747
-    }
748
-    .form-title {
749
-      font-size: 28rpx;
750
-      font-weight: bold;
751
-    }
752
-  }
753
-  .triangle {
754
-    width: 0;
755
-    height: 0;
756
-    border-left: 100rpx solid #fff;
757
-    border-top: 200rpx solid #fff;
758
-    border-right: 200rpx solid transparent;
759
-    border-bottom: 100rpx solid transparent;
760
-    transform: rotate(45deg);
761
-    border-top-left-radius: 5rpx;
762
-    position: absolute;
763
-    top: 520rpx;
764
-  }
765
-  .triangle-left {
766
-    left: 37rpx;
767
-  }
768
-  .triangle-right {
769
-    right: 37rpx;
770
-  }
771
-  .sms-btn {
772
-    height: 35px;
773
-    line-height: 35px;
774
-    border-bottom: 1px solid #dcdfe6;
775
-    border-radius: 0 !important;
776
-    font-size: 24rpx;
777
-  }
778
-  .forget {
779
-    .line {
780
-      width: 1px;
781
-      height: 10px;
782
-      background-color: #333;
783
-    }
784
-  }
785
-  .agreement {
786
-    position: absolute;
787
-    bottom: 50rpx;
788
-    left: 0;
789
-  }
790
-}
791
-</style>

+ 0 - 177
src/base/register.vue

@@ -1,177 +0,0 @@
1
-<!--
2
- * @Author: wyd
3
- * @Date: 2024-03
4
- * @LastEditors: wyd
5
- * @LastEditTime: 2024-05
6
- * @Description: 注册
7
--->
8
-<script setup lang="ts">
9
-import { ref } from 'vue'
10
-import { onLoad, onPullDownRefresh, onReady } from '@dcloudio/uni-app'
11
-import { system } from '@/stores/modules/system'
12
-import { useGlobal } from '@/composables'
13
-import { validatorMobile, verifyPwd } from '@/utils/verification'
14
-import { registerApi } from '@/api/login'
15
-import Captcha from '@/base/components/Captcha.vue'
16
-import type { captchaInfo } from '@/types/login'
17
-
18
-const systemStore = system()
19
-// 表单信息
20
-const form = ref({
21
-  // username: '',
22
-  realname: '',
23
-  password: '',
24
-  mobile: '',
25
-  uuid: '',
26
-  code: ''
27
-})
28
-// 表单校验
29
-const rules: UniHelper.UniFormsRules = {
30
-  // username: {
31
-  //   rules: [
32
-  //     {
33
-  //       required: true,
34
-  //       errorMessage: '请输入用户名'
35
-  //     },
36
-  //     {
37
-  //       maxLength: useGlobal().textMax,
38
-  //       errorMessage: '限制{maxLength}个字符'
39
-  //     }
40
-  //   ]
41
-  // },
42
-  realname: {
43
-    rules: [
44
-      {
45
-        required: true,
46
-        errorMessage: '请输入姓名'
47
-      },
48
-      {
49
-        maxLength: useGlobal().textMax,
50
-        errorMessage: '限制{maxLength}个字符'
51
-      }
52
-    ]
53
-  },
54
-  password: {
55
-    rules: [
56
-      {
57
-        required: true,
58
-        errorMessage: '请输入密码'
59
-      },
60
-      {
61
-        validateFunction: verifyPwd
62
-      }
63
-    ]
64
-  },
65
-  code: {
66
-    rules: [
67
-      {
68
-        required: true,
69
-        errorMessage: '请输入验证码'
70
-      }
71
-    ]
72
-  },
73
-  mobile: {
74
-    rules: [
75
-      {
76
-        required: true,
77
-        errorMessage: '请输入手机号'
78
-      },
79
-      {
80
-        validateFunction: validatorMobile
81
-      }
82
-    ]
83
-  }
84
-}
85
-// 表单组件实例
86
-const formRef = ref<UniHelper.UniFormsInstance>()
87
-
88
-// 是否展示验证码
89
-let isShowCaptcha: boolean = true
90
-const captchaRef = ref()
91
-const getCaptchaInfo = (val: captchaInfo) => {
92
-  isShowCaptcha = val.captchaEnabled
93
-  form.value.uuid = val.uuid
94
-  form.value.code = val.code
95
-}
96
-
97
-const handleSubmit = async () => {
98
-  formRef.value?.validate?.((err, formData) => {
99
-    if (!err) {
100
-      uni.showLoading()
101
-      registerApi(form.value)
102
-        .then(() => {
103
-          // 注册成功,返回上一页
104
-          uni.showToast({
105
-            title: '操作成功',
106
-            icon: 'none'
107
-          })
108
-          setTimeout(() => {
109
-            uni.navigateBack()
110
-          }, 1000)
111
-        })
112
-        .catch((err) => {
113
-          captchaRef.value?.getCaptchaImage()
114
-        })
115
-    }
116
-  })
117
-}
118
-onLoad(async () => {
119
-  await systemStore.getConfigData()
120
-})
121
-onReady(() => {
122
-  formRef.value?.setRules?.(rules)
123
-})
124
-// 下拉刷新
125
-onPullDownRefresh(async () => {
126
-  await systemStore.getConfigData()
127
-  uni.stopPullDownRefresh()
128
-})
129
-</script>
130
-
131
-<template>
132
-  <view class="login-box">
133
-    <view class="center-box p15 shadow">
134
-      <view class="tc f18 weight-600 black-color mb20">注册</view>
135
-      <uni-forms ref="formRef" :model="form" :rules="rules">
136
-        <uni-forms-item name="mobile">
137
-          <uni-easyinput type="text" v-model="form.mobile" placeholder="请输入手机号" />
138
-        </uni-forms-item>
139
-        <!-- <uni-forms-item name="username">
140
-          <uni-easyinput type="text" v-model="form.username" placeholder="请输入用户名" />
141
-        </uni-forms-item> -->
142
-        <uni-forms-item name="realname">
143
-          <uni-easyinput type="text" v-model="form.realname" placeholder="请输入姓名" />
144
-        </uni-forms-item>
145
-        <uni-forms-item name="password">
146
-          <uni-easyinput type="password" v-model="form.password" placeholder="请输入密码" />
147
-        </uni-forms-item>
148
-        <uni-forms-item v-if="isShowCaptcha && systemStore.config.captcha === 'true'" name="code">
149
-          <Captcha ref="captchaRef" @getCaptchaInfo="getCaptchaInfo" class="w100" />
150
-        </uni-forms-item>
151
-      </uni-forms>
152
-      <view class="btn-b warning-bg" @tap="handleSubmit">确 定</view>
153
-    </view>
154
-  </view>
155
-</template>
156
-
157
-<style lang="scss" scoped>
158
-.login-box {
159
-  width: 100vw;
160
-  height: 100vh;
161
-  background: url('http://1.94.6.75:9000/zlhy-app/bg_login.jpg') center no-repeat;
162
-  background-size: 100% 100%;
163
-  position: relative;
164
-  .center-box {
165
-    width: calc(100vw - 60px);
166
-    min-height: 200px;
167
-    border-radius: 10rpx;
168
-    background: rgba($color: #fff, $alpha: 0.8);
169
-    position: absolute;
170
-    left: 15px;
171
-    top: 20%;
172
-    .btn-b {
173
-      width: 100% !important;
174
-    }
175
-  }
176
-}
177
-</style>

+ 171 - 0
src/base/suggest/index.vue

@@ -0,0 +1,171 @@
1
+<template>
2
+  <view class="suggest-container">
3
+    <view class="suggest-header-wrapper" :style="{ backgroundImage: `url(${minioUrl}/suggest_bg.png)` }">
4
+      <view class="suggest-tips">
5
+        <view class="title">尊敬的顾客</view>
6
+        <view class="sub-title">欢迎您对我们服务区提出建议反馈,我们会对您的问题进行全程跟踪</view>
7
+      </view>
8
+      <image class="header-logo" :src="minioUrl + '/icon_kefubz_01.png'" mode="scaleToFill" />
9
+    </view>
10
+    <view class="suggest-block-wrapper">
11
+      <view class="block-item" @click="toSuggest">
12
+        <view class="b-title">投诉建议</view>
13
+        <view class="b-sub-title">提交您建议,将有专业人士为您跟进处理</view>
14
+        <image class="bottom-img" :src="minioUrl + '/icon_kefubz_02.png'" mode="scaleToFill" />
15
+      </view>
16
+      <view class="block-item" @click="toRecord">
17
+        <view class="b-title">反馈记录</view>
18
+        <view class="b-sub-title">查看您的反馈记录</view>
19
+        <image class="bottom-img" :src="minioUrl + '/icon_kefubz_3.png'" mode="scaleToFill" />
20
+      </view>
21
+    </view>
22
+    <HyCard title="常见问题">
23
+      <view class="suggest-list-wrapper">
24
+        <view class="list-item" v-for="index in 3" :key="index">
25
+          <view class="text">我想要修车,怎么联系服务区修车店?</view>
26
+          <image :src="minioUrl + '/icon_kefubz_05.png'" mode="scaleToFill" />
27
+        </view>
28
+        <view class="refresh-wrap">
29
+          <image :src="minioUrl + '/icon_kefubz_04.png'" mode="scaleToFill" />
30
+          <text>换一换</text>
31
+        </view>
32
+      </view>
33
+    </HyCard>
34
+  </view>
35
+</template>
36
+
37
+<script lang="ts" setup>
38
+import { ref } from 'vue'
39
+import { useGlobal } from '@/composables/index'
40
+
41
+const { minioUrl } = useGlobal()
42
+
43
+const toSuggest = () => {
44
+  uni.navigateTo({
45
+    url: '/base/suggest/suggest'
46
+  })
47
+}
48
+
49
+const toRecord = () => {
50
+  uni.navigateTo({
51
+    url: '/base/suggest/record'
52
+  })
53
+}
54
+</script>
55
+
56
+<style lang="scss" scoped>
57
+.suggest-container {
58
+  width: 100%;
59
+  box-sizing: border-box;
60
+  .suggest-header-wrapper {
61
+    width: 100%;
62
+    height: 380rpx;
63
+    background-position: center center;
64
+    background-repeat: no-repeat;
65
+    background-size: cover;
66
+    display: flex;
67
+    box-sizing: border-box;
68
+    padding: 70rpx 0 0 56rpx;
69
+    .header-logo {
70
+      width: 338rpx;
71
+      height: 198rpx;
72
+    }
73
+    .suggest-tips {
74
+      .title {
75
+        font-size: 28rpx;
76
+        color: #333;
77
+        font-weight: 600;
78
+        line-height: 21rpx;
79
+        margin-bottom: 24rpx;
80
+      }
81
+      .sub-title {
82
+        font-size: 21rpx;
83
+        color: #333;
84
+        font-weight: 400;
85
+        width: 338rpx;
86
+        line-height: 33rpx;
87
+      }
88
+    }
89
+  }
90
+  .suggest-block-wrapper {
91
+    width: 100%;
92
+    display: flex;
93
+    align-items: center;
94
+    justify-content: center;
95
+    margin-top: -90rpx;
96
+    margin-bottom: 30rpx;
97
+    .block-item {
98
+      width: 333rpx;
99
+      height: 179rpx;
100
+      border-radius: 14rpx;
101
+      background-color: #fff;
102
+      box-sizing: border-box;
103
+      padding: 29rpx 0 0 36rpx;
104
+      position: relative;
105
+      &:nth-child(2) {
106
+        margin-left: 24rpx;
107
+        .bottom-img {
108
+          width: 69rpx;
109
+          height: 74rpx;
110
+        }
111
+      }
112
+      .bottom-img {
113
+        position: absolute;
114
+        bottom: 19rpx;
115
+        right: 17rpx;
116
+        width: 77rpx;
117
+        height: 65rpx;
118
+      }
119
+      .b-title {
120
+        font-size: 31rpx;
121
+        color: #333;
122
+        margin-bottom: 10rpx;
123
+      }
124
+      .b-sub-title {
125
+        font-size: 17rpx;
126
+        width: 166rpx;
127
+      }
128
+    }
129
+  }
130
+  :deep(.hy-card-container) {
131
+    width: 694rpx;
132
+    margin: 0 auto;
133
+  }
134
+  .suggest-list-wrapper {
135
+    margin-top: 11rpx;
136
+    .list-item {
137
+      width: 100%;
138
+      height: 77rpx;
139
+      display: flex;
140
+      align-items: center;
141
+      justify-content: space-between;
142
+      box-sizing: border-box;
143
+      border-bottom: 1rpx solid #f4f5f9;
144
+      .text {
145
+        font-size: 25rpx;
146
+        color: #333;
147
+      }
148
+      image {
149
+        width: 9rpx;
150
+        height: 15rpx;
151
+      }
152
+    }
153
+    .refresh-wrap {
154
+      display: flex;
155
+      align-items: center;
156
+      justify-content: center;
157
+      width: 100%;
158
+      margin-top: 20rpx;
159
+      image {
160
+        width: 24rpx;
161
+        height: 24rpx;
162
+      }
163
+      text {
164
+        font-size: 25rpx;
165
+        color: #4e6cef;
166
+        margin-left: 10rpx;
167
+      }
168
+    }
169
+  }
170
+}
171
+</style>

+ 7 - 0
src/base/suggest/record.vue

@@ -0,0 +1,7 @@
1
+<template>
2
+  <view></view>
3
+</template>
4
+
5
+<script lang="ts" setup></script>
6
+
7
+<style lang="scss" scoped></style>

+ 7 - 0
src/base/suggest/suggest.vue

@@ -0,0 +1,7 @@
1
+<template>
2
+  <view></view>
3
+</template>
4
+
5
+<script lang="ts" setup></script>
6
+
7
+<style lang="scss" scoped></style>

+ 82 - 0
src/components/HyCard.vue

@@ -0,0 +1,82 @@
1
+<template>
2
+  <view
3
+    class="hy-card-container"
4
+    :class="isBg ? 'bg-card' : ''"
5
+    :style="{ marginBottom: marginBottom + 'rpx', backgroundImage: isBg ? 'url(' + bgImg + ')' : 'none' }"
6
+  >
7
+    <view class="main-title-box" v-if="title">
8
+      <view class="text">{{ title }}</view>
9
+      <view v-if="isShowRight" class="right-text-wrap">
10
+        <text>{{ rightText }}</text>
11
+        <u-icon name="arrow-right-double" size="21" color="#292e36"></u-icon>
12
+      </view>
13
+    </view>
14
+    <slot></slot>
15
+  </view>
16
+</template>
17
+
18
+<script lang="ts" setup>
19
+import { ref } from 'vue'
20
+
21
+const props = defineProps({
22
+  title: {
23
+    type: String,
24
+    default: ''
25
+  },
26
+  marginBottom: {
27
+    type: Number,
28
+    default: 24
29
+  },
30
+  isShowRight: {
31
+    type: Boolean,
32
+    default: false
33
+  },
34
+  rightText: {
35
+    type: String,
36
+    default: '更多'
37
+  },
38
+  isBg: {
39
+    type: Boolean,
40
+    default: false
41
+  },
42
+  bgImg: {
43
+    type: String,
44
+    default: ''
45
+  }
46
+})
47
+</script>
48
+
49
+<style lang="scss" scoped>
50
+.hy-card-container {
51
+  width: 100%;
52
+  box-sizing: border-box;
53
+  padding: 22rpx;
54
+  background-color: #fff;
55
+  border-radius: 14rpx;
56
+  &.bg-card {
57
+    width: 100%;
58
+    min-height: 549rpx;
59
+    background-position: center center;
60
+    background-size: 100% 100%;
61
+    background-repeat: no-repeat;
62
+  }
63
+  .main-title-box {
64
+    display: flex;
65
+    align-items: center;
66
+    justify-content: space-between;
67
+    .text {
68
+      font-size: 31rpx;
69
+      color: #333;
70
+      font-weight: 600;
71
+    }
72
+    .right-text-wrap {
73
+      display: flex;
74
+      align-items: center;
75
+      text {
76
+        font-size: 21rpx;
77
+        color: #292e36;
78
+      }
79
+    }
80
+  }
81
+}
82
+</style>

+ 8 - 4
src/components/HyService.vue

@@ -1,10 +1,10 @@
1 1
 <template>
2 2
   <view class="service-item-wrapper">
3 3
     <view class="item-left-wrap">
4
-      <image src="/static/images/icon_service_01.png" mode="scaleToFill" />
4
+      <image :src="minioUrl + '/icon_service_01.png'" mode="scaleToFill" />
5 5
       <text>停车场</text>
6 6
     </view>
7
-    <view class="item-right-wrap">
7
+    <view class="item-right-wrap" :style="{ backgroundImage: `url(${minioUrl}/bg_service_03.png)` }">
8 8
       <!-- <view class="main-text">淋浴/洗衣</view> -->
9 9
       <view class="block-content">
10 10
         <view class="label">空闲</view>
@@ -17,7 +17,10 @@
17 17
   </view>
18 18
 </template>
19 19
 
20
-<script lang="ts" setup></script>
20
+<script lang="ts" setup>
21
+import { useGlobal } from '@/composables/index'
22
+const { minioUrl } = useGlobal()
23
+</script>
21 24
 
22 25
 <style lang="scss" scoped>
23 26
 .service-item-wrapper {
@@ -48,7 +51,8 @@
48 51
   .item-right-wrap {
49 52
     width: 157rpx;
50 53
     height: 52rpx;
51
-    background: url('/static/images/bg_service_03.png') no-repeat center center;
54
+    background-position: center center;
55
+    background-repeat: no-repeat;
52 56
     background-size: 157rpx 52rpx;
53 57
     display: flex;
54 58
     align-items: center;

+ 134 - 3
src/components/HyStation.vue

@@ -1,7 +1,138 @@
1 1
 <template>
2
-  <view></view>
2
+  <view class="station-item-wrapper">
3
+    <template v-if="type === 1">
4
+      <view class="station-card-wrap">
5
+        <view class="card-header">
6
+          <view class="text">92#</view>
7
+          <image :src="minioUrl + '/icon_sm_jiayou.png'" mode="scaleToFill" />
8
+        </view>
9
+        <view class="card-bottom">
10
+          <view class="price-box">
11
+            <view class="price"><text>7.25</text>元</view>
12
+          </view>
13
+          <view class="origin-box">原价:7.90元</view>
14
+        </view>
15
+      </view>
16
+    </template>
17
+    <template v-else>
18
+      <view class="station-list-wrap">
19
+        <view class="station-name">92#</view>
20
+        <view class="price-wrap">
21
+          <view class="price-text"><text>7.25</text>元</view>
22
+          <view class="origin-text">原价:7.90元</view>
23
+        </view>
24
+      </view>
25
+    </template>
26
+  </view>
3 27
 </template>
4 28
 
5
-<script lang="ts" setup></script>
29
+<script lang="ts" setup>
30
+import { useGlobal } from '@/composables/index'
31
+const { minioUrl } = useGlobal()
6 32
 
7
-<style lang="scss" scoped></style>
33
+// type 类型区分 1卡片形式 2列表形式, 通过props传递
34
+defineProps({
35
+  type: {
36
+    type: Number,
37
+    default: 1
38
+  }
39
+})
40
+</script>
41
+
42
+<style lang="scss" scoped>
43
+.station-item-wrapper {
44
+  box-sizing: border-box;
45
+  .station-card-wrap {
46
+    width: 140rpx;
47
+    height: 140rpx;
48
+    background-image: linear-gradient(90deg, #fe594d 0%, #ff8f58 100%), linear-gradient(#f2f1f6, #f2f1f6);
49
+    background-blend-mode: normal, normal;
50
+    border-radius: 8rpx;
51
+    display: flex;
52
+    flex-direction: column;
53
+    .card-header {
54
+      width: 100%;
55
+      height: 35rpx;
56
+      display: flex;
57
+      align-items: center;
58
+      justify-content: space-between;
59
+      box-sizing: border-box;
60
+      padding: 0 10rpx 0 17rpx;
61
+      .text {
62
+        font-size: 26rpx;
63
+        color: #fff;
64
+        font-weight: 600;
65
+      }
66
+      image {
67
+        width: 21rpx;
68
+        height: 19rpx;
69
+      }
70
+    }
71
+    .card-bottom {
72
+      width: 137rpx;
73
+      height: 103rpx;
74
+      background-color: #fff;
75
+      border-radius: 8rpx;
76
+      margin: 0 auto;
77
+      box-sizing: border-box;
78
+      display: flex;
79
+      flex-direction: column;
80
+      align-items: center;
81
+      justify-content: center;
82
+      .price-box {
83
+        display: flex;
84
+        align-items: baseline;
85
+        .price {
86
+          font-size: 20rpx;
87
+          color: #333;
88
+          text {
89
+            font-size: 42rpx;
90
+            color: #ff3d28;
91
+            font-weight: 600;
92
+          }
93
+        }
94
+      }
95
+      .origin-box {
96
+        font-size: 16rpx;
97
+        color: #999;
98
+        text-decoration: line-through;
99
+      }
100
+    }
101
+  }
102
+  .station-list-wrap {
103
+    width: 100%;
104
+    height: 84rpx;
105
+    border-radius: 8rpx;
106
+    background-color: #f6f7fb;
107
+    display: flex;
108
+    align-items: center;
109
+    justify-content: space-between;
110
+    padding: 0 24rpx;
111
+    margin-bottom: 24rpx;
112
+    .station-name {
113
+      font-size: 28rpx;
114
+      color: #333;
115
+      font-weight: 600;
116
+    }
117
+    .price-wrap {
118
+      display: flex;
119
+      align-items: center;
120
+      .price-text {
121
+        font-size: 21rpx;
122
+        color: #333;
123
+        text {
124
+          font-size: 28rpx;
125
+          color: #ff3d28;
126
+          font-weight: 600;
127
+        }
128
+      }
129
+      .origin-text {
130
+        font-size: 21rpx;
131
+        color: #999;
132
+        margin-left: 10rpx;
133
+        text-decoration: line-through;
134
+      }
135
+    }
136
+  }
137
+}
138
+</style>

+ 59 - 0
src/components/HyTag.vue

@@ -0,0 +1,59 @@
1
+<template>
2
+  <view class="tag-item-wrapper">
3
+    <view class="tag-item-content" :class="{ 'need-border': needBorder }">
4
+      <image :src="minioUrl + '/icon_iot_canyin.png'" :style="{ width: imgWidth, height: imgHeight }" />
5
+      <view class="tag-item-text" :style="{ fontSize: fontSize }">{{ text }}</view>
6
+    </view>
7
+  </view>
8
+</template>
9
+
10
+<script lang="ts" setup>
11
+import { useGlobal } from '@/composables/index'
12
+const { minioUrl } = useGlobal()
13
+
14
+// props需要传入 图片宽高、文字、字号、是否需要背景边框
15
+defineProps({
16
+  imgWidth: {
17
+    type: String,
18
+    default: '25rpx'
19
+  },
20
+  imgHeight: {
21
+    type: String,
22
+    default: '25rpx'
23
+  },
24
+  text: {
25
+    type: String,
26
+    default: '餐饮'
27
+  },
28
+  fontSize: {
29
+    type: String,
30
+    default: '22rpx'
31
+  },
32
+  needBorder: {
33
+    type: Boolean,
34
+    default: false
35
+  }
36
+})
37
+</script>
38
+
39
+<style lang="scss" scoped>
40
+.tag-item-wrapper {
41
+  box-sizing: border-box;
42
+  margin: 0 10rpx 20rpx 0;
43
+  .tag-item-content {
44
+    padding: 4rpx 22rpx;
45
+    border-radius: 4rpx;
46
+    display: flex;
47
+    align-items: center;
48
+    width: fit-content;
49
+    &.need-border {
50
+      border: 1rpx solid#2374ff;
51
+      background-color: #e6efff;
52
+    }
53
+    .tag-item-text {
54
+      margin-left: 6rpx;
55
+      line-height: 30rpx;
56
+    }
57
+  }
58
+}
59
+</style>

+ 23 - 0
src/components/README.md

@@ -0,0 +1,23 @@
1
+```vue
2
+<script setup lang="ts">
3
+import { ref } from 'vue'
4
+
5
+const showCustomPopup = ref(false)
6
+</script>
7
+
8
+<template>
9
+  <view class="index-container u-padding-20">
10
+    <HyCharge /><br />
11
+    <HyService /><br />
12
+    <HyShopItem /><br />
13
+    <HyStation /><br />
14
+    <HyTag needBorder />
15
+    <HyPopup v-model="showCustomPopup" width="650rpx" showClose>
16
+      <view style="height: 500rpx; background-color: #fff"></view>
17
+    </HyPopup>
18
+  </view>
19
+</template>
20
+
21
+<style lang="scss" scoped></style>
22
+
23
+```

+ 3 - 0
src/composables/index.ts

@@ -5,6 +5,8 @@ export const useGlobal = () => {
5 5
   const userStore = user()
6 6
   // 文件路径前缀
7 7
   const pathPre: string = import.meta.env.VITE_OSS_URL + import.meta.env.VITE_BARREL_URL
8
+  // Minio地址配置
9
+  const minioUrl: string = import.meta.env.VITE_MINIO_URL
8 10
   // 整数最大值
9 11
   const numberMax: number = 9999999999
10 12
   // 小数最大值
@@ -139,6 +141,7 @@ export const useGlobal = () => {
139 141
   }
140 142
   return {
141 143
     pathPre,
144
+    minioUrl,
142 145
     numberMax,
143 146
     decimalMax,
144 147
     textareaMax,

+ 10 - 10
src/pages.json

@@ -4,7 +4,7 @@
4 4
       "path": "pages/index",
5 5
       "style": {
6 6
         "navigationBarTitleText": "首页",
7
-        "enablePullDownRefresh": false,
7
+        "enablePullDownRefresh": true,
8 8
         "navigationStyle": "custom"
9 9
       }
10 10
     },
@@ -12,7 +12,8 @@
12 12
       "path": "pages/service",
13 13
       "style": {
14 14
         "navigationBarTitleText": "服务区",
15
-        "enablePullDownRefresh": false
15
+        "enablePullDownRefresh": true,
16
+        "navigationStyle": "custom"
16 17
       }
17 18
     },
18 19
     {
@@ -29,24 +30,23 @@
29 30
       "root": "base",
30 31
       "pages": [
31 32
         {
32
-          "path": "login",
33
+          "path": "suggest/index",
33 34
           "style": {
34
-            "navigationBarTitleText": "登录",
35
-            "enablePullDownRefresh": false,
36
-            "navigationStyle": "custom"
35
+            "navigationBarTitleText": "投诉建议",
36
+            "enablePullDownRefresh": false
37 37
           }
38 38
         },
39 39
         {
40
-          "path": "register",
40
+          "path": "suggest/record",
41 41
           "style": {
42
-            "navigationBarTitleText": "注册",
42
+            "navigationBarTitleText": "反馈记录",
43 43
             "enablePullDownRefresh": true
44 44
           }
45 45
         },
46 46
         {
47
-          "path": "forget",
47
+          "path": "suggest/suggest",
48 48
           "style": {
49
-            "navigationBarTitleText": "忘记密码",
49
+            "navigationBarTitleText": "投诉建议",
50 50
             "enablePullDownRefresh": false
51 51
           }
52 52
         }

+ 408 - 10
src/pages/index.vue

@@ -1,19 +1,417 @@
1 1
 <script setup lang="ts">
2 2
 import { ref } from 'vue'
3
+import { onLoad } from '@dcloudio/uni-app'
4
+import { useGlobal } from '@/composables/index'
5
+const { minioUrl } = useGlobal()
3 6
 
4
-const showCustomPopup = ref(false)
7
+// 轮播图数据
8
+const list = ref([`${minioUrl}/home_banner.png`])
9
+
10
+const tabs = ref([{ name: '加油' }, { name: '充电' }])
11
+const tabActive = ref(0)
12
+
13
+// 金刚区列表
14
+const navList = ref([
15
+  { image: `${minioUrl}/icon_nav_01.png`, text: '我要加油', path: '', w: '53rpx', h: '53rpx' },
16
+  { image: `${minioUrl}/icon_nav_02.png`, text: '我要充电', path: '', w: '60rpx', h: '53rpx' },
17
+  { image: `${minioUrl}/icon_nav_03.png`, text: '我要吃饭', path: '', w: '57rpx', h: '57rpx' },
18
+  { image: `${minioUrl}/icon_nav_04.png`, text: '我要修车', path: '', w: '60rpx', h: '56rpx' },
19
+  { image: `${minioUrl}/icon_nav_05.png`, text: '投诉建议', path: '/base/suggest/index', w: '59rpx', h: '53rpx' },
20
+  { image: `${minioUrl}/icon_nav_06.png`, text: '一键救援', path: '', w: '76rpx', h: '76rpx' },
21
+  { image: `${minioUrl}/icon_nav_07.png`, text: '司机之家', path: '', w: '76rpx', h: '76rpx' },
22
+  { image: `${minioUrl}/icon_nav_08.png`, text: '母婴室', path: '', w: '76rpx', h: '76rpx' },
23
+  { image: `${minioUrl}/icon_nav_09.png`, text: '失物招领', path: '', w: '76rpx', h: '76rpx' },
24
+  { image: `${minioUrl}/icon_nav_10.png`, text: '招商加盟', path: '', w: '76rpx', h: '76rpx' }
25
+])
26
+
27
+// 金刚区点击事件
28
+const navClick = (item: { path: string }) => {
29
+  uni.navigateTo({
30
+    url: item.path
31
+  })
32
+}
5 33
 </script>
6 34
 
7 35
 <template>
8
-  <view class="index-container u-padding-20">
9
-    <HyCharge /><br />
10
-    <HyService /><br />
11
-    <HyShopItem /><br />
12
-    <HyStation /><br />
13
-    <HyPopup v-model="showCustomPopup" width="650rpx" showClose>
14
-      <view style="height: 500rpx; background-color: #fff"></view>
15
-    </HyPopup>
36
+  <view class="index-container">
37
+    <!-- 头部背景 -->
38
+    <view class="header-bg" :style="{ backgroundImage: 'url(' + minioUrl + '/index_bg.png)' }"></view>
39
+    <!-- header -->
40
+    <view class="index-header-wrapper">
41
+      <image :src="minioUrl + '/logo.png'" mode="scaleToFill" class="logo" />
42
+      <view class="header-right-wrap">
43
+        <view class="top-title-box">
44
+          <view class="title-text">海青服务区</view>
45
+          <image :src="minioUrl + '/icon_bottom_arrow.png'" mode="scaleToFill" />
46
+        </view>
47
+        <image class="star-img" :src="minioUrl + '/star.png'" mode="scaleToFill" />
48
+        <view class="bottom-box">
49
+          <!-- 天气 -->
50
+          <!-- 营业状态 -->
51
+          <view class="bottom-tag-item">正常营业</view>
52
+        </view>
53
+      </view>
54
+    </view>
55
+    <!-- 轮播图 -->
56
+    <u-swiper :height="306" :list="list"></u-swiper>
57
+    <!-- 金刚区 -->
58
+    <view class="index-nav-wrapper">
59
+      <view class="nav-item" v-for="(item, index) in navList" :key="index" @click="navClick(item)">
60
+        <view class="nav-image">
61
+          <image :src="item.image" mode="scaleToFill" :style="{ width: item.w, height: item.h }" />
62
+        </view>
63
+        <view class="nav-item-text">{{ item.text }}</view>
64
+      </view>
65
+    </view>
66
+    <!-- 出行提示 -->
67
+    <view class="index-tips-wrapper">
68
+      <image class="tips-img" :src="minioUrl + '/icon_chuxing.png'" mode="scaleToFill" />
69
+      <view class="tips-line"></view>
70
+      <view class="tips-content">临时交通管制的通知</view>
71
+    </view>
72
+    <!-- 加油充电 -->
73
+    <view class="index-tabs-wrapper">
74
+      <u-tabs
75
+        v-model="tabActive"
76
+        :list="tabs"
77
+        active-color="#333"
78
+        :font-size="31"
79
+        :height="60"
80
+        :gutter="20"
81
+        :bar-style="{
82
+          width: '30rpx',
83
+          height: '6rpx',
84
+          backgroundImage: 'linear-gradient(90deg, #0f91f8 0%, #32a9c8 68%, #54c197 100%), linear-gradient(#6c85fc, #6c85fc)',
85
+          backgroundBlendMode: 'normal, normal',
86
+          borderRadius: '2rpx'
87
+        }"
88
+      ></u-tabs>
89
+      <template v-if="tabActive === 0">
90
+        <view class="tab-station-wrap">
91
+          <HyStation v-for="index in 4" :key="index" />
92
+        </view>
93
+      </template>
94
+      <template v-if="tabActive === 1">
95
+        <view class="tab-station-wrap">
96
+          <HyCharge />
97
+        </view>
98
+      </template>
99
+    </view>
100
+    <!-- 服务设施 -->
101
+    <HyCard title="服务设施" marginBottom="24">
102
+      <view class="index-service-wrapper">
103
+        <HyService v-for="index in 6" :key="index"></HyService>
104
+      </view>
105
+    </HyCard>
106
+    <!-- 人气优选商家 -->
107
+    <HyCard title="人气优选商家" rightText="更多商家" isShowRight marginBottom="24">
108
+      <view class="index-shop-wrapper">
109
+        <view class="shop-item" v-for="index in 5" :key="index">
110
+          <view class="img-wrap">
111
+            <image src="" mode="scaleToFill" />
112
+          </view>
113
+          <view class="item-text u-line-1">肯德基</view>
114
+        </view>
115
+      </view>
116
+    </HyCard>
117
+    <!-- 附近景点 -->
118
+    <HyCard title="附近景点" isBg :bgImg="minioUrl + '/index_spot_bg.png'">
119
+      <view class="index-spot-wrapper">
120
+        <view class="spot-item" v-for="index in 5" :key="index">
121
+          <view class="spot-img-wrap">
122
+            <image src="" mode="scaleToFill" />
123
+          </view>
124
+          <view class="spot-right-wrap">
125
+            <view class="right-header">
126
+              <view class="title">琅琊台风景区</view>
127
+              <view class="price">50元</view>
128
+            </view>
129
+            <view class="right-tag-box">
130
+              <view class="s-tag-green">自然风光</view>
131
+              <view class="s-tag-green">历史遗迹</view>
132
+              <view class="s-tag-green">休闲度假</view>
133
+            </view>
134
+            <view class="right-location-box">
135
+              <view class="left">
136
+                <image class="icon-address" :src="minioUrl + '/icon_address.png'" mode="scaleToFill" />
137
+                <view class="address-text-wrap">距您<text>30.5</text>公里,驾车预计<text>40</text>分钟</view>
138
+              </view>
139
+              <view class="right-go-icon">
140
+                <image :src="minioUrl + '/icon_go.png'" mode="scaleToFill" />
141
+              </view>
142
+            </view>
143
+          </view>
144
+        </view>
145
+      </view>
146
+    </HyCard>
16 147
   </view>
17 148
 </template>
18 149
 
19
-<style lang="scss" scoped></style>
150
+<style lang="scss" scoped>
151
+.index-container {
152
+  width: 100%;
153
+  min-height: 100dvh;
154
+  position: relative;
155
+  box-sizing: border-box;
156
+  padding: 24rpx;
157
+  background-color: #f6f7fb;
158
+  .header-bg {
159
+    width: 100%;
160
+    height: 494rpx;
161
+    background-position: center center;
162
+    background-repeat: no-repeat;
163
+    background-size: 100% 494rpx;
164
+    position: absolute;
165
+    top: 0;
166
+    left: 0;
167
+    z-index: 1;
168
+  }
169
+  .index-header-wrapper {
170
+    display: flex;
171
+    align-items: center;
172
+    box-sizing: border-box;
173
+    padding-left: 33rpx;
174
+    position: relative;
175
+    z-index: 2;
176
+    margin-bottom: 24rpx;
177
+    .logo {
178
+      width: 105rpx;
179
+      height: 92rpx;
180
+    }
181
+    .header-right-wrap {
182
+      display: flex;
183
+      flex-direction: column;
184
+      margin-left: 24rpx;
185
+      .top-title-box {
186
+        display: flex;
187
+        align-items: center;
188
+        .title-text {
189
+          font-size: 30rpx;
190
+          color: #3b3c3b;
191
+          font-weight: 600;
192
+        }
193
+        image {
194
+          width: 13rpx;
195
+          height: 10rpx;
196
+          margin-left: 8rpx;
197
+        }
198
+      }
199
+      .star-img {
200
+        width: 146rpx;
201
+        height: 27rpx;
202
+      }
203
+      .bottom-box {
204
+        display: flex;
205
+        align-items: center;
206
+        margin-top: 10rpx;
207
+        .bottom-tag-item {
208
+          font-size: 18rpx;
209
+          color: #fff;
210
+          box-sizing: border-box;
211
+          padding: 6rpx 12rpx;
212
+          background-color: #3bb0b9;
213
+          border-radius: 8rpx;
214
+        }
215
+      }
216
+    }
217
+  }
218
+  :deep(.u-swiper-wrap) {
219
+    position: relative;
220
+    z-index: 2;
221
+  }
222
+  .index-nav-wrapper {
223
+    display: flex;
224
+    align-items: center;
225
+    flex-wrap: wrap;
226
+    justify-content: space-around;
227
+    margin-top: 40rpx;
228
+    .nav-item {
229
+      display: flex;
230
+      flex-direction: column;
231
+      align-items: center;
232
+      width: 20%;
233
+      .nav-image {
234
+        width: 76rpx;
235
+        height: 76rpx;
236
+        display: flex;
237
+        align-items: center;
238
+        justify-content: center;
239
+        margin-bottom: 14rpx;
240
+      }
241
+      &:nth-child(-n + 5) {
242
+        margin-bottom: 50rpx;
243
+      }
244
+    }
245
+  }
246
+  .index-tips-wrapper {
247
+    width: 100%;
248
+    height: 84rpx;
249
+    border-radius: 14rpx;
250
+    background-color: #fff;
251
+    margin: 24rpx 0;
252
+    box-sizing: border-box;
253
+    padding: 0 30rpx;
254
+    display: flex;
255
+    align-items: center;
256
+    .tips-img {
257
+      width: 53rpx;
258
+      height: 48rpx;
259
+    }
260
+    .tips-line {
261
+      width: 4rpx;
262
+      height: 34rpx;
263
+      background-color: #3866ae;
264
+      margin: 0 23rpx 0 13rpx;
265
+    }
266
+    .tips-content {
267
+      font-size: 25rpx;
268
+      color: #333;
269
+      font-weight: 500;
270
+      line-height: 40rpx;
271
+    }
272
+  }
273
+  .index-tabs-wrapper {
274
+    width: 100%;
275
+    background-color: #fff;
276
+    border-radius: 14rpx;
277
+    box-sizing: border-box;
278
+    padding: 22rpx;
279
+    margin-bottom: 24rpx;
280
+    .tab-station-wrap {
281
+      display: flex;
282
+      align-items: center;
283
+      justify-content: space-around;
284
+      margin-top: 24rpx;
285
+    }
286
+  }
287
+  .index-service-wrapper {
288
+    margin-top: 24rpx;
289
+    display: flex;
290
+    flex-wrap: wrap;
291
+    justify-content: space-between;
292
+    :deep(.service-item-wrapper) {
293
+      margin-bottom: 20rpx;
294
+      &:nth-child(5) {
295
+        margin-bottom: 0;
296
+      }
297
+      &:nth-child(6) {
298
+        margin-bottom: 0;
299
+      }
300
+    }
301
+  }
302
+  .index-shop-wrapper {
303
+    margin-top: 26rpx;
304
+    display: flex;
305
+    align-items: center;
306
+    justify-content: space-between;
307
+    .shop-item {
308
+      display: flex;
309
+      flex-direction: column;
310
+      .img-wrap {
311
+        width: 104rpx;
312
+        height: 104rpx;
313
+        border-radius: 8rpx;
314
+        background-color: #f0f6ff;
315
+        image {
316
+          width: 104rpx;
317
+          height: 104rpx;
318
+          border-radius: 8rpx;
319
+        }
320
+      }
321
+      .item-text {
322
+        width: 100rpx;
323
+        text-align: center;
324
+        margin-top: 12rpx;
325
+      }
326
+    }
327
+  }
328
+  .index-spot-wrapper {
329
+    margin-top: 24rpx;
330
+    .spot-item {
331
+      display: flex;
332
+      align-items: center;
333
+      margin-bottom: 40rpx;
334
+      &:last-child {
335
+        margin-bottom: 0;
336
+      }
337
+      .spot-img-wrap {
338
+        width: 196rpx;
339
+        height: 151rpx;
340
+        background-color: #f6f7fb;
341
+        border-radius: 8rpx;
342
+        image {
343
+          width: 196rpx;
344
+          height: 151rpx;
345
+          border-radius: 8rpx;
346
+        }
347
+      }
348
+      .spot-right-wrap {
349
+        width: 100%;
350
+        flex: 1;
351
+        margin-left: 28rpx;
352
+        .right-header {
353
+          display: flex;
354
+          align-items: center;
355
+          justify-content: space-between;
356
+          .title {
357
+            font-size: 28rpx;
358
+            color: #333;
359
+            font-weight: 600;
360
+          }
361
+          .price {
362
+            font-size: 21rpx;
363
+            color: #fc9b0d;
364
+          }
365
+        }
366
+        .right-tag-box {
367
+          display: flex;
368
+          align-items: center;
369
+          margin-top: 6rpx;
370
+          .s-tag-green {
371
+            width: 125rpx;
372
+            height: 32rpx;
373
+            background-color: #e2f3e4;
374
+            border-radius: 16rpx;
375
+            display: flex;
376
+            align-items: center;
377
+            justify-content: center;
378
+            font-size: 21rpx;
379
+            color: #239450;
380
+            margin-right: 20rpx;
381
+          }
382
+        }
383
+        .right-location-box {
384
+          margin-top: 50rpx;
385
+          display: flex;
386
+          align-items: center;
387
+          justify-content: space-between;
388
+          .left {
389
+            display: flex;
390
+            align-items: center;
391
+            .icon-address {
392
+              width: 18rpx;
393
+              height: 22rpx;
394
+            }
395
+            .address-text-wrap {
396
+              font-size: 21rpx;
397
+              color: #c3c3c3;
398
+              margin-left: 8rpx;
399
+              text {
400
+                color: #fc9904;
401
+              }
402
+            }
403
+          }
404
+          .right-go-icon {
405
+            width: 40rpx;
406
+            height: 40rpx;
407
+            image {
408
+              width: 40rpx;
409
+              height: 40rpx;
410
+            }
411
+          }
412
+        }
413
+      }
414
+    }
415
+  }
416
+}
417
+</style>

+ 89 - 0
src/pages/my.vue

@@ -1,8 +1,97 @@
1 1
 <script setup lang="ts">
2
+import { useGlobal } from '@/composables/index'
3
+const { minioUrl } = useGlobal()
2 4
 </script>
3 5
 
4 6
 <template>
7
+  <view class="my-container" :style="{ backgroundImage: `url(${minioUrl + '/my_bg.png'})` }">
8
+    <view class="my-header-wrapper">
9
+      <image :src="minioUrl + '/icon_default_user.png'" mode="scaleToFill" />
10
+      <text class="user-name-text">用户15615550287</text>
11
+    </view>
12
+    <HyCard>
13
+      <view class="my-list-item">
14
+        <view class="list-left">
15
+          <image :src="minioUrl + '/icon_feed.png'" mode="scaleToFill" style="width: 42rpx; height: 42rpx" />
16
+          <text class="text">我的反馈</text>
17
+        </view>
18
+        <view class="list-right">
19
+          <image :src="minioUrl + '/icon_right.png'" mode="scaleToFill" />
20
+        </view>
21
+      </view>
22
+      <view class="my-list-item">
23
+        <view class="list-left">
24
+          <image :src="minioUrl + '/icon_phone.png'" mode="scaleToFill" style="width: 42rpx; height: 42rpx" />
25
+          <text class="text">客服热线:0532-88886666</text>
26
+        </view>
27
+      </view>
28
+      <view class="my-list-item">
29
+        <view class="list-left">
30
+          <image :src="minioUrl + '/icon_time.png'" mode="scaleToFill" style="width: 42rpx; height: 42rpx" />
31
+          <text class="text">咨询时间:09:00-22:00</text>
32
+        </view>
33
+      </view>
34
+    </HyCard>
35
+  </view>
5 36
 </template>
6 37
 
7 38
 <style lang="scss" scoped>
39
+page {
40
+  background-color: #edf1f5;
41
+}
42
+.my-container {
43
+  width: 100%;
44
+  min-height: 900rpx;
45
+  background-position: center center;
46
+  background-repeat: no-repeat;
47
+  background-size: cover;
48
+  box-sizing: border-box;
49
+  padding: 100rpx 28rpx 0 28rpx;
50
+  .my-header-wrapper {
51
+    display: flex;
52
+    align-items: center;
53
+    margin-bottom: 60rpx;
54
+    image {
55
+      width: 139rpx;
56
+      height: 139rpx;
57
+      margin-right: 36rpx;
58
+    }
59
+    .user-name-text {
60
+      font-size: 35rpx;
61
+      color: #333;
62
+      font-weight: 500;
63
+    }
64
+  }
65
+  :deep(.hy-card-container) {
66
+    padding-bottom: 0;
67
+  }
68
+  .my-list-item {
69
+    display: flex;
70
+    align-items: center;
71
+    justify-content: space-between;
72
+    padding: 31rpx 7rpx 31rpx 7rpx;
73
+    border-bottom: 1rpx solid #ebebeb;
74
+    &:first-child {
75
+      padding-top: 7rpx;
76
+    }
77
+    &:last-child {
78
+      border-bottom: none;
79
+    }
80
+    .list-left {
81
+      display: flex;
82
+      align-items: center;
83
+      .text {
84
+        font-size: 28rpx;
85
+        color: #333;
86
+        margin-left: 28rpx;
87
+      }
88
+    }
89
+    .list-right {
90
+      image {
91
+        width: 11rpx;
92
+        height: 19rpx;
93
+      }
94
+    }
95
+  }
96
+}
8 97
 </style>

+ 124 - 3
src/pages/service.vue

@@ -1,7 +1,128 @@
1
-<script setup lang="ts"></script>
1
+<script setup lang="ts">
2
+import { ref } from 'vue'
3
+import { useGlobal } from '@/composables/index'
4
+
5
+const { minioUrl } = useGlobal()
6
+</script>
2 7
 
3 8
 <template>
4
-  <view></view>
9
+  <view class="service-container">
10
+    <HyCard v-for="item in 5" :key="item" class="service-content-wrapper" marginBottom="36">
11
+      <view class="service-item">
12
+        <view class="item-header-wrap">
13
+          <view class="header-img-box">
14
+            <image src="" mode="scaleToFill" />
15
+          </view>
16
+          <view class="header-right-box">
17
+            <view class="u-line-1 title">黄岛服务区(沈海高速沈阳方向)</view>
18
+            <view class="rate-content">
19
+              <uni-rate :value="5" activeColor="#ff5702"></uni-rate>
20
+              <text class="rate-text">五星级服务区</text>
21
+            </view>
22
+            <view class="distance">55.6KM</view>
23
+            <view class="address-wrap">
24
+              <view class="block">
25
+                <view class="t">海口</view>
26
+                <image class="icon-arrive" :src="minioUrl + '/icon_arrive.png'" mode="scaleToFill" />
27
+                <view class="t">沈阳</view>
28
+              </view>
29
+              <view class="icon-go">
30
+                <image :src="minioUrl + '/icon_blue_go.png'" mode="scaleToFill" />
31
+              </view>
32
+            </view>
33
+          </view>
34
+        </view>
35
+        <view class="item-bottom-wrap">
36
+          <HyTag needBorder v-for="index in 8" :key="index" />
37
+        </view>
38
+      </view>
39
+    </HyCard>
40
+  </view>
5 41
 </template>
6 42
 
7
-<style lang="scss" scoped></style>
43
+<style lang="scss" scoped>
44
+.service-container {
45
+  width: 100%;
46
+  min-height: 10dvh;
47
+  padding: 24rpx 24rpx 0 24rpx;
48
+  background-color: #edf1f5;
49
+  .service-item {
50
+    .item-header-wrap {
51
+      display: flex;
52
+      align-items: center;
53
+      .header-img-box {
54
+        width: 174rpx;
55
+        height: 174rpx;
56
+        position: relative;
57
+        background-color: aliceblue;
58
+        image {
59
+          width: 174rpx;
60
+          height: 174rpx;
61
+        }
62
+      }
63
+      .header-right-box {
64
+        flex: 1;
65
+        margin-left: 20rpx;
66
+        .title {
67
+          width: 464rpx;
68
+          font-size: 31rpx;
69
+          color: #333;
70
+          font-weight: 600;
71
+          line-height: 50rpx;
72
+        }
73
+        .rate-content {
74
+          display: flex;
75
+          align-items: center;
76
+          .rate-text {
77
+            font-size: 22rpx;
78
+            color: #ff5702;
79
+            margin-left: 14rpx;
80
+          }
81
+        }
82
+        .distance {
83
+          font-size: 22rpx;
84
+          color: #999;
85
+          line-height: 42rpx;
86
+        }
87
+        .address-wrap {
88
+          display: flex;
89
+          align-items: center;
90
+          justify-content: space-between;
91
+          .block {
92
+            min-width: 174rpx;
93
+            box-sizing: border-box;
94
+            padding: 10rpx 24rpx;
95
+            background-color: #edf1f5;
96
+            border-radius: 21rpx;
97
+            display: flex;
98
+            align-items: center;
99
+            .icon-arrive {
100
+              width: 31rpx;
101
+              height: 13rpx;
102
+              margin: 0 8rpx;
103
+            }
104
+            .t {
105
+              font-size: 22rpx;
106
+              color: #333;
107
+            }
108
+          }
109
+          .icon-go {
110
+            width: 60rpx;
111
+            height: 60rpx;
112
+            image {
113
+              width: 60rpx;
114
+              height: 60rpx;
115
+            }
116
+          }
117
+        }
118
+      }
119
+    }
120
+    .item-bottom-wrap {
121
+      width: 550rpx;
122
+      display: flex;
123
+      flex-wrap: wrap;
124
+      margin-top: 40rpx;
125
+    }
126
+  }
127
+}
128
+</style>