Compare commits
111 Commits
dev-cjl
...
e8ec9bdc47
| Author | SHA1 | Date | |
|---|---|---|---|
| e8ec9bdc47 | |||
|
|
6726bd7756 | ||
|
|
a8bf678f2e | ||
|
|
3ec039050f | ||
|
|
6baa78a94f | ||
|
|
770d700ba1 | ||
|
|
039ab28c15 | ||
|
|
f75140e135 | ||
|
|
467e1eceb7 | ||
|
|
47bac0315f | ||
|
|
ce0da69b5f | ||
|
|
0d99fbac80 | ||
|
|
06681658fc | ||
| 6a07c0c711 | |||
|
|
3abe367954 | ||
| b3435a9a86 | |||
|
|
2eac9d0fc9 | ||
|
|
f7924d7cb1 | ||
|
|
6ac19b825e | ||
| 6d83198a9f | |||
|
|
e10fedb4dc | ||
|
|
f70cdfb074 | ||
| 29ee948cbb | |||
|
|
ac70a128c1 | ||
| f044dbbe7b | |||
|
|
21ff3718bd | ||
| 1c0f0651d8 | |||
|
|
1778ea9fb4 | ||
| b052af5507 | |||
|
|
e4ae5e275f | ||
| f7e81fad0e | |||
|
|
df3a4b9f46 | ||
|
|
fc22c16cb1 | ||
| c6b6602181 | |||
|
|
9b7543f65f | ||
| f2cea1585d | |||
|
|
1d1f1762f8 | ||
| 32bda88a41 | |||
|
|
ffc7691b47 | ||
|
|
c01fea9c5b | ||
|
|
beb12193b3 | ||
|
|
b09ba1547c | ||
|
|
72341a96a5 | ||
| 6d866b7e7c | |||
|
|
9cdfa01d33 | ||
|
|
af84640a4b | ||
|
|
15179707d9 | ||
|
|
fe4270178f | ||
| bbecf28819 | |||
|
|
cd22e26589 | ||
| 5740ca00d1 | |||
| 206315c642 | |||
|
|
b475af8dd3 | ||
|
|
3022106428 | ||
|
|
d4641eacb5 | ||
|
|
d3b4a360dd | ||
| b9a3424d98 | |||
|
|
91a509d49d | ||
|
|
7e771a89e4 | ||
|
|
24e540f151 | ||
| 16cb210988 | |||
|
|
0ef7703f7d | ||
| bc3c4f2ebc | |||
|
|
fa39fd8492 | ||
|
|
b8bc124162 | ||
|
|
e1036fd2c3 | ||
|
|
014bb14170 | ||
| 6ba54c8bcd | |||
|
|
eaaceb9595 | ||
| acccadd43a | |||
|
|
1ab365efae | ||
| 0f04d7e80c | |||
|
|
f74ed7e62d | ||
| 02af7babc4 | |||
|
|
8fc9dd449b | ||
| 505139cfe2 | |||
|
|
9c7ba2fb87 | ||
| 6246d46d53 | |||
|
|
2406bcb8e4 | ||
|
|
41c20623d4 | ||
|
|
a8ea0e4b26 | ||
| 98a5d374a4 | |||
|
|
8de29b754a | ||
|
|
5f63709c2f | ||
| dd049320f6 | |||
|
|
a89bd1b59c | ||
| 696e96a0c0 | |||
|
|
4daf01a21b | ||
|
|
83d04e7333 | ||
| ee770b9e60 | |||
| a81d21dd3b | |||
| 6908183a36 | |||
| af9bcb7024 | |||
| 5c6c3df4d0 | |||
| acdcc670de | |||
| 9472c5ea56 | |||
| 4f150b0912 | |||
| f76d6dce66 | |||
| dd4bdb0786 | |||
| 477a279867 | |||
| c87637cc78 | |||
| a544840679 | |||
| 247c6ed4a0 | |||
| 769c901954 | |||
| 18bfc7d353 | |||
| fb6b341242 | |||
| fea69da8ed | |||
| 125f6f28ad | |||
| 39cddaa8e0 | |||
| ffd15e2433 | |||
| 2bebd0480e |
12
.env.base
12
.env.base
@@ -4,11 +4,17 @@ VITE_NODE_ENV=development
|
||||
VITE_DEV=true
|
||||
|
||||
# 请求路径
|
||||
VITE_BASE_URL='http://118.31.23.45:48080'
|
||||
# VITE_BASE_URL='http://114.55.169.15:48080'
|
||||
VITE_BASE_URL='http://localhost:48080'
|
||||
# VITE_BASE_URL='http://47.98.161.246:48080'
|
||||
#VITE_BASE_URL='http://114.215.207.150:48080'
|
||||
|
||||
# 小程序接口请求路劲
|
||||
VITE_APPLET_URL='https://cloud.ahduima.com'
|
||||
# 题库路径
|
||||
VITE_TIKU_URL = 'http://47.98.161.246'
|
||||
|
||||
# 上传路径
|
||||
VITE_UPLOAD_URL='http://118.31.23.45:48080/admin-api/system/file/upload'
|
||||
VITE_UPLOAD_URL='http://47.98.161.246:48080/admin-api/system/file/upload'
|
||||
# VITE_UPLOAD_URL='http://114.55.169.15:48080/admin-api/system/file/upload'
|
||||
|
||||
# 接口前缀
|
||||
|
||||
2
.env.dev
2
.env.dev
@@ -7,7 +7,7 @@ VITE_DEV=false
|
||||
VITE_BASE_URL='http://localhost:48080'
|
||||
|
||||
# 上传路径
|
||||
VITE_UPLOAD_URL='http://118.31.23.45:48080/admin-api/system/file/upload'
|
||||
VITE_UPLOAD_URL='http://47.98.161.246:48080/admin-api/system/file/upload'
|
||||
|
||||
# 接口前缀
|
||||
VITE_API_BASEPATH=/crm-api
|
||||
|
||||
@@ -4,10 +4,11 @@ VITE_NODE_ENV=development
|
||||
VITE_DEV=true
|
||||
|
||||
# 请求路径
|
||||
VITE_BASE_URL='http://118.31.23.45:48080'
|
||||
#VITE_BASE_URL='http://47.98.161.246:48080'
|
||||
VITE_BASE_URL='http://localhost:48080'
|
||||
|
||||
# 上传路径
|
||||
VITE_UPLOAD_URL='http://118.31.23.45:48080/admin-api/system/file/upload'
|
||||
VITE_UPLOAD_URL='http://47.98.161.246:48080/admin-api/system/file/upload'
|
||||
|
||||
# 接口前缀
|
||||
VITE_API_BASEPATH=/crm-api
|
||||
|
||||
@@ -7,7 +7,7 @@ VITE_DEV=false
|
||||
VITE_BASE_URL='http://api-dashboard.yudao.iocoder.cn'
|
||||
|
||||
# 上传路径
|
||||
VITE_UPLOAD_URL='http://118.31.23.45:48080/admin-api/system/file/upload'
|
||||
VITE_UPLOAD_URL='http://47.98.161.246:48080/admin-api/system/file/upload'
|
||||
|
||||
# 接口前缀
|
||||
VITE_API_BASEPATH=/crm-api
|
||||
|
||||
@@ -7,7 +7,7 @@ VITE_DEV=false
|
||||
VITE_BASE_URL='http://localhost:48080'
|
||||
|
||||
# 上传路径
|
||||
VITE_UPLOAD_URL='http://118.31.23.45:48080/admin-api/system/file/upload'
|
||||
VITE_UPLOAD_URL='http://47.98.161.246:48080/admin-api/system/file/upload'
|
||||
|
||||
# 接口前缀
|
||||
VITE_API_BASEPATH=/crm-api
|
||||
|
||||
@@ -7,7 +7,7 @@ VITE_DEV=false
|
||||
VITE_BASE_URL='http://localhost:48080'
|
||||
|
||||
# 上传路径
|
||||
VITE_UPLOAD_URL='http://118.31.23.45:48080/admin-api/system/file/upload'
|
||||
VITE_UPLOAD_URL='http://47.98.161.246:48080/admin-api/system/file/upload'
|
||||
|
||||
# 接口前缀
|
||||
VITE_API_BASEPATH=/crm-api
|
||||
|
||||
15
.vscode/extensions.json
vendored
15
.vscode/extensions.json
vendored
@@ -1,15 +0,0 @@
|
||||
{
|
||||
"recommendations": [
|
||||
"voorjaar.windicss-intellisense",
|
||||
"vscode-icons-team.vscode-icons",
|
||||
"davidanson.vscode-markdownlint",
|
||||
"stylelint.vscode-stylelint",
|
||||
"dbaeumer.vscode-eslint",
|
||||
"esbenp.prettier-vscode",
|
||||
"vue.volar",
|
||||
"lokalise.i18n-ally",
|
||||
"mikestead.dotenv",
|
||||
"eamodio.gitlens",
|
||||
"antfu.iconify"
|
||||
]
|
||||
}
|
||||
16
.vscode/launch.json
vendored
16
.vscode/launch.json
vendored
@@ -1,16 +0,0 @@
|
||||
{
|
||||
// Use IntelliSense to learn about possible attributes.
|
||||
// Hover to view descriptions of existing attributes.
|
||||
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
|
||||
"version": "0.2.0",
|
||||
"configurations": [
|
||||
{
|
||||
"type": "msedge",
|
||||
"request": "launch",
|
||||
"name": "Launch Edge against localhost",
|
||||
"url": "http://localhost",
|
||||
"webRoot": "${workspaceFolder}/src",
|
||||
"sourceMaps": true
|
||||
}
|
||||
]
|
||||
}
|
||||
54
.vscode/settings.json
vendored
54
.vscode/settings.json
vendored
@@ -1,54 +0,0 @@
|
||||
{
|
||||
"typescript.tsdk": "node_modules/typescript/lib",
|
||||
"prettier.enable": true,
|
||||
"editor.formatOnType": true,
|
||||
"editor.formatOnSave": true,
|
||||
"editor.formatOnPaste": true,
|
||||
"editor.codeActionsOnSave": {
|
||||
"source.fixAll.eslint": "explicit"
|
||||
},
|
||||
"[vue]": {
|
||||
"editor.defaultFormatter": "rvest.vs-code-prettier-eslint"
|
||||
},
|
||||
"[javascript]": {
|
||||
"editor.defaultFormatter": "esbenp.prettier-vscode"
|
||||
},
|
||||
"[typescript]": {
|
||||
"editor.defaultFormatter": "rvest.vs-code-prettier-eslint"
|
||||
},
|
||||
"[json]": {
|
||||
"editor.defaultFormatter": "esbenp.prettier-vscode"
|
||||
},
|
||||
"[jsonc]": {
|
||||
"editor.defaultFormatter": "esbenp.prettier-vscode"
|
||||
},
|
||||
"[html]": {
|
||||
"editor.defaultFormatter": "esbenp.prettier-vscode"
|
||||
},
|
||||
"[css]": {
|
||||
"editor.defaultFormatter": "esbenp.prettier-vscode"
|
||||
},
|
||||
"[scss]": {
|
||||
"editor.defaultFormatter": "esbenp.prettier-vscode"
|
||||
},
|
||||
"i18n-ally.localesPaths": ["src/locales"],
|
||||
"i18n-ally.keystyle": "nested",
|
||||
"i18n-ally.sortKeys": true,
|
||||
"i18n-ally.namespace": true,
|
||||
"i18n-ally.enabledParsers": ["ts"],
|
||||
"i18n-ally.sourceLanguage": "en",
|
||||
"i18n-ally.displayLanguage": "zh-CN",
|
||||
"i18n-ally.enabledFrameworks": ["vue", "react"],
|
||||
"god.tsconfig": "./tsconfig.json",
|
||||
"vue-i18n.i18nPaths": "src/locales",
|
||||
"explorer.fileNesting.enabled": true,
|
||||
"explorer.fileNesting.expand": false,
|
||||
"explorer.fileNesting.patterns": {
|
||||
"*.ts": "$(capture).test.ts, $(capture).test.tsx",
|
||||
"*.tsx": "$(capture).test.ts, $(capture).test.tsx",
|
||||
"*.env": "$(capture).env.*",
|
||||
"CHANGELOG.md": "CHANGELOG*",
|
||||
"package.json": "pnpm-lock.yaml,pnpm-workspace.yaml,LICENSE,.gitattributes,.gitignore,.gitpod.yml,CNAME,README*,.npmrc,.browserslistrc,vite.config.*,windi.*,tailwind.*,tsconfig.*,postcss*",
|
||||
".eslintrc.js": ".eslintignore,.eslintrc-*,.prettierignore,.stylelintignore,.commitlintrc.js,.prettierrc.js,.stylelint*,stylelint*,prettier.*,.editorconfig"
|
||||
}
|
||||
}
|
||||
@@ -60,6 +60,7 @@
|
||||
"min-dash": "^4.1.1",
|
||||
"mitt": "^3.0.0",
|
||||
"nprogress": "^0.2.0",
|
||||
"or": "^0.2.0",
|
||||
"pinia": "^2.1.3",
|
||||
"qrcode": "^1.5.3",
|
||||
"qs": "^6.11.2",
|
||||
@@ -69,6 +70,7 @@
|
||||
"vue": "3.3.4",
|
||||
"vue-amap": "^0.5.10",
|
||||
"vue-dompurify-html": "^5.0.1",
|
||||
"vue-draggable-plus": "^0.5.3",
|
||||
"vue-i18n": "9.2.2",
|
||||
"vue-router": "^4.2.1",
|
||||
"vue-types": "^5.0.3",
|
||||
|
||||
@@ -6,8 +6,8 @@ export const getSimpleClueList = async () => {
|
||||
}
|
||||
|
||||
// 查询列表
|
||||
export const getCluePage = async (params) => {
|
||||
return await request.get({ url: '/admin-api/crm/sch-clue/page', params })
|
||||
export const getCluePage = async (data) => {
|
||||
return await request.post({ url: '/admin-api/crm/sch-clue/page', data })
|
||||
}
|
||||
|
||||
// 查询详情
|
||||
|
||||
@@ -5,7 +5,7 @@ export const getPaymentList = async (params) => {
|
||||
return await request.get({ url: '/admin-api/crm/sign-pay-record/list', params })
|
||||
}
|
||||
|
||||
// 查询(精简)列表
|
||||
// 查询列表
|
||||
export const getPaymentPage = async (params) => {
|
||||
return await request.get({ url: '/admin-api/crm/sign-pay-record/page', params })
|
||||
}
|
||||
@@ -45,3 +45,8 @@ export const updateApplyPayment = async (data) => {
|
||||
data
|
||||
})
|
||||
}
|
||||
|
||||
// 导出
|
||||
export const exportPayment = async (params) => {
|
||||
return await request.download({ url: '/admin-api/crm/sign-pay-record/export-excel', params })
|
||||
}
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import request from '@/config/axios'
|
||||
|
||||
// 查询列表
|
||||
export const getSignPage = async (params) => {
|
||||
return await request.get({ url: '/admin-api/crm/sign/page', params })
|
||||
export const getSignPage = async (data) => {
|
||||
return await request.post({ url: '/admin-api/crm/sign/page', data })
|
||||
}
|
||||
|
||||
// 查询详情
|
||||
@@ -51,3 +51,13 @@ export const updateProduceSort = (data) => {
|
||||
export const getSignProfit = (params) => {
|
||||
return request.get({ url: '/admin-api/crm/sign/profit', params })
|
||||
}
|
||||
|
||||
// 新增产品
|
||||
export const addOrderProduct = (data) => {
|
||||
return request.post({ url: '/admin-api/crm/sign-prodcut/create', data })
|
||||
}
|
||||
|
||||
// 新增产品
|
||||
export const removeOrderProduct = (id) => {
|
||||
return request.delete({ url: '/admin-api/crm/sign-prodcut/delete?id=' + id })
|
||||
}
|
||||
|
||||
8
src/api/home/reportChannel.js
Normal file
8
src/api/home/reportChannel.js
Normal file
@@ -0,0 +1,8 @@
|
||||
import request from '@/config/axios'
|
||||
export const getList = async (data) => {
|
||||
return await request.post({ url: '/admin-api/crm/sch-clue/clueQuality/report', data })
|
||||
}
|
||||
|
||||
export const getPeriodList = async (data) => {
|
||||
return await request.post({ url: '/admin-api/crm/sch-clue/clueQuality/period/report', data })
|
||||
}
|
||||
8
src/api/home/reportSaler.js
Normal file
8
src/api/home/reportSaler.js
Normal file
@@ -0,0 +1,8 @@
|
||||
import request from '@/config/axios'
|
||||
export const getInfo = async (data) => {
|
||||
return await request.post({ url: '/admin-api/crm/sch-clue/sale/report/detail', data })
|
||||
}
|
||||
|
||||
export const getList = async (data) => {
|
||||
return await request.post({ url: '/admin-api/crm/sch-clue/sale/report', data })
|
||||
}
|
||||
8
src/api/home/reportSignDetail.js
Normal file
8
src/api/home/reportSignDetail.js
Normal file
@@ -0,0 +1,8 @@
|
||||
import request from '@/config/axios'
|
||||
export const getInfo = async (data) => {
|
||||
return await request.post({ url: '/admin-api/crm/sch-clue/signData/report/one', data })
|
||||
}
|
||||
|
||||
export const getList = async (data) => {
|
||||
return await request.post({ url: '/admin-api/crm/sch-clue/signData/report/two', data })
|
||||
}
|
||||
4
src/api/home/reportSignRate.js
Normal file
4
src/api/home/reportSignRate.js
Normal file
@@ -0,0 +1,4 @@
|
||||
import request from '@/config/axios'
|
||||
export const getList = async (data) => {
|
||||
return await request.post({ url: '/admin-api/crm/sch-clue/signRate/report', data })
|
||||
}
|
||||
42
src/api/kpi/index.js
Normal file
42
src/api/kpi/index.js
Normal file
@@ -0,0 +1,42 @@
|
||||
import request from '@/config/axios'
|
||||
|
||||
// 创建
|
||||
export const createKpi = (data) => {
|
||||
return request.post({
|
||||
url: '/admin-api/oa/examine-configuration/create',
|
||||
data,
|
||||
isSubmitForm: true
|
||||
})
|
||||
}
|
||||
|
||||
// 修改
|
||||
export const updateKpi = (data) => {
|
||||
return request.put({ url: '/admin-api/oa/examine-configuration/update', data })
|
||||
}
|
||||
|
||||
// 修改状态
|
||||
export const updateKpiStatus = (data) => {
|
||||
return request.put({ url: '/admin-api/oa/examine-configuration/status/update', data })
|
||||
}
|
||||
|
||||
// 分页
|
||||
export const getKpiPage = (params) => {
|
||||
return request.get({ url: '/admin-api/oa/examine-configuration/page', params })
|
||||
}
|
||||
|
||||
// 详情
|
||||
export const getKpiDetail = (id) => {
|
||||
return request.get({ url: '/admin-api/oa/examine-configuration/get', params: { id } })
|
||||
}
|
||||
|
||||
// 删除
|
||||
export const deleteKpi = (id) => {
|
||||
return request.delete({ url: '/admin-api/oa/examine-configuration/delete', params: { id } })
|
||||
}
|
||||
|
||||
//
|
||||
export const getKpiEmployees = () => {
|
||||
return request.get({
|
||||
url: '/admin-api/oa/employee/examinedEmployees'
|
||||
})
|
||||
}
|
||||
41
src/api/kpi/score.js
Normal file
41
src/api/kpi/score.js
Normal file
@@ -0,0 +1,41 @@
|
||||
import request from '@/config/axios'
|
||||
|
||||
// 打分数据
|
||||
export const getKpiEmployees = (params) => {
|
||||
return request.get({ url: '/admin-api/oa/examine-score/getExamineUserExamineScore', params })
|
||||
}
|
||||
|
||||
// 保存考勤打分数据
|
||||
export const saveKpiScore = (data) => {
|
||||
return request.put({ url: '/admin-api/oa/examine-score/update', data })
|
||||
}
|
||||
|
||||
// 删除考核项
|
||||
export const deleteKpiItem = (params) => {
|
||||
return request.delete({ url: '/admin-api/oa/examine-score/delete', params })
|
||||
}
|
||||
|
||||
// 考评分页数据
|
||||
export const getScorePage = (params) => {
|
||||
return request.get({ url: '/admin-api/oa/examine-period/page', params })
|
||||
}
|
||||
|
||||
// 封存
|
||||
export const saveScoreRecord = (params) => {
|
||||
return request.get({ url: '/admin-api/oa/examine-period/confirm', params })
|
||||
}
|
||||
|
||||
// 查询周期是否已封存
|
||||
export const getKpiPeriodStatus = (params) => {
|
||||
return request.get({ url: '/admin-api/oa/examine-period/getExaminePeriodByPeriod', params })
|
||||
}
|
||||
|
||||
// 查询考核记录
|
||||
export const getScoreRecord = (data) => {
|
||||
return request.post({ url: '/admin-api/oa/examine-result/periodPage', data })
|
||||
}
|
||||
|
||||
// 查详情
|
||||
export const getScoreDetail = (params) => {
|
||||
return request.get({ url: '/admin-api/oa/examine-result/detail', params })
|
||||
}
|
||||
33
src/api/school/setting/supplier.js
Normal file
33
src/api/school/setting/supplier.js
Normal file
@@ -0,0 +1,33 @@
|
||||
import request from '@/config/axios'
|
||||
// 查询列表
|
||||
export const getSupplierPage = async (params) => {
|
||||
return await request.get({ url: '/admin-api/crm/erp-supplier/page', params })
|
||||
}
|
||||
|
||||
export const getSupplierSimpleList = async (params) => {
|
||||
return await request.get({ url: '/admin-api/crm/erp-supplier/simple-list', params })
|
||||
}
|
||||
|
||||
// 查询详情
|
||||
export const getSupplier = async (id) => {
|
||||
return await request.get({ url: '/admin-api/crm/erp-supplier/get?id=' + id })
|
||||
}
|
||||
|
||||
// 新增
|
||||
export const createSupplier = async (data) => {
|
||||
return await request.post({
|
||||
url: '/admin-api/crm/erp-supplier/create',
|
||||
data: data,
|
||||
isSubmitForm: true
|
||||
})
|
||||
}
|
||||
|
||||
// 修改
|
||||
export const updateSupplier = async (params) => {
|
||||
return await request.put({ url: '/admin-api/crm/erp-supplier/update', data: params })
|
||||
}
|
||||
|
||||
// 删除
|
||||
export const deleteSupplier = async (id) => {
|
||||
return await request.delete({ url: '/admin-api/crm/erp-supplier/delete?id=' + id })
|
||||
}
|
||||
@@ -12,9 +12,11 @@ export interface UserVO {
|
||||
avatar: string
|
||||
loginIp: string
|
||||
status: number
|
||||
hireStatus: number
|
||||
remark: string
|
||||
loginDate: Date
|
||||
createTime: Date
|
||||
retireDate: string
|
||||
}
|
||||
|
||||
// 查询用户管理列表
|
||||
@@ -62,10 +64,17 @@ export const resetUserPwd = (id: number, password: string) => {
|
||||
}
|
||||
|
||||
// 用户状态修改
|
||||
export const updateUserStatus = (id: number, status: number) => {
|
||||
export const updateUserStatus = (
|
||||
id: number,
|
||||
status: number,
|
||||
retireDate: string,
|
||||
hireStatus: number
|
||||
) => {
|
||||
const data = {
|
||||
id,
|
||||
status
|
||||
status,
|
||||
retireDate,
|
||||
hireStatus
|
||||
}
|
||||
return request.put({ url: '/admin-api/system/user/update-status', data: data })
|
||||
}
|
||||
@@ -84,3 +93,8 @@ export const getAllUserList = (): Promise<UserVO[]> => {
|
||||
export const getAllUserListWithHire = (): Promise<UserVO[]> => {
|
||||
return request.get({ url: '/admin-api/system/user/no/permission/list-all' })
|
||||
}
|
||||
|
||||
// 获取钉钉id
|
||||
export const getDDIdByPhone = (params) => {
|
||||
return request.get({ url: '/admin-api/system/user/getDingTalkUserIdByMobile', params })
|
||||
}
|
||||
|
||||
23
src/api/system/wechat/index.js
Normal file
23
src/api/system/wechat/index.js
Normal file
@@ -0,0 +1,23 @@
|
||||
import request from '@/config/axios'
|
||||
|
||||
// 更新
|
||||
export const updateWechatConfig = (data) => {
|
||||
return request.post({ url: '/admin-api/system/wx-robot-message-setting/save', data })
|
||||
}
|
||||
|
||||
// 更新发送时间
|
||||
export const updateWechatSendTime = (data) => {
|
||||
return request.put({ url: '/admin-api/system/wx-robot-message-setting/update-send-time', data })
|
||||
}
|
||||
// 更新微信群
|
||||
export const updateWechatGroup = (data) => {
|
||||
return request.put({ url: '/admin-api/system/wx-robot-message-setting/update-wx-group', data })
|
||||
}
|
||||
|
||||
// 获得列表
|
||||
export const getWechatConfigList = (params) => {
|
||||
return request.get({
|
||||
url: '/admin-api/system/wx-robot-message-setting/list',
|
||||
params
|
||||
})
|
||||
}
|
||||
48
src/api/xjapplet/discount.js
Normal file
48
src/api/xjapplet/discount.js
Normal file
@@ -0,0 +1,48 @@
|
||||
import request from '@/config/axios'
|
||||
|
||||
export const getVipDiscountList = async (params) => {
|
||||
return await request.get({
|
||||
url: '/admin-api/applet/xunjia/member/discount/list',
|
||||
params: params
|
||||
})
|
||||
}
|
||||
|
||||
export const addVipDiscount = async (data) => {
|
||||
return await request.post({
|
||||
url: '/admin-api/applet/xunjia/member/discount/add',
|
||||
data
|
||||
})
|
||||
}
|
||||
|
||||
export const updateVipDiscount = async (data) => {
|
||||
return await request.post({
|
||||
url: '/admin-api/applet/xunjia/member/discount/update',
|
||||
data
|
||||
})
|
||||
}
|
||||
|
||||
export const deleteVipDiscount = async (id) => {
|
||||
return await request.delete({
|
||||
url: '/admin-api/applet/xunjia/member/discount/delete?discountId=' + id
|
||||
})
|
||||
}
|
||||
|
||||
export const getVipDiscountOptions = async () => {
|
||||
return await request.get({
|
||||
url: '/admin-api/applet/xunjia/member/discount/simple-list'
|
||||
})
|
||||
}
|
||||
|
||||
export const getUserDiscountList = async (params) => {
|
||||
return await request.get({
|
||||
url: '/admin-api/applet/xunjia/user/discount/list',
|
||||
params: params
|
||||
})
|
||||
}
|
||||
|
||||
export const giveUserDiscount = async (data) => {
|
||||
return await request.post({
|
||||
url: '/admin-api/applet/xunjia/user/discount/send',
|
||||
data
|
||||
})
|
||||
}
|
||||
32
src/api/xjapplet/resell.js
Normal file
32
src/api/xjapplet/resell.js
Normal file
@@ -0,0 +1,32 @@
|
||||
import request from '@/config/axios'
|
||||
|
||||
export const getResellList = async (params) => {
|
||||
return await request.get({
|
||||
url: '/admin-api/applet/xunjia/distribution/list',
|
||||
params: params
|
||||
})
|
||||
}
|
||||
|
||||
export const addResell = async (data) => {
|
||||
return await request.post({
|
||||
url: '/admin-api/applet/xunjia/distribution/add',
|
||||
data
|
||||
})
|
||||
}
|
||||
|
||||
export const updateResell = async (data) => {
|
||||
return await request.put({
|
||||
url: '/admin-api/applet/xunjia/distribution/update',
|
||||
data
|
||||
})
|
||||
}
|
||||
export const deleteResell = async (id) => {
|
||||
return await request.delete({
|
||||
url: '/admin-api/applet/xunjia/distribution/delete?distributionId=' + id
|
||||
})
|
||||
}
|
||||
export const getResellDetail = async (id) => {
|
||||
return await request.get({
|
||||
url: '/admin-api/applet/xunjia/distribution/' + id
|
||||
})
|
||||
}
|
||||
49
src/api/xjapplet/vip.js
Normal file
49
src/api/xjapplet/vip.js
Normal file
@@ -0,0 +1,49 @@
|
||||
import request from '@/config/axios'
|
||||
|
||||
export const getUserMemberList = async (params) => {
|
||||
return await request.get({
|
||||
url: '/admin-api/applet/xunjia/user/member/list',
|
||||
params: params
|
||||
})
|
||||
}
|
||||
|
||||
export const giveUserMember = async (data) => {
|
||||
return await request.post({
|
||||
url: '/admin-api/applet/xunjia/user/member/add',
|
||||
data
|
||||
})
|
||||
}
|
||||
|
||||
export const getVipTypeList = async (params) => {
|
||||
return await request.get({
|
||||
url: '/admin-api/applet/xunjia/member/list',
|
||||
params: params
|
||||
})
|
||||
}
|
||||
|
||||
export const addVipType = async (data) => {
|
||||
return await request.post({
|
||||
url: '/admin-api/applet/xunjia/member/add',
|
||||
data
|
||||
})
|
||||
}
|
||||
|
||||
export const updateVipType = async (data) => {
|
||||
return await request.post({
|
||||
url: '/admin-api/applet/xunjia/member/update',
|
||||
data
|
||||
})
|
||||
}
|
||||
|
||||
export const deleteVipType = async (id) => {
|
||||
return await request.delete({
|
||||
url: '/admin-api/applet/xunjia/member/memberId?id=' + id
|
||||
})
|
||||
}
|
||||
|
||||
export const getVipTypeOptions = async (params) => {
|
||||
return await request.get({
|
||||
url: '/admin-api/applet/xunjia/member/simple-list',
|
||||
params: params
|
||||
})
|
||||
}
|
||||
67
src/api/xjapplet/vipdatabase.js
Normal file
67
src/api/xjapplet/vipdatabase.js
Normal file
@@ -0,0 +1,67 @@
|
||||
import request from '@/config/axios'
|
||||
|
||||
export const addJx = async (data) => {
|
||||
return await request.post({
|
||||
url: '/admin-api/applet/xunjia/question/jx/add',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
export const getJxQuestionList = async (params) => {
|
||||
return await request.get({
|
||||
url: '/admin-api/applet/xunjia/question/jx/list',
|
||||
params: params
|
||||
})
|
||||
}
|
||||
|
||||
export const delJxData = async (id) => {
|
||||
return await request.delete({
|
||||
url: `/admin-api/applet/xunjia/question/jx/delete?id=${id}`
|
||||
})
|
||||
}
|
||||
|
||||
export const addMj = async (data) => {
|
||||
return await request.post({
|
||||
url: '/admin-api/applet/xunjia/secret/add',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
export const getMjList = async (params) => {
|
||||
return await request.get({
|
||||
url: '/admin-api/applet/xunjia/secret/list',
|
||||
params: params
|
||||
})
|
||||
}
|
||||
|
||||
export const delMj = async (secretId) => {
|
||||
return await request.delete({
|
||||
url: `/admin-api/applet/xunjia/secret/delete?secretId=${secretId}`
|
||||
})
|
||||
}
|
||||
|
||||
export const clearMj = async (secretId) => {
|
||||
return await request.delete({
|
||||
url: `/admin-api/applet/xunjia/secret/clear?secretId=${secretId}`
|
||||
})
|
||||
}
|
||||
|
||||
export const getMjQuestionList = async (params) => {
|
||||
return await request.get({
|
||||
url: '/admin-api/applet/xunjia/secret/question/list',
|
||||
params: params
|
||||
})
|
||||
}
|
||||
|
||||
export const addMjQuestion = async (data) => {
|
||||
return await request.post({
|
||||
url: '/admin-api/applet/xunjia/secret/question/add',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
export const delMjQuestion = async (id) => {
|
||||
return await request.delete({
|
||||
url: `/admin-api/applet/xunjia/secret/question/delete?id=${id}`
|
||||
})
|
||||
}
|
||||
48
src/api/xjapplet/xjdatabase.js
Normal file
48
src/api/xjapplet/xjdatabase.js
Normal file
@@ -0,0 +1,48 @@
|
||||
import request from '@/config/axios'
|
||||
export const searchQuestion = async (param) => {
|
||||
return await request.get({
|
||||
url: '/admin-api/applet/xunjia/question/list',
|
||||
params: param
|
||||
})
|
||||
}
|
||||
|
||||
export const updateQuestion = async (data) => {
|
||||
return await request.put({
|
||||
url: '/admin-api/applet/xunjia/question/update',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
export const addQuestion = async (data) => {
|
||||
return await request.post({
|
||||
url: '/admin-api/applet/xunjia/question/add',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
export const deleteQuestion = async (id) => {
|
||||
return await request.delete({
|
||||
url: '/admin-api/applet/xunjia/question/delete?id=' + id
|
||||
})
|
||||
}
|
||||
|
||||
export const uploadFile = async (data) => {
|
||||
return await request.post({
|
||||
url: '/admin-api/applet/xunjia/question/upload',
|
||||
data: data
|
||||
})
|
||||
}
|
||||
|
||||
export const getQuestionSort = async (param) => {
|
||||
return await request.get({
|
||||
url: '/admin-api/applet/xunjia/question/sort/list',
|
||||
params: param
|
||||
})
|
||||
}
|
||||
|
||||
export const getMjList = async (params) => {
|
||||
return await request.get({
|
||||
url: 'http://localhost/tiku-api/tiku/xunjia/secret/list',
|
||||
params: params
|
||||
})
|
||||
}
|
||||
BIN
src/assets/imgs/login2.gif
Normal file
BIN
src/assets/imgs/login2.gif
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 309 KiB |
BIN
src/assets/imgs/shisong.jpg
Normal file
BIN
src/assets/imgs/shisong.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 192 KiB |
@@ -22,13 +22,13 @@ onMounted(() => {
|
||||
appStore.setCssVarTheme()
|
||||
})
|
||||
|
||||
const { width } = useWindowSize()
|
||||
const { width, height } = useWindowSize()
|
||||
|
||||
// 监听窗口变化
|
||||
watch(
|
||||
() => width.value,
|
||||
(width: number) => {
|
||||
if (width < 768) {
|
||||
if (width < 768 || height.value < 450) {
|
||||
!appStore.getMobile ? appStore.setMobile(true) : undefined
|
||||
setCssVar('--left-menu-min-width', '0')
|
||||
appStore.setCollapse(true)
|
||||
|
||||
@@ -24,7 +24,27 @@ const props = defineProps({
|
||||
default: () => undefined
|
||||
},
|
||||
readonly: propTypes.bool.def(false),
|
||||
modelValue: propTypes.string.def('')
|
||||
modelValue: propTypes.string.def(''),
|
||||
toolbarConfig: {
|
||||
type: Object,
|
||||
default: () => ({
|
||||
excludeKeys: [
|
||||
'insertVideo', // 网络视频
|
||||
'insertImage', // 网络图片
|
||||
'insertLink', // 链接
|
||||
'codeBlock', // 代码块
|
||||
'headerSelect', // 标题
|
||||
'blockquote', // 引用
|
||||
'fontFamily', // 字体
|
||||
'todo', // 代办
|
||||
'group-indent', // 缩进
|
||||
'emotion', // 表情
|
||||
'undo', // 撤销
|
||||
'redo', // 重做
|
||||
'fullScreen'
|
||||
]
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
const emit = defineEmits(['change', 'update:modelValue'])
|
||||
@@ -212,24 +232,6 @@ const editorStyle = computed(() => {
|
||||
}
|
||||
})
|
||||
|
||||
const toolbarConfig = ref({
|
||||
excludeKeys: [
|
||||
'insertVideo', // 网络视频
|
||||
'insertImage', // 网络图片
|
||||
'insertLink', // 链接
|
||||
'codeBlock', // 代码块
|
||||
'headerSelect', // 标题
|
||||
'blockquote', // 引用
|
||||
'fontFamily', // 字体
|
||||
'todo', // 代办
|
||||
'group-indent', // 缩进
|
||||
'emotion', // 表情
|
||||
'undo', // 撤销
|
||||
'redo', // 重做
|
||||
'fullScreen'
|
||||
]
|
||||
})
|
||||
|
||||
// 回调函数
|
||||
const handleChange = (editor: IDomEditor) => {
|
||||
emit('change', editor)
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
v-model:page-size="pageSize"
|
||||
:small="small"
|
||||
:background="true"
|
||||
:page-sizes="[10, 20, 30, 50, 100]"
|
||||
:page-sizes="[10, 50, 100, 500, 1000]"
|
||||
:pager-count="pagerCount"
|
||||
:total="total"
|
||||
class="float-right mt-15px mb-15px"
|
||||
|
||||
@@ -47,7 +47,7 @@ const props = defineProps({
|
||||
// fileType: propTypes.array.def(['doc', 'xls', 'ppt', 'txt', 'pdf']), // 文件类型, 例如['png', 'jpg', 'jpeg']
|
||||
fileType: propTypes.array.def([]),
|
||||
accept: propTypes.string.def('*'),
|
||||
fileSize: propTypes.number.def(5), // 大小限制(MB)
|
||||
fileSize: propTypes.number.def(100), // 大小限制(MB)
|
||||
limit: propTypes.number.def(5), // 数量限制
|
||||
autoUpload: propTypes.bool.def(true), // 自动上传
|
||||
drag: propTypes.bool.def(false), // 拖拽上传
|
||||
|
||||
@@ -132,6 +132,9 @@ service.interceptors.response.use(
|
||||
throw new Error()
|
||||
}
|
||||
const { t } = useI18n()
|
||||
// if (data.code === undefined) {
|
||||
// return response
|
||||
// }
|
||||
// 未设置状态码则默认成功状态
|
||||
const code = data.code || result_code
|
||||
// 二进制数据则直接返回
|
||||
|
||||
@@ -13,7 +13,12 @@ export const useMessage = () => {
|
||||
},
|
||||
// 成功消息
|
||||
success(content: string) {
|
||||
ElMessage.success(content)
|
||||
// ElMessage.success(content)
|
||||
ElMessage({
|
||||
message: content,
|
||||
duration: 1500,
|
||||
type: 'success'
|
||||
})
|
||||
},
|
||||
// 警告消息
|
||||
warning(content: string) {
|
||||
|
||||
@@ -45,7 +45,7 @@ router.beforeEach(async (to, from, next) => {
|
||||
const dictStore = useDictStoreWithOut()
|
||||
const userStore = useUserStoreWithOut()
|
||||
const permissionStore = usePermissionStoreWithOut()
|
||||
if (!dictStore.getIsSetDict) {
|
||||
if (!dictStore.getIsSetDict && to.path != '/nm-detail') {
|
||||
await dictStore.setDictMap()
|
||||
}
|
||||
if (!userStore.getIsSetUser) {
|
||||
|
||||
@@ -106,7 +106,6 @@ const remainingRouter: AppRouteRecordRaw[] = [
|
||||
}
|
||||
]
|
||||
},
|
||||
|
||||
// {
|
||||
// path: '/dict',
|
||||
// component: Layout,
|
||||
@@ -134,7 +133,7 @@ const remainingRouter: AppRouteRecordRaw[] = [
|
||||
// path: '/Basic',
|
||||
// component: Layout,
|
||||
// name: 'Basic',
|
||||
// meta: {},
|
||||
// meta: { title: '菜单管理' },
|
||||
// redirect: '/Basic/menu',
|
||||
// children: [
|
||||
// {
|
||||
|
||||
@@ -69,3 +69,42 @@
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
.el-table {
|
||||
.caret-wrapper {
|
||||
width: 16px;
|
||||
}
|
||||
.sort-caret {
|
||||
left: 2px;
|
||||
}
|
||||
}
|
||||
|
||||
.el-table--default .cell {
|
||||
padding: 0 5px;
|
||||
}
|
||||
|
||||
.flex-radio{
|
||||
display: flex;
|
||||
width: 100%;
|
||||
align-items: center;
|
||||
line-height: 40px;
|
||||
height: 40px;
|
||||
.el-radio__label {
|
||||
display: flex;
|
||||
width: 100%;
|
||||
align-items: center;
|
||||
font-size: 16px;
|
||||
}
|
||||
}
|
||||
|
||||
/* 去除 Chrome、Safari、Edge、Opera 中的指示器 */
|
||||
.el-input__inner::-webkit-outer-spin-button,
|
||||
.el-input__inner::-webkit-inner-spin-button {
|
||||
-webkit-appearance: none;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
/* 去除 Firefox 中的指示器 */
|
||||
.el-input__inner[type='number'] {
|
||||
-moz-appearance: textfield;
|
||||
}
|
||||
@@ -74,13 +74,13 @@ export const SystemUserSocialTypeEnum = {
|
||||
type: 20,
|
||||
source: 'dingtalk',
|
||||
img: 'https://s1.ax1x.com/2022/05/22/OzMDRs.png'
|
||||
},
|
||||
WECHAT_ENTERPRISE: {
|
||||
title: '企业微信',
|
||||
type: 30,
|
||||
source: 'wechat_enterprise',
|
||||
img: 'https://s1.ax1x.com/2022/05/22/OzMrzn.png'
|
||||
}
|
||||
// WECHAT_ENTERPRISE: {
|
||||
// title: '企业微信',
|
||||
// type: 30,
|
||||
// source: 'wechat_enterprise',
|
||||
// img: 'https://s1.ax1x.com/2022/05/22/OzMrzn.png'
|
||||
// }
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
<!-- 列表 -->
|
||||
<el-table v-loading="loading" :data="list" row-key="id" default-expand-all border>
|
||||
<el-table-column prop="name" label="部门名称" />
|
||||
<el-table-column prop="leader" label="负责人" width="120" />
|
||||
<el-table-column prop="leaderUserName" label="负责人" width="120" />
|
||||
<el-table-column prop="sort" label="排序" width="200" />
|
||||
<el-table-column prop="status" label="状态" width="100">
|
||||
<template #default="{ row }">
|
||||
|
||||
@@ -7,6 +7,9 @@
|
||||
<!-- <el-tab-pane label="销售提成" :name="10" v-if="checkPermi(['basic:setting:comission'])">
|
||||
<BSSalerComission v-if="tabIndex == 10" />
|
||||
</el-tab-pane> -->
|
||||
<!-- <el-tab-pane label="微信机器人" :name="20">
|
||||
<BSSalerComission v-if="tabIndex == 20" />
|
||||
</el-tab-pane> -->
|
||||
</el-tabs>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -84,7 +84,7 @@
|
||||
<!-- 表单弹窗:添加/修改 -->
|
||||
<MenuForm ref="formRef" :appList="appList" @success="getList" />
|
||||
</template>
|
||||
<script lang="ts" name="SystemMenu" setup>
|
||||
<script lang="ts" name="Menu" setup>
|
||||
import { handleTree } from '@/utils/tree'
|
||||
import * as MenuApi from '@/api/system/menu'
|
||||
import MenuForm from './MenuForm.vue'
|
||||
|
||||
@@ -9,6 +9,18 @@
|
||||
:label="col.label"
|
||||
:width="col.width"
|
||||
/>
|
||||
<el-table-column label="状态" key="status">
|
||||
<template #default="scope">
|
||||
<el-switch
|
||||
v-model="scope.row.status"
|
||||
:active-value="0"
|
||||
active-text="在职"
|
||||
inactive-text="离职"
|
||||
:inactive-value="1"
|
||||
disabled
|
||||
/>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<Pagination
|
||||
v-model:limit="pageSize"
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
v-loading="formLoading"
|
||||
:model="formData"
|
||||
:rules="formRules"
|
||||
label-width="80px"
|
||||
label-width="100px"
|
||||
>
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="12">
|
||||
@@ -85,6 +85,11 @@
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="12" :offset="0">
|
||||
<el-form-item label="微信号" prop="wxAlias">
|
||||
<el-input v-model="formData.wxAlias" placeholder="请输入微信号" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12" :offset="0">
|
||||
<el-form-item label="入职日期" prop="hireDate">
|
||||
<el-date-picker
|
||||
@@ -97,6 +102,23 @@
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="12" :offset="0">
|
||||
<el-form-item label="钉钉手机号">
|
||||
<el-input
|
||||
v-model="formData.ddPhone"
|
||||
placeholder="请输入钉钉手机号"
|
||||
clearable
|
||||
@blur="getDDId"
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12" :offset="0">
|
||||
<el-form-item label="钉钉Id" prop="dingUserId">
|
||||
<el-input v-model="formData.dingUserId" disabled />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="24">
|
||||
<el-form-item label="备注">
|
||||
@@ -127,7 +149,7 @@ const dialogVisible = ref(false) // 弹窗的是否展示
|
||||
const dialogTitle = ref('') // 弹窗的标题
|
||||
const formLoading = ref(false) // 表单的加载中:1)修改时的数据加载;2)提交的按钮禁用
|
||||
const formType = ref('') // 表单的类型:create - 新增;update - 修改
|
||||
const formData = ref({
|
||||
const formData = ref<any>({
|
||||
nickname: '',
|
||||
deptId: '',
|
||||
mobile: '',
|
||||
@@ -140,7 +162,8 @@ const formData = ref({
|
||||
status: CommonStatusEnum.ENABLE,
|
||||
roleIds: [],
|
||||
hireDate: '',
|
||||
isNewUserConfirm: false
|
||||
isNewUserConfirm: false,
|
||||
wxAlias: ''
|
||||
})
|
||||
const formRules = ref<any>({
|
||||
username: [{ required: true, message: '登录账号不能为空', trigger: 'blur' }],
|
||||
@@ -228,8 +251,21 @@ const resetForm = () => {
|
||||
status: CommonStatusEnum.ENABLE,
|
||||
roleIds: [],
|
||||
hireDate: formatDate(new Date(), 'YYYY-MM-DD'),
|
||||
isNewUserConfirm: false
|
||||
isNewUserConfirm: false,
|
||||
wxAlias: ''
|
||||
}
|
||||
formRef.value?.resetFields()
|
||||
}
|
||||
|
||||
async function getDDId() {
|
||||
try {
|
||||
if (!formData.value.ddPhone) {
|
||||
formData.value.dingUserId = undefined
|
||||
return
|
||||
}
|
||||
const data = await UserApi.getDDIdByPhone({ mobile: formData.value.ddPhone })
|
||||
formData.value.dingUserId = data
|
||||
} finally {
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -6,8 +6,8 @@
|
||||
</el-col>
|
||||
<el-col :span="20" :xs="24">
|
||||
<!-- 搜索 -->
|
||||
<el-form :model="queryParams" ref="queryFormRef" inline label-width="68px">
|
||||
<el-form-item label="姓名" prop="nickname">
|
||||
<el-form :model="queryParams" ref="queryFormRef" inline label-width="0">
|
||||
<el-form-item>
|
||||
<el-input
|
||||
v-model="queryParams.nickname"
|
||||
placeholder="请输入姓名"
|
||||
@@ -16,7 +16,7 @@
|
||||
class="!w-240px"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="手机号码" prop="mobile">
|
||||
<el-form-item>
|
||||
<el-input
|
||||
v-model="queryParams.mobile"
|
||||
placeholder="请输入手机号码"
|
||||
@@ -25,6 +25,12 @@
|
||||
class="!w-240px"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-radio-group v-model="queryParams.status" @change="handleQuery">
|
||||
<el-radio :label="0"> 启用 </el-radio>
|
||||
<el-radio :label="1"> 禁用 </el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button @click="handleQuery" v-hasPermi="['basic:employee:search']">搜索</el-button>
|
||||
<el-button @click="resetQuery" v-hasPermi="['basic:employee:reset']">重置</el-button>
|
||||
@@ -39,29 +45,45 @@
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<el-table v-loading="loading" :data="list">
|
||||
<el-table-column label="用户编号" key="id" prop="id" />
|
||||
<el-table-column label="用户编号" key="id" prop="id" width="100" />
|
||||
<el-table-column label="登录账号" prop="username" />
|
||||
<el-table-column label="用户姓名" prop="nickname" />
|
||||
<el-table-column label="部门" key="deptName" prop="deptName" />
|
||||
<el-table-column label="部门" key="deptName" prop="deptName" width="100" />
|
||||
<el-table-column label="手机号码" prop="mobile" width="120" />
|
||||
<el-table-column label="状态" key="status">
|
||||
<el-table-column label="微信号" prop="wxAlias" min-width="80" />
|
||||
<el-table-column label="钉钉号" prop="dingUserId" min-width="80" />
|
||||
<el-table-column label="在职状态" key="hireStatus" width="120">
|
||||
<template #default="scope">
|
||||
<el-switch
|
||||
v-model="scope.row.status"
|
||||
v-model="scope.row.hireStatus"
|
||||
:active-value="0"
|
||||
:inactive-value="1"
|
||||
active-text="在职"
|
||||
inactive-text="离职"
|
||||
size="small"
|
||||
v-hasPermi="['basic:employee:update']"
|
||||
@change="handleStatusChange(scope.row)"
|
||||
/>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column
|
||||
label="创建时间"
|
||||
prop="createTime"
|
||||
width="180"
|
||||
:formatter="dateFormatter"
|
||||
<el-table-column label="启用状态" key="status" width="120">
|
||||
<template #default="scope">
|
||||
<el-switch
|
||||
v-model="scope.row.status"
|
||||
:active-value="0"
|
||||
:inactive-value="1"
|
||||
active-text="启用"
|
||||
inactive-text="禁用"
|
||||
size="small"
|
||||
:disabled="scope.row.hireStatus == 1"
|
||||
v-hasPermi="['basic:employee:update']"
|
||||
@change="handleUseChange(scope.row)"
|
||||
/>
|
||||
<el-table-column label="操作" width="260">
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="入职日期" prop="hireDate" width="120" :formatter="dateFormatter" />
|
||||
<el-table-column label="离职日期" prop="" width="120" :formatter="dateFormatter" />
|
||||
<el-table-column label="操作" width="180" fixed="right">
|
||||
<template #default="scope">
|
||||
<el-button
|
||||
type="primary"
|
||||
@@ -114,13 +136,14 @@ const { t } = useI18n() // 国际化
|
||||
const loading = ref(true) // 列表的加载中
|
||||
const total = ref(0) // 列表的总页数
|
||||
const list = ref([]) // 列表的数
|
||||
const queryParams = reactive({
|
||||
const queryParams = ref({
|
||||
pageNo: 1,
|
||||
pageSize: 10,
|
||||
pageSize: 20,
|
||||
username: undefined,
|
||||
nickname: undefined,
|
||||
mobile: undefined,
|
||||
deptId: undefined
|
||||
deptId: undefined,
|
||||
status: undefined
|
||||
})
|
||||
const queryFormRef = ref() // 搜索的表单
|
||||
|
||||
@@ -128,7 +151,7 @@ const queryFormRef = ref() // 搜索的表单
|
||||
const getList = async () => {
|
||||
loading.value = true
|
||||
try {
|
||||
const data = await UserApi.getUserPage(queryParams)
|
||||
const data = await UserApi.getUserPage(queryParams.value)
|
||||
list.value = data.list
|
||||
total.value = data.total
|
||||
} finally {
|
||||
@@ -138,19 +161,28 @@ const getList = async () => {
|
||||
|
||||
/** 搜索按钮操作 */
|
||||
const handleQuery = () => {
|
||||
queryParams.pageNo = 1
|
||||
queryParams.value.pageNo = 1
|
||||
getList()
|
||||
}
|
||||
|
||||
/** 重置按钮操作 */
|
||||
const resetQuery = () => {
|
||||
queryFormRef.value?.resetFields()
|
||||
queryParams.value = {
|
||||
pageNo: 1,
|
||||
pageSize: 20,
|
||||
username: undefined,
|
||||
nickname: undefined,
|
||||
mobile: undefined,
|
||||
deptId: undefined,
|
||||
status: undefined
|
||||
}
|
||||
handleQuery()
|
||||
}
|
||||
|
||||
/** 处理部门被点击 */
|
||||
const handleDeptNodeClick = async (row) => {
|
||||
queryParams.deptId = row.id
|
||||
queryParams.value.deptId = row.id
|
||||
await getList()
|
||||
}
|
||||
|
||||
@@ -160,14 +192,49 @@ const openForm = (type: string, id?: number) => {
|
||||
formRef.value.open(type, id)
|
||||
}
|
||||
|
||||
/** 修改用户状态 */
|
||||
/** 修改在职状态 */
|
||||
const handleStatusChange = async (row: UserApi.UserVO) => {
|
||||
try {
|
||||
// 修改状态的二次确认
|
||||
const text = row.status === CommonStatusEnum.ENABLE ? '启用' : '停用'
|
||||
await message.confirm('确认要"' + text + '""' + row.username + '"用户吗?')
|
||||
const text = row.hireStatus === CommonStatusEnum.ENABLE ? '入职' : '离职'
|
||||
let result = { value: '' }
|
||||
if (text == '入职') {
|
||||
await message.confirm('确认要"' + text + '""' + row.nickname + '"吗?')
|
||||
} else {
|
||||
result = await message.prompt(
|
||||
'请输入年月日,如2024-01-01',
|
||||
'确认要"' + text + '""' + row.nickname + '"用户吗?'
|
||||
)
|
||||
const regex = /^\d{4}-\d{2}-\d{2}$/
|
||||
|
||||
if (!regex.test(result.value)) {
|
||||
message.error('请输入离职日期')
|
||||
throw new Error()
|
||||
}
|
||||
}
|
||||
// 发起修改状态
|
||||
await UserApi.updateUserStatus(row.id, row.status)
|
||||
await UserApi.updateUserStatus(row.id, row.status, result.value, row.hireStatus)
|
||||
message.success('修改成功')
|
||||
// 刷新列表
|
||||
await getList()
|
||||
} catch {
|
||||
// 取消后,进行恢复按钮
|
||||
row.hireStatus =
|
||||
row.hireStatus === CommonStatusEnum.ENABLE
|
||||
? CommonStatusEnum.DISABLE
|
||||
: CommonStatusEnum.ENABLE
|
||||
}
|
||||
}
|
||||
|
||||
// 修改启用状态
|
||||
const handleUseChange = async (row: UserApi.UserVO) => {
|
||||
try {
|
||||
// 修改状态的二次确认
|
||||
const text = row.status === CommonStatusEnum.ENABLE ? '启用' : '禁用'
|
||||
await message.confirm('确认要"' + text + '""' + row.nickname + '"吗?')
|
||||
// 发起修改状态
|
||||
await UserApi.updateUserStatus(row.id, row.status, row.retireDate, row.hireStatus)
|
||||
message.success('修改成功')
|
||||
// 刷新列表
|
||||
await getList()
|
||||
} catch {
|
||||
|
||||
7
src/views/Basic/WxRobot/Record.vue
Normal file
7
src/views/Basic/WxRobot/Record.vue
Normal file
@@ -0,0 +1,7 @@
|
||||
<template>
|
||||
<div> record </div>
|
||||
</template>
|
||||
|
||||
<script setup name="WXRecord"></script>
|
||||
|
||||
<style lang="scss" scoped></style>
|
||||
228
src/views/Basic/WxRobot/Setting.vue
Normal file
228
src/views/Basic/WxRobot/Setting.vue
Normal file
@@ -0,0 +1,228 @@
|
||||
<template>
|
||||
<div>
|
||||
<el-table v-loading="loading" :data="tableList" border stripe>
|
||||
<el-table-column v-for="col in columns" :prop="col.prop" :key="col.prop" :label="col.label" />
|
||||
<el-table-column label="发送方式" min-width="150">
|
||||
<template #default="{ row }">
|
||||
<el-checkbox-group v-model="row.sendType" size="small" @change="rowChange(row)">
|
||||
<el-checkbox label="微信"> 微信 </el-checkbox>
|
||||
<el-checkbox label="微信群"> 微信群 </el-checkbox>
|
||||
</el-checkbox-group>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="群二维码" width="100">
|
||||
<template #default="{ row }">
|
||||
<el-upload
|
||||
ref="rewardFile"
|
||||
action="#"
|
||||
:limit="1"
|
||||
accept=".jpg,.jpeg,.png"
|
||||
:disabled="loading"
|
||||
:show-file-list="false"
|
||||
:before-upload="fileBeforeUpload"
|
||||
:http-request="(data) => rewardUpload(data, row.id)"
|
||||
>
|
||||
<el-button type="primary" size="small">
|
||||
<Icon icon="ep:upload" class="mr-5px" size="small" /> 点击上传
|
||||
</el-button>
|
||||
</el-upload>
|
||||
<!-- <UploadImg v-model="row.wxQrCode" /> -->
|
||||
|
||||
<!-- <img :src="row.wxCode" width="80px" height="80px" alt="" /> -->
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="微信群名称" prop="wxGroup" min-width="150" />
|
||||
<el-table-column label="操作" width="100">
|
||||
<template #default="{ row }">
|
||||
<!-- <div>
|
||||
<el-button
|
||||
v-if="row.sendType.includes('微信群')"
|
||||
type="primary"
|
||||
style="padding: 5px 0"
|
||||
text
|
||||
@click="row.edit = true"
|
||||
>
|
||||
修改群名称
|
||||
</el-button>
|
||||
</div> -->
|
||||
<div v-if="['每日', '每月'].includes(row.sendFrequency)">
|
||||
<el-button type="primary" style="padding: 5px 0" text @click="changeSendTime(row)">
|
||||
修改发送时间
|
||||
</el-button>
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
|
||||
<Dialog
|
||||
:title="`修改【${sendTimeInfo.messageName}】发送时间`"
|
||||
v-model="showSendTime"
|
||||
width="500px"
|
||||
>
|
||||
<el-text type="danger">
|
||||
<span v-if="sendTimeInfo.sendFrequency == '每日'">
|
||||
请按照时间格式输入,多个时间以“、”分割,如09:00、15:00、18:00
|
||||
</span>
|
||||
<span v-else-if="sendTimeInfo.sendFrequency == '每月'">
|
||||
请按照"日期-时间"格式输入,多个日期以“、”分割,如26-18:00、28-14:00、30-15:00、30-18:00
|
||||
</span>
|
||||
</el-text>
|
||||
<el-input
|
||||
type="textarea"
|
||||
class="mt-20px mb-20px"
|
||||
:autosize="{ minRows: 3 }"
|
||||
v-model="sendTimeInfo.sendTime"
|
||||
placeholder="请输入"
|
||||
clearable
|
||||
/>
|
||||
<template #footer>
|
||||
<el-button :disabled="formLoading" type="primary" @click="submitSendTime">确 定</el-button>
|
||||
<el-button @click="showSendTime = false">取 消</el-button>
|
||||
</template>
|
||||
</Dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup name="WXSetting">
|
||||
import * as WechatApi from '@/api/system/wechat'
|
||||
|
||||
const message = useMessage() // 消息弹窗
|
||||
|
||||
const loading = ref(false)
|
||||
const tableList = ref([])
|
||||
const columns = [
|
||||
{
|
||||
label: '消息名称',
|
||||
prop: 'messageName'
|
||||
},
|
||||
{
|
||||
label: '解释说明',
|
||||
prop: 'description'
|
||||
},
|
||||
{
|
||||
label: '发送频率',
|
||||
prop: 'sendFrequency'
|
||||
},
|
||||
{
|
||||
label: '发送时间',
|
||||
prop: 'sendTime'
|
||||
}
|
||||
]
|
||||
|
||||
onMounted(() => {
|
||||
getTableList()
|
||||
})
|
||||
|
||||
async function getTableList() {
|
||||
try {
|
||||
loading.value = true
|
||||
const list = await WechatApi.getWechatConfigList()
|
||||
tableList.value = list.map((it) => ({ ...it, sendType: it.sendType || [] }))
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
let timer = ref(null)
|
||||
const lastId = ref('')
|
||||
function rowChange(row) {
|
||||
try {
|
||||
lastId.value == row.id && clearTimeout(timer.value)
|
||||
timer.value = setTimeout(async () => {
|
||||
lastId.value = row.id
|
||||
await WechatApi.updateWechatGroup({
|
||||
id: row.id,
|
||||
sendType: row.sendType,
|
||||
wxGroup: row.wxGroup
|
||||
})
|
||||
// message.success('修改成功')
|
||||
}, 1500)
|
||||
} finally {
|
||||
}
|
||||
}
|
||||
|
||||
const showSendTime = ref(false)
|
||||
const sendTimeInfo = ref({})
|
||||
function changeSendTime(row) {
|
||||
showSendTime.value = true
|
||||
sendTimeInfo.value = { ...row }
|
||||
}
|
||||
|
||||
const formLoading = ref(false)
|
||||
|
||||
async function submitSendTime() {
|
||||
// 校验时间是否有效
|
||||
const arr = sendTimeInfo.value.sendTime ? sendTimeInfo.value.sendTime.split('、') : []
|
||||
|
||||
if (sendTimeInfo.value.sendFrequency == '每日') {
|
||||
// 正则校验是否HH:MM
|
||||
if (arr.some((it) => !/^([01]?[0-9]|2[0-3]):[0-5][0-9]$/.test(it))) {
|
||||
message.error('请检查输入格式是否正确!!!')
|
||||
return
|
||||
} else {
|
||||
try {
|
||||
formLoading.value = true
|
||||
await WechatApi.updateWechatSendTime({
|
||||
id: sendTimeInfo.value.id,
|
||||
sendTime: arr.join('、')
|
||||
})
|
||||
message.success('修改成功!')
|
||||
getTableList()
|
||||
showSendTime.value = false
|
||||
} finally {
|
||||
formLoading.value = false
|
||||
}
|
||||
}
|
||||
} else if (sendTimeInfo.value.sendFrequency == '每月') {
|
||||
// 正则校验是否【1-31】-HH:MM
|
||||
const reg = /^([1-9]|[1-2]\d|3[01])-([01]?[0-9]|2[0-3]):[0-5][0-9]$/
|
||||
if (arr.length > 0 && arr.some((it) => !reg.test(it))) {
|
||||
message.error('请检查输入格式是否正确!!!')
|
||||
return
|
||||
} else {
|
||||
try {
|
||||
formLoading.value = true
|
||||
await WechatApi.updateWechatSendTime({
|
||||
id: sendTimeInfo.value.id,
|
||||
sendTime: arr.join('、')
|
||||
})
|
||||
message.success('修改成功!')
|
||||
getTableList()
|
||||
showSendTime.value = false
|
||||
} finally {
|
||||
formLoading.value = false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const fileBeforeUpload = (file) => {
|
||||
let format = '.' + file.name.split('.')[1]
|
||||
if (!['.jpg', '.png', 'jpeg'].includes(format)) {
|
||||
message.error(`请上传指定格式".jpg,.jpeg,.png"文件`)
|
||||
return false
|
||||
}
|
||||
let isRightSize = file.size / 1024 / 1024 < 20
|
||||
if (!isRightSize) {
|
||||
message.error('文件大小超过 20MB')
|
||||
}
|
||||
return isRightSize
|
||||
}
|
||||
|
||||
const fd = ref(new FormData())
|
||||
|
||||
async function rewardUpload(data, id) {
|
||||
fd.value.append('file', data.file)
|
||||
fd.value.append('id', id)
|
||||
// 上传接口,成功后得到微信群名
|
||||
try {
|
||||
// 上传文件
|
||||
setTimeout(() => {
|
||||
getTableList()
|
||||
}, 2000)
|
||||
} finally {
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped></style>
|
||||
@@ -1,62 +0,0 @@
|
||||
// import { CrudSchema } from '@/hooks/web/useCrudSchemas'
|
||||
import { dateFormatter } from '@/utils/formatTime'
|
||||
|
||||
const statusOptions = [
|
||||
{ label: '发送成功', value: 1 },
|
||||
{ label: '发送失败', value: 2 },
|
||||
{ label: '排队中', value: 3 },
|
||||
{ label: '微信端卡住', value: 4 }
|
||||
]
|
||||
|
||||
// CrudSchema:https://doc.iocoder.cn/vue3/crud-schema/
|
||||
const crudSchemas = reactive([
|
||||
{
|
||||
label: '发送对象',
|
||||
field: 'sendUser',
|
||||
isSearch: true,
|
||||
isTable: true
|
||||
},
|
||||
{
|
||||
label: '发送内容',
|
||||
field: 'content',
|
||||
isSearch: false,
|
||||
isTable: true
|
||||
},
|
||||
{
|
||||
label: '发送状态',
|
||||
field: 'status',
|
||||
isSearch: true,
|
||||
isTable: true,
|
||||
search: {
|
||||
component: 'Select',
|
||||
api: () => statusOptions,
|
||||
componentProps: {
|
||||
optionsAlias: {
|
||||
labelField: 'label',
|
||||
valueField: 'value'
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
label: '发送时间',
|
||||
field: 'createTime',
|
||||
isSearch: true,
|
||||
isTable: true,
|
||||
formatter: dateFormatter,
|
||||
detail: {
|
||||
dateFormat: 'YYYY-MM-DD'
|
||||
},
|
||||
search: {
|
||||
component: 'DatePicker',
|
||||
componentProps: {
|
||||
type: 'daterange',
|
||||
format: 'YYYY-MM-DD',
|
||||
valueFormat: 'YYYY-MM-DD',
|
||||
startPlaceholder: '发送时间',
|
||||
endPlaceholder: '发送时间'
|
||||
}
|
||||
}
|
||||
}
|
||||
])
|
||||
export const { allSchemas } = useCrudSchemas(crudSchemas)
|
||||
@@ -1,77 +1,19 @@
|
||||
<template>
|
||||
<el-tabs v-model="tabIndex" type="border-card">
|
||||
<el-tab-pane label="微信消息记录" :name="0">
|
||||
<!-- 搜索工作栏 -->
|
||||
<Search
|
||||
:schema="allSchemas.searchSchema"
|
||||
labelWidth="0"
|
||||
@search="setSearchParams"
|
||||
@reset="setSearchParams"
|
||||
/>
|
||||
<!-- 列表 -->
|
||||
<SSTable
|
||||
class="mt-20px"
|
||||
v-model:tableObject="tableObject"
|
||||
:tableColumns="allSchemas.tableColumns"
|
||||
@get-list="getTableList"
|
||||
>
|
||||
<el-table-column
|
||||
v-for="item in allSchemas.tableColumns"
|
||||
:key="item.field"
|
||||
:prop="item.field"
|
||||
:label="item.label"
|
||||
:fixed="item.fixed"
|
||||
min-width="150px"
|
||||
showOverflowTooltip
|
||||
/>
|
||||
<el-table-column label="操作" width="150px" fixed="right">
|
||||
<template #default="{ row }">
|
||||
<el-button type="primary" link @click="sendMsg(row)">再次发送</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</SSTable>
|
||||
<!-- <el-tab-pane label="微信消息记录" :name="0">
|
||||
<Record />
|
||||
</el-tab-pane> -->
|
||||
<el-tab-pane label="消息配置" :name="1">
|
||||
<Setting />
|
||||
</el-tab-pane>
|
||||
</el-tabs>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { allSchemas } from './index.data.js'
|
||||
<script setup name="WXBoot">
|
||||
// import Record from './Record.vue'
|
||||
import Setting from './Setting.vue'
|
||||
|
||||
const tabIndex = ref(0)
|
||||
|
||||
const tableObject = ref({
|
||||
tableList: [],
|
||||
loading: false,
|
||||
total: 1,
|
||||
pageSize: 20,
|
||||
currentPage: 1
|
||||
})
|
||||
|
||||
function setSearchParams() {
|
||||
tableObject.value.tableList = [
|
||||
{
|
||||
sendUser: '测试',
|
||||
content: '您今日有10条待跟进的线索',
|
||||
status: '发送成功',
|
||||
createTime: '2024-04-25 12:00:00'
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
function getTableList() {
|
||||
tableObject.value.tableList = [
|
||||
{
|
||||
sendUser: '测试',
|
||||
content: '您今日有10条待跟进的线索',
|
||||
status: '发送成功',
|
||||
createTime: '2024-04-25 12:00:00'
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
function sendMsg() {
|
||||
console.log('测试')
|
||||
}
|
||||
const tabIndex = ref(1)
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped></style>
|
||||
|
||||
@@ -83,6 +83,18 @@
|
||||
style="width: 240px"
|
||||
/>
|
||||
</el-form-item>
|
||||
<template v-if="appStore.getAppInfo?.instanceType == 1">
|
||||
<el-form-item>
|
||||
<el-select v-model="searchForm.area" placeholder="选择区域" clearable filterable>
|
||||
<el-option
|
||||
v-for="item in areaOptions"
|
||||
:key="item.id"
|
||||
:label="item.name"
|
||||
:value="item.id"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</template>
|
||||
<el-form-item>
|
||||
<el-button @click="handleSearch" v-hasPermi="['clue:order:after-sale-search']">
|
||||
查询
|
||||
@@ -104,8 +116,14 @@
|
||||
>
|
||||
<el-table-column type="selection" width="55" :selectable="(row) => row.state == 1" />
|
||||
<el-table-column prop="signId" label="成交单号" min-width="150px" />
|
||||
<el-table-column prop="name" label="线索名称" min-width="200px" />
|
||||
<el-table-column prop="phone" label="联系方式" min-width="150px" />
|
||||
<el-table-column prop="name" label="线索名称" min-width="150px" />
|
||||
<el-table-column prop="phone" label="联系方式" min-width="120px" />
|
||||
<el-table-column
|
||||
v-if="appStore.getAppInfo?.instanceType == 1"
|
||||
prop="area"
|
||||
label="区域"
|
||||
min-width="90"
|
||||
/>
|
||||
<el-table-column prop="reason" label="售后原因" min-width="150px" />
|
||||
<el-table-column prop="refundAmount" label="退款金额" min-width="90px" />
|
||||
<el-table-column prop="percentageDeductAmount" label="提成扣款" min-width="90px" />
|
||||
@@ -183,6 +201,7 @@
|
||||
<script setup name="AfterSales">
|
||||
import * as AfterSaleApi from '@/api/clue/afterSale'
|
||||
import { getSimpleUserList as getUserOption } from '@/api/system/user'
|
||||
import { getAreaSimpleList } from '@/api/school/setting/area'
|
||||
import { useUserStore } from '@/store/modules/user'
|
||||
|
||||
import DialogAfterSaleAudit from './DialogAfterSaleAudit.vue'
|
||||
@@ -191,10 +210,12 @@ import DialogBatchAudit from './DialogBatchAudit.vue'
|
||||
|
||||
import { removeNullField } from '@/utils'
|
||||
import { dateFormatter } from '@/utils/formatTime'
|
||||
import { useAppStore } from '@/store/modules/app'
|
||||
|
||||
const afterSaleAuditDialog = ref()
|
||||
|
||||
const userStore = useUserStore()
|
||||
const appStore = useAppStore()
|
||||
const message = useMessage() // 消息弹窗
|
||||
|
||||
const currentUserId = userStore.getUser.id
|
||||
@@ -284,7 +305,12 @@ function handleAudit(row) {
|
||||
afterSaleAuditDialog.value.open(row)
|
||||
}
|
||||
|
||||
const areaOptions = ref([])
|
||||
function getOptions() {
|
||||
// 区域
|
||||
getAreaSimpleList().then((data) => {
|
||||
areaOptions.value = data
|
||||
})
|
||||
getUserOption().then((data) => {
|
||||
userOptions.value = data
|
||||
})
|
||||
|
||||
130
src/views/Clue/Order/Comp/DialogAddProduct.vue
Normal file
130
src/views/Clue/Order/Comp/DialogAddProduct.vue
Normal file
@@ -0,0 +1,130 @@
|
||||
<template>
|
||||
<Dialog title="添加产品" v-model="show" width="800px">
|
||||
<el-form :model="form" ref="formRef" :rules="rules" label-width="80px">
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="12" :offset="0">
|
||||
<el-form-item label="成交产品" prop="productId">
|
||||
<el-select
|
||||
v-model="form.productId"
|
||||
placeholder="选择成交产品"
|
||||
filterable
|
||||
@change="form.specsId = undefined"
|
||||
>
|
||||
<el-option
|
||||
v-for="item in prodOptions"
|
||||
:key="item.productId"
|
||||
:label="item.productName"
|
||||
:value="item.productId"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12" :offset="0">
|
||||
<el-form-item label="产品规格" prop="specsId">
|
||||
<el-select
|
||||
v-model="form.specsId"
|
||||
placeholder="选择规格"
|
||||
filterable
|
||||
:disabled="!form.productId"
|
||||
>
|
||||
<el-option
|
||||
v-for="item in specsOptions(form.productId)"
|
||||
:key="item.specsId"
|
||||
:label="item.specsName"
|
||||
:value="item.specsId"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12" :offset="0">
|
||||
<el-form-item label="成交数量" prop="signNum">
|
||||
<el-input-number v-model="form.signNum" :min="1" :controls="false" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12" :offset="0">
|
||||
<el-form-item label="生产状态" prop="isProduced">
|
||||
<el-radio-group v-model="form.isProduced">
|
||||
<el-radio :label="0">待生产</el-radio>
|
||||
<el-radio :label="1">已生产</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="24" :offset="0">
|
||||
<el-form-item label="备注" prop="remark">
|
||||
<el-input
|
||||
type="textarea"
|
||||
:autoSize="{ minRows: 3 }"
|
||||
v-model="form.remark"
|
||||
placeholder="请输入备注"
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<span>
|
||||
<el-button @click="show = false">取 消</el-button>
|
||||
<el-button :disabled="formLoading" type="primary" @click="handleSave">保 存</el-button>
|
||||
</span>
|
||||
</template>
|
||||
</Dialog>
|
||||
</template>
|
||||
|
||||
<script setup name="DialogProduct">
|
||||
import { addOrderProduct } from '@/api/clue/sign'
|
||||
|
||||
const message = useMessage() // 消息弹窗
|
||||
|
||||
const specsOptions = computed({
|
||||
get() {
|
||||
return (prodId) => {
|
||||
if (prodId) {
|
||||
return prodOptions.value.find((it) => it.productId == prodId).productSpecList
|
||||
}
|
||||
return []
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
const show = ref(false)
|
||||
const form = ref({})
|
||||
const rules = {
|
||||
productId: { required: true, message: '成交产品不可为空', trigger: 'change' },
|
||||
specsId: { required: true, message: '产品规格不可为空', trigger: 'change' },
|
||||
signNum: { required: true, message: '成交数量不可为空', trigger: 'blur' }
|
||||
}
|
||||
|
||||
const prodOptions = ref([])
|
||||
|
||||
function open(signId, arr) {
|
||||
prodOptions.value = arr
|
||||
form.value.signId = signId
|
||||
form.value.isProduced = 0
|
||||
show.value = true
|
||||
}
|
||||
defineExpose({ open })
|
||||
|
||||
const emit = defineEmits(['success'])
|
||||
const formRef = ref()
|
||||
const formLoading = ref(false)
|
||||
async function handleSave() {
|
||||
// 校验表单
|
||||
if (!formRef.value) return
|
||||
const valid = await formRef.value.validate()
|
||||
if (!valid) return
|
||||
|
||||
// 提交请求
|
||||
formLoading.value = true
|
||||
try {
|
||||
await addOrderProduct(form.value)
|
||||
message.success('新增成功!')
|
||||
show.value = false
|
||||
// 发送操作成功的事件
|
||||
emit('success')
|
||||
} finally {
|
||||
formLoading.value = false
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped></style>
|
||||
@@ -45,7 +45,7 @@
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="24" :offset="0">
|
||||
<el-col :span="24" :offset="0" v-if="appStore.getAppInfo?.instanceType == 2">
|
||||
<el-form-item label="是否退货" prop="isReturns">
|
||||
<el-radio-group v-model="form.isReturns">
|
||||
<el-radio :label="true"> 退货 </el-radio>
|
||||
@@ -79,7 +79,9 @@
|
||||
|
||||
<script setup name="DialogAfterSale">
|
||||
import { getAfterSalePage, createAfterSale } from '@/api/clue/afterSale'
|
||||
import { useAppStore } from '@/store/modules/app'
|
||||
|
||||
const appStore = useAppStore()
|
||||
const message = useMessage() // 消息弹窗
|
||||
|
||||
const show = ref(false)
|
||||
|
||||
@@ -28,9 +28,9 @@
|
||||
<el-select v-model="form.supplier" placeholder="选择供应商" filterable>
|
||||
<el-option
|
||||
v-for="item in supplierOptions"
|
||||
:key="item.value"
|
||||
:label="item.label"
|
||||
:value="item.value"
|
||||
:key="item.id"
|
||||
:label="item.supplierName"
|
||||
:value="item.id"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
@@ -141,15 +141,18 @@
|
||||
import { getSimpleWarehouseList, getRemainInventoryList } from '@/api/mall/warehouse'
|
||||
import { createDelivery } from '@/api/clue/delivery'
|
||||
import { getConfigList } from '@/api/system/set'
|
||||
import { getDictOptions } from '@/utils/dict'
|
||||
import { getSupplierSimpleList } from '@/api/school/setting/supplier'
|
||||
import { getExtraFeeSimpleList } from '@/api/clue/extraFee'
|
||||
|
||||
const message = useMessage() // 消息弹窗
|
||||
|
||||
const warehouseOptions = ref([])
|
||||
const extraPayOptions = ref([])
|
||||
const supplierOptions = getDictOptions('erp_supplier')
|
||||
const supplierOptions = ref([])
|
||||
function getOptions() {
|
||||
getSupplierSimpleList().then((data) => {
|
||||
supplierOptions.value = data
|
||||
})
|
||||
getSimpleWarehouseList().then((data) => {
|
||||
warehouseOptions.value = data
|
||||
})
|
||||
|
||||
@@ -61,14 +61,15 @@
|
||||
</el-table>
|
||||
</div>
|
||||
</el-tab-pane>
|
||||
<el-tab-pane
|
||||
label="回款记录"
|
||||
name="returnRecord"
|
||||
v-if="checkPermi(['clue:order:return-list'])"
|
||||
>
|
||||
<el-tab-pane label="回款记录" name="returnRecord">
|
||||
<el-table :data="returnRecordList" border stripe>
|
||||
<el-table-column type="index" width="50" />
|
||||
<el-table-column prop="money" label="回款金额" />
|
||||
<el-table-column
|
||||
v-if="checkPermi(['clue:order:return-list'])"
|
||||
prop="money"
|
||||
label="回款金额"
|
||||
/>
|
||||
<el-table-column prop="personProfit" label="员工利润" />
|
||||
<el-table-column prop="applyTime" label="申请日期" />
|
||||
<el-table-column prop="isPayoff" label="是否结清" />
|
||||
<el-table-column prop="stateName" label="审核状态" />
|
||||
|
||||
@@ -74,8 +74,8 @@
|
||||
@get-checked-columns="getCheckedColumns"
|
||||
@selection-change="handleSelectionChange"
|
||||
>
|
||||
<el-table-column type="selection" width="60" />
|
||||
<el-table-column type="expand">
|
||||
<el-table-column type="selection" width="60" fixed="left" />
|
||||
<el-table-column type="expand" fixed="left">
|
||||
<template #default="{ row }">
|
||||
<div class="p-10px flex justify-center">
|
||||
<VueDraggable
|
||||
@@ -91,8 +91,8 @@
|
||||
stripe
|
||||
style="width: 80%; margin: 0 auto"
|
||||
>
|
||||
<el-table-column prop="productName" label="成交产品" />
|
||||
<el-table-column prop="specsName" label="产品规格" />
|
||||
<el-table-column prop="productName" label="成交产品" width="120px" />
|
||||
<el-table-column prop="specsName" label="产品规格" width="120px" />
|
||||
<el-table-column prop="signNum" label="成交数量" width="90px" />
|
||||
<el-table-column prop="remark" label="成交备注" width="100px" />
|
||||
<el-table-column label="生产状态" width="160px">
|
||||
@@ -147,6 +147,15 @@
|
||||
>
|
||||
发货
|
||||
</el-button>
|
||||
<el-button
|
||||
type="danger"
|
||||
link
|
||||
style="padding: 0; margin-left: 0"
|
||||
v-hasPermi="['clue:order:remove-product']"
|
||||
@click="handleRemoveProduct(scope.row)"
|
||||
>
|
||||
删除
|
||||
</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
@@ -228,6 +237,16 @@
|
||||
>
|
||||
取消登记
|
||||
</el-button>
|
||||
<el-button
|
||||
type="primary"
|
||||
class="mr-10px"
|
||||
link
|
||||
style="padding: 0; margin-left: 0"
|
||||
v-hasPermi="['clue:order:add-product']"
|
||||
@click="handleAddProduct(scope.row)"
|
||||
>
|
||||
添加产品
|
||||
</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</SSTable>
|
||||
@@ -238,6 +257,7 @@
|
||||
<DialogAfterSale ref="afterSaleDialog" />
|
||||
<DialogExtraFee ref="extraFeeDialog" />
|
||||
<DialogDelivery ref="deliveryDialog" @success="getTableList" />
|
||||
<DialogAddProduct ref="addProductDialog" @success="getTableList" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -252,6 +272,7 @@ import DialogFeeback from './DialogFeeback.vue'
|
||||
import DialogAfterSale from './DialogAfterSale.vue'
|
||||
import DialogExtraFee from './DialogExtraPay.vue'
|
||||
import DialogDelivery from './DialogDelivery.vue'
|
||||
import DialogAddProduct from './DialogAddProduct.vue'
|
||||
|
||||
import { removeNullField } from '@/utils'
|
||||
import { formatDate } from '@/utils/formatTime'
|
||||
@@ -404,6 +425,11 @@ function handleDelivery(row) {
|
||||
deliveryDialog.value.open(row)
|
||||
}
|
||||
|
||||
const addProductDialog = ref()
|
||||
function handleAddProduct(row) {
|
||||
addProductDialog.value.open(row.signId, prodOptions.value)
|
||||
}
|
||||
|
||||
const batchIds = ref([])
|
||||
function handleSelectionChange(val) {
|
||||
batchIds.value = val.map((it) => it.signId)
|
||||
@@ -460,6 +486,18 @@ function handleBatchUpdateInstall() {
|
||||
}
|
||||
}
|
||||
|
||||
async function handleRemoveProduct(row) {
|
||||
try {
|
||||
// 修改状态的二次确认
|
||||
await message.confirm(`确认要删除${row.productName}吗?`)
|
||||
// 发起修改状态
|
||||
await SignApi.removeOrderProduct(row.id)
|
||||
message.success('删除成功')
|
||||
// 刷新列表
|
||||
getTableList()
|
||||
} catch {}
|
||||
}
|
||||
|
||||
async function handleChangeProdoce(row) {
|
||||
try {
|
||||
// 修改状态的二次确认
|
||||
|
||||
@@ -116,9 +116,9 @@
|
||||
>
|
||||
<el-option
|
||||
v-for="item in supplierOptions"
|
||||
:key="item.value"
|
||||
:label="item.label"
|
||||
:value="item.value"
|
||||
:key="item.id"
|
||||
:label="item.supplierName"
|
||||
:value="item.id"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
@@ -267,7 +267,7 @@ import { getSimpleUserList as getUserOption } from '@/api/system/user'
|
||||
import { getSimpleProductList } from '@/api/mall/product'
|
||||
import * as SettleApi from '@/api/clue/settle'
|
||||
|
||||
import { getDictOptions } from '@/utils/dict'
|
||||
import { getSupplierSimpleList } from '@/api/school/setting/supplier'
|
||||
import { removeNullField } from '@/utils/index'
|
||||
import { dateFormatter } from '@/utils/formatTime'
|
||||
|
||||
@@ -419,9 +419,12 @@ async function handleSaveSettle() {
|
||||
|
||||
const userOptions = ref([])
|
||||
const prodOptions = ref([])
|
||||
const supplierOptions = getDictOptions('erp_supplier')
|
||||
const supplierOptions = ref([])
|
||||
|
||||
function getOptions() {
|
||||
getSupplierSimpleList().then((data) => {
|
||||
supplierOptions.value = data
|
||||
})
|
||||
// 产品
|
||||
getSimpleProductList().then((data) => {
|
||||
prodOptions.value = data
|
||||
|
||||
@@ -100,10 +100,20 @@
|
||||
<span v-else-if="item.form?.component == 'DatePicker'">
|
||||
{{ formatDate(row[item.field]) }}
|
||||
</span>
|
||||
<div v-else-if="item.form?.component == 'UploadImg'">
|
||||
<el-image
|
||||
v-if="row[item.field]"
|
||||
:src="row[item.field]"
|
||||
preview-teleported
|
||||
:lazy="true"
|
||||
:preview-src-list="[row[item.field]]"
|
||||
/>
|
||||
<!-- <img v-if="row[item.field]" :src="row[item.field]" style="width: 100%; height: 100%" /> -->
|
||||
</div>
|
||||
<span v-else>{{ row[item.field] }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作" width="240px" fixed="right">
|
||||
<el-table-column label="操作" width="170px" fixed="right">
|
||||
<template #default="scope">
|
||||
<el-button
|
||||
type="primary"
|
||||
@@ -150,6 +160,7 @@
|
||||
type="primary"
|
||||
class="mr-10px"
|
||||
link
|
||||
:disabled="scope.row.isCommissioned != '未发放'"
|
||||
style="padding: 0; margin-left: 0"
|
||||
v-hasPermi="['clue:pool:enroll']"
|
||||
@click="cancelDeal(scope.row)"
|
||||
@@ -240,6 +251,11 @@ async function getTableList() {
|
||||
pageNo: tableObject.value.pageNo,
|
||||
pageSize: tableObject.value.pageSize
|
||||
}
|
||||
searchFieldList.value.map((it) => {
|
||||
if (params[it.field]) {
|
||||
params.diyParams[it.field] = params[it.field]
|
||||
}
|
||||
})
|
||||
const data = await SignApi.getSignPage(removeNullField(params))
|
||||
tableObject.value.tableList = data.list.map((it) => ({ ...it, ...it.diyParams }))
|
||||
tableObject.value.total = data.total
|
||||
@@ -249,10 +265,12 @@ async function getTableList() {
|
||||
}
|
||||
|
||||
const loading = ref(true)
|
||||
const searchFieldList = ref([])
|
||||
async function getCurdSchemas() {
|
||||
loading.value = true
|
||||
try {
|
||||
const data = await getSimpleFieldList()
|
||||
searchFieldList.value = data.filter((it) => it.isCustom)
|
||||
data.forEach((elem) => {
|
||||
if (['createUser'].includes(elem.field)) {
|
||||
elem.search.options = userOptions.value
|
||||
|
||||
@@ -18,7 +18,8 @@
|
||||
<el-option label="已驳回" :value="4" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item v-if="appStore.getAppInfo?.instanceType == 1">
|
||||
<template v-if="appStore.getAppInfo?.instanceType == 1">
|
||||
<el-form-item>
|
||||
<el-select
|
||||
v-model="searchForm.receiver"
|
||||
placeholder="接待人"
|
||||
@@ -34,6 +35,76 @@
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-select
|
||||
v-model="searchForm.signSchool"
|
||||
placeholder="选择驾校"
|
||||
filterable
|
||||
clearable
|
||||
@change="changeSchool"
|
||||
>
|
||||
<el-option
|
||||
v-for="item in schoolOptions"
|
||||
:key="item.schoolId"
|
||||
:label="item.schoolName"
|
||||
:value="item.schoolId"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-select
|
||||
v-model="searchForm.signPlace"
|
||||
placeholder="选择场地"
|
||||
filterable
|
||||
clearable
|
||||
:disabled="!searchForm.signSchool"
|
||||
@change="changePlace"
|
||||
>
|
||||
<el-option
|
||||
v-for="item in placeOptions"
|
||||
:key="item.placeId"
|
||||
:label="item.name"
|
||||
:value="item.placeId"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-select
|
||||
v-model="searchForm.signClass"
|
||||
:disabled="!searchForm.signPlace"
|
||||
placeholder="选择班型"
|
||||
filterable
|
||||
clearable
|
||||
>
|
||||
<el-option
|
||||
v-for="item in classOptions"
|
||||
:key="item.typeId"
|
||||
:label="item.typeName"
|
||||
:value="item.typeId"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-select v-model="searchForm.licenseType" placeholder="驾照类型" filterable clearable>
|
||||
<el-option
|
||||
v-for="item in getDictOptions(DICT_TYPE.LINCENSE_TYPE)"
|
||||
:key="item.value"
|
||||
:label="item.label"
|
||||
:value="item.value"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-select v-model="searchForm.area" placeholder="选择区域" clearable filterable>
|
||||
<el-option
|
||||
v-for="item in areaOptions"
|
||||
:key="item.id"
|
||||
:label="item.name"
|
||||
:value="item.id"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</template>
|
||||
<el-form-item v-else-if="appStore.getAppInfo?.instanceType == 2">
|
||||
<el-select
|
||||
v-model="searchForm.convertPeople"
|
||||
@@ -121,6 +192,7 @@
|
||||
<el-form-item>
|
||||
<el-button @click="handleSearch" v-hasPermi="['clue:order:return-search']">查询</el-button>
|
||||
<el-button @click="handleReset" v-hasPermi="['clue:order:return-reset']">重置</el-button>
|
||||
<el-button @click="handleExport" v-hasPermi="['clue:order:return-export']">导出</el-button>
|
||||
<el-button @click="batchAudit" v-hasPermi="['clue:order:return-batch-audit']">
|
||||
批量审核
|
||||
</el-button>
|
||||
@@ -136,7 +208,7 @@
|
||||
@selection-change="handleSelectionChange"
|
||||
>
|
||||
<el-table-column type="selection" width="60" :selectable="(row) => row.state == 1" />
|
||||
<el-table-column prop="signId" label="成交单号" min-width="150px" />
|
||||
<el-table-column prop="signId" label="成交单号" min-width="120px" />
|
||||
<el-table-column prop="name" label="线索名称" width="120px" />
|
||||
<el-table-column prop="phone" label="联系方式" width="120px" />companyProfit
|
||||
<el-table-column prop="money" label="回款金额" min-width="120px">
|
||||
@@ -188,7 +260,7 @@
|
||||
<div v-else>{{ row.personProfit }}</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="signPrice" label="成交价" min-width="90" />
|
||||
<el-table-column prop="signPrice" label="成交价" min-width="120" />
|
||||
<el-table-column prop="isPayoff" label="是否结清" min-width="90">
|
||||
<template #default="{ row }">
|
||||
<el-select v-if="row.edit" size="small" v-model="row.isPayoffValue">
|
||||
@@ -198,6 +270,30 @@
|
||||
<div v-else>{{ row.isPayoff }}</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column
|
||||
v-if="appStore.getAppInfo?.instanceType == 1"
|
||||
prop="signSchool"
|
||||
label="报名驾校"
|
||||
min-width="90"
|
||||
/>
|
||||
<el-table-column
|
||||
v-if="appStore.getAppInfo?.instanceType == 1"
|
||||
prop="signPlace"
|
||||
label="报名场地"
|
||||
min-width="90"
|
||||
/>
|
||||
<el-table-column
|
||||
v-if="appStore.getAppInfo?.instanceType == 1"
|
||||
prop="signClass"
|
||||
label="报名班型"
|
||||
min-width="90"
|
||||
/>
|
||||
<el-table-column
|
||||
v-if="appStore.getAppInfo?.instanceType == 1"
|
||||
prop="licenseType"
|
||||
label="驾照类型"
|
||||
min-width="90"
|
||||
/>
|
||||
<el-table-column
|
||||
v-if="appStore.getAppInfo?.instanceType == 1"
|
||||
prop="receiverName"
|
||||
@@ -253,11 +349,12 @@
|
||||
:formatter="dateFormatter"
|
||||
/>
|
||||
<el-table-column prop="stateName" label="审核状态" fixed="right" min-width="90" />
|
||||
<el-table-column label="操作" width="220px" fixed="right">
|
||||
<el-table-column label="操作" width="150px" fixed="right">
|
||||
<template #default="{ row }">
|
||||
<el-button
|
||||
type="primary"
|
||||
style="padding: 0"
|
||||
class="mr-10px"
|
||||
style="padding: 0; margin-left: 0"
|
||||
text
|
||||
v-hasPermi="['clue:order:return-detail']"
|
||||
@click="handleDetail(row)"
|
||||
@@ -266,7 +363,8 @@
|
||||
</el-button>
|
||||
<el-button
|
||||
type="primary"
|
||||
style="padding: 0"
|
||||
class="mr-10px"
|
||||
style="padding: 0; margin-left: 0"
|
||||
text
|
||||
v-if="row.state == 1 && currentUserId == row.applyUser"
|
||||
v-hasPermi="['clue:order:return']"
|
||||
@@ -276,7 +374,8 @@
|
||||
</el-button>
|
||||
<el-button
|
||||
type="primary"
|
||||
style="padding: 0"
|
||||
class="mr-10px"
|
||||
style="padding: 0; margin-left: 0"
|
||||
text
|
||||
v-if="row.state == 1"
|
||||
v-hasPermi="['clue:order:update-return-money']"
|
||||
@@ -286,7 +385,8 @@
|
||||
</el-button>
|
||||
<el-button
|
||||
type="primary"
|
||||
style="padding: 0"
|
||||
class="mr-10px"
|
||||
style="padding: 0; margin-left: 0"
|
||||
text
|
||||
v-if="row.state == 1"
|
||||
v-hasPermi="['clue:order:return-audit']"
|
||||
@@ -313,6 +413,10 @@
|
||||
<script setup name="Reback">
|
||||
import * as FeebackApi from '@/api/clue/payment'
|
||||
import { getSimpleUserList as getUserOption, getAllUserList } from '@/api/system/user'
|
||||
import { getPlaceList } from '@/api/school/place'
|
||||
import { getClassTypeList } from '@/api/school/class'
|
||||
import { getAreaSimpleList } from '@/api/school/setting/area'
|
||||
import { DICT_TYPE, getDictOptions } from '@/utils/dict'
|
||||
import { useUserStore } from '@/store/modules/user'
|
||||
|
||||
import DialogFeebackAudit from './DialogFeebackAudit.vue'
|
||||
@@ -320,8 +424,10 @@ import DialogFeebackDetail from './DialogFeebackDetail.vue'
|
||||
import DialogBatchAudit from './DialogBatchAudit.vue'
|
||||
|
||||
import { removeNullField } from '@/utils'
|
||||
import download from '@/utils/download'
|
||||
import { useAppStore } from '@/store/modules/app'
|
||||
import { dateFormatter, formatDate } from '@/utils/formatTime'
|
||||
import { ElLoading } from 'element-plus'
|
||||
|
||||
const userStore = useUserStore()
|
||||
const message = useMessage() // 消息弹窗
|
||||
@@ -348,6 +454,13 @@ const searchForm = ref({
|
||||
const userOptions = ref([])
|
||||
const allUserOptions = ref([])
|
||||
|
||||
const schoolOptions = ref([])
|
||||
const allPlaceOptions = ref([])
|
||||
|
||||
const placeOptions = computed(() => {
|
||||
return allPlaceOptions.value.filter((it) => it.schoolId == searchForm.value.signSchool)
|
||||
})
|
||||
|
||||
const tableList = ref([])
|
||||
const total = ref(0)
|
||||
|
||||
@@ -369,11 +482,31 @@ function handleReset() {
|
||||
applyUser: undefined,
|
||||
checkTime: [],
|
||||
mobile: undefined,
|
||||
signSchool: undefined,
|
||||
signPlace: undefined,
|
||||
signClass: undefined,
|
||||
area: undefined,
|
||||
pageNo: 1,
|
||||
pageSize: 20
|
||||
}
|
||||
}
|
||||
|
||||
function changeSchool() {
|
||||
searchForm.value.signPlace = undefined
|
||||
searchForm.value.signClass = undefined
|
||||
}
|
||||
|
||||
function changePlace() {
|
||||
searchForm.value.signClass = undefined
|
||||
getClassTypeOptions()
|
||||
}
|
||||
|
||||
const classOptions = ref([])
|
||||
async function getClassTypeOptions() {
|
||||
const data = await getClassTypeList({ placeId: searchForm.value.signPlace, status: 0 })
|
||||
classOptions.value = data
|
||||
}
|
||||
|
||||
const totalInfo = ref({})
|
||||
const loading = ref(false)
|
||||
async function getList() {
|
||||
@@ -388,6 +521,26 @@ async function getList() {
|
||||
}
|
||||
}
|
||||
|
||||
async function handleExport() {
|
||||
try {
|
||||
ElLoading.service({
|
||||
lock: true,
|
||||
text: '正在导出数据,请稍后...',
|
||||
background: 'rgba(0, 0, 0, 0.7)'
|
||||
})
|
||||
const params = removeNullField(searchForm.value)
|
||||
delete params.pageNo
|
||||
delete params.pageSize
|
||||
const data = await FeebackApi.exportPayment(params)
|
||||
download.excel(data, `汇款记录表${new Date().getTime()}.xls`)
|
||||
} finally {
|
||||
setTimeout(() => {
|
||||
const loadingInstance = ElLoading.service()
|
||||
loadingInstance.close()
|
||||
}, 400)
|
||||
}
|
||||
}
|
||||
|
||||
const batchIds = ref([])
|
||||
function handleSelectionChange(val) {
|
||||
batchIds.value = val.map((it) => it.id)
|
||||
@@ -396,7 +549,7 @@ function handleSelectionChange(val) {
|
||||
const batchAuditDialog = ref()
|
||||
function batchAudit() {
|
||||
if (batchIds.value.length) {
|
||||
batchAuditDialog.value.open('aftersale', batchIds.value)
|
||||
batchAuditDialog.value.open('feeback', batchIds.value)
|
||||
} else {
|
||||
message.info('请选择表格中需要审核的数据')
|
||||
}
|
||||
@@ -423,7 +576,17 @@ function handleAudit(row) {
|
||||
feebackDialog.value.open(row)
|
||||
}
|
||||
|
||||
const areaOptions = ref([])
|
||||
function getOptions() {
|
||||
// 驾校
|
||||
getPlaceList({ placeStatus: 0, schoolStatus: 0, isSearchSchool: true }).then((data) => {
|
||||
schoolOptions.value = data.schoolList
|
||||
allPlaceOptions.value = data.placeList
|
||||
})
|
||||
// 区域
|
||||
getAreaSimpleList().then((data) => {
|
||||
areaOptions.value = data
|
||||
})
|
||||
getUserOption().then((data) => {
|
||||
userOptions.value = data
|
||||
})
|
||||
|
||||
@@ -248,6 +248,16 @@
|
||||
<el-form-item label="结算金额">
|
||||
{{ settleAmount }}
|
||||
</el-form-item>
|
||||
<el-form-item label="结算日期">
|
||||
<el-date-picker
|
||||
v-model="form.settlementDate"
|
||||
type="date"
|
||||
placeholder="选择日期时间"
|
||||
format="YYYY-MM-DD"
|
||||
value-format="YYYY-MM-DD"
|
||||
:clearable="false"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="结算备注" prop="remark">
|
||||
<Editor v-model:modelValue="form.remark" />
|
||||
</el-form-item>
|
||||
@@ -270,7 +280,7 @@ import { getPlaceList } from '@/api/school/place'
|
||||
import { getClassTypeList } from '@/api/school/class'
|
||||
import * as SettleApi from '@/api/clue/settle'
|
||||
|
||||
import { dateFormatter } from '@/utils/formatTime'
|
||||
import { dateFormatter, formatDate } from '@/utils/formatTime'
|
||||
import { removeNullField } from '@/utils/index'
|
||||
|
||||
const message = useMessage() // 消息弹窗
|
||||
@@ -395,6 +405,7 @@ function batchSettle() {
|
||||
showDailog.value = true
|
||||
form.value = {
|
||||
settlementIds: selectList.value.map((it) => it.settlementId),
|
||||
settlementDate: formatDate(new Date()),
|
||||
reamrk: undefined
|
||||
}
|
||||
settleAmount.value = selectList.value.reduce((pre, cur) => pre + cur.settlementMoney, 0)
|
||||
|
||||
@@ -52,7 +52,7 @@ const form = ref({
|
||||
})
|
||||
|
||||
const rules = ref({
|
||||
originFollowUserId: { required: true, message: '原跟进人不可为空', trigger: 'change' }
|
||||
// originFollowUserId: { required: true, message: '原跟进人不可为空', trigger: 'change' }
|
||||
// newFollowUserId: { required: true, message: '新跟进人不可为空', trigger: 'change' }
|
||||
})
|
||||
|
||||
|
||||
@@ -92,6 +92,9 @@
|
||||
快速新增
|
||||
</el-button>
|
||||
</div>
|
||||
<div v-else-if="item.field == 'intentionStateName'">
|
||||
<DictTag type="intention_state" :value="row.intentionState + ''" />
|
||||
</div>
|
||||
<div v-else-if="item.field == 'contact'">
|
||||
<span>{{ row[item.field] }}</span>
|
||||
<Icon class="ml-5px" icon="ep:phone" @click="makeCall(row.contact)" />
|
||||
@@ -228,11 +231,13 @@ const followRef = ref()
|
||||
const loading = ref(true)
|
||||
|
||||
const allSchemas = ref({})
|
||||
const searchFieldList = ref([])
|
||||
|
||||
async function getCurdSchemas() {
|
||||
loading.value = true
|
||||
try {
|
||||
const data = await getSimpleFieldList()
|
||||
searchFieldList.value = data.filter((it) => it.isCustom)
|
||||
data.forEach((elem) => {
|
||||
if (['followUser', 'convertPeople'].includes(elem.field)) {
|
||||
elem.search.options = userOptions.value
|
||||
@@ -290,12 +295,18 @@ async function getTableList() {
|
||||
getSearchCount()
|
||||
try {
|
||||
const queryParams = await searchRef.value.getFormModel()
|
||||
const params = {
|
||||
let params = {
|
||||
...queryParams,
|
||||
pageNo: tableObject.value.pageNo,
|
||||
pageSize: tableObject.value.pageSize,
|
||||
queryType: queryType.value
|
||||
queryType: queryType.value,
|
||||
diyParams: {}
|
||||
}
|
||||
searchFieldList.value.map((it) => {
|
||||
if (params[it.field]) {
|
||||
params.diyParams[it.field] = params[it.field]
|
||||
}
|
||||
})
|
||||
const data = await ClueApi.getCluePage(removeNullField(params))
|
||||
tableObject.value.tableList = data.list.map((it) => ({ ...it, ...it.diyParams }))
|
||||
tableObject.value.total = data.total
|
||||
|
||||
@@ -7,21 +7,64 @@
|
||||
:rules="formRules"
|
||||
label-width="80px"
|
||||
>
|
||||
<el-form-item v-if="formData.parentSource" label="上级来源">
|
||||
<el-row :gutter="20">
|
||||
<el-col v-if="formData.parentSource" :span="12" :offset="0">
|
||||
<el-form-item label="上级来源">
|
||||
<el-input v-model="formData.parentSource" disabled />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12" :offset="0">
|
||||
<el-form-item label="来源名称" prop="sourceName">
|
||||
<el-input v-model="formData.sourceName" placeholder="请输入来源名称" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12" :offset="0">
|
||||
<el-form-item label="排序" prop="sort">
|
||||
<el-input v-model="formData.sort" placeholder="请输入排序" type="number" :min="0" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12" :offset="0">
|
||||
<el-form-item label="状态" prop="status">
|
||||
<el-radio-group v-model="formData.status">
|
||||
<el-radio :label="0"> 启用 </el-radio>
|
||||
<el-radio :label="1"> 禁用 </el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12" :offset="0">
|
||||
<el-form-item label="开户费用" prop="accountFee">
|
||||
<el-input-number
|
||||
v-model="formData.accountFee"
|
||||
:controls="false"
|
||||
style="width: 100%"
|
||||
:min="0"
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12" :offset="0">
|
||||
<el-form-item label="开户日期" prop="accountStartTime">
|
||||
<el-date-picker
|
||||
v-model="formData.accountStartTime"
|
||||
type="date"
|
||||
format="YYYY-MM-DD"
|
||||
value-format="YYYY-MM-DD"
|
||||
placeholder="选择日期时间"
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12" :offset="0">
|
||||
<el-form-item label="到期日期" prop="accountEndTime">
|
||||
<el-date-picker
|
||||
v-model="formData.accountEndTime"
|
||||
type="date"
|
||||
format="YYYY-MM-DD"
|
||||
value-format="YYYY-MM-DD"
|
||||
placeholder="选择日期时间"
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
|
||||
<el-col :span="24" :offset="0">
|
||||
<el-form-item label="备注" prop="remark">
|
||||
<el-input
|
||||
type="textarea"
|
||||
@@ -30,7 +73,64 @@
|
||||
:autosize="{ minRows: 4, maxRows: 8 }"
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-form>
|
||||
|
||||
<el-divider direction="horizontal" content-position="left">线索成本</el-divider>
|
||||
|
||||
<el-button class="mb-10px" type="primary" @click="handleAddPrice"> 添加年份 </el-button>
|
||||
<el-table :data="formData.sourceCostVOS" border>
|
||||
<el-table-column label="年份" width="120">
|
||||
<template #default="{ row }">
|
||||
<el-date-picker
|
||||
v-model="row.year"
|
||||
type="year"
|
||||
placeholder="选择年份"
|
||||
size="small"
|
||||
format="YYYY"
|
||||
value-format="YYYY"
|
||||
style="width: 100%"
|
||||
/>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column v-for="col in 12" :key="col" :label="`${col}月`" width="100px">
|
||||
<el-table-column label="充值">
|
||||
<template #default="{ row }">
|
||||
<el-input-number
|
||||
v-model="row.monthData[col - 1].rechargeFee"
|
||||
size="small"
|
||||
:min="0"
|
||||
:controls="false"
|
||||
style="width: 100%"
|
||||
/>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="消耗">
|
||||
<template #default="{ row }">
|
||||
<el-input-number
|
||||
v-model="row.monthData[col - 1].consumeFee"
|
||||
size="small"
|
||||
:controls="false"
|
||||
:min="0"
|
||||
style="width: 100%"
|
||||
/>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="线索数">
|
||||
<template #default="{ row }">
|
||||
<el-input-number
|
||||
v-model="row.monthData[col - 1].clueNum"
|
||||
size="small"
|
||||
:controls="false"
|
||||
style="width: 100%"
|
||||
:min="0"
|
||||
/>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
|
||||
<template #footer>
|
||||
<el-button :disabled="formLoading" type="primary" @click="submitForm">确 定</el-button>
|
||||
<el-button @click="dialogVisible = false">取 消</el-button>
|
||||
@@ -49,6 +149,7 @@ const formLoading = ref(false) // 表单的加载中:1)修改时的数据加
|
||||
const formType = ref('') // 表单的类型:create - 新增;update - 修改
|
||||
const formData = ref({
|
||||
sourceName: '',
|
||||
accountFee: undefined,
|
||||
sort: 1,
|
||||
remark: ''
|
||||
})
|
||||
@@ -111,8 +212,29 @@ const resetForm = () => {
|
||||
sourceName: '',
|
||||
status: 0,
|
||||
sort: 1,
|
||||
remark: ''
|
||||
remark: '',
|
||||
costModel: 0,
|
||||
sourceCostVOS: []
|
||||
}
|
||||
formRef.value?.resetFields()
|
||||
}
|
||||
|
||||
function handleAddPrice() {
|
||||
const obj = {
|
||||
year: undefined,
|
||||
monthData: []
|
||||
}
|
||||
for (let i = 1; i <= 12; i++) {
|
||||
obj.monthData.push({
|
||||
month: i,
|
||||
consumeFee: 0,
|
||||
rechargeFee: 0,
|
||||
clueNum: 0
|
||||
})
|
||||
}
|
||||
if (!formData.value.sourceCostVOS) {
|
||||
formData.value.sourceCostVOS = []
|
||||
}
|
||||
formData.value.sourceCostVOS.push(obj)
|
||||
}
|
||||
</script>
|
||||
|
||||
35
src/views/Home/ChannelReport.vue
Normal file
35
src/views/Home/ChannelReport.vue
Normal file
@@ -0,0 +1,35 @@
|
||||
<template>
|
||||
<div>
|
||||
<el-tabs v-model="tabName" type="border-card" tab-position="top">
|
||||
<el-tab-pane label="月度统计" name="monthly">
|
||||
<ChannelMonthly :sourceOptions="sourceOptions" />
|
||||
</el-tab-pane>
|
||||
<el-tab-pane label="周期统计" name="period">
|
||||
<ChannelPeriod :sourceOptions="sourceOptions" />
|
||||
</el-tab-pane>
|
||||
</el-tabs>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup name="ChannelReport">
|
||||
import ChannelMonthly from './Comp/ChannelMonthly.vue'
|
||||
import ChannelPeriod from './Comp/ChannelPeriod.vue'
|
||||
import { getSimpleSourceList } from '@/api/clue/source'
|
||||
import { handleTree } from '@/utils/tree'
|
||||
|
||||
const tabName = ref('monthly')
|
||||
|
||||
onMounted(() => {
|
||||
getOptions()
|
||||
})
|
||||
|
||||
const sourceOptions = ref([])
|
||||
|
||||
function getOptions() {
|
||||
getSimpleSourceList().then((data) => {
|
||||
sourceOptions.value = handleTree(data, 'sourceId')
|
||||
})
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped></style>
|
||||
@@ -1,7 +1,112 @@
|
||||
<template>
|
||||
<div> 成交率 </div>
|
||||
<div>
|
||||
<ContentWrap>
|
||||
<el-form :model="searchForm" label-width="0" inline>
|
||||
<el-form-item>
|
||||
<el-date-picker
|
||||
v-model="searchForm.year"
|
||||
type="year"
|
||||
format="YYYY"
|
||||
value-format="YYYY"
|
||||
placeholder="选择年"
|
||||
:clearable="false"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-input v-model="searchForm.nickname" placeholder="销售姓名" clearable />
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button @click="handleSearch">查询</el-button>
|
||||
<el-button @click="handleReset">重置</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
|
||||
<el-table v-loading="loading" :data="tableList" border stripe>
|
||||
<el-table-column type="index" width="50" fixed="left" />
|
||||
<el-table-column prop="nickname" label="姓名" width="80" fixed="left" />
|
||||
<el-table-column
|
||||
v-for="(item, index) in 12"
|
||||
:key="index"
|
||||
:label="item + '月'"
|
||||
sortable
|
||||
min-width="150"
|
||||
:sort-method="(pre, cur) => monthSort(pre, cur, index)"
|
||||
>
|
||||
<template #default="{ row }">
|
||||
<span>{{ (row.monthClueSignRateReportList[index].rate * 100).toFixed(2) }}%</span>
|
||||
<span>({{ row.monthClueSignRateReportList[index].clueSignNum }}/</span>
|
||||
<span>{{ row.monthClueSignRateReportList[index].followClueNum }})</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column
|
||||
label="总成交率"
|
||||
prop="totalRate"
|
||||
min-width="150"
|
||||
fixed="right"
|
||||
sortable
|
||||
:sort-method="totalSort"
|
||||
/>
|
||||
</el-table>
|
||||
</ContentWrap>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup name="CloseRate"></script>
|
||||
<script setup name="CloseRate">
|
||||
import * as reportApi from '@/api/home/reportSignRate'
|
||||
import { removeNullField } from '@/utils'
|
||||
|
||||
onMounted(() => {
|
||||
handleReset()
|
||||
handleSearch()
|
||||
})
|
||||
|
||||
const searchForm = ref({})
|
||||
|
||||
function handleReset() {
|
||||
searchForm.value = {
|
||||
nickname: undefined,
|
||||
year: new Date().getFullYear() + ''
|
||||
}
|
||||
}
|
||||
|
||||
const loading = ref(false)
|
||||
const tableList = ref([])
|
||||
async function handleSearch() {
|
||||
loading.value = true
|
||||
try {
|
||||
const data = await reportApi.getList(removeNullField(searchForm.value))
|
||||
tableList.value = data.map((item) => {
|
||||
const count = item.monthClueSignRateReportList.reduce(
|
||||
(pre, cur) => {
|
||||
return {
|
||||
signCount: pre.signCount + cur.clueSignNum,
|
||||
clueCount: pre.clueCount + cur.followClueNum
|
||||
}
|
||||
},
|
||||
{ signCount: 0, clueCount: 0 }
|
||||
)
|
||||
const rate = count.clueCount > 0 ? ((count.signCount * 100) / count.clueCount).toFixed(2) : 0
|
||||
return {
|
||||
...item,
|
||||
totalRate: `${rate}%(${count.signCount}/${count.clueCount})`,
|
||||
totalRateNum: rate
|
||||
}
|
||||
})
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
function totalSort(pre, cur) {
|
||||
return Number(pre.totalRateNum) - Number(cur.totalRateNum)
|
||||
}
|
||||
|
||||
function monthSort(pre, cur, idx) {
|
||||
return (
|
||||
Number(pre.monthClueSignRateReportList[idx].rate) -
|
||||
Number(cur.monthClueSignRateReportList[idx].rate)
|
||||
)
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped></style>
|
||||
|
||||
62
src/views/Home/Comp/BarSalerClueReport.vue
Normal file
62
src/views/Home/Comp/BarSalerClueReport.vue
Normal file
@@ -0,0 +1,62 @@
|
||||
<template>
|
||||
<Echart :options="optionsData" :width="400" :height="200" />
|
||||
</template>
|
||||
|
||||
<script setup name="BarSalerClueReport">
|
||||
import { set } from 'lodash-es'
|
||||
|
||||
onMounted(() => {
|
||||
nextTick(() => {
|
||||
getReportData()
|
||||
})
|
||||
})
|
||||
|
||||
const optionsData = ref({})
|
||||
|
||||
const getReportData = async () => {
|
||||
// const data = await HomeApi.getClueSignSignRate()
|
||||
set(optionsData, 'radiusAxis', {
|
||||
type: 'category',
|
||||
data: ['宝典', '一点通', '抖音', '小红书'],
|
||||
z: 10
|
||||
})
|
||||
set(optionsData, 'series', [
|
||||
{
|
||||
type: 'bar',
|
||||
data: [1, 2, 3, 4],
|
||||
coordinateSystem: 'polar',
|
||||
name: '高意向A',
|
||||
stack: 'a',
|
||||
emphasis: {
|
||||
focus: 'series'
|
||||
}
|
||||
},
|
||||
{
|
||||
type: 'bar',
|
||||
data: [2, 4, 6, 8],
|
||||
coordinateSystem: 'polar',
|
||||
name: '中意向B',
|
||||
stack: 'a',
|
||||
emphasis: {
|
||||
focus: 'series'
|
||||
}
|
||||
},
|
||||
{
|
||||
type: 'bar',
|
||||
data: [1, 2, 3, 4],
|
||||
coordinateSystem: 'polar',
|
||||
name: '低意向C',
|
||||
stack: 'a',
|
||||
emphasis: {
|
||||
focus: 'series'
|
||||
}
|
||||
}
|
||||
])
|
||||
}
|
||||
|
||||
defineExpose({
|
||||
getReportData
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped></style>
|
||||
148
src/views/Home/Comp/ChannelMonthly.vue
Normal file
148
src/views/Home/Comp/ChannelMonthly.vue
Normal file
@@ -0,0 +1,148 @@
|
||||
<template>
|
||||
<ContentWrap>
|
||||
<el-form :model="searchForm" label-width="0" inline>
|
||||
<el-form-item>
|
||||
<el-date-picker
|
||||
v-model="searchForm.period"
|
||||
type="month"
|
||||
format="YYYY-MM"
|
||||
value-format="YYYY-MM"
|
||||
placeholder="选择年月"
|
||||
:clearable="false"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-tree-select
|
||||
v-model="searchForm.sourceId"
|
||||
:data="props.sourceOptions"
|
||||
:props="defaultProps"
|
||||
check-strictly
|
||||
filterable
|
||||
node-key="sourceId"
|
||||
placeholder="请选择渠道"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button @click="handleSearch">查询</el-button>
|
||||
<el-button @click="handleReset">重置</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
|
||||
<el-table
|
||||
v-loading="loading"
|
||||
:data="tableList"
|
||||
border
|
||||
stripe
|
||||
:summary-method="getSummaries"
|
||||
show-summary
|
||||
>
|
||||
<el-table-column prop="sourceName" label="渠道名称" />
|
||||
<el-table-column prop="newClueSignNum" label="新线索当月成交数" sortable>
|
||||
<template #header>
|
||||
<Tooltip message="当月咨询且当月成交的线索数" /> <span>新线索当月成交数</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="newClueNum" label="新线索总数" sortable>
|
||||
<template #header>
|
||||
<Tooltip message="当月咨询的线索数" /> <span>新线索总数</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="rate" label="新线索当月成交率" sortable :formatter="parseRate" />
|
||||
<el-table-column prop="reallyClueSignNum" sortable>
|
||||
<template #header>
|
||||
<Tooltip message="成交日期在本月的线索数" /> <span>当月总成交数</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</ContentWrap>
|
||||
</template>
|
||||
|
||||
<script setup name="ChannelMonthly">
|
||||
import * as reportApi from '@/api/home/reportChannel'
|
||||
import { removeNullField } from '@/utils'
|
||||
import { formatDate } from '@/utils/formatTime'
|
||||
|
||||
const props = defineProps({
|
||||
sourceOptions: {
|
||||
type: Array,
|
||||
default: () => []
|
||||
}
|
||||
})
|
||||
|
||||
const defaultProps = {
|
||||
children: 'children',
|
||||
label: 'sourceName',
|
||||
value: 'sourceId',
|
||||
isLeaf: 'leaf'
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
getOptions()
|
||||
handleReset()
|
||||
handleSearch()
|
||||
})
|
||||
|
||||
const searchForm = ref({})
|
||||
|
||||
function handleReset() {
|
||||
searchForm.value = {
|
||||
sourceId: undefined,
|
||||
period: formatDate(new Date(), 'YYYY-MM')
|
||||
}
|
||||
}
|
||||
|
||||
function getOptions() {}
|
||||
|
||||
const loading = ref(false)
|
||||
const tableList = ref([])
|
||||
async function handleSearch() {
|
||||
loading.value = true
|
||||
try {
|
||||
const data = await reportApi.getList(removeNullField(searchForm.value))
|
||||
tableList.value = data
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
function parseRate(row) {
|
||||
return Number(row.rate * 100).toFixed(2) + '%'
|
||||
}
|
||||
|
||||
function getSummaries({ columns, data }) {
|
||||
let sums = []
|
||||
columns.forEach((column, index) => {
|
||||
if (index == 0) {
|
||||
sums[index] = '合计'
|
||||
return
|
||||
}
|
||||
const values = data.map((item) => Number(item[column.property]))
|
||||
if (!values.every((value) => Number.isNaN(value))) {
|
||||
if (column.property == 'rate') {
|
||||
const sum = data.reduce(
|
||||
(pre, cur) => ({
|
||||
clueNum: pre.clueNum + cur.newClueNum,
|
||||
signNum: pre.signNum + cur.newClueSignNum
|
||||
}),
|
||||
{ clueNum: 0, signNum: 0 }
|
||||
)
|
||||
sums[index] = sum.clueNum > 0 ? ((sum.signNum * 100) / sum.clueNum).toFixed(2) + '%' : 0
|
||||
} else {
|
||||
sums[index] = values.reduce((prev, curr) => {
|
||||
const value = Number(curr)
|
||||
if (!Number.isNaN(value)) {
|
||||
return prev + curr
|
||||
} else {
|
||||
return prev
|
||||
}
|
||||
}, 0)
|
||||
}
|
||||
} else {
|
||||
sums[index] = ''
|
||||
}
|
||||
})
|
||||
return sums
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped></style>
|
||||
170
src/views/Home/Comp/ChannelPeriod.vue
Normal file
170
src/views/Home/Comp/ChannelPeriod.vue
Normal file
@@ -0,0 +1,170 @@
|
||||
<template>
|
||||
<ContentWrap>
|
||||
<el-form ref="searchRef" :model="searchForm" :rules="searchRules" label-width="100px" inline>
|
||||
<el-form-item label="咨询日期" prop="consultDate">
|
||||
<el-date-picker
|
||||
v-model="searchForm.consultDate"
|
||||
type="daterange"
|
||||
format="YYYY-MM-DD"
|
||||
value-format="YYYY-MM-DD"
|
||||
start-placeholder="选择日期"
|
||||
end-placeholder="选择日期"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="成交周期" prop="periodNumber">
|
||||
<el-input-number
|
||||
v-model="searchForm.periodNumber"
|
||||
:min="1"
|
||||
:controls="false"
|
||||
style="width: 100%"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="渠道来源" prop="sourceId">
|
||||
<el-tree-select
|
||||
v-model="searchForm.sourceId"
|
||||
:data="props.sourceOptions"
|
||||
:props="defaultProps"
|
||||
check-strictly
|
||||
filterable
|
||||
node-key="sourceId"
|
||||
placeholder="请选择渠道"
|
||||
style="width: 100%"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button @click="handleSearch">查询</el-button>
|
||||
<el-button @click="handleReset">重置</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
|
||||
<el-table
|
||||
v-loading="loading"
|
||||
:data="tableList"
|
||||
border
|
||||
stripe
|
||||
:summary-method="getSummaries"
|
||||
show-summary
|
||||
>
|
||||
<el-table-column prop="sourceName" label="渠道名称" />
|
||||
<el-table-column prop="newClueSignNum" label="新线索成交数" sortable>
|
||||
<template #header>
|
||||
<Tooltip message="咨询日期在查询范围内,且在所查成交周期内成交的线索数" />
|
||||
<span>新线索成交数</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="newClueNum" label="新线索总数" sortable>
|
||||
<template #header>
|
||||
<Tooltip message="咨询日期在查询范围内,咨询的线索数" /> <span>新线索总数</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="rate" label="新线索成交率" sortable :formatter="parseRate" />
|
||||
</el-table>
|
||||
</ContentWrap>
|
||||
</template>
|
||||
|
||||
<script setup name="ChannelPeriod">
|
||||
import { getPeriodList } from '@/api/home/reportChannel'
|
||||
import { removeNullField } from '@/utils'
|
||||
import { formatDate } from '@/utils/formatTime'
|
||||
|
||||
const props = defineProps({
|
||||
sourceOptions: {
|
||||
type: Array,
|
||||
default: () => []
|
||||
}
|
||||
})
|
||||
|
||||
const defaultProps = {
|
||||
children: 'children',
|
||||
label: 'sourceName',
|
||||
value: 'sourceId',
|
||||
isLeaf: 'leaf'
|
||||
}
|
||||
|
||||
const searchRef = ref()
|
||||
|
||||
const searchRules = {
|
||||
// consultDate: { required: true, type: 'array', message: '请选择咨询日期', trigger: 'change,blur' },
|
||||
periodNumber: { required: true, message: '请输入成交周期', trigger: 'blur' }
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
getOptions()
|
||||
handleReset()
|
||||
handleSearch()
|
||||
})
|
||||
|
||||
const searchForm = ref({})
|
||||
|
||||
function handleReset() {
|
||||
const date = new Date()
|
||||
date.setDate(1)
|
||||
searchForm.value = {
|
||||
sourceId: undefined,
|
||||
periodNumber: 30,
|
||||
consultDate: [formatDate(date), formatDate(new Date())]
|
||||
}
|
||||
}
|
||||
|
||||
function getOptions() {}
|
||||
|
||||
const loading = ref(false)
|
||||
const tableList = ref([])
|
||||
async function handleSearch() {
|
||||
try {
|
||||
const valid = await searchRef.value.validate()
|
||||
if (!valid) return
|
||||
loading.value = true
|
||||
const params = { ...searchForm.value }
|
||||
if (params.consultDate && params.consultDate.length) {
|
||||
params.startDateTime = params.consultDate[0]
|
||||
params.endDateTime = params.consultDate[1]
|
||||
}
|
||||
const data = await getPeriodList(removeNullField(params))
|
||||
tableList.value = data
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
function parseRate(row) {
|
||||
return Number(row.rate * 100).toFixed(2) + '%'
|
||||
}
|
||||
|
||||
function getSummaries({ columns, data }) {
|
||||
let sums = []
|
||||
columns.forEach((column, index) => {
|
||||
if (index == 0) {
|
||||
sums[index] = '合计'
|
||||
return
|
||||
}
|
||||
const values = data.map((item) => Number(item[column.property]))
|
||||
if (!values.every((value) => Number.isNaN(value))) {
|
||||
if (column.property == 'rate') {
|
||||
const sum = data.reduce(
|
||||
(pre, cur) => ({
|
||||
clueNum: pre.clueNum + cur.newClueNum,
|
||||
signNum: pre.signNum + cur.newClueSignNum
|
||||
}),
|
||||
{ clueNum: 0, signNum: 0 }
|
||||
)
|
||||
sums[index] = sum.clueNum > 0 ? ((sum.signNum * 100) / sum.clueNum).toFixed(2) + '%' : 0
|
||||
} else {
|
||||
sums[index] = values.reduce((prev, curr) => {
|
||||
const value = Number(curr)
|
||||
if (!Number.isNaN(value)) {
|
||||
return prev + curr
|
||||
} else {
|
||||
return prev
|
||||
}
|
||||
}, 0)
|
||||
}
|
||||
} else {
|
||||
sums[index] = ''
|
||||
}
|
||||
})
|
||||
return sums
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped></style>
|
||||
402
src/views/Home/Comp/DialogSalerReportDetail.vue
Normal file
402
src/views/Home/Comp/DialogSalerReportDetail.vue
Normal file
@@ -0,0 +1,402 @@
|
||||
<template>
|
||||
<Dialog :title="dialogTitle" v-model="dialogVisible" width="1000px">
|
||||
<el-form :model="searchForm" label-width="0" inline>
|
||||
<el-form-item>
|
||||
<el-date-picker
|
||||
v-model="searchForm.consultTime"
|
||||
type="daterange"
|
||||
format="YYYY-MM-DD"
|
||||
value-format="YYYY-MM-DD"
|
||||
start-placeholder="选择日期"
|
||||
end-placeholder="选择日期"
|
||||
:clearable="false"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-tree-select
|
||||
v-model="searchForm.sourceId"
|
||||
:data="props.sourceOptions"
|
||||
:props="defaultProps"
|
||||
check-strictly
|
||||
clearable
|
||||
filterable
|
||||
node-key="sourceId"
|
||||
placeholder="请选择渠道"
|
||||
@change="sourceChange"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item v-if="appStore.getAppInfo?.instanceType == 1">
|
||||
<el-select
|
||||
v-model="searchForm.licenseTypeList"
|
||||
placeholder="选择驾照类型"
|
||||
clearable
|
||||
multiple
|
||||
>
|
||||
<el-option
|
||||
v-for="item in props.licenseTypeOptions"
|
||||
:key="item.label"
|
||||
:label="item.label"
|
||||
:value="item.label"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item>
|
||||
<el-button @click="handleSearch">查询</el-button>
|
||||
<el-button @click="handleReset">重置</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
|
||||
<el-tabs v-model="showPane" class="pb-10px">
|
||||
<el-tab-pane label="详细数据" :name="1">
|
||||
<el-table v-loading="loading" :data="tableList" border stripe>
|
||||
<el-table-column prop="sourceName" label="渠道名称" width="100" fixed="left" />
|
||||
<el-table-column prop="newClueNumber" label="新线索数" sortable min-width="100" />
|
||||
<el-table-column prop="signNumber" label="成交数" sortable min-width="100" />
|
||||
<el-table-column
|
||||
prop="signRate"
|
||||
label="成交率"
|
||||
sortable
|
||||
min-width="100"
|
||||
:formatter="(row) => row.signRate + '%'"
|
||||
/>
|
||||
<el-table-column prop="averageSignPeriod" label="成交周期" sortable min-width="100" />
|
||||
<el-table-column
|
||||
prop="grossProfitOfSingleSign"
|
||||
label="毛利/单"
|
||||
sortable
|
||||
min-width="100"
|
||||
/>
|
||||
<el-table-column prop="payPriceOfSingleSign" label="支出/单" sortable min-width="100" />
|
||||
<el-table-column prop="costOfSingleClue" label="线索成本/条" sortable min-width="100" />
|
||||
<el-table-column
|
||||
prop="netProfitOfSingleSign"
|
||||
label="净利润/单"
|
||||
sortable
|
||||
min-width="100"
|
||||
/>
|
||||
<el-table-column label="总线索成本" prop="clueCostTotal" sortable min-width="100" />
|
||||
<el-table-column label="总支出" prop="payPriceTotal" sortable min-width="100" />
|
||||
<el-table-column label="总利润" prop="profitTotal" sortable min-width="100" />
|
||||
</el-table>
|
||||
</el-tab-pane>
|
||||
<el-tab-pane label="图表展示" :name="2" lazy>
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="24" :offset="0">
|
||||
<el-checkbox-group v-model="showChannel" @change="setReportData">
|
||||
<el-checkbox v-for="(item, index) in channelArr" :key="index" :label="item" />
|
||||
</el-checkbox-group>
|
||||
</el-col>
|
||||
|
||||
<el-col :span="12" :offset="0">
|
||||
<el-skeleton :loading="loading" animated>
|
||||
<Echart :options="echart1Option" width="100%" :height="400" />
|
||||
</el-skeleton>
|
||||
</el-col>
|
||||
<el-col :span="12" :offset="0" v-if="appStore.getAppInfo?.instanceType == 1">
|
||||
<Echart :options="echart2Option" width="100%" :height="400" />
|
||||
</el-col>
|
||||
<el-col :span="24" :offset="0">
|
||||
<Echart :options="echart3Option" width="100%" :height="400" />
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-tab-pane>
|
||||
</el-tabs>
|
||||
</Dialog>
|
||||
</template>
|
||||
|
||||
<script setup name="DialogSalerReportDetail">
|
||||
import { set } from 'lodash-es'
|
||||
import * as reportApi from '@/api/home/reportSaler'
|
||||
import { getIntDictOptions } from '@/utils/dict'
|
||||
import { removeNullField } from '@/utils'
|
||||
import { useAppStore } from '@/store/modules/app'
|
||||
import { formatDate } from '@/utils/formatTime'
|
||||
|
||||
const appStore = useAppStore()
|
||||
|
||||
const props = defineProps({
|
||||
licenseTypeOptions: {
|
||||
type: Array,
|
||||
default: () => []
|
||||
},
|
||||
sourceOptions: {
|
||||
type: Array,
|
||||
default: () => []
|
||||
}
|
||||
})
|
||||
|
||||
const defaultProps = {
|
||||
children: 'children',
|
||||
label: 'sourceName',
|
||||
value: 'sourceId',
|
||||
isLeaf: 'leaf'
|
||||
}
|
||||
|
||||
const showChannel = ref([])
|
||||
const intentionOptions = getIntDictOptions('intention_state')
|
||||
|
||||
const channelArr = computed(() => {
|
||||
if (searchForm.value.sourceId) {
|
||||
let arr =
|
||||
props.sourceOptions.find((it) => it.sourceId == searchForm.value.sourceId)?.children || []
|
||||
return arr.map((it) => it.sourceName)
|
||||
} else {
|
||||
return props.sourceOptions.map((it) => it.sourceName)
|
||||
}
|
||||
})
|
||||
|
||||
const dialogVisible = ref(false) // 弹窗的是否展示
|
||||
const dialogTitle = ref('') // 弹窗的标题
|
||||
const loading = ref(false)
|
||||
const searchForm = ref({})
|
||||
|
||||
const showPane = ref(1)
|
||||
|
||||
function handleReset() {
|
||||
searchForm.value = {
|
||||
nickname: undefined,
|
||||
consultTime: [`${year}-${month}-01`, formatDate(new Date())],
|
||||
licenseTypeList: [],
|
||||
sourceId: undefined
|
||||
}
|
||||
}
|
||||
|
||||
/** 打开弹窗 */
|
||||
const open = async (info, queryInfo) => {
|
||||
showPane.value = 1
|
||||
dialogVisible.value = true
|
||||
dialogTitle.value = info.nickname
|
||||
searchForm.value = { ...queryInfo }
|
||||
searchForm.value.userId = info.userId
|
||||
|
||||
sourceChange()
|
||||
}
|
||||
defineExpose({ open }) // 提供 open 方法,用于打开弹窗
|
||||
|
||||
const tableList = ref([])
|
||||
|
||||
async function handleSearch() {
|
||||
loading.value = true
|
||||
try {
|
||||
const params = { ...searchForm.value }
|
||||
const data = await reportApi.getInfo(removeNullField(params))
|
||||
tableList.value = data.sourceDetailVOList
|
||||
setReportData(data)
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
function sourceChange() {
|
||||
handleSearch()
|
||||
showChannel.value = channelArr.value
|
||||
}
|
||||
|
||||
const echart1Option = ref({
|
||||
title: {
|
||||
text: '线索分布图'
|
||||
},
|
||||
radiusAxis: {},
|
||||
angleAxis: {},
|
||||
tooltip: {
|
||||
show: true,
|
||||
formatter: '{b0}-{a}: {c}'
|
||||
},
|
||||
polar: {},
|
||||
legend: {
|
||||
show: true,
|
||||
type: 'scroll',
|
||||
left: 100
|
||||
}
|
||||
})
|
||||
|
||||
const echart2Option = ref({
|
||||
title: {
|
||||
text: '成交分布图'
|
||||
},
|
||||
radiusAxis: {},
|
||||
angleAxis: {},
|
||||
tooltip: {
|
||||
show: true,
|
||||
formatter: '{b0}-{a}: {c}'
|
||||
},
|
||||
polar: {},
|
||||
legend: {
|
||||
show: true,
|
||||
type: 'scroll',
|
||||
left: 100
|
||||
}
|
||||
})
|
||||
|
||||
const echart3Option = ref({
|
||||
title: {
|
||||
text: '销售雷达图'
|
||||
},
|
||||
radar: {
|
||||
indicator: [
|
||||
{ name: '成交周期' },
|
||||
{ name: '毛利/单' },
|
||||
{ name: '支出/单' },
|
||||
{ name: '线索成本/条' },
|
||||
{ name: '净利润/单' },
|
||||
{ name: '成交率' }
|
||||
// { name: '线索总成本' },
|
||||
// { name: '总支出' },
|
||||
// { name: '总利润' }
|
||||
]
|
||||
},
|
||||
legend: {
|
||||
show: true,
|
||||
right: '10px',
|
||||
orient: 'vertical'
|
||||
}
|
||||
})
|
||||
|
||||
const setReportData = async (data) => {
|
||||
// const data = await HomeApi.getClueSignSignRate()
|
||||
|
||||
const channelClueArr = showChannel.value.sort((pre, cur) => {
|
||||
const preArr = tableList.value.find((it) => it.sourceName == pre).clueIntentionNumVOList || []
|
||||
const curArr = tableList.value.find((it) => it.sourceName == cur).clueIntentionNumVOList || []
|
||||
const preCount = preArr.reduce((preVal, curVal) => preVal + curVal.intentionNum, 0)
|
||||
const curCount = curArr.reduce((preVal, curVal) => preVal + curVal.intentionNum, 0)
|
||||
return preCount - curCount
|
||||
})
|
||||
|
||||
const arr1 = intentionOptions.map((intention) => {
|
||||
const list = []
|
||||
channelClueArr.map((item) => {
|
||||
const row = tableList.value.find((it) => item == it.sourceName)
|
||||
if (row) {
|
||||
list.push(
|
||||
row.clueIntentionNumVOList.find((it) => it.intentionState == intention.value).intentionNum
|
||||
)
|
||||
}
|
||||
})
|
||||
return {
|
||||
type: 'bar',
|
||||
data: list,
|
||||
coordinateSystem: 'polar',
|
||||
name: intention.label,
|
||||
stack: 'a',
|
||||
emphasis: {
|
||||
focus: 'series'
|
||||
},
|
||||
label: {
|
||||
show: true,
|
||||
position: 'middle'
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
const channelSignArr = showChannel.value.sort((pre, cur) => {
|
||||
const preArr = tableList.value.find((it) => it.sourceName == pre).signLicenseTypeNumVOList || []
|
||||
const curArr = tableList.value.find((it) => it.sourceName == cur).signLicenseTypeNumVOList || []
|
||||
const preCount = preArr.reduce((preVal, curVal) => preVal + curVal.licenseTypeNum, 0)
|
||||
const curCount = curArr.reduce((preVal, curVal) => preVal + curVal.licenseTypeNum, 0)
|
||||
return preCount - curCount
|
||||
})
|
||||
const arr2 = props.licenseTypeOptions.map((cartype) => {
|
||||
const list = []
|
||||
channelClueArr.map((item) => {
|
||||
const row = tableList.value.find((it) => item == it.sourceName)
|
||||
if (row) {
|
||||
list.push(
|
||||
row.signLicenseTypeNumVOList.find((it) => it.licenseType == cartype.label).licenseTypeNum
|
||||
)
|
||||
}
|
||||
})
|
||||
return {
|
||||
type: 'bar',
|
||||
data: list,
|
||||
coordinateSystem: 'polar',
|
||||
name: cartype.label,
|
||||
stack: 'a',
|
||||
emphasis: {
|
||||
focus: 'series'
|
||||
},
|
||||
label: {
|
||||
show: true,
|
||||
position: 'middle'
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
set(echart1Option.value, 'radiusAxis', {
|
||||
type: 'category',
|
||||
data: channelClueArr,
|
||||
axisLabel: {
|
||||
margin: 5,
|
||||
fontSize: 10,
|
||||
interval: 0
|
||||
}
|
||||
})
|
||||
|
||||
set(echart1Option.value, 'series', arr1)
|
||||
|
||||
set(echart2Option.value, 'radiusAxis', {
|
||||
type: 'category',
|
||||
data: channelSignArr,
|
||||
axisLabel: {
|
||||
margin: 5,
|
||||
fontSize: 10,
|
||||
interval: 0
|
||||
}
|
||||
})
|
||||
set(echart2Option.value, 'series', arr2)
|
||||
const { personDetail, averageDetail } = data
|
||||
set(echart3Option.value, 'series', [
|
||||
{
|
||||
type: 'radar',
|
||||
data: [
|
||||
{
|
||||
value: [
|
||||
averageDetail.averageSignPeriod,
|
||||
averageDetail.grossProfitOfSingleSign,
|
||||
averageDetail.payPriceOfSingleSign,
|
||||
averageDetail.costOfSingleClue,
|
||||
averageDetail.netProfitOfSingleSign,
|
||||
averageDetail.signRate
|
||||
// averageDetail.clueCostTotal,
|
||||
// averageDetail.payPriceTotal,
|
||||
// averageDetail.profitTotal
|
||||
],
|
||||
name: '平均水平',
|
||||
lineStyle: {
|
||||
type: 'dashed'
|
||||
},
|
||||
areaStyle: {
|
||||
color: 'rgba(0, 191, 255, 0.6)'
|
||||
},
|
||||
label: {
|
||||
show: true
|
||||
}
|
||||
},
|
||||
{
|
||||
value: [
|
||||
personDetail.averageSignPeriod,
|
||||
personDetail.grossProfitOfSingleSign,
|
||||
personDetail.payPriceOfSingleSign,
|
||||
personDetail.costOfSingleClue,
|
||||
personDetail.netProfitOfSingleSign,
|
||||
personDetail.signRate
|
||||
// personDetail.clueCostTotal,
|
||||
// personDetail.payPriceTotal,
|
||||
// personDetail.profitTotal
|
||||
],
|
||||
name: dialogTitle.value,
|
||||
areaStyle: {
|
||||
color: 'rgba(255, 145, 124, 0.4)'
|
||||
},
|
||||
label: {
|
||||
show: true
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
])
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped></style>
|
||||
162
src/views/Home/Comp/SignListCard.vue
Normal file
162
src/views/Home/Comp/SignListCard.vue
Normal file
@@ -0,0 +1,162 @@
|
||||
<template>
|
||||
<ContentWrap>
|
||||
<el-form :model="searchForm" label-width="0" inline>
|
||||
<el-form-item>
|
||||
<el-select v-model="searchForm.detailType" :clearable="false" filterable>
|
||||
<el-option
|
||||
v-for="item in detailTypeOptions"
|
||||
:key="item.value"
|
||||
:label="item.label"
|
||||
:value="item.value"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item v-if="searchForm.detailType == 1">
|
||||
<el-date-picker
|
||||
v-model="searchForm.month"
|
||||
type="month"
|
||||
format="YYYY-MM"
|
||||
value-format="YYYY-MM"
|
||||
placeholder="选择年月"
|
||||
:clearable="false"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item v-if="[2, 3].includes(searchForm.detailType)">
|
||||
<el-date-picker
|
||||
v-model="searchForm.year"
|
||||
type="year"
|
||||
format="YYYY"
|
||||
value-format="YYYY"
|
||||
placeholder="选择年"
|
||||
:clearable="false"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button @click="handleSearch">查询</el-button>
|
||||
<el-button @click="handleReset">重置</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<el-table :data="tableList" border stripe show-summary :summary-method="getSummaries">
|
||||
<el-table-column label="周期" prop="period" width="110px" />
|
||||
<el-table-column label="成交" align="center">
|
||||
<el-table-column prop="signNumber" label="成交数" />
|
||||
<el-table-column prop="signPrice" label="成交额" />
|
||||
<el-table-column prop="signPriceSequentialGrowth" label="环比" :formatter="parseRate" />
|
||||
<el-table-column
|
||||
prop="signPriceSamePeriodCompareGrowth"
|
||||
label="同比"
|
||||
:formatter="parseRate"
|
||||
/>
|
||||
</el-table-column>
|
||||
<el-table-column label="利润" align="center">
|
||||
<el-table-column prop="profit">
|
||||
<template #header> <Tooltip message="公司利润" /> <span>利润</span> </template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="profitSequentialGrowth" label="环比" :formatter="parseRate" />
|
||||
<el-table-column prop="profitSamePeriodCompareGrowth" label="同比" :formatter="parseRate" />
|
||||
</el-table-column>
|
||||
|
||||
<el-table-column prop="notReceivedMoney">
|
||||
<template #header>
|
||||
<Tooltip message="回款列表中未审核的公司利润" /> <span>未到账</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="receivedMoney">
|
||||
<template #header>
|
||||
<Tooltip message="回款列表中已审核的公司利润" /> <span>已到账</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="waitSettleMoney" label="待结算金额" />
|
||||
<el-table-column prop="settledMoney" label="已结算金额" />
|
||||
<el-table-column prop="refundAmount" label="已退款金额" />
|
||||
<el-table-column prop="remainMoney">
|
||||
<template #header>
|
||||
<Tooltip message="已到账金额-已结算金额-已退款金额" /> <span>结余</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</ContentWrap>
|
||||
</template>
|
||||
|
||||
<script setup name="SignListCard">
|
||||
import { getList } from '@/api/home/reportSignDetail'
|
||||
import { formatDate } from '@/utils/formatTime'
|
||||
import { removeNullField } from '@/utils'
|
||||
|
||||
const detailTypeOptions = [
|
||||
{ label: '日明细', value: 1 },
|
||||
{ label: '月明细', value: 2 },
|
||||
{ label: '季明细', value: 3 },
|
||||
{ label: '年明细', value: 4 }
|
||||
]
|
||||
|
||||
onMounted(() => {
|
||||
handleReset()
|
||||
handleSearch()
|
||||
})
|
||||
|
||||
const searchForm = ref({})
|
||||
|
||||
function handleReset() {
|
||||
searchForm.value = {
|
||||
detailType: 2,
|
||||
month: formatDate(new Date(), 'YYYY-MM'),
|
||||
year: new Date().getFullYear() + ''
|
||||
}
|
||||
}
|
||||
const loading = ref(false)
|
||||
const tableList = ref([])
|
||||
async function handleSearch() {
|
||||
loading.value = true
|
||||
try {
|
||||
let params = { detailType: searchForm.value.detailType }
|
||||
if (params.detailType == 1) {
|
||||
const d = new Date(searchForm.value.month)
|
||||
params.month = d.getMonth() + 1
|
||||
params.year = d.getFullYear()
|
||||
} else if ([2, 3].includes(params.detailType)) {
|
||||
params.year = searchForm.value.year
|
||||
}
|
||||
const data = await getList(removeNullField(params))
|
||||
tableList.value = data
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
function parseRate(row, column) {
|
||||
return Number(row[column.property] * 100).toFixed(2) + '%'
|
||||
}
|
||||
|
||||
function getSummaries({ columns, data }) {
|
||||
let sums = []
|
||||
columns.forEach((column, index) => {
|
||||
if (index == 0) {
|
||||
sums[index] = '合计'
|
||||
return
|
||||
}
|
||||
const values = data.map((item) => Number(item[column.property]))
|
||||
if (!values.every((value) => Number.isNaN(value))) {
|
||||
if (column.property.includes('Growth')) {
|
||||
sums[index] = ''
|
||||
} else {
|
||||
sums[index] = values
|
||||
.reduce((prev, curr) => {
|
||||
const value = Number(curr)
|
||||
if (!Number.isNaN(value)) {
|
||||
return prev + curr
|
||||
} else {
|
||||
return prev
|
||||
}
|
||||
}, 0)
|
||||
.toFixed(2)
|
||||
}
|
||||
} else {
|
||||
sums[index] = ''
|
||||
}
|
||||
})
|
||||
return sums
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped></style>
|
||||
195
src/views/Home/Comp/SignTotalCard.vue
Normal file
195
src/views/Home/Comp/SignTotalCard.vue
Normal file
@@ -0,0 +1,195 @@
|
||||
<template>
|
||||
<ContentWrap>
|
||||
<div class="flex items-center">
|
||||
<span class="mr-20px">成交概况</span>
|
||||
<el-dropdown>
|
||||
<el-button size="small"
|
||||
>{{ periodTypeOptions[periodType] }} <Icon icon="ep:arrow-down"
|
||||
/></el-button>
|
||||
<template #dropdown>
|
||||
<el-dropdown-menu>
|
||||
<el-dropdown-item
|
||||
v-for="(item, index) in periodTypeOptions"
|
||||
:key="index"
|
||||
@click="handleSearchTotalInfo(index)"
|
||||
>
|
||||
{{ item }}
|
||||
</el-dropdown-item>
|
||||
</el-dropdown-menu>
|
||||
</template>
|
||||
</el-dropdown>
|
||||
</div>
|
||||
<el-row :gutter="20" v-loading="infoLoading">
|
||||
<el-col :span="8" :offset="0">
|
||||
<div class="statistic-card">
|
||||
<el-statistic :value="totalData.signNumber" title="成交数" />
|
||||
<div
|
||||
class="statistic-footer"
|
||||
:class="periodType != 4 ? 'justify-between' : 'justify-center'"
|
||||
>
|
||||
<div class="footer-item">
|
||||
<span>环比</span>
|
||||
<span :class="getColor(totalData.signNumberSequentialGrowth)">
|
||||
{{ totalData.signNumberSequentialGrowth }}%
|
||||
<Icon class="ml-2px" :icon="getIcon(totalData.signNumberSequentialGrowth)" />
|
||||
</span>
|
||||
</div>
|
||||
<div class="footer-item" v-if="periodType != 4">
|
||||
<span>同比</span>
|
||||
<span :class="getColor(totalData.signNumberPeriodCompareGrowth)">
|
||||
{{ totalData.signNumberPeriodCompareGrowth }}%
|
||||
<Icon class="ml-2px" :icon="getIcon(totalData.signNumberPeriodCompareGrowth)" />
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</el-col>
|
||||
<el-col :span="8" :offset="0">
|
||||
<div class="statistic-card">
|
||||
<el-statistic :value="totalData.signPrice" title="成交额" :precision="2" />
|
||||
<div
|
||||
class="statistic-footer"
|
||||
:class="periodType != 4 ? 'justify-between' : 'justify-center'"
|
||||
>
|
||||
<div class="footer-item">
|
||||
<span>环比</span>
|
||||
<span :class="getColor(totalData.signPriceSequentialGrowth)">
|
||||
{{ totalData.signPriceSequentialGrowth }}%
|
||||
<Icon class="ml-2px" :icon="getIcon(totalData.signPriceSequentialGrowth)" />
|
||||
</span>
|
||||
</div>
|
||||
<div class="footer-item" v-if="periodType != 4">
|
||||
<span>同比</span>
|
||||
<span :class="getColor(totalData.signPriceSamePeriodCompareGrowth)">
|
||||
{{ totalData.signPriceSamePeriodCompareGrowth }}%
|
||||
<Icon class="ml-2px" :icon="getIcon(totalData.signPriceSamePeriodCompareGrowth)" />
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</el-col>
|
||||
<el-col :span="8" :offset="0">
|
||||
<div class="statistic-card">
|
||||
<el-statistic :value="totalData.profit" title="利润" :precision="2" />
|
||||
<div
|
||||
class="statistic-footer"
|
||||
:class="periodType != 4 ? 'justify-between' : 'justify-center'"
|
||||
>
|
||||
<div class="footer-item">
|
||||
<span>环比</span>
|
||||
<span :class="getColor(totalData.profitSequentialGrowth)">
|
||||
{{ totalData.profitSequentialGrowth }}%
|
||||
<Icon class="ml-2px" :icon="getIcon(totalData.profitSequentialGrowth)" />
|
||||
</span>
|
||||
</div>
|
||||
<div class="footer-item" v-if="periodType != 4">
|
||||
<span>同比</span>
|
||||
<span :class="getColor(totalData.profitSamePeriodCompareGrowth)">
|
||||
{{ totalData.profitSamePeriodCompareGrowth }}%
|
||||
<Icon class="ml-2px" :icon="getIcon(totalData.profitSamePeriodCompareGrowth)" />
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</ContentWrap>
|
||||
</template>
|
||||
|
||||
<script setup name="SignTotalCard">
|
||||
import { getInfo } from '@/api/home/reportSignDetail'
|
||||
|
||||
const periodTypeOptions = ['本日', '本周', '本月', '本季', '本年']
|
||||
|
||||
const periodType = ref(2)
|
||||
const totalData = ref({})
|
||||
|
||||
onMounted(() => {
|
||||
handleSearchTotalInfo(2)
|
||||
})
|
||||
|
||||
const infoLoading = ref(false)
|
||||
async function handleSearchTotalInfo(index) {
|
||||
periodType.value = index
|
||||
infoLoading.value = true
|
||||
try {
|
||||
const data = await getInfo({ periodType: periodType.value + 1 })
|
||||
totalData.value = data
|
||||
} finally {
|
||||
infoLoading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
function getIcon(val) {
|
||||
if (val > 0) {
|
||||
return 'ep:caret-top'
|
||||
} else if (val < 0) {
|
||||
return 'ep:caret-bottom'
|
||||
} else {
|
||||
return 'ep:minus'
|
||||
}
|
||||
}
|
||||
|
||||
function getColor(val) {
|
||||
if (val > 0) {
|
||||
return 'green'
|
||||
} else if (val < 0) {
|
||||
return 'red'
|
||||
} else {
|
||||
return 'gley'
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.el-col {
|
||||
text-align: center;
|
||||
}
|
||||
:global(h2#card-usage ~ .example .example-showcase) {
|
||||
background-color: var(--el-fill-color) !important;
|
||||
}
|
||||
|
||||
.el-statistic {
|
||||
--el-statistic-content-font-size: 28px;
|
||||
}
|
||||
|
||||
.statistic-card {
|
||||
height: 100%;
|
||||
// padding: 20px;
|
||||
border-radius: 4px;
|
||||
background-color: var(--el-bg-color-overlay);
|
||||
}
|
||||
|
||||
.statistic-footer {
|
||||
margin: 16px auto 0;
|
||||
display: flex;
|
||||
// justify-content: space-between;
|
||||
align-items: center;
|
||||
flex-wrap: wrap;
|
||||
font-size: 12px;
|
||||
color: var(--el-text-color-regular);
|
||||
width: 300px;
|
||||
}
|
||||
|
||||
.statistic-footer .footer-item {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.statistic-footer .footer-item span:last-child {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
margin-left: 4px;
|
||||
}
|
||||
|
||||
.green {
|
||||
color: var(--el-color-success);
|
||||
}
|
||||
.gley {
|
||||
color: var(--el-color-info);
|
||||
}
|
||||
.red {
|
||||
color: var(--el-color-error);
|
||||
}
|
||||
</style>
|
||||
@@ -67,8 +67,8 @@
|
||||
</el-row>
|
||||
</el-skeleton>
|
||||
</el-card>
|
||||
<div class="mt-10px flex">
|
||||
<el-card shadow="never" class="flex-1">
|
||||
<div class="mt-10px flex" style="flex-wrap: wrap">
|
||||
<el-card shadow="never" :class="mobile ? 'w-full mb-10px' : 'flex-1'">
|
||||
<template #header>
|
||||
<div class="flex justify-between">
|
||||
<span>成交率</span>
|
||||
@@ -80,7 +80,7 @@
|
||||
</el-skeleton>
|
||||
</el-skeleton>
|
||||
</el-card>
|
||||
<el-card shadow="never" class="ml-10px">
|
||||
<el-card shadow="never" :class="mobile ? 'flex-1' : 'ml-10px'">
|
||||
<template #header>
|
||||
<div class="flex justify-between items-center">
|
||||
<span>跟进榜Top10</span>
|
||||
@@ -107,16 +107,23 @@
|
||||
</ul>
|
||||
</el-skeleton>
|
||||
</el-card>
|
||||
<el-card shadow="never" class="ml-10px">
|
||||
<el-card shadow="never" :class="mobile ? 'flex-1 ml-10px' : 'ml-10px'">
|
||||
<template #header>
|
||||
<div class="flex justify-between"> 本月成交榜Top10 </div>
|
||||
</template>
|
||||
<el-skeleton :loading="loading" animated>
|
||||
<el-table :data="signList" size="small">
|
||||
<el-table-column type="index" />
|
||||
<el-table-column prop="username" label="姓名" width="100" />
|
||||
<el-table-column prop="followNum" label="成交数量" width="70" />
|
||||
<el-table-column prop="deptName" label="所属组织" />
|
||||
<el-table-column type="index" width="40" />
|
||||
<el-table-column prop="username" label="姓名" width="80" />
|
||||
<el-table-column prop="followNum" label="成交数量" width="90" sortable />
|
||||
<el-table-column
|
||||
v-if="appStore.getAppInfo?.instanceType != 1"
|
||||
prop="signPrice"
|
||||
label="成交额"
|
||||
min-width="90"
|
||||
sortable
|
||||
/>
|
||||
<el-table-column prop="deptName" label="所属组织" width="150" />
|
||||
</el-table>
|
||||
</el-skeleton>
|
||||
</el-card>
|
||||
@@ -142,12 +149,15 @@ import { useUserStore } from '@/store/modules/user'
|
||||
import avatarImg from '@/assets/imgs/avatar.gif'
|
||||
import { lineOptions } from './echarts-data'
|
||||
import * as HomeApi from '@/api/home/index'
|
||||
import { useAppStore } from '@/store/modules/app'
|
||||
|
||||
const { t } = useI18n()
|
||||
const userStore = useUserStore()
|
||||
const appStore = useAppStore()
|
||||
const loading = ref(false)
|
||||
const avatar = userStore.getUser.avatar ? userStore.getUser.avatar : avatarImg
|
||||
const username = userStore.getUser.nickname
|
||||
const mobile = appStore.getMobile
|
||||
|
||||
const followDate = ref(1)
|
||||
|
||||
|
||||
248
src/views/Home/SalesReport.vue
Normal file
248
src/views/Home/SalesReport.vue
Normal file
@@ -0,0 +1,248 @@
|
||||
<template>
|
||||
<ContentWrap>
|
||||
<el-form :model="searchForm" label-width="0" inline>
|
||||
<el-form-item>
|
||||
<el-date-picker
|
||||
v-model="searchForm.consultTime"
|
||||
type="daterange"
|
||||
format="YYYY-MM-DD"
|
||||
value-format="YYYY-MM-DD"
|
||||
start-placeholder="选择日期"
|
||||
end-placeholder="选择日期"
|
||||
:clearable="false"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-input v-model="searchForm.nickname" placeholder="销售姓名" clearable />
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-tree-select
|
||||
v-model="searchForm.sourceId"
|
||||
:data="sourceOptions"
|
||||
:props="defaultProps"
|
||||
check-strictly
|
||||
clearable
|
||||
filterable
|
||||
node-key="sourceId"
|
||||
placeholder="请选择渠道"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item v-if="appStore.getAppInfo?.instanceType == 1">
|
||||
<el-select
|
||||
v-model="searchForm.licenseTypeList"
|
||||
placeholder="选择驾照类型"
|
||||
clearable
|
||||
multiple
|
||||
>
|
||||
<el-option
|
||||
v-for="item in licenseTypeOptions"
|
||||
:key="item.label"
|
||||
:label="item.label"
|
||||
:value="item.label"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item>
|
||||
<el-button @click="handleSearch">查询</el-button>
|
||||
<el-button @click="handleReset">重置</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
|
||||
<el-table
|
||||
v-loading="loading"
|
||||
:data="tableList"
|
||||
border
|
||||
stripe
|
||||
show-summary
|
||||
:summary-method="getSummaries"
|
||||
>
|
||||
<el-table-column type="index" width="60" fixed="left" align="center" />
|
||||
<el-table-column prop="nickname" label="姓名" width="80" fixed="left">
|
||||
<template #default="{ row }">
|
||||
<el-button type="primary" style="padding: 0" text @click="handleDetail(row)">{{
|
||||
row.nickname
|
||||
}}</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="newClueNumber" sortable min-width="100">
|
||||
<template #header>
|
||||
<Tooltip message="咨询日期在所选周期内的线索总数" />
|
||||
<span>新线索数</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="成交数" prop="signNumber" sortable min-width="100">
|
||||
<template #header>
|
||||
<Tooltip message="成交日期在所选周期内的成交数" />
|
||||
<span>成交数</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column
|
||||
label="成交率"
|
||||
prop="signRate"
|
||||
sortable
|
||||
min-width="100"
|
||||
:formatter="(row) => row.signRate + '%'"
|
||||
/>
|
||||
<el-table-column label="跟进数/日" prop="dayFollowNumber" sortable min-width="100" />
|
||||
<el-table-column label="成交数/日" prop="daySignNumber" sortable min-width="100" />
|
||||
<el-table-column label="平均成交周期" prop="averageSignPeriod" sortable min-width="100">
|
||||
<template #header>
|
||||
<Tooltip message="平均每条成交的线索,从咨询开始到成交所需要的天数" />
|
||||
<span>平均成交周期</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="grossProfitOfSingleSign" sortable min-width="100">
|
||||
<template #header>
|
||||
<Tooltip
|
||||
message="平均每笔成交订单的公司利润【按照登记,不按审核到账】=总公司利润/总成交数"
|
||||
/>
|
||||
<span>毛利/单</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="payPriceOfSingleSign" sortable min-width="100">
|
||||
<template #header>
|
||||
<Tooltip
|
||||
message="平均每笔成交订单的额外支出(返费等)【按照登记,不按审核到账】=总支出/总成交数"
|
||||
/>
|
||||
<span>支出/单</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="costOfSingleClue" sortable min-width="100">
|
||||
<template #header>
|
||||
<Tooltip message="平均每条线索的成本=总线索成本/新线索条数" />
|
||||
<span>线索成本/条</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="netProfitOfSingleSign" sortable min-width="100">
|
||||
<template #header>
|
||||
<Tooltip message="=(总公司利润-总支出-总线索成本)/总成交数" />
|
||||
<span>净利润/单</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="总线索成本" prop="clueCostTotal" sortable min-width="100" />
|
||||
<el-table-column label="总支出" prop="payPriceTotal" sortable min-width="100" />
|
||||
<el-table-column label="总利润" prop="profitTotal" sortable min-width="100" />
|
||||
<el-table-column prop="efficiency" sortable min-width="100">
|
||||
<template #header>
|
||||
<Tooltip message="=总利润/(总成本+总支出)" />
|
||||
<span>能效</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="撞单数" prop="repeatClueNum" sortable min-width="100" />
|
||||
<el-table-column label="撞单成交数" prop="repeatClueSignNum" sortable min-width="100" />
|
||||
</el-table>
|
||||
|
||||
<DialogSalerReportDetail
|
||||
:licenseTypeOptions="licenseTypeOptions"
|
||||
:source-options="sourceOptions"
|
||||
ref="SalerDetailDialog"
|
||||
/>
|
||||
</ContentWrap>
|
||||
</template>
|
||||
|
||||
<script setup name="SalesReport">
|
||||
import DialogSalerReportDetail from './Comp/DialogSalerReportDetail.vue'
|
||||
|
||||
import * as reportApi from '@/api/home/reportSaler'
|
||||
import { getSimpleSourceList as getResourceOptions } from '@/api/clue/source'
|
||||
|
||||
import { removeNullField } from '@/utils'
|
||||
import { formatDate } from '@/utils/formatTime'
|
||||
import { handleTree } from '@/utils/tree'
|
||||
import { getIntDictOptions } from '@/utils/dict'
|
||||
import { useAppStore } from '@/store/modules/app'
|
||||
|
||||
const appStore = useAppStore()
|
||||
onMounted(() => {
|
||||
getOptions()
|
||||
handleReset()
|
||||
handleSearch()
|
||||
})
|
||||
|
||||
const defaultProps = {
|
||||
children: 'children',
|
||||
label: 'sourceName',
|
||||
value: 'sourceId',
|
||||
isLeaf: 'leaf'
|
||||
}
|
||||
|
||||
const sourceOptions = ref([])
|
||||
const licenseTypeOptions = ref([])
|
||||
|
||||
const searchForm = ref({})
|
||||
|
||||
function handleReset() {
|
||||
const year = new Date().getFullYear()
|
||||
const month = new Date().getMonth() + 1 + ''
|
||||
searchForm.value = {
|
||||
nickname: undefined,
|
||||
consultTime: [`${year}-${month.padStart(2, '0')}-01`, formatDate(new Date())],
|
||||
licenseTypeList: [],
|
||||
sourceId: undefined
|
||||
}
|
||||
}
|
||||
|
||||
const loading = ref(false)
|
||||
const tableList = ref([])
|
||||
const avgData = ref({})
|
||||
async function handleSearch() {
|
||||
loading.value = true
|
||||
try {
|
||||
const data = await reportApi.getList(removeNullField(searchForm.value))
|
||||
tableList.value = data.personDataVOList
|
||||
avgData.value = data.averageDataVO
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
function getSummaries({ columns, data }) {
|
||||
const sums = []
|
||||
columns.forEach((column, index) => {
|
||||
if (index == 0) {
|
||||
sums[index] = '统计'
|
||||
return
|
||||
}
|
||||
|
||||
const doubleSumsColumns = [
|
||||
'newClueNumber',
|
||||
'signNumber',
|
||||
'clueCostTotal',
|
||||
'payPriceTotal',
|
||||
'profitTotal'
|
||||
]
|
||||
if (column.property in avgData.value) {
|
||||
sums[index] = `均值: ${avgData.value[column.property]}`
|
||||
} else if (doubleSumsColumns.includes(column.property)) {
|
||||
const values = data.map((item) => Number(item[column.property]))
|
||||
let sum = values.reduce((prev, curr) => prev + curr, 0)
|
||||
let avg = sum / values.length
|
||||
if (['newClueNumber', 'signNumber'].includes(column.property)) {
|
||||
avg = Math.floor(avg)
|
||||
} else {
|
||||
sum = sum.toFixed(2)
|
||||
avg = avg.toFixed(2)
|
||||
}
|
||||
sums[index] = h('div', {}, [h('div', {}, `合计:${sum}`), h('div', {}, `均值:${avg}`)])
|
||||
} else {
|
||||
sums[index] = ''
|
||||
}
|
||||
})
|
||||
return sums
|
||||
}
|
||||
|
||||
const SalerDetailDialog = ref()
|
||||
function handleDetail(info) {
|
||||
SalerDetailDialog.value.open(info, searchForm.value)
|
||||
}
|
||||
|
||||
function getOptions() {
|
||||
getResourceOptions().then((data) => {
|
||||
sourceOptions.value = handleTree(data, 'sourceId')
|
||||
})
|
||||
licenseTypeOptions.value = getIntDictOptions('license_type')
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped></style>
|
||||
13
src/views/Home/SignReport.vue
Normal file
13
src/views/Home/SignReport.vue
Normal file
@@ -0,0 +1,13 @@
|
||||
<template>
|
||||
<div>
|
||||
<SignTotalCard />
|
||||
<SignListCard />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup name="SignReport">
|
||||
import SignTotalCard from './Comp/SignTotalCard.vue'
|
||||
import SignListCard from './Comp/SignListCard.vue'
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped></style>
|
||||
@@ -1,7 +0,0 @@
|
||||
<template>
|
||||
<div> </div>
|
||||
</template>
|
||||
|
||||
<script setup></script>
|
||||
|
||||
<style lang="scss" scoped></style>
|
||||
225
src/views/Kpi/Appraise/Components/DialogAppraise.vue
Normal file
225
src/views/Kpi/Appraise/Components/DialogAppraise.vue
Normal file
@@ -0,0 +1,225 @@
|
||||
<template>
|
||||
<Dialog :title="title" v-model="show" width="800px">
|
||||
<el-form v-loading="formLoading" :model="form" ref="formRef" :rules="rules" label-width="80px">
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="12" :offset="0">
|
||||
<el-form-item label="考核指标" prop="examineTarget">
|
||||
<el-input v-model="form.examineTarget" placeholder="请输入" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12" :offset="0">
|
||||
<el-form-item label="生效日期" prop="effectiveDate">
|
||||
<el-date-picker
|
||||
v-model="form.effectiveDate"
|
||||
:disabled="form.id"
|
||||
type="date"
|
||||
format="YYYY-MM-DD"
|
||||
value-format="YYYY-MM-DD"
|
||||
placeholder="选择日期时间"
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="24" :offset="0">
|
||||
<el-form-item label="模式" prop="type">
|
||||
<el-radio-group v-model="form.type">
|
||||
<el-radio
|
||||
v-for="(item, index) in prop.kpiModeOoptions"
|
||||
:key="index"
|
||||
:label="item.value"
|
||||
>
|
||||
{{ item.label }}
|
||||
</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="12" :offset="0">
|
||||
<el-form-item label="权重" prop="weight">
|
||||
<el-input-number v-model="form.weight" :min="0" :step="1" :controls="false" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12" :offset="0">
|
||||
<el-form-item label="评分上限" prop="examineScore">
|
||||
<el-input-number v-model="form.examineScore" :min="0" :step="1" :controls="false" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="24" :offset="0">
|
||||
<el-form-item label="考核内容" prop="examineContent">
|
||||
<Editor
|
||||
v-model:modelValue="form.examineContent"
|
||||
height="150px"
|
||||
:toolbarConfig="{
|
||||
toolbarKeys: []
|
||||
}"
|
||||
style="width: 100%"
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="24" :offset="0">
|
||||
<el-form-item label="考核规则" prop="examineRule">
|
||||
<Editor
|
||||
v-model:modelValue="form.examineRule"
|
||||
height="150px"
|
||||
:toolbarConfig="{ toolbarKeys: [] }"
|
||||
style="width: 100%"
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="24" :offset="0">
|
||||
<el-form-item label="考核员工" prop="examinedUserIdList">
|
||||
<div>
|
||||
<el-checkbox
|
||||
v-model="checkAll"
|
||||
:indeterminate="isIndeterminate"
|
||||
@change="handleCheckAllChange"
|
||||
>
|
||||
全选
|
||||
</el-checkbox>
|
||||
<el-checkbox-group v-model="form.examinedUserIdList" @change="handleCheckedChange">
|
||||
<el-checkbox
|
||||
v-for="item in employeeOptions"
|
||||
:key="item.id"
|
||||
:disabled="item.status == 1"
|
||||
:label="item.id"
|
||||
:value="item.id"
|
||||
>
|
||||
{{ item.name }}
|
||||
</el-checkbox>
|
||||
</el-checkbox-group>
|
||||
</div>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<span>
|
||||
<el-button @click="show = false">取 消</el-button>
|
||||
<el-button type="primary" :disabled="formLoading" @click="handleSave">保 存</el-button>
|
||||
</span>
|
||||
</template>
|
||||
</Dialog>
|
||||
</template>
|
||||
|
||||
<script setup name="DialogAppraise">
|
||||
import * as KpiApi from '@/api/kpi/index.js'
|
||||
|
||||
const prop = defineProps({
|
||||
kpiModeOoptions: {
|
||||
type: Array,
|
||||
default: () => []
|
||||
}
|
||||
})
|
||||
|
||||
const { t } = useI18n() // 国际化
|
||||
const message = useMessage() // 消息弹窗
|
||||
|
||||
const show = ref(false)
|
||||
const title = ref('')
|
||||
const formType = ref('create')
|
||||
|
||||
const form = ref({})
|
||||
const formLoading = ref(false)
|
||||
const rules = ref({
|
||||
examineTarget: { required: true, message: '标题不可为空', trigger: 'blur' },
|
||||
effectiveDate: { required: true, message: '生效日期不可为空', trigger: 'blur,change' },
|
||||
examineContent: { required: true, message: '标题不可为空', trigger: 'blur' },
|
||||
examineRule: { required: true, message: '标题不可为空', trigger: 'blur' }
|
||||
})
|
||||
|
||||
async function open(type, val) {
|
||||
show.value = true
|
||||
title.value = type == 'update' ? '修改考核项' : '新增考核项'
|
||||
formType.value = type
|
||||
resetForm()
|
||||
if (val) {
|
||||
formLoading.value = true
|
||||
try {
|
||||
form.value = await KpiApi.getKpiDetail(val)
|
||||
} finally {
|
||||
formLoading.value = false
|
||||
}
|
||||
}
|
||||
getOptions()
|
||||
}
|
||||
|
||||
function getOptions() {
|
||||
KpiApi.getKpiEmployees().then((data) => {
|
||||
employeeOptions.value = data
|
||||
if (formType.value == 'update') {
|
||||
handleCheckedChange(form.value.examinedUserIdList)
|
||||
} else {
|
||||
handleCheckAllChange(true)
|
||||
checkAll.value = true
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
function resetForm() {
|
||||
form.value = {
|
||||
examineTarget: '',
|
||||
type: '3',
|
||||
weight: 0,
|
||||
examineContent: ``,
|
||||
examineRule: ``,
|
||||
examineScore: 5,
|
||||
examinedUserIdList: []
|
||||
}
|
||||
}
|
||||
|
||||
defineExpose({ open }) // 提供 open 方法,用于打开弹窗
|
||||
|
||||
const formRef = ref()
|
||||
|
||||
/** 提交表单 */
|
||||
const emit = defineEmits(['success']) // 定义 success 事件,用于操作成功后的回调
|
||||
async function handleSave() {
|
||||
// 校验表单
|
||||
if (!formRef.value) return
|
||||
const valid = await formRef.value.validate()
|
||||
if (!valid) return
|
||||
// 提交请求
|
||||
formLoading.value = true
|
||||
try {
|
||||
if (formType.value === 'create') {
|
||||
await KpiApi.createKpi(form.value)
|
||||
message.success(t('common.createSuccess'))
|
||||
} else {
|
||||
await KpiApi.updateKpi(form.value)
|
||||
message.success(t('common.updateSuccess'))
|
||||
}
|
||||
show.value = false
|
||||
// 发送操作成功的事件
|
||||
emit('success')
|
||||
} finally {
|
||||
formLoading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
const checkAll = ref(false)
|
||||
const isIndeterminate = ref(false)
|
||||
const employeeOptions = ref([])
|
||||
|
||||
function handleCheckAllChange(val) {
|
||||
form.value.examinedUserIdList = val ? employeeOptions.value.map((it) => it.id) : []
|
||||
isIndeterminate.value = false
|
||||
}
|
||||
|
||||
function handleCheckedChange(value) {
|
||||
const checkedCount = value.length
|
||||
checkAll.value = checkedCount === employeeOptions.value.length
|
||||
isIndeterminate.value = checkedCount > 0 && checkedCount < employeeOptions.value.length
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped></style>
|
||||
156
src/views/Kpi/Appraise/index.vue
Normal file
156
src/views/Kpi/Appraise/index.vue
Normal file
@@ -0,0 +1,156 @@
|
||||
<template>
|
||||
<div>
|
||||
<!-- 搜索工作栏 -->
|
||||
<el-form :model="searchForm" ref="queryFormRef" inline label-width="0">
|
||||
<el-form-item>
|
||||
<el-input v-model="searchForm.examineTarget" placeholder="请输入考核指标" clearable />
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button @click="handleQuery" v-hasPermi="['kpi:appraise:search']"> 搜索</el-button>
|
||||
<el-button @click="resetQuery" v-hasPermi="['kpi:appraise:reset']"> 重置</el-button>
|
||||
<el-button
|
||||
type="primary"
|
||||
plain
|
||||
@click="openForm('create')"
|
||||
v-hasPermi="['kpi:appraise:add']"
|
||||
>
|
||||
新增
|
||||
</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
|
||||
<!-- 列表 -->
|
||||
<el-table v-loading="loading" :data="list" border>
|
||||
<el-table-column prop="examineTarget" label="考核指标" width="180" />
|
||||
<el-table-column prop="typeName" label="分值模式" width="90" :formatter="getKpiModeLabel" />
|
||||
<el-table-column prop="weight" label="权重%" width="90" />
|
||||
<el-table-column label="考核内容">
|
||||
<template #default="{ row }">
|
||||
<div v-dompurify-html="row.examineContent"></div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="考核规则" prop="examineRule">
|
||||
<template #default="{ row }">
|
||||
<div v-dompurify-html="row.examineRule"></div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="评分上限" prop="examineScore" width="90" />
|
||||
<el-table-column label="考核人数" width="90">
|
||||
<template #default="{ row }">
|
||||
{{ row.examinedUserIdList?.length || 0 }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="生效日期" prop="effectiveDate" width="120" />
|
||||
<el-table-column label="操作" class-name="fixed-width" width="120">
|
||||
<template #default="{ row }">
|
||||
<el-button
|
||||
link
|
||||
type="primary"
|
||||
@click="openForm('update', row.id)"
|
||||
v-hasPermi="['kpi:appraise:update']"
|
||||
>
|
||||
修改
|
||||
</el-button>
|
||||
<el-button
|
||||
link
|
||||
type="danger"
|
||||
@click="handleDelete(row.id)"
|
||||
v-hasPermi="['kpi:appraise:remove']"
|
||||
>
|
||||
删除
|
||||
</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<Pagination
|
||||
v-model:limit="searchForm.pageSize"
|
||||
v-model:page="searchForm.pageNo"
|
||||
:total="total"
|
||||
@pagination="getList"
|
||||
/>
|
||||
|
||||
<DialogAppraise ref="formRef" :kpiModeOoptions="kpiModeOoptions" @success="handleQuery" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup name="KpiContent">
|
||||
import { removeNullField } from '@/utils'
|
||||
import DialogAppraise from './Components/DialogAppraise.vue'
|
||||
import * as KpiApi from '@/api/kpi/index.js'
|
||||
import { getGeneralSysDictData } from '@/api/system/dict/dict.data'
|
||||
|
||||
const { t } = useI18n() // 国际化
|
||||
const message = useMessage() // 消息弹窗
|
||||
const loading = ref(false) // 列表的加载中
|
||||
const list = ref([]) // 列表的数据
|
||||
const searchForm = ref({
|
||||
examineTarget: undefined,
|
||||
pageNo: 1,
|
||||
pageSize: 20
|
||||
})
|
||||
const total = ref(0)
|
||||
|
||||
/** 初始化 **/
|
||||
onMounted(() => {
|
||||
getOptions()
|
||||
handleQuery()
|
||||
})
|
||||
|
||||
const kpiModeOoptions = ref([])
|
||||
function getOptions() {
|
||||
getGeneralSysDictData('examine_type').then((data) => {
|
||||
kpiModeOoptions.value = data
|
||||
})
|
||||
}
|
||||
|
||||
function getKpiModeLabel(row) {
|
||||
return kpiModeOoptions.value.find((it) => it.value == row.type)?.label
|
||||
}
|
||||
|
||||
const getList = async () => {
|
||||
loading.value = true
|
||||
try {
|
||||
const data = await KpiApi.getKpiPage(removeNullField(searchForm.value))
|
||||
list.value = data.list
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
/** 搜索按钮操作 */
|
||||
const handleQuery = () => {
|
||||
searchForm.value.pageNo = 1
|
||||
getList()
|
||||
}
|
||||
|
||||
/** 重置按钮操作 */
|
||||
const resetQuery = () => {
|
||||
searchForm.value = {
|
||||
examineTarget: undefined,
|
||||
pageNo: 1,
|
||||
pageSize: 20
|
||||
}
|
||||
handleQuery()
|
||||
}
|
||||
|
||||
/** 添加/修改操作 */
|
||||
const formRef = ref()
|
||||
const openForm = (type, id) => {
|
||||
formRef.value.open(type, id)
|
||||
}
|
||||
|
||||
/** 删除按钮操作 */
|
||||
const handleDelete = async (id) => {
|
||||
try {
|
||||
// 删除的二次确认
|
||||
await message.delConfirm()
|
||||
// 发起删除
|
||||
await KpiApi.deleteKpi(id)
|
||||
message.success(t('common.delSuccess'))
|
||||
// 刷新列表
|
||||
await getList()
|
||||
} catch {}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped></style>
|
||||
214
src/views/Kpi/Score/index.vue
Normal file
214
src/views/Kpi/Score/index.vue
Normal file
@@ -0,0 +1,214 @@
|
||||
<template>
|
||||
<div>
|
||||
<!-- 搜索工作栏 -->
|
||||
<el-form :model="searchForm" inline label-width="0">
|
||||
<el-form-item>
|
||||
<el-date-picker
|
||||
v-model="searchForm.period"
|
||||
type="month"
|
||||
format="YYYY-MM"
|
||||
value-format="YYYY-MM"
|
||||
:clearable="false"
|
||||
style="width: 100%"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button @click="getList" v-hasPermi="['kpi:score:search']"> 搜索</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<el-tabs v-model="employeeId" @tab-click="getList">
|
||||
<el-tab-pane
|
||||
v-for="item in employeeOptions"
|
||||
:key="item.examinedUser"
|
||||
:name="item.examinedUser"
|
||||
>
|
||||
<template #label>
|
||||
<span>{{ item.examinedUserName }}</span>
|
||||
<el-badge :is-dot="item.notExaminedCount > 0" type="danger" />
|
||||
</template>
|
||||
<div class="mb-10px">
|
||||
<el-tag v-if="periodSaved == 1" type="danger">
|
||||
当前周期已封存,不可修改当月绩效考核
|
||||
</el-tag>
|
||||
<el-tag v-else type="warning">
|
||||
当前周期未封存,可选择修改当月绩效考核,两秒内不操作将自动保存考核结果
|
||||
</el-tag>
|
||||
</div>
|
||||
|
||||
<el-table
|
||||
v-loading="loading"
|
||||
:data="list"
|
||||
border
|
||||
show-summary
|
||||
:summary-method="getSummaries"
|
||||
>
|
||||
<el-table-column prop="examineTarget" label="考核指标" width="180" />
|
||||
<el-table-column prop="typeName" label="分值模式" width="90" />
|
||||
<el-table-column prop="weight" label="权重%" width="90" />
|
||||
<el-table-column label="考核内容">
|
||||
<template #default="{ row }">
|
||||
<div v-dompurify-html="row.examineContent"></div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="考核规则" prop="examineRule">
|
||||
<template #default="{ row }">
|
||||
<div v-dompurify-html="row.examineRule"></div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="评分上限" prop="examineScore" width="90" />
|
||||
<el-table-column label="考核评分" width="120">
|
||||
<template #default="{ row }">
|
||||
<div v-if="periodSaved == 1">{{ row.score }}</div>
|
||||
<el-input-number
|
||||
v-else
|
||||
v-model="row.score"
|
||||
:min="0"
|
||||
:max="row.examineScore"
|
||||
:step="1"
|
||||
:controls="false"
|
||||
size="small"
|
||||
style="width: 100%"
|
||||
@input="handleSave(row)"
|
||||
/>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="评分备注" width="200">
|
||||
<template #default="{ row }">
|
||||
<div v-if="periodSaved == 1">{{ row.remark }}</div>
|
||||
<el-input
|
||||
v-else
|
||||
v-model="row.remark"
|
||||
type="textarea"
|
||||
:rows="3"
|
||||
size="small"
|
||||
style="width: 100%"
|
||||
@input="handleSave(row)"
|
||||
/>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="加权得分" prop="weightSocre" width="90" />
|
||||
<el-table-column label="操作" fixed="right" width="100">
|
||||
<template #default="{ row }">
|
||||
<el-button style="padding: 0" type="danger" text @click="handleDelete(row)">
|
||||
删除
|
||||
</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</el-tab-pane>
|
||||
</el-tabs>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup name="KpiScore">
|
||||
import { formatDate } from '@/utils/formatTime'
|
||||
import * as KpiApi from '@/api/kpi/score.js'
|
||||
|
||||
const { t } = useI18n() // 国际化
|
||||
const message = useMessage() // 消息弹窗
|
||||
const loading = ref(false) // 列表的加载中
|
||||
const list = ref([]) // 列表的数据
|
||||
const searchForm = ref({
|
||||
period: undefined
|
||||
})
|
||||
|
||||
const periodSaved = ref(0)
|
||||
|
||||
const employeeOptions = ref([])
|
||||
const employeeId = ref('')
|
||||
|
||||
onMounted(() => {
|
||||
// 默认取上个月
|
||||
const today = new Date()
|
||||
searchForm.value.period = formatDate(
|
||||
new Date(today.getFullYear(), today.getMonth() - 1),
|
||||
'YYYY-MM'
|
||||
)
|
||||
|
||||
KpiApi.getKpiPeriodStatus({ period: searchForm.value.period }).then((data) => {
|
||||
periodSaved.value = data.status
|
||||
})
|
||||
|
||||
getList()
|
||||
})
|
||||
|
||||
async function getList() {
|
||||
try {
|
||||
// 获取当前要考核的人员
|
||||
const arr = await KpiApi.getKpiEmployees(searchForm.value)
|
||||
employeeOptions.value = arr || []
|
||||
if (employeeOptions.value.length > 0) {
|
||||
if (!employeeId.value) {
|
||||
employeeId.value = employeeOptions.value[0].examinedUser
|
||||
}
|
||||
} else {
|
||||
message.error('当月无待考核员工!!!')
|
||||
return
|
||||
}
|
||||
list.value =
|
||||
employeeOptions.value.find((it) => it.examinedUser == employeeId.value)
|
||||
?.examineScoreRespVOList || []
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
let timer = ref(null)
|
||||
const lastId = ref('')
|
||||
function handleSave(row) {
|
||||
try {
|
||||
lastId.value == row.id && clearTimeout(timer.value)
|
||||
timer.value = setTimeout(async () => {
|
||||
lastId.value = row.id
|
||||
// message.success('正在保存数据,请稍后')
|
||||
await KpiApi.saveKpiScore({
|
||||
id: row.id,
|
||||
score: row.score,
|
||||
remark: row.remark
|
||||
})
|
||||
message.success('修改成功!')
|
||||
getList()
|
||||
}, 2000)
|
||||
} catch (err) {
|
||||
console.log(err)
|
||||
}
|
||||
}
|
||||
|
||||
async function handleDelete(row) {
|
||||
try {
|
||||
// 删除的二次确认
|
||||
await message.delConfirm(
|
||||
`删除后,该员工于${searchForm.value.period}将不再考核此项,是否确认删除?`
|
||||
)
|
||||
// 发起删除
|
||||
await KpiApi.deleteKpiItem({ id: row.id })
|
||||
message.success(t('common.delSuccess'))
|
||||
// 刷新列表
|
||||
await getList()
|
||||
} catch {}
|
||||
}
|
||||
|
||||
function getSummaries({ columns, data }) {
|
||||
let sums = []
|
||||
columns.forEach((column, index) => {
|
||||
if (index == 0) {
|
||||
sums[index] = '考核总分'
|
||||
return
|
||||
}
|
||||
const values = data.map((item) => Number(item[column.property]))
|
||||
if (!values.every((value) => Number.isNaN(value))) {
|
||||
if (column.property == 'weightSocre') {
|
||||
const employee = employeeOptions.value.find((it) => it.examinedUser == employeeId.value)
|
||||
sums[index] = employee.scoreTotal + '分'
|
||||
} else {
|
||||
sums[index] = ''
|
||||
}
|
||||
} else {
|
||||
sums[index] = ''
|
||||
}
|
||||
})
|
||||
return sums
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped></style>
|
||||
@@ -1,54 +1,25 @@
|
||||
<template>
|
||||
<div
|
||||
:class="prefixCls"
|
||||
class="h-[100%] relative <xl:bg-v-dark <sm:px-10px <xl:px-10px <md:px-10px"
|
||||
class="h-[100%] relative dark:bg-v-dark <sm:px-10px <xl:px-10px <md:px-10px"
|
||||
>
|
||||
<div class="relative h-full flex mx-auto">
|
||||
<div
|
||||
:class="`${prefixCls}__left flex-1 bg-gray-500 bg-opacity-20 relative p-30px <xl:hidden`"
|
||||
>
|
||||
<div :class="`${prefixCls}__left w-[30%] <lg:w-[100%] left-box relative p-30px <xl:p-20px`">
|
||||
<!-- 左上角的 logo + 系统标题 -->
|
||||
<div class="flex items-center relative text-white">
|
||||
<img alt="" class="w-48px h-48px mr-10px" :src="appInfo.instanceIcon" />
|
||||
<span class="text-20px font-bold">{{ underlineToHump(appInfo.instanceName) }}</span>
|
||||
</div>
|
||||
<!-- 左边的背景图 + 欢迎语 -->
|
||||
<div class="flex justify-center">
|
||||
<TransitionGroup
|
||||
appear
|
||||
enter-active-class="animate__animated animate__bounceInLeft"
|
||||
tag="div"
|
||||
>
|
||||
<img key="1" alt="" class="w-350px" src="@/assets/svgs/login-box-bg.svg" />
|
||||
<!-- <div key="2" class="text-3xl text-white">{{ t('login.welcome') }}</div> -->
|
||||
</TransitionGroup>
|
||||
</div>
|
||||
|
||||
<div class="landIn">
|
||||
<transition-group name="text-animation">
|
||||
<span v-for="(char, index) in chars" :key="index">{{ char }}</span>
|
||||
</transition-group>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex-1 p-30px <sm:p-10px dark:bg-v-dark relative">
|
||||
<!-- 右上角的主题、语言选择 -->
|
||||
<div class="flex justify-between items-center text-white @2xl:justify-end @xl:justify-end">
|
||||
<div class="flex items-center @2xl:hidden @xl:hidden">
|
||||
<img alt="" class="w-48px h-48px mr-10px" :src="appInfo.instanceIcon" />
|
||||
<span class="text-20px font-bold">{{ underlineToHump(appInfo.instanceName) }}</span>
|
||||
</div>
|
||||
<div class="flex justify-end items-center space-x-10px">
|
||||
<ThemeSwitch />
|
||||
<!-- <LocaleDropdown class="<xl:text-white dark:text-white" /> -->
|
||||
</div>
|
||||
<div class="flex items-center relative">
|
||||
<h2 class="mb-3 text-2xl font-bold">
|
||||
欢迎使用 {{ underlineToHump(appInfo.instanceName) }}
|
||||
</h2>
|
||||
<!-- <img alt="" class="w-48px h-48px mr-10px" :src="appInfo.instanceIcon" /> -->
|
||||
<!-- <span class="text-20px font-bold">{{ underlineToHump(appInfo.instanceName) }}</span> -->
|
||||
</div>
|
||||
<!-- 右边的登录界面 -->
|
||||
<Transition appear enter-active-class="animate__animated animate__bounceInRight">
|
||||
<div
|
||||
class="h-full flex items-center m-auto w-[100%] @2xl:max-w-500px @xl:max-w-500px @md:max-w-500px @lg:max-w-500px"
|
||||
class="flex items-center m-auto w-[100%] @2xl:max-w-500px @xl:max-w-500px @md:max-w-500px @lg:max-w-500px <xl:mt-20px"
|
||||
>
|
||||
<!-- 账号登录 -->
|
||||
<LoginForm class="p-20px h-auto m-auto <xl:(rounded-3xl light:bg-white)" />
|
||||
<LoginForm class="p-40px <xl:p-10px h-auto m-auto" />
|
||||
<!-- 手机登录 -->
|
||||
<!-- <MobileForm class="p-20px h-auto m-auto <xl:(rounded-3xl light:bg-white)" /> -->
|
||||
<!-- 二维码登录 -->
|
||||
@@ -59,6 +30,39 @@
|
||||
<!-- <SSOLoginVue class="p-20px h-auto m-auto <xl:(rounded-3xl light:bg-white)" /> -->
|
||||
</div>
|
||||
</Transition>
|
||||
<!-- 左边的背景图 + 欢迎语 -->
|
||||
<div class="flex justify-center">
|
||||
<TransitionGroup
|
||||
appear
|
||||
enter-active-class="animate__animated animate__bounceInLeft"
|
||||
tag="div"
|
||||
>
|
||||
<!-- <img key="1" alt="" class="w-350px" src="@/assets/svgs/login-box-bg.svg" /> -->
|
||||
<img key="1" alt="" class="w-[50%] m-auto" src="@/assets/imgs/login2.gif" />
|
||||
</TransitionGroup>
|
||||
</div>
|
||||
|
||||
<div class="landIn">
|
||||
<transition-group name="text-animation">
|
||||
<span v-for="(char, index) in chars" :key="index">{{ char }}</span>
|
||||
</transition-group>
|
||||
</div>
|
||||
</div>
|
||||
<div class="login-box flex-1 p-30px <sm:p-10px dark:bg-v-dark <lg:hidden relative">
|
||||
<!-- 右上角的主题、语言选择 -->
|
||||
<div class="flex justify-between items-center text-white @2xl:justify-end @xl:justify-end">
|
||||
<div class="flex items-center @2xl:hidden @xl:hidden">
|
||||
<!-- <img alt="" class="w-48px h-48px mr-10px" :src="appInfo.instanceIcon" /> -->
|
||||
<!-- <span class="text-20px font-bold text-black">
|
||||
欢迎使用 {{ underlineToHump(appInfo.instanceName) }}
|
||||
</span> -->
|
||||
</div>
|
||||
<div class="flex justify-end items-center space-x-10px">
|
||||
<ThemeSwitch />
|
||||
<!-- <LocaleDropdown class="<xl:text-white dark:text-white" /> -->
|
||||
</div>
|
||||
</div>
|
||||
<img src="@/assets/imgs/shisong.jpg" width="80%" alt="" srcset="" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -96,7 +100,7 @@ const chars = computed(() => {
|
||||
return text.slice(0, currentCharIndex.value)
|
||||
})
|
||||
|
||||
async function init() {
|
||||
function init() {
|
||||
const res = currentRoute.value?.query?.tenantId
|
||||
authUtil.setTenantId(res)
|
||||
|
||||
@@ -137,7 +141,7 @@ $prefix-cls: #{$namespace}-login;
|
||||
z-index: -1;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background-image: url('@/assets/svgs/login-bg.svg');
|
||||
// background-image: url('@/assets/svgs/login-bg.svg');
|
||||
background-position: center;
|
||||
background-repeat: no-repeat;
|
||||
content: '';
|
||||
@@ -145,6 +149,20 @@ $prefix-cls: #{$namespace}-login;
|
||||
}
|
||||
}
|
||||
|
||||
// .left-box {
|
||||
// background: #f3f4fb;
|
||||
// background-image: url('@/assets/imgs/login-left-bg.png');
|
||||
// background-repeat: no-repeat;
|
||||
// background-size: 100% 100%;
|
||||
// }
|
||||
|
||||
.login-box {
|
||||
background-image: url('@/assets/imgs/shisong.jpg');
|
||||
background-position: center center;
|
||||
background-repeat: no-repeat;
|
||||
background-size: 80% 80%;
|
||||
}
|
||||
|
||||
.text-animation-enter-active,
|
||||
.text-animation-leave-active {
|
||||
transition: opacity 0.5s;
|
||||
@@ -160,12 +178,11 @@ $prefix-cls: #{$namespace}-login;
|
||||
width: 80%;
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
font-size: 18px;
|
||||
line-height: 1.8;
|
||||
font-size: 1rem;
|
||||
line-height: 1.3;
|
||||
font-family: Lora, serif;
|
||||
white-space: pre;
|
||||
font-weight: 600;
|
||||
color: #6ee1f5;
|
||||
|
||||
span {
|
||||
animation: landIn 0.8s ease-out both;
|
||||
|
||||
@@ -11,10 +11,13 @@
|
||||
>
|
||||
<el-row style="margin-left: -10px; margin-right: -10px">
|
||||
<el-col :span="24" style="padding-left: 10px; padding-right: 10px">
|
||||
<slot name="title"></slot>
|
||||
</el-col>
|
||||
<!-- <el-col :span="24" style="padding-left: 10px; padding-right: 10px">
|
||||
<el-form-item>
|
||||
<LoginFormTitle style="width: 100%" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-col> -->
|
||||
<el-col :span="24" style="padding-left: 10px; padding-right: 10px">
|
||||
<el-form-item v-if="loginData.tenantEnable === 'true'" prop="tenantId">
|
||||
<el-input
|
||||
@@ -107,7 +110,7 @@
|
||||
</template>
|
||||
<script name="LoginForm" setup>
|
||||
import { ElLoading } from 'element-plus'
|
||||
import LoginFormTitle from './LoginFormTitle.vue'
|
||||
// import LoginFormTitle from './LoginFormTitle.vue'
|
||||
|
||||
import { useIcon } from '@/hooks/web/useIcon'
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<Dialog :title="dialogTitle" v-model="dialogVisible" width="800px">
|
||||
<Dialog :title="dialogTitle" v-model="dialogVisible" width="500px">
|
||||
<el-form
|
||||
ref="formRef"
|
||||
v-loading="formLoading"
|
||||
@@ -7,11 +7,17 @@
|
||||
:rules="formRules"
|
||||
label-width="80px"
|
||||
>
|
||||
<el-form-item label="名称" prop="label">
|
||||
<el-input v-model="formData.label" placeholder="请输入供应商名称" />
|
||||
<el-form-item label="名称" prop="supplierName">
|
||||
<el-input v-model="formData.supplierName" placeholder="请输入供应商名称" />
|
||||
</el-form-item>
|
||||
<el-form-item label="排序" prop="sort">
|
||||
<el-input v-model="formData.sort" placeholder="请输入排序" type="number" :min="0" />
|
||||
<el-form-item label="微信群" prop="wechatGroup">
|
||||
<el-input v-model="formData.wechatGroup" placeholder="请输入微信群" />
|
||||
</el-form-item>
|
||||
<el-form-item label="状态" prop="status">
|
||||
<el-radio-group v-model="formData.status">
|
||||
<el-radio :label="0"> 启用 </el-radio>
|
||||
<el-radio :label="1"> 禁用 </el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
<el-form-item label="备注" prop="remark">
|
||||
<el-input
|
||||
@@ -29,7 +35,7 @@
|
||||
</Dialog>
|
||||
</template>
|
||||
<script name="DialogSupplier" setup>
|
||||
import * as dictApi from '@/api/system/dict/dict.data'
|
||||
import * as SupplierApi from '@/api/school/setting/supplier'
|
||||
const { t } = useI18n() // 国际化
|
||||
const message = useMessage() // 消息弹窗
|
||||
|
||||
@@ -38,12 +44,12 @@ const dialogTitle = ref('') // 弹窗的标题
|
||||
const formLoading = ref(false) // 表单的加载中:1)修改时的数据加载;2)提交的按钮禁用
|
||||
const formType = ref('') // 表单的类型:create - 新增;update - 修改
|
||||
const formData = ref({
|
||||
label: '',
|
||||
sort: 1,
|
||||
supplierName: '',
|
||||
wechatGroup: 1,
|
||||
remark: ''
|
||||
})
|
||||
const formRules = reactive({
|
||||
label: [{ required: true, message: '名称不能为空', trigger: 'blur' }]
|
||||
supplierName: [{ required: true, message: '名称不能为空', trigger: 'blur' }]
|
||||
})
|
||||
const formRef = ref() // 表单 Ref
|
||||
|
||||
@@ -57,7 +63,7 @@ const open = async (type, id) => {
|
||||
if (id) {
|
||||
formLoading.value = true
|
||||
try {
|
||||
formData.value = await dictApi.getDictData(id)
|
||||
formData.value = await SupplierApi.getSupplier(id)
|
||||
} finally {
|
||||
formLoading.value = false
|
||||
}
|
||||
@@ -75,14 +81,11 @@ const submitForm = async () => {
|
||||
// 提交请求
|
||||
formLoading.value = true
|
||||
try {
|
||||
if (!formData.value.value) {
|
||||
formData.value.value = formData.value.label
|
||||
}
|
||||
if (formType.value === 'create') {
|
||||
await dictApi.createDictData(formData.value)
|
||||
await SupplierApi.createSupplier(formData.value)
|
||||
message.success(t('common.createSuccess'))
|
||||
} else {
|
||||
await dictApi.updateDictData(formData.value)
|
||||
await SupplierApi.updateSupplier(formData.value)
|
||||
message.success(t('common.updateSuccess'))
|
||||
}
|
||||
dialogVisible.value = false
|
||||
@@ -96,10 +99,9 @@ const submitForm = async () => {
|
||||
/** 重置表单 */
|
||||
const resetForm = () => {
|
||||
formData.value = {
|
||||
label: '',
|
||||
sort: 1,
|
||||
supplierName: '',
|
||||
wechatGroup: '',
|
||||
status: 0,
|
||||
dictType: 'erp_supplier',
|
||||
remark: ''
|
||||
}
|
||||
formRef.value?.resetFields()
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<el-form ref="queryForm" :model="searchForm" label-width="0" inline>
|
||||
<el-form-item>
|
||||
<el-input
|
||||
v-model="searchForm.name"
|
||||
v-model="searchForm.supplierName"
|
||||
placeholder="请输入名称"
|
||||
clearable
|
||||
@keyup.enter="handleQuery"
|
||||
@@ -16,8 +16,13 @@
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<el-table v-loading="loading" :data="tableList">
|
||||
<el-table-column prop="label" label="供应商名称" />
|
||||
<el-table-column prop="sort" label="排序" width="100px" />
|
||||
<el-table-column prop="supplierName" label="供应商名称" />
|
||||
<el-table-column prop="wechatGroup" label="微信群名称" />
|
||||
<el-table-column label="状态" min-width="150" prop="status">
|
||||
<template #default="scope">
|
||||
<dict-tag :type="DICT_TYPE.COMMON_STATUS" :value="scope.row.status" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="remark" label="备注" />
|
||||
<el-table-column
|
||||
label="创建时间"
|
||||
@@ -46,16 +51,16 @@
|
||||
<script setup name="SupplierSet">
|
||||
import { dateFormatter } from '@/utils/formatTime'
|
||||
import DialogSupplier from './DialogSupplier.vue'
|
||||
import * as dictApi from '@/api/system/dict/dict.data'
|
||||
import * as SupplierApi from '@/api/school/setting/supplier'
|
||||
import { DICT_TYPE } from '@/utils/dict'
|
||||
|
||||
const { t } = useI18n() // 国际化
|
||||
const message = useMessage() // 消息弹窗
|
||||
|
||||
const searchForm = ref({
|
||||
label: '',
|
||||
supplierName: '',
|
||||
pageSize: 20,
|
||||
pageNo: 1,
|
||||
dictType: 'erp_supplier'
|
||||
pageNo: 1
|
||||
})
|
||||
|
||||
const total = ref(0)
|
||||
@@ -69,10 +74,9 @@ function handleQuery() {
|
||||
}
|
||||
function resetQuery() {
|
||||
searchForm.value = {
|
||||
label: '',
|
||||
supplierName: '',
|
||||
pageSize: 20,
|
||||
pageNo: 1,
|
||||
dictType: 'erp_supplier'
|
||||
pageNo: 1
|
||||
}
|
||||
getList()
|
||||
}
|
||||
@@ -80,7 +84,7 @@ function resetQuery() {
|
||||
async function getList() {
|
||||
loading.value = true
|
||||
try {
|
||||
const data = await dictApi.getDictDataPage(searchForm.value)
|
||||
const data = await SupplierApi.getSupplierPage(searchForm.value)
|
||||
tableList.value = data.list
|
||||
total.value = data.total
|
||||
} finally {
|
||||
@@ -97,7 +101,7 @@ async function handleDelete(id) {
|
||||
// 删除的二次确认
|
||||
await message.delConfirm()
|
||||
// 发起删除
|
||||
await dictApi.deleteDictData(id)
|
||||
await SupplierApi.deleteSupplier(id)
|
||||
message.success(t('common.delSuccess'))
|
||||
// 刷新列表
|
||||
await getList()
|
||||
|
||||
@@ -49,7 +49,7 @@
|
||||
|
||||
<!-- 列表 -->
|
||||
<el-table v-loading="loading" class="mt-20px" :data="tableList" border>
|
||||
<el-table-column type="expand" width="30">
|
||||
<el-table-column type="expand" width="30" fixed="left">
|
||||
<template #default="{ row }">
|
||||
<div class="pl-100px pr-100px">
|
||||
<el-table :data="row.productSpecList">
|
||||
@@ -71,16 +71,16 @@
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column key="productId" label="产品编码" prop="productId" />
|
||||
<el-table-column show-overflow-tooltip label="产品名称" min-width="200" prop="productName" />
|
||||
<el-table-column label="分类" min-width="90" prop="productCategoryName" />
|
||||
<el-table-column label="品牌" min-width="90" prop="productBrandName" />
|
||||
<el-table-column show-overflow-tooltip label="产品名称" min-width="100" prop="productName" />
|
||||
<el-table-column label="分类" width="90" prop="productCategoryName" />
|
||||
<el-table-column label="品牌" width="90" prop="productBrandName" />
|
||||
<el-table-column label="商品图" min-width="80">
|
||||
<template #default="{ row }">
|
||||
<el-image :src="row.mainImage" @click="imagePreview(row.mainImage)" class="w-30px h-30px" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="简介" min-width="70" prop="productIntro" />
|
||||
<el-table-column :formatter="dateFormatter" label="创建时间" prop="createTime" width="180" />
|
||||
<el-table-column label="简介" min-width="200" prop="productIntro" />
|
||||
<el-table-column :formatter="dateFormatter" label="创建时间" prop="createTime" width="120" />
|
||||
<el-table-column v-for="item in diyFieldList" :key="item.clueParamId" :label="item.label">
|
||||
<template #default="{ row }">
|
||||
<div v-if="item.component == 'DatePicker'">
|
||||
|
||||
@@ -37,9 +37,9 @@
|
||||
<el-select v-model="queryParams.supplier" placeholder="供应商" clearable filterable>
|
||||
<el-option
|
||||
v-for="item in opts.supplier"
|
||||
:key="item.value"
|
||||
:label="item.label"
|
||||
:value="item.value"
|
||||
:key="item.id"
|
||||
:label="item.supplierName"
|
||||
:value="item.id"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
@@ -135,7 +135,7 @@ import { allSchemas } from './index.data.js'
|
||||
import DialogAdd from './Comp/DialogAdd.vue'
|
||||
import DialogAudit from './Comp/DialogAudit.vue'
|
||||
import * as PurchaseApi from '@/api/mall/purchase'
|
||||
import { getDictOptions } from '@/utils/dict'
|
||||
import { getSupplierSimpleList } from '@/api/school/setting/supplier'
|
||||
|
||||
const creatPurchase = ref()
|
||||
const auditPurchase = ref()
|
||||
@@ -164,7 +164,9 @@ function getOptions() {
|
||||
ProductApi.getSimpleProductList().then((data) => {
|
||||
opts.value.product = data
|
||||
})
|
||||
opts.value.supplier = getDictOptions('erp_supplier')
|
||||
getSupplierSimpleList().then((data) => {
|
||||
opts.value.supplier = data
|
||||
})
|
||||
}
|
||||
|
||||
function changeProd(val) {
|
||||
|
||||
@@ -22,16 +22,16 @@
|
||||
<el-tab-pane :label="t('profile.info.resetPwd')" name="resetPwd">
|
||||
<ResetPwd />
|
||||
</el-tab-pane>
|
||||
<el-tab-pane :label="t('profile.info.userSocial')" name="userSocial">
|
||||
<!-- <el-tab-pane :label="t('profile.info.userSocial')" name="userSocial">
|
||||
<UserSocial />
|
||||
</el-tab-pane>
|
||||
</el-tab-pane> -->
|
||||
</el-tabs>
|
||||
</div>
|
||||
</el-card>
|
||||
</div>
|
||||
</template>
|
||||
<script setup lang="ts" name="Profile">
|
||||
import { BasicInfo, ProfileUser, ResetPwd, UserSocial } from './components/'
|
||||
import { BasicInfo, ProfileUser, ResetPwd } from './components/'
|
||||
const { t } = useI18n()
|
||||
|
||||
const activeName = ref('basicInfo')
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
-->
|
||||
<template>
|
||||
<div class="container">
|
||||
<ContentWrap style="max-width: 1000px; margin: 0 auto">
|
||||
<ContentWrap style="max-width: 1000px; margin: 0 auto; overflow-x: auto">
|
||||
<div class="text-center">
|
||||
<div class="mb-10px" style="font-size: 24px; letter-spacing: 2px">
|
||||
{{ info.title }}
|
||||
@@ -82,6 +82,8 @@ onMounted(() => {
|
||||
text-align: left;
|
||||
border-width: 1px;
|
||||
box-sizing: border-box;
|
||||
min-width: 44px;
|
||||
word-break: break-all;
|
||||
}
|
||||
:deep(.el-card__body) {
|
||||
padding: 20px 10px;
|
||||
|
||||
@@ -26,14 +26,14 @@ const { t } = useI18n()
|
||||
// 表单校验
|
||||
const rules = reactive<FormRules>({
|
||||
nickname: [{ required: true, message: t('profile.rules.nickname'), trigger: 'blur' }],
|
||||
email: [
|
||||
{ required: true, message: t('profile.rules.mail'), trigger: 'blur' },
|
||||
{
|
||||
type: 'email',
|
||||
message: t('profile.rules.truemail'),
|
||||
trigger: ['blur', 'change']
|
||||
}
|
||||
],
|
||||
// email: [
|
||||
// { required: true, message: t('profile.rules.mail'), trigger: 'blur' },
|
||||
// {
|
||||
// type: 'email',
|
||||
// message: t('profile.rules.truemail'),
|
||||
// trigger: ['blur', 'change']
|
||||
// }
|
||||
// ],
|
||||
mobile: [
|
||||
{ required: true, message: t('profile.rules.phone'), trigger: 'blur' },
|
||||
{
|
||||
@@ -59,6 +59,11 @@ const schema = reactive<FormSchema[]>([
|
||||
label: t('profile.user.email'),
|
||||
component: 'Input'
|
||||
},
|
||||
{
|
||||
field: 'wxAlias',
|
||||
label: '微信号',
|
||||
component: 'Input'
|
||||
},
|
||||
{
|
||||
field: 'sex',
|
||||
label: t('profile.user.sex'),
|
||||
|
||||
@@ -61,8 +61,8 @@ const bindSocial = () => {
|
||||
})
|
||||
}
|
||||
const bind = (row) => {
|
||||
message.info('暂未开放,敬请期待')
|
||||
return
|
||||
// message.info('暂未开放,敬请期待')
|
||||
// return
|
||||
const redirectUri = location.origin + '/user/profile?type=' + row.type
|
||||
// 进行跳转
|
||||
socialAuthRedirect(row.type, encodeURIComponent(redirectUri)).then((res) => {
|
||||
|
||||
193
src/views/XjApplet/Resell/index.vue
Normal file
193
src/views/XjApplet/Resell/index.vue
Normal file
@@ -0,0 +1,193 @@
|
||||
<template>
|
||||
<div>
|
||||
<el-form :model="searchForm" label-width="0" inline @submit.prevent>
|
||||
<el-form-item>
|
||||
<el-input v-model="searchForm.phone" placeholder="请输入手机号" />
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-input v-model="searchForm.name" placeholder="请输入姓名" />
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button @click="searchList" v-hasPermi="['xj-applet:resell:search']">查询</el-button>
|
||||
<el-button type="primary" @click="handleAdd" v-hasPermi="['xj-applet:resell:add']"
|
||||
>新增</el-button
|
||||
>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<el-table v-loading="loading" :data="tableData" max-height="calc(100vh - 200px)">
|
||||
<el-table-column type="index" width="50" />
|
||||
<el-table-column prop="phone" label="手机号" />
|
||||
<el-table-column prop="name" label="姓名" />
|
||||
<el-table-column prop="mark" label="标记" />
|
||||
<el-table-column prop="memberName" label="赠送会员" />
|
||||
<el-table-column prop="discountDiscription" label="赠送折扣" />
|
||||
<el-table-column label="小程序码" align="center" width="120px">
|
||||
<template #default="{ row }">
|
||||
<img :src="row.appletUrl" style="width: 80px; height: 80px" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作" width="120px">
|
||||
<template #default="{ row }">
|
||||
<el-button
|
||||
type="primary"
|
||||
link
|
||||
@click="handleEdit(row)"
|
||||
v-hasPermi="['xj-applet:resell:update']"
|
||||
>修改</el-button
|
||||
>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<pagination
|
||||
v-show="total > 0"
|
||||
:total="total"
|
||||
v-model:page="searchForm.pageNo"
|
||||
v-model:limit="searchForm.pageSize"
|
||||
@pagination="getList"
|
||||
/>
|
||||
|
||||
<el-dialog title="分销配置" v-model="showDialog" width="500px">
|
||||
<el-form :model="form" ref="formRef" :rules="rules" label-width="80px">
|
||||
<el-form-item label="手机号" prop="phone">
|
||||
<el-input v-model="form.phone" maxlength="11" />
|
||||
</el-form-item>
|
||||
<el-form-item label="姓名" prop="name">
|
||||
<el-input v-model="form.name" />
|
||||
</el-form-item>
|
||||
<el-form-item label="标记" prop="mark">
|
||||
<el-input v-model="form.mark" />
|
||||
</el-form-item>
|
||||
<el-form-item label="赠送会员" prop="memberId">
|
||||
<el-select v-model="form.memberId" clearable filterable style="width: 100%">
|
||||
<el-option
|
||||
v-for="item in vipOptions"
|
||||
:key="item.memberId"
|
||||
:label="item.memberName"
|
||||
:value="item.memberId"
|
||||
>
|
||||
<span style="float: left">{{ item.memberName }}</span>
|
||||
<span style="float: right; color: #aaa">{{ item.carName }}</span>
|
||||
</el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="赠送折扣" prop="discountId">
|
||||
<el-select v-model="form.discountId" clearable filterable style="width: 100%">
|
||||
<el-option
|
||||
v-for="item in discountOptions"
|
||||
:key="item.id"
|
||||
:label="item.description"
|
||||
:value="item.id"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
|
||||
<template #footer>
|
||||
<span>
|
||||
<el-button @click="showDialog = false">取消</el-button>
|
||||
<el-button type="primary" @click="handleSave">确认</el-button>
|
||||
</span>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup name="Resell">
|
||||
import { getResellList, addResell, updateResell } from '@/api/xjapplet/resell'
|
||||
import { getVipDiscountOptions } from '@/api/xjapplet/discount'
|
||||
import { getVipTypeOptions } from '@/api/xjapplet/vip'
|
||||
|
||||
const message = useMessage()
|
||||
|
||||
const searchForm = ref({
|
||||
pageNo: 1,
|
||||
pageSize: 50,
|
||||
phone: '',
|
||||
name: ''
|
||||
})
|
||||
|
||||
const tableData = ref([])
|
||||
const total = ref(0)
|
||||
|
||||
const loading = ref(false)
|
||||
const showDialog = ref(false)
|
||||
|
||||
const form = ref({
|
||||
phone: '',
|
||||
name: '',
|
||||
mark: '',
|
||||
discountId: undefined,
|
||||
memberId: undefined
|
||||
})
|
||||
|
||||
const rules = {
|
||||
phone: [{ required: true, message: '请输入手机号', trigger: 'blur' }],
|
||||
name: [{ required: true, message: '请输入姓名', trigger: 'blur' }]
|
||||
}
|
||||
|
||||
const vipOptions = ref([])
|
||||
const discountOptions = ref([])
|
||||
|
||||
onMounted(() => {
|
||||
searchList()
|
||||
getVipDiscountOptions().then((response) => {
|
||||
discountOptions.value = response
|
||||
})
|
||||
getVipTypeOptions({ carTypeId: searchForm.value.carTypeId }).then((response) => {
|
||||
vipOptions.value = response
|
||||
})
|
||||
})
|
||||
|
||||
function searchList() {
|
||||
searchForm.value.pageNo = 1
|
||||
getList()
|
||||
}
|
||||
|
||||
function getList() {
|
||||
loading.value = true
|
||||
getResellList(searchForm.value).then((response) => {
|
||||
tableData.value = response.list
|
||||
total.value = response.total
|
||||
loading.value = false
|
||||
})
|
||||
}
|
||||
|
||||
function handleAdd() {
|
||||
showDialog.value = true
|
||||
form.value = {
|
||||
phone: '',
|
||||
name: '',
|
||||
mark: '',
|
||||
discountId: undefined,
|
||||
memberId: undefined
|
||||
}
|
||||
}
|
||||
function handleEdit(row) {
|
||||
form.value = { ...row }
|
||||
showDialog.value = true
|
||||
}
|
||||
|
||||
const formRef = ref(null)
|
||||
async function handleSave() {
|
||||
if (!formRef.value) return
|
||||
const valid = await formRef.value.validate()
|
||||
if (!valid) return
|
||||
|
||||
// 调用接口
|
||||
if (form.value.distributionId) {
|
||||
updateResell(form.value).then(() => {
|
||||
message.success('修改成功')
|
||||
showDialog.value = false
|
||||
searchList()
|
||||
})
|
||||
} else {
|
||||
addResell(form.value).then(() => {
|
||||
message.success('新增成功')
|
||||
showDialog.value = false
|
||||
searchList()
|
||||
})
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped></style>
|
||||
144
src/views/XjApplet/Vip/components/UserDiscount.vue
Normal file
144
src/views/XjApplet/Vip/components/UserDiscount.vue
Normal file
@@ -0,0 +1,144 @@
|
||||
<template>
|
||||
<div>
|
||||
<el-form :model="searchForm" inline @submit.prevent>
|
||||
<el-form-item>
|
||||
<el-input v-model="searchForm.phone" placeholder="学员手机号" />
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button @click="searchList" v-hasPermi="['xj-applet:vip:user-discount:search']"
|
||||
>查询</el-button
|
||||
>
|
||||
<el-button
|
||||
type="primary"
|
||||
@click="addVipUser"
|
||||
v-hasPermi="['xj-applet:vip:user-discount:send']"
|
||||
>赠送折扣</el-button
|
||||
>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<el-table v-loading="loading" :data="tableList">
|
||||
<el-table-column type="index" width="55" align="center" />
|
||||
<el-table-column label="手机号" align="left" prop="phone" width="140" />
|
||||
<el-table-column label="折扣描述" align="left" prop="description" min-width="140" />
|
||||
<el-table-column label="折后价格" align="center" prop="discount" width="100" />
|
||||
<el-table-column
|
||||
label="截止时间"
|
||||
align="left"
|
||||
prop="endTime"
|
||||
:formatter="dateFormatter"
|
||||
width="150"
|
||||
/>
|
||||
<el-table-column label="操作人" align="left" prop="operUser" width="100" />
|
||||
<el-table-column label="操作时间" align="left" prop="operTime" width="150" />
|
||||
</el-table>
|
||||
<pagination
|
||||
v-show="total > 0"
|
||||
:total="total"
|
||||
v-model:page="searchForm.pageNo"
|
||||
v-model:limit="searchForm.pageSize"
|
||||
@pagination="getList"
|
||||
/>
|
||||
|
||||
<el-dialog title="赠送折扣" v-model="showDialog" width="500px" :close-on-click-modal="false">
|
||||
<el-form :model="form" ref="formRef" :rules="rules" label-width="80px">
|
||||
<el-form-item label="手机号" prop="phone">
|
||||
<el-input v-model="form.phone" maxlength="11" />
|
||||
</el-form-item>
|
||||
<el-form-item label="折扣描述" prop="discountId">
|
||||
<el-select v-model="form.discountId" clearable filterable style="width: 100%">
|
||||
<el-option
|
||||
v-for="item in discountOptions"
|
||||
:key="item.id"
|
||||
:label="item.description"
|
||||
:value="item.id"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
|
||||
<template #footer>
|
||||
<span>
|
||||
<el-button @click="showDialog = false">取消</el-button>
|
||||
<el-button type="primary" @click="sureAdd">确定</el-button>
|
||||
</span>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup name="UserDiscount">
|
||||
import {
|
||||
getUserDiscountList,
|
||||
giveUserDiscount,
|
||||
getVipDiscountOptions
|
||||
} from '@/api/xjapplet/discount'
|
||||
import { dateFormatter } from '@/utils/formatTime'
|
||||
|
||||
const message = useMessage()
|
||||
|
||||
const searchForm = ref({
|
||||
phone: '',
|
||||
pageNo: 1,
|
||||
pageSize: 50
|
||||
})
|
||||
|
||||
const loading = ref(false)
|
||||
const tableList = ref([])
|
||||
const total = ref(0)
|
||||
|
||||
const discountOptions = ref([])
|
||||
|
||||
onMounted(() => {
|
||||
searchList()
|
||||
getVipDiscountOptions().then((response) => {
|
||||
discountOptions.value = response
|
||||
})
|
||||
})
|
||||
function searchList() {
|
||||
searchForm.value.pageNo = 1
|
||||
getList()
|
||||
}
|
||||
|
||||
function getList() {
|
||||
loading.value = true
|
||||
getUserDiscountList(searchForm.value).then((response) => {
|
||||
tableList.value = response.list
|
||||
total.value = response.total
|
||||
loading.value = false
|
||||
})
|
||||
}
|
||||
|
||||
const showDialog = ref(false)
|
||||
const form = ref({
|
||||
phone: '',
|
||||
discountId: ''
|
||||
})
|
||||
const rules = ref({
|
||||
phone: [{ required: true, message: '请输入用户手机号', trigger: 'blur' }],
|
||||
discountId: [{ required: true, message: '请选择会员类型', trigger: 'change' }]
|
||||
})
|
||||
|
||||
function addVipUser() {
|
||||
showDialog.value = true
|
||||
}
|
||||
|
||||
const formRef = ref(null)
|
||||
async function sureAdd() {
|
||||
if (!formRef.value) return
|
||||
const valid = await formRef.value.validate()
|
||||
if (!valid) return
|
||||
|
||||
// 调用接口
|
||||
giveUserDiscount(form.value).then((response) => {
|
||||
if (response) {
|
||||
message.success('赠送成功')
|
||||
showDialog.value = false
|
||||
searchList()
|
||||
} else {
|
||||
message.error('赠送失败')
|
||||
}
|
||||
})
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped></style>
|
||||
286
src/views/XjApplet/Vip/components/VipDiscount.vue
Normal file
286
src/views/XjApplet/Vip/components/VipDiscount.vue
Normal file
@@ -0,0 +1,286 @@
|
||||
<template>
|
||||
<div>
|
||||
<el-row>
|
||||
<el-button @click="searchList" v-hasPermi="['xj-applet:vip:vip-discount:search']"
|
||||
>查询</el-button
|
||||
>
|
||||
<el-button type="primary" @click="addDiscount" v-hasPermi="['xj-applet:vip:vip-discount:add']"
|
||||
>新增折扣</el-button
|
||||
>
|
||||
</el-row>
|
||||
|
||||
<el-table v-loading="loading" :data="tableList">
|
||||
<el-table-column type="index" width="55" align="center" />
|
||||
<el-table-column label="折扣描述" align="center" prop="description" min-width="140" />
|
||||
<el-table-column label="折扣价" align="center" prop="discount" />
|
||||
<el-table-column label="有效期" align="center" prop="duration" />
|
||||
<el-table-column label="单位" align="center">
|
||||
<template #default="{ row }">
|
||||
<el-tag v-if="row.unit == 1">天</el-tag>
|
||||
<el-tag v-else type="danger">年</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作" align="center">
|
||||
<template #default="{ row }">
|
||||
<el-button
|
||||
type="primary"
|
||||
link
|
||||
@click="editDiscount(row)"
|
||||
v-hasPermi="['xj-applet:vip:vip-discount:update']"
|
||||
>修改</el-button
|
||||
>
|
||||
<el-button
|
||||
type="primary"
|
||||
link
|
||||
@click="deleteDiscount(row)"
|
||||
v-hasPermi="['xj-applet:vip:vip-discount:delete']"
|
||||
>删除</el-button
|
||||
>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<pagination
|
||||
v-show="total > 0"
|
||||
:total="total"
|
||||
v-model:page="searchForm.pageNo"
|
||||
v-model:limit="searchForm.pageSize"
|
||||
@pagination="getList"
|
||||
/>
|
||||
|
||||
<el-dialog title="折扣详情" v-model="showDialog" width="500px" :close-on-click-modal="false">
|
||||
<el-form :model="form" ref="formRef" :rules="rules" label-width="80px">
|
||||
<el-form-item label="折扣类型" prop="discountType">
|
||||
<el-select v-model="form.discountType" style="width: 100%" :disabled="!!form.id">
|
||||
<el-option
|
||||
v-for="item in discountTypeOptions"
|
||||
:key="item.value"
|
||||
:label="item.label"
|
||||
:value="item.value"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="折扣描述">
|
||||
{{ discountDesc }}
|
||||
</el-form-item>
|
||||
<el-form-item v-if="form.discountType == 1" label="科目" prop="subject">
|
||||
<el-select v-model="form.subject" style="width: 100%" :disabled="!!form.id">
|
||||
<el-option
|
||||
v-for="item in subjectOptions"
|
||||
:key="item.value"
|
||||
:label="item.label"
|
||||
:value="item.value"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="变量1" prop="param1">
|
||||
<el-select v-model="form.param1" style="width: 100%" clearable :disabled="!!form.id">
|
||||
<el-option
|
||||
v-for="item in vipTypeOptions"
|
||||
:key="item.memberId"
|
||||
:label="item.memberName"
|
||||
:value="item.memberId"
|
||||
>
|
||||
<span style="float: left">{{ item.memberName }}</span>
|
||||
<span style="float: right; color: #aaa">{{ item.carName }}</span>
|
||||
</el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="变量2" prop="param2">
|
||||
<el-select v-model="form.param2" style="width: 100%" :disabled="!!form.id">
|
||||
<el-option
|
||||
v-for="item in vipTypeOptions"
|
||||
:key="item.memberId"
|
||||
:label="item.memberName"
|
||||
:value="item.memberId"
|
||||
>
|
||||
<span style="float: left">{{ item.memberName }}</span>
|
||||
<span style="float: right; color: #aaa">{{ item.carName }}</span>
|
||||
</el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="折扣价" prop="discount">
|
||||
<el-input v-model="form.discount" type="number" />
|
||||
</el-form-item>
|
||||
<el-form-item label="有效期" prop="duration">
|
||||
<el-input v-model="form.duration" type="number" />
|
||||
</el-form-item>
|
||||
<el-form-item label="单位" prop="unit">
|
||||
<el-radio-group v-model="form.unit">
|
||||
<el-radio v-for="(item, index) in unitOptions" :key="index" :label="item.value">{{
|
||||
item.label
|
||||
}}</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
|
||||
<template #footer>
|
||||
<span>
|
||||
<el-button @click="showDialog = false">取消</el-button>
|
||||
<el-button type="primary" @click="sureAdd">确定</el-button>
|
||||
</span>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup name="VipDiscount">
|
||||
import {
|
||||
getVipDiscountList,
|
||||
addVipDiscount,
|
||||
updateVipDiscount,
|
||||
deleteVipDiscount
|
||||
} from '@/api/xjapplet/discount'
|
||||
|
||||
import { getVipTypeOptions } from '@/api/xjapplet/vip'
|
||||
|
||||
const message = useMessage()
|
||||
|
||||
const searchForm = ref({
|
||||
pageNo: 1,
|
||||
pageSize: 50
|
||||
})
|
||||
|
||||
onMounted(() => {
|
||||
searchList()
|
||||
|
||||
getVipTypeOptions().then((response) => {
|
||||
vipTypeOptions.value = response
|
||||
})
|
||||
})
|
||||
|
||||
const loading = ref(false)
|
||||
const tableList = ref([])
|
||||
const total = ref(0)
|
||||
|
||||
const discountTypeOptions = [
|
||||
{
|
||||
label: '【$科目】模考首次通过后,在拥有【$会员名1】的情况下,购买【$会员名2】,享折扣价',
|
||||
value: '1'
|
||||
},
|
||||
{ label: '之前拥有过【$会员名1】,购买【$会员名2】,享折扣价', value: '2' }
|
||||
]
|
||||
|
||||
const subjectOptions = [
|
||||
{ label: '科一', value: '1' },
|
||||
{ label: '科四', value: '4' }
|
||||
]
|
||||
|
||||
const unitOptions = [
|
||||
{ label: '天', value: '1' },
|
||||
{ label: '年', value: '3' }
|
||||
]
|
||||
|
||||
const discountDesc = computed(() => {
|
||||
const vipTypeObj = {}
|
||||
vipTypeOptions.value.forEach((item) => {
|
||||
vipTypeObj[item.memberId] = `${item.memberName}(${item.carName})`
|
||||
})
|
||||
const { discountType, subject, param1, param2, discount, duration, unit } = form.value
|
||||
let baseStr = discountTypeOptions.find((item) => item.value == discountType).label
|
||||
const vip1 = param1 ? vipTypeObj[param1] : '无需会员'
|
||||
const vip2 = param2 ? vipTypeObj[param2] : '会员名2'
|
||||
return baseStr
|
||||
.replace('$科目', subjectOptions.find((item) => item.value == subject).label)
|
||||
.replace('$会员名1', vip1)
|
||||
.replace('$会员名2', vip2)
|
||||
.concat(
|
||||
`${discount || ''},有效期${duration || 0}${
|
||||
unitOptions.find((item) => item.value == unit).label
|
||||
}`
|
||||
)
|
||||
})
|
||||
|
||||
onMounted(() => {
|
||||
searchList()
|
||||
})
|
||||
|
||||
const vipTypeOptions = ref([])
|
||||
function searchList() {
|
||||
searchForm.value.pageNo = 1
|
||||
getList()
|
||||
}
|
||||
|
||||
function getList() {
|
||||
loading.value = true
|
||||
getVipDiscountList(searchForm.value).then((response) => {
|
||||
tableList.value = response.list
|
||||
total.value = response.total
|
||||
loading.value = false
|
||||
})
|
||||
}
|
||||
|
||||
const showDialog = ref(false)
|
||||
const form = ref({
|
||||
discountType: '1',
|
||||
subject: '1',
|
||||
param1: undefined,
|
||||
param2: undefined,
|
||||
discount: undefined,
|
||||
duration: undefined,
|
||||
unit: '1',
|
||||
description: undefined
|
||||
})
|
||||
const rules = ref({
|
||||
param2: [{ required: true, message: '请选择变量2', trigger: 'change' }],
|
||||
discount: [{ required: true, message: '请输入折扣价', trigger: 'blur' }],
|
||||
duration: [{ required: true, message: '请输入有效期', trigger: 'blur' }]
|
||||
})
|
||||
|
||||
function addDiscount() {
|
||||
showDialog.value = true
|
||||
form.value = {
|
||||
discountType: '1',
|
||||
subject: '1',
|
||||
param1: undefined,
|
||||
param2: undefined,
|
||||
discount: undefined,
|
||||
duration: undefined,
|
||||
unit: '1',
|
||||
description: undefined
|
||||
}
|
||||
}
|
||||
|
||||
function editDiscount(row) {
|
||||
form.value = {
|
||||
...row,
|
||||
param1: row.param1 ? Number(row.param1) : undefined,
|
||||
param2: row.param2 ? Number(row.param2) : undefined
|
||||
}
|
||||
showDialog.value = true
|
||||
}
|
||||
|
||||
function deleteDiscount(row) {
|
||||
message.confirm('确定删除该折扣?').then(() => {
|
||||
deleteVipDiscount(row.id).then(() => {
|
||||
message.success('删除成功')
|
||||
searchList()
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
const formRef = ref(null)
|
||||
async function sureAdd() {
|
||||
if (!formRef.value) return
|
||||
const valid = await formRef.value.validate()
|
||||
if (!valid) return
|
||||
|
||||
form.value.description = discountDesc.value
|
||||
|
||||
// 调用接口
|
||||
if (form.value.id) {
|
||||
updateVipDiscount(form.value).then(() => {
|
||||
showDialog.value = false
|
||||
message.success('修改成功')
|
||||
searchList()
|
||||
})
|
||||
} else {
|
||||
addVipDiscount(form.value).then(() => {
|
||||
showDialog.value = false
|
||||
message.success('新增成功')
|
||||
searchList()
|
||||
})
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped></style>
|
||||
217
src/views/XjApplet/Vip/components/VipType.vue
Normal file
217
src/views/XjApplet/Vip/components/VipType.vue
Normal file
@@ -0,0 +1,217 @@
|
||||
<template>
|
||||
<div>
|
||||
<el-form :model="searchForm" inline @submit.prevent>
|
||||
<el-form-item>
|
||||
<el-input v-model="searchForm.memberName" placeholder="会员名称" />
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-radio-group v-model="searchForm.carTypeId" @change="searchList">
|
||||
<el-radio label="1001">小车</el-radio>
|
||||
<el-radio label="1002">摩托车</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button @click="searchList" v-hasPermi="['xj-applet:vip:vip-type:search']"
|
||||
>查询</el-button
|
||||
>
|
||||
<el-button type="primary" @click="addVip" v-hasPermi="['xj-applet:vip:vip-type:add']"
|
||||
>新增会员类型</el-button
|
||||
>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<el-table v-loading="loading" :data="tableList" height="calc(100vh - 260px)">
|
||||
<el-table-column type="index" width="55" align="center" />
|
||||
<el-table-column label="会员名" align="center" prop="memberName" min-width="140" />
|
||||
<el-table-column label="车型" align="center" prop="carName" />
|
||||
<el-table-column label="科目" align="center" prop="subjects" />
|
||||
<el-table-column label="原价" align="center" prop="price" />
|
||||
<el-table-column label="折扣价" align="center" prop="discount" />
|
||||
<el-table-column label="有效期" align="center" prop="duration" />
|
||||
<el-table-column label="单位" align="center">
|
||||
<template #default="{ row }">
|
||||
<el-tag v-if="row.unit == 1">天</el-tag>
|
||||
<el-tag v-else type="danger">年</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="使用方式" align="center" prop="useTypeName" />
|
||||
<el-table-column label="操作" width="160">
|
||||
<template #default="{ row }">
|
||||
<el-button
|
||||
type="primary"
|
||||
link
|
||||
@click="editVip(row)"
|
||||
v-hasPermi="['xj-applet:vip:vip-type:update']"
|
||||
>修改</el-button
|
||||
>
|
||||
<el-button
|
||||
type="primary"
|
||||
link
|
||||
@click="handleDelete(row.memberId)"
|
||||
v-hasPermi="['xj-applet:vip:vip-type:delete']"
|
||||
>删除</el-button
|
||||
>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<pagination
|
||||
v-show="total > 0"
|
||||
:total="total"
|
||||
v-model:page="searchForm.pageNo"
|
||||
v-model:limit="searchForm.pageSize"
|
||||
@pagination="getList"
|
||||
/>
|
||||
|
||||
<el-dialog title="会员详情" v-model="showDialog" width="500px" :close-on-click-modal="false">
|
||||
<el-form :model="form" ref="formRef" :rules="rules" label-width="80px">
|
||||
<el-form-item label="会员名称" prop="memberName">
|
||||
<el-input v-model="form.memberName" />
|
||||
</el-form-item>
|
||||
<el-form-item label="车型" prop="carTypeId">
|
||||
<el-select v-model="form.carTypeId" style="width: 100%">
|
||||
<el-option label="小车" value="1001" />
|
||||
<el-option label="摩托车" value="1002" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="科目" prop="subjects">
|
||||
<el-select v-model="form.subjects" placeholder="多选" multiple style="width: 100%">
|
||||
<el-option label="科一" value="4" />
|
||||
<el-option label="科四" value="1" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="原价" prop="price">
|
||||
<el-input v-model="form.price" type="number" />
|
||||
</el-form-item>
|
||||
<el-form-item label="折扣价" prop="discount">
|
||||
<el-input v-model="form.discount" type="number" />
|
||||
</el-form-item>
|
||||
<el-form-item label="有效期" prop="duration">
|
||||
<el-input v-model="form.duration" type="number" />
|
||||
</el-form-item>
|
||||
<el-form-item label="单位" prop="unit">
|
||||
<el-radio-group v-model="form.unit">
|
||||
<el-radio :label="1">天</el-radio>
|
||||
<el-radio :label="3">年</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
<el-form-item label="使用方式" prop="useTypes">
|
||||
<el-checkbox-group v-model="form.useTypes">
|
||||
<el-checkbox :label="1"> 用户购买 </el-checkbox>
|
||||
<el-checkbox :label="2"> 客服赠送 </el-checkbox>
|
||||
</el-checkbox-group>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
|
||||
<template #footer>
|
||||
<span>
|
||||
<el-button @click="showDialog = false">取消</el-button>
|
||||
<el-button type="primary" @click="sureAdd">确定</el-button>
|
||||
</span>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup name="VipType">
|
||||
import { getVipTypeList, addVipType, updateVipType, deleteVipType } from '@/api/xjapplet/vip'
|
||||
const message = useMessage()
|
||||
const searchForm = ref({
|
||||
memberName: undefined,
|
||||
carTypeId: '1001',
|
||||
pageNo: 1,
|
||||
pageSize: 50
|
||||
})
|
||||
|
||||
const loading = ref(false)
|
||||
const tableList = ref([])
|
||||
const total = ref(0)
|
||||
|
||||
onMounted(() => {
|
||||
searchList()
|
||||
})
|
||||
function searchList() {
|
||||
searchForm.value.pageNo = 1
|
||||
getList()
|
||||
}
|
||||
|
||||
function getList() {
|
||||
loading.value = true
|
||||
getVipTypeList(searchForm.value).then((response) => {
|
||||
tableList.value = response.list
|
||||
total.value = response.total
|
||||
loading.value = false
|
||||
})
|
||||
}
|
||||
|
||||
const showDialog = ref(false)
|
||||
const form = ref({
|
||||
memberName: '',
|
||||
carTypeId: undefined,
|
||||
subjects: [],
|
||||
price: '',
|
||||
discount: '',
|
||||
duration: '',
|
||||
unit: 1,
|
||||
useTypes: [1]
|
||||
})
|
||||
const rules = ref({
|
||||
memberName: [{ required: true, message: '请输入会员名称', trigger: 'blur' }],
|
||||
carTypeId: [{ required: true, message: '请输入车型', trigger: 'change' }],
|
||||
subjects: [{ required: true, message: '请输入科目', trigger: 'blur' }],
|
||||
price: [{ required: true, message: '请输入价格', trigger: 'blur' }],
|
||||
discount: [{ required: true, message: '请输入折扣价', trigger: 'blur' }],
|
||||
duration: [{ required: true, message: '请输入有效期', trigger: 'blur' }]
|
||||
})
|
||||
|
||||
function addVip() {
|
||||
showDialog.value = true
|
||||
|
||||
form.value.carTypeId = {
|
||||
memberName: '',
|
||||
carTypeId: searchForm.value.carTypeId,
|
||||
subjects: [],
|
||||
price: '',
|
||||
discount: '',
|
||||
duration: '',
|
||||
unit: 1,
|
||||
useTypes: [1]
|
||||
}
|
||||
}
|
||||
|
||||
function editVip(row) {
|
||||
form.value = { ...row, subjects: row.subjects.split(','), unit: Number(row.unit) }
|
||||
showDialog.value = true
|
||||
}
|
||||
|
||||
const formRef = ref(null)
|
||||
async function sureAdd() {
|
||||
if (!formRef.value) return
|
||||
const valid = await formRef.value.validate()
|
||||
if (!valid) return
|
||||
|
||||
// 调用接口
|
||||
if (form.value.memberId) {
|
||||
updateVipType(form.value).then(() => {
|
||||
message.success('修改成功')
|
||||
showDialog.value = false
|
||||
searchList()
|
||||
})
|
||||
} else {
|
||||
addVipType(form.value).then(() => {
|
||||
message.success('添加成功')
|
||||
showDialog.value = false
|
||||
searchList()
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
function handleDelete(id) {
|
||||
message.confirm('是否确认删除该会员?').then(() => {
|
||||
deleteVipType(id).then(() => {
|
||||
message.success('删除成功')
|
||||
searchList()
|
||||
})
|
||||
})
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped></style>
|
||||
159
src/views/XjApplet/Vip/components/VipUser.vue
Normal file
159
src/views/XjApplet/Vip/components/VipUser.vue
Normal file
@@ -0,0 +1,159 @@
|
||||
<template>
|
||||
<div>
|
||||
<el-form :model="searchForm" inline @submit.prevent>
|
||||
<el-form-item>
|
||||
<el-input v-model="searchForm.phone" placeholder="学员手机号" />
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-radio-group v-model="searchForm.carTypeId" @change="changeCarType">
|
||||
<el-radio label="1001">小车</el-radio>
|
||||
<el-radio label="1002">摩托车</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
<!-- <el-form-item>
|
||||
<el-select v-model="searchForm.memberId" placeholder="选择会员类型" clearable filterable>
|
||||
<el-option
|
||||
v-for="item in vipOptions"
|
||||
:key="item.value"
|
||||
:label="item.label"
|
||||
:value="item.value"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item> -->
|
||||
<el-form-item>
|
||||
<el-button @click="searchList" v-hasPermi="['xj-applet:vip:vip-user:search']"
|
||||
>查询</el-button
|
||||
>
|
||||
<el-button type="primary" @click="addVipUser" v-hasPermi="['xj-applet:vip:vip-user:send']"
|
||||
>赠送会员</el-button
|
||||
>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<el-table v-loading="loading" :data="tableList" height="calc(100vh - 260px)">
|
||||
<el-table-column type="index" width="55" align="center" />
|
||||
<el-table-column label="手机号" align="left" prop="phone" min-width="140" />
|
||||
<el-table-column label="会员名" align="left" prop="memberName" min-width="140" />
|
||||
<el-table-column label="车型" align="left" min-width="100">
|
||||
<template #default="{ row }">
|
||||
{{ row.carTypeId == 1001 ? '小车' : '摩托车' }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="科目" align="left" prop="subjects" min-width="100" />
|
||||
<el-table-column label="来源" align="left" prop="useTypeName" min-width="100" />
|
||||
<el-table-column label="截止日期" align="left" prop="endDate" min-width="120" />
|
||||
<el-table-column label="操作人" align="left" prop="operUser" min-width="100" />
|
||||
<el-table-column label="操作时间" align="left" prop="createTime" min-width="120" />
|
||||
</el-table>
|
||||
<pagination
|
||||
v-show="total > 0"
|
||||
:total="total"
|
||||
v-model:page="searchForm.pageNo"
|
||||
v-model:limit="searchForm.pageSize"
|
||||
@pagination="getList"
|
||||
/>
|
||||
|
||||
<el-dialog title="赠送会员" v-model="showDialog" width="500px" :close-on-click-modal="false">
|
||||
<el-form :model="form" ref="formRef" :rules="rules" label-width="80px">
|
||||
<el-form-item label="手机号" prop="phone">
|
||||
<el-input v-model="form.phone" maxlength="11" />
|
||||
</el-form-item>
|
||||
<el-form-item label="会员类型" prop="memberId">
|
||||
<el-select v-model="form.memberId" clearable filterable style="width: 100%">
|
||||
<el-option
|
||||
v-for="item in vipOptions"
|
||||
:key="item.memberId"
|
||||
:label="item.memberName"
|
||||
:value="item.memberId"
|
||||
>
|
||||
<span style="float: left">{{ item.memberName }}</span>
|
||||
<span style="float: right; color: #aaa">{{ item.carName }}</span>
|
||||
</el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
|
||||
<template #footer>
|
||||
<span>
|
||||
<el-button @click="showDialog = false">取消</el-button>
|
||||
<el-button type="primary" @click="sureAdd">确定</el-button>
|
||||
</span>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup name="VipUser">
|
||||
import { getUserMemberList, giveUserMember, getVipTypeOptions } from '@/api/xjapplet/vip'
|
||||
const message = useMessage()
|
||||
const searchForm = ref({
|
||||
carTypeId: '1001',
|
||||
memberId: undefined,
|
||||
phone: undefined,
|
||||
pageNo: 1,
|
||||
pageSize: 50
|
||||
})
|
||||
|
||||
const loading = ref(false)
|
||||
const tableList = ref([])
|
||||
const total = ref(0)
|
||||
|
||||
const vipOptions = ref([])
|
||||
|
||||
onMounted(() => {
|
||||
changeCarType()
|
||||
})
|
||||
|
||||
function changeCarType() {
|
||||
getVipTypeOptions({ carTypeId: searchForm.value.carTypeId }).then((response) => {
|
||||
vipOptions.value = response
|
||||
})
|
||||
searchList()
|
||||
}
|
||||
|
||||
function searchList() {
|
||||
searchForm.value.pageNo = 1
|
||||
getList()
|
||||
}
|
||||
|
||||
function getList() {
|
||||
loading.value = true
|
||||
getUserMemberList(searchForm.value).then((response) => {
|
||||
tableList.value = response.list
|
||||
total.value = response.total
|
||||
loading.value = false
|
||||
})
|
||||
}
|
||||
|
||||
const showDialog = ref(false)
|
||||
const form = ref({
|
||||
phone: '',
|
||||
memberId: ''
|
||||
})
|
||||
const rules = ref({
|
||||
phone: [{ required: true, message: '请输入用户手机号', trigger: 'blur' }],
|
||||
memberId: [{ required: true, message: '请选择会员类型', trigger: 'change' }]
|
||||
})
|
||||
|
||||
function addVipUser() {
|
||||
showDialog.value = true
|
||||
}
|
||||
|
||||
const formRef = ref(null)
|
||||
async function sureAdd() {
|
||||
if (!formRef.value) return
|
||||
const valid = await formRef.value.validate()
|
||||
if (!valid) return
|
||||
|
||||
giveUserMember(form.value).then((response) => {
|
||||
if (response) {
|
||||
message.success('赠送成功')
|
||||
showDialog.value = false
|
||||
searchList()
|
||||
} else {
|
||||
message.error('赠送失败')
|
||||
}
|
||||
})
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped></style>
|
||||
30
src/views/XjApplet/Vip/index.vue
Normal file
30
src/views/XjApplet/Vip/index.vue
Normal file
@@ -0,0 +1,30 @@
|
||||
<template>
|
||||
<div>
|
||||
<el-tabs v-model="tabIndex">
|
||||
<el-tab-pane label="会员用户" :name="1" v-if="checkPermi(['xj-applet:vip:vip-user'])">
|
||||
<VipUser v-if="tabIndex == 1" />
|
||||
</el-tab-pane>
|
||||
<el-tab-pane label="会员类型" :name="2" v-if="checkPermi(['xj-applet:vip:vip-type'])">
|
||||
<VipType v-if="tabIndex == 2" />
|
||||
</el-tab-pane>
|
||||
<el-tab-pane label="会员折扣" :name="3" v-if="checkPermi(['xj-applet:vip:vip-discount'])">
|
||||
<VipDiscount v-if="tabIndex == 3" />
|
||||
</el-tab-pane>
|
||||
<el-tab-pane label="用户折扣" :name="4" v-if="checkPermi(['xj-applet:vip:user-discount'])">
|
||||
<UserDiscount v-if="tabIndex == 4" />
|
||||
</el-tab-pane>
|
||||
</el-tabs>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup name="Vip">
|
||||
import { checkPermi } from '@/utils/permission'
|
||||
import UserDiscount from './components/UserDiscount.vue'
|
||||
import VipDiscount from './components/VipDiscount.vue'
|
||||
import VipType from './components/VipType.vue'
|
||||
import VipUser from './components/VipUser.vue'
|
||||
|
||||
const tabIndex = ref(1)
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped></style>
|
||||
295
src/views/XjApplet/VipData/conponents/SecretData.vue
Normal file
295
src/views/XjApplet/VipData/conponents/SecretData.vue
Normal file
@@ -0,0 +1,295 @@
|
||||
<template>
|
||||
<div>
|
||||
<el-form inline label-width="68px" @submit.prevent>
|
||||
<el-row :gutter="20">
|
||||
<el-form-item label="车型" @change="searchMj">
|
||||
<el-radio-group v-model="queryParams.carTypeId">
|
||||
<el-radio label="1001">小车</el-radio>
|
||||
<el-radio label="1002">摩托车</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
<el-form-item label="科目">
|
||||
<el-radio-group v-model="queryParams.subject" @change="searchMj">
|
||||
<el-radio label="1">科一</el-radio>
|
||||
<el-radio label="4">科四</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
</el-row>
|
||||
</el-form>
|
||||
<div style="display: flex; padding: 10px; border: 1px solid #eee; height: calc(100vh - 80px)">
|
||||
<div style="width: 300px; border-right: 1px solid #eee">
|
||||
<div style="display: flex; justify-content: space-between; padding-right: 20px">
|
||||
<div style="font-size: 18px; font-weight: bold">密卷</div>
|
||||
<el-button
|
||||
type="primary"
|
||||
link
|
||||
@click="addMjItem"
|
||||
v-hasPermi="['xj-applet:vip-data:mj:add']"
|
||||
>新增</el-button
|
||||
>
|
||||
</div>
|
||||
<el-radio-group
|
||||
v-model="mjIndex"
|
||||
style="width: 100%; display: block; margin-top: 20px"
|
||||
@change="getQuestionList"
|
||||
>
|
||||
<el-radio v-for="(item, index) in mjList" :key="index" :label="index" class="flex-radio">
|
||||
<div style="flex: 1; width: 100px">{{ getMjTitle(index) }}</div>
|
||||
<div style="padding-right: 15px">
|
||||
<el-button
|
||||
type="primary"
|
||||
link
|
||||
@click="removeMj(item)"
|
||||
v-hasPermi="['xj-applet:vip-data:mj:remove']"
|
||||
>删除</el-button
|
||||
>
|
||||
<el-button
|
||||
type="primary"
|
||||
link
|
||||
@click="clearMjDetail(item)"
|
||||
v-hasPermi="['xj-applet:vip-data:mj:clear']"
|
||||
>清空</el-button
|
||||
>
|
||||
</div>
|
||||
</el-radio>
|
||||
</el-radio-group>
|
||||
</div>
|
||||
<div style="flex: 1; padding-left: 20px; padding-right: 20px">
|
||||
<div
|
||||
v-if="mjList.length > 0"
|
||||
style="display: flex; justify-content: space-between; align-items: center"
|
||||
>
|
||||
<div style="font-size: 20px; font-weight: bold">
|
||||
当前密卷:{{ getMjTitle(mjIndex) }}
|
||||
</div>
|
||||
<div style="margin-left: 10px; margin-right: 10px"> 题目数:{{ tableList.length }} </div>
|
||||
<el-button
|
||||
type="primary"
|
||||
@click="addMjDetail"
|
||||
v-hasPermi="['xj-applet:vip-data:mj:add-question']"
|
||||
>新增密卷试题</el-button
|
||||
>
|
||||
</div>
|
||||
<el-table
|
||||
v-loading="loading"
|
||||
:data="tableList"
|
||||
style="margin-top: 10px"
|
||||
max-height="calc(100vh - 280px)"
|
||||
>
|
||||
<el-table-column type="index" width="55" align="center" />
|
||||
<el-table-column label="题目" align="left" prop="question" min-width="140" />
|
||||
<el-table-column label="选项" align="left" min-width="140">
|
||||
<template #default="{ row }">
|
||||
<p v-if="row.chooseA">A:{{ row.chooseA }}</p>
|
||||
<p v-if="row.chooseB">B:{{ row.chooseB }}</p>
|
||||
<p v-if="row.chooseC">C:{{ row.chooseC }}</p>
|
||||
<p v-if="row.chooseD">D:{{ row.chooseD }}</p>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="答案" align="center" prop="trueAnswer" width="100" />
|
||||
<el-table-column label="科目" align="center" prop="subject" width="100">
|
||||
<template #default="{ row }">
|
||||
<span v-if="row.subject == '1'">科一</span>
|
||||
<span v-if="row.subject == '4'">科四</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="章节" align="center" prop="chapterName" min-width="100" />
|
||||
<el-table-column label="图片" align="center" width="100">
|
||||
<template #default="{ row }">
|
||||
<el-image
|
||||
v-if="row.imageUrl"
|
||||
:src="`https://ss-cloud.ahduima.com/xjxc/pic/${row.imageUrl}`"
|
||||
:preview-src-list="[`https://ss-cloud.ahduima.com/xjxc/pic/${row.imageUrl}`]"
|
||||
:lazy="true"
|
||||
style="width: 80px"
|
||||
/>
|
||||
</template>
|
||||
</el-table-column>
|
||||
|
||||
<el-table-column label="操作" align="center" width="100px">
|
||||
<template #default="{ row }">
|
||||
<el-button
|
||||
type="primary"
|
||||
link
|
||||
@click="takeoutMj(row)"
|
||||
v-hasPermi="['xj-applet:vip-data:mj:remove-question']"
|
||||
>移出密卷</el-button
|
||||
>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<el-dialog v-model="showDialog" title="新增试题" width="80%">
|
||||
<div style="display: flex; align-items: center; margin-bottom: 20px">
|
||||
<el-input v-model="quesName" placeholder="请输入题目" clearable @keyup.enter="searchQues" />
|
||||
<el-button style="margin-left: 20px" type="primary" @click="searchQues">搜索</el-button>
|
||||
</div>
|
||||
<el-table :data="quesLit" highlight-current-row height="calc(100vh - 260px)">
|
||||
<el-table-column label="题目" align="left" prop="question" min-width="140" />
|
||||
<el-table-column label="选项" align="left" min-width="140">
|
||||
<template #default="{ row }">
|
||||
<p v-if="row.chooseA">A:{{ row.chooseA }}</p>
|
||||
<p v-if="row.chooseB">B:{{ row.chooseB }}</p>
|
||||
<p v-if="row.chooseC">C:{{ row.chooseC }}</p>
|
||||
<p v-if="row.chooseD">D:{{ row.chooseD }}</p>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="答案" align="center" prop="trueAnswer" width="100" />
|
||||
<el-table-column label="章节" align="center" prop="chapterName" min-width="100" />
|
||||
<el-table-column label="图片" align="center" width="100">
|
||||
<template #default="{ row }">
|
||||
<el-image
|
||||
v-if="row.imageUrl"
|
||||
:src="`https://ss-cloud.ahduima.com/xjxc/pic/${row.imageUrl}`"
|
||||
:preview-src-list="[`https://ss-cloud.ahduima.com/xjxc/pic/${row.imageUrl}`]"
|
||||
:lazy="true"
|
||||
style="width: 80px"
|
||||
/>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作" width="100px">
|
||||
<template #default="{ row }">
|
||||
<el-button type="primary" link @click="sureAddQues(row)">加入密卷</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup name="SimpleData">
|
||||
import {
|
||||
getMjList,
|
||||
addMj,
|
||||
delMj,
|
||||
clearMj,
|
||||
getMjQuestionList,
|
||||
addMjQuestion,
|
||||
delMjQuestion
|
||||
} from '@/api/xjapplet/vipdatabase.js'
|
||||
import { searchQuestion } from '@/api/xjapplet/xjdatabase.js'
|
||||
const message = useMessage() // 消息弹窗
|
||||
|
||||
const queryParams = ref({
|
||||
carTypeId: '1001',
|
||||
subject: '1'
|
||||
})
|
||||
const mjIndex = ref(0)
|
||||
const mjList = ref([])
|
||||
const loading = ref(false)
|
||||
const tableList = ref([])
|
||||
const showDialog = ref(false)
|
||||
const quesName = ref('')
|
||||
const quesLit = ref([])
|
||||
|
||||
onMounted(() => {
|
||||
searchMj()
|
||||
})
|
||||
|
||||
function addMjItem() {
|
||||
addMj(queryParams.value).then((res) => {
|
||||
if (res) {
|
||||
searchMj()
|
||||
message.success('添加成功')
|
||||
} else {
|
||||
message.error('添加失败')
|
||||
}
|
||||
})
|
||||
}
|
||||
function getMjTitle(idx) {
|
||||
// const cjObj = {
|
||||
// 1001: '小车',
|
||||
// 1002: '摩托车'
|
||||
// }
|
||||
// const kmObj = {
|
||||
// 1: '科一',
|
||||
// 4: '科四'
|
||||
// }
|
||||
// return `${cjObj[queryParams.value.carTypeId]}${kmObj[queryParams.value.subject]}考前密卷 第${
|
||||
// idx + 1
|
||||
// }套`
|
||||
return `考前密卷 第${idx + 1}套`
|
||||
}
|
||||
function removeMj(item) {
|
||||
message
|
||||
.confirm('是否确认删除密卷?')
|
||||
.then(function () {
|
||||
return delMj(item.secretId)
|
||||
})
|
||||
.then(() => {
|
||||
searchMj()
|
||||
message.success('删除成功')
|
||||
})
|
||||
.catch(() => {})
|
||||
}
|
||||
function clearMjDetail(item) {
|
||||
message
|
||||
.confirm('是否确认清空密卷题目?')
|
||||
.then(function () {
|
||||
return clearMj(item.secretId)
|
||||
})
|
||||
.then(() => {
|
||||
getQuestionList()
|
||||
message.success('清空题目成功')
|
||||
})
|
||||
.catch(() => {})
|
||||
}
|
||||
function searchMj() {
|
||||
getMjList(queryParams.value).then((resp) => {
|
||||
mjList.value = resp || []
|
||||
if (mjList.value.length > 0) {
|
||||
mjIndex.value = 0
|
||||
getQuestionList()
|
||||
}
|
||||
})
|
||||
}
|
||||
function addMjDetail() {
|
||||
showDialog.value = true
|
||||
}
|
||||
function searchQues() {
|
||||
searchQuestion({
|
||||
...queryParams.value,
|
||||
pageNo: 1,
|
||||
pageSize: 1000,
|
||||
question: quesName.value
|
||||
}).then((response) => {
|
||||
quesLit.value = response.list
|
||||
})
|
||||
}
|
||||
function takeoutMj(row) {
|
||||
message
|
||||
.confirm('是否确认将该题移出密卷?')
|
||||
.then(function () {
|
||||
return delMjQuestion(row.id)
|
||||
})
|
||||
.then(() => {
|
||||
getQuestionList()
|
||||
message.success('清空题目成功')
|
||||
})
|
||||
.catch(() => {})
|
||||
}
|
||||
function getQuestionList() {
|
||||
getMjQuestionList({
|
||||
secretId: mjList.value[mjIndex.value].secretId
|
||||
}).then((resp) => {
|
||||
tableList.value = resp
|
||||
})
|
||||
}
|
||||
function sureAddQues(row) {
|
||||
addMjQuestion({
|
||||
secretId: mjList.value[mjIndex.value].secretId,
|
||||
subId: row.subId
|
||||
})
|
||||
.then((resp) => {
|
||||
if (resp) {
|
||||
message.success('添加成功')
|
||||
getQuestionList()
|
||||
}
|
||||
})
|
||||
.catch(() => {})
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped></style>
|
||||
197
src/views/XjApplet/VipData/conponents/SimpleData.vue
Normal file
197
src/views/XjApplet/VipData/conponents/SimpleData.vue
Normal file
@@ -0,0 +1,197 @@
|
||||
<template>
|
||||
<div>
|
||||
<el-form inline label-width="68px" @submit.prevent>
|
||||
<el-row :gutter="20">
|
||||
<el-form-item label="车型">
|
||||
<el-radio-group v-model="queryParams.carTypeId">
|
||||
<el-radio label="1001">小车</el-radio>
|
||||
<el-radio label="1002">摩托车</el-radio>
|
||||
<!-- <el-radio label="B2">货车</el-radio> -->
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
<el-form-item label="科目">
|
||||
<el-radio-group v-model="queryParams.subject">
|
||||
<el-radio label="1">科一</el-radio>
|
||||
<el-radio label="4">科四</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button
|
||||
type="primary"
|
||||
v-hasPermi="['xj-applet:vip-data:jx:search']"
|
||||
@click="
|
||||
() => {
|
||||
queryParams.pageNo = 1
|
||||
getQuestionList()
|
||||
}
|
||||
"
|
||||
>
|
||||
查询
|
||||
</el-button>
|
||||
<el-button type="primary" @click="addMjDetail" v-hasPermi="['xj-applet:vip-data:jx:add']">
|
||||
新增精选试题
|
||||
</el-button>
|
||||
</el-form-item>
|
||||
</el-row>
|
||||
</el-form>
|
||||
<el-table v-loading="loading" :data="tableList" height="calc(100vh - 250px)">
|
||||
<el-table-column type="index" width="55" align="center" />
|
||||
<el-table-column label="题目" align="left" prop="question" min-width="140" />
|
||||
<el-table-column label="选项" align="left" min-width="140">
|
||||
<template #default="{ row }">
|
||||
<p v-if="row.chooseA">A:{{ row.chooseA }}</p>
|
||||
<p v-if="row.chooseB">B:{{ row.chooseB }}</p>
|
||||
<p v-if="row.chooseC">C:{{ row.chooseC }}</p>
|
||||
<p v-if="row.chooseD">D:{{ row.chooseD }}</p>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="答案" align="center" prop="trueAnswer" width="100" />
|
||||
<el-table-column label="科目" align="center" prop="subject" width="100">
|
||||
<template #default="{ row }">
|
||||
<span v-if="row.subject == '1'">科一</span>
|
||||
<span v-if="row.subject == '4'">科四</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="图片" align="center" width="100">
|
||||
<template #default="{ row }">
|
||||
<el-image
|
||||
v-if="row.imageUrl"
|
||||
:src="`https://ss-cloud.ahduima.com/xjxc/pic/${row.imageUrl}`"
|
||||
:preview-src-list="[`https://ss-cloud.ahduima.com/xjxc/pic/${row.imageUrl}`]"
|
||||
:lazy="true"
|
||||
style="width: 80px"
|
||||
/>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作" align="center" width="100px">
|
||||
<template #default="{ row }">
|
||||
<el-button
|
||||
size="small"
|
||||
type="primary"
|
||||
link
|
||||
@click="takeoutMj(row)"
|
||||
v-hasPermi="['xj-applet:vip-data:jx:remove']"
|
||||
>
|
||||
移出精选
|
||||
</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<pagination
|
||||
v-show="total > 0"
|
||||
:total="total"
|
||||
v-model:page="queryParams.pageNo"
|
||||
v-model:limit="queryParams.pageSize"
|
||||
@pagination="getQuestionList"
|
||||
/>
|
||||
|
||||
<el-dialog v-model="showDialog" title="新增试题" width="80%">
|
||||
<div style="display: flex; align-items: center; margin-bottom: 20px">
|
||||
<el-input v-model="quesName" placeholder="请输入题目" clearable @keyup.enter="searchQues" />
|
||||
<el-button style="margin-left: 20px" type="primary" @click="searchQues">搜索</el-button>
|
||||
</div>
|
||||
<el-table :data="quesLit" highlight-current-row height="calc(100vh - 260px)">
|
||||
<el-table-column label="题目" align="left" prop="question" min-width="140" />
|
||||
<el-table-column label="选项" align="left" min-width="140">
|
||||
<template #default="{ row }">
|
||||
<p v-if="row.chooseA">A:{{ row.chooseA }}</p>
|
||||
<p v-if="row.chooseB">B:{{ row.chooseB }}</p>
|
||||
<p v-if="row.chooseC">C:{{ row.chooseC }}</p>
|
||||
<p v-if="row.chooseD">D:{{ row.chooseD }}</p>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="答案" align="center" prop="trueAnswer" width="100" />
|
||||
<el-table-column label="章节" align="center" prop="chapterName" min-width="100" />
|
||||
<el-table-column label="图片" align="center" width="100">
|
||||
<template #default="{ row }">
|
||||
<el-image
|
||||
v-if="row.imageUrl"
|
||||
:src="`https://ss-cloud.ahduima.com/xjxc/pic/${row.imageUrl}`"
|
||||
:preview-src-list="[`https://ss-cloud.ahduima.com/xjxc/pic/${row.imageUrl}`]"
|
||||
:lazy="true"
|
||||
style="width: 80px"
|
||||
/>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作" width="100px">
|
||||
<template #default="{ row }">
|
||||
<el-button type="primary" link @click="sureAddQues(row)">加入精选</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup name="SimpleData">
|
||||
import { getJxQuestionList, addJx, delJxData } from '@/api/xjapplet/vipdatabase.js'
|
||||
import { searchQuestion } from '@/api/xjapplet/xjdatabase.js'
|
||||
|
||||
const message = useMessage() // 消息弹窗
|
||||
|
||||
const queryParams = ref({
|
||||
carTypeId: '1001',
|
||||
subject: '1',
|
||||
pageNo: 1,
|
||||
pageSize: 100
|
||||
})
|
||||
|
||||
const total = ref(0)
|
||||
|
||||
const loading = ref(false)
|
||||
const tableList = ref([])
|
||||
const showDialog = ref(false)
|
||||
const quesName = ref('')
|
||||
const quesLit = ref([])
|
||||
|
||||
onMounted(() => {
|
||||
getQuestionList()
|
||||
})
|
||||
function addMjDetail() {
|
||||
showDialog.value = true
|
||||
}
|
||||
function searchQues() {
|
||||
searchQuestion({
|
||||
...queryParams.value,
|
||||
pageNo: 1,
|
||||
pageSize: 1000,
|
||||
question: quesName.value
|
||||
}).then((response) => {
|
||||
quesLit.value = response.list
|
||||
})
|
||||
}
|
||||
function takeoutMj(row) {
|
||||
message
|
||||
.confirm('是否确认将该题移出精选?')
|
||||
.then(function () {
|
||||
return delJxData(row.id)
|
||||
})
|
||||
.then(() => {
|
||||
getQuestionList()
|
||||
message.success('清空题目成功')
|
||||
})
|
||||
.catch(() => {})
|
||||
}
|
||||
function getQuestionList() {
|
||||
getJxQuestionList(queryParams.value).then((resp) => {
|
||||
tableList.value = resp.list
|
||||
total.value = resp.total
|
||||
})
|
||||
}
|
||||
function sureAddQues(row) {
|
||||
addJx({
|
||||
questionId: row.questionId,
|
||||
carTypeId: queryParams.value.carTypeId,
|
||||
subject: queryParams.value.subject
|
||||
})
|
||||
.then((resp) => {
|
||||
if (resp) {
|
||||
message.success('添加成功')
|
||||
getQuestionList()
|
||||
}
|
||||
})
|
||||
.catch(() => {})
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped></style>
|
||||
20
src/views/XjApplet/VipData/index.vue
Normal file
20
src/views/XjApplet/VipData/index.vue
Normal file
@@ -0,0 +1,20 @@
|
||||
<template>
|
||||
<el-tabs v-model="tabIndex">
|
||||
<el-tab-pane label="精选题" :name="1" v-if="checkPermi(['xj-applet:vip-data:jx'])">
|
||||
<SimpleData v-if="tabIndex == 1" />
|
||||
</el-tab-pane>
|
||||
<el-tab-pane label="密卷" :name="2" v-if="checkPermi(['xj-applet:vip-data:mj'])">
|
||||
<SecretData v-if="tabIndex == 2" />
|
||||
</el-tab-pane>
|
||||
</el-tabs>
|
||||
</template>
|
||||
|
||||
<script setup name="VipData">
|
||||
import { checkPermi } from '@/utils/permission'
|
||||
import SimpleData from './conponents/SimpleData.vue'
|
||||
import SecretData from './conponents/SecretData.vue'
|
||||
|
||||
const tabIndex = ref(1)
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped></style>
|
||||
341
src/views/XjApplet/XjDatabase/Components/QuestionAddForm.vue
Normal file
341
src/views/XjApplet/XjDatabase/Components/QuestionAddForm.vue
Normal file
@@ -0,0 +1,341 @@
|
||||
<template>
|
||||
<el-dialog
|
||||
title="试题"
|
||||
:close-on-click-modal="false"
|
||||
append-to-body
|
||||
v-model="visible"
|
||||
width="900px"
|
||||
>
|
||||
<div>
|
||||
<el-form
|
||||
ref="dialogFormRef"
|
||||
:model="dialogForm"
|
||||
:rules="dataRule"
|
||||
label-width="auto"
|
||||
label-position="right"
|
||||
@keyup.enter="dialogFormSubmit()"
|
||||
>
|
||||
<el-row>
|
||||
<el-col :span="24">
|
||||
<el-form-item label="题目" prop="question">
|
||||
<el-input
|
||||
v-model="dialogForm.question"
|
||||
maxlength="200"
|
||||
placeholder="请输入题目"
|
||||
clearable
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row>
|
||||
<el-col :span="24">
|
||||
<el-form-item label="题目关键字" prop="titleWords">
|
||||
<el-input
|
||||
v-model="dialogForm.titleWords"
|
||||
maxlength="1000"
|
||||
placeholder="请输入"
|
||||
clearable
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="12">
|
||||
<el-form-item label="专项" prop="categoryTitle">
|
||||
<el-input
|
||||
v-model="dialogForm.categoryTitle"
|
||||
maxlength="1000"
|
||||
placeholder="请输入"
|
||||
clearable
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="章节" prop="chapter">
|
||||
<el-select v-model="dialogForm.chapter" style="width: 100%">
|
||||
<el-option
|
||||
v-for="dict in chapterOptions"
|
||||
:key="dict.chapterId"
|
||||
:label="dict.chapterName"
|
||||
:value="dict.chapterId"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="12">
|
||||
<el-form-item label="题型" prop="type">
|
||||
<el-radio-group v-model="dialogForm.type">
|
||||
<el-radio label="1">判断题</el-radio>
|
||||
<el-radio label="2">单选题</el-radio>
|
||||
<el-radio label="3">多选题</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
|
||||
<el-col :span="12">
|
||||
<el-form-item label="排序" prop="showOrder">
|
||||
<el-input
|
||||
v-model="dialogForm.showOrder"
|
||||
maxlength="20"
|
||||
placeholder="请输入"
|
||||
clearable
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="12">
|
||||
<el-form-item label="选项A" prop="chooseA">
|
||||
<el-input
|
||||
v-model="dialogForm.chooseA"
|
||||
maxlength="200"
|
||||
placeholder="请输入"
|
||||
clearable
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="选项B" prop="chooseB">
|
||||
<el-input
|
||||
v-model="dialogForm.chooseB"
|
||||
maxlength="200"
|
||||
placeholder="请输入"
|
||||
clearable
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="12">
|
||||
<el-form-item label="选项C" prop="chooseC">
|
||||
<el-input
|
||||
v-model="dialogForm.chooseC"
|
||||
maxlength="200"
|
||||
placeholder="请输入"
|
||||
clearable
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="选项D" prop="chooseD">
|
||||
<el-input
|
||||
v-model="dialogForm.chooseD"
|
||||
maxlength="200"
|
||||
placeholder="请输入"
|
||||
clearable
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row>
|
||||
<el-col :span="24">
|
||||
<el-form-item label="答案" prop="trueAnswer">
|
||||
<el-input
|
||||
v-model="dialogForm.trueAnswer"
|
||||
maxlength="200"
|
||||
placeholder="请输入"
|
||||
clearable
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row>
|
||||
<el-col :span="24">
|
||||
<el-form-item label="难点分析" prop="bestAnswer">
|
||||
<el-input
|
||||
v-model="dialogForm.bestAnswer"
|
||||
maxlength="1000"
|
||||
placeholder="请输入"
|
||||
clearable
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row>
|
||||
<el-col :span="24">
|
||||
<el-form-item label="答题技巧" prop="skillInfo">
|
||||
<el-input
|
||||
v-model="dialogForm.skillInfo"
|
||||
maxlength="1000"
|
||||
placeholder="请输入"
|
||||
clearable
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<el-row>
|
||||
<el-col :span="24">
|
||||
<el-form-item label="技巧关键字" prop="skillWords">
|
||||
<el-input
|
||||
v-model="dialogForm.skillWords"
|
||||
maxlength="1000"
|
||||
placeholder="请输入"
|
||||
clearable
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row>
|
||||
<el-col :span="24">
|
||||
<el-form-item label="车型" prop="carTypeId">
|
||||
<span v-if="dialogForm.carTypeId == '1001'">小车</span>
|
||||
<span v-if="dialogForm.carTypeId == '1002'">摩托车</span>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row>
|
||||
<el-col :span="24">
|
||||
<el-form-item label="科目" prop="subject">
|
||||
<span v-if="dialogForm.subject == '1'">科一</span>
|
||||
<span v-if="dialogForm.subject == '4'">科四</span>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row :gutter="20">
|
||||
<el-form-item label="题目图片" prop="imageUrl">
|
||||
<el-upload
|
||||
action="#"
|
||||
accept=".png,.jpg,.jpeg,.gif"
|
||||
:limit="1"
|
||||
:http-request="handleImport"
|
||||
:on-exceed="handleExceed"
|
||||
:show-file-list="false"
|
||||
>
|
||||
<img v-if="imgUrl" :src="imgUrl" style="width: 200px" />
|
||||
<i v-else class="el-icon-plus"></i>
|
||||
</el-upload>
|
||||
</el-form-item>
|
||||
</el-row>
|
||||
</el-form>
|
||||
</div>
|
||||
<template #footer>
|
||||
<span class="dialog-footer">
|
||||
<el-button plain @click="visible = false">取消</el-button>
|
||||
<el-button type="primary" :disabled="!canSubmit" @click="dialogFormSubmit()"
|
||||
>确定</el-button
|
||||
>
|
||||
</span>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</template>
|
||||
|
||||
<script setup name="QuesAddForm">
|
||||
import { addQuestion, updateQuestion, uploadFile } from '@/api/xjapplet/xjdatabase'
|
||||
|
||||
const message = useMessage() // 消息弹窗
|
||||
|
||||
const emit = defineEmits(['update'])
|
||||
|
||||
const visible = ref(false)
|
||||
const canSubmit = ref(true)
|
||||
const imgUrl = ref(undefined)
|
||||
const chapterOptions = ref([])
|
||||
const dialogForm = ref({
|
||||
id: undefined,
|
||||
showOrder: 0,
|
||||
categoryTitle: undefined,
|
||||
question: undefined,
|
||||
bestAnswer: undefined,
|
||||
chooseA: undefined,
|
||||
chooseB: undefined,
|
||||
chooseC: undefined,
|
||||
chooseD: undefined,
|
||||
imageUrl: undefined,
|
||||
type: undefined,
|
||||
trueAnswer: undefined,
|
||||
carTypeId: undefined,
|
||||
skillInfoPic: undefined
|
||||
})
|
||||
|
||||
const dataRule = {
|
||||
schoolName: [{ required: true, message: '驾校名称不能为空', trigger: 'blur' }],
|
||||
status: [{ required: true, message: '状态不能为空', trigger: 'blur' }]
|
||||
}
|
||||
|
||||
const open = (info = undefined, opts) => {
|
||||
visible.value = true
|
||||
chapterOptions.value = opts
|
||||
resetDialogForm()
|
||||
if (info) {
|
||||
dialogForm.value = { ...info, chapter: parseInt(info.chapter) }
|
||||
if (dialogForm.value.imageUrl) {
|
||||
imgUrl.value = 'https://ss-cloud.ahduima.com/xjxc/pic/' + dialogForm.value.imageUrl
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
defineExpose({
|
||||
open
|
||||
})
|
||||
|
||||
const resetDialogForm = () => {
|
||||
dialogForm.value = {
|
||||
id: undefined,
|
||||
showOrder: 0,
|
||||
question: undefined,
|
||||
bestAnswer: undefined,
|
||||
chooseA: undefined,
|
||||
chooseB: undefined,
|
||||
chooseC: undefined,
|
||||
chooseD: undefined,
|
||||
imageUrl: undefined,
|
||||
type: undefined,
|
||||
trueAnswer: undefined,
|
||||
carTypeId: undefined,
|
||||
skillInfoPic: undefined,
|
||||
skillInfo: undefined,
|
||||
jqtp: undefined,
|
||||
biaoji: undefined,
|
||||
titleWords: undefined,
|
||||
skillWords: undefined,
|
||||
optaWords: undefined,
|
||||
optbWords: undefined,
|
||||
optcWords: undefined,
|
||||
optdWords: undefined,
|
||||
carTypeId: undefined
|
||||
}
|
||||
}
|
||||
const dialogFormRef = ref(null)
|
||||
// 表单提交
|
||||
const dialogFormSubmit = async () => {
|
||||
if (!dialogFormRef.value) return
|
||||
const valid = await dialogFormRef.value.validate()
|
||||
if (!valid) return
|
||||
|
||||
if (dialogForm.value.questionId) {
|
||||
updateQuestion(dialogForm.value).then((response) => {
|
||||
if (response) {
|
||||
message.success('修改成功')
|
||||
emit('update')
|
||||
visible.value = false
|
||||
}
|
||||
})
|
||||
} else {
|
||||
addQuestion(dialogForm.value).then((response) => {
|
||||
if (response) {
|
||||
message.success('新增成功')
|
||||
visible.value = false
|
||||
emit('update')
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
const handleImport = (opt) => {
|
||||
const data = new FormData()
|
||||
data.append('file', opt.file)
|
||||
uploadFile(data).then((resp) => {
|
||||
if (resp.code == 200) {
|
||||
message.success('文件上传成功')
|
||||
dialogForm.value.imageUrl = resp.data
|
||||
imgUrl.value = 'https://ss-cloud.ahduima.com/xjxc/pic/' + dialogForm.value.imageUrl
|
||||
}
|
||||
})
|
||||
}
|
||||
const handleExceed = (files) => {
|
||||
handleImport({ file: files[0] })
|
||||
}
|
||||
</script>
|
||||
183
src/views/XjApplet/XjDatabase/index.vue
Normal file
183
src/views/XjApplet/XjDatabase/index.vue
Normal file
@@ -0,0 +1,183 @@
|
||||
<template>
|
||||
<div class="app-container" style="text-align: center">
|
||||
<el-form :inline="true" label-width="68px" @submit.prevent>
|
||||
<el-row :gutter="20">
|
||||
<el-form-item label="车型">
|
||||
<el-radio-group v-model="queryParams.carTypeId" @change="getQuestionChapter">
|
||||
<el-radio label="1001">小车</el-radio>
|
||||
<el-radio label="1002">摩托车</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
<el-form-item label="科目">
|
||||
<el-radio-group v-model="queryParams.subject" @change="getQuestionChapter">
|
||||
<el-radio label="1">科一</el-radio>
|
||||
<el-radio label="4">科四</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="有图片">
|
||||
<el-radio-group v-model="queryParams.isPic">
|
||||
<el-radio :label="true">有</el-radio>
|
||||
<el-radio :label="false">无</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
</el-row>
|
||||
<el-row :gutter="20">
|
||||
<el-form-item label="题目">
|
||||
<el-input
|
||||
v-model="queryParams.question"
|
||||
placeholder="请输入题目"
|
||||
clearable
|
||||
style="width: 400px"
|
||||
@keyup.enter="handleQuery"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button
|
||||
type="primary"
|
||||
@click="handleQuery"
|
||||
v-hasPermi="['xj-applet:xj-database:search']"
|
||||
>搜索</el-button
|
||||
>
|
||||
<el-button type="primary" @click="handleAdd" v-hasPermi="['xj-applet:xj-database:add']"
|
||||
>新增</el-button
|
||||
>
|
||||
</el-form-item>
|
||||
</el-row>
|
||||
</el-form>
|
||||
|
||||
<el-table
|
||||
v-loading="loading"
|
||||
:data="tableList"
|
||||
highlight-current-row
|
||||
max-height="calc(100vh - 260px)"
|
||||
>
|
||||
<el-table-column type="index" width="55" align="center" />
|
||||
<el-table-column label="题目" align="left" prop="question" min-width="140" />
|
||||
<el-table-column label="选项" align="left" min-width="140">
|
||||
<template #default="{ row }">
|
||||
<p v-if="row.chooseA">A:{{ row.chooseA }}</p>
|
||||
<p v-if="row.chooseB">B:{{ row.chooseB }}</p>
|
||||
<p v-if="row.chooseC">C:{{ row.chooseC }}</p>
|
||||
<p v-if="row.chooseD">D:{{ row.chooseD }}</p>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="答案" align="center" prop="trueAnswer" width="100" />
|
||||
<el-table-column label="科目" align="center" prop="subject" width="100">
|
||||
<template #default="{ row }">
|
||||
<span v-if="row.subject == '1'">科一</span>
|
||||
<span v-if="row.subject == '4'">科四</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="章节" align="center" prop="chapterName" min-width="100" />
|
||||
<el-table-column label="图片" align="center" width="100">
|
||||
<template #default="{ row }">
|
||||
<el-image
|
||||
v-if="row.imageUrl"
|
||||
:src="`https://ss-cloud.ahduima.com/xjxc/pic/${row.imageUrl}`"
|
||||
:preview-src-list="[`https://ss-cloud.ahduima.com/xjxc/pic/${row.imageUrl}`]"
|
||||
:lazy="true"
|
||||
style="width: 80px"
|
||||
/>
|
||||
</template>
|
||||
</el-table-column>
|
||||
|
||||
<el-table-column label="操作" align="center" width="140">
|
||||
<template #default="scope">
|
||||
<el-button
|
||||
type="primary"
|
||||
link
|
||||
@click="handleEdit(scope.row)"
|
||||
v-hasPermi="['xj-applet:xj-database:edit']"
|
||||
>
|
||||
编辑
|
||||
</el-button>
|
||||
<el-button
|
||||
type="primary"
|
||||
link
|
||||
@click="handleDelete(scope.row.id)"
|
||||
v-hasPermi="['xj-applet:xj-database:remove']"
|
||||
>
|
||||
删除
|
||||
</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
|
||||
<pagination
|
||||
v-show="total > 0"
|
||||
:total="total"
|
||||
v-model:page="queryParams.pageNo"
|
||||
v-model:limit="queryParams.pageSize"
|
||||
@pagination="getList"
|
||||
/>
|
||||
<QuestionAddForm ref="dialogAddForm" @update="getList" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup name="XjDatabase">
|
||||
import { searchQuestion, deleteQuestion, getQuestionSort } from '@/api/xjapplet/xjdatabase'
|
||||
import QuestionAddForm from './components/QuestionAddForm.vue'
|
||||
|
||||
const loading = ref(false)
|
||||
const total = ref(0)
|
||||
const tableList = ref([])
|
||||
const queryParams = ref({
|
||||
question: '',
|
||||
carTypeId: '1001',
|
||||
subject: '1',
|
||||
isPic: undefined,
|
||||
pageNo: 1,
|
||||
pageSize: 100
|
||||
})
|
||||
|
||||
onMounted(() => {
|
||||
getQuestionChapter()
|
||||
})
|
||||
|
||||
const chapterOptions = ref([])
|
||||
const getQuestionChapter = () => {
|
||||
getQuestionSort({
|
||||
carTypeId: queryParams.value.carTypeId,
|
||||
subject: queryParams.value.subject
|
||||
}).then((data) => {
|
||||
chapterOptions.value = data
|
||||
})
|
||||
}
|
||||
|
||||
function getList() {
|
||||
loading.value = true
|
||||
searchQuestion(queryParams.value).then((response) => {
|
||||
tableList.value = response.list
|
||||
total.value = response.total
|
||||
loading.value = false
|
||||
})
|
||||
}
|
||||
|
||||
function handleQuery() {
|
||||
getList()
|
||||
}
|
||||
|
||||
const dialogAddForm = ref(null)
|
||||
function handleEdit(item) {
|
||||
dialogAddForm.value.open(item, chapterOptions.value)
|
||||
}
|
||||
|
||||
function handleAdd() {
|
||||
dialogAddForm.value.open(
|
||||
{
|
||||
subject: queryParams.value.subject,
|
||||
carTypeId: queryParams.value.carTypeId
|
||||
},
|
||||
chapterOptions.value
|
||||
)
|
||||
}
|
||||
|
||||
function handleDelete(id) {
|
||||
deleteQuestion(id).then(() => {
|
||||
getList()
|
||||
})
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped></style>
|
||||
@@ -45,6 +45,18 @@ export default ({ command, mode }) => {
|
||||
ws: false,
|
||||
changeOrigin: true,
|
||||
rewrite: (path) => path.replace(new RegExp(`^/crm-api`), '')
|
||||
},
|
||||
['/applet-api']: {
|
||||
target: env.VITE_APPLET_URL,
|
||||
ws: false,
|
||||
changeOrigin: true,
|
||||
rewrite: (path) => path.replace(new RegExp(`^/applet-api`), '')
|
||||
},
|
||||
['/tiku-api']: {
|
||||
target: env.VITE_TIKU_URL,
|
||||
ws: false,
|
||||
changeOrigin: true,
|
||||
rewrite: (path) => path.replace(new RegExp(`^/tiku-api`), '')
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user