Compare commits
10 Commits
d9ecdc9a53
...
b197f9de4b
| Author | SHA1 | Date |
|---|---|---|
|
|
b197f9de4b | |
|
|
38a2844619 | |
|
|
f2a0f69465 | |
|
|
589151d1ed | |
|
|
2dbc91653a | |
|
|
ff14fa5e4e | |
|
|
c2005614bc | |
|
|
42c1ea3c89 | |
|
|
4b038efabf | |
|
|
ea552797b5 |
|
|
@ -0,0 +1,3 @@
|
||||||
|
|
||||||
|
# Don't index SpecStory auto-save files, but allow explicit context inclusion via @ references
|
||||||
|
.specstory/**
|
||||||
|
|
@ -1,8 +1,8 @@
|
||||||
# 页面标题
|
# 页面标题
|
||||||
VITE_APP_TITLE = 若依管理系统
|
VITE_APP_TITLE = 智奇管理系统
|
||||||
|
|
||||||
# 开发环境配置
|
# 开发环境配置
|
||||||
VITE_APP_ENV = 'development'
|
VITE_APP_ENV = 'development'
|
||||||
|
|
||||||
# 若依管理系统/开发环境
|
# 智奇管理系统/开发环境
|
||||||
VITE_APP_BASE_API = '/dev-api'
|
VITE_APP_BASE_API = '/dev-api'
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,10 @@
|
||||||
# 页面标题
|
# 页面标题
|
||||||
VITE_APP_TITLE = 若依管理系统
|
VITE_APP_TITLE = 智奇管理系统
|
||||||
|
|
||||||
# 生产环境配置
|
# 生产环境配置
|
||||||
VITE_APP_ENV = 'production'
|
VITE_APP_ENV = 'production'
|
||||||
|
|
||||||
# 若依管理系统/生产环境
|
# 智奇管理系统/生产环境
|
||||||
VITE_APP_BASE_API = '/prod-api'
|
VITE_APP_BASE_API = '/prod-api'
|
||||||
|
|
||||||
# 是否在打包时开启压缩,支持 gzip 和 brotli
|
# 是否在打包时开启压缩,支持 gzip 和 brotli
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,10 @@
|
||||||
# 页面标题
|
# 页面标题
|
||||||
VITE_APP_TITLE = 若依管理系统
|
VITE_APP_TITLE = 智奇管理系统
|
||||||
|
|
||||||
# 生产环境配置
|
# 生产环境配置
|
||||||
VITE_APP_ENV = 'staging'
|
VITE_APP_ENV = 'staging'
|
||||||
|
|
||||||
# 若依管理系统/生产环境
|
# 智奇管理系统/生产环境
|
||||||
VITE_APP_BASE_API = '/stage-api'
|
VITE_APP_BASE_API = '/stage-api'
|
||||||
|
|
||||||
# 是否在打包时开启压缩,支持 gzip 和 brotli
|
# 是否在打包时开启压缩,支持 gzip 和 brotli
|
||||||
|
|
|
||||||
|
|
@ -1 +0,0 @@
|
||||||
custom: http://doc.ruoyi.vip/ruoyi-vue/other/donate.html
|
|
||||||
|
|
@ -18,6 +18,7 @@ selenium-debug.log
|
||||||
*.njsproj
|
*.njsproj
|
||||||
*.sln
|
*.sln
|
||||||
*.local
|
*.local
|
||||||
|
.specstory
|
||||||
|
|
||||||
package-lock.json
|
package-lock.json
|
||||||
yarn.lock
|
yarn.lock
|
||||||
|
|
|
||||||
|
|
@ -103,6 +103,6 @@ yarn dev
|
||||||
</table>
|
</table>
|
||||||
|
|
||||||
|
|
||||||
## 若依前后端分离交流群
|
## 智奇前后端分离交流群
|
||||||
|
|
||||||
QQ群: [](https://jq.qq.com/?_wv=1027&k=5bVB1og) [](https://jq.qq.com/?_wv=1027&k=5eiA4DH) [](https://jq.qq.com/?_wv=1027&k=5AxMKlC) [](https://jq.qq.com/?_wv=1027&k=51G72yr) [](https://jq.qq.com/?_wv=1027&k=VvjN2nvu) [](https://jq.qq.com/?_wv=1027&k=5vYAqA05) [](https://jq.qq.com/?_wv=1027&k=kOIINEb5) [](https://jq.qq.com/?_wv=1027&k=UKtX5jhs) [](https://jq.qq.com/?_wv=1027&k=EI9an8lJ) [](https://jq.qq.com/?_wv=1027&k=SWCtLnMz) [](https://jq.qq.com/?_wv=1027&k=96Dkdq0k) [](https://jq.qq.com/?_wv=1027&k=0fsNiYZt) [](https://jq.qq.com/?_wv=1027&k=7xw4xUG1) [](https://jq.qq.com/?_wv=1027&k=eCx8eyoJ) [](https://jq.qq.com/?_wv=1027&k=SpyH2875) [](https://jq.qq.com/?_wv=1027&k=tKEt51dz) [](http://qm.qq.com/cgi-bin/qm/qr?_wv=1027&k=0vBbSb0ztbBgVtn3kJS-Q4HUNYwip89G&authKey=8irq5PhutrZmWIvsUsklBxhj57l%2F1nOZqjzigkXZVoZE451GG4JHPOqW7AW6cf0T&noverify=0&group_code=143961921) [](http://qm.qq.com/cgi-bin/qm/qr?_wv=1027&k=ZFAPAbp09S2ltvwrJzp7wGlbopsc0rwi&authKey=HB2cxpxP2yspk%2Bo3WKTBfktRCccVkU26cgi5B16u0KcAYrVu7sBaE7XSEqmMdFQp&noverify=0&group_code=174951577) [](http://qm.qq.com/cgi-bin/qm/qr?_wv=1027&k=Fn2aF5IHpwsy8j6VlalNJK6qbwFLFHat&authKey=uyIT%2B97x2AXj3odyXpsSpVaPMC%2Bidw0LxG5MAtEqlrcBcWJUA%2FeS43rsF1Tg7IRJ&noverify=0&group_code=161281055) [](http://qm.qq.com/cgi-bin/qm/qr?_wv=1027&k=XIzkm_mV2xTsUtFxo63bmicYoDBA6Ifm&authKey=dDW%2F4qsmw3x9govoZY9w%2FoWAoC4wbHqGal%2BbqLzoS6VBarU8EBptIgPKN%2FviyC8j&noverify=0&group_code=138988063) [](http://qm.qq.com/cgi-bin/qm/qr?_wv=1027&k=DkugnCg68PevlycJSKSwjhFqfIgrWWwR&authKey=pR1Pa5lPIeGF%2FFtIk6d%2FGB5qFi0EdvyErtpQXULzo03zbhopBHLWcuqdpwY241R%2F&noverify=0&group_code=151450850) [](http://qm.qq.com/cgi-bin/qm/qr?_wv=1027&k=F58bgRa-Dp-rsQJThiJqIYv8t4-lWfXh&authKey=UmUs4CVG5OPA1whvsa4uSespOvyd8%2FAr9olEGaWAfdLmfKQk%2FVBp2YU3u2xXXt76&noverify=0&group_code=224622315) [](http://qm.qq.com/cgi-bin/qm/qr?_wv=1027&k=Nxb2EQ5qozWa218Wbs7zgBnjLSNk_tVT&authKey=obBKXj6SBKgrFTJZx0AqQnIYbNOvBB2kmgwWvGhzxR67RoRr84%2Bus5OadzMcdJl5&noverify=0&group_code=287842588) [](http://qm.qq.com/cgi-bin/qm/qr?_wv=1027&k=numtK1M_I4eVd2Gvg8qtbuL8JgX42qNh&authKey=giV9XWMaFZTY%2FqPlmWbkB9g3fi0Ev5CwEtT9Tgei0oUlFFCQLDp4ozWRiVIzubIm&noverify=0&group_code=187944233) 点击按钮入群。
|
QQ群: [](https://jq.qq.com/?_wv=1027&k=5bVB1og) [](https://jq.qq.com/?_wv=1027&k=5eiA4DH) [](https://jq.qq.com/?_wv=1027&k=5AxMKlC) [](https://jq.qq.com/?_wv=1027&k=51G72yr) [](https://jq.qq.com/?_wv=1027&k=VvjN2nvu) [](https://jq.qq.com/?_wv=1027&k=5vYAqA05) [](https://jq.qq.com/?_wv=1027&k=kOIINEb5) [](https://jq.qq.com/?_wv=1027&k=UKtX5jhs) [](https://jq.qq.com/?_wv=1027&k=EI9an8lJ) [](https://jq.qq.com/?_wv=1027&k=SWCtLnMz) [](https://jq.qq.com/?_wv=1027&k=96Dkdq0k) [](https://jq.qq.com/?_wv=1027&k=0fsNiYZt) [](https://jq.qq.com/?_wv=1027&k=7xw4xUG1) [](https://jq.qq.com/?_wv=1027&k=eCx8eyoJ) [](https://jq.qq.com/?_wv=1027&k=SpyH2875) [](https://jq.qq.com/?_wv=1027&k=tKEt51dz) [](http://qm.qq.com/cgi-bin/qm/qr?_wv=1027&k=0vBbSb0ztbBgVtn3kJS-Q4HUNYwip89G&authKey=8irq5PhutrZmWIvsUsklBxhj57l%2F1nOZqjzigkXZVoZE451GG4JHPOqW7AW6cf0T&noverify=0&group_code=143961921) [](http://qm.qq.com/cgi-bin/qm/qr?_wv=1027&k=ZFAPAbp09S2ltvwrJzp7wGlbopsc0rwi&authKey=HB2cxpxP2yspk%2Bo3WKTBfktRCccVkU26cgi5B16u0KcAYrVu7sBaE7XSEqmMdFQp&noverify=0&group_code=174951577) [](http://qm.qq.com/cgi-bin/qm/qr?_wv=1027&k=Fn2aF5IHpwsy8j6VlalNJK6qbwFLFHat&authKey=uyIT%2B97x2AXj3odyXpsSpVaPMC%2Bidw0LxG5MAtEqlrcBcWJUA%2FeS43rsF1Tg7IRJ&noverify=0&group_code=161281055) [](http://qm.qq.com/cgi-bin/qm/qr?_wv=1027&k=XIzkm_mV2xTsUtFxo63bmicYoDBA6Ifm&authKey=dDW%2F4qsmw3x9govoZY9w%2FoWAoC4wbHqGal%2BbqLzoS6VBarU8EBptIgPKN%2FviyC8j&noverify=0&group_code=138988063) [](http://qm.qq.com/cgi-bin/qm/qr?_wv=1027&k=DkugnCg68PevlycJSKSwjhFqfIgrWWwR&authKey=pR1Pa5lPIeGF%2FFtIk6d%2FGB5qFi0EdvyErtpQXULzo03zbhopBHLWcuqdpwY241R%2F&noverify=0&group_code=151450850) [](http://qm.qq.com/cgi-bin/qm/qr?_wv=1027&k=F58bgRa-Dp-rsQJThiJqIYv8t4-lWfXh&authKey=UmUs4CVG5OPA1whvsa4uSespOvyd8%2FAr9olEGaWAfdLmfKQk%2FVBp2YU3u2xXXt76&noverify=0&group_code=224622315) [](http://qm.qq.com/cgi-bin/qm/qr?_wv=1027&k=Nxb2EQ5qozWa218Wbs7zgBnjLSNk_tVT&authKey=obBKXj6SBKgrFTJZx0AqQnIYbNOvBB2kmgwWvGhzxR67RoRr84%2Bus5OadzMcdJl5&noverify=0&group_code=287842588) [](http://qm.qq.com/cgi-bin/qm/qr?_wv=1027&k=numtK1M_I4eVd2Gvg8qtbuL8JgX42qNh&authKey=giV9XWMaFZTY%2FqPlmWbkB9g3fi0Ev5CwEtT9Tgei0oUlFFCQLDp4ozWRiVIzubIm&noverify=0&group_code=187944233) [](http://qm.qq.com/cgi-bin/qm/qr?_wv=1027&k=G6r5KGCaa3pqdbUSXNIgYloyb8e0_L0D&authKey=4w8tF1eGW7%2FedWn%2FHAypQksdrML%2BDHolQSx7094Agm7Luakj9EbfPnSTxSi2T1LQ&noverify=0&group_code=228578329) 点击按钮入群。
|
||||||
|
|
@ -7,7 +7,7 @@
|
||||||
<meta name="renderer" content="webkit">
|
<meta name="renderer" content="webkit">
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no">
|
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no">
|
||||||
<link rel="icon" href="/favicon.ico">
|
<link rel="icon" href="/favicon.ico">
|
||||||
<title>若依管理系统</title>
|
<title>智奇管理系统</title>
|
||||||
<!--[if lt IE 11]><script>window.location.href='/html/ie.html';</script><![endif]-->
|
<!--[if lt IE 11]><script>window.location.href='/html/ie.html';</script><![endif]-->
|
||||||
<style>
|
<style>
|
||||||
html,
|
html,
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,8 @@
|
||||||
{
|
{
|
||||||
"name": "ruoyi",
|
"name": "ruoyi",
|
||||||
"version": "3.8.9",
|
"version": "3.8.9",
|
||||||
"description": "若依管理系统",
|
"description": "智奇管理系统",
|
||||||
"author": "若依",
|
"author": "智奇",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
|
|
|
||||||
|
|
@ -1,60 +1,50 @@
|
||||||
import request from '@/utils/request'
|
// 静态登录方法
|
||||||
|
const staticUser = {
|
||||||
|
username: 'admin',
|
||||||
|
password: 'admin123',
|
||||||
|
token: 'static-token',
|
||||||
|
userId: 1,
|
||||||
|
userName: 'admin',
|
||||||
|
avatar: '',
|
||||||
|
roles: ['admin'],
|
||||||
|
permissions: ['*:*:*']
|
||||||
|
};
|
||||||
|
|
||||||
// 登录方法
|
// 登录方法
|
||||||
export function login(username, password, code, uuid) {
|
export function login(username, password, code, uuid) {
|
||||||
const data = {
|
return new Promise((resolve, reject) => {
|
||||||
username,
|
if (username === staticUser.username && password === staticUser.password) {
|
||||||
password,
|
resolve({ token: staticUser.token });
|
||||||
code,
|
} else {
|
||||||
uuid
|
reject(new Error('账号或密码错误'));
|
||||||
}
|
}
|
||||||
return request({
|
});
|
||||||
url: '/login',
|
|
||||||
headers: {
|
|
||||||
isToken: false,
|
|
||||||
repeatSubmit: false
|
|
||||||
},
|
|
||||||
method: 'post',
|
|
||||||
data: data
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 注册方法
|
// 注册方法(静态页面不实现)
|
||||||
export function register(data) {
|
export function register(data) {
|
||||||
return request({
|
return Promise.reject(new Error('静态页面不支持注册'));
|
||||||
url: '/register',
|
|
||||||
headers: {
|
|
||||||
isToken: false
|
|
||||||
},
|
|
||||||
method: 'post',
|
|
||||||
data: data
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 获取用户详细信息
|
// 获取用户详细信息
|
||||||
export function getInfo() {
|
export function getInfo() {
|
||||||
return request({
|
return Promise.resolve({
|
||||||
url: '/getInfo',
|
user: staticUser,
|
||||||
method: 'get'
|
roles: staticUser.roles,
|
||||||
})
|
permissions: staticUser.permissions
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// 退出方法
|
// 退出方法
|
||||||
export function logout() {
|
export function logout() {
|
||||||
return request({
|
return Promise.resolve();
|
||||||
url: '/logout',
|
|
||||||
method: 'post'
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 获取验证码
|
// 获取验证码(静态假数据)
|
||||||
export function getCodeImg() {
|
export function getCodeImg() {
|
||||||
return request({
|
return Promise.resolve({
|
||||||
url: '/captchaImage',
|
img: '',
|
||||||
headers: {
|
uuid: 'static-uuid',
|
||||||
isToken: false
|
captchaEnabled: false
|
||||||
},
|
});
|
||||||
method: 'get',
|
|
||||||
timeout: 20000
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
@ -1,60 +1,80 @@
|
||||||
import request from '@/utils/request'
|
// 模拟静态数据
|
||||||
|
const mockConfigs = [
|
||||||
|
{ configId: 1, configName: '系统名称', configKey: 'sys.name', configValue: '智奇管理系统', configType: 'Y', status: '0', createTime: '2023-01-01' },
|
||||||
|
{ configId: 2, configName: '系统版本', configKey: 'sys.version', configValue: 'v1.0.0', configType: 'Y', status: '0', createTime: '2023-01-02' }
|
||||||
|
];
|
||||||
|
|
||||||
// 查询参数列表
|
// 查询参数列表
|
||||||
export function listConfig(query) {
|
export function listConfig(query) {
|
||||||
return request({
|
const { pageNum, pageSize, configName, configKey, configType, status } = query;
|
||||||
url: '/system/config/list',
|
let filteredConfigs = [...mockConfigs];
|
||||||
method: 'get',
|
|
||||||
params: query
|
if (configName) {
|
||||||
})
|
filteredConfigs = filteredConfigs.filter(config => config.configName.includes(configName));
|
||||||
|
}
|
||||||
|
if (configKey) {
|
||||||
|
filteredConfigs = filteredConfigs.filter(config => config.configKey.includes(configKey));
|
||||||
|
}
|
||||||
|
if (configType) {
|
||||||
|
filteredConfigs = filteredConfigs.filter(config => config.configType === configType);
|
||||||
|
}
|
||||||
|
if (status) {
|
||||||
|
filteredConfigs = filteredConfigs.filter(config => config.status === status);
|
||||||
|
}
|
||||||
|
|
||||||
|
const total = filteredConfigs.length;
|
||||||
|
const start = (pageNum - 1) * pageSize;
|
||||||
|
const end = start + pageSize;
|
||||||
|
const list = filteredConfigs.slice(start, end);
|
||||||
|
|
||||||
|
return Promise.resolve({
|
||||||
|
total,
|
||||||
|
rows: list
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// 查询参数详细
|
// 查询参数详细
|
||||||
export function getConfig(configId) {
|
export function getConfig(configId) {
|
||||||
return request({
|
const config = mockConfigs.find(c => c.configId === parseInt(configId));
|
||||||
url: '/system/config/' + configId,
|
return Promise.resolve(config || {});
|
||||||
method: 'get'
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 根据参数键名查询参数值
|
// 根据参数键名查询参数值
|
||||||
export function getConfigKey(configKey) {
|
export function getConfigKey(configKey) {
|
||||||
return request({
|
const config = mockConfigs.find(c => c.configKey === configKey);
|
||||||
url: '/system/config/configKey/' + configKey,
|
return Promise.resolve(config ? config.configValue : '');
|
||||||
method: 'get'
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 新增参数配置
|
// 新增参数配置
|
||||||
export function addConfig(data) {
|
export function addConfig(data) {
|
||||||
return request({
|
const newConfig = {
|
||||||
url: '/system/config',
|
configId: mockConfigs.length + 1,
|
||||||
method: 'post',
|
...data,
|
||||||
data: data
|
createTime: new Date().toISOString().split('T')[0]
|
||||||
})
|
};
|
||||||
|
mockConfigs.push(newConfig);
|
||||||
|
return Promise.resolve({ code: 200, msg: '新增成功' });
|
||||||
}
|
}
|
||||||
|
|
||||||
// 修改参数配置
|
// 修改参数配置
|
||||||
export function updateConfig(data) {
|
export function updateConfig(data) {
|
||||||
return request({
|
const index = mockConfigs.findIndex(c => c.configId === data.configId);
|
||||||
url: '/system/config',
|
if (index !== -1) {
|
||||||
method: 'put',
|
mockConfigs[index] = { ...mockConfigs[index], ...data };
|
||||||
data: data
|
}
|
||||||
})
|
return Promise.resolve({ code: 200, msg: '修改成功' });
|
||||||
}
|
}
|
||||||
|
|
||||||
// 删除参数配置
|
// 删除参数配置
|
||||||
export function delConfig(configId) {
|
export function delConfig(configId) {
|
||||||
return request({
|
const index = mockConfigs.findIndex(c => c.configId === parseInt(configId));
|
||||||
url: '/system/config/' + configId,
|
if (index !== -1) {
|
||||||
method: 'delete'
|
mockConfigs.splice(index, 1);
|
||||||
})
|
}
|
||||||
|
return Promise.resolve({ code: 200, msg: '删除成功' });
|
||||||
}
|
}
|
||||||
|
|
||||||
// 刷新参数缓存
|
// 刷新参数缓存
|
||||||
export function refreshCache() {
|
export function refreshCache() {
|
||||||
return request({
|
return Promise.resolve({ code: 200, msg: '刷新成功' });
|
||||||
url: '/system/config/refreshCache',
|
|
||||||
method: 'delete'
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,52 +1,62 @@
|
||||||
import request from '@/utils/request'
|
// 模拟静态数据
|
||||||
|
const mockDepts = [
|
||||||
|
{ deptId: 1, parentId: 0, deptName: '总公司', orderNum: 1, leader: '张三', phone: '15888888888', status: '0', createTime: '2023-01-01' },
|
||||||
|
{ deptId: 2, parentId: 1, deptName: '研发部', orderNum: 1, leader: '李四', phone: '15666666666', status: '0', createTime: '2023-01-02' },
|
||||||
|
{ deptId: 3, parentId: 1, deptName: '市场部', orderNum: 2, leader: '王五', phone: '15555555555', status: '0', createTime: '2023-01-03' }
|
||||||
|
];
|
||||||
|
|
||||||
// 查询部门列表
|
// 查询部门列表
|
||||||
export function listDept(query) {
|
export function listDept(query) {
|
||||||
return request({
|
const { deptName, status } = query;
|
||||||
url: '/system/dept/list',
|
let filteredDepts = [...mockDepts];
|
||||||
method: 'get',
|
|
||||||
params: query
|
if (deptName) {
|
||||||
})
|
filteredDepts = filteredDepts.filter(dept => dept.deptName.includes(deptName));
|
||||||
|
}
|
||||||
|
if (status) {
|
||||||
|
filteredDepts = filteredDepts.filter(dept => dept.status === status);
|
||||||
|
}
|
||||||
|
|
||||||
|
return Promise.resolve(filteredDepts);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 查询部门列表(排除节点)
|
// 查询部门列表(排除节点)
|
||||||
export function listDeptExcludeChild(deptId) {
|
export function listDeptExcludeChild(deptId) {
|
||||||
return request({
|
const excludeDepts = mockDepts.filter(dept => dept.deptId !== parseInt(deptId));
|
||||||
url: '/system/dept/list/exclude/' + deptId,
|
return Promise.resolve(excludeDepts);
|
||||||
method: 'get'
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 查询部门详细
|
// 查询部门详细
|
||||||
export function getDept(deptId) {
|
export function getDept(deptId) {
|
||||||
return request({
|
const dept = mockDepts.find(d => d.deptId === parseInt(deptId));
|
||||||
url: '/system/dept/' + deptId,
|
return Promise.resolve(dept || {});
|
||||||
method: 'get'
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 新增部门
|
// 新增部门
|
||||||
export function addDept(data) {
|
export function addDept(data) {
|
||||||
return request({
|
const newDept = {
|
||||||
url: '/system/dept',
|
deptId: mockDepts.length + 1,
|
||||||
method: 'post',
|
...data,
|
||||||
data: data
|
createTime: new Date().toISOString().split('T')[0]
|
||||||
})
|
};
|
||||||
|
mockDepts.push(newDept);
|
||||||
|
return Promise.resolve({ code: 200, msg: '新增成功' });
|
||||||
}
|
}
|
||||||
|
|
||||||
// 修改部门
|
// 修改部门
|
||||||
export function updateDept(data) {
|
export function updateDept(data) {
|
||||||
return request({
|
const index = mockDepts.findIndex(d => d.deptId === data.deptId);
|
||||||
url: '/system/dept',
|
if (index !== -1) {
|
||||||
method: 'put',
|
mockDepts[index] = { ...mockDepts[index], ...data };
|
||||||
data: data
|
}
|
||||||
})
|
return Promise.resolve({ code: 200, msg: '修改成功' });
|
||||||
}
|
}
|
||||||
|
|
||||||
// 删除部门
|
// 删除部门
|
||||||
export function delDept(deptId) {
|
export function delDept(deptId) {
|
||||||
return request({
|
const index = mockDepts.findIndex(d => d.deptId === parseInt(deptId));
|
||||||
url: '/system/dept/' + deptId,
|
if (index !== -1) {
|
||||||
method: 'delete'
|
mockDepts.splice(index, 1);
|
||||||
})
|
}
|
||||||
|
return Promise.resolve({ code: 200, msg: '删除成功' });
|
||||||
}
|
}
|
||||||
|
|
@ -1,60 +1,87 @@
|
||||||
import request from '@/utils/request'
|
// 模拟静态数据
|
||||||
|
const mockMenus = [
|
||||||
|
{ menuId: 1, menuName: '系统管理', parentId: 0, orderNum: 1, path: 'system', component: 'Layout', status: '0' },
|
||||||
|
{ menuId: 2, menuName: '用户管理', parentId: 1, orderNum: 1, path: 'user', component: 'system/user/index', status: '0' },
|
||||||
|
{ menuId: 3, menuName: '角色管理', parentId: 1, orderNum: 2, path: 'role', component: 'system/role/index', status: '0' },
|
||||||
|
{ menuId: 4, menuName: '菜单管理', parentId: 1, orderNum: 3, path: 'menu', component: 'system/menu/index', status: '0' }
|
||||||
|
];
|
||||||
|
|
||||||
// 查询菜单列表
|
// 查询菜单列表
|
||||||
export function listMenu(query) {
|
export function listMenu(query) {
|
||||||
return request({
|
const { menuName, status } = query;
|
||||||
url: '/system/menu/list',
|
let filteredMenus = [...mockMenus];
|
||||||
method: 'get',
|
|
||||||
params: query
|
if (menuName) {
|
||||||
})
|
filteredMenus = filteredMenus.filter(menu => menu.menuName.includes(menuName));
|
||||||
|
}
|
||||||
|
if (status) {
|
||||||
|
filteredMenus = filteredMenus.filter(menu => menu.status === status);
|
||||||
|
}
|
||||||
|
|
||||||
|
return Promise.resolve(filteredMenus);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 查询菜单详细
|
// 查询菜单详细
|
||||||
export function getMenu(menuId) {
|
export function getMenu(menuId) {
|
||||||
return request({
|
const menu = mockMenus.find(m => m.menuId === parseInt(menuId));
|
||||||
url: '/system/menu/' + menuId,
|
return Promise.resolve(menu || {});
|
||||||
method: 'get'
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 查询菜单下拉树结构
|
// 查询菜单下拉树结构
|
||||||
export function treeselect() {
|
export function treeselect() {
|
||||||
return request({
|
const buildTree = (parentId) => {
|
||||||
url: '/system/menu/treeselect',
|
const children = mockMenus.filter(m => m.parentId === parentId);
|
||||||
method: 'get'
|
return children.map(child => ({
|
||||||
})
|
id: child.menuId,
|
||||||
|
label: child.menuName,
|
||||||
|
children: buildTree(child.menuId)
|
||||||
|
}));
|
||||||
|
};
|
||||||
|
|
||||||
|
return Promise.resolve(buildTree(0));
|
||||||
}
|
}
|
||||||
|
|
||||||
// 根据角色ID查询菜单下拉树结构
|
// 根据角色ID查询菜单下拉树结构
|
||||||
export function roleMenuTreeselect(roleId) {
|
export function roleMenuTreeselect(roleId) {
|
||||||
return request({
|
const buildTree = (parentId) => {
|
||||||
url: '/system/menu/roleMenuTreeselect/' + roleId,
|
const children = mockMenus.filter(m => m.parentId === parentId);
|
||||||
method: 'get'
|
return children.map(child => ({
|
||||||
})
|
id: child.menuId,
|
||||||
|
label: child.menuName,
|
||||||
|
children: buildTree(child.menuId)
|
||||||
|
}));
|
||||||
|
};
|
||||||
|
|
||||||
|
return Promise.resolve({
|
||||||
|
menus: buildTree(0),
|
||||||
|
checkedKeys: [1, 2, 3, 4] // 模拟已选中的菜单
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// 新增菜单
|
// 新增菜单
|
||||||
export function addMenu(data) {
|
export function addMenu(data) {
|
||||||
return request({
|
const newMenu = {
|
||||||
url: '/system/menu',
|
menuId: mockMenus.length + 1,
|
||||||
method: 'post',
|
...data
|
||||||
data: data
|
};
|
||||||
})
|
mockMenus.push(newMenu);
|
||||||
|
return Promise.resolve({ code: 200, msg: '新增成功' });
|
||||||
}
|
}
|
||||||
|
|
||||||
// 修改菜单
|
// 修改菜单
|
||||||
export function updateMenu(data) {
|
export function updateMenu(data) {
|
||||||
return request({
|
const index = mockMenus.findIndex(m => m.menuId === data.menuId);
|
||||||
url: '/system/menu',
|
if (index !== -1) {
|
||||||
method: 'put',
|
mockMenus[index] = { ...mockMenus[index], ...data };
|
||||||
data: data
|
}
|
||||||
})
|
return Promise.resolve({ code: 200, msg: '修改成功' });
|
||||||
}
|
}
|
||||||
|
|
||||||
// 删除菜单
|
// 删除菜单
|
||||||
export function delMenu(menuId) {
|
export function delMenu(menuId) {
|
||||||
return request({
|
const index = mockMenus.findIndex(m => m.menuId === parseInt(menuId));
|
||||||
url: '/system/menu/' + menuId,
|
if (index !== -1) {
|
||||||
method: 'delete'
|
mockMenus.splice(index, 1);
|
||||||
})
|
}
|
||||||
|
return Promise.resolve({ code: 200, msg: '删除成功' });
|
||||||
}
|
}
|
||||||
|
|
@ -1,44 +1,66 @@
|
||||||
import request from '@/utils/request'
|
// 模拟静态数据
|
||||||
|
const mockNotices = [
|
||||||
|
{ noticeId: 1, noticeTitle: '系统公告', noticeType: '1', status: '0', createTime: '2023-01-01' },
|
||||||
|
{ noticeId: 2, noticeTitle: '维护通知', noticeType: '2', status: '0', createTime: '2023-01-02' }
|
||||||
|
];
|
||||||
|
|
||||||
// 查询公告列表
|
// 查询公告列表
|
||||||
export function listNotice(query) {
|
export function listNotice(query) {
|
||||||
return request({
|
const { pageNum, pageSize, noticeTitle, noticeType, status } = query;
|
||||||
url: '/system/notice/list',
|
let filteredNotices = [...mockNotices];
|
||||||
method: 'get',
|
|
||||||
params: query
|
if (noticeTitle) {
|
||||||
})
|
filteredNotices = filteredNotices.filter(notice => notice.noticeTitle.includes(noticeTitle));
|
||||||
|
}
|
||||||
|
if (noticeType) {
|
||||||
|
filteredNotices = filteredNotices.filter(notice => notice.noticeType === noticeType);
|
||||||
|
}
|
||||||
|
if (status) {
|
||||||
|
filteredNotices = filteredNotices.filter(notice => notice.status === status);
|
||||||
|
}
|
||||||
|
|
||||||
|
const total = filteredNotices.length;
|
||||||
|
const start = (pageNum - 1) * pageSize;
|
||||||
|
const end = start + pageSize;
|
||||||
|
const list = filteredNotices.slice(start, end);
|
||||||
|
|
||||||
|
return Promise.resolve({
|
||||||
|
total,
|
||||||
|
rows: list
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// 查询公告详细
|
// 查询公告详细
|
||||||
export function getNotice(noticeId) {
|
export function getNotice(noticeId) {
|
||||||
return request({
|
const notice = mockNotices.find(n => n.noticeId === parseInt(noticeId));
|
||||||
url: '/system/notice/' + noticeId,
|
return Promise.resolve(notice || {});
|
||||||
method: 'get'
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 新增公告
|
// 新增公告
|
||||||
export function addNotice(data) {
|
export function addNotice(data) {
|
||||||
return request({
|
const newNotice = {
|
||||||
url: '/system/notice',
|
noticeId: mockNotices.length + 1,
|
||||||
method: 'post',
|
...data,
|
||||||
data: data
|
createTime: new Date().toISOString().split('T')[0]
|
||||||
})
|
};
|
||||||
|
mockNotices.push(newNotice);
|
||||||
|
return Promise.resolve({ code: 200, msg: '新增成功' });
|
||||||
}
|
}
|
||||||
|
|
||||||
// 修改公告
|
// 修改公告
|
||||||
export function updateNotice(data) {
|
export function updateNotice(data) {
|
||||||
return request({
|
const index = mockNotices.findIndex(n => n.noticeId === data.noticeId);
|
||||||
url: '/system/notice',
|
if (index !== -1) {
|
||||||
method: 'put',
|
mockNotices[index] = { ...mockNotices[index], ...data };
|
||||||
data: data
|
}
|
||||||
})
|
return Promise.resolve({ code: 200, msg: '修改成功' });
|
||||||
}
|
}
|
||||||
|
|
||||||
// 删除公告
|
// 删除公告
|
||||||
export function delNotice(noticeId) {
|
export function delNotice(noticeId) {
|
||||||
return request({
|
const index = mockNotices.findIndex(n => n.noticeId === parseInt(noticeId));
|
||||||
url: '/system/notice/' + noticeId,
|
if (index !== -1) {
|
||||||
method: 'delete'
|
mockNotices.splice(index, 1);
|
||||||
})
|
}
|
||||||
|
return Promise.resolve({ code: 200, msg: '删除成功' });
|
||||||
}
|
}
|
||||||
|
|
@ -1,44 +1,66 @@
|
||||||
import request from '@/utils/request'
|
// 模拟静态数据
|
||||||
|
const mockPosts = [
|
||||||
|
{ postId: 1, postName: '总经理', postCode: 'CEO', postSort: 1, status: '0', createTime: '2023-01-01' },
|
||||||
|
{ postId: 2, postName: '技术总监', postCode: 'CTO', postSort: 2, status: '0', createTime: '2023-01-02' }
|
||||||
|
];
|
||||||
|
|
||||||
// 查询岗位列表
|
// 查询岗位列表
|
||||||
export function listPost(query) {
|
export function listPost(query) {
|
||||||
return request({
|
const { pageNum, pageSize, postName, postCode, status } = query;
|
||||||
url: '/system/post/list',
|
let filteredPosts = [...mockPosts];
|
||||||
method: 'get',
|
|
||||||
params: query
|
if (postName) {
|
||||||
})
|
filteredPosts = filteredPosts.filter(post => post.postName.includes(postName));
|
||||||
|
}
|
||||||
|
if (postCode) {
|
||||||
|
filteredPosts = filteredPosts.filter(post => post.postCode.includes(postCode));
|
||||||
|
}
|
||||||
|
if (status) {
|
||||||
|
filteredPosts = filteredPosts.filter(post => post.status === status);
|
||||||
|
}
|
||||||
|
|
||||||
|
const total = filteredPosts.length;
|
||||||
|
const start = (pageNum - 1) * pageSize;
|
||||||
|
const end = start + pageSize;
|
||||||
|
const list = filteredPosts.slice(start, end);
|
||||||
|
|
||||||
|
return Promise.resolve({
|
||||||
|
total,
|
||||||
|
rows: list
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// 查询岗位详细
|
// 查询岗位详细
|
||||||
export function getPost(postId) {
|
export function getPost(postId) {
|
||||||
return request({
|
const post = mockPosts.find(p => p.postId === parseInt(postId));
|
||||||
url: '/system/post/' + postId,
|
return Promise.resolve(post || {});
|
||||||
method: 'get'
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 新增岗位
|
// 新增岗位
|
||||||
export function addPost(data) {
|
export function addPost(data) {
|
||||||
return request({
|
const newPost = {
|
||||||
url: '/system/post',
|
postId: mockPosts.length + 1,
|
||||||
method: 'post',
|
...data,
|
||||||
data: data
|
createTime: new Date().toISOString().split('T')[0]
|
||||||
})
|
};
|
||||||
|
mockPosts.push(newPost);
|
||||||
|
return Promise.resolve({ code: 200, msg: '新增成功' });
|
||||||
}
|
}
|
||||||
|
|
||||||
// 修改岗位
|
// 修改岗位
|
||||||
export function updatePost(data) {
|
export function updatePost(data) {
|
||||||
return request({
|
const index = mockPosts.findIndex(p => p.postId === data.postId);
|
||||||
url: '/system/post',
|
if (index !== -1) {
|
||||||
method: 'put',
|
mockPosts[index] = { ...mockPosts[index], ...data };
|
||||||
data: data
|
}
|
||||||
})
|
return Promise.resolve({ code: 200, msg: '修改成功' });
|
||||||
}
|
}
|
||||||
|
|
||||||
// 删除岗位
|
// 删除岗位
|
||||||
export function delPost(postId) {
|
export function delPost(postId) {
|
||||||
return request({
|
const index = mockPosts.findIndex(p => p.postId === parseInt(postId));
|
||||||
url: '/system/post/' + postId,
|
if (index !== -1) {
|
||||||
method: 'delete'
|
mockPosts.splice(index, 1);
|
||||||
})
|
}
|
||||||
|
return Promise.resolve({ code: 200, msg: '删除成功' });
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,119 +1,119 @@
|
||||||
import request from '@/utils/request'
|
// 模拟静态数据
|
||||||
|
const mockRoles = [
|
||||||
|
{ roleId: 1, roleName: '管理员', roleKey: 'admin', roleSort: 1, status: '0', createTime: '2023-01-01' },
|
||||||
|
{ roleId: 2, roleName: '普通角色', roleKey: 'common', roleSort: 2, status: '0', createTime: '2023-01-02' }
|
||||||
|
];
|
||||||
|
|
||||||
// 查询角色列表
|
// 查询角色列表
|
||||||
export function listRole(query) {
|
export function listRole(query) {
|
||||||
return request({
|
// 模拟分页
|
||||||
url: '/system/role/list',
|
const { pageNum, pageSize, roleName, roleKey, status } = query;
|
||||||
method: 'get',
|
let filteredRoles = [...mockRoles];
|
||||||
params: query
|
|
||||||
})
|
if (roleName) {
|
||||||
|
filteredRoles = filteredRoles.filter(role => role.roleName.includes(roleName));
|
||||||
|
}
|
||||||
|
if (roleKey) {
|
||||||
|
filteredRoles = filteredRoles.filter(role => role.roleKey.includes(roleKey));
|
||||||
|
}
|
||||||
|
if (status) {
|
||||||
|
filteredRoles = filteredRoles.filter(role => role.status === status);
|
||||||
|
}
|
||||||
|
|
||||||
|
const total = filteredRoles.length;
|
||||||
|
const start = (pageNum - 1) * pageSize;
|
||||||
|
const end = start + pageSize;
|
||||||
|
const list = filteredRoles.slice(start, end);
|
||||||
|
|
||||||
|
return Promise.resolve({
|
||||||
|
total,
|
||||||
|
rows: list
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// 查询角色详细
|
// 查询角色详细
|
||||||
export function getRole(roleId) {
|
export function getRole(roleId) {
|
||||||
return request({
|
const role = mockRoles.find(r => r.roleId === parseInt(roleId));
|
||||||
url: '/system/role/' + roleId,
|
return Promise.resolve(role || {});
|
||||||
method: 'get'
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 新增角色
|
// 新增角色
|
||||||
export function addRole(data) {
|
export function addRole(data) {
|
||||||
return request({
|
const newRole = {
|
||||||
url: '/system/role',
|
roleId: mockRoles.length + 1,
|
||||||
method: 'post',
|
...data,
|
||||||
data: data
|
createTime: new Date().toISOString().split('T')[0]
|
||||||
})
|
};
|
||||||
|
mockRoles.push(newRole);
|
||||||
|
return Promise.resolve({ code: 200, msg: '新增成功' });
|
||||||
}
|
}
|
||||||
|
|
||||||
// 修改角色
|
// 修改角色
|
||||||
export function updateRole(data) {
|
export function updateRole(data) {
|
||||||
return request({
|
const index = mockRoles.findIndex(r => r.roleId === data.roleId);
|
||||||
url: '/system/role',
|
if (index !== -1) {
|
||||||
method: 'put',
|
mockRoles[index] = { ...mockRoles[index], ...data };
|
||||||
data: data
|
}
|
||||||
})
|
return Promise.resolve({ code: 200, msg: '修改成功' });
|
||||||
}
|
}
|
||||||
|
|
||||||
// 角色数据权限
|
// 角色数据权限
|
||||||
export function dataScope(data) {
|
export function dataScope(data) {
|
||||||
return request({
|
return Promise.resolve({ code: 200, msg: '修改成功' });
|
||||||
url: '/system/role/dataScope',
|
|
||||||
method: 'put',
|
|
||||||
data: data
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 角色状态修改
|
// 角色状态修改
|
||||||
export function changeRoleStatus(roleId, status) {
|
export function changeRoleStatus(roleId, status) {
|
||||||
const data = {
|
const role = mockRoles.find(r => r.roleId === parseInt(roleId));
|
||||||
roleId,
|
if (role) {
|
||||||
status
|
role.status = status;
|
||||||
}
|
}
|
||||||
return request({
|
return Promise.resolve({ code: 200, msg: '修改成功' });
|
||||||
url: '/system/role/changeStatus',
|
|
||||||
method: 'put',
|
|
||||||
data: data
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 删除角色
|
// 删除角色
|
||||||
export function delRole(roleId) {
|
export function delRole(roleId) {
|
||||||
return request({
|
const index = mockRoles.findIndex(r => r.roleId === parseInt(roleId));
|
||||||
url: '/system/role/' + roleId,
|
if (index !== -1) {
|
||||||
method: 'delete'
|
mockRoles.splice(index, 1);
|
||||||
})
|
}
|
||||||
|
return Promise.resolve({ code: 200, msg: '删除成功' });
|
||||||
}
|
}
|
||||||
|
|
||||||
// 查询角色已授权用户列表
|
// 查询角色已授权用户列表
|
||||||
export function allocatedUserList(query) {
|
export function allocatedUserList(query) {
|
||||||
return request({
|
return Promise.resolve({
|
||||||
url: '/system/role/authUser/allocatedList',
|
total: 1,
|
||||||
method: 'get',
|
rows: [{ userId: 1, userName: 'admin', nickName: '管理员' }]
|
||||||
params: query
|
});
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 查询角色未授权用户列表
|
// 查询角色未授权用户列表
|
||||||
export function unallocatedUserList(query) {
|
export function unallocatedUserList(query) {
|
||||||
return request({
|
return Promise.resolve({
|
||||||
url: '/system/role/authUser/unallocatedList',
|
total: 1,
|
||||||
method: 'get',
|
rows: [{ userId: 2, userName: 'test', nickName: '测试用户' }]
|
||||||
params: query
|
});
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 取消用户授权角色
|
// 取消用户授权角色
|
||||||
export function authUserCancel(data) {
|
export function authUserCancel(data) {
|
||||||
return request({
|
return Promise.resolve({ code: 200, msg: '取消授权成功' });
|
||||||
url: '/system/role/authUser/cancel',
|
|
||||||
method: 'put',
|
|
||||||
data: data
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 批量取消用户授权角色
|
// 批量取消用户授权角色
|
||||||
export function authUserCancelAll(data) {
|
export function authUserCancelAll(data) {
|
||||||
return request({
|
return Promise.resolve({ code: 200, msg: '批量取消授权成功' });
|
||||||
url: '/system/role/authUser/cancelAll',
|
|
||||||
method: 'put',
|
|
||||||
params: data
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 授权用户选择
|
// 授权用户选择
|
||||||
export function authUserSelectAll(data) {
|
export function authUserSelectAll(data) {
|
||||||
return request({
|
return Promise.resolve({ code: 200, msg: '授权成功' });
|
||||||
url: '/system/role/authUser/selectAll',
|
|
||||||
method: 'put',
|
|
||||||
params: data
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 根据角色ID查询部门树结构
|
// 根据角色ID查询部门树结构
|
||||||
export function deptTreeSelect(roleId) {
|
export function deptTreeSelect(roleId) {
|
||||||
return request({
|
return Promise.resolve([
|
||||||
url: '/system/role/deptTree/' + roleId,
|
{ id: 1, label: '总公司', children: [{ id: 2, label: '研发部' }] }
|
||||||
method: 'get'
|
]);
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,136 +1,125 @@
|
||||||
import request from '@/utils/request'
|
// 模拟静态数据
|
||||||
import { parseStrEmpty } from "@/utils/ruoyi";
|
const mockUsers = [
|
||||||
|
{ userId: 1, userName: 'admin', nickName: '管理员', dept: { deptName: '总公司' }, phonenumber: '15888888888', status: '0', createTime: '2023-01-01' },
|
||||||
|
{ userId: 2, userName: 'test', nickName: '测试用户', dept: { deptName: '研发部' }, phonenumber: '15666666666', status: '0', createTime: '2023-01-02' }
|
||||||
|
];
|
||||||
|
|
||||||
// 查询用户列表
|
// 查询用户列表
|
||||||
export function listUser(query) {
|
export function listUser(query) {
|
||||||
return request({
|
// 模拟分页
|
||||||
url: '/system/user/list',
|
const { pageNum, pageSize, userName, phonenumber, status, deptId } = query;
|
||||||
method: 'get',
|
let filteredUsers = [...mockUsers];
|
||||||
params: query
|
|
||||||
})
|
if (userName) {
|
||||||
|
filteredUsers = filteredUsers.filter(user => user.userName.includes(userName));
|
||||||
|
}
|
||||||
|
if (phonenumber) {
|
||||||
|
filteredUsers = filteredUsers.filter(user => user.phonenumber.includes(phonenumber));
|
||||||
|
}
|
||||||
|
if (status) {
|
||||||
|
filteredUsers = filteredUsers.filter(user => user.status === status);
|
||||||
|
}
|
||||||
|
if (deptId) {
|
||||||
|
filteredUsers = filteredUsers.filter(user => user.dept.deptId === deptId);
|
||||||
|
}
|
||||||
|
|
||||||
|
const total = filteredUsers.length;
|
||||||
|
const start = (pageNum - 1) * pageSize;
|
||||||
|
const end = start + pageSize;
|
||||||
|
const list = filteredUsers.slice(start, end);
|
||||||
|
|
||||||
|
return Promise.resolve({
|
||||||
|
total,
|
||||||
|
rows: list
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// 查询用户详细
|
// 查询用户详细
|
||||||
export function getUser(userId) {
|
export function getUser(userId) {
|
||||||
return request({
|
const user = mockUsers.find(u => u.userId === parseInt(userId));
|
||||||
url: '/system/user/' + parseStrEmpty(userId),
|
return Promise.resolve(user || {});
|
||||||
method: 'get'
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 新增用户
|
// 新增用户
|
||||||
export function addUser(data) {
|
export function addUser(data) {
|
||||||
return request({
|
const newUser = {
|
||||||
url: '/system/user',
|
userId: mockUsers.length + 1,
|
||||||
method: 'post',
|
...data,
|
||||||
data: data
|
createTime: new Date().toISOString().split('T')[0]
|
||||||
})
|
};
|
||||||
|
mockUsers.push(newUser);
|
||||||
|
return Promise.resolve({ code: 200, msg: '新增成功' });
|
||||||
}
|
}
|
||||||
|
|
||||||
// 修改用户
|
// 修改用户
|
||||||
export function updateUser(data) {
|
export function updateUser(data) {
|
||||||
return request({
|
const index = mockUsers.findIndex(u => u.userId === data.userId);
|
||||||
url: '/system/user',
|
if (index !== -1) {
|
||||||
method: 'put',
|
mockUsers[index] = { ...mockUsers[index], ...data };
|
||||||
data: data
|
}
|
||||||
})
|
return Promise.resolve({ code: 200, msg: '修改成功' });
|
||||||
}
|
}
|
||||||
|
|
||||||
// 删除用户
|
// 删除用户
|
||||||
export function delUser(userId) {
|
export function delUser(userId) {
|
||||||
return request({
|
const index = mockUsers.findIndex(u => u.userId === parseInt(userId));
|
||||||
url: '/system/user/' + userId,
|
if (index !== -1) {
|
||||||
method: 'delete'
|
mockUsers.splice(index, 1);
|
||||||
})
|
}
|
||||||
|
return Promise.resolve({ code: 200, msg: '删除成功' });
|
||||||
}
|
}
|
||||||
|
|
||||||
// 用户密码重置
|
// 用户密码重置
|
||||||
export function resetUserPwd(userId, password) {
|
export function resetUserPwd(userId, password) {
|
||||||
const data = {
|
return Promise.resolve({ code: 200, msg: '重置成功' });
|
||||||
userId,
|
|
||||||
password
|
|
||||||
}
|
|
||||||
return request({
|
|
||||||
url: '/system/user/resetPwd',
|
|
||||||
method: 'put',
|
|
||||||
data: data
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 用户状态修改
|
// 用户状态修改
|
||||||
export function changeUserStatus(userId, status) {
|
export function changeUserStatus(userId, status) {
|
||||||
const data = {
|
const user = mockUsers.find(u => u.userId === parseInt(userId));
|
||||||
userId,
|
if (user) {
|
||||||
status
|
user.status = status;
|
||||||
}
|
}
|
||||||
return request({
|
return Promise.resolve({ code: 200, msg: '修改成功' });
|
||||||
url: '/system/user/changeStatus',
|
|
||||||
method: 'put',
|
|
||||||
data: data
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 查询用户个人信息
|
// 查询用户个人信息
|
||||||
export function getUserProfile() {
|
export function getUserProfile() {
|
||||||
return request({
|
return Promise.resolve(mockUsers[0]);
|
||||||
url: '/system/user/profile',
|
|
||||||
method: 'get'
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 修改用户个人信息
|
// 修改用户个人信息
|
||||||
export function updateUserProfile(data) {
|
export function updateUserProfile(data) {
|
||||||
return request({
|
Object.assign(mockUsers[0], data);
|
||||||
url: '/system/user/profile',
|
return Promise.resolve({ code: 200, msg: '修改成功' });
|
||||||
method: 'put',
|
|
||||||
data: data
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 用户密码重置
|
// 用户密码重置
|
||||||
export function updateUserPwd(oldPassword, newPassword) {
|
export function updateUserPwd(oldPassword, newPassword) {
|
||||||
const data = {
|
return Promise.resolve({ code: 200, msg: '修改成功' });
|
||||||
oldPassword,
|
|
||||||
newPassword
|
|
||||||
}
|
|
||||||
return request({
|
|
||||||
url: '/system/user/profile/updatePwd',
|
|
||||||
method: 'put',
|
|
||||||
data: data
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 用户头像上传
|
// 用户头像上传
|
||||||
export function uploadAvatar(data) {
|
export function uploadAvatar(data) {
|
||||||
return request({
|
return Promise.resolve({ code: 200, msg: '上传成功' });
|
||||||
url: '/system/user/profile/avatar',
|
|
||||||
method: 'post',
|
|
||||||
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
|
|
||||||
data: data
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 查询授权角色
|
// 查询授权角色
|
||||||
export function getAuthRole(userId) {
|
export function getAuthRole(userId) {
|
||||||
return request({
|
return Promise.resolve({
|
||||||
url: '/system/user/authRole/' + userId,
|
user: mockUsers.find(u => u.userId === parseInt(userId)),
|
||||||
method: 'get'
|
roles: [{ roleId: 1, roleName: '管理员' }]
|
||||||
})
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// 保存授权角色
|
// 保存授权角色
|
||||||
export function updateAuthRole(data) {
|
export function updateAuthRole(data) {
|
||||||
return request({
|
return Promise.resolve({ code: 200, msg: '授权成功' });
|
||||||
url: '/system/user/authRole',
|
|
||||||
method: 'put',
|
|
||||||
params: data
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 查询部门下拉树结构
|
// 查询部门下拉树结构
|
||||||
export function deptTreeSelect() {
|
export function deptTreeSelect() {
|
||||||
return request({
|
return Promise.resolve([
|
||||||
url: '/system/user/deptTree',
|
{ id: 1, label: '总公司', children: [{ id: 2, label: '研发部' }] }
|
||||||
method: 'get'
|
]);
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Binary file not shown.
|
After Width: | Height: | Size: 923 KiB |
|
|
@ -5,6 +5,7 @@
|
||||||
:action="uploadFileUrl"
|
:action="uploadFileUrl"
|
||||||
:before-upload="handleBeforeUpload"
|
:before-upload="handleBeforeUpload"
|
||||||
:file-list="fileList"
|
:file-list="fileList"
|
||||||
|
:data="data"
|
||||||
:limit="limit"
|
:limit="limit"
|
||||||
:on-error="handleUploadError"
|
:on-error="handleUploadError"
|
||||||
:on-exceed="handleExceed"
|
:on-exceed="handleExceed"
|
||||||
|
|
@ -44,6 +45,15 @@ import { getToken } from "@/utils/auth";
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
modelValue: [String, Object, Array],
|
modelValue: [String, Object, Array],
|
||||||
|
// 上传接口地址
|
||||||
|
action: {
|
||||||
|
type: String,
|
||||||
|
default: "/common/upload"
|
||||||
|
},
|
||||||
|
// 上传携带的参数
|
||||||
|
data: {
|
||||||
|
type: Object
|
||||||
|
},
|
||||||
// 数量限制
|
// 数量限制
|
||||||
limit: {
|
limit: {
|
||||||
type: Number,
|
type: Number,
|
||||||
|
|
@ -76,7 +86,7 @@ const emit = defineEmits();
|
||||||
const number = ref(0);
|
const number = ref(0);
|
||||||
const uploadList = ref([]);
|
const uploadList = ref([]);
|
||||||
const baseUrl = import.meta.env.VITE_APP_BASE_API;
|
const baseUrl = import.meta.env.VITE_APP_BASE_API;
|
||||||
const uploadFileUrl = ref(import.meta.env.VITE_APP_BASE_API + "/common/upload"); // 上传文件服务器地址
|
const uploadFileUrl = ref(import.meta.env.VITE_APP_BASE_API + props.action); // 上传文件服务器地址
|
||||||
const headers = ref({ Authorization: "Bearer " + getToken() });
|
const headers = ref({ Authorization: "Bearer " + getToken() });
|
||||||
const fileList = ref([]);
|
const fileList = ref([]);
|
||||||
const showTip = computed(
|
const showTip = computed(
|
||||||
|
|
@ -140,6 +150,7 @@ function handleExceed() {
|
||||||
// 上传失败
|
// 上传失败
|
||||||
function handleUploadError(err) {
|
function handleUploadError(err) {
|
||||||
proxy.$modal.msgError("上传文件失败");
|
proxy.$modal.msgError("上传文件失败");
|
||||||
|
proxy.$modal.closeLoading();
|
||||||
}
|
}
|
||||||
|
|
||||||
// 上传成功回调
|
// 上传成功回调
|
||||||
|
|
|
||||||
|
|
@ -1,19 +1,41 @@
|
||||||
<template>
|
<template>
|
||||||
<div :class="{ 'show': show }" class="header-search">
|
<div class="header-search">
|
||||||
<svg-icon class-name="search-icon" icon-class="search" @click.stop="click" />
|
<svg-icon class-name="search-icon" icon-class="search" @click.stop="click" />
|
||||||
<el-select
|
<el-dialog
|
||||||
ref="headerSearchSelectRef"
|
v-model="show"
|
||||||
v-model="search"
|
width="600"
|
||||||
:remote-method="querySearch"
|
@close="close"
|
||||||
filterable
|
:show-close="false"
|
||||||
default-first-option
|
append-to-body
|
||||||
remote
|
|
||||||
placeholder="Search"
|
|
||||||
class="header-search-select"
|
|
||||||
@change="change"
|
|
||||||
>
|
>
|
||||||
<el-option v-for="option in options" :key="option.item.path" :value="option.item" :label="option.item.title.join(' > ')" />
|
<el-input
|
||||||
</el-select>
|
v-model="search"
|
||||||
|
ref="headerSearchSelectRef"
|
||||||
|
size="large"
|
||||||
|
@input="querySearch"
|
||||||
|
prefix-icon="Search"
|
||||||
|
placeholder="菜单搜索,支持标题、URL模糊查询"
|
||||||
|
>
|
||||||
|
</el-input>
|
||||||
|
|
||||||
|
<div class="result-wrap">
|
||||||
|
<el-scrollbar>
|
||||||
|
<div class="search-item" tabindex="1" v-for="item in options" :key="item.path">
|
||||||
|
<div class="left">
|
||||||
|
<svg-icon class="menu-icon" :icon-class="item.icon" />
|
||||||
|
</div>
|
||||||
|
<div class="search-info" @click="change(item)">
|
||||||
|
<div class="menu-title">
|
||||||
|
{{ item.title.join(" / ") }}
|
||||||
|
</div>
|
||||||
|
<div class="menu-path">
|
||||||
|
{{ item.path }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</el-scrollbar>
|
||||||
|
</div>
|
||||||
|
</el-dialog>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
|
|
@ -23,36 +45,40 @@ import { getNormalPath } from '@/utils/ruoyi'
|
||||||
import { isHttp } from '@/utils/validate'
|
import { isHttp } from '@/utils/validate'
|
||||||
import usePermissionStore from '@/store/modules/permission'
|
import usePermissionStore from '@/store/modules/permission'
|
||||||
|
|
||||||
const search = ref('');
|
const search = ref('')
|
||||||
const options = ref([]);
|
const options = ref([])
|
||||||
const searchPool = ref([]);
|
const searchPool = ref([])
|
||||||
const show = ref(false);
|
const show = ref(false)
|
||||||
const fuse = ref(undefined);
|
const fuse = ref(undefined)
|
||||||
const headerSearchSelectRef = ref(null);
|
const headerSearchSelectRef = ref(null)
|
||||||
const router = useRouter();
|
const router = useRouter()
|
||||||
const routes = computed(() => usePermissionStore().defaultRoutes);
|
const routes = computed(() => usePermissionStore().defaultRoutes)
|
||||||
|
|
||||||
function click() {
|
function click() {
|
||||||
show.value = !show.value
|
show.value = !show.value
|
||||||
if (show.value) {
|
if (show.value) {
|
||||||
headerSearchSelectRef.value && headerSearchSelectRef.value.focus()
|
headerSearchSelectRef.value && headerSearchSelectRef.value.focus()
|
||||||
|
options.value = searchPool.value
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
|
|
||||||
function close() {
|
function close() {
|
||||||
headerSearchSelectRef.value && headerSearchSelectRef.value.blur()
|
headerSearchSelectRef.value && headerSearchSelectRef.value.blur()
|
||||||
|
search.value = ''
|
||||||
options.value = []
|
options.value = []
|
||||||
show.value = false
|
show.value = false
|
||||||
}
|
}
|
||||||
|
|
||||||
function change(val) {
|
function change(val) {
|
||||||
const path = val.path;
|
const path = val.path
|
||||||
const query = val.query;
|
const query = val.query
|
||||||
if (isHttp(path)) {
|
if (isHttp(path)) {
|
||||||
// http(s):// 路径新窗口打开
|
// http(s):// 路径新窗口打开
|
||||||
const pindex = path.indexOf("http");
|
const pindex = path.indexOf("http")
|
||||||
window.open(path.substr(pindex, path.length), "_blank");
|
window.open(path.substr(pindex, path.length), "_blank")
|
||||||
} else {
|
} else {
|
||||||
if (query) {
|
if (query) {
|
||||||
router.push({ path: path, query: JSON.parse(query) });
|
router.push({ path: path, query: JSON.parse(query) })
|
||||||
} else {
|
} else {
|
||||||
router.push(path)
|
router.push(path)
|
||||||
}
|
}
|
||||||
|
|
@ -64,6 +90,7 @@ function change(val) {
|
||||||
show.value = false
|
show.value = false
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
function initFuse(list) {
|
function initFuse(list) {
|
||||||
fuse.value = new Fuse(list, {
|
fuse.value = new Fuse(list, {
|
||||||
shouldSort: true,
|
shouldSort: true,
|
||||||
|
|
@ -80,6 +107,7 @@ function initFuse(list) {
|
||||||
}]
|
}]
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// Filter out the routes that can be displayed in the sidebar
|
// Filter out the routes that can be displayed in the sidebar
|
||||||
// And generate the internationalized title
|
// And generate the internationalized title
|
||||||
function generateRoutes(routes, basePath = '', prefixTitle = []) {
|
function generateRoutes(routes, basePath = '', prefixTitle = []) {
|
||||||
|
|
@ -88,16 +116,17 @@ function generateRoutes(routes, basePath = '', prefixTitle = []) {
|
||||||
for (const r of routes) {
|
for (const r of routes) {
|
||||||
// skip hidden router
|
// skip hidden router
|
||||||
if (r.hidden) { continue }
|
if (r.hidden) { continue }
|
||||||
const p = r.path.length > 0 && r.path[0] === '/' ? r.path : '/' + r.path;
|
const p = r.path.length > 0 && r.path[0] === '/' ? r.path : '/' + r.path
|
||||||
const data = {
|
const data = {
|
||||||
path: !isHttp(r.path) ? getNormalPath(basePath + p) : r.path,
|
path: !isHttp(r.path) ? getNormalPath(basePath + p) : r.path,
|
||||||
title: [...prefixTitle]
|
title: [...prefixTitle],
|
||||||
|
icon: ''
|
||||||
}
|
}
|
||||||
|
|
||||||
if (r.meta && r.meta.title) {
|
if (r.meta && r.meta.title) {
|
||||||
data.title = [...data.title, r.meta.title]
|
data.title = [...data.title, r.meta.title]
|
||||||
|
data.icon = r.meta.icon
|
||||||
if (r.redirect !== 'noRedirect') {
|
if (r.redirect !== "noRedirect") {
|
||||||
// only push the routes with title
|
// only push the routes with title
|
||||||
// special case: need to exclude parent router without redirect
|
// special case: need to exclude parent router without redirect
|
||||||
res.push(data)
|
res.push(data)
|
||||||
|
|
@ -117,30 +146,19 @@ function generateRoutes(routes, basePath = '', prefixTitle = []) {
|
||||||
}
|
}
|
||||||
return res
|
return res
|
||||||
}
|
}
|
||||||
|
|
||||||
function querySearch(query) {
|
function querySearch(query) {
|
||||||
if (query !== '') {
|
if (query !== '') {
|
||||||
options.value = fuse.value.search(query)
|
options.value = fuse.value.search(query).map((item) => item.item) ?? searchPool.value
|
||||||
} else {
|
} else {
|
||||||
options.value = []
|
options.value = searchPool.value
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
searchPool.value = generateRoutes(routes.value);
|
|
||||||
})
|
|
||||||
|
|
||||||
watchEffect(() => {
|
|
||||||
searchPool.value = generateRoutes(routes.value)
|
searchPool.value = generateRoutes(routes.value)
|
||||||
})
|
})
|
||||||
|
|
||||||
watch(show, (value) => {
|
|
||||||
if (value) {
|
|
||||||
document.body.addEventListener('click', close)
|
|
||||||
} else {
|
|
||||||
document.body.removeEventListener('click', close)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
watch(searchPool, (list) => {
|
watch(searchPool, (list) => {
|
||||||
initFuse(list)
|
initFuse(list)
|
||||||
})
|
})
|
||||||
|
|
@ -148,40 +166,52 @@ watch(searchPool, (list) => {
|
||||||
|
|
||||||
<style lang='scss' scoped>
|
<style lang='scss' scoped>
|
||||||
.header-search {
|
.header-search {
|
||||||
font-size: 0 !important;
|
|
||||||
|
|
||||||
.search-icon {
|
.search-icon {
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
font-size: 18px;
|
font-size: 18px;
|
||||||
vertical-align: middle;
|
vertical-align: middle;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.header-search-select {
|
.result-wrap {
|
||||||
font-size: 18px;
|
height: 280px;
|
||||||
transition: width 0.2s;
|
margin: 10px 0;
|
||||||
width: 0;
|
|
||||||
overflow: hidden;
|
|
||||||
background: transparent;
|
|
||||||
border-radius: 0;
|
|
||||||
display: inline-block;
|
|
||||||
vertical-align: middle;
|
|
||||||
|
|
||||||
:deep(.el-input__inner) {
|
.search-item {
|
||||||
border-radius: 0;
|
display: flex;
|
||||||
border: 0;
|
height: 48px;
|
||||||
padding-left: 0;
|
|
||||||
padding-right: 0;
|
.left {
|
||||||
box-shadow: none !important;
|
width: 60px;
|
||||||
border-bottom: 1px solid #d9d9d9;
|
text-align: center;
|
||||||
vertical-align: middle;
|
|
||||||
|
.menu-icon {
|
||||||
|
width: 18px;
|
||||||
|
height: 18px;
|
||||||
|
margin-top: 5px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.search-info {
|
||||||
|
padding-left: 5px;
|
||||||
|
width: 100%;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
justify-content: flex-start;
|
||||||
|
|
||||||
|
.menu-title,
|
||||||
|
.menu-path {
|
||||||
|
height: 20px;
|
||||||
|
}
|
||||||
|
.menu-path {
|
||||||
|
color: #ccc;
|
||||||
|
font-size: 10px;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
&.show {
|
.search-item:hover {
|
||||||
.header-search-select {
|
cursor: pointer;
|
||||||
width: 210px;
|
|
||||||
margin-left: 10px;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,7 @@
|
||||||
list-type="picture-card"
|
list-type="picture-card"
|
||||||
:on-success="handleUploadSuccess"
|
:on-success="handleUploadSuccess"
|
||||||
:before-upload="handleBeforeUpload"
|
:before-upload="handleBeforeUpload"
|
||||||
|
:data="data"
|
||||||
:limit="limit"
|
:limit="limit"
|
||||||
:on-error="handleUploadError"
|
:on-error="handleUploadError"
|
||||||
:on-exceed="handleExceed"
|
:on-exceed="handleExceed"
|
||||||
|
|
@ -51,6 +52,15 @@ import { isExternal } from "@/utils/validate";
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
modelValue: [String, Object, Array],
|
modelValue: [String, Object, Array],
|
||||||
|
// 上传接口地址
|
||||||
|
action: {
|
||||||
|
type: String,
|
||||||
|
default: "/common/upload"
|
||||||
|
},
|
||||||
|
// 上传携带的参数
|
||||||
|
data: {
|
||||||
|
type: Object
|
||||||
|
},
|
||||||
// 图片数量限制
|
// 图片数量限制
|
||||||
limit: {
|
limit: {
|
||||||
type: Number,
|
type: Number,
|
||||||
|
|
@ -80,7 +90,7 @@ const uploadList = ref([]);
|
||||||
const dialogImageUrl = ref("");
|
const dialogImageUrl = ref("");
|
||||||
const dialogVisible = ref(false);
|
const dialogVisible = ref(false);
|
||||||
const baseUrl = import.meta.env.VITE_APP_BASE_API;
|
const baseUrl = import.meta.env.VITE_APP_BASE_API;
|
||||||
const uploadImgUrl = ref(import.meta.env.VITE_APP_BASE_API + "/common/upload"); // 上传的图片服务器地址
|
const uploadImgUrl = ref(import.meta.env.VITE_APP_BASE_API + props.action); // 上传的图片服务器地址
|
||||||
const headers = ref({ Authorization: "Bearer " + getToken() });
|
const headers = ref({ Authorization: "Bearer " + getToken() });
|
||||||
const fileList = ref([]);
|
const fileList = ref([]);
|
||||||
const showTip = computed(
|
const showTip = computed(
|
||||||
|
|
|
||||||
|
|
@ -13,9 +13,14 @@
|
||||||
<el-button circle icon="Menu" />
|
<el-button circle icon="Menu" />
|
||||||
<template #dropdown>
|
<template #dropdown>
|
||||||
<el-dropdown-menu>
|
<el-dropdown-menu>
|
||||||
|
<!-- 全选/反选 按钮 -->
|
||||||
|
<el-dropdown-item>
|
||||||
|
<el-checkbox :indeterminate="isIndeterminate" v-model="isChecked" @change="toggleCheckAll"> 列展示 </el-checkbox>
|
||||||
|
</el-dropdown-item>
|
||||||
|
<div class="check-line"></div>
|
||||||
<template v-for="item in columns" :key="item.key">
|
<template v-for="item in columns" :key="item.key">
|
||||||
<el-dropdown-item>
|
<el-dropdown-item>
|
||||||
<el-checkbox :checked="item.visible" @change="checkboxChange($event, item.label)" :label="item.label" />
|
<el-checkbox v-model="item.visible" @change="checkboxChange($event, item.label)" :label="item.label" />
|
||||||
</el-dropdown-item>
|
</el-dropdown-item>
|
||||||
</template>
|
</template>
|
||||||
</el-dropdown-menu>
|
</el-dropdown-menu>
|
||||||
|
|
@ -39,83 +44,95 @@ const props = defineProps({
|
||||||
/* 是否显示检索条件 */
|
/* 是否显示检索条件 */
|
||||||
showSearch: {
|
showSearch: {
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
default: true,
|
default: true
|
||||||
},
|
},
|
||||||
/* 显隐列信息 */
|
/* 显隐列信息 */
|
||||||
columns: {
|
columns: {
|
||||||
type: Array,
|
type: Array
|
||||||
},
|
},
|
||||||
/* 是否显示检索图标 */
|
/* 是否显示检索图标 */
|
||||||
search: {
|
search: {
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
default: true,
|
default: true
|
||||||
},
|
},
|
||||||
/* 显隐列类型(transfer穿梭框、checkbox复选框) */
|
/* 显隐列类型(transfer穿梭框、checkbox复选框) */
|
||||||
showColumnsType: {
|
showColumnsType: {
|
||||||
type: String,
|
type: String,
|
||||||
default: "checkbox",
|
default: "checkbox"
|
||||||
},
|
},
|
||||||
/* 右外边距 */
|
/* 右外边距 */
|
||||||
gutter: {
|
gutter: {
|
||||||
type: Number,
|
type: Number,
|
||||||
default: 10,
|
default: 10
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
const emits = defineEmits(['update:showSearch', 'queryTable']);
|
const emits = defineEmits(['update:showSearch', 'queryTable'])
|
||||||
|
|
||||||
// 显隐数据
|
// 显隐数据
|
||||||
const value = ref([]);
|
const value = ref([])
|
||||||
// 弹出层标题
|
// 弹出层标题
|
||||||
const title = ref("显示/隐藏");
|
const title = ref("显示/隐藏")
|
||||||
// 是否显示弹出层
|
// 是否显示弹出层
|
||||||
const open = ref(false);
|
const open = ref(false)
|
||||||
|
|
||||||
const style = computed(() => {
|
const style = computed(() => {
|
||||||
const ret = {};
|
const ret = {}
|
||||||
if (props.gutter) {
|
if (props.gutter) {
|
||||||
ret.marginRight = `${props.gutter / 2}px`;
|
ret.marginRight = `${props.gutter / 2}px`
|
||||||
}
|
}
|
||||||
return ret;
|
return ret
|
||||||
});
|
})
|
||||||
|
|
||||||
|
// 是否全选/半选 状态
|
||||||
|
const isChecked = computed({
|
||||||
|
get: () => props.columns.every(col => col.visible),
|
||||||
|
set: () => {}
|
||||||
|
})
|
||||||
|
const isIndeterminate = computed(() => props.columns.some((col) => col.visible) && !isChecked.value)
|
||||||
|
|
||||||
// 搜索
|
// 搜索
|
||||||
function toggleSearch() {
|
function toggleSearch() {
|
||||||
emits("update:showSearch", !props.showSearch);
|
emits("update:showSearch", !props.showSearch)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 刷新
|
// 刷新
|
||||||
function refresh() {
|
function refresh() {
|
||||||
emits("queryTable");
|
emits("queryTable")
|
||||||
}
|
}
|
||||||
|
|
||||||
// 右侧列表元素变化
|
// 右侧列表元素变化
|
||||||
function dataChange(data) {
|
function dataChange(data) {
|
||||||
for (let item in props.columns) {
|
for (let item in props.columns) {
|
||||||
const key = props.columns[item].key;
|
const key = props.columns[item].key
|
||||||
props.columns[item].visible = !data.includes(key);
|
props.columns[item].visible = !data.includes(key)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 打开显隐列dialog
|
// 打开显隐列dialog
|
||||||
function showColumn() {
|
function showColumn() {
|
||||||
open.value = true;
|
open.value = true
|
||||||
}
|
}
|
||||||
|
|
||||||
if (props.showColumnsType == 'transfer') {
|
if (props.showColumnsType == 'transfer') {
|
||||||
// 显隐列初始默认隐藏列
|
// 显隐列初始默认隐藏列
|
||||||
for (let item in props.columns) {
|
for (let item in props.columns) {
|
||||||
if (props.columns[item].visible === false) {
|
if (props.columns[item].visible === false) {
|
||||||
value.value.push(parseInt(item));
|
value.value.push(parseInt(item))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 勾选
|
// 单勾选
|
||||||
function checkboxChange(event, label) {
|
function checkboxChange(event, label) {
|
||||||
props.columns.filter(item => item.label == label)[0].visible = event;
|
props.columns.filter(item => item.label == label)[0].visible = event
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 切换全选/反选
|
||||||
|
function toggleCheckAll() {
|
||||||
|
const newValue = !isChecked.value
|
||||||
|
props.columns.forEach((col) => (col.visible = newValue))
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang='scss' scoped>
|
<style lang='scss' scoped>
|
||||||
|
|
@ -131,4 +148,10 @@ function checkboxChange(event, label) {
|
||||||
line-height: 30px;
|
line-height: 30px;
|
||||||
padding: 0 17px;
|
padding: 0 17px;
|
||||||
}
|
}
|
||||||
|
.check-line {
|
||||||
|
width: 90%;
|
||||||
|
height: 1px;
|
||||||
|
background-color: #ccc;
|
||||||
|
margin: 3px auto;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,12 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="sidebar-logo-container" :class="{ 'collapse': collapse }">
|
<div class="sidebar-logo-container" :class="{ collapse: collapse }">
|
||||||
<transition name="sidebarLogoFade">
|
<transition name="sidebarLogoFade">
|
||||||
<router-link v-if="collapse" key="collapse" class="sidebar-logo-link" to="/">
|
<router-link
|
||||||
|
v-if="collapse"
|
||||||
|
key="collapse"
|
||||||
|
class="sidebar-logo-link"
|
||||||
|
to="/"
|
||||||
|
>
|
||||||
<img v-if="logo" :src="logo" class="sidebar-logo" />
|
<img v-if="logo" :src="logo" class="sidebar-logo" />
|
||||||
<h1 v-else class="sidebar-title">{{ title }}</h1>
|
<h1 v-else class="sidebar-title">{{ title }}</h1>
|
||||||
</router-link>
|
</router-link>
|
||||||
|
|
@ -14,16 +19,16 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
import logo from '@/assets/logo/logo.png'
|
import logo from "@/assets/logo/logo.png";
|
||||||
import useSettingsStore from '@/store/modules/settings'
|
import useSettingsStore from "@/store/modules/settings";
|
||||||
import variables from '@/assets/styles/variables.module.scss'
|
import variables from "@/assets/styles/variables.module.scss";
|
||||||
|
|
||||||
defineProps({
|
defineProps({
|
||||||
collapse: {
|
collapse: {
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
required: true
|
required: true,
|
||||||
}
|
},
|
||||||
})
|
});
|
||||||
|
|
||||||
const title = import.meta.env.VITE_APP_TITLE;
|
const title = import.meta.env.VITE_APP_TITLE;
|
||||||
const settingsStore = useSettingsStore();
|
const settingsStore = useSettingsStore();
|
||||||
|
|
@ -32,22 +37,24 @@ const sideTheme = computed(() => settingsStore.sideTheme);
|
||||||
// 获取Logo背景色
|
// 获取Logo背景色
|
||||||
const getLogoBackground = computed(() => {
|
const getLogoBackground = computed(() => {
|
||||||
if (settingsStore.isDark) {
|
if (settingsStore.isDark) {
|
||||||
return 'var(--sidebar-bg)';
|
return "var(--sidebar-bg)";
|
||||||
}
|
}
|
||||||
return sideTheme.value === 'theme-dark' ? variables.menuBg : variables.menuLightBg;
|
return sideTheme.value === "theme-dark"
|
||||||
|
? variables.menuBg
|
||||||
|
: variables.menuLightBg;
|
||||||
});
|
});
|
||||||
|
|
||||||
// 获取Logo文字颜色
|
// 获取Logo文字颜色
|
||||||
const getLogoTextColor = computed(() => {
|
const getLogoTextColor = computed(() => {
|
||||||
if (settingsStore.isDark) {
|
if (settingsStore.isDark) {
|
||||||
return 'var(--sidebar-text)';
|
return "var(--sidebar-text)";
|
||||||
}
|
}
|
||||||
return sideTheme.value === 'theme-dark' ? '#fff' : variables.menuLightText;
|
return sideTheme.value === "theme-dark" ? "#fff" : variables.menuLightText;
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
@import '@/assets/styles/variables.module.scss';
|
@import "@/assets/styles/variables.module.scss";
|
||||||
|
|
||||||
.sidebarLogoFade-enter-active {
|
.sidebarLogoFade-enter-active {
|
||||||
transition: opacity 1.5s;
|
transition: opacity 1.5s;
|
||||||
|
|
|
||||||
|
|
@ -21,6 +21,7 @@ import defaultSettings from '@/settings'
|
||||||
|
|
||||||
import useAppStore from '@/store/modules/app'
|
import useAppStore from '@/store/modules/app'
|
||||||
import useSettingsStore from '@/store/modules/settings'
|
import useSettingsStore from '@/store/modules/settings'
|
||||||
|
import usePermissionStore from '@/store/modules/permission'
|
||||||
|
|
||||||
const settingsStore = useSettingsStore()
|
const settingsStore = useSettingsStore()
|
||||||
const theme = computed(() => settingsStore.theme);
|
const theme = computed(() => settingsStore.theme);
|
||||||
|
|
@ -40,6 +41,8 @@ const classObj = computed(() => ({
|
||||||
const { width, height } = useWindowSize();
|
const { width, height } = useWindowSize();
|
||||||
const WIDTH = 992; // refer to Bootstrap's responsive design
|
const WIDTH = 992; // refer to Bootstrap's responsive design
|
||||||
|
|
||||||
|
const permissionStore = usePermissionStore()
|
||||||
|
|
||||||
watch(() => device.value, () => {
|
watch(() => device.value, () => {
|
||||||
if (device.value === 'mobile' && sidebar.value.opened) {
|
if (device.value === 'mobile' && sidebar.value.opened) {
|
||||||
useAppStore().closeSideBar({ withoutAnimation: false })
|
useAppStore().closeSideBar({ withoutAnimation: false })
|
||||||
|
|
@ -63,6 +66,10 @@ const settingRef = ref(null);
|
||||||
function setLayout() {
|
function setLayout() {
|
||||||
settingRef.value.openSetting();
|
settingRef.value.openSetting();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
permissionStore.generateRoutes()
|
||||||
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
|
|
|
||||||
|
|
@ -7,7 +7,6 @@ import { isHttp, isPathMatch } from '@/utils/validate'
|
||||||
import { isRelogin } from '@/utils/request'
|
import { isRelogin } from '@/utils/request'
|
||||||
import useUserStore from '@/store/modules/user'
|
import useUserStore from '@/store/modules/user'
|
||||||
import useSettingsStore from '@/store/modules/settings'
|
import useSettingsStore from '@/store/modules/settings'
|
||||||
import usePermissionStore from '@/store/modules/permission'
|
|
||||||
|
|
||||||
NProgress.configure({ showSpinner: false })
|
NProgress.configure({ showSpinner: false })
|
||||||
|
|
||||||
|
|
@ -33,15 +32,8 @@ router.beforeEach((to, from, next) => {
|
||||||
// 判断当前用户是否已拉取完user_info信息
|
// 判断当前用户是否已拉取完user_info信息
|
||||||
useUserStore().getInfo().then(() => {
|
useUserStore().getInfo().then(() => {
|
||||||
isRelogin.show = false
|
isRelogin.show = false
|
||||||
usePermissionStore().generateRoutes().then(accessRoutes => {
|
// 不再动态加载路由,直接使用静态路由
|
||||||
// 根据roles权限生成可访问的路由表
|
next({ ...to, replace: true })
|
||||||
accessRoutes.forEach(route => {
|
|
||||||
if (!isHttp(route.path)) {
|
|
||||||
router.addRoute(route) // 动态添加可访问路由表
|
|
||||||
}
|
|
||||||
})
|
|
||||||
next({ ...to, replace: true }) // hack方法 确保addRoutes已完成
|
|
||||||
})
|
|
||||||
}).catch(err => {
|
}).catch(err => {
|
||||||
useUserStore().logOut().then(() => {
|
useUserStore().logOut().then(() => {
|
||||||
ElMessage.error(err)
|
ElMessage.error(err)
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
import { createWebHistory, createRouter } from 'vue-router'
|
import { createWebHistory, createRouter } from 'vue-router'
|
||||||
/* Layout */
|
/* Layout */
|
||||||
import Layout from '@/layout'
|
import Layout from '@/layout/index.vue'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Note: 路由配置项
|
* Note: 路由配置项
|
||||||
|
|
@ -39,22 +39,22 @@ export const constantRoutes = [
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: '/login',
|
path: '/login',
|
||||||
component: () => import('@/views/login'),
|
component: () => import('@/views/login.vue'),
|
||||||
hidden: true
|
hidden: true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: '/register',
|
path: '/register',
|
||||||
component: () => import('@/views/register'),
|
component: () => import('@/views/register.vue'),
|
||||||
hidden: true
|
hidden: true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: "/:pathMatch(.*)*",
|
path: "/:pathMatch(.*)*",
|
||||||
component: () => import('@/views/error/404'),
|
component: () => import('@/views/error/404.vue'),
|
||||||
hidden: true
|
hidden: true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: '/401',
|
path: '/401',
|
||||||
component: () => import('@/views/error/401'),
|
component: () => import('@/views/error/401.vue'),
|
||||||
hidden: true
|
hidden: true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
@ -64,7 +64,7 @@ export const constantRoutes = [
|
||||||
children: [
|
children: [
|
||||||
{
|
{
|
||||||
path: '/index',
|
path: '/index',
|
||||||
component: () => import('@/views/index'),
|
component: () => import('@/views/index.vue'),
|
||||||
name: 'Index',
|
name: 'Index',
|
||||||
meta: { title: '首页', icon: 'dashboard', affix: true }
|
meta: { title: '首页', icon: 'dashboard', affix: true }
|
||||||
}
|
}
|
||||||
|
|
@ -74,20 +74,40 @@ export const constantRoutes = [
|
||||||
path: '/user',
|
path: '/user',
|
||||||
component: Layout,
|
component: Layout,
|
||||||
hidden: true,
|
hidden: true,
|
||||||
redirect: 'noredirect',
|
redirect: 'noRedirect',
|
||||||
|
name: 'UserCenter',
|
||||||
|
meta: { title: '个人中心', icon: 'user' },
|
||||||
children: [
|
children: [
|
||||||
{
|
{
|
||||||
path: 'profile',
|
path: 'profile',
|
||||||
component: () => import('@/views/system/user/profile/index'),
|
component: () => import('@/views/user/profile/index.vue'),
|
||||||
name: 'Profile',
|
name: 'Profile',
|
||||||
meta: { title: '个人中心', icon: 'user' }
|
meta: { title: '个人中心', icon: 'user' }
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
},
|
||||||
]
|
{
|
||||||
|
path: '/order',
|
||||||
// 动态路由,基于用户权限动态去加载
|
component: Layout,
|
||||||
export const dynamicRoutes = [
|
hidden: false,
|
||||||
|
name: 'Order',
|
||||||
|
meta: { title: '订单管理', icon: 'shopping' },
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
path: 'intention',
|
||||||
|
component: () => import('@/views/order/intention/index.vue'),
|
||||||
|
name: 'Intention',
|
||||||
|
meta: { title: '意向订单', icon: 'form' }
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: 'create',
|
||||||
|
component: () => import('@/views/order/intention/create.vue'),
|
||||||
|
name: 'IntentionCreate',
|
||||||
|
meta: { title: '创建意向订单', icon: 'edit', hidden: true }
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
// 以下为原动态路由,现在直接合并到静态路由中
|
||||||
{
|
{
|
||||||
path: '/system/user-auth',
|
path: '/system/user-auth',
|
||||||
component: Layout,
|
component: Layout,
|
||||||
|
|
@ -96,7 +116,7 @@ export const dynamicRoutes = [
|
||||||
children: [
|
children: [
|
||||||
{
|
{
|
||||||
path: 'role/:userId(\\d+)',
|
path: 'role/:userId(\\d+)',
|
||||||
component: () => import('@/views/system/user/authRole'),
|
component: () => import('@/views/system/user/authRole.vue'),
|
||||||
name: 'AuthRole',
|
name: 'AuthRole',
|
||||||
meta: { title: '分配角色', activeMenu: '/system/user' }
|
meta: { title: '分配角色', activeMenu: '/system/user' }
|
||||||
}
|
}
|
||||||
|
|
@ -110,7 +130,7 @@ export const dynamicRoutes = [
|
||||||
children: [
|
children: [
|
||||||
{
|
{
|
||||||
path: 'user/:roleId(\\d+)',
|
path: 'user/:roleId(\\d+)',
|
||||||
component: () => import('@/views/system/role/authUser'),
|
component: () => import('@/views/system/role/authUser.vue'),
|
||||||
name: 'AuthUser',
|
name: 'AuthUser',
|
||||||
meta: { title: '分配用户', activeMenu: '/system/role' }
|
meta: { title: '分配用户', activeMenu: '/system/role' }
|
||||||
}
|
}
|
||||||
|
|
@ -124,7 +144,7 @@ export const dynamicRoutes = [
|
||||||
children: [
|
children: [
|
||||||
{
|
{
|
||||||
path: 'index/:dictId(\\d+)',
|
path: 'index/:dictId(\\d+)',
|
||||||
component: () => import('@/views/system/dict/data'),
|
component: () => import('@/views/system/dict/data.vue'),
|
||||||
name: 'Data',
|
name: 'Data',
|
||||||
meta: { title: '字典数据', activeMenu: '/system/dict' }
|
meta: { title: '字典数据', activeMenu: '/system/dict' }
|
||||||
}
|
}
|
||||||
|
|
@ -138,7 +158,7 @@ export const dynamicRoutes = [
|
||||||
children: [
|
children: [
|
||||||
{
|
{
|
||||||
path: 'index/:jobId(\\d+)',
|
path: 'index/:jobId(\\d+)',
|
||||||
component: () => import('@/views/monitor/job/log'),
|
component: () => import('@/views/monitor/job/log.vue'),
|
||||||
name: 'JobLog',
|
name: 'JobLog',
|
||||||
meta: { title: '调度日志', activeMenu: '/monitor/job' }
|
meta: { title: '调度日志', activeMenu: '/monitor/job' }
|
||||||
}
|
}
|
||||||
|
|
@ -152,13 +172,16 @@ export const dynamicRoutes = [
|
||||||
children: [
|
children: [
|
||||||
{
|
{
|
||||||
path: 'index/:tableId(\\d+)',
|
path: 'index/:tableId(\\d+)',
|
||||||
component: () => import('@/views/tool/gen/editTable'),
|
component: () => import('@/views/tool/gen/editTable.vue'),
|
||||||
name: 'GenEdit',
|
name: 'GenEdit',
|
||||||
meta: { title: '修改生成配置', activeMenu: '/tool/gen' }
|
meta: { title: '修改生成配置', activeMenu: '/tool/gen' }
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
]
|
];
|
||||||
|
|
||||||
|
// 动态路由已合并到静态路由中,不再需要
|
||||||
|
// export const dynamicRoutes = [ ... ];
|
||||||
|
|
||||||
const router = createRouter({
|
const router = createRouter({
|
||||||
history: createWebHistory(),
|
history: createWebHistory(),
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
import auth from '@/plugins/auth'
|
import auth from '@/plugins/auth'
|
||||||
import router, { constantRoutes, dynamicRoutes } from '@/router'
|
import router, { constantRoutes } from '@/router'
|
||||||
import { getRouters } from '@/api/menu'
|
// 不再需要动态路由,移除 dynamicRoutes 和 getRouters
|
||||||
|
// import { getRouters } from '@/api/menu'
|
||||||
import Layout from '@/layout/index'
|
import Layout from '@/layout/index'
|
||||||
import ParentView from '@/components/ParentView'
|
import ParentView from '@/components/ParentView'
|
||||||
import InnerLink from '@/layout/components/InnerLink'
|
import InnerLink from '@/layout/components/InnerLink'
|
||||||
|
|
@ -34,22 +35,13 @@ const usePermissionStore = defineStore(
|
||||||
},
|
},
|
||||||
generateRoutes(roles) {
|
generateRoutes(roles) {
|
||||||
return new Promise(resolve => {
|
return new Promise(resolve => {
|
||||||
// 向后端请求路由数据
|
// 不再调用后端接口,直接使用静态路由
|
||||||
getRouters().then(res => {
|
const staticRoutes = constantRoutes
|
||||||
const sdata = JSON.parse(JSON.stringify(res.data))
|
this.setRoutes(staticRoutes)
|
||||||
const rdata = JSON.parse(JSON.stringify(res.data))
|
this.setSidebarRouters(staticRoutes)
|
||||||
const defaultData = JSON.parse(JSON.stringify(res.data))
|
this.setDefaultRoutes(staticRoutes)
|
||||||
const sidebarRoutes = filterAsyncRouter(sdata)
|
this.setTopbarRoutes(staticRoutes)
|
||||||
const rewriteRoutes = filterAsyncRouter(rdata, false, true)
|
resolve(staticRoutes)
|
||||||
const defaultRoutes = filterAsyncRouter(defaultData)
|
|
||||||
const asyncRoutes = filterDynamicRoutes(dynamicRoutes)
|
|
||||||
asyncRoutes.forEach(route => { router.addRoute(route) })
|
|
||||||
this.setRoutes(rewriteRoutes)
|
|
||||||
this.setSidebarRouters(constantRoutes.concat(sidebarRoutes))
|
|
||||||
this.setDefaultRoutes(sidebarRoutes)
|
|
||||||
this.setTopbarRoutes(defaultRoutes)
|
|
||||||
resolve(rewriteRoutes)
|
|
||||||
})
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -167,6 +167,9 @@ export function handleTree(data, id, parentId, children) {
|
||||||
for (let d of data) {
|
for (let d of data) {
|
||||||
let id = d[config.id];
|
let id = d[config.id];
|
||||||
childrenListMap[id] = d;
|
childrenListMap[id] = d;
|
||||||
|
if (!d[config.childrenList]) {
|
||||||
|
d[config.childrenList] = [];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (let d of data) {
|
for (let d of data) {
|
||||||
|
|
@ -216,7 +219,7 @@ export function getNormalPath(p) {
|
||||||
if (res[res.length - 1] === '/') {
|
if (res[res.length - 1] === '/') {
|
||||||
return res.slice(0, res.length - 1)
|
return res.slice(0, res.length - 1)
|
||||||
}
|
}
|
||||||
return res;
|
return res
|
||||||
}
|
}
|
||||||
|
|
||||||
// 验证是否为blob格式
|
// 验证是否为blob格式
|
||||||
|
|
|
||||||
|
|
@ -2,9 +2,9 @@
|
||||||
<div class="app-container home">
|
<div class="app-container home">
|
||||||
<el-row :gutter="20">
|
<el-row :gutter="20">
|
||||||
<el-col :sm="24" :lg="12" style="padding-left: 20px">
|
<el-col :sm="24" :lg="12" style="padding-left: 20px">
|
||||||
<h2>若依后台管理框架</h2>
|
<h2>智奇后台管理框架</h2>
|
||||||
<p>
|
<p>
|
||||||
一直想做一款后台管理系统,看了很多优秀的开源项目但是发现没有合适自己的。于是利用空闲休息时间开始自己写一套后台系统。如此有了若依管理系统,她可以用于所有的Web应用程序,如网站管理后台,网站会员中心,CMS,CRM,OA等等,当然,您也可以对她进行深度定制,以做出更强系统。所有前端后台代码封装过后十分精简易上手,出错概率低。同时支持移动客户端访问。系统会陆续更新一些实用功能。
|
一直想做一款后台管理系统,看了很多优秀的开源项目但是发现没有合适自己的。于是利用空闲休息时间开始自己写一套后台系统。如此有了智奇管理系统,她可以用于所有的Web应用程序,如网站管理后台,网站会员中心,CMS,CRM,OA等等,当然,您也可以对她进行深度定制,以做出更强系统。所有前端后台代码封装过后十分精简易上手,出错概率低。同时支持移动客户端访问。系统会陆续更新一些实用功能。
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
<b>当前版本:</b> <span>v{{ version }}</span>
|
<b>当前版本:</b> <span>v{{ version }}</span>
|
||||||
|
|
@ -81,25 +81,32 @@
|
||||||
>
|
>
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
<i class="el-icon-user-solid"></i> QQ群:<s> 满937441 </s> <s> 满887144332 </s>
|
<i class="el-icon-user-solid"></i> QQ群:<s> 满937441 </s>
|
||||||
<s> 满180251782 </s> <s> 满104180207 </s> <s> 满186866453 </s> <s> 满201396349 </s>
|
<s> 满887144332 </s> <s> 满180251782 </s> <s> 满104180207 </s>
|
||||||
<s> 满101456076 </s> <s> 满101539465 </s> <s> 满264312783 </s> <s> 满167385320 </s>
|
<s> 满186866453 </s> <s> 满201396349 </s> <s> 满101456076 </s>
|
||||||
<s> 满104748341 </s> <s> 满160110482 </s> <s> 满170801498 </s> <s> 满108482800 </s>
|
<s> 满101539465 </s> <s> 满264312783 </s> <s> 满167385320 </s>
|
||||||
<s> 满101046199 </s> <s> 满136919097 </s> <s> 满143961921 </s> <s> 满174951577 </s>
|
<s> 满104748341 </s> <s> 满160110482 </s> <s> 满170801498 </s>
|
||||||
<s> 满161281055 </s> <s> 满138988063 </s> <s> 满151450850 </s> <s> 满224622315 </s>
|
<s> 满108482800 </s> <s> 满101046199 </s> <s> 满136919097 </s>
|
||||||
<s> 满287842588 </s> <a href="http://qm.qq.com/cgi-bin/qm/qr?_wv=1027&k=HKz42pk2zQ1WUMbbRo50-N7tY4IDKKrj&authKey=ukBg3edmUbnmoo40xZCo6R1tYdTglYJ1nBFRW9gTHuZwLI1r8wgFT6cWUrAldwcr&noverify=0&group_code=187944233" target="_blank">187944233</a>
|
<s> 满143961921 </s> <s> 满174951577 </s> <s> 满161281055 </s>
|
||||||
|
<s> 满138988063 </s> <s> 满151450850 </s> <s> 满224622315 </s>
|
||||||
|
<s> 满287842588 </s> <s> 满187944233 </s>
|
||||||
|
<a
|
||||||
|
href="http://qm.qq.com/cgi-bin/qm/qr?_wv=1027&k=G6r5KGCaa3pqdbUSXNIgYloyb8e0_L0D&authKey=4w8tF1eGW7%2FedWn%2FHAypQksdrML%2BDHolQSx7094Agm7Luakj9EbfPnSTxSi2T1LQ&noverify=0&group_code=228578329"
|
||||||
|
target="_blank"
|
||||||
|
>228578329</a
|
||||||
|
>
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
<i class="el-icon-chat-dot-round"></i> 微信:<a
|
<i class="el-icon-chat-dot-round"></i> 微信:<a
|
||||||
href="javascript:;"
|
href="javascript:;"
|
||||||
>/ *若依</a
|
>/ *智奇</a
|
||||||
>
|
>
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
<i class="el-icon-money"></i> 支付宝:<a
|
<i class="el-icon-money"></i> 支付宝:<a
|
||||||
href="javascript:;"
|
href="javascript:;"
|
||||||
class="支付宝信息"
|
class="支付宝信息"
|
||||||
>/ *若依</a
|
>/ *智奇</a
|
||||||
>
|
>
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
@ -297,7 +304,9 @@
|
||||||
<li>修复table中更多按钮切换主题色未生效修复问题</li>
|
<li>修复table中更多按钮切换主题色未生效修复问题</li>
|
||||||
<li>修复某些特性的环境生成代码变乱码TXT文件问题</li>
|
<li>修复某些特性的环境生成代码变乱码TXT文件问题</li>
|
||||||
<li>修复代码生成图片/文件/单选时选择必填无法校验问题</li>
|
<li>修复代码生成图片/文件/单选时选择必填无法校验问题</li>
|
||||||
<li>修复某些特性的情况用户编辑对话框中角色和部门无法修改问题</li>
|
<li>
|
||||||
|
修复某些特性的情况用户编辑对话框中角色和部门无法修改问题
|
||||||
|
</li>
|
||||||
<li>其他细节优化</li>
|
<li>其他细节优化</li>
|
||||||
</ol>
|
</ol>
|
||||||
</el-collapse-item>
|
</el-collapse-item>
|
||||||
|
|
@ -358,7 +367,9 @@
|
||||||
<li>新增获取不带后缀文件名称方法</li>
|
<li>新增获取不带后缀文件名称方法</li>
|
||||||
<li>新增获取配置文件中的属性值方法</li>
|
<li>新增获取配置文件中的属性值方法</li>
|
||||||
<li>新增内容编码/解码方便插件集成使用</li>
|
<li>新增内容编码/解码方便插件集成使用</li>
|
||||||
<li>字典类型必须以字母开头,且只能为(小写字母,数字,下滑线)</li>
|
<li>
|
||||||
|
字典类型必须以字母开头,且只能为(小写字母,数字,下滑线)
|
||||||
|
</li>
|
||||||
<li>优化设置分页参数默认值</li>
|
<li>优化设置分页参数默认值</li>
|
||||||
<li>优化对空字符串参数处理的过滤</li>
|
<li>优化对空字符串参数处理的过滤</li>
|
||||||
<li>优化显示顺序orderNum类型为整型</li>
|
<li>优化显示顺序orderNum类型为整型</li>
|
||||||
|
|
@ -562,8 +573,12 @@
|
||||||
<li>BLOB下载时清除URL对象引用</li>
|
<li>BLOB下载时清除URL对象引用</li>
|
||||||
<li>代码生成导入表按创建时间排序</li>
|
<li>代码生成导入表按创建时间排序</li>
|
||||||
<li>修复代码生成页面数据编辑保存之后总是跳转第一页的问题</li>
|
<li>修复代码生成页面数据编辑保存之后总是跳转第一页的问题</li>
|
||||||
<li>修复带safari浏览器无法格式化utc日期格式yyyy-MM-dd'T'HH:mm:ss.SSS问题</li>
|
<li>
|
||||||
<li>多图上传组件移除多余的api地址&验证失败导致图片删除问题&无法删除相应图片修复</li>
|
修复带safari浏览器无法格式化utc日期格式yyyy-MM-dd'T'HH:mm:ss.SSS问题
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
多图上传组件移除多余的api地址&验证失败导致图片删除问题&无法删除相应图片修复
|
||||||
|
</li>
|
||||||
<li>其他细节优化</li>
|
<li>其他细节优化</li>
|
||||||
</ol>
|
</ol>
|
||||||
</el-collapse-item>
|
</el-collapse-item>
|
||||||
|
|
@ -608,7 +623,9 @@
|
||||||
</el-collapse-item>
|
</el-collapse-item>
|
||||||
<el-collapse-item title="v3.5.0 - 2021-05-25">
|
<el-collapse-item title="v3.5.0 - 2021-05-25">
|
||||||
<ol>
|
<ol>
|
||||||
<li>新增菜单导航显示风格TopNav(false为左侧导航菜单,true为顶部导航菜单)</li>
|
<li>
|
||||||
|
新增菜单导航显示风格TopNav(false为左侧导航菜单,true为顶部导航菜单)
|
||||||
|
</li>
|
||||||
<li>布局设置支持保存&重置配置</li>
|
<li>布局设置支持保存&重置配置</li>
|
||||||
<li>修复树表数据显示不全&加载慢问题</li>
|
<li>修复树表数据显示不全&加载慢问题</li>
|
||||||
<li>新增IE浏览器版本过低提示页面</li>
|
<li>新增IE浏览器版本过低提示页面</li>
|
||||||
|
|
@ -627,7 +644,9 @@
|
||||||
<li>升级druid到最新版本v1.2.6</li>
|
<li>升级druid到最新版本v1.2.6</li>
|
||||||
<li>升级mybatis到最新版3.5.6 阻止远程代码执行漏洞</li>
|
<li>升级mybatis到最新版3.5.6 阻止远程代码执行漏洞</li>
|
||||||
<li>升级oshi到最新版本v5.6.0</li>
|
<li>升级oshi到最新版本v5.6.0</li>
|
||||||
<li>velocity剔除commons-collections版本,防止3.2.1版本的反序列化漏洞</li>
|
<li>
|
||||||
|
velocity剔除commons-collections版本,防止3.2.1版本的反序列化漏洞
|
||||||
|
</li>
|
||||||
<li>数据监控页默认账户密码防止越权访问</li>
|
<li>数据监控页默认账户密码防止越权访问</li>
|
||||||
<li>修复firefox下表单构建拖拽会新打卡一个选项卡</li>
|
<li>修复firefox下表单构建拖拽会新打卡一个选项卡</li>
|
||||||
<li>修正后端导入表权限标识</li>
|
<li>修正后端导入表权限标识</li>
|
||||||
|
|
@ -992,7 +1011,7 @@
|
||||||
</el-collapse-item>
|
</el-collapse-item>
|
||||||
<el-collapse-item title="v1.0.0 - 2019-10-08">
|
<el-collapse-item title="v1.0.0 - 2019-10-08">
|
||||||
<ol>
|
<ol>
|
||||||
<li>若依前后端分离系统正式发布</li>
|
<li>智奇前后端分离系统正式发布</li>
|
||||||
</ol>
|
</ol>
|
||||||
</el-collapse-item>
|
</el-collapse-item>
|
||||||
</el-collapse>
|
</el-collapse>
|
||||||
|
|
@ -1009,7 +1028,7 @@
|
||||||
<img
|
<img
|
||||||
src="@/assets/images/pay.png"
|
src="@/assets/images/pay.png"
|
||||||
alt="donate"
|
alt="donate"
|
||||||
style="width:100%"
|
style="width: 100%"
|
||||||
/>
|
/>
|
||||||
<span style="display: inline-block; height: 30px; line-height: 30px"
|
<span style="display: inline-block; height: 30px; line-height: 30px"
|
||||||
>你可以请作者喝杯咖啡表示鼓励</span
|
>你可以请作者喝杯咖啡表示鼓励</span
|
||||||
|
|
@ -1022,10 +1041,10 @@
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup name="Index">
|
<script setup name="Index">
|
||||||
const version = ref('3.8.9')
|
const version = ref("3.8.9");
|
||||||
|
|
||||||
function goTarget(url) {
|
function goTarget(url) {
|
||||||
window.open(url, '__blank')
|
window.open(url, "__blank");
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="login">
|
<div class="login">
|
||||||
<el-form ref="loginRef" :model="loginForm" :rules="loginRules" class="login-form">
|
<el-form ref="loginRef" :model="loginForm" :rules="loginRules" class="login-form">
|
||||||
<h3 class="title">若依后台管理系统</h3>
|
<h3 class="title">{{ title }}</h3>
|
||||||
<el-form-item prop="username">
|
<el-form-item prop="username">
|
||||||
<el-input
|
<el-input
|
||||||
v-model="loginForm.username"
|
v-model="loginForm.username"
|
||||||
|
|
@ -59,7 +59,7 @@
|
||||||
</el-form>
|
</el-form>
|
||||||
<!-- 底部 -->
|
<!-- 底部 -->
|
||||||
<div class="el-login-footer">
|
<div class="el-login-footer">
|
||||||
<span>Copyright © 2018-2025 ruoyi.vip All Rights Reserved.</span>
|
<span>Copyright © 2018-2025 guoan All Rights Reserved.</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
@ -70,7 +70,8 @@ import Cookies from "js-cookie";
|
||||||
import { encrypt, decrypt } from "@/utils/jsencrypt";
|
import { encrypt, decrypt } from "@/utils/jsencrypt";
|
||||||
import useUserStore from '@/store/modules/user'
|
import useUserStore from '@/store/modules/user'
|
||||||
|
|
||||||
const userStore = useUserStore()
|
const title = import.meta.env.VITE_APP_TITLE;
|
||||||
|
const userStore = useUserStore();
|
||||||
const route = useRoute();
|
const route = useRoute();
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const { proxy } = getCurrentInstance();
|
const { proxy } = getCurrentInstance();
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,54 @@
|
||||||
|
<template>
|
||||||
|
<el-form
|
||||||
|
label-width="100px"
|
||||||
|
class="step-form"
|
||||||
|
style="margin-top: 32px; max-width: 500px"
|
||||||
|
>
|
||||||
|
<el-form-item label="客户名称" required>
|
||||||
|
<el-select v-model="form.customerName" placeholder="请选择客户名称">
|
||||||
|
<el-option label="广州铁路局" value="广州铁路局" />
|
||||||
|
<el-option label="深圳地铁" value="深圳地铁" />
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="客户地址" required>
|
||||||
|
<el-select v-model="form.customerAddress" placeholder="请选择客户地址">
|
||||||
|
<el-option label="广州市天河区" value="广州市天河区" />
|
||||||
|
<el-option label="深圳市福田区" value="深圳市福田区" />
|
||||||
|
<el-option label="广州市越秀区" value="广州市越秀区" />
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="邮编">
|
||||||
|
<el-input v-model="form.zip" placeholder="请输入邮编" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="联系人">
|
||||||
|
<el-select v-model="form.linkman" placeholder="请选择联系人">
|
||||||
|
<el-option label="张三" value="张三" />
|
||||||
|
<el-option label="李四" value="李四" />
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="联系方式">
|
||||||
|
<el-select v-model="form.linkway" placeholder="请选择联系方式">
|
||||||
|
<el-option label="13921000000" value="13921000000" />
|
||||||
|
<el-option label="13921000001" value="13921000001" />
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item>
|
||||||
|
<el-button type="primary" @click="$emit('next-step')">下一步</el-button>
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
const props = defineProps({
|
||||||
|
form: {
|
||||||
|
type: Object,
|
||||||
|
required: true,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.step-form {
|
||||||
|
margin-top: 32px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
@ -0,0 +1,284 @@
|
||||||
|
<template>
|
||||||
|
<div class="step2-container">
|
||||||
|
<div class="main-row">
|
||||||
|
<div class="step-indicator">
|
||||||
|
<div :class="['circle', selectedCarTypeProxy === '其他' ? 'active' : '']">
|
||||||
|
1
|
||||||
|
</div>
|
||||||
|
<div class="line"></div>
|
||||||
|
<div :class="['circle', selectedCarTypeProxy !== '其他' ? 'active' : '']">
|
||||||
|
2
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="right-content">
|
||||||
|
<div v-if="selectedCarTypeProxy === '其他'">
|
||||||
|
<div class="step-title">第一步:选择车型</div>
|
||||||
|
<el-form label-width="120px" class="model-form">
|
||||||
|
<el-form-item label="可参考车型列表" required>
|
||||||
|
<el-select
|
||||||
|
v-model="selectedCarTypeProxy"
|
||||||
|
placeholder="请选择车型"
|
||||||
|
filterable
|
||||||
|
>
|
||||||
|
<el-option-group label="最近常选">
|
||||||
|
<el-option label="CR400AF" value="CR400AF" />
|
||||||
|
<el-option label="CR800AF" value="CR800AF" />
|
||||||
|
</el-option-group>
|
||||||
|
<el-option-group label="全部车型">
|
||||||
|
<el-option label="CR400AF" value="CR400AF" />
|
||||||
|
<el-option label="CR800AF" value="CR800AF" />
|
||||||
|
<el-option label="其他" value="其他" />
|
||||||
|
</el-option-group>
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="计划购买数量">
|
||||||
|
<el-input v-model="planCount" placeholder="请输入计划购买数量" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="车型输入">
|
||||||
|
<el-input v-model="modelInput" placeholder="请输入车型" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="车型描述">
|
||||||
|
<el-input
|
||||||
|
type="textarea"
|
||||||
|
:rows="5"
|
||||||
|
v-model="modelDesc"
|
||||||
|
placeholder="请输入车型描述"
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
</div>
|
||||||
|
<div v-else>
|
||||||
|
<div class="step">
|
||||||
|
<div class="step-left">
|
||||||
|
<div class="step-title">
|
||||||
|
第一步:选择车型
|
||||||
|
<el-form label-width="120px" class="model-form">
|
||||||
|
<el-form-item label="可参考车型列表" required>
|
||||||
|
<el-select
|
||||||
|
v-model="selectedCarTypeProxy"
|
||||||
|
placeholder="请选择车型"
|
||||||
|
filterable
|
||||||
|
>
|
||||||
|
<el-option-group label="最近常选">
|
||||||
|
<el-option label="CR400AF" value="CR400AF" />
|
||||||
|
<el-option label="CR800AF" value="CR800AF" />
|
||||||
|
</el-option-group>
|
||||||
|
<el-option-group label="全部车型">
|
||||||
|
<el-option label="CR400AF" value="CR400AF" />
|
||||||
|
<el-option label="JR800AF" value="JR800AF" />
|
||||||
|
<el-option label="其他" value="其他" />
|
||||||
|
</el-option-group>
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="计划购买数量">
|
||||||
|
<el-input v-model="planCount" placeholder="请输入计划购买数量" />
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
</div>
|
||||||
|
<div class="step-title">第二步:选择轮对商品</div>
|
||||||
|
</div>
|
||||||
|
<div class="image-box right-align">
|
||||||
|
<img
|
||||||
|
src="../../../assets/images/3D.png"
|
||||||
|
alt="3D结构图"
|
||||||
|
class="main-img"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="table-title">商品参数选择</div>
|
||||||
|
<div class="tree-table-row">
|
||||||
|
<el-tree
|
||||||
|
:data="treeData"
|
||||||
|
:props="treeProps"
|
||||||
|
node-key="id"
|
||||||
|
highlight-current
|
||||||
|
@current-change="handleTreeSelect"
|
||||||
|
class="tree-box"
|
||||||
|
default-expand-all
|
||||||
|
/>
|
||||||
|
<el-table
|
||||||
|
:data="selectedPartInfo ? [selectedPartInfo] : []"
|
||||||
|
border
|
||||||
|
class="part-table"
|
||||||
|
style="width: 600px; margin-left: 24px"
|
||||||
|
>
|
||||||
|
<el-table-column prop="name" label="部件名称" />
|
||||||
|
<el-table-column prop="spec" label="规格" />
|
||||||
|
<el-table-column prop="desc" label="描述" />
|
||||||
|
</el-table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="btn-row">
|
||||||
|
<el-button @click="$emit('prev-step')">上一步</el-button>
|
||||||
|
<el-button type="primary" @click="$emit('next-step')"
|
||||||
|
>下一步</el-button
|
||||||
|
>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import { ref, computed } from "vue";
|
||||||
|
const props = defineProps({
|
||||||
|
form: Object,
|
||||||
|
selectedCarType: String
|
||||||
|
});
|
||||||
|
// 彻底双向绑定
|
||||||
|
const selectedCarTypeProxy = computed({
|
||||||
|
get() {
|
||||||
|
return props.form?.selectedCarType ?? "其他";
|
||||||
|
},
|
||||||
|
set(val) {
|
||||||
|
if (props.form) props.form.selectedCarType = val;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
const planCount = ref("");
|
||||||
|
const modelInput = ref("");
|
||||||
|
const modelDesc = ref("");
|
||||||
|
|
||||||
|
// 模拟树形结构数据
|
||||||
|
const treeData = [
|
||||||
|
{
|
||||||
|
id: 1,
|
||||||
|
label: "D310000000032,动力轮对轴箱组装(带联轴节)",
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
id: 2,
|
||||||
|
label: "D311000000013,动力轮对(带轴承+联轴节)",
|
||||||
|
children: [
|
||||||
|
{ id: 3, label: "D311001000011,动力轮对" },
|
||||||
|
{ id: 4, label: "D325000000013,左轴箱组装" },
|
||||||
|
{ id: 5, label: "D326000000037,左轴端装置" },
|
||||||
|
{ id: 6, label: "D325000000014,右轴箱组装" },
|
||||||
|
{ id: 7, label: "D326000000038,右轴端装置" },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
];
|
||||||
|
const treeProps = { children: "children", label: "label" };
|
||||||
|
|
||||||
|
// 模拟部件关键信息
|
||||||
|
const partInfoMap = {
|
||||||
|
1: { name: "动力轮对轴箱组装", spec: "组装件", desc: "包含轮对、轴箱等" },
|
||||||
|
2: { name: "动力轮对", spec: "带轴承+联轴节", desc: "动力传递部件" },
|
||||||
|
3: { name: "动力轮对", spec: "标准型", desc: "主驱动部件" },
|
||||||
|
4: { name: "左轴箱组装", spec: "组装件", desc: "左侧支撑" },
|
||||||
|
5: { name: "左轴端装置", spec: "端装置", desc: "左端保护" },
|
||||||
|
6: { name: "右轴箱组装", spec: "组装件", desc: "右侧支撑" },
|
||||||
|
7: { name: "右轴端装置", spec: "端装置", desc: "右端保护" },
|
||||||
|
};
|
||||||
|
const selectedPartInfo = ref(null);
|
||||||
|
function handleTreeSelect(node) {
|
||||||
|
if (node && node.id && partInfoMap[node.id]) {
|
||||||
|
selectedPartInfo.value = partInfoMap[node.id];
|
||||||
|
} else {
|
||||||
|
selectedPartInfo.value = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped lang="scss">
|
||||||
|
.step2-container {
|
||||||
|
background: #fff;
|
||||||
|
padding: 24px;
|
||||||
|
border-radius: 8px;
|
||||||
|
}
|
||||||
|
.main-row {
|
||||||
|
display: flex;
|
||||||
|
align-items: flex-start;
|
||||||
|
}
|
||||||
|
.step-indicator {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
margin-right: 24px;
|
||||||
|
margin-top: 10px;
|
||||||
|
}
|
||||||
|
.circle {
|
||||||
|
width: 32px;
|
||||||
|
height: 32px;
|
||||||
|
border-radius: 50%;
|
||||||
|
background: #ccc;
|
||||||
|
color: #fff;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
font-size: 20px;
|
||||||
|
margin-bottom: 8px;
|
||||||
|
}
|
||||||
|
.circle.active {
|
||||||
|
background: #2156f3;
|
||||||
|
}
|
||||||
|
.line {
|
||||||
|
width: 4px;
|
||||||
|
height: 170px;
|
||||||
|
background: #ccc;
|
||||||
|
margin-bottom: 8px;
|
||||||
|
}
|
||||||
|
.right-content {
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
.step {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
}
|
||||||
|
.step-left {
|
||||||
|
flex: 1;
|
||||||
|
margin-right: 10px;
|
||||||
|
}
|
||||||
|
.step-title {
|
||||||
|
font-size: 18px;
|
||||||
|
margin: 15px 0;
|
||||||
|
}
|
||||||
|
.step-title:last-child {
|
||||||
|
margin-top: 145px;
|
||||||
|
}
|
||||||
|
.image-box.right-align {
|
||||||
|
display: flex;
|
||||||
|
justify-content: flex-end;
|
||||||
|
align-items: flex-start;
|
||||||
|
margin-bottom: 16px;
|
||||||
|
}
|
||||||
|
.table-title {
|
||||||
|
font-weight: bold;
|
||||||
|
margin: 16px 0 8px 0;
|
||||||
|
}
|
||||||
|
.tree-table-row {
|
||||||
|
display: flex;
|
||||||
|
align-items: flex-start;
|
||||||
|
border: 1px solid #bbb;
|
||||||
|
border-radius: 4px;
|
||||||
|
padding: 12px;
|
||||||
|
background: #fff;
|
||||||
|
}
|
||||||
|
.tree-box {
|
||||||
|
min-width: 340px;
|
||||||
|
max-width: 400px;
|
||||||
|
margin-right: 24px;
|
||||||
|
background: #fff;
|
||||||
|
}
|
||||||
|
.part-table {
|
||||||
|
min-width: 350px;
|
||||||
|
max-width: 600px;
|
||||||
|
}
|
||||||
|
.model-form {
|
||||||
|
max-width: 900px;
|
||||||
|
margin-top: 16px;
|
||||||
|
}
|
||||||
|
.btn-row {
|
||||||
|
display: flex;
|
||||||
|
justify-content: flex-end;
|
||||||
|
gap: 16px;
|
||||||
|
margin-top: 20px;
|
||||||
|
}
|
||||||
|
.main-img {
|
||||||
|
width: 580px;
|
||||||
|
height: 260px;
|
||||||
|
object-fit: contain;
|
||||||
|
border-radius: 8px;
|
||||||
|
border: 1px solid #eee;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
@ -0,0 +1,75 @@
|
||||||
|
<template>
|
||||||
|
<div class="step3-container">
|
||||||
|
<el-form label-width="100px">
|
||||||
|
<el-form-item label="付款条件">
|
||||||
|
<el-input
|
||||||
|
type="textarea"
|
||||||
|
:rows="2"
|
||||||
|
v-model="form.pay"
|
||||||
|
placeholder="请输入付款条件"
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="异议">
|
||||||
|
<el-input
|
||||||
|
type="textarea"
|
||||||
|
:rows="2"
|
||||||
|
v-model="form.objection"
|
||||||
|
placeholder="请输入异议信息"
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="不可抗力">
|
||||||
|
<el-input
|
||||||
|
type="textarea"
|
||||||
|
:rows="2"
|
||||||
|
v-model="form.force"
|
||||||
|
placeholder="请输入不可抗力信息"
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="争议解决">
|
||||||
|
<el-input
|
||||||
|
type="textarea"
|
||||||
|
:rows="2"
|
||||||
|
v-model="form.dispute"
|
||||||
|
placeholder="请输入争议解决方式"
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="其他">
|
||||||
|
<el-input
|
||||||
|
type="textarea"
|
||||||
|
:rows="2"
|
||||||
|
v-model="form.other"
|
||||||
|
placeholder="请输入其他信息"
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
|
<div class="btn-row">
|
||||||
|
<el-button @click="$emit('prev-step')">上一步</el-button>
|
||||||
|
<el-button type="primary" @click="$emit('next-step')">下一步</el-button>
|
||||||
|
</div>
|
||||||
|
</el-form>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import { reactive } from "vue";
|
||||||
|
const form = reactive({
|
||||||
|
pay: "货物出厂验收合格前,买方需将所提货物全额货款支付给卖方。",
|
||||||
|
objection: "",
|
||||||
|
force: "",
|
||||||
|
dispute: "",
|
||||||
|
other: "",
|
||||||
|
});
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped lang="scss">
|
||||||
|
.step3-container {
|
||||||
|
background: #fff;
|
||||||
|
padding: 24px;
|
||||||
|
border-radius: 8px;
|
||||||
|
}
|
||||||
|
.btn-row {
|
||||||
|
display: flex;
|
||||||
|
justify-content: flex-end;
|
||||||
|
gap: 16px;
|
||||||
|
margin-top: 20px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
@ -0,0 +1,58 @@
|
||||||
|
<template>
|
||||||
|
<div class="step4-container">
|
||||||
|
<div class="success-box">
|
||||||
|
<div class="success-icon">
|
||||||
|
<svg viewBox="0 0 100 100" width="100" height="100">
|
||||||
|
<circle cx="50" cy="50" r="48" fill="#b6ff3a" />
|
||||||
|
<polyline
|
||||||
|
points="30,55 48,72 72,38"
|
||||||
|
fill="none"
|
||||||
|
stroke="#222"
|
||||||
|
stroke-width="8"
|
||||||
|
stroke-linecap="round"
|
||||||
|
stroke-linejoin="round"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
<div class="success-text">订单/意向生成,我们会尽快安排生产/研发</div>
|
||||||
|
</div>
|
||||||
|
<div class="btn-row">
|
||||||
|
<el-button @click="$emit('prev-step')">上一步</el-button>
|
||||||
|
<el-button type="primary">查看列表</el-button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped lang="scss">
|
||||||
|
.step4-container {
|
||||||
|
background: #fff;
|
||||||
|
padding: 24px;
|
||||||
|
border-radius: 8px;
|
||||||
|
}
|
||||||
|
.success-box {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
margin: 48px 0 24px 0;
|
||||||
|
}
|
||||||
|
.success-icon {
|
||||||
|
margin-bottom: 24px;
|
||||||
|
}
|
||||||
|
.success-text {
|
||||||
|
padding: 24px 0;
|
||||||
|
width: 100%;
|
||||||
|
text-align: center;
|
||||||
|
font-size: 22px;
|
||||||
|
color: #333;
|
||||||
|
border-radius: 6px;
|
||||||
|
}
|
||||||
|
.btn-row {
|
||||||
|
display: flex;
|
||||||
|
justify-content: flex-end;
|
||||||
|
gap: 16px;
|
||||||
|
margin-top: 20px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
@ -0,0 +1,123 @@
|
||||||
|
<template>
|
||||||
|
<div class="app-container">
|
||||||
|
<div class="header">
|
||||||
|
<h2>创建订单/意向</h2>
|
||||||
|
<div class="price-box">
|
||||||
|
<span>预计总价:</span>
|
||||||
|
<span class="price">300万</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<el-steps :active="stepForStepsBar" finish-status="success" align-center>
|
||||||
|
<el-step title="选择客户信息" description="选择销售客户对象" />
|
||||||
|
<el-step title="选择商品规格" description="根据客户需求选择商品" />
|
||||||
|
<el-step
|
||||||
|
title="录入商务信息"
|
||||||
|
description="根据客户要求录入商务信息"
|
||||||
|
:status="step3Status"
|
||||||
|
/>
|
||||||
|
<el-step title="完成创建" description="完成订单/意向" />
|
||||||
|
</el-steps>
|
||||||
|
<component
|
||||||
|
:is="currentStepComponent"
|
||||||
|
:form="form"
|
||||||
|
:selected-car-type="selectedCarType"
|
||||||
|
@next-step="handleNextStep"
|
||||||
|
@prev-step="handlePrevStep"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import { ref, computed } from "vue";
|
||||||
|
import Step1 from "./Step1.vue";
|
||||||
|
import Step2 from "./Step2.vue";
|
||||||
|
import Step3 from "./Step3.vue";
|
||||||
|
import Step4 from "./Step4.vue";
|
||||||
|
|
||||||
|
const step = ref(1);
|
||||||
|
const form = ref({
|
||||||
|
customerName: "",
|
||||||
|
customerAddress: "",
|
||||||
|
zip: "",
|
||||||
|
linkman: "",
|
||||||
|
linkway: "",
|
||||||
|
selectedCarType: "其他",
|
||||||
|
planCount: "",
|
||||||
|
});
|
||||||
|
|
||||||
|
const selectedCarType = computed(() => form.value.selectedCarType);
|
||||||
|
|
||||||
|
const currentStepComponent = computed(() => {
|
||||||
|
if (step.value === 1) return Step1;
|
||||||
|
if (step.value === 2) return Step2;
|
||||||
|
if (step.value === 3) return Step3;
|
||||||
|
if (step.value === 4) return Step4;
|
||||||
|
return Step1;
|
||||||
|
});
|
||||||
|
|
||||||
|
// 步骤条动态高亮/置灰
|
||||||
|
const stepForStepsBar = computed(() => {
|
||||||
|
// 车型为其他且在step2时,步骤条高亮到2,step4时高亮到4
|
||||||
|
if (selectedCarType.value === "其他") {
|
||||||
|
if (step.value === 2) return 2;
|
||||||
|
if (step.value === 4) return 4;
|
||||||
|
}
|
||||||
|
return step.value;
|
||||||
|
});
|
||||||
|
const step3Status = computed(() => {
|
||||||
|
// 车型为其他且在step2或step4时,步骤三置灰
|
||||||
|
if (
|
||||||
|
selectedCarType.value === "其他" &&
|
||||||
|
(step.value === 2 || step.value === 4)
|
||||||
|
) {
|
||||||
|
return "wait";
|
||||||
|
}
|
||||||
|
return undefined;
|
||||||
|
});
|
||||||
|
|
||||||
|
function handleNextStep() {
|
||||||
|
// 车型为其他时直接跳到step4
|
||||||
|
if (step.value === 2 && selectedCarType.value === "其他") {
|
||||||
|
step.value = 4;
|
||||||
|
} else if (step.value < 4) {
|
||||||
|
step.value++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
function handlePrevStep() {
|
||||||
|
if (step.value === 2) {
|
||||||
|
step.value = 1;
|
||||||
|
} else if (step.value === 3) {
|
||||||
|
step.value = 2;
|
||||||
|
} else if (step.value === 4) {
|
||||||
|
if (selectedCarType.value === "其他") {
|
||||||
|
step.value = 2;
|
||||||
|
} else {
|
||||||
|
step.value = 3;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped lang="scss">
|
||||||
|
.app-container {
|
||||||
|
max-width: 1200px;
|
||||||
|
margin: 0 auto;
|
||||||
|
.header {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
.price-box {
|
||||||
|
margin-top: 10px;
|
||||||
|
font-size: 20px;
|
||||||
|
}
|
||||||
|
.price {
|
||||||
|
background: #a6a6fa;
|
||||||
|
color: #fff;
|
||||||
|
padding: 4px 16px;
|
||||||
|
border-radius: 4px;
|
||||||
|
font-size: 28px;
|
||||||
|
margin-left: 8px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
@ -0,0 +1,340 @@
|
||||||
|
<template>
|
||||||
|
<div class="app-container">
|
||||||
|
<el-form :model="queryParams" ref="queryForm" :inline="true" v-show="showSearch" label-width="68px">
|
||||||
|
<el-form-item label="订单编号" prop="orderNo">
|
||||||
|
<el-input
|
||||||
|
v-model="queryParams.orderNo"
|
||||||
|
placeholder="请输入订单编号"
|
||||||
|
clearable
|
||||||
|
style="width: 240px"
|
||||||
|
@keyup.enter="handleQuery"
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="客户名称" prop="customerName">
|
||||||
|
<el-input
|
||||||
|
v-model="queryParams.customerName"
|
||||||
|
placeholder="请输入客户名称"
|
||||||
|
clearable
|
||||||
|
style="width: 240px"
|
||||||
|
@keyup.enter="handleQuery"
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="状态" prop="status">
|
||||||
|
<el-select v-model="queryParams.status" placeholder="请选择状态" clearable>
|
||||||
|
<el-option
|
||||||
|
v-for="dict in statusOptions"
|
||||||
|
:key="dict.value"
|
||||||
|
:label="dict.label"
|
||||||
|
:value="dict.value"
|
||||||
|
/>
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item>
|
||||||
|
<el-button type="primary" icon="Search" @click="handleQuery">搜索</el-button>
|
||||||
|
<el-button icon="Refresh" @click="resetQuery">重置</el-button>
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
|
||||||
|
<el-row :gutter="10" class="mb8">
|
||||||
|
<el-col :span="1.5">
|
||||||
|
<el-button
|
||||||
|
type="primary"
|
||||||
|
plain
|
||||||
|
icon="Plus"
|
||||||
|
@click="handleAdd"
|
||||||
|
v-hasPermi="['order:intention:add']"
|
||||||
|
>新增</el-button>
|
||||||
|
</el-col>
|
||||||
|
<el-col :span="1.5">
|
||||||
|
<el-button
|
||||||
|
type="success"
|
||||||
|
plain
|
||||||
|
icon="Edit"
|
||||||
|
:disabled="single"
|
||||||
|
@click="handleUpdate"
|
||||||
|
v-hasPermi="['order:intention:edit']"
|
||||||
|
>修改</el-button>
|
||||||
|
</el-col>
|
||||||
|
<el-col :span="1.5">
|
||||||
|
<el-button
|
||||||
|
type="danger"
|
||||||
|
plain
|
||||||
|
icon="Delete"
|
||||||
|
:disabled="multiple"
|
||||||
|
@click="handleDelete"
|
||||||
|
v-hasPermi="['order:intention:remove']"
|
||||||
|
>删除</el-button>
|
||||||
|
</el-col>
|
||||||
|
<right-toolbar v-model:showSearch="showSearch" @queryTable="getList"></right-toolbar>
|
||||||
|
</el-row>
|
||||||
|
|
||||||
|
<el-table v-loading="loading" :data="intentionList" @selection-change="handleSelectionChange">
|
||||||
|
<el-table-column type="selection" width="55" align="center" />
|
||||||
|
<el-table-column label="订单编号" align="center" prop="orderNo" />
|
||||||
|
<el-table-column label="客户名称" align="center" prop="customerName" />
|
||||||
|
<el-table-column label="联系电话" align="center" prop="phone" />
|
||||||
|
<el-table-column label="意向产品" align="center" prop="product" />
|
||||||
|
<el-table-column label="数量" align="center" prop="quantity" />
|
||||||
|
<el-table-column label="状态" align="center" prop="status">
|
||||||
|
<template #default="scope">
|
||||||
|
<dict-tag :options="statusOptions" :value="scope.row.status"/>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column label="创建时间" align="center" prop="createTime" width="180">
|
||||||
|
<template #default="scope">
|
||||||
|
<span>{{ parseTime(scope.row.createTime) }}</span>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column label="操作" align="center" class-name="small-padding fixed-width">
|
||||||
|
<template #default="scope">
|
||||||
|
<el-button
|
||||||
|
type="text"
|
||||||
|
icon="Edit"
|
||||||
|
@click="handleUpdate(scope.row)"
|
||||||
|
v-hasPermi="['order:intention:edit']"
|
||||||
|
>修改</el-button>
|
||||||
|
<el-button
|
||||||
|
type="text"
|
||||||
|
icon="Delete"
|
||||||
|
@click="handleDelete(scope.row)"
|
||||||
|
v-hasPermi="['order:intention:remove']"
|
||||||
|
>删除</el-button>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
</el-table>
|
||||||
|
|
||||||
|
<pagination
|
||||||
|
v-show="total>0"
|
||||||
|
:total="total"
|
||||||
|
v-model:page="queryParams.pageNum"
|
||||||
|
v-model:limit="queryParams.pageSize"
|
||||||
|
@pagination="getList"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<!-- 添加或修改意向订单对话框 -->
|
||||||
|
<el-dialog :title="title" v-model="open" width="500px" append-to-body>
|
||||||
|
<el-form ref="intentionRef" :model="form" :rules="rules" label-width="80px">
|
||||||
|
<el-form-item label="客户名称" prop="customerName">
|
||||||
|
<el-input v-model="form.customerName" placeholder="请输入客户名称" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="联系电话" prop="phone">
|
||||||
|
<el-input v-model="form.phone" placeholder="请输入联系电话" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="意向产品" prop="product">
|
||||||
|
<el-input v-model="form.product" placeholder="请输入意向产品" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="数量" prop="quantity">
|
||||||
|
<el-input-number v-model="form.quantity" :min="1" :max="999" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="状态" prop="status">
|
||||||
|
<el-select v-model="form.status" placeholder="请选择状态">
|
||||||
|
<el-option
|
||||||
|
v-for="dict in statusOptions"
|
||||||
|
:key="dict.value"
|
||||||
|
:label="dict.label"
|
||||||
|
:value="dict.value"
|
||||||
|
></el-option>
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="备注" prop="remark">
|
||||||
|
<el-input v-model="form.remark" type="textarea" placeholder="请输入内容" />
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
<template #footer>
|
||||||
|
<div class="dialog-footer">
|
||||||
|
<el-button type="primary" @click="submitForm">确 定</el-button>
|
||||||
|
<el-button @click="cancel">取 消</el-button>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</el-dialog>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup name="Intention">
|
||||||
|
import { ref, reactive, onMounted } from 'vue'
|
||||||
|
import { ElMessage, ElMessageBox } from 'element-plus'
|
||||||
|
import { parseTime } from '@/utils/ruoyi'
|
||||||
|
|
||||||
|
// 遮罩层
|
||||||
|
const loading = ref(true)
|
||||||
|
// 选中数组
|
||||||
|
const ids = ref([])
|
||||||
|
// 非单个禁用
|
||||||
|
const single = ref(true)
|
||||||
|
// 非多个禁用
|
||||||
|
const multiple = ref(true)
|
||||||
|
// 显示搜索条件
|
||||||
|
const showSearch = ref(true)
|
||||||
|
// 总条数
|
||||||
|
const total = ref(0)
|
||||||
|
// 意向订单表格数据
|
||||||
|
const intentionList = ref([])
|
||||||
|
// 弹出层标题
|
||||||
|
const title = ref("")
|
||||||
|
// 是否显示弹出层
|
||||||
|
const open = ref(false)
|
||||||
|
|
||||||
|
// 状态数据字典
|
||||||
|
const statusOptions = ref([
|
||||||
|
{ value: "0", label: "待处理" },
|
||||||
|
{ value: "1", label: "已确认" },
|
||||||
|
{ value: "2", label: "已取消" }
|
||||||
|
])
|
||||||
|
|
||||||
|
// 查询参数
|
||||||
|
const queryParams = ref({
|
||||||
|
pageNum: 1,
|
||||||
|
pageSize: 10,
|
||||||
|
orderNo: undefined,
|
||||||
|
customerName: undefined,
|
||||||
|
status: undefined
|
||||||
|
})
|
||||||
|
|
||||||
|
// 表单参数
|
||||||
|
const form = ref({})
|
||||||
|
// 表单校验
|
||||||
|
const rules = ref({
|
||||||
|
customerName: [
|
||||||
|
{ required: true, message: "客户名称不能为空", trigger: "blur" }
|
||||||
|
],
|
||||||
|
phone: [
|
||||||
|
{ required: true, message: "联系电话不能为空", trigger: "blur" }
|
||||||
|
],
|
||||||
|
product: [
|
||||||
|
{ required: true, message: "意向产品不能为空", trigger: "blur" }
|
||||||
|
],
|
||||||
|
quantity: [
|
||||||
|
{ required: true, message: "数量不能为空", trigger: "blur" }
|
||||||
|
],
|
||||||
|
status: [
|
||||||
|
{ required: true, message: "状态不能为空", trigger: "change" }
|
||||||
|
]
|
||||||
|
})
|
||||||
|
|
||||||
|
// 模拟数据
|
||||||
|
const mockData = [
|
||||||
|
{
|
||||||
|
id: 1,
|
||||||
|
orderNo: "INT202403150001",
|
||||||
|
customerName: "张三",
|
||||||
|
phone: "13800138000",
|
||||||
|
product: "产品A",
|
||||||
|
quantity: 2,
|
||||||
|
status: "0",
|
||||||
|
createTime: "2024-03-15 10:00:00",
|
||||||
|
remark: "测试数据1"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 2,
|
||||||
|
orderNo: "INT202403150002",
|
||||||
|
customerName: "李四",
|
||||||
|
phone: "13800138001",
|
||||||
|
product: "产品B",
|
||||||
|
quantity: 1,
|
||||||
|
status: "1",
|
||||||
|
createTime: "2024-03-15 11:00:00",
|
||||||
|
remark: "测试数据2"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
|
/** 查询意向订单列表 */
|
||||||
|
function getList() {
|
||||||
|
loading.value = true
|
||||||
|
// 模拟后端分页
|
||||||
|
const start = (queryParams.value.pageNum - 1) * queryParams.value.pageSize
|
||||||
|
const end = start + queryParams.value.pageSize
|
||||||
|
const filteredData = mockData.filter(item => {
|
||||||
|
if (queryParams.value.orderNo && !item.orderNo.includes(queryParams.value.orderNo)) return false
|
||||||
|
if (queryParams.value.customerName && !item.customerName.includes(queryParams.value.customerName)) return false
|
||||||
|
if (queryParams.value.status && item.status !== queryParams.value.status) return false
|
||||||
|
return true
|
||||||
|
})
|
||||||
|
intentionList.value = filteredData.slice(start, end)
|
||||||
|
total.value = filteredData.length
|
||||||
|
loading.value = false
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 搜索按钮操作 */
|
||||||
|
function handleQuery() {
|
||||||
|
queryParams.value.pageNum = 1
|
||||||
|
getList()
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 重置按钮操作 */
|
||||||
|
function resetQuery() {
|
||||||
|
queryParams.value = {
|
||||||
|
pageNum: 1,
|
||||||
|
pageSize: 10,
|
||||||
|
orderNo: undefined,
|
||||||
|
customerName: undefined,
|
||||||
|
status: undefined
|
||||||
|
}
|
||||||
|
handleQuery()
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 多选框选中数据 */
|
||||||
|
function handleSelectionChange(selection) {
|
||||||
|
ids.value = selection.map(item => item.id)
|
||||||
|
single.value = selection.length !== 1
|
||||||
|
multiple.value = !selection.length
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 新增按钮操作 */
|
||||||
|
function handleAdd() {
|
||||||
|
form.value = {
|
||||||
|
status: "0"
|
||||||
|
}
|
||||||
|
open.value = true
|
||||||
|
title.value = "添加意向订单"
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 修改按钮操作 */
|
||||||
|
function handleUpdate(row) {
|
||||||
|
const id = row.id || ids.value[0]
|
||||||
|
const intention = mockData.find(item => item.id === id)
|
||||||
|
if (intention) {
|
||||||
|
form.value = { ...intention }
|
||||||
|
open.value = true
|
||||||
|
title.value = "修改意向订单"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 提交按钮 */
|
||||||
|
function submitForm() {
|
||||||
|
// 模拟提交
|
||||||
|
ElMessage.success("操作成功")
|
||||||
|
open.value = false
|
||||||
|
getList()
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 删除按钮操作 */
|
||||||
|
function handleDelete(row) {
|
||||||
|
const intentionIds = row.id || ids.value
|
||||||
|
ElMessageBox.confirm('是否确认删除意向订单编号为"' + intentionIds + '"的数据项?', "警告", {
|
||||||
|
confirmButtonText: "确定",
|
||||||
|
cancelButtonText: "取消",
|
||||||
|
type: "warning"
|
||||||
|
}).then(() => {
|
||||||
|
// 模拟删除
|
||||||
|
ElMessage.success("删除成功")
|
||||||
|
getList()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 取消按钮 */
|
||||||
|
function cancel() {
|
||||||
|
open.value = false
|
||||||
|
reset()
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 表单重置 */
|
||||||
|
function reset() {
|
||||||
|
form.value = {
|
||||||
|
status: "0"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
getList()
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
<template>
|
<template>
|
||||||
<div class="register">
|
<div class="register">
|
||||||
<el-form ref="registerRef" :model="registerForm" :rules="registerRules" class="register-form">
|
<el-form ref="registerRef" :model="registerForm" :rules="registerRules" class="register-form">
|
||||||
<h3 class="title">若依后台管理系统</h3>
|
<h3 class="title">{{ title }}</h3>
|
||||||
<el-form-item prop="username">
|
<el-form-item prop="username">
|
||||||
<el-input
|
<el-input
|
||||||
v-model="registerForm.username"
|
v-model="registerForm.username"
|
||||||
|
|
@ -70,7 +70,7 @@
|
||||||
</el-form>
|
</el-form>
|
||||||
<!-- 底部 -->
|
<!-- 底部 -->
|
||||||
<div class="el-register-footer">
|
<div class="el-register-footer">
|
||||||
<span>Copyright © 2018-2025 ruoyi.vip All Rights Reserved.</span>
|
<span>Copyright © 2018-2025 guoan All Rights Reserved.</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
@ -79,6 +79,7 @@
|
||||||
import { ElMessageBox } from "element-plus";
|
import { ElMessageBox } from "element-plus";
|
||||||
import { getCodeImg, register } from "@/api/login";
|
import { getCodeImg, register } from "@/api/login";
|
||||||
|
|
||||||
|
const title = import.meta.env.VITE_APP_TITLE;
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const { proxy } = getCurrentInstance();
|
const { proxy } = getCurrentInstance();
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -23,7 +23,7 @@
|
||||||
<span>{{ (pageNum - 1) * pageSize + scope.$index + 1 }}</span>
|
<span>{{ (pageNum - 1) * pageSize + scope.$index + 1 }}</span>
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
<el-table-column type="selection" :reserve-selection="true" width="55"></el-table-column>
|
<el-table-column type="selection" :reserve-selection="true" :selectable="checkSelectable" width="55"></el-table-column>
|
||||||
<el-table-column label="角色编号" align="center" prop="roleId" />
|
<el-table-column label="角色编号" align="center" prop="roleId" />
|
||||||
<el-table-column label="角色名称" align="center" prop="roleName" />
|
<el-table-column label="角色名称" align="center" prop="roleName" />
|
||||||
<el-table-column label="权限字符" align="center" prop="roleKey" />
|
<el-table-column label="权限字符" align="center" prop="roleKey" />
|
||||||
|
|
@ -65,7 +65,9 @@ const form = ref({
|
||||||
|
|
||||||
/** 单击选中行数据 */
|
/** 单击选中行数据 */
|
||||||
function clickRow(row) {
|
function clickRow(row) {
|
||||||
proxy.$refs["roleRef"].toggleRowSelection(row);
|
if (checkSelectable(row)) {
|
||||||
|
proxy.$refs["roleRef"].toggleRowSelection(row);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
/** 多选框选中数据 */
|
/** 多选框选中数据 */
|
||||||
|
|
@ -78,6 +80,11 @@ function getRowKey(row) {
|
||||||
return row.roleId;
|
return row.roleId;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// 检查角色状态
|
||||||
|
function checkSelectable(row) {
|
||||||
|
return row.status === "0" ? true : false;
|
||||||
|
};
|
||||||
|
|
||||||
/** 关闭按钮 */
|
/** 关闭按钮 */
|
||||||
function close() {
|
function close() {
|
||||||
const obj = { path: "/system/user" };
|
const obj = { path: "/system/user" };
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,107 @@
|
||||||
|
<template>
|
||||||
|
<div class="app-container">
|
||||||
|
<el-row :gutter="20">
|
||||||
|
<el-col :span="6" :xs="24">
|
||||||
|
<el-card class="box-card">
|
||||||
|
<template #header>
|
||||||
|
<div class="card-header">
|
||||||
|
<span>个人信息</span>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<div>
|
||||||
|
<div class="text-center">
|
||||||
|
<userAvatar :user="user" />
|
||||||
|
</div>
|
||||||
|
<ul class="list-group list-group-striped">
|
||||||
|
<li class="list-group-item">
|
||||||
|
<user :user="user" />
|
||||||
|
</li>
|
||||||
|
<li class="list-group-item">
|
||||||
|
<span>手机号码</span>
|
||||||
|
<div>{{ user.phonenumber }}</div>
|
||||||
|
</li>
|
||||||
|
<li class="list-group-item">
|
||||||
|
<span>邮箱</span>
|
||||||
|
<div>{{ user.email }}</div>
|
||||||
|
</li>
|
||||||
|
<li class="list-group-item">
|
||||||
|
<span>创建时间</span>
|
||||||
|
<div>{{ user.createTime }}</div>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</el-card>
|
||||||
|
</el-col>
|
||||||
|
|
||||||
|
<el-col :span="18" :xs="24">
|
||||||
|
<el-card>
|
||||||
|
<template #header>
|
||||||
|
<div class="card-header">
|
||||||
|
<span>基本资料</span>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<el-tabs v-model="activeTab">
|
||||||
|
<el-tab-pane label="基本资料" name="userinfo">
|
||||||
|
<userInfo :user="user" />
|
||||||
|
</el-tab-pane>
|
||||||
|
<el-tab-pane label="修改密码" name="resetPwd">
|
||||||
|
<resetPwd :user="user" />
|
||||||
|
</el-tab-pane>
|
||||||
|
</el-tabs>
|
||||||
|
</el-card>
|
||||||
|
</el-col>
|
||||||
|
</el-row>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup name="Profile">
|
||||||
|
import { ref } from 'vue'
|
||||||
|
import userAvatar from './userAvatar.vue'
|
||||||
|
import userInfo from './userInfo.vue'
|
||||||
|
import resetPwd from './resetPwd.vue'
|
||||||
|
|
||||||
|
const activeTab = ref("userinfo")
|
||||||
|
const user = ref({
|
||||||
|
userId: 1,
|
||||||
|
deptId: 103,
|
||||||
|
userName: "admin",
|
||||||
|
nickName: "管理员",
|
||||||
|
email: "admin@163.com",
|
||||||
|
phonenumber: "15888888888",
|
||||||
|
sex: "1",
|
||||||
|
avatar: "",
|
||||||
|
status: "0",
|
||||||
|
loginIp: "127.0.0.1",
|
||||||
|
createTime: "2024-03-15 10:00:00"
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.box-card {
|
||||||
|
.card-header {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.list-group-striped {
|
||||||
|
padding: 0;
|
||||||
|
list-style: none;
|
||||||
|
|
||||||
|
.list-group-item {
|
||||||
|
border-bottom: 1px solid #eee;
|
||||||
|
padding: 10px 0;
|
||||||
|
|
||||||
|
&:last-child {
|
||||||
|
border-bottom: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
span {
|
||||||
|
color: #666;
|
||||||
|
display: inline-block;
|
||||||
|
width: 100px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
@ -0,0 +1,62 @@
|
||||||
|
<template>
|
||||||
|
<el-form ref="pwdRef" :model="user" :rules="rules" label-width="100px">
|
||||||
|
<el-form-item label="旧密码" prop="oldPassword">
|
||||||
|
<el-input v-model="user.oldPassword" placeholder="请输入旧密码" type="password" show-password />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="新密码" prop="newPassword">
|
||||||
|
<el-input v-model="user.newPassword" placeholder="请输入新密码" type="password" show-password />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="确认密码" prop="confirmPassword">
|
||||||
|
<el-input v-model="user.confirmPassword" placeholder="请确认密码" type="password" show-password />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item>
|
||||||
|
<el-button type="primary" @click="submit">保存</el-button>
|
||||||
|
<el-button type="danger" @click="close">关闭</el-button>
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import { ref } from 'vue'
|
||||||
|
import { ElMessage } from 'element-plus'
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
user: {
|
||||||
|
type: Object,
|
||||||
|
required: true
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
const rules = ref({
|
||||||
|
oldPassword: [
|
||||||
|
{ required: true, message: "旧密码不能为空", trigger: "blur" }
|
||||||
|
],
|
||||||
|
newPassword: [
|
||||||
|
{ required: true, message: "新密码不能为空", trigger: "blur" },
|
||||||
|
{ min: 6, message: "新密码长度不能小于6位", trigger: "blur" }
|
||||||
|
],
|
||||||
|
confirmPassword: [
|
||||||
|
{ required: true, message: "确认密码不能为空", trigger: "blur" },
|
||||||
|
{
|
||||||
|
validator: (rule, value, callback) => {
|
||||||
|
if (value !== props.user.newPassword) {
|
||||||
|
callback(new Error("两次输入的密码不一致"));
|
||||||
|
} else {
|
||||||
|
callback();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
trigger: "blur"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
})
|
||||||
|
|
||||||
|
const submit = () => {
|
||||||
|
// 模拟提交
|
||||||
|
ElMessage.success("修改成功")
|
||||||
|
}
|
||||||
|
|
||||||
|
const close = () => {
|
||||||
|
// 模拟关闭
|
||||||
|
ElMessage.info("已取消")
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
@ -0,0 +1,53 @@
|
||||||
|
<template>
|
||||||
|
<div class="user-profile">
|
||||||
|
<div class="box-center">
|
||||||
|
<div class="user-bio">
|
||||||
|
<div class="user-education user-bio-section">
|
||||||
|
<div class="user-bio-section-header">
|
||||||
|
<svg-icon icon-class="user" />
|
||||||
|
<span>头像</span>
|
||||||
|
</div>
|
||||||
|
<div class="user-bio-section-body">
|
||||||
|
<div class="text-center">
|
||||||
|
<el-avatar :size="100" :src="user.avatar || require('@/assets/images/profile.jpg')" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
defineProps({
|
||||||
|
user: {
|
||||||
|
type: Object,
|
||||||
|
required: true
|
||||||
|
}
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.user-profile {
|
||||||
|
.box-center {
|
||||||
|
margin: 0 auto;
|
||||||
|
display: table;
|
||||||
|
}
|
||||||
|
|
||||||
|
.user-bio {
|
||||||
|
margin-top: 20px;
|
||||||
|
color: #606266;
|
||||||
|
|
||||||
|
.user-bio-section {
|
||||||
|
margin-bottom: 15px;
|
||||||
|
|
||||||
|
.user-bio-section-header {
|
||||||
|
border-bottom: 1px solid #dfe6ec;
|
||||||
|
padding-bottom: 10px;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
@ -0,0 +1,62 @@
|
||||||
|
<template>
|
||||||
|
<el-form ref="userRef" :model="user" :rules="rules" label-width="80px">
|
||||||
|
<el-form-item label="用户名称" prop="userName">
|
||||||
|
<el-input v-model="user.userName" disabled />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="用户昵称" prop="nickName">
|
||||||
|
<el-input v-model="user.nickName" placeholder="请输入用户昵称" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="手机号码" prop="phonenumber">
|
||||||
|
<el-input v-model="user.phonenumber" placeholder="请输入手机号码" maxlength="11" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="邮箱" prop="email">
|
||||||
|
<el-input v-model="user.email" placeholder="请输入邮箱" maxlength="50" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="性别">
|
||||||
|
<el-radio-group v-model="user.sex">
|
||||||
|
<el-radio label="0">男</el-radio>
|
||||||
|
<el-radio label="1">女</el-radio>
|
||||||
|
</el-radio-group>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item>
|
||||||
|
<el-button type="primary" @click="submit">保存</el-button>
|
||||||
|
<el-button type="danger" @click="close">关闭</el-button>
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import { ref } from 'vue'
|
||||||
|
import { ElMessage } from 'element-plus'
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
user: {
|
||||||
|
type: Object,
|
||||||
|
required: true
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
const rules = ref({
|
||||||
|
nickName: [
|
||||||
|
{ required: true, message: "用户昵称不能为空", trigger: "blur" }
|
||||||
|
],
|
||||||
|
email: [
|
||||||
|
{ required: true, message: "邮箱地址不能为空", trigger: "blur" },
|
||||||
|
{ type: "email", message: "请输入正确的邮箱地址", trigger: "blur" }
|
||||||
|
],
|
||||||
|
phonenumber: [
|
||||||
|
{ required: true, message: "手机号码不能为空", trigger: "blur" },
|
||||||
|
{ pattern: /^1[3|4|5|6|7|8|9][0-9]\d{8}$/, message: "请输入正确的手机号码", trigger: "blur" }
|
||||||
|
]
|
||||||
|
})
|
||||||
|
|
||||||
|
const submit = () => {
|
||||||
|
// 模拟提交
|
||||||
|
ElMessage.success("修改成功")
|
||||||
|
}
|
||||||
|
|
||||||
|
const close = () => {
|
||||||
|
// 模拟关闭
|
||||||
|
ElMessage.info("已取消")
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
@ -23,6 +23,21 @@ export default defineConfig(({ mode, command }) => {
|
||||||
// https://cn.vitejs.dev/config/#resolve-extensions
|
// https://cn.vitejs.dev/config/#resolve-extensions
|
||||||
extensions: ['.mjs', '.js', '.ts', '.jsx', '.tsx', '.json', '.vue']
|
extensions: ['.mjs', '.js', '.ts', '.jsx', '.tsx', '.json', '.vue']
|
||||||
},
|
},
|
||||||
|
// 打包配置
|
||||||
|
build: {
|
||||||
|
// https://vite.dev/config/build-options.html
|
||||||
|
sourcemap: command === 'build' ? false : 'inline',
|
||||||
|
outDir: 'dist',
|
||||||
|
assetsDir: 'assets',
|
||||||
|
chunkSizeWarningLimit: 2000,
|
||||||
|
rollupOptions: {
|
||||||
|
output: {
|
||||||
|
chunkFileNames: 'static/js/[name]-[hash].js',
|
||||||
|
entryFileNames: 'static/js/[name]-[hash].js',
|
||||||
|
assetFileNames: 'static/[ext]/[name]-[hash].[ext]'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
// vite 相关配置
|
// vite 相关配置
|
||||||
server: {
|
server: {
|
||||||
port: 80,
|
port: 80,
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue