Commit 28a6a1b3 by haojie

1

parent 1d1efc5c
<template>
<Teleport to="body">
<div class="custom-context-menu" ref="ContextMenu">
<template v-for="item in list" :key="item.value">
<div class="menu-item" @click="MenuClick(item)">
{{ item.label }}
</div>
</template>
</div>
</Teleport>
</template>
<script lang="ts" setup>
import { nextTick, onBeforeUnmount, onMounted, ref } from 'vue';
const props = withDefaults(
defineProps<{
list?: any;
el?: HTMLElement;
}>(),
{
list: [
{
label: '删除',
value: 'delete',
},
],
}
);
const emit = defineEmits(['change']);
const ContextMenu = ref();
const MenuClick = (item: any) => {
emit('change', item);
};
// 鼠标右击事件
const ContextMenuFun = (e) => {
// 阻止默认事件
e.preventDefault();
ContextMenu.value.style.display = 'block'; //点击右键菜单显示出来
let X = e.clientX + 10;
let Y = e.clientY + 10;
ContextMenu.value.style.left = X + 'px';
ContextMenu.value.style.top = Y + 'px';
};
// 全局点击事件
const GlobalClick = (event) => {
console.log('我执行了');
// 后面改
if (event.target.tagName == 'li') {
return;
}
//如果点击菜单外的任意位置,菜单被隐藏
if (ContextMenu.value) {
ContextMenu.value.style.display = 'none';
}
};
const CreateMenu = (el: HTMLElement) => {
el.addEventListener('contextmenu', ContextMenuFun);
window.addEventListener('click', GlobalClick);
};
onMounted(() => {
nextTick(() => {
// 创建一个自定义右键菜单
if (props.el) {
CreateMenu(props.el);
}
});
});
onBeforeUnmount(() => {
// 销毁window点击事件
window.removeEventListener('click', GlobalClick);
// 销毁元素事件
if (props.el) {
props.el.removeEventListener('contextmenu', ContextMenuFun);
}
});
</script>
<style lang="less">
.custom-context-menu {
background: rgb(17, 18, 20);
border-radius: 8px;
display: none;
position: fixed;
cursor: pointer;
padding: 12px;
box-sizing: border-box;
z-index: 1000;
.menu-item {
width: 188px;
color: #ddd;
height: 30px;
display: flex;
align-items: center;
font-weight: 600;
}
}
</style>
......@@ -51,8 +51,6 @@ const RecordList = reactive({
// 绘图列表
list: [],
});
// 最后展示的预览图
const LastImage = ref('');
const loading = ref(false);
const pageNum = ref<number>(1);
const pageSize = ref<number>(10);
......@@ -83,7 +81,6 @@ const columns = [
title: '图片',
colKey: 'url',
align: 'center',
className: 'td-img',
},
{
title: '下载',
......@@ -179,8 +176,6 @@ onBeforeMount(() => {
cursor: pointer;
}
}
.td-img {
}
}
}
}
......
import { defineComponent, onMounted, reactive, ref } from 'vue';
import { defineComponent, reactive, ref } from 'vue';
import './index.less';
import { Swiper, SwiperSlide } from 'swiper/vue';
import { Pagination, Navigation, Autoplay } from 'swiper';
import { Navigation, Autoplay } from 'swiper';
import 'swiper/css';
import 'swiper/css/pagination';
export default defineComponent({
......@@ -45,9 +45,6 @@ export default defineComponent({
},
],
});
const goDetail = (item: any) => {
console.log(1);
};
return () => (
<div class="custom-home-swiper">
<div class="swiper-button-prev swiper-button-white prev-next">
......
<template>
<div class="drag-box">
<div
ref="DragBox"
:class="[DefaultClassName, 'drag']"
:style="{
backgroundImage: `url(${img})`,
}"
></div>
>
<CustomContextMenu :el="DragBox" @change="MenuChange"></CustomContextMenu>
</div>
</div>
</template>
<script lang="ts" setup>
import { onMounted, ref } from 'vue';
import CustomContextMenu from '@/components/custom/ContextMenu.vue';
const props = defineProps<{
id: string;
img: string;
}>();
const emit = defineEmits(['delete']);
// 拖拽元素
const DragBox = ref();
// 默认类名
const DefaultClassName = ref('custom-drag-img' + props.id);
const MenuChange = (item: any) => {
if (item.value == 'delete') {
emit('delete', item);
}
};
class Drag {
constructor(
target,
......@@ -266,6 +278,7 @@ class Drag {
this.zoom(this.rightBottom, 'rightBottom');
}
}
onMounted(() => {
let dragEl = document.querySelector(`.${DefaultClassName.value}`);
if (dragEl) {
......
<template>
<div class="change-img">
<div>
<!-- <div style="color: white">宽高:</div>
<TInput v-model="input_value" style="width: 100px"></TInput> -->
<a id="link"></a>
<div class="course-container" id="myImage">
<template v-if="backimg">
......@@ -16,30 +14,17 @@
<template v-if="MarkImg.list.length">
<div class="drag-img-parent">
<template v-for="(item, index) in MarkImg.list" :key="index">
<ImageDarg :id="item.id" :img="item.img"></ImageDarg>
<ImageDarg
:id="item.id"
:img="item.img"
@delete="DeleteImg"
></ImageDarg>
</template>
</div>
</template>
<!-- <div
class="code"
:style="{ top: DragData.top + 'px', left: DragData.left + 'px' }"
>
<img
id="clothes_img"
:src="imgs.upImg"
:style="{
width: DragData.width + 'px',
height: DragData.height + 'px',
}"
/>
<template v-if="img_src">
<img :src="img_src" alt="" />
</template>
</div> -->
</div>
</div>
<div class="custom-tools">
<!-- 背景图 -->
<UploadImg
@SubmitImg="SubmitBackImg"
:list_len="MarkImg.list.length"
......@@ -60,58 +45,27 @@
</template>
<script setup lang="ts">
// 添加可以拖拽功能,放大缩小功能
import html2canvas from 'html2canvas';
import { onMounted, reactive, ref, watch } from 'vue';
import { reactive, ref } from 'vue';
import { Button as TButton } from 'tdesign-vue-next';
import ImageDarg from './img_darg.vue';
import UploadImg from './upload.vue';
// const imgs = {
// backimg: new URL('../../../assets/img/mote2.png', import.meta.url).href,
// // ../../../assets/img/yifu.png 无背景色的
// upImg: new URL('../../../assets/img/clothes.jpeg', import.meta.url).href,
// };
// 添加可以拖拽功能,放大缩小功能
const backimg = ref('');
// 上传的列表
const MarkImg = reactive({
list: [],
});
// 上传的图片src
// const img_src = ref('');
// 可拖拽图片默认的top,left
const default_top = 300;
const default_left = 300;
// 拖拽数据
const DragData = reactive({
start: null,
top: default_top,
old_top: default_top,
left: default_left,
old_left: default_left,
is_first: true,
// 判断图片要执行的动作类型--默认拖动
event_type: 'darg',
// 宽高
width: 150,
height: 150,
});
// 输入框的值
const input_value = ref(default_top);
watch(
() => input_value.value,
(v) => {
DragData.width = v;
DragData.height = v;
}
);
// 删除最后一个
const DeleteLast = () => {
if (MarkImg.list.length) {
MarkImg.list.pop();
}
};
// menu事件
const DeleteImg = () => {
DeleteLast();
};
// 背景图
const SubmitBackImg = (obj: any) => {
backimg.value = obj.img;
......@@ -122,9 +76,9 @@ const SubmitImg = (obj: any) => {
};
const save = () => {
html2canvas(document.querySelector('#myImage')).then((canvas) => {
var image = canvas
.toDataURL('image/png')
.replace('image/png', 'image/octet-stream'); // here is the most important part because if you dont replace you will get a DOM 18 exception.
// var image = canvas
// .toDataURL('image/png')
// .replace('image/png', 'image/octet-stream');
//save as download without name and extension
//window.location.href = image;
......@@ -137,51 +91,6 @@ const save = () => {
link.click();
});
};
//
onMounted(() => {
// darg();
});
// 获取鼠标点击位于元素的哪个位置
const get_offset = (el: HTMLElement) => {
let client = el.getBoundingClientRect();
console.log(client);
};
// 拖拽
const darg = () => {
// 获取当前元素
let oDiv = document.querySelector('#clothes_img');
oDiv.onmousedown = (e) => {
// 算出鼠标相对元素的位置
let disX = e.clientX - oDiv.offsetLeft;
let disY = e.clientY - oDiv.offsetTop;
// 获取鼠标按下的位置
// get_offset(oDiv);
//
document.onmousemove = (e) => {
// 用鼠标的位置减去鼠标相对元素的位置,得到移动的距离
let left = e.clientX - disX;
let top = e.clientY - disY;
// 第一次拖拽使用这个
if (DragData.is_first) {
DragData.left = default_left + left;
DragData.top = default_top + top;
} else {
// 二次拖拽
DragData.left = DragData.old_left + left;
DragData.top = DragData.old_top + top;
}
};
document.onmouseup = () => {
DragData.is_first = false;
DragData.old_left = DragData.left;
DragData.old_top = DragData.top;
document.onmousemove = null;
document.onmouseup = null;
};
return false;
};
};
</script>
<style scoped lang="less">
......
......@@ -3,13 +3,13 @@
<TButton
>{{ label }}
<input
ref="referenceUpload"
class="custom-upload-file"
type="file"
@change="displayimg"
name="myfile"
accept="image/png, image/jpeg"
required
multiple
/>
</TButton>
</div>
......@@ -17,13 +17,15 @@
<script lang="ts" setup>
import { Button as TButton } from 'tdesign-vue-next';
import { ref } from 'vue';
const props = defineProps<{
list_len: number;
label: string;
}>();
const emit = defineEmits(['SubmitImg']);
const referenceUpload = ref<HTMLElement>();
const displayimg = (e) => {
// //获取第一个文件对象
// 单个上传
// var file = e.target.files[0];
// //判断当前是否支持使用FileReader
// if (window.FileReader) {
......@@ -42,27 +44,29 @@ const displayimg = (e) => {
// };
// }
// 循环获取
let files = e.target.files;
for (let i = 0, j = files.length; i < j; i++) {
console.log(files[i]);
//判断当前是否支持使用FileReader
if (window.FileReader) {
//创建读取文件的对象
var fr = new FileReader();
const fr = new FileReader();
//以读取文件字符串的方式读取文件 但是不能直接读取file
//因为文件的内容是存在file对象下面的files数组中的
//该方法结束后图片会以data:URL格式的字符串(base64编码)存储在fr对象的result中
fr.readAsDataURL(files[i]);
fr.onloadend = function () {
// 添加到列表
emit('SubmitImg', {
img: fr.result.replace(/\s/g, encodeURIComponent(' ')),
id: props.list_len + 1 + '',
});
if (fr.result) {
// 添加到列表
emit('SubmitImg', {
img: fr.result.replace(/\s/g, encodeURIComponent(' ')),
id: props.list_len + 1 + '',
});
}
};
}
}
referenceUpload.value.value = null;
};
</script>
......
:root {
font-family: Inter, Avenir, Helvetica, Arial, sans-serif;
font-size: 16px;
line-height: 24px;
font-weight: 400;
color-scheme: light dark;
color: rgba(255, 255, 255, 0.87);
background-color: #242424;
font-synthesis: none;
text-rendering: optimizeLegibility;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
-webkit-text-size-adjust: 100%;
}
a {
font-weight: 500;
color: #646cff;
text-decoration: inherit;
}
a:hover {
color: #535bf2;
}
body {
margin: 0;
display: flex;
place-items: center;
min-width: 320px;
min-height: 100vh;
}
h1 {
font-size: 3.2em;
line-height: 1.1;
}
button {
border-radius: 8px;
border: 1px solid transparent;
padding: 0.6em 1.2em;
font-size: 1em;
font-weight: 500;
font-family: inherit;
background-color: #1a1a1a;
cursor: pointer;
transition: border-color 0.25s;
}
button:hover {
border-color: #646cff;
}
button:focus,
button:focus-visible {
outline: 4px auto -webkit-focus-ring-color;
}
.card {
padding: 2em;
}
#app {
max-width: 1280px;
margin: 0 auto;
padding: 2rem;
text-align: center;
}
@media (prefers-color-scheme: light) {
:root {
color: #213547;
background-color: #ffffff;
}
a:hover {
color: #747bff;
}
button {
background-color: #f9f9f9;
}
}
export const isMobile = () => {
return navigator.userAgent.match(
/(phone|pad|pod|iPhone|iPod|ios|iPad|Android|Mobile|BlackBerry|IEMobile|MQQBrowser|JUC|Fennec|wOSBrowser|BrowserNG|WebOS|Symbian|Windows Phone)/i
);
};
export const openApp = (url: string) => {
let res = isMobile();
if (!res) {
return;
}
// 是手机
// window.location.href = 'https://wa.me/';
let _href = 'https://api.whatsapp.com/send?';
_href += '&text=' + encodeURIComponent(url); //标题
// _href += '&url=' + encodeURIComponent(url); //链接
window.location.href = _href;
};
export const getDomBounding = (dom: any) => {
let client = dom.getBoundingClientRect();
let bodyHeight = document.documentElement.clientHeight;
return {
client,
bodyHeight,
};
};
export function zipImg(file: File) {
return new Promise((resolve) => {
if (file && (file.size / 1024 > 500 || file.type !== 'image/gif')) {
let img = new Image();
const img = new Image();
img.src = URL.createObjectURL(file);
let cvs = document.createElement('canvas');
let maxRatio = 0.75; // 大图比率
let minRatio = 0.8; // 小图比率
let imgQulity = 0.2; // 图像质量
const cvs = document.createElement('canvas');
const maxRatio = 0.75; // 大图比率
const minRatio = 0.8; // 小图比率
const imgQulity = 0.2; // 图像质量
img.onload = async function () {
let ratio =
const ratio =
img.naturalWidth > 1000 || img.naturalHeight > 1000
? maxRatio
: minRatio;
cvs.width = img.naturalWidth * ratio;
cvs.height = img.naturalHeight * ratio;
let ctx: any = cvs.getContext('2d');
const ctx: any = cvs.getContext('2d');
ctx.drawImage(img, 0, 0, cvs.width, cvs.height);
// 压缩后新图的 base64
let zipBase64 = cvs.toDataURL('image/jpeg', imgQulity);
let add = base642File(zipBase64);
const zipBase64 = cvs.toDataURL('image/jpeg', imgQulity);
const add = base642File(zipBase64);
resolve(add);
};
} else {
......@@ -30,14 +30,14 @@ export function zipImg(file: File) {
// base64转图片
export function dataURLtoFile(dataurl: any, filename: any, mime: any) {
return new Promise((resolve) => {
let arr = dataurl.split(';base64,');
let bstr = atob(arr[1]);
const arr = dataurl.split(';base64,');
const bstr = atob(arr[1]);
let n = bstr.length;
let u8arr = new Uint8Array(n);
const u8arr = new Uint8Array(n);
while (n--) {
u8arr[n] = bstr.charCodeAt(n);
}
let data = new File([u8arr], `${filename}`, {
const data = new File([u8arr], `${filename}`, {
type: mime,
});
resolve(data);
......@@ -58,7 +58,7 @@ export function base642File(base64: any) {
export const ChangeBlob = (value: string) => {
//将字符串 转换成 Blob 对象
let blob = new Blob([value], {
const blob = new Blob([value], {
type: 'text/plain',
});
return blob;
......
export interface ColumnProps {
value: string;
label: string;
panel: any;
is_load?: boolean;
}
export const WatchColor = (value: string) => {
try {
if (value[0] === '-') {
return '#F05451';
} else {
return '#25A69A';
}
} catch (e) {
return '#25A69A';
}
};
/**
* 分转成千分位元展示
* @param {*} value
* @returns
*/
export const thousandthMoney = function (value: any, unit = '') {
if (value !== '') {
value = (Number(value) / 100000).toFixed(2);
value = String(value);
const match = value.split('.');
const res = match
.map((item: any) => item.replace(/(\d)(?=(\d{3})+$)/g, '$1,'))
.join('.');
return unit ? unit + res : res;
}
return '';
};
// 转换成美式数字展示---并展示两位小数
export const conversionNum = (value: string, fra: number = 0) => {
let num;
try {
num = parseFloat(value).toFixed(fra);
//先保留两位小数
num = num.toString().replace(/\B(?=(\d{3})+\b)/g, ',');
return num;
} catch (e) {
return value;
}
};
// 去除字符串之间的空格
export const RemoveSpaces = (value: string) => {
const newVal = value.replace(/\s*/g, '');
return newVal;
};
export const parseCoinAmount = function (value: any) {
let res = '';
if (!value) {
return res;
}
let valueStr = '';
const str = String(value).toLowerCase();
if (str.indexOf('e') >= 1) {
const precision = str.substring(str.indexOf('e') + 2);
valueStr = Number(value).toFixed(~~precision + 3);
} else {
valueStr = value + '';
}
// 大于1时, 展示2位小数
if (value >= 1) {
res = parseFloat(parseFloat(valueStr).toFixed(2)) + '';
} else if (value * 10000 >= 1) {
res = parseFloat(parseFloat(valueStr).toFixed(6)) + '';
} else {
const match = valueStr.match(/\.(\d+?)[1-9]/g);
if (match && match[0]) {
res =
'0.0{' +
(match[0].length - 2) +
'}' +
valueStr.substring(match[0].length, match[0].length + 4);
}
}
return res;
};
// 加百分比
export const parsePercent = function (
value: string | number,
prefix: boolean = false
) {
if (value === undefined || value === null) {
return 'N/A';
}
const result = (parseFloat(value + '') * 100).toFixed(1);
let prefixStr = '';
if (prefix) {
prefixStr = parseFloat(value + '') >= 0 ? '+' : '';
return prefixStr + result + '%';
}
return result + '%';
};
export function parseNumberToK(num: any, fixed = 1) {
if (!num) {
return '0';
}
if (num < 1000) {
return parseFloat(parseFloat(num).toFixed(1));
} else if (num >= 1000 && num < 1000000) {
return parseFloat((num / 1000).toFixed(fixed)) + 'K';
} else {
return parseFloat((num / 1000000).toFixed(fixed)) + 'M';
}
}
//
/**
* 函数节流处理
......@@ -153,102 +58,6 @@ export function debounce(func: () => void, delay: number, Immediate = false) {
};
}
/**
* 科学计数法转换
*/
// 将科学计数法转换成小数
export const scientificToNumber = function (num: number) {
const m: any = num.toExponential().match(/\d(?:\.(\d*))?e([+-]\d+)/);
return num.toFixed(Math.max(0, (m[1] || '').length - m[2]));
};
/**
* 计算最大的值
*/
export const getMaxNum = (list: any, key: string) => {
let max = 0;
list.forEach((item: any) => {
const amount = item[key].replace(',', '');
if (parseFloat(amount) > max) {
max = parseFloat(amount);
}
});
return max;
};
// 获取小数位数
export const FractionalDigits = (num: number | string) => {
const newNum = num + '';
if (newNum.indexOf('.') !== -1) {
const list = newNum.split('.');
if (list.length) {
return list[1].length;
}
} else {
return 0;
}
};
// 将指定数据列置顶
export const ListSort = (list: any, sortlist: any) => {
const newList = JSON.parse(JSON.stringify(list));
for (const i in sortlist) {
const index = list.findIndex((item: any) => item.num == sortlist[i]);
if (index !== -1) {
// 存在,删除
newList.splice(index, 1);
// 置顶
newList.unshift(list[index]);
}
}
return newList;
};
// 随机获取指定长度的数组
export const getDesignationList = ({ list, num, max, min }: any) => {
let length = list.length;
const new_list: any = [];
let max_list: any = [];
let min_list: any = [];
let type = '';
if (max) {
// 限制最大的数据
max_list = list.filter((item: any) => item.price <= max);
if (max_list.length >= num) {
length = max_list.length;
type = 'num';
}
} else if (min) {
min_list = list.filter((item: any) => item.price >= min);
if (min_list.length >= num) {
length = min_list.length;
type = 'num';
}
}
// 循环指定次数
for (let i = 0; i < num; i++) {
let new_num = Math.floor(Math.random() * length + 1);
if (new_num >= length) {
new_num = length - 1;
}
if (max) {
if (type == 'num') {
new_list.push(max_list[new_num]);
} else {
new_list.push(list[new_num]);
}
} else if (min) {
if (type == 'num') {
new_list.push(min_list[new_num]);
} else {
new_list.push(list[new_num]);
}
} else {
new_list.push(list[new_num]);
}
}
return new_list;
};
// 表格排序--降序
export const TableSort = (list: any, field: string) => {
let minIndex, temp;
......
export function updateProgress(event: any) {
if (event.lengthComputable) {
var percentComplete = event.loaded / event.total;
}
}
export function xhr() {
var xhr = new XMLHttpRequest();
return {
request: (method: any, url: any, data: any, success: any, err: any) => {
xhr.open(method, url);
xhr.onprogress = updateProgress;
xhr.upload.onprogress = updateProgress;
if (method == 'GET') {
xhr.send();
} else {
xhr.setRequestHeader(
'Content-Type',
'application/x-www-form-urlencoded'
);
// xhr.setRequestHeader('test', 'test');
// xhr.setRequestHeader('Host', '192.168.1.1:5000');
// xhr.setRequestHeader('Origin', 'http://192.168.1.1:5000');
// xhr.setRequestHeader('Referer', 'http://192.168.1.1:5000/upload');
// 声明请求源
xhr.setRequestHeader('Origin', 'http://a.example.com');
xhr.send(data);
}
xhr.onreadystatechange = function () {
if (xhr.readyState === 4 && xhr.status === 200) {
success(xhr.responseText);
} else {
err();
}
};
},
};
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment