Browse Source

v1.7版本接口调试

yuwenjun1997 2 years ago
parent
commit
fa54b50957

+ 2 - 0
.gitignore

@@ -88,3 +88,5 @@ sw.*
 
 # Vim swap files
 *.swp
+
+.env.*

+ 82 - 28
apis/index.js

@@ -4,66 +4,113 @@
  */
 const queryStringify = function (search = {}) {
   return Object.entries(search)
-    .reduce((t, v) => `${t}${v[0]}=${encodeURIComponent(v[1])}&`, Object.keys(search).length ? '?' : '')
+    .reduce(
+      (t, v) => `${t}${v[0]}=${encodeURIComponent(v[1])}&`,
+      Object.keys(search).length ? '?' : ''
+    )
     .replace(/&$/, '')
 }
 
 export default ($axios) => {
   // 订阅号用户登录
-  const customLogin = (data) => $axios.post('/wx/user/login/subscribe/verify/code', data)
+  const customLogin = (data) =>
+    $axios.post('/wx/user/login/subscribe/verify/code', data)
   // 订阅号用户绑定邀请码登录
-  const customLoginWithCode = (data) => $axios.post('/wx/user/login/subscribe/invitation/code', data)
+  const customLoginWithCode = (data) =>
+    $axios.post('/wx/user/login/subscribe/invitation/code', data)
   // 服务号微信授权登录
-  const wechatLogin = (data) => $axios.post('/wx/user/login/authorization', data)
+  const wechatLogin = (data) =>
+    $axios.post('/wx/user/login/authorization', data)
   // 服务号微信授权绑定邀请码登录
-  const wechatLoginWithCode = (data) => $axios.post('/wx/user/login/service/invitation/code', data)
+  const wechatLoginWithCode = (data) =>
+    $axios.post('/wx/user/login/service/invitation/code', data)
   // 获取jssdk配置参数
-  const initWxConfig = (params = {}) => $axios.get('/wx/sdk/config/data', { params })
+  const initWxConfig = (params = {}) =>
+    $axios.get('/wx/sdk/config/data', { params })
   // 发送验证码
-  const sendVerifyCode = (data = {}) => $axios.post('/wx/user/verify/code/send', data)
+  const sendVerifyCode = (data = {}) =>
+    $axios.post('/wx/user/login/verify/code/send', data)
   // 获取文章列表
-  const getArticleList = (params = {}) => $axios.get('/wx/data/article/list', { params })
+  const getArticleList = (params = {}) =>
+    $axios.get('/wx/data/article/list', { params })
   // 获取图片列表
-  const getImageList = (params = {}) => $axios.get('/wx/data/image/list', { params })
+  const getImageList = (params = {}) =>
+    $axios.get('/wx/data/image/list', { params })
   // 获取视频列表
-  const getVideoList = (params = {}) => $axios.get('/wx/data/video/list', { params })
+  const getVideoList = (params = {}) =>
+    $axios.get('/wx/data/video/list', { params })
   // 获取文件列表
-  const getFileList = (params = {}) => $axios.get('/wx/data/file/list', { params })
+  const getFileList = (params = {}) =>
+    $axios.get('/wx/data/file/list', { params })
   // 获取文章详情
-  const getArticleDetail = (params = {}) => $axios.get('/wx/data/article/form/data', { params })
+  const getArticleDetail = (params = {}) =>
+    $axios.get('/wx/data/article/form/data', { params })
   // 用户反馈
   const feedback = (data = {}) => $axios.post('/wx/data/feedback/submit', data)
   // 获取已认证机构列表
-  const getAuthClubList = (params = {}) => $axios.get('/wx/auth/club/list', { params })
+  const getAuthClubList = (params = {}) =>
+    $axios.get('/wx/auth/club/list', { params })
   // 获取已认证机构详情
-  const getAuthClubDetail = (params = {}) => $axios.get('/wx/auth/club/details', { params })
+  const getAuthClubDetail = (params = {}) =>
+    $axios.get('/wx/auth/club/details', { params })
   // 获取已认证商品分类
-  const getAuthProductCateList = (params = {}) => $axios.get('/wx/auth/product/type/list', { params })
+  const getAuthProductCateList = (params = {}) =>
+    $axios.get('/wx/auth/product/type/list', { params })
   // 获取已认证商品列表
-  const getAuthProductList = (params = {}) => $axios.get('/wx/auth/product/list', { params })
+  const getAuthProductList = (params = {}) =>
+    $axios.get('/wx/auth/product/list', { params })
   // 验证token是否到期
-  const checkToken = (data = {}) => $axios.post('/wx/user/token/check', data)
+  const checkToken = (data = {}) =>
+    $axios.post('/wx/user/login/token/check', data)
   // 下载文件
   const downFile = (params = {}) => $axios.get('/download/file', { params })
   // 获取城市列表
-  const fetchCityList = (params = {}) => $axios.get('/address/select', { params })
+  const fetchCityList = (params = {}) =>
+    $axios.get('/address/select', { params })
   // 获取城市列表
   const fetchAllCityList = () => $axios.get('/address/select/all')
   // 获取供应商信息
-  const fetchSupplierInfo = (params = {}) => $axios.get('/wx/auth/shop/info', { params })
+  const fetchSupplierInfo = (params = {}) =>
+    $axios.get('/wx/auth/shop/info', { params })
   // 获取医师列表
-  const fetchDoctorList = (params = {}) => $axios.get('/wx/auth/doctor/list', { params })
+  const fetchDoctorList = (params = {}) =>
+    $axios.get('/wx/auth/doctor/list', { params })
   // 获取医师详情
-  const fetchDoctorDetail = (params = {}) => $axios.get('/wx/auth/doctor/details', { params })
+  const fetchDoctorDetail = (params = {}) =>
+    $axios.get('/wx/auth/doctor/details', { params })
   // 公众号类型
-  const checkAccountType = (params = {}) => $axios.get('/wx/sdk/account/type', { params })
+  const checkAccountType = (params = {}) =>
+    $axios.get('/wx/sdk/account/type', { params })
   // 判断用户手机号是否绑定了机构信息
-  const checkouMobileBindClub = (params = {}) => $axios.get('/wx/user/info', { params })
-
-
+  const checkouMobileBindClub = (params = {}) =>
+    $axios.get('/wx/user/info', { params })
+  // 获取品牌列表
+  const fetchBrandList = (params = {}) =>
+    $axios.get('/shop/brand/list', { params })
+  // 获取设备分类列表
+  const fetchProductSelectList = (params = {}) =>
+    $axios.get('/auth/product/type/select', { params })
+  // 机构注册(全部信息注册)
+  const clubUserRegisterAll = (data = {}) =>
+    $axios.post('/wx/user/register/all', data)
+  // 机构账号注册
+  const clubUserRegister = (data = {}) =>
+    $axios.post('/wx/user/register/simple', data)
+  // 机构账号密码找回
+  const clubUserReset = (data = {}) =>
+    $axios.post('/wx/user/password/update', data)
+  // 机构账号登录
+  const clubUserLogin = (data = {}) =>
+    $axios.post('/wx/user/login/password', data)
+  // 机构账号验证码
+  const clubUserCodeSend = (data = {}) =>
+    $axios.post('/wx/user/login/code/send', data)
   // 高德地图api : 将坐标转化为高德地图坐标
   const assistant = (params = {}) =>
-    fetch('https://restapi.amap.com/v3/assistant/coordinate/convert' + queryStringify(params))
+    fetch(
+      'https://restapi.amap.com/v3/assistant/coordinate/convert' +
+        queryStringify(params)
+    )
 
   return {
     customLogin,
@@ -91,6 +138,13 @@ export default ($axios) => {
     fetchDoctorDetail,
     checkAccountType,
     assistant,
-    checkouMobileBindClub
+    checkouMobileBindClub,
+    fetchBrandList,
+    fetchProductSelectList,
+    clubUserRegisterAll,
+    clubUserRegister,
+    clubUserReset,
+    clubUserLogin,
+    clubUserCodeSend,
   }
-}
+}

