Merge branch 'Branch_1' of https://git.u8t.cn/tangxinyue/alipay-emulator into Branch_1
# Conflicts: # pages/index/index.nvue
|
|
@ -261,6 +261,7 @@
|
|||
}
|
||||
|
||||
.flex-column {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -4,12 +4,12 @@
|
|||
@touchmove="touchMove($event, index)" @touchend="touchEnd($event, index)"
|
||||
:style="{ 'transform': 'translateX(' + swiperList[index] + 'px)', 'transition': swiperList[index] == 0 ? 'all 0.3s' : '' }">
|
||||
<view class="avatar" v-if="type != 'xiaomi'">
|
||||
<!-- 显示图片头像 -->
|
||||
<image class="iosAvatar" v-if="type == 'ios' && item.avatar" :src="item.avatar" mode=""></image>
|
||||
<!-- 显示文字头像 -->
|
||||
<view v-else-if="type == 'ios' && item.name && item.avatarType === 'text'" class="text-avatar">
|
||||
<view v-if="type == 'ios' && item.name && item.avatarType === 'text'" class="text-avatar" :class="{'text-avatar-18':getAvatarLength(item.name)==2}">
|
||||
{{ getAvatarText(item.name) }}
|
||||
</view>
|
||||
<!-- 显示图片头像 -->
|
||||
<image class="iosAvatar" v-else-if="type == 'ios' && item.avatar" :src="item.avatar" mode=""></image>
|
||||
<!-- 默认头像 -->
|
||||
<image v-else-if="type == 'ios'" src="/static/image/call/iosAvatar.png" mode=""></image>
|
||||
<!-- 其他类型的状态图标 -->
|
||||
|
|
@ -133,7 +133,7 @@
|
|||
</view>
|
||||
<view class="form-item">
|
||||
<text class="form-label">运营商</text>
|
||||
<uni-data-select v-model="editForm.yys" :localdata="yysOptions" @change="onYysChange"
|
||||
<uni-data-select class="form-select" v-model="editForm.yys" :localdata="yysOptions" @change="onYysChange"
|
||||
:clear="false"></uni-data-select>
|
||||
</view>
|
||||
<view class="form-item">
|
||||
|
|
@ -148,13 +148,13 @@
|
|||
</view>
|
||||
<view class="form-item">
|
||||
<text class="form-label">卡几</text>
|
||||
<uni-data-select v-model="editForm.kj" :localdata="kjOptions" @change="onKjChange"
|
||||
<uni-data-select class="form-select" v-model="editForm.kj" :localdata="kjOptions" @change="onKjChange"
|
||||
:clear="false"></uni-data-select>
|
||||
|
||||
</view>
|
||||
<view class="form-item">
|
||||
<text class="form-label">状态</text>
|
||||
<uni-data-select v-model="editForm.status" :localdata="statusOptions" @change="onStatusChange"
|
||||
<uni-data-select class="form-select" v-model="editForm.status" :localdata="statusOptions" @change="onStatusChange"
|
||||
:clear="false"></uni-data-select>
|
||||
</view>
|
||||
<view class="form-item">
|
||||
|
|
@ -163,7 +163,7 @@
|
|||
</view>
|
||||
<view class="form-item">
|
||||
<text class="form-label">头像类型</text>
|
||||
<uni-data-select v-model="editForm.avatarType" :localdata="avatarTypeOptions" @change="onAvatarTypeChange"
|
||||
<uni-data-select class="form-select" v-model="editForm.avatarType" :localdata="avatarTypeOptions" @change="onAvatarTypeChange"
|
||||
:clear="false"></uni-data-select>
|
||||
</view>
|
||||
<view class="form-item">
|
||||
|
|
@ -211,28 +211,28 @@
|
|||
<input class="form-input" v-model="addForm.name" placeholder="请输入姓名" />
|
||||
</view>
|
||||
<view class="form-item">
|
||||
<text class="form-label">电话 *</text>
|
||||
<text class="form-label">电话 <text>*</text></text>
|
||||
<input class="form-input" v-model="addForm.phone" placeholder="请输入电话" />
|
||||
</view>
|
||||
<view class="form-item">
|
||||
<text class="form-label">运营商 *</text>
|
||||
<uni-data-select v-model="addForm.yys" :localdata="yysOptions" @change="onAddYysChange"
|
||||
<text class="form-label">运营商 <text>*</text></text>
|
||||
<uni-data-select class="form-select" v-model="addForm.yys" :localdata="yysOptions" @change="onAddYysChange"
|
||||
:clear="false"></uni-data-select>
|
||||
</view>
|
||||
<view class="form-item">
|
||||
<text class="form-label">时间 *</text>
|
||||
<text class="form-label">时间 <text>*</text></text>
|
||||
<view class="form-input form-input-time" @click="setAddTimeType">
|
||||
{{ addForm.time }}
|
||||
</view>
|
||||
</view>
|
||||
<view class="form-item">
|
||||
<text class="form-label">状态 *</text>
|
||||
<uni-data-select v-model="addForm.status" :localdata="statusOptions" @change="onAddStatusChange"
|
||||
<text class="form-label">状态 <text>*</text></text>
|
||||
<uni-data-select class="form-select" v-model="addForm.status" :localdata="statusOptions" @change="onAddStatusChange"
|
||||
:clear="false"></uni-data-select>
|
||||
</view>
|
||||
<view class="form-item">
|
||||
<text class="form-label">卡几</text>
|
||||
<uni-data-select v-model="addForm.kj" :localdata="kjOptions" @change="onAddKjChange"
|
||||
<uni-data-select class="form-select" v-model="addForm.kj" :localdata="kjOptions" @change="onAddKjChange"
|
||||
:clear="false"></uni-data-select>
|
||||
</view>
|
||||
<view class="form-item">
|
||||
|
|
@ -249,7 +249,7 @@
|
|||
</view>
|
||||
<view class="form-item">
|
||||
<text class="form-label">头像类型</text>
|
||||
<uni-data-select v-model="addForm.avatarType" :localdata="avatarTypeOptions" @change="onAddAvatarTypeChange"
|
||||
<uni-data-select class="form-select" v-model="addForm.avatarType" :localdata="avatarTypeOptions" @change="onAddAvatarTypeChange"
|
||||
:clear="false"></uni-data-select>
|
||||
</view>
|
||||
<view class="form-item">
|
||||
|
|
@ -264,9 +264,13 @@
|
|||
<view v-if="addForm.avatar" class="avatar-clear" @click="clearAvatar">清除</view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="form-item">
|
||||
<text class="form-label">随机数据</text>
|
||||
<image class="suiji" src="/static/image/common/random-dice.png" mode="" @click="generateRandomData"></image>
|
||||
</view>
|
||||
</view>
|
||||
<view class="modal-footer">
|
||||
<view class="btn btn-random" @click="generateRandomData">随机数据</view>
|
||||
<!-- <view class="btn btn-random" @click="generateRandomData">随机数据</view> -->
|
||||
<view class="btn btn-cancel" @click="closeAddModal">取消</view>
|
||||
<view class="btn btn-confirm" @click="saveAdd">保存</view>
|
||||
</view>
|
||||
|
|
@ -328,7 +332,7 @@
|
|||
"address": "北京",
|
||||
"time": "2026-02-01 10:00:00",
|
||||
"status": 1,
|
||||
avatarType: 'image', // image 或 text
|
||||
"avatarType": 'image', // image 或 text
|
||||
"notes": "工作往来"
|
||||
},
|
||||
{
|
||||
|
|
@ -341,7 +345,7 @@
|
|||
"address": "上海",
|
||||
"time": "2026-02-02 14:30:00",
|
||||
"status": 4,
|
||||
avatarType: 'image', // image 或 text
|
||||
"avatarType": 'image', // image 或 text
|
||||
"notes": "老朋友"
|
||||
},
|
||||
{
|
||||
|
|
@ -354,7 +358,7 @@
|
|||
"address": "广州",
|
||||
"time": "2026-03-03 16:45:00",
|
||||
"status": 0,
|
||||
avatarType: 'image', // image 或 text
|
||||
"avatarType": 'image', // image 或 text
|
||||
"notes": "推销电话"
|
||||
},
|
||||
{
|
||||
|
|
@ -367,7 +371,7 @@
|
|||
"address": "深圳",
|
||||
"time": "2026-03-04 10:30:00",
|
||||
"status": 2,
|
||||
avatarType: 'image', // image 或 text
|
||||
"avatarType": 'image', // image 或 text
|
||||
"notes": "快递小哥"
|
||||
},
|
||||
{
|
||||
|
|
@ -380,7 +384,7 @@
|
|||
"address": "杭州",
|
||||
"time": "2026-03-05 15:00:00",
|
||||
"status": 3,
|
||||
avatarType: 'image', // image 或 text
|
||||
"avatarType": 'image', // image 或 text
|
||||
"notes": "大学同学"
|
||||
},
|
||||
{
|
||||
|
|
@ -393,7 +397,7 @@
|
|||
"address": "成都",
|
||||
"time": "2026-03-01 18:00:00",
|
||||
"status": 5,
|
||||
avatarType: 'image', // image 或 text
|
||||
"avatarType": 'image', // image 或 text
|
||||
"notes": "骚扰电话"
|
||||
},
|
||||
{
|
||||
|
|
@ -406,7 +410,7 @@
|
|||
"address": "武汉",
|
||||
"time": "2026-02-05 18:30:00",
|
||||
"status": 1,
|
||||
avatarType: 'image', // image 或 text
|
||||
"avatarType": 'image', // image 或 text
|
||||
"notes": "客户"
|
||||
},
|
||||
{
|
||||
|
|
@ -419,7 +423,7 @@
|
|||
"address": "西安",
|
||||
"time": "2026-02-05 18:30:00",
|
||||
"status": 4,
|
||||
avatarType: 'image', // image 或 text
|
||||
"avatarType": 'image', // image 或 text
|
||||
"notes": "家人"
|
||||
},
|
||||
{
|
||||
|
|
@ -432,7 +436,7 @@
|
|||
"address": "南京",
|
||||
"time": "2026-02-05 18:30:00",
|
||||
"status": 0,
|
||||
avatarType: 'image', // image 或 text
|
||||
"avatarType": 'image', // image 或 text
|
||||
"notes": "广告推销"
|
||||
},
|
||||
{
|
||||
|
|
@ -445,7 +449,7 @@
|
|||
"address": "长沙",
|
||||
"time": "2026-03-05 18:30:00",
|
||||
"status": 2,
|
||||
avatarType: 'image', // image 或 text
|
||||
"avatarType": 'image', // image 或 text
|
||||
"notes": "同事"
|
||||
},
|
||||
{
|
||||
|
|
@ -458,7 +462,7 @@
|
|||
"address": "天津",
|
||||
"time": "2025-08-05 18:30:00",
|
||||
"status": 3,
|
||||
avatarType: 'image', // image 或 text
|
||||
"avatarType": 'image', // image 或 text
|
||||
"notes": "未知号码"
|
||||
},
|
||||
{
|
||||
|
|
@ -471,7 +475,7 @@
|
|||
"address": "苏州",
|
||||
"time": "2026-03-05 18:30:00",
|
||||
"status": 5,
|
||||
avatarType: 'image', // image 或 text
|
||||
"avatarType": 'image', // image 或 text
|
||||
"notes": "健身房"
|
||||
},
|
||||
{
|
||||
|
|
@ -484,7 +488,7 @@
|
|||
"address": "青岛",
|
||||
"time": "2026-03-05 18:30:00",
|
||||
"status": 1,
|
||||
avatarType: 'image', // image 或 text
|
||||
"avatarType": 'image', // image 或 text
|
||||
"notes": "学校老师"
|
||||
},
|
||||
{
|
||||
|
|
@ -497,7 +501,7 @@
|
|||
"address": "大连",
|
||||
"time": "2026-03-05 18:30:00",
|
||||
"status": 4,
|
||||
avatarType: 'image', // image 或 text
|
||||
"avatarType": 'image', // image 或 text
|
||||
"notes": "保险推销"
|
||||
},
|
||||
{
|
||||
|
|
@ -510,7 +514,7 @@
|
|||
"address": "厦门",
|
||||
"time": "2026-03-05 18:30:00",
|
||||
"status": 0,
|
||||
avatarType: 'image', // image 或 text
|
||||
"avatarType": 'image', // image 或 text
|
||||
"notes": "外卖"
|
||||
},
|
||||
{
|
||||
|
|
@ -523,7 +527,7 @@
|
|||
"address": "宁波",
|
||||
"time": "2026-03-05 18:30:00",
|
||||
"status": 2,
|
||||
avatarType: 'image', // image 或 text
|
||||
"avatarType": 'image', // image 或 text
|
||||
"notes": "亲戚"
|
||||
},
|
||||
{
|
||||
|
|
@ -536,7 +540,7 @@
|
|||
"address": "郑州",
|
||||
"time": "2026-03-05 18:30:00",
|
||||
"status": 3,
|
||||
avatarType: 'image', // image 或 text
|
||||
"avatarType": 'image', // image 或 text
|
||||
"notes": "房产中介"
|
||||
},
|
||||
{
|
||||
|
|
@ -549,7 +553,7 @@
|
|||
"address": "沈阳",
|
||||
"time": "2026-03-05 18:30:00",
|
||||
"status": 5,
|
||||
avatarType: 'image', // image 或 text
|
||||
"avatarType": 'image', // image 或 text
|
||||
"notes": "银行客服"
|
||||
},
|
||||
{
|
||||
|
|
@ -562,7 +566,7 @@
|
|||
"address": "济南",
|
||||
"time": "2026-02-05 18:30:00",
|
||||
"status": 1,
|
||||
avatarType: 'image', // image 或 text
|
||||
"avatarType": 'image', // image 或 text
|
||||
"notes": "合作伙伴"
|
||||
},
|
||||
{
|
||||
|
|
@ -575,7 +579,7 @@
|
|||
"address": "重庆",
|
||||
"time": "2026-03-05 18:30:00",
|
||||
"status": 4,
|
||||
avatarType: 'image', // image 或 text
|
||||
"avatarType": 'image', // image 或 text
|
||||
"notes": "骚扰电话"
|
||||
}
|
||||
]);
|
||||
|
|
@ -598,7 +602,7 @@
|
|||
kj: '1',
|
||||
address: '',
|
||||
time: '',
|
||||
status: '0',
|
||||
status: 0,
|
||||
notes: ''
|
||||
});
|
||||
const editIndex = ref(-1);
|
||||
|
|
@ -615,7 +619,7 @@
|
|||
kj: '1',
|
||||
address: '',
|
||||
time: '',
|
||||
status: '0',
|
||||
status: 0,
|
||||
notes: ''
|
||||
});
|
||||
|
||||
|
|
@ -632,27 +636,27 @@
|
|||
// 状态选项(对象数组,用于显示中文文本)
|
||||
const statusOptions = ref([{
|
||||
value: 0,
|
||||
text: '来电-未接'
|
||||
},
|
||||
{
|
||||
value: 1,
|
||||
text: '来电-已接'
|
||||
},
|
||||
{
|
||||
value: 2,
|
||||
text: '来电-拒接'
|
||||
},
|
||||
{
|
||||
value: 3,
|
||||
text: '播出-未接'
|
||||
},
|
||||
{
|
||||
value: 4,
|
||||
value: 1,
|
||||
text: '播出-已接'
|
||||
},
|
||||
{
|
||||
value: 5,
|
||||
value: 2,
|
||||
text: '播出-拒接'
|
||||
},
|
||||
{
|
||||
value: 3,
|
||||
text: '来电-未接'
|
||||
},
|
||||
{
|
||||
value: 4,
|
||||
text: '来电-已接'
|
||||
},
|
||||
{
|
||||
value: 5,
|
||||
text: '来电-拒接'
|
||||
}
|
||||
]);
|
||||
// 运营商选项
|
||||
|
|
@ -791,7 +795,7 @@
|
|||
editForm.kj = item.kj || '1';
|
||||
editForm.address = item.address || '';
|
||||
editForm.time = item.time || '';
|
||||
editForm.status = item.status || '0';
|
||||
editForm.status = item.status ||0;
|
||||
editForm.notes = item.notes || '';
|
||||
showEditModal.value = true;
|
||||
};
|
||||
|
|
@ -887,7 +891,7 @@
|
|||
addForm.address = '';
|
||||
addForm.time =
|
||||
`${currentYear}-${currentMonth + 1}-${currentDay} ${now.getHours()}:${now.getMinutes()}:${now.getSeconds()}`;
|
||||
addForm.status = '0';
|
||||
addForm.status = 0;
|
||||
addForm.notes = '';
|
||||
showAddModal.value = true;
|
||||
console.log(showAddModal.value)
|
||||
|
|
@ -920,13 +924,13 @@
|
|||
});
|
||||
return;
|
||||
}
|
||||
if (!addForm.status) {
|
||||
uni.showToast({
|
||||
title: '请选择状态',
|
||||
icon: 'none'
|
||||
});
|
||||
return;
|
||||
}
|
||||
// if (!addForm.status) {
|
||||
// uni.showToast({
|
||||
// title: '请选择状态',
|
||||
// icon: 'none'
|
||||
// });
|
||||
// return;
|
||||
// }
|
||||
|
||||
// 创建新联系人
|
||||
const newItem = {
|
||||
|
|
@ -1169,8 +1173,21 @@
|
|||
// 获取头像文字
|
||||
const getAvatarText = (name) => {
|
||||
if (!name) return '';
|
||||
// 取姓名的第一个字符
|
||||
// 获取字符串长度(考虑中文字符)
|
||||
const length = name.length;
|
||||
|
||||
if (length === 2) {
|
||||
// 两个字:返回全部
|
||||
return name;
|
||||
} else {
|
||||
// 返回第一个字符
|
||||
return name.charAt(0);
|
||||
}
|
||||
};
|
||||
// 获取头像文字
|
||||
const getAvatarLength = (name) => {
|
||||
const length = name.length;
|
||||
return length
|
||||
};
|
||||
|
||||
// 获取头像数字
|
||||
|
|
@ -1245,7 +1262,7 @@
|
|||
addForm.kj = Math.random() > 0.5 ? '1' : '2';
|
||||
addForm.address = addresses[Math.floor(Math.random() * addresses.length)];
|
||||
addForm.time = randomTime();
|
||||
addForm.status = Math.floor(Math.random() * 6).toString();
|
||||
addForm.status = Math.floor(Math.random() * 6);
|
||||
addForm.notes = notes[Math.floor(Math.random() * notes.length)];
|
||||
addForm.avatar = ''; // 随机数据不生成头像
|
||||
addForm.avatarType = Math.random() > 0.5 ? 'image' : 'text'; // 随机选择头像类型
|
||||
|
|
@ -1725,6 +1742,9 @@
|
|||
|
||||
.form-item {
|
||||
margin-bottom: 30rpx;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.form-label {
|
||||
|
|
@ -1732,10 +1752,13 @@
|
|||
font-size: 28rpx;
|
||||
color: #666;
|
||||
margin-bottom: 16rpx;
|
||||
text{
|
||||
color: red;
|
||||
}
|
||||
}
|
||||
|
||||
.form-input {
|
||||
width: 100%;
|
||||
width: 364rpx;
|
||||
height: 80rpx;
|
||||
border: 1rpx solid #ddd;
|
||||
border-radius: 8rpx;
|
||||
|
|
@ -1744,12 +1767,18 @@
|
|||
box-sizing: border-box;
|
||||
color: #1A1A1A;
|
||||
}
|
||||
|
||||
.form-select{
|
||||
width: 364rpx !important;
|
||||
flex: none;
|
||||
}
|
||||
.form-avatar-container {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.suiji{
|
||||
width: 50rpx;
|
||||
height: 50rpx;
|
||||
}
|
||||
.form-avatar {
|
||||
width: 80px;
|
||||
height: 80px;
|
||||
|
|
@ -1794,7 +1823,9 @@
|
|||
font-weight: bold;
|
||||
background: linear-gradient(180deg, #A1A8B8 0%, #878B94 100%);
|
||||
}
|
||||
|
||||
.text-avatar-18{
|
||||
font-size: 36rpx !important;
|
||||
}
|
||||
.form-input-time {
|
||||
line-height: 40px;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,930 @@
|
|||
<template>
|
||||
<view class="page-container flex-column" :class="`${phone}-style`">
|
||||
<view class="status-placeholder w100" :style="{ height: `${data.statusBarHeight}px` }"></view>
|
||||
<!-- <view class="top-placeholder" :style="{ height: data.topBoxHeight + 'px' }"></view> -->
|
||||
<view class="top-box " :style="{ top: data.statusBarHeight + 'px' }">
|
||||
<slot name="top">
|
||||
<view class="top-container" v-show="!sortMode">
|
||||
<view class="h100 flex-align-center flex-justify-between">
|
||||
<view class="left flex-align-center">
|
||||
<image @click="util.goBack()" :src="`/static/image/phone-message/${phone}/back.png`">
|
||||
</image>
|
||||
<view v-if="phone == 'iphone'" class="number-box">256</view>
|
||||
</view>
|
||||
<view class="center">
|
||||
<image v-if="phone == 'iphone' || phone == 'huawei'" class="img shrink-0"
|
||||
:src="chatInfo.img || `/static/image/phone-message/${phone}/default.png`">
|
||||
</image>
|
||||
<text v-if="phone != 'iphone'" class="title">{{ chatInfo.title }}</text>
|
||||
<text v-if="phone == 'oppo'" class="second-text">重庆</text>
|
||||
</view>
|
||||
<view class="right flex-align-center">
|
||||
<image v-if="phone == 'mi'" class="img shrink-0"
|
||||
src="/static/image/phone-message/mi/nav-right-icon.png">
|
||||
</image>
|
||||
<image v-if="phone == 'oppo'" class="img shrink-0"
|
||||
src="/static/image/phone-message/oppo/nav-right-icon.png">
|
||||
</image>
|
||||
<image v-if="phone == 'huawei'" class="img shrink-0"
|
||||
src="/static/image/phone-message/huawei/chat-more.png">
|
||||
</image>
|
||||
<image v-if="phone == 'vivo'" class="img shrink-0"
|
||||
src="/static/image/phone-message/vivo/select.png">
|
||||
</image>
|
||||
<image v-if="phone == 'vivo'" class="img shrink-0 m-l-50"
|
||||
src="/static/image/phone-message/vivo/more.png">
|
||||
</image>
|
||||
</view>
|
||||
</view>
|
||||
<view v-if="phone == 'iphone'" class="text-box flex-align-center flex-justify-center">
|
||||
<text class="title">{{ chatInfo.title }}</text>
|
||||
<uni-icons type="right" size="10" color="#D8D8D8"></uni-icons>
|
||||
</view>
|
||||
</view>
|
||||
</slot>
|
||||
</view>
|
||||
<view class="center-box flex-1">
|
||||
<scroll-view :scroll-top="scrollTop" scroll-y="true" scroll-with-animation :show-scrollbar="false"
|
||||
class="scroll-view h100">
|
||||
<slot></slot>
|
||||
</scroll-view>
|
||||
</view>
|
||||
<view class="bottom-box fixed-bottom-box" :style="{ bottom: data.keyboardHeight + 'px' }">
|
||||
<slot name="bottom">
|
||||
<view class="bottom-container flex-align-center" v-show="!sortMode">
|
||||
<image v-if="phone != 'huawei' && phone != 'vivo'" class="add-img shrink-0"
|
||||
:src="`/static/image/phone-message/${phone}/chat-left.png`"></image>
|
||||
<image v-if="phone == 'huawei'" class="add-img shrink-0"
|
||||
:src="`/static/image/phone-message/huawei/emoji.png`"></image>
|
||||
<view class="search-box flex-1 flex-align-center">
|
||||
<image v-if="phone == 'huawei'" class="left-icon ka" @click="changeSim"
|
||||
:src="`/static/image/phone-message/huawei/ka${simIndex}.png`"></image>
|
||||
<image v-if="phone == 'huawei'" class="left-icon down"
|
||||
src="/static/image/phone-message/huawei/down.png"></image>
|
||||
<!-- <input class="input flex-1" :placeholder="showInfo.placeholder" v-model="content"
|
||||
@input="onInput"></input> -->
|
||||
<textarea class="input flex-1" :adjust-position="false" fixed auto-height
|
||||
:placeholder="showInfo.placeholder" v-model="content" @input="onInput" @focus="onFocus"
|
||||
@blur="onBlur"></textarea>
|
||||
<!-- <editor class="input flex-1" :placeholder="showInfo.placeholder"></editor> -->
|
||||
<image v-if="phone == 'iphone' && !isSend" class="right-icon"
|
||||
src="/static/image/phone-message/iphone/mic.png"></image>
|
||||
<image v-if="phone == 'iphone' && isSend" class="right-send-icon"
|
||||
src="/static/image/phone-message/iphone/send.png" @click="sendMessage"></image>
|
||||
<image v-if="phone == 'oppo'" class="right-icon"
|
||||
:src="`/static/image/phone-message/oppo/ka${simIndex}.png`" @click="changeSim">
|
||||
</image>
|
||||
</view>
|
||||
<view v-if="phone != 'iphone'" class="flex-align-center">
|
||||
<image v-if="phone == 'mi'" class="right-icon"
|
||||
:src="`/static/image/phone-message/mi/${isSend ? 'send' : 'unsend'}.png`"
|
||||
@click="sendMessage">
|
||||
</image>
|
||||
<image v-if="phone == 'oppo'" class="right-icon"
|
||||
:src="`/static/image/phone-message/oppo/${isSend ? 'send' : 'unsend'}.png`"
|
||||
@click="sendMessage">
|
||||
</image>
|
||||
<image v-if="phone == 'huawei'" class="right-icon"
|
||||
src="/static/image/phone-message/huawei/chat-add.png">
|
||||
</image>
|
||||
<image v-if="phone == 'huawei'" class="right-icon m-l-34"
|
||||
:src="`/static/image/phone-message/huawei/${isSend ? 'send' : 'unsend'}.png`"
|
||||
@click="sendMessage">
|
||||
</image>
|
||||
<text v-if="phone == 'vivo'" class="send-text" :class="{ 'send-text-active': isSend }"
|
||||
@click="sendMessage">发送</text>
|
||||
</view>
|
||||
</view>
|
||||
</slot>
|
||||
</view>
|
||||
<view class="bottom-placeholder" :style="{ height: (data.bottomBoxHeight + data.keyboardHeight) + 'px' }">
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
<script setup>
|
||||
import { ref, reactive, onMounted, computed, toRefs } from 'vue'
|
||||
import { onLoad, onPageScroll } from "@dcloudio/uni-app";
|
||||
import { stringUtil, dateUtil, util } from '@/utils/common.js';
|
||||
const props = defineProps({
|
||||
// 手机品牌
|
||||
phone: {
|
||||
type: String,
|
||||
default: 'iphone'
|
||||
},
|
||||
chatInfo: {
|
||||
type: Object,
|
||||
default: {
|
||||
unRead: false,
|
||||
noNotice: false,
|
||||
img: "",
|
||||
title: "淘宝通知",
|
||||
content: "您关注的商品降价啦,快来抢购吧!",
|
||||
time: "2026-02-20 20:55:12"
|
||||
}
|
||||
},
|
||||
// 是否处于拖拽排序模式
|
||||
sortMode: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
}
|
||||
})
|
||||
|
||||
const data = reactive({
|
||||
statusBarHeight: 0,
|
||||
topBoxHeight: 0,
|
||||
bottomBoxHeight: 0,
|
||||
isSend: false,
|
||||
content: "",
|
||||
simIndex: 1,
|
||||
scrollTop: 0,
|
||||
keyboardHeight: 0
|
||||
})
|
||||
|
||||
let { isSend, content, simIndex, scrollTop } = toRefs(data)
|
||||
|
||||
const emit = defineEmits(['send'])
|
||||
|
||||
onMounted(() => {
|
||||
// 延迟极小时间确保 DOM 已更新循环并将滚动条拉到极限底端
|
||||
setTimeout(() => {
|
||||
data.scrollTop = 99999 + Math.random();
|
||||
}, 100);
|
||||
})
|
||||
|
||||
const onInput = (e) => {
|
||||
content.value = e.detail.value
|
||||
if (content.value.length > 0) {
|
||||
isSend.value = true
|
||||
} else {
|
||||
isSend.value = false
|
||||
}
|
||||
setTimeout(() => {
|
||||
uni.createSelectorQuery().select('.bottom-box').boundingClientRect(rect => {
|
||||
if (rect) {
|
||||
data.bottomBoxHeight = rect.height;
|
||||
}
|
||||
}).exec();
|
||||
}, 50);
|
||||
}
|
||||
|
||||
const onFocus = (e) => {
|
||||
if (e.detail.height) {
|
||||
data.keyboardHeight = e.detail.height;
|
||||
// 键盘弹起时,也需要滚动到底部
|
||||
setTimeout(() => {
|
||||
data.scrollTop = 99999 + Math.random();
|
||||
}, 100);
|
||||
}
|
||||
}
|
||||
|
||||
const onBlur = () => {
|
||||
data.keyboardHeight = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* 切换sim卡
|
||||
*/
|
||||
const changeSim = () => {
|
||||
simIndex.value = simIndex.value == 1 ? 2 : 1
|
||||
}
|
||||
|
||||
/**
|
||||
* 发送消息
|
||||
*/
|
||||
const sendMessage = () => {
|
||||
if (!isSend.value) return
|
||||
const date = dateUtil.now("YYYY-MM-DD HH:mm")
|
||||
const params = {
|
||||
content: `<p>${content.value}</p>`,
|
||||
simIndex: simIndex.value,
|
||||
time: date,
|
||||
isMe: true
|
||||
}
|
||||
emit('send', params)
|
||||
data.content = ""
|
||||
data.isSend = false
|
||||
|
||||
// 延迟极小时间确保 DOM 已更新循环并将滚动条拉到极限底端
|
||||
setTimeout(() => {
|
||||
data.scrollTop = 99999 + Math.random();
|
||||
uni.createSelectorQuery().select('.bottom-box').boundingClientRect(rect => {
|
||||
if (rect) {
|
||||
data.bottomBoxHeight = rect.height;
|
||||
}
|
||||
}).exec();
|
||||
}, 100);
|
||||
}
|
||||
|
||||
// 展示文字信息
|
||||
const showInfo = computed(() => {
|
||||
let placeholder
|
||||
switch (props.phone) {
|
||||
case "iphone":
|
||||
placeholder = '信息 · 短信'
|
||||
break;
|
||||
case "mi":
|
||||
placeholder = '短信'
|
||||
break;
|
||||
case "oppo":
|
||||
placeholder = '5G 消息'
|
||||
break;
|
||||
case "huawei":
|
||||
placeholder = '短信/彩信'
|
||||
break;
|
||||
case "vivo":
|
||||
placeholder = '输入内容'
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return { placeholder }
|
||||
})
|
||||
|
||||
onMounted(() => {
|
||||
// 同步获取系统信息
|
||||
const systemInfo = uni.getSystemInfoSync();
|
||||
data.statusBarHeight = systemInfo.statusBarHeight || 0;
|
||||
|
||||
// 动态获取 top-box 和 bottom-box 高度
|
||||
setTimeout(() => {
|
||||
uni.createSelectorQuery().select('.top-box').boundingClientRect(rect => {
|
||||
if (rect) {
|
||||
data.topBoxHeight = rect.height;
|
||||
}
|
||||
}).exec();
|
||||
uni.createSelectorQuery().select('.bottom-box').boundingClientRect(rect => {
|
||||
if (rect) {
|
||||
data.bottomBoxHeight = rect.height;
|
||||
}
|
||||
}).exec();
|
||||
}, 50);
|
||||
|
||||
})
|
||||
|
||||
|
||||
|
||||
</script>
|
||||
<style>
|
||||
@import '@/common/main.css';
|
||||
</style>
|
||||
<style lang="less" scoped>
|
||||
.page-container {
|
||||
width: 100vw;
|
||||
height: 100vh;
|
||||
background-color: #fff;
|
||||
}
|
||||
|
||||
.m-l-34 {
|
||||
margin-left: 34rpx;
|
||||
}
|
||||
|
||||
.m-l-50 {
|
||||
margin-left: 50rpx;
|
||||
}
|
||||
|
||||
.center-box {
|
||||
overflow: hidden;
|
||||
// overflow-y: scroll;
|
||||
padding-bottom: 12px;
|
||||
}
|
||||
|
||||
.fixed-bottom-box {
|
||||
position: fixed;
|
||||
width: 100%;
|
||||
background: #fff;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
}
|
||||
|
||||
.fixed-top-box {
|
||||
position: fixed;
|
||||
width: 100%;
|
||||
z-index: 9;
|
||||
left: 0;
|
||||
top: 0;
|
||||
}
|
||||
|
||||
//苹果样式
|
||||
.iphone-style {
|
||||
|
||||
.status-placeholder {
|
||||
background-color: #F7F7F7;
|
||||
}
|
||||
|
||||
.top-box {
|
||||
|
||||
.top-container {
|
||||
padding: 32rpx 24rpx 20rpx;
|
||||
background-color: #F7F7F7;
|
||||
box-shadow: inset 0 -0.3px 0 0 #B5B5B5;
|
||||
|
||||
.left {
|
||||
width: 180rpx;
|
||||
|
||||
image {
|
||||
width: 40rpx;
|
||||
height: 40rpx;
|
||||
}
|
||||
|
||||
.number-box {
|
||||
height: 36rpx;
|
||||
font-size: 24rpx;
|
||||
line-height: 24rpx;
|
||||
padding: 6rpx 10rpx;
|
||||
border-radius: 18rpx;
|
||||
color: #fff;
|
||||
background-color: #0276FF;
|
||||
margin-left: 4rpx;
|
||||
}
|
||||
}
|
||||
|
||||
.center {
|
||||
height: 96rpx;
|
||||
|
||||
.img {
|
||||
width: 96rpx;
|
||||
height: 96rpx;
|
||||
}
|
||||
}
|
||||
|
||||
.right {
|
||||
width: 180rpx;
|
||||
|
||||
}
|
||||
|
||||
.text-box {
|
||||
margin-top: 10rpx;
|
||||
height: 20rpx;
|
||||
|
||||
.title {
|
||||
font-size: 20rpx;
|
||||
color: #1A1A1A;
|
||||
left: 20rpx;
|
||||
margin-right: 6rpx;
|
||||
font-weight: 500;
|
||||
line-height: 20rpx;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
.bottom-container {
|
||||
padding: 10rpx 28rpx;
|
||||
|
||||
.add-img {
|
||||
width: 66rpx;
|
||||
height: 66rpx;
|
||||
margin-right: 24rpx;
|
||||
}
|
||||
|
||||
.search-box {
|
||||
min-height: 70rpx;
|
||||
border-radius: 35rpx 35rpx 35rpx 35rpx;
|
||||
border: 2rpx solid #DFDFDF;
|
||||
padding: 8rpx 26rpx;
|
||||
|
||||
::v-deep .ql-container {
|
||||
// height: 100%;
|
||||
min-height: 48rpx !important;
|
||||
font-size: 32rpx;
|
||||
}
|
||||
|
||||
::v-deep .ql-blank::before {
|
||||
color: #C3C3C3;
|
||||
font-size: 32rpx;
|
||||
line-height: inherit;
|
||||
font-style: normal;
|
||||
}
|
||||
|
||||
.input {
|
||||
height: 100%;
|
||||
width: auto;
|
||||
min-height: 46rpx;
|
||||
max-height: 160rpx;
|
||||
overflow: hidden;
|
||||
overflow-y: scroll;
|
||||
// min-height: 48rpx !important;
|
||||
}
|
||||
|
||||
.right-icon {
|
||||
width: 32rpx;
|
||||
height: 32rpx;
|
||||
margin-left: 20rpx;
|
||||
}
|
||||
|
||||
.right-send-icon {
|
||||
width: 52rpx;
|
||||
height: 52rpx;
|
||||
margin-left: 20rpx;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
//小米样式
|
||||
.mi-style {
|
||||
background-color: #F7F7F7 !important;
|
||||
|
||||
.top-box {
|
||||
|
||||
.top-container {
|
||||
padding: 0 52rpx;
|
||||
background-color: #F7F7F7;
|
||||
height: 88rpx;
|
||||
|
||||
.left {
|
||||
width: 80rpx;
|
||||
|
||||
image {
|
||||
width: 48rpx;
|
||||
height: 48rpx;
|
||||
}
|
||||
|
||||
.number-box {
|
||||
height: 36rpx;
|
||||
font-size: 24rpx;
|
||||
line-height: 24rpx;
|
||||
padding: 6rpx 10rpx;
|
||||
border-radius: 18rpx;
|
||||
color: #fff;
|
||||
background-color: #0276FF;
|
||||
margin-left: 4rpx;
|
||||
}
|
||||
}
|
||||
|
||||
.center {
|
||||
.title {
|
||||
font-size: 36rpx;
|
||||
color: #1A1A1A;
|
||||
font-weight: 500;
|
||||
}
|
||||
}
|
||||
|
||||
.right {
|
||||
width: 80rpx;
|
||||
text-align: right;
|
||||
|
||||
.img {
|
||||
width: 48rpx;
|
||||
height: 48rpx;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
.text-box {
|
||||
.title {
|
||||
font-size: 20rpx;
|
||||
color: #1A1A1A;
|
||||
left: 20rpx;
|
||||
margin-right: 6rpx;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
.bottom-box {
|
||||
.bottom-container {
|
||||
padding: 20rpx 28rpx;
|
||||
|
||||
.add-img {
|
||||
width: 72rpx;
|
||||
height: 72rpx;
|
||||
}
|
||||
|
||||
.search-box {
|
||||
min-height: 96rpx;
|
||||
border-radius: 28rpx;
|
||||
padding: 12rpx 42rpx;
|
||||
margin: 0 20rpx;
|
||||
background-color: #E8E8E8;
|
||||
|
||||
::v-deep .ql-container {
|
||||
height: 100%;
|
||||
min-height: auto;
|
||||
}
|
||||
|
||||
::v-deep .ql-blank::before {
|
||||
color: #8B8B8B;
|
||||
font-size: 30rpx;
|
||||
line-height: inherit;
|
||||
font-style: normal;
|
||||
}
|
||||
|
||||
.input {
|
||||
height: 100%;
|
||||
width: auto;
|
||||
min-height: 46rpx;
|
||||
max-height: 160rpx;
|
||||
overflow: hidden;
|
||||
overflow-y: scroll;
|
||||
}
|
||||
|
||||
.right-icon {
|
||||
width: 32rpx;
|
||||
height: 32rpx;
|
||||
margin-left: 20rpx;
|
||||
}
|
||||
}
|
||||
|
||||
.right-icon {
|
||||
width: 72rpx;
|
||||
height: 72rpx;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
//oppo样式
|
||||
.oppo-style {
|
||||
background-color: #F0F1F3 !important;
|
||||
|
||||
.top-box {
|
||||
|
||||
.top-container {
|
||||
padding: 0 52rpx;
|
||||
background-color: #F0F1F3;
|
||||
height: 88rpx;
|
||||
box-shadow: inset 0 -0.3px 0 0 #D0D1D3;
|
||||
|
||||
.left {
|
||||
|
||||
image {
|
||||
width: 48rpx;
|
||||
height: 48rpx;
|
||||
}
|
||||
|
||||
.number-box {
|
||||
height: 36rpx;
|
||||
font-size: 24rpx;
|
||||
line-height: 24rpx;
|
||||
padding: 6rpx 10rpx;
|
||||
border-radius: 18rpx;
|
||||
color: #fff;
|
||||
background-color: #0276FF;
|
||||
margin-left: 4rpx;
|
||||
}
|
||||
}
|
||||
|
||||
.center {
|
||||
flex: 1;
|
||||
height: 100%;
|
||||
margin: 0 18rpx;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
|
||||
.title {
|
||||
font-size: 36rpx;
|
||||
color: #1A1A1A;
|
||||
font-weight: 500;
|
||||
line-height: 36rpx;
|
||||
}
|
||||
|
||||
.second-text {
|
||||
color: #6F6F6F;
|
||||
font-size: 28rpx;
|
||||
line-height: 28rpx;
|
||||
margin-top: 14rpx;
|
||||
}
|
||||
}
|
||||
|
||||
.right {
|
||||
text-align: right;
|
||||
|
||||
.img {
|
||||
width: 48rpx;
|
||||
height: 48rpx;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
.text-box {
|
||||
.title {
|
||||
font-size: 20rpx;
|
||||
color: #1A1A1A;
|
||||
left: 20rpx;
|
||||
margin-right: 6rpx;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
.bottom-box {
|
||||
background-color: #FAFAFA;
|
||||
|
||||
.bottom-container {
|
||||
padding: 16rpx 32rpx 50rpx;
|
||||
|
||||
.add-img {
|
||||
width: 84rpx;
|
||||
height: 84rpx;
|
||||
}
|
||||
|
||||
.search-box {
|
||||
min-height: 84rpx;
|
||||
border-radius: 42rpx;
|
||||
margin: 0 16rpx;
|
||||
background-color: #E6E6E6;
|
||||
padding: 12rpx 38rpx;
|
||||
|
||||
::v-deep .ql-container {
|
||||
height: 100%;
|
||||
min-height: auto;
|
||||
}
|
||||
|
||||
::v-deep .ql-blank::before {
|
||||
color: #6C6C6E;
|
||||
font-size: 32rpx;
|
||||
line-height: inherit;
|
||||
font-style: normal;
|
||||
}
|
||||
|
||||
.input {
|
||||
height: 100%;
|
||||
width: auto;
|
||||
min-height: 46rpx;
|
||||
max-height: 160rpx;
|
||||
overflow: hidden;
|
||||
overflow-y: scroll;
|
||||
}
|
||||
|
||||
.right-icon {
|
||||
width: 32rpx;
|
||||
height: 40rpx;
|
||||
margin-right: 14rpx;
|
||||
}
|
||||
}
|
||||
|
||||
.right-icon {
|
||||
width: 84rpx;
|
||||
height: 84rpx;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
//华为样式
|
||||
.huawei-style {
|
||||
background-color: #F1F5F8 !important;
|
||||
|
||||
.top-box {
|
||||
|
||||
.top-container {
|
||||
padding: 0 32rpx;
|
||||
background-color: #F1F5F8;
|
||||
height: 88rpx;
|
||||
|
||||
.left {
|
||||
|
||||
image {
|
||||
width: 76rpx;
|
||||
height: 76rpx;
|
||||
}
|
||||
|
||||
.number-box {
|
||||
height: 36rpx;
|
||||
font-size: 24rpx;
|
||||
line-height: 24rpx;
|
||||
padding: 6rpx 10rpx;
|
||||
border-radius: 18rpx;
|
||||
color: #fff;
|
||||
background-color: #0276FF;
|
||||
margin-left: 4rpx;
|
||||
}
|
||||
}
|
||||
|
||||
.center {
|
||||
flex: 1;
|
||||
height: 100%;
|
||||
margin: 0 16rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
.img {
|
||||
width: 72rpx;
|
||||
height: 72rpx;
|
||||
flex-shrink: 0;
|
||||
margin-right: 18rpx;
|
||||
}
|
||||
|
||||
.title {
|
||||
font-size: 40rpx;
|
||||
color: #1A1A1A;
|
||||
font-weight: 500;
|
||||
line-height: 36rpx;
|
||||
}
|
||||
|
||||
.second-text {
|
||||
color: #6F6F6F;
|
||||
font-size: 28rpx;
|
||||
line-height: 28rpx;
|
||||
margin-top: 14rpx;
|
||||
}
|
||||
}
|
||||
|
||||
.right {
|
||||
text-align: right;
|
||||
|
||||
.img {
|
||||
width: 72rpx;
|
||||
height: 72rpx;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
.text-box {
|
||||
.title {
|
||||
font-size: 20rpx;
|
||||
color: #1A1A1A;
|
||||
left: 20rpx;
|
||||
margin-right: 6rpx;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
.bottom-box {
|
||||
background-color: #FFFFFF;
|
||||
|
||||
.bottom-container {
|
||||
padding: 12rpx 32rpx;
|
||||
|
||||
.add-img {
|
||||
width: 44rpx;
|
||||
height: 44rpx;
|
||||
}
|
||||
|
||||
.search-box {
|
||||
min-height: 76rpx;
|
||||
border-radius: 42rpx;
|
||||
padding: 10rpx 28rpx;
|
||||
margin: 0 30rpx;
|
||||
background-color: #F4F4F4;
|
||||
|
||||
.ka {
|
||||
width: 44rpx;
|
||||
height: 44rpx;
|
||||
}
|
||||
|
||||
.down {
|
||||
width: 24rpx;
|
||||
height: 24rpx;
|
||||
margin-left: 10rpx;
|
||||
}
|
||||
|
||||
.input {
|
||||
height: 100%;
|
||||
width: auto;
|
||||
min-height: 46rpx;
|
||||
max-height: 160rpx;
|
||||
overflow: hidden;
|
||||
overflow-y: scroll;
|
||||
margin-left: 32rpx;
|
||||
|
||||
::v-deep .ql-container {
|
||||
height: 100%;
|
||||
min-height: auto;
|
||||
}
|
||||
|
||||
::v-deep .ql-blank::before {
|
||||
color: #606060;
|
||||
font-size: 30rpx;
|
||||
line-height: inherit;
|
||||
font-style: normal;
|
||||
}
|
||||
}
|
||||
|
||||
.right-icon {
|
||||
width: 32rpx;
|
||||
height: 40rpx;
|
||||
margin-right: 14rpx;
|
||||
}
|
||||
}
|
||||
|
||||
.right-icon {
|
||||
width: 44rpx;
|
||||
height: 44rpx;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
//vivo样式
|
||||
.vivo-style {
|
||||
background-color: #F6F6F6 !important;
|
||||
|
||||
.top-box {
|
||||
|
||||
.top-container {
|
||||
padding: 66rpx 52rpx 20rpx 42rpx;
|
||||
background-color: #F6F6F6;
|
||||
|
||||
.left {
|
||||
|
||||
image {
|
||||
width: 40rpx;
|
||||
height: 40rpx;
|
||||
}
|
||||
}
|
||||
|
||||
.center {
|
||||
flex: 1;
|
||||
height: 100%;
|
||||
margin: 0 18rpx;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
|
||||
.title {
|
||||
font-size: 34rpx;
|
||||
color: #1A1A1A;
|
||||
font-weight: 500;
|
||||
line-height: 34rpx;
|
||||
}
|
||||
}
|
||||
|
||||
.right {
|
||||
text-align: right;
|
||||
|
||||
.img {
|
||||
width: 44rpx;
|
||||
height: 44rpx;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
.text-box {
|
||||
.title {
|
||||
font-size: 20rpx;
|
||||
color: #1A1A1A;
|
||||
left: 20rpx;
|
||||
margin-right: 6rpx;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
.bottom-box {
|
||||
background-color: #fff;
|
||||
|
||||
.bottom-container {
|
||||
padding: 20rpx 48rpx;
|
||||
|
||||
.search-box {
|
||||
min-height: 64rpx;
|
||||
border-radius: 16rpx;
|
||||
padding: 8rpx 18rpx;
|
||||
margin-right: 42rpx;
|
||||
background-color: #F2F2F2;
|
||||
|
||||
.input {
|
||||
height: 100%;
|
||||
width: auto;
|
||||
min-height: 46rpx;
|
||||
max-height: 160rpx;
|
||||
overflow: hidden;
|
||||
overflow-y: scroll;
|
||||
|
||||
::v-deep .ql-container {
|
||||
height: 100%;
|
||||
min-height: auto;
|
||||
}
|
||||
|
||||
::v-deep .ql-blank::before {
|
||||
color: #C8C8C8;
|
||||
font-size: 30rpx;
|
||||
line-height: inherit;
|
||||
font-style: normal;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.send-text {
|
||||
|
||||
color: #9CC9FF;
|
||||
font-size: 34rpx;
|
||||
}
|
||||
|
||||
.send-text-active {
|
||||
color: #0078FE;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
</style>
|
||||
|
|
@ -0,0 +1,857 @@
|
|||
<template>
|
||||
<view>
|
||||
<view v-for="(message, index) in displayList" :key="message.id || index" class="message-item" :class="{
|
||||
isMe: message.isMe,
|
||||
'm-t-16': shouldApplyMt16(index) && !shouldShowTime(index),
|
||||
'is-sort-mode': sortMode,
|
||||
'sort-dragging': sortMode && dragIndex === index,
|
||||
'sort-drop-before': sortMode && dragOverIndex === index && dragIndex !== index && dropPosition === 'before',
|
||||
'sort-drop-after': sortMode && dragOverIndex === index && dragIndex !== index && dropPosition === 'after'
|
||||
}">
|
||||
|
||||
<!-- 排序模式下的拖拽手柄 -->
|
||||
<view v-if="sortMode" class="sort-handle-wrap" @longpress="onSortLongPress(index, $event)"
|
||||
@touchmove.stop.prevent="onSortTouchMove(index, $event)" @touchend.stop="onSortTouchEnd(index, $event)">
|
||||
<view class="sort-handle-bar"></view>
|
||||
<view class="sort-handle-bar"></view>
|
||||
<view class="sort-handle-bar"></view>
|
||||
</view>
|
||||
|
||||
<view style="flex: 1; overflow: hidden;">
|
||||
<view class="time m-t-44" v-if="shouldShowTime(index)">
|
||||
<view class="top-text" v-if="phone == 'iphone' && index == 0">信息 · 短信</view>
|
||||
<view class="top-text" v-if="phone == 'huawei' && index == 0">短信/彩信</view>
|
||||
<text v-if="phone == 'huawei'">{{ formatHuaweiTopTime(message.time) }}</text>
|
||||
<text v-else>{{ formatChatTime(message.time) }} <text
|
||||
v-if="(phone == 'oppo' || (phone == 'vivo' && message.isMe)) && message.simIndex"> 中国联通
|
||||
</text></text>
|
||||
|
||||
<image style="width: 20rpx;height: 24rpx;margin-left: 8rpx; "
|
||||
v-if="phone == 'oppo' || (phone == 'vivo' && message.isMe)"
|
||||
:src="`/static/image/phone-message/huawei/chat-ka${message.simIndex}.png`">
|
||||
</image>
|
||||
</view>
|
||||
<view class="chat-box" :id="'msg-' + index" :class="{
|
||||
'tail-right': shouldApplyTailRight(index),
|
||||
'tail-left': shouldApplyTailLeft(index),
|
||||
'delivered': isLastMeMessage(index)
|
||||
}" @longpress="!sortMode && onMessageLongPress(index, message)">
|
||||
<text v-if="message.isMe && phone == 'mi'" class="send-text">送达</text>
|
||||
<view class="chat-bubble">
|
||||
<view v-html="formatMessageContent(message.content, message.isMe)"></view>
|
||||
</view>
|
||||
</view>
|
||||
<view v-if="phone == 'huawei'" class="second-info">
|
||||
<text>{{ formatHuaweiBottomTime(message.time) }}</text>
|
||||
<image :src="`/static/image/phone-message/huawei/chat-ka${message.simIndex}.png`"></image>
|
||||
</view>
|
||||
<view v-if="(phone == 'oppo' || phone == 'vivo') && message.isMe" class="second-info">
|
||||
<text v-if="message.isMe" class="delivered">已送达</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, reactive, onMounted, computed, watch, nextTick } from 'vue'
|
||||
import { onLoad, onPageScroll } from "@dcloudio/uni-app";
|
||||
import { stringUtil, util } from '@/utils/common.js';
|
||||
const props = defineProps({
|
||||
// 手机品牌
|
||||
phone: {
|
||||
type: String,
|
||||
default: 'iphone'
|
||||
},
|
||||
messageList: {
|
||||
type: Array,
|
||||
default: []
|
||||
},
|
||||
// 是否处于拖拽排序模式
|
||||
sortMode: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
}
|
||||
})
|
||||
|
||||
const emit = defineEmits(['onMessageLongPress', 'sort'])
|
||||
|
||||
// 排序模式下的本地唦本列表
|
||||
// 不直接修改 props.messageList,排序完成后 emit 给父组件
|
||||
const localSortList = ref([])
|
||||
const dragIndex = ref(-1)
|
||||
const dragOverIndex = ref(-1)
|
||||
const dropPosition = ref('after') // 'before'=插入目标上方, 'after'=插入目标下方
|
||||
let isDragging = false
|
||||
let sortItemRects = []
|
||||
|
||||
// 当 sortMode 切换时同步本地副本
|
||||
watch(() => props.sortMode, (val) => {
|
||||
if (val) {
|
||||
localSortList.value = props.messageList.map(item => ({ ...item }))
|
||||
} else {
|
||||
dragIndex.value = -1
|
||||
dragOverIndex.value = -1
|
||||
isDragging = false
|
||||
sortItemRects = []
|
||||
localSortList.value = []
|
||||
}
|
||||
})
|
||||
|
||||
// 实际渲染用的列表:排序模式下用内部副本,否则用原始列表
|
||||
const displayList = computed(() => {
|
||||
if (props.sortMode) return localSortList.value
|
||||
return props.messageList
|
||||
})
|
||||
|
||||
// 判断消息isMe==true时上一条消息是否也是isMe==true,如果不是则应用m-t-16样式
|
||||
const shouldApplyMt16 = (index) => {
|
||||
if (index === 0) return false;
|
||||
const currentMsg = displayList.value[index];
|
||||
const prevMsg = displayList.value[index - 1];
|
||||
if (currentMsg.isMe && !prevMsg.isMe) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// 判断消息isMe==true时下一条消息是否也是isMe==true,如果不是则应用m-t-16样式
|
||||
const shouldApplyNextIsMe = (index) => {
|
||||
const currentMsg = displayList.value[index];
|
||||
// 拦截:如果已经是数组的最后一条消息,必然不存在下一条消息
|
||||
if (index >= displayList.value.length - 1) return false;
|
||||
|
||||
const nextMsg = displayList.value[index + 1];
|
||||
if (currentMsg.isMe && nextMsg.isMe) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// 判断是否为最后一条自己发出的消息
|
||||
const isLastMeMessage = (currentIndex) => {
|
||||
const currentMsg = displayList.value[currentIndex];
|
||||
if (!currentMsg.isMe) return false;
|
||||
|
||||
// 往后遍历,如果还能找到isMe=true的消息,说明当前这条不是最后一条
|
||||
for (let i = currentIndex + 1; i < displayList.value.length; i++) {
|
||||
if (displayList.value[i].isMe) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
// 判断 tail-right(右侧我方气泡尾巴)样式是否生效
|
||||
const shouldApplyTailRight = (index) => {
|
||||
const currentMsg = displayList.value[index];
|
||||
if (!currentMsg.isMe) return false; // 不是我发的直接没右侧尾巴
|
||||
|
||||
// 如果这是整体列表最后一条消息,必然有尾巴
|
||||
if (index === displayList.value.length - 1) return true;
|
||||
|
||||
const nextMsg = displayList.value[index + 1];
|
||||
|
||||
// 条件 c: 下一条消息 isMe == false (也就是被打断了)
|
||||
if (!nextMsg.isMe) return true;
|
||||
|
||||
// 条件 a: 下一条消息间隔三分钟以上 (180000 毫秒) - 时间分割线导致当前段落结束
|
||||
const currentMsgTime = new Date(currentMsg.time.replace(/-/g, '/')).getTime();
|
||||
const nextMsgTime = new Date(nextMsg.time.replace(/-/g, '/')).getTime();
|
||||
if (!isNaN(currentMsgTime) && !isNaN(nextMsgTime) && (nextMsgTime - currentMsgTime > 180000)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// 其他情况(连绵不断的连发,中间没超时间也没被对方打断),则隐藏中间环节气泡尾巴
|
||||
return false;
|
||||
}
|
||||
|
||||
// 判断 tail-left 样式是否生效
|
||||
const shouldApplyTailLeft = (index) => {
|
||||
const currentMsg = displayList.value[index];
|
||||
if (currentMsg.isMe) return false;
|
||||
|
||||
// 如果这是整体列表最后一条消息,也就符合最后一条isMe==false的特征
|
||||
if (index === displayList.value.length - 1) return true;
|
||||
|
||||
const nextMsg = displayList.value[index + 1];
|
||||
|
||||
// 条件 c: 下一条消息 isMe == true
|
||||
if (nextMsg.isMe) return true;
|
||||
|
||||
// 条件 a: 下一条消息间隔三分钟以上 (180000 毫秒)
|
||||
const currentMsgTime = new Date(currentMsg.time.replace(/-/g, '/')).getTime();
|
||||
const nextMsgTime = new Date(nextMsg.time.replace(/-/g, '/')).getTime();
|
||||
if (!isNaN(currentMsgTime) && !isNaN(nextMsgTime) && (nextMsgTime - currentMsgTime > 180000)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// 条件 b: 这是最后一条 isMe == false 的消息
|
||||
for (let i = index + 1; i < displayList.value.length; i++) {
|
||||
if (!displayList.value[i].isMe) {
|
||||
return false; // 往后还有不是自己发的消息,所以当前并非最后一条 isMe==false
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// 获取格式化后的聊天时间显示
|
||||
const formatChatTime = (timeStr) => {
|
||||
if (!timeStr) return '';
|
||||
const date = new Date(timeStr.replace(/-/g, '/'));
|
||||
if (isNaN(date.getTime())) return timeStr;
|
||||
|
||||
const now = new Date();
|
||||
const today = new Date(now.getFullYear(), now.getMonth(), now.getDate());
|
||||
const target = new Date(date.getFullYear(), date.getMonth(), date.getDate());
|
||||
|
||||
const diffDays = Math.floor((today.getTime() - target.getTime()) / (1000 * 60 * 60 * 24));
|
||||
|
||||
const hours = String(date.getHours()).padStart(2, '0');
|
||||
const minutes = String(date.getMinutes()).padStart(2, '0');
|
||||
const timeNum = `${hours}:${minutes}`;
|
||||
|
||||
if (diffDays === 0) {
|
||||
if (props.phone == 'iphone' || props.phone == 'oppo') return `今天 ${timeNum}`;
|
||||
return `${timeNum}`;
|
||||
} else if (diffDays === 1) {
|
||||
return `昨天 ${timeNum}`;
|
||||
} else if (diffDays > 1 && diffDays < 7) {
|
||||
const weekDays = ['星期日', '星期一', '星期二', '星期三', '星期四', '星期五', '星期六'];
|
||||
return `${weekDays[date.getDay()]} ${timeNum}`;
|
||||
} else {
|
||||
// 超过一周,同一年显示 月日 时分,跨年显示 年月日 时分
|
||||
if (date.getFullYear() === now.getFullYear()) {
|
||||
return `${date.getMonth() + 1}月${date.getDate()}日 ${timeNum}`;
|
||||
} else {
|
||||
return `${date.getFullYear()}年${date.getMonth() + 1}月${date.getDate()}日 ${timeNum}`;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 华为顶部时间栏:只显示日期维度的信息,无具体时分秒
|
||||
const formatHuaweiTopTime = (timeStr) => {
|
||||
if (!timeStr) return '';
|
||||
const date = new Date(timeStr.replace(/-/g, '/'));
|
||||
if (isNaN(date.getTime())) return timeStr;
|
||||
|
||||
const now = new Date();
|
||||
const today = new Date(now.getFullYear(), now.getMonth(), now.getDate());
|
||||
const target = new Date(date.getFullYear(), date.getMonth(), date.getDate());
|
||||
const diffDays = Math.floor((today.getTime() - target.getTime()) / (1000 * 60 * 60 * 24));
|
||||
|
||||
if (diffDays === 0) {
|
||||
return `今天`;
|
||||
} else if (diffDays === 1) {
|
||||
return `昨天`;
|
||||
} else {
|
||||
const weekDays = ['星期日', '星期一', '星期二', '星期三', '星期四', '星期五', '星期六'];
|
||||
const weekdayStr = weekDays[date.getDay()];
|
||||
return `${date.getFullYear()}年${date.getMonth() + 1}月${date.getDate()}日${weekdayStr}`;
|
||||
}
|
||||
}
|
||||
|
||||
// 华为底部小尾巴附加时间:上下午时分、或者“刚刚”
|
||||
const formatHuaweiBottomTime = (timeStr) => {
|
||||
if (!timeStr) return '';
|
||||
const date = new Date(timeStr.replace(/-/g, '/'));
|
||||
if (isNaN(date.getTime())) return timeStr;
|
||||
|
||||
const now = new Date();
|
||||
// 判断是否在一分钟内(差值在 60000 毫秒以内)
|
||||
const diffMs = now.getTime() - date.getTime();
|
||||
if (diffMs >= 0 && diffMs <= 60000) {
|
||||
return '刚刚';
|
||||
}
|
||||
|
||||
const ampm = date.getHours() >= 12 ? '下午' : '上午';
|
||||
// 转换为 12 小时制
|
||||
let displayHour = date.getHours() % 12;
|
||||
displayHour = displayHour ? displayHour : 12; // 如果是 0(午夜) 则是12
|
||||
const minutes = String(date.getMinutes()).padStart(2, '0');
|
||||
|
||||
return `${ampm}${displayHour}:${minutes}`;
|
||||
}
|
||||
|
||||
// 判断是否显示时间
|
||||
// timeMode: 'show'=强制显示, 'hide'=强制隐藏, 'auto'或无=按间隔逻辑判断
|
||||
const shouldShowTime = (index) => {
|
||||
const currentMsg = displayList.value[index];
|
||||
const mode = currentMsg.timeMode;
|
||||
|
||||
// 强制隐藏(兼容旧 hideTime 字段)
|
||||
if (mode === 'hide' || currentMsg.hideTime) return false;
|
||||
|
||||
// 强制显示
|
||||
if (mode === 'show') return true;
|
||||
|
||||
// 以下为 auto / 默认 逻辑
|
||||
if (index === 0) return true; // 第一条必定显示
|
||||
|
||||
// 排序模式下每条均显示时间
|
||||
if (props.sortMode) return true;
|
||||
|
||||
const currentMsgTime = new Date(currentMsg.time.replace(/-/g, '/')).getTime();
|
||||
const prevMsgTime = new Date(displayList.value[index - 1].time.replace(/-/g, '/')).getTime();
|
||||
|
||||
if (isNaN(currentMsgTime) || isNaN(prevMsgTime)) return true;
|
||||
|
||||
// 绝对值比较,兼容排序后时间倒序
|
||||
return Math.abs(currentMsgTime - prevMsgTime) > 180000;
|
||||
}
|
||||
|
||||
// 格式化文本:给网址和 5 位以上连续数字加上下划线样式,非我方消息同时赋予文字蓝色
|
||||
const formatMessageContent = (content, isMe) => {
|
||||
if (!content) return '';
|
||||
|
||||
// 匹配 url 或 5位以上连续数字
|
||||
// URL: http(s)://... 或 xxx.xxx/...
|
||||
const combinedRegex = /((?:https?:\/\/)?(?:[a-zA-Z0-9-]+\.)+[a-zA-Z]{2,}(?:\/[a-zA-Z0-9_/%-]*)*)|(\b\d{5,}(?:\.\d+)?\b)/g;
|
||||
|
||||
return content.replace(combinedRegex, (match, p1, p2) => {
|
||||
// 如果是数字且包含小数点(通常是金额),则不添加下划线样式
|
||||
if (p2 && p2.includes('.')) {
|
||||
return match;
|
||||
}
|
||||
|
||||
let color = '#3A85F8'
|
||||
if (props.phone == 'iphone') color = '#0B7AE3'
|
||||
if (props.phone == 'mi') color = '#3A85F8'
|
||||
const colorStyle = !isMe ? `color: ${color};` : '';
|
||||
return `<span style="text-decoration: underline; ${colorStyle}">${match}</span>`;
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 长按信息
|
||||
* @param index
|
||||
* @param message
|
||||
*/
|
||||
const onMessageLongPress = (index, message) => {
|
||||
emit('onLongPress', index, message)
|
||||
}
|
||||
|
||||
// ===================== 拖拽排序逻辑 =====================
|
||||
|
||||
/**
|
||||
* 刷新所有排序条目的 rect 坐标缓存
|
||||
*/
|
||||
const refreshSortRects = (callback) => {
|
||||
const query = uni.createSelectorQuery()
|
||||
const len = localSortList.value.length
|
||||
let collected = []
|
||||
let done = 0
|
||||
for (let i = 0; i < len; i++) {
|
||||
query.select('#msg-' + i).boundingClientRect(rect => {
|
||||
collected[i] = rect
|
||||
done++
|
||||
if (done === len && callback) callback(collected)
|
||||
})
|
||||
}
|
||||
query.exec()
|
||||
}
|
||||
|
||||
/**
|
||||
* 长按手柄开始拖拽
|
||||
*/
|
||||
const onSortLongPress = (idx, e) => {
|
||||
dragIndex.value = idx
|
||||
dragOverIndex.value = idx
|
||||
isDragging = true
|
||||
refreshSortRects(rects => {
|
||||
sortItemRects = rects
|
||||
})
|
||||
uni.vibrateShort({ type: 'medium' })
|
||||
}
|
||||
|
||||
/**
|
||||
* 拖拽移动 - 根据触摸点 Y 坐标確定悬停位置和插入方向
|
||||
*/
|
||||
const onSortTouchMove = (idx, e) => {
|
||||
if (!isDragging || dragIndex.value === -1) return
|
||||
if (!e.touches || !e.touches[0]) return
|
||||
const touchY = e.touches[0].clientY
|
||||
if (!sortItemRects || sortItemRects.length === 0) return
|
||||
let overIdx = dragOverIndex.value
|
||||
for (let i = 0; i < sortItemRects.length; i++) {
|
||||
const rect = sortItemRects[i]
|
||||
if (rect && touchY >= rect.top && touchY <= rect.bottom) {
|
||||
overIdx = i
|
||||
// 判断手指在该元素的上半区还是下半区
|
||||
const mid = rect.top + rect.height / 2
|
||||
dropPosition.value = touchY < mid ? 'before' : 'after'
|
||||
break
|
||||
}
|
||||
}
|
||||
dragOverIndex.value = overIdx
|
||||
}
|
||||
|
||||
/**
|
||||
* 拖拽结束 - 根据 dropPosition 決定插入到目标的上方还是下方
|
||||
*/
|
||||
const onSortTouchEnd = (idx, e) => {
|
||||
if (!isDragging) return
|
||||
const from = dragIndex.value
|
||||
const to = dragOverIndex.value
|
||||
if (from !== -1 && to !== -1 && from !== to) {
|
||||
const list = [...localSortList.value]
|
||||
const [removed] = list.splice(from, 1)
|
||||
// from 被移除后,如果 to 在 from 后面,to 需要 -1
|
||||
let insertAt = to > from ? to - 1 : to
|
||||
if (dropPosition.value === 'after') insertAt += 1
|
||||
list.splice(insertAt, 0, removed)
|
||||
localSortList.value = list
|
||||
emit('sort', list.map(item => ({ ...item })))
|
||||
nextTick(() => {
|
||||
refreshSortRects(rects => {
|
||||
sortItemRects = rects
|
||||
})
|
||||
})
|
||||
}
|
||||
dragIndex.value = -1
|
||||
dragOverIndex.value = -1
|
||||
dropPosition.value = 'after'
|
||||
isDragging = false
|
||||
}
|
||||
</script>
|
||||
<style lang="less" scoped>
|
||||
/* ===== 排序模式公共样式(跨所有手机品牌通用)===== */
|
||||
/* 排序模式下每条消息左右横排(手柄 + 气泡取剩余宽度)*/
|
||||
.is-sort-mode {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: stretch;
|
||||
}
|
||||
|
||||
/* 拖拽手柄区域 */
|
||||
.sort-handle-wrap {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
width: 60rpx;
|
||||
flex-shrink: 0;
|
||||
padding: 0 8rpx;
|
||||
|
||||
.sort-handle-bar {
|
||||
margin: 3rpx 0;
|
||||
}
|
||||
}
|
||||
|
||||
.sort-handle-bar {
|
||||
width: 30rpx;
|
||||
height: 4rpx;
|
||||
border-radius: 2rpx;
|
||||
background-color: #CCCCCC;
|
||||
}
|
||||
|
||||
/* 被拖拽中的条目:高亮蓝边 + 轻微缩进 */
|
||||
.sort-dragging {
|
||||
opacity: 0.7;
|
||||
background-color: rgba(0, 122, 255, 0.06) !important;
|
||||
border-left: 4rpx solid #007AFF;
|
||||
}
|
||||
|
||||
/* 插入线:将拖拽项放在目标上方 */
|
||||
.sort-drop-before {
|
||||
border-top: 3rpx dashed #007AFF !important;
|
||||
padding-top: 4rpx;
|
||||
}
|
||||
|
||||
/* 插入线:将拖拽项放在目标下方 */
|
||||
.sort-drop-after {
|
||||
border-bottom: 3rpx dashed #007AFF !important;
|
||||
padding-bottom: 4rpx;
|
||||
}
|
||||
|
||||
// 苹果样式
|
||||
.iphone-style {
|
||||
.m-t-16 {
|
||||
margin-top: 16rpx !important;
|
||||
}
|
||||
|
||||
.top-text {
|
||||
text-align: center;
|
||||
font-size: 20rpx;
|
||||
color: #838383;
|
||||
margin-top: 26rpx;
|
||||
}
|
||||
|
||||
.time {
|
||||
color: #838383;
|
||||
font-size: 20rpx;
|
||||
text-align: center;
|
||||
margin-bottom: 20rpx;
|
||||
margin-top: 36rpx;
|
||||
}
|
||||
|
||||
.chat-box {
|
||||
display: flex;
|
||||
justify-content: flex-start;
|
||||
width: 100%;
|
||||
|
||||
.chat-bubble {
|
||||
padding: 18rpx 22rpx;
|
||||
background-color: #E9E9EB;
|
||||
border-radius: 34rpx;
|
||||
max-width: 600rpx;
|
||||
font-size: 32rpx;
|
||||
line-height: 38rpx;
|
||||
color: #1A1A1A;
|
||||
margin: 4rpx 30rpx 0;
|
||||
word-break: break-all;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
.isMe {
|
||||
.chat-box {
|
||||
justify-content: flex-end;
|
||||
}
|
||||
|
||||
.chat-bubble {
|
||||
background-color: #34C85A;
|
||||
color: #fff;
|
||||
}
|
||||
}
|
||||
|
||||
.tail-left {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.tail-left::after {
|
||||
position: absolute;
|
||||
left: 18rpx;
|
||||
bottom: 0;
|
||||
content: '';
|
||||
background: url('/static/image/phone-message/iphone/left-msg-box.png') no-repeat center bottom;
|
||||
background-size: 100% 100%;
|
||||
width: 28rpx;
|
||||
height: 36rpx;
|
||||
}
|
||||
|
||||
.tail-right {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.delivered::before {
|
||||
position: absolute;
|
||||
right: 18rpx;
|
||||
bottom: -12rpx;
|
||||
content: '已送达';
|
||||
font-size: 20rpx;
|
||||
line-height: 20rpx;
|
||||
color: #8B8B8B;
|
||||
transform: translateY(100%);
|
||||
}
|
||||
|
||||
.tail-right::after {
|
||||
position: absolute;
|
||||
right: 18rpx;
|
||||
bottom: 0;
|
||||
content: '';
|
||||
background: url('/static/image/phone-message/iphone/right-msg-box.png') no-repeat center bottom;
|
||||
background-size: 100% 100%;
|
||||
width: 28rpx;
|
||||
height: 36rpx;
|
||||
}
|
||||
}
|
||||
|
||||
// 小米样式
|
||||
.mi-style {
|
||||
.top-text {
|
||||
text-align: center;
|
||||
font-size: 20rpx;
|
||||
color: #838383;
|
||||
margin-top: 26rpx;
|
||||
}
|
||||
|
||||
.time {
|
||||
padding: 0 54rpx;
|
||||
color: #646464;
|
||||
font-size: 24rpx;
|
||||
line-height: 28rpx;
|
||||
margin-bottom: 16rpx;
|
||||
}
|
||||
|
||||
.chat-box {
|
||||
display: flex;
|
||||
justify-content: flex-start;
|
||||
width: 100%;
|
||||
|
||||
.chat-bubble {
|
||||
padding: 22rpx 42rpx;
|
||||
background-color: #FFFFFF;
|
||||
border-radius: 32rpx;
|
||||
font-size: 32rpx;
|
||||
line-height: 50rpx;
|
||||
color: #1A1A1A;
|
||||
max-width: 550rpx;
|
||||
margin: 0 24rpx;
|
||||
word-break: break-all;
|
||||
}
|
||||
}
|
||||
|
||||
.message-item {
|
||||
margin-top: 16rpx;
|
||||
}
|
||||
|
||||
.m-t-44 {
|
||||
margin-top: 44rpx;
|
||||
}
|
||||
|
||||
|
||||
.isMe {
|
||||
|
||||
.time {
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.chat-box {
|
||||
justify-content: flex-end;
|
||||
align-items: center;
|
||||
|
||||
.send-text {
|
||||
color: #646464;
|
||||
font-size: 24rpx;
|
||||
}
|
||||
|
||||
.chat-bubble {
|
||||
margin-left: 12rpx;
|
||||
background-color: #3681FF;
|
||||
color: #FFFFFF;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
// oppo样式
|
||||
.oppo-style {
|
||||
.m-t-16 {
|
||||
margin-top: 24rpx !important;
|
||||
}
|
||||
|
||||
.time {
|
||||
color: #727377;
|
||||
font-size: 24rpx;
|
||||
line-height: 24rpx;
|
||||
text-align: center;
|
||||
margin-bottom: 20rpx;
|
||||
margin-top: 48rpx;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.message-item {
|
||||
margin-top: 12rpx;
|
||||
}
|
||||
|
||||
.chat-box {
|
||||
display: flex;
|
||||
justify-content: flex-start;
|
||||
width: 100%;
|
||||
|
||||
.chat-bubble {
|
||||
padding: 28rpx 48rpx;
|
||||
background-color: #FFFFFF;
|
||||
border-radius: 32rpx;
|
||||
max-width: 600rpx;
|
||||
font-size: 32rpx;
|
||||
line-height: 44rpx;
|
||||
color: #1A1A1A;
|
||||
margin: 0 32rpx;
|
||||
word-break: break-all;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
.isMe {
|
||||
.chat-box {
|
||||
justify-content: flex-end;
|
||||
}
|
||||
|
||||
.chat-bubble {
|
||||
background-color: #00A1FF;
|
||||
color: #fff;
|
||||
}
|
||||
}
|
||||
|
||||
.second-info {
|
||||
font-size: 24rpx;
|
||||
text-align: right;
|
||||
padding: 0 34rpx;
|
||||
margin-top: 14rpx;
|
||||
padding-bottom: 6rpx;
|
||||
|
||||
.delivered {
|
||||
font-size: 24rpx;
|
||||
line-height: 24rpx;
|
||||
color: #727377;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// 华为样式
|
||||
.huawei-style {
|
||||
.top-text {
|
||||
text-align: center;
|
||||
font-size: 22rpx;
|
||||
line-height: 22rpx;
|
||||
color: #545454;
|
||||
margin-bottom: 12rpx;
|
||||
}
|
||||
|
||||
.time {
|
||||
text-align: center;
|
||||
font-size: 22rpx;
|
||||
line-height: 22rpx;
|
||||
color: #545454;
|
||||
margin-top: 40rpx;
|
||||
}
|
||||
|
||||
.chat-box {
|
||||
display: flex;
|
||||
justify-content: flex-start;
|
||||
width: 100%;
|
||||
|
||||
.chat-bubble {
|
||||
padding: 12rpx 24rpx;
|
||||
background-color: #FFFFFF;
|
||||
border-radius: 4rpx 28rpx 28rpx 28rpx;
|
||||
max-width: 550rpx;
|
||||
margin: 0 30rpx;
|
||||
margin-top: 34rpx;
|
||||
font-size: 30rpx;
|
||||
line-height: 38rpx;
|
||||
color: #1a1a1a;
|
||||
font-weight: 500;
|
||||
word-break: break-all;
|
||||
}
|
||||
}
|
||||
|
||||
.second-info {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
color: #545454;
|
||||
font-size: 20rpx;
|
||||
line-height: 24rpx;
|
||||
margin-top: 16rpx;
|
||||
padding-left: 52rpx;
|
||||
padding-right: 56rpx;
|
||||
|
||||
image {
|
||||
width: 14rpx;
|
||||
height: 18rpx;
|
||||
margin-left: 8rpx;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
.m-t-44 {
|
||||
margin-top: 44rpx;
|
||||
}
|
||||
|
||||
|
||||
.isMe {
|
||||
.second-info {
|
||||
justify-content: flex-end;
|
||||
}
|
||||
|
||||
.message-item {
|
||||
margin-top: 34rpx;
|
||||
}
|
||||
|
||||
.chat-box {
|
||||
justify-content: flex-end;
|
||||
align-items: center;
|
||||
|
||||
.send-text {
|
||||
color: #646464;
|
||||
font-size: 24rpx;
|
||||
}
|
||||
|
||||
.chat-bubble {
|
||||
margin-left: 12rpx;
|
||||
background-color: #3681FF;
|
||||
color: #FFFFFF;
|
||||
border-radius: 28rpx 4rpx 28rpx 28rpx;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
// vivo样式
|
||||
.vivo-style {
|
||||
.m-t-16 {
|
||||
margin-top: 24rpx !important;
|
||||
}
|
||||
|
||||
.time {
|
||||
color: #ACACAC;
|
||||
font-size: 24rpx;
|
||||
line-height: 24rpx;
|
||||
text-align: center;
|
||||
margin: 40rpx 0;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.message-item {
|
||||
margin-top: 12rpx;
|
||||
}
|
||||
|
||||
.chat-box {
|
||||
display: flex;
|
||||
justify-content: flex-start;
|
||||
width: 100%;
|
||||
|
||||
.chat-bubble {
|
||||
padding: 38rpx 38rpx 32rpx 40rpx;
|
||||
background-color: #FFFFFF;
|
||||
border-radius: 32rpx;
|
||||
max-width: 100%;
|
||||
font-size: 36rpx;
|
||||
line-height: 48rpx;
|
||||
color: #1A1A1A;
|
||||
margin: 0 32rpx;
|
||||
word-break: break-all;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
.isMe {
|
||||
.chat-box {
|
||||
justify-content: flex-end;
|
||||
}
|
||||
|
||||
.chat-bubble {
|
||||
background-color: #0078FE;
|
||||
color: #fff;
|
||||
}
|
||||
}
|
||||
|
||||
.second-info {
|
||||
font-size: 24rpx;
|
||||
text-align: right;
|
||||
padding: 0 34rpx;
|
||||
margin-top: 12rpx;
|
||||
padding-bottom: 6rpx;
|
||||
|
||||
.delivered {
|
||||
font-size: 24rpx;
|
||||
line-height: 24rpx;
|
||||
color: #ACACAC;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
</style>
|
||||
|
|
@ -1,145 +0,0 @@
|
|||
<template>
|
||||
<view :style="`${phone}-style`">
|
||||
<uni-swipe-action class="swipe-action">
|
||||
<!-- 使用插槽 (请自行给定插槽内容宽度)-->
|
||||
<uni-swipe-action-item>
|
||||
<view>
|
||||
<view class="item flex-align-center">
|
||||
<view class="dot"></view>
|
||||
<image class="img shrink-0" :src="`/static/image/phone-message/${phone}/default.png`"></image>
|
||||
<view class="border-box m-l-24 flex-1 flex flex-align-start">
|
||||
<view class="main-box flex-1">
|
||||
<view class="title-box flex-between">
|
||||
<text class="title">title</text>
|
||||
<text class="time">昨天</text>
|
||||
</view>
|
||||
<view class="content">市燃管办温馨提示:依法安全文明燃放烟花爆竹,共护平安幸福家园呵呵哈哈哈还好...</view>
|
||||
</view>
|
||||
<view class="box-right h100 flex-column flex-align-center">
|
||||
<uni-icons type="right" size="14" color="#C1C1C1"></uni-icons>
|
||||
<image class="img" src="/static/image/phone-message/iphone/notice.png"></image>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<template v-slot:right>
|
||||
<view><text>删除</text></view>
|
||||
</template>
|
||||
</uni-swipe-action-item>
|
||||
</uni-swipe-action>
|
||||
</view>
|
||||
</template>
|
||||
<script setup>
|
||||
import {
|
||||
ref,
|
||||
reactive,
|
||||
computed
|
||||
} from 'vue'
|
||||
import {
|
||||
util
|
||||
} from '@/utils/common.js';
|
||||
|
||||
const props = defineProps({
|
||||
// 手机品牌
|
||||
phone: {
|
||||
type: String,
|
||||
default: 'iphone'
|
||||
}
|
||||
})
|
||||
</script>
|
||||
<style>
|
||||
@import '@/common/main.css';
|
||||
</style>
|
||||
<style lang="less" scoped>
|
||||
.m-t-4 {
|
||||
margin-top: 4rpx;
|
||||
}
|
||||
|
||||
.m-l-24 {
|
||||
margin-left: 24rpx;
|
||||
}
|
||||
|
||||
.iphone-style {
|
||||
|
||||
.swipe-action {
|
||||
margin-top: 24rpx;
|
||||
}
|
||||
|
||||
.item:first-child {
|
||||
.border-box {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.border-box::before {
|
||||
position: absolute;
|
||||
content: '';
|
||||
width: 100%;
|
||||
height: 1px;
|
||||
top: -1px;
|
||||
left: 0;
|
||||
right: 0;
|
||||
box-shadow: 0 0.3px 0 0 #C2C2C2;
|
||||
}
|
||||
}
|
||||
|
||||
.item {
|
||||
padding: 0 0 0 14rpx;
|
||||
height: 146rpx;
|
||||
|
||||
.dot {
|
||||
width: 20rpx;
|
||||
height: 20rpx;
|
||||
background-color: #007BFD;
|
||||
border-radius: 50%;
|
||||
}
|
||||
|
||||
.img {
|
||||
width: 84rpx;
|
||||
height: 84rpx;
|
||||
margin-left: 16rpx;
|
||||
}
|
||||
|
||||
.border-box {
|
||||
padding: 18rpx 14rpx 14rpx 0;
|
||||
height: 100%;
|
||||
box-shadow: inset 0 -0.3px 0 0 #C2C2C2;
|
||||
}
|
||||
|
||||
.main-box {
|
||||
.title-box {
|
||||
margin-bottom: 6rpx;
|
||||
|
||||
.title {
|
||||
color: #1A1A1A;
|
||||
font-size: 32rpx;
|
||||
line-height: 32rpx;
|
||||
}
|
||||
|
||||
.time {
|
||||
color: #838383;
|
||||
font-size: 28rpx;
|
||||
line-height: 28rpx;
|
||||
}
|
||||
}
|
||||
|
||||
.content {
|
||||
font-size: 28rpx;
|
||||
line-height: 38rpx;
|
||||
color: #838383;
|
||||
display: -webkit-box;
|
||||
-webkit-box-orient: vertical;
|
||||
-webkit-line-clamp: 2;
|
||||
overflow: hidden;
|
||||
}
|
||||
}
|
||||
|
||||
.box-right {
|
||||
.img {
|
||||
width: 28rpx;
|
||||
height: 28rpx;
|
||||
margin: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
|
@ -0,0 +1,655 @@
|
|||
<template>
|
||||
<view :style="`${phone}-style`">
|
||||
<uni-swipe-action class="swipe-action">
|
||||
<!-- 使用插槽 (请自行给定插槽内容宽度)-->
|
||||
<uni-swipe-action-item class="swipe-action-item" v-for="(item, index) in list" :key="item.id">
|
||||
<view class="flex flex-align-center " @click="clickItem(item)">
|
||||
|
||||
<view class="item flex w100">
|
||||
<view class="flex flex-align-center left-box">
|
||||
<view :class="{ 'opacity-0': !item.unRead }" class="dot shrink-0"><text
|
||||
v-if="phone == 'huawei' || phone == 'oppo'">{{ item.unReadNumber
|
||||
> 99
|
||||
? '99+' : (item.unReadNumber || 1) }}</text>
|
||||
</view>
|
||||
<image class="img avatar shrink-0"
|
||||
:src="item.img || `/static/image/phone-message/${phone}/default.png`">
|
||||
</image>
|
||||
</view>
|
||||
<view class="border-box m-l-24 flex-1 flex flex-align-start">
|
||||
<view class="main-box flex-1">
|
||||
<view class="title-box flex-between">
|
||||
<text class="title">{{ item.title }}</text>
|
||||
<text class="time">{{ formatDate(item.chatList?.[item.chatList?.length -
|
||||
1]?.time || item.time)
|
||||
}}</text>
|
||||
</view>
|
||||
<view class="content"
|
||||
v-html="item.chatList?.[item.chatList?.length - 1]?.content || ''"></view>
|
||||
</view>
|
||||
<view class="box-right h100 flex-column flex-align-center">
|
||||
<image v-if="phone == 'iphone'" class="m-t-4"
|
||||
src="/static/image/phone-message/iphone/right.png"></image>
|
||||
<image v-if="item.noNotice && phone == 'iphone'" class="m-t-8"
|
||||
src="/static/image/phone-message/iphone/notice.png"></image>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<template v-slot:right>
|
||||
<view class="flex flex-align-center" style="margin-left: 1px;">
|
||||
<view class="btn flex-center flex-align-center edit" style="color: #fff;"
|
||||
@click="editItem(item)">
|
||||
编辑
|
||||
<!-- <image :src="`/static/image/phone-message/iphone/delete.png`">
|
||||
</image> -->
|
||||
</view>
|
||||
<view class="btn flex-center flex-align-center delete" @click="deleteItem(item)">
|
||||
<image
|
||||
:src="`/static/image/phone-message/${phone == 'huawei' || phone == 'vivo' ? 'huawei' : 'iphone'}/delete.png`">
|
||||
</image>
|
||||
</view>
|
||||
|
||||
</view>
|
||||
|
||||
</template>
|
||||
</uni-swipe-action-item>
|
||||
</uni-swipe-action>
|
||||
</view>
|
||||
</template>
|
||||
<script>
|
||||
import { stringUtil } from '@/utils/common.js';
|
||||
|
||||
</script>
|
||||
<script setup>
|
||||
import {
|
||||
ref,
|
||||
reactive,
|
||||
computed
|
||||
} from 'vue'
|
||||
import {
|
||||
util,
|
||||
dateUtil
|
||||
} from '@/utils/common.js';
|
||||
// 定义事件
|
||||
const emit = defineEmits(['item-click', 'delete-item', 'edit-item'])
|
||||
const props = defineProps({
|
||||
// 手机品牌
|
||||
phone: {
|
||||
type: String,
|
||||
default: 'iphone'
|
||||
},
|
||||
list: {
|
||||
type: Array,
|
||||
default: () => []
|
||||
}
|
||||
})
|
||||
|
||||
/**
|
||||
* 时间日期格式化判断
|
||||
* @param date
|
||||
*/
|
||||
const formatDate = (date) => {
|
||||
if (props.phone == 'oppo') {
|
||||
return dateUtil.formatMessageTime(date, true)
|
||||
} else if (props.phone == 'huawei') {
|
||||
return dateUtil.formatMessageTime(date, true, 'YYYY年M月D日')
|
||||
} else if (props.phone == 'vivo') {
|
||||
let d = date;
|
||||
if (typeof d === 'string') {
|
||||
d = new Date(d.replace(/-/g, '/'));
|
||||
} else if (typeof d === 'number') {
|
||||
d = new Date(d);
|
||||
}
|
||||
const isCurrentYear = d.getFullYear() === new Date().getFullYear();
|
||||
return dateUtil.formatMessageTime(date, true, isCurrentYear ? 'M月D日' : 'YYYY/M/D')
|
||||
} else {
|
||||
return dateUtil.formatMessageTime(date)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 点击列表元素
|
||||
*/
|
||||
const clickItem = (item) => {
|
||||
emit('item-click', item)
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除列表元素
|
||||
*/
|
||||
const deleteItem = (item) => {
|
||||
emit('delete-item', item)
|
||||
}
|
||||
|
||||
/**
|
||||
* 修改元素
|
||||
* @param item
|
||||
*/
|
||||
const editItem = (item) => {
|
||||
emit('edit-item', item)
|
||||
}
|
||||
</script>
|
||||
<style>
|
||||
@import '@/common/main.css';
|
||||
</style>
|
||||
<style lang="less" scoped>
|
||||
.m-t-4 {
|
||||
margin-top: 4rpx;
|
||||
}
|
||||
|
||||
.m-t-8 {
|
||||
margin-top: 8rpx;
|
||||
}
|
||||
|
||||
.m-l-24 {
|
||||
margin-left: 24rpx;
|
||||
}
|
||||
|
||||
.opacity-0 {
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
.swipe-action-item {
|
||||
background-color: #FFFFFF;
|
||||
}
|
||||
|
||||
.avatar {
|
||||
border-radius: 50%;
|
||||
}
|
||||
|
||||
.edit {
|
||||
background-color: #5855D6;
|
||||
}
|
||||
|
||||
// 苹果样式
|
||||
.iphone-style {
|
||||
|
||||
.swipe-action {
|
||||
margin-top: 28rpx;
|
||||
}
|
||||
|
||||
.swipe-action-item:first-child {
|
||||
.border-box {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.border-box::before {
|
||||
position: absolute;
|
||||
content: '';
|
||||
width: 100%;
|
||||
height: 1px;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
background-color: #D8D8D8;
|
||||
transform: scaleY(0.3);
|
||||
}
|
||||
}
|
||||
|
||||
.item {
|
||||
padding: 0 0 0 14rpx;
|
||||
height: 146rpx;
|
||||
align-items: center;
|
||||
|
||||
.dot {
|
||||
width: 20rpx;
|
||||
height: 20rpx;
|
||||
background-color: #007BFD;
|
||||
border-radius: 50%;
|
||||
}
|
||||
|
||||
.img {
|
||||
width: 84rpx;
|
||||
height: 84rpx;
|
||||
margin-left: 16rpx;
|
||||
border-radius: 50%;
|
||||
}
|
||||
|
||||
.border-box {
|
||||
position: relative;
|
||||
padding: 18rpx 14rpx 14rpx 0;
|
||||
height: 100%;
|
||||
// box-shadow: inset 0 -0.3px 0 0 #D8D8D8;
|
||||
}
|
||||
|
||||
.border-box::after {
|
||||
position: absolute;
|
||||
content: '';
|
||||
width: 100%;
|
||||
height: 1px;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
background-color: #D8D8D8;
|
||||
transform: scaleY(0.3);
|
||||
}
|
||||
|
||||
.main-box {
|
||||
.title-box {
|
||||
margin-bottom: 6rpx;
|
||||
|
||||
.title {
|
||||
color: #1A1A1A;
|
||||
font-size: 32rpx;
|
||||
line-height: 32rpx;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.time {
|
||||
color: #838383;
|
||||
font-size: 28rpx;
|
||||
line-height: 28rpx;
|
||||
}
|
||||
}
|
||||
|
||||
.content {
|
||||
font-size: 28rpx;
|
||||
line-height: 38rpx;
|
||||
color: #838383;
|
||||
display: -webkit-box;
|
||||
-webkit-box-orient: vertical;
|
||||
-webkit-line-clamp: 2;
|
||||
overflow: hidden;
|
||||
}
|
||||
}
|
||||
|
||||
.box-right {
|
||||
image {
|
||||
width: 28rpx;
|
||||
height: 28rpx;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.btn {
|
||||
width: 140rpx;
|
||||
height: 146rpx;
|
||||
|
||||
image {
|
||||
width: 48rpx;
|
||||
height: 48rpx;
|
||||
}
|
||||
}
|
||||
|
||||
.delete {
|
||||
background-color: #FC3E30;
|
||||
}
|
||||
|
||||
.edit {
|
||||
background-color: #5855D6;
|
||||
}
|
||||
}
|
||||
|
||||
// 小米样式
|
||||
.mi-style {
|
||||
.item {
|
||||
padding-top: 44rpx;
|
||||
height: 170rpx;
|
||||
|
||||
.left-box {
|
||||
position: relative;
|
||||
height: 40px;
|
||||
align-items: flex-start;
|
||||
padding-left: 54rpx;
|
||||
}
|
||||
|
||||
.dot {
|
||||
position: absolute;
|
||||
width: 20rpx;
|
||||
height: 20rpx;
|
||||
background-color: #FA3D30;
|
||||
border-radius: 50%;
|
||||
left: 16rpx;
|
||||
top: 50%;
|
||||
transform: translateY(-50%);
|
||||
}
|
||||
|
||||
.img {
|
||||
width: 76rpx;
|
||||
height: 76rpx;
|
||||
}
|
||||
|
||||
.border-box {
|
||||
position: relative;
|
||||
padding: 4rpx 52rpx 8rpx 0;
|
||||
margin-left: 22rpx;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.main-box {
|
||||
.title-box {
|
||||
margin-bottom: 6rpx;
|
||||
|
||||
.title {
|
||||
color: #1A1A1A;
|
||||
font-size: 32rpx;
|
||||
line-height: 32rpx;
|
||||
}
|
||||
|
||||
.time {
|
||||
color: #9A9A9A;
|
||||
font-size: 26rpx;
|
||||
line-height: 28rpx;
|
||||
}
|
||||
}
|
||||
|
||||
.content {
|
||||
font-size: 26rpx;
|
||||
line-height: 38rpx;
|
||||
color: #656565;
|
||||
display: -webkit-box;
|
||||
-webkit-box-orient: vertical;
|
||||
-webkit-line-clamp: 2;
|
||||
overflow: hidden;
|
||||
}
|
||||
}
|
||||
|
||||
.box-right {
|
||||
image {
|
||||
width: 28rpx;
|
||||
height: 28rpx;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.btn {
|
||||
width: 140rpx;
|
||||
height: 146rpx;
|
||||
|
||||
image {
|
||||
width: 48rpx;
|
||||
height: 48rpx;
|
||||
}
|
||||
}
|
||||
|
||||
.delete {
|
||||
background-color: #FC3E30;
|
||||
}
|
||||
|
||||
.edit {
|
||||
background-color: #5855D6;
|
||||
}
|
||||
}
|
||||
|
||||
// oppo样式
|
||||
.oppo-style {
|
||||
.item {
|
||||
padding-top: 28rpx;
|
||||
height: 178rpx;
|
||||
|
||||
.left-box {
|
||||
position: relative;
|
||||
height: 40px;
|
||||
align-items: flex-start;
|
||||
padding-left: 34rpx;
|
||||
}
|
||||
|
||||
.dot {
|
||||
position: absolute;
|
||||
padding: 4rpx 10rpx;
|
||||
background-color: #E93A22;
|
||||
color: #FFFFFF;
|
||||
line-height: 20rpx;
|
||||
font-size: 20rpx;
|
||||
border-radius: 16rpx;
|
||||
right: -8rpx;
|
||||
top: -4rpx;
|
||||
z-index: 1;
|
||||
border: 2rpx solid #FFFFFF;
|
||||
}
|
||||
|
||||
.img {
|
||||
width: 76rpx;
|
||||
height: 76rpx;
|
||||
}
|
||||
|
||||
.border-box {
|
||||
position: relative;
|
||||
padding: 4rpx 34rpx 28rpx 0;
|
||||
margin-left: 32rpx;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.main-box {
|
||||
.title-box {
|
||||
margin-bottom: 16rpx;
|
||||
|
||||
.title {
|
||||
color: #1A1A1A;
|
||||
font-size: 32rpx;
|
||||
line-height: 32rpx;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.time {
|
||||
color: #656565;
|
||||
font-size: 26rpx;
|
||||
line-height: 28rpx;
|
||||
}
|
||||
}
|
||||
|
||||
.content {
|
||||
font-size: 28rpx;
|
||||
line-height: 38rpx;
|
||||
color: #737373;
|
||||
display: -webkit-box;
|
||||
-webkit-box-orient: vertical;
|
||||
-webkit-line-clamp: 2;
|
||||
overflow: hidden;
|
||||
}
|
||||
}
|
||||
|
||||
.box-right {
|
||||
image {
|
||||
width: 28rpx;
|
||||
height: 28rpx;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.btn {
|
||||
width: 140rpx;
|
||||
height: 146rpx;
|
||||
|
||||
image {
|
||||
width: 48rpx;
|
||||
height: 48rpx;
|
||||
}
|
||||
}
|
||||
|
||||
.delete {
|
||||
background-color: #FC3E30;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// 华为样式
|
||||
.huawei-style {
|
||||
.item {
|
||||
padding: 0 32rpx;
|
||||
height: 156rpx;
|
||||
align-items: center;
|
||||
|
||||
.left-box {
|
||||
position: relative;
|
||||
height: 40px;
|
||||
align-items: flex-start;
|
||||
}
|
||||
|
||||
.dot {
|
||||
position: absolute;
|
||||
padding: 6rpx 10rpx;
|
||||
background-color: #E93A22;
|
||||
color: #FFFFFF;
|
||||
line-height: 20rpx;
|
||||
font-size: 20rpx;
|
||||
border-radius: 16rpx;
|
||||
right: -8rpx;
|
||||
top: -12rpx;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.img {
|
||||
width: 76rpx;
|
||||
height: 76rpx;
|
||||
}
|
||||
|
||||
.border-box {
|
||||
padding: 18rpx 0;
|
||||
position: relative;
|
||||
margin-left: 30rpx;
|
||||
margin-top: 2rpx;
|
||||
height: 100%;
|
||||
box-shadow: 0 -0.3px 0 0 #CFCFCF;
|
||||
}
|
||||
|
||||
// .border-box::after {
|
||||
// position: absolute;
|
||||
// content: '';
|
||||
// width: 100%;
|
||||
// height: 1px;
|
||||
// bottom: 0;
|
||||
// left: 0;
|
||||
// right: 0;
|
||||
// background-color: #CFCFCF;
|
||||
// transform: scaleY(0.3);
|
||||
// }
|
||||
|
||||
.main-box {
|
||||
.title-box {
|
||||
margin-bottom: 6rpx;
|
||||
|
||||
.title {
|
||||
color: #1A1A1A;
|
||||
font-size: 32rpx;
|
||||
line-height: 32rpx;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.time {
|
||||
color: #656565;
|
||||
font-size: 24rpx;
|
||||
line-height: 28rpx;
|
||||
}
|
||||
}
|
||||
|
||||
.content {
|
||||
font-size: 28rpx;
|
||||
line-height: 38rpx;
|
||||
color: #6F6F6F;
|
||||
display: -webkit-box;
|
||||
-webkit-box-orient: vertical;
|
||||
-webkit-line-clamp: 2;
|
||||
overflow: hidden;
|
||||
}
|
||||
}
|
||||
|
||||
.box-right {
|
||||
image {
|
||||
width: 28rpx;
|
||||
height: 28rpx;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.btn {
|
||||
width: 140rpx;
|
||||
height: 156rpx;
|
||||
|
||||
image {
|
||||
width: 76rpx;
|
||||
height: 76rpx;
|
||||
}
|
||||
}
|
||||
|
||||
.delete {
|
||||
background-color: #F4F4F4;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// vivo样式
|
||||
.vivo-style {
|
||||
.item {
|
||||
padding: 0 50rpx 0 24rpx;
|
||||
height: 172rpx;
|
||||
align-items: center;
|
||||
|
||||
|
||||
.dot {
|
||||
width: 12rpx;
|
||||
height: 12rpx;
|
||||
background-color: #409DFE;
|
||||
border-radius: 50%;
|
||||
flex-shrink: 0;
|
||||
margin-right: 10rpx;
|
||||
}
|
||||
|
||||
|
||||
.img {
|
||||
width: 80rpx;
|
||||
height: 80rpx;
|
||||
}
|
||||
|
||||
.border-box {
|
||||
padding: 28rpx 0 18rpx;
|
||||
position: relative;
|
||||
margin-left: 24rpx;
|
||||
margin-top: 2rpx;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.main-box {
|
||||
.title-box {
|
||||
margin-bottom: 18rpx;
|
||||
|
||||
.title {
|
||||
color: #1A1A1A;
|
||||
font-size: 32rpx;
|
||||
line-height: 32rpx;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.time {
|
||||
color: #7C7C7C;
|
||||
font-size: 26rpx;
|
||||
line-height: 26rpx;
|
||||
}
|
||||
}
|
||||
|
||||
.content {
|
||||
font-size: 28rpx;
|
||||
line-height: 38rpx;
|
||||
color: #6F6F6F;
|
||||
display: -webkit-box;
|
||||
-webkit-box-orient: vertical;
|
||||
-webkit-line-clamp: 2;
|
||||
overflow: hidden;
|
||||
}
|
||||
}
|
||||
|
||||
.box-right {
|
||||
image {
|
||||
width: 28rpx;
|
||||
height: 28rpx;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.btn {
|
||||
width: 140rpx;
|
||||
height: 156rpx;
|
||||
|
||||
image {
|
||||
width: 76rpx;
|
||||
height: 76rpx;
|
||||
}
|
||||
}
|
||||
|
||||
.delete {
|
||||
background-color: #F4F4F4;
|
||||
}
|
||||
|
||||
}
|
||||
</style>
|
||||
|
|
@ -1,7 +1,10 @@
|
|||
<template>
|
||||
<view :class="`${phone}-style`">
|
||||
<!-- 导航样式 -->
|
||||
<NavBar :isBack="false">
|
||||
<view class="nav-bar-box" :class="{ 'border-nav-bar-box': isScroll }">
|
||||
<NavBar :isBack="false" :bgColor="isScroll ? data.navBar.bgColor : '#fff'" :buttonGroup="buttonGroup"
|
||||
@button-click="util.clickTitlePopupButton" tipLayerType="message-list-tip" isTipLayer
|
||||
tipLayerText="添加短信" @add="emit('add')">
|
||||
<!-- 左侧文字图标 -->
|
||||
<template v-slot:left>
|
||||
<view v-if="phone == 'iphone'" class="flex flex-align-center">
|
||||
|
|
@ -23,8 +26,10 @@
|
|||
<template v-slot:right>
|
||||
<!-- iphone -->
|
||||
<view v-if="phone == 'iphone'">
|
||||
<image class="right-icon mg-r-30" src="/static/image/phone-message/iphone/more.png" mode=""></image>
|
||||
<image class="right-icon mg-r-5" src="/static/image/phone-message/iphone/edit.png" mode=""></image>
|
||||
<image class="right-icon mg-r-30" src="/static/image/phone-message/iphone/more.png" mode="">
|
||||
</image>
|
||||
<image class="right-icon mg-r-5" src="/static/image/phone-message/iphone/edit.png" mode="">
|
||||
</image>
|
||||
</view>
|
||||
<!-- mi -->
|
||||
<view v-if="phone == 'mi'">
|
||||
|
|
@ -32,13 +37,15 @@
|
|||
</view>
|
||||
<!-- oppo -->
|
||||
<view v-if="phone == 'oppo'">
|
||||
<image class="right-icon mg-r-52" src="/static/image/phone-message/oppo/search.png" mode=""></image>
|
||||
<image class="right-icon mg-r-14" src="/static/image/phone-message/oppo/more.png" mode=""></image>
|
||||
<image class="right-icon mg-r-52" src="/static/image/phone-message/oppo/search.png" mode="">
|
||||
</image>
|
||||
<image class="right-icon mg-r-14" src="/static/image/phone-message/oppo/more.png" mode="">
|
||||
</image>
|
||||
</view>
|
||||
<!-- huawei -->
|
||||
<view v-if="phone == 'huawei'">
|
||||
<image v-if="isScroll" class="right-icon" src="/static/image/phone-message/huawei/nav-search.png"
|
||||
mode="">
|
||||
<image v-if="isScroll" class="right-icon"
|
||||
src="/static/image/phone-message/huawei/nav-search.png" mode="">
|
||||
</image>
|
||||
<image class="right-icon" src="/static/image/phone-message/huawei/add.png" mode=""></image>
|
||||
<image class="right-icon" src="/static/image/phone-message/huawei/more.png" mode=""></image>
|
||||
|
|
@ -48,10 +55,13 @@
|
|||
<image class="right-icon" src="/static/image/phone-message/vivo/select.png" mode="">
|
||||
</image>
|
||||
<image class="right-icon" src="/static/image/phone-message/vivo/add.png" mode=""></image>
|
||||
<image class="right-icon m-r-34" src="/static/image/phone-message/vivo/more.png" mode=""></image>
|
||||
<image class="right-icon m-r-34" src="/static/image/phone-message/vivo/more.png" mode="">
|
||||
</image>
|
||||
</view>
|
||||
</template>
|
||||
</NavBar>
|
||||
</view>
|
||||
|
||||
<!-- 主体内容 -->
|
||||
<view class="main-container">
|
||||
<!-- 顶部搜索栏样式 -->
|
||||
|
|
@ -62,12 +72,15 @@
|
|||
<image class="icon" :src="`/static/image/phone-message/${props.phone}/search.png`">
|
||||
</image>
|
||||
<input class="input flex-1" :placeholder="showInfo.placeholder" type="text">
|
||||
<image class="icon" :src="`/static/image/phone-message/${props.phone}/mic.png`"></image>
|
||||
<image v-if="phone != 'mi'" class="icon"
|
||||
:src="`/static/image/phone-message/${props.phone}/mic.png`">
|
||||
</image>
|
||||
</view>
|
||||
</view>
|
||||
<slot>
|
||||
</slot>
|
||||
</view>
|
||||
<view class="bottom-placeholder"></view>
|
||||
<!-- 底部样式 -->
|
||||
<template v-if="phone == 'mi' || phone == 'oppo'">
|
||||
<image class="add-message" :src="`/static/image/phone-message/${props.phone}/add-message.png`"></image>
|
||||
|
|
@ -97,6 +110,15 @@ import {
|
|||
util
|
||||
} from '@/utils/common.js';
|
||||
|
||||
const emit = defineEmits(['add'])
|
||||
|
||||
const buttonGroup = [{
|
||||
name: "添加短信",
|
||||
click: () => {
|
||||
emit('add')
|
||||
}
|
||||
}]
|
||||
|
||||
const props = defineProps({
|
||||
// 手机品牌
|
||||
phone: {
|
||||
|
|
@ -110,6 +132,14 @@ const props = defineProps({
|
|||
}
|
||||
})
|
||||
|
||||
|
||||
const data = reactive({
|
||||
navBar: {
|
||||
title: '信息',
|
||||
bgColor: '#FFFFFF',
|
||||
}
|
||||
})
|
||||
|
||||
// 展示文字信息
|
||||
const showInfo = computed(() => {
|
||||
let text, placeholder, secondText, bottomLtext, bottomRtext
|
||||
|
|
@ -117,6 +147,7 @@ const showInfo = computed(() => {
|
|||
case "iphone":
|
||||
text = "信息"
|
||||
placeholder = '搜索'
|
||||
data.navBar.bgColor = '#F8F8F8'
|
||||
break;
|
||||
case "mi":
|
||||
text = "主要"
|
||||
|
|
@ -144,12 +175,6 @@ const showInfo = computed(() => {
|
|||
return { text, placeholder, secondText, bottomLtext, bottomRtext }
|
||||
})
|
||||
|
||||
const data = reactive({
|
||||
navBar: {
|
||||
title: '信息',
|
||||
bgColor: '#FFFFFF',
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<style>
|
||||
|
|
@ -159,7 +184,7 @@ page {
|
|||
background-color: #FFFFFF;
|
||||
}
|
||||
</style>
|
||||
<style lang="less">
|
||||
<style lang="less" scoped>
|
||||
::v-deep .uni-navbar__header-btns {
|
||||
width: 100px !important;
|
||||
flex: 1;
|
||||
|
|
@ -177,6 +202,7 @@ page {
|
|||
height: 120rpx;
|
||||
border-top: 1rpx solid #E7E7E7;
|
||||
background-color: #ffffff;
|
||||
z-index: 9;
|
||||
|
||||
.item {
|
||||
display: flex;
|
||||
|
|
@ -231,6 +257,12 @@ page {
|
|||
color: #1a1a1a;
|
||||
}
|
||||
|
||||
.border-nav-bar-box {
|
||||
::v-deep .nav-bar {
|
||||
border-bottom: 1rpx solid #B5B5B5;
|
||||
}
|
||||
}
|
||||
|
||||
.main-container {
|
||||
.top-box {
|
||||
padding: 20rpx 32rpx 0;
|
||||
|
|
@ -239,6 +271,7 @@ page {
|
|||
color: #1A1A1A;
|
||||
font-size: 64rpx;
|
||||
font-weight: 700;
|
||||
line-height: 72rpx;
|
||||
}
|
||||
|
||||
.search-box {
|
||||
|
|
@ -318,6 +351,10 @@ page {
|
|||
height: 120rpx;
|
||||
}
|
||||
|
||||
.bottom-placeholder {
|
||||
height: 120rpx;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
|
@ -359,6 +396,12 @@ page {
|
|||
right: 46rpx;
|
||||
}
|
||||
|
||||
.bottom-placeholder {
|
||||
height: 148rpx;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
|
||||
.bottom-box {
|
||||
height: 148rpx;
|
||||
background-color: #FAFAFA;
|
||||
|
|
@ -403,6 +446,12 @@ page {
|
|||
margin: 0 8rpx;
|
||||
}
|
||||
|
||||
.border-nav-bar-box {
|
||||
::v-deep .nav-bar {
|
||||
border-bottom: 1rpx solid #D1D1D1;
|
||||
}
|
||||
}
|
||||
|
||||
.main-container {
|
||||
|
||||
|
||||
2
main.js
|
|
@ -27,7 +27,7 @@ export function createApp() {
|
|||
const systemInfo = uni.getStorageSync('systemInfo') || {}
|
||||
app.config.globalProperties.$system = systemInfo.platform == 'ios' ? 'iOS' : 'Android'
|
||||
app.config.globalProperties.$systemInfo = systemInfo
|
||||
uni.setStorageSync('version', '1.0.3.sp2')
|
||||
uni.setStorageSync('version', '1.0.3.sp4')
|
||||
app.config.globalProperties.$version = uni.getStorageSync('version')
|
||||
|
||||
app.use(globalMethods);
|
||||
|
|
|
|||
10
pages.json
|
|
@ -28,7 +28,15 @@
|
|||
"navigationBarTitleText": "短信列表首页",
|
||||
"navigationStyle": "custom"
|
||||
}
|
||||
}]
|
||||
},
|
||||
{
|
||||
"path": "chat-page/chat-page",
|
||||
"style": {
|
||||
"navigationBarTitleText": "短信聊天页面",
|
||||
"navigationStyle": "custom"
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"root": "pages/balance",
|
||||
|
|
|
|||
|
|
@ -53,8 +53,11 @@
|
|||
<view class="detail-info-container">
|
||||
<template v-for="item in billData.itemInfoList" :key="item.id">
|
||||
<view class="info-item-box" v-if="item.key != 'paymentReward'">
|
||||
<view class="item-label">
|
||||
{{ item.label }}
|
||||
<view class="item-label" :class="{ 'switchable-label': item.key === 'createTime' }"
|
||||
@click="toggleCreateTimeLabel(item)">
|
||||
<text>{{ item.label }}</text>
|
||||
<image v-if="item.key === 'createTime'" class="switch-icon"
|
||||
src="/static/image/bill/add-bill/edit.png"></image>
|
||||
</view>
|
||||
<view v-if="item.type != 'link'" class="info-item-input" @click="onClickItemInfo(item)">
|
||||
<!-- 隐藏的text用于测量宽度 -->
|
||||
|
|
@ -882,6 +885,15 @@ const onRightClick = async () => {
|
|||
}, 500)
|
||||
}
|
||||
|
||||
/**
|
||||
* 切换创建时间/支付时间标签
|
||||
*/
|
||||
const toggleCreateTimeLabel = (item) => {
|
||||
if (item.key === 'createTime') {
|
||||
item.label = item.label === '创建时间' ? '支付时间' : '创建时间'
|
||||
}
|
||||
}
|
||||
|
||||
// 点击itemInfo
|
||||
const onClickItemInfo = async (item, action) => {
|
||||
console.log(item)
|
||||
|
|
@ -1230,6 +1242,22 @@ page {
|
|||
font-size: 26rpx;
|
||||
color: var(--text-secondary);
|
||||
}
|
||||
|
||||
.switchable-label {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
padding: 4rpx 10rpx 4rpx 10rpx;
|
||||
margin-left: -10rpx;
|
||||
border-radius: 8rpx;
|
||||
border: 1px dashed #d9d9d9;
|
||||
background-color: #fcfcfc;
|
||||
|
||||
.switch-icon {
|
||||
width: 18rpx;
|
||||
height: 18rpx;
|
||||
margin-left: 6rpx;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.info-item-input {
|
||||
|
|
|
|||
|
|
@ -1,4 +1,11 @@
|
|||
<template>
|
||||
<!-- 水印 -->
|
||||
<view v-if="$isVip()">
|
||||
<watermark dark="light" />
|
||||
<liu-drag-button :canDocking="false" @clickBtn="$goRechargePage('watermark')">
|
||||
<c-lottie ref="cLottieRef" :src='$watermark()' width="94px" height='74px' :loop="true"></c-lottie>
|
||||
</liu-drag-button>
|
||||
</view>
|
||||
<view class="container">
|
||||
<!-- 自定义头部导航栏 -->
|
||||
<ZdyNavbar :type="data.type" :scrollTop="data.scrollTop" @click="open"/>
|
||||
|
|
@ -27,6 +34,7 @@
|
|||
onLoad((option) => {
|
||||
data.type=option.type
|
||||
|
||||
|
||||
})
|
||||
onReady(() => {
|
||||
|
||||
|
|
@ -36,8 +44,9 @@
|
|||
if (plus.os.name === 'Android') {
|
||||
let colorTabbar="#FAFAFA"
|
||||
if(data.type=='xiaomi'||data.type=='oppo'){
|
||||
colorTabbar="#FFF"
|
||||
colorTabbar="#FFFFFF"
|
||||
}
|
||||
console.log(colorTabbar);
|
||||
util.setAndroidSystemBarColor(colorTabbar)
|
||||
setTimeout(() => {
|
||||
plus.navigator.setStatusBarStyle("dark");
|
||||
|
|
|
|||
|
|
@ -20,7 +20,8 @@
|
|||
import NavBar from '@/components/nav-bar/nav-bar.vue'
|
||||
import {
|
||||
ref,
|
||||
reactive
|
||||
reactive,
|
||||
getCurrentInstance
|
||||
} from 'vue'
|
||||
import {
|
||||
onLoad
|
||||
|
|
@ -29,6 +30,7 @@ import {
|
|||
util
|
||||
} from '@/utils/common.js';
|
||||
|
||||
const { appContext, proxy } = getCurrentInstance();
|
||||
// 列表
|
||||
const source = ref([{
|
||||
name: '苹果',
|
||||
|
|
@ -101,7 +103,14 @@ onLoad((options) => {
|
|||
})
|
||||
|
||||
function goPage(url) {
|
||||
|
||||
if (url) {
|
||||
proxy.$apiUserEvent('all', {
|
||||
type: 'event',
|
||||
key: data.type=='message'?'message':'call-log',
|
||||
prefix: '.uni.other.',
|
||||
value: data.type=='message'?'短信':"通话"
|
||||
})
|
||||
util.goPage(url)
|
||||
} else {
|
||||
uni.showToast({
|
||||
|
|
@ -148,8 +157,9 @@ function goPage(url) {
|
|||
border-radius: 16rpx;
|
||||
color: #ffffff;
|
||||
font-size: 28rpx;
|
||||
line-height: 64rpx;
|
||||
text-align: center;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,637 @@
|
|||
<template>
|
||||
<view :class="`${data.phone}-style`">
|
||||
<ChatLayout :phone="data.phone" :chatInfo="data.data" :sortMode="isSortMode" @send="handleSend">
|
||||
<!-- 弹出操作层及遮罩 -->
|
||||
<view v-if="showActionPopup" class="action-mask" @tap="closeActionPopup">
|
||||
<view class="action-popup" :style="{ top: popupTop + 'px', left: popupLeft + 'px' }">
|
||||
<view class="action-item" @tap.stop="handleEdit">
|
||||
<image class="action-icon" src="/static/image/phone-message/bianji.png"></image>
|
||||
<text>编辑</text>
|
||||
</view>
|
||||
<view class="action-item" @tap.stop="handleSwap">
|
||||
<image class="action-icon" src="/static/image/phone-message/huhuan.png"></image>
|
||||
<text>消息互换</text>
|
||||
</view>
|
||||
<view class="action-item" @tap.stop="handleSort">
|
||||
<image class="action-icon" src="/static/image/phone-message/bianji.png"></image>
|
||||
<text>排序</text>
|
||||
</view>
|
||||
<view class="action-item" @tap.stop="handleDelete">
|
||||
<image class="action-icon" src="/static/image/phone-message/shanchu.png"></image>
|
||||
<text>删除</text>
|
||||
</view>
|
||||
<!-- 向上指的三角形,因为要求在长按元素下方 -->
|
||||
<view class="triangle"></view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 编辑消息弹窗 -->
|
||||
<view v-if="showEditPopup" class="edit-mask" @tap="closeEditPopup">
|
||||
<view class="edit-popup" @tap.stop>
|
||||
<view class="edit-header">编辑消息</view>
|
||||
<view class="edit-body">
|
||||
<view class="edit-row">
|
||||
<text class="edit-label">时间:</text>
|
||||
<view class="time-picker-group">
|
||||
<picker mode="date" :value="editingDate" @change="onDateChange">
|
||||
<view class="time-picker-item">
|
||||
<text>{{ editingDate || '选择日期' }}</text>
|
||||
</view>
|
||||
</picker>
|
||||
<picker mode="time" :value="editingTimeOfDay" @change="onTimeOfDayChange">
|
||||
<view class="time-picker-item">
|
||||
<text>{{ editingTimeOfDay || '选择时刻' }}</text>
|
||||
</view>
|
||||
</picker>
|
||||
</view>
|
||||
</view>
|
||||
<view class="edit-row" style="align-items: flex-start;">
|
||||
<text class="edit-label" style="padding-top: 6rpx;">时间显示:</text>
|
||||
<view class="time-mode-group">
|
||||
<view class="time-mode-btn" :class="{ active: editingTimeMode === 'auto' }"
|
||||
@tap="editingTimeMode = 'auto'">自动</view>
|
||||
<view class="time-mode-btn" :class="{ active: editingTimeMode === 'show' }"
|
||||
@tap="editingTimeMode = 'show'">强制显示</view>
|
||||
<view class="time-mode-btn" :class="{ active: editingTimeMode === 'hide' }"
|
||||
@tap="editingTimeMode = 'hide'">强制隐藏</view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="edit-row"
|
||||
v-if="data.phone == 'huawei' || data.phone == 'oppo' || data.phone == 'vivo'">
|
||||
<text class="edit-label">SIM卡:</text>
|
||||
<view class="edit-input"
|
||||
style="padding: 0; background: transparent; display: flex; align-items: center; border-radius: 8rpx; height: 70rpx;">
|
||||
<uni-data-select v-model="editingSimKa" :localdata="simList" :clear="false"
|
||||
placeholder="请选择卡号"
|
||||
style="flex: 1; border: none !important; width: 100%;"></uni-data-select>
|
||||
</view>
|
||||
</view>
|
||||
<view class="edit-row">
|
||||
<text class="edit-label">正文:</text>
|
||||
</view>
|
||||
<editor id="editor" class="edit-textarea" placeholder="请输入消息内容..." @ready="onEditorReady">
|
||||
</editor>
|
||||
</view>
|
||||
<view class="edit-footer">
|
||||
<view class="edit-btn cancel" @tap="closeEditPopup">取消</view>
|
||||
<view class="edit-btn confirm" @tap="confirmEdit">确定</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<ChatList :messageList="messageList" :phone="data.phone" :sortMode="isSortMode"
|
||||
@onLongPress="onMessageLongPress" @sort="onSortChange"></ChatList>
|
||||
|
||||
|
||||
</ChatLayout>
|
||||
|
||||
<!-- 排序模式底部工具条 -->
|
||||
<view v-if="isSortMode" class="sort-toolbar">
|
||||
<view class="sort-toolbar-tip">长按消息并拖动调整顺序</view>
|
||||
<view class="sort-toolbar-actions">
|
||||
<view class="sort-toolbar-btn cancel" @tap="cancelSort">取消</view>
|
||||
<view class="sort-toolbar-btn confirm" @tap="confirmSort">完成</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import ChatLayout from '@/components/message/chat/chat-layout.vue'
|
||||
import ChatList from '@/components/message/chat/chat-list.vue'
|
||||
import defaultData from '../defaultData.json'
|
||||
import {
|
||||
ref,
|
||||
reactive,
|
||||
computed,
|
||||
nextTick
|
||||
} from 'vue'
|
||||
import {
|
||||
onLoad,
|
||||
onPageScroll
|
||||
} from "@dcloudio/uni-app";
|
||||
import {
|
||||
stringUtil,
|
||||
util
|
||||
} from '@/utils/common.js';
|
||||
|
||||
const defaultList = defaultData
|
||||
|
||||
// 与 list-index.vue 共用同一个缓存 key
|
||||
const STORAGE_KEY = 'message_list'
|
||||
let currentId = null // 当前会话的 id
|
||||
|
||||
const messageList = ref([])
|
||||
const showActionPopup = ref(false)
|
||||
const popupTop = ref(0)
|
||||
const popupLeft = ref(0)
|
||||
const selectedMessage = ref(null)
|
||||
|
||||
const showEditPopup = ref(false)
|
||||
const editingMessage = ref(null)
|
||||
const editingTime = ref("") // 完整时间字符串 "YYYY-MM-DD HH:mm"
|
||||
const editingDate = ref("") // 日期部分 "YYYY-MM-DD"
|
||||
const editingTimeOfDay = ref("") // 时刻部分 "HH:mm"
|
||||
const editingSimKa = ref("")
|
||||
const editingTimeMode = ref('auto') // 'auto' | 'show' | 'hide'
|
||||
let editorCtx = null // 用于保存 editor 上下文
|
||||
|
||||
// ===== 排序模式相关状态 =====
|
||||
const isSortMode = ref(false)
|
||||
let sortingListCache = [] // 拖拽过程中的中间列表,由 ChatList emit 过来
|
||||
|
||||
const simList = [
|
||||
{ text: "无卡号(不显)", value: "" },
|
||||
{ text: "卡1", value: 1 },
|
||||
{ text: "卡2", value: 2 }
|
||||
]
|
||||
|
||||
/**
|
||||
* 将当前 messageList 写回缓存中对应会话的 chatList
|
||||
* 同时更新列表条目顶层的 time(最新消息时间)
|
||||
*/
|
||||
const saveChatList = () => {
|
||||
if (!currentId) return
|
||||
try {
|
||||
const cached = uni.getStorageSync(STORAGE_KEY)
|
||||
const list = cached ? JSON.parse(cached) : defaultList
|
||||
const idx = list.findIndex(item => item.id == currentId)
|
||||
if (idx > -1) {
|
||||
list[idx].chatList = messageList.value.map(m => ({ ...m }))
|
||||
const chatArr = list[idx].chatList
|
||||
const last = chatArr.length ? chatArr[chatArr.length - 1] : null
|
||||
if (last && last.time) list[idx].time = last.time
|
||||
uni.setStorageSync(STORAGE_KEY, JSON.stringify(list))
|
||||
}
|
||||
} catch (e) { }
|
||||
}
|
||||
|
||||
const onEditorReady = () => {
|
||||
uni.createSelectorQuery().select('#editor').context((res) => {
|
||||
editorCtx = res.context
|
||||
if (editingMessage.value && editingMessage.value.content) {
|
||||
editorCtx.setContents({
|
||||
html: editingMessage.value.content
|
||||
})
|
||||
}
|
||||
}).exec()
|
||||
}
|
||||
|
||||
// 长按弹出弹出层
|
||||
const onMessageLongPress = (index, message) => {
|
||||
selectedMessage.value = message;
|
||||
uni.createSelectorQuery().select('#msg-' + index).boundingClientRect(rect => {
|
||||
if (rect) {
|
||||
// 将弹窗定位在元素正下方 (bottom边界 + 一点点边距)
|
||||
popupTop.value = rect.bottom + 10;
|
||||
// 弹窗水平居中于该内容
|
||||
let left = rect.left + rect.width / 2;
|
||||
|
||||
// 获取系统信息,防止弹出框超出屏幕左右侧
|
||||
uni.getSystemInfo({
|
||||
success: (info) => {
|
||||
let popupWidth = 150; // 预估弹出层的固定宽度(可根据实际情况微调)
|
||||
if (left < popupWidth / 2 + 10) left = popupWidth / 2 + 10;
|
||||
if (left > info.windowWidth - popupWidth / 2 - 10) left = info.windowWidth - popupWidth / 2 - 10;
|
||||
popupLeft.value = left;
|
||||
showActionPopup.value = true;
|
||||
}
|
||||
})
|
||||
}
|
||||
}).exec();
|
||||
}
|
||||
|
||||
const closeActionPopup = () => {
|
||||
showActionPopup.value = false;
|
||||
selectedMessage.value = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 编辑消息
|
||||
*/
|
||||
const handleEdit = () => {
|
||||
editingMessage.value = selectedMessage.value;
|
||||
editingTime.value = selectedMessage.value.time || "";
|
||||
// 拆分日期和时刻部分
|
||||
const parts = (selectedMessage.value.time || "").split(' ')
|
||||
editingDate.value = parts[0] || ""
|
||||
editingTimeOfDay.value = parts[1] || ""
|
||||
editingSimKa.value = selectedMessage.value.simKa !== undefined ? selectedMessage.value.simKa : "";
|
||||
editingTimeMode.value = selectedMessage.value.timeMode || 'auto';
|
||||
|
||||
showEditPopup.value = true;
|
||||
closeActionPopup();
|
||||
|
||||
// 如果已经初始化过了直接赋值
|
||||
if (editorCtx) {
|
||||
setTimeout(() => {
|
||||
editorCtx.setContents({
|
||||
html: editingMessage.value.content
|
||||
})
|
||||
}, 100)
|
||||
}
|
||||
}
|
||||
|
||||
const closeEditPopup = () => {
|
||||
showEditPopup.value = false;
|
||||
editingMessage.value = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 日期选择器回调 - 更新日期部分并同步 editingTime
|
||||
*/
|
||||
const onDateChange = (e) => {
|
||||
editingDate.value = e.detail.value
|
||||
editingTime.value = `${editingDate.value} ${editingTimeOfDay.value}`.trim()
|
||||
}
|
||||
|
||||
/**
|
||||
* 时刻选择器回调 - 更新时刻部分并同步 editingTime
|
||||
*/
|
||||
const onTimeOfDayChange = (e) => {
|
||||
editingTimeOfDay.value = e.detail.value
|
||||
editingTime.value = `${editingDate.value} ${editingTimeOfDay.value}`.trim()
|
||||
}
|
||||
|
||||
const confirmEdit = () => {
|
||||
if (editingMessage.value && editorCtx) {
|
||||
editorCtx.getContents({
|
||||
success: (res) => {
|
||||
const index = messageList.value.findIndex(item => item.id === editingMessage.value.id)
|
||||
if (index > -1) {
|
||||
messageList.value[index].content = res.html;
|
||||
messageList.value[index].time = editingTime.value;
|
||||
// 保存 timeMode: 'auto'|'show'|'hide'
|
||||
if (editingTimeMode.value === 'auto') {
|
||||
delete messageList.value[index].timeMode;
|
||||
} else {
|
||||
messageList.value[index].timeMode = editingTimeMode.value;
|
||||
}
|
||||
// 兼容旧 hideTime 字段:一并清除
|
||||
delete messageList.value[index].hideTime;
|
||||
if (editingSimKa.value) {
|
||||
messageList.value[index].simKa = Number(editingSimKa.value);
|
||||
} else {
|
||||
delete messageList.value[index].simKa;
|
||||
}
|
||||
}
|
||||
closeEditPopup();
|
||||
saveChatList();
|
||||
}
|
||||
})
|
||||
} else {
|
||||
closeEditPopup();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 消息互换
|
||||
*/
|
||||
const handleSwap = () => {
|
||||
const index = messageList.value.findIndex(item => item.id === selectedMessage.value.id)
|
||||
messageList.value[index].isMe = !messageList.value[index].isMe
|
||||
closeActionPopup();
|
||||
saveChatList()
|
||||
}
|
||||
|
||||
/**
|
||||
* 进入拖动排序模式
|
||||
*/
|
||||
const handleSort = () => {
|
||||
isSortMode.value = true
|
||||
sortingListCache = messageList.value.map(item => ({ ...item }))
|
||||
closeActionPopup()
|
||||
}
|
||||
|
||||
/**
|
||||
* ChatList 拖拽排序后回调 - 实时更新中间缓存
|
||||
*/
|
||||
const onSortChange = (newList) => {
|
||||
sortingListCache = newList
|
||||
}
|
||||
|
||||
/**
|
||||
* 取消排序,还原 messageList
|
||||
*/
|
||||
const cancelSort = () => {
|
||||
isSortMode.value = false
|
||||
// 不应用排序结果,无需重置messageList
|
||||
}
|
||||
|
||||
/**
|
||||
* 确认排序,将拖拽结果写回 messageList
|
||||
*/
|
||||
const confirmSort = () => {
|
||||
if (sortingListCache.length > 0) {
|
||||
messageList.value = sortingListCache.map(item => ({ ...item }))
|
||||
}
|
||||
isSortMode.value = false
|
||||
saveChatList()
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除消息
|
||||
*/
|
||||
const handleDelete = () => {
|
||||
const index = messageList.value.findIndex(item => item.id === selectedMessage.value.id)
|
||||
messageList.value.splice(index, 1)
|
||||
closeActionPopup();
|
||||
saveChatList()
|
||||
}
|
||||
|
||||
const data = reactive({
|
||||
phone: "iphone",
|
||||
data: {}
|
||||
})
|
||||
onLoad((options) => {
|
||||
console.log(options)
|
||||
if (options.phone) {
|
||||
data.phone = options.phone
|
||||
}
|
||||
if (options.id) {
|
||||
currentId = options.id
|
||||
// 优先从缓存读取
|
||||
try {
|
||||
const cached = uni.getStorageSync(STORAGE_KEY)
|
||||
const list = cached ? JSON.parse(cached) : defaultList
|
||||
const found = list.find(item => item.id == options.id)
|
||||
if (found) {
|
||||
data.data = found
|
||||
messageList.value = found.chatList || []
|
||||
return
|
||||
}
|
||||
} catch (e) { }
|
||||
// 缓存未命中时降级用内建默认数据
|
||||
data.data = defaultList.find(item => item.id == options.id)
|
||||
messageList.value = data.data?.chatList || []
|
||||
}
|
||||
})
|
||||
|
||||
const handleSend = (params) => {
|
||||
console.log(params)
|
||||
params.id = stringUtil.uuid()
|
||||
messageList.value.push(params)
|
||||
saveChatList()
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="less" scoped>
|
||||
.action-mask {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
z-index: 999;
|
||||
}
|
||||
|
||||
/* 拖动排序弹窗样式 */
|
||||
.sort-mask {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
background-color: rgba(0, 0, 0, 0.5);
|
||||
z-index: 1100;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.sort-popup {
|
||||
width: 680rpx;
|
||||
max-height: 80vh;
|
||||
background-color: #FFFFFF;
|
||||
border-radius: 24rpx;
|
||||
overflow: hidden;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
/* 排序模式底部工具条 - 覆盖在输入框上,高度与输入框一致 */
|
||||
.sort-toolbar {
|
||||
position: fixed;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
z-index: 1200;
|
||||
background-color: rgba(255, 255, 255, 0.97);
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
padding: 16rpx 32rpx;
|
||||
box-shadow: 0 -1rpx 0 rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
.sort-toolbar-tip {
|
||||
flex: 1;
|
||||
font-size: 24rpx;
|
||||
color: #999999;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.sort-toolbar-actions {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
gap: 20rpx;
|
||||
}
|
||||
|
||||
.sort-toolbar-btn {
|
||||
height: 72rpx;
|
||||
line-height: 72rpx;
|
||||
padding: 0 44rpx;
|
||||
text-align: center;
|
||||
font-size: 30rpx;
|
||||
border-radius: 36rpx;
|
||||
}
|
||||
|
||||
.sort-toolbar-btn.cancel {
|
||||
background-color: #F0F0F0;
|
||||
color: #666666;
|
||||
}
|
||||
|
||||
.sort-toolbar-btn.confirm {
|
||||
background-color: #007AFF;
|
||||
color: #FFFFFF;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
|
||||
.action-popup {
|
||||
position: fixed;
|
||||
background-color: #4C4C4C;
|
||||
border-radius: 12rpx;
|
||||
padding: 20rpx 30rpx;
|
||||
display: flex;
|
||||
transform: translateX(-50%);
|
||||
width: 540rpx;
|
||||
z-index: 10;
|
||||
}
|
||||
|
||||
.action-popup .triangle {
|
||||
position: absolute;
|
||||
top: -12rpx;
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
width: 0;
|
||||
height: 0;
|
||||
border-left: 12rpx solid transparent;
|
||||
border-right: 12rpx solid transparent;
|
||||
border-bottom: 14rpx solid #4C4C4C;
|
||||
}
|
||||
|
||||
.action-item {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
color: #FFFFFF;
|
||||
font-size: 20rpx;
|
||||
margin: 0 20rpx;
|
||||
}
|
||||
|
||||
.action-icon {
|
||||
width: 36rpx;
|
||||
height: 36rpx;
|
||||
margin-bottom: 8rpx;
|
||||
}
|
||||
|
||||
/* 编辑弹窗样式 */
|
||||
.edit-mask {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
background-color: rgba(0, 0, 0, 0.5);
|
||||
z-index: 10;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.edit-popup {
|
||||
width: 600rpx;
|
||||
background-color: #FFFFFF;
|
||||
border-radius: 16rpx;
|
||||
overflow: hidden;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.edit-header {
|
||||
font-size: 32rpx;
|
||||
font-weight: bold;
|
||||
color: #333;
|
||||
text-align: center;
|
||||
padding: 30rpx 0;
|
||||
border-bottom: 1rpx solid #EEEEEE;
|
||||
}
|
||||
|
||||
.edit-body {
|
||||
padding: 30rpx;
|
||||
}
|
||||
|
||||
.edit-textarea {
|
||||
width: 100%;
|
||||
height: 200rpx;
|
||||
background-color: #F8F8F8;
|
||||
padding: 20rpx;
|
||||
border-radius: 8rpx;
|
||||
font-size: 28rpx;
|
||||
color: #333;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
::v-deep .ql-container {
|
||||
min-height: 100px;
|
||||
}
|
||||
|
||||
.edit-footer {
|
||||
display: flex;
|
||||
border-top: 1rpx solid #EEEEEE;
|
||||
}
|
||||
|
||||
.edit-btn {
|
||||
flex: 1;
|
||||
height: 90rpx;
|
||||
line-height: 90rpx;
|
||||
text-align: center;
|
||||
font-size: 32rpx;
|
||||
}
|
||||
|
||||
.edit-btn.cancel {
|
||||
color: #666;
|
||||
border-right: 1rpx solid #EEEEEE;
|
||||
}
|
||||
|
||||
.edit-btn.confirm {
|
||||
color: #007AFF;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.edit-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-bottom: 20rpx;
|
||||
}
|
||||
|
||||
.edit-label {
|
||||
font-size: 28rpx;
|
||||
color: #333;
|
||||
width: 150rpx;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.edit-input {
|
||||
flex: 1;
|
||||
height: 70rpx;
|
||||
background-color: #F8F8F8;
|
||||
border-radius: 8rpx;
|
||||
padding: 0 20rpx;
|
||||
font-size: 28rpx;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.time-mode-group {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.time-mode-btn {
|
||||
padding: 4rpx 12rpx;
|
||||
border-radius: 32rpx;
|
||||
font-size: 26rpx;
|
||||
color: #666666;
|
||||
margin: 0 6rpx;
|
||||
background-color: #F0F0F0;
|
||||
border: 2rpx solid transparent;
|
||||
}
|
||||
|
||||
.time-mode-btn.active {
|
||||
color: #007AFF;
|
||||
background-color: #E8F2FF;
|
||||
border-color: #007AFF;
|
||||
}
|
||||
|
||||
/* 时间选择器组合样式 */
|
||||
.time-picker-group {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
gap: 16rpx;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.time-picker-item {
|
||||
flex: 1;
|
||||
height: 70rpx;
|
||||
line-height: 70rpx;
|
||||
background-color: #F8F8F8;
|
||||
border-radius: 8rpx;
|
||||
padding: 0 20rpx;
|
||||
font-size: 28rpx;
|
||||
color: #333333;
|
||||
text-align: center;
|
||||
}
|
||||
</style>
|
||||
|
|
@ -0,0 +1,234 @@
|
|||
[
|
||||
{
|
||||
"id": 12365458895222,
|
||||
"unRead": true,
|
||||
"unReadNumber": 2,
|
||||
"noNotice": true,
|
||||
"img": "",
|
||||
"title": "京东快递",
|
||||
"content": "【京东快递】您的快递已送达博朗郡二期...",
|
||||
"chatList": [
|
||||
{
|
||||
"id": "uuid-001-a1",
|
||||
"time": "2026-03-15 10:24",
|
||||
"content": "<p>【京东快递】快递尾号29398已放在博朗郡二期惠美家便利店,快递员电话15320547739。</p>",
|
||||
"isMe": false,
|
||||
"simKa": 1
|
||||
},
|
||||
{
|
||||
"id": "uuid-001-a2",
|
||||
"time": "2026-03-16 08:31",
|
||||
"content": "<p>【京东快递】您的包裹已签收,感谢使用京东快递,期待再次为您服务。</p>",
|
||||
"isMe": false,
|
||||
"simKa": 1
|
||||
}
|
||||
],
|
||||
"time": "2026-03-16 08:31:00"
|
||||
},
|
||||
{
|
||||
"id": 12365458895223,
|
||||
"unRead": false,
|
||||
"unReadNumber": 0,
|
||||
"noNotice": false,
|
||||
"img": "",
|
||||
"title": "招商银行",
|
||||
"content": "【招商银行】账户变动提醒",
|
||||
"chatList": [
|
||||
{
|
||||
"id": "uuid-002-b1",
|
||||
"time": "2026-03-15 12:00",
|
||||
"content": "<p>【招商银行】您尾号8888的账户于03月15日11:58收入人民币90000.00元,余额965600.50元。</p>",
|
||||
"isMe": false,
|
||||
"simKa": 1
|
||||
}
|
||||
],
|
||||
"time": "2026-03-15 12:00:00"
|
||||
},
|
||||
{
|
||||
"id": 12365458895224,
|
||||
"unRead": true,
|
||||
"unReadNumber": 1,
|
||||
"noNotice": false,
|
||||
"img": "",
|
||||
"title": "中国移动",
|
||||
"content": "【中国移动】流量使用周报",
|
||||
"chatList": [
|
||||
{
|
||||
"id": "uuid-003-c1",
|
||||
"time": "2026-03-14 09:00",
|
||||
"content": "<p>【中国移动】截至14日09时,您本月已使用通用流量18.5GB,剩余3.2GB。</p>",
|
||||
"isMe": false,
|
||||
"simKa": 1
|
||||
},
|
||||
{
|
||||
"id": "uuid-003-c2",
|
||||
"time": "2026-03-14 18:30",
|
||||
"content": "<p>【中国移动】流量包订购成功!您已成功领取5GB周末流量包,立即生效。</p>",
|
||||
"isMe": false,
|
||||
"simKa: 1"
|
||||
}
|
||||
],
|
||||
"time": "2026-03-14 18:30:00"
|
||||
},
|
||||
{
|
||||
"id": 12365458895225,
|
||||
"unRead": false,
|
||||
"unReadNumber": 0,
|
||||
"noNotice": false,
|
||||
"img": "",
|
||||
"title": "验证码中心",
|
||||
"content": "【抖音】您的验证码是 882931",
|
||||
"chatList": [
|
||||
{
|
||||
"id": "uuid-004-d1",
|
||||
"time": "2026-03-16 01:10",
|
||||
"content": "<p>【抖音】验证码 882931,用于手机绑定。5分钟内有效,请勿泄露给他人。</p>",
|
||||
"isMe": false,
|
||||
"simKa": 1
|
||||
}
|
||||
],
|
||||
"time": "2026-03-16 01:10:00"
|
||||
},
|
||||
{
|
||||
"id": 12365458895226,
|
||||
"unRead": false,
|
||||
"unReadNumber": 0,
|
||||
"noNotice": true,
|
||||
"img": "",
|
||||
"title": "美团外卖",
|
||||
"content": "【美团】订单配送通知",
|
||||
"chatList": [
|
||||
{
|
||||
"id": "uuid-005-e1",
|
||||
"time": "2026-03-15 18:30",
|
||||
"content": "<p>【美团】商家已接单,骑手正在赶往商家,请耐心等待。</p>",
|
||||
"isMe": false,
|
||||
"simKa": 2
|
||||
},
|
||||
{
|
||||
"id": "uuid-005-e2",
|
||||
"time": "2026-03-15 19:15",
|
||||
"content": "<p>【美团】骑手已送达!祝您用餐愉快,给个五星好评吧!</p>",
|
||||
"isMe": false,
|
||||
"simKa": 2
|
||||
}
|
||||
],
|
||||
"time": "2026-03-15 19:15:00"
|
||||
},
|
||||
{
|
||||
"id": 12365458895227,
|
||||
"unRead": true,
|
||||
"unReadNumber": 3,
|
||||
"noNotice": false,
|
||||
"img": "",
|
||||
"title": "建设银行",
|
||||
"content": "【建设银行】还款提醒",
|
||||
"chatList": [
|
||||
{
|
||||
"id": "uuid-006-f1",
|
||||
"time": "2026-03-10 10:00",
|
||||
"content": "<p>【建设银行】您03月信用卡账单已出,应还款额为¥3,200.00。</p>",
|
||||
"isMe": false,
|
||||
"simKa": 1
|
||||
},
|
||||
{
|
||||
"id": "uuid-006-f2",
|
||||
"time": "2026-03-13 14:00",
|
||||
"content": "<p>【建设银行】温馨提醒:您的账单将于3天后到期,请确保扣款账户余额充足。</p>",
|
||||
"isMe": false,
|
||||
"simKa": 1
|
||||
},
|
||||
{
|
||||
"id": "uuid-006-f3",
|
||||
"time": "2026-03-16 09:00",
|
||||
"content": "<p>【建设银行】扣款成功。感谢您使用建设银行信用卡。</p>",
|
||||
"isMe": false,
|
||||
"simKa": 1
|
||||
}
|
||||
],
|
||||
"time": "2026-03-16 09:00:00"
|
||||
},
|
||||
{
|
||||
"id": 12365458895228,
|
||||
"unRead": false,
|
||||
"unReadNumber": 0,
|
||||
"noNotice": false,
|
||||
"img": "",
|
||||
"title": "12306",
|
||||
"content": "【12306】购票成功通知",
|
||||
"chatList": [
|
||||
{
|
||||
"id": "uuid-007-g1",
|
||||
"time": "2026-03-11 11:00",
|
||||
"content": "<p>【12306】订单EG12345678支付成功。北京南-上海虹桥,03月20日14:00开。</p>",
|
||||
"isMe": false,
|
||||
"simKa": 2
|
||||
}
|
||||
],
|
||||
"time": "2026-03-11 11:00:00"
|
||||
},
|
||||
{
|
||||
"id": 12365458895229,
|
||||
"unRead": false,
|
||||
"unReadNumber": 0,
|
||||
"noNotice": true,
|
||||
"img": "",
|
||||
"title": "腾讯科技",
|
||||
"content": "【腾讯科技】安全提醒",
|
||||
"chatList": [
|
||||
{
|
||||
"id": "uuid-008-h1",
|
||||
"time": "2026-03-13 22:30",
|
||||
"content": "<p>【腾讯科技】您的QQ账号在异地登录,登录地点:广州。如非本人操作请及时改密。</p>",
|
||||
"isMe": false,
|
||||
"simKa": 1
|
||||
}
|
||||
],
|
||||
"time": "2026-03-13 22:30:00"
|
||||
},
|
||||
{
|
||||
"id": 12365458895230,
|
||||
"unRead": true,
|
||||
"unReadNumber": 1,
|
||||
"noNotice": false,
|
||||
"img": "",
|
||||
"title": "菜鸟驿站",
|
||||
"content": "【菜鸟驿站】取件通知",
|
||||
"chatList": [
|
||||
{
|
||||
"id": "uuid-009-i1",
|
||||
"time": "2026-03-16 10:15",
|
||||
"content": "<p>【菜鸟驿站】您的中通包裹已到达。凭取件码 8-2-4002 领取,地址:博朗郡东门。</p>",
|
||||
"isMe": false,
|
||||
"simKa: 1"
|
||||
}
|
||||
],
|
||||
"time": "2026-03-16 10:15:00"
|
||||
},
|
||||
{
|
||||
"id": 12365458895231,
|
||||
"unRead": false,
|
||||
"unReadNumber": 0,
|
||||
"noNotice": false,
|
||||
"img": "",
|
||||
"title": "阿里云",
|
||||
"content": "【阿里云】资源包即将到期",
|
||||
"chatList": [
|
||||
{
|
||||
"id": "uuid-010-j1",
|
||||
"time": "2026-03-12 09:00",
|
||||
"content": "<p>【阿里云】尊敬的用户,您的OSS存储包将于7天后到期,请及时续费。</p>",
|
||||
"isMe": false,
|
||||
"simKa": 1
|
||||
},
|
||||
{
|
||||
"id": "uuid-010-j2",
|
||||
"time": "2026-03-15 15:00",
|
||||
"content": "<p>【阿里云】续费成功。您的OSS存储包有效期已延长至2027年03月。</p>",
|
||||
"isMe": false,
|
||||
"simKa": 1
|
||||
}
|
||||
],
|
||||
"time": "2026-03-15 15:00:00"
|
||||
}
|
||||
]
|
||||
|
|
@ -1,29 +1,86 @@
|
|||
<template>
|
||||
<view>
|
||||
<MessageNavBar :phone="data.phone" :isScroll="data.isScroll">
|
||||
<!-- <view v-for="index in 100">{{ index }}</view> -->
|
||||
<!-- <List :phone="data.phone"></List> -->
|
||||
<MessageList></MessageList>
|
||||
|
||||
<MessageNavBar :phone="data.phone" :isScroll="data.isScroll" @add="openAddPopup">
|
||||
<MessageList :phone="data.phone" :list="defaultList" @item-click="itemClick" @delete-item="deleteItem"
|
||||
@edit-item="editItem">
|
||||
</MessageList>
|
||||
</MessageNavBar>
|
||||
|
||||
<!-- 添加短信弹窗 -->
|
||||
<view v-if="showAddPopup" class="add-mask" @tap="closeAddPopup">
|
||||
<view class="add-popup" @tap.stop>
|
||||
<view class="add-header">{{ editingItem ? '编辑短信' : '新建短信' }}</view>
|
||||
<view class="add-body">
|
||||
<view class="add-row">
|
||||
<text class="add-label">头像URL:</text>
|
||||
<view class="image-box" style="width: 84rpx;height: 84rpx;" @tap="selectImage">
|
||||
<image v-if="addForm.img" class="image w100 h100" :src="addForm.img" mode="aspectFill">
|
||||
</image>
|
||||
<image v-else class="image w100 h100" src="/static/image/phone-message/add.png"
|
||||
mode="aspectFill">
|
||||
</image>
|
||||
</view>
|
||||
</view>
|
||||
<view class="add-row">
|
||||
<text class="add-label required">联系人</text>
|
||||
<input class="add-input" v-model="addForm.title" placeholder="请输入名称或号码" />
|
||||
</view>
|
||||
<view class="add-row between" v-if="data.phone == 'iphone'">
|
||||
<text class="add-label">取消通知</text>
|
||||
<switch :checked="addForm.noNotice" @change="addForm.noNotice = !addForm.noNotice" />
|
||||
</view>
|
||||
<view class="add-row between">
|
||||
<text class="add-label">是否未读</text>
|
||||
<switch :checked="addForm.unRead" @change="addForm.unRead = !addForm.unRead" />
|
||||
</view>
|
||||
<view class="add-row" v-if="addForm.unRead && data.phone == 'oppo' || data.phone == 'huawei'">
|
||||
<text class="add-label">未读数量</text>
|
||||
<input class="add-input" type="number" v-model="addForm.unReadNumber" placeholder="请输入未读数量" />
|
||||
</view>
|
||||
<view class="add-row" v-if="addForm.chatList.length == 0 && editingItem">
|
||||
<text class="add-label">消息时间</text>
|
||||
<view class="time-picker-group">
|
||||
<picker mode="date" :value="addForm.date" @change="onAddDateChange">
|
||||
<view class="time-picker-item">
|
||||
<text>{{ addForm.date || '选择日期' }}</text>
|
||||
</view>
|
||||
</picker>
|
||||
<picker mode="time" :value="addForm.timeOfDay" @change="onAddTimeChange">
|
||||
<view class="time-picker-item">
|
||||
<text>{{ addForm.timeOfDay || '选择时刻' }}</text>
|
||||
</view>
|
||||
</picker>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="add-footer">
|
||||
<view class="add-btn cancel" @tap="closeAddPopup">取消</view>
|
||||
<view class="add-btn confirm" @tap="confirmAdd">确定</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import MessageNavBar from '@/components/message/message-nav-bar.vue'
|
||||
import MessageList from '@/components/message/list.vue'
|
||||
import MessageNavBar from '@/components/message/list/message-nav-bar.vue'
|
||||
import MessageList from '@/components/message/list/list.vue'
|
||||
import defaultData from './defaultData.json'
|
||||
import {
|
||||
ref,
|
||||
reactive
|
||||
} from 'vue'
|
||||
import {
|
||||
onLoad,
|
||||
onShow,
|
||||
onPageScroll
|
||||
} from "@dcloudio/uni-app";
|
||||
import {
|
||||
stringUtil,
|
||||
util
|
||||
} from '@/utils/common.js';
|
||||
import List from '@/components/call-log/list/list.vue';
|
||||
|
||||
const defaultList = ref(defaultData)
|
||||
|
||||
const data = reactive({
|
||||
navBar: {
|
||||
|
|
@ -34,26 +91,27 @@ const data = reactive({
|
|||
isScroll: false
|
||||
})
|
||||
|
||||
const options = [{
|
||||
text: '取消',
|
||||
style: {
|
||||
backgroundColor: '#007aff'
|
||||
}
|
||||
}, {
|
||||
text: '确认',
|
||||
style: {
|
||||
backgroundColor: '#dd524d'
|
||||
}
|
||||
}]
|
||||
const STORAGE_KEY = 'message_list'
|
||||
|
||||
onLoad((options) => {
|
||||
if (options.phone) {
|
||||
data.phone = options.phone
|
||||
}
|
||||
|
||||
})
|
||||
|
||||
onShow(() => {
|
||||
// 优先从缓存加载列表
|
||||
try {
|
||||
const cached = uni.getStorageSync(STORAGE_KEY)
|
||||
if (cached) {
|
||||
defaultList.value = JSON.parse(cached)
|
||||
}
|
||||
} catch (e) { }
|
||||
})
|
||||
|
||||
|
||||
onPageScroll((e) => {
|
||||
console.log(e.scrollTop)
|
||||
if (e.scrollTop > 60) {
|
||||
data.isScroll = true
|
||||
} else {
|
||||
|
|
@ -61,6 +119,155 @@ onPageScroll((e) => {
|
|||
}
|
||||
})
|
||||
|
||||
/**
|
||||
* 删除元素
|
||||
* @param item
|
||||
*/
|
||||
const deleteItem = (item) => {
|
||||
uni.showModal({
|
||||
title: '提示',
|
||||
content: '确定要删除吗?',
|
||||
success: (res) => {
|
||||
if (res.confirm) {
|
||||
defaultList.value.splice(defaultList.value.findIndex(i => i.id === item.id), 1)
|
||||
uni.setStorageSync(STORAGE_KEY, JSON.stringify(defaultList.value))
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 选择图片
|
||||
*/
|
||||
const selectImage = () => {
|
||||
uni.chooseImage({
|
||||
count: 1,
|
||||
sizeType: ['original', 'compressed'],
|
||||
sourceType: ['album', 'camera'],
|
||||
success: (res) => {
|
||||
addForm.img = res.tempFilePaths[0]
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
const itemClick = (item) => {
|
||||
util.goPage(`/pages/message/chat-page/chat-page?phone=${data.phone}&id=${item.id}`)
|
||||
}
|
||||
|
||||
// ===== 添加短信弹窗 =====
|
||||
const showAddPopup = ref(false)
|
||||
const addForm = reactive({
|
||||
title: '',
|
||||
img: '',
|
||||
content: '',
|
||||
date: '',
|
||||
timeOfDay: ''
|
||||
})
|
||||
|
||||
// 弹窗模式:null=新增,有值=编辑中的条目
|
||||
const editingItem = ref(null)
|
||||
|
||||
const openAddPopup = () => {
|
||||
// 重置表单,默认时间为当前
|
||||
const now = new Date()
|
||||
const pad = v => String(v).padStart(2, '0')
|
||||
editingItem.value = null
|
||||
addForm.title = ''
|
||||
addForm.img = ''
|
||||
addForm.content = ''
|
||||
addForm.unRead = false
|
||||
addForm.unReadNumber = 1
|
||||
addForm.noNotice = false
|
||||
addForm.chatList = []
|
||||
addForm.date = `${now.getFullYear()}-${pad(now.getMonth() + 1)}-${pad(now.getDate())}`
|
||||
addForm.timeOfDay = `${pad(now.getHours())}:${pad(now.getMinutes())}`
|
||||
showAddPopup.value = true
|
||||
}
|
||||
|
||||
const closeAddPopup = () => {
|
||||
showAddPopup.value = false
|
||||
}
|
||||
|
||||
const onAddDateChange = (e) => {
|
||||
addForm.date = e.detail.value
|
||||
}
|
||||
|
||||
const onAddTimeChange = (e) => {
|
||||
addForm.timeOfDay = e.detail.value
|
||||
}
|
||||
|
||||
/**
|
||||
* 编辑元素:回填表单并打开弹窗
|
||||
*/
|
||||
const editItem = (item) => {
|
||||
editingItem.value = item
|
||||
const lastMsg = item.chatList && item.chatList.length ? item.chatList[item.chatList.length - 1] : null
|
||||
const timeStr = lastMsg ? lastMsg.time : (item.time || '')
|
||||
const parts = timeStr.split(' ')
|
||||
addForm.title = item.title || ''
|
||||
addForm.img = item.img || ''
|
||||
addForm.content = ''
|
||||
addForm.unRead = !!item.unRead
|
||||
addForm.unReadNumber = item.unReadNumber || 0
|
||||
addForm.date = parts[0] || ''
|
||||
addForm.timeOfDay = parts[1] || ''
|
||||
addForm.chatList = item.chatList || []
|
||||
showAddPopup.value = true
|
||||
}
|
||||
|
||||
const confirmAdd = () => {
|
||||
if (!addForm.title.trim()) {
|
||||
uni.showToast({ title: '请输入联系人名称', icon: 'none' })
|
||||
return
|
||||
}
|
||||
console.log(addForm)
|
||||
const time = `${addForm.date} ${addForm.timeOfDay}`.trim()
|
||||
|
||||
if (editingItem.value) {
|
||||
// ===== 编辑模式:更新已有条目 =====
|
||||
const idx = defaultList.value.findIndex(i => i.id === editingItem.value.id)
|
||||
if (idx > -1) {
|
||||
defaultList.value[idx].title = addForm.title.trim()
|
||||
defaultList.value[idx].img = addForm.img.trim()
|
||||
defaultList.value[idx].unRead = addForm.unRead
|
||||
defaultList.value[idx].unReadNumber = addForm.unReadNumber
|
||||
defaultList.value[idx].noNotice = addForm.noNotice
|
||||
defaultList.value[idx].time = time
|
||||
}
|
||||
} else {
|
||||
// ===== 新增模式 =====
|
||||
const newItem = {
|
||||
id: Date.now(),
|
||||
unRead: addForm.unRead,
|
||||
unReadNumber: addForm.unReadNumber,
|
||||
noNotice: addForm.noNotice,
|
||||
img: addForm.img.trim(),
|
||||
title: addForm.title.trim(),
|
||||
chatList: addForm.content.trim() ? [
|
||||
{
|
||||
id: stringUtil.uuid(),
|
||||
time: time,
|
||||
content: `<p>${addForm.content.trim()}</p>`,
|
||||
isMe: false
|
||||
}
|
||||
] : [],
|
||||
time: time
|
||||
}
|
||||
defaultList.value.unshift(newItem)
|
||||
}
|
||||
|
||||
// 按最新消息时间降序排列
|
||||
defaultList.value.sort((a, b) => {
|
||||
const timeA = new Date(((a.chatList && a.chatList.length ? a.chatList[a.chatList.length - 1].time : null) || a.time || '').replace(/-/g, '/')).getTime()
|
||||
const timeB = new Date(((b.chatList && b.chatList.length ? b.chatList[b.chatList.length - 1].time : null) || b.time || '').replace(/-/g, '/')).getTime()
|
||||
return timeB - timeA
|
||||
})
|
||||
try {
|
||||
uni.setStorageSync(STORAGE_KEY, JSON.stringify(defaultList.value))
|
||||
} catch (e) { }
|
||||
closeAddPopup()
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
<style>
|
||||
|
|
@ -114,4 +321,126 @@ page {
|
|||
margin-right: 30rpx;
|
||||
}
|
||||
}
|
||||
|
||||
/* ===== 添加短信弹窗 ===== */
|
||||
.add-mask {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
background-color: rgba(0, 0, 0, 0.5);
|
||||
z-index: 999;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
width: 100vw;
|
||||
height: 100vh;
|
||||
}
|
||||
|
||||
.add-popup {
|
||||
width: 88%;
|
||||
background-color: #ffffff;
|
||||
border-radius: 24rpx;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.add-header {
|
||||
padding: 36rpx 40rpx 20rpx;
|
||||
font-size: 34rpx;
|
||||
font-weight: bold;
|
||||
color: #1A1A1A;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.add-body {
|
||||
padding: 8rpx 0;
|
||||
}
|
||||
|
||||
.add-row {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 20rpx 32rpx;
|
||||
gap: 16rpx;
|
||||
}
|
||||
|
||||
.between {
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.required {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.required::before {
|
||||
position: absolute;
|
||||
left: -10px;
|
||||
content: '*';
|
||||
top: 0;
|
||||
color: #EA0000;
|
||||
}
|
||||
|
||||
.add-label {
|
||||
font-size: 28rpx;
|
||||
color: #1A1A1A;
|
||||
width: 150rpx;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.add-input {
|
||||
flex: 1;
|
||||
height: 70rpx;
|
||||
background-color: #F6F6F6;
|
||||
border-radius: 14rpx;
|
||||
padding: 0 20rpx;
|
||||
font-size: 28rpx;
|
||||
color: #1A1A1A;
|
||||
|
||||
::v-deep .uni-input {
|
||||
color: #aaaaaa;
|
||||
}
|
||||
}
|
||||
|
||||
.add-footer {
|
||||
padding: 32rpx 24rpx;
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.add-btn {
|
||||
flex: 1;
|
||||
height: 90rpx;
|
||||
line-height: 90rpx;
|
||||
text-align: center;
|
||||
font-size: 32rpx;
|
||||
background-color: #F1F1F1;
|
||||
margin: 0 16rpx;
|
||||
border-radius: 12rpx;
|
||||
}
|
||||
|
||||
.add-btn.cancel {
|
||||
color: #767676;
|
||||
}
|
||||
|
||||
.add-btn.confirm {
|
||||
color: #fff;
|
||||
background-color: #1777FF;
|
||||
}
|
||||
|
||||
.time-picker-group {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
gap: 16rpx;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.time-picker-item {
|
||||
flex: 1;
|
||||
height: 70rpx;
|
||||
line-height: 70rpx;
|
||||
background-color: #F8F8F8;
|
||||
border-radius: 8rpx;
|
||||
padding: 0 20rpx;
|
||||
font-size: 28rpx;
|
||||
color: #333333;
|
||||
text-align: center;
|
||||
}
|
||||
</style>
|
||||
|
|
|
|||
|
|
@ -8,7 +8,8 @@
|
|||
<view class="button-container">
|
||||
<button class="btn-save-image" @click="saveImage">保存图片</button>
|
||||
</view>
|
||||
<l-painter isCanvasToTempFilePath @success="successImage" @progress="progress" hidden
|
||||
<view style="width: 0px;height: 0;overflow: hidden;" v-if="data.shuaxing">
|
||||
<l-painter isCanvasToTempFilePath @success="successImage" @progress="progress"
|
||||
:css="`width:${data.width}px;height:${data.height }px;background-color:#fff;`">
|
||||
<l-painter-view
|
||||
:css="`margin-top:109px;margin-left:75px;position: relative;width:666px;height:406px;background-image: url('/static/image/other/card/cardBGImg.png');background-size:6660px 406px;`">
|
||||
|
|
@ -19,20 +20,20 @@
|
|||
<l-painter-view :css="`position: absolute;left:122px;top:100px;`">
|
||||
<l-painter-text :css="data.textCss2+data.textCssLeft" :text="data.form.gender" />
|
||||
</l-painter-view>
|
||||
<l-painter-view :css="`position: absolute;left:260px;top:100px;`">
|
||||
<l-painter-view :css="`position: absolute;left:262px;top:100px;`">
|
||||
<l-painter-text :css="data.textCss2+data.textCssLeft" :text="data.form.nation" />
|
||||
</l-painter-view>
|
||||
<l-painter-view :css="`position: absolute;left:122px;top:148px;`">
|
||||
<l-painter-view :css="`position: absolute;left:122px;top:150px;`">
|
||||
<l-painter-text v-for="(item,index) in data.form.year.toString()" :css="data.textCss3+data.textCssLeft"
|
||||
:text="item" :key="index" />
|
||||
</l-painter-view>
|
||||
<l-painter-view :css="`position: absolute;left:190px;top:148px;`">
|
||||
<l-painter-view :css="`position: absolute;left:190px;top:150px;`">
|
||||
<l-painter-text :css="data.textCss2+data.textCssCenter" :text="data.form.month.toString()" />
|
||||
</l-painter-view>
|
||||
<l-painter-view :css="`position: absolute;left:250px;top:148px;`">
|
||||
<l-painter-view :css="`position: absolute;left:250px;top:150px;`">
|
||||
<l-painter-text :css="data.textCss2+data.textCssCenter" :text="data.form.day.toString()" />
|
||||
</l-painter-view>
|
||||
<l-painter-view :css="`position: absolute;left:122px;top:196px;width:274px;`">
|
||||
<l-painter-view :css="`position: absolute;left:122px;top:198px;width:274px;`">
|
||||
<l-painter-text v-for="(item,index) in data.form.address" :css="data.textCss3+data.textCssLeft" :text="item"
|
||||
:key="index" />
|
||||
</l-painter-view>
|
||||
|
|
@ -50,6 +51,8 @@
|
|||
</l-painter-view>
|
||||
</l-painter-view>
|
||||
</l-painter>
|
||||
</view>
|
||||
|
||||
|
||||
<!-- 编辑弹窗 -->
|
||||
<view v-if="showEditPopup" class="popup-overlay">
|
||||
|
|
@ -120,6 +123,7 @@
|
|||
import {
|
||||
onLoad,
|
||||
onShow,
|
||||
onUnload,
|
||||
onReady,
|
||||
onPullDownRefresh,
|
||||
onReachBottom
|
||||
|
|
@ -129,6 +133,7 @@
|
|||
proxy
|
||||
} = getCurrentInstance();
|
||||
const data = reactive({
|
||||
shuaxing:true,
|
||||
navbar: {
|
||||
title: "身份证",
|
||||
bgColor: '#EDEDED',
|
||||
|
|
@ -137,20 +142,20 @@
|
|||
height: 600,
|
||||
code: '',
|
||||
form: {
|
||||
name: '李某',
|
||||
name: '某某',
|
||||
gender: '男',
|
||||
nation: '汉',
|
||||
year: 2025,
|
||||
month: 2,
|
||||
year: 2000,
|
||||
month: 1,
|
||||
day: 1,
|
||||
address: '翻斗大街翻斗花园二号楼1001室',
|
||||
idNumber: '500000000000000000',
|
||||
idNumber: '110000200001010000',
|
||||
photo: '',
|
||||
},
|
||||
textCss: 'font-family: "SimHei", "Microsoft YaHei", sans-serif;width:100px;color:#3D3D3D;font-size:24px; mix-blend-mode: overlay;',
|
||||
textCss2: 'font-family: "SimHei", "Microsoft YaHei", sans-serif;width:100px;text-align: left;color:#3D3D3D;font-size:22px;mix-blend-mode: overlay;',
|
||||
textCss3: 'word-spacing: 10px;letter-spacing: 10px;margin-right:2px;font-family: "SimHei", "Microsoft YaHei", sans-serif;text-align: left;color:#3D3D3D;font-size:22px;mix-blend-mode: overlay;',
|
||||
textCss4: 'word-spacing: 10px;margin-right:3px;letter-spacing: 10px;font-family: "card", "Microsoft YaHei", sans-serif;text-align: left;color:#1a1a1a;font-size:28px;mix-blend-mode: overlay;display:flex;',
|
||||
textCss: 'word-spacing: 20px;font-family: "SimHei", "Microsoft YaHei", sans-serif;width:100px;color:#3D3D3D;font-size:24px; mix-blend-mode: overlay;',
|
||||
textCss2: 'word-spacing: 20px;font-family: "SimHei", "Microsoft YaHei", sans-serif;width:100px;text-align: left;color:#3D3D3D;font-size:22px;mix-blend-mode: overlay;',
|
||||
textCss3: 'word-spacing: 20px;letter-spacing: 10px;margin-right:2px;font-family: "SimHei", "Microsoft YaHei", sans-serif;text-align: left;color:#3D3D3D;font-size:22px;mix-blend-mode: overlay;',
|
||||
textCss4: 'word-spacing: 20px;margin-right:3px;letter-spacing: 10px;font-family: "card", "Microsoft YaHei", sans-serif;text-align: left;color:#1a1a1a;font-size:28px;mix-blend-mode: overlay;display:flex;',
|
||||
|
||||
textCssLeft: 'text-align: left;',
|
||||
textCssCenter: 'text-align: center;'
|
||||
|
|
@ -159,25 +164,50 @@
|
|||
// 弹窗相关
|
||||
const showEditPopup = ref(false);
|
||||
const editForm = ref({});
|
||||
//
|
||||
function successImage(e) {
|
||||
console.log(e)
|
||||
data.code = e
|
||||
}
|
||||
|
||||
function progress(e) {
|
||||
console.log(e)
|
||||
if (e < 0.03) {
|
||||
onLoad((option) => {
|
||||
uni.showLoading({
|
||||
title: "生成中"
|
||||
})
|
||||
// 进入身份证页面埋点
|
||||
proxy.$apiUserEvent('all', {
|
||||
type: 'event',
|
||||
key: 'idcard',
|
||||
prefix: '.uni.other.',
|
||||
value: "身份证"
|
||||
})
|
||||
let formdata=uni.getStorageSync("cardForm")
|
||||
if(formdata){
|
||||
data.form=formdata
|
||||
}
|
||||
if (e == 1) {
|
||||
setTimeout(() => {
|
||||
uni.hideLoading()
|
||||
}, 1000)
|
||||
uni.$on("editFormPhoto",(info)=>{
|
||||
data.code = ""
|
||||
editForm.value.photo = info;
|
||||
data.form.photo = info;
|
||||
})
|
||||
})
|
||||
onUnload(() => {
|
||||
uni.$off('editFormPhoto')
|
||||
})
|
||||
onReady(() => {
|
||||
|
||||
})
|
||||
onShow(() => {})
|
||||
onPullDownRefresh(() => {
|
||||
setTimeout(() => {
|
||||
uni.stopPullDownRefresh();
|
||||
}, 1000);
|
||||
})
|
||||
onReachBottom(() => {
|
||||
|
||||
})
|
||||
function successImage(e) {
|
||||
data.code = e
|
||||
uni.hideLoading()
|
||||
}
|
||||
|
||||
function progress(e) {
|
||||
// console.log(e)
|
||||
|
||||
}
|
||||
// 打开编辑弹窗
|
||||
function edit() {
|
||||
|
|
@ -195,8 +225,16 @@
|
|||
|
||||
// 保存编辑表单
|
||||
function saveEditForm() {
|
||||
uni.showLoading({
|
||||
title: "生成中"
|
||||
})
|
||||
data.shuaxing=false
|
||||
// 将编辑后的数据复制回原始表单
|
||||
data.form = JSON.parse(JSON.stringify(editForm.value));
|
||||
data.form = editForm.value
|
||||
data.form.photo=editForm.value.photo
|
||||
setTimeout(()=>{
|
||||
data.shuaxing=true
|
||||
},100)
|
||||
uni.setStorageSync("cardForm",data.form)
|
||||
// 关闭弹窗
|
||||
showEditPopup.value = false;
|
||||
|
|
@ -208,32 +246,13 @@
|
|||
count: 1,
|
||||
sizeType: ['original', 'compressed'],
|
||||
sourceType: ['album', 'camera'],
|
||||
crop:{
|
||||
width:'196px',
|
||||
height:"230px"
|
||||
},
|
||||
// crop:{
|
||||
// width:'196px',
|
||||
// height:"230px"
|
||||
// },
|
||||
success: (res) => {
|
||||
uni.showLoading({
|
||||
title:"抠图中"
|
||||
})
|
||||
convertLocalImageToFile(res.tempFilePaths[0]).then(file => {
|
||||
proxy.$imageUpload(file.split(',', 2)[1]).then(resimage=>{
|
||||
console.log(resimage);
|
||||
console.log(resimage.data);
|
||||
editForm.value.photo = decodeURI(resimage.data);
|
||||
uni.hideLoading()
|
||||
}).catch(err=>{
|
||||
uni.hideLoading()
|
||||
console.log(err.data.message)
|
||||
uni.showToast({
|
||||
icon:"none",
|
||||
title:err.data.message
|
||||
})
|
||||
})
|
||||
|
||||
}).catch(err=>{
|
||||
|
||||
uni.hideLoading()
|
||||
uni.navigateTo({
|
||||
url:'/pages/other/qf-image/qf-image?src='+res.tempFilePaths[0]
|
||||
})
|
||||
},
|
||||
fail: (err) => {
|
||||
|
|
@ -419,31 +438,7 @@
|
|||
}
|
||||
}
|
||||
|
||||
onLoad((option) => {
|
||||
// 进入身份证页面埋点
|
||||
proxy.$apiUserEvent('all', {
|
||||
type: 'event',
|
||||
key: 'idcard',
|
||||
prefix: '.uni.other.',
|
||||
value: "身份证"
|
||||
})
|
||||
let formdata=uni.getStorageSync("cardForm")
|
||||
if(formdata){
|
||||
data.form=formdata
|
||||
}
|
||||
})
|
||||
onReady(() => {
|
||||
|
||||
})
|
||||
onShow(() => {})
|
||||
onPullDownRefresh(() => {
|
||||
setTimeout(() => {
|
||||
uni.stopPullDownRefresh();
|
||||
}, 1000);
|
||||
})
|
||||
onReachBottom(() => {
|
||||
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
|
|
@ -573,8 +568,8 @@
|
|||
.upload-btn {
|
||||
width: 120rpx;
|
||||
height: 160rpx;
|
||||
border: 1rpx dashed #ddd;
|
||||
border-radius: 8rpx;
|
||||
border: 1rpx dashed #ddd;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
|
|
@ -588,10 +583,11 @@
|
|||
}
|
||||
|
||||
.upload-image {
|
||||
width: 120rpx;
|
||||
height: 160rpx;
|
||||
border-radius: 8rpx;
|
||||
width: 98rpx;
|
||||
height: 115rpx;
|
||||
border-radius: 1rpx;
|
||||
object-fit: cover;
|
||||
border: 1px dashed #ddd;
|
||||
}
|
||||
|
||||
.popup-footer {
|
||||
|
|
|
|||
|
|
@ -1,20 +1,92 @@
|
|||
<template>
|
||||
<view>
|
||||
<qf-image-cropper :width="196" :height="230" :radius="0" @crop="handleCrop" :reverseRotatable="true"></qf-image-cropper>
|
||||
<qf-image-cropper :src="data.src" :width="196" :height="230" :radius="0" @crop="handleCrop"
|
||||
:reverseRotatable="true"></qf-image-cropper>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
methods: {
|
||||
handleCrop(e) {
|
||||
uni.saveFile({
|
||||
tempFilePath: e.tempFilePath,
|
||||
success: function(res) {
|
||||
res.avatar = res.savedFilePath
|
||||
<script setup>
|
||||
import {
|
||||
ref,
|
||||
reactive,
|
||||
watch,
|
||||
nextTick,
|
||||
getCurrentInstance
|
||||
} from "vue";
|
||||
import {
|
||||
onLoad,
|
||||
onShow,
|
||||
onReady,
|
||||
onPullDownRefresh,
|
||||
onReachBottom
|
||||
} from "@dcloudio/uni-app";
|
||||
const {
|
||||
appContext,
|
||||
proxy
|
||||
} = getCurrentInstance();
|
||||
const data = reactive({
|
||||
src: ""
|
||||
})
|
||||
onLoad((option) => {
|
||||
console.log(option)
|
||||
data.src = option.src
|
||||
})
|
||||
|
||||
function handleCrop(e) {
|
||||
uni.showLoading({
|
||||
title:"抠图中"
|
||||
})
|
||||
convertLocalImageToFile(e.tempFilePath).then(file => {
|
||||
proxy.$imageUpload(file.split(',', 2)[1]).then(resimage => {
|
||||
uni.hideLoading()
|
||||
// editForm.value.photo = decodeURI(resimage.data);
|
||||
uni.$emit("editFormPhoto", decodeURI(resimage.data))
|
||||
uni.navigateBack()
|
||||
}).catch(err => {
|
||||
uni.hideLoading()
|
||||
console.log(err.data.message)
|
||||
uni.showToast({
|
||||
icon: "none",
|
||||
title: "图片不是人像或者过大"
|
||||
})
|
||||
})
|
||||
|
||||
}).catch(err => {
|
||||
uni.hideLoading()
|
||||
})
|
||||
// uni.saveFile({
|
||||
// tempFilePath: e.tempFilePath,
|
||||
// success: function(res) {
|
||||
// console.log(res)
|
||||
// // res.avatar = res.savedFilePath
|
||||
// }
|
||||
// });
|
||||
}
|
||||
/**
|
||||
* 将本地图片路径通过 Canvas 转换为 File 对象
|
||||
* @param {string} localPath - 本地图片路径(如从 uni.chooseImage 获取的 tempFilePath)
|
||||
* @param {Object} options - 可选参数
|
||||
* @param {string} options.format - 输出格式 'image/png' 或 'image/jpeg',默认 'image/png'
|
||||
* @param {number} options.quality - 图片质量(仅 jpeg 有效),0~1,默认 0.92
|
||||
* @param {number} options.maxWidth - 最大宽度(等比缩放),不填则使用原图尺寸
|
||||
* @param {number} options.maxHeight - 最大高度(等比缩放),不填则使用原图尺寸
|
||||
* @returns {Promise<File>} 返回一个 Promise,resolve 为 File 对象
|
||||
*/
|
||||
function convertLocalImageToFile(localPath) {
|
||||
return new Promise((resolve, reject) => {
|
||||
// 1. 读取本地图片为 Base64(避免 Image 对象直接加载本地路径的兼容问题)
|
||||
plus.io.resolveLocalFileSystemURL(localPath, (entry) => {
|
||||
entry.file((file) => {
|
||||
const reader = new plus.io.FileReader();
|
||||
reader.onload = (e) => {
|
||||
const base64Data = e.target.result; // 格式如 data:image/jpeg;base64,/9j/...
|
||||
resolve(e.target.result)
|
||||
};
|
||||
reader.onerror = (err) => reject(err);
|
||||
reader.readAsDataURL(file); // 读取为 DataURL
|
||||
}, reject);
|
||||
}, reject);
|
||||
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
|
@ -525,9 +525,7 @@ const showFristAndLastNumber = (str) => {
|
|||
|
||||
.section_4 {
|
||||
filter: drop-shadow(0rpx 10rpx 10rpx #00000008);
|
||||
background-image: url('https://ide.code.fun/api/image?token=69ae28bb97ce8400118178f6&name=ce4aca9097cf1d57763cc647764b458d.png');
|
||||
background-size: 100% 100%;
|
||||
background-repeat: no-repeat;
|
||||
background-color: #fff;
|
||||
|
||||
.group_5 {
|
||||
padding-bottom: 32rpx;
|
||||
|
|
|
|||
|
|
@ -341,7 +341,7 @@ page {
|
|||
border-radius: 25rpx;
|
||||
width: 106rpx;
|
||||
height: 50rpx;
|
||||
border: solid 0.5px #28c2dd;
|
||||
border: solid 1rpx #28c2dd;
|
||||
|
||||
.text_6 {
|
||||
color: #28c2dd;
|
||||
|
|
|
|||
|
Before Width: | Height: | Size: 1.6 KiB After Width: | Height: | Size: 3.1 KiB |
|
After Width: | Height: | Size: 8.6 KiB |
|
Before Width: | Height: | Size: 214 KiB After Width: | Height: | Size: 216 KiB |
|
After Width: | Height: | Size: 1.7 KiB |
|
After Width: | Height: | Size: 930 B |
|
After Width: | Height: | Size: 1.2 KiB |
|
After Width: | Height: | Size: 1.5 KiB |
|
After Width: | Height: | Size: 1.8 KiB |
|
After Width: | Height: | Size: 369 B |
|
After Width: | Height: | Size: 534 B |
|
After Width: | Height: | Size: 1.3 KiB |
|
After Width: | Height: | Size: 1.9 KiB |
|
After Width: | Height: | Size: 1.9 KiB |
|
After Width: | Height: | Size: 524 B |
|
After Width: | Height: | Size: 1.8 KiB |
|
After Width: | Height: | Size: 1.5 KiB |
|
After Width: | Height: | Size: 1.6 KiB |
|
After Width: | Height: | Size: 1.0 KiB |
|
After Width: | Height: | Size: 1.0 KiB |
|
After Width: | Height: | Size: 1.6 KiB |
|
After Width: | Height: | Size: 1.1 KiB |
|
After Width: | Height: | Size: 818 B |
|
After Width: | Height: | Size: 832 B |
|
After Width: | Height: | Size: 821 B |
|
After Width: | Height: | Size: 1.3 KiB |
|
After Width: | Height: | Size: 983 B |
|
After Width: | Height: | Size: 1.2 KiB |
|
After Width: | Height: | Size: 1.7 KiB |
|
After Width: | Height: | Size: 205 B |
|
After Width: | Height: | Size: 1.8 KiB |
|
After Width: | Height: | Size: 1.6 KiB |
|
After Width: | Height: | Size: 1.3 KiB |
|
After Width: | Height: | Size: 983 B |
|
After Width: | Height: | Size: 1.3 KiB |
|
After Width: | Height: | Size: 1.6 KiB |
|
After Width: | Height: | Size: 629 B |
|
After Width: | Height: | Size: 915 B |
|
After Width: | Height: | Size: 205 B |
|
After Width: | Height: | Size: 3.7 KiB |
|
After Width: | Height: | Size: 2.3 KiB |
|
After Width: | Height: | Size: 1.0 KiB |
|
After Width: | Height: | Size: 939 B |
|
After Width: | Height: | Size: 1.9 KiB |
|
|
@ -104,6 +104,64 @@ export const dateUtil = {
|
|||
const day = String(date.getDate()).padStart(2, '0');
|
||||
return `${month}-${day} ${timeStr}`;
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* 仿支付宝消息列表时间格式化
|
||||
* 今天: 原样不显示 (或者按需可显示 HH:mm) 但根据你的图示只有 昨天/星期/日期
|
||||
* 昨天: 返回 "昨天"
|
||||
* 近一周内且不是今天昨天: 返回 "星期X"
|
||||
* 超过一周: 按照 format 返回 (或根据 hideCurrentYear 决定当年是否隐藏年份)
|
||||
* @param {Date|string|number} date - 日期对象或时间戳
|
||||
* @param {boolean} hideCurrentYear - 是否在当年隐藏年份,默认为 true
|
||||
* @param {string} format - 自定义日期格式,默认为 'YYYY/M/D' (支持 YYYY, MM, M, DD, D 等)
|
||||
* @returns {string} 格式化后的时间字符串
|
||||
*/
|
||||
formatMessageTime(date, hideCurrentYear = false, format = 'YYYY/M/D') {
|
||||
if (!date) return '';
|
||||
|
||||
if (typeof date === 'string' || typeof date === 'number') {
|
||||
// 兼容 iOS 的 YYYY-MM-DD 解析
|
||||
if (typeof date === 'string') {
|
||||
date = date.replace(/-/g, '/');
|
||||
}
|
||||
date = new Date(date);
|
||||
}
|
||||
|
||||
const now = new Date();
|
||||
// 将时分秒抹零,只取日期部分进行比对
|
||||
const today = new Date(now.getFullYear(), now.getMonth(), now.getDate());
|
||||
const targetDate = new Date(date.getFullYear(), date.getMonth(), date.getDate());
|
||||
|
||||
const diffDays = Math.floor((today.getTime() - targetDate.getTime()) / (1000 * 60 * 60 * 24));
|
||||
|
||||
if (diffDays === 0) {
|
||||
// 如果需要显示今天的具体时间可返回: `${String(date.getHours()).padStart(2, '0')}:${String(date.getMinutes()).padStart(2, '0')}`
|
||||
return `${String(date.getHours()).padStart(2, '0')}:${String(date.getMinutes()).padStart(2, '0')}`;
|
||||
} else if (diffDays === 1) {
|
||||
return '昨天';
|
||||
} else if (diffDays > 1 && diffDays < 7) {
|
||||
const weekDays = ['星期日', '星期一', '星期二', '星期三', '星期四', '星期五', '星期六'];
|
||||
return weekDays[date.getDay()];
|
||||
} else {
|
||||
// 超过7天
|
||||
let resultFormat = format;
|
||||
if (hideCurrentYear && targetDate.getFullYear() === now.getFullYear()) {
|
||||
// 智能去除年份及紧跟的连接符、汉字或变宽空格(例如: "YYYY-", "YYYY/", "YYYY年")
|
||||
resultFormat = resultFormat.replace(/YYYY[-/年\s]*/g, '');
|
||||
}
|
||||
|
||||
const year = date.getFullYear();
|
||||
const month = date.getMonth() + 1;
|
||||
const day = date.getDate();
|
||||
|
||||
return resultFormat
|
||||
.replace(/YYYY/g, year)
|
||||
.replace(/MM/g, String(month).padStart(2, '0'))
|
||||
.replace(/M/g, month)
|
||||
.replace(/DD/g, String(day).padStart(2, '0'))
|
||||
.replace(/D/g, day);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
|||