自主产品,供应链食堂系统。将两个端拆开了。
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.

284 lines
7.3 KiB

  1. <template>
  2. <view>
  3. <swiper class="swiper" :animation="animationData"
  4. :style="{ 'opacity': animation ? '0' : '1',
  5. 'background-color': perspective ? 'rgba(0,0,0,0.5)' : '#000000'}"
  6. :current="swiper_current" :circular="circular"
  7. v-if="show_assembly" @click="hideAssembly"
  8. @touchmove.stop.prevent="touchmove"
  9. @change="e => switchItem(e.detail.current)">
  10. <swiper-item v-for="(item, index) in images" :key="index"
  11. class="swiper-item">
  12. <!-- 可移动缩放操作的容器 -->
  13. <movable-area class="movable-area" :scale-area="true">
  14. <movable-view class="movable-view" direction="all" :scale="true" :inertia="true" @scale="scale" :scale-value="scale_values[swiper_current]">
  15. <image :src="item" class="swiper-image" mode="aspectFit" @mousewheel.stop="mousewheel" @longpress="longpress"></image>
  16. </movable-view>
  17. </movable-area>
  18. <!-- 控制器 -->
  19. <view class="controls" v-if="controls">
  20. <view>
  21. <view @click.stop="switchItem(swiper_current - 1)" v-if="swiper_current != 0">
  22. <uni-icons type="arrowleft" size="36" color="#fff"></uni-icons>
  23. </view>
  24. <view v-else></view>
  25. </view>
  26. <view>
  27. <view @click.stop="switchItem(swiper_current + 1)" v-if="swiper_current != images.length - 1">
  28. <uni-icons type="arrowright" size="36" color="#fff"></uni-icons>
  29. </view>
  30. <view v-else></view>
  31. </view>
  32. </view>
  33. <!-- 指示器 -->
  34. <view class="indicators" v-if="indicators && images.length > 1">
  35. <view class="number" v-if="indicatorType == 'number'">{{ swiper_current + 1 }} / {{ images.length }}</view>
  36. <view class="square" v-else-if="indicatorType == 'square'">
  37. <view v-for="(item, index) in images" :key="index" class="indicators-icon" :style="swiper_current == index ? 'background-color:'+ themeColor : ''"></view>
  38. </view>
  39. <view class="dot" v-else-if="indicatorType == 'dot'">
  40. <view v-for="(item, index) in images" :key="index" class="indicators-icon" :style="swiper_current == index ? 'background-color:'+ themeColor : ''"></view>
  41. </view>
  42. </view>
  43. <!-- 底部菜单弹出层 -->
  44. <view class="menu-popup" v-if="menu && show_menu" @click.stop>
  45. <button @click="download">保存图片</button>
  46. </view>
  47. </swiper-item>
  48. </swiper>
  49. </view>
  50. </template>
  51. <script>
  52. export default {
  53. props: {
  54. controls: {
  55. type: Boolean, // 是否显示左右箭头控制
  56. default: true
  57. },
  58. indicators: {
  59. type: Boolean,
  60. default: true // 是否显示指示器
  61. },
  62. indicatorType: {
  63. type: String, // 指示器类型,dot圆点,square方形,number数字
  64. default: 'number'
  65. },
  66. perspective: {
  67. type: Boolean, // 是否开启背景透视,遮罩透明可以看见底下其他元素
  68. default: true
  69. },
  70. circular: {
  71. type: Boolean, // 是否可以衔接滑动
  72. default: false
  73. },
  74. themeColor: {
  75. type: String, // 主题色
  76. default: '#1833F2'
  77. },
  78. animation: {
  79. type: Boolean, // 是否开启动画
  80. default: false
  81. },
  82. menu: {
  83. type: Boolean, // 长按图片时是否显示菜单按钮,H5不支持,传参无效果
  84. default: true
  85. }
  86. },
  87. data(){
  88. return {
  89. swiper_current: 0, // 当前显示的图片下标
  90. images: [],
  91. show_assembly: false, // 显示预览图片
  92. duration: 500, // 动画持续时间
  93. animationObj: {}, // 动画配置对象
  94. animationData: {}, // 动画导出执行对象
  95. scale_values: [], // 每张图的缩放比例
  96. clientY: 0,
  97. show_menu: false
  98. }
  99. },
  100. created(){
  101. // TODO PC端屏蔽遮罩滚动穿透
  102. // TODO PC端滚轮放大缩小
  103. // TODO 双击时会被关掉
  104. },
  105. methods: {
  106. // 初始化加载动画
  107. initAnimation(){
  108. let animationObj = uni.createAnimation({
  109. duration: this.duration
  110. });
  111. this.animationObj = animationObj;
  112. animationObj.opacity(0).step();
  113. animationObj.opacity(1).step();
  114. this.animationData = animationObj.export();
  115. },
  116. // swiper touchmove事件
  117. touchmove(){
  118. return false;
  119. },
  120. // swiper被改变
  121. switchItem(current){
  122. this.swiper_current = current;
  123. // this.clientY = 0; // 切换时需要复位
  124. },
  125. // movable 缩放事件
  126. scale(event){
  127. let scale = parseInt(event.detail.scale * 100) +'%';
  128. uni.showToast({
  129. title: scale,
  130. icon: 'none',
  131. position: 'bottom'
  132. })
  133. },
  134. // 鼠标滚动缩放事件,目前会触发浏览器页面滚动事件,暂时先不要这个功能
  135. mousewheel(event){
  136. return;
  137. let scale = this.scale_values[this.swiper_current];
  138. if(this.clientY > event.clientY){
  139. if(scale < 10){
  140. scale += 0.5;
  141. }
  142. }else{
  143. if(scale > 0.5){
  144. scale -= 0.5;
  145. }
  146. }
  147. this.clientY = event.clientY;
  148. this.scale_values.splice(this.swiper_current, 1, scale);
  149. },
  150. // 显示图片
  151. show(options){
  152. this.images = options.images || [];
  153. this.swiper_current = options.current || 0;
  154. this.scale_values = this.images.map(item => 1);
  155. this.show_assembly = true;
  156. if(this.$props.animation){
  157. this.initAnimation();
  158. }
  159. },
  160. // 关闭隐藏图片
  161. hideAssembly(){
  162. if(this.$props.animation){
  163. this.animationObj.opacity(0).step();
  164. this.animationData = this.animationObj.export();
  165. setTimeout(() => {
  166. this.show_assembly = false;
  167. }, this.duration);
  168. }else{
  169. this.show_assembly = false;
  170. }
  171. },
  172. // 长按事件
  173. longpress(event){
  174. // #ifndef H5
  175. if(this.$props.menu){
  176. this.show_menu = true;
  177. }
  178. // #endif
  179. },
  180. // 保存图片
  181. download(){
  182. uni.showLoading({
  183. title: '正在保存'
  184. })
  185. this.show_menu = false;
  186. uni.downloadFile({
  187. url: this.images[this.swiper_current],
  188. success: res => {
  189. let tempFilePath = res.tempFilePath;
  190. uni.saveFile({
  191. tempFilePath,
  192. success: result => {
  193. uni.showToast({
  194. title: '保存成功',
  195. icon: 'success'
  196. })
  197. },
  198. fail: err => {
  199. uni.showToast({
  200. title: '保存失败',
  201. icon: 'none'
  202. })
  203. },
  204. complete: () => uni.hideLoading()
  205. })
  206. },
  207. complete: () => uni.hideLoading()
  208. })
  209. }
  210. }
  211. }
  212. </script>
  213. <style lang="scss" scoped="scoped">
  214. .swiper{
  215. width: 100vw;
  216. height: 100vh;
  217. position: fixed;
  218. top: 0;
  219. left: 0;
  220. z-index: 1000;
  221. .swiper-item{
  222. position: relative;
  223. .movable-area, .movable-view, .swiper-image{
  224. width: 100%;
  225. height: 100%;
  226. }
  227. .controls{
  228. position: absolute;
  229. padding: 0 16rpx;
  230. box-sizing: border-box;
  231. display: flex;
  232. justify-content: space-between;
  233. top: 47vh;
  234. left: 0;
  235. font-size: 40rpx;
  236. width: 100%;
  237. z-index: 1002;
  238. }
  239. .indicators{
  240. width: 100%;
  241. position: absolute;
  242. left: 0;
  243. bottom: 10vh;
  244. z-index: 1002;
  245. display: flex;
  246. justify-content: center;
  247. .number, .square, .dot{
  248. width: max-content;
  249. height: max-content;
  250. background-color: #dfe4ea;
  251. }
  252. .number{
  253. padding: 4rpx 26rpx;
  254. border-radius: 40rpx;
  255. color: #555555;
  256. }
  257. .square{
  258. border-radius: 40rpx;
  259. display: flex;
  260. padding: 2rpx 4rpx;
  261. view{
  262. border-radius: 50%;
  263. }
  264. }
  265. .dot{
  266. display: flex;
  267. padding: 4rpx 4rpx;
  268. }
  269. .indicators-icon{
  270. width: 20rpx;
  271. height: 20rpx;
  272. background-color: #bdc5bd;
  273. margin: 2rpx;
  274. }
  275. }
  276. }
  277. }
  278. // 轻提示框样式
  279. /deep/.uni-sample-toast{
  280. z-index: 1002;
  281. }
  282. </style>