Переглянути джерело

Merge branch 'develop' of ssh://gogs.zlhuiyun.com:8080/szr190/ShenhaiH5 into develop

# Conflicts:
#	src/manifest.json
fengyu 2 місяців тому
батько
коміт
c69178b761
41 змінених файлів з 2347 додано та 1775 видалено
  1. 1 1
      src/App.vue
  2. 116 0
      src/base/base.scss
  3. 69 0
      src/base/common/charge.vue
  4. 37 0
      src/base/common/driversHome.vue
  5. 25 0
      src/base/common/join.vue
  6. 49 0
      src/base/common/nursingRoom.vue
  7. 37 0
      src/base/common/refuel.vue
  8. 34 0
      src/base/common/repair.vue
  9. 45 0
      src/base/common/spot.vue
  10. 0 63
      src/base/components/Captcha.vue
  11. 0 238
      src/base/components/Editors/editor-icon.css
  12. BIN
      src/base/components/Editors/iconfont.ttf
  13. 0 286
      src/base/components/Editors/index.vue
  14. 0 181
      src/base/forget.vue
  15. 0 791
      src/base/login.vue
  16. 0 177
      src/base/register.vue
  17. 42 0
      src/base/shop/details.vue
  18. 14 0
      src/base/shop/index.vue
  19. 14 0
      src/base/shop/meal.vue
  20. 171 0
      src/base/suggest/index.vue
  21. 99 0
      src/base/suggest/record.vue
  22. 91 0
      src/base/suggest/suggest.vue
  23. 68 0
      src/base/travel/tips.vue
  24. 88 0
      src/components/HyCard.vue
  25. 1 1
      src/components/HyPopup.vue
  26. 184 0
      src/components/HySelect.vue
  27. 8 4
      src/components/HyService.vue
  28. 22 2
      src/components/HyShopItem.vue
  29. 134 3
      src/components/HyStation.vue
  30. 63 0
      src/components/HyTag.vue
  31. 23 0
      src/components/README.md
  32. 3 0
      src/composables/index.ts
  33. 85 9
      src/pages.json
  34. 587 9
      src/pages/index.vue
  35. 89 0
      src/pages/my.vue
  36. 142 3
      src/pages/service.vue
  37. BIN
      src/static/images/bg_service_03.png
  38. BIN
      src/static/images/icon_service_01.png
  39. BIN
      src/static/images/icon_sm_jiayou.png
  40. BIN
      src/static/images/service_active.png
  41. 6 7
      vite.config.ts

+ 1 - 1
src/App.vue

