Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a0ce1f3a5b | ||
|
|
10a5d4d265 | ||
|
|
024d08555b | ||
|
|
948ef08533 | ||
|
|
bf3e051d74 | ||
|
|
49a806a6f9 | ||
|
|
2dcd131097 | ||
|
|
d852103929 | ||
|
|
2c9393702d | ||
|
|
9fa769b16a | ||
|
|
163613e90c | ||
|
|
1e70a1d57e | ||
|
|
68fc1760a5 | ||
|
|
24849335a8 | ||
|
|
2a55d88a44 | ||
|
|
9cbb3aa84a | ||
|
|
5151824110 | ||
|
|
25347b3ea5 | ||
|
|
d29dfef7c7 | ||
|
|
26bb847383 | ||
|
|
fdfb028dd9 | ||
|
|
a77658b336 | ||
|
|
66c48a913c | ||
|
|
2be14817a0 | ||
|
|
a9400c5171 | ||
|
|
10afbe8e2f | ||
|
|
a38ddb699a | ||
|
|
20b4a7fd66 | ||
|
|
4f89eb6ba9 | ||
|
|
2b8eb49f71 | ||
|
|
bdf95dc0f8 | ||
|
|
a9c231bed6 | ||
|
|
efe702d383 | ||
|
|
2c76d3aeee | ||
|
|
c0905fdc8e | ||
|
|
f1c858b9af | ||
|
|
a5d6d18b6a | ||
|
|
ab64a1d1df | ||
|
|
1e68cd53a0 | ||
|
|
804859bbd8 | ||
|
|
ec87c1b61d | ||
|
|
4eb3c8a2f9 | ||
|
|
a0f7775321 | ||
|
|
09fa30b0d6 | ||
|
|
29e7161eba | ||
|
|
9bdba0f67e | ||
|
|
3af6811c72 | ||
|
|
8801c82e15 | ||
|
|
17be1abb65 | ||
|
|
d36c4ab36c | ||
|
|
1fb7701e6e | ||
|
|
81d84877bc | ||
|
|
7fd564307d | ||
|
|
5e265a9094 | ||
|
|
d71097ac31 | ||
|
|
4fbb3b88e6 | ||
|
|
7448f6e08c | ||
|
|
0a4023ef00 | ||
|
|
45521c139c | ||
|
|
0338caeef6 | ||
|
|
1eefa02c4c | ||
|
|
631c105f94 | ||
|
|
d766b36e72 | ||
|
|
8aa49f2e1d | ||
|
|
ccb997ac2a | ||
|
|
aadad39a69 | ||
|
|
261d8b2aa4 | ||
|
|
67630e31b2 | ||
|
|
32dab6bc08 | ||
|
|
2223f7f197 | ||
|
|
21e8dbf985 | ||
|
|
9591ed7084 | ||
|
|
b86f082306 | ||
|
|
2cfe006def | ||
|
|
dd4482fad2 | ||
|
|
a1fd3afc32 | ||
|
|
ca35e1a4af | ||
|
|
df1c565cd9 | ||
|
|
be4750a174 | ||
|
|
346863a8a9 | ||
|
|
1abff21e56 | ||
|
|
80355f3acd | ||
|
|
33162a12c0 | ||
|
|
41a404a52c | ||
|
|
455688eaa8 | ||
|
|
59c6670775 | ||
|
|
26a082788d | ||
|
|
edfdee8cc1 | ||
|
|
76025b6821 | ||
|
|
605f906056 | ||
|
|
59ff6b13fb | ||
|
|
9ff4f1f7a9 | ||
|
|
a7512ba2c7 | ||
|
|
5002fc5fcc | ||
|
|
16aac73858 | ||
|
|
cd89f75b07 | ||
|
|
784623724d | ||
|
|
eec3a21852 | ||
|
|
9128fa98f5 | ||
|
|
b4aa09aa6c | ||
|
|
1e6ceabd3b | ||
|
|
ce79dd68a3 | ||
|
|
d6291382e8 | ||
|
|
f8c4700c2e | ||
|
|
b3d66cd0aa | ||
|
|
ee9667504f | ||
|
|
0c613e94a8 | ||
|
|
dee8215da2 | ||
|
|
d9ed3d1a6b | ||
|
|
dde61c29a0 | ||
|
|
ba49b2851c | ||
|
|
2f10f0ffc1 | ||
|
|
aac60380bc | ||
|
|
2a0b329565 | ||
|
|
19e3297667 | ||
|
|
55c64d9c1b | ||
|
|
3009c04d0a | ||
|
|
c47aabe880 | ||
|
|
f9994c585c | ||
|
|
7748704639 | ||
|
|
5eb30c7e85 | ||
|
|
0b9cf35b17 | ||
|
|
85fe4ad8d6 | ||
|
|
aecc9bb8cb | ||
|
|
3d2d48b601 | ||
|
|
f3e89c0c55 | ||
|
|
fa421d0895 | ||
|
|
a6deb69ddc | ||
|
|
3407cc66a9 | ||
|
|
dd4a77ff22 | ||
|
|
6593c603a2 | ||
|
|
2ceb3b978c | ||
|
|
5766dc81d1 | ||
|
|
b103c40874 | ||
|
|
1066551fd1 | ||
|
|
757fbddef2 | ||
|
|
a77173f2ae | ||
|
|
d6ff66dc1d | ||
|
|
b982397449 | ||
|
|
3c8ffc8c21 | ||
|
|
c058048fac | ||
|
|
2c1edfa781 | ||
|
|
af67db577d | ||
|
|
b0c1e55c6a | ||
|
|
d5f9d31d32 | ||
|
|
7e44b621a5 | ||
|
|
bdc80ea55b | ||
|
|
b4ba1043d6 | ||
|
|
a711e96b23 | ||
|
|
915702183d | ||
|
|
08be359578 | ||
|
|
23a6bf5ef5 | ||
|
|
88e5e67a89 | ||
|
|
31a42fc3f7 | ||
|
|
5f26c4afe9 | ||
|
|
7de0e93d5a | ||
|
|
2cda0cdd9f | ||
|
|
8b787176b6 | ||
|
|
443f65b5dc | ||
|
|
ddfceddf4a | ||
|
|
ae8cb9218c | ||
|
|
85810c0786 | ||
|
|
c5b2fdecd5 | ||
|
|
50430bb9cd | ||
|
|
661dc2ff67 | ||
|
|
d69134adfa | ||
|
|
1ba450103e | ||
|
|
24773a6eaa | ||
|
|
e1f9e16a77 | ||
|
|
3c19cb1a85 | ||
|
|
b3515b2db5 | ||
|
|
4e1748caa3 | ||
|
|
cec8582a90 | ||
|
|
324d1e859e | ||
|
|
c2e8eaed60 | ||
|
|
7df7719be3 | ||
|
|
d4dc105b4f | ||
|
|
36702f5f90 | ||
|
|
937fc17150 | ||
|
|
eb97447bb7 | ||
|
|
764e1715be | ||
|
|
2700b08c69 | ||
|
|
aceac6daae | ||
|
|
4440ec866c |
No files matched your search
@@ -1,19 +0,0 @@
|
|||||||
# 本地开发环境
|
|
||||||
NODE_ENV=development
|
|
||||||
|
|
||||||
VITE_DEV=true
|
|
||||||
|
|
||||||
# 请求路径
|
|
||||||
VITE_BASE_URL='http://127.0.0.1:48080'
|
|
||||||
|
|
||||||
# 上传路径
|
|
||||||
VITE_UPLOAD_URL='http://127.0.0.1:48080/admin-api/infra/file/upload'
|
|
||||||
|
|
||||||
# 接口前缀
|
|
||||||
VITE_API_BASEPATH=/dev-api
|
|
||||||
|
|
||||||
# 接口地址
|
|
||||||
VITE_API_URL=/admin-api
|
|
||||||
|
|
||||||
# 打包路径
|
|
||||||
VITE_BASE_PATH=/
|
|
||||||
@@ -1,13 +1,14 @@
|
|||||||
# 开发环境
|
# 开发环境:本地只启动前端项目,依赖开发环境(后端、APP)
|
||||||
NODE_ENV=development
|
NODE_ENV=development
|
||||||
|
|
||||||
VITE_DEV=false
|
VITE_DEV=true
|
||||||
|
|
||||||
# 请求路径
|
# 请求路径
|
||||||
VITE_BASE_URL='http://localhost:48080'
|
VITE_BASE_URL='http://api-dashboard.yudao.iocoder.cn'
|
||||||
|
# VITE_BASE_URL='http://dofast.demo.huizhizao.vip:20001'
|
||||||
|
|
||||||
# 上传路径
|
# 上传路径
|
||||||
VITE_UPLOAD_URL='http://localhost:48080/admin-api/infra/file/upload'
|
VITE_UPLOAD_URL='http://api-dashboard.yudao.iocoder.cn/admin-api/infra/file/upload'
|
||||||
|
|
||||||
# 接口前缀
|
# 接口前缀
|
||||||
VITE_API_BASEPATH=/dev-api
|
VITE_API_BASEPATH=/dev-api
|
||||||
@@ -15,17 +16,23 @@ VITE_API_BASEPATH=/dev-api
|
|||||||
# 接口地址
|
# 接口地址
|
||||||
VITE_API_URL=/admin-api
|
VITE_API_URL=/admin-api
|
||||||
|
|
||||||
# 打包路径
|
|
||||||
VITE_BASE_PATH=/
|
|
||||||
|
|
||||||
# 是否删除debugger
|
# 是否删除debugger
|
||||||
VITE_DROP_DEBUGGER=true
|
VITE_DROP_DEBUGGER=false
|
||||||
|
|
||||||
# 是否删除console.log
|
# 是否删除console.log
|
||||||
VITE_DROP_CONSOLE=false
|
VITE_DROP_CONSOLE=false
|
||||||
|
|
||||||
# 是否sourcemap
|
# 是否sourcemap
|
||||||
VITE_SOURCEMAP=false
|
VITE_SOURCEMAP=true
|
||||||
|
|
||||||
|
# 打包路径
|
||||||
|
VITE_BASE_PATH=/
|
||||||
|
|
||||||
# 输出路径
|
# 输出路径
|
||||||
VITE_OUT_DIR=dist
|
VITE_OUT_DIR=dist
|
||||||
|
|
||||||
|
# 商城H5会员端域名
|
||||||
|
VITE_MALL_H5_DOMAIN='http://mall.yudao.iocoder.cn'
|
||||||
|
|
||||||
|
# 验证码的开关
|
||||||
|
VITE_APP_CAPTCHA_ENABLE=false
|
||||||
+10
-10
@@ -1,13 +1,13 @@
|
|||||||
# 本地开发环境
|
# 本地开发环境:本地启动所有项目(前端、后端、APP)时使用,不依赖外部环境
|
||||||
NODE_ENV=development
|
NODE_ENV=development
|
||||||
|
|
||||||
VITE_DEV=true
|
VITE_DEV=true
|
||||||
|
|
||||||
# 请求路径
|
# 请求路径
|
||||||
VITE_BASE_URL='http://api-dashboard.yudao.iocoder.cn'
|
VITE_BASE_URL='http://localhost:48080'
|
||||||
|
|
||||||
# 上传路径
|
# 上传路径
|
||||||
VITE_UPLOAD_URL='http://api-dashboard.yudao.iocoder.cn/admin-api/infra/file/upload'
|
VITE_UPLOAD_URL='http://localhost:48080/admin-api/infra/file/upload'
|
||||||
|
|
||||||
# 接口前缀
|
# 接口前缀
|
||||||
VITE_API_BASEPATH=/dev-api
|
VITE_API_BASEPATH=/dev-api
|
||||||
@@ -15,12 +15,6 @@ VITE_API_BASEPATH=/dev-api
|
|||||||
# 接口地址
|
# 接口地址
|
||||||
VITE_API_URL=/admin-api
|
VITE_API_URL=/admin-api
|
||||||
|
|
||||||
# 打包路径
|
|
||||||
VITE_BASE_PATH=/
|
|
||||||
|
|
||||||
# 项目本地运行端口号, 与.vscode/launch.json配合
|
|
||||||
VITE_PORT=80
|
|
||||||
|
|
||||||
# 是否删除debugger
|
# 是否删除debugger
|
||||||
VITE_DROP_DEBUGGER=false
|
VITE_DROP_DEBUGGER=false
|
||||||
|
|
||||||
@@ -28,7 +22,13 @@ VITE_DROP_DEBUGGER=false
|
|||||||
VITE_DROP_CONSOLE=false
|
VITE_DROP_CONSOLE=false
|
||||||
|
|
||||||
# 是否sourcemap
|
# 是否sourcemap
|
||||||
VITE_SOURCEMAP=true
|
VITE_SOURCEMAP=false
|
||||||
|
|
||||||
|
# 打包路径
|
||||||
|
VITE_BASE_PATH=/
|
||||||
|
|
||||||
|
# 商城H5会员端域名
|
||||||
|
VITE_MALL_H5_DOMAIN='http://localhost:3000'
|
||||||
|
|
||||||
# 验证码的开关
|
# 验证码的开关
|
||||||
VITE_APP_CAPTCHA_ENABLE=false
|
VITE_APP_CAPTCHA_ENABLE=false
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
# 生产环境
|
# 生产环境:只在打包时使用
|
||||||
NODE_ENV=production
|
NODE_ENV=production
|
||||||
|
|
||||||
VITE_DEV=false
|
VITE_DEV=false
|
||||||
@@ -28,4 +28,7 @@ VITE_SOURCEMAP=false
|
|||||||
VITE_BASE_PATH=/
|
VITE_BASE_PATH=/
|
||||||
|
|
||||||
# 输出路径
|
# 输出路径
|
||||||
VITE_OUT_DIR=dist-pro
|
VITE_OUT_DIR=dist-prod
|
||||||
|
|
||||||
|
# 商城H5会员端域名
|
||||||
|
VITE_MALL_H5_DOMAIN='http://mall.yudao.iocoder.cn'
|
||||||
+4
-1
@@ -1,4 +1,4 @@
|
|||||||
# 生产环境
|
# 预发布环境:只在打包时使用
|
||||||
NODE_ENV=production
|
NODE_ENV=production
|
||||||
|
|
||||||
VITE_DEV=false
|
VITE_DEV=false
|
||||||
@@ -29,3 +29,6 @@ VITE_BASE_PATH='http://static-vue3.yudao.iocoder.cn/'
|
|||||||
|
|
||||||
# 输出路径
|
# 输出路径
|
||||||
VITE_OUT_DIR=dist-stage
|
VITE_OUT_DIR=dist-stage
|
||||||
|
|
||||||
|
# 商城H5会员端域名
|
||||||
|
VITE_MALL_H5_DOMAIN='http://mall.yudao.iocoder.cn'
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
# 开发环境
|
# 测试环境:只在打包时使用
|
||||||
NODE_ENV=production
|
NODE_ENV=production
|
||||||
|
|
||||||
VITE_DEV=false
|
VITE_DEV=false
|
||||||
@@ -28,4 +28,7 @@ VITE_SOURCEMAP=false
|
|||||||
VITE_BASE_PATH=/admin-ui-vue3/
|
VITE_BASE_PATH=/admin-ui-vue3/
|
||||||
|
|
||||||
# 输出路径
|
# 输出路径
|
||||||
VITE_OUT_DIR=dist-dev
|
VITE_OUT_DIR=dist-test
|
||||||
|
|
||||||
|
# 商城H5会员端域名
|
||||||
|
VITE_MALL_H5_DOMAIN='http://mall.yudao.iocoder.cn'
|
||||||
Vendored
+1
-1
@@ -83,7 +83,7 @@
|
|||||||
"editor.defaultFormatter": "esbenp.prettier-vscode"
|
"editor.defaultFormatter": "esbenp.prettier-vscode"
|
||||||
},
|
},
|
||||||
"editor.codeActionsOnSave": {
|
"editor.codeActionsOnSave": {
|
||||||
"source.fixAll.eslint": true
|
"source.fixAll.eslint": "explicit"
|
||||||
},
|
},
|
||||||
"[vue]": {
|
"[vue]": {
|
||||||
"editor.defaultFormatter": "rvest.vs-code-prettier-eslint"
|
"editor.defaultFormatter": "rvest.vs-code-prettier-eslint"
|
||||||
|
|||||||
+9
-12
@@ -1,23 +1,22 @@
|
|||||||
{
|
{
|
||||||
"name": "yudao-ui-admin-vue3",
|
"name": "yudao-ui-admin-vue3",
|
||||||
"version": "1.8.3-snapshot",
|
"version": "2.0.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 --mode base",
|
"dev": "vite --mode local-dev",
|
||||||
"front": "vite --mode front",
|
"dev-server": "vite --mode dev",
|
||||||
"ts:check": "vue-tsc --noEmit",
|
"ts:check": "vue-tsc --noEmit",
|
||||||
"build:pro": "node --max_old_space_size=8192 ./node_modules/vite/bin/vite.js build --mode pro",
|
"build:local-dev": "node --max_old_space_size=8192 ./node_modules/vite/bin/vite.js build --mode local-dev",
|
||||||
"build:dev": "node --max_old_space_size=8192 ./node_modules/vite/bin/vite.js build --mode dev",
|
"build:dev": "node --max_old_space_size=8192 ./node_modules/vite/bin/vite.js build --mode local-dev",
|
||||||
"build:base": "node --max_old_space_size=8192 ./node_modules/vite/bin/vite.js build --mode base",
|
"build:test": "node --max_old_space_size=8192 ./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 --max_old_space_size=8192 ./node_modules/vite/bin/vite.js build --mode stage",
|
||||||
"build:static": "node --max_old_space_size=8192 ./node_modules/vite/bin/vite.js build --mode static",
|
"build:prod": "node --max_old_space_size=8192 ./node_modules/vite/bin/vite.js build --mode prod",
|
||||||
"build:front": "node --max_old_space_size=8192 ./node_modules/vite/bin/vite.js build --mode front",
|
|
||||||
"serve:pro": "vite preview --mode pro",
|
|
||||||
"serve:dev": "vite preview --mode dev",
|
"serve:dev": "vite preview --mode dev",
|
||||||
"preview": "pnpm build:base && vite preview",
|
"serve:prod": "vite preview --mode prod",
|
||||||
|
"preview": "pnpm build:local-dev && 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",
|
||||||
@@ -58,7 +57,6 @@
|
|||||||
"pinia": "^2.1.7",
|
"pinia": "^2.1.7",
|
||||||
"qrcode": "^1.5.3",
|
"qrcode": "^1.5.3",
|
||||||
"qs": "^6.11.2",
|
"qs": "^6.11.2",
|
||||||
"sortablejs": "^1.15.0",
|
|
||||||
"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",
|
||||||
@@ -82,7 +80,6 @@
|
|||||||
"@types/nprogress": "^0.2.3",
|
"@types/nprogress": "^0.2.3",
|
||||||
"@types/qrcode": "^1.5.5",
|
"@types/qrcode": "^1.5.5",
|
||||||
"@types/qs": "^6.9.10",
|
"@types/qs": "^6.9.10",
|
||||||
"@types/sortablejs": "^1.15.5",
|
|
||||||
"@typescript-eslint/eslint-plugin": "^6.11.0",
|
"@typescript-eslint/eslint-plugin": "^6.11.0",
|
||||||
"@typescript-eslint/parser": "^6.11.0",
|
"@typescript-eslint/parser": "^6.11.0",
|
||||||
"@unocss/transformer-variant-group": "^0.57.4",
|
"@unocss/transformer-variant-group": "^0.57.4",
|
||||||
|
|||||||
@@ -20,6 +20,17 @@ export type ProcessInstanceVO = {
|
|||||||
endTime: string
|
endTime: string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type ProcessInstanceCCVO = {
|
||||||
|
type: number,
|
||||||
|
taskName: string,
|
||||||
|
taskKey: string,
|
||||||
|
processInstanceName: string,
|
||||||
|
processInstanceKey: string,
|
||||||
|
startUserId: string,
|
||||||
|
options:string [],
|
||||||
|
reason: string
|
||||||
|
}
|
||||||
|
|
||||||
export const getMyProcessInstancePage = async (params) => {
|
export const getMyProcessInstancePage = async (params) => {
|
||||||
return await request.get({ url: '/bpm/process-instance/my-page', params })
|
return await request.get({ url: '/bpm/process-instance/my-page', params })
|
||||||
}
|
}
|
||||||
@@ -39,3 +50,21 @@ export const cancelProcessInstance = async (id: number, reason: string) => {
|
|||||||
export const getProcessInstance = async (id: number) => {
|
export const getProcessInstance = async (id: number) => {
|
||||||
return await request.get({ url: '/bpm/process-instance/get?id=' + id })
|
return await request.get({ url: '/bpm/process-instance/get?id=' + id })
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 抄送
|
||||||
|
* @param data 抄送数据
|
||||||
|
* @returns 是否抄送成功
|
||||||
|
*/
|
||||||
|
export const createProcessInstanceCC = async (data) => {
|
||||||
|
return await request.post({ url: '/bpm/process-instance/cc/create', data: data })
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 抄送列表
|
||||||
|
* @param params
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
export const getProcessInstanceCCPage = async (params) => {
|
||||||
|
return await request.get({ url: '/bpm/process-instance/cc/my-page', params })
|
||||||
|
}
|
||||||
@@ -44,7 +44,7 @@ export const exportTask = async (params) => {
|
|||||||
|
|
||||||
// 获取所有可回退的节点
|
// 获取所有可回退的节点
|
||||||
export const getReturnList = async (params) => {
|
export const getReturnList = async (params) => {
|
||||||
return await request.get({ url: '/bpm/task/get-return-list', params })
|
return await request.get({ url: '/bpm/task/return-list', params })
|
||||||
}
|
}
|
||||||
|
|
||||||
// 回退
|
// 回退
|
||||||
|
|||||||
@@ -0,0 +1,72 @@
|
|||||||
|
import request from '@/config/axios'
|
||||||
|
|
||||||
|
export interface BusinessVO {
|
||||||
|
id: number
|
||||||
|
name: string
|
||||||
|
statusTypeId: number
|
||||||
|
statusId: number
|
||||||
|
contactNextTime: Date
|
||||||
|
customerId: number
|
||||||
|
dealTime: Date
|
||||||
|
price: number
|
||||||
|
discountPercent: number
|
||||||
|
productPrice: number
|
||||||
|
remark: string
|
||||||
|
ownerUserId: number
|
||||||
|
roUserIds: string
|
||||||
|
rwUserIds: string
|
||||||
|
endStatus: number
|
||||||
|
endRemark: string
|
||||||
|
contactLastTime: Date
|
||||||
|
followUpStatus: number
|
||||||
|
}
|
||||||
|
|
||||||
|
// 查询 CRM 商机列表
|
||||||
|
export const getBusinessPage = async (params) => {
|
||||||
|
return await request.get({ url: `/crm/business/page`, params })
|
||||||
|
}
|
||||||
|
|
||||||
|
// 查询 CRM 商机列表,基于指定客户
|
||||||
|
export const getBusinessPageByCustomer = async (params) => {
|
||||||
|
return await request.get({ url: `/crm/business/page-by-customer`, params })
|
||||||
|
}
|
||||||
|
|
||||||
|
// 查询 CRM 商机详情
|
||||||
|
export const getBusiness = async (id: number) => {
|
||||||
|
return await request.get({ url: `/crm/business/get?id=` + id })
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获得 CRM 商机列表(精简)
|
||||||
|
export const getSimpleBusinessList = async () => {
|
||||||
|
return await request.get({ url: `/crm/business/simple-all-list` })
|
||||||
|
}
|
||||||
|
|
||||||
|
// 新增 CRM 商机
|
||||||
|
export const createBusiness = async (data: BusinessVO) => {
|
||||||
|
return await request.post({ url: `/crm/business/create`, data })
|
||||||
|
}
|
||||||
|
|
||||||
|
// 修改 CRM 商机
|
||||||
|
export const updateBusiness = async (data: BusinessVO) => {
|
||||||
|
return await request.put({ url: `/crm/business/update`, data })
|
||||||
|
}
|
||||||
|
|
||||||
|
// 删除 CRM 商机
|
||||||
|
export const deleteBusiness = async (id: number) => {
|
||||||
|
return await request.delete({ url: `/crm/business/delete?id=` + id })
|
||||||
|
}
|
||||||
|
|
||||||
|
// 导出 CRM 商机 Excel
|
||||||
|
export const exportBusiness = async (params) => {
|
||||||
|
return await request.download({ url: `/crm/business/export-excel`, params })
|
||||||
|
}
|
||||||
|
|
||||||
|
// 联系人关联商机列表
|
||||||
|
export const getBusinessPageByContact = async (params) => {
|
||||||
|
return await request.get({ url: `/crm/business/page-by-contact`, params })
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获得 CRM 商机列表
|
||||||
|
export const getBusinessListByIds = async (val: number[]) => {
|
||||||
|
return await request.get({ url: '/crm/business/list-by-ids', params: { ids: val.join(',') } })
|
||||||
|
}
|
||||||
@@ -0,0 +1,48 @@
|
|||||||
|
import request from '@/config/axios'
|
||||||
|
|
||||||
|
export interface BusinessStatusTypeVO {
|
||||||
|
id: number
|
||||||
|
name: string
|
||||||
|
deptIds: number[]
|
||||||
|
status: boolean
|
||||||
|
}
|
||||||
|
|
||||||
|
// 查询商机状态类型列表
|
||||||
|
export const getBusinessStatusTypePage = async (params) => {
|
||||||
|
return await request.get({ url: `/crm/business-status-type/page`, params })
|
||||||
|
}
|
||||||
|
|
||||||
|
// 查询商机状态类型详情
|
||||||
|
export const getBusinessStatusType = async (id: number) => {
|
||||||
|
return await request.get({ url: `/crm/business-status-type/get?id=` + id })
|
||||||
|
}
|
||||||
|
|
||||||
|
// 新增商机状态类型
|
||||||
|
export const createBusinessStatusType = async (data: BusinessStatusTypeVO) => {
|
||||||
|
return await request.post({ url: `/crm/business-status-type/create`, data })
|
||||||
|
}
|
||||||
|
|
||||||
|
// 修改商机状态类型
|
||||||
|
export const updateBusinessStatusType = async (data: BusinessStatusTypeVO) => {
|
||||||
|
return await request.put({ url: `/crm/business-status-type/update`, data })
|
||||||
|
}
|
||||||
|
|
||||||
|
// 删除商机状态类型
|
||||||
|
export const deleteBusinessStatusType = async (id: number) => {
|
||||||
|
return await request.delete({ url: `/crm/business-status-type/delete?id=` + id })
|
||||||
|
}
|
||||||
|
|
||||||
|
// 导出商机状态类型 Excel
|
||||||
|
export const exportBusinessStatusType = async (params) => {
|
||||||
|
return await request.download({ url: `/crm/business-status-type/export-excel`, params })
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取商机状态类型信息列表
|
||||||
|
export const getBusinessStatusTypeList = async () => {
|
||||||
|
return await request.get({ url: `/crm/business-status-type/get-simple-list` })
|
||||||
|
}
|
||||||
|
|
||||||
|
// 根据类型ID获取商机状态信息列表
|
||||||
|
export const getBusinessStatusListByTypeId = async (typeId: number) => {
|
||||||
|
return await request.get({ url: `/crm/business-status-type/get-status-list?typeId=` + typeId })
|
||||||
|
}
|
||||||
@@ -1,10 +1,3 @@
|
|||||||
/*
|
|
||||||
* @Author: zyna
|
|
||||||
* @Date: 2023-11-05 13:34:41
|
|
||||||
* @LastEditTime: 2023-11-26 20:47:04
|
|
||||||
* @FilePath: \yudao-ui-admin-vue3\src\api\crm\contact\index.ts
|
|
||||||
* @Description:
|
|
||||||
*/
|
|
||||||
import request from '@/config/axios'
|
import request from '@/config/axios'
|
||||||
|
|
||||||
export interface ContactVO {
|
export interface ContactVO {
|
||||||
@@ -15,7 +8,7 @@ export interface ContactVO {
|
|||||||
email: string
|
email: string
|
||||||
post: string
|
post: string
|
||||||
customerId: number
|
customerId: number
|
||||||
address: string
|
detailAddress: string
|
||||||
remark: string
|
remark: string
|
||||||
ownerUserId: string
|
ownerUserId: string
|
||||||
lastTime: Date
|
lastTime: Date
|
||||||
@@ -28,40 +21,72 @@ export interface ContactVO {
|
|||||||
creatorName: string
|
creatorName: string
|
||||||
updateTime?: Date
|
updateTime?: Date
|
||||||
createTime?: Date
|
createTime?: Date
|
||||||
customerName: string,
|
customerName: string
|
||||||
areaName: string,
|
areaName: string
|
||||||
ownerUserName: string
|
ownerUserName: string
|
||||||
}
|
}
|
||||||
|
|
||||||
// 查询crm联系人列表
|
export interface ContactBusinessReqVO {
|
||||||
|
contactId: number
|
||||||
|
businessIds: number[]
|
||||||
|
}
|
||||||
|
|
||||||
|
// 查询 CRM 联系人列表
|
||||||
export const getContactPage = async (params) => {
|
export const getContactPage = async (params) => {
|
||||||
return await request.get({ url: `/crm/contact/page`, params })
|
return await request.get({ url: `/crm/contact/page`, params })
|
||||||
}
|
}
|
||||||
|
|
||||||
// 查询crm联系人详情
|
// 查询 CRM 联系人列表,基于指定客户
|
||||||
|
export const getContactPageByCustomer = async (params: any) => {
|
||||||
|
return await request.get({ url: `/crm/contact/page-by-customer`, params })
|
||||||
|
}
|
||||||
|
|
||||||
|
// 查询 CRM 联系人详情
|
||||||
export const getContact = async (id: number) => {
|
export const getContact = async (id: number) => {
|
||||||
return await request.get({ url: `/crm/contact/get?id=` + id })
|
return await request.get({ url: `/crm/contact/get?id=` + id })
|
||||||
}
|
}
|
||||||
|
|
||||||
// 新增crm联系人
|
// 新增 CRM 联系人
|
||||||
export const createContact = async (data: ContactVO) => {
|
export const createContact = async (data: ContactVO) => {
|
||||||
return await request.post({ url: `/crm/contact/create`, data })
|
return await request.post({ url: `/crm/contact/create`, data })
|
||||||
}
|
}
|
||||||
|
|
||||||
// 修改crm联系人
|
// 修改 CRM 联系人
|
||||||
export const updateContact = async (data: ContactVO) => {
|
export const updateContact = async (data: ContactVO) => {
|
||||||
return await request.put({ url: `/crm/contact/update`, data })
|
return await request.put({ url: `/crm/contact/update`, data })
|
||||||
}
|
}
|
||||||
|
|
||||||
// 删除crm联系人
|
// 删除 CRM 联系人
|
||||||
export const deleteContact = async (id: number) => {
|
export const deleteContact = async (id: number) => {
|
||||||
return await request.delete({ url: `/crm/contact/delete?id=` + id })
|
return await request.delete({ url: `/crm/contact/delete?id=` + id })
|
||||||
}
|
}
|
||||||
|
|
||||||
// 导出crm联系人 Excel
|
// 导出 CRM 联系人 Excel
|
||||||
export const exportContact = async (params) => {
|
export const exportContact = async (params) => {
|
||||||
return await request.download({ url: `/crm/contact/export-excel`, params })
|
return await request.download({ url: `/crm/contact/export-excel`, params })
|
||||||
}
|
}
|
||||||
export const simpleAllList = async () => {
|
|
||||||
|
// 获得 CRM 联系人列表(精简)
|
||||||
|
export const getSimpleContactList = async () => {
|
||||||
return await request.get({ url: `/crm/contact/simple-all-list` })
|
return await request.get({ url: `/crm/contact/simple-all-list` })
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 获得 CRM 联系人列表
|
||||||
|
export const getContactListByIds = async (val: number[]) => {
|
||||||
|
return await request.get({ url: '/crm/contact/list-by-ids', params: { ids: val.join(',') } })
|
||||||
|
}
|
||||||
|
|
||||||
|
// 批量新增联系人商机关联
|
||||||
|
export const createContactBusinessList = async (data: ContactBusinessReqVO) => {
|
||||||
|
return await request.post({ url: `/crm/contact/create-business-list`, data })
|
||||||
|
}
|
||||||
|
|
||||||
|
// 解除联系人商机关联
|
||||||
|
export const deleteContactBusinessList = async (data: ContactBusinessReqVO) => {
|
||||||
|
return await request.delete({ url: `/crm/contact/delete-business-list`, data })
|
||||||
|
}
|
||||||
|
|
||||||
|
// 查询联系人操作日志
|
||||||
|
export const getOperateLogPage = async (params: any) => {
|
||||||
|
return await request.get({ url: '/crm/contact/operate-log-page', params })
|
||||||
|
}
|
||||||
@@ -22,32 +22,37 @@ export interface ContractVO {
|
|||||||
remark: string
|
remark: string
|
||||||
}
|
}
|
||||||
|
|
||||||
// 查询合同列表
|
// 查询 CRM 合同列表
|
||||||
export const getContractPage = async (params) => {
|
export const getContractPage = async (params) => {
|
||||||
return await request.get({ url: `/crm/contract/page`, params })
|
return await request.get({ url: `/crm/contract/page`, params })
|
||||||
}
|
}
|
||||||
|
|
||||||
// 查询合同详情
|
// 查询 CRM 联系人列表,基于指定客户
|
||||||
|
export const getContractPageByCustomer = async (params: any) => {
|
||||||
|
return await request.get({ url: `/crm/contract/page-by-customer`, params })
|
||||||
|
}
|
||||||
|
|
||||||
|
// 查询 CRM 合同详情
|
||||||
export const getContract = async (id: number) => {
|
export const getContract = async (id: number) => {
|
||||||
return await request.get({ url: `/crm/contract/get?id=` + id })
|
return await request.get({ url: `/crm/contract/get?id=` + id })
|
||||||
}
|
}
|
||||||
|
|
||||||
// 新增合同
|
// 新增 CRM 合同
|
||||||
export const createContract = async (data: ContractVO) => {
|
export const createContract = async (data: ContractVO) => {
|
||||||
return await request.post({ url: `/crm/contract/create`, data })
|
return await request.post({ url: `/crm/contract/create`, data })
|
||||||
}
|
}
|
||||||
|
|
||||||
// 修改合同
|
// 修改 CRM 合同
|
||||||
export const updateContract = async (data: ContractVO) => {
|
export const updateContract = async (data: ContractVO) => {
|
||||||
return await request.put({ url: `/crm/contract/update`, data })
|
return await request.put({ url: `/crm/contract/update`, data })
|
||||||
}
|
}
|
||||||
|
|
||||||
// 删除合同
|
// 删除 CRM 合同
|
||||||
export const deleteContract = async (id: number) => {
|
export const deleteContract = async (id: number) => {
|
||||||
return await request.delete({ url: `/crm/contract/delete?id=` + id })
|
return await request.delete({ url: `/crm/contract/delete?id=` + id })
|
||||||
}
|
}
|
||||||
|
|
||||||
// 导出合同 Excel
|
// 导出 CRM 合同 Excel
|
||||||
export const exportContract = async (params) => {
|
export const exportContract = async (params) => {
|
||||||
return await request.download({ url: `/crm/contract/export-excel`, params })
|
return await request.download({ url: `/crm/contract/export-excel`, params })
|
||||||
}
|
}
|
||||||
@@ -59,10 +59,44 @@ export const deleteCustomer = async (id: number) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 导出客户 Excel
|
// 导出客户 Excel
|
||||||
export const exportCustomer = async (params) => {
|
export const exportCustomer = async (params: any) => {
|
||||||
return await request.download({ url: `/crm/customer/export-excel`, params })
|
return await request.download({ url: `/crm/customer/export-excel`, params })
|
||||||
}
|
}
|
||||||
//客户列表
|
|
||||||
export const queryAllList = async () => {
|
// 客户列表
|
||||||
return await request.get({ url: `/crm/customer/query-all-list` })
|
export const getSimpleCustomerList = async () => {
|
||||||
}
|
return await request.get({ url: `/crm/customer/list-all-simple` })
|
||||||
|
}
|
||||||
|
|
||||||
|
// 查询客户操作日志
|
||||||
|
export const getOperateLogPage = async (id: number) => {
|
||||||
|
return await request.get({ url: '/crm/customer/operate-log-page?id=' + id })
|
||||||
|
}
|
||||||
|
|
||||||
|
// ======================= 业务操作 =======================
|
||||||
|
|
||||||
|
export interface TransferReqVO {
|
||||||
|
id: number | undefined // 客户编号
|
||||||
|
newOwnerUserId: number | undefined // 新负责人的用户编号
|
||||||
|
oldOwnerPermissionLevel: number | undefined // 老负责人加入团队后的权限级别
|
||||||
|
}
|
||||||
|
|
||||||
|
// 客户转移
|
||||||
|
export const transfer = async (data: TransferReqVO) => {
|
||||||
|
return await request.put({ url: '/crm/customer/transfer', data })
|
||||||
|
}
|
||||||
|
|
||||||
|
// 锁定/解锁客户
|
||||||
|
export const lockCustomer = async (id: number, lockStatus: boolean) => {
|
||||||
|
return await request.put({ url: `/crm/customer/lock`, data: { id, lockStatus } })
|
||||||
|
}
|
||||||
|
|
||||||
|
// 领取公海客户
|
||||||
|
export const receiveCustomer = async (ids: any[]) => {
|
||||||
|
return await request.put({ url: '/crm/customer/receive', params: { ids: ids.join(',') } })
|
||||||
|
}
|
||||||
|
|
||||||
|
// 客户放入公海
|
||||||
|
export const putCustomerPool = async (id: number) => {
|
||||||
|
return await request.put({ url: `/crm/customer/put-pool?id=${id}` })
|
||||||
|
}
|
||||||
@@ -9,6 +9,20 @@ export interface CustomerLimitConfigVO {
|
|||||||
dealCountEnabled?: boolean
|
dealCountEnabled?: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 客户限制配置类型
|
||||||
|
*/
|
||||||
|
export enum LimitConfType {
|
||||||
|
/**
|
||||||
|
* 拥有客户数限制
|
||||||
|
*/
|
||||||
|
CUSTOMER_QUANTITY_LIMIT = 1,
|
||||||
|
/**
|
||||||
|
* 锁定客户数限制
|
||||||
|
*/
|
||||||
|
CUSTOMER_LOCK_LIMIT = 2
|
||||||
|
}
|
||||||
|
|
||||||
// 查询客户限制配置列表
|
// 查询客户限制配置列表
|
||||||
export const getCustomerLimitConfigPage = async (params) => {
|
export const getCustomerLimitConfigPage = async (params) => {
|
||||||
return await request.get({ url: `/crm/customer-limit-config/page`, params })
|
return await request.get({ url: `/crm/customer-limit-config/page`, params })
|
||||||
@@ -5,7 +5,7 @@ export interface CustomerPoolConfigVO {
|
|||||||
contactExpireDays?: number
|
contactExpireDays?: number
|
||||||
dealExpireDays?: number
|
dealExpireDays?: number
|
||||||
notifyEnabled?: boolean
|
notifyEnabled?: boolean
|
||||||
notifyDays: number
|
notifyDays?: number
|
||||||
}
|
}
|
||||||
|
|
||||||
// 获取客户公海规则设置
|
// 获取客户公海规则设置
|
||||||
@@ -14,6 +14,6 @@ export const getCustomerPoolConfig = async () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 更新客户公海规则设置
|
// 更新客户公海规则设置
|
||||||
export const updateCustomerPoolConfig = async (data: ConfigVO) => {
|
export const saveCustomerPoolConfig = async (data: CustomerPoolConfigVO) => {
|
||||||
return await request.put({ url: `/crm/customer-pool-config/update`, data })
|
return await request.put({ url: `/crm/customer-pool-config/save`, data })
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,46 @@
|
|||||||
|
import request from '@/config/axios'
|
||||||
|
|
||||||
|
// 跟进记录 VO
|
||||||
|
export interface FollowUpRecordVO {
|
||||||
|
id: number // 编号
|
||||||
|
bizType: number // 数据类型
|
||||||
|
bizId: number // 数据编号
|
||||||
|
type: number // 跟进类型
|
||||||
|
content: string // 跟进内容
|
||||||
|
nextTime: Date // 下次联系时间
|
||||||
|
businessIds: number[] // 关联的商机编号数组
|
||||||
|
contactIds: number[] // 关联的联系人编号数组
|
||||||
|
}
|
||||||
|
|
||||||
|
// 跟进记录 API
|
||||||
|
export const FollowUpRecordApi = {
|
||||||
|
// 查询跟进记录分页
|
||||||
|
getFollowUpRecordPage: async (params: any) => {
|
||||||
|
return await request.get({ url: `/crm/follow-up-record/page`, params })
|
||||||
|
},
|
||||||
|
|
||||||
|
// 查询跟进记录详情
|
||||||
|
getFollowUpRecord: async (id: number) => {
|
||||||
|
return await request.get({ url: `/crm/follow-up-record/get?id=` + id })
|
||||||
|
},
|
||||||
|
|
||||||
|
// 新增跟进记录
|
||||||
|
createFollowUpRecord: async (data: FollowUpRecordVO) => {
|
||||||
|
return await request.post({ url: `/crm/follow-up-record/create`, data })
|
||||||
|
},
|
||||||
|
|
||||||
|
// 修改跟进记录
|
||||||
|
updateFollowUpRecord: async (data: FollowUpRecordVO) => {
|
||||||
|
return await request.put({ url: `/crm/follow-up-record/update`, data })
|
||||||
|
},
|
||||||
|
|
||||||
|
// 删除跟进记录
|
||||||
|
deleteFollowUpRecord: async (id: number) => {
|
||||||
|
return await request.delete({ url: `/crm/follow-up-record/delete?id=` + id })
|
||||||
|
},
|
||||||
|
|
||||||
|
// 导出跟进记录 Excel
|
||||||
|
exportFollowUpRecord: async (params) => {
|
||||||
|
return await request.download({ url: `/crm/follow-up-record/export-excel`, params })
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,40 @@
|
|||||||
|
import request from '@/config/axios'
|
||||||
|
|
||||||
|
export interface CustomerVO {
|
||||||
|
id?: number
|
||||||
|
name: string
|
||||||
|
industryId: number
|
||||||
|
level: number
|
||||||
|
source: number
|
||||||
|
followUpStatus?: boolean
|
||||||
|
lockStatus?: boolean
|
||||||
|
dealStatus?: boolean
|
||||||
|
mobile: string
|
||||||
|
telephone: string
|
||||||
|
website: string
|
||||||
|
qq: string
|
||||||
|
wechat: string
|
||||||
|
email: string
|
||||||
|
description: string
|
||||||
|
remark: string
|
||||||
|
ownerUserId?: number
|
||||||
|
ownerUserName?: string
|
||||||
|
ownerUserDept?: string
|
||||||
|
roUserIds?: string
|
||||||
|
rwUserIds?: string
|
||||||
|
areaId?: number
|
||||||
|
areaName?: string
|
||||||
|
detailAddress: string
|
||||||
|
contactLastTime?: Date
|
||||||
|
contactNextTime: Date
|
||||||
|
createTime?: Date
|
||||||
|
updateTime?: Date
|
||||||
|
creator?: string
|
||||||
|
creatorName?: string
|
||||||
|
}
|
||||||
|
|
||||||
|
// 查询客户列表
|
||||||
|
// TODO @芋艿:看看是不是后续融合到 getCustomerPage 里;
|
||||||
|
export const getTodayCustomerPage = async (params) => {
|
||||||
|
return await request.get({ url: `/crm/message/todayCustomer`, params })
|
||||||
|
}
|
||||||
@@ -12,37 +12,49 @@ export interface PermissionVO {
|
|||||||
createTime?: Date
|
createTime?: Date
|
||||||
}
|
}
|
||||||
|
|
||||||
// 查询团队成员列表
|
/**
|
||||||
|
* CRM 业务类型枚举
|
||||||
|
*
|
||||||
|
* @author HUIHUI
|
||||||
|
*/
|
||||||
|
export enum BizTypeEnum {
|
||||||
|
CRM_LEADS = 1, // 线索
|
||||||
|
CRM_CUSTOMER = 2, // 客户
|
||||||
|
CRM_CONTACT = 3, // 联系人
|
||||||
|
CRM_BUSINESS = 5, // 商机
|
||||||
|
CRM_CONTRACT = 6 // 合同
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* CRM 数据权限级别枚举
|
||||||
|
*/
|
||||||
|
export enum PermissionLevelEnum {
|
||||||
|
OWNER = 1, // 负责人
|
||||||
|
READ = 2, // 只读
|
||||||
|
WRITE = 3 // 读写
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获得数据权限列表(查询团队成员列表)
|
||||||
export const getPermissionList = async (params) => {
|
export const getPermissionList = async (params) => {
|
||||||
return await request.get({ url: `/crm/permission/list`, params })
|
return await request.get({ url: `/crm/permission/list`, params })
|
||||||
}
|
}
|
||||||
|
|
||||||
// 新增团队成员
|
// 创建数据权限(新增团队成员)
|
||||||
export const createPermission = async (data: PermissionVO) => {
|
export const createPermission = async (data: PermissionVO) => {
|
||||||
return await request.post({ url: `/crm/permission/create`, data })
|
return await request.post({ url: `/crm/permission/create`, data })
|
||||||
}
|
}
|
||||||
|
|
||||||
// 修改团队成员权限级别
|
// 编辑数据权限(修改团队成员权限级别)
|
||||||
export const updatePermission = async (data) => {
|
export const updatePermission = async (data) => {
|
||||||
return await request.put({ url: `/crm/permission/update`, data })
|
return await request.put({ url: `/crm/permission/update`, data })
|
||||||
}
|
}
|
||||||
|
|
||||||
// 删除团队成员
|
// 删除数据权限(删除团队成员)
|
||||||
export const deletePermission = async (params) => {
|
export const deletePermissionBatch = async (params) => {
|
||||||
return await request.delete({ url: '/crm/permission/delete', params })
|
return await request.delete({ url: '/crm/permission/delete', params })
|
||||||
}
|
}
|
||||||
|
|
||||||
// 退出团队
|
// 删除自己的数据权限(退出团队)
|
||||||
export const quitTeam = async (id) => {
|
export const deleteSelfPermission = async (id) => {
|
||||||
return await request.delete({ url: '/crm/permission/quit-team?id=' + id })
|
return await request.delete({ url: '/crm/permission/quit-team?id=' + id })
|
||||||
}
|
}
|
||||||
|
|
||||||
// 领取公海数据
|
|
||||||
export const receive = async (data: { bizType: number; bizId: number }) => {
|
|
||||||
return await request.put({ url: `/crm/permission/receive`, data })
|
|
||||||
}
|
|
||||||
|
|
||||||
// 数据放入公海
|
|
||||||
export const putPool = async (data: { bizType: number; bizId: number }) => {
|
|
||||||
return await request.put({ url: `/crm/permission/put-pool`, data })
|
|
||||||
}
|
|
||||||
@@ -4,7 +4,7 @@ export interface ProductVO {
|
|||||||
id: number
|
id: number
|
||||||
name: string
|
name: string
|
||||||
no: string
|
no: string
|
||||||
unit: string
|
unit: number
|
||||||
price: number
|
price: number
|
||||||
status: number
|
status: number
|
||||||
categoryId: number
|
categoryId: number
|
||||||
@@ -41,3 +41,8 @@ export const deleteProduct = async (id: number) => {
|
|||||||
export const exportProduct = async (params) => {
|
export const exportProduct = async (params) => {
|
||||||
return await request.download({ url: `/crm/product/export-excel`, params })
|
return await request.download({ url: `/crm/product/export-excel`, params })
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 查询产品操作日志
|
||||||
|
export const getOperateLogPage = async (params: any) => {
|
||||||
|
return await request.get({ url: '/crm/product/operate-log-page', params })
|
||||||
|
}
|
||||||
File renamed without changes.
@@ -6,46 +6,47 @@ export interface ReceivableVO {
|
|||||||
planId: number
|
planId: number
|
||||||
customerId: number
|
customerId: number
|
||||||
contractId: number
|
contractId: number
|
||||||
checkStatus: number
|
auditStatus: number
|
||||||
processInstanceId: number
|
processInstanceId: number
|
||||||
returnTime: Date
|
returnTime: Date
|
||||||
returnType: string
|
returnType: string
|
||||||
price: number
|
price: number
|
||||||
ownerUserId: number
|
ownerUserId: number
|
||||||
batchId: number
|
|
||||||
sort: number
|
sort: number
|
||||||
dataScope: number
|
|
||||||
dataScopeDeptIds: string
|
|
||||||
status: number
|
|
||||||
remark: string
|
remark: string
|
||||||
}
|
}
|
||||||
|
|
||||||
// 查询回款管理列表
|
// 查询回款列表
|
||||||
export const getReceivablePage = async (params) => {
|
export const getReceivablePage = async (params) => {
|
||||||
return await request.get({ url: `/crm/receivable/page`, params })
|
return await request.get({ url: `/crm/receivable/page`, params })
|
||||||
}
|
}
|
||||||
|
|
||||||
// 查询回款管理详情
|
// 查询回款列表
|
||||||
|
export const getReceivablePageByCustomer = async (params) => {
|
||||||
|
return await request.get({ url: `/crm/receivable/page-by-customer`, params })
|
||||||
|
}
|
||||||
|
|
||||||
|
// 查询回款详情
|
||||||
export const getReceivable = async (id: number) => {
|
export const getReceivable = async (id: number) => {
|
||||||
return await request.get({ url: `/crm/receivable/get?id=` + id })
|
return await request.get({ url: `/crm/receivable/get?id=` + id })
|
||||||
}
|
}
|
||||||
|
|
||||||
// 新增回款管理
|
// 新增回款
|
||||||
export const createReceivable = async (data: ReceivableVO) => {
|
export const createReceivable = async (data: ReceivableVO) => {
|
||||||
return await request.post({ url: `/crm/receivable/create`, data })
|
return await request.post({ url: `/crm/receivable/create`, data })
|
||||||
}
|
}
|
||||||
|
|
||||||
// 修改回款管理
|
// 修改回款
|
||||||
export const updateReceivable = async (data: ReceivableVO) => {
|
export const updateReceivable = async (data: ReceivableVO) => {
|
||||||
return await request.put({ url: `/crm/receivable/update`, data })
|
return await request.put({ url: `/crm/receivable/update`, data })
|
||||||
}
|
}
|
||||||
|
|
||||||
// 删除回款管理
|
// 删除回款
|
||||||
export const deleteReceivable = async (id: number) => {
|
export const deleteReceivable = async (id: number) => {
|
||||||
return await request.delete({ url: `/crm/receivable/delete?id=` + id })
|
return await request.delete({ url: `/crm/receivable/delete?id=` + id })
|
||||||
}
|
}
|
||||||
|
|
||||||
// 导出回款管理 Excel
|
// 导出回款 Excel
|
||||||
export const exportReceivable = async (params) => {
|
export const exportReceivable = async (params) => {
|
||||||
return await request.download({ url: `/crm/receivable/export-excel`, params })
|
return await request.download({ url: `/crm/receivable/export-excel`, params })
|
||||||
}
|
}
|
||||||
@@ -23,6 +23,11 @@ export const getReceivablePlanPage = async (params) => {
|
|||||||
return await request.get({ url: `/crm/receivable-plan/page`, params })
|
return await request.get({ url: `/crm/receivable-plan/page`, params })
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 查询回款计划列表
|
||||||
|
export const getReceivablePlanPageByCustomer = async (params) => {
|
||||||
|
return await request.get({ url: `/crm/receivable-plan/page-by-customer`, params })
|
||||||
|
}
|
||||||
|
|
||||||
// 查询回款计划详情
|
// 查询回款计划详情
|
||||||
export const getReceivablePlan = async (id: number) => {
|
export const getReceivablePlan = async (id: number) => {
|
||||||
return await request.get({ url: `/crm/receivable-plan/get?id=` + id })
|
return await request.get({ url: `/crm/receivable-plan/get?id=` + id })
|
||||||
@@ -28,7 +28,7 @@ export type CodegenColumnVO = {
|
|||||||
columnComment: string
|
columnComment: string
|
||||||
nullable: number
|
nullable: number
|
||||||
primaryKey: number
|
primaryKey: number
|
||||||
autoIncrement: string
|
autoIncrement: boolean
|
||||||
ordinalPosition: number
|
ordinalPosition: number
|
||||||
javaType: string
|
javaType: string
|
||||||
javaField: string
|
javaField: string
|
||||||
|
|||||||
@@ -37,4 +37,4 @@ export const deleteDemo01Contact = async (id: number) => {
|
|||||||
// 导出示例联系人 Excel
|
// 导出示例联系人 Excel
|
||||||
export const exportDemo01Contact = async (params) => {
|
export const exportDemo01Contact = async (params) => {
|
||||||
return await request.download({ url: `/infra/demo01-contact/export-excel`, params })
|
return await request.download({ url: `/infra/demo01-contact/export-excel`, params })
|
||||||
}
|
}
|
||||||
@@ -34,4 +34,4 @@ export const deleteDemo02Category = async (id: number) => {
|
|||||||
// 导出示例分类 Excel
|
// 导出示例分类 Excel
|
||||||
export const exportDemo02Category = async (params) => {
|
export const exportDemo02Category = async (params) => {
|
||||||
return await request.download({ url: `/infra/demo02-category/export-excel`, params })
|
return await request.download({ url: `/infra/demo02-category/export-excel`, params })
|
||||||
}
|
}
|
||||||
@@ -88,4 +88,4 @@ export const deleteDemo03Grade = async (id: number) => {
|
|||||||
// 获得学生班级
|
// 获得学生班级
|
||||||
export const getDemo03Grade = async (id: number) => {
|
export const getDemo03Grade = async (id: number) => {
|
||||||
return await request.get({ url: `/infra/demo03-student/demo03-grade/get?id=` + id })
|
return await request.get({ url: `/infra/demo03-student/demo03-grade/get?id=` + id })
|
||||||
}
|
}
|
||||||
@@ -42,12 +42,16 @@ export const exportDemo03Student = async (params) => {
|
|||||||
|
|
||||||
// 获得学生课程列表
|
// 获得学生课程列表
|
||||||
export const getDemo03CourseListByStudentId = async (studentId) => {
|
export const getDemo03CourseListByStudentId = async (studentId) => {
|
||||||
return await request.get({ url: `/infra/demo03-student/demo03-course/list-by-student-id?studentId=` + studentId })
|
return await request.get({
|
||||||
|
url: `/infra/demo03-student/demo03-course/list-by-student-id?studentId=` + studentId
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// ==================== 子表(学生班级) ====================
|
// ==================== 子表(学生班级) ====================
|
||||||
|
|
||||||
// 获得学生班级
|
// 获得学生班级
|
||||||
export const getDemo03GradeByStudentId = async (studentId) => {
|
export const getDemo03GradeByStudentId = async (studentId) => {
|
||||||
return await request.get({ url: `/infra/demo03-student/demo03-grade/get-by-student-id?studentId=` + studentId })
|
return await request.get({
|
||||||
}
|
url: `/infra/demo03-student/demo03-grade/get-by-student-id?studentId=` + studentId
|
||||||
|
})
|
||||||
|
}
|
||||||
@@ -42,12 +42,16 @@ export const exportDemo03Student = async (params) => {
|
|||||||
|
|
||||||
// 获得学生课程列表
|
// 获得学生课程列表
|
||||||
export const getDemo03CourseListByStudentId = async (studentId) => {
|
export const getDemo03CourseListByStudentId = async (studentId) => {
|
||||||
return await request.get({ url: `/infra/demo03-student/demo03-course/list-by-student-id?studentId=` + studentId })
|
return await request.get({
|
||||||
|
url: `/infra/demo03-student/demo03-course/list-by-student-id?studentId=` + studentId
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// ==================== 子表(学生班级) ====================
|
// ==================== 子表(学生班级) ====================
|
||||||
|
|
||||||
// 获得学生班级
|
// 获得学生班级
|
||||||
export const getDemo03GradeByStudentId = async (studentId) => {
|
export const getDemo03GradeByStudentId = async (studentId) => {
|
||||||
return await request.get({ url: `/infra/demo03-student/demo03-grade/get-by-student-id?studentId=` + studentId })
|
return await request.get({
|
||||||
}
|
url: `/infra/demo03-student/demo03-grade/get-by-student-id?studentId=` + studentId
|
||||||
|
})
|
||||||
|
}
|
||||||
@@ -17,7 +17,7 @@ export interface FileClientConfig {
|
|||||||
export interface FileConfigVO {
|
export interface FileConfigVO {
|
||||||
id: number
|
id: number
|
||||||
name: string
|
name: string
|
||||||
storage: any
|
storage?: number
|
||||||
master: boolean
|
master: boolean
|
||||||
visible: boolean
|
visible: boolean
|
||||||
config: FileClientConfig
|
config: FileClientConfig
|
||||||
|
|||||||
@@ -7,8 +7,8 @@ export interface JobLogVO {
|
|||||||
handlerParam: string
|
handlerParam: string
|
||||||
cronExpression: string
|
cronExpression: string
|
||||||
executeIndex: string
|
executeIndex: string
|
||||||
beginTime: string
|
beginTime: Date
|
||||||
endTime: string
|
endTime: Date
|
||||||
duration: string
|
duration: string
|
||||||
status: number
|
status: number
|
||||||
createTime: string
|
createTime: string
|
||||||
|
|||||||
@@ -20,10 +20,6 @@ export interface CategoryVO {
|
|||||||
* 移动端分类图
|
* 移动端分类图
|
||||||
*/
|
*/
|
||||||
picUrl: string
|
picUrl: string
|
||||||
/**
|
|
||||||
* PC 端分类图
|
|
||||||
*/
|
|
||||||
bigPicUrl?: string
|
|
||||||
/**
|
/**
|
||||||
* 分类排序
|
* 分类排序
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -65,16 +65,6 @@ export const getPropertyPage = (params: PageParam) => {
|
|||||||
return request.get({ url: '/product/property/page', params })
|
return request.get({ url: '/product/property/page', params })
|
||||||
}
|
}
|
||||||
|
|
||||||
// 获得属性项列表
|
|
||||||
export const getPropertyList = (params: any) => {
|
|
||||||
return request.get({ url: '/product/property/list', params })
|
|
||||||
}
|
|
||||||
|
|
||||||
// 获得属性项列表
|
|
||||||
export const getPropertyListAndValue = (data: any) => {
|
|
||||||
return request.post({ url: '/product/property/get-value-list', data })
|
|
||||||
}
|
|
||||||
|
|
||||||
// ------------------------ 属性值 -------------------
|
// ------------------------ 属性值 -------------------
|
||||||
|
|
||||||
// 获得属性值分页
|
// 获得属性值分页
|
||||||
|
|||||||
@@ -33,14 +33,15 @@ export interface GiveCouponTemplate {
|
|||||||
export interface Spu {
|
export interface Spu {
|
||||||
id?: number
|
id?: number
|
||||||
name?: string // 商品名称
|
name?: string // 商品名称
|
||||||
categoryId?: number | undefined // 商品分类
|
categoryId?: number // 商品分类
|
||||||
keyword?: string // 关键字
|
keyword?: string // 关键字
|
||||||
unit?: number | undefined // 单位
|
unit?: number | undefined // 单位
|
||||||
picUrl?: string // 商品封面图
|
picUrl?: string // 商品封面图
|
||||||
sliderPicUrls?: string[] // 商品轮播图
|
sliderPicUrls?: string[] // 商品轮播图
|
||||||
introduction?: string // 商品简介
|
introduction?: string // 商品简介
|
||||||
|
deliveryTypes?: number[] // 配送方式
|
||||||
deliveryTemplateId?: number | undefined // 运费模版
|
deliveryTemplateId?: number | undefined // 运费模版
|
||||||
brandId?: number | undefined // 商品品牌编号
|
brandId?: number // 商品品牌编号
|
||||||
specType?: boolean // 商品规格
|
specType?: boolean // 商品规格
|
||||||
subCommissionType?: boolean // 分销类型
|
subCommissionType?: boolean // 分销类型
|
||||||
skus?: Sku[] // sku数组
|
skus?: Sku[] // sku数组
|
||||||
@@ -48,11 +49,6 @@ export interface Spu {
|
|||||||
sort?: number // 商品排序
|
sort?: number // 商品排序
|
||||||
giveIntegral?: number // 赠送积分
|
giveIntegral?: number // 赠送积分
|
||||||
virtualSalesCount?: number // 虚拟销量
|
virtualSalesCount?: number // 虚拟销量
|
||||||
recommendHot?: boolean // 是否热卖
|
|
||||||
recommendBenefit?: boolean // 是否优惠
|
|
||||||
recommendBest?: boolean // 是否精品
|
|
||||||
recommendNew?: boolean // 是否新品
|
|
||||||
recommendGood?: boolean // 是否优品
|
|
||||||
price?: number // 商品价格
|
price?: number // 商品价格
|
||||||
salesCount?: number // 商品销量
|
salesCount?: number // 商品销量
|
||||||
marketPrice?: number // 市场价
|
marketPrice?: number // 市场价
|
||||||
@@ -60,7 +56,6 @@ export interface Spu {
|
|||||||
stock?: number // 商品库存
|
stock?: number // 商品库存
|
||||||
createTime?: Date // 商品创建时间
|
createTime?: Date // 商品创建时间
|
||||||
status?: number // 商品状态
|
status?: number // 商品状态
|
||||||
activityOrders: number[] // 活动排序
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 获得 Spu 列表
|
// 获得 Spu 列表
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ export interface ArticleVO {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 查询文章管理列表
|
// 查询文章管理列表
|
||||||
export const getArticlePage = async (params) => {
|
export const getArticlePage = async (params: any) => {
|
||||||
return await request.get({ url: `/promotion/article/page`, params })
|
return await request.get({ url: `/promotion/article/page`, params })
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -18,15 +18,10 @@ export interface CombinationRecordVO {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 查询拼团记录列表
|
// 查询拼团记录列表
|
||||||
export const getCombinationRecordPage = async (params) => {
|
export const getCombinationRecordPage = async (params: any) => {
|
||||||
return await request.get({ url: '/promotion/combination-record/page', params })
|
return await request.get({ url: '/promotion/combination-record/page', params })
|
||||||
}
|
}
|
||||||
|
|
||||||
// 查询一个拼团的完整拼团记录
|
|
||||||
export const getCombinationRecordPageByHeadId = async (params) => {
|
|
||||||
return await request.get({ url: '/promotion/combination-record/page-by-headId', params })
|
|
||||||
}
|
|
||||||
|
|
||||||
// 获得拼团记录的概要信息
|
// 获得拼团记录的概要信息
|
||||||
export const getCombinationRecordSummary = async () => {
|
export const getCombinationRecordSummary = async () => {
|
||||||
return await request.get({ url: '/promotion/combination-record/get-summary' })
|
return await request.get({ url: '/promotion/combination-record/get-summary' })
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ export interface DiyPageVO {
|
|||||||
templateId?: number
|
templateId?: number
|
||||||
name: string
|
name: string
|
||||||
remark: string
|
remark: string
|
||||||
previewImageUrls: string[]
|
previewPicUrls: string[]
|
||||||
property: string
|
property: string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ export interface DiyTemplateVO {
|
|||||||
used: boolean
|
used: boolean
|
||||||
usedTime?: Date
|
usedTime?: Date
|
||||||
remark: string
|
remark: string
|
||||||
previewImageUrls: string[]
|
previewPicUrls: string[]
|
||||||
property: string
|
property: string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,48 @@
|
|||||||
|
import request from '@/config/axios'
|
||||||
|
|
||||||
|
export interface DiscountActivityVO {
|
||||||
|
id?: number
|
||||||
|
name?: string
|
||||||
|
startTime?: Date
|
||||||
|
endTime?: Date
|
||||||
|
remark?: string
|
||||||
|
conditionType?: number
|
||||||
|
productScope?: number
|
||||||
|
productSpuIds?: number[]
|
||||||
|
rules?: DiscountProductVO[]
|
||||||
|
}
|
||||||
|
|
||||||
|
// 优惠规则
|
||||||
|
export interface DiscountProductVO {
|
||||||
|
limit: number
|
||||||
|
discountPrice: number
|
||||||
|
freeDelivery: boolean
|
||||||
|
point: number
|
||||||
|
couponIds: number[]
|
||||||
|
couponCounts: number[]
|
||||||
|
}
|
||||||
|
|
||||||
|
// 新增满减送活动
|
||||||
|
export const createRewardActivity = async (data: DiscountActivityVO) => {
|
||||||
|
return await request.post({ url: '/promotion/reward-activity/create', data })
|
||||||
|
}
|
||||||
|
|
||||||
|
// 更新满减送活动
|
||||||
|
export const updateRewardActivity = async (data: DiscountActivityVO) => {
|
||||||
|
return await request.put({ url: '/promotion/reward-activity/update', data })
|
||||||
|
}
|
||||||
|
|
||||||
|
// 查询满减送活动列表
|
||||||
|
export const getRewardActivityPage = async (params) => {
|
||||||
|
return await request.get({ url: '/promotion/reward-activity/page', params })
|
||||||
|
}
|
||||||
|
|
||||||
|
// 查询满减送活动详情
|
||||||
|
export const getReward = async (id: number) => {
|
||||||
|
return await request.get({ url: '/promotion/reward-activity/get?id=' + id })
|
||||||
|
}
|
||||||
|
|
||||||
|
// 删除限时折扣活动
|
||||||
|
export const deleteRewardActivity = async (id: number) => {
|
||||||
|
return await request.delete({ url: '/promotion/reward-activity/delete?id=' + id })
|
||||||
|
}
|
||||||
@@ -0,0 +1,52 @@
|
|||||||
|
import request from '@/config/axios'
|
||||||
|
import { DataComparisonRespVO } from '@/api/mall/statistics/common'
|
||||||
|
|
||||||
|
export interface ProductStatisticsVO {
|
||||||
|
id: number
|
||||||
|
day: string
|
||||||
|
spuId: number
|
||||||
|
spuName: string
|
||||||
|
spuPicUrl: string
|
||||||
|
browseCount: number
|
||||||
|
browseUserCount: number
|
||||||
|
favoriteCount: number
|
||||||
|
cartCount: number
|
||||||
|
orderCount: number
|
||||||
|
orderPayCount: number
|
||||||
|
orderPayPrice: number
|
||||||
|
afterSaleCount: number
|
||||||
|
afterSaleRefundPrice: number
|
||||||
|
browseConvertPercent: number
|
||||||
|
}
|
||||||
|
|
||||||
|
// 商品统计 API
|
||||||
|
export const ProductStatisticsApi = {
|
||||||
|
// 获得商品统计分析
|
||||||
|
getProductStatisticsAnalyse: (params: any) => {
|
||||||
|
return request.get<DataComparisonRespVO<ProductStatisticsVO>>({
|
||||||
|
url: '/statistics/product/analyse',
|
||||||
|
params
|
||||||
|
})
|
||||||
|
},
|
||||||
|
// 获得商品状况明细
|
||||||
|
getProductStatisticsList: (params: any) => {
|
||||||
|
return request.get<ProductStatisticsVO[]>({
|
||||||
|
url: '/statistics/product/list',
|
||||||
|
params
|
||||||
|
})
|
||||||
|
},
|
||||||
|
// 导出获得商品状况明细 Excel
|
||||||
|
exportProductStatisticsExcel: (params: any) => {
|
||||||
|
return request.download({
|
||||||
|
url: '/statistics/product/export-excel',
|
||||||
|
params
|
||||||
|
})
|
||||||
|
},
|
||||||
|
// 获得商品排行榜分页
|
||||||
|
getProductStatisticsRankPage: async (params: any) => {
|
||||||
|
return await request.get({
|
||||||
|
url: `/statistics/product/rank-page`,
|
||||||
|
params
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -66,9 +66,9 @@ export const getTradeStatisticsSummary = () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 获得交易状况统计
|
// 获得交易状况统计
|
||||||
export const getTradeTrendSummary = (params: TradeTrendReqVO) => {
|
export const getTradeStatisticsAnalyse = (params: TradeTrendReqVO) => {
|
||||||
return request.get<DataComparisonRespVO<TradeTrendSummaryRespVO>>({
|
return request.get<DataComparisonRespVO<TradeTrendSummaryRespVO>>({
|
||||||
url: '/statistics/trade/trend/summary',
|
url: '/statistics/trade/analyse',
|
||||||
params: formatDateParam(params)
|
params: formatDateParam(params)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,7 +8,6 @@ export interface ConfigVO {
|
|||||||
brokerageFirstPercent: number
|
brokerageFirstPercent: number
|
||||||
brokerageSecondPercent: number
|
brokerageSecondPercent: number
|
||||||
brokerageWithdrawMinPrice: number
|
brokerageWithdrawMinPrice: number
|
||||||
brokerageBankNames: string
|
|
||||||
brokerageFrozenDays: number
|
brokerageFrozenDays: number
|
||||||
brokerageWithdrawTypes: string
|
brokerageWithdrawTypes: string
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,39 @@
|
|||||||
|
import request from '@/config/axios'
|
||||||
|
|
||||||
|
export interface UReportDataVO {
|
||||||
|
id: number
|
||||||
|
name: string
|
||||||
|
status: number
|
||||||
|
content: string
|
||||||
|
remark: string
|
||||||
|
}
|
||||||
|
|
||||||
|
// 查询Ureport2报表分页
|
||||||
|
export const getUReportDataPage = async (params) => {
|
||||||
|
return await request.get({ url: `/report/ureport-data/page`, params })
|
||||||
|
}
|
||||||
|
|
||||||
|
// 查询Ureport2报表详情
|
||||||
|
export const getUReportData = async (id: number) => {
|
||||||
|
return await request.get({ url: `/report/ureport-data/get?id=` + id })
|
||||||
|
}
|
||||||
|
|
||||||
|
// 新增Ureport2报表
|
||||||
|
export const createUReportData = async (data: UReportDataVO) => {
|
||||||
|
return await request.post({ url: `/report/ureport-data/create`, data })
|
||||||
|
}
|
||||||
|
|
||||||
|
// 修改Ureport2报表
|
||||||
|
export const updateUReportData = async (data: UReportDataVO) => {
|
||||||
|
return await request.put({ url: `/report/ureport-data/update`, data })
|
||||||
|
}
|
||||||
|
|
||||||
|
// 删除Ureport2报表
|
||||||
|
export const deleteUReportData = async (id: number) => {
|
||||||
|
return await request.delete({ url: `/report/ureport-data/delete?id=` + id })
|
||||||
|
}
|
||||||
|
|
||||||
|
// 导出Ureport2报表 Excel
|
||||||
|
export const exportUReportData = async (params) => {
|
||||||
|
return await request.download({ url: `/report/ureport-data/export-excel`, params })
|
||||||
|
}
|
||||||
@@ -14,7 +14,7 @@ export interface DeptVO {
|
|||||||
|
|
||||||
// 查询部门(精简)列表
|
// 查询部门(精简)列表
|
||||||
export const getSimpleDeptList = async (): Promise<DeptVO[]> => {
|
export const getSimpleDeptList = async (): Promise<DeptVO[]> => {
|
||||||
return await request.get({ url: '/system/dept/list-all-simple' })
|
return await request.get({ url: '/system/dept/simple-list' })
|
||||||
}
|
}
|
||||||
|
|
||||||
// 查询部门列表
|
// 查询部门列表
|
||||||
|
|||||||
@@ -14,8 +14,8 @@ export type DictDataVO = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 查询字典数据(精简)列表
|
// 查询字典数据(精简)列表
|
||||||
export const listSimpleDictData = () => {
|
export const getSimpleDictDataList = () => {
|
||||||
return request.get({ url: '/system/dict-data/list-all-simple' })
|
return request.get({ url: '/system/dict-data/simple-list' })
|
||||||
}
|
}
|
||||||
|
|
||||||
// 查询字典数据列表
|
// 查询字典数据列表
|
||||||
@@ -45,5 +45,5 @@ export const deleteDictData = (id: number) => {
|
|||||||
|
|
||||||
// 导出字典类型数据
|
// 导出字典类型数据
|
||||||
export const exportDictData = (params) => {
|
export const exportDictData = (params) => {
|
||||||
return request.get({ url: '/system/dict-data/export', params })
|
return request.download({ url: '/system/dict-data/export', params })
|
||||||
}
|
}
|
||||||
@@ -40,5 +40,5 @@ export const deleteDictType = (id: number) => {
|
|||||||
}
|
}
|
||||||
// 导出字典类型
|
// 导出字典类型
|
||||||
export const exportDictType = (params) => {
|
export const exportDictType = (params) => {
|
||||||
return request.get({ url: '/system/dict-type/export', params })
|
return request.download({ url: '/system/dict-type/export', params })
|
||||||
}
|
}
|
||||||
@@ -7,6 +7,7 @@ export interface LoginLogVO {
|
|||||||
userId: number
|
userId: number
|
||||||
userType: number
|
userType: number
|
||||||
username: string
|
username: string
|
||||||
|
result: number
|
||||||
status: number
|
status: number
|
||||||
userIp: string
|
userIp: string
|
||||||
userAgent: string
|
userAgent: string
|
||||||
|
|||||||
@@ -37,5 +37,5 @@ export const deleteMailAccount = async (id: number) => {
|
|||||||
|
|
||||||
// 获得邮箱账号精简列表
|
// 获得邮箱账号精简列表
|
||||||
export const getSimpleMailAccountList = async () => {
|
export const getSimpleMailAccountList = async () => {
|
||||||
return request.get({ url: '/system/mail-account/list-all-simple' })
|
return request.get({ url: '/system/mail-account/simple-list' })
|
||||||
}
|
}
|
||||||
@@ -20,7 +20,7 @@ export interface MenuVO {
|
|||||||
|
|
||||||
// 查询菜单(精简)列表
|
// 查询菜单(精简)列表
|
||||||
export const getSimpleMenusList = () => {
|
export const getSimpleMenusList = () => {
|
||||||
return request.get({ url: '/system/menu/list-all-simple' })
|
return request.get({ url: '/system/menu/simple-list' })
|
||||||
}
|
}
|
||||||
|
|
||||||
// 查询菜单列表
|
// 查询菜单列表
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ export interface NotifyMessageVO {
|
|||||||
templateParams: string
|
templateParams: string
|
||||||
readStatus: boolean
|
readStatus: boolean
|
||||||
readTime: Date
|
readTime: Date
|
||||||
|
createTime: Date
|
||||||
}
|
}
|
||||||
|
|
||||||
// 查询站内信消息列表
|
// 查询站内信消息列表
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ export interface NotifyTemplateVO {
|
|||||||
nickname: string
|
nickname: string
|
||||||
code: string
|
code: string
|
||||||
content: string
|
content: string
|
||||||
type: number
|
type?: number
|
||||||
params: string
|
params: string
|
||||||
status: number
|
status: number
|
||||||
remark: string
|
remark: string
|
||||||
|
|||||||
@@ -23,6 +23,32 @@ export type OperateLogVO = {
|
|||||||
resultData: string
|
resultData: string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export type OperateLogV2VO = {
|
||||||
|
id: number
|
||||||
|
userNickname: string
|
||||||
|
traceId: string
|
||||||
|
userType: number
|
||||||
|
userId: number
|
||||||
|
userName: string
|
||||||
|
type: string
|
||||||
|
subType: string
|
||||||
|
bizId: number
|
||||||
|
action: string
|
||||||
|
extra: string
|
||||||
|
requestMethod: string
|
||||||
|
requestUrl: string
|
||||||
|
userIp: string
|
||||||
|
userAgent: string
|
||||||
|
creator: string
|
||||||
|
creatorName: string
|
||||||
|
createTime: Date
|
||||||
|
// 数据扩展,渲染时使用
|
||||||
|
title: string // 操作标题(如果为空则取 name 值)
|
||||||
|
colSize: number // 变更记录行数
|
||||||
|
contentStrList: string[]
|
||||||
|
tagsContentList: string[]
|
||||||
|
}
|
||||||
|
|
||||||
// 查询操作日志列表
|
// 查询操作日志列表
|
||||||
export const getOperateLogPage = (params: PageParam) => {
|
export const getOperateLogPage = (params: PageParam) => {
|
||||||
return request.get({ url: '/system/operate-log/page', params })
|
return request.get({ url: '/system/operate-log/page', params })
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ export const getPostPage = async (params: PageParam) => {
|
|||||||
|
|
||||||
// 获取岗位精简信息列表
|
// 获取岗位精简信息列表
|
||||||
export const getSimplePostList = async (): Promise<PostVO[]> => {
|
export const getSimplePostList = async (): Promise<PostVO[]> => {
|
||||||
return await request.get({ url: '/system/post/list-all-simple' })
|
return await request.get({ url: '/system/post/simple-list' })
|
||||||
}
|
}
|
||||||
|
|
||||||
// 查询岗位详情
|
// 查询岗位详情
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ export const getRolePage = async (params: PageParam) => {
|
|||||||
|
|
||||||
// 查询角色(精简)列表
|
// 查询角色(精简)列表
|
||||||
export const getSimpleRoleList = async (): Promise<RoleVO[]> => {
|
export const getSimpleRoleList = async (): Promise<RoleVO[]> => {
|
||||||
return await request.get({ url: '/system/role/list-all-simple' })
|
return await request.get({ url: '/system/role/simple-list' })
|
||||||
}
|
}
|
||||||
|
|
||||||
// 查询角色详情
|
// 查询角色详情
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ export const getSmsChannelPage = (params: PageParam) => {
|
|||||||
|
|
||||||
// 获得短信渠道精简列表
|
// 获得短信渠道精简列表
|
||||||
export function getSimpleSmsChannelList() {
|
export function getSimpleSmsChannelList() {
|
||||||
return request.get({ url: '/system/sms-channel/list-all-simple' })
|
return request.get({ url: '/system/sms-channel/simple-list' })
|
||||||
}
|
}
|
||||||
|
|
||||||
// 查询短信渠道详情
|
// 查询短信渠道详情
|
||||||
|
|||||||
@@ -1,15 +1,15 @@
|
|||||||
import request from '@/config/axios'
|
import request from '@/config/axios'
|
||||||
|
|
||||||
export interface SmsTemplateVO {
|
export interface SmsTemplateVO {
|
||||||
id: number | null
|
id?: number
|
||||||
type: number | null
|
type?: number
|
||||||
status: number
|
status: number
|
||||||
code: string
|
code: string
|
||||||
name: string
|
name: string
|
||||||
content: string
|
content: string
|
||||||
remark: string
|
remark: string
|
||||||
apiTemplateId: string
|
apiTemplateId: string
|
||||||
channelId: number | null
|
channelId?: number
|
||||||
channelCode?: string
|
channelCode?: string
|
||||||
params?: string[]
|
params?: string[]
|
||||||
createTime?: Date
|
createTime?: Date
|
||||||
|
|||||||
@@ -38,5 +38,5 @@ export const deleteTenantPackage = (id: number) => {
|
|||||||
}
|
}
|
||||||
// 获取租户套餐精简信息列表
|
// 获取租户套餐精简信息列表
|
||||||
export const getTenantPackageList = () => {
|
export const getTenantPackageList = () => {
|
||||||
return request.get({ url: '/system/tenant-package/get-simple-list' })
|
return request.get({ url: '/system/tenant-package/simple-list' })
|
||||||
}
|
}
|
||||||
@@ -77,5 +77,5 @@ export const updateUserStatus = (id: number, status: number) => {
|
|||||||
|
|
||||||
// 获取用户精简信息列表
|
// 获取用户精简信息列表
|
||||||
export const getSimpleUserList = (): Promise<UserVO[]> => {
|
export const getSimpleUserList = (): Promise<UserVO[]> => {
|
||||||
return request.get({ url: '/system/user/list-all-simple' })
|
return request.get({ url: '/system/user/simple-list' })
|
||||||
}
|
}
|
||||||
@@ -1,37 +1,25 @@
|
|||||||
import request from '@/config/axios'
|
import request from '@/config/axios'
|
||||||
|
|
||||||
export interface ProfileDept {
|
|
||||||
id: number
|
|
||||||
name: string
|
|
||||||
}
|
|
||||||
export interface ProfileRole {
|
|
||||||
id: number
|
|
||||||
name: string
|
|
||||||
}
|
|
||||||
export interface ProfilePost {
|
|
||||||
id: number
|
|
||||||
name: string
|
|
||||||
}
|
|
||||||
export interface SocialUser {
|
|
||||||
id: number
|
|
||||||
type: number
|
|
||||||
openid: string
|
|
||||||
token: string
|
|
||||||
rawTokenInfo: string
|
|
||||||
nickname: string
|
|
||||||
avatar: string
|
|
||||||
rawUserInfo: string
|
|
||||||
code: string
|
|
||||||
state: string
|
|
||||||
}
|
|
||||||
export interface ProfileVO {
|
export interface ProfileVO {
|
||||||
id: number
|
id: number
|
||||||
username: string
|
username: string
|
||||||
nickname: string
|
nickname: string
|
||||||
dept: ProfileDept
|
dept: {
|
||||||
roles: ProfileRole[]
|
id: number
|
||||||
posts: ProfilePost[]
|
name: string
|
||||||
socialUsers: SocialUser[]
|
}
|
||||||
|
roles: {
|
||||||
|
id: number
|
||||||
|
name: string
|
||||||
|
}[]
|
||||||
|
posts: {
|
||||||
|
id: number
|
||||||
|
name: string
|
||||||
|
}[]
|
||||||
|
socialUsers: {
|
||||||
|
type: number
|
||||||
|
openid: string
|
||||||
|
}[]
|
||||||
email: string
|
email: string
|
||||||
mobile: string
|
mobile: string
|
||||||
sex: number
|
sex: number
|
||||||
|
|||||||
@@ -0,0 +1,207 @@
|
|||||||
|
<template>
|
||||||
|
<Dialog v-model="dialogVisible" title="选择链接" width="65%">
|
||||||
|
<div class="h-500px flex gap-8px">
|
||||||
|
<!-- 左侧分组列表 -->
|
||||||
|
<el-scrollbar wrap-class="h-full" ref="groupScrollbar" view-class="flex flex-col">
|
||||||
|
<el-button
|
||||||
|
v-for="(group, groupIndex) in APP_LINK_GROUP_LIST"
|
||||||
|
:key="groupIndex"
|
||||||
|
:class="[
|
||||||
|
'm-r-16px m-l-0px! justify-start! w-90px',
|
||||||
|
{ active: activeGroup === group.name }
|
||||||
|
]"
|
||||||
|
ref="groupBtnRefs"
|
||||||
|
:text="activeGroup !== group.name"
|
||||||
|
:type="activeGroup === group.name ? 'primary' : 'default'"
|
||||||
|
@click="handleGroupSelected(group.name)"
|
||||||
|
>
|
||||||
|
{{ group.name }}
|
||||||
|
</el-button>
|
||||||
|
</el-scrollbar>
|
||||||
|
<!-- 右侧链接列表 -->
|
||||||
|
<el-scrollbar class="h-full flex-1" @scroll="handleScroll" ref="linkScrollbar">
|
||||||
|
<div v-for="(group, groupIndex) in APP_LINK_GROUP_LIST" :key="groupIndex">
|
||||||
|
<!-- 分组标题 -->
|
||||||
|
<div class="font-bold" ref="groupTitleRefs">{{ group.name }}</div>
|
||||||
|
<!-- 链接列表 -->
|
||||||
|
<el-tooltip
|
||||||
|
v-for="(appLink, appLinkIndex) in group.links"
|
||||||
|
:key="appLinkIndex"
|
||||||
|
:content="appLink.path"
|
||||||
|
placement="bottom"
|
||||||
|
:show-after="300"
|
||||||
|
>
|
||||||
|
<el-button
|
||||||
|
class="m-b-8px m-r-8px m-l-0px!"
|
||||||
|
:type="isSameLink(appLink.path, activeAppLink.path) ? 'primary' : 'default'"
|
||||||
|
@click="handleAppLinkSelected(appLink)"
|
||||||
|
>
|
||||||
|
{{ appLink.name }}
|
||||||
|
</el-button>
|
||||||
|
</el-tooltip>
|
||||||
|
</div>
|
||||||
|
</el-scrollbar>
|
||||||
|
</div>
|
||||||
|
<!-- 底部对话框操作按钮 -->
|
||||||
|
<template #footer>
|
||||||
|
<el-button type="primary" @click="handleSubmit">确 定</el-button>
|
||||||
|
<el-button @click="dialogVisible = false">取 消</el-button>
|
||||||
|
</template>
|
||||||
|
</Dialog>
|
||||||
|
<Dialog v-model="detailSelectDialog.visible" title="" width="50%">
|
||||||
|
<el-form class="min-h-200px">
|
||||||
|
<el-form-item
|
||||||
|
label="选择分类"
|
||||||
|
v-if="detailSelectDialog.type === APP_LINK_TYPE_ENUM.PRODUCT_CATEGORY_LIST"
|
||||||
|
>
|
||||||
|
<ProductCategorySelect
|
||||||
|
v-model="detailSelectDialog.id"
|
||||||
|
:parent-id="0"
|
||||||
|
@update:model-value="handleProductCategorySelected"
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
</Dialog>
|
||||||
|
</template>
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import { APP_LINK_GROUP_LIST, APP_LINK_TYPE_ENUM, AppLink } from './data'
|
||||||
|
import { ButtonInstance, ScrollbarInstance } from 'element-plus'
|
||||||
|
import { split } from 'lodash-es'
|
||||||
|
import ProductCategorySelect from '@/views/mall/product/category/components/ProductCategorySelect.vue'
|
||||||
|
import { getUrlNumberValue } from '@/utils'
|
||||||
|
|
||||||
|
// APP 链接选择弹框
|
||||||
|
defineOptions({ name: 'AppLinkSelectDialog' })
|
||||||
|
// 选中的分组,默认选中第一个
|
||||||
|
const activeGroup = ref(APP_LINK_GROUP_LIST[0].name)
|
||||||
|
// 选中的 APP 链接
|
||||||
|
const activeAppLink = ref({} as AppLink)
|
||||||
|
|
||||||
|
/** 打开弹窗 */
|
||||||
|
const dialogVisible = ref(false)
|
||||||
|
const open = (link: string) => {
|
||||||
|
activeAppLink.value.path = link
|
||||||
|
dialogVisible.value = true
|
||||||
|
|
||||||
|
// 滚动到当前的链接
|
||||||
|
const group = APP_LINK_GROUP_LIST.find((group) =>
|
||||||
|
group.links.some((linkItem) => {
|
||||||
|
const sameLink = isSameLink(linkItem.path, link)
|
||||||
|
if (sameLink) {
|
||||||
|
activeAppLink.value = { ...linkItem, path: link }
|
||||||
|
}
|
||||||
|
return sameLink
|
||||||
|
})
|
||||||
|
)
|
||||||
|
if (group) {
|
||||||
|
// 使用 nextTick 的原因:可能 Dom 还没生成,导致滚动失败
|
||||||
|
nextTick(() => handleGroupSelected(group.name))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
defineExpose({ open })
|
||||||
|
|
||||||
|
// 处理 APP 链接选中
|
||||||
|
const handleAppLinkSelected = (appLink: AppLink) => {
|
||||||
|
if (!isSameLink(appLink.path, activeAppLink.value.path)) {
|
||||||
|
activeAppLink.value = appLink
|
||||||
|
}
|
||||||
|
switch (appLink.type) {
|
||||||
|
case APP_LINK_TYPE_ENUM.PRODUCT_CATEGORY_LIST:
|
||||||
|
detailSelectDialog.value.visible = true
|
||||||
|
detailSelectDialog.value.type = appLink.type
|
||||||
|
// 返显
|
||||||
|
detailSelectDialog.value.id =
|
||||||
|
getUrlNumberValue('id', 'http://127.0.0.1' + activeAppLink.value.path) || undefined
|
||||||
|
break
|
||||||
|
default:
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 处理绑定值更新
|
||||||
|
const emit = defineEmits<{
|
||||||
|
change: [link: string]
|
||||||
|
appLinkChange: [appLink: AppLink]
|
||||||
|
}>()
|
||||||
|
const handleSubmit = () => {
|
||||||
|
dialogVisible.value = false
|
||||||
|
emit('change', activeAppLink.value.path)
|
||||||
|
emit('appLinkChange', activeAppLink.value)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 分组标题引用列表
|
||||||
|
const groupTitleRefs = ref<HTMLInputElement[]>([])
|
||||||
|
/**
|
||||||
|
* 处理右侧链接列表滚动
|
||||||
|
* @param scrollTop 滚动条的位置
|
||||||
|
*/
|
||||||
|
const handleScroll = ({ scrollTop }: { scrollTop: number }) => {
|
||||||
|
const titleEl = groupTitleRefs.value.find((titleEl: HTMLInputElement) => {
|
||||||
|
// 获取标题的位置信息
|
||||||
|
const { offsetHeight, offsetTop } = titleEl
|
||||||
|
// 判断标题是否在可视范围内
|
||||||
|
return scrollTop >= offsetTop && scrollTop < offsetTop + offsetHeight
|
||||||
|
})
|
||||||
|
// 只需处理一次
|
||||||
|
if (titleEl && activeGroup.value !== titleEl.textContent) {
|
||||||
|
activeGroup.value = titleEl.textContent || ''
|
||||||
|
// 同步左侧的滚动条位置
|
||||||
|
scrollToGroupBtn(activeGroup.value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 右侧滚动条
|
||||||
|
const linkScrollbar = ref<ScrollbarInstance>()
|
||||||
|
// 处理分组选中
|
||||||
|
const handleGroupSelected = (group: string) => {
|
||||||
|
activeGroup.value = group
|
||||||
|
const titleRef = groupTitleRefs.value.find((item: HTMLInputElement) => item.textContent === group)
|
||||||
|
if (titleRef) {
|
||||||
|
// 滚动分组标题
|
||||||
|
linkScrollbar.value?.setScrollTop(titleRef.offsetTop)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 分组滚动条
|
||||||
|
const groupScrollbar = ref<ScrollbarInstance>()
|
||||||
|
// 分组引用列表
|
||||||
|
const groupBtnRefs = ref<ButtonInstance[]>([])
|
||||||
|
// 自动滚动分组按钮,确保分组按钮保持在可视区域内
|
||||||
|
const scrollToGroupBtn = (group: string) => {
|
||||||
|
const groupBtn = groupBtnRefs.value
|
||||||
|
.map((btn: ButtonInstance) => btn['ref'])
|
||||||
|
.find((ref: Node) => ref.textContent === group)
|
||||||
|
if (groupBtn) {
|
||||||
|
groupScrollbar.value?.setScrollTop(groupBtn.offsetTop)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 是否为相同的链接(不比较参数,只比较链接)
|
||||||
|
const isSameLink = (link1: string, link2: string) => {
|
||||||
|
return split(link1, '?', 1)[0] === split(link2, '?', 1)[0]
|
||||||
|
}
|
||||||
|
|
||||||
|
// 详情选择对话框
|
||||||
|
const detailSelectDialog = ref<{
|
||||||
|
visible: boolean
|
||||||
|
id?: number
|
||||||
|
type?: APP_LINK_TYPE_ENUM
|
||||||
|
}>({
|
||||||
|
visible: false,
|
||||||
|
id: undefined,
|
||||||
|
type: undefined
|
||||||
|
})
|
||||||
|
// 处理详情选择
|
||||||
|
const handleProductCategorySelected = (id: number) => {
|
||||||
|
const url = new URL(activeAppLink.value.path, 'http://127.0.0.1')
|
||||||
|
// 修改 id 参数
|
||||||
|
url.searchParams.set('id', `${id}`)
|
||||||
|
// 排除域名
|
||||||
|
activeAppLink.value.path = `${url.pathname}${url.search}`
|
||||||
|
// 关闭对话框
|
||||||
|
detailSelectDialog.value.visible = false
|
||||||
|
// 重置 id
|
||||||
|
detailSelectDialog.value.id = undefined
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
<style lang="scss" scoped></style>
|
||||||
@@ -0,0 +1,228 @@
|
|||||||
|
// APP 链接分组
|
||||||
|
export interface AppLinkGroup {
|
||||||
|
// 分组名称
|
||||||
|
name: string
|
||||||
|
// 链接列表
|
||||||
|
links: AppLink[]
|
||||||
|
}
|
||||||
|
// APP 链接
|
||||||
|
export interface AppLink {
|
||||||
|
// 链接名称
|
||||||
|
name: string
|
||||||
|
// 链接地址
|
||||||
|
path: string
|
||||||
|
// 链接的类型
|
||||||
|
type?: APP_LINK_TYPE_ENUM
|
||||||
|
}
|
||||||
|
|
||||||
|
// APP 链接类型(需要特殊处理,例如商品详情)
|
||||||
|
export const enum APP_LINK_TYPE_ENUM {
|
||||||
|
// 拼团活动
|
||||||
|
ACTIVITY_COMBINATION,
|
||||||
|
// 秒杀活动
|
||||||
|
ACTIVITY_SECKILL,
|
||||||
|
// 文章详情
|
||||||
|
ARTICLE_DETAIL,
|
||||||
|
// 优惠券详情
|
||||||
|
COUPON_DETAIL,
|
||||||
|
// 自定义页面详情
|
||||||
|
DIY_PAGE_DETAIL,
|
||||||
|
// 品类列表
|
||||||
|
PRODUCT_CATEGORY_LIST,
|
||||||
|
// 商品列表
|
||||||
|
PRODUCT_LIST,
|
||||||
|
// 商品详情
|
||||||
|
PRODUCT_DETAIL_NORMAL,
|
||||||
|
// 拼团商品详情
|
||||||
|
PRODUCT_DETAIL_COMBINATION,
|
||||||
|
// 秒杀商品详情
|
||||||
|
PRODUCT_DETAIL_SECKILL
|
||||||
|
}
|
||||||
|
|
||||||
|
// APP 链接列表(做一下持久化?)
|
||||||
|
export const APP_LINK_GROUP_LIST = [
|
||||||
|
{
|
||||||
|
name: '商城',
|
||||||
|
links: [
|
||||||
|
{
|
||||||
|
name: '首页',
|
||||||
|
path: '/pages/index/index'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: '商品分类',
|
||||||
|
path: '/pages/index/category',
|
||||||
|
type: APP_LINK_TYPE_ENUM.PRODUCT_CATEGORY_LIST
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: '购物车',
|
||||||
|
path: '/pages/index/cart'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: '个人中心',
|
||||||
|
path: '/pages/index/user'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: '商品搜索',
|
||||||
|
path: '/pages/index/search'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: '自定义页面',
|
||||||
|
path: '/pages/index/page',
|
||||||
|
type: APP_LINK_TYPE_ENUM.DIY_PAGE_DETAIL
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: '客服',
|
||||||
|
path: '/pages/chat/index'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: '系统设置',
|
||||||
|
path: '/pages/public/setting'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: '常见问题',
|
||||||
|
path: '/pages/public/faq'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: '商品',
|
||||||
|
links: [
|
||||||
|
{
|
||||||
|
name: '商品列表',
|
||||||
|
path: '/pages/goods/list',
|
||||||
|
type: APP_LINK_TYPE_ENUM.PRODUCT_LIST
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: '商品详情',
|
||||||
|
path: '/pages/goods/index',
|
||||||
|
type: APP_LINK_TYPE_ENUM.PRODUCT_DETAIL_NORMAL
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: '拼团商品详情',
|
||||||
|
path: '/pages/goods/groupon',
|
||||||
|
type: APP_LINK_TYPE_ENUM.PRODUCT_DETAIL_COMBINATION
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: '秒杀商品详情',
|
||||||
|
path: '/pages/goods/seckill',
|
||||||
|
type: APP_LINK_TYPE_ENUM.PRODUCT_DETAIL_SECKILL
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: '营销活动',
|
||||||
|
links: [
|
||||||
|
{
|
||||||
|
name: '拼团订单',
|
||||||
|
path: '/pages/activity/groupon/order'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: '营销商品',
|
||||||
|
path: '/pages/activity/index'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: '拼团活动',
|
||||||
|
path: '/pages/activity/groupon/list',
|
||||||
|
type: APP_LINK_TYPE_ENUM.ACTIVITY_COMBINATION
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: '秒杀活动',
|
||||||
|
path: '/pages/activity/seckill/list',
|
||||||
|
type: APP_LINK_TYPE_ENUM.ACTIVITY_SECKILL
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: '签到中心',
|
||||||
|
path: '/pages/app/sign'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: '优惠券中心',
|
||||||
|
path: '/pages/coupon/list'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: '优惠券详情',
|
||||||
|
path: '/pages/coupon/detail',
|
||||||
|
type: APP_LINK_TYPE_ENUM.COUPON_DETAIL
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: '文章详情',
|
||||||
|
path: '/pages/public/richtext',
|
||||||
|
type: APP_LINK_TYPE_ENUM.ARTICLE_DETAIL
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: '分销商城',
|
||||||
|
links: [
|
||||||
|
{
|
||||||
|
name: '分销中心',
|
||||||
|
path: '/pages/commission/index'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: '推广商品',
|
||||||
|
path: '/pages/commission/goods'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: '分销订单',
|
||||||
|
path: '/pages/commission/order'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: '我的团队',
|
||||||
|
path: '/pages/commission/team'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: '支付',
|
||||||
|
links: [
|
||||||
|
{
|
||||||
|
name: '充值余额',
|
||||||
|
path: '/pages/pay/recharge'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: '充值记录',
|
||||||
|
path: '/pages/pay/recharge-log'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: '用户中心',
|
||||||
|
links: [
|
||||||
|
{
|
||||||
|
name: '用户信息',
|
||||||
|
path: '/pages/user/info'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: '用户订单',
|
||||||
|
path: '/pages/order/list'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: '售后订单',
|
||||||
|
path: '/pages/order/aftersale/list'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: '商品收藏',
|
||||||
|
path: '/pages/user/goods-collect'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: '浏览记录',
|
||||||
|
path: '/pages/user/goods-log'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: '地址管理',
|
||||||
|
path: '/pages/user/address/list'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: '用户佣金',
|
||||||
|
path: '/pages/user/wallet/commission'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: '用户余额',
|
||||||
|
path: '/pages/user/wallet/money'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: '用户积分',
|
||||||
|
path: '/pages/user/wallet/score'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
] as AppLinkGroup[]
|
||||||
@@ -0,0 +1,43 @@
|
|||||||
|
<template>
|
||||||
|
<el-input v-model="appLink" placeholder="输入或选择链接">
|
||||||
|
<template #append>
|
||||||
|
<el-button @click="handleOpenDialog">选择</el-button>
|
||||||
|
</template>
|
||||||
|
</el-input>
|
||||||
|
<AppLinkSelectDialog ref="dialogRef" @change="handleLinkSelected" />
|
||||||
|
</template>
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import { propTypes } from '@/utils/propTypes'
|
||||||
|
|
||||||
|
// APP 链接输入框
|
||||||
|
defineOptions({ name: 'AppLinkInput' })
|
||||||
|
// 定义属性
|
||||||
|
const props = defineProps({
|
||||||
|
// 当前选中的链接
|
||||||
|
modelValue: propTypes.string.def('')
|
||||||
|
})
|
||||||
|
// 当前的链接
|
||||||
|
const appLink = ref('')
|
||||||
|
// 选择对话框
|
||||||
|
const dialogRef = ref()
|
||||||
|
// 处理打开对话框
|
||||||
|
const handleOpenDialog = () => dialogRef.value?.open(appLink.value)
|
||||||
|
// 处理 APP 链接选中
|
||||||
|
const handleLinkSelected = (link: string) => (appLink.value = link)
|
||||||
|
|
||||||
|
// getter
|
||||||
|
watch(
|
||||||
|
() => props.modelValue,
|
||||||
|
() => (appLink.value = props.modelValue),
|
||||||
|
{ immediate: true }
|
||||||
|
)
|
||||||
|
|
||||||
|
// setter
|
||||||
|
const emit = defineEmits<{
|
||||||
|
'update:modelValue': [link: string]
|
||||||
|
}>()
|
||||||
|
watch(
|
||||||
|
() => appLink.value,
|
||||||
|
() => emit('update:modelValue', appLink.value)
|
||||||
|
)
|
||||||
|
</script>
|
||||||
@@ -8,11 +8,11 @@
|
|||||||
<component :is="component.id" :property="component.property" />
|
<component :is="component.id" :property="component.property" />
|
||||||
</div>
|
</div>
|
||||||
<div class="component-wrap">
|
<div class="component-wrap">
|
||||||
<!-- 左侧组件名 -->
|
<!-- 左侧:组件名(悬浮的小贴条) -->
|
||||||
<div class="component-name" v-if="component.name">
|
<div class="component-name" v-if="component.name">
|
||||||
{{ component.name }}
|
{{ component.name }}
|
||||||
</div>
|
</div>
|
||||||
<!-- 左侧:组件操作工具栏 -->
|
<!-- 右侧:组件操作工具栏 -->
|
||||||
<div class="component-toolbar" v-if="showToolbar && component.name && active">
|
<div class="component-toolbar" v-if="showToolbar && component.name && active">
|
||||||
<VerticalButtonGroup type="primary">
|
<VerticalButtonGroup type="primary">
|
||||||
<el-tooltip content="上移" placement="right">
|
<el-tooltip content="上移" placement="right">
|
||||||
@@ -54,7 +54,7 @@ import { propTypes } from '@/utils/propTypes'
|
|||||||
import { object } from 'vue-types'
|
import { object } from 'vue-types'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 组件容器
|
* 组件容器:目前在中间部分
|
||||||
* 用于包裹组件,为组件提供 背景、外边距、内边距、边框等样式
|
* 用于包裹组件,为组件提供 背景、外边距、内边距、边框等样式
|
||||||
*/
|
*/
|
||||||
defineOptions({ name: 'ComponentContainer' })
|
defineOptions({ name: 'ComponentContainer' })
|
||||||
@@ -100,6 +100,7 @@ const emits = defineEmits<{
|
|||||||
(e: 'copy'): void
|
(e: 'copy'): void
|
||||||
(e: 'delete'): void
|
(e: 'delete'): void
|
||||||
}>()
|
}>()
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 移动组件
|
* 移动组件
|
||||||
* @param direction 移动方向
|
* @param direction 移动方向
|
||||||
@@ -107,12 +108,14 @@ const emits = defineEmits<{
|
|||||||
const handleMoveComponent = (direction: number) => {
|
const handleMoveComponent = (direction: number) => {
|
||||||
emits('move', direction)
|
emits('move', direction)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 复制组件
|
* 复制组件
|
||||||
*/
|
*/
|
||||||
const handleCopyComponent = () => {
|
const handleCopyComponent = () => {
|
||||||
emits('copy')
|
emits('copy')
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 删除组件
|
* 删除组件
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -1,8 +1,11 @@
|
|||||||
<template>
|
<template>
|
||||||
<el-tabs stretch>
|
<el-tabs stretch>
|
||||||
<el-tab-pane label="内容">
|
<!-- 每个组件的自定义内容 -->
|
||||||
|
<el-tab-pane label="内容" v-if="$slots.default">
|
||||||
<slot></slot>
|
<slot></slot>
|
||||||
</el-tab-pane>
|
</el-tab-pane>
|
||||||
|
|
||||||
|
<!-- 每个组件的通用内容 -->
|
||||||
<el-tab-pane label="样式" lazy>
|
<el-tab-pane label="样式" lazy>
|
||||||
<el-card header="组件样式" class="property-group">
|
<el-card header="组件样式" class="property-group">
|
||||||
<el-form :model="formData" label-width="80px">
|
<el-form :model="formData" label-width="80px">
|
||||||
@@ -20,7 +23,7 @@
|
|||||||
<template #tip>建议宽度 750px</template>
|
<template #tip>建议宽度 750px</template>
|
||||||
</UploadImg>
|
</UploadImg>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-tree :data="treeData" :expand-on-click-node="false">
|
<el-tree :data="treeData" :expand-on-click-node="false" default-expand-all>
|
||||||
<template #default="{ node, data }">
|
<template #default="{ node, data }">
|
||||||
<el-form-item
|
<el-form-item
|
||||||
:label="data.label"
|
:label="data.label"
|
||||||
@@ -40,7 +43,7 @@
|
|||||||
</el-form-item>
|
</el-form-item>
|
||||||
</template>
|
</template>
|
||||||
</el-tree>
|
</el-tree>
|
||||||
<slot name="style" :formData="formData"></slot>
|
<slot name="style" :style="formData"></slot>
|
||||||
</el-form>
|
</el-form>
|
||||||
</el-card>
|
</el-card>
|
||||||
</el-tab-pane>
|
</el-tab-pane>
|
||||||
@@ -51,7 +54,7 @@
|
|||||||
import { ComponentStyle, usePropertyForm } from '@/components/DiyEditor/util'
|
import { ComponentStyle, usePropertyForm } from '@/components/DiyEditor/util'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 组件容器属性
|
* 组件容器属性:目前右边部分
|
||||||
* 用于包裹组件,为组件提供 背景、外边距、内边距、边框等样式
|
* 用于包裹组件,为组件提供 背景、外边距、内边距、边框等样式
|
||||||
*/
|
*/
|
||||||
defineOptions({ name: 'ComponentContainer' })
|
defineOptions({ name: 'ComponentContainer' })
|
||||||
|
|||||||
@@ -41,16 +41,19 @@ import { componentConfigs } from '../components/mobile/index'
|
|||||||
import { cloneDeep } from 'lodash-es'
|
import { cloneDeep } from 'lodash-es'
|
||||||
import { DiyComponent, DiyComponentLibrary } from '@/components/DiyEditor/util'
|
import { DiyComponent, DiyComponentLibrary } from '@/components/DiyEditor/util'
|
||||||
|
|
||||||
/** 组件库 */
|
/** 组件库:目前左侧的【基础组件】、【图文组件】部分 */
|
||||||
defineOptions({ name: 'ComponentLibrary' })
|
defineOptions({ name: 'ComponentLibrary' })
|
||||||
|
|
||||||
// 组件列表
|
// 组件列表
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
list: DiyComponentLibrary[]
|
list: DiyComponentLibrary[]
|
||||||
}>()
|
}>()
|
||||||
|
// 组件分组
|
||||||
const groups = reactive<any[]>([])
|
const groups = reactive<any[]>([])
|
||||||
// 展开的折叠面板
|
// 展开的折叠面板
|
||||||
const extendGroups = reactive<string[]>([])
|
const extendGroups = reactive<string[]>([])
|
||||||
|
|
||||||
|
// 监听 list 属性,按照 DiyComponentLibrary 的 name 分组
|
||||||
watch(
|
watch(
|
||||||
() => props.list,
|
() => props.list,
|
||||||
() => {
|
() => {
|
||||||
@@ -82,7 +85,9 @@ watch(
|
|||||||
|
|
||||||
// 克隆组件
|
// 克隆组件
|
||||||
const handleCloneComponent = (component: DiyComponent<any>) => {
|
const handleCloneComponent = (component: DiyComponent<any>) => {
|
||||||
return cloneDeep(component)
|
const instance = cloneDeep(component)
|
||||||
|
instance.uid = new Date().getTime()
|
||||||
|
return instance
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|||||||
@@ -39,87 +39,60 @@
|
|||||||
</el-form-item>
|
</el-form-item>
|
||||||
</el-card>
|
</el-card>
|
||||||
<el-card header="内容设置" class="property-group" shadow="never">
|
<el-card header="内容设置" class="property-group" shadow="never">
|
||||||
<el-text type="info" size="small"> 拖动左上角的小圆点可对其排序 </el-text>
|
<Draggable v-model="formData.items" :empty-item="{ type: 'img' }">
|
||||||
<template v-if="formData.items[0]">
|
<template #default="{ element }">
|
||||||
<draggable
|
<el-form-item label="类型" prop="type" class="m-b-8px!" label-width="40px">
|
||||||
:list="formData.items"
|
<el-radio-group v-model="element.type">
|
||||||
:force-fallback="true"
|
<el-radio label="img">图片</el-radio>
|
||||||
:animation="200"
|
<el-radio label="video">视频</el-radio>
|
||||||
handle=".drag-icon"
|
</el-radio-group>
|
||||||
class="m-t-8px"
|
</el-form-item>
|
||||||
item-key="index"
|
<el-form-item
|
||||||
>
|
label="图片"
|
||||||
<template #item="{ element, index }">
|
class="m-b-8px!"
|
||||||
<div class="content mb-4px flex flex-col gap-4px rounded bg-gray-50 p-8px">
|
label-width="40px"
|
||||||
<div
|
v-if="element.type === 'img'"
|
||||||
class="m--8px m-b-8px flex flex-row items-center justify-between bg-gray-100 p-8px"
|
>
|
||||||
>
|
<UploadImg
|
||||||
<Icon icon="ic:round-drag-indicator" class="drag-icon cursor-move" />
|
v-model="element.imgUrl"
|
||||||
<Icon
|
draggable="false"
|
||||||
icon="ep:delete"
|
height="80px"
|
||||||
class="cursor-pointer text-red-5"
|
width="100%"
|
||||||
@click="handleDeleteImage(index)"
|
class="min-w-80px"
|
||||||
v-if="formData.items.length > 1"
|
/>
|
||||||
/>
|
</el-form-item>
|
||||||
</div>
|
<template v-else>
|
||||||
<el-form-item label="类型" prop="type" class="m-b-8px!" label-width="50px">
|
<el-form-item label="封面" class="m-b-8px!" label-width="40px">
|
||||||
<el-radio-group v-model="element.type">
|
<UploadImg
|
||||||
<el-radio label="img">图片</el-radio>
|
v-model="element.imgUrl"
|
||||||
<el-radio label="video">视频</el-radio>
|
draggable="false"
|
||||||
</el-radio-group>
|
height="80px"
|
||||||
</el-form-item>
|
width="100%"
|
||||||
<el-form-item
|
class="min-w-80px"
|
||||||
label="图片"
|
/>
|
||||||
class="m-b-8px!"
|
</el-form-item>
|
||||||
label-width="50px"
|
<el-form-item label="视频" class="m-b-8px!" label-width="40px">
|
||||||
v-if="element.type === 'img'"
|
<UploadFile
|
||||||
>
|
v-model="element.videoUrl"
|
||||||
<UploadImg
|
:file-type="['mp4']"
|
||||||
v-model="element.imgUrl"
|
:limit="1"
|
||||||
draggable="false"
|
:file-size="100"
|
||||||
height="80px"
|
class="min-w-80px"
|
||||||
width="100%"
|
/>
|
||||||
class="min-w-80px"
|
</el-form-item>
|
||||||
/>
|
|
||||||
</el-form-item>
|
|
||||||
<template v-else>
|
|
||||||
<el-form-item label="封面" class="m-b-8px!" label-width="50px">
|
|
||||||
<UploadImg
|
|
||||||
v-model="element.imgUrl"
|
|
||||||
draggable="false"
|
|
||||||
height="80px"
|
|
||||||
width="100%"
|
|
||||||
class="min-w-80px"
|
|
||||||
/>
|
|
||||||
</el-form-item>
|
|
||||||
<el-form-item label="视频" class="m-b-8px!" label-width="50px">
|
|
||||||
<UploadFile
|
|
||||||
v-model="element.videoUrl"
|
|
||||||
:file-type="['mp4']"
|
|
||||||
:limit="1"
|
|
||||||
:file-size="100"
|
|
||||||
class="min-w-80px"
|
|
||||||
/>
|
|
||||||
</el-form-item>
|
|
||||||
</template>
|
|
||||||
<el-form-item label="链接" class="m-b-8px!" label-width="50px">
|
|
||||||
<el-input placeholder="链接" v-model="element.url" />
|
|
||||||
</el-form-item>
|
|
||||||
</div>
|
|
||||||
</template>
|
</template>
|
||||||
</draggable>
|
<el-form-item label="链接" class="m-b-8px!" label-width="40px">
|
||||||
</template>
|
<AppLinkInput v-model="element.url" />
|
||||||
<el-button @click="handleAddImage" type="primary" plain class="w-full">
|
</el-form-item>
|
||||||
添加图片
|
</template>
|
||||||
</el-button>
|
</Draggable>
|
||||||
</el-card>
|
</el-card>
|
||||||
</el-form>
|
</el-form>
|
||||||
</ComponentContainerProperty>
|
</ComponentContainerProperty>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import draggable from 'vuedraggable' //拖拽组件
|
import { CarouselProperty } from './config'
|
||||||
import { CarouselItemProperty, CarouselProperty } from './config'
|
|
||||||
import { usePropertyForm } from '@/components/DiyEditor/util'
|
import { usePropertyForm } from '@/components/DiyEditor/util'
|
||||||
|
|
||||||
// 轮播图属性面板
|
// 轮播图属性面板
|
||||||
@@ -128,15 +101,6 @@ defineOptions({ name: 'CarouselProperty' })
|
|||||||
const props = defineProps<{ modelValue: CarouselProperty }>()
|
const props = defineProps<{ modelValue: CarouselProperty }>()
|
||||||
const emit = defineEmits(['update:modelValue'])
|
const emit = defineEmits(['update:modelValue'])
|
||||||
const { formData } = usePropertyForm(props.modelValue, emit)
|
const { formData } = usePropertyForm(props.modelValue, emit)
|
||||||
|
|
||||||
// 添加图片
|
|
||||||
const handleAddImage = () => {
|
|
||||||
formData.value.items.push({} as CarouselItemProperty)
|
|
||||||
}
|
|
||||||
// 删除图片
|
|
||||||
const handleDeleteImage = (index: number) => {
|
|
||||||
formData.value.items.splice(index, 1)
|
|
||||||
}
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped lang="scss"></style>
|
<style scoped lang="scss"></style>
|
||||||
@@ -2,14 +2,13 @@ import * as CouponTemplateApi from '@/api/mall/promotion/coupon/couponTemplate'
|
|||||||
import { CouponTemplateValidityTypeEnum, PromotionDiscountTypeEnum } from '@/utils/constants'
|
import { CouponTemplateValidityTypeEnum, PromotionDiscountTypeEnum } from '@/utils/constants'
|
||||||
import { floatToFixed2 } from '@/utils'
|
import { floatToFixed2 } from '@/utils'
|
||||||
import { formatDate } from '@/utils/formatTime'
|
import { formatDate } from '@/utils/formatTime'
|
||||||
|
import { object } from 'vue-types'
|
||||||
|
|
||||||
// 优惠值
|
// 优惠值
|
||||||
export const CouponDiscount = defineComponent({
|
export const CouponDiscount = defineComponent({
|
||||||
name: 'CouponDiscount',
|
name: 'CouponDiscount',
|
||||||
props: {
|
props: {
|
||||||
coupon: {
|
coupon: object<CouponTemplateApi.CouponTemplateVO>()
|
||||||
type: CouponTemplateApi.CouponTemplateVO
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
setup(props) {
|
setup(props) {
|
||||||
const coupon = props.coupon as CouponTemplateApi.CouponTemplateVO
|
const coupon = props.coupon as CouponTemplateApi.CouponTemplateVO
|
||||||
@@ -34,9 +33,7 @@ export const CouponDiscount = defineComponent({
|
|||||||
export const CouponDiscountDesc = defineComponent({
|
export const CouponDiscountDesc = defineComponent({
|
||||||
name: 'CouponDiscountDesc',
|
name: 'CouponDiscountDesc',
|
||||||
props: {
|
props: {
|
||||||
coupon: {
|
coupon: object<CouponTemplateApi.CouponTemplateVO>()
|
||||||
type: CouponTemplateApi.CouponTemplateVO
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
setup(props) {
|
setup(props) {
|
||||||
const coupon = props.coupon as CouponTemplateApi.CouponTemplateVO
|
const coupon = props.coupon as CouponTemplateApi.CouponTemplateVO
|
||||||
@@ -60,9 +57,7 @@ export const CouponDiscountDesc = defineComponent({
|
|||||||
export const CouponValidTerm = defineComponent({
|
export const CouponValidTerm = defineComponent({
|
||||||
name: 'CouponValidTerm',
|
name: 'CouponValidTerm',
|
||||||
props: {
|
props: {
|
||||||
coupon: {
|
coupon: object<CouponTemplateApi.CouponTemplateVO>()
|
||||||
type: CouponTemplateApi.CouponTemplateVO
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
setup(props) {
|
setup(props) {
|
||||||
const coupon = props.coupon as CouponTemplateApi.CouponTemplateVO
|
const coupon = props.coupon as CouponTemplateApi.CouponTemplateVO
|
||||||
|
|||||||
@@ -0,0 +1,36 @@
|
|||||||
|
import { DiyComponent } from '@/components/DiyEditor/util'
|
||||||
|
|
||||||
|
// 悬浮按钮属性
|
||||||
|
export interface FloatingActionButtonProperty {
|
||||||
|
// 展开方向
|
||||||
|
direction: 'horizontal' | 'vertical'
|
||||||
|
// 是否显示文字
|
||||||
|
showText: boolean
|
||||||
|
// 按钮列表
|
||||||
|
list: FloatingActionButtonItemProperty[]
|
||||||
|
}
|
||||||
|
|
||||||
|
// 悬浮按钮项属性
|
||||||
|
export interface FloatingActionButtonItemProperty {
|
||||||
|
// 图片地址
|
||||||
|
imgUrl: string
|
||||||
|
// 跳转连接
|
||||||
|
url: string
|
||||||
|
// 文字
|
||||||
|
text: string
|
||||||
|
// 文字颜色
|
||||||
|
textColor: string
|
||||||
|
}
|
||||||
|
|
||||||
|
// 定义组件
|
||||||
|
export const component = {
|
||||||
|
id: 'FloatingActionButton',
|
||||||
|
name: '悬浮按钮',
|
||||||
|
icon: 'tabler:float-right',
|
||||||
|
position: 'fixed',
|
||||||
|
property: {
|
||||||
|
direction: 'vertical',
|
||||||
|
showText: true,
|
||||||
|
list: [{ textColor: '#fff' }]
|
||||||
|
}
|
||||||
|
} as DiyComponent<FloatingActionButtonProperty>
|
||||||
@@ -0,0 +1,74 @@
|
|||||||
|
<template>
|
||||||
|
<div
|
||||||
|
:class="[
|
||||||
|
'absolute bottom-32px right-[calc(50%-375px/2+32px)] flex z-12 gap-12px items-center',
|
||||||
|
{
|
||||||
|
'flex-row': property.direction === 'horizontal',
|
||||||
|
'flex-col': property.direction === 'vertical'
|
||||||
|
}
|
||||||
|
]"
|
||||||
|
>
|
||||||
|
<template v-if="expanded">
|
||||||
|
<div
|
||||||
|
v-for="(item, index) in property.list"
|
||||||
|
:key="index"
|
||||||
|
class="flex flex-col items-center"
|
||||||
|
@click="handleActive(index)"
|
||||||
|
>
|
||||||
|
<el-image :src="item.imgUrl" fit="contain" class="h-27px w-27px">
|
||||||
|
<template #error>
|
||||||
|
<div class="h-full w-full flex items-center justify-center">
|
||||||
|
<Icon icon="ep:picture" :color="item.textColor" />
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</el-image>
|
||||||
|
<span v-if="property.showText" class="mt-4px text-12px" :style="{ color: item.textColor }">
|
||||||
|
{{ item.text }}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<!-- todo: @owen 使用APP主题色 -->
|
||||||
|
<el-button type="primary" size="large" circle @click="handleToggleFab">
|
||||||
|
<Icon icon="ep:plus" :class="['fab-icon', { active: expanded }]" />
|
||||||
|
</el-button>
|
||||||
|
</div>
|
||||||
|
<!-- 模态背景:展开时显示,点击后折叠 -->
|
||||||
|
<div v-if="expanded" class="modal-bg" @click="handleToggleFab"></div>
|
||||||
|
</template>
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { FloatingActionButtonProperty } from './config'
|
||||||
|
|
||||||
|
/** 悬浮按钮 */
|
||||||
|
defineOptions({ name: 'FloatingActionButton' })
|
||||||
|
// 定义属性
|
||||||
|
defineProps<{ property: FloatingActionButtonProperty }>()
|
||||||
|
|
||||||
|
// 是否展开
|
||||||
|
const expanded = ref(true)
|
||||||
|
// 处理展开/折叠
|
||||||
|
const handleToggleFab = () => {
|
||||||
|
expanded.value = !expanded.value
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped lang="scss">
|
||||||
|
/* 模态背景 */
|
||||||
|
.modal-bg {
|
||||||
|
position: absolute;
|
||||||
|
left: calc(50% - 375px / 2);
|
||||||
|
top: 0;
|
||||||
|
z-index: 11;
|
||||||
|
width: 375px;
|
||||||
|
height: 100%;
|
||||||
|
background-color: rgba(#000000, 0.4);
|
||||||
|
}
|
||||||
|
|
||||||
|
.fab-icon {
|
||||||
|
transform: rotate(0deg);
|
||||||
|
transition: transform 0.3s;
|
||||||
|
|
||||||
|
&.active {
|
||||||
|
transform: rotate(135deg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -0,0 +1,44 @@
|
|||||||
|
<template>
|
||||||
|
<el-form label-width="80px" :model="formData">
|
||||||
|
<el-card header="按钮配置" class="property-group" shadow="never">
|
||||||
|
<el-form-item label="展开方向" prop="direction">
|
||||||
|
<el-radio-group v-model="formData.direction">
|
||||||
|
<el-radio label="vertical">垂直</el-radio>
|
||||||
|
<el-radio label="horizontal">水平</el-radio>
|
||||||
|
</el-radio-group>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="显示文字" prop="showText">
|
||||||
|
<el-switch v-model="formData.showText" />
|
||||||
|
</el-form-item>
|
||||||
|
</el-card>
|
||||||
|
<el-card header="按钮列表" class="property-group" shadow="never">
|
||||||
|
<Draggable v-model="formData.list" :empty-item="{ textColor: '#fff' }">
|
||||||
|
<template #default="{ element, index }">
|
||||||
|
<el-form-item label="图标" :prop="`list[${index}].imgUrl`">
|
||||||
|
<UploadImg v-model="element.imgUrl" height="56px" width="56px" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="文字" :prop="`list[${index}].text`">
|
||||||
|
<InputWithColor v-model="element.text" v-model:color="element.textColor" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="跳转链接" :prop="`list[${index}].url`">
|
||||||
|
<AppLinkInput v-model="element.url" />
|
||||||
|
</el-form-item>
|
||||||
|
</template>
|
||||||
|
</Draggable>
|
||||||
|
</el-card>
|
||||||
|
</el-form>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { FloatingActionButtonProperty } from './config'
|
||||||
|
import { usePropertyForm } from '@/components/DiyEditor/util'
|
||||||
|
|
||||||
|
// 悬浮按钮属性面板
|
||||||
|
defineOptions({ name: 'FloatingActionButtonProperty' })
|
||||||
|
|
||||||
|
const props = defineProps<{ modelValue: FloatingActionButtonProperty }>()
|
||||||
|
const emit = defineEmits(['update:modelValue'])
|
||||||
|
const { formData } = usePropertyForm(props.modelValue, emit)
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped lang="scss"></style>
|
||||||
+143
@@ -0,0 +1,143 @@
|
|||||||
|
import { HotZoneItemProperty } from '@/components/DiyEditor/components/mobile/HotZone/config'
|
||||||
|
import { StyleValue } from 'vue'
|
||||||
|
|
||||||
|
// 热区的最小宽高
|
||||||
|
export const HOT_ZONE_MIN_SIZE = 100
|
||||||
|
|
||||||
|
// 控制的类型
|
||||||
|
export enum CONTROL_TYPE_ENUM {
|
||||||
|
LEFT,
|
||||||
|
TOP,
|
||||||
|
WIDTH,
|
||||||
|
HEIGHT
|
||||||
|
}
|
||||||
|
|
||||||
|
// 定义热区的控制点
|
||||||
|
export interface ControlDot {
|
||||||
|
position: string
|
||||||
|
types: CONTROL_TYPE_ENUM[]
|
||||||
|
style: StyleValue
|
||||||
|
}
|
||||||
|
|
||||||
|
// 热区的8个控制点
|
||||||
|
export const CONTROL_DOT_LIST = [
|
||||||
|
{
|
||||||
|
position: '左上角',
|
||||||
|
types: [
|
||||||
|
CONTROL_TYPE_ENUM.LEFT,
|
||||||
|
CONTROL_TYPE_ENUM.TOP,
|
||||||
|
CONTROL_TYPE_ENUM.WIDTH,
|
||||||
|
CONTROL_TYPE_ENUM.HEIGHT
|
||||||
|
],
|
||||||
|
style: { left: '-5px', top: '-5px', cursor: 'nwse-resize' }
|
||||||
|
},
|
||||||
|
{
|
||||||
|
position: '上方中间',
|
||||||
|
types: [CONTROL_TYPE_ENUM.TOP, CONTROL_TYPE_ENUM.HEIGHT],
|
||||||
|
style: { left: '50%', top: '-5px', cursor: 'n-resize', transform: 'translateX(-50%)' }
|
||||||
|
},
|
||||||
|
{
|
||||||
|
position: '右上角',
|
||||||
|
types: [CONTROL_TYPE_ENUM.TOP, CONTROL_TYPE_ENUM.WIDTH, CONTROL_TYPE_ENUM.HEIGHT],
|
||||||
|
style: { right: '-5px', top: '-5px', cursor: 'nesw-resize' }
|
||||||
|
},
|
||||||
|
{
|
||||||
|
position: '右侧中间',
|
||||||
|
types: [CONTROL_TYPE_ENUM.WIDTH],
|
||||||
|
style: { right: '-5px', top: '50%', cursor: 'e-resize', transform: 'translateX(-50%)' }
|
||||||
|
},
|
||||||
|
{
|
||||||
|
position: '右下角',
|
||||||
|
types: [CONTROL_TYPE_ENUM.WIDTH, CONTROL_TYPE_ENUM.HEIGHT],
|
||||||
|
style: { right: '-5px', bottom: '-5px', cursor: 'nwse-resize' }
|
||||||
|
},
|
||||||
|
{
|
||||||
|
position: '下方中间',
|
||||||
|
types: [CONTROL_TYPE_ENUM.HEIGHT],
|
||||||
|
style: { left: '50%', bottom: '-5px', cursor: 's-resize', transform: 'translateX(-50%)' }
|
||||||
|
},
|
||||||
|
{
|
||||||
|
position: '左下角',
|
||||||
|
types: [CONTROL_TYPE_ENUM.LEFT, CONTROL_TYPE_ENUM.WIDTH, CONTROL_TYPE_ENUM.HEIGHT],
|
||||||
|
style: { left: '-5px', bottom: '-5px', cursor: 'nesw-resize' }
|
||||||
|
},
|
||||||
|
{
|
||||||
|
position: '左侧中间',
|
||||||
|
types: [CONTROL_TYPE_ENUM.LEFT, CONTROL_TYPE_ENUM.WIDTH],
|
||||||
|
style: { left: '-5px', top: '50%', cursor: 'w-resize', transform: 'translateX(-50%)' }
|
||||||
|
}
|
||||||
|
] as ControlDot[]
|
||||||
|
|
||||||
|
//region 热区的缩放
|
||||||
|
// 热区的缩放比例
|
||||||
|
export const HOT_ZONE_SCALE_RATE = 2
|
||||||
|
// 缩小:缩回适合手机屏幕的大小
|
||||||
|
export const zoomOut = (list?: HotZoneItemProperty[]) => {
|
||||||
|
return (
|
||||||
|
list?.map((hotZone) => ({
|
||||||
|
...hotZone,
|
||||||
|
left: (hotZone.left /= HOT_ZONE_SCALE_RATE),
|
||||||
|
top: (hotZone.top /= HOT_ZONE_SCALE_RATE),
|
||||||
|
width: (hotZone.width /= HOT_ZONE_SCALE_RATE),
|
||||||
|
height: (hotZone.height /= HOT_ZONE_SCALE_RATE)
|
||||||
|
})) || []
|
||||||
|
)
|
||||||
|
}
|
||||||
|
// 放大:作用是为了方便在电脑屏幕上编辑
|
||||||
|
export const zoomIn = (list?: HotZoneItemProperty[]) => {
|
||||||
|
return (
|
||||||
|
list?.map((hotZone) => ({
|
||||||
|
...hotZone,
|
||||||
|
left: (hotZone.left *= HOT_ZONE_SCALE_RATE),
|
||||||
|
top: (hotZone.top *= HOT_ZONE_SCALE_RATE),
|
||||||
|
width: (hotZone.width *= HOT_ZONE_SCALE_RATE),
|
||||||
|
height: (hotZone.height *= HOT_ZONE_SCALE_RATE)
|
||||||
|
})) || []
|
||||||
|
)
|
||||||
|
}
|
||||||
|
//endregion
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 封装热区拖拽
|
||||||
|
*
|
||||||
|
* 注:为什么不使用vueuse的useDraggable。在本场景下,其使用方式比较复杂
|
||||||
|
* @param hotZone 热区
|
||||||
|
* @param downEvent 鼠标按下事件
|
||||||
|
* @param callback 回调函数
|
||||||
|
*/
|
||||||
|
export const useDraggable = (
|
||||||
|
hotZone: HotZoneItemProperty,
|
||||||
|
downEvent: MouseEvent,
|
||||||
|
callback: (
|
||||||
|
left: number,
|
||||||
|
top: number,
|
||||||
|
width: number,
|
||||||
|
height: number,
|
||||||
|
moveWidth: number,
|
||||||
|
moveHeight: number
|
||||||
|
) => void
|
||||||
|
) => {
|
||||||
|
// 阻止事件冒泡
|
||||||
|
downEvent.stopPropagation()
|
||||||
|
|
||||||
|
// 移动前的鼠标坐标
|
||||||
|
const { clientX: startX, clientY: startY } = downEvent
|
||||||
|
// 移动前的热区坐标、大小
|
||||||
|
const { left, top, width, height } = hotZone
|
||||||
|
|
||||||
|
// 监听鼠标移动
|
||||||
|
document.onmousemove = (e) => {
|
||||||
|
// 移动宽度
|
||||||
|
const moveWidth = e.clientX - startX
|
||||||
|
// 移动高度
|
||||||
|
const moveHeight = e.clientY - startY
|
||||||
|
// 移动回调
|
||||||
|
callback(left, top, width, height, moveWidth, moveHeight)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 松开鼠标后,结束拖拽
|
||||||
|
document.onmouseup = () => {
|
||||||
|
document.onmousemove = null
|
||||||
|
document.onmouseup = null
|
||||||
|
}
|
||||||
|
}
|
||||||
+236
@@ -0,0 +1,236 @@
|
|||||||
|
<template>
|
||||||
|
<Dialog v-model="dialogVisible" title="设置热区" width="780" @close="handleClose">
|
||||||
|
<div ref="container" class="relative h-full w-750px">
|
||||||
|
<el-image :src="imgUrl" class="pointer-events-none h-full w-750px select-none" />
|
||||||
|
<div
|
||||||
|
v-for="(item, hotZoneIndex) in formData"
|
||||||
|
:key="hotZoneIndex"
|
||||||
|
class="hot-zone"
|
||||||
|
:style="{
|
||||||
|
width: `${item.width}px`,
|
||||||
|
height: `${item.height}px`,
|
||||||
|
top: `${item.top}px`,
|
||||||
|
left: `${item.left}px`
|
||||||
|
}"
|
||||||
|
@mousedown="handleMove(item, $event)"
|
||||||
|
@dblclick="handleShowAppLinkDialog(item)"
|
||||||
|
>
|
||||||
|
<span class="pointer-events-none select-none">{{ item.name || '双击选择链接' }}</span>
|
||||||
|
<Icon icon="ep:close" class="delete" :size="14" @click="handleRemove(item)" />
|
||||||
|
|
||||||
|
<!-- 8个控制点 -->
|
||||||
|
<span
|
||||||
|
class="ctrl-dot"
|
||||||
|
v-for="(dot, dotIndex) in CONTROL_DOT_LIST"
|
||||||
|
:key="dotIndex"
|
||||||
|
:style="dot.style"
|
||||||
|
@mousedown="handleResize(item, dot, $event)"
|
||||||
|
></span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<template #footer>
|
||||||
|
<el-button @click="handleAdd" type="primary" plain>
|
||||||
|
<Icon icon="ep:plus" class="mr-5px" />
|
||||||
|
添加热区
|
||||||
|
</el-button>
|
||||||
|
<el-button @click="handleSubmit" type="primary" plain>
|
||||||
|
<Icon icon="ep:check" class="mr-5px" />
|
||||||
|
确定
|
||||||
|
</el-button>
|
||||||
|
</template>
|
||||||
|
</Dialog>
|
||||||
|
<AppLinkSelectDialog ref="appLinkDialogRef" @app-link-change="handleAppLinkChange" />
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { HotZoneItemProperty } from '@/components/DiyEditor/components/mobile/HotZone/config'
|
||||||
|
import { array, string } from 'vue-types'
|
||||||
|
import {
|
||||||
|
CONTROL_DOT_LIST,
|
||||||
|
CONTROL_TYPE_ENUM,
|
||||||
|
ControlDot,
|
||||||
|
HOT_ZONE_MIN_SIZE,
|
||||||
|
useDraggable,
|
||||||
|
zoomIn,
|
||||||
|
zoomOut
|
||||||
|
} from './controller'
|
||||||
|
import { AppLink } from '@/components/AppLinkInput/data'
|
||||||
|
import { remove } from 'lodash-es'
|
||||||
|
|
||||||
|
/** 热区编辑对话框 */
|
||||||
|
defineOptions({ name: 'HotZoneEditDialog' })
|
||||||
|
|
||||||
|
// 定义属性
|
||||||
|
const props = defineProps({
|
||||||
|
modelValue: array<HotZoneItemProperty>(),
|
||||||
|
imgUrl: string().def('')
|
||||||
|
})
|
||||||
|
const emit = defineEmits(['update:modelValue'])
|
||||||
|
const formData = ref<HotZoneItemProperty[]>([])
|
||||||
|
|
||||||
|
// 弹窗的是否显示
|
||||||
|
const dialogVisible = ref(false)
|
||||||
|
// 打开弹窗
|
||||||
|
const open = () => {
|
||||||
|
// 放大
|
||||||
|
formData.value = zoomIn(props.modelValue)
|
||||||
|
dialogVisible.value = true
|
||||||
|
}
|
||||||
|
// 提供 open 方法,用于打开弹窗
|
||||||
|
defineExpose({ open })
|
||||||
|
|
||||||
|
// 热区容器
|
||||||
|
const container = ref<HTMLDivElement>()
|
||||||
|
|
||||||
|
// 增加热区
|
||||||
|
const handleAdd = () => {
|
||||||
|
formData.value.push({
|
||||||
|
width: HOT_ZONE_MIN_SIZE,
|
||||||
|
height: HOT_ZONE_MIN_SIZE,
|
||||||
|
top: 0,
|
||||||
|
left: 0
|
||||||
|
} as HotZoneItemProperty)
|
||||||
|
}
|
||||||
|
// 删除热区
|
||||||
|
const handleRemove = (hotZone: HotZoneItemProperty) => {
|
||||||
|
remove(formData.value, hotZone)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 移动热区
|
||||||
|
const handleMove = (item: HotZoneItemProperty, e: MouseEvent) => {
|
||||||
|
useDraggable(item, e, (left, top, _, __, moveWidth, moveHeight) => {
|
||||||
|
setLeft(item, left + moveWidth)
|
||||||
|
setTop(item, top + moveHeight)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 调整热区大小、位置
|
||||||
|
const handleResize = (item: HotZoneItemProperty, ctrlDot: ControlDot, e: MouseEvent) => {
|
||||||
|
useDraggable(item, e, (left, top, width, height, moveWidth, moveHeight) => {
|
||||||
|
ctrlDot.types.forEach((type) => {
|
||||||
|
switch (type) {
|
||||||
|
case CONTROL_TYPE_ENUM.LEFT:
|
||||||
|
setLeft(item, left + moveWidth)
|
||||||
|
break
|
||||||
|
case CONTROL_TYPE_ENUM.TOP:
|
||||||
|
setTop(item, top + moveHeight)
|
||||||
|
break
|
||||||
|
case CONTROL_TYPE_ENUM.WIDTH:
|
||||||
|
{
|
||||||
|
// 上移时,高度为减少
|
||||||
|
const direction = ctrlDot.types.includes(CONTROL_TYPE_ENUM.LEFT) ? -1 : 1
|
||||||
|
setWidth(item, width + moveWidth * direction)
|
||||||
|
}
|
||||||
|
break
|
||||||
|
case CONTROL_TYPE_ENUM.HEIGHT:
|
||||||
|
{
|
||||||
|
// 左移时,宽度为减少
|
||||||
|
const direction = ctrlDot.types.includes(CONTROL_TYPE_ENUM.TOP) ? -1 : 1
|
||||||
|
setHeight(item, height + moveHeight * direction)
|
||||||
|
}
|
||||||
|
break
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 设置X轴坐标
|
||||||
|
const setLeft = (item: HotZoneItemProperty, left: number) => {
|
||||||
|
// 不能超出容器
|
||||||
|
if (left >= 0 && left <= container.value!.offsetWidth - item.width) {
|
||||||
|
item.left = left
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 设置Y轴坐标
|
||||||
|
const setTop = (item: HotZoneItemProperty, top: number) => {
|
||||||
|
// 不能超出容器
|
||||||
|
if (top >= 0 && top <= container.value!.offsetHeight - item.height) {
|
||||||
|
item.top = top
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 设置宽度
|
||||||
|
const setWidth = (item: HotZoneItemProperty, width: number) => {
|
||||||
|
// 不能小于最小宽度 && 不能超出容器右边
|
||||||
|
if (width >= HOT_ZONE_MIN_SIZE && item.left + width <= container.value!.offsetWidth) {
|
||||||
|
item.width = width
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 设置高度
|
||||||
|
const setHeight = (item: HotZoneItemProperty, height: number) => {
|
||||||
|
// 不能小于最小高度 && 不能超出容器底部
|
||||||
|
if (height >= HOT_ZONE_MIN_SIZE && item.top + height <= container.value!.offsetHeight) {
|
||||||
|
item.height = height
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 处理对话框关闭
|
||||||
|
const handleSubmit = () => {
|
||||||
|
// 会自动触发handleClose
|
||||||
|
dialogVisible.value = false
|
||||||
|
}
|
||||||
|
|
||||||
|
// 处理对话框关闭
|
||||||
|
const handleClose = () => {
|
||||||
|
// 缩小
|
||||||
|
const list = zoomOut(formData.value)
|
||||||
|
emit('update:modelValue', list)
|
||||||
|
}
|
||||||
|
|
||||||
|
const activeHotZone = ref<HotZoneItemProperty>()
|
||||||
|
const appLinkDialogRef = ref()
|
||||||
|
const handleShowAppLinkDialog = (hotZone: HotZoneItemProperty) => {
|
||||||
|
activeHotZone.value = hotZone
|
||||||
|
appLinkDialogRef.value.open(hotZone.url)
|
||||||
|
}
|
||||||
|
const handleAppLinkChange = (appLink: AppLink) => {
|
||||||
|
if (!appLink || !activeHotZone.value) return
|
||||||
|
activeHotZone.value.name = appLink.name
|
||||||
|
activeHotZone.value.url = appLink.path
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped lang="scss">
|
||||||
|
.hot-zone {
|
||||||
|
position: absolute;
|
||||||
|
background: var(--el-color-primary-light-7);
|
||||||
|
opacity: 0.8;
|
||||||
|
border: 1px solid var(--el-color-primary);
|
||||||
|
color: var(--el-color-primary);
|
||||||
|
font-size: 16px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
cursor: move;
|
||||||
|
z-index: 10;
|
||||||
|
|
||||||
|
/* 控制点 */
|
||||||
|
.ctrl-dot {
|
||||||
|
position: absolute;
|
||||||
|
width: 8px;
|
||||||
|
height: 8px;
|
||||||
|
border-radius: 50%;
|
||||||
|
border: inherit;
|
||||||
|
background-color: #fff;
|
||||||
|
z-index: 11;
|
||||||
|
}
|
||||||
|
|
||||||
|
.delete {
|
||||||
|
display: none;
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
right: 0;
|
||||||
|
padding: 2px 2px 6px 6px;
|
||||||
|
background-color: var(--el-color-primary);
|
||||||
|
border-radius: 0 0 0 80%;
|
||||||
|
cursor: pointer;
|
||||||
|
color: #fff;
|
||||||
|
text-align: right;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
.delete {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -0,0 +1,43 @@
|
|||||||
|
import { ComponentStyle, DiyComponent } from '@/components/DiyEditor/util'
|
||||||
|
|
||||||
|
/** 热区属性 */
|
||||||
|
export interface HotZoneProperty {
|
||||||
|
// 图片地址
|
||||||
|
imgUrl: string
|
||||||
|
// 导航菜单列表
|
||||||
|
list: HotZoneItemProperty[]
|
||||||
|
// 组件样式
|
||||||
|
style: ComponentStyle
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 热区项目属性 */
|
||||||
|
export interface HotZoneItemProperty {
|
||||||
|
// 链接的名称
|
||||||
|
name: string
|
||||||
|
// 链接
|
||||||
|
url: string
|
||||||
|
// 宽
|
||||||
|
width: number
|
||||||
|
// 高
|
||||||
|
height: number
|
||||||
|
// 上
|
||||||
|
top: number
|
||||||
|
// 左
|
||||||
|
left: number
|
||||||
|
}
|
||||||
|
|
||||||
|
// 定义组件
|
||||||
|
export const component = {
|
||||||
|
id: 'HotZone',
|
||||||
|
name: '热区',
|
||||||
|
icon: 'tabler:hand-click',
|
||||||
|
property: {
|
||||||
|
imgUrl: '',
|
||||||
|
list: [] as HotZoneItemProperty[],
|
||||||
|
style: {
|
||||||
|
bgType: 'color',
|
||||||
|
bgColor: '#fff',
|
||||||
|
marginBottom: 8
|
||||||
|
} as ComponentStyle
|
||||||
|
}
|
||||||
|
} as DiyComponent<HotZoneProperty>
|
||||||
@@ -0,0 +1,42 @@
|
|||||||
|
<template>
|
||||||
|
<div class="relative h-full min-h-30px w-full">
|
||||||
|
<el-image :src="property.imgUrl" class="pointer-events-none h-full w-full select-none" />
|
||||||
|
<div
|
||||||
|
v-for="(item, index) in property.list"
|
||||||
|
:key="index"
|
||||||
|
class="hot-zone"
|
||||||
|
:style="{
|
||||||
|
width: `${item.width}px`,
|
||||||
|
height: `${item.height}px`,
|
||||||
|
top: `${item.top}px`,
|
||||||
|
left: `${item.left}px`
|
||||||
|
}"
|
||||||
|
>
|
||||||
|
{{ item.name }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { HotZoneProperty } from './config'
|
||||||
|
|
||||||
|
/** 热区 */
|
||||||
|
defineOptions({ name: 'HotZone' })
|
||||||
|
const props = defineProps<{ property: HotZoneProperty }>()
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped lang="scss">
|
||||||
|
.hot-zone {
|
||||||
|
position: absolute;
|
||||||
|
background: var(--el-color-primary-light-7);
|
||||||
|
opacity: 0.8;
|
||||||
|
border: 1px solid var(--el-color-primary);
|
||||||
|
color: var(--el-color-primary);
|
||||||
|
font-size: 14px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
cursor: move;
|
||||||
|
z-index: 10;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -0,0 +1,63 @@
|
|||||||
|
<template>
|
||||||
|
<ComponentContainerProperty v-model="formData.style">
|
||||||
|
<!-- 表单 -->
|
||||||
|
<el-form label-width="80px" :model="formData" class="m-t-8px">
|
||||||
|
<el-form-item label="上传图片" prop="imgUrl">
|
||||||
|
<UploadImg v-model="formData.imgUrl" height="50px" width="auto" class="min-w-80px">
|
||||||
|
<template #tip>
|
||||||
|
<el-text type="info" size="small"> 推荐宽度 750</el-text>
|
||||||
|
</template>
|
||||||
|
</UploadImg>
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
|
||||||
|
<el-button type="primary" plain class="w-full" @click="handleOpenEditDialog">
|
||||||
|
设置热区
|
||||||
|
</el-button>
|
||||||
|
</ComponentContainerProperty>
|
||||||
|
<!-- 热区编辑对话框 -->
|
||||||
|
<HotZoneEditDialog ref="editDialogRef" v-model="formData.list" :img-url="formData.imgUrl" />
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { usePropertyForm } from '@/components/DiyEditor/util'
|
||||||
|
import { HotZoneProperty } from '@/components/DiyEditor/components/mobile/HotZone/config'
|
||||||
|
import HotZoneEditDialog from './components/HotZoneEditDialog/index.vue'
|
||||||
|
|
||||||
|
/** 热区属性面板 */
|
||||||
|
defineOptions({ name: 'HotZoneProperty' })
|
||||||
|
|
||||||
|
const props = defineProps<{ modelValue: HotZoneProperty }>()
|
||||||
|
const emit = defineEmits(['update:modelValue'])
|
||||||
|
const { formData } = usePropertyForm(props.modelValue, emit)
|
||||||
|
|
||||||
|
// 热区编辑对话框
|
||||||
|
const editDialogRef = ref()
|
||||||
|
// 打开热区编辑对话框
|
||||||
|
const handleOpenEditDialog = () => {
|
||||||
|
editDialogRef.value.open()
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped lang="scss">
|
||||||
|
.hot-zone {
|
||||||
|
position: absolute;
|
||||||
|
background: #409effbf;
|
||||||
|
border: 1px solid var(--el-color-primary);
|
||||||
|
color: #fff;
|
||||||
|
font-size: 12px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
cursor: move;
|
||||||
|
|
||||||
|
/* 控制点 */
|
||||||
|
.ctrl-dot {
|
||||||
|
position: absolute;
|
||||||
|
width: 4px;
|
||||||
|
height: 4px;
|
||||||
|
border-radius: 50%;
|
||||||
|
background-color: #fff;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -13,7 +13,7 @@
|
|||||||
</UploadImg>
|
</UploadImg>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item label="链接" prop="url">
|
<el-form-item label="链接" prop="url">
|
||||||
<el-input placeholder="链接" v-model="formData.url" />
|
<AppLinkInput v-model="formData.url" />
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</el-form>
|
</el-form>
|
||||||
</ComponentContainerProperty>
|
</ComponentContainerProperty>
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ export interface MagicCubeProperty {
|
|||||||
// 组件样式
|
// 组件样式
|
||||||
style: ComponentStyle
|
style: ComponentStyle
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 广告魔方项目属性 */
|
/** 广告魔方项目属性 */
|
||||||
export interface MagicCubeItemProperty {
|
export interface MagicCubeItemProperty {
|
||||||
// 图标链接
|
// 图标链接
|
||||||
|
|||||||
@@ -17,7 +17,7 @@
|
|||||||
<UploadImg v-model="hotArea.imgUrl" height="80px" width="80px" />
|
<UploadImg v-model="hotArea.imgUrl" height="80px" width="80px" />
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item label="链接" :prop="`list[${index}].url`">
|
<el-form-item label="链接" :prop="`list[${index}].url`">
|
||||||
<el-input v-model="hotArea.url" placeholder="请输入链接" />
|
<AppLinkInput v-model="hotArea.url" />
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</template>
|
</template>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ export interface MenuGridProperty {
|
|||||||
// 组件样式
|
// 组件样式
|
||||||
style: ComponentStyle
|
style: ComponentStyle
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 宫格导航项目属性 */
|
/** 宫格导航项目属性 */
|
||||||
export interface MenuGridItemProperty {
|
export interface MenuGridItemProperty {
|
||||||
// 图标链接
|
// 图标链接
|
||||||
|
|||||||
@@ -9,72 +9,50 @@
|
|||||||
</el-radio-group>
|
</el-radio-group>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
|
|
||||||
<el-text tag="p"> 菜单设置 </el-text>
|
<el-card header="菜单设置" class="property-group" shadow="never">
|
||||||
<el-text type="info" size="small"> 拖动左侧的小圆点可以调整顺序 </el-text>
|
<Draggable v-model="formData.list" :empty-item="EMPTY_MENU_GRID_ITEM_PROPERTY">
|
||||||
<template v-if="formData.list.length">
|
<template #default="{ element }">
|
||||||
<VueDraggable
|
<el-form-item label="图标" prop="iconUrl">
|
||||||
class="m-t-8px"
|
<UploadImg v-model="element.iconUrl" height="80px" width="80px">
|
||||||
:list="formData.list"
|
<template #tip> 建议尺寸:44 * 44 </template>
|
||||||
item-key="index"
|
</UploadImg>
|
||||||
handle=".drag-icon"
|
</el-form-item>
|
||||||
:forceFallback="true"
|
<el-form-item label="标题" prop="title">
|
||||||
:animation="200"
|
<InputWithColor v-model="element.title" v-model:color="element.titleColor" />
|
||||||
>
|
</el-form-item>
|
||||||
<template #item="{ element, index }">
|
<el-form-item label="副标题" prop="subtitle">
|
||||||
<div class="mb-4px flex flex-col gap-4px rounded bg-gray-100 p-8px">
|
<InputWithColor v-model="element.subtitle" v-model:color="element.subtitleColor" />
|
||||||
<div class="flex flex-row justify-between">
|
</el-form-item>
|
||||||
<Icon icon="ic:round-drag-indicator" class="drag-icon cursor-move" />
|
<el-form-item label="链接" prop="url">
|
||||||
<Icon icon="ep:delete" class="text-red-500" @click="handleDeleteMenu(index)" />
|
<AppLinkInput v-model="element.url" />
|
||||||
</div>
|
</el-form-item>
|
||||||
<el-form-item label="图标" prop="iconUrl">
|
<el-form-item label="显示角标" prop="badge.show">
|
||||||
<UploadImg v-model="element.iconUrl" height="80px" width="80px">
|
<el-switch v-model="element.badge.show" />
|
||||||
<template #tip> 建议尺寸:44 * 44 </template>
|
</el-form-item>
|
||||||
</UploadImg>
|
<template v-if="element.badge.show">
|
||||||
|
<el-form-item label="角标内容" prop="badge.text">
|
||||||
|
<InputWithColor
|
||||||
|
v-model="element.badge.text"
|
||||||
|
v-model:color="element.badge.textColor"
|
||||||
|
/>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item label="标题" prop="title">
|
<el-form-item label="背景颜色" prop="badge.bgColor">
|
||||||
<InputWithColor v-model="element.title" v-model:color="element.titleColor" />
|
<ColorInput v-model="element.badge.bgColor" />
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item label="副标题" prop="subtitle">
|
</template>
|
||||||
<InputWithColor v-model="element.subtitle" v-model:color="element.subtitleColor" />
|
|
||||||
</el-form-item>
|
|
||||||
<el-form-item label="链接" prop="url">
|
|
||||||
<el-input v-model="element.url" />
|
|
||||||
</el-form-item>
|
|
||||||
<el-form-item label="显示角标" prop="badge.show">
|
|
||||||
<el-switch v-model="element.badge.show" />
|
|
||||||
</el-form-item>
|
|
||||||
<template v-if="element.badge.show">
|
|
||||||
<el-form-item label="角标内容" prop="badge.text">
|
|
||||||
<InputWithColor
|
|
||||||
v-model="element.badge.text"
|
|
||||||
v-model:color="element.badge.textColor"
|
|
||||||
/>
|
|
||||||
</el-form-item>
|
|
||||||
<el-form-item label="背景颜色" prop="badge.bgColor">
|
|
||||||
<ColorInput v-model="element.badge.bgColor" />
|
|
||||||
</el-form-item>
|
|
||||||
</template>
|
|
||||||
</div>
|
|
||||||
</template>
|
</template>
|
||||||
</VueDraggable>
|
</Draggable>
|
||||||
</template>
|
</el-card>
|
||||||
<el-form-item label-width="0">
|
|
||||||
<el-button @click="handleAddMenu" type="primary" plain class="m-t-8px w-full">
|
|
||||||
<Icon icon="ep:plus" class="mr-5px" /> 添加菜单
|
|
||||||
</el-button>
|
|
||||||
</el-form-item>
|
|
||||||
</el-form>
|
</el-form>
|
||||||
</ComponentContainerProperty>
|
</ComponentContainerProperty>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import VueDraggable from 'vuedraggable'
|
|
||||||
import { usePropertyForm } from '@/components/DiyEditor/util'
|
import { usePropertyForm } from '@/components/DiyEditor/util'
|
||||||
import {
|
import {
|
||||||
EMPTY_MENU_GRID_ITEM_PROPERTY,
|
EMPTY_MENU_GRID_ITEM_PROPERTY,
|
||||||
MenuGridProperty
|
MenuGridProperty
|
||||||
} from '@/components/DiyEditor/components/mobile/MenuGrid/config'
|
} from '@/components/DiyEditor/components/mobile/MenuGrid/config'
|
||||||
import { cloneDeep } from 'lodash-es'
|
|
||||||
|
|
||||||
/** 宫格导航属性面板 */
|
/** 宫格导航属性面板 */
|
||||||
defineOptions({ name: 'MenuGridProperty' })
|
defineOptions({ name: 'MenuGridProperty' })
|
||||||
@@ -82,15 +60,6 @@ defineOptions({ name: 'MenuGridProperty' })
|
|||||||
const props = defineProps<{ modelValue: MenuGridProperty }>()
|
const props = defineProps<{ modelValue: MenuGridProperty }>()
|
||||||
const emit = defineEmits(['update:modelValue'])
|
const emit = defineEmits(['update:modelValue'])
|
||||||
const { formData } = usePropertyForm(props.modelValue, emit)
|
const { formData } = usePropertyForm(props.modelValue, emit)
|
||||||
|
|
||||||
/* 添加菜单 */
|
|
||||||
const handleAddMenu = () => {
|
|
||||||
formData.value.list.push(cloneDeep(EMPTY_MENU_GRID_ITEM_PROPERTY))
|
|
||||||
}
|
|
||||||
/* 删除菜单 */
|
|
||||||
const handleDeleteMenu = (index: number) => {
|
|
||||||
formData.value.list.splice(index, 1)
|
|
||||||
}
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped lang="scss"></style>
|
<style scoped lang="scss"></style>
|
||||||
@@ -8,6 +8,7 @@ export interface MenuListProperty {
|
|||||||
// 组件样式
|
// 组件样式
|
||||||
style: ComponentStyle
|
style: ComponentStyle
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 列表导航项目属性 */
|
/** 列表导航项目属性 */
|
||||||
export interface MenuListItemProperty {
|
export interface MenuListItemProperty {
|
||||||
// 图标链接
|
// 图标链接
|
||||||
|
|||||||
@@ -5,55 +5,34 @@
|
|||||||
|
|
||||||
<!-- 表单 -->
|
<!-- 表单 -->
|
||||||
<el-form label-width="60px" :model="formData" class="m-t-8px">
|
<el-form label-width="60px" :model="formData" class="m-t-8px">
|
||||||
<div v-if="formData.list.length">
|
<Draggable v-model="formData.list" :empty-item="EMPTY_MENU_LIST_ITEM_PROPERTY">
|
||||||
<VueDraggable
|
<template #default="{ element }">
|
||||||
:list="formData.list"
|
<el-form-item label="图标" prop="iconUrl">
|
||||||
item-key="index"
|
<UploadImg v-model="element.iconUrl" height="80px" width="80px">
|
||||||
handle=".drag-icon"
|
<template #tip> 建议尺寸:44 * 44 </template>
|
||||||
:forceFallback="true"
|
</UploadImg>
|
||||||
:animation="200"
|
</el-form-item>
|
||||||
>
|
<el-form-item label="标题" prop="title">
|
||||||
<template #item="{ element, index }">
|
<InputWithColor v-model="element.title" v-model:color="element.titleColor" />
|
||||||
<div class="mb-4px flex flex-col gap-4px rounded bg-gray-100 p-8px">
|
</el-form-item>
|
||||||
<div class="flex flex-row justify-between">
|
<el-form-item label="副标题" prop="subtitle">
|
||||||
<Icon icon="ic:round-drag-indicator" class="drag-icon cursor-move" />
|
<InputWithColor v-model="element.subtitle" v-model:color="element.subtitleColor" />
|
||||||
<Icon icon="ep:delete" class="text-red-500" @click="handleDeleteMenu(index)" />
|
</el-form-item>
|
||||||
</div>
|
<el-form-item label="链接" prop="url">
|
||||||
<el-form-item label="图标" prop="iconUrl">
|
<AppLinkInput v-model="element.url" />
|
||||||
<UploadImg v-model="element.iconUrl" height="80px" width="80px">
|
</el-form-item>
|
||||||
<template #tip> 建议尺寸:44 * 44 </template>
|
</template>
|
||||||
</UploadImg>
|
</Draggable>
|
||||||
</el-form-item>
|
|
||||||
<el-form-item label="标题" prop="title">
|
|
||||||
<InputWithColor v-model="element.title" v-model:color="element.titleColor" />
|
|
||||||
</el-form-item>
|
|
||||||
<el-form-item label="副标题" prop="subtitle">
|
|
||||||
<InputWithColor v-model="element.subtitle" v-model:color="element.subtitleColor" />
|
|
||||||
</el-form-item>
|
|
||||||
<el-form-item label="链接" prop="url">
|
|
||||||
<el-input v-model="element.url" />
|
|
||||||
</el-form-item>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
</VueDraggable>
|
|
||||||
</div>
|
|
||||||
<el-form-item label-width="0">
|
|
||||||
<el-button @click="handleAddMenu" type="primary" plain class="m-t-8px w-full">
|
|
||||||
<Icon icon="ep:plus" class="mr-5px" /> 添加菜单
|
|
||||||
</el-button>
|
|
||||||
</el-form-item>
|
|
||||||
</el-form>
|
</el-form>
|
||||||
</ComponentContainerProperty>
|
</ComponentContainerProperty>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import VueDraggable from 'vuedraggable'
|
|
||||||
import { usePropertyForm } from '@/components/DiyEditor/util'
|
import { usePropertyForm } from '@/components/DiyEditor/util'
|
||||||
import {
|
import {
|
||||||
EMPTY_MENU_LIST_ITEM_PROPERTY,
|
EMPTY_MENU_LIST_ITEM_PROPERTY,
|
||||||
MenuListProperty
|
MenuListProperty
|
||||||
} from '@/components/DiyEditor/components/mobile/MenuList/config'
|
} from '@/components/DiyEditor/components/mobile/MenuList/config'
|
||||||
import { cloneDeep } from 'lodash-es'
|
|
||||||
|
|
||||||
/** 列表导航属性面板 */
|
/** 列表导航属性面板 */
|
||||||
defineOptions({ name: 'MenuListProperty' })
|
defineOptions({ name: 'MenuListProperty' })
|
||||||
@@ -61,15 +40,6 @@ defineOptions({ name: 'MenuListProperty' })
|
|||||||
const props = defineProps<{ modelValue: MenuListProperty }>()
|
const props = defineProps<{ modelValue: MenuListProperty }>()
|
||||||
const emit = defineEmits(['update:modelValue'])
|
const emit = defineEmits(['update:modelValue'])
|
||||||
const { formData } = usePropertyForm(props.modelValue, emit)
|
const { formData } = usePropertyForm(props.modelValue, emit)
|
||||||
|
|
||||||
/* 添加菜单 */
|
|
||||||
const handleAddMenu = () => {
|
|
||||||
formData.value.list.push(cloneDeep(EMPTY_MENU_LIST_ITEM_PROPERTY))
|
|
||||||
}
|
|
||||||
/* 删除菜单 */
|
|
||||||
const handleDeleteMenu = (index: number) => {
|
|
||||||
formData.value.list.splice(index, 1)
|
|
||||||
}
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped lang="scss"></style>
|
<style scoped lang="scss"></style>
|
||||||
@@ -28,7 +28,7 @@
|
|||||||
<!-- 标题 -->
|
<!-- 标题 -->
|
||||||
<span
|
<span
|
||||||
v-if="property.layout === 'iconText'"
|
v-if="property.layout === 'iconText'"
|
||||||
class="text-14px"
|
class="text-12px"
|
||||||
:style="{
|
:style="{
|
||||||
color: item.titleColor,
|
color: item.titleColor,
|
||||||
height: `${TITLE_HEIGHT}px`,
|
height: `${TITLE_HEIGHT}px`,
|
||||||
@@ -51,7 +51,7 @@ const props = defineProps<{ property: MenuSwiperProperty }>()
|
|||||||
// 标题的高度
|
// 标题的高度
|
||||||
const TITLE_HEIGHT = 20
|
const TITLE_HEIGHT = 20
|
||||||
// 图标的高度
|
// 图标的高度
|
||||||
const ICON_SIZE = 50
|
const ICON_SIZE = 42
|
||||||
// 垂直间距:一行上下的间距
|
// 垂直间距:一行上下的间距
|
||||||
const SPACE_Y = 16
|
const SPACE_Y = 16
|
||||||
|
|
||||||
|
|||||||
@@ -22,63 +22,42 @@
|
|||||||
</el-radio-group>
|
</el-radio-group>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
|
|
||||||
<el-text tag="p"> 菜单设置 </el-text>
|
<el-card header="菜单设置" class="property-group" shadow="never">
|
||||||
<el-text type="info" size="small"> 拖动左侧的小圆点可以调整顺序 </el-text>
|
<Draggable v-model="formData.list" :empty-item="cloneDeep(EMPTY_MENU_SWIPER_ITEM_PROPERTY)">
|
||||||
<template v-if="formData.list.length">
|
<template #default="{ element }">
|
||||||
<VueDraggable
|
<el-form-item label="图标" prop="iconUrl">
|
||||||
class="m-t-8px"
|
<UploadImg v-model="element.iconUrl" height="80px" width="80px">
|
||||||
:list="formData.list"
|
<template #tip> 建议尺寸:98 * 98 </template>
|
||||||
item-key="index"
|
</UploadImg>
|
||||||
handle=".drag-icon"
|
</el-form-item>
|
||||||
:forceFallback="true"
|
<el-form-item label="标题" prop="title">
|
||||||
:animation="200"
|
<InputWithColor v-model="element.title" v-model:color="element.titleColor" />
|
||||||
>
|
</el-form-item>
|
||||||
<template #item="{ element, index }">
|
<el-form-item label="链接" prop="url">
|
||||||
<div class="mb-4px flex flex-col gap-4px rounded bg-gray-100 p-8px">
|
<AppLinkInput v-model="element.url" />
|
||||||
<div class="flex flex-row justify-between">
|
</el-form-item>
|
||||||
<Icon icon="ic:round-drag-indicator" class="drag-icon cursor-move" />
|
<el-form-item label="显示角标" prop="badge.show">
|
||||||
<Icon icon="ep:delete" class="text-red-500" @click="handleDeleteMenu(index)" />
|
<el-switch v-model="element.badge.show" />
|
||||||
</div>
|
</el-form-item>
|
||||||
<el-form-item label="图标" prop="iconUrl">
|
<template v-if="element.badge.show">
|
||||||
<UploadImg v-model="element.iconUrl" height="80px" width="80px">
|
<el-form-item label="角标内容" prop="badge.text">
|
||||||
<template #tip> 建议尺寸:98 * 98 </template>
|
<InputWithColor
|
||||||
</UploadImg>
|
v-model="element.badge.text"
|
||||||
|
v-model:color="element.badge.textColor"
|
||||||
|
/>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item label="标题" prop="title">
|
<el-form-item label="背景颜色" prop="badge.bgColor">
|
||||||
<InputWithColor v-model="element.title" v-model:color="element.titleColor" />
|
<ColorInput v-model="element.badge.bgColor" />
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item label="链接" prop="url">
|
</template>
|
||||||
<el-input v-model="element.url" />
|
|
||||||
</el-form-item>
|
|
||||||
<el-form-item label="显示角标" prop="badge.show">
|
|
||||||
<el-switch v-model="element.badge.show" />
|
|
||||||
</el-form-item>
|
|
||||||
<template v-if="element.badge.show">
|
|
||||||
<el-form-item label="角标内容" prop="badge.text">
|
|
||||||
<InputWithColor
|
|
||||||
v-model="element.badge.text"
|
|
||||||
v-model:color="element.badge.textColor"
|
|
||||||
/>
|
|
||||||
</el-form-item>
|
|
||||||
<el-form-item label="背景颜色" prop="badge.bgColor">
|
|
||||||
<ColorInput v-model="element.badge.bgColor" />
|
|
||||||
</el-form-item>
|
|
||||||
</template>
|
|
||||||
</div>
|
|
||||||
</template>
|
</template>
|
||||||
</VueDraggable>
|
</Draggable>
|
||||||
</template>
|
</el-card>
|
||||||
<el-form-item label-width="0">
|
|
||||||
<el-button @click="handleAddMenu" type="primary" plain class="m-t-8px w-full">
|
|
||||||
<Icon icon="ep:plus" class="mr-5px" /> 添加菜单
|
|
||||||
</el-button>
|
|
||||||
</el-form-item>
|
|
||||||
</el-form>
|
</el-form>
|
||||||
</ComponentContainerProperty>
|
</ComponentContainerProperty>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import VueDraggable from 'vuedraggable'
|
|
||||||
import { usePropertyForm } from '@/components/DiyEditor/util'
|
import { usePropertyForm } from '@/components/DiyEditor/util'
|
||||||
import {
|
import {
|
||||||
EMPTY_MENU_SWIPER_ITEM_PROPERTY,
|
EMPTY_MENU_SWIPER_ITEM_PROPERTY,
|
||||||
@@ -92,15 +71,6 @@ defineOptions({ name: 'MenuSwiperProperty' })
|
|||||||
const props = defineProps<{ modelValue: MenuSwiperProperty }>()
|
const props = defineProps<{ modelValue: MenuSwiperProperty }>()
|
||||||
const emit = defineEmits(['update:modelValue'])
|
const emit = defineEmits(['update:modelValue'])
|
||||||
const { formData } = usePropertyForm(props.modelValue, emit)
|
const { formData } = usePropertyForm(props.modelValue, emit)
|
||||||
|
|
||||||
/* 添加菜单 */
|
|
||||||
const handleAddMenu = () => {
|
|
||||||
formData.value.list.push(cloneDeep(EMPTY_MENU_SWIPER_ITEM_PROPERTY))
|
|
||||||
}
|
|
||||||
/* 删除菜单 */
|
|
||||||
const handleDeleteMenu = (index: number) => {
|
|
||||||
formData.value.list.splice(index, 1)
|
|
||||||
}
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped lang="scss"></style>
|
<style scoped lang="scss"></style>
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
import { DiyComponent } from '@/components/DiyEditor/util'
|
import { ComponentStyle, DiyComponent } from '@/components/DiyEditor/util'
|
||||||
|
|
||||||
/** 公告栏属性 */
|
/** 公告栏属性 */
|
||||||
export interface NoticeBarProperty {
|
export interface NoticeBarProperty {
|
||||||
@@ -10,6 +10,8 @@ export interface NoticeBarProperty {
|
|||||||
backgroundColor: string
|
backgroundColor: string
|
||||||
// 文字颜色
|
// 文字颜色
|
||||||
textColor: string
|
textColor: string
|
||||||
|
// 组件样式
|
||||||
|
style: ComponentStyle
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 内容属性 */
|
/** 内容属性 */
|
||||||
@@ -34,6 +36,11 @@ export const component = {
|
|||||||
}
|
}
|
||||||
],
|
],
|
||||||
backgroundColor: '#fff',
|
backgroundColor: '#fff',
|
||||||
textColor: '#333'
|
textColor: '#333',
|
||||||
|
style: {
|
||||||
|
bgType: 'color',
|
||||||
|
bgColor: '#fff',
|
||||||
|
marginBottom: 8
|
||||||
|
} as ComponentStyle
|
||||||
}
|
}
|
||||||
} as DiyComponent<NoticeBarProperty>
|
} as DiyComponent<NoticeBarProperty>
|
||||||
@@ -1,58 +1,36 @@
|
|||||||
<template>
|
<template>
|
||||||
<el-form label-width="80px" :model="formData" :rules="rules">
|
<ComponentContainerProperty v-model="formData.style">
|
||||||
<el-form-item label="公告图标" prop="iconUrl">
|
<el-form label-width="80px" :model="formData" :rules="rules">
|
||||||
<UploadImg v-model="formData.iconUrl" height="48px">
|
<el-form-item label="公告图标" prop="iconUrl">
|
||||||
<template #tip>建议尺寸:24 * 24</template>
|
<UploadImg v-model="formData.iconUrl" height="48px">
|
||||||
</UploadImg>
|
<template #tip>建议尺寸:24 * 24</template>
|
||||||
</el-form-item>
|
</UploadImg>
|
||||||
<el-form-item label="背景颜色" prop="backgroundColor">
|
</el-form-item>
|
||||||
<ColorInput v-model="formData.backgroundColor" />
|
<el-form-item label="背景颜色" prop="backgroundColor">
|
||||||
</el-form-item>
|
<ColorInput v-model="formData.backgroundColor" />
|
||||||
<el-form-item label="文字颜色" prop="文字颜色">
|
</el-form-item>
|
||||||
<ColorInput v-model="formData.textColor" />
|
<el-form-item label="文字颜色" prop="文字颜色">
|
||||||
</el-form-item>
|
<ColorInput v-model="formData.textColor" />
|
||||||
<el-text tag="p"> 公告内容 </el-text>
|
</el-form-item>
|
||||||
<el-text type="info" size="small"> 拖动左上角的小圆点可以调整热词顺序 </el-text>
|
<el-card header="公告内容" class="property-group" shadow="never">
|
||||||
<template v-if="formData.contents.length">
|
<Draggable v-model="formData.contents">
|
||||||
<VueDraggable
|
<template #default="{ element }">
|
||||||
:list="formData.contents"
|
<el-form-item label="公告" prop="text" label-width="40px">
|
||||||
item-key="index"
|
|
||||||
handle=".drag-icon"
|
|
||||||
:forceFallback="true"
|
|
||||||
:animation="200"
|
|
||||||
class="m-t-8px"
|
|
||||||
>
|
|
||||||
<template #item="{ element, index }">
|
|
||||||
<div class="mb-4px flex flex-row gap-4px rounded bg-gray-100 p-8px">
|
|
||||||
<div class="flex flex-col items-start justify-between">
|
|
||||||
<Icon icon="ic:round-drag-indicator" class="drag-icon cursor-move" />
|
|
||||||
<Icon
|
|
||||||
icon="ep:delete"
|
|
||||||
class="cursor-pointer text-red-5"
|
|
||||||
@click="handleDeleteContent(index)"
|
|
||||||
v-if="formData.contents.length > 1"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div class="w-full flex flex-col gap-8px">
|
|
||||||
<el-input v-model="element.text" placeholder="请输入公告" />
|
<el-input v-model="element.text" placeholder="请输入公告" />
|
||||||
<el-input v-model="element.url" placeholder="请输入链接" />
|
</el-form-item>
|
||||||
</div>
|
<el-form-item label="链接" prop="url" label-width="40px">
|
||||||
</div>
|
<AppLinkInput v-model="element.url" />
|
||||||
</template>
|
</el-form-item>
|
||||||
</VueDraggable>
|
</template>
|
||||||
</template>
|
</Draggable>
|
||||||
<el-form-item label-width="0">
|
</el-card>
|
||||||
<el-button @click="handleAddContent" type="primary" plain class="m-t-8px w-full">
|
</el-form>
|
||||||
添加内容
|
</ComponentContainerProperty>
|
||||||
</el-button>
|
|
||||||
</el-form-item>
|
|
||||||
</el-form>
|
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { NoticeBarProperty, NoticeContentProperty } from './config'
|
import { NoticeBarProperty } from './config'
|
||||||
import { usePropertyForm } from '@/components/DiyEditor/util'
|
import { usePropertyForm } from '@/components/DiyEditor/util'
|
||||||
import VueDraggable from 'vuedraggable'
|
|
||||||
// 通知栏属性面板
|
// 通知栏属性面板
|
||||||
defineOptions({ name: 'NoticeBarProperty' })
|
defineOptions({ name: 'NoticeBarProperty' })
|
||||||
// 表单校验
|
// 表单校验
|
||||||
@@ -63,15 +41,6 @@ const rules = {
|
|||||||
const props = defineProps<{ modelValue: NoticeBarProperty }>()
|
const props = defineProps<{ modelValue: NoticeBarProperty }>()
|
||||||
const emit = defineEmits(['update:modelValue'])
|
const emit = defineEmits(['update:modelValue'])
|
||||||
const { formData } = usePropertyForm(props.modelValue, emit)
|
const { formData } = usePropertyForm(props.modelValue, emit)
|
||||||
|
|
||||||
/* 添加公告 */
|
|
||||||
const handleAddContent = () => {
|
|
||||||
formData.value.contents.push({} as NoticeContentProperty)
|
|
||||||
}
|
|
||||||
/* 删除公告 */
|
|
||||||
const handleDeleteContent = (index: number) => {
|
|
||||||
formData.value.contents.splice(index, 1)
|
|
||||||
}
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped lang="scss"></style>
|
<style scoped lang="scss"></style>
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
import { DiyComponent } from '@/components/DiyEditor/util'
|
||||||
|
|
||||||
|
/** 弹窗广告属性 */
|
||||||
|
export interface PopoverProperty {
|
||||||
|
list: PopoverItemProperty[]
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface PopoverItemProperty {
|
||||||
|
// 图片地址
|
||||||
|
imgUrl: string
|
||||||
|
// 跳转连接
|
||||||
|
url: string
|
||||||
|
// 显示类型:仅显示一次、每次启动都会显示
|
||||||
|
showType: 'once' | 'always'
|
||||||
|
}
|
||||||
|
|
||||||
|
// 定义组件
|
||||||
|
export const component = {
|
||||||
|
id: 'Popover',
|
||||||
|
name: '弹窗广告',
|
||||||
|
icon: 'carbon:popup',
|
||||||
|
position: 'fixed',
|
||||||
|
property: {
|
||||||
|
list: [{ showType: 'once' }]
|
||||||
|
}
|
||||||
|
} as DiyComponent<PopoverProperty>
|
||||||
@@ -0,0 +1,38 @@
|
|||||||
|
<template>
|
||||||
|
<div
|
||||||
|
v-for="(item, index) in property.list"
|
||||||
|
:key="index"
|
||||||
|
class="absolute bottom-50% right-50% h-454px w-292px border-1px border-gray border-rounded-4px border-solid bg-white p-1px"
|
||||||
|
:style="{
|
||||||
|
zIndex: 100 + index + (activeIndex === index ? 100 : 0),
|
||||||
|
marginRight: `${-146 - index * 20}px`,
|
||||||
|
marginBottom: `${-227 - index * 20}px`
|
||||||
|
}"
|
||||||
|
@click="handleActive(index)"
|
||||||
|
>
|
||||||
|
<el-image :src="item.imgUrl" fit="contain" class="h-full w-full">
|
||||||
|
<template #error>
|
||||||
|
<div class="h-full w-full flex items-center justify-center">
|
||||||
|
<Icon icon="ep:picture" />
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</el-image>
|
||||||
|
<div class="absolute right-1 top-1 text-12px">{{ index + 1 }}</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { PopoverProperty } from './config'
|
||||||
|
|
||||||
|
/** 弹窗广告 */
|
||||||
|
defineOptions({ name: 'Popover' })
|
||||||
|
// 定义属性
|
||||||
|
defineProps<{ property: PopoverProperty }>()
|
||||||
|
|
||||||
|
// 处理选中
|
||||||
|
const activeIndex = ref(0)
|
||||||
|
const handleActive = (index: number) => {
|
||||||
|
activeIndex.value = index
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped lang="scss"></style>
|
||||||
@@ -0,0 +1,38 @@
|
|||||||
|
<template>
|
||||||
|
<el-form label-width="80px" :model="formData">
|
||||||
|
<Draggable v-model="formData.list" :empty-item="{ showType: 'once' }">
|
||||||
|
<template #default="{ element, index }">
|
||||||
|
<el-form-item label="图片" :prop="`list[${index}].imgUrl`">
|
||||||
|
<UploadImg v-model="element.imgUrl" height="56px" width="56px" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="跳转链接" :prop="`list[${index}].url`">
|
||||||
|
<AppLinkInput v-model="element.url" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="显示次数" :prop="`list[${index}].showType`">
|
||||||
|
<el-radio-group v-model="element.showType">
|
||||||
|
<el-tooltip content="只显示一次,下次打开时不显示" placement="bottom">
|
||||||
|
<el-radio label="once">一次</el-radio>
|
||||||
|
</el-tooltip>
|
||||||
|
<el-tooltip content="每次打开时都会显示" placement="bottom">
|
||||||
|
<el-radio label="always">不限</el-radio>
|
||||||
|
</el-tooltip>
|
||||||
|
</el-radio-group>
|
||||||
|
</el-form-item>
|
||||||
|
</template>
|
||||||
|
</Draggable>
|
||||||
|
</el-form>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { PopoverProperty } from './config'
|
||||||
|
import { usePropertyForm } from '@/components/DiyEditor/util'
|
||||||
|
|
||||||
|
// 弹窗广告属性面板
|
||||||
|
defineOptions({ name: 'PopoverProperty' })
|
||||||
|
|
||||||
|
const props = defineProps<{ modelValue: PopoverProperty }>()
|
||||||
|
const emit = defineEmits(['update:modelValue'])
|
||||||
|
const { formData } = usePropertyForm(props.modelValue, emit)
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped lang="scss"></style>
|
||||||
@@ -134,6 +134,7 @@ watch(
|
|||||||
deep: true
|
deep: true
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 计算商品的间距
|
* 计算商品的间距
|
||||||
* @param index 商品索引
|
* @param index 商品索引
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { ComponentStyle, DiyComponent } from '@/components/DiyEditor/util'
|
import { ComponentStyle, DiyComponent } from '@/components/DiyEditor/util'
|
||||||
|
|
||||||
/** 商品卡片属性 */
|
/** 商品栏属性 */
|
||||||
export interface ProductListProperty {
|
export interface ProductListProperty {
|
||||||
// 布局类型:双列 | 三列 | 水平滑动
|
// 布局类型:双列 | 三列 | 水平滑动
|
||||||
layoutType: 'twoCol' | 'threeCol' | 'horizSwiper'
|
layoutType: 'twoCol' | 'threeCol' | 'horizSwiper'
|
||||||
|
|||||||
@@ -66,7 +66,7 @@
|
|||||||
import { ProductListProperty } from './config'
|
import { ProductListProperty } from './config'
|
||||||
import * as ProductSpuApi from '@/api/mall/product/spu'
|
import * as ProductSpuApi from '@/api/mall/product/spu'
|
||||||
|
|
||||||
/** 商品卡片 */
|
/** 商品栏 */
|
||||||
defineOptions({ name: 'ProductList' })
|
defineOptions({ name: 'ProductList' })
|
||||||
// 定义属性
|
// 定义属性
|
||||||
const props = defineProps<{ property: ProductListProperty }>()
|
const props = defineProps<{ property: ProductListProperty }>()
|
||||||
|
|||||||
@@ -88,7 +88,7 @@ import { ProductListProperty } from './config'
|
|||||||
import { usePropertyForm } from '@/components/DiyEditor/util'
|
import { usePropertyForm } from '@/components/DiyEditor/util'
|
||||||
import SpuShowcase from '@/views/mall/product/spu/components/SpuShowcase.vue'
|
import SpuShowcase from '@/views/mall/product/spu/components/SpuShowcase.vue'
|
||||||
|
|
||||||
// 商品卡片属性面板
|
// 商品栏属性面板
|
||||||
defineOptions({ name: 'ProductListProperty' })
|
defineOptions({ name: 'ProductListProperty' })
|
||||||
|
|
||||||
const props = defineProps<{ modelValue: ProductListProperty }>()
|
const props = defineProps<{ modelValue: ProductListProperty }>()
|
||||||
|
|||||||
@@ -0,0 +1,25 @@
|
|||||||
|
import { ComponentStyle, DiyComponent } from '@/components/DiyEditor/util'
|
||||||
|
|
||||||
|
/** 营销文章属性 */
|
||||||
|
export interface PromotionArticleProperty {
|
||||||
|
// 文章编号
|
||||||
|
id: number
|
||||||
|
// 组件样式
|
||||||
|
style: ComponentStyle
|
||||||
|
}
|
||||||
|
|
||||||
|
// 定义组件
|
||||||
|
export const component = {
|
||||||
|
id: 'PromotionArticle',
|
||||||
|
name: '营销文章',
|
||||||
|
icon: 'ph:article-medium',
|
||||||
|
property: {
|
||||||
|
style: {
|
||||||
|
bgType: 'color',
|
||||||
|
bgColor: '',
|
||||||
|
marginLeft: 8,
|
||||||
|
marginRight: 8,
|
||||||
|
marginBottom: 8
|
||||||
|
} as ComponentStyle
|
||||||
|
}
|
||||||
|
} as DiyComponent<PromotionArticleProperty>
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
<template>
|
||||||
|
<div class="min-h-30px" v-html="article?.content"></div>
|
||||||
|
</template>
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { PromotionArticleProperty } from './config'
|
||||||
|
import * as ArticleApi from '@/api/mall/promotion/article/index'
|
||||||
|
|
||||||
|
/** 营销文章 */
|
||||||
|
defineOptions({ name: 'PromotionArticle' })
|
||||||
|
// 定义属性
|
||||||
|
const props = defineProps<{ property: PromotionArticleProperty }>()
|
||||||
|
// 商品列表
|
||||||
|
const article = ref<ArticleApi.ArticleVO>()
|
||||||
|
watch(
|
||||||
|
() => props.property.id,
|
||||||
|
async () => {
|
||||||
|
if (props.property.id) {
|
||||||
|
article.value = await ArticleApi.getArticle(props.property.id)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
immediate: true
|
||||||
|
}
|
||||||
|
)
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped lang="scss"></style>
|
||||||
@@ -0,0 +1,56 @@
|
|||||||
|
<template>
|
||||||
|
<ComponentContainerProperty v-model="formData.style">
|
||||||
|
<el-form label-width="40px" :model="formData">
|
||||||
|
<el-form-item label="文章" prop="id">
|
||||||
|
<el-select
|
||||||
|
v-model="formData.id"
|
||||||
|
placeholder="请选择文章"
|
||||||
|
class="w-full"
|
||||||
|
filterable
|
||||||
|
remote
|
||||||
|
:remote-method="queryArticleList"
|
||||||
|
:loading="loading"
|
||||||
|
>
|
||||||
|
<el-option
|
||||||
|
v-for="article in articles"
|
||||||
|
:key="article.id"
|
||||||
|
:label="article.title"
|
||||||
|
:value="article.id"
|
||||||
|
/>
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
</ComponentContainerProperty>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { PromotionArticleProperty } from './config'
|
||||||
|
import { usePropertyForm } from '@/components/DiyEditor/util'
|
||||||
|
import * as ArticleApi from '@/api/mall/promotion/article/index'
|
||||||
|
|
||||||
|
// 营销文章属性面板
|
||||||
|
defineOptions({ name: 'PromotionArticleProperty' })
|
||||||
|
|
||||||
|
const props = defineProps<{ modelValue: PromotionArticleProperty }>()
|
||||||
|
const emit = defineEmits(['update:modelValue'])
|
||||||
|
const { formData } = usePropertyForm(props.modelValue, emit)
|
||||||
|
// 文章列表
|
||||||
|
const articles = ref<ArticleApi.ArticleVO>([])
|
||||||
|
|
||||||
|
// 加载中
|
||||||
|
const loading = ref(false)
|
||||||
|
// 查询文章列表
|
||||||
|
const queryArticleList = async (title?: string) => {
|
||||||
|
loading.value = true
|
||||||
|
const { list } = await ArticleApi.getArticlePage({ title, pageSize: 10 })
|
||||||
|
articles.value = list
|
||||||
|
loading.value = false
|
||||||
|
}
|
||||||
|
|
||||||
|
// 初始化
|
||||||
|
onMounted(() => {
|
||||||
|
queryArticleList()
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped lang="scss"></style>
|
||||||
@@ -0,0 +1,64 @@
|
|||||||
|
import { ComponentStyle, DiyComponent } from '@/components/DiyEditor/util'
|
||||||
|
|
||||||
|
/** 拼团属性 */
|
||||||
|
export interface PromotionCombinationProperty {
|
||||||
|
// 布局类型:单列 | 三列
|
||||||
|
layoutType: 'oneCol' | 'threeCol'
|
||||||
|
// 商品字段
|
||||||
|
fields: {
|
||||||
|
// 商品名称
|
||||||
|
name: PromotionCombinationFieldProperty
|
||||||
|
// 商品价格
|
||||||
|
price: PromotionCombinationFieldProperty
|
||||||
|
}
|
||||||
|
// 角标
|
||||||
|
badge: {
|
||||||
|
// 是否显示
|
||||||
|
show: boolean
|
||||||
|
// 角标图片
|
||||||
|
imgUrl: string
|
||||||
|
}
|
||||||
|
// 上圆角
|
||||||
|
borderRadiusTop: number
|
||||||
|
// 下圆角
|
||||||
|
borderRadiusBottom: number
|
||||||
|
// 间距
|
||||||
|
space: number
|
||||||
|
// 拼团活动编号
|
||||||
|
activityId: number
|
||||||
|
// 组件样式
|
||||||
|
style: ComponentStyle
|
||||||
|
}
|
||||||
|
|
||||||
|
// 商品字段
|
||||||
|
export interface PromotionCombinationFieldProperty {
|
||||||
|
// 是否显示
|
||||||
|
show: boolean
|
||||||
|
// 颜色
|
||||||
|
color: string
|
||||||
|
}
|
||||||
|
|
||||||
|
// 定义组件
|
||||||
|
export const component = {
|
||||||
|
id: 'PromotionCombination',
|
||||||
|
name: '拼团',
|
||||||
|
icon: 'mdi:account-group',
|
||||||
|
property: {
|
||||||
|
layoutType: 'oneCol',
|
||||||
|
fields: {
|
||||||
|
name: { show: true, color: '#000' },
|
||||||
|
price: { show: true, color: '#ff3000' }
|
||||||
|
},
|
||||||
|
badge: { show: false, imgUrl: '' },
|
||||||
|
borderRadiusTop: 8,
|
||||||
|
borderRadiusBottom: 8,
|
||||||
|
space: 8,
|
||||||
|
style: {
|
||||||
|
bgType: 'color',
|
||||||
|
bgColor: '',
|
||||||
|
marginLeft: 8,
|
||||||
|
marginRight: 8,
|
||||||
|
marginBottom: 8
|
||||||
|
} as ComponentStyle
|
||||||
|
}
|
||||||
|
} as DiyComponent<PromotionCombinationProperty>
|
||||||
@@ -0,0 +1,125 @@
|
|||||||
|
<template>
|
||||||
|
<el-scrollbar class="z-1 min-h-30px" wrap-class="w-full" ref="containerRef">
|
||||||
|
<!-- 商品网格 -->
|
||||||
|
<div
|
||||||
|
class="grid overflow-x-auto"
|
||||||
|
:style="{
|
||||||
|
gridGap: `${property.space}px`,
|
||||||
|
gridTemplateColumns,
|
||||||
|
width: scrollbarWidth
|
||||||
|
}"
|
||||||
|
>
|
||||||
|
<!-- 商品 -->
|
||||||
|
<div
|
||||||
|
class="relative box-content flex flex-row flex-wrap overflow-hidden bg-white"
|
||||||
|
:style="{
|
||||||
|
borderTopLeftRadius: `${property.borderRadiusTop}px`,
|
||||||
|
borderTopRightRadius: `${property.borderRadiusTop}px`,
|
||||||
|
borderBottomLeftRadius: `${property.borderRadiusBottom}px`,
|
||||||
|
borderBottomRightRadius: `${property.borderRadiusBottom}px`
|
||||||
|
}"
|
||||||
|
v-for="(spu, index) in spuList"
|
||||||
|
:key="index"
|
||||||
|
>
|
||||||
|
<!-- 角标 -->
|
||||||
|
<div
|
||||||
|
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" />
|
||||||
|
</div>
|
||||||
|
<!-- 商品封面图 -->
|
||||||
|
<el-image fit="cover" :src="spu.picUrl" :style="{ width: imageSize, height: imageSize }" />
|
||||||
|
<div
|
||||||
|
:class="[
|
||||||
|
'flex flex-col gap-8px p-8px box-border',
|
||||||
|
{
|
||||||
|
'w-[calc(100%-64px)]': columns === 2,
|
||||||
|
'w-full': columns === 3
|
||||||
|
}
|
||||||
|
]"
|
||||||
|
>
|
||||||
|
<!-- 商品名称 -->
|
||||||
|
<div
|
||||||
|
v-if="property.fields.name.show"
|
||||||
|
class="truncate text-12px"
|
||||||
|
:style="{ color: property.fields.name.color }"
|
||||||
|
>
|
||||||
|
{{ spu.name }}
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<!-- 商品价格 -->
|
||||||
|
<span
|
||||||
|
v-if="property.fields.price.show"
|
||||||
|
class="text-12px"
|
||||||
|
:style="{ color: property.fields.price.color }"
|
||||||
|
>
|
||||||
|
¥{{ spu.price }}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</el-scrollbar>
|
||||||
|
</template>
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { PromotionCombinationProperty } from './config'
|
||||||
|
import * as ProductSpuApi from '@/api/mall/product/spu'
|
||||||
|
import * as CombinationActivityApi from '@/api/mall/promotion/combination/combinationActivity'
|
||||||
|
|
||||||
|
/** 拼团 */
|
||||||
|
defineOptions({ name: 'PromotionCombination' })
|
||||||
|
// 定义属性
|
||||||
|
const props = defineProps<{ property: PromotionCombinationProperty }>()
|
||||||
|
// 商品列表
|
||||||
|
const spuList = ref<ProductSpuApi.Spu[]>([])
|
||||||
|
watch(
|
||||||
|
() => props.property.activityId,
|
||||||
|
async () => {
|
||||||
|
if (!props.property.activityId) return
|
||||||
|
const activity = await CombinationActivityApi.getCombinationActivity(props.property.activityId)
|
||||||
|
if (!activity?.spuId) return
|
||||||
|
spuList.value = [await ProductSpuApi.getSpu(activity.spuId)]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
immediate: true,
|
||||||
|
deep: true
|
||||||
|
}
|
||||||
|
)
|
||||||
|
// 手机宽度
|
||||||
|
const phoneWidth = ref(375)
|
||||||
|
// 容器
|
||||||
|
const containerRef = ref()
|
||||||
|
// 商品的列数
|
||||||
|
const columns = ref(2)
|
||||||
|
// 滚动条宽度
|
||||||
|
const scrollbarWidth = ref('100%')
|
||||||
|
// 商品图大小
|
||||||
|
const imageSize = ref('0')
|
||||||
|
// 商品网络列数
|
||||||
|
const gridTemplateColumns = ref('')
|
||||||
|
// 计算布局参数
|
||||||
|
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>
|
||||||
|
|
||||||
|
<style scoped lang="scss"></style>
|
||||||
@@ -0,0 +1,112 @@
|
|||||||
|
<template>
|
||||||
|
<ComponentContainerProperty v-model="formData.style">
|
||||||
|
<el-form label-width="80px" :model="formData">
|
||||||
|
<el-card header="拼团活动" class="property-group" shadow="never">
|
||||||
|
<el-form-item label="拼团活动" prop="activityId">
|
||||||
|
<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 header="商品样式" class="property-group" 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 label="oneCol">
|
||||||
|
<Icon icon="fluent:text-column-one-24-filled" />
|
||||||
|
</el-radio-button>
|
||||||
|
</el-tooltip>
|
||||||
|
<el-tooltip class="item" content="三列" placement="bottom">
|
||||||
|
<el-radio-button label="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.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-card>
|
||||||
|
<el-card header="角标" class="property-group" shadow="never">
|
||||||
|
<el-form-item label="角标" prop="badge.show">
|
||||||
|
<el-switch v-model="formData.badge.show" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="角标" prop="badge.imgUrl" v-if="formData.badge.show">
|
||||||
|
<UploadImg v-model="formData.badge.imgUrl" height="44px" width="72px">
|
||||||
|
<template #tip> 建议尺寸:36 * 22 </template>
|
||||||
|
</UploadImg>
|
||||||
|
</el-form-item>
|
||||||
|
</el-card>
|
||||||
|
<el-card header="商品样式" class="property-group" shadow="never">
|
||||||
|
<el-form-item label="上圆角" prop="borderRadiusTop">
|
||||||
|
<el-slider
|
||||||
|
v-model="formData.borderRadiusTop"
|
||||||
|
:max="100"
|
||||||
|
:min="0"
|
||||||
|
show-input
|
||||||
|
input-size="small"
|
||||||
|
:show-input-controls="false"
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="下圆角" prop="borderRadiusBottom">
|
||||||
|
<el-slider
|
||||||
|
v-model="formData.borderRadiusBottom"
|
||||||
|
:max="100"
|
||||||
|
:min="0"
|
||||||
|
show-input
|
||||||
|
input-size="small"
|
||||||
|
:show-input-controls="false"
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="间隔" prop="space">
|
||||||
|
<el-slider
|
||||||
|
v-model="formData.space"
|
||||||
|
:max="100"
|
||||||
|
:min="0"
|
||||||
|
show-input
|
||||||
|
input-size="small"
|
||||||
|
:show-input-controls="false"
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
|
</el-card>
|
||||||
|
</el-form>
|
||||||
|
</ComponentContainerProperty>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { PromotionCombinationProperty } from './config'
|
||||||
|
import { usePropertyForm } from '@/components/DiyEditor/util'
|
||||||
|
import * as CombinationActivityApi from '@/api/mall/promotion/combination/combinationActivity'
|
||||||
|
import { CommonStatusEnum } from '@/utils/constants'
|
||||||
|
|
||||||
|
// 拼团属性面板
|
||||||
|
defineOptions({ name: 'PromotionCombinationProperty' })
|
||||||
|
|
||||||
|
const props = defineProps<{ modelValue: PromotionCombinationProperty }>()
|
||||||
|
const emit = defineEmits(['update:modelValue'])
|
||||||
|
const { formData } = usePropertyForm(props.modelValue, emit)
|
||||||
|
// 活动列表
|
||||||
|
const activityList = ref<CombinationActivityApi.CombinationActivityVO>([])
|
||||||
|
onMounted(async () => {
|
||||||
|
const { list } = await CombinationActivityApi.getCombinationActivityPage({
|
||||||
|
status: CommonStatusEnum.ENABLE
|
||||||
|
})
|
||||||
|
activityList.value = list
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped lang="scss"></style>
|
||||||
Loaded 100 of 297 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user