Merge branch 'master' of https://gitee.com/yudaocode/yudao-ui-admin-vue3 into feature/im
# Conflicts: # package.json # pnpm-lock.yaml # src/router/modules/remaining.ts # vite.config.tsfeature/im
5
.env
|
|
@ -18,3 +18,8 @@ VITE_APP_DOCALERT_ENABLE=true
|
||||||
|
|
||||||
# 百度统计
|
# 百度统计
|
||||||
VITE_APP_BAIDU_CODE = a1ff8825baa73c3a78eb96aa40325abc
|
VITE_APP_BAIDU_CODE = a1ff8825baa73c3a78eb96aa40325abc
|
||||||
|
|
||||||
|
# 默认账户密码
|
||||||
|
VITE_APP_DEFAULT_LOGIN_TENANT = 芋道源码
|
||||||
|
VITE_APP_DEFAULT_LOGIN_USERNAME = admin
|
||||||
|
VITE_APP_DEFAULT_LOGIN_PASSWORD = admin123
|
||||||
|
|
|
||||||
2
.env.dev
|
|
@ -8,8 +8,6 @@ VITE_BASE_URL='http://api-dashboard.yudao.iocoder.cn'
|
||||||
|
|
||||||
# 文件上传类型:server - 后端上传, client - 前端直连上传,仅支持S3服务
|
# 文件上传类型:server - 后端上传, client - 前端直连上传,仅支持S3服务
|
||||||
VITE_UPLOAD_TYPE=server
|
VITE_UPLOAD_TYPE=server
|
||||||
# 上传路径
|
|
||||||
VITE_UPLOAD_URL='http://api-dashboard.yudao.iocoder.cn/admin-api/infra/file/upload'
|
|
||||||
|
|
||||||
# 接口地址
|
# 接口地址
|
||||||
VITE_API_URL=/admin-api
|
VITE_API_URL=/admin-api
|
||||||
|
|
|
||||||
|
|
@ -8,8 +8,6 @@ VITE_BASE_URL='http://localhost:48080'
|
||||||
|
|
||||||
# 文件上传类型:server - 后端上传, client - 前端直连上传,仅支持 S3 服务
|
# 文件上传类型:server - 后端上传, client - 前端直连上传,仅支持 S3 服务
|
||||||
VITE_UPLOAD_TYPE=server
|
VITE_UPLOAD_TYPE=server
|
||||||
# 上传路径
|
|
||||||
VITE_UPLOAD_URL='http://localhost:48080/admin-api/infra/file/upload'
|
|
||||||
|
|
||||||
# 接口地址
|
# 接口地址
|
||||||
VITE_API_URL=/admin-api
|
VITE_API_URL=/admin-api
|
||||||
|
|
|
||||||
|
|
@ -8,8 +8,6 @@ VITE_BASE_URL='http://localhost:48080'
|
||||||
|
|
||||||
# 文件上传类型:server - 后端上传, client - 前端直连上传,仅支持S3服务
|
# 文件上传类型:server - 后端上传, client - 前端直连上传,仅支持S3服务
|
||||||
VITE_UPLOAD_TYPE=server
|
VITE_UPLOAD_TYPE=server
|
||||||
# 上传路径
|
|
||||||
VITE_UPLOAD_URL='http://localhost:48080/admin-api/infra/file/upload'
|
|
||||||
|
|
||||||
# 接口地址
|
# 接口地址
|
||||||
VITE_API_URL=/admin-api
|
VITE_API_URL=/admin-api
|
||||||
|
|
|
||||||
|
|
@ -8,8 +8,6 @@ VITE_BASE_URL='http://api-dashboard.yudao.iocoder.cn'
|
||||||
|
|
||||||
# 文件上传类型:server - 后端上传, client - 前端直连上传,仅支持S3服务
|
# 文件上传类型:server - 后端上传, client - 前端直连上传,仅支持S3服务
|
||||||
VITE_UPLOAD_TYPE=server
|
VITE_UPLOAD_TYPE=server
|
||||||
# 上传路径
|
|
||||||
VITE_UPLOAD_URL='http://api-dashboard.yudao.iocoder.cn/admin-api/infra/file/upload'
|
|
||||||
|
|
||||||
# 接口地址
|
# 接口地址
|
||||||
VITE_API_URL=/admin-api
|
VITE_API_URL=/admin-api
|
||||||
|
|
|
||||||
|
|
@ -8,8 +8,6 @@ VITE_BASE_URL='http://localhost:48080'
|
||||||
|
|
||||||
# 文件上传类型:server - 后端上传, client - 前端直连上传,仅支持S3服务
|
# 文件上传类型:server - 后端上传, client - 前端直连上传,仅支持S3服务
|
||||||
VITE_UPLOAD_TYPE=server
|
VITE_UPLOAD_TYPE=server
|
||||||
# 上传路径
|
|
||||||
VITE_UPLOAD_URL='http://localhost:48080/admin-api/infra/file/upload'
|
|
||||||
|
|
||||||
# 接口地址
|
# 接口地址
|
||||||
VITE_API_URL=/admin-api
|
VITE_API_URL=/admin-api
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,6 @@ node_modules
|
||||||
.DS_Store
|
.DS_Store
|
||||||
dist
|
dist
|
||||||
dist-ssr
|
dist-ssr
|
||||||
*.local
|
|
||||||
/dist*
|
/dist*
|
||||||
pnpm-debug
|
pnpm-debug
|
||||||
auto-*.d.ts
|
auto-*.d.ts
|
||||||
|
|
|
||||||
|
After Width: | Height: | Size: 21 KiB |
|
After Width: | Height: | Size: 348 KiB |
|
|
@ -83,10 +83,11 @@
|
||||||
"editor.defaultFormatter": "esbenp.prettier-vscode"
|
"editor.defaultFormatter": "esbenp.prettier-vscode"
|
||||||
},
|
},
|
||||||
"editor.codeActionsOnSave": {
|
"editor.codeActionsOnSave": {
|
||||||
"source.fixAll.eslint": "explicit"
|
"source.fixAll.eslint": "explicit",
|
||||||
|
"source.fixAll.stylelint": "explicit"
|
||||||
},
|
},
|
||||||
"[vue]": {
|
"[vue]": {
|
||||||
"editor.defaultFormatter": "rvest.vs-code-prettier-eslint"
|
"editor.defaultFormatter": "esbenp.prettier-vscode"
|
||||||
},
|
},
|
||||||
"i18n-ally.localesPaths": ["src/locales"],
|
"i18n-ally.localesPaths": ["src/locales"],
|
||||||
"i18n-ally.keystyle": "nested",
|
"i18n-ally.keystyle": "nested",
|
||||||
|
|
|
||||||
19
README.md
|
|
@ -55,9 +55,8 @@
|
||||||
推荐 VS Code 开发,配合插件如下:
|
推荐 VS Code 开发,配合插件如下:
|
||||||
|
|
||||||
| 插件名 | 功能 |
|
| 插件名 | 功能 |
|
||||||
|-------------------------------|--------------------------|
|
|-------------------------------|---------------------|
|
||||||
| TypeScript Vue Plugin (Volar) | 用于 TypeScript 的 Vue 插件 |
|
| Vue - Official | Vue 与 TypeScript 支持 |
|
||||||
| Vue Language Features (Volar) | Vue3.0 语法支持 |
|
|
||||||
| unocss | unocss for vscode |
|
| unocss | unocss for vscode |
|
||||||
| Iconify IntelliSense | Iconify 预览和搜索 |
|
| Iconify IntelliSense | Iconify 预览和搜索 |
|
||||||
| i18n Ally | 国际化智能提示 |
|
| i18n Ally | 国际化智能提示 |
|
||||||
|
|
@ -192,26 +191,24 @@ ps:核心功能已经实现,正在对接微信小程序中...
|
||||||
|
|
||||||
### 商城系统
|
### 商城系统
|
||||||
|
|
||||||
|
演示地址:<https://doc.iocoder.cn/mall-preview/>
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
_前端基于 crmeb uniapp 经过授权重构,优化代码实现,接入芋道快速开发平台_
|
|
||||||
|
|
||||||
演示地址:<https://doc.iocoder.cn/mall-preview/>
|
|
||||||
|
|
||||||
### ERP 系统
|
### ERP 系统
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
演示地址:<https://doc.iocoder.cn/erp-preview/>
|
演示地址:<https://doc.iocoder.cn/erp-preview/>
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
### CRM 系统
|
### CRM 系统
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
演示地址:<https://doc.iocoder.cn/crm-preview/>
|
演示地址:<https://doc.iocoder.cn/crm-preview/>
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
## 🐷 演示图
|
## 🐷 演示图
|
||||||
|
|
||||||
### 系统功能
|
### 系统功能
|
||||||
|
|
|
||||||
|
|
@ -27,6 +27,12 @@ const include = [
|
||||||
'echarts-wordcloud',
|
'echarts-wordcloud',
|
||||||
'@wangeditor/editor',
|
'@wangeditor/editor',
|
||||||
'@wangeditor/editor-for-vue',
|
'@wangeditor/editor-for-vue',
|
||||||
|
'@microsoft/fetch-event-source',
|
||||||
|
'markdown-it',
|
||||||
|
'markmap-view',
|
||||||
|
'markmap-lib',
|
||||||
|
'markmap-toolbar',
|
||||||
|
'highlight.js',
|
||||||
'element-plus',
|
'element-plus',
|
||||||
'element-plus/es',
|
'element-plus/es',
|
||||||
'element-plus/es/locale/lang/zh-cn',
|
'element-plus/es/locale/lang/zh-cn',
|
||||||
|
|
@ -104,7 +110,11 @@ const include = [
|
||||||
'element-plus/es/components/collapse/style/css',
|
'element-plus/es/components/collapse/style/css',
|
||||||
'element-plus/es/components/collapse-item/style/css',
|
'element-plus/es/components/collapse-item/style/css',
|
||||||
'element-plus/es/components/button-group/style/css',
|
'element-plus/es/components/button-group/style/css',
|
||||||
'element-plus/es/components/text/style/css'
|
'element-plus/es/components/text/style/css',
|
||||||
|
'element-plus/es/components/segmented/style/css',
|
||||||
|
'@element-plus/icons-vue',
|
||||||
|
'element-plus/es/components/footer/style/css',
|
||||||
|
'element-plus/es/components/empty/style/css'
|
||||||
]
|
]
|
||||||
|
|
||||||
const exclude = ['@iconify/json']
|
const exclude = ['@iconify/json']
|
||||||
|
|
|
||||||
34
package.json
|
|
@ -1,22 +1,22 @@
|
||||||
{
|
{
|
||||||
"name": "yudao-ui-admin-vue3",
|
"name": "yudao-ui-admin-vue3",
|
||||||
"version": "2.1.0-snapshot",
|
"version": "2.3.0-snapshot",
|
||||||
"description": "基于vue3、vite4、element-plus、typesScript",
|
"description": "基于vue3、vite4、element-plus、typesScript",
|
||||||
"author": "xingyu",
|
"author": "xingyu",
|
||||||
"private": false,
|
"private": false,
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"i": "pnpm install",
|
"i": "pnpm install",
|
||||||
"dev": "vite",
|
"dev": "vite --mode env.local",
|
||||||
"dev-server": "vite --mode dev",
|
"dev-server": "vite --mode dev",
|
||||||
"ts:check": "vue-tsc --noEmit",
|
"ts:check": "vue-tsc --noEmit",
|
||||||
"build:local": "node --max_old_space_size=8192 ./node_modules/vite/bin/vite.js build",
|
"build:local": "node ./node_modules/vite/bin/vite.js build",
|
||||||
"build:dev": "node --max_old_space_size=8192 ./node_modules/vite/bin/vite.js build --mode local-dev",
|
"build:dev": "node ./node_modules/vite/bin/vite.js build --mode dev",
|
||||||
"build:test": "node --max_old_space_size=8192 ./node_modules/vite/bin/vite.js build --mode test",
|
"build:test": "node ./node_modules/vite/bin/vite.js build --mode test",
|
||||||
"build:stage": "node --max_old_space_size=8192 ./node_modules/vite/bin/vite.js build --mode stage",
|
"build:stage": "node ./node_modules/vite/bin/vite.js build --mode stage",
|
||||||
"build:prod": "node --max_old_space_size=8192 ./node_modules/vite/bin/vite.js build --mode prod",
|
"build:prod": "node ./node_modules/vite/bin/vite.js build --mode prod",
|
||||||
"serve:dev": "vite preview --mode dev",
|
"serve:dev": "vite preview --mode dev",
|
||||||
"serve:prod": "vite preview --mode prod",
|
"serve:prod": "vite preview --mode prod",
|
||||||
"preview": "pnpm build:local-dev && vite preview",
|
"preview": "pnpm build:local && vite preview",
|
||||||
"clean": "npx rimraf node_modules",
|
"clean": "npx rimraf node_modules",
|
||||||
"clean:cache": "npx rimraf node_modules/.cache",
|
"clean:cache": "npx rimraf node_modules/.cache",
|
||||||
"lint:eslint": "eslint --fix --ext .js,.ts,.vue ./src",
|
"lint:eslint": "eslint --fix --ext .js,.ts,.vue ./src",
|
||||||
|
|
@ -26,9 +26,10 @@
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@element-plus/icons-vue": "^2.1.0",
|
"@element-plus/icons-vue": "^2.1.0",
|
||||||
"@form-create/designer": "^3.1.3",
|
"@form-create/designer": "^3.2.6",
|
||||||
"@form-create/element-ui": "^3.1.24",
|
"@form-create/element-ui": "^3.2.11",
|
||||||
"@iconify/iconify": "^3.1.1",
|
"@iconify/iconify": "^3.1.1",
|
||||||
|
"@microsoft/fetch-event-source": "^2.0.1",
|
||||||
"@videojs-player/vue": "^1.0.0",
|
"@videojs-player/vue": "^1.0.0",
|
||||||
"@vueuse/core": "^10.9.0",
|
"@vueuse/core": "^10.9.0",
|
||||||
"@wangeditor/editor": "^5.1.23",
|
"@wangeditor/editor": "^5.1.23",
|
||||||
|
|
@ -46,12 +47,17 @@
|
||||||
"driver.js": "^1.3.1",
|
"driver.js": "^1.3.1",
|
||||||
"echarts": "^5.5.0",
|
"echarts": "^5.5.0",
|
||||||
"echarts-wordcloud": "^2.1.0",
|
"echarts-wordcloud": "^2.1.0",
|
||||||
"element-plus": "2.6.1",
|
"element-plus": "2.8.4",
|
||||||
"fast-xml-parser": "^4.3.2",
|
"fast-xml-parser": "^4.3.2",
|
||||||
"highlight.js": "^11.9.0",
|
"highlight.js": "^11.9.0",
|
||||||
"jsencrypt": "^3.3.2",
|
"jsencrypt": "^3.3.2",
|
||||||
"lodash": "^4.17.21",
|
"lodash": "^4.17.21",
|
||||||
"lodash-es": "^4.17.21",
|
"lodash-es": "^4.17.21",
|
||||||
|
"markdown-it": "^14.1.0",
|
||||||
|
"markmap-common": "^0.16.0",
|
||||||
|
"markmap-lib": "^0.16.1",
|
||||||
|
"markmap-toolbar": "^0.17.0",
|
||||||
|
"markmap-view": "^0.16.0",
|
||||||
"min-dash": "^4.1.1",
|
"min-dash": "^4.1.1",
|
||||||
"mitt": "^3.0.1",
|
"mitt": "^3.0.1",
|
||||||
"nprogress": "^0.2.0",
|
"nprogress": "^0.2.0",
|
||||||
|
|
@ -62,7 +68,7 @@
|
||||||
"steady-xml": "^0.1.0",
|
"steady-xml": "^0.1.0",
|
||||||
"url": "^0.11.3",
|
"url": "^0.11.3",
|
||||||
"video.js": "^7.21.5",
|
"video.js": "^7.21.5",
|
||||||
"vue": "3.4.21",
|
"vue": "3.5.12",
|
||||||
"vue-at": "3.0.0-alpha.2",
|
"vue-at": "3.0.0-alpha.2",
|
||||||
"vue-dompurify-html": "^4.1.4",
|
"vue-dompurify-html": "^4.1.4",
|
||||||
"vue-i18n": "9.10.2",
|
"vue-i18n": "9.10.2",
|
||||||
|
|
@ -85,8 +91,8 @@
|
||||||
"@types/qs": "^6.9.12",
|
"@types/qs": "^6.9.12",
|
||||||
"@typescript-eslint/eslint-plugin": "^7.1.0",
|
"@typescript-eslint/eslint-plugin": "^7.1.0",
|
||||||
"@typescript-eslint/parser": "^7.1.0",
|
"@typescript-eslint/parser": "^7.1.0",
|
||||||
"@unocss/transformer-variant-group": "^0.58.5",
|
|
||||||
"@unocss/eslint-config": "^0.57.4",
|
"@unocss/eslint-config": "^0.57.4",
|
||||||
|
"@unocss/transformer-variant-group": "^0.58.5",
|
||||||
"@vitejs/plugin-legacy": "^5.3.1",
|
"@vitejs/plugin-legacy": "^5.3.1",
|
||||||
"@vitejs/plugin-vue": "^5.0.4",
|
"@vitejs/plugin-vue": "^5.0.4",
|
||||||
"@vitejs/plugin-vue-jsx": "^3.1.0",
|
"@vitejs/plugin-vue-jsx": "^3.1.0",
|
||||||
|
|
@ -126,7 +132,7 @@
|
||||||
"vite-plugin-progress": "^0.0.7",
|
"vite-plugin-progress": "^0.0.7",
|
||||||
"vite-plugin-purge-icons": "^0.10.0",
|
"vite-plugin-purge-icons": "^0.10.0",
|
||||||
"vite-plugin-svg-icons": "^2.0.1",
|
"vite-plugin-svg-icons": "^2.0.1",
|
||||||
"vite-plugin-top-level-await": "^1.3.1",
|
"vite-plugin-top-level-await": "^1.4.4",
|
||||||
"vue-eslint-parser": "^9.3.2",
|
"vue-eslint-parser": "^9.3.2",
|
||||||
"vue-tsc": "^1.8.27"
|
"vue-tsc": "^1.8.27"
|
||||||
},
|
},
|
||||||
|
|
|
||||||
3558
pnpm-lock.yaml
|
|
@ -0,0 +1,65 @@
|
||||||
|
import request from '@/config/axios'
|
||||||
|
|
||||||
|
// AI 聊天对话 VO
|
||||||
|
export interface ChatConversationVO {
|
||||||
|
id: number // ID 编号
|
||||||
|
userId: number // 用户编号
|
||||||
|
title: string // 对话标题
|
||||||
|
pinned: boolean // 是否置顶
|
||||||
|
roleId: number // 角色编号
|
||||||
|
modelId: number // 模型编号
|
||||||
|
model: string // 模型标志
|
||||||
|
temperature: number // 温度参数
|
||||||
|
maxTokens: number // 单条回复的最大 Token 数量
|
||||||
|
maxContexts: number // 上下文的最大 Message 数量
|
||||||
|
createTime?: Date // 创建时间
|
||||||
|
// 额外字段
|
||||||
|
systemMessage?: string // 角色设定
|
||||||
|
modelName?: string // 模型名字
|
||||||
|
roleAvatar?: string // 角色头像
|
||||||
|
modelMaxTokens?: string // 模型的单条回复的最大 Token 数量
|
||||||
|
modelMaxContexts?: string // 模型的上下文的最大 Message 数量
|
||||||
|
}
|
||||||
|
|
||||||
|
// AI 聊天对话 API
|
||||||
|
export const ChatConversationApi = {
|
||||||
|
// 获得【我的】聊天对话
|
||||||
|
getChatConversationMy: async (id: number) => {
|
||||||
|
return await request.get({ url: `/ai/chat/conversation/get-my?id=${id}` })
|
||||||
|
},
|
||||||
|
|
||||||
|
// 新增【我的】聊天对话
|
||||||
|
createChatConversationMy: async (data?: ChatConversationVO) => {
|
||||||
|
return await request.post({ url: `/ai/chat/conversation/create-my`, data })
|
||||||
|
},
|
||||||
|
|
||||||
|
// 更新【我的】聊天对话
|
||||||
|
updateChatConversationMy: async (data: ChatConversationVO) => {
|
||||||
|
return await request.put({ url: `/ai/chat/conversation/update-my`, data })
|
||||||
|
},
|
||||||
|
|
||||||
|
// 删除【我的】聊天对话
|
||||||
|
deleteChatConversationMy: async (id: string) => {
|
||||||
|
return await request.delete({ url: `/ai/chat/conversation/delete-my?id=${id}` })
|
||||||
|
},
|
||||||
|
|
||||||
|
// 删除【我的】所有对话,置顶除外
|
||||||
|
deleteChatConversationMyByUnpinned: async () => {
|
||||||
|
return await request.delete({ url: `/ai/chat/conversation/delete-by-unpinned` })
|
||||||
|
},
|
||||||
|
|
||||||
|
// 获得【我的】聊天对话列表
|
||||||
|
getChatConversationMyList: async () => {
|
||||||
|
return await request.get({ url: `/ai/chat/conversation/my-list` })
|
||||||
|
},
|
||||||
|
|
||||||
|
// 获得对话分页
|
||||||
|
getChatConversationPage: async (params: any) => {
|
||||||
|
return await request.get({ url: `/ai/chat/conversation/page`, params })
|
||||||
|
},
|
||||||
|
|
||||||
|
// 管理员删除消息
|
||||||
|
deleteChatConversationByAdmin: async (id: number) => {
|
||||||
|
return await request.delete({ url: `/ai/chat/conversation/delete-by-admin?id=${id}` })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,83 @@
|
||||||
|
import request from '@/config/axios'
|
||||||
|
import { fetchEventSource } from '@microsoft/fetch-event-source'
|
||||||
|
import { getAccessToken } from '@/utils/auth'
|
||||||
|
import { config } from '@/config/axios/config'
|
||||||
|
|
||||||
|
// 聊天VO
|
||||||
|
export interface ChatMessageVO {
|
||||||
|
id: number // 编号
|
||||||
|
conversationId: number // 对话编号
|
||||||
|
type: string // 消息类型
|
||||||
|
userId: string // 用户编号
|
||||||
|
roleId: string // 角色编号
|
||||||
|
model: number // 模型标志
|
||||||
|
modelId: number // 模型编号
|
||||||
|
content: string // 聊天内容
|
||||||
|
tokens: number // 消耗 Token 数量
|
||||||
|
createTime: Date // 创建时间
|
||||||
|
roleAvatar: string // 角色头像
|
||||||
|
userAvatar: string // 创建时间
|
||||||
|
}
|
||||||
|
|
||||||
|
// AI chat 聊天
|
||||||
|
export const ChatMessageApi = {
|
||||||
|
// 消息列表
|
||||||
|
getChatMessageListByConversationId: async (conversationId: number | null) => {
|
||||||
|
return await request.get({
|
||||||
|
url: `/ai/chat/message/list-by-conversation-id?conversationId=${conversationId}`
|
||||||
|
})
|
||||||
|
},
|
||||||
|
|
||||||
|
// 发送 Stream 消息
|
||||||
|
// 为什么不用 axios 呢?因为它不支持 SSE 调用
|
||||||
|
sendChatMessageStream: async (
|
||||||
|
conversationId: number,
|
||||||
|
content: string,
|
||||||
|
ctrl,
|
||||||
|
enableContext: boolean,
|
||||||
|
onMessage,
|
||||||
|
onError,
|
||||||
|
onClose
|
||||||
|
) => {
|
||||||
|
const token = getAccessToken()
|
||||||
|
return fetchEventSource(`${config.base_url}/ai/chat/message/send-stream`, {
|
||||||
|
method: 'post',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
Authorization: `Bearer ${token}`
|
||||||
|
},
|
||||||
|
openWhenHidden: true,
|
||||||
|
body: JSON.stringify({
|
||||||
|
conversationId,
|
||||||
|
content,
|
||||||
|
useContext: enableContext
|
||||||
|
}),
|
||||||
|
onmessage: onMessage,
|
||||||
|
onerror: onError,
|
||||||
|
onclose: onClose,
|
||||||
|
signal: ctrl.signal
|
||||||
|
})
|
||||||
|
},
|
||||||
|
|
||||||
|
// 删除消息
|
||||||
|
deleteChatMessage: async (id: string) => {
|
||||||
|
return await request.delete({ url: `/ai/chat/message/delete?id=${id}` })
|
||||||
|
},
|
||||||
|
|
||||||
|
// 删除指定对话的消息
|
||||||
|
deleteByConversationId: async (conversationId: number) => {
|
||||||
|
return await request.delete({
|
||||||
|
url: `/ai/chat/message/delete-by-conversation-id?conversationId=${conversationId}`
|
||||||
|
})
|
||||||
|
},
|
||||||
|
|
||||||
|
// 获得消息分页
|
||||||
|
getChatMessagePage: async (params: any) => {
|
||||||
|
return await request.get({ url: '/ai/chat/message/page', params })
|
||||||
|
},
|
||||||
|
|
||||||
|
// 管理员删除消息
|
||||||
|
deleteChatMessageByAdmin: async (id: number) => {
|
||||||
|
return await request.delete({ url: `/ai/chat/message/delete-by-admin?id=${id}` })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,103 @@
|
||||||
|
import request from '@/config/axios'
|
||||||
|
|
||||||
|
// AI 绘图 VO
|
||||||
|
export interface ImageVO {
|
||||||
|
id: number // 编号
|
||||||
|
platform: string // 平台
|
||||||
|
model: string // 模型
|
||||||
|
prompt: string // 提示词
|
||||||
|
width: number // 图片宽度
|
||||||
|
height: number // 图片高度
|
||||||
|
status: number // 状态
|
||||||
|
publicStatus: boolean // 公开状态
|
||||||
|
picUrl: string // 任务地址
|
||||||
|
errorMessage: string // 错误信息
|
||||||
|
options: any // 配置 Map<string, string>
|
||||||
|
taskId: number // 任务编号
|
||||||
|
buttons: ImageMidjourneyButtonsVO[] // mj 操作按钮
|
||||||
|
createTime: Date // 创建时间
|
||||||
|
finishTime: Date // 完成时间
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ImageDrawReqVO {
|
||||||
|
platform: string // 平台
|
||||||
|
prompt: string // 提示词
|
||||||
|
model: string // 模型
|
||||||
|
style: string // 图像生成的风格
|
||||||
|
width: string // 图片宽度
|
||||||
|
height: string // 图片高度
|
||||||
|
options: object // 绘制参数,Map<String, String>
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ImageMidjourneyImagineReqVO {
|
||||||
|
prompt: string // 提示词
|
||||||
|
model: string // 模型 mj nijj
|
||||||
|
base64Array: string[] // size不能为空
|
||||||
|
width: string // 图片宽度
|
||||||
|
height: string // 图片高度
|
||||||
|
version: string // 版本
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ImageMidjourneyActionVO {
|
||||||
|
id: number // 图片编号
|
||||||
|
customId: string // MJ::JOB::upsample::1::85a4b4c1-8835-46c5-a15c-aea34fad1862 动作标识
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ImageMidjourneyButtonsVO {
|
||||||
|
customId: string // MJ::JOB::upsample::1::85a4b4c1-8835-46c5-a15c-aea34fad1862 动作标识
|
||||||
|
emoji: string // 图标 emoji
|
||||||
|
label: string // Make Variations 文本
|
||||||
|
style: number // 样式: 2(Primary)、3(Green)
|
||||||
|
}
|
||||||
|
|
||||||
|
// AI 图片 API
|
||||||
|
export const ImageApi = {
|
||||||
|
// 获取【我的】绘图分页
|
||||||
|
getImagePageMy: async (params: any) => {
|
||||||
|
return await request.get({ url: `/ai/image/my-page`, params })
|
||||||
|
},
|
||||||
|
// 获取【我的】绘图记录
|
||||||
|
getImageMy: async (id: number) => {
|
||||||
|
return await request.get({ url: `/ai/image/get-my?id=${id}` })
|
||||||
|
},
|
||||||
|
// 获取【我的】绘图记录列表
|
||||||
|
getImageListMyByIds: async (ids: number[]) => {
|
||||||
|
return await request.get({ url: `/ai/image/my-list-by-ids`, params: { ids: ids.join(',') } })
|
||||||
|
},
|
||||||
|
// 生成图片
|
||||||
|
drawImage: async (data: ImageDrawReqVO) => {
|
||||||
|
return await request.post({ url: `/ai/image/draw`, data })
|
||||||
|
},
|
||||||
|
// 删除【我的】绘画记录
|
||||||
|
deleteImageMy: async (id: number) => {
|
||||||
|
return await request.delete({ url: `/ai/image/delete-my?id=${id}` })
|
||||||
|
},
|
||||||
|
|
||||||
|
// ================ midjourney 专属 ================
|
||||||
|
|
||||||
|
// 【Midjourney】生成图片
|
||||||
|
midjourneyImagine: async (data: ImageMidjourneyImagineReqVO) => {
|
||||||
|
return await request.post({ url: `/ai/image/midjourney/imagine`, data })
|
||||||
|
},
|
||||||
|
// 【Midjourney】Action 操作(二次生成图片)
|
||||||
|
midjourneyAction: async (data: ImageMidjourneyActionVO) => {
|
||||||
|
return await request.post({ url: `/ai/image/midjourney/action`, data })
|
||||||
|
},
|
||||||
|
|
||||||
|
// ================ 绘图管理 ================
|
||||||
|
|
||||||
|
// 查询绘画分页
|
||||||
|
getImagePage: async (params: any) => {
|
||||||
|
return await request.get({ url: `/ai/image/page`, params })
|
||||||
|
},
|
||||||
|
|
||||||
|
// 更新绘画发布状态
|
||||||
|
updateImage: async (data: any) => {
|
||||||
|
return await request.put({ url: '/ai/image/update', data })
|
||||||
|
},
|
||||||
|
|
||||||
|
// 删除绘画
|
||||||
|
deleteImage: async (id: number) => {
|
||||||
|
return await request.delete({ url: `/ai/image/delete?id=` + id })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,60 @@
|
||||||
|
import { getAccessToken } from '@/utils/auth'
|
||||||
|
import { fetchEventSource } from '@microsoft/fetch-event-source'
|
||||||
|
import { config } from '@/config/axios/config'
|
||||||
|
import request from '@/config/axios' // AI 思维导图 VO
|
||||||
|
|
||||||
|
// AI 思维导图 VO
|
||||||
|
export interface MindMapVO {
|
||||||
|
id: number // 编号
|
||||||
|
userId: number // 用户编号
|
||||||
|
prompt: string // 生成内容提示
|
||||||
|
generatedContent: string // 生成的思维导图内容
|
||||||
|
platform: string // 平台
|
||||||
|
model: string // 模型
|
||||||
|
errorMessage: string // 错误信息
|
||||||
|
}
|
||||||
|
|
||||||
|
// AI 思维导图生成 VO
|
||||||
|
export interface AiMindMapGenerateReqVO {
|
||||||
|
prompt: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export const AiMindMapApi = {
|
||||||
|
generateMindMap: ({
|
||||||
|
data,
|
||||||
|
onClose,
|
||||||
|
onMessage,
|
||||||
|
onError,
|
||||||
|
ctrl
|
||||||
|
}: {
|
||||||
|
data: AiMindMapGenerateReqVO
|
||||||
|
onMessage?: (res: any) => void
|
||||||
|
onError?: (...args: any[]) => void
|
||||||
|
onClose?: (...args: any[]) => void
|
||||||
|
ctrl: AbortController
|
||||||
|
}) => {
|
||||||
|
const token = getAccessToken()
|
||||||
|
return fetchEventSource(`${config.base_url}/ai/mind-map/generate-stream`, {
|
||||||
|
method: 'post',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
Authorization: `Bearer ${token}`
|
||||||
|
},
|
||||||
|
openWhenHidden: true,
|
||||||
|
body: JSON.stringify(data),
|
||||||
|
onmessage: onMessage,
|
||||||
|
onerror: onError,
|
||||||
|
onclose: onClose,
|
||||||
|
signal: ctrl.signal
|
||||||
|
})
|
||||||
|
},
|
||||||
|
|
||||||
|
// 查询思维导图分页
|
||||||
|
getMindMapPage: async (params: any) => {
|
||||||
|
return await request.get({ url: `/ai/mind-map/page`, params })
|
||||||
|
},
|
||||||
|
// 删除思维导图
|
||||||
|
deleteMindMap: async (id: number) => {
|
||||||
|
return await request.delete({ url: `/ai/mind-map/delete?id=` + id })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,44 @@
|
||||||
|
import request from '@/config/axios'
|
||||||
|
|
||||||
|
// AI API 密钥 VO
|
||||||
|
export interface ApiKeyVO {
|
||||||
|
id: number // 编号
|
||||||
|
name: string // 名称
|
||||||
|
apiKey: string // 密钥
|
||||||
|
platform: string // 平台
|
||||||
|
url: string // 自定义 API 地址
|
||||||
|
status: number // 状态
|
||||||
|
}
|
||||||
|
|
||||||
|
// AI API 密钥 API
|
||||||
|
export const ApiKeyApi = {
|
||||||
|
// 查询 API 密钥分页
|
||||||
|
getApiKeyPage: async (params: any) => {
|
||||||
|
return await request.get({ url: `/ai/api-key/page`, params })
|
||||||
|
},
|
||||||
|
|
||||||
|
// 获得 API 密钥列表
|
||||||
|
getApiKeySimpleList: async () => {
|
||||||
|
return await request.get({ url: `/ai/api-key/simple-list` })
|
||||||
|
},
|
||||||
|
|
||||||
|
// 查询 API 密钥详情
|
||||||
|
getApiKey: async (id: number) => {
|
||||||
|
return await request.get({ url: `/ai/api-key/get?id=` + id })
|
||||||
|
},
|
||||||
|
|
||||||
|
// 新增 API 密钥
|
||||||
|
createApiKey: async (data: ApiKeyVO) => {
|
||||||
|
return await request.post({ url: `/ai/api-key/create`, data })
|
||||||
|
},
|
||||||
|
|
||||||
|
// 修改 API 密钥
|
||||||
|
updateApiKey: async (data: ApiKeyVO) => {
|
||||||
|
return await request.put({ url: `/ai/api-key/update`, data })
|
||||||
|
},
|
||||||
|
|
||||||
|
// 删除 API 密钥
|
||||||
|
deleteApiKey: async (id: number) => {
|
||||||
|
return await request.delete({ url: `/ai/api-key/delete?id=` + id })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,53 @@
|
||||||
|
import request from '@/config/axios'
|
||||||
|
|
||||||
|
// AI 聊天模型 VO
|
||||||
|
export interface ChatModelVO {
|
||||||
|
id: number // 编号
|
||||||
|
keyId: number // API 秘钥编号
|
||||||
|
name: string // 模型名字
|
||||||
|
model: string // 模型标识
|
||||||
|
platform: string // 模型平台
|
||||||
|
sort: number // 排序
|
||||||
|
status: number // 状态
|
||||||
|
temperature: number // 温度参数
|
||||||
|
maxTokens: number // 单条回复的最大 Token 数量
|
||||||
|
maxContexts: number // 上下文的最大 Message 数量
|
||||||
|
}
|
||||||
|
|
||||||
|
// AI 聊天模型 API
|
||||||
|
export const ChatModelApi = {
|
||||||
|
// 查询聊天模型分页
|
||||||
|
getChatModelPage: async (params: any) => {
|
||||||
|
return await request.get({ url: `/ai/chat-model/page`, params })
|
||||||
|
},
|
||||||
|
|
||||||
|
// 获得聊天模型列表
|
||||||
|
getChatModelSimpleList: async (status?: number) => {
|
||||||
|
return await request.get({
|
||||||
|
url: `/ai/chat-model/simple-list`,
|
||||||
|
params: {
|
||||||
|
status
|
||||||
|
}
|
||||||
|
})
|
||||||
|
},
|
||||||
|
|
||||||
|
// 查询聊天模型详情
|
||||||
|
getChatModel: async (id: number) => {
|
||||||
|
return await request.get({ url: `/ai/chat-model/get?id=` + id })
|
||||||
|
},
|
||||||
|
|
||||||
|
// 新增聊天模型
|
||||||
|
createChatModel: async (data: ChatModelVO) => {
|
||||||
|
return await request.post({ url: `/ai/chat-model/create`, data })
|
||||||
|
},
|
||||||
|
|
||||||
|
// 修改聊天模型
|
||||||
|
updateChatModel: async (data: ChatModelVO) => {
|
||||||
|
return await request.put({ url: `/ai/chat-model/update`, data })
|
||||||
|
},
|
||||||
|
|
||||||
|
// 删除聊天模型
|
||||||
|
deleteChatModel: async (id: number) => {
|
||||||
|
return await request.delete({ url: `/ai/chat-model/delete?id=` + id })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,80 @@
|
||||||
|
import request from '@/config/axios'
|
||||||
|
|
||||||
|
// AI 聊天角色 VO
|
||||||
|
export interface ChatRoleVO {
|
||||||
|
id: number // 角色编号
|
||||||
|
modelId: number // 模型编号
|
||||||
|
name: string // 角色名称
|
||||||
|
avatar: string // 角色头像
|
||||||
|
category: string // 角色类别
|
||||||
|
sort: number // 角色排序
|
||||||
|
description: string // 角色描述
|
||||||
|
systemMessage: string // 角色设定
|
||||||
|
welcomeMessage: string // 角色设定
|
||||||
|
publicStatus: boolean // 是否公开
|
||||||
|
status: number // 状态
|
||||||
|
}
|
||||||
|
|
||||||
|
// AI 聊天角色 分页请求 vo
|
||||||
|
export interface ChatRolePageReqVO {
|
||||||
|
name?: string // 角色名称
|
||||||
|
category?: string // 角色类别
|
||||||
|
publicStatus: boolean // 是否公开
|
||||||
|
pageNo: number // 是否公开
|
||||||
|
pageSize: number // 是否公开
|
||||||
|
}
|
||||||
|
|
||||||
|
// AI 聊天角色 API
|
||||||
|
export const ChatRoleApi = {
|
||||||
|
// 查询聊天角色分页
|
||||||
|
getChatRolePage: async (params: any) => {
|
||||||
|
return await request.get({ url: `/ai/chat-role/page`, params })
|
||||||
|
},
|
||||||
|
|
||||||
|
// 查询聊天角色详情
|
||||||
|
getChatRole: async (id: number) => {
|
||||||
|
return await request.get({ url: `/ai/chat-role/get?id=` + id })
|
||||||
|
},
|
||||||
|
|
||||||
|
// 新增聊天角色
|
||||||
|
createChatRole: async (data: ChatRoleVO) => {
|
||||||
|
return await request.post({ url: `/ai/chat-role/create`, data })
|
||||||
|
},
|
||||||
|
|
||||||
|
// 修改聊天角色
|
||||||
|
updateChatRole: async (data: ChatRoleVO) => {
|
||||||
|
return await request.put({ url: `/ai/chat-role/update`, data })
|
||||||
|
},
|
||||||
|
|
||||||
|
// 删除聊天角色
|
||||||
|
deleteChatRole: async (id: number) => {
|
||||||
|
return await request.delete({ url: `/ai/chat-role/delete?id=` + id })
|
||||||
|
},
|
||||||
|
|
||||||
|
// ======= chat 聊天
|
||||||
|
|
||||||
|
// 获取 my role
|
||||||
|
getMyPage: async (params: ChatRolePageReqVO) => {
|
||||||
|
return await request.get({ url: `/ai/chat-role/my-page`, params})
|
||||||
|
},
|
||||||
|
|
||||||
|
// 获取角色分类
|
||||||
|
getCategoryList: async () => {
|
||||||
|
return await request.get({ url: `/ai/chat-role/category-list`})
|
||||||
|
},
|
||||||
|
|
||||||
|
// 创建角色
|
||||||
|
createMy: async (data: ChatRoleVO) => {
|
||||||
|
return await request.post({ url: `/ai/chat-role/create-my`, data})
|
||||||
|
},
|
||||||
|
|
||||||
|
// 更新角色
|
||||||
|
updateMy: async (data: ChatRoleVO) => {
|
||||||
|
return await request.put({ url: `/ai/chat-role/update-my`, data})
|
||||||
|
},
|
||||||
|
|
||||||
|
// 删除角色 my
|
||||||
|
deleteMy: async (id: number) => {
|
||||||
|
return await request.delete({ url: `/ai/chat-role/delete-my?id=` + id })
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,41 @@
|
||||||
|
import request from '@/config/axios'
|
||||||
|
|
||||||
|
// AI 音乐 VO
|
||||||
|
export interface MusicVO {
|
||||||
|
id: number // 编号
|
||||||
|
userId: number // 用户编号
|
||||||
|
title: string // 音乐名称
|
||||||
|
lyric: string // 歌词
|
||||||
|
imageUrl: string // 图片地址
|
||||||
|
audioUrl: string // 音频地址
|
||||||
|
videoUrl: string // 视频地址
|
||||||
|
status: number // 音乐状态
|
||||||
|
gptDescriptionPrompt: string // 描述词
|
||||||
|
prompt: string // 提示词
|
||||||
|
platform: string // 模型平台
|
||||||
|
model: string // 模型
|
||||||
|
generateMode: number // 生成模式
|
||||||
|
tags: string // 音乐风格标签
|
||||||
|
duration: number // 音乐时长
|
||||||
|
publicStatus: boolean // 是否发布
|
||||||
|
taskId: string // 任务id
|
||||||
|
errorMessage: string // 错误信息
|
||||||
|
}
|
||||||
|
|
||||||
|
// AI 音乐 API
|
||||||
|
export const MusicApi = {
|
||||||
|
// 查询音乐分页
|
||||||
|
getMusicPage: async (params: any) => {
|
||||||
|
return await request.get({ url: `/ai/music/page`, params })
|
||||||
|
},
|
||||||
|
|
||||||
|
// 更新音乐
|
||||||
|
updateMusic: async (data: any) => {
|
||||||
|
return await request.put({ url: '/ai/music/update', data })
|
||||||
|
},
|
||||||
|
|
||||||
|
// 删除音乐
|
||||||
|
deleteMusic: async (id: number) => {
|
||||||
|
return await request.delete({ url: `/ai/music/delete?id=` + id })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,85 @@
|
||||||
|
import { fetchEventSource } from '@microsoft/fetch-event-source'
|
||||||
|
|
||||||
|
import { getAccessToken } from '@/utils/auth'
|
||||||
|
import { config } from '@/config/axios/config'
|
||||||
|
import { AiWriteTypeEnum } from '@/views/ai/utils/constants'
|
||||||
|
import request from '@/config/axios'
|
||||||
|
|
||||||
|
export interface WriteVO {
|
||||||
|
type: AiWriteTypeEnum.WRITING | AiWriteTypeEnum.REPLY // 1:撰写 2:回复
|
||||||
|
prompt: string // 写作内容提示 1。撰写 2回复
|
||||||
|
originalContent: string // 原文
|
||||||
|
length: number // 长度
|
||||||
|
format: number // 格式
|
||||||
|
tone: number // 语气
|
||||||
|
language: number // 语言
|
||||||
|
userId?: number // 用户编号
|
||||||
|
platform?: string // 平台
|
||||||
|
model?: string // 模型
|
||||||
|
generatedContent?: string // 生成的内容
|
||||||
|
errorMessage?: string // 错误信息
|
||||||
|
createTime?: Date // 创建时间
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface AiWritePageReqVO extends PageParam {
|
||||||
|
userId?: number // 用户编号
|
||||||
|
type?: AiWriteTypeEnum // 写作类型
|
||||||
|
platform?: string // 平台
|
||||||
|
createTime?: [string, string] // 创建时间
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface AiWriteRespVo {
|
||||||
|
id: number
|
||||||
|
userId: number
|
||||||
|
type: number
|
||||||
|
platform: string
|
||||||
|
model: string
|
||||||
|
prompt: string
|
||||||
|
generatedContent: string
|
||||||
|
originalContent: string
|
||||||
|
length: number
|
||||||
|
format: number
|
||||||
|
tone: number
|
||||||
|
language: number
|
||||||
|
errorMessage: string
|
||||||
|
createTime: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export const WriteApi = {
|
||||||
|
writeStream: ({
|
||||||
|
data,
|
||||||
|
onClose,
|
||||||
|
onMessage,
|
||||||
|
onError,
|
||||||
|
ctrl
|
||||||
|
}: {
|
||||||
|
data: WriteVO
|
||||||
|
onMessage?: (res: any) => void
|
||||||
|
onError?: (...args: any[]) => void
|
||||||
|
onClose?: (...args: any[]) => void
|
||||||
|
ctrl: AbortController
|
||||||
|
}) => {
|
||||||
|
const token = getAccessToken()
|
||||||
|
return fetchEventSource(`${config.base_url}/ai/write/generate-stream`, {
|
||||||
|
method: 'post',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
Authorization: `Bearer ${token}`
|
||||||
|
},
|
||||||
|
openWhenHidden: true,
|
||||||
|
body: JSON.stringify(data),
|
||||||
|
onmessage: onMessage,
|
||||||
|
onerror: onError,
|
||||||
|
onclose: onClose,
|
||||||
|
signal: ctrl.signal
|
||||||
|
})
|
||||||
|
},
|
||||||
|
// 获取写作列表
|
||||||
|
getWritePage: (params: AiWritePageReqVO) => {
|
||||||
|
return request.get<PageResult<AiWriteRespVo[]>>({ url: `/ai/write/page`, params })
|
||||||
|
},
|
||||||
|
// 删除写作
|
||||||
|
deleteWrite(id: number) {
|
||||||
|
return request.delete({ url: `/ai/write/delete`, params: { id } })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
import request from '@/config/axios'
|
import request from '@/config/axios'
|
||||||
|
|
||||||
export const getProcessDefinition = async (id: number, key: string) => {
|
export const getProcessDefinition = async (id?: string, key?: string) => {
|
||||||
return await request.get({
|
return await request.get({
|
||||||
url: '/bpm/process-definition/get',
|
url: '/bpm/process-definition/get',
|
||||||
params: { id, key }
|
params: { id, key }
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,7 @@ export type ProcessDefinitionVO = {
|
||||||
version: number
|
version: number
|
||||||
deploymentTIme: string
|
deploymentTIme: string
|
||||||
suspensionState: number
|
suspensionState: number
|
||||||
|
formType?: number
|
||||||
}
|
}
|
||||||
|
|
||||||
export type ModelVO = {
|
export type ModelVO = {
|
||||||
|
|
@ -29,7 +30,7 @@ export const getModelPage = async (params) => {
|
||||||
return await request.get({ url: '/bpm/model/page', params })
|
return await request.get({ url: '/bpm/model/page', params })
|
||||||
}
|
}
|
||||||
|
|
||||||
export const getModel = async (id: number) => {
|
export const getModel = async (id: string) => {
|
||||||
return await request.get({ url: '/bpm/model/get?id=' + id })
|
return await request.get({ url: '/bpm/model/get?id=' + id })
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -37,6 +38,10 @@ export const updateModel = async (data: ModelVO) => {
|
||||||
return await request.put({ url: '/bpm/model/update', data: data })
|
return await request.put({ url: '/bpm/model/update', data: data })
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const updateModelBpmn = async (data: ModelVO) => {
|
||||||
|
return await request.put({ url: '/bpm/model/update-bpmn', data: data })
|
||||||
|
}
|
||||||
|
|
||||||
// 任务状态修改
|
// 任务状态修改
|
||||||
export const updateModelState = async (id: number, state: number) => {
|
export const updateModelState = async (id: number, state: number) => {
|
||||||
const data = {
|
const data = {
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
import request from '@/config/axios'
|
import request from '@/config/axios'
|
||||||
|
import { ProcessDefinitionVO } from '@/api/bpm/model'
|
||||||
|
import { NodeType } from '@/components/SimpleProcessDesignerV2/src/consts'
|
||||||
export type Task = {
|
export type Task = {
|
||||||
id: string
|
id: string
|
||||||
name: string
|
name: string
|
||||||
|
|
@ -18,17 +19,36 @@ export type ProcessInstanceVO = {
|
||||||
businessKey: string
|
businessKey: string
|
||||||
createTime: string
|
createTime: string
|
||||||
endTime: string
|
endTime: string
|
||||||
|
processDefinition?: ProcessDefinitionVO
|
||||||
}
|
}
|
||||||
|
|
||||||
export type ProcessInstanceCopyVO = {
|
// 用户信息
|
||||||
type: number
|
export type User = {
|
||||||
taskName: string
|
id: number,
|
||||||
taskKey: string
|
nickname: string,
|
||||||
processInstanceName: string
|
avatar: string
|
||||||
processInstanceKey: string
|
}
|
||||||
startUserId: string
|
|
||||||
options: string[]
|
// 审批任务信息
|
||||||
|
export type ApprovalTaskInfo = {
|
||||||
|
id: number,
|
||||||
|
ownerUser: User,
|
||||||
|
assigneeUser: User,
|
||||||
|
status: number,
|
||||||
reason: string
|
reason: string
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// 审批节点信息
|
||||||
|
export type ApprovalNodeInfo = {
|
||||||
|
id : number
|
||||||
|
name: string
|
||||||
|
nodeType: NodeType
|
||||||
|
status: number
|
||||||
|
startTime?: Date
|
||||||
|
endTime?: Date
|
||||||
|
candidateUserList?: User[]
|
||||||
|
tasks: ApprovalTaskInfo[]
|
||||||
}
|
}
|
||||||
|
|
||||||
export const getProcessInstanceMyPage = async (params: any) => {
|
export const getProcessInstanceMyPage = async (params: any) => {
|
||||||
|
|
@ -66,3 +86,14 @@ export const getProcessInstance = async (id: string) => {
|
||||||
export const getProcessInstanceCopyPage = async (params: any) => {
|
export const getProcessInstanceCopyPage = async (params: any) => {
|
||||||
return await request.get({ url: '/bpm/process-instance/copy/page', params })
|
return await request.get({ url: '/bpm/process-instance/copy/page', params })
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 获取审批详情
|
||||||
|
export const getApprovalDetail = async (processInstanceId?:string, processDefinitionId?:string) => {
|
||||||
|
const param = processInstanceId ? '?processInstanceId='+ processInstanceId : '?processDefinitionId='+ processDefinitionId
|
||||||
|
return await request.get({ url: 'bpm/process-instance/get-approval-detail'+ param })
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取表单字段权限
|
||||||
|
export const getFormFieldsPermission = async (params: any) => {
|
||||||
|
return await request.get({ url: '/bpm/process-instance/get-form-fields-permission', params })
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,15 @@
|
||||||
|
import request from '@/config/axios'
|
||||||
|
|
||||||
|
|
||||||
|
export const updateBpmSimpleModel = async (data) => {
|
||||||
|
return await request.post({
|
||||||
|
url: '/bpm/model/simple/update',
|
||||||
|
data: data
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export const getBpmSimpleModel = async (id) => {
|
||||||
|
return await request.get({
|
||||||
|
url: '/bpm/model/simple/get?id=' + id
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
@ -1,5 +1,51 @@
|
||||||
import request from '@/config/axios'
|
import request from '@/config/axios'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 任务状态枚举
|
||||||
|
*/
|
||||||
|
export enum TaskStatusEnum {
|
||||||
|
/**
|
||||||
|
* 未开始
|
||||||
|
*/
|
||||||
|
NOT_START = -1,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 待审批
|
||||||
|
*/
|
||||||
|
WAIT = 0,
|
||||||
|
/**
|
||||||
|
* 审批中
|
||||||
|
*/
|
||||||
|
RUNNING = 1,
|
||||||
|
/**
|
||||||
|
* 审批通过
|
||||||
|
*/
|
||||||
|
APPROVE = 2,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 审批不通过
|
||||||
|
*/
|
||||||
|
REJECT = 3,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 已取消
|
||||||
|
*/
|
||||||
|
CANCEL = 4,
|
||||||
|
/**
|
||||||
|
* 已退回
|
||||||
|
*/
|
||||||
|
RETURN = 5,
|
||||||
|
/**
|
||||||
|
* 委派中
|
||||||
|
*/
|
||||||
|
DELEGATE = 6,
|
||||||
|
/**
|
||||||
|
* 审批通过中
|
||||||
|
*/
|
||||||
|
APPROVING = 7,
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
export type TaskVO = {
|
export type TaskVO = {
|
||||||
id: number
|
id: number
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -13,8 +13,8 @@ export interface ProductCategoryVO {
|
||||||
// ERP 产品分类 API
|
// ERP 产品分类 API
|
||||||
export const ProductCategoryApi = {
|
export const ProductCategoryApi = {
|
||||||
// 查询产品分类列表
|
// 查询产品分类列表
|
||||||
getProductCategoryList: async (params) => {
|
getProductCategoryList: async () => {
|
||||||
return await request.get({ url: `/erp/product-category/list`, params })
|
return await request.get({ url: `/erp/product-category/list` })
|
||||||
},
|
},
|
||||||
|
|
||||||
// 查询产品分类精简列表
|
// 查询产品分类精简列表
|
||||||
|
|
|
||||||
|
|
@ -7,8 +7,8 @@ export interface Demo02CategoryVO {
|
||||||
}
|
}
|
||||||
|
|
||||||
// 查询示例分类列表
|
// 查询示例分类列表
|
||||||
export const getDemo02CategoryList = async (params) => {
|
export const getDemo02CategoryList = async () => {
|
||||||
return await request.get({ url: `/infra/demo02-category/list`, params })
|
return await request.get({ url: `/infra/demo02-category/list` })
|
||||||
}
|
}
|
||||||
|
|
||||||
// 查询示例分类详情
|
// 查询示例分类详情
|
||||||
|
|
|
||||||
|
|
@ -12,6 +12,7 @@ export interface JobLogVO {
|
||||||
duration: string
|
duration: string
|
||||||
status: number
|
status: number
|
||||||
createTime: string
|
createTime: string
|
||||||
|
result: string
|
||||||
}
|
}
|
||||||
|
|
||||||
// 任务日志列表
|
// 任务日志列表
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,74 @@
|
||||||
|
import request from '@/config/axios'
|
||||||
|
|
||||||
|
// IoT 设备 VO
|
||||||
|
export interface DeviceVO {
|
||||||
|
id: number // 设备 ID,主键,自增
|
||||||
|
deviceKey: string // 设备唯一标识符
|
||||||
|
deviceName: string // 设备名称
|
||||||
|
productId: number // 产品编号
|
||||||
|
productKey: string // 产品标识
|
||||||
|
deviceType: number // 设备类型
|
||||||
|
nickname: string // 设备备注名称
|
||||||
|
gatewayId: number // 网关设备 ID
|
||||||
|
status: number // 设备状态
|
||||||
|
statusLastUpdateTime: Date // 设备状态最后更新时间
|
||||||
|
lastOnlineTime: Date // 最后上线时间
|
||||||
|
lastOfflineTime: Date // 最后离线时间
|
||||||
|
activeTime: Date // 设备激活时间
|
||||||
|
createTime: Date // 创建时间
|
||||||
|
ip: string // 设备的 IP 地址
|
||||||
|
firmwareVersion: string // 设备的固件版本
|
||||||
|
deviceSecret: string // 设备密钥,用于设备认证,需安全存储
|
||||||
|
mqttClientId: string // MQTT 客户端 ID
|
||||||
|
mqttUsername: string // MQTT 用户名
|
||||||
|
mqttPassword: string // MQTT 密码
|
||||||
|
authType: string // 认证类型
|
||||||
|
latitude: number // 设备位置的纬度
|
||||||
|
longitude: number // 设备位置的经度
|
||||||
|
areaId: number // 地区编码
|
||||||
|
address: string // 设备详细地址
|
||||||
|
serialNumber: string // 设备序列号
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface DeviceUpdateStatusVO {
|
||||||
|
id: number // 设备 ID,主键,自增
|
||||||
|
status: number // 设备状态
|
||||||
|
}
|
||||||
|
|
||||||
|
// 设备 API
|
||||||
|
export const DeviceApi = {
|
||||||
|
// 查询设备分页
|
||||||
|
getDevicePage: async (params: any) => {
|
||||||
|
return await request.get({ url: `/iot/device/page`, params })
|
||||||
|
},
|
||||||
|
|
||||||
|
// 查询设备详情
|
||||||
|
getDevice: async (id: number) => {
|
||||||
|
return await request.get({ url: `/iot/device/get?id=` + id })
|
||||||
|
},
|
||||||
|
|
||||||
|
// 新增设备
|
||||||
|
createDevice: async (data: DeviceVO) => {
|
||||||
|
return await request.post({ url: `/iot/device/create`, data })
|
||||||
|
},
|
||||||
|
|
||||||
|
// 修改设备
|
||||||
|
updateDevice: async (data: DeviceVO) => {
|
||||||
|
return await request.put({ url: `/iot/device/update`, data })
|
||||||
|
},
|
||||||
|
|
||||||
|
// 修改设备状态
|
||||||
|
updateDeviceStatus: async (data: DeviceUpdateStatusVO) => {
|
||||||
|
return await request.put({ url: `/iot/device/update-status`, data })
|
||||||
|
},
|
||||||
|
|
||||||
|
// 删除设备
|
||||||
|
deleteDevice: async (id: number) => {
|
||||||
|
return await request.delete({ url: `/iot/device/delete?id=` + id })
|
||||||
|
},
|
||||||
|
|
||||||
|
// 获取设备数量
|
||||||
|
getDeviceCount: async (productId: number) => {
|
||||||
|
return await request.get({ url: `/iot/device/count?productId=` + productId })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,62 @@
|
||||||
|
import request from '@/config/axios'
|
||||||
|
|
||||||
|
// IoT 产品 VO
|
||||||
|
export interface ProductVO {
|
||||||
|
id: number // 产品编号
|
||||||
|
name: string // 产品名称
|
||||||
|
productKey: string // 产品标识
|
||||||
|
protocolId: number // 协议编号
|
||||||
|
categoryId: number // 产品所属品类标识符
|
||||||
|
description: string // 产品描述
|
||||||
|
validateType: number // 数据校验级别
|
||||||
|
status: number // 产品状态
|
||||||
|
deviceType: number // 设备类型
|
||||||
|
netType: number // 联网方式
|
||||||
|
protocolType: number // 接入网关协议
|
||||||
|
dataFormat: number // 数据格式
|
||||||
|
deviceCount: number // 设备数量
|
||||||
|
createTime: Date // 创建时间
|
||||||
|
}
|
||||||
|
|
||||||
|
// IoT 产品 API
|
||||||
|
export const ProductApi = {
|
||||||
|
// 查询产品分页
|
||||||
|
getProductPage: async (params: any) => {
|
||||||
|
return await request.get({ url: `/iot/product/page`, params })
|
||||||
|
},
|
||||||
|
|
||||||
|
// 查询产品详情
|
||||||
|
getProduct: async (id: number) => {
|
||||||
|
return await request.get({ url: `/iot/product/get?id=` + id })
|
||||||
|
},
|
||||||
|
|
||||||
|
// 新增产品
|
||||||
|
createProduct: async (data: ProductVO) => {
|
||||||
|
return await request.post({ url: `/iot/product/create`, data })
|
||||||
|
},
|
||||||
|
|
||||||
|
// 修改产品
|
||||||
|
updateProduct: async (data: ProductVO) => {
|
||||||
|
return await request.put({ url: `/iot/product/update`, data })
|
||||||
|
},
|
||||||
|
|
||||||
|
// 删除产品
|
||||||
|
deleteProduct: async (id: number) => {
|
||||||
|
return await request.delete({ url: `/iot/product/delete?id=` + id })
|
||||||
|
},
|
||||||
|
|
||||||
|
// 导出产品 Excel
|
||||||
|
exportProduct: async (params) => {
|
||||||
|
return await request.download({ url: `/iot/product/export-excel`, params })
|
||||||
|
},
|
||||||
|
|
||||||
|
// 更新产品状态
|
||||||
|
updateProductStatus: async (id: number, status: number) => {
|
||||||
|
return await request.put({ url: `/iot/product/update-status?id=` + id + `&status=` + status })
|
||||||
|
},
|
||||||
|
|
||||||
|
// 查询产品(精简)列表
|
||||||
|
getSimpleProductList() {
|
||||||
|
return request.get({ url: '/iot/product/list-all-simple' })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,55 @@
|
||||||
|
import request from '@/config/axios'
|
||||||
|
|
||||||
|
// IoT 产品物模型 VO
|
||||||
|
export interface ThinkModelFunctionVO {
|
||||||
|
id: number // 物模型功能编号
|
||||||
|
identifier: string // 功能标识
|
||||||
|
name: string // 功能名称
|
||||||
|
description: string // 功能描述
|
||||||
|
productId: number // 产品编号
|
||||||
|
productKey: string // 产品标识
|
||||||
|
type: number // 功能类型
|
||||||
|
property: string // 属性
|
||||||
|
event: string // 事件
|
||||||
|
service: string // 服务
|
||||||
|
}
|
||||||
|
|
||||||
|
// IoT 产品物模型 API
|
||||||
|
export const ThinkModelFunctionApi = {
|
||||||
|
// 查询产品物模型分页
|
||||||
|
getThinkModelFunctionPage: async (params: any) => {
|
||||||
|
return await request.get({ url: `/iot/think-model-function/page`, params })
|
||||||
|
},
|
||||||
|
// 获得产品物模型
|
||||||
|
getThinkModelFunctionListByProductId: async (params: any) => {
|
||||||
|
return await request.get({
|
||||||
|
url: `/iot/think-model-function/list-by-product-id`,
|
||||||
|
params
|
||||||
|
})
|
||||||
|
},
|
||||||
|
|
||||||
|
// 查询产品物模型详情
|
||||||
|
getThinkModelFunction: async (id: number) => {
|
||||||
|
return await request.get({ url: `/iot/think-model-function/get?id=` + id })
|
||||||
|
},
|
||||||
|
|
||||||
|
// 新增产品物模型
|
||||||
|
createThinkModelFunction: async (data: ThinkModelFunctionVO) => {
|
||||||
|
return await request.post({ url: `/iot/think-model-function/create`, data })
|
||||||
|
},
|
||||||
|
|
||||||
|
// 修改产品物模型
|
||||||
|
updateThinkModelFunction: async (data: ThinkModelFunctionVO) => {
|
||||||
|
return await request.put({ url: `/iot/think-model-function/update`, data })
|
||||||
|
},
|
||||||
|
|
||||||
|
// 删除产品物模型
|
||||||
|
deleteThinkModelFunction: async (id: number) => {
|
||||||
|
return await request.delete({ url: `/iot/think-model-function/delete?id=` + id })
|
||||||
|
},
|
||||||
|
|
||||||
|
// 导出产品物模型 Excel
|
||||||
|
exportThinkModelFunction: async (params) => {
|
||||||
|
return await request.download({ url: `/iot/think-model-function/export-excel`, params })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
import request from '@/config/axios'
|
import request from '@/config/axios'
|
||||||
import { getRefreshToken } from '@/utils/auth'
|
import { getRefreshToken } from '@/utils/auth'
|
||||||
import type { UserLoginVO } from './types'
|
import type { RegisterVO, UserLoginVO } from './types'
|
||||||
|
|
||||||
export interface SmsCodeVO {
|
export interface SmsCodeVO {
|
||||||
mobile: string
|
mobile: string
|
||||||
|
|
@ -17,6 +17,11 @@ export const login = (data: UserLoginVO) => {
|
||||||
return request.post({ url: '/system/auth/login', data })
|
return request.post({ url: '/system/auth/login', data })
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 注册
|
||||||
|
export const register = (data: RegisterVO) => {
|
||||||
|
return request.post({ url: '/system/auth/register', data })
|
||||||
|
}
|
||||||
|
|
||||||
// 刷新访问令牌
|
// 刷新访问令牌
|
||||||
export const refreshToken = () => {
|
export const refreshToken = () => {
|
||||||
return request.post({ url: '/system/auth/refresh-token?refreshToken=' + getRefreshToken() })
|
return request.post({ url: '/system/auth/refresh-token?refreshToken=' + getRefreshToken() })
|
||||||
|
|
|
||||||
|
|
@ -29,3 +29,10 @@ export type UserVO = {
|
||||||
loginIp: string
|
loginIp: string
|
||||||
loginDate: string
|
loginDate: string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type RegisterVO = {
|
||||||
|
tenantName: string
|
||||||
|
username: string
|
||||||
|
password: string
|
||||||
|
captchaVerification: string
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,10 @@
|
||||||
|
import request from '@/config/axios'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获得商品浏览记录分页
|
||||||
|
*
|
||||||
|
* @param params 请求参数
|
||||||
|
*/
|
||||||
|
export const getBrowseHistoryPage = (params: any) => {
|
||||||
|
return request.get({ url: '/product/browse-history/page', params })
|
||||||
|
}
|
||||||
|
|
@ -24,20 +24,6 @@ export interface PropertyValueVO {
|
||||||
remark?: string
|
remark?: string
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* 商品属性值的明细
|
|
||||||
*/
|
|
||||||
export interface PropertyValueDetailVO {
|
|
||||||
/** 属性项的编号 */
|
|
||||||
propertyId: number // 属性的编号
|
|
||||||
/** 属性的名称 */
|
|
||||||
propertyName: string
|
|
||||||
/** 属性值的编号 */
|
|
||||||
valueId: number
|
|
||||||
/** 属性值的名称 */
|
|
||||||
valueName: string
|
|
||||||
}
|
|
||||||
|
|
||||||
// ------------------------ 属性项 -------------------
|
// ------------------------ 属性项 -------------------
|
||||||
|
|
||||||
// 创建属性项
|
// 创建属性项
|
||||||
|
|
@ -65,6 +51,11 @@ export const getPropertyPage = (params: PageParam) => {
|
||||||
return request.get({ url: '/product/property/page', params })
|
return request.get({ url: '/product/property/page', params })
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 获得属性项精简列表
|
||||||
|
export const getPropertySimpleList = (): Promise<PropertyVO[]> => {
|
||||||
|
return request.get({ url: '/product/property/simple-list' })
|
||||||
|
}
|
||||||
|
|
||||||
// ------------------------ 属性值 -------------------
|
// ------------------------ 属性值 -------------------
|
||||||
|
|
||||||
// 获得属性值分页
|
// 获得属性值分页
|
||||||
|
|
@ -91,3 +82,8 @@ export const updatePropertyValue = (data: PropertyValueVO) => {
|
||||||
export const deletePropertyValue = (id: number) => {
|
export const deletePropertyValue = (id: number) => {
|
||||||
return request.delete({ url: `/product/property/value/delete?id=${id}` })
|
return request.delete({ url: `/product/property/value/delete?id=${id}` })
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 获得属性值精简列表
|
||||||
|
export const getPropertyValueSimpleList = (propertyId: number): Promise<PropertyValueVO[]> => {
|
||||||
|
return request.get({ url: '/product/property/value/simple-list', params: { propertyId } })
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -50,6 +50,8 @@ export interface Spu {
|
||||||
giveIntegral?: number // 赠送积分
|
giveIntegral?: number // 赠送积分
|
||||||
virtualSalesCount?: number // 虚拟销量
|
virtualSalesCount?: number // 虚拟销量
|
||||||
price?: number // 商品价格
|
price?: number // 商品价格
|
||||||
|
combinationPrice?: number // 商品拼团价格
|
||||||
|
seckillPrice?: number // 商品秒杀价格
|
||||||
salesCount?: number // 商品销量
|
salesCount?: number // 商品销量
|
||||||
marketPrice?: number // 市场价
|
marketPrice?: number // 市场价
|
||||||
costPrice?: number // 成本价
|
costPrice?: number // 成本价
|
||||||
|
|
|
||||||
|
|
@ -16,6 +16,7 @@ export interface CombinationActivityVO {
|
||||||
virtualGroup?: number
|
virtualGroup?: number
|
||||||
status?: number
|
status?: number
|
||||||
limitDuration?: number
|
limitDuration?: number
|
||||||
|
combinationPrice?: number
|
||||||
products: CombinationProductVO[]
|
products: CombinationProductVO[]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -36,7 +37,7 @@ export interface SpuExtension extends Spu {
|
||||||
}
|
}
|
||||||
|
|
||||||
// 查询拼团活动列表
|
// 查询拼团活动列表
|
||||||
export const getCombinationActivityPage = async (params) => {
|
export const getCombinationActivityPage = async (params: any) => {
|
||||||
return await request.get({ url: '/promotion/combination-activity/page', params })
|
return await request.get({ url: '/promotion/combination-activity/page', params })
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -45,6 +46,11 @@ export const getCombinationActivity = async (id: number) => {
|
||||||
return await request.get({ url: '/promotion/combination-activity/get?id=' + id })
|
return await request.get({ url: '/promotion/combination-activity/get?id=' + id })
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 获得拼团活动列表,基于活动编号数组
|
||||||
|
export const getCombinationActivityListByIds = (ids: number[]) => {
|
||||||
|
return request.get({ url: `/promotion/combination-activity/list-by-ids?ids=${ids}` })
|
||||||
|
}
|
||||||
|
|
||||||
// 新增拼团活动
|
// 新增拼团活动
|
||||||
export const createCombinationActivity = async (data: CombinationActivityVO) => {
|
export const createCombinationActivity = async (data: CombinationActivityVO) => {
|
||||||
return await request.post({ url: '/promotion/combination-activity/create', data })
|
return await request.post({ url: '/promotion/combination-activity/create', data })
|
||||||
|
|
|
||||||
|
|
@ -74,7 +74,7 @@ export function getCouponTemplatePage(params: PageParam) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// 获得优惠劵模板分页
|
// 获得优惠劵模板分页
|
||||||
export function getCouponTemplateList(ids: number[]) {
|
export function getCouponTemplateList(ids: number[]): Promise<CouponTemplateVO[]> {
|
||||||
return request.get({
|
return request.get({
|
||||||
url: `/promotion/coupon-template/list?ids=${ids}`
|
url: `/promotion/coupon-template/list?ids=${ids}`
|
||||||
})
|
})
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,35 @@
|
||||||
|
import request from '@/config/axios'
|
||||||
|
|
||||||
|
export interface KeFuConversationRespVO {
|
||||||
|
id: number // 编号
|
||||||
|
userId: number // 会话所属用户
|
||||||
|
userAvatar: string // 会话所属用户头像
|
||||||
|
userNickname: string // 会话所属用户昵称
|
||||||
|
lastMessageTime: Date // 最后聊天时间
|
||||||
|
lastMessageContent: string // 最后聊天内容
|
||||||
|
lastMessageContentType: number // 最后发送的消息类型
|
||||||
|
adminPinned: boolean // 管理端置顶
|
||||||
|
userDeleted: boolean // 用户是否可见
|
||||||
|
adminDeleted: boolean // 管理员是否可见
|
||||||
|
adminUnreadMessageCount: number // 管理员未读消息数
|
||||||
|
createTime?: string // 创建时间
|
||||||
|
}
|
||||||
|
|
||||||
|
// 客服会话 API
|
||||||
|
export const KeFuConversationApi = {
|
||||||
|
// 获得客服会话列表
|
||||||
|
getConversationList: async () => {
|
||||||
|
return await request.get({ url: '/promotion/kefu-conversation/list' })
|
||||||
|
},
|
||||||
|
// 客服会话置顶
|
||||||
|
updateConversationPinned: async (data: any) => {
|
||||||
|
return await request.put({
|
||||||
|
url: '/promotion/kefu-conversation/update-conversation-pinned',
|
||||||
|
data
|
||||||
|
})
|
||||||
|
},
|
||||||
|
// 删除客服会话
|
||||||
|
deleteConversation: async (id: number) => {
|
||||||
|
return await request.delete({ url: `/promotion/kefu-conversation/delete?id=${id}`})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,36 @@
|
||||||
|
import request from '@/config/axios'
|
||||||
|
|
||||||
|
export interface KeFuMessageRespVO {
|
||||||
|
id: number // 编号
|
||||||
|
conversationId: number // 会话编号
|
||||||
|
senderId: number // 发送人编号
|
||||||
|
senderAvatar: string // 发送人头像
|
||||||
|
senderType: number // 发送人类型
|
||||||
|
receiverId: number // 接收人编号
|
||||||
|
receiverType: number // 接收人类型
|
||||||
|
contentType: number // 消息类型
|
||||||
|
content: string // 消息
|
||||||
|
readStatus: boolean // 是否已读
|
||||||
|
createTime: Date // 创建时间
|
||||||
|
}
|
||||||
|
|
||||||
|
// 客服会话 API
|
||||||
|
export const KeFuMessageApi = {
|
||||||
|
// 发送客服消息
|
||||||
|
sendKeFuMessage: async (data: any) => {
|
||||||
|
return await request.post({
|
||||||
|
url: '/promotion/kefu-message/send',
|
||||||
|
data
|
||||||
|
})
|
||||||
|
},
|
||||||
|
// 更新客服消息已读状态
|
||||||
|
updateKeFuMessageReadStatus: async (conversationId: number) => {
|
||||||
|
return await request.put({
|
||||||
|
url: '/promotion/kefu-message/update-read-status?conversationId=' + conversationId
|
||||||
|
})
|
||||||
|
},
|
||||||
|
// 获得消息分页数据
|
||||||
|
getKeFuMessagePage: async (params: any) => {
|
||||||
|
return await request.get({ url: '/promotion/kefu-message/page', params })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,91 @@
|
||||||
|
import request from '@/config/axios'
|
||||||
|
import { Sku, Spu } from '@/api/mall/product/spu' // 积分商城活动 VO
|
||||||
|
|
||||||
|
// 积分商城活动 VO
|
||||||
|
export interface PointActivityVO {
|
||||||
|
id: number // 积分商城活动编号
|
||||||
|
spuId: number // 积分商城活动商品
|
||||||
|
status: number // 活动状态
|
||||||
|
stock: number // 积分商城活动库存
|
||||||
|
totalStock: number // 积分商城活动总库存
|
||||||
|
remark?: string // 备注
|
||||||
|
sort: number // 排序
|
||||||
|
createTime: string // 创建时间
|
||||||
|
products: PointProductVO[] // 积分商城商品
|
||||||
|
|
||||||
|
// ========== 商品字段 ==========
|
||||||
|
spuName: string // 商品名称
|
||||||
|
picUrl: string // 商品主图
|
||||||
|
marketPrice: number // 商品市场价,单位:分
|
||||||
|
|
||||||
|
//======================= 显示所需兑换积分最少的 sku 信息 =======================
|
||||||
|
point: number // 兑换积分
|
||||||
|
price: number // 兑换金额,单位:分
|
||||||
|
}
|
||||||
|
|
||||||
|
// 秒杀活动所需属性
|
||||||
|
export interface PointProductVO {
|
||||||
|
id?: number // 积分商城商品编号
|
||||||
|
activityId?: number // 积分商城活动 id
|
||||||
|
spuId?: number // 商品 SPU 编号
|
||||||
|
skuId: number // 商品 SKU 编号
|
||||||
|
count: number // 可兑换数量
|
||||||
|
point: number // 兑换积分
|
||||||
|
price: number // 兑换金额,单位:分
|
||||||
|
stock: number // 积分商城商品库存
|
||||||
|
activityStatus?: number // 积分商城商品状态
|
||||||
|
}
|
||||||
|
|
||||||
|
// 扩展 Sku 配置
|
||||||
|
export type SkuExtension = Sku & {
|
||||||
|
productConfig: PointProductVO
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface SpuExtension extends Spu {
|
||||||
|
skus: SkuExtension[] // 重写类型
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface SpuExtension0 extends Spu {
|
||||||
|
pointStock: number // 积分商城活动库存
|
||||||
|
pointTotalStock: number // 积分商城活动总库存
|
||||||
|
point: number // 兑换积分
|
||||||
|
pointPrice: number // 兑换金额,单位:分
|
||||||
|
}
|
||||||
|
|
||||||
|
// 积分商城活动 API
|
||||||
|
export const PointActivityApi = {
|
||||||
|
// 查询积分商城活动分页
|
||||||
|
getPointActivityPage: async (params: any) => {
|
||||||
|
return await request.get({ url: `/promotion/point-activity/page`, params })
|
||||||
|
},
|
||||||
|
|
||||||
|
// 查询积分商城活动详情
|
||||||
|
getPointActivity: async (id: number) => {
|
||||||
|
return await request.get({ url: `/promotion/point-activity/get?id=` + id })
|
||||||
|
},
|
||||||
|
|
||||||
|
// 查询积分商城活动列表,基于活动编号数组
|
||||||
|
getPointActivityListByIds: async (ids: number[]) => {
|
||||||
|
return request.get({ url: `/promotion/point-activity/list-by-ids?ids=${ids}` })
|
||||||
|
},
|
||||||
|
|
||||||
|
// 新增积分商城活动
|
||||||
|
createPointActivity: async (data: PointActivityVO) => {
|
||||||
|
return await request.post({ url: `/promotion/point-activity/create`, data })
|
||||||
|
},
|
||||||
|
|
||||||
|
// 修改积分商城活动
|
||||||
|
updatePointActivity: async (data: PointActivityVO) => {
|
||||||
|
return await request.put({ url: `/promotion/point-activity/update`, data })
|
||||||
|
},
|
||||||
|
|
||||||
|
// 删除积分商城活动
|
||||||
|
deletePointActivity: async (id: number) => {
|
||||||
|
return await request.delete({ url: `/promotion/point-activity/delete?id=` + id })
|
||||||
|
},
|
||||||
|
|
||||||
|
// 关闭秒杀活动
|
||||||
|
closePointActivity: async (id: number) => {
|
||||||
|
return await request.put({ url: '/promotion/point-activity/close?id=' + id })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,34 +1,39 @@
|
||||||
import request from '@/config/axios'
|
import request from '@/config/axios'
|
||||||
|
|
||||||
export interface DiscountActivityVO {
|
export interface RewardActivityVO {
|
||||||
id?: number
|
id?: number
|
||||||
name?: string
|
name?: string
|
||||||
startTime?: Date
|
startTime?: Date
|
||||||
endTime?: Date
|
endTime?: Date
|
||||||
|
startAndEndTime?: Date[] // 只前端使用
|
||||||
remark?: string
|
remark?: string
|
||||||
conditionType?: number
|
conditionType?: number
|
||||||
productScope?: number
|
productScope?: number
|
||||||
|
rules: RewardRule[]
|
||||||
|
// 如下仅用于表单,不提交
|
||||||
|
productScopeValues?: number[] // 商品范围:值为品类编号列表、商品编号列表
|
||||||
|
productCategoryIds?: number[]
|
||||||
productSpuIds?: number[]
|
productSpuIds?: number[]
|
||||||
rules?: DiscountProductVO[]
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 优惠规则
|
// 优惠规则
|
||||||
export interface DiscountProductVO {
|
export interface RewardRule {
|
||||||
limit: number
|
limit?: number
|
||||||
discountPrice: number
|
discountPrice?: number
|
||||||
freeDelivery: boolean
|
freeDelivery?: boolean
|
||||||
point: number
|
point: number
|
||||||
couponIds: number[]
|
giveCouponTemplateCounts?: {
|
||||||
couponCounts: number[]
|
[key: number]: number
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 新增满减送活动
|
// 新增满减送活动
|
||||||
export const createRewardActivity = async (data: DiscountActivityVO) => {
|
export const createRewardActivity = async (data: RewardActivityVO) => {
|
||||||
return await request.post({ url: '/promotion/reward-activity/create', data })
|
return await request.post({ url: '/promotion/reward-activity/create', data })
|
||||||
}
|
}
|
||||||
|
|
||||||
// 更新满减送活动
|
// 更新满减送活动
|
||||||
export const updateRewardActivity = async (data: DiscountActivityVO) => {
|
export const updateRewardActivity = async (data: RewardActivityVO) => {
|
||||||
return await request.put({ url: '/promotion/reward-activity/update', data })
|
return await request.put({ url: '/promotion/reward-activity/update', data })
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -42,7 +47,12 @@ export const getReward = async (id: number) => {
|
||||||
return await request.get({ url: '/promotion/reward-activity/get?id=' + id })
|
return await request.get({ url: '/promotion/reward-activity/get?id=' + id })
|
||||||
}
|
}
|
||||||
|
|
||||||
// 删除限时折扣活动
|
// 删除满减送活动
|
||||||
export const deleteRewardActivity = async (id: number) => {
|
export const deleteRewardActivity = async (id: number) => {
|
||||||
return await request.delete({ url: '/promotion/reward-activity/delete?id=' + id })
|
return await request.delete({ url: '/promotion/reward-activity/delete?id=' + id })
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 关闭满减送活动
|
||||||
|
export const closeRewardActivity = async (id: number) => {
|
||||||
|
return await request.put({ url: '/promotion/reward-activity/close?id=' + id })
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -18,12 +18,14 @@ export interface SeckillActivityVO {
|
||||||
singleLimitCount?: number
|
singleLimitCount?: number
|
||||||
stock?: number
|
stock?: number
|
||||||
totalStock?: number
|
totalStock?: number
|
||||||
|
seckillPrice?: number
|
||||||
products?: SeckillProductVO[]
|
products?: SeckillProductVO[]
|
||||||
}
|
}
|
||||||
|
|
||||||
// 秒杀活动所需属性
|
// 秒杀活动所需属性
|
||||||
export interface SeckillProductVO {
|
export interface SeckillProductVO {
|
||||||
skuId: number
|
skuId: number
|
||||||
|
spuId: number
|
||||||
seckillPrice: number
|
seckillPrice: number
|
||||||
stock: number
|
stock: number
|
||||||
}
|
}
|
||||||
|
|
@ -42,6 +44,11 @@ export const getSeckillActivityPage = async (params) => {
|
||||||
return await request.get({ url: '/promotion/seckill-activity/page', params })
|
return await request.get({ url: '/promotion/seckill-activity/page', params })
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 查询秒杀活动列表,基于活动编号数组
|
||||||
|
export const getSeckillActivityListByIds = (ids: number[]) => {
|
||||||
|
return request.get({ url: `/promotion/seckill-activity/list-by-ids?ids=${ids}` })
|
||||||
|
}
|
||||||
|
|
||||||
// 查询秒杀活动详情
|
// 查询秒杀活动详情
|
||||||
export const getSeckillActivity = async (id: number) => {
|
export const getSeckillActivity = async (id: number) => {
|
||||||
return await request.get({ url: '/promotion/seckill-activity/get?id=' + id })
|
return await request.get({ url: '/promotion/seckill-activity/get?id=' + id })
|
||||||
|
|
|
||||||
|
|
@ -19,7 +19,7 @@ export const SeckillConfigApi = {
|
||||||
|
|
||||||
// 查询秒杀时段列表
|
// 查询秒杀时段列表
|
||||||
getSimpleSeckillConfigList: async () => {
|
getSimpleSeckillConfigList: async () => {
|
||||||
return await request.get({ url: `/promotion/seckill-config/simple-list` })
|
return await request.get({ url: `/promotion/seckill-config/list` })
|
||||||
},
|
},
|
||||||
|
|
||||||
// 查询秒杀时段详情
|
// 查询秒杀时段详情
|
||||||
|
|
|
||||||
|
|
@ -141,7 +141,7 @@ export const getExpressTrackList = async (id: number | null) => {
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface DeliveryVO {
|
export interface DeliveryVO {
|
||||||
id: number // 订单编号
|
id?: number // 订单编号
|
||||||
logisticsId: number | null // 物流公司编号
|
logisticsId: number | null // 物流公司编号
|
||||||
logisticsNo: string // 物流编号
|
logisticsNo: string // 物流编号
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -46,8 +46,3 @@ export const updateUserLevel = async (data: any) => {
|
||||||
export const updateUserPoint = async (data: any) => {
|
export const updateUserPoint = async (data: any) => {
|
||||||
return await request.put({ url: `/member/user/update-point`, data })
|
return await request.put({ url: `/member/user/update-point`, data })
|
||||||
}
|
}
|
||||||
|
|
||||||
// 修改会员用户余额
|
|
||||||
export const updateUserBalance = async (data: any) => {
|
|
||||||
return await request.put({ url: `/member/user/update-balance`, data })
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@ import request from '@/config/axios'
|
||||||
|
|
||||||
export interface AppVO {
|
export interface AppVO {
|
||||||
id: number
|
id: number
|
||||||
|
appKey: string
|
||||||
name: string
|
name: string
|
||||||
status: number
|
status: number
|
||||||
remark: string
|
remark: string
|
||||||
|
|
|
||||||
|
|
@ -84,8 +84,14 @@ export const getOrderPage = async (params: OrderPageReqVO) => {
|
||||||
}
|
}
|
||||||
|
|
||||||
// 查询详情支付订单
|
// 查询详情支付订单
|
||||||
export const getOrder = async (id: number) => {
|
export const getOrder = async (id: number, sync?: boolean) => {
|
||||||
return await request.get({ url: '/pay/order/get?id=' + id })
|
return await request.get({
|
||||||
|
url: '/pay/order/get',
|
||||||
|
params: {
|
||||||
|
id,
|
||||||
|
sync
|
||||||
|
}
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// 获得支付订单的明细
|
// 获得支付订单的明细
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,7 @@ import request from '@/config/axios'
|
||||||
export interface PayWalletUserReqVO {
|
export interface PayWalletUserReqVO {
|
||||||
userId: number
|
userId: number
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 钱包 VO */
|
/** 钱包 VO */
|
||||||
export interface WalletVO {
|
export interface WalletVO {
|
||||||
id: number
|
id: number
|
||||||
|
|
@ -20,7 +21,12 @@ export const getWallet = async (params: PayWalletUserReqVO) => {
|
||||||
return await request.get<WalletVO>({ url: `/pay/wallet/get`, params })
|
return await request.get<WalletVO>({ url: `/pay/wallet/get`, params })
|
||||||
}
|
}
|
||||||
|
|
||||||
// 查询会员钱包列表
|
/** 查询会员钱包列表 */
|
||||||
export const getWalletPage = async (params) => {
|
export const getWalletPage = async (params: any) => {
|
||||||
return await request.get({ url: `/pay/wallet/page`, params })
|
return await request.get({ url: `/pay/wallet/page`, params })
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** 修改会员钱包余额 */
|
||||||
|
export const updateWalletBalance = async (data: any) => {
|
||||||
|
return await request.put({ url: `/pay/wallet/update-balance`, data })
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1 @@
|
||||||
|
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1715606039621" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="4256" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><path d="M878.250667 981.333333H375.338667a104.661333 104.661333 0 0 1-104.661334-104.661333V375.338667a104.661333 104.661333 0 0 1 104.661334-104.661334h502.912a104.661333 104.661333 0 0 1 104.661333 104.661334v502.912C981.333333 934.485333 934.485333 981.333333 878.250667 981.333333zM375.338667 364.373333a10.666667 10.666667 0 0 0-10.922667 10.965334v502.912c0 6.229333 4.693333 10.922667 10.922667 10.922666h502.912a10.666667 10.666667 0 0 0 10.922666-10.922666V375.338667a10.666667 10.666667 0 0 0-10.922666-10.922667H375.338667z" fill="#ffffff" p-id="4257"></path><path d="M192.597333 753.322667H147.328A104.661333 104.661333 0 0 1 42.666667 648.661333V147.328A104.661333 104.661333 0 0 1 147.328 42.666667H650.24a104.661333 104.661333 0 0 1 104.618667 104.661333v49.962667c0 26.538667-20.309333 46.848-46.848 46.848a46.037333 46.037333 0 0 1-46.848-46.848V147.328a10.666667 10.666667 0 0 0-10.922667-10.965333H147.328a10.666667 10.666667 0 0 0-10.965333 10.965333V650.24c0 6.229333 4.693333 10.922667 10.965333 10.922667h45.269333c26.538667 0 46.848 20.309333 46.848 46.848 0 26.538667-21.845333 45.312-46.848 45.312z" fill="#ffffff" p-id="4258"></path></svg>
|
||||||
|
After Width: | Height: | Size: 1.5 KiB |
|
|
@ -0,0 +1 @@
|
||||||
|
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1715352878351" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="1499" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><path d="M624.5 786.3c92.9 0 168.2-75.3 168.2-168.2V309c0-92.4-75.3-168.2-168.2-168.2H303.6c-92.4 0-168.2 75.3-168.2 168.2v309.1c0 92.4 75.3 168.2 168.2 168.2h320.9zM178.2 618.1V309c0-69.4 56.1-125.5 125.5-125.5h320.9c69.4 0 125.5 56.1 125.5 125.5v309.1c0 69.4-56.1 125.5-125.5 125.5h-321c-69.4 0-125.4-56.1-125.4-125.5z" p-id="1500" fill="#8a8a8a"></path><path d="M849.8 295.1v361.5c0 102.7-83.6 186.3-186.3 186.3H279.1v42.7h384.4c126.3 0 229.1-102.8 229.1-229.1V295.1h-42.8zM307.9 361.8h312.3c11.8 0 21.4-9.6 21.4-21.4 0-11.8-9.6-21.4-21.4-21.4H307.9c-11.8 0-21.4 9.6-21.4 21.4 0 11.9 9.6 21.4 21.4 21.4zM307.9 484.6h312.3c11.8 0 21.4-9.6 21.4-21.4 0-11.8-9.6-21.4-21.4-21.4H307.9c-11.8 0-21.4 9.6-21.4 21.4 0 11.9 9.6 21.4 21.4 21.4z" p-id="1501" fill="#8a8a8a"></path><path d="M620.2 607.4c11.8 0 21.4-9.6 21.4-21.4 0-11.8-9.6-21.4-21.4-21.4H307.9c-11.8 0-21.4 9.6-21.4 21.4 0 11.8 9.6 21.4 21.4 21.4h312.3z" p-id="1502" fill="#8a8a8a"></path></svg>
|
||||||
|
After Width: | Height: | Size: 1.2 KiB |
|
After Width: | Height: | Size: 108 KiB |
|
After Width: | Height: | Size: 87 KiB |
|
|
@ -0,0 +1 @@
|
||||||
|
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1715354120346" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="3256" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><path d="M907.1 263.7H118.9c-9.1 0-16.4-7.3-16.4-16.4s7.3-16.4 16.4-16.4H907c9.1 0 16.4 7.3 16.4 16.4s-7.3 16.4-16.3 16.4z" fill="#8a8a8a" p-id="3257"></path><path d="M772.5 928.3H257.4c-27.7 0-50.2-22.5-50.2-50.2V247.2c0-9.1 7.3-16.4 16.4-16.4H801c12.1 0 21.9 9.8 21.9 21.9v625.2c0 27.8-22.6 50.4-50.4 50.4zM240 263.7v614.4c0 9.6 7.8 17.4 17.4 17.4h515.2c9.7 0 17.5-7.9 17.5-17.5V263.7H240zM657.4 131.1H368.6c-9.1 0-16.4-7.3-16.4-16.4s7.3-16.4 16.4-16.4h288.7c9.1 0 16.4 7.3 16.4 16.4s-7.3 16.4-16.3 16.4z" fill="#8a8a8a" p-id="3258"></path><path d="M416 754.5c-9.1 0-16.4-7.3-16.4-16.4V517.8c0-9.1 7.3-16.4 16.4-16.4s16.4 7.3 16.4 16.4V738c0.1 9.1-7.3 16.5-16.4 16.5z" fill="#8a8a8a" p-id="3259"></path><path d="M416 465.2c-9.1 0-16.4-7.3-16.4-16.4v-59.4c0-9.1 7.3-16.4 16.4-16.4s16.4 7.3 16.4 16.4v59.4c0.1 9.1-7.3 16.4-16.4 16.4zM604.9 754.5c-9.1 0-16.4-7.3-16.4-16.4v-67.2c0-9.1 7.3-16.4 16.4-16.4s16.4 7.3 16.4 16.4V738c0 9.1-7.3 16.5-16.4 16.5z" fill="#8a8a8a" opacity=".4" p-id="3260"></path><path d="M604.9 619.1c-9.1 0-16.4-7.3-16.4-16.4V389.4c0-9.1 7.3-16.4 16.4-16.4s16.4 7.3 16.4 16.4v213.3c0 9.1-7.3 16.4-16.4 16.4z" fill="#8a8a8a" p-id="3261"></path></svg>
|
||||||
|
After Width: | Height: | Size: 1.5 KiB |
|
|
@ -0,0 +1 @@
|
||||||
|
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1716345268026" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="5622" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><path d="M956.408445 419.226665a250.670939 250.670939 0 0 0-22.425219-209.609236A263.163526 263.163526 0 0 0 652.490412 85.715535 259.784384 259.784384 0 0 0 457.728923 0.008192a261.422756 261.422756 0 0 0-249.44216 178.582564 258.453206 258.453206 0 0 0-172.848261 123.901894c-57.03583 96.868753-44.031251 219.132275 32.153053 302.279661a250.670939 250.670939 0 0 0 22.32282 209.609237 263.163526 263.163526 0 0 0 281.595213 123.901893A259.067596 259.067596 0 0 0 566.271077 1023.990784a260.60357 260.60357 0 0 0 249.339762-178.889759 258.453206 258.453206 0 0 0 172.848261-123.901893c57.445423-96.868753 44.13365-218.82508-32.050655-302.074865zM566.578272 957.124721c-45.362429 0-89.496079-15.666934-124.516283-44.543243 1.638372-0.921584 4.198329-2.150363 6.143895-3.481541l206.537289-117.757998a32.35785 32.35785 0 0 0 16.895713-29.081105V474.82892l87.243317 49.97035c1.023983 0.307195 1.638372 1.228779 1.638372 2.252762v238.075953c0 105.8798-86.936122 191.689541-193.942303 191.996736zM148.588578 781.102113a189.846373 189.846373 0 0 1-23.346803-128.612213c1.535974 1.023983 4.09593 2.559956 6.143895 3.48154L337.922959 773.729439c10.444622 6.143896 23.346803 6.143896 34.098621 0l252.30931-143.664758v99.531108c0 1.023983-0.307195 1.945567-1.331177 2.559956l-208.892449 118.986778a196.297463 196.297463 0 0 1-265.518686-70.04041zM94.112704 335.97688c22.630015-39.013737 58.367008-68.81163 101.16948-84.171369V494.591784c0 11.7758 6.45109 22.93721 16.793315 28.978707l252.30931 143.767156L377.141493 716.796006a3.174346 3.174346 0 0 1-2.867152 0.307195l-208.892448-118.986777A190.870355 190.870355 0 0 1 94.215102 335.874482z m717.607001 164.861198L559.410394 357.070922 646.653711 307.20297a3.174346 3.174346 0 0 1 2.969549-0.307195l208.892449 118.986777a190.358364 190.358364 0 0 1 70.961994 262.139544 194.556693 194.556693 0 0 1-101.16948 84.171369V529.407192a31.538664 31.538664 0 0 0-16.588518-28.671513z m87.03852-129.329002c-1.74077-1.023983-4.300727-2.559956-6.246294-3.48154l-206.639687-117.757999a34.09862 34.09862 0 0 0-33.996222 0L399.566711 393.934295v-99.531108c0-1.023983 0.307195-1.945567 1.331178-2.559956l208.892449-119.089176a195.990268 195.990268 0 0 1 265.518686 70.450003c22.732414 38.706542 31.129071 84.171369 23.346803 128.305018zM352.258716 548.862861l-87.243317-49.560757a2.457558 2.457558 0 0 1-1.638372-2.252762V258.870991c0-105.8798 87.243317-191.996736 194.556692-191.689541a194.556693 194.556693 0 0 1 124.209089 44.543243c-1.638372 0.921584-4.198329 2.252762-6.143896 3.48154l-206.639687 117.757999a31.948257 31.948257 0 0 0-16.793315 29.081105l-0.307194 286.715126z m47.307995-100.759887L512 384.001664l112.535687 63.998912v127.997824l-112.228492 63.998912-112.535687-63.998912-0.307195-127.997824z" p-id="5623" fill="#707070"></path></svg>
|
||||||
|
After Width: | Height: | Size: 3.0 KiB |
|
After Width: | Height: | Size: 86 KiB |
|
After Width: | Height: | Size: 121 KiB |
|
After Width: | Height: | Size: 7.4 KiB |
|
After Width: | Height: | Size: 10 KiB |
|
After Width: | Height: | Size: 11 KiB |
|
|
@ -0,0 +1 @@
|
||||||
|
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1724297262365" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="1396" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><path d="M707.91 103c16.28 0 29.522 13.007 29.897 29.195l0.009 0.706v111.878a29.96 29.96 0 0 1-0.898 7.3l171.177-0.001c16.28 0 29.522 13.007 29.897 29.195l0.008 0.706v637.12c0 16.278-13.01 29.518-29.2 29.893l-0.705 0.008H270.884c-16.28 0-29.522-13.007-29.897-29.195l-0.008-0.706V787.274c0-16.514 13.389-29.9 29.905-29.9 16.28 0 29.522 13.007 29.897 29.194l0.008 0.706v101.924h577.4V311.88h-577.4v88.787c0 16.278-13.009 29.518-29.2 29.893l-0.705 0.008c-16.28 0-29.522-13.008-29.897-29.195l-0.008-0.706V281.979c0-16.278 13.009-29.518 29.2-29.893l0.705-0.008h408.019a29.916 29.916 0 0 1-0.89-6.593l-0.008-0.706v-81.978H132.808v407.113h385.787L408.223 456.982c-11.36-11.624-11.329-30.143-0.066-41.729l0.554-0.555c11.625-11.358 30.147-11.327 41.734-0.066l0.555 0.554 161.028 164.762c11.244 11.504 11.344 29.793 0.362 41.42l-0.55 0.565-161.027 161.849c-11.648 11.707-30.583 11.757-42.292 0.11-11.524-11.461-11.754-29.979-0.657-41.723l0.546-0.563 111.319-111.89H102.905c-16.28 0-29.522-13.007-29.897-29.195l-0.008-0.705V132.9c0-16.278 13.01-29.518 29.2-29.893l0.705-0.008H707.91z" p-id="1397"></path></svg>
|
||||||
|
After Width: | Height: | Size: 1.4 KiB |
|
|
@ -5,6 +5,7 @@ export interface AppLinkGroup {
|
||||||
// 链接列表
|
// 链接列表
|
||||||
links: AppLink[]
|
links: AppLink[]
|
||||||
}
|
}
|
||||||
|
|
||||||
// APP 链接
|
// APP 链接
|
||||||
export interface AppLink {
|
export interface AppLink {
|
||||||
// 链接名称
|
// 链接名称
|
||||||
|
|
@ -21,6 +22,8 @@ export const enum APP_LINK_TYPE_ENUM {
|
||||||
ACTIVITY_COMBINATION,
|
ACTIVITY_COMBINATION,
|
||||||
// 秒杀活动
|
// 秒杀活动
|
||||||
ACTIVITY_SECKILL,
|
ACTIVITY_SECKILL,
|
||||||
|
// 积分商城活动
|
||||||
|
ACTIVITY_POINT,
|
||||||
// 文章详情
|
// 文章详情
|
||||||
ARTICLE_DETAIL,
|
ARTICLE_DETAIL,
|
||||||
// 优惠券详情
|
// 优惠券详情
|
||||||
|
|
@ -130,6 +133,11 @@ export const APP_LINK_GROUP_LIST = [
|
||||||
path: '/pages/activity/seckill/list',
|
path: '/pages/activity/seckill/list',
|
||||||
type: APP_LINK_TYPE_ENUM.ACTIVITY_SECKILL
|
type: APP_LINK_TYPE_ENUM.ACTIVITY_SECKILL
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: '积分商城活动',
|
||||||
|
path: '/pages/activity/point/list',
|
||||||
|
type: APP_LINK_TYPE_ENUM.ACTIVITY_POINT
|
||||||
|
},
|
||||||
{
|
{
|
||||||
name: '签到中心',
|
name: '签到中心',
|
||||||
path: '/pages/app/sign'
|
path: '/pages/app/sign'
|
||||||
|
|
|
||||||
|
|
@ -54,7 +54,7 @@ const currentLocale = computed(() => localeStore.currentLocale)
|
||||||
<ElConfigProvider
|
<ElConfigProvider
|
||||||
:namespace="variables.elNamespace"
|
:namespace="variables.elNamespace"
|
||||||
:locale="currentLocale.elLocale"
|
:locale="currentLocale.elLocale"
|
||||||
:message="{ max: 1 }"
|
:message="{ max: 5 }"
|
||||||
:size="size"
|
:size="size"
|
||||||
>
|
>
|
||||||
<slot></slot>
|
<slot></slot>
|
||||||
|
|
|
||||||
|
|
@ -10,12 +10,13 @@ const prefixCls = getPrefixCls('content-wrap')
|
||||||
|
|
||||||
defineProps({
|
defineProps({
|
||||||
title: propTypes.string.def(''),
|
title: propTypes.string.def(''),
|
||||||
message: propTypes.string.def('')
|
message: propTypes.string.def(''),
|
||||||
|
bodyStyle: propTypes.object.def({ padding: '10px' })
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<ElCard :class="[prefixCls, 'mb-15px']" shadow="never">
|
<ElCard :body-style="bodyStyle" :class="[prefixCls, 'mb-15px']" shadow="never">
|
||||||
<template v-if="title" #header>
|
<template v-if="title" #header>
|
||||||
<div class="flex items-center">
|
<div class="flex items-center">
|
||||||
<span class="text-16px font-700">{{ title }}</span>
|
<span class="text-16px font-700">{{ title }}</span>
|
||||||
|
|
@ -30,8 +31,6 @@ defineProps({
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
<div>
|
|
||||||
<slot></slot>
|
<slot></slot>
|
||||||
</div>
|
|
||||||
</ElCard>
|
</ElCard>
|
||||||
</template>
|
</template>
|
||||||
|
|
|
||||||
|
|
@ -548,10 +548,10 @@ const inputChange = () => {
|
||||||
<el-form>
|
<el-form>
|
||||||
<el-form-item label="类型">
|
<el-form-item label="类型">
|
||||||
<el-radio-group v-model="cronValue.second.type">
|
<el-radio-group v-model="cronValue.second.type">
|
||||||
<el-radio-button label="0">任意值</el-radio-button>
|
<el-radio-button value="0">任意值</el-radio-button>
|
||||||
<el-radio-button label="1">范围</el-radio-button>
|
<el-radio-button value="1">范围</el-radio-button>
|
||||||
<el-radio-button label="2">间隔</el-radio-button>
|
<el-radio-button value="2">间隔</el-radio-button>
|
||||||
<el-radio-button label="3">指定</el-radio-button>
|
<el-radio-button value="3">指定</el-radio-button>
|
||||||
</el-radio-group>
|
</el-radio-group>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item v-if="cronValue.second.type == '1'" label="范围">
|
<el-form-item v-if="cronValue.second.type == '1'" label="范围">
|
||||||
|
|
@ -607,10 +607,10 @@ const inputChange = () => {
|
||||||
<el-form>
|
<el-form>
|
||||||
<el-form-item label="类型">
|
<el-form-item label="类型">
|
||||||
<el-radio-group v-model="cronValue.minute.type">
|
<el-radio-group v-model="cronValue.minute.type">
|
||||||
<el-radio-button label="0">任意值</el-radio-button>
|
<el-radio-button value="0">任意值</el-radio-button>
|
||||||
<el-radio-button label="1">范围</el-radio-button>
|
<el-radio-button value="1">范围</el-radio-button>
|
||||||
<el-radio-button label="2">间隔</el-radio-button>
|
<el-radio-button value="2">间隔</el-radio-button>
|
||||||
<el-radio-button label="3">指定</el-radio-button>
|
<el-radio-button value="3">指定</el-radio-button>
|
||||||
</el-radio-group>
|
</el-radio-group>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item v-if="cronValue.minute.type == '1'" label="范围">
|
<el-form-item v-if="cronValue.minute.type == '1'" label="范围">
|
||||||
|
|
@ -666,10 +666,10 @@ const inputChange = () => {
|
||||||
<el-form>
|
<el-form>
|
||||||
<el-form-item label="类型">
|
<el-form-item label="类型">
|
||||||
<el-radio-group v-model="cronValue.hour.type">
|
<el-radio-group v-model="cronValue.hour.type">
|
||||||
<el-radio-button label="0">任意值</el-radio-button>
|
<el-radio-button value="0">任意值</el-radio-button>
|
||||||
<el-radio-button label="1">范围</el-radio-button>
|
<el-radio-button value="1">范围</el-radio-button>
|
||||||
<el-radio-button label="2">间隔</el-radio-button>
|
<el-radio-button value="2">间隔</el-radio-button>
|
||||||
<el-radio-button label="3">指定</el-radio-button>
|
<el-radio-button value="3">指定</el-radio-button>
|
||||||
</el-radio-group>
|
</el-radio-group>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item v-if="cronValue.hour.type == '1'" label="范围">
|
<el-form-item v-if="cronValue.hour.type == '1'" label="范围">
|
||||||
|
|
@ -725,12 +725,12 @@ const inputChange = () => {
|
||||||
<el-form>
|
<el-form>
|
||||||
<el-form-item label="类型">
|
<el-form-item label="类型">
|
||||||
<el-radio-group v-model="cronValue.day.type">
|
<el-radio-group v-model="cronValue.day.type">
|
||||||
<el-radio-button label="0">任意值</el-radio-button>
|
<el-radio-button value="0">任意值</el-radio-button>
|
||||||
<el-radio-button label="1">范围</el-radio-button>
|
<el-radio-button value="1">范围</el-radio-button>
|
||||||
<el-radio-button label="2">间隔</el-radio-button>
|
<el-radio-button value="2">间隔</el-radio-button>
|
||||||
<el-radio-button label="3">指定</el-radio-button>
|
<el-radio-button value="3">指定</el-radio-button>
|
||||||
<el-radio-button label="4">本月最后一天</el-radio-button>
|
<el-radio-button value="4">本月最后一天</el-radio-button>
|
||||||
<el-radio-button label="5">不指定</el-radio-button>
|
<el-radio-button value="5">不指定</el-radio-button>
|
||||||
</el-radio-group>
|
</el-radio-group>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item v-if="cronValue.day.type == '1'" label="范围">
|
<el-form-item v-if="cronValue.day.type == '1'" label="范围">
|
||||||
|
|
@ -786,10 +786,10 @@ const inputChange = () => {
|
||||||
<el-form>
|
<el-form>
|
||||||
<el-form-item label="类型">
|
<el-form-item label="类型">
|
||||||
<el-radio-group v-model="cronValue.month.type">
|
<el-radio-group v-model="cronValue.month.type">
|
||||||
<el-radio-button label="0">任意值</el-radio-button>
|
<el-radio-button value="0">任意值</el-radio-button>
|
||||||
<el-radio-button label="1">范围</el-radio-button>
|
<el-radio-button value="1">范围</el-radio-button>
|
||||||
<el-radio-button label="2">间隔</el-radio-button>
|
<el-radio-button value="2">间隔</el-radio-button>
|
||||||
<el-radio-button label="3">指定</el-radio-button>
|
<el-radio-button value="3">指定</el-radio-button>
|
||||||
</el-radio-group>
|
</el-radio-group>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item v-if="cronValue.month.type == '1'" label="范围">
|
<el-form-item v-if="cronValue.month.type == '1'" label="范围">
|
||||||
|
|
@ -846,12 +846,12 @@ const inputChange = () => {
|
||||||
<el-form>
|
<el-form>
|
||||||
<el-form-item label="类型">
|
<el-form-item label="类型">
|
||||||
<el-radio-group v-model="cronValue.week.type">
|
<el-radio-group v-model="cronValue.week.type">
|
||||||
<el-radio-button label="0">任意值</el-radio-button>
|
<el-radio-button value="0">任意值</el-radio-button>
|
||||||
<el-radio-button label="1">范围</el-radio-button>
|
<el-radio-button value="1">范围</el-radio-button>
|
||||||
<el-radio-button label="2">间隔</el-radio-button>
|
<el-radio-button value="2">间隔</el-radio-button>
|
||||||
<el-radio-button label="3">指定</el-radio-button>
|
<el-radio-button value="3">指定</el-radio-button>
|
||||||
<el-radio-button label="4">本月最后一周</el-radio-button>
|
<el-radio-button value="4">本月最后一周</el-radio-button>
|
||||||
<el-radio-button label="5">不指定</el-radio-button>
|
<el-radio-button value="5">不指定</el-radio-button>
|
||||||
</el-radio-group>
|
</el-radio-group>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item v-if="cronValue.week.type == '1'" label="范围">
|
<el-form-item v-if="cronValue.week.type == '1'" label="范围">
|
||||||
|
|
@ -925,11 +925,11 @@ const inputChange = () => {
|
||||||
<el-form>
|
<el-form>
|
||||||
<el-form-item label="类型">
|
<el-form-item label="类型">
|
||||||
<el-radio-group v-model="cronValue.year.type">
|
<el-radio-group v-model="cronValue.year.type">
|
||||||
<el-radio-button label="-1">忽略</el-radio-button>
|
<el-radio-button value="-1">忽略</el-radio-button>
|
||||||
<el-radio-button label="0">任意值</el-radio-button>
|
<el-radio-button value="0">任意值</el-radio-button>
|
||||||
<el-radio-button label="1">范围</el-radio-button>
|
<el-radio-button value="1">范围</el-radio-button>
|
||||||
<el-radio-button label="2">间隔</el-radio-button>
|
<el-radio-button value="2">间隔</el-radio-button>
|
||||||
<el-radio-button label="3">指定</el-radio-button>
|
<el-radio-button value="3">指定</el-radio-button>
|
||||||
</el-radio-group>
|
</el-radio-group>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item v-if="cronValue.year.type == '1'" label="范围">
|
<el-form-item v-if="cronValue.year.type == '1'" label="范围">
|
||||||
|
|
|
||||||
|
|
@ -1,3 +0,0 @@
|
||||||
import DictSelect from './src/DictSelect.vue'
|
|
||||||
|
|
||||||
export { DictSelect }
|
|
||||||
|
|
@ -1,46 +0,0 @@
|
||||||
<!-- 数据字典 Select 选择器 -->
|
|
||||||
<template>
|
|
||||||
<el-select class="w-1/1" v-bind="attrs">
|
|
||||||
<template v-if="valueType === 'int'">
|
|
||||||
<el-option
|
|
||||||
v-for="(dict, index) in getIntDictOptions(dictType)"
|
|
||||||
:key="index"
|
|
||||||
:label="dict.label"
|
|
||||||
:value="dict.value"
|
|
||||||
/>
|
|
||||||
</template>
|
|
||||||
<template v-if="valueType === 'str'">
|
|
||||||
<el-option
|
|
||||||
v-for="(dict, index) in getStrDictOptions(dictType)"
|
|
||||||
:key="index"
|
|
||||||
:label="dict.label"
|
|
||||||
:value="dict.value"
|
|
||||||
/>
|
|
||||||
</template>
|
|
||||||
<template v-if="valueType === 'bool'">
|
|
||||||
<el-option
|
|
||||||
v-for="(dict, index) in getBoolDictOptions(dictType)"
|
|
||||||
:key="index"
|
|
||||||
:label="dict.label"
|
|
||||||
:value="dict.value"
|
|
||||||
/>
|
|
||||||
</template>
|
|
||||||
</el-select>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script lang="ts" setup>
|
|
||||||
import { getBoolDictOptions, getIntDictOptions, getStrDictOptions } from '@/utils/dict'
|
|
||||||
|
|
||||||
// 接受父组件参数
|
|
||||||
interface Props {
|
|
||||||
dictType: string // 字典类型
|
|
||||||
valueType: string // 字典值类型
|
|
||||||
}
|
|
||||||
|
|
||||||
withDefaults(defineProps<Props>(), {
|
|
||||||
dictType: '',
|
|
||||||
valueType: 'str'
|
|
||||||
})
|
|
||||||
const attrs = useAttrs()
|
|
||||||
defineOptions({ name: 'DictSelect' })
|
|
||||||
</script>
|
|
||||||
|
|
@ -1,8 +1,9 @@
|
||||||
<script lang="tsx">
|
<script lang="tsx">
|
||||||
import { defineComponent, PropType, ref } from 'vue'
|
import { computed, defineComponent, PropType } from 'vue'
|
||||||
import { isHexColor } from '@/utils/color'
|
import { isHexColor } from '@/utils/color'
|
||||||
import { ElTag } from 'element-plus'
|
import { ElTag } from 'element-plus'
|
||||||
import { DictDataType, getDictOptions } from '@/utils/dict'
|
import { DictDataType, getDictOptions } from '@/utils/dict'
|
||||||
|
import { isArray, isBoolean, isNumber, isString } from '@/utils/is'
|
||||||
|
|
||||||
export default defineComponent({
|
export default defineComponent({
|
||||||
name: 'DictTag',
|
name: 'DictTag',
|
||||||
|
|
@ -12,49 +13,78 @@ export default defineComponent({
|
||||||
required: true
|
required: true
|
||||||
},
|
},
|
||||||
value: {
|
value: {
|
||||||
type: [String, Number, Boolean] as PropType<string | number | boolean>,
|
type: [String, Number, Boolean, Array],
|
||||||
required: true
|
required: true
|
||||||
|
},
|
||||||
|
// 字符串分隔符 只有当 props.value 传入值为字符串时有效
|
||||||
|
separator: {
|
||||||
|
type: String as PropType<string>,
|
||||||
|
default: ','
|
||||||
|
},
|
||||||
|
// 每个 tag 之间的间隔,默认为 5px,参考的 el-row 的 gutter
|
||||||
|
gutter: {
|
||||||
|
type: String as PropType<string>,
|
||||||
|
default: '5px'
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
setup(props) {
|
setup(props) {
|
||||||
const dictData = ref<DictDataType>()
|
const valueArr: any = computed(() => {
|
||||||
const getDictObj = (dictType: string, value: string) => {
|
// 1. 是 Number 类型和 Boolean 类型的情况
|
||||||
const dictOptions = getDictOptions(dictType)
|
if (isNumber(props.value) || isBoolean(props.value)) {
|
||||||
dictOptions.forEach((dict: DictDataType) => {
|
return [String(props.value)]
|
||||||
if (dict.value === value) {
|
|
||||||
if (dict.colorType + '' === 'primary' || dict.colorType + '' === 'default') {
|
|
||||||
dict.colorType = ''
|
|
||||||
}
|
}
|
||||||
dictData.value = dict
|
// 2. 是字符串(进一步判断是否有包含分隔符号 -> props.sepSymbol )
|
||||||
|
else if (isString(props.value)) {
|
||||||
|
return props.value.split(props.separator)
|
||||||
}
|
}
|
||||||
|
// 3. 数组
|
||||||
|
else if (isArray(props.value)) {
|
||||||
|
return props.value.map(String)
|
||||||
|
}
|
||||||
|
return []
|
||||||
})
|
})
|
||||||
}
|
const renderDictTag = () => {
|
||||||
const rederDictTag = () => {
|
|
||||||
if (!props.type) {
|
if (!props.type) {
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
// 解决自定义字典标签值为零时标签不渲染的问题
|
// 解决自定义字典标签值为零时标签不渲染的问题
|
||||||
if (props.value === undefined || props.value === null) {
|
if (props.value === undefined || props.value === null || props.value === '') {
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
getDictObj(props.type, props.value.toString())
|
const dictOptions = getDictOptions(props.type)
|
||||||
// 添加标签的文字颜色为白色,解决自定义背景颜色时标签文字看不清的问题
|
|
||||||
return (
|
return (
|
||||||
<ElTag
|
<div
|
||||||
style={dictData.value?.cssClass ? 'color: #fff' : ''}
|
class="dict-tag"
|
||||||
type={dictData.value?.colorType}
|
style={{
|
||||||
color={
|
display: 'inline-flex',
|
||||||
dictData.value?.cssClass && isHexColor(dictData.value?.cssClass)
|
gap: props.gutter,
|
||||||
? dictData.value?.cssClass
|
justifyContent: 'center',
|
||||||
: ''
|
alignItems: 'center'
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{dictOptions.map((dict: DictDataType) => {
|
||||||
|
if (valueArr.value.includes(dict.value)) {
|
||||||
|
if (dict.colorType + '' === 'primary' || dict.colorType + '' === 'default') {
|
||||||
|
dict.colorType = ''
|
||||||
}
|
}
|
||||||
|
return (
|
||||||
|
// 添加标签的文字颜色为白色,解决自定义背景颜色时标签文字看不清的问题
|
||||||
|
<ElTag
|
||||||
|
style={dict?.cssClass ? 'color: #fff' : ''}
|
||||||
|
type={dict?.colorType || null}
|
||||||
|
color={dict?.cssClass && isHexColor(dict?.cssClass) ? dict?.cssClass : ''}
|
||||||
disableTransitions={true}
|
disableTransitions={true}
|
||||||
>
|
>
|
||||||
{dictData.value?.label}
|
{dict?.label}
|
||||||
</ElTag>
|
</ElTag>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
return () => rederDictTag()
|
})}
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
return () => renderDictTag()
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
|
||||||
|
|
@ -165,6 +165,7 @@ $toolbar-position: -55px;
|
||||||
width: 80px;
|
width: 80px;
|
||||||
height: 25px;
|
height: 25px;
|
||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
|
color: #6a6a6a;
|
||||||
line-height: 25px;
|
line-height: 25px;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
background: #fff;
|
background: #fff;
|
||||||
|
|
|
||||||
|
|
@ -11,8 +11,8 @@
|
||||||
<el-form :model="formData" label-width="80px">
|
<el-form :model="formData" label-width="80px">
|
||||||
<el-form-item label="组件背景" prop="bgType">
|
<el-form-item label="组件背景" prop="bgType">
|
||||||
<el-radio-group v-model="formData.bgType">
|
<el-radio-group v-model="formData.bgType">
|
||||||
<el-radio label="color">纯色</el-radio>
|
<el-radio value="color">纯色</el-radio>
|
||||||
<el-radio label="img">图片</el-radio>
|
<el-radio value="img">图片</el-radio>
|
||||||
</el-radio-group>
|
</el-radio-group>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item label="选择颜色" prop="bgColor" v-if="formData.bgType === 'color'">
|
<el-form-item label="选择颜色" prop="bgColor" v-if="formData.bgType === 'color'">
|
||||||
|
|
|
||||||
|
|
@ -95,6 +95,7 @@ const handleCloneComponent = (component: DiyComponent<any>) => {
|
||||||
.editor-left {
|
.editor-left {
|
||||||
z-index: 1;
|
z-index: 1;
|
||||||
flex-shrink: 0;
|
flex-shrink: 0;
|
||||||
|
user-select: none;
|
||||||
box-shadow: 8px 0 8px -8px rgb(0 0 0 / 12%);
|
box-shadow: 8px 0 8px -8px rgb(0 0 0 / 12%);
|
||||||
|
|
||||||
:deep(.el-collapse) {
|
:deep(.el-collapse) {
|
||||||
|
|
|
||||||
|
|
@ -5,12 +5,12 @@
|
||||||
<el-form-item label="样式" prop="type">
|
<el-form-item label="样式" prop="type">
|
||||||
<el-radio-group v-model="formData.type">
|
<el-radio-group v-model="formData.type">
|
||||||
<el-tooltip class="item" content="默认" placement="bottom">
|
<el-tooltip class="item" content="默认" placement="bottom">
|
||||||
<el-radio-button label="default">
|
<el-radio-button value="default">
|
||||||
<Icon icon="system-uicons:carousel" />
|
<Icon icon="system-uicons:carousel" />
|
||||||
</el-radio-button>
|
</el-radio-button>
|
||||||
</el-tooltip>
|
</el-tooltip>
|
||||||
<el-tooltip class="item" content="卡片" placement="bottom">
|
<el-tooltip class="item" content="卡片" placement="bottom">
|
||||||
<el-radio-button label="card">
|
<el-radio-button value="card">
|
||||||
<Icon icon="ic:round-view-carousel" />
|
<Icon icon="ic:round-view-carousel" />
|
||||||
</el-radio-button>
|
</el-radio-button>
|
||||||
</el-tooltip>
|
</el-tooltip>
|
||||||
|
|
@ -18,8 +18,8 @@
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item label="指示器" prop="indicator">
|
<el-form-item label="指示器" prop="indicator">
|
||||||
<el-radio-group v-model="formData.indicator">
|
<el-radio-group v-model="formData.indicator">
|
||||||
<el-radio label="dot">小圆点</el-radio>
|
<el-radio value="dot">小圆点</el-radio>
|
||||||
<el-radio label="number">数字</el-radio>
|
<el-radio value="number">数字</el-radio>
|
||||||
</el-radio-group>
|
</el-radio-group>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item label="是否轮播" prop="autoplay">
|
<el-form-item label="是否轮播" prop="autoplay">
|
||||||
|
|
@ -43,8 +43,8 @@
|
||||||
<template #default="{ element }">
|
<template #default="{ element }">
|
||||||
<el-form-item label="类型" prop="type" class="m-b-8px!" label-width="40px">
|
<el-form-item label="类型" prop="type" class="m-b-8px!" label-width="40px">
|
||||||
<el-radio-group v-model="element.type">
|
<el-radio-group v-model="element.type">
|
||||||
<el-radio label="img">图片</el-radio>
|
<el-radio value="img">图片</el-radio>
|
||||||
<el-radio label="video">视频</el-radio>
|
<el-radio value="video">视频</el-radio>
|
||||||
</el-radio-group>
|
</el-radio-group>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item
|
<el-form-item
|
||||||
|
|
|
||||||
|
|
@ -26,17 +26,17 @@
|
||||||
<el-form-item label="列数" prop="type">
|
<el-form-item label="列数" prop="type">
|
||||||
<el-radio-group v-model="formData.columns">
|
<el-radio-group v-model="formData.columns">
|
||||||
<el-tooltip class="item" content="一列" placement="bottom">
|
<el-tooltip class="item" content="一列" placement="bottom">
|
||||||
<el-radio-button :label="1">
|
<el-radio-button :value="1">
|
||||||
<Icon icon="fluent:text-column-one-24-filled" />
|
<Icon icon="fluent:text-column-one-24-filled" />
|
||||||
</el-radio-button>
|
</el-radio-button>
|
||||||
</el-tooltip>
|
</el-tooltip>
|
||||||
<el-tooltip class="item" content="二列" placement="bottom">
|
<el-tooltip class="item" content="二列" placement="bottom">
|
||||||
<el-radio-button :label="2">
|
<el-radio-button :value="2">
|
||||||
<Icon icon="fluent:text-column-two-24-filled" />
|
<Icon icon="fluent:text-column-two-24-filled" />
|
||||||
</el-radio-button>
|
</el-radio-button>
|
||||||
</el-tooltip>
|
</el-tooltip>
|
||||||
<el-tooltip class="item" content="三列" placement="bottom">
|
<el-tooltip class="item" content="三列" placement="bottom">
|
||||||
<el-radio-button :label="3">
|
<el-radio-button :value="3">
|
||||||
<Icon icon="fluent:text-column-three-24-filled" />
|
<Icon icon="fluent:text-column-three-24-filled" />
|
||||||
</el-radio-button>
|
</el-radio-button>
|
||||||
</el-tooltip>
|
</el-tooltip>
|
||||||
|
|
|
||||||
|
|
@ -11,7 +11,7 @@
|
||||||
:key="index"
|
:key="index"
|
||||||
:content="item.text"
|
:content="item.text"
|
||||||
>
|
>
|
||||||
<el-radio-button :label="item.type">
|
<el-radio-button :value="item.type">
|
||||||
<Icon :icon="item.icon" />
|
<Icon :icon="item.icon" />
|
||||||
</el-radio-button>
|
</el-radio-button>
|
||||||
</el-tooltip>
|
</el-tooltip>
|
||||||
|
|
@ -24,12 +24,12 @@
|
||||||
<el-form-item label="左右边距" prop="paddingType">
|
<el-form-item label="左右边距" prop="paddingType">
|
||||||
<el-radio-group v-model="formData!.paddingType">
|
<el-radio-group v-model="formData!.paddingType">
|
||||||
<el-tooltip content="无边距" placement="top">
|
<el-tooltip content="无边距" placement="top">
|
||||||
<el-radio-button label="none">
|
<el-radio-button value="none">
|
||||||
<Icon icon="tabler:box-padding" />
|
<Icon icon="tabler:box-padding" />
|
||||||
</el-radio-button>
|
</el-radio-button>
|
||||||
</el-tooltip>
|
</el-tooltip>
|
||||||
<el-tooltip content="左右留边" placement="top">
|
<el-tooltip content="左右留边" placement="top">
|
||||||
<el-radio-button label="horizontal">
|
<el-radio-button value="horizontal">
|
||||||
<Icon icon="vaadin:padding" />
|
<Icon icon="vaadin:padding" />
|
||||||
</el-radio-button>
|
</el-radio-button>
|
||||||
</el-tooltip>
|
</el-tooltip>
|
||||||
|
|
|
||||||
|
|
@ -44,7 +44,7 @@ defineOptions({ name: 'FloatingActionButton' })
|
||||||
defineProps<{ property: FloatingActionButtonProperty }>()
|
defineProps<{ property: FloatingActionButtonProperty }>()
|
||||||
|
|
||||||
// 是否展开
|
// 是否展开
|
||||||
const expanded = ref(true)
|
const expanded = ref(false)
|
||||||
// 处理展开/折叠
|
// 处理展开/折叠
|
||||||
const handleToggleFab = () => {
|
const handleToggleFab = () => {
|
||||||
expanded.value = !expanded.value
|
expanded.value = !expanded.value
|
||||||
|
|
|
||||||
|
|
@ -3,8 +3,8 @@
|
||||||
<el-card header="按钮配置" class="property-group" shadow="never">
|
<el-card header="按钮配置" class="property-group" shadow="never">
|
||||||
<el-form-item label="展开方向" prop="direction">
|
<el-form-item label="展开方向" prop="direction">
|
||||||
<el-radio-group v-model="formData.direction">
|
<el-radio-group v-model="formData.direction">
|
||||||
<el-radio label="vertical">垂直</el-radio>
|
<el-radio value="vertical">垂直</el-radio>
|
||||||
<el-radio label="horizontal">水平</el-radio>
|
<el-radio value="horizontal">水平</el-radio>
|
||||||
</el-radio-group>
|
</el-radio-group>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item label="显示文字" prop="showText">
|
<el-form-item label="显示文字" prop="showText">
|
||||||
|
|
|
||||||
|
|
@ -4,8 +4,8 @@
|
||||||
<el-form label-width="80px" :model="formData" class="m-t-8px">
|
<el-form label-width="80px" :model="formData" class="m-t-8px">
|
||||||
<el-form-item label="每行数量" prop="column">
|
<el-form-item label="每行数量" prop="column">
|
||||||
<el-radio-group v-model="formData.column">
|
<el-radio-group v-model="formData.column">
|
||||||
<el-radio :label="3">3个</el-radio>
|
<el-radio :value="3">3个</el-radio>
|
||||||
<el-radio :label="4">4个</el-radio>
|
<el-radio :value="4">4个</el-radio>
|
||||||
</el-radio-group>
|
</el-radio-group>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -4,21 +4,21 @@
|
||||||
<el-form label-width="80px" :model="formData" class="m-t-8px">
|
<el-form label-width="80px" :model="formData" class="m-t-8px">
|
||||||
<el-form-item label="布局" prop="layout">
|
<el-form-item label="布局" prop="layout">
|
||||||
<el-radio-group v-model="formData.layout">
|
<el-radio-group v-model="formData.layout">
|
||||||
<el-radio label="iconText">图标+文字</el-radio>
|
<el-radio value="iconText">图标+文字</el-radio>
|
||||||
<el-radio label="icon">仅图标</el-radio>
|
<el-radio value="icon">仅图标</el-radio>
|
||||||
</el-radio-group>
|
</el-radio-group>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item label="行数" prop="row">
|
<el-form-item label="行数" prop="row">
|
||||||
<el-radio-group v-model="formData.row">
|
<el-radio-group v-model="formData.row">
|
||||||
<el-radio :label="1">1行</el-radio>
|
<el-radio :value="1">1行</el-radio>
|
||||||
<el-radio :label="2">2行</el-radio>
|
<el-radio :value="2">2行</el-radio>
|
||||||
</el-radio-group>
|
</el-radio-group>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item label="列数" prop="column">
|
<el-form-item label="列数" prop="column">
|
||||||
<el-radio-group v-model="formData.column">
|
<el-radio-group v-model="formData.column">
|
||||||
<el-radio :label="3">3列</el-radio>
|
<el-radio :value="3">3列</el-radio>
|
||||||
<el-radio :label="4">4列</el-radio>
|
<el-radio :value="4">4列</el-radio>
|
||||||
<el-radio :label="5">5列</el-radio>
|
<el-radio :value="5">5列</el-radio>
|
||||||
</el-radio-group>
|
</el-radio-group>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -14,9 +14,9 @@
|
||||||
<template v-if="selectedHotAreaIndex === cellIndex">
|
<template v-if="selectedHotAreaIndex === cellIndex">
|
||||||
<el-form-item label="类型" :prop="`cell[${cellIndex}].type`">
|
<el-form-item label="类型" :prop="`cell[${cellIndex}].type`">
|
||||||
<el-radio-group v-model="cell.type">
|
<el-radio-group v-model="cell.type">
|
||||||
<el-radio label="text">文字</el-radio>
|
<el-radio value="text">文字</el-radio>
|
||||||
<el-radio label="image">图片</el-radio>
|
<el-radio value="image">图片</el-radio>
|
||||||
<el-radio label="search">搜索框</el-radio>
|
<el-radio value="search">搜索框</el-radio>
|
||||||
</el-radio-group>
|
</el-radio-group>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<!-- 1. 文字 -->
|
<!-- 1. 文字 -->
|
||||||
|
|
|
||||||
|
|
@ -2,27 +2,27 @@
|
||||||
<el-form label-width="80px" :model="formData" :rules="rules">
|
<el-form label-width="80px" :model="formData" :rules="rules">
|
||||||
<el-form-item label="样式" prop="styleType">
|
<el-form-item label="样式" prop="styleType">
|
||||||
<el-radio-group v-model="formData!.styleType">
|
<el-radio-group v-model="formData!.styleType">
|
||||||
<el-radio label="normal">标准</el-radio>
|
<el-radio value="normal">标准</el-radio>
|
||||||
<el-tooltip
|
<el-tooltip
|
||||||
content="沉侵式头部仅支持微信小程序、APP,建议页面第一个组件为图片展示类组件"
|
content="沉侵式头部仅支持微信小程序、APP,建议页面第一个组件为图片展示类组件"
|
||||||
placement="top"
|
placement="top"
|
||||||
>
|
>
|
||||||
<el-radio label="inner">沉浸式</el-radio>
|
<el-radio value="inner">沉浸式</el-radio>
|
||||||
</el-tooltip>
|
</el-tooltip>
|
||||||
</el-radio-group>
|
</el-radio-group>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item label="常驻显示" prop="alwaysShow" v-if="formData.styleType === 'inner'">
|
<el-form-item label="常驻显示" prop="alwaysShow" v-if="formData.styleType === 'inner'">
|
||||||
<el-radio-group v-model="formData!.alwaysShow">
|
<el-radio-group v-model="formData!.alwaysShow">
|
||||||
<el-radio :label="false">关闭</el-radio>
|
<el-radio :value="false">关闭</el-radio>
|
||||||
<el-tooltip content="常驻显示关闭后,头部小组件将在页面滑动时淡入" placement="top">
|
<el-tooltip content="常驻显示关闭后,头部小组件将在页面滑动时淡入" placement="top">
|
||||||
<el-radio :label="true">开启</el-radio>
|
<el-radio :value="true">开启</el-radio>
|
||||||
</el-tooltip>
|
</el-tooltip>
|
||||||
</el-radio-group>
|
</el-radio-group>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item label="背景类型" prop="bgType">
|
<el-form-item label="背景类型" prop="bgType">
|
||||||
<el-radio-group v-model="formData.bgType">
|
<el-radio-group v-model="formData.bgType">
|
||||||
<el-radio label="color">纯色</el-radio>
|
<el-radio value="color">纯色</el-radio>
|
||||||
<el-radio label="img">图片</el-radio>
|
<el-radio value="img">图片</el-radio>
|
||||||
</el-radio-group>
|
</el-radio-group>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item label="背景颜色" prop="bgColor" v-if="formData.bgType === 'color'">
|
<el-form-item label="背景颜色" prop="bgColor" v-if="formData.bgType === 'color'">
|
||||||
|
|
|
||||||
|
|
@ -11,10 +11,10 @@
|
||||||
<el-form-item label="显示次数" :prop="`list[${index}].showType`">
|
<el-form-item label="显示次数" :prop="`list[${index}].showType`">
|
||||||
<el-radio-group v-model="element.showType">
|
<el-radio-group v-model="element.showType">
|
||||||
<el-tooltip content="只显示一次,下次打开时不显示" placement="bottom">
|
<el-tooltip content="只显示一次,下次打开时不显示" placement="bottom">
|
||||||
<el-radio label="once">一次</el-radio>
|
<el-radio value="once">一次</el-radio>
|
||||||
</el-tooltip>
|
</el-tooltip>
|
||||||
<el-tooltip content="每次打开时都会显示" placement="bottom">
|
<el-tooltip content="每次打开时都会显示" placement="bottom">
|
||||||
<el-radio label="always">不限</el-radio>
|
<el-radio value="always">不限</el-radio>
|
||||||
</el-tooltip>
|
</el-tooltip>
|
||||||
</el-radio-group>
|
</el-radio-group>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
|
|
|
||||||
|
|
@ -67,15 +67,15 @@
|
||||||
class="text-16px"
|
class="text-16px"
|
||||||
:style="{ color: property.fields.price.color }"
|
:style="{ color: property.fields.price.color }"
|
||||||
>
|
>
|
||||||
¥{{ spu.price }}
|
¥{{ fenToYuan(spu.price as any) }}
|
||||||
</span>
|
</span>
|
||||||
<!-- 市场价 -->
|
<!-- 市场价 -->
|
||||||
<span
|
<span
|
||||||
v-if="property.fields.marketPrice.show && spu.marketPrice"
|
v-if="property.fields.marketPrice.show && spu.marketPrice"
|
||||||
class="ml-4px text-10px line-through"
|
class="ml-4px text-10px line-through"
|
||||||
:style="{ color: property.fields.marketPrice.color }"
|
:style="{ color: property.fields.marketPrice.color }"
|
||||||
>¥{{ spu.marketPrice }}</span
|
>¥{{ fenToYuan(spu.marketPrice) }}
|
||||||
>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="text-12px">
|
<div class="text-12px">
|
||||||
<!-- 销量 -->
|
<!-- 销量 -->
|
||||||
|
|
@ -117,6 +117,7 @@
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { ProductCardProperty } from './config'
|
import { ProductCardProperty } from './config'
|
||||||
import * as ProductSpuApi from '@/api/mall/product/spu'
|
import * as ProductSpuApi from '@/api/mall/product/spu'
|
||||||
|
import { fenToYuan } from '../../../../../utils'
|
||||||
|
|
||||||
/** 商品卡片 */
|
/** 商品卡片 */
|
||||||
defineOptions({ name: 'ProductCard' })
|
defineOptions({ name: 'ProductCard' })
|
||||||
|
|
|
||||||
|
|
@ -8,17 +8,17 @@
|
||||||
<el-form-item label="布局" prop="type">
|
<el-form-item label="布局" prop="type">
|
||||||
<el-radio-group v-model="formData.layoutType">
|
<el-radio-group v-model="formData.layoutType">
|
||||||
<el-tooltip class="item" content="单列大图" placement="bottom">
|
<el-tooltip class="item" content="单列大图" placement="bottom">
|
||||||
<el-radio-button label="oneColBigImg">
|
<el-radio-button value="oneColBigImg">
|
||||||
<Icon icon="fluent:text-column-one-24-filled" />
|
<Icon icon="fluent:text-column-one-24-filled" />
|
||||||
</el-radio-button>
|
</el-radio-button>
|
||||||
</el-tooltip>
|
</el-tooltip>
|
||||||
<el-tooltip class="item" content="单列小图" placement="bottom">
|
<el-tooltip class="item" content="单列小图" placement="bottom">
|
||||||
<el-radio-button label="oneColSmallImg">
|
<el-radio-button value="oneColSmallImg">
|
||||||
<Icon icon="fluent:text-column-two-left-24-filled" />
|
<Icon icon="fluent:text-column-two-left-24-filled" />
|
||||||
</el-radio-button>
|
</el-radio-button>
|
||||||
</el-tooltip>
|
</el-tooltip>
|
||||||
<el-tooltip class="item" content="双列" placement="bottom">
|
<el-tooltip class="item" content="双列" placement="bottom">
|
||||||
<el-radio-button label="twoCol">
|
<el-radio-button value="twoCol">
|
||||||
<Icon icon="fluent:text-column-two-24-filled" />
|
<Icon icon="fluent:text-column-two-24-filled" />
|
||||||
</el-radio-button>
|
</el-radio-button>
|
||||||
</el-tooltip>
|
</el-tooltip>
|
||||||
|
|
@ -74,8 +74,8 @@
|
||||||
<el-card header="按钮" class="property-group" shadow="never">
|
<el-card header="按钮" class="property-group" shadow="never">
|
||||||
<el-form-item label="按钮类型" prop="btnBuy.type">
|
<el-form-item label="按钮类型" prop="btnBuy.type">
|
||||||
<el-radio-group v-model="formData.btnBuy.type">
|
<el-radio-group v-model="formData.btnBuy.type">
|
||||||
<el-radio-button label="text">文字</el-radio-button>
|
<el-radio-button value="text">文字</el-radio-button>
|
||||||
<el-radio-button label="img">图片</el-radio-button>
|
<el-radio-button value="img">图片</el-radio-button>
|
||||||
</el-radio-group>
|
</el-radio-group>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<template v-if="formData.btnBuy.type === 'text'">
|
<template v-if="formData.btnBuy.type === 'text'">
|
||||||
|
|
|
||||||
|
|
@ -54,7 +54,7 @@
|
||||||
class="text-12px"
|
class="text-12px"
|
||||||
:style="{ color: property.fields.price.color }"
|
:style="{ color: property.fields.price.color }"
|
||||||
>
|
>
|
||||||
¥{{ spu.price }}
|
¥{{ fenToYuan(spu.price) }}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -65,6 +65,7 @@
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { ProductListProperty } from './config'
|
import { ProductListProperty } from './config'
|
||||||
import * as ProductSpuApi from '@/api/mall/product/spu'
|
import * as ProductSpuApi from '@/api/mall/product/spu'
|
||||||
|
import { fenToYuan } from '@/utils'
|
||||||
|
|
||||||
/** 商品栏 */
|
/** 商品栏 */
|
||||||
defineOptions({ name: 'ProductList' })
|
defineOptions({ name: 'ProductList' })
|
||||||
|
|
|
||||||
|
|
@ -8,17 +8,17 @@
|
||||||
<el-form-item label="布局" prop="type">
|
<el-form-item label="布局" prop="type">
|
||||||
<el-radio-group v-model="formData.layoutType">
|
<el-radio-group v-model="formData.layoutType">
|
||||||
<el-tooltip class="item" content="双列" placement="bottom">
|
<el-tooltip class="item" content="双列" placement="bottom">
|
||||||
<el-radio-button label="twoCol">
|
<el-radio-button value="twoCol">
|
||||||
<Icon icon="fluent:text-column-two-24-filled" />
|
<Icon icon="fluent:text-column-two-24-filled" />
|
||||||
</el-radio-button>
|
</el-radio-button>
|
||||||
</el-tooltip>
|
</el-tooltip>
|
||||||
<el-tooltip class="item" content="三列" placement="bottom">
|
<el-tooltip class="item" content="三列" placement="bottom">
|
||||||
<el-radio-button label="threeCol">
|
<el-radio-button value="threeCol">
|
||||||
<Icon icon="fluent:text-column-three-24-filled" />
|
<Icon icon="fluent:text-column-three-24-filled" />
|
||||||
</el-radio-button>
|
</el-radio-button>
|
||||||
</el-tooltip>
|
</el-tooltip>
|
||||||
<el-tooltip class="item" content="水平滑动" placement="bottom">
|
<el-tooltip class="item" content="水平滑动" placement="bottom">
|
||||||
<el-radio-button label="horizSwiper">
|
<el-radio-button value="horizSwiper">
|
||||||
<Icon icon="system-uicons:carousel" />
|
<Icon icon="system-uicons:carousel" />
|
||||||
</el-radio-button>
|
</el-radio-button>
|
||||||
</el-tooltip>
|
</el-tooltip>
|
||||||
|
|
|
||||||
|
|
@ -3,13 +3,21 @@ import { ComponentStyle, DiyComponent } from '@/components/DiyEditor/util'
|
||||||
/** 拼团属性 */
|
/** 拼团属性 */
|
||||||
export interface PromotionCombinationProperty {
|
export interface PromotionCombinationProperty {
|
||||||
// 布局类型:单列 | 三列
|
// 布局类型:单列 | 三列
|
||||||
layoutType: 'oneCol' | 'threeCol'
|
layoutType: 'oneColBigImg' | 'oneColSmallImg' | 'twoCol'
|
||||||
// 商品字段
|
// 商品字段
|
||||||
fields: {
|
fields: {
|
||||||
// 商品名称
|
// 商品名称
|
||||||
name: PromotionCombinationFieldProperty
|
name: PromotionCombinationFieldProperty
|
||||||
|
// 商品简介
|
||||||
|
introduction: PromotionCombinationFieldProperty
|
||||||
// 商品价格
|
// 商品价格
|
||||||
price: PromotionCombinationFieldProperty
|
price: PromotionCombinationFieldProperty
|
||||||
|
// 市场价
|
||||||
|
marketPrice: PromotionCombinationFieldProperty
|
||||||
|
// 商品销量
|
||||||
|
salesCount: PromotionCombinationFieldProperty
|
||||||
|
// 商品库存
|
||||||
|
stock: PromotionCombinationFieldProperty
|
||||||
}
|
}
|
||||||
// 角标
|
// 角标
|
||||||
badge: {
|
badge: {
|
||||||
|
|
@ -18,6 +26,19 @@ export interface PromotionCombinationProperty {
|
||||||
// 角标图片
|
// 角标图片
|
||||||
imgUrl: string
|
imgUrl: string
|
||||||
}
|
}
|
||||||
|
// 按钮
|
||||||
|
btnBuy: {
|
||||||
|
// 类型:文字 | 图片
|
||||||
|
type: 'text' | 'img'
|
||||||
|
// 文字
|
||||||
|
text: string
|
||||||
|
// 文字按钮:背景渐变起始颜色
|
||||||
|
bgBeginColor: string
|
||||||
|
// 文字按钮:背景渐变结束颜色
|
||||||
|
bgEndColor: string
|
||||||
|
// 图片按钮:图片地址
|
||||||
|
imgUrl: string
|
||||||
|
}
|
||||||
// 上圆角
|
// 上圆角
|
||||||
borderRadiusTop: number
|
borderRadiusTop: number
|
||||||
// 下圆角
|
// 下圆角
|
||||||
|
|
@ -25,7 +46,7 @@ export interface PromotionCombinationProperty {
|
||||||
// 间距
|
// 间距
|
||||||
space: number
|
space: number
|
||||||
// 拼团活动编号
|
// 拼团活动编号
|
||||||
activityId: number
|
activityIds: number[]
|
||||||
// 组件样式
|
// 组件样式
|
||||||
style: ComponentStyle
|
style: ComponentStyle
|
||||||
}
|
}
|
||||||
|
|
@ -44,12 +65,23 @@ export const component = {
|
||||||
name: '拼团',
|
name: '拼团',
|
||||||
icon: 'mdi:account-group',
|
icon: 'mdi:account-group',
|
||||||
property: {
|
property: {
|
||||||
layoutType: 'oneCol',
|
layoutType: 'oneColBigImg',
|
||||||
fields: {
|
fields: {
|
||||||
name: { show: true, color: '#000' },
|
name: { show: true, color: '#000' },
|
||||||
price: { show: true, color: '#ff3000' }
|
introduction: { show: true, color: '#999' },
|
||||||
|
price: { show: true, color: '#ff3000' },
|
||||||
|
marketPrice: { show: true, color: '#c4c4c4' },
|
||||||
|
salesCount: { show: true, color: '#c4c4c4' },
|
||||||
|
stock: { show: false, color: '#c4c4c4' }
|
||||||
},
|
},
|
||||||
badge: { show: false, imgUrl: '' },
|
badge: { show: false, imgUrl: '' },
|
||||||
|
btnBuy: {
|
||||||
|
type: 'text',
|
||||||
|
text: '去拼团',
|
||||||
|
bgBeginColor: '#FF6000',
|
||||||
|
bgEndColor: '#FE832A',
|
||||||
|
imgUrl: ''
|
||||||
|
},
|
||||||
borderRadiusTop: 8,
|
borderRadiusTop: 8,
|
||||||
borderRadiusBottom: 8,
|
borderRadiusBottom: 8,
|
||||||
space: 8,
|
space: 8,
|
||||||
|
|
|
||||||
|
|
@ -1,18 +1,10 @@
|
||||||
<template>
|
<template>
|
||||||
<el-scrollbar class="z-1 min-h-30px" wrap-class="w-full" ref="containerRef">
|
<div :class="`box-content min-h-30px w-full flex flex-row flex-wrap`" ref="containerRef">
|
||||||
<!-- 商品网格 -->
|
|
||||||
<div
|
|
||||||
class="grid overflow-x-auto"
|
|
||||||
:style="{
|
|
||||||
gridGap: `${property.space}px`,
|
|
||||||
gridTemplateColumns,
|
|
||||||
width: scrollbarWidth
|
|
||||||
}"
|
|
||||||
>
|
|
||||||
<!-- 商品 -->
|
|
||||||
<div
|
<div
|
||||||
class="relative box-content flex flex-row flex-wrap overflow-hidden bg-white"
|
class="relative box-content flex flex-row flex-wrap overflow-hidden bg-white"
|
||||||
:style="{
|
:style="{
|
||||||
|
...calculateSpace(index),
|
||||||
|
...calculateWidth(),
|
||||||
borderTopLeftRadius: `${property.borderRadiusTop}px`,
|
borderTopLeftRadius: `${property.borderRadiusTop}px`,
|
||||||
borderTopRightRadius: `${property.borderRadiusTop}px`,
|
borderTopRightRadius: `${property.borderRadiusTop}px`,
|
||||||
borderBottomLeftRadius: `${property.borderRadiusBottom}px`,
|
borderBottomLeftRadius: `${property.borderRadiusBottom}px`,
|
||||||
|
|
@ -22,104 +14,188 @@
|
||||||
:key="index"
|
:key="index"
|
||||||
>
|
>
|
||||||
<!-- 角标 -->
|
<!-- 角标 -->
|
||||||
<div
|
<div v-if="property.badge.show" class="absolute left-0 top-0 z-1 items-center justify-center">
|
||||||
v-if="property.badge.show"
|
|
||||||
class="absolute left-0 top-0 z-1 items-center justify-center"
|
|
||||||
>
|
|
||||||
<el-image fit="cover" :src="property.badge.imgUrl" class="h-26px w-38px" />
|
<el-image fit="cover" :src="property.badge.imgUrl" class="h-26px w-38px" />
|
||||||
</div>
|
</div>
|
||||||
<!-- 商品封面图 -->
|
<!-- 商品封面图 -->
|
||||||
<el-image fit="cover" :src="spu.picUrl" :style="{ width: imageSize, height: imageSize }" />
|
<div
|
||||||
|
:class="[
|
||||||
|
'h-140px',
|
||||||
|
{
|
||||||
|
'w-full': property.layoutType !== 'oneColSmallImg',
|
||||||
|
'w-140px': property.layoutType === 'oneColSmallImg'
|
||||||
|
}
|
||||||
|
]"
|
||||||
|
>
|
||||||
|
<el-image fit="cover" class="h-full w-full" :src="spu.picUrl" />
|
||||||
|
</div>
|
||||||
<div
|
<div
|
||||||
:class="[
|
:class="[
|
||||||
' flex flex-col gap-8px p-8px box-border',
|
' flex flex-col gap-8px p-8px box-border',
|
||||||
{
|
{
|
||||||
'w-[calc(100%-64px)]': columns === 2,
|
'w-full': property.layoutType !== 'oneColSmallImg',
|
||||||
'w-full': columns === 3
|
'w-[calc(100%-140px-16px)]': property.layoutType === 'oneColSmallImg'
|
||||||
}
|
}
|
||||||
]"
|
]"
|
||||||
>
|
>
|
||||||
<!-- 商品名称 -->
|
<!-- 商品名称 -->
|
||||||
<div
|
<div
|
||||||
v-if="property.fields.name.show"
|
v-if="property.fields.name.show"
|
||||||
class="truncate text-12px"
|
:class="[
|
||||||
|
'text-14px ',
|
||||||
|
{
|
||||||
|
truncate: property.layoutType !== 'oneColSmallImg',
|
||||||
|
'overflow-ellipsis line-clamp-2': property.layoutType === 'oneColSmallImg'
|
||||||
|
}
|
||||||
|
]"
|
||||||
:style="{ color: property.fields.name.color }"
|
:style="{ color: property.fields.name.color }"
|
||||||
>
|
>
|
||||||
{{ spu.name }}
|
{{ spu.name }}
|
||||||
</div>
|
</div>
|
||||||
|
<!-- 商品简介 -->
|
||||||
|
<div
|
||||||
|
v-if="property.fields.introduction.show"
|
||||||
|
class="truncate text-12px"
|
||||||
|
:style="{ color: property.fields.introduction.color }"
|
||||||
|
>
|
||||||
|
{{ spu.introduction }}
|
||||||
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<!-- 商品价格 -->
|
<!-- 价格 -->
|
||||||
<span
|
<span
|
||||||
v-if="property.fields.price.show"
|
v-if="property.fields.price.show"
|
||||||
class="text-12px"
|
class="text-16px"
|
||||||
:style="{ color: property.fields.price.color }"
|
:style="{ color: property.fields.price.color }"
|
||||||
>
|
>
|
||||||
¥{{ spu.price }}
|
¥{{ fenToYuan(spu.price || Infinity) }}
|
||||||
|
</span>
|
||||||
|
<!-- 市场价 -->
|
||||||
|
<span
|
||||||
|
v-if="property.fields.marketPrice.show && spu.marketPrice"
|
||||||
|
class="ml-4px text-10px line-through"
|
||||||
|
:style="{ color: property.fields.marketPrice.color }"
|
||||||
|
>¥{{ fenToYuan(spu.marketPrice) }}</span
|
||||||
|
>
|
||||||
|
</div>
|
||||||
|
<div class="text-12px">
|
||||||
|
<!-- 销量 -->
|
||||||
|
<span
|
||||||
|
v-if="property.fields.salesCount.show"
|
||||||
|
:style="{ color: property.fields.salesCount.color }"
|
||||||
|
>
|
||||||
|
已售{{ (spu.salesCount || 0) + (spu.virtualSalesCount || 0) }}件
|
||||||
|
</span>
|
||||||
|
<!-- 库存 -->
|
||||||
|
<span v-if="property.fields.stock.show" :style="{ color: property.fields.stock.color }">
|
||||||
|
库存{{ spu.stock || 0 }}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<!-- 购买按钮 -->
|
||||||
|
<div class="absolute bottom-8px right-8px">
|
||||||
|
<!-- 文字按钮 -->
|
||||||
|
<span
|
||||||
|
v-if="property.btnBuy.type === 'text'"
|
||||||
|
class="rounded-full p-x-12px p-y-4px text-12px text-white"
|
||||||
|
:style="{
|
||||||
|
background: `linear-gradient(to right, ${property.btnBuy.bgBeginColor}, ${property.btnBuy.bgEndColor}`
|
||||||
|
}"
|
||||||
|
>
|
||||||
|
{{ property.btnBuy.text }}
|
||||||
|
</span>
|
||||||
|
<!-- 图片按钮 -->
|
||||||
|
<el-image
|
||||||
|
v-else
|
||||||
|
class="h-28px w-28px rounded-full"
|
||||||
|
fit="cover"
|
||||||
|
:src="property.btnBuy.imgUrl"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</el-scrollbar>
|
|
||||||
</template>
|
</template>
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { PromotionCombinationProperty } from './config'
|
import { PromotionCombinationProperty } from './config'
|
||||||
import * as ProductSpuApi from '@/api/mall/product/spu'
|
import * as ProductSpuApi from '@/api/mall/product/spu'
|
||||||
import * as CombinationActivityApi from '@/api/mall/promotion/combination/combinationActivity'
|
import * as CombinationActivityApi from '@/api/mall/promotion/combination/combinationActivity'
|
||||||
|
import { fenToYuan } from '@/utils'
|
||||||
|
|
||||||
/** 拼团 */
|
/** 拼团卡片 */
|
||||||
defineOptions({ name: 'PromotionCombination' })
|
defineOptions({ name: 'PromotionCombination' })
|
||||||
// 定义属性
|
// 定义属性
|
||||||
const props = defineProps<{ property: PromotionCombinationProperty }>()
|
const props = defineProps<{ property: PromotionCombinationProperty }>()
|
||||||
// 商品列表
|
// 商品列表
|
||||||
const spuList = ref<ProductSpuApi.Spu[]>([])
|
const spuList = ref<ProductSpuApi.Spu[]>([])
|
||||||
|
const spuIdList = ref<number[]>([])
|
||||||
|
const combinationActivityList = ref<CombinationActivityApi.CombinationActivityVO[]>([])
|
||||||
|
|
||||||
watch(
|
watch(
|
||||||
() => props.property.activityId,
|
() => props.property.activityIds,
|
||||||
async () => {
|
async () => {
|
||||||
if (!props.property.activityId) return
|
try {
|
||||||
const activity = await CombinationActivityApi.getCombinationActivity(props.property.activityId)
|
// 新添加的拼团组件,是没有活动ID的
|
||||||
if (!activity?.spuId) return
|
const activityIds = props.property.activityIds
|
||||||
spuList.value = [await ProductSpuApi.getSpu(activity.spuId)]
|
// 检查活动ID的有效性
|
||||||
|
if (Array.isArray(activityIds) && activityIds.length > 0) {
|
||||||
|
// 获取拼团活动详情列表
|
||||||
|
combinationActivityList.value =
|
||||||
|
await CombinationActivityApi.getCombinationActivityListByIds(activityIds)
|
||||||
|
|
||||||
|
// 获取拼团活动的 SPU 详情列表
|
||||||
|
spuList.value = []
|
||||||
|
spuIdList.value = combinationActivityList.value
|
||||||
|
.map((activity) => activity.spuId)
|
||||||
|
.filter((spuId): spuId is number => typeof spuId === 'number')
|
||||||
|
if (spuIdList.value.length > 0) {
|
||||||
|
spuList.value = await ProductSpuApi.getSpuDetailList(spuIdList.value)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 更新 SPU 的最低价格
|
||||||
|
combinationActivityList.value.forEach((activity) => {
|
||||||
|
// 匹配spuId
|
||||||
|
const spu = spuList.value.find((spu) => spu.id === activity.spuId)
|
||||||
|
if (spu) {
|
||||||
|
// 赋值活动价格,哪个最便宜就赋值哪个
|
||||||
|
spu.price = Math.min(activity.combinationPrice || Infinity, spu.price || Infinity)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('获取拼团活动细节或 SPU 细节时出错:', error)
|
||||||
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
immediate: true,
|
immediate: true,
|
||||||
deep: true
|
deep: true
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
// 手机宽度
|
|
||||||
const phoneWidth = ref(375)
|
/**
|
||||||
|
* 计算商品的间距
|
||||||
|
* @param index 商品索引
|
||||||
|
*/
|
||||||
|
const calculateSpace = (index: number) => {
|
||||||
|
// 商品的列数
|
||||||
|
const columns = props.property.layoutType === 'twoCol' ? 2 : 1
|
||||||
|
// 第一列没有左边距
|
||||||
|
const marginLeft = index % columns === 0 ? '0' : props.property.space + 'px'
|
||||||
|
// 第一行没有上边距
|
||||||
|
const marginTop = index < columns ? '0' : props.property.space + 'px'
|
||||||
|
|
||||||
|
return { marginLeft, marginTop }
|
||||||
|
}
|
||||||
|
|
||||||
// 容器
|
// 容器
|
||||||
const containerRef = ref()
|
const containerRef = ref()
|
||||||
// 商品的列数
|
// 计算商品的宽度
|
||||||
const columns = ref(2)
|
const calculateWidth = () => {
|
||||||
// 滚动条宽度
|
let width = '100%'
|
||||||
const scrollbarWidth = ref('100%')
|
// 双列时每列的宽度为:(总宽度 - 间距)/ 2
|
||||||
// 商品图大小
|
if (props.property.layoutType === 'twoCol') {
|
||||||
const imageSize = ref('0')
|
width = `${(containerRef.value.offsetWidth - props.property.space) / 2}px`
|
||||||
// 商品网络列数
|
}
|
||||||
const gridTemplateColumns = ref('')
|
return { width }
|
||||||
// 计算布局参数
|
}
|
||||||
watch(
|
|
||||||
() => [props.property, phoneWidth, spuList.value.length],
|
|
||||||
() => {
|
|
||||||
// 计算列数
|
|
||||||
columns.value = props.property.layoutType === 'oneCol' ? 1 : 3
|
|
||||||
// 每列的宽度为:(总宽度 - 间距 * (列数 - 1))/ 列数
|
|
||||||
const productWidth =
|
|
||||||
(phoneWidth.value - props.property.space * (columns.value - 1)) / columns.value
|
|
||||||
// 商品图布局:2列时,左右布局 3列时,上下布局
|
|
||||||
imageSize.value = columns.value === 2 ? '64px' : `${productWidth}px`
|
|
||||||
// 指定列数
|
|
||||||
gridTemplateColumns.value = `repeat(${columns.value}, auto)`
|
|
||||||
// 不滚动
|
|
||||||
scrollbarWidth.value = '100%'
|
|
||||||
},
|
|
||||||
{ immediate: true, deep: true }
|
|
||||||
)
|
|
||||||
onMounted(() => {
|
|
||||||
// 提取手机宽度
|
|
||||||
phoneWidth.value = containerRef.value?.wrapRef?.offsetWidth || 375
|
|
||||||
})
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped lang="scss"></style>
|
<style scoped lang="scss"></style>
|
||||||
|
|
|
||||||
|
|
@ -2,30 +2,31 @@
|
||||||
<ComponentContainerProperty v-model="formData.style">
|
<ComponentContainerProperty v-model="formData.style">
|
||||||
<el-form label-width="80px" :model="formData">
|
<el-form label-width="80px" :model="formData">
|
||||||
<el-card header="拼团活动" class="property-group" shadow="never">
|
<el-card header="拼团活动" class="property-group" shadow="never">
|
||||||
<el-form-item label="拼团活动" prop="activityId">
|
<CombinationShowcase v-model="formData.activityIds" />
|
||||||
<el-select v-model="formData.activityId">
|
|
||||||
<el-option
|
|
||||||
v-for="activity in activityList"
|
|
||||||
:key="activity.id"
|
|
||||||
:label="activity.name"
|
|
||||||
:value="activity.id"
|
|
||||||
/>
|
|
||||||
</el-select>
|
|
||||||
</el-form-item>
|
|
||||||
</el-card>
|
</el-card>
|
||||||
<el-card header="商品样式" class="property-group" shadow="never">
|
<el-card header="商品样式" class="property-group" shadow="never">
|
||||||
<el-form-item label="布局" prop="type">
|
<el-form-item label="布局" prop="type">
|
||||||
<el-radio-group v-model="formData.layoutType">
|
<el-radio-group v-model="formData.layoutType">
|
||||||
<el-tooltip class="item" content="单列" placement="bottom">
|
<el-tooltip class="item" content="单列大图" placement="bottom">
|
||||||
<el-radio-button label="oneCol">
|
<el-radio-button value="oneColBigImg">
|
||||||
<Icon icon="fluent:text-column-one-24-filled" />
|
<Icon icon="fluent:text-column-one-24-filled" />
|
||||||
</el-radio-button>
|
</el-radio-button>
|
||||||
</el-tooltip>
|
</el-tooltip>
|
||||||
<el-tooltip class="item" content="三列" placement="bottom">
|
<el-tooltip class="item" content="单列小图" placement="bottom">
|
||||||
<el-radio-button label="threeCol">
|
<el-radio-button value="oneColSmallImg">
|
||||||
<Icon icon="fluent:text-column-three-24-filled" />
|
<Icon icon="fluent:text-column-two-left-24-filled" />
|
||||||
</el-radio-button>
|
</el-radio-button>
|
||||||
</el-tooltip>
|
</el-tooltip>
|
||||||
|
<el-tooltip class="item" content="双列" placement="bottom">
|
||||||
|
<el-radio-button value="twoCol">
|
||||||
|
<Icon icon="fluent:text-column-two-24-filled" />
|
||||||
|
</el-radio-button>
|
||||||
|
</el-tooltip>
|
||||||
|
<!--<el-tooltip class="item" content="三列" placement="bottom">
|
||||||
|
<el-radio-button value="threeCol">
|
||||||
|
<Icon icon="fluent:text-column-three-24-filled" />
|
||||||
|
</el-radio-button>
|
||||||
|
</el-tooltip>-->
|
||||||
</el-radio-group>
|
</el-radio-group>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item label="商品名称" prop="fields.name.show">
|
<el-form-item label="商品名称" prop="fields.name.show">
|
||||||
|
|
@ -34,12 +35,36 @@
|
||||||
<el-checkbox v-model="formData.fields.name.show" />
|
<el-checkbox v-model="formData.fields.name.show" />
|
||||||
</div>
|
</div>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
|
<el-form-item label="商品简介" prop="fields.introduction.show">
|
||||||
|
<div class="flex gap-8px">
|
||||||
|
<ColorInput v-model="formData.fields.introduction.color" />
|
||||||
|
<el-checkbox v-model="formData.fields.introduction.show" />
|
||||||
|
</div>
|
||||||
|
</el-form-item>
|
||||||
<el-form-item label="商品价格" prop="fields.price.show">
|
<el-form-item label="商品价格" prop="fields.price.show">
|
||||||
<div class="flex gap-8px">
|
<div class="flex gap-8px">
|
||||||
<ColorInput v-model="formData.fields.price.color" />
|
<ColorInput v-model="formData.fields.price.color" />
|
||||||
<el-checkbox v-model="formData.fields.price.show" />
|
<el-checkbox v-model="formData.fields.price.show" />
|
||||||
</div>
|
</div>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
|
<el-form-item label="市场价" prop="fields.marketPrice.show">
|
||||||
|
<div class="flex gap-8px">
|
||||||
|
<ColorInput v-model="formData.fields.marketPrice.color" />
|
||||||
|
<el-checkbox v-model="formData.fields.marketPrice.show" />
|
||||||
|
</div>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="商品销量" prop="fields.salesCount.show">
|
||||||
|
<div class="flex gap-8px">
|
||||||
|
<ColorInput v-model="formData.fields.salesCount.color" />
|
||||||
|
<el-checkbox v-model="formData.fields.salesCount.show" />
|
||||||
|
</div>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="商品库存" prop="fields.stock.show">
|
||||||
|
<div class="flex gap-8px">
|
||||||
|
<ColorInput v-model="formData.fields.stock.color" />
|
||||||
|
<el-checkbox v-model="formData.fields.stock.show" />
|
||||||
|
</div>
|
||||||
|
</el-form-item>
|
||||||
</el-card>
|
</el-card>
|
||||||
<el-card header="角标" class="property-group" shadow="never">
|
<el-card header="角标" class="property-group" shadow="never">
|
||||||
<el-form-item label="角标" prop="badge.show">
|
<el-form-item label="角标" prop="badge.show">
|
||||||
|
|
@ -51,6 +76,32 @@
|
||||||
</UploadImg>
|
</UploadImg>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</el-card>
|
</el-card>
|
||||||
|
<el-card header="按钮" class="property-group" shadow="never">
|
||||||
|
<el-form-item label="按钮类型" prop="btnBuy.type">
|
||||||
|
<el-radio-group v-model="formData.btnBuy.type">
|
||||||
|
<el-radio-button value="text">文字</el-radio-button>
|
||||||
|
<el-radio-button value="img">图片</el-radio-button>
|
||||||
|
</el-radio-group>
|
||||||
|
</el-form-item>
|
||||||
|
<template v-if="formData.btnBuy.type === 'text'">
|
||||||
|
<el-form-item label="按钮文字" prop="btnBuy.text">
|
||||||
|
<el-input v-model="formData.btnBuy.text" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="左侧背景" prop="btnBuy.bgBeginColor">
|
||||||
|
<ColorInput v-model="formData.btnBuy.bgBeginColor" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="右侧背景" prop="btnBuy.bgEndColor">
|
||||||
|
<ColorInput v-model="formData.btnBuy.bgEndColor" />
|
||||||
|
</el-form-item>
|
||||||
|
</template>
|
||||||
|
<template v-else>
|
||||||
|
<el-form-item label="图片" prop="btnBuy.imgUrl">
|
||||||
|
<UploadImg v-model="formData.btnBuy.imgUrl" height="56px" width="56px">
|
||||||
|
<template #tip> 建议尺寸:56 * 56</template>
|
||||||
|
</UploadImg>
|
||||||
|
</el-form-item>
|
||||||
|
</template>
|
||||||
|
</el-card>
|
||||||
<el-card header="商品样式" class="property-group" shadow="never">
|
<el-card header="商品样式" class="property-group" shadow="never">
|
||||||
<el-form-item label="上圆角" prop="borderRadiusTop">
|
<el-form-item label="上圆角" prop="borderRadiusTop">
|
||||||
<el-slider
|
<el-slider
|
||||||
|
|
@ -92,6 +143,7 @@ import { PromotionCombinationProperty } from './config'
|
||||||
import { usePropertyForm } from '@/components/DiyEditor/util'
|
import { usePropertyForm } from '@/components/DiyEditor/util'
|
||||||
import * as CombinationActivityApi from '@/api/mall/promotion/combination/combinationActivity'
|
import * as CombinationActivityApi from '@/api/mall/promotion/combination/combinationActivity'
|
||||||
import { CommonStatusEnum } from '@/utils/constants'
|
import { CommonStatusEnum } from '@/utils/constants'
|
||||||
|
import CombinationShowcase from '@/views/mall/promotion/combination/components/CombinationShowcase.vue'
|
||||||
|
|
||||||
// 拼团属性面板
|
// 拼团属性面板
|
||||||
defineOptions({ name: 'PromotionCombinationProperty' })
|
defineOptions({ name: 'PromotionCombinationProperty' })
|
||||||
|
|
@ -100,7 +152,7 @@ const props = defineProps<{ modelValue: PromotionCombinationProperty }>()
|
||||||
const emit = defineEmits(['update:modelValue'])
|
const emit = defineEmits(['update:modelValue'])
|
||||||
const { formData } = usePropertyForm(props.modelValue, emit)
|
const { formData } = usePropertyForm(props.modelValue, emit)
|
||||||
// 活动列表
|
// 活动列表
|
||||||
const activityList = ref<CombinationActivityApi.CombinationActivityVO>([])
|
const activityList = ref<CombinationActivityApi.CombinationActivityVO[]>([])
|
||||||
onMounted(async () => {
|
onMounted(async () => {
|
||||||
const { list } = await CombinationActivityApi.getCombinationActivityPage({
|
const { list } = await CombinationActivityApi.getCombinationActivityPage({
|
||||||
status: CommonStatusEnum.ENABLE
|
status: CommonStatusEnum.ENABLE
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,96 @@
|
||||||
|
import {ComponentStyle, DiyComponent} from '@/components/DiyEditor/util'
|
||||||
|
|
||||||
|
/** 积分商城属性 */
|
||||||
|
export interface PromotionPointProperty {
|
||||||
|
// 布局类型:单列 | 三列
|
||||||
|
layoutType: 'oneColBigImg' | 'oneColSmallImg' | 'twoCol'
|
||||||
|
// 商品字段
|
||||||
|
fields: {
|
||||||
|
// 商品名称
|
||||||
|
name: PromotionPointFieldProperty
|
||||||
|
// 商品简介
|
||||||
|
introduction: PromotionPointFieldProperty
|
||||||
|
// 商品价格
|
||||||
|
price: PromotionPointFieldProperty
|
||||||
|
// 市场价
|
||||||
|
marketPrice: PromotionPointFieldProperty
|
||||||
|
// 商品销量
|
||||||
|
salesCount: PromotionPointFieldProperty
|
||||||
|
// 商品库存
|
||||||
|
stock: PromotionPointFieldProperty
|
||||||
|
}
|
||||||
|
// 角标
|
||||||
|
badge: {
|
||||||
|
// 是否显示
|
||||||
|
show: boolean
|
||||||
|
// 角标图片
|
||||||
|
imgUrl: string
|
||||||
|
}
|
||||||
|
// 按钮
|
||||||
|
btnBuy: {
|
||||||
|
// 类型:文字 | 图片
|
||||||
|
type: 'text' | 'img'
|
||||||
|
// 文字
|
||||||
|
text: string
|
||||||
|
// 文字按钮:背景渐变起始颜色
|
||||||
|
bgBeginColor: string
|
||||||
|
// 文字按钮:背景渐变结束颜色
|
||||||
|
bgEndColor: string
|
||||||
|
// 图片按钮:图片地址
|
||||||
|
imgUrl: string
|
||||||
|
}
|
||||||
|
// 上圆角
|
||||||
|
borderRadiusTop: number
|
||||||
|
// 下圆角
|
||||||
|
borderRadiusBottom: number
|
||||||
|
// 间距
|
||||||
|
space: number
|
||||||
|
// 秒杀活动编号
|
||||||
|
activityIds: number[]
|
||||||
|
// 组件样式
|
||||||
|
style: ComponentStyle
|
||||||
|
}
|
||||||
|
|
||||||
|
// 商品字段
|
||||||
|
export interface PromotionPointFieldProperty {
|
||||||
|
// 是否显示
|
||||||
|
show: boolean
|
||||||
|
// 颜色
|
||||||
|
color: string
|
||||||
|
}
|
||||||
|
|
||||||
|
// 定义组件
|
||||||
|
export const component = {
|
||||||
|
id: 'PromotionPoint',
|
||||||
|
name: '积分商城',
|
||||||
|
icon: 'ep:present',
|
||||||
|
property: {
|
||||||
|
layoutType: 'oneColBigImg',
|
||||||
|
fields: {
|
||||||
|
name: { show: true, color: '#000' },
|
||||||
|
introduction: { show: true, color: '#999' },
|
||||||
|
price: { show: true, color: '#ff3000' },
|
||||||
|
marketPrice: { show: true, color: '#c4c4c4' },
|
||||||
|
salesCount: { show: true, color: '#c4c4c4' },
|
||||||
|
stock: { show: false, color: '#c4c4c4' }
|
||||||
|
},
|
||||||
|
badge: { show: false, imgUrl: '' },
|
||||||
|
btnBuy: {
|
||||||
|
type: 'text',
|
||||||
|
text: '立即兑换',
|
||||||
|
bgBeginColor: '#FF6000',
|
||||||
|
bgEndColor: '#FE832A',
|
||||||
|
imgUrl: ''
|
||||||
|
},
|
||||||
|
borderRadiusTop: 8,
|
||||||
|
borderRadiusBottom: 8,
|
||||||
|
space: 8,
|
||||||
|
style: {
|
||||||
|
bgType: 'color',
|
||||||
|
bgColor: '',
|
||||||
|
marginLeft: 8,
|
||||||
|
marginRight: 8,
|
||||||
|
marginBottom: 8
|
||||||
|
} as ComponentStyle
|
||||||
|
}
|
||||||
|
} as DiyComponent<PromotionPointProperty>
|
||||||
|
|
@ -0,0 +1,202 @@
|
||||||
|
<template>
|
||||||
|
<div ref="containerRef" :class="`box-content min-h-30px w-full flex flex-row flex-wrap`">
|
||||||
|
<div
|
||||||
|
v-for="(spu, index) in spuList"
|
||||||
|
:key="index"
|
||||||
|
:style="{
|
||||||
|
...calculateSpace(index),
|
||||||
|
...calculateWidth(),
|
||||||
|
borderTopLeftRadius: `${property.borderRadiusTop}px`,
|
||||||
|
borderTopRightRadius: `${property.borderRadiusTop}px`,
|
||||||
|
borderBottomLeftRadius: `${property.borderRadiusBottom}px`,
|
||||||
|
borderBottomRightRadius: `${property.borderRadiusBottom}px`
|
||||||
|
}"
|
||||||
|
class="relative box-content flex flex-row flex-wrap overflow-hidden bg-white"
|
||||||
|
>
|
||||||
|
<!-- 角标 -->
|
||||||
|
<div v-if="property.badge.show" class="absolute left-0 top-0 z-1 items-center justify-center">
|
||||||
|
<el-image :src="property.badge.imgUrl" class="h-26px w-38px" fit="cover" />
|
||||||
|
</div>
|
||||||
|
<!-- 商品封面图 -->
|
||||||
|
<div
|
||||||
|
:class="[
|
||||||
|
'h-140px',
|
||||||
|
{
|
||||||
|
'w-full': property.layoutType !== 'oneColSmallImg',
|
||||||
|
'w-140px': property.layoutType === 'oneColSmallImg'
|
||||||
|
}
|
||||||
|
]"
|
||||||
|
>
|
||||||
|
<el-image :src="spu.picUrl" class="h-full w-full" fit="cover" />
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
:class="[
|
||||||
|
' flex flex-col gap-8px p-8px box-border',
|
||||||
|
{
|
||||||
|
'w-full': property.layoutType !== 'oneColSmallImg',
|
||||||
|
'w-[calc(100%-140px-16px)]': property.layoutType === 'oneColSmallImg'
|
||||||
|
}
|
||||||
|
]"
|
||||||
|
>
|
||||||
|
<!-- 商品名称 -->
|
||||||
|
<div
|
||||||
|
v-if="property.fields.name.show"
|
||||||
|
:class="[
|
||||||
|
'text-14px ',
|
||||||
|
{
|
||||||
|
truncate: property.layoutType !== 'oneColSmallImg',
|
||||||
|
'overflow-ellipsis line-clamp-2': property.layoutType === 'oneColSmallImg'
|
||||||
|
}
|
||||||
|
]"
|
||||||
|
:style="{ color: property.fields.name.color }"
|
||||||
|
>
|
||||||
|
{{ spu.name }}
|
||||||
|
</div>
|
||||||
|
<!-- 商品简介 -->
|
||||||
|
<div
|
||||||
|
v-if="property.fields.introduction.show"
|
||||||
|
:style="{ color: property.fields.introduction.color }"
|
||||||
|
class="truncate text-12px"
|
||||||
|
>
|
||||||
|
{{ spu.introduction }}
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<!-- 积分 -->
|
||||||
|
<span
|
||||||
|
v-if="property.fields.price.show"
|
||||||
|
:style="{ color: property.fields.price.color }"
|
||||||
|
class="text-16px"
|
||||||
|
>
|
||||||
|
{{ spu.point }}积分
|
||||||
|
{{ !spu.pointPrice || spu.pointPrice === 0 ? '' : `+${fenToYuan(spu.pointPrice)}元` }}
|
||||||
|
</span>
|
||||||
|
<!-- 市场价 -->
|
||||||
|
<span
|
||||||
|
v-if="property.fields.marketPrice.show && spu.marketPrice"
|
||||||
|
:style="{ color: property.fields.marketPrice.color }"
|
||||||
|
class="ml-4px text-10px line-through"
|
||||||
|
>
|
||||||
|
¥{{ fenToYuan(spu.marketPrice) }}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div class="text-12px">
|
||||||
|
<!-- 销量 -->
|
||||||
|
<span
|
||||||
|
v-if="property.fields.salesCount.show"
|
||||||
|
:style="{ color: property.fields.salesCount.color }"
|
||||||
|
>
|
||||||
|
已兑{{ (spu.pointTotalStock || 0) - (spu.pointStock || 0) }}件
|
||||||
|
</span>
|
||||||
|
<!-- 库存 -->
|
||||||
|
<span v-if="property.fields.stock.show" :style="{ color: property.fields.stock.color }">
|
||||||
|
库存{{ spu.pointTotalStock || 0 }}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<!-- 购买按钮 -->
|
||||||
|
<div class="absolute bottom-8px right-8px">
|
||||||
|
<!-- 文字按钮 -->
|
||||||
|
<span
|
||||||
|
v-if="property.btnBuy.type === 'text'"
|
||||||
|
:style="{
|
||||||
|
background: `linear-gradient(to right, ${property.btnBuy.bgBeginColor}, ${property.btnBuy.bgEndColor}`
|
||||||
|
}"
|
||||||
|
class="rounded-full p-x-12px p-y-4px text-12px text-white"
|
||||||
|
>
|
||||||
|
{{ property.btnBuy.text }}
|
||||||
|
</span>
|
||||||
|
<!-- 图片按钮 -->
|
||||||
|
<el-image
|
||||||
|
v-else
|
||||||
|
:src="property.btnBuy.imgUrl"
|
||||||
|
class="h-28px w-28px rounded-full"
|
||||||
|
fit="cover"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import { PromotionPointProperty } from './config'
|
||||||
|
import * as ProductSpuApi from '@/api/mall/product/spu'
|
||||||
|
import { PointActivityApi, PointActivityVO, SpuExtension0 } from '@/api/mall/promotion/point'
|
||||||
|
import { fenToYuan } from '@/utils'
|
||||||
|
|
||||||
|
/** 积分商城卡片 */
|
||||||
|
defineOptions({ name: 'PromotionPoint' })
|
||||||
|
// 定义属性
|
||||||
|
const props = defineProps<{ property: PromotionPointProperty }>()
|
||||||
|
// 商品列表
|
||||||
|
const spuList = ref<SpuExtension0[]>([])
|
||||||
|
const spuIdList = ref<number[]>([])
|
||||||
|
const pointActivityList = ref<PointActivityVO[]>([])
|
||||||
|
|
||||||
|
watch(
|
||||||
|
() => props.property.activityIds,
|
||||||
|
async () => {
|
||||||
|
try {
|
||||||
|
// 新添加的积分商城组件,是没有活动ID的
|
||||||
|
const activityIds = props.property.activityIds
|
||||||
|
// 检查活动ID的有效性
|
||||||
|
if (Array.isArray(activityIds) && activityIds.length > 0) {
|
||||||
|
// 获取积分商城活动详情列表
|
||||||
|
pointActivityList.value = await PointActivityApi.getPointActivityListByIds(activityIds)
|
||||||
|
|
||||||
|
// 获取积分商城活动的 SPU 详情列表
|
||||||
|
spuList.value = []
|
||||||
|
spuIdList.value = pointActivityList.value.map((activity) => activity.spuId)
|
||||||
|
if (spuIdList.value.length > 0) {
|
||||||
|
spuList.value = await ProductSpuApi.getSpuDetailList(spuIdList.value)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 更新 SPU 的最低兑换积分和所需兑换金额
|
||||||
|
pointActivityList.value.forEach((activity) => {
|
||||||
|
// 匹配spuId
|
||||||
|
const spu = spuList.value.find((spu) => spu.id === activity.spuId)
|
||||||
|
if (spu) {
|
||||||
|
spu.pointStock = activity.stock
|
||||||
|
spu.pointTotalStock = activity.totalStock
|
||||||
|
spu.point = activity.point
|
||||||
|
spu.pointPrice = activity.price
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('获取积分商城活动细节或 SPU 细节时出错:', error)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
immediate: true,
|
||||||
|
deep: true
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 计算商品的间距
|
||||||
|
* @param index 商品索引
|
||||||
|
*/
|
||||||
|
const calculateSpace = (index: number) => {
|
||||||
|
// 商品的列数
|
||||||
|
const columns = props.property.layoutType === 'twoCol' ? 2 : 1
|
||||||
|
// 第一列没有左边距
|
||||||
|
const marginLeft = index % columns === 0 ? '0' : props.property.space + 'px'
|
||||||
|
// 第一行没有上边距
|
||||||
|
const marginTop = index < columns ? '0' : props.property.space + 'px'
|
||||||
|
|
||||||
|
return { marginLeft, marginTop }
|
||||||
|
}
|
||||||
|
|
||||||
|
// 容器
|
||||||
|
const containerRef = ref()
|
||||||
|
// 计算商品的宽度
|
||||||
|
const calculateWidth = () => {
|
||||||
|
let width = '100%'
|
||||||
|
// 双列时每列的宽度为:(总宽度 - 间距)/ 2
|
||||||
|
if (props.property.layoutType === 'twoCol') {
|
||||||
|
width = `${(containerRef.value.offsetWidth - props.property.space) / 2}px`
|
||||||
|
}
|
||||||
|
return { width }
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped></style>
|
||||||
|
|
@ -0,0 +1,154 @@
|
||||||
|
<template>
|
||||||
|
<ComponentContainerProperty v-model="formData.style">
|
||||||
|
<el-form :model="formData" label-width="80px">
|
||||||
|
<el-card class="property-group" header="积分商城活动" shadow="never">
|
||||||
|
<PointShowcase v-model="formData.activityIds" />
|
||||||
|
</el-card>
|
||||||
|
<el-card class="property-group" header="商品样式" shadow="never">
|
||||||
|
<el-form-item label="布局" prop="type">
|
||||||
|
<el-radio-group v-model="formData.layoutType">
|
||||||
|
<el-tooltip class="item" content="单列大图" placement="bottom">
|
||||||
|
<el-radio-button value="oneColBigImg">
|
||||||
|
<Icon icon="fluent:text-column-one-24-filled" />
|
||||||
|
</el-radio-button>
|
||||||
|
</el-tooltip>
|
||||||
|
<el-tooltip class="item" content="单列小图" placement="bottom">
|
||||||
|
<el-radio-button value="oneColSmallImg">
|
||||||
|
<Icon icon="fluent:text-column-two-left-24-filled" />
|
||||||
|
</el-radio-button>
|
||||||
|
</el-tooltip>
|
||||||
|
<el-tooltip class="item" content="双列" placement="bottom">
|
||||||
|
<el-radio-button value="twoCol">
|
||||||
|
<Icon icon="fluent:text-column-two-24-filled" />
|
||||||
|
</el-radio-button>
|
||||||
|
</el-tooltip>
|
||||||
|
<!--<el-tooltip class="item" content="三列" placement="bottom">
|
||||||
|
<el-radio-button value="threeCol">
|
||||||
|
<Icon icon="fluent:text-column-three-24-filled" />
|
||||||
|
</el-radio-button>
|
||||||
|
</el-tooltip>-->
|
||||||
|
</el-radio-group>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="商品名称" prop="fields.name.show">
|
||||||
|
<div class="flex gap-8px">
|
||||||
|
<ColorInput v-model="formData.fields.name.color" />
|
||||||
|
<el-checkbox v-model="formData.fields.name.show" />
|
||||||
|
</div>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="商品简介" prop="fields.introduction.show">
|
||||||
|
<div class="flex gap-8px">
|
||||||
|
<ColorInput v-model="formData.fields.introduction.color" />
|
||||||
|
<el-checkbox v-model="formData.fields.introduction.show" />
|
||||||
|
</div>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="商品价格" prop="fields.price.show">
|
||||||
|
<div class="flex gap-8px">
|
||||||
|
<ColorInput v-model="formData.fields.price.color" />
|
||||||
|
<el-checkbox v-model="formData.fields.price.show" />
|
||||||
|
</div>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="市场价" prop="fields.marketPrice.show">
|
||||||
|
<div class="flex gap-8px">
|
||||||
|
<ColorInput v-model="formData.fields.marketPrice.color" />
|
||||||
|
<el-checkbox v-model="formData.fields.marketPrice.show" />
|
||||||
|
</div>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="商品销量" prop="fields.salesCount.show">
|
||||||
|
<div class="flex gap-8px">
|
||||||
|
<ColorInput v-model="formData.fields.salesCount.color" />
|
||||||
|
<el-checkbox v-model="formData.fields.salesCount.show" />
|
||||||
|
</div>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="商品库存" prop="fields.stock.show">
|
||||||
|
<div class="flex gap-8px">
|
||||||
|
<ColorInput v-model="formData.fields.stock.color" />
|
||||||
|
<el-checkbox v-model="formData.fields.stock.show" />
|
||||||
|
</div>
|
||||||
|
</el-form-item>
|
||||||
|
</el-card>
|
||||||
|
<el-card class="property-group" header="角标" shadow="never">
|
||||||
|
<el-form-item label="角标" prop="badge.show">
|
||||||
|
<el-switch v-model="formData.badge.show" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item v-if="formData.badge.show" label="角标" prop="badge.imgUrl">
|
||||||
|
<UploadImg v-model="formData.badge.imgUrl" height="44px" width="72px">
|
||||||
|
<template #tip> 建议尺寸:36 * 22</template>
|
||||||
|
</UploadImg>
|
||||||
|
</el-form-item>
|
||||||
|
</el-card>
|
||||||
|
<el-card class="property-group" header="按钮" shadow="never">
|
||||||
|
<el-form-item label="按钮类型" prop="btnBuy.type">
|
||||||
|
<el-radio-group v-model="formData.btnBuy.type">
|
||||||
|
<el-radio-button value="text">文字</el-radio-button>
|
||||||
|
<el-radio-button value="img">图片</el-radio-button>
|
||||||
|
</el-radio-group>
|
||||||
|
</el-form-item>
|
||||||
|
<template v-if="formData.btnBuy.type === 'text'">
|
||||||
|
<el-form-item label="按钮文字" prop="btnBuy.text">
|
||||||
|
<el-input v-model="formData.btnBuy.text" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="左侧背景" prop="btnBuy.bgBeginColor">
|
||||||
|
<ColorInput v-model="formData.btnBuy.bgBeginColor" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="右侧背景" prop="btnBuy.bgEndColor">
|
||||||
|
<ColorInput v-model="formData.btnBuy.bgEndColor" />
|
||||||
|
</el-form-item>
|
||||||
|
</template>
|
||||||
|
<template v-else>
|
||||||
|
<el-form-item label="图片" prop="btnBuy.imgUrl">
|
||||||
|
<UploadImg v-model="formData.btnBuy.imgUrl" height="56px" width="56px">
|
||||||
|
<template #tip> 建议尺寸:56 * 56</template>
|
||||||
|
</UploadImg>
|
||||||
|
</el-form-item>
|
||||||
|
</template>
|
||||||
|
</el-card>
|
||||||
|
<el-card class="property-group" header="商品样式" shadow="never">
|
||||||
|
<el-form-item label="上圆角" prop="borderRadiusTop">
|
||||||
|
<el-slider
|
||||||
|
v-model="formData.borderRadiusTop"
|
||||||
|
:max="100"
|
||||||
|
:min="0"
|
||||||
|
:show-input-controls="false"
|
||||||
|
input-size="small"
|
||||||
|
show-input
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="下圆角" prop="borderRadiusBottom">
|
||||||
|
<el-slider
|
||||||
|
v-model="formData.borderRadiusBottom"
|
||||||
|
:max="100"
|
||||||
|
:min="0"
|
||||||
|
:show-input-controls="false"
|
||||||
|
input-size="small"
|
||||||
|
show-input
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="间隔" prop="space">
|
||||||
|
<el-slider
|
||||||
|
v-model="formData.space"
|
||||||
|
:max="100"
|
||||||
|
:min="0"
|
||||||
|
:show-input-controls="false"
|
||||||
|
input-size="small"
|
||||||
|
show-input
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
|
</el-card>
|
||||||
|
</el-form>
|
||||||
|
</ComponentContainerProperty>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import { PromotionPointProperty } from './config'
|
||||||
|
import { usePropertyForm } from '@/components/DiyEditor/util'
|
||||||
|
import PointShowcase from '@/views/mall/promotion/point/components/PointShowcase.vue'
|
||||||
|
|
||||||
|
// 秒杀属性面板
|
||||||
|
defineOptions({ name: 'PromotionPointProperty' })
|
||||||
|
|
||||||
|
const props = defineProps<{ modelValue: PromotionPointProperty }>()
|
||||||
|
const emit = defineEmits(['update:modelValue'])
|
||||||
|
const { formData } = usePropertyForm(props.modelValue, emit)
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped></style>
|
||||||
|
|
@ -3,13 +3,21 @@ import { ComponentStyle, DiyComponent } from '@/components/DiyEditor/util'
|
||||||
/** 秒杀属性 */
|
/** 秒杀属性 */
|
||||||
export interface PromotionSeckillProperty {
|
export interface PromotionSeckillProperty {
|
||||||
// 布局类型:单列 | 三列
|
// 布局类型:单列 | 三列
|
||||||
layoutType: 'oneCol' | 'threeCol'
|
layoutType: 'oneColBigImg' | 'oneColSmallImg' | 'twoCol'
|
||||||
// 商品字段
|
// 商品字段
|
||||||
fields: {
|
fields: {
|
||||||
// 商品名称
|
// 商品名称
|
||||||
name: PromotionSeckillFieldProperty
|
name: PromotionSeckillFieldProperty
|
||||||
|
// 商品简介
|
||||||
|
introduction: PromotionSeckillFieldProperty
|
||||||
// 商品价格
|
// 商品价格
|
||||||
price: PromotionSeckillFieldProperty
|
price: PromotionSeckillFieldProperty
|
||||||
|
// 市场价
|
||||||
|
marketPrice: PromotionSeckillFieldProperty
|
||||||
|
// 商品销量
|
||||||
|
salesCount: PromotionSeckillFieldProperty
|
||||||
|
// 商品库存
|
||||||
|
stock: PromotionSeckillFieldProperty
|
||||||
}
|
}
|
||||||
// 角标
|
// 角标
|
||||||
badge: {
|
badge: {
|
||||||
|
|
@ -18,6 +26,19 @@ export interface PromotionSeckillProperty {
|
||||||
// 角标图片
|
// 角标图片
|
||||||
imgUrl: string
|
imgUrl: string
|
||||||
}
|
}
|
||||||
|
// 按钮
|
||||||
|
btnBuy: {
|
||||||
|
// 类型:文字 | 图片
|
||||||
|
type: 'text' | 'img'
|
||||||
|
// 文字
|
||||||
|
text: string
|
||||||
|
// 文字按钮:背景渐变起始颜色
|
||||||
|
bgBeginColor: string
|
||||||
|
// 文字按钮:背景渐变结束颜色
|
||||||
|
bgEndColor: string
|
||||||
|
// 图片按钮:图片地址
|
||||||
|
imgUrl: string
|
||||||
|
}
|
||||||
// 上圆角
|
// 上圆角
|
||||||
borderRadiusTop: number
|
borderRadiusTop: number
|
||||||
// 下圆角
|
// 下圆角
|
||||||
|
|
@ -25,10 +46,11 @@ export interface PromotionSeckillProperty {
|
||||||
// 间距
|
// 间距
|
||||||
space: number
|
space: number
|
||||||
// 秒杀活动编号
|
// 秒杀活动编号
|
||||||
activityId: number
|
activityIds: number[]
|
||||||
// 组件样式
|
// 组件样式
|
||||||
style: ComponentStyle
|
style: ComponentStyle
|
||||||
}
|
}
|
||||||
|
|
||||||
// 商品字段
|
// 商品字段
|
||||||
export interface PromotionSeckillFieldProperty {
|
export interface PromotionSeckillFieldProperty {
|
||||||
// 是否显示
|
// 是否显示
|
||||||
|
|
@ -43,13 +65,23 @@ export const component = {
|
||||||
name: '秒杀',
|
name: '秒杀',
|
||||||
icon: 'mdi:calendar-time',
|
icon: 'mdi:calendar-time',
|
||||||
property: {
|
property: {
|
||||||
activityId: undefined,
|
layoutType: 'oneColBigImg',
|
||||||
layoutType: 'oneCol',
|
|
||||||
fields: {
|
fields: {
|
||||||
name: { show: true, color: '#000' },
|
name: { show: true, color: '#000' },
|
||||||
price: { show: true, color: '#ff3000' }
|
introduction: { show: true, color: '#999' },
|
||||||
|
price: { show: true, color: '#ff3000' },
|
||||||
|
marketPrice: { show: true, color: '#c4c4c4' },
|
||||||
|
salesCount: { show: true, color: '#c4c4c4' },
|
||||||
|
stock: { show: false, color: '#c4c4c4' }
|
||||||
},
|
},
|
||||||
badge: { show: false, imgUrl: '' },
|
badge: { show: false, imgUrl: '' },
|
||||||
|
btnBuy: {
|
||||||
|
type: 'text',
|
||||||
|
text: '立即秒杀',
|
||||||
|
bgBeginColor: '#FF6000',
|
||||||
|
bgEndColor: '#FE832A',
|
||||||
|
imgUrl: ''
|
||||||
|
},
|
||||||
borderRadiusTop: 8,
|
borderRadiusTop: 8,
|
||||||
borderRadiusBottom: 8,
|
borderRadiusBottom: 8,
|
||||||
space: 8,
|
space: 8,
|
||||||
|
|
|
||||||
|
|
@ -1,18 +1,10 @@
|
||||||
<template>
|
<template>
|
||||||
<el-scrollbar class="z-1 min-h-30px" wrap-class="w-full" ref="containerRef">
|
<div :class="`box-content min-h-30px w-full flex flex-row flex-wrap`" ref="containerRef">
|
||||||
<!-- 商品网格 -->
|
|
||||||
<div
|
|
||||||
class="grid overflow-x-auto"
|
|
||||||
:style="{
|
|
||||||
gridGap: `${property.space}px`,
|
|
||||||
gridTemplateColumns,
|
|
||||||
width: scrollbarWidth
|
|
||||||
}"
|
|
||||||
>
|
|
||||||
<!-- 商品 -->
|
|
||||||
<div
|
<div
|
||||||
class="relative box-content flex flex-row flex-wrap overflow-hidden bg-white"
|
class="relative box-content flex flex-row flex-wrap overflow-hidden bg-white"
|
||||||
:style="{
|
:style="{
|
||||||
|
...calculateSpace(index),
|
||||||
|
...calculateWidth(),
|
||||||
borderTopLeftRadius: `${property.borderRadiusTop}px`,
|
borderTopLeftRadius: `${property.borderRadiusTop}px`,
|
||||||
borderTopRightRadius: `${property.borderRadiusTop}px`,
|
borderTopRightRadius: `${property.borderRadiusTop}px`,
|
||||||
borderBottomLeftRadius: `${property.borderRadiusBottom}px`,
|
borderBottomLeftRadius: `${property.borderRadiusBottom}px`,
|
||||||
|
|
@ -22,104 +14,188 @@
|
||||||
:key="index"
|
:key="index"
|
||||||
>
|
>
|
||||||
<!-- 角标 -->
|
<!-- 角标 -->
|
||||||
<div
|
<div v-if="property.badge.show" class="absolute left-0 top-0 z-1 items-center justify-center">
|
||||||
v-if="property.badge.show"
|
|
||||||
class="absolute left-0 top-0 z-1 items-center justify-center"
|
|
||||||
>
|
|
||||||
<el-image fit="cover" :src="property.badge.imgUrl" class="h-26px w-38px" />
|
<el-image fit="cover" :src="property.badge.imgUrl" class="h-26px w-38px" />
|
||||||
</div>
|
</div>
|
||||||
<!-- 商品封面图 -->
|
<!-- 商品封面图 -->
|
||||||
<el-image fit="cover" :src="spu.picUrl" :style="{ width: imageSize, height: imageSize }" />
|
<div
|
||||||
|
:class="[
|
||||||
|
'h-140px',
|
||||||
|
{
|
||||||
|
'w-full': property.layoutType !== 'oneColSmallImg',
|
||||||
|
'w-140px': property.layoutType === 'oneColSmallImg'
|
||||||
|
}
|
||||||
|
]"
|
||||||
|
>
|
||||||
|
<el-image fit="cover" class="h-full w-full" :src="spu.picUrl" />
|
||||||
|
</div>
|
||||||
<div
|
<div
|
||||||
:class="[
|
:class="[
|
||||||
' flex flex-col gap-8px p-8px box-border',
|
' flex flex-col gap-8px p-8px box-border',
|
||||||
{
|
{
|
||||||
'w-[calc(100%-64px)]': columns === 2,
|
'w-full': property.layoutType !== 'oneColSmallImg',
|
||||||
'w-full': columns === 3
|
'w-[calc(100%-140px-16px)]': property.layoutType === 'oneColSmallImg'
|
||||||
}
|
}
|
||||||
]"
|
]"
|
||||||
>
|
>
|
||||||
<!-- 商品名称 -->
|
<!-- 商品名称 -->
|
||||||
<div
|
<div
|
||||||
v-if="property.fields.name.show"
|
v-if="property.fields.name.show"
|
||||||
class="truncate text-12px"
|
:class="[
|
||||||
|
'text-14px ',
|
||||||
|
{
|
||||||
|
truncate: property.layoutType !== 'oneColSmallImg',
|
||||||
|
'overflow-ellipsis line-clamp-2': property.layoutType === 'oneColSmallImg'
|
||||||
|
}
|
||||||
|
]"
|
||||||
:style="{ color: property.fields.name.color }"
|
:style="{ color: property.fields.name.color }"
|
||||||
>
|
>
|
||||||
{{ spu.name }}
|
{{ spu.name }}
|
||||||
</div>
|
</div>
|
||||||
|
<!-- 商品简介 -->
|
||||||
|
<div
|
||||||
|
v-if="property.fields.introduction.show"
|
||||||
|
class="truncate text-12px"
|
||||||
|
:style="{ color: property.fields.introduction.color }"
|
||||||
|
>
|
||||||
|
{{ spu.introduction }}
|
||||||
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<!-- 商品价格 -->
|
<!-- 价格 -->
|
||||||
<span
|
<span
|
||||||
v-if="property.fields.price.show"
|
v-if="property.fields.price.show"
|
||||||
class="text-12px"
|
class="text-16px"
|
||||||
:style="{ color: property.fields.price.color }"
|
:style="{ color: property.fields.price.color }"
|
||||||
>
|
>
|
||||||
¥{{ spu.price }}
|
¥{{ fenToYuan(spu.price || Infinity) }}
|
||||||
|
</span>
|
||||||
|
<!-- 市场价 -->
|
||||||
|
<span
|
||||||
|
v-if="property.fields.marketPrice.show && spu.marketPrice"
|
||||||
|
class="ml-4px text-10px line-through"
|
||||||
|
:style="{ color: property.fields.marketPrice.color }"
|
||||||
|
>¥{{ fenToYuan(spu.marketPrice) }}</span
|
||||||
|
>
|
||||||
|
</div>
|
||||||
|
<div class="text-12px">
|
||||||
|
<!-- 销量 -->
|
||||||
|
<span
|
||||||
|
v-if="property.fields.salesCount.show"
|
||||||
|
:style="{ color: property.fields.salesCount.color }"
|
||||||
|
>
|
||||||
|
已售{{ (spu.salesCount || 0) + (spu.virtualSalesCount || 0) }}件
|
||||||
|
</span>
|
||||||
|
<!-- 库存 -->
|
||||||
|
<span v-if="property.fields.stock.show" :style="{ color: property.fields.stock.color }">
|
||||||
|
库存{{ spu.stock || 0 }}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<!-- 购买按钮 -->
|
||||||
|
<div class="absolute bottom-8px right-8px">
|
||||||
|
<!-- 文字按钮 -->
|
||||||
|
<span
|
||||||
|
v-if="property.btnBuy.type === 'text'"
|
||||||
|
class="rounded-full p-x-12px p-y-4px text-12px text-white"
|
||||||
|
:style="{
|
||||||
|
background: `linear-gradient(to right, ${property.btnBuy.bgBeginColor}, ${property.btnBuy.bgEndColor}`
|
||||||
|
}"
|
||||||
|
>
|
||||||
|
{{ property.btnBuy.text }}
|
||||||
|
</span>
|
||||||
|
<!-- 图片按钮 -->
|
||||||
|
<el-image
|
||||||
|
v-else
|
||||||
|
class="h-28px w-28px rounded-full"
|
||||||
|
fit="cover"
|
||||||
|
:src="property.btnBuy.imgUrl"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</el-scrollbar>
|
|
||||||
</template>
|
</template>
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { PromotionSeckillProperty } from './config'
|
import { PromotionSeckillProperty } from './config'
|
||||||
import * as ProductSpuApi from '@/api/mall/product/spu'
|
import * as ProductSpuApi from '@/api/mall/product/spu'
|
||||||
import * as SeckillActivityApi from '@/api/mall/promotion/seckill/seckillActivity'
|
import * as SeckillActivityApi from '@/api/mall/promotion/seckill/seckillActivity'
|
||||||
|
import { fenToYuan } from '@/utils'
|
||||||
|
|
||||||
/** 秒杀 */
|
/** 秒杀卡片 */
|
||||||
defineOptions({ name: 'PromotionSeckill' })
|
defineOptions({ name: 'PromotionSeckill' })
|
||||||
// 定义属性
|
// 定义属性
|
||||||
const props = defineProps<{ property: PromotionSeckillProperty }>()
|
const props = defineProps<{ property: PromotionSeckillProperty }>()
|
||||||
// 商品列表
|
// 商品列表
|
||||||
const spuList = ref<ProductSpuApi.Spu[]>([])
|
const spuList = ref<ProductSpuApi.Spu[]>([])
|
||||||
|
const spuIdList = ref<number[]>([])
|
||||||
|
const seckillActivityList = ref<SeckillActivityApi.SeckillActivityVO[]>([])
|
||||||
|
|
||||||
watch(
|
watch(
|
||||||
() => props.property.activityId,
|
() => props.property.activityIds,
|
||||||
async () => {
|
async () => {
|
||||||
if (!props.property.activityId) return
|
try {
|
||||||
const activity = await SeckillActivityApi.getSeckillActivity(props.property.activityId)
|
// 新添加的秒杀组件,是没有活动ID的
|
||||||
if (!activity?.spuId) return
|
const activityIds = props.property.activityIds
|
||||||
spuList.value = [await ProductSpuApi.getSpu(activity.spuId)]
|
// 检查活动ID的有效性
|
||||||
|
if (Array.isArray(activityIds) && activityIds.length > 0) {
|
||||||
|
// 获取秒杀活动详情列表
|
||||||
|
seckillActivityList.value =
|
||||||
|
await SeckillActivityApi.getSeckillActivityListByIds(activityIds)
|
||||||
|
|
||||||
|
// 获取秒杀活动的 SPU 详情列表
|
||||||
|
spuList.value = []
|
||||||
|
spuIdList.value = seckillActivityList.value
|
||||||
|
.map((activity) => activity.spuId)
|
||||||
|
.filter((spuId): spuId is number => typeof spuId === 'number')
|
||||||
|
if (spuIdList.value.length > 0) {
|
||||||
|
spuList.value = await ProductSpuApi.getSpuDetailList(spuIdList.value)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 更新 SPU 的最低价格
|
||||||
|
seckillActivityList.value.forEach((activity) => {
|
||||||
|
// 匹配spuId
|
||||||
|
const spu = spuList.value.find((spu) => spu.id === activity.spuId)
|
||||||
|
if (spu) {
|
||||||
|
// 赋值活动价格,哪个最便宜就赋值哪个
|
||||||
|
spu.price = Math.min(activity.seckillPrice || Infinity, spu.price || Infinity)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('获取秒杀活动细节或 SPU 细节时出错:', error)
|
||||||
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
immediate: true,
|
immediate: true,
|
||||||
deep: true
|
deep: true
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
// 手机宽度
|
|
||||||
const phoneWidth = ref(375)
|
/**
|
||||||
|
* 计算商品的间距
|
||||||
|
* @param index 商品索引
|
||||||
|
*/
|
||||||
|
const calculateSpace = (index: number) => {
|
||||||
|
// 商品的列数
|
||||||
|
const columns = props.property.layoutType === 'twoCol' ? 2 : 1
|
||||||
|
// 第一列没有左边距
|
||||||
|
const marginLeft = index % columns === 0 ? '0' : props.property.space + 'px'
|
||||||
|
// 第一行没有上边距
|
||||||
|
const marginTop = index < columns ? '0' : props.property.space + 'px'
|
||||||
|
|
||||||
|
return { marginLeft, marginTop }
|
||||||
|
}
|
||||||
|
|
||||||
// 容器
|
// 容器
|
||||||
const containerRef = ref()
|
const containerRef = ref()
|
||||||
// 商品的列数
|
// 计算商品的宽度
|
||||||
const columns = ref(2)
|
const calculateWidth = () => {
|
||||||
// 滚动条宽度
|
let width = '100%'
|
||||||
const scrollbarWidth = ref('100%')
|
// 双列时每列的宽度为:(总宽度 - 间距)/ 2
|
||||||
// 商品图大小
|
if (props.property.layoutType === 'twoCol') {
|
||||||
const imageSize = ref('0')
|
width = `${(containerRef.value.offsetWidth - props.property.space) / 2}px`
|
||||||
// 商品网络列数
|
}
|
||||||
const gridTemplateColumns = ref('')
|
return { width }
|
||||||
// 计算布局参数
|
}
|
||||||
watch(
|
|
||||||
() => [props.property, phoneWidth, spuList.value.length],
|
|
||||||
() => {
|
|
||||||
// 计算列数
|
|
||||||
columns.value = props.property.layoutType === 'oneCol' ? 1 : 3
|
|
||||||
// 每列的宽度为:(总宽度 - 间距 * (列数 - 1))/ 列数
|
|
||||||
const productWidth =
|
|
||||||
(phoneWidth.value - props.property.space * (columns.value - 1)) / columns.value
|
|
||||||
// 商品图布局:2列时,左右布局 3列时,上下布局
|
|
||||||
imageSize.value = columns.value === 2 ? '64px' : `${productWidth}px`
|
|
||||||
// 指定列数
|
|
||||||
gridTemplateColumns.value = `repeat(${columns.value}, auto)`
|
|
||||||
// 不滚动
|
|
||||||
scrollbarWidth.value = '100%'
|
|
||||||
},
|
|
||||||
{ immediate: true, deep: true }
|
|
||||||
)
|
|
||||||
onMounted(() => {
|
|
||||||
// 提取手机宽度
|
|
||||||
phoneWidth.value = containerRef.value?.wrapRef?.offsetWidth || 375
|
|
||||||
})
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped lang="scss"></style>
|
<style scoped lang="scss"></style>
|
||||||
|
|
|
||||||
|
|
@ -2,30 +2,31 @@
|
||||||
<ComponentContainerProperty v-model="formData.style">
|
<ComponentContainerProperty v-model="formData.style">
|
||||||
<el-form label-width="80px" :model="formData">
|
<el-form label-width="80px" :model="formData">
|
||||||
<el-card header="秒杀活动" class="property-group" shadow="never">
|
<el-card header="秒杀活动" class="property-group" shadow="never">
|
||||||
<el-form-item label="秒杀活动" prop="activityId">
|
<SeckillShowcase v-model="formData.activityIds" />
|
||||||
<el-select v-model="formData.activityId">
|
|
||||||
<el-option
|
|
||||||
v-for="activity in activityList"
|
|
||||||
:key="activity.id"
|
|
||||||
:label="activity.name"
|
|
||||||
:value="activity.id"
|
|
||||||
/>
|
|
||||||
</el-select>
|
|
||||||
</el-form-item>
|
|
||||||
</el-card>
|
</el-card>
|
||||||
<el-card header="商品样式" class="property-group" shadow="never">
|
<el-card header="商品样式" class="property-group" shadow="never">
|
||||||
<el-form-item label="布局" prop="type">
|
<el-form-item label="布局" prop="type">
|
||||||
<el-radio-group v-model="formData.layoutType">
|
<el-radio-group v-model="formData.layoutType">
|
||||||
<el-tooltip class="item" content="单列" placement="bottom">
|
<el-tooltip class="item" content="单列大图" placement="bottom">
|
||||||
<el-radio-button label="oneCol">
|
<el-radio-button value="oneColBigImg">
|
||||||
<Icon icon="fluent:text-column-one-24-filled" />
|
<Icon icon="fluent:text-column-one-24-filled" />
|
||||||
</el-radio-button>
|
</el-radio-button>
|
||||||
</el-tooltip>
|
</el-tooltip>
|
||||||
<el-tooltip class="item" content="三列" placement="bottom">
|
<el-tooltip class="item" content="单列小图" placement="bottom">
|
||||||
<el-radio-button label="threeCol">
|
<el-radio-button value="oneColSmallImg">
|
||||||
<Icon icon="fluent:text-column-three-24-filled" />
|
<Icon icon="fluent:text-column-two-left-24-filled" />
|
||||||
</el-radio-button>
|
</el-radio-button>
|
||||||
</el-tooltip>
|
</el-tooltip>
|
||||||
|
<el-tooltip class="item" content="双列" placement="bottom">
|
||||||
|
<el-radio-button value="twoCol">
|
||||||
|
<Icon icon="fluent:text-column-two-24-filled" />
|
||||||
|
</el-radio-button>
|
||||||
|
</el-tooltip>
|
||||||
|
<!--<el-tooltip class="item" content="三列" placement="bottom">
|
||||||
|
<el-radio-button value="threeCol">
|
||||||
|
<Icon icon="fluent:text-column-three-24-filled" />
|
||||||
|
</el-radio-button>
|
||||||
|
</el-tooltip>-->
|
||||||
</el-radio-group>
|
</el-radio-group>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item label="商品名称" prop="fields.name.show">
|
<el-form-item label="商品名称" prop="fields.name.show">
|
||||||
|
|
@ -34,12 +35,36 @@
|
||||||
<el-checkbox v-model="formData.fields.name.show" />
|
<el-checkbox v-model="formData.fields.name.show" />
|
||||||
</div>
|
</div>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
|
<el-form-item label="商品简介" prop="fields.introduction.show">
|
||||||
|
<div class="flex gap-8px">
|
||||||
|
<ColorInput v-model="formData.fields.introduction.color" />
|
||||||
|
<el-checkbox v-model="formData.fields.introduction.show" />
|
||||||
|
</div>
|
||||||
|
</el-form-item>
|
||||||
<el-form-item label="商品价格" prop="fields.price.show">
|
<el-form-item label="商品价格" prop="fields.price.show">
|
||||||
<div class="flex gap-8px">
|
<div class="flex gap-8px">
|
||||||
<ColorInput v-model="formData.fields.price.color" />
|
<ColorInput v-model="formData.fields.price.color" />
|
||||||
<el-checkbox v-model="formData.fields.price.show" />
|
<el-checkbox v-model="formData.fields.price.show" />
|
||||||
</div>
|
</div>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
|
<el-form-item label="市场价" prop="fields.marketPrice.show">
|
||||||
|
<div class="flex gap-8px">
|
||||||
|
<ColorInput v-model="formData.fields.marketPrice.color" />
|
||||||
|
<el-checkbox v-model="formData.fields.marketPrice.show" />
|
||||||
|
</div>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="商品销量" prop="fields.salesCount.show">
|
||||||
|
<div class="flex gap-8px">
|
||||||
|
<ColorInput v-model="formData.fields.salesCount.color" />
|
||||||
|
<el-checkbox v-model="formData.fields.salesCount.show" />
|
||||||
|
</div>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="商品库存" prop="fields.stock.show">
|
||||||
|
<div class="flex gap-8px">
|
||||||
|
<ColorInput v-model="formData.fields.stock.color" />
|
||||||
|
<el-checkbox v-model="formData.fields.stock.show" />
|
||||||
|
</div>
|
||||||
|
</el-form-item>
|
||||||
</el-card>
|
</el-card>
|
||||||
<el-card header="角标" class="property-group" shadow="never">
|
<el-card header="角标" class="property-group" shadow="never">
|
||||||
<el-form-item label="角标" prop="badge.show">
|
<el-form-item label="角标" prop="badge.show">
|
||||||
|
|
@ -51,6 +76,32 @@
|
||||||
</UploadImg>
|
</UploadImg>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</el-card>
|
</el-card>
|
||||||
|
<el-card header="按钮" class="property-group" shadow="never">
|
||||||
|
<el-form-item label="按钮类型" prop="btnBuy.type">
|
||||||
|
<el-radio-group v-model="formData.btnBuy.type">
|
||||||
|
<el-radio-button value="text">文字</el-radio-button>
|
||||||
|
<el-radio-button value="img">图片</el-radio-button>
|
||||||
|
</el-radio-group>
|
||||||
|
</el-form-item>
|
||||||
|
<template v-if="formData.btnBuy.type === 'text'">
|
||||||
|
<el-form-item label="按钮文字" prop="btnBuy.text">
|
||||||
|
<el-input v-model="formData.btnBuy.text" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="左侧背景" prop="btnBuy.bgBeginColor">
|
||||||
|
<ColorInput v-model="formData.btnBuy.bgBeginColor" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="右侧背景" prop="btnBuy.bgEndColor">
|
||||||
|
<ColorInput v-model="formData.btnBuy.bgEndColor" />
|
||||||
|
</el-form-item>
|
||||||
|
</template>
|
||||||
|
<template v-else>
|
||||||
|
<el-form-item label="图片" prop="btnBuy.imgUrl">
|
||||||
|
<UploadImg v-model="formData.btnBuy.imgUrl" height="56px" width="56px">
|
||||||
|
<template #tip> 建议尺寸:56 * 56</template>
|
||||||
|
</UploadImg>
|
||||||
|
</el-form-item>
|
||||||
|
</template>
|
||||||
|
</el-card>
|
||||||
<el-card header="商品样式" class="property-group" shadow="never">
|
<el-card header="商品样式" class="property-group" shadow="never">
|
||||||
<el-form-item label="上圆角" prop="borderRadiusTop">
|
<el-form-item label="上圆角" prop="borderRadiusTop">
|
||||||
<el-slider
|
<el-slider
|
||||||
|
|
@ -92,6 +143,7 @@ import { PromotionSeckillProperty } from './config'
|
||||||
import { usePropertyForm } from '@/components/DiyEditor/util'
|
import { usePropertyForm } from '@/components/DiyEditor/util'
|
||||||
import * as SeckillActivityApi from '@/api/mall/promotion/seckill/seckillActivity'
|
import * as SeckillActivityApi from '@/api/mall/promotion/seckill/seckillActivity'
|
||||||
import { CommonStatusEnum } from '@/utils/constants'
|
import { CommonStatusEnum } from '@/utils/constants'
|
||||||
|
import SeckillShowcase from '@/views/mall/promotion/seckill/components/SeckillShowcase.vue'
|
||||||
|
|
||||||
// 秒杀属性面板
|
// 秒杀属性面板
|
||||||
defineOptions({ name: 'PromotionSeckillProperty' })
|
defineOptions({ name: 'PromotionSeckillProperty' })
|
||||||
|
|
@ -100,7 +152,7 @@ const props = defineProps<{ modelValue: PromotionSeckillProperty }>()
|
||||||
const emit = defineEmits(['update:modelValue'])
|
const emit = defineEmits(['update:modelValue'])
|
||||||
const { formData } = usePropertyForm(props.modelValue, emit)
|
const { formData } = usePropertyForm(props.modelValue, emit)
|
||||||
// 活动列表
|
// 活动列表
|
||||||
const activityList = ref<SeckillActivityApi.SeckillActivityVO>([])
|
const activityList = ref<SeckillActivityApi.SeckillActivityVO[]>([])
|
||||||
onMounted(async () => {
|
onMounted(async () => {
|
||||||
const { list } = await SeckillActivityApi.getSeckillActivityPage({
|
const { list } = await SeckillActivityApi.getSeckillActivityPage({
|
||||||
status: CommonStatusEnum.ENABLE
|
status: CommonStatusEnum.ENABLE
|
||||||
|
|
|
||||||
|
|
@ -13,12 +13,12 @@
|
||||||
<el-form-item label="框体样式">
|
<el-form-item label="框体样式">
|
||||||
<el-radio-group v-model="formData!.borderRadius">
|
<el-radio-group v-model="formData!.borderRadius">
|
||||||
<el-tooltip content="方形" placement="top">
|
<el-tooltip content="方形" placement="top">
|
||||||
<el-radio-button :label="0">
|
<el-radio-button :value="0">
|
||||||
<Icon icon="tabler:input-search" />
|
<Icon icon="tabler:input-search" />
|
||||||
</el-radio-button>
|
</el-radio-button>
|
||||||
</el-tooltip>
|
</el-tooltip>
|
||||||
<el-tooltip content="圆形" placement="top">
|
<el-tooltip content="圆形" placement="top">
|
||||||
<el-radio-button :label="10">
|
<el-radio-button :value="10">
|
||||||
<Icon icon="iconoir:input-search" />
|
<Icon icon="iconoir:input-search" />
|
||||||
</el-radio-button>
|
</el-radio-button>
|
||||||
</el-tooltip>
|
</el-tooltip>
|
||||||
|
|
@ -30,12 +30,12 @@
|
||||||
<el-form-item label="文本位置" prop="placeholderPosition">
|
<el-form-item label="文本位置" prop="placeholderPosition">
|
||||||
<el-radio-group v-model="formData!.placeholderPosition">
|
<el-radio-group v-model="formData!.placeholderPosition">
|
||||||
<el-tooltip content="居左" placement="top">
|
<el-tooltip content="居左" placement="top">
|
||||||
<el-radio-button label="left">
|
<el-radio-button value="left">
|
||||||
<Icon icon="ant-design:align-left-outlined" />
|
<Icon icon="ant-design:align-left-outlined" />
|
||||||
</el-radio-button>
|
</el-radio-button>
|
||||||
</el-tooltip>
|
</el-tooltip>
|
||||||
<el-tooltip content="居中" placement="top">
|
<el-tooltip content="居中" placement="top">
|
||||||
<el-radio-button label="center">
|
<el-radio-button value="center">
|
||||||
<Icon icon="ant-design:align-center-outlined" />
|
<Icon icon="ant-design:align-center-outlined" />
|
||||||
</el-radio-button>
|
</el-radio-button>
|
||||||
</el-tooltip>
|
</el-tooltip>
|
||||||
|
|
|
||||||
|
|
@ -27,8 +27,8 @@
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item label="导航背景">
|
<el-form-item label="导航背景">
|
||||||
<el-radio-group v-model="formData!.style.bgType">
|
<el-radio-group v-model="formData!.style.bgType">
|
||||||
<el-radio-button label="color">纯色</el-radio-button>
|
<el-radio-button value="color">纯色</el-radio-button>
|
||||||
<el-radio-button label="img">图片</el-radio-button>
|
<el-radio-button value="img">图片</el-radio-button>
|
||||||
</el-radio-group>
|
</el-radio-group>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item label="选择颜色" v-if="formData!.style.bgType === 'color'">
|
<el-form-item label="选择颜色" v-if="formData!.style.bgType === 'color'">
|
||||||
|
|
@ -79,7 +79,7 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { TabBarProperty, THEME_LIST } from './config'
|
import { TabBarProperty, component, THEME_LIST } from './config'
|
||||||
import { usePropertyForm } from '@/components/DiyEditor/util'
|
import { usePropertyForm } from '@/components/DiyEditor/util'
|
||||||
// 底部导航栏
|
// 底部导航栏
|
||||||
defineOptions({ name: 'TabBarProperty' })
|
defineOptions({ name: 'TabBarProperty' })
|
||||||
|
|
@ -88,6 +88,9 @@ const props = defineProps<{ modelValue: TabBarProperty }>()
|
||||||
const emit = defineEmits(['update:modelValue'])
|
const emit = defineEmits(['update:modelValue'])
|
||||||
const { formData } = usePropertyForm(props.modelValue, emit)
|
const { formData } = usePropertyForm(props.modelValue, emit)
|
||||||
|
|
||||||
|
// 将数据库的值更新到右侧属性栏
|
||||||
|
component.property.items = formData.value.items
|
||||||
|
|
||||||
// 要的主题
|
// 要的主题
|
||||||
const handleThemeChange = () => {
|
const handleThemeChange = () => {
|
||||||
const theme = THEME_LIST.find((theme) => theme.id === formData.value.theme)
|
const theme = THEME_LIST.find((theme) => theme.id === formData.value.theme)
|
||||||
|
|
|
||||||