@@ -25,7 +25,7 @@ onHide(() => {
25 25
 <style lang="scss">
26 26
 @import './uni_modules/vk-uview-ui/index.scss';
27 27
 page {
28
-  background-color: #f8f8f8;
28
+  background-color: #edf1f5;
29 29
 }
30 30
 /* @font-face {
31 31
   font-family: 'MyFont';

+ 116 - 0
src/base/base.scss

@@ -0,0 +1,116 @@
1
+.common-details-container {
2
+  .common-header-wrapper {
3
+    width: 100%;
4
+    height: 420rpx;
5
+    background-color: #f5f5f5;
6
+    position: relative;
7
+    z-index: 1;
8
+    &.join {
9
+      height: 403rpx;
10
+      image {
11
+        width: 100%;
12
+        height: 403rpx;
13
+      }
14
+    }
15
+    image {
16
+      width: 100%;
17
+      height: 420rpx;
18
+    }
19
+  }
20
+  .common-content-wrapper {
21
+    width: 694rpx;
22
+    margin: -42rpx auto 0 auto;
23
+    position: relative;
24
+    z-index: 2;
25
+  }
26
+  .common-card {
27
+    width: 694rpx;
28
+    box-sizing: border-box;
29
+    padding: 28rpx;
30
+    background-color: #fff;
31
+    border-radius: 14rpx;
32
+    margin-bottom: 24rpx;
33
+    .common-card-title {
34
+      font-size: 31rpx;
35
+      color: #333;
36
+      font-weight: 600;
37
+      margin-bottom: 10rpx;
38
+      display: flex;
39
+      align-items: center;
40
+      .text {
41
+        font-size: 31rpx;
42
+        color: #333;
43
+        font-weight: 600;
44
+      }
45
+      .card-tag {
46
+        box-sizing: border-box;
47
+        padding: 0 15rpx;
48
+        border-radius: 14rpx;
49
+        background-color: #f5840b;
50
+        font-size: 21rpx;
51
+        color: #fff;
52
+        margin-left: 14rpx;
53
+        display: flex;
54
+        align-items: center;
55
+        justify-content: center;
56
+        vertical-align: middle;
57
+      }
58
+    }
59
+    .common-sub-title {
60
+      font-size: 25rpx;
61
+      color: #333;
62
+      margin-top: 12rpx;
63
+      display: flex;
64
+      align-items: center;
65
+      justify-content: space-between;
66
+      .text {
67
+        font-size: 25rpx;
68
+        color: #333;
69
+      }
70
+      .sub-img-wrap {
71
+        width: 43rpx;
72
+        height: 43rpx;
73
+        image {
74
+          width: 43rpx;
75
+          height: 43rpx;
76
+        }
77
+      }
78
+    }
79
+    .common-card-content {
80
+      font-size: 22rpx;
81
+      color: #333;
82
+      line-height: 42rpx;
83
+      text {
84
+        display: inline-block;
85
+      }
86
+    }
87
+    .common-card-list {
88
+      display: flex;
89
+      flex-wrap: wrap;
90
+      justify-content: space-between;
91
+      margin-top: 24rpx;
92
+      .card-list-item {
93
+        display: flex;
94
+        flex-direction: column;
95
+        align-items: center;
96
+        justify-content: center;
97
+        margin-bottom: 22rpx;
98
+        .item-img-wrap {
99
+          width: 180rpx;
100
+          height: 180rpx;
101
+          background-color: #f5f5f5;
102
+          image {
103
+            width: 180rpx;
104
+            height: 180rpx;
105
+          }
106
+        }
107
+        .list-text {
108
+          font-size: 22rpx;
109
+          color: #333;
110
+          line-height: 42rpx;
111
+          margin-top: 12rpx;
112
+        }
113
+      }
114
+    }
115
+  }
116
+}

+ 69 - 0
src/base/common/charge.vue

@@ -0,0 +1,69 @@
1
+<template>
2
+  <view class="common-details-container">
3
+    <view class="common-header-wrapper">
4
+      <image :src="minioUrl + '/img_chongdian_bg.png'" mode="scaleToFill" />
5
+    </view>
6
+    <view class="common-content-wrapper">
7
+      <view class="common-card">
8
+        <!-- common title -->
9
+        <view class="common-card-title">山高新能源海清高速服务区充电站</view>
10
+        <!-- sub title -->
11
+        <view class="common-sub-title">营业时间:24小时</view>
12
+        <view class="common-sub-title">客服电话:0532-8886666</view>
13
+      </view>
14
+      <view class="common-card">
15
+        <view class="common-card-title u-margin-bottom-20">当前电价</view>
16
+        <!-- 当前电价及时间展示 -->
17
+        <view class="charge-current-wrapper">
18
+          <view class="current-left-wrap">
19
+            <view class="text">1.36</view>
20
+            <view class="unit">元/度</view>
21
+          </view>
22
+          <view class="current-right-wrap">09:00-12:00</view>
23
+        </view>
24
+        <HyCharge />
25
+        <view class="common-card-title u-margin-top-45">温馨提示</view>
26
+        <view class="common-card-content">
27
+          <text>1、进出充电站时,请严格遵守交通规则和指示标识,注意观察礼让其他车辆。</text>
28
+          <text>2、充电前请确认车辆与充电桩兼容(如接口类型、功率匹配),优先选择支持直流快充的桩。</text>
29
+          <text>3、充电前确保车辆熄火(混动车型需熄火),连接充电枪后启动充电程序。</text>
30
+          <text>4、充电过程中若发现设备故障、异味或漏电,立即停止并联系工作人员。</text>
31
+          <text>5、充电结束时先关闭电源,再拔枪并妥善存放。</text>
32
+        </view>
33
+      </view>
34
+    </view>
35
+  </view>
36
+</template>
37
+
38
+<script setup lang="ts">
39
+import { ref } from 'vue'
40
+import { useGlobal } from '../../composables/index'
41
+const { minioUrl } = useGlobal()
42
+</script>
43
+
44
+<style lang="scss" scoepd>
45
+@import '../base.scss';
46
+.charge-current-wrapper {
47
+  display: flex;
48
+  align-items: center;
49
+  justify-content: space-between;
50
+  margin-bottom: 20rpx;
51
+  .current-left-wrap {
52
+    display: flex;
53
+    align-items: center;
54
+    .text {
55
+      font-size: 31rpx;
56
+      color: #ff3d28;
57
+      font-weight: 600;
58
+    }
59
+    .unit {
60
+      font-size: 21rpx;
61
+      color: #333;
62
+    }
63
+  }
64
+  .current-right-wrap {
65
+    font-size: 21rpx;
66
+    color: #999;
67
+  }
68
+}
69
+</style>

+ 37 - 0
src/base/common/driversHome.vue

@@ -0,0 +1,37 @@
1
+<template>
2
+  <view class="common-details-container">
3
+    <view class="common-header-wrapper">
4
+      <image :src="minioUrl + '/img_sijizhijia_bg.png'" mode="scaleToFill" />
5
+    </view>
6
+    <view class="common-content-wrapper">
7
+      <view class="common-card">
8
+        <!-- common title -->
9
+        <view class="common-card-title">海清服务区司机之家</view>
10
+        <!-- sub title -->
11
+        <view class="common-sub-title">营业时间:24小时</view>
12
+        <view class="common-sub-title">客服电话:0532-8886666</view>
13
+      </view>
14
+      <view class="common-card">
15
+        <view class="common-card-title u-margin-bottom-20">基本介绍</view>
16
+        <view class="common-card-content">
17
+          “司机之家”是指主要为道路运输车辆及驾驶人员提供公共服务的场所和建筑设施,一般具有餐饮、休息、停车安保、宣传教育等服务功能。按照建设要求,“司机之家”应在高速公路服务区、普通国省干线公路服务区(站)、物流通道货物集散地或者物流园区等地建设,截至2025年4月全国30个省(自治区、直辖市)已建成1900余个“司机之家”线下服务点。
18
+        </view>
19
+        <view class="common-card-title u-margin-top-45">设施配置</view>
20
+        <view class="u-flex u-col-center u-margin-top-24">
21
+          <HyTag text="淋浴(有热水)" imgUrl="/icon_ly.png" />
22
+          <HyTag text="洗衣机" imgUrl="/icon_xyj.png" />
23
+        </view>
24
+      </view>
25
+    </view>
26
+  </view>
27
+</template>
28
+
29
+<script setup lang="ts">
30
+import { ref } from 'vue'
31
+import { useGlobal } from '../../composables/index'
32
+const { minioUrl } = useGlobal()
33
+</script>
34
+
35
+<style lang="scss" scoepd>
36
+@import '../base.scss';
37
+</style>

Різницю між файлами не показано, бо вона завелика
+ 25 - 0
src/base/common/join.vue


+ 49 - 0
src/base/common/nursingRoom.vue

@@ -0,0 +1,49 @@
1
+<template>
2
+  <view class="common-details-container">
3
+    <view class="common-header-wrapper">
4
+      <image :src="minioUrl + '/img_muying_bg.png'" mode="scaleToFill" />
5
+    </view>
6
+    <view class="common-content-wrapper">
7
+      <view class="common-card">
8
+        <!-- common title -->
9
+        <view class="common-card-title">海清服务区母婴室</view>
10
+        <!-- sub title -->
11
+        <view class="common-sub-title">营业时间:24小时</view>
12
+        <view class="common-sub-title">客服电话:0532-8886666</view>
13
+      </view>
14
+      <view class="common-card">
15
+        <view class="common-card-title u-margin-bottom-20">基本介绍</view>
16
+        <view class="common-card-content">
17
+          母婴室,也称作母乳喂养室,通常建设于大型公共场所,如:商业综合体、交通枢纽、医疗机构、游乐场馆、文体教育场所等。以便于携婴父母出门在外照料哺乳期婴儿进行护理、哺乳、集乳、喂食、备餐的功能。建设主体包括商业场所及政府非营利性公共服务设施。
18
+        </view>
19
+        <view class="common-card-title u-margin-top-45">设施配置</view>
20
+        <view class="bottom-box-wrap">
21
+          <HyTag text="哺乳椅" imgUrl="/icon_muying_01.png" />
22
+          <HyTag text="饮水机(有热水)" imgUrl="/icon_muying_02.png" />
23
+          <HyTag text="尿布台" imgUrl="/icon_muying_03.png" />
24
+          <HyTag text="婴儿床" imgUrl="/icon_muying_04.png" />
25
+        </view>
26
+      </view>
27
+    </view>
28
+  </view>
29
+</template>
30
+
31
+<script setup lang="ts">
32
+import { ref } from 'vue'
33
+import { useGlobal } from '../../composables/index'
34
+const { minioUrl } = useGlobal()
35
+</script>
36
+
37
+<style lang="scss" scoepd>
38
+@import '../base.scss';
39
+.bottom-box-wrap {
40
+  display: flex;
41
+  flex-wrap: wrap;
42
+  align-items: center;
43
+  justify-content: space-between;
44
+  margin-top: 24rpx;
45
+  :deep(.tag-item-wrapper) {
46
+    margin-right: 30rpx;
47
+  }
48
+}
49
+</style>

+ 37 - 0
src/base/common/refuel.vue

@@ -0,0 +1,37 @@
1
+<template>
2
+  <view class="common-details-container">
3
+    <view class="common-header-wrapper">
4
+      <image :src="minioUrl + '/img_jiayou_bg.png'" mode="scaleToFill" />
5
+    </view>
6
+    <view class="common-content-wrapper">
7
+      <view class="common-card">
8
+        <!-- common title -->
9
+        <view class="common-card-title">中石化海清高速服务区加油站</view>
10
+        <!-- sub title -->
11
+        <view class="common-sub-title">营业时间:24小时</view>
12
+        <view class="common-sub-title">客服电话:0532-8886666</view>
13
+      </view>
14
+      <view class="common-card">
15
+        <view class="common-card-title u-margin-bottom-20">当前油价</view>
16
+        <HyStation type="2" v-for="index in 4" :key="index"></HyStation>
17
+        <view class="common-card-title u-margin-top-45">温馨提示</view>
18
+        <view class="common-card-content">
19
+          <text>1、进出加油站时,请严格遵守交通规则和指示标识,注意观察礼让其他车辆。</text>
20
+          <text>2、加油前请熄火,不要拨打电话。</text>
21
+          <text>3、加油站区域内请勿吸烟、使用明火(如打火机)。</text>
22
+          <text>4、如需开具发票,请联系站内工作人员。</text>
23
+        </view>
24
+      </view>
25
+    </view>
26
+  </view>
27
+</template>
28
+
29
+<script setup lang="ts">
30
+import { ref } from 'vue'
31
+import { useGlobal } from '../../composables/index'
32
+const { minioUrl } = useGlobal()
33
+</script>
34
+
35
+<style lang="scss" scoepd>
36
+@import '../base.scss';
37
+</style>

+ 34 - 0
src/base/common/repair.vue

@@ -0,0 +1,34 @@
1
+<template>
2
+  <view class="common-details-container">
3
+    <view class="common-header-wrapper">
4
+      <image :src="minioUrl + '/img_xiuche_bg.png'" mode="scaleToFill" />
5
+    </view>
6
+    <view class="common-content-wrapper">
7
+      <view class="common-card">
8
+        <!-- common title -->
9
+        <view class="common-card-title">驿驰汽修</view>
10
+        <!-- sub title -->
11
+        <view class="common-sub-title">营业时间:24小时</view>
12
+        <view class="common-sub-title">客服电话:0532-8886666</view>
13
+      </view>
14
+      <view class="common-card">
15
+        <view class="common-card-title u-margin-bottom-20">免费项目</view>
16
+        <view class="common-card-content">
17
+          <text>大货车免费汽修项目:启动搭电、胎压检测</text>
18
+          <text>小客车免费汽修项目:启动搭电、胎压检测、轮胎充气</text>
19
+        </view>
20
+        <view class="common-card-title u-margin-top-45">收费项目</view>
21
+      </view>
22
+    </view>
23
+  </view>
24
+</template>
25
+
26
+<script setup lang="ts">
27
+import { ref } from 'vue'
28
+import { useGlobal } from '../../composables/index'
29
+const { minioUrl } = useGlobal()
30
+</script>
31
+
32
+<style lang="scss" scoepd>
33
+@import '../base.scss';
34
+</style>

+ 45 - 0
src/base/common/spot.vue

@@ -0,0 +1,45 @@
1
+<template>
2
+  <view class="common-details-container">
3
+    <view class="common-header-wrapper">
4
+      <image src="" mode="scaleToFill" />
5
+    </view>
6
+    <view class="common-content-wrapper">
7
+      <view class="common-card">
8
+        <!-- common title -->
9
+        <view class="common-card-title">
10
+          <view class="text">少海风景区</view>
11
+          <view class="card-tag">4A景区</view>
12
+        </view>
13
+        <!-- sub title -->
14
+        <view class="common-sub-title">开放时间:24小时</view>
15
+        <view class="common-sub-title">
16
+          <view class="text">地址:山东省青岛市胶州市香港路1号</view>
17
+          <view class="sub-img-wrap">
18
+            <image :src="minioUrl + '/icon_go.png'" mode="scaleToFill" />
19
+          </view>
20
+        </view>
21
+      </view>
22
+      <view class="common-card">
23
+        <view class="common-card-title">景区介绍</view>
24
+        <view class="common-card-content"
25
+          >少海风景区位于胶州市区东南侧,地处胶州湾与大沽河交汇处,规划面积22.91平方公里
26
+          。景区由南湖、北湖及云溪河构成水域体系,湿地覆盖率83.9%,拥有166种野生脊椎动物和93种维管束植物
27
+          ,形成"两湖、一带、三岛"的生态格局。作为山东省首家国家级湿地公园(2012年挂牌),景区融合16公里环湖景观带与仿古建筑群,复建板桥镇、慈云寺等文化地标
28
+          。设有游艇俱乐部、观景台等设施,2019年获评国家4A级旅游景区。</view
29
+        >
30
+        <view class="common-card-title u-margin-top-45">门票政策</view>
31
+        <view class="common-card-content">免费开放</view>
32
+      </view>
33
+    </view>
34
+  </view>
35
+</template>
36
+
37
+<script setup lang="ts">
38
+import { ref } from 'vue'
39
+import { useGlobal } from '../../composables/index'
40
+const { minioUrl } = useGlobal()
41
+</script>
42
+
43
+<style lang="scss" scoepd>
44
+@import '../base.scss';
45
+</style>

+ 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>

Різницю між файлами не показано, бо вона завелика
+ 0 - 238
src/base/components/Editors/editor-icon.css


BIN
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>

+ 42 - 0
src/base/shop/details.vue

@@ -0,0 +1,42 @@
1
+<template>
2
+  <view class="common-details-container">
3
+    <view class="common-header-wrapper">
4
+      <image src="" mode="scaleToFill" />
5
+    </view>
6
+    <view class="common-content-wrapper">
7
+      <view class="common-card">
8
+        <!-- common title -->
9
+        <view class="common-card-title">肯德基海清高速服务区店</view>
10
+        <!-- rate -->
11
+        <view class="common-card-rate">
12
+          <uni-rate allow-half :value="5" color="#ddd" activeColor="#ff5702" />
13
+        </view>
14
+        <!-- sub title -->
15
+        <view class="common-sub-title">营业时间: 08:00-24:00</view>
16
+      </view>
17
+      <view class="common-card">
18
+        <view class="common-card-title">店铺简介</view>
19
+        <view class="common-card-content"
20
+          >肯德基(Kentucky Fried Chicken),简称KFC,
21
+          全球炸鸡连锁餐厅,总部位于美国肯塔基州路易斯维尔市,在世界上105个国家和地区开设有15000多家肯德基餐厅,
22
+          隶属于全球最大的餐饮连锁企业百胜餐饮集团。 产品以鸡肉为主,并不断推出新产品。</view
23
+        >
24
+        <view class="common-card-title u-margin-top-12">精品推荐</view>
25
+        <view class="common-card-list">
26
+          <view class="card-list-item" v-for="index in 6" :key="index">
27
+            <view class="item-img-wrap">
28
+              <image src="" mode="scaleToFill" />
29
+            </view>
30
+            <view class="list-text">蜜汁全鸡</view>
31
+          </view>
32
+        </view>
33
+      </view>
34
+    </view>
35
+  </view>
36
+</template>
37
+
38
+<script lang="ts" setup></script>
39
+
40
+<style lang="scss" scoepd>
41
+@import '../base.scss';
42
+</style>

+ 14 - 0
src/base/shop/index.vue

@@ -0,0 +1,14 @@
1
+<template>
2
+  <view class="shop-index-container">
3
+    <HyShopItem v-for="index in 5" :key="index" />
4
+  </view>
5
+</template>
6
+
7
+<script lang="ts" setup></script>
8
+
9
+<style lang="scss" scoepd>
10
+.shop-index-container {
11
+  box-sizing: border-box;
12
+  padding: 32rpx 28rpx;
13
+}
14
+</style>

+ 14 - 0
src/base/shop/meal.vue

@@ -0,0 +1,14 @@
1
+<template>
2
+  <view class="shop-index-container">
3
+    <HyShopItem v-for="index in 5" :key="index" price />
4
+  </view>
5
+</template>
6
+
7
+<script lang="ts" setup></script>
8
+
9
+<style lang="scss" scoepd>
10
+.shop-index-container {
11
+  box-sizing: border-box;
12
+  padding: 32rpx 28rpx;
13
+}
14
+</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>

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

@@ -0,0 +1,99 @@
1
+<template>
2
+  <view class="suggest-record-container">
3
+    <view class="record-item">
4
+      <view class="record-item-header">
5
+        <view class="title u-line-2">功能还有不少需要提升的,需要改进的地方不少希望尽快处理提升完善</view>
6
+        <view class="pic-list">
7
+          <view class="pic-wrap">
8
+            <image src="" mode="scaleToFill" />
9
+          </view>
10
+          <view class="pic-wrap">
11
+            <image src="" mode="scaleToFill" />
12
+          </view>
13
+        </view>
14
+        <view class="time-status-wrap">
15
+          <view class="time-text">2025-10-12 12:36:50</view>
16
+          <view class="status-text">已完结</view>
17
+        </view>
18
+      </view>
19
+      <view class="record-item-bottom">
20
+        <view class="bottom-title-text">感谢您对我们的反馈,我们会持续优化, 继续完善!</view>
21
+        <view class="bottom-time-text">2025-10-12 12:36:50</view>
22
+      </view>
23
+    </view>
24
+  </view>
25
+</template>
26
+
27
+<script lang="ts" setup></script>
28
+
29
+<style lang="scss" scoped>
30
+.suggest-record-container {
31
+  box-sizing: border-box;
32
+  width: 100%;
33
+  padding: 34rpx 28rpx;
34
+  .record-item {
35
+    box-sizing: border-box;
36
+    padding: 34rpx 34rpx 38rpx 34rpx;
37
+    background-color: #fff;
38
+    border-radius: 14rpx;
39
+    .record-item-header {
40
+      box-sizing: border-box;
41
+      padding-bottom: 24rpx;
42
+      border-bottom: 1rpx solid #f4f5f9;
43
+      .title {
44
+        font-size: 28rpx;
45
+        color: #333;
46
+        font-weight: 600;
47
+      }
48
+      .pic-list {
49
+        display: flex;
50
+        align-items: center;
51
+        margin-top: 20rpx;
52
+        .pic-wrap {
53
+          width: 139rpx;
54
+          height: 139rpx;
55
+          border-radius: 8rpx;
56
+          background-color: #f4f5f9;
57
+          margin-right: 24rpx;
58
+          image {
59
+            width: 139rpx;
60
+            height: 139rpx;
61
+            border-radius: 8rpx;
62
+          }
63
+        }
64
+      }
65
+      .time-status-wrap {
66
+        display: flex;
67
+        align-items: center;
68
+        justify-content: space-between;
69
+        margin-top: 22rpx;
70
+        .time-text {
71
+          font-size: 25rpx;
72
+          color: #999;
73
+        }
74
+        .status-text {
75
+          font-size: 25rpx;
76
+          color: #999;
77
+          &.warning {
78
+            color: #ff7024;
79
+          }
80
+        }
81
+      }
82
+    }
83
+    .record-item-bottom {
84
+      box-sizing: border-box;
85
+      padding-top: 26rpx;
86
+      .bottom-title-text {
87
+        font-size: 28rpx;
88
+        font-weight: 500;
89
+        line-height: 42rpx;
90
+        margin-bottom: 20rpx;
91
+      }
92
+      .bottom-time-text {
93
+        font-size: 25rpx;
94
+        color: #999;
95
+      }
96
+    }
97
+  }
98
+}
99
+</style>

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

@@ -0,0 +1,91 @@
1
+<template>
2
+  <view class="suggest-submit-container">
3
+    <!-- 问题类型 -->
4
+    <HyCard title="请选择您要反馈的问题类型">
5
+      <view class="suggest-type-wrapper">
6
+        <view class="type-item active">现场环境</view>
7
+        <view class="type-item">商家服务</view>
8
+        <view class="type-item">商品问题</view>
9
+        <view class="type-item">价格问题</view>
10
+        <view class="type-item">其他</view>
11
+      </view>
12
+    </HyCard>
13
+    <!-- 描述问题 -->
14
+    <HyCard title="请描述您的问题">
15
+      <view class="suggest-common-wrapper">
16
+        <u-input
17
+          type="textarea"
18
+          :height="328"
19
+          :maxlength="200"
20
+          border
21
+          placeholder="请详细描述您的问题,以便于我们及时跟进及解决(最多可输入200字)"
22
+        ></u-input>
23
+      </view>
24
+    </HyCard>
25
+    <!-- 图片描述 -->
26
+    <HyCard title="图片描述(选填)">
27
+      <view class="suggest-common-wrapper">
28
+        <u-upload />
29
+      </view>
30
+    </HyCard>
31
+    <!-- 联系方式 -->
32
+    <HyCard title="联系方式">
33
+      <view class="suggest-common-wrapper">
34
+        <u-input placeholder="请输入您的联系方式" />
35
+      </view>
36
+    </HyCard>
37
+    <!-- 提交 -->
38
+    <view class="submit-btn-wrapper">提交</view>
39
+  </view>
40
+</template>
41
+
42
+<script lang="ts" setup></script>
43
+
44
+<style lang="scss" scoped>
45
+.suggest-submit-container {
46
+  box-sizing: border-box;
47
+  padding: 34rpx 28rpx 172rpx 28rpx;
48
+  .suggest-type-wrapper {
49
+    margin-top: 26rpx;
50
+    display: flex;
51
+    flex-wrap: wrap;
52
+    .type-item {
53
+      width: 201rpx;
54
+      height: 63rpx;
55
+      display: flex;
56
+      align-items: center;
57
+      justify-content: center;
58
+      background-color: #f6f7fb;
59
+      font-size: 28rpx;
60
+      color: #a2a8be;
61
+      border-radius: 14rpx;
62
+      margin-right: 16rpx;
63
+      margin-bottom: 15rpx;
64
+      &:nth-child(3n) {
65
+        margin-right: 0;
66
+      }
67
+      &.active {
68
+        background-color: #2375ff;
69
+        color: #fff;
70
+      }
71
+    }
72
+  }
73
+  .suggest-common-wrapper {
74
+    margin-top: 26rpx;
75
+  }
76
+  .submit-btn-wrapper {
77
+    width: 694rpx;
78
+    height: 83rpx;
79
+    display: flex;
80
+    align-items: center;
81
+    justify-content: center;
82
+    background-color: #2374ff;
83
+    font-size: 31rpx;
84
+    color: #fff;
85
+    border-radius: 42rpx;
86
+    position: fixed;
87
+    bottom: 31rpx;
88
+    left: 28rpx;
89
+  }
90
+}
91
+</style>

+ 68 - 0
src/base/travel/tips.vue

@@ -0,0 +1,68 @@
1
+<template>
2
+  <view class="travel-tips-container">
3
+    <!-- tabs -->
4
+    <u-tabs
5
+      v-model="tabActive"
6
+      :list="list"
7
+      :is-scroll="false"
8
+      inactive-color="#333"
9
+      active-color="#333"
10
+      :bar-style="{
11
+        backgroundColor: '#4a8cfe'
12
+      }"
13
+    ></u-tabs>
14
+    <!-- list -->
15
+    <view class="travel-tips-list">
16
+      <view class="tips-list-item">
17
+        <view class="title u-line-2">沈海高速海口方向K31+900M~K29+000M处养护施工的公告</view>
18
+        <view class="content"
19
+          >沈海高速山东段青岛往海口方向K31+900M~K29+000M处正在养护施工,占用主车道及应急车道,该路段目前超车道单道通行,请途经车辆谨慎驾驶,提前减速。</view
20
+        >
21
+        <view class="time">2025-10-12 12:36:50</view>
22
+      </view>
23
+    </view>
24
+  </view>
25
+</template>
26
+
27
+<script lang="ts" setup>
28
+import { ref } from 'vue'
29
+
30
+const list = ref([{ name: '路况信息' }, { name: '优惠促销' }, { name: '天气预警' }])
31
+const tabActive = ref(0)
32
+</script>
33
+
34
+<style lang="scss" scoped>
35
+.travel-tips-container {
36
+  box-sizing: border-box;
37
+  .travel-tips-list {
38
+    width: 694rpx;
39
+    margin: 0 auto;
40
+    box-sizing: border-box;
41
+    padding-top: 35rpx;
42
+    .tips-list-item {
43
+      width: 100%;
44
+      border-radius: 14rpx;
45
+      box-sizing: border-box;
46
+      padding: 33rpx;
47
+      background-color: #fff;
48
+      .title {
49
+        font-size: 28rpx;
50
+        color: #333;
51
+        line-height: 42rpx;
52
+        font-weight: 500;
53
+        margin-bottom: 24rpx;
54
+      }
55
+      .content {
56
+        font-size: 22rpx;
57
+        color: #333;
58
+        line-height: 33rpx;
59
+        margin-bottom: 22rpx;
60
+      }
61
+      .time {
62
+        font-size: 22rpx;
63
+        color: #999;
64
+      }
65
+    }
66
+  }
67
+}
68
+</style>

+ 88 - 0
src/components/HyCard.vue

@@ -0,0 +1,88 @@
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" @click="handleClickRight">
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
+
48
+const emit = defineEmits(['clickRight'])
49
+
50
+const handleClickRight = () => {
51
+  emit('clickRight')
52
+}
53
+</script>
54
+
55
+<style lang="scss" scoped>
56
+.hy-card-container {
57
+  width: 100%;
58
+  box-sizing: border-box;
59
+  padding: 22rpx;
60
+  background-color: #fff;
61
+  border-radius: 14rpx;
62
+  &.bg-card {
63
+    width: 100%;
64
+    min-height: 549rpx;
65
+    background-position: center center;
66
+    background-size: 100% 100%;
67
+    background-repeat: no-repeat;
68
+  }
69
+  .main-title-box {
70
+    display: flex;
71
+    align-items: center;
72
+    justify-content: space-between;
73
+    .text {
74
+      font-size: 31rpx;
75
+      color: #333;
76
+      font-weight: 600;
77
+    }
78
+    .right-text-wrap {
79
+      display: flex;
80
+      align-items: center;
81
+      text {
82
+        font-size: 21rpx;
83
+        color: #292e36;
84
+      }
85
+    }
86
+  }
87
+}
88
+</style>

+ 1 - 1
src/components/HyPopup.vue

@@ -89,7 +89,7 @@ const props = withDefaults(defineProps<Props>(), {
89 89
   animationType: 'scale',
90 90
   position: 'center',
91 91
   width: '80%',
92
-  maxWidth: '600rpx',
92
+  maxWidth: '750rpx',
93 93
   backgroundColor: 'transparent',
94 94
   overlayBackgroundColor: 'rgba(0, 0, 0, 0.8)',
95 95
   borderRadius: 24,

+ 184 - 0
src/components/HySelect.vue

@@ -0,0 +1,184 @@
1
+<template>
2
+  <view class="select-container">
3
+    <!-- 下拉选择触发区域 -->
4
+    <view class="custom-select" @tap="toggleDropdown">
5
+      <text class="select-text">{{ selectedOptionText || placeholder }}</text>
6
+      <image class="select-icon" :class="{ rotate: dropdownVisible }" :src="minioUrl + '/icon_bottom_arrow.png'" mode="scaleToFill" />
7
+    </view>
8
+
9
+    <!-- 下拉菜单 -->
10
+    <view class="dropdown-menu" v-if="dropdownVisible" @tap.stop>
11
+      <view class="dropdown-item" v-for="item in parkList" :key="item.id" :class="{ selected: selectedValue === item.id }" @tap="selectItem(item)">
12
+        <text>{{ item.name }}</text>
13
+        <u-icon v-if="selectedValue === item.id" name="checkmark" color="#2979ff" size="32"></u-icon>
14
+      </view>
15
+    </view>
16
+
17
+    <!-- 点击外部区域关闭下拉菜单的遮罩 - 移动端使用tap事件 -->
18
+    <view class="dropdown-overlay" v-if="dropdownVisible" @tap="closeDropdown"></view>
19
+  </view>
20
+</template>
21
+
22
+<script setup lang="ts">
23
+import { ref, computed, watch } from 'vue'
24
+import { useGlobal } from '@/composables/index'
25
+const { minioUrl } = useGlobal()
26
+
27
+// 定义组件接收的props
28
+const props = defineProps({
29
+  // 当前选中的值
30
+  modelValue: {
31
+    type: String,
32
+    default: ''
33
+  },
34
+  // 占位符文本
35
+  placeholder: {
36
+    type: String,
37
+    default: '请选择'
38
+  }
39
+})
40
+
41
+// 定义组件触发的事件
42
+const emit = defineEmits(['update:modelValue', 'change'])
43
+
44
+// 内部状态
45
+const dropdownVisible = ref(false)
46
+const selectedValue = ref(props.modelValue)
47
+
48
+const parkList = ref([
49
+  { id: '1', name: '海青服务区' },
50
+  { id: '2', name: '黄岛服务区' },
51
+  { id: '3', name: '南村服务区' },
52
+  { id: '4', name: '胶州服务区' }
53
+])
54
+
55
+// 计算当前选中的选项文本
56
+const selectedOptionText = computed(() => {
57
+  const selected = parkList.value.find((item) => item.id === selectedValue.value)
58
+  return selected ? selected.name : ''
59
+})
60
+
61
+// 监听modelValue变化,同步内部状态
62
+watch(
63
+  () => props.modelValue,
64
+  (newValue) => {
65
+    selectedValue.value = newValue
66
+  }
67
+)
68
+
69
+// 切换下拉菜单显示/隐藏
70
+const toggleDropdown = () => {
71
+  dropdownVisible.value = !dropdownVisible.value
72
+}
73
+
74
+// 关闭下拉菜单
75
+const closeDropdown = () => {
76
+  dropdownVisible.value = false
77
+}
78
+
79
+// 选择选项
80
+const selectItem = (item: { id: string; name: string }) => {
81
+  selectedValue.value = item.id
82
+  dropdownVisible.value = false
83
+
84
+  // 触发事件通知父组件
85
+  emit('update:modelValue', item.id)
86
+  emit('change', item)
87
+}
88
+</script>
89
+
90
+<style lang="scss" scoped>
91
+.select-container {
92
+  position: relative;
93
+  z-index: 100;
94
+  .custom-select {
95
+    width: 100%;
96
+    height: 60rpx;
97
+    display: flex;
98
+    align-items: center;
99
+    justify-content: space-between;
100
+    // padding: 0 20rpx;
101
+    cursor: pointer;
102
+    transition: background-color 0.2s;
103
+
104
+    .select-text {
105
+      font-size: 30rpx;
106
+      color: #3b3c3b;
107
+      font-weight: 600;
108
+      overflow: hidden;
109
+      text-overflow: ellipsis;
110
+      white-space: nowrap;
111
+      flex: 1;
112
+    }
113
+
114
+    .select-icon {
115
+      width: 13rpx;
116
+      height: 10rpx;
117
+      margin-left: 10rpx;
118
+      transition: transform 0.2s;
119
+    }
120
+
121
+    .select-icon.rotate {
122
+      transform: rotate(180deg);
123
+    }
124
+  }
125
+
126
+  .dropdown-menu {
127
+    position: absolute;
128
+    top: 70rpx;
129
+    width: 384rpx;
130
+    max-height: 400rpx;
131
+    background-color: #fff;
132
+    border-radius: 12rpx;
133
+    box-shadow: 0 4rpx 16rpx rgba(0, 0, 0, 0.1);
134
+    overflow-y: auto;
135
+    z-index: 200;
136
+    box-sizing: border-box;
137
+    padding: 15rpx;
138
+
139
+    .dropdown-item {
140
+      height: 80rpx;
141
+      padding: 0 24rpx;
142
+      display: flex;
143
+      align-items: center;
144
+      justify-content: space-between;
145
+      cursor: pointer;
146
+      transition: background-color 0.2s;
147
+
148
+      &:last-child {
149
+        border-bottom: none;
150
+      }
151
+
152
+      &.selected {
153
+        background-color: #f0f7ff;
154
+        color: #007aff;
155
+        border-radius: 8rpx;
156
+        text {
157
+          color: #007aff;
158
+          font-weight: 500;
159
+        }
160
+      }
161
+
162
+      text {
163
+        font-size: 26rpx;
164
+        color: #1d2129;
165
+      }
166
+
167
+      .check-icon {
168
+        width: 32rpx;
169
+        height: 32rpx;
170
+      }
171
+    }
172
+  }
173
+}
174
+/* 点击外部区域关闭下拉菜单的遮罩 */
175
+.dropdown-overlay {
176
+  position: fixed;
177
+  top: 0;
178
+  left: 0;
179
+  right: 0;
180
+  bottom: 0;
181
+  z-index: 99;
182
+  background-color: transparent;
183
+}
184
+</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;

+ 22 - 2
src/components/HyShopItem.vue

@@ -1,13 +1,14 @@
1 1
 <template>
2
-  <view class="shop-item-wrapper">
2
+  <view class="shop-item-wrapper" @click="navigateToDetails">
3 3
     <view class="item-left-wrap"></view>
4 4
     <view class="item-right-wrap">
5 5
       <view class="top">
6 6
         <view class="title u-line-1">肯德基</view>
7 7
         <view class="rate-box">
8
-          <uni-rate allow-half v-model="rate" color="#ddd" activeColor="#ff5702" />
8
+          <uni-rate allow-half v-model="rate" color="#ddd" activeColor="#ff5702" size="20" />
9 9
           <text class="rate-text">{{ rate }}</text>
10 10
         </view>
11
+        <view class="price" v-if="price">¥50/人</view>
11 12
       </view>
12 13
       <view class="bottom">
13 14
         <view class="tag-item">西式快餐</view>
@@ -21,6 +22,19 @@
21 22
 import { ref } from 'vue'
22 23
 
23 24
 const rate = ref(3.5)
25
+
26
+defineProps({
27
+  price: {
28
+    type: Boolean,
29
+    default: false
30
+  }
31
+})
32
+
33
+const navigateToDetails = () => {
34
+  uni.navigateTo({
35
+    url: '/base/shop/details'
36
+  })
37
+}
24 38
 </script>
25 39
 
26 40
 <style lang="scss" scoped>
@@ -31,6 +45,7 @@ const rate = ref(3.5)
31 45
   background-color: #fff;
32 46
   border-radius: 14rpx;
33 47
   display: flex;
48
+  margin-bottom: 32rpx;
34 49
   .item-left-wrap {
35 50
     width: 188rpx;
36 51
     height: 188rpx;
@@ -62,6 +77,11 @@ const rate = ref(3.5)
62 77
           margin-left: 14rpx;
63 78
         }
64 79
       }
80
+      .price {
81
+        font-size: 25rpx;
82
+        color: #ff5704;
83
+        margin-top: 4rpx;
84
+      }
65 85
     }
