| | |
| | | <script setup lang="ts"> |
| | | import { onLaunch, onShow, onHide } from '@dcloudio/uni-app' |
| | | |
| | | import { useUserStore } from '@/store/user' |
| | | const userStore = useUserStore() |
| | | onLaunch(async (options: any) => { |
| | | |
| | | // 初始化用户状态 |
| | | userStore.initLoginState() |
| | | |
| | | const query = options.q ? decodeURIComponent(options.q) : '' |
| | | const querydto: Record<string, string> = {} |
| | | |
| | |
| | | |
| | | uni.getSystemSetting({ |
| | | success: (e: any) => { |
| | | debugger; |
| | | try { |
| | | const custom = uni.getMenuButtonBoundingClientRect() |
| | | uni.setStorageSync('StatusBar', e.statusBarHeight) |
| | |
| | | |
| | | <script setup lang="ts"> |
| | | import { ref, computed } from 'vue' |
| | | import { useNavigator } from '@/composables/useNavigator' |
| | | const { navigateTo, reLaunchTo} = useNavigator() |
| | | |
| | | interface TabItem { |
| | | text: string |
| | |
| | | }) |
| | | |
| | | function go(index: number, item: TabItem) { |
| | | uni.reLaunch({ |
| | | url: item.pagePath, |
| | | }) |
| | | // uni.reLaunch({ |
| | | // url: item.pagePath, |
| | | // }) |
| | | |
| | | reLaunchTo(item.pagePath) |
| | | |
| | | } |
| | | </script> |
| | | |
| | |
| | | <!-- 左侧图标 + 标题 --> |
| | | <view class="left"> |
| | | <up-icon name="/static/images/tabbar/line-Film.png" size="40rpx" color="#fff" /> |
| | | <text class="title">光影旅程</text> |
| | | <text class="title">影途</text> |
| | | </view> |
| | | |
| | | <!-- 右侧按钮 --> |
| | | <view class="right"> |
| | | <view class="login-view" @click="goLogin"> |
| | | <view class="login-view" @click="goLogin" v-if="!isLoggedIn"> |
| | | <up-icon name="/static/images/tabbar/user.png" size="30rpx" /> |
| | | <text class="login-text">登录</text> |
| | | </view> |
| | |
| | | </template> |
| | | |
| | | <script setup lang="ts"> |
| | | import { ref, onMounted } from 'vue' |
| | | |
| | | const CustomBar = ref<number>(uni.getStorageSync('CustomBar') || 0) |
| | | const StatusBar = ref<number>(uni.getStorageSync('StatusBar') || 0) |
| | | import { ref, onMounted ,computed} from 'vue' |
| | | import { useUserStore } from '@/store/user' |
| | | const userStore = useUserStore() |
| | | const isLoggedIn = computed(() => userStore.hasLogin) |
| | | console.log("登录状态") |
| | | console.log(isLoggedIn.value) |
| | | |
| | | // 可选:自动获取系统信息进行替代 |
| | | onMounted(() => { |
对比新文件 |
| | |
| | | import { ref } from 'vue' |
| | | import http from '@/plugins/http.js' |
| | | import { useUserStore } from '../store/user' |
| | | |
| | | const userStore = useUserStore() |
| | | export function useLocation() { |
| | | const latitude = ref<number | null>(null) |
| | | const longitude = ref<number | null>(null) |
| | | const error = ref<string | null>(null) |
| | | const address = ref<string | null>(null) |
| | | const province= ref<string | null>(null) |
| | | |
| | | const getLocation = () => { |
| | | uni.getLocation({ |
| | | type: 'wgs84', |
| | | geocode: true, |
| | | success: async (res:any) => { |
| | | latitude.value = res.latitude |
| | | longitude.value = res.longitude |
| | | const { |
| | | code,data |
| | | } = await http.request('get', '/api/pub/customer/home/address/parse', { |
| | | data: {}, |
| | | params: { |
| | | // https://apis.map.qq.com/ws/geocoder/v1/?location=39.984154,116.307490&key=[你的key]&get_poi=1 |
| | | location:`${res.latitude},${res.longitude}` |
| | | } |
| | | }) |
| | | |
| | | if (data && data.result && data.result.ad_info) { |
| | | address.value = data.result.address |
| | | province.value = data.result.ad_info.province |
| | | console.log("省的值",province.value ) |
| | | // 更新地址信息到缓存中 |
| | | userStore.updateAddress(data.result) |
| | | } |
| | | }, |
| | | fail: (err:any) => { |
| | | error.value = '获取位置失败:' + (err.errMsg || '未知错误') |
| | | }, |
| | | complete: function () { |
| | | console.log('getLocation 请求已完成'); |
| | | } |
| | | }) |
| | | } |
| | | |
| | | return { |
| | | latitude, |
| | | longitude, |
| | | address, |
| | | province, |
| | | error, |
| | | getLocation |
| | | } |
| | | } |
对比新文件 |
| | |
| | | import { useUserStore } from '@/store/user' |
| | | |
| | | export const useNavigator = () => { |
| | | const userStore = useUserStore() |
| | | // 黑名单列表:未登录时禁止访问这些路径 |
| | | const loginRequiredRoutes = [ |
| | | '/sub-pages/mine/settings', |
| | | '/pages/settings/index', |
| | | '/sub-pages/mine/index', |
| | | ] |
| | | |
| | | const navigateTo = (url: string) => { |
| | | const isLogin = userStore.hasLogin |
| | | const noQueryUrl = url.split('?')[0] |
| | | |
| | | if (!isLogin && loginRequiredRoutes.includes(noQueryUrl)) { |
| | | uni.navigateTo({ |
| | | url: '/pages/login/login' |
| | | }) |
| | | return |
| | | } |
| | | |
| | | uni.navigateTo({ url }) |
| | | } |
| | | |
| | | const reLaunchTo = (url: string) => { |
| | | |
| | | const isLogin = userStore.hasLogin |
| | | const noQueryUrl = url.split('?')[0] |
| | | |
| | | if (!isLogin && loginRequiredRoutes.includes(noQueryUrl)) { |
| | | uni.navigateTo({ |
| | | url: '/pages/login/login' |
| | | }) |
| | | return |
| | | } |
| | | |
| | | uni.reLaunch({ url }) |
| | | } |
| | | |
| | | const toProtocol = (title: string) => { |
| | | uni.navigateTo({ |
| | | url: `/sub-pages/protocol/protocol?title=${encodeURIComponent(title)}` |
| | | }) |
| | | } |
| | | |
| | | return { |
| | | navigateTo, |
| | | reLaunchTo, |
| | | toProtocol |
| | | } |
| | | } |
| | |
| | | import { createPinia } from 'pinia' |
| | | import message from './plugins/message' |
| | | import http from './plugins/http.js' |
| | | import store from './store' |
| | | // import store from './store/index.js' |
| | | |
| | | // #ifndef VUE3 |
| | | import Vue from 'vue' |
| | | import './uni.promisify.adaptor' |
| | | Vue.config.productionTip = false |
| | | App.mpType = 'app' |
| | | const app = new Vue({ |
| | | ...App |
| | | }) |
| | | app.$mount() |
| | | // #endif |
| | | |
| | | // #ifdef VUE3 |
| | | import { |
| | | createSSRApp |
| | | } from 'vue' |
| | |
| | | // 全局属性 |
| | | app.config.globalProperties.$message = message |
| | | app.config.globalProperties.$http = http |
| | | app.config.globalProperties.$store = store |
| | | // app.config.globalProperties.$store = store |
| | | |
| | | // uni.$u.setConfig({ |
| | | // config: { |
| | |
| | | app |
| | | } |
| | | } |
| | | // #endif |
| | |
| | | /* 小程序特有相关 */ |
| | | "mp-weixin" : { |
| | | // "appid" : "wx1441324401626290", |
| | | "appid" : "wxede6e3ad8e89693a", |
| | | "appid" : "wxf2e5534be77e3a80", |
| | | "setting" : { |
| | | "urlCheck" : false |
| | | "urlCheck" : false, |
| | | "es6" : true, |
| | | "minified" : true |
| | | }, |
| | | "usingComponents" : true, |
| | | "permission" : { |
| | | "scope.userLocation" : { |
| | | "desc" : "获取当前位置功能" |
| | | } |
| | | }, |
| | | "requiredPrivateInfos" : [ "chooseLocation", "getLocation" ], |
| | | "plugins": {}, |
| | | "lazyCodeLoading": "requiredComponents", |
| | | "mergeVirtualHostAttributes" : true |
| | | }, |
| | | "mp-alipay" : { |
| | |
| | | "uni-app": { |
| | | "scripts": { |
| | | "film": { |
| | | "title": "光影", |
| | | "title": "影途", |
| | | "BROWSER": "Chrome", |
| | | "env": { |
| | | "UNI_PLATFORM": "mp-weixin", |
| | |
| | | { |
| | | "path": "pages/home/home", |
| | | "style": { |
| | | "navigationBarTitleText": "片场" |
| | | "navigationBarTitleText": "影途", |
| | | "enablePullDownRefresh": true, |
| | | "onReachBottomDistance": 50 |
| | | } |
| | | }, |
| | | { |
| | |
| | | ], |
| | | "globalStyle": { |
| | | "navigationBarTextStyle": "black", |
| | | "navigationBarTitleText": "片场", |
| | | "navigationBarTitleText": "影途", |
| | | "navigationBarBackgroundColor": "#F8F8F8", |
| | | "backgroundColor": "#F8F8F8" |
| | | }, |
| | |
| | | <template> |
| | | <view class="post-card"> |
| | | <!-- 用户信息 --> |
| | | <view class="user-info"> |
| | | <up-avatar :src="avatar" size="80rpx" shape="circle" /> |
| | | <view class="user-text"> |
| | | <text class="nickname">{{ nickname }}</text> |
| | | <text class="time">{{ time }}</text> |
| | | </view> |
| | | <view class="post-card"> |
| | | <!-- 用户信息 --> |
| | | <view class="user-info"> |
| | | <up-avatar :src="avatar" size="80rpx" shape="circle" /> |
| | | <view class="user-text"> |
| | | <text class="nickname">{{ nickname }}</text> |
| | | <text class="time">{{ time }}</text> |
| | | </view> |
| | | |
| | | <view class="inner-content"> |
| | | <!-- 内容图片 --> |
| | | <image class="main-image" :src="image" mode="aspectFill" /> |
| | | |
| | | <!-- 描述内容 --> |
| | | <view class="description"> |
| | | {{ content }} |
| | | </view> |
| | | |
| | | <view class="inner-content"> |
| | | <!-- 内容图片 --> |
| | | <image class="main-image" :src="image" mode="aspectFill" /> |
| | | <!-- <image class="main-image" :src="image" mode="aspectFit" /> --> |
| | | <!-- <image class="main-image" :src="image" mode="scaleToFill" /> --> |
| | | <!-- <view class="main-image-wrapper" :style="{ backgroundImage: `url(${image})` }"></view> --> |
| | | <!-- 下面图片可以展示完全,但是拉伸变得模糊 --> |
| | | <!-- <image :src="image" class="main-image" ></image> --> |
| | | <!-- <up-image :show-loading="true" :src="image" width="100%" shape="square"></up-image> --> |
| | | <!-- 动态计算高度:能够展示完全,高度会拉伸很高 --> |
| | | <!-- <up-image v-if="imageHeight > 0" :src="image" width="100%" :height="imageHeight + 'px'" shape="square" |
| | | :show-loading="true" /> --> |
| | | <!-- 描述内容 --> |
| | | <view class="description"> |
| | | {{ content }} |
| | | </view> |
| | | |
| | | <!-- 底部操作栏 --> |
| | | <view class="footer"> |
| | | <view class="action"> |
| | | <up-icon name="heart-fill" size="30rpx" color="#fff" /> |
| | | <text class="count">{{ likeCount }}</text> |
| | | </view> |
| | | |
| | | <!-- 底部操作栏 --> |
| | | <view class="footer"> |
| | | <view class="action"> |
| | | <up-icon name="heart-fill" size="30rpx" color="#fff" /> |
| | | <text class="count">{{ likeCount }}</text> |
| | | </view> |
| | | <view class="action"> |
| | | <up-icon name="chat-fill" size="30rpx" color="#fff" /> |
| | | <text class="count">{{ commentCount }}</text> |
| | | </view> |
| | | <view class="action"> |
| | | <up-icon name="share" size="30rpx" color="#fff" /> |
| | | </view> |
| | | <view class="action"> |
| | | <up-icon name="chat-fill" size="30rpx" color="#fff" /> |
| | | <text class="count">{{ commentCount }}</text> |
| | | </view> |
| | | <view class="action"> |
| | | <up-icon name="share" size="30rpx" color="#fff" /> |
| | | </view> |
| | | </view> |
| | | </view> |
| | | </template> |
| | | |
| | | <script setup lang="ts"> |
| | | interface Props { |
| | | avatar?: string; |
| | | nickname?: string; |
| | | time?: string; |
| | | image?: string; |
| | | content?: string; |
| | | likeCount?: number | string; |
| | | commentCount?: number | string; |
| | | } |
| | | |
| | | const props = defineProps<Props>(); |
| | | </script> |
| | | |
| | | <style scoped lang="scss"> |
| | | .post-card { |
| | | background-color: #1e1e1e; |
| | | border-radius: 24rpx; |
| | | padding: 10rpx; |
| | | color: #fff; |
| | | font-size: 28rpx; |
| | | margin-top: 20rpx; |
| | | } |
| | | |
| | | .user-info { |
| | | margin: 20rpx 30rpx; |
| | | display: flex; |
| | | align-items: center; |
| | | } |
| | | |
| | | .user-text { |
| | | font-size: 24rpx; |
| | | margin-left: 16rpx; |
| | | } |
| | | |
| | | .nickname { |
| | | font-weight: bold; |
| | | display: block; |
| | | } |
| | | |
| | | .time { |
| | | font-size: 18rpx; |
| | | color: #aaa; |
| | | } |
| | | |
| | | .inner-content { |
| | | margin: 0 20rpx; |
| | | } |
| | | |
| | | .main-image { |
| | | width: 100%; |
| | | height: 250rpx; |
| | | border-radius: 24rpx; |
| | | } |
| | | |
| | | .description { |
| | | margin: 20rpx 0; |
| | | font-size: 24rpx; |
| | | font-weight: bold; |
| | | color: #ddd; |
| | | letter-spacing: 2rpx; |
| | | } |
| | | |
| | | .footer { |
| | | display: flex; |
| | | justify-content: space-between; |
| | | align-items: center; |
| | | } |
| | | |
| | | .action { |
| | | display: flex; |
| | | align-items: center; |
| | | } |
| | | |
| | | .count { |
| | | margin-left: 10rpx; |
| | | font-size: 26rpx; |
| | | color: #ccc; |
| | | } |
| | | </style> |
| | | |
| | | </view> |
| | | </template> |
| | | |
| | | <script setup lang="ts"> |
| | | import { ref, watch,onMounted } from 'vue' |
| | | import { onReady } from '@dcloudio/uni-app' |
| | | |
| | | interface Props { |
| | | avatar?: string; |
| | | nickname?: string; |
| | | time?: string; |
| | | image?: string; |
| | | content?: string; |
| | | likeCount?: number | string; |
| | | commentCount?: number | string; |
| | | } |
| | | |
| | | const props = defineProps<Props>(); |
| | | const imageHeight = ref(0) |
| | | |
| | | const computeImageHeight = (src?: string) => { |
| | | if (!src) return |
| | | uni.getImageInfo({ |
| | | src, |
| | | success: (res) => { |
| | | const screenWidth = uni.getSystemSetting().windowWidth |
| | | imageHeight.value = screenWidth * (res.height / res.width) |
| | | }, |
| | | fail: () => { |
| | | console.warn('获取图片信息失败') |
| | | } |
| | | }) |
| | | } |
| | | |
| | | watch( |
| | | () => props.image, |
| | | (newImage) => { |
| | | if (!newImage) return |
| | | computeImageHeight(newImage) |
| | | }, |
| | | { immediate: true } |
| | | ) |
| | | |
| | | onReady(() => { |
| | | computeImageHeight(props.image) |
| | | }) |
| | | </script> |
| | | |
| | | <style scoped lang="scss"> |
| | | .post-card { |
| | | background-color: #1e1e1e; |
| | | border-radius: 24rpx; |
| | | padding: 10rpx; |
| | | color: #fff; |
| | | font-size: 28rpx; |
| | | margin-top: 20rpx; |
| | | } |
| | | |
| | | .user-info { |
| | | margin: 20rpx 30rpx; |
| | | display: flex; |
| | | align-items: center; |
| | | } |
| | | |
| | | .user-text { |
| | | font-size: 24rpx; |
| | | margin-left: 16rpx; |
| | | } |
| | | |
| | | .nickname { |
| | | font-weight: bold; |
| | | display: block; |
| | | } |
| | | |
| | | .time { |
| | | font-size: 18rpx; |
| | | color: #aaa; |
| | | } |
| | | |
| | | .inner-content { |
| | | margin: 0 20rpx; |
| | | } |
| | | |
| | | .main-image { |
| | | width: 100%; |
| | | height: 250rpx; |
| | | border-radius: 24rpx; |
| | | } |
| | | |
| | | .description { |
| | | margin: 20rpx 0; |
| | | font-size: 24rpx; |
| | | font-weight: bold; |
| | | color: #ddd; |
| | | letter-spacing: 2rpx; |
| | | } |
| | | |
| | | .footer { |
| | | display: flex; |
| | | justify-content: space-between; |
| | | align-items: center; |
| | | } |
| | | |
| | | .action { |
| | | display: flex; |
| | | align-items: center; |
| | | } |
| | | |
| | | .count { |
| | | margin-left: 10rpx; |
| | | font-size: 26rpx; |
| | | color: #ccc; |
| | | } |
| | | |
| | | .main-image-wrapper { |
| | | width: 100%; |
| | | width: 500px; |
| | | height: 200px; |
| | | /* 你可以根据需求自适应设置 */ |
| | | background-size: contain; |
| | | /* 保证完整显示 */ |
| | | background-repeat: no-repeat; |
| | | background-position: center; |
| | | } |
| | | </style> |
| | |
| | | <template> |
| | | <view> |
| | | <view class="card" v-if="!showVideo"> |
| | | <!-- <view class="card" v-if="!showVideo"> --> |
| | | <view class="card"> |
| | | <view class="main-title">每一帧画面,都藏着一个等待探索的世界</view> |
| | | <view class="sub-title"> |
| | | 从经典场景到幕后故事,开启你的专属影视朝圣之旅 |
| | |
| | | </view> |
| | | |
| | | <!-- 视频播放区域 --> |
| | | <view v-else class="video-wrapper"> |
| | | <!-- <view v-else class="video-wrapper"> |
| | | <video |
| | | :src="currentVideo" |
| | | controls |
| | |
| | | @ended="onVideoEnd" |
| | | style="width: 100%; height: 400rpx;" |
| | | ></video> |
| | | </view> |
| | | </view> --> |
| | | </view> |
| | | </template> |
| | | |
| | |
| | | <Community v-for="(item, index) in communitys" :key="index" :avatar="item.avatar" :nickname="item.nickname" |
| | | :time="formatRelativeTime(item.createTime)" :image="item.coverUrl" :content="item.coverAlt" |
| | | :likeCount="item.likeCount" :commentCount="item.commentCount" /> |
| | | |
| | | <up-loadmore :status="communityStatus" :line="true" /> |
| | | <view style="height: 300rpx;"></view> |
| | | </view> |
| | | |
| | |
| | | |
| | | <script setup lang="ts"> |
| | | import { ref, computed, onMounted } from 'vue' |
| | | import { onShow } from '@dcloudio/uni-app' |
| | | import { onShow, onPullDownRefresh, onReachBottom } from '@dcloudio/uni-app' |
| | | |
| | | import HomeMain from './home-main.vue' |
| | | import TripCard from './trip-card.vue' |
| | |
| | | import { FilmWorks } from '@/types/index' |
| | | import { formatRelativeTime } from '@/utils/time' |
| | | import { FilmWorksCategory } from '@/enums/dict' |
| | | |
| | | |
| | | // 主题 |
| | | const theme = ref('light') |
| | |
| | | getCommunitys() |
| | | }); |
| | | |
| | | async function getContentSelected() { |
| | | tripCardList.value = await getFilmWorks(FilmWorksCategory.CONTENT_SELECTED, 20); |
| | | console.log('内容精选', tripCardList.value); |
| | | |
| | | onPullDownRefresh(async () => { |
| | | console.log('用户下拉刷新了') |
| | | // 内容精选 |
| | | getContentSelected() |
| | | // 光影社区 |
| | | communityPage.value = 1 |
| | | getCommunitys() |
| | | uni.stopPullDownRefresh() // 停止下拉刷新动画 |
| | | }) |
| | | |
| | | onReachBottom(() => { |
| | | console.log('用户触底了') |
| | | getCommunitys() |
| | | }) |
| | | |
| | | const getContentSelected = async () => { |
| | | tripCardList.value = await getFilmWorks(FilmWorksCategory.CONTENT_SELECTED, 20, 1); |
| | | } |
| | | |
| | | async function getCommunitys() { |
| | | communitys.value = await getFilmWorks(FilmWorksCategory.COMMUNITY, 10); |
| | | |
| | | const communityPage = ref(1) |
| | | const communitySize = 10 |
| | | const communityStatus = ref('loading') |
| | | |
| | | |
| | | const getCommunitys = async () => { |
| | | if (communityStatus.value === 'nomore') return |
| | | |
| | | communityStatus.value = 'loading' |
| | | |
| | | const records = await getFilmWorks(FilmWorksCategory.COMMUNITY, communitySize, communityPage.value) |
| | | |
| | | if (records && records.length > 0) { |
| | | // 使用 Set 进行去重 |
| | | const existingIds = new Set(communitys.value.map(item => item.id)) |
| | | const uniqueRecords = records.filter(item => !existingIds.has(item.id)) |
| | | |
| | | communitys.value = [...communitys.value, ...uniqueRecords] |
| | | |
| | | // 如果返回的记录数少于请求的 size,说明没有更多数据 |
| | | if (records.length < communitySize) { |
| | | communityStatus.value = 'noMore' |
| | | } |
| | | |
| | | communityPage.value++ |
| | | } else { |
| | | communityStatus.value = 'noMore' |
| | | } |
| | | } |
| | | const getCommunitys_bak = async () => { |
| | | communitys.value = await getFilmWorks(FilmWorksCategory.COMMUNITY, 10, 1); |
| | | } |
| | | // 内容精选 |
| | | const getFilmWorks = async (type: String, pageSize: Number) => { |
| | | const getFilmWorks = async (type: String, pageSize: Number, currentPage: Number) => { |
| | | const { |
| | | code, data |
| | | } = await $http.request('get', '/api/filmWorks/list', { |
| | | params: { |
| | | classify: type, |
| | | size: pageSize, |
| | | current: currentPage |
| | | } |
| | | }) |
| | | if (code == 0) { |
| | |
| | | </view> |
| | | |
| | | <view class="logo-container"> |
| | | <text class="app-name">光影</text> |
| | | <text class="app-name">影途</text> |
| | | <text class="subtitle">登录后,体验更多功能</text> |
| | | </view> |
| | | |
| | |
| | | import { onShow } from '@dcloudio/uni-app' |
| | | import { useGlobal } from '@/composables/useGlobal' |
| | | import { usePlatformLoginType } from '@/composables/usePlatformLoginType' |
| | | import { useUserStore } from '@/store/user' |
| | | |
| | | const { apitype } = usePlatformLoginType() |
| | | const { $http, $message,$store } = useGlobal() |
| | | const { $http, $message, $store } = useGlobal() |
| | | const userStore = useUserStore() |
| | | |
| | | const isChecked = ref(false) |
| | | const toggleCheck = () => { |
| | |
| | | uni.showToast({ title: '请先同意协议', icon: 'none' }); |
| | | return; |
| | | } |
| | | // 微信登录逻辑 |
| | | uni.showToast({ title: '微信登录', icon: 'success' }); |
| | | // 微信登录 |
| | | uni.login({ |
| | | "provider": "weixin", |
| | | "onlyAuthorize": true, // 微信登录仅请求授权认证 |
| | | success: async function (event:any) { |
| | | const { code } = event |
| | | $message.showLoading(); |
| | | const resp = await userStore.loginwx({ code, phoneNumber: wxUser.value.phoneNumber, purePhoneNumber: wxUser.value.purePhoneNumber }) |
| | | if (resp && resp.data) { |
| | | // that.$forceUpdate() |
| | | uni.reLaunch({ |
| | | url: '/pages/home/home' |
| | | }) |
| | | } |
| | | $message.hideLoading(); |
| | | }, |
| | | fail: function (err:any) { |
| | | // 登录授权失败 |
| | | // err.code是错误码 |
| | | } |
| | | }) |
| | | |
| | | |
| | | }; |
| | | |
| | | onShow(() => { |
| | |
| | | "provider": "weixin", |
| | | "onlyAuthorize": true, // 微信登录仅请求授权认证 |
| | | success: async function (event: any) { |
| | | console.log("aaaa") |
| | | console.log(event) |
| | | const { code } = event |
| | | let openIdExistFlag = await getExistUserByOpenId(code); |
| | | console.log(openIdExistFlag) |
| | | console.log(wxUser.value) |
| | | |
| | | }, |
| | | fail: function (err: any) { |
| | | // 登录授权失败 |
| | |
| | | } |
| | | }) |
| | | if (code == 0) { |
| | | console.log("查看当前用户是否已经绑定过") |
| | | console.log(data) |
| | | wxUser.value = data |
| | | if (wxUser && wxUser.user) { |
| | | return true |
| | |
| | | } |
| | | } |
| | | |
| | | const handleGetPhoneNumber = async (e:any) => { |
| | | const handleGetPhoneNumber = async (e: any) => { |
| | | |
| | | if (!isChecked.value) { |
| | | $message.showToast('请同意用户协议') |
| | | return |
| | | } |
| | | debugger; |
| | | console.log(e) |
| | | console.log(e.detail.code) // 动态令牌 |
| | | console.log(e.detail.errMsg) // 回调信息(成功失败都会返回) |
| | | console.log(e.detail.errno) // 错误码(失败时返回) |
| | | |
| | | if (e.detail.errMsg == 'getPhoneNumber:ok') { |
| | | console.log('获取成功') |
| | | const { |
| | |
| | | } |
| | | }) |
| | | if (code == 0) { |
| | | |
| | | // 获取手机号码后,实现微信登录 |
| | | console.log(data) |
| | | const phoneNumber = data.phone_info.phoneNumber; |
| | | const purePhoneNumber = data.phone_info.purePhoneNumber; |
| | | wxUser.value.phoneNumber = phoneNumber |
| | | wxUser.value.purePhoneNumber = purePhoneNumber |
| | | // that.handleWechatClick() |
| | | handleWechatClick() |
| | | } else { |
| | | |
| | | } |
| | |
| | | uni.login({ |
| | | "provider": "weixin", |
| | | "onlyAuthorize": true, // 微信登录仅请求授权认证 |
| | | success: async function (event) { |
| | | success: async function (event: any) { |
| | | const { code } = event |
| | | $message.showLoading(); |
| | | const resp = await $store.dispatch('loginwx', { code, phoneNumber: wxUser.phoneNumber, purePhoneNumber: wxUser.purePhoneNumber }) |
| | | console.log("获取到的微信用户信息是:", wxUser.value) |
| | | const resp = await userStore.loginwx({ code, phoneNumber: wxUser.value.phoneNumber, purePhoneNumber: wxUser.value.purePhoneNumber }) |
| | | if (resp && resp.data) { |
| | | // $forceUpdate() |
| | | uni.reLaunch({ |
| | |
| | | } |
| | | $message.hideLoading(); |
| | | }, |
| | | fail: function (err) { |
| | | fail: function (err: any) { |
| | | // 登录授权失败 |
| | | // err.code是错误码 |
| | | } |
| | |
| | | import environments from '@/environments' |
| | | import message from './message' |
| | | import storage from './storage' |
| | | import store from '../store' |
| | | // import store from '../store' |
| | | import utils from './util.js' |
| | | |
| | | let pre = 'api' |
| | |
| | | console.log('resp', res) |
| | | // storage.removeItem('token') |
| | | // await store.dispatch('logout') |
| | | store.commit('updateLogin', false) |
| | | // TODO |
| | | // store.commit('updateLogin', false) |
| | | |
| | | await message.showToast('登录信息失效') |
| | | |
| | | // #ifdef PUB_CUSTOMER |
| | | uni.reLaunch({ |
| | | url: '/pages/user/supplier-user' |
| | | url: '/pages/login/login' |
| | | }) |
| | | // #endif |
| | | // #ifndef PUB_CUSTOMER |
| | | uni.reLaunch({ |
| | | url: '/pages/login/supplier-login' |
| | | url: '/pages/login/login' |
| | | }) |
| | | // #endif |
| | | reject({ |
| | |
| | | console.log('401', res) |
| | | // storage.removeItem('token') |
| | | // await store.dispatch('logout') |
| | | store.commit('updateLogin', false) |
| | | // TODO |
| | | // store.commit('updateLogin', false) |
| | | await message.showToast('登录信息失效') |
| | | |
| | | |
| | |
| | | // #endif |
| | | // #ifndef PUB_CUSTOMER |
| | | uni.reLaunch({ |
| | | url: '/pages/login/supplier-login' |
| | | url: '/pages/login/login' |
| | | }) |
| | | // #endif |
| | | reject({ |
| | |
| | | import http from '../plugins/http' |
| | | import http from '../plugins/http.js' |
| | | import storage from '../plugins/storage.js' |
| | | import message from '../plugins/message.js' |
| | | import Vue from 'vue' |
| | |
| | | // store/index.js |
| | | import { createStore } from 'vuex' |
| | | import http from '../plugins/http' |
| | | import storage from '../plugins/storage.js' |
| | | import message from '../plugins/message.js' |
| | | |
| | | const store = createStore({ |
| | | state() { |
| | | return { |
| | | hasLogin: false, |
| | | isUniverifyLogin: false, |
| | | loginProvider: '', |
| | | openid: null, |
| | | testvuex: false, |
| | | colorIndex: 0, |
| | | colorList: ['#FF0000', '#00FF00', '#0000FF'], |
| | | noMatchLeftWindow: true, |
| | | active: 'componentPage', |
| | | leftWinActive: '/pages/component/view/view', |
| | | activeOpen: '', |
| | | menu: [], |
| | | appMenu: [], |
| | | univerifyErrorMsg: '', |
| | | currentInfo: {}, |
| | | cache_address: {}, |
| | | sign: { |
| | | enterprise: 0, |
| | | info: 0, |
| | | flower: 0, |
| | | shopping: 0, |
| | | follow: 0, |
| | | delivery: 0, |
| | | order: 0, |
| | | shopnum: 0, |
| | | coupon: 0, |
| | | cache_topay: 0, |
| | | }, |
| | | cache: { |
| | | coupon: {}, |
| | | goods: [], |
| | | cache_coupon_select_cancel: 0, |
| | | }, |
| | | defaultaddress: {}, |
| | | addressDesc: '', |
| | | } |
| | | }, |
| | | mutations: { |
| | | updateLogin(state, provider) { |
| | | state.hasLogin = !!provider |
| | | if (!state.hasLogin) { |
| | | state.currentInfo = {} |
| | | storage.removeItem('token') |
| | | } |
| | | }, |
| | | login(state, provider) { |
| | | state.hasLogin = true |
| | | state.loginProvider = provider |
| | | }, |
| | | logout(state) { |
| | | state.hasLogin = false |
| | | state.openid = null |
| | | state.currentInfo = {} |
| | | state.appMenu = [] |
| | | storage.removeItem('token') |
| | | storage.removeItem('appMenu') |
| | | message.showToast('退出登录成功') |
| | | }, |
| | | setOpenid(state, openid) { |
| | | state.openid = openid |
| | | }, |
| | | setDefaultAddress(state, defaultaddress) { |
| | | state.defaultaddress = defaultaddress |
| | | }, |
| | | setAddressDesc(state, addressDesc) { |
| | | state.addressDesc = addressDesc |
| | | storage.setItem('defaultaddress', addressDesc || '') |
| | | }, |
| | | setTestTrue(state) { |
| | | state.testvuex = true |
| | | }, |
| | | setTestFalse(state) { |
| | | state.testvuex = false |
| | | }, |
| | | setColorIndex(state, index) { |
| | | state.colorIndex = index |
| | | }, |
| | | setActive(state, tabPage) { |
| | | state.active = tabPage |
| | | }, |
| | | setActiveOpen(state, activeOpen) { |
| | | state.activeOpen = activeOpen |
| | | }, |
| | | setMenu(state, menu) { |
| | | state.menu = menu |
| | | }, |
| | | setUniverifyLogin(state, payload) { |
| | | state.isUniverifyLogin = !!payload |
| | | }, |
| | | setUniverifyErrorMsg(state, payload = '') { |
| | | state.univerifyErrorMsg = payload |
| | | } |
| | | }, |
| | | getters: { |
| | | currentColor(state) { |
| | | return state.colorList[state.colorIndex] |
| | | } |
| | | }, |
| | | actions: { |
| | | async sign_add({ state }, key) { |
| | | state.sign[key] = 1 |
| | | }, |
| | | async sign_clear({ state }, key) { |
| | | state.sign[key] = 0 |
| | | }, |
| | | async cache_coupon_select({ state }, dto) { |
| | | state.cache.coupon = dto || {} |
| | | }, |
| | | async cache_coupon_select_cancel({ state }, val) { |
| | | state.cache.cache_coupon_select_cancel = val || 0 |
| | | }, |
| | | async cache_goods_select({ state }, goods) { |
| | | state.cache.goods = goods || [] |
| | | }, |
| | | async logout({ commit }) { |
| | | commit('logout') |
| | | }, |
| | | async getUserOpenId({ state, commit }) { |
| | | if (state.openid) return state.openid |
| | | return new Promise((resolve, reject) => { |
| | | uni.login({ |
| | | success(data) { |
| | | commit('login') |
| | | setTimeout(() => { |
| | | const openid = '123456789' |
| | | commit('setOpenid', openid) |
| | | resolve(openid) |
| | | }, 1000) |
| | | }, |
| | | fail(err) { |
| | | reject(err) |
| | | } |
| | | }) |
| | | }) |
| | | }, |
| | | async getCurrentInfo({ commit, state }) { |
| | | const currentInfo = await http.request('get', '/api/current/user', {}) |
| | | if (currentInfo && currentInfo.code === 0) { |
| | | state.currentInfo = currentInfo.data || {} |
| | | state.type = currentInfo.data.type || '' |
| | | state.spacecode = currentInfo.data.spacecode || '' |
| | | commit('updateLogin', true) |
| | | } else { |
| | | commit('updateLogin', false) |
| | | } |
| | | }, |
| | | async getSwitchSubAccount({ commit, state }, payload) { |
| | | const currentInfo = await http.request('get', '/api/supplierSub/getSwitchById', { |
| | | params: { |
| | | id: payload.id, |
| | | type: payload.type |
| | | } |
| | | }) |
| | | if (currentInfo && currentInfo.code === 0) { |
| | | state.currentInfo = currentInfo.data || {} |
| | | state.type = currentInfo.data.type || '' |
| | | state.spacecode = currentInfo.data.spacecode || '' |
| | | commit('updateLogin', true) |
| | | } else { |
| | | commit('updateLogin', false) |
| | | } |
| | | return currentInfo |
| | | }, |
| | | async getAppMenu({ state }) { |
| | | const menu = await http.request('get', '/api/app/menu/permission/menu', {}) |
| | | storage.setItem('appMenu', []) |
| | | if (menu && menu.code === 0) { |
| | | const appMenu = menu.data || [] |
| | | storage.setItem('appMenu', appMenu) |
| | | } |
| | | }, |
| | | async getAppMenuSupplier({ state }) { |
| | | const menu = await http.request('get', '/api/app/menu/supplier/permission/menu', {}) |
| | | storage.setItem('appMenu', []) |
| | | if (menu && menu.code === 0) { |
| | | const appMenu = menu.data || [] |
| | | storage.setItem('appMenu', appMenu) |
| | | } |
| | | }, |
| | | async getAppMenuPartner({ state }) { |
| | | const menu = await http.request('get', '/api/app/menu/partner/permission/menu', {}) |
| | | storage.setItem('appMenu', []) |
| | | if (menu && menu.code === 0) { |
| | | const appMenu = menu.data || [] |
| | | storage.setItem('appMenu', appMenu) |
| | | } |
| | | }, |
| | | async loginwx({ dispatch, state }, data) { |
| | | const resp = await http.request('post', '/api/login/wechat', { |
| | | data: { |
| | | code: data.code, |
| | | imgurl: data.imgurl || '', |
| | | nickname: data.nickname || '', |
| | | inviter: data.inviter || '', |
| | | phoneNumber: data.phoneNumber || '', |
| | | purePhoneNumber: data.purePhoneNumber || '', |
| | | } |
| | | }) |
| | | if (resp && resp.code === 0) { |
| | | state.isBind = true |
| | | storage.setItem('token', resp.data.access_token || '') |
| | | state.hasLogin = true |
| | | if (data.inviter) { |
| | | storage.removeItem('inviter') |
| | | storage.removeItem('inviterTime') |
| | | storage.removeItem('inviterName') |
| | | } |
| | | await dispatch('getCurrentInfo') |
| | | } else { |
| | | message.showToast('登录失败: ' + (resp && resp.msg)) |
| | | storage.removeItem('openid') |
| | | storage.removeItem('tel') |
| | | storage.removeItem('token') |
| | | } |
| | | return resp |
| | | } |
| | | } |
| | | }) |
| | | |
| | | export default store |
对比新文件 |
| | |
| | | // stores/user.ts |
| | | import { defineStore } from 'pinia' |
| | | import { ref } from 'vue' |
| | | import http from '@/plugins/http.js' |
| | | import storage from '@/plugins/storage' |
| | | import message from '@/plugins/message' |
| | | import { WechatLoginData } from '@/types/index' |
| | | |
| | | |
| | | export const useUserStore = defineStore('user', () => { |
| | | const hasLogin = ref(false) |
| | | const isBind = ref(false) |
| | | |
| | | async function getCurrentInfo() { |
| | | // 模拟请求用户信息 |
| | | console.log('获取当前用户信息') |
| | | // 这里你可以请求 /api/user/info 等接口获取用户数据 |
| | | } |
| | | |
| | | async function loginwx(data: WechatLoginData) { |
| | | const { |
| | | code, |
| | | imgurl = '', |
| | | nickname = '', |
| | | inviter = '', |
| | | phoneNumber = '', |
| | | purePhoneNumber = '' |
| | | } = data |
| | | |
| | | const resp = await http.request('post', '/api/login/wechat', { |
| | | data: { |
| | | code, |
| | | imgurl, |
| | | nickname, |
| | | inviter, |
| | | phoneNumber, |
| | | purePhoneNumber |
| | | } |
| | | }) |
| | | |
| | | if (resp && resp.code === 0) { |
| | | isBind.value = true |
| | | storage.setItem('token', resp.data.access_token || '') |
| | | hasLogin.value = true |
| | | |
| | | if (inviter) { |
| | | storage.removeItem('inviter') |
| | | storage.removeItem('inviterTime') |
| | | storage.removeItem('inviterName') |
| | | } |
| | | |
| | | await getCurrentInfo() |
| | | } else { |
| | | message.showToast('登录失败:' + (resp?.msg || '未知错误')) |
| | | storage.removeItem('openid') |
| | | storage.removeItem('tel') |
| | | storage.removeItem('token') |
| | | } |
| | | |
| | | return resp |
| | | } |
| | | |
| | | return { |
| | | hasLogin, |
| | | isBind, |
| | | loginwx, |
| | | getCurrentInfo |
| | | } |
| | | }) |
| | |
| | | // stores/user.ts |
| | | import { defineStore } from 'pinia' |
| | | import { ref } from 'vue' |
| | | import http from '@/plugins/http.js' // 请替换成你实际的 http 封装路径 |
| | | import storage from '@/plugins/storage' // 同样替换为你的封装路径 |
| | | import message from '@/plugins/message' // 你使用的消息组件封装 |
| | | import { WechatLoginData } from '@/types/index' |
| | | |
| | | |
| | | export const useUserStore = defineStore('user', () => { |
| | | |
| | | }) |
| | | const hasLogin = ref(false) |
| | | const isBind = ref(false) |
| | | const userInfo = ref<any>(null) |
| | | const address = ref<any>(null) |
| | | |
| | | async function getCurrentInfo() { |
| | | const currentInfo = await http.request('get', '/api/current/user', {}) |
| | | if (currentInfo && currentInfo.code == 0) { |
| | | console.log('当前用户信息', currentInfo) |
| | | userInfo.value = currentInfo.data // 赋值到 store |
| | | } else { |
| | | message.showToast('获取用户信息失败') |
| | | } |
| | | } |
| | | |
| | | async function loginwx(data: WechatLoginData) { |
| | | const { |
| | | code, |
| | | imgurl = '', |
| | | nickname = '', |
| | | inviter = '', |
| | | phoneNumber = '', |
| | | purePhoneNumber = '' |
| | | } = data |
| | | |
| | | const resp = await http.request('post', '/api/login/wechat', { |
| | | data: { |
| | | code, |
| | | imgurl, |
| | | nickname, |
| | | inviter, |
| | | phoneNumber, |
| | | purePhoneNumber |
| | | } |
| | | }) |
| | | |
| | | if (resp && resp.code === 0) { |
| | | |
| | | isBind.value = true |
| | | storage.setItem('token', resp.data.access_token || '') |
| | | hasLogin.value = true |
| | | await getCurrentInfo() |
| | | } else { |
| | | message.showToast('登录失败:' + (resp?.msg || '未知错误')) |
| | | storage.removeItem('openid') |
| | | storage.removeItem('tel') |
| | | storage.removeItem('token') |
| | | } |
| | | |
| | | return resp |
| | | } |
| | | |
| | | |
| | | function initLoginState() { |
| | | const token = storage.getItem('token') |
| | | if (token) { |
| | | hasLogin.value = true |
| | | |
| | | const savedUserInfo = storage.getItem('userInfo') |
| | | if (savedUserInfo) { |
| | | userInfo.value = savedUserInfo |
| | | } else { |
| | | getCurrentInfo() |
| | | } |
| | | } else { |
| | | hasLogin.value = false |
| | | } |
| | | } |
| | | |
| | | |
| | | function updateAddress(addressParam:any) { |
| | | |
| | | // 合并新信息到 userInfo 中 |
| | | console.log("addressParam",addressParam) |
| | | address.value = addressParam |
| | | // 如果你有持久化,这里也同步一下 |
| | | // storage.setItem('userInfo', userInfo.value) |
| | | } |
| | | |
| | | |
| | | return { |
| | | hasLogin, |
| | | isBind, |
| | | userInfo, |
| | | address, |
| | | loginwx, |
| | | getCurrentInfo, |
| | | initLoginState, |
| | | updateAddress, |
| | | } |
| | | }) |
| | |
| | | <view class="profile-content"> |
| | | <view class="profile-header"> |
| | | <view class="avatar-container"> |
| | | <image class="avatar" src="https://ai-public.mastergo.com/ai/img_res/e8ae645c666c247b895b488e60b048f5.jpg" |
| | | mode="aspectFill" /> |
| | | <image v-if="userStore.userInfo.customerDTO.cover" class="avatar" |
| | | :src="userStore.userInfo.customerDTO.cover" mode="aspectFill" /> |
| | | <image v-else class="avatar" |
| | | src="https://ai-public.mastergo.com/ai/img_res/e8ae645c666c247b895b488e60b048f5.jpg" mode="aspectFill" /> |
| | | <view class="add-icon"> |
| | | <up-icon name="plus" size="20rpx" color="#333" /> |
| | | </view> |
| | | </view> |
| | | <view class="user-info"> |
| | | <view class="username">小红薯 6786F040</view> |
| | | <view class="user-id">小红书号:95619601810</view> |
| | | <view class="location">IP 属地:江苏</view> |
| | | <view class="username">影途 {{ userStore?.userInfo?.customerDTO?.name }}</view> |
| | | <view class="user-id">影途号:{{ userStore?.userInfo?.customerDTO?.id }}</view> |
| | | <view class="location">IP 属地:{{ userStore.address?.ad_info?.province }}</view> |
| | | </view> |
| | | </view> |
| | | |
| | |
| | | <view class="action-buttons"> |
| | | <up-button text="编辑资料" size="mini" type="info" plain hairline |
| | | :custom-style="{ backgroundColor: 'transparent', borderColor: '#B1ABA9' }" /> |
| | | <up-icon name="setting" size="40rpx" /> |
| | | <up-icon name="setting" size="40rpx" @click="gotoUrl('/sub-pages/mine/settings')" /> |
| | | </view> |
| | | </view> |
| | | </view> |
| | |
| | | </template> |
| | | |
| | | <script setup lang="ts"> |
| | | import { ref, computed } from 'vue'; |
| | | import { ref, computed, watch } from 'vue'; |
| | | import { onShow } from '@dcloudio/uni-app' |
| | | import SettingPopup from '@/components/setting/setting-popup.vue'; |
| | | import { usePlatformLoginType } from '@/composables/usePlatformLoginType' |
| | | import { useUserStore } from '@/store/user' |
| | | import { useLocation } from '@/composables/useLocation' |
| | | import { useNavigator } from '@/composables/useNavigator' |
| | | |
| | | const { apitype } = usePlatformLoginType() |
| | | const userStore = useUserStore() |
| | | const { navigateTo } = useNavigator() |
| | | const {getLocation,province } = useLocation() |
| | | |
| | | |
| | | const gotoUrl = (url: string) => { |
| | | navigateTo(url) |
| | | } |
| | | |
| | | watch( |
| | | () => userStore.userInfo, // 监听 userInfo 这个响应式属性 |
| | | (newVal, oldVal) => { |
| | | console.log('userInfo 发生变化:', newVal) |
| | | }, |
| | | { deep: true } // 如果 userInfo 是对象,需要深度监听其属性变化 |
| | | ) |
| | | |
| | | onShow(() => { |
| | | console.log('我的') |
| | | getLocation() |
| | | }); |
| | | |
| | | |
| | | |
| | | // 当前 tab 索引 |
| | | const current = ref(0); |
| | |
| | | <!-- 法律文案 --> |
| | | <view class="portotal-actions"> |
| | | <view class="action-row"> |
| | | <up-text size="10" :bold="true" text="《个人信息收集清单》" color="#38516E" /> |
| | | <up-text size="10" :bold="true" text="《第三方信息共享清单》" color="#38516E" /> |
| | | <up-text size="10" :bold="true" text="《个人信息收集清单》" @click="toPortotalUni('个人信息收集清单')" color="#38516E" /> |
| | | <up-text size="10" :bold="true" text="《第三方信息共享清单》" @click="toPortotalUni('个人信息收集清单')" color="#38516E" /> |
| | | </view> |
| | | <view class="action-row"> |
| | | <up-text size="10" :bold="true" text="《用户服务协议》" color="#38516E" /> |
| | | <up-text size="10" :bold="true" text="《用户隐私政策》" color="#38516E" /> |
| | | <up-text size="10" :bold="true" text="《用户服务协议》" @click="toPortotalUni('用户协议')" color="#38516E" /> |
| | | <up-text size="10" :bold="true" text="《用户隐私政策》" @click="toPortotalUni('隐私政策')" color="#38516E" /> |
| | | </view> |
| | | </view> |
| | | </view> |
| | |
| | | |
| | | <script setup lang="ts"> |
| | | import { ref } from 'vue' |
| | | import { useNavigator } from '@/composables/useNavigator' |
| | | const { toProtocol } = useNavigator() |
| | | |
| | | |
| | | const toPortotalUni = (title:string) => { |
| | | toProtocol(title) |
| | | } |
| | | |
| | | // 菜单项类型定义 |
| | | interface MenuItem { |
| | |
| | | |
| | | .portotal-actions { |
| | | margin-top: 30rpx; |
| | | margin-bottom: 100rpx; |
| | | margin-bottom: 200rpx; |
| | | display: flex; |
| | | flex-direction: column; |
| | | align-items: center; |
| | |
| | | export interface ProtocolData { |
| | | id: number; |
| | | title: string; |
| | | content: string; |
| | | } |
| | | |
| | | export interface WxUser { |
| | | openId: String; |
| | | sessionKey: String; |
| | |
| | | purePhoneNumber: String; |
| | | } |
| | | |
| | | export interface WechatLoginData { |
| | | code: string |
| | | imgurl?: string |
| | | nickname?: string |
| | | inviter?: string |
| | | phoneNumber?: string |
| | | purePhoneNumber?: string |
| | | } |
| | | |
| | | export interface ProtocolData { |
| | | id: number; |
| | | title: string; |
| | | content: string; |
| | | } |
| | | |
| | | export interface FilmWorks{ |
| | | |
| | | id?: number; |
| | |
| | | // utils/time.ts |
| | | |
| | | export function formatRelativeTime(inputTime: string | Date): string { |
| | | const now = new Date() |
| | | |
| | | // 如果是字符串格式,先进行兼容性处理 |
| | | let time: Date |
| | | if (typeof inputTime === 'string') { |
| | | // 替换中间的空格为 'T' 或替换 '-' 为 '/' |
| | | const compatibleStr = inputTime.replace(/-/g, '/') |
| | | time = new Date(compatibleStr) |
| | | } else { |
| | | time = inputTime |
| | | } |
| | | |
| | | const diffMs = now.getTime() - time.getTime() |
| | | |
| | | const minutes = Math.floor(diffMs / (1000 * 60)) |
| | | const hours = Math.floor(minutes / 60) |
| | | const days = Math.floor(hours / 24) |
| | | const months = Math.floor(days / 30) |
| | | const years = Math.floor(days / 365) |
| | | |
| | | if (years > 0) return `${years} 年前` |
| | | if (months > 0) return `${months} 个月前` |
| | | if (days > 0) return `${days} 天前` |
| | | if (hours > 0) return `${hours} 小时前` |
| | | if (minutes > 0) return `${minutes} 分钟前` |
| | | return '刚刚' |
| | | } |
| | | |
| | | export function formatRelativeTime2(inputTime: string | Date): string { |
| | | const now = new Date() |
| | | const time = typeof inputTime === 'string' ? new Date(inputTime) : inputTime |
| | | const diffMs = now.getTime() - time.getTime() |