Browse Source

feat: 新增 watermark 组件 (#1436)

* feat: 添加range组件、calendar组件在线文档

* fix: 文档调整

* fix: 重构calendar组件

* feat: 日历组件重构,文档修改,功能完善

* fix: 格式化

* fix: 代码格式化调整。

* fix: 去除无用代码

* fix: 文档调整

* fix:  文档调整

* fix: taro  demo 样式修改

* feat: range组件功能完善,新增 竖向操作,刻度展示。

* fix: 冲突解决

* feat: taro功能新增,兼容处理,文档修改

* feat: 添加range组件,jdt主题色

* fix: 修改组件初始化逻辑

* feat: 新增h5 日期多选功能

* feat: 新增waterMark

* feat: 新增水印组件

* fix: watermark 单元测试

* feat: taro版本水印调整

* fix: 主题添加水印变量

Co-authored-by: lkjh3214 <13121007159@163.com>
Co-authored-by: love_forever <1039168735@qq.com>
lkjh3214 3 years ago
parent
commit
f4fc2d9f8a

+ 11 - 0
src/config.json

@@ -1077,6 +1077,17 @@
           "tarodoc": false,
           "type": "component",
           "author": "yangxiaolu3"
+        },
+        {
+          "version": "3.0.0",
+          "name": "WaterMark",
+          "cType": "展示组件",
+          "cName": "水印",
+          "desc": "页面上添加特定的文字或图案。适用于防止信息盗用。",
+          "show": true,
+          "tarodoc": false,
+          "type": "component",
+          "author": "liukun"
         }
       ]
     },

+ 13 - 3
src/packages/__VUE/calendar/demo.vue

@@ -29,15 +29,21 @@
       >
       </nut-cell>
       <nut-calendar
+        ref="calendarRef"
         v-model:visible="isVisible1"
         :default-value="date1"
-        type="range"
+        type="multiple"
         :start-date="`2019-12-22`"
         :end-date="`2021-01-08`"
         @close="closeSwitch('isVisible1')"
         @choose="setChooseValue1"
         @select="select"
       >
+        <template v-slot:btn>
+          <div class="wrapper">
+            <div class="d_div"> <span class="d_btn" @click="goDate">去某个时间</span></div>
+          </div>
+        </template>
       </nut-calendar>
     </div>
     <div>
