Merge remote-tracking branch 'origin/dev_vite' into StableVersion

This commit is contained in:
X1627315083
2025-10-14 16:47:08 +08:00
158 changed files with 10430 additions and 8233 deletions

View File

@@ -1,4 +1,4 @@
VITE_USER_NODE_ENV = 'production'
VITE_USER_NODE_ENV = 'development'
# VITE_APP_BASE_URL = 'https://aida.com.hk/test'
# VITE_APP_BASE_URL = 'http://18.167.251.121:10088'
# VITE_APP_BASE_URL = 'https://api.aida.com.hk'

View File

@@ -1,12 +1,6 @@
<<<<<<< HEAD
VITE_USER_NODE_ENV = 'development'
VITE_APP_BASE_URL = 'https://test.api.aida.com.hk'
# VITE_APP_BASE_URL = 'https://api.aida.com.hk'
=======
NODE_ENV = 'development'
# VUE_APP_BASE_URL = 'https://api.aida.com.hk'
VUE_APP_BASE_URL = 'https://test.api.aida.com.hk'
>>>>>>> 5d8304ce3ece21dd3200ffffb0c76e3ef55dd213
# VITE_APP_BASE_URL = 'http://18.167.251.121:10086'
# VITE_APP_BASE_URL = 'http://192.168.1.9:5567'

1
.gitignore vendored
View File

@@ -23,3 +23,4 @@ dist.rar
*.sln
*.sw?
.eslintrc-auto-import.json
components.d.ts

31
components.d.ts vendored
View File

@@ -1,31 +0,0 @@
/* eslint-disable */
// @ts-nocheck
// Generated by unplugin-vue-components
// Read more: https://github.com/vuejs/core/pull/3399
// biome-ignore lint: disable
export {}
/* prettier-ignore */
declare module 'vue' {
export interface GlobalComponents {
ABadge: typeof import('ant-design-vue/es')['Badge']
ACheckbox: typeof import('ant-design-vue/es')['Checkbox']
ADrawer: typeof import('ant-design-vue/es')['Drawer']
AImage: typeof import('ant-design-vue/es')['Image']
AInputNumber: typeof import('ant-design-vue/es')['InputNumber']
AModal: typeof import('ant-design-vue/es')['Modal']
APagination: typeof import('ant-design-vue/es')['Pagination']
APopover: typeof import('ant-design-vue/es')['Popover']
ARangePicker: typeof import('ant-design-vue/es')['RangePicker']
ASelect: typeof import('ant-design-vue/es')['Select']
ASelectOption: typeof import('ant-design-vue/es')['SelectOption']
ASlider: typeof import('ant-design-vue/es')['Slider']
ASpin: typeof import('ant-design-vue/es')['Spin']
ASwitch: typeof import('ant-design-vue/es')['Switch']
ATable: typeof import('ant-design-vue/es')['Table']
AUpload: typeof import('ant-design-vue/es')['Upload']
ElCascader: typeof import('element-plus/es')['ElCascader']
RouterLink: typeof import('vue-router')['RouterLink']
RouterView: typeof import('vue-router')['RouterView']
}
}

BIN
dist.7z

Binary file not shown.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.8 KiB

View File

Before

Width:  |  Height:  |  Size: 8.9 KiB

After

Width:  |  Height:  |  Size: 8.9 KiB

BIN
public/image/brush/fur.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.3 KiB

View File

Before

Width:  |  Height:  |  Size: 5.0 KiB

After

Width:  |  Height:  |  Size: 5.0 KiB

View File

Before

Width:  |  Height:  |  Size: 3.0 KiB

After

Width:  |  Height:  |  Size: 3.0 KiB

View File

Before

Width:  |  Height:  |  Size: 3.7 KiB

After

Width:  |  Height:  |  Size: 3.7 KiB

BIN
public/image/brush/pen.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

View File

Before

Width:  |  Height:  |  Size: 2.8 KiB

After

Width:  |  Height:  |  Size: 2.8 KiB

View File

Before

Width:  |  Height:  |  Size: 7.4 KiB

After

Width:  |  Height:  |  Size: 7.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

View File

Before

Width:  |  Height:  |  Size: 201 KiB

After

Width:  |  Height:  |  Size: 201 KiB

View File

Before

Width:  |  Height:  |  Size: 4.3 KiB

After

Width:  |  Height:  |  Size: 4.3 KiB

View File

