hookehuyr

✨ feat: 新增电子签名功能,测试上传七牛服务器

...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
2 VITE_PORT = 8208 2 VITE_PORT = 8208
3 3
4 # 反向代理服务器地址 4 # 反向代理服务器地址
5 -# VITE_PROXY_TARGET = http://voice.onwall.cn 5 +VITE_PROXY_TARGET = https://oa.onwall.cn
6 6
7 # API请求前缀 7 # API请求前缀
8 VITE_PROXY_PREFIX = /srv/ 8 VITE_PROXY_PREFIX = /srv/
......
...@@ -15,7 +15,7 @@ VITE_PIN = ...@@ -15,7 +15,7 @@ VITE_PIN =
15 15
16 # 反向代理服务器地址 16 # 反向代理服务器地址
17 # VITE_PROXY_TARGET = http://oa-dev.onwall.cn 17 # VITE_PROXY_TARGET = http://oa-dev.onwall.cn
18 -VITE_PROXY_TARGET = http://guanzong.onwall.cn 18 +# VITE_PROXY_TARGET = http://guanzong.onwall.cn
19 19
20 # PC端地址 20 # PC端地址
21 VITE_MOBILE_URL = http://localhost:5173/ 21 VITE_MOBILE_URL = http://localhost:5173/
......
...@@ -11,8 +11,8 @@ VITE_APP_ID = ...@@ -11,8 +11,8 @@ VITE_APP_ID =
11 VITE_APP_PIN = 11 VITE_APP_PIN =
12 12
13 # 反向代理服务器地址 13 # 反向代理服务器地址
14 -VITE_PROXY_TARGET = http://guanzong.onwall.cn 14 +# VITE_PROXY_TARGET = http://guanzong.onwall.cn
15 15
16 # PC端地址 16 # PC端地址
17 # VITE_MOBILE_URL = http://oa.onwall.cn/f/guanzong/web/ 17 # VITE_MOBILE_URL = http://oa.onwall.cn/f/guanzong/web/
18 -VITE_MOBILE_URL = http://guanzong.onwall.cn/f/guanzong/web/ 18 +# VITE_MOBILE_URL = http://guanzong.onwall.cn/f/guanzong/web/
......
This diff could not be displayed because it is too large.
...@@ -55,6 +55,7 @@ ...@@ -55,6 +55,7 @@
55 "unplugin-vue-define-options": "^0.6.1", 55 "unplugin-vue-define-options": "^0.6.1",
56 "vite": "^2.9.9", 56 "vite": "^2.9.9",
57 "vite-plugin-style-import": "1.4.1", 57 "vite-plugin-style-import": "1.4.1",
58 + "vue-esign": "^1.1.4",
58 "vue-router": "^4.0.15" 59 "vue-router": "^4.0.15"
59 } 60 }
60 } 61 }
......
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
2 * @Author: hookehuyr hookehuyr@gmail.com 2 * @Author: hookehuyr hookehuyr@gmail.com
3 * @Date: 2022-05-26 23:52:36 3 * @Date: 2022-05-26 23:52:36
4 * @LastEditors: hookehuyr hookehuyr@gmail.com 4 * @LastEditors: hookehuyr hookehuyr@gmail.com
5 - * @LastEditTime: 2022-08-30 06:13:04 5 + * @LastEditTime: 2022-09-06 16:24:19
6 * @FilePath: /data-table/src/App.vue 6 * @FilePath: /data-table/src/App.vue
7 * @Description: 7 * @Description:
8 --> 8 -->
...@@ -49,10 +49,10 @@ watch(() => $router.currentRoute.value, (newValue, oldValue) => { ...@@ -49,10 +49,10 @@ watch(() => $router.currentRoute.value, (newValue, oldValue) => {
49 }, { immediate: true }) 49 }, { immediate: true })
50 50
51 // TAG: 全局配置Toast 51 // TAG: 全局配置Toast
52 -Toast.setDefaultOptions({ 52 +// Toast.setDefaultOptions({
53 - duration: 2000, 53 +// duration: 2000,
54 - className: 'zIndex' 54 +// className: 'zIndex'
55 -}); 55 +// });
56 56
57 // web端判断 57 // web端判断
58 const is_pc = computed(() => wxInfo().isPC) 58 const is_pc = computed(() => wxInfo().isPC)
......
1 +/*
2 + * @Date: 2022-06-17 14:54:29
3 + * @LastEditors: hookehuyr hookehuyr@gmail.com
4 + * @LastEditTime: 2022-09-06 17:03:55
5 + * @FilePath: /data-table/src/api/common.js
6 + * @Description: 通用接口
7 + */
8 +import { fn, fetch, uploadFn } from '@/api/fn';
9 +
10 +const Api = {
11 + SMS: '/srv/?a=sms',
12 + TOKEN: '/srv/?a=upload',
13 + SAVE_FILE: '/srv/?a=upload&t=save_file',
14 +}
15 +
16 +/**
17 + * @description: 发送验证码
18 + * @param {*} phone 手机号码
19 + * @returns
20 + */
21 +export const smsAPI = (params) => fn(fetch.post(Api.SMS, params));
22 +
23 +/**
24 + * @description: 获取七牛token
25 + * @param {*} filename 文件名
26 + * @param {*} file 图片base64
27 + * @returns
28 + */
29 +export const qiniuTokenAPI = (params) => fn(fetch.stringifyPost(Api.TOKEN, params));
30 +
31 +/**
32 + * @description: 上传七牛
33 + * @param {*}
34 + * @returns
35 + */
36 +export const qiniuUploadAPI = (url, data, config) => uploadFn(fetch.basePost(url, data, config));
37 +
38 +/**
39 + * @description: 保存图片
40 + * @param {*} format
41 + * @param {*} hash
42 + * @param {*} height
43 + * @param {*} width
44 + * @param {*} filekey
45 + * @returns
46 + */
47 +export const saveFileAPI = (params) => fn(fetch.stringifyPost(Api.SAVE_FILE, params));
1 +<!--
2 + * @Date: 2022-09-06 16:29:31
3 + * @LastEditors: hookehuyr hookehuyr@gmail.com
4 + * @LastEditTime: 2022-09-07 00:46:48
5 + * @FilePath: /data-table/src/components/SignField/index.vue
6 + * @Description: 文件描述
7 +-->
8 +<template>
9 + <div class="sign-page">
10 + <div class="label">{{ item.label }}<span v-if="item.required">&nbsp;*</span></div>
11 + <div style="padding: 1rem;">
12 +
13 + <vue-esign ref="esign" style="border: 1px dashed #c2c1c1;" :height="700" :isCrop="isCrop" :lineWidth="lineWidth"
14 + :lineColor="lineColor" :bgColor.sync="bgColor" />
15 + </div>
16 + <div class="control-sign">
17 + <button @click="handleReset">清空画板</button>
18 + <button @click="handleGenerate">生成图片</button>
19 + </div>
20 + </div>
21 +</template>
22 +
23 +<script setup>
24 +const props = defineProps({
25 + item: Object
26 +});
27 +</script>
28 +
29 +<script>
30 +import { v4 as uuidv4 } from 'uuid';
31 +import { qiniuTokenAPI, qiniuUploadAPI, saveFileAPI } from '@/api/common'
32 +
33 +export default {
34 + data() {
35 + return {
36 + lineWidth: 6,
37 + lineColor: "#000000",
38 + bgColor: "",
39 + resultImg: "",
40 + isCrop: false,
41 + }
42 + },
43 + methods: {
44 + //清空画板..
45 + handleReset() {
46 + this.$refs.esign.reset();
47 + this.resultImg = "";
48 + },
49 + //生成签名图片..
50 + handleGenerate () {
51 + this.$refs.esign.generate()
52 + .then(async res => {
53 + // let fileName = "img1.png";
54 + // let file = this.dataURLtoFile(res, fileName);
55 + // console.log("file", file);
56 + let affix = uuidv4();
57 + let base64url = res.slice(res.indexOf(',') + 1); // 截取前缀的base64 data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAnoAAAJeCAYAA.......
58 + // 获取七牛token
59 + const { token, key, code } = await qiniuTokenAPI({ filename: `${affix}_sign`, file: base64url });
60 + if (code) {
61 + const config = {
62 + headers: {
63 + 'Content-Type': 'application/octet-stream',
64 + 'Authorization': 'UpToken ' + token, // UpToken后必须有一个 ' '(空格)
65 + }
66 + }
67 + // 上传七牛服务器
68 + const { filekey, hash, image_info } = await qiniuUploadAPI('http://upload.qiniup.com/putb64/-1/key/' + key, base64url, config)
69 + if (filekey) {
70 + // 保存图片
71 + const { data } = await saveFileAPI({ filekey, hash, format: image_info.format, height: image_info.height, width: image_info.width });
72 + console.warn(data.src);
73 + }
74 + }
75 + });
76 + },
77 + //将图片base64转换为文件
78 + dataURLtoFile(dataurl, filename) {
79 + var arr = dataurl.split(","),
80 + mime = arr[0].match(/:(.*?);/)[1],
81 + bstr = atob(arr[1]),
82 + n = bstr.length,
83 + u8arr = new Uint8Array(n);
84 + while (n--) {
85 + u8arr[n] = bstr.charCodeAt(n);
86 + }
87 + return new File([u8arr], filename, { type: mime });
88 + }
89 + }
90 +}
91 +</script>
92 +
93 +<style lang="less" scoped>
94 +.sign-page {
95 + .label {
96 + padding: 1rem 1rem 0 1rem;
97 + font-size: 0.9rem;
98 + font-weight: bold;
99 +
100 + span {
101 + color: red;
102 + }
103 + }
104 +}
105 +</style>
...@@ -10,6 +10,7 @@ import DatePickerField from '@/components/DatePickerField/index.vue' ...@@ -10,6 +10,7 @@ import DatePickerField from '@/components/DatePickerField/index.vue'
10 import ImageUploaderField from '@/components/ImageUploaderField/index.vue' 10 import ImageUploaderField from '@/components/ImageUploaderField/index.vue'
11 import PhoneField from '@/components/PhoneField/index.vue' 11 import PhoneField from '@/components/PhoneField/index.vue'
12 import EmailField from '@/components/EmailField/index.vue' 12 import EmailField from '@/components/EmailField/index.vue'
13 +import SignField from '@/components/SignField/index.vue'
13 14
14 /** 15 /**
15 * 生成自定义组件类型 16 * 生成自定义组件类型
...@@ -24,6 +25,7 @@ import EmailField from '@/components/EmailField/index.vue' ...@@ -24,6 +25,7 @@ import EmailField from '@/components/EmailField/index.vue'
24 * @type image_uploader 图片上传 ImageUploaderField 25 * @type image_uploader 图片上传 ImageUploaderField
25 * @type phone 手机输入框 PhoneField 26 * @type phone 手机输入框 PhoneField
26 * @type email 邮箱输入框 EmailField 27 * @type email 邮箱输入框 EmailField
28 + * @type sign 电子签名输入框 SignField
27 */ 29 */
28 export function createComponentType(data) { 30 export function createComponentType(data) {
29 // 判断类型和使用组件 31 // 判断类型和使用组件
...@@ -75,5 +77,9 @@ export function createComponentType(data) { ...@@ -75,5 +77,9 @@ export function createComponentType(data) {
75 item.name = item.key; 77 item.name = item.key;
76 item.component = EmailField; 78 item.component = EmailField;
77 } 79 }
80 + if (item.component_props.name === 'sign') {
81 + item.name = item.key;
82 + item.component = SignField;
83 + }
78 }) 84 })
79 } 85 }
......
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
2 * @Author: hookehuyr hookehuyr@gmail.com 2 * @Author: hookehuyr hookehuyr@gmail.com
3 * @Date: 2022-05-31 12:06:19 3 * @Date: 2022-05-31 12:06:19
4 * @LastEditors: hookehuyr hookehuyr@gmail.com 4 * @LastEditors: hookehuyr hookehuyr@gmail.com
5 - * @LastEditTime: 2022-08-31 13:14:09 5 + * @LastEditTime: 2022-09-06 16:25:52
6 * @FilePath: /data-table/src/main.js 6 * @FilePath: /data-table/src/main.js
7 * @Description: 7 * @Description:
8 */ 8 */
...@@ -14,6 +14,7 @@ import App from './App.vue'; ...@@ -14,6 +14,7 @@ import App from './App.vue';
14 import axios from '@/utils/axios'; 14 import axios from '@/utils/axios';
15 // import 'default-passive-events'; // 解决Chrome控制台non-passive event listener输出问题 15 // import 'default-passive-events'; // 解决Chrome控制台non-passive event listener输出问题
16 import { createPinia } from 'pinia'; 16 import { createPinia } from 'pinia';
17 +import vueEsign from 'vue-esign'
17 18
18 const pinia = createPinia(); 19 const pinia = createPinia();
19 const app = createApp(App); 20 const app = createApp(App);
...@@ -24,5 +25,5 @@ app.config.warnHandler = () => null; ...@@ -24,5 +25,5 @@ app.config.warnHandler = () => null;
24 app.config.globalProperties.$http = axios; // 关键语句 25 app.config.globalProperties.$http = axios; // 关键语句
25 26
26 app.use(pinia).use(router).use(Button).use(VanImage).use(Col).use(Row).use(Icon).use(Form).use(Field).use(CellGroup).use(Toast).use(Uploader).use(Empty).use(Tab).use(Tabs).use(Overlay).use(NumberKeyboard).use(Lazyload).use(List).use(PullRefresh).use(Popup).use(Picker).use(Sticky).use(Stepper).use(Tag).use(Swipe).use(SwipeItem).use(Dialog).use(ActionSheet).use(Loading).use(Checkbox).use(Search).use(ConfigProvider).use(NavBar).use(Collapse).use(CollapseItem).use(Radio).use(RadioGroup).use(CheckboxGroup).use(Area).use(DatePicker); 27 app.use(pinia).use(router).use(Button).use(VanImage).use(Col).use(Row).use(Icon).use(Form).use(Field).use(CellGroup).use(Toast).use(Uploader).use(Empty).use(Tab).use(Tabs).use(Overlay).use(NumberKeyboard).use(Lazyload).use(List).use(PullRefresh).use(Popup).use(Picker).use(Sticky).use(Stepper).use(Tag).use(Swipe).use(SwipeItem).use(Dialog).use(ActionSheet).use(Loading).use(Checkbox).use(Search).use(ConfigProvider).use(NavBar).use(Collapse).use(CollapseItem).use(Radio).use(RadioGroup).use(CheckboxGroup).use(Area).use(DatePicker);
27 - 28 +app.use(vueEsign)
28 app.mount('#app'); 29 app.mount('#app');
......
...@@ -2,8 +2,8 @@ ...@@ -2,8 +2,8 @@
2 * @Author: hookehuyr hookehuyr@gmail.com 2 * @Author: hookehuyr hookehuyr@gmail.com
3 * @Date: 2022-05-28 10:17:40 3 * @Date: 2022-05-28 10:17:40
4 * @LastEditors: hookehuyr hookehuyr@gmail.com 4 * @LastEditors: hookehuyr hookehuyr@gmail.com
5 - * @LastEditTime: 2022-08-16 15:34:02 5 + * @LastEditTime: 2022-09-06 18:01:36
6 - * @FilePath: /front/src/utils/axios.js 6 + * @FilePath: /data-table/src/utils/axios.js
7 * @Description: 7 * @Description:
8 */ 8 */
9 import axios from 'axios'; 9 import axios from 'axios';
...@@ -13,8 +13,7 @@ import { strExist } from '@/utils/tools' ...@@ -13,8 +13,7 @@ import { strExist } from '@/utils/tools'
13 // import { parseQueryString } from '@/utils/tools' 13 // import { parseQueryString } from '@/utils/tools'
14 14
15 axios.defaults.params = { 15 axios.defaults.params = {
16 - f: 'guanzong', 16 + f: 'customize',
17 - client_id: '81661'
18 }; 17 };
19 18
20 /** 19 /**
......
1 <!-- 1 <!--
2 * @Date: 2022-07-18 10:22:22 2 * @Date: 2022-07-18 10:22:22
3 * @LastEditors: hookehuyr hookehuyr@gmail.com 3 * @LastEditors: hookehuyr hookehuyr@gmail.com
4 - * @LastEditTime: 2022-09-06 15:53:40 4 + * @LastEditTime: 2022-09-06 18:25:18
5 * @FilePath: /data-table/src/views/index.vue 5 * @FilePath: /data-table/src/views/index.vue
6 * @Description: 首页 6 * @Description: 首页
7 --> 7 -->
...@@ -137,6 +137,16 @@ onMounted(() => { ...@@ -137,6 +137,16 @@ onMounted(() => {
137 // ], 137 // ],
138 // required: true, 138 // required: true,
139 // }, 139 // },
140 + {
141 + key: 'sign',
142 + value: '',
143 + label: '电子签名',
144 + placeholder: '',
145 + component: '',
146 + component_props: {
147 + name: 'sign'
148 + },
149 + },
140 { 150 {
141 key: 'city', 151 key: 'city',
142 value: '天津市/天津市/和平区', 152 value: '天津市/天津市/和平区',
...@@ -160,16 +170,16 @@ onMounted(() => { ...@@ -160,16 +170,16 @@ onMounted(() => {
160 // columns_type: ['year', 'month'] 170 // columns_type: ['year', 'month']
161 // }, 171 // },
162 // }, 172 // },
163 - // { 173 + {
164 - // key: 'imageUploader', 174 + key: 'imageUploader',
165 - // value: '', 175 + value: '',
166 - // label: '图片上传', 176 + label: '图片上传',
167 - // component_props: { 177 + component_props: {
168 - // name: 'image_uploader', 178 + name: 'image_uploader',
169 - // image_type: ['jpg', 'png'], 179 + image_type: ['jpg', 'png'],
170 - // multiple: false 180 + multiple: false
171 - // } 181 + }
172 - // } 182 + }
173 ]; 183 ];
174 // 生成自定义组件 184 // 生成自定义组件
175 createComponentType(mockData.value) 185 createComponentType(mockData.value)
...@@ -177,7 +187,7 @@ onMounted(() => { ...@@ -177,7 +187,7 @@ onMounted(() => {
177 187
178 const onSubmit = (values) => { 188 const onSubmit = (values) => {
179 console.log('submit', values); 189 console.log('submit', values);
180 - console.warn(mockData.value); 190 + // console.warn(mockData.value);
181 }; 191 };
182 192
183 </script> 193 </script>
......
...@@ -27,13 +27,13 @@ export default ({ command, mode }) => { ...@@ -27,13 +27,13 @@ export default ({ command, mode }) => {
27 vue(), 27 vue(),
28 styleImport({ 28 styleImport({
29 resolves: [VantResolve()], 29 resolves: [VantResolve()],
30 - // libs: [ 30 + libs: [
31 - // { 31 + {
32 - // libraryName: 'vant', 32 + libraryName: 'vant',
33 - // esModule: true, 33 + esModule: true,
34 - // resolveStyle: name => `../es/${name}/style` 34 + resolveStyle: name => `../es/${name}/style`
35 - // } 35 + }
36 - // ] 36 + ]
37 }), // 按需引入 vant 位置报错问题 37 }), // 按需引入 vant 位置报错问题
38 dynamicImport(), // 增强 Vite 内置的 dynamic import, 支持在 import() 中使用别名 38 dynamicImport(), // 增强 Vite 内置的 dynamic import, 支持在 import() 中使用别名
39 // legacy({ 39 // legacy({
......
This diff could not be displayed because it is too large.