@@ -289,8 +295,12 @@ export default createDemo({
     const select = (param: string) => {
       console.log(param);
     };
-    const setChooseValue1 = (param: string) => {
-      state.date1 = [...[param[0][3], param[1][3]]];
+    const setChooseValue1 = (chooseData: any) => {
+      let dateArr = chooseData.map((item: any) => {
+        return item[3];
+      });
+      console.log('changevalue 1 ', chooseData, dateArr);
+      state.date1 = [...dateArr];
     };
     const setChooseValue2 = (param: string) => {
       state.date2 = param[3];

+ 1 - 0
src/packages/__VUE/calendaritem/index.vue

@@ -348,6 +348,7 @@ export default create({
         }
         if (!isFirst) {
           // 点击日期 触发
+          console.log(state.chooseData);
           emit('select', state.chooseData);
           if (props.isAutoBackFill || !props.poppable) {
             confirm();

+ 0 - 1
src/packages/__VUE/imagepreview/index.scss

@@ -59,7 +59,6 @@
   justify-content: center;
   height: 100%;
 
-
   .nut-imagepreview-box {
     width: 100%;
   }

File diff suppressed because it is too large
+ 7 - 0
src/packages/__VUE/watermark/__tests__/__snapshots__/watermark.spec.ts.snap


+ 38 - 0
src/packages/__VUE/watermark/__tests__/watermark.spec.ts

@@ -0,0 +1,38 @@
+import { config, DOMWrapper, mount } from '@vue/test-utils';
+import Watermark from '../index.vue';
+import Cell from './../../cell/index.vue';
+import { nextTick, toRefs, reactive } from 'vue';
+
+test('render fullPage props ', async () => {
+  const wrapper = mount({
+    components: {
+      'nut-cell': Cell,
+      'nut-watermark': Watermark
+    },
+    template: `
+      <nut-cell>
+        <nut-watermark  font-color="#fa2c19" content="nut-ui"></nut-watermark>
+      </nut-cell>
+    `,
+    setup() {}
+  });
+  await nextTick();
+
+  expect(wrapper.findAll('.nut-watermark').length).toBe(1);
+  expect(wrapper.findAll('.nut-watermark-full-page').length).toBe(1);
+});
+test('should render watermark', () => {
+  const wrapper = mount({
+    components: {
+      'nut-cell': Cell,
+      'nut-watermark': Watermark
+    },
+    template: `
+      <nut-cell>
+        <nut-watermark :fullPage="false" font-color="#fa2c19" content="nut-ui"></nut-watermark>
+      </nut-cell>
+    `,
+    setup() {}
+  });
+  expect(wrapper.html()).toMatchSnapshot();
+});

+ 85 - 0
src/packages/__VUE/watermark/demo.vue

@@ -0,0 +1,85 @@
+<template>
+  <div class="demo">
+    <h4>{{ translate('basic') }}</h4>
+    <nut-cell class="wrap">
+      <nut-button @click="showTextMark">{{ translate('btn1') }}</nut-button>
+      <nut-button @click="showImageMark">{{ translate('btn2') }}</nut-button>
+      <nut-watermark v-if="!flag" class="mark1" z-index="1" content="nut-ui-water-mark"></nut-watermark>
+      <nut-watermark
+        v-if="flag"
+        class="mark1"
+        :image-width="60"
+        :image-height="23"
+        z-index="1"
+        :image="imgSrc"
+      ></nut-watermark>
+    </nut-cell>
+    <h4>{{ translate('title2') }}</h4>
+    <nut-cell class="wrap wrap2">
+      <img :src="src" alt="" />
+      <nut-watermark :fullPage="false" font-color="#fa2c19" content="nut-ui"></nut-watermark>
+    </nut-cell>
+  </div>
+</template>
+<script lang="ts">
+import { ref } from 'vue';
+import { createComponent } from '@/packages/utils/create';
+const { createDemo, translate } = createComponent('watermark');
+import { useTranslate } from '@/sites/assets/util/useTranslate';
+useTranslate({
+  'zh-CN': {
+    basic: '基本用法',
+    title2: '局部用法',
+    btn1: '文字水印',
+    btn2: '图片水印'
+  },
+  'en-US': {
+    basic: 'Basic Usage',
+    title2: 'Part Usage',
+    btn1: 'Text WaterMark',
+    btn2: 'Image WaterMark'
+  }
+});
+export default createDemo({
+  props: {},
+  setup() {
+    const src = ref('//img10.360buyimg.com/ling/jfs/t1/181258/24/10385/53029/60d04978Ef21f2d42/92baeb21f907cd24.jpg');
+    const imgSrc = ref(
+      '//img11.360buyimg.com/imagetools/jfs/t1/57345/6/20069/8019/62b995cdEd96fef03/51d3302dfeccd1d2.png'
+    );
+    const flag = ref(false);
+    const showTextMark = () => {
+      flag.value = false;
+    };
+    const showImageMark = () => {
+      flag.value = true;
+    };
+    return { translate, src, imgSrc, showTextMark, showImageMark, flag };
+  }
+});
+</script>
+<style lang="scss" scoped>
+.demo {
+  .wrap {
+    width: 100%;
+    height: 240px;
+    display: block;
+    position: relative;
+    background: #fff;
+    > img {
+      width: 100%;
+    }
+    .mark1 {
+      width: 100%;
+      top: 50px;
+    }
+    .nut-button {
+      margin-right: 10px;
+    }
+  }
+  .wrap2 {
+    position: relative;
+    z-index: 2;
+  }
+}
+</style>

+ 125 - 0
src/packages/__VUE/watermark/doc.en-US.md

@@ -0,0 +1,125 @@
+# watermark 
+
+### Intro
+
+### Install
+
+```javascript 
+
+import { createApp } from 'vue';
+// vue
+import { Watermark } from '@nutui/nutui';
+// taro
+import { Watermark } from '@nutui/nutui-taro';
+
+const app = createApp();
+app.use(Watermark);
+
+```
+
+### Basic Usage
+
+:::demo
+
+```html
+<template>
+  <nut-cell class="wrap">
+    <nut-button @click="showTextMark">Text WaterMark</nut-button>
+    <nut-button @click="showImageMark">Image WaterMark</nut-button>
+    <nut-watermark v-if="!flag" class="mark1" z-index="1" content="nut-ui-water-mark"></nut-watermark>
+    <nut-watermark
+      v-if="flag"
+      class="mark1"
+      :image-width="60"
+      :image-height="23"
+      z-index="1"
+      :image="imgSrc"
+    ></nut-watermark>
+  </nut-cell>
+</template>
+<script lang="ts">
+  export default {
+    setup() {
+      const imgSrc = ref('//img11.360buyimg.com/imagetools/jfs/t1/57345/6/20069/8019/62b995cdEd96fef03/51d3302dfeccd1d2.png');
+      const flag = ref(false);
+      const showTextMark = () => {
+        flag.value = false;
+      };
+      const showImageMark = () => {
+        flag.value = true;
+      };
+      return { translate, imgSrc, showTextMark, showImageMark, flag };
+    }
+  };
+</script>
+<style lang="scss" scoped>
+  .wrap {
+    width: 100%;
+    height: 240px;
+    display: block;
+    background: #fff;
+    >img {
+      width: 100%;
+    }
+    .mark1 {
+      width: 100%;
+      top: 50px;
+    }
+    .nut-button{
+      margin-right: 10px;
+    }
+  }
+</style>
+```
+:::
+
+### Part Usage
+:::demo
+
+```html
+<template>
+  <nut-cell class="wrap">
+    <img :src="src" alt="" />
+    <nut-watermark :fullPage="false" font-color="#fa2c19" content="nut-ui"></nut-watermark>
+  </nut-cell>
+</template>
+<script lang="ts">
+  export default {
+    setup() {
+     const src = ref('//img10.360buyimg.com/ling/jfs/t1/181258/24/10385/53029/60d04978Ef21f2d42/92baeb21f907cd24.jpg');
+      return { src };
+    }
+  };
+</script>
+<style lang="scss" scoped>
+  .wrap {
+    width: 100%;
+    height: 240px;
+    display: block;
+    background: #fff;
+     > img {
+      width: 100%;
+    }
+  }
+</style>
+```
+:::
+## API
+
+### Props
+| Attribute         | Description                             | Type   | Default           |
+|--------------|----------------------------------|--------|------------------|
+| width       | Width of watermark     | `number`           | `120`                |
+| height      | Height of watermark               | `number`           | `64`                 |
+| rotate      | Rotation angle when drawing watermark   | `number`           | `-22`                |
+| image       | Image source, it is recommended to export 2x or 3x images, and the image rendering watermark is preferred | `string`           | -                    |
+| imageWidth  | Width of image                                             | `number`           | `120`                |
+| imageHeight | Height of image                                             | `number`           | `64`                 |
+| zIndex      | Z-index of the appended watermark element                             | `number`           | `2000`               |
+| content     | Watermark text content                                         | `string`           | -                    |
+| fontColor   | Watermark text color                                         | `string`           | `rgba(0, 0, 0, .15)` |
+| fontSize    | Watermark text font size                                             | `string \| number` | `16`                 |
+| gapX        | Horizontal spacing between watermarks                                   | `number`           | `24`                 |
+| gapY        | Vertical spacing between watermarks                                   | `number`           | `48`                 |
+| fullPage    | Overwrite entire page                                     | `boolean`          | `true`               |
+| fontFamily  | Watermark text font family                  | `boolean`          | `true`               |

+ 132 - 0
src/packages/__VUE/watermark/doc.md

@@ -0,0 +1,132 @@
+# watermark 水印
+
+### 介绍
+
+页面上添加特定的文字或图案,可用于防止信息盗用。
+
+### 安装
+
+```javascript
+
+import { createApp } from 'vue';
+// vue
+import { Watermark } from '@nutui/nutui';
+// taro
+import { Watermark } from '@nutui/nutui-taro';
+
+const app = createApp();
+app.use(Watermark);
+
+```
+
+### 基础用法
+
+:::demo
+
+```html
+<template>
+  <nut-cell class="wrap">
+    <nut-button @click="showTextMark">文字水印</nut-button>
+    <nut-button @click="showImageMark">图片水印</nut-button>
+    <nut-watermark v-if="!flag" class="mark1" z-index="1" content="nut-ui-water-mark"></nut-watermark>
+    <nut-watermark
+      v-if="flag"
+      class="mark1"
+      :image-width="60"
+      :image-height="23"
+      z-index="1"
+      :image="imgSrc"
+    ></nut-watermark>
+  </nut-cell>
+</template>
+<script lang="ts">
+  export default {
+    setup() {
+      const imgSrc = ref('//img11.360buyimg.com/imagetools/jfs/t1/57345/6/20069/8019/62b995cdEd96fef03/51d3302dfeccd1d2.png');
+      const flag = ref(false);
+      const showTextMark = () => {
+        flag.value = false;
+      };
+      const showImageMark = () => {
+        flag.value = true;
+      };
+      return { translate, imgSrc, showTextMark, showImageMark, flag };
+    }
+  };
+</script>
+<style lang="scss" scoped>
+  .wrap {
+    width: 100%;
+    height: 240px;
+    display: block;
+    background: #fff;
+    >img {
+      width: 100%;
+    }
+    .mark1 {
+      width: 100%;
+      top: 50px;
+    }
+    .nut-button{
+      margin-right: 10px;
+    }
+  }
+</style>
+```
+:::
+
+### 局部用法
+
+:::demo
+
+```html
+<template>
+  <nut-cell class="wrap">
+    <img :src="src" alt="" />
+    <nut-watermark :fullPage="false" font-color="#fa2c19" content="nut-ui"></nut-watermark>
+  </nut-cell>
+</template>
+<script lang="ts">
+  export default {
+    setup() {
+     const src = ref('//img10.360buyimg.com/ling/jfs/t1/181258/24/10385/53029/60d04978Ef21f2d42/92baeb21f907cd24.jpg');
+      return { src };
+    }
+  };
+</script>
+<style lang="scss" scoped>
+  .wrap {
+    width: 100%;
+    height: 240px;
+    display: block;
+    background: #fff;
+     > img {
+      width: 100%;
+    }
+  }
+</style>
+```
+:::
+
+## API
+
+### Props
+
+| 参数          | 说明                             | 类型   | 默认值           |
+|--------------|----------------------------------|--------|------------------|
+| width       | 水印的宽度                                           | `number`           | `120`                |
+| height      | 水印的高度                                           | `number`           | `64`                 |
+| rotate      | 水印绘制时,旋转的角度                  | `number`           | `-22`                |
+| image       | 图片源,建议导出 2 倍或 3 倍图,优先使用图片渲染水印 | `string`           | -                    |
+| imageWidth  | 图片宽度                                             | `number`           | `120`                |
+| imageHeight | 图片高度                                             | `number`           | `64`                 |
+| zIndex      | 追加的水印元素的 z-index                             | `number`           | `2000`               |
+| content     | 水印文字内容                                         | `string`           | -                    |
+| fontColor   | 水印文字颜色                                         | `string`           | `rgba(0, 0, 0, .15)` |
+| fontSize    | 文字大小                                             | `string \| number` | `16`                 |
+| gapX        | 水印之间的水平间距                                   | `number`           | `24`                 |
+| gapY        | 水印之间的垂直间距                                   | `number`           | `48`                 |
+| fullPage    | 是否覆盖整个页面                                     | `boolean`          | `true`               |
+| fontFamily  | 水印文字字体                                     | `boolean`          | `true`               |
+
+

+ 13 - 0
src/packages/__VUE/watermark/index.scss

@@ -0,0 +1,13 @@
+.nut-watermark {
+  position: absolute;
+  z-index: $nut-water-mark-z-index;
+  left: 0;
+  right: 0;
+  top: 0;
+  bottom: 0;
+  pointer-events: none;
+  background-repeat: repeat;
+  &-full-page {
+    position: fixed;
+  }
+}

+ 206 - 0
src/packages/__VUE/watermark/index.taro.vue

@@ -0,0 +1,206 @@
+<template>
+  <view
+    :class="classes"
+    :style="{
+      zIndex,
+      backgroundSize: `${gapX + width}px`,
+      backgroundImage: `url('${base64Url}')`
+    }"
+  >
+  </view>
+</template>
+<script lang="ts">
+import { reactive, toRefs, computed, watch } from 'vue';
+import { createComponent } from '@/packages/utils/create';
+import Taro from '@tarojs/taro';
+
+const { componentName, create } = createComponent('watermark');
+export default create({
+  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, { emit }) {
+    const state = reactive({
+      base64Url: ''
+    });
+    const {
+      zIndex,
+      gapX,
+      gapY,
+      width,
+      height,
+      rotate,
+      image,
+      imageWidth,
+      imageHeight,
+      content,
+      fontStyle,
+      fontWeight,
+      fontColor,
+      fontSize,
+      fontFamily
+    } = 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: any = Taro.createOffscreenCanvas({
+        type: '2d',
+        width: Number(canvasWidth),
+        height: Number(canvasHeight)
+      });
+      const ctx: any = canvas.getContext('2d');
+
+      if (ctx) {
+        if (image) {
+          ctx.translate(markWidth / 2, markHeight / 2);
+          ctx.rotate((Math.PI / 180) * Number(rotate));
+          // 创建一个图片
+          const img = canvas.createImage();
+
+          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 = ctx.canvas.toDataURL();
+          };
+        } else if (content) {
+          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 = ctx.canvas.toDataURL();
+        }
+      } else {
+        throw new Error('当前环境不支持Canvas');
+      }
+    };
+    init();
+
+    watch(
+      () => [
+        zIndex,
+        gapX,
+        gapY,
+        width,
+        height,
+        rotate,
+        image,
+        imageWidth,
+        imageHeight,
+        content,
+        fontStyle,
+        fontWeight,
+        fontColor,
+        fontSize,
+        fontFamily
+      ],
+      () => {
+        init();
+      }
+    );
+    const classes = computed(() => {
+      const prefixCls = componentName;
+      return {
+        [prefixCls]: true,
+        [`${prefixCls}-full-page`]: props.fullPage
+      };
+    });
+
+    return { ...toRefs(state), classes };
+  }
+});
+</script>

+ 198 - 0
src/packages/__VUE/watermark/index.vue

@@ -0,0 +1,198 @@
+<template>
+  <view
+    :class="classes"
+    :style="{
+      zIndex,
+      backgroundSize: `${gapX + width}px`,
+      backgroundImage: `url('${base64Url}')`
+    }"
+  >
+  </view>
+</template>
+<script lang="ts">
+import { reactive, toRefs, computed, watch } from 'vue';
+import { createComponent } from '@/packages/utils/create';
+const { componentName, create } = createComponent('watermark');
+export default create({
+  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, { emit }) {
+    const state = reactive({
+      base64Url: ''
+    });
+    const {
+      zIndex,
+      gapX,
+      gapY,
+      width,
+      height,
+      rotate,
+      image,
+      imageWidth,
+      imageHeight,
+      content,
+      fontStyle,
+      fontWeight,
+      fontColor,
+      fontSize,
+      fontFamily
+    } = props;
+    const init = () => {
+      const canvas = document.createElement('canvas');
+      const ratio = window.devicePixelRatio;
+      console.log(ratio);
+      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) {
+          ctx.translate(markWidth / 2, markHeight / 2);
+          ctx.rotate((Math.PI / 180) * Number(rotate));
+
+          const img = new Image();
+          img.crossOrigin = 'anonymous';
+          img.referrerPolicy = 'no-referrer';
+          img.src = image;
+          img.onload = () => {
+            ctx.drawImage(
+              img,
+              (-imageWidth * ratio) / 2,
+              (-imageHeight * ratio) / 2,
+              imageWidth * ratio,
+              imageHeight * ratio
+            );
+            ctx.restore();
+            state.base64Url = canvas.toDataURL();
+            console.log(state.base64Url);
+          };
+        } else if (content) {
+          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();
+          console.log(state.base64Url);
+        }
+      } else {
+        throw new Error('当前环境不支持Canvas');
+      }
+    };
+    init();
+
+    watch(
+      () => [
+        zIndex,
+        gapX,
+        gapY,
+        width,
+        height,
+        rotate,
+        image,
+        imageWidth,
+        imageHeight,
+        content,
+        fontStyle,
+        fontWeight,
+        fontColor,
+        fontSize,
+        fontFamily
+      ],
+      () => {
+        init();
+      }
+    );
+    const classes = computed(() => {
+      const prefixCls = componentName;
+      return {
+        [prefixCls]: true,
+        [`${prefixCls}-full-page`]: props.fullPage
+      };
+    });
+
+    return { ...toRefs(state), classes };
+  }
+});
+</script>

