Browse Source

Merge branch 'liwenxuan_v4'

# Conflicts:
#	src/components/DesignForm/assembly/index.ts
#	src/components/DesignForm/formControlAttr.vue
#	src/components/DesignForm/public/form/form.vue
#	src/components/DesignForm/public/form/formGroup.vue
#	src/views/sysworkflow/codepage/page.vue
#	src/widget/index.ts
yjf_v2
超级管理员 2 years ago
parent
commit
327d0ace67
  1. 29
      src/api/DesignForm/types.ts
  2. BIN
      src/assets/404_images/untilUploadImg.png
  3. BIN
      src/assets/paintboard.png
  4. 71
      src/components/DesignForm/assembly/index.ts
  5. 354
      src/components/DesignForm/formControlAttr.vue
  6. 8
      src/components/DesignForm/public/form/form.vue
  7. 6
      src/components/DesignForm/public/form/formGroup.vue
  8. 3
      src/main.ts
  9. 46
      src/store/modules/lowcodevideo.ts
  10. 1
      src/views/knowledge/news/index.vue
  11. 5
      src/views/sysworkflow/codepage/createform.vue
  12. 17
      src/views/sysworkflow/codepage/page.vue
  13. 19
      src/views/sysworkflow/codepage/page_black.vue
  14. 181
      src/widget/carousel/index.vue
  15. 112
      src/widget/carousel/lowcodeCarousel.vue
  16. 12
      src/widget/index.ts
  17. 181
      src/widget/videoupload/index.vue
  18. 32
      src/widget/videoupload/videoUploadPlay.vue
  19. 186
      src/widget/writingboard/index.vue
  20. 124
      src/widget/writingboard/paintBoard.vue
  21. 713
      src/widget/writingboard/vueSignature.vue

29
src/api/DesignForm/types.ts

@ -154,3 +154,32 @@ export interface PublicAtrr{
inputStyle?:string; inputStyle?:string;
vIf?:any; vIf?:any;
} }
//低代码视频单个属性对象
export interface VideoMsg {
CreatedAt: string
UpdatedAt: string
fileSize: number
id: number
key: string
name: string
physicspath: string
size: string
tag: string
type: number
url: string//视频地址
videoReady:boolean//当前视频是否成功上传
videoHeight?:number//视频高
videoWidth?:number//视频宽
videoAutoPlay:boolean//是否自动播放
attrId:string//字段标识 e.g : videoUpAndPlay1705024134559
poster?:string//视频封面url
loop:boolean//是否循环播放
}
//低代码轮播图单个属性对象
export interface CarsuselConfig {
uploadFlag:boolean,
imgId:string,
imgSort:number,
imgUrl:string,
link:string
}

BIN
src/assets/404_images/untilUploadImg.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.4 KiB

BIN
src/assets/paintboard.png

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

71
src/components/DesignForm/assembly/index.ts

@ -248,6 +248,7 @@ const selectOption: any = [
config: {} config: {}
}, },
{ {
<<<<<<< HEAD
type: 'colorPicker', type: 'colorPicker',
label: '取色器', label: '取色器',
icon: 'color', icon: 'color',
@ -256,6 +257,24 @@ const selectOption: any = [
modelValue: '' modelValue: ''
}, },
config: {} config: {}
=======
type: 'orgCentent',
label: '行政组织',
icon: 'sliders',
iconFont: 'fa-sliders',
control: {
},
config: {}
},
{
type: 'signaturemap',
label: '签名板',
icon: 'faedit',
iconFont: 'fa-edit',
control: {
},
config: {}
>>>>>>> liwenxuan_v4
} }
] ]
}, },
@ -307,6 +326,58 @@ const selectOption: any = [
modelValue: '' modelValue: ''
}, },
config: {} config: {}
},
{
type: 'videoUpAndPlay',
label: '视频',
icon: '',
iconFont: 'fa-play-circle',
control: {
modelValue: '',
videoMsg:[{
CreatedAt: '',
UpdatedAt: '',
fileSize: "0n",
id: "0n",
key: '',
name: '',
physicspath: '',
size: '',
tag: '',
type: 0,
url: '',
videoReady: false,
videoHeight:400,
videoWidth:700,
videoAutoPlay: false,
attrId: '',
poster:'',
loop: false
}]
},
config: {}
},
{
type: 'lowcodeCarsusel',
label: '轮播图',
icon: '',
iconFont: 'fa-window-restore',
control: {
modelValue: '',
carsuselConfigArr:[{
uploadFlag:false,
imgId:'',
imgSort:1,
imgUrl: '',
link: ''
}],
config: {
carsuselWidth:448,
carsuselHeight:252,
interval:2000,
}
},
config: {}
} }
] ]
}, },

354
src/components/DesignForm/formControlAttr.vue

