| 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174 |
- <!-- 我的页面 -->
- <template>
- <view class="mine-root">
- <mine-drawer-shell :open="panelOpen" :dismiss-tip="$t('mine.back')" @close="closeSettingsPanel">
- <view class="mine-page">
- <scroll-view class="mine-scroll" scroll-y :style="{ height: scrollHeight + 'px' }" :show-scrollbar="false"
- :lower-threshold="120" :refresher-enabled="!panelOpen" :refresher-triggered="pullRefreshing"
- refresher-background="#f4f6fb" refresher-default-style="black" @refresherrefresh="onRefresherRefresh"
- @refresherrestore="onRefresherEnd" @refresherabort="onRefresherEnd" @scrolltolower="onScrollToLower">
- <view class="mine-hero">
- <view v-if="showMailBtn" class="mine-mail-btn" :class="{ 'mine-mail-btn--card': showMailBtnBg }"
- :style="{ top: (statusBar + 12) + 'px' }" @tap="onMail">
- <image class="mine-mail-icon" src="/static/icon/icon-notification.png" mode="aspectFit" />
- <view v-if="showMailBadge" class="mine-mail-dot" />
- </view>
- <view class="mine-cover">
- <image v-if="coverUrl" class="mine-cover-img" :src="coverUrl" mode="aspectFill" />
- <view v-else class="mine-cover-placeholder" />
- <view class="mine-cover-fade" />
- </view>
- <view class="mine-profile" :style="profilePadStyle">
- <view class="mine-avatar-wrap" @tap="onAvatarTap">
- <image v-if="signedIn && avatarDisplayUrl" class="mine-avatar" :src="avatarDisplayUrl"
- mode="aspectFill" />
- <view v-else class="mine-avatar mine-avatar--empty" />
- <!-- #ifdef MP-WEIXIN -->
- <button v-if="signedIn && isSelf" class="mine-avatar-cam-btn" open-type="chooseAvatar"
- @chooseavatar="onWeixinChooseAvatar" @tap.stop>
- <view class="mine-avatar-cam">
- <text class="mine-avatar-cam-icon">📷</text>
- </view>
- </button>
- <!-- #endif -->
- <!-- #ifndef MP-WEIXIN -->
- <view v-if="signedIn && isSelf" class="mine-avatar-cam" @tap.stop="onChangeAvatar">
- <text class="mine-avatar-cam-icon">📷</text>
- </view>
- <!-- #endif -->
- <!-- 新增:編輯按鈕(頭像右邊) -->
- <view v-if="signedIn && isSelf" class="mine-edit-btn" @tap="onEditProfile">
- <text class="mine-edit-icon">✏️</text>
- </view>
- </view>
- <text class="mine-name">{{ displayName }}</text>
- <text class="mine-bio">{{ displayBio }}</text>
- <!-- <view v-if="signedIn && isSelf && profile.user_no" class="mine-account" @tap.stop="copyAccount">
- <text class="mine-account-label">{{ copy.accountLabel }}</text>
- <text class="mine-account-no">{{ profile.user_no }}</text>
- <text class="mine-account-copy">{{ copy.copyBtn }}</text>
- </view> -->
- <button v-if="signedIn && !isSelf" class="mine-follow-btn" :disabled="followBusy" @tap="onFollowTap">
- {{ profile.isFollow === 1 ? copy.followed : copy.follow }}
- </button>
- <view v-if="!signedIn" class="mine-guest-bar">
- <text class="mine-guest-bar-txt">{{ copy.guestBar }}</text>
- <button class="mine-guest-bar-btn" size="mini" @tap="goLogin">{{ copy.loginCta }}</button>
- </view>
- <view class="mine-stats">
- <view class="mine-stat" @tap="onStatTap('fans')">
- <text class="mine-stat-num">{{ fmtCount(profile.fans) }}</text>
- <text class="mine-stat-label">{{ copy.statFans }}</text>
- </view>
- <view class="mine-stat" @tap="onStatTap('follow')">
- <text class="mine-stat-num">{{ fmtCount(profile.follow) }}</text>
- <text class="mine-stat-label">{{ copy.statFollow }}</text>
- </view>
- <view class="mine-stat" @tap="onStatTap('views')">
- <text class="mine-stat-num">{{ fmtCount(viewCount) }}</text>
- <text class="mine-stat-label">{{ copy.statViews }}</text>
- </view>
- <view class="mine-stat" @tap="onStatTap('praised')">
- <text class="mine-stat-num">{{ fmtCount(profile.praised) }}</text>
- <text class="mine-stat-label">{{ copy.statLikes }}</text>
- </view>
- </view>
- <!-- 钻石余额(仅本人) -->
- <view class="mine-diamond-bar" v-if="signedIn && isSelf" @tap="goDiamondTopUp">
- <view class="mine-diamond-left">
- <image class="mine-diamond-icon" src="/static/icon/ic_diamond.png" mode="aspectFit" />
- <text class="mine-diamond-label">{{ copy.diamond }}</text>
- </view>
- <view class="mine-diamond-right">
- <text class="mine-diamond-value">{{ diamondDisplay }}</text>
- <text class="wlIcon-fanhui2 mine-diamond-arrow"></text>
- </view>
- </view>
- <!-- 领券中心和我的优惠卷入口 -->
- <view class="mine-assets-bar" v-if="signedIn && isSelf">
- <view class="mine-assets-card" @tap="goCoupon">
- <view class="mine-assets-info">
- <text class="mine-assets-title">{{ $t('mine.couponCenter') }}</text>
- <text class="mine-assets-desc">{{ $t('mine.receiveCoupon') }} <text
- class="wlIcon-fanhui2 mine-assets-arrow"></text></text>
- </view>
- <text class="wlIcon-31hongbao mine-assets-icon"></text>
- </view>
- <view class="mine-assets-divider"></view>
- <view class="mine-assets-card" @tap="goMyCouponList">
- <view class="mine-assets-info">
- <text class="mine-assets-title">{{ $t('mall.coupon') }}</text>
- <text class="mine-assets-desc">{{ $t('mine.myCoupon') }}<text
- class="wlIcon-fanhui2 mine-assets-arrow"></text> </text>
- </view>
- <text class="wlIcon-qianbao mine-assets-icon"></text>
- </view>
- </view>
- <!-- 购物车、收藏、关注店铺、足迹入口 -->
- <view class="mine-shortcut-bar" v-if="signedIn && isSelf">
- <view class="mine-shortcut-item" @tap="goCart">
- <text class="wlIcon-gouwuche mine-shortcut-icon"></text>
- <text class="mine-shortcut-text">{{ $t('mall.cart') }}</text>
- </view>
- <view class="mine-shortcut-item" @tap="goFavorite">
- <text class="wlIcon-shoucang mine-shortcut-icon"></text>
- <text class="mine-shortcut-text">{{ $t('mall.collect') }}</text>
- </view>
- <view class="mine-shortcut-item" @tap="goFollowShop">
- <text class="wlIcon-dianpu mine-shortcut-icon"></text>
- <text class="mine-shortcut-text">{{ $t('mine.followShop') }}</text>
- </view>
- <view class="mine-shortcut-item" @tap="goFootprint">
- <text class="wlIcon-zuji mine-shortcut-icon"></text>
- <text class="mine-shortcut-text">{{ $t('mine.footprint') }}</text>
- </view>
- </view>
- <!-- 订单快捷入口 -->
- <view class="mine-order-bar" v-if="signedIn && isSelf">
- <view class="mine-order-bar-item" @tap="goOrderList(1)">
- <text class="wlIcon-31daifukuan mine-order-bar-icon"></text>
- <text class="mine-order-bar-text">{{ $t('order.pendingPayment') }}</text>
- <view class="cu-tag badge bg-orange mine-order-bar-badge" v-if="orderStats.pay > 0">{{ orderStats.pay
- > 99 ? '99+' : orderStats.pay }}</view>
- </view>
- <view class="mine-order-bar-item" @tap="goOrderList(2)">
- <text class="wlIcon-31daifahuo mine-order-bar-icon"></text>
- <text class="mine-order-bar-text">{{ $t('order.pendingShipment') }}</text>
- <view class="cu-tag badge bg-orange mine-order-bar-badge" v-if="orderStats.delive > 0">{{
- orderStats.delive > 99 ? '99+' : orderStats.delive }}</view>
- </view>
- <view class="mine-order-bar-item" @tap="goOrderList(3)">
- <text class="wlIcon-31daishouhuo mine-order-bar-icon"></text>
- <text class="mine-order-bar-text">{{ $t('order.pendingReceipt') }}</text>
- <view class="cu-tag badge bg-orange mine-order-bar-badge" v-if="orderStats.receiving > 0">{{
- orderStats.receiving > 99 ? '99+' : orderStats.receiving }}</view>
- </view>
- <view class="mine-order-bar-item" @tap="goOrderList(4)">
- <text class="wlIcon-31daipingjia mine-order-bar-icon"></text>
- <text class="mine-order-bar-text">{{ $t('order.pendingReview') }}</text>
- <view class="cu-tag badge bg-orange mine-order-bar-badge" v-if="orderStats.evaluate > 0">{{
- orderStats.evaluate > 99 ? '99+' : orderStats.evaluate }}</view>
- </view>
- <view class="mine-order-bar-item solid-left" @tap="goRefundList()">
- <text class="wlIcon-31youhuiquan mine-order-bar-icon"></text>
- <text class="mine-order-bar-text">{{ $t('order.refundAfterSales') }}</text>
- <view class="cu-tag badge bg-orange mine-order-bar-badge" v-if="orderStats.customer > 0">{{
- orderStats.customer > 99 ? '99+' : orderStats.customer }}</view>
- </view>
- </view>
- <!-- 操作按钮 -->
- <view v-if="displayActions.length" class="mine-actions">
- <view v-for="act in displayActions" :key="act.key" class="mine-action" @tap="onAction(act.key)">
- <!-- <view class="mine-action-icon-wrap">
- <image class="mine-action-icon-img" :src="act.iconSrc" mode="aspectFit" />
- </view> -->
- <view class="mine-action-icon-wrap">
- <text v-if="act.iconClass" class="mine-action-icon-font" :class="act.iconClass"></text>
- <image v-else class="mine-action-icon-img" :src="act.iconSrc" mode="aspectFit" />
- </view>
- <text class="mine-action-label">{{ act.label }}</text>
- </view>
- </view>
- </view>
- </view>
- <view class="mine-body">
- <view class="mine-tabs">
- <view v-for="tab in visibleTabs" :key="tab.key" class="mine-tab-pill"
- :class="{ 'mine-tab-pill--on': activeTab === tab.key }" @tap="switchTab(tab.key)">
- <text class="mine-tab-pill-text">{{ tab.label }}</text>
- </view>
- </view>
- <view v-if="isGuestHome" class="mine-empty mine-empty--guest">
- <text class="mine-empty-txt">{{ copy.loginHint }}</text>
- <button class="mine-primary-btn" size="mini" @tap="goLogin">
- {{ copy.loginCta }}
- </button>
- </view>
- <view v-else-if="!activeFeed.loaded && !activeFeed.loading" class="mine-empty">
- <text class="mine-empty-txt">{{ copy.loading }}</text>
- </view>
- <view v-else-if="activeFeed.loading && !activeFeed.items.length" class="mine-empty">
- <text class="mine-empty-txt">{{ copy.loading }}</text>
- </view>
- <view v-else-if="activeFeed.loaded && !activeFeed.items.length" class="mine-empty">
- <image v-if="emptyImageUrl" class="mine-empty-img" :src="emptyImageUrl" mode="aspectFit" />
- <text class="mine-empty-txt">{{ emptyHint }}</text>
- <button v-if="!signedIn" class="mine-primary-btn" size="mini" @tap="goLogin">
- {{ copy.loginCta }}
- </button>
- <button v-else-if="isSelf && activeTab === 'series'" class="mine-primary-btn" size="mini"
- @tap="onCreateSeries">
- {{ copy.createSeries }}
- </button>
- </view>
- <view v-else-if="activeTab === 'series'" class="mine-grid">
- <view v-for="(item, index) in activeFeed.items" :key="gridKey(item, index)" class="mine-card"
- @tap="openItem(item)">
- <view class="mine-card-media">
- <image class="mine-card-img" :src="cardCover(item)" mode="aspectFill" />
- </view>
- <view class="mine-card-series">
- <text class="mine-card-series-title">{{ item.title || copy.unnamedSeries }}</text>
- <view v-if="isSelf" class="mine-card-self">
- <text class="mine-card-action" @tap.stop="onEditSeries(item)">{{ copy.edit }}</text>
- <text class="mine-card-action mine-card-action--del" @tap.stop="onDeleteItem(index)">{{ copy.delete
- }}</text>
- </view>
- </view>
- </view>
- </view>
- <view v-else class="mine-works-list">
- <view v-for="(item, index) in activeFeed.items" :key="gridKey(item, index)" class="mine-works-item">
- <view class="mine-works-cover" @tap="openItem(item)">
- <image v-if="workCover(item)" class="mine-works-cover-img" :src="workCover(item)" mode="aspectFill" />
- <view v-else class="mine-works-cover-empty" />
- <view v-if="item.type === 'video'" class="mine-works-video-badge">
- <text class="mine-works-video-badge-icon">▶</text>
- </view>
- <view v-if="item.state && item.state !== 'normal'" class="mine-works-state">
- <text class="mine-works-state-txt">{{ stateLabel(item.state) }}</text>
- </view>
- </view>
- <view class="mine-works-body">
- <view class="mine-works-desc" @tap="openItem(item)">
- <text v-if="item.type_text" class="mine-works-tag">{{ item.type_text }}</text>
- <text class="mine-works-text">{{ formatContent(item.content) || ' ' }}</text>
- </view>
- <view class="mine-works-foot">
- <view class="mine-works-foot-left">
- <text class="mine-works-date">{{ formatDate(item.createtime) }} {{ copy.publishedAt }}</text>
- <text v-if="isSelf && activeTab === 'works'" class="mine-works-del"
- @tap.stop="onDeleteItem(index)">{{ copy.delete }}</text>
- </view>
- <view class="mine-works-like" :class="{ 'mine-works-like--on': item.isLike === 1 }"
- @tap.stop="onLikeItem(index)">
- <text class="mine-works-like-icon">♥</text>
- <text class="mine-works-like-num">{{ item.likes || 0 }}</text>
- </view>
- </view>
- </view>
- </view>
- </view>
- <view v-if="!isGuestHome && activeFeed.items.length" class="mine-more">
- <text class="mine-more-txt">{{ loadMoreText }}</text>
- </view>
- </view>
- <view v-show="!panelOpen" class="mine-tabbar-space" :style="{ height: listBottomPad + 'px' }" />
- </scroll-view>
- <view v-if="isSelf && activeTab === 'series' && !isGuestHome" class="mine-series-fab" @tap="onCreateSeries">
- <text class="mine-series-fab-icon">+</text>
- </view>
- </view>
- <mine-side-panel slot="drawer" :active="panelOpen" @close="closeSettingsPanel" @publish="onPublish"
- @open-security="onPanelOpenSecurity" @navigate="onPanelNavigate" />
- </mine-drawer-shell>
- <app-tabbar v-show="!panelOpen" active="mine" />
- <!-- #ifndef MP-WEIXIN -->
- <view v-if="avatarCropUrl" class="mine-avatar-crop-layer">
- <image-cutter :url="avatarCropUrl" :fixed="true" :width="200" :height="200" :max-width="500" :max-height="500"
- :blob="false" @ok="onAvatarCropOk" @cancel="onAvatarCropCancel" />
- </view>
- <!-- #endif -->
- </view>
- </template>
- <script>
- import { mapState } from 'vuex'
- import { fetchOrderStats } from '@/features/mall/mall-gateway'
- import AppTabbar from '@/components/app-tabbar/app-tabbar.vue'
- import MineDrawerShell from '@/components/mine-drawer-shell/mine-drawer-shell.vue'
- import MineSidePanel from '@/components/mine-side-panel/mine-side-panel.vue'
- import { detailPageUrl, videoDetailUrl } from '@/features/dynamics/post-media'
- import {
- fetchProfileCard,
- fetchProfileContentPage,
- fetchProfileSeriesPage,
- toggleProfileFollow,
- toggleProfileLike,
- removeProfileContent,
- removeProfileSeries,
- fetchWalletSnapshot
- } from '@/features/profile/profile-gateway'
- import { fetchRealnameStatus } from '@/features/account/realname-gateway'
- import {
- normalizeRealnameFlag,
- shouldOpenRealnameDetail,
- REALNAME_NONE
- } from '@/features/mine-panel/realname-bridge'
- import { uploadAndUpdateAvatar } from '@/features/account/avatar-gateway'
- import { absolutizeMedia } from '@/features/feed/media-resolve'
- import ImageCutter from '@/components/image-cutter/image-cutter.vue'
- const EMPTY_IMAGE_PATH = '/assets/img/empty.png'
- const TABBAR_H = 100
- const TAB_DEFS = [
- { key: 'works', labelKey: 'mine.tabWorks' },
- { key: 'likes', labelKey: 'mine.tabLikes' },
- { key: 'series', labelKey: 'mine.tabSeries', ownerOnly: true, hidden: true }
- ]
- function filterVisibleTabs(tabs, isSelf) {
- return tabs.filter((t) => {
- if (t.hidden) return false
- if (t.ownerOnly && !isSelf) return false
- return true
- })
- }
- const ACTION_DEFS = [
- { key: 'comment', labelKey: 'mine.actionComment', iconClass: 'wlIcon-icon_pinglun wanl-text-red' },
- { key: 'address', labelKey: 'mine.actionAddress', iconClass: 'wlIcon-dizhi wanl-text-yellow' },
- { key: 'smartAssistant', labelKey: 'mine.actionSmartAssistant', iconClass: 'wlIcon-kefu wanl-text-green' },
- { key: 'settings', labelKey: 'mine.actionSettings', iconClass: 'wlIcon-shezhi wanl-text-purple' }
- ]
- const GUEST_TABS = [{ key: 'works', labelKey: 'mine.tabWorks' }]
- function emptyProfile() {
- return {
- user_no: '',
- user: { avatar: '', nickname: '—', bio: '' },
- follow: 0,
- fans: 0,
- praised: 0,
- look_number: 0,
- works: 0,
- views: 0,
- isFollow: 0,
- image: '',
- shop: null
- }
- }
- function emptyFeedState() {
- return {
- items: [],
- page: 1,
- lastPage: 1,
- loading: false,
- loaded: false,
- status: 'more'
- }
- }
- export default {
- components: { AppTabbar, MineDrawerShell, MineSidePanel, ImageCutter },
- data() {
- return {
- panelOpen: false,
- statusBar: 0,
- safeBottom: 0,
- windowHeight: 667,
- pullRefreshing: false,
- targetUserId: null,
- isSelf: false,
- followBusy: false,
- activeTab: 'works',
- profile: emptyProfile(),
- feeds: {
- works: emptyFeedState(),
- likes: emptyFeedState(),
- series: emptyFeedState()
- },
- refreshing: false,
- pendingReload: false,
- showMailBadge: false,
- showMailBtnBg: false,
- avatarCropUrl: '',
- avatarUploading: false,
- avatarPreviewPath: '',
- diamondBalance: null
- }
- },
- computed: {
- ...mapState({
- storeUser: (state) => state.user,
- orderStats: (state) => state.statistics.order
- }),
- copy() {
- const t = this.$t.bind(this)
- return {
- accountLabel: t('mine.accountLabel'),
- copyBtn: t('mine.copyBtn'),
- statFans: t('mine.statFans'),
- statFollow: t('mine.statFollow'),
- statViews: t('mine.statViews'),
- statLikes: t('mine.statLikes'),
- loginCta: t('mine.loginCta'),
- loginHint: t('mine.loginHint'),
- guestName: t('mine.guestName'),
- guestBio: t('mine.guestBio'),
- guestBar: t('mine.guestBar'),
- defaultBio: t('mine.defaultBio'),
- follow: t('mine.follow'),
- followed: t('mine.followed'),
- loading: t('mine.loading'),
- noMore: t('mine.noMore'),
- loadMore: t('mine.loadMore'),
- deleteConfirm: t('mine.deleteConfirm'),
- deleteSeriesConfirm: t('mine.deleteSeriesConfirm'),
- emptyWorks: t('mine.emptyWorks'),
- emptyLikes: t('mine.emptyLikes'),
- emptySeries: t('mine.emptySeries'),
- dev: t('mine.dev'),
- unnamedSeries: t('mine.unnamedSeries'),
- edit: t('mine.edit'),
- delete: t('mine.delete'),
- publishBtn: t('mine.publishBtn'),
- createSeries: t('mine.createSeries'),
- publishedAt: t('mine.publishedAt'),
- editSeries: t('mine.editSeries'),
- loadFailed: t('mine.loadFailed'),
- actionFailed: t('mine.actionFailed'),
- deleted: t('mine.deleted'),
- deleteFailed: t('mine.deleteFailed'),
- avatarUploading: t('mine.avatarUploading'),
- avatarUpdated: t('mine.avatarUpdated'),
- avatarUploadFailed: t('mine.avatarUploadFailed'),
- editProfile: t('mine.editProfile'),
- seriesDetail: t('mine.seriesDetail'),
- needLogin: t('mine.needLogin'),
- viewsTip: t('mine.viewsTip'),
- diamond: t('money.diamond')
- }
- },
- signedIn() {
- return !!this.storeUser.isLogin
- },
- diamondDisplay() {
- const n = Number(this.diamondBalance)
- if (!Number.isFinite(n)) return '0.00'
- return n.toFixed(2)
- },
- isGuestHome() {
- return !this.signedIn && !this.targetUserId
- },
- displayName() {
- if (this.signedIn) return this.profile.user.nickname || '—'
- return this.copy.guestName
- },
- profileBioText() {
- const raw =
- (this.profile.user && this.profile.user.bio) ||
- this.profile.bio ||
- ''
- return String(raw)
- .replace(/<[^>]+>/g, '')
- .replace(/ /g, ' ')
- .trim()
- },
- displayBio() {
- if (this.isGuestHome) return this.copy.guestBio
- return this.profileBioText || this.copy.defaultBio
- },
- displayActions() {
- // 仅在浏览他人主页时隐藏;Tab 本人页 / 游客始终展示
- if (this.signedIn && this.targetUserId && !this.isSelf) return []
- return ACTION_DEFS.map((item) => ({
- ...item,
- label: this.$t(item.labelKey)
- }))
- },
- visibleTabs() {
- const tabs = !this.signedIn ? GUEST_TABS : TAB_DEFS
- return filterVisibleTabs(tabs, this.isSelf).map((item) => ({
- ...item,
- label: this.$t(item.labelKey)
- }))
- },
- activeFeed() {
- return this.feeds[this.activeTab] || emptyFeedState()
- },
- tabbarPad() {
- return uni.upx2px(TABBAR_H)
- },
- tabbarTotalHeight() {
- return this.tabbarPad + (this.safeBottom || 0)
- },
- scrollHeight() {
- return Math.max(200, this.windowHeight - this.tabbarTotalHeight)
- },
- listBottomPad() {
- return this.tabbarTotalHeight + uni.upx2px(24)
- },
- coverUrl() {
- if (!this.profile.image) return ''
- return this.$appKit.oss(this.profile.image, 750, 320, 1, 'transparent', 'jpg')
- },
- avatarUrl() {
- return this.$appKit.oss(this.profile.user.avatar, 200, 200, 2, 'avatar')
- },
- avatarDisplayUrl() {
- if (this.avatarPreviewPath) return this.avatarPreviewPath
- return this.avatarUrl
- },
- viewCount() {
- const v = this.profile.look_number ?? this.profile.views ?? this.profile.works ?? 0
- const n = Number(v)
- return Number.isFinite(n) && n >= 0 ? n : 0
- },
- emptyHint() {
- if (this.isGuestHome) return this.copy.loginHint
- if (this.activeTab === 'likes') return this.copy.emptyLikes
- if (this.activeTab === 'series') return this.copy.emptySeries
- return this.copy.emptyWorks
- },
- emptyImageUrl() {
- return absolutizeMedia(EMPTY_IMAGE_PATH)
- },
- loadMoreText() {
- if (this.activeFeed.status === 'loading') return this.copy.loading
- if (this.activeFeed.status === 'noMore') return this.copy.noMore
- return this.copy.loadMore
- },
- profilePadStyle() {
- const top = (this.statusBar || 0) + uni.upx2px(32)
- return { paddingTop: `${top}px` }
- },
- showMailBtn() {
- return !this.targetUserId || this.isSelf
- }
- },
- watch: {
- '$store.state.user.isLogin'(val) {
- if (val) {
- this.resetAllFeeds()
- this.reloadAll({ silent: true })
- } else {
- this.resetGuest()
- }
- }
- },
- onLoad(query) {
- try {
- const info = uni.getSystemInfoSync()
- this.statusBar = info.statusBarHeight || 0
- this.safeBottom = (info.safeAreaInsets && info.safeAreaInsets.bottom) || 0
- this.windowHeight = info.windowHeight || 667
- } catch (_) { }
- if (query && query.id) this.targetUserId = query.id
- this.reloadAll({ silent: true })
- },
- onShow() {
- setTimeout(() => {
- try { uni.hideTabBar({ animation: false }) } catch (_) { }
- }, 0)
- this.reloadAll({ silent: true })
- },
- onHide() {
- this.panelOpen = false
- },
- onBackPress() {
- if (this.panelOpen) {
- this.closeSettingsPanel()
- return true
- }
- return false
- },
- methods: {
- async loadOrderStats() {
- try {
- const stats = await fetchOrderStats()
- this.$store.commit('statistics/order', stats)
- } catch (e) {
- console.error('[mine] 加载订单统计失败:', e)
- }
- },
- openSettingsPanel() {
- this.panelOpen = true
- },
- closeSettingsPanel() {
- this.panelOpen = false
- },
- fmtCount(n) {
- const val = Number(n)
- if (!Number.isFinite(val) || val < 0) return 0
- return this.$appKit.toFormat(val, 'thousand')
- },
- resetGuest() {
- this.isSelf = false
- this.profile = emptyProfile()
- this.diamondBalance = null
- this.resetAllFeeds()
- },
- resetAllFeeds() {
- Object.keys(this.feeds).forEach((k) => {
- this.feeds[k] = emptyFeedState()
- })
- },
- ensureActiveTabVisible() {
- const keys = this.visibleTabs.map((t) => t.key)
- if (!keys.includes(this.activeTab)) {
- this.activeTab = keys[0] || 'works'
- }
- },
- requireLogin() {
- return this.$appKit && this.$appKit.requireLogin()
- },
- onMail() {
- uni.navigateTo({ url: '/pages/notice/sys-notice/index' })
- },
- onRefresherRefresh() {
- if (this.panelOpen) {
- this.pullRefreshing = false
- return
- }
- this.pullRefresh()
- },
- onRefresherEnd() {
- this.pullRefreshing = false
- },
- async pullRefresh() {
- if (this.panelOpen || this.pullRefreshing) return
- this.pullRefreshing = true
- try {
- await this.reloadAll({ silent: true })
- } finally {
- this.pullRefreshing = false
- }
- },
- onScrollToLower() {
- if (this.panelOpen) return
- this.loadFindData('append')
- },
- async reloadAll({ silent = false } = {}) {
- if (this.refreshing) {
- this.pendingReload = true
- return
- }
- this.refreshing = true
- try {
- if (!this.signedIn && !this.targetUserId) {
- this.resetGuest()
- this.activeTab = 'works'
- const feed = this.feeds.works
- feed.items = []
- feed.page = 1
- feed.lastPage = 1
- feed.loading = false
- feed.loaded = true
- feed.status = 'noMore'
- return
- }
- this.ensureActiveTabVisible()
- await this.loadProfile()
- if (this.signedIn && this.isSelf) {
- await this.loadDiamondBalance()
- await this.loadOrderStats()
- } else {
- this.diamondBalance = null
- }
- await this.loadFindData(silent ? 'silentRefresh' : 'refresh')
- } catch (err) {
- if (!silent) {
- this.$appKit.msg(err.message || this.copy.loadFailed)
- }
- } finally {
- this.refreshing = false
- if (this.pendingReload) {
- this.pendingReload = false
- this.reloadAll({ silent: true })
- }
- }
- },
- onPublish() {
- if (!this.requireLogin()) return
- uni.navigateTo({ url: '/pages/dynamics/publish' })
- },
- onPanelOpenSecurity() {
- if (!this.requireLogin()) return
- this.onPanelNavigate({ url: '/pages/account/security' })
- },
- async loadProfile() {
- const card = await fetchProfileCard(this.targetUserId)
- const userNo = card.user_no || (card.user && card.user.user_no) || ''
- this.profile = {
- ...emptyProfile(),
- ...card,
- user_no: userNo,
- user: {
- ...emptyProfile().user,
- ...(card.user || {}),
- bio: ((card.user && card.user.bio) || card.bio || '').trim()
- }
- }
- if (this.signedIn && Number(card.user_id) === Number(this.storeUser.id)) {
- this.isSelf = true
- } else if (this.signedIn && !this.targetUserId) {
- this.isSelf = true
- } else {
- this.isSelf = false
- }
- if (!this.isSelf && this.activeTab === 'series') {
- this.activeTab = 'works'
- }
- this.ensureActiveTabVisible()
- },
- switchTab(key) {
- const tab = TAB_DEFS.find((t) => t.key === key) || GUEST_TABS.find((t) => t.key === key)
- if (tab && tab.hidden) return
- if (this.isGuestHome) {
- this.requireLogin()
- return
- }
- if (!this.signedIn && key !== 'works') {
- this.requireLogin()
- return
- }
- const sameTab = this.activeTab === key
- this.activeTab = key
- this.loadFindData(sameTab ? 'refresh' : 'tabChange')
- },
- /** 同 myuser.vue loadFindData / loadCompilationList */
- async loadFindData(source = 'append') {
- if (this.isGuestHome) return
- if (!this.profile.user_no) return
- const feed = this.feeds[this.activeTab]
- if (!feed) return
- const isRefresh = source === 'refresh' || source === 'silentRefresh'
- if (isRefresh) {
- feed.page = 1
- feed.status = 'more'
- feed.loaded = false
- if (source === 'refresh') feed.items = []
- }
- if (feed.status === 'noMore' && source === 'append') return
- if (source === 'tabChange' && feed.loaded) return
- if (feed.status === 'loading') return
- feed.status = 'loading'
- feed.loading = true
- try {
- let pack
- if (this.activeTab === 'series') {
- pack = await fetchProfileSeriesPage(this.profile.user_no, feed.page)
- } else {
- const isMyfindWorks = this.activeTab === 'works' && this.isSelf
- pack = await fetchProfileContentPage({
- tabKey: this.activeTab,
- userNo: this.profile.user_no,
- page: feed.page,
- isOwner: isMyfindWorks
- })
- }
- feed.page = pack.currentPage
- if (pack.currentPage >= pack.lastPage) {
- feed.status = 'noMore'
- } else {
- feed.status = 'more'
- feed.page = pack.currentPage + 1
- }
- const incoming = pack.rows || []
- if (isRefresh) {
- feed.items = incoming
- } else {
- feed.items = feed.items.concat(incoming)
- }
- feed.loaded = true
- } catch (err) {
- feed.status = 'more'
- feed.loaded = true
- if (source !== 'silentRefresh') {
- this.$appKit.msg(err.message || this.copy.loadFailed)
- }
- } finally {
- feed.loading = false
- }
- },
- formatContent(content) {
- if (!content) return ''
- return String(content).replace(/<\/?.+?>/g, '').replace(/ /g, '').trim()
- },
- formatDate(ts) {
- if (!ts) return ''
- return this.$appKit.timeFormat(ts, 'mm-dd') || ''
- },
- copyAccount() {
- if (!this.profile.user_no) return
- this.$appKit.copy(this.profile.user_no)
- },
- onCreateSeries() {
- if (!this.requireLogin()) return
- this.hintSoon(this.copy.createSeries)
- },
- onEditSeries(item) {
- if (!this.isSelf || !item) return
- this.hintSoon(this.copy.editSeries)
- },
- onStatTap(type) {
- if (!this.requireLogin()) return
- if (!this.profile.user_no) {
- this.$appKit.msg(this.copy.loading)
- return
- }
- if (type === 'praised') {
- this.showPraiseTip()
- return
- }
- if (type === 'views') {
- this.showViewsTip()
- return
- }
- if (type === 'fans') {
- uni.navigateTo({
- url: `/pages/mine/fans?id=${encodeURIComponent(this.profile.user_no)}`
- })
- return
- }
- if (type === 'follow') {
- uni.navigateTo({
- url: `/pages/mine/follow?id=${encodeURIComponent(this.profile.user_no)}`
- })
- return
- }
- },
- gridKey(item, index) {
- return item.id || item.series_id || `row-${index}`
- },
- cardCover(item) {
- if (this.activeTab === 'series') {
- const path = item.image || item.cover || ''
- return path ? this.$appKit.oss(path, 400, 520) : ''
- }
- return this.workCover(item)
- },
- workCover(item) {
- if (!item) return ''
- const images = Array.isArray(item.images) ? item.images : []
- const path = images[0] || (item.video && item.video.cover_url) || item.image || item.cover || ''
- if (!path) return ''
- return this.$appKit.oss(path, 400, 920)
- },
- stateLabel(state) {
- const map = {
- examine: 'mine.stateExamine',
- hazard: 'mine.stateHazard',
- transcoding: 'mine.stateTranscoding',
- screenshot: 'mine.stateScreenshot',
- publish: 'mine.statePublish'
- }
- const key = map[state]
- return key ? this.$t(key) : state
- },
- openItem(item) {
- if (this.activeTab === 'series') {
- this.hintSoon(this.copy.seriesDetail)
- return
- }
- if (item.state && item.state !== 'normal') {
- this.$appKit.msg(this.stateLabel(item.state))
- return
- }
- if (item.type === 'video') {
- uni.navigateTo({
- url: videoDetailUrl(item.id, { labelId: item.label_id })
- })
- return
- }
- if (item.type === 'live' && item.live_id) {
- uni.navigateTo({ url: `/pages/live/watch?id=${item.live_id}` })
- return
- }
- if (item.id) {
- uni.navigateTo({ url: detailPageUrl(item.id) })
- }
- },
- openItemComments(item) {
- if (!item || !item.id) return
- if (item.state && item.state !== 'normal') {
- this.$appKit.msg(this.stateLabel(item.state))
- return
- }
- uni.navigateTo({ url: detailPageUrl(item.id, { focus: 'comments' }) })
- },
- async onFollowTap() {
- if (!this.requireLogin()) return
- if (this.followBusy) return
- this.followBusy = true
- try {
- const flag = await toggleProfileFollow(this.profile.user_no)
- if (flag === 0 || flag === '0') {
- this.profile.isFollow = 0
- this.profile.fans = Math.max(0, Number(this.profile.fans || 0) - 1)
- } else {
- this.profile.isFollow = 1
- this.profile.fans = Number(this.profile.fans || 0) + 1
- }
- } catch (err) {
- this.$appKit.msg(err.message || this.copy.actionFailed)
- } finally {
- this.followBusy = false
- }
- },
- async onLikeItem(index) {
- if (!this.requireLogin()) return
- const row = this.activeFeed.items[index]
- if (!row || !row.id) return
- try {
- const flag = await toggleProfileLike(row.id)
- if (flag === 0 || flag === '0') {
- row.likes = Math.max(0, Number(row.likes || 0) - 1)
- row.isLike = 0
- } else {
- row.likes = Number(row.likes || 0) + 1
- row.isLike = 1
- }
- } catch (err) {
- this.$appKit.msg(err.message || this.copy.actionFailed)
- }
- },
- onDeleteItem(index) {
- if (!this.isSelf) return
- const row = this.activeFeed.items[index]
- if (!row) return
- const isSeries = this.activeTab === 'series'
- uni.showModal({
- content: isSeries ? this.copy.deleteSeriesConfirm : this.copy.deleteConfirm,
- success: async (res) => {
- if (!res.confirm) return
- try {
- if (isSeries) {
- await removeProfileSeries(row.id || row.series_id)
- } else {
- await removeProfileContent(row.id)
- }
- this.activeFeed.items.splice(index, 1)
- this.$appKit.msg(this.copy.deleted)
- if (!this.activeFeed.items.length) {
- this.loadFindData('refresh')
- }
- } catch (err) {
- this.$appKit.msg(err.message || this.copy.deleteFailed)
- }
- }
- })
- },
- showPraiseTip() {
- const name = this.profile.user.nickname || ''
- const n = this.fmtCount(this.profile.praised)
- uni.showModal({
- content: this.$t('mine.praiseModal', { name, count: n }),
- showCancel: false
- })
- },
- showViewsTip() {
- const name = this.profile.user.nickname || ''
- const n = this.fmtCount(this.viewCount)
- uni.showModal({
- content: this.$t('mine.viewsModal', { name, count: n }),
- showCancel: false
- })
- },
- onAvatarTap() {
- if (!this.requireLogin()) return
- },
- onChangeAvatar() {
- if (!this.requireLogin() || !this.isSelf || this.avatarUploading) return
- uni.chooseImage({
- count: 1,
- sizeType: ['compressed'],
- sourceType: ['album', 'camera'],
- success: (res) => {
- const path = res.tempFilePaths && res.tempFilePaths[0]
- if (path) this.avatarCropUrl = path
- }
- })
- },
- onAvatarCropCancel() {
- this.avatarCropUrl = ''
- },
- onAvatarCropOk(e) {
- this.avatarCropUrl = ''
- const path = e && e.path
- if (path) this.submitAvatarFile(path)
- },
- onWeixinChooseAvatar(e) {
- const path = e && e.detail && e.detail.avatarUrl
- if (path) this.submitAvatarFile(path)
- },
- async submitAvatarFile(filePath) {
- if (!filePath || this.avatarUploading) return
- this.avatarUploading = true
- this.avatarPreviewPath = filePath
- uni.showLoading({ mask: true, title: this.copy.avatarUploading })
- try {
- const avatar = await uploadAndUpdateAvatar(filePath)
- this.$store.commit('user/apply', { avatar })
- const user = { ...(this.profile.user || {}), avatar }
- this.profile = { ...this.profile, user }
- this.avatarPreviewPath = ''
- this.$appKit.msg(this.copy.avatarUpdated)
- } catch (err) {
- this.avatarPreviewPath = ''
- this.$appKit.msg((err && err.message) || this.copy.avatarUploadFailed)
- } finally {
- this.avatarUploading = false
- uni.hideLoading()
- }
- },
- onEditProfile() {
- if (!this.requireLogin()) return
- uni.navigateTo({
- url: '/pages/account/profile-edit',
- fail: () => this.$appKit.to('/pages/account/profile-edit')
- })
- },
- onPanelNavigate(payload) {
- const url = payload && payload.url
- if (!url) return
- const opts = { url }
- if (payload.animationType) {
- opts.animationType = payload.animationType
- opts.animationDuration = payload.animationDuration != null ? payload.animationDuration : 300
- }
- opts.fail = (err) => {
- if (this.$appKit && this.$appKit.to) {
- this.$appKit.to(url, payload.animationType, payload.animationDuration)
- } else {
- console.warn('[mine] panel navigate failed', url, err)
- }
- }
- uni.navigateTo(opts)
- },
- onAction(key) {
- if (!this.requireLogin()) return
- switch (key) {
- case 'comment':
- uni.navigateTo({ url: '/pages/user/comment/comment' })
- break
- case 'address':
- uni.navigateTo({ url: '/pages/user/address/address' })
- break
- case 'smartAssistant':
- uni.navigateTo({ url: '/pages/user/service' })
- break
- case 'settings':
- this.openSettingsPanel()
- break
- default:
- this.hintSoon()
- }
- },
- goLogin() {
- this.requireLogin()
- },
- goOrderList(state) {
- uni.navigateTo({ url: `/pages/order/list?state=${state}` })
- },
- goRefundList() {
- uni.navigateTo({ url: '/pages/user/refund/lists' })
- },
- goCoupon() {
- uni.navigateTo({ url: '/pages/user/coupon/list' })
- },
- goMyCouponList() {
- uni.navigateTo({ url: '/pages/user/coupon/mycard' })
- },
- goDiamondTopUp() {
- if (!this.requireLogin()) return
- uni.navigateTo({ url: '/pages/user/money/diamond-top-up' })
- },
- async loadDiamondBalance() {
- try {
- const snap = await fetchWalletSnapshot()
- this.diamondBalance = snap && snap.diamond != null ? Number(snap.diamond) || 0 : 0
- } catch (_) {
- this.diamondBalance = 0
- }
- },
- // 购物车
- goCart() {
- uni.navigateTo({ url: '/pages/cart/index' })
- },
- // 收藏列表
- goFavorite() {
- uni.navigateTo({ url: '/pages/user/collect' })
- },
- // 关注列表
- goFollowShop() {
- uni.navigateTo({ url: '/pages/user/follow' })
- },
- // 足迹列表
- goFootprint() {
- uni.navigateTo({ url: '/pages/user/footprint' })
- },
- hintSoon(label) {
- this.$appKit.msg(label || this.copy.dev)
- }
- }
- }
- </script>
- <style scoped>
- .mine-root {
- min-height: 100vh;
- width: 100%;
- position: relative;
- }
- .mine-page {
- height: 100vh;
- width: 100%;
- display: flex;
- flex-direction: column;
- overflow: hidden;
- box-sizing: border-box;
- background-color: #f4f6fb;
- }
- .mine-scroll {
- flex: 1;
- width: 100%;
- min-height: 0;
- }
- .mine-hero {
- position: relative;
- width: 100%;
- min-height: 420rpx;
- }
- .mine-mail-btn {
- position: absolute;
- right: 32rpx;
- z-index: 3;
- width: 72rpx;
- height: 72rpx;
- border-radius: 50%;
- background-color: rgba(0, 0, 0, 0.28);
- display: flex;
- align-items: center;
- justify-content: center;
- }
- .mine-mail-btn--card {
- border-radius: 20rpx;
- background-color: rgba(255, 255, 255, 0.92);
- box-shadow: 0 6rpx 20rpx rgba(74, 140, 255, 0.12);
- }
- .mine-mail-icon {
- width: 40rpx;
- height: 40rpx;
- flex-shrink: 0;
- }
- .mine-mail-dot {
- position: absolute;
- top: 12rpx;
- right: 12rpx;
- width: 14rpx;
- height: 14rpx;
- border-radius: 7rpx;
- background-color: #ff4d4f;
- }
- .mine-cover {
- position: absolute;
- top: 0;
- left: 0;
- right: 0;
- width: 100%;
- height: 420rpx;
- overflow: hidden;
- }
- .mine-cover-img,
- .mine-cover-placeholder {
- width: 100%;
- height: 100%;
- }
- .mine-cover-placeholder {
- background: linear-gradient(160deg, #8ec5ff 0%, #e0ecff 55%, #f4f6fb 100%);
- }
- .mine-cover-fade {
- position: absolute;
- left: 0;
- right: 0;
- bottom: 0;
- height: 220rpx;
- background: linear-gradient(to bottom, rgba(244, 246, 251, 0), rgba(244, 246, 251, 1));
- }
- .mine-profile {
- position: relative;
- z-index: 2;
- display: flex;
- flex-direction: column;
- align-items: center;
- width: 100%;
- padding-left: 28rpx;
- padding-right: 28rpx;
- padding-bottom: 24rpx;
- box-sizing: border-box;
- }
- .mine-guest-bar {
- display: flex;
- flex-direction: row;
- align-items: center;
- justify-content: space-between;
- width: 100%;
- margin-top: 20rpx;
- padding: 16rpx 20rpx;
- border-radius: 16rpx;
- background-color: rgba(74, 140, 255, 0.1);
- box-sizing: border-box;
- }
- .mine-guest-bar-txt {
- flex: 1;
- font-size: 24rpx;
- color: #4a8cff;
- line-height: 1.4;
- padding-right: 16rpx;
- }
- .mine-guest-bar-btn {
- flex-shrink: 0;
- padding: 0 24rpx;
- height: 56rpx;
- line-height: 56rpx;
- font-size: 24rpx;
- color: #ffffff;
- background: linear-gradient(135deg, #4a8cff 0%, #6ec8ff 100%);
- border-radius: 999rpx;
- border: none;
- }
- .mine-avatar-wrap {
- position: relative;
- display: flex;
- flex-direction: row;
- justify-content: center;
- align-items: center;
- margin-bottom: 20rpx;
- }
- .mine-avatar {
- width: 176rpx;
- height: 176rpx;
- border-radius: 88rpx;
- border: 6rpx solid #ffffff;
- background-color: #e8edf5;
- box-shadow: 0 12rpx 40rpx rgba(74, 140, 255, 0.18);
- }
- .mine-avatar--empty {
- background-color: #dce4f0;
- }
- .mine-avatar-cam {
- position: absolute;
- left: 8rpx;
- bottom: 8rpx;
- width: 52rpx;
- height: 52rpx;
- border-radius: 26rpx;
- background-color: #1a1d26;
- display: flex;
- align-items: center;
- justify-content: center;
- border: 4rpx solid #ffffff;
- }
- .mine-avatar-cam-icon {
- font-size: 24rpx;
- line-height: 1;
- }
- .mine-avatar-cam-btn {
- position: absolute;
- left: 8rpx;
- bottom: 8rpx;
- padding: 0;
- margin: 0;
- background: transparent;
- border: none;
- line-height: 1;
- }
- .mine-avatar-cam-btn::after {
- border: none;
- }
- .mine-avatar-crop-layer {
- position: fixed;
- left: 0;
- top: 0;
- right: 0;
- bottom: 0;
- z-index: 10100;
- }
- /* 编辑按钮样式 */
- .mine-edit-btn {
- position: absolute;
- bottom: 0;
- right: -6rpx;
- width: 48rpx;
- height: 48rpx;
- border-radius: 24rpx;
- background: #ffffff;
- border: 2rpx solid #e8edf5;
- box-shadow: 0 4rpx 12rpx rgba(0, 0, 0, 0.08);
- display: flex;
- align-items: center;
- justify-content: center;
- }
- .mine-edit-icon {
- font-size: 32rpx;
- /* 反转图标 */
- transform: scaleX(-1);
- display: inline-block;
- /* 讓 transform 生效 */
- }
- .mine-name {
- display: block;
- text-align: center;
- font-size: 40rpx;
- font-weight: 700;
- color: #1a1d26;
- line-height: 1.3;
- }
- .mine-bio {
- display: block;
- margin-top: 12rpx;
- text-align: center;
- font-size: 26rpx;
- color: #8a8d9e;
- line-height: 1.5;
- padding: 0 16rpx;
- }
- .mine-account {
- display: flex;
- flex-direction: row;
- align-items: center;
- margin-top: 16rpx;
- padding: 8rpx 20rpx;
- border-radius: 999rpx;
- background-color: rgba(255, 255, 255, 0.72);
- }
- .mine-account-label,
- .mine-account-no,
- .mine-account-copy {
- font-size: 22rpx;
- color: #8a8d9e;
- }
- .mine-account-no {
- margin: 0 12rpx;
- color: #4b5563;
- }
- .mine-account-copy {
- color: #4a8cff;
- }
- .mine-follow-btn {
- align-self: center;
- margin-top: 20rpx;
- padding: 0 48rpx;
- height: 64rpx;
- line-height: 64rpx;
- font-size: 26rpx;
- color: #ffffff;
- background: linear-gradient(135deg, #4a8cff 0%, #6ec8ff 100%);
- border-radius: 999rpx;
- border: none;
- }
- .mine-login-btn,
- .mine-primary-btn {
- margin-top: 24rpx;
- padding: 0 40rpx;
- height: 72rpx;
- line-height: 72rpx;
- font-size: 28rpx;
- color: #ffffff;
- background: linear-gradient(135deg, #4a8cff 0%, #6ec8ff 100%);
- border-radius: 999rpx;
- border: none;
- }
- .mine-stats {
- display: flex;
- flex-direction: row;
- width: 100%;
- margin-top: 32rpx;
- padding: 0 8rpx;
- box-sizing: border-box;
- }
- .mine-stat {
- flex: 1;
- text-align: center;
- }
- .mine-stat-num {
- display: block;
- font-size: 36rpx;
- font-weight: 700;
- color: #1a1d26;
- line-height: 1.2;
- }
- .mine-stat-label {
- display: block;
- margin-top: 8rpx;
- font-size: 24rpx;
- color: #8a8d9e;
- }
- /* 购物车、收藏、关注店铺、足迹入口样式 */
- .mine-shortcut-bar {
- display: flex;
- justify-content: space-around;
- width: 100%;
- margin-top: 20rpx;
- padding: 24rpx 16rpx;
- box-sizing: border-box;
- background: #ffffff;
- border-radius: 20rpx;
- border: 2rpx solid #e8edf5;
- }
- .mine-shortcut-item {
- display: flex;
- flex-direction: column;
- align-items: center;
- position: relative;
- }
- .mine-shortcut-icon {
- font-size: 40rpx;
- line-height: 1;
- color: #ff6b35;
- }
- .mine-shortcut-text {
- font-size: 22rpx;
- color: #8a8d9e;
- margin-top: 8rpx;
- white-space: nowrap;
- }
- /* 订单快捷入口条 */
- .mine-order-bar {
- display: flex;
- justify-content: space-between;
- width: 100%;
- margin-top: 20rpx;
- padding: 24rpx 16rpx;
- box-sizing: border-box;
- background: #ffffff;
- border-radius: 20rpx;
- border: 2rpx solid #e8edf5;
- }
- .mine-order-bar-item {
- display: flex;
- flex-direction: column;
- align-items: center;
- position: relative;
- min-width: 0;
- }
- .mine-order-bar-icon {
- font-size: 40rpx;
- line-height: 1;
- }
- .mine-order-bar-text {
- font-size: 22rpx;
- color: #8a8d9e;
- margin-top: 8rpx;
- white-space: nowrap;
- }
- .mine-order-bar-sub {
- font-size: 18rpx;
- color: #b0b5c3;
- margin-top: 2rpx;
- white-space: nowrap;
- }
- .mine-order-bar-badge {
- position: absolute;
- top: -6rpx;
- right: -10rpx;
- transform: scale(0.8);
- }
- .mine-assets-bar {
- display: flex;
- align-items: center;
- width: 100%;
- margin-top: 28rpx;
- padding: 0 16rpx;
- box-sizing: border-box;
- background: #ffffff;
- border-radius: 20rpx;
- border: 2rpx solid #e8edf5;
- }
- .mine-diamond-bar {
- display: flex;
- flex-direction: row;
- align-items: center;
- justify-content: space-between;
- width: 100%;
- margin-top: 28rpx;
- padding: 28rpx 28rpx;
- box-sizing: border-box;
- background: #ffffff;
- border-radius: 20rpx;
- border: 2rpx solid #e8edf5;
- }
- .mine-diamond-left {
- display: flex;
- flex-direction: row;
- align-items: center;
- }
- .mine-diamond-icon {
- width: 44rpx;
- height: 44rpx;
- margin-right: 16rpx;
- }
- .mine-diamond-label {
- font-size: 28rpx;
- color: #121836;
- font-weight: 600;
- line-height: 40rpx;
- }
- .mine-diamond-right {
- display: flex;
- flex-direction: row;
- align-items: center;
- }
- .mine-diamond-value {
- font-size: 34rpx;
- font-weight: 700;
- color: #ff6b35;
- line-height: 44rpx;
- }
- .mine-diamond-arrow {
- font-size: 22rpx;
- color: #b0b5c3;
- margin-left: 10rpx;
- }
- .mine-assets-card {
- flex: 1;
- display: flex;
- align-items: center;
- justify-content: space-between;
- padding: 24rpx 20rpx 24rpx 24rpx;
- }
- .mine-assets-info {
- display: flex;
- flex-direction: column;
- }
- .mine-assets-title {
- font-size: 28rpx;
- font-weight: bold;
- color: #121836;
- line-height: 40rpx;
- }
- .mine-assets-desc {
- font-size: 20rpx;
- color: #ff6b35;
- line-height: 28rpx;
- }
- .mine-assets-arrow {
- font-size: 18rpx;
- color: #b0b5c3;
- margin-left: 4rpx;
- }
- .mine-assets-icon {
- font-size: 48rpx;
- color: #ff6b35;
- flex-shrink: 0;
- margin-right: 12rpx;
- }
- .mine-assets-divider {
- width: 2rpx;
- height: 60rpx;
- background: #e8edf5;
- flex-shrink: 0;
- }
- .mine-user-assets {
- display: flex;
- justify-content: space-around;
- width: 100%;
- max-width: 640rpx;
- margin-top: 20rpx;
- padding: 28rpx 16rpx;
- box-sizing: border-box;
- background: #ffffff;
- border-radius: 20rpx;
- border: 2rpx solid #e8edf5;
- }
- .mine-user-assets-item {
- display: flex;
- flex-direction: column;
- align-items: center;
- flex: 1;
- }
- .mine-user-assets-value {
- font-size: 36rpx;
- font-weight: bold;
- color: #121836;
- line-height: 50rpx;
- }
- .mine-user-assets-label {
- font-size: 22rpx;
- color: #8a8d9e;
- margin-top: 4rpx;
- }
- /* 分隔线 */
- .solid-left {
- border-left: 1px solid rgba(0, 0, 0, 0.06);
- padding-left: 20rpx;
- margin-left: 10rpx;
- }
- .bg-orange {
- background-color: #ff6b35 !important;
- }
- .mine-actions {
- display: flex;
- flex-direction: row;
- flex-wrap: wrap;
- justify-content: flex-start;
- width: 100%;
- margin-top: 36rpx;
- padding: 0 12rpx;
- box-sizing: border-box;
- }
- .mine-action {
- width: 20%;
- display: flex;
- flex-direction: column;
- align-items: center;
- margin-bottom: 20rpx;
- }
- .mine-action-icon-wrap {
- width: 96rpx;
- height: 96rpx;
- border-radius: 48rpx;
- background-color: #ffffff;
- border: 2rpx solid #e8edf5;
- box-shadow: 0 6rpx 20rpx rgba(74, 140, 255, 0.08);
- display: flex;
- align-items: center;
- justify-content: center;
- }
- .mine-action-icon-img {
- display: block;
- width: 48rpx;
- height: 48rpx;
- flex-shrink: 0;
- }
- .mine-action-icon-font {
- font-size: 44rpx;
- line-height: 1;
- }
- .mine-action-label {
- margin-top: 12rpx;
- font-size: 22rpx;
- color: #8a8d9e;
- text-align: center;
- }
- .mine-body {
- position: relative;
- z-index: 1;
- padding: 8rpx 28rpx 0;
- box-sizing: border-box;
- }
- .mine-tabs {
- display: flex;
- flex-direction: row;
- flex-wrap: wrap;
- margin-bottom: 24rpx;
- }
- .mine-tab-pill {
- padding: 12rpx 28rpx;
- margin-right: 16rpx;
- margin-bottom: 8rpx;
- border-radius: 999rpx;
- background-color: #eef1f6;
- }
- .mine-tab-pill--on {
- background-color: #ffffff;
- box-shadow: 0 4rpx 16rpx rgba(74, 140, 255, 0.12);
- }
- .mine-tab-pill-text {
- font-size: 28rpx;
- color: #8a8d9e;
- }
- .mine-tab-pill--on .mine-tab-pill-text {
- color: #4a8cff;
- font-weight: 600;
- }
- .mine-works-list {
- display: flex;
- flex-direction: row;
- flex-wrap: wrap;
- justify-content: space-between;
- width: 100%;
- box-sizing: border-box;
- }
- .mine-works-item {
- width: 48%;
- margin-bottom: 40rpx;
- box-sizing: border-box;
- overflow: hidden;
- }
- .mine-works-cover {
- position: relative;
- width: 100%;
- height: 460rpx;
- border-radius: 16rpx;
- overflow: hidden;
- background-color: #e8edf5;
- }
- .mine-works-cover-img,
- .mine-works-cover-empty {
- width: 100%;
- height: 100%;
- }
- .mine-works-cover-empty {
- background-color: #e8edf5;
- }
- .mine-works-video-badge {
- position: absolute;
- right: 16rpx;
- top: 16rpx;
- width: 48rpx;
- height: 48rpx;
- border-radius: 24rpx;
- background-color: rgba(0, 0, 0, 0.45);
- display: flex;
- align-items: center;
- justify-content: center;
- }
- .mine-works-video-badge-icon {
- color: #ffffff;
- font-size: 22rpx;
- }
- .mine-works-state {
- position: absolute;
- left: 0;
- right: 0;
- top: 0;
- bottom: 0;
- background-color: rgba(0, 0, 0, 0.42);
- display: flex;
- align-items: center;
- justify-content: center;
- padding: 16rpx;
- box-sizing: border-box;
- }
- .mine-works-state-txt {
- color: #ffffff;
- font-size: 26rpx;
- text-align: center;
- line-height: 1.4;
- }
- .mine-works-body {
- padding: 12rpx 4rpx 0;
- }
- .mine-works-desc {
- min-height: 72rpx;
- }
- .mine-works-tag {
- display: inline-block;
- margin-right: 8rpx;
- padding: 2rpx 10rpx;
- border-radius: 8rpx;
- background-color: #eef4ff;
- font-size: 20rpx;
- color: #4a8cff;
- vertical-align: middle;
- }
- .mine-works-text {
- font-size: 24rpx;
- color: #4b5563;
- line-height: 1.45;
- display: -webkit-box;
- -webkit-box-orient: vertical;
- -webkit-line-clamp: 2;
- line-clamp: 2;
- overflow: hidden;
- }
- .mine-works-foot {
- display: flex;
- flex-direction: row;
- align-items: center;
- justify-content: space-between;
- margin-top: 12rpx;
- }
- .mine-works-foot-left {
- display: flex;
- flex-direction: row;
- align-items: center;
- flex: 1;
- min-width: 0;
- }
- .mine-works-date {
- font-size: 22rpx;
- color: #b0b4c0;
- }
- .mine-works-del {
- margin-left: 12rpx;
- font-size: 22rpx;
- color: #ff4d6a;
- flex-shrink: 0;
- }
- .mine-works-like {
- display: flex;
- flex-direction: row;
- align-items: center;
- flex-shrink: 0;
- margin-left: 8rpx;
- }
- .mine-works-like-icon {
- font-size: 26rpx;
- color: #b0b4c0;
- line-height: 1;
- }
- .mine-works-like--on .mine-works-like-icon,
- .mine-works-like--on .mine-works-like-num {
- color: #ff4d6a;
- }
- .mine-works-like-num {
- margin-left: 6rpx;
- font-size: 22rpx;
- color: #8a8d9e;
- }
- .mine-grid {
- display: flex;
- flex-direction: row;
- flex-wrap: wrap;
- justify-content: space-between;
- width: 100%;
- box-sizing: border-box;
- }
- .mine-card {
- width: 48%;
- margin-bottom: 28rpx;
- box-sizing: border-box;
- }
- .mine-card-media {
- position: relative;
- width: 100%;
- height: 420rpx;
- border-radius: 24rpx;
- overflow: hidden;
- background-color: #e8edf5;
- }
- .mine-card-img {
- width: 100%;
- height: 100%;
- }
- .mine-card-play {
- position: absolute;
- right: 16rpx;
- top: 16rpx;
- width: 48rpx;
- height: 48rpx;
- border-radius: 24rpx;
- background-color: rgba(0, 0, 0, 0.45);
- display: flex;
- align-items: center;
- justify-content: center;
- }
- .mine-card-play-icon {
- color: #ffffff;
- font-size: 22rpx;
- }
- .mine-card-mask {
- position: absolute;
- left: 0;
- right: 0;
- top: 0;
- bottom: 0;
- background-color: rgba(0, 0, 0, 0.45);
- display: flex;
- align-items: center;
- justify-content: center;
- }
- .mine-card-mask-txt {
- color: #ffffff;
- font-size: 26rpx;
- }
- .mine-card-del {
- position: absolute;
- right: 12rpx;
- top: 12rpx;
- width: 44rpx;
- height: 44rpx;
- border-radius: 22rpx;
- background-color: rgba(0, 0, 0, 0.5);
- display: flex;
- align-items: center;
- justify-content: center;
- }
- .mine-card-del-txt {
- color: #ffffff;
- font-size: 32rpx;
- line-height: 1;
- }
- .mine-card-meta {
- display: flex;
- flex-direction: row;
- align-items: center;
- margin-top: 12rpx;
- padding: 0 4rpx;
- }
- .mine-card-stat {
- display: flex;
- flex-direction: row;
- align-items: center;
- margin-right: 24rpx;
- }
- .mine-card-stat-icon {
- font-size: 28rpx;
- color: #b0b4c0;
- line-height: 1;
- }
- .mine-card-stat-icon--on {
- color: #ff4d6a;
- }
- .mine-card-stat-icon--muted {
- font-size: 26rpx;
- }
- .mine-card-stat-num {
- margin-left: 8rpx;
- font-size: 24rpx;
- color: #8a8d9e;
- }
- .mine-empty--guest {
- padding-top: 48rpx;
- }
- .mine-card-caption {
- margin-top: 12rpx;
- padding: 0 4rpx;
- }
- .mine-card-tag {
- display: inline-block;
- margin-right: 8rpx;
- padding: 2rpx 10rpx;
- border-radius: 8rpx;
- background-color: #eef4ff;
- font-size: 20rpx;
- color: #4a8cff;
- vertical-align: middle;
- }
- .mine-card-text {
- font-size: 24rpx;
- color: #4b5563;
- line-height: 1.4;
- display: -webkit-box;
- -webkit-box-orient: vertical;
- -webkit-line-clamp: 2;
- line-clamp: 2;
- overflow: hidden;
- }
- .mine-card-series {
- margin-top: 12rpx;
- padding: 0 4rpx;
- }
- .mine-card-series-title {
- display: block;
- font-size: 26rpx;
- color: #1f2937;
- font-weight: 600;
- line-height: 1.4;
- }
- .mine-card-self {
- display: flex;
- flex-direction: row;
- align-items: center;
- justify-content: space-between;
- margin-top: 10rpx;
- padding: 0 4rpx;
- }
- .mine-card-date {
- font-size: 22rpx;
- color: #b0b4c0;
- }
- .mine-card-action {
- font-size: 22rpx;
- color: #4a8cff;
- margin-left: 16rpx;
- }
- .mine-card-action--del {
- color: #ff4d6a;
- }
- .mine-series-fab {
- position: fixed;
- right: 32rpx;
- bottom: 220rpx;
- width: 96rpx;
- height: 96rpx;
- border-radius: 48rpx;
- background: linear-gradient(135deg, #4a8cff, #6ea8ff);
- box-shadow: 0 8rpx 24rpx rgba(74, 140, 255, 0.35);
- display: flex;
- align-items: center;
- justify-content: center;
- z-index: 20;
- }
- .mine-series-fab-icon {
- color: #ffffff;
- font-size: 44rpx;
- line-height: 1;
- }
- .mine-empty {
- padding: 80rpx 32rpx;
- text-align: center;
- }
- .mine-empty-img {
- width: 320rpx;
- height: 320rpx;
- margin: 0 auto 24rpx;
- }
- .mine-empty-txt {
- display: block;
- font-size: 28rpx;
- color: #8a8d9e;
- margin-bottom: 24rpx;
- }
- .mine-more {
- padding: 16rpx 0 32rpx;
- text-align: center;
- }
- .mine-more-txt {
- font-size: 24rpx;
- color: #b0b4c0;
- }
- .mine-tabbar-space {
- width: 100%;
- }
- </style>
|