fix: 提交

This commit is contained in:
wangdongbo 2026-02-02 08:53:26 +08:00
parent 68e4f01bd2
commit dcc84cf449
6 changed files with 1311 additions and 700 deletions

View File

@ -0,0 +1,89 @@
<template>
<view class="share-actions">
<view v-if="showSave" class="action-btn save-btn" @click="handleSave">
<text class="btn-text">{{ saveText }}</text>
</view>
<button
v-if="showShare"
class="action-btn share-btn"
open-type="share"
>
<text class="btn-text">{{ shareText }}</text>
</button>
</view>
</template>
<script setup>
import { defineProps, defineEmits } from 'vue'
const props = defineProps({
//
showSave: {
type: Boolean,
default: true
},
//
showShare: {
type: Boolean,
default: true
},
//
saveText: {
type: String,
default: '保存图片'
},
//
shareText: {
type: String,
default: '分享微信'
}
})
const emit = defineEmits(['save', 'share'])
function handleSave() {
emit('save')
}
</script>
<style scoped>
.share-actions {
display: flex;
gap: 20rpx;
padding: 0 30rpx;
}
.action-btn {
flex: 1;
height: 80rpx;
display: flex;
align-items: center;
justify-content: center;
border-radius: 12rpx;
font-size: 28rpx;
}
.save-btn {
border: 2rpx solid #0074ff;
background: transparent;
}
.save-btn .btn-text {
color: #0074ff;
}
.share-btn {
background: #0074ff;
border: none;
padding: 0;
line-height: normal;
}
.share-btn::after {
border: none;
}
.share-btn .btn-text {
color: #ffffff;
}
</style>

View File