+ 44 - 0
components/SimpleAMap/common/utils.js

@@ -0,0 +1,44 @@
+export function initGeocoder() {
+  return new Promise((resolve, reject) => {
+    window.AMap.plugin('AMap.Geocoder', () => {
+      try {
+        const geocoder = new window.AMap.Geocoder({
+          // city 指定进行编码查询的城市,支持传入城市名、adcode 和 citycode
+          city: '全国',
+        })
+        resolve(geocoder)
+      } catch (error) {
+        reject(error)
+      }
+    })
+  })
+}
+
+export function geolocation() {
+  return new Promise((resolve, reject) => {
+    window.AMap.plugin('AMap.Geolocation', () => {
+      const geolocation = new window.AMap.Geolocation({
+        // 是否使用高精度定位,默认:true
+        enableHighAccuracy: true,
+        // 设置定位超时时间,默认:无穷大
+        timeout: 10000,
+        // 定位按钮的停靠位置的偏移量
+        offset: [10, 20],
+        //  定位成功后调整地图视野范围使定位位置及精度范围视野内可见,默认:false
+        zoomToAccuracy: true,
+        //  定位按钮的排放位置,  RB表示右下
+        position: 'RB',
+      })
+
+      geolocation.getCurrentPosition((status, result) => {
+        if (status === 'complete') {
+          resolve(result)
+        } else {
+          reject(result)
+        }
+      })
+    })
+  })
+}
+
+export default {}

+ 160 - 0
components/SimpleAMap/index.vue

@@ -0,0 +1,160 @@
+<template>
+  <div class="a-map" id="aMap"></div>
+</template>
+
+<script>
+import { geolocation, initGeocoder } from './common/utils'
+export default {
+  name: 'AMap',
+  props: {
+    zoom: {
+      type: Number,
+      default: 17
+    },
+    markerIcon: {
+      type: String,
+      default: '/location.png'
+    },
+    center: {
+      type: Boolean,
+      default: false
+    },
+    address: {
+      type: String,
+      default: ''
+    },
+    lnglat: {
+      type: Array,
+      default: null
+    }
+  },
+  data () {
+    return {
+      marker: null,
+      map: null,
+      timer: null,
+      geocoder: null
+    }
+  },
+  methods: {
+    async init () {
+      if (this.lnglat) {
+        console.log('经纬度定位')
+        this.initMap(this.lnglat)
+      } else if (this.address) {
+        console.log('地址定位')
+        this.initGeocoder()
+      } else {
+        console.log('高精度自动定位')
+        this.geolocation()
+      }
+    },
+    // 高精度定位
+    async geolocation () {
+      try {
+        const result = await geolocation()
+        this.$emit('position', {
+          lng: result.position.lng,
+          lat: result.position.lat
+        })
+        this.initMap(result.position)
+      } catch (error) {
+        this.initMap()
+        alert('获取当前位置信息失败,已为您定位到当前城市!')
+        console.log('获取当前位置信息失败,已为您定位到当前城市!')
+      }
+    },
+    // 根据地址信息定位
+    async initGeocoder () {
+      try {
+        this.geocoder = await initGeocoder()
+        console.log(this.geocoder)
+
+        this.geocoder.getLocation(this.address, (status, result) => {
+          if (status === 'complete' && result.info === 'OK') {
+            const position = result.geocodes[0].location
+            this.initMap(position)
+            this.$emit('position', {
+              lng: position.lng,
+              lat: position.lat
+            })
+          }
+        })
+      } catch (error) {
+        console.log(error)
+      }
+    },
+    // 初始化地图
+    initMap (position) {
+      this.map = new window.AMap.Map('aMap', {
+        viewMode: '2D', // 默认使用 2D 模式,如果希望使用带有俯仰角的 3D 模式,请设置 viewMode: '3D',
+        zoom: this.zoom, // 初始化地图层级
+        center: position // 初始化地图中心点
+      })
+      this.map.on('click', this.onMapClick)
+      if (position) {
+        this.map.add(this.initMarker(position))
+      }
+      this.initMapControl()
+    },
+    // 初始化标记点
+    initMarker (position) {
+      this.marker = new window.AMap.Marker({
+        icon: this.markerIcon,
+        position,
+        anchor: 'bottom-center',
+        draggable: true
+      })
+      this.marker.on('dragging', this.moveMarker)
+      return this.marker
+    },
+    // 修改标记点
+    onMapClick (map) {
+      if (this.marker) this.map.remove(this.marker)
+      if (this.center) this.map.setCenter(map.lnglat)
+      this.map.add(this.initMarker(map.lnglat))
+      this.$emit('position', {
+        lng: map.lnglat.lng,
+        lat: map.lnglat.lat
+      })
+    },
+    // 标记点移动
+    moveMarker (marker) {
+      if (this.timer) clearTimeout(this.timer)
+      this.timer = setTimeout(() => {
+        if (this.center) this.map.setCenter(marker.lnglat)
+        this.$emit('position', {
+          lng: marker.lnglat.lng,
+          lat: marker.lnglat.lat
+        })
+      }, 200)
+    },
+    // 地图控件
+    initMapControl () {
+      window.AMap.plugin(
+        ['AMap.ToolBar', 'AMap.Scale', 'AMap.MapType', 'AMap.Geolocation'],
+        () => {
+          // 在图面添加工具条控件,工具条控件集成了缩放、平移、定位等功能按钮在内的组合控件
+          this.map.addControl(new window.AMap.ToolBar())
+
+          // 在图面添加比例尺控件,展示地图在当前层级和纬度下的比例尺
+          this.map.addControl(new window.AMap.Scale())
+
+          // 在图面添加类别切换控件,实现默认图层与卫星图、实施交通图层之间切换的控制
+          this.map.addControl(new window.AMap.MapType())
+
+          // 在图面添加定位控件,用来获取和展示用户主机所在的经纬度位置
+          // this.map.addControl(new window.AMap.Geolocation())
+        }
+      )
+    }
+  }
+}
+</script>
+
+<style lang="scss" scoped>
+#aMap {
+  height: 100%;
+  min-height: 400px;
+}
+</style>

