Browse Source

增加水印页面和登录页面

ChenGanBin 2 years ago
parent
commit
c94b25a59a

+ 2 - 2
src/app.config.ts

@@ -1,7 +1,7 @@
 export default defineAppConfig({
   pages: [
-    'pages/watermark/index',
-    'pages/index/index'
+    'pages/index/index',
+    'pages/login/index'
   ],
   window: {
     backgroundTextStyle: 'light',

+ 259 - 0
src/core/components/watermark.vue

@@ -0,0 +1,259 @@
+<template>
+  <view :class="classes" :style="{
+    zIndex,
+    backgroundSize: `${gapX + width}px`
+  }">
+  </view>
+</template>
+<script lang="ts">
+import { reactive, toRefs, computed, watch } from 'vue';
+import Taro from '@tarojs/taro';
+import { log } from 'console';
+export default {
+  props: {
+    name: {
+      type: String,
+      default: ''
+    },
+    gapY: {
+      type: Number,
+      default: 48
+    },
+    gapX: {
+      type: Number,
+      default: 24
+    },
+    zIndex: {
+      type: Number,
+      default: 2000
+    },
+    width: {
+      type: Number,
+      default: 120
+    },
+    height: {
+      type: Number,
+      default: 64
+    },
+    rotate: {
+      type: Number,
+      default: -22
+    },
+    image: {
+      type: String,
+      default: ''
+    },
+    imageWidth: {
+      type: Number,
+      default: 120
+    },
+    imageHeight: {
+      type: Number,
+      default: 64
+    },
+    content: {
+      type: String,
+      default: ''
+    },
+    fontColor: {
+      type: String,
+      default: 'rgba(0,0,0,.15)'
+    },
+    fontStyle: {
+      type: String,
+      default: 'normal'
+    },
+    fontFamily: {
+      type: String,
+      default: 'PingFang SC'
+    },
+    fontWeight: {
+      type: String,
+      default: 'normal'
+    },
+    fontSize: {
+      type: [String, Number],
+      default: 14
+    },
+    fullPage: {
+      type: Boolean,
+      default: ''
+    }
+  },
+  emits: ['click'],
+
+  setup(props) {
+    const state = reactive({
+      base64Url: ''
+    });
+    const {
+      zIndex,
+      gapX,
+      gapY,
+      width,
+      height,
+      rotate,
+      image,
+      imageWidth,
+      imageHeight,
+      content,
+      fontStyle,
+      fontWeight,
+      fontColor,
+      fontSize,
+      fontFamily
+    } = toRefs(props);
+
+    const init = async () => {
+      let ratio = 1;
+      Taro.getSystemInfo({
+        success(res) {
+          ratio = res.pixelRatio;
+        }
+      });
+      const canvasWidth = `${(gapX + width) * ratio}`;
+      const canvasHeight = `${(gapY + height) * ratio}`;
+      const markWidth = width * ratio;
+      const markHeight = height * ratio;
+      const canvas: Taro.OffscreenCanvas = Taro.createOffscreenCanvas({
+        type: '2d',
+        width: Number(canvasWidth),
+        height: Number(canvasHeight)
+      });
+      const ctx: any = canvas.getContext('2d');
+
+      if (ctx) {
+        if (image) {
+          // 创建一个图片
+          const img = canvas.createImage() as HTMLImageElement;
+          dealWithImage(ctx, img, ratio, ctx.canvas, markWidth, markHeight);
+        } else if (content) {
+          dealWithText(ctx, ratio, ctx.canvas, markWidth, markHeight);
+        }
+      } else {
+        throw new Error('当前环境不支持Canvas');
+      }
+    };
+    const initH5 = () => {
+      const canvas = document.createElement('canvas');
+      const ratio = window.devicePixelRatio;
+      const ctx = canvas.getContext('2d');
+      const canvasWidth = `${(gapX + width) * ratio}px`;
+      const canvasHeight = `${(gapY + height) * ratio}px`;
+      const markWidth = width * ratio;
+      const markHeight = height * ratio;
+      canvas.setAttribute('width', canvasWidth);
+      canvas.setAttribute('height', canvasHeight);
+
+      if (ctx) {
+        if (image) {
+          const img = new Image();
+          dealWithImage(ctx, img, ratio, canvas, markWidth, markHeight);
+        } else if (content) {
+          dealWithText(ctx, ratio, canvas, markWidth, markHeight);
+        }
+      } else {
+        throw new Error('当前环境不支持Canvas');
+      }
+    };
+    const dealWithImage = (
+      ctx: any,
+      img: HTMLImageElement,
+      ratio: number,
+      canvas: HTMLCanvasElement,
+      markWidth: number,
+      markHeight: number
+    ) => {
+      ctx.translate(markWidth / 2, markHeight / 2);
+      ctx.rotate((Math.PI / 180) * Number(rotate));
+      img.crossOrigin = 'anonymous';
+      img.referrerPolicy = 'no-referrer';
+      img.src = image; // 要加载的图片 url, 可以是base64
+      img.onload = () => {
+        ctx.drawImage(
+          img,
+          (-imageWidth * ratio) / 2,
+          (-imageHeight * ratio) / 2,
+          imageWidth * ratio,
+          imageHeight * ratio
+        );
+        ctx.restore();
+        state.base64Url = canvas.toDataURL();
+      };
+    };
+    const dealWithText = (
+      ctx: any,
+      ratio: number,
+      canvas: HTMLCanvasElement,
+      markWidth: number,
+      markHeight: number
+    ) => {
+      ctx.textBaseline = 'middle';
+      ctx.textAlign = 'center';
+      // 文字绕中间旋转
+      ctx.translate(markWidth / 2, markHeight / 2);
+      ctx.rotate((Math.PI / 180) * Number(rotate));
+      const markSize = Number(fontSize) * ratio;
+      ctx.font = `${fontStyle} normal ${fontWeight} ${markSize}px/${markHeight}px ${fontFamily}`;
+      ctx.fillStyle = fontColor;
+      ctx.fillText(content, 0, 0);
+      ctx.restore();
+      state.base64Url = canvas.toDataURL();
+    };
+    if (Taro.getEnv() === 'WEB') {
+      initH5();
+    } else {
+      init();
+    }
+
+    watch(
+      () => [
+        zIndex,
+        gapX,
+        gapY,
+        width,
+        height,
+        rotate,
+        image,
+        imageWidth,
+        imageHeight,
+        content,
+        fontStyle,
+        fontWeight,
+        fontColor,
+        fontSize,
+        fontFamily
+      ],
+      () => {
+        init();
+      }
+    );
+    const classes = computed(() => {
+      const prefixCls = 'watermark';
+      return {
+        [prefixCls]: true,
+        [`${prefixCls}-full-page`]: props.fullPage
+      };
+    });
+
+    return { ...toRefs(state), classes };
+  }
+};
+</script>
+
+<style lang="scss">
+.watermark {
+  position: absolute;
+  z-index: $watermark-z-index;
+  left: 0;
+  right: 0;
+  top: 0;
+  bottom: 0;
+  pointer-events: none;
+  background-repeat: repeat;
+
+  &-full-page {
+    position: fixed;
+  }
+}
+</style>

+ 0 - 0
src/pages/watermark/index.config.ts → src/pages/bak/index.config.ts


+ 162 - 0
src/pages/bak/index.vue

@@ -0,0 +1,162 @@
+<template>
+  <view class="index">
+    <view>
+      <img src="" alt="">
+    </view>
+    {{ msg }}
+    <Dongdong />
+    <view class="btn">
+      <nut-button type="primary" @click="handleClick('text', msg2, true)">点我</nut-button>
+      <nut-button type="primary" @click="login()">登录</nut-button>
+      <nut-button type="primary" @click="upload()">上传文件</nut-button>
+      <nut-button type="primary" @click="download()">下载文件</nut-button>
+      <nut-button type="primary" @click="http()">http封装</nut-button>
+    </view>
+    <nut-toast :msg="msg2" v-model:visible="show" :type="type" :cover="cover" />
+  </view>
+</template>
+
+<script lang="ts">
+// import JsonResponse from './vo/JsonResponse';
+import Taro from '@tarojs/taro';
+import { reactive, toRefs } from 'vue';
+import { Dongdong } from '@nutui/icons-vue-taro';
+import httpClient from '../../core/http';
+
+export default {
+  name: 'Index',
+  components: {
+    Dongdong
+  },
+  setup() {
+    const state = reactive({
+      msg: '欢迎使用 NutUI4.0 开发小程序',
+      msg2: '你成功了~',
+      type: 'text',
+      show: false,
+      cover: false
+    });
+
+    const handleClick = (type, msg, cover = false) => {
+      state.show = true;
+      state.msg2 = msg;
+      state.type = type;
+      state.cover = cover;
+    };
+
+    const login = () => {
+      var token = Taro.getStorageSync("token");
+      if (token) {
+        console.log("缓存中有token");
+        Taro.request({
+          url: BASE_URL + '/watermark/make',
+          header: {
+            "Authorization": token
+          },
+          method: "POST",
+          data: {},
+          success: function (ret) {
+            console.log("返回结果", ret);
+          }
+        })
+      } else {
+        console.log("缓存中没有token");
+        Taro.login({
+          success: function (res) {
+            console.log("登录拉拉了", res);
+            if (res.code) {
+              //发起网络请求
+              Taro.request({
+                url: 'http://localhost:8080/wechat/miniprogram/login',
+                method: "POST",
+                data: {
+                  code: res.code
+                },
+                success: function (ret) {
+                  console.log("返回结果", ret);
+                  // TODO 把token放入缓存
+                  Taro.setStorageSync("token", ret.data.data);
+                }
+              })
+            } else {
+              console.log('登录失败!' + res.errMsg)
+            }
+          }
+        });
+      }
+    };
+
+    const upload = () => {
+      Taro.chooseImage({
+        success(res) {
+          const tempFilePaths = res.tempFilePaths;
+          Taro.uploadFile({
+            url: 'http://localhost:8080/oss/upload', //仅为示例,非真实的接口地址
+            header: {
+              "Authorization": Taro.getStorageSync("token")
+            },
+            filePath: tempFilePaths[0],
+            name: 'files',
+            formData: {
+              'user': 'test'
+            },
+            success(ret) {
+              console.log("上传结果233", ret.data)
+              //do something
+              var data = JSON.parse(ret.data);
+              Taro.setStorageSync("attachmentId", data.data[0]);
+            }
+          })
+        }
+      })
+    };
+
+    const download = () => {
+      console.log("附件ID:", Taro.getStorageSync("attachmentId"))
+      Taro.downloadFile({
+        url: 'http://localhost:8080/oss/download?attachmentId=' + Taro.getStorageSync("attachmentId"), //仅为示例,并非真实的资源
+        header: {
+
+          "Authorization": Taro.getStorageSync("token")
+        },
+        success: function (res) {
+          // 只要服务器有响应数据,就会把响应内容写入文件并进入 success 回调,业务需要自行判断是否下载到了想要的内容
+          if (res.statusCode === 200) {
+            console.log("下载成功了", res);
+            Taro.previewMedia({
+              sources: [{
+                "url": res.tempFilePath,
+                "type": "image"
+              }]
+            })
+          }
+        }
+      })
+    };
+
+    const http = () => {
+      httpClient({
+        url: "/oss/test",
+        method: "GET",
+        data: {},
+        success(result: string) {
+          console.log("成功啦", result);
+        }
+      });
+    }
+
+    return {
+      ...toRefs(state), handleClick, login, upload, download, http
+    }
+  }
+}
+</script>
+
+<style lang="scss">
+.index {
+  font-family: "Avenir", Helvetica, Arial, sans-serif;
+  -webkit-font-smoothing: antialiased;
+  -moz-osx-font-smoothing: grayscale;
+  text-align: center;
+}
+</style>

+ 1 - 1
src/pages/index/index.config.ts

@@ -1,3 +1,3 @@
 export default definePageConfig({
-  navigationBarTitleText: '首页'
+  navigationBarTitleText: '水印'
 })

+ 48 - 136
src/pages/index/index.vue

@@ -1,162 +1,74 @@
 <template>
-  <view class="index">
-    <view>
-      <img src="" alt="">
+  <view class="wrap">
+    <nut-cell>
+      <view v-if="state.editMode">
+        <img :src="state.imgSrc" alt="" />
+        <watermark v-if="state.editMode" :fullPage="false" font-color="#fa2c19" :content="state.watermarkContent">
+        </watermark>
+      </view>
+      <nut-empty v-if="!state.editMode" image="empty" description="无内容"></nut-empty>
+    </nut-cell>
+    <nut-cell v-if="!state.editMode">
+      <nut-button type='warning' size="large" shape="square" @click="chooseImg">选择图片</nut-button>
+    </nut-cell>
+    <view v-if="state.editMode">
+      <nut-input v-model="state.watermarkContent" placeholder="请输入要显示的水印内容" clearable clearSize="14" show-word-limit
+        :max-length="20" :showClearIcon="true" @update:model-value="refresh"></nut-input>
+      <nut-cell>
+        <nut-button type='success' size="large" shape="square">下载</nut-button>
+      </nut-cell>
     </view>
-    {{ msg }}
-    <Dongdong />
-    <view class="btn">
-      <nut-button type="primary" @click="handleClick('text', msg2, true)">点我</nut-button>
-      <nut-button type="primary" @click="login()">登录</nut-button>
-      <nut-button type="primary" @click="upload()">上传文件</nut-button>
-      <nut-button type="primary" @click="download()">下载文件</nut-button>
-      <nut-button type="primary" @click="http()">http封装</nut-button>
-    </view>
-    <nut-toast :msg="msg2" v-model:visible="show" :type="type" :cover="cover" />
   </view>
 </template>
 
-<script lang="ts">
-// import JsonResponse from './vo/JsonResponse';
-import Taro from '@tarojs/taro';
+<script>
 import { reactive, toRefs } from 'vue';
-import { Dongdong } from '@nutui/icons-vue-taro';
-import httpClient from '../../core/http';
-
+import { Plus } from '@nutui/icons-vue-taro';
+import watermark from '../../core/components/watermark.vue';
 export default {
   name: 'Index',
   components: {
-    Dongdong
+    Plus, watermark
   },
   setup() {
     const state = reactive({
-      msg: '欢迎使用 NutUI4.0 开发小程序',
-      msg2: '你成功了~',
-      type: 'text',
-      show: false,
-      cover: false
+      editMode: true,
+      imgSrc: "https://img10.360buyimg.com/ling/jfs/t1/181258/24/10385/53029/60d04978Ef21f2d42/92baeb21f907cd24.jpg",
+      watermarkContent: '仅供入职使用',
     });
 
-    const handleClick = (type, msg, cover = false) => {
-      state.show = true;
-      state.msg2 = msg;
-      state.type = type;
-      state.cover = cover;
-    };
-
-    const login = () => {
-      var token = Taro.getStorageSync("token");
-      if (token) {
-        console.log("缓存中有token");
-        Taro.request({
-          url: BASE_URL + '/watermark/make',
-          header: {
-            "Authorization": token
-          },
-          method: "POST",
-          data: {},
-          success: function (ret) {
-            console.log("返回结果", ret);
-          }
-        })
-      } else {
-        console.log("缓存中没有token");
-        Taro.login({
-          success: function (res) {
-            console.log("登录拉拉了", res);
-            if (res.code) {
-              //发起网络请求
-              Taro.request({
-                url: 'http://localhost:8080/wechat/miniprogram/login',
-                method: "POST",
-                data: {
-                  code: res.code
-                },
-                success: function (ret) {
-                  console.log("返回结果", ret);
-                  // TODO 把token放入缓存
-                  Taro.setStorageSync("token", ret.data.data);
-                }
-              })
-            } else {
-              console.log('登录失败!' + res.errMsg)
-            }
-          }
-        });
-      }
+    const chooseImg = function () {
+      state.editMode = !state.editMode;
+      console.log(state.editMode);
     };
 
-    const upload = () => {
-      Taro.chooseImage({
-        success(res) {
-          const tempFilePaths = res.tempFilePaths;
-          Taro.uploadFile({
-            url: 'http://localhost:8080/oss/upload', //仅为示例,非真实的接口地址
-            header: {
-              "Authorization": Taro.getStorageSync("token")
-            },
-            filePath: tempFilePaths[0],
-            name: 'files',
-            formData: {
-              'user': 'test'
-            },
-            success(ret) {
-              console.log("上传结果233", ret.data)
-              //do something
-              var data = JSON.parse(ret.data);
-              Taro.setStorageSync("attachmentId", data.data[0]);
-            }
-          })
-        }
-      })
-    };
-
-    const download = () => {
-      console.log("附件ID:", Taro.getStorageSync("attachmentId"))
-      Taro.downloadFile({
-        url: 'http://localhost:8080/oss/download?attachmentId=' + Taro.getStorageSync("attachmentId"), //仅为示例,并非真实的资源
-        header: {
-
-          "Authorization": Taro.getStorageSync("token")
-        },
-        success: function (res) {
-          // 只要服务器有响应数据,就会把响应内容写入文件并进入 success 回调,业务需要自行判断是否下载到了想要的内容
-          if (res.statusCode === 200) {
-            console.log("下载成功了", res);
-            Taro.previewMedia({
-              sources: [{
-                "url": res.tempFilePath,
-                "type": "image"
-              }]
-            })
-          }
-        }
-      })
-    };
-
-    const http = () => {
-      httpClient({
-        url: "/oss/test",
-        method: "GET",
-        data: {},
-        success(result: string) {
-          console.log("成功啦", result);
-        }
-      });
+    const refresh = function () {
+      var tmpImgSrc = state.imgSrc;
+      state.imgSrc = '';
+      state.imgSrc = tmpImgSrc;
     }
 
     return {
-      ...toRefs(state), handleClick, login, upload, download, http
-    }
+      state,
+      chooseImg,
+      refresh
+    };
   }
 }
 </script>
 
 <style lang="scss">
-.index {
-  font-family: "Avenir", Helvetica, Arial, sans-serif;
-  -webkit-font-smoothing: antialiased;
-  -moz-osx-font-smoothing: grayscale;
-  text-align: center;
+.wrap {
+  width: 100vw;
+  height: 100vh;
+  background-color: #eff2f5;
+
+  .watermark {
+    width: 100%;
+    height: 50vh;
+    margin-bottom: 20px;
+    display: block;
+    background-color: #fff;
+  }
 }
 </style>

+ 3 - 0
src/pages/login/index.config.ts

@@ -0,0 +1,3 @@
+export default definePageConfig({
+  navigationBarTitleText: '登录'
+})

+ 62 - 0
src/pages/login/index.vue

@@ -0,0 +1,62 @@
+<template>
+  <view class="login">
+    <view class="login-wrap">
+      <view class="logo">
+        <My size="100"></My>
+      </view>
+      <View class="login-btn">
+        <nut-button size="large" type="success" shape="square" plain="true">登录</nut-button>
+      </View>
+    </view>
+  </view>
+</template>
+
+<script>
+import { reactive, toRefs } from 'vue';
+import { My } from '@nutui/icons-vue-taro';
+import { View } from '@tarojs/components';
+export default {
+  name: 'Index',
+  components: {
+    My,
+    View
+  },
+  setup() {
+
+
+  }
+}
+</script>
+
+<style lang="scss">
+.login {
+  width: 100vw;
+  height: 100vh;
+  background-color: #eff2f5;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+
+  &-wrap {
+    display: flex;
+    flex-direction: column;
+    align-items: center;
+  }
+
+  .logo {
+    width: 300px;
+    height: 300px;
+    border-radius: 50%;
+    background: #fff;
+    display: flex;
+    align-items: center;
+    justify-content: center;
+  }
+
+  .login-btn {
+    width: 500px;
+    margin-top: 100px;
+    margin-bottom: 100px;
+  }
+}
+</style>

+ 0 - 53
src/pages/watermark/index.vue

@@ -1,53 +0,0 @@
-<template>
-  <view class="index">
-    <view>
-      <img src="" alt="">
-    </view>
-    {{ msg }} <Dongdong />
-    <view class="btn">
-      <nut-button type="primary" @click="handleClick('text', msg2, true)">点我</nut-button>
-    </view>
-    <nut-toast :msg="msg2" v-model:visible="show" :type="type" :cover="cover"/>
-  </view>
-</template>
-
-<script>
-import { reactive, toRefs } from 'vue';
-import { Dongdong } from '@nutui/icons-vue-taro';
-export default {
-  name: 'Index',
-  components: {
-    Dongdong
-  },
-  setup() {
-    const state = reactive({
-      msg: '欢迎使用 NutUI4.0 开发小程序',
-      msg2: '你成功了~',
-      type: 'text',
-      show: false,
-      cover: false
-    });
-
-    const handleClick = (type, msg, cover = false) => {
-      state.show = true;
-      state.msg2 = msg;
-      state.type = type;
-      state.cover = cover;
-    };
-
-    return {
-      ...toRefs(state),
-      handleClick
-    }
-  }
-}
-</script>
-
-<style lang="scss">
-.index {
-  font-family: "Avenir", Helvetica, Arial, sans-serif;
-  -webkit-font-smoothing: antialiased;
-  -moz-osx-font-smoothing: grayscale;
-  text-align: center;
-}
-</style>