@ -1,34 +1,18 @@
<template>
<view class="message-page">
<!-- 头部组件 -->
<message-header
v-model:activeTab="activeTab"
@team-change="handleTeamChange"
@add-patient="handleAddPatient"
/>
<message-header v-model:activeTab="activeTab" @team-change="handleTeamChange" @add-patient="handleAddPatient" />
<!-- 消息列表 -->
<scroll-view
class="message-list"
scroll-y="true"
refresher-enabled
:refresher-triggered="refreshing"
@refresherrefresh="handleRefresh"
@scrolltolower="handleLoadMore"
>
<scroll-view class="message-list" scroll-y="true" refresher-enabled :refresher-triggered="refreshing"
@refresherrefresh="handleRefresh" @scrolltolower="handleLoadMore">
<!-- 消息列表项 -->
<view
v-for="conversation in filteredConversationList"
:key="conversation.groupID || conversation.conversationID"
class="message-item"
@click="handleClickConversation(conversation)"
>
<view v-for="conversation in filteredConversationList"
:key="conversation.groupID || conversation.conversationID" class="message-item"
@click="handleClickConversation(conversation)">
<view class="avatar-container">
<image
class="avatar"
:src="conversation.avatar || '/static/default-avatar.png'"
mode="aspectFill"
/>
<image class="avatar" :src="conversation.avatar || '/static/default-avatar.png'"
mode="aspectFill" />
<view v-if="conversation.unreadCount > 0" class="unread-badge">
<text class="unread-text">{{
conversation.unreadCount > 99 ? "99+" : conversation.unreadCount
@ -40,10 +24,7 @@
<view class="header">
<view class="name-info">
<text class="name">{{ formatPatientName(conversation) }}</text>
<text
v-if="conversation.patientSex || conversation.patientAge"
class="patient-info"
>
<text v-if="conversation.patientSex || conversation.patientAge" class="patient-info">
{{ formatPatientInfo(conversation) }}
</text>
</view>
@ -60,10 +41,7 @@
</view>
<!-- 空状态 -->
<view
v-if="!loading && filteredConversationList.length === 0"
class="empty-container"
>
<view v-if="!loading && filteredConversationList.length === 0" class="empty-container">
<image class="empty-image" src="/static/empty.svg" mode="aspectFit" />
<text class="empty-text">{{
activeTab === "processing" ? "暂无处理中的会话" : "暂无已结束的会话"
@ -71,10 +49,7 @@
</view>
<!-- 加载更多 -->
<view
v-if="hasMore && filteredConversationList.length > 0"
class="load-more"
>
<view v-if="hasMore && filteredConversationList.length > 0" class="load-more">
<text class="load-more-text">{{
loadingMore ? "加载中..." : "上拉加载更多"
}}</text>
@ -84,40 +59,63 @@
</template>
<script setup>
import { ref, computed } from "vue";
import { onLoad, onShow, onHide } from "@dcloudio/uni-app";
import { storeToRefs } from "pinia";
import useAccountStore from "@/store/account.js";
import useTeamStore from "@/store/team.js";
import useInfoCheck from "@/hooks/useInfoCheck.js";
import { globalTimChatManager } from "@/utils/tim-chat.js";
import { mergeConversationWithGroupDetails } from "@/utils/conversation-merger.js";
import MessageHeader from "./components/message-header.vue";
import {
ref,
computed
} from "vue";
import {
onLoad,
onShow,
onHide
} from "@dcloudio/uni-app";
import {
storeToRefs
} from "pinia";
import useAccountStore from "@/store/account.js";
import useTeamStore from "@/store/team.js";
import useInfoCheck from "@/hooks/useInfoCheck.js";
import {
globalTimChatManager
} from "@/utils/tim-chat.js";
import {
mergeConversationWithGroupDetails
} from "@/utils/conversation-merger.js";
import MessageHeader from "./components/message-header.vue";
//
const { account, openid, isIMInitialized } = storeToRefs(useAccountStore());
const { initIMAfterLogin } = useAccountStore();
//
const {
account,
openid,
isIMInitialized
} = storeToRefs(useAccountStore());
const {
initIMAfterLogin
} = useAccountStore();
//
const teamStore = useTeamStore();
const { getTeams } = teamStore;
//
const teamStore = useTeamStore();
const {
getTeams
} = teamStore;
//
const { withInfo } = useInfoCheck();
//
const {
withInfo
} = useInfoCheck();
//
const currentTeamId = ref(""); // ""
//
const currentTeamId = ref(""); // ""
//
const conversationList = ref([]);
const loading = ref(false);
const loadingMore = ref(false);
const hasMore = ref(false);
const refreshing = ref(false);
const activeTab = ref("processing");
//
const conversationList = ref([]);
const loading = ref(false);
const loadingMore = ref(false);
const hasMore = ref(false);
const refreshing = ref(false);
const activeTab = ref("processing");
// orderStatus
const filteredConversationList = computed(() => {
// orderStatus
const filteredConversationList = computed(() => {
let filtered = [];
if (activeTab.value === "processing") {
@ -141,23 +139,23 @@ const filteredConversationList = computed(() => {
filtered = filtered.filter((conv) => conv.teamId === currentTeamId.value);
}
return filtered;
});
});
//
const handleTeamChange = (teamId) => {
//
const handleTeamChange = (teamId) => {
currentTeamId.value = teamId;
console.log("切换到团队ID:", teamId);
};
};
// - 使 withInfo 使
const handleAddPatient = withInfo(() => {
// - 使 withInfo 使
const handleAddPatient = withInfo(() => {
uni.navigateTo({
url: "/pages/work/team/invite/invite-patient",
});
});
});
// IM
const initIM = async () => {
// IM
const initIM = async () => {
if (!isIMInitialized.value) {
uni.showLoading({
title: "连接中...",
@ -184,10 +182,10 @@ const initIM = async () => {
}
}
return true;
};
};
//
const loadConversationList = async () => {
//
const loadConversationList = async () => {
if (loading.value) return;
loading.value = true;
try {
@ -232,13 +230,13 @@ const loadConversationList = async () => {
} finally {
loading.value = false;
}
};
};
//
let updateTimer = null;
//
let updateTimer = null;
//
const setupConversationListener = () => {
//
const setupConversationListener = () => {
if (!globalTimChatManager) return;
//
@ -309,7 +307,8 @@ const setupConversationListener = () => {
const existing = conversationList.value[existingIndex];
if (
existing.lastMessage !== conversationData.lastMessage ||
existing.lastMessageTime !== conversationData.lastMessageTime ||
existing.lastMessageTime !== conversationData
.lastMessageTime ||
existing.unreadCount !== conversationData.unreadCount ||
existing.patientName !== conversationData.patientName ||
existing.patientSex !== conversationData.patientSex ||
@ -387,15 +386,15 @@ const setupConversationListener = () => {
);
}
});
};
};
//
const formatPatientName = (conversation) => {
//
const formatPatientName = (conversation) => {
return conversation.patientName || "未知患者";
};
};
// +
const formatPatientInfo = (conversation) => {
// +
const formatPatientInfo = (conversation) => {
const parts = [];
//
@ -411,10 +410,10 @@ const formatPatientInfo = (conversation) => {
}
return parts.join(" ");
};
};
//
const formatMessageTime = (timestamp) => {
//
const formatMessageTime = (timestamp) => {
if (!timestamp) return "";
const now = Date.now();
@ -453,10 +452,10 @@ const formatMessageTime = (timestamp) => {
//
return `${date.getFullYear()}${date.getMonth() + 1}${date.getDate()}`;
};
};
//
const handleClickConversation = (conversation) => {
//
const handleClickConversation = (conversation) => {
console.log("点击会话:", conversation);
//
@ -472,10 +471,10 @@ const handleClickConversation = (conversation) => {
uni.navigateTo({
url: `/pages/message/index?conversationID=${conversation.conversationID}&groupID=${conversation.groupID}`,
});
};
};
//
const handleLoadMore = () => {
//
const handleLoadMore = () => {
if (loadingMore.value || !hasMore.value) return;
loadingMore.value = true;
@ -483,25 +482,25 @@ const handleLoadMore = () => {
setTimeout(() => {
loadingMore.value = false;
}, 1000);
};
};
//
const handleRefresh = async () => {
//
const handleRefresh = async () => {
refreshing.value = true;
try {
await loadConversationList();
} finally {
refreshing.value = false;
}
};
};
//
onLoad(() => {
//
onLoad(() => {
console.log("消息列表页面加载");
});
});
//
onShow(async () => {
//
onShow(async () => {
try {
//
await getTeams();
@ -525,10 +524,10 @@ onShow(async () => {
icon: "none",
});
}
});
});
//
onHide(() => {
//
onHide(() => {
//
if (updateTimer) {
clearTimeout(updateTimer);
@ -540,49 +539,49 @@ onHide(() => {
globalTimChatManager.setCallback("onConversationListUpdated", null);
globalTimChatManager.setCallback("onMessageReceived", null);
}
});
});
</script>
<style scoped lang="scss">
.message-page {
.message-page {
width: 100%;
height: 100vh;
background-color: #f5f5f5;
display: flex;
flex-direction: column;
}
}
.message-list {
.message-list {
width: 100%;
flex: 1;
}
}
.loading-container,
.empty-container {
.loading-container,
.empty-container {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
padding: 100rpx 0;
}
}
.loading-text {
.loading-text {
font-size: 28rpx;
color: #999;
}
}
.empty-image {
.empty-image {
width: 200rpx;
height: 200rpx;
margin-bottom: 20rpx;
}
}
.empty-text {
.empty-text {
font-size: 28rpx;
color: #999;
}
}
.message-item {
.message-item {
display: flex;
align-items: center;
padding: 24rpx 32rpx;
@ -592,21 +591,21 @@ onHide(() => {
&:active {
background-color: #f5f5f5;
}
}
}
.avatar-container {
.avatar-container {
position: relative;
margin-right: 24rpx;
flex-shrink: 0;
}
}
.avatar {
.avatar {
width: 96rpx;
height: 96rpx;
border-radius: 8rpx;
}
}
.unread-badge {
.unread-badge {
position: absolute;
top: -8rpx;
right: -8rpx;
@ -618,37 +617,37 @@ onHide(() => {
display: flex;
align-items: center;
justify-content: center;
}
}
.unread-text {
.unread-text {
font-size: 20rpx;
color: #fff;
line-height: 1;
}
}
.content {
.content {
flex: 1;
display: flex;
flex-direction: column;
justify-content: center;
min-width: 0;
}
}
.header {
.header {
display: flex;
align-items: center;
justify-content: space-between;
margin-bottom: 8rpx;
}
}
.name-info {
.name-info {
display: flex;
align-items: center;
flex: 1;
min-width: 0;
}
}
.name {
.name {
font-size: 30rpx;
font-weight: 500;
color: #333;
@ -656,42 +655,42 @@ onHide(() => {
text-overflow: ellipsis;
white-space: nowrap;
flex-shrink: 1;
}
}
.patient-info {
.patient-info {
font-size: 26rpx;
padding-left: 12rpx;
color: #999;
flex-shrink: 0;
}
}
.time {
.time {
font-size: 24rpx;
color: #999;
margin-left: 16rpx;
flex-shrink: 0;
}
}
.message-preview {
.message-preview {
display: flex;
align-items: center;
}
}
.preview-text {
.preview-text {
font-size: 26rpx;
color: #999;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
}
.load-more {
.load-more {
padding: 20rpx 0;
text-align: center;
}
}
.load-more-text {
.load-more-text {
font-size: 24rpx;
color: #999;
}
}
</style>

View File

@ -42,8 +42,8 @@
</view>
<view class="px-15 text-base text-gray leading-normal text-center">进入团队首页即可发起线上咨询建档授权等服务</view>
<view class="mt-10 flex px-15">
<view class="mr-10 border-auto rounded py-10 text-base text-primary text-center flex-grow">保存图片</view>
<view class="bg-primary rounded py-10 text-base text-white text-center flex-grow">分享微信</view>
<view class="mr-10 border-auto rounded py-10 text-base text-primary text-center flex-grow" @click="saveImage">保存图片</view>
<button class=" bg-primary rounded py-10 text-base text-white text-center flex-grow" open-type="share">分享微信</button>
</view>
</view>
</view>
@ -56,7 +56,7 @@ import { onLoad } from "@dcloudio/uni-app";
import useAccountStore from "@/store/account.js";
import useGuard from '@/hooks/useGuard';
import api from "@/utils/api.js";
import { toast } from "@/utils/widget";
import { toast, saveImageToPhotosAlbum, shareToWeChat } from "@/utils/widget";
import emptyData from "@/components/empty-data.vue";
import renamePopup from "./rename-popup.vue";
@ -69,6 +69,7 @@ const current = ref(0);
const list = ref([]);
const visible = ref(false);
const teamId = ref('')
const qrcodes = ref(null);
const indicator = computed(() => ({
prev: current.value > 0,
next: current.value < list.value.length - 1
@ -113,6 +114,65 @@ async function change(name) {
}
}
//
async function saveImage() {
if (!team.value || !team.value.qrcode) {
toast('暂无二维码');
return;
}
try {
const qrcodeComponent = qrcodes.value[current.value];
if (!qrcodeComponent) {
toast('二维码未加载完成');
return;
}
//
const tempFilePath = qrcodeComponent.toTempFilePath();
if (tempFilePath) {
await saveImageToPhotosAlbum(tempFilePath);
} else {
toast('获取二维码失败');
}
} catch (err) {
console.error('保存图片失败:', err);
toast('保存失败');
}
}
//
function onShareAppMessage() {
if (!team.value) {
return shareToWeChat({
title: '邀请患者加入团队',
path: '/pages/work/team/invite/invite-patient'
});
}
return shareToWeChat({
title: `邀请您加入${team.value.name}`,
path: `/pages/work/team/invite/invite-patient?teamId=${team.value.teamId}`,
imageUrl: team.value.qrcode || ''
});
}
//
function onShareTimeline() {
if (!team.value) {
return {
title: '邀请患者加入团队',
path: '/pages/work/team/invite/invite-patient'
};
}
return {
title: `邀请您加入${team.value.name}`,
query: `teamId=${team.value.teamId}`,
imageUrl: team.value.qrcode || ''
};
}
onLoad(opts => {
teamId.value = opts.teamId || '';
})
@ -121,6 +181,12 @@ useShow(() => {
getTeams()
})
// 使
defineExpose({
onShareAppMessage,
onShareTimeline
})
</script>
<style>
.w-100 {
@ -148,4 +214,15 @@ useShow(() => {
.h-30 {
height: 60rpx;
}
.share-btn {
border: none;
padding: 0;
line-height: normal;
background: transparent;
}
.share-btn::after {
border: none;
}
</style>

View File

@ -0,0 +1,234 @@
# 微信小程序分享功能使用指南
## 功能说明
提供了完整的微信小程序分享功能,包括:
- 分享给好友
- 分享到朋友圈
- 保存图片到相册
## 使用方法
### 1. 基础分享(在页面中)
```vue
<template>
<view>
<button open-type="share">分享给好友</button>
</view>
</template>
<script setup>
import { createShareMessage, createShareTimeline } from '@/utils/share'
// 分享给好友
function onShareAppMessage() {
return createShareMessage({
title: '分享标题',
path: '/pages/index/index?id=123',
imageUrl: 'https://example.com/share.jpg'
})
}
// 分享到朋友圈(需要在 app.json 中配置)
function onShareTimeline() {
return createShareTimeline({
title: '朋友圈标题',
query: 'id=123',
imageUrl: 'https://example.com/share.jpg'
})
}
// 导出分享方法
defineExpose({
onShareAppMessage,
onShareTimeline
})
</script>
```
### 2. 使用分享组件
```vue
<template>
<view>
<share-actions
@save="handleSave"
:show-save="true"
:show-share="true"
save-text="保存图片"
share-text="分享微信"
/>
</view>
</template>
<script setup>
import { saveImageToAlbum, createShareMessage } from '@/utils/share'
import shareActions from '@/components/share-actions.vue'
// 保存图片
async function handleSave() {
const imagePath = 'https://example.com/image.jpg'
await saveImageToAlbum(imagePath)
}
// 分享配置
function onShareAppMessage() {
return createShareMessage({
title: '分享标题',
path: '/pages/index/index'
})
}
defineExpose({
onShareAppMessage
})
</script>
```
### 3. 保存二维码图片
```vue
<template>
<view>
<uqrcode ref="qrcode" canvasId="qrcode" :value="qrcodeUrl" />
<button @click="saveQrcode">保存二维码</button>
</view>
</template>
<script setup>
import { ref } from 'vue'
import { saveImageToAlbum } from '@/utils/share'
import { toast } from '@/utils/widget'
const qrcode = ref(null)
const qrcodeUrl = ref('https://example.com')
async function saveQrcode() {
try {
if (!qrcode.value) {
toast('二维码未加载完成')
return
}
// 获取二维码临时文件路径
const tempFilePath = qrcode.value.toTempFilePath()
if (tempFilePath) {
await saveImageToAlbum(tempFilePath)
} else {
toast('获取二维码失败')
}
} catch (err) {
console.error('保存失败:', err)
toast('保存失败')
}
}
</script>
```
### 4. 动态分享内容
```vue
<script setup>
import { ref, computed } from 'vue'
import { createShareMessage } from '@/utils/share'
const currentItem = ref({
id: '123',
title: '商品标题',
image: 'https://example.com/product.jpg'
})
// 动态生成分享配置
function onShareAppMessage() {
return createShareMessage({
title: currentItem.value.title,
path: `/pages/detail/detail?id=${currentItem.value.id}`,
imageUrl: currentItem.value.image
})
}
defineExpose({
onShareAppMessage
})
</script>
```
## 配置说明
### 1. 启用分享到朋友圈
`pages.json` 中配置页面:
```json
{
"path": "pages/index/index",
"style": {
"navigationBarTitleText": "首页",
"enableShareTimeline": true
}
}
```
### 2. 全局分享配置
`App.vue` 中配置全局分享:
```vue
<script>
export default {
onShareAppMessage() {
return {
title: '默认分享标题',
path: '/pages/index/index'
}
},
onShareTimeline() {
return {
title: '默认朋友圈标题'
}
}
}
</script>
```
## API 说明
### createShareMessage(options)
创建分享给好友的配置
**参数:**
- `title` (string): 分享标题
- `path` (string): 分享路径
- `imageUrl` (string): 分享图片URL
**返回:** 分享配置对象
### createShareTimeline(options)
创建分享到朋友圈的配置
**参数:**
- `title` (string): 分享标题
- `query` (string): 分享路径参数
- `imageUrl` (string): 分享图片URL
**返回:** 分享配置对象
### saveImageToAlbum(filePath)
保存图片到相册
**参数:**
- `filePath` (string): 图片路径(本地临时路径或网络路径)
**返回:** Promise<boolean>
## 注意事项
1. 分享图片建议尺寸5:4推荐 500x400px
2. 分享路径必须是已注册的页面路径
3. 保存图片需要用户授权相册权限
4. 分享到朋友圈需要在页面配置中启用
5. 网络图片会自动下载后保存到相册

169
utils/share.js Normal file
View File

@ -0,0 +1,169 @@
/**
* 微信小程序分享工具
*/
import { toast } from './widget'
/**
* 创建分享到好友的配置
* @param {Object} options 分享配置
* @param {string} options.title 分享标题
* @param {string} options.path 分享路径
* @param {string} options.imageUrl 分享图片URL
* @returns {Object} 分享配置对象
*/
export function createShareMessage(options = {}) {
const { title = '', path = '', imageUrl = '' } = options
return {
title,
path,
imageUrl,
success: () => {
toast('分享成功')
},
fail: (err) => {
console.error('分享失败:', err)
toast('分享失败')
}
}
}
/**
* 创建分享到朋友圈的配置
* @param {Object} options 分享配置
* @param {string} options.title 分享标题
* @param {string} options.query 分享路径参数
* @param {string} options.imageUrl 分享图片URL
* @returns {Object} 分享配置对象
*/
export function createShareTimeline(options = {}) {
const { title = '', query = '', imageUrl = '' } = options
return {
title,
query,
imageUrl
}
}
/**
* 在页面中启用分享功能
* 使用方法在页面的 setup 中调用
*
* @example
* import { enableShare } from '@/utils/share'
*
* // 在 setup 中
* enableShare({
* message: {
* title: '分享标题',
* path: '/pages/index/index',
* imageUrl: 'https://example.com/image.jpg'
* },
* timeline: {
* title: '朋友圈标题',
* query: 'id=123',
* imageUrl: 'https://example.com/image.jpg'
* }
* })
*/
export function enableShare(config = {}) {
const { message, timeline } = config
// 分享给好友
if (message) {
uni.$on('onShareAppMessage', () => {
return createShareMessage(message)
})
}
// 分享到朋友圈
if (timeline) {
uni.$on('onShareTimeline', () => {
return createShareTimeline(timeline)
})
}
}
/**
* 保存图片到相册
* @param {string} filePath 图片路径本地临时路径或网络路径
*/
export async function saveImageToAlbum(filePath) {
try {
// 如果是网络图片,先下载
let localPath = filePath
if (filePath.startsWith('http')) {
const res = await uni.downloadFile({ url: filePath })
if (res[0]) {
throw new Error('下载图片失败')
}
localPath = res[1].tempFilePath
}
// 检查授权
const authRes = await uni.getSetting()
if (!authRes[1].authSetting['scope.writePhotosAlbum']) {
// 请求授权
try {
await uni.authorize({ scope: 'scope.writePhotosAlbum' })
} catch (err) {
// 用户拒绝授权,引导去设置
const [modalErr, modalRes] = await uni.showModal({
title: '提示',
content: '需要您授权保存相册',
confirmText: '去设置',
cancelText: '取消'
})
if (modalRes && modalRes.confirm) {
await uni.openSetting()
}
return false
}
}
// 保存图片
const [saveErr] = await uni.saveImageToPhotosAlbum({ filePath: localPath })
if (saveErr) {
throw saveErr
}
await toast('保存成功')
return true
} catch (err) {
console.error('保存图片失败:', err)
await toast('保存失败')
return false
}
}
/**
* 生成带参数的小程序码
* 需要后端接口支持
* @param {Object} options
* @param {string} options.scene 场景值
* @param {string} options.page 页面路径
* @returns {Promise<string>} 返回小程序码图片URL
*/
export async function generateMiniCode(options = {}) {
// 这里需要调用后端接口生成小程序码
// 示例代码,需要根据实际后端接口调整
try {
const res = await uni.request({
url: '/api/wechat/generateMiniCode',
method: 'POST',
data: options
})
if (res[0] || !res[1].data.success) {
throw new Error('生成小程序码失败')
}
return res[1].data.data.url
} catch (err) {
console.error('生成小程序码失败:', err)
throw err
}
}

View File

@ -51,3 +51,46 @@ export async function confirm(content, opt = {}) {
})
})
}
// 保存图片到相册
export async function saveImageToPhotosAlbum(filePath) {
try {
// 检查授权
const authRes = await uni.getSetting()
if (!authRes[1].authSetting['scope.writePhotosAlbum']) {
// 请求授权
try {
await uni.authorize({ scope: 'scope.writePhotosAlbum' })
} catch (err) {
await confirm('需要您授权保存相册', { title: '提示', showCancel: false })
await uni.openSetting()
return
}
}
// 保存图片
await uni.saveImageToPhotosAlbum({ filePath })
await toast('保存成功')
} catch (err) {
console.error('保存图片失败:', err)
await toast('保存失败')
}
}
// 分享到微信
export function shareToWeChat(options = {}) {
const { title = '', path = '', imageUrl = '' } = options
return {
title,
path,
imageUrl,
success: () => {
toast('分享成功')
},
fail: (err) => {
console.error('分享失败:', err)
toast('分享失败')
}
}
}