topfans/frontend/pages/support-activity/components/MessageInput.vue

272 lines
7.3 KiB
Vue

<template>
<!--
Figma 147:325 (Group 50, 296x42)
展示型留言条:左侧圆形头像 + 中间居中文本 + 右侧表情 + 发送箭头
点击整条 / 点击发送箭头会触发 send 事件,父组件可弹出真实输入面板
-->
<view class="message-row" @click="handleTap">
<!-- 外层 pill 容器 (296x42, radius 20) -->
<view class="message-row__pill">
<!-- 内嵌凹槽 (238x30, radius 16) -->
<view class="message-row__groove">
<text class="message-row__text">{{ displayText }}</text>
</view>
</view>
<!-- 左侧圆形头像 (35x35) -->
<view class="message-row__avatar">
<image
class="message-row__avatar-img"
:src="avatar || defaultAvatar"
mode="aspectFill"
/>
<image
class="message-row__avatar-fallback"
src="/static/square/gerentouxiangkuang.png"
mode="aspectFill"
/>
</view>
<!-- 骰子表情 (17x17):点击按顺序循环替换 text -->
<image
class="message-row__emoji"
:class="{ 'is-pressing': pressing }"
src="/static/rank/activity-support-icon/message-row/emoji.png"
mode="aspectFit"
@click.stop="handleEmoji"
@touchstart="pressing = true"
@touchend="pressing = false"
@touchcancel="pressing = false"
/>
<!-- 发送箭头 (21x21) -->
<view class="message-row__send" @click.stop="handleSend">
<image
class="message-row__send-img"
src="/static/rank/activity-support-icon/message-row/send.png"
mode="aspectFit"
/>
</view>
</view>
</template>
<script setup>
import { computed, ref } from "vue";
import { useStore } from "vuex";
/**
* 点击骰子 emoji 时按顺序循环选用的祝福语。
* 顺序循环:首次取第一条,再点取下一条,到末尾后回到开头。
* 调整 / 增删文案直接改这里即可。
*/
const GREETINGS = [
"今天第一天姐妹们大家冲鸭!!!",
"冲冲冲!!!生日应援走起~",
"永远支持你!生日快乐 🎂",
"前排打卡!爱了爱了 ❤️",
"姐妹们冲鸭,今天也要元气满满~",
"生日快乐 🎉 永远守护你!",
"第一第一!!我永远在你身后",
"鸭鸭冲鸭!为爱发电不停歇~",
];
// 模块级游标:组件被卸载重建时不会从 0 开始,避免反复重置
let greetingCursor = 0;
function pickNextGreeting() {
if (GREETINGS.length === 0) return "";
const next = GREETINGS[greetingCursor % GREETINGS.length];
greetingCursor += 1;
return next;
}
const props = defineProps({
/**
* 展示的文案:父组件可传入最新一条留言,默认使用 Figma 设计稿文案。
* 推荐通过 v-model:text 双向绑定,以便点击 emoji 时组件能通知父组件更新。
*/
text: {
type: String,
default: "",
},
/**
* 头像 URL,未传时回退到当前登录用户的 avatar_url。
*/
avatar: {
type: String,
default: "",
},
/**
* 兼容旧用法:外部如果还在传 placeholder,作为 text 的兜底。
*/
placeholder: {
type: String,
default: "",
},
});
const emit = defineEmits(["send", "tap", "update:text"]);
// 兜底默认头像:不再硬编码,直接读取 Vuex user 模块中的 avatar_url
// (登录时已通过 SET_USER_INFO 写入 store 与本地缓存,头像更新后也会触发 userInfoUpdated 通知)
const store = useStore();
const userInfo = computed(() => store.state.user?.userInfo || null);
const defaultAvatar = computed(() => userInfo.value?.avatar_url || "");
// 按下反馈:触摸 / 按下时短暂为 true,松开恢复
const pressing = ref(false);
const displayText = computed(() => {
if (props.text && props.text.trim().length > 0) return props.text;
if (props.placeholder && props.placeholder.trim().length > 0)
return props.placeholder;
return "今天第一天姐妹们大家冲鸭!!!";
});
function handleTap() {
emit("tap");
}
function handleEmoji() {
// 抛 update:text 让父组件以 v-model:text 接收;不修改 props.text 本地副本,
// 避免父组件没接 v-model 时出现"看起来生效但父组件状态没变"的伪成功。
emit("update:text", pickNextGreeting());
}
function handleSend() {
// 展示型组件:点击发送图标抛出事件,业务侧决定弹出输入面板 / 直接发送等行为
emit("send", displayText.value);
}
</script>
<style scoped>
/*
* 设计稿原始尺寸 (px) → rpx (按 750 / 375 = 2 倍换算)
* 容器 296 x 42 → 592rpx x 84rpx
* 凹槽 238 x 30 → 476rpx x 60rpx (left 12px=24rpx,top 6px=12rpx)
* 头像 35 x 35 → 70rpx x 70rpx (left 6px=12rpx,top 3px=6rpx)
* 文本 11px → 22rpx
* emoji 17 x 17 → 34rpx x 34rpx
* send 21 x 21 → 42rpx x 42rpx
*/
.message-row {
position: fixed;
left: 22rpx; /* 11px */
bottom: 32rpx;
width: 592rpx;
height: 84rpx;
z-index: 50;
}
/* 外层 pill */
.message-row__pill {
position: absolute;
inset: 0;
background: rgba(217, 217, 217, 0.4);
border-radius: 40rpx; /* 20px */
box-shadow: 2rpx 4rpx 8rpx 0 rgba(0, 0, 0, 0.25);
}
/* 内嵌凹槽 */
.message-row__groove {
position: absolute;
left: 24rpx; /* 12px */
top: 12rpx; /* 6px */
width: 476rpx; /* 238px */
height: 60rpx; /* 30px */
background: rgba(54, 51, 51, 0.1);
border-radius: 32rpx; /* 16px */
display: flex;
align-items: center;
/* 头像突出在 pill 左侧,文字从凹槽右半段开始;末尾留出表情 + 发送图标空间 */
padding-left: 80rpx;
padding-right: 120rpx;
overflow: hidden;
}
.message-row__text {
display: block;
width: 100%;
font-size: 22rpx; /* 11px */
line-height: 1.3;
color: #ffffff;
font-weight: bold;
font-family: "Abhaya Libre ExtraBold", "PingFang SC", sans-serif;
text-shadow: -2rpx 2rpx 8rpx rgba(206, 9, 9, 0.45);
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
/* 设计稿的轻微倾斜 */
transform: rotate(-0.44deg) skewX(-0.1deg);
transform-origin: center;
}
/* 左侧头像 (突出 pill 左缘) */
.message-row__avatar {
position: absolute;
left: 12rpx; /* 6px */
top: 6rpx; /* 3px */
width: 70rpx; /* 35px */
height: 70rpx;
border-radius: 50%;
overflow: hidden;
box-shadow: 0 0 14rpx 0 rgba(203, 24, 24, 0.84);
background: #2a1111;
}
.message-row__avatar-img {
width: 100%;
height: 100%;
display: block;
}
.message-row__avatar-fallback {
width: 100%;
height: 100%;
display: block;
position: absolute;
top: 0;
transform: scale(1.05);
}
/* 骰子表情 */
.message-row__emoji {
position: absolute;
right: 92rpx; /* ≈ 46px from right edge */
top: 24rpx; /* (84-34)/2 ≈ 25 */
width: 34rpx; /* 17px */
height: 34rpx;
filter: drop-shadow(0 4rpx 8rpx rgba(138, 6, 6, 0.74));
transition: transform 0.12s ease;
}
/* emoji 按下反馈(顺序循环选祝福语的可点击提示) */
.message-row__emoji.is-pressing,
.message-row__emoji:active {
transform: scale(0.85);
}
/* 右侧发送箭头 (可点击) */
.message-row__send {
position: absolute;
right: 28rpx; /* ≈ 14px */
top: 20rpx; /* 21 */
width: 42rpx; /* 21px */
height: 42rpx;
display: flex;
align-items: center;
justify-content: center;
filter: drop-shadow(0 6rpx 8rpx rgba(0, 0, 0, 0.47));
}
.message-row__send-img {
width: 100%;
height: 100%;
}
.message-row__send:active {
transform: scale(0.92);
transition: transform 0.12s ease;
}
</style>