66 86
     .bottom {
67 87
       display: flex;

+ 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>

+ 63 - 0
src/components/HyTag.vue

@@ -0,0 +1,63 @@
1
+<template>
2
+  <view class="tag-item-wrapper">
3
+    <view class="tag-item-content" :class="{ 'need-border': needBorder }">
4
+      <image :src="minioUrl + imgUrl" :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
+  imgUrl: {
25
+    type: String,
26
+    default: '/icon_iot_canyin.png'
27
+  },
28
+  text: {
29
+    type: String,
30
+    default: '餐饮'
31
+  },
32
+  fontSize: {
33
+    type: String,
34
+    default: '22rpx'
35
+  },
36
+  needBorder: {
37
+    type: Boolean,
38
+    default: false
39
+  }
40
+})
41
+</script>
42
+
43
+<style lang="scss" scoped>
44
+.tag-item-wrapper {
45
+  box-sizing: border-box;
46
+  margin: 0 10rpx 20rpx 0;
47
+  .tag-item-content {
48
+    border-radius: 4rpx;
49
+    display: flex;
50
+    align-items: center;
51
+    width: fit-content;
52
+    &.need-border {
53
+      padding: 4rpx 22rpx;
54
+      border: 1rpx solid#2374ff;
55
+      background-color: #e6efff;
56
+    }
57
+    .tag-item-text {
58
+      margin-left: 6rpx;
59
+      line-height: 30rpx;
60
+    }
61
+  }
62
+}
63
+</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,

