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 = 'https://aida.com.hk/test'
# VITE_APP_BASE_URL = 'http://18.167.251.121:10088' # VITE_APP_BASE_URL = 'http://18.167.251.121:10088'
# VITE_APP_BASE_URL = 'https://api.aida.com.hk' # VITE_APP_BASE_URL = 'https://api.aida.com.hk'

View File

@@ -1,12 +1,6 @@
<<<<<<< HEAD
VITE_USER_NODE_ENV = 'development' VITE_USER_NODE_ENV = 'development'
VITE_APP_BASE_URL = 'https://test.api.aida.com.hk' VITE_APP_BASE_URL = 'https://test.api.aida.com.hk'
# VITE_APP_BASE_URL = 'https://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://18.167.251.121:10086'
# VITE_APP_BASE_URL = 'http://192.168.1.9:5567' # VITE_APP_BASE_URL = 'http://192.168.1.9:5567'

1
.gitignore vendored
View File

@@ -23,3 +23,4 @@ dist.rar
*.sln *.sln
*.sw? *.sw?
.eslintrc-auto-import.json .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-face {
font-family: "iconfont"; /* Project id 4292253 */ font-family: "iconfont"; /* Project id 4292253 */
src: url('iconfont.woff2?t=1727415711578') format('woff2'), src: url('iconfont.woff2?t=1759888699816') format('woff2'),
url('iconfont.woff?t=1727415711578') format('woff'), url('iconfont.woff?t=1759888699816') format('woff'),
url('iconfont.ttf?t=1727415711578') format('truetype'); url('iconfont.ttf?t=1759888699816') format('truetype');
} }
.iconfont { .iconfont {
font-family: "iconfont" !important; font-family: "iconfont" !important;
font-size: 1.8rem; font-size: 1.6rem;
font-style: normal; font-style: normal;
-webkit-font-smoothing: antialiased; -webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale; -moz-osx-font-smoothing: grayscale;
} }
.icon-clothes:before {
content: "\e8d1";
}
.icon-caizhi:before { .icon-caizhi:before {
content: "\e647"; content: "\e647";
} }

View File