+ 2 - 0
src/packages/styles/variables-jdb.scss

@@ -869,5 +869,7 @@ $comment-shop-color: $primary-color !default;
 // Ellipsis
 $ellipsis-expand-collapse-color: #3460fa !default;
 
+// WaterMark
+$nut-water-mark-z-index: 2000 !default;
 @import './mixins/index';
 @import './animation/index';

+ 2 - 0
src/packages/styles/variables-jdt.scss

@@ -775,5 +775,7 @@ $comment-shop-color: $primary-color !default;
 // Ellipsis
 $ellipsis-expand-collapse-color: #3460fa !default;
 
+// WaterMark
+$nut-water-mark-z-index: 2000 !default;
 @import './mixins/index';
 @import './animation/index';

+ 3 - 0
src/packages/styles/variables.scss

@@ -800,5 +800,8 @@ $comment-shop-color: $primary-color !default;
 // Ellipsis
 $ellipsis-expand-collapse-color: #3460fa !default;
 
+// WaterMark
+$nut-water-mark-z-index: 2000 !default;
+
 @import './mixins/index';
 @import './animation/index';

+ 0 - 1
src/sites/mobile-taro/vue/src/app.config.ts

@@ -105,7 +105,6 @@ const subPackages = [
     ]
   }
 ];