+ 85 - 9
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
     },
@@ -37,24 +37,100 @@
37 37
       "root": "base",
38 38
       "pages": [
39 39
         {
40
-          "path": "login",
40
+          "path": "suggest/index",
41 41
           "style": {
42
-            "navigationBarTitleText": "登录",
43
-            "enablePullDownRefresh": false,
44
-            "navigationStyle": "custom"
42
+            "navigationBarTitleText": "投诉建议",
43
+            "enablePullDownRefresh": false
45 44
           }
46 45
         },
47 46
         {
48
-          "path": "register",
47
+          "path": "suggest/record",
49 48
           "style": {
50
-            "navigationBarTitleText": "注册",
49
+            "navigationBarTitleText": "反馈历史",
51 50
             "enablePullDownRefresh": true
52 51
           }
53 52
         },
54 53
         {
55
-          "path": "forget",
54
+          "path": "suggest/suggest",
55
+          "style": {
56
+            "navigationBarTitleText": "投诉建议",
57
+            "enablePullDownRefresh": false
58
+          }
59
+        },
60
+        {
61
+          "path": "travel/tips",
62
+          "style": {
63
+            "navigationBarTitleText": "出行提示",
64
+            "enablePullDownRefresh": false
65
+          }
66
+        },
67
+        {
68
+          "path": "shop/index",
69
+          "style": {
70
+            "navigationBarTitleText": "精选商家",
71
+            "enablePullDownRefresh": false
72
+          }
73
+        },
74
+        {
75
+          "path": "shop/meal",
76
+          "style": {
77
+            "navigationBarTitleText": "我要吃饭",
78
+            "enablePullDownRefresh": false
79
+          }
80
+        },
81
+        {
82
+          "path": "shop/details",
83
+          "style": {
84
+            "navigationBarTitleText": "详情",
85
+            "enablePullDownRefresh": false
86
+          }
87
+        },
88
+        {
89
+          "path": "common/spot",
90
+          "style": {
91
+            "navigationBarTitleText": "景点",
92
+            "enablePullDownRefresh": false
93
+          }
94
+        },
95
+        {
96
+          "path": "common/join",
97
+          "style": {
98
+            "navigationBarTitleText": "招商加盟",
99
+            "enablePullDownRefresh": false
100
+          }
101
+        },
102
+        {
103
+          "path": "common/refuel",
104
+          "style": {
105
+            "navigationBarTitleText": "我要加油",
106
+            "enablePullDownRefresh": false
107
+          }
108
+        },
109
+        {
110
+          "path": "common/charge",
111
+          "style": {
112
+            "navigationBarTitleText": "我要充电",
113
+            "enablePullDownRefresh": false
114
+          }
115
+        },
116
+        {
117
+          "path": "common/repair",
118
+          "style": {
119
+            "navigationBarTitleText": "我要修车",
120
+            "enablePullDownRefresh": false
121
+          }
122
+        },
123
+        {
124
+          "path": "common/driversHome",
125
+          "style": {
126
+            "navigationBarTitleText": "司机之家",
127
+            "enablePullDownRefresh": false
128
+          }
129
+        },
130
+        {
131
+          "path": "common/nursingRoom",
56 132
           "style": {
57
-            "navigationBarTitleText": "忘记密码",
133
+            "navigationBarTitleText": "母婴室",
58 134
             "enablePullDownRefresh": false
59 135
           }
60 136
         }

+ 587 - 9
src/pages/index.vue

@@ -1,19 +1,597 @@
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: '/base/common/refuel', w: '53rpx', h: '53rpx' },
16
+  { image: `${minioUrl}/icon_nav_02.png`, text: '我要充电', path: '/base/common/charge', w: '60rpx', h: '53rpx' },
17
+  { image: `${minioUrl}/icon_nav_03.png`, text: '我要吃饭', path: '/base/shop/meal', w: '57rpx', h: '57rpx' },
18
+  { image: `${minioUrl}/icon_nav_04.png`, text: '我要修车', path: '/base/common/repair', 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', type: 'popup' },
21
+  { image: `${minioUrl}/icon_nav_07.png`, text: '司机之家', path: '/base/common/driversHome', w: '76rpx', h: '76rpx' },
22
+  { image: `${minioUrl}/icon_nav_08.png`, text: '母婴室', path: '/base/common/nursingRoom', w: '76rpx', h: '76rpx' },
23
+  { image: `${minioUrl}/icon_nav_09.png`, text: '失物招领', path: '', w: '76rpx', h: '76rpx', type: 'popup' },
24
+  { image: `${minioUrl}/icon_nav_10.png`, text: '招商加盟', path: '/base/common/join', w: '76rpx', h: '76rpx' }
25
+])
26
+
27
+const selectedValue = ref('1')
28
+
29
+// 一键救援弹窗展示
30
+const isRescueShow = ref(false)
31
+// 失物招领弹窗展示
32
+const isLostFoundShow = ref(false)
33
+
34
+// 金刚区点击事件
35
+const navClick = (item: { path: string; type?: string; text?: string }) => {
36
+  if (item.type === 'popup') {
37
+    // 弹窗展示
38
+    if (item.text === '一键救援') {
39
+      isRescueShow.value = true
40
+    } else if (item.text === '失物招领') {
41
+      isLostFoundShow.value = true
42
+    }
43
+    return
44
+  }
45
+  uni.navigateTo({
46
+    url: item.path
47
+  })
48
+}
49
+
50
+// 出行提示点击事件
51
+const navigateToTips = () => {
52
+  uni.navigateTo({
53
+    url: '/base/travel/tips'
54
+  })
55
+}
56
+
57
+// 精选商家点击事件
58
+const navigateToShop = () => {
59
+  uni.navigateTo({
60
+    url: '/base/shop/index'
61
+  })
62
+}
63
+
64
+// 景点点击事件
65
+const navigateToSpot = () => {
66
+  uni.navigateTo({
67
+    url: '/base/common/spot'
68
+  })
69
+}
70
+
71
+// 导航功能实现
72
+const goToNavigation = () => {
73
+  // 这里需要替换为实际的景点坐标
74
+  const latitude = 35.86166 // 示例纬度(琅琊台风景区)
75
+  const longitude = 119.97444 // 示例经度(琅琊台风景区)
76
+  const name = '琅琊台风景区'
77
+  // 使用uni-app的地图API打开系统地图进行导航
78
+  uni.openLocation({
79
+    latitude: latitude,
80
+    longitude: longitude,
81
+    name: name,
82
+    success: () => {
83
+      console.log('导航功能调用成功')
84
+    }
85
+  })
86
+}
87
+
88
+// 拨打电话
89
+const callPhone = (phone: string) => {
90
+  uni.makePhoneCall({
91
+    phoneNumber: phone
92
+  })
93
+}
5 94
 </script>
6 95
 
7 96
 <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>
97
+  <view class="index-container">
98
+    <!-- 头部背景 -->
99
+    <view class="header-bg" :style="{ backgroundImage: 'url(' + minioUrl + '/index_bg.png)' }"></view>
100
+    <!-- header -->
101
+    <view class="index-header-wrapper">
102
+      <image :src="minioUrl + '/logo.png'" mode="scaleToFill" class="logo" />
103
+      <view class="header-right-wrap">
104
+        <view class="top-title-box">
105
+          <HySelect v-model="selectedValue" />
106
+        </view>
107
+        <image class="star-img" :src="minioUrl + '/star.png'" mode="scaleToFill" />
108
+        <view class="bottom-box">
109
+          <!-- 天气 -->
110
+          <iframe
111
+            scrolling="no"
112
+            src="https://widget.tianqiapi.com/?style=tv&skin=cucumber&color=333"
113
+            frameborder="0"
114
+            width="130"
115
+            height="25"
116
+            allowtransparency="true"
117
+          ></iframe>
118
+          <!-- 营业状态 -->
119
+          <view class="bottom-tag-item">正常营业</view>
120
+        </view>
121
+      </view>
122
+    </view>
123
+    <!-- 轮播图 -->
124
+    <u-swiper :height="306" :list="list"></u-swiper>
125
+    <!-- 金刚区 -->
126
+    <view class="index-nav-wrapper">
127
+      <view class="nav-item" v-for="(item, index) in navList" :key="index" @click="navClick(item)">
128
+        <view class="nav-image">
129
+          <image :src="item.image" mode="scaleToFill" :style="{ width: item.w, height: item.h }" />
130
+        </view>
131
+        <view class="nav-item-text">{{ item.text }}</view>
132
+      </view>
133
+    </view>
134
+    <!-- 出行提示 -->
135
+    <view class="index-tips-wrapper" @click="navigateToTips">
136
+      <image class="tips-img" :src="minioUrl + '/icon_chuxing.png'" mode="scaleToFill" />
137
+      <view class="tips-line"></view>
138
+      <view class="tips-content">临时交通管制的通知</view>
139
+    </view>
140
+    <!-- 加油充电 -->
141
+    <view class="index-tabs-wrapper">
142
+      <u-tabs
143
+        v-model="tabActive"
144
+        :list="tabs"
145
+        active-color="#333"
146
+        :font-size="31"
147
+        :height="60"
148
+        :gutter="20"
149
+        :bar-style="{
150
+          width: '30rpx',
151
+          height: '6rpx',
152
+          backgroundImage: 'linear-gradient(90deg, #0f91f8 0%, #32a9c8 68%, #54c197 100%), linear-gradient(#6c85fc, #6c85fc)',
153
+          backgroundBlendMode: 'normal, normal',
154
+          borderRadius: '2rpx'
155
+        }"
156
+      ></u-tabs>
157
+      <template v-if="tabActive === 0">
158
+        <view class="tab-station-wrap">
159
+          <HyStation v-for="index in 4" :key="index" />
160
+        </view>
161
+      </template>
162
+      <template v-if="tabActive === 1">
163
+        <view class="tab-station-wrap">
164
+          <HyCharge />
165
+        </view>
166
+      </template>
167
+    </view>
168
+    <!-- 服务设施 -->
169
+    <HyCard title="服务设施" :marginBottom="24">
170
+      <view class="index-service-wrapper">
171
+        <HyService v-for="index in 6" :key="index"></HyService>
172
+      </view>
173
+    </HyCard>
174
+    <!-- 人气优选商家 -->
175
+    <HyCard title="人气优选商家" rightText="更多商家" isShowRight :marginBottom="24" @clickRight="navigateToShop">
176
+      <view class="index-shop-wrapper">
177
+        <view class="shop-item" v-for="index in 5" :key="index">
178
+          <view class="img-wrap">
179
+            <image src="" mode="scaleToFill" />
180
+          </view>
181
+          <view class="item-text u-line-1">肯德基</view>
182
+        </view>
183
+      </view>
184
+    </HyCard>
185
+    <!-- 附近景点 -->
186
+    <HyCard title="附近景点" isBg :bgImg="minioUrl + '/index_spot_bg.png'">
187
+      <view class="index-spot-wrapper">
188
+        <view class="spot-item" v-for="index in 5" :key="index" @click="navigateToSpot">
189
+          <view class="spot-img-wrap">
190
+            <image src="" mode="scaleToFill" />
191
+          </view>
192
+          <view class="spot-right-wrap">
193
+            <view class="right-header">
194
+              <view class="title">琅琊台风景区</view>
195
+              <view class="price">50元</view>
196
+            </view>
197
+            <view class="right-tag-box">
198
+              <view class="s-tag-green">自然风光</view>
199
+              <view class="s-tag-green">历史遗迹</view>
200
+              <view class="s-tag-green">休闲度假</view>
201
+            </view>
202
+            <view class="right-location-box">
203
+              <view class="left">
204
+                <image class="icon-address" :src="minioUrl + '/icon_address.png'" mode="scaleToFill" />
205
+                <view class="address-text-wrap">距您<text>30.5</text>公里,驾车预计<text>40</text>分钟</view>
206
+              </view>
207
+              <view class="right-go-icon" @click.stop="goToNavigation">
208
+                <image :src="minioUrl + '/icon_go.png'" mode="scaleToFill" />
209
+              </view>
210
+            </view>
211
+          </view>
212
+        </view>
213
+      </view>
214
+    </HyCard>
215
+    <!-- 一键救援 -->
216
+    <HyPopup v-model="isRescueShow" width="700rpx" showClose>
217
+      <view class="popup-content-wrapper">
218
+        <view class="popup-content-item" @click="callPhone('96659')">
219
+          <view class="p-item-left">
220
+            <view class="left-title">车辆救援</view>
221
+            <view class="left-sub-title">山东高速集团出行服务热线</view>
222
+          </view>
223
+          <view class="p-item-right">
224
+            <view class="mobile">96659</view>
225
+            <view class="icon-box">
226
+              <u-icon name="phone-fill" color="#fff" size="24"></u-icon>
227
+            </view>
228
+          </view>
229
+        </view>
230
+        <view class="popup-content-item" @click="callPhone('12122')">
231
+          <view class="p-item-left">
232
+            <view class="left-title">事故报警</view>
233
+            <view class="left-sub-title">山东高速交警报警热线</view>
234
+          </view>
235
+          <view class="p-item-right">
236
+            <view class="mobile">12122</view>
237
+            <view class="icon-box">
238
+              <u-icon name="phone-fill" color="#fff" size="24"></u-icon>
239
+            </view>
240
+          </view>
241
+        </view>
242
+      </view>
243
+    </HyPopup>
244
+    <!-- 失物招领 -->
245
+    <HyPopup v-model="isLostFoundShow" width="700rpx" showClose>
246
+      <view class="popup-content-wrapper">
247
+        <view class="popup-content-item column" @click="callPhone('0532-8889999')">
248
+          <view class="column-title">失物招领</view>
249
+          <view class="column-mobile">0532-8889999</view>
250
+          <view class="icon-box">
251
+            <u-icon name="phone-fill" color="#fff" size="24"></u-icon>
252
+          </view>
253
+        </view>
254
+      </view>
15 255
     </HyPopup>