@ -14,8 +14,14 @@ import { PublicAtrr } from '@/api/DesignForm/types'
import { chineseToPinyin } from '@/api/DesignForm/requestapi' import { chineseToPinyin } from '@/api/DesignForm/requestapi'
<<<<<<< HEAD
// //
import MathFormula from '@/components/DesignForm/math/mathFormula.vue' import MathFormula from '@/components/DesignForm/math/mathFormula.vue'
=======
import { uploadUrl } from '@/api/DesignForm'
import { UploadFilled } from '@element-plus/icons-vue'
import { UploadFile, UploadFiles } from 'element-plus/es/components/upload/src/upload';
>>>>>>> liwenxuan_v4
const props = withDefaults( // const props = withDefaults( //
defineProps<{ defineProps<{
@ -40,6 +46,7 @@ const mathBoxShow = ref(false)
const emits = defineEmits<{ const emits = defineEmits<{
(e: 'openDialog', data: any): void (e: 'openDialog', data: any): void
(e: 'update:formOtherData', data: any): void (e: 'update:formOtherData', data: any): void
(e: 'videoMsgChange', data: VideoMsg): void
//(e: 'update:formData', data: any): void //(e: 'update:formData', data: any): void
//(e: 'update:formConfig', data: any): void //(e: 'update:formConfig', data: any): void
}>() }>()
@ -196,6 +203,7 @@ const attrList = computed(() => {
columnIndex = list[0].type === 'index' columnIndex = list[0].type === 'index'
} }
} }
const temp =reactive<PublicAtrr[]>([ const temp =reactive<PublicAtrr[]>([
{ {
label: '自定义Class', label: '自定义Class',
@ -596,6 +604,64 @@ const attrList = computed(() => {
path: 'config.componentName', path: 'config.componentName',
vShow: ['component'] vShow: ['component']
}, },
{
label: '轮播图设置',
value: config.carousel,
path: 'config.carousel',
type: 'carousel',
vIf: state.isSearch,
vShow: ['lowcodeCarsusel']
},
{
label: '上传视频',
value: config.uploadvideo,
path: 'config.uploadvideo',
type: 'uploadvideo_url',
vIf: state.isSearch,
vShow: ['videoUpAndPlay']
},
/* {
label: '上传视频封面',
value: config.uploadvideo,
path: 'config.uploadvideo',
type: 'uploadvideo_poster',
vIf: state.isSearch,
vShow: ['videoUpAndPlay']
}, */
{
label: '视频宽度(像素)',
value: config.uploadvideo,
path: 'config.uploadvideo',
type: 'uploadvideo_width',
vIf: state.isSearch,
vShow: ['videoUpAndPlay']
},
{
label: '视频高度(像素)',
value: config.uploadvideo,
path: 'config.uploadvideo',
type: 'uploadvideo_height',
vIf: state.isSearch,
vShow: ['videoUpAndPlay']
},
{
label: '视频自动播放',
value: config.uploadvideo,
path: 'config.uploadvideo',
type: 'uploadvideo_autoPlay',
vIf: state.isSearch,
vShow: ['videoUpAndPlay']
},
{
label: '视频循环播放',
value: config.uploadvideo,
path: 'config.uploadvideo',
type: 'uploadvideo_loopPlay',
vIf: state.isSearch,
vShow: ['videoUpAndPlay']
},
{ {
label: '上传地址', label: '上传地址',
value: control.action, value: control.action,
@ -796,6 +862,7 @@ watch(
} }
} }
) )
const fileSignAry = reactive<any>([]) const fileSignAry = reactive<any>([])
const controlChange = (obj: any, val: any) => { const controlChange = (obj: any, val: any) => {
// console.log("--1--",obj) // console.log("--1--",obj)
@ -1234,6 +1301,7 @@ const isNotWrite = (val:any) =>{
} }
return false return false
} }
<<<<<<< HEAD
const unitInfo = ref<any>() const unitInfo = ref<any>()
const formListmap = ref<any>() const formListmap = ref<any>()
const subUnit = ref<any>() const subUnit = ref<any>()
@ -1288,6 +1356,159 @@ const updataDigit = (key:sring,val:any) => {
}) })
// console.log("-5-->",props.formConfig) // console.log("-5-->",props.formConfig)
} }
=======
//liwenxuan 20240108 vidioupload start
import { VideoMsg } from '@/api/DesignForm/types'
import { genFileId } from 'element-plus'
import type { UploadInstance, UploadProps, UploadRawFile } from 'element-plus'
//:el-upload limit on-exceed
const upload = ref<UploadInstance>()
const handleExceed: UploadProps['onExceed'] = (files) => {
upload.value!.clearFiles()
const file = files[0] as UploadRawFile
file.uid = genFileId()
upload.value!.handleStart(file)
}
//videoMsg[videoIndex]
const videoIndex = ref(0);
//
function videoUploadOk(response: any, uploadFile: UploadFile, uploadFiles: UploadFiles) {
const { control = {} } : { control: any } = controlData.value
control.videoMsg[videoIndex.value].url = response.data.url;
control.videoMsg[videoIndex.value].videoReady = true;
control.videoMsg[videoIndex.value].CreatedAt = response.data.CreatedAt;
control.videoMsg[videoIndex.value].UpdatedAt = response.data.UpdatedAt;
control.videoMsg[videoIndex.value].fileSize = response.data.fileSize;
control.videoMsg[videoIndex.value].id = response.data.id;
control.videoMsg[videoIndex.value].key = response.data.key;
control.videoMsg[videoIndex.value].name = response.data.name;
control.videoMsg[videoIndex.value].physicspath = response.data.physicspath;
control.videoMsg[videoIndex.value].size = response.data.size;
control.videoMsg[videoIndex.value].tag = response.data.tag;
control.videoMsg[videoIndex.value].type = response.data.type;
}
//
function beforeRemove(){
return false;
}
//
function videoUploadErr(error: Error, uploadFile: UploadFile, uploadFiles: UploadFiles){
alert("上传失败,请重试")
console.log("上传失败"+error);
}
//liwenxuan 20240108 vidioupload end
//liwenxuan 20240111 carousel start
import { Delete,Plus } from '@element-plus/icons-vue'
import { CarsuselConfig } from '@/api/DesignForm/types'
import errimg from '@/assets/404_images/untilUploadImg.png'
import { v4 as uuidv4 } from "uuid";
//
const dialogTableVisible = ref(false)
//
const showImagePreview = ref(false)
//imgId
let currentUploadImgid = "";
//,imgiddesignForm.activeKey,,controlData.value.type=='lowcodeCarsusel',imgid=''uuid
watch(()=>store.activeKey, () => {
if(controlData.value.type==='lowcodeCarsusel'){
const carsuselConfigData:CarsuselConfig[] = controlData.value.control.carsuselConfigArr
carsuselConfigData.forEach(element => {
//console.log(element)
if(element.imgId==''){
//console.log("imgid")
let onlyNumber = uuidv4().replaceAll('-','').toString(); //
element.imgId = onlyNumber;
}
});
}
})
//
function carouselImgUploadSuccess(response: any, uploadFile: UploadFile, uploadFiles: UploadFiles){
const carsuselConfigData:CarsuselConfig[] = controlData.value.control.carsuselConfigArr
carsuselConfigData.forEach(element => {
if(element.imgId==currentUploadImgid){
element.imgUrl=response.data.url
element.uploadFlag=true
}
});
}
//url,
const carouselImgUrlList = computed(() => {
const list:string[] = [];
const carsuselConfigData:CarsuselConfig[] = store.controlAttr.control.carsuselConfigArr
carsuselConfigData.forEach(element => {
if(element.uploadFlag===true){
list.push(element.imgUrl)
}
});
return list;
})
//table
function createRow(clickedRow:any){
const newRow:CarsuselConfig = {
uploadFlag:false,
imgId:'',
imgSort:1,
imgUrl: '',
link: ''
}
let onlyNumber = uuidv4().replaceAll('-','').toString(); //
newRow.imgId = onlyNumber;
const nextSort:number = clickedRow.sort+1;
newRow.imgSort = nextSort;
controlData.value.control.carsuselConfigArr.push(newRow)
}
function deleteRow(clickedRow:any){
//alert('')
const carsuselConfigData:CarsuselConfig[] = controlData.value.control.carsuselConfigArr
for (var i = 0; i < carsuselConfigData.length; i++) {
if(carsuselConfigData[i].imgId===clickedRow.imgId){
carsuselConfigData.splice(i, 1);
i--; //
}
}
}
//
const closePreview = () => {
showImagePreview.value = false
}
//
const handlePreview = (index: number, row: any) => {
showImagePreview.value = true
}
const changeCurrentUploadImgid = (clickedRow:any) => {
currentUploadImgid = clickedRow.imgId
}
//liwenxuan 20240111 carousel end
>>>>>>> liwenxuan_v4
</script> </script>
<template> <template>
<div class="sidebar-tools"> <div class="sidebar-tools">
@ -1296,6 +1517,10 @@ const updataDigit = (key:sring,val:any) => {
<el-form size="small" class="form"> <el-form size="small" class="form">
<div class=""><h3>通用属性</h3></div> <div class=""><h3>通用属性</h3></div>
<template v-for="(item, index) in attrList" :key="index"> <template v-for="(item, index) in attrList" :key="index">
<<<<<<< HEAD
=======
>>>>>>> liwenxuan_v4
<el-form-item :label="item.label"> <el-form-item :label="item.label">
<el-select <el-select
v-if="item.type === 'select'" v-if="item.type === 'select'"
@ -1320,6 +1545,7 @@ const updataDigit = (key:sring,val:any) => {
v-model="item.value" v-model="item.value"
@change="controlChange(item, $event)" @change="controlChange(item, $event)"
/> />
<<<<<<< HEAD
<template v-else-if="item.type === 'digitpage'"> <template v-else-if="item.type === 'digitpage'">
<el-row> <el-row>
<el-col :span="12"> <el-col :span="12">
@ -1353,6 +1579,65 @@ const updataDigit = (key:sring,val:any) => {
<MathFormula v-model:show="mathBoxShow" :sub-unit="subUnit" :unit-info="attrList" :form-listmap="formListmap" @updata-digit="updataDigit" /> <MathFormula v-model:show="mathBoxShow" :sub-unit="subUnit" :unit-info="attrList" :form-listmap="formListmap" @updata-digit="updataDigit" />
</template> </template>
=======
<el-row v-else-if="item.type === 'uploadvideo_url'">
<!-- {{ controlData.control }} -->
<el-upload
:action="uploadUrl" :before-remove="beforeRemove"
:on-success="videoUploadOk" :show-file-list="true"
:on-error="videoUploadErr"
:limit="1"
:on-exceed="handleExceed"
accept=".mp4,.MOV,.WMV,.FLV,.AVI,.AVCHD,.WebM,.MKV,.rmvb">
<el-button v-if="!controlData.control.videoMsg[videoIndex].videoReady" type="primary">点此上传</el-button>
<el-button v-if="controlData.control.videoMsg[videoIndex].videoReady" type="primary">已上传,点击修改</el-button>
</el-upload>
<!-- <el-button v-if="controlData.control.videoMsg[videoIndex].videoReady" type="primary">已上传,点击修改</el-button>
v-if="!controlData.control.videoMsg[videoIndex].videoReady"-->
</el-row>
<!-- <el-row v-else-if="item.type === 'uploadvideo_poster'">
<el-upload
v-if="controlData.control.videoMsg[videoIndex].videoReady&&controlData.control.videoMsg[videoIndex].poster===''"
:action="uploadUrl" :before-remove="beforeRemove"
:on-success="videoUploadOk" :show-file-list="true"
:on-error="videoUploadErr"
accept=".mp4,.MOV,.WMV,.FLV,.AVI,.AVCHD,.WebM,.MKV,.rmvb">
<el-tooltip
class="box-item"
content="不上传则视频封面默认为视频第一帧"
placement="top-end"
>
<el-button type="primary">点此上传</el-button>
</el-tooltip>
</el-upload>
<el-button v-if="!controlData.control[videoIndex].videoReady" type="warning">请先上传视频</el-button>
<el-button v-if="controlData.control.videoMsg[videoIndex].videoReady&&controlData.control.videoMsg[videoIndex].poster!=''" type="primary">已上传,点击修改</el-button>
</el-row> -->
<el-row v-else-if="item.type === 'uploadvideo_autoPlay'">
<el-switch v-model="controlData.control.videoMsg[videoIndex].videoAutoPlay" />
</el-row>
<el-row v-else-if="item.type === 'uploadvideo_loopPlay'">
<el-switch v-model="controlData.control.videoMsg[videoIndex].loop" />
</el-row>
<el-row v-else-if="item.type === 'uploadvideo_width'">
<el-input-number v-model="controlData.control.videoMsg[videoIndex].videoWidth" :step="50" :max="4096"/>
</el-row>
<el-row v-else-if="item.type === 'uploadvideo_height'">
<el-input-number v-model="controlData.control.videoMsg[videoIndex].videoHeight" :step="50" :max="2160"/>
</el-row>
<el-row v-else-if="item.type === 'carousel'">
<el-button type="primary" append-to-body="true" modal="true" @click="dialogTableVisible = true" >轮播图设置</el-button>
</el-row>
>>>>>>> liwenxuan_v4
<el-input <el-input
v-else v-else
:type="item.inputStyle" :type="item.inputStyle"
@ -1361,9 +1646,16 @@ const updataDigit = (key:sring,val:any) => {
@input="controlChange(item, $event)" @input="controlChange(item, $event)"
/> />
<<<<<<< HEAD
=======
>>>>>>> liwenxuan_v4
</el-form-item> </el-form-item>
</template> </template>
<template v-if="controlData.config"> <template v-if="controlData.config">
<el-form-item label="联动条件"> <el-form-item label="联动条件">
<el-switch v-model="controlData.config.linkKey" /> <el-switch v-model="controlData.config.linkKey" />
@ -1845,6 +2137,68 @@ const updataDigit = (key:sring,val:any) => {
</div> </div>
<!-- 轮播图设置弹窗 20240122 start -->
<el-dialog v-model="dialogTableVisible" title="轮播图设置" top="20px" style="margin-top:70px,margin-left:270px">
<div v-if="controlData.type=='lowcodeCarsusel'" style="margin-bottom: 30px;">
<span style="margin-right: 10px;">轮播图宽度(像素):</span><el-input-number v-model="controlData.control.config.carsuselWidth" :step="64" :max="4096" :min="178"/>
<span style="margin-left:15px;margin-right: 10px;">轮播图高度(像素):</span><el-input-number v-model="controlData.control.config.carsuselHeight" :step="36" :max="4096" :min="100"/>
<span style="margin-left:15px;margin-right: 10px;">切图间隔(毫秒):</span><el-input-number v-model="controlData.control.config.interval" :step="200"/>
</div>
<el-table v-if="controlData.type=='lowcodeCarsusel'" :data="controlData.control.carsuselConfigArr" border style="width: 100%" row-key="imgId">
<el-table-column label="设置图片" width="180" >
<template #default="propFlag">
<el-upload
:action="uploadUrl" :before-remove="beforeRemove"
:on-success="carouselImgUploadSuccess"
:on-error="videoUploadErr"
:limit="1"
accept=".jpg,.jpeg,.png,.tif,.tga,.bmp,.dds,.svg,.eps,.pdf,.hdr,.raw,.exr,.psd,.afphoto,.afdesign">
<el-button v-if="!propFlag.row.uploadFlag" type="primary" @click="changeCurrentUploadImgid(propFlag.row)">点此上传</el-button>
<el-button v-if="propFlag.row.uploadFlag" type="primary" @click="changeCurrentUploadImgid(propFlag.row)">已上传,点击修改</el-button>
</el-upload>
</template>
</el-table-column>
<el-table-column prop="imgUrl" label="查看图片" width="180">
<template #default="imgScope">
<!-- {{ imgScope.row.imgUrl }} -->
<el-image style="width: 100px; height: 100px;cursor:pointer;" :src="imgScope.row.imgUrl" fit="contain" alt="暂未上传" @click="handlePreview(imgScope.$index, imgScope.row)" >
<template #error>
<div class="image-slot">
<el-image style="width: 100px; height: 100px;" :src="errimg" fit="contain" ></el-image>
</div>
</template>
</el-image>
</template>
</el-table-column>
<el-table-column prop="link" label="链接地址">
<template #default="linkScope">
<el-input v-model="linkScope.row.link" placeholder="输入图片的链接地址" />
</template>
</el-table-column>
<el-table-column fixed="right" label="操作(增/删)" width="120" >
<template #default="scope">
<el-button v-if="controlData.control.carsuselConfigArr.length>1" type="danger" :icon="Delete" circle @click="deleteRow(scope.row)"/>
<el-button v-if="controlData.control.carsuselConfigArr[controlData.control.carsuselConfigArr.length-1].imgId == scope.row.imgId" type="success" :icon="Plus" circle @click="createRow(scope.row)" />
</template>
</el-table-column>
</el-table>
</el-dialog>
<!-- 图片预览 -->
<el-image-viewer
v-if="showImagePreview"
:zoom-rate="1.2"
:url-list="carouselImgUrlList"
@close="closePreview"
/>
<!-- 轮播图设置弹窗 liwenxuan 20240122 end -->
</template> </template>
<style lang='scss' scoped> <style lang='scss' scoped>

8
src/components/DesignForm/public/form/form.vue

@ -166,6 +166,7 @@ const forEachGetFormModel = (list: FormList[], obj: any) => {
// tProp // tProp
provide(constControlChange, ({ key, value, data, tProp, type, attribute }: any) => { provide(constControlChange, ({ key, value, data, tProp, type, attribute }: any) => {
<<<<<<< HEAD
// console.log("----------1--------->",key) // console.log("----------1--------->",key)
// console.log("----------2--------->",value) // console.log("----------2--------->",value)
// console.log("----------3--------->",data) // console.log("----------3--------->",data)
@ -240,6 +241,9 @@ provide(constControlChange, ({ key, value, data, tProp, type, attribute }: any)
// console.log("-5-constControlChange-->",type) // console.log("-5-constControlChange-->",type)
// console.log("-6-constControlChange-->",attribute) // console.log("-6-constControlChange-->",attribute)
=======
console.log("监听表单--constControlChange-->",key, value, data, tProp,type,attribute)
>>>>>>> liwenxuan_v4
if (typeof props.changeKeyVal === 'function') { if (typeof props.changeKeyVal === 'function') {
props.changeKeyVal(key, value,type,attribute) props.changeKeyVal(key, value,type,attribute)
} }
@ -1085,7 +1089,11 @@ defineExpose({
'detail-form': type === 3 || type === 4 || type === 1 'detail-form': type === 3 || type === 4 || type === 1
}" }"
> >
<<<<<<< HEAD
=======
>>>>>>> liwenxuan_v4
<FormGroup :tableinfo="formData.form" :numrun="numrun" :data="formData.list" /> <FormGroup :tableinfo="formData.form" :numrun="numrun" :data="formData.list" />
<slot></slot> <slot></slot>

6
src/components/DesignForm/public/form/formGroup.vue

@ -427,7 +427,13 @@ onMounted(()=>{
</div> </div>
</template> </template>
<<<<<<< HEAD
<DigitPage v-else-if="element.type === 'digitpage'" :data="element" /> <DigitPage v-else-if="element.type === 'digitpage'" :data="element" />
=======
<VideoUpAndPlay v-else-if="element.type === 'videoUpAndPlay'" :data="element" />
<LowcodeCarsusel v-else-if="element.type === 'lowcodeCarsusel'" :data="element" />
<SignatureMap v-else-if="element.type === 'signaturemap'" :data="element" />
>>>>>>> liwenxuan_v4
<OrgCentent v-else-if="element.type === 'orgCentent'" :data="element" /> <OrgCentent v-else-if="element.type === 'orgCentent'" :data="element" />
<BaiduMap v-else-if="element.type === 'baidumap'" :data="element" /> <BaiduMap v-else-if="element.type === 'baidumap'" :data="element" />
<OrgCitys v-else-if="element.type === 'organization'" :data="element" /> <OrgCitys v-else-if="element.type === 'organization'" :data="element" />

3
src/main.ts

@ -26,6 +26,7 @@ import '@/styles/workflowcss/override-element-ui.scss'
import ComComponents from '@/components/DesignForm/index' import ComComponents from '@/components/DesignForm/index'
import ComWidget from '@/widget/index' import ComWidget from '@/widget/index'
import AKDesign from '@/views/sysworkflow/codepage/index' import AKDesign from '@/views/sysworkflow/codepage/index'
import * as pinia from './store/index'
const app = createApp(App); const app = createApp(App);
// 全局注册 自定义指令(directive) // 全局注册 自定义指令(directive)
@ -45,6 +46,6 @@ app.directive('focus', {
app.use(router).use(i18n).use(ComComponents).use(ComWidget).use(ElementPlus, { app.use(router).use(i18n).use(ComComponents).use(ComWidget).use(ElementPlus, {
locale: zhCn locale: zhCn
}).use(AKDesign).mount('#app'); }).use(AKDesign).use(pinia.store).mount('#app');

46
src/store/modules/lowcodevideo.ts

@ -0,0 +1,46 @@
//定义组合式API仓库
import { defineStore } from "pinia";
import { ref, computed,watch,reactive} from 'vue';
import { VideoMsg } from '@/api/DesignForm/types'
/* type VideoObj = {} */
//创建小仓库
let uselowcodevideoStore = defineStore('lowcodevideo', () => {
//视频地址
//const videoResource = ref<string>();
//是否上传成功
//const videoReady = ref(false);
//成功后接受的视频详细信息
const videoMsg = reactive<VideoMsg>({
CreatedAt: "",
UpdatedAt: "",
fileSize: 0,
id: 0,
key: "",
name: "",
physicspath: "",
size: "",
tag: "",
type: 0,
url: "",
videoReady: false,
videoAutoPlay: false,
attrId: "",
loop: false
});
const videoMsgUse = reactive<VideoMsg[]>([])
const videoOnShowIndex = ref(0);//当当前表单有多个视频控件时,字段配置栏目前所展示的视频属性在pinia数组中的索引,用来在formControlAttr.vue中的dom属性上绑定显示数据
//表单视频信息数组
/* const videoArr = reactive<> */
//务必要返回一个对象:属性与方法可以提供给组件使用
return {
videoOnShowIndex,
videoMsgUse,
}
});
export default uselowcodevideoStore;

1
src/views/knowledge/news/index.vue

@ -433,4 +433,3 @@ li:hover {
} }
</style> </style>

5
src/views/sysworkflow/codepage/createform.vue

@ -32,7 +32,7 @@ import AceDrawer from '@/components/DesignForm/aceDrawer.vue'
import { ref, reactive, provide, onMounted } from 'vue' import { ref, reactive, provide, onMounted } from 'vue'
import { ElMessage } from 'element-plus' import { ElMessage } from 'element-plus'
import { useLayoutStore } from '@/store/DesignForm/layout' import { useLayoutStore } from '@/store/DesignForm/layout'
import { FormData,formStruct,DrawerStruct } from '@/api/DesignForm/types' import { FormData,formStruct,DrawerStruct, VideoMsg } from '@/api/DesignForm/types'
import { customerFormVersionCont } from '@/api/DesignForm/type' import { customerFormVersionCont } from '@/api/DesignForm/type'
import { saveProductionForm,getOneProductionForm,haveCustomerFormVersion,editCustomerFormInfo,saveAsNewVersion,enableVersion,judgeSubmitCancel } from '@/api/DesignForm/requestapi' import { saveProductionForm,getOneProductionForm,haveCustomerFormVersion,editCustomerFormInfo,saveAsNewVersion,enableVersion,judgeSubmitCancel } from '@/api/DesignForm/requestapi'
@ -892,6 +892,9 @@ const editversionstaus = (id:string) =>{
:form-list="state.formData.list" :form-list="state.formData.list"
@open-dialog="openAceEditDrawer" @open-dialog="openAceEditDrawer"
/> />
<!-- liwenxuan 轮播图设置界面 20240112 start -->
<!-- liwenxuan 轮播图设置界面 20240112 end -->
<ace-drawer <ace-drawer
v-model="drawer.visible" v-model="drawer.visible"
:title="drawer.title" :title="drawer.title"

17
src/views/sysworkflow/codepage/page.vue

@ -4,6 +4,7 @@
@ 备注: 模拟计算公式编辑器 @ 备注: 模拟计算公式编辑器
--> -->
<template> <template>
<<<<<<< HEAD
<el-dialog <el-dialog
v-model="isShow" v-model="isShow"
title="公式编辑" title="公式编辑"
@ -409,3 +410,19 @@ const computeNumber = (val:string) =>{
} }
</style> </style>
=======
<SignatureMap />
<PaintBoard @updataconbt="qianming" />
</template>
<script lang='ts' setup>
import PaintBoard from '@/widget/writingboard/paintBoard.vue';
const qianming = (val:any) =>{
console.log("图片回传--->",val)
}
</script>
<style lang='scss' scoped>
</style>
>>>>>>> liwenxuan_v4

19
src/views/sysworkflow/codepage/page_black.vue

@ -0,0 +1,19 @@
<!--
@ 作者: 秦东
@ 时间: 2023-12-07 16:49:57
@ 备注:
-->
<template>
<baidu-map class="map" ak="ljiKlTAsS7SNVqDM16IUwRVFFhrvbxiF" v="3.0" :center="{lng: 116.404, lat: 39.915}" :zoom="15" :scroll-wheel-zoom="true">
<bm-map-type :map-types="['BMAP_NORMAL_MAP', 'BMAP_HYBRID_MAP']" anchor="BMAP_ANCHOR_TOP_LEFT"></bm-map-type>
</baidu-map>
</template>
<script lang='ts' setup>
import { BaiduMap } from 'vue-baidu-map-3x'
</script>
<style lang='scss' scoped>
.map {
width: 100%;
height: calc(100vh - 90px);
}
</style>

181
src/widget/carousel/index.vue

@ -0,0 +1,181 @@
<!--
@ 作者: 李文轩
@ 时间: 2024-01-02 13:49:57
@ 备注:
-->
<template>
<el-form-item
v-bind="data.item"
:prop="tProp || data.name"
:class="config.className"
:rules="itemRules as any"
:label="getLabel(data.item as FormItem)"
>
<input v-model="value" type="hidden" >
</el-form-item>
<LowcodeCarousel :data="props.data"></LowcodeCarousel>
</template>
<script lang='ts' setup>
import LowcodeCarousel from './lowcodeCarousel.vue';
import {
constControlChange,
constFormProps,
} from '@/api/DesignForm/utils'
import validate from '@/api/DesignForm/validate'
import { FormItem, FormList } from '@/api/DesignForm/types'
const props = withDefaults(
defineProps<{
data: FormList
tablekey: any
numrun?: number
modelValue?: any //
tProp?: string // form-itemprop
}>(),
{}
)
const emits = defineEmits<{
(e: 'update:modelValue', numVal: any): void
}>()
const formProps = inject(constFormProps, {}) as any
const type = computed(() => {
return formProps.value.type
})
const config = computed(() => {
return props.data.config || {}
})
const changeEvent = inject(constControlChange, '') as any
const value = computed({
get() {
if (props.tProp) {
//
return props.modelValue
} else {
return formProps.value.model[props.data.name]
}
},
set(newVal: any) {
if (props.tProp) {
emits('update:modelValue', newVal)
}
updateModel(newVal)
}
})
const updateModel = (val: any) => {
let controlAttribute = ""
if(props.data.control){
if(props.data.control.type){
controlAttribute = props.data.control.type
}
}
changeEvent &&
changeEvent({
key: props.data.name,
value: val,
data: props.data,
tProp: props.tProp,
type: props.data.type,
attribute: controlAttribute
})
}
const getLabel = (ele: FormItem) => {
const showColon = formProps.value.showColon ? ':' : ''
if (ele) {
return ele.showLabel ? '' : ele.label + showColon
} else {
return ''
}
}
// item
const itemRules = computed(() => {
let temp
const itemR: any = props.data.item?.rules || []
const customR = formatCustomRules()
// undefined
if (itemR?.length || customR?.length) {
temp = [...customR, ...itemR]
}
return temp
})
// customRulesrules
const formatCustomRules = () => {
const rulesReg: any = {}
validate &&
validate.forEach(item => {
rulesReg[item.type] = item.regExp
})
// 使provide
const temp: any = []
props.data.customRules?.forEach((item: any) => {
if (!item.message && item.type !== 'methods') {
return //
}
let obj = {}
if (item.type === 'required') {
obj = { required: true }
} else if (item.type === 'rules') {
//
obj = { pattern: item.rules }
} else if (item.type === 'methods') {
//
const methods: any = item.methods
if (methods) {
obj = { validator: inject(methods, {}) }
}
} else if (item.type) {
obj = { pattern: rulesReg[item.type as string] }
}
// push
let message: any = { message: item.message }
if (!item.message) {
// 使validatormessage使 callback(new Error('x'));
message = {}
}
temp.push(
Object.assign(
{
trigger: item.trigger || 'blur'
},
obj,
message
)
)
})
return temp
}
</script>
<style lang='scss' scoped>
.imgbox{
padding: 0 5px;
max-width: 300px;
max-height: 200px;
width: 100%;
height: 200px;
.image-slot {
display: flex;
justify-content: center;
align-items: center;
width: 100%;
height: 100%;
background: var(--el-fill-color-light);
color: var(--el-text-color-secondary);
font-size: 30px;
}
}
</style>

112
src/widget/carousel/lowcodeCarousel.vue

@ -0,0 +1,112 @@
<template>
<el-carousel arrow="hover" :style="{height:carsuselHeight,width:carsuselWidth}" :interval = "interval" :height="carsuselHeight" trigger="click">
<el-carousel-item v-for="item in carsouselData" :key="item.imgId">
<el-image
style="width: 100%; height: 100%;cursor:pointer;" :src="item.imgUrl" fit="cover" alt="暂未上传"
@click="handleLink(item)">
<template #error>
<!-- <div class="image-slot"> -->
<el-image style="width: 100%; height: 100%" :src="errimg" fit="fill" @click="noMsg" ></el-image>
<!-- </div> -->
<!-- <div class="image-slot">
<el-icon><icon-picture /></el-icon>
</div> -->
</template>
</el-image>
</el-carousel-item>
</el-carousel>
</template>
<script setup lang="ts">
import { CarsuselConfig } from '@/api/DesignForm/types'
import { Picture as IconPicture } from '@element-plus/icons-vue'
import errimg from '@/assets/404_images/imgNotFound.png'
//import errimg from '@/assets/404_images/untilUploadImg.png'
const props = defineProps({
// eslint-disable-next-line vue/require-default-prop
data: {
type: Object,
}
})
const carsouselData: CarsuselConfig[] = props.data?.control.carsuselConfigArr
const carsuselHeight = props.data?.control.config.carsuselHeight+'px'
const carsuselWidth = props.data?.control.config.carsuselWidth+'px'
const interval = props.data?.control.config.interval
function errorImg(e: any) {
e.srcElement.src = errimg;
//.once
e.srcElement.onerror = null; //
}
function handleLink(item: any) {
console.log("handleLink")
let url = "";
let urlStart = 'http://'
// http:// 7
//https:// 8
if (item.link.length < 7) {
if (item.link == '') {
alert("未配置跳转地址")
return
}
url = urlStart + "" + item.link
} else {
const linkStartComplete1 = item.link.startsWith("http://")
const linkStartComplete2 = item.link.startsWith("https://")
if (linkStartComplete1 || linkStartComplete2) {
url = item.link
} else {
url = urlStart + "" + item.link
}
}
window.open(url, '_blank')
}
function noMsg() {
alert("轮播图未配置")
}
</script>
<style scoped>
.image-slot {
display: flex;
justify-content: center;
align-items: center;
width: 100%;
height: 100%;
background: var(--el-fill-color-light);
color: var(--el-text-color-secondary);
font-size: 30px;
}
.demonstration {
color: var(--el-text-color-secondary);
}
.el-carousel__item h3 {
color: black;
opacity: 0.75;
line-height: 150px;
margin: 0;
text-align: center;
}
.el-carousel__item:nth-child(2n) {
background-color: #99a9bf;
}
.el-carousel__item:nth-child(2n + 1) {
background-color: #d3dce6;
}
/* .el-carousel__item:nth-child(2n) {
background-color: white;
}
.el-carousel__item:nth-child(2n + 1) {
background-color: white;
} */
</style>

12
src/widget/index.ts

@ -6,7 +6,13 @@ import urlLink from './urllink/index.vue'
import orgCitys from './orgcitys/index.vue' import orgCitys from './orgcitys/index.vue'
import baiduMap from './baidumap/index.vue' import baiduMap from './baidumap/index.vue'
import orgCentent from './org/index.vue' import orgCentent from './org/index.vue'
<<<<<<< HEAD
import digitPage from './digitpage/index.vue' import digitPage from './digitpage/index.vue'
=======
import signatureMap from './writingboard/index.vue'
import videoUpAndPlay from './videoupload/index.vue'
import lowcodeCarsusel from './carousel/index.vue'
>>>>>>> liwenxuan_v4
export default (app: any) => { export default (app: any) => {
app.component('SerialNumber', serialNumber) app.component('SerialNumber', serialNumber)
@ -14,5 +20,11 @@ export default (app: any) => {
app.component('OrgCitys', orgCitys) app.component('OrgCitys', orgCitys)
app.component('BaiduMap', baiduMap) app.component('BaiduMap', baiduMap)
app.component('OrgCentent', orgCentent) app.component('OrgCentent', orgCentent)
<<<<<<< HEAD
app.component('DigitPage', digitPage) app.component('DigitPage', digitPage)
=======
app.component('SignatureMap', signatureMap)
app.component('VideoUpAndPlay',videoUpAndPlay)
app.component('LowcodeCarsusel',lowcodeCarsusel)
>>>>>>> liwenxuan_v4
} }

181
src/widget/videoupload/index.vue

@ -0,0 +1,181 @@
<!--
@ 作者: 李文轩
@ 时间: 2024-01-02 13:49:57
@ 备注:
-->
<template>
<el-form-item
v-bind="data.item"
:prop="tProp || data.name"
:class="config.className"
:rules="itemRules as any"
:label="getLabel(data.item as FormItem)"
>
<input v-model="value" type="hidden" >
</el-form-item>
<VideoUploadPlay :data="props.data"></VideoUploadPlay>
</template>
<script lang='ts' setup>
import VideoUploadPlay from './videoUploadPlay.vue';
import {
constControlChange,
constFormProps,
} from '@/api/DesignForm/utils'
import validate from '@/api/DesignForm/validate'
import { FormItem, FormList } from '@/api/DesignForm/types'
const props = withDefaults(
defineProps<{
data: FormList
tablekey: any
numrun?: number
modelValue?: any //
tProp?: string // form-itemprop
}>(),
{}
)
const emits = defineEmits<{
(e: 'update:modelValue', numVal: any): void
}>()
const formProps = inject(constFormProps, {}) as any
const type = computed(() => {
return formProps.value.type
})
const config = computed(() => {
return props.data.config || {}
})
const changeEvent = inject(constControlChange, '') as any
const value = computed({
get() {
if (props.tProp) {
//
return props.modelValue
} else {
return formProps.value.model[props.data.name]
}
},
set(newVal: any) {
if (props.tProp) {
emits('update:modelValue', newVal)
}
updateModel(newVal)
}
})
const updateModel = (val: any) => {
let controlAttribute = ""
if(props.data.control){
if(props.data.control.type){
controlAttribute = props.data.control.type
}
}
changeEvent &&
changeEvent({
key: props.data.name,
value: val,
data: props.data,
tProp: props.tProp,
type: props.data.type,
attribute: controlAttribute
})
}
const getLabel = (ele: FormItem) => {
const showColon = formProps.value.showColon ? ':' : ''
if (ele) {
return ele.showLabel ? '' : ele.label + showColon
} else {
return ''
}
}
// item
const itemRules = computed(() => {
let temp
const itemR: any = props.data.item?.rules || []
const customR = formatCustomRules()
// undefined
if (itemR?.length || customR?.length) {
temp = [...customR, ...itemR]
}
return temp
})
// customRulesrules
const formatCustomRules = () => {
const rulesReg: any = {}
validate &&
validate.forEach(item => {
rulesReg[item.type] = item.regExp
})
// 使provide
const temp: any = []
props.data.customRules?.forEach((item: any) => {
if (!item.message && item.type !== 'methods') {
return //
}
let obj = {}
if (item.type === 'required') {
obj = { required: true }
} else if (item.type === 'rules') {
//
obj = { pattern: item.rules }
} else if (item.type === 'methods') {
//
const methods: any = item.methods
if (methods) {
obj = { validator: inject(methods, {}) }
}
} else if (item.type) {
obj = { pattern: rulesReg[item.type as string] }
}
// push
let message: any = { message: item.message }
if (!item.message) {
// 使validatormessage使 callback(new Error('x'));
message = {}
}
temp.push(
Object.assign(
{
trigger: item.trigger || 'blur'
},
obj,
message
)
)
})
return temp
}
</script>
<style lang='scss' scoped>
.imgbox{
padding: 0 5px;
max-width: 300px;
max-height: 200px;
width: 100%;
height: 200px;
.image-slot {
display: flex;
justify-content: center;
align-items: center;
width: 100%;
height: 100%;
background: var(--el-fill-color-light);
color: var(--el-text-color-secondary);
font-size: 30px;
}
}
</style>

32
src/widget/videoupload/videoUploadPlay.vue

@ -0,0 +1,32 @@
<template>
<!-- <p>{{ videoHeight }}</p>
<p>{{ videoWidth }}</p> -->
<video
:src="data?.control.videoMsg[0].url"
:loop="data?.control.videoMsg[0].loop"
:autoplay="data?.control.videoMsg[0].videoAutoPlay"
:style="{height:videoHeight,width:videoWidth}"
controls
>
</video>
</template>
<script setup lang="ts">
const props = defineProps({
// eslint-disable-next-line vue/require-default-prop
data: {
type: Object,
}
})
const videoHeight = props.data?.control.videoMsg[0].videoHeight+'px'
const videoWidth = props.data?.control.videoMsg[0].videoWidth+'px'
</script>
<style scoped></style>

186
src/widget/writingboard/index.vue

@ -0,0 +1,186 @@
<!--
@ 作者: 秦东
@ 时间: 2023-12-08 16:49:57
@ 备注:
-->
<template>
<el-form-item
v-bind="data.item"
:prop="tProp || data.name"
:class="config.className"
:rules="itemRules as any"
:label="getLabel(data.item as FormItem)"
>
<input v-model="value" type="hidden" >
</el-form-item>
<PaintBoard @updataconbt="qianming" />
</template>
<script lang='ts' setup>
import PaintBoard from './paintBoard.vue';
import {
constControlChange,
constFormProps,
} from '@/api/DesignForm/utils'
import validate from '@/api/DesignForm/validate'
import { FormItem, FormList } from '@/api/DesignForm/types'
const props = withDefaults(
defineProps<{
data: FormList
tablekey: any
numrun?: number
modelValue?: any //
tProp?: string // form-itemprop
}>(),
{}
)
const emits = defineEmits<{
(e: 'update:modelValue', numVal: any): void
}>()
const formProps = inject(constFormProps, {}) as any
const type = computed(() => {
return formProps.value.type
})
const config = computed(() => {
return props.data.config || {}
})
const changeEvent = inject(constControlChange, '') as any
const value = computed({
get() {
if (props.tProp) {
//
return props.modelValue
} else {
return formProps.value.model[props.data.name]
}
},
set(newVal: any) {
if (props.tProp) {
emits('update:modelValue', newVal)
}
updateModel(newVal)
}
})
const updateModel = (val: any) => {
let controlAttribute = ""
if(props.data.control){
if(props.data.control.type){
controlAttribute = props.data.control.type
}
}
changeEvent &&
changeEvent({
key: props.data.name,
value: val,
data: props.data,
tProp: props.tProp,
type: props.data.type,
attribute: controlAttribute
})
}
const getLabel = (ele: FormItem) => {
const showColon = formProps.value.showColon ? ':' : ''
if (ele) {
return ele.showLabel ? '' : ele.label + showColon
} else {
return ''
}
}
// item
const itemRules = computed(() => {
let temp
const itemR: any = props.data.item?.rules || []
const customR = formatCustomRules()
// undefined
if (itemR?.length || customR?.length) {
temp = [...customR, ...itemR]
}
return temp
})
// customRulesrules
const formatCustomRules = () => {
const rulesReg: any = {}
validate &&
validate.forEach(item => {
rulesReg[item.type] = item.regExp
})
// 使provide
const temp: any = []
props.data.customRules?.forEach((item: any) => {
if (!item.message && item.type !== 'methods') {
return //
}
let obj = {}
if (item.type === 'required') {
obj = { required: true }
} else if (item.type === 'rules') {
//
obj = { pattern: item.rules }
} else if (item.type === 'methods') {
//
const methods: any = item.methods
if (methods) {
obj = { validator: inject(methods, {}) }
}
} else if (item.type) {
obj = { pattern: rulesReg[item.type as string] }
}
// push
let message: any = { message: item.message }
if (!item.message) {
// 使validatormessage使 callback(new Error('x'));
message = {}
}
temp.push(
Object.assign(
{
trigger: item.trigger || 'blur'
},
obj,
message
)
)
})
return temp
}
const qianming = (val:any) =>{
console.log("图片回传--->",val)
value.value = val
}
</script>
<style lang='scss' scoped>
.imgbox{
padding: 0 5px;
max-width: 300px;
max-height: 200px;
width: 100%;
height: 200px;
.image-slot {
display: flex;
justify-content: center;
align-items: center;
width: 100%;
height: 100%;
background: var(--el-fill-color-light);
color: var(--el-text-color-secondary);
font-size: 30px;
}
}
</style>

124
src/widget/writingboard/paintBoard.vue

@ -0,0 +1,124 @@
<template>
<div >
<div v-show="pantareaisShow" id="canvasWrap" style="width:100%;">
<vueOnlineSignature ref="vueSignatureRef" v-bind="params"/>
</div>
<img v-if="imagesSRC" :src="imagesSRC" alt="" style="max-width: 100%">
<div class="buttonList" >
<div class="button" @click="confirm">签名完成</div>
<div class="button" @click="reset">重签</div>
</div>
</div>
</template>
<script setup lang="ts">
import vueOnlineSignature from '@/widget/writingboard/vueSignature.vue';
import { ref, reactive } from 'vue'
const pantareaisShow = ref<boolean>(true)
const params = reactive<any>({
width: 751,// 1px,1px
height: 301,//
lineWidth: 5,
lineColor: '#000',
canvasBack: new URL('@/assets/paintboard.png', import.meta.url).href,//
isCrop: true,
edg: 0,
fullScreen: false,
domId: '',
imgType: 'image/png',
imgBack: new URL('@/assets/paintboard.png', import.meta.url).href,//
isRepeat: '',
noRotation: false,
backIsCenter: false,
verticalDeductWidth: 10,
verticalDeductHeight: 24,
acrossDeductWidth: 30,
acrossDeductHeight: 20,
recoverPoints: [],
isBrush: false,
brushLine: 20
})
const emits = defineEmits(["updataconbt"]);
let vueSignatureRef = ref<any>(null)
const imagesSRC = ref<string>('')
const confirm = () => {
vueSignatureRef.value.confirm()
.then((res:{base64: string, points: any}) => {
imagesSRC.value = res.base64
sessionStorage.setItem('points', JSON.stringify(res.points))
pantareaisShow.value = false
emits("updataconbt",res.base64)
})
.catch(() => {
alert('未曾签名')
})
}
const reset = () => {
imagesSRC.value = ''
pantareaisShow.value = true
if(vueSignatureRef.value != null){
vueSignatureRef.value.reset()
}
}
</script>
<style lang="less" scoped>
#canvasWrap{
margin: 1px 0;
}
canvas{
border: 1px dashed #ccc
}
.input__wrap{
list-style-type: none;
width: 100%;
padding: 0;
li{
line-height: 30px;
display: flex;
justify-content: flex-start;
align-items: center;
flex-wrap: wrap;
}
span{
width: 130px;
display: inline-block;
text-align: right;
margin-right: 10px
}
input[type="checkbox"]{
margin-left: 0
}
p{
margin: 0 0 10px 140px;
color:#aaa
}
}
.button{
width: 290px;
height: 35px;
display: flex;
justify-content: center;
align-items: center;
font-size: 15px;
border: 1px solid #ccc;
border-radius: 5px;
cursor: pointer;
}
.buttonList{
display: flex;
.button ~ .button{
margin-left: 10px;
}
}
</style>

713
src/widget/writingboard/vueSignature.vue

@ -0,0 +1,713 @@
<template>
<canvas v-if="esignReset" ref="canvasRef" @mousedown="onMouseDown" @mousemove="onMouseMove" @mouseup="onMouseUp" @touchstart="onTouchStart" @touchmove="onTouchMove" @touchend="onTouchEnd"></canvas>
<!-- <img v-show="false" ref="penRef" :src="penImg" > -->
</template>
<script setup lang="ts">
import { ref, reactive, watch, computed, onMounted, nextTick, onBeforeMount, onUnmounted, toRaw } from 'vue'
const emits = defineEmits(['onDrawingStatus', 'onMouseDown', 'onMouseMove', 'onMouseUp', 'onTouchStart', 'onTouchMove', 'onTouchEnd'])
interface pointsType {
x: number,
y: number,
direction: string
}
interface Props {
width?: number,
height?: number,
lineWidth?: number,
lineColor?: string,
canvasBack?: string,
isCrop?: boolean,
edg?: number,
fullScreen?: boolean,
domId?: string,
imgBack?: string,
isRepeat?: string,
noRotation?: boolean,
imgType?: string,
backIsCenter?: boolean,
acrossDeductWidth?: number,
acrossDeductHeight?: number
verticalDeductWidth?: number,
verticalDeductHeight?: number,
recoverPoints?: pointsType[],
isBrush?: boolean,
brushLine?: number
}
const props = withDefaults(defineProps<Props>(), {
width: 0, // ()
height: 0, //
lineWidth: 8, //
lineColor: '#000000', //
canvasBack: '', // '#ccc''#E5A1A1''rgb(229, 161, 161)''rgba(0,0,0,.6)''red', 'http''https''base64'
isCrop: false, //
edg: 0, // ,90270,
fullScreen: false, //
domId: '', // 使canvasID, width
imgBack: '', //canvasBack '#ccc''#E5A1A1''rgb(229, 161, 161)''rgba(0,0,0,.6)''red', 'http''https''base64'
isRepeat: '', // (:'repeat','repeat-x','repeat-y' )
noRotation: false, // (true)
imgType: 'image/png', // 'image/jpeg'
backIsCenter: false, // (使domIdcanvas)
verticalDeductWidth: 0, //
verticalDeductHeight: 0, //
acrossDeductWidth: 0, //
acrossDeductHeight: 0, //
recoverPoints: () => [], // canvasconfirm[{x:0,y:0,direction:'across'}], directionacrossvertical
isBrush: false, // 使imgBack
brushLine: 20 // 线20isBrushtrue
})
const hasDrew = ref<boolean>(false)
const isOrientationchange = ref<boolean>(false)
const esignReset = ref<boolean>(true)
const resultImg = ref<string>('')
const points = ref<any[]>([])
let canvasTxt = ref<CanvasRenderingContext2D | null>(null)
let canvasRef = ref<HTMLCanvasElement | null>(null)
let cropCanvas = ref<HTMLCanvasElement | null>(null)
let cropCanvasTxt = ref<CanvasRenderingContext2D | null>(null)
const startX = ref<number>(0)
const startY = ref<number>(0)
const sratio = ref<number>(1)
const isDrawing = ref<boolean>(false)
const isLoad = ref<boolean>(false)
let imgBackDom = ref<any>(null)
let canvasBackDom = ref<any>(null)
let screenPatams = reactive<{ width:number, height: number }>({
width: 0,
height: 0
})
let domPatams = reactive<{ width:number, height: number }>({
width: 0,
height: 0
})
// --------------------------------
const hasPoints = ref<any[]>([])
const pointsArr = ref<any[]>([])
const smoothness = ref<number>(80)
const l = ref<number>(props.brushLine < 20 ? 20 : props.brushLine)
//const penImg = new URL('@/assets/pen2.png', import.meta.url).href
let penRef = ref<any>(null)
// --------------------------------
const ratio = computed(() => (domPatams.height ? domPatams.height : props.fullScreen ? screenPatams.height : props.height) / (domPatams.width ? domPatams.width : props.fullScreen ? screenPatams.width : props.width) )
const canvasBackground = computed(() => props.canvasBack ? props.canvasBack : 'rgba(255, 255, 255, 0)' )
watch(canvasBackground, async (newVal: string) => {
await nextTick()
canvasRef.value && (canvasRef.value.style.background = newVal)
})
watch(hasDrew, (newVal: boolean) => {
emits('onDrawingStatus', newVal)
})
const getSizeRatio = () => {
return !props.fullScreen && props.backIsCenter
}
const setCanvasImageBack = (status: any) => {
const canvas = canvasRef.value as HTMLCanvasElement
let pat = canvasTxt.value?.createPattern(canvasBackDom.value, (props.isRepeat || "no-repeat"));
canvasTxt.value?.rect(0,0,canvas.width ,canvas.height)
canvasTxt!.value!.fillStyle = (pat as any);
canvasTxt.value?.fill();
if (status) {
autoDraw(null, null)
}
}
const setCanvasBack = (status: any) => {
const canvas = canvasRef.value as HTMLCanvasElement
if (props.canvasBack && canvasBackDom.value && isImgaes(props.canvasBack)) {
setCanvasImageBack(status)
} else {
canvas.style.background = canvasBackground.value
}
}
const getDomSize = () => {
const canvas = canvasRef.value as HTMLCanvasElement
if (props.domId) {
let dom = document.getElementById(props.domId)
let domWidth = dom ? dom.clientWidth || dom.offsetWidth : props.fullScreen ? screenPatams.width : props.width
let domHeight = dom ? dom.clientHeight || dom.offsetHeight : props.fullScreen ? screenPatams.height : props.height
canvas.height = domHeight
canvas.width = domWidth
domPatams.width = domWidth
domPatams.height = domHeight
} else {
canvas.height = props.fullScreen ? screenPatams.height : props.height
canvas.width = props.fullScreen ? screenPatams.width : props.width
}
}
const resizeHandler = (status: any) => {
if (isOrientationchange.value) return false
const canvas = canvasRef.value as HTMLCanvasElement
canvas.style.width = (domPatams.width ? domPatams.width : props.fullScreen ? screenPatams.width : props.width) + "px"
const realw = parseFloat(window.getComputedStyle(canvas).width)
canvas.style.height = ratio.value * realw + "px";
canvasTxt.value = canvas.getContext('2d')
canvasTxt.value?.scale(1 * sratio.value, 1 * sratio.value)
sratio.value = realw / (domPatams.width ? domPatams.width : props.fullScreen ? screenPatams.width : props.width)
canvasTxt.value?.scale(1 / sratio.value, 1 / sratio.value)
if (props.canvasBack) {
let IntervaId = setInterval(() => {
if ((canvasBackDom.value && isLoad.value) || !isImgaes(props.canvasBack)) {
setCanvasBack(status)
clearInterval(IntervaId)
}
}, 100)
} else {
if (status) {
autoDraw(null, null)
}
}
}
const orientationchangeEvent = () => {
let directionWidth = window.orientation == 0 || window.orientation == 180 ? props.verticalDeductWidth : props.acrossDeductWidth
let directionHeight = window.orientation == 0 || window.orientation == 180 ? props.verticalDeductHeight : props.acrossDeductHeight
screenPatams.width = window.navigator.platform.indexOf('Win') || window.navigator.platform.indexOf('Mac') ? (document.body.clientHeight || document.body.offsetHeight) - directionWidth : (document.body.clientWidth || document.body.offsetWidth) - directionWidth
screenPatams.height = window.navigator.platform.indexOf('Win') || window.navigator.platform.indexOf('Mac') ? (document.body.clientWidth || document.body.offsetWidth) - directionHeight : (document.body.clientHeight || document.body.offsetHeight) - directionHeight
getImages()
isOrientationchange.value = true
esignReset.value = false
let setIntervalId = setInterval(async() => {
if (isLoad.value) {
clearInterval(setIntervalId)
esignReset.value = true
isOrientationchange.value = false
await nextTick()
getDomSize()
resizeHandler(true)
}
}, 100)
}
const isImgaes = (params: string) => {
let imgType = ['.jpeg', '.bmp', '.jpg', '.gif', '.webp', '.pcx', '.tif', '.tga', '.exif', '.fpx', '.svg', '.cdr', '.pcd', '.dxf', '.ufo', '.eps', '.ai', '.png', '.hdri', '.raw', '.wmf', '.flic', '.emf', '.ico', '.avif', '.apng']
let regex = /^\s*data:([a-z]+\/[a-z0-9-+.]+(;[a-z-]+=[a-z0-9-]+)?)?(;base64)?,([a-z0-9!$&',()*+;=\-._~:@/?%\s]*?)\s*$/i;
let status = params.includes('http://') || params.includes('https://') || regex.test(params) || imgType.some(item => params.includes(item))
return status
}
const rotateBase64Img = (src: string, edg: number, type: string = 'not') => {
return new Promise((resolve, reject) => {
let canvas: HTMLCanvasElement = document.createElement("canvas");
let ctx = canvas.getContext("2d") as CanvasRenderingContext2D
let imgW;//
let imgH;//
let size;//canvas
if (edg % 90 != 0) {
reject("旋转角度必须是90的倍数!");
throw '旋转角度必须是90的倍数!';
}
(edg < 0) && (edg = (edg % 360) + 360)
const quadrant = (edg / 90) % 4; //
const cutCoor = {sx: 0, sy: 0, ex: 0, ey: 0}; //
let image = new Image();
image.crossOrigin = "anonymous"
image.src = src;
image.onload = function () {
imgW = image.width;
imgH = image.height;
//console.log(imgH, 'imgH')
size = imgW > imgH ? imgW : imgH;
canvas.width = size * 2;
canvas.height = size * 2;
let Cwidth = domPatams.width ? domPatams.width : props.fullScreen ? screenPatams.width : props.width
let ratio = getSizeRatio() && type == 'init'
switch (quadrant) {
case 0:
cutCoor.sx = getSizeRatio() && type == 'init' && imgW > screenPatams.width ? size - ((imgW - (getDirection() == 'across' ? screenPatams.width : screenPatams.height)) / 2) : size
cutCoor.sy = size;
cutCoor.ex = size + imgW;
cutCoor.ey = size + imgH;
break;
case 1:
cutCoor.sx = ratio ? size - props.height : window.orientation == 0 || window.orientation == 180 ? size - Cwidth : size - imgH
cutCoor.sy = size
cutCoor.ex = size;
cutCoor.ey = size + imgW;
break;
case 2:
cutCoor.sx = size - imgW;
cutCoor.sy = size - imgH;
cutCoor.ex = size;
cutCoor.ey = size;
break;
case 3:
cutCoor.sx = size;
cutCoor.sy = size - imgW;
cutCoor.ex = size + imgH;
cutCoor.ey = size + imgW;
break;
}
ctx.translate(size, size);
ctx.rotate(edg * Math.PI / 180);
ctx.drawImage(image, 0, 0);
var imgData = ctx.getImageData(cutCoor.sx, cutCoor.sy, cutCoor.ex, cutCoor.ey);
if (quadrant % 2 == 0) {
canvas.width = imgW;
canvas.height = imgH;
} else {
canvas.width = imgH;
canvas.height = imgW;
}
//putImageData()
ctx.putImageData(imgData, 0, 0);
resolve(canvas.toDataURL(props.imgType))
}
})
}
const getImages = async () => {
isLoad.value = false
let edg = window.orientation == 0 || window.orientation == 180 ? 90 : 0
let ratio = props.fullScreen ? edg : 0
if (isImgaes(props.imgBack)) {
let res = await rotateBase64Img(props.imgBack, ratio, 'init')
if (res) {
imgBackDom.value = new Image();
imgBackDom.value.crossOrigin = "anonymous"
imgBackDom!.value!.src = res;
}
}
if (isImgaes(props.canvasBack)){
let res = await rotateBase64Img(props.canvasBack, ratio, 'init')
if (res) {
canvasBackDom.value = new Image();
canvasBackDom.value.crossOrigin = "anonymous"
canvasBackDom!.value!.src = res;
canvasBackDom!.value.onload = () => {
isLoad.value = true
}
}
}
}
onMounted(() => {
//
let directionWidth = window.orientation == 0 || window.orientation == 180 ? props.verticalDeductWidth : props.acrossDeductWidth
//
let directionHeight = window.orientation == 0 || window.orientation == 180 ? props.verticalDeductHeight : props.acrossDeductHeight
screenPatams.width = (document.body.clientWidth || document.body.offsetWidth) - directionWidth
screenPatams.height = (document.body.clientHeight || document.body.offsetHeight) - directionHeight
getImages()
getDomSize()
window.addEventListener("orientationchange", orientationchangeEvent)
resizeHandler(props.recoverPoints && props.recoverPoints.length && !props.isBrush ? true : false)
//
document.onmouseup= () => {
isDrawing.value = false
}
})
const getDirection = () => {
return window.orientation == 90 || window.orientation == -90 ? 'across' : 'vertical'
}
// ----------------------------------------------------------------
const distance = (a: {x:number, y:number}, b: {x:number, y:number}) => {
let x = b.x - a.x , y = b.y - a.y;
return Math.sqrt(x*x+y*y);
}
const customMouseDown = (e: {x:number, y:number, direction: string}) => {
hasPoints.value = []
startX.value = e.x
startY.value = e.y
pointsArr.value.unshift(e);
}
const customMouseMove = (e: {x:number, y:number, direction: string}) => {
let of = e; //move
let up = {
x: startX.value,
y: startY.value,
} //down
hasPoints.value.unshift({time:new Date().getTime() ,dis: distance(up,of)});
let dis = 0;
for (let n = 0; n < hasPoints.value.length-1; n++) {
dis += hasPoints.value[n].dis;
if (dis > smoothness.value)
break;
}
startX.value = of.x;
startY.value = of.y;
let len = Math.round(hasPoints.value[0].dis/2)+1;
for (let i = 0; i < len; i++) {
let x = up.x + (of.x-up.x)/len*i;
let y = up.y + (of.y-up.y)/len*i;
canvasTxt.value?.beginPath();
x = x-l.value /2;
y = y - l.value /2;
pointsArr.value.unshift({x,y,direction: e.direction});
canvasTxt.value?.drawImage(penRef.value,x,y,l.value ,l.value );
l.value = l.value - 0.2;
if( l.value < 10) l.value = 10;
}
}
const customMouseUp = () => {
l.value = props.brushLine < 20 ? 20 : props.brushLine;
if(pointsArr.value.length > 100){
for(var j = 0; j <60 ;j++){
pointsArr.value[j].x = pointsArr.value[j].x-l.value/4;
pointsArr.value[j].y = pointsArr.value[j].y - l.value/4;
canvasTxt.value?.drawImage(penRef.value,pointsArr.value[j].x,pointsArr.value[j].y,l.value,l.value);
l.value = l.value - 0.3;
if( l.value < 5) l.value = 5;
}
l.value = props.brushLine < 20 ? 20 : props.brushLine;
pointsArr.value = [];
}
if (pointsArr.value.length==1) {
canvasTxt.value?.drawImage(penRef.value,pointsArr.value[0].x - l.value/2,pointsArr.value[0].y - l.value/2,l.value,l.value);
pointsArr.value = [];
}
}
// ----------------------------------------------------------------
// pc
const onMouseDown = (e: any) => {
e = e || event
e.preventDefault()
isDrawing.value = true
hasDrew.value = true
let params = {
x: e.offsetX,
y: e.offsetY,
direction: getDirection()
}
// ---------------------------------
if (props.isBrush) {
customMouseDown(params)
} else {
drawStart(params)
}
// ---------------------------------
emits('onMouseDown', e)
}
const onMouseMove = (e: any) => {
e = e || event
e.preventDefault()
if (isDrawing.value) {
let obj = {
x: e.offsetX,
y: e.offsetY,
direction: getDirection()
}
// ---------------------------------
if (props.isBrush) {
customMouseMove(obj)
} else {
drawMove(obj)
}
// ---------------------------------
}
emits('onMouseMove', e)
}
const onMouseUp = (e: any) => {
e = e || event
e.preventDefault()
let obj = {
x: e.offsetX,
y: e.offsetY,
direction: getDirection()
}
if (props.isBrush) {
customMouseUp()
} else {
drawEnd(obj)
}
isDrawing.value = false
emits('onMouseUp', e)
}
// mobile
const onTouchStart = (e: any) => {
e = e || event
e.preventDefault()
hasDrew.value = true
if (e.touches.length === 1) {
let canvas = canvasRef.value as HTMLCanvasElement
let obj = {
x: e.targetTouches[0].clientX - canvas.getBoundingClientRect().left,
y: e.targetTouches[0].clientY - canvas.getBoundingClientRect().top,
direction: getDirection()
}
// ---------------------------------
if (props.isBrush) {
customMouseDown(obj)
} else {
drawStart(obj)
}
// ---------------------------------
}
emits('onTouchStart', e)
}
const onTouchMove = (e: any) => {
e = e || event
e.preventDefault()
if (e.touches.length >= 1) {
let canvas = canvasRef.value as HTMLCanvasElement
let obj = {
x: e.targetTouches[0].clientX - canvas.getBoundingClientRect().left,
y: e.targetTouches[0].clientY - canvas.getBoundingClientRect().top,
direction: getDirection()
}
// ---------------------------------
if (props.isBrush) {
customMouseMove(obj)
} else {
drawMove(obj)
}
// ---------------------------------
}
emits('onTouchMove', e)
}
const onTouchEnd = (e: any) => {
e = e || event
e.preventDefault()
console.log(e.touches, 'e.touches')
if (e.touches.length === 1) {
let canvas = canvasRef.value as HTMLCanvasElement
let obj = {
x: e.targetTouches[0].clientX - canvas.getBoundingClientRect().left,
y: e.targetTouches[0].clientY - canvas.getBoundingClientRect().top,
direction: getDirection()
}
// ---------------------------------
if (props.isBrush) {
customMouseUp()
} else {
drawEnd(obj)
}
// ---------------------------------
} else {
if (props.isBrush) {
customMouseUp()
} else {
points.value.push({x: -1, y: -1, direction: getDirection()})
}
}
emits('onTouchEnd', e)
}
//
const drawStart = (params: { x: number, y: number}) => {
startX.value = params.x
startY.value = params.y
canvasTxt.value?.beginPath()
canvasTxt.value?.moveTo(startX.value, startY.value)
canvasTxt.value?.lineTo(params.x, params.y)
canvasTxt!.value!.lineCap = 'round'
canvasTxt!.value!.lineJoin = 'round'
canvasTxt!.value!.lineWidth = props.lineWidth * sratio.value
canvasTxt.value?.stroke()
canvasTxt.value?.closePath()
points.value.push(params)
}
const drawMove = (params: { x: number, y: number}) => {
canvasTxt.value?.beginPath()
canvasTxt.value?.moveTo(startX.value, startY.value)
canvasTxt.value?.lineTo(params.x, params.y)
canvasTxt!.value!.strokeStyle = props.lineColor
canvasTxt!.value!.lineWidth = props.lineWidth * sratio.value
canvasTxt!.value!.lineCap = 'round'
canvasTxt!.value!.lineJoin = 'round'
canvasTxt.value?.stroke()
canvasTxt.value?.closePath()
startY.value = params.y
startX.value = params.x
points.value.push(params)
}
const drawEnd = (params: { x: number, y: number}) => {
canvasTxt.value?.beginPath()
canvasTxt.value?.moveTo(startX.value, startY.value)
canvasTxt.value?.lineTo(params.x, params.y)
canvasTxt!.value!.lineCap = 'round'
canvasTxt!.value!.lineJoin = 'round'
canvasTxt.value?.stroke()
canvasTxt.value?.closePath()
points.value.push(params)
points.value.push({x: -1, y: -1})
}
const autoDraw = (canvasRefs: HTMLCanvasElement | null, canvas2d: CanvasRenderingContext2D | null) => {
if ((points.value && points.value.length || props.recoverPoints && props.recoverPoints.length) && !props.isBrush) {
let canvas = canvasRefs || canvasRef.value as HTMLCanvasElement
let canvasText = canvas2d || canvasTxt.value
let pointsList = props.recoverPoints && props.recoverPoints.length ? props.recoverPoints : points.value
if (pointsList && pointsList.length) {
hasDrew.value = true
}
pointsList.reduce((acc, cur) => {
if (cur.x != -1 && cur.y != -1 && acc.x != -1 && acc.y != -1) {
canvasText?.beginPath()
let position = { accX: acc.x, accY: acc.y, curX: cur.x, curY: cur.y }
if (props.fullScreen) {
if ((window.orientation == 0 || window.orientation == 180) && acc.direction == 'across' && cur.direction == 'across') { //
position = { accX: canvas.width - acc.y, accY: acc.x, curX: canvas.width - cur.y, curY: cur.x }
} else if ((window.orientation == 90 || window.orientation == -90) && acc.direction == 'vertical' && cur.direction == 'vertical') { //
position = { accX: acc.y, accY: canvas.height - acc.x, curX: cur.y, curY: canvas.height - cur.x }
}
}
canvasText!.moveTo(position.accX, position.accY)
canvasText!.lineTo(position.curX, position.curY)
canvasText!.strokeStyle = props.lineColor
canvasText!.lineWidth = props.lineWidth * sratio.value
canvasText!.lineCap = 'round'
canvasText!.lineJoin = 'round'
canvasText!.stroke()
canvasText!.closePath()
}
return cur
})
}
}
//
const confirm = () => {
return new Promise((resolve, reject) => {
if (!hasDrew.value) {
reject(`Warning: Not Signned!`)
return
}
let canvas = canvasRef.value as HTMLCanvasElement
let resImgData = (canvasTxt.value as CanvasRenderingContext2D).getImageData(0, 0, canvas.width, canvas.height)
canvasTxt!.value!.globalCompositeOperation = "destination-over"
if (props.canvasBack && props.imgBack && !props.isBrush) {
canvasTxt.value?.clearRect(0,0,canvas.width ,canvas.height);
autoDraw(null, null)
}
// canvas
if (props.imgBack && isImgaes(props.imgBack) && !props.isBrush){
let pat = canvasTxt.value?.createPattern(imgBackDom.value, (props.isRepeat || "no-repeat"));
canvasTxt.value?.rect(0,0,canvas.width ,canvas.height);
canvasTxt!.value!.fillStyle = (pat as any);
canvasTxt.value?.fill();
} else if(props.imgBack && !isImgaes(props.imgBack) && !props.isBrush) {
canvasTxt!.value!.fillStyle = props.imgBack;
canvasTxt.value?.fillRect(0,0, canvas.width, canvas.height);
}
resultImg.value = canvas.toDataURL()
let resultImgs:any = resultImg.value
canvasTxt.value?.clearRect(0, 0, canvas.width ,canvas.height)
canvasTxt.value?.putImageData(resImgData, 0, 0)
canvasTxt!.value!.globalCompositeOperation = "source-over"
if (props.isCrop) {
const crop_area = getCropArea(resImgData.data) as [number, number, number,number]
let crop_canvas: HTMLCanvasElement | null = document.createElement('canvas')
const crop_ctx = crop_canvas.getContext('2d')
crop_canvas.width = crop_area[2] - crop_area[0]
crop_canvas.height = crop_area[3] - crop_area[1]
const crop_imgData = (cropCanvasTxt.value || canvasTxt.value)?.getImageData(...crop_area)
//const crop_imgData = (cropCanvasTxt.value || canvasTxt.value)?.getImageData.apply(null, crop_area)
crop_ctx!.globalCompositeOperation = "destination-over"
crop_ctx?.putImageData(crop_imgData!, 0, 0)
resultImgs = crop_canvas.toDataURL()
crop_canvas = null
}
let edg = (props.fullScreen && ((window.orientation == 0 || window.orientation == 180) || ((window.orientation == 90 || window.orientation == -90) && !props.noRotation))) || (window.orientation === undefined) && props.noRotation ? props.edg : 0
rotateBase64Img(resultImgs, edg)
.then(base64 => {
resolve({
base64,
points: points.value
})
})
})
}
const reset = () => {
let canvas = canvasRef.value as HTMLCanvasElement
canvasTxt.value?.clearRect(
0,
0,
canvas.width,
canvas.height
)
points.value = []
hasDrew.value = false
resultImg.value = ''
setCanvasBack(false)
}
const getCropArea = (imgData: any) => {
if (props.imgBack && !props.isBrush) {
cropCanvas.value = document.createElement('canvas')
if (props.domId) {
let dom = document.getElementById(props.domId)
let domWidth = dom ? dom.clientWidth || dom.offsetWidth : props.fullScreen ? screenPatams.width : props.width
let domHeight = dom ? dom.clientHeight || dom.offsetHeight : props.fullScreen ? screenPatams.height : props.height
cropCanvas.value.height = domHeight
cropCanvas.value.width = domWidth
domPatams.width = domWidth
domPatams.height = domHeight
} else {
cropCanvas.value.height = props.fullScreen ? screenPatams.height : props.height
cropCanvas.value.width = props.fullScreen ? screenPatams.width : props.width
}
cropCanvasTxt.value = cropCanvas.value.getContext('2d')
if (isImgaes(props.imgBack)){
let pat = cropCanvasTxt.value?.createPattern(imgBackDom.value, (props.isRepeat || "no-repeat"));
cropCanvasTxt.value?.rect(0,0,cropCanvas.value.width ,cropCanvas.value.height);
cropCanvasTxt!.value!.fillStyle = (pat as any);
cropCanvasTxt.value?.fill();
} else if(!isImgaes(props.imgBack)) {
cropCanvasTxt!.value!.fillStyle = props.imgBack;
cropCanvasTxt.value?.fillRect(0,0, cropCanvas.value.width, cropCanvas.value.height);
}
autoDraw(cropCanvas.value, cropCanvasTxt.value)
}
let canvas = cropCanvas.value as HTMLCanvasElement || canvasRef.value as HTMLCanvasElement
let topX = canvas.width;
var btmX = 0;
var topY = canvas.height;
var btnY = 0
for (var i = 0; i < canvas.width; i++) {
for (var j = 0; j < canvas.height; j++) {
var pos = (i + canvas.width * j) * 4
if (imgData[pos] > 0 || imgData[pos + 1] > 0 || imgData[pos + 2] || imgData[pos + 3] > 0) {
btnY = Math.max(j, btnY)
btmX = Math.max(i, btmX)
topY = Math.min(j, topY)
topX = Math.min(i, topX)
}
}
}
topX++
btmX++
topY++
btnY++
const data = [topX, topY, btmX, btnY]
return data
}
const recoverDraw = (pointsList: pointsType[]) => {
if (props.isBrush) return false
let canvas = canvasRef.value as HTMLCanvasElement
canvasTxt.value?.clearRect(
0,
0,
canvas.width,
canvas.height
)
points.value = pointsList
hasDrew.value = true
resultImg.value = ''
setCanvasBack(true)
}
onBeforeMount(() => {
if (props.fullScreen) {
// let bodyDom = document.getElementsByTagName('body')
// bodyDom[0].style = 'height: 100vh';
document.body.style.height = '100vh';
}
window.addEventListener('resize', resizeHandler)
})
onUnmounted(() => {
if (props.fullScreen) {
// let bodyDom = document.getElementsByTagName('body')
// bodyDom[0].style = '';
document.body.style.height = '';
}
window.removeEventListener('resize', resizeHandler)
window.removeEventListener("orientationchange", orientationchangeEvent)
})
defineExpose({
confirm: toRaw(confirm),
reset,
recoverDraw
})
</script>
<style scoped>
canvas {
max-width: 100%;
display: block;
}
</style>
Loading…
Cancel
Save