hookehuyr

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

......@@ -2,7 +2,7 @@
VITE_PORT = 8208
# 反向代理服务器地址
# VITE_PROXY_TARGET = http://voice.onwall.cn
VITE_PROXY_TARGET = https://oa.onwall.cn
# API请求前缀
VITE_PROXY_PREFIX = /srv/
......
......@@ -15,7 +15,7 @@ VITE_PIN =
# 反向代理服务器地址
# VITE_PROXY_TARGET = http://oa-dev.onwall.cn
VITE_PROXY_TARGET = http://guanzong.onwall.cn
# VITE_PROXY_TARGET = http://guanzong.onwall.cn
# PC端地址
VITE_MOBILE_URL = http://localhost:5173/
......
......@@ -11,8 +11,8 @@ VITE_APP_ID =
VITE_APP_PIN =
# 反向代理服务器地址
VITE_PROXY_TARGET = http://guanzong.onwall.cn
# VITE_PROXY_TARGET = http://guanzong.onwall.cn
# PC端地址
# VITE_MOBILE_URL = http://oa.onwall.cn/f/guanzong/web/
VITE_MOBILE_URL = http://guanzong.onwall.cn/f/guanzong/web/
# 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 @@
"unplugin-vue-define-options": "^0.6.1",
"vite": "^2.9.9",
"vite-plugin-style-import": "1.4.1",
"vue-esign": "^1.1.4",
"vue-router": "^4.0.15"
}
}
......
......@@ -2,7 +2,7 @@
* @Author: hookehuyr hookehuyr@gmail.com
* @Date: 2022-05-26 23:52:36
* @LastEditors: hookehuyr hookehuyr@gmail.com
* @LastEditTime: 2022-08-30 06:13:04
* @LastEditTime: 2022-09-06 16:24:19
* @FilePath: /data-table/src/App.vue
* @Description:
-->
......@@ -49,10 +49,10 @@ watch(() => $router.currentRoute.value, (newValue, oldValue) => {
}, { immediate: true })
// TAG: 全局配置Toast
Toast.setDefaultOptions({
duration: 2000,
className: 'zIndex'
});
// Toast.setDefaultOptions({
// duration: 2000,
// className: 'zIndex'
// });
// web端判断
const is_pc = computed(() => wxInfo().isPC)
......
/*
* @Date: 2022-06-17 14:54:29
* @LastEditors: hookehuyr hookehuyr@gmail.com
* @LastEditTime: 2022-09-06 17:03:55
* @FilePath: /data-table/src/api/common.js
* @Description: 通用接口
*/
import { fn, fetch, uploadFn } from '@/api/fn';
const Api = {
SMS: '/srv/?a=sms',
TOKEN: '/srv/?a=upload',
SAVE_FILE: '/srv/?a=upload&t=save_file',
}
/**
* @description: 发送验证码
* @param {*} phone 手机号码
* @returns
*/
export const smsAPI = (params) => fn(fetch.post(Api.SMS, params));
/**
* @description: 获取七牛token
* @param {*} filename 文件名
* @param {*} file 图片base64
* @returns
*/
export const qiniuTokenAPI = (params) => fn(fetch.stringifyPost(Api.TOKEN, params));
/**
* @description: 上传七牛
* @param {*}
* @returns
*/
export const qiniuUploadAPI = (url, data, config) => uploadFn(fetch.basePost(url, data, config));
/**
* @description: 保存图片
* @param {*} format
* @param {*} hash
* @param {*} height
* @param {*} width
* @param {*} filekey
* @returns
*/
export const saveFileAPI = (params) => fn(fetch.stringifyPost(Api.SAVE_FILE, params));
<!--
* @Date: 2022-09-06 16:29:31
* @LastEditors: hookehuyr hookehuyr@gmail.com
* @LastEditTime: 2022-09-07 00:46:48
* @FilePath: /data-table/src/components/SignField/index.vue
* @Description: 文件描述
-->
<template>
<div class="sign-page">
<div class="label">{{ item.label }}<span v-if="item.required">&nbsp;*</span></div>
<div style="padding: 1rem;">
<vue-esign ref="esign" style="border: 1px dashed #c2c1c1;" :height="700" :isCrop="isCrop" :lineWidth="lineWidth"
:lineColor="lineColor" :bgColor.sync="bgColor" />
</div>
<div class="control-sign">
<button @click="handleReset">清空画板</button>
<button @click="handleGenerate">生成图片</button>
</div>
</div>
</template>
<script setup>
const props = defineProps({
item: Object
});
</script>
<script>
import { v4 as uuidv4 } from 'uuid';
import { qiniuTokenAPI, qiniuUploadAPI, saveFileAPI } from '@/api/common'
export default {
data() {
return {
lineWidth: 6,
lineColor: "#000000",
bgColor: "",
resultImg: "",
isCrop: false,
}
},
methods: {
//清空画板..
handleReset() {
this.$refs.esign.reset();
this.resultImg = "";
},
//生成签名图片..
handleGenerate () {
this.$refs.esign.generate()
.then(async res => {
// let fileName = "img1.png";
// let file = this.dataURLtoFile(res, fileName);
// console.log("file", file);
let affix = uuidv4();
let base64url = res.slice(res.indexOf(',') + 1); // 截取前缀的base64 data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAnoAAAJeCAYAA.......
// 获取七牛token
const { token, key, code } = await qiniuTokenAPI({ filename: `${affix}_sign`, file: base64url });
if (code) {
const config = {
headers: {
'Content-Type': 'application/octet-stream',
'Authorization': 'UpToken ' + token, // UpToken后必须有一个 ' '(空格)
}
}
// 上传七牛服务器
const { filekey, hash, image_info } = await qiniuUploadAPI('http://upload.qiniup.com/putb64/-1/key/' + key, base64url, config)
if (filekey) {
// 保存图片
const { data } = await saveFileAPI({ filekey, hash, format: image_info.format, height: image_info.height, width: image_info.width });
console.warn(data.src);
}
}
});
},
//将图片base64转换为文件
dataURLtoFile(dataurl, filename) {
var arr = dataurl.split(","),
mime = arr[0].match(/:(.*?);/)[1],
bstr = atob(arr[1]),
n = bstr.length,
u8arr = new Uint8Array(n);
while (n--) {
u8arr[n] = bstr.charCodeAt(n);
}
return new File([u8arr], filename, { type: mime });
}
}
}
</script>
<style lang="less" scoped>
.sign-page {
.label {
padding: 1rem 1rem 0 1rem;
font-size: 0.9rem;
font-weight: bold;
span {
color: red;
}
}
}
</style>
......@@ -10,6 +10,7 @@ import DatePickerField from '@/components/DatePickerField/index.vue'
import ImageUploaderField from '@/components/ImageUploaderField/index.vue'
import PhoneField from '@/components/PhoneField/index.vue'
import EmailField from '@/components/EmailField/index.vue'
import SignField from '@/components/SignField/index.vue'
/**
* 生成自定义组件类型
......@@ -24,6 +25,7 @@ import EmailField from '@/components/EmailField/index.vue'
* @type image_uploader 图片上传 ImageUploaderField
* @type phone 手机输入框 PhoneField
* @type email 邮箱输入框 EmailField
* @type sign 电子签名输入框 SignField
*/
export function createComponentType(data) {
// 判断类型和使用组件
......@@ -75,5 +77,9 @@ export function createComponentType(data) {
item.name = item.key;
item.component = EmailField;
}
if (item.component_props.name === 'sign') {
item.name = item.key;
item.component = SignField;
}
})
}
......
......@@ -2,7 +2,7 @@
* @Author: hookehuyr hookehuyr@gmail.com
* @Date: 2022-05-31 12:06:19
* @LastEditors: hookehuyr hookehuyr@gmail.com
* @LastEditTime: 2022-08-31 13:14:09
* @LastEditTime: 2022-09-06 16:25:52
* @FilePath: /data-table/src/main.js
* @Description:
*/
......@@ -14,6 +14,7 @@ import App from './App.vue';
import axios from '@/utils/axios';
// import 'default-passive-events'; // 解决Chrome控制台non-passive event listener输出问题
import { createPinia } from 'pinia';
import vueEsign from 'vue-esign'
const pinia = createPinia();
const app = createApp(App);
......@@ -24,5 +25,5 @@ app.config.warnHandler = () => null;
app.config.globalProperties.$http = axios; // 关键语句
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);
app.use(vueEsign)
app.mount('#app');
......
......@@ -2,8 +2,8 @@
* @Author: hookehuyr hookehuyr@gmail.com
* @Date: 2022-05-28 10:17:40
* @LastEditors: hookehuyr hookehuyr@gmail.com
* @LastEditTime: 2022-08-16 15:34:02
* @FilePath: /front/src/utils/axios.js
* @LastEditTime: 2022-09-06 18:01:36
* @FilePath: /data-table/src/utils/axios.js
* @Description:
*/
import axios from 'axios';
......@@ -13,8 +13,7 @@ import { strExist } from '@/utils/tools'
// import { parseQueryString } from '@/utils/tools'
axios.defaults.params = {
f: 'guanzong',
client_id: '81661'
f: 'customize',
};
/**
......
<!--
* @Date: 2022-07-18 10:22:22
* @LastEditors: hookehuyr hookehuyr@gmail.com
* @LastEditTime: 2022-09-06 15:53:40
* @LastEditTime: 2022-09-06 18:25:18
* @FilePath: /data-table/src/views/index.vue
* @Description: 首页
-->
......@@ -138,6 +138,16 @@ onMounted(() => {
// required: true,
// },
{
key: 'sign',
value: '',
label: '电子签名',
placeholder: '',
component: '',
component_props: {
name: 'sign'
},
},
{
key: 'city',
value: '天津市/天津市/和平区',
city_code: '120101',
......@@ -160,16 +170,16 @@ onMounted(() => {
// columns_type: ['year', 'month']
// },
// },
// {
// key: 'imageUploader',
// value: '',
// label: '图片上传',
// component_props: {
// name: 'image_uploader',
// image_type: ['jpg', 'png'],
// multiple: false
// }
// }
{
key: 'imageUploader',
value: '',
label: '图片上传',
component_props: {
name: 'image_uploader',
image_type: ['jpg', 'png'],
multiple: false
}
}
];
// 生成自定义组件
createComponentType(mockData.value)
......@@ -177,7 +187,7 @@ onMounted(() => {
const onSubmit = (values) => {
console.log('submit', values);
console.warn(mockData.value);
// console.warn(mockData.value);
};
</script>
......
......@@ -27,13 +27,13 @@ export default ({ command, mode }) => {
vue(),
styleImport({
resolves: [VantResolve()],
// libs: [
// {
// libraryName: 'vant',
// esModule: true,
// resolveStyle: name => `../es/${name}/style`
// }
// ]
libs: [
{
libraryName: 'vant',
esModule: true,
resolveStyle: name => `../es/${name}/style`
}
]
}), // 按需引入 vant 位置报错问题
dynamicImport(), // 增强 Vite 内置的 dynamic import, 支持在 import() 中使用别名
// legacy({
......
This diff could not be displayed because it is too large.