156 Commits

Author SHA1 Message Date
X1627315083@163.com
5eaa77596e fix 2026-05-13 16:36:23 +08:00
李志鹏
848e7b4692 更改卖家审批提示词 2026-05-12 11:23:18 +08:00
李志鹏
fd140ebc56 还原绑定 2026-05-12 11:08:18 +08:00
李志鹏
34094c8c92 Merge branch 'dev_vite' of http://18.167.251.121:10003/aidlab/aida_front into dev_vite 2026-05-12 11:01:54 +08:00
李志鹏
e4dc2bf729 还原绑定 2026-05-12 11:01:53 +08:00
李志鹏
7f226179d9 绑定修改 2026-05-11 10:50:17 +08:00
李志鹏
3edff6b05c Merge branch 'dev_vite' of http://18.167.251.121:10003/aidlab/aida_front into dev_vite 2026-05-11 10:01:19 +08:00
李志鹏
6fd1212298 修改 2026-05-11 10:01:18 +08:00
e093cccb8d bugfix: 没有产品主图时自动选中问题 2026-05-07 17:06:06 +08:00
b2c6c61515 bugfix: 封面必填校验 2026-05-07 15:53:47 +08:00
0219b1a2f4 docs: 商品编辑页面的agents.md 2026-05-07 13:53:06 +08:00
133433a260 feat: 视频保存 2026-05-07 13:15:21 +08:00
90a59a3dc5 feat: 视频显示到列表 2026-05-07 10:21:08 +08:00
c8a65ee2cb Merge branch 'dev_vite' of ssh://18.167.251.121:10002/aidlab/aida_front into dev_vite 2026-05-06 16:33:32 +08:00
226918e941 style: 裁剪弹窗标题被遮挡问题 2026-05-06 16:33:31 +08:00
李志鹏
494bfd68ca 1 2026-05-06 16:13:34 +08:00
X1627315083@163.com
95b70792ba Merge branch 'dev_vite' of ssh://18.167.251.121:10002/aidlab/aida_front into dev_vite 2026-05-06 16:06:56 +08:00
X1627315083@163.com
a0fffa5896 fix 2026-05-06 16:06:54 +08:00
李志鹏
06eaabc742 Merge branch 'dev_vite' of http://18.167.251.121:10003/aidlab/aida_front into dev_vite 2026-05-06 13:45:29 +08:00
李志鹏
27da4739a6 语言适配 2026-05-06 13:45:28 +08:00
52576aa0a1 style: 裁剪框被遮挡 2026-05-06 13:25:42 +08:00
22aa7c37cd bugfix: 裁剪组件重置状态 2026-05-06 11:04:46 +08:00
752b33f196 bugfix: 裁剪组件切换类型清除之前的缓存 2026-05-05 15:19:31 +08:00
f3b873b7ae Merge branch 'dev_vite' of ssh://18.167.251.121:10002/aidlab/aida_front into dev_vite 2026-05-05 15:10:16 +08:00
006c2e3f9c bugfix: 剪切组件顶部来源按钮样式 2026-05-05 15:10:14 +08:00
X1627315083@163.com
1f413b36ca fix 2026-05-05 14:53:49 +08:00
X1627315083@163.com
e7b052f100 fix 2026-05-05 14:43:00 +08:00
X1627315083@163.com
19bb412470 Merge branch 'dev_vite' of ssh://18.167.251.121:10002/aidlab/aida_front into dev_vite 2026-05-05 14:41:17 +08:00
X1627315083@163.com
a1e071f7bc fix 2026-05-05 14:41:14 +08:00
李志鹏
5388b2df4c Merge branch 'dev_vite' of http://18.167.251.121:10003/aidlab/aida_front into dev_vite 2026-05-05 14:39:43 +08:00
李志鹏
76e507cae3 显示id 2026-05-05 14:39:41 +08:00
X1627315083@163.com
d4e9462d39 fix 2026-05-05 14:34:12 +08:00
X1627315083@163.com
4a11d172d2 fix 2026-05-05 14:27:59 +08:00
X1627315083@163.com
e20092c77f fix 2026-05-05 13:41:18 +08:00
X1627315083@163.com
5f4656c629 fix 2026-05-05 13:38:43 +08:00
X1627315083@163.com
14eca9aff2 fix 2026-05-05 13:33:51 +08:00
X1627315083@163.com
88f0528553 Merge branch 'dev_vite' of ssh://18.167.251.121:10002/aidlab/aida_front into dev_vite 2026-05-05 13:17:24 +08:00
X1627315083@163.com
afbea289fb fix 2026-05-05 13:17:20 +08:00
eb2baa26a7 bugfix: 导航i18n 2026-05-05 10:12:19 +08:00
X1627315083@163.com
62829395ce fix 2026-05-05 09:58:01 +08:00
X1627315083@163.com
16532ce44b fix 2026-05-05 09:25:33 +08:00
李志鹏
27de720137 Merge branch 'dev_vite' of http://18.167.251.121:10003/aidlab/aida_front into dev_vite 2026-05-04 14:08:18 +08:00
李志鹏
acf2029efe 删除设计师 2026-05-04 14:08:16 +08:00
X1627315083@163.com
49398aac48 语言适配 2026-05-04 14:06:16 +08:00
李志鹏
b3d9bce440 卖家端多语言 2026-05-04 11:18:56 +08:00
李志鹏
596bc75b83 设置裁剪框原图裁剪 2026-04-30 15:43:55 +08:00
李志鹏
c673948dd3 Merge branch 'dev_vite' of http://18.167.251.121:10003/aidlab/aida_front into dev_vite 2026-04-29 17:20:25 +08:00
李志鹏
1f8ee2e48e 1 2026-04-29 17:20:23 +08:00
e5ae549a3d Merge branch 'dev_vite' of ssh://18.167.251.121:10002/aidlab/aida_front into dev_vite 2026-04-29 17:19:05 +08:00
88c2ae8583 style: 裁剪 2026-04-29 17:19:00 +08:00
X1627315083@163.com
f4897e2c92 修复点击商品草稿删除里面没有内容 2026-04-29 16:59:09 +08:00
aab96bbe70 Merge branch 'dev_vite' of ssh://18.167.251.121:10002/aidlab/aida_front into dev_vite 2026-04-29 16:33:17 +08:00
4004fa2703 style: 性别label 2026-04-29 16:33:14 +08:00
李志鹏
4a078186a9 11 2026-04-29 16:23:42 +08:00
fafccf0352 bugfix: 切换性别清空选中的商品类别 2026-04-29 16:17:32 +08:00
64844105b5 bugfix: 面包屑导航 2026-04-29 15:41:20 +08:00
d6a07e7fc7 feat: 面包屑导航 2026-04-29 15:21:50 +08:00
7eff1b0506 Merge branch 'dev_vite' of ssh://18.167.251.121:10002/aidlab/aida_front into dev_vite 2026-04-29 15:07:44 +08:00
2dc98e5dd8 bugfix: productList 2026-04-29 15:07:43 +08:00
X1627315083@163.com
3e7a90bd2d Merge branch 'dev_vite' of ssh://18.167.251.121:10002/aidlab/aida_front into dev_vite 2026-04-29 14:50:00 +08:00
X1627315083@163.com
d42a1bef8d fix 2026-04-29 14:49:58 +08:00
5d184bf1f9 bugfix: 保存草稿同样要填写必填项 2026-04-29 14:47:23 +08:00
56825fcbb1 bugfix: 多页编辑 2026-04-29 14:16:23 +08:00
7cf37e2da6 bugfix: props类型 2026-04-29 13:11:36 +08:00
59a7c169ad feat: 裁剪组件 2026-04-29 10:47:25 +08:00
fc997a1cce bugfix: 接口地址 2026-04-29 09:56:11 +08:00
李志鹏
8abcdb151d Merge branch 'dev_vite' of http://18.167.251.121:10003/aidlab/aida_front into dev_vite 2026-04-28 17:25:01 +08:00
李志鹏
55481c6d51 更换接口 2026-04-28 17:24:59 +08:00
ca39910f4c chore: 拆分组件 2026-04-28 16:33:46 +08:00
646b48ca6b Merge branch 'dev_vite' of ssh://18.167.251.121:10002/aidlab/aida_front into dev_vite 2026-04-28 16:03:54 +08:00
45f150769e feat: 草稿编辑 2026-04-28 16:03:52 +08:00
X1627315083@163.com
7115feb20c fix 2026-04-28 15:30:47 +08:00
00d488cbc2 chore: 撤销部署配置 2026-04-28 15:21:05 +08:00
12233b952b chore: 部署文件暂时修改 2026-04-28 15:05:20 +08:00
4b8554f41e style: 去除底边距 2026-04-28 15:05:02 +08:00
f30a749f65 feat: 管理员页面 2026-04-28 14:25:17 +08:00
X1627315083@163.com
af544d2da8 fix 2026-04-28 11:46:36 +08:00
aedd633f8e Merge branch 'dev_vite' of ssh://18.167.251.121:10002/aidlab/aida_front into dev_vite 2026-04-28 11:43:14 +08:00
1e4134f8b9 feat: 商品编辑 2026-04-28 11:43:12 +08:00
X1627315083@163.com
27da8f6680 fix 2026-04-28 09:38:15 +08:00
X1627315083@163.com
88c66c634f fix 2026-04-28 09:36:54 +08:00
5cfccabcd7 feat: 返回list列表 2026-04-27 17:23:31 +08:00
04ad920590 Merge branch 'dev_vite' of ssh://18.167.251.121:10002/aidlab/aida_front into dev_vite 2026-04-27 17:18:56 +08:00
05178c4cb0 feat: 提交页面 2026-04-27 16:58:45 +08:00
a385aba49f feat: 接口 2026-04-27 14:39:59 +08:00
李志鹏
813918eb39 Merge branch 'dev_vite' of http://18.167.251.121:10003/aidlab/aida_front into dev_vite 2026-04-27 13:17:47 +08:00
李志鹏
f77a2f2e01 1 2026-04-27 13:17:45 +08:00
X1627315083@163.com
9dc25ddced 增加商品列表和更新商品状态接口 2026-04-27 11:41:14 +08:00
李志鹏
e453d1efd4 Merge branch 'dev_vite' of http://18.167.251.121:10003/aidlab/aida_front into dev_vite 2026-04-27 11:35:10 +08:00
李志鹏
78922a5b91 上传头像 2026-04-27 11:35:09 +08:00
X1627315083@163.com
b04bcb5918 Merge branch 'dev_vite' of ssh://18.167.251.121:10002/aidlab/aida_front into dev_vite 2026-04-27 11:08:07 +08:00
X1627315083@163.com
6d41e8cc34 选择线稿图后下一步 2026-04-27 11:07:51 +08:00
李志鹏
9dca4e3155 Merge branch 'dev_vite' of http://18.167.251.121:10003/aidlab/aida_front into dev_vite 2026-04-27 10:49:38 +08:00
李志鹏
0c140c2896 店铺信息 2026-04-27 10:49:37 +08:00
X1627315083@163.com
bd603e1bf9 fix 2026-04-27 09:50:20 +08:00
f269a40431 Merge branch 'dev_vite' of ssh://18.167.251.121:10002/aidlab/aida_front into dev_vite 2026-04-27 09:40:03 +08:00
55a8675806 feat: sketchDetail接口 2026-04-27 09:40:00 +08:00
X1627315083@163.com
c0e5611897 Merge branch 'dev_vite' of ssh://18.167.251.121:10002/aidlab/aida_front into dev_vite 2026-04-27 09:39:29 +08:00
X1627315083@163.com
821f35b7c7 fix 2026-04-27 09:39:26 +08:00
李志鹏
39c0ee110a 11 2026-04-24 17:36:58 +08:00
李志鹏
22e8efda25 Merge branch 'dev_vite' of http://18.167.251.121:10003/aidlab/aida_front into dev_vite 2026-04-24 17:30:08 +08:00
李志鹏
114a998031 111 2026-04-24 17:30:07 +08:00
X1627315083@163.com
ee4eef1558 fix 2026-04-24 17:28:14 +08:00
李志鹏
a9e5b979a1 Merge branch 'StableVersion' of http://18.167.251.121:10003/aidlab/aida_front into dev_vite 2026-04-24 17:13:42 +08:00
李志鹏
7bb38bf2e5 处理Safari 不支持requestIdleCallback方法 2026-04-24 17:11:48 +08:00
李志鹏
e4fc51c574 卖家端申请接口。人口 2026-04-24 16:57:13 +08:00
李志鹏
071930f257 请求配置更改 2026-04-24 13:31:45 +08:00
李志鹏
136c24ce30 1 2026-04-24 13:08:35 +08:00
李志鹏
2708c5442c 12 2026-04-24 13:07:06 +08:00
李志鹏
0cbd0c848d 11 2026-04-24 13:05:38 +08:00
李志鹏
9845b2ebb0 Merge branch 'dev_vite' of http://18.167.251.121:10003/aidlab/aida_front into dev_vite 2026-04-24 13:04:57 +08:00
李志鹏
41f8bed6a3 ok 2026-04-24 13:04:36 +08:00
X1627315083@163.com
ca21169fda fix 2026-04-24 11:32:12 +08:00
X1627315083@163.com
b1458de4e0 更新接口地址 2026-04-24 11:26:05 +08:00
X1627315083@163.com
743fc762d6 fix 2026-04-23 16:57:03 +08:00
7297e4e7a4 bugfix 2026-04-23 14:08:59 +08:00
3bff1ebb66 feat: 工具函数 2026-04-23 14:05:00 +08:00
0d1656ee0a style: 字体间距 2026-04-23 13:56:54 +08:00
7d0873d874 style: 调整活动页样式 2026-04-23 13:56:47 +08:00
d8caa472ea style: 字体间距 2026-04-23 12:23:51 +08:00
6b138b7a04 style: 调整活动页样式 2026-04-23 11:27:10 +08:00
82941bca7c feat: 活动页 2026-04-23 11:22:21 +08:00
949ff9292d feat: 活动页面文案 2026-04-23 11:10:02 +08:00
19230f6c5a Merge branch 'dev_vite' of ssh://18.167.251.121:10002/aidlab/aida_front into dev_vite 2026-04-23 11:07:26 +08:00
583f61d875 feat: 活动页面文案 2026-04-23 11:07:25 +08:00
X1627315083@163.com
44250f2f79 Merge remote-tracking branch 'origin/StableVersion' into dev_vite 2026-04-22 16:12:55 +08:00
X1627315083@163.com
11c9de8ced fix 2026-04-22 16:12:23 +08:00
X1627315083@163.com
06d78524ed Merge remote-tracking branch 'origin/StableVersion' into dev_vite 2026-04-22 13:24:44 +08:00
X1627315083@163.com
fd518ad9b3 更新youtube地址 2026-04-22 13:24:23 +08:00
19ef5b031e feat: 裁剪 2026-04-22 10:20:49 +08:00
4394158d30 feat: tips 2026-04-21 16:44:27 +08:00
ec632554e2 feat: 裁剪组件 2026-04-21 16:13:55 +08:00
429c7db195 feat: 图片裁剪组件 2026-04-21 13:47:23 +08:00
8a9b217314 feat: 跳转award中文参数改为cn 2026-04-20 17:20:51 +08:00
f9caa4b279 style: 活动文案 2026-04-17 19:04:05 +08:00
68f17ba87b Merge branch 'dev_vite' of ssh://18.167.251.121:10002/aidlab/aida_front into dev_vite 2026-04-17 18:00:06 +08:00
92c789510f style: 活动介绍文字修改 2026-04-17 18:00:04 +08:00
0a442f8132 feat: 裁剪工具 2026-04-17 17:59:51 +08:00
X1627315083@163.com
1fe1bd4aa2 Merge remote-tracking branch 'origin/StableVersion' into dev_vite 2026-04-17 16:36:13 +08:00
X1627315083@163.com
a2b45e2041 Merge branch 'StableVersion' of ssh://18.167.251.121:10002/aidlab/aida_front into StableVersion 2026-04-17 16:35:14 +08:00
X1627315083@163.com
da64b57c1c 登录页路由加语言设置 2026-04-17 16:35:12 +08:00
李志鹏
7ad29081af Merge branch 'dev_vite' of http://18.167.251.121:10003/aidlab/aida_front into dev_vite 2026-04-17 10:17:05 +08:00
李志鹏
ce6522ef90 fix 2026-04-17 10:17:03 +08:00
9758c562c3 Merge branch 'dev_vite' of ssh://18.167.251.121:10002/aidlab/aida_front into dev_vite 2026-04-16 17:31:33 +08:00
9e12d54540 feat: 提交页面 2026-04-16 17:31:31 +08:00
X1627315083@163.com
44abb1b1ee 调整两个拖拽互补干扰 2026-04-16 16:55:52 +08:00
X1627315083@163.com
27b71f1bec Merge branch 'dev_vite' of ssh://18.167.251.121:10002/aidlab/aida_front into dev_vite 2026-04-16 15:58:25 +08:00
X1627315083@163.com
ddb7a366b5 修改loding样式 2026-04-16 15:58:22 +08:00
d4d104c690 feat: edit页面 2026-04-16 14:03:05 +08:00
48516a5f42 style: 修改活动文字以及去除底部黑线 2026-04-16 10:10:16 +08:00
646ecd072c bugfix: 参数拼接 2026-04-15 15:21:45 +08:00
ce0f9f0bbf feat: 开发环境跳转award携带dev参数 2026-04-15 15:01:56 +08:00
0985a004de feat: dev跳转本地 2026-04-15 13:40:32 +08:00
1d9fdfa9f8 Merge branch 'dev_vite' of ssh://18.167.251.121:10002/aidlab/aida_front into dev_vite 2026-04-15 13:16:37 +08:00
f56718e93a feat: 系统消息如果是一个链接则直接跳转 2026-04-15 13:16:35 +08:00
8a9e7205eb feat: 开放event页面 2026-04-15 10:57:58 +08:00
80 changed files with 8079 additions and 4789 deletions

View File

@@ -3,11 +3,13 @@ VITE_USER_NODE_ENV = 'development'
# VITE_APP_BASE_URL = 'https://api.aida.com.hk'
# VITE_APP_BASE_URL = 'http://18.167.251.121:10086'
VITE_APP_BASE_URL = 'https://develop.api.aida.com.hk'
# VITE_APP_BASE_URL = 'https://develop.api.aida.com.hk'
VITE_APP_BASE_URL = 'http://18.167.251.121:10094'
# VITE_APP_BASE_URL = 'https://www.api.aida.com.hk'
# 徐佩
# VITE_APP_BASE_URL = 'http://192.168.31.118:5567'
# 李天祥
# VITE_APP_BASE_URL = 'http://192.168.31.82:5567'
# VITE_APP_BASE_URL = 'http://192.168.31.82:5569'
# 海波
# VITE_APP_BASE_URL = 'http://192.168.31.34:5567'

View File

@@ -1,7 +1,8 @@
VITE_USER_NODE_ENV = 'development'
VITE_USER_NODE_ENV = 'development_cloud'
# VITE_APP_BASE_URL = 'https://aida.com.hk/test'
# VITE_APP_BASE_URL = 'http://18.167.251.121:10088'
# VITE_APP_BASE_URL = 'https://api.aida.com.hk'
VITE_APP_BASE_URL = 'https://develop.api.aida.com.hk'
# VITE_APP_BASE_URL = 'https://develop.api.aida.com.hk'
VITE_APP_BASE_URL = 'https://www.develop-ms.api.aida.com.hk'
# VITE_APP_BASE_URL = 'http://localhost:22170'

4
.gitignore vendored
View File

@@ -24,4 +24,6 @@ dist.rar
*.sw?
.eslintrc-auto-import.json
components.d.ts
.cursor
.cursor
*.zip
*.7z

View File

@@ -1,33 +0,0 @@
/** @type {import('prettier').Config} */
module.exports = {
// 打印宽度
printWidth: 100,
// 使用 4 空格缩进
tabWidth: 4,
// 使用 4 空格缩进,不使用制表符
useTabs: true,
// 行尾使用 LF (Unix 风格)
endOfLine: 'lf',
// 语句末尾使用分号
semi: false,
// 使用单引号
singleQuote: false,
// 对象和数组末尾不添加尾随逗号
trailingComma: 'none',
// JSX 引号使用单引号
jsxSingleQuote: false,
// 括号内侧空格
bracketSpacing: true,
// JSX 标签不换行
bracketSameLine: false,
// 箭头函数参数始终使用括号
arrowParens: 'always',
// HTML、Vue、Angular 和 Markdown 使用 LF
htmlWhitespaceSensitivity: 'css',
// Vue 文件脚本和样式缩进
vueIndentScriptAndStyle: false,
// 行注释位置在注释上方,不加空格
proseWrap: 'preserve',
// 根据文件类型自动推断
embeddedLanguageFormatting: 'auto',
};

17
.prettierrc.json Normal file
View File

@@ -0,0 +1,17 @@
{
"printWidth": 100,
"tabWidth": 4,
"useTabs": true,
"endOfLine": "lf",
"semi": false,
"singleQuote": false,
"trailingComma": "none",
"jsxSingleQuote": false,
"bracketSpacing": true,
"bracketSameLine": false,
"arrowParens": "always",
"htmlWhitespaceSensitivity": "css",
"vueIndentScriptAndStyle": true,
"proseWrap": "preserve",
"embeddedLanguageFormatting": "auto"
}

View File