+ 1 - 1
components/SimpleDialog/index.vue

@@ -30,7 +30,7 @@
           v-if="cancel"
           @click="onCancel"
         >
-          确定
+          取消
         </div>
       </div>
     </div>

+ 120 - 23
components/SimpleLogin/index.vue

@@ -4,7 +4,15 @@
       <div class="wrapper flex justify-center items-center" @click.stop>
         <div class="block flex items-center flex-col py-6">
           <div class="close" @click="onClose"></div>
-          <div class="title pb-6">登录</div>
+          <div class="title pb-6">
+            {{
+              formType === 'login'
+                ? '登录'
+                : formType === 'register'
+                ? '注册'
+                : '忘记密码'
+            }}
+          </div>
           <div class="form">
             <div class="form-item mb-4">
               <input
@@ -26,15 +34,15 @@
             </div>
             <div class="form-item mb-4">
               <input
-                type="text"
+                type="password"
                 placeholder="密码"
                 v-model="formData.password"
                 maxlength="11"
               />
             </div>
-            <div class="form-item mb-4" v-if="formType === 'register'">
+            <div class="form-item mb-4" v-if="formType !== 'login'">
               <input
-                type="text"
+                type="password"
                 placeholder="确认密码"
                 v-model="formData.confirmPwd"
               />
@@ -44,8 +52,8 @@
               class="flex justify-between control mt-2"
               v-if="formType === 'login'"
             >
-              <span class="forget">忘记密码</span>
-              <span class="regist">立即注册</span>
+              <span class="forget" @click="onForgetPwd">忘记密码</span>
+              <span class="regist" @click="onRegister">立即注册</span>
             </div>
           </div>
         </div>
@@ -111,41 +119,130 @@ export default {
     },
   },
   methods: {
+    // 忘记密码
+    onForgetPwd() {
+      this.$emit('click', 'forget')
+    },
+    // 立即注册
+    onRegister() {
+      this.$emit('click', 'register')
+    },
+
     async onSubmit() {
       // 验证手机号是否合法
       if (!isMobile(this.formData.mobile)) {
         this.$toast('请输入正确的手机号')
         return
       }
+
+      if (this.formType === 'register') {
+        this.onRegisterSubmit()
+      } else if (this.formType === 'forget') {
+        this.onForgetSubmit()
+      } else {
+        this.onLoginSubmit()
+      }
+    },
+
+    // 忘记密码
+    async onForgetSubmit() {
       if (!this.formData.verifyCode) {
         this.$toast('请输入验证码')
         return
       }
+
+      if (this.formData.password.length < 6) {
+        this.$toast('密码长度需大于6位')
+        return
+      }
+
+      if (this.formData.password !== this.formData.confirmPwd) {
+        this.$toast('两次输入的密码不一致')
+        return
+      }
+
       try {
-        this.formData.authUserId = this.authUserId
-        const res = await this.$http.api.customLogin(this.formData)
-        this.$store.dispatch('user/login', res.data)
-        this.$setStorage(this.routePrefix, 'userInfo', res.data)
-        // 关闭登录窗口
-        this.onClose()
-        // 重定向
-        const login_redicret = this.$getStorage(
-          this.routePrefix,
-          'login_redicret'
-        )
-        if (login_redicret && login_redicret !== this.$route.path) {
-          this.$router.push(login_redicret)
-        }
+        await this.$http.api.clubUserReset({
+          mobile: this.formData.mobile,
+          verifyCode: this.formData.verifyCode,
+          password: this.formData.password,
+          authUserId: this.authUserId,
+        })
+      } catch (error) {}
+    },
+
+    // 用户注册
+    async onRegisterSubmit() {
+      if (!this.formData.verifyCode) {
+        this.$toast('请输入验证码')
+        return
+      }
+      if (this.formData.password.length < 6) {
+        this.$toast('密码长度需大于6位')
+        return
+      }
+      if (this.formData.password !== this.formData.confirmPwd) {
+        this.$toast('两次输入的密码不一致')
+        return
+      }
+
+      try {
+        await this.$http.api.clubUserRegister({
+          mobile: this.formData.mobile,
+          verifyCode: this.formData.verifyCode,
+          password: this.formData.password,
+          authUserId: this.authUserId,
+        })
+        this.$toast('注册成功,请登录')
+        this.$emit('click', 'login')
+      } catch (error) {
+        console.log(error)
+        this.$toast(error.msg)
+      }
+    },
+
+    // 用户登录
+    async onLoginSubmit() {
+      if (!this.formData.password) {
+        this.$toast('密码不能为空')
+        return
+      }
+
+      try {
+        const res = await this.$http.api.clubUserLogin({
+          mobile: this.formData.mobile,
+          password: this.formData.password,
+          authUserId: this.authUserId,
+        })
+        this.login(res)
       } catch (error) {
         console.log(error)
+        this.$toast(error.msg)
+      }
+    },
+
+    login(res) {
+      this.$store.dispatch('user/login', res.data)
+      this.$setStorage(this.routePrefix, 'userInfo', res.data)
+      // 关闭登录窗口
+      this.onClose()
+      // 重定向
+      const login_redicret = this.$getStorage(
+        this.routePrefix,
+        'login_redicret'
+      )
+      if (login_redicret && login_redicret !== this.$route.path) {
+        this.$router.push(login_redicret)
       }
     },
+
     // 关闭登录窗口
     onClose() {
       this.$store.commit('app/HIDE_LOGIN')
       this.formData.mobile = ''
       this.formData.verifyCode = ''
-      this.formData.authUserId = ''
+      this.formData.password = ''
+      this.formData.confirmPwd = ''
     },
     async onSend() {
       if (this.sendStatus > 0) return
@@ -156,10 +253,10 @@ export default {
       }
       try {
         // 发送验证码
-        const res = await this.$http.api.sendVerifyCode({
+        await this.$http.api.clubUserCodeSend({
           mobile: this.formData.mobile,
           authUserId: this.authUserId,
-          type: 1,
+          type: this.formType === 'register' ? 1 : 2,
         })
         this.$toast('验证码已发送')
         // 开启倒计时

+ 1 - 1
components/SimpleStep/index.vue

@@ -4,7 +4,7 @@
       class="simple-step__item"
       v-for="(item, index) in list"
       :key="index"
-      :class="{ active: index === active }"
+      :class="{ active: item.id === active }"
     >
       <span v-text="item[label]"></span>
       <span class="simple-step__line"></span>

+ 5 - 0
components/SimpleUploadImage/index.vue

@@ -14,6 +14,7 @@
       :multiple="multiple"
       :accept="accept"
       :file-list="imageList"
+      :disabled="disabled"
     >
       <div v-if="tip" slot="tip" class="el-upload__tip">{{ tip }}</div>
       <i slot="default" class="el-icon-plus" />
@@ -61,6 +62,10 @@ export default {
       type: Function,
       default: () => true,
     },
+    disabled: {
+      type: Boolean,
+      default: false,
+    },
   },
   data() {
     return {

+ 5 - 1
layouts/app.vue

@@ -40,7 +40,7 @@
       <div class="footer flex justify-center items-center">
         - 由采美网提供技术支持 -
       </div>
-      <SimpleLogin :type="formType"></SimpleLogin>
+      <SimpleLogin :type="formType" @click="onLoginClick"></SimpleLogin>
     </div>
   </div>
 </template>
@@ -83,6 +83,10 @@ export default {
     window.removeEventListener('resize', () => {})
   },
   methods: {
+    onLoginClick(type) {
+      this.formType = type
+    },
+
     init() {
       this.responseWidth()
       this.initPageData()

+ 8 - 0
nuxt.config.js

@@ -21,6 +21,14 @@ export default {
       { name: 'format-detection', content: 'telephone=no' },
     ],
     // link: [{ rel: 'icon', type: 'image/x-icon', href: '/favicon.ico' }],
+    script: [
+      {
+        src: '/map.config.js',
+      },
+      {
+        src: 'https://webapi.amap.com/maps?v=2.0&key=eae3be059db26dc1f9cae1d1bee9d4cb',
+      },
+    ],
   },
 
   // Global CSS: https://go.nuxtjs.dev/config-css

+ 96 - 51
pages/_template/app/form/club-register.vue

@@ -8,54 +8,57 @@
       ></div>
     </div>
     <div class="page-content">
-      <template v-if="registerType.indexOf(step) > -1">
-        <!-- 进步条 -->
-        <SimpleStep :list="stepList" :active="step - 1" />
-
-        <div class="step-list py-4">
-          <keep-alive>
-            <!-- 账号注册表单 -->
-            <template
-              ><FormClubRegister
-                v-if="step === 1"
-                ref="userForm"
-                @step="onUserFormStep"
-            /></template>
-            <!-- 机构认证表单 -->
-            <template
-              ><FormClubInfo
-                v-if="step === 2"
-                ref="clubInfoForm"
-                @step="onClubInfoFormStep"
-            /></template>
-            <!-- 设备认证表单 -->
-            <template><FormClubDevice v-if="step === 3" /></template>
-          </keep-alive>
-        </div>
-      </template>
+      <!-- 进步条 -->
+      <SimpleStep :list="stepList" :active="step" />
+
+      <div class="step-list py-4">
+        <keep-alive>
+          <!-- 账号注册表单 -->
+          <template
+            ><FormClubRegister
+              v-if="step === 1"
+              ref="userForm"
+              @step="onUserFormStep"
+          /></template>
+          <!-- 机构认证表单 -->
+          <template
+            ><FormClubInfo
+              v-if="step === 2"
+              ref="clubInfoForm"
+              @step="onClubInfoFormStep"
+          /></template>
+          <!-- 设备认证表单 -->
+          <template
+            ><FormClubDevice
+              v-if="step === 3"
+              ref="clubDeviceForm"
+              @step="onclubDeviceFormStep"
+          /></template>
+        </keep-alive>
+      </div>
       <!-- 机构已认证 || 机构认证中 -->
-      <template v-else>
+      <!-- <template>
         <div class="message">
-          <div class="status-icon danger"></div>
+          <div class="status-icon success"></div>
           <div class="status">机构已认证</div>
           <div class="tip">提示:可点击认证记录看查看详情</div>
         </div>
-      </template>
+      </template> -->
 
       <!-- 操作 -->
       <div class="control flex flex-col items-center">
         <div
-          class="button prev flex justify-center items-center mb-2"
-          @click="onPrevStep"
-          v-if="step > 1"
+          class="button next flex justify-center items-center mb-2"
+          @click="onNextStep"
         >
-          上一步
+          {{ step === 3 ? '提交' : '下一步' }}
         </div>
         <div
-          class="button next flex justify-center items-center"
-          @click="onNextStep"
+          class="button prev flex justify-center items-center"
+          @click="onPrevStep"
+          v-if="step > 1"
         >
-          一步
+          一步
         </div>
         <div class="record mt-2" @click="toRecord">认证记录</div>
       </div>
@@ -89,7 +92,7 @@ export default {
   data() {
     return {
       registerType: [3],
-      step: 3,
+      step: 1,
       stepList: [
         {
           label: '账号注册',
@@ -107,6 +110,14 @@ export default {
           recordRoute: '/record/device',
         },
       ],
+      clubUserInfo: {},
+      authInfo: {},
+      productInfo: {},
+
+      authId: '',
+      clubUserId: '',
+
+      tipObj: {},
     }
   },
   created() {
@@ -117,10 +128,9 @@ export default {
       const validateAction = {
         1: this.$refs.userForm?.validate(),
         2: this.$refs.clubInfoForm?.validate(),
+        3: this.$refs.clubDeviceForm?.validate(),
       }
 
-      debugger
-
       if (this.registerType.indexOf(this.step) > -1) {
         try {
           await validateAction[this.step]
@@ -129,8 +139,10 @@ export default {
           return error
         }
       }
-
-      if (this.step < this.stepList.length) {
+      if (this.step === 3) {
+        this.onSubmit()
+      }
+      if (this.step < 3) {
         this.step++
       }
     },
@@ -140,35 +152,61 @@ export default {
       }
     },
 
+    async onSubmit() {
+      const params = {
+        registerType: this.registerType.join(','),
+        authUserId: this.authUserId,
+        authId: this.authId,
+        clubUserId: this.clubUserId,
+        clubUserInfo: this.clubUserInfo,
+        authInfo: this.authInfo,
+        productInfo: this.productInfo,
+      }
+
+      console.log(params)
+
+      try {
+        const res = await this.$http.api.clubUserRegisterAll(params)
+        console.log(res)
+      } catch (error) {
+        console.log(error)
+      }
+    },
+
     onUserFormStep(data) {
       console.log(data)
+      this.clubUserInfo = data
     },
 
     onClubInfoFormStep(data) {
       console.log(data)
+      this.authInfo = data
+    },
+
+    onclubDeviceFormStep(data) {
+      console.log(data)
+      this.productInfo = data
     },
 
     toRecord() {
       const step = this.stepList.find((item) => item.id === this.step)
       this.$router.push(`${this.routePrefix + step.recordRoute}`)
     },
+
     // 初始化表单
-    initForm() {
-      if (true) {
-        this.registerType.push(2)
-      }
+    async initForm() {
       // 判断用户是否登录
-      if (this.accessToken) {
-        // 判断用户是否注册了账号
-        this.checkouMobileBindClub()
-        // 判断用户是否注册了机构
+      if (!this.accessToken) {
+        this.registerType.push(1, 2)
       } else {
-        this.registerType.push(1)
+        this.step = 2
+        this.stepList = this.stepList.filter((item) => item.id !== 1)
+        await this.checkouClubInfo()
       }
     },
 
     // 判断用户手机号是否绑定机构
-    async checkouMobileBindClub() {
+    async checkouClubInfo() {
       try {
         const res = await this.$http.api.checkouMobileBindClub({
           authUserId: this.authUserId,
@@ -180,12 +218,19 @@ export default {
         if (!res.data.auth) {
           this.registerType.push(2)
         }
+        return Promise.resolve(res)
       } catch (error) {
         console.log(error)
+        return Promise.reject(error)
       }
     },
 
-    // 判断用户是否注册了机构
+    hasClubInfo() {
+      return false
+    },
+
+    // 机构注册提示
+    generateRegisterTip() {},
   },
 }
 </script>

+ 261 - 30
pages/_template/app/form/components/form-club-device.vue

@@ -1,49 +1,93 @@
 <template>
   <div class="club-device">
-    <template v-for="i in 2">
-      <div :key="i">
-        <el-form :model="formData" :rules="rules">
-          <el-form-item :label="`设备名称${i}:`">
+    <template v-for="formItem in formList">
+      <div :key="formItem.uid">
+        <el-form :model="formItem" :rules="rules" ref="form">
+          <el-form-item prop="productName" :label="`设备名称${formItem.uuid}:`">
             <el-select
-              v-model="formData.deviceName"
+              v-model="formItem.productName"
               filterable
               allow-create
               placeholder="请输入新设备名称或选择已有设备"
+              @change="onProductNameChange(formItem, $event)"
             >
-              <el-option label="中国" :value="1"> </el-option>
+              <el-option
+                v-for="item in deviceList"
+                :key="item.productTypeId"
+                :label="item.name"
+                :value="item.productTypeId"
+              />
             </el-select>
           </el-form-item>
-          <el-form-item prop="mobile" label="设备图片:">
+          <el-form-item prop="productImage" label="设备图片:">
             <br />
-            <el-input v-show="false"></el-input>
-            <SimpleUploadImage :limit="1" />
+            <el-input v-show="false" v-model="formItem.productImage"></el-input>
+            <SimpleUploadImage
+              :disabled="Boolean(formItem.productTypeId)"
+              :limit="1"
+              :image-list="formItem.productImageList"
+              :before-upload="beforeProductImageUpload"
+              @success="uploadProductImageSuccess(formItem, $event)"
+              @remove="handleProductImageRemove(formItem, $event)"
+            />
           </el-form-item>
-          <el-form-item label="所属品牌:">
-            <el-select v-model="formData.deviceName" placeholder="请选择品牌">
-              <el-option label="中国" :value="1"> </el-option>
+          <el-form-item label="所属品牌:" prop="brandId">
+            <el-select v-model="formItem.brandId" placeholder="请选择品牌">
+              <el-option
+                v-for="item in brandList"
+                :key="item.id"
+                :label="item.name"
+                :value="item.id"
+              />
             </el-select>
           </el-form-item>
-          <el-form-item prop="mobile" label="购买渠道:">
-            <el-input placeholder="请输入购买渠道"></el-input>
+          <el-form-item prop="purchaseWay" label="购买渠道:">
+            <el-input
+              placeholder="请输入购买渠道"
+              v-model="formItem.purchaseWay"
+            ></el-input>
           </el-form-item>
-          <el-form-item prop="mobile" label="发票:">
+          <el-form-item prop="invoiceImage" label="发票:">
             <br />
-            <el-input v-show="false"></el-input>
-            <SimpleUploadImage :limit="1" />
+            <el-input v-show="false" v-model="formItem.invoiceImage"></el-input>
+            <SimpleUploadImage
+              :limit="1"
+              :image-list="formItem.invoiceImageList"
+              :before-upload="beforeInvoiceImageUpload"
+              @success="uploadInvoiceImageSuccess(formItem, $event)"
+              @remove="handleInvoiceImageRemove(formItem, $event)"
+            />
           </el-form-item>
-          <el-form-item prop="mobile" label="设备SN码:">
-            <el-input placeholder="请输入设备SN码"></el-input>
+          <el-form-item prop="snCode" label="设备SN码:">
+            <el-input
+              placeholder="请输入设备SN码"
+              v-model="formItem.snCode"
+            ></el-input>
           </el-form-item>
-          <el-form-item prop="mobile" label="设备参数:">
+          <el-form-item prop="paramList" label="设备参数:">
             <br />
             <div class="device-param-list">
-              <span class="add-param">添加参数</span>
-              <template v-for="i in 3">
-                <div :key="i">
+              <span class="add-param" @click="insertParam(formItem)"
+                >添加参数</span
+              >
+              <template v-for="(param, index) in formItem.paramList">
+                <div :key="index">
                   <div class="param flex justify-between mb-4">
-                    <el-input placeholder="例如:品牌" class="mr-2"></el-input>
-                    <el-input placeholder="请输入参数信息"></el-input>
-                    <span class="remove el-icon-close"></span>
+                    <el-input
+                      style="width: 40%"
+                      placeholder="例如:品牌"
+                      class="mr-2"
+                      v-model="param.paramName"
+                    ></el-input>
+                    <el-input
+                      placeholder="请输入参数信息"
+                      v-model="param.paramContent"
+                    ></el-input>
+                    <span
+                      class="remove el-icon-close"
+                      @click="removeParam(formItem, index)"
+                      v-if="formItem.paramList.length > 4"
+                    ></span>
                   </div>
                 </div>
               </template>
@@ -54,7 +98,7 @@
       </div>
     </template>
 
-    <div class="add-device">
+    <div class="add-device" @click="insertOne">
       <div class="add-icon"></div>
       添加设备
     </div>
@@ -63,18 +107,205 @@
 
 <script>
 import SimpleUploadImage from '@/components/SimpleUploadImage'
+import { mapGetters } from 'vuex'
 export default {
   components: {
     SimpleUploadImage,
   },
   data() {
+    const productNameValidate = (rule, value, callback) => {
+      if (value.toString().length > 50) {
+        callback(new Error('设备名称长度需要在50个字符内'))
+      } else {
+        callback()
+      }
+    }
+
     return {
-      formData: {
-        deviceName: '',
+      uuid: 0, // 表单id
+      productImageList: [],
+      rules: {
+        productName: [
+          { required: true, message: '设备名称不能为空', trigger: ['change'] },
+          { validator: productNameValidate, trigger: ['change'] },
+        ],
+        productImage: [
+          { required: true, message: '设备图片不能为空', trigger: ['change'] },
+        ],
+        brandId: [
+          { required: true, message: '所属品牌不能为空', trigger: ['change'] },
+        ],
+        snCode: [
+          { required: true, message: '设备SN码不能为空', trigger: ['blur'] },
+        ],
+        paramList: [{ required: true, message: '参数不能为空' }],
       },
-      rules: {},
+      formList: [],
+      brandList: [],
+      deviceList: [],
     }
   },
+
+  computed: {
+    ...mapGetters(['authUserId']),
+  },
+
+  created() {
+    this.fetchBrandList()
+    this.fetchDeviceList()
+    this.initFormList()
+  },
+
+  methods: {
+    // 表单验证
+    validate() {
+      this.$emit('step', this.formatFormList())
+      return Promise.all(this.$refs.form.map((item) => item.validate()))
+    },
+
+    formatFormList() {
+      const list = []
+      this.formList.forEach((formItem) => {
+        const obj = {}
+        obj.productImage = formItem.productImage
+        obj.productName = formItem.productName
+        obj.snCode = formItem.snCode
+        obj.brandId = formItem.brandId
+        obj.productTypeId = formItem.productTypeId
+        obj.purchaseWay = formItem.purchaseWay
+        obj.invoiceImage = formItem.invoiceImage
+        obj.paramList = formItem.paramList
+        list.push(obj)
+      })
+      return list
+    },
+
+    generateFormData() {
+      return {
+        uuid: ++this.uuid,
+        authUserId: '',
+        authId: '', //	授权id
+        createBy: '', //	创建人id
+        // 	设备参数列表
+        paramList: this.initParams(),
+        productId: '', //	授权设备id
+        productImage: '', //	设备图片
+        productName: '', //	设备名称
+        snCode: '', //	设备SN码
+        brandId: '',
+        productTypeId: '',
+        purchaseWay: '', // 购买渠道
+        invoiceImage: '', // 发票
+        productImageList: [],
+        invoiceImageList: [],
+      }
+    },
+
+    generageProductParam() {
+      return {
+        paramContent: '',
+        paramName: '',
+      }
+    },
+
+    initParams() {
+      const list = []
+      for (let i = 0; i < 4; i++) {
+        list.push(this.generageProductParam())
+      }
+      return list
+    },
+
+    insertParam(formItem) {
+      formItem.paramList.push(this.generateFormData())
+    },
+
+    removeParam(formItem, index) {
+      formItem.paramList.splice(index, 1)
+    },
+
+    initFormList() {
+      this.formList.push(this.generateFormData())
+      console.log(this.formList)
+    },
+    insertOne() {
+      this.formList.push(this.generateFormData())
+    },
+    removeOne() {},
+    onProductNameChange(formItem, value) {
+      if (typeof value === 'number') {
+        formItem.productTypeId = value
+        const deviceInfo = this.deviceList.find(
+          (item) => item.productTypeId === value
+        )
+        formItem.productImage = deviceInfo.image
+        formItem.productImageList = [{ name: '', url: deviceInfo.image }]
+      } else {
+        formItem.productTypeId = ''
+        formItem.productImage = ''
+        formItem.productImageList = []
+      }
+    },
+
+    // 获取品牌列表
+    async fetchBrandList() {
+      try {
+        const res = await this.$http.api.fetchBrandList({
+          type: 3,
+          authUserId: this.authUserId,
+        })
+        this.brandList = res.data
+      } catch (error) {
+        console.log(error)
+      }
+    },
+
+    // 获取设备列表
+    async fetchDeviceList() {
+      try {
+        const res = await this.$http.api.fetchProductSelectList({
+          authUserId: this.authUserId,
+        })
+        this.deviceList = res.data
+      } catch (error) {
+        console.log(error)
+      }
+    },
+
+    // 产品图片上传
+    beforeProductImageUpload(file) {
+      const flag = file.size / 1024 / 1024 < 1
+      if (!flag) {
+        this.$message.error('上传产品图片大小不能超过 1MB!')
+      }
+      return flag
+    },
+    uploadProductImageSuccess(formItem, { response, file, fileList }) {
+      formItem.productImageList = fileList
+      formItem.productImage = response.data
+    },
+    handleProductImageRemove(formItem, { file, fileList }) {
+      formItem.productImageList = fileList
+      formItem.productImage = ''
+    },
+
+    // 发票上传
+    beforeInvoiceImageUpload(file) {
+      const flag = file.size / 1024 / 1024 < 1
+      if (!flag) {
+        this.$message.error('发票图片大小不能超过 1MB!')
+      }
+      return flag
+    },
+    uploadInvoiceImageSuccess(formItem, { response, file, fileList }) {
+      formItem.invoiceImageList = fileList
+      formItem.invoiceImage = response.data
+    },
+    handleInvoiceImageRemove(formItem, { file, fileList }) {
+      formItem.invoiceImageList = fileList
+      formItem.invoiceImage = ''
+    },
+  },
 }
 </script>
 

+ 211 - 2
pages/_template/app/form/components/form-club-info.vue

@@ -31,6 +31,7 @@
             placeholder="请选择"
             v-model="formData.cityId"
             @change="onCityChange"
+            class="mx-2"
           >
             <template v-for="item in cityList">
               <el-option :label="item.name" :value="item.id" :key="item.id">
@@ -65,7 +66,7 @@
               >(提示:打开地图,将定位图标移到具体位置)</span
             >
           </div>
-          <div class="postion-btn">定位</div>
+          <div class="postion-btn" @click="initMap">定位</div>
         </div>
       </el-form-item>
       <el-form-item prop="logoImage" label="logo:">
@@ -140,6 +141,32 @@
         />
       </el-form-item>
     </el-form>
+
+    <div class="position-select" v-if="mapVisiable">
+      <div class="position-select-container">
+        <SimpleAMap
+          @position="onPosition"
+          ref="aMap"
+          :lnglat="lnglat"
+          :address="fullAddress"
+        />
+        <div class="position-select-footer">
+          <div class="lnglat">当前经纬度:{{ lgnlatText }}</div>
+          <div
+            class="position-cancel postion-control"
+            @click="mapVisiable = false"
+          >
+            取消
+          </div>
+          <div
+            class="position-confirm postion-control"
+            @click="mapVisiable = false"
+          >
+            确定
+          </div>
+        </div>
+      </div>
+    </div>
   </div>
 </template>
 
@@ -193,6 +220,8 @@ export default {
     }
 
     return {
+      lnglat: null,
+      mapVisiable: false,
       clubTypeList: [
         { value: 1, name: '医美' },
         { value: 2, name: '生美' },
@@ -281,6 +310,9 @@ export default {
     }
   },
   computed: {
+    lgnlatText() {
+      return this.lnglat ? this.lnglat.join(',') : ''
+    },
     cityList() {
       const province = this.provinceList.find(
         (item) => item.id === this.formData.provinceId
@@ -299,11 +331,45 @@ export default {
       }
       return []
     },
+
+    fullAddress() {
+      let str = ''
+      this.provinceList.forEach((pro) => {
+        if (pro.id === this.formData.provinceId) {
+          str += pro.name
+          pro.children.forEach((city) => {
+            if (city.id === this.formData.cityId) {
+              str += city.name
+              city.children.forEach((town) => {
+                if (town.id === this.formData.townId) {
+                  str += town.name
+                }
+              })
+            }
+          })
+        }
+      })
+      return (str += this.formData.fullAddress)
+    },
   },
   created() {
     this.fetchAllCityList()
   },
   methods: {
+    // 地图定位
+    initMap() {
+      this.mapVisiable = true
+      this.$nextTick(() => {
+        this.$refs.aMap.init()
+      })
+    },
+
+    onPosition(lnglat) {
+      console.log(lnglat)
+      this.lnglat = [lnglat.lng, lnglat.lat]
+      this.formData.point = this.lnglat.join(',')
+    },
+
     async fetchAllCityList() {
       try {
         const res = await this.$http.api.fetchAllCityList()
@@ -314,9 +380,30 @@ export default {
       }
     },
 
+    genetageFormData() {
+      return {
+        authParty: this.formData.name,
+        provinceId: this.formData.provinceId,
+        cityId: this.formData.cityId,
+        townId: this.formData.townId,
+        address: this.formData.fullAddress,
+        mobile: this.formData.mobile,
+        logo: this.formData.logoImage,
+        lngAndLat: this.lgnlatText,
+        remarks: this.formData.remarks,
+        empNum: this.formData.empNum,
+        firstClubType: this.formData.firstClubType,
+        secondClubType: this.formData.secondClubType,
+        medicalLicenseImage: this.formData.medicalLicenseImage,
+        bannerList: this.bannerList.map((item) =>
+          item.response ? item.response.data : item.url
+        ),
+      }
+    },
+
     // 表单验证
     validate() {
-      this.$emit('step', this.formData)
+      this.$emit('step', this.genetageFormData())
       return this.$refs.form.validate()
     },
 
@@ -404,6 +491,67 @@ export default {
 <style lang="scss" scoped>
 // pc端
 @media screen and (min-width: 768px) {
+  .position-select {
+    width: 100vw;
+    height: 100vh;
+    background: rgba(0, 0, 0, 0.39);
+    position: fixed;
+    top: 0;
+    left: 0;
+    z-index: 999;
+
+    display: flex;
+    justify-content: center;
+    align-items: center;
+
+    .position-select-container {
+      background: #fff;
+      width: 60%;
+      box-sizing: border-box;
+      padding: 24px;
+
+      .position-select-footer {
+        position: relative;
+        display: flex;
+        justify-content: flex-end;
+        align-items: center;
+        padding-top: 24px;
+
+        .lnglat {
+          position: absolute;
+          font-size: 14px;
+          color: #666;
+
+          left: 0;
+          top: 50%;
+          transform: translateY(-50%);
+        }
+      }
+
+      .postion-control {
+        width: 120px;
+        height: 40px;
+        font-size: 14px;
+        border-radius: 4px;
+        display: flex;
+        justify-content: center;
+        align-items: center;
+        cursor: pointer;
+        margin-left: 16px;
+
+        &.position-confirm {
+          background: #f56c6c;
+          color: #fff;
+        }
+
+        &.position-cancel {
+          background: #b1b1b1;
+          color: #fff;
+        }
+      }
+    }
+  }
+
   .normal-row {
     position: relative;
     .label {
@@ -457,6 +605,67 @@ export default {
     }
   }
 
+  .position-select {
+    width: 100vw;
+    height: 100vh;
+    background: rgba(0, 0, 0, 0.39);
+    position: fixed;
+    top: 0;
+    left: 0;
+    z-index: 999;
+
+    display: flex;
+    justify-content: center;
+    align-items: center;
+
+    .position-select-container {
+      background: #fff;
+      width: 80%;
+      box-sizing: border-box;
+      padding: 3.2vw;
+
+      .position-select-footer {
+        padding-top: 10vw;
+        position: relative;
+        display: flex;
+        justify-content: flex-end;
+        align-items: center;
+
+        .lnglat {
+          position: absolute;
+          font-size: 3.2vw;
+          color: #666;
+
+          left: 0;
+          top: 5vw;
+          transform: translateY(-50%);
+        }
+      }
+
+      .postion-control {
+        width: 16vw;
+        height: 7vw;
+        font-size: 3.4vw;
+        border-radius: 0.4vw;
+        display: flex;
+        justify-content: center;
+        align-items: center;
+        cursor: pointer;
+        margin-left: 3.6vw;
+
+        &.position-confirm {
+          background: #f56c6c;
+          color: #fff;
+        }
+
+        &.position-cancel {
+          background: #b1b1b1;
+          color: #fff;
+        }
+      }
+    }
+  }
+
   .normal-row {
     position: relative;
     .label {

+ 49 - 2
pages/_template/app/form/components/form-club-register.vue

@@ -18,7 +18,7 @@
             maxlength="6"
             show-word-limit
           ></el-input>
-          <div class="send ml-8">获取验证码</div>
+          <div class="send ml-8" @click="onSend">{{ sendCodeBtnText }}</div>
         </div>
       </el-form-item>
       <el-form-item prop="password">
@@ -62,6 +62,7 @@ export default {
         password: '',
         confirmPwd: '',
       },
+      sendStatus: 0,
       active: false,
       rules: {
         mobile: [
@@ -82,8 +83,46 @@ export default {
   },
   computed: {
     ...mapGetters(['authUserId', 'routePrefix', 'accessToken']),
+    sendCodeBtnText() {
+      return this.sendStatus === 0
+        ? '获取验证码'
+        : `再次发送${this.sendStatus}s`
+    },
   },
   methods: {
+    async onSend() {
+      if (this.sendStatus > 0) return
+      // 验证手机号是否合法
+      if (!isMobile(this.formData.mobile)) {
+        this.$toast('请输入正确的手机号')
+        return
+      }
+      try {
+        // 发送验证码
+        await this.$http.api.sendVerifyCode({
+          mobile: this.formData.mobile,
+          authUserId: this.authUserId,
+          type: 1,
+        })
+        this.$toast('验证码已发送')
+        // 开启倒计时
+        this.countdown()
+      } catch (error) {
+        console.log(error)
+      }
+    },
+
+    countdown() {
+      this.sendStatus = 30
+      this.timer = setInterval(() => {
+        if (this.sendStatus === 0) {
+          clearInterval(this.timer)
+          return
+        }
+        this.sendStatus--
+      }, 1000)
+    },
+
     onConfirm() {
       this.$router.push(`${this.routePrefix}`)
     },
@@ -110,9 +149,17 @@ export default {
       }
     },
 
+    genetageFormData() {
+      return {
+        mobile: this.formData.mobile,
+        verifyCode: this.formData.verifyCode,
+        password: this.formData.password,
+      }
+    },
+
     // 表单验证
     validate() {
-      this.$emit('step', this.formData)
+      this.$emit('step', this.genetageFormData())
       return this.$refs.form.validate()
     },
   },

BIN
static/location.png


+ 5 - 0
static/map.config.js

@@ -0,0 +1,5 @@
+window._AMapSecurityConfig = {
+  // serviceHost: '您的代理服务器域名或地址/_AMapService',
+  securityJsCode: 'ff3114e0be935539f0ca5c9a80406752',
+  // 例如 :serviceHost:'http://1.1.1.1:80/_AMapService',
+}