完成支付宝短信

This commit is contained in:
tangxinyue 2026-03-18 10:12:20 +08:00
parent 261918d871
commit 2c7da360f4
10 changed files with 426 additions and 45 deletions

View File

@ -9,7 +9,7 @@
<view class="left flex-align-center"> <view class="left flex-align-center">
<image @click="util.goBack()" :src="`/static/image/phone-message/${phone}/back.png`"> <image @click="util.goBack()" :src="`/static/image/phone-message/${phone}/back.png`">
</image> </image>
<view v-if="phone == 'iphone'" class="number-box">256</view> <view v-if="phone == 'iphone' && number > 0" class="number-box">{{ number }}</view>
</view> </view>
<view class="center"> <view class="center">
<image v-if="phone == 'iphone' || phone == 'huawei'" class="img shrink-0" <image v-if="phone == 'iphone' || phone == 'huawei'" class="img shrink-0"
@ -49,7 +49,8 @@
<slot></slot> <slot></slot>
</scroll-view> </scroll-view>
</view> </view>
<view class="bottom-box fixed-bottom-box" :style="{ bottom: data.keyboardHeight + 'px' }"> <view class="bottom-box fixed-bottom-box" :class="{ 'safe-area': $system == 'iOS' && data.keyboardHeight == 0 }"
:style="{ bottom: data.keyboardHeight + 'px' }">
<slot name="bottom"> <slot name="bottom">
<view class="bottom-container flex-align-center" v-show="!sortMode"> <view class="bottom-container flex-align-center" v-show="!sortMode">
<image v-if="phone != 'huawei' && phone != 'vivo'" class="add-img shrink-0" <image v-if="phone != 'huawei' && phone != 'vivo'" class="add-img shrink-0"
@ -63,9 +64,9 @@
src="/static/image/phone-message/huawei/down.png"></image> src="/static/image/phone-message/huawei/down.png"></image>
<!-- <input class="input flex-1" :placeholder="showInfo.placeholder" v-model="content" <!-- <input class="input flex-1" :placeholder="showInfo.placeholder" v-model="content"
@input="onInput"></input> --> @input="onInput"></input> -->
<textarea class="input flex-1" :adjust-position="false" fixed auto-height <textarea class="input flex-1" :adjust-position="false" fixed auto-height show-confirm-bar
:placeholder="showInfo.placeholder" v-model="content" @input="onInput" @focus="onFocus" auto-blur :placeholder="showInfo.placeholder" v-model="content" @input="onInput"
@blur="onBlur"></textarea> @focus="onFocus" @blur="onBlur"></textarea>
<!-- <editor class="input flex-1" :placeholder="showInfo.placeholder"></editor> --> <!-- <editor class="input flex-1" :placeholder="showInfo.placeholder"></editor> -->
<image v-if="phone == 'iphone' && !isSend" class="right-icon" <image v-if="phone == 'iphone' && !isSend" class="right-icon"
src="/static/image/phone-message/iphone/mic.png"></image> src="/static/image/phone-message/iphone/mic.png"></image>
@ -126,6 +127,10 @@ const props = defineProps({
sortMode: { sortMode: {
type: Boolean, type: Boolean,
default: false default: false
},
number: {
type: Number,
default: 0
} }
}) })
@ -296,6 +301,7 @@ onMounted(() => {
left: 0; left: 0;
} }
.fixed-top-box { .fixed-top-box {
position: fixed; position: fixed;
width: 100%; width: 100%;
@ -344,6 +350,7 @@ onMounted(() => {
.img { .img {
width: 96rpx; width: 96rpx;
height: 96rpx; height: 96rpx;
border-radius: 50%;
} }
} }
@ -357,6 +364,10 @@ onMounted(() => {
height: 20rpx; height: 20rpx;
.title { .title {
text-align: center;
max-width: 400rpx;
overflow: hidden;
text-overflow: ellipsis;
font-size: 20rpx; font-size: 20rpx;
color: #1A1A1A; color: #1A1A1A;
left: 20rpx; left: 20rpx;
@ -370,8 +381,19 @@ onMounted(() => {
} }
.bottom-box {
padding-bottom: 10rpx;
}
.safe-area {
padding-bottom: calc(10rpx + constant(safe-area-inset-bottom));
padding-bottom: calc(10rpx + env(safe-area-inset-bottom));
}
.bottom-container { .bottom-container {
padding: 10rpx 28rpx; padding: 10rpx 28rpx 0;
.add-img { .add-img {
width: 66rpx; width: 66rpx;
@ -431,7 +453,7 @@ onMounted(() => {
.top-box { .top-box {
.top-container { .top-container {
padding: 0 52rpx; padding: 0 20rpx 0 52rpx;
background-color: #F7F7F7; background-color: #F7F7F7;
height: 88rpx; height: 88rpx;
@ -456,10 +478,21 @@ onMounted(() => {
} }
.center { .center {
flex: 1;
display: flex;
align-items: center;
.title { .title {
flex: 1;
display: block;
font-size: 36rpx; font-size: 36rpx;
color: #1A1A1A; color: #1A1A1A;
font-weight: 500; font-weight: 500;
width: 20px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
margin-right: 10rpx;
} }
} }
@ -488,8 +521,19 @@ onMounted(() => {
} }
.bottom-box { .bottom-box {
padding-bottom: 20rpx;
}
.safe-area {
padding-bottom: calc(20rpx + constant(safe-area-inset-bottom));
padding-bottom: calc(20rpx + env(safe-area-inset-bottom));
}
.bottom-box {
.bottom-container { .bottom-container {
padding: 20rpx 28rpx; padding: 20rpx 28rpx 0;
.add-img { .add-img {
width: 72rpx; width: 72rpx;
@ -573,6 +617,7 @@ onMounted(() => {
.center { .center {
flex: 1; flex: 1;
width: 20%;
height: 100%; height: 100%;
margin: 0 18rpx; margin: 0 18rpx;
display: flex; display: flex;
@ -580,10 +625,15 @@ onMounted(() => {
justify-content: center; justify-content: center;
.title { .title {
width: 100%;
font-size: 36rpx; font-size: 36rpx;
color: #1A1A1A; color: #1A1A1A;
font-weight: 500; font-weight: 500;
line-height: 36rpx; line-height: 36rpx;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
margin-right: 10rpx;
} }
.second-text { .second-text {
@ -617,11 +667,21 @@ onMounted(() => {
} }
.bottom-box {
padding-bottom: 10rpx;
}
.safe-area {
padding-bottom: calc(10rpx + constant(safe-area-inset-bottom));
padding-bottom: calc(10rpx + env(safe-area-inset-bottom));
}
.bottom-box { .bottom-box {
background-color: #FAFAFA; background-color: #FAFAFA;
.bottom-container { .bottom-container {
padding: 16rpx 32rpx 50rpx; padding: 16rpx 32rpx 0;
.add-img { .add-img {
width: 84rpx; width: 84rpx;
@ -704,16 +764,19 @@ onMounted(() => {
.center { .center {
flex: 1; flex: 1;
width: 20%;
height: 100%; height: 100%;
margin: 0 16rpx; margin: 0 16rpx;
display: flex; display: flex;
align-items: center; align-items: center;
overflow: hidden;
.img { .img {
width: 72rpx; width: 72rpx;
height: 72rpx; height: 72rpx;
flex-shrink: 0; flex-shrink: 0;
margin-right: 18rpx; margin-right: 18rpx;
border-radius: 50%;
} }
.title { .title {
@ -721,6 +784,10 @@ onMounted(() => {
color: #1A1A1A; color: #1A1A1A;
font-weight: 500; font-weight: 500;
line-height: 36rpx; line-height: 36rpx;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
margin-right: 10rpx;
} }
.second-text { .second-text {
@ -754,11 +821,20 @@ onMounted(() => {
} }
.bottom-box {
padding-bottom: 12rpx;
}
.safe-area {
padding-bottom: calc(12rpx + constant(safe-area-inset-bottom));
padding-bottom: calc(12rpx + env(safe-area-inset-bottom));
}
.bottom-box { .bottom-box {
background-color: #FFFFFF; background-color: #FFFFFF;
.bottom-container { .bottom-container {
padding: 12rpx 32rpx; padding: 12rpx 32rpx 0;
.add-img { .add-img {
width: 44rpx; width: 44rpx;
@ -842,17 +918,23 @@ onMounted(() => {
.center { .center {
flex: 1; flex: 1;
width: 20%;
height: 100%; height: 100%;
margin: 0 18rpx; margin: 0 18rpx;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
justify-content: center; justify-content: center;
overflow: hidden;
.title { .title {
font-size: 34rpx; font-size: 34rpx;
color: #1A1A1A; color: #1A1A1A;
font-weight: 500; font-weight: 500;
line-height: 34rpx; line-height: 34rpx;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
margin-right: 10rpx;
} }
} }
@ -879,11 +961,21 @@ onMounted(() => {
} }
.bottom-box {
padding-bottom: 20rpx;
}
.safe-area {
padding-bottom: 20rpx;
padding-bottom: calc(20rpx + constant(safe-area-inset-bottom));
padding-bottom: calc(20rpx + env(safe-area-inset-bottom));
}
.bottom-box { .bottom-box {
background-color: #fff; background-color: #fff;
.bottom-container { .bottom-container {
padding: 20rpx 48rpx; padding: 20rpx 48rpx 0;
.search-box { .search-box {
min-height: 64rpx; min-height: 64rpx;

View File

@ -23,7 +23,8 @@
<view class="top-text" v-if="phone == 'huawei' && 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-if="phone == 'huawei'">{{ formatHuaweiTopTime(message.time) }}</text>
<text v-else>{{ formatChatTime(message.time) }} <text <text v-else>{{ formatChatTime(message.time) }} <text
v-if="(phone == 'oppo' || (phone == 'vivo' && message.isMe)) && message.simIndex"> 中国联通 v-if="(phone == 'oppo' || (phone == 'vivo' && message.isMe)) && message.simIndex">
{{ simInfo[`sim${message.simIndex}`] }}
</text></text> </text></text>
<image style="width: 20rpx;height: 24rpx;margin-left: 8rpx; " <image style="width: 20rpx;height: 24rpx;margin-left: 8rpx; "
@ -74,8 +75,23 @@ const props = defineProps({
} }
}) })
const SIM_STORAGE_KEY = 'sim_info'
const emit = defineEmits(['onMessageLongPress', 'sort']) const emit = defineEmits(['onMessageLongPress', 'sort'])
const simInfo = ref({
sim1: '中国电信',
sim2: '中国移动'
})
onMounted(() => {
try {
const cached = uni.getStorageSync(SIM_STORAGE_KEY)
simInfo.value = cached ? JSON.parse(cached) : { sim1: '中国电信', sim2: '中国移动' }
} catch (e) {
simInfo.value = { sim1: '中国电信', sim2: '中国移动' }
}
})
// //
// props.messageList emit // props.messageList emit
const localSortList = ref([]) const localSortList = ref([])

View File

@ -12,8 +12,8 @@
> 99 > 99
? '99+' : (item.unReadNumber || 1) }}</text> ? '99+' : (item.unReadNumber || 1) }}</text>
</view> </view>
<image class="img avatar shrink-0" <image class="img avatar shrink-0" :class="item.imgShape"
:src="item.img || `/static/image/phone-message/${phone}/default.png`"> :src="item.img || `/static/image/phone-message/${phone}/default.png`" mode="aspectFill">
</image> </image>
</view> </view>
<view class="border-box m-l-24 flex-1 flex flex-align-start"> <view class="border-box m-l-24 flex-1 flex flex-align-start">
@ -40,9 +40,8 @@
<view class="flex flex-align-center" style="margin-left: 1px;"> <view class="flex flex-align-center" style="margin-left: 1px;">
<view class="btn flex-center flex-align-center edit" style="color: #fff;" <view class="btn flex-center flex-align-center edit" style="color: #fff;"
@click="editItem(item)"> @click="editItem(item)">
编辑 <image :src="`/static/image/phone-message/edit.png`">
<!-- <image :src="`/static/image/phone-message/iphone/delete.png`"> </image>
</image> -->
</view> </view>
<view class="btn flex-center flex-align-center delete" @click="deleteItem(item)"> <view class="btn flex-center flex-align-center delete" @click="deleteItem(item)">
<image <image
@ -158,12 +157,45 @@ const editItem = (item) => {
border-radius: 50%; border-radius: 50%;
} }
.edit { .edit {
background-color: #5855D6; background-color: #5855D6;
} }
.main-box {
.title-box {
margin-bottom: 6rpx;
.title {
width: 20px;
flex: 1;
color: #1A1A1A;
font-size: 32rpx;
line-height: 32rpx;
white-space: nowrap;
font-weight: 600;
overflow: hidden;
text-overflow: ellipsis;
}
.time {
flex-shrink: 0;
margin-left: 20rpx;
}
}
}
// //
.iphone-style { .iphone-style {
.circle {
border-radius: 50% !important;
}
.square {
border-radius: 16rpx !important;
}
.swipe-action { .swipe-action {
margin-top: 28rpx; margin-top: 28rpx;

View File

@ -71,7 +71,7 @@
<view v-if="showInfo.placeholder" class="search-box flex flex-align-center"> <view v-if="showInfo.placeholder" class="search-box flex flex-align-center">
<image class="icon" :src="`/static/image/phone-message/${props.phone}/search.png`"> <image class="icon" :src="`/static/image/phone-message/${props.phone}/search.png`">
</image> </image>
<input class="input flex-1" :placeholder="showInfo.placeholder" type="text"> <input class="input flex-1" :placeholder="showInfo.placeholder" type="text" disabled>
<image v-if="phone != 'mi'" class="icon" <image v-if="phone != 'mi'" class="icon"
:src="`/static/image/phone-message/${props.phone}/mic.png`"> :src="`/static/image/phone-message/${props.phone}/mic.png`">
</image> </image>
@ -83,7 +83,8 @@
<view class="bottom-placeholder"></view> <view class="bottom-placeholder"></view>
<!-- 底部样式 --> <!-- 底部样式 -->
<template v-if="phone == 'mi' || phone == 'oppo'"> <template v-if="phone == 'mi' || phone == 'oppo'">
<image class="add-message" :src="`/static/image/phone-message/${props.phone}/add-message.png`"></image> <image class="add-message" @click="emit('add')"
:src="`/static/image/phone-message/${props.phone}/add-message.png`"></image>
<view class="bottom-box flex"> <view class="bottom-box flex">
<view class="item flex-1 h100"> <view class="item flex-1 h100">
<image class="icon" :src="`/static/image/phone-message/${props.phone}/bottom-left.png`"></image> <image class="icon" :src="`/static/image/phone-message/${props.phone}/bottom-left.png`"></image>
@ -117,6 +118,11 @@ const buttonGroup = [{
click: () => { click: () => {
emit('add') emit('add')
} }
}, {
name: "设置卡1卡2运营商",
click: () => {
emit('setSim')
}
}] }]
const props = defineProps({ const props = defineProps({

View File

@ -27,7 +27,7 @@ export function createApp() {
const systemInfo = uni.getStorageSync('systemInfo') || {} const systemInfo = uni.getStorageSync('systemInfo') || {}
app.config.globalProperties.$system = systemInfo.platform == 'ios' ? 'iOS' : 'Android' app.config.globalProperties.$system = systemInfo.platform == 'ios' ? 'iOS' : 'Android'
app.config.globalProperties.$systemInfo = systemInfo app.config.globalProperties.$systemInfo = systemInfo
uni.setStorageSync('version', '1.0.3.sp2') uni.setStorageSync('version', '1.0.3.sp3')
app.config.globalProperties.$version = uni.getStorageSync('version') app.config.globalProperties.$version = uni.getStorageSync('version')
app.use(globalMethods); app.use(globalMethods);

View File

@ -1,6 +1,7 @@
<template> <template>
<view :class="`${data.phone}-style`"> <view :class="`${data.phone}-style`">
<ChatLayout :phone="data.phone" :chatInfo="data.data" :sortMode="isSortMode" @send="handleSend"> <ChatLayout :phone="data.phone" :chatInfo="data.data" :sortMode="isSortMode" @send="handleSend"
:number="data.number">
<!-- 弹出操作层及遮罩 --> <!-- 弹出操作层及遮罩 -->
<view v-if="showActionPopup" class="action-mask" @tap="closeActionPopup"> <view v-if="showActionPopup" class="action-mask" @tap="closeActionPopup">
<view class="action-popup" :style="{ top: popupTop + 'px', left: popupLeft + 'px' }"> <view class="action-popup" :style="{ top: popupTop + 'px', left: popupLeft + 'px' }">
@ -13,9 +14,13 @@
<text>消息互换</text> <text>消息互换</text>
</view> </view>
<view class="action-item" @tap.stop="handleSort"> <view class="action-item" @tap.stop="handleSort">
<image class="action-icon" src="/static/image/phone-message/bianji.png"></image> <image class="action-icon" src="/static/image/phone-message/sort.png"></image>
<text>排序</text> <text>排序</text>
</view> </view>
<view class="action-item" @tap.stop="handleChangeSpeaker">
<image class="action-icon" src="/static/image/phone-message/change.png"></image>
<text>切换发言人</text>
</view>
<view class="action-item" @tap.stop="handleDelete"> <view class="action-item" @tap.stop="handleDelete">
<image class="action-icon" src="/static/image/phone-message/shanchu.png"></image> <image class="action-icon" src="/static/image/phone-message/shanchu.png"></image>
<text>删除</text> <text>删除</text>
@ -33,12 +38,14 @@
<view class="edit-row"> <view class="edit-row">
<text class="edit-label">时间</text> <text class="edit-label">时间</text>
<view class="time-picker-group"> <view class="time-picker-group">
<picker mode="date" :value="editingDate" @change="onDateChange"> <picker mode="date" :fields="$system == 'Android' ? 'day' : ''" :value="editingDate"
@change="onDateChange">
<view class="time-picker-item"> <view class="time-picker-item">
<text>{{ editingDate || '选择日期' }}</text> <text>{{ editingDate || '选择日期' }}</text>
</view> </view>
</picker> </picker>
<picker mode="time" :value="editingTimeOfDay" @change="onTimeOfDayChange"> <picker mode="time" :fields="$system == 'Android' ? 'minute' : ''"
:value="editingTimeOfDay" @change="onTimeOfDayChange">
<view class="time-picker-item"> <view class="time-picker-item">
<text>{{ editingTimeOfDay || '选择时刻' }}</text> <text>{{ editingTimeOfDay || '选择时刻' }}</text>
</view> </view>
@ -62,12 +69,12 @@
<view class="edit-input" <view class="edit-input"
style="padding: 0; background: transparent; display: flex; align-items: center; border-radius: 8rpx; height: 70rpx;"> 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" <uni-data-select v-model="editingSimKa" :localdata="simList" :clear="false"
placeholder="请选择卡号" placeholder="请选择卡号" @change="onSimKaChange"
style="flex: 1; border: none !important; width: 100%;"></uni-data-select> style="flex: 1; border: none !important; width: 100%;"></uni-data-select>
</view> </view>
</view> </view>
<view class="edit-row"> <view class="edit-row">
<text class="edit-label">正文</text> <text class="edit-label">内容</text>
</view> </view>
<editor id="editor" class="edit-textarea" placeholder="请输入消息内容..." @ready="onEditorReady"> <editor id="editor" class="edit-textarea" placeholder="请输入消息内容..." @ready="onEditorReady">
</editor> </editor>
@ -108,6 +115,7 @@ import {
} from 'vue' } from 'vue'
import { import {
onLoad, onLoad,
onShow,
onPageScroll onPageScroll
} from "@dcloudio/uni-app"; } from "@dcloudio/uni-app";
import { import {
@ -121,6 +129,8 @@ const defaultList = defaultData
const STORAGE_KEY = 'message_list' const STORAGE_KEY = 'message_list'
let currentId = null // id let currentId = null // id
let isMe = ref(true)
const messageList = ref([]) const messageList = ref([])
const showActionPopup = ref(false) const showActionPopup = ref(false)
const popupTop = ref(0) const popupTop = ref(0)
@ -216,7 +226,7 @@ const handleEdit = () => {
const parts = (selectedMessage.value.time || "").split(' ') const parts = (selectedMessage.value.time || "").split(' ')
editingDate.value = parts[0] || "" editingDate.value = parts[0] || ""
editingTimeOfDay.value = parts[1] || "" editingTimeOfDay.value = parts[1] || ""
editingSimKa.value = selectedMessage.value.simKa !== undefined ? selectedMessage.value.simKa : ""; editingSimKa.value = selectedMessage.value.simIndex !== undefined ? selectedMessage.value.simIndex : "";
editingTimeMode.value = selectedMessage.value.timeMode || 'auto'; editingTimeMode.value = selectedMessage.value.timeMode || 'auto';
showEditPopup.value = true; showEditPopup.value = true;
@ -253,6 +263,14 @@ const onTimeOfDayChange = (e) => {
editingTime.value = `${editingDate.value} ${editingTimeOfDay.value}`.trim() editingTime.value = `${editingDate.value} ${editingTimeOfDay.value}`.trim()
} }
/**
* 切换sim卡
* @param value
*/
const onSimKaChange = (value) => {
editingSimKa.value = value
}
const confirmEdit = () => { const confirmEdit = () => {
if (editingMessage.value && editorCtx) { if (editingMessage.value && editorCtx) {
editorCtx.getContents({ editorCtx.getContents({
@ -270,9 +288,9 @@ const confirmEdit = () => {
// hideTime // hideTime
delete messageList.value[index].hideTime; delete messageList.value[index].hideTime;
if (editingSimKa.value) { if (editingSimKa.value) {
messageList.value[index].simKa = Number(editingSimKa.value); messageList.value[index].simIndex = Number(editingSimKa.value);
} else { } else {
delete messageList.value[index].simKa; delete messageList.value[index].simIndex;
} }
} }
closeEditPopup(); closeEditPopup();
@ -329,6 +347,23 @@ const confirmSort = () => {
saveChatList() saveChatList()
} }
const handleChangeSpeaker = () => {
isMe.value = !isMe.value
if (isMe.value) {
uni.showToast({
title: "现在是自己发言",
icon: "none"
})
} else {
uni.showToast({
title: "现在是对方发言",
icon: "none"
})
}
closeActionPopup();
}
/** /**
* 删除消息 * 删除消息
*/ */
@ -341,7 +376,8 @@ const handleDelete = () => {
const data = reactive({ const data = reactive({
phone: "iphone", phone: "iphone",
data: {} data: {},
number: 0
}) })
onLoad((options) => { onLoad((options) => {
console.log(options) console.log(options)
@ -353,10 +389,23 @@ onLoad((options) => {
// //
try { try {
const cached = uni.getStorageSync(STORAGE_KEY) const cached = uni.getStorageSync(STORAGE_KEY)
const list = cached ? JSON.parse(cached) : defaultList let list = cached ? JSON.parse(cached) : defaultList
const found = list.find(item => item.id == options.id) const found = list.find(item => item.id == options.id)
if (found) { if (found) {
data.data = found data.data = found
let number = 0
list.forEach(item => {
if (item.id == options.id) {
item.unRead = false
item.unReadNumber = 1
}
console.log(number + item.unRead ? item.unReadNumber : 0)
number = number + Number(item.unRead ? item.unReadNumber : 0)
})
data.number = number
console.log(data.data)
uni.setStorageSync(STORAGE_KEY, JSON.stringify(list))
messageList.value = found.chatList || [] messageList.value = found.chatList || []
return return
} }
@ -367,9 +416,23 @@ onLoad((options) => {
} }
}) })
onShow(() => {
// #ifdef APP-PLUS
if (data.phone == 'oppo') {
util.setAndroidSystemBarColor('#FAFAFA')
} else {
util.setAndroidSystemBarColor('#ffffff')
}
setTimeout(() => {
plus.navigator.setStatusBarStyle("dark");
}, 500)
// #endif
})
const handleSend = (params) => { const handleSend = (params) => {
console.log(params) console.log(params)
params.id = stringUtil.uuid() params.id = stringUtil.uuid()
params.isMe = isMe.value
messageList.value.push(params) messageList.value.push(params)
saveChatList() saveChatList()
} }
@ -464,6 +527,7 @@ const handleSend = (params) => {
border-radius: 12rpx; border-radius: 12rpx;
padding: 20rpx 30rpx; padding: 20rpx 30rpx;
display: flex; display: flex;
justify-content: space-between;
transform: translateX(-50%); transform: translateX(-50%);
width: 540rpx; width: 540rpx;
z-index: 10; z-index: 10;
@ -488,6 +552,10 @@ const handleSend = (params) => {
color: #FFFFFF; color: #FFFFFF;
font-size: 20rpx; font-size: 20rpx;
margin: 0 20rpx; margin: 0 20rpx;
text {
white-space: nowrap;
}
} }
.action-icon { .action-icon {
@ -525,7 +593,6 @@ const handleSend = (params) => {
color: #333; color: #333;
text-align: center; text-align: center;
padding: 30rpx 0; padding: 30rpx 0;
border-bottom: 1rpx solid #EEEEEE;
} }
.edit-body { .edit-body {
@ -549,7 +616,6 @@ const handleSend = (params) => {
.edit-footer { .edit-footer {
display: flex; display: flex;
border-top: 1rpx solid #EEEEEE;
} }
.edit-btn { .edit-btn {
@ -558,16 +624,22 @@ const handleSend = (params) => {
line-height: 90rpx; line-height: 90rpx;
text-align: center; text-align: center;
font-size: 32rpx; font-size: 32rpx;
border-radius: 12rpx;
margin: 32rpx 16rpx;
margin-top: 0;
} }
.edit-btn.cancel { .edit-btn.cancel {
color: #666; color: #767676;
border-right: 1rpx solid #EEEEEE; background-color: #F1F1F1;
margin-left: 30rpx;
} }
.edit-btn.confirm { .edit-btn.confirm {
color: #007AFF; color: #FFFFFF;
font-weight: bold; font-weight: bold;
background-color: #1777FF;
margin-right: 30rpx;
} }
.edit-row { .edit-row {

View File

@ -1,6 +1,6 @@
<template> <template>
<view> <view>
<MessageNavBar :phone="data.phone" :isScroll="data.isScroll" @add="openAddPopup"> <MessageNavBar :phone="data.phone" :isScroll="data.isScroll" @add="openAddPopup" @setSim="setSim">
<MessageList :phone="data.phone" :list="defaultList" @item-click="itemClick" @delete-item="deleteItem" <MessageList :phone="data.phone" :list="defaultList" @item-click="itemClick" @delete-item="deleteItem"
@edit-item="editItem"> @edit-item="editItem">
</MessageList> </MessageList>
@ -13,13 +13,19 @@
<view class="add-body"> <view class="add-body">
<view class="add-row"> <view class="add-row">
<text class="add-label">头像URL</text> <text class="add-label">头像URL</text>
<view class="image-box" style="width: 84rpx;height: 84rpx;" @tap="selectImage"> <view class="image-box" :class="addForm.imgShape" style="width: 84rpx;height: 84rpx;"
@tap="selectImage">
<image v-if="addForm.img" class="image w100 h100" :src="addForm.img" mode="aspectFill"> <image v-if="addForm.img" class="image w100 h100" :src="addForm.img" mode="aspectFill">
</image> </image>
<image v-else class="image w100 h100" src="/static/image/phone-message/add.png" <image v-else class="image w100 h100" src="/static/image/phone-message/add.png"
mode="aspectFill"> mode="aspectFill">
</image> </image>
</view> </view>
<view v-if="data.phone == 'iphone'"
style="flex: 1;display: flex;align-items: center;justify-content: flex-end;">
<uni-data-checkbox style="display: flex;justify-content: flex-end;"
v-model="addForm.imgShape" :localdata="shape"></uni-data-checkbox>
</view>
</view> </view>
<view class="add-row"> <view class="add-row">
<text class="add-label required">联系人</text> <text class="add-label required">联系人</text>
@ -33,7 +39,8 @@
<text class="add-label">是否未读</text> <text class="add-label">是否未读</text>
<switch :checked="addForm.unRead" @change="addForm.unRead = !addForm.unRead" /> <switch :checked="addForm.unRead" @change="addForm.unRead = !addForm.unRead" />
</view> </view>
<view class="add-row" v-if="addForm.unRead && data.phone == 'oppo' || data.phone == 'huawei'"> <view class="add-row"
v-if="addForm.unRead && (data.phone == 'oppo' || data.phone == 'huawei' || data.phone == 'iphone')">
<text class="add-label">未读数量</text> <text class="add-label">未读数量</text>
<input class="add-input" type="number" v-model="addForm.unReadNumber" placeholder="请输入未读数量" /> <input class="add-input" type="number" v-model="addForm.unReadNumber" placeholder="请输入未读数量" />
</view> </view>
@ -59,6 +66,27 @@
</view> </view>
</view> </view>
</view> </view>
<!-- 设置运营商弹窗 -->
<view v-if="showSimPopup" class="add-mask" @tap="closeSimPopup">
<view class="add-popup" @tap.stop>
<view class="add-header">设置卡1卡2运营商</view>
<view class="add-body">
<view class="add-row">
<text class="add-label">卡1运营商</text>
<input class="add-input" v-model="simForm.sim1" placeholder="请输入卡1运营商名称" />
</view>
<view class="add-row">
<text class="add-label">卡2运营商</text>
<input class="add-input" v-model="simForm.sim2" placeholder="请输入卡2运营商名称" />
</view>
</view>
<view class="add-footer">
<view class="add-btn cancel" @tap="closeSimPopup">取消</view>
<view class="add-btn confirm" @tap="confirmSim">确定</view>
</view>
</view>
</view>
</view> </view>
</template> </template>
@ -91,7 +119,16 @@ const data = reactive({
isScroll: false isScroll: false
}) })
const shape = [{
text: '圆形',
value: 'circle'
}, {
text: '方形',
value: 'square',
}]
const STORAGE_KEY = 'message_list' const STORAGE_KEY = 'message_list'
const SIM_STORAGE_KEY = 'sim_info'
onLoad((options) => { onLoad((options) => {
if (options.phone) { if (options.phone) {
@ -101,6 +138,16 @@ onLoad((options) => {
}) })
onShow(() => { onShow(() => {
// #ifdef APP-PLUS
if (data.phone == 'oppo') {
util.setAndroidSystemBarColor('#FAFAFA')
} else {
util.setAndroidSystemBarColor('#ffffff')
}
setTimeout(() => {
plus.navigator.setStatusBarStyle("dark");
}, 500)
// #endif
// //
try { try {
const cached = uni.getStorageSync(STORAGE_KEY) const cached = uni.getStorageSync(STORAGE_KEY)
@ -119,6 +166,49 @@ onPageScroll((e) => {
} }
}) })
/**
* 将图片保存到本地持久化存储
* @param {string} tempFilePath 临时文件路径
* @returns {Promise<string>} 持久化后的文件路径
*/
const saveImageToLocal = (tempFilePath) => {
return new Promise((resolve, reject) => {
//
if (!tempFilePath || tempFilePath.startsWith('_doc') || tempFilePath.startsWith('/static')) {
return resolve(tempFilePath)
}
uni.saveFile({
tempFilePath: tempFilePath,
success: (res) => {
console.log('图片持久化成功:', res.savedFilePath)
resolve(res.savedFilePath)
},
fail: (err) => {
console.error('图片持久化失败:', err)
resolve(tempFilePath) // 退使
}
})
})
}
/**
* 删除本地持久化文件
* @param {string} filePath 文件路径
*/
const removeLocalFile = (filePath) => {
if (filePath && filePath.startsWith('_doc')) {
uni.removeSavedFile({
filePath: filePath,
success: () => {
console.log('本地文件删除成功:', filePath)
},
fail: (err) => {
console.warn('本地文件删除失败:', filePath, err)
}
})
}
}
/** /**
* 删除元素 * 删除元素
* @param item * @param item
@ -129,10 +219,17 @@ const deleteItem = (item) => {
content: '确定要删除吗?', content: '确定要删除吗?',
success: (res) => { success: (res) => {
if (res.confirm) { if (res.confirm) {
defaultList.value.splice(defaultList.value.findIndex(i => i.id === item.id), 1) const idx = defaultList.value.findIndex(i => i.id === item.id)
if (idx > -1) {
//
if (item.img) {
removeLocalFile(item.img)
}
defaultList.value.splice(idx, 1)
uni.setStorageSync(STORAGE_KEY, JSON.stringify(defaultList.value)) uni.setStorageSync(STORAGE_KEY, JSON.stringify(defaultList.value))
} }
} }
}
}) })
} }
@ -179,6 +276,7 @@ const openAddPopup = () => {
addForm.unReadNumber = 1 addForm.unReadNumber = 1
addForm.noNotice = false addForm.noNotice = false
addForm.chatList = [] addForm.chatList = []
addForm.imgShape = 'circle'
addForm.date = `${now.getFullYear()}-${pad(now.getMonth() + 1)}-${pad(now.getDate())}` addForm.date = `${now.getFullYear()}-${pad(now.getMonth() + 1)}-${pad(now.getDate())}`
addForm.timeOfDay = `${pad(now.getHours())}:${pad(now.getMinutes())}` addForm.timeOfDay = `${pad(now.getHours())}:${pad(now.getMinutes())}`
showAddPopup.value = true showAddPopup.value = true
@ -206,32 +304,48 @@ const editItem = (item) => {
const parts = timeStr.split(' ') const parts = timeStr.split(' ')
addForm.title = item.title || '' addForm.title = item.title || ''
addForm.img = item.img || '' addForm.img = item.img || ''
addForm.imgShape = item.imgShape || 'circle'
addForm.content = '' addForm.content = ''
addForm.unRead = !!item.unRead addForm.unRead = !!item.unRead
addForm.unReadNumber = item.unReadNumber || 0 addForm.unReadNumber = item.unReadNumber || 0
addForm.noNotice = item.noNotice || false
addForm.date = parts[0] || '' addForm.date = parts[0] || ''
addForm.timeOfDay = parts[1] || '' addForm.timeOfDay = parts[1] || ''
addForm.chatList = item.chatList || [] addForm.chatList = item.chatList || []
showAddPopup.value = true showAddPopup.value = true
} }
const confirmAdd = () => { const confirmAdd = async () => {
if (!addForm.title.trim()) { if (!addForm.title.trim()) {
uni.showToast({ title: '请输入联系人名称', icon: 'none' }) uni.showToast({ title: '请输入联系人名称', icon: 'none' })
return return
} }
console.log(addForm)
const time = `${addForm.date} ${addForm.timeOfDay}`.trim() const time = `${addForm.date} ${addForm.timeOfDay}`.trim()
//
let finalImgPath = addForm.img
if (editingItem.value) {
//
if (addForm.img !== editingItem.value.img) {
finalImgPath = await saveImageToLocal(addForm.img)
removeLocalFile(editingItem.value.img)
}
} else {
//
finalImgPath = await saveImageToLocal(addForm.img)
}
if (editingItem.value) { if (editingItem.value) {
// ===== ===== // ===== =====
const idx = defaultList.value.findIndex(i => i.id === editingItem.value.id) const idx = defaultList.value.findIndex(i => i.id === editingItem.value.id)
if (idx > -1) { if (idx > -1) {
defaultList.value[idx].title = addForm.title.trim() defaultList.value[idx].title = addForm.title.trim()
defaultList.value[idx].img = addForm.img.trim() defaultList.value[idx].img = finalImgPath
defaultList.value[idx].unRead = addForm.unRead defaultList.value[idx].unRead = addForm.unRead
defaultList.value[idx].unReadNumber = addForm.unReadNumber defaultList.value[idx].unReadNumber = addForm.unReadNumber
defaultList.value[idx].noNotice = addForm.noNotice defaultList.value[idx].noNotice = addForm.noNotice
defaultList.value[idx].imgShape = addForm.imgShape
defaultList.value[idx].time = time defaultList.value[idx].time = time
} }
} else { } else {
@ -241,8 +355,9 @@ const confirmAdd = () => {
unRead: addForm.unRead, unRead: addForm.unRead,
unReadNumber: addForm.unReadNumber, unReadNumber: addForm.unReadNumber,
noNotice: addForm.noNotice, noNotice: addForm.noNotice,
img: addForm.img.trim(), img: finalImgPath,
title: addForm.title.trim(), title: addForm.title.trim(),
imgShape: addForm.imgShape,
chatList: addForm.content.trim() ? [ chatList: addForm.content.trim() ? [
{ {
id: stringUtil.uuid(), id: stringUtil.uuid(),
@ -268,6 +383,41 @@ const confirmAdd = () => {
closeAddPopup() closeAddPopup()
} }
// ===== =====
const showSimPopup = ref(false)
const simForm = reactive({
sim1: '',
sim2: ''
})
const setSim = () => {
try {
const cached = uni.getStorageSync(SIM_STORAGE_KEY)
const simInfo = cached ? JSON.parse(cached) : { sim1: '中国电信', sim2: '中国移动' }
simForm.sim1 = simInfo.sim1
simForm.sim2 = simInfo.sim2
} catch (e) {
simForm.sim1 = '中国电信'
simForm.sim2 = '中国移动'
}
showSimPopup.value = true
}
const closeSimPopup = () => {
showSimPopup.value = false
}
const confirmSim = () => {
try {
uni.setStorageSync(SIM_STORAGE_KEY, JSON.stringify({
sim1: simForm.sim1.trim() || '联通',
sim2: simForm.sim2.trim() || '移动'
}))
uni.showToast({ title: '保存成功', icon: 'success' })
} catch (e) { }
closeSimPopup()
}
</script> </script>
<style> <style>
@ -361,6 +511,19 @@ page {
align-items: center; align-items: center;
padding: 20rpx 32rpx; padding: 20rpx 32rpx;
gap: 16rpx; gap: 16rpx;
.image-box {
border-radius: 50%;
overflow: hidden;
}
.circle {
border-radius: 50%;
}
.square {
border-radius: 16rpx;
}
} }
.between { .between {

Binary file not shown.

After

Width:  |  Height:  |  Size: 934 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 568 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 740 B