-
 export default {
   pages: ['pages/index/index'],
   subPackages,

+ 1 - 0
src/sites/mobile-taro/vue/src/exhibition/pages/watermark/index.config.ts

@@ -0,0 +1 @@
+export default { navigationBarTitleText: 'WaterMark' };

+ 68 - 0
src/sites/mobile-taro/vue/src/exhibition/pages/watermark/index.vue

@@ -0,0 +1,68 @@
+<template>
+  <div class="demo">
+    <h2>基础用法</h2>
+    <nut-cell class="wrap">
+      <nut-button @click="showTextMark">文字水印</nut-button>
+      <nut-button @click="showImageMark">图片水印</nut-button>
+      <nut-watermark v-if="!flag" class="mark1" :z-index="1" content="nut-ui-water-mark"></nut-watermark>
+      <nut-watermark
+        v-if="flag"
+        class="mark1"
+        :image-width="60"
+        :image-height="23"
+        :z-index="1"
+        :image="imgSrc"
+      ></nut-watermark>
+    </nut-cell>
+    <h2>局部用法</h2>
+    <nut-cell class="wrap wrap2">
+      <img :src="src" alt="" />
+      <nut-watermark :fullPage="false" font-color="#fa2c19" content="nut-ui"></nut-watermark>
+    </nut-cell>
+  </div>
+</template>
+<script lang="ts">
+import { defineComponent, ref } from 'vue';
+export default defineComponent({
+  props: {},
+  setup() {
+    const src = ref('//img10.360buyimg.com/ling/jfs/t1/181258/24/10385/53029/60d04978Ef21f2d42/92baeb21f907cd24.jpg');
+    const imgSrc = ref(
+      'http://img11.360buyimg.com/imagetools/jfs/t1/57345/6/20069/8019/62b995cdEd96fef03/51d3302dfeccd1d2.png'
+    );
+    const flag = ref(false);
+    const showTextMark = () => {
+      flag.value = false;
+    };
+    const showImageMark = () => {
+      flag.value = true;
+    };
+    return { src, imgSrc, showTextMark, showImageMark, flag };
+  }
+});
+</script>
+<style lang="scss" scoped>
+.demo {
+  .wrap {
+    width: 100%;
+    height: 240px;
+    display: block;
+    position: relative;
+    background: #fff;
+    > img {
+      width: 100%;
+    }
+    .mark1 {
+      width: 100%;
+      top: 50px;
+    }
+    .nut-button {
+      margin-right: 10px;
+    }
+  }
+  .wrap2 {
+    position: relative;
+    z-index: 2;
+  }
+}
+</style>