瑶都万能墙
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

255 lines
5.2 KiB

  1. <template>
  2. <view class="qrcode-container">
  3. <navbar title="店铺二维码" leftClick @leftClick="$utils.navigateBack"/>
  4. <!-- 加载状态 -->
  5. <view class="loading-container" v-if="loading">
  6. <view class="loading-spinner"></view>
  7. <text class="loading-text">加载中...</text>
  8. </view>
  9. <!-- 二维码图片 -->
  10. <view class="qrcode-wrapper" v-if="qrCodeUrl">
  11. <image
  12. class="qrcode-image"
  13. :src="qrCodeUrl"
  14. mode="aspectFit"
  15. @load="imageLoaded"
  16. @error="imageError"
  17. @longpress="saveImage"
  18. ></image>
  19. <text class="qrcode-tip" v-if="!loading && qrCodeUrl">扫描二维码查看店铺详情</text>
  20. <text class="save-tip" v-if="!loading && qrCodeUrl">长按图片可保存到相册</text>
  21. </view>
  22. <!-- 错误状态 -->
  23. <view class="error-container" v-if="!loading && !qrCodeUrl && hasError">
  24. <text class="error-text">二维码加载失败请重试</text>
  25. <button class="retry-button" @click="loadQrCode">重新加载</button>
  26. </view>
  27. </view>
  28. </template>
  29. <script>
  30. export default {
  31. data() {
  32. return {
  33. shopId: '',
  34. qrCodeUrl: '',
  35. loading: true,
  36. hasError: false,
  37. }
  38. },
  39. onLoad(options) {
  40. if (options.id) {
  41. this.shopId = options.id;
  42. this.loadQrCode();
  43. } else {
  44. this.loading = false;
  45. this.hasError = true;
  46. uni.showToast({
  47. title: '缺少店铺ID参数',
  48. icon: 'none'
  49. });
  50. }
  51. },
  52. methods: {
  53. // 加载二维码
  54. loadQrCode() {
  55. if (!this.shopId) return;
  56. this.loading = true;
  57. this.hasError = false;
  58. // 直接使用图片链接请求二维码
  59. this.qrCodeUrl = `${this.$config.baseUrl}/city/shop/shopQrCode?id=${this.shopId}`;
  60. // 由于image标签会自动处理加载状态,我们在imageLoaded和imageError中处理状态变化
  61. },
  62. // 图片加载成功
  63. imageLoaded() {
  64. this.loading = false;
  65. },
  66. // 图片加载失败
  67. imageError() {
  68. this.loading = false;
  69. this.hasError = true;
  70. this.qrCodeUrl = '';
  71. uni.showToast({
  72. title: '二维码加载失败',
  73. icon: 'none'
  74. });
  75. },
  76. // 保存图片到相册
  77. saveImage() {
  78. if (!this.qrCodeUrl) return;
  79. uni.showLoading({
  80. title: '保存中...'
  81. });
  82. // 下载图片
  83. uni.downloadFile({
  84. url: this.qrCodeUrl,
  85. success: (res) => {
  86. if (res.statusCode === 200) {
  87. // 保存图片到相册
  88. uni.saveImageToPhotosAlbum({
  89. filePath: res.tempFilePath,
  90. success: () => {
  91. uni.hideLoading();
  92. uni.showToast({
  93. title: '保存成功',
  94. icon: 'success'
  95. });
  96. },
  97. fail: (err) => {
  98. uni.hideLoading();
  99. console.error('保存图片失败:', err);
  100. // 如果是用户拒绝授权导致的失败
  101. if (err.errMsg.indexOf('auth deny') >= 0 || err.errMsg.indexOf('authorize') >= 0) {
  102. this.showAuthModal();
  103. } else {
  104. uni.showToast({
  105. title: '保存失败',
  106. icon: 'none'
  107. });
  108. }
  109. }
  110. });
  111. } else {
  112. uni.hideLoading();
  113. uni.showToast({
  114. title: '图片下载失败',
  115. icon: 'none'
  116. });
  117. }
  118. },
  119. fail: () => {
  120. uni.hideLoading();
  121. uni.showToast({
  122. title: '图片下载失败',
  123. icon: 'none'
  124. });
  125. }
  126. });
  127. },
  128. // 显示授权提示弹窗
  129. showAuthModal() {
  130. uni.showModal({
  131. title: '提示',
  132. content: '保存图片需要您授权访问相册权限',
  133. confirmText: '去授权',
  134. cancelText: '取消',
  135. success: (res) => {
  136. if (res.confirm) {
  137. // 打开设置页面
  138. uni.openSetting({
  139. success: (settingRes) => {
  140. console.log('设置页面成功打开:', settingRes);
  141. }
  142. });
  143. }
  144. }
  145. });
  146. }
  147. }
  148. }
  149. </script>
  150. <style lang="scss" scoped>
  151. .qrcode-container {
  152. display: flex;
  153. flex-direction: column;
  154. align-items: center;
  155. min-height: 100vh;
  156. background-color: #f5f5f5;
  157. padding: 30rpx;
  158. }
  159. .loading-container {
  160. display: flex;
  161. flex-direction: column;
  162. justify-content: center;
  163. align-items: center;
  164. margin-top: 200rpx;
  165. .loading-spinner {
  166. width: 80rpx;
  167. height: 80rpx;
  168. border: 6rpx solid #f3f3f3;
  169. border-top: 6rpx solid #3B5CF0;
  170. border-radius: 50%;
  171. animation: spin 1s linear infinite;
  172. margin-bottom: 20rpx;
  173. }
  174. .loading-text {
  175. font-size: 28rpx;
  176. color: #666;
  177. }
  178. @keyframes spin {
  179. 0% { transform: rotate(0deg); }
  180. 100% { transform: rotate(360deg); }
  181. }
  182. }
  183. .qrcode-wrapper {
  184. display: flex;
  185. flex-direction: column;
  186. align-items: center;
  187. margin-top: 100rpx;
  188. background-color: #fff;
  189. padding: 40rpx;
  190. border-radius: 16rpx;
  191. box-shadow: 0 4rpx 16rpx rgba(0, 0, 0, 0.1);
  192. .qrcode-image {
  193. width: 500rpx;
  194. height: 500rpx;
  195. margin-bottom: 30rpx;
  196. }
  197. .qrcode-tip {
  198. font-size: 28rpx;
  199. color: #666;
  200. margin-top: 20rpx;
  201. }
  202. .save-tip {
  203. font-size: 24rpx;
  204. color: $uni-color-primary;
  205. margin-top: 10rpx;
  206. }
  207. }
  208. .error-container {
  209. display: flex;
  210. flex-direction: column;
  211. align-items: center;
  212. margin-top: 200rpx;
  213. .error-text {
  214. font-size: 28rpx;
  215. color: #999;
  216. margin-bottom: 30rpx;
  217. }
  218. .retry-button {
  219. background-color: #3B5CF0;
  220. color: #fff;
  221. font-size: 28rpx;
  222. padding: 16rpx 40rpx;
  223. border-radius: 40rpx;
  224. border: none;
  225. }
  226. }
  227. </style>