16 256
   </view>
17 257
 </template>
18 258
 
19
-<style lang="scss" scoped></style>
259
+<style lang="scss" scoped>
260
+.popup-content-wrapper {
261
+  height: 330rpx;
262
+  background-color: #fff;
263
+  border-radius: 14rpx;
264
+  .popup-content-item {
265
+    width: 100%;
266
+    display: flex;
267
+    align-items: center;
268
+    justify-content: space-between;
269
+    box-sizing: border-box;
270
+    padding: 48rpx 48rpx 24rpx 48rpx;
271
+    &.column {
272
+      flex-direction: column;
273
+      justify-content: center;
274
+      .column-title {
275
+        font-size: 28rpx;
276
+        color: #607f2f;
277
+      }
278
+      .column-mobile {
279
+        font-size: 42rpx;
280
+        color: #333;
281
+        font-weight: 600;
282
+        margin: 40rpx 0;
283
+      }
284
+      .icon-box {
285
+        width: 48rpx;
286
+        height: 48rpx;
287
+        background-color: #247d54;
288
+        display: flex;
289
+        align-items: center;
290
+        justify-content: center;
291
+        border-radius: 50%;
292
+        margin-left: 16rpx;
293
+      }
294
+    }
295
+    .p-item-left {
296
+      display: flex;
297
+      flex-direction: column;
298
+      .left-title {
299
+        font-size: 28rpx;
300
+        color: #607f2f;
301
+      }
302
+      .left-sub-title {
303
+        font-size: 24rpx;
304
+        color: #999;
305
+        margin-top: 8rpx;
306
+      }
307
+    }
308
+    .p-item-right {
309
+      display: flex;
310
+      align-items: center;
311
+      .mobile {
312
+        font-size: 42rpx;
313
+        color: #333;
314
+        font-weight: 600;
315
+      }
316
+      .icon-box {
317
+        width: 48rpx;
318
+        height: 48rpx;
319
+        background-color: #247d54;
320
+        display: flex;
321
+        align-items: center;
322
+        justify-content: center;
323
+        border-radius: 50%;
324
+        margin-left: 16rpx;
325
+      }
326
+    }
327
+  }
328
+}
329
+.index-container {
330
+  width: 100%;
331
+  min-height: 100dvh;
332
+  position: relative;
333
+  box-sizing: border-box;
334
+  padding: 24rpx;
335
+  background-color: #f6f7fb;
336
+  .header-bg {
337
+    width: 100%;
338
+    height: 494rpx;
339
+    background-position: center center;
340
+    background-repeat: no-repeat;
341
+    background-size: 100% 494rpx;
342
+    position: absolute;
343
+    top: 0;
344
+    left: 0;
345
+    z-index: 1;
346
+  }
347
+  .index-header-wrapper {
348
+    display: flex;
349
+    align-items: center;
350
+    box-sizing: border-box;
351
+    padding-left: 33rpx;
352
+    position: relative;
353
+    z-index: 2;
354
+    margin-bottom: 24rpx;
355
+    .logo {
356
+      width: 105rpx;
357
+      height: 92rpx;
358
+    }
359
+    .header-right-wrap {
360
+      display: flex;
361
+      flex-direction: column;
362
+      margin-left: 24rpx;
363
+      .top-title-box {
364
+        display: flex;
365
+        align-items: center;
366
+        .title-text {
367
+          font-size: 30rpx;
368
+          color: #3b3c3b;
369
+          font-weight: 600;
370
+        }
371
+        image {
372
+          width: 13rpx;
373
+          height: 10rpx;
374
+          margin-left: 8rpx;
375
+        }
376
+      }
377
+      .star-img {
378
+        width: 146rpx;
379
+        height: 27rpx;
380
+      }
381
+      .bottom-box {
382
+        display: flex;
383
+        align-items: center;
384
+        margin-top: 10rpx;
385
+        .bottom-tag-item {
386
+          font-size: 17rpx;
387
+          color: #fff;
388
+          box-sizing: border-box;
389
+          padding: 0 12rpx;
390
+          background-color: #3bb0b9;
391
+          border-radius: 8rpx;
392
+          line-height: 40rpx;
393
+          margin-top: -15rpx;
394
+        }
395
+      }
396
+    }
397
+  }
398
+  :deep(.u-swiper-wrap) {
399
+    position: relative;
400
+    z-index: 1;
401
+  }
402
+  .index-nav-wrapper {
403
+    display: flex;
404
+    align-items: center;
405
+    flex-wrap: wrap;
406
+    justify-content: space-around;
407
+    margin-top: 40rpx;
408
+    .nav-item {
409
+      display: flex;
410
+      flex-direction: column;
411
+      align-items: center;
412
+      width: 20%;
413
+      .nav-image {
414
+        width: 76rpx;
415
+        height: 76rpx;
416
+        display: flex;
417
+        align-items: center;
418
+        justify-content: center;
419
+        margin-bottom: 14rpx;
420
+      }
421
+      &:nth-child(-n + 5) {
422
+        margin-bottom: 50rpx;
423
+      }
424
+    }
425
+  }
426
+  .index-tips-wrapper {
427
+    width: 100%;
428
+    height: 84rpx;
429
+    border-radius: 14rpx;
430
+    background-color: #fff;
431
+    margin: 24rpx 0;
432
+    box-sizing: border-box;
433
+    padding: 0 30rpx;
434
+    display: flex;
435
+    align-items: center;
436
+    .tips-img {
437
+      width: 53rpx;
438
+      height: 48rpx;
439
+    }
440
+    .tips-line {
441
+      width: 4rpx;
442
+      height: 34rpx;
443
+      background-color: #3866ae;
444
+      margin: 0 23rpx 0 13rpx;
445
+    }
446
+    .tips-content {
447
+      font-size: 25rpx;
448
+      color: #333;
449
+      font-weight: 500;
450
+      line-height: 40rpx;
451
+    }
452
+  }
453
+  .index-tabs-wrapper {
454
+    width: 100%;
455
+    background-color: #fff;
456
+    border-radius: 14rpx;
457
+    box-sizing: border-box;
458
+    padding: 22rpx;
459
+    margin-bottom: 24rpx;
460
+    .tab-station-wrap {
461
+      display: flex;
462
+      align-items: center;
463
+      justify-content: space-around;
464
+      margin-top: 24rpx;
465
+    }
466
+  }
467
+  .index-service-wrapper {
468
+    margin-top: 24rpx;
469
+    display: flex;
470
+    flex-wrap: wrap;
471
+    justify-content: space-between;
472
+    :deep(.service-item-wrapper) {
473
+      margin-bottom: 20rpx;
474
+      &:nth-child(5) {
475
+        margin-bottom: 0;
476
+      }
477
+      &:nth-child(6) {
478
+        margin-bottom: 0;
479
+      }
480
+    }
481
+  }
482
+  .index-shop-wrapper {
483
+    margin-top: 26rpx;
484
+    display: flex;
485
+    align-items: center;
486
+    justify-content: space-between;
487
+    .shop-item {
488
+      display: flex;
489
+      flex-direction: column;
490
+      .img-wrap {
491
+        width: 104rpx;
492
+        height: 104rpx;
493
+        border-radius: 8rpx;
494
+        background-color: #f0f6ff;
495
+        image {
496
+          width: 104rpx;
497
+          height: 104rpx;
498
+          border-radius: 8rpx;
499
+        }
500
+      }
501
+      .item-text {
502
+        width: 100rpx;
503
+        text-align: center;
504
+        margin-top: 12rpx;
505
+      }
506
+    }
507
+  }
508
+  .index-spot-wrapper {
509
+    margin-top: 24rpx;
510
+    .spot-item {
511
+      display: flex;
512
+      align-items: center;
513
+      margin-bottom: 40rpx;
514
+      &:last-child {
515
+        margin-bottom: 0;
516
+      }
517
+      .spot-img-wrap {
518
+        width: 196rpx;
519
+        height: 151rpx;
520
+        background-color: #f6f7fb;
521
+        border-radius: 8rpx;
522
+        image {
523
+          width: 196rpx;
524
+          height: 151rpx;
525
+          border-radius: 8rpx;
526
+        }
527
+      }
528
+      .spot-right-wrap {
529
+        width: 100%;
530
+        flex: 1;
531
+        margin-left: 28rpx;
532
+        .right-header {
533
+          display: flex;
534
+          align-items: center;
535
+          justify-content: space-between;
536
+          .title {
537
+            font-size: 28rpx;
538
+            color: #333;
539
+            font-weight: 600;
540
+          }
541
+          .price {
542
+            font-size: 21rpx;
543
+            color: #fc9b0d;
544
+          }
545
+        }
546
+        .right-tag-box {
547
+          display: flex;
548
+          align-items: center;
549
+          margin-top: 6rpx;
550
+          .s-tag-green {
551
+            width: 125rpx;
552
+            height: 32rpx;
553
+            background-color: #e2f3e4;
554
+            border-radius: 16rpx;
555
+            display: flex;
556
+            align-items: center;
557
+            justify-content: center;
558
+            font-size: 21rpx;
559
+            color: #239450;
560
+            margin-right: 20rpx;
561
+          }
562
+        }
563
+        .right-location-box {
564
+          margin-top: 50rpx;
565
+          display: flex;
566
+          align-items: center;
567
+          justify-content: space-between;
568
+          .left {
569
+            display: flex;
570
+            align-items: center;
571
+            .icon-address {
572
+              width: 18rpx;
573
+              height: 22rpx;
574
+            }
575
+            .address-text-wrap {
576
+              font-size: 21rpx;
577
+              color: #c3c3c3;
578
+              margin-left: 8rpx;
579
+              text {
580
+                color: #fc9904;
581
+              }
582
+            }
583
+          }
584
+          .right-go-icon {
585
+            width: 40rpx;
586
+            height: 40rpx;
587
+            image {
588
+              width: 40rpx;
589
+              height: 40rpx;
590
+            }
591
+          }
592
+        }
593
+      }
594
+    }
595
+  }
596
+}
597
+</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>