@@ -167,8 +167,7 @@ li {
padding: 0.6rem 0.5rem;
}
.ant-modal-mask {
background-color: #666666;
opacity: 0.5;
background-color: rgba(102, 102, 102, 0.5);
}
.select_block {
height: 4rem;
@@ -1251,8 +1250,8 @@ tr > .ant-picker-cell-in-view.ant-picker-cell-range-hover-start:last-child::afte
border-color: #000 !important;
}
.ant-spin .ant-spin-dot {
width: 1.5em;
height: 1.5em;
width: 4.5rem;
height: 4.5rem;
}
.ant-spin-dot-item {
background-color: #000000 !important;

View File

@@ -0,0 +1,3 @@
<svg width="14" height="14" viewBox="0 0 14 14" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M13.4872 5.28094L9.06266 0.853126C8.51506 0.306793 7.7733 0 7 0C6.2267 0 5.48494 0.306793 4.93734 0.853126L0.512757 5.28094C0.349671 5.44308 0.220373 5.636 0.132355 5.84851C0.0443375 6.06102 -0.000647733 6.2889 7.04636e-06 6.51894V12.249C7.04636e-06 12.7134 0.184381 13.1587 0.51257 13.4871C0.840758 13.8155 1.28588 14 1.75001 14H12.25C12.7141 14 13.1592 13.8155 13.4874 13.4871C13.8156 13.1587 14 12.7134 14 12.249V6.51894C14.0006 6.2889 13.9557 6.06102 13.8676 5.84851C13.7796 5.636 13.6503 5.44308 13.4872 5.28094ZM8.75 12.8326H5.25V10.5364C5.25 10.072 5.43438 9.62663 5.76256 9.29825C6.09075 8.96986 6.53587 8.78538 7 8.78538C7.46413 8.78538 7.90925 8.96986 8.23744 9.29825C8.56562 9.62663 8.75 10.072 8.75 10.5364V12.8326ZM12.8333 12.249C12.8333 12.4038 12.7719 12.5522 12.6625 12.6617C12.5531 12.7711 12.4047 12.8326 12.25 12.8326H9.91666V10.5364C9.91666 9.76241 9.60937 9.0201 9.06239 8.47279C8.51541 7.92549 7.77355 7.61801 7 7.61801C6.22645 7.61801 5.48459 7.92549 4.93761 8.47279C4.39063 9.0201 4.08334 9.76241 4.08334 10.5364V12.8326H1.75001C1.5953 12.8326 1.44692 12.7711 1.33753 12.6617C1.22813 12.5522 1.16667 12.4038 1.16667 12.249V6.51894C1.16721 6.36425 1.22862 6.216 1.33759 6.10627L5.76217 1.6802C6.09099 1.35272 6.53605 1.16887 7 1.16887C7.46394 1.16887 7.90901 1.35272 8.23783 1.6802L12.6624 6.10802C12.771 6.21732 12.8323 6.36486 12.8333 6.51894V12.249Z" fill="#585858"/>
</svg>

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 686 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 73 KiB

View File

@@ -9,6 +9,11 @@
"id": 2,
"title": "AiDA X SFT AI Fashion Award 2024",
"imgUrl": "/image/events/Fashion-Award-2024.png"
},
{
"id": 3,
"title": "AiDA Global Design Awards 2026",
"imgUrl": "/image/events/award-poster.gif"
}
],
"eventsItem": [
@@ -16,18 +21,19 @@
"id": 3,
"title": "AiDA Global Design Awards 2026",
"imgUrl": "/image/events/award-poster.gif",
"tips": "For inquiries: awards2026@code-create.com.hk",
"textList": [
{
"paragraph": [
{
"text": "Click the “View Details” button for more information and to join the competition! The AiDA Global Design Award 2026 is an international design competition hosted by CodeCreate, a globally leading AI fashion solutions provider, celebrating the future of creativity powered by artificial intelligence. Open to designers from Hong Kong, China, Singapore, South Korea, and beyond, the competition brings together global talent, empowering AI as a creative partner—pushing fashion beyond traditional boundaries and unlocking new possibilities where technology amplifies human imagination."
"text": "Click the “View Details” button for more information and to join the competition! The AiDA Global Design Award 2026 is an international design competition hosted by CodeCreate, a globally leading AI fashion solutions provider, celebrating the future of creativity powered by artificial intelligence. Open to designers worldwide the competition brings together global talent, empowering AI as a creative partner—pushing fashion beyond traditional boundaries and unlocking new possibilities where technology amplifies human imagination."
}
]
},
{
"paragraph": [
{
"text": "Participants have the opportunity to compete for cash prizes totaling up to US$9,000, gain global media exposure showcased by top international platforms, and connect with designers and industry leaders worldwide. Finalists will also attend an exclusive award ceremony in Hong Kong, with travel support provided, allowing them to showcase their talent, network with professionals, and celebrate their achievements on an international stage."
"text": "Participants have the opportunity to compete for cash prizes totaling up to US$9,000, gain global media exposure showcased by top international platforms, and connect with designers and industry leaders worldwide. Finalists will also attend an exclusive award ceremony in Hong Kong, with travel allowance, allowing them to showcase their talent, network with professionals, and celebrate their achievements on an international stage."
}
]
}

View File

@@ -9,6 +9,11 @@
"id": 2,
"title": "AiDA X SFT AI时尚设计比赛2024",
"imgUrl": "/image/events/Fashion-Award-2024.png"
},
{
"id": 3,
"title": "AiDA全球设计奖 2026",
"imgUrl": "/image/events/award-poster-zh.gif"
}
],
"eventsItem": [
@@ -16,18 +21,19 @@
"id": 3,
"title": "AiDA全球设计奖 2026",
"imgUrl": "/image/events/award-poster-zh.gif",
"tips": "如有疑问请联系awards2026@code-create.com.hk",
"textList": [
{
"paragraph": [
{
"text": "秉承推动 AI 赋能创意设计的初衷CodeCreate 举办了「AiDA 全球设计大奖 2026」面向来自香港、中国、新加坡、韩国及全球的设计师,鼓励大家探索 AI 与时尚设计的无限可能,突破传统界限,释放科技与想象力的创新潜能。点击“查看详情”按钮获取更多比赛信息,抓住成为 AI 时尚先锋的机会吧!"
"text": "秉承推动 AI 赋能创意设计的初衷CodeCreate 举办了「AiDA 全球设计大奖 2026」面向来全球的设计师鼓励大家探索 AI 与时尚设计的无限可能,突破传统界限,释放科技与想象力的创新潜能。点击“查看详情”按钮获取更多比赛信息,抓住成为 AI 时尚先锋的机会吧!"
}
]
},
{
"paragraph": [
{
"text": "参赛者将有机会赢取总奖金 9,000 美元,作品还将获得国际媒体展示机会,并与全球设计师和行业领袖建立联系。入围决赛者将受邀参加在香港举办的 专属颁奖典礼,主办方提供差旅支持,让设计师在国际舞台展示才华、拓展人脉,并共同庆祝创意成果。"
"text": "参赛者将有机会赢取总奖金 9,000 美元,作品还将获得国际媒体展示机会,并与全球设计师和行业领袖建立联系。入围决赛者将受邀参加在香港举办的 专属颁奖典礼,主办方提供差旅津贴,让设计师在国际舞台展示才华、拓展人脉,并共同庆祝创意成果。"
}
]
}

View File

@@ -1251,8 +1251,8 @@ tr > .ant-picker-cell-in-view.ant-picker-cell-range-hover-start:last-child::afte
border-color: #000 !important;
}
.ant-spin .ant-spin-dot {
width: 1.5em;
height: 1.5em;
width: 4.5rem;
height: 4.5rem;
}
.ant-spin-dot-item {
background-color: #000000 !important;
@@ -2470,6 +2470,22 @@ textarea:focus {
opacity: 0.8;
border-radius: 0.7rem;
}
.mini-scrollbar::-webkit-scrollbar {
width: 0.4rem;
}
.mini-scrollbar::-webkit-scrollbar-thumb {
border-radius: 0.4rem;
background: rgba(0, 0, 0, 0.2);
}
.mosaic-bg {
--mosaic-bg-size: 1rem;
--mosaic-bg-color1: #efefef;
--mosaic-bg-color2: #fff;
background-image: repeating-conic-gradient(var(--mosaic-bg-color1) 0% 25%, var(--mosaic-bg-color2) 0% 50%);
background-repeat: repeat;
background-position: 50% 50%;
background-size: var(--mosaic-bg-size) var(--mosaic-bg-size);
}
.mark_loading {
position: fixed;
width: 100%;
@@ -2507,6 +2523,6 @@ textarea:focus {
.justify-center {
justify-content: center;
}
.flex-1{
flex: 1;
}
.flex-1 {
flex: 1;
}

View File

@@ -172,8 +172,9 @@ input:focus{
}
}
.ant-modal-mask{
background-color: #666666;
opacity: .5;
background-color: rgba(102,102,102,0.5);
// background-color: #666666;
// opacity: .5;
}
.select_block{
height: 4rem;
@@ -1379,8 +1380,8 @@ tr > .ant-picker-cell-in-view.ant-picker-cell-range-hover-start:last-child::afte
}
//loding样式
.ant-spin .ant-spin-dot{
width: 1.5em;
height: 1.5em;
width: 4.5rem;
height: 4.5rem;
}
.ant-spin-dot-item{
background-color: #000000 !important;

View File

@@ -1,22 +1,37 @@
<template>
<div class="account_systemMessage">
<div class="account_systemMessage">
<div class="account_generalMessage_title modal_title_text">
<!-- <span>系统消息</span> -->
<div class="account_generalMessage_title_setting" @click="allRead">{{$t('account.AllRead')}}</div>
<div class="account_generalMessage_title_setting" @click="allRead">
{{ $t("account.AllRead") }}
</div>
</div>
<div class="account_generalMessage_item modal_title_text" v-for="item in dataList" :key="item.id" @click="setRead(item)">
<div
class="account_generalMessage_item modal_title_text"
v-for="item in dataList"
:key="item.id"
@click="setRead(item)"
>
<a-badge :dot="item.isRead == 0"></a-badge>
<div class="account_generalMessage_item_title">
<div class="account_generalMessage_item_title_text" :title="item.content">{{ item.content.title }}</div>
<div class="account_generalMessage_item_title_text" :title="item.content">
{{ item.content.title }}
</div>
<div class="modal_title_text_intro">{{ item.createTime }}</div>
</div>
<div class="modal_title_text_intro">
{{ item.content.content }}
<span v-if="item.content.link" class="account_generalMessage_item_link">{{ item.content.link }}</span>
<span v-if="item.content.link" class="account_generalMessage_item_link">{{
item.content.link
}}</span>
</div>
</div>
<div class="account_generalMessage_item modal_title_text" style="display:flex;justify-content: center;" v-if="dataList.length == 0 && isNoData">
{{$t('account.dataNull')}}
<div
class="account_generalMessage_item modal_title_text"
style="display: flex; justify-content: center"
v-if="dataList.length == 0 && isNoData"
>
{{ $t("account.dataNull") }}
</div>
<div class="page_loading_box" v-show="!isNoData">
<span class="page_loading" ref="loadingDom" v-show="!isShowMark"></span>
@@ -24,120 +39,138 @@
<a-spin size="large" />
</span>
</div>
</div>
</div>
</template>
<script lang="ts">
import { defineComponent,computed,ref,reactive,nextTick,toRefs,createVNode, onMounted} from 'vue'
import { Https } from "@/tool/https";
import { useRouter,useRoute } from 'vue-router'
import { useStore } from "vuex";
import { useI18n } from 'vue-i18n'
import {
defineComponent,
computed,
ref,
reactive,
nextTick,
toRefs,
createVNode,
onMounted
} from "vue"
import { Https } from "@/tool/https"
import { useRouter, useRoute } from "vue-router"
import { useStore } from "vuex"
import { useI18n } from "vue-i18n"
import { isValidUrl } from "@/tool/util"
export default defineComponent({
components:{
},
components: {},
// emits:['putListData'],
props:['setReadStatus','setAllmessage','getHistory'],
setup(prop,{emit}) {
props: ["setReadStatus", "setAllmessage", "getHistory"],
setup(prop, { emit }) {
const router = useRouter()
const store = useStore();
const store = useStore()
let accountMessage = reactive({
dataList: [],
page:1,
size:10,
page: 1,
size: 10,
isNoData: false,
isShowMark: false,
isShowMark: false
})
let loadingDom:any = ref(null)
let setmessageList = ()=>{
let loadingDom: any = ref(null)
let setmessageList = () => {
accountMessage.isShowMark = true
let data = {
page: accountMessage.page,
size: accountMessage.size,
size: accountMessage.size
}
prop.getHistory(data).then((rv:any)=>{
accountMessage.isShowMark = false
if(rv.content.length == 0) {
prop.getHistory(data)
.then((rv: any) => {
accountMessage.isShowMark = false
if (rv.content.length == 0) {
accountMessage.isNoData = true
} else {
rv.content.forEach((item: any) => {
item.content = JSON.parse(item.content)
})
accountMessage.dataList.push(...rv.content)
}
})
.catch(() => {
accountMessage.isShowMark = false
accountMessage.isNoData = true
}else{
rv.content.forEach((item:any) => {
item.content = JSON.parse(item.content)
});
accountMessage.dataList.push(...rv.content)
})
}
let setRead = (item: any) => {
let content = item.content.content
if (isValidUrl(content)) {
if (import.meta.env.VITE_APP_BASE_URL === "https://develop.api.aida.com.hk") {
content += "&env=dev"
}
}).catch(() => {
accountMessage.isShowMark = false
accountMessage.isNoData = true
})
}
let setRead = (item:any)=>{
prop.setReadStatus(item).then((rv:any)=>{
item.isRead = 1
}).catch((err:any)=>{
})
}
let allRead = ()=>{
// emit('setAllmessage')
prop.setAllmessage().then(()=>{
accountMessage.dataList.forEach((item:any)=>{
window.open(content, "_blank")
}
prop.setReadStatus(item)
.then((rv: any) => {
item.isRead = 1
})
}).catch((err:any)=>{
})
.catch((err: any) => {})
}
let allRead = () => {
// emit('setAllmessage')
prop.setAllmessage()
.then(() => {
accountMessage.dataList.forEach((item: any) => {
item.isRead = 1
})
})
.catch((err: any) => {})
}
// provide('exhibitionList',exhibitionList)
onMounted (()=>{
onMounted(() => {
accountMessage.isNoData = false
accountMessage.page = 0
let imgParent:any = document.querySelector('.account_systemMessage .page_loading')
let imgParent: any = document.querySelector(".account_systemMessage .page_loading")
new IntersectionObserver(
(entries, observer) => {
// 如果不是相交,则直接返回
// console.log(entries[0]);
if (!entries[0].intersectionRatio) return;
accountMessage.page+=1
if (!entries[0].intersectionRatio) return
accountMessage.page += 1
setmessageList()
},
}
// { root:worksPage }
).observe(loadingDom.value);
).observe(loadingDom.value)
})
return{
...toRefs(accountMessage),
setmessageList,
setRead,
allRead,
loadingDom,
}
},
data(){
return{
}
},
return {
...toRefs(accountMessage),
setmessageList,
setRead,
allRead,
loadingDom
}
},
data() {
return {}
}
})
</script>
<style lang="less" scoped>
.account_systemMessage{
.account_systemMessage {
width: 100%;
.account_generalMessage_item{
.account_generalMessage_item {
font-size: var(--aida-fsize1-6);
.account_generalMessage_item_title{
.account_generalMessage_item_title {
display: flex;
align-items: center;
margin-bottom: 2rem;
.account_generalMessage_item_title_text{
.account_generalMessage_item_title_text {
max-width: 80%;
white-space: nowrap;
overflow: hidden;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.modal_title_text_intro{
.modal_title_text_intro {
margin-left: 4rem;
}
}
}
.modal_title_text_intro{
.modal_title_text_intro {
word-break: break-word;
white-space: pre-wrap;
font-family: Arial, sans-serif;

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -409,7 +409,7 @@ export class BrushIndicator {
// this.show(e.e);
this._mouseEnterHandler && this._mouseEnterHandler(e)
} else {
// requestIdleCallback(() => {
// setTimeout(() => {
// this.updatePosition(e.e);
// });

View File

@@ -133,11 +133,12 @@ export class RedGreenModeManager {
this.canvas.on("mouse:up", (event) => {
// 可以在这里添加更多逻辑,比如生成图片或更新状态
nextTick(() => {
requestIdleCallback(async () => {
setTimeout(async () => {
if (!this.isInitialized) {
console.warn("红绿图模式未初始化,无法处理鼠标事件");
return;
}
console.log("鼠标抬起事件触发", this.onImageGenerated);
if (this.onImageGenerated) {
const imageData = await this.canvasManager.exportImage({
restoreOpacityInRedGreen: true, // 恢复红绿图模式下的透明度

View File

@@ -37,7 +37,6 @@ export class ThumbnailManager {
// 延迟执行避免阻塞UI
fabricObjects.length > 0 &&
requestIdleCallback(() => {
setTimeout(async () => {
const base64 = await this._generateLayerThumbnailNow(fabricObjects, layer);
// this.layerThumbnails.set(layerId, base64);
@@ -55,7 +54,6 @@ export class ThumbnailManager {
console.error("生成图层缩略图时出错:", error);
}
});
});
}
/**
@@ -65,7 +63,7 @@ export class ThumbnailManager {
generateAllLayerThumbnails(layers) {
if (!layers || !Array.isArray(layers)) return;
requestIdleCallback(() => {
setTimeout(() => {
setTimeout(() => {
layers.forEach((layer) => {
if (layer && layer.id) {

View File

@@ -155,7 +155,7 @@ export class LiquifyRealTimeUpdater {
// 使用requestAnimationFrame进行批量渲染优化
// if (!this.renderingScheduled && !this.config.skipRenderDuringDrag) {
// this.renderingScheduled = true;
// requestIdleCallback(() => {
// setTimeout(() => {
// this.canvas.renderAll();
// this.renderingScheduled = false;
// });

View File

@@ -12,7 +12,7 @@
<div class="upload_item">
<div class="upload_file_item upload_component">
<a-upload
:action="uploadUrl + (upLoadHttpsUrl?upLoadHttpsUrl:'/api/element/upload')"
:action="uploadUrl + (upLoadHttpsUrl?upLoadHttpsUrl:'/aida/api/element/upload')"
list-type="picture-card"
:capture="null"
:multiple="true"

View File

@@ -233,10 +233,10 @@ export default defineComponent({
let sketchH = editPrintElementData.sketchWH.height * editPrintElementData.sketchWH.scale[1]
let x = sketchW / 2 - (sketchW * (width / editPrintElementData.sketchWH.width)/2)
let y = sketchH / 2 -(sketchH * height/2)
if(editPrintElementData.stateOverallSingle !== 'single'){
x = sketchW / 2
y = sketchH / 2
}
// if(editPrintElementData.stateOverallSingle !== 'single'){
// x = sketchW / 2
// y = sketchH / 2
// }
let location = [x,y]
resolve({scale,location})
}

View File

@@ -189,11 +189,11 @@ export default defineComponent({
}
return { scaleX, scaleY, rotate };
}
const initMoveableForSelected = () => {
const initMoveableForSelected = async (isDestroy:boolean = false) => {
// 销毁旧的实例
if(selectItem.imgDomIndex == -1)return
if (moveableInstance.value) {
moveableInstance.value.destroy();
if (moveableInstance?.value?.destroy && !isDestroy) {
moveableInstance?.value?.destroy();
}
const selectedEl = elementRefs.value[selectItem.imgDomIndex];
@@ -509,7 +509,7 @@ export default defineComponent({
watch(()=>detailData.frontBack.front.length,(newValue,oldValue)=>{
if(selectItem.selectDetail?.id)selectItem.imgDomIndex = detailData.frontBack.front.findIndex((item:any)=>item.id == selectItem.selectDetail?.id)
setTimeout(()=>{
initMoveableForSelected()
initMoveableForSelected(oldValue == 0)
},100)
})
const setRevocation = async ()=>{

View File

@@ -21,7 +21,11 @@
{{ $t("event.detail") }}
</div>
</div>
<div class="modal_title_text content" v-for="item in eventsDetail.textList">
<div
class="modal_title_text content"
v-for="item in eventsDetail.textList"
:class="{ award: eventsDetail.id === 3 }"
>
<div class="eventsDetail_content_right_btn_box">
<div
class="eventsDetail_content_right_btn"
@@ -43,6 +47,7 @@
v-detailText="introItem.text"
></div>
</div>
<div class="tips" v-if="eventsDetail.tips">{{ eventsDetail.tips }}</div>
</div>
</div>
</div>
@@ -120,11 +125,16 @@ export default defineComponent({
})
}
const openDetail = () => {
let language = locale.value === "ENGLISH" ? "en" : "zh"
let language = locale.value === "ENGLISH" ? "en" : "cn"
let url = `https://aida-global-design-awards.com.hk/${language}`
// 如果是dev环境把域名换成http://192.168.31.198
if (import.meta.env.VITE_APP_BASE_URL === "https://develop.api.aida.com.hk") {
url += "?env=dev"
}
window.open(url, "_blank")
// router.push("/award/index")
// router.push("/award/index")
}
onMounted(() => {
const currentLocale = locale.value
@@ -233,7 +243,7 @@ export default defineComponent({
}
.eventsDetail_content_right {
.modal_title_text {
letter-spacing: 0.4rem;
letter-spacing: 0.3rem;
font-weight: 600;
&-header {
display: flex;
@@ -265,18 +275,21 @@ export default defineComponent({
.eventsDetail_content_right_btn_box {
display: flex;
justify-content: space-evenly;
.eventsDetail_content_right_btn {
}
// .eventsDetail_content_right_btn {
// }
}
}
.modal_title_text:last-child {
}
// .modal_title_text:last-child {
// }
.modal_title_text:last-child::after {
content: "";
display: block;
border-top: 3px solid;
height: 6rem;
}
.modal_title_text.award:last-child:after {
display: none;
}
}
}
}
@@ -293,4 +306,13 @@ export default defineComponent({
white-space: nowrap;
cursor: pointer;
}
.tips{
color: rgba(0, 0, 0, 0.45);
font-size: var(--aida-fsize1-4);
font-weight: 400;
letter-spacing: 0.3rem;
}
.modal_title_text.content.award{
line-height: 1.3;
}
</style>

View File

@@ -63,7 +63,7 @@
<a-upload
class="search_upImg"
:capture="null"
:action="uploadUrl + '/api/element/upload'"
:action="uploadUrl + '/aida/api/element/upload'"
list-type="picture-card"
:data="{
...upload
@@ -161,7 +161,7 @@
<a-upload
class="search_upImg"
:capture="null"
:action="uploadUrl + '/api/element/upload'"
:action="uploadUrl + '/aida/api/element/upload'"
list-type="picture-card"
:data="{
...upload

View File

@@ -24,7 +24,7 @@
<div class="upload_file_item upload_component" v-show="fileList.length < 15">
<a-upload
:capture="null"
:action="uploadUrl + '/api/element/upload'"
:action="uploadUrl + '/aida/api/element/upload'"
list-type="picture-card"
:data="{
...upload

View File

@@ -13,7 +13,7 @@
</div>
<div class="upload_file_item upload_component" v-show="fileList.length < 10">
<a-upload
:action="uploadUrl + '/api/element/upload'"
:action="uploadUrl + '/aida/api/element/upload'"
:capture="null"
list-type="picture-card"
:data="{

View File

@@ -103,7 +103,7 @@
</div>
<a-upload
:capture="null"
:action="uploadUrl + '/api/element/upload'"
:action="uploadUrl + '/aida/api/element/upload'"
list-type="picture-card"
:data="{
...upload,

View File

@@ -45,7 +45,7 @@
<div class="upload_item" v-show="uploadList.length < 1">
<div class="upload_file_item upload_component">
<a-upload
:action="uploadUrl + '/api/history/brandLogoUpload'"
:action="uploadUrl + '/aida/api/history/brandLogoUpload'"
list-type="picture-card"
:capture="null"
:headers="{ Authorization: token }"

View File

@@ -213,10 +213,10 @@
</div>
</div>
<div class="upload_file_item upload_component">
<!-- :action="uploadUrl + '/api/history/toProductImageElementUpload'" -->
<!-- :action="uploadUrl + '/aida/api/history/toProductImageElementUpload'" -->
<a-upload
:action="
getUploadUrl() + '/api/history/toProductImageElementUpload'
getUploadUrl() + '/aida/api/history/toProductImageElementUpload'
"
list-type="picture-card"
:capture="null"

View File

@@ -60,7 +60,7 @@
>
<a-upload
:action="uploadUrl + '/api/element/upload'"
:action="uploadUrl + '/aida/api/element/upload'"
list-type="picture-card"
:capture="null"
:data="{

View File

@@ -54,7 +54,7 @@
</div>
<div class="upload_file_item upload_component" v-show="printboardList.length < 16">
<a-upload
:action="uploadUrl + '/api/element/upload'"
:action="uploadUrl + '/aida/api/element/upload'"
:capture="null"
list-type="picture-card"
:before-upload="beforeUpload"
@@ -415,7 +415,7 @@ export default defineComponent({
file:data.originFileObj
}
let fileUid = data.uid
Https.axiosPost('/api/element/upload', new_data,{headers:{'Content-Type': 'multipart/form-data'}}).then(
Https.axiosPost('/aida/api/element/upload', new_data,{headers:{'Content-Type': 'multipart/form-data'}}).then(
(rv: any) => {
if (rv) {
for(let file of this.fileList){
@@ -491,7 +491,7 @@ export default defineComponent({
}
this.isUpload = true
const hide = message.loading('loading', 0);
Https.axiosPost('/api/element/upload', new_data,{headers:{'Content-Type': 'multipart/form-data'}}).then(
Https.axiosPost('/aida/api/element/upload', new_data,{headers:{'Content-Type': 'multipart/form-data'}}).then(
(rv: any) => {
for(let file of this.fileList){
if(fileData.uid === file.uid){

View File

@@ -70,7 +70,7 @@
v-show="sketchboardList.length < 20"
>
<a-upload
:action="uploadUrl + '/api/element/upload'"
:action="uploadUrl + '/aida/api/element/upload'"
:capture="null"
list-type="picture-card"
:data="{

View File

@@ -16,7 +16,7 @@
<keep-alive :include="cachedRoutes">
<component
:is="Component"
:key="route.name"
:cachedRoutes="cachedRoutes"
/>
</keep-alive>
</router-view>

View File

@@ -11,7 +11,7 @@
:options="[{value:'product',label:'product'},{value:'sketch',label:'sketch'}]"
></a-select>
</div> -->
<selectList @selectImgItem="selectImgItem" :deReconstructionList="segmentationTypeList" :isSegmentation="true" upLoadHttpsUrl="/api/element/imageSegmentation" level1Type="Sketchboard" :randomId="false" type="sketch" :catecoryList="sketchCatecoryList">
<selectList @selectImgItem="selectImgItem" :deReconstructionList="segmentationTypeList" :isSegmentation="true" upLoadHttpsUrl="/aida/api/element/imageSegmentation" level1Type="Sketchboard" :randomId="false" type="sketch" :catecoryList="sketchCatecoryList">
<template v-slot:selectSex>
<div class="generalModel_state" style="margin-left: auto">
<div class="generalModel_state_item smail" style="margin-right: 0">

View File

@@ -390,7 +390,7 @@ export default defineComponent({
// //fetch get请求携带token
// fetch("https://develop.api.aida.com.hk/api/project/getThreeDGlb?threeDSimpleId=1", {
// fetch("https://develop.api.aida.com.hk/aida/api/project/getThreeDGlb?threeDSimpleId=1", {
// headers:{
// authorization:'Bearer-eyJhbGciOiJIUzUxMiJ9.eyJqdGkiOiI4OCIsInN1YiI6IntcImNvdW50cnlcIjpcIkNoaW5hXCIsXCJpZFwiOjg4LFwibGFuZ3VhZ2VcIjpcIkVOR0xJU0hcIixcInVzZXJuYW1lXCI6XCJzaGJcIn0iLCJpYXQiOjE3NDMzNDkwNjQsImlzcyI6IkRXSiIsImF1dGhvcml0aWVzIjoiW10iLCJleHAiOjE3NTE5ODkwNjR9.gmL0JufYy9wd23qCY-ibwhgpXZ2X68WAiHSeC99I4x7cipWyxLaQmuIBk2SJSdWBm0tTN2Mx-etXO9a7MtQmpw',
// }

View File

@@ -78,7 +78,7 @@
<div class="upload_file_item">
<a-upload
key="common"
:action="getUploadUrl() + '/api/history/toProductImageElementUpload'"
:action="getUploadUrl() + '/aida/api/history/toProductImageElementUpload'"
list-type="picture-card"
:capture="null"
:data="{
@@ -111,7 +111,7 @@
<a-upload
key="lastframes"
:action="
getUploadUrl() + '/api/history/toProductImageElementUpload'
getUploadUrl() + '/aida/api/history/toProductImageElementUpload'
"
list-type="picture-card"
:capture="null"

View File

@@ -50,7 +50,7 @@
<div class="upload_item item" v-show="!isDesignPage">
<div class="upload_file_item">
<a-upload
:action="uploadUrl + '/api/history/toProductImageElementUpload'"
:action="uploadUrl + '/aida/api/history/toProductImageElementUpload'"
list-type="picture-card"
:capture="null"
:data="{

File diff suppressed because it is too large Load Diff

View File

@@ -24,6 +24,7 @@ export default {
SubscribeNow: 'Subscribe now',
TaskList: 'Task List',
ViewOrders: 'View Orders',
PersonalCenter: 'Personal Center',
BecomeSeller: 'Become a Seller',
SellerDashboard: 'Seller Dashboard',
toolsToProduct: 'To product image',
@@ -1788,16 +1789,157 @@ export default {
GetStarted: 'Get Started',
},
SellerListEdit:{
saveDraft:'Save Draft',
saveDraft:'Save All New',
publish:'Publish',
sketch:'Sketch',
mainProductImage:'Main Product Image',
cover:'Cover',
productImageDesc:'Choose from product image',
cropDesc:'Crop from main product image or sketch',
productImageMainTitle:'Product Image ',
productImageMainTitle:'Product Media ',
productImageSubTitle:'(from design collection)',
apparelSketchTitle:'Apparel Sketch ',
apparelSketchSubTitle:'(from design collection)',
productName:'Product Name',
price:'Price',
productDescription:'Product Description',
designFor:'Designed For',
productCategory:'Product Category',
categoryTips:'select all that apply',
policy:'By default, all sales follow the platform\'s licensing policy — buyers will receive a usage license upon download.',
learnMore:'Learn more',
requiredFieldTips:'Please fill in {field}',
requiredFieldTipsWithPage:'Listing {index}: Please fill in {field}',
draftSaved: 'Draft Saved',
draftDesc: 'Your listing has been saved as a draft. \nYou can continue editing or publish it later from My Listings.',
listingLive:'Listing Live',
publishDesc:'Your listing is now live on the marketplace.\nBuyers can discover and purchase your design.'
},
Seller: {
brandProfile: "Brand Profile",
myListings: "My Listings",
myOrders: "My Orders",
settings: "Settings",
// Brand Profile
brandProfileBgNullTip: "Your brand banner has not been set up yet.",
changeBrandBanner: "Change Brand Banner",
edit: "Edit",
cancel: "Cancel",
confirm: "Confirm",
saveChange: "Save Change",
brandProfileEditTip: "Changes will be reflected on your Stylish Parade brand page.",
storeName: "Store Name",
storeNameDesc: "Enter your store name",
ownerName: "Owners Full Name",
ownerNameDesc: "Enter store owner's full name",
email: "Email",
emailDesc: "Enter email",
mobile: "Phone Number",
mobileDesc: "Enter phone number",
link: "Link {index}",
links: "Portfoilo/Social Media Links",
storeDescription: "Store Description",
storeDescriptionDesc: "Briefly describe your design style and store features...",
storeDescriptionErr: "Please enter store description",
cropBrandBanner: "Crop Brand Banner",
cropAvatar: "Crop Avatar",
cropFrom: "Crop from:",
sketch: "Sketch",
mainProductImage: "Main Product Image",
cropPreview: "Crop Preview",
imageClipCoverTip: "Align crown to top, mid-thigh to bottom for best results.",
imageClipMainProductImageTip: "Align crown to top, foot base to bottom for best results.",
imageClipSketchTip: "Align crown to top, foot base to bottom for best results.",
imageClipApparelTip: "Trim whitespace and center your apparel sketch.",
// 我的订单
totalRevenue: "Total Revenue",
totalPurchases: "Total Purchases",
totalViews: "Total Views",
allInvoice: "All Invoice",
myOrdersTip: "A summary of all completed transactions.",
myOrdersSearchPlaceholder: "Search by item name or order ID",
orderId: "Order ID",
item: "Item",
price: "Price",
buyerUsername: "Buyer Username",
date: "Date",
// 设置
notifications: "Notifications",
notificationsTitle: "New order notification",
notificationsTip: "Receive an inbox message when a new order is placed.",
payout: "Payout",
payoutTitle: "Payment Providers",
payoutTip: "Select how you want to receive payments.",
unbound: "Unbound",
manage: "Manage",
bindNow: "Bind Now",
paymentCurrency: "Payment Currency",
HKD: "HKD - Hong Kong Dollar",
fixed: "Fixed",
dataPrivacy: "Data & Privacy",
dataPrivacyTitle: "Copyright licence",
dataPrivacyTip1: "A licence certificate is automatically included with every purchase download. View the default licensing terms applied to your listings.",
dataPrivacyTip2: "BThis licence is issued by Code-Create and is legally binding upon purchase. It certifies the buyer's right to use the purchased design asset in accordance with the terms below.",
dataPrivacyTip3: "For custom licensing arrangements, <span onclick='{click}()'>contact us</span>.",
downloadToView: "Download to View",
stopSelling: "Stop Selling",
stopSellingTitle: "Deactivate seller account",
stopSellingTip: "Permanently deactivate your seller account. All listings and invoice records will be deleted. You may re-register as a seller in the future, but your previous sales data cannot be recovered.",
deactivate: "Deactivate",
newListing: "New Listing",
draftMessage: 'Product moved to drafts and stats reset.',
publishMessage: 'Item is now live on the Marketplace.',
ActiveListings: 'Active Listings',
Praka: 'Praka',
Drafts: 'Drafts',
Cancel: 'Cancel',
Delete: 'Delete',
DeleteConfirm: 'Delete this listing?',
DeleteDesc: 'Your listing and its details will be permanently removed.',
Edit: 'Edit',
Draft: 'Draft',
Publish: 'Publish',
sketchesSelected: 'Sketches selected',
Next: 'Next',
All: 'All',
SeriesDesign: 'Series Design',
SingleDesign: 'Single Design',
sketchs: 'sketches',
MyListings: 'My Listings',
MyListingsInfo: 'Active listings and unpublished inventory',
SelectCollection: 'Select Collection',
SelectSketch: 'Select Sketch',
EditListingDetails: 'Edit Listing Details',
VideoWarning:'The first selected item is the main product image. Videos cannot be used.',
selectSketchMaxNum: 'Select up to 9 sketches',
},
ApplySeller: {
applySellerTitle: 'Apply to Become a Seller',
applySellerDesc: 'Join the Stylish Parade and start selling your design work',
formTitle: 'Brand Information',
formTip: 'Share a few details to set up your seller profile',
termsTitle: 'Seller Terms',
termsTip: 'Please carefully read and agree to the following terms',
agreementTitle: 'AiDA Seller Agreement',
agreementTip: 'Please read and agree to the following agreement',
agreementItem1: "Provide accurate and truthful personal and store information",
agreementItem2: "Only sell original designs or content with proper licensing",
agreementItem3: "Maintain high quality standards for all products",
agreementItem4: "Respond to customer inquiries within 48 hours",
agreementItem5: "Ship orders within promised timeframes",
agreementItem6: "Comply with AiDA's terms of service and community guidelines",
agreementItem7: "Pay applicable platform fees and transaction charges",
agreementItem8: "Handle customer disputes professionally and fairly",
agreementAgreement: "I have read and agree to the Seller Agreement, understanding my responsibilities and obligations as a seller on the AiDA platform.",
submitApplication: "Submit Application",
applicationSubmitted: "Application Submitted",
applicationSubmittedTip: "Approval is expected shortly. Upon receipt, please ensure your payout account is linked under <span>Seller Dashboard > Settings</span> prior to listing any items.",
auditStatus1_title: "Step 1: Submit Application",
auditStatus1_tip: "Fill out the seller information form and agree to our terms",
auditStatus2_title: "Step 2: Review & Verification",
auditStatus2_tip: "Our team will review your application (typically 1-3 business days)",
auditStatus3_title: "Step 3: Start Selling",
auditStatus3_tip: "Once approved, access your seller dashboard and start listing products",
backToHomepage: "Back to Homepage",
}
}

View File

@@ -1,8 +1,63 @@
import { createRouter, createWebHistory, RouteRecordRaw, createWebHashHistory } from "vue-router"
import type { RouteLocationNormalizedLoaded, RouteLocationRaw } from "vue-router"
import store from "@/store"
import { Https } from "@/tool/https"
import { getCookie, setCookie } from "@/tool/cookie"
type SellerRouteMetaValue<T> = T | ((route: RouteLocationNormalizedLoaded) => T)
type SellerBreadcrumbItem = {
title?: SellerRouteMetaValue<string>
titleKey?: SellerRouteMetaValue<string>
to?: SellerRouteMetaValue<RouteLocationRaw | undefined>
}
type SellerBreadcrumbList = SellerRouteMetaValue<SellerBreadcrumbItem[]>
const myListingsBreadcrumb: SellerBreadcrumbItem = {
titleKey: "Seller.MyListings",
to: { name: "myListingsIndex" }
}
const selectCollectionBreadcrumb: SellerBreadcrumbItem = {
titleKey: "Seller.SelectCollection",
to: { name: "myListingsSelect" }
}
const selectSketchBreadcrumb: SellerBreadcrumbItem = {
titleKey: "Seller.SelectSketch",
to: (route) => {
const collectionId =
route.params.collectionId ||
(typeof history !== "undefined" ? history.state?.collectionId : "")
return collectionId
? { name: "myListingsSelectItem", params: { collectionId } }
: undefined
}
}
const editListingBreadcrumb: SellerBreadcrumbItem = {
titleKey: "Seller.EditListingDetails"
}
const statusBreadcrumb: SellerBreadcrumbItem = {
titleKey: (route) =>
route.params.status === "publish"
? "SellerListEdit.listingLive"
: "SellerListEdit.draftSaved"
}
const createListingBreadcrumbs: SellerBreadcrumbItem[] = [
myListingsBreadcrumb,
selectCollectionBreadcrumb,
selectSketchBreadcrumb,
editListingBreadcrumb
]
const isEditingListingFromList = () =>
typeof history !== "undefined" && history.state?.type === "edit"
const editListingBreadcrumbs: SellerBreadcrumbList = () =>
isEditingListingFromList()
? [myListingsBreadcrumb, editListingBreadcrumb]
: createListingBreadcrumbs
const listingStatusBreadcrumbs: SellerBreadcrumbList = () =>
isEditingListingFromList()
? [myListingsBreadcrumb, editListingBreadcrumb, statusBreadcrumb]
: [...createListingBreadcrumbs, statusBreadcrumb]
const routes: Array<RouteRecordRaw> = [
{
path: "/",
@@ -167,7 +222,11 @@ const routes: Array<RouteRecordRaw> = [
{
path: "becomeSeller",
name: "becomeSeller",
meta: { enter: "all" },
meta: {
enter: "all",
sellerHeaderTitle: "ApplySeller.applySellerTitle",
sellerHeaderTip: "ApplySeller.applySellerDesc"
},
component: () => import("@/views/SellerDashboard/BecomeSeller/index.vue")
},
{
@@ -176,6 +235,11 @@ const routes: Array<RouteRecordRaw> = [
meta: { enter: "all" },
component: () => import("@/views/SellerDashboard/index.vue"),
children: [
{
path: "",
meta: { enter: "all" },
redirect: "/home/seller/brandProfile"
},
{
path: "brandProfile",
name: "brandProfile",
@@ -185,7 +249,7 @@ const routes: Array<RouteRecordRaw> = [
{
path: "myListings",
name: "myListings",
meta: { enter: "all" },
meta: { enter: "all", sellerBreadcrumb: myListingsBreadcrumb },
children: [
{
path: "",
@@ -196,30 +260,65 @@ const routes: Array<RouteRecordRaw> = [
{
path: "index",
name: "myListingsIndex",
meta: { enter: "all" },
meta: {
enter: "all",
sellerHeaderTitleKey: "Seller.MyListings",
sellerHeaderTipKey: "Seller.MyListingsInfo",
sellerBreadcrumbs: [myListingsBreadcrumb]
},
component: () =>
import("@/views/SellerDashboard/MyListings/main/index.vue")
},
{
path: "select",
name: "myListingsSelect",
meta: { enter: "all" },
meta: {
enter: "all",
sellerHeaderTitleKey: "Seller.SelectCollection",
sellerBreadcrumbs: [
myListingsBreadcrumb,
selectCollectionBreadcrumb
]
},
component: () =>
import("@/views/SellerDashboard/MyListings/createSelect/index.vue")
},
{
path: "select/:id",
path: "select/:collectionId",
name: "myListingsSelectItem",
meta: { enter: "all" },
meta: {
enter: "all",
sellerHeaderTitleKey: "Seller.SelectSketch",
sellerBreadcrumbs: [
myListingsBreadcrumb,
selectCollectionBreadcrumb,
selectSketchBreadcrumb
]
},
component: () =>
import("@/views/SellerDashboard/MyListings/createSelectItem/index.vue")
},
{
path: "edit",
name: "EditDetail",
meta: { enter: "all" },
meta: {
enter: "all",
sellerHeaderTitleKey: "Seller.EditListingDetails",
sellerBreadcrumbs: editListingBreadcrumbs
},
component: () =>
import("@/views/SellerDashboard/MyListings/EditDetail/index.vue")
},
{
path: "edit/status/:status",
name: "Status",
meta: {
enter: "all",
sellerHeaderTitleKey: "Seller.EditListingDetails",
sellerBreadcrumbs: listingStatusBreadcrumbs
},
component: () =>
import("@/views/SellerDashboard/MyListings/EditDetail/Status.vue")
}
]
},
@@ -245,6 +344,12 @@ const routes: Array<RouteRecordRaw> = [
meta: { enter: "all" },
component: () => import("@/views/HomeRecommend.vue")
},
{
path: "/Square/:lang",
name: "HomeRecommendLang",
meta: { enter: "all" },
component: () => import("@/views/HomeRecommend.vue"),
},
{
path: "/administrator",
name: "administrator",
@@ -483,7 +588,6 @@ const routes: Array<RouteRecordRaw> = [
},
component: () => import("@/views/userManual.vue")
},
{
path: "/:catchAll(.*)",
redirect: "/404"

View File

@@ -7,6 +7,7 @@ import UserHabit from './userHabit/userHabit'
import Workspace from './workspace/workspace'
import Guide from './guide/guide'
import adminPage from './adminPage/adminPage'
import seller from './seller/index'
export interface RootState{
}
@@ -41,5 +42,6 @@ export default createStore<RootState>({
Workspace,
Guide,
adminPage,
seller,
}
})

6
src/store/seller/index.d.ts vendored Normal file
View File

@@ -0,0 +1,6 @@
export const ApplyStatus = {
Null: null,// 未申请过
Pending: 0,// 已提交, 待审核
Approved: 1,// 审核通过
Rejected: 2,// 审核拒绝
}

91
src/store/seller/index.ts Normal file
View File

@@ -0,0 +1,91 @@
import { Module } from 'vuex'
import { RootState } from '../index'
import i18n from "@/lang/index";
import { Https } from '@/tool/https'
import { ApplyStatus } from "./index.d"
import store from '../index'
interface DesignerInfo {
shopName: string,
avatar: string,
brandBanner: string,
ownerName: string,
email: string,
mobile: string,
socialLinks: string[] | string,
description: string,
}
interface Seller {
isSeller: boolean,
applyStatus: number | null,
designerInfo: DesignerInfo,
}
const seller: Module<Seller, RootState> = {
namespaced: true,
state: {
isSeller: false,
applyStatus: null,
designerInfo: {
shopName: "--",
avatar: null,
brandBanner: null,
ownerName: "--",
email: "--",
mobile: "--",
socialLinks: ["--"],
description: "--"
},
},
mutations: {
set_isSeller(state: Seller, value: boolean) {
state.isSeller = value
},
set_applyStatus(state: Seller, value: number) {
state.applyStatus = value
if (value == ApplyStatus.Approved) {
state.isSeller = true
}
},
set_designerInfo(state: Seller, value: DesignerInfo) {
state.designerInfo = {
...state.designerInfo,
...value,
}
if (typeof value.socialLinks === "string") {
state.designerInfo.socialLinks = JSON.parse(value.socialLinks)
} else if (Array.isArray(value.socialLinks)) {
state.designerInfo.socialLinks = value.socialLinks
}
},
clear_state(state: Seller) {
state.isSeller = false
state.applyStatus = null
state.designerInfo = {
shopName: "--",
avatar: "",
brandBanner: "",
ownerName: "--",
email: "--",
mobile: "--",
socialLinks: ["--"],
description: "--"
}
},
},
actions: {
get_isSeller({ commit }) {
Https.axiosGet(Https.httpUrls.checkSellerDesigner).then(rv => {
commit('set_isSeller', !!rv)
})
},
async get_designerInfo({ commit }) {
const rv = await Https.axiosGet(Https.httpUrls.getDesignerInfo)
commit('set_designerInfo', rv)
return rv
},
}
}
export default seller

View File

@@ -13,24 +13,13 @@ axios.defaults.headers.post['lang'] = 'en' //配置语言请求头
axios.defaults.withCredentials = true //跨域携带cookie
import { message } from 'ant-design-vue'
import store from '@/store'
// if(import.meta.env.VITE_USER_NODE_ENV == "development"){
// axios.defaults.baseURL = ""; //配置接口地址
// }else{
// axios.defaults.baseURL = import.meta.env.VITE_APP_BASE_URL; //配置接口地址
// }
// let httpIp
// if(import.meta.env.VITE_USER_NODE_ENV == 'development'){
// httpIp = 'http://192.168.1.12:10086'
// }else{
// httpIp = ''
// }
let httpIp = import.meta.env.VITE_USER_NODE_ENV == 'development' ? '' : ''
// let httpIp = import.meta.env.VITE_USER_NODE_ENV == 'development' ? "https://192.168.1.8:10086" : "";
axios.defaults.baseURL = httpIp //配置接口地址
// console.log(axios.defaults.baseURL);
axios.defaults.baseURL = import.meta.env.VITE_APP_BASE_URL //配置接口地址
console.log(import.meta.env.VITE_APP_BASE_URL)
const baseURL = import.meta.env.VITE_APP_BASE_URL
console.log(baseURL)
if (import.meta.env.VITE_USER_NODE_ENV == "development") {
axios.defaults.baseURL = "";
} else {
axios.defaults.baseURL = baseURL;
}
// 创建取消令牌
const CancelToken = axios.CancelToken
@@ -149,341 +138,358 @@ axios.interceptors.response.use(
export const Https = {
httpUrls: {
interfaceUrl: '',
parseGoogleCredential: '/api/third/party/parseGoogleCredential', //谷歌登录注册
parseWeChatCode: '/api/third/party/parseWeChatCode', //微信登录
accountIsLogin: '/api/account/isLogin', //判断用户是否登录
accountLogin: `/api/account/login`, //账号密码登录接口
organizationNameSearch: `/api/account/organizationNameSearch`, //查询学校或者企业版名字
getUserLanguage: `/api/account/getUserLanguage`, //获取当前用户语言
changeUserLanguage: `/api/account/changeUserLanguage`, //切换用户当前语言
uploadAvatar: `/api/account/uploadAvatar`, //修改头像
editUserName: `/api/account/editUserName`, //修改用户名
updateUserInfo: `/api/account/updateUserInfo`, //修改国家职业
accountDetail: `/api/account/getAccountDetail`, //用户详细信息
parseGoogleCredential: '/aida/api/third/party/parseGoogleCredential', //谷歌登录注册
parseWeChatCode: '/aida/api/third/party/parseWeChatCode', //微信登录
accountIsLogin: '/aida/api/account/isLogin', //判断用户是否登录
accountLogin: `/aida/api/account/login`, //账号密码登录接口
organizationNameSearch: `/aida/api/account/organizationNameSearch`, //查询学校或者企业版名字
getUserLanguage: `/aida/api/account/getUserLanguage`, //获取当前用户语言
changeUserLanguage: `/aida/api/account/changeUserLanguage`, //切换用户当前语言
uploadAvatar: `/aida/api/account/uploadAvatar`, //修改头像
editUserName: `/aida/api/account/editUserName`, //修改用户名
updateUserInfo: `/aida/api/account/updateUserInfo`, //修改国家职业
accountDetail: `/aida/api/account/getAccountDetail`, //用户详细信息
trialUserLogout: `/api/account/trialUserLogout`, //试用用户退出登录接口
completeGuidancet: `/api/account/completeGuidance`, //用户指引结束
trialUserLogout: `/aida/api/account/trialUserLogout`, //试用用户退出登录接口
completeGuidancet: `/aida/api/account/completeGuidance`, //用户指引结束
getExpiredTime: `/api/account/getExpiredTime`, //获取用户到期时间
getExpiredTime: `/aida/api/account/getExpiredTime`, //获取用户到期时间
addNoLoginRequired: `/api/third/party/addNoLoginRequired`, //机房用户注册
deleteNoLoginRequired: `/api/third/party/deleteNoLoginRequired`, //机房用户注销
noLoginRequired: `api/account/noLoginRequired`, //机房用户登录
existNoLoginRequired: `/api/third/party/existNoLoginRequired`, //获取唯一标识是否存在
addNoLoginRequired: `/aida/api/third/party/addNoLoginRequired`, //机房用户注册
deleteNoLoginRequired: `/aida/api/third/party/deleteNoLoginRequired`, //机房用户注销
noLoginRequired: `/aida/api/account/noLoginRequired`, //机房用户登录
existNoLoginRequired: `/aida/api/third/party/existNoLoginRequired`, //获取唯一标识是否存在
deleteNoLoginRequiredNew: `/api/third/party/deleteNoLoginRequiredNew`, //机房用户注销
addNoLoginRequiredNew: `api/third/party/addNoLoginRequiredNew`, //机房用户注册
updateNoLoginRequiredNew: `api/third/party/updateNoLoginRequiredNew`, //机房用户更新
deleteNoLoginRequiredNew: `/aida/api/third/party/deleteNoLoginRequiredNew`, //机房用户注销
addNoLoginRequiredNew: `/aida/api/third/party/addNoLoginRequiredNew`, //机房用户注册
updateNoLoginRequiredNew: `/aida/api/third/party/updateNoLoginRequiredNew`, //机房用户更新
endpoint: `api/third/party/your-secured-endpoint`, //获取唯一标识是否存在
endpoint: `/aida/api/third/party/your-secured-endpoint`, //获取唯一标识是否存在
designWorksRegister: '/api/account/designWorksRegister', //注册
designWorksRegisterCode: '/api/account/designWorksRegisterCode', //注册
designWorksRegister: '/aida/api/account/designWorksRegister', //注册
designWorksRegisterCode: '/aida/api/account/designWorksRegisterCode', //注册
preLogin: '/api/account/preLogin', //预先登入
schoolLogin: '/api/account/schoolLogin', //学校管理员登录
enterpriseLogin: '/api/account/enterpriseLogin', //企业管理员登录
accountSendEmail: `/api/account/sendEmail`, //发送邮件
accountResetPwd: '/api/account/resetPwd', //忘记密码修改
accountLogout: '/api/account/logout', //登出
accountBindEmail: '/api/account/bindEmail', //绑定邮箱
bindGoogle: '/api/account/bindGoogle', //绑定谷歌
bindWeChat: '/api/account/bindWeChat', //绑定微信
unbindGoogle: `/api/account/unbindGoogle`, //取消绑定谷歌
unbindWeChat: '/api/account/unbindWeChat', //取消绑定微信
elementGeneratePrint: '/api/element/generatePrint', //生成印花
elementSavePrint: '/api/element/savePrint', //保存印花
getRgbByTcx: '/api/element/getRgbByTcx', // 通过hsv值获取潘通信息
getRgbByHsv: '/api/element/getRgbByHsv', //通过hsv值获取潘通信息
elementDelete: '/api/element/delete', //删除上传的图片
designCollection: `/api/design/designCollection`, //设计 Conllection
reDesignCollection: `/api/design/reDesignCollection`, //重新设计 Conllection
countDesignProcess: '/api/design/countDesignProcess', //统计design进度
getDesignResult: '/api/design/getDesignResult', //查询design结果
designSort: `/api/design/sort`, //design排序
collectionLikeUpdate: `/api/history/collectionLikeUpdate`, //赋值排序
preLogin: '/aida/api/account/preLogin', //预先登入
schoolLogin: '/aida/api/account/schoolLogin', //学校管理员登录
enterpriseLogin: '/aida/api/account/enterpriseLogin', //企业管理员登录
accountSendEmail: `/aida/api/account/sendEmail`, //发送邮件
accountResetPwd: '/aida/api/account/resetPwd', //忘记密码修改
accountLogout: '/aida/api/account/logout', //登出
accountBindEmail: '/aida/api/account/bindEmail', //绑定邮箱
bindGoogle: '/aida/api/account/bindGoogle', //绑定谷歌
bindWeChat: '/aida/api/account/bindWeChat', //绑定微信
unbindGoogle: `/aida/api/account/unbindGoogle`, //取消绑定谷歌
unbindWeChat: '/aida/api/account/unbindWeChat', //取消绑定微信
elementGeneratePrint: '/aida/api/element/generatePrint', //生成印花
elementSavePrint: '/aida/api/element/savePrint', //保存印花
getRgbByTcx: '/aida/api/element/getRgbByTcx', // 通过hsv值获取潘通信息
getRgbByHsv: '/aida/api/element/getRgbByHsv', //通过hsv值获取潘通信息
elementDelete: '/aida/api/element/delete', //删除上传的图片
designCollection: `/aida/api/design/designCollection`, //设计 Conllection
reDesignCollection: `/aida/api/design/reDesignCollection`, //重新设计 Conllection
countDesignProcess: '/aida/api/design/countDesignProcess', //统计design进度
getDesignResult: '/aida/api/design/getDesignResult', //查询design结果
designSort: `/aida/api/design/sort`, //design排序
collectionLikeUpdate: `/aida/api/history/collectionLikeUpdate`, //赋值排序
designProcess: `/api/design/designProcess`, //统计design进度
designGetModel: `/api/design/getModel`, //导出获取模特链接
designProcess: `/aida/api/design/designProcess`, //统计design进度
designGetModel: `/aida/api/design/getModel`, //导出获取模特链接
//充值相关
productList: `/api/product/list`, //获取商品列表
payAlipay: `/api/ali-pay/trade/page/pay`, //支付宝确认支付
payAlipayHK: `/api/alipay-hk/createOrder`, //香港支付宝确认支付
payStripe: `/api/stripe/createOrder`, //Stripe支付
payPaypal: `/api/paypal/trade`, //paypal确认支付
getCredits: `/api/credits/getCredits`, //查询用户积分
productList: `/aida/api/product/list`, //获取商品列表
payAlipay: `/aida/api/ali-pay/trade/page/pay`, //支付宝确认支付
payAlipayHK: `/aida/api/alipay-hk/createOrder`, //香港支付宝确认支付
payStripe: `/aida/api/stripe/createOrder`, //Stripe支付
payPaypal: `/aida/api/paypal/trade`, //paypal确认支付
getCredits: `/aida/api/credits/getCredits`, //查询用户积分
cancelSubscription: `/api/stripe/cancelSubscription`, //取消订阅
cancelSubscription: `/aida/api/stripe/cancelSubscription`, //取消订阅
orderInfoList: `/api/order-info/list`, //查询订单列表
getCreditsDetail: `/api/credits/getCreditsDetail`, //查询积分列表
tradeRefundAlipay: `/api/ali-pay/trade/refund`, //支付宝退款
tradeRefundPaypal: `/api/paypal/trade/refund`, //paypal退款
orderInfoList: `/aida/api/order-info/list`, //查询订单列表
getCreditsDetail: `/aida/api/credits/getCreditsDetail`, //查询积分列表
tradeRefundAlipay: `/aida/api/ali-pay/trade/refund`, //支付宝退款
tradeRefundPaypal: `/aida/api/paypal/trade/refund`, //paypal退款
tradeQuery: `/api/ali-pay/trade/query/{orderNo}`, //查询订单状态
tradeQuery: `/aida/api/ali-pay/trade/query/{orderNo}`, //查询订单状态
getRgbByHsvBatch: `/api/element/getRgbByHsvBatch`, //通过hsv值数组批量获取潘通信息
designLike: `/api/design/like`, //Design Like
designDislike: `/api/design/dislike`, //Design Dislike
queryUserGroup: `/api/history/queryUserGroup`, //History用户分页分组列表
deleteUserGroup: `/api/history/deleteUserGroup`, //History删除用户分组
updateUserGroupName: `/api/history/updateUserGroupName`, //History修改用户分组名
projectSaveOrUpdate: `/api/project/saveOrUpdate`, //History修改用户分组名
historyChoose: `/api/history/choose`, //History choose
getDesignDetail: `/api/design/detail/getDetail`, //查询design详情
addSysSketchToLibrary: `/api/library/addSysSketchToLibrary`, //把系统衣服添加的library
designSingleWithGradient: `/api/design/detail/designSingleWithGradient`, //查询需要更新mask列表
getNextSysElement: '/api/design/detail/getNextSysElement', //切换系统的element
detailPrintDot: '/api/design/detail/printDot', //print打点预览
designSingle: `/api/design/detail/designSingle`, //单个design
queryLibraryPage: `/api/library/queryLibraryPage`, //Library分页列表
libraryUpload: `/api/library/upload`, // Library文件上传
setSketchLibrary: `/api/library/updateLibraryLevel2Type`, // 修改图片类型
updateElementLevel2Type: `/api/element/updateElementLevel2Type`, // 修改拼贴上传的衣服类型
getRgbByHsvBatch: `/aida/api/element/getRgbByHsvBatch`, //通过hsv值数组批量获取潘通信息
designLike: `/aida/api/design/like`, //Design Like
designDislike: `/aida/api/design/dislike`, //Design Dislike
queryUserGroup: `/aida/api/history/queryUserGroup`, //History用户分页分组列表
deleteUserGroup: `/aida/api/history/deleteUserGroup`, //History删除用户分组
updateUserGroupName: `/aida/api/history/updateUserGroupName`, //History修改用户分组名
projectSaveOrUpdate: `/aida/api/project/saveOrUpdate`, //History修改用户分组名
historyChoose: `/aida/api/history/choose`, //History choose
getDesignDetail: `/aida/api/design/detail/getDetail`, //查询design详情
addSysSketchToLibrary: `/aida/api/library/addSysSketchToLibrary`, //把系统衣服添加的library
designSingleWithGradient: `/aida/api/design/detail/designSingleWithGradient`, //查询需要更新mask列表
getNextSysElement: '/aida/api/design/detail/getNextSysElement', //切换系统的element
detailPrintDot: '/aida/api/design/detail/printDot', //print打点预览
designSingle: `/aida/api/design/detail/designSingle`, //单个design
queryLibraryPage: `/aida/api/library/queryLibraryPage`, //Library分页列表
libraryUpload: `/aida/api/library/upload`, // Library文件上传
setSketchLibrary: `/aida/api/library/updateLibraryLevel2Type`, // 修改图片类型
updateElementLevel2Type: `/aida/api/element/updateElementLevel2Type`, // 修改拼贴上传的衣服类型
queryClassification: `/api/classification/queryClassification`, //标签类别查询
classificationSaveOrUpdate: `/api/classification/saveOrUpdate`, //标签类别新增修改
classificationDelete: `/api/classification/delete`, //标签类别新增修改
relationLibrary: `/api/classification/relationLibrary`, //标签类别新增修改
getRelClassificationIdList: `/api/classification/getRelClassificationIdList`, //标签类别新增修改
getRelPublicClassificationIdList: `/api/classification/getRelPublicClassificationIdList`, //多选获取公共标签
editRelPublicClassificationIdList: `/api/classification/editRelPublicClassificationIdList`, //多选修改公共标签
queryClassification: `/aida/api/classification/queryClassification`, //标签类别查询
classificationSaveOrUpdate: `/aida/api/classification/saveOrUpdate`, //标签类别新增修改
classificationDelete: `/aida/api/classification/delete`, //标签类别新增修改
relationLibrary: `/aida/api/classification/relationLibrary`, //标签类别新增修改
getRelClassificationIdList: `/aida/api/classification/getRelClassificationIdList`, //标签类别新增修改
getRelPublicClassificationIdList: `/aida/api/classification/getRelPublicClassificationIdList`, //多选获取公共标签
editRelPublicClassificationIdList: `/aida/api/classification/editRelPublicClassificationIdList`, //多选修改公共标签
//模块化
llmStream: `/api/llm/streamNew`, //聊天
// llmStream:`/api/llm/stream`,//聊天
chatCreateProject: `/api/llm/chatCreateProject`, //聊天创建项目
getChatHistory: `/api/llm/getChatHistory`, //获取聊天历史记录
llmUploadFile: `/api/llm/uploadFile`, //聊天上传文件
llmStream: `/aida/api/llm/streamNew`, //聊天
// llmStream:`/aida/api/llm/stream`,//聊天
chatCreateProject: `/aida/api/llm/chatCreateProject`, //聊天创建项目
getChatHistory: `/aida/api/llm/getChatHistory`, //获取聊天历史记录
llmUploadFile: `/aida/api/llm/uploadFile`, //聊天上传文件
saveOrUpdate: `/api/project/saveOrUpdate`, //模块化新增修改
getModuleContent: `/api/project/getModuleContent`, //获取模块内容
saveModuleContent: `/api/project/saveModuleContent`, //储存模块内容
historyProject: `/api/project/page`, //项目记录
projectDetail: `/api/project/delete`, //删除项目
saveOrUpdate: `/aida/api/project/saveOrUpdate`, //模块化新增修改
getModuleContent: `/aida/api/project/getModuleContent`, //获取模块内容
saveModuleContent: `/aida/api/project/saveModuleContent`, //储存模块内容
historyProject: `/aida/api/project/page`, //项目记录
projectDetail: `/aida/api/project/delete`, //删除项目
//3d
threeDPage: `/api/project/threeDPage`,
downloadZip: `/api/project/downloadZip`, //下载zip
getThreeDSize: `/api/project/getThreeDSize`, //下载列表
getLayoutDetail: `/api/project/getLayoutDetail`, //获取3d详情
getThreeDGlb: `/api/project/getThreeDGlb`,
selectHistoryProject: `/api/project/choose`, //选择项目
getMannequinDetail: `/api/project/getMannequinDetail`, //模块化查看模特点位
modifyProportion: `/api/generate/modifyProportion`, //模特拉伸
addSysModelToLib: `/api/library/addSysModelToLib`,
poselikeOrDisike: `/api/generate/likeOrDislike`, //postTransform like
getAllPose: `/api/generate/getAllPose`, //获取动作
threeDPage: `/aida/api/project/threeDPage`,
downloadZip: `/aida/api/project/downloadZip`, //下载zip
getThreeDSize: `/aida/api/project/getThreeDSize`, //下载列表
getLayoutDetail: `/aida/api/project/getLayoutDetail`, //获取3d详情
getThreeDGlb: `/aida/api/project/getThreeDGlb`,
selectHistoryProject: `/aida/api/project/choose`, //选择项目
getMannequinDetail: `/aida/api/project/getMannequinDetail`, //模块化查看模特点位
modifyProportion: `/aida/api/generate/modifyProportion`, //模特拉伸
addSysModelToLib: `/aida/api/library/addSysModelToLib`,
poselikeOrDisike: `/aida/api/generate/likeOrDislike`, //postTransform like
getAllPose: `/aida/api/generate/getAllPose`, //获取动作
//拼贴
genSketchRecon: `/api/generate/genSketchRecon`,
saveReconCanvas: `/api/generate/saveReconCanvas`,
genSketchRecon: `/aida/api/generate/genSketchRecon`,
saveReconCanvas: `/aida/api/generate/saveReconCanvas`,
//动作变换
poseTransform: `/api/generate/poseTransform`,
poseTransformResult: `/api/generate/poseTransformResult`,
poseTransform: `/aida/api/generate/poseTransform`,
poseTransformResult: `/aida/api/generate/poseTransformResult`,
batchUpdateLibraryName: '/api/library/batchUpdateLibraryName', //Library修改用户文件名
batchDeleteLibrary: '/api/library/batchDeleteLibrary', //删除library
queryLibraryTopAndBottomPage: '/api/library/queryLibraryTopAndBottomPage', //Library分页列表(查询top和bottom)
saveOrEditTemplatePoint: '/api/library/saveOrEditTemplatePoint', //保存或者编辑template打点
libraryModelsDot: '/api/library/modelsDot', //Models打点预览
chatStreamTest: `/api/python/chatStream`, //机器人助力
pictureLikeOrUnLike: `/api/python/pictureLikeOrUnLike`, //机器人生成图喜欢
getBloodBars: `/api/python/getBloodBars`, //机器人血条
batchUpdateLibraryName: '/aida/api/library/batchUpdateLibraryName', //Library修改用户文件名
batchDeleteLibrary: '/aida/api/library/batchDeleteLibrary', //删除library
queryLibraryTopAndBottomPage: '/aida/api/library/queryLibraryTopAndBottomPage', //Library分页列表(查询top和bottom)
saveOrEditTemplatePoint: '/aida/api/library/saveOrEditTemplatePoint', //保存或者编辑template打点
libraryModelsDot: '/aida/api/library/modelsDot', //Models打点预览
chatStreamTest: `/aida/api/python/chatStream`, //机器人助力
pictureLikeOrUnLike: `/aida/api/python/pictureLikeOrUnLike`, //机器人生成图喜欢
getBloodBars: `/aida/api/python/getBloodBars`, //机器人血条
//工作空间
workspaceDetail: `/api/workspace/detail`, //用户习惯详情
workspaceenumValues: `/api/workspace/enumValues`, //getSex
workspaceDetail: `/aida/api/workspace/detail`, //用户习惯详情
workspaceenumValues: `/aida/api/workspace/enumValues`, //getSex
workspaceRemove: `/api/workspace/remove`, //删除用户习惯详情
workspacesaveOrUpdate: `/api/workspace/saveOrUpdate`, //修改用户习惯详情
getMannequins: `/api/workspace/getMannequins`, //模特
getStyleList: `/api/workspace/styleList`, //获取所有风格列表
workspaceRemove: `/aida/api/workspace/remove`, //删除用户习惯详情
workspacesaveOrUpdate: `/aida/api/workspace/saveOrUpdate`, //修改用户习惯详情
getMannequins: `/aida/api/workspace/getMannequins`, //模特
getStyleList: `/aida/api/workspace/styleList`, //获取所有风格列表
workspaceList: `/api/workspace/list`,
sketchAndPrintGenerate: '/api/generate/sketchAndPrint', //sketchGenerate生成图片
workspaceList: `/aida/api/workspace/list`,
sketchAndPrintGenerate: '/aida/api/generate/sketchAndPrint', //sketchGenerate生成图片
generatePrepare: '/api/generate/prepare', //开始生成generate图片
generateStopWaiting: '/api/generate/stopWaiting', //取消生成
generateResult: '/api/generate/result', //获取生成结果
generateLike: '/api/generate/like', //喜欢ganerate图片
generateDislike: '/api/generate/dislike', //喜欢ganerate图片
imageToSketch: '/api/generate/imageToSketch', //成品图转为线稿
modifySketch: '/api/generate/modifySketch', //修改画布内容并且储存
generatePrepare: '/aida/api/generate/prepare', //开始生成generate图片
generateStopWaiting: '/aida/api/generate/stopWaiting', //取消生成
generateResult: '/aida/api/generate/result', //获取生成结果
generateLike: '/aida/api/generate/like', //喜欢ganerate图片
generateDislike: '/aida/api/generate/dislike', //喜欢ganerate图片
imageToSketch: '/aida/api/generate/imageToSketch', //成品图转为线稿
modifySketch: '/aida/api/generate/modifySketch', //修改画布内容并且储存
elementUpload: `/api/element/upload`, //上传图片
imageSegmentation: `/api/element/imageSegmentation`, //分割衣服
convertRelightElement: `/api/history/convertRelightElement`, //toproduct复制到上传图片位置
elementUpload: `/aida/api/element/upload`, //上传图片
imageSegmentation: `/aida/api/element/imageSegmentation`, //分割衣服
convertRelightElement: `/aida/api/history/convertRelightElement`, //toproduct复制到上传图片位置
// oldHis:`/oldHis/history/queryUserGroup`,//上传图片
sketchBoardsBoundingBox: `/api/design/sketchBoardsBoundingBox`, //裁剪sketch图片
sketchBoardsBoundingBox: `/aida/api/design/sketchBoardsBoundingBox`, //裁剪sketch图片
trialOrderList: `/api/account/trialOrderList`, //获取审批列表
switchIsAutoApproval: `/api/account/switchIsAutoApproval`, //切换是否自动审批
getIsAutoApproval: `/api/account/getIsAutoApproval`, //获取是否自动审批
trialOrderApproval: `/api/account/trialOrderApproval`, //通过审批
trialOrderRefuse: `/api/account/trialOrderRefuse`, //拒绝审批
trialOrderList: `/aida/api/account/trialOrderList`, //获取审批列表
switchIsAutoApproval: `/aida/api/account/switchIsAutoApproval`, //切换是否自动审批
getIsAutoApproval: `/aida/api/account/getIsAutoApproval`, //获取是否自动审批
trialOrderApproval: `/aida/api/account/trialOrderApproval`, //通过审批
trialOrderRefuse: `/aida/api/account/trialOrderRefuse`, //拒绝审批
//管理员接口
//查询所有试用用户
inquiryGetTrial: `/api/inquiry/getTrial`, //查询所有试用用户
getCities: `/api/inquiry/getCities`, //获取所有付款订单使用的国家
getUserInfo: `/api/inquiry/getUserInfo`, //查询所有用户
queryTransaction: `/api/inquiry/queryTransaction`, //查询交易记录
queryTransactionDownload: `/api/inquiry/queryTransaction/download`, //导出交易记录
createCoupon: `/api/stripe/createCoupon`, //创建优惠码
updatePromCodeInfo: `/api/stripe/updatePromCodeInfo`, //修改优惠码
getAllCoupons: `/api/stripe/getAllCoupons`, //查询优惠码列表
checkCoupon: `/api/stripe/checkCoupon`, //根据优惠码获取结算后的金额
deletePromCode: `/api/stripe/deletePromCode`, //删除优惠券
addOrganization: `/api/inquiry/addOrganization`, //添加企业版或者教育版
queryOrganization: `/api/inquiry/queryOrganization`, //查询企业版或者教育版
createSubscribePlan: '/api/subscription_plan/createPlan', // 创建订阅计划
deleteSubscribePlan: '/api/subscription_plan/deletePlan', // 删除订阅计划
updateSubscribePlan: '/api/subscription_plan/updatePlan', // 修改订阅计划
searchAllSubscribePlan: '/api/subscription_plan/searchByPage', // 分页查询所有订阅计划
searchSubscribeByOrg: '/api/subscription_plan/searchByOrganizationIdAndStatus', // 不分页查询
switchSubscribePlan: '/api/subscription_plan/switchSubscriptionPlan', // 切换管理员订阅计划
inquiryGetTrial: `/aida/api/inquiry/getTrial`, //查询所有试用用户
getCities: `/aida/api/inquiry/getCities`, //获取所有付款订单使用的国家
getUserInfo: `/aida/api/inquiry/getUserInfo`, //查询所有用户
queryTransaction: `/aida/api/inquiry/queryTransaction`, //查询交易记录
queryTransactionDownload: `/aida/api/inquiry/queryTransaction/download`, //导出交易记录
createCoupon: `/aida/api/stripe/createCoupon`, //创建优惠码
updatePromCodeInfo: `/aida/api/stripe/updatePromCodeInfo`, //修改优惠码
getAllCoupons: `/aida/api/stripe/getAllCoupons`, //查询优惠码列表
checkCoupon: `/aida/api/stripe/checkCoupon`, //根据优惠码获取结算后的金额
deletePromCode: `/aida/api/stripe/deletePromCode`, //删除优惠券
addOrganization: `/aida/api/inquiry/addOrganization`, //添加企业版或者教育版
queryOrganization: `/aida/api/inquiry/queryOrganization`, //查询企业版或者教育版
createSubscribePlan: '/aida/api/subscription_plan/createPlan', // 创建订阅计划
deleteSubscribePlan: '/aida/api/subscription_plan/deletePlan', // 删除订阅计划
updateSubscribePlan: '/aida/api/subscription_plan/updatePlan', // 修改订阅计划
searchAllSubscribePlan: '/aida/api/subscription_plan/searchByPage', // 分页查询所有订阅计划
searchSubscribeByOrg: '/aida/api/subscription_plan/searchByOrganizationIdAndStatus', // 不分页查询
switchSubscribePlan: '/aida/api/subscription_plan/switchSubscriptionPlan', // 切换管理员订阅计划
switchSubAccountSubscribePlan:
'/api/subscription_plan/switchSubAccSubscriptionPlan', // 切换子账号订阅计划
'/aida/api/subscription_plan/switchSubAccSubscriptionPlan', // 切换子账号订阅计划
//云生成
designCloud: `/api/design/designCloud`, //创建云生成
cloudPage: `/api/design/cloudPage`, //创建云生成
cloudTaskDelete: `/api/design/cloudTaskDelete`, //删除云生成
cloudTaskNameUpdate: `/api/design/cloudTaskNameUpdate`, //修改云生成名字
getDesignCloudResult: `/api/design/getDesignCloudResult`, //查询这条云生成记录的所有内容
designCloud: `/aida/api/design/designCloud`, //创建云生成
cloudPage: `/aida/api/design/cloudPage`, //创建云生成
cloudTaskDelete: `/aida/api/design/cloudTaskDelete`, //删除云生成
cloudTaskNameUpdate: `/aida/api/design/cloudTaskNameUpdate`, //修改云生成名字
getDesignCloudResult: `/aida/api/design/getDesignCloudResult`, //查询这条云生成记录的所有内容
//企业版教育版管理员页面
subAccountList: `/api/account/subAccountList`, //查询子账号
addOrUpdateSubAccount: `/api/account/addOrUpdateSubAccount`, //添加子账号
deleteSubAccount: `/api/account/deleteSubAccount`, //删除子账号
subAccountImportExcelDownload: `/api/account/subAccountImportExcelDownload`, //批量添加模板下载模板
exportAccountsToExcel: `/api/account/exportAccountsToExcel`, //教育版导出用户数据
getNextSequence: `/api/project/getNextSequence`, //批量添加模板下载模板
subAccountImport: `/api/account/subAccountImport`, //模板导入
getGenerateFrequency: `/api/inquiry/getGenerateFrequency`, //积分使用详情
getAllGenerateFuncName: `/api/inquiry/getAllGenerateFuncName`, //获取所有generate类型
subAccountList: `/aida/api/account/subAccountList`, //查询子账号
addOrUpdateSubAccount: `/aida/api/account/addOrUpdateSubAccount`, //添加子账号
deleteSubAccount: `/aida/api/account/deleteSubAccount`, //删除子账号
subAccountImportExcelDownload: `/aida/api/account/subAccountImportExcelDownload`, //批量添加模板下载模板
exportAccountsToExcel: `/aida/api/account/exportAccountsToExcel`, //教育版导出用户数据
getNextSequence: `/aida/api/project/getNextSequence`, //批量添加模板下载模板
subAccountImport: `/aida/api/account/subAccountImport`, //模板导入
getGenerateFrequency: `/aida/api/inquiry/getGenerateFrequency`, //积分使用详情
getAllGenerateFuncName: `/aida/api/inquiry/getAllGenerateFuncName`, //获取所有generate类型
//查询某个时间内design点击次数
getDesignStatistic: `/api/inquiry/getDesignStatistic`, //拒绝审批
getAllQuestionnaire: `/api/inquiry/getAllQuestionnaire`, //拒绝审批
getActiveUserFunc: `/api/inquiry/getActiveUserFunc`, //获取各模块功能
toProductImageElementDelete: `/api/history/toProductImageElementDelete`, //删除指定模块上传的内容
recentActiveUser: `/api/inquiry/recentActiveUser`, //获取近期活跃用户
recentActiveUserChart: `/api/inquiry/recentActiveUserChart`, //获取近期活跃用户图表数据
recentNewUser: `/api/inquiry/recentNewUser`, //获取近期新增用户
recentNewUserChart: `/api/inquiry/recentNewUserChart`, //获取新增用户图表
trialUserCountry: `/api/inquiry/trialUserCountry`, //试用用户国家-城市分布
conversionRate: `/api/inquiry/conversionRate`, //试用用户国家-城市分布
getAllUserId: `/api/inquiry/getAllUserId`, //获取所有用户id和Name
adminAddUser: `/api/inquiry/addUser`, //添加用户
modifyUser: `/api/inquiry/modifyUser`, //修改用户
publishSysMessage: `/api/message/publishSysMessage`, //发布系统任务
getDesignStatistic: `/aida/api/inquiry/getDesignStatistic`, //拒绝审批
getAllQuestionnaire: `/aida/api/inquiry/getAllQuestionnaire`, //拒绝审批
getActiveUserFunc: `/aida/api/inquiry/getActiveUserFunc`, //获取各模块功能
toProductImageElementDelete: `/aida/api/history/toProductImageElementDelete`, //删除指定模块上传的内容
recentActiveUser: `/aida/api/inquiry/recentActiveUser`, //获取近期活跃用户
recentActiveUserChart: `/aida/api/inquiry/recentActiveUserChart`, //获取近期活跃用户图表数据
recentNewUser: `/aida/api/inquiry/recentNewUser`, //获取近期新增用户
recentNewUserChart: `/aida/api/inquiry/recentNewUserChart`, //获取新增用户图表
trialUserCountry: `/aida/api/inquiry/trialUserCountry`, //试用用户国家-城市分布
conversionRate: `/aida/api/inquiry/conversionRate`, //试用用户国家-城市分布
getAllUserId: `/aida/api/inquiry/getAllUserId`, //获取所有用户id和Name
adminAddUser: `/aida/api/inquiry/addUser`, //添加用户
modifyUser: `/aida/api/inquiry/modifyUser`, //修改用户
publishSysMessage: `/aida/api/message/publishSysMessage`, //发布系统任务
//affiliate接口
viewsIncrease: `/api/affiliate/viewsIncrease`, //增加访问量
affiliateRegistration: `/api/affiliate/registration`, //affiliate注册
personalCenter: `/api/affiliate/personalCenter`, //affiliate个人中心
affiliateList: `/api/affiliate/list`, //affiliate审批列表
updateCommission: `/api/affiliate/updateCommission`, //编辑佣金比例
editAffiliate: `/api/affiliate/editAffiliate`, //编辑affiliate
getEachAffiliateGeneratedRevenue: `/api/affiliate/getEachAffiliateGeneratedRevenue`, //affiliate每个用户根据日期查询收益
affiliateApproval: `/api/affiliate/approval`, //affiliate同意 审批
getPersonalMonthlyIncome: `/api/affiliate/getPersonalMonthlyIncome`, //affiliate图表接口
getReferrals: `/api/affiliate/getReferrals`, //affiliate Referral列表
editReferral: `/api/affiliate/editReferral`, //affiliate编辑referral
batchDeleteReferral: `/api/affiliate/batchDeleteReferral`, //affiliate删除referral
// batchDeleteReferral:`/api/affiliate/batchDeleteReferral`,//affiliate删除referral
viewsIncrease: `/aida/api/affiliate/viewsIncrease`, //增加访问量
affiliateRegistration: `/aida/api/affiliate/registration`, //affiliate注册
personalCenter: `/aida/api/affiliate/personalCenter`, //affiliate个人中心
affiliateList: `/aida/api/affiliate/list`, //affiliate审批列表
updateCommission: `/aida/api/affiliate/updateCommission`, //编辑佣金比例
editAffiliate: `/aida/api/affiliate/editAffiliate`, //编辑affiliate
getEachAffiliateGeneratedRevenue: `/aida/api/affiliate/getEachAffiliateGeneratedRevenue`, //affiliate每个用户根据日期查询收益
affiliateApproval: `/aida/api/affiliate/approval`, //affiliate同意 审批
getPersonalMonthlyIncome: `/aida/api/affiliate/getPersonalMonthlyIncome`, //affiliate图表接口
getReferrals: `/aida/api/affiliate/getReferrals`, //affiliate Referral列表
editReferral: `/aida/api/affiliate/editReferral`, //affiliate编辑referral
batchDeleteReferral: `/aida/api/affiliate/batchDeleteReferral`, //affiliate删除referral
// batchDeleteReferral:`/aida/api/affiliate/batchDeleteReferral`,//affiliate删除referral
getTasksList: `/api/tasks/getList`, //获取w为执行完的所有任务
getTasksHistory: `/api/tasks/getAllTask`, //获取所有任务列表
prepareForSR: `/api/python/prepareForSR`, //超分
getTasksList: `/aida/api/tasks/getList`, //获取w为执行完的所有任务
getTasksHistory: `/aida/api/tasks/getAllTask`, //获取所有任务列表
prepareForSR: `/aida/api/python/prepareForSR`, //超分
//作品广场
publish: `/api/portfolio/publish`, //发布作品到作品广场
getPorfolio: `/api/portfolio/page`, //查询作品广场
getPorfolioDetail: `/api/portfolio/detail`, //查询作品广场作品详情
setPorfolioChoose: `/api/portfolio/choose`, //二次创作
portfolioLike: `/api/portfolio/like`, //作品广场点赞
portfolioNoLike: `/api/portfolio/unlike`, //作品广场取消点赞
portfolioComment: `/api/portfolio/comment`, //作品广场评论
portfolioCommentPage: `/api/portfolio/commentPage`, //作品广场评论列表
commentDelete: `/api/portfolio/commentDelete`, //删除评论
porfolioDelete: `/api/portfolio/delete`, //删除作品
porfolioFollow: `/api/portfolio/follow`, //删除作品
porfolioFollow: `/api/portfolio/follow`, //关注
porfolioCancelFollow: `/api/portfolio/cancelFollow`, //取消关注
porfolioGetFolloweeList: `/api/portfolio/getFolloweeList`, //获取关注列表
porfolioGetFollowerList: `/api/portfolio/getFollowerList`, //获取粉丝列表
publish: `/aida/api/portfolio/publish`, //发布作品到作品广场
getPorfolio: `/aida/api/portfolio/page`, //查询作品广场
getPorfolioDetail: `/aida/api/portfolio/detail`, //查询作品广场作品详情
setPorfolioChoose: `/aida/api/portfolio/choose`, //二次创作
portfolioLike: `/aida/api/portfolio/like`, //作品广场点赞
portfolioNoLike: `/aida/api/portfolio/unlike`, //作品广场取消点赞
portfolioComment: `/aida/api/portfolio/comment`, //作品广场评论
portfolioCommentPage: `/aida/api/portfolio/commentPage`, //作品广场评论列表
commentDelete: `/aida/api/portfolio/commentDelete`, //删除评论
porfolioDelete: `/aida/api/portfolio/delete`, //删除作品
porfolioFollow: `/aida/api/portfolio/follow`, //删除作品
porfolioFollow: `/aida/api/portfolio/follow`, //关注
porfolioCancelFollow: `/aida/api/portfolio/cancelFollow`, //取消关注
porfolioGetFolloweeList: `/aida/api/portfolio/getFolloweeList`, //获取关注列表
porfolioGetFollowerList: `/aida/api/portfolio/getFollowerList`, //获取粉丝列表
//product生成
toProduct: `/api/history/toProduct`, //开始生成
toProductImageResult: `/api/history/toProductImageResult`, //获取结果
toProductImageElementUpload: `/api/history/toProductImageElementUpload`, //上传
historyDeleteResult: `/api/history/deleteResult`, //relight toproduct删除
generateDeleteResult: `/api/generate/deleteResult`, //pose删除
toProduct: `/aida/api/history/toProduct`, //开始生成
toProductImageResult: `/aida/api/history/toProductImageResult`, //获取结果
toProductImageElementUpload: `/aida/api/history/toProductImageElementUpload`, //上传
historyDeleteResult: `/aida/api/history/deleteResult`, //relight toproduct删除
generateDeleteResult: `/aida/api/generate/deleteResult`, //pose删除
productImageLike: `/api/history/productImageLike`, //like生成结果
productImageUnLike: `/api/history/productImageUnLike`, //取消like生成结果
productImageLikeList: `/api/history/productImageLikeList`, //like生成结果
productImageLike: `/aida/api/history/productImageLike`, //like生成结果
productImageUnLike: `/aida/api/history/productImageUnLike`, //取消like生成结果
productImageLikeList: `/aida/api/history/productImageLikeList`, //like生成结果
//打光
relight: `/api/history/relight`, //开始生成
relightResult: `/api/history/relightResult`, //开始生成
relight: `/aida/api/history/relight`, //开始生成
relightResult: `/aida/api/history/relightResult`, //开始生成
//保存画布
canvasElementUpload: `/api/history/canvasElementUpload`, //画布上传临时图片
exportSave: `/api/history/exportSave`, //保存画布
exportSearch: `/api/history/exportSearch`, //保存画布
canvasElementUpload: `/aida/api/history/canvasElementUpload`, //画布上传临时图片
exportSave: `/aida/api/history/exportSave`, //保存画布
exportSearch: `/aida/api/history/exportSearch`, //保存画布
//活动
activity: `/api/account/activity`,
activity: `/aida/api/account/activity`,
//bradDNA
brandLogoUpload: `/api/history/brandLogoUpload`, //上传bradDNA
brandDNAGenerate: `/api/history/brandDNAGenerate`, //上传bradDNA
brandDNAUpload: `/api/history/brandDNAUpload`, //上传DNA图片
getInitializeProgress: `/api/history/getInitializeProgress`, //获取brand进度
brandDNADelete: `/api/history/brandDNADelete`, //删除brandDna
brandLogoUpload: `/aida/api/history/brandLogoUpload`, //上传bradDNA
brandDNAGenerate: `/aida/api/history/brandDNAGenerate`, //上传bradDNA
brandDNAUpload: `/aida/api/history/brandDNAUpload`, //上传DNA图片
getInitializeProgress: `/aida/api/history/getInitializeProgress`, //获取brand进度
brandDNADelete: `/aida/api/history/brandDNADelete`, //删除brandDna
brandDNAPage: `/api/history/brandDNAPage`, //brand列表
brandDNASaveOrUpdate: `/api/history/brandDNASaveOrUpdate`, //提交个人信息
productImageInitialize: `/api/history/productImageInitialize`, //产品识别
brandDNAPage: `/aida/api/history/brandDNAPage`, //brand列表
brandDNASaveOrUpdate: `/aida/api/history/brandDNASaveOrUpdate`, //提交个人信息
productImageInitialize: `/aida/api/history/productImageInitialize`, //产品识别
//调查问卷
questionnaire: `/api/account/questionnaire`, //保存画布
questionnaire: `/aida/api/account/questionnaire`, //保存画布
//消息系统
getUnreadCount: `/api/message/getUnreadCount`, //获取未读消息
setReadStatus: `/api/message/setReadStatus`, //设置消息已读
getHistoryNotification: `/api/message/getHistoryNotification`, //获取历史消息
oneClickRead: `/api/message/oneClickRead`, //全部设为已读
personalHomepage: `/api/account/personalHomepage`, //获取个人主页信息
refreshMinioUrl: `/api/third/party/refreshMinioUrl`, //获取可以使用的minio地址
getUnreadCount: `/aida/api/message/getUnreadCount`, //获取未读消息
setReadStatus: `/aida/api/message/setReadStatus`, //设置消息已读
getHistoryNotification: `/aida/api/message/getHistoryNotification`, //获取历史消息
oneClickRead: `/aida/api/message/oneClickRead`, //全部设为已读
personalHomepage: `/aida/api/account/personalHomepage`, //获取个人主页信息
refreshMinioUrl: `/aida/api/third/party/refreshMinioUrl`, //获取可以使用的minio地址
// 画布
segAnything: `/api/python/segAnything`, //分割Anything
segAnything: `/aida/api/python/segAnything`, //分割Anything
// award页面
checkEmail: '/api/global-award/checkEmail', // 检查邮箱是否存在
checkOTP: '/api/global-award/checkCode', // 检查验证码是否正确
initPdfUpload: '/api/global-award/uploads/pdf/init', // 初始化pdf上传
initVideoUpload: '/api/global-award/uploads/video/init', // 初始化video上传
uploadPDF: '/api/global-award/uploads/pdf/chunk', // 上传pdf
uploadVideo: '/api/global-award/uploads/video/chunk', // 上传video
uploadPDFComplete: '/api/global-award/uploads/pdf/complete', // 上传pdf完成
uploadVideoComplete: '/api/global-award/uploads/video/complete', // 上传video完成
submitForm: '/api/global-award/contestants/save', // 提交表单
getContestantByID: '/api/global-award/contestants/' // 获取表单
checkEmail: '/aida/api/global-award/checkEmail', // 检查邮箱是否存在
checkOTP: '/aida/api/global-award/checkCode', // 检查验证码是否正确
initPdfUpload: '/aida/api/global-award/uploads/pdf/init', // 初始化pdf上传
initVideoUpload: '/aida/api/global-award/uploads/video/init', // 初始化video上传
uploadPDF: '/aida/api/global-award/uploads/pdf/chunk', // 上传pdf
uploadVideo: '/aida/api/global-award/uploads/video/chunk', // 上传video
uploadPDFComplete: '/aida/api/global-award/uploads/pdf/complete', // 上传pdf完成
uploadVideoComplete: '/aida/api/global-award/uploads/video/complete', // 上传video完成
submitForm: '/aida/api/global-award/contestants/save', // 提交表单
getContestantByID: '/aida/api/global-award/contestants/', // 获取表单
// 卖家端接口
sellerUploadFile: '/seller/file/upload', // 卖家上传文件
checkSellerDesigner: '/seller/designer/check', // 检查卖家是否为设计师
getSellerApplyStatus: '/seller/designer/apply/status', // 获取卖家申请状态
submitSellerApply: '/seller/designer/apply', // 提交卖家申请
deleteSellerDesigner: '/seller/designer/delete', // 删除设计师
getDesignerInfo: '/seller/designer/info', // 获取设计师信息
updateDesignerInfo: '/seller/designer/update', // 更新设计师信息
getSellerOrderSummary: '/seller/order/summary', // 获取卖家订单数据总览
getSellerOrderList: '/seller/order/page', // 获取卖家订单列表
getListingPopup: '/seller/listing/popup/check', // 获取是否勾选发布作品提示
setListingPopup: '/seller/listing/popup/set', // 设置是否勾选发布作品提示
getListingList: '/seller/listing/page', // 获取商品列表,发布和未发布
putListingStatus: '/seller/listing/status', // 更新商品状态
},
axiosGet(url, config) {
axiosGet(url, config, pathParams) {
return new Promise((resolve, reject) => {
if (isLoginTime && url != '/api/portfolio/page') {
if (isLoginTime && url != '/aida/api/portfolio/page') {
resolve('')
return
}
axios
.get(url, config)
.get(setPathParams(url, pathParams), config)
.then(response => {
resolve(response)
})
@@ -493,14 +499,14 @@ export const Https = {
})
},
axiosPut(url, data) {
axiosPut(url, data, pathParams) {
return new Promise((resolve, reject) => {
if (isLoginTime && url != '/api/portfolio/page') {
if (isLoginTime && url != '/aida/api/portfolio/page') {
resolve('')
return
}
axios
.put(url, data)
.put(setPathParams(url, pathParams), data)
.then(response => {
resolve(response)
})
@@ -510,14 +516,14 @@ export const Https = {
})
},
axiosPost(url, data, config) {
axiosPost(url, data, config, pathParams) {
return new Promise((resolve, reject) => {
if (isLoginTime && url != '/api/portfolio/page') {
if (isLoginTime && url != '/aida/api/portfolio/page') {
resolve('')
return
}
axios
.post(url, data, config)
.post(setPathParams(url, pathParams), data, config)
.then(response => {
resolve(response)
})
@@ -527,14 +533,14 @@ export const Https = {
})
},
axiosDelete(url, newData) {
axiosDelete(url, newData, pathParams) {
return new Promise((resolve, reject) => {
if (isLoginTime && url != '/api/portfolio/page') {
if (isLoginTime && url != '/aida/api/portfolio/page') {
resolve('')
return
}
axios
.delete(url, { data: newData })
.delete(setPathParams(url, pathParams), { data: newData })
.then(response => {
resolve(response)
})
@@ -544,3 +550,12 @@ export const Https = {
})
}
}
function setPathParams(url, pathParams = {}) {
const obj = typeof pathParams === 'object' ? pathParams : { id: pathParams }
const keys = Object.keys(obj)
keys.forEach(key => {
url = url.replace(new RegExp(`\\{${key}\\}`, 'g'), obj[key]);
})
return url
}

View File

@@ -672,28 +672,40 @@ function sketchToMask(sketchImage) {
img.src = sketchImage;
});
}
function isValidUrl(string) {
try {
const url = new URL(string)
// 通常我们只需要 http 或 https 协议
return url.protocol === "http:" || url.protocol === "https:"
} catch (err) {
return false
}
}
export {
isEmail,
getUploadUrl,
getUniversalZoomLevel,
rgbaToHex,
getMinioUrl,
base64ToFile,
dataURLtoFile,
blobToFile,
base64toFile,
rgbToHsv,
formatTime,
dataURLtoBlob,
isMoible,
downloadIamge,
downloadVideoWithFetch,
getBrowserInfo,
setPubDate,
murmur,
setGradual,
calculateGradientCoordinate,
segmentImage,
UrlToFile,
sketchToMask
isEmail,
getUploadUrl,
getUniversalZoomLevel,
rgbaToHex,
getMinioUrl,
base64ToFile,
dataURLtoFile,
blobToFile,
base64toFile,
rgbToHsv,
formatTime,
dataURLtoBlob,
isMoible,
downloadIamge,
downloadVideoWithFetch,
getBrowserInfo,
setPubDate,
murmur,
setGradual,
calculateGradientCoordinate,
segmentImage,
UrlToFile,
sketchToMask,
isValidUrl
}

View File

@@ -318,7 +318,7 @@
</div>
</div>
<div class="homeMain_user">
<div class="homeMain_user_icon" @click="openAccount">
<div class="homeMain_user_icon">
<img :src="userDetail.avatar" alt="" />
</div>
<div class="homeMain_user_detail">
@@ -373,11 +373,15 @@
<i class="fi fi-rs-notebook"></i>
<span class="select_item_des">{{ $t('Header.ViewOrders') }}</span>
</div>
<div class="select_item" @click="onBecomeSeller">
<div class="select_item" @click="openAccount">
<span class="icon"><svg-icon name="home" /></span>
<span class="select_item_des">{{ $t('Header.PersonalCenter') }}</span>
</div>
<div class="select_item" @click="onBecomeSeller" v-if="!isSeller">
<span class="icon"><svg-icon name="seller-sellerIndex" /></span>
<span class="select_item_des">{{ $t('Header.BecomeSeller') }}</span>
</div>
<div class="select_item" @click="onSellerDashboard">
<div class="select_item" @click="onSellerDashboard" v-else>
<span class="icon"><svg-icon name="seller-sellerIndex" /></span>
<span class="select_item_des">{{ $t('Header.SellerDashboard') }}</span>
<a-badge :dot="true"></a-badge>
@@ -409,7 +413,7 @@
<a href="https://www.facebook.com/CodeCreateAI" target="_blank" >
<img src="@/assets/images/socialMediaLogo/faceBookIcon.svg" alt="">
</a>
<a href="https://www.youtube.com/@AiDA-3.1" target="_blank" >
<a href="https://www.youtube.com/@Code-Create_AiDA" target="_blank" >
<img src="@/assets/images/socialMediaLogo/socialIcons.svg" alt="">
</a>
<a href="https://www.linkedin.com/company/code-create-limited" target="_blank" >
@@ -1073,9 +1077,13 @@ export default defineComponent({
}
}
const isSeller = computed(() => {
return store.state.seller.isSeller
})
return {
store,
userDetail,
isSeller,
t,
...toRefs(homeMainData),
...toRefs(historyData),
@@ -1150,6 +1158,7 @@ export default defineComponent({
reject()
})
})
this.store.dispatch('seller/get_isSeller')//获取是否是卖家
let isMurmur = getCookie('isMurmur') //获取是否是试用用户
this.isMurmur = JSON.parse(isMurmur)
if (this.userDetail.userId && this.userDetail.userId > -1) {
@@ -1378,7 +1387,6 @@ export default defineComponent({
this.store.commit('set_dataLoading', false)
})
},
setLocale(v) {
// 同步更新 localStorage 中的 loginLanguage
if (v) {

View File

@@ -62,7 +62,7 @@ import { ExclamationCircleOutlined } from '@ant-design/icons-vue'
import { useStore } from 'vuex'
import { setLang } from '@/tool/guide'
import { useI18n } from 'vue-i18n'
import { useRouter } from 'vue-router'
import { useRouter, useRoute } from 'vue-router'
import { gsap, TweenMax } from 'gsap'
import { ScrollTrigger } from 'gsap/ScrollTrigger'
export default defineComponent({
@@ -71,6 +71,7 @@ export default defineComponent({
const {t, locale} = useI18n()
const store = useStore()
const router = useRouter()
const route = useRoute()
let registerModel = ref()
let data = reactive({})
@@ -117,7 +118,14 @@ export default defineComponent({
onMounted(() => {
window.addEventListener('resize', updataIsMoblie)
// 初始化语言设置
const savedLang = localStorage.getItem('loginLanguage')
let savedLang = localStorage.getItem('loginLanguage')
if(route?.params?.lang == 'cn'){
savedLang = 'CHINESE_SIMPLIFIED'
localStorage.setItem('loginLanguage', savedLang)
}{
savedLang = 'ENGLISH'
localStorage.setItem('loginLanguage', savedLang)
}
if (savedLang) {
isChinese.value = savedLang === 'CHINESE_SIMPLIFIED'
locale.value = savedLang

View File

@@ -338,7 +338,7 @@
<a-upload
class="search_upImg"
:capture="null"
:action="uploadUrl + '/api/element/upload'"
:action="uploadUrl + '/aida/api/element/upload'"
list-type="picture-card"
:before-upload="beforeUpload"
:data="{
@@ -387,7 +387,7 @@
<a-upload
class="search_upImg"
:capture="null"
:action="uploadUrl + '/api/element/upload'"
:action="uploadUrl + '/aida/api/element/upload'"
list-type="picture-card"
:data="{
...upload,

View File

@@ -1,12 +1,9 @@
<template>
<div class="become-seller">
<seller-header
title="Apply to Become a Seller"
tip="Join the Stylish Parade and start selling your design work"
/>
<seller-header />
<div class="content">
<seller-review v-if="isSubmit" />
<seller-apply v-else @submit="isSubmit = true" />
<seller-apply v-if="applyStatus === null" @submit="onSubmit" />
<seller-review v-else />
</div>
</div>
</template>
@@ -16,7 +13,20 @@
import sellerHeader from "../seller-header.vue"
import sellerReview from "./sellerReview.vue"
import sellerApply from "./sellerApply.vue"
const isSubmit = ref(false)
import { Https } from "@/tool/https"
import { useStore } from "vuex"
import { ApplyStatus } from "@/store/seller/index.d"
const store = useStore()
const applyStatus = computed(() => store.state.seller.applyStatus)
const onSubmit = () => store.commit("seller/set_applyStatus", ApplyStatus.Pending)
const getSellerApplyStatus = () => {
store.commit("set_loading", true)
Https.axiosGet(Https.httpUrls.getSellerApplyStatus).then((res) => {
store.commit("set_loading", false)
store.commit("seller/set_applyStatus", res)
})
}
getSellerApplyStatus()
</script>
<style scoped lang="less">
.become-seller {

View File

@@ -2,56 +2,58 @@
<div class="seller-apply">
<div class="session">
<div class="content mini-scrollbar">
<div class="title">Brand Information</div>
<div class="tip">Share a few details to set up your seller profile</div>
<div class="title">{{ $t("ApplySeller.formTitle") }}</div>
<div class="tip">{{ $t("ApplySeller.formTip") }}</div>
<div class="form">
<a-form :model="formData" :rules="formRules" layout="vertical" ref="formRef">
<a-form-item label="Store Name" name="storeName">
<a-form-item :label="$t('Seller.storeName')" name="storeName">
<a-input
v-model:value="formData.storeName"
placeholder="Enter the store name"
:placeholder="$t('Seller.storeNameDesc')"
:maxlength="80"
/>
<span class="tip-length">{{ formData.storeName.length }}/80</span>
</a-form-item>
<a-form-item label="Owners Full Name" name="fullName">
<a-form-item :label="$t('Seller.ownerName')" name="fullName">
<a-input
v-model:value="formData.fullName"
placeholder="Enter store owner's full name"
:placeholder="$t('Seller.ownerNameDesc')"
/>
</a-form-item>
<div class="form-group">
<a-form-item label="Email" name="email">
<a-form-item :label="$t('Seller.email')" name="email">
<a-input
type="email"
v-model:value="formData.email"
placeholder="Enter email"
:placeholder="$t('Seller.emailDesc')"
/>
</a-form-item>
<a-form-item label="Phone Number" name="phoneNumber">
<a-form-item :label="$t('Seller.mobile')" name="mobile">
<a-input
type="tel"
v-model:value="formData.phoneNumber"
placeholder="Enter phone number"
v-model:value="formData.mobile"
:placeholder="$t('Seller.mobileDesc')"
/>
</a-form-item>
</div>
<a-form-item label="Store Description" name="description">
<a-form-item :label="$t('Seller.storeDescription')" name="description">
<a-textarea
v-model:value="formData.description"
placeholder="Briefly describe your design style and store features..."
:placeholder="$t('Seller.storeDescriptionDesc')"
:maxlength="500"
/>
<span class="tip-length">{{ formData.description.length }}/500</span>
</a-form-item>
<a-form-item label="Portfoilo/Social Media Links">
<a-form-item :label="$t('Seller.links')">
<a-input
placeholder="https://"
v-for="(v, i) in formData.links"
:key="i"
v-model:value="formData.links[i]"
>
<template #prefix>Link {{ i + 1 }}</template>
<template #prefix>{{
$t("Seller.link", { index: i + 1 })
}}</template>
</a-input>
<a-input
placeholder="https://"
@@ -71,33 +73,25 @@
</div>
<div class="session">
<div class="content">
<div class="title">Brand Information</div>
<div class="tip">Share a few details to set up your seller profile</div>
<div class="title">{{ $t("ApplySeller.termsTitle") }}</div>
<div class="tip">{{ $t("ApplySeller.termsTip") }}</div>
<div class="agreement">
<div class="title">AiDA Seller Agreement</div>
<div class="tip">
By checking the box below, you agree to comply with the following terms:
</div>
<div class="title">{{ $t("ApplySeller.agreementTitle") }}</div>
<div class="tip">{{ $t("ApplySeller.agreementTip") }}</div>
<ul>
<li>Provide accurate and truthful personal and store information</li>
<li>Only sell original designs or content with proper licensing</li>
<li>Maintain high quality standards for all products</li>
<li>Respond to customer inquiries within 48 hours</li>
<li>Ship orders within promised timeframes</li>
<li>Comply with AiDA's terms of service and community guidelines</li>
<li>Pay applicable platform fees and transaction charges</li>
<li>Handle customer disputes professionally and fairly</li>
<li v-for="(v, i) in 8" :key="i">
{{ $t(`ApplySeller.agreementItem${i + 1}`) }}
</li>
</ul>
</div>
<a-checkbox class="agree-agreement" v-model:checked="isAgreement">
I have read and agree to the Seller Agreement, understanding my responsibilities
and obligations as a seller on the AiDA platform.
{{ $t("ApplySeller.agreementAgreement") }}
</a-checkbox>
</div>
<div class="btns">
<button class="cancel" @click="onCancel">Cancel</button>
<button class="cancel" @click="onCancel">{{ $t("Seller.cancel") }}</button>
<button class="submit" :disabled="!isAgreement" @click="onSubmit">
Submit Application
{{ $t("ApplySeller.submitApplication") }}
</button>
</div>
</div>
@@ -106,23 +100,26 @@
<script setup>
import { ref, reactive } from "vue"
import { useI18n } from "vue-i18n"
const { t } = useI18n()
import { useRoute, useRouter } from "vue-router"
const route = useRoute()
const router = useRouter()
import { Https } from "@/tool/https"
const emit = defineEmits(["submit"])
const formRules = {
storeName: [{ required: true, message: "Enter the store name" }],
fullName: [{ required: true, message: "Enter store owner's full name" }],
email: [{ required: true, message: "Enter email" }],
phoneNumber: [{ required: true, message: "Enter phone number" }],
description: [{ required: true, message: "Enter store description" }]
storeName: [{ required: true, message: t("Seller.storeNameDesc") }],
fullName: [{ required: true, message: t("Seller.ownerNameDesc") }],
email: [{ required: true, message: t("Seller.emailDesc") }],
mobile: [{ required: true, message: t("Seller.mobileDesc") }],
description: [{ required: true, message: t("Seller.storeDescriptionErr") }]
}
const formRef = ref(null)
const formData = reactive({
storeName: "",
fullName: "",
email: "",
phoneNumber: "",
mobile: "",
description: "",
links: ["", ""]
})
@@ -139,8 +136,20 @@
formRef.value
.validate()
.then(() => {
console.log(formData)
emit("submit")
const data = {
// userId: 0,
shopName: formData.storeName,
// avatar: "",
// brandBanner: "",
ownerName: formData.fullName,
email: formData.email,
mobile: formData.mobile,
description: formData.description,
socialLinks: JSON.stringify(formData.links.filter((v) => v))
}
Https.axiosPost(Https.httpUrls.submitSellerApply, data).then((res) => {
emit("submit")
})
})
.catch(() => {
console.log("validate failed")

View File

@@ -1,11 +1,11 @@
<template>
<div class="seller-review">
<img class="success" src="@/assets/images/seller/success-1.png" />
<div class="title">Application Submitted</div>
<div class="tip">
Our team will review your application and get back to you within 13 business days.
You'll receive a notification in your email once a decision has been made.
</div>
<div class="title">{{ $t("ApplySeller.applicationSubmitted") }}</div>
<div
class="tip"
v-html="$t('ApplySeller.applicationSubmittedTip', { click: 'onPersonalCenter' })"
></div>
<div class="step-list">
<div v-for="v in list" :key="v.title" class="step-item">
<img v-show="!v.active" src="@/assets/images/seller/success-0.png" />
@@ -16,35 +16,49 @@
</div>
</div>
</div>
<button class="home-btn" @click="onBackToHome">Back to Homepage</button>
<button class="home-btn" @click="onBackToHome">
{{ $t("ApplySeller.backToHomepage") }}
</button>
<div class="tip">ID: {{ userId }}</div>
</div>
</template>
<script setup>
import { ref, computed } from "vue"
import { useRoute, useRouter } from "vue-router"
import { useStore } from "vuex"
import { ApplyStatus } from "@/store/seller/index.d"
import { useI18n } from "vue-i18n"
const { t } = useI18n()
const route = useRoute()
const router = useRouter()
const list = ref([
const store = useStore()
const userId = computed(() => store.state.UserHabit.userDetail.userId)
const applyStatus = computed(() => store.state.seller.applyStatus)
const list = computed(() => [
{
title: "Step 1: Submit Application",
tip: "Fill out the seller information form and agree to our terms",
active: true
title: t("ApplySeller.auditStatus1_title"),
tip: t("ApplySeller.auditStatus1_tip"),
active: [ApplyStatus.Pending, ApplyStatus.Approved].includes(applyStatus.value)
},
{
title: "Step 2: Review & Verification",
tip: "Our team will review your application (typically 1-3 business days)",
active: false
title: t("ApplySeller.auditStatus2_title"),
tip: t("ApplySeller.auditStatus2_tip"),
active: applyStatus.value === ApplyStatus.Approved
},
{
title: "Step 3: Start Selling",
tip: "Once approved, access your seller dashboard and start listing products ",
active: false
title: t("ApplySeller.auditStatus3_title"),
tip: t("ApplySeller.auditStatus3_tip"),
active: applyStatus.value === ApplyStatus.Approved
}
])
const onBackToHome = () => {
router.push({ name: "home" })
}
window.onPersonalCenter = () => {
router.push("/home/account")
}
</script>
<style scoped lang="less">
.seller-review {
@@ -69,11 +83,15 @@
color: #000;
}
> .tip {
font-family: pingfang_medium;
font-family: pingfang_regular;
font-size: 1.8rem;
line-height: 170%;
text-align: center;
color: #585858;
&:deep(span) {
color: #585858;
font-family: pingfang_heavy;
}
}
> .step-list {
margin: 2.6rem 0;

View File

@@ -2,53 +2,53 @@
<div class="brand-info">
<a-form :model="formData" :rules="isEdit ? formRules : {}" layout="vertical" ref="formRef">
<div class="form-group">
<a-form-item label="Store Name" name="storeName">
<a-form-item :label="$t('Seller.storeName')" name="shopName">
<a-input
v-model:value="formData.storeName"
placeholder="Enter the store name"
v-model:value="formData.shopName"
:placeholder="$t('Seller.storeNameDesc')"
:maxlength="80"
:readonly="!isEdit"
/>
<span v-show="isEdit" class="tip-length"
>{{ formData.storeName.length }}/80</span
>{{ formData.shopName.length }}/80</span
>
</a-form-item>
<a-form-item label="Owners Full Name" name="fullName">
<a-form-item :label="$t('Seller.ownerName')" name="ownerName">
<a-input
v-model:value="formData.fullName"
placeholder="Enter store owner's full name"
v-model:value="formData.ownerName"
:placeholder="$t('Seller.ownerNameDesc')"
:readonly="!isEdit"
/>
</a-form-item>
</div>
<div class="form-group">
<a-form-item label="Email" name="email">
<a-form-item :label="$t('Seller.email')" name="email">
<a-input
type="email"
v-model:value="formData.email"
placeholder="Enter email"
:placeholder="$t('Seller.emailDesc')"
:readonly="!isEdit"
/>
</a-form-item>
<a-form-item label="Phone Number" name="phoneNumber">
<a-form-item :label="$t('Seller.mobile')" name="mobile">
<a-input
type="tel"
v-model:value="formData.phoneNumber"
placeholder="Enter phone number"
v-model:value="formData.mobile"
:placeholder="$t('Seller.mobileDesc')"
:readonly="!isEdit"
/>
</a-form-item>
</div>
<div class="form-group">
<a-form-item label="Portfoilo/Social Media Links">
<a-form-item :label="$t('Seller.links')">
<a-input
placeholder="https://"
v-for="(v, i) in formData.links"
v-for="(v, i) in formData.socialLinks"
:key="i"
v-model:value="formData.links[i]"
v-model:value="formData.socialLinks[i]"
:readonly="!isEdit"
>
<template #prefix>Link {{ i + 1 }}</template>
<template #prefix>{{ $t("Seller.link", { index: i + 1 }) }}</template>
</a-input>
<a-input
placeholder="https://"
@@ -63,10 +63,10 @@
</template>
</a-input>
</a-form-item>
<a-form-item label="Store Description" name="description">
<a-form-item :label="$t('Seller.storeDescription')" name="description">
<a-textarea
v-model:value="formData.description"
placeholder="Briefly describe your design style and store features..."
:placeholder="$t('Seller.storeDescriptionDesc')"
:maxlength="500"
:readonly="!isEdit"
/>
@@ -82,6 +82,12 @@
<script setup>
import { ref, reactive, watch } from "vue"
import { useRoute, useRouter } from "vue-router"
import { useStore } from "vuex"
import { useI18n } from "vue-i18n"
const { t } = useI18n()
const store = useStore()
const designerInfo = computed(() => store.state.seller.designerInfo)
const route = useRoute()
const router = useRouter()
const props = defineProps({
@@ -91,48 +97,53 @@
}
})
const formRules = {
storeName: [{ required: true, message: "Enter the store name" }],
fullName: [{ required: true, message: "Enter store owner's full name" }],
email: [{ required: true, message: "Enter email" }],
phoneNumber: [{ required: true, message: "Enter phone number" }],
description: [{ required: true, message: "Enter store description" }]
shopName: [{ required: true, message: t("Seller.storeNameDesc") }],
ownerName: [{ required: true, message: t("Seller.ownerNameDesc") }],
email: [{ required: true, message: t("Seller.emailDesc") }],
mobile: [{ required: true, message: t("Seller.mobileDesc") }],
description: [{ required: true, message: t("Seller.storeDescriptionErr") }]
}
const formRef = ref(null)
const formData = reactive({
storeName: "",
fullName: "",
shopName: "",
ownerName: "",
email: "",
phoneNumber: "",
mobile: "",
description: "",
links: ["", ""]
socialLinks: ["", ""]
})
const newLink = ref("")
const addLink = () => {
formData.links.push(newLink.value)
formData.socialLinks.push(newLink.value)
newLink.value = ""
}
watch(
() => designerInfo.value,
(v) => updateFormData()
)
const updateFormData = () => {
formData.shopName = designerInfo.value.shopName
formData.ownerName = designerInfo.value.ownerName
formData.email = designerInfo.value.email
formData.mobile = designerInfo.value.mobile
formData.description = designerInfo.value.description
formData.socialLinks = JSON.parse(JSON.stringify(designerInfo.value.socialLinks))
}
updateFormData()
watch(
() => props.isEdit,
(v) => (v ? edit() : cancel())
)
const edit = () => {
formData.storeName = "测试"
formData.fullName = "测试"
formData.email = "测试"
formData.phoneNumber = "测试"
formData.description = "测试"
formData.links = ["https://www.baidu.com", "https://www.taobao.com"]
}
const edit = () => {}
const cancel = () => {
formRef.value.clearValidate()
edit()
updateFormData()
}
const submit = async () => {
const valid = await formRef.value.validate()
if (!valid) return Promise.reject(false)
console.log(valid)
return valid
await formRef.value.validate()
return JSON.parse(JSON.stringify(formData))
}
defineExpose({
submit

View File

@@ -1,6 +1,7 @@
<template>
<a-modal
class="image-clip-dialog generalModel"
:class="{ 'is-product': data.isProduct }"
v-model:visible="show"
:footer="null"
width="70%"
@@ -9,30 +10,66 @@
:closable="false"
wrapClassName="#app"
:keyboard="false"
:destroyOnClose="true"
>
<div class="image-clip-dialog-box">
<div class="header">
<div class="header" :class="{ 'is-product': data.isProduct }">
<div class="title">{{ data.title }}</div>
<div class="right">
<div class="right flex">
<div v-if="coverOrigin.length > 1" class="origin-container flex align-center">
<span>{{ $t("Seller.cropFrom") }}</span>
<div class="origin-select flex align-center">
<div
class="origin-item sketch"
:class="{ selected: currentOrigin === 'sketch' }"
@click="handleChangeOrigin('sketch')"
>
{{ $t("Seller.sketch") }}
</div>
<div
class="origin-item product"
:class="{ selected: currentOrigin === 'mainProductImage' }"
@click="handleChangeOrigin('mainProductImage')"
>
{{ $t("Seller.mainProductImage") }}
</div>
</div>
</div>
<div class="submit" v-if="!data.isPreview" @click="onSubmit">
<svg-icon name="seller-dui" size="24" />
</div>
<button @click="onCancel">Cancel</button>
<button @click="onCancel">{{ $t("Seller.cancel") }}</button>
</div>
</div>
<div class="content">
<image-clip
ref="imageClipRef"
:ratio="data.ratio"
:url="data.url"
@change="(v) => (data.preview_url = v)"
/>
<div class="preview" v-if="data.isPreview">
<div class="content" :class="{ 'is-product': data.isProduct }">
<div class="crop-wrapper">
<div v-if="data.isProduct" class="tips">{{ tips }}</div>
<image-clip
ref="imageClipRef"
v-bind="$attrs"
:ratio="data.ratio"
:isProduct="isProduct"
:url="data.url"
:type="type"
@change="(v) => (data.preview_url = v)"
/>
</div>
<div
class="preview"
:class="{
'is-product': data.isProduct,
'is-apparel': type === 'apparel',
'is-cover': type === 'cover'
}"
v-if="data.isPreview"
>
<div class="title">
<span class="icon"><svg-icon name="seller-preview" size="24" /></span>
<span class="label">Crop Preview</span>
<span class="label">{{ $t("Seller.cropPreview") }}</span>
</div>
<div class="preview-image">
<img :src="data.preview_url" />
</div>
<img :src="data.preview_url" />
<div class="submit" @click="onSubmit">
<svg-icon name="seller-dui" size="24" />
</div>
@@ -43,29 +80,82 @@
</template>
<script setup>
import { ref } from "vue"
import { ref, reactive, computed } from "vue"
import ImageClip from "./image-clip.vue"
import { useI18n } from "vue-i18n"
const { t } = useI18n()
const props = defineProps({
type: {
type: String,
default: ""
},
isProduct: {
type: Boolean,
default: false
}
})
const tips = computed(() => {
if (props.type === "cover") {
return t("Seller.imageClipCoverTip")
}
if (props.type === "mainProductImage") {
return t("Seller.imageClipMainProductImageTip")
}
if (props.type === "sketch") {
return t("Seller.imageClipSketchTip")
}
if (props.type === "apparel") {
return t("Seller.imageClipApparelTip")
}
})
const data = reactive({
url: "",
title: "Crop Image",
preview_url: "",
ratio: [1, 1],
isPreview: true,
callback: null
callback: null,
isProduct: false // 是否商品编辑
})
const currentOrigin = ref("sketch")
const coverOrigin = ref([])
const handleChangeOrigin = (type) => {
const targetOrigin = coverOrigin.value.find((el) => el.type === type)
if (!targetOrigin) return
currentOrigin.value = type
data.url = targetOrigin.url
}
const show = ref(false)
const open = (url, callback, options) => {
if (!url || !callback) return
const open = (url, callback, options, origin) => {
if (!props.isProduct) {
if (!url || !callback) return
}
coverOrigin.value = []
data.url = null
currentOrigin.value = "sketch"
data.url = url
data.callback = callback
data.ratio = [1, 1]
data.ratio = options.ratio || [1, 1]
data.isPreview = true
data.preview_url = ""
data.title = "Crop Image"
data.title = options.title || "Crop Image"
if (options) {
if (options.hasOwnProperty("isPreview")) data.isPreview = options.isPreview
if (options.hasOwnProperty("ratio")) data.ratio = options.ratio
if (options.hasOwnProperty("title")) data.title = options.title
data.isProduct = options.isProduct
}
if (origin?.length) {
coverOrigin.value = origin
const defaultOrigin = origin.find((el) => el.type === options?.coverFrom) || origin[0]
currentOrigin.value = defaultOrigin.type
data.url = defaultOrigin.url
}
show.value = true
}
@@ -75,7 +165,7 @@
const imageClipRef = ref(null)
const onSubmit = () => {
imageClipRef.value.getCropBlob().then((blob) => {
if (data.callback) data.callback(blobToFile(blob, "image.png"))
if (data.callback) data.callback(blobToFile(blob, "image.png"), currentOrigin.value)
onCancel()
})
}
@@ -106,6 +196,9 @@
display: flex;
justify-content: space-between;
margin-bottom: 5rem;
&.is-product {
margin-bottom: 2.4rem;
}
> .title {
font-family: pingfang_heavy;
font-size: 2.4rem;
@@ -126,6 +219,35 @@
font-size: 1.6rem;
color: #000;
}
.origin-container {
font-weight: 400;
color: #000;
font-size: 1.4rem;
.origin-select {
margin-left: 1.2rem;
height: 4.8rem;
border: 1px solid #c7c7c7;
border-radius: 3rem;
column-gap: 1.2rem;
padding: 0.8rem;
.origin-item {
height: 3.2rem;
line-height: 3.2rem;
border-radius: 2rem;
cursor: pointer;
&.selected {
background-color: #000;
color: #fff;
}
&.sketch {
padding: 0 1.9rem;
}
&.product {
padding: 0 2.5rem;
}
}
}
}
}
}
> .content {
@@ -134,17 +256,41 @@
display: flex;
align-items: center;
justify-content: center;
.crop-wrapper {
width: 100%;
padding-top: 1.2rem;
.tips {
text-align: center;
color: #585858;
font-size: 1.4rem;
font-weight: 400;
}
}
&.is-product {
column-gap: 18.6rem;
.crop-wrapper {
width: initial;
}
}
> .image-clip {
flex: 1;
&.is-product {
width: initial;
flex: none;
}
}
> .preview {
margin-left: 6rem;
width: 28rem;
height: 100%;
display: flex;
flex-direction: column;
justify-content: center;
justify-content: flex-start;
align-items: center;
gap: 2.4rem;
min-height: 0;
> .title {
display: flex;
align-items: center;
@@ -155,10 +301,44 @@
font-size: 1.6rem;
}
}
> img {
> .preview-image {
width: 100%;
flex: 1;
min-height: 0;
display: flex;
align-items: center;
justify-content: center;
}
> .preview-image > img {
width: 100%;
height: auto;
margin-bottom: 3rem;
max-height: 100%;
}
> .submit {
margin-top: auto;
flex-shrink: 0;
}
&.is-product {
margin-left: 0;
> .preview-image > img {
width: 20.8rem;
height: 36.7rem;
box-shadow: 4px 4px 16px 0px #0000000f;
border: 1px solid #ededed;
}
&.is-cover {
> .preview-image > img {
width: 29.7rem;
height: 37.5rem;
}
}
&.is-apparel {
> .preview-image > img {
width: 100%;
height: auto;
max-height: 100%;
}
}
}
}
}

View File

@@ -1,16 +1,17 @@
<template>
<div class="image-clip">
<div class="image-clip-body" ref="imageClipBody">
<div class="image-clip" :class="{ 'is-product': isProduct }" :data-crop-type="type || ''">
<div class="image-clip-body" :class="{ 'is-cover': type === 'cover' }" ref="imageClipBody">
<VueCropper
ref="cropper"
:img="url"
crossOrigin="Anonymous"
:autoCrop="true"
:fixedNumber="ratio"
fixed
:fixed="isProduct ? type !== 'apparel' : true"
movable
centerBox
@realTime="onChange"
outputType="png"
:full="true"
></VueCropper>
</div>
<div class="clip_opterate">
@@ -33,7 +34,7 @@
</div>
</template>
<script setup>
import { ref, useAttrs, onMounted, onBeforeUnmount } from "vue"
import { ref, useAttrs, onMounted, onBeforeUnmount, computed, nextTick, watch } from "vue"
import "vue-cropper/dist/index.css"
import { VueCropper } from "vue-cropper"
const props = defineProps({
@@ -44,10 +45,29 @@
ratio: {
type: Array,
default: () => [1, 1]
},
isProduct: {
type: Boolean,
default: false
},
fixedBox: {
type: Boolean,
default: true
},
type: {
type: String,
default: () => ""
}
})
const attrs = useAttrs()
const autoCropHeight = computed(() => {
let height = 426
if (props.type === "cover") height = 375
else if (props.type === "apparel") height = 320
return height
})
const onChange = (data) => {
if (attrs.onChange) {
getCropUrl().then((url) => attrs.onChange(url))
@@ -55,14 +75,78 @@
}
const cropper = ref(null)
const imageClipBody = ref(null)
let injectLabelFrame = 0
const observer = new ResizeObserver((entries) => {
refreshCrop()
})
const clearCropLabels = (cropperBox) => {
if (!cropperBox) return
cropperBox.querySelectorAll(".cropper-line-label").forEach((node) => node.remove())
}
const createCropLabel = ({ text, top, className }) => {
const label = document.createElement("div")
label.className = `cropper-line-label ${className}`
label.textContent = text
label.style.top = top
label.style.left = className === "label-v" ? "50%" : "0"
label.style.transform =
className === "label-v" ? "translate(-50%, -50%)" : "translateY(-50%)"
return label
}
const centerLabelTop = "8px"
const cropLabelMap = {
cover: [
{ text: "crown", top: "2.67%", className: "label-h" },
{ text: "hip line", top: "63.47%", className: "label-h" },
{ text: "mid-thigh", top: "92.8%", className: "label-h" },
{ text: "center", top: centerLabelTop, className: "label-v" }
],
mainProductImage: [
{ text: "crown", top: "2.67%", className: "label-h" },
{ text: "footbase", top: "97.6%", className: "label-h" },
{ text: "center", top: centerLabelTop, className: "label-v" }
],
sketch: [
{ text: "crown", top: "2.67%", className: "label-h" },
{ text: "footbase", top: "97.6%", className: "label-h" },
{ text: "center", top: centerLabelTop, className: "label-v" }
],
apparel: [{ text: "center", top: centerLabelTop, className: "label-v" }]
}
const injectCropLabel = () => {
const cropperBox = imageClipBody.value?.querySelector(".cropper-view-box")
if (!cropperBox) return false
clearCropLabels(cropperBox)
;(cropLabelMap[props.type] || []).forEach((config) => {
cropperBox.appendChild(createCropLabel(config))
})
return true
}
const scheduleInjectCropLabel = (retry = 0) => {
cancelAnimationFrame(injectLabelFrame)
injectLabelFrame = requestAnimationFrame(() => {
if (!injectCropLabel() && retry < 10) {
scheduleInjectCropLabel(retry + 1)
}
})
}
onMounted(() => {
observer.observe(imageClipBody.value)
scheduleInjectCropLabel()
})
onBeforeUnmount(() => {
observer.disconnect()
cancelAnimationFrame(injectLabelFrame)
})
const rotateLeft = () => {
cropper.value.rotateLeft()
@@ -86,6 +170,16 @@
cropper.value.getCropBlob(resolve)
})
}
watch(
[() => props.type, () => props.url],
async () => {
await nextTick()
scheduleInjectCropLabel()
},
{ flush: "post" }
)
defineExpose({
getCropUrl,
getCropBlob
@@ -108,6 +202,28 @@
height: calc(40rem * 1.2);
// height: 53rem;
background: yellow;
:deep(.cropper-box) {
.cropper-box-canvas {
background-color: #ffffff;
// img {
// height: 100%;
// }
}
}
&.is-cover {
:deep(.vue-cropper) {
overflow: hidden;
}
// :deep(.cropper-box-canvas) {
// width: 31.1rem !important;
// left: 50% !important;
// transform: translateX(-50%) !important;
// img {
// display: none;
// }
// }
}
}
.clip_opterate {
@@ -153,5 +269,123 @@
}
}
}
&.is-product {
.image-clip-body {
width: 45.7rem;
height: 45.7rem;
:deep(.cropper-modal) {
background: transparent;
}
:deep(.vue-cropper .cropper-view-box) {
position: relative;
overflow: visible !important;
/* 原有的蓝色边框outline由组件控制这里不干涉 */
}
:deep(.vue-cropper .cropper-view-box::after) {
content: "";
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
box-sizing: border-box;
border: 1px solid rgba(75, 165, 255, 0.85);
pointer-events: none;
z-index: 9; /* 位于图片之上,但在控制点之下 */
background-image: none;
background-repeat: no-repeat;
}
:deep(.vue-cropper .crop-point) {
z-index: 10;
}
}
&[data-crop-type="cover"] {
.image-clip-body {
:deep(.vue-cropper .cropper-view-box::after) {
background-image:
linear-gradient(to right, #4ba5ff 50%, transparent 50%),
linear-gradient(to right, #4ba5ff 50%, transparent 50%),
linear-gradient(to right, #4ba5ff 50%, transparent 50%),
linear-gradient(to bottom, #4ba5ff 50%, transparent 50%);
background-repeat: repeat-x, repeat-x, repeat-x, repeat-y;
background-size:
8px 1px,
8px 1px,
8px 1px,
1px 8px;
background-position:
0 2.67%,
0 63.47%,
0 92.8%,
50% 0;
}
}
}
&[data-crop-type="mainProductImage"],
&[data-crop-type="sketch"] {
.image-clip-body {
:deep(.vue-cropper .cropper-view-box::after) {
background-image:
linear-gradient(to right, #4ba5ff 50%, transparent 50%),
linear-gradient(to right, #4ba5ff 50%, transparent 50%),
linear-gradient(to bottom, #4ba5ff 50%, transparent 50%);
background-repeat: repeat-x, repeat-x, repeat-y;
background-size:
8px 1px,
8px 1px,
1px 8px;
background-position:
0 2.67%,
0 97.6%,
50% 0;
}
}
}
&[data-crop-type="apparel"] {
.image-clip-body {
:deep(.vue-cropper .cropper-view-box::after) {
background-image: linear-gradient(to bottom, #4ba5ff 50%, transparent 50%);
background-repeat: repeat-y;
background-size: 1px 8px;
background-position: 50% 0;
}
}
}
}
}
</style>
<style lang="less">
.cropper-line-label {
position: absolute;
color: #4ba5ff; /* 统一颜色 */
font-size: 11px;
font-weight: bold;
background: rgba(255, 255, 255); /* 浅色背景确保在深色图片上可见 */
// padding: 0 4px;
border-radius: 2px;
white-space: nowrap;
pointer-events: none;
z-index: 10;
line-height: 1.2;
}
/* 水平线名称:放在线段上方 2px */
.label-h {
transform: translateY(-100%);
margin-top: -2px;
left: 4px;
}
/* 垂直线名称:放在裁剪框顶部边缘上方 */
.label-v {
transform: translateX(-50%);
top: -20px;
left: 50%;
}
</style>

View File

@@ -5,9 +5,9 @@
<img v-if="banner" :src="banner" />
<div v-else class="null">
<span class="icon"><svg-icon name="seller-picture" size="60" /></span>
<span class="tip">Your brand banner has not been set up yet.</span>
<span class="tip">{{ $t("Seller.brandProfileBgNullTip") }}</span>
</div>
<button @click="onChangeBanner">Change Brand Banner</button>
<button @click="onChangeBanner">{{ $t("Seller.changeBrandBanner") }}</button>
</div>
<!-- 头像 -->
<div class="avatar">
@@ -25,14 +25,14 @@
<div class="and-profile-footer">
<template v-if="isEdit">
<div class="btns">
<button class="cancel" @click="onCancel">Cancel</button>
<button class="submit" @click="onSubmit">Save Change</button>
<button class="cancel" @click="onCancel()">{{ $t("Seller.cancel") }}</button>
<button class="submit" @click="onSubmit()">{{ $t("Seller.saveChange") }}</button>
</div>
<p class="tip">Changes will be reflected on your Stylish Parade brand page.</p>
<p class="tip">{{ $t("Seller.brandProfileEditTip") }}</p>
</template>
<template v-else>
<div class="btns">
<button class="edit" @click="onEdit">Edit</button>
<button class="edit" @click="onEdit">{{ $t("Seller.edit") }}</button>
</div>
<p class="tip">&nbsp;</p>
</template>
@@ -41,11 +41,18 @@
</template>
<script setup>
import { ref } from "vue"
import { Https } from "@/tool/https"
import { ref, computed } from "vue"
import BrandInfo from "./brand-info.vue"
import ImageClipDialog from "./image-clip-dialog.vue"
const banner = ref("")
const avatar = ref("")
import { useStore } from "vuex"
import { useI18n } from "vue-i18n"
const { t } = useI18n()
const store = useStore()
store.dispatch("seller/get_designerInfo")
const designerInfo = computed(() => store.state.seller.designerInfo)
const avatar = computed(() => designerInfo.value.avatar)
const banner = computed(() => designerInfo.value.brandBanner)
const isEdit = ref(false)
const brandInfoRef = ref(null)
const imageClipDialogRef = ref(null)
@@ -65,14 +72,27 @@
input.click()
}
const uploadFile = async (file) => {
const formData = new FormData()
formData.append("file", file)
return Https.axiosPost(Https.httpUrls.sellerUploadFile, formData, {
headers: {
"Content-Type": "multipart/form-data"
}
})
}
const onChangeBanner = () => {
uploadImg(({ url }) => {
imageClipDialogRef.value.open(
url,
(file) => {
banner.value = URL.createObjectURL(file)
async (file) => {
store.commit("set_loading", true)
const res = await uploadFile(file)
onSubmit({ brandBanner: res })
store.commit("set_loading", false)
},
{ ratio: [40, 7], isPreview: false, title: "Crop Brand Banner" }
{ ratio: [40, 7], isPreview: false, title: t("Seller.cropBrandBanner") }
)
})
}
@@ -80,10 +100,13 @@
uploadImg(({ url }) => {
imageClipDialogRef.value.open(
url,
(file) => {
avatar.value = URL.createObjectURL(file)
async (file) => {
store.commit("set_loading", true)
const res = await uploadFile(file)
onSubmit({ avatar: res })
store.commit("set_loading", false)
},
{ ratio: [1, 1], isPreview: true, title: "Crop Avatar" }
{ ratio: [1, 1], isPreview: true, title: t("Seller.cropAvatar") }
)
})
}
@@ -93,13 +116,21 @@
const onCancel = () => {
isEdit.value = false
}
const onSubmit = () => {
brandInfoRef.value
.submit()
.then(() => {
isEdit.value = false
})
.catch(() => {})
const onSubmit = async (value = null) => {
const res = value ? value : await brandInfoRef.value.submit()
const data = {
...designerInfo.value,
...res
}
try {
data.socialLinks = JSON.stringify(data.socialLinks)
} catch (error) {
data.socialLinks = JSON.stringify([])
}
Https.axiosPut(Https.httpUrls.updateDesignerInfo, data).then((res) => {
isEdit.value = false
store.commit("seller/set_designerInfo", data)
})
}
</script>
<style scoped lang="less">
@@ -113,6 +144,7 @@
margin-bottom: 6rem;
> .bg {
position: relative;
min-height: 15rem;
> img {
width: 100%;
height: auto;

View File

@@ -0,0 +1,74 @@
<template>
<div class="status-wrapper flex flex-col flex-1">
<seller-header
class="edit-detail-header"
/>
<div class="status-container flex flex-col flex-1 flex-center">
<img src="@/assets/images/seller/success-0.png" class="icon" alt="" />
<div class="title">{{ $t(title) }}</div>
<div class="desc">
{{ $t(desc) }}
</div>
<div class="btn" @click="handleBack">Back to My Listings</div>
</div>
</div>
</template>
<script setup lang="ts">
import { ref, computed } from "vue"
import { useRoute, useRouter } from "vue-router"
import SellerHeader from "../../seller-header.vue"
const ROUTE = useRoute()
const ROUTER = useRouter()
const title = computed(() => {
if (ROUTE.params.status === "publish") return "SellerListEdit.listingLive"
if (ROUTE.params.status === "draft") return "SellerListEdit.draftSaved"
})
const desc = computed(() => {
if (ROUTE.params.status === "publish") return "SellerListEdit.publishDesc"
if (ROUTE.params.status === "draft") return "SellerListEdit.draftDesc"
})
const handleBack = () => {
ROUTER.push({ name: "myListingsIndex" })
}
</script>
<style lang="less" scoped>
.status-wrapper {
.status-container {
row-gap: 2.4rem;
font-weight: 400;
.title {
font-size: 2.2rem;
}
.icon {
width: 8.33rem;
height: 8.33rem;
}
.desc {
width: 58.2rem;
text-align: center;
white-space: pre-line;
color: #585858;
font-size: 1.8rem;
}
.btn {
margin-top: 3.6rem;
height: 6rem;
width: 30rem;
border-radius: 4rem;
text-align: center;
line-height: 6rem;
padding: 0 2rem;
font-size: 1.6rem;
column-gap: 0.8rem;
cursor: pointer;
background-color: #000;
color: #fff;
}
}
}
</style>

View File

@@ -0,0 +1,115 @@
# EditDetail Page Agent Notes
This directory owns the seller listing edit/create detail page.
## Files
- `index.vue`: route-level container. Keep orchestration here: history-state entry mode, API calls, page switching, validation, save/publish navigation, image crop dialog wiring.
- `api.ts`: API wrappers for listing detail, sketch detail, listing batch save, status update, and file upload.
- `types.ts`: shared page-local TypeScript types. Add new cross-component page types here instead of duplicating interfaces in child components.
- `components/TopImageSection.vue`: presentational block for `sketch`, `mainProductImage`, and `cover`.
- `components/ProductImageList.vue`: presentational product-image selector and selected/main badge display.
- `components/ApparelSketchList.vue`: presentational apparel sketch list and crop trigger.
- `components/ListingForm.vue`: presentational listing form. Emits field updates; does not call APIs.
- `components/Radio.vue`: local radio/multi-select button component.
- `Status.vue`: save/publish result status page.
## Component Boundaries
- Keep `index.vue` as the single source of truth for `selectList`, `currentPage`, `currentListing`, per-listing `firstSelectedIndex`, and crop/save behavior.
- Child components should receive props and emit events only. Do not import listing APIs or mutate parent state directly from children.
- If a new visual section is added to this page, prefer a new child component under `components/` plus shared types in `types.ts`.
## Vue SFC Order
- Vue single-file components must keep sections in this order: `<template>` first, then `<script setup lang="ts">`, then `<style>`.
- When creating or refactoring `.vue` files in this page, preserve that order even if external Vue guidance suggests another layout.
## Image Category Mapping
Detail API images are mapped by `category`:
- `cover` -> `currentListing.cover`
- `cover_from` -> `currentListing.coverFrom`; `imageUrl` stores the source image URL, not the literal source key. Resolve it against `sketch` and `mainProductImage` when hydrating. Keep backward compatibility for old rows whose `imageUrl` is `sketch` or `mainProductImage`.
- `sketch` -> `currentListing.sketch`
- `mainProductImage` or `main_product` -> `currentListing.mainProductImage`
- `product` -> non-video entries in `currentListing.prodImageList`
- `firstFrame`, `gif`, and `video` -> one video entry in `currentListing.prodImageList` when the three rows share the same `sortOrder`
- `apparel` -> `currentListing.sketchList`
When saving, preserve the backend's expected image categories. Confirm backend naming before changing `main_product`, `product`, `firstFrame`, `gif`, `video`, or `mainProductImage`.
中文补充:
- 详情接口返回的 `images` 要按 `category` 回填到页面状态,不要只按数组顺序猜类型。
- `cover_from` 不是封面图本身,而是记录封面裁剪来源。它的 `imageUrl` 传来源图片链接,回显时用这个 URL 和 `sketch``mainProductImage` 比较,恢复裁剪弹窗里的来源选择。
- `main_product` 表示页面右上方的主产品图 URL普通 `product` 只表示产品图列表里的非视频图片。
- 视频不要保存成 `product`。视频必须拆成 `firstFrame``gif``video` 三类,并在回显时按相同 `sortOrder` 合并成一个视频项。
## Product Image Rules
- The `main` badge represents the first selected product image, not the most recently selected one.
- `firstSelectedIndex` is stored on each `ListingItem` and passed to `ProductImageList.vue`.
- Hydrating detail data must only set `mainProductImage` from explicit `main_product` or `mainProductImage` rows. Never infer it from selected `product` rows.
- Selecting a product image should only set `mainProductImage` when no main image is currently tracked by that listing's `firstSelectedIndex`.
- Unselecting the current main product image clears `mainProductImage` and resets `firstSelectedIndex`.
- Videos can be selected and saved, but they cannot become `mainProductImage`, must not set `firstSelectedIndex`, and must not display the `main` badge.
中文补充:
- `main` 标识只给图片,不给视频。
- 数据回显时,只有接口明确返回 `main_product` / `mainProductImage` 才能设置主图。不要因为某个 `product` 是已选中状态,就自动把它当成 `mainProductImage` 或显示 `main`
- 第一次选择视频时可以弹 warning但视频本身仍然要保持选中只是不要把它写入 `mainProductImage`,也不要更新 `firstSelectedIndex`
- 如果先选中视频,再选中图片,图片仍然可以成为第一个主图。
- 如果取消的是当前主图图片,需要清空 `mainProductImage``firstSelectedIndex`;取消普通图片或视频不应影响主图。
## Save Image Ordering
- Every saved image row must include `sortOrder`.
- `sortOrder` is scoped per category; each category starts its own sequence.
- For `product` rows, save the image currently used as `mainProductImage` first, selected non-main images next, and unselected images last.
- Save video media as three rows with categories `firstFrame`, `gif`, and `video`. The three rows from the same video item must share the same `sortOrder`.
- When hydrating detail data, group `firstFrame`, `gif`, and `video` rows by matching `sortOrder` and restore the combined video item, including its selected state. Accept `isSelected`, old typo `isSeleted`, and `selected` from the API.
中文补充:
- `sortOrder` 是按 category 分开排的,不同 category 之间不要共用一个全局序号。
- `product` 的排序规则是:当前作为 `mainProductImage` 的图片第一,其他已选图片其次,未选图片最后。
- 同一个视频拆出的 `firstFrame``gif``video` 三条数据必须使用同一个 `sortOrder`。例如第一个视频三条都是 `sortOrder: 1`,第二个视频三条都是 `sortOrder: 2`
- 回显视频时,用相同 `sortOrder` 找回一组 `firstFrame/gif/video`。三条里任意一条带选中标记,都应恢复为这个视频已选中。
- 选中字段要兼容 `isSelected`、历史拼写 `isSeleted`、以及 `selected`
## Crop Flow
- `TopImageSection.vue` and `ApparelSketchList.vue` emit `crop`.
- `index.vue` handles `handleClickCrop`, opens `ImageClipDialog`, uploads with `uploadFile`, then writes the returned URL into the correct field/list item.
- Keep cover crop ratio at `[4, 5]`; other crop types use `[9, 16]`.
- Cover crop can be based on `sketch` or `mainProductImage`. Store the chosen source in `coverFrom`, save it via `cover_from.imageUrl` as the source image URL, and pass it back into `ImageClipDialog` so reopening cover crop restores the selected source.
中文补充:
- cover 裁剪弹窗会在 `sketch``mainProductImage` 之间切来源。用户保存 cover 时,父组件需要同时保存裁剪后的 cover URL 和本次使用的来源。
- 下次重新打开 cover 裁剪时,应该按 `coverFrom` 恢复来源选择,并用对应的原图作为裁剪图,不要直接拿已裁好的 cover 图再次裁。
- 如果只有一个来源图可用,就按可用来源打开;如果两个来源都可用,要显示来源切换。
## Form Flow
- `ListingForm.vue` accepts scalar form props and emits `update:*` events.
- `index.vue` writes those events back into `currentListing`.
- Category options are derived from current gender and Vuex `UserHabit` state.
## Validation And Navigation
- `validatePublishRequired()` validates each listing before publish.
- Draft currently requires `cover` before save.
- After save/publish, routing goes to `Status` with route param `status`.
## Verification
- Run `npm run build` after behavior or type changes.
- Build can fail before code bundling completes if the existing `feedbackSurvey.vue` Google Fonts import cannot be fetched. If the failure is only `fonts.googleapis.com` socket/DNS related, retry when network is available.
- Project ESLint currently fails before linting files because `.eslintrc.js` contains invalid env key `se6`; do not treat that as a page-specific regression.
## Known Caution
- This page has active local edits. Before broad refactors, inspect both staged and unstaged diffs.

View File

@@ -0,0 +1,62 @@
import { Https } from "@/tool/https"
import type { ListingImageCategory, SketchDetailResponse } from "./types"
// 编辑时根据ID获取信息
export const fetchListingDetailById = (id) => {
return Https.axiosGet("/seller/listing/detail", { params: { id } })
}
type SketchIDs = Array<number | string>
// 获取designItemId对应的产品图
export const fetchSketchDetail = (data: SketchIDs): Promise<SketchDetailResponse[]> => {
let params = "?"
data.forEach((id, index) => {
if (index === data.length - 1) {
params += `designItemIds=${id}`
} else {
params += `designItemIds=${id}&`
}
})
return Https.axiosGet(`/aida/api/seller/sketchDetail${params}`)
}
interface ImageObj {
id?: number // 图片id,有值会更新,没有会自动新增
category: ListingImageCategory // 图片类型
imageUrl?: string | null
isSelected?: number
sortOrder?: number
}
interface DetailData {
id: number | string // 商品Id
title: string // 商品名
description: string // 商品描述
price: number | string // 价格
stock?: number // 库存
viewCount?: number // 浏览量
status: 0 | 1 | 2 // 0草稿 1发布 2删除
images: ImageObj[]
designFor: "male" | "female"
productCategory: string[] | null
}
// 保存/更新表单
export const fetchUpdateListing = (data: DetailData[]) => {
return Https.axiosPost("/seller/listing/batch", data)
}
interface StatusData {
id: number | string
status: 0 | 1 | 2 // 0草稿 1发布 2删除
}
// 设置商品状态
export const fetchChangeStatus = (data: StatusData) => {
return Https.axiosPut("/seller/listing/status", data)
}
export const uploadFile = (file) => {
const formData = new FormData()
formData.append("file", file)
return Https.axiosPost("/seller/file/upload", formData, {
headers: { "Content-Type": "multipart/form-data", Accept: "*/*" }
})
}

View File

@@ -0,0 +1,90 @@
<template>
<div class="apparel-container">
<div class="title">
<span class="main-title">{{ $t("SellerListEdit.apparelSketchTitle") }}</span>
<span class="sub-title">{{ $t("SellerListEdit.apparelSketchSubTitle") }}</span>
</div>
<div class="sketch-list-container flex">
<div
v-for="(item, index) in sketchList"
:key="index"
class="sketch-element flex flex-center"
>
<img class="img-src" :src="item.url || ''" alt="" />
<div
class="crop-tool flex flex-center"
@click="emit('crop', item.url, 'apparel', index)"
>
<SvgIcon name="CCrop" color="#fff" size="12" />
</div>
</div>
</div>
</div>
</template>
<script setup lang="ts">
import type { ListingItem } from "../types"
defineProps<{
sketchList: ListingItem["sketchList"]
}>()
const emit = defineEmits<{
(e: "crop", data: string | null, type: "apparel", index?: number): void
}>()
</script>
<style lang="less" scoped>
.apparel-container {
margin-top: 3rem;
.title {
font-size: 1.4rem;
margin-bottom: 0.8rem;
.main-title {
font-weight: 400;
font-style: bold;
&::after {
content: "*";
color: #df2b2c;
}
}
.sub-title {
font-size: 1.2rem;
color: #999;
}
}
.sketch-list-container {
column-gap: 1rem;
flex-wrap: wrap;
.sketch-element {
width: 10rem;
height: 14.6rem;
border: 0.15rem solid #c7c7c7;
border-radius: 1.2rem;
position: relative;
overflow: hidden;
.img-src {
width: 100%;
height: 100%;
object-fit: contain;
}
.crop-tool {
position: absolute;
top: 0.4rem;
right: 0.4rem;
width: 2rem;
height: 2rem;
border-radius: 50%;
background-color: #000000;
}
}
}
}
</style>

View File

@@ -0,0 +1,222 @@
<template>
<div class="form-container flex flex-col">
<div class="form-item">
<div class="form-item-label required">
{{ $t("SellerListEdit.productName") }}
</div>
<div class="form-item-value product-name">
<a-input
:value="productName"
show-count
placeholder="Enter product name"
:bordered="false"
:maxlength="60"
@update:value="emit('update:productName', $event)"
/>
</div>
</div>
<div class="form-item">
<div class="form-item-label required">{{ $t("SellerListEdit.price") }}</div>
<div class="form-item-value price flex align-center">
<span>HK$</span>
<a-input
:value="price"
placeholder="0.00"
:bordered="false"
@update:value="emit('update:price', $event)"
/>
</div>
</div>
<div class="form-item">
<div class="form-item-label required">
{{ $t("SellerListEdit.productDescription") }}
</div>
<div class="form-item-value desc">
<a-textarea
:value="desc"
show-count
:rows="4"
placeholder="Enter product description"
:bordered="false"
:maxlength="500"
@update:value="emit('update:desc', $event)"
/>
</div>
</div>
<div class="form-item">
<div class="form-item-label required">
{{ $t("SellerListEdit.designFor") }}
</div>
<div class="form-item-value no-border">
<Radio
:options="genderOptions"
:model-value="gender"
@update:model-value="emit('update:gender', $event)"
/>
</div>
</div>
<div class="form-item">
<div class="form-item-label with-tip">
<span class="required">{{ $t("SellerListEdit.productCategory") }}</span>
<span class="help-text">{{ $t("SellerListEdit.categoryTips") }}</span>
</div>
<div class="form-item-value no-border">
<Radio
multiple
:options="categoryOptions"
:model-value="category"
@update:model-value="emit('update:category', $event)"
/>
</div>
</div>
<div class="license-note flex align-center">
<img src="@/assets/images/seller/tips.png" class="info-icon" />
<div class="note-copy">
{{ $t("SellerListEdit.policy") }}
<a href="javascript:void(0)">{{ $t("SellerListEdit.learnMore") }}</a>
</div>
</div>
</div>
</template>
<script setup lang="ts">
import Radio from "./Radio.vue"
import type { RadioOption } from "../types"
defineProps<{
productName: string
price: string
desc: string
gender: string
category: string[] | null
genderOptions: RadioOption[]
categoryOptions: RadioOption[]
}>()
const emit = defineEmits<{
(e: "update:productName", value: string): void
(e: "update:price", value: string): void
(e: "update:desc", value: string): void
(e: "update:gender", value: string): void
(e: "update:category", value: string[] | null): void
}>()
</script>
<style lang="less" scoped>
.required {
&::after {
content: "*";
color: #df2b2c;
margin-left: 0.4rem;
}
}
.form-container {
row-gap: 3rem;
.form-item {
.form-item-label {
font-size: 1.4rem;
font-weight: 400;
font-style: bold;
margin-bottom: 0.6rem;
line-height: 1.5;
&.with-tip {
display: flex;
align-items: center;
column-gap: 0.8rem;
}
.help-text {
font-size: 1rem;
color: #999;
}
}
.form-item-value {
border: 0.16rem solid #d1d1d1;
border-radius: 1.2rem;
position: relative;
padding: 1.6rem;
box-sizing: border-box;
font-size: 1.2rem;
color: #000;
&.no-border {
border: none;
padding: 0;
}
&.price {
column-gap: 0.6rem;
}
:deep(.ant-input),
:deep(.ant-input-affix-wrapper),
:deep(.ant-input-textarea textarea) {
border: none;
padding: 0;
font-size: 1.2rem;
color: #000;
box-shadow: none;
}
:deep(.ant-input) {
line-height: 1.5;
}
:deep(.ant-input-show-count-suffix) {
color: #df2c2c;
font-size: 1rem;
}
:deep(textarea.ant-input) {
resize: none;
min-height: 5.4rem;
padding-bottom: 1.8rem;
line-height: 1.5;
}
:deep(.ant-input-textarea-show-count) {
position: relative;
&::after {
float: none;
position: absolute;
color: #df2c2c;
bottom: 0;
right: 1.6rem;
}
}
}
}
}
.license-note {
padding: 1.6rem;
column-gap: 1.6rem;
background: #f7f7f7;
border-radius: 0.8rem;
.info-icon {
width: 2.4rem;
height: 2.4rem;
flex-shrink: 0;
}
.note-copy {
font-size: 1.2rem;
line-height: 1.5;
color: #000;
font-weight: 400;
font-style: medium;
a {
color: #0080ed;
text-decoration: underline;
margin-left: 0.4rem;
}
}
}
</style>

View File

@@ -0,0 +1,238 @@
<template>
<div class="product-image-list-container">
<div class="title flex align-center space-between">
<div class="title-left">
<span class="main-title">{{ $t("SellerListEdit.productImageMainTitle") }}</span>
<span class="sub-title">{{ $t("SellerListEdit.productImageSubTitle") }}</span>
</div>
<div class="title-right">{{ selectedCount }}/{{ imageList.length }} selected</div>
</div>
<div class="product-image-list flex">
<div
v-for="(item, index) in imageList"
:key="index"
class="product-image-item flex flex-center"
:class="{ selected: item.selected, video: item.isVideo }"
@click="emit('select', index)"
@mouseenter="handleMouseEnter(index, item)"
@mouseleave="handleMouseLeave(index, item)"
>
<img
v-if="item.selected"
src="@/assets/images/seller/checked.png"
class="checked"
alt=""
/>
<img class="img-src" :src="getDisplayUrl(item, index)" alt="" />
<div v-if="item.isVideo && durationMap[index]" class="video-duration">
{{ durationMap[index] }}
</div>
<div v-if="item.selected && index === firstSelectedIndex && !item.isVideo" class="main-pic">
main
</div>
</div>
</div>
</div>
</template>
<script setup lang="ts">
import { computed, ref, watch } from "vue"
import type { ListingItem, ProductMediaItem } from "../types"
const props = defineProps<{
imageList: ListingItem["prodImageList"]
firstSelectedIndex: number | null
}>()
const emit = defineEmits<{
(e: "select", index: number): void
}>()
const selectedCount = computed(() => props.imageList.filter((item) => item.selected).length)
const hoveredVideoIndex = ref<number | null>(null)
const durationMap = ref<Record<number, string>>({})
const videoSourceKey = computed(() =>
props.imageList
.map((item) => `${item.videoUrl || ""}::${item.url || ""}`)
.join("|")
)
const getDisplayUrl = (item: ProductMediaItem, index: number) => {
if (item.isVideo && hoveredVideoIndex.value === index && item.gifUrl) {
return item.gifUrl
}
return item.url
}
const handleMouseEnter = (index: number, item: ProductMediaItem) => {
if (!item.isVideo || !item.gifUrl) return
hoveredVideoIndex.value = index
}
const handleMouseLeave = (index: number, item: ProductMediaItem) => {
if (!item.isVideo) return
if (hoveredVideoIndex.value === index) hoveredVideoIndex.value = null
}
const formatVideoDuration = (duration: number) => {
if (!Number.isFinite(duration) || duration <= 0) return ""
const totalSeconds = Math.round(duration)
const minutes = Math.floor(totalSeconds / 60)
const seconds = totalSeconds % 60
return `${String(minutes).padStart(2, "0")}:${String(seconds).padStart(2, "0")}`
}
const loadVideoDurations = () => {
durationMap.value = {}
props.imageList.forEach((item, index) => {
if (!item.isVideo || !item.videoUrl) return
const video = document.createElement("video")
video.preload = "metadata"
video.src = item.videoUrl
video.onloadedmetadata = () => {
durationMap.value = {
...durationMap.value,
[index]: formatVideoDuration(video.duration)
}
}
video.onerror = () => {
video.src = ""
}
})
}
watch(
videoSourceKey,
() => {
hoveredVideoIndex.value = null
loadVideoDurations()
},
{ immediate: true }
)
</script>
<style lang="less" scoped>
.product-image-list-container {
margin-top: 3rem;
.title {
font-size: 1.4rem;
margin-bottom: 1.2rem;
.main-title {
font-weight: 400;
font-style: bold;
}
.sub-title {
font-size: 1.2rem;
color: #999;
}
.title-right {
color: #585858;
font-size: 1.4rem;
}
}
.product-image-list {
overflow-x: auto;
overflow-y: hidden;
column-gap: 0.8rem;
max-width: 80.2rem;
padding-bottom: 1.2rem;
&::-webkit-scrollbar {
height: 0.8rem;
}
&::-webkit-scrollbar-track {
background: #d9d9d9;
border-radius: 0.8rem;
}
&::-webkit-scrollbar-thumb {
background: #000000;
border-radius: 0.8rem;
}
.product-image-item {
width: 11.6rem;
height: 20.6rem;
border-radius: 1rem;
border: 0.15rem solid #c7c7c7;
position: relative;
cursor: pointer;
overflow: hidden;
flex-shrink: 0;
&::after {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
content: "";
background-color: #fcfcfc;
opacity: 0.7;
border-radius: 1rem;
}
&.selected {
border-color: #000;
&::after {
display: none;
}
}
.checked {
width: 2rem;
height: 2rem;
position: absolute;
top: 0.8rem;
right: 0.8rem;
z-index: 1;
}
.img-src {
width: 100%;
height: 100%;
object-fit: contain;
}
.video-duration {
position: absolute;
right: 0.8rem;
bottom: 0.8rem;
z-index: 1;
padding: 0 0.8rem;
height: 2.4rem;
line-height: 2.4rem;
border-radius: 1.2rem;
background: rgba(0, 0, 0, 0.8);
color: #fff;
font-size: 1.2rem;
}
.main-pic {
position: absolute;
height: 2.4rem;
line-height: 2.4rem;
left: 0.8rem;
right: 0.8rem;
bottom: 0.8rem;
z-index: 1;
background: rgba(0, 0, 0, 0.8);
color: #fff;
font-size: 1.4rem;
border-radius: 1.2rem;
text-align: center;
}
}
}
}
</style>

View File

@@ -0,0 +1,123 @@
<template>
<div class="radio-button-group">
<button
v-for="item in options"
:key="item.key"
type="button"
:class="[
'radio-button',
{
'is-active': multiple
? selectedValues.includes(normalizeValue(item.key))
: modelValue === item.key
}
]"
@click="selectOption(item.key)"
>
{{ item.name }}
</button>
</div>
</template>
<script setup lang="ts">
import { computed } from "vue"
interface Option {
name: string | number
value: string | number | boolean
key: string
optype: boolean
}
const props = defineProps<{
modelValue:
| string
| number
| boolean
| Array<string | number | boolean | null | undefined>
| null
options: Option[] // 按钮选项数组
multiple?: boolean // 是否支持多选,默认为 false
}>()
const emit = defineEmits<{
(e: "update:modelValue", value: any): void
}>()
const multiple = props.multiple === true
const normalizeValue = (value: any) =>
typeof value === "string" ? value.toLocaleLowerCase() : value
const selectedValues = computed(() => {
if (!multiple) {
return typeof props.modelValue === "undefined" || props.modelValue === null
? []
: [props.modelValue]
}
return Array.isArray(props.modelValue)
? props.modelValue
.filter((value) => value !== null && typeof value !== "undefined")
.map(normalizeValue)
: []
})
const selectOption = (value: any) => {
if (multiple) {
const selectedValue = normalizeValue(value)
const current = Array.isArray(props.modelValue)
? props.modelValue
.filter((item) => item !== null && typeof item !== "undefined")
.map(normalizeValue)
: []
const index = current.indexOf(selectedValue)
if (index >= 0) {
current.splice(index, 1)
} else {
current.push(selectedValue)
}
emit("update:modelValue", current.length ? current : null)
return
}
if (props.modelValue !== value) {
emit("update:modelValue", value)
}
}
</script>
<style lang="less" scoped>
.radio-button-group {
display: flex;
gap: 8px;
flex-wrap: wrap;
}
.radio-button {
border: 1px solid #d9d9d9;
height: 3.2rem;
min-width: 8rem;
padding: 0 1.7rem;
color: #000;
cursor: pointer;
border-radius: 2rem;
outline: none;
background-color: #fff;
font-size: 1.2rem;
transition: all 0.2s ease;
}
.radio-button:hover {
border-color: #000;
}
.radio-button.is-active {
color: #fff;
background-color: #000;
border-color: #000;
font-family: pingfang_medium;
}
</style>

View File

@@ -0,0 +1,156 @@
<template>
<div class="main-image-container flex">
<div
v-for="type in topImageList"
:key="type"
:class="`main-image-item flex flex-col align-center ${type}`"
>
<div class="title" :class="{ required: type !== 'mainProductImage' }">
{{ $t(topImageTitleMap[type]) }}
</div>
<div class="sketch-item flex flex-center" :class="type">
<div
v-if="images[type]"
class="crop-tool flex flex-center"
@click="emit('crop', images[type], type)"
>
<SvgIcon name="CCrop" color="#fff" size="12" />
</div>
<img
v-if="images[type]"
:src="images[type] || ''"
class="sketch-img"
:class="type"
alt=""
/>
<div v-else class="trigger flex flex-col align-center">
<div
v-if="type === 'cover'"
class="cover-trigger flex flex-col align-center"
@click="emit('crop', null, 'cover')"
>
<SvgIcon class="trigger-icon" name="CCrop" color="#585858" size="24" />
<div class="trigger-tips">
{{ $t("SellerListEdit.cropDesc") }}
</div>
</div>
<template v-else>
<div class="trigger-img placeholder"></div>
<div class="trigger-tips">
{{ $t("SellerListEdit.productImageDesc") }}
</div>
</template>
</div>
</div>
</div>
</div>
</template>
<script setup lang="ts">
import type { TopImageType } from "../types"
defineProps<{
images: Record<TopImageType, string | null>
}>()
const emit = defineEmits<{
(e: "crop", data: string | null, type: TopImageType): void
}>()
const topImageList: TopImageType[] = ["sketch", "mainProductImage", "cover"]
const topImageTitleMap: Record<TopImageType, string> = {
sketch: "SellerListEdit.sketch",
mainProductImage: "SellerListEdit.mainProductImage",
cover: "SellerListEdit.cover"
}
</script>
<style lang="less" scoped>
.c-svg {
width: initial;
height: initial;
}
.required {
&::after {
content: "*";
color: #df2b2c;
margin-left: 0.4rem;
}
}
.main-image-container {
column-gap: 3.5rem;
.main-image-item {
flex-shrink: 0;
.title {
font-size: 1.4rem;
margin-bottom: 0.8rem;
text-align: center;
}
.sketch-item {
width: 11.6rem;
height: 20.4rem;
border: 0.15rem solid #d1d1d1;
border-radius: 1rem;
position: relative;
background-color: #f6f6f6;
overflow: hidden;
&.cover {
width: 16.2rem;
background-image: url("data:image/svg+xml,%3csvg width='100%25' height='100%25' xmlns='http://www.w3.org/2000/svg'%3e%3crect width='100%25' height='100%25' rx='11' ry='11' fill='none' stroke='%23D1D1D1' stroke-width='1.5' stroke-dasharray='8%2c 5' stroke-linecap='square'/%3e%3c/svg%3e");
border: none;
}
.crop-tool {
position: absolute;
top: 0.8rem;
right: 0.8rem;
width: 2rem;
height: 2rem;
border-radius: 50%;
background-color: #000000;
z-index: 1;
cursor: pointer;
}
.sketch-img {
height: 100%;
object-fit: contain;
height: 100%;
&.sketch {
width: 100%;
}
}
.trigger {
cursor: pointer;
height: 100%;
padding: 6rem 2rem 0;
&,
.cover-trigger {
row-gap: 1.2rem;
}
.placeholder {
width: 2.4rem;
height: 2.4rem;
border-radius: 0.6rem;
background: linear-gradient(135deg, #efefef 0%, #cdcdcd 100%);
}
.trigger-tips {
font-size: 1.2rem;
text-align: center;
color: #585858;
line-height: 1.3;
}
}
}
}
}
</style>

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,82 @@
export type RadioOption = {
name: string | number
value: string | number | boolean
key: string
optype: boolean
}
export type TopImageType = "sketch" | "mainProductImage" | "cover"
export type CropType = TopImageType | "apparel"
export type CoverSourceType = "sketch" | "mainProductImage"
export type ListingImageCategory =
| "cover"
| "cover_from"
| "main_product"
| "mainProductImage"
| "product"
| "sketch"
| "apparel"
| "firstFrame"
| "gif"
| "video"
export type ProductMediaItem = {
url: string
selected?: boolean
isVideo?: boolean
videoUrl?: string
gifUrl?: string
firstFrameUrl?: string
}
export type ListingItem = {
designItemId: number | string | null
sketch: string | null
mainProductImage: string
cover: string
productImage: string[]
apparelSketch: string[]
productName: string
price: string
desc: string
gender: string
category: string[] | null
coverFrom: CoverSourceType
firstSelectedIndex: number | null
prodImageList: ProductMediaItem[]
sketchList: Array<{ url: string | null }>
}
export type ListingDetailImage = {
category?: ListingImageCategory | string | null
imageUrl?: string | null
isSelected?: boolean | number | string | null
isSeleted?: boolean | number | string | null
selected?: boolean | number | string | null
sortOrder?: number | null
}
export type ListingDetailResponse = {
id?: number | string | null
title?: string | null
description?: string | null
price?: number | string | null
designFor?: string | null
productCategory?: string | string[] | null
images?: ListingDetailImage[] | null
}
export type SketchDetailVideo = {
firstFrameUrl?: string | null
gifUrl?: string | null
videoUrl?: string | null
}
export type SketchDetailResponse = {
clothes?: string[] | null
designItemId?: number | string | null
toProductImageUrls?: string[] | null
videos?: SketchDetailVideo[] | null
}
export type StatusType = "draft" | "publish"

View File

@@ -1,116 +1,84 @@
<script setup lang="ts">
import { ref, onMounted, onUnmounted, reactive, toRefs } from "vue";
import { Https } from '@/tool/https'
import { setPubDate } from "@/tool/util";
import { useI18n } from "vue-i18n";
import { useStore } from "vuex";
//const props = defineProps({
//})
const props = defineProps({
getCollectionListData: {
type: Object,
default: () => ({})
}
})
const emit = defineEmits([
'selectCollectionItem',
])
const current = ref(1)
const {t} = useI18n()
const store = useStore()
const page = ref(1)
const size = ref(6)
const total = ref(0)
const list = ref([
{
imgList:[
{
url:'https://www.minio-api.aida.com.hk/aida-users/83/avatar/2b3d5756-ea29-4020-86a9-3b02cfc73b5a.png?response-content-type=image%2Fpng&response-content-disposition=inline&X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=admin%2F20260410%2Fus-east-1%2Fs3%2Faws4_request&X-Amz-Date=20260410T013002Z&X-Amz-Expires=86400&X-Amz-SignedHeaders=host&X-Amz-Signature=eedfd4114bde66f0ff8fe5e4d7a7274d9007b25deb40ee82a19427f29601e89b',
},{
url:'https://www.minio-api.aida.com.hk/aida-users/83/avatar/2b3d5756-ea29-4020-86a9-3b02cfc73b5a.png?response-content-type=image%2Fpng&response-content-disposition=inline&X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=admin%2F20260410%2Fus-east-1%2Fs3%2Faws4_request&X-Amz-Date=20260410T013002Z&X-Amz-Expires=86400&X-Amz-SignedHeaders=host&X-Amz-Signature=eedfd4114bde66f0ff8fe5e4d7a7274d9007b25deb40ee82a19427f29601e89b',
},{
url:'https://www.minio-api.aida.com.hk/aida-users/83/avatar/2b3d5756-ea29-4020-86a9-3b02cfc73b5a.png?response-content-type=image%2Fpng&response-content-disposition=inline&X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=admin%2F20260410%2Fus-east-1%2Fs3%2Faws4_request&X-Amz-Date=20260410T013002Z&X-Amz-Expires=86400&X-Amz-SignedHeaders=host&X-Amz-Signature=eedfd4114bde66f0ff8fe5e4d7a7274d9007b25deb40ee82a19427f29601e89b',
},{
url:'https://www.minio-api.aida.com.hk/aida-users/83/avatar/2b3d5756-ea29-4020-86a9-3b02cfc73b5a.png?response-content-type=image%2Fpng&response-content-disposition=inline&X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=admin%2F20260410%2Fus-east-1%2Fs3%2Faws4_request&X-Amz-Date=20260410T013002Z&X-Amz-Expires=86400&X-Amz-SignedHeaders=host&X-Amz-Signature=eedfd4114bde66f0ff8fe5e4d7a7274d9007b25deb40ee82a19427f29601e89b',
},
],
type:'Series',
name:'Christmas',
sketchNum: 7,
date: 'today',
id:'1',
},{
imgList:[
{
url:'https://www.minio-api.aida.com.hk/aida-users/83/avatar/2b3d5756-ea29-4020-86a9-3b02cfc73b5a.png?response-content-type=image%2Fpng&response-content-disposition=inline&X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=admin%2F20260410%2Fus-east-1%2Fs3%2Faws4_request&X-Amz-Date=20260410T013002Z&X-Amz-Expires=86400&X-Amz-SignedHeaders=host&X-Amz-Signature=eedfd4114bde66f0ff8fe5e4d7a7274d9007b25deb40ee82a19427f29601e89b',
},{
url:'https://www.minio-api.aida.com.hk/aida-users/83/avatar/2b3d5756-ea29-4020-86a9-3b02cfc73b5a.png?response-content-type=image%2Fpng&response-content-disposition=inline&X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=admin%2F20260410%2Fus-east-1%2Fs3%2Faws4_request&X-Amz-Date=20260410T013002Z&X-Amz-Expires=86400&X-Amz-SignedHeaders=host&X-Amz-Signature=eedfd4114bde66f0ff8fe5e4d7a7274d9007b25deb40ee82a19427f29601e89b',
},{
url:'https://www.minio-api.aida.com.hk/aida-users/83/avatar/2b3d5756-ea29-4020-86a9-3b02cfc73b5a.png?response-content-type=image%2Fpng&response-content-disposition=inline&X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=admin%2F20260410%2Fus-east-1%2Fs3%2Faws4_request&X-Amz-Date=20260410T013002Z&X-Amz-Expires=86400&X-Amz-SignedHeaders=host&X-Amz-Signature=eedfd4114bde66f0ff8fe5e4d7a7274d9007b25deb40ee82a19427f29601e89b',
},{
url:'https://www.minio-api.aida.com.hk/aida-users/83/avatar/2b3d5756-ea29-4020-86a9-3b02cfc73b5a.png?response-content-type=image%2Fpng&response-content-disposition=inline&X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=admin%2F20260410%2Fus-east-1%2Fs3%2Faws4_request&X-Amz-Date=20260410T013002Z&X-Amz-Expires=86400&X-Amz-SignedHeaders=host&X-Amz-Signature=eedfd4114bde66f0ff8fe5e4d7a7274d9007b25deb40ee82a19427f29601e89b',
},
],
type:'Series',
name:'Christmas',
sketchNum: 7,
date: 'today',
id:'2',
},{
imgList:[
{
url:'https://www.minio-api.aida.com.hk/aida-users/83/avatar/2b3d5756-ea29-4020-86a9-3b02cfc73b5a.png?response-content-type=image%2Fpng&response-content-disposition=inline&X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=admin%2F20260410%2Fus-east-1%2Fs3%2Faws4_request&X-Amz-Date=20260410T013002Z&X-Amz-Expires=86400&X-Amz-SignedHeaders=host&X-Amz-Signature=eedfd4114bde66f0ff8fe5e4d7a7274d9007b25deb40ee82a19427f29601e89b',
},{
url:'https://www.minio-api.aida.com.hk/aida-users/83/avatar/2b3d5756-ea29-4020-86a9-3b02cfc73b5a.png?response-content-type=image%2Fpng&response-content-disposition=inline&X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=admin%2F20260410%2Fus-east-1%2Fs3%2Faws4_request&X-Amz-Date=20260410T013002Z&X-Amz-Expires=86400&X-Amz-SignedHeaders=host&X-Amz-Signature=eedfd4114bde66f0ff8fe5e4d7a7274d9007b25deb40ee82a19427f29601e89b',
},{
url:'https://www.minio-api.aida.com.hk/aida-users/83/avatar/2b3d5756-ea29-4020-86a9-3b02cfc73b5a.png?response-content-type=image%2Fpng&response-content-disposition=inline&X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=admin%2F20260410%2Fus-east-1%2Fs3%2Faws4_request&X-Amz-Date=20260410T013002Z&X-Amz-Expires=86400&X-Amz-SignedHeaders=host&X-Amz-Signature=eedfd4114bde66f0ff8fe5e4d7a7274d9007b25deb40ee82a19427f29601e89b',
},{
url:'https://www.minio-api.aida.com.hk/aida-users/83/avatar/2b3d5756-ea29-4020-86a9-3b02cfc73b5a.png?response-content-type=image%2Fpng&response-content-disposition=inline&X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=admin%2F20260410%2Fus-east-1%2Fs3%2Faws4_request&X-Amz-Date=20260410T013002Z&X-Amz-Expires=86400&X-Amz-SignedHeaders=host&X-Amz-Signature=eedfd4114bde66f0ff8fe5e4d7a7274d9007b25deb40ee82a19427f29601e89b',
},
],
type:'Series',
name:'Christmas',
sketchNum: 7,
date: 'today',
id:'1',
},{
imgList:[
{
url:'https://www.minio-api.aida.com.hk/aida-users/83/avatar/2b3d5756-ea29-4020-86a9-3b02cfc73b5a.png?response-content-type=image%2Fpng&response-content-disposition=inline&X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=admin%2F20260410%2Fus-east-1%2Fs3%2Faws4_request&X-Amz-Date=20260410T013002Z&X-Amz-Expires=86400&X-Amz-SignedHeaders=host&X-Amz-Signature=eedfd4114bde66f0ff8fe5e4d7a7274d9007b25deb40ee82a19427f29601e89b',
},{
url:'https://www.minio-api.aida.com.hk/aida-users/83/avatar/2b3d5756-ea29-4020-86a9-3b02cfc73b5a.png?response-content-type=image%2Fpng&response-content-disposition=inline&X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=admin%2F20260410%2Fus-east-1%2Fs3%2Faws4_request&X-Amz-Date=20260410T013002Z&X-Amz-Expires=86400&X-Amz-SignedHeaders=host&X-Amz-Signature=eedfd4114bde66f0ff8fe5e4d7a7274d9007b25deb40ee82a19427f29601e89b',
},{
url:'https://www.minio-api.aida.com.hk/aida-users/83/avatar/2b3d5756-ea29-4020-86a9-3b02cfc73b5a.png?response-content-type=image%2Fpng&response-content-disposition=inline&X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=admin%2F20260410%2Fus-east-1%2Fs3%2Faws4_request&X-Amz-Date=20260410T013002Z&X-Amz-Expires=86400&X-Amz-SignedHeaders=host&X-Amz-Signature=eedfd4114bde66f0ff8fe5e4d7a7274d9007b25deb40ee82a19427f29601e89b',
},{
url:'https://www.minio-api.aida.com.hk/aida-users/83/avatar/2b3d5756-ea29-4020-86a9-3b02cfc73b5a.png?response-content-type=image%2Fpng&response-content-disposition=inline&X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=admin%2F20260410%2Fus-east-1%2Fs3%2Faws4_request&X-Amz-Date=20260410T013002Z&X-Amz-Expires=86400&X-Amz-SignedHeaders=host&X-Amz-Signature=eedfd4114bde66f0ff8fe5e4d7a7274d9007b25deb40ee82a19427f29601e89b',
},
],
type:'Series',
name:'Christmas',
sketchNum: 7,
date: 'today',
id:'1',
},
])
const getListData = ()=>{
return {
...props.getCollectionListData,
page: page.value,
size: size.value,
}
}
const getCreateList = ()=>{
let data = getListData()
store.commit("set_loading", true)
Https.axiosPost(Https.httpUrls.historyProject, data).then((rv)=>{
list.value = rv.content || []
total.value = rv.total || 0
store.commit("set_loading", false)
}).catch(()=>{
store.commit("set_loading", false)
})
}
const selectCollectionItem = (item:any)=>{
emit('selectCollectionItem',item)
console.log(item)
if(item.userLikeGroupVO?.groupDetails?.length > 0){
emit('selectCollectionItem',item)
}
}
onMounted(()=>{
getCreateList()
})
onUnmounted(()=>{
})
defineExpose({})
defineExpose({getCreateList})
</script>
<template>
<div class="historyList">
<div class="list">
<div v-for="(item,index) in list" :key="index" class="item" @click="selectCollectionItem(item)">
<div class="imgList">
<div v-for="(img,index) in item.imgList" :key="index" class="img">
<div v-if="item.userLikeGroupVO?.groupDetails?.length > 0" v-for="(img,index) in item.userLikeGroupVO?.groupDetails.slice(0, 4)" :key="index" class="img">
<img :src="img.url" alt="">
</div>
<div v-else class="img null">
<img src="@/assets/images/seller/selectCollectionNullStatus.png" alt="">
</div>
</div>
<div class="detail">
<div class="name">{{item.name}}</div>
<div class="bottom">
<div>{{item.sketchNum}} sketchs</div>
<div>{{item.date}}</div>
<div>{{item.userLikeGroupVO?.groupDetails?.length || 0}} {{ $t('Seller.sketchs') }}</div>
<div>{{setPubDate(item.updateTime,t)}}</div>
</div>
</div>
<div class="type" :class="item.type">
{{item.type}}
{{item.process == "SERIES_DESIGN"?"Series":"Single"}}
</div>
</div>
</div>
<div class="pagination">
<a-pagination v-model:current="current" :pageSize="6" :showSizeChanger="false" show-quick-jumper :total="100" show-less-items />
<a-pagination v-model:current="page" @change="getCreateList" :pageSize="6" :showSizeChanger="false" show-quick-jumper :total="total" show-less-items />
</div>
</div>
</template>
@@ -154,13 +122,18 @@ defineExpose({})
padding: 1rem 1.3rem;
display: flex;
gap: .4rem;
justify-content: center;
> .img{
width: 9.7rem;
height: 17.2rem;
border-radius: 1rem;
overflow: hidden;
background-color: #fff;
&.null{
background-color: transparent;
}
> img{
object-fit: cover;
object-fit: contain;
height: 100%;
width: 100%;
}

View File

@@ -4,66 +4,70 @@ import sellerHeader from "../../seller-header.vue"
import historyList from "./historyList.vue"
import { useRouter } from "vue-router"
//const props = defineProps({
//})
//const emit = defineEmits([
//])
const router = useRouter()
let data = reactive({
})
const searchType = ref('all')
const searchText = ref('')
let getCollectionListData = reactive({
process: [],
projectName: '',
const historyListRef = ref(null)
})
const isShowMark = ref(false)
const historyListRef = ref(null) as any
const handleSearch = () => {
historyListRef.value.getCreateList()
}
const setProcess = (type:any) => {
if(type){
getCollectionListData.process = [type]
}else{
getCollectionListData.process = []
}
historyListRef.value.getCreateList()
}
const selectCollectionItem = (item:any) => {
router.push({path:'/home/seller/myListings/select/'+item.id})
}
onMounted(()=>{
})
onUnmounted(()=>{
})
defineExpose({})
const {} = toRefs(data);
</script>
<template>
<div class="myListings-seller">
<seller-header
title="Select Collection"
:breadcrumbs="[
{title:'My Listings', name:'myListingsIndex'},
{title:'Select Collection', name: 'myListingsSelect' }
]"
>
</seller-header>
<div class="create-select">
<seller-header />
<div class="content">
<div class="title">
<div class="left">
<div :class="{active:searchType == 'all'}" @click="searchType = 'all'">All</div>
<div :class="{active:searchType == 'series'}" @click="searchType = 'series'">Series Design</div>
<div :class="{active:searchType == 'single'}" @click="searchType = 'single'">Single Design</div>
<div :class="{active:!getCollectionListData.process?.[0]}" @click="setProcess('')">{{$t('Seller.All')}}</div>
<div :class="{active:getCollectionListData.process[0] == 'SERIES_DESIGN'}" @click="setProcess('SERIES_DESIGN')">{{$t('Seller.SeriesDesign')}}</div>
<div :class="{active:getCollectionListData.process[0] == 'SINGLE_DESIGN'}" @click="setProcess('SINGLE_DESIGN')">{{$t('Seller.SingleDesign')}}</div>
</div>
<div class="right">
<div class="search_input flex flex-align-center">
<input
class="search_input_inner"
v-model="searchText"
v-model="getCollectionListData.projectName"
:bordered="false"
@keydown.enter="handleSearch"
placeholder="123123"
placeholder="Search"
/>
<!-- <SearchOutlined class="search_input_icon" @click="handleSearch" /> -->
<SvgIcon name="CSearch" size="20" class="search_input_icon" @click="handleSearch" />
</div>
</div>
</div>
<historyList ref="historyListRef" @selectCollectionItem="selectCollectionItem"></historyList>
<historyList ref="historyListRef" :getCollectionListData="getCollectionListData" @selectCollectionItem="selectCollectionItem"></historyList>
</div>
</div>
</template>
<style scoped lang="less">
.myListings-seller {
.create-select {
width: 100%;
height: 100%;
overflow: hidden;
@@ -141,4 +145,4 @@ const {} = toRefs(data);
}
}
}
</style>
</style>

View File

@@ -3,47 +3,68 @@ import { ref, onMounted, onUnmounted, reactive, toRefs } from "vue";
import sellerHeader from "../../seller-header.vue"
import { VueDraggable } from "vue-draggable-plus"
import selectMenu from '@/component/modules/selectMenu.vue'
import { Https } from '@/tool/https'
import { useRoute } from 'vue-router'
import { useStore } from 'vuex'
import { useI18n } from 'vue-i18n'
import { message } from 'ant-design-vue';
// 定义组件名称
defineOptions({
name: 'myListingsSelectItem'
})
const { t } = useI18n()
//const props = defineProps({
//})
//const emit = defineEmits([
//])
let data = reactive({
})
const store = useStore()
const route = useRoute()
const domSize = ref('Small')
const domSizeList = ref([
{
label:'Small',
label:t('Header.Small'),
value:'Small',
},
{
label:'Medium',
label:t('Header.Medium'),
value:'Medium',
},
{
label:'Large',
label:t('Header.Large'),
value:'Large',
},
])
const router = useRouter()
const list = ref([
{ id: "1" },
{ id: "2" },
{ id: "3" },
{ id: "4" },
{ id: "5" },
{ id: "6" },
{ id: "7" },
{ id: "8" },
{ id: "9" },
{ id: "10" }
])
const showList = ref([])
const chooseList = ref([])
const chooseItem = (item:any)=>{
chooseList.value.push(item)
if(chooseList.value.findIndex((i:any)=>i.designItemId == item.designItemId) != -1){
chooseList.value.splice(chooseList.value.findIndex((i:any)=>i.designItemId == item.designItemId),1)
}else{
if(chooseList.value.length >= 9){
message.info(t('Seller.selectSketchMaxNum'))
return
}
chooseList.value.push(item)
}
}
const next = ()=>{
if(chooseList.value.length == 0)return
let designItemIds = chooseList.value.map((item:any)=>({designOutfitUrl:item.designOutfitUrl,designItemId:item.designItemId}))
router.push({
path:'/home/seller/myListings/edit',
state: {
designItemIds,
type:'create',
collectionId: route.params.collectionId
}
})
}
const config = ref({
@@ -78,7 +99,7 @@ const setDomSize = (width: number)=>{
if(!listingsBoxRef.value)return
let listDom = listingsBoxRef.value.querySelector('.list')
let listItemDom = listDom.querySelector('.item')
let offsetWidth = listItemDom.getBoundingClientRect().width
let offsetWidth = listItemDom?.getBoundingClientRect?.()?.width
let lineNum = Math.floor(width / offsetWidth)
let itemNum = Math.floor((width - (lineNum - 1) * parseInt(gap.value[domSize.value])) / offsetWidth)
listDom.style.maxWidth = ((itemNum - 1) * parseInt(gap.value[domSize.value]) + itemNum * (offsetWidth)) + 'px'
@@ -89,6 +110,32 @@ const changeDomSize = ()=>{
setDomSize(listingsBoxRef.value.clientWidth)
},350)
}
const changeGender = ()=>{
}
const setShowList = ()=>{
if(gender.value == 'All'){
showList.value = list.value
}else{
showList.value = list.value.filter((item:any)=>item.sex == gender.value)
}
}
const getCollectionDetail = ()=>{
store.commit("set_loading", true)
let moduleList = [] as any
moduleList = ['design',]
let value:any = {
"id":route.params.collectionId,
"moduleList":moduleList,
}
Https.axiosPost(Https.httpUrls.getModuleContent,value).then(async (rv)=>{
list.value = rv.design.userLikeDetails
setShowList()
store.commit("set_loading", false)
}).catch(()=>{
store.commit("set_loading", false)
})
}
onMounted(()=>{
// 创建观察器
nextTick(()=>{
@@ -105,6 +152,10 @@ onMounted(()=>{
// 开始监听
if(resizeObserver)resizeObserver.observe(listingsBoxRef.value)
})
chooseList.value = []
getCollectionDetail()
})
onActivated(()=>{
})
onUnmounted(()=>{
})
@@ -112,22 +163,15 @@ defineExpose({})
const {} = toRefs(data);
</script>
<template>
<div class="myListings-seller">
<seller-header
title="Select Collection"
:breadcrumbs="[
{title:'My Listings', name:'myListingsIndex'},
{title:'Select Collection', name: 'myListingsSelect' },
{title:'Select Sketch', name: 'myListingsSelectItem' }
]"
>
<div class="create-select-item">
<seller-header>
<template #right>
<div class="header-right">
<div class="chooseNum">
{{ chooseList.length }} sketches selected
{{ chooseList.length }} / 9 {{ t('Seller.sketchesSelected') }}
</div>
<div class="button" @click="next">
<span>Next</span>
<span>{{ $t('Seller.Next') }}</span>
<div class="icon">
<i class="fi fi-rr-arrow-small-right"></i>
</div>
@@ -138,32 +182,11 @@ const {} = toRefs(data);
<div class="content" ref="listingsBoxRef">
<div class="title">
<div class="left">
<i class="fi fi-rs-comments"></i>
<span>Active Listings</span>
<i class="fi fi-rs-comments"></i>
<span>{{ $t('Seller.Praka') }}</span>
</div>
<div class="right">
<div class="generalModel_state">
<div>
<selectMenu
:selectList="domSizeList"
@change="changeDomSize"
:isBtnOpen='true'
:style="{
'border-radius':'0rem',
'border':'none',
'font-weight': '900',
'border-right':'2px solid rgba(0,0,0,.2)',
'line-height': '3rem',
'height': '3rem',
'background': 'rgba(0,0,0,0)',
}"
v-model:select="domSize"
>
<template v-slot:btnText>
{{ $t('Header.Filter') }}
</template>
</selectMenu>
</div>
<div>
<selectMenu
:selectList="domSizeList"
@@ -188,7 +211,7 @@ const {} = toRefs(data);
</div>
</div>
<VueDraggable
v-model="list"
v-model="showList"
class="list"
:class="domSize"
v-bind="config"
@@ -208,13 +231,14 @@ const {} = toRefs(data);
<div class="choose">
<i class="fi fi-rr-check"></i>
</div>
<img :src="v.designOutfitUrl || v.url" alt="">
</div>
</VueDraggable>
</div>
</div>
</template>
<style scoped lang="less">
.myListings-seller {
.create-select-item {
width: 100%;
height: 100%;
overflow: hidden;
@@ -298,6 +322,7 @@ const {} = toRefs(data);
margin: 0 auto;
overflow-y: auto;
align-content: flex-start;
min-width: 90%;
&::-webkit-scrollbar {
display: none;
}
@@ -329,6 +354,9 @@ const {} = toRefs(data);
border: 1.5px solid #C7C7C7;
// transition: all .3s;
position: relative;
overflow: hidden;
display: flex;
justify-content: center;
> .choose{
display: flex;
width: var(--iconWH);
@@ -347,6 +375,11 @@ const {} = toRefs(data);
display: flex;
}
}
> img{
height: 100%;
width: 100%;
object-fit: contain;
}
&.active{
border: 1.5px solid #000;
> .choose{
@@ -360,4 +393,4 @@ const {} = toRefs(data);
}
}
}
</style>
</style>

View File

@@ -4,37 +4,34 @@ import { VueDraggable } from "vue-draggable-plus"
import contentItem from "./contentItem.vue"
import selectMenu from '@/component/modules/selectMenu.vue'
import deleteDrafts from './deleteDrafts.vue'
import { Https } from '@/tool/https'
import { message } from 'ant-design-vue'
import { useRouter } from 'vue-router'
import { useI18n } from 'vue-i18n'
//const props = defineProps({
//})
//const emit = defineEmits([
//])
const { t:$t } = useI18n()
let data = reactive({
showDrafts: false,
})
const publishData = reactive({
pageSize: 10,
pageNum: 1,
isShowMark:false,
isNoData:false,
})
const noPublishData = reactive({
pageSize: 10,
pageNum: 1,
isShowMark:false,
isNoData:false,
})
const list = ref([
{ id: "1" },
{ id: "2" },
{ id: "3" },
{ id: "4" },
{ id: "5" },
{ id: "6" },
{ id: "7" },
{ id: "8" },
{ id: "9" },
{ id: "10" }
])
const list2 = ref([
{ id: "1" },
{ id: "2" },
{ id: "3" },
{ id: "4" },
{ id: "5" },
{ id: "6" },
{ id: "7" },
{ id: "8" },
{ id: "9" },
{ id: "10" }
])
const config = ref({
"data-container-type": "root",
@@ -55,40 +52,130 @@ const config = ref({
const domSize = ref('Small')
const domSizeList = ref([
{
label:'Small',
label:$t('Header.Small'),
value:'Small',
},
{
label:'Medium',
label:$t('Header.Medium'),
value:'Medium',
},
{
label:'Large',
label:$t('Header.Large'),
value:'Large',
},
])
const visible = ref(false)
const deleteDraftsRef = ref(null)
const router = useRouter()
const deleteDraft = (item: any)=>{
deleteDraftsRef.value.open(()=>{
list2.value = list2.value.filter((v: any)=>v.id != item.id)
deleteDraftsRef.value.open(item,()=>{
putListingStatus(item,2).then(()=>{
list2.value = list2.value.filter((v: any)=>v.id != item.id)
})
})
}
const draftListing = (item: any)=>{
//数组前面添加item
list2.value.unshift(item)
list.value = list.value.filter((v: any)=>v.id != item.id)
const vObserve = {
mounted (el,binding) {
// console.log(binding.instance);
data.isShowMark = false
data.isNoData = false
let parentDom = el.parentNode
new IntersectionObserver(
(entries, observer) => {
// 如果不是相交,则直接返回
// console.log(entries[0]);
if (!entries[0].intersectionRatio) return;
data.pageNum += 1
binding.value()
},
// { root:worksPage }
).observe(el);
}
}
const publishListing = (item: any)=>{
list.value.unshift(item)
list2.value = list2.value.filter((v: any)=>v.id != item.id)
const getPublishedData = async ()=>{
if(publishData.isShowMark && !publishData.isNoData)return
publishData.isShowMark = true
let value = {
pageSize: publishData.pageSize,
pageNum: publishData.pageNum,
status: 1,
}
await getPublishList(value).then((res)=>{
if(res.content.length == 0)publishData.isNoData = true
publishData.pageNum += 1
list.value.push(...res.content)
})
publishData.isShowMark = false
}
const getNoPublishedData = async ()=>{
if(noPublishData.isShowMark && !noPublishData.isNoData)return
noPublishData.isShowMark = true
let value = {
pageSize: noPublishData.pageSize,
pageNum: noPublishData.pageNum,
status: 0,
}
await getPublishList(value).then((res)=>{
if(res.content.length == 0)noPublishData.isNoData = true
noPublishData.pageNum += 1
list2.value.push(...res.content)
})
noPublishData.isShowMark = false
}
const getPublishList = (data:any)=>{
return new Promise<void>((resolve, reject) => {
Https.axiosGet(Https.httpUrls.getListingList,{params:data}).then((res:any)=>{
resolve(res)
}).catch((err:any)=>{
reject(err)
})
})
}
const putListingStatus = async (item,status:number)=>{
return new Promise<void>((resolve, reject) => {
let data = {
id:item.id,
status:status,
}
Https.axiosPut(Https.httpUrls.putListingStatus,data).then((res:any)=>{
resolve(res)
}).catch((err:any)=>{
reject(err)
})
})
}
const draftListing = async (item: any)=>{
//数组前面添加item
await putListingStatus(item,0).then(()=>{
list2.value.unshift(item)
list.value = list.value.filter((v: any)=>v.id != item.id)
})
message.success($t('Seller.draftMessage'))
}
const publishListing = async (item: any)=>{
await putListingStatus(item,1).then(()=>{
list.value.unshift(item)
list2.value = list2.value.filter((v: any)=>v.id != item.id)
})
message.success($t('Seller.publishMessage'))
}
const editListing = (item: any)=>{
router.push({
path:'/home/seller/myListings/edit',
state: {
id:item.id,
type:'edit'
}
})
}
@@ -107,7 +194,7 @@ const setDomSize = (width: number)=>{
if(!listingsBoxRef.value)return
let listDom = listingsBoxRef.value.querySelector('.list')
let listItemDom = listDom.querySelector('.item')
let offsetWidth = listItemDom.getBoundingClientRect().width
let offsetWidth = listItemDom?.getBoundingClientRect?.()?.width
let lineNum = Math.floor(width / offsetWidth)
let itemNum = Math.floor((width - (lineNum - 1) * parseInt(gap.value[domSize.value])) / offsetWidth)
listDom.style.maxWidth = ((itemNum - 1) * parseInt(gap.value[domSize.value]) + itemNum * (offsetWidth)) + 'px'
@@ -127,11 +214,7 @@ onMounted(()=>{
nextTick(()=>{
resizeObserver = new ResizeObserver((entries) => {
for (const entry of entries) {
// 获取元素尺寸
const width = entry.contentRect.width
const height = entry.contentRect.height
const borderBoxSize = entry.borderBoxSize[0] // 包含边框
const contentBoxSize = entry.contentBoxSize[0] // 内容区域
if(upDataDomWidthTime)clearTimeout(upDataDomWidthTime)
upDataDomWidthTime = setTimeout(()=>{
setDomSize(width)
@@ -156,31 +239,10 @@ const { showDrafts } = toRefs(data);
<div class="title">
<div class="left">
<i class="fi fi-rs-comments"></i>
<span>Active Listings</span>
<span>{{ $t('Seller.ActiveListings') }}</span>
</div>
<div class="right">
<div class="generalModel_state">
<div>
<selectMenu
:selectList="domSizeList"
@change="changeDomSize"
:isBtnOpen='true'
:style="{
'border-radius':'0rem',
'border':'none',
'font-weight': '900',
'border-right':'2px solid rgba(0,0,0,.2)',
'line-height': '3rem',
'height': '3rem',
'background': 'rgba(0,0,0,0)',
}"
v-model:select="domSize"
>
<template v-slot:btnText>
{{ $t('Header.Filter') }}
</template>
</selectMenu>
</div>
<div>
<selectMenu
:selectList="domSizeList"
@@ -225,6 +287,12 @@ const { showDrafts } = toRefs(data);
@draftListing="draftListing"
@editListing="editListing"
/>
<div v-show="!publishData.isNoData" class="material_content_list_loding">
<span class="page_loading" v-show="!publishData.isShowMark" v-observe="getPublishedData"></span>
<span v-show="publishData.isShowMark">
<a-spin size="large" />
</span>
</div>
</VueDraggable>
</div>
<div class="openOrCloseDrafts" :class="{'active': showDrafts}" @click="unfoldDrafits">
@@ -236,7 +304,7 @@ const { showDrafts } = toRefs(data);
<div class="title">
<div class="left">
<i class="fi fi-rs-comments"></i>
<span>Drafts</span>
<span>{{ $t('Seller.Drafts') }}</span>
</div>
</div>
<VueDraggable
@@ -245,7 +313,7 @@ const { showDrafts } = toRefs(data);
v-bind="config"
:group="{
name: 'sortable',
pull: true,
pull: false,
put: true
}"
>
@@ -259,6 +327,12 @@ const { showDrafts } = toRefs(data);
@editListing="editListing"
@publishListing="publishListing"
/>
<div v-show="!noPublishData.isNoData" class="material_content_list_loding">
<span class="page_loading" v-show="!noPublishData.isShowMark" v-observe="getNoPublishedData"></span>
<span v-show="noPublishData.isShowMark">
<a-spin size="large" />
</span>
</div>
</VueDraggable>
</div>
</div>
@@ -326,9 +400,23 @@ const { showDrafts } = toRefs(data);
margin: 0 auto;
align-items: flex-start;
overflow: auto;
min-width: 90%;
&::-webkit-scrollbar {
display: none;
}
> .material_content_list_loding{
width: 100%;
height: 5rem;
aspect-ratio: 1/1;
overflow: hidden;
display: flex;
justify-content: center;
> img{
width: 100%;
height: 100%;
object-fit: contain;
}
}
}
}
}

View File

@@ -32,39 +32,39 @@ const {} = toRefs(data);
<template>
<div class="item" :draging="true" :class="domSize">
<div class="imgBox">
<img src="" alt="">
<img :src="item.cover" alt="">
<div class="maskBtn">
<div @click="$emit('editListing',item)">
<svgIcon name="seller-edit" :size="domSize == 'Small'?32:domSize == 'Medium'?40:48" />
<div>Edit</div>
<div>{{ $t('Seller.Edit') }}</div>
</div>
<div v-if="type == 'listings'" @click="$emit('draftListing',item)">
<svgIcon name="seller-draft" :size="domSize == 'Small'?32:domSize == 'Medium'?40:48" />
<div>Draft</div>
<div>{{ $t('Seller.Draft') }}</div>
</div>
<div v-else-if="type == 'drafts'" @click="$emit('publishListing',item)">
<svgIcon name="seller-share" :size="domSize == 'Small'?32:domSize == 'Medium'?40:48" />
<div>Publish</div>
<div>{{ $t('Seller.Publish') }}</div>
</div>
</div>
</div>
<div class="detail">
<div class="left">
<div class="name">item name</div>
<div class="price">$1123</div>
<div class="name">{{item.title}}</div>
<div class="price">${{item.price}}</div>
</div>
<div class="right">
<div class="detailItem" v-if="type == 'listings'">
<div class="shopping1">
<i class="fi fi-rr-shopping-bag-add"></i>
</div>
<span>123</span>
<span>{{ item.salesVolume || 0 }}</span>
</div>
<div class="detailItem" v-if="type == 'listings'">
<div class="eye1">
<i class="fi fi-rs-eye"></i>
</div>
<span>123</span>
<span>{{ item.viewCount }}</span>
</div>
<div class="detailItem drafts" v-if="type == 'drafts'" @click="$emit('deleteDraft',item)">
<div class="">
@@ -128,6 +128,12 @@ const {} = toRefs(data);
position: relative;
height: var(--itemImgHeight);
width: 100%;
> img{
width: 100%;
height: 100%;
object-fit: cover;
background-color: #fff;
}
> .maskBtn{
position: absolute;
width: 100%;
@@ -182,6 +188,7 @@ const {} = toRefs(data);
gap: var(--detailRightItemGap);
flex-direction: column;
justify-content: space-between;
align-items: center;
> div{
color: var(--rightColor);
display: flex;

View File

@@ -15,13 +15,15 @@ const router = useRouter()
const {t} = useI18n()
let data = reactive({
})
const item = ref<any>()
const fun = ref(null)
let deleteDraftsRef = ref(null)
const open = (fun)=>{
fun.value = fun
const open = (data:any,deleteFun)=>{
console.log(data)
item.value = data
fun.value = deleteFun
emit('update:visible', true)
}
@@ -35,6 +37,7 @@ const deleteDrafts = ()=>{
const cleardata = ()=>{
item.value = null
emit('update:visible', false)
}
@@ -72,22 +75,22 @@ const { showAgain } = toRefs(data);
<i class="fi fi-rr-trash"></i>
</div>
<div class="titleText">
<h1>Delete this listing?</h1>
<p>Your listing and its details will be permanently removed.</p>
<h1>{{ $t('Seller.DeleteConfirm') }}</h1>
<p>{{ $t('Seller.DeleteDesc') }}</p>
</div>
</div>
<div class="deleteContent">
<div class="img">
<img src="" alt="">
<img :src="item?.cover" alt="">
</div>
<div class="detail">
<div class="name">Item Name</div>
<div class="price">HK$392.00 · Draft</div>
<div class="name">{{ item?.title }}</div>
<div class="price">HK${{ item?.price }} · {{ $t('Seller.Drafts') }}</div>
</div>
</div>
<div class="btnBox">
<div class="btn" @click.stop="cleardata()">Cancel</div>
<div class="btn" @click.stop="deleteDrafts()">Delete</div>
<div class="btn" @click.stop="cleardata()">{{ $t('Seller.Cancel') }}</div>
<div class="btn" @click.stop="deleteDrafts()">{{ $t('Seller.Delete') }}</div>
</div>
</a-modal>
</div>

View File

@@ -3,17 +3,36 @@ import { ref, onMounted, onUnmounted, reactive, toRefs } from "vue";
import sellerHeader from "../../seller-header.vue"
import sellerContent from "./content.vue"
import myEvent from "@/tool/myEvents.js"
import { Https } from '@/tool/https'
import { useStore } from "vuex";
import { useRouter } from 'vue-router'
//const props = defineProps({
//})
//const emit = defineEmits([
//])
const router = useRouter()
const store = useStore()
let data = reactive({
})
const newListing = ()=>{
myEvent.emit('newListing')
const newListing = async ()=>{
let path = '/home/seller/myListings/select'
store.commit("set_loading", true)
//1弹窗 0不弹窗
Https.axiosGet(Https.httpUrls.getListingPopup,).then((rv)=>{
if(rv == 0){
router.push({path:'/home/seller/myListings/select'})
}else{
myEvent.emit('newListing',path)
}
store.commit("set_loading", false)
}).catch(()=>{
store.commit("set_loading", false)
})
}
onMounted(()=>{
let listingPopup = sessionStorage.getItem('listingPopup')
})
onUnmounted(()=>{
})
@@ -22,13 +41,10 @@ const {} = toRefs(data);
</script>
<template>
<div class="myListings-seller">
<seller-header
title="My Listings"
tip="Active listings and unpublished inventory."
>
<seller-header :displayBack="false">
<template #right>
<div class="button" @click="newListing">
<span>New Listing</span>
<span>{{ $t('Seller.newListing') }}</span>
<div class="icon">
<i class="fi fi-br-plus"></i>
</div>
@@ -80,4 +96,4 @@ const {} = toRefs(data);
position: relative;
}
}
</style>
</style>

View File

@@ -11,8 +11,8 @@
</div>
<div class="filter-box">
<div class="left">
<div class="title">All Invoice</div>
<div class="tip">A summary of all completed transactions.</div>
<div class="title">{{ t("Seller.allInvoice") }}</div>
<div class="tip">{{ t("Seller.myOrdersTip") }}</div>
</div>
<div class="right">
<div class="input">
@@ -22,7 +22,7 @@
<input
type="text"
v-model="nameOrId"
placeholder="Search by item name or order ID"
:placeholder="t('Seller.myOrdersSearchPlaceholder')"
@keydown.enter.prevent="getList(true)"
/>
</div>
@@ -30,11 +30,11 @@
</div>
<div class="table">
<div class="header">
<div class="order-id">Order ID</div>
<div class="item">Item</div>
<div class="price">Price</div>
<div class="buyer-username">Buyer Username</div>
<div class="date">Date</div>
<div class="order-id">{{ t("Seller.orderId") }}</div>
<div class="item">{{ t("Seller.item") }}</div>
<div class="price">{{ t("Seller.price") }}</div>
<div class="buyer-username">{{ t("Seller.buyerUsername") }}</div>
<div class="date">{{ t("Seller.date") }}</div>
</div>
<div class="body">
<div class="item" v-for="v in list" :key="v.orderId">
@@ -42,8 +42,8 @@
<div class="item">
<div class="images">
<img
v-for="(v, i) in v.item.slice(0, maxItemNum)"
:key="i"
v-for="v in v.item.slice(0, maxItemNum)"
:key="v.id"
:src="v.url"
/>
<span v-if="v.item.length > maxItemNum"
@@ -51,7 +51,7 @@
>
</div>
<div class="titles">
<div v-for="(v, i) in v.item.slice(0, maxItemNum)" :key="i">
<div v-for="v in v.item.slice(0, maxItemNum)" :key="v.id">
{{ v.title }}
</div>
<span v-if="v.item.length > maxItemNum">...</span>
@@ -65,29 +65,42 @@
</div>
</div>
</div>
<div class="null" v-show="list.length === 0 && !loading && finish">
<img src="@/assets/images/homePage/null_img.png" alt="" />
</div>
<div class="placeholder" ref="placeholderRef" v-show="!loading"></div>
<div class="footer" v-if="!finish"><a-spin :delay="0.5" v-show="loading" /></div>
<div class="footer" :class="{ null: list.length === 0 }" v-if="!finish">
<a-spin :delay="0.5" v-show="loading" />
</div>
</div>
</div>
</template>
<script setup>
import { ref, onMounted, onBeforeUnmount } from "vue"
const totals = ref([
import { ref, onMounted, onBeforeUnmount, computed } from "vue"
import { Https } from "@/tool/https"
import { useI18n } from "vue-i18n"
const { t } = useI18n()
const totals_obj = ref({
totalRevenue: "--",
totalPurchases: "--",
totalViews: "--"
})
const totals = computed(() => [
{
icon: "seller-qiandaizi",
title: "Total Revenue",
value: "HK$ 54,32.00"
title: t("Seller.totalRevenue"),
value: `HK$ ${totals_obj.value.totalRevenue}`
},
{
icon: "seller-gouwudai",
title: "Total Purchases",
value: "128"
title: t("Seller.totalPurchases"),
value: totals_obj.value.totalPurchases
},
{
icon: "seller-eye",
title: "Total Views",
value: "4,982"
title: t("Seller.totalViews"),
value: totals_obj.value.totalViews
}
])
const maxItemNum = ref(2)
@@ -99,6 +112,7 @@
const nameOrId = ref("")
const list = ref([])
const getList = (isReload = false) => {
if (loading.value) return
loading.value = true
if (isReload) {
list.value = []
@@ -109,38 +123,46 @@
page: page.value,
size: size.value
}
if (nameOrId.value) data.nameOrId = nameOrId.value
console.log(data)
setTimeout(() => {
for (let i = 0; i < size.value; i++) {
let { date, time, dateTime } = formatTimestamp(new Date(2026, 4, 20, 13, 14).getTime())
list.value.push({
orderId: "SP" + Math.random().toString().substring(2, 10),
price: "HK$ " + (Math.random() * 500).toFixed(2),
username: "@liuyuchen",
date: date,
time: time,
item: [
{
url: "http://118.31.39.42:3000/falls/o-1.png",
title: "North Outfit Set"
},
{
url: "http://118.31.39.42:3000/falls/o-2.png",
title: "Heritage Layered Set"
},
{},
{}
]
if (nameOrId.value) data.keyword = nameOrId.value
Https.axiosGet(Https.httpUrls.getSellerOrderList, { params: data })
.then((res) => {
res.content?.forEach((v) => {
const obj = {
orderId: v.orderId,
items: v.items.map((item) => ({
id: item.productId,
url: item.thumbnailUrl,
title: item.productName
})),
price: "HK$ " + v.price,
username: v.buyerUsername,
date: v.date,
time: v.date
}
list.value.push(obj)
})
}
total.value = 30
page.value++
finish.value = page.value > total.value / 10
loading.value = false
}, 1000)
total.value = res.total
page.value++
finish.value = page.value > total.value / size.value
loading.value = false
})
.catch(() => {
finish.value = true
loading.value = false
})
}
const getSummary = () => {
Https.axiosGet(Https.httpUrls.getSellerOrderSummary).then((res) => {
totals_obj.value.totalRevenue = res.totalRevenue
totals_obj.value.totalPurchases = res.totalPurchases
totals_obj.value.totalViews = res.totalViews
})
}
getSummary()
getList(true)
const placeholderRef = ref(null)
const observer = new IntersectionObserver(
(entries) => {
@@ -345,11 +367,25 @@
}
}
}
> .null {
margin-top: 10rem;
display: flex;
align-items: center;
justify-content: center;
flex-direction: column;
> img {
width: 30rem;
height: auto;
}
}
> .footer {
min-height: 10rem;
display: flex;
align-items: center;
justify-content: center;
&.null {
height: 30rem;
}
}
> .placeholder {
width: 100%;

View File

@@ -2,11 +2,11 @@
<div class="settings-index">
<div>
<div class="notification">
<div class="header">Notifications</div>
<div class="header">{{ $t("Seller.notifications") }}</div>
<div class="content">
<div class="left">
<div class="title">New order notification</div>
<div class="tip">Receive an inbox message when a new order is placed.</div>
<div class="title">{{ $t("Seller.notificationsTitle") }}</div>
<div class="tip">{{ $t("Seller.notificationsTip") }}</div>
</div>
<div class="right">
<a-switch v-model:checked="checked" />
@@ -14,29 +14,29 @@
</div>
</div>
<div class="payout">
<div class="header">Payout</div>
<div class="header">{{ $t("Seller.payout") }}</div>
<div class="content">
<div class="header">
<div class="title">Payment Providers</div>
<div class="tip">Select how you want to receive payments.</div>
<div class="title">{{ $t("Seller.payoutTitle") }}</div>
<div class="tip">{{ $t("Seller.payoutTip") }}</div>
</div>
<div class="pay-item" v-for="v in payList" :key="v.type">
<div class="left">
<img :src="v.icon" />
<div class="value">{{ v.value || "Unbound" }}</div>
<div class="value">{{ v.value || $t("Seller.unbound") }}</div>
</div>
<div class="right">
<button v-if="v.value" class="manage">Manage</button>
<button v-else class="bind-now">Bind Now</button>
<button v-if="v.value" class="manage">{{ $t("Seller.manage") }}</button>
<button v-else class="bind-now">{{ $t("Seller.bindNow") }}</button>
</div>
</div>
<div class="footer">
<div class="left">
<div class="title">Payment Currency</div>
<div class="tip">HKD - Hong Kong Dollar</div>
<div class="title">{{ $t("Seller.paymentCurrency") }}</div>
<div class="tip">{{ $t("Seller.HKD") }}</div>
</div>
<div class="right">
<button>Fixed</button>
<button>{{ $t("Seller.fixed") }}</button>
</div>
</div>
</div>
@@ -44,40 +44,32 @@
</div>
<div>
<div class="data-privacy">
<div class="header">Data & Privacy</div>
<div class="header">{{ $t("Seller.dataPrivacy") }}</div>
<div class="content">
<div class="title">Copyright licence</div>
<div class="tip">
A licence certificate is automatically included with every purchase
download. View the default licensing terms applied to your listings.
</div>
<div class="tip">
This licence is issued by Code-Create and is legally binding upon purchase.
It certifies the buyer's right to use the purchased design asset in
accordance with the terms below.
</div>
<div class="tip">
For custom licensing arrangements, <span>contact us</span>.
</div>
<div class="title">{{ $t("Seller.dataPrivacyTitle") }}</div>
<div class="tip">{{ $t("Seller.dataPrivacyTip1") }}</div>
<div class="tip">{{ $t("Seller.dataPrivacyTip2") }}</div>
<div
class="tip"
v-html="
$t('Seller.dataPrivacyTip3', { click: 'onSellerSettingsContactUs' })
"
></div>
<div class="btns">
<button>
<button @click="onDownloadDataPrivacy">
<span class="icon"><svg-icon name="seller-download" size="14" /></span>
<span class="label">Download to View</span>
<span class="label">{{ $t("Seller.downloadToView") }}</span>
</button>
</div>
</div>
</div>
<div class="stop">
<div class="header">Stop Selling</div>
<div class="header">{{ $t("Seller.stopSelling") }}</div>
<div class="content">
<div class="title">Deactivate seller account</div>
<div class="tip">
Permanently deactivate your seller account. All listings and invoice records
will be deleted. You may re-register as a seller in the future, but your
previous sales data cannot be recovered.
</div>
<div class="title">{{ $t("Seller.stopSellingTitle") }}</div>
<div class="tip">{{ $t("Seller.stopSellingTip") }}</div>
<div class="btns">
<button>Deactivate</button>
<button @click="onStopSelling">{{ $t("Seller.deactivate") }}</button>
</div>
</div>
</div>
@@ -87,10 +79,19 @@
<script setup>
import { ref } from "vue"
import { Https } from "@/tool/https"
import { Modal } from "ant-design-vue"
import paypal from "@/assets/images/seller/setting/paypal.png"
import stripe from "@/assets/images/seller/setting/stripe.png"
import alipayHk from "@/assets/images/seller/setting/alipay-hk.png"
import alipayChinese from "@/assets/images/seller/setting/alipay-chinese.png"
import { useI18n } from "vue-i18n"
const { t } = useI18n()
import { useStore } from "vuex"
const store = useStore()
import { useRouter } from "vue-router"
const router = useRouter()
const checked = ref(true)
const payList = ref([
{
@@ -114,6 +115,33 @@
value: "123123"
}
])
window.onSellerSettingsContactUs = () => {
console.log("contact us")
}
const onDownloadDataPrivacy = () => {
console.log("download data privacy")
}
const onStopSelling = () => {
Modal.confirm({
title: t("Seller.stopSellingTitle"),
content: t("Seller.stopSellingTip"),
okText: t("Seller.confirm"),
cancelText: t("Seller.cancel"),
centered: true,
onOk() {
store.commit("set_loading", true)
Https.axiosDelete(Https.httpUrls.deleteSellerDesigner)
.then((res) => {
store.commit("set_loading", false)
store.commit("seller/clear_state")
router.push({ name: "home" })
})
.catch(() => {
store.commit("set_loading", false)
})
}
})
}
</script>
<style scoped lang="less">
.settings-index {
@@ -281,7 +309,7 @@
font-size: 1.4rem;
color: #999;
margin-bottom: 3rem;
> span {
&:deep(*) {
cursor: pointer;
text-decoration: underline;
color: #0080ed;

View File

@@ -1,5 +1,5 @@
<template>
<div class="seller-dashboard-index">
<div class="seller-dashboard-index" v-if="isSeller">
<div class="nav">
<div
v-for="v in list"
@@ -12,53 +12,73 @@
</div>
</div>
<router-view></router-view>
<toolTipBox v-model:visible="visible" @close="() => {}"></toolTipBox>
<router-view v-slot="{ Component }">
<keep-alive :include="cachedRoutes">
<component :is="Component" />
</keep-alive>
</router-view>
<toolTipBox v-model:visible="visible" @close="() => {}" ref="toolTipBoxRef"></toolTipBox>
</div>
</template>
<script setup>
import { ref, computed } from "vue"
import { Https } from "@/tool/https"
import { ref, computed, onMounted, onUnmounted } from "vue"
import { useRoute, useRouter } from "vue-router"
import toolTipBox from "./toolTipBox.vue"
import myEvent from "@/tool/myEvents.js"
import { useStore } from "vuex"
import { useI18n } from "vue-i18n"
const { t } = useI18n()
const props = defineProps({
cachedRoutes: {
type: Array,
default: () => []
}
})
const store = useStore()
const isSeller = computed(() => store.state.seller.isSeller)
// store.dispatch("seller/get_designerInfo")
const route = useRoute()
const router = useRouter()
const visible = ref(false)
const list = ref([
{
icon: "seller-brandProfile",
layer: "Brand Profile",
layer: t("Seller.brandProfile"),
path: "/home/seller/brandProfile"
},
{
icon: "seller-myListings",
layer: "My Listings",
layer: t("Seller.myListings"),
path: "/home/seller/myListings"
},
{
icon: "seller-myOrders",
layer: "My Orders",
layer: t("Seller.myOrders"),
path: "/home/seller/myOrders"
},
{
icon: "seller-settings",
layer: "Settings",
layer: t("Seller.settings"),
path: "/home/seller/settings"
}
])
const toolTipBoxRef = ref(null)
const activePath = computed(() => route.path)
const handleClick = (path) => {
if (path === activePath.value) return
router.push(path)
}
onMounted(()=>{
myEvent.add('newListing',()=>{
onMounted(() => {
myEvent.add("newListing", (path) => {
toolTipBoxRef.value.routerPath = path
visible.value = true
})
})
onUnmounted(()=>{
myEvent.remove('newListing')
onUnmounted(() => {
myEvent.remove("newListing")
})
</script>
<style scoped lang="less">

View File

@@ -1,27 +1,23 @@
<template>
<div class="seller-header">
<div class="back" @click="() => router.back()">
<div class="back" v-if="displayBack" @click="() => router.back()">
<svg-icon name="seller-back" size="24" />
</div>
<div class="content">
<span class="title" v-show="title">{{ title }}</span>
<span class="tip" v-show="tip">{{ tip }}</span>
<div class="breadcrumbs" v-show="breadcrumbs.length > 0">
<template v-for="(v, i) in breadcrumbs" :key="i">
<span class="title" v-show="displayTitle">{{ displayTitle }}</span>
<span class="tip" v-show="displayTip">{{ displayTip }}</span>
<div class="breadcrumbs" v-show="breadcrumbList.length > 1">
<template v-for="(v, i) in breadcrumbList" :key="`${v.title}-${i}`">
<span
class="title"
:class="{
last: i === breadcrumbs.length - 1
last: i === breadcrumbList.length - 1,
clickable: i < breadcrumbList.length - 1
}"
@click="
() => {
const index = -(breadcrumbs.length - i - 1)
if (index < 0) router.go(index)
}
"
>{{ v.title }}</span
@click="onBreadcrumbClick(v, i)"
>{{ $t(v.title) }}</span
>
<span class="icon" v-show="i < breadcrumbs.length - 1">
<span class="icon" v-show="i < breadcrumbList.length - 1">
<svg-icon name="seller-arrow_right_solid" size="10" />
</span>
</template>
@@ -33,25 +29,177 @@
</div>
</template>
<script setup>
import { ref, computed } from "vue"
<script setup lang="ts">
import { computed } from "vue"
import { useI18n } from "vue-i18n"
import { useRoute, useRouter } from "vue-router"
const props = defineProps({
title: {
type: String,
default: ""
},
tip: {
type: String,
default: ""
},
breadcrumbs: {
type: Array, // { title: string, name: string }
default: () => []
import type { RouteLocationNormalizedLoaded, RouteLocationRaw } from "vue-router"
type RouteMetaValue<T> = T | ((route: RouteLocationNormalizedLoaded) => T)
type SellerBreadcrumbSource =
| string
| {
title?: RouteMetaValue<string>
titleKey?: RouteMetaValue<string>
to?: RouteMetaValue<RouteLocationRaw | undefined>
name?: string
path?: string
}
type SellerBreadcrumbItem = {
title: string
to?: RouteLocationRaw
}
type SellerBreadcrumbsSource = RouteMetaValue<SellerBreadcrumbSource[]>
const props = withDefaults(
defineProps<{
title?: string
tip?: string
displayBack?: boolean
breadcrumbs?: SellerBreadcrumbSource[]
}>(),
{
title: "",
tip: "",
breadcrumbs: () => [],
displayBack: true
}
})
)
const route = useRoute()
const router = useRouter()
const { t } = useI18n()
const resolveMetaValue = <T,>(value?: RouteMetaValue<T>) => {
if (typeof value === "function") {
return (value as (route: RouteLocationNormalizedLoaded) => T)(route)
}
return value
}
const resolveRouteLocation = (to?: RouteLocationRaw) => {
if (!to || typeof to === "string") return to
const location = to as any
if (location.name) {
return {
...location,
params: {
...route.params,
...location.params
}
}
}
return to
}
const resolveBreadcrumb = (source: SellerBreadcrumbSource) => {
if (typeof source === "string") {
return {
title: source
}
}
const titleKey = resolveMetaValue(source.titleKey)
const title = titleKey ? t(titleKey) : resolveMetaValue(source.title)
const to = resolveRouteLocation(resolveMetaValue(source.to))
const fallbackTo = source.name ? { name: source.name } : source.path
return {
title: title || "",
to: resolveRouteLocation(to || fallbackTo)
}
}
const resolveTitle = (title?: RouteMetaValue<string>, titleKey?: RouteMetaValue<string>) => {
let key = title || titleKey
// const key = resolveMetaValue(titleKey)
return key ? t(key) || "" : ""
}
const autoBreadcrumbs = computed(() => {
const currentRecord = route.matched[route.matched.length - 1]
const customBreadcrumbs = resolveMetaValue(
currentRecord?.meta?.sellerBreadcrumbs as SellerBreadcrumbsSource | undefined
)
if (Array.isArray(customBreadcrumbs)) {
return customBreadcrumbs
.map((breadcrumb) => resolveBreadcrumb(breadcrumb as SellerBreadcrumbSource))
.filter((breadcrumb) => breadcrumb.title)
}
return route.matched
.map((record) => record.meta?.sellerBreadcrumb)
.filter(
(breadcrumb): breadcrumb is SellerBreadcrumbSource =>
typeof breadcrumb === "string" ||
(typeof breadcrumb === "object" && breadcrumb !== null)
)
.map(resolveBreadcrumb)
.filter((breadcrumb) => breadcrumb.title)
})
const breadcrumbList = computed<SellerBreadcrumbItem[]>(() => {
if (props.breadcrumbs.length) {
return props.breadcrumbs
.map(resolveBreadcrumb)
.filter((breadcrumb) => breadcrumb.title)
}
return autoBreadcrumbs.value
})
const displayTitle = computed(() => {
return (
props.title ||
resolveTitle(
route.meta.sellerHeaderTitle as RouteMetaValue<string> | undefined,
route.meta.sellerHeaderTitleKey as RouteMetaValue<string> | undefined
) ||
breadcrumbList.value[breadcrumbList.value.length - 1]?.title ||
""
)
})
const displayTip = computed(() => {
return (
props.tip ||
resolveTitle(
route.meta.sellerHeaderTip as RouteMetaValue<string> | undefined,
route.meta.sellerHeaderTipKey as RouteMetaValue<string> | undefined
)
)
})
const onBreadcrumbClick = (breadcrumb: SellerBreadcrumbItem, index: number) => {
if (index >= breadcrumbList.value.length - 1) return
const historyIndex = -(breadcrumbList.value.length - index - 1)
if (canUseBreadcrumbHistory(Math.abs(historyIndex))) {
router.go(historyIndex)
return
}
if (breadcrumb.to) {
router.replace(breadcrumb.to)
}
}
const canUseBreadcrumbHistory = (steps: number) => {
if (steps <= 0 || typeof history === "undefined") return false
const state = history.state as {
back?: unknown
position?: unknown
} | null
const backPath = state?.back
return (
typeof state?.position === "number" &&
state.position >= steps &&
typeof backPath === "string" &&
backPath.startsWith("/home/seller/myListings")
)
}
</script>
<style scoped lang="less">
.seller-header {
@@ -90,8 +238,8 @@
font-family: "pingfang_regular";
color: #999;
font-size: 1.4rem;
cursor: pointer;
&:not(.last) {
&.clickable {
cursor: pointer;
text-decoration: underline;
}
}

View File

@@ -5,6 +5,7 @@ import sellerToolTipImg2 from '@/assets/images/seller/sellerToolTip-2.png'
import sellerToolTipImg3 from '@/assets/images/seller/sellerToolTip-3.png'
import { useI18n } from 'vue-i18n'
import { useRouter } from 'vue-router'
import { Https } from '@/tool/https'
const props = defineProps({
visible: {
type: Boolean,
@@ -38,6 +39,8 @@ let data = reactive({
showAgain: false,
})
let toolTipBoxRef = ref(null)
const routerPath = ref('')
const cleardata = ()=>{
emit('update:visible', false)
@@ -45,8 +48,9 @@ const cleardata = ()=>{
}
const getStarted = ()=>{
if(data.showAgain){Https.axiosPost(Https.httpUrls.setListingPopup)}
emit('update:visible', false)
router.push({path:'/home/seller/myListings/select'})
router.push({path:routerPath.value})
}
@@ -54,7 +58,7 @@ onMounted(()=>{
})
onUnmounted(()=>{
})
defineExpose({})
defineExpose({routerPath})
const { showAgain } = toRefs(data);
</script>
<template>

View File

@@ -11,7 +11,7 @@ import { ElementPlusResolver } from "unplugin-vue-components/resolvers";
export default defineConfig(({ mode }) => {
// 加载环境变量
const env = loadEnv(mode, process.cwd(), "");
console.log(mode)
const baseURL = env.VITE_APP_BASE_URL
return {
resolve: {
alias: {
@@ -88,9 +88,16 @@ export default defineConfig(({ mode }) => {
overlay: true,
},
proxy: {
"/api": {
target: "http://192.168.1.7:5567",
"/aida": {
// target: 'https://develop.api.aida.com.hk',
target: baseURL,
rewrite: (path) => path.replace(/^\/aida/, "/aida"),
changeOrigin: true,
},
"/seller": {
// target: 'https://develop.api.aida.com.hk',
target: baseURL,
rewrite: (path) => path.replace(/^\/seller/, "/seller"),
changeOrigin: true,
},
"/xupei": {