Files
hejiu/pages/circle-publish/circle-publish.vue
T
cg d335ec290a refactor(app): 重构应用主题和页面结构
- 移除动态主题切换功能,改为全局统一深色主题
- 实现自定义TabBar组件替换原生tabBar,解决iOS闪白问题
- 创建main容器页面统一管理三个tab页面的生命周期和状态
- 将所有页面的onShow/onHide等生命周期方法迁移到子组件内部
- 更新页面路径从index改为main,并调整路由跳转逻辑
- 移除theme-mixin和相关主题工具函数
- 统一页面背景色设置,优化用户体验一致性
2026-09-23 20:56:14 +08:00

445 lines
10 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<template>
<view class="page-container publish-page">
<!-- 顶部导航 -->
<view class="publish-nav" :style="{ paddingTop: navPaddingTop, paddingRight: navPaddingRight }">
<view class="publish-nav-inner">
<view class="publish-back" @click="goBack">
<text class="publish-back-icon">‹</text>
</view>
<text class="publish-nav-title">发布动态</text>
<view class="publish-send" :class="{ disabled: !canSubmit }" @click="handlePublish">
<text class="publish-send-text">发布</text>
</view>
</view>
</view>
<scroll-view class="publish-scroll" scroll-y>
<view class="publish-body">
<!-- 关联饮酒记录 -->
<view class="publish-record" v-if="lastRecord">
<view class="record-badge">
<text class="record-badge-icon">🍻</text>
</view>
<view class="record-info">
<text class="record-title">关联最近饮酒记录</text>
<text class="record-desc">{{ recordSummary }}</text>
</view>
<view class="record-check" :class="{ checked: linkRecord }" @click="linkRecord = !linkRecord">
<text class="record-check-icon">{{ linkRecord ? '✓' : '' }}</text>
</view>
</view>
<!-- 文字输入 -->
<textarea
class="publish-textarea"
v-model="text"
placeholder="分享你的饮酒故事..."
placeholder-class="publish-placeholder"
:maxlength="500"
auto-height
/>
<!-- 图片选择 -->
<view class="publish-images">
<view class="publish-img-item" v-for="(img, i) in images" :key="i">
<image class="publish-img" :src="img" mode="aspectFill"></image>
<view class="publish-img-del" @click="removeImage(i)">
<text class="publish-img-del-icon">×</text>
</view>
</view>
<view v-if="images.length < 9" class="publish-img-add" @click="chooseImage">
<text class="publish-img-add-icon">+</text>
<text class="publish-img-add-text">{{ images.length }}/9</text>
</view>
</view>
<!-- 可见范围 -->
<view class="publish-visibility">
<text class="publish-vis-label">谁可以看</text>
<view class="publish-vis-options">
<view
class="publish-vis-opt"
:class="{ active: visibility === 'friends' }"
@click="visibility = 'friends'"
>
<text>所有酒友</text>
</view>
<view
class="publish-vis-opt"
:class="{ active: visibility === 'private' }"
@click="visibility = 'private'"
>
<text>仅自己</text>
</view>
</view>
</view>
</view>
</scroll-view>
</view>
</template>
<script>
import { PublishFeed, UploadImage } from '../../common/api'
import client from '../../common/api'
export default {
data() {
const menuBtn = uni.getMenuButtonBoundingClientRect()
const sysInfo = uni.getSystemInfoSync()
return {
navPaddingTop: menuBtn.top + 'px',
navPaddingRight: (sysInfo.windowWidth - menuBtn.left + 8) + 'px',
text: '',
images: [],
visibility: 'friends',
linkRecord: true,
lastRecord: null,
publishing: false
}
},
computed: {
canSubmit() {
return (this.text.trim() || this.images.length) && !this.publishing
},
recordSummary() {
if (!this.lastRecord) return ''
const drinks = this.lastRecord.drinks || []
if (!drinks.length) return '戒酒打卡'
return drinks.map(d => `${d.brand || d.product || d.category} ${d.amount}${d.unit}`).join('、')
}
},
onLoad() {
this.loadLastRecord()
},
methods: {
goBack() {
uni.navigateBack()
},
async loadLastRecord() {
try {
const res = await client.GetRecords({ page: 1, pageSize: 5, month: '', mode: '' })
const list = res.list || []
const drank = list.find(r => String(r.mode) === 'drank' || String(r.mode) === 'Drank')
if (drank) this.lastRecord = drank
} catch (e) { /* ignore */ }
},
chooseImage() {
const remaining = 9 - this.images.length
uni.chooseImage({
count: remaining,
sizeType: ['compressed'],
sourceType: ['album', 'camera'],
success: (res) => {
this.images = [...this.images, ...res.tempFilePaths].slice(0, 9)
}
})
},
removeImage(index) {
this.images.splice(index, 1)
},
async handlePublish() {
if (!this.canSubmit) return
this.publishing = true
try {
// 先上传图片获取远程URL
let uploadedUrls = []
if (this.images.length) {
uni.showLoading({ title: '上传图片中...' })
for (const img of this.images) {
const upRes = await UploadImage({ filePath: img })
if (upRes.data && upRes.data.url) {
uploadedUrls.push(upRes.data.url)
}
}
uni.hideLoading()
}
await PublishFeed({
text: this.text,
images: uploadedUrls,
recordId: this.linkRecord && this.lastRecord ? this.lastRecord.id : null,
visibility: this.visibility
})
uni.showToast({ title: '发布成功 🍻', icon: 'none' })
setTimeout(() => uni.navigateBack(), 800)
} catch (e) {
uni.hideLoading()
// 全局拦截器已弹出错误提示
}
this.publishing = false
}
}
}
</script>
<style lang="scss" scoped>
.publish-page {
display: flex;
flex-direction: column;
height: 100vh;
overflow: hidden;
}
.publish-nav {
background: $bg-base;
padding-left: $sp-lg;
padding-right: $sp-lg;
position: relative;
z-index: 10;
}
.publish-nav-inner {
display: flex;
align-items: center;
justify-content: space-between;
height: 88rpx;
}
.publish-back {
width: 64rpx;
height: 64rpx;
display: flex;
align-items: center;
justify-content: center;
border-radius: 50%;
background: $bg-card;
&:active { background: $bg-card-alt; }
}
.publish-back-icon {
font-size: $fs-xl;
color: $text-primary;
font-weight: $fw-bold;
}
.publish-nav-title {
font-size: $fs-base;
font-weight: $fw-bold;
color: $text-primary;
}
.publish-send {
padding: $sp-sm $sp-lg;
background: linear-gradient(135deg, $amber, $amber-deep);
border-radius: $radius-full;
&.disabled { opacity: 0.4; }
&:active { transform: scale(0.95); }
}
.publish-send-text {
font-size: $fs-sm;
font-weight: $fw-bold;
color: $text-on-amber;
}
.publish-scroll {
flex: 1;
height: 0;
}
.publish-body {
padding: $sp-lg;
padding-bottom: calc(env(safe-area-inset-bottom) + #{$sp-2xl});
}
/* 关联记录 */
.publish-record {
display: flex;
align-items: center;
gap: $sp-md;
padding: $sp-lg;
background: rgba(232,168,56,0.06);
border: 1rpx solid rgba(232,168,56,0.15);
border-radius: $radius-lg;
margin-bottom: $sp-xl;
}
.record-badge {
width: 72rpx;
height: 72rpx;
border-radius: $radius-md;
background: rgba(232,168,56,0.12);
display: flex;
align-items: center;
justify-content: center;
flex-shrink: 0;
}
.record-badge-icon {
font-size: 36rpx;
}
.record-info {
flex: 1;
min-width: 0;
display: flex;
flex-direction: column;
gap: 4rpx;
}
.record-title {
font-size: $fs-sm;
font-weight: $fw-medium;
color: $text-primary;
}
.record-desc {
font-size: $fs-xs;
color: $text-tertiary;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.record-check {
width: 44rpx;
height: 44rpx;
border-radius: 50%;
border: 2rpx solid var(--border-subtle, rgba(255,255,255,0.10));
display: flex;
align-items: center;
justify-content: center;
flex-shrink: 0;
&.checked {
background: $amber;
border-color: $amber;
}
}
.record-check-icon {
font-size: $fs-sm;
color: $text-on-amber;
font-weight: $fw-bold;
}
/* 文字输入 */
.publish-textarea {
width: 100%;
min-height: 240rpx;
font-size: $fs-base;
color: $text-primary;
line-height: $lh-loose;
padding: $sp-lg;
background: $bg-card;
border-radius: $radius-xl;
border: 1rpx solid var(--border-faint, rgba(255,255,255,0.08));
margin-bottom: $sp-xl;
box-sizing: border-box;
}
.publish-placeholder {
color: $text-tertiary;
}
/* 图片 */
.publish-images {
display: flex;
flex-wrap: wrap;
gap: $sp-sm;
margin-bottom: $sp-xl;
}
.publish-img-item {
position: relative;
width: calc(33.33% - 11rpx);
height: 200rpx;
}
.publish-img {
width: 100%;
height: 100%;
border-radius: $radius-md;
}
.publish-img-del {
position: absolute;
top: -12rpx;
right: -12rpx;
width: 40rpx;
height: 40rpx;
border-radius: 50%;
background: $coral;
display: flex;
align-items: center;
justify-content: center;
}
.publish-img-del-icon {
font-size: $fs-sm;
color: #fff;
font-weight: $fw-bold;
}
.publish-img-add {
width: calc(33.33% - 11rpx);
height: 200rpx;
border-radius: $radius-md;
border: 2rpx dashed var(--border-dashed, rgba(255,255,255,0.12));
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
gap: 8rpx;
&:active {
background: var(--bg-hover, rgba(255,255,255,0.08));
}
}
.publish-img-add-icon {
font-size: $fs-xl;
color: $text-tertiary;
}
.publish-img-add-text {
font-size: $fs-xs;
color: $text-tertiary;
}
/* 可见范围 */
.publish-visibility {
display: flex;
align-items: center;
justify-content: space-between;
padding: $sp-lg;
background: $bg-card;
border-radius: $radius-lg;
border: 1rpx solid var(--border-faint, rgba(255,255,255,0.08));
}
.publish-vis-label {
font-size: $fs-base;
color: $text-primary;
font-weight: $fw-medium;
}
.publish-vis-options {
display: flex;
gap: $sp-sm;
}
.publish-vis-opt {
padding: 8rpx 24rpx;
border-radius: $radius-full;
background: $bg-elevated;
border: 1rpx solid var(--border-micro, rgba(255,255,255,0.05));
text {
font-size: $fs-sm;
color: $text-tertiary;
}
&.active {
background: rgba(232,168,56,0.12);
border-color: rgba(232,168,56,0.3);
text {
color: $amber;
font-weight: $fw-medium;
}
}
}
</style>