alipay-emulator/pages/message/list-index.vue

610 lines
14 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<template>
<view>
<MessageNavBar :phone="data.phone" :isScroll="data.isScroll" @add="openAddPopup" @setSim="setSim">
<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" :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>
<image v-else class="image w100 h100" src="/static/image/phone-message/add.png"
mode="aspectFill">
</image>
</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 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' || data.phone == 'iphone')">
<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 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>
</template>
<script setup>
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';
const defaultList = ref(defaultData)
const data = reactive({
navBar: {
title: '信息',
bgColor: '#FFFFFF',
},
phone: 'iphone',
isScroll: false
})
const shape = [{
text: '圆形',
value: 'circle'
}, {
text: '方形',
value: 'square',
}]
const STORAGE_KEY = 'message_list'
const SIM_STORAGE_KEY = 'sim_info'
onLoad((options) => {
if (options.phone) {
data.phone = options.phone
}
})
onShow(() => {
// #ifdef APP-PLUS
if (data.phone == 'oppo') {
util.setAndroidSystemBarColor('#FAFAFA')
} else {
util.setAndroidSystemBarColor('#ffffff')
}
setTimeout(() => {
plus.navigator.setStatusBarStyle("dark");
}, 500)
// #endif
// 优先从缓存加载列表
try {
const cached = uni.getStorageSync(STORAGE_KEY)
if (cached) {
defaultList.value = JSON.parse(cached)
}
} catch (e) { }
})
onPageScroll((e) => {
if (e.scrollTop > 60) {
data.isScroll = true
} else {
data.isScroll = false
}
})
/**
* 将图片保存到本地持久化存储
* @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
*/
const deleteItem = (item) => {
uni.showModal({
title: '提示',
content: '确定要删除吗?',
success: (res) => {
if (res.confirm) {
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))
}
}
}
})
}
/**
* 选择图片
*/
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.imgShape = 'circle'
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.imgShape = item.imgShape || 'circle'
addForm.content = ''
addForm.unRead = !!item.unRead
addForm.unReadNumber = item.unReadNumber || 0
addForm.noNotice = item.noNotice || false
addForm.date = parts[0] || ''
addForm.timeOfDay = parts[1] || ''
addForm.chatList = item.chatList || []
showAddPopup.value = true
}
const confirmAdd = async () => {
if (!addForm.title.trim()) {
uni.showToast({ title: '请输入联系人名称', icon: 'none' })
return
}
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) {
// ===== 编辑模式:更新已有条目 =====
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 = finalImgPath
defaultList.value[idx].unRead = addForm.unRead
defaultList.value[idx].unReadNumber = addForm.unReadNumber
defaultList.value[idx].noNotice = addForm.noNotice
defaultList.value[idx].imgShape = addForm.imgShape
defaultList.value[idx].time = time
}
} else {
// ===== 新增模式 =====
const newItem = {
id: Date.now(),
unRead: addForm.unRead,
unReadNumber: addForm.unReadNumber,
noNotice: addForm.noNotice,
img: finalImgPath,
title: addForm.title.trim(),
imgShape: addForm.imgShape,
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()
}
// ===== 设置运营商弹窗 =====
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>
<style>
@import '@/common/main.css';
page {
background-color: #FFFFFF;
}
</style>
<style lang="less">
::v-deep .uni-navbar__header-btns {
width: 100px !important;
flex: 1;
}
.iphone-style {
.mg-r-30 {
margin-right: 60rpx;
}
.mg-r-5 {
margin-right: 10rpx;
}
.left-icon {
width: 40rpx;
height: 40rpx;
}
.right-icon {
width: 48rpx;
height: 48rpx;
}
.left-text {
font-size: 32rpx;
color: #0278E2;
margin-left: 10rpx;
}
.center-text {
font-size: 32rpx;
color: #1a1a1a;
}
}
.mi-style {
.right-icon {
width: 40rpx;
height: 40rpx;
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;
.image-box {
border-radius: 50%;
overflow: hidden;
}
.circle {
border-radius: 50%;
}
.square {
border-radius: 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>