瀏覽代碼

v1.7.2版本优化,bug修改

yuwenjun1997 2 年之前
父節點
當前提交
0cc2009329
共有 46 個文件被更改,包括 1791 次插入2698 次删除
  1. 4 4
      .env.development
  2. 99 0
      mixins/clubDetail.js
  3. 125 0
      mixins/clubList.js
  4. 56 0
      mixins/clubStarList.js
  5. 76 0
      mixins/deviceDetail.js
  6. 117 0
      mixins/deviceList.js
  7. 35 0
      mixins/feedback.js
  8. 31 0
      mixins/operatDoctorDetail.js
  9. 61 0
      mixins/operatDoctorList.js
  10. 6 90
      pages/_template/app/approve/club/detail.vue
  11. 21 138
      pages/_template/app/approve/club/index.vue
  12. 3 70
      pages/_template/app/approve/club/star-list.vue
  13. 712 0
      pages/_template/app/approve/device/detail.vue
  14. 8 118
      pages/_template/app/approve/device/index.vue
  15. 0 289
      pages/_template/app/approve/device/index1.vue
  16. 0 351
      pages/_template/app/approve/device/list.vue
  17. 3 36
      pages/_template/app/approve/personnel/operate/detail.vue
  18. 7 69
      pages/_template/app/approve/personnel/operate/index.vue
  19. 3 35
      pages/_template/app/feedback/index.vue
  20. 2 2
      pages/_template/app/form/club-bind.vue
  21. 1 1
      pages/_template/app/form/components/form-club-device.vue
  22. 307 9
      pages/_template/app/index.vue
  23. 2 93
      pages/_template/ross/approve/club/detail.vue
  24. 2 63
      pages/_template/ross/approve/club/star-list.vue
  25. 37 94
      pages/_template/ross/approve/device/detail.vue
  26. 9 119
      pages/_template/ross/approve/device/index.vue
  27. 0 273
      pages/_template/ross/approve/device/index1.vue
  28. 0 327
      pages/_template/ross/approve/device/list.vue
  29. 0 227
      pages/_template/ross/approve/device/search.vue
  30. 4 37
      pages/_template/ross/approve/personnel/operate/detail.vue
  31. 14 70
      pages/_template/ross/approve/personnel/operate/index.vue
  32. 1 1
      pages/_template/ross/database/article.vue
  33. 1 1
      pages/_template/ross/database/file.vue
  34. 1 1
      pages/_template/ross/database/image.vue
  35. 1 1
      pages/_template/ross/database/package.vue
  36. 1 1
      pages/_template/ross/database/video.vue
  37. 3 35
      pages/_template/ross/feedback/index.vue
  38. 1 1
      pages/_template/ross/form/club-register.vue
  39. 3 2
      pages/_template/ross/form/components/form-club-device.vue
  40. 14 135
      pages/_template/ross/index.vue
  41. 1 1
      pages/_template/ross/record/club/detail.vue
  42. 1 1
      pages/_template/ross/record/club/edit.vue
  43. 1 1
      pages/_template/ross/record/device/detail.vue
  44. 1 1
      pages/_template/ross/record/device/edit.vue
  45. 1 1
      pages/_template/ross/record/device/index.vue
  46. 15 0
      utils/index.js

+ 4 - 4
.env.development

@@ -2,12 +2,12 @@
 EVN = 'development'
 
 # 网站地址
-# LOCALHOSE = 'https://zp-b.caimei365.com'
-LOCALHOSE = 'http://192.168.2.92:8888'
+LOCALHOSE = 'https://zp-b.caimei365.com'
+# LOCALHOSE = 'http://192.168.2.92:8888'
 
 # 接口api地址
-# BASE_URL = 'https://zplma-b.caimei365.com'
-BASE_URL = 'http://192.168.2.68:8012'
+BASE_URL = 'https://zplma-b.caimei365.com'
+# BASE_URL = 'http://192.168.2.68:8012'
 
 # 静态资源文件地址
 STATIC_URL = 'https://static.caimei365.com/www/authentic'

+ 99 - 0
mixins/clubDetail.js

@@ -0,0 +1,99 @@
+import { drawLogo } from '@/utils'
+import { mapNavigate } from '@/utils/map-utils'
+import { callMobile } from '@/utils'
+import { mapGetters } from 'vuex'
+
+export default {
+  filters: {
+    formatEmpty(val) {
+      return val || '暂无'
+    },
+    formatSnCode(code) {
+      if (!code) return ''
+      return code.replace(/^(\w{2})\w+(\w{4})$/, '$1******$2')
+    },
+  },
+  data() {
+    return {
+      authId: '',
+      clubInfo: {},
+    }
+  },
+  computed: {
+    ...mapGetters(['routePrefix']),
+    address() {
+      let resultAddress = this.clubInfo.area
+        ? this.clubInfo.area + this.clubInfo.address
+        : this.clubInfo.address
+      return resultAddress || '暂无'
+    },
+    isEmpty() {
+      return this.clubInfo.productList
+        ? this.clubInfo.productList.length === 0
+        : true
+    },
+  },
+  mounted() {
+    this.initData()
+  },
+  beforeDestroy() {
+    this.$removeStorage(this.routePrefix, 'clubInfo')
+  },
+  methods: {
+    callMobile,
+
+    // 设备详情
+    toDetail(item) {
+      const url = `${this.routePrefix}/approve/device/detail?id=${item.productId}`
+      this.$router.push(url)
+    },
+
+    // 初始化
+    initData() {
+      this.authId = parseInt(this.$route.query.id)
+      const clubInfo = this.$getStorage(this.routePrefix, 'clubInfo')
+      if (clubInfo) {
+        this.clubInfo = clubInfo
+      }
+      this.fetchDetail()
+    },
+
+    // 获取机构详细信息
+    async fetchDetail() {
+      try {
+        const res = await this.$http.api.getAuthClubDetail({
+          authId: this.authId,
+        })
+        this.clubInfo = { ...this.clubInfo, ...res.data } // 合并
+      } catch (error) {
+        console.log(error)
+      }
+      // 默认轮播图
+      if (this.clubInfo.bannerList.length <= 0) {
+        this.clubInfo.bannerList.push('/placeholder.png')
+      }
+      // 默认logo
+      if (!this.clubInfo.logo) {
+        this.clubInfo.logo = drawLogo(this.clubInfo.authParty)
+      }
+    },
+
+    // 地图导航
+    onMapNav() {
+      this.$refs.mapNav.open()
+    },
+
+    // 导航
+    navigation(type) {
+      const point = this.clubInfo.lngAndLat.split(',')
+      let lng = point[0]
+      let lat = point[1]
+
+      mapNavigate(
+        { lat, lng, title: this.clubInfo.authParty, address: this.address },
+        type
+      )
+      this.$refs.mapNav.close()
+    },
+  },
+}

+ 125 - 0
mixins/clubList.js

@@ -0,0 +1,125 @@
+import { mapGetters } from 'vuex'
+import { geolocation } from '@/utils/map-utils'
+import { drawLogo, debounce } from '@/utils'
+
+export default {
+  data() {
+    return {
+      isRequest: false, // 是否发起请求
+      finished: true, // 列表加载是否完毕(加载完了所有数据)
+      loadingMore: false, // 是否正在加载
+      listQuery: {
+        authUserId: '',
+        lngAndLat: '',
+        authParty: '',
+        provinceId: '',
+        cityId: '',
+        townId: '',
+        pageNum: 1,
+        pageSize: 12,
+      },
+      list: [], // 机构列表
+      starList: [], // 明星机构列表
+      total: 0, // 机构数量
+    }
+  },
+  computed: {
+    ...mapGetters(['authUserId', 'routePrefix']),
+  },
+  created() {
+    const cacheData = this.$getStorage(this.routePrefix, 'club_list_data')
+    if (cacheData) {
+      this.initFromCache(cacheData)
+      this.$removeStorage(this.routePrefix, 'club_list_data')
+    } else {
+      this.init()
+    }
+    this.fetchStarClubList()
+  },
+  beforeDestroy() {
+    this.$toast.clear()
+  },
+  methods: {
+    // 绘制logo的方法
+    drawLogo,
+
+    // 页面初始化
+    async init() {
+      this.listQuery.authUserId = this.authUserId
+      this.$toast.loading({
+        message: '正在获取您附近的机构...',
+        duration: 0,
+      })
+      try {
+        const resLocation = await geolocation()
+        this.listQuery.lngAndLat = `${resLocation.position.lng},${resLocation.position.lat}`
+      } catch (error) {
+        this.$toast('获取定位信息失败')
+      } finally {
+        this.filterClubList()
+      }
+    },
+
+    // 搜索机构列表
+    filterClubList() {
+      this.listQuery.pageNum = 1
+      this.list = []
+      this.loadingMore = true
+      this.isRequest = true
+      this.fetchClubList()
+    },
+
+    // 获取机构列表
+    fetchClubList: debounce(async function () {
+      try {
+        const res = await this.$http.api.getAuthClubList(this.listQuery)
+        this.list = [...this.list, ...res.data.list]
+        this.finished = !res.data.hasNextPage
+        this.listQuery.pageNum++
+        this.total = res.data.total
+      } catch (error) {
+        console.log('机构获取失败')
+      } finally {
+        this.$toast.clear()
+        this.loadingMore = false
+        this.isRequest = false
+      }
+    }, 500),
+
+    // 获取明星机构列表
+    async fetchStarClubList() {
+      try {
+        const res = await this.$http.api.getAuthClubStarList({
+          authUserId: this.authUserId,
+        })
+        this.starList = res.data.slice(0, 2)
+      } catch (error) {
+        console.log(error)
+      }
+    },
+
+    // 查看机构详情
+    toDetail(item) {
+      // 缓存数据
+      this.$setStorage(this.routePrefix, 'club_list_data', this.$data, {
+        expiredTime: 5 * 60 * 1000,
+      })
+      this.$setStorage(this.routePrefix, 'clubInfo', item)
+      const url = `${this.routePrefix}/approve/club/detail?id=${item.authId}`
+      this.$router.push(url)
+    },
+
+    // 格式化地址
+    formatAddress(a1, a2) {
+      let resutl = ''
+      if (a1) resutl += a1
+      if (a2) resutl += a2
+      return resutl || '暂无'
+    },
+
+    // 格式化距离
+    formatDistance(value) {
+      return value > 1 ? value + 'km' : value * 1000 + 'm'
+    },
+  },
+}

+ 56 - 0
mixins/clubStarList.js

@@ -0,0 +1,56 @@
+import { mapGetters } from 'vuex'
+import { drawLogo } from '@/utils'
+
+export default {
+  data() {
+    return {
+      isRequest: false,
+      list: [],
+    }
+  },
+  computed: {
+    ...mapGetters(['authUserId', 'routePrefix']),
+    total() {
+      return this.list.length
+    },
+  },
+  mounted() {
+    this.fetchList()
+  },
+  methods: {
+    // 绘制logo的方法
+    drawLogo,
+
+    // 查看详情
+    toDetail(item) {
+      const url = `${this.routePrefix}/approve/club/detail?id=${item.authId}`
+      this.$router.push(url)
+    },
+
+    async fetchList() {
+      // 自定义加载图标
+      this.$toast.loading({
+        message: '正在获取明星机构...',
+        duration: 0,
+      })
+      try {
+        const res = await this.$http.api.getAuthClubStarList({
+          authUserId: this.authUserId,
+        })
+        this.list = res.data
+      } catch (error) {
+        console.log(error)
+      } finally {
+        this.$toast.clear()
+      }
+    },
+
+    // 格式化地址
+    formatAddress(a1, a2) {
+      let resutl = ''
+      if (a1) resutl += a1
+      if (a2) resutl += a2
+      return resutl || '暂无'
+    },
+  },
+}

+ 76 - 0
mixins/deviceDetail.js

@@ -0,0 +1,76 @@
+import { mapGetters } from 'vuex'
+import { chunk } from 'lodash'
+
+export default {
+  filters: {
+    snCodeRender(code) {
+      return code.replace(/^(\w{2})\w+(\w{4})$/, '$1******$2')
+    },
+  },
+  data() {
+    return {
+      showAuthCard: false,
+      bannerList: [],
+      productInfo: null,
+    }
+  },
+  watch: {
+    isPc() {
+      this.$nextTick(() => {
+        window.location.reload()
+      })
+    },
+  },
+  computed: {
+    ...mapGetters(['isPc']),
+    // 参数列表
+    paramListRender() {
+      if (!this.productInfo) return []
+      return this.isPc
+        ? chunk(this.productInfo.paramList, 2)
+        : chunk(this.productInfo.paramList, 1)
+    },
+    // 机构logo
+    brandLogoImage() {
+      if (!this.productInfo) return ''
+      return this.productInfo.brandLogo
+    },
+    // 授权牌
+    authCardImage() {
+      if (!this.productInfo) return ''
+      return this.productInfo.appletsCertificateImage
+    },
+    // 机构logo列表
+    clubLogo() {
+      if (!this.productInfo) return []
+      return this.productInfo.clubList.map((item) => item.logo)
+    },
+  },
+  created() {
+    this.fetchProductDetails()
+  },
+  methods: {
+    // 显示授权牌
+    onShowAuthCard() {
+      this.showAuthCard = true
+    },
+
+    // 隐藏授权牌
+    onHideAuthCard() {
+      this.showAuthCard = false
+    },
+
+    // 获取设备详情
+    async fetchProductDetails() {
+      const productId = this.$route.query.id
+      try {
+        const { data } = await this.$http.api.fetchProductDetails({
+          productId: productId,
+        })
+        this.productInfo = data
+      } catch (error) {
+        console.log(error)
+      }
+    },
+  },
+}

+ 117 - 0
mixins/deviceList.js

@@ -0,0 +1,117 @@
+import { debounce } from '@/utils'
+import { mapGetters } from 'vuex'
+export default {
+  filters: {
+    formatSnCode(code) {
+      if (!code) return ''
+      return code.replace(/^(\w{2})\w+(\w{4})$/, '$1******$2')
+    },
+  },
+  data() {
+    return {
+      loadingMore: false,
+      finished: true,
+      isRequest: false,
+      searchQuery: {
+        type: 2,
+        keyword: '',
+      },
+      listQuery: {
+        listType: 1,
+        productTypeId: '',
+        snCode: '',
+        authParty: '',
+        pageNum: 1,
+        pageSize: 10,
+      },
+      list: [],
+      total: 0,
+      productSelectList: [],
+      searchFlag: false,
+    }
+  },
+  computed: {
+    ...mapGetters(['routePrefix', 'supplierInfo', 'authUserId']),
+  },
+  mounted() {
+    this.fetchProductSelectList()
+  },
+  methods: {
+    // 获取设备列表
+    fetchList: debounce(async function () {
+      try {
+        this.isRequest = true
+        this.loadingMore = true
+        const res = await this.$http.api.getAuthProductList(this.listQuery)
+        this.list = [...this.list, ...res.data.list]
+        this.finished = !res.data.hasNextPage
+        this.total = res.data.total
+        this.loadingMore = false
+        this.listQuery.pageNum += 1
+        this.searchFlag = true
+      } catch (error) {
+        console.log(error)
+      } finally {
+        this.isRequest = false
+        this.searchFlag = true
+      }
+    }, 400),
+
+    // 搜索
+    onSearch() {
+      const { productTypeId } = this.listQuery
+      const { type, keyword } = this.searchQuery
+      if (!productTypeId) {
+        return this.$toast('请选择设备分类')
+      }
+      if (!type) {
+        return this.$toast('请选择搜索条件')
+      }
+      if (!keyword) {
+        return this.$toast('搜索内容不能为空')
+      }
+
+      if (type === 1) {
+        this.listQuery.snCode = keyword
+        this.listQuery.authParty = ''
+      } else {
+        this.listQuery.snCode = ''
+        this.listQuery.authParty = keyword
+      }
+
+      this.list = []
+      this.listQuery.pageNum = 1
+      this.fetchList()
+    },
+
+    // 获取设备种类
+    async fetchProductSelectList() {
+      try {
+        const res = await this.$http.api.fetchProductSelectList({
+          authUserId: this.authUserId,
+        })
+        this.productSelectList = res.data
+      } catch (error) {
+        console.log(error)
+      }
+    },
+
+    // 设备详情
+    toDetail(item) {
+      const url = `${this.routePrefix}/approve/device/detail?id=${item.productId}`
+      this.$router.push(url)
+    },
+
+    // 机构详情
+    toClubDetail(item) {
+      const url = `${this.routePrefix}/approve/club/detail?id=${item.authId}`
+      this.$router.push(url)
+    },
+
+    // 加载更多
+    onLoadMore() {
+      if (!this.searchFlag) return
+      this.fetchList()
+    },
+  },
+}

+ 35 - 0
mixins/feedback.js

@@ -0,0 +1,35 @@
+import { mapGetters } from 'vuex'
+export default {
+  data() {
+    return {
+      content: '',
+      showModal: false,
+    }
+  },
+  computed: {
+    ...mapGetters(['supplierInfo', 'userInfo', 'routePrefix']),
+    isEmpty() {
+      return this.content.length === 0
+    },
+  },
+  methods: {
+    async onSubmit() {
+      const { clubUserId } = this.userInfo
+      if (this.isEmpty) {
+        this.$toast('留言不能为空')
+        return
+      }
+      try {
+        await this.$http.api.feedback({ clubUserId, content: this.content })
+        this.showModal = true
+        this.content = ''
+      } catch (error) {
+        console.log(error)
+      }
+    },
+    onConfirm() {
+      this.showModal = false
+      this.$router.push(this.routePrefix)
+    },
+  },
+}