+ 142 - 3
src/pages/service.vue

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

BIN
src/static/images/bg_service_03.png


BIN
src/static/images/icon_service_01.png


BIN
src/static/images/icon_sm_jiayou.png


BIN
src/static/images/service_active.png


+ 6 - 7
vite.config.ts

@@ -1,10 +1,3 @@
1
-/*
2
- * @Author: wyd
3
- * @Date: 2024-02
4
- * @LastEditors: wyd
5
- * @LastEditTime: 2024-03
6
- * @Description:
7
- */
8 1
 import { defineConfig, loadEnv } from 'vite'
9 2
 import uni from '@dcloudio/vite-plugin-uni'
10 3
 // import { resolve } from 'path'
@@ -20,6 +13,9 @@ export default defineConfig(({ command, mode }) => {
20 13
       // envDir: resolve(__dirname, 'env'),
21 14
       define: {
22 15
         __VITE_BASE_URL__: JSON.stringify(env.VITE_BASE_URL)
16
+      },
17
+      server: {
18
+        port: 5174
23 19
       }
24 20
     }
25 21
   } else if (mode === 'production') {
@@ -47,6 +43,9 @@ export default defineConfig(({ command, mode }) => {
47 43
       // envDir: resolve(__dirname, 'env'),
48 44
       define: {
49 45
         __VITE_BASE_URL__: JSON.stringify(env.VITE_BASE_URL)
46
+      },
47
+      server: {
48
+        port: 5174
50 49
       }
51 50
     }
52 51
   }