@@ -1,170 +1,219 @@
{ {
"id": "", "id": "4292253",
"name": "", "name": "aida",
"font_family": "iconfont", "font_family": "iconfont",
"css_prefix_text": "icon-", "css_prefix_text": "icon-",
"description": "", "description": "",
"glyphs": [ "glyphs": [
{ {
"icon_id": "124968799", "icon_id": "20183053",
"name": "外套_长款外套1@1x", "name": "clothes",
"font_class": "a-waitao_changkuanwaitao11x", "font_class": "clothes",
"unicode": "e66c", "unicode": "e8d1",
"unicode_decimal": 58988 "unicode_decimal": 59601
}, },
{ {
"icon_id": "125198319", "icon_id": "15739173",
"name": "撤销 返回 撤回 上一步", "name": "材质",
"font_class": "fanchehui", "font_class": "caizhi",
"unicode": "e626", "unicode": "e647",
"unicode_decimal": 58918 "unicode_decimal": 58951
}, },
{ {
"icon_id": "125198320", "icon_id": "35023469",
"name": "撤销 返回 撤回 上一步", "name": "IC-液化",
"font_class": "chehui", "font_class": "IC-yehua",
"unicode": "e609", "unicode": "e61b",
"unicode_decimal": 58889 "unicode_decimal": 58907
}, },
{ {
"icon_id": "125524062", "icon_id": "12096844",
"name": "语言", "name": "上一层",
"font_class": "yuyan", "font_class": "shangyiceng",
"unicode": "e85f", "unicode": "e751",
"unicode_decimal": 59487 "unicode_decimal": 59217
}, },
{ {
"icon_id": "126177191", "icon_id": "16531912",
"name": "标签", "name": "上一层",
"font_class": "biaoqian", "font_class": "shangyiceng1",
"unicode": "e603", "unicode": "e604",
"unicode_decimal": 58883 "unicode_decimal": 58884
}, },
{ {
"icon_id": "126459101", "icon_id": "24253227",
"name": "并集", "name": "下一层",
"font_class": "bingji", "font_class": "xiayiceng",
"unicode": "e620", "unicode": "e68a",
"unicode_decimal": 58912 "unicode_decimal": 59018
}, },
{ {
"icon_id": "126459102", "icon_id": "24253230",
"name": "并集", "name": "上一层",
"font_class": "bingji1", "font_class": "shangyiceng2",
"unicode": "e668", "unicode": "e68b",
"unicode_decimal": 58984 "unicode_decimal": 59019
}, },
{ {
"icon_id": "126901286", "icon_id": "3663275",
"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",
"name": "审批", "name": "审批",
"font_class": "shenpi", "font_class": "shenpi",
"unicode": "e6a1", "unicode": "e6a1",
"unicode_decimal": 59041 "unicode_decimal": 59041
}, },
{ {
"icon_id": "130751284", "icon_id": "7638976",
"name": "用户", "name": "用户",
"font_class": "yonghu", "font_class": "yonghu",
"unicode": "e617", "unicode": "e617",
"unicode_decimal": 58903 "unicode_decimal": 58903
}, },
{ {
"icon_id": "130751285", "icon_id": "9775414",
"name": "使用次数", "name": "使用次数",
"font_class": "usetime", "font_class": "usetime",
"unicode": "e601", "unicode": "e601",
"unicode_decimal": 58881 "unicode_decimal": 58881
}, },
{ {
"icon_id": "130751286", "icon_id": "16843615",
"name": "下拉", "name": "下拉",
"font_class": "xiala", "font_class": "xiala",
"unicode": "e634", "unicode": "e634",
"unicode_decimal": 58932 "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%; height: 100%;
margin: 0 auto; margin: 0 auto;
} }
.ant-dropdown-menu {
border-radius: 1rem;
}
.button_second { .button_second {
width: 14rem; width: 14rem;
text-align: center; text-align: center;
@@ -237,7 +240,7 @@ li {
color: #fff; color: #fff;
background-color: #000; background-color: #000;
text-align: center; text-align: center;
font-weight: 600; font-weight: 500;
border: 2px solid #000; border: 2px solid #000;
cursor: pointer; cursor: pointer;
box-sizing: border-box; box-sizing: border-box;
@@ -2106,7 +2109,7 @@ textarea:focus {
.generalMenu_printModel_upload .input_border .input_box_btnBox .upload_item .upload_file_item, .generalMenu_printModel_upload .input_border .input_box_btnBox .upload_item .upload_file_item,
.generate .input_border .input_box_btnBox .upload_item .upload_file_item { .generate .input_border .input_box_btnBox .upload_item .upload_file_item {
position: absolute; position: absolute;
left: 0; left: 1rem;
top: 50%; top: 50%;
transform: translateY(-50%); transform: translateY(-50%);
height: 4.7rem; height: 4.7rem;
@@ -2207,7 +2210,7 @@ textarea:focus {
cursor: pointer; cursor: pointer;
transition: all 0.3s; transition: all 0.3s;
position: relative; position: relative;
width: 4rem; width: 3rem;
display: flex; display: flex;
min-height: 3rem; min-height: 3rem;
justify-content: center; justify-content: center;

View File

@@ -63,6 +63,9 @@ input:focus{
height: 100%; height: 100%;
margin: 0 auto; margin: 0 auto;
} }
.ant-dropdown-menu{
border-radius: 1rem;
}
.button_second{ .button_second{
width: 14rem; width: 14rem;
text-align: center; text-align: center;
@@ -241,7 +244,7 @@ input:focus{
color: #fff; color: #fff;
background-color: #000; background-color: #000;
text-align: center; text-align: center;
font-weight: 600; font-weight: 500;
border: 2px solid #000; border: 2px solid #000;
cursor: pointer; cursor: pointer;
box-sizing: border-box; box-sizing: border-box;
@@ -2105,7 +2108,7 @@ textarea:focus{
width: 4.7rem; width: 4.7rem;
.upload_file_item{ .upload_file_item{
position: absolute; position: absolute;
left: 0; left: 1rem;
top: 50%; top: 50%;
transform: translateY(-50%); transform: translateY(-50%);
height: 4.7rem; height: 4.7rem;
@@ -2150,7 +2153,7 @@ textarea:focus{
cursor: pointer; cursor: pointer;
transition: all .3s; transition: all .3s;
position: relative; position: relative;
width: 4rem; width: 3rem;
align-items: center; align-items: center;
justify-content: 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,6 +111,13 @@ export default defineComponent({
.catch((res) => {accountHomeData.loadingShow = false}); .catch((res) => {accountHomeData.loadingShow = false});
} }
const ungroupWeiXinModel = ()=>{ const ungroupWeiXinModel = ()=>{
Modal.confirm({
title: t('frontPage.UnbindTip'),
okText: t('Yes'),
cancelText: t('No'),
mask:false,
centered:true,
onOk() {
Https.axiosGet(Https.httpUrls.unbindWeChat,).then((rv)=>{ Https.axiosGet(Https.httpUrls.unbindWeChat,).then((rv)=>{
message.success(t('frontPage.jsContent1')); message.success(t('frontPage.jsContent1'));
let value = { let value = {
@@ -122,7 +129,16 @@ export default defineComponent({
store.commit("upUserDetail", value) store.commit("upUserDetail", value)
}) })
} }
});
}
const ungroupGoogleModel = ()=>{ const ungroupGoogleModel = ()=>{
Modal.confirm({
title: t('frontPage.UnbindTip'),
okText: t('Yes'),
cancelText: t('No'),
mask:false,
centered:true,
onOk() {
Https.axiosGet(Https.httpUrls.unbindGoogle,).then((rv)=>{ Https.axiosGet(Https.httpUrls.unbindGoogle,).then((rv)=>{
let value = { let value = {
accountExtendList:{ accountExtendList:{
@@ -134,6 +150,8 @@ export default defineComponent({
message.success(t('frontPage.jsContent1')); message.success(t('frontPage.jsContent1'));
}) })
} }
});
}
const modifyEmail = ()=>{ const modifyEmail = ()=>{
bindPageDom.bindEmail.init('Modify') bindPageDom.bindEmail.init('Modify')

View File

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

View File

@@ -2,11 +2,10 @@
<div class="test_cli admin_page"> <div class="test_cli admin_page">
<div class="admin_table_search"> <div class="admin_table_search">
<div class="admin_state"> <div class="admin_state">
<div class="admin_state_item"> <div class="admin_state_item">
<span>{{ $t('admin.StartDate') }}:</span> <span>{{ $t("admin.StartDate") }}:</span>
<a-range-picker <a-range-picker
style="width:250px" style="width: 250px"
class="range_picker" class="range_picker"
v-model:value="rangePickerValue" v-model:value="rangePickerValue"
:placeholder="[ :placeholder="[
@@ -23,11 +22,20 @@
</a-range-picker> </a-range-picker>
</div> </div>
<div class="admin_state_item"> <div class="admin_state_item">
<span>{{ $t('admin.StartTime') }}:</span> <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" /> <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>
<div class="admin_state_item"> <div class="admin_state_item">
<span>{{ $t('admin.Email') }}:</span> <span>{{ $t("admin.Email") }}:</span>
<input <input
v-model="email" v-model="email"
:placeholder="$t('admin.enterEmail')" :placeholder="$t('admin.enterEmail')"
@@ -37,7 +45,7 @@
/> />
</div> </div>
<div class="admin_state_item"> <div class="admin_state_item">
<span>{{ $t('admin.UserName') }}:</span> <span>{{ $t("admin.UserName") }}:</span>
<a-select <a-select
v-model:value="ids" v-model:value="ids"
mode="multiple" mode="multiple"
@@ -49,9 +57,21 @@
@keydown.enter="gettrialList" @keydown.enter="gettrialList"
></a-select> ></a-select>
</div> </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>
<div class="admin_search"> <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>
</div> </div>
@@ -76,44 +96,43 @@
</div> </div>
</template> </template>
<script lang="ts"> <script lang="ts">
import { defineComponent, ref, createVNode, computed } from "vue"; import { defineComponent, ref, createVNode, computed } from "vue";
import { useStore } from "vuex"; import { useStore } from "vuex";
import { Https } from "@/tool/https"; import { Https } from "@/tool/https";
import { useI18n } from 'vue-i18n' import { useI18n } from "vue-i18n";
export default defineComponent({ export default defineComponent({
components: { components: {},
},
setup() { setup() {
const store:any = useStore() const store: any = useStore();
let rangePickerValue: any = ref([]); let rangePickerValue: any = ref([]);
let rangeTimeValue: any = ref([]); let rangeTimeValue: any = ref([]);
let renameData: any = ref({}); //修改名字选中的数据 let renameData: any = ref({}); //修改名字选中的数据
const {t} = useI18n() const { t } = useI18n();
const columns: any = computed(() => { const columns: any = computed(() => {
return [ return [
{ {
title: t('admin.Email'), title: t("admin.Email"),
align: "center", align: "center",
dataIndex: "userEmail", dataIndex: "userEmail",
key: "userEmail", key: "userEmail",
width:200, width: 200,
fixed: "left", fixed: "left",
}, },
{ {
title: t('admin.UserId'), title: t("admin.UserId"),
align: "center", align: "center",
ellipsis: true, ellipsis: true,
dataIndex: "accountId", dataIndex: "accountId",
key: "accountId", key: "accountId",
width:100, width: 100,
}, },
{ {
title: t('admin.UserName'), title: t("admin.UserName"),
align: "center", align: "center",
ellipsis: 200, ellipsis: 200,
dataIndex: "userName", dataIndex: "userName",
key: "userName", key: "userName",
width:100, width: 100,
// customRender: (record: any) => { // customRender: (record: any) => {
// let time = formatTime( // let time = formatTime(
// record.text / 1000, // record.text / 1000,
@@ -123,15 +142,15 @@ export default defineComponent({
// }, // },
}, },
{ {
title: t('admin.Frequency'), title: t("admin.Frequency"),
align: "center", align: "center",
ellipsis: true, ellipsis: true,
dataIndex: "designTimes", dataIndex: "designTimes",
key: "designTimes", key: "designTimes",
width:100, width: 100,
}, },
{ {
title: t('admin.CreateTime'), title: t("admin.CreateTime"),
align: "center", align: "center",
ellipsis: true, ellipsis: true,
// width: 150, // width: 150,
@@ -140,10 +159,10 @@ export default defineComponent({
// resizable: true, // resizable: true,
dataIndex: "createTime", dataIndex: "createTime",
key: "createTime", key: "createTime",
width:200, width: 200,
}, },
{ {
title: t('admin.Credits'), title: t("admin.Credits"),
align: "center", align: "center",
ellipsis: true, ellipsis: true,
// width: 150, // width: 150,
@@ -152,18 +171,19 @@ export default defineComponent({
// resizable: true, // resizable: true,
dataIndex: "credits", dataIndex: "credits",
key: "credits", key: "credits",
width:100, width: 100,
}, },
]; ];
}); });
let allUserList: any = computed(()=>{ let allUserList: any = computed(() => {
return store.state.adminPage.allUserList return store.state.adminPage.allUserList;
}) });
let ids = ref([]) let ids = ref([]);
let email = ref('') let email = ref("");
let dataList: any = ref([]); let dataList: any = ref([]);
let status: any = ref(0); let status: any = ref(0);
let organizationName: any = ref("");
let filterOption = (input: any, option: any) => { let filterOption = (input: any, option: any) => {
// 使用 option.label 进行搜索 // 使用 option.label 进行搜索
return option.label.toLowerCase().indexOf(input.toLowerCase()) >= 0; return option.label.toLowerCase().indexOf(input.toLowerCase()) >= 0;
@@ -179,6 +199,7 @@ export default defineComponent({
renameData, renameData,
status, status,
filterOption, filterOption,
organizationName,
}; };
}, },
data() { data() {
@@ -187,7 +208,7 @@ export default defineComponent({
pageSize: 10, pageSize: 10,
total: 0, total: 0,
historyTableHeight: 0, historyTableHeight: 0,
handleResizeColumn: (w:any, col:any) => { handleResizeColumn: (w: any, col: any) => {
col.width = w; col.width = w;
}, },
}; };
@@ -215,29 +236,32 @@ export default defineComponent({
gettrialList() { gettrialList() {
let startTime: any = this.rangeTimeValue?.[0] let startTime: any = this.rangeTimeValue?.[0]
? this.rangeTimeValue[0] ? this.rangeTimeValue[0]
: '00:00:00'; : "00:00:00";
let endTime: any = this.rangeTimeValue?.[1] let endTime: any = this.rangeTimeValue?.[1]
? this.rangeTimeValue[1] ? this.rangeTimeValue[1]
: '23:59:59'; : "23:59:59";
let startDate: any = this.rangePickerValue?.[0] let startDate: any = this.rangePickerValue?.[0]
? this.rangePickerValue[0]+' '+startTime ? this.rangePickerValue[0] + " " + startTime
: ""; : "";
let endDate: any = this.rangePickerValue?.[1] let endDate: any = this.rangePickerValue?.[1]
? this.rangePickerValue[1]+' '+endTime ? this.rangePickerValue[1] + " " + endTime
: ""; : "";
let ids = this.ids.join(',') let ids = this.ids.join(",");
let data = { let data = {
endTime:endDate, endTime: endDate,
startTime:startDate, startTime: startDate,
ids:ids, ids: ids,
email:this.email.trim(), email: this.email.trim(),
} organizationName: this.organizationName,
Https.axiosGet(Https.httpUrls.getDesignStatistic,{params:data}).then((rv: any) => { };
Https.axiosGet(Https.httpUrls.getDesignStatistic, {
params: data,
}).then((rv: any) => {
if (rv) { if (rv) {
this.dataList = rv this.dataList = rv;
// this.workspaceItem.position = this.singleTypeList[0].label // this.workspaceItem.position = this.singleTypeList[0].label
} }
}) });
}, },
//删除分组 //删除分组
@@ -264,14 +288,12 @@ export default defineComponent({
// }, // },
// }); // });
// }, // },
}, },
}); });
</script> </script>
<style lang="less" scoped> <style lang="less" scoped>
.admin_page .admin_table_search .admin_state { .admin_page .admin_table_search .admin_state {
display: flex; display: flex;
flex-wrap: wrap; flex-wrap: wrap;
} }
</style> </style>

View File

@@ -2,11 +2,10 @@
<div class="test_cli admin_page"> <div class="test_cli admin_page">
<div class="admin_table_search"> <div class="admin_table_search">
<div class="admin_state"> <div class="admin_state">
<div class="admin_state_item"> <div class="admin_state_item">
<span>Start Date:</span> <span>Start Date:</span>
<a-range-picker <a-range-picker
style="width:250px" style="width: 250px"
class="range_picker" class="range_picker"
v-model:value="rangePickerValue" v-model:value="rangePickerValue"
:placeholder="[ :placeholder="[
@@ -24,7 +23,12 @@
</div> </div>
<div class="admin_state_item"> <div class="admin_state_item">
<span>Start Time:</span> <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>
<div class="admin_state_item"> <div class="admin_state_item">
<span>Email:</span> <span>Email:</span>
@@ -49,9 +53,21 @@
@keydown.enter="gettrialList" @keydown.enter="gettrialList"
></a-select> ></a-select>
</div> </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>
<div class="admin_search"> <div class="admin_search">
<div class="admin_search_item" @click="searchHistoryList">Search</div> <div class="admin_search_item" @click="searchHistoryList">
Search
</div>
</div> </div>
</div> </div>
@@ -76,42 +92,42 @@
</div> </div>
</template> </template>
<script lang="ts"> <script lang="ts">
import { defineComponent, ref, createVNode, computed } from "vue"; import { defineComponent, ref, createVNode, computed } from "vue";
import { useStore } from "vuex"; import { useStore } from "vuex";
import { Https } from "@/tool/https"; import { Https } from "@/tool/https";
export default defineComponent({ export default defineComponent({
components: { components: {},
},
setup() { setup() {
const store:any = useStore() const store: any = useStore();
let rangePickerValue: any = ref([]); let rangePickerValue: any = ref([]);
let rangeTimeValue: any = ref([]); let rangeTimeValue: any = ref([]);
let renameData: any = ref({}); //修改名字选中的数据 let renameData: any = ref({}); //修改名字选中的数据
let organizationName: any = ref("");
const columns: any = computed(() => { const columns: any = computed(() => {
return [ return [
{ {
title: 'Email', title: "Email",
align: "center", align: "center",
dataIndex: "userEmail", dataIndex: "userEmail",
key: "userEmail", key: "userEmail",
width:200, width: 200,
fixed: "left", fixed: "left",
}, },
{ {
title: 'User Id', title: "User Id",
align: "center", align: "center",
ellipsis: true, ellipsis: true,
dataIndex: "accountId", dataIndex: "accountId",
key: "accountId", key: "accountId",
width:100, width: 100,
}, },
{ {
title: 'User Name', title: "User Name",
align: "center", align: "center",
ellipsis: 200, ellipsis: 200,
dataIndex: "userName", dataIndex: "userName",
key: "userName", key: "userName",
width:100, width: 100,
// customRender: (record: any) => { // customRender: (record: any) => {
// let time = formatTime( // let time = formatTime(
// record.text / 1000, // record.text / 1000,
@@ -121,72 +137,72 @@ export default defineComponent({
// }, // },
}, },
{ {
title: 'isTrial', title: "isTrial",
align: "center", align: "center",
ellipsis: true, ellipsis: true,
dataIndex: "isTrial", dataIndex: "isTrial",
key: "isTrial", key: "isTrial",
width:100, width: 100,
customRender: (record: any) => { customRender: (record: any) => {
let str let str;
if(record.value == 1){ if (record.value == 1) {
str ='Yes' str = "Yes";
}else{ } else {
str ='No' str = "No";
} }
return str; return str;
}, },
}, },
{ {
title: 'Frequency', title: "Frequency",
align: "center", align: "center",
ellipsis: true, ellipsis: true,
dataIndex: "designTimes", dataIndex: "designTimes",
key: "designTimes", key: "designTimes",
width:100, width: 100,
}, },
{ {
title: 'Country', title: "Country",
align: "center", align: "center",
ellipsis: true, ellipsis: true,
dataIndex: "country", dataIndex: "country",
key: "country", key: "country",
width:200, width: 200,
}, },
{ {
title: 'Title', title: "Title",
align: "center", align: "center",
ellipsis: true, ellipsis: true,
dataIndex: "title", dataIndex: "title",
key: "title", key: "title",
width:100, width: 100,
}, },
{ {
title: 'Surname', title: "Surname",
align: "center", align: "center",
ellipsis: true, ellipsis: true,
dataIndex: "surname", dataIndex: "surname",
key: "surname", key: "surname",
width:150, width: 150,
}, },
{ {
title: 'Given Name', title: "Given Name",
align: "center", align: "center",
ellipsis: true, ellipsis: true,
dataIndex: "givenName", dataIndex: "givenName",
key: "givenName", key: "givenName",
width:100, width: 100,
}, },
{ {
title: 'Create Time', title: "Create Time",
align: "center", align: "center",
ellipsis: true, ellipsis: true,
dataIndex: "createTime", dataIndex: "createTime",
key: "createTime", key: "createTime",
width:200, width: 200,
}, },
{ {
title: 'Credits', title: "Credits",
align: "center", align: "center",
ellipsis: true, ellipsis: true,
// width: 150, // width: 150,
@@ -195,10 +211,10 @@ export default defineComponent({
// resizable: true, // resizable: true,
dataIndex: "credits", dataIndex: "credits",
key: "credits", key: "credits",
width:100, width: 100,
}, },
{ {
title: 'Occupation', title: "Occupation",
align: "center", align: "center",
ellipsis: true, ellipsis: true,
// width: 150, // width: 150,
@@ -207,26 +223,26 @@ export default defineComponent({
// resizable: true, // resizable: true,
dataIndex: "occupation", dataIndex: "occupation",
key: "occupation", key: "occupation",
width:100, width: 100,
}, },
{ {
title: 'Trial Order Id', title: "Trial Order Id",
align: "center", align: "center",
ellipsis: true, ellipsis: true,
// width: 150, // width: 150,
// resizable: true, // resizable: true,
dataIndex: "trialOrderId", dataIndex: "trialOrderId",
key: "trialOrderId", key: "trialOrderId",
width:100, width: 100,
}, },
]; ];
}); });
let allUserList: any = computed(()=>{ let allUserList: any = computed(() => {
return store.state.adminPage.allUserList return store.state.adminPage.allUserList;
}) });
let ids = ref([]) let ids = ref([]);
let email = ref('') let email = ref("");
let dataList: any = ref([]); let dataList: any = ref([]);
let status: any = ref(0); let status: any = ref(0);
let filterOption = (input: any, option: any) => { let filterOption = (input: any, option: any) => {
@@ -235,6 +251,7 @@ export default defineComponent({
}; };
return { return {
rangePickerValue, rangePickerValue,
organizationName,
rangeTimeValue, rangeTimeValue,
columns, columns,
dataList, dataList,
@@ -252,7 +269,7 @@ export default defineComponent({
pageSize: 10, pageSize: 10,
total: 0, total: 0,
historyTableHeight: 0, historyTableHeight: 0,
handleResizeColumn: (w:any, col:any) => { handleResizeColumn: (w: any, col: any) => {
col.width = w; col.width = w;
}, },
}; };
@@ -280,29 +297,32 @@ export default defineComponent({
gettrialList() { gettrialList() {
let startTime: any = this.rangeTimeValue?.[0] let startTime: any = this.rangeTimeValue?.[0]
? this.rangeTimeValue?.[0] ? this.rangeTimeValue?.[0]
: '00:00:00'; : "00:00:00";
let endTime: any = this.rangeTimeValue[1] let endTime: any = this.rangeTimeValue[1]
? this.rangeTimeValue[1] ? this.rangeTimeValue[1]
: '23:59:59'; : "23:59:59";
let startDate: any = this.rangePickerValue[0] let startDate: any = this.rangePickerValue[0]
? this.rangePickerValue[0]+' '+startTime ? this.rangePickerValue[0] + " " + startTime
: ""; : "";
let endDate: any = this.rangePickerValue[1] let endDate: any = this.rangePickerValue[1]
? this.rangePickerValue[1]+' '+endTime ? this.rangePickerValue[1] + " " + endTime
: ""; : "";
let ids = this.ids.join(',') let ids = this.ids.join(",");
let data = { let data = {
endTime:endDate, endTime: endDate,
startTime:startDate, startTime: startDate,
ids:ids, ids: ids,
email:this.email.trim(), email: this.email.trim(),
} organizationName: this.organizationName,
Https.axiosGet(Https.httpUrls.getDesignStatistic,{params:data}).then((rv: any) => { };
Https.axiosGet(Https.httpUrls.getDesignStatistic, {
params: data,
}).then((rv: any) => {
if (rv) { if (rv) {
this.dataList = rv this.dataList = rv;
// this.workspaceItem.position = this.singleTypeList[0].label // this.workspaceItem.position = this.singleTypeList[0].label
} }
}) });
}, },
//删除分组 //删除分组
@@ -329,14 +349,12 @@ export default defineComponent({
// }, // },
// }); // });
// }, // },
}, },
}); });
</script> </script>
<style lang="less" scoped> <style lang="less" scoped>
.admin_page .admin_table_search .admin_state { .admin_page .admin_table_search .admin_state {
display: flex; display: flex;
flex-wrap: wrap; flex-wrap: wrap;
} }
</style> </style>

View File

@@ -1,7 +1,26 @@
<template> <template>
<div class="admin_page"> <div class="admin_page">
<div class="admin_table_search" > <div class="admin_table_search">
<div class="admin_state"> <div class="admin_state">
<div class="admin_state_item">
<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"> <div class="admin_state_item">
<span>Status:</span> <span>Status:</span>
<a-select <a-select
@@ -11,10 +30,25 @@
optionFilterProp="label" optionFilterProp="label"
:options="statusList" :options="statusList"
placeholder="Please select" placeholder="Please select"
@change="changeStatus"
allowClear allowClear
show-search show-search
></a-select> ></a-select>
</div> </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>
<div class="admin_search"> <div class="admin_search">
<div class="admin_search_item" @click="searchHistoryList"> <div class="admin_search_item" @click="searchHistoryList">
@@ -33,7 +67,7 @@
:data-source="dataList" :data-source="dataList"
:scroll="{ y: historyTableHeight }" :scroll="{ y: historyTableHeight }"
@change="changePage" @change="changePage"
:showSorterTooltip='false' :showSorterTooltip="false"
:pagination="{ :pagination="{
showSizeChanger: true, showSizeChanger: true,
current: currentPage, current: currentPage,
@@ -49,7 +83,7 @@
</div> </div>
</template> </template>
<script lang="ts"> <script lang="ts">
import { import {
defineComponent, defineComponent,
ref, ref,
createVNode, createVNode,
@@ -57,24 +91,23 @@ import {
reactive, reactive,
toRefs, toRefs,
onMounted, onMounted,
} from "vue"; } from "vue";
import { formatTime } from "@/tool/util"; import { formatTime } from "@/tool/util";
import { useStore } from "vuex"; import { useStore } from "vuex";
import { Https } from "@/tool/https"; import { Https } from "@/tool/https";
import {getCookie,clonAllCookie} from '@/tool/cookie' import { getCookie, clonAllCookie } from "@/tool/cookie";
import add from './add.vue' import add from "./add.vue";
export default defineComponent({ export default defineComponent({
components: {add}, components: { add },
setup() { setup() {
const store:any = useStore() const store: any = useStore();
let filter: any = reactive({ let filter: any = reactive({
dataList: [], dataList: [],
tableLoading: false, tableLoading: false,
countryList: computed(()=>{ countryList: computed(() => {
return store.state.adminPage.country return store.state.adminPage.country;
}), }),
add:null as any, add: null as any,
status:'',
}); });
let filterData: any = reactive({ let filterData: any = reactive({
currentPage: 1, currentPage: 1,
@@ -82,24 +115,26 @@ export default defineComponent({
total: 0, total: 0,
country: "", country: "",
status: "", status: "",
type: "", school: "",
rangePickerValue: [],
}); });
let selectList=reactive({ let selectList = reactive({
statusList:[ statusList: [
{ {
label: "all", label: "all",
value: "", value: "",
}, },
{ {
label:'Enterprise', label: "Enterprise",
value:'Enterprise', value: "Enterprise",
}, },
{ {
label:'Education', label: "Education",
value:'Education', value: "Education",
}, },
], ],
}) schoolList: [],
});
let renameData: any = ref({}); //修改名字选中的数据 let renameData: any = ref({}); //修改名字选中的数据
const columns: any = computed(() => { const columns: any = computed(() => {
return [ return [
@@ -108,42 +143,62 @@ export default defineComponent({
align: "center", align: "center",
dataIndex: "id", dataIndex: "id",
key: "id", key: "id",
width:150, width: 150,
ellipsis:true ellipsis: true,
}, },
{ {
title: "Name", title: "Name",
align: "center", align: "center",
dataIndex: "name", dataIndex: "name",
key: "name", key: "name",
width:150, width: 150,
ellipsis:true ellipsis: true,
}, },
{ {
title: "Create Time", title: "Create Time",
align: "center", align: "center",
dataIndex: "createTime", dataIndex: "createTime",
key: "createTime", key: "createTime",
width:150, width: 150,
ellipsis:true ellipsis: true,
}, },
{ {
title: "Type", title: "Type",
align: "center", align: "center",
dataIndex: "type", dataIndex: "type",
key: "type", key: "type",
width:150, width: 150,
ellipsis:true, ellipsis: true,
}, },
]; ];
}); });
//改变页码 //改变页码
let changePage = (e: any, filters:any, sorter:any) => { let changePage = (e: any, filters: any, sorter: any) => {
filterData.currentPage = e.current; filterData.currentPage = e.current;
filterData.pageSize = e.pageSize; filterData.pageSize = e.pageSize;
gettrialList(); 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 = () => { let searchHistoryList = () => {
filterData.currentPage = 1; filterData.currentPage = 1;
@@ -152,22 +207,42 @@ export default defineComponent({
//获取列表 //获取列表
let gettrialList = () => { let gettrialList = () => {
filter.tableLoading = true; filter.tableLoading = true;
Https.axiosGet(Https.httpUrls.queryOrganization, {params:{type:'Enterprise'}}).then( 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) => { (rv: any) => {
if (rv) { if (rv) {
console.log(rv) console.log(rv);
filter.dataList = rv // filter.dataList = rv;
// filter.dataList = rv.content; filter.dataList = rv.records;
// filterData.total = rv.total; filterData.total = rv.total;
filter.tableLoading = false; filter.tableLoading = false;
// this.workspaceItem.position = this.singleTypeList[0].label // this.workspaceItem.position = this.singleTypeList[0].label
} }
} }
); );
}; };
let addhHistoryList = () => { let addhHistoryList = () => {
filter.add.init('Add','') filter.add.init("Add", "");
};
const handleFocus = () => {
if (selectList.schoolList.length == 0) {
filterOption("");
}
};
const changeStatus = () => {
filterData.school = "";
selectList.schoolList = [];
}; };
onMounted(() => { onMounted(() => {
gettrialList(); gettrialList();
@@ -182,6 +257,8 @@ export default defineComponent({
searchHistoryList, searchHistoryList,
gettrialList, gettrialList,
addhHistoryList, addhHistoryList,
handleFocus,
changeStatus,
}; };
}, },
data() { data() {
@@ -197,32 +274,32 @@ export default defineComponent({
this.historyTableHeight = historyTable.clientHeight - 200; this.historyTableHeight = historyTable.clientHeight - 200;
}, },
methods: {}, methods: {},
}); });
</script> </script>
<style lang="less" scoped> <style lang="less" scoped>
.admin_page .admin_table_search .admin_state { .admin_page .admin_table_search .admin_state {
display: flex; display: flex;
flex-wrap: wrap; flex-wrap: wrap;
} }
:deep(.operate_list){ :deep(.operate_list) {
.fi{ .fi {
font-size: 2rem; font-size: 2rem;
margin-right: 1rem; margin-right: 1rem;
} }
.success{ .success {
.fi-ss-check-circle{ .fi-ss-check-circle {
color: #3ab45c; color: #3ab45c;
} }
} }
.pending{ .pending {
.fi-ss-check-circle{ .fi-ss-check-circle {
color: #ffc628; color: #ffc628;
} }
} }
.fail{ .fail {
.fi-ss-check-circle{ .fi-ss-check-circle {
color: #ff0000; color: #ff0000;
} }
} }
} }
</style> </style>

View File

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

View File

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

View File

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

View File

@@ -2569,7 +2569,6 @@ export class CreateImageLayerCommand extends Command {
this.fabricImage = options.fabricImage; this.fabricImage = options.fabricImage;
this.toolManager = options.toolManager; this.toolManager = options.toolManager;
this.layerName = options.layerName || null; this.layerName = options.layerName || null;
this.imageId = generateId("image_"); this.imageId = generateId("image_");
// 存储执行过程中的结果 // 存储执行过程中的结果
@@ -2617,6 +2616,18 @@ export class CreateImageLayerCommand extends Command {
this.commands.push(createLayerCmd); this.commands.push(createLayerCmd);
this.executedCommands.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. 添加图片对象到图层命令 // 2. 添加图片对象到图层命令
const addObjectCmd = new AddObjectToLayerCommand({ const addObjectCmd = new AddObjectToLayerCommand({
canvas: this.layerManager.canvas, canvas: this.layerManager.canvas,
@@ -4197,6 +4208,7 @@ export class RemoveChildLayerCommand extends Command {
this.isActiveLayer = this.layerId === this.activeLayerId.value; this.isActiveLayer = this.layerId === this.activeLayerId.value;
this.originalObjects = this.canvas.getObjects().filter((obj) => { this.originalObjects = this.canvas.getObjects().filter((obj) => {
obj.parentId = this.parentId;
return obj.layerId === this.layerId; return obj.layerId === this.layerId;
}); });
} }

View File

@@ -1,6 +1,8 @@
import { generateId, optimizeCanvasRendering } from "../utils/helper"; import { generateId, optimizeCanvasRendering } from "../utils/helper";
import { createLayer, LayerType, OperationType } from "../utils/layerHelper"; import { createLayer, LayerType, OperationType } from "../utils/layerHelper";
import { Command } from "./Command"; 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 = { this.defaultOptions = {
text: "双击编辑文本", text: t('Canvas.DoubleClickText'),
fontFamily: "Arial", fontFamily: "Arial",
fontSize: 24, fontSize: 24,
fontWeight: "normal", fontWeight: "normal",

View File

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

View File

@@ -14,7 +14,8 @@
@click="setBrushTypeWithCommand(brush.id)" @click="setBrushTypeWithCommand(brush.id)"
:class="['brush-type-item', { active: brushStore.state.type === 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> <span class="brush-name">{{ brush.name }}</span>
</div> </div>
</div> </div>
@@ -189,7 +190,7 @@
</div> </div>
<div class="uploaded-textures-section"> <div class="uploaded-textures-section">
<div class="uploaded-textures-divider"> <div class="uploaded-textures-divider">
<span>{{ $t("上传的纹理") }}</span> <span>{{ $t("Canvas.UploadedTexture") }}</span>
</div> </div>
</div> </div>
<div class="texture-grid"> <div class="texture-grid">
@@ -200,7 +201,7 @@
<div class="upload-icon"> <div class="upload-icon">
<span>+</span> <span>+</span>
</div> </div>
<span class="texture-label">{{ $t("上传纹理") }}</span> <span class="texture-label">{{ $t("Canvas.UploadTexture") }}</span>
</div> </div>
<div <div
v-for="textureId in brushStore.state.uploadedTextures" v-for="textureId in brushStore.state.uploadedTextures"
@@ -218,7 +219,7 @@
<div <div
class="texture-remove-btn" class="texture-remove-btn"
@click.stop="removeUploadedTexture(textureId)" @click.stop="removeUploadedTexture(textureId)"
:title="$t('删除纹理')" :title="$t('Canvas.DeleteTexture')"
> >
<span>×</span> <span>×</span>
</div> </div>
@@ -534,6 +535,9 @@ import {
TextureUploadCommand, TextureUploadCommand,
} from "../commands/BrushCommands"; } from "../commands/BrushCommands";
import { debounce } from "lodash-es"; import { debounce } from "lodash-es";
import { useI18n } from "vue-i18n";
const { t } = useI18n();
// 从工具管理器获取可用笔刷类型 // 从工具管理器获取可用笔刷类型
const toolManager = inject("toolManager"); const toolManager = inject("toolManager");
@@ -872,7 +876,7 @@ function applyPresetWithCommand(presetIndex) {
// 保存当前设置为预设 // 保存当前设置为预设
function saveCurrentAsPreset() { 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) { if (name) {
const presetIndex = BrushStore.saveCurrentAsPreset(name); const presetIndex = BrushStore.saveCurrentAsPreset(name);
// 应用新创建的预设(可选) // 应用新创建的预设(可选)
@@ -886,6 +890,7 @@ onMounted(() => {
const availableBrushes = toolManager.brushManager const availableBrushes = toolManager.brushManager
.getBrushTypes() .getBrushTypes()
?.filter((brush) => brush.id !== "eraser"); ?.filter((brush) => brush.id !== "eraser");
console.log(availableBrushes)
BrushStore.setAvailableBrushes(availableBrushes); BrushStore.setAvailableBrushes(availableBrushes);
} }
}); });
@@ -1178,6 +1183,8 @@ const brushStore = BrushStore;
margin-bottom: 8px; margin-bottom: 8px;
border-radius: 4px; border-radius: 4px;
background-color: rgba(0, 0, 0, 0.02); 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, action: actionDisplay,
windows: shortcut.key.replace(/cmdOrCtrl\+/g, "Ctrl+"), windows: shortcut.key.replace(/cmdOrCtrl\+/g, "Ctrl+"),
mac: shortcut.key.replace(/cmdOrCtrl\+/g, "⌘+"), mac: shortcut.key.replace(/cmdOrCtrl\+/g, "⌘+"),
touch: shortcut.touch || "触控界面点击对应工具", touch: shortcut.touch || t('Canvas.touchTools'),
displayKey: shortcut.displayKey, displayKey: shortcut.displayKey,
}); });
} }
@@ -326,8 +326,8 @@ function getShortcutsByCategory(category) {
<span v-else-if="platform.isIPad">iPad</span> <span v-else-if="platform.isIPad">iPad</span>
<span v-else-if="platform.isIOS">iOS</span> <span v-else-if="platform.isIOS">iOS</span>
<span v-else-if="platform.isAndroid">Android</span> <span v-else-if="platform.isAndroid">Android</span>
<span v-else>其他</span> <span v-else>{{ $t('Canvas.other') }}</span>
<span v-if="platform.isTouchDevice"> (触控设备)</span> <span v-if="platform.isTouchDevice"> ({{ $t('Canvas.touchDevice') }})</span>
</div> </div>
<div class="shortcuts-category"> <div class="shortcuts-category">
@@ -421,14 +421,14 @@ function getShortcutsByCategory(category) {
</div> --> </div> -->
<div class="touch-tips" v-if="platform.isTouchDevice"> <div class="touch-tips" v-if="platform.isTouchDevice">
<h3>触控设备提示</h3> <h3>{{ $t('Canvas.touchDevicePrompts') }}</h3>
<ul> <ul>
<li>长按图层面板可访问更多选项</li> <li>{{ $t('Canvas.touchDevicePrompts_1') }}</li>
<li>双击元素可快速进入编辑模式</li> <li>{{ $t('Canvas.touchDevicePrompts_2') }}</li>
<li>双指拖动可平移画布</li> <li>{{ $t('Canvas.touchDevicePrompts_3') }}</li>
<li>双指捏合可缩放画布</li> <li>{{ $t('Canvas.touchDevicePrompts_4') }}</li>
<li>双指连按可显示元素变换控制点</li> <li>{{ $t('Canvas.touchDevicePrompts_5') }}</li>
<li>三指左右滑动可进行撤销/重做操作</li> <li>{{ $t('Canvas.touchDevicePrompts_6') }}</li>
</ul> </ul>
</div> </div>
</Skeleton> </Skeleton>

View File

@@ -258,6 +258,23 @@ const setClosePanel = ()=>{
closePanel.value = !closePanel.value 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 的实现方式 // 监听当前工具变化 - 参考 SelectionPanel 的实现方式
watch( watch(
() => props.activeTool, () => props.activeTool,
@@ -273,6 +290,7 @@ watch(
// 检查是否有可液化的对象 // 检查是否有可液化的对象
checkAndShowPanel(); checkAndShowPanel();
} }
setBrushIndicatorSize(size.value)
} else { } else {
visible.value = false; // 切换到其他工具时隐藏面板 visible.value = false; // 切换到其他工具时隐藏面板
// 切换到其他工具,隐藏液化面板 // 切换到其他工具,隐藏液化面板
@@ -444,16 +462,16 @@ function showPanel(event) {
if (detail.layerStatus && detail.layerStatus.message) { if (detail.layerStatus && detail.layerStatus.message) {
console.log("液化操作提示:", detail.layerStatus.message); console.log("液化操作提示:", detail.layerStatus.message);
Modal.error({ Modal.error({
title: "错误提示", title: t('Canvas.ErrorMessage'),
content: detail.layerStatus.message, content: detail.layerStatus.message,
okText: "确定", okText: t('Canvas.confirm'),
centered: true, centered: true,
}); });
} else { } else {
Modal.error({ Modal.error({
title: "错误提示", title: t('Canvas.ErrorMessage'),
content: "未选择有效图像或图层不适合液化操作", content: t('Canvas.LiquidationError'),
okText: "确定", okText: t('Canvas.confirm'),
centered: true, centered: true,
}); });
console.log("未选择有效图像或图层不适合液化操作"); console.log("未选择有效图像或图层不适合液化操作");
@@ -1641,15 +1659,17 @@ function stopPressTimer() {
transform: rotate(90deg); transform: rotate(90deg);
} }
} }
} }
> .btn{ > .btn{
width: 100%; width: 100%;
text-align: center; text-align: center;
cursor: pointer; cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
> i{ > i{
font-size: 1.4rem; font-size: 1.4rem;
display: block;
transform: rotate(270deg); transform: rotate(270deg);
} }
} }

View File

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

View File

@@ -16,8 +16,9 @@ const emit = defineEmits([
"zoom-out", "zoom-out",
"toggle-red-green-mode", "toggle-red-green-mode",
"undo-redo-status-changed", "undo-redo-status-changed",
"trigger-library"
]); ]);
const {t} = useI18n() const {t,locale} = useI18n()
const props = defineProps({ const props = defineProps({
activeTool: String, activeTool: String,
minimapEnabled: { minimapEnabled: {
@@ -151,6 +152,13 @@ const normalToolsList = ref([
icon: { name: "CUpload", size: "26" }, icon: { name: "CUpload", size: "26" },
class: "upload-btn", class: "upload-btn",
}, },
{
id: "library",
title: t("LibraryPage.library"),
action: triggerLibrary,
icon: { name: "CLibrary", size: "26" },
class: "library-btn",
},
{ {
id: "addText", id: "addText",
title: t("Canvas.AddText"), title: t("Canvas.AddText"),
@@ -158,6 +166,17 @@ const normalToolsList = ref([
icon: { name: "CFont", size: "20" }, icon: { name: "CFont", size: "20" },
class: "text-btn", 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"); emit("trigger-image-upload");
} }
function triggerLibrary() {
emit("trigger-library");
}
function addText() { function addText() {
emit("add-text"); 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() { function undo() {
if (!canUndo.value) return; if (!canUndo.value) return;
undoFun(); undoFun();
@@ -327,6 +358,9 @@ const handleToolClick = (tool) => {
@change="fillColorChange" @change="fillColorChange"
style="width: 0; height: 0; opacity: 0" style="width: 0; height: 0; opacity: 0"
/> />
<div class="tools-list">
<slot name="customToolsTop" :tool-button-props="{ activeTool, canUndo, canRedo }" />
<ToolButton <ToolButton
v-for="tool in toolsList" v-for="tool in toolsList"
@@ -336,10 +370,13 @@ const handleToolClick = (tool) => {
:can-undo="canUndo" :can-undo="canUndo"
:can-redo="canRedo" :can-redo="canRedo"
@click="handleToolClick" @click="handleToolClick"
tip-body
/> />
<!-- 自定义工具栏按钮插槽 --> <!-- 自定义工具栏按钮插槽 -->
<slot name="customTools" :tool-button-props="{ activeTool, canUndo, canRedo }" /> <slot name="customToolsBottom" :tool-button-props="{ activeTool, canUndo, canRedo }" />
</div>
</div> </div>
</template> </template>
@@ -347,15 +384,25 @@ const handleToolClick = (tool) => {
.tools-sidebar { .tools-sidebar {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
gap: 1.0rem;
padding: 1.5rem 1.0rem; padding: 1.5rem 1.0rem;
border-right: .1rem solid #e0e0e0; border-right: .1rem solid #e0e0e0;
background-color: #ffffff; background-color: #ffffff;
user-select: none; user-select: none;
min-width: 5.8rem; min-width: 5.8rem;
height: 100%; 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 { .red-green-mode {
background-color: #fff4f4; background-color: #fff4f4;
} }

View File

@@ -21,6 +21,9 @@ import { RedGreenModeManager } from "./managers/RedGreenModeManager";
import texturePresetManager from "./managers/brushes/TexturePresetManager"; import texturePresetManager from "./managers/brushes/TexturePresetManager";
import { BrushStore } from "./store/BrushStore"; import { BrushStore } from "./store/BrushStore";
import cuowuImg from '@/assets/images/homePage/cuowu.svg' 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"; // import { MinimapManager } from "./managers/minimap/MinimapManager";
@@ -52,6 +55,7 @@ const emit = defineEmits([
"trigger-red-green-mouseup", // 红绿图模式鼠标抬起事件 "trigger-red-green-mouseup", // 红绿图模式鼠标抬起事件
"changeCanvas", // 画布变更事件 "changeCanvas", // 画布变更事件
"canvasInit", // 画布初始化事件 "canvasInit", // 画布初始化事件
"trigger-library", // 触发打开Library选择图片事件
]); ]);
const props = defineProps({ const props = defineProps({
@@ -101,6 +105,14 @@ const props = defineProps({
type: Boolean, type: Boolean,
default: false, // 是否显示固定图层 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, x: canvasManager.canvas.width / 2,
y: canvasManager.canvas.height / 2, y: canvasManager.canvas.height / 2,
}; };
canvasManager.animateZoom(centerPoint, newZoom); canvasManager.animateZoom(centerPoint, newZoom);
} }
@@ -601,7 +612,7 @@ async function addLayer() {
await layerManager.createLayer(t("Canvas.EmptyLayer")); await layerManager.createLayer(t("Canvas.EmptyLayer"));
} }
async function addTopLayer() { async function addTopLayer() {
await layerManager.createLayer("空图层", LayerType.EMPTY, { await layerManager.createLayer(t("Canvas.EmptyLayer"), LayerType.EMPTY, {
insertTop: true, insertTop: true,
}); });
} }
@@ -667,7 +678,15 @@ function deleteFun(){
function removeLayer(layerId) { function removeLayer(layerId) {
// Check if this is the last layer - prevent deletion // Check if this is the last layer - prevent deletion
if (layers.value.length <= 2) { 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( console.warn(
"Cannot delete the last layer. At least one layer must remain." "Cannot delete the last layer. At least one layer must remain."
); );
@@ -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() { function handleAddText() {
if (toolManager && canvasManager && canvasManager.canvas) { if (toolManager && canvasManager && canvasManager.canvas) {
// 在画布中央创建文本 // 在画布中央创建文本
@@ -828,6 +862,7 @@ const changeCanvas = async (command) => {
setTimeout(async ()=>{ setTimeout(async ()=>{
const imageData = await canvasManager.exportImage({ const imageData = await canvasManager.exportImage({
restoreOpacityInRedGreen: true, // 恢复红绿图模式下的透明度 restoreOpacityInRedGreen: true, // 恢复红绿图模式下的透明度
isCropByBg:true,
}); });
emit("trigger-red-green-mouseup", imageData); emit("trigger-red-green-mouseup", imageData);
},100) },100)
@@ -1066,10 +1101,14 @@ defineExpose({
@zoom-in="zoomIn" @zoom-in="zoomIn"
@zoom-out="zoomOut" @zoom-out="zoomOut"
@undo-redo-status-changed="changeCanvas" @undo-redo-status-changed="changeCanvas"
@trigger-library="triggerLibrary"
> >
<template #customToolsTop="{ toolTopProps }">
<slot name="customToolsTop" :tool-button-props="toolTopProps" />
</template>
<!-- 扩展插槽 --> <!-- 扩展插槽 -->
<template #customTools="{ toolButtonProps }"> <template #customToolsBottom="{ toolButtonProps }">
<slot name="customTools" :tool-button-props="toolButtonProps" /> <slot name="customToolsBottom" :tool-button-props="toolButtonProps" />
</template> </template>
</ToolsSidebar> </ToolsSidebar>
</div> </div>
@@ -1184,6 +1223,14 @@ defineExpose({
style="display: none" style="display: none"
@change="handleImageUpload" @change="handleImageUpload"
/> />
<SelectImages
ref="selectImages"
full-data
radio
@select="handleImageSelect"
:api="Https.httpUrls.queryLibraryPage"
isLibrary
/>
</div> </div>
</template> </template>

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,6 +1,8 @@
import { BaseBrush } from "../BaseBrush"; import { BaseBrush } from "../BaseBrush";
import { fabric } from "fabric-with-all"; import { fabric } from "fabric-with-all";
import texturePresetManager from "../TexturePresetManager"; 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 = [ const textureProperties = [
{ {
id: "textureSelector", id: "textureSelector",
name: "材质选择", name: t('Canvas.TextureSelector'),
type: "texture-grid", type: "texture-grid",
defaultValue: this.selectedTextureId, defaultValue: this.selectedTextureId,
options: textureOptions, options: textureOptions,
description: "选择要使用的纹理", description: t('Canvas.selectTexture'),
category: "纹理设置", category: t('Canvas.TextureSettings'),
order: 100, order: 100,
hidden: allTextures.length === 0, hidden: allTextures.length === 0,
}, },

View File

@@ -706,7 +706,8 @@ export class CanvasEventManager {
if (e.target.id) { if (e.target.id) {
// 如果该元素是分组图层的一部分,也更新分组图层的缩略图 // 如果该元素是分组图层的一部分,也更新分组图层的缩略图
if (e.target.parentId) { 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 { LiquifyWebGLManager } from "./LiquifyWebGLManager";
import { LiquifyCPUManager } from "./LiquifyCPUManager"; import { LiquifyCPUManager } from "./LiquifyCPUManager";
import { findLayerRecursively, LayerType } from "../../utils/layerHelper"; import { findLayerRecursively, LayerType } from "../../utils/layerHelper";
import i18n from "@/lang/index.ts";
const {t} = i18n.global;
export class EnhancedLiquifyManager { export class EnhancedLiquifyManager {
/** /**
@@ -20,13 +22,13 @@ export class EnhancedLiquifyManager {
// 是否强制使用WebGL模式 // 是否强制使用WebGL模式
forceWebGL: options.forceWebGL || false, 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网格精度 // WebGL网格精度
meshResolution: options.meshResolution || 64, meshResolution: options.meshResolution || 64,
}; };
@@ -599,7 +601,7 @@ export class EnhancedLiquifyManager {
if (objectsToCheck.length === 0) { if (objectsToCheck.length === 0) {
return { return {
valid: false, valid: false,
message: "图层为空,无法进行液化操作", message: t('Canvas.layerEmptyNoLiquidation'),
needsRasterization: false, needsRasterization: false,
isImage: false, isImage: false,
isEmpty: true, isEmpty: true,

View File

@@ -5,17 +5,20 @@
export class LiquifyCPUManager { export class LiquifyCPUManager {
constructor(options = {}) { constructor(options = {}) {
this.config = { this.config = {
gridSize: options.gridSize || 16, // 稍微增大网格提高性能 gridSize: 8, // 稍微增大网格提高性能
maxStrength: options.maxStrength || 200, // 适度降低最大强度 maxStrength: 200, // 适度降低最大强度
smoothingIterations: options.smoothingIterations || 1, // 增加平滑处理 smoothingIterations: 1, // 增加平滑处理
relaxFactor: options.relaxFactor || 0.1, // 适度松弛 relaxFactor: 0.05, // 适度松弛
sharpenAmount: 0.3, // 添加锐化强度参数
...options,
}; };
console.log("CPU版本的液化管理器config",this.config);
this.params = { this.params = {
size: 80, // 增大默认尺寸 size: 60, // 增大默认尺寸
pressure: 0.8, // 增大默认压力 pressure: 0.6, // 增大默认压力
distortion: 0, distortion: 0,
power: 0.8, // 增大默认动力 power: 0.7, // 增大默认动力
}; };
this.modes = { this.modes = {
@@ -132,6 +135,10 @@ export class LiquifyCPUManager {
} }
setParam(param, value) { setParam(param, value) {
if (param === 'sharpness') {
this.config.sharpenAmount = Math.max(0, Math.min(1, value));
return true;
}
if (param in this.params) { if (param in this.params) {
this.params[param] = value; this.params[param] = value;
return true; return true;
@@ -140,15 +147,19 @@ export class LiquifyCPUManager {
} }
getParams() { getParams() {
return { ...this.params }; return { ...this.params, sharpness: this.config.sharpenAmount, };
}
// 添加清晰度控制方法
setSharpness(amount) {
this.config.sharpenAmount = Math.max(0, Math.min(1, amount));
return this;
} }
resetParams() { resetParams() {
this.params = { this.params = {
size: 80, // 增大默认尺寸 size: 60, // 增大默认尺寸
pressure: 0.8, // 增大默认压力 pressure: 0.6, // 增大默认压力
distortion: 0, distortion: 0,
power: 0.8, // 增大默认动力 power: 0.7, // 增大默认动力
}; };
} }
@@ -645,53 +656,69 @@ export class LiquifyCPUManager {
* @returns {Array|null} RGBA颜色值数组或null * @returns {Array|null} RGBA颜色值数组或null
*/ */
_bilinearSample(data, width, height, x, y) { _bilinearSample(data, width, height, x, y) {
if (x < 0 || x >= width - 1 || y < 0 || y >= height - 1) { return this._bicubicInterpolate(data, width, height, x, y);
return null; }
/**
* 双三次插值实现 - 确保正确处理Alpha通道
* @param {Uint8ClampedArray} data 图像数据
* @param {number} width 图像宽度
* @param {number} height 图像高度
* @param {number} x X坐标
* @param {number} y Y坐标
* @returns {Array|null} RGBA颜色值数组或null
*/
_bicubicInterpolate(data, width, height, x, y) {
// 获取周围16个像素点
const x1 = Math.floor(x) - 1;
const y1 = Math.floor(y) - 1;
// 创建16个采样点的颜色数组
const pixels = [];
for (let ky = 0; ky < 4; ky++) {
for (let kx = 0; kx < 4; kx++) {
const px = Math.max(0, Math.min(width - 1, x1 + kx));
const py = Math.max(0, Math.min(height - 1, y1 + ky));
const idx = (py * width + px) * 4;
pixels[ky * 4 + kx] = [data[idx], data[idx + 1], data[idx + 2], data[idx + 3]];
}
} }
const x1 = Math.floor(x); // 计算小数部分
const y1 = Math.floor(y); const fx = x - (x1 + 1);
const x2 = x1 + 1; const fy = y - (y1 + 1);
const y2 = y1 + 1;
const fx = x - x1; // 计算行插值
const fy = y - y1; const row0 = this._cubicInterpolateRow(pixels[0], pixels[1], pixels[2], pixels[3], fx);
const row1 = this._cubicInterpolateRow(pixels[4], pixels[5], pixels[6], pixels[7], fx);
const row2 = this._cubicInterpolateRow(pixels[8], pixels[9], pixels[10], pixels[11], fx);
const row3 = this._cubicInterpolateRow(pixels[12], pixels[13], pixels[14], pixels[15], fx);
const getPixel = (px, py) => { // 计算最终结果
const idx = (py * width + px) * 4; return this._cubicInterpolateRow(row0, row1, row2, row3, fy);
return [data[idx], data[idx + 1], data[idx + 2], data[idx + 3]]; }
}; // 三次插值辅助方法 - 单行插值
_cubicInterpolateRow(p0, p1, p2, p3, t) {
// 使用三次多项式插值公式
const a = [0, 0, 0, 0];
const b = [0, 0, 0, 0];
const c = [0, 0, 0, 0];
const p1 = getPixel(x1, y1); // 为每个通道计算插值系数
const p2 = getPixel(x2, y1); for (let i = 0; i < 4; i++) {
const p3 = getPixel(x1, y2); a[i] = -0.5 * p0[i] + 1.5 * p1[i] - 1.5 * p2[i] + 0.5 * p3[i];
const p4 = getPixel(x2, y2); b[i] = p0[i] - 2.5 * p1[i] + 2 * p2[i] - 0.5 * p3[i];
c[i] = -0.5 * p0[i] + 0.5 * p2[i];
}
// 应用三次多项式
const t2 = t * t;
const t3 = t * t2;
return [ return [
Math.round( Math.round(a[0] * t3 + b[0] * t2 + c[0] * t + p1[0]),
(1 - fx) * (1 - fy) * p1[0] + Math.round(a[1] * t3 + b[1] * t2 + c[1] * t + p1[1]),
fx * (1 - fy) * p2[0] + Math.round(a[2] * t3 + b[2] * t2 + c[2] * t + p1[2]),
(1 - fx) * fy * p3[0] + Math.round(a[3] * t3 + b[3] * t2 + c[3] * t + p1[3]) // 确保Alpha通道也被正确插值
fx * fy * p4[0]
),
Math.round(
(1 - fx) * (1 - fy) * p1[1] +
fx * (1 - fy) * p2[1] +
(1 - fx) * fy * p3[1] +
fx * fy * p4[1]
),
Math.round(
(1 - fx) * (1 - fy) * p1[2] +
fx * (1 - fy) * p2[2] +
(1 - fx) * fy * p3[2] +
fx * fy * p4[2]
),
Math.round(
(1 - fx) * (1 - fy) * p1[3] +
fx * (1 - fy) * p2[3] +
(1 - fx) * fy * p3[3] +
fx * fy * p4[3]
),
]; ];
} }
@@ -928,31 +955,40 @@ export class LiquifyCPUManager {
const srcData = this.originalImageData.data; const srcData = this.originalImageData.data;
const dstData = result.data; const dstData = result.data;
// 性能优化:使用步长采样减少计算量 // 移除步长采样始终使用1:1采样
const step = width > 1000 || height > 1000 ? 2 : 1; for (let y = 0; y < height; y++) {
for (let x = 0; x < width; x++) {
for (let y = 0; y < height; y += step) {
for (let x = 0; x < width; x += step) {
const srcPos = this._mapPointBack(x, y); const srcPos = this._mapPointBack(x, y);
const dstIdx = (y * width + x) * 4;
if (srcPos.x >= 0 && srcPos.x < width && srcPos.y >= 0 && srcPos.y < height) { if (srcPos.x >= 0 && srcPos.x < width && srcPos.y >= 0 && srcPos.y < height) {
const color = this._bilinearInterpolate(srcData, width, height, srcPos.x, srcPos.y); // 使用双三次插值获取颜色
const color = this._bicubicInterpolate(srcData, width, height, srcPos.x, srcPos.y);
// 如果使用步长采样,需要填充相邻像素
for (let dy = 0; dy < step && y + dy < height; dy++) {
for (let dx = 0; dx < step && x + dx < width; dx++) {
const dstIdx = ((y + dy) * width + (x + dx)) * 4;
dstData[dstIdx] = color[0]; dstData[dstIdx] = color[0];
dstData[dstIdx + 1] = color[1]; dstData[dstIdx + 1] = color[1];
dstData[dstIdx + 2] = color[2]; dstData[dstIdx + 2] = color[2];
dstData[dstIdx + 3] = color[3]; dstData[dstIdx + 3] = color[3]; // 确保Alpha通道值被正确设置
} } else {
} // 对于边界外的点使用最近的有效像素或保持原Alpha通道
// 这里我们确保Alpha通道不为0防止出现透明区域
const nearestX = Math.max(0, Math.min(width - 1, Math.round(srcPos.x)));
const nearestY = Math.max(0, Math.min(height - 1, Math.round(srcPos.y)));
const nearestIdx = (nearestY * width + nearestX) * 4;
// 复制最近像素的颜色但保持Alpha通道为不透明
dstData[dstIdx] = srcData[nearestIdx];
dstData[dstIdx + 1] = srcData[nearestIdx + 1];
dstData[dstIdx + 2] = srcData[nearestIdx + 2];
dstData[dstIdx + 3] = 255; // 强制设置为完全不透明
} }
} }
} }
this.currentImageData = result; this.currentImageData = result;
// 添加锐化处理
if (this.config.sharpenAmount > 0) {
this.currentImageData = this._sharpenImage(this.currentImageData, this.config.sharpenAmount);
}
return result; return result;
} }
@@ -1066,21 +1102,122 @@ export class LiquifyCPUManager {
const originalX = x1 * gridSize + fx * gridSize; const originalX = x1 * gridSize + fx * gridSize;
const originalY = y1 * gridSize + fy * gridSize; const originalY = y1 * gridSize + fy * gridSize;
// 计算偏移量并应用反向映射 // 检查是否接近边缘,如果是则减少偏移量
const offsetX = deformedX - originalX; const isNearEdge = this._isNearEdge(originalX, originalY);
const offsetY = deformedY - originalY; const edgeProtectionFactor = isNearEdge ? 0.2 : 1.0; // 边缘区域减少变形量
// 限制偏移量,防止过度扭曲 // 计算偏移量并应用反向映射
const maxOffset = gridSize * 0.5; const offsetX = (deformedX - originalX) * edgeProtectionFactor;
const limitedOffsetX = Math.max(-maxOffset, Math.min(maxOffset, offsetX)); const offsetY = (deformedY - originalY) * edgeProtectionFactor;
const limitedOffsetY = Math.max(-maxOffset, Math.min(maxOffset, offsetY));
return { return {
x: Math.max(0, Math.min(this.mesh.width - 1, x - limitedOffsetX)), x: Math.max(0, Math.min(this.mesh.width - 1, x - offsetX)),
y: Math.max(0, Math.min(this.mesh.height - 1, y - limitedOffsetY)), y: Math.max(0, Math.min(this.mesh.height - 1, y - offsetY)),
}; };
} }
// 边缘检测辅助方法
_isNearEdge(x, y, threshold = 10) {
if (!this.originalImageData) return false;
const data = this.originalImageData.data;
const width = this.originalImageData.width;
const height = this.originalImageData.height;
// 检查像素是否在边缘
if (x <= 0 || x >= width - 1 || y <= 0 || y >= height - 1) return true;
// 简单的Sobel边缘检测
const getPixelBrightness = (px, py) => {
const idx = (py * width + px) * 4;
return (data[idx] + data[idx + 1] + data[idx + 2]) / 3;
};
const kernelX = [
[-1, 0, 1],
[-2, 0, 2],
[-1, 0, 1]
];
const kernelY = [
[-1, -2, -1],
[0, 0, 0],
[1, 2, 1]
];
let gradientX = 0;
let gradientY = 0;
for (let ky = -1; ky <= 1; ky++) {
for (let kx = -1; kx <= 1; kx++) {
const px = Math.min(Math.max(0, x + kx), width - 1);
const py = Math.min(Math.max(0, y + ky), height - 1);
const brightness = getPixelBrightness(px, py);
gradientX += brightness * kernelX[ky + 1][kx + 1];
gradientY += brightness * kernelY[ky + 1][kx + 1];
}
}
const gradientMagnitude = Math.sqrt(gradientX * gradientX + gradientY * gradientY);
return gradientMagnitude > threshold;
}
// 图像锐化方法
_sharpenImage(imageData, amount = 0.5) {
if (!imageData) return imageData;
const data = new Uint8ClampedArray(imageData.data);
const width = imageData.width;
const height = imageData.height;
const result = new ImageData(width, height);
const dstData = result.data;
// 锐化核 - 中心为5周围为-1
const kernel = [
[0, -1, 0],
[-1, 5, -1],
[0, -1, 0]
];
for (let y = 0; y < height; y++) {
for (let x = 0; x < width; x++) {
// 边缘像素不处理
if (x === 0 || x === width - 1 || y === 0 || y === height - 1) {
const idx = (y * width + x) * 4;
for (let c = 0; c < 4; c++) {
dstData[idx + c] = data[idx + c];
}
continue;
}
const sharpened = [0, 0, 0, 0];
// 应用锐化核
for (let ky = -1; ky <= 1; ky++) {
for (let kx = -1; kx <= 1; kx++) {
const px = x + kx;
const py = y + ky;
const idx = (py * width + px) * 4;
const weight = kernel[ky + 1][kx + 1];
for (let c = 0; c < 3; c++) { // 只锐化RGB通道
sharpened[c] += data[idx + c] * weight;
}
sharpened[3] = data[idx + 3]; // 保持Alpha通道不变
}
}
// 应用锐化强度并裁剪值范围
const idx = (y * width + x) * 4;
for (let c = 0; c < 3; c++) {
const original = data[idx + c];
const diff = sharpened[c] - original;
dstData[idx + c] = Math.max(0, Math.min(255, original + diff * amount));
}
dstData[idx + 3] = sharpened[3];
}
}
return result;
}
_bilinearInterpolate(data, width, height, x, y) { _bilinearInterpolate(data, width, height, x, y) {
const x1 = Math.floor(x); const x1 = Math.floor(x);
const y1 = Math.floor(y); const y1 = Math.floor(y);
@@ -1253,8 +1390,9 @@ export class LiquifyCPUManager {
// 应用变形 // 应用变形
this._applyDeformation(x, y, radius, finalStrength, mode, this.params.distortion); this._applyDeformation(x, y, radius, finalStrength, mode, this.params.distortion);
// 平滑处理 // 有条件地应用平滑处理,仅在特定模式下应用
if (this.config.smoothingIterations > 0) { const smoothingModes = [this.modes.CRYSTAL, this.modes.EDGE];
if (smoothingModes.includes(mode) && this.config.smoothingIterations > 0) {
this._smoothMesh(); this._smoothMesh();
} }

View File

@@ -31,10 +31,10 @@ export class LiquifyManager {
// 创建增强版液化管理器实例 // 创建增强版液化管理器实例
this.enhancedManager = new EnhancedLiquifyManager({ this.enhancedManager = new EnhancedLiquifyManager({
// 配置选项 // 配置选项
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,
meshResolution: options.meshResolution || 64, meshResolution: options.meshResolution || 64,
// 根据环境选择合适的渲染模式 // 根据环境选择合适的渲染模式
forceCPU: true, // 默认不强制使用CPU forceCPU: true, // 默认不强制使用CPU

View File

@@ -21,6 +21,10 @@ const props = defineProps({
type: Boolean, type: Boolean,
default: true, default: true,
}, },
tipBody: {
type: Boolean,
default: false,
},
}); });
const emit = defineEmits(["click"]); const emit = defineEmits(["click"]);
@@ -47,10 +51,45 @@ const handleClick = () => {
if (isDisabled.value) return; if (isDisabled.value) return;
emit("click", props.tool); 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> </script>
<template> <template>
<div <div
ref="el"
:class="[ :class="[
'tool-btn', 'tool-btn',
tool.class, tool.class,
@@ -63,7 +102,10 @@ const handleClick = () => {
@click="handleClick" @click="handleClick"
> >
<SvgIcon :name="tool.icon.name" :size="tool.icon.size"></SvgIcon> <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> </div>
</template> </template>
@@ -82,6 +124,7 @@ const handleClick = () => {
font-size: 1.6rem; font-size: 1.6rem;
color: #333; color: #333;
transition: all 0.2s ease; transition: all 0.2s ease;
flex-shrink: 0;
} }
.tool-btn:hover { .tool-btn:hover {
@@ -115,7 +158,7 @@ const handleClick = () => {
margin-left: .8rem; margin-left: .8rem;
white-space: nowrap; white-space: nowrap;
font-size: 1.2rem; font-size: 1.2rem;
z-index: 10; z-index: 9999;
} }
.tool-tooltip:before { .tool-tooltip:before {

View File

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

View File

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

View File

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

View File

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

View File

@@ -9,6 +9,7 @@
<editCanvas v-if="canvasLoad" :config="canvasConfig" <editCanvas v-if="canvasLoad" :config="canvasConfig"
@canvasInit="canvasInit" @canvasInit="canvasInit"
@changeCanvas="changeCanvas" @changeCanvas="changeCanvas"
is-edit
:clothingImageUrl="selectDetail?.undividedLayerWithSinglePrint || selectDetail.undividedLayer || selectDetail.path" :clothingImageUrl="selectDetail?.undividedLayerWithSinglePrint || selectDetail.undividedLayer || selectDetail.path"
showFixedLayer showFixedLayer
:canvasJSON="canvasJSON" :canvasJSON="canvasJSON"
@@ -16,13 +17,6 @@
imageMode:'contains', imageMode:'contains',
}" }"
ref="editCanvas"> 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> </editCanvas>
<!-- <canvasContent ref="canvasContent"></canvasContent> --> <!-- <canvasContent ref="canvasContent"></canvasContent> -->
</div> </div>
@@ -34,20 +28,16 @@
ref="editFrontBack"> ref="editFrontBack">
</editFrontBack> --> </editFrontBack> -->
<editCanvas v-if="canvasLoad" :config="canvasConfig" <editCanvas v-if="canvasLoad" :config="canvasConfig"
@canvasInit="canvasInit"
:enabledRedGreenMode="true" :enabledRedGreenMode="true"
:clothingImageUrl="selectDetail.path" :clothingImageUrl="selectDetail.path"
:redGreenImageUrl="frontBack.front[imgDomIndex].maskUrl" :redGreenImageUrl="frontBack.front[imgDomIndex].maskUrl"
@trigger-red-green-mouseup="frontBackChange" @trigger-red-green-mouseup="frontBackChange"
is-edit
:clothing-image-opts="{ :clothing-image-opts="{
imageMode:'contains', imageMode:'contains',
}" }"
ref="editCanvasBackFront"> 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> </editCanvas>
</div> </div>
</div> </div>
@@ -79,6 +69,10 @@ export default defineComponent({
editCanvas editCanvas
}, },
props:{ props:{
isEditPattern:{
type:String,
default:''
}
}, },
setup(props,{emit}) { setup(props,{emit}) {
const store = useStore(); const store = useStore();
@@ -106,7 +100,7 @@ export default defineComponent({
canvasLoad:false, canvasLoad:false,
canvasConfig:{ canvasConfig:{
} as any, } as any,
currentView:'', currentView:props.isEditPattern,
getCanvasIfEdit:inject('getCanvasIfEdit')as any, getCanvasIfEdit:inject('getCanvasIfEdit')as any,
canvasInstance:null as any, canvasInstance:null as any,
canvasJSON:'', canvasJSON:'',
@@ -117,6 +111,7 @@ export default defineComponent({
},{immediate: true}) },{immediate: true})
provide('isShowMark',detailData.isShowMark) provide('isShowMark',detailData.isShowMark)
provide('canvasType',detailData.canvasType) provide('canvasType',detailData.canvasType)
const editFront = (str:any)=>{//编辑前后片 const editFront = (str:any)=>{//编辑前后片
let canvasJSON = '' as any let canvasJSON = '' as any
@@ -184,12 +179,55 @@ export default defineComponent({
img.src = url 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)=>{ const frontBackChange = (value:any)=>{
let full = detailData.frontBack.front[detailData.imgDomIndex]?.undividedLayerWithSinglePrint || detailData.frontBack.front[detailData.imgDomIndex].undividedLayer || detailData.selectDetail.path let full = detailData.frontBack.front[detailData.imgDomIndex]?.undividedLayerWithSinglePrint || detailData.frontBack.front[detailData.imgDomIndex].undividedLayer || detailData.selectDetail.path
let size = { let size = {
...detailData.canvasConfig, ...detailData.canvasConfig,
} }
segmentImage(value,full,size).then((rv)=>{
segmentImage(value,full,size).then(async (rv)=>{
let front = detailData.frontBack.front[detailData.imgDomIndex] let front = detailData.frontBack.front[detailData.imgDomIndex]
let back = detailData.frontBack.back[detailData.imgDomIndex] let back = detailData.frontBack.back[detailData.imgDomIndex]
if(!front?.oldImageUrl)front.oldImageUrl = front.imageUrl if(!front?.oldImageUrl)front.oldImageUrl = front.imageUrl
@@ -198,14 +236,14 @@ export default defineComponent({
if(!front?.oldMaskUrl)store.commit('DesignDetail/updataDetailItem',{maskUrl:front.oldMaskUrl}) if(!front?.oldMaskUrl)store.commit('DesignDetail/updataDetailItem',{maskUrl:front.oldMaskUrl})
front.imageUrl = rv.targetFrontUrl front.imageUrl = rv.targetFrontUrl
front.maskUrl = value let base64 = await resizeImageWithNativeCanvas(front.oldMaskUrl,value)
front.maskUrl = base64
back.imageUrl = rv.targetBackUrl back.imageUrl = rv.targetBackUrl
store.commit('DesignDetail/updataDetailItem',{maskUrl:value}) store.commit('DesignDetail/updataDetailItem',{maskUrl:value})
}) })
} }
const canvasInit = (value:any)=>{ const canvasInit = (value:any)=>{
// detailDom.editCanvas.addImageToLayer(detailData.selectDetail.undividedLayer,{layerId:value.layers.value[0].id,imageMode:'contains',undoable:false})
detailData.canvasInstance = value detailData.canvasInstance = value
detailData.getCanvasIfEdit.fun = getCanvasLength detailData.getCanvasIfEdit.fun = getCanvasLength
detailData.isShowMark = false detailData.isShowMark = false
@@ -254,9 +292,11 @@ export default defineComponent({
saveCanvas('auto') saveCanvas('auto')
},3000) },3000)
} }
onBeforeUnmount(()=>{ onBeforeUnmount(()=>{
let front = detailData.frontBack.front[detailData.imgDomIndex] let front = detailData.frontBack.front[detailData.imgDomIndex]
let back = detailData.frontBack.back[detailData.imgDomIndex] let back = detailData.frontBack.back[detailData.imgDomIndex]
if(front?.oldImageUrl)front.imageUrl = front.oldImageUrl if(front?.oldImageUrl)front.imageUrl = front.oldImageUrl
if(front?.oldMaskUrl)front.maskUrl = front.oldMaskUrl if(front?.oldMaskUrl)front.maskUrl = front.oldMaskUrl
if(back?.oldImageUrl)back.imageUrl = back.oldImageUrl if(back?.oldImageUrl)back.imageUrl = back.oldImageUrl
@@ -269,9 +309,7 @@ export default defineComponent({
}) })
onMounted(()=>{ onMounted(()=>{
nextTick(async ()=>{ nextTick(async ()=>{
detailData.currentView = 'canvasEditor' // detailData.currentView = 'canvasEditor'
setTimeout(()=>{
})
if(detailData.selectDetail.canvasId){ if(detailData.selectDetail.canvasId){
detailData.isShowMark = true detailData.isShowMark = true
await new Promise((resolve, reject) => { await new Promise((resolve, reject) => {

View File

@@ -16,8 +16,16 @@
</div> </div>
</div> </div>
<div class="detailText">{{$t('DesignPrintOperation.Colorfromimage')}}</div> <div class="detailText">{{$t('DesignPrintOperation.Colorfromimage')}}</div>
<div class="uploadImage"> <div class="uploadImage flex flex-align-center flex-justify-center">
<upload @selectUplpadColor="selectUplpadColor"></upload> <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>
<div class="detailText">{{$t('DesignPrintOperation.ColorCode')}}</div> <div class="detailText">{{$t('DesignPrintOperation.ColorCode')}}</div>
<div class="colorCode"> <div class="colorCode">
@@ -30,7 +38,14 @@
<!-- <div class="getTcxColorBtn" @click="getTcxColor">{{$t('DesignPrintOperation.ExtractColor')}}</div> --> <!-- <div class="getTcxColorBtn" @click="getTcxColor">{{$t('DesignPrintOperation.ExtractColor')}}</div> -->
</div> </div>
</div> </div>
<SelectImages
ref="selectImages"
@select="handleImageSelect"
radio
full-data
:api="Https.httpUrls.queryLibraryPage"
isLibrary
/>
</template> </template>
<script lang="ts"> <script lang="ts">
import { defineComponent,computed,ref,watch,nextTick,toRefs, reactive, onMounted} from 'vue' 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 { useStore } from "vuex";
import { Https } from "@/tool/https"; import { Https } from "@/tool/https";
import { useI18n } from 'vue-i18n' 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 { message,Upload} from 'ant-design-vue';
import SelectImages from '@/component/common/SelectImages.vue'
import upload from './upload.vue' import upload from './upload.vue'
import pallet from './pallet.vue' import pallet from './pallet.vue'
export default defineComponent({ export default defineComponent({
components:{ components:{
upload,pallet upload,pallet,SelectImages
}, },
setup(props,{emit}) { setup(props,{emit}) {
const store = useStore(); 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)=>{ const selectUplpadColor = (item:any)=>{
colorData.selectColor = JSON.parse(JSON.stringify(item)) colorData.selectColor = JSON.parse(JSON.stringify(item))
} }
@@ -301,6 +342,14 @@ export default defineComponent({
selectUplpadColor, selectUplpadColor,
setSelectColor, setSelectColor,
getTcxColor, getTcxColor,
handleOpenLibrary,
Https,
showLibrary,
selectImages,
uploadRef,
handleImageSelect,
handleShowListChange,
} }
}, },
@@ -312,21 +361,46 @@ export default defineComponent({
}) })
</script> </script>
<style lang="less" scoped> <style lang="less" scoped>
.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{ .color{
// width: 34rem; // width: 34rem;
width: 100%; width: 100%;
height: 100%; // height: 100%;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
row-gap: 1rem;
overflow-y: auto; overflow-y: auto;
> .detailText{ // > .detailText{
margin-bottom: .5rem; // margin-bottom: .5rem;
} // }
> .pallet{ // > .pallet{
margin-bottom: 2rem; // margin-bottom: 2rem;
} // }
> .colorBox{ > .colorBox{
margin-bottom: 1rem; // margin-bottom: 1rem;
display: flex; display: flex;
flex-wrap: wrap; flex-wrap: wrap;
justify-content: space-between; justify-content: space-between;
@@ -368,15 +442,44 @@ export default defineComponent({
} }
> .uploadImage{ > .uploadImage{
flex-shrink: 0; flex-shrink: 0;
margin-bottom: 3rem; // margin-bottom: 3rem;
border: 1px dashed transparent; 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); background: linear-gradient(#fff, #fff) padding-box,repeating-linear-gradient(-45deg,#fff 0,#fff 0.3em, #000 0,#000 0.6em);
height: 10rem; height: 10rem;
width: 100%; width: 100%;
border-radius: .5rem; 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{ > .colorCode{
margin-bottom: 3rem; // margin-bottom: 3rem;
> .generalModel_state{ > .generalModel_state{
> .generalModel_state_item{ > .generalModel_state_item{
width: 100%; width: 100%;

View File

@@ -8,7 +8,7 @@
</div> </div>
</div> </div>
<div class="palletBox"> <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> <Chrome class="chrome_color" v-model="color_"></Chrome>
<div class="color_setting_operateSingle"> <div class="color_setting_operateSingle">
<div class="color_setting_btn" :class="{active:!color?.gradient?.gradientShow}">{{ $t('ColorboardUpload.Single') }}</div> <div class="color_setting_btn" :class="{active:!color?.gradient?.gradientShow}">{{ $t('ColorboardUpload.Single') }}</div>
@@ -42,7 +42,7 @@
</template> </template>
<script lang="ts"> <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 { useStore } from "vuex";
import { useI18n } from 'vue-i18n' import { useI18n } from 'vue-i18n'
import { message,Upload} from 'ant-design-vue'; import { message,Upload} from 'ant-design-vue';
@@ -273,7 +273,24 @@ export default defineComponent({
}else{ }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(()=>{ onMounted(()=>{
// 添加点击外部区域监听器
document.addEventListener('click', handleClickOutside);
nextTick().then(()=>{ nextTick().then(()=>{
const backIcon = document.createElement('div'); const backIcon = document.createElement('div');
backIcon.classList.add('vc-sketch-color-wrap') backIcon.classList.add('vc-sketch-color-wrap')
@@ -297,6 +314,11 @@ export default defineComponent({
}) })
}) })
}) })
onUnmounted(()=>{
// 清理事件监听器
document.removeEventListener('click', handleClickOutside);
})
return{ return{
...toRefs(palletData), ...toRefs(palletData),
...toRefs(getpalletListDom), ...toRefs(getpalletListDom),

View File

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

View File

@@ -122,10 +122,11 @@ export default defineComponent({
padding: 1rem; padding: 1rem;
text-align: center; text-align: center;
border-radius: .5rem; border-radius: .5rem;
// border: 1px dashed #202020; border: 1px solid #000;
border: 1px dashed transparent; // 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); 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; display: flex;
flex-wrap: wrap; flex-wrap: wrap;
overflow: hidden; overflow: hidden;

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -120,11 +120,11 @@ export default defineComponent({
height: 23.5rem; height: 23.5rem;
padding: 1rem; padding: 1rem;
text-align: center; text-align: center;
border-radius: .5rem; border: 1px solid #000;
// border: 1px dashed #202020; // border: 1px dashed transparent;
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); 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; display: flex;
flex-wrap: wrap; flex-wrap: wrap;
overflow: hidden; overflow: hidden;

View File

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

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