|
|
@@ -0,0 +1,755 @@
|
|
|
+<template>
|
|
|
+ <view class="page-container">
|
|
|
+ <nav-bar title="推荐官"></nav-bar>
|
|
|
+
|
|
|
+ <view class="top-section">
|
|
|
+ <view class="flex align-center justify-between">
|
|
|
+ <view class="left flex align-center">
|
|
|
+ <image src="@/static/images/my/badge.png" class="badge-icon"></image>
|
|
|
+ <view>
|
|
|
+ <view class="name">金牌推荐官</view>
|
|
|
+ <view class="sub-name">{{ userName }}/{{ userPhone }}</view>
|
|
|
+ </view>
|
|
|
+ </view>
|
|
|
+ <view class="poster-btn" @click="getPosterData">获取专属推荐海报</view>
|
|
|
+ </view>
|
|
|
+ <view class="progress-wrapper">
|
|
|
+ <view class="progress-bar-wrapper">
|
|
|
+ <view class="progress-bar" :style="{ width: progressBarWidth }"></view>
|
|
|
+ </view>
|
|
|
+ <image src="@/static/images/my/badge2.png" class="badge badge1"></image>
|
|
|
+ <image src="@/static/images/my/badge2.png" class="badge badge2"></image>
|
|
|
+ <image src="@/static/images/my/badge2.png" class="badge badge3"></image>
|
|
|
+ </view>
|
|
|
+ <view class="names flex align-center">
|
|
|
+ <view class="name">推荐10人</view>
|
|
|
+ <view class="name">推荐20人</view>
|
|
|
+ <view class="name">推荐30人</view>
|
|
|
+ </view>
|
|
|
+ </view>
|
|
|
+
|
|
|
+ <view class="toolbar-row flex align-center justify-between margin">
|
|
|
+ <view class="add-btn" @click="getWechatQrcode">点击添加企微兑现权益</view>
|
|
|
+ <view class="time-button flex align-center" @click="openDatePicker">
|
|
|
+ <text>{{ dateFilterText }}</text>
|
|
|
+ <u-icon name="arrow-down" size="24" style="margin-left: 2px;"></u-icon>
|
|
|
+ </view>
|
|
|
+ </view>
|
|
|
+
|
|
|
+ <view class="list-wrapper margin">
|
|
|
+ <view class="tabs flex justify-center">
|
|
|
+ <view
|
|
|
+ class="tab-item"
|
|
|
+ :class="{ active: activeTab === item.value }"
|
|
|
+ v-for="item in tabs"
|
|
|
+ :key="item.value"
|
|
|
+ @click="switchTab(item.value)"
|
|
|
+ >
|
|
|
+ {{ item.name }}
|
|
|
+ <view class="border"></view>
|
|
|
+ </view>
|
|
|
+ </view>
|
|
|
+
|
|
|
+ <scroll-view
|
|
|
+ class="list-scroll"
|
|
|
+ scroll-y
|
|
|
+ refresher-enabled
|
|
|
+ :refresher-threshold="80"
|
|
|
+ refresher-default-style="black"
|
|
|
+ :refresher-triggered="isRefreshing"
|
|
|
+ @refresherrefresh="handleRefresherRefresh"
|
|
|
+ @scrolltolower="handleScrollToLower"
|
|
|
+ :lower-threshold="80"
|
|
|
+ >
|
|
|
+ <view class="list-scroll-inner">
|
|
|
+ <view class="user-item flex justify-between" v-for="item in list" :key="item.user_id || item.id">
|
|
|
+ <view class="flex align-center">
|
|
|
+ <image :src="item.avatar || defaultAvatar" class="avatar"></image>
|
|
|
+ <view class="user-info">
|
|
|
+ <view class="user-name">{{ item.user_name }}</view>
|
|
|
+ <view class="user-tel">{{ item.phone }}</view>
|
|
|
+ </view>
|
|
|
+ </view>
|
|
|
+ <view class="time">{{ item.create_time }}</view>
|
|
|
+ </view>
|
|
|
+ <view v-if="!loadingMore && list.length === 0" class="list-empty">暂无数据</view>
|
|
|
+ <view v-if="loadingMore" class="list-load-tip">加载中...</view>
|
|
|
+ <view v-if="!hasMore && list.length > 0" class="list-nomore">— 没有更多了 —</view>
|
|
|
+ </view>
|
|
|
+ </scroll-view>
|
|
|
+ </view>
|
|
|
+ <view style="height: 20rpx;"></view>
|
|
|
+
|
|
|
+ <u-popup v-model="showDatePickerPopup" mode="bottom" border-radius="20" :safeAreaInsetBottom="true">
|
|
|
+ <view class="date-picker-panel">
|
|
|
+ <view class="date-picker-header">
|
|
|
+ <text class="date-picker-btn" @click="showDatePickerPopup = false">取消</text>
|
|
|
+ <text class="date-picker-title">选择日期</text>
|
|
|
+ <text class="date-picker-btn date-picker-btn--primary" @click="confirmDatePicker">确定</text>
|
|
|
+ </view>
|
|
|
+ <!-- indicator 高度须与每行 date-picker-item 高度一致(px),否则滚轮视觉与 value 索引错位(微信官方说明) -->
|
|
|
+ <view class="date-picker-view-wrap">
|
|
|
+ <picker-view
|
|
|
+ v-if="showDatePickerPopup && yearList.length && dayList.length"
|
|
|
+ :key="pickerMountKey"
|
|
|
+ class="date-picker-view"
|
|
|
+ :indicator-style="pickerIndicatorStyle"
|
|
|
+ :value="datePickerValue"
|
|
|
+ @change="onDatePickerViewChange"
|
|
|
+ >
|
|
|
+ <picker-view-column>
|
|
|
+ <view v-for="(t, i) in modeColumn" :key="'mode-' + i" class="date-picker-item">
|
|
|
+ <text class="date-picker-item-text">{{ t }}</text>
|
|
|
+ </view>
|
|
|
+ </picker-view-column>
|
|
|
+ <picker-view-column>
|
|
|
+ <view v-for="(y, i) in yearList" :key="'y-' + i" class="date-picker-item">
|
|
|
+ <text class="date-picker-item-text">{{ y }}年</text>
|
|
|
+ </view>
|
|
|
+ </picker-view-column>
|
|
|
+ <picker-view-column>
|
|
|
+ <view v-for="(m, i) in monthList" :key="'mo-' + i" class="date-picker-item">
|
|
|
+ <text class="date-picker-item-text">{{ m }}月</text>
|
|
|
+ </view>
|
|
|
+ </picker-view-column>
|
|
|
+ <picker-view-column>
|
|
|
+ <view v-for="(d, i) in dayList" :key="'d-' + i" class="date-picker-item">
|
|
|
+ <text class="date-picker-item-text">{{ d }}日</text>
|
|
|
+ </view>
|
|
|
+ </picker-view-column>
|
|
|
+ </picker-view>
|
|
|
+ <!-- 全部:盖住后三列(H5 / APP-PLUS 用普通 view 即可) -->
|
|
|
+ <view
|
|
|
+ v-if="datePickerValue[0] === 0"
|
|
|
+ class="date-picker-ymd-mask"
|
|
|
+ @touchmove.stop.prevent="noop"
|
|
|
+ ></view>
|
|
|
+ </view>
|
|
|
+ </view>
|
|
|
+ </u-popup>
|
|
|
+
|
|
|
+ <Poster
|
|
|
+ :showTitle="false"
|
|
|
+ :showTip="showTip"
|
|
|
+ :value="showPoster"
|
|
|
+ :posterBase64Url="posterBase64Url"
|
|
|
+ :saveBtnText="saveBtnText"
|
|
|
+ @close="showPoster = false"
|
|
|
+ @saved="handleSaved"
|
|
|
+ ></Poster>
|
|
|
+ </view>
|
|
|
+</template>
|
|
|
+
|
|
|
+<script>
|
|
|
+ import navBar from '@/components/nav-bar/index.vue';
|
|
|
+ import Poster from '@/components/poster/poster.vue'
|
|
|
+ export default {
|
|
|
+ components: {
|
|
|
+ navBar,
|
|
|
+ Poster
|
|
|
+ },
|
|
|
+ data() {
|
|
|
+ return {
|
|
|
+ tabs: [
|
|
|
+ {
|
|
|
+ name: '已注册用户',
|
|
|
+ value: 0
|
|
|
+ },
|
|
|
+ {
|
|
|
+ name: '已达标用户',
|
|
|
+ value: 1
|
|
|
+ },
|
|
|
+ ],
|
|
|
+ list: [],
|
|
|
+ defaultAvatar: '/static/logo.png',
|
|
|
+ showPoster: false,
|
|
|
+ activeTab: 0,
|
|
|
+ page: 1,
|
|
|
+ limit: 10,
|
|
|
+ totalPages: 1,
|
|
|
+ hasMore: true,
|
|
|
+ isRefreshing: false,
|
|
|
+ loadingMore: false,
|
|
|
+ /** 内容不足一屏时自动补页,防止无法触底 */
|
|
|
+ autoLoadMoreGuard: 0,
|
|
|
+ /** 每次发起列表请求递增,用于丢弃过期响应(避免刷新后仍被旧请求覆盖 hasMore/list) */
|
|
|
+ listReqId: 0,
|
|
|
+ /** 筛选日期:null 表示「全部」;否则为 YYYY-MM-DD */
|
|
|
+ filterDate: null,
|
|
|
+ showDatePickerPopup: false,
|
|
|
+ modeColumn: ['全部', '按日选择'],
|
|
|
+ yearList: [],
|
|
|
+ monthList: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12],
|
|
|
+ dayList: [],
|
|
|
+ /** [模式索引, 年索引, 月索引, 日索引],与 picker-view 四列一一对应 */
|
|
|
+ datePickerValue: [0, 0, 0, 0],
|
|
|
+ /** 与下方 .date-picker-item 高度一致,单位 px */
|
|
|
+ pickerIndicatorStyle: 'height: 50px;',
|
|
|
+ /** 每次打开弹层递增,强制重建 picker-view,避免残留错位 */
|
|
|
+ pickerMountKey: 0,
|
|
|
+ userName: '',
|
|
|
+ userPhone: '',
|
|
|
+ inviteTotal: 0,
|
|
|
+ posterBase64Url: '',
|
|
|
+ loading: false,
|
|
|
+ posterType: 0, // 0: 推荐官海报, 1: 企微二维码
|
|
|
+ }
|
|
|
+ },
|
|
|
+ computed: {
|
|
|
+ dateFilterText() {
|
|
|
+ if (this.filterDate == null) return '全部'
|
|
|
+ return this.filterDate.replace(/-/g, '.')
|
|
|
+ },
|
|
|
+ showTip() {
|
|
|
+ return this.posterType === 0
|
|
|
+ },
|
|
|
+ saveBtnText() {
|
|
|
+ return this.posterType === 0 ? '保存海报并成为推荐官' : '保存企微二维码'
|
|
|
+ },
|
|
|
+ progressBarWidth() {
|
|
|
+ let percent = this.inviteTotal / 30
|
|
|
+ if (percent > 1) {
|
|
|
+ percent = 1
|
|
|
+ }
|
|
|
+ return percent * 100 + '%'
|
|
|
+ }
|
|
|
+ },
|
|
|
+ onLoad() {
|
|
|
+ const years = []
|
|
|
+ for (let y = 2010; y <= 2040; y++) years.push(y)
|
|
|
+ this.yearList = years
|
|
|
+ },
|
|
|
+ onShow() {
|
|
|
+ // 判断有没登录,如果没有登录则跳转登录
|
|
|
+ if (!uni.getStorageSync("userId")) {
|
|
|
+ this.$queue.toLogin()
|
|
|
+ return
|
|
|
+ }
|
|
|
+ this.getBasicInfo()
|
|
|
+ this.page = 1
|
|
|
+ this.getList()
|
|
|
+ },
|
|
|
+ methods: {
|
|
|
+ switchTab(value) {
|
|
|
+ if (this.activeTab === value) return
|
|
|
+ this.activeTab = value
|
|
|
+ this.page = 1
|
|
|
+ this.hasMore = true
|
|
|
+ this.getList()
|
|
|
+ },
|
|
|
+ /** scroll-view 下拉刷新(勿命名为 onPullDownRefresh,与 uni 页面生命周期重名会冲突) */
|
|
|
+ handleRefresherRefresh() {
|
|
|
+ this.loadingMore = false
|
|
|
+ this.isRefreshing = true
|
|
|
+ this.page = 1
|
|
|
+ this.hasMore = true
|
|
|
+ this.autoLoadMoreGuard = 0
|
|
|
+ this.getList()
|
|
|
+ this.getBasicInfo()
|
|
|
+ },
|
|
|
+ handleScrollToLower() {
|
|
|
+ if (!this.hasMore || this.loadingMore || this.isRefreshing) return
|
|
|
+ this.loadingMore = true
|
|
|
+ this.page += 1
|
|
|
+ this.getList()
|
|
|
+ },
|
|
|
+ /** 列表高度未超过 scroll-view 时不会触发 scrolltolower,自动再请求一页(最多 5 次) */
|
|
|
+ tryFillScrollArea() {
|
|
|
+ if (!this.hasMore || this.loadingMore || this.isRefreshing) return
|
|
|
+ if (this.autoLoadMoreGuard >= 5) return
|
|
|
+ this.$nextTick(() => {
|
|
|
+ setTimeout(() => {
|
|
|
+ const q = uni.createSelectorQuery().in(this)
|
|
|
+ q.select('.list-scroll').boundingClientRect()
|
|
|
+ q.select('.list-scroll-inner').boundingClientRect()
|
|
|
+ q.exec((res) => {
|
|
|
+ const scrollRect = res && res[0]
|
|
|
+ const innerRect = res && res[1]
|
|
|
+ if (!scrollRect || !innerRect) return
|
|
|
+ // 内容区高度未量到时不补页,避免误连请求
|
|
|
+ if (scrollRect.height > 20 && innerRect.height < 1) return
|
|
|
+ if (innerRect.height <= scrollRect.height + 4) {
|
|
|
+ this.autoLoadMoreGuard += 1
|
|
|
+ this.handleScrollToLower()
|
|
|
+ }
|
|
|
+ })
|
|
|
+ }, 100)
|
|
|
+ })
|
|
|
+ },
|
|
|
+ /** month 为自然月 1-12 */
|
|
|
+ getDaysInMonth(year, month) {
|
|
|
+ return new Date(year, month, 0).getDate()
|
|
|
+ },
|
|
|
+ updateDayList() {
|
|
|
+ let yi = Number(this.datePickerValue[1])
|
|
|
+ let mi = Number(this.datePickerValue[2])
|
|
|
+ yi = Math.min(Math.max(0, yi), this.yearList.length - 1)
|
|
|
+ mi = Math.min(Math.max(0, mi), this.monthList.length - 1)
|
|
|
+ const y = this.yearList[yi]
|
|
|
+ const m = this.monthList[mi]
|
|
|
+ const max = this.getDaysInMonth(y, m)
|
|
|
+ const days = []
|
|
|
+ for (let d = 1; d <= max; d++) days.push(d)
|
|
|
+ this.dayList = days
|
|
|
+ },
|
|
|
+ noop() {},
|
|
|
+ onDatePickerViewChange(e) {
|
|
|
+ const raw = e.detail.value
|
|
|
+ if (!raw || raw.length < 4) return
|
|
|
+ const val = raw.map((n) => Number(n))
|
|
|
+ const old = this.datePickerValue
|
|
|
+ // 全部:禁止年月日列变化(与遮罩配合;兜底)
|
|
|
+ if (old[0] === 0 && val[0] === 0 && (val[1] !== old[1] || val[2] !== old[2] || val[3] !== old[3])) {
|
|
|
+ this.datePickerValue = [0, old[1], old[2], old[3]]
|
|
|
+ return
|
|
|
+ }
|
|
|
+ this.datePickerValue = val
|
|
|
+ if (val[0] === 0) return
|
|
|
+ if (val[1] !== old[1] || val[2] !== old[2]) {
|
|
|
+ this.updateDayList()
|
|
|
+ this.$nextTick(() => {
|
|
|
+ const maxD = this.dayList.length
|
|
|
+ let di = val[3]
|
|
|
+ if (di >= maxD) di = Math.max(0, maxD - 1)
|
|
|
+ this.datePickerValue = [val[0], val[1], val[2], di]
|
|
|
+ })
|
|
|
+ }
|
|
|
+ },
|
|
|
+ openDatePicker() {
|
|
|
+ const t = new Date()
|
|
|
+ const ty = t.getFullYear()
|
|
|
+ const tm = t.getMonth() + 1
|
|
|
+ const td = t.getDate()
|
|
|
+ const yi = Math.max(0, this.yearList.indexOf(ty))
|
|
|
+ const moi = tm - 1
|
|
|
+ if (this.filterDate) {
|
|
|
+ const p = this.filterDate.split('-')
|
|
|
+ if (p.length === 3) {
|
|
|
+ const y = parseInt(p[0], 10)
|
|
|
+ const m = parseInt(p[1], 10)
|
|
|
+ const d = parseInt(p[2], 10)
|
|
|
+ const yIdx = Math.max(0, this.yearList.indexOf(y))
|
|
|
+ const mIdx = Math.min(Math.max(0, m - 1), 11)
|
|
|
+ this.datePickerValue = [1, yIdx, mIdx, 0]
|
|
|
+ this.updateDayList()
|
|
|
+ let di = this.dayList.indexOf(d)
|
|
|
+ if (di < 0) di = 0
|
|
|
+ di = Math.min(di, this.dayList.length - 1)
|
|
|
+ this.datePickerValue = [1, yIdx, mIdx, di]
|
|
|
+ } else {
|
|
|
+ this.datePickerValue = [0, yi, moi, 0]
|
|
|
+ this.updateDayList()
|
|
|
+ let di = this.dayList.indexOf(td)
|
|
|
+ if (di < 0) di = 0
|
|
|
+ di = Math.min(di, this.dayList.length - 1)
|
|
|
+ this.datePickerValue = [0, yi, moi, di]
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ this.datePickerValue = [0, yi, moi, 0]
|
|
|
+ this.updateDayList()
|
|
|
+ let di = this.dayList.indexOf(td)
|
|
|
+ if (di < 0) di = 0
|
|
|
+ di = Math.min(di, this.dayList.length - 1)
|
|
|
+ this.datePickerValue = [0, yi, moi, di]
|
|
|
+ }
|
|
|
+ this.pickerMountKey += 1
|
|
|
+ this.showDatePickerPopup = true
|
|
|
+ },
|
|
|
+ confirmDatePicker() {
|
|
|
+ const modeIdx = Number(this.datePickerValue[0])
|
|
|
+ let yi = Number(this.datePickerValue[1])
|
|
|
+ let moi = Number(this.datePickerValue[2])
|
|
|
+ let di = Number(this.datePickerValue[3])
|
|
|
+ yi = Math.min(Math.max(0, yi), this.yearList.length - 1)
|
|
|
+ moi = Math.min(Math.max(0, moi), 11)
|
|
|
+ di = Math.min(Math.max(0, di), Math.max(0, this.dayList.length - 1))
|
|
|
+ if (modeIdx === 0) {
|
|
|
+ this.filterDate = null
|
|
|
+ } else {
|
|
|
+ const y = this.yearList[yi]
|
|
|
+ const m = this.monthList[moi]
|
|
|
+ const d = this.dayList[di]
|
|
|
+ if (y != null && m != null && d != null) {
|
|
|
+ this.filterDate = `${y}-${String(m).padStart(2, '0')}-${String(d).padStart(2, '0')}`
|
|
|
+ }
|
|
|
+ }
|
|
|
+ this.showDatePickerPopup = false
|
|
|
+ this.page = 1
|
|
|
+ this.hasMore = true
|
|
|
+ this.getList()
|
|
|
+ },
|
|
|
+ getList() {
|
|
|
+ const expectPage = this.page
|
|
|
+ const reqId = ++this.listReqId
|
|
|
+ // 接口:inviteDate 为 yyyy.MM.dd,不传查全部
|
|
|
+ const inviteDate =
|
|
|
+ this.filterDate ? this.filterDate.replace(/-/g, '.') : ''
|
|
|
+ const params = {
|
|
|
+ page: expectPage,
|
|
|
+ limit: this.limit,
|
|
|
+ type: this.activeTab,
|
|
|
+ inviteDate
|
|
|
+ }
|
|
|
+ return this.$Request.get('/app/invite/selectInviteList', params)
|
|
|
+ .then((res) => {
|
|
|
+ if (reqId !== this.listReqId) return
|
|
|
+ if (expectPage !== this.page) return
|
|
|
+ if (res.code !== 0) return
|
|
|
+ const data = res.data || {}
|
|
|
+ const newList = data.list || []
|
|
|
+ const pageSize = Number(data.pageSize) || this.limit
|
|
|
+ const currPage = Number(data.currPage) || expectPage
|
|
|
+ let totalPage = Number(data.totalPage)
|
|
|
+ const totalCount = data.totalCount != null ? Number(data.totalCount) : null
|
|
|
+ if (Number.isNaN(totalPage) || totalPage < 1) {
|
|
|
+ if (totalCount != null && !Number.isNaN(totalCount) && pageSize > 0) {
|
|
|
+ totalPage = Math.max(1, Math.ceil(totalCount / pageSize))
|
|
|
+ } else {
|
|
|
+ totalPage = data.pages ?? data.pageTotal
|
|
|
+ totalPage = totalPage != null && !Number.isNaN(Number(totalPage))
|
|
|
+ ? Math.max(1, Number(totalPage))
|
|
|
+ : 1
|
|
|
+ }
|
|
|
+ }
|
|
|
+ if (expectPage === 1) {
|
|
|
+ this.list = newList
|
|
|
+ this.autoLoadMoreGuard = 0
|
|
|
+ } else {
|
|
|
+ this.list = this.list.concat(newList)
|
|
|
+ }
|
|
|
+ this.totalPages = totalPage
|
|
|
+ // 优先用 totalCount 与已加载条数比较(与接口返回结构一致,最稳)
|
|
|
+ if (totalCount != null && !Number.isNaN(totalCount)) {
|
|
|
+ this.hasMore = this.list.length < totalCount
|
|
|
+ } else {
|
|
|
+ this.hasMore = currPage < totalPage
|
|
|
+ }
|
|
|
+ if (newList.length === 0 && expectPage > 1) this.hasMore = false
|
|
|
+ })
|
|
|
+ .catch(() => {
|
|
|
+ if (reqId !== this.listReqId) return
|
|
|
+ if (expectPage !== this.page) return
|
|
|
+ if (this.page > 1) this.page -= 1
|
|
|
+ })
|
|
|
+ .finally(() => {
|
|
|
+ if (reqId !== this.listReqId) return
|
|
|
+ this.isRefreshing = false
|
|
|
+ this.loadingMore = false
|
|
|
+ // 必须在 finally 之后:若放在 then 的 $nextTick 里,可能早于本处执行,isRefreshing 仍为 true,tryFillScrollArea 会直接 return(下拉刷新后无法补页/触底)
|
|
|
+ this.$nextTick(() => this.tryFillScrollArea())
|
|
|
+ })
|
|
|
+ },
|
|
|
+ // 获取基本信息
|
|
|
+ getBasicInfo() {
|
|
|
+ this.$Request.getT('/app/invite/selectInviteTotal').then((res) => {
|
|
|
+ if (res.code == 0) {
|
|
|
+ this.basicInfo = res.data
|
|
|
+ this.userName = res.data.userName
|
|
|
+ this.userPhone = res.data.phone
|
|
|
+ this.inviteTotal = res.data.totalCount
|
|
|
+ }
|
|
|
+ })
|
|
|
+ },
|
|
|
+ // 获取海报数据
|
|
|
+ getPosterData() {
|
|
|
+ if (this.loading) return
|
|
|
+ this.posterType = 0
|
|
|
+ this.loading = true
|
|
|
+ this.$queue.showLoading('获取海报数据中...')
|
|
|
+ this.$Request.getT('/app/invite/getMyPoster')
|
|
|
+ .then(res => {
|
|
|
+ if (res.code == 0) {
|
|
|
+ this.showPoster = true
|
|
|
+ this.posterBase64Url = res.data?.image ?? ''
|
|
|
+ }
|
|
|
+ }).finally(() => {
|
|
|
+ this.$queue.hideLoading()
|
|
|
+ this.loading = false
|
|
|
+ })
|
|
|
+ },
|
|
|
+ handleSaved() {
|
|
|
+ this.showPoster = false
|
|
|
+ // #ifndef H5
|
|
|
+ this.$queue.showToast('图片已保存到相册')
|
|
|
+ // #endif
|
|
|
+ // #ifdef H5
|
|
|
+ this.$queue.showToast('保存成功')
|
|
|
+ // #endif
|
|
|
+ },
|
|
|
+ // 获取企微二维码
|
|
|
+ getWechatQrcode() {
|
|
|
+ if (this.loading) return
|
|
|
+ this.posterType = 1
|
|
|
+ this.loading = true
|
|
|
+ this.$queue.showLoading('获取企微二维码中...')
|
|
|
+ this.$Request.getT('/app/common/type/830').then((res) => {
|
|
|
+ if (res.code == 0) {
|
|
|
+ this.posterBase64Url = res.data?.value ?? ''
|
|
|
+ this.showPoster = true
|
|
|
+ }
|
|
|
+ }).finally(() => {
|
|
|
+ this.$queue.hideLoading()
|
|
|
+ this.loading = false
|
|
|
+ })
|
|
|
+ },
|
|
|
+ }
|
|
|
+ }
|
|
|
+</script>
|
|
|
+
|
|
|
+<style lang="scss" scoped>
|
|
|
+ .page-container {
|
|
|
+ display: flex;
|
|
|
+ flex-direction: column;
|
|
|
+ height: 100vh;
|
|
|
+ /* 允许子项 flex:1 区域正确收缩,内部 scroll-view 才能滚 */
|
|
|
+ min-height: 0;
|
|
|
+ box-sizing: border-box;
|
|
|
+ font-family: DM Sans;
|
|
|
+ .top-section {
|
|
|
+ flex-shrink: 0;
|
|
|
+ margin: 24rpx 36rpx;
|
|
|
+ padding: 32rpx 28rpx 40rpx;
|
|
|
+ border-radius: 24rpx;
|
|
|
+ background-image: url('@/static/images/my/bg3.png'),
|
|
|
+ linear-gradient(90deg, #4a5cff 0%, #3d8aed 45%, #5ee7df 100%);
|
|
|
+ background-size: 100% 100%;
|
|
|
+ background-repeat: no-repeat;
|
|
|
+ background-position: center;
|
|
|
+ .badge-icon {
|
|
|
+ width: 80rpx;
|
|
|
+ height: 80rpx;
|
|
|
+ margin-right: 4rpx;
|
|
|
+ }
|
|
|
+ .name {
|
|
|
+ font-size: 36rpx;
|
|
|
+ line-height: 1;
|
|
|
+ color: #fff;
|
|
|
+ }
|
|
|
+ .sub-name {
|
|
|
+ font-size: 24rpx;
|
|
|
+ line-height: 36rpx;
|
|
|
+ color: #fff;
|
|
|
+ max-width: 240rpx;
|
|
|
+ overflow: hidden;
|
|
|
+ text-overflow: ellipsis;
|
|
|
+ white-space: nowrap;
|
|
|
+ }
|
|
|
+ .poster-btn {
|
|
|
+ color: rgba(255, 255, 255, 1);
|
|
|
+ font-style: Regular;
|
|
|
+ font-size: 14px;
|
|
|
+ font-weight: 400;
|
|
|
+ line-height: 24px;
|
|
|
+ padding: 0 30rpx;
|
|
|
+ border-radius: 30px;
|
|
|
+ background: rgba(255, 255, 255, 0.35);
|
|
|
+ }
|
|
|
+ .progress-wrapper {
|
|
|
+ position: relative;
|
|
|
+ margin: 80rpx auto 32rpx;
|
|
|
+ .progress-bar-wrapper {
|
|
|
+ width: 600rpx;
|
|
|
+ height: 20rpx;
|
|
|
+ border-radius: 20rpx;
|
|
|
+ background-color: rgba(255, 255, 255, 0.6);
|
|
|
+ margin: 0 auto;
|
|
|
+ .progress-bar {
|
|
|
+ height: 100%;
|
|
|
+ border-radius: 20rpx;
|
|
|
+ background: linear-gradient(90.00deg, rgba(27, 234, 228, 1) 0%,rgba(1, 107, 251, 1) 100%);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ .badge {
|
|
|
+ position: absolute;
|
|
|
+ top: -22rpx;
|
|
|
+ width: 60rpx;
|
|
|
+ height: 60rpx;
|
|
|
+ transform: translateX(-100%);
|
|
|
+ }
|
|
|
+ .badge1 {
|
|
|
+ left: 34%;
|
|
|
+ }
|
|
|
+ .badge2 {
|
|
|
+ left: 66%;
|
|
|
+ }
|
|
|
+ .badge3 {
|
|
|
+ left: 100%;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ .names {
|
|
|
+ .name {
|
|
|
+ flex: 1;
|
|
|
+ text-align: right;
|
|
|
+ color: rgba(255, 255, 255, 1);
|
|
|
+ font-size: 14px;
|
|
|
+ font-weight: 400;
|
|
|
+ line-height: 26px;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ .margin {
|
|
|
+ margin: 0 36rpx;
|
|
|
+ }
|
|
|
+ .toolbar-row {
|
|
|
+ flex-shrink: 0;
|
|
|
+ }
|
|
|
+ .add-btn {
|
|
|
+ color: rgba(1, 107, 246, 1);
|
|
|
+ font-size: 28rpx;
|
|
|
+ font-weight: 400;
|
|
|
+ line-height: 1;
|
|
|
+ }
|
|
|
+ .time-button {
|
|
|
+ box-sizing: border-box;
|
|
|
+ border: 0.5px solid rgba(1, 107, 246, 1);
|
|
|
+ border-radius: 4px;
|
|
|
+ background: rgba(153, 196, 250, 0.4);
|
|
|
+ padding: 8rpx;
|
|
|
+ color: rgba(1, 107, 246, 1);
|
|
|
+ font-size: 28rpx;
|
|
|
+ font-weight: 400;
|
|
|
+ line-height: 1;
|
|
|
+ }
|
|
|
+ .list-wrapper {
|
|
|
+ flex: 1;
|
|
|
+ min-height: 0;
|
|
|
+ /* 与内部 scroll-view 的 flex:1;height:0 配合,否则部分端无法滚动、触底不触发 */
|
|
|
+ height: 0;
|
|
|
+ display: flex;
|
|
|
+ flex-direction: column;
|
|
|
+ overflow: hidden;
|
|
|
+ border-radius: 12rpx;
|
|
|
+ box-shadow: 0 0 20rpx rgba(0, 0, 0, 0.1);
|
|
|
+ padding: 20rpx 0 40rpx;
|
|
|
+ margin-top: 32rpx;
|
|
|
+ box-sizing: border-box;
|
|
|
+ .tabs {
|
|
|
+ flex-shrink: 0;
|
|
|
+ height: 72rpx;
|
|
|
+ gap: 40rpx;
|
|
|
+ .tab-item {
|
|
|
+ position: relative;
|
|
|
+ color: rgba(102, 102, 102, 1);
|
|
|
+ font-size: 28rpx;
|
|
|
+ font-weight: 500;
|
|
|
+ line-height: 32rpx;
|
|
|
+ .border {
|
|
|
+ position: absolute;
|
|
|
+ left: 50%;
|
|
|
+ bottom: 0;
|
|
|
+ width: 80rpx;
|
|
|
+ height: 4rpx;
|
|
|
+ background-color: #fff;
|
|
|
+ transform: translateX(-50%);
|
|
|
+ border-radius: 4rpx 4rpx 0 0;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ .active {
|
|
|
+ color: #016BF6;
|
|
|
+ .border {
|
|
|
+ background: #016BF6;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ .list-scroll {
|
|
|
+ flex: 1;
|
|
|
+ height: 0;
|
|
|
+ width: 100%;
|
|
|
+ }
|
|
|
+
|
|
|
+ .user-item {
|
|
|
+ margin: 28rpx 36rpx 0;
|
|
|
+ .avatar {
|
|
|
+ width: 50rpx;
|
|
|
+ height: 50rpx;
|
|
|
+ border-radius: 50%;
|
|
|
+ margin-right: 24rpx;
|
|
|
+ border: 1px solid #eee;
|
|
|
+ }
|
|
|
+ .user-info {
|
|
|
+ color: rgba(51, 51, 51, 1);
|
|
|
+ font-size: 28rpx;
|
|
|
+ line-height: 32rpx;
|
|
|
+ .user-name {
|
|
|
+ max-width: 300rpx;
|
|
|
+ overflow: hidden;
|
|
|
+ text-overflow: ellipsis;
|
|
|
+ white-space: nowrap;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ .time {
|
|
|
+ height: 36rpx;
|
|
|
+ color: rgba(153, 153, 153, 1);
|
|
|
+ font-size: 24rpx;
|
|
|
+ font-weight: 400;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ .list-empty,
|
|
|
+ .list-load-tip,
|
|
|
+ .list-nomore {
|
|
|
+ text-align: center;
|
|
|
+ padding: 32rpx;
|
|
|
+ font-size: 26rpx;
|
|
|
+ color: #999;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ .date-picker-panel {
|
|
|
+ background: #fff;
|
|
|
+ padding-bottom: constant(safe-area-inset-bottom);
|
|
|
+ padding-bottom: env(safe-area-inset-bottom);
|
|
|
+ }
|
|
|
+ .date-picker-header {
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ justify-content: space-between;
|
|
|
+ padding: 24rpx 32rpx;
|
|
|
+ border-bottom: 1rpx solid #eee;
|
|
|
+ }
|
|
|
+ .date-picker-title {
|
|
|
+ font-size: 32rpx;
|
|
|
+ font-weight: 500;
|
|
|
+ color: #303133;
|
|
|
+ }
|
|
|
+ .date-picker-btn {
|
|
|
+ font-size: 30rpx;
|
|
|
+ color: #606266;
|
|
|
+ padding: 8rpx 12rpx;
|
|
|
+ }
|
|
|
+ .date-picker-btn--primary {
|
|
|
+ color: #2979ff;
|
|
|
+ }
|
|
|
+ .date-picker-view-wrap {
|
|
|
+ position: relative;
|
|
|
+ width: 100%;
|
|
|
+ min-height: 250px;
|
|
|
+ }
|
|
|
+ .date-picker-ymd-mask {
|
|
|
+ position: absolute;
|
|
|
+ left: 25%;
|
|
|
+ top: 0;
|
|
|
+ width: 75%;
|
|
|
+ height: 100%;
|
|
|
+ z-index: 2;
|
|
|
+ background: rgba(0, 0, 0, 0.06);
|
|
|
+ }
|
|
|
+ .date-picker-view {
|
|
|
+ width: 100%;
|
|
|
+ /* 5 行 × 50px,与 uni-datetime-picker 一致,避免半行偏移 */
|
|
|
+ height: 250px;
|
|
|
+ }
|
|
|
+ .date-picker-item {
|
|
|
+ height: 50px;
|
|
|
+ line-height: 50px;
|
|
|
+ text-align: center;
|
|
|
+ box-sizing: border-box;
|
|
|
+ }
|
|
|
+ .date-picker-item-text {
|
|
|
+ font-size: 16px;
|
|
|
+ color: #303133;
|
|
|
+ line-height: 50px;
|
|
|
+ height: 50px;
|
|
|
+ }
|
|
|
+</style>
|