This commit is contained in:
18631081161 2026-03-01 18:09:05 +08:00
parent 91f22008ed
commit 85a3107a38
2 changed files with 290 additions and 277 deletions

View File

@ -1,304 +1,317 @@
<template>
<view class="butler-page">
<!-- 页面加载状态 -->
<Loading type="page" :loading="pageLoading" />
<!-- 顶部背景图 -->
<view class="top-bg">
<image src="/static/title_bg.png" mode="aspectFill" class="bg-img" />
</view>
<!-- 自定义导航栏 -->
<view class="custom-navbar" :style="{ paddingTop: statusBarHeight + 'px' }">
<view class="navbar-content">
<view class="navbar-back" @click="handleBack">
<text class="back-icon"></text>
</view>
<text class="navbar-title">联系我们</text>
<view class="navbar-placeholder"></view>
</view>
</view>
<!-- 内容区域 -->
<view class="content-area" :style="{ marginTop: (statusBarHeight + 44) + 'px' }">
<view class="qrcode-card">
<!-- 二维码图片 -->
<view class="qrcode-wrapper">
<image
v-if="butlerQrcode"
class="qrcode-img"
:src="butlerQrcode"
mode="aspectFit"
@click="previewQrcode"
/>
<view v-else class="qrcode-placeholder">
<text>暂无二维码</text>
</view>
</view>
<!-- 保存按钮 -->
<button class="btn-save" @click="saveQrcode" :disabled="!butlerQrcode || saving">
{{ saving ? '保存中...' : '保存图片' }}
</button>
</view>
</view>
</view>
<view class="butler-page">
<!-- 页面加载状态 -->
<Loading type="page" :loading="pageLoading" />
<!-- 顶部背景图 -->
<view class="top-bg">
<image src="/static/title_bg.png" mode="aspectFill" class="bg-img" />
</view>
<!-- 自定义导航栏 -->
<view class="custom-navbar" :style="{ paddingTop: statusBarHeight + 'px' }">
<view class="navbar-content">
<view class="navbar-back" @click="handleBack">
<text class="back-icon"></text>
</view>
<text class="navbar-title">联系我们</text>
<view class="navbar-placeholder"></view>
</view>
</view>
<!-- 内容区域 - 可滚动 -->
<scroll-view class="content-area" scroll-y
:style="{ marginTop: (statusBarHeight + 44) + 'px', height: 'calc(100vh - ' + (statusBarHeight + 44) + 'px)' }">
<view class="content-wrapper">
<view class="qrcode-card">
<!-- 二维码图片 -->
<view class="qrcode-wrapper">
<image v-if="butlerQrcode" class="qrcode-img" :src="butlerQrcode" mode="widthFix"
@click="previewQrcode" />
<view v-else class="qrcode-placeholder">
<text>暂无二维码</text>
</view>
</view>
<!-- 保存按钮 -->
<button class="btn-save" @click="saveQrcode" :disabled="!butlerQrcode || saving">
{{ saving ? '保存中...' : '保存图片' }}
</button>
</view>
</view>
</scroll-view>
</view>
</template>
<script setup>
import { ref, computed, onMounted } from 'vue'
import { useConfigStore } from '@/store/config.js'
import { getFullImageUrl } from '@/utils/image.js'
import Loading from '@/components/Loading/index.vue'
import {
ref,
computed,
onMounted
} from 'vue'
import {
useConfigStore
} from '@/store/config.js'
import {
getFullImageUrl
} from '@/utils/image.js'
import Loading from '@/components/Loading/index.vue'
const configStore = useConfigStore()
const configStore = useConfigStore()
//
const statusBarHeight = ref(20)
//
const statusBarHeight = ref(20)
//
const pageLoading = ref(true)
const saving = ref(false)
//
const pageLoading = ref(true)
const saving = ref(false)
//
const butlerQrcode = computed(() => {
const qrcode = configStore.butlerQrcode
return qrcode ? getFullImageUrl(qrcode) : ''
})
//
const butlerQrcode = computed(() => {
const qrcode = configStore.butlerQrcode
return qrcode ? getFullImageUrl(qrcode) : ''
})
//
const getSystemInfo = () => {
try {
const res = uni.getSystemInfoSync()
statusBarHeight.value = res.statusBarHeight || 20
} catch (error) {
console.error('获取系统信息失败:', error)
}
}
//
const getSystemInfo = () => {
try {
const res = uni.getSystemInfoSync()
statusBarHeight.value = res.statusBarHeight || 20
} catch (error) {
console.error('获取系统信息失败:', error)
}
}
//
const handleBack = () => {
uni.navigateBack()
}
//
const handleBack = () => {
uni.navigateBack()
}
//
const previewQrcode = () => {
if (!butlerQrcode.value) return
uni.previewImage({
urls: [butlerQrcode.value]
})
}
//
const previewQrcode = () => {
if (!butlerQrcode.value) return
uni.previewImage({
urls: [butlerQrcode.value]
})
}
//
const saveQrcode = async () => {
if (!butlerQrcode.value || saving.value) return
saving.value = true
try {
//
const downloadRes = await new Promise((resolve, reject) => {
uni.downloadFile({
url: butlerQrcode.value,
success: resolve,
fail: reject
})
})
if (downloadRes.statusCode !== 200) {
throw new Error('下载图片失败')
}
//
await new Promise((resolve, reject) => {
uni.saveImageToPhotosAlbum({
filePath: downloadRes.tempFilePath,
success: resolve,
fail: (err) => {
//
if (err.errMsg.includes('auth deny') || err.errMsg.includes('authorize')) {
uni.showModal({
title: '提示',
content: '需要您授权保存图片到相册',
confirmText: '去设置',
success: (res) => {
if (res.confirm) {
uni.openSetting()
}
}
})
}
reject(err)
}
})
})
uni.showToast({
title: '保存成功',
icon: 'success'
})
} catch (error) {
console.error('保存图片失败:', error)
if (!error.errMsg?.includes('auth')) {
uni.showToast({
title: '保存失败',
icon: 'none'
})
}
} finally {
saving.value = false
}
}
//
const saveQrcode = async () => {
if (!butlerQrcode.value || saving.value) return
//
const initPage = async () => {
pageLoading.value = true
try {
getSystemInfo()
//
configStore.isLoaded = false
await configStore.loadAppConfig()
} catch (error) {
console.error('初始化页面失败:', error)
} finally {
pageLoading.value = false
}
}
saving.value = true
onMounted(() => {
initPage()
})
try {
//
const downloadRes = await new Promise((resolve, reject) => {
uni.downloadFile({
url: butlerQrcode.value,
success: resolve,
fail: reject
})
})
if (downloadRes.statusCode !== 200) {
throw new Error('下载图片失败')
}
//
await new Promise((resolve, reject) => {
uni.saveImageToPhotosAlbum({
filePath: downloadRes.tempFilePath,
success: resolve,
fail: (err) => {
//
if (err.errMsg.includes('auth deny') || err.errMsg.includes(
'authorize')) {
uni.showModal({
title: '提示',
content: '需要您授权保存图片到相册',
confirmText: '去设置',
success: (res) => {
if (res.confirm) {
uni.openSetting()
}
}
})
}
reject(err)
}
})
})
uni.showToast({
title: '保存成功',
icon: 'success'
})
} catch (error) {
console.error('保存图片失败:', error)
if (!error.errMsg?.includes('auth')) {
uni.showToast({
title: '保存失败',
icon: 'none'
})
}
} finally {
saving.value = false
}
}
//
const initPage = async () => {
pageLoading.value = true
try {
getSystemInfo()
//
configStore.isLoaded = false
await configStore.loadAppConfig()
} catch (error) {
console.error('初始化页面失败:', error)
} finally {
pageLoading.value = false
}
}
onMounted(() => {
initPage()
})
</script>
<style lang="scss" scoped>
.butler-page {
height: 100vh;
background-color: #f5f6fa;
overflow: hidden;
}
.butler-page {
height: 100vh;
background-color: #f5f6fa;
overflow: hidden;
}
//
.top-bg {
position: fixed;
top: 0;
left: 0;
right: 0;
height: 400rpx;
z-index: 0;
//
.top-bg {
position: fixed;
top: 0;
left: 0;
right: 0;
height: 400rpx;
z-index: 0;
.bg-img {
width: 100%;
height: 100%;
}
}
.bg-img {
width: 100%;
height: 100%;
}
}
//
.custom-navbar {
position: fixed;
top: 0;
left: 0;
right: 0;
z-index: 100;
//
.custom-navbar {
position: fixed;
top: 0;
left: 0;
right: 0;
z-index: 100;
.navbar-content {
height: 44px;
display: flex;
align-items: center;
justify-content: space-between;
padding: 0 24rpx;
.navbar-content {
height: 44px;
display: flex;
align-items: center;
justify-content: space-between;
padding: 0 24rpx;
.navbar-back {
width: 80rpx;
height: 80rpx;
display: flex;
align-items: center;
justify-content: center;
.navbar-back {
width: 80rpx;
height: 80rpx;
display: flex;
align-items: center;
justify-content: center;
.back-icon {
font-size: 56rpx;
color: #333;
font-weight: 300;
}
}
.back-icon {
font-size: 56rpx;
color: #333;
font-weight: 300;
}
}
.navbar-title {
font-size: 34rpx;
font-weight: 600;
color: #333;
}
.navbar-title {
font-size: 34rpx;
font-weight: 600;
color: #333;
}
.navbar-placeholder {
width: 80rpx;
}
}
}
.navbar-placeholder {
width: 80rpx;
}
}
}
//
.content-area {
position: relative;
z-index: 1;
padding: 40rpx 30rpx;
}
//
.content-area {
position: relative;
z-index: 1;
}
//
.qrcode-card {
background: #fff;
border-radius: 24rpx;
padding: 60rpx 40rpx;
display: flex;
flex-direction: column;
align-items: center;
box-shadow: 0 4rpx 20rpx rgba(0, 0, 0, 0.05);
}
//
.content-wrapper {
padding: 40rpx 0 80rpx;
}
//
.qrcode-wrapper {
width: 480rpx;
height: 480rpx;
display: flex;
align-items: center;
justify-content: center;
margin-bottom: 60rpx;
.qrcode-img {
width: 100%;
height: 100%;
}
.qrcode-placeholder {
width: 100%;
height: 100%;
background: #f5f5f5;
border-radius: 16rpx;
display: flex;
align-items: center;
justify-content: center;
text {
font-size: 28rpx;
color: #999;
}
}
}
//
.qrcode-card {
background: #fff;
width: 90%;
margin: 0 auto;
border-radius: 24rpx;
padding: 60rpx 0;
display: flex;
flex-direction: column;
align-items: center;
box-shadow: 0 4rpx 20rpx rgba(0, 0, 0, 0.05);
}
//
.btn-save {
width: 320rpx;
height: 88rpx;
line-height: 88rpx;
background: linear-gradient(135deg, #FFBDC2 0%, #FF8A93 100%);
border-radius: 44rpx;
font-size: 32rpx;
color: #fff;
border: none;
&::after {
border: none;
}
&[disabled] {
opacity: 0.6;
}
}
</style>
//
.qrcode-wrapper {
width: 100%;
max-width: 600rpx;
display: flex;
align-items: center;
justify-content: center;
margin-bottom: 60rpx;
.qrcode-img {
width: 100%;
display: block;
}
.qrcode-placeholder {
width: 100%;
height: 100%;
background: #f5f5f5;
border-radius: 16rpx;
display: flex;
align-items: center;
justify-content: center;
text {
font-size: 28rpx;
color: #999;
}
}
}
//
.btn-save {
width: 320rpx;
height: 88rpx;
line-height: 88rpx;
background: linear-gradient(135deg, #FFBDC2 0%, #FF8A93 100%);
border-radius: 44rpx;
font-size: 32rpx;
color: #fff;
border: none;
&::after {
border: none;
}
&[disabled] {
opacity: 0.6;
}
}
</style>

View File

@ -1430,7 +1430,7 @@
}
.btn-submit {
background: linear-gradient(135deg, #1890ff 0%, #096dd9 100%);
background: linear-gradient(135deg, #ff6b6b 0%, #ff5252 100%);
text {
color: #fff;
@ -1438,7 +1438,7 @@
}
.btn-done {
background: linear-gradient(135deg, #1890ff 0%, #096dd9 100%);
background: linear-gradient(135deg, #ff6b6b 0%, #ff5252 100%);
text {
color: #fff;