@@ -1,18 +1,22 @@
@font-face {
font-family: "iconfont"; /* Project id 4292253 */
src: url('iconfont.woff2?t=1727415711578') format('woff2'),
url('iconfont.woff?t=1727415711578') format('woff'),
url('iconfont.ttf?t=1727415711578') format('truetype');
src: url('iconfont.woff2?t=1759888699816') format('woff2'),
url('iconfont.woff?t=1759888699816') format('woff'),
url('iconfont.ttf?t=1759888699816') format('truetype');
}
.iconfont {
font-family: "iconfont" !important;
font-size: 1.8rem;
font-size: 1.6rem;
font-style: normal;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
.icon-clothes:before {
content: "\e8d1";
}
.icon-caizhi:before {
content: "\e647";
}

View File

@@ -1,170 +1,219 @@
{
"id": "",
"name": "",
"id": "4292253",
"name": "aida",
"font_family": "iconfont",
"css_prefix_text": "icon-",
"description": "",
"glyphs": [
{
"icon_id": "124968799",
"name": "外套_长款外套1@1x",
"font_class": "a-waitao_changkuanwaitao11x",
"unicode": "e66c",
"unicode_decimal": 58988
"icon_id": "20183053",
"name": "clothes",
"font_class": "clothes",
"unicode": "e8d1",
"unicode_decimal": 59601
},
{
"icon_id": "125198319",
"name": "撤销 返回 撤回 上一步",
"font_class": "fanchehui",
"unicode": "e626",
"unicode_decimal": 58918
"icon_id": "15739173",
"name": "材质",
"font_class": "caizhi",
"unicode": "e647",
"unicode_decimal": 58951
},
{
"icon_id": "125198320",
"name": "撤销 返回 撤回 上一步",
"font_class": "chehui",
"unicode": "e609",
"unicode_decimal": 58889
"icon_id": "35023469",
"name": "IC-液化",
"font_class": "IC-yehua",
"unicode": "e61b",
"unicode_decimal": 58907
},
{
"icon_id": "125524062",
"name": "语言",
"font_class": "yuyan",
"unicode": "e85f",
"unicode_decimal": 59487
"icon_id": "12096844",
"name": "上一层",
"font_class": "shangyiceng",
"unicode": "e751",
"unicode_decimal": 59217
},
{
"icon_id": "126177191",
"name": "标签",
"font_class": "biaoqian",
"unicode": "e603",
"unicode_decimal": 58883
"icon_id": "16531912",
"name": "上一层",
"font_class": "shangyiceng1",
"unicode": "e604",
"unicode_decimal": 58884
},
{
"icon_id": "126459101",
"name": "并集",
"font_class": "bingji",
"unicode": "e620",
"unicode_decimal": 58912
"icon_id": "24253227",
"name": "下一层",
"font_class": "xiayiceng",
"unicode": "e68a",
"unicode_decimal": 59018
},
{
"icon_id": "126459102",
"name": "并集",
"font_class": "bingji1",
"unicode": "e668",
"unicode_decimal": 58984
"icon_id": "24253230",
"name": "上一层",
"font_class": "shangyiceng2",
"unicode": "e68b",
"unicode_decimal": 59019
},
{
"icon_id": "126901286",
"name": "点位",
"font_class": "dianwei",
"unicode": "e685",
"unicode_decimal": 59013
},
{
"icon_id": "130743908",
"name": "编辑",
"font_class": "bianji",
"unicode": "e600",
"unicode_decimal": 58880
},
{
"icon_id": "130743909",
"name": "圆形",
"font_class": "circle",
"unicode": "e64f",
"unicode_decimal": 58959
},
{
"icon_id": "130743910",
"name": "三角形",
"font_class": "sanjiaoxing",
"unicode": "e615",
"unicode_decimal": 58901
},
{
"icon_id": "130743911",
"name": "图层",
"font_class": "tuceng",
"unicode": "e632",
"unicode_decimal": 58930
},
{
"icon_id": "130743912",
"name": "平移",
"font_class": "move",
"unicode": "e616",
"unicode_decimal": 58902
},
{
"icon_id": "130743913",
"name": "橡皮",
"font_class": "xiangpi_huaban1",
"unicode": "e67b",
"unicode_decimal": 59003
},
{
"icon_id": "130743914",
"name": "tx-fill-椭圆形",
"font_class": "tx-fill-tuoyuanxing",
"unicode": "e64c",
"unicode_decimal": 58956
},
{
"icon_id": "130743915",
"name": "直线",
"font_class": "zhixian",
"unicode": "e602",
"unicode_decimal": 58882
},
{
"icon_id": "130743916",
"name": "线",
"font_class": "xian",
"unicode": "ec5f",
"unicode_decimal": 60511
},
{
"icon_id": "130743917",
"name": "正方形",
"font_class": "checkbox-full",
"unicode": "ea6f",
"unicode_decimal": 60015
},
{
"icon_id": "130743918",
"name": "图层",
"font_class": "tuceng1",
"unicode": "e62d",
"unicode_decimal": 58925
},
{
"icon_id": "130751283",
"icon_id": "3663275",
"name": "审批",
"font_class": "shenpi",
"unicode": "e6a1",
"unicode_decimal": 59041
},
{
"icon_id": "130751284",
"icon_id": "7638976",
"name": "用户",
"font_class": "yonghu",
"unicode": "e617",
"unicode_decimal": 58903
},
{
"icon_id": "130751285",
"icon_id": "9775414",
"name": "使用次数",
"font_class": "usetime",
"unicode": "e601",
"unicode_decimal": 58881
},
{
"icon_id": "130751286",
"icon_id": "16843615",
"name": "下拉",
"font_class": "xiala",
"unicode": "e634",
"unicode_decimal": 58932
},
{
"icon_id": "1264",
"name": "编辑",
"font_class": "bianji",
"unicode": "e600",
"unicode_decimal": 58880
},
{
"icon_id": "755612",
"name": "圆形",
"font_class": "circle",
"unicode": "e64f",
"unicode_decimal": 58959
},
{
"icon_id": "3101162",
"name": "三角形",
"font_class": "sanjiaoxing",
"unicode": "e615",
"unicode_decimal": 58901
},
{
"icon_id": "6774075",
"name": "图层",
"font_class": "tuceng",
"unicode": "e632",
"unicode_decimal": 58930
},
{
"icon_id": "10905244",
"name": "平移",
"font_class": "move",
"unicode": "e616",
"unicode_decimal": 58902
},
{
"icon_id": "14421659",
"name": "橡皮",
"font_class": "xiangpi_huaban1",
"unicode": "e67b",
"unicode_decimal": 59003
},
{
"icon_id": "14718690",
"name": "tx-fill-椭圆形",
"font_class": "tx-fill-tuoyuanxing",
"unicode": "e64c",
"unicode_decimal": 58956
},
{
"icon_id": "17521049",
"name": "直线",
"font_class": "zhixian",
"unicode": "e602",
"unicode_decimal": 58882
},
{
"icon_id": "17581689",
"name": "线",
"font_class": "xian",
"unicode": "ec5f",
"unicode_decimal": 60511
},
{
"icon_id": "18175800",
"name": "正方形",
"font_class": "checkbox-full",
"unicode": "ea6f",
"unicode_decimal": 60015
},
{
"icon_id": "26998795",
"name": "图层",
"font_class": "tuceng1",
"unicode": "e62d",
"unicode_decimal": 58925
},
{
"icon_id": "31762941",
"name": "点位",
"font_class": "dianwei",
"unicode": "e685",
"unicode_decimal": 59013
},
{
"icon_id": "8722601",
"name": "并集",
"font_class": "bingji",
"unicode": "e620",
"unicode_decimal": 58912
},
{
"icon_id": "15192904",
"name": "并集",
"font_class": "bingji1",
"unicode": "e668",
"unicode_decimal": 58984
},
{
"icon_id": "17863630",
"name": "标签",
"font_class": "biaoqian",
"unicode": "e603",
"unicode_decimal": 58883
},
{
"icon_id": "16399020",
"name": "语言",
"font_class": "yuyan",
"unicode": "e85f",
"unicode_decimal": 59487
},
{
"icon_id": "4240742",
"name": "撤销 返回 撤回 上一步",
"font_class": "fanchehui",
"unicode": "e626",
"unicode_decimal": 58918
},
{
"icon_id": "6126117",
"name": "撤销 返回 撤回 上一步",
"font_class": "chehui",
"unicode": "e609",
"unicode_decimal": 58889
},
{
"icon_id": "33174601",
"name": "外套_长款外套1@1x",
"font_class": "a-waitao_changkuanwaitao11x",
"unicode": "e66c",
"unicode_decimal": 58988
}
]
}

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -0,0 +1 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1759472135618" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="15482" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><path d="M341.12 85.333h373.845A170.667 170.667 0 0 1 835.67 135.34l130.987 130.986a46.933 46.933 0 0 1 8.79 54.187l-82.774 165.547A46.933 46.933 0 0 1 850.688 512h-40.021v298.667a128 128 0 0 1-128 128H341.333a128 128 0 0 1-128-128V512h-40.021a46.933 46.933 0 0 1-41.984-25.941L48.555 320.512a46.933 46.933 0 0 1 8.789-54.187L188.331 135.34a170.667 170.667 0 0 1 120.704-50.006h32.085z m-18.944 85.334h-13.141a85.333 85.333 0 0 0-60.374 25.002L137.26 307.072l59.776 119.595h54.698a46.933 46.933 0 0 1 46.934 46.933v337.067a42.667 42.667 0 0 0 42.666 42.666h341.334a42.667 42.667 0 0 0 42.666-42.666V473.6a46.933 46.933 0 0 1 46.934-46.933h54.698l59.776-119.595L775.34 195.669a85.333 85.333 0 0 0-60.374-25.002h-13.141L575.787 312.49a85.333 85.333 0 0 1-127.574 0L322.176 170.667z m114.176 0L512 255.787l75.648-85.12H436.352z" fill="#2c2c2c" p-id="15483"></path></svg>

After

Width:  |  Height:  |  Size: 1.2 KiB

View File

@@ -0,0 +1,17 @@
<?xml version="1.0" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 20010904//EN"
"http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd">
<svg version="1.0" xmlns="http://www.w3.org/2000/svg"
width="64.000000pt" height="64.000000pt" viewBox="0 0 64.000000 64.000000"
preserveAspectRatio="xMidYMid meet">
<g transform="translate(0.000000,64.000000) scale(0.100000,-0.100000)"
fill="#000000" stroke="none">
<path d="M223 622 c-109 -39 -178 -112 -210 -221 -29 -102 4 -228 82 -306 122
-121 328 -121 450 0 91 92 118 241 64 356 -69 146 -241 223 -386 171z m152
-146 c68 -29 73 -85 13 -154 -24 -27 -48 -57 -54 -67 -15 -27 -44 -12 -44 22
0 18 13 41 41 69 46 47 47 79 3 90 -28 7 -52 -9 -65 -44 -6 -17 -15 -22 -32
-20 -30 4 -33 38 -6 72 36 46 84 57 144 32z m-31 -278 c8 -14 7 -21 -6 -34
-30 -30 -75 9 -48 42 16 18 39 15 54 -8z"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 847 B

View File

@@ -0,0 +1,2 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg xmlns="http://www.w3.org/2000/svg" id="Layer_1" data-name="Layer 1" viewBox="0 0 24 24" width="512" height="512"><path d="M7,0h-3C1.79,0,0,1.79,0,4v3c0,2.21,1.79,4,4,4h3c2.21,0,4-1.79,4-4v-3C11,1.79,9.21,0,7,0Zm2,7c0,1.1-.9,2-2,2h-3c-1.1,0-2-.9-2-2v-3c0-1.1,.9-2,2-2h3c1.1,0,2,.9,2,2v3Zm11,6h-3c-2.21,0-4,1.79-4,4v3c0,2.21,1.79,4,4,4h3c2.21,0,4-1.79,4-4v-3c0-2.21-1.79-4-4-4Zm2,7c0,1.1-.9,2-2,2h-3c-1.1,0-2-.9-2-2v-3c0-1.1,.9-2,2-2h3c1.1,0,2,.9,2,2v3ZM7,13h-3c-2.21,0-4,1.79-4,4v3c0,2.21,1.79,4,4,4h3c2.21,0,4-1.79,4-4v-3c0-2.21-1.79-4-4-4Zm2,7c0,1.1-.9,2-2,2h-3c-1.1,0-2-.9-2-2v-3c0-1.1,.9-2,2-2h3c1.1,0,2,.9,2,2v3Zm8.18-10.47c.38,.31,.85,.46,1.32,.46s.94-.15,1.32-.46c1.56-1.25,4.18-3.7,4.18-6.06,0-1.92-1.46-3.48-3.25-3.48-.85,0-1.65,.36-2.25,.94-.59-.59-1.39-.94-2.25-.94-1.79,0-3.25,1.56-3.25,3.48,0,2.35,2.62,4.81,4.18,6.06Zm-.93-7.53c.6,0,1.14,.5,1.26,1.17,.08,.48,.5,.83,.98,.83s.9-.35,.98-.83c.12-.67,.67-1.17,1.27-1.17,.69,0,1.25,.66,1.25,1.48,0,1.06-1.35,2.83-3.43,4.5-.04,.03-.1,.03-.14,0-2.08-1.67-3.43-3.44-3.43-4.5,0-.81,.56-1.48,1.25-1.48Z"/></svg>

After

Width:  |  Height:  |  Size: 1.1 KiB

View File

@@ -1 +1,19 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1747121290756" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="51598" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><path d="M829.888 739.392L547.84 457.28 706.112 364.8l-512-170.624L364.8 706.112l92.48-158.336 282.112 282.112z" p-id="51599"></path></svg>
<?xml version="1.0" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 20010904//EN"
"http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd">
<svg version="1.0" xmlns="http://www.w3.org/2000/svg"
width="64.000000pt" height="64.000000pt" viewBox="0 0 64.000000 64.000000"
preserveAspectRatio="xMidYMid meet">
<g transform="translate(0.000000,64.000000) scale(0.100000,-0.100000)"
fill="#000000" stroke="none">
<path d="M256 561 c-50 -51 -53 -71 -10 -71 23 0 24 -3 24 -60 l0 -60 -60 0
c-57 0 -60 1 -60 24 0 14 -5 28 -11 32 -14 8 -109 -84 -109 -106 0 -22 95
-114 109 -106 6 4 11 18 11 32 0 23 3 24 60 24 l60 0 0 -60 c0 -57 -1 -60 -24
-60 -14 0 -28 -5 -32 -11 -8 -14 84 -109 106 -109 22 0 114 95 106 109 -4 6
-18 11 -32 11 -23 0 -24 3 -24 60 l0 60 60 0 c57 0 60 -1 60 -24 0 -14 5 -28
11 -32 14 -8 109 84 109 106 0 22 -95 114 -109 106 -6 -4 -11 -18 -11 -32 0
-23 -3 -24 -60 -24 l-60 0 0 60 c0 57 1 60 24 60 14 0 28 5 32 11 8 14 -84
109 -106 109 -9 0 -37 -22 -64 -49z"/>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 465 B

After

Width:  |  Height:  |  Size: 989 B

View File

@@ -0,0 +1,12 @@
<?xml version="1.0" ?>
<!-- Uploaded to: SVG Repo, www.svgrepo.com, Generator: SVG Repo Mixer Tools -->
<svg width="800px" height="800px" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<g clip-path="url(#clip0_105_1836)">
<path d="M13 3.99976H6C4.89543 3.99976 4 4.89519 4 5.99976V17.9998C4 19.1043 4.89543 19.9998 6 19.9998H13M17 3.99976H18C19.1046 3.99976 20 4.89519 20 5.99976V6.99976M20 16.9998V17.9998C20 19.1043 19.1046 19.9998 18 19.9998H17M20 10.9998V12.9998M12 1.99976V21.9998" stroke="#292929" stroke-linecap="round" stroke-linejoin="round" stroke-width="2"/>
</g>
<defs>

After

Width:  |  Height:  |  Size: 752 B

View File

@@ -0,0 +1,22 @@
<?xml version="1.0" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 20010904//EN"
"http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd">
<svg version="1.0" xmlns="http://www.w3.org/2000/svg"
width="288.000000pt" height="288.000000pt" viewBox="0 0 288.000000 288.000000"
preserveAspectRatio="xMidYMid meet">
<g transform="translate(0.000000,288.000000) scale(0.100000,-0.100000)"
fill="#000000" stroke="none">
<path d="M379 2446 c-101 -36 -179 -116 -209 -214 -19 -62 -20 -93 -20 -792 0
-701 1 -730 20 -793 26 -83 88 -155 168 -194 l57 -28 1045 0 1045 0 47 22
c101 46 170 138 188 250 6 32 10 329 10 668 0 659 -3 692 -54 774 -35 54 -101
108 -163 131 -52 19 -78 20 -626 20 l-572 0 -46 53 c-25 29 -70 66 -100 82
l-54 30 -350 2 c-269 2 -358 0 -386 -11z m710 -175 c20 -11 42 -25 48 -33 6
-7 48 -92 92 -188 85 -183 118 -229 196 -274 73 -43 119 -46 635 -46 l490 0 0
-517 c0 -496 -1 -519 -20 -547 -10 -17 -36 -40 -57 -53 l-38 -23 -993 0 c-968
0 -994 0 -1031 20 -22 11 -48 35 -60 53 -21 34 -21 40 -21 777 0 737 0 743 21
777 12 18 37 42 57 53 34 18 60 19 340 20 280 0 306 -1 341 -19z m1381 -174
c53 -28 80 -75 80 -139 l0 -48 -506 0 c-498 0 -507 0 -541 21 -31 19 -74 88
-103 165 l-10 24 519 0 518 0 43 -23z"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.2 KiB

View File

@@ -0,0 +1,60 @@
<?xml version="1.0" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 20010904//EN"
"http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd">
<svg version="1.0" xmlns="http://www.w3.org/2000/svg"
width="405.000000pt" height="405.000000pt" viewBox="0 0 405.000000 405.000000"
preserveAspectRatio="xMidYMid meet">
<g transform="translate(0.000000,405.000000) scale(0.100000,-0.100000)"
fill="#000000" stroke="none">
<path d="M1385 3584 c-343 -47 -677 -124 -939 -214 -180 -62 -244 -94 -252
-124 -4 -18 13 -77 60 -203 36 -98 96 -263 134 -368 60 -168 71 -191 94 -199
20 -7 66 2 210 42 100 29 186 52 191 52 4 0 7 -485 7 -1078 0 -1033 1 -1080
18 -1095 17 -16 109 -17 1119 -17 1050 0 1101 1 1116 18 15 16 17 66 19 447
l3 429 119 36 c124 37 156 59 156 105 0 44 -33 67 -156 109 l-119 41 -3 503
c-1 345 1 502 8 502 6 0 92 -23 191 -52 143 -41 186 -49 207 -42 24 8 34 29
89 184 34 96 94 262 134 368 50 137 69 200 65 218 -8 30 -72 62 -252 124 -271
94 -596 167 -947 215 -179 24 -208 25 -645 24 -423 -1 -470 -3 -627 -25z
m1009 -131 c-12 -51 -55 -125 -100 -173 -141 -151 -397 -151 -538 0 -45 48
-88 122 -100 173 l-6 27 375 0 375 0 -6 -27z m-860 -25 c26 -163 199 -336 376
-377 110 -26 254 -6 354 47 116 62 233 215 252 330 8 50 3 49 166 26 294 -40
599 -110 853 -195 83 -27 157 -54 165 -59 13 -8 11 -20 -13 -87 -16 -43 -63
-172 -104 -288 -42 -115 -77 -211 -78 -213 -2 -2 -90 22 -196 53 -107 30 -202
55 -213 55 -11 0 -28 -7 -38 -17 -17 -15 -18 -46 -18 -555 0 -296 -3 -538 -8
-538 -4 0 -72 22 -150 48 -142 47 -143 48 -123 66 18 17 21 31 21 109 0 101
-12 127 -60 127 -50 0 -62 -27 -58 -134 2 -53 3 -96 2 -96 0 0 -40 13 -89 30
-100 34 -131 37 -162 15 -37 -26 -35 -54 17 -211 28 -81 50 -154 50 -161 0 -7
-10 -13 -22 -13 -50 0 -76 -63 -41 -102 11 -13 30 -18 64 -18 l48 0 81 -250
c85 -263 95 -280 151 -280 53 0 67 24 130 235 50 168 65 204 90 228 60 56 59
63 59 -333 l0 -360 -1015 0 -1015 0 0 1087 c0 997 -1 1088 -17 1105 -9 10 -26
18 -37 18 -12 0 -108 -25 -215 -55 -106 -31 -194 -55 -196 -53 -1 2 -36 98
-78 213 -41 116 -88 245 -104 288 -24 67 -26 79 -13 87 30 19 318 110 455 144
235 59 586 123 686 125 35 1 37 0 43 -41z"/>
<path d="M1410 2700 c-25 -25 -25 -51 -1 -81 16 -19 30 -24 90 -27 110 -6 151
12 151 67 0 49 -26 61 -128 61 -79 0 -95 -3 -112 -20z"/>
<path d="M1747 2702 c-21 -23 -22 -66 -1 -86 22 -23 55 -29 135 -24 60 3 74 8
90 27 24 30 24 56 -1 81 -17 17 -33 20 -113 20 -78 0 -97 -3 -110 -18z"/>
<path d="M2080 2700 c-25 -25 -25 -51 -1 -81 16 -19 30 -24 90 -27 110 -6 151
12 151 67 0 49 -26 61 -128 61 -79 0 -95 -3 -112 -20z"/>
<path d="M2417 2702 c-21 -23 -22 -66 -1 -86 22 -23 55 -29 135 -24 60 3 74 8
90 27 24 30 24 56 -1 81 -17 17 -33 20 -113 20 -78 0 -97 -3 -110 -18z"/>
<path d="M1286 2574 c-12 -12 -16 -37 -16 -109 0 -86 2 -95 22 -109 30 -21 54
-20 78 4 17 17 20 33 20 108 0 73 -3 92 -18 105 -23 21 -66 22 -86 1z"/>
<path d="M2677 2572 c-13 -14 -17 -39 -17 -105 0 -74 3 -90 20 -107 24 -24 48
-25 78 -4 20 14 22 23 22 109 0 72 -4 97 -16 109 -21 22 -67 20 -87 -2z"/>
<path d="M1292 2264 c-20 -14 -22 -23 -22 -109 0 -104 10 -125 59 -125 48 0
61 26 61 123 0 74 -3 90 -20 107 -24 24 -48 25 -78 4z"/>
<path d="M2680 2260 c-17 -17 -20 -33 -20 -108 0 -98 12 -122 63 -122 47 0 57
22 57 125 0 86 -2 95 -22 109 -30 21 -54 20 -78 -4z"/>
<path d="M1287 1942 c-26 -28 -24 -192 1 -215 24 -22 65 -21 85 1 26 28 24
192 -1 215 -24 22 -65 21 -85 -1z"/>
<path d="M1310 1643 c-32 -11 -40 -37 -40 -129 0 -85 2 -94 22 -108 30 -21 54
-20 78 4 17 17 20 33 20 109 0 85 -1 91 -26 110 -28 22 -30 23 -54 14z"/>
<path d="M1410 1370 c-24 -24 -25 -48 -4 -78 14 -20 23 -22 113 -22 108 0 131
11 131 62 0 46 -27 58 -128 58 -79 0 -95 -3 -112 -20z"/>
<path d="M1747 1372 c-22 -24 -21 -65 1 -85 15 -13 39 -17 115 -17 88 0 97 2
111 22 21 30 20 54 -4 78 -17 17 -33 20 -113 20 -78 0 -97 -3 -110 -18z"/>
<path d="M2080 1370 c-24 -24 -25 -48 -4 -78 14 -20 23 -22 113 -22 108 0 131
11 131 62 0 46 -27 58 -128 58 -79 0 -95 -3 -112 -20z"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 3.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 632 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 582 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 400 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 355 B

View File

@@ -62,6 +62,9 @@ li {
height: 100%;
margin: 0 auto;
}
.ant-dropdown-menu {
border-radius: 1rem;
}
.button_second {
width: 14rem;
text-align: center;
@@ -237,7 +240,7 @@ li {
color: #fff;
background-color: #000;
text-align: center;
font-weight: 600;
font-weight: 500;
border: 2px solid #000;
cursor: pointer;
box-sizing: border-box;
@@ -2106,7 +2109,7 @@ textarea:focus {
.generalMenu_printModel_upload .input_border .input_box_btnBox .upload_item .upload_file_item,
.generate .input_border .input_box_btnBox .upload_item .upload_file_item {
position: absolute;
left: 0;
left: 1rem;
top: 50%;
transform: translateY(-50%);
height: 4.7rem;
@@ -2207,7 +2210,7 @@ textarea:focus {
cursor: pointer;
transition: all 0.3s;
position: relative;
width: 4rem;
width: 3rem;
display: flex;
min-height: 3rem;
justify-content: center;

View File

@@ -63,6 +63,9 @@ input:focus{
height: 100%;
margin: 0 auto;
}
.ant-dropdown-menu{
border-radius: 1rem;
}
.button_second{
width: 14rem;
text-align: center;
@@ -241,7 +244,7 @@ input:focus{
color: #fff;
background-color: #000;
text-align: center;
font-weight: 600;
font-weight: 500;
border: 2px solid #000;
cursor: pointer;
box-sizing: border-box;
@@ -2105,7 +2108,7 @@ textarea:focus{
width: 4.7rem;
.upload_file_item{
position: absolute;
left: 0;
left: 1rem;
top: 50%;
transform: translateY(-50%);
height: 4.7rem;
@@ -2150,7 +2153,7 @@ textarea:focus{
cursor: pointer;
transition: all .3s;
position: relative;
width: 4rem;
width: 3rem;
align-items: center;
justify-content: center;

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 43 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.9 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 987 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 42 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 588 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 29 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 127 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 57 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 801 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.1 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 223 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 288 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 206 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 88 KiB

View File

@@ -111,28 +111,46 @@ export default defineComponent({
.catch((res) => {accountHomeData.loadingShow = false});
}
const ungroupWeiXinModel = ()=>{
Https.axiosGet(Https.httpUrls.unbindWeChat,).then((rv)=>{
message.success(t('frontPage.jsContent1'));
let value = {
accountExtendList:{
WeChat:undefined,
Google:accountHomeData.userDetail.accountExtendList?.Google
}
Modal.confirm({
title: t('frontPage.UnbindTip'),
okText: t('Yes'),
cancelText: t('No'),
mask:false,
centered:true,
onOk() {
Https.axiosGet(Https.httpUrls.unbindWeChat,).then((rv)=>{
message.success(t('frontPage.jsContent1'));
let value = {
accountExtendList:{
WeChat:undefined,
Google:accountHomeData.userDetail.accountExtendList?.Google
}
}
store.commit("upUserDetail", value)
})
}
store.commit("upUserDetail", value)
})
});
}
const ungroupGoogleModel = ()=>{
Https.axiosGet(Https.httpUrls.unbindGoogle,).then((rv)=>{
let value = {
accountExtendList:{
WeChat:accountHomeData.userDetail.accountExtendList?.WeChat,
Google:undefined,
}
Modal.confirm({
title: t('frontPage.UnbindTip'),
okText: t('Yes'),
cancelText: t('No'),
mask:false,
centered:true,
onOk() {
Https.axiosGet(Https.httpUrls.unbindGoogle,).then((rv)=>{
let value = {
accountExtendList:{
WeChat:accountHomeData.userDetail.accountExtendList?.WeChat,
Google:undefined,
}
}
store.commit("upUserDetail", value)
message.success(t('frontPage.jsContent1'));
})
}
store.commit("upUserDetail", value)
message.success(t('frontPage.jsContent1'));
})
});
}
const modifyEmail = ()=>{
bindPageDom.bindEmail.init('Modify')

View File

@@ -117,13 +117,12 @@ export default defineComponent({
let value = {
country:accountHomeData.Country,
title:accountHomeData.selectSex,
userName:accountHomeData.editUserName,
surname:accountHomeData.surname,
givenName:accountHomeData.givenName,
}
store.commit('upUserDetail',value)
accountHomeData.loadingShow = false
message.success(t('exportModel.jsContent7'))
message.success(t('account.jsContent13'))
}).catch((err:any)=>{
accountHomeData.loadingShow = false
})

View File

@@ -1,12 +1,11 @@
<template>
<div class="test_cli admin_page">
<div class="test_cli admin_page">
<div class="admin_table_search">
<div class="admin_state">
<div class="admin_state_item">
<span>{{ $t('admin.StartDate') }}:</span>
<span>{{ $t("admin.StartDate") }}:</span>
<a-range-picker
style="width:250px"
style="width: 250px"
class="range_picker"
v-model:value="rangePickerValue"
:placeholder="[
@@ -23,35 +22,56 @@
</a-range-picker>
</div>
<div class="admin_state_item">
<span>{{ $t('admin.StartTime') }}:</span>
<a-time-range-picker style="width:250px" :placeholder="[$t('admin.startTime'), $t('admin.endTime'),]" class="range_picker" valueFormat="HH:mm:ss" v-model:value="rangeTimeValue" />
<span>{{ $t("admin.StartTime") }}:</span>
<a-time-range-picker
style="width: 250px"
:placeholder="[
$t('admin.startTime'),
$t('admin.endTime'),
]"
class="range_picker"
valueFormat="HH:mm:ss"
v-model:value="rangeTimeValue"
/>
</div>
<div class="admin_state_item">
<span>{{ $t('admin.Email') }}:</span>
<input
v-model="email"
:placeholder="$t('admin.enterEmail')"
@keydown.enter="gettrialList"
type="text"
style="width: 250px"
/>
</div>
<span>{{ $t("admin.Email") }}:</span>
<input
v-model="email"
:placeholder="$t('admin.enterEmail')"
@keydown.enter="gettrialList"
type="text"
style="width: 250px"
/>
</div>
<div class="admin_state_item">
<span>{{ $t('admin.UserName') }}:</span>
<a-select
v-model:value="ids"
mode="multiple"
style="width: 250px"
:filter-option="filterOption"
:placeholder="$t('admin.selectUserName')"
max-tag-count="responsive"
:options="allUserList"
@keydown.enter="gettrialList"
></a-select>
</div>
<span>{{ $t("admin.UserName") }}:</span>
<a-select
v-model:value="ids"
mode="multiple"
style="width: 250px"
:filter-option="filterOption"
:placeholder="$t('admin.selectUserName')"
max-tag-count="responsive"
:options="allUserList"
@keydown.enter="gettrialList"
></a-select>
</div>
<div class="admin_state_item">
<span>Organization Name:</span>
<input
v-model="organizationName"
placeholder="Please enter Organization Name"
@keydown.enter="gettrialList"
type="text"
style="width: 250px"
/>
</div>
</div>
<div class="admin_search">
<div class="admin_search_item" @click="searchHistoryList">{{ $t('admin.search') }}</div>
<div class="admin_search_item" @click="searchHistoryList">
{{ $t("admin.search") }}
</div>
</div>
</div>
@@ -73,205 +93,207 @@
>
</a-table>
</div>
</div>
</div>
</template>
<script lang="ts">
import { defineComponent, ref, createVNode, computed } from "vue";
import { useStore } from "vuex";
import { Https } from "@/tool/https";
import { useI18n } from 'vue-i18n'
export default defineComponent({
components: {
},
setup() {
const store:any = useStore()
let rangePickerValue: any = ref([]);
let rangeTimeValue: any = ref([]);
let renameData: any = ref({}); //修改名字选中的数据
const {t} = useI18n()
const columns: any = computed(() => {
return [
{
title: t('admin.Email'),
align: "center",
dataIndex: "userEmail",
key: "userEmail",
width:200,
fixed: "left",
},
{
title: t('admin.UserId'),
align: "center",
ellipsis: true,
dataIndex: "accountId",
key: "accountId",
width:100,
},
{
title: t('admin.UserName'),
align: "center",
ellipsis: 200,
dataIndex: "userName",
key: "userName",
width:100,
// customRender: (record: any) => {
// let time = formatTime(
// record.text / 1000,
// "YYYY-MM-DD hh:mm:ss"
// );
// return time;
// },
},
{
title: t('admin.Frequency'),
align: "center",
ellipsis: true,
dataIndex: "designTimes",
key: "designTimes",
width:100,
},
{
title: t('admin.CreateTime'),
align: "center",
ellipsis: true,
// width: 150,
// minWidth: 100,
// maxWidth: 200,
// resizable: true,
dataIndex: "createTime",
key: "createTime",
width:200,
},
{
title: t('admin.Credits'),
align: "center",
ellipsis: true,
// width: 150,
// minWidth: 100,
// maxWidth: 200,
// resizable: true,
dataIndex: "credits",
key: "credits",
width:100,
},
];
});
import { defineComponent, ref, createVNode, computed } from "vue";
import { useStore } from "vuex";
import { Https } from "@/tool/https";
import { useI18n } from "vue-i18n";
export default defineComponent({
components: {},
setup() {
const store: any = useStore();
let rangePickerValue: any = ref([]);
let rangeTimeValue: any = ref([]);
let renameData: any = ref({}); //修改名字选中的数据
const { t } = useI18n();
const columns: any = computed(() => {
return [
{
title: t("admin.Email"),
align: "center",
dataIndex: "userEmail",
key: "userEmail",
width: 200,
fixed: "left",
},
{
title: t("admin.UserId"),
align: "center",
ellipsis: true,
dataIndex: "accountId",
key: "accountId",
width: 100,
},
{
title: t("admin.UserName"),
align: "center",
ellipsis: 200,
dataIndex: "userName",
key: "userName",
width: 100,
// customRender: (record: any) => {
// let time = formatTime(
// record.text / 1000,
// "YYYY-MM-DD hh:mm:ss"
// );
// return time;
// },
},
{
title: t("admin.Frequency"),
align: "center",
ellipsis: true,
dataIndex: "designTimes",
key: "designTimes",
width: 100,
},
{
title: t("admin.CreateTime"),
align: "center",
ellipsis: true,
// width: 150,
// minWidth: 100,
// maxWidth: 200,
// resizable: true,
dataIndex: "createTime",
key: "createTime",
width: 200,
},
{
title: t("admin.Credits"),
align: "center",
ellipsis: true,
// width: 150,
// minWidth: 100,
// maxWidth: 200,
// resizable: true,
dataIndex: "credits",
key: "credits",
width: 100,
},
];
});
let allUserList: any = computed(()=>{
return store.state.adminPage.allUserList
})
let ids = ref([])
let email = ref('')
let dataList: any = ref([]);
let status: any = ref(0);
let filterOption = (input: any, option: any) => {
// 使用 option.label 进行搜索
return option.label.toLowerCase().indexOf(input.toLowerCase()) >= 0;
};
return {
rangePickerValue,
rangeTimeValue,
columns,
dataList,
allUserList,
ids,
email,
renameData,
status,
filterOption,
};
},
data() {
return {
currentPage: 1,
pageSize: 10,
total: 0,
historyTableHeight: 0,
handleResizeColumn: (w:any, col:any) => {
col.width = w;
let allUserList: any = computed(() => {
return store.state.adminPage.allUserList;
});
let ids = ref([]);
let email = ref("");
let dataList: any = ref([]);
let status: any = ref(0);
let organizationName: any = ref("");
let filterOption = (input: any, option: any) => {
// 使用 option.label 进行搜索
return option.label.toLowerCase().indexOf(input.toLowerCase()) >= 0;
};
return {
rangePickerValue,
rangeTimeValue,
columns,
dataList,
allUserList,
ids,
email,
renameData,
status,
filterOption,
organizationName,
};
},
data() {
return {
currentPage: 1,
pageSize: 10,
total: 0,
historyTableHeight: 0,
handleResizeColumn: (w: any, col: any) => {
col.width = w;
},
};
},
mounted() {
let historyTable: any = this.$refs.historyTable;
this.historyTableHeight = historyTable.clientHeight - 200;
this.gettrialList();
},
methods: {
//改变页码
changePage(e: any) {
this.currentPage = e.current;
this.pageSize = e.pageSize;
this.gettrialList();
},
};
},
mounted() {
let historyTable: any = this.$refs.historyTable;
this.historyTableHeight = historyTable.clientHeight - 200;
this.gettrialList();
},
methods: {
//改变页码
changePage(e: any) {
this.currentPage = e.current;
this.pageSize = e.pageSize;
this.gettrialList();
},
//查询列表
searchHistoryList() {
this.currentPage = 1;
this.gettrialList();
},
//查询列表
searchHistoryList() {
this.currentPage = 1;
this.gettrialList();
},
//获取列表
gettrialList() {
let startTime: any = this.rangeTimeValue?.[0]
? this.rangeTimeValue[0]
: '00:00:00';
let endTime: any = this.rangeTimeValue?.[1]
? this.rangeTimeValue[1]
: '23:59:59';
let startDate: any = this.rangePickerValue?.[0]
? this.rangePickerValue[0]+' '+startTime
: "";
let endDate: any = this.rangePickerValue?.[1]
? this.rangePickerValue[1]+' '+endTime
: "";
let ids = this.ids.join(',')
let data = {
endTime:endDate,
startTime:startDate,
ids:ids,
email:this.email.trim(),
}
Https.axiosGet(Https.httpUrls.getDesignStatistic,{params:data}).then((rv: any) => {
if (rv) {
this.dataList = rv
// this.workspaceItem.position = this.singleTypeList[0].label
}
})
},
//删除分组
// deleteGroup(record: any, index: number) {
// let deleteGroupFun = (id: any, index: number) => {
// let data = {
// userGroupId: id,
// };
// Https.axiosPost(Https.httpUrls.deleteUserGroup, data).then(
// (rv: any) => {
// this.dataList.splice(index, 1);
// }
// );
// };
// Modal.confirm({
// title: "",
// icon: createVNode(ExclamationCircleOutlined),
// okText: "Yes",
// cancelText: "No",
// centered: true,
// mask: false,
// onOk() {
// deleteGroupFun(record.id, index);
// },
// });
// },
},
});
//获取列表
gettrialList() {
let startTime: any = this.rangeTimeValue?.[0]
? this.rangeTimeValue[0]
: "00:00:00";
let endTime: any = this.rangeTimeValue?.[1]
? this.rangeTimeValue[1]
: "23:59:59";
let startDate: any = this.rangePickerValue?.[0]
? this.rangePickerValue[0] + " " + startTime
: "";
let endDate: any = this.rangePickerValue?.[1]
? this.rangePickerValue[1] + " " + endTime
: "";
let ids = this.ids.join(",");
let data = {
endTime: endDate,
startTime: startDate,
ids: ids,
email: this.email.trim(),
organizationName: this.organizationName,
};
Https.axiosGet(Https.httpUrls.getDesignStatistic, {
params: data,
}).then((rv: any) => {
if (rv) {
this.dataList = rv;
// this.workspaceItem.position = this.singleTypeList[0].label
}
});
},
//删除分组
// deleteGroup(record: any, index: number) {
// let deleteGroupFun = (id: any, index: number) => {
// let data = {
// userGroupId: id,
// };
// Https.axiosPost(Https.httpUrls.deleteUserGroup, data).then(
// (rv: any) => {
// this.dataList.splice(index, 1);
// }
// );
// };
// Modal.confirm({
// title: "",
// icon: createVNode(ExclamationCircleOutlined),
// okText: "Yes",
// cancelText: "No",
// centered: true,
// mask: false,
// onOk() {
// deleteGroupFun(record.id, index);
// },
// });
// },
},
});
</script>
<style lang="less" scoped>
.admin_page .admin_table_search .admin_state {
display: flex;
flex-wrap: wrap;
}
.admin_page .admin_table_search .admin_state {
display: flex;
flex-wrap: wrap;
}
</style>

View File

@@ -1,12 +1,11 @@
<template>
<div class="test_cli admin_page">
<div class="test_cli admin_page">
<div class="admin_table_search">
<div class="admin_state">
<div class="admin_state_item">
<span>Start Date:</span>
<a-range-picker
style="width:250px"
style="width: 250px"
class="range_picker"
v-model:value="rangePickerValue"
:placeholder="[
@@ -24,34 +23,51 @@
</div>
<div class="admin_state_item">
<span>Start Time:</span>
<a-time-range-picker style="width:250px" class="range_picker" valueFormat="HH:mm:ss" v-model:value="rangeTimeValue" />
<a-time-range-picker
style="width: 250px"
class="range_picker"
valueFormat="HH:mm:ss"
v-model:value="rangeTimeValue"
/>
</div>
<div class="admin_state_item">
<span>Email:</span>
<input
v-model="email"
placeholder="Please enter email"
@keydown.enter="gettrialList"
type="text"
style="width: 250px"
/>
</div>
<span>Email:</span>
<input
v-model="email"
placeholder="Please enter email"
@keydown.enter="gettrialList"
type="text"
style="width: 250px"
/>
</div>
<div class="admin_state_item">
<span>User Name:</span>
<a-select
v-model:value="ids"
mode="multiple"
style="width: 250px"
:filter-option="filterOption"
placeholder="Select Item..."
max-tag-count="responsive"
:options="allUserList"
@keydown.enter="gettrialList"
></a-select>
</div>
<span>User Name:</span>
<a-select
v-model:value="ids"
mode="multiple"
style="width: 250px"
:filter-option="filterOption"
placeholder="Select Item..."
max-tag-count="responsive"
:options="allUserList"
@keydown.enter="gettrialList"
></a-select>
</div>
<div class="admin_state_item">
<span>Organization Name:</span>
<input
v-model="organizationName"
placeholder="Please enter Organization Name"
@keydown.enter="gettrialList"
type="text"
style="width: 250px"
/>
</div>
</div>
<div class="admin_search">
<div class="admin_search_item" @click="searchHistoryList">Search</div>
<div class="admin_search_item" @click="searchHistoryList">
Search
</div>
</div>
</div>
@@ -73,270 +89,272 @@
>
</a-table>
</div>
</div>
</div>
</template>
<script lang="ts">
import { defineComponent, ref, createVNode, computed } from "vue";
import { useStore } from "vuex";
import { Https } from "@/tool/https";
export default defineComponent({
components: {
},
setup() {
const store:any = useStore()
let rangePickerValue: any = ref([]);
let rangeTimeValue: any = ref([]);
let renameData: any = ref({}); //修改名字选中的数据
const columns: any = computed(() => {
return [
{
title: 'Email',
align: "center",
dataIndex: "userEmail",
key: "userEmail",
width:200,
fixed: "left",
},
{
title: 'User Id',
align: "center",
ellipsis: true,
dataIndex: "accountId",
key: "accountId",
width:100,
},
{
title: 'User Name',
align: "center",
ellipsis: 200,
dataIndex: "userName",
key: "userName",
width:100,
// customRender: (record: any) => {
// let time = formatTime(
// record.text / 1000,
// "YYYY-MM-DD hh:mm:ss"
// );
// return time;
// },
},
{
title: 'isTrial',
align: "center",
ellipsis: true,
dataIndex: "isTrial",
key: "isTrial",
width:100,
customRender: (record: any) => {
let str
if(record.value == 1){
str ='Yes'
}else{
str ='No'
}
return str;
},
},
{
title: 'Frequency',
align: "center",
ellipsis: true,
dataIndex: "designTimes",
key: "designTimes",
width:100,
},
{
title: 'Country',
align: "center",
ellipsis: true,
dataIndex: "country",
key: "country",
width:200,
},
{
title: 'Title',
align: "center",
ellipsis: true,
dataIndex: "title",
key: "title",
width:100,
},
{
title: 'Surname',
align: "center",
ellipsis: true,
dataIndex: "surname",
key: "surname",
width:150,
},
{
title: 'Given Name',
align: "center",
ellipsis: true,
dataIndex: "givenName",
key: "givenName",
width:100,
},
{
title: 'Create Time',
align: "center",
ellipsis: true,
dataIndex: "createTime",
key: "createTime",
width:200,
},
{
title: 'Credits',
align: "center",
ellipsis: true,
// width: 150,
// minWidth: 100,
// maxWidth: 200,
// resizable: true,
dataIndex: "credits",
key: "credits",
width:100,
},
{
title: 'Occupation',
align: "center",
ellipsis: true,
// width: 150,
// minWidth: 100,
// maxWidth: 200,
// resizable: true,
dataIndex: "occupation",
key: "occupation",
width:100,
},
{
title: 'Trial Order Id',
align: "center",
ellipsis: true,
// width: 150,
// resizable: true,
dataIndex: "trialOrderId",
key: "trialOrderId",
width:100,
},
];
});
import { defineComponent, ref, createVNode, computed } from "vue";
import { useStore } from "vuex";
import { Https } from "@/tool/https";
export default defineComponent({
components: {},
setup() {
const store: any = useStore();
let rangePickerValue: any = ref([]);
let rangeTimeValue: any = ref([]);
let renameData: any = ref({}); //修改名字选中的数据
let organizationName: any = ref("");
const columns: any = computed(() => {
return [
{
title: "Email",
align: "center",
dataIndex: "userEmail",
key: "userEmail",
width: 200,
fixed: "left",
},
{
title: "User Id",
align: "center",
ellipsis: true,
dataIndex: "accountId",
key: "accountId",
width: 100,
},
{
title: "User Name",
align: "center",
ellipsis: 200,
dataIndex: "userName",
key: "userName",
width: 100,
// customRender: (record: any) => {
// let time = formatTime(
// record.text / 1000,
// "YYYY-MM-DD hh:mm:ss"
// );
// return time;
// },
},
{
title: "isTrial",
align: "center",
ellipsis: true,
dataIndex: "isTrial",
key: "isTrial",
width: 100,
customRender: (record: any) => {
let str;
if (record.value == 1) {
str = "Yes";
} else {
str = "No";
}
return str;
},
},
{
title: "Frequency",
align: "center",
ellipsis: true,
dataIndex: "designTimes",
key: "designTimes",
width: 100,
},
{
title: "Country",
align: "center",
ellipsis: true,
dataIndex: "country",
key: "country",
width: 200,
},
{
title: "Title",
align: "center",
ellipsis: true,
dataIndex: "title",
key: "title",
width: 100,
},
{
title: "Surname",
align: "center",
ellipsis: true,
dataIndex: "surname",
key: "surname",
width: 150,
},
{
title: "Given Name",
align: "center",
ellipsis: true,
dataIndex: "givenName",
key: "givenName",
width: 100,
},
{
title: "Create Time",
align: "center",
ellipsis: true,
dataIndex: "createTime",
key: "createTime",
width: 200,
},
{
title: "Credits",
align: "center",
ellipsis: true,
// width: 150,
// minWidth: 100,
// maxWidth: 200,
// resizable: true,
dataIndex: "credits",
key: "credits",
width: 100,
},
{
title: "Occupation",
align: "center",
ellipsis: true,
// width: 150,
// minWidth: 100,
// maxWidth: 200,
// resizable: true,
dataIndex: "occupation",
key: "occupation",
width: 100,
},
{
title: "Trial Order Id",
align: "center",
ellipsis: true,
// width: 150,
// resizable: true,
dataIndex: "trialOrderId",
key: "trialOrderId",
width: 100,
},
];
});
let allUserList: any = computed(()=>{
return store.state.adminPage.allUserList
})
let ids = ref([])
let email = ref('')
let dataList: any = ref([]);
let status: any = ref(0);
let filterOption = (input: any, option: any) => {
// 使用 option.label 进行搜索
return option.label.toLowerCase().indexOf(input.toLowerCase()) >= 0;
};
return {
rangePickerValue,
rangeTimeValue,
columns,
dataList,
allUserList,
ids,
email,
renameData,
status,
filterOption,
};
},
data() {
return {
currentPage: 1,
pageSize: 10,
total: 0,
historyTableHeight: 0,
handleResizeColumn: (w:any, col:any) => {
col.width = w;
let allUserList: any = computed(() => {
return store.state.adminPage.allUserList;
});
let ids = ref([]);
let email = ref("");
let dataList: any = ref([]);
let status: any = ref(0);
let filterOption = (input: any, option: any) => {
// 使用 option.label 进行搜索
return option.label.toLowerCase().indexOf(input.toLowerCase()) >= 0;
};
return {
rangePickerValue,
organizationName,
rangeTimeValue,
columns,
dataList,
allUserList,
ids,
email,
renameData,
status,
filterOption,
};
},
data() {
return {
currentPage: 1,
pageSize: 10,
total: 0,
historyTableHeight: 0,
handleResizeColumn: (w: any, col: any) => {
col.width = w;
},
};
},
mounted() {
let historyTable: any = this.$refs.historyTable;
this.historyTableHeight = historyTable.clientHeight - 200;
this.gettrialList();
},
methods: {
//改变页码
changePage(e: any) {
this.currentPage = e.current;
this.pageSize = e.pageSize;
this.gettrialList();
},
};
},
mounted() {
let historyTable: any = this.$refs.historyTable;
this.historyTableHeight = historyTable.clientHeight - 200;
this.gettrialList();
},
methods: {
//改变页码
changePage(e: any) {
this.currentPage = e.current;
this.pageSize = e.pageSize;
this.gettrialList();
},
//查询列表
searchHistoryList() {
this.currentPage = 1;
this.gettrialList();
},
//查询列表
searchHistoryList() {
this.currentPage = 1;
this.gettrialList();
},
//获取列表
gettrialList() {
let startTime: any = this.rangeTimeValue?.[0]
? this.rangeTimeValue?.[0]
: '00:00:00';
let endTime: any = this.rangeTimeValue[1]
? this.rangeTimeValue[1]
: '23:59:59';
let startDate: any = this.rangePickerValue[0]
? this.rangePickerValue[0]+' '+startTime
: "";
let endDate: any = this.rangePickerValue[1]
? this.rangePickerValue[1]+' '+endTime
: "";
let ids = this.ids.join(',')
let data = {
endTime:endDate,
startTime:startDate,
ids:ids,
email:this.email.trim(),
}
Https.axiosGet(Https.httpUrls.getDesignStatistic,{params:data}).then((rv: any) => {
if (rv) {
this.dataList = rv
// this.workspaceItem.position = this.singleTypeList[0].label
}
})
},
//删除分组
// deleteGroup(record: any, index: number) {
// let deleteGroupFun = (id: any, index: number) => {
// let data = {
// userGroupId: id,
// };
// Https.axiosPost(Https.httpUrls.deleteUserGroup, data).then(
// (rv: any) => {
// this.dataList.splice(index, 1);
// }
// );
// };
// Modal.confirm({
// title: "",
// icon: createVNode(ExclamationCircleOutlined),
// okText: "Yes",
// cancelText: "No",
// centered: true,
// mask: false,
// onOk() {
// deleteGroupFun(record.id, index);
// },
// });
// },
},
});
//获取列表
gettrialList() {
let startTime: any = this.rangeTimeValue?.[0]
? this.rangeTimeValue?.[0]
: "00:00:00";
let endTime: any = this.rangeTimeValue[1]
? this.rangeTimeValue[1]
: "23:59:59";
let startDate: any = this.rangePickerValue[0]
? this.rangePickerValue[0] + " " + startTime
: "";
let endDate: any = this.rangePickerValue[1]
? this.rangePickerValue[1] + " " + endTime
: "";
let ids = this.ids.join(",");
let data = {
endTime: endDate,
startTime: startDate,
ids: ids,
email: this.email.trim(),
organizationName: this.organizationName,
};
Https.axiosGet(Https.httpUrls.getDesignStatistic, {
params: data,
}).then((rv: any) => {
if (rv) {
this.dataList = rv;
// this.workspaceItem.position = this.singleTypeList[0].label
}
});
},
//删除分组
// deleteGroup(record: any, index: number) {
// let deleteGroupFun = (id: any, index: number) => {
// let data = {
// userGroupId: id,
// };
// Https.axiosPost(Https.httpUrls.deleteUserGroup, data).then(
// (rv: any) => {
// this.dataList.splice(index, 1);
// }
// );
// };
// Modal.confirm({
// title: "",
// icon: createVNode(ExclamationCircleOutlined),
// okText: "Yes",
// cancelText: "No",
// centered: true,
// mask: false,
// onOk() {
// deleteGroupFun(record.id, index);
// },
// });
// },
},
});
</script>
<style lang="less" scoped>
.admin_page .admin_table_search .admin_state {
display: flex;
flex-wrap: wrap;
}
.admin_page .admin_table_search .admin_state {
display: flex;
flex-wrap: wrap;
}
</style>

View File

@@ -1,228 +1,305 @@
<template>
<div class="admin_page">
<div class="admin_table_search" >
<div class="admin_state">
<div class="admin_page">
<div class="admin_table_search">
<div class="admin_state">
<div class="admin_state_item">
<span>Status:</span>
<a-select
v-model:value="status"
size="large"
style="width: 250px"
optionFilterProp="label"
:options="statusList"
placeholder="Please select"
allowClear
show-search
></a-select>
</div>
</div>
<div class="admin_search">
<div class="admin_search_item" @click="searchHistoryList">
Search
</div>
<div class="admin_search_item" @click="addhHistoryList">
Add
</div>
</div>
</div>
<div class="admin_table_content" ref="historyTable">
<a-table
@resizeColumn="handleResizeColumn"
:loading="tableLoading"
:columns="columns"
:data-source="dataList"
:scroll="{ y: historyTableHeight }"
@change="changePage"
:showSorterTooltip='false'
:pagination="{
showSizeChanger: true,
current: currentPage,
pageSize: pageSize,
total: total,
showQuickJumper: true,
bordered: false,
}"
>
</a-table>
</div>
<span>Create Time:</span>
<a-range-picker
style="width: 250px"
class="range_picker"
v-model:value="rangePickerValue"
:placeholder="[
$t('HistoryPage.StartDate'),
$t('HistoryPage.EndDate'),
]"
valueFormat="YYYY-MM-DD"
>
<template #suffixIcon>
<span
class="icon iconfont range_picker_icon icon-rili"
></span>
</template>
</a-range-picker>
</div>
<div class="admin_state_item">
<span>Status:</span>
<a-select
v-model:value="status"
size="large"
style="width: 250px"
optionFilterProp="label"
:options="statusList"
placeholder="Please select"
@change="changeStatus"
allowClear
show-search
></a-select>
</div>
<div class="admin_state_item">
<span>School:</span>
<a-select
v-model:value="school"
size="large"
style="width: 250px"
optionFilterProp="label"
:options="schoolList"
placeholder="Please select"
allowClear
show-search
@focus="handleFocus"
></a-select>
</div>
</div>
<div class="admin_search">
<div class="admin_search_item" @click="searchHistoryList">
Search
</div>
<div class="admin_search_item" @click="addhHistoryList">
Add
</div>
</div>
</div>
<div class="admin_table_content" ref="historyTable">
<a-table
@resizeColumn="handleResizeColumn"
:loading="tableLoading"
:columns="columns"
:data-source="dataList"
:scroll="{ y: historyTableHeight }"
@change="changePage"
:showSorterTooltip="false"
:pagination="{
showSizeChanger: true,
current: currentPage,
pageSize: pageSize,
total: total,
showQuickJumper: true,
bordered: false,
}"
>
</a-table>
</div>
<add ref="add" @searchHistoryList="searchHistoryList"></add>
</div>
</div>
</template>
<script lang="ts">
import {
defineComponent,
ref,
createVNode,
computed,
reactive,
toRefs,
onMounted,
} from "vue";
import { formatTime } from "@/tool/util";
import { useStore } from "vuex";
import { Https } from "@/tool/https";
import {getCookie,clonAllCookie} from '@/tool/cookie'
import add from './add.vue'
export default defineComponent({
components: {add},
setup() {
const store:any = useStore()
let filter: any = reactive({
dataList: [],
tableLoading: false,
countryList: computed(()=>{
return store.state.adminPage.country
}),
add:null as any,
status:'',
});
let filterData: any = reactive({
currentPage: 1,
pageSize: 10,
total: 0,
country: "",
status: "",
type: "",
});
let selectList=reactive({
statusList:[
{
label: "all",
value: "",
import {
defineComponent,
ref,
createVNode,
computed,
reactive,
toRefs,
onMounted,
} from "vue";
import { formatTime } from "@/tool/util";
import { useStore } from "vuex";
import { Https } from "@/tool/https";
import { getCookie, clonAllCookie } from "@/tool/cookie";
import add from "./add.vue";
export default defineComponent({
components: { add },
setup() {
const store: any = useStore();
let filter: any = reactive({
dataList: [],
tableLoading: false,
countryList: computed(() => {
return store.state.adminPage.country;
}),
add: null as any,
});
let filterData: any = reactive({
currentPage: 1,
pageSize: 10,
total: 0,
country: "",
status: "",
school: "",
rangePickerValue: [],
});
let selectList = reactive({
statusList: [
{
label: "all",
value: "",
},
{
label: "Enterprise",
value: "Enterprise",
},
{
label: "Education",
value: "Education",
},
],
schoolList: [],
});
let renameData: any = ref({}); //修改名字选中的数据
const columns: any = computed(() => {
return [
{
title: "Id",
align: "center",
dataIndex: "id",
key: "id",
width: 150,
ellipsis: true,
},
{
title: "Name",
align: "center",
dataIndex: "name",
key: "name",
width: 150,
ellipsis: true,
},
{
title: "Create Time",
align: "center",
dataIndex: "createTime",
key: "createTime",
width: 150,
ellipsis: true,
},
{
title: "Type",
align: "center",
dataIndex: "type",
key: "type",
width: 150,
ellipsis: true,
},
];
});
//改变页码
let changePage = (e: any, filters: any, sorter: any) => {
filterData.currentPage = e.current;
filterData.pageSize = e.pageSize;
gettrialList();
};
const filterOption = (e: any) => {
let type = filterData.status;
if (type == "Education") type = "School";
let params = {
name: e,
type,
};
Https.axiosPost(
Https.httpUrls.organizationNameSearch,
{},
{ params: params }
).then((rv: any) => {
if (rv.length == 0) return (selectList.schoolList = []);
selectList.schoolList = rv.map((item: any) => {
return {
label: item,
value: item,
};
});
});
};
//查询列表
let searchHistoryList = () => {
filterData.currentPage = 1;
gettrialList();
};
//获取列表
let gettrialList = () => {
filter.tableLoading = true;
const dateArr = filterData.rangePickerValue;
const startDate = dateArr?.[0] ? dateArr[0] + " " + "00:00:00" : "";
const endDate = dateArr?.[1] ? dateArr[1] + " " + "23:59:59" : "";
const params = {
startTime: startDate,
endTime: endDate,
id: "",
name: filterData.school,
type: filterData.status,
size: filterData.pageSize,
page: filterData.currentPage,
}; //type: "Enterprise"
Https.axiosPost(Https.httpUrls.queryOrganization, params).then(
(rv: any) => {
if (rv) {
console.log(rv);
// filter.dataList = rv;
filter.dataList = rv.records;
filterData.total = rv.total;
filter.tableLoading = false;
// this.workspaceItem.position = this.singleTypeList[0].label
}
}
);
};
let addhHistoryList = () => {
filter.add.init("Add", "");
};
const handleFocus = () => {
if (selectList.schoolList.length == 0) {
filterOption("");
}
};
const changeStatus = () => {
filterData.school = "";
selectList.schoolList = [];
};
onMounted(() => {
gettrialList();
});
return {
...toRefs(filter),
...toRefs(filterData),
...toRefs(selectList),
columns,
renameData,
changePage,
searchHistoryList,
gettrialList,
addhHistoryList,
handleFocus,
changeStatus,
};
},
data() {
return {
historyTableHeight: 0,
handleResizeColumn: (w: any, col: any) => {
col.width = w;
},
{
label:'Enterprise',
value:'Enterprise',
},
{
label:'Education',
value:'Education',
},
],
})
let renameData: any = ref({}); //修改名字选中的数据
const columns: any = computed(() => {
return [
{
title: "Id",
align: "center",
dataIndex: "id",
key: "id",
width:150,
ellipsis:true
},
{
title: "Name",
align: "center",
dataIndex: "name",
key: "name",
width:150,
ellipsis:true
},
{
title: "Create Time",
align: "center",
dataIndex: "createTime",
key: "createTime",
width:150,
ellipsis:true
},
{
title: "Type",
align: "center",
dataIndex: "type",
key: "type",
width:150,
ellipsis:true,
},
];
});
//改变页码
let changePage = (e: any, filters:any, sorter:any) => {
filterData.currentPage = e.current;
filterData.pageSize = e.pageSize;
gettrialList();
};
//查询列表
let searchHistoryList = () => {
filterData.currentPage = 1;
gettrialList();
};
//获取列表
let gettrialList = () => {
filter.tableLoading = true;
Https.axiosGet(Https.httpUrls.queryOrganization, {params:{type:'Enterprise'}}).then(
(rv: any) => {
if (rv) {
console.log(rv)
filter.dataList = rv
// filter.dataList = rv.content;
// filterData.total = rv.total;
filter.tableLoading = false;
// this.workspaceItem.position = this.singleTypeList[0].label
}
}
);
};
let addhHistoryList = () => {
filter.add.init('Add','')
};
onMounted(() => {
gettrialList();
});
return {
...toRefs(filter),
...toRefs(filterData),
...toRefs(selectList),
columns,
renameData,
changePage,
searchHistoryList,
gettrialList,
addhHistoryList,
};
},
data() {
return {
historyTableHeight: 0,
handleResizeColumn: (w: any, col: any) => {
col.width = w;
},
};
},
mounted() {
let historyTable: any = this.$refs.historyTable;
this.historyTableHeight = historyTable.clientHeight - 200;
},
methods: {},
});
};
},
mounted() {
let historyTable: any = this.$refs.historyTable;
this.historyTableHeight = historyTable.clientHeight - 200;
},
methods: {},
});
</script>
<style lang="less" scoped>
.admin_page .admin_table_search .admin_state {
display: flex;
flex-wrap: wrap;
}
:deep(.operate_list){
.fi{
font-size: 2rem;
margin-right: 1rem;
.admin_page .admin_table_search .admin_state {
display: flex;
flex-wrap: wrap;
}
.success{
.fi-ss-check-circle{
color: #3ab45c;
:deep(.operate_list) {
.fi {
font-size: 2rem;
margin-right: 1rem;
}
.success {
.fi-ss-check-circle {
color: #3ab45c;
}
}
.pending {
.fi-ss-check-circle {
color: #ffc628;
}
}
.fail {
.fi-ss-check-circle {
color: #ff0000;
}
}
}
.pending{
.fi-ss-check-circle{
color: #ffc628;
}
}
.fail{
.fi-ss-check-circle{
color: #ff0000;
}
}
}
</style>

View File

@@ -10,6 +10,8 @@ import { fabric } from "fabric-with-all";
import { generateId } from "../utils/helper.js";
import { ClearSelectionCommand } from "./LassoCutoutCommand.js";
import { ClearSelectionContentCommand } from "./ClearSelectionContentCommand.js";
import i18n from "@/lang/index.ts";
const { t } = i18n.global;
/**
* 剪切选区到新图层命令
@@ -36,7 +38,7 @@ export class CutSelectionToNewLayerCommand extends CompositeCommand {
this.baseResolutionScale = options.baseResolutionScale || 2; // 基础分辨率倍数
this.groupId = options.groupId || generateId("lasso-copy-group-");
this.groupName = options.groupName || `选区组`;
this.groupName = options.groupName || t(`Canvas.ConstituencyGroup`);
this.groupLayer = null; // 新增:保存组图层的引用
this.originalLayersLength = 0; // 新增:保存原始图层数量
@@ -179,7 +181,7 @@ export class CutSelectionToNewLayerCommand extends CompositeCommand {
// 创建新的组图层
this.groupLayer = createLayer({
id: this.groupId,
name: this.groupName || `选区组`,
name: this.groupName || t(`Canvas.ConstituencyGroup`),
type: LayerType.GROUP,
visible: true,
locked: false,

View File

@@ -11,6 +11,10 @@ import {
} from "./LayerCommands.js";
import { fabric } from "fabric-with-all";
import { generateId } from "../utils/helper.js";
import i18n from "@/lang/index.ts";
const { t } = i18n.global;
/**
* 套索抠图命令
@@ -37,7 +41,7 @@ export class LassoCutoutCommand extends CompositeCommand {
this.baseResolutionScale = options.baseResolutionScale || 2; // 基础分辨率倍数
this.groupId = options.groupId || generateId("lasso-group-");
this.groupName = options.groupName || `选区组`;
this.groupName = options.groupName || t(`Canvas.ConstituencyGroup`);
this.clippingMaskId = generateId("clipping-mask-");
@@ -238,7 +242,7 @@ export class LassoCutoutCommand extends CompositeCommand {
// 创建新的组图层
this.groupLayer = createLayer({
id: this.groupId,
name: this.groupName || `选区组`,
name: this.groupName || t(`Canvas.ConstituencyGroup`),
type: LayerType.GROUP,
visible: true,
locked: false,

View File

@@ -13,6 +13,9 @@ import {
import { fabric } from "fabric-with-all";
import { generateId } from "../utils/helper.js";
import { ToolCommand } from "./ToolCommands.js";
import i18n from "@/lang/index.ts";
const { t } = i18n.global;
/**
* 套索抠图命令
@@ -39,7 +42,7 @@ export class LassoCutoutCommand extends CompositeCommand {
this.baseResolutionScale = options.baseResolutionScale || 2; // 基础分辨率倍数
this.groupId = options.groupId || generateId("lasso-group-");
this.groupName = options.groupName || `选区组`;
this.groupName = options.groupName || t(`Canvas.ConstituencyGroup`);
this.clippingMaskId = generateId("clipping-mask-");
@@ -220,16 +223,26 @@ export class LassoCutoutCommand extends CompositeCommand {
await clearSelectionCmd.execute();
this.executedCommands.push(clearSelectionCmd);
const topLayerIndex = this.layerManager.layers.value.findIndex(
(layer) => layer.id === this.originalLayer.id
);
const layers = this.layerManager.layers.value;
var topLayerIndex = 0;
layers.forEach((layer, index) => {
if (layer.id === this.originalLayer.id) {
topLayerIndex = index;
}else if (layer.children.length > 0) {
layer.children.forEach((childLayer) => {
if (childLayer.id === this.originalLayer.id) {
topLayerIndex = index;
}
});
}
});
// const selectLayer = this.layerManager.layers.value[topLayerIndex];
// 创建新的组图层
this.groupLayer = createLayer({
id: this.groupId,
name: this.groupName || `选区组`,
name: this.groupName || t(`Canvas.ConstituencyGroup`),
type: LayerType.GROUP,
visible: true,
locked: false,
@@ -244,7 +257,7 @@ export class LassoCutoutCommand extends CompositeCommand {
// });
const selectLayer = createLayer({
name: `选区空图层`,
name: t(`Canvas.ConstituencyEmptyLayer`),
type: LayerType.EMPTY,
visible: true,
locked: false,
@@ -284,6 +297,7 @@ export class LassoCutoutCommand extends CompositeCommand {
this.groupLayer.children.push(selectLayer);
// 插入新组图层
console.log("新增套索添加index", topLayerIndex);
this.layerManager.layers.value.splice(topLayerIndex, 0, this.groupLayer);
this.layerManager.activeLayerId.value = selectLayer.id; // 设置新组图层为活动图层

View File

@@ -116,7 +116,7 @@ export class AddLayerCommand extends Command {
parentLayer.children.splice(insertIndex, 0, newLayer);
console.log(
`新图层已插入到子图层位置: ${insertIndex} (父图层: ${parentLayer.name})`
`新图层已插入到子图层位置: ${insertIndex} (父图层: ${parentLayer.name})`
);
} else {
// 当前激活图层是一级图层
@@ -2569,7 +2569,6 @@ export class CreateImageLayerCommand extends Command {
this.fabricImage = options.fabricImage;
this.toolManager = options.toolManager;
this.layerName = options.layerName || null;
this.imageId = generateId("image_");
// 存储执行过程中的结果
@@ -2617,6 +2616,18 @@ export class CreateImageLayerCommand extends Command {
this.commands.push(createLayerCmd);
this.executedCommands.push(createLayerCmd);
// 新加功能-选区套索内添加图片自动居中
const { parent } = findLayerRecursively(this.layerManager.layers.value, this.newLayerId);
if(parent && parent.selectObject){
let {top,left,width,height} = parent.selectObject;
const ltop = top + height / 2;
const lleft = left + width / 2;
this.fabricImage.set({
top: ltop,
left: lleft,
})
}
// 2. 添加图片对象到图层命令
const addObjectCmd = new AddObjectToLayerCommand({
canvas: this.layerManager.canvas,
@@ -4197,6 +4208,7 @@ export class RemoveChildLayerCommand extends Command {
this.isActiveLayer = this.layerId === this.activeLayerId.value;
this.originalObjects = this.canvas.getObjects().filter((obj) => {
obj.parentId = this.parentId;
return obj.layerId === this.layerId;
});
}

View File

@@ -1,6 +1,8 @@
import { generateId, optimizeCanvasRendering } from "../utils/helper";
import { createLayer, LayerType, OperationType } from "../utils/layerHelper";
import { Command } from "./Command";
import i18n from "@/lang/index.ts";
const { t } = i18n.global;
/**
* 文本内容命令
@@ -329,7 +331,7 @@ export class CreateTextCommand extends Command {
// 默认文本属性
this.defaultOptions = {
text: "双击编辑文本",
text: t('Canvas.DoubleClickText'),
fontFamily: "Arial",
fontSize: 24,
fontWeight: "normal",

View File

@@ -362,6 +362,11 @@ watch(
setBrushSize(newSize);
}
);
watch(()=>isVisible.value, (newVisible) => {
if (newVisible) {
setBrushSize(brushSize.value);
}
})
// 监听brushOpacity的变化更新到BrushStore
watch(

View File

@@ -14,7 +14,8 @@
@click="setBrushTypeWithCommand(brush.id)"
:class="['brush-type-item', { active: brushStore.state.type === brush.id }]"
>
<div class="brush-preview" :style="getBrushPreviewStyle(brush)"></div>
<!-- <div class="brush-preview" :style="getBrushPreviewStyle(brush)"></div> -->
<img class="brush-preview" :src="brush.imgUrl" :title="brush.name" alt="">
<span class="brush-name">{{ brush.name }}</span>
</div>
</div>
@@ -189,7 +190,7 @@
</div>
<div class="uploaded-textures-section">
<div class="uploaded-textures-divider">
<span>{{ $t("上传的纹理") }}</span>
<span>{{ $t("Canvas.UploadedTexture") }}</span>
</div>
</div>
<div class="texture-grid">
@@ -200,7 +201,7 @@
<div class="upload-icon">
<span>+</span>
</div>
<span class="texture-label">{{ $t("上传纹理") }}</span>
<span class="texture-label">{{ $t("Canvas.UploadTexture") }}</span>
</div>
<div
v-for="textureId in brushStore.state.uploadedTextures"
@@ -218,7 +219,7 @@
<div
class="texture-remove-btn"
@click.stop="removeUploadedTexture(textureId)"
:title="$t('删除纹理')"
:title="$t('Canvas.DeleteTexture')"
>
<span>×</span>
</div>
@@ -534,6 +535,9 @@ import {
TextureUploadCommand,
} from "../commands/BrushCommands";
import { debounce } from "lodash-es";
import { useI18n } from "vue-i18n";
const { t } = useI18n();
// 从工具管理器获取可用笔刷类型
const toolManager = inject("toolManager");
@@ -872,7 +876,7 @@ function applyPresetWithCommand(presetIndex) {
// 保存当前设置为预设
function saveCurrentAsPreset() {
// 简单实现,可以后续优化为弹窗输入名称
const name = prompt("请输入预设名称:", `预设 ${BrushStore.state.presets.length + 1}`);
const name = prompt(t('Canvas.presetNamePrompt'), `${t('Canvas.preset')} ${BrushStore.state.presets.length + 1}`);
if (name) {
const presetIndex = BrushStore.saveCurrentAsPreset(name);
// 应用新创建的预设(可选)
@@ -886,6 +890,7 @@ onMounted(() => {
const availableBrushes = toolManager.brushManager
.getBrushTypes()
?.filter((brush) => brush.id !== "eraser");
console.log(availableBrushes)
BrushStore.setAvailableBrushes(availableBrushes);
}
});
@@ -1178,6 +1183,8 @@ const brushStore = BrushStore;
margin-bottom: 8px;
border-radius: 4px;
background-color: rgba(0, 0, 0, 0.02);
object-fit: contain;
background-color: #fff;
}
/* 保持笔刷预览内容样式一致 */

View File

@@ -136,7 +136,7 @@ function convertShortcuts(managerShortcuts) {
action: actionDisplay,
windows: shortcut.key.replace(/cmdOrCtrl\+/g, "Ctrl+"),
mac: shortcut.key.replace(/cmdOrCtrl\+/g, "⌘+"),
touch: shortcut.touch || "触控界面点击对应工具",
touch: shortcut.touch || t('Canvas.touchTools'),
displayKey: shortcut.displayKey,
});
}
@@ -326,8 +326,8 @@ function getShortcutsByCategory(category) {
<span v-else-if="platform.isIPad">iPad</span>
<span v-else-if="platform.isIOS">iOS</span>
<span v-else-if="platform.isAndroid">Android</span>
<span v-else>其他</span>
<span v-if="platform.isTouchDevice"> (触控设备)</span>
<span v-else>{{ $t('Canvas.other') }}</span>
<span v-if="platform.isTouchDevice"> ({{ $t('Canvas.touchDevice') }})</span>
</div>
<div class="shortcuts-category">
@@ -421,14 +421,14 @@ function getShortcutsByCategory(category) {
</div> -->
<div class="touch-tips" v-if="platform.isTouchDevice">
<h3>触控设备提示</h3>
<h3>{{ $t('Canvas.touchDevicePrompts') }}</h3>
<ul>
<li>长按图层面板可访问更多选项</li>
<li>双击元素可快速进入编辑模式</li>
<li>双指拖动可平移画布</li>
<li>双指捏合可缩放画布</li>
<li>双指连按可显示元素变换控制点</li>
<li>三指左右滑动可进行撤销/重做操作</li>
<li>{{ $t('Canvas.touchDevicePrompts_1') }}</li>
<li>{{ $t('Canvas.touchDevicePrompts_2') }}</li>
<li>{{ $t('Canvas.touchDevicePrompts_3') }}</li>
<li>{{ $t('Canvas.touchDevicePrompts_4') }}</li>
<li>{{ $t('Canvas.touchDevicePrompts_5') }}</li>
<li>{{ $t('Canvas.touchDevicePrompts_6') }}</li>
</ul>
</div>
</Skeleton>

View File

@@ -258,6 +258,23 @@ const setClosePanel = ()=>{
closePanel.value = !closePanel.value
}
// 工具管理器和画布管理器
const toolManager = inject("toolManager");
const canvasManager = inject("canvasManager");
watch(size, (newSize, oldSize) => {
setBrushIndicatorSize(newSize)
})
// 设置笔刷指示器大小
function setBrushIndicatorSize(size) {
// 如果工具管理器存在,立即应用此更改
console.log(`=========== ${size}`,toolManager);
if (toolManager) {
toolManager.updateBrushIndicatorSize(size);
}
}
// 监听当前工具变化 - 参考 SelectionPanel 的实现方式
watch(
() => props.activeTool,
@@ -269,10 +286,11 @@ watch(
// 如果面板未显示且有合适的目标对象,则显示面板
if (!visible.value) {
visible.value = true;
closePanel.value = true
closePanel.value = true
// 检查是否有可液化的对象
checkAndShowPanel();
}
setBrushIndicatorSize(size.value)
} else {
visible.value = false; // 切换到其他工具时隐藏面板
// 切换到其他工具,隐藏液化面板
@@ -444,16 +462,16 @@ function showPanel(event) {
if (detail.layerStatus && detail.layerStatus.message) {
console.log("液化操作提示:", detail.layerStatus.message);
Modal.error({
title: "错误提示",
title: t('Canvas.ErrorMessage'),
content: detail.layerStatus.message,
okText: "确定",
okText: t('Canvas.confirm'),
centered: true,
});
} else {
Modal.error({
title: "错误提示",
content: "未选择有效图像或图层不适合液化操作",
okText: "确定",
title: t('Canvas.ErrorMessage'),
content: t('Canvas.LiquidationError'),
okText: t('Canvas.confirm'),
centered: true,
});
console.log("未选择有效图像或图层不适合液化操作");
@@ -1634,24 +1652,26 @@ function stopPressTimer() {
color: #333;
border: 1px solid rgba(0, 0, 0, 0.05);
padding-bottom: 12px;
&.active{
transform: translateY(100%);
> .btn{
> i{
transform: rotate(90deg);
&.active{
transform: translateY(100%);
> .btn{
> i{
transform: rotate(90deg);
}
}
}
}
> .btn{
width: 100%;
text-align: center;
cursor: pointer;
> i{
font-size: 1.4rem;
display: block;
transform: rotate(270deg);
}
width: 100%;
text-align: center;
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
> i{
font-size: 1.4rem;
transform: rotate(270deg);
}
}
}

View File

@@ -145,10 +145,10 @@
</div>
<div class="dialog-buttons">
<button class="cancel-btn" @click="cancelFeather">
{{ $t("取消") }}
{{ $t("Canvas.close") }}
</button>
<button class="confirm-btn" @click="applyFeather">
{{ $t("确认") }}
{{ $t("Canvas.confirmEdit") }}
</button>
</div>
</div>
@@ -159,7 +159,7 @@
<div v-if="showColorPicker" class="dialog-overlay">
<div class="dialog-container">
<div class="dialog-header">
<h3>{{ $t("选择填充颜色") }}</h3>
<h3>{{ $t("Canvas.SelectFillColor") }}</h3>
<button class="close-dialog-btn" @click="cancelColorPicker">
×
</button>
@@ -168,10 +168,10 @@
<input type="color" v-model="fillColor" class="color-picker" />
<div class="dialog-buttons">
<button class="cancel-btn" @click="cancelColorPicker">
{{ $t("取消") }}
{{ $t("Canvas.close") }}
</button>
<button class="confirm-btn" @click="confirmColorPicker">
{{ $t("确认") }}
{{ $t("Canvas.confirmEdit") }}
</button>
</div>
</div>

View File

@@ -16,8 +16,9 @@ const emit = defineEmits([
"zoom-out",
"toggle-red-green-mode",
"undo-redo-status-changed",
"trigger-library"
]);
const {t} = useI18n()
const {t,locale} = useI18n()
const props = defineProps({
activeTool: String,
minimapEnabled: {
@@ -151,6 +152,13 @@ const normalToolsList = ref([
icon: { name: "CUpload", size: "26" },
class: "upload-btn",
},
{
id: "library",
title: t("LibraryPage.library"),
action: triggerLibrary,
icon: { name: "CLibrary", size: "26" },
class: "library-btn",
},
{
id: "addText",
title: t("Canvas.AddText"),
@@ -158,6 +166,17 @@ const normalToolsList = ref([
icon: { name: "CFont", size: "20" },
class: "text-btn",
},
{
id: "help",
title: t("Canvas.help"),
action: () => openTutorial(),
icon: { name: "CHelp", size: "30" },
class: "text-btn",
style: {
'position': 'absolute',
'bottom': '0',
},
},
]);
// 红绿图模式工具列表
@@ -228,10 +247,22 @@ function triggerImageUpload() {
emit("trigger-image-upload");
}
function triggerLibrary() {
emit("trigger-library");
}
function addText() {
emit("add-text");
}
function openTutorial() {
if(locale.value == 'ENGLISH'){
window.open('https://aida-user-manual.super.site/specific-scenarios/freely-sketching-in-canvas', '_blank');
}else{
window.open('https://aida-user-manual-chinese.super.site/%e4%bd%bf%e7%94%a8%e7%94%bb%e5%b8%83%e8%bf%9b%e8%a1%8c%e7%bc%96%e8%be%91 ', '_blank');
}
}
function undo() {
if (!canUndo.value) return;
undoFun();
@@ -327,19 +358,25 @@ const handleToolClick = (tool) => {
@change="fillColorChange"
style="width: 0; height: 0; opacity: 0"
/>
<div class="tools-list">
<slot name="customToolsTop" :tool-button-props="{ activeTool, canUndo, canRedo }" />
<ToolButton
v-for="tool in toolsList"
:key="tool.id"
:tool="tool"
:active-tool="activeTool"
:can-undo="canUndo"
:can-redo="canRedo"
@click="handleToolClick"
/>
<!-- 自定义工具栏按钮插槽 -->
<slot name="customTools" :tool-button-props="{ activeTool, canUndo, canRedo }" />
<ToolButton
v-for="tool in toolsList"
:key="tool.id"
:tool="tool"
:active-tool="activeTool"
:can-undo="canUndo"
:can-redo="canRedo"
@click="handleToolClick"
tip-body
/>
<!-- 自定义工具栏按钮插槽 -->
<slot name="customToolsBottom" :tool-button-props="{ activeTool, canUndo, canRedo }" />
</div>
</div>
</template>
@@ -347,15 +384,25 @@ const handleToolClick = (tool) => {
.tools-sidebar {
display: flex;
flex-direction: column;
gap: 1.0rem;
padding: 1.5rem 1.0rem;
border-right: .1rem solid #e0e0e0;
background-color: #ffffff;
user-select: none;
min-width: 5.8rem;
height: 100%;
padding-top: .5rem;
padding-bottom: 4rem;
/* overflow-y: auto; */
/* overflow-x: hidden; */
}
.tools-list{
display: flex;
flex-direction: column;
gap: 0.5rem;
flex: 1;
overflow-y: auto;
overflow-x: hidden;
}
.red-green-mode {
background-color: #fff4f4;
}

View File

@@ -21,6 +21,9 @@ import { RedGreenModeManager } from "./managers/RedGreenModeManager";
import texturePresetManager from "./managers/brushes/TexturePresetManager";
import { BrushStore } from "./store/BrushStore";
import cuowuImg from '@/assets/images/homePage/cuowu.svg'
import { Https } from "@/tool/https";
import SelectImages from '@/component/common/SelectImages.vue'
import { UrlToFile } from '@/tool/util'
// import { MinimapManager } from "./managers/minimap/MinimapManager";
@@ -52,6 +55,7 @@ const emit = defineEmits([
"trigger-red-green-mouseup", // 红绿图模式鼠标抬起事件
"changeCanvas", // 画布变更事件
"canvasInit", // 画布初始化事件
"trigger-library", // 触发打开Library选择图片事件
]);
const props = defineProps({
@@ -101,6 +105,14 @@ const props = defineProps({
type: Boolean,
default: false, // 是否显示固定图层
},
isGeneral: { // 从generalMiniCanvas来的
type: Boolean,
default:false
},
isEdit: { // 从design点击喜欢过的图片,再点击顶部的编辑图标
type: Boolean,
default: false
}
});
// 引用和状态
@@ -569,7 +581,6 @@ function zoomOut() {
x: canvasManager.canvas.width / 2,
y: canvasManager.canvas.height / 2,
};
canvasManager.animateZoom(centerPoint, newZoom);
}
@@ -601,7 +612,7 @@ async function addLayer() {
await layerManager.createLayer(t("Canvas.EmptyLayer"));
}
async function addTopLayer() {
await layerManager.createLayer("空图层", LayerType.EMPTY, {
await layerManager.createLayer(t("Canvas.EmptyLayer"), LayerType.EMPTY, {
insertTop: true,
});
}
@@ -667,12 +678,20 @@ function deleteFun(){
function removeLayer(layerId) {
// Check if this is the last layer - prevent deletion
if (layers.value.length <= 2) {
console.warn(
var isChild = false;
var parentLength = 0;
layers.value.forEach((layer) => {
if(layer.children.some(v => v.id == layerId)){
isChild = true;
parentLength = layer.children.length;
}
})
if(isChild && parentLength == 1 || layers.value.length <= 3){
console.warn(
"Cannot delete the last layer. At least one layer must remain."
);
return;
}
}
if (canvasManager && canvasManager.canvas) {
const layerToRemove = layers.value.find((l) => l.id === layerId);
@@ -727,6 +746,21 @@ function handleImageUpload(event) {
});
}
const selectImages = ref(null);
const handleImageSelect = (data) => {
UrlToFile(data.url,data.name).then((file)=>{
handleImageUpload({ target: { files: [file] } })
})
}
function triggerLibrary() {
// console.log('CanvasEditor', '打开收藏')
if (props.isGeneral || props.isEdit) {
selectImages.value.init()
} else {
emit("trigger-library");
}
}
function handleAddText() {
if (toolManager && canvasManager && canvasManager.canvas) {
// 在画布中央创建文本
@@ -828,6 +862,7 @@ const changeCanvas = async (command) => {
setTimeout(async ()=>{
const imageData = await canvasManager.exportImage({
restoreOpacityInRedGreen: true, // 恢复红绿图模式下的透明度
isCropByBg:true,
});
emit("trigger-red-green-mouseup", imageData);
},100)
@@ -1066,10 +1101,14 @@ defineExpose({
@zoom-in="zoomIn"
@zoom-out="zoomOut"
@undo-redo-status-changed="changeCanvas"
@trigger-library="triggerLibrary"
>
<template #customToolsTop="{ toolTopProps }">
<slot name="customToolsTop" :tool-button-props="toolTopProps" />
</template>
<!-- 扩展插槽 -->
<template #customTools="{ toolButtonProps }">
<slot name="customTools" :tool-button-props="toolButtonProps" />
<template #customToolsBottom="{ toolButtonProps }">
<slot name="customToolsBottom" :tool-button-props="toolButtonProps" />
</template>
</ToolsSidebar>
</div>
@@ -1184,6 +1223,14 @@ defineExpose({
style="display: none"
@change="handleImageUpload"
/>
<SelectImages
ref="selectImages"
full-data
radio
@select="handleImageSelect"
:api="Https.httpUrls.queryLibraryPage"
isLibrary
/>
</div>
</template>

View File

@@ -1,4 +1,5 @@
import { fabric } from "fabric-with-all";
import { OperationType } from "../utils/layerHelper";
/**
* 笔刷指示器
@@ -104,9 +105,8 @@ export class BrushIndicator {
this.canvas.freeDrawingBrush &&
this.canvas.freeDrawingBrush.type === "eraser";
if (!isBrushMode && !isEraserMode) {
return;
}
const isLiquifyMode = this.canvas.toolId === OperationType.LIQUIFY;// 检查是否在液化模式
if ([isBrushMode, isEraserMode, isLiquifyMode].every(v => !v)) return;
let hasChanges = false;
@@ -391,6 +391,9 @@ export class BrushIndicator {
this._mouseEnterHandler = (e) => {
if (this._shouldShowIndicator()) {
this.show(e.e);
const currentVpt = this.canvas.viewportTransform;
this.staticCanvas.setViewportTransform([...currentVpt]);
this.staticCanvas.renderAll();
}
};
@@ -471,9 +474,12 @@ export class BrushIndicator {
* @returns {Boolean} 是否显示
*/
_shouldShowIndicator() {
// 检查画布是否在绘图模式
if (!this.canvas.isDrawingMode) return false;
const isDrawingMode = this.canvas.isDrawingMode;// 检查画布是否在绘图模式
const isLiquifyMode = this.canvas.toolId === OperationType.LIQUIFY;// 检查是否在液化模式
// console.log(`笔刷指示器\n绘图模式:${isDrawingMode}\n液化模式:${isLiquifyMode}`)
// 检查画布是否在绘图模式OR液化模式
if ([isDrawingMode, isLiquifyMode].every(v => !v)) return false;
// 检查是否有笔刷
if (!this.canvas.freeDrawingBrush) return false;

View File

@@ -137,7 +137,9 @@ export class ExportManager {
return await this._exportWithCanvasSize(
objectsToExport,
expPicType,
restoreOpacityInRedGreen
restoreOpacityInRedGreen,
isCropByBg, // 是否使用背景大小裁剪
isEnhanceImg, // 是否是增强图片
);
}
@@ -224,7 +226,7 @@ export class ExportManager {
const objectsToExport = this._collectObjectsByLayerOrder(
null, // 导出所有图层
isContainBg,
isContainFixed
isContainFixed,
);
if (objectsToExport.length === 0) {
@@ -549,7 +551,7 @@ export class ExportManager {
return await this._exportWithCanvasSize(
objectsToExport,
expPicType,
restoreOpacityInRedGreen
restoreOpacityInRedGreen,
);
}

View File

@@ -373,6 +373,8 @@ export class ToolManager {
// 设置工具特定的状态
const tool = this.tools[toolId];
if (tool && typeof tool.setup === "function") {
console.log(`画布切换工具:${tool.name}(${toolId})`)
this.canvas.toolId = toolId;
tool.setup();
}
@@ -450,6 +452,7 @@ export class ToolManager {
if (!this.canvas) return;
this.canvas.isDrawingMode = false;
this.canvas.selection = true;
}
/**
@@ -750,6 +753,7 @@ export class ToolManager {
detail: panelDetail,
})
);
this._enableBrushIndicator();
}
/**
@@ -759,14 +763,14 @@ export class ToolManager {
* @private
*/
_showRasterizeConfirmModal(isGroup, layerId) {
const title = "栅格化图层";
const content = "需要先栅格化才能进行液化操作,是否立即栅格化?";
const title = this.t("Canvas.RasterizedLayer");
const content = this.t("Canvas.rasterizeImmediately");
Modal.confirm({
title,
content,
okText: "确定栅格化",
cancelText: "取消",
okText: this.t("Canvas.ConfirmRasterization"),
cancelText: this.t("Canvas.close"),
centered: true,
icon: h("span", { style: "color: #faad14;" }, "⚠️"),
onOk: () => {
@@ -800,8 +804,8 @@ export class ToolManager {
if (!this.commandManager || !this.layerManager) return;
// 显示加载Modal
const loadingModal = Modal.info({
title: "正在栅格化",
content: "正在栅格化图层,请稍候...",
title: this.t('Canvas.beingRasterized'),
content: this.t('Canvas.waitRasterizing'),
okButtonProps: { style: { display: "none" } },
centered: true,
closable: false,
@@ -823,16 +827,16 @@ export class ToolManager {
if (result) {
// 栅格化成功,启动液化
message.success("图层已成功栅格化,可以进行液化操作");
message.success(this.t('Canvas.successRasterizing'));
this._startLiquify(result);
this.setTool(OperationType.LIQUIFY); // 切换到液化工具
} else {
// 栅格化失败
Modal.error({
title: "栅格化失败",
content: "栅格化失败,无法进行液化操作",
okText: "确定",
title: this.t('Canvas.gridingFailed'),
content: this.t('Canvas.gridingFailedNoOperation'),
okText: this.t('Canvas.ok'),
centered: true,
});
}
@@ -840,9 +844,9 @@ export class ToolManager {
console.error("栅格化图层失败:", error);
Modal.error({
title: "栅格化错误",
content: `栅格化失败${error.message}`,
okText: "确定",
title: this.t('Canvas.gridingError'),
content: `${this.t('Canvas.gridingFailed')}${error.message}`,
okText: this.t('Canvas.ok'),
centered: true,
});
} finally {
@@ -866,9 +870,9 @@ export class ToolManager {
);
if (!layer) {
Modal.error({
title: "图层错误",
content: "图层不存在",
okText: "确定",
title: this.t('Canvas.LayerError'),
content: this.t('Canvas.LayerDoesNotExist'),
okText: this.t('Canvas.ok'),
centered: true,
});
return;
@@ -880,9 +884,9 @@ export class ToolManager {
// 背景图层使用 fabricObject (单数)
if (!layer.fabricObject) {
Modal.warning({
title: "背景图层为空",
content: "背景图层为空,无法进行液化操作",
okText: "确定",
title: this.t('Canvas.backgroundEmpty'),
content: this.t('Canvas.backgroundEmptyNoLiquidation'),
okText: this.t('Canvas.ok'),
centered: true,
});
return;
@@ -892,9 +896,9 @@ export class ToolManager {
// 普通图层使用 fabricObjects (复数)
if (!layer.fabricObjects || layer.fabricObjects.length === 0) {
Modal.warning({
title: "图层为空",
content: "图层为空,无法进行液化操作",
okText: "确定",
title: this.t('Canvas.layerEmpty'),
content: this.t('Canvas.layerEmptyNoLiquidation'),
okText: this.t('Canvas.ok'),
centered: true,
});
return;
@@ -906,9 +910,9 @@ export class ToolManager {
const liquifyManager = this.canvasManager?.liquifyManager;
if (!liquifyManager) {
Modal.error({
title: "液化管理器错误",
content: "液化管理器未初始化",
okText: "确定",
title: this.t('Canvas.liqueficationManagerError'),
content: this.t('Canvas.liqueficationManagerErrorInitialized'),
okText: this.t('Canvas.ok'),
centered: true,
});
return;
@@ -917,8 +921,8 @@ export class ToolManager {
try {
// 显示准备中的Modal
const preparingModal = Modal.info({
title: "准备液化环境",
content: "正在准备液化环境,请稍候...",
title: this.t('Canvas.liquefactionEnvironment'),
content: this.t('Canvas.liquefactionEnvironmentLoading'),
okButtonProps: { style: { display: "none" } },
centered: true,
closable: false,
@@ -963,9 +967,9 @@ export class ToolManager {
} catch (error) {
console.error("启动液化工具失败:", error);
Modal.error({
title: "液化工具启动失败",
content: `启动液化工具失败:${error.message}`,
okText: "确定",
title: this.t('Canvas.LiqueficationFailed'),
content: `${this.t('Canvas.LiqueficationFailed')}:${error.message}`,
okText: this.t('Canvas.ok'),
centered: true,
});
}
@@ -1041,7 +1045,7 @@ export class ToolManager {
_createTextDirect(x, y, options = {}) {
// 默认文本属性
const defaultOptions = {
text: "双击编辑文本",
text: this.t('Canvas.DoubleClickText'),
fontFamily: "Arial",
fontSize: 24,
fontWeight: "normal",
@@ -1188,6 +1192,7 @@ export class ToolManager {
if (this.brushIndicator) {
this.brushIndicator.dispose();
this.brushIndicator = null;
console.log("笔刷指示器已清理");
}
// 移除文本编辑相关事件监听器
@@ -1381,6 +1386,7 @@ export class ToolManager {
if (!this.brushIndicator) return;
this.brushIndicator.updateSize(size);
console.log(`笔刷指示器大小已更新为: ${size}`);
}
/**
@@ -1391,6 +1397,7 @@ export class ToolManager {
if (!this.brushIndicator) return;
this.brushIndicator.updateColor(color);
console.log(`笔刷指示器颜色已更新为: ${color}`);
}
/**
@@ -1465,6 +1472,7 @@ export class ToolManager {
OperationType.ERASER,
OperationType.RED_BRUSH,
OperationType.GREEN_BRUSH,
OperationType.LIQUIFY,
];
return brushTools.includes(currentTool);

View File

@@ -173,7 +173,7 @@ export class AnimationManager {
this._zoomAnimation = null;
// 确保最终状态准确
this._applyZoom(point, targetZoom, true);
// this._applyZoom(point, targetZoom, true);
},
};

View File

@@ -65,6 +65,7 @@ export class BrushManager {
description: "基础铅笔工具,适合精细线条绘制",
t:this.t,
category: this.t('Canvas.BasicBrushes'),
imgUrl:'./image/brush/pencil.jpg',
});
// 注册材质笔刷
@@ -73,6 +74,7 @@ export class BrushManager {
description: "使用纹理图片作为笔刷,支持缩放和透明度",
t:this.t,
category: this.t('Canvas.BasicBrushes'),
imgUrl:'./image/brush/texture.jpg',
});
// 注册集成的笔刷类型
@@ -81,54 +83,63 @@ export class BrushManager {
description: "使用纹理图片作为笔刷,支持缩放和透明度",
t:this.t,
category: this.t('Canvas.BasicBrushes'),
imgUrl:'./image/brush/crayon.jpg',
});
brushRegistry.register("fur", FurBrush, {
name: this.t("Canvas.Fur"),
description: "使用纹理图片作为笔刷,支持缩放和透明度",
t:this.t,
category: this.t('Canvas.BasicBrushes'),
imgUrl:'./image/brush/fur.jpg',
});
brushRegistry.register("ink", InkBrush, {
name: this.t("Canvas.Ink"),
description: "墨水笔刷,适合书写和绘图",
t:this.t,
category: this.t('Canvas.BasicBrushes'),
imgUrl:'./image/brush/ink.jpg',
});
brushRegistry.register("", LongfurBrush, {
name: this.t("Canvas.Longfur"),
description: "长毛发笔刷,适合绘制动物毛皮、草或头发",
t:this.t,
category: this.t('Canvas.BasicBrushes'),
imgUrl:'./image/brush/longFur.jpg',
});
brushRegistry.register("writing", WritingBrush, {
name: this.t("Canvas.Writing"),
description: "书法笔刷,模拟中国传统书法毛笔效果,具有笔锋和墨色变化",
t:this.t,
category: this.t('Canvas.BasicBrushes'),
imgUrl:'./image/brush/writing.jpg',
});
brushRegistry.register("marker", MarkerBrush, {
name: this.t("Canvas.Marker"),
description: "马克笔笔刷,适合粗线条和填充",
t:this.t,
category: this.t('Canvas.BasicBrushes'),
imgUrl:'./image/brush/marker.jpg',
});
brushRegistry.register("pen", CustomPenBrush, {
name: this.t("Canvas.Pen"),
description: "自定义钢笔笔刷,适合书写和绘图",
t:this.t,
category: this.t('Canvas.BasicBrushes'),
imgUrl:'./image/brush/pen.jpg',
});
brushRegistry.register("ribbon", RibbonBrush, {
name: this.t("Canvas.Ribbon"),
description: "丝带笔刷,适合创建流动的丝带效果",
t:this.t,
category: this.t('Canvas.BasicBrushes'),
imgUrl:'./image/brush/ribbon.jpg',
});
brushRegistry.register("shaded", ShadedBrush, {
name: this.t("Canvas.Shaded"),
description: "阴影笔刷,适合创建渐变和阴影效果",
t:this.t,
category: this.t('Canvas.BasicBrushes'),
imgUrl:'./image/brush/shaded.jpg',
});
brushRegistry.register("spray", SprayBrush, {
@@ -136,6 +147,7 @@ export class BrushManager {
description: "模拟喷枪效果,创建散点效果",
t:this.t,
category: this.t('Canvas.BasicBrushes'),
imgUrl:'./image/brush/spray.jpg',
});
// brushRegistry.register("sketchy", SketchyBrush);
@@ -365,6 +377,7 @@ export class BrushManager {
description: brushInfo.metadata.description || "",
category: brushInfo.metadata.category || "默认",
icon: brushInfo.metadata.icon || null,
imgUrl: brushInfo.metadata.imgUrl || null,
}));
}

View File

@@ -1,6 +1,8 @@
import { BaseBrush } from "../BaseBrush";
import { fabric } from "fabric-with-all";
import texturePresetManager from "../TexturePresetManager";
import i18n from "@/lang/index.ts";
const {t} = i18n.global;
/**
* 纹理笔刷
@@ -468,12 +470,12 @@ export class TextureBrush extends BaseBrush {
const textureProperties = [
{
id: "textureSelector",
name: "材质选择",
name: t('Canvas.TextureSelector'),
type: "texture-grid",
defaultValue: this.selectedTextureId,
options: textureOptions,
description: "选择要使用的纹理",
category: "纹理设置",
description: t('Canvas.selectTexture'),
category: t('Canvas.TextureSettings'),
order: 100,
hidden: allTextures.length === 0,
},

View File

@@ -706,7 +706,8 @@ export class CanvasEventManager {
if (e.target.id) {
// 如果该元素是分组图层的一部分,也更新分组图层的缩略图
if (e.target.parentId) {
setTimeout(() => this.updateLayerThumbnail(e.target.parentId), 50);
// setTimeout(() => this.updateLayerThumbnail(e.target.parentId), 50);
this.thumbnailManager.generateLayerThumbnail(e.target.parentId);
}
}
}

View File

@@ -5,6 +5,8 @@
import { LiquifyWebGLManager } from "./LiquifyWebGLManager";
import { LiquifyCPUManager } from "./LiquifyCPUManager";
import { findLayerRecursively, LayerType } from "../../utils/layerHelper";
import i18n from "@/lang/index.ts";
const {t} = i18n.global;
export class EnhancedLiquifyManager {
/**
@@ -20,13 +22,13 @@ export class EnhancedLiquifyManager {
// 是否强制使用WebGL模式
forceWebGL: options.forceWebGL || false,
// 网格大小
gridSize: options.gridSize || 15,
gridSize: options.gridSize || 8,
// 最大变形强度
maxStrength: options.maxStrength || 100,
maxStrength: options.maxStrength || 200,
// 平滑迭代次数
smoothingIterations: options.smoothingIterations || 2,
smoothingIterations: options.smoothingIterations || 1,
// 网格弹性因子
relaxFactor: options.relaxFactor || 0.25,
relaxFactor: options.relaxFactor || 0.05,
// WebGL网格精度
meshResolution: options.meshResolution || 64,
};
@@ -599,7 +601,7 @@ export class EnhancedLiquifyManager {
if (objectsToCheck.length === 0) {
return {
valid: false,
message: "图层为空,无法进行液化操作",
message: t('Canvas.layerEmptyNoLiquidation'),
needsRasterization: false,
isImage: false,
isEmpty: true,

View File

@@ -31,10 +31,10 @@ export class LiquifyManager {
// 创建增强版液化管理器实例
this.enhancedManager = new EnhancedLiquifyManager({
// 配置选项
gridSize: options.gridSize || 15,
maxStrength: options.maxStrength || 100,
smoothingIterations: options.smoothingIterations || 2,
relaxFactor: options.relaxFactor || 0.25,
gridSize: options.gridSize || 8,
maxStrength: options.maxStrength || 200,
smoothingIterations: options.smoothingIterations || 1,
relaxFactor: options.relaxFactor || 0.05,
meshResolution: options.meshResolution || 64,
// 根据环境选择合适的渲染模式
forceCPU: true, // 默认不强制使用CPU

View File

@@ -21,6 +21,10 @@ const props = defineProps({
type: Boolean,
default: true,
},
tipBody: {
type: Boolean,
default: false,
},
});
const emit = defineEmits(["click"]);
@@ -47,10 +51,45 @@ const handleClick = () => {
if (isDisabled.value) return;
emit("click", props.tool);
};
const tipId = "tooltip-" + Math.random().toString(36).substring(2);
const el = ref(null);
// 鼠标移入获取位置信息
const handleMouseEnter = (e) => {
const tooltip = el.value;
const rect = tooltip.getBoundingClientRect();
const left = rect.left + rect.width;
const top = rect.top + rect.height / 2;
const tip = document.getElementById(tipId);
tip.style.position = 'fixed';
tip.style.left = `${left}px`;
tip.style.top = `${top}px`;
tip.style.display = 'block';
}
// 鼠标移出隐藏提示
const handleMouseLeave = () => {
const tip = document.getElementById(tipId);
tip.style.display = 'none';
}
onMounted(() => {
if(props.tipBody){
el.value.addEventListener('mouseenter', handleMouseEnter);
el.value.addEventListener('mouseleave', handleMouseLeave);
}
})
onUnmounted(() => {
if(props.tipBody && el.value){
el.value.removeEventListener('mouseenter', handleMouseEnter);
el.value.removeEventListener('mouseleave', handleMouseLeave);
}
})
</script>
<template>
<div
ref="el"
:class="[
'tool-btn',
tool.class,
@@ -63,7 +102,10 @@ const handleClick = () => {
@click="handleClick"
>
<SvgIcon :name="tool.icon.name" :size="tool.icon.size"></SvgIcon>
<div class="tool-tooltip">{{ t(tool.title) }}</div>
<teleport to="body" v-if="tipBody">
<div class="tool-tooltip" :id="tipId">{{ t(tool.title) }}</div>
</teleport>
<div class="tool-tooltip" v-else>{{ t(tool.title) }}</div>
</div>
</template>
@@ -82,6 +124,7 @@ const handleClick = () => {
font-size: 1.6rem;
color: #333;
transition: all 0.2s ease;
flex-shrink: 0;
}
.tool-btn:hover {
@@ -115,7 +158,7 @@ const handleClick = () => {
margin-left: .8rem;
white-space: nowrap;
font-size: 1.2rem;
z-index: 10;
z-index: 9999;
}
.tool-tooltip:before {

View File

@@ -4,7 +4,7 @@
<div
class="image-list-trigger"
@click="showPanel = true"
:title="$t('打开图片库')"
:title="$t('Canvas.photoGallery')"
>
<SvgIcon name="CImageList" :size="20" />
</div>
@@ -56,6 +56,14 @@
<span class="image-name">{{ item.name || "未命名" }}</span>
</div>
</div>
<!-- <div class="image-select" v-show="selectList.includes(item.url)">
<i class="fi fi-sr-check-circle"></i>
</div> -->
<img
src="@/assets/images/icon/selected.png"
class="image-select"
v-show="selectList.includes(item.url)"
>
</div>
</div>
@@ -70,6 +78,7 @@
<div class="image-count">
{{ $t("Canvas.general") }} {{ filteredImages.length }} {{ $t("Canvas.PicturesInTotal") }}
</div>
<div class="image-submit gallery_btn" @click="confirm">{{ $t("Canvas.confirm") }}</div>
</div>
</div>
</div>
@@ -103,6 +112,7 @@ const emits = defineEmits(["select"]);
// 响应式数据
const showPanel = ref(false);
const selectedCategory = ref(t("Canvas.all"));
const selectList = ref([])
// 计算属性:获取所有分类
const categories = computed(() => {
@@ -152,8 +162,12 @@ const filteredImages = computed(() => {
// 处理图片点击
const handleImageClick = (item) => {
emits("select", item);
showPanel.value = false;
// 已选中,取消选中
if(selectList.value.includes(item.url)){
selectList.value = selectList.value.filter(url => url !== item.url)
}else{
selectList.value.push(item.url)
}
};
// 处理图片加载错误
@@ -162,6 +176,13 @@ const handleImageError = (event) => {
"data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMjAwIiBoZWlnaHQ9IjIwMCIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj48cmVjdCB3aWR0aD0iMTAwJSIgaGVpZ2h0PSIxMDAlIiBmaWxsPSIjZjVmNWY1Ii8+PHRleHQgeD0iNTAlIiB5PSI1MCUiIGZvbnQtZmFtaWx5PSJBcmlhbCwgc2Fucy1zZXJpZiIgZm9udC1zaXplPSIxNCIgZmlsbD0iIzk5OSIgdGV4dC1hbmNob3I9Im1pZGRsZSIgZHk9Ii4zZW0iPuWbvueJh+WKoOi9veWksei0pe+8jOivt+ajgOafpeWbvueJh+i3r+W+hDwvdGV4dD48L3N2Zz4=";
event.target.alt = "图片加载失败";
};
//提交选中的T图片
const confirm = ()=>{
emits("select", selectList.value);
selectList.value = []
showPanel.value = false;
}
</script>
<style scoped lang="less">
@@ -368,10 +389,11 @@ const handleImageError = (event) => {
.image-item {
cursor: pointer;
border-radius: 8px;
overflow: hidden;
// overflow: hidden;
transition: all 0.2s ease;
background-color: #fff;
border: 1px solid rgba(0, 0, 0, 0.05);
position: relative;
&:hover {
transform: translateY(-4px);
@@ -414,6 +436,17 @@ const handleImageError = (event) => {
transition: opacity 0.2s ease;
}
.image-select{
position: absolute;
bottom: 0;
right: 0;
z-index: 2;
transform: translate(50%,50%);
i{
font-size: 2.5rem;
}
}
.image-name {
font-size: 14px;
font-weight: 500;
@@ -455,6 +488,10 @@ const handleImageError = (event) => {
justify-content: space-between;
align-items: center;
flex-shrink: 0;
> .image-submit{
font-size: 1.2rem;
line-height: 4rem;
}
}
.image-count {

View File

@@ -12,6 +12,7 @@
:enabledRedGreenMode="true"
:clothingImageUrl="imageUrls.baseImage"
:redGreenImageUrl="imageUrls.maskImage"
@trigger-red-green-mouseup="frontBackChange"
:clothingImageOpts="{ imageMode: 'contains' }"
/>
</div>
@@ -51,6 +52,9 @@ const loadJSON = () => {
const changeFixedImage = () => {
canvasEditor.value.changeFixedImage(changeImageUrl);
};
const frontBackChange = (value) =>{
console.log(value)
}
// 组件挂载时绑定键盘事件
onMounted(() => {});

View File

@@ -52,6 +52,7 @@ const imageData = [
const handleImageSelect = (selectedImage) => {
console.log("选中的图片:", selectedImage);
console.log(selectedImage)
// selectedImage 包含url, name, categoryName, categoryType, originalItem
};
@@ -176,6 +177,10 @@ const canvasInit = () => {
// });
};
const frontBackChange =(value)=>{
console.log(value)
}
const isShowLeft = ref(true);
</script>
@@ -195,6 +200,7 @@ const isShowLeft = ref(true);
<RedGreenModeExample
v-if="currentView === 'redGreenExample'"
key="redGreenExample"
@trigger-red-green-mouseup="frontBackChange"
>
</RedGreenModeExample>
@@ -218,7 +224,7 @@ const isShowLeft = ref(true);
</template>
<!-- 使用插槽添加自定义工具栏按钮 -->
<template #customTools="{ toolButtonProps }">
<template #customToolsBottom="{ toolButtonProps }">
<!-- 分隔线 -->
<div class="tool-separator"></div>

View File

@@ -59,7 +59,7 @@
:key="positionKey"
@canvasReload="canvasReload"
@detailEdit="detailEdit"
@addSketch="()=>isEditPattern.value=false"
@addSketch="()=>isEditPattern.value = ''"
@revocation="revocation"
@oppositeRevocation="oppositeRevocation"
@modelOnLoad="modelOnLoad"
@@ -69,7 +69,7 @@
<div v-show="isEditPattern.value" style="margin-left: 2rem;" class="gallery_btn" @click="previwe">{{$t('DesignPrintOperation.Preview')}}</div>
</div>
</div>
<div class="item detailRight">
<div class="item detailRight" :class="{canvas:isEditPattern.value}">
<div class="submit">
</div>
<div class="contentRight" v-if="currentDetailType && !isEditPattern.value">
@@ -88,7 +88,7 @@
</div>
</div>
<div class="contentRight" v-if="selectDetail && selectDetail.id && currentDetailType && isEditPattern.value">
<canvasBox ref="canvasBox" :key="canvasKey"></canvasBox>
<canvasBox ref="canvasBox" :key="canvasKey || isEditPattern.value" :isEditPattern="isEditPattern.value"></canvasBox>
</div>
<!-- 画布 -->
<!-- <div class="content" v-else-if="selectDetail && selectDetail.id">
@@ -145,7 +145,7 @@ export default defineComponent({
oppositeRevocationShow:-1,
revocationShow:-1,
isEditPattern:{
value:false,
value:'' as any,
},// 是否编辑图案
canvasKey:0,
singleOveral:{
@@ -158,6 +158,7 @@ export default defineComponent({
isUndividedLayerWithSinglePrint:false,
})
provide('getCanvasIfEdit',detailData.getCanvasIfEdit)
provide('singleOveral',detailData.singleOveral)
provide('isEditPattern',detailData.isEditPattern)
@@ -375,7 +376,7 @@ export default defineComponent({
designSingleItemDTOList:clothes,
isPreview:true,
// ifSubmit:designItemDetail.isPreview,
gender:workspace?.sex == 'Male'?1:0,
gender:workspace?.sex,
sketchString:'',
modelId:(detailData.currentDetailType == 'models' && detailData.designDetail.newModel)?detailData.designDetail.newModel.id:detailData.designDetail.oldModel?detailData.designDetail.oldModel.id:'',
modelType:(detailData.currentDetailType == 'models' && detailData.designDetail.newModel)?detailData.designDetail.newModel.type:detailData.designDetail.oldModel?detailData.designDetail.oldModel.type:'',
@@ -451,11 +452,18 @@ export default defineComponent({
})
}
const detailEdit = async (str:any)=>{
if(str == 'edit'){
if(detailData.isEditPattern.value){
if(str){
if(detailData.isEditPattern.value && detailData.isEditPattern.value == str){
await detailDom.canvasBox.saveCanvas()
detailData.isEditPattern.value = ''
}else{
if(detailData.isEditPattern.value){
detailDom.canvasBox.editFront(str)
}
detailData.isEditPattern.value = str
}
detailData.isEditPattern.value = !detailData.isEditPattern.value
}else{
detailData.isEditPattern.value = ''
}
}
const canvasReload = async ()=>{
@@ -543,7 +551,7 @@ export default defineComponent({
</script>
<style lang="less" scoped>
:deep(.detailText){
font-size: 1.8rem;
font-size: 1.6rem;
font-weight: 600;
}
@@ -559,6 +567,7 @@ export default defineComponent({
:deep(>div){
> .ant-modal-root{
> .ant-modal-centered{
overflow: hidden;
> .fullScreen{
> .ant-modal-content{
box-shadow: none;
@@ -587,19 +596,28 @@ export default defineComponent({
height: 100%;
overflow: hidden;
&.detailLeft{
width: 34rem;
width: 30rem;
// width: 34rem;
display: flex;
flex-direction: column;
justify-content: space-between;
overflow: hidden;
}
&.isEditPattern{width: 0px;}
&.model{
width: 50rem;
margin: 0 10rem;
flex: 1;
// width: 45rem;
// width: 50rem;
margin: 0 8rem;
// margin: 0 10rem;
display: flex;
flex-direction: column;
}
&.detailRight{
flex: 1;
width: 30%;
&.canvas{
width: 60%;
}
display: flex;
flex-direction: column;
// padding-bottom: calc(6rem + 1rem);

View File

@@ -9,6 +9,7 @@
<editCanvas v-if="canvasLoad" :config="canvasConfig"
@canvasInit="canvasInit"
@changeCanvas="changeCanvas"
is-edit
:clothingImageUrl="selectDetail?.undividedLayerWithSinglePrint || selectDetail.undividedLayer || selectDetail.path"
showFixedLayer
:canvasJSON="canvasJSON"
@@ -16,13 +17,6 @@
imageMode:'contains',
}"
ref="editCanvas">
<template #customTools="{ toolButtonProps }">
<!-- 也可以直接使用普通的按钮 -->
<div class="custom-tool-btn" :class="{active:currentView === 'redGreenExample'}" @click="editFront('redGreenExample')">
<i class="fi fi-sr-layers"></i>
<div class="tool-tooltip">Edit the front and back sections</div>
</div>
</template>
</editCanvas>
<!-- <canvasContent ref="canvasContent"></canvasContent> -->
</div>
@@ -34,20 +28,16 @@
ref="editFrontBack">
</editFrontBack> -->
<editCanvas v-if="canvasLoad" :config="canvasConfig"
@canvasInit="canvasInit"
:enabledRedGreenMode="true"
:clothingImageUrl="selectDetail.path"
:redGreenImageUrl="frontBack.front[imgDomIndex].maskUrl"
@trigger-red-green-mouseup="frontBackChange"
is-edit
:clothing-image-opts="{
imageMode:'contains',
}"
ref="editCanvasBackFront">
<template #customTools="{ toolButtonProps }">
<div class="custom-tool-btn" :class="{active:currentView === 'redGreenExample'}" @click="editFront('canvasEditor')">
<i class="fi fi-sr-layers"></i>
<div class="tool-tooltip">Edit the front and back sections</div>
</div>
</template>
</editCanvas>
</div>
</div>
@@ -79,6 +69,10 @@ export default defineComponent({
editCanvas
},
props:{
isEditPattern:{
type:String,
default:''
}
},
setup(props,{emit}) {
const store = useStore();
@@ -106,7 +100,7 @@ export default defineComponent({
canvasLoad:false,
canvasConfig:{
} as any,
currentView:'',
currentView:props.isEditPattern,
getCanvasIfEdit:inject('getCanvasIfEdit')as any,
canvasInstance:null as any,
canvasJSON:'',
@@ -117,6 +111,7 @@ export default defineComponent({
},{immediate: true})
provide('isShowMark',detailData.isShowMark)
provide('canvasType',detailData.canvasType)
const editFront = (str:any)=>{//编辑前后片
let canvasJSON = '' as any
@@ -184,12 +179,55 @@ export default defineComponent({
img.src = url
})
}
const resizeImageWithNativeCanvas = async (image1Url, imageBUrl)=>{
try {
// 加载第一张图片获取尺寸
const img1 = await loadImage(image1Url);
const targetWidth = img1.naturalWidth;
const targetHeight = img1.naturalHeight;
// 加载第二张图片
const imgB = await loadImage(imageBUrl);
// 创建canvas元素
const canvas = document.createElement('canvas');
canvas.width = targetWidth;
canvas.height = targetHeight;
const ctx = canvas.getContext('2d');
// 绘制调整尺寸后的图片
ctx.drawImage(imgB, 0, 0, targetWidth, targetHeight);
// 导出base64
const base64 = canvas.toDataURL('image/png', 1);
return base64;
} catch (error) {
console.error('处理图片时出错:', error);
throw error;
}
}
// 图片加载辅助函数
const loadImage = (url)=>{
return new Promise((resolve, reject) => {
const img = new Image();
img.crossOrigin = 'anonymous';
img.onload = () => resolve(img);
img.onerror = reject;
img.src = url;
});
}
const frontBackChange = (value:any)=>{
let full = detailData.frontBack.front[detailData.imgDomIndex]?.undividedLayerWithSinglePrint || detailData.frontBack.front[detailData.imgDomIndex].undividedLayer || detailData.selectDetail.path
let size = {
...detailData.canvasConfig,
}
segmentImage(value,full,size).then((rv)=>{
segmentImage(value,full,size).then(async (rv)=>{
let front = detailData.frontBack.front[detailData.imgDomIndex]
let back = detailData.frontBack.back[detailData.imgDomIndex]
if(!front?.oldImageUrl)front.oldImageUrl = front.imageUrl
@@ -198,14 +236,14 @@ export default defineComponent({
if(!front?.oldMaskUrl)store.commit('DesignDetail/updataDetailItem',{maskUrl:front.oldMaskUrl})
front.imageUrl = rv.targetFrontUrl
front.maskUrl = value
let base64 = await resizeImageWithNativeCanvas(front.oldMaskUrl,value)
front.maskUrl = base64
back.imageUrl = rv.targetBackUrl
store.commit('DesignDetail/updataDetailItem',{maskUrl:value})
})
}
const canvasInit = (value:any)=>{
// detailDom.editCanvas.addImageToLayer(detailData.selectDetail.undividedLayer,{layerId:value.layers.value[0].id,imageMode:'contains',undoable:false})
detailData.canvasInstance = value
detailData.getCanvasIfEdit.fun = getCanvasLength
detailData.isShowMark = false
@@ -254,9 +292,11 @@ export default defineComponent({
saveCanvas('auto')
},3000)
}
onBeforeUnmount(()=>{
let front = detailData.frontBack.front[detailData.imgDomIndex]
let back = detailData.frontBack.back[detailData.imgDomIndex]
if(front?.oldImageUrl)front.imageUrl = front.oldImageUrl
if(front?.oldMaskUrl)front.maskUrl = front.oldMaskUrl
if(back?.oldImageUrl)back.imageUrl = back.oldImageUrl
@@ -269,9 +309,7 @@ export default defineComponent({
})
onMounted(()=>{
nextTick(async ()=>{
detailData.currentView = 'canvasEditor'
setTimeout(()=>{
})
// detailData.currentView = 'canvasEditor'
if(detailData.selectDetail.canvasId){
detailData.isShowMark = true
await new Promise((resolve, reject) => {

View File

@@ -16,8 +16,16 @@
</div>
</div>
<div class="detailText">{{$t('DesignPrintOperation.Colorfromimage')}}</div>
<div class="uploadImage">
<upload @selectUplpadColor="selectUplpadColor"></upload>
<div class="uploadImage flex flex-align-center flex-justify-center">
<div class="upload-container" :class="{'hide': !showLibrary}">
<upload ref="uploadRef" @selectUplpadColor="selectUplpadColor" @selectFile="showLibrary = false" @deleteColor="showLibrary = true"></upload>
<div class="upload-text" v-show="showLibrary"> {{ $t('LibraryPage.Upload') }} </div>
</div>
<div class="upload-container " v-show="showLibrary">
<!-- <i class="fi fi-rr-followcollection"></i> -->
<SvgIcon name="CLibrary" class="svg-btn" size="20" @click="handleOpenLibrary" />
<div class="upload-text"> {{ $t('LibraryPage.library') }} </div>
</div>
</div>
<div class="detailText">{{$t('DesignPrintOperation.ColorCode')}}</div>
<div class="colorCode">
@@ -30,7 +38,14 @@
<!-- <div class="getTcxColorBtn" @click="getTcxColor">{{$t('DesignPrintOperation.ExtractColor')}}</div> -->
</div>
</div>
<SelectImages
ref="selectImages"
@select="handleImageSelect"
radio
full-data
:api="Https.httpUrls.queryLibraryPage"
isLibrary
/>
</template>
<script lang="ts">
import { defineComponent,computed,ref,watch,nextTick,toRefs, reactive, onMounted} from 'vue'
@@ -39,14 +54,16 @@ import { ExclamationCircleOutlined } from '@ant-design/icons-vue';
import { useStore } from "vuex";
import { Https } from "@/tool/https";
import { useI18n } from 'vue-i18n'
import { rgbaToHex,rgbToHsv } from "@/tool/util"
import { rgbaToHex,rgbToHsv,UrlToFile } from "@/tool/util"
import { message,Upload} from 'ant-design-vue';
import SelectImages from '@/component/common/SelectImages.vue'
import upload from './upload.vue'
import pallet from './pallet.vue'
export default defineComponent({
components:{
upload,pallet
upload,pallet,SelectImages
},
setup(props,{emit}) {
const store = useStore();
@@ -243,6 +260,30 @@ export default defineComponent({
});
})
}
const uploadRef = ref<any>(null)
const selectImages = ref<any>(null)
const showLibrary = ref(true)
const handleOpenLibrary = ()=>{
selectImages.value.init()
}
const handleImageSelect = (item:any)=>{
UrlToFile(item.url,item.name).then((file:any)=>{
// 构造符合 fileUploadChange 期望的数据结构
const fileData = {
file: {
originFileObj: file, // 将 File 对象放在 originFileObj 属性中
status: 'done'
}
}
uploadRef.value.fileUploadChange(fileData)
})
}
const handleShowListChange=(val:boolean)=>{
console.log('val',val)
showLibrary.value = !val
}
const selectUplpadColor = (item:any)=>{
colorData.selectColor = JSON.parse(JSON.stringify(item))
}
@@ -301,6 +342,14 @@ export default defineComponent({
selectUplpadColor,
setSelectColor,
getTcxColor,
handleOpenLibrary,
Https,
showLibrary,
selectImages,
uploadRef,
handleImageSelect,
handleShowListChange,
}
},
@@ -312,21 +361,46 @@ export default defineComponent({
})
</script>
<style lang="less" scoped>
.color{
.flex{
display: flex;
}
.flex-column{
flex-direction: column;
}
.flex-align-center{
align-items: center;
}
.flex-justify-center{
justify-content: center;
}
.flex-justify-between{
justify-content: space-between;
}
.flex-justify-around{
justify-content: space-around;
}
.color{
// width: 34rem;
width: 100%;
height: 100%;
// height: 100%;
display: flex;
flex-direction: column;
row-gap: 1rem;
overflow-y: auto;
> .detailText{
margin-bottom: .5rem;
}
> .pallet{
margin-bottom: 2rem;
}
// > .detailText{
// margin-bottom: .5rem;
// }
// > .pallet{
// margin-bottom: 2rem;
// }
> .colorBox{
margin-bottom: 1rem;
// margin-bottom: 1rem;
display: flex;
flex-wrap: wrap;
justify-content: space-between;
@@ -368,15 +442,44 @@ export default defineComponent({
}
> .uploadImage{
flex-shrink: 0;
margin-bottom: 3rem;
// margin-bottom: 3rem;
border: 1px dashed transparent;
background: linear-gradient(#fff, #fff) padding-box,repeating-linear-gradient(-45deg,#fff 0,#fff 0.3em, #000 0,#000 0.6em);
height: 10rem;
width: 100%;
border-radius: .5rem;
column-gap: 2rem;
.upload-container{
display: flex;
flex-direction: column;
justify-content: space-evenly;
align-items: center;
background-color: #EDEDED;
border-radius: 1rem;
width: 7rem;
height: 7rem;
// row-gap: 1rem;
&.hide{
width: initial;
height: initial;
border: none;
padding: 0;
flex: 1;
background-color: transparent;
padding-left: 1rem;
}
.svg-btn{
cursor: pointer;
width: initial;
height: initial;
}
:deep(.ant-upload){
background: transparent;
}
}
}
> .colorCode{
margin-bottom: 3rem;
// margin-bottom: 3rem;
> .generalModel_state{
> .generalModel_state_item{
width: 100%;

View File

@@ -8,7 +8,7 @@
</div>
</div>
<div class="palletBox">
<div v-show="palletShow" class="color_setting_block">
<div v-show="palletShow" class="color_setting_block" @click.stop>
<Chrome class="chrome_color" v-model="color_"></Chrome>
<div class="color_setting_operateSingle">
<div class="color_setting_btn" :class="{active:!color?.gradient?.gradientShow}">{{ $t('ColorboardUpload.Single') }}</div>
@@ -42,7 +42,7 @@
</template>
<script lang="ts">
import { defineComponent,computed,ref,watch,nextTick,onMounted,toRefs, reactive} from 'vue'
import { defineComponent,computed,ref,watch,nextTick,onMounted,onUnmounted,toRefs, reactive} from 'vue'
import { useStore } from "vuex";
import { useI18n } from 'vue-i18n'
import { message,Upload} from 'ant-design-vue';
@@ -273,7 +273,24 @@ export default defineComponent({
}else{
}
}
// 点击外部区域关闭颜色选择器
const handleClickOutside = (event: Event) => {
const target = event.target as HTMLElement;
const colorSettingBlock = document.querySelector('.color_setting_block');
const palletColo = document.querySelector('.palletColo');
// 如果点击的是 .palletColo 或 .color_setting_block 内部,则不关闭
if (palletData.palletShow && colorSettingBlock &&
!colorSettingBlock.contains(target) &&
!palletColo?.contains(target)) {
palletData.palletShow = false;
}
}
onMounted(()=>{
// 添加点击外部区域监听器
document.addEventListener('click', handleClickOutside);
nextTick().then(()=>{
const backIcon = document.createElement('div');
backIcon.classList.add('vc-sketch-color-wrap')
@@ -297,6 +314,11 @@ export default defineComponent({
})
})
})
onUnmounted(()=>{
// 清理事件监听器
document.removeEventListener('click', handleClickOutside);
})
return{
...toRefs(palletData),
...toRefs(getpalletListDom),

View File

@@ -44,7 +44,7 @@ import { rgbaToHex } from "@/tool/util"
export default defineComponent({
components:{
},
emits:['selectUplpadColor'],
emits:['selectUplpadColor','deleteColor','selectFile'],
setup(props,{emit}) {
const {t} = useI18n();
const store = useStore();
@@ -63,6 +63,8 @@ export default defineComponent({
})
const fileUploadChange = (data:any)=>{
emit('selectFile')
console.log('fileUploadChange',data)
let file:any = data.file
let fileData = file.originFileObj
var reader = new FileReader();
@@ -157,6 +159,7 @@ export default defineComponent({
const colorDeleteFile = ()=>{
colorUpload.uploadList[colorUpload.selectDetail.id] = []
colorUpload.colorList[colorUpload.selectDetail.id] = []
emit('deleteColor')
}
return{
...toRefs(colorUpload),
@@ -178,8 +181,8 @@ export default defineComponent({
<style lang="less" scoped>
.upload{
// width: 34rem;
width: 100%;
height: 100%;
// width: 100%;
// height: 100%;
display: flex;
align-items: center;
:deep(.ant-upload-picture-card-wrapper){
@@ -201,7 +204,7 @@ export default defineComponent({
}
.upload_tip_block{
i{
font-size: 4rem;
font-size: 2rem;
display: flex;
}
}

View File

@@ -122,10 +122,11 @@ export default defineComponent({
padding: 1rem;
text-align: center;
border-radius: .5rem;
// border: 1px dashed #202020;
border: 1px dashed transparent;
border: 1px solid #000;
// border: 1px dashed transparent;
border-radius: 1.5rem;
background: linear-gradient(#fff, #fff) padding-box,repeating-linear-gradient(-45deg,#fff 0,#fff 0.3em, #000 0,#000 0.6em);
margin-bottom: 3rem;
margin-bottom: 1.4rem;
display: flex;
flex-wrap: wrap;
overflow: hidden;

View File

@@ -70,10 +70,12 @@ export default defineComponent({
</script>
<style lang="less" scoped>
.detailLeft{
width: 34rem;
// width: 34rem;
// width: 100%;
height: 100%;
// height: 100%;
flex: 1;
display: flex;
overflow: hidden;
flex-direction: column;
}
</style>

View File

@@ -100,11 +100,12 @@ export default defineComponent({
padding: 1rem 0;
text-align: center;
border-radius: .5rem;
// border: 1px dashed #202020;
border: 1px dashed transparent;
border: 1px solid #000;
// border: 1px dashed transparent;
border-radius: 1.5rem;
position: relative;
background: linear-gradient(#fff, #fff) padding-box,repeating-linear-gradient(-45deg,#fff 0,#fff 0.3em, #000 0,#000 0.6em);
margin-bottom: 3rem;
margin-bottom: 1.4rem;
> img{
width: 100%;
height: 100%;

View File

@@ -107,9 +107,9 @@ export default defineComponent({
align-content: flex-start;
&::-webkit-scrollbar{display: none;}
> .content_img_item{
width: calc((50% - 1rem));
> .content_img_item_block{
width: calc((34rem - 2rem) / 2);
height: calc((34rem - 2rem) / 2);
aspect-ratio: 1/1;
position: relative;
margin-bottom: 2rem;
@@ -121,16 +121,6 @@ export default defineComponent({
}
}
}
> .material_content_list_loding{
width: 100%;
height: calc((34rem - 2rem) / 2);
}
> .upload_item{
width: calc((34rem - 2rem) / 2);
height: calc((34rem - 2rem) / 2);
align-items: center;
justify-content: center;
}
}
}

View File

@@ -6,7 +6,7 @@
v-model:value="designType"
:options="(designTypeList)"
@change="handleChange"
style="width:100%"
style="width:100%; font-size: 1.4rem;"
size="large"
:fieldNames="{ label: 'name', value: 'value' }"
>
@@ -23,7 +23,7 @@
v-model:value="mannequinData.system"
:options="systemList"
@change="handleChange"
style="width:100%"
style="width:100%; font-size: 1.4rem;"
size="large"
:fieldNames="{ label: 'name', value: 'value' }"
>
@@ -41,7 +41,7 @@
v-model:value="mannequinData.style"
:options="mannequinStyle"
@change="handleChange"
style="width:100%"
style="width:100%; font-size: 1.4rem;"
size="large"
:fieldNames="{ label: 'name', value: 'value' }"
>
@@ -58,7 +58,7 @@
v-model:value="mannequinData.ageGroup"
:options="ageGroupList"
@change="handleChange"
style="width:100%"
style="width:100%; font-size: 1.4rem;"
size="large"
:fieldNames="{ label: 'name', value: 'value' }"
>
@@ -75,7 +75,7 @@
v-model:value="mannequinData.sex"
:options="sexList"
@change="handleChange"
style="width:100%"
style="width:100%; font-size: 1.4rem;"
size="large"
:fieldNames="{ label: 'name', value: 'value' }"
>
@@ -333,6 +333,9 @@ export default defineComponent({
justify-content: center;
cursor: pointer;
}
> .search_input{
font-size: 1.4rem;
}
}
> .generalModel_state_item:last-child{
// margin-top: 2rem;
@@ -347,11 +350,11 @@ export default defineComponent({
margin-top: 1rem;
justify-content: space-between;
align-content: flex-start;
&::-webkit-scrollbar{display: none;}
// &::-webkit-scrollbar{display: none;}
> .content_img_item{
width: calc((50% - 1rem));
> .content_img_item_block{
width: calc((34rem - 2rem) / 2);
height: calc((34rem - 2rem) / 2);
aspect-ratio: 1/1;
position: relative;
margin-bottom: 2rem;
cursor: pointer;
@@ -364,7 +367,7 @@ export default defineComponent({
}
> .material_content_list_loding{
width: 100%;
height: calc((34rem - 2rem) / 2);
aspect-ratio: 1/1;
overflow: hidden;
> img{
width: 100%;

View File

@@ -1,6 +1,6 @@
<template>
<div class="selectList">
<div class="selectList_header">
<div class="selectList_header" :class="{'element': type == 'element'}">
<div class="switch_type_list" v-if="type != 'element'">
<div
@click.stop="openCurrent()"
@@ -161,10 +161,16 @@ export default defineComponent({
height: auto;
display: flex;
flex-direction: column;
border-radius: 1.5rem;
border: 1px solid #000;
padding: 1rem;
> .selectList_header{
margin-bottom: 2.5rem;
display: flex;
align-items: center;
&.element{
margin-bottom: 0rem;
}
> .switch_type_list{
display: flex;
> .switch_type_item:last-child{
@@ -173,8 +179,11 @@ export default defineComponent({
> .switch_type_item{
position: relative;
cursor: pointer;
margin-right: 6.5rem;
margin-right: 4rem;
font-size: 1.8rem;
> .detailText{
font-size: 1.6rem;
}
}
> .switch_type_item::before {
position: absolute;

View File

@@ -200,9 +200,9 @@ export default defineComponent({
align-content: flex-start;
&::-webkit-scrollbar{display: none;}
> .content_img_item{
width: calc((50% - 1rem));
> .content_img_item_block{
width: calc((34rem - 2rem) / 2);
height: calc((34rem - 2rem) / 2);
aspect-ratio: 1/1;
position: relative;
margin-bottom: 2rem;
@@ -216,11 +216,11 @@ export default defineComponent({
}
> .material_content_list_loding{
width: 100%;
height: calc((34rem - 2rem) / 2);
aspect-ratio: 1/1;
}
> .upload_item{
width: calc((34rem - 2rem) / 2);
height: calc((34rem - 2rem) / 2);
width: calc((50% - 1rem));
aspect-ratio: 1/1;
align-items: center;
justify-content: center;
}

View File

@@ -262,9 +262,10 @@ import sketchCategory from "@/component/HomePage/sketchCategory.vue";
align-content: flex-start;
&::-webkit-scrollbar{display: none;}
> .content_img_item{
width: calc((50% - 1rem));
> .content_img_item_block{
width: calc((34rem - 2rem) / 2);
height: calc((34rem - 2rem) / 2);
width: 100%;
aspect-ratio: 1/1;
position: relative;
margin-bottom: 2rem;
margin: 1rem;
@@ -279,11 +280,11 @@ import sketchCategory from "@/component/HomePage/sketchCategory.vue";
}
> .material_content_list_loding{
width: 100%;
height: calc((34rem - 2rem) / 2);
aspect-ratio: 1/1;
}
> .upload_item{
width: calc((34rem - 2rem) / 2);
height: calc((34rem - 2rem) / 2);
width: calc((50% - 1rem));
aspect-ratio: 1/1;
align-items: center;
justify-content: center;
.upload{

View File

@@ -145,9 +145,9 @@ export default defineComponent({
align-content: flex-start;
&::-webkit-scrollbar{display: none;}
> .content_img_item{
width: calc((50% - 1rem));
> .content_img_item_block{
width: calc((34rem - 2rem) / 2);
height: calc((34rem - 2rem) / 2);
aspect-ratio: 1/1;
position: relative;
margin-bottom: 2rem;
@@ -174,11 +174,11 @@ export default defineComponent({
}
> .material_content_list_loding{
width: 100%;
height: calc((34rem - 2rem) / 2);
aspect-ratio: 1/1;
}
> .upload_item{
width: calc((34rem - 2rem) / 2);
height: calc((34rem - 2rem) / 2);
width: calc((50% - 1rem));
aspect-ratio: 1/1;
align-items: center;
justify-content: center;
.upload{

View File

@@ -120,11 +120,11 @@ export default defineComponent({
height: 23.5rem;
padding: 1rem;
text-align: center;
border-radius: .5rem;
// border: 1px dashed #202020;
border: 1px dashed transparent;
border: 1px solid #000;
// border: 1px dashed transparent;
border-radius: 1.5rem;
background: linear-gradient(#fff, #fff) padding-box,repeating-linear-gradient(-45deg,#fff 0,#fff 0.3em, #000 0,#000 0.6em);
margin-bottom: 3rem;
margin-bottom: 1.4rem;
display: flex;
flex-wrap: wrap;
overflow: hidden;

View File

@@ -126,11 +126,12 @@ export default defineComponent({
padding: 1rem 0;
text-align: center;
border-radius: .5rem;
// border: 1px dashed #202020;
border: 1px dashed transparent;
border: 1px solid #000;
// border: 1px dashed transparent;
border-radius: 1.5rem;
position: relative;
background: linear-gradient(#fff, #fff) padding-box,repeating-linear-gradient(-45deg,#fff 0,#fff 0.3em, #000 0,#000 0.6em);
margin-bottom: 3rem;
margin-bottom: 1.4rem;
> img{
width: 100%;
height: 100%;

Some files were not shown because too many files have changed in this diff Show More