+ 31 - 0
mixins/operatDoctorDetail.js

@@ -0,0 +1,31 @@
+export default {
+  layout: 'app',
+  data() {
+    return {
+      doctorId: '',
+      doctorInfo: {
+        tagList: [],
+        paramList: [],
+      },
+    }
+  },
+  mounted() {
+    this.fetchDetail()
+  },
+  methods: {
+    async fetchDetail() {
+      try {
+        this.doctorId = this.$route.query.id
+        const res = await this.$http.api.fetchDoctorDetail({
+          doctorId: this.doctorId,
+        })
+        this.doctorInfo = { ...this.clubInfo, ...res.data }
+      } catch (error) {
+        console.log(error)
+      }
+      if (this.doctorInfo.bannerList.length <= 0) {
+        this.doctorInfo.bannerList.push('/placeholder.png')
+      }
+    },
+  },
+}

+ 61 - 0
mixins/operatDoctorList.js

@@ -0,0 +1,61 @@
+import { mapGetters } from 'vuex'
+import { debounce } from '@/utils'
+
+export default {
+  data() {
+    return {
+      loadingMore: false,
+      finished: true,
+      isRequest: true,
+      listQuery: {
+        authUserId: '',
+        doctorType: 1,
+        doctorName: '',
+        pageNum: 1,
+        pageSize: 12,
+      },
+      list: [],
+      total: 0,
+    }
+  },
+  computed: {
+    ...mapGetters(['supplierInfo', 'authUserId', 'routePrefix']),
+  },
+  mounted() {
+    this.fetchList()
+  },
+  methods: {
+    fetchList: debounce(async function () {
+      try {
+        this.loadingMore = true
+        this.listQuery.authUserId = this.authUserId
+        const res = await this.$http.api.fetchDoctorList(this.listQuery)
+        this.list = [...this.list, ...res.data.list]
+        this.finished = !res.data.hasNextPage
+        this.listQuery.pageNum += 1
+        this.total = res.data.total
+      } catch (error) {
+        console.log(error)
+      } finally {
+        this.isRequest = false
+        this.loadingMore = false
+      }
+    }, 400),
+
+    // 搜索
+    onSearch() {
+      this.list = []
+      this.listQuery.pageNum = 1
+      this.fetchList()
+    },
+    // 医师详情
+    toDetail(item) {
+      const url = `${this.routePrefix}/approve/personnel/operate/detail?id=${item.doctorId}`
+      this.$router.push(url)
+    },
+    // 加载更多
+    onLoadMore() {
+      this.fetchList()
+    },
+  },
+}

+ 6 - 90
pages/_template/app/approve/club/detail.vue

@@ -20,7 +20,9 @@
         <div class="section flex justify-between items-center">
           <div class="info">
             <div class="name" v-text="clubInfo.authParty"></div>
-            <div class="mobile"  @click="callMobile(clubInfo.mobile)">{{ clubInfo.mobile | formatEmpty }}</div>
+            <div class="mobile" @click="callMobile(clubInfo.mobile)">
+              {{ clubInfo.mobile | formatEmpty }}
+            </div>
             <div class="address" v-text="address"></div>
           </div>
           <div class="logo"><img :src="clubInfo.logo" /></div>
@@ -63,97 +65,10 @@
 </template>
 
 <script>
-import { drawLogo } from '@/utils'
-import { mapNavigate } from '@/utils/map-utils'
-import { mapGetters } from 'vuex'
-import { callMobile } from '@/utils'
+import clubDetailMixin from '@/mixins/clubDetail'
 export default {
   layout: 'app',
-  filters: {
-    formatEmpty(val) {
-      return val || '暂无'
-    },
-    formatSnCode(code) {
-      if (!code) return ''
-      return code.replace(/^(\w{2})\w+(\w{4})$/, '$1******$2')
-    },
-  },
-  data() {
-    return {
-      authId: '',
-      clubInfo: {},
-    }
-  },
-  computed: {
-    ...mapGetters(['routePrefix']),
-    address() {
-      let resultAddress = this.clubInfo.area
-        ? this.clubInfo.area + this.clubInfo.address
-        : this.clubInfo.address
-      return resultAddress || '暂无'
-    },
-    isEmpty() {
-      return this.clubInfo.productList
-        ? this.clubInfo.productList.length === 0
-        : true
-    },
-  },
-  mounted() {
-    this.initData()
-  },
-  beforeDestroy() {
-    this.$removeStorage(this.routePrefix, 'clubInfo')
-  },
-  methods: {
-    callMobile,
-    // 设备详情
-    toDetail(item) {
-      window.location.href = `${process.env.CIMEI_LOCAL}/product/auth/product-${item.productId}.html`
-    },
-    // 初始化
-    initData() {
-      this.authId = parseInt(this.$route.query.id)
-      const clubInfo = this.$getStorage(this.routePrefix, 'clubInfo')
-      if (clubInfo) {
-        this.clubInfo = clubInfo
-      }
-      this.fetchDetail()
-    },
-    // 获取机构详细信息
-    async fetchDetail() {
-      try {
-        const res = await this.$http.api.getAuthClubDetail({
-          authId: this.authId,
-        })
-        this.clubInfo = { ...this.clubInfo, ...res.data } // 合并
-      } catch (error) {
-        console.log(error)
-      }
-      // 默认轮播图
-      if (this.clubInfo.bannerList.length <= 0) {
-        this.clubInfo.bannerList.push('/placeholder.png')
-      }
-      // 默认logo
-      if (!this.clubInfo.logo) {
-        this.clubInfo.logo = drawLogo(this.clubInfo.authParty)
-      }
-    },
-    // 地图导航
-    onMapNav() {
-      this.$refs.mapNav.open()
-    },
-    // 导航
-    navigation(type) {
-      const point = this.clubInfo.lngAndLat.split(',')
-      const lng = point[0]
-      const lat = point[1]
-      mapNavigate(
-        { lat, lng, title: this.clubInfo.authParty, address: this.address },
-        type
-      )
-      this.$refs.mapNav.close()
-    },
-  },
+  mixins: [clubDetailMixin],
 }
 </script>
 
@@ -367,6 +282,7 @@ export default {
           border-radius: 4px;
           font-size: 14px;
           color: #ffffff;
+          cursor: pointer;
           @include themify($themes) {
             background: themed('color');
           }

+ 21 - 138
pages/_template/app/approve/club/index.vue

@@ -1,11 +1,11 @@
 <template>
   <div class="page">
     <van-list
-      v-model="isLoadingMore"
+      v-model="loadingMore"
       :finished="finished"
       :immediate-check="false"
       :finished-text="total ? '没有更多了' : ''"
-      @load="onLoadMore"
+      @load="fetchClubList"
     >
       <div class="page-top flex flex-col justify-center items-center">
         <!-- <img class="logo" :src="supplierInfo.logo" /> -->
@@ -18,8 +18,8 @@
         <!-- 搜索区域 -->
         <div class="search">
           <simple-search
-            v-model="listQuery.clubName"
-            @search="onSearch"
+            v-model="listQuery.authParty"
+            @search="filterClubList"
             placeholder="搜索机构"
           />
         </div>
@@ -50,10 +50,10 @@
               >
                 <img
                   class="cover"
-                  :src="item.logo || drawLogo(item.clubName)"
+                  :src="item.logo || drawLogo(item.authParty)"
                 />
                 <div class="info">
-                  <div class="name" v-text="item.clubName"></div>
+                  <div class="name" v-text="item.authParty"></div>
                   <div class="mobile">{{ item.mobile || '暂无' }}</div>
                   <div class="address">
                     {{ formatAddress(item.area, item.address) }}
@@ -81,16 +81,16 @@
               :key="item.authId"
               @click="toDetail(item)"
             >
-              <img class="cover" :src="item.logo || drawLogo(item.clubName)" />
+              <img class="cover" :src="item.logo || drawLogo(item.authParty)" />
               <div class="info">
-                <div class="name" v-text="item.clubName"></div>
+                <div class="name" v-text="item.authParty"></div>
                 <div class="mobile">{{ item.mobile || '暂无' }}</div>
                 <div class="address">
                   {{ formatAddress(item.area, item.address) }}
                 </div>
                 <div
                   class="distance"
-                  v-text="generateDistance(item.distance)"
+                  v-text="formatDistance(item.distance)"
                   v-if="item.distance && item.distance !== 99999"
                 ></div>
               </div>
@@ -111,34 +111,19 @@
 
 <script>
 import { mapGetters } from 'vuex'
-import { geolocation } from '@/utils/map-utils'
-import { drawLogo, debounce } from '@/utils'
+import clubListMixin from '@/mixins/clubList'
+import { objectCover } from '@/utils'
 export default {
   layout: 'app',
+  mixins: [clubListMixin],
   data() {
     return {
-      isLoadingMore: true,
-      finished: false,
-      isRequest: true,
-      list: [],
-      listQuery: {
-        authUserId: '',
-        lngAndLat: '',
-        clubName: '',
-        provinceId: '',
-        cityId: '',
-        townId: '',
-        pageNum: 1,
-        pageSize: 9,
-      },
-      total: 0,
       cityList: [],
       selectValue: [],
-      starList: [],
     }
   },
   computed: {
-    ...mapGetters(['supplierInfo', 'authUserId', 'routePrefix']),
+    ...mapGetters(['supplierInfo']),
     emptyList() {
       return 3 - (this.list.length % 3)
     },
@@ -146,104 +131,25 @@ export default {
       return 3 - (this.starList.length % 3)
     },
   },
-  mounted() {
-    const cacheData = this.$getStorage(this.routePrefix, 'club_list_data')
-    if (cacheData) {
-      this.initFromCache(cacheData)
-      this.$removeStorage(this.routePrefix, 'club_list_data')
-    } else {
-      this.initData()
-      this.fetchCityList()
-    }
-    this.fetchAuthClubStarList()
-  },
-  beforeDestroy() {
-    this.$toast.clear()
+  created() {
+    this.fetchCityList()
   },
   methods: {
-    // 获取明星机构列表
-    async fetchAuthClubStarList() {
-      try {
-        const res = await this.$http.api.getAuthClubStarList({
-          authUserId: this.authUserId,
-        })
-        this.starList = res.data.slice(0, 3)
-      } catch (error) {
-        console.log(error)
-      }
-    },
-    // 绘制logo的方法
-    drawLogo,
-    // 查看详情
-    toDetail(item) {
-      this.$setStorage(this.routePrefix, 'club_list_data', this.$data, {
-        expiredTime: 5 * 60 * 1000,
-      })
-      this.$setStorage(this.routePrefix, 'clubInfo', item)
-      const url = `${this.routePrefix}/approve/club/detail?id=${item.authId}`
-      this.$router.push(url)
-    },
     // 从缓存中获取数据
     initFromCache(cacheData) {
-      this.isLoadingMore = cacheData.isLoadingMore
-      this.finished = cacheData.finished
-      this.isRequest = cacheData.isRequest
-      this.list = cacheData.list
-      this.listQuery = cacheData.listQuery
-      this.total = cacheData.total
-      this.cityList = cacheData.cityList
-      this.selectValue = cacheData.selectValue
-      this.$refs.citySelect.setSelectValue(this.selectValue)
-    },
-
-    generateDistance(value) {
-      return value > 1 ? value + 'km' : value * 1000 + 'm'
-    },
-
-    // 初始化页面数据
-    async initData() {
-      this.listQuery.authUserId = this.authUserId
-
-      // 自定义加载图标
-      this.$toast.loading({
-        message: '正在获取您附近的机构...',
-        duration: 0,
+      const data = objectCover(this, cacheData)
+      this.$nextTick(() => {
+        this.$refs.citySelect.setSelectValue(data.selectValue)
       })
-
-      // 获取定位信息 这里使用的是高德地图获取用户具体位置信息
-      try {
-        const resLocation = await geolocation()
-        this.listQuery.lngAndLat = `${resLocation.position.lng},${resLocation.position.lat}`
-      } catch (error) {
-        this.$toast('获取定位信息失败,请确保您开启的定位权限并保存网络畅通')
-        this.isRequest = false
-      }
-
-      // 获取机构列表
-      this.fetchList()
     },
-    fetchList: debounce(async function () {
-      try {
-        this.isLoadingMore = true
-        const res = await this.$http.api.getAuthClubList(this.listQuery)
-        this.total = res.data.total
-        this.list = [...this.list, ...res.data.list]
-        this.finished = !res.data.hasNextPage
-        this.isLoadingMore = false
-        this.listQuery.pageNum += 1
-      } catch (error) {
-        console.log(error)
-      } finally {
-        this.$toast.clear()
-        this.isRequest = false
-      }
-    }, 400),
+
     // 获取地址列表
     fetchCityList() {
       this.$http.api.fetchAllCityList().then((res) => {
         this.cityList = res.data
       })
     },
+
     // 城市变化
     onCityChange(selectValue) {
       this.listQuery.provinceId = ''
@@ -260,30 +166,7 @@ export default {
           this.listQuery.townId = item.id
         }
       })
-      this.listQuery.pageNum = 1
-      this.list = []
-      this.fetchList()
-    },
-    // 搜索
-    onSearch() {
-      this.listQuery.pageNum = 1
-      this.list = []
-      this.fetchList()
-    },
-    // 格式化地址
-    formatAddress(a1, a2) {
-      let resutl = ''
-      if (typeof a1 === 'string') {
-        resutl += a1
-      }
-      if (typeof a2 === 'string') {
-        resutl += a2
-      }
-      return resutl || '暂无'
-    },
-    // 加载更多
-    onLoadMore() {
-      this.fetchList()
+      this.filterClubList()
     },
   },
 }

+ 3 - 70
pages/_template/app/approve/club/star-list.vue

@@ -45,77 +45,10 @@
 </template>
 
 <script>
-import { mapGetters } from 'vuex'
-import { drawLogo, debounce } from '@/utils'
+import clubStarListMixin from '@/mixins/clubStarList'
 export default {
-  layout: 'app',
-  data() {
-    return {
-      finished: false,
-      isRequest: true,
-      list: [],
-    }
-  },
-  computed: {
-    ...mapGetters(['supplierInfo', 'authUserId', 'routePrefix']),
-    emptyList() {
-      return 3 - (this.list.length % 3)
-    },
-    total() {
-      return this.list.length
-    },
-  },
-  mounted() {
-    this.initData()
-  },
-  beforeDestroy() {
-    this.$toast.clear()
-  },
-  methods: {
-    // 绘制logo的方法
-    drawLogo,
-    // 查看详情
-    toDetail(item) {
-      const url = `${this.routePrefix}/approve/club/detail?id=${item.authId}`
-      this.$router.push(url)
-    },
-
-    // 初始化页面数据
-    async initData() {
-      // 获取机构列表
-      this.fetchList()
-    },
-
-    fetchList: debounce(async function () {
-      try {
-        // 自定义加载图标
-        this.$toast.loading({
-          message: '正在获取明星机构...',
-          duration: 0,
-        })
-        const res = await this.$http.api.getAuthClubStarList({
-          authUserId: this.authUserId,
-        })
-        this.list = res.data
-      } catch (error) {
-        console.log(error)
-      } finally {
-        this.$toast.clear()
-      }
-    }, 400),
-
-    // 格式化地址
-    formatAddress(a1, a2) {
-      let resutl = ''
-      if (typeof a1 === 'string') {
-        resutl += a1
-      }
-      if (typeof a2 === 'string') {
-        resutl += a2
-      }
-      return resutl || '暂无'
-    },
-  },
+  layout: 'app-ross',
+  mixins: [clubStarListMixin],
 }
 </script>
 

+ 712 - 0
pages/_template/app/approve/device/detail.vue

@@ -0,0 +1,712 @@
+<template>
+  <div class="device-detail" v-if="productInfo">
+    <div class="detail-title">设备认证</div>
+    <div class="page-top">
+      <div class="swiper-body">
+        <img :src="productInfo.pcImage" class="device-image" />
+        <div class="auth-seal"></div>
+        <img
+          class="auth-card"
+          :src="authCardImage"
+          @click="onShowAuthCard"
+          v-if="!showAuthCard"
+        />
+        <img class="auth-logo" :src="brandLogoImage" />
+      </div>
+      <div class="device-info">
+        <div class="logo">
+          <div class="logo-swiper" v-if="clubLogo.length > 1">
+            <SimpleSwiper
+              :imageList="clubLogo"
+              :pagination="false"
+            ></SimpleSwiper>
+          </div>
+          <img :src="clubLogo[0]" alt="logo" v-else />
+        </div>
+        <div class="section">
+          <div class="name" v-text="productInfo.productName"></div>
+          <div class="sncode mobile">
+            SN码:<span>{{ productInfo.snCode | snCodeRender }}</span>
+          </div>
+          <div class="row">
+            <!-- <span>产地:{{ productInfo.producePlace }}</span> -->
+            <span>品牌:{{ productInfo.brandName }}</span
+            ><i></i><span>产地:{{ productInfo.producePlace }}</span>
+            <!-- <span>品牌:RÖS'S</span><i></i><span>产地:西班牙巴塞罗那</span> -->
+          </div>
+          <div class="sncode pc">
+            SN码:<span>{{ productInfo.snCode | snCodeRender }}</span>
+          </div>
+          <div class="maker">制造商:{{ productInfo.manufacturer }}</div>
+          <div class="supplier">供应商:{{ productInfo.shopName }}</div>
+          <!-- <div class="supplier">供应商:ACEBELLE生物科技(深圳)有限公司</div> -->
+        </div>
+        <div class="auth" v-if="productInfo">
+          <div class="auth-icon"></div>
+          <template v-for="item in productInfo.clubList">
+            <div class="auth-info" :key="item.authId">
+              <span class="font-bold">ROS'S</span>
+              <span>授予</span>
+              <span>{{ item.authParty }}</span>
+              <span>正品拥有</span>
+            </div>
+          </template>
+        </div>
+      </div>
+    </div>
+    <div class="page-content">
+      <div class="device-params">
+        <div class="title">相关参数</div>
+        <div class="line"></div>
+        <div class="params">
+          <div class="row" v-for="(row, index) in paramListRender" :key="index">
+            <template v-for="(col, index) in row">
+              <div class="col" :key="'name' + index">
+                {{ col.paramName }}{{ isPc ? ':' : '' }}
+              </div>
+              <div class="col" :key="'content' + index">
+                {{ col.paramContent }}
+              </div>
+            </template>
+          </div>
+        </div>
+      </div>
+    </div>
+    <!-- 授权牌弹窗 -->
+    <div class="mask" v-if="showAuthCard" @click="onHideAuthCard"></div>
+    <transition
+      enter-active-class="animate__zoomIn"
+      leave-active-class="animate__zoomOut"
+    >
+      <div class="auth-card-content animate__animated" v-if="showAuthCard">
+        <div class="auth-card-popup">
+          <span class="el-icon-circle-close" @click="onHideAuthCard"></span>
+          <img :src="authCardImage" />
+        </div>
+      </div>
+    </transition>
+  </div>
+</template>
+
+<script>
+import deviceDetailMixin from '@/mixins/deviceDetail'
+export default {
+  layout: 'app',
+  mixins: [deviceDetailMixin],
+}
+</script>
+
+<style lang="scss" scoped>
+::v-deep {
+  .swiper-pagination-bullet {
+    @include themify($themes) {
+      background: themed('color') !important;
+    }
+  }
+
+  .simple-swiper {
+    .swiper-pagination-bullet-active {
+      @include themify($themes) {
+        background: themed('color') !important;
+      }
+    }
+  }
+}
+
+.mask {
+  width: 100vw;
+  height: 100vh;
+  position: fixed;
+  left: 0;
+  top: 0;
+  z-index: 8;
+  background: rgba(0, 0, 0, 0.5);
+}
+
+// pc 端
+@media screen and (min-width: 768px) {
+  .device-detail {
+    margin-bottom: 48px;
+
+    .auth-card-content {
+      width: 100vw;
+      height: 100vh;
+      position: fixed;
+      left: 0;
+      top: 0;
+      display: flex;
+      justify-content: center;
+      align-items: center;
+      z-index: 9;
+      .auth-card-popup {
+        position: relative;
+        width: 622px;
+
+        img {
+          display: block;
+          width: 100%;
+          height: auto;
+        }
+
+        .el-icon-circle-close {
+          position: absolute;
+          top: -50px;
+          right: 0;
+          font-size: 32px;
+          color: #fff;
+          cursor: pointer;
+        }
+      }
+    }
+
+    .detail-title {
+      font-size: 24px;
+      color: #282828;
+      margin: 32px auto 24px;
+      width: 1200px;
+    }
+
+    .page-top,
+    .page-content {
+      width: 1200px;
+      margin: 0 auto;
+      background: #fff;
+    }
+    .page-top {
+      display: flex;
+      justify-content: space-between;
+      align-items: flex-start;
+      padding: 24px;
+      padding-right: 40px;
+      .swiper-body {
+        position: relative;
+        width: 540px;
+        height: 540px;
+        background: #f7f7f7;
+        .device-image {
+          width: 100%;
+          height: 100%;
+        }
+
+        .auth-seal {
+          position: absolute;
+          width: 70px;
+          height: 70px;
+          background: url(~assets/theme-images/ross/pc-icon-auth-seal.png)
+            no-repeat center;
+          background-size: 70px;
+          right: 24px;
+          bottom: 24px;
+          z-index: 2;
+        }
+
+        .auth-card {
+          position: absolute;
+          width: auto;
+          height: 110px;
+          display: block;
+          bottom: 24px;
+          left: 24px;
+          z-index: 2;
+          cursor: pointer;
+        }
+
+        .auth-logo {
+          position: absolute;
+          max-width: 120px;
+          max-height: 120px;
+          top: 24px;
+          left: 24px;
+          z-index: 2;
+        }
+      }
+      .device-info {
+        width: 572px;
+        position: relative;
+
+        .section {
+          width: 440px;
+          word-break: break-all;
+        }
+
+        .logo {
+          width: 114px;
+          height: 114px;
+          border-radius: 50%;
+          // background: #d8d8d8;
+          border: 1px solid #d8d8d8;
+          // box-sizing: border-box;
+          position: absolute;
+          right: 0;
+          top: 0;
+
+          .logo-swiper {
+            width: 112px;
+            height: 112px;
+            overflow: hidden;
+            border-radius: 50%;
+          }
+
+          ::v-deep {
+            img {
+              width: 112px;
+              height: 112px;
+              border-radius: 50%;
+            }
+          }
+
+          &::after {
+            content: '';
+            position: absolute;
+            z-index: 1;
+            right: 6px;
+            bottom: 0;
+            display: block;
+            width: 24px;
+            height: 24px;
+            background: url(~assets/theme-images/ross/pc-icon-auth-ren.png)
+              no-repeat center;
+            background-size: 23px;
+          }
+
+          img {
+            display: block;
+            width: 100%;
+            height: 100%;
+            // background: pink;
+            border-radius: 50%;
+          }
+        }
+
+        .name {
+          font-size: 24px;
+          color: #282828;
+          line-height: 1.6;
+          margin-bottom: 24px;
+          font-weight: bold;
+        }
+
+        .row,
+        .sncode,
+        .maker {
+          margin-bottom: 16px;
+        }
+        .row {
+          line-height: 24px;
+          i {
+            position: relative;
+            margin: 0 16px;
+            &::after {
+              content: '';
+              display: block;
+              width: 1px;
+              height: 16px;
+              background: #999999;
+              position: absolute;
+              top: 0;
+              left: 0;
+            }
+          }
+
+          span {
+            font-size: 18px;
+            color: #999999;
+          }
+        }
+
+        .sncode.mobile {
+          display: none;
+        }
+
+        .sncode.pc {
+          font-size: 18px;
+          color: #282828;
+        }
+        .supplier,
+        .maker {
+          font-size: 20px;
+          color: #282828;
+        }
+
+        .auth {
+          width: 100%;
+          min-height: 114px;
+          @include themify($themes) {
+            background: themed('color') !important;
+          }
+          margin-top: 56px;
+          box-sizing: border-box;
+          padding: 24px;
+
+          .auth-icon {
+            height: 28px;
+            background: url(~assets/theme-images/ross/pc-icon-auth.png)
+              no-repeat left center;
+            background-size: auto 28px;
+            margin-bottom: 10px;
+          }
+
+          .auth-info {
+            font-size: 0;
+            span {
+              font-size: 20px;
+              color: #fff;
+            }
+          }
+        }
+      }
+    }
+    .page-content {
+      margin-top: 16px;
+      box-sizing: border-box;
+      padding: 24px;
+      .device-params {
+        .title {
+          font-size: 28px;
+          color: #282828;
+          font-weight: bold;
+        }
+        .line {
+          height: 1px;
+          background: #ececec;
+          position: relative;
+          margin-top: 10px;
+          margin-bottom: 20px;
+          &::after {
+            content: '';
+            position: absolute;
+            width: 73px;
+            height: 2px;
+            @include themify($themes) {
+              background: themed('color') !important;
+            }
+            left: 0;
+            bottom: 0;
+          }
+        }
+        .params {
+          width: 100%;
+          .row {
+            display: table-row;
+            width: 100%;
+          }
+
+          .col {
+            display: table-cell;
+            font-size: 16px;
+            padding: 12px 0;
+
+            &:nth-child(2n-1) {
+              color: #999999;
+              white-space: nowrap;
+            }
+
+            &:nth-child(2n) {
+              color: #282828;
+              padding-left: 12px;
+            }
+
+            &:nth-child(3) {
+              padding-left: 100px;
+            }
+          }
+        }
+      }
+    }
+  }
+}
+
+// 移动端
+@media screen and (max-width: 768px) {
+  .device-detail {
+    .detail-title {
+      display: none;
+    }
+
+    .auth-card-content {
+      width: 100vw;
+      height: 100vh;
+      position: fixed;
+      left: 0;
+      top: 0;
+      display: flex;
+      justify-content: center;
+      align-items: center;
+      z-index: 9;
+      .auth-card-popup {
+        position: relative;
+        width: 86vw;
+
+        img {
+          display: block;
+          width: 100%;
+          height: auto;
+        }
+
+        .el-icon-circle-close {
+          position: absolute;
+          top: -8vw;
+          right: 0;
+          font-size: 7vw;
+          color: #fff;
+          cursor: pointer;
+        }
+      }
+    }
+
+    .page-top,
+    .page-content {
+      margin: 0 auto;
+      background: #fff;
+    }
+    .page-top {
+      .swiper-body {
+        position: relative;
+        width: 100vw;
+        height: 100vw;
+        background: #f7f7f7;
+
+        .device-image {
+          width: 100%;
+          height: 100%;
+        }
+
+        ::v-deep {
+          img {
+            width: 100vw;
+            height: 100vw;
+          }
+        }
+
+        .auth-seal {
+          position: absolute;
+          width: 13.8vw;
+          height: 13.8vw;
+          background: url(~assets/theme-images/ross/h5-icon-auth-seal.png)
+            no-repeat center;
+          background-size: 13.8vw;
+          right: 4vw;
+          bottom: 4vw;
+          z-index: 2;
+        }
+
+        .auth-card {
+          position: absolute;
+          width: auto;
+          height: 20.6vw;
+          display: block;
+          bottom: 4vw;
+          left: 4vw;
+          z-index: 2;
+        }
+
+        .auth-logo {
+          position: absolute;
+          max-width: 18vw;
+          max-height: 18vw;
+          top: 4vw;
+          left: 4vw;
+          z-index: 2;
+        }
+      }
+      .device-info {
+        position: relative;
+
+        .section {
+          word-break: break-all;
+          padding: 4vw 4vw 0;
+        }
+
+        .logo {
+          width: 18vw;
+          height: 18vw;
+          border-radius: 50%;
+          // background: #d8d8d8;
+          border: 0.1vw solid #d8d8d8;
+          box-sizing: border-box;
+          position: absolute;
+          right: 4vw;
+          top: 5.8vw;
+
+          .logo-swiper {
+            width: 18vw;
+            height: 18vw;
+            overflow: hidden;
+            border-radius: 50%;
+          }
+
+          ::v-deep {
+            img {
+              width: 18vw;
+              height: 18vw;
+              border-radius: 50%;
+            }
+          }
+
+          &::after {
+            content: '';
+            position: absolute;
+            z-index: 1;
+            right: 0.7vw;
+            bottom: 0;
+            display: block;
+            width: 3.6vw;
+            height: 3.6vw;
+            background: url(~assets/theme-images/ross/h5-icon-auth-ren.png)
+              no-repeat center;
+            background-size: 3.6vw;
+          }
+
+          img {
+            display: block;
+            width: 100%;
+            height: 100%;
+            // background: pink;
+            border-radius: 50%;
+          }
+        }
+
+        .name {
+          font-size: 5.4vw;
+          color: #282828;
+          line-height: 7.4vw;
+          margin-bottom: 2.4vw;
+          font-weight: bold;
+        }
+
+        .sncode.pc {
+          display: none;
+        }
+        .sncode.mobile {
+          margin: 2.4vw 0 5.6vw;
+          color: #282828;
+          font-size: 4vw;
+        }
+
+        .row {
+          line-height: 4.7vw;
+          i {
+            position: relative;
+            margin: 0 4vw;
+            &::after {
+              content: '';
+              display: block;
+              width: 0.2vw;
+              height: 3vw;
+              background: #282828;
+              position: absolute;
+              top: 1vw;
+              left: 0;
+            }
+          }
+
+          span {
+            font-size: 3.6vw;
+            color: #282828;
+          }
+        }
+
+        .row,
+        .maker {
+          margin-bottom: 1.2vw;
+        }
+
+        .supplier,
+        .maker {
+          font-size: 3.6vw;
+          color: #282828;
+        }
+
+        .auth {
+          // width: 100%;
+          margin: 0 4vw;
+          min-height: 20vw;
+          @include themify($themes) {
+            background: themed('color') !important;
+          }
+          margin-top: 4vw;
+          box-sizing: border-box;
+          padding: 5.2vw 4vw;
+          border-radius: 1.2vw;
+
+          .auth-icon {
+            height: 4.9vw;
+            background: url(~assets/theme-images/ross/h5-icon-auth.png)
+              no-repeat left center;
+            background-size: auto 4.9vw;
+            margin-bottom: 1vw;
+          }
+
+          .auth-info {
+            font-size: 0;
+            span {
+              font-size: 3.6vw;
+              line-height: 6.4vw;
+              color: #fff;
+              font-weight: bold;
+            }
+          }
+        }
+      }
+    }
+    .page-content {
+      .device-params {
+        padding: 8vw 4vw;
+        box-sizing: border-box;
+        .title {
+          font-size: 4.6vw;
+          color: #282828;
+          font-weight: bold;
+        }
+        .line {
+          // height: 0.2vw;
+          // background: #ececec;
+          position: relative;
+          margin-top: 4.7vw;
+          margin-bottom: 1.2vw;
+        }
+        .params {
+          width: 100%;
+          word-break: break-all;
+          text-align: justify;
+          .row {
+            display: table-row;
+            width: 100%;
+
+            &:first-child {
+              .col {
+                padding-top: 0;
+              }
+            }
+
+            &:last-child {
+              .col {
+                padding-bottom: 0;
+              }
+            }
+          }
+
+          .col {
+            display: table-cell;
+            font-size: 3.6vw;
+            padding: 1.6vw 0;
+
+            &:nth-child(2n-1) {
+              color: #282828;
+              padding-right: 3.2vw;
+              font-weight: bold;
+              white-space: nowrap;
+              border-right: 0.2vw solid #d8d8d8;
+            }
+
+            &:nth-child(2n) {
+              color: #4e4e4e;
+              padding-left: 3.2vw;
+            }
+          }
+        }
+      }
+    }
+  }
+}
+</style>

+ 8 - 118
pages/_template/app/approve/device/index.vue

@@ -8,7 +8,7 @@
       @load="onLoadMore"
     >
       <div class="page-top flex flex-col justify-center items-center">
-        <img class="logo" :src="supplierInfo.logo" />
+        <!-- <img class="logo" :src="supplierInfo.logo" /> -->
         <div class="mt-2 name">
           <span v-text="supplierInfo.shopName"></span><span>官方授权设备</span>
         </div>
@@ -80,8 +80,10 @@
                   </template>
                   <div class="club-name" slot="reference">
                     所属机构:
-                    <template v-for="club in item.clubList">
-                      ,<span :key="club.authId">{{ club.authParty }}</span>
+                    <template v-for="(club, index) in item.clubList">
+                      <span :key="club.authId"
+                        ><i v-if="index !== 0">,</i>{{ club.authParty }}</span
+                      >
                     </template>
                   </div>
                 </el-popover>
@@ -101,123 +103,10 @@
 </template>
 
 <script>
-import { debounce } from '@/utils'
-import { mapGetters } from 'vuex'
+import deviceListMixin from '@/mixins/deviceList'
 export default {
   layout: 'app',
-  filters: {
-    formatSnCode(code) {
-      if (!code) return ''
-      return code.replace(/^(\w{2})\w+(\w{4})$/, '$1******$2')
-    },
-  },
-  data() {
-    return {
-      isLoadingMore: false,
-      finished: false,
-      isRequest: true,
-      searchQuery: {
-        type: 2,
-        keyword: '测试机构A',
-      },
-      listQuery: {
-        listType: 1,
-        productTypeId: '',
-        snCode: '',
-        authParty: '测试机构A',
-        pageNum: 1,
-        pageSize: 10,
-      },
-      list: [],
-      total: 0,
-      productSelectList: [],
-      searchFlag: false,
-    }
-  },
-  computed: {
-    ...mapGetters(['routePrefix', 'supplierInfo', 'authUserId']),
-  },
-  mounted() {
-    this.fetchProductSelectList()
-  },
-  methods: {
-    // 获取设备列表
-    fetchList: debounce(async function () {
-      try {
-        this.isLoadingMore = true
-        const res = await this.$http.api.getAuthProductList(this.listQuery)
-        this.list = [...this.list, ...res.data.list]
-        this.finished = !res.data.hasNextPage
-        this.total = res.data.total
-        this.isLoadingMore = false
-        this.listQuery.pageNum += 1
-        this.searchFlag = true
-      } catch (error) {
-        console.log(error)
-      } finally {
-        this.isRequest = false
-        this.searchFlag = true
-      }
-    }, 400),
-
-    // 搜索
-    onSearch() {
-      const { productTypeId } = this.listQuery
-      const { type, keyword } = this.searchQuery
-      if (!productTypeId) {
-        return this.$toast('请选择设备分类')
-      }
-      if (!type) {
-        return this.$toast('请选择搜索条件')
-      }
-      if (!keyword) {
-        return this.$toast('搜索内容不能为空')
-      }
-
-      if (type === 1) {
-        this.listQuery.snCode = keyword
-        this.listQuery.authParty = ''
-      } else {
-        this.listQuery.snCode = ''
-        this.listQuery.authParty = keyword
-      }
-
-      this.list = []
-      this.listQuery.pageNum = 1
-      this.fetchList()
-    },
-
-    // 获取设备种类
-    async fetchProductSelectList() {
-      try {
-        const res = await this.$http.api.fetchProductSelectList({
-          authUserId: this.authUserId,
-        })
-        this.productSelectList = res.data
-      } catch (error) {
-        console.log(error)
-      }
-    },
-
-    // 设备详情
-    toDetail(item) {
-      // window.location.href = `${process.env.CIMEI_LOCAL}/product/auth/product-${item.productId}.html`
-      this.$router.push(
-        `${this.routePrefix}/approve/device/detail?id=${item.productId}`
-      )
-    },
-
-    // 机构详情
-    toClubDetail(item) {
-      const url = `${this.routePrefix}/approve/club/detail?id=${item.authId}`
-      this.$router.push(url)
-    },
-
-    // 加载更多
-    onLoadMore() {
-      this.fetchList()
-    },
-  },
+  mixins: [deviceListMixin],
 }
 </script>
 
@@ -452,6 +341,7 @@ export default {
           height: 19.6vw;
         }
         .info {
+          width: 64vw;
           position: relative;
           margin-left: 3.2vw;
           .name {

+ 0 - 289
pages/_template/app/approve/device/index1.vue

@@ -1,289 +0,0 @@
-<template>
-  <div class="page">
-    <van-list
-      v-model="isLoadingMore"
-      :finished="finished"
-      :immediate-check="false"
-      :finished-text="total ? '没有更多了' : ''"
-      @load="onLoadMore"
-    >
-      <div class="page-top flex flex-col justify-center items-center">
-        <!-- <img class="logo" :src="supplierInfo.logo" /> -->
-        <div class="mt-2 name">
-          <span v-text="supplierInfo.shopName"></span>
-          <span>官方授权设备</span>
-        </div>
-      </div>
-      <div class="page-content">
-        <!-- 搜索区域 -->
-        <div class="search">
-          <SimpleSearch
-            v-model="listQuery.name"
-            @search="onSearch"
-            placeholder="搜索设备"
-          />
-        </div>
-        <!-- 标题 -->
-        <div class="title px-4 pt-12 pb-6">
-          共<span v-text="total"></span>种设备
-        </div>
-        <!-- 列表 -->
-        <div class="list">
-          <div
-            class="section flex items-center mb-4"
-            v-for="item in list"
-            :key="item.productTypeId"
-            @click="toDetail(item)"
-          >
-            <img class="cover" :src="item.image" />
-            <div class="name" v-text="item.name"></div>
-          </div>
-          <div class="empty" v-for="i in emptyList" :key="'empty-' + i"></div>
-        </div>
-
-        <!-- 列表为空 -->
-        <SimpleEmpty
-          v-if="!total && !isRequest"
-          name="icon-empty-device.png"
-          description="敬请期待~"
-        ></SimpleEmpty>
-      </div>
-    </van-list>
-  </div>
-</template>
-
-<script>
-import { mapGetters } from 'vuex'
-import { debounce } from '@/utils'
-export default {
-  layout: 'app',
-  data() {
-    return {
-      isLoadingMore: true,
-      finished: false,
-      isRequest: true,
-      listQuery: {
-        authUserId: '',
-        name: '',
-        pageNum: 1,
-        pageSize: 9,
-      },
-      list: [],
-      total: 0,
-    }
-  },
-  computed: {
-    ...mapGetters(['supplierInfo', 'authUserId', 'routePrefix']),
-    emptyList() {
-      return 3 - (this.list.length % 3)
-    },
-  },
-  mounted() {
-    this.fetchList()
-  },
-  methods: {
-    // 获取设备分类
-    fetchList: debounce(async function () {
-      try {
-        this.isLoadingMore = true
-        this.listQuery.authUserId = this.authUserId
-        const res = await this.$http.api.getAuthProductCateList(this.listQuery)
-        this.list = [...this.list, ...res.data.list]
-        this.finished = !res.data.hasNextPage
-        this.total = res.data.total
-        this.isLoadingMore = false
-        this.listQuery.pageNum += 1
-      } catch (error) {
-        console.log(error)
-      } finally {
-        this.isRequest = false
-      }
-    }, 400),
-
-    // 设备详情
-    toDetail(item) {
-      const url = `${this.routePrefix}/approve/device/list?id=${item.productTypeId}`
-      this.$router.push(url)
-    },
-    // 搜索
-    onSearch() {
-      this.listQuery.pageNum = 1
-      this.list = []
-      this.fetchList()
-    },
-    // 加载更多
-    onLoadMore() {
-      this.fetchList()
-    },
-  },
-}
-</script>
-
-<style scoped lang="scss">
-// pc 端
-@media screen and (min-width: 768px) {
-  .page {
-    position: relative;
-    min-height: calc(100vh - 80px - 80px);
-    background-color: #fff;
-  }
-  .page-top {
-    height: 420px;
-    @include themify($themes) {
-      background: themed('pc-banner-device');
-    }
-    background-size: auto 420px;
-
-    .logo {
-      display: block;
-      width: 120px;
-      height: 120px;
-      border-radius: 50%;
-      background: #fff;
-    }
-    .name {
-      font-size: 30px;
-      color: #fff;
-    }
-
-    .logo,
-    .name {
-      transform: translateY(-30px);
-    }
-  }
-  .page-content {
-    width: 1200px;
-    margin: 0 auto;
-
-    .search {
-      position: absolute;
-      left: 50%;
-      top: 300px;
-      transform: translateX(-50%);
-    }
-
-    .title {
-      font-size: 16px;
-      color: #404040;
-
-      span {
-        @include themify($themes) {
-          color: themed('color');
-        }
-      }
-    }
-
-    .list {
-      display: flex;
-      align-items: center;
-      flex-wrap: wrap;
-      justify-content: space-between;
-
-      .empty {
-        width: 390px;
-      }
-
-      .section {
-        width: 390px;
-        height: 108px;
-        background-color: #f3f5f6;
-        border-radius: 4px;
-        box-sizing: border-box;
-        padding: 16px;
-        cursor: pointer;
-        transition: all 0.4s;
-
-        &:hover {
-          box-shadow: 0 0 24px rgba(0, 0, 0, 0.2);
-        }
-
-        .cover {
-          display: block;
-          width: 84px;
-          height: 84px;
-        }
-        .name {
-          width: 260px;
-          font-size: 16px;
-          color: #101010;
-          text-overflow: ellipsis;
-          white-space: nowrap;
-          overflow: hidden;
-          margin-left: 16px;
-        }
-      }
-    }
-  }
-}
-
-// 移动 端
-@media screen and (max-width: 768px) {
-  .page-top {
-    height: 46vw;
-    @include themify($themes) {
-      background: themed('h5-banner-device');
-    }
-    background-size: auto 46vw;
-    .logo {
-      display: block;
-      width: 14.8vw;
-      height: 14.8vw;
-      border-radius: 50%;
-      background: #fff;
-    }
-    .name {
-      font-size: 4vw;
-      color: #fff;
-    }
-  }
-  .page-content {
-    position: relative;
-    .search {
-      position: absolute;
-      left: 50%;
-      top: 0;
-      transform: translate(-50%, -50%);
-    }
-
-    .title {
-      font-size: 3.4vw;
-      color: #404040;
-
-      span {
-        @include themify($themes) {
-          color: themed('color');
-        }
-      }
-    }
-
-    .list {
-      display: flex;
-      align-items: center;
-      flex-direction: column;
-      .section {
-        width: 93.6vw;
-        height: 26vw;
-        background-color: #f3f5f6;
-        border-radius: 4px;
-        box-sizing: border-box;
-        padding: 3.2vw;
-
-        .cover {
-          display: block;
-          width: 19.6vw;
-          height: 19.6vw;
-        }
-        .name {
-          width: 66vw;
-          font-size: 4vw;
-          color: #101010;
-          text-overflow: ellipsis;
-          white-space: nowrap;
-          overflow: hidden;
-          margin-left: 3.2vw;
-        }
-      }
-    }
-  }
-}
-</style>

+ 0 - 351
pages/_template/app/approve/device/list.vue

@@ -1,351 +0,0 @@
-<template>
-  <div class="page">
-    <van-list
-      v-model="isLoadingMore"
-      :finished="finished"
-      :immediate-check="false"
-      :finished-text="total ? '没有更多了' : ''"
-      @load="onLoadMore"
-    >
-      <div class="page-top flex flex-col justify-center items-center">
-        <!-- <img class="logo" :src="supplierInfo.logo" /> -->
-        <div class="mt-2 name">
-          <span v-text="supplierInfo.shopName"></span>
-          <span>官方授权设备</span>
-        </div>
-      </div>
-      <div class="page-content">
-        <!-- 搜索区域 -->
-        <div class="search">
-          <SimpleSearch
-            v-model="listQuery.snCode"
-            maxLength="4"
-            @search="onSearch"
-            placeholder="搜索设备SN码后四位"
-          />
-        </div>
-        <!-- 标题 -->
-        <div class="title px-4 pt-12 pb-6 md:px-0">
-          共<span v-text="total"></span>台设备
-        </div>
-        <!-- 列表 -->
-        <div class="list">
-          <div
-            class="section flex justify-between mb-4"
-            v-for="item in list"
-            :key="item.productId"
-            @click="toDetail(item)"
-          >
-            <img class="cover" :src="item.productImage" />
-            <div class="info">
-              <div class="name" v-text="item.productName"></div>
-              <div class="code">SN码:{{ item.snCode | formatSnCode }}</div>
-              <div class="club-name">
-                所属机构:<span @click.stop="toClubDetail(item)">{{
-                  item.clubName
-                }}</span>
-              </div>
-            </div>
-          </div>
-
-          <div class="empty" v-for="i in emptyList" :key="'empty-' + i"></div>
-        </div>
-
-        <!-- 列表为空 -->
-        <SimpleEmpty
-          v-if="!total && !isRequest"
-          name="icon-empty-device.png"
-          description="敬请期待~"
-        ></SimpleEmpty>
-      </div>
-    </van-list>
-  </div>
-</template>
-
-<script>
-import { debounce } from '@/utils'
-import { mapGetters } from 'vuex'
-export default {
-  layout: 'app',
-  filters: {
-    formatSnCode(code) {
-      if (!code) return ''
-      return code.replace(/^(\w{2})\w+(\w{4})$/, '$1******$2')
-    },
-  },
-  data() {
-    return {
-      isLoadingMore: true,
-      finished: false,
-      isRequest: true,
-      listQuery: {
-        productTypeId: '',
-        snCode: '',
-        pageNum: 1,
-        pageSize: 9,
-      },
-      list: [],
-      total: 0,
-    }
-  },
-  computed: {
-    ...mapGetters(['routePrefix', 'supplierInfo']),
-    isEmpty() {
-      return this.list.length === 0
-    },
-    emptyList() {
-      return 3 - (this.list.length % 3)
-    },
-  },
-  mounted() {
-    this.initData()
-  },
-  methods: {
-    initData() {
-      this.listQuery.productTypeId = parseInt(this.$route.query.id)
-      this.fetchList()
-    },
-    fetchList: debounce(async function () {
-      try {
-        this.isLoadingMore = true
-        const res = await this.$http.api.getAuthProductList(this.listQuery)
-        this.list = [...this.list, ...res.data.list]
-        this.finished = !res.data.hasNextPage
-        this.total = res.data.total
-        this.isLoadingMore = false
-        this.listQuery.pageNum += 1
-      } catch (error) {
-        console.log(error)
-      } finally {
-        this.isRequest = false
-      }
-    }, 400),
-    // 搜索
-    onSearch() {
-      this.list = []
-      this.listQuery.pageNum = 1
-      this.fetchList()
-    },
-    // 设备详情
-    toDetail(item) {
-      window.location.href = `${process.env.CIMEI_LOCAL}/product/auth/product-${item.productId}.html`
-    },
-    // 机构详情
-    toClubDetail(item) {
-      const url = `${this.routePrefix}/approve/club/detail?id=${item.authId}`
-      this.$router.push(url)
-    },
-    // 加载更多
-    onLoadMore() {
-      this.fetchList()
-    },
-  },
-}
-</script>
-
-<style scoped lang="scss">
-// pc 端
-@media screen and (min-width: 768px) {
-  .page {
-    position: relative;
-    min-height: calc(100vh - 80px - 80px);
-    background-color: #fff;
-  }
-  .page-top {
-    height: 420px;
-    @include themify($themes) {
-      background: themed('pc-banner-device');
-    }
-    background-size: auto 420px;
-
-    .logo {
-      display: block;
-      width: 120px;
-      height: 120px;
-      border-radius: 50%;
-      background: #fff;
-    }
-    .name {
-      font-size: 30px;
-      color: #fff;
-    }
-
-    .logo,
-    .name {
-      transform: translateY(-30px);
-    }
-  }
-  .page-content {
-    width: 1200px;
-    margin: 0 auto;
-
-    .search {
-      position: absolute;
-      left: 50%;
-      top: 300px;
-      transform: translateX(-50%);
-    }
-
-    .title {
-      font-size: 16px;
-      color: #404040;
-
-      span {
-        @include themify($themes) {
-          color: themed('color');
-        }
-      }
-    }
-
-    .list {
-      display: flex;
-      align-items: center;
-      flex-wrap: wrap;
-      justify-content: space-between;
-
-      .empty {
-        width: 390px;
-      }
-
-      .section {
-        width: 390px;
-        height: 108px;
-        background-color: #f3f5f6;
-        border-radius: 4px;
-        box-sizing: border-box;
-        padding: 12px;
-        cursor: pointer;
-        transition: all 0.4s;
-        &:hover {
-          box-shadow: 0 0 24px rgba(0, 0, 0, 0.2);
-        }
-
-        .cover {
-          display: block;
-          width: 84px;
-          height: 84px;
-        }
-        .info {
-          width: 270px;
-          position: relative;
-          .name {
-            font-size: 16px;
-            color: #101010;
-            font-weight: bold;
-            margin-bottom: 16px;
-            text-overflow: ellipsis;
-            white-space: nowrap;
-            overflow: hidden;
-          }
-          .code,
-          .club-name {
-            position: relative;
-            font-size: 14px;
-            color: #404040;
-            line-height: 24px;
-            text-overflow: ellipsis;
-            white-space: nowrap;
-            overflow: hidden;
-
-            span {
-              color: #6d9eff;
-            }
-          }
-        }
-      }
-    }
-  }
-}
-
-// 移动 端
-@media screen and (max-width: 768px) {
-  .page-top {
-    height: 46vw;
-    @include themify($themes) {
-      background: themed('h5-banner-device');
-    }
-    background-size: auto 46vw;
-
-    .logo {
-      display: block;
-      width: 14.8vw;
-      height: 14.8vw;
-      border-radius: 50%;
-      background: #fff;
-    }
-    .name {
-      font-size: 4vw;
-      color: #fff;
-    }
-  }
-  .page-content {
-    position: relative;
-    .search {
-      position: absolute;
-      left: 50%;
-      top: 0;
-      transform: translate(-50%, -50%);
-    }
-
-    .title {
-      font-size: 3.4vw;
-      color: #404040;
-
-      span {
-        @include themify($themes) {
-          color: themed('color');
-        }
-      }
-    }
-
-    .list {
-      display: flex;
-      align-items: center;
-      flex-direction: column;
-
-      .section {
-        width: 93.6vw;
-        height: 26vw;
-        background-color: #f3f5f6;
-        border-radius: 4px;
-        box-sizing: border-box;
-        padding: 3.2vw;
-
-        .cover {
-          display: block;
-          width: 19.6vw;
-          height: 19.6vw;
-        }
-        .info {
-          position: relative;
-          margin-left: 3.2vw;
-          .name {
-            font-size: 4vw;
-            color: #101010;
-            font-weight: bold;
-            margin-bottom: 4vw;
-            text-overflow: ellipsis;
-            white-space: nowrap;
-            overflow: hidden;
-          }
-          .code,
-          .club-name {
-            width: 66vw;
-            position: relative;
-            font-size: 3vw;
-            color: #404040;
-            line-height: 5vw;
-            text-overflow: ellipsis;
-            white-space: nowrap;
-            overflow: hidden;
-
-            span {
-              color: #6d9eff;
-            }
-          }
-        }
-      }
-    }
-  }
-}
-</style>

+ 3 - 36
pages/_template/app/approve/personnel/operate/detail.vue

@@ -1,6 +1,6 @@
 <template>
   <div class="page md:flex md:justify-between">
-    <div class="page-title">{{ themeName === 'ross' ? '体疗师认证' : '医师认证' }}</div>
+    <div class="page-title">操作师认证</div>
     <div class="page-top">
       <div class="swiper">
         <SimpleSwiper :imageList="doctorInfo.bannerList"></SimpleSwiper>
@@ -47,43 +47,10 @@
 </template>
 
 <script>
-import { mapGetters } from 'vuex'
+import operatDoctorDetailMixin from '@/mixins/operatDoctorDetail'
 export default {
   layout: 'app',
-  data() {
-    return {
-      doctorId: '',
-      doctorInfo: {
-        tagList: [],
-        paramList: [],
-      },
-    }
-  },
-  computed:{
-    ...mapGetters(['themeName'])
-  },
-  mounted() {
-    this.initData()
-  },
-  methods: {
-    initData() {
-      this.doctorId = parseInt(this.$route.query.id)
-      this.fetchDetail()
-    },
-    async fetchDetail() {
-      try {
-        const res = await this.$http.api.fetchDoctorDetail({
-          doctorId: this.doctorId,
-        })
-        this.doctorInfo = { ...this.clubInfo, ...res.data }
-      } catch (error) {
-        console.log(error)
-      }
-      if (this.doctorInfo.bannerList.length <= 0) {
-        this.doctorInfo.bannerList.push('/placeholder.png')
-      }
-    },
-  },
+  mixins: [operatDoctorDetailMixin],
 }
 </script>
 

+ 7 - 69
pages/_template/app/approve/personnel/operate/index.vue

@@ -1,7 +1,7 @@
 <template>
   <div class="page">
     <van-list
-      v-model="isLoadingMore"
+      v-model="loadingMore"
       :finished="finished"
       :immediate-check="false"
       :finished-text="total ? '没有更多了' : ''"
@@ -10,7 +10,8 @@
       <div class="page-top flex flex-col justify-center items-center">
         <!-- <img class="logo" :src="supplierInfo.logo" /> -->
         <div class="mt-2 name">
-          <span v-text="supplierInfo.shopName"></span><span v-if="themeName === 'ross'">官方体疗师认证</span><span v-else>官方医师认证</span>
+          <span v-text="supplierInfo.shopName"></span>
+          <span>官方操作师认证</span>
         </div>
       </div>
       <div class="page-content">
@@ -19,14 +20,12 @@
           <SimpleSearch
             v-model="listQuery.doctorName"
             @search="onSearch"
-            :placeholder="searchPlaceholder"
+            placeholder="搜索操作师"
           />
         </div>
         <!-- 标题 -->
         <div class="title px-4 pt-12 pb-6">
-          共<span v-text="total"></span>位{{
-            themeName === 'ross' ? '体疗师' : '医师'
-          }}
+          共<span v-text="total"></span>位体操作师
         </div>
         <!-- 列表 -->
         <div class="list">
@@ -59,75 +58,14 @@
 </template>
 
 <script>
-import { mapGetters } from 'vuex'
-import { debounce } from '@/utils'
+import operatDoctorListMixin from '@/mixins/operatDoctorList'
 export default {
   layout: 'app',
-  data() {
-    return {
-      isLoadingMore: true,
-      finished: false,
-      isRequest: true,
-      listQuery: {
-        authUserId: '',
-        doctorType: 1,
-        doctorName: '',
-        pageNum: 1,
-        pageSize: 9,
-      },
-      list: [],
-      total: 0,
-    }
-  },
+  mixins: [operatDoctorListMixin],
   computed: {
-    ...mapGetters(['supplierInfo', 'authUserId', 'routePrefix', 'themeName']),
-    isEmpty() {
-      return this.list.length === 0
-    },
     emptyList() {
       return 3 - (this.list.length % 3)
     },
-
-    searchPlaceholder() {
-      return this.themeName === 'ross' ? '搜索体疗师' : '搜索医师'
-    },
-  },
-  mounted() {
-    this.fetchList()
-  },
-  methods: {
-    fetchList: debounce(async function () {
-      try {
-        this.isLoadingMore = true
-        this.listQuery.authUserId = this.authUserId
-        const res = await this.$http.api.fetchDoctorList(this.listQuery)
-        this.list = [...this.list, ...res.data.list]
-        this.finished = !res.data.hasNextPage
-        this.total = res.data.total
-        this.listQuery.pageNum += 1
-        this.isLoadingMore = false
-      } catch (error) {
-        console.log(error)
-      } finally {
-        this.isRequest = false
-      }
-    }, 400),
-
-    // 搜索
-    onSearch() {
-      this.list = []
-      this.listQuery.pageNum = 1
-      this.fetchList()
-    },
-    // 医师详情
-    toDetail(item) {
-      const url = `${this.routePrefix}/approve/personnel/operate/detail?id=${item.doctorId}`
-      this.$router.push(url)
-    },
-    // 加载更多
-    onLoadMore() {
-      this.fetchList()
-    },
   },
 }
 </script>

+ 3 - 35
pages/_template/app/feedback/index.vue

@@ -25,41 +25,10 @@
 </template>
 
 <script>
-import { mapGetters } from 'vuex'
+import feedbackMixin from '@/mixins/feedback'
 export default {
-  layout: 'app',
-  data() {
-    return {
-      content: '',
-      showModal: false,
-    }
-  },
-  computed: {
-    ...mapGetters(['supplierInfo', 'userInfo', 'routePrefix']),
-    isEmpty() {
-      return this.content.length === 0
-    },
-  },
-  methods: {
-    async onSubmit() {
-      const { clubUserId } = this.userInfo
-      if (this.isEmpty) {
-        this.$toast('留言不能为空')
-        return
-      }
-      try {
-        await this.$http.api.feedback({ clubUserId, content: this.content })
-        this.showModal = true
-        this.content = ''
-      } catch (error) {
-        console.log(error)
-      }
-    },
-    onConfirm() {
-      this.showModal = false
-      this.$router.push(this.routePrefix)
-    },
-  },
+  layout: 'app-ross',
+  mixins: [feedbackMixin],
 }
 </script>
 
@@ -249,7 +218,6 @@ export default {
         font-size: 3.2vw;
       }
 
-      
       .image-icon {
         width: 30vw;
         height: 20vw;

+ 2 - 2
pages/_template/app/form/club-bind.vue

@@ -2,8 +2,8 @@
   <div class="club-bind">
     <div class="form-container">
       <div class="logo">
-        <img src="~/assets/theme-images/ross/ross-logo.png" alt="" />
-        <!-- <img :src="supplierInfo.logo" :alt="supplierInfo.shopName" /> -->
+        <!-- <img src="~/assets/theme-images/ross/ross-logo.png" alt="" /> -->
+        <img :src="supplierInfo.logo" :alt="supplierInfo.shopName" />
       </div>
       <div class="tip">输入验证码即可完成账号注册及ROSS授权牌匾制作与寄送</div>
       <div class="form">

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

@@ -257,7 +257,7 @@ export default {
   methods: {
     // sn码输入框是去焦点
     async onSnCodeBlur(formItem) {
-      if (!formItem.authType === 1) return
+      if (formItem.authType === 1) return
       if (!formItem.snCode) return
       try {
         const res = await this.$http.api.fetchDetialBySnCode({

+ 307 - 9
pages/_template/app/index.vue

@@ -1,22 +1,320 @@
 <template>
-  <div>
-    <RossHomePage v-if="themeName === 'ross'"></RossHomePage>
-    <NormalHomePage v-else></NormalHomePage>
+  <div class="page">
+    <div class="page-top flex flex-col justify-center items-center">
+      <!-- <img class="logo" :src="supplierInfo.logo" /> -->
+      <div class="name mt-2" v-text="supplierInfo.shopName"></div>
+    </div>
+    <div class="page-content">
+      <keep-alive>
+        <div class="list">
+          <div
+            class="section flex flex-col justify-center items-center mt-6 mb-6"
+            v-for="item in list"
+            :key="item.id"
+            @click="toDetail(item)"
+            @mouseover="onMouseover(item)"
+            @mouseleave="onMouselevel(item)"
+          >
+            <div class="icon-image" :class="'item' + item.id"></div>
+            <div class="name mt-4" v-text="item.name"></div>
+          </div>
+        </div>
+      </keep-alive>
+    </div>
   </div>
 </template>
 
 <script>
-import NormalHomePage from '@/views/NormalHomePage.vue'
-import RossHomePage from '@/views/RossHomePage.vue'
 import { mapGetters } from 'vuex'
+import { toAuthorization } from '~/utils'
+import { isWeChat } from '~/utils/validator'
 export default {
   layout: 'app',
-  components: {
-    NormalHomePage,
-    RossHomePage,
+  data() {
+    return {
+      list: [],
+    }
   },
   computed: {
-    ...mapGetters(['themeName']),
+    ...mapGetters([
+      'supplierInfo',
+      'authUserId',
+      'appId',
+      'routePrefix',
+      'accountType',
+    ]),
+  },
+  created() {
+    this.initEntryItems()
+  },
+  beforeDestroy() {
+    this.$removeStorage(this.routePrefix, 'login_redicret')
+  },
+  methods: {
+    // 初始化入口图标
+    initEntryItems() {
+      this.list = [
+        {
+          id: 0,
+          name: '正品授权申请入口',
+          path: '/form/club-register',
+          active: false,
+        },
+        {
+          id: 1,
+          name: '正品授权',
+          path: '/approve',
+          active: false,
+        },
+        {
+          id: 2,
+          name: '资料库',
+          path: '/database/article',
+          active: false,
+        },
+        {
+          id: 3,
+          name: '意见反馈',
+          path: '/feedback',
+          active: false,
+        },
+      ]
+    },
+
+    toDetail(item) {
+      const hasLogin = this.$store.getters.accessToken
+      // 保存登录重定向路由
+      this.$setStorage(
+        this.routePrefix,
+        'login_redicret',
+        this.routePrefix + item.path
+      )
+      if (item.id > 1 && !hasLogin) {
+        // 在微信浏览器中使用微信授权登录
+        if (isWeChat() && this.appId && this.accountType === 2) {
+          const payload = {
+            authUserId: this.authUserId,
+            routePrefix: this.routePrefix,
+          }
+          return toAuthorization(this.appId, payload)
+        }
+        this.$toast({ message: '请先登录', duration: 1000 })
+        this.$store.commit('app/SHOW_LOGIN')
+        return
+      }
+
+      if (item.id === 0) {
+        const url = this.routePrefix + item.path
+        this.$router.push(url)
+      } else {
+        const url = this.routePrefix + item.path
+        this.$router.push(url)
+      }
+    },
+    onMouseover(item) {
+      const isPc = this.$store.getters.isPc
+      if (!isPc) return
+      item.active = true
+    },
+    onMouselevel(item) {
+      const isPc = this.$store.getters.isPc
+      if (!isPc) return
+      item.active = false
+    },
   },
 }
 </script>
+
+<style scoped lang="scss">
+// pc 端
+@media screen and (min-width: 768px) {
+  .page-top {
+    height: 360px;
+    @include themify($themes) {
+      background: themed('pc-banner-home');
+      background-size: auto 360px;
+    }
+    .logo {
+      display: block;
+      width: 120px;
+      height: 120px;
+      border-radius: 50%;
+      background: #fff;
+    }
+    .name {
+      font-size: 30px;
+      color: #fff;
+    }
+  }
+
+  .page-content {
+    width: 1200px;
+    margin: 0 auto;
+    overflow: hidden;
+
+    .list {
+      display: flex;
+      justify-content: space-between;
+      align-items: center;
+    }
+
+    .section {
+      width: 284px;
+      height: 260px;
+      margin-left: auto;
+      margin-right: auto;
+      background-color: #fff;
+      transition: all 0.4s;
+      cursor: pointer;
+      border-radius: 4px;
+
+      .icon-image {
+        width: 86px;
+        height: 86px;
+        background-size: 86px 86px;
+        background-repeat: no-repeat;
+        background-position: center;
+
+        &.item0 {
+          @include themify($themes) {
+            background-image: themed('pc-icon-home-edit');
+          }
+        }
+
+        &.item1 {
+          @include themify($themes) {
+            background-image: themed('pc-icon-home-approve');
+          }
+        }
+
+        &.item2 {
+          @include themify($themes) {
+            background-image: themed('pc-icon-home-doc');
+          }
+        }
+
+        &.item3 {
+          @include themify($themes) {
+            background-image: themed('pc-icon-home-feedback');
+          }
+        }
+      }
+
+      &:hover {
+        transform: translateY(-10px);
+
+        @include themify($themes) {
+          background-color: themed('color');
+        }
+
+        .icon-image {
+          &.item0 {
+            background-image: url(~assets/theme-images/common/pc-icon-edit-active.png) !important;
+          }
+
+          &.item1 {
+            background-image: url(~assets/theme-images/common/pc-icon-approve-active.png) !important;
+          }
+
+          &.item2 {
+            background-image: url(~assets/theme-images/common/pc-icon-doc-active.png) !important;
+          }
+
+          &.item3 {
+            background-image: url(~assets/theme-images/common/pc-icon-feedback-active.png) !important;
+          }
+        }
+
+        .name {
+          color: #fff;
+        }
+      }
+
+      .icon {
+        width: 86px;
+        height: 86px;
+      }
+
+      .name {
+        font-size: 24px;
+        color: #404040;
+      }
+    }
+  }
+}
+
+// 移动 端
+@media screen and (max-width: 768px) {
+  .page-top {
+    height: 46vw;
+    @include themify($themes) {
+      background: themed('h5-banner-home');
+      background-size: auto 46vw;
+    }
+    .logo {
+      display: block;
+      width: 14.8vw;
+      height: 14.8vw;
+      border-radius: 50%;
+      background: #fff;
+    }
+    .name {
+      font-size: 4vw;
+      color: #fff;
+    }
+  }
+
+  .page-content {
+    .section {
+      width: 85.6vw;
+      height: 58vw;
+      margin-left: auto;
+      margin-right: auto;
+      box-shadow: 0px 0.4vw 2vw rgba(0, 6, 32, 0.08);
+      border-radius: 4px;
+
+      .icon {
+        width: 20vw;
+        height: 20vw;
+      }
+
+      .name {
+        font-size: 4.8vw;
+        color: #404040;
+      }
+
+      .icon-image {
+        width: 86px;
+        height: 86px;
+        background-size: 86px 86px;
+        background-repeat: no-repeat;
+        background-position: center;
+
+        &.item0 {
+          @include themify($themes) {
+            background-image: themed('h5-icon-home-edit');
+          }
+        }
+
+        &.item1 {
+          @include themify($themes) {
+            background-image: themed('h5-icon-home-approve');
+          }
+        }
+
+        &.item2 {
+          @include themify($themes) {
+            background-image: themed('h5-icon-home-doc');
+          }
+        }
+
+        &.item3 {
+          @include themify($themes) {
+            background-image: themed('h5-icon-home-feedback');
+          }
+        }
+      }
+    }
+  }
+}
+</style>

+ 2 - 93
pages/_template/ross/approve/club/detail.vue

@@ -69,101 +69,10 @@
 </template>
 
 <script>
-import { drawLogo } from '@/utils'
-import { convertor, mapNavigate } from '@/utils/map-utils'
-import { callMobile } from '@/utils'
-import { mapGetters } from 'vuex'
+import clubDetailMixin from '@/mixins/clubDetail'
 export default {
   layout: 'app-ross',
-  filters: {
-    formatEmpty(val) {
-      return val || '暂无'
-    },
-    formatSnCode(code) {
-      if (!code) return ''
-      return code.replace(/^(\w{2})\w+(\w{4})$/, '$1******$2')
-    },
-  },
-  data() {
-    return {
-      authId: '',
-      clubInfo: {},
-    }
-  },
-  computed: {
-    ...mapGetters(['routePrefix']),
-    address() {
-      let resultAddress = this.clubInfo.area
-        ? this.clubInfo.area + this.clubInfo.address
-        : this.clubInfo.address
-      return resultAddress || '暂无'
-    },
-    isEmpty() {
-      return this.clubInfo.productList
-        ? this.clubInfo.productList.length === 0
-        : true
-    },
-  },
-  mounted() {
-    this.initData()
-  },
-  beforeDestroy() {
-    this.$removeStorage(this.routePrefix, 'clubInfo')
-  },
-  methods: {
-    callMobile,
-    // 设备详情
-    toDetail(item) {
-      // window.location.href = `${process.env.CIMEI_LOCAL}/product/auth/product-${item.productId}.html`
-      this.$router.push(
-        `${this.routePrefix}/approve/device/detail?id=${item.productId}`
-      )
-    },
-    // 初始化
-    initData() {
-      this.authId = parseInt(this.$route.query.id)
-      const clubInfo = this.$getStorage(this.routePrefix, 'clubInfo')
-      if (clubInfo) {
-        this.clubInfo = clubInfo
-      }
-      this.fetchDetail()
-    },
-    // 获取机构详细信息
-    async fetchDetail() {
-      try {
-        const res = await this.$http.api.getAuthClubDetail({
-          authId: this.authId,
-        })
-        this.clubInfo = { ...this.clubInfo, ...res.data } // 合并
-      } catch (error) {
-        console.log(error)
-      }
-      // 默认轮播图
-      if (this.clubInfo.bannerList.length <= 0) {
-        this.clubInfo.bannerList.push('/placeholder.png')
-      }
-      // 默认logo
-      if (!this.clubInfo.logo) {
-        this.clubInfo.logo = drawLogo(this.clubInfo.authParty)
-      }
-    },
-    // 地图导航
-    onMapNav() {
-      this.$refs.mapNav.open()
-    },
-    // 导航
-    navigation(type) {
-      const point = this.clubInfo.lngAndLat.split(',')
-      let lng = point[0]
-      let lat = point[1]
-
-      mapNavigate(
-        { lat, lng, title: this.clubInfo.authParty, address: this.address },
-        type
-      )
-      this.$refs.mapNav.close()
-    },
-  },
+  mixins: [clubDetailMixin],
 }
 </script>
 

+ 2 - 63
pages/_template/ross/approve/club/star-list.vue

@@ -30,71 +30,10 @@
 </template>
 
 <script>
-import { mapGetters } from 'vuex'
-import { drawLogo, debounce } from '@/utils'
+import clubStarListMixin from '@/mixins/clubStarList'
 export default {
   layout: 'app-ross',
-  data() {
-    return {
-      finished: false,
-      isRequest: true,
-      list: [],
-    }
-  },
-  computed: {
-    ...mapGetters(['authUserId', 'routePrefix']),
-    total() {
-      return this.list.length
-    },
-  },
-  mounted() {
-    this.initData()
-  },
-  methods: {
-    // 绘制logo的方法
-    drawLogo,
-    // 查看详情
-    toDetail(item) {
-      const url = `${this.routePrefix}/approve/club/detail?id=${item.authId}`
-      this.$router.push(url)
-    },
-
-    // 初始化页面数据
-    async initData() {
-      // 获取机构列表
-      this.fetchList()
-    },
-
-    fetchList: debounce(async function () {
-      try {
-        // 自定义加载图标
-        this.$toast.loading({
-          message: '正在获取明星机构...',
-          duration: 0,
-        })
-        const res = await this.$http.api.getAuthClubStarList({
-          authUserId: this.authUserId,
-        })
-        this.list = res.data
-      } catch (error) {
-        console.log(error)
-      } finally {
-        this.$toast.clear()
-      }
-    }, 400),
-
-    // 格式化地址
-    formatAddress(a1, a2) {
-      let resutl = ''
-      if (typeof a1 === 'string') {
-        resutl += a1
-      }
-      if (typeof a2 === 'string') {
-        resutl += a2
-      }
-      return resutl || '暂无'
-    },
-  },
+  mixins: [clubStarListMixin],
 }
 </script>
 

+ 37 - 94
pages/_template/ross/approve/device/detail.vue

@@ -11,14 +11,17 @@
           @click="onShowAuthCard"
           v-if="!showAuthCard"
         />
-        <img class="auth-logo" :src="authLogoImage" />
+        <img class="auth-logo" :src="brandLogoImage" />
       </div>
       <div class="device-info">
         <div class="logo">
-          <div class="logo-swiper">
-            <SimpleSwiper :imageList="[productInfo.clubLogo]" :pagination="false"></SimpleSwiper>
+          <div class="logo-swiper" v-if="clubLogo.length > 1">
+            <SimpleSwiper
+              :imageList="clubLogo"
+              :pagination="false"
+            ></SimpleSwiper>
           </div>
-          <!-- <img :src="productInfo.clubLogo" alt="logo" /> -->
+          <img :src="clubLogo[0]" alt="logo" v-else />
         </div>
         <div class="section">
           <div class="name" v-text="productInfo.productName"></div>
@@ -26,31 +29,28 @@
             SN码:<span>{{ productInfo.snCode | snCodeRender }}</span>
           </div>
           <div class="row">
-            <!-- <span>产地:{{ productInfo.productionPlace }}</span> -->
-            <!-- <span>品牌:{{ productInfo.brandName }}</span><i></i><span>产地:西班牙巴塞罗那</span> -->
-            <span>品牌:RÖS'S</span><i></i><span>产地:西班牙巴塞罗那</span>
+            <!-- <span>产地:{{ productInfo.producePlace }}</span> -->
+            <span>品牌:{{ productInfo.brandName }}</span
+            ><i></i><span>产地:{{ productInfo.producePlace }}</span>
+            <!-- <span>品牌:RÖS'S</span><i></i><span>产地:西班牙巴塞罗那</span> -->
           </div>
           <div class="sncode pc">
             SN码:<span>{{ productInfo.snCode | snCodeRender }}</span>
           </div>
           <div class="maker">制造商:{{ productInfo.manufacturer }}</div>
-          <!-- <div class="supplier">代理商:{{ productInfo.shopName }}</div> -->
-          <div class="supplier">代理商:ACEBELLE生物科技(深圳)有限公司</div>
+          <div class="supplier">供应商:{{ productInfo.shopName }}</div>
+          <!-- <div class="supplier">供应商:ACEBELLE生物科技(深圳)有限公司</div> -->
         </div>
-        <div class="auth">
+        <div class="auth" v-if="productInfo">
           <div class="auth-icon"></div>
-          <div class="auth-info">
-            <span class="font-bold">ROS'S</span>
-            <span>授予</span>
-            <span>{{ productInfo.authParty }}</span>
-            <span>正品拥有</span>
-          </div>
-          <div class="auth-info">
-            <span class="font-bold">ROS'S</span>
-            <span>授予</span>
-            <span>{{ productInfo.authParty }}</span>
-            <span>正品拥有</span>
-          </div>
+          <template v-for="item in productInfo.clubList">
+            <div class="auth-info" :key="item.authId">
+              <span class="font-bold">ROS'S</span>
+              <span>授予</span>
+              <span>{{ item.authParty }}</span>
+              <span>正品拥有</span>
+            </div>
+          </template>
         </div>
       </div>
     </div>
@@ -87,74 +87,15 @@
     </transition>
   </div>
 </template>
+
 <script>
-import { mapGetters } from 'vuex'
-import { chunk } from 'lodash'
+import deviceDetailMixin from '@/mixins/deviceDetail'
 export default {
-  layout: 'app-ross',
-  filters: {
-    snCodeRender(code) {
-      return code.replace(/^(\w{2})\w+(\w{4})$/, '$1******$2')
-    },
-  },
-  data() {
-    return {
-      showAuthCard: false,
-      bannerList: [],
-      productInfo: null,
-    }
-  },
-  watch: {
-    isPc() {
-      this.$nextTick(() => {
-        window.location.reload()
-      })
-    },
-  },
-  computed: {
-    ...mapGetters(['isPc']),
-    // 参数列表
-    paramListRender() {
-      if (!this.productInfo) return []
-      return this.isPc
-        ? chunk(this.productInfo.paramList, 2)
-        : chunk(this.productInfo.paramList, 1)
-    },
-    // 机构logo
-    authLogoImage() {
-      if (!this.productInfo) return ''
-      return this.productInfo.authLogo
-    },
-    // 授权牌
-    authCardImage() {
-      if (!this.productInfo) return ''
-      return this.productInfo.appletsCertificateImage
-    },
-  },
-  created() {
-    this.fetchProductDetails()
-  },
-  methods: {
-    onShowAuthCard() {
-      this.showAuthCard = true
-    },
-    onHideAuthCard() {
-      this.showAuthCard = false
-    },
-    async fetchProductDetails() {
-      const productId = this.$route.query.id
-      try {
-        const { data } = await this.$http.api.fetchProductDetails({
-          productId: productId,
-        })
-        this.productInfo = data
-      } catch (error) {
-        console.log(error)
-      }
-    },
-  },
+  layout: 'app',
+  mixins: [deviceDetailMixin],
 }
 </script>
+
 <style lang="scss" scoped>
 ::v-deep {
   .swiper-pagination-bullet {
@@ -242,7 +183,7 @@ export default {
           width: 100%;
           height: 100%;
         }
-        
+
         .auth-seal {
           position: absolute;
           width: 70px;
@@ -290,22 +231,23 @@ export default {
           border-radius: 50%;
           // background: #d8d8d8;
           border: 1px solid #d8d8d8;
-          box-sizing: border-box;
+          // box-sizing: border-box;
           position: absolute;
           right: 0;
           top: 0;
 
-          .logo-swiper{
-            width: 114px;
-            height: 114px;
+          .logo-swiper {
+            width: 112px;
+            height: 112px;
             overflow: hidden;
             border-radius: 50%;
           }
 
           ::v-deep {
             img {
-              width: 114px;
-              height: 114px;
+              width: 112px;
+              height: 112px;
+              border-radius: 50%;
             }
           }
 
@@ -578,7 +520,7 @@ export default {
           right: 4vw;
           top: 5.8vw;
 
-          .logo-swiper{
+          .logo-swiper {
             width: 18vw;
             height: 18vw;
             overflow: hidden;
@@ -589,6 +531,7 @@ export default {
             img {
               width: 18vw;
               height: 18vw;
+              border-radius: 50%;
             }
           }
 

+ 9 - 119
pages/_template/ross/approve/device/index.vue

@@ -1,14 +1,14 @@
 <template>
   <div class="page">
     <van-list
-      v-model="isLoadingMore"
+      v-model="loadingMore"
       :finished="finished"
       :immediate-check="false"
       :finished-text="total ? '没有更多了' : ''"
       @load="onLoadMore"
     >
       <div class="page-top flex flex-col justify-center items-center">
-        <img class="logo" :src="supplierInfo.logo" />
+        <!-- <img class="logo" :src="supplierInfo.logo" /> -->
         <div class="mt-2 name">
           <span v-text="supplierInfo.shopName"></span><span>官方授权设备</span>
         </div>
@@ -80,8 +80,10 @@
                   </template>
                   <div class="club-name" slot="reference">
                     所属机构:
-                    <template v-for="club in item.clubList">
-                      ,<span :key="club.authId">{{ club.authParty }}</span>
+                    <template v-for="(club, index) in item.clubList">
+                      <span :key="club.authId"
+                        ><i v-if="index !== 0">,</i>{{ club.authParty }}</span
+                      >
                     </template>
                   </div>
                 </el-popover>
@@ -101,123 +103,10 @@
 </template>
 
 <script>
-import { debounce } from '@/utils'
-import { mapGetters } from 'vuex'
+import deviceListMixin from '@/mixins/deviceList'
 export default {
   layout: 'app-ross',
-  filters: {
-    formatSnCode(code) {
-      if (!code) return ''
-      return code.replace(/^(\w{2})\w+(\w{4})$/, '$1******$2')
-    },
-  },
-  data() {
-    return {
-      isLoadingMore: false,
-      finished: false,
-      isRequest: true,
-      searchQuery: {
-        type: 2,
-        keyword: '测试机构A',
-      },
-      listQuery: {
-        listType: 1,
-        productTypeId: '',
-        snCode: '',
-        authParty: '测试机构A',
-        pageNum: 1,
-        pageSize: 10,
-      },
-      list: [],
-      total: 0,
-      productSelectList: [],
-      searchFlag: false,
-    }
-  },
-  computed: {
-    ...mapGetters(['routePrefix', 'supplierInfo', 'authUserId']),
-  },
-  mounted() {
-    this.fetchProductSelectList()
-  },
-  methods: {
-    // 获取设备列表
-    fetchList: debounce(async function () {
-      try {
-        this.isLoadingMore = true
-        const res = await this.$http.api.getAuthProductList(this.listQuery)
-        this.list = [...this.list, ...res.data.list]
-        this.finished = !res.data.hasNextPage
-        this.total = res.data.total
-        this.isLoadingMore = false
-        this.listQuery.pageNum += 1
-        this.searchFlag = true
-      } catch (error) {
-        console.log(error)
-      } finally {
-        this.isRequest = false
-        this.searchFlag = true
-      }
-    }, 400),
-
-    // 搜索
-    onSearch() {
-      const { productTypeId } = this.listQuery
-      const { type, keyword } = this.searchQuery
-      if (!productTypeId) {
-        return this.$toast('请选择设备分类')
-      }
-      if (!type) {
-        return this.$toast('请选择搜索条件')
-      }
-      if (!keyword) {
-        return this.$toast('搜索内容不能为空')
-      }
-
-      if (type === 1) {
-        this.listQuery.snCode = keyword
-        this.listQuery.authParty = ''
-      } else {
-        this.listQuery.snCode = ''
-        this.listQuery.authParty = keyword
-      }
-
-      this.list = []
-      this.listQuery.pageNum = 1
-      this.fetchList()
-    },
-
-    // 获取设备种类
-    async fetchProductSelectList() {
-      try {
-        const res = await this.$http.api.fetchProductSelectList({
-          authUserId: this.authUserId,
-        })
-        this.productSelectList = res.data
-      } catch (error) {
-        console.log(error)
-      }
-    },
-
-    // 设备详情
-    toDetail(item) {
-      // window.location.href = `${process.env.CIMEI_LOCAL}/product/auth/product-${item.productId}.html`
-      this.$router.push(
-        `${this.routePrefix}/approve/device/detail?id=${item.productId}`
-      )
-    },
-
-    // 机构详情
-    toClubDetail(item) {
-      const url = `${this.routePrefix}/approve/club/detail?id=${item.authId}`
-      this.$router.push(url)
-    },
-
-    // 加载更多
-    onLoadMore() {
-      this.fetchList()
-    },
-  },
+  mixins: [deviceListMixin],
 }
 </script>
 
@@ -452,6 +341,7 @@ export default {
           height: 19.6vw;
         }
         .info {
+          width: 64vw;
           position: relative;
           margin-left: 3.2vw;
           .name {

+ 0 - 273
pages/_template/ross/approve/device/index1.vue

@@ -1,273 +0,0 @@
-<template>
-  <div class="page">
-    <van-list
-      v-model="isLoadingMore"
-      :finished="finished"
-      :immediate-check="false"
-      :finished-text="total ? '没有更多了' : ''"
-      @load="onLoadMore"
-    >
-      <div class="page-top flex flex-col justify-center items-center">
-        <img class="logo" :src="supplierInfo.logo" />
-        <div class="mt-2 name">
-          <span v-text="supplierInfo.shopName"></span><span>官方授权设备</span>
-        </div>
-      </div>
-      <div class="page-content">
-        <!-- 标题 -->
-        <div class="title px-4 pt-12 pb-6">
-          共<span v-text="total"></span>种设备
-        </div>
-        <!-- 列表 -->
-        <div class="list">
-          <div
-            class="section flex items-center mb-4"
-            v-for="item in list"
-            :key="item.productTypeId"
-            @click="toDetail(item)"
-          >
-            <img class="cover" :src="item.image" />
-            <div class="name" v-text="item.name"></div>
-          </div>
-        </div>
-
-        <!-- 列表为空 -->
-        <SimpleEmpty
-          v-if="!total && !isRequest"
-          name="icon-empty-device.png"
-          description="敬请期待~"
-        ></SimpleEmpty>
-      </div>
-    </van-list>
-  </div>
-</template>
-
-<script>
-import { mapGetters } from 'vuex'
-import { debounce } from '@/utils'
-export default {
-  layout: 'app-ross',
-  data() {
-    return {
-      isLoadingMore: true,
-      finished: false,
-      isRequest: true,
-      listQuery: {
-        authUserId: '',
-        name: '',
-        pageNum: 1,
-        pageSize: 9,
-      },
-      list: [],
-      total: 0,
-    }
-  },
-  computed: {
-    ...mapGetters(['supplierInfo', 'authUserId', 'routePrefix']),
-  },
-  mounted() {
-    this.fetchList()
-  },
-  methods: {
-    // 获取设备分类
-    fetchList: debounce(async function () {
-      try {
-        this.isLoadingMore = true
-        this.listQuery.authUserId = this.authUserId
-        const res = await this.$http.api.getAuthProductCateList(this.listQuery)
-        this.list = [...this.list, ...res.data.list]
-        this.finished = !res.data.hasNextPage
-        this.total = res.data.total
-        this.isLoadingMore = false
-        this.listQuery.pageNum += 1
-      } catch (error) {
-        console.log(error)
-      } finally {
-        this.isRequest = false
-      }
-    }, 400),
-
-    // 设备详情
-    toDetail(item) {
-      const url = `${this.routePrefix}/approve/device/search?id=${item.productTypeId}`
-      this.$router.push(url)
-    },
-    // 搜索
-    onSearch() {
-      this.listQuery.pageNum = 1
-      this.list = []
-      this.fetchList()
-    },
-    // 加载更多
-    onLoadMore() {
-      this.fetchList()
-    },
-  },
-}
-</script>
-
-<style scoped lang="scss">
-// pc 端
-@media screen and (min-width: 768px) {
-  .page {
-    position: relative;
-    min-height: calc(100vh - 80px - 80px);
-    background-color: #fff;
-  }
-  .page-top {
-    height: 420px;
-    @include themify($themes) {
-      background: themed('pc-banner-device');
-      background-size: auto 420px;
-    }
-
-    .logo {
-      display: block;
-      width: 120px;
-      height: 120px;
-      border-radius: 50%;
-      background: #fff;
-    }
-    .name {
-      font-size: 30px;
-      color: #fff;
-    }
-  }
-  .page-content {
-    width: 1000px;
-    margin: 0 auto;
-
-    .search {
-      position: absolute;
-      left: 50%;
-      top: 300px;
-      transform: translateX(-50%);
-    }
-
-    .title {
-      font-size: 16px;
-      color: #404040;
-
-      span {
-        @include themify($themes) {
-          color: themed('color');
-        }
-      }
-    }
-
-    .list {
-      display: flex;
-      align-items: center;
-      flex-wrap: wrap;
-      justify-content: space-between;
-
-      .empty {
-        width: 390px;
-      }
-
-      .section {
-        width: 490px;
-        height: 136px;
-        background-color: #f3f5f6;
-        border-radius: 4px;
-        box-sizing: border-box;
-        padding: 16px;
-        cursor: pointer;
-        transition: all 0.4s;
-
-        &:hover {
-          box-shadow: 0 0 24px rgba(0, 0, 0, 0.2);
-        }
-
-        .cover {
-          display: block;
-          width: 104px;
-          height: 104px;
-        }
-        .name {
-          width: 260px;
-          font-size: 18px;
-          color: #101010;
-          text-overflow: ellipsis;
-          white-space: nowrap;
-          overflow: hidden;
-          margin-left: 16px;
-          font-weight: bold;
-        }
-      }
-    }
-  }
-}
-
-// 移动 端
-@media screen and (max-width: 768px) {
-  .page-top {
-    height: 46vw;
-    @include themify($themes) {
-      background: themed('h5-banner-device');
-      background-size: auto 46vw;
-    }
-    
-    .logo {
-      display: block;
-      width: 14.8vw;
-      height: 14.8vw;
-      border-radius: 50%;
-      background: #fff;
-    }
-    .name {
-      font-size: 4vw;
-      color: #fff;
-    }
-  }
-  .page-content {
-    position: relative;
-    .search {
-      position: absolute;
-      left: 50%;
-      top: 0;
-      transform: translate(-50%, -50%);
-    }
-
-    .title {
-      font-size: 3.4vw;
-      color: #404040;
-
-      span {
-        @include themify($themes) {
-          color: themed('color');
-        }
-      }
-    }
-
-    .list {
-      display: flex;
-      align-items: center;
-      flex-direction: column;
-      .section {
-        width: 93.6vw;
-        height: 26vw;
-        background-color: #f3f5f6;
-        border-radius: 4px;
-        box-sizing: border-box;
-        padding: 3.2vw;
-
-        .cover {
-          display: block;
-          width: 19.6vw;
-          height: 19.6vw;
-        }
-        .name {
-          width: 66vw;
-          font-size: 4vw;
-          color: #101010;
-          text-overflow: ellipsis;
-          white-space: nowrap;
-          overflow: hidden;
-          margin-left: 3.2vw;
-        }
-      }
-    }
-  }
-}
-</style>

+ 0 - 327
pages/_template/ross/approve/device/list.vue

@@ -1,327 +0,0 @@
-<template>
-  <div class="page">
-    <van-list
-      v-model="isLoadingMore"
-      :finished="finished"
-      :immediate-check="false"
-      :finished-text="total ? '没有更多了' : ''"
-      @load="onLoadMore"
-    >
-      <div class="page-top flex flex-col justify-center items-center">
-        <img class="logo" :src="supplierInfo.logo" />
-        <div class="mt-2 name">
-          <span v-text="supplierInfo.shopName"></span><span>官方授权设备</span>
-        </div>
-      </div>
-      <div class="page-content">
-        <!-- 标题 -->
-        <div class="title px-4 pt-12 pb-6 md:px-0">
-          共<span v-text="total"></span>台设备
-        </div>
-        <!-- 列表 -->
-        <div class="list">
-          <div
-            class="section flex justify-between mb-4"
-            v-for="item in list"
-            :key="item.productId"
-            @click="toDetail(item)"
-          >
-            <img class="cover" :src="item.productImage" />
-            <div class="info">
-              <div class="name" v-text="item.productName"></div>
-              <div class="code">SN码:{{ item.snCode | formatSnCode }}</div>
-              <div class="club-name">
-                所属机构:<span @click.stop="toClubDetail(item)">{{
-                  item.clubName
-                }}</span>
-              </div>
-            </div>
-          </div>
-        </div>
-
-        <!-- 列表为空 -->
-        <SimpleEmpty
-          v-if="!total && !isRequest"
-          name="icon-empty-device.png"
-          description="敬请期待~"
-        ></SimpleEmpty>
-      </div>
-    </van-list>
-  </div>
-</template>
-
-<script>
-import { debounce } from '@/utils'
-import { mapGetters } from 'vuex'
-export default {
-  layout: 'app-ross',
-  filters: {
-    formatSnCode(code) {
-      if (!code) return ''
-      return code.replace(/^(\w{2})\w+(\w{4})$/, '$1******$2')
-    },
-  },
-  data() {
-    return {
-      isLoadingMore: true,
-      finished: false,
-      isRequest: true,
-      listQuery: {
-        productTypeId: '',
-        snCode: '',
-        authParty: '',
-        pageNum: 1,
-        pageSize: 10,
-      },
-      list: [],
-      total: 0,
-    }
-  },
-  computed: {
-    ...mapGetters(['routePrefix', 'supplierInfo']),
-  },
-  mounted() {
-    this.initData()
-  },
-  methods: {
-    initData() {
-      const { id, keyword, type } = this.$route.query
-      this.listQuery.productTypeId = parseInt(id)
-      if (type === '1') {
-        this.listQuery.snCode = decodeURIComponent(keyword)
-      } else {
-        this.listQuery.authParty = decodeURIComponent(keyword)
-      }
-      this.fetchList()
-    },
-    fetchList: debounce(async function () {
-      try {
-        this.isLoadingMore = true
-        const res = await this.$http.api.getAuthProductList(this.listQuery)
-        this.list = [...this.list, ...res.data.list]
-        this.finished = !res.data.hasNextPage
-        this.total = res.data.total
-        this.isLoadingMore = false
-        this.listQuery.pageNum += 1
-      } catch (error) {
-        console.log(error)
-      } finally {
-        this.isRequest = false
-      }
-    }, 400),
-    // 搜索
-    onSearch() {
-      this.list = []
-      this.listQuery.pageNum = 1
-      this.fetchList()
-    },
-    // 设备详情
-    toDetail(item) {
-      // window.location.href = `${process.env.CIMEI_LOCAL}/product/auth/product-${item.productId}.html`
-      this.$router.push(
-        `${this.routePrefix}/approve/device/detail?id=${item.productId}`
-      )
-    },
-    // 机构详情
-    toClubDetail(item) {
-      const url = `${this.routePrefix}/approve/club/detail?id=${item.authId}`
-      this.$router.push(url)
-    },
-    // 加载更多
-    onLoadMore() {
-      this.fetchList()
-    },
-  },
-}
-</script>
-
-<style scoped lang="scss">
-// pc 端
-@media screen and (min-width: 768px) {
-  .page {
-    position: relative;
-    min-height: calc(100vh - 80px - 80px);
-    background-color: #fff;
-  }
-  .page-top {
-    height: 420px;
-    @include themify($themes) {
-      background: themed('pc-banner-device');
-    }
-    background-size: auto 420px;
-
-    .logo {
-      display: block;
-      width: 120px;
-      height: 120px;
-      border-radius: 50%;
-      background: #fff;
-    }
-    .name {
-      font-size: 30px;
-      color: #fff;
-    }
-  }
-  .page-content {
-    width: 1000px;
-    margin: 0 auto;
-
-    .title {
-      font-size: 16px;
-      color: #404040;
-
-      span {
-        @include themify($themes) {
-          color: themed('color');
-        }
-      }
-    }
-
-    .list {
-      display: flex;
-      align-items: center;
-      flex-wrap: wrap;
-      justify-content: space-between;
-
-      .empty {
-        width: 390px;
-      }
-
-      .section {
-        width: 490px;
-        height: 136px;
-        background-color: #f3f5f6;
-        border-radius: 4px;
-        box-sizing: border-box;
-        padding: 16px;
-        cursor: pointer;
-        transition: all 0.4s;
-        &:hover {
-          box-shadow: 0 0 24px rgba(0, 0, 0, 0.2);
-        }
-
-        .cover {
-          display: block;
-          width: 104px;
-          height: 104px;
-        }
-        .info {
-          width: 336px;
-          position: relative;
-          .name {
-            font-size: 18px;
-            color: #101010;
-            font-weight: bold;
-            margin-bottom: 16px;
-            margin-top: 4px;
-            text-overflow: ellipsis;
-            white-space: nowrap;
-            overflow: hidden;
-          }
-          .code,
-          .club-name {
-            position: relative;
-            font-size: 14px;
-            color: #666;
-            line-height: 24px;
-            text-overflow: ellipsis;
-            white-space: nowrap;
-            overflow: hidden;
-            margin-top: 6px;
-
-            span {
-              color: #1890ff;
-            }
-          }
-        }
-      }
-    }
-  }
-}
-
-// 移动 端
-@media screen and (max-width: 768px) {
-  .page-top {
-    height: 46vw;
-    @include themify($themes) {
-      background: themed('h5-banner-device');
-      background-size: auto 46vw;
-    }
-
-    .logo {
-      display: block;
-      width: 14.8vw;
-      height: 14.8vw;
-      border-radius: 50%;
-      background: #fff;
-    }
-    .name {
-      font-size: 4vw;
-      color: #fff;
-    }
-  }
-  .page-content {
-    position: relative;
-
-    .title {
-      font-size: 3.4vw;
-      color: #404040;
-
-      span {
-        @include themify($themes) {
-          color: themed('color');
-        }
-      }
-    }
-
-    .list {
-      display: flex;
-      align-items: center;
-      flex-direction: column;
-
-      .section {
-        width: 93.6vw;
-        height: 26vw;
-        background-color: #f3f5f6;
-        border-radius: 4px;
-        box-sizing: border-box;
-        padding: 3.2vw;
-
-        .cover {
-          display: block;
-          width: 19.6vw;
-          height: 19.6vw;
-        }
-        .info {
-          position: relative;
-          margin-left: 3.2vw;
-          .name {
-            font-size: 4vw;
-            color: #101010;
-            font-weight: bold;
-            margin-bottom: 4vw;
-            text-overflow: ellipsis;
-            white-space: nowrap;
-            overflow: hidden;
-          }
-          .code,
-          .club-name {
-            width: 66vw;
-            position: relative;
-            font-size: 3vw;
-            color: #404040;
-            line-height: 5vw;
-            text-overflow: ellipsis;
-            white-space: nowrap;
-            overflow: hidden;
-
-            span {
-              color: #6d9eff;
-            }
-          }
-        }
-      }
-    }
-  }
-}
-</style>

+ 0 - 227
pages/_template/ross/approve/device/search.vue

@@ -1,227 +0,0 @@
-<template>
-  <div class="page">
-    <div class="page-top flex flex-col justify-center items-center">
-      <img class="logo" :src="supplierInfo.logo" />
-      <div class="mt-2 name">
-        <span v-text="supplierInfo.shopName"></span><span>官方授权设备</span>
-      </div>
-    </div>
-    <div class="page-content">
-      <div class="title">SN码或机构名称:</div>
-      <el-input
-        placeholder="请输入设备SN码或机构名称搜索查询"
-        v-model="keyword"
-        class="input-with-select"
-      >
-        <el-select
-          v-model="type"
-          slot="prepend"
-          placeholder="请选择"
-          class="select-with-input"
-        >
-          <el-option label="SN码" value="1"></el-option>
-          <el-option label="机构名" value="2"></el-option>
-        </el-select>
-        <i slot="prefix" class="el-input__icon el-icon-search"></i>
-      </el-input>
-      <div class="button flex justify-center items-center" @click="onSearch">
-        查询
-      </div>
-    </div>
-  </div>
-</template>
-
-<script>
-import { mapGetters } from 'vuex'
-export default {
-  layout: 'app-ross',
-  data() {
-    return {
-      keyword: '',
-      type: '1',
-    }
-  },
-  computed: {
-    ...mapGetters(['supplierInfo', 'authUserId', 'routePrefix']),
-  },
-  methods: {
-    onSearch() {
-      if (!this.keyword) {
-        this.$toast('请输入要查询的内容')
-        return
-      }
-      this.$router.push(
-        `${this.routePrefix}/approve/device/list?type=${
-          this.type
-        }&keyword=${encodeURIComponent(this.keyword)}&id=${
-          this.$route.query.id
-        }`
-      )
-    },
-  },
-}
-</script>
-
-<style scoped lang="scss">
-.el-input {
-  & > ::v-deep {
-    .el-input.is-active .el-input__inner,
-    .el-input__inner:focus {
-      @include themify($themes) {
-        border-color: themed('color');
-      }
-    }
-  }
-}
-
-.input-with-select {
-  ::v-deep {
-    .el-input-group__prepend {
-      background-color: #fff;
-    }
-  }
-}
-
-// pc 端
-@media screen and (min-width: 768px) {
-  .el-input {
-    ::v-deep {
-      .el-select .el-input {
-        width: 130px;
-      }
-    }
-  }
-
-  .page {
-    position: relative;
-    min-height: calc(100vh - 80px - 80px);
-    background-color: #fff;
-  }
-  .page-top {
-    height: 420px;
-    @include themify($themes) {
-      background: themed('pc-banner-device');
-    }
-    background-size: auto 420px;
-
-    .logo {
-      display: block;
-      width: 120px;
-      height: 120px;
-      border-radius: 50%;
-      background: #fff;
-    }
-    .name {
-      font-size: 30px;
-      color: #fff;
-    }
-  }
-  .page-content {
-    width: 518px;
-    margin: 0 auto;
-
-    .title {
-      font-size: 18px;
-      line-height: 24px;
-      color: #282828;
-      margin: 68px 0 12px;
-    }
-
-    .el-input {
-      height: 46px;
-      font-size: 16px;
-      .el-input__icon {
-        font-size: 24px;
-        line-height: 46px;
-        margin-left: 12px;
-      }
-
-      ::v-deep {
-        & > .el-input__inner {
-          height: 46px;
-          padding-left: 55px;
-          font-size: 18px;
-        }
-      }
-    }
-
-    .button {
-      width: 295px;
-      height: 50px;
-      margin: 0 auto;
-      border-radius: 4px;
-      margin-top: 90px;
-      cursor: pointer;
-      @include themify($themes) {
-        background-color: themed('color');
-        color: #fff;
-      }
-    }
-  }
-}
-
-// 移动 端
-@media screen and (max-width: 768px) {
-  .el-input {
-    ::v-deep {
-      .select-with-input .el-input {
-        width: 22vw;
-
-        .el-input__inner {
-          padding-left: 2.4vw;
-        }
-      }
-    }
-  }
-
-  .page-top {
-    height: 46vw;
-    @include themify($themes) {
-      background: themed('h5-banner-device');
-      background-size: auto 46vw;
-    }
-    .logo {
-      display: block;
-      width: 14.8vw;
-      height: 14.8vw;
-      border-radius: 50%;
-      background: #fff;
-    }
-    .name {
-      font-size: 4vw;
-      color: #fff;
-    }
-  }
-  .page-content {
-    position: relative;
-    padding: 0 4.2vw;
-
-    .title {
-      font-size: 3.4vw;
-      line-height: 4.5vw;
-      color: #666;
-      margin: 18vw 0 1.6vw;
-    }
-
-    .el-input {
-      font-size: 3.4vw;
-      .el-input__icon {
-        font-size: 5.4vw;
-      }
-    }
-
-    .button {
-      height: 12vw;
-      margin: 0 auto;
-      border-radius: 2px;
-      margin-top: 12.8vw;
-      font-size: 3.6vw;
-      cursor: pointer;
-      @include themify($themes) {
-        background-color: themed('color');
-        color: #fff;
-      }
-    }
-  }
-}
-</style>

+ 4 - 37
pages/_template/ross/approve/personnel/operate/detail.vue

@@ -1,6 +1,6 @@
 <template>
   <div class="page md:flex md:justify-between">
-    <div class="page-title">{{ themeName === 'ross' ? '体疗师认证' : '医师认证' }}</div>
+    <div class="page-title">体疗师认证</div>
     <div class="page-top">
       <div class="swiper">
         <SimpleSwiper :imageList="doctorInfo.bannerList"></SimpleSwiper>
@@ -47,43 +47,10 @@
 </template>
 
 <script>
-import { mapGetters } from 'vuex'
+import operatDoctorDetailMixin from '@/mixins/operatDoctorDetail'
 export default {
-  layout: 'app-ross',
-  data() {
-    return {
-      doctorId: '',
-      doctorInfo: {
-        tagList: [],
-        paramList: [],
-      },
-    }
-  },
-  computed:{
-    ...mapGetters(['themeName'])
-  },
-  mounted() {
-    this.initData()
-  },
-  methods: {
-    initData() {
-      this.doctorId = parseInt(this.$route.query.id)
-      this.fetchDetail()
-    },
-    async fetchDetail() {
-      try {
-        const res = await this.$http.api.fetchDoctorDetail({
-          doctorId: this.doctorId,
-        })
-        this.doctorInfo = { ...this.clubInfo, ...res.data }
-      } catch (error) {
-        console.log(error)
-      }
-      if (this.doctorInfo.bannerList.length <= 0) {
-        this.doctorInfo.bannerList.push('/placeholder.png')
-      }
-    },
-  },
+  layout: 'app',
+  mixins: [operatDoctorDetailMixin],
 }
 </script>
 

+ 14 - 70
pages/_template/ross/approve/personnel/operate/index.vue

@@ -1,16 +1,17 @@
 <template>
   <div class="page">
     <van-list
-      v-model="isLoadingMore"
+      v-model="loadingMore"
       :finished="finished"
       :immediate-check="false"
       :finished-text="total ? '没有更多了' : ''"
       @load="onLoadMore"
     >
       <div class="page-top flex flex-col justify-center items-center">
-        <img class="logo" :src="supplierInfo.logo" />
+        <!-- <img class="logo" :src="supplierInfo.logo" /> -->
         <div class="mt-2 name">
-          <span v-text="supplierInfo.shopName"></span><span v-if="themeName === 'ross'">官方体疗师认证</span><span v-else>官方医师认证</span>
+          <span v-text="supplierInfo.shopName"></span>
+          <span>官方体疗师认证</span>
         </div>
       </div>
       <div class="page-content">
@@ -19,14 +20,12 @@
           <SimpleSearch
             v-model="listQuery.doctorName"
             @search="onSearch"
-            :placeholder="searchPlaceholder"
+            placeholder="搜索体疗师"
           />
         </div>
         <!-- 标题 -->
         <div class="title px-4 pt-12 pb-6">
-          共<span v-text="total"></span>位{{
-            themeName === 'ross' ? '体疗师' : '医师'
-          }}
+          共<span v-text="total"></span>位体疗师
         </div>
         <!-- 列表 -->
         <div class="list">
@@ -40,8 +39,12 @@
             <div class="info">
               <div class="name" v-text="item.doctorName"></div>
               <div class="tag">{{ item.tagList.join(' | ') }}</div>
-              <div class="code">资格证编号:<span>{{ item.certificateNo }}</span></div>
-              <div class="club-name">所在机构:<span>{{ item.clubName }}</span></div>
+              <div class="code">
+                资格证编号:<span>{{ item.certificateNo }}</span>
+              </div>
+              <div class="club-name">
+                所在机构:<span>{{ item.clubName }}</span>
+              </div>
             </div>
           </div>
         </div>
@@ -58,69 +61,10 @@
 </template>
 
 <script>
-import { mapGetters } from 'vuex'
-import { debounce } from '@/utils'
+import operatDoctorListMixin from '@/mixins/operatDoctorList'
 export default {
   layout: 'app-ross',
-  data() {
-    return {
-      isLoadingMore: true,
-      finished: false,
-      isRequest: true,
-      listQuery: {
-        authUserId: '',
-        doctorType: 1,
-        doctorName: '',
-        pageNum: 1,
-        pageSize: 9,
-      },
-      list: [],
-      total: 0,
-    }
-  },
-  computed: {
-    ...mapGetters(['supplierInfo', 'authUserId', 'routePrefix', 'themeName']),
-    searchPlaceholder() {
-      return this.themeName === 'ross' ? '搜索体疗师' : '搜索医师'
-    },
-  },
-  mounted() {
-    this.fetchList()
-  },
-  methods: {
-    fetchList: debounce(async function () {
-      try {
-        this.isLoadingMore = true
-        this.listQuery.authUserId = this.authUserId
-        const res = await this.$http.api.fetchDoctorList(this.listQuery)
-        this.list = [...this.list, ...res.data.list]
-        this.finished = !res.data.hasNextPage
-        this.total = res.data.total
-        this.listQuery.pageNum += 1
-        this.isLoadingMore = false
-      } catch (error) {
-        console.log(error)
-      } finally {
-        this.isRequest = false
-      }
-    }, 400),
-
-    // 搜索
-    onSearch() {
-      this.list = []
-      this.listQuery.pageNum = 1
-      this.fetchList()
-    },
-    // 医师详情
-    toDetail(item) {
-      const url = `${this.routePrefix}/approve/personnel/operate/detail?id=${item.doctorId}`
-      this.$router.push(url)
-    },
-    // 加载更多
-    onLoadMore() {
-      this.fetchList()
-    },
-  },
+  mixins: [operatDoctorListMixin],
 }
 </script>
 

+ 1 - 1
pages/_template/ross/database/article.vue

@@ -8,7 +8,7 @@
       @load="onLoadMore"
     >
       <div class="page-top flex flex-col justify-center items-center">
-        <img class="logo" :src="supplierInfo.logo" />
+        <!-- <img class="logo" :src="supplierInfo.logo" /> -->
         <span
           class="name mt-2"
           v-text="supplierInfo.shopName + '资料库'"

+ 1 - 1
pages/_template/ross/database/file.vue

@@ -8,7 +8,7 @@
       @load="onLoadMore"
     >
       <div class="page-top flex flex-col justify-center items-center">
-        <img class="logo" :src="supplierInfo.logo" />
+        <!-- <img class="logo" :src="supplierInfo.logo" /> -->
         <span
           class="name mt-2"
           v-text="supplierInfo.shopName + '资料库'"

+ 1 - 1
pages/_template/ross/database/image.vue

@@ -8,7 +8,7 @@
       @load="onLoadMore"
     >
       <div class="page-top flex flex-col justify-center items-center">
-        <img class="logo" :src="supplierInfo.logo" />
+        <!-- <img class="logo" :src="supplierInfo.logo" /> -->
         <span
           class="name mt-2"
           v-text="supplierInfo.shopName + '资料库'"

+ 1 - 1
pages/_template/ross/database/package.vue

@@ -8,7 +8,7 @@
       @load="onLoadMore"
     >
       <div class="page-top flex flex-col justify-center items-center">
-        <img class="logo" :src="supplierInfo.logo" />
+        <!-- <img class="logo" :src="supplierInfo.logo" /> -->
         <span
           class="name mt-2"
           v-text="supplierInfo.shopName + '资料库'"

+ 1 - 1
pages/_template/ross/database/video.vue

@@ -8,7 +8,7 @@
       @load="onLoadMore"
     >
       <div class="page-top flex flex-col justify-center items-center">
-        <img class="logo" :src="supplierInfo.logo" />
+        <!-- <img class="logo" :src="supplierInfo.logo" /> -->
         <span
           class="name mt-2"
           v-text="supplierInfo.shopName + '资料库'"

+ 3 - 35
pages/_template/ross/feedback/index.vue

@@ -1,7 +1,7 @@
 <template>
   <div class="page">
     <div class="page-top flex flex-col justify-center items-center">
-      <img class="logo" :src="supplierInfo.logo" />
+      <!-- <img class="logo" :src="supplierInfo.logo" /> -->
       <div class="name mt-2" v-text="supplierInfo.shopName + '意见反馈'"></div>
     </div>
     <div class="page-content p-4 md:my-4">
@@ -25,41 +25,10 @@
 </template>
 
 <script>
-import { mapGetters } from 'vuex'
+import feedbackMixin from '@/mixins/feedback'
 export default {
   layout: 'app-ross',
-  data() {
-    return {
-      content: '',
-      showModal: false,
-    }
-  },
-  computed: {
-    ...mapGetters(['supplierInfo', 'userInfo', 'routePrefix']),
-    isEmpty() {
-      return this.content.length === 0
-    },
-  },
-  methods: {
-    async onSubmit() {
-      const { clubUserId } = this.userInfo
-      if (this.isEmpty) {
-        this.$toast('留言不能为空')
-        return
-      }
-      try {
-        await this.$http.api.feedback({ clubUserId, content: this.content })
-        this.showModal = true
-        this.content = ''
-      } catch (error) {
-        console.log(error)
-      }
-    },
-    onConfirm() {
-      this.showModal = false
-      this.$router.push(this.routePrefix)
-    },
-  },
+  mixins: [feedbackMixin],
 }
 </script>
 
@@ -249,7 +218,6 @@ export default {
         font-size: 3.2vw;
       }
 
-      
       .image-icon {
         width: 30vw;
         height: 20vw;

+ 1 - 1
pages/_template/ross/form/club-register.vue

@@ -1,7 +1,7 @@
 <template>
   <div class="page">
     <div class="page-top flex flex-col justify-center items-center">
-      <!-- <img class="logo" :src="supplierInfo.logo" /> -->
+      <!-- <!-- <img class="logo" :src="supplierInfo.logo" /> --> -->
       <div
         class="name mt-2"
         v-text="supplierInfo.shopName + '正品授权申请'"

+ 3 - 2
pages/_template/ross/form/components/form-club-device.vue

@@ -172,7 +172,7 @@ export default {
     formType: {
       type: String,
       default: 'add',
-    },
+    }
   },
   data() {
     const productNameValidate = (rule, value, callback) => {
@@ -257,7 +257,7 @@ export default {
   methods: {
     // sn码输入框是去焦点
     async onSnCodeBlur(formItem) {
-      if (!formItem.authType === 1) return
+      if (formItem.authType === 1) return
       if (!formItem.snCode) return
       try {
         const res = await this.$http.api.fetchDetialBySnCode({
@@ -318,6 +318,7 @@ export default {
       ]
       obj.uuid = ++this.uuid
       obj.authType = formData.authType
+      obj.relationId = formData.relationId || ''
       obj.productImageList = productImageList
       obj.invoiceImageList = invoiceImageList
       obj.productImage = formData.productImage

+ 14 - 135
pages/_template/ross/index.vue

@@ -1,11 +1,11 @@
 <template>
   <div class="page">
     <van-list
-      v-model="isLoadingMore"
+      v-model="loadingMore"
       :finished="finished"
       :immediate-check="false"
       :finished-text="total ? '没有更多了' : ''"
-      @load="onLoadMore"
+      @load="fetchClubList"
     >
       <div class="page-top flex flex-col justify-center items-center"></div>
       <div class="page-content">
@@ -34,7 +34,7 @@
             <el-input
               placeholder="搜索机构"
               v-model="listQuery.authParty"
-              @change="onSearch"
+              @change="filterClubList"
             >
               <i slot="prefix" class="el-input__icon el-icon-search"></i>
             </el-input>
@@ -70,11 +70,6 @@
                   <div class="address">
                     {{ formatAddress(item.area, item.address) }}
                   </div>
-                  <!-- <div
-                    class="distance"
-                    v-text="generateDistance(item.distance)"
-                    v-if="item.distance && item.distance !== 99999"
-                  ></div> -->
                 </div>
               </div>
             </template>
@@ -103,7 +98,7 @@
                 </div>
                 <div
                   class="distance"
-                  v-text="generateDistance(item.distance)"
+                  v-text="formatDistance(item.distance)"
                   v-if="item.distance && item.distance !== 99999"
                 ></div>
               </div>
@@ -123,168 +118,52 @@
 
 <script>
 import { mapGetters } from 'vuex'
-import { geolocation } from '@/utils/map-utils'
-import { drawLogo, debounce } from '@/utils'
+import clubListMixin from '@/mixins/clubList'
+import { objectCover } from '@/utils'
 export default {
   layout: 'app-ross',
+  mixins: [clubListMixin],
   data() {
     return {
-      isLoadingMore: true,
-      finished: false,
-      isRequest: true,
-      list: [],
-      listQuery: {
-        authUserId: '',
-        lngAndLat: '',
-        authParty: '',
-        provinceId: '',
-        cityId: '',
-        townId: '',
-        pageNum: 1,
-        pageSize: 10,
-      },
-      total: 0,
       scrollTop: 0,
-      starList: [],
     }
   },
   computed: {
-    ...mapGetters(['authUserId', 'routePrefix', 'screenWidth', 'isPc']),
+    ...mapGetters(['screenWidth', 'isPc']),
     offsetTop() {
       if (this.scrollTop <= window.innerHeight / 2) return '240px'
       return 240 + this.scrollTop - window.innerHeight / 2 + 'px'
     },
   },
   mounted() {
-    const cacheData = this.$getStorage(this.routePrefix, 'club_list_data')
-    if (cacheData) {
-      this.initFromCache(cacheData)
-      this.$removeStorage(this.routePrefix, 'club_list_data')
-    } else {
-      this.initData()
-    }
     window.addEventListener('scroll', () => {
       this.scrollTop = document.documentElement.scrollTop
     })
-    this.fetchAuthClubStarList()
   },
   beforeDestroy() {
-    this.$toast.clear()
     window.removeEventListener('scroll', () => {})
   },
   methods: {
-    // 获取明星机构列表
-    async fetchAuthClubStarList() {
-      try {
-        const res = await this.$http.api.getAuthClubStarList({
-          authUserId: this.authUserId,
-        })
-        this.starList = res.data.slice(0, 2)
-      } catch (error) {
-        console.log(error)
-      }
-    },
-
-    // 绘制logo的方法
-    drawLogo,
-    // 查看详情
-    toDetail(item) {
-      this.$setStorage(this.routePrefix, 'club_list_data', this.$data, {
-        expiredTime: 5 * 60 * 1000,
-      })
-      this.$setStorage(this.routePrefix, 'clubInfo', item)
-      const url = `${this.routePrefix}/approve/club/detail?id=${item.authId}`
-      this.$router.push(url)
-    },
     // 从缓存中获取数据
     initFromCache(cacheData) {
-      this.isLoadingMore = cacheData.isLoadingMore
-      this.finished = cacheData.finished
-      this.isRequest = cacheData.isRequest
-      this.list = cacheData.list
-      this.listQuery = cacheData.listQuery
-      this.total = cacheData.total
+      const data = objectCover(this, cacheData)
+      console.log(data)
       this.$nextTick(() => {
         this.$refs.citySelect.initSelectValue({
-          provinceId: this.listQuery.provinceId,
-          cityId: this.listQuery.cityId,
-          townId: this.listQuery.townId,
+          provinceId: data.provinceId,
+          cityId: data.cityId,
+          townId: data.townId,
         })
       })
     },
 
-    generateDistance(value) {
-      return value > 1 ? value + 'km' : value * 1000 + 'm'
-    },
-
-    // 初始化页面数据
-    async initData() {
-      this.listQuery.authUserId = this.authUserId
-
-      // 自定义加载图标
-      this.$toast.loading({
-        message: '正在获取您附近的机构...',
-        duration: 0,
-      })
-
-      // 获取定位信息 这里使用的是高德地图获取用户具体位置信息
-      try {
-        const resLocation = await geolocation()
-        this.listQuery.lngAndLat = `${resLocation.position.lng},${resLocation.position.lat}`
-      } catch (error) {
-        this.$toast('获取定位信息失败,请确保您开启的定位权限并保存网络畅通')
-        this.isRequest = false
-      }
-      // 获取机构列表
-      this.fetchList()
-    },
-    fetchList: debounce(async function () {
-      try {
-        this.isLoadingMore = true
-        const res = await this.$http.api.getAuthClubList(this.listQuery)
-        this.total = res.data.total
-        this.list = [...this.list, ...res.data.list]
-        this.finished = !res.data.hasNextPage
-        this.isLoadingMore = false
-        this.listQuery.pageNum += 1
-      } catch (error) {
-        console.log(error)
-      } finally {
-        this.$toast.clear()
-        this.isRequest = false
-      }
-    }, 400),
     // 城市变化
     onCityChange(valueMap) {
       const { provinceId, cityId, townId } = valueMap
       this.listQuery.provinceId = provinceId
       this.listQuery.cityId = cityId
       this.listQuery.townId = townId
-
-      this.listQuery.pageNum = 1
-      this.list = []
-      this.fetchList()
-    },
-    // 搜索
-    onSearch() {
-      this.listQuery.pageNum = 1
-      this.list = []
-      this.fetchList()
-    },
-    // 格式化地址
-    formatAddress(a1, a2) {
-      let resutl = ''
-      if (typeof a1 === 'string') {
-        resutl += a1
-      }
-      if (typeof a2 === 'string') {
-        resutl += a2
-      }
-      return resutl || '暂无'
-    },
-    // 加载更多
-    onLoadMore() {
-      this.fetchList()
+      this.filterClubList()
     },
   },
 }

+ 1 - 1
pages/_template/ross/record/club/detail.vue

@@ -1,7 +1,7 @@
 <template>
   <div class="page">
     <div class="page-top flex flex-col justify-center items-center">
-      <img class="logo" :src="supplierInfo.logo" />
+      <!-- <img class="logo" :src="supplierInfo.logo" /> -->
       <div class="name mt-2" v-text="supplierInfo.shopName + '认证记录'"></div>
     </div>
     <div class="page-content">

+ 1 - 1
pages/_template/ross/record/club/edit.vue

@@ -1,7 +1,7 @@
 <template>
   <div class="club-info page">
     <div class="page-top flex flex-col justify-center items-center">
-      <img class="logo" :src="supplierInfo.logo" />
+      <!-- <img class="logo" :src="supplierInfo.logo" /> -->
       <div class="name mt-2" v-text="supplierInfo.shopName + '认证记录'"></div>
     </div>
     <div class="page-content">

+ 1 - 1
pages/_template/ross/record/device/detail.vue

@@ -1,7 +1,7 @@
 <template>
   <div class="page">
     <div class="page-top flex flex-col justify-center items-center">
-      <img class="logo" :src="supplierInfo.logo" />
+      <!-- <img class="logo" :src="supplierInfo.logo" /> -->
       <div class="name mt-2" v-text="supplierInfo.shopName + '认证记录'"></div>
     </div>
     <div class="page-content">

+ 1 - 1
pages/_template/ross/record/device/edit.vue

@@ -1,7 +1,7 @@
 <template>
   <div class="club-device page">
     <div class="page-top flex flex-col justify-center items-center">
-      <img class="logo" :src="supplierInfo.logo" />
+      <!-- <img class="logo" :src="supplierInfo.logo" /> -->
       <div class="name mt-2" v-text="supplierInfo.shopName + '认证记录'"></div>
     </div>
     <div class="page-content">

+ 1 - 1
pages/_template/ross/record/device/index.vue

@@ -8,7 +8,7 @@
       @load="onLoadMore"
     >
       <div class="page-top flex flex-col justify-center items-center">
-        <img class="logo" :src="supplierInfo.logo" />
+        <!-- <img class="logo" :src="supplierInfo.logo" /> -->
         <div
           class="name mt-2"
           v-text="supplierInfo.shopName + '认证记录'"

+ 15 - 0
utils/index.js

@@ -162,3 +162,18 @@ export function callMobile(mobile) {
   a.href = 'tel:' + mobile
   a.click()
 }
+
+/**
+ * Merges one object to another object
+ * @param {Object} target
+ * @param {(Object)} source
+ * @returns {Object}
+ */
+export function objectCover(target, source) {
+  for (const key in source) {
+    if (Object.hasOwnProperty.call(target, key)) {
+      target[key] = source[key]
+    }
+  }
+  return target
+}