Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6b3112b547 | ||
|
|
ad93451897 | ||
|
|
55ca703755 | ||
|
|
8ef70b7ee8 | ||
|
|
ea6208ba8a | ||
|
|
011f54cf9c | ||
|
|
6d0b76e41e | ||
|
|
ba176fe8e4 | ||
|
|
b582f5c73b | ||
|
|
b80265f3e8 | ||
|
|
9073b8310e | ||
|
|
6a624d9a3a | ||
|
|
e8f5f0b85a | ||
|
|
9d099007c6 | ||
|
|
7e255d6a66 | ||
|
|
185429a94c | ||
|
|
4383fa69de | ||
|
|
78426364f3 | ||
|
|
6b54fb10a2 | ||
|
|
3ab7f108cf | ||
|
|
c695a89cc4 | ||
|
|
0a17d3589e | ||
|
|
362ffecd24 | ||
|
|
bb276d26c2 | ||
|
|
3f8e33c35c | ||
|
|
2d00c4b518 | ||
|
|
643f68f26c | ||
|
|
04b99e7a68 | ||
|
|
0ae4b70f03 | ||
|
|
ccb4379b73 | ||
|
|
9a725515ec | ||
|
|
b03598b061 | ||
|
|
dbb9d1475b | ||
|
|
84deb1f932 | ||
|
|
d09e144aaf | ||
|
|
96e5df3a45 | ||
|
|
d9f5eb8958 | ||
|
|
c4cad4c2bf | ||
|
|
ee3c6e8f37 | ||
|
|
3d4b033c1c | ||
|
|
975efc36e1 | ||
|
|
eecb7aa9d4 | ||
|
|
238a089f4f | ||
|
|
f224bdd440 | ||
|
|
a329869e65 | ||
|
|
d657eaf56d | ||
|
|
78233fa7d9 | ||
|
|
98417ecbab | ||
|
|
40406b7a81 | ||
|
|
2538f2b7aa | ||
|
|
5fda0a03cb | ||
|
|
2901e631fa | ||
|
|
d75b193221 | ||
|
|
5289380ef5 | ||
|
|
5fc99c6d84 | ||
|
|
017e64125d | ||
|
|
455bafdeca | ||
|
|
ff457233e9 | ||
|
|
6d1b19d838 | ||
|
|
b7cc8bac61 | ||
|
|
d099a26534 | ||
|
|
da15eabeed | ||
|
|
fc26893525 | ||
|
|
1851d57f7f | ||
|
|
3bb53d3450 | ||
|
|
352f15a832 | ||
|
|
b0247d5efc | ||
|
|
01aafbd872 | ||
|
|
990155682d | ||
|
|
99b55452c7 | ||
|
|
3be482c569 | ||
|
|
2101e6065d |
@@ -0,0 +1,24 @@
|
||||
# Logs
|
||||
logs
|
||||
*.log
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
pnpm-debug.log*
|
||||
lerna-debug.log*
|
||||
|
||||
node_modules
|
||||
dist
|
||||
dist-ssr
|
||||
*.local
|
||||
|
||||
# Editor directories and files
|
||||
.vscode/*
|
||||
!.vscode/extensions.json
|
||||
.idea
|
||||
.DS_Store
|
||||
*.suo
|
||||
*.ntvs*
|
||||
*.njsproj
|
||||
*.sln
|
||||
*.sw?
|
||||
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"hash": "b40a7241",
|
||||
"configHash": "e4e9a696",
|
||||
"lockfileHash": "e3b0c442",
|
||||
"browserHash": "3514e43b",
|
||||
"optimized": {},
|
||||
"chunks": {}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
{
|
||||
"type": "module"
|
||||
}
|
||||
@@ -1,92 +1,95 @@
|
||||
# 管理端重庆创兴佳维修微信小程序-后台管理
|
||||
#💐💐💐 Vue 3.2 + vuex + Vite2 + Element-Plus 后台管理系统
|
||||
|
||||

|
||||
|
||||
### 前言 📖
|
||||
|
||||
## Getting started
|
||||
项目基于 Vue3.2、Javascript、Vite2、vuex、Element-Plus 开发的一套后台管理模板,目前利用空余时间开发 🕒🕒🕒🕒。项目中借鉴很多优秀的后台管理系统,从 0 开始搭建项目(其中 ui 图是借鉴了【即时设计】里的优秀的设计图),整个项目还有很多地方不完善,后期会持续更新,希望大家能多提意见。
|
||||
|
||||
To make it easy for you to get started with GitLab, here's a list of recommended next steps.
|
||||
### ?为什么开发这个管理系统 👩🔬
|
||||
|
||||
Already a pro? Just edit this README.md and make it your own. Want to make it easy? [Use the template at the bottom](#editing-this-readme)!
|
||||
- 主要是想把自己常用的组件进行一个封装 二次巩固
|
||||
- 目前看了很多优秀的 Vue3 的开源后台管理系统,感觉都非常庞大(想开发一套适合自己的)
|
||||
|
||||
## Add your files
|
||||
### 一、在线预览 🛫
|
||||
|
||||
- [ ] [Create](https://docs.gitlab.com/ee/user/project/repository/web_editor.html#create-a-file) or [upload](https://docs.gitlab.com/ee/user/project/repository/web_editor.html#upload-a-file) files
|
||||
- [ ] [Add files using the command line](https://docs.gitlab.com/ee/gitlab-basics/add-file.html#add-a-file-using-the-command-line) or push an existing Git repository with the following command:
|
||||
- Link:http://124.221.156.158:9797/#/login
|
||||
|
||||
### 二、gitee 地址 🛫
|
||||
|
||||
- Link:https://gitee.com/tanzo/vue-manage-system
|
||||
|
||||
### 三、本地预览 🛫🛫🛫
|
||||
|
||||
```
|
||||
cd existing_repo
|
||||
git remote add origin http://gitlab.lzyyj.com/hzd/backstage-repair.git
|
||||
git branch -M main
|
||||
git push -uf origin main
|
||||
git clone 项目地址
|
||||
|
||||
npm install (依赖安装失败可以尝试cnpm install)
|
||||
|
||||
npm run dev
|
||||
```
|
||||
|
||||
## Integrate with your tools
|
||||
### 四、🔨 项目功能
|
||||
|
||||
- [ ] [Set up project integrations](http://gitlab.lzyyj.com/hzd/backstage-repair/-/settings/integrations)
|
||||
- 🚀 使用 Vue3.2 开发,单文件组件 `<script setup>`开发
|
||||
- 🚀 采用动态路由菜单模式
|
||||
- 🚀 数据模拟用到了 MockJS
|
||||
- 🚀 使用 Vuex getter 模块化管理( 加入了持久化插件)
|
||||
- 🚀 Axios 的二次封装 (全局错误拦截、常用请求封装、全局请求 Loading、取消重复请求……)
|
||||
- 🚀 对 el-table 组件进行的二次封装
|
||||
- 🚀 使用 vue-router 进行路由权限拦截(403 页面)、页面按钮权限配置、路由懒加载
|
||||
- 🚀 使用 keep-alive 对整个页面进行缓存,支持多级嵌套页面(缓存路由里可配置、页面切换加入动画效果)
|
||||
- 🚀 常用自定义指令的封装 直接 v-copy 即可实现复制 (复制、拖拽、、、等等)
|
||||
- 🚀 vite.config.js 打包配置(静态资源合并打包/清除 log/gzip 压缩配置等)
|
||||
|
||||
## Collaborate with your team
|
||||
### 五、项目截图 📷
|
||||
|
||||
- [ ] [Invite team members and collaborators](https://docs.gitlab.com/ee/user/project/members/)
|
||||
- [ ] [Create a new merge request](https://docs.gitlab.com/ee/user/project/merge_requests/creating_merge_requests.html)
|
||||
- [ ] [Automatically close issues from merge requests](https://docs.gitlab.com/ee/user/project/issues/managing_issues.html#closing-issues-automatically)
|
||||
- [ ] [Enable merge request approvals](https://docs.gitlab.com/ee/user/project/merge_requests/approvals/)
|
||||
- [ ] [Automatically merge when pipeline succeeds](https://docs.gitlab.com/ee/user/project/merge_requests/merge_when_pipeline_succeeds.html)
|
||||
#### 1、登录页:
|
||||
|
||||
## Test and Deploy
|
||||

|
||||
|
||||
Use the built-in continuous integration in GitLab.
|
||||
#### 2、首页:
|
||||
|
||||
- [ ] [Get started with GitLab CI/CD](https://docs.gitlab.com/ee/ci/quick_start/index.html)
|
||||
- [ ] [Analyze your code for known vulnerabilities with Static Application Security Testing(SAST)](https://docs.gitlab.com/ee/user/application_security/sast/)
|
||||
- [ ] [Deploy to Kubernetes, Amazon EC2, or Amazon ECS using Auto Deploy](https://docs.gitlab.com/ee/topics/autodevops/requirements.html)
|
||||
- [ ] [Use pull-based deployments for improved Kubernetes management](https://docs.gitlab.com/ee/user/clusters/agent/)
|
||||
- [ ] [Set up protected environments](https://docs.gitlab.com/ee/ci/environments/protected_environments.html)
|
||||

|
||||
|
||||
***
|
||||
#### 3、表格页:
|
||||
|
||||
# Editing this README
|
||||

|
||||
|
||||
When you're ready to make this README your own, just edit this file and use the handy template below (or feel free to structure it however you want - this is just a starting point!). Thank you to [makeareadme.com](https://www.makeareadme.com/) for this template.
|
||||
#### 4、流程图:
|
||||
|
||||
## Suggestions for a good README
|
||||
Every project is different, so consider which of these sections apply to yours. The sections used in the template are suggestions for most open source projects. Also keep in mind that while a README can be too long and detailed, too long is better than too short. If you think your README is too long, consider utilizing another form of documentation rather than cutting out information.
|
||||

|
||||
|
||||
## Name
|
||||
Choose a self-explaining name for your project.
|
||||
### 六、文件资源目录 📚
|
||||
|
||||
## Description
|
||||
Let people know what your project can do specifically. Provide context and add a link to any reference visitors might be unfamiliar with. A list of Features or a Background subsection can also be added here. If there are alternatives to your project, this is a good place to list differentiating factors.
|
||||
```text
|
||||
├─ .vscode # vscode推荐配置
|
||||
├─ public # 静态资源文件(忽略打包)
|
||||
├─ src
|
||||
│ ├─ api # API 接口管理
|
||||
│ ├─ assets # 静态资源文件
|
||||
│ ├─ components # 全局组件
|
||||
│ ├─ config # 全局配置项
|
||||
│ ├─ instruction # 指令文件
|
||||
│ ├─ layout # 框架布局
|
||||
│ ├─ router # 路由管理
|
||||
│ ├─ store # VUEX模块化管理
|
||||
│ ├─ styles # 全局样式
|
||||
│ ├─ utils # 工具库(color函数的封装)
|
||||
│ ├─ views # 项目所有页面
|
||||
│ ├─ App.vue # 入口页面
|
||||
│ └─ main.js # 入口文件
|
||||
└─ vite.config.ts # vite 配置
|
||||
```
|
||||
|
||||
## Badges
|
||||
On some READMEs, you may see small images that convey metadata, such as whether or not all the tests are passing for the project. You can use Shields to add some to your README. Many services also have instructions for adding a badge.
|
||||
### 七、浏览器支持
|
||||
|
||||
## Visuals
|
||||
Depending on what you are making, it can be a good idea to include screenshots or even a video (you'll frequently see GIFs rather than actual videos). Tools like ttygif can help, but check out Asciinema for a more sophisticated method.
|
||||
> vue3.2 不支持 IE 浏览器。其他还行吧(其实我没测 🤣🧐)
|
||||
|
||||
## Installation
|
||||
Within a particular ecosystem, there may be a common way of installing things, such as using Yarn, NuGet, or Homebrew. However, consider the possibility that whoever is reading your README is a novice and would like more guidance. Listing specific steps helps remove ambiguity and gets people to using your project as quickly as possible. If it only runs in a specific context like a particular programming language version or operating system or has dependencies that have to be installed manually, also add a Requirements subsection.
|
||||
### 八、项目后台接口 🧩🧐
|
||||
|
||||
## Usage
|
||||
Use examples liberally, and show the expected output if you can. It's helpful to have inline the smallest example of usage that you can demonstrate, while providing links to more sophisticated examples if they are too long to reasonably include in the README.
|
||||
> 项目后台接口全部采用 Mock 数据
|
||||
|
||||
## Support
|
||||
Tell people where they can go to for help. It can be any combination of an issue tracker, a chat room, an email address, etc.
|
||||
### 九 最后求个关注 👀 🌟🌟🌟🙇♀️🙇♀️🙇♀️
|
||||
|
||||
## Roadmap
|
||||
If you have ideas for releases in the future, it is a good idea to list them in the README.
|
||||
|
||||
## Contributing
|
||||
State if you are open to contributions and what your requirements are for accepting them.
|
||||
|
||||
For people who want to make changes to your project, it's helpful to have some documentation on how to get started. Perhaps there is a script that they should run or some environment variables that they need to set. Make these steps explicit. These instructions could also be useful to your future self.
|
||||
|
||||
You can also document commands to lint the code or run tests. These steps help to ensure high code quality and reduce the likelihood that the changes inadvertently break something. Having instructions for running tests is especially helpful if it requires external setup, such as starting a Selenium server for testing in a browser.
|
||||
|
||||
## Authors and acknowledgment
|
||||
Show your appreciation to those who have contributed to the project.
|
||||
|
||||
## License
|
||||
For open source projects, say how it is licensed.
|
||||
|
||||
## Project status
|
||||
If you have run out of energy or time for your project, put a note at the top of the README saying that development has slowed down or stopped completely. Someone may choose to fork your project or volunteer to step in as a maintainer or owner, allowing your project to keep going. You can also make an explicit request for maintainers.
|
||||

|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<link rel="icon" href="/favicon.ico" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<link href="//at.alicdn.com/t/c/font_3654542_filhj6brvds.css" />
|
||||
<title>后台管理系统</title>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div id="app"></div>
|
||||
<script type="module" src="/src/main.js"></script>
|
||||
<!-- 百度地图引入 -->
|
||||
<!-- <script type="text/javascript"
|
||||
src="https://api.map.baidu.com/api?v=1.0&type=webgl&ak=Ol0d1CqPErteakupIcX4actXo6GiQI7g"></script> -->
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,50 @@
|
||||
{
|
||||
"name": "vue-manage-system",
|
||||
"version": "0.0.0",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"build": "vite build",
|
||||
"dev": "vite",
|
||||
"preview": "vite preview"
|
||||
},
|
||||
"dependencies": {
|
||||
"@braks/vue-flow": "^0.4.38",
|
||||
"@element-plus/icons-vue": "^1.1.4",
|
||||
"@hellowuxin/mindmap": "^2.1.4",
|
||||
"@kangc/v-md-editor": "^2.3.15",
|
||||
"@wangeditor/editor": "^5.1.14",
|
||||
"@wangeditor/editor-for-vue": "^5.1.12",
|
||||
"axios": "^0.27.2",
|
||||
"driver.js": "^0.9.8",
|
||||
"echarts": "^5.3.3",
|
||||
"element-plus": "^2.1.11",
|
||||
"js-base64": "^3.7.2",
|
||||
"jsmind": "^0.6.4",
|
||||
"markmap-common": "^0.15.0",
|
||||
"markmap-lib": "^0.15.2",
|
||||
"markmap-toolbar": "^0.15.0",
|
||||
"markmap-view": "^0.15.0",
|
||||
"mockjs": "^1.1.0",
|
||||
"nprogress": "^0.2.0",
|
||||
"screenfull": "^6.0.1",
|
||||
"sortablejs": "^1.15.0",
|
||||
"video.js": "^7.20.3",
|
||||
"vue": "^3.2.25",
|
||||
"vue-router": "^4.0.13",
|
||||
"vue-video-player": "^6.0.0",
|
||||
"vue3-mindmap": "^0.5.12",
|
||||
"vue3-print-nb": "^0.1.4",
|
||||
"vuedraggable": "^4.1.0",
|
||||
"vuex": "^4.0.2",
|
||||
"vuex-persistedstate": "^4.1.0",
|
||||
"xlsx": "^0.18.5"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@vitejs/plugin-vue": "^2.3.1",
|
||||
"sass": "^1.79.5",
|
||||
"vite": "^2.9.7",
|
||||
"vite-plugin-compression": "^0.5.1",
|
||||
"vite-plugin-mock": "^2.9.6",
|
||||
"vue-cli-plugin-style-resources-loader": "~0.1.5"
|
||||
}
|
||||
}
|
||||
|
After Width: | Height: | Size: 4.2 KiB |
|
After Width: | Height: | Size: 224 KiB |
|
After Width: | Height: | Size: 192 KiB |
@@ -0,0 +1,25 @@
|
||||
<template>
|
||||
<router-view v-if="isRouterAlive"></router-view>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { onMounted, ref, nextTick, provide } from "vue";
|
||||
import { useStore } from "vuex";
|
||||
// 局部组件刷新
|
||||
const isRouterAlive = ref(true);
|
||||
const globalStore = useStore();
|
||||
const reload = () => {
|
||||
isRouterAlive.value = false;
|
||||
nextTick(() => {
|
||||
isRouterAlive.value = true;
|
||||
console.log("数据刷新成功");
|
||||
});
|
||||
};
|
||||
|
||||
provide("reload", reload);
|
||||
onMounted(() => {
|
||||
globalStore.dispatch("user/changeThem", "#4060c7");
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss"></style>
|
||||
@@ -0,0 +1,9 @@
|
||||
import service from "../request.js";
|
||||
|
||||
export function updateAdmin(params) {
|
||||
return service({
|
||||
method: "put",
|
||||
url: '/sys/user/password',
|
||||
data: params,
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
import service from "../request.js";
|
||||
|
||||
const API_BASE_URL = '/application';
|
||||
|
||||
// 待审核列表
|
||||
export function getApplicationList(query) {
|
||||
return service({
|
||||
method: "get",
|
||||
url: `${API_BASE_URL}/list`,
|
||||
data: query,
|
||||
});
|
||||
}
|
||||
|
||||
// 通过
|
||||
export function approveApplication(query) {
|
||||
return service({
|
||||
method: "get",
|
||||
url: `${API_BASE_URL}/approve`,
|
||||
data: query,
|
||||
});
|
||||
}
|
||||
|
||||
// 拒绝
|
||||
export function rejectApplication(query) {
|
||||
return service({
|
||||
method: "get",
|
||||
url: `${API_BASE_URL}/reject`,
|
||||
data: query,
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
import service from "../request.js";
|
||||
const API_BASE_URL = '/goods';
|
||||
|
||||
// 服务项目列表
|
||||
export function getGoodsList(query) {
|
||||
return service({
|
||||
method: "get",
|
||||
url: `${API_BASE_URL}/list`,
|
||||
data: query,
|
||||
});
|
||||
}
|
||||
|
||||
// 添加服务项目
|
||||
export function addGoods(query) {
|
||||
return service({
|
||||
method: "post",
|
||||
url: `${API_BASE_URL}`,
|
||||
data: query,
|
||||
});
|
||||
}
|
||||
|
||||
// 更新服务项目
|
||||
export function updateGoods(query) {
|
||||
return service({
|
||||
method: "put",
|
||||
url: `${API_BASE_URL}`,
|
||||
data: query,
|
||||
});
|
||||
}
|
||||
|
||||
// 删除服务项目
|
||||
export function delGoods(query) {
|
||||
return service({
|
||||
method: "delete",
|
||||
url: `${API_BASE_URL}/${query}`,
|
||||
data: query,
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
//src/api/user/index.js
|
||||
|
||||
import service from "../request.js";
|
||||
|
||||
export function LoginInfo(query) {
|
||||
return service({
|
||||
method: "POST",
|
||||
url: "/login",
|
||||
data: query,
|
||||
});
|
||||
}
|
||||
export function getMenuList(query) {
|
||||
return service({
|
||||
method: "get",
|
||||
url: "/permission/getMenuList",
|
||||
data: query,
|
||||
});
|
||||
}
|
||||
|
||||
export function getUserList(query) {
|
||||
return service({
|
||||
method: "get",
|
||||
url: "/permission/UserList",
|
||||
data: query,
|
||||
});
|
||||
}
|
||||
export function addUserList(query) {
|
||||
return service({
|
||||
method: "post",
|
||||
url: "/permission/addUserList",
|
||||
data: query,
|
||||
});
|
||||
}
|
||||
export function listUpdate(query) {
|
||||
return service({
|
||||
method: "post",
|
||||
url: "/permission/listUpdate",
|
||||
data: query,
|
||||
});
|
||||
}
|
||||
export function Newslist(query) {
|
||||
return service({
|
||||
method: "get",
|
||||
url: "/permission/Newslist",
|
||||
data: query,
|
||||
});
|
||||
}
|
||||
export function orderLists(query) {
|
||||
return service({
|
||||
method: "get",
|
||||
url: "/permission/orderLists",
|
||||
data: query,
|
||||
});
|
||||
}
|
||||
export function homeList(query) {
|
||||
return service({
|
||||
method: "get",
|
||||
url: "/permission/homeList",
|
||||
data: query,
|
||||
});
|
||||
}
|
||||
export function noticeLists(query) {
|
||||
return service({
|
||||
method: "get",
|
||||
url: "/permission/noticeLists",
|
||||
data: query,
|
||||
});
|
||||
}
|
||||
export function cardlists(query) {
|
||||
return service({
|
||||
method: "get",
|
||||
url: "/permission/cardlists",
|
||||
data: query,
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
import service from "../request.js";
|
||||
|
||||
const API_BASE_URL = '/order';
|
||||
|
||||
// 订单列表
|
||||
export function getOrderList(query) {
|
||||
return service({
|
||||
method: "get",
|
||||
url: `${API_BASE_URL}/all/list`,
|
||||
params: query,
|
||||
});
|
||||
}
|
||||
|
||||
// 导出订单
|
||||
export function exportOrder(query) {
|
||||
|
||||
const urlParams = new URLSearchParams(query);
|
||||
return `https://api.cqcxj.cn/order/list/export?${urlParams.toString()}`;
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
import service from "../request.js";
|
||||
|
||||
const API_BASE_URL = '/application';
|
||||
|
||||
// 工作岗位资格申请审批管理
|
||||
export function getApplyList(query) {
|
||||
return service({
|
||||
method: "get",
|
||||
url: `${API_BASE_URL}/list`,
|
||||
params: query,
|
||||
});
|
||||
}
|
||||
|
||||
// 工作岗位资格申请拒绝
|
||||
export function reject(applicationId) {
|
||||
return service({
|
||||
method: "put",
|
||||
url: `${API_BASE_URL}/reject/`+applicationId
|
||||
});
|
||||
}
|
||||
|
||||
// 工作岗位资格申请通过
|
||||
export function approve(applicationId) {
|
||||
return service({
|
||||
method: "put",
|
||||
url: `${API_BASE_URL}/approve/`+applicationId
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
import service from "../request.js";
|
||||
const API_BASE_URL = '/schedule';
|
||||
|
||||
// 获取排班人员吗
|
||||
export function getScheduleList(params) {
|
||||
return service({
|
||||
method: "get",
|
||||
url: `${API_BASE_URL}/${params.timeQuantumId}?&date=${params.date}&roleId=${params.roleId}`,
|
||||
data: params
|
||||
});
|
||||
}
|
||||
|
||||
// 添加排班
|
||||
export function addSchedule(params) {
|
||||
return service({
|
||||
method: "post",
|
||||
url: `${API_BASE_URL}`,
|
||||
data: params,
|
||||
});
|
||||
}
|
||||
|
||||
// 修改排班
|
||||
export function updateSchedule(params) {
|
||||
return service({
|
||||
method: "put",
|
||||
url: `${API_BASE_URL}`,
|
||||
data: params,
|
||||
});
|
||||
}
|
||||
|
||||
// 删除排班
|
||||
export function delSchedule(params) {
|
||||
return service({
|
||||
method: "delete",
|
||||
url: `${API_BASE_URL}/${params.id}`,
|
||||
data: params,
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
import service from "../request.js";
|
||||
const API_BASE_URL = '/time/quantum';
|
||||
|
||||
// 获取排班时段列表
|
||||
export function listQuantum() {
|
||||
return service({
|
||||
method: "get",
|
||||
url: `${API_BASE_URL}`
|
||||
});
|
||||
}
|
||||
|
||||
// 修改排班时段
|
||||
export function addQuantum(params) {
|
||||
return service({
|
||||
method: "post",
|
||||
url: `${API_BASE_URL}`,
|
||||
data: params,
|
||||
});
|
||||
}
|
||||
|
||||
// 修改排班时段
|
||||
export function updateQuantum(params) {
|
||||
return service({
|
||||
method: "put",
|
||||
url: `${API_BASE_URL}`,
|
||||
data: params,
|
||||
});
|
||||
}
|
||||
|
||||
// 删除排班时段
|
||||
export function delQuantum(params) {
|
||||
return service({
|
||||
method: "delete",
|
||||
url: `${API_BASE_URL}/${params.id}`
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
import service from "../request.js";
|
||||
const API_BASE_URL = '/user';
|
||||
|
||||
// 查询勘测人员列表
|
||||
export function listSurveyorsUser(query) {
|
||||
return service({
|
||||
method: "get",
|
||||
url: `${API_BASE_URL}/surveyors`,
|
||||
params: query
|
||||
});
|
||||
}
|
||||
|
||||
// 查询勘测人员列表
|
||||
export function listMaintenanceUser(query) {
|
||||
return service({
|
||||
method: "get",
|
||||
url: `${API_BASE_URL}/maintenance`,
|
||||
params: query
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
import service from "../request.js";
|
||||
const API_BASE_URL = '/config';
|
||||
|
||||
// 修改配置
|
||||
export function updateSystem(params) {
|
||||
return service({
|
||||
method: "put",
|
||||
url: `${API_BASE_URL}`,
|
||||
data: params,
|
||||
});
|
||||
}
|
||||
|
||||
// 获取配置信息列表(全部配置)
|
||||
export function getConfigList(query) {
|
||||
return service({
|
||||
method: "get",
|
||||
url: `${API_BASE_URL}/list`,
|
||||
data: query,
|
||||
});
|
||||
}
|
||||
|
||||
// 更新单个配置项内容(仅更新内容字段)
|
||||
export function updateConfig(query) {
|
||||
return service({
|
||||
method: "put",
|
||||
url: `${API_BASE_URL}`,
|
||||
data: query,
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
import service from "../request.js";
|
||||
const API_BASE_URL = '/file';
|
||||
|
||||
// 上传文件
|
||||
export function addFile(formData) {
|
||||
return service({
|
||||
method: "post",
|
||||
url: `${API_BASE_URL}`,
|
||||
data: formData
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
import service from "../request.js";
|
||||
|
||||
const API_BASE_URL = '/user';
|
||||
|
||||
// 客户列表
|
||||
export function getUserList(roleld, query) {
|
||||
return service({
|
||||
method: "get",
|
||||
url: `${API_BASE_URL}/${roleld}`,
|
||||
params: query,
|
||||
});
|
||||
}
|
||||
|
||||
// 获取勘测人员列表
|
||||
export function getSurveyorsList(query) {
|
||||
return service({
|
||||
method: "get",
|
||||
url: `${API_BASE_URL}/surveyors`,
|
||||
data: query,
|
||||
});
|
||||
}
|
||||
|
||||
// 获取维修人员列表
|
||||
export function getMaintenanceList(query) {
|
||||
return service({
|
||||
method: "get",
|
||||
url: `${API_BASE_URL}/maintenance`,
|
||||
data: query,
|
||||
});
|
||||
}
|
||||
|
||||
// 删除人员
|
||||
export function deleteUser(ItemId) {
|
||||
return service({
|
||||
method: "delete",
|
||||
url: `/application/role/${ItemId}`,
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,109 @@
|
||||
import axios from "axios";
|
||||
import { ElMessage, ElMessageBox } from "element-plus";
|
||||
import router from "../router/router";
|
||||
|
||||
const errorCode = {
|
||||
401: "登录状态已过期,即将返回首页重新登录。",
|
||||
403: "当前操作没有权限",
|
||||
404: "访问资源不存在",
|
||||
default: "系统未知错误,请反馈给管理员",
|
||||
};
|
||||
|
||||
const url = "https://api.cqcxj.cn";
|
||||
|
||||
const service = axios.create({
|
||||
baseURL: url,
|
||||
timeout: 60000,
|
||||
});
|
||||
|
||||
// Request interceptors
|
||||
service.interceptors.request.use(
|
||||
(config) => {
|
||||
if (localStorage.getItem("token")) {
|
||||
config.headers["Authorization"] =
|
||||
"Bearer " + localStorage.getItem("token");
|
||||
}
|
||||
|
||||
if (config.url === "/login") {
|
||||
config.headers["Content-Type"] = "multipart/form-data";
|
||||
} else {
|
||||
config.headers["Content-Type"] = "application/json";
|
||||
}
|
||||
|
||||
if (config.url === "/file") {
|
||||
config.headers["Content-Type"] = "multipart/form-data";
|
||||
}
|
||||
|
||||
let params = config.params;
|
||||
let newData = {};
|
||||
for (let key in params) {
|
||||
if (params[key] !== null && params[key] !== "") {
|
||||
newData[key] = params[key];
|
||||
}
|
||||
}
|
||||
config.params = newData;
|
||||
return config;
|
||||
},
|
||||
(error) => {
|
||||
Promise.reject(error).then(() => {});
|
||||
}
|
||||
);
|
||||
|
||||
// Response interceptors
|
||||
service.interceptors.response.use(
|
||||
(res) => {
|
||||
// 对响应数据做点什么
|
||||
const response = res.data;
|
||||
const code = res.data.code || 200;
|
||||
const message = errorCode[code] || res.data.msg || errorCode["default"];
|
||||
if (code === 401) {
|
||||
if (res.code === 401 || res.code === 4001) {
|
||||
// 清除浏览器全部临时缓存
|
||||
sessionStorage.clear();
|
||||
localStorage.clear();
|
||||
router.push("/login");
|
||||
// store.commit('setMenuData', {});
|
||||
// resetRouter(); // 重置路由
|
||||
ElMessageBox.alert("你已被登出,请重新登录", "提示", {})
|
||||
.then(() => {})
|
||||
.catch(() => {});
|
||||
}
|
||||
} else if (code === 500) {
|
||||
// Message({ message: msg, type: 'error' })
|
||||
ElMessage.error(message);
|
||||
return Promise.reject(new Error(msg));
|
||||
} else if (code === 601) {
|
||||
// Message({ message: msg, type: 'warning' })
|
||||
ElMessage.error(message);
|
||||
return Promise.reject("error");
|
||||
} else if (code !== 200) {
|
||||
ElMessage.error(message);
|
||||
return Promise.reject("error");
|
||||
} else {
|
||||
return response;
|
||||
}
|
||||
},
|
||||
(error) => {
|
||||
console.log(error.response.data); // 获取错误状态码
|
||||
// 对响应错误做点什么
|
||||
if (error.message.indexOf("timeout") !== -1) {
|
||||
ElMessage.error("网络超时");
|
||||
} else if (error.message === "Network Error") {
|
||||
ElMessage.error("网络连接错误");
|
||||
} else if (error.response.data.code === "401") {
|
||||
sessionStorage.clear();
|
||||
localStorage.clear();
|
||||
router.push("/login");
|
||||
ElMessage.error(error.response.data.msg);
|
||||
} else {
|
||||
ElMessage.error(
|
||||
error.response.data.msg ||
|
||||
error.response.data.error ||
|
||||
error.response.data.errMessage
|
||||
);
|
||||
}
|
||||
return Promise.reject(error);
|
||||
}
|
||||
);
|
||||
|
||||
export default service;
|
||||
@@ -0,0 +1,41 @@
|
||||
export const showMessage = (status) => {
|
||||
let message = "";
|
||||
switch (status) {
|
||||
case 400:
|
||||
message = "请求错误(400)";
|
||||
break;
|
||||
case 401:
|
||||
message = "未授权,请重新登录(401)";
|
||||
break;
|
||||
case 403:
|
||||
message = "拒绝访问(403)";
|
||||
break;
|
||||
case 404:
|
||||
message = "请求出错(404)";
|
||||
break;
|
||||
case 408:
|
||||
message = "请求超时(408)";
|
||||
break;
|
||||
case 500:
|
||||
message = "服务器错误(500)";
|
||||
break;
|
||||
case 501:
|
||||
message = "服务未实现(501)";
|
||||
break;
|
||||
case 502:
|
||||
message = "网络错误(502)";
|
||||
break;
|
||||
case 503:
|
||||
message = "服务不可用(503)";
|
||||
break;
|
||||
case 504:
|
||||
message = "网络超时(504)";
|
||||
break;
|
||||
case 505:
|
||||
message = "HTTP版本不受支持(505)";
|
||||
break;
|
||||
default:
|
||||
message = `连接出错(${status})!`;
|
||||
}
|
||||
return `${message},请检查网络或联系管理员!`;
|
||||
};
|
||||
|
After Width: | Height: | Size: 20 KiB |
|
After Width: | Height: | Size: 101 KiB |
|
After Width: | Height: | Size: 15 KiB |
|
After Width: | Height: | Size: 378 KiB |
|
After Width: | Height: | Size: 1.3 MiB |
|
After Width: | Height: | Size: 868 B |
|
After Width: | Height: | Size: 447 B |
|
After Width: | Height: | Size: 51 KiB |
|
After Width: | Height: | Size: 119 KiB |
|
After Width: | Height: | Size: 281 KiB |
|
After Width: | Height: | Size: 10 KiB |
|
After Width: | Height: | Size: 78 KiB |
|
After Width: | Height: | Size: 108 KiB |
|
After Width: | Height: | Size: 5.6 KiB |
@@ -0,0 +1,80 @@
|
||||
<template>
|
||||
<div class="videoPlay">
|
||||
<video
|
||||
ref="m3u8_video"
|
||||
class="video-js vjs-default-skin vjs-big-play-centered"
|
||||
controls
|
||||
>
|
||||
<source :src="videoSrc" />
|
||||
</video>
|
||||
</div>
|
||||
</template>
|
||||
<script setup>
|
||||
import { nextTick, onBeforeUnmount, onMounted, ref, watch } from 'vue'
|
||||
import videojs from 'video.js'
|
||||
import 'video.js/dist/video-js.css'
|
||||
const props = defineProps({
|
||||
videoSrc: String,
|
||||
autoPlay: Boolean,
|
||||
})
|
||||
|
||||
const m3u8_video = ref()
|
||||
let player
|
||||
const initPlay = async () => {
|
||||
videojs.addLanguage('zh-CN', zh)
|
||||
await nextTick()
|
||||
const options = {
|
||||
muted: true,
|
||||
controls: true,
|
||||
autoplay: true,
|
||||
loop: true,
|
||||
language: 'zh-CN',
|
||||
techOrder: ['html5'],
|
||||
}
|
||||
player = videojs(m3u8_video.value, options, () => {
|
||||
videojs.log('播放器已经准备好了!')
|
||||
if (props.autoPlay && props.videoSrc) {
|
||||
player.play()
|
||||
}
|
||||
player.on('ended', () => {
|
||||
videojs.log('播放结束了!')
|
||||
})
|
||||
player.on('error', () => {
|
||||
videojs.log('播放器解析出错!')
|
||||
})
|
||||
})
|
||||
}
|
||||
onMounted(() => {
|
||||
initPlay()
|
||||
})
|
||||
//直接改变路径测试
|
||||
watch(
|
||||
() => props.videoSrc,
|
||||
() => {
|
||||
player.pause()
|
||||
player.src(props.videoSrc)
|
||||
player.load()
|
||||
if (props.videoSrc) {
|
||||
player.play()
|
||||
}
|
||||
}
|
||||
)
|
||||
onBeforeUnmount(() => {
|
||||
player?.dispose()
|
||||
})
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
.videoPlay {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
.video-js {
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
object-fit: fill;
|
||||
}
|
||||
}
|
||||
|
||||
:deep(.vjs-tech) {
|
||||
object-fit: fill;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,55 @@
|
||||
<template>
|
||||
<el-dialog v-model="dialogVisible" title="添加数据">
|
||||
<el-form v-model="uploadFrom">
|
||||
<el-form-item label="文件上传:">
|
||||
<el-upload
|
||||
style="width: 80%"
|
||||
drag
|
||||
multiple
|
||||
:limit="excelLimit"
|
||||
:on-success="uploadSuccess"
|
||||
accept="application/vnd.ms-excel,application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"
|
||||
>
|
||||
<el-icon class="el-icon--upload"><upload-filled /></el-icon>
|
||||
<div class="el-upload__text">将文件拖到此处<em>或点击上传</em></div>
|
||||
<template #tip>
|
||||
<div class="el-upload__tip">请上传 .xls , .xlsx 标准格式文件</div>
|
||||
</template>
|
||||
</el-upload>
|
||||
</el-form-item>
|
||||
<el-form-item label="数据覆盖:">
|
||||
<el-switch v-model="is_cover" />
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<el-button @click="dialogVisible = false">取消</el-button>
|
||||
<el-button type="primary" @click="dialogVisible = false">确认</el-button>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</template>
|
||||
|
||||
<script setup name="filesUpload">
|
||||
import { ref, defineEmits } from 'vue'
|
||||
const excelLimit = ref(1)
|
||||
const dialogVisible = ref(false)
|
||||
const parameter = ref({})
|
||||
const is_cover = ref(false)
|
||||
const emit = defineEmits(['acceptParams'])
|
||||
|
||||
// 接收父组件参数
|
||||
const acceptParams = (params) => {
|
||||
parameter.value = params
|
||||
emit('gatewayData', parameter.value)
|
||||
dialogVisible.value = true
|
||||
}
|
||||
const uploadExcel = (param) => {
|
||||
console.log(param)
|
||||
}
|
||||
const uploadSuccess = (files) => {
|
||||
console.log('上传成功' + JSON.stringify(files))
|
||||
}
|
||||
// 接受父组件得参数
|
||||
defineExpose({
|
||||
acceptParams,
|
||||
})
|
||||
</script>
|
||||
@@ -0,0 +1,24 @@
|
||||
import draggable from "./modules/draggable";
|
||||
import copy from "./modules/copy";
|
||||
import debounce from "./modules/debounce";
|
||||
import longPress from "./modules/longPress";
|
||||
import throttle from "./modules/throttle";
|
||||
|
||||
const directivesList = {
|
||||
draggable,
|
||||
copy,
|
||||
debounce,
|
||||
longPress,
|
||||
throttle
|
||||
};
|
||||
|
||||
const directives = {
|
||||
install: function (app) {
|
||||
Object.keys(directivesList).forEach((key) => {
|
||||
// 注册所有自定义指令
|
||||
app.directive(key, directivesList[key]);
|
||||
});
|
||||
},
|
||||
};
|
||||
|
||||
export default directives;
|
||||
@@ -0,0 +1,58 @@
|
||||
/*
|
||||
需求:实现一个复制指定。复制指定内容至剪贴板
|
||||
1.动态创建 textarea 标签,并设置 readOnly 属性及移出可视区域
|
||||
2.将要复制的值赋给 textarea 标签的 value 属性,并插入到 body
|
||||
3.选中值 textarea 并复制
|
||||
4.将 body 中插入的 textarea 移除
|
||||
5.在第一次调用时绑定事件,在解绑时移除事件
|
||||
*/
|
||||
import { ElMessage } from "element-plus";
|
||||
export const copy = {
|
||||
mounted: function (el, { value }) {
|
||||
console.log(arguments);
|
||||
el.$value = value;
|
||||
|
||||
el.onclick = () => {
|
||||
if (!el.$value) {
|
||||
// 值为空的时候,给出提示。可根据项目UI仔细设计
|
||||
ElMessage({
|
||||
type: "error",
|
||||
message: "无复制内容",
|
||||
});
|
||||
return;
|
||||
}
|
||||
// 动态创建 textarea 标签
|
||||
const textarea = document.createElement("input");
|
||||
// 将该 textarea 设为 readonly 防止 iOS 下自动唤起键盘,同时将 textarea 移出可视区域
|
||||
textarea.readOnly = "readonly";
|
||||
textarea.style.position = "absolute";
|
||||
textarea.style.left = "-9999px";
|
||||
// 将要 copy 的值赋给 textarea 标签的 value 属性
|
||||
textarea.value = el.$value;
|
||||
// 将 textarea 插入到 body 中
|
||||
document.body.appendChild(textarea);
|
||||
// 选中值并复制
|
||||
textarea.select();
|
||||
const result = document.execCommand("Copy");
|
||||
if (result) {
|
||||
ElMessage({
|
||||
type: "success",
|
||||
message: "复制成功",
|
||||
});
|
||||
}
|
||||
document.body.removeChild(textarea);
|
||||
};
|
||||
// 绑定点击事件,就是所谓的一键 copy 啦
|
||||
el.addEventListener("click", el.handler);
|
||||
},
|
||||
// 当传进来的值更新的时候触发
|
||||
beforeUpdate(el, { value }) {
|
||||
el.$value = value;
|
||||
},
|
||||
// 指令与元素解绑的时候,移除事件绑定
|
||||
unmounted(el) {
|
||||
el.removeEventListener("click", el.handler);
|
||||
},
|
||||
};
|
||||
|
||||
export default copy;
|
||||
@@ -0,0 +1,28 @@
|
||||
/**
|
||||
* v-debounce
|
||||
* 按钮防抖指令,可自行扩展至input
|
||||
* 接收参数:function类型
|
||||
*/
|
||||
|
||||
const debounce = {
|
||||
mounted(el, binding) {
|
||||
if (typeof binding.value != "function") {
|
||||
console.log("callback must be a function");
|
||||
}
|
||||
let timer = null;
|
||||
el.__handleClick__ = function () {
|
||||
// if (timer) clearInterval(timer);
|
||||
//
|
||||
timer && clearTimeout(timer)
|
||||
timer = setTimeout(() => {
|
||||
binding.value();
|
||||
}, 3000);
|
||||
};
|
||||
el.addEventListener("click", el.__handleClick__);
|
||||
},
|
||||
beforeUnmount(el) {
|
||||
el.removeEventListener("click", el.__handleClick__);
|
||||
},
|
||||
};
|
||||
|
||||
export default debounce;
|
||||
@@ -0,0 +1,47 @@
|
||||
/*
|
||||
需求:实现一个拖拽指令,可在父元素区域任意拖拽元素。
|
||||
|
||||
思路:
|
||||
1、设置需要拖拽的元素为absolute,其父元素为relative。
|
||||
2、鼠标按下(onmousedown)时记录目标元素当前的 left 和 top 值。
|
||||
3、鼠标移动(onmousemove)时计算每次移动的横向距离和纵向距离的变化值,并改变元素的 left 和 top 值
|
||||
4、鼠标松开(onmouseup)时完成一次拖拽
|
||||
|
||||
使用:在 Dom 上加上 v-draggable 即可
|
||||
<div class="dialog-model" v-draggable></div>
|
||||
*/
|
||||
const draggable = {
|
||||
mounted: function (el) {
|
||||
console.log(el)
|
||||
el.style.cursor = "move";
|
||||
el.style.position = "absolute";
|
||||
el.onmousedown = function (e) {
|
||||
let disX = e.pageX - el.offsetLeft;
|
||||
let disY = e.pageY - el.offsetTop;
|
||||
document.onmousemove = function (e) {
|
||||
let x = e.pageX - disX;
|
||||
let y = e.pageY - disY;
|
||||
console.log(el.parentNode.offsetWidth)
|
||||
let maxX = el.parentNode.offsetWidth - el.offsetWidth;
|
||||
let maxY = el.parentNode.offsetHeight - el.offsetHeight;
|
||||
if (x < 0) {
|
||||
x = 0;
|
||||
} else if (x > maxX) {
|
||||
x = maxX;
|
||||
}
|
||||
|
||||
if (y < 0) {
|
||||
y = 0;
|
||||
} else if (y > maxY) {
|
||||
y = maxY;
|
||||
}
|
||||
el.style.left = x + "px";
|
||||
el.style.top = y + "px";
|
||||
};
|
||||
document.onmouseup = function () {
|
||||
document.onmousemove = document.onmouseup = null;
|
||||
};
|
||||
};
|
||||
}
|
||||
};
|
||||
export default draggable;
|
||||
@@ -0,0 +1,44 @@
|
||||
const longPress = {
|
||||
mounted(el, binding) {
|
||||
if (typeof binding.value != "function") {
|
||||
console.log("callback must be a function");
|
||||
}
|
||||
let pressTimer = null;
|
||||
let start = (e) => {
|
||||
if (e.type === "click" && e.button !== 0) {
|
||||
return;
|
||||
}
|
||||
if (pressTimer === null) {
|
||||
pressTimer = setTimeout(() => {
|
||||
// 执行函数
|
||||
handler();
|
||||
}, 2000);
|
||||
}
|
||||
};
|
||||
// 取消计时器
|
||||
let cancel = () => {
|
||||
// 检查计时器是否有值
|
||||
if (pressTimer !== null) {
|
||||
clearTimeout(pressTimer);
|
||||
pressTimer = null;
|
||||
}
|
||||
};
|
||||
// 运行函数
|
||||
const handler = (e) => {
|
||||
// 执行传递给指令的方法
|
||||
binding.value(e);
|
||||
console.log(e);
|
||||
};
|
||||
// 添加事件监听器
|
||||
el.addEventListener("mousedown", start);
|
||||
el.addEventListener("touchstart", start);
|
||||
el.addEventListener("click", start);
|
||||
// 取消计时器
|
||||
el.addEventListener("click", cancel);
|
||||
el.addEventListener("mouseout", cancel);
|
||||
el.addEventListener("touchend", cancel);
|
||||
el.addEventListener("touchcancel", cancel);
|
||||
},
|
||||
};
|
||||
|
||||
export default longPress;
|
||||
@@ -0,0 +1,30 @@
|
||||
/**
|
||||
* 一段时间内控制请求的频率
|
||||
* 上一次时间触发的时间和这次触发的时间相减 传入节流的时间
|
||||
*
|
||||
*
|
||||
* 应用场景:scroll加载的时候 和input框输入的情况
|
||||
*/
|
||||
|
||||
const throttle = {
|
||||
mounted(el, binding) {
|
||||
if (typeof binding.value != "function") {
|
||||
console.log("callback must be a function");
|
||||
}
|
||||
let lastTime = 0
|
||||
|
||||
let newtimer = +new Date(); //拿到当前的时间
|
||||
el.__handleClick__ = function () {
|
||||
if (newtimer - lastTime > 3000) {
|
||||
binding.value()
|
||||
lastTime = newtimer
|
||||
}
|
||||
};
|
||||
el.addEventListener("click", el.__handleClick__);
|
||||
},
|
||||
beforeUnmount(el) {
|
||||
el.removeEventListener("click", el.__handleClick__);
|
||||
},
|
||||
}
|
||||
|
||||
export default throttle
|
||||
@@ -0,0 +1,92 @@
|
||||
<template>
|
||||
|
||||
<el-tag
|
||||
ref="tagRef"
|
||||
v-for="tag in tags"
|
||||
:key="tag.title"
|
||||
class="mR10 tabs"
|
||||
:closable="tag.close"
|
||||
:effect="tag.checked ? 'dark' : 'plain'"
|
||||
@click="tagChange(tag)"
|
||||
@close="delectTag(tag, index)"
|
||||
>
|
||||
<template class="spanmenu">
|
||||
<el-icon class="tabs-icon">
|
||||
<component :is="tag.icon"></component>
|
||||
</el-icon>
|
||||
{{ tag.title }}
|
||||
</template>
|
||||
|
||||
</el-tag>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { useRoute, useRouter } from 'vue-router'
|
||||
import { computed, nextTick, onMounted, ref, watch } from 'vue'
|
||||
import store from '../../store/index.js'
|
||||
import { useStore } from 'vuex'
|
||||
|
||||
const tabsMenuValue = ref('')
|
||||
const nameScroll = ref(false)
|
||||
const tagRef = ref()
|
||||
const route = useRoute()
|
||||
const router = useRouter()
|
||||
const globalStore = useStore() // 该方法用于返回store 实例
|
||||
|
||||
// 监听路由的变化(防止浏览器后退/前进不变化 tabsMenuValue)
|
||||
watch(
|
||||
() => route.path,
|
||||
() => {
|
||||
let params = {
|
||||
title: route.meta.name,
|
||||
url: route.path,
|
||||
icon: 'Menu',
|
||||
close: true,
|
||||
checked: true,
|
||||
}
|
||||
globalStore.dispatch('tabs/addTabs', params)
|
||||
},
|
||||
{
|
||||
immediate: true,
|
||||
}
|
||||
)
|
||||
|
||||
const tags = store.getters.tabsMenuList
|
||||
|
||||
const tagChange = (item) => {
|
||||
item.checked = true
|
||||
router.push(item.url)
|
||||
}
|
||||
|
||||
const delectTag = (item, index) => {
|
||||
globalStore.dispatch('tabs/delectTag', item)
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
nextTick(() => {})
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.mR10 {
|
||||
margin-right: 10px;
|
||||
}
|
||||
|
||||
.tabs {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
height: 30px;
|
||||
padding: 0 10px; /* 调整内边距 */
|
||||
border-radius: 4px; /* 圆角 */
|
||||
cursor: pointer;
|
||||
|
||||
.tabs-icon {
|
||||
margin-right: 5px; /* 图标和文字之间的间距 */
|
||||
}
|
||||
|
||||
.spanmenu{
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,64 @@
|
||||
<template>
|
||||
<el-icon class="icon-style" id="collapseIcon" @click="changeShrinkage">
|
||||
<component :is="!isCollapse ? 'Fold' : 'Expand'"></component>
|
||||
</el-icon>
|
||||
<el-breadcrumb separator="/" id="breadcrumb">
|
||||
<el-breadcrumb-item v-for="(item, index) in breadcrumbList" :key="index">
|
||||
<span class="no-redirect" v-if="index === breadcrumbList.length - 1" :style="{ color: themeConfig.footColor }">{{ item.meta.name }}</span>
|
||||
<span class="redirect" v-else @click="handleRedirect(item.path)" :style="{ color: themeConfig.footColor }">{{ item.meta.name }}</span>
|
||||
</el-breadcrumb-item>
|
||||
</el-breadcrumb>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { watch, ref } from "vue";
|
||||
import { useRoute, useRouter } from "vue-router";
|
||||
import store from "../../../store";
|
||||
import { useStore } from "vuex";
|
||||
const route = useRoute();
|
||||
const router = useRouter();
|
||||
|
||||
const breadcrumbList = ref([]);
|
||||
const isCollapse = ref(false);
|
||||
const globalStore = useStore();
|
||||
const themeConfig = store.getters.themeConfig;
|
||||
const initBreadcrumbList = () => {
|
||||
breadcrumbList.value = route.matched;
|
||||
console.log(route.matched);
|
||||
};
|
||||
const handleRedirect = (path) => {
|
||||
router.push(path);
|
||||
};
|
||||
|
||||
const changeShrinkage = () => {
|
||||
isCollapse.value = !store.getters.isCollapse;
|
||||
globalStore.dispatch("user/changeIsCollapse", isCollapse.value);
|
||||
};
|
||||
|
||||
watch(
|
||||
route,
|
||||
() => {
|
||||
initBreadcrumbList();
|
||||
},
|
||||
{ deep: true, immediate: true }
|
||||
);
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.no-redirect {
|
||||
// color: #97a8be;
|
||||
cursor: text;
|
||||
}
|
||||
.redirect {
|
||||
font-weight: 600;
|
||||
cursor: pointer;
|
||||
&:hover {
|
||||
color: #97a8be;
|
||||
}
|
||||
}
|
||||
.icon-style {
|
||||
font-size: 20px;
|
||||
cursor: pointer;
|
||||
margin-right: 20px;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,25 @@
|
||||
export const steps = () => [{
|
||||
element: "#guide",
|
||||
popover: {
|
||||
title: "菜单栏",
|
||||
description: "Body of the popover",
|
||||
position: "left",
|
||||
},
|
||||
},
|
||||
{
|
||||
element: "#breadcrumb",
|
||||
popover: {
|
||||
title: "标签页",
|
||||
description: "",
|
||||
position: "bottom",
|
||||
},
|
||||
},
|
||||
{
|
||||
element: "#screenFul",
|
||||
popover: {
|
||||
title: "全屏",
|
||||
description: "Body of the popover",
|
||||
position: "bottom",
|
||||
},
|
||||
},
|
||||
];
|
||||
@@ -0,0 +1,33 @@
|
||||
<template>
|
||||
<el-icon @click="handleDriver"><Position /></el-icon>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import Driver from 'driver.js'
|
||||
import 'driver.js/dist/driver.min.css'
|
||||
import { onMounted, ref } from 'vue'
|
||||
import { steps } from './index.js'
|
||||
|
||||
let driver = ref('')
|
||||
|
||||
onMounted(() => {
|
||||
driver.value = new Driver({
|
||||
className: 'scoped-class', //包裹driver.js弹窗的类名 className to wrap driver.js popover
|
||||
animate: true, // 高亮元素改变时是否显示动画 Animate while changing highlighted element
|
||||
opacity: 0.75, //背景透明度(0 表示只有弹窗并且没有遮罩) Background opacity (0 means only popovers and without overlay)
|
||||
padding: 10, // 元素与边缘的距离 Distance of element from around the edges
|
||||
allowClose: true, // 是否允许点击遮罩时关闭 Whether clicking on overlay should close or not
|
||||
overlayClickNext: false, //是否允许点击遮罩时移到到下一步 Should it move to next step on overlay click
|
||||
doneBtnText: '完成', // 最终按钮上的文本 Text on the final button
|
||||
closeBtnText: '跳过', // 当前步骤关闭按钮上的文本 Text on the close button for this step
|
||||
nextBtnText: '下一步', //当前步骤下一步按钮上的文本 Next button text for this step
|
||||
prevBtnText: '上一步',
|
||||
})
|
||||
})
|
||||
const handleDriver = () => {
|
||||
driver.value.defineSteps(steps)
|
||||
driver.value.start()
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped></style>
|
||||
@@ -0,0 +1,66 @@
|
||||
<template>
|
||||
<el-icon class="icon-style" @click="dialogVisible = true"><Search /></el-icon>
|
||||
|
||||
<el-dialog v-model="dialogVisible" :show-close="false">
|
||||
<template #header>
|
||||
<el-input
|
||||
v-model="searchValue"
|
||||
placeholder="搜索"
|
||||
class="input-with-select"
|
||||
>
|
||||
<template #prepend>
|
||||
<el-button :icon="Search" />
|
||||
</template>
|
||||
</el-input>
|
||||
</template>
|
||||
<div class="centent col-center">暂无搜索结果🦁🦁🦁🦁</div>
|
||||
<template #footer>
|
||||
<div class="footer-tip flx-row">
|
||||
<span>↩</span> 确认 <span class="ml10">⇅</span>切换
|
||||
<span class="ml10" style="font-size: 10px">ESC</span>关闭
|
||||
</div>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { Search, Top, Bottom } from '@element-plus/icons-vue'
|
||||
import { nextTick, onMounted, ref } from 'vue'
|
||||
const dialogVisible = ref(false)
|
||||
const searchValue = ref('')
|
||||
|
||||
onMounted(() => {
|
||||
nextTick(() => {
|
||||
document.addEventListener('keyup', function (e) {
|
||||
if (e.keyCode == 27) {
|
||||
dialogVisible.value = false
|
||||
}
|
||||
})
|
||||
})
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.input-with-select {
|
||||
height: 40px;
|
||||
}
|
||||
.footer-tip {
|
||||
height: 20px;
|
||||
padding: 0;
|
||||
font-size: 12px;
|
||||
|
||||
span {
|
||||
display: flex;
|
||||
width: 20px;
|
||||
height: 18px;
|
||||
padding-bottom: 2px;
|
||||
margin-right: 0.4em;
|
||||
background-color: linear-gradient(-225deg, #d5dbe4, #f8f8f8);
|
||||
border-radius: 2px;
|
||||
box-shadow: inset 0 -2px #cdcde6, inset 0 0 1px 1px #fff,
|
||||
0 1px 2px 1px #1e235a66;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,62 @@
|
||||
<template>
|
||||
<el-dropdown placement="bottom">
|
||||
<div class="flx-center">
|
||||
<div class="avatar">
|
||||
<img src="../../../assets/images/avart.jpg" alt="avatar" />
|
||||
</div>
|
||||
<span class="username" :style="{ color: themeConfig.footColor }">管理员</span>
|
||||
</div>
|
||||
<template #dropdown>
|
||||
<el-dropdown-menu>
|
||||
<!-- <el-dropdown-item>个人中心</el-dropdown-item> -->
|
||||
<el-dropdown-item @click="gotoLogin">退出登录</el-dropdown-item>
|
||||
</el-dropdown-menu>
|
||||
</template>
|
||||
</el-dropdown>
|
||||
</template>
|
||||
<script setup>
|
||||
import { ArrowDown } from "@element-plus/icons-vue";
|
||||
import { nextTick, onMounted, ref } from "vue";
|
||||
import store from "../../../store/index.js";
|
||||
import { useRouter } from "vue-router";
|
||||
const router = useRouter();
|
||||
const themeConfig = store.getters.themeConfig;
|
||||
const handleClick = () => {
|
||||
// eslint-disable-next-line no-alert
|
||||
alert("button click");
|
||||
};
|
||||
|
||||
const gotoLogin = () => {
|
||||
sessionStorage.removeItem("token");
|
||||
router.push("/login");
|
||||
};
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
.el-dropdown {
|
||||
cursor: pointer;
|
||||
.el-tooltip__trigger {
|
||||
display: flex;
|
||||
flex-flow: row;
|
||||
align-items: center;
|
||||
}
|
||||
}
|
||||
.username {
|
||||
font-size: 15px;
|
||||
|
||||
margin: 0 0 0 10px;
|
||||
}
|
||||
|
||||
.avatar {
|
||||
width: 35px;
|
||||
height: 35px;
|
||||
border-radius: 50%;
|
||||
overflow: hidden;
|
||||
cursor: pointer;
|
||||
margin-left: 20px;
|
||||
|
||||
img {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,33 @@
|
||||
<template>
|
||||
<el-icon id="fullscreen" class="icon-style" @click="handleFullScreen">
|
||||
<component :is="icon ? 'Rank' : 'FullScreen'"></component>
|
||||
</el-icon>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import screenfull from 'screenfull'
|
||||
import { ref, onMounted, onBeforeMount } from 'vue'
|
||||
|
||||
import { ElMessage } from 'element-plus'
|
||||
|
||||
let icon = ref(screenfull.isFullscreen)
|
||||
const handleFullScreen = () => {
|
||||
if (screenfull.isEnabled) {
|
||||
screenfull.toggle()
|
||||
}
|
||||
}
|
||||
|
||||
const changeIcon = () => {
|
||||
icon.value = screenfull.isFullscreen
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
screenfull.on('change', changeIcon)
|
||||
})
|
||||
|
||||
onBeforeMount(() => {
|
||||
screenfull.off('change')
|
||||
})
|
||||
</script>
|
||||
|
||||
<style scoped></style>
|
||||
@@ -0,0 +1,85 @@
|
||||
<template>
|
||||
<el-popover :width="400" placement="bottom" trigger="click">
|
||||
<!-- <el-tabs v-model="activeName" class="demo-tabs" @tab-click="handleClick"> -->
|
||||
<el-tabs v-model="activeName" class="demo-tabs">
|
||||
<el-tab-pane label="通知" name="first">
|
||||
<div class="notice-lists">
|
||||
<div class="item one-cut-txt" v-for="item in notList" :key="item.id">
|
||||
<span class="type">通知</span> {{ item.text }}
|
||||
</div>
|
||||
</div>
|
||||
</el-tab-pane>
|
||||
<el-tab-pane label="公告" name="second">
|
||||
<div class="notice-lists">
|
||||
<div class="item one-cut-txt" v-for="item in notList" :key="item.id">
|
||||
<span class="type">公告</span> {{ item.text }}
|
||||
</div>
|
||||
</div>
|
||||
</el-tab-pane>
|
||||
<el-tab-pane label="系统消息" name="third">
|
||||
<div class="notice-lists">
|
||||
<div class="item one-cut-txt" v-for="item in notList" :key="item.id">
|
||||
<span class="type" style="color: rgb(47, 96, 194); background-color: #e8f3ff"
|
||||
>系统消息</span
|
||||
>
|
||||
{{ item.text }}
|
||||
</div>
|
||||
</div>
|
||||
</el-tab-pane>
|
||||
</el-tabs>
|
||||
<template #reference>
|
||||
<el-icon class="icon-style" @click="visible = !visible"><bell /></el-icon>
|
||||
</template>
|
||||
</el-popover>
|
||||
</template>
|
||||
<script setup name="messages">
|
||||
import { Bell } from "@element-plus/icons-vue";
|
||||
import { nextTick, onMounted, ref } from "vue";
|
||||
|
||||
import { noticeLists } from "../../../api/modules/index.js";
|
||||
const visible = ref(false);
|
||||
const activeName = ref("first");
|
||||
const notList = ref("first");
|
||||
|
||||
onMounted(() => {
|
||||
nextTick(() => {
|
||||
noticeLists().then((res) => {
|
||||
notList.value = res.data.data;
|
||||
});
|
||||
});
|
||||
});
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
.icon-style {
|
||||
font-size: 20px;
|
||||
|
||||
cursor: pointer;
|
||||
margin: 0 11px;
|
||||
}
|
||||
.notice-lists {
|
||||
height: 260px;
|
||||
overflow: scroll;
|
||||
|
||||
.item {
|
||||
font-size: 14px;
|
||||
padding: 10px;
|
||||
color: #666;
|
||||
|
||||
.type {
|
||||
font-size: 10px;
|
||||
padding: 5px 10px;
|
||||
font-weight: 500;
|
||||
line-height: 0px;
|
||||
color: #fa4a1e;
|
||||
text-align: left;
|
||||
border-radius: 6px;
|
||||
background: #fa4a1e14;
|
||||
|
||||
&.error-type {
|
||||
color: #4060c7;
|
||||
background: #e8f3ff;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,192 @@
|
||||
<template>
|
||||
<el-icon @click="openDrawer" class="icon-style"><Setting /></el-icon>
|
||||
<el-drawer v-model="drawerVisible" title="布局设置" size="300px">
|
||||
<el-divider>主题颜色</el-divider>
|
||||
<div class="flx-row">
|
||||
<div
|
||||
class="theme-item"
|
||||
v-for="item in colorList"
|
||||
:key="item"
|
||||
:style="{ backgroundColor: item }"
|
||||
@click="changePrimary(item)"
|
||||
>
|
||||
<el-icon v-if="item == themeConfig.primary" class="icon"
|
||||
><Select
|
||||
/></el-icon>
|
||||
</div>
|
||||
</div>
|
||||
<el-divider>导航栏颜色</el-divider>
|
||||
<div class="flx-row">
|
||||
<div
|
||||
class="theme-item"
|
||||
v-for="item in tabColorList"
|
||||
:key="item"
|
||||
:style="{ backgroundColor: item }"
|
||||
@click="changeTabCole(item)"
|
||||
>
|
||||
<el-icon v-if="item == themeConfig.tabColor" class="icon"
|
||||
><Select
|
||||
/></el-icon>
|
||||
</div>
|
||||
</div>
|
||||
<!-- <el-switch v-model="value2" class="mt-2" style="margin-left: 24px" inline-prompt :active-icon="Check" :inactive-icon="Close" /> -->
|
||||
<div class="flx-row">
|
||||
<div class="flx-tit">侧边栏模式</div>
|
||||
<el-switch
|
||||
v-model="value2"
|
||||
@change="changeGreyOrWeak($event)"
|
||||
class="mt-2"
|
||||
style="margin-left: 24px"
|
||||
inline-prompt
|
||||
:active-icon="Sunny"
|
||||
:inactive-icon="Moon"
|
||||
/>
|
||||
</div>
|
||||
<div class="flx-row">
|
||||
<div class="flx-tit">标签栏是否显示</div>
|
||||
<el-switch
|
||||
v-model="istags"
|
||||
@change="changeTags($event)"
|
||||
class="mt-2"
|
||||
style="margin-left: 24px"
|
||||
/>
|
||||
</div>
|
||||
<!-- <div class="theme-item">
|
||||
<el-color-picker
|
||||
v-model="themeConfig.primary"
|
||||
:predefine="colorList"
|
||||
@change="changePrimary"
|
||||
>
|
||||
</el-color-picker>
|
||||
</div> -->
|
||||
<!-- <div class="theme-item">
|
||||
<span>暗黑模式</span>
|
||||
<SwitchDark></SwitchDark>
|
||||
</div>
|
||||
<div class="theme-item">
|
||||
<span>灰色模式</span>
|
||||
<el-switch
|
||||
v-model="themeConfig.isGrey"
|
||||
@change="changeGreyOrWeak($event, 'grey')"
|
||||
/>
|
||||
</div>
|
||||
<div class="theme-item">
|
||||
<span>色弱模式</span>
|
||||
<el-switch
|
||||
v-model="themeConfig.isWeak"
|
||||
@change="changeGreyOrWeak($event, 'weak')"
|
||||
/>
|
||||
</div>
|
||||
<br />
|
||||
<el-divider class="divider" content-position="center">
|
||||
<el-icon><Setting /></el-icon>
|
||||
界面设置
|
||||
</el-divider>
|
||||
<div class="theme-item">
|
||||
<span>折叠菜单</span>
|
||||
<el-switch v-model="isCollapse" />
|
||||
</div>
|
||||
<div class="theme-item">
|
||||
<span>面包屑导航</span>
|
||||
<el-switch v-model="themeConfig.breadcrumb" />
|
||||
</div>
|
||||
<div class="theme-item">
|
||||
<span>标签栏</span>
|
||||
<el-switch v-model="themeConfig.tabs" />
|
||||
</div>
|
||||
<div class="theme-item">
|
||||
<span>页脚</span>
|
||||
<el-switch v-model="themeConfig.footer" />
|
||||
</div> -->
|
||||
</el-drawer>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, computed, useAttrs } from 'vue'
|
||||
import { Sunny, Moon } from '@element-plus/icons-vue'
|
||||
import store from '../../../store/index.js'
|
||||
import { mix } from '../../../utils/color.js'
|
||||
import { useStore } from 'vuex'
|
||||
const value2 = ref(true)
|
||||
const istags = computed(() => {
|
||||
return store.getters.themeConfig.istags
|
||||
})
|
||||
// 预定义主题颜色
|
||||
const colorList = [
|
||||
'#4060c7',
|
||||
'#409EFF',
|
||||
'#009688',
|
||||
'#27ae60',
|
||||
'#e74c3c',
|
||||
'#fd726d',
|
||||
'#f39c12',
|
||||
'#9b59b6',
|
||||
]
|
||||
// 预导航栏颜色
|
||||
const tabColorList = [
|
||||
'#FFFFFF',
|
||||
'#333333',
|
||||
'#009688',
|
||||
'#27ae60',
|
||||
'#e74c3c',
|
||||
'#fd726d',
|
||||
'#f39c12',
|
||||
'#9b59b6',
|
||||
]
|
||||
|
||||
// 主题初始化
|
||||
const globalStore = useStore()
|
||||
const themeConfig = computed(() => {
|
||||
return store.getters.themeConfig
|
||||
})
|
||||
|
||||
const changePrimary = (val) => {
|
||||
globalStore.dispatch('user/changeThem', val)
|
||||
}
|
||||
const changeTabCole = (val) => {
|
||||
globalStore.dispatch('user/changeTabColor', val)
|
||||
}
|
||||
const drawerVisible = ref(false)
|
||||
const openDrawer = () => {
|
||||
drawerVisible.value = true
|
||||
}
|
||||
|
||||
const changeGreyOrWeak = (e) => {
|
||||
globalStore.dispatch('user/changeMenuColor', e)
|
||||
}
|
||||
const changeTags = (e) => {
|
||||
globalStore.dispatch('user/changeTags', e)
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.icon-style {
|
||||
font-size: 20px;
|
||||
cursor: pointer;
|
||||
margin: 0 11px;
|
||||
}
|
||||
.flx-row {
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
.flx-tit {
|
||||
color: #303133;
|
||||
flex: 1;
|
||||
font-size: 14px;
|
||||
}
|
||||
.theme-item {
|
||||
margin: 16px 5px;
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
cursor: pointer;
|
||||
border: 1px solid #ddd;
|
||||
border-radius: 2px;
|
||||
position: relative;
|
||||
.icon {
|
||||
color: #fff;
|
||||
position: absolute;
|
||||
top: 3px;
|
||||
right: 3px;
|
||||
font-size: 14px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,89 @@
|
||||
<template>
|
||||
<div
|
||||
class="header"
|
||||
:style="{ backgroundColor: themeConfig.tabColor, color: themeConfig.footColor }"
|
||||
>
|
||||
<div class="header-lf flx-center">
|
||||
<Breadcrumb></Breadcrumb>
|
||||
</div>
|
||||
<div class="header-ri flx-center">
|
||||
<!-- <Search></Search> -->
|
||||
<el-icon class="icon-style" @click="refresh"><Refresh /></el-icon>
|
||||
<!-- <message></message> -->
|
||||
|
||||
<!-- <Driver></Driver> -->
|
||||
<fullScreen></fullScreen>
|
||||
<avatar></avatar>
|
||||
<!-- <setting></setting>-->
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script setup name="header">
|
||||
import { inject, ref } from "vue";
|
||||
import store from "../../store/index.js";
|
||||
// import { Setting, Search } from '@element-plus/icons-vue'
|
||||
import avatar from "./components/avatar.vue";
|
||||
import setting from "./components/setting.vue";
|
||||
import Search from "./components/Search.vue";
|
||||
import fullScreen from "./components/fullScreen.vue";
|
||||
import message from "./components/message.vue";
|
||||
import Breadcrumb from "./components/Breadcrumb.vue";
|
||||
// import Driver from "./components/Driver/index.vue";
|
||||
// 局部数据刷新
|
||||
const refresh = inject("reload");
|
||||
|
||||
const themeConfig = store.getters.themeConfig;
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.header {
|
||||
border-bottom: 1px solid #f6f6f6;
|
||||
box-sizing: border-box;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding: 0 15px;
|
||||
height: 48px;
|
||||
line-height: 48px;
|
||||
|
||||
.header-lf {
|
||||
.collapse-icon {
|
||||
font-size: 22px;
|
||||
margin-right: 20px;
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
|
||||
.header-ri {
|
||||
// margin-left:10px;
|
||||
|
||||
.message-box {
|
||||
height: 100%;
|
||||
position: relative;
|
||||
|
||||
.dot {
|
||||
position: absolute;
|
||||
top: -5px;
|
||||
right: 10px;
|
||||
background-color: #d54f50;
|
||||
border-radius: 50%;
|
||||
width: 8px;
|
||||
height: 8px;
|
||||
}
|
||||
|
||||
.bell-icon {
|
||||
font-size: 22px;
|
||||
margin-right: 20px;
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
|
||||
.icon-style {
|
||||
font-size: 20px;
|
||||
|
||||
cursor: pointer;
|
||||
margin: 0 0 0 11px;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,63 @@
|
||||
.el-container {
|
||||
width: 100%;
|
||||
height: 100vh;
|
||||
overflow: hidden;
|
||||
|
||||
.el-aside {
|
||||
width: auto;
|
||||
overflow: inherit;
|
||||
}
|
||||
|
||||
.el-header,
|
||||
.el-footer {
|
||||
height: auto;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.main-tabs {
|
||||
margin-bottom: 10px;
|
||||
background-color: #fff;
|
||||
border: 0;
|
||||
box-shadow: none;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.el-main {
|
||||
background: #f0f2f5;
|
||||
padding: 10px 13px;
|
||||
box-sizing: border-box;
|
||||
// 防止切换出现横向滚动条
|
||||
overflow-x: hidden;
|
||||
|
||||
.main-box {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
// background-color: #f5f5f5;
|
||||
// box-shadow: 0 2px 12px 0 rgb(0 0 0 / 10%);
|
||||
// border-radius: 4px;
|
||||
// padding: 20px;
|
||||
box-sizing: border-box;
|
||||
overflow: auto;
|
||||
overflow-x: hidden !important;
|
||||
|
||||
&::-webkit-scrollbar {
|
||||
background-color: white;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
.shake {
|
||||
animation: shake 0.5s linear;
|
||||
}
|
||||
|
||||
@keyframes shake {
|
||||
from {
|
||||
transform: translateX(-100%);
|
||||
}
|
||||
|
||||
to {
|
||||
transform: translateX(0);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
<template>
|
||||
<el-container class="container">
|
||||
<el-aside :style="{ backgroundColor: themeConfig.backgroundColor }"
|
||||
><Menu></Menu
|
||||
></el-aside>
|
||||
<el-container>
|
||||
<el-header>
|
||||
<Header></Header>
|
||||
</el-header>
|
||||
<el-main>
|
||||
<div class="main-tabs flx-row p10" v-if="themeConfig.istags">
|
||||
<tabs></tabs>
|
||||
</div>
|
||||
|
||||
<div class="main-box">
|
||||
<router-view :class="{ shake: disabled }"></router-view>
|
||||
</div>
|
||||
</el-main>
|
||||
</el-container>
|
||||
</el-container>
|
||||
</template>
|
||||
|
||||
<script setup name="layout">
|
||||
import { useRouter, onBeforeRouteUpdate } from 'vue-router'
|
||||
import { ref, watch } from 'vue'
|
||||
import store from '../store/index.js'
|
||||
import Header from './header/index.vue'
|
||||
import Menu from './menu/index.vue'
|
||||
import tabs from './Tabs/index.vue'
|
||||
const themeConfig = store.getters.themeConfig
|
||||
console.log(themeConfig.backgroundColor)
|
||||
|
||||
let router = useRouter()
|
||||
const disabled = ref(false)
|
||||
// onMounted(() => {
|
||||
// disabled.value = true
|
||||
// setTimeout(() => {
|
||||
// disabled.value = false
|
||||
// }, 1500)
|
||||
// })
|
||||
|
||||
watch(
|
||||
() => router.currentRoute.value.path,
|
||||
(newValue, oldValue) => {
|
||||
console.log('watch', newValue)
|
||||
console.log('watch', oldValue)
|
||||
if (newValue == oldValue) {
|
||||
} else {
|
||||
disabled.value = true
|
||||
setTimeout(() => {
|
||||
disabled.value = false
|
||||
}, 1000)
|
||||
}
|
||||
},
|
||||
{ immediate: true }
|
||||
)
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
@import './index.scss';
|
||||
</style>
|
||||
@@ -0,0 +1,48 @@
|
||||
<template>
|
||||
<template v-for="subItem in menuList" :key="subItem.path">
|
||||
<el-sub-menu
|
||||
v-if="subItem.children && subItem.children.length > 0"
|
||||
:index="subItem.path"
|
||||
>
|
||||
<template #title>
|
||||
<el-icon>
|
||||
<component :is="subItem.meta.icon"></component>
|
||||
</el-icon>
|
||||
<span>{{ subItem.meta.title }}</span>
|
||||
</template>
|
||||
<Menu-items :menuList="subItem.children"></Menu-items>
|
||||
<!--<template v-for="child in subItem.children" :key="child.path">
|
||||
<template v-if="child.children && child.children.length > 0">
|
||||
<Menu-items :menuList="child.children"></Menu-items>
|
||||
</template>
|
||||
<el-menu-item v-else :index="child.path">
|
||||
<el-icon>
|
||||
<component :is="child.meta.icon"></component>
|
||||
</el-icon>
|
||||
<template #title>
|
||||
<span>{{ child.meta.title }}</span>
|
||||
</template>
|
||||
</el-menu-item>
|
||||
</template> -->
|
||||
</el-sub-menu>
|
||||
<el-menu-item v-else :index="subItem.path">
|
||||
<el-icon>
|
||||
<component :is="subItem.meta.icon"></component>
|
||||
</el-icon>
|
||||
<template #title>
|
||||
<span>{{ subItem.meta.title }}</span>
|
||||
</template>
|
||||
</el-menu-item>
|
||||
</template>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
defineProps(["menuList"]);
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.icons {
|
||||
width: 24px;
|
||||
height: 18px;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,32 @@
|
||||
<template>
|
||||
<template v-for="subItem in menuList" :key="subItem.path">
|
||||
<el-menu-item v-if="!subItem.children" :index="subItem.url" :route="subItem.url">
|
||||
<el-icon>
|
||||
<component :is="subItem.icon"></component>
|
||||
</el-icon>
|
||||
<span>{{ subItem.title }}</span>
|
||||
</el-menu-item>
|
||||
<!--有多级菜单-->
|
||||
<el-sub-menu v-if="subItem.children" :key="subItem.url" :index="subItem.url">
|
||||
<template #title>
|
||||
<el-icon>
|
||||
<component :is="subItem.icon"></component>
|
||||
</el-icon>
|
||||
<span> {{ subItem.title }}</span>
|
||||
</template>
|
||||
<!--递归组件,把遍历的值传回子组件,完成递归调用-->
|
||||
<menu-items :menuList="subItem.children"></menu-items>
|
||||
</el-sub-menu>
|
||||
</template>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
defineProps(["menuList"]);
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.icons {
|
||||
width: 24px;
|
||||
height: 18px;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,118 @@
|
||||
.menu {
|
||||
transition: all 0.3s ease;
|
||||
background: #20222a;
|
||||
height: 100%;
|
||||
position: relative;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
.logo {
|
||||
transition: width 0.3s ease;
|
||||
height: 55px;
|
||||
box-sizing: border-box;
|
||||
border-bottom: 2px solid #1d1e26;
|
||||
box-shadow: 2px 0 6px rgb(0 21 41 / 35%);
|
||||
|
||||
span {
|
||||
font-size: 22px;
|
||||
font-weight: bold;
|
||||
color: #dadada;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
img {
|
||||
width: 30px;
|
||||
object-fit: contain;
|
||||
margin-right: 8px;
|
||||
}
|
||||
}
|
||||
|
||||
.el-scrollbar {
|
||||
height: calc(100% - 55px);
|
||||
|
||||
.el-menu {
|
||||
// flex: 1;
|
||||
overflow: auto;
|
||||
overflow-x: hidden;
|
||||
border: none !important;
|
||||
width: 100% !important;
|
||||
|
||||
&::-webkit-scrollbar {
|
||||
background-color: #20222a;
|
||||
}
|
||||
|
||||
&::-webkit-scrollbar-thumb {
|
||||
background-color: #41444b;
|
||||
}
|
||||
|
||||
.el-menu-item {
|
||||
width: 200px;
|
||||
|
||||
&.is-active {
|
||||
background-color: #060708 !important;
|
||||
}
|
||||
|
||||
&.is-active::before {
|
||||
content: "";
|
||||
top: 0;
|
||||
left: 0;
|
||||
bottom: 0;
|
||||
width: 4px;
|
||||
background: $bg-color;
|
||||
position: absolute;
|
||||
}
|
||||
}
|
||||
|
||||
.href {
|
||||
display: inline-block;
|
||||
text-decoration: none;
|
||||
color: #bdbdc0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// .el-menu--popup {
|
||||
// .el-menu-item {
|
||||
// background-color: #20222a;
|
||||
// width: 200px;
|
||||
|
||||
// i {
|
||||
// margin-right: 5px;
|
||||
// }
|
||||
|
||||
// i,
|
||||
// span {
|
||||
// color: hsla(0, 0%, 100%, 0.7);
|
||||
// }
|
||||
// //
|
||||
// &:hover {
|
||||
|
||||
// i,
|
||||
// span {
|
||||
// color: #fff !important;
|
||||
// }
|
||||
// }
|
||||
|
||||
// &.is-active {
|
||||
// background-color: #060708 !important;
|
||||
|
||||
// &:before {
|
||||
// content: "";
|
||||
// top: 0;
|
||||
// left: 0;
|
||||
// bottom: 0;
|
||||
// width: 4px;
|
||||
// background: $bg-color;
|
||||
// position: absolute;
|
||||
// }
|
||||
|
||||
// i,
|
||||
// span {
|
||||
// color: #fff !important;
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
@@ -0,0 +1,78 @@
|
||||
<template>
|
||||
<div
|
||||
id="guide"
|
||||
class="menu"
|
||||
:style="{
|
||||
width: store.getters.isCollapse == true ? '' : '200px',
|
||||
height: '100%',
|
||||
color: themeConfig.textColor,
|
||||
}"
|
||||
>
|
||||
<div class="logo" v-if="!store.getters.isCollapse">
|
||||
<img src="../../assets/logo.png" alt="" style="margin-right: 5px" /> 创兴佳
|
||||
</div>
|
||||
<div class="logo" v-else>
|
||||
<img src="../../assets/logo.png" alt="" />
|
||||
</div>
|
||||
<el-scrollbar style="height: 100%">
|
||||
<el-menu
|
||||
:default-active="activeMenu"
|
||||
:router="true"
|
||||
:collapse="store.getters.isCollapse"
|
||||
:collapse-transition="false"
|
||||
:unique-opened="true"
|
||||
:background-color="themeConfig.backgroundColor"
|
||||
:text-color="themeConfig.textColor"
|
||||
:active-text-color="themeConfig.primary"
|
||||
>
|
||||
<menuItems :menuList="menuList"></menuItems>
|
||||
</el-menu>
|
||||
</el-scrollbar>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { computed, onMounted, reactive, ref } from "vue";
|
||||
import { menuListItem } from "../../mock/permission";
|
||||
import store from "../../store/index.js";
|
||||
import menuItems from "./components/menuItems.vue";
|
||||
// 引入本地路由表渲染导航栏
|
||||
import menuTable from "./menuTable.js";
|
||||
import { useRouter, useRoute } from "vue-router";
|
||||
const route = useRoute();
|
||||
const activeMenu = computed(() => {
|
||||
return route.path;
|
||||
});
|
||||
const menuList = ref([]);
|
||||
const handleOpen = (key, keyPath) => {
|
||||
console.log(key, keyPath);
|
||||
};
|
||||
const handleClose = (key, keyPath) => {
|
||||
console.log(key, keyPath);
|
||||
};
|
||||
const themeConfig = store.getters.themeConfig;
|
||||
|
||||
// 菜单表存在后端的操作方式
|
||||
onMounted(() => {
|
||||
// 获取本地菜单表渲染
|
||||
menuList.value = menuListItem;
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.menu {
|
||||
// width: 200px;
|
||||
.logo {
|
||||
height: 48px;
|
||||
line-height: 48px;
|
||||
padding: 0 20px;
|
||||
list-style: none;
|
||||
cursor: pointer;
|
||||
position: relative;
|
||||
img {
|
||||
width: 25px;
|
||||
vertical-align: middle;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,119 @@
|
||||
// 菜单列表改了路由要记得改菜单相互匹配
|
||||
const menuTable = [
|
||||
{
|
||||
title: "首页",
|
||||
url: "/home",
|
||||
icon: "HomeFilled",
|
||||
},
|
||||
{
|
||||
title: "用户管理",
|
||||
url: "/home",
|
||||
icon: "HomeFilled",
|
||||
},
|
||||
{
|
||||
title: "服务项目",
|
||||
url: "/goods",
|
||||
icon: "HomeFilled",
|
||||
},
|
||||
{
|
||||
title: "页面管理",
|
||||
url: "/page",
|
||||
icon: "Checked",
|
||||
children: [
|
||||
{
|
||||
title: "表单页",
|
||||
url: "/form",
|
||||
icon: "Checked",
|
||||
children: [
|
||||
{
|
||||
title: "基础表单",
|
||||
url: "/baseForm",
|
||||
icon: "Menu",
|
||||
},
|
||||
{
|
||||
url: "/stepFrom",
|
||||
title: "步骤表单",
|
||||
icon: "Menu",
|
||||
},
|
||||
{
|
||||
url: "/advancedForm",
|
||||
title: "高级表单",
|
||||
icon: "Menu",
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
url: "/system",
|
||||
title: "表格管理",
|
||||
icon: "Grid",
|
||||
children: [
|
||||
{
|
||||
url: "/Department",
|
||||
title: "基础表格",
|
||||
icon: "Menu",
|
||||
},
|
||||
{
|
||||
url: "/UserList",
|
||||
title: "内嵌表格",
|
||||
icon: "Menu",
|
||||
},
|
||||
{
|
||||
url: "/RoleList",
|
||||
title: "滑动加载",
|
||||
icon: "Menu",
|
||||
},
|
||||
{
|
||||
url: "/MenuList",
|
||||
title: "可编辑Table",
|
||||
icon: "Menu",
|
||||
},
|
||||
{
|
||||
url: "/importExcel",
|
||||
title: "导入Excel",
|
||||
icon: "Menu",
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
url: "/goods",
|
||||
title: "列表页",
|
||||
icon: "List",
|
||||
children: [
|
||||
{
|
||||
url: "/goodCategory",
|
||||
title: "基础列表",
|
||||
icon: "Menu",
|
||||
},
|
||||
{
|
||||
url: "/cardList",
|
||||
title: "卡片列表",
|
||||
icon: "Menu",
|
||||
},
|
||||
{
|
||||
url: "/searchList",
|
||||
title: "搜索列表",
|
||||
icon: "Menu",
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
url: "/ErrorMessage",
|
||||
title: "异常页面",
|
||||
icon: "WarningFilled",
|
||||
children: [
|
||||
{
|
||||
url: "/404",
|
||||
title: "404",
|
||||
icon: "Menu",
|
||||
},
|
||||
{
|
||||
url: "/500",
|
||||
title: "500",
|
||||
icon: "Menu",
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
||||
export default menuTable;
|
||||
@@ -0,0 +1,31 @@
|
||||
import {
|
||||
createApp
|
||||
} from "vue";
|
||||
import router from "./router/index";
|
||||
import store from "../src/store/index";
|
||||
import './styles/common.scss'
|
||||
import ElementPlus from "element-plus";
|
||||
import zhCn from "element-plus/es/locale/lang/zh-cn";
|
||||
// 引入注册自定义指令
|
||||
import directives from "./instruction/index.js";
|
||||
import "element-plus/dist/index.css";
|
||||
import App from "./App.vue";
|
||||
import * as ELIcons from "@element-plus/icons-vue";
|
||||
|
||||
import VMdEditor from "@kangc/v-md-editor";
|
||||
import "@kangc/v-md-editor/lib/style/base-editor.css";
|
||||
import "@kangc/v-md-editor/lib/theme/style/vuepress.css";
|
||||
|
||||
const app = createApp(App);
|
||||
|
||||
// 小图标引用
|
||||
for (const iconName in ELIcons) {
|
||||
// 注册组件
|
||||
app.component(iconName, ELIcons[iconName]);
|
||||
}
|
||||
|
||||
app.use(router).use(store).use(directives).use(VMdEditor).use(ElementPlus, {
|
||||
locale: zhCn,
|
||||
});
|
||||
|
||||
app.mount("#app");
|
||||
@@ -0,0 +1,115 @@
|
||||
// 引入mockjs
|
||||
|
||||
export const menuListItem = [
|
||||
{
|
||||
title: "首页",
|
||||
url: "/home",
|
||||
icon: "HomeFilled",
|
||||
},
|
||||
{
|
||||
title: "系统配置",
|
||||
url: "/system",
|
||||
icon: "Setting",
|
||||
children: [
|
||||
{
|
||||
title: "小程序首页",
|
||||
url: "/homesite",
|
||||
icon: "Menu",
|
||||
},
|
||||
{
|
||||
title: "客服电话",
|
||||
url: "/customer",
|
||||
icon: "Menu",
|
||||
},
|
||||
{
|
||||
title: "活动管理",
|
||||
url: "/activity",
|
||||
icon: "Menu",
|
||||
},
|
||||
{
|
||||
title: "重复下单开关",
|
||||
url: "/switchOrder",
|
||||
icon: "Menu",
|
||||
},
|
||||
]
|
||||
},
|
||||
{
|
||||
title: "服务项目",
|
||||
url: "/goods",
|
||||
icon: "Goods",
|
||||
},
|
||||
{
|
||||
title: "订单管理",
|
||||
url: "/order",
|
||||
icon: "ShoppingCart",
|
||||
},
|
||||
{
|
||||
title: "用户管理",
|
||||
url: "/user",
|
||||
icon: "User",
|
||||
children: [
|
||||
{
|
||||
title: "客户",
|
||||
url: "/index",
|
||||
icon: "Menu",
|
||||
},
|
||||
{
|
||||
title: "勘测人员",
|
||||
url: "/reconnaissanceList",
|
||||
icon: "Menu",
|
||||
},
|
||||
{
|
||||
title: "维修人员",
|
||||
url: "/maintainList",
|
||||
icon: "Menu",
|
||||
},
|
||||
]
|
||||
},
|
||||
{
|
||||
title: "工程师审核",
|
||||
url: "/engineer",
|
||||
icon: "Postcard",
|
||||
children: [
|
||||
{
|
||||
title: "勘测工程师",
|
||||
url: "/reconnaissance",
|
||||
icon: "Menu",
|
||||
},
|
||||
{
|
||||
title: "维修工程师",
|
||||
url: "/maintain",
|
||||
icon: "Menu",
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
title: "工程师排班",
|
||||
url: "/scheduling",
|
||||
icon: "Calendar",
|
||||
children: [
|
||||
{
|
||||
title: "排班时段",
|
||||
url: "/timeFrame",
|
||||
icon: "Menu",
|
||||
},
|
||||
{
|
||||
title: "勘测工程师",
|
||||
url: "/reconnaissanceScheduling",
|
||||
icon: "Menu",
|
||||
},
|
||||
{
|
||||
title: "维修工程师",
|
||||
url: "/maintainScheduling",
|
||||
icon: "Menu",
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
title: "修改资料",
|
||||
url: "/edit",
|
||||
icon: "Edit",
|
||||
},
|
||||
|
||||
];
|
||||
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
import router from "./router.js";
|
||||
import NProgress from "nprogress";
|
||||
import "nprogress/nprogress.css";
|
||||
import store from "../store";
|
||||
NProgress.configure({
|
||||
ease: "ease",
|
||||
speed: 500,
|
||||
});
|
||||
|
||||
const writeNames = ["/login"];
|
||||
router.beforeEach((to, from, next) => {
|
||||
NProgress.start();
|
||||
console.log(store.getters);
|
||||
if (sessionStorage.getItem("token")) {
|
||||
if (to.path === "/login") {
|
||||
next("/");
|
||||
}
|
||||
return next();
|
||||
} else {
|
||||
if (writeNames.includes(to.path)) {
|
||||
next();
|
||||
} else {
|
||||
next("/login");
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
router.afterEach((transition) => {
|
||||
NProgress.done();
|
||||
});
|
||||
|
||||
export default router;
|
||||
@@ -0,0 +1,265 @@
|
||||
// 模板自带的功能路由表
|
||||
import login from "../views/login/index.vue";
|
||||
import Layout from "../layout/index.vue";
|
||||
const routerList = [
|
||||
{
|
||||
path: "/login",
|
||||
name: login,
|
||||
component: login,
|
||||
},
|
||||
{
|
||||
path: "/",
|
||||
component: Layout,
|
||||
name: "container",
|
||||
redirect: "home",
|
||||
meta: {
|
||||
requiresAuth: true, //有一些页面是否登录才能进去
|
||||
name: "首页",
|
||||
},
|
||||
children: [
|
||||
{
|
||||
path: "/home",
|
||||
name: "home",
|
||||
component: () => import("../views/home/index.vue"),
|
||||
meta: {
|
||||
requiresAuth: true, //有一些页面是否登录才能进去
|
||||
name: "首页",
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
path: "/system",
|
||||
component: Layout,
|
||||
name: "system",
|
||||
meta: {
|
||||
name: "系统配置",
|
||||
requiresAuth: true, //有一些页面是否登录才能进去
|
||||
},
|
||||
children: [
|
||||
{
|
||||
path: "/homeSite",
|
||||
name: "homesite",
|
||||
component: () => import("../views/system/homeSite.vue"),
|
||||
meta: {
|
||||
name: "小程序首页",
|
||||
requiresAuth: true, //有一些页面是否登录才能进去
|
||||
},
|
||||
},
|
||||
{
|
||||
path: "/customer",
|
||||
name: "customer",
|
||||
component: () => import("../views/system/customer.vue"),
|
||||
meta: {
|
||||
name: "客服电话",
|
||||
requiresAuth: true, //有一些页面是否登录才能进去
|
||||
},
|
||||
},
|
||||
{
|
||||
path: "/activity",
|
||||
name: "activity",
|
||||
component: () => import("../views/system/activity.vue"),
|
||||
meta: {
|
||||
name: "活动管理",
|
||||
requiresAuth: true, //有一些页面是否登录才能进去
|
||||
},
|
||||
},
|
||||
{
|
||||
path: "/switchOrder",
|
||||
name: "switchOrder",
|
||||
component: () => import("../views/system/switchOrder.vue"),
|
||||
meta: {
|
||||
name: "重复下单开关",
|
||||
requiresAuth: true, //有一些页面是否登录才能进去
|
||||
},
|
||||
},
|
||||
{
|
||||
path: "/edit",
|
||||
name: "edit",
|
||||
component: () => import("../views/system/edit.vue"),
|
||||
meta: {
|
||||
name: "编辑资料",
|
||||
requiresAuth: true, //有一些页面是否登录才能进去
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
|
||||
{
|
||||
path: "/goods",
|
||||
component: Layout,
|
||||
redirect: "goods",
|
||||
meta: {
|
||||
requiresAuth: true, //有一些页面是否登录才能进去
|
||||
name: "服务项目",
|
||||
},
|
||||
children: [
|
||||
{
|
||||
path: "/goods",
|
||||
name: "goods",
|
||||
component: () => import("../views/goods/index.vue"),
|
||||
meta: {
|
||||
requiresAuth: true, //有一些页面是否登录才能进去
|
||||
name: "服务项目",
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
path: "/order",
|
||||
component: Layout,
|
||||
redirect: "order",
|
||||
meta: {
|
||||
requiresAuth: true, //有一些页面是否登录才能进去
|
||||
name: "订单",
|
||||
},
|
||||
children: [
|
||||
{
|
||||
path: "/order",
|
||||
name: "order",
|
||||
component: () => import("../views/order/index.vue"),
|
||||
meta: {
|
||||
requiresAuth: true, //有一些页面是否登录才能进去
|
||||
name: "订单列表",
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
path: "/users",
|
||||
name: "users",
|
||||
component: Layout,
|
||||
meta: {
|
||||
name: "客户",
|
||||
},
|
||||
children: [
|
||||
{
|
||||
path: "/index",
|
||||
name: "index",
|
||||
component: () => import("../views/users/index.vue"),
|
||||
meta: {
|
||||
requiresAuth: true,
|
||||
name: "客户列表",
|
||||
},
|
||||
},
|
||||
{
|
||||
path: "/reconnaissanceList",
|
||||
name: "reconnaissanceList",
|
||||
component: () => import("../views/users/reconnaissance.vue"),
|
||||
meta: {
|
||||
requiresAuth: true,
|
||||
name: "勘测工程师",
|
||||
},
|
||||
},
|
||||
{
|
||||
path: "/maintainList",
|
||||
name: "maintainList",
|
||||
component: () => import("../views/users/maintain.vue"),
|
||||
meta: {
|
||||
requiresAuth: true,
|
||||
name: "维修人员",
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
path: "/engineer",
|
||||
component: Layout,
|
||||
name: "engineer",
|
||||
meta: {
|
||||
requiresAuth: true, //有一些页面是否登录才能进去
|
||||
name: "工程师审核",
|
||||
},
|
||||
children: [
|
||||
{
|
||||
path: "/reconnaissance",
|
||||
name: "reconnaissance",
|
||||
component: () => import("../views/engineer/reconnaissance.vue"),
|
||||
meta: {
|
||||
requiresAuth: true,
|
||||
name: "勘测工程师审核",
|
||||
},
|
||||
},
|
||||
{
|
||||
path: "/maintain",
|
||||
name: "maintain",
|
||||
component: () => import("../views/engineer/maintain.vue"),
|
||||
meta: {
|
||||
requiresAuth: true,
|
||||
name: "维修人员审核",
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
path: "/scheduling",
|
||||
component: Layout,
|
||||
name: "scheduling",
|
||||
meta: {
|
||||
requiresAuth: true, //有一些页面是否登录才能进去
|
||||
name: "工程师审核",
|
||||
},
|
||||
children: [
|
||||
{
|
||||
path: "/timeFrame",
|
||||
name: "timeFrame",
|
||||
component: () => import("../views/scheduling/timeFrame.vue"),
|
||||
meta: {
|
||||
requiresAuth: true,
|
||||
name: "排班时段",
|
||||
},
|
||||
},
|
||||
{
|
||||
path: "/reconnaissanceScheduling",
|
||||
name: "reconnaissanceScheduling",
|
||||
component: () => import("../views/scheduling/reconnaissance.vue"),
|
||||
meta: {
|
||||
requiresAuth: true,
|
||||
name: "勘测人员排班",
|
||||
},
|
||||
},
|
||||
{
|
||||
path: "/maintainScheduling",
|
||||
name: "maintainScheduling",
|
||||
component: () => import("../views/scheduling/maintain.vue"),
|
||||
meta: {
|
||||
requiresAuth: true,
|
||||
name: "维修人员排班",
|
||||
},
|
||||
},
|
||||
|
||||
],
|
||||
},
|
||||
|
||||
{
|
||||
path: "/ErrorMessage",
|
||||
name: "ErrorMessage",
|
||||
component: Layout,
|
||||
meta: {
|
||||
name: "异常页面",
|
||||
},
|
||||
children: [
|
||||
{
|
||||
path: "/404",
|
||||
name: "404",
|
||||
component: () => import("../views/ErrorMessage/404.vue"),
|
||||
meta: {
|
||||
requiresAuth: true, //有一些页面是否登录才能进去
|
||||
name: "404",
|
||||
},
|
||||
},
|
||||
{
|
||||
path: "/500",
|
||||
name: "500",
|
||||
component: () => import("../views/ErrorMessage/500.vue"),
|
||||
meta: {
|
||||
requiresAuth: true, //有一些页面是否登录才能进去
|
||||
name: "500",
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
|
||||
|
||||
];
|
||||
export default routerList;
|
||||
@@ -0,0 +1,15 @@
|
||||
import { createRouter, createWebHashHistory } from "vue-router";
|
||||
import home from "../views/home/index.vue";
|
||||
import login from "../views/login/index.vue";
|
||||
import Layout from "../layout/index.vue";
|
||||
// 原本模板功能路由表
|
||||
import routerList from "./originalRoutingTable.js";
|
||||
|
||||
const routes = routerList;
|
||||
|
||||
const router = createRouter({
|
||||
history: createWebHashHistory(),
|
||||
routes,
|
||||
});
|
||||
|
||||
export default router;
|
||||
@@ -0,0 +1,9 @@
|
||||
const getters = {
|
||||
token: (state) => state.user.token,
|
||||
isCollapse: (state) => state.user.isCollapse,
|
||||
UserInfo: (state) => state.user.UserInfo,
|
||||
themeConfig: (state) => state.user.themeConfig,
|
||||
tabsMenuList: (state) => state.tabs.tabsMenuList
|
||||
};
|
||||
|
||||
export default getters;
|
||||
@@ -0,0 +1,23 @@
|
||||
import {
|
||||
createStore
|
||||
} from "vuex";
|
||||
import user from "./modules/users";
|
||||
import tabs from './modules/tabs'
|
||||
import getters from "./getters";
|
||||
import createPersistedState from "vuex-persistedstate";
|
||||
export default createStore({
|
||||
getters,
|
||||
modules: {
|
||||
user,
|
||||
tabs
|
||||
},
|
||||
plugins: [
|
||||
// 默认储存在localstorage
|
||||
createPersistedState({
|
||||
// 本地储存名
|
||||
key: "user",
|
||||
// 指定模块
|
||||
paths: ["user"],
|
||||
}),
|
||||
],
|
||||
});
|
||||
@@ -0,0 +1,69 @@
|
||||
import {
|
||||
ElStep
|
||||
} from "element-plus";
|
||||
import router from "../../router/router.js";
|
||||
export default {
|
||||
namespaced: true,
|
||||
state: {
|
||||
tabsMenuList: [{
|
||||
title: "首页",
|
||||
url: 'home',
|
||||
icon: "Menu",
|
||||
close: false,
|
||||
checked: true
|
||||
}]
|
||||
},
|
||||
mutations: {
|
||||
addTabsMenu(state, obj) {
|
||||
|
||||
let titles = state.tabsMenuList.map(item => item.title).join(',')
|
||||
if (titles.includes(obj.title)) {
|
||||
for (let index = 0; index < state.tabsMenuList.length; index++) {
|
||||
const element = state.tabsMenuList[index];
|
||||
state.tabsMenuList[index].checked = false
|
||||
if (element.title == obj.title) {
|
||||
state.tabsMenuList[index].checked = true
|
||||
}
|
||||
}
|
||||
} else {
|
||||
state.tabsMenuList.forEach(item => {
|
||||
item.checked = false
|
||||
});
|
||||
state.tabsMenuList.push(obj)
|
||||
}
|
||||
},
|
||||
|
||||
delectTagMenu(state, item) {
|
||||
if (state.tabsMenuList.length == 0) {
|
||||
return
|
||||
}
|
||||
for (let i = 0; i < state.tabsMenuList.length; i++) {
|
||||
let ele = state.tabsMenuList[i]
|
||||
console.log(ele)
|
||||
if (ele == item) {
|
||||
state.tabsMenuList[i - 1].checked = true
|
||||
state.tabsMenuList.splice(i, 1)
|
||||
return router.push(state.tabsMenuList[i - 1].url)
|
||||
}
|
||||
}
|
||||
console.log(state.tabsMenuList)
|
||||
}
|
||||
},
|
||||
|
||||
actions: {
|
||||
|
||||
addTabs({
|
||||
commit
|
||||
}, str) {
|
||||
console.log(str);
|
||||
commit("addTabsMenu", str);
|
||||
},
|
||||
delectTag({
|
||||
commit
|
||||
}, val) {
|
||||
|
||||
commit('delectTagMenu', val)
|
||||
}
|
||||
|
||||
},
|
||||
};
|
||||
@@ -0,0 +1,114 @@
|
||||
import { LoginInfo } from "../../api/modules/index.js";
|
||||
import router from "../../router/router.js";
|
||||
import { mix } from "../../utils/color.js";
|
||||
import { ElMessage } from "element-plus";
|
||||
|
||||
export default {
|
||||
namespaced: true,
|
||||
state: {
|
||||
UserInfo: {},
|
||||
token: sessionStorage.getItem("token") || "",
|
||||
isCollapse: false,
|
||||
themeConfig: {
|
||||
primary: "#4060c7",
|
||||
tabColor: "#FFFFFF",
|
||||
footColor: "#606266",
|
||||
backgroundColor: "#FFFFFF",
|
||||
textColor: "#00000099",
|
||||
istags: true,
|
||||
},
|
||||
},
|
||||
mutations: {
|
||||
setToken(state, token) {
|
||||
state.token = token;
|
||||
},
|
||||
setUserInfo(state, userinfo) {
|
||||
state.UserInfo = userinfo;
|
||||
},
|
||||
SetIsCollapse(state, isCollapse) {
|
||||
state.isCollapse = isCollapse;
|
||||
},
|
||||
setThemeConfig(state, primary) {
|
||||
state.themeConfig.primary = primary;
|
||||
},
|
||||
setThemeConfigTbaColor(state, primary) {
|
||||
state.themeConfig.tabColor = primary;
|
||||
if (primary == "#FFFFFF") {
|
||||
state.themeConfig.footColor = "#606266";
|
||||
} else {
|
||||
state.themeConfig.footColor = "#ffffff";
|
||||
}
|
||||
},
|
||||
setThemeConfigMenuColor(state, primary) {
|
||||
if (primary) {
|
||||
state.themeConfig.backgroundColor = "#FFFFFF";
|
||||
state.themeConfig.textColor = "#00000099";
|
||||
} else {
|
||||
state.themeConfig.backgroundColor = "#1d2129";
|
||||
state.themeConfig.textColor = "#bdbdc0";
|
||||
}
|
||||
},
|
||||
setThemeConfigchangeTags(state, primary) {
|
||||
if (primary) {
|
||||
state.themeConfig.istags = true;
|
||||
} else {
|
||||
state.themeConfig.istags = false;
|
||||
}
|
||||
},
|
||||
},
|
||||
|
||||
actions: {
|
||||
// 登录
|
||||
login({ commit }, userInfo) {
|
||||
return new Promise((resolve, reject) => {
|
||||
LoginInfo(userInfo)
|
||||
.then((res) => {
|
||||
console.log(res.data.token, "token");
|
||||
sessionStorage.setItem("token", res.data.token);
|
||||
localStorage.setItem("token", res.data.token);
|
||||
sessionStorage.setItem("UserInfo", JSON.stringify(res.data));
|
||||
commit("setToken", res.data.token);
|
||||
commit("setUserInfo", res.data);
|
||||
router.replace("/");
|
||||
ElMessage({
|
||||
message: "登录成功",
|
||||
type: "success",
|
||||
});
|
||||
resolve();
|
||||
})
|
||||
.catch((err) => {
|
||||
reject(err);
|
||||
});
|
||||
});
|
||||
},
|
||||
changeIsCollapse({ commit }, str) {
|
||||
console.log(str);
|
||||
commit("SetIsCollapse", str);
|
||||
},
|
||||
|
||||
changeThem({ commit }, str) {
|
||||
commit("setThemeConfig", str);
|
||||
const pre = "--el-color-primary";
|
||||
// 白色混合色
|
||||
const mixWhite = "#ffffff";
|
||||
// 黑色混合色
|
||||
const mixBlack = "#000000";
|
||||
const el = document.documentElement;
|
||||
el.style.setProperty(pre, str);
|
||||
// 这里是覆盖原有颜色的核心代码
|
||||
for (let i = 1; i < 10; i += 1) {
|
||||
el.style.setProperty(`${pre}-light-${i}`, mix(str, mixWhite, i * 0.1));
|
||||
}
|
||||
el.style.setProperty("--el-color-primary-dark", mix(str, mixBlack, 0.1));
|
||||
},
|
||||
changeTabColor({ commit }, val) {
|
||||
commit("setThemeConfigTbaColor", val);
|
||||
},
|
||||
changeMenuColor({ commit }, val) {
|
||||
commit("setThemeConfigMenuColor", val);
|
||||
},
|
||||
changeTags({ commit }, val) {
|
||||
commit("setThemeConfigchangeTags", val);
|
||||
},
|
||||
},
|
||||
};
|
||||
@@ -0,0 +1,216 @@
|
||||
/*
|
||||
Reset style sheet
|
||||
*/
|
||||
|
||||
html,
|
||||
body {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
border: 0;
|
||||
font-size: 100%;
|
||||
font: inherit;
|
||||
vertical-align: baseline;
|
||||
}
|
||||
|
||||
/* HTML5 display-role reset for older browsers */
|
||||
article,
|
||||
aside,
|
||||
details,
|
||||
figcaption,
|
||||
figure,
|
||||
footer,
|
||||
header,
|
||||
hgroup,
|
||||
menu,
|
||||
nav,
|
||||
section {
|
||||
display: block;
|
||||
}
|
||||
|
||||
|
||||
ol,
|
||||
ul {
|
||||
list-style: none;
|
||||
}
|
||||
|
||||
blockquote,
|
||||
q {
|
||||
quotes: none;
|
||||
}
|
||||
|
||||
blockquote:before,
|
||||
blockquote:after,
|
||||
q:before,
|
||||
q:after {
|
||||
content: "";
|
||||
content: none;
|
||||
}
|
||||
|
||||
table {
|
||||
border-collapse: collapse;
|
||||
border-spacing: 0;
|
||||
}
|
||||
|
||||
html,
|
||||
body,
|
||||
#app {
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
/* flex */
|
||||
.flx-center {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.flx-justify-between {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.flx-align-center {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.flx-row {
|
||||
display: flex;
|
||||
flex-flow: row;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.flx-row-right {
|
||||
display: flex;
|
||||
flex-flow: row-reverse;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.col-center {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
text-align: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.ml10 {
|
||||
margin-left: 10px;
|
||||
}
|
||||
|
||||
.p10 {
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
.p20 {
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
.m10 {
|
||||
margin: 10px;
|
||||
}
|
||||
|
||||
.m20 {
|
||||
margin: 20px;
|
||||
}
|
||||
|
||||
.mt10 {
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
.mt20 {
|
||||
margin-top: 20px;
|
||||
}
|
||||
|
||||
.w100 {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.ml20 {
|
||||
margin-left: 20px;
|
||||
}
|
||||
|
||||
.mR10 {
|
||||
margin-right: 10px;
|
||||
}
|
||||
|
||||
.one-cut-txt {
|
||||
/*强制文字在一行文本框内*/
|
||||
white-space: nowrap;
|
||||
/*溢出部分文字隐藏*/
|
||||
overflow: hidden;
|
||||
/*溢出部分省略号处理*/
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
/* 清除浮动 */
|
||||
.clearfix::after {
|
||||
content: "";
|
||||
display: block;
|
||||
height: 0px;
|
||||
clear: both;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
/* fade-transform */
|
||||
.fade-transform-leave-active,
|
||||
.fade-transform-enter-active {
|
||||
transition: all 0.2s;
|
||||
}
|
||||
|
||||
.fade-transform-enter-from {
|
||||
opacity: 0;
|
||||
transform: translateX(-30px);
|
||||
transition: all 0.2s;
|
||||
}
|
||||
|
||||
.fade-transform-leave-to {
|
||||
opacity: 0;
|
||||
transform: translateX(30px);
|
||||
transition: all 0.2s;
|
||||
}
|
||||
|
||||
/* Breadcrumb */
|
||||
.breadcrumb-enter-active,
|
||||
.breadcrumb-leave-active {
|
||||
transition: all 0.2s ease;
|
||||
}
|
||||
|
||||
.breadcrumb-enter-from,
|
||||
.breadcrumb-leave-active {
|
||||
opacity: 0;
|
||||
transform: translateX(10px);
|
||||
}
|
||||
|
||||
.breadcrumb-leave-active {
|
||||
position: absolute;
|
||||
z-index: -1;
|
||||
}
|
||||
|
||||
/* scroll bar */
|
||||
::-webkit-scrollbar {
|
||||
width: 8px;
|
||||
height: 8px;
|
||||
background-color: white;
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-thumb {
|
||||
box-shadow: inset 0 0 0px white;
|
||||
-webkit-box-shadow: inset 0 0 0px white;
|
||||
background-color: #dddee0;
|
||||
border-radius: 20px;
|
||||
}
|
||||
|
||||
.content-box {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
height: 100%;
|
||||
font-size: 23px;
|
||||
font-weight: bold;
|
||||
color: rgb(88, 88, 88);
|
||||
line-height: 100px;
|
||||
min-height: 400px;
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
export const mix = (c1, c2, ratio) => {
|
||||
ratio = Math.max(Math.min(Number(ratio), 1), 0)
|
||||
let r1 = parseInt(c1.substring(1, 3), 16)
|
||||
let g1 = parseInt(c1.substring(3, 5), 16)
|
||||
let b1 = parseInt(c1.substring(5, 7), 16)
|
||||
let r2 = parseInt(c2.substring(1, 3), 16)
|
||||
let g2 = parseInt(c2.substring(3, 5), 16)
|
||||
let b2 = parseInt(c2.substring(5, 7), 16)
|
||||
let r = Math.round(r1 * (1 - ratio) + r2 * ratio) + ''
|
||||
let g = Math.round(g1 * (1 - ratio) + g2 * ratio) + ''
|
||||
let b = Math.round(b1 * (1 - ratio) + b2 * ratio) + ''
|
||||
r = ('0' + (r || 0).toString(16)).slice(-2)
|
||||
g = ('0' + (g || 0).toString(16)).slice(-2)
|
||||
b = ('0' + (b || 0).toString(16)).slice(-2)
|
||||
return '#' + r + g + b
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
export function getDateTime(type) {
|
||||
var date = new Date();
|
||||
var hengGang = "-";
|
||||
var maoHao = ":";
|
||||
var year = date.getFullYear();
|
||||
var month = date.getMonth() + 1;
|
||||
var curDate = date.getDate();
|
||||
var curHours = date.getHours();
|
||||
var curMinutes = date.getMinutes();
|
||||
var curSeconds = date.getSeconds();
|
||||
|
||||
if (month >= 1 && month <= 9) {
|
||||
month = "0" + month;
|
||||
}
|
||||
if (curDate >= 0 && curDate <= 9) {
|
||||
curDate = "0" + curDate;
|
||||
}
|
||||
if (curHours >= 0 && curHours <= 9) {
|
||||
curHours = "0" + curHours;
|
||||
}
|
||||
if (curMinutes >= 0 && curMinutes <= 9) {
|
||||
curMinutes = "0" + curMinutes;
|
||||
}
|
||||
if (curSeconds >= 0 && curSeconds <= 9) {
|
||||
curSeconds = "0" + curSeconds;
|
||||
}
|
||||
var currentdate = "";
|
||||
if (type == "year") {
|
||||
currentdate = year;
|
||||
return currentdate;
|
||||
} else if (type == "month") {
|
||||
currentdate = year + hengGang + month;
|
||||
return currentdate;
|
||||
} else {
|
||||
currentdate = year + hengGang + month + hengGang + curDate + " "
|
||||
return currentdate;
|
||||
}
|
||||
}
|
||||
// var year = getDateTime('year');
|
||||
// console.log(year); // 2021
|
||||
// var month = getDateTime('month');
|
||||
// console.log(month); // 12
|
||||
// var date = getDateTime('');
|
||||
// console.log(date); // 2021-12-03 09:00:00
|
||||
@@ -0,0 +1,456 @@
|
||||
<template>
|
||||
<div class="col-center">
|
||||
<svg
|
||||
width="400px"
|
||||
height="300px"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<circle opacity=".3" cx="33.329" cy="107.131" r="5.356" fill="#4060c7" />
|
||||
<path
|
||||
d="M373.107 238.514h-13.863c-1.471-7.141-2.584-21.424 4.726-21.424 7.309 0 9.137 14.283 9.137 21.424Z"
|
||||
fill="#E2E5EC"
|
||||
/>
|
||||
<path
|
||||
d="M364.151 221.816c.84 1.785 2.52 7.499 2.52 16.068"
|
||||
stroke="#FFFEFE"
|
||||
stroke-width="1.26"
|
||||
stroke-linecap="round"
|
||||
/>
|
||||
<path
|
||||
d="M355.958 238.514H341.78c1.786-4.096 6.365-12.162 10.398-11.657 4.032.504 4.2 7.981 3.78 11.657Z"
|
||||
fill="#E2E5EC"
|
||||
/>
|
||||
<path
|
||||
d="M349.657 232.843c-.525 1.576-1.575 5.041-1.575 6.302"
|
||||
stroke="#FFFEFE"
|
||||
stroke-width="1.26"
|
||||
stroke-linecap="round"
|
||||
/>
|
||||
<path
|
||||
opacity=".6"
|
||||
d="M34.84 201.221c-40.337-44.164 20.284-44.435 23.447-67.198 7.684-55.319 57.32-93.027 111.792-79.099 73.24 18.719 67.033 76.611 129.865 64.354 80.714-15.706 114.15 37.333 51.403 94.438-32.732 29.792 19.258 25.667 19.258 25.667l-294.482 1.022c-34.78-30.41 7.822-1.098-17.836-10.077-32.063-11.201 3.097-.046-23.447-29.107Z"
|
||||
fill="url(#a)"
|
||||
/>
|
||||
<path
|
||||
d="M22.301 239.144h350.356M376.932 239.144h5.986"
|
||||
stroke="#011650"
|
||||
stroke-width="1.26"
|
||||
stroke-linecap="round"
|
||||
/>
|
||||
<path
|
||||
d="M50.177 238.514h-25.34C18.878 228.2 9.95 207.011 21.901 204.77c14.938-2.8 18.806 16.005 18.806 18.406 7.682-2.454 9.514 9.203 9.47 15.338Z"
|
||||
fill="#E2E5EC"
|
||||
/>
|
||||
<path
|
||||
d="M25.966 212.639c1.823 2.934 6.037 11.17 10.198 24.615M43.572 227.177l-3.334 6.936"
|
||||
stroke="#fff"
|
||||
stroke-width="1.26"
|
||||
stroke-linecap="round"
|
||||
/>
|
||||
<path
|
||||
d="M128.521 238.514h-15.157v-13.863h-45.49c-4.55 0-7.6-4.673-5.768-8.838l50.548-114.947a10.083 10.083 0 0 1 9.229-6.023h.336a6.301 6.301 0 0 1 6.302 6.301v109.3h25.425a7.103 7.103 0 0 1 7.103 7.103 7.103 7.103 0 0 1-7.103 7.104h-25.425v13.863Zm-15.157-109.101-34.98 81.031h34.98v-81.031ZM160.419 167.918c0-18.586 3.76-34.942 11.279-49.067 7.637-14.126 16.39-21.189 26.26-21.189 9.869 0 18.622 7.063 26.259 21.189 7.636 14.125 11.455 30.543 11.455 49.253s-3.819 35.128-11.455 49.254c-7.637 14.125-16.39 21.188-26.259 21.188-9.87 0-18.623-7.063-26.26-21.188-7.519-14.126-11.279-30.606-11.279-49.44Zm16.214-1.074c0 17.223 2.232 32.051 6.697 41.964 4.465 9.789 9.34 14.683 14.628 14.683 5.287 0 10.162-4.894 14.627-14.683 4.582-9.913 6.873-23.543 6.873-40.89 0-17.719-2.291-31.348-6.873-40.889-4.465-9.541-9.34-14.312-14.627-14.312-5.288 0-10.163 4.771-14.628 14.312s-6.697 21.972-6.697 39.815ZM308.986 238.514H293.83v-13.863h-45.9c-4.557 0-7.607-4.688-5.761-8.854L293.11 100.84a10.083 10.083 0 0 1 9.218-5.997h.357a6.301 6.301 0 0 1 6.301 6.301v109.3h22.856a7.103 7.103 0 0 1 7.103 7.103 7.103 7.103 0 0 1-7.103 7.104h-22.856v13.863ZM293.83 129.413l-35.227 81.031h35.227v-81.031Z"
|
||||
fill="#CDDAFE"
|
||||
/>
|
||||
<mask
|
||||
id="b"
|
||||
style="mask-type: alpha"
|
||||
maskUnits="userSpaceOnUse"
|
||||
x="61"
|
||||
y="94"
|
||||
width="278"
|
||||
height="145"
|
||||
>
|
||||
<path
|
||||
d="M128.521 238.514h-15.156v-13.863h-45.49c-4.55 0-7.6-4.673-5.769-8.838l53.198-120.97h13.217v115.601h25.425a7.103 7.103 0 1 1 0 14.207h-25.425v13.863Zm-15.156-109.101-34.98 81.031h34.98v-81.031ZM160.42 167.918c0-18.586 3.76-34.942 11.279-49.067 7.637-14.126 16.39-21.189 26.259-21.189s18.622 7.063 26.259 21.189c7.637 14.125 11.455 30.543 11.455 49.253s-3.818 35.128-11.455 49.254c-7.637 14.125-16.39 21.188-26.259 21.188s-18.622-7.063-26.259-21.188c-7.519-14.126-11.279-30.606-11.279-49.44Zm16.214.186c0 17.223 2.232 30.791 6.696 40.704 4.465 9.789 9.341 14.683 14.628 14.683 5.287 0 10.163-4.894 14.628-14.683 4.582-9.913 6.873-23.543 6.873-40.89 0-17.719-2.291-31.348-6.873-40.889-4.465-9.541-9.341-14.312-14.628-14.312-5.287 0-10.163 4.771-14.628 14.312-4.464 9.541-6.696 23.232-6.696 41.075ZM308.986 238.514H293.83v-13.863h-45.9c-4.557 0-7.607-4.688-5.761-8.854l53.6-120.954h13.217v115.601h22.856a7.104 7.104 0 1 1 0 14.207h-22.856v13.863ZM293.83 129.413l-35.227 81.031h35.227v-81.031Z"
|
||||
fill="#CDDAFE"
|
||||
/>
|
||||
</mask>
|
||||
<g mask="url(#b)">
|
||||
<ellipse
|
||||
opacity=".8"
|
||||
cx="198.741"
|
||||
cy="177.076"
|
||||
rx="44.11"
|
||||
ry="63.329"
|
||||
transform="rotate(-180 198.741 177.076)"
|
||||
fill="url(#c)"
|
||||
/>
|
||||
<ellipse
|
||||
cx="197.48"
|
||||
cy="201.651"
|
||||
rx="44.11"
|
||||
ry="57.027"
|
||||
fill="url(#d)"
|
||||
/>
|
||||
<ellipse
|
||||
opacity=".6"
|
||||
cx="54.998"
|
||||
cy="195.995"
|
||||
rx="44.11"
|
||||
ry="58.603"
|
||||
transform="rotate(22.14 54.998 195.995)"
|
||||
fill="url(#e)"
|
||||
/>
|
||||
<ellipse
|
||||
opacity=".6"
|
||||
cx="247.196"
|
||||
cy="226.466"
|
||||
rx="19.626"
|
||||
ry="58.603"
|
||||
transform="rotate(22.14 247.196 226.466)"
|
||||
fill="url(#f)"
|
||||
/>
|
||||
<path
|
||||
d="m71.138 224.966 7.561-14.808h34.658v14.808h-42.22Z"
|
||||
fill="#8BD9FC"
|
||||
/>
|
||||
<mask
|
||||
id="g"
|
||||
style="mask-type: alpha"
|
||||
maskUnits="userSpaceOnUse"
|
||||
x="71"
|
||||
y="210"
|
||||
width="43"
|
||||
height="15"
|
||||
>
|
||||
<path
|
||||
d="m71.138 224.966 7.561-14.808h34.658v14.808h-42.22Z"
|
||||
fill="#8BD9FC"
|
||||
/>
|
||||
</mask>
|
||||
<g mask="url(#g)">
|
||||
<ellipse
|
||||
rx="20.794"
|
||||
ry="31.822"
|
||||
transform="matrix(-1 0 0 1 74.603 213.624)"
|
||||
fill="url(#h)"
|
||||
style="mix-blend-mode: multiply"
|
||||
opacity=".6"
|
||||
/>
|
||||
</g>
|
||||
<path
|
||||
d="m251.042 225.281 7.715-15.123h35.134v15.123h-42.849Z"
|
||||
fill="#8BD9FC"
|
||||
/>
|
||||
<mask
|
||||
id="i"
|
||||
style="mask-type: alpha"
|
||||
maskUnits="userSpaceOnUse"
|
||||
x="251"
|
||||
y="210"
|
||||
width="43"
|
||||
height="16"
|
||||
>
|
||||
<path
|
||||
d="m251.042 225.281 7.715-15.123h35.134v15.123h-42.849Z"
|
||||
fill="#8BD9FC"
|
||||
/>
|
||||
</mask>
|
||||
<g mask="url(#i)">
|
||||
<ellipse
|
||||
rx="20.794"
|
||||
ry="31.822"
|
||||
transform="matrix(-1 0 0 1 255.453 213.624)"
|
||||
fill="url(#j)"
|
||||
style="mix-blend-mode: multiply"
|
||||
opacity=".6"
|
||||
/>
|
||||
</g>
|
||||
<path
|
||||
d="M128.48 210.158h25.521a7.248 7.248 0 0 1 0 14.493H128.48v-14.493ZM309.014 210.158h23a7.248 7.248 0 0 1 0 14.493h-23v-14.493Z"
|
||||
fill="#8BD9FC"
|
||||
/>
|
||||
<path
|
||||
d="M128.48 224.651v-14.178l6.932 14.178h-6.932ZM309.014 224.651v-14.178l6.932 14.178h-6.932Z"
|
||||
fill="#6DBADD"
|
||||
/>
|
||||
<path
|
||||
d="m113.042 129.815 15.438-29.931v29.931h-15.438Z"
|
||||
fill="url(#k)"
|
||||
/>
|
||||
<path
|
||||
d="m293.891 129.815 15.123-29.616.315 29.616h-15.438Z"
|
||||
fill="url(#l)"
|
||||
/>
|
||||
<path
|
||||
d="M114.016 94.493c.371 4.57-2.625 17.776-11.171 34.503-10.683 20.91-39.795 91.472-41.474 97.545-1.343 4.859 2.267 4.876 3.78 0 .997-3.211 25.43-63.343 35.139-77.657 12.137-17.893 13.825-42.923 19.528-54.095 5.703-11.172 7.848-4.138 9.239-2.677 1.266 1.33 1.996-21.022-1.682-9.29-4.958 8.871-18.931 70.821-28.511 81.651-7.247 8.191-23.031 41.427-19.85 45.685M295.753 91.063c.371 4.57-2.453 19.504-10.999 36.232-10.683 20.91-39.967 89.743-41.646 95.817-1.343 4.859 2.268 4.876 3.781 0 .996-3.211 25.429-63.343 35.138-77.657 12.137-17.893 13.825-42.923 19.528-54.095 5.703-11.172 7.848-4.138 9.239-2.677 1.266 1.33 1.997-21.022-1.682-9.29-4.958 8.871-19.819 71.415-29.399 82.244-7.247 8.192-31.223 60.647-28.041 64.905"
|
||||
stroke="#fff"
|
||||
stroke-width=".441"
|
||||
/>
|
||||
<circle
|
||||
opacity=".3"
|
||||
cx="208.192"
|
||||
cy="118.788"
|
||||
r="4.411"
|
||||
fill="#758CD6"
|
||||
/>
|
||||
<circle
|
||||
opacity=".3"
|
||||
cx="214.179"
|
||||
cy="130.446"
|
||||
r=".945"
|
||||
fill="#758CD6"
|
||||
/>
|
||||
<circle opacity=".3" cx="214.494" cy="109.966" r=".63" fill="#758CD6" />
|
||||
<circle
|
||||
opacity=".3"
|
||||
cx="229.932"
|
||||
cy="132.336"
|
||||
r="1.575"
|
||||
fill="#758CD6"
|
||||
/>
|
||||
<circle
|
||||
opacity=".3"
|
||||
cx="230.877"
|
||||
cy="147.144"
|
||||
r="1.26"
|
||||
fill="#758CD6"
|
||||
/>
|
||||
<circle
|
||||
opacity=".3"
|
||||
cx="218.905"
|
||||
cy="138.952"
|
||||
r="5.041"
|
||||
fill="#758CD6"
|
||||
/>
|
||||
<circle
|
||||
opacity=".3"
|
||||
cx="227.727"
|
||||
cy="117.528"
|
||||
r="6.301"
|
||||
fill="#758CD6"
|
||||
/>
|
||||
</g>
|
||||
<path
|
||||
d="M373.918 77.64c1.524.761 2.221 2.468-.367 2.835m-2.469 0c-6.617-.63-26.781-6.617-35.288-14.808"
|
||||
stroke="#F6BCD1"
|
||||
stroke-width=".945"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
/>
|
||||
<path
|
||||
d="M363.836 56.53c-12.099-4.538-18.484 3.15-20.165 7.56.42.841 2.836 4.412 10.082 7.563 7.031 3.056 13.863 3.36 16.699 3.15 1.575-4.2 2.458-13.737-6.616-18.274Z"
|
||||
fill="#F6BCD1"
|
||||
/>
|
||||
<path
|
||||
d="M359.424 61.57c-1.105-.553-2.835-.82-4.096 2.206-.504 1.26.21 3.045.631 3.78-1.051 0-3.214.378-3.466 1.89 0 .316 0 .63 1.575 1.261 1.26.504 2.416 1.05 2.836 1.26.21.105.567.378.315.63-.209.21-.577.144-.816.056l-.129-.056c.035.018.079.038.129.056l1.698.728c.065.235.249.226.378.162l-.378-.162c-.041-.149-.034-.394.063-.784.315-1.26.945-3.465-.945-4.41.63 0 2.52-.316 3.151-2.521.63-2.206.315-3.466-.946-4.096Z"
|
||||
fill="#fff"
|
||||
/>
|
||||
<path
|
||||
d="M341.466 61.255c-3.256-.525-9.263-.82-9.768 2.206-.504 3.024 7.247 9.767 20.795 14.808 15.159 5.64 25.881 5.986 26.781 2.835.9-3.15-1.891-5.04-4.726-6.616"
|
||||
stroke="#F6BCD1"
|
||||
stroke-width="1.26"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
/>
|
||||
<path
|
||||
stroke="#F6BCD1"
|
||||
stroke-width=".945"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
d="m374.516 60.215-1.764 1.323M372.642 57.963l-1.103 1.91M375.965 62.988h-2.205"
|
||||
/>
|
||||
<path
|
||||
d="m344.616 81.61-6.301 4.726M356.235 85.748v9.095"
|
||||
stroke="#F6BCD1"
|
||||
stroke-width="1.26"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
/>
|
||||
<path
|
||||
d="M182.185 184.416c3.269 2.569 13.913 20.531 18.973 29.19l-14.887 7.882c-1.07-1.265-4.32-6.072-7.589-9.341 1.167 3.269 2.53 18.584 2.919 25.103a3067.97 3067.97 0 0 0-16.347 3.211c-1.634-2.101-2.335-22.476-9.049-29.774l-3.502-3.21 2.043-11.677 20.141-14.886c1.07.097 4.028.934 7.298 3.502Z"
|
||||
fill="#5783FC"
|
||||
/>
|
||||
<path
|
||||
d="m192.438 232.212 23.315-11.599c-1.459-2.336-5.546-1.752-7.881-1.752-1.868 0-5.254-3.503-6.714-5.254l-15.179 7.589 6.459 11.016Z"
|
||||
fill="#020641"
|
||||
/>
|
||||
<path
|
||||
stroke="#fff"
|
||||
stroke-width=".584"
|
||||
d="m199.869 217.319 3.503-1.751M200.584 218.747l3.956-2.012"
|
||||
/>
|
||||
<path
|
||||
d="M164.688 250.454h27.129c-.247-2.743-4.157-4.067-6.241-5.122-1.667-.843-3.756-5.86-4.267-8.081l-16.054 3.211-.567 9.992Z"
|
||||
fill="#020641"
|
||||
/>
|
||||
<path
|
||||
stroke="#fff"
|
||||
stroke-width=".584"
|
||||
d="m179.258 241.452 3.884-.505M179.464 243.036l4.398-.603"
|
||||
/>
|
||||
<path
|
||||
d="M118.55 210.104c-6.538-.934-7.005-11.481-6.422-16.638l63.051-14.303v1.751l-9.341 5.254-1.751 4.087 3.211 1.459 1.167 4.087c-2.53-.195-8.29.175-11.092 3.211-2.802 3.036-1.946 9.049-1.168 11.676-9.827.194-31.116.35-37.655-.584Z"
|
||||
fill="#5783FC"
|
||||
/>
|
||||
<path
|
||||
d="M178.098 211.272c-4.379-3.795-7.998-11.968-9.049-14.595-.584-1.46-1.557-3.99-1.751-4.963-.682-.194-2.161-.7-2.628-1.167"
|
||||
stroke="#000"
|
||||
stroke-width=".584"
|
||||
stroke-linecap="round"
|
||||
/>
|
||||
<path
|
||||
d="m175.179 183.011-13.271 9.341h-14.173c-1.09 0-1.586-1.361-.751-2.062l4.843-4.068 11.649-2.627 1.648-9.2a2.916 2.916 0 0 1 1.799-2.199l10.73-4.249a.584.584 0 0 1 .784.672l-3.258 14.392Z"
|
||||
fill="#66DED2"
|
||||
/>
|
||||
<path
|
||||
fill-rule="evenodd"
|
||||
clip-rule="evenodd"
|
||||
d="M133.145 156.739a8.174 8.174 0 1 0-6.196-13.502 4.086 4.086 0 1 0-1.642 7.651 8.175 8.175 0 0 0 7.838 5.851Z"
|
||||
fill="#FC466F"
|
||||
/>
|
||||
<path
|
||||
d="M146.281 140.685c0-6.305-5.449-5.741-8.173-4.67-3.211-3.503-7.298 1.167-8.174 2.335-1.751 0-2.627.292-3.794 1.459-.934.934-1.168 3.114-1.168 4.087.876.194 2.627.759 2.627 1.459-.233 1.401.292 1.946.584 2.044.876 0 1.053-1.408 1.46-2.627.875-2.628 5.254-.973 7.005 0 3.211 1.264 9.633 2.218 9.633-4.087Z"
|
||||
fill="#202020"
|
||||
/>
|
||||
<path
|
||||
d="M131.686 158.199c-12.61-1.635-18.682 6.519-20.141 10.8-1.168 3.016-3.62 10.45-4.087 16.055-.584 7.005 1.46 12.551 14.011 13.427 10.042.701 21.115-4.573 25.396-7.297l4.962-7.59c4.184-1.946 12.085-5.955 10.217-6.422-1.869-.467-8.757-.973-11.968-1.167-.487-.876-1.81-3.503-3.211-7.006-1.752-4.378-5.838-9.633-15.179-10.8Z"
|
||||
fill="#011650"
|
||||
/>
|
||||
<path
|
||||
d="M146.865 191.184c-1.635-4.204-1.071-10.217-.584-12.552 8.465-.876 10.8 0 11.676.292.233-.701 1.459-1.46 3.211-1.752 1.751-.292 3.503.584 3.503.584l-1.46 7.59c-.194.097-.7.233-1.167 0 .467 2.802-.973 3.113-1.752 2.919-.584.291-2.043.875-3.211.875-2.919.876-4.378-.292-4.962-.875-.584-.584-1.46-.876-2.043 0-.584.875-2.044 2.043-3.211 2.919Z"
|
||||
fill="#FC466F"
|
||||
/>
|
||||
<path
|
||||
d="m125.264 169 .292 11.092c4.379-.487 14.362-1.46 19.265-1.46M141.027 167.832c.876 1.751 2.627 5.429 2.627 6.13"
|
||||
stroke="#fff"
|
||||
stroke-width=".584"
|
||||
stroke-linecap="round"
|
||||
/>
|
||||
<path
|
||||
d="M153.287 182.427c1.167.973 3.502 3.62 3.502 6.422M157.081 180.967c1.07.973 3.153 3.737 2.919 7.006M158.833 179.799c.973.973 2.977 3.211 3.21 4.379M162.627 179.507c.389.487 1.168 1.635 1.168 2.336"
|
||||
stroke="#000"
|
||||
stroke-width=".584"
|
||||
stroke-linecap="round"
|
||||
/>
|
||||
<ellipse
|
||||
cx="170.801"
|
||||
cy="178.34"
|
||||
rx=".876"
|
||||
ry="1.751"
|
||||
transform="rotate(7.122 170.801 178.34)"
|
||||
fill="#fff"
|
||||
/>
|
||||
<path
|
||||
d="m180.931 155.117 4.196-2.427c.538-.311 1.154.264.881.823l-1.492 3.053 2.348 2.433a.63.63 0 0 1-.187 1.009l-5.155 2.401c-.629.293-1.201-.497-.725-1.003l2.289-2.435-2.331-2.915a.63.63 0 0 1 .176-.939ZM174.853 143.68l2.382-2.976c.191-.239.575-.086.549.218l-.223 2.684 2.233 1.029a.31.31 0 0 1 .094.495l-3.207 3.349c-.236.246-.637-.013-.51-.329l1.078-2.672-2.31-1.338a.308.308 0 0 1-.086-.46Z"
|
||||
fill="#5783FC"
|
||||
/>
|
||||
<defs>
|
||||
<linearGradient
|
||||
id="a"
|
||||
x1="201.893"
|
||||
y1="108.609"
|
||||
x2="201.893"
|
||||
y2="240.405"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
>
|
||||
<stop stop-color="#F7F4FD" />
|
||||
<stop offset="1" stop-color="#F7F4FD" stop-opacity="0" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
id="c"
|
||||
x1="186.768"
|
||||
y1="113.747"
|
||||
x2="182.306"
|
||||
y2="188.099"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
>
|
||||
<stop stop-color="#ABE7FF" />
|
||||
<stop offset="1" stop-color="#ADE4FF" stop-opacity="0" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
id="d"
|
||||
x1="194.645"
|
||||
y1="251.117"
|
||||
x2="202.045"
|
||||
y2="197.234"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
>
|
||||
<stop stop-color="#A4BCFE" />
|
||||
<stop offset="1" stop-color="#B0C5FD" stop-opacity="0" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
id="e"
|
||||
x1="47.309"
|
||||
y1="221.217"
|
||||
x2="99.469"
|
||||
y2="200.52"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
>
|
||||
<stop stop-color="#92AFFD" />
|
||||
<stop offset="1" stop-color="#92AFFD" stop-opacity="0" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
id="f"
|
||||
x1="247.196"
|
||||
y1="240.392"
|
||||
x2="272.201"
|
||||
y2="230.641"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
>
|
||||
<stop stop-color="#92AFFD" />
|
||||
<stop offset="1" stop-color="#92AFFD" stop-opacity="0" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
id="h"
|
||||
x1="28.671"
|
||||
y1="18.904"
|
||||
x2="3.896"
|
||||
y2="20.454"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
>
|
||||
<stop stop-color="#92AFFD" />
|
||||
<stop offset="1" stop-color="#92AFFD" stop-opacity="0" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
id="j"
|
||||
x1="28.671"
|
||||
y1="18.904"
|
||||
x2="3.896"
|
||||
y2="20.454"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
>
|
||||
<stop stop-color="#6DBADD" />
|
||||
<stop offset="1" stop-color="#8BD9FC" stop-opacity="0" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
id="k"
|
||||
x1="120.761"
|
||||
y1="94.843"
|
||||
x2="120.761"
|
||||
y2="129.815"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
>
|
||||
<stop stop-color="#7793DE" />
|
||||
<stop offset="1" stop-color="#92AFFD" stop-opacity="0" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
id="l"
|
||||
x1="301.61"
|
||||
y1="94.843"
|
||||
x2="301.61"
|
||||
y2="129.815"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
>
|
||||
<stop stop-color="#7793DE" />
|
||||
<stop offset="1" stop-color="#92AFFD" stop-opacity="0" />
|
||||
</linearGradient>
|
||||
</defs>
|
||||
</svg>
|
||||
<el-button type="primary" @click="backUrl()">返回首页</el-button>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { useRouter } from 'vue-router'
|
||||
const router = useRouter()
|
||||
const backUrl = () => {
|
||||
router.push({
|
||||
name: 'home',
|
||||
query: {
|
||||
// ...route.query,
|
||||
},
|
||||
})
|
||||
}
|
||||
</script>
|
||||
@@ -0,0 +1,470 @@
|
||||
<template>
|
||||
<div class="col-center">
|
||||
<svg
|
||||
width="400px"
|
||||
height="300px"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M371.128 249.745h-13.805c-1.464-7.112-2.573-21.335 4.706-21.335s9.099 14.223 9.099 21.335Z"
|
||||
fill="#E2E5EC"
|
||||
/>
|
||||
<path
|
||||
d="M362.209 233.116c.837 1.778 2.51 7.467 2.51 16.002"
|
||||
stroke="#fff"
|
||||
stroke-width="1.255"
|
||||
stroke-linecap="round"
|
||||
/>
|
||||
<path
|
||||
d="M352.797 249.745h-14.119c1.778-4.079 6.338-12.111 10.354-11.609 4.016.502 4.183 7.948 3.765 11.609Z"
|
||||
fill="#E2E5EC"
|
||||
/>
|
||||
<path
|
||||
d="M347.777 244.097c-.523 1.569-1.569 5.02-1.569 6.275"
|
||||
stroke="#fff"
|
||||
stroke-width="1.255"
|
||||
stroke-linecap="round"
|
||||
/>
|
||||
<path
|
||||
opacity=".6"
|
||||
d="M369.442 211.845c40.308-44.132-20.269-44.402-23.43-67.15-7.679-55.278-57.279-92.958-111.711-79.04-73.187 18.705-66.984 76.555-129.771 64.306-80.655-15.694-112.281 27.948-49.58 85.012 32.708 29.77-21.03 35.007-21.03 35.007L328.19 251c34.754-30.387-7.816-1.097 17.823-10.069 32.039-11.193-3.095-.046 23.43-29.086Z"
|
||||
fill="url(#a)"
|
||||
/>
|
||||
<path
|
||||
d="M23.044 249.745h348.891M376.192 249.745h5.962"
|
||||
stroke="#011650"
|
||||
stroke-width="1.255"
|
||||
stroke-linecap="round"
|
||||
/>
|
||||
<path
|
||||
d="M115.867 160.108c10.348 0 18.894 3.771 25.637 11.312 6.86 7.541 10.29 17.987 10.29 31.339 0 12.857-3.895 23.797-11.685 32.822-7.674 9.024-17.964 13.537-30.869 13.537-10.697 0-18.952-3.524-24.765-10.57-5.814-7.047-8.72-15.144-8.72-24.292 0-9.148 14.432-11.26 14.998 0 0 7.252 1.686 11.188 5.058 14.649 3.488 3.462 7.964 5.192 13.429 5.192 7.441 0 13.719-2.719 18.835-8.159 5.116-5.563 7.674-13.289 7.674-23.179 0-9.396-1.919-16.319-5.755-20.769-3.837-4.574-8.546-6.861-14.127-6.861-5.697 0-10.522.927-14.475 2.781-3.837 1.731-6.453 4.265-7.849 7.603H79.94l4.186-73.961 49.941-4.119c5.475-.451 9.86 4.463 8.79 9.852a8.27 8.27 0 0 1-7.194 6.608l-38.98 4.348-2.093 38.172c6.976-4.203 14.068-6.305 21.277-6.305ZM164.874 178.652c0-18.544 3.721-34.862 11.162-48.955 7.557-14.093 16.219-21.14 25.986-21.14 9.766 0 18.428 7.047 25.986 21.14 7.557 14.093 11.336 30.473 11.336 49.14 0 18.668-3.779 35.048-11.336 49.141-7.558 14.093-16.22 21.14-25.986 21.14-9.767 0-18.429-7.047-25.986-21.14-7.441-14.093-11.162-30.535-11.162-49.326Zm16.045.185c0 17.184 2.209 30.721 6.627 40.611 4.419 9.766 9.244 14.649 14.476 14.649 5.232 0 10.057-4.883 14.475-14.649 4.535-9.89 6.802-23.489 6.802-40.796 0-17.678-2.267-31.277-6.802-40.796-4.418-9.519-9.243-14.279-14.475-14.279-5.232 0-10.057 4.76-14.476 14.279-4.418 9.519-6.627 23.18-6.627 40.981ZM254.168 178.652c0-18.544 3.721-34.862 11.162-48.955 7.557-14.093 16.219-21.14 25.986-21.14 9.766 0 18.428 7.047 25.986 21.14 7.557 14.093 11.336 30.473 11.336 49.14 0 18.668-3.779 35.048-11.336 49.141-7.558 14.093-16.22 21.14-25.986 21.14-9.767 0-18.429-7.047-25.986-21.14-7.441-14.093-11.162-30.535-11.162-49.326Zm16.045.185c0 17.184 2.209 30.721 6.627 40.611 4.419 9.766 9.244 14.649 14.476 14.649 5.232 0 10.057-4.883 14.475-14.649 4.535-9.89 6.802-23.489 6.802-40.796 0-17.678-2.267-31.277-6.802-40.796-4.418-9.519-9.243-14.279-14.475-14.279-5.232 0-10.057 4.76-14.476 14.279-4.418 9.519-6.627 23.18-6.627 40.981Z"
|
||||
fill="#C9D7FF"
|
||||
/>
|
||||
<mask
|
||||
id="b"
|
||||
style="mask-type: alpha"
|
||||
maskUnits="userSpaceOnUse"
|
||||
x="75"
|
||||
y="107"
|
||||
width="254"
|
||||
height="143"
|
||||
>
|
||||
<path
|
||||
d="M115.867 160.108c10.348 0 18.894 3.771 25.637 11.312 6.86 7.541 10.29 17.987 10.29 31.339 0 12.857-3.895 23.797-11.685 32.822-7.674 9.024-17.964 13.537-30.869 13.537-10.697 0-18.952-3.524-24.765-10.57-5.814-7.047-8.72-15.144-8.72-24.292 0-9.148 14.432-11.26 14.998 0 .566 11.259 1.686 11.188 5.058 14.649 3.488 3.462 7.964 5.192 13.429 5.192 7.441 0 13.719-2.719 18.835-8.159 5.116-5.563 7.674-13.289 7.674-23.179 0-9.396-1.919-16.319-5.755-20.769-3.837-4.574-8.546-6.861-14.127-6.861-5.697 0-10.522.927-14.475 2.781-3.837 1.731-6.453 4.265-7.849 7.603H79.94l4.186-73.961 49.941-4.119c5.475-.451 9.86 4.463 8.79 9.852a8.27 8.27 0 0 1-7.194 6.608l-38.98 4.348-2.093 38.172c6.976-4.203 14.068-6.305 21.277-6.305ZM164.874 178.652c0-18.544 3.721-34.862 11.162-48.955 7.557-14.093 16.219-21.14 25.986-21.14 9.766 0 18.428 7.047 25.986 21.14 7.557 14.093 11.336 30.473 11.336 49.14 0 18.668-3.779 35.048-11.336 49.141-7.558 14.093-16.22 21.14-25.986 21.14-9.767 0-18.429-7.047-25.986-21.14-7.441-14.093-11.162-30.535-11.162-49.326Zm16.045.185c0 17.184 2.209 30.721 6.627 40.611 4.419 9.766 9.244 14.649 14.476 14.649 5.232 0 10.057-4.883 14.475-14.649 4.535-9.89 6.802-23.489 6.802-40.796 0-17.678-2.267-31.277-6.802-40.796-4.418-9.519-9.243-14.279-14.475-14.279-5.232 0-10.057 4.76-14.476 14.279-4.418 9.519-6.627 23.18-6.627 40.981ZM254.168 178.652c0-18.544 3.721-34.862 11.162-48.955 7.557-14.093 16.219-21.14 25.986-21.14 9.766 0 18.428 7.047 25.986 21.14 7.557 14.093 11.336 30.473 11.336 49.14 0 18.668-3.779 35.048-11.336 49.141-7.558 14.093-16.22 21.14-25.986 21.14-9.767 0-18.429-7.047-25.986-21.14-7.441-14.093-11.162-30.535-11.162-49.326Zm16.045.185c0 17.184 2.209 30.721 6.627 40.611 4.419 9.766 9.244 14.649 14.476 14.649 5.232 0 10.057-4.883 14.475-14.649 4.535-9.89 6.802-23.489 6.802-40.796 0-17.678-2.267-31.277-6.802-40.796-4.418-9.519-9.243-14.279-14.475-14.279-5.232 0-10.057 4.76-14.476 14.279-4.418 9.519-6.627 23.18-6.627 40.981Z"
|
||||
fill="#B0C4FE"
|
||||
/>
|
||||
</mask>
|
||||
<g mask="url(#b)">
|
||||
<ellipse
|
||||
opacity=".6"
|
||||
cx="84.54"
|
||||
cy="179.779"
|
||||
rx="43.925"
|
||||
ry="31.689"
|
||||
fill="url(#c)"
|
||||
/>
|
||||
<ellipse
|
||||
opacity=".8"
|
||||
cx="196.235"
|
||||
cy="162.836"
|
||||
rx="43.925"
|
||||
ry="56.789"
|
||||
fill="url(#d)"
|
||||
/>
|
||||
<ellipse
|
||||
cx="301.028"
|
||||
cy="197.976"
|
||||
rx="43.925"
|
||||
ry="56.789"
|
||||
fill="url(#e)"
|
||||
/>
|
||||
<ellipse
|
||||
cx="208.158"
|
||||
cy="197.976"
|
||||
rx="43.925"
|
||||
ry="56.789"
|
||||
fill="url(#f)"
|
||||
/>
|
||||
<path
|
||||
d="m97.717 109.498-.941 18.825 44.866-5.02 3.452-16.942-47.377 3.137Z"
|
||||
fill="#8BD9FC"
|
||||
/>
|
||||
<ellipse
|
||||
opacity=".6"
|
||||
cx="137.25"
|
||||
cy="118.284"
|
||||
rx="20.708"
|
||||
ry="31.689"
|
||||
fill="url(#g)"
|
||||
/>
|
||||
<path
|
||||
opacity=".2"
|
||||
d="m126.896 127.696 5.878-13.664c1.55-3.258 3.901-2.815 7.059-.139 2.987 2.531 8.587 8.219 11.03 10.561"
|
||||
stroke="#000"
|
||||
stroke-width=".941"
|
||||
/>
|
||||
<circle
|
||||
opacity=".3"
|
||||
cx="172.39"
|
||||
cy="139.932"
|
||||
r="4.393"
|
||||
fill="#758CD6"
|
||||
/>
|
||||
<circle
|
||||
opacity=".3"
|
||||
cx="178.351"
|
||||
cy="151.541"
|
||||
r=".941"
|
||||
fill="#758CD6"
|
||||
/>
|
||||
<circle
|
||||
opacity=".3"
|
||||
cx="178.665"
|
||||
cy="131.147"
|
||||
r=".628"
|
||||
fill="#758CD6"
|
||||
/>
|
||||
<circle
|
||||
opacity=".3"
|
||||
cx="168.938"
|
||||
cy="165.974"
|
||||
r="1.569"
|
||||
fill="#758CD6"
|
||||
/>
|
||||
<circle
|
||||
opacity=".3"
|
||||
cx="196.235"
|
||||
cy="121.108"
|
||||
r="1.255"
|
||||
fill="#758CD6"
|
||||
/>
|
||||
<circle
|
||||
opacity=".3"
|
||||
cx="183.058"
|
||||
cy="160.012"
|
||||
r="5.02"
|
||||
fill="#758CD6"
|
||||
/>
|
||||
<circle
|
||||
opacity=".3"
|
||||
cx="191.842"
|
||||
cy="131.775"
|
||||
r="6.275"
|
||||
fill="#758CD6"
|
||||
/>
|
||||
<path
|
||||
d="m93.324 186.681 1.569-20.707c4.706-6.066 15.813-17.947 22.59-16.943 8.471 1.255 21.963 11.923 30.748 16.001 8.785 4.079 4.706 14.433 7.216 20.08 2.51 5.648 0 29.493 0 43.926 0 14.432-5.961 15.06-16.001 20.08-10.04 5.02-29.179 5.02-40.16 3.765-10.982-1.255-14.433-7.217-20.708-11.923-6.275-4.706-10.354-17.57-12.236-25.414-1.883-7.844 5.96-12.236 8.785-13.177 2.823-.942 18.51 4.078 20.08 7.53 1.569 3.451 11.922 13.491 13.805 13.491 1.506 0 11.922 1.255 16.942 1.883 2.092-6.38 6.338-19.453 6.589-20.708.251-1.255-1.987-11.4-3.137-16.315l-13.492-5.961-22.59 4.392Z"
|
||||
fill="#CDDAFE"
|
||||
/>
|
||||
<rect
|
||||
x="116.755"
|
||||
y="136.972"
|
||||
width="38.651"
|
||||
height="30.409"
|
||||
rx="1.883"
|
||||
transform="rotate(-9.889 116.755 136.972)"
|
||||
fill="url(#h)"
|
||||
/>
|
||||
<ellipse
|
||||
cx="72.617"
|
||||
cy="235.94"
|
||||
rx="43.925"
|
||||
ry="31.375"
|
||||
fill="url(#i)"
|
||||
/>
|
||||
<path
|
||||
d="m97.09 184.798-2.51-18.511-1.255 21.963 3.765-3.452Z"
|
||||
fill="#91A4D7"
|
||||
style="mix-blend-mode: multiply"
|
||||
/>
|
||||
<path
|
||||
d="m102.11 128.951-4.393-19.139-.941 19.139h5.334Z"
|
||||
fill="#6DBADD"
|
||||
/>
|
||||
<path
|
||||
d="M86.422 107.302c1.568 4.288 2.196 17.758-1.57 36.081-4.705 22.904-2.823 38.592-2.823 44.867 0 5.02 6.066 3.765 8.471 4.392.105-3.346-8.345-13.177-2.823-29.492 6.902-20.394 1.882-44.867 4.392-57.103 2.51-12.236 6.435-6.054 8.158-5.02 1.568.941-3.66-20.708-4.08-8.471-2.404 9.831-1.254 69.652-4.078 83.771-1.651 8.256 2.092 22.486 6.275 25.728"
|
||||
stroke="#fff"
|
||||
stroke-width=".439"
|
||||
/>
|
||||
</g>
|
||||
<path
|
||||
d="M328.679 179.346c0-1.973-.042-3.921-.127-5.843m-4.746-27.924c1.821 5.757 3.131 11.823 3.931 18.198"
|
||||
stroke="#5783FC"
|
||||
stroke-width="1.255"
|
||||
stroke-linecap="round"
|
||||
/>
|
||||
<path
|
||||
d="m121.707 131.292 8.678-16.152c2.165-3.291 4.408-3.664 8.382-.74 3.756 2.766 10.878 9.072 13.973 11.657"
|
||||
stroke="#000"
|
||||
stroke-width="1.122"
|
||||
/>
|
||||
<rect
|
||||
x="118.638"
|
||||
y="131.323"
|
||||
width="38.651"
|
||||
height="30.409"
|
||||
rx="1.883"
|
||||
transform="rotate(-9.889 118.638 131.323)"
|
||||
fill="#E4E4ED"
|
||||
/>
|
||||
<mask
|
||||
id="j"
|
||||
style="mask-type: alpha"
|
||||
maskUnits="userSpaceOnUse"
|
||||
x="118"
|
||||
y="124"
|
||||
width="44"
|
||||
height="37"
|
||||
>
|
||||
<rect
|
||||
x="118.638"
|
||||
y="131.323"
|
||||
width="38.651"
|
||||
height="30.409"
|
||||
rx="1.883"
|
||||
transform="rotate(-9.889 118.638 131.323)"
|
||||
fill="#F0F3FA"
|
||||
/>
|
||||
</mask>
|
||||
<g mask="url(#j)">
|
||||
<circle
|
||||
cx="153.152"
|
||||
cy="151.414"
|
||||
r="11.226"
|
||||
transform="rotate(-9.889 153.152 151.414)"
|
||||
fill="#7DB5FF"
|
||||
/>
|
||||
<circle
|
||||
r="1.847"
|
||||
transform="scale(1 -1) rotate(9.889 863.109 762.065)"
|
||||
fill="#7DB5FF"
|
||||
/>
|
||||
<path
|
||||
stroke="#E01E67"
|
||||
stroke-width=".748"
|
||||
stroke-linecap="round"
|
||||
d="m125.215 138.74 13.542-2.36M126.191 144.339l13.542-2.36"
|
||||
/>
|
||||
</g>
|
||||
<path
|
||||
fill-rule="evenodd"
|
||||
clip-rule="evenodd"
|
||||
d="M268.084 141.814a6.589 6.589 0 1 0-6.559-5.956 2.51 2.51 0 0 0 2.349 4.436 6.564 6.564 0 0 0 4.21 1.52Z"
|
||||
fill="#FC466F"
|
||||
/>
|
||||
<path
|
||||
d="M270.594 129.578c2.761-1.757 2.614-3.242 2.196-3.765-.753-1.004-1.987-.418-2.51 0-.418.418-1.631 1.255-3.137 1.255-1.883 0-3.765 1.255-4.707 1.883-2.51-3.452-5.333-4.393-9.098-2.824-3.012 1.255-3.347 5.334-3.138 7.216-2.719-.418-8.158.063-8.158 5.334 0 6.589 5.02 6.275 10.354 5.647 5.334-.627 7.217-5.02 7.53-7.53.314-2.51-.941-4.392-.627-5.02.314-.627.941-.313 1.255.314-.251 1.757.104 3.661.314 4.393l2.51-.942c.418.314 1.38.565 1.882-.941.628-1.882 1.883-2.824 5.334-5.02Z"
|
||||
fill="#000"
|
||||
/>
|
||||
<path
|
||||
d="m273.418 149.062-5.962.628-3.451-.628-1.255-4.738c.523-.104 2.657.191 5.648.628 4.078.596 5.02 1.255 5.961 2.196l-.941 1.914Z"
|
||||
fill="#FC466F"
|
||||
/>
|
||||
<path
|
||||
d="M236.708 205.82c5.02-10.542 8.995-23.427 10.354-28.551 7.635.313 23.28.941 24.786.941 1.883 3.765 8.158 18.825 10.041 24.786 1.506 4.769 6.275 23.009 8.471 31.062-4.184 1.359-13.052 4.329-15.06 5.333-.942-7.216-13.178-39.532-14.433-42.356-1.255 3.137-7.844 19.452-11.295 23.845-2.761 3.514-7.425 12.341-9.412 16.315l-14.433-6.589c1.569-3.869 5.961-14.244 10.981-24.786Z"
|
||||
fill="#5783FC"
|
||||
/>
|
||||
<path
|
||||
d="M264.946 189.504c-.418.419-1.632 1.255-3.138 1.255 0 2.51-.313 4.707-.941 6.275"
|
||||
stroke="#000"
|
||||
stroke-width=".628"
|
||||
stroke-linecap="round"
|
||||
/>
|
||||
<path
|
||||
d="m277.496 248.804-2.197-9.727 15.06-5.02a4.778 4.778 0 0 0 4.393 3.765c3.012.251 4.602 2.824 5.02 4.079l-22.276 6.903ZM241.728 249.745l-20.08-10.668 4.392-8.471c4.079 1.778 12.613 5.585 14.119 6.589-1.255 3.451.942 5.334 1.883 7.53.753 1.757.104 4.079-.314 5.02Z"
|
||||
fill="#020641"
|
||||
/>
|
||||
<path
|
||||
stroke="#fff"
|
||||
stroke-width=".628"
|
||||
d="m287.082 237.542 3.765-1.883M287.85 239.077l4.252-2.163M236.531 237.425l3.858 1.685M235.844 238.998l4.384 1.88"
|
||||
/>
|
||||
<path
|
||||
d="m243.925 159.698-9.727-10.353c4.393-3.765 8.158-5.02 15.06-5.648 5.522-.502 11.714.209 14.119.628.105 1.359 1.13 4.204 4.393 4.706 3.263.502 5.333-1.464 5.961-2.51 1.778 1.464 5.898 5.396 8.157 9.412 2.259 4.016 5.334 4.184 6.589 3.765.209 4.079.69 12.55.941 13.805-12.863.942-15.687-5.02-17.883-8.471-2.761 3.765-.314 10.563 1.255 13.491-8.472-.209-25.791-.69-27.297-.941-.627-6.275 4.393-18.825 5.648-21.649-3.263-.251-6.171 2.406-7.216 3.765Z"
|
||||
fill="#011650"
|
||||
/>
|
||||
<path
|
||||
d="M296.321 145.266h4.392"
|
||||
stroke="#5783FC"
|
||||
stroke-width="1.255"
|
||||
stroke-linecap="round"
|
||||
/>
|
||||
<path
|
||||
d="M298.203 165.66c-3.012 4.016-7.321 6.275-9.099 6.902l-.627-12.864c.941-.418 3.137-1.757 4.392-3.765 1.569-2.51 1.883-5.647 4.393-8.785 2.51-3.137 7.216-4.392 9.413-4.392 2.196 0 3.765.941 3.765 1.882 0 .753-1.255.523-1.883.314.209.105.628.565.628 1.569 0 1.255-.314 1.882-.628 2.196.314.628.628.941.314 2.196-.314 1.255-1.569 2.51-2.824 2.51s-2.51 2.197-3.451 3.765c-.941 1.569-.628 3.452-4.393 8.472ZM231.688 164.091c-2.761-8.534.523-13.387 2.51-14.746 2.51 2.614 7.781 8.094 8.785 9.098-1.882 3.765 1.255 6.903 2.824 6.903 1.255 0 2.196-1.046 2.51-1.569.209-.418.941-1.38 2.196-1.882 1.569-.628 2.51 0 2.824.313.251.251.314.942.314 1.255.209-.104.753-.125 1.255.628.502.753.418 1.36.313 1.569.105.104.314.376.314.627.314.628-.314 1.569-.627 1.883-.314.313-.942.941-1.255 1.568-.314.628-3.138 2.824-5.02 4.707-1.506 1.506-3.138 1.673-3.765 1.568-3.243-.418-10.417-3.388-13.178-11.922Z"
|
||||
fill="#FC466F"
|
||||
/>
|
||||
<path
|
||||
d="M252.709 163.463c-1.15.105-3.514.816-3.765 2.824M254.278 165.659c-.837-.104-2.698.44-3.451 3.452"
|
||||
stroke="#000"
|
||||
stroke-width=".628"
|
||||
stroke-linecap="round"
|
||||
/>
|
||||
<path
|
||||
d="M275.3 150.6c0 3.451 0 7.843-3.765 12.55"
|
||||
stroke="#fff"
|
||||
stroke-width=".628"
|
||||
stroke-linecap="round"
|
||||
/>
|
||||
<path
|
||||
d="M304.165 143.697c.209.941-.063 2.886-2.824 3.137.942.942 1.255 3.138 0 4.393M307.302 145.893c-1.045.418-3.012 1.632-2.51 3.138.314.941 2.197.313 2.824 0-1.568.941-2.824 1.882-2.196 3.451"
|
||||
stroke="#000"
|
||||
stroke-width=".628"
|
||||
stroke-linecap="round"
|
||||
/>
|
||||
<path
|
||||
d="M305.733 145.266h7.53"
|
||||
stroke="#5783FC"
|
||||
stroke-width="1.255"
|
||||
stroke-linecap="round"
|
||||
/>
|
||||
<path
|
||||
stroke="#F6BCD1"
|
||||
stroke-width="1.255"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
d="M23.671 80.319h96.008M41.87 82.829l14.432 11.788-14.432 11.788 14.432 11.788-14.432 11.788 14.432 11.788-14.432 11.788 14.432 11.788-14.432 11.788 14.432 11.788-14.432 11.788 14.432 11.789-14.432 11.788 14.432 11.788-14.432 11.788"
|
||||
/>
|
||||
<path
|
||||
d="m56.616 50.826.372-.505a.628.628 0 0 0-1 .505h.628Zm.627 198.291V50.827h-1.255v198.29h1.255Zm-1-197.786 39.533 29.18.745-1.01-39.533-29.18-.745 1.01Z"
|
||||
fill="#F6BCD1"
|
||||
/>
|
||||
<path
|
||||
d="m41.242 80.633 15.373-13.805M41.242 249.117V80.947M122.146 83.853l10.668 26.983"
|
||||
stroke="#F6BCD1"
|
||||
stroke-width="1.255"
|
||||
/>
|
||||
<path
|
||||
d="M50.804 249.118H25.569c-5.933-10.272-14.823-31.372-2.922-33.603 14.875-2.789 18.727 15.938 18.727 18.329 7.65-2.444 9.474 9.164 9.43 15.274Z"
|
||||
fill="#E2E5EC"
|
||||
/>
|
||||
<path
|
||||
d="M26.694 223.351c1.816 2.922 6.012 11.124 10.156 24.512M44.226 237.828l-3.32 6.907"
|
||||
stroke="#fff"
|
||||
stroke-width="1.255"
|
||||
stroke-linecap="round"
|
||||
/>
|
||||
<circle
|
||||
cx="121.249"
|
||||
cy="81.888"
|
||||
r="2.824"
|
||||
fill="#fff"
|
||||
stroke="#F6BCD1"
|
||||
stroke-width="1.255"
|
||||
/>
|
||||
<circle
|
||||
cx="133.798"
|
||||
cy="112.008"
|
||||
r="2.196"
|
||||
fill="#fff"
|
||||
stroke="#F6BCD1"
|
||||
stroke-width="1.255"
|
||||
/>
|
||||
<circle opacity=".3" cx="374.76" cy="123.303" r="5.334" fill="#8BD9FC" />
|
||||
<defs>
|
||||
<linearGradient
|
||||
id="a"
|
||||
x1="202.51"
|
||||
y1="119.301"
|
||||
x2="202.51"
|
||||
y2="251"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
>
|
||||
<stop stop-color="#F7F4FD" />
|
||||
<stop offset="1" stop-color="#F7F4FD" stop-opacity="0" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
id="c"
|
||||
x1="84.54"
|
||||
y1="187.309"
|
||||
x2="102.424"
|
||||
y2="158.444"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
>
|
||||
<stop stop-color="#92AFFD" />
|
||||
<stop offset="1" stop-color="#92AFFD" stop-opacity="0" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
id="d"
|
||||
x1="184.312"
|
||||
y1="106.047"
|
||||
x2="180.707"
|
||||
y2="172.767"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
>
|
||||
<stop stop-color="#ABE7FF" />
|
||||
<stop offset="1" stop-color="#ADE4FF" stop-opacity="0" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
id="e"
|
||||
x1="298.204"
|
||||
y1="247.235"
|
||||
x2="305.574"
|
||||
y2="193.577"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
>
|
||||
<stop stop-color="#A4BCFE" />
|
||||
<stop offset="1" stop-color="#B0C5FD" stop-opacity="0" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
id="f"
|
||||
x1="205.334"
|
||||
y1="247.235"
|
||||
x2="212.704"
|
||||
y2="193.577"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
>
|
||||
<stop stop-color="#A4BCFE" />
|
||||
<stop offset="1" stop-color="#B0C5FD" stop-opacity="0" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
id="g"
|
||||
x1="142.583"
|
||||
y1="96.948"
|
||||
x2="119.437"
|
||||
y2="93.029"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
>
|
||||
<stop stop-color="#C9D7FF" />
|
||||
<stop offset="1" stop-color="#C9D7FF" stop-opacity="0" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
id="h"
|
||||
x1="108.697"
|
||||
y1="176.472"
|
||||
x2="131.959"
|
||||
y2="148.997"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
>
|
||||
<stop stop-color="#7793DE" />
|
||||
<stop offset="1" stop-color="#92AFFD" stop-opacity="0" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
id="i"
|
||||
x1="66.342"
|
||||
y1="212.095"
|
||||
x2="103.79"
|
||||
y2="225.01"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
>
|
||||
<stop stop-color="#A6E6D3" stop-opacity=".4" />
|
||||
<stop offset="1" stop-color="#A6E6D3" stop-opacity="0" />
|
||||
</linearGradient>
|
||||
</defs>
|
||||
</svg>
|
||||
<el-button type="primary" @click="backUrl">返回首页</el-button>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { useRouter } from 'vue-router'
|
||||
const router = useRouter()
|
||||
const backUrl = () => {
|
||||
router.push({
|
||||
name: 'home',
|
||||
query: {
|
||||
// ...route.query,
|
||||
},
|
||||
})
|
||||
}
|
||||
</script>
|
||||
@@ -0,0 +1,305 @@
|
||||
<template>
|
||||
<!-- <el-card class="mt10">-->
|
||||
|
||||
<!-- <el-row>-->
|
||||
<!-- <el-col :span="6">-->
|
||||
<!-- <el-statistic title="合计" :value="268500" />-->
|
||||
<!-- </el-col>-->
|
||||
<!-- <el-col :span="6">-->
|
||||
<!-- <el-statistic title="已通过" :value="268500" />-->
|
||||
<!-- </el-col>-->
|
||||
<!-- <el-col :span="6">-->
|
||||
<!-- <el-statistic title="待审核" :value="268500" />-->
|
||||
<!-- </el-col>-->
|
||||
<!-- <el-col :span="6">-->
|
||||
<!-- <el-statistic title="已驳回" :value="268500" />-->
|
||||
<!-- </el-col>-->
|
||||
|
||||
<!-- </el-row>-->
|
||||
<!-- </el-card>-->
|
||||
|
||||
<el-card class="mt10">
|
||||
|
||||
<el-form :inline="true" :model="formSearch" class="demo-form-inline">
|
||||
<el-form-item label="姓名">
|
||||
<el-input
|
||||
v-model="formSearch.name"
|
||||
style="max-width: 300px"
|
||||
placeholder="模糊查询姓名"
|
||||
class="input-with-select"
|
||||
/>
|
||||
</el-form-item>
|
||||
|
||||
<!-- <el-form-item label="手机号">-->
|
||||
<!-- <el-input-->
|
||||
<!-- v-model="searchWechatNickname"-->
|
||||
<!-- style="max-width: 300px"-->
|
||||
<!-- placeholder="模糊查询手机号"-->
|
||||
<!-- class="input-with-select"-->
|
||||
<!-- />-->
|
||||
<!-- </el-form-item>-->
|
||||
<el-form-item label="时间">
|
||||
<el-date-picker
|
||||
v-model="formSearch.date"
|
||||
type="date"
|
||||
placeholder="请选择时间"
|
||||
@clear="clearDate"
|
||||
/>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item>
|
||||
<el-button type="primary" @click="onSearchSubmit">搜索</el-button>
|
||||
</el-form-item>
|
||||
|
||||
|
||||
|
||||
</el-form>
|
||||
|
||||
|
||||
<el-table
|
||||
ref="table"
|
||||
:data="tableData"
|
||||
:default-sort="{ prop: 'date', order: 'descending' }"
|
||||
:header-cell-style="{ backgroundColor: '#ecf5ff' }"
|
||||
border
|
||||
stripe
|
||||
class="mt10"
|
||||
>
|
||||
<el-table-column
|
||||
label="序号"
|
||||
width="80"
|
||||
align="center"
|
||||
show-overflow-tooltip
|
||||
sortable
|
||||
>
|
||||
<template #default="scope">
|
||||
{{ scope.$index + 1 }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
|
||||
<el-table-column label="头像" width="200" align="center">
|
||||
<template #default="scope">
|
||||
<el-avatar :size="50" :src="scope.row.avatar" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
|
||||
<el-table-column
|
||||
label="名字"
|
||||
prop="name"
|
||||
align="center"
|
||||
show-overflow-tooltip
|
||||
/>
|
||||
<el-table-column
|
||||
label="手机号"
|
||||
prop="mobile"
|
||||
align="center"
|
||||
show-overflow-tooltip
|
||||
/>
|
||||
<el-table-column
|
||||
label="申请岗位"
|
||||
prop="roleId"
|
||||
:formatter="postFormater"
|
||||
align="center"
|
||||
show-overflow-tooltip
|
||||
/>
|
||||
<el-table-column
|
||||
label="申请时间"
|
||||
prop="createTime"
|
||||
width="auto"
|
||||
align="center"
|
||||
show-overflow-tooltip
|
||||
sortable
|
||||
/>
|
||||
<el-table-column
|
||||
label="状态"
|
||||
prop="status"
|
||||
:formatter="statusFormater"
|
||||
width="auto"
|
||||
align="center"
|
||||
show-overflow-tooltip
|
||||
sortable
|
||||
/>
|
||||
|
||||
<el-table-column
|
||||
label="操作"
|
||||
fixed="right"
|
||||
min-width="120"
|
||||
>
|
||||
<template v-slot:default="scope" >
|
||||
|
||||
<template v-if="scope.row.status === 0">
|
||||
<el-button type="success" @click="handleApprove(scope.row)">通过</el-button>
|
||||
<el-button type="danger" @click="handleReject(scope.row)">驳回</el-button>
|
||||
</template>
|
||||
<template v-else>
|
||||
<el-tag type="info">处理完成</el-tag>
|
||||
</template>
|
||||
</template>
|
||||
</el-table-column>
|
||||
|
||||
</el-table>
|
||||
<el-pagination
|
||||
size="small"
|
||||
background
|
||||
layout="prev, pager, next"
|
||||
:total="tableData.length"
|
||||
class="mt-4 mt10"
|
||||
@current-change="handleCurrentChange"
|
||||
/>
|
||||
</el-card>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, onMounted } from 'vue';
|
||||
import {getApplyList, reject, approve} from "../../api/modules/postApply";
|
||||
import { ElMessage, ElMessageBox } from 'element-plus'
|
||||
const tableData = ref([]);
|
||||
|
||||
const total = ref(0)
|
||||
const formSearch = ref({
|
||||
roleId: 3,
|
||||
name: '',
|
||||
page: 1,
|
||||
size: 10,
|
||||
current: 1,
|
||||
date: ''
|
||||
});
|
||||
|
||||
|
||||
const initData = () => {
|
||||
getApplyList(formSearch.value).then((res) => {
|
||||
tableData.value = res.data.records;
|
||||
});
|
||||
};
|
||||
|
||||
const formatDate = (date) => {
|
||||
if (typeof date === 'string') {
|
||||
// 检查是否已经是格式化的字符串
|
||||
const regex = /^\d{4}-\d{2}-\d{2}$/;
|
||||
if (regex.test(date)) {
|
||||
return date; // 已经是格式化的字符串,直接返回
|
||||
}
|
||||
}
|
||||
|
||||
const year = date.getFullYear();
|
||||
const month = String(date.getMonth() + 1).padStart(2, '0');
|
||||
const day = String(date.getDate()).padStart(2, '0');
|
||||
return `${year}-${month}-${day}`;
|
||||
}
|
||||
|
||||
const statusFormater = (row, column, cellValue) => {
|
||||
// 0待审核(审核中) 1驳回 2通过
|
||||
if (cellValue === 1) {
|
||||
return '已驳回';
|
||||
} else if (cellValue === 2) {
|
||||
return '已通过';
|
||||
} else {
|
||||
return '待审核';
|
||||
}
|
||||
}
|
||||
|
||||
const postFormater = (row, column, cellValue) => {
|
||||
if (cellValue === 2) {
|
||||
return '勘测工程师';
|
||||
} else if (cellValue === 3) {
|
||||
return '维修工程师';
|
||||
} else {
|
||||
return '工程师';
|
||||
}
|
||||
}
|
||||
|
||||
// 搜索事件
|
||||
const onSearchSubmit = ()=> {
|
||||
let params = {...formSearch.value}
|
||||
|
||||
if(params.date !== '')
|
||||
{
|
||||
params.date = formatDate(params.date);
|
||||
}
|
||||
|
||||
getApplyList(params).then((res) => {
|
||||
tableData.value = res.data.records
|
||||
total.value = res.data.total
|
||||
})
|
||||
|
||||
};
|
||||
|
||||
// 清空日期
|
||||
const clearDate = () => {
|
||||
formSearch.value.date = ''
|
||||
}
|
||||
|
||||
const handleApprove = (item) => {
|
||||
ElMessageBox.confirm('确定要通过该申请?', '提示', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning'
|
||||
}).then(() => {
|
||||
approve(item.id).then(() => {
|
||||
item.status = 2;
|
||||
ElMessage({
|
||||
type: 'success',
|
||||
message: '审核完成'
|
||||
})
|
||||
})
|
||||
}).catch(() => {
|
||||
});
|
||||
}
|
||||
|
||||
const handleReject = (item) => {
|
||||
ElMessageBox.confirm('确定要拒绝该申请?', '提示', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning'
|
||||
}).then(() => {
|
||||
reject(item.id).then(() => {
|
||||
item.status = 1;
|
||||
ElMessage({
|
||||
type: 'success',
|
||||
message: '驳回完成'
|
||||
})
|
||||
})
|
||||
}).catch(() => {
|
||||
});
|
||||
}
|
||||
|
||||
// 驳回原因
|
||||
const openModel = (index) => {
|
||||
ElMessageBox.prompt('请输入驳回原因', '', {
|
||||
confirmButtonText: '确认',
|
||||
cancelButtonText: '取消',
|
||||
inputType: 'textarea',
|
||||
})
|
||||
.then(({ value }) => {
|
||||
// 用户确认后,调用 rejectApplication 函数
|
||||
if (value) { // 确保用户输入了驳回原因
|
||||
reject(applicationId, value);
|
||||
} else {
|
||||
ElMessage({
|
||||
type: 'warning',
|
||||
message: '请输入驳回原因',
|
||||
});
|
||||
}
|
||||
})
|
||||
.catch(() => {
|
||||
// 用户取消操作,无需处理
|
||||
});
|
||||
};
|
||||
|
||||
const handleCurrentChange = (val) => {
|
||||
formSearch.value.size = 10;
|
||||
formSearch.value.page = val;
|
||||
initData(formSearch.value);
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
initData();
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
|
||||
.el-col {
|
||||
text-align: center;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,326 @@
|
||||
<template>
|
||||
|
||||
<!-- <el-card class="mt10">-->
|
||||
|
||||
<!-- <el-row>-->
|
||||
<!-- <el-col :span="6">-->
|
||||
<!-- <el-statistic title="合计" :value="268500" />-->
|
||||
<!-- </el-col>-->
|
||||
<!-- <el-col :span="6">-->
|
||||
<!-- <el-statistic title="已通过" :value="268500" />-->
|
||||
<!-- </el-col>-->
|
||||
<!-- <el-col :span="6">-->
|
||||
<!-- <el-statistic title="待审核" :value="268500" />-->
|
||||
<!-- </el-col>-->
|
||||
<!-- <el-col :span="6">-->
|
||||
<!-- <el-statistic title="已驳回" :value="268500" />-->
|
||||
<!-- </el-col>-->
|
||||
|
||||
<!-- </el-row>-->
|
||||
<!-- </el-card>-->
|
||||
|
||||
<el-card class="mt10">
|
||||
|
||||
<el-form :inline="true" :model="formSearch" class="demo-form-inline">
|
||||
<el-form-item label="姓名">
|
||||
<el-input
|
||||
v-model="formSearch.name"
|
||||
style="max-width: 300px"
|
||||
placeholder="模糊查询姓名"
|
||||
class="input-with-select"
|
||||
/>
|
||||
</el-form-item>
|
||||
|
||||
<!-- <el-form-item label="手机号">-->
|
||||
<!-- <el-input-->
|
||||
<!-- v-model="searchWechatNickname"-->
|
||||
<!-- style="max-width: 300px"-->
|
||||
<!-- placeholder="模糊查询手机号"-->
|
||||
<!-- class="input-with-select"-->
|
||||
<!-- />-->
|
||||
<!-- </el-form-item>-->
|
||||
<el-form-item label="时间">
|
||||
<el-date-picker
|
||||
v-model="formSearch.date"
|
||||
type="date"
|
||||
placeholder="请选择时间"
|
||||
@clear="clearDate"
|
||||
/>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item>
|
||||
<el-button type="primary" @click="onSearchSubmit">搜索</el-button>
|
||||
</el-form-item>
|
||||
|
||||
</el-form>
|
||||
|
||||
<el-table
|
||||
ref="table"
|
||||
:data="tableData"
|
||||
:default-sort="{ prop: 'date', order: 'descending' }"
|
||||
:header-cell-style="{ backgroundColor: '#ecf5ff' }"
|
||||
border
|
||||
stripe
|
||||
class="mt10"
|
||||
>
|
||||
<el-table-column
|
||||
label="序号"
|
||||
width="80"
|
||||
align="center"
|
||||
show-overflow-tooltip
|
||||
sortable
|
||||
>
|
||||
<template #default="scope">
|
||||
{{ scope.$index + 1 }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
|
||||
<el-table-column label="头像" width="200" align="center">
|
||||
<template #default="scope">
|
||||
<el-avatar :size="50" :src="scope.row.avatar" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
|
||||
<el-table-column
|
||||
label="名字"
|
||||
prop="name"
|
||||
align="center"
|
||||
show-overflow-tooltip
|
||||
/>
|
||||
<el-table-column
|
||||
label="手机号"
|
||||
prop="mobile"
|
||||
align="center"
|
||||
show-overflow-tooltip
|
||||
/>
|
||||
<el-table-column
|
||||
label="申请岗位"
|
||||
prop="roleId"
|
||||
:formatter="postFormater"
|
||||
align="center"
|
||||
show-overflow-tooltip
|
||||
/>
|
||||
<el-table-column
|
||||
label="申请时间"
|
||||
prop="createTime"
|
||||
width="auto"
|
||||
align="center"
|
||||
show-overflow-tooltip
|
||||
sortable
|
||||
/>
|
||||
<el-table-column
|
||||
label="状态"
|
||||
prop="status"
|
||||
:formatter="statusFormater"
|
||||
width="auto"
|
||||
align="center"
|
||||
show-overflow-tooltip
|
||||
sortable
|
||||
/>
|
||||
|
||||
<el-table-column
|
||||
label="操作"
|
||||
fixed="right"
|
||||
min-width="120"
|
||||
>
|
||||
<template v-slot:default="scope" >
|
||||
|
||||
<template v-if="scope.row.status === 0">
|
||||
<el-button type="success" @click="handleApprove(scope.row)">通过</el-button>
|
||||
<el-button type="danger" @click="handleReject(scope.row)">驳回</el-button>
|
||||
</template>
|
||||
<template v-else>
|
||||
<el-tag type="info">处理完成</el-tag>
|
||||
</template>
|
||||
</template>
|
||||
</el-table-column>
|
||||
|
||||
</el-table>
|
||||
<el-pagination
|
||||
size="small"
|
||||
background
|
||||
layout="prev, pager, next"
|
||||
:total="tableData.length"
|
||||
class="mt-4 mt10"
|
||||
@current-change="handleCurrentChange"
|
||||
/>
|
||||
</el-card>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, onMounted } from 'vue';
|
||||
import {getApplyList, reject, approve} from "../../api/modules/postApply";
|
||||
|
||||
// 示例数据
|
||||
// import {options} from './options.js'
|
||||
// const exampleData = ref(JSON.parse(JSON.stringify(options)))
|
||||
|
||||
import { ElMessage, ElMessageBox } from 'element-plus'
|
||||
|
||||
const tableData = ref([]);
|
||||
const searchWechatNickname = ref('');
|
||||
const searchName = ref('');
|
||||
const searchMobile = ref('');
|
||||
const queryData = ref({
|
||||
keyWord: '',
|
||||
page: 1,
|
||||
size: 10,
|
||||
});
|
||||
const total = ref(0)
|
||||
const formSearch = ref({
|
||||
roleId: 2,
|
||||
name: '',
|
||||
page: 1,
|
||||
size: 10,
|
||||
current: 1,
|
||||
date: ''
|
||||
});
|
||||
|
||||
const initData = () => {
|
||||
|
||||
getApplyList(formSearch.value).then((res) => {
|
||||
tableData.value = res.data.records;
|
||||
});
|
||||
};
|
||||
|
||||
const formatDate = (date) => {
|
||||
if (typeof date === 'string') {
|
||||
// 检查是否已经是格式化的字符串
|
||||
const regex = /^\d{4}-\d{2}-\d{2}$/;
|
||||
if (regex.test(date)) {
|
||||
return date; // 已经是格式化的字符串,直接返回
|
||||
}
|
||||
}
|
||||
|
||||
const year = date.getFullYear();
|
||||
const month = String(date.getMonth() + 1).padStart(2, '0');
|
||||
const day = String(date.getDate()).padStart(2, '0');
|
||||
return `${year}-${month}-${day}`;
|
||||
}
|
||||
|
||||
const statusFormater = (row, column, cellValue) => {
|
||||
// 0待审核(审核中) 1驳回 2通过
|
||||
if (cellValue === 1) {
|
||||
return '已驳回';
|
||||
} else if (cellValue === 2) {
|
||||
return '已通过';
|
||||
} else {
|
||||
return '待审核';
|
||||
}
|
||||
}
|
||||
|
||||
const postFormater = (row, column, cellValue) => {
|
||||
if (cellValue === 2) {
|
||||
return '勘测工程师';
|
||||
} else if (cellValue === 3) {
|
||||
return '维修工程师';
|
||||
} else {
|
||||
return '工程师';
|
||||
}
|
||||
}
|
||||
|
||||
// 清空日期
|
||||
const clearDate = () => {
|
||||
formSearch.value.date = ''
|
||||
}
|
||||
|
||||
// 搜索事件
|
||||
const onSearchSubmit = () => {
|
||||
let params = {...formSearch.value}
|
||||
|
||||
if(params.date !== '')
|
||||
{
|
||||
params.date = formatDate(params.date);
|
||||
}
|
||||
|
||||
getApplyList(params).then((res) => {
|
||||
tableData.value = res.data.records
|
||||
total.value = res.data.total
|
||||
})
|
||||
};
|
||||
|
||||
const handleApprove = (item) => {
|
||||
ElMessageBox.confirm('确定要通过该申请?', '提示', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning'
|
||||
}).then(() => {
|
||||
approve(item.id).then((res) => {
|
||||
item.status = 2;
|
||||
ElMessage({
|
||||
type: 'success',
|
||||
message: '申请已被通过!'
|
||||
})
|
||||
})
|
||||
}).catch(() => {
|
||||
});
|
||||
}
|
||||
|
||||
const handleReject = (item) => {
|
||||
ElMessageBox.confirm('确定要拒绝该申请?', '提示', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning'
|
||||
}).then(() => {
|
||||
reject(item.id).then((res) => {
|
||||
item.status = 1;
|
||||
ElMessage({
|
||||
type: 'success',
|
||||
message: '申请已被拒绝!'
|
||||
})
|
||||
})
|
||||
}).catch(() => {
|
||||
});
|
||||
}
|
||||
|
||||
// 驳回原因
|
||||
const openModel = (index) => {
|
||||
ElMessageBox.prompt('请输入驳回原因', '', {
|
||||
confirmButtonText: '确认',
|
||||
cancelButtonText: '取消',
|
||||
inputType: 'textarea',
|
||||
})
|
||||
.then(({ value }) => {
|
||||
// 用户确认后,调用 rejectApplication 函数
|
||||
if (value) { // 确保用户输入了驳回原因
|
||||
reject(applicationId, value);
|
||||
} else {
|
||||
ElMessage({
|
||||
type: 'warning',
|
||||
message: '请输入驳回原因',
|
||||
});
|
||||
}
|
||||
})
|
||||
.catch(() => {
|
||||
// 用户取消操作,无需处理
|
||||
});
|
||||
};
|
||||
|
||||
const handleCurrentChange = (val) => {
|
||||
queryData.value.size = 10;
|
||||
queryData.value.page = val;
|
||||
initData(queryData.value);
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
initData();
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.mt-4 {
|
||||
float: right;
|
||||
text-align: right;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.input-with-select {
|
||||
max-width: 300px;
|
||||
}
|
||||
|
||||
.expand-title {
|
||||
padding: 10px;
|
||||
color: #919399;
|
||||
font-weight: 600;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,157 @@
|
||||
.infinite-list {
|
||||
height: 72vh;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
list-style: none;
|
||||
}
|
||||
|
||||
|
||||
|
||||
.infinite-list {
|
||||
margin-top: 20px;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
|
||||
|
||||
.barlist {
|
||||
margin-bottom: 10px;
|
||||
background-color: #fff;
|
||||
padding: 10px;
|
||||
border-radius: 2px;
|
||||
|
||||
.item-img {
|
||||
margin-right: 10px;
|
||||
|
||||
img {
|
||||
width: 34px;
|
||||
height: 34px;
|
||||
border-radius: 50%;
|
||||
}
|
||||
}
|
||||
|
||||
.item-left {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
.name {
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.subname {
|
||||
font-size: 14px;
|
||||
color: rgba(83, 88, 117, 1);
|
||||
}
|
||||
}
|
||||
|
||||
.item-state {
|
||||
margin-left: 20px;
|
||||
|
||||
span {
|
||||
display: inline-block;
|
||||
color: #fff;
|
||||
font-size: 12px;
|
||||
letter-spacing: 1px;
|
||||
padding: 5px 10px;
|
||||
border-radius: 6px;
|
||||
// background-color: #1250FC;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
.card-lists {
|
||||
margin: 10px;
|
||||
position: relative;
|
||||
|
||||
.item-state-pos {
|
||||
position: absolute;
|
||||
right: 0;
|
||||
top: 10px;
|
||||
// background-color: #1250FC;
|
||||
color: #6C88D5;
|
||||
font-size: 14px;
|
||||
padding: 5px 10px;
|
||||
border-radius: 0 0 0 20px;
|
||||
}
|
||||
|
||||
.item-title {
|
||||
img {
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
border-radius: 50%;
|
||||
margin-right: 5px;
|
||||
vertical-align: text-top;
|
||||
}
|
||||
|
||||
font-size: 16px;
|
||||
font-weight: 600;
|
||||
color: rgba(15, 18, 34, 1);
|
||||
text-align: left;
|
||||
vertical-align: top;
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
|
||||
.item-content {
|
||||
margin-bottom: 5px;
|
||||
|
||||
.cot {
|
||||
flex: 1;
|
||||
|
||||
.cot-tit {
|
||||
font-size: 14px;
|
||||
font-weight: 400;
|
||||
color: rgba(83, 88, 117, 1);
|
||||
padding-bottom: 5px;
|
||||
|
||||
span {
|
||||
font-size: 14px;
|
||||
color: rgba(15, 18, 34, 1);
|
||||
|
||||
&.type {
|
||||
font-size: 10px;
|
||||
padding: 5px 10px;
|
||||
font-weight: 500;
|
||||
line-height: 0px;
|
||||
color: #fa4a1e;
|
||||
text-align: left;
|
||||
border-radius: 6px;
|
||||
background: #fa4a1e14;
|
||||
|
||||
&.error-type {
|
||||
color: #4060c7;
|
||||
background: #e8f3ff;
|
||||
}
|
||||
|
||||
&.suc-type {
|
||||
color: #48ac4c;
|
||||
background: #e2ffef;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
.cot-txt {
|
||||
font-size: 14px;
|
||||
font-weight: 400;
|
||||
color: rgba(15, 18, 34, 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.item-bottom {
|
||||
flex-flow: row-reverse;
|
||||
}
|
||||
}
|
||||
|
||||
.infinite-list .infinite-list-item+.list-item {
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
.mt-4 {
|
||||
float: right;
|
||||
text-align: right;
|
||||
}
|
||||
@@ -0,0 +1,546 @@
|
||||
<template>
|
||||
<el-card class="mt10">
|
||||
|
||||
<el-button type="success" @click="drawer = true;drawerEdit = false">
|
||||
<el-icon style="margin-right: 10px;"><CirclePlus /></el-icon>
|
||||
创建服务
|
||||
</el-button>
|
||||
|
||||
<el-drawer v-model="drawer" :direction="direction" :close-on-click-modal="false" :close-on-press-escape="false" :destroy-on-close="true" :show-close="false">
|
||||
<template v-if="!drawerEdit" #header>
|
||||
<h4>创建服务</h4>
|
||||
</template>
|
||||
<template v-else #header>
|
||||
<h4>编辑服务</h4>
|
||||
</template>
|
||||
|
||||
<template #default>
|
||||
<el-form
|
||||
:model="formData"
|
||||
label-width="auto" v-loading="loading">
|
||||
|
||||
<el-form-item label="服务名称" prop="formData.goodsName">
|
||||
<el-input
|
||||
v-model="formData.goodsName"
|
||||
maxlength="6"
|
||||
placeholder="请输入服务名称"
|
||||
></el-input>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="服务价格" prop="price">
|
||||
<el-input type="number" v-model="formData.price" autocomplete="off"></el-input>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="状态" prop="formData.isList">
|
||||
|
||||
<el-switch
|
||||
v-model="formData.isList"
|
||||
inline-prompt
|
||||
active-text="上架"
|
||||
inactive-text="下架"
|
||||
/>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="服务图标">
|
||||
<el-upload
|
||||
v-model:file-list="fileIcon"
|
||||
list-type="picture-card"
|
||||
:before-upload="beforeUpload"
|
||||
:http-request="addUploadIconFile"
|
||||
:limit="1"
|
||||
:on-exceed="handleIconExceed"
|
||||
:on-remove="handleIconRemove"
|
||||
>
|
||||
<el-icon><Plus /></el-icon>
|
||||
</el-upload>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="轮播图">
|
||||
<el-upload
|
||||
v-model:file-list="fileBannerList"
|
||||
multiple
|
||||
list-type="picture-card"
|
||||
:before-upload="beforeUpload"
|
||||
:http-request="addUploadCarouselImageFile"
|
||||
:limit="5"
|
||||
:on-exceed="handleBannerExceed"
|
||||
:on-remove="handleBannerRemove"
|
||||
>
|
||||
<el-icon><Plus /></el-icon>
|
||||
</el-upload>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="详情图">
|
||||
<el-upload
|
||||
v-model:file-list="fileDetail"
|
||||
list-type="picture-card"
|
||||
:before-upload="beforeUpload"
|
||||
:http-request="addUploadDetailFile"
|
||||
:limit="1"
|
||||
:on-exceed="handleDetailExceed"
|
||||
:on-remove="handleDetailRemove"
|
||||
>
|
||||
<el-icon><Plus /></el-icon>
|
||||
</el-upload>
|
||||
</el-form-item>
|
||||
|
||||
<div style="display: flex; justify-content: flex-end; margin-top: 50px">
|
||||
<el-button @click="cancelDrawer">取消</el-button>
|
||||
<el-button type="primary" @click="onSubmit">确认</el-button>
|
||||
</div>
|
||||
</el-form>
|
||||
</template>
|
||||
</el-drawer>
|
||||
|
||||
<el-table
|
||||
ref="table"
|
||||
:data="tableData"
|
||||
:default-sort="{ prop: 'date', order: 'descending' }"
|
||||
:header-cell-style="{ backgroundColor: '#ecf5ff' }"
|
||||
class="mt20"
|
||||
>
|
||||
<el-table-column
|
||||
v-for="item in options"
|
||||
:key="item.type"
|
||||
:prop="item.props"
|
||||
:label="item.label"
|
||||
:width="item.width"
|
||||
:align="item.align"
|
||||
>
|
||||
<template v-slot:default="scope" v-if="item.props === 'price'">
|
||||
{{ formatPrice(scope.row.price) }}
|
||||
</template>
|
||||
|
||||
<template v-slot:default="scope" v-if="item.props === 'isList'">
|
||||
<el-switch
|
||||
v-model="scope.row.isList"
|
||||
inline-prompt
|
||||
active-text="上架"
|
||||
inactive-text="下架"
|
||||
@change="upStatus(scope.row)"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<template v-slot:default="scope" v-if="item.props === 'icon'">
|
||||
<el-image
|
||||
style="width: 300px; height: 200px"
|
||||
:src="scope.row.icon"
|
||||
|
||||
/>
|
||||
</template>
|
||||
|
||||
<template v-slot:default="scope" v-if="item.props === 'carouselImage'">
|
||||
|
||||
<view v-for="itemImg in scope.row.carouselImage">
|
||||
|
||||
<el-image
|
||||
:src="itemImg.url"
|
||||
style="width:150px; height: 80px;margin-right: 5px;"
|
||||
:style="{
|
||||
boxShadow: `var(light)`,
|
||||
}"
|
||||
/>
|
||||
|
||||
</view>
|
||||
|
||||
</template>
|
||||
|
||||
<template v-slot:default="scope" v-if="item.props === 'details'">
|
||||
<el-image
|
||||
style="width: 300px; height: 200px"
|
||||
:src="scope.row.details"
|
||||
|
||||
/>
|
||||
</template>
|
||||
|
||||
<template v-slot:default="scope" v-if="item.props === 'actions'">
|
||||
<!-- <el-button-->
|
||||
<!-- type="info"-->
|
||||
<!-- size="small"-->
|
||||
<!-- @click="viewItem(scope.row)"-->
|
||||
<!-- >查看</el-button>-->
|
||||
<!-- <el-button-->
|
||||
<!-- type="info"-->
|
||||
<!-- size="small"-->
|
||||
<!-- @click="editItem(scope.row)"-->
|
||||
<!-- >编辑</el-button>-->
|
||||
<el-popconfirm
|
||||
confirm-button-text="确认"
|
||||
cancel-button-text="取消"
|
||||
:icon="InfoFilled"
|
||||
icon-color="#626AEF"
|
||||
title="确定要删除该服务项目?"
|
||||
@confirm="deleteItem(scope.row)"
|
||||
>
|
||||
<template #reference>
|
||||
<el-button type="danger" size="small">删除</el-button>
|
||||
</template>
|
||||
</el-popconfirm>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
|
||||
<el-pagination
|
||||
size="small"
|
||||
background
|
||||
layout="prev, pager, next"
|
||||
:total="tableData.length"
|
||||
class="mt-4 mt10"
|
||||
@current-change="handleCurrentChange"
|
||||
/>
|
||||
</el-card>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import {
|
||||
CirclePlus,
|
||||
Plus,
|
||||
InfoFilled,
|
||||
View,
|
||||
Edit,
|
||||
Delete,
|
||||
} from '@element-plus/icons-vue'
|
||||
import {getGoodsList, addGoods, updateGoods, delGoods} from '../../api/modules/goods.js'
|
||||
import { addFile } from '../../api/modules/upload'
|
||||
import { options } from './options.js'
|
||||
import { onMounted, reactive, ref } from 'vue'
|
||||
import { ElMessage } from 'element-plus'
|
||||
|
||||
const queryData = ref({
|
||||
keyWord: '',
|
||||
page: 1,
|
||||
size: 10,
|
||||
})
|
||||
const tableData = ref([])
|
||||
const total = ref(0)
|
||||
const drawer = ref(false)
|
||||
const drawerEdit = ref(false)
|
||||
const direction = ref('rtl')
|
||||
const loading = ref(false)
|
||||
|
||||
const formData = ref({
|
||||
goodsName: '',
|
||||
price: '',
|
||||
carouselImage: [],
|
||||
details: '',
|
||||
icon: '',
|
||||
isList: false
|
||||
})
|
||||
|
||||
const fileBannerList = ref([]); // 轮播图
|
||||
const fileDetail = ref([]); // 详情图
|
||||
const fileIcon = ref([]); // 服务图标
|
||||
|
||||
// 页面初始化
|
||||
const initData = () => {
|
||||
|
||||
getGoodsList(queryData.value).then((res) => {
|
||||
|
||||
tableData.value = res.data.map(item => {
|
||||
try {
|
||||
// 尝试将字符串转换为 JSON 对象
|
||||
item.carouselImage = JSON.parse(item.carouselImage);
|
||||
} catch (error) {
|
||||
// 设置一个默认值或者保留原来的字符串
|
||||
item.carouselImage = {}; // 或者保留原来的字符串
|
||||
}
|
||||
return item;
|
||||
});
|
||||
total.value = res.total
|
||||
})
|
||||
}
|
||||
|
||||
// 上传前的钩子
|
||||
const beforeUpload = (file) => {
|
||||
|
||||
loading.value = true;
|
||||
|
||||
const maxSize = 5 * 1024 * 1024; // 最大 2MB
|
||||
if (file.size > maxSize) {
|
||||
alert('文件大小不能超过 5MB');
|
||||
loading.value = false;
|
||||
return false;
|
||||
}
|
||||
|
||||
const allowedTypes = ['image/jpeg', 'image/png'];
|
||||
if (!allowedTypes.includes(file.type)) {
|
||||
alert('只允许上传 JPEG 或 PNG 图片');
|
||||
loading.value = false;
|
||||
return false;
|
||||
}
|
||||
|
||||
console.log('文件准备上传:', file);
|
||||
return true;
|
||||
}
|
||||
|
||||
// 轮播图片上传限制
|
||||
const handleBannerExceed= (files, uploadFiles) => {
|
||||
ElMessage.warning(
|
||||
`轮播图最多可上传5张`
|
||||
)
|
||||
}
|
||||
|
||||
// 详情图片上传限制
|
||||
const handleDetailExceed= (files, uploadFiles) => {
|
||||
ElMessage.warning(
|
||||
`详情图最多可上传1张`
|
||||
)
|
||||
}
|
||||
|
||||
// 服务图标上传限制
|
||||
const handleIconExceed = (files, uploadFiles) => {
|
||||
ElMessage.warning(
|
||||
`服务图标最多可上传1张`
|
||||
)
|
||||
}
|
||||
|
||||
// 轮播图移除
|
||||
const handleBannerRemove = (file) => {
|
||||
|
||||
let fileName = file.name; // 移除的文件名
|
||||
// 更新 formData 中的 details
|
||||
formData.value.carouselImage = formData.value.carouselImage.filter(item => item.name !== file.name);
|
||||
|
||||
}
|
||||
|
||||
// 详情图移除
|
||||
const handleDetailRemove = (file, fileList) => {
|
||||
formData.value.details = '';
|
||||
}
|
||||
|
||||
// 服务图表移除
|
||||
const handleIconRemove = (file, fileList) => {
|
||||
formData.value.icon = '';
|
||||
}
|
||||
|
||||
// 轮播上传请求
|
||||
const addUploadCarouselImageFile = (params) => {
|
||||
const formData = new FormData();
|
||||
formData.append('files', params.file);
|
||||
|
||||
addFile(formData)
|
||||
.then((response) => {
|
||||
handleCarouselImageSuccess(response, params.file, params.fileList);
|
||||
})
|
||||
.catch((error) => {
|
||||
if(error)
|
||||
{
|
||||
handleError(error, params.file, params.fileList);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// 详情上传
|
||||
const addUploadDetailFile = (params) => {
|
||||
const formData = new FormData();
|
||||
formData.append('files', params.file);
|
||||
|
||||
addFile(formData)
|
||||
.then((response) => {
|
||||
handleDetailSuccess(response, params.file, params.fileList);
|
||||
})
|
||||
.catch((error) => {
|
||||
if(error)
|
||||
{
|
||||
handleError(error, params.file, params.fileList);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// 服务图标上传
|
||||
const addUploadIconFile = (params) => {
|
||||
const formData = new FormData();
|
||||
formData.append('files', params.file);
|
||||
|
||||
addFile(formData)
|
||||
.then((response) => {
|
||||
handleIconSuccess(response, params.file, params.fileList);
|
||||
})
|
||||
.catch((error) => {
|
||||
if(error)
|
||||
{
|
||||
handleError(error, params.file, params.fileList);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// 处理轮播上传成功的回调
|
||||
const handleCarouselImageSuccess = (response, file) => {
|
||||
|
||||
let fileObj = {
|
||||
'name': file.name,
|
||||
'url': response.data[0]
|
||||
};
|
||||
|
||||
formData.value.carouselImage.push(fileObj);
|
||||
loading.value = false;
|
||||
}
|
||||
|
||||
// 处理详情上传成功的回调
|
||||
const handleDetailSuccess = (response) => {
|
||||
formData.value.details = response.data[0];
|
||||
loading.value = false;
|
||||
}
|
||||
|
||||
// 处理服务图标上传成功的回调
|
||||
const handleIconSuccess = (response) => {
|
||||
formData.value.icon = response.data[0];
|
||||
loading.value = false;
|
||||
}
|
||||
|
||||
// 处理上传失败的回调
|
||||
const handleError = (error, file, fileList) => {
|
||||
loading.value = false;
|
||||
console.error('上传失败:', error, file, fileList);
|
||||
}
|
||||
|
||||
// 上下架处理
|
||||
const upStatus = (env) => {
|
||||
|
||||
let formData = {
|
||||
id: env.id,
|
||||
isList: env.isList
|
||||
};
|
||||
|
||||
updateGoods(formData).then((res) => {
|
||||
|
||||
if(formData.isList)
|
||||
{
|
||||
ElMessage({
|
||||
message: '上架成功',
|
||||
type: 'success',
|
||||
});
|
||||
}else{
|
||||
let msg = '';
|
||||
ElMessage({
|
||||
message: '下架成功',
|
||||
type: 'success',
|
||||
});
|
||||
}
|
||||
initData();
|
||||
});
|
||||
}
|
||||
|
||||
// 格式化价格
|
||||
const formatPrice = (price) => {
|
||||
return parseFloat(price).toFixed(2);
|
||||
}
|
||||
|
||||
// 提交表单
|
||||
const onSubmit = () => {
|
||||
|
||||
if (!formData.value.goodsName || !formData.value.price || !formData.value.details || formData.value.carouselImage.length < 1 ) {
|
||||
ElMessage.error('请填写完整信息');
|
||||
return;
|
||||
}
|
||||
|
||||
let params = {...formData.value}
|
||||
params.carouselImage = JSON.stringify(params.carouselImage); // 转为字符串
|
||||
|
||||
addGoods(params)
|
||||
.then((res) => {
|
||||
|
||||
drawer.value = false;
|
||||
|
||||
formData.value = {
|
||||
goodsName: '',
|
||||
price: '',
|
||||
carouselImage: [],
|
||||
details: '',
|
||||
isList: false
|
||||
}
|
||||
|
||||
fileBannerList.value = [];
|
||||
fileDetail.value = [];
|
||||
|
||||
ElMessage({
|
||||
message: '添加成功',
|
||||
type: 'success',
|
||||
})
|
||||
|
||||
initData();
|
||||
})
|
||||
.catch((err) => {
|
||||
console.error('添加失败:', err);
|
||||
})
|
||||
|
||||
// if (drawer.value) {
|
||||
//
|
||||
// } else {
|
||||
// updateGoods(formData.value)
|
||||
// .then((res) => {
|
||||
// ElMessage({
|
||||
// message: '编辑服务项目成功',
|
||||
// type: 'success',
|
||||
// })
|
||||
// tableData.value = tableData.value.map(item => item.id === formData.value.id ? formData.value : item)
|
||||
// drawer.value = false
|
||||
// })
|
||||
// .catch((err) => {
|
||||
// console.error('更新失败:', err);
|
||||
// })
|
||||
// }
|
||||
}
|
||||
|
||||
// 点击取消
|
||||
const cancelDrawer = () => {
|
||||
drawer.value = false;
|
||||
drawerEdit.value = false;
|
||||
loading.value = false;
|
||||
formData.value = {
|
||||
goodsName: '',
|
||||
price: '',
|
||||
carouselImage: [],
|
||||
details: '',
|
||||
isList: false
|
||||
}
|
||||
fileBannerList.value = [];
|
||||
fileDetail.value = [];
|
||||
}
|
||||
|
||||
// 分页切换
|
||||
const handleCurrentChange = (val) => {
|
||||
queryData.value.page = val
|
||||
initData()
|
||||
}
|
||||
|
||||
// 编辑项
|
||||
const editItem = (item) => {
|
||||
formData.value = { ...item }
|
||||
|
||||
drawer.value = true;
|
||||
drawerEdit.value = true;
|
||||
}
|
||||
|
||||
// 查看项
|
||||
const viewItem = (item) => {
|
||||
console.log('查看项:', item)
|
||||
}
|
||||
|
||||
// 删除项
|
||||
const deleteItem = (env) => {
|
||||
|
||||
let ItemId = env.id; // 项目ID
|
||||
|
||||
// 删除
|
||||
delGoods(ItemId).then((res) =>{
|
||||
|
||||
ElMessage({
|
||||
message: '删除成功',
|
||||
type: 'success',
|
||||
});
|
||||
|
||||
initData(); // 重新加载数据
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
// 进入页面加载
|
||||
onMounted(() => {
|
||||
initData()
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
|
||||
</style>
|
||||
@@ -0,0 +1,45 @@
|
||||
export const options = [
|
||||
|
||||
{
|
||||
label: '服务名称',
|
||||
props: 'goodsName',
|
||||
width: '200',
|
||||
align: 'left'
|
||||
},
|
||||
{
|
||||
label: '服务价格',
|
||||
props: 'price',
|
||||
width: '150',
|
||||
align: 'left'
|
||||
}, {
|
||||
label: '状态',
|
||||
props: 'isList',
|
||||
width: '150',
|
||||
align: 'left'
|
||||
},
|
||||
{
|
||||
label: '服务图标',
|
||||
props: 'icon',
|
||||
width: 'auto',
|
||||
align: 'left'
|
||||
},
|
||||
{
|
||||
label: '轮播图',
|
||||
props: 'carouselImage',
|
||||
width: '500',
|
||||
align: 'left'
|
||||
},
|
||||
{
|
||||
label: '服务详情',
|
||||
props: 'details',
|
||||
width: 'auto',
|
||||
align: 'left',
|
||||
},
|
||||
{
|
||||
label: '操作',
|
||||
props: 'actions',
|
||||
width: '300',
|
||||
align: 'center',
|
||||
fixed: 'right',
|
||||
}
|
||||
]
|
||||
@@ -0,0 +1,390 @@
|
||||
.data-lists {
|
||||
width: 100%;
|
||||
|
||||
.el-col {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.data-item-one {
|
||||
background: #2969ff;
|
||||
margin-right: 10px;
|
||||
padding: 10px;
|
||||
height: 156px;
|
||||
border-radius: 6px;
|
||||
color: #fff;
|
||||
|
||||
.item-left {}
|
||||
|
||||
.item-right {
|
||||
padding-left: 20px;
|
||||
padding-right: 10px;
|
||||
flex: 1;
|
||||
|
||||
.item-right-top {
|
||||
margin-bottom: 10px;
|
||||
|
||||
.tit {
|
||||
padding-bottom: 5px;
|
||||
}
|
||||
|
||||
.num {
|
||||
font-size: 24px;
|
||||
font-weight: 700;
|
||||
text-align: left;
|
||||
}
|
||||
}
|
||||
|
||||
.line {
|
||||
width: 100%;
|
||||
height: 2px;
|
||||
background-color: #fff;
|
||||
opacity: 0.1;
|
||||
}
|
||||
|
||||
.item-right-bottom {
|
||||
margin-top: 10px;
|
||||
|
||||
.item {
|
||||
flex: 1;
|
||||
|
||||
.tit {
|
||||
font-size: 14px;
|
||||
padding-bottom: 5px;
|
||||
}
|
||||
|
||||
.num {
|
||||
font-size: 18px;
|
||||
font-weight: bold;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.data-item-two {
|
||||
height: 176px;
|
||||
margin-right: 10px;
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
|
||||
.item {
|
||||
height: 50%;
|
||||
width: 50%;
|
||||
|
||||
.item-des {
|
||||
margin-right: 8px;
|
||||
margin-bottom: 8px;
|
||||
background-color: #fff;
|
||||
padding: 20px;
|
||||
border-radius: 6px;
|
||||
|
||||
&:nth-child(2n) {
|
||||
margin-right: 0px;
|
||||
}
|
||||
|
||||
.right {
|
||||
flex: 1;
|
||||
padding-left: 22px;
|
||||
|
||||
.num {
|
||||
font-size: 18px;
|
||||
font-weight: 700;
|
||||
letter-spacing: 0px;
|
||||
|
||||
span {
|
||||
font-size: 12px;
|
||||
color: rgba(30, 189, 94, 1);
|
||||
}
|
||||
}
|
||||
|
||||
.txt {
|
||||
padding-top: 8px;
|
||||
font-size: 12px;
|
||||
font-weight: 400;
|
||||
|
||||
color: rgba(0, 0, 0, 0.3);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.data-item-three {
|
||||
padding: 10px;
|
||||
height: 156px;
|
||||
margin-bottom: 10px;
|
||||
background-color: #fff;
|
||||
border-radius: 6px;
|
||||
|
||||
.tit {
|
||||
font-size: 14px;
|
||||
padding-bottom: 5px;
|
||||
}
|
||||
|
||||
.content {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
|
||||
.item {
|
||||
width: 50%;
|
||||
height: 50%;
|
||||
|
||||
.item-data {
|
||||
margin: 5px;
|
||||
opacity: 1;
|
||||
padding: 10px;
|
||||
border-radius: 6px;
|
||||
background: #fafafcff;
|
||||
position: relative;
|
||||
|
||||
.icon {
|
||||
position: absolute;
|
||||
right: 16px;
|
||||
top: 40%;
|
||||
color: rgba(25, 30, 36, 0.5);
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.tit {
|
||||
font-size: 12px;
|
||||
font-weight: 400;
|
||||
color: rgba(25, 30, 36, 0.5);
|
||||
padding-bottom: 5px;
|
||||
}
|
||||
|
||||
.num {
|
||||
color: #0052d9;
|
||||
font-size: 14px;
|
||||
font-weight: 700;
|
||||
|
||||
span {
|
||||
color: #666;
|
||||
font-weight: 400;
|
||||
color: rgba(25, 30, 36, 0.5);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.dataLayer {
|
||||
.datalayer-echarts {
|
||||
flex: 1;
|
||||
height: 260px;
|
||||
margin-right: 10px;
|
||||
margin-bottom: 10px;
|
||||
background-color: #fff;
|
||||
border-radius: 6px;
|
||||
}
|
||||
|
||||
.table-data {
|
||||
border-radius: 6px;
|
||||
margin-right: 10px;
|
||||
background-color: #fff;
|
||||
padding-bottom: 10px;
|
||||
|
||||
.tit {
|
||||
padding: 10px 10px 0 10px;
|
||||
font-size: 14px;
|
||||
font-weight: bold;
|
||||
letter-spacing: 1px;
|
||||
}
|
||||
|
||||
.table-box {
|
||||
margin: 10px;
|
||||
|
||||
.el-table {
|
||||
width: 100%;
|
||||
height: 360px;
|
||||
|
||||
.type {
|
||||
font-size: 10px;
|
||||
padding: 5px 10px;
|
||||
font-weight: 500;
|
||||
line-height: 0px;
|
||||
color: #fa4a1e;
|
||||
text-align: left;
|
||||
border-radius: 6px;
|
||||
background: #fa4a1e14;
|
||||
|
||||
&.error-type {
|
||||
color: #4060c7;
|
||||
background: #e8f3ff;
|
||||
}
|
||||
}
|
||||
|
||||
.state {
|
||||
display: inline-block;
|
||||
width: 8px;
|
||||
height: 8px;
|
||||
background-color: #4060c7;
|
||||
border-radius: 50%;
|
||||
margin-right: 5px;
|
||||
|
||||
&.error-state {
|
||||
background-color: #fa4a1e;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.data-item-three {
|
||||
padding: 10px;
|
||||
// height: 156px;
|
||||
margin-bottom: 10px;
|
||||
background-color: #fff;
|
||||
border-radius: 6px;
|
||||
|
||||
.tit {
|
||||
font-size: 14px;
|
||||
padding-bottom: 5px;
|
||||
}
|
||||
|
||||
.content {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
|
||||
.item {
|
||||
width: 50%;
|
||||
height: 50%;
|
||||
|
||||
.item-data {
|
||||
margin: 5px;
|
||||
opacity: 1;
|
||||
padding: 10px;
|
||||
border-radius: 6px;
|
||||
background: #fafafcff;
|
||||
position: relative;
|
||||
align-items: center;
|
||||
|
||||
.icon {
|
||||
position: absolute;
|
||||
right: 16px;
|
||||
top: 40%;
|
||||
color: rgba(25, 30, 36, 0.5);
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.tit {
|
||||
font-size: 12px;
|
||||
font-weight: 400;
|
||||
color: rgba(25, 30, 36, 0.5);
|
||||
padding-bottom: 5px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
.el-icon {
|
||||
font-size: 20px;
|
||||
color: rgba(25, 30, 36, 0.5);
|
||||
margin-right: 5px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.notice {
|
||||
width: 100%;
|
||||
height: 152px;
|
||||
background-color: #fff;
|
||||
border-radius: 6px;
|
||||
overflow: hidden;
|
||||
margin-bottom: 10px;
|
||||
|
||||
.notice-news {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
position: relative;
|
||||
|
||||
img {
|
||||
width: 100%;
|
||||
object-fit: cover;
|
||||
}
|
||||
|
||||
.txt {
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
right: 0;
|
||||
left: 0;
|
||||
background-color: rgba(0, 0, 0, 0.3);
|
||||
font-size: 14px;
|
||||
color: #fff;
|
||||
padding: 10px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.notice-lists {
|
||||
height: 260px;
|
||||
overflow: scroll;
|
||||
|
||||
.item {
|
||||
font-size: 14px;
|
||||
padding: 10px;
|
||||
color: #666;
|
||||
|
||||
.type {
|
||||
font-size: 10px;
|
||||
padding: 5px 10px;
|
||||
font-weight: 500;
|
||||
line-height: 0px;
|
||||
color: #fa4a1e;
|
||||
text-align: left;
|
||||
border-radius: 6px;
|
||||
background: #fa4a1e14;
|
||||
|
||||
&.error-type {
|
||||
color: #4060c7;
|
||||
background: #e8f3ff;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.border-right-1px {
|
||||
border-right: 1px solid #eae3e3;
|
||||
}
|
||||
|
||||
.icon-edit {
|
||||
font-size: 20px;
|
||||
color: #2f60c2;
|
||||
margin: 0 10px;
|
||||
}
|
||||
|
||||
.icon-dele {
|
||||
font-size: 20px;
|
||||
color: #ff5722;
|
||||
}
|
||||
|
||||
.shake {
|
||||
animation: shake 0.5s linear;
|
||||
}
|
||||
|
||||
.shakeRight {
|
||||
animation: shakeRight 0.5s linear;
|
||||
}
|
||||
|
||||
@keyframes shake {
|
||||
from {
|
||||
transform: translateX(-100%);
|
||||
}
|
||||
|
||||
to {
|
||||
transform: translateX(0);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes shakeRight {
|
||||
from {
|
||||
transform: translateX(100%);
|
||||
}
|
||||
|
||||
to {
|
||||
transform: translateX(0);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
<template>
|
||||
<el-card>
|
||||
<div>
|
||||
欢迎回来,重庆创兴佳后台管理后台!
|
||||
</div>
|
||||
</el-card>
|
||||
</template>
|
||||
|
||||
<script setup></script>
|
||||
|
||||
<style lang="scss" scoped></style>
|
||||
@@ -0,0 +1,82 @@
|
||||
export const options = [
|
||||
{
|
||||
label: "名称",
|
||||
props: "name",
|
||||
width: "auto",
|
||||
align: "left",
|
||||
show: true,
|
||||
fixed: false,
|
||||
itemEdit: "itemEdit",
|
||||
sortable: false,
|
||||
},
|
||||
{
|
||||
label: "主题页id",
|
||||
props: "title_id",
|
||||
width: "auto",
|
||||
align: "left",
|
||||
show: true,
|
||||
fixed: false,
|
||||
itemEdit: "itemEdit",
|
||||
sortable: false,
|
||||
},
|
||||
{
|
||||
label: "类型",
|
||||
props: "type",
|
||||
width: "auto",
|
||||
align: "left",
|
||||
show: true,
|
||||
fixed: false,
|
||||
itemEdit: "itemEdit",
|
||||
sortable: false,
|
||||
},
|
||||
{
|
||||
label: "商品数",
|
||||
props: "number",
|
||||
width: "auto",
|
||||
align: "left",
|
||||
show: true,
|
||||
fixed: false,
|
||||
itemEdit: "itemEdit",
|
||||
sortable: false,
|
||||
},
|
||||
{
|
||||
label: "总订单数",
|
||||
props: "order_number",
|
||||
width: "auto",
|
||||
align: "left",
|
||||
show: true,
|
||||
fixed: false,
|
||||
itemEdit: "itemEdit",
|
||||
sortable: false,
|
||||
},
|
||||
{
|
||||
label: "今日GMV",
|
||||
props: "GWV_number",
|
||||
width: "auto",
|
||||
align: "left",
|
||||
show: true,
|
||||
fixed: false,
|
||||
itemEdit: "itemEdit",
|
||||
sortable: false,
|
||||
},
|
||||
{
|
||||
label: "状态",
|
||||
props: "state",
|
||||
width: "auto",
|
||||
align: "left",
|
||||
show: true,
|
||||
fixed: false,
|
||||
itemEdit: "itemEdit",
|
||||
sortable: false,
|
||||
},
|
||||
{
|
||||
label: "操作",
|
||||
props: "actions",
|
||||
width: "auto",
|
||||
align: "left",
|
||||
show: true,
|
||||
fixed: "right",
|
||||
itemEdit: "itemEdit",
|
||||
sortable: false,
|
||||
},
|
||||
];
|
||||
@@ -0,0 +1,99 @@
|
||||
.login-container {
|
||||
width: 100vw;
|
||||
height: 100vh;
|
||||
// background-color: #6c88d5;
|
||||
background-image: url('../../assets/bg.png');
|
||||
background-size: 100% 100%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
|
||||
.login-box {
|
||||
width: 96%;
|
||||
height: 94%;
|
||||
background-color: hsla(0, 0%, 100%, 0.85);
|
||||
border-radius: 10px;
|
||||
padding: 0 8% 0 50px;
|
||||
box-sizing: border-box;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-around;
|
||||
|
||||
.login-left {
|
||||
width: 800px;
|
||||
color: #6E736FFF;
|
||||
|
||||
.title {
|
||||
font-size: 48px;
|
||||
font-weight: 400;
|
||||
letter-spacing: 0px;
|
||||
color: rgba(47, 57, 78, 1);
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
img {
|
||||
width: 735px;
|
||||
height: 465px;
|
||||
}
|
||||
}
|
||||
|
||||
.login-form {
|
||||
width: 400px;
|
||||
height: 260px;
|
||||
padding: 10px 40px 35px 40px;
|
||||
border-radius: 10px;
|
||||
-webkit-box-shadow: 2px 3px 7px rgb(0 0 0 / 20%);
|
||||
box-shadow: 2px 3px 7px rgb(0 0 0 / 20%);
|
||||
|
||||
.login-logo {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
// margin-bottom: 45px;
|
||||
|
||||
.login-icon {
|
||||
width: 60px;
|
||||
// height: 52px;
|
||||
}
|
||||
|
||||
.logo-text {
|
||||
font-weight: bold;
|
||||
font-size: 32px;
|
||||
padding-left: 25px;
|
||||
white-space: nowrap;
|
||||
color: #2d51e6;
|
||||
}
|
||||
}
|
||||
|
||||
.el-form-item {
|
||||
margin-bottom: 20px;
|
||||
|
||||
}
|
||||
|
||||
.flx-row {
|
||||
span {
|
||||
font-size: 14px;
|
||||
color: #2d51e6;
|
||||
}
|
||||
|
||||
.img {
|
||||
flex: 1;
|
||||
text-align: center;
|
||||
}
|
||||
}
|
||||
|
||||
.login-btn {
|
||||
margin-top: 20px;
|
||||
padding-bottom: 20px;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
width: 100%;
|
||||
white-space: nowrap;
|
||||
|
||||
.el-button {
|
||||
width: 185px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,225 @@
|
||||
<template>
|
||||
<div class="login-container flx-center">
|
||||
<div class="login-box">
|
||||
<div class="login-left">
|
||||
<div class="title">创兴佳科技</div>
|
||||
<div class="sub">控制面板</div>
|
||||
<img src="../../assets/login.png" alt="" />
|
||||
</div>
|
||||
|
||||
<div class="login-form">
|
||||
<div class="login-logo">
|
||||
<img class="login-icon" src="../../assets/logo.png" alt="" />
|
||||
<p class="logo-text">欢迎登录</p>
|
||||
</div>
|
||||
<el-form ref="loginFormRef" :model="loginForm" :rules="loginRules" size="large">
|
||||
<el-form-item prop="username">
|
||||
<el-input
|
||||
:prefix-icon="User"
|
||||
v-model="loginForm.username"
|
||||
placeholder="用户名"
|
||||
>
|
||||
</el-input>
|
||||
</el-form-item>
|
||||
<el-form-item prop="password">
|
||||
<el-input
|
||||
:prefix-icon="Lock"
|
||||
type="password"
|
||||
show-password
|
||||
v-model="loginForm.password"
|
||||
placeholder="密码"
|
||||
>
|
||||
</el-input>
|
||||
</el-form-item>
|
||||
<!-- <div class="flx-row">
|
||||
<el-checkbox
|
||||
style="flex: 1"
|
||||
v-model="loginForm.checked"
|
||||
label="记住我"
|
||||
size="large"
|
||||
/>
|
||||
<span>忘记密码?</span>
|
||||
</div> -->
|
||||
</el-form>
|
||||
|
||||
<div class="login-btn">
|
||||
<el-button
|
||||
:icon="CircleClose"
|
||||
round
|
||||
@click="resetForm(loginFormRef)"
|
||||
size="large"
|
||||
>重置</el-button
|
||||
>
|
||||
<el-button
|
||||
:icon="UserFilled"
|
||||
round
|
||||
@click="login(loginFormRef)"
|
||||
size="large"
|
||||
type="primary"
|
||||
:loading="loading"
|
||||
>登录</el-button>
|
||||
</div>
|
||||
<!-- <el-divider>其他方式登录 </el-divider>
|
||||
<div class="flx-row">
|
||||
<span class="img"
|
||||
><svg
|
||||
focusable="false"
|
||||
class=""
|
||||
data-icon="github"
|
||||
width="25px"
|
||||
height="25px"
|
||||
fill="#888888"
|
||||
aria-hidden="true"
|
||||
viewBox="64 64 896 896"
|
||||
>
|
||||
<path
|
||||
d="M511.6 76.3C264.3 76.2 64 276.4 64 523.5 64 718.9 189.3 885 363.8 946c23.5 5.9 19.9-10.8 19.9-22.2v-77.5c-135.7 15.9-141.2-73.9-150.3-88.9C215 726 171.5 718 184.5 703c30.9-15.9 62.4 4 98.9 57.9 26.4 39.1 77.9 32.5 104 26 5.7-23.5 17.9-44.5 34.7-60.8-140.6-25.2-199.2-111-199.2-213 0-49.5 16.3-95 48.3-131.7-20.4-60.5 1.9-112.3 4.9-120 58.1-5.2 118.5 41.6 123.2 45.3 33-8.9 70.7-13.6 112.9-13.6 42.4 0 80.2 4.9 113.5 13.9 11.3-8.6 67.3-48.8 121.3-43.9 2.9 7.7 24.7 58.3 5.5 118 32.4 36.8 48.9 82.7 48.9 132.3 0 102.2-59 188.1-200 212.9a127.5 127.5 0 0138.1 91v112.5c.8 9 0 17.9 15 17.9 177.1-59.7 304.6-227 304.6-424.1 0-247.2-200.4-447.3-447.5-447.3z"
|
||||
></path></svg></span
|
||||
><span class="img"
|
||||
><svg
|
||||
focusable="false"
|
||||
class=""
|
||||
data-icon="wechat"
|
||||
width="25px"
|
||||
height="25px"
|
||||
fill="#888888"
|
||||
aria-hidden="true"
|
||||
viewBox="64 64 896 896"
|
||||
>
|
||||
<path
|
||||
d="M690.1 377.4c5.9 0 11.8.2 17.6.5-24.4-128.7-158.3-227.1-319.9-227.1C209 150.8 64 271.4 64 420.2c0 81.1 43.6 154.2 111.9 203.6a21.5 21.5 0 019.1 17.6c0 2.4-.5 4.6-1.1 6.9-5.5 20.3-14.2 52.8-14.6 54.3-.7 2.6-1.7 5.2-1.7 7.9 0 5.9 4.8 10.8 10.8 10.8 2.3 0 4.2-.9 6.2-2l70.9-40.9c5.3-3.1 11-5 17.2-5 3.2 0 6.4.5 9.5 1.4 33.1 9.5 68.8 14.8 105.7 14.8 6 0 11.9-.1 17.8-.4-7.1-21-10.9-43.1-10.9-66 0-135.8 132.2-245.8 295.3-245.8zm-194.3-86.5c23.8 0 43.2 19.3 43.2 43.1s-19.3 43.1-43.2 43.1c-23.8 0-43.2-19.3-43.2-43.1s19.4-43.1 43.2-43.1zm-215.9 86.2c-23.8 0-43.2-19.3-43.2-43.1s19.3-43.1 43.2-43.1 43.2 19.3 43.2 43.1-19.4 43.1-43.2 43.1zm586.8 415.6c56.9-41.2 93.2-102 93.2-169.7 0-124-120.8-224.5-269.9-224.5-149 0-269.9 100.5-269.9 224.5S540.9 847.5 690 847.5c30.8 0 60.6-4.4 88.1-12.3 2.6-.8 5.2-1.2 7.9-1.2 5.2 0 9.9 1.6 14.3 4.1l59.1 34c1.7 1 3.3 1.7 5.2 1.7a9 9 0 006.4-2.6 9 9 0 002.6-6.4c0-2.2-.9-4.4-1.4-6.6-.3-1.2-7.6-28.3-12.2-45.3-.5-1.9-.9-3.8-.9-5.7.1-5.9 3.1-11.2 7.6-14.5zM600.2 587.2c-19.9 0-36-16.1-36-35.9 0-19.8 16.1-35.9 36-35.9s36 16.1 36 35.9c0 19.8-16.2 35.9-36 35.9zm179.9 0c-19.9 0-36-16.1-36-35.9 0-19.8 16.1-35.9 36-35.9s36 16.1 36 35.9a36.08 36.08 0 01-36 35.9z"
|
||||
></path></svg></span
|
||||
><span class="img"
|
||||
><svg
|
||||
focusable="false"
|
||||
class=""
|
||||
data-icon="alipay-circle"
|
||||
width="25px"
|
||||
height="25px"
|
||||
fill="#888888"
|
||||
aria-hidden="true"
|
||||
viewBox="64 64 896 896"
|
||||
>
|
||||
<path
|
||||
d="M308.6 545.7c-19.8 2-57.1 10.7-77.4 28.6-61 53-24.5 150 99 150 71.8 0 143.5-45.7 199.8-119-80.2-38.9-148.1-66.8-221.4-59.6zm460.5 67c100.1 33.4 154.7 43 166.7 44.8A445.9 445.9 0 00960 512c0-247.4-200.6-448-448-448S64 264.6 64 512s200.6 448 448 448c155.9 0 293.2-79.7 373.5-200.5-75.6-29.8-213.6-85-286.8-120.1-69.9 85.7-160.1 137.8-253.7 137.8-158.4 0-212.1-138.1-137.2-229 16.3-19.8 44.2-38.7 87.3-49.4 67.5-16.5 175 10.3 275.7 43.4 18.1-33.3 33.4-69.9 44.7-108.9H305.1V402h160v-56.2H271.3v-31.3h193.8v-80.1s0-13.5 13.7-13.5H557v93.6h191.7v31.3H557.1V402h156.4c-15 61.1-37.7 117.4-66.2 166.8 47.5 17.1 90.1 33.3 121.8 43.9z"
|
||||
></path></svg></span
|
||||
><span class="img"
|
||||
><svg
|
||||
focusable="false"
|
||||
class=""
|
||||
data-icon="google-circle"
|
||||
width="25px"
|
||||
height="25px"
|
||||
fill="#888888"
|
||||
aria-hidden="true"
|
||||
viewBox="64 64 896 896"
|
||||
>
|
||||
<path
|
||||
d="M512 64C264.6 64 64 264.6 64 512s200.6 448 448 448 448-200.6 448-448S759.4 64 512 64zm167 633.6C638.4 735 583 757 516.9 757c-95.7 0-178.5-54.9-218.8-134.9C281.5 589 272 551.6 272 512s9.5-77 26.1-110.1c40.3-80.1 123.1-135 218.8-135 66 0 121.4 24.3 163.9 63.8L610.6 401c-25.4-24.3-57.7-36.6-93.6-36.6-63.8 0-117.8 43.1-137.1 101-4.9 14.7-7.7 30.4-7.7 46.6s2.8 31.9 7.7 46.6c19.3 57.9 73.3 101 137 101 33 0 61-8.7 82.9-23.4 26-17.4 43.2-43.3 48.9-74H516.9v-94.8h230.7c2.9 16.1 4.4 32.8 4.4 50.1 0 74.7-26.7 137.4-73 180.1z"
|
||||
></path></svg></span
|
||||
><span class="img"
|
||||
><svg
|
||||
focusable="false"
|
||||
class=""
|
||||
data-icon="twitter-circle"
|
||||
width="25px"
|
||||
height="25px"
|
||||
fill="#888888"
|
||||
aria-hidden="true"
|
||||
viewBox="64 64 896 896"
|
||||
>
|
||||
<path
|
||||
d="M512 64C264.6 64 64 264.6 64 512s200.6 448 448 448 448-200.6 448-448S759.4 64 512 64zm215.3 337.7c.3 4.7.3 9.6.3 14.4 0 146.8-111.8 315.9-316.1 315.9-63 0-121.4-18.3-170.6-49.8 9 1 17.6 1.4 26.8 1.4 52 0 99.8-17.6 137.9-47.4-48.8-1-89.8-33-103.8-77 17.1 2.5 32.5 2.5 50.1-2a111 111 0 01-88.9-109v-1.4c14.7 8.3 32 13.4 50.1 14.1a111.13 111.13 0 01-49.5-92.4c0-20.7 5.4-39.6 15.1-56a315.28 315.28 0 00229 116.1C492 353.1 548.4 292 616.2 292c32 0 60.8 13.4 81.1 35 25.1-4.7 49.1-14.1 70.5-26.7-8.3 25.7-25.7 47.4-48.8 61.1 22.4-2.4 44-8.6 64-17.3-15.1 22.2-34 41.9-55.7 57.6z"
|
||||
></path></svg
|
||||
></span>
|
||||
</div> -->
|
||||
</div>
|
||||
</div>
|
||||
<!-- 备案号放在 login-box 外面,居中显示 -->
|
||||
<div class="beian-info" style="position: absolute; bottom: 20px; width: 100%; text-align: center; color: #999; font-size: 16px;">
|
||||
渝ICP备2024040579号
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup name="login">
|
||||
import axios from "axios";
|
||||
import { useStore } from "vuex"; // 引入useStore 方法
|
||||
import { CircleClose, UserFilled, Lock, User, Iphone } from "@element-plus/icons-vue";
|
||||
import { Base64 } from "js-base64";
|
||||
import { reactive, ref } from "vue";
|
||||
import { useRouter } from "vue-router";
|
||||
import { ElMessage } from "element-plus";
|
||||
const loginFormRef = ref();
|
||||
const loading = ref(false);
|
||||
const router = useRouter();
|
||||
const loginForm = reactive({
|
||||
username: "",
|
||||
password: "",
|
||||
checked: false,
|
||||
});
|
||||
const loginRules = reactive({
|
||||
username: [{ required: true, message: "请输入用户名", trigger: "blur" }],
|
||||
password: [{ required: true, message: "请输入密码", trigger: "blur" }],
|
||||
});
|
||||
|
||||
const globalStore = useStore(); // 该方法用于返回store 实例
|
||||
|
||||
const login = (formEl) => {
|
||||
if (!formEl) return;
|
||||
formEl.validate((valid) => {
|
||||
if (valid) {
|
||||
// 不知道提交失败后怎么关闭,暂时先不开启
|
||||
// loading.value = true;
|
||||
console.log(globalStore);
|
||||
|
||||
if (loginForm.checked) {
|
||||
let password = Base64.encode(loginForm.password); // base64加密
|
||||
setCookie(loginForm.username, password, 7);
|
||||
} else {
|
||||
setCookie("", "", -1);
|
||||
}
|
||||
|
||||
globalStore.dispatch("user/login", loginForm);
|
||||
} else {
|
||||
console.log("error submit!");
|
||||
return false;
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
const setCookie = (userId, password, days) => {
|
||||
let date = new Date(); // 获取时间
|
||||
date.setTime(date.getTime() + 24 * 60 * 60 * 1000 * days); // 保存的天数
|
||||
// 字符串拼接cookie
|
||||
window.document.cookie =
|
||||
"userId" + "=" + userId + ";path=/;expires=" + date.toGMTString();
|
||||
window.document.cookie =
|
||||
"password" + "=" + password + ";path=/;expires=" + date.toGMTString();
|
||||
};
|
||||
|
||||
// 读取cookie 将用户名和密码回显到input框中
|
||||
const getCookie = () => {
|
||||
if (document.cookie.length > 0) {
|
||||
let arr = document.cookie.split("; "); //分割成一个个独立的“key=value”的形式
|
||||
for (let i = 0; i < arr.length; i++) {
|
||||
let arr2 = arr[i].split("="); // 再次切割,arr2[0]为key值,arr2[1]为对应的value
|
||||
if (arr2[0] === "userId") {
|
||||
loginForm.username = arr2[1];
|
||||
} else if (arr2[0] === "password") {
|
||||
loginForm.password = Base64.decode(arr2[1]); // base64解密
|
||||
loginForm.checked = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
const resetForm = (formEl) => {
|
||||
if (!formEl) return;
|
||||
formEl.resetFields();
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
@import "./index.scss";
|
||||
</style>
|
||||
@@ -0,0 +1,746 @@
|
||||
<template>
|
||||
|
||||
<el-card class="mt10">
|
||||
<el-form :inline="true" :model="formSearch" class="demo-form-inline">
|
||||
<el-form-item label="服务项目">
|
||||
<el-input
|
||||
v-model="formSearch.goodsName"
|
||||
style="max-width: 300px"
|
||||
placeholder="请输入服务项目"
|
||||
class="input-with-select"
|
||||
/>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="订单编号">
|
||||
<el-input
|
||||
v-model="formSearch.orderId"
|
||||
style="max-width: 300px"
|
||||
placeholder="订单编号"
|
||||
class="input-with-select"
|
||||
/>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="订单状态">
|
||||
<el-select v-model="formSearch.status" placeholder="订单状态" style="width: 200px">
|
||||
<el-option label="全部" value=" " />
|
||||
<el-option label="待服务" value="0" />
|
||||
<el-option label="服务中" value="1" />
|
||||
<el-option label="待付款" value="2" />
|
||||
<el-option label="已完成" value="3" />
|
||||
<el-option label="已取消" value="4" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
|
||||
<!-- <el-form-item label="维修费用">-->
|
||||
<!-- <el-input-->
|
||||
<!-- v-model="formSearch.amount"-->
|
||||
<!-- style="max-width: 300px"-->
|
||||
<!-- placeholder="维修费用"-->
|
||||
<!-- class="input-with-select"-->
|
||||
<!-- />-->
|
||||
<!-- </el-form-item>-->
|
||||
|
||||
<br>
|
||||
<el-form-item label="年">
|
||||
<el-date-picker
|
||||
v-model="formSearch.year"
|
||||
type="year"
|
||||
placeholder="请选择年份"
|
||||
:disabled-date="disablePastAndSpecificYear"
|
||||
style="margin-right: 20px"
|
||||
@clear="clearYearDate"
|
||||
@change="isYearClick"
|
||||
/>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="月">
|
||||
<el-date-picker
|
||||
v-model="formSearch.month"
|
||||
type="month"
|
||||
placeholder="请选择月份"
|
||||
:disabled="!formSearch.year"
|
||||
:disabled-date="disablePastAndSpecificMonth"
|
||||
@clear="clearMonthDate"
|
||||
@change="isMonthClick"
|
||||
:default-value="new Date(formatDate(formSearch.year).year, 1, 1)"
|
||||
:arrow-control="true"
|
||||
/>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="日">
|
||||
<el-date-picker
|
||||
v-model="formSearch.day"
|
||||
:disabled="!formSearch.month"
|
||||
:disabled-date="disablePastAndSpecificDay"
|
||||
type="date"
|
||||
placeholder="请选择日期"
|
||||
@clear="clearDay"
|
||||
@change="isDayClick"
|
||||
:default-value="new Date(formatDate(formSearch.year).year, formatDate(formSearch.month).month-1, 1)"
|
||||
:arrow-control="true"
|
||||
/>
|
||||
</el-form-item>
|
||||
|
||||
<br>
|
||||
<el-form-item label="用户姓名">
|
||||
<el-input
|
||||
v-model="formSearch.username"
|
||||
style="max-width: 300px"
|
||||
placeholder="用户姓名"
|
||||
class="input-with-select"
|
||||
/>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="勘测工程师姓名">
|
||||
<el-input
|
||||
v-model="formSearch.surveyorsName"
|
||||
style="max-width: 300px"
|
||||
placeholder="工程师姓名"
|
||||
class="input-with-select"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="维修工程师姓名">
|
||||
<el-input
|
||||
v-model="formSearch.repairerName"
|
||||
style="max-width: 300px"
|
||||
placeholder="工程师姓名"
|
||||
class="input-with-select"
|
||||
/>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item>
|
||||
<el-button type="primary" @click="onSearchSubmit">搜索</el-button>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item style="float: right">
|
||||
<el-button type="warning" @click="excelDown">导出</el-button>
|
||||
</el-form-item>
|
||||
|
||||
</el-form>
|
||||
|
||||
<el-table
|
||||
ref="table"
|
||||
:data="tableData"
|
||||
:default-sort="{ prop: 'date', order: 'descending' }"
|
||||
:header-cell-style="{ backgroundColor: '#ecf5ff' }"
|
||||
class="m10"
|
||||
>
|
||||
|
||||
<el-table-column
|
||||
label="编号"
|
||||
prop="id"
|
||||
width="120"
|
||||
align="center"
|
||||
show-overflow-tooltip
|
||||
sortable
|
||||
/>
|
||||
|
||||
<el-table-column
|
||||
label="状态"
|
||||
prop="status"
|
||||
width="120"
|
||||
align="center"
|
||||
sortable
|
||||
>
|
||||
<template #default="scope">
|
||||
<el-tag type="info" v-if="scope.row.status === 0">待服务</el-tag>
|
||||
<el-tag type="warning" v-if="scope.row.status === 1">服务中</el-tag>
|
||||
<el-tag type="danger" v-if="scope.row.status === 2">待付款 </el-tag>
|
||||
<el-tag type="success" v-if="scope.row.status === 3">已完成</el-tag>
|
||||
<el-tag type="info" style="color: #d54f50" v-if="scope.row.status === 4">已取消</el-tag>
|
||||
</template>
|
||||
</el-table-column>
|
||||
|
||||
<el-table-column
|
||||
label="服务项目"
|
||||
prop="goodsName"
|
||||
width="150"
|
||||
align="center"
|
||||
show-overflow-tooltip
|
||||
/>
|
||||
|
||||
<el-table-column
|
||||
label="预约时间"
|
||||
prop="appointmentTime"
|
||||
align="center"
|
||||
show-overflow-tooltip
|
||||
sortable
|
||||
/>
|
||||
|
||||
<el-table-column
|
||||
label="项目费用"
|
||||
width="120"
|
||||
align="center"
|
||||
show-overflow-tooltip
|
||||
sortable
|
||||
>
|
||||
<template #default="scope">
|
||||
{{ formatPrice(scope.row.goodsPrice) }}元
|
||||
</template>
|
||||
</el-table-column>
|
||||
|
||||
<el-table-column
|
||||
label="勘测工程师"
|
||||
width="120"
|
||||
align="center"
|
||||
show-overflow-tooltip
|
||||
sortable
|
||||
>
|
||||
<template #default="scope">
|
||||
{{ scope.row.surveyorsName }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
|
||||
<el-table-column
|
||||
label="维修工程师"
|
||||
width="120"
|
||||
align="center"
|
||||
show-overflow-tooltip
|
||||
sortable
|
||||
>
|
||||
<template #default="scope">
|
||||
<template v-if="scope.row.repairerName">
|
||||
{{ scope.row.repairerName }}
|
||||
</template>
|
||||
<template v-else>
|
||||
<el-tag type="info">未分配</el-tag>
|
||||
</template>
|
||||
</template>
|
||||
</el-table-column>
|
||||
|
||||
<el-table-column
|
||||
label="报价费用"
|
||||
width="120"
|
||||
align="center"
|
||||
show-overflow-tooltip
|
||||
sortable
|
||||
>
|
||||
<template #default="scope">
|
||||
<template v-if="scope.row.amount > 0">
|
||||
{{ formatPrice(scope.row.amount) }}元
|
||||
</template>
|
||||
<template v-else>
|
||||
<el-tag type="info">暂未报价</el-tag>
|
||||
</template>
|
||||
</template>
|
||||
</el-table-column>
|
||||
|
||||
<el-table-column
|
||||
label="用户姓名"
|
||||
prop="username"
|
||||
width="300"
|
||||
align="center"
|
||||
show-overflow-tooltip
|
||||
/>
|
||||
|
||||
|
||||
<el-table-column label="头像" width="200" align="center">
|
||||
<template #default="scope">
|
||||
<el-avatar :size="50" :src="scope.row.avatar" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
|
||||
<el-table-column
|
||||
label="下单时间"
|
||||
prop="createTime"
|
||||
width="auto"
|
||||
align="center"
|
||||
show-overflow-tooltip
|
||||
sortable
|
||||
/>
|
||||
|
||||
<el-table-column
|
||||
label="付款时间"
|
||||
prop="completionTime"
|
||||
width="auto"
|
||||
align="center"
|
||||
show-overflow-tooltip
|
||||
sortable
|
||||
/>
|
||||
|
||||
<el-table-column label="操作" width="280px" align="center">
|
||||
<template v-slot:default="scope">
|
||||
<el-button type="info" @click="detail(scope.row)">详情</el-button>
|
||||
<!-- <el-button type="danger">退款</el-button>-->
|
||||
</template>
|
||||
</el-table-column>
|
||||
|
||||
</el-table>
|
||||
<el-pagination
|
||||
size="small"
|
||||
background
|
||||
layout="prev, pager, next"
|
||||
:total="tableData.length"
|
||||
class="mt-4 mt10"
|
||||
@current-change="handleCurrentChange"
|
||||
/>
|
||||
|
||||
<el-drawer v-model="drawer">
|
||||
<template #header>
|
||||
<h4>订单详情</h4>
|
||||
</template>
|
||||
<template #default>
|
||||
|
||||
<el-descriptions
|
||||
class="margin-top"
|
||||
title="订单信息"
|
||||
:column="3"
|
||||
size="small"
|
||||
border
|
||||
style="margin-bottom: 20px"
|
||||
>
|
||||
|
||||
<el-descriptions-item>
|
||||
<template #label>
|
||||
<div class="cell-item">
|
||||
订单编号
|
||||
</div>
|
||||
</template>
|
||||
{{detailData.id}}
|
||||
</el-descriptions-item>
|
||||
|
||||
|
||||
<el-descriptions-item>
|
||||
<template #label>
|
||||
<div class="cell-item">
|
||||
订单状态
|
||||
</div>
|
||||
</template>
|
||||
<el-tag type="info" v-if="detailData.status === 0">待服务</el-tag>
|
||||
<el-tag type="warning" v-if="detailData.status === 1">服务中</el-tag>
|
||||
<el-tag type="danger" v-if="detailData.status === 2">待付款 </el-tag>
|
||||
<el-tag type="success" v-if="detailData.status === 3">已完成</el-tag>
|
||||
</el-descriptions-item>
|
||||
|
||||
<el-descriptions-item>
|
||||
<template #label>
|
||||
<div class="cell-item">
|
||||
预约费用
|
||||
</div>
|
||||
</template>
|
||||
{{ formatPrice(detailData.goodsPrice) }}元
|
||||
</el-descriptions-item>
|
||||
|
||||
<el-descriptions-item>
|
||||
<template #label>
|
||||
<div class="cell-item">
|
||||
报价费用
|
||||
</div>
|
||||
</template>
|
||||
<template v-if="detailData.amount > 0">
|
||||
{{ formatPrice(detailData.amount) }}元
|
||||
</template>
|
||||
<template v-else>
|
||||
<el-tag type="info">暂未报价</el-tag>
|
||||
</template>
|
||||
|
||||
</el-descriptions-item>
|
||||
|
||||
<el-descriptions-item>
|
||||
<template #label>
|
||||
<div class="cell-item">
|
||||
预约时间
|
||||
</div>
|
||||
</template>
|
||||
{{detailData.appointmentTime}}
|
||||
</el-descriptions-item>
|
||||
|
||||
<el-descriptions-item>
|
||||
<template #label>
|
||||
<div class="cell-item">
|
||||
付款时间
|
||||
</div>
|
||||
</template>
|
||||
{{detailData.completionTime}}
|
||||
</el-descriptions-item>
|
||||
</el-descriptions>
|
||||
|
||||
<el-descriptions
|
||||
class="margin-top"
|
||||
title="客户信息"
|
||||
:column="3"
|
||||
size="small"
|
||||
border
|
||||
style="margin-bottom: 20px"
|
||||
>
|
||||
<el-descriptions-item label="微信昵称">{{ detailData.username }}</el-descriptions-item>
|
||||
<el-descriptions-item label="联系电话">{{ detailData.mobile }}</el-descriptions-item>
|
||||
<el-descriptions-item label="地址">
|
||||
{{ detailData.address }}
|
||||
</el-descriptions-item>
|
||||
</el-descriptions>
|
||||
|
||||
<el-descriptions
|
||||
class="margin-top"
|
||||
title="勘测工程师信息"
|
||||
:column="3"
|
||||
size="small"
|
||||
border
|
||||
style="margin-bottom: 20px"
|
||||
>
|
||||
|
||||
<el-descriptions-item label="名字">{{ detailData.surveyorsName }}</el-descriptions-item>
|
||||
<el-descriptions-item label="联系方式">{{ detailData.surveyorsMobile }}</el-descriptions-item>
|
||||
<el-descriptions-item label="预约时间">{{ detailData.appointmentTime }}</el-descriptions-item>
|
||||
|
||||
</el-descriptions>
|
||||
|
||||
<el-descriptions
|
||||
class="margin-top"
|
||||
title="维修工程师信息"
|
||||
:column="3"
|
||||
size="small"
|
||||
border
|
||||
style="margin-bottom: 20px"
|
||||
>
|
||||
|
||||
<el-descriptions-item label="名字">{{ detailData.repairerName }}</el-descriptions-item>
|
||||
<el-descriptions-item label="联系方式">{{ detailData.repairerMobile }}</el-descriptions-item>
|
||||
<el-descriptions-item label="预约时间">{{ detailData.maintenanceTime }}</el-descriptions-item>
|
||||
</el-descriptions>
|
||||
|
||||
<div style="display: flex;justify-content: flex-end;margin-top: 50px">
|
||||
<el-button @click="drawer = false">取消</el-button>
|
||||
<!-- <el-button type="danger" @click="confirmClick">退款</el-button>-->
|
||||
</div>
|
||||
</template>
|
||||
|
||||
</el-drawer>
|
||||
</el-card>
|
||||
</template>
|
||||
<script setup>
|
||||
|
||||
import {nextTick, onMounted, reactive, ref} from 'vue'
|
||||
import {exportOrder, getOrderList} from "../../api/modules/order";
|
||||
import {ElMessage} from "element-plus";
|
||||
|
||||
const tableData = ref([])
|
||||
const table = ref()
|
||||
|
||||
const disabled = ref(false)
|
||||
const dialogVisible = ref(false)
|
||||
const total = ref(0)
|
||||
|
||||
const searchName = ref('');
|
||||
const drawer = ref(false)
|
||||
const formSearch = ref({
|
||||
goodsName: '',
|
||||
orderId: '',
|
||||
status: ' ',
|
||||
amount: '',
|
||||
username: '',
|
||||
surveyorsName: '',
|
||||
repairerName: '',
|
||||
month: '',
|
||||
year: '',
|
||||
day: '',
|
||||
page: 1,
|
||||
size: 10,
|
||||
current: 1,
|
||||
})
|
||||
|
||||
const detailData = ref();
|
||||
|
||||
const isStatus = ref('');
|
||||
|
||||
// 初始化数据
|
||||
const initData = () => {
|
||||
|
||||
getOrderList(formSearch.value).then((res) => {
|
||||
tableData.value = res.data.records
|
||||
total.value = res.data.total
|
||||
})
|
||||
}
|
||||
|
||||
// 格式化价格
|
||||
const formatPrice = (price) => {
|
||||
|
||||
price = price == null ? price = 0 : price;
|
||||
|
||||
return parseFloat(price).toFixed(2);
|
||||
}
|
||||
|
||||
|
||||
// 订单详情
|
||||
const detail = (row) => {
|
||||
drawer.value = true
|
||||
detailData.value = { ...row }
|
||||
|
||||
console.log('detailData', detailData.value);
|
||||
}
|
||||
|
||||
const handleCurrentChange = (val) => {
|
||||
formSearch.value.size = 10
|
||||
formSearch.value.page = val
|
||||
initData(formSearch.value)
|
||||
}
|
||||
|
||||
// 清空年份
|
||||
const clearYearDate = () => {
|
||||
formSearch.value.year = ''
|
||||
}
|
||||
|
||||
// 清空月份
|
||||
const clearMonthDate = () => {
|
||||
formSearch.value.month = ''
|
||||
}
|
||||
|
||||
// 清空日期
|
||||
const clearDay = () => {
|
||||
formSearch.value.day = ''
|
||||
}
|
||||
|
||||
// 禁止选择之后的年份
|
||||
const disablePastAndSpecificYear = (date) => {
|
||||
// 获取当前日期
|
||||
const currentDate = new Date();
|
||||
|
||||
currentDate.setDate(currentDate.getDate() - 1);
|
||||
|
||||
// 禁止选择今天的前一天及之前的日期(不包含今天)
|
||||
if (date > currentDate) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// 禁止选择特定的未来日期,例如节假日
|
||||
const disabledDates = [new Date(2023, 9, 1), new Date(2023, 10, 1)];
|
||||
return disabledDates.some((disabledDate) => {
|
||||
return (
|
||||
date.getFullYear() === disabledDate.getFullYear() &&
|
||||
date.getMonth() === disabledDate.getMonth() &&
|
||||
date.getDate() === disabledDate.getDate()
|
||||
);
|
||||
});
|
||||
};
|
||||
|
||||
// 选中年份
|
||||
const isYearClick = () => {
|
||||
formSearch.value.month = '';
|
||||
formSearch.value.day = '';
|
||||
}
|
||||
|
||||
// 选中月份
|
||||
const isMonthClick = () => {
|
||||
formSearch.value.day = '';
|
||||
}
|
||||
|
||||
// 选中天
|
||||
const isDayClick = () => {
|
||||
|
||||
}
|
||||
|
||||
// 选中月份禁止调整年份
|
||||
const disablePastAndSpecificMonth = (date) => {
|
||||
|
||||
if (!formSearch.value.year) {
|
||||
return true; // 未选择年份时,禁用所有月份
|
||||
}
|
||||
|
||||
const selectedYear = formatDate(formSearch.value.year).year;
|
||||
const year = date.getFullYear();
|
||||
|
||||
// 只允许选择选中年份的月份
|
||||
if (year !== selectedYear) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
};
|
||||
|
||||
// 选中年月禁止调整天
|
||||
const disablePastAndSpecificDay = (date) => {
|
||||
|
||||
if (!formSearch.value.month) {
|
||||
return true; // 未选择月份时,禁用所有日期
|
||||
}
|
||||
|
||||
const selectedYear = formatDate(formSearch.value.year).year;
|
||||
const selectedMonth = formatDate(formSearch.value.month).month;
|
||||
const year = date.getFullYear();
|
||||
const month = date.getMonth()+1;
|
||||
|
||||
if (year !== selectedYear || month != selectedMonth) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
|
||||
}
|
||||
|
||||
const excelDown = async () => {
|
||||
const url = downloadFile();
|
||||
|
||||
const currentDate = new Date().toISOString().split('T')[0]; // 获取当前日期,格式为 YYYY-MM-DD
|
||||
const randomNum = generateRandomNumber();
|
||||
|
||||
try {
|
||||
const response = await fetch(url, {
|
||||
method: 'GET',
|
||||
headers: {
|
||||
'Authorization': "Bearer " + localStorage.getItem("token"), // 替换为实际的 token
|
||||
'Content-Type': 'application/json'
|
||||
}
|
||||
});
|
||||
|
||||
const blob = await response.blob();
|
||||
const downloadUrl = window.URL.createObjectURL(blob);
|
||||
const a = document.createElement('a');
|
||||
a.href = downloadUrl;
|
||||
a.download = `${formatDate(currentDate)}-${randomNum}.xlsx`; // 设置下载文件的名称
|
||||
a.click();
|
||||
window.URL.revokeObjectURL(downloadUrl);
|
||||
} catch (error) {
|
||||
console.error('Error downloading file:', error);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
const generateRandomNumber = () => {
|
||||
return Math.floor(Math.random() * 10000); // 生成一个 0 到 9999 之间的随机数
|
||||
};
|
||||
|
||||
// 导出文件事件
|
||||
const downloadFile = () => {
|
||||
|
||||
let params = {...formSearch.value}
|
||||
|
||||
if(params.status !== ' ')
|
||||
{
|
||||
params.status = parseInt(params.status, 10);
|
||||
}else{
|
||||
delete params.status;
|
||||
}
|
||||
|
||||
if(params.year !== '')
|
||||
{
|
||||
let {
|
||||
year
|
||||
} = {...formatDate(params.year)};
|
||||
|
||||
params.year = year;
|
||||
}else{
|
||||
delete params.year;
|
||||
}
|
||||
|
||||
if(params.month !== '')
|
||||
{
|
||||
let {
|
||||
month
|
||||
} = {...formatDate(params.month)};
|
||||
|
||||
params.month = month;
|
||||
}else{
|
||||
delete params.month;
|
||||
}
|
||||
|
||||
if(params.day !== '')
|
||||
{
|
||||
let {
|
||||
year,
|
||||
month,
|
||||
day
|
||||
} = {...formatDate(params.day)};
|
||||
|
||||
params.year = year;
|
||||
params.month = month;
|
||||
params.day = day;
|
||||
}else{
|
||||
delete params.day;
|
||||
}
|
||||
|
||||
return exportOrder(params);
|
||||
|
||||
}
|
||||
// 搜索事件
|
||||
const onSearchSubmit = ()=> {
|
||||
|
||||
let params = {...formSearch.value}
|
||||
if(params.status !== ' ')
|
||||
{
|
||||
params.status = parseInt(params.status, 10);
|
||||
}else{
|
||||
delete params.status;
|
||||
}
|
||||
|
||||
if(params.year !== '')
|
||||
{
|
||||
let {
|
||||
year
|
||||
} = {...formatDate(params.year)};
|
||||
|
||||
params.year = year;
|
||||
}else{
|
||||
delete params.year;
|
||||
}
|
||||
|
||||
if(params.month !== '')
|
||||
{
|
||||
let {
|
||||
month
|
||||
} = {...formatDate(params.month)};
|
||||
|
||||
params.month = month;
|
||||
}else{
|
||||
delete params.month;
|
||||
}
|
||||
|
||||
if(params.day !== '')
|
||||
{
|
||||
let {
|
||||
year,
|
||||
month,
|
||||
day
|
||||
} = {...formatDate(params.day)};
|
||||
|
||||
params.year = year;
|
||||
params.month = month;
|
||||
params.day = day;
|
||||
}else{
|
||||
delete params.day;
|
||||
}
|
||||
|
||||
getOrderList(params).then((res) => {
|
||||
tableData.value = res.data.records
|
||||
total.value = res.data.total
|
||||
})
|
||||
|
||||
};
|
||||
|
||||
// 日期格式
|
||||
const formatDate = (date) => {
|
||||
|
||||
if(date === '')
|
||||
{
|
||||
return {
|
||||
year: 1999,
|
||||
month: 1,
|
||||
day: 1,
|
||||
};
|
||||
}
|
||||
|
||||
if (typeof date === 'string') {
|
||||
// 检查是否已经是格式化的字符串
|
||||
const regex = /^\d{4}-\d{2}-\d{2}$/;
|
||||
if (regex.test(date)) {
|
||||
return date; // 已经是格式化的字符串,直接返回
|
||||
}
|
||||
}
|
||||
|
||||
const year = date.getFullYear();
|
||||
const month = String(date.getMonth() + 1).padStart(2, '0');
|
||||
const day = String(date.getDate()).padStart(2, '0');
|
||||
|
||||
return {
|
||||
year: year,
|
||||
month: month,
|
||||
day: day,
|
||||
};
|
||||
};
|
||||
onMounted(() => {
|
||||
initData()
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
|
||||
</style>
|
||||
@@ -0,0 +1,86 @@
|
||||
export const options = [
|
||||
{
|
||||
userId: 1,
|
||||
avatar: 'https://pic.qqans.com/up/2024-6/2024620113729773.jpg',
|
||||
name: '高永建',
|
||||
nickname: '软件开发老高',
|
||||
mobile: '19136448763',
|
||||
date: '2024-10-11 10:11:01'
|
||||
},
|
||||
{
|
||||
userId: 2,
|
||||
avatar: 'https://pic.qqans.com/up/2024-6/2024620113729773.jpg',
|
||||
name: '张三',
|
||||
nickname: '软件开发老高',
|
||||
mobile: '19136448763',
|
||||
date: '2024-10-10 10:11:01'
|
||||
},
|
||||
{
|
||||
userId: 3,
|
||||
avatar: 'https://pic.qqans.com/up/2024-6/2024620113729773.jpg',
|
||||
name: '李四',
|
||||
nickname: '软件开发老高',
|
||||
mobile: '19136448763',
|
||||
date: '2024-10-13 10:11:01'
|
||||
},
|
||||
{
|
||||
userId: 4,
|
||||
avatar: 'https://pic.qqans.com/up/2024-6/2024620113729773.jpg',
|
||||
name: '名称',
|
||||
nickname: '软件开发老高',
|
||||
mobile: '19136448763',
|
||||
date: '2024-10-02 10:11:01'
|
||||
},{
|
||||
userId: 5,
|
||||
avatar: 'https://pic.qqans.com/up/2024-6/2024620113729773.jpg',
|
||||
name: '名称',
|
||||
nickname: '软件开发老高',
|
||||
mobile: '19136448763',
|
||||
date: '2024-10-01 10:11:01'
|
||||
},
|
||||
{
|
||||
userId: 6,
|
||||
avatar: 'https://pic.qqans.com/up/2024-6/2024620113729773.jpg',
|
||||
name: '名称',
|
||||
nickname: '软件开发老高',
|
||||
mobile: '19136448763',
|
||||
date: '2024-10-11 10:11:01'
|
||||
},{
|
||||
userId: 7,
|
||||
avatar: 'https://pic.qqans.com/up/2024-6/2024620113729773.jpg',
|
||||
name: '名称',
|
||||
nickname: '软件开发老高',
|
||||
mobile: '19136448763',
|
||||
date: '2024-10-11 10:11:01'
|
||||
},{
|
||||
userId: 8,
|
||||
avatar: 'https://pic.qqans.com/up/2024-6/2024620113729773.jpg',
|
||||
name: '名称',
|
||||
nickname: '软件开发老高',
|
||||
mobile: '19136448763',
|
||||
date: '2024-10-11 10:11:01'
|
||||
},{
|
||||
userId: 9,
|
||||
avatar: 'https://pic.qqans.com/up/2024-6/2024620113729773.jpg',
|
||||
name: '名称',
|
||||
nickname: '软件开发老高',
|
||||
mobile: '19136448763',
|
||||
date: '2024-10-11 10:11:01'
|
||||
},{
|
||||
userId: 10,
|
||||
avatar: 'https://pic.qqans.com/up/2024-6/2024620113729773.jpg',
|
||||
name: '名称',
|
||||
nickname: '软件开发老高',
|
||||
mobile: '19136448763',
|
||||
date: '2024-10-11 10:11:01'
|
||||
},
|
||||
{
|
||||
userId: 11,
|
||||
avatar: 'https://pic.qqans.com/up/2024-6/2024620113729773.jpg',
|
||||
name: '名称',
|
||||
nickname: '软件开发老高',
|
||||
mobile: '19136448763',
|
||||
date: '2024-10-11 10:11:01'
|
||||
},
|
||||
|
||||
];
|
||||
@@ -0,0 +1,11 @@
|
||||
<script setup>
|
||||
|
||||
</script>
|
||||
|
||||
<template>
|
||||
|
||||
</template>
|
||||
|
||||
<style scoped lang="scss">
|
||||
|
||||
</style>
|
||||
@@ -0,0 +1,562 @@
|
||||
<template>
|
||||
<el-card class="mt10">
|
||||
<el-drawer v-model="drawer" :direction="direction" :close-on-click-modal="false" :close-on-press-escape="false" :destroy-on-close="true" :show-close="false">
|
||||
<template #header>
|
||||
<h4 v-if="!editStatus">创建排班</h4>
|
||||
<h4 v-else>更新排班</h4>
|
||||
</template>
|
||||
<template #default>
|
||||
|
||||
<el-form :model="scheduleForm" label-width="auto" style="max-width: 600px">
|
||||
|
||||
<el-form-item label="日期时间">
|
||||
<el-date-picker
|
||||
v-if="!editStatus"
|
||||
v-model="scheduleForm.date"
|
||||
type="date"
|
||||
placeholder="排班日期"
|
||||
:disabled-date="disablePastAndSpecificDates"
|
||||
style="margin-right: 20px"
|
||||
@change="handleDateChange"
|
||||
/>
|
||||
<el-date-picker
|
||||
v-else
|
||||
v-model="scheduleForm.date"
|
||||
type="date"
|
||||
placeholder="排班日期"
|
||||
:disabled-date="disablePastAndSpecificDates"
|
||||
style="margin-right: 20px"
|
||||
@change="handleDateChange"
|
||||
disabled
|
||||
/>
|
||||
|
||||
<el-select
|
||||
v-model="scheduleForm.timeQuantumId"
|
||||
filterable
|
||||
placeholder="请选择排班时间"
|
||||
style="width: 240px"
|
||||
@change="handleDateChange"
|
||||
>
|
||||
<el-option
|
||||
v-for="item in timeDataList"
|
||||
:key="item.value"
|
||||
:label="item.label"
|
||||
:value="item.value"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="排班人员">
|
||||
|
||||
<template v-if="!editStatus">
|
||||
<template v-if="userList.length">
|
||||
<el-checkbox-group v-model="scheduleForm.userids" size="large">
|
||||
<el-checkbox-button v-for="item in userList" :key="item.id" :value="item.id">
|
||||
{{ item.nickName }}
|
||||
</el-checkbox-button>
|
||||
</el-checkbox-group>
|
||||
</template>
|
||||
|
||||
<template v-else>
|
||||
<el-alert title="选择排班日期时间后,将会获取排班人员" type="warning" />
|
||||
</template>
|
||||
</template>
|
||||
|
||||
<template v-else>
|
||||
<el-tag type="info">{{scheduleForm.username}}</el-tag>
|
||||
</template>
|
||||
|
||||
</el-form-item>
|
||||
|
||||
<div style="display: flex;justify-content: center;margin-top: 50px">
|
||||
<el-button @click="handleCancelSchedule">取消</el-button>
|
||||
<el-button type="success" @click="handleSaveSchedule">确认</el-button>
|
||||
</div>
|
||||
|
||||
</el-form>
|
||||
</template>
|
||||
|
||||
</el-drawer>
|
||||
|
||||
<el-container class="mt10" style="height: 80vh">
|
||||
<el-aside width="220px">
|
||||
<el-scrollbar>
|
||||
<el-menu >
|
||||
<el-sub-menu v-for="(dateItem, index) in dateList" :index="index.toString()" :key="index">
|
||||
<template #title>
|
||||
<el-icon><Calendar /></el-icon>
|
||||
<template v-if="defaultOpeneds === index">
|
||||
<b>今天:</b> {{ dateItem }}
|
||||
</template>
|
||||
<template v-else>
|
||||
{{ dateItem }}
|
||||
</template>
|
||||
</template>
|
||||
<el-menu-item-group>
|
||||
<el-menu-item v-for="(timeItem, timeIndex) in timeList" :key="timeItem.id" :index="index.toString() + '-' + timeIndex.toString()" @click="selectTimeClick(dateItem, timeItem)">
|
||||
{{timeItem.startTime}} - {{timeItem.endTime}}
|
||||
</el-menu-item>
|
||||
</el-menu-item-group>
|
||||
</el-sub-menu>
|
||||
</el-menu>
|
||||
</el-scrollbar>
|
||||
</el-aside>
|
||||
<el-container class="ml20">
|
||||
<el-main>
|
||||
|
||||
<el-button type="success" @click="drawer = true" class="mR10">
|
||||
<el-icon style="margin-right: 10px;"><CirclePlus /></el-icon>
|
||||
创建排班
|
||||
</el-button>
|
||||
|
||||
<el-tag type="success" class="mR10">当前日期:{{ isDate }}</el-tag>
|
||||
<el-tag type="info" class="mR10" v-if="isRangeTime">时间段:{{isRangeTime}}</el-tag>
|
||||
|
||||
<el-scrollbar class="mt20">
|
||||
<el-table :data="userDataList">
|
||||
|
||||
<el-table-column prop="date" label="日期" width="200" />
|
||||
<el-table-column prop="time" label="时间段" width="200" />
|
||||
|
||||
<el-table-column prop="id" label="ID" width="200" />
|
||||
<el-table-column prop="username" label="名称" width="120" />
|
||||
<el-table-column label="头像" width="200" align="center">
|
||||
<template #default="scope">
|
||||
<el-avatar :size="50" :src="scope.row.avatar" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="mobile" label="手机号" />
|
||||
|
||||
<el-table-column label="操作" width="280px" align="center">
|
||||
<template v-slot:default="scope">
|
||||
<el-button type="info" @click="editClick(scope.row)">编辑</el-button>
|
||||
<el-popconfirm title="确认删除这个排班吗?" @confirm="delQuantumClick(scope.row)">
|
||||
<template #reference>
|
||||
<el-button type="danger">删除</el-button>
|
||||
</template>
|
||||
</el-popconfirm>
|
||||
</template>
|
||||
</el-table-column>
|
||||
|
||||
</el-table>
|
||||
</el-scrollbar>
|
||||
</el-main>
|
||||
</el-container>
|
||||
</el-container>
|
||||
</el-card>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { onMounted, ref, nextTick } from 'vue';
|
||||
import {Menu as IconMenu, Message, Setting, Calendar, CirclePlus} from '@element-plus/icons-vue';
|
||||
import {delQuantum, listQuantum} from "../../api/modules/schedulingTime";
|
||||
import {ElMessage} from "element-plus";
|
||||
import {addSchedule, delSchedule, getScheduleList, updateSchedule} from "../../api/modules/scheduling";
|
||||
import {listMaintenanceUser, listSurveyorsUser} from "../../api/modules/schedulingUser";
|
||||
|
||||
|
||||
const scheduleForm = ref({
|
||||
userids: [],
|
||||
roleId: 3,
|
||||
date: '',
|
||||
timeQuantumId: '',
|
||||
});
|
||||
|
||||
const direction = ref('rtl')
|
||||
const drawer = ref(false)
|
||||
const editStatus = ref(false)
|
||||
const userList = ref([]); // 勘测人员列表
|
||||
|
||||
// 日期列表
|
||||
const dateList = ref([]);
|
||||
const timeList = ref([]); // 时间段
|
||||
const timeDataList = ref([]); // 时间段
|
||||
const defaultOpeneds = ref([]);
|
||||
|
||||
const userDataList = ref([]); // 已排期用户
|
||||
const isDate = ref(''); // 选中的日期
|
||||
const istimeQuantumId = ref(''); // 时间段ID
|
||||
const isRangeTime = ref(''); // 时间段
|
||||
|
||||
// 初始化
|
||||
const initData = () => {
|
||||
|
||||
const currentDate = new Date();
|
||||
const currentYear = currentDate.getFullYear();
|
||||
const currentMonth = currentDate.getMonth();
|
||||
|
||||
// 上个月的第一天和最后一天
|
||||
const lastMonthFirstDay = new Date(currentYear, currentMonth - 1, 1);
|
||||
const lastMonthLastDay = new Date(currentYear, currentMonth, 0);
|
||||
|
||||
// 这个月的第一天和最后一天
|
||||
const thisMonthFirstDay = new Date(currentYear, currentMonth, 1);
|
||||
const thisMonthLastDay = new Date(currentYear, currentMonth + 1, 0);
|
||||
|
||||
// 下个月的第一天和最后一天
|
||||
const nextMonthFirstDay = new Date(currentYear, currentMonth + 1, 1);
|
||||
const nextMonthLastDay = new Date(currentYear, currentMonth + 2, 0);
|
||||
|
||||
// 添加上个月的日期
|
||||
// for (let d = lastMonthFirstDay; d <= lastMonthLastDay; d.setDate(d.getDate() + 1)) {
|
||||
// dateList.value.push(formatDate(new Date(d)));
|
||||
// }
|
||||
|
||||
// 添加这个月的日期
|
||||
for (let d = thisMonthFirstDay; d <= thisMonthLastDay; d.setDate(d.getDate() + 1)) {
|
||||
dateList.value.push(formatDate(new Date(d)));
|
||||
}
|
||||
|
||||
// 添加下个月的日期
|
||||
for (let d = nextMonthFirstDay; d <= nextMonthLastDay; d.setDate(d.getDate() + 1)) {
|
||||
dateList.value.push(formatDate(new Date(d)));
|
||||
}
|
||||
|
||||
// 找到今天的日期在 dateList 中的索引
|
||||
const todayIndex = dateList.value.indexOf(formatDate(currentDate));
|
||||
if (todayIndex !== -1) {
|
||||
defaultOpeneds.value = todayIndex;
|
||||
}
|
||||
|
||||
isDate.value = formatDate(currentDate); // 今天
|
||||
scheduleForm.value.date = formatDate(currentDate); // 默认的日期
|
||||
// 获取排班时段
|
||||
listQuantum().then((res) => {
|
||||
|
||||
timeList.value = res.data;
|
||||
|
||||
timeDataList.value = res.data.map(item => ({
|
||||
value: item.id,
|
||||
label: `${item.startTime}-${item.endTime}`
|
||||
}));
|
||||
|
||||
initSelectTimeClick(formatDate(currentDate), timeList.value);// 获取当天所有时段人员
|
||||
})
|
||||
};
|
||||
|
||||
// 监听选中状态
|
||||
const handleDateChange = (value) => {
|
||||
|
||||
if (editStatus.value) {
|
||||
return false;
|
||||
} // 编辑的时候无需获取
|
||||
|
||||
if (!scheduleForm.value.date) {
|
||||
scheduleForm.value.timeQuantumId = '';
|
||||
|
||||
ElMessage({
|
||||
type: 'warning',
|
||||
message: '请先选择排班日期'
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
if (!scheduleForm.value.timeQuantumId) {
|
||||
ElMessage({
|
||||
type: 'warning',
|
||||
message: '请继续选择排班时间段'
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
// 查询勘测人员列表
|
||||
let params = {
|
||||
date: formatDate(scheduleForm.value.date),
|
||||
timeQuantumId: scheduleForm.value.timeQuantumId
|
||||
};
|
||||
|
||||
userList.value = [];
|
||||
|
||||
listMaintenanceUser(params).then((res) => {
|
||||
|
||||
userList.value = res.data
|
||||
});
|
||||
}
|
||||
|
||||
// 添加排班人员
|
||||
const handleSaveSchedule = () => {
|
||||
|
||||
// 如果不是编辑,则是添加,如果是编辑,则是更新
|
||||
if (!editStatus.value) {
|
||||
if (!scheduleForm.value.date) {
|
||||
ElMessage({
|
||||
type: 'warning',
|
||||
message: '请选择排班日期'
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
if (!scheduleForm.value.timeQuantumId) {
|
||||
ElMessage({
|
||||
type: 'warning',
|
||||
message: '请选择排版时段'
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
if (scheduleForm.value.userids.length < 1) {
|
||||
ElMessage({
|
||||
type: 'warning',
|
||||
message: '请选择人员'
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
scheduleForm.value.date = formatDate(scheduleForm.value.date); //转换日期
|
||||
|
||||
// 添加人员
|
||||
addSchedule(scheduleForm.value).then((res) => {
|
||||
|
||||
if (res.code === 200) {
|
||||
|
||||
if (!istimeQuantumId.value) {
|
||||
initSelectTimeClick(scheduleForm.value.date, timeList.value);// 获取当天所有时段人员
|
||||
} else {
|
||||
let dateParams = timeList.value.filter(item => item.id === scheduleForm.value.timeQuantumId);
|
||||
selectTimeClick(scheduleForm.value.date, {
|
||||
id: istimeQuantumId.value,
|
||||
startTime: dateParams[0].startTime,
|
||||
endTime: dateParams[0].endTime
|
||||
});// 获取当天所有时段人员
|
||||
}
|
||||
|
||||
scheduleForm.value = {
|
||||
userids: [],
|
||||
roleId: 3,
|
||||
date: '',
|
||||
timeQuantumId: '',
|
||||
}
|
||||
|
||||
drawer.value = false;
|
||||
editStatus.value = false;
|
||||
|
||||
ElMessage({
|
||||
type: 'success',
|
||||
message: '新增成功!'
|
||||
});
|
||||
}
|
||||
|
||||
})
|
||||
} else {
|
||||
|
||||
let params = {
|
||||
id: scheduleForm.value.id,
|
||||
userid: scheduleForm.value.userid,
|
||||
date: scheduleForm.value.date,
|
||||
timeQuantumId: scheduleForm.value.timeQuantumId,
|
||||
status: true
|
||||
};
|
||||
|
||||
// 修改时间段
|
||||
updateSchedule(params).then(res => {
|
||||
|
||||
if (res.code === 200) {
|
||||
|
||||
if (!istimeQuantumId.value) {
|
||||
initSelectTimeClick(scheduleForm.value.date, timeList.value);// 获取当天所有时段人员
|
||||
} else {
|
||||
|
||||
let dateParams = timeList.value.filter(item => item.id === params.timeQuantumId);
|
||||
selectTimeClick(params.date, {
|
||||
id: istimeQuantumId.value,
|
||||
startTime: dateParams[0].startTime,
|
||||
endTime: dateParams[0].endTime
|
||||
});// 获取当天所有时段人员
|
||||
}
|
||||
|
||||
scheduleForm.value = {
|
||||
userids: [],
|
||||
roleId: 3,
|
||||
date: '',
|
||||
timeQuantumId: '',
|
||||
}
|
||||
|
||||
drawer.value = false;
|
||||
editStatus.value = false;
|
||||
|
||||
ElMessage({
|
||||
type: 'success',
|
||||
message: '更新成功!'
|
||||
});
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
};
|
||||
// 取消添加排班人员
|
||||
const handleCancelSchedule = () => {
|
||||
|
||||
drawer.value = false;
|
||||
editStatus.value = false;
|
||||
|
||||
scheduleForm.value = {
|
||||
userids: [],
|
||||
roleId: 3,
|
||||
date: '',
|
||||
timeQuantumId: '',
|
||||
};
|
||||
};
|
||||
|
||||
// 禁止选择之前的日期
|
||||
const disablePastAndSpecificDates = (date) => {
|
||||
// 获取当前日期
|
||||
const currentDate = new Date();
|
||||
|
||||
currentDate.setDate(currentDate.getDate() - 1);
|
||||
|
||||
// 禁止选择今天的前一天及之前的日期(不包含今天)
|
||||
if (date < currentDate) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// 禁止选择特定的未来日期,例如节假日
|
||||
const disabledDates = [new Date(2023, 9, 1), new Date(2023, 10, 1)];
|
||||
return disabledDates.some((disabledDate) => {
|
||||
return (
|
||||
date.getFullYear() === disabledDate.getFullYear() &&
|
||||
date.getMonth() === disabledDate.getMonth() &&
|
||||
date.getDate() === disabledDate.getDate()
|
||||
);
|
||||
});
|
||||
};
|
||||
|
||||
// 获取当天时间段所有人员
|
||||
const initSelectTimeClick = (dateItem, timeList) => {
|
||||
|
||||
let params = {
|
||||
date: dateItem,
|
||||
roleId: 3,
|
||||
}
|
||||
|
||||
userDataList.value = [];
|
||||
|
||||
timeList.map(item => {
|
||||
|
||||
let params = {
|
||||
date: dateItem,
|
||||
roleId: 3,
|
||||
timeQuantumId: item.id
|
||||
}
|
||||
|
||||
// 获取用户数据
|
||||
getScheduleList(params).then((res) => {
|
||||
|
||||
if (res.data.length) {
|
||||
res.data.map(resItem => {
|
||||
|
||||
resItem.date = params.date;
|
||||
resItem.time = item.startTime + '-' + item.endTime;
|
||||
userDataList.value.push(resItem);
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
})
|
||||
|
||||
|
||||
}
|
||||
// 获取用户列表
|
||||
const selectTimeClick = (dateItem, timeItem) => {
|
||||
|
||||
let params = {
|
||||
date: dateItem,
|
||||
roleId: 3,
|
||||
timeQuantumId: timeItem.id
|
||||
};
|
||||
|
||||
userDataList.value = [];
|
||||
|
||||
scheduleForm.value.date = dateItem; // 选中的日期
|
||||
isDate.value = dateItem; // 选中的日期
|
||||
istimeQuantumId.value = timeItem.id; // 选中的时间段id
|
||||
isRangeTime.value = timeItem.startTime + '-' + timeItem.endTime; // 选中的时间段时间
|
||||
|
||||
|
||||
getScheduleList(params).then((res) => {
|
||||
|
||||
res.data.map(resItem => {
|
||||
|
||||
resItem.date = params.date;
|
||||
resItem.time = timeItem.startTime + '-' + timeItem.endTime;
|
||||
userDataList.value.push(resItem);
|
||||
})
|
||||
|
||||
})
|
||||
}
|
||||
|
||||
// 日期格式
|
||||
const formatDate = (date) => {
|
||||
|
||||
if (typeof date === 'string') {
|
||||
// 检查是否已经是格式化的字符串
|
||||
const regex = /^\d{4}-\d{2}-\d{2}$/;
|
||||
if (regex.test(date)) {
|
||||
return date; // 已经是格式化的字符串,直接返回
|
||||
}
|
||||
}
|
||||
|
||||
const year = date.getFullYear();
|
||||
const month = String(date.getMonth() + 1).padStart(2, '0');
|
||||
const day = String(date.getDate()).padStart(2, '0');
|
||||
return `${year}-${month}-${day}`;
|
||||
};
|
||||
|
||||
// 删除排班
|
||||
const delQuantumClick = (row) => {
|
||||
|
||||
let params = {
|
||||
id: row.id,
|
||||
date: row.date
|
||||
};
|
||||
|
||||
// 删除数据
|
||||
delSchedule(params).then((res) => {
|
||||
|
||||
if (res.code === 200) {
|
||||
|
||||
if (!istimeQuantumId.value) {
|
||||
initSelectTimeClick(params.date, timeList.value);// 获取当天所有时段人员
|
||||
} else {
|
||||
let dateParams = timeList.value.filter(item => item.id === istimeQuantumId.value);
|
||||
selectTimeClick(params.date, {
|
||||
id: istimeQuantumId.value,
|
||||
startTime: dateParams[0].startTime,
|
||||
endTime: dateParams[0].endTime
|
||||
});// 获取当天所有时段人员
|
||||
}
|
||||
|
||||
ElMessage({
|
||||
type: 'success',
|
||||
message: '删除成功'
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
// 编辑数据
|
||||
const editClick = (row) => {
|
||||
|
||||
editStatus.value = true;
|
||||
drawer.value = true;
|
||||
|
||||
scheduleForm.value = {...row}
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
initData();
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.mt10 {
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
.ml20 {
|
||||
margin-left: 20px;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,557 @@
|
||||
<template>
|
||||
<el-card class="mt10">
|
||||
<el-drawer v-model="drawer" :direction="direction" :close-on-click-modal="false" :close-on-press-escape="false" :destroy-on-close="true" :show-close="false">
|
||||
<template #header>
|
||||
<h4 v-if="!editStatus">创建排班</h4>
|
||||
<h4 v-else>更新排班</h4>
|
||||
</template>
|
||||
<template #default>
|
||||
|
||||
<el-form :model="scheduleForm" label-width="auto" style="max-width: 600px">
|
||||
|
||||
<el-form-item label="日期时间">
|
||||
<el-date-picker
|
||||
v-if="!editStatus"
|
||||
v-model="scheduleForm.date"
|
||||
type="date"
|
||||
placeholder="排班日期"
|
||||
:disabled-date="disablePastAndSpecificDates"
|
||||
style="margin-right: 20px"
|
||||
@change="handleDateChange"
|
||||
/>
|
||||
<el-date-picker
|
||||
v-else
|
||||
v-model="scheduleForm.date"
|
||||
type="date"
|
||||
placeholder="排班日期"
|
||||
:disabled-date="disablePastAndSpecificDates"
|
||||
style="margin-right: 20px"
|
||||
@change="handleDateChange"
|
||||
disabled
|
||||
/>
|
||||
|
||||
<el-select
|
||||
v-model="scheduleForm.timeQuantumId"
|
||||
filterable
|
||||
placeholder="请选择排班时间"
|
||||
style="width: 240px"
|
||||
@change="handleDateChange"
|
||||
>
|
||||
<el-option
|
||||
v-for="item in timeDataList"
|
||||
:key="item.value"
|
||||
:label="item.label"
|
||||
:value="item.value"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="排班人员">
|
||||
|
||||
<template v-if="!editStatus">
|
||||
<template v-if="userList.length">
|
||||
<el-checkbox-group v-model="scheduleForm.userids" size="large">
|
||||
<el-checkbox-button v-for="item in userList" :key="item.id" :value="item.id">
|
||||
{{ item.nickName }}
|
||||
</el-checkbox-button>
|
||||
</el-checkbox-group>
|
||||
</template>
|
||||
|
||||
<template v-else>
|
||||
<el-alert title="选择排班日期时间后,将会获取排班人员" type="warning" />
|
||||
</template>
|
||||
</template>
|
||||
|
||||
<template v-else>
|
||||
<el-tag type="info">{{scheduleForm.username}}</el-tag>
|
||||
</template>
|
||||
|
||||
</el-form-item>
|
||||
|
||||
<div style="display: flex;justify-content: center;margin-top: 50px">
|
||||
<el-button @click="handleCancelSchedule">取消</el-button>
|
||||
<el-button type="success" @click="handleSaveSchedule">确认</el-button>
|
||||
</div>
|
||||
|
||||
</el-form>
|
||||
</template>
|
||||
|
||||
</el-drawer>
|
||||
|
||||
<el-container class="mt10" style="height: 80vh">
|
||||
<el-aside width="220px">
|
||||
<el-scrollbar>
|
||||
<el-menu >
|
||||
<el-sub-menu v-for="(dateItem, index) in dateList" :index="index.toString()" :key="index">
|
||||
<template #title>
|
||||
<el-icon><Calendar /></el-icon>
|
||||
<template v-if="defaultOpeneds === index">
|
||||
<b>今天:</b> {{ dateItem }}
|
||||
</template>
|
||||
<template v-else>
|
||||
{{ dateItem }}
|
||||
</template>
|
||||
</template>
|
||||
<el-menu-item-group>
|
||||
<el-menu-item v-for="(timeItem, timeIndex) in timeList" :key="timeItem.id" :index="index.toString() + '-' + timeIndex.toString()" @click="selectTimeClick(dateItem, timeItem)">
|
||||
{{timeItem.startTime}} - {{timeItem.endTime}}
|
||||
</el-menu-item>
|
||||
</el-menu-item-group>
|
||||
</el-sub-menu>
|
||||
</el-menu>
|
||||
</el-scrollbar>
|
||||
</el-aside>
|
||||
<el-container class="ml20">
|
||||
<el-main>
|
||||
|
||||
<el-button type="success" @click="drawer = true" class="mR10">
|
||||
<el-icon style="margin-right: 10px;"><CirclePlus /></el-icon>
|
||||
创建排班
|
||||
</el-button>
|
||||
|
||||
<el-tag type="success" class="mR10">当前日期:{{ isDate }}</el-tag>
|
||||
<el-tag type="info" class="mR10" v-if="isRangeTime">时间段:{{isRangeTime}}</el-tag>
|
||||
|
||||
<el-scrollbar class="mt20">
|
||||
<el-table :data="userDataList">
|
||||
|
||||
<el-table-column prop="date" label="日期" width="200" />
|
||||
<el-table-column prop="time" label="时间段" width="200" />
|
||||
|
||||
<el-table-column prop="id" label="ID" width="200" />
|
||||
<el-table-column prop="username" label="名称" width="120" />
|
||||
<el-table-column label="头像" width="200" align="center">
|
||||
<template #default="scope">
|
||||
<el-avatar :size="50" :src="scope.row.avatar" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="mobile" label="手机号" />
|
||||
|
||||
<el-table-column label="操作" width="280px" align="center">
|
||||
<template v-slot:default="scope">
|
||||
<el-button type="info" @click="editClick(scope.row)">编辑</el-button>
|
||||
<el-popconfirm title="确认删除这个排班吗?" @confirm="delQuantumClick(scope.row)">
|
||||
<template #reference>
|
||||
<el-button type="danger">删除</el-button>
|
||||
</template>
|
||||
</el-popconfirm>
|
||||
</template>
|
||||
</el-table-column>
|
||||
|
||||
</el-table>
|
||||
</el-scrollbar>
|
||||
</el-main>
|
||||
</el-container>
|
||||
</el-container>
|
||||
</el-card>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { onMounted, ref, nextTick } from 'vue';
|
||||
import {Menu as IconMenu, Message, Setting, Calendar, CirclePlus} from '@element-plus/icons-vue';
|
||||
import {delQuantum, listQuantum} from "../../api/modules/schedulingTime";
|
||||
import {ElMessage} from "element-plus";
|
||||
import {addSchedule, delSchedule, getScheduleList, updateSchedule} from "../../api/modules/scheduling";
|
||||
import {listSurveyorsUser} from "../../api/modules/schedulingUser";
|
||||
|
||||
|
||||
const scheduleForm = ref({
|
||||
userids: [],
|
||||
roleId: 2,
|
||||
date: '',
|
||||
timeQuantumId: '',
|
||||
});
|
||||
|
||||
const direction = ref('rtl')
|
||||
const drawer = ref(false)
|
||||
const editStatus = ref(false)
|
||||
const userList = ref([]); // 勘测人员列表
|
||||
|
||||
// 日期列表
|
||||
const dateList = ref([]);
|
||||
const timeList = ref([]); // 时间段
|
||||
const timeDataList = ref([]); // 时间段
|
||||
const defaultOpeneds = ref([]);
|
||||
|
||||
const userDataList = ref([]); // 已排期用户
|
||||
const isDate = ref(''); // 选中的日期
|
||||
const istimeQuantumId = ref(''); // 时间段ID
|
||||
const isRangeTime = ref(''); // 时间段
|
||||
|
||||
// 初始化
|
||||
const initData = () => {
|
||||
|
||||
const currentDate = new Date();
|
||||
const currentYear = currentDate.getFullYear();
|
||||
const currentMonth = currentDate.getMonth();
|
||||
|
||||
// 上个月的第一天和最后一天
|
||||
const lastMonthFirstDay = new Date(currentYear, currentMonth - 1, 1);
|
||||
const lastMonthLastDay = new Date(currentYear, currentMonth, 0);
|
||||
|
||||
// 这个月的第一天和最后一天
|
||||
const thisMonthFirstDay = new Date(currentYear, currentMonth, 1);
|
||||
const thisMonthLastDay = new Date(currentYear, currentMonth + 1, 0);
|
||||
|
||||
// 下个月的第一天和最后一天
|
||||
const nextMonthFirstDay = new Date(currentYear, currentMonth + 1, 1);
|
||||
const nextMonthLastDay = new Date(currentYear, currentMonth + 2, 0);
|
||||
|
||||
// 添加上个月的日期
|
||||
// for (let d = lastMonthFirstDay; d <= lastMonthLastDay; d.setDate(d.getDate() + 1)) {
|
||||
// dateList.value.push(formatDate(new Date(d)));
|
||||
// }
|
||||
|
||||
// 添加这个月的日期
|
||||
for (let d = thisMonthFirstDay; d <= thisMonthLastDay; d.setDate(d.getDate() + 1)) {
|
||||
dateList.value.push(formatDate(new Date(d)));
|
||||
}
|
||||
|
||||
// 添加下个月的日期
|
||||
for (let d = nextMonthFirstDay; d <= nextMonthLastDay; d.setDate(d.getDate() + 1)) {
|
||||
dateList.value.push(formatDate(new Date(d)));
|
||||
}
|
||||
|
||||
// 找到今天的日期在 dateList 中的索引
|
||||
const todayIndex = dateList.value.indexOf(formatDate(currentDate));
|
||||
if (todayIndex !== -1) {
|
||||
defaultOpeneds.value = todayIndex;
|
||||
}
|
||||
|
||||
isDate.value = formatDate(currentDate); // 今天
|
||||
scheduleForm.value.date = formatDate(currentDate); // 默认的日期
|
||||
// 获取排班时段
|
||||
listQuantum().then((res) => {
|
||||
|
||||
timeList.value = res.data;
|
||||
|
||||
timeDataList.value = res.data.map(item => ({
|
||||
value: item.id,
|
||||
label: `${item.startTime}-${item.endTime}`
|
||||
}));
|
||||
|
||||
initSelectTimeClick(formatDate(currentDate), timeList.value);// 获取当天所有时段人员
|
||||
})
|
||||
};
|
||||
|
||||
// 监听选中状态
|
||||
const handleDateChange = (value) => {
|
||||
|
||||
if(editStatus.value){return false;} // 编辑的时候无需获取
|
||||
|
||||
if(!scheduleForm.value.date)
|
||||
{
|
||||
scheduleForm.value.timeQuantumId = '';
|
||||
|
||||
ElMessage({
|
||||
type: 'warning',
|
||||
message: '请先选择排班日期'
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
if(!scheduleForm.value.timeQuantumId)
|
||||
{
|
||||
ElMessage({
|
||||
type: 'warning',
|
||||
message: '请继续选择排班时间段'
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
// 查询勘测人员列表
|
||||
let params = {
|
||||
date: formatDate(scheduleForm.value.date),
|
||||
timeQuantumId: scheduleForm.value.timeQuantumId
|
||||
};
|
||||
|
||||
userList.value = [];
|
||||
|
||||
listSurveyorsUser(params).then((res)=>{
|
||||
|
||||
userList.value = res.data
|
||||
});
|
||||
}
|
||||
|
||||
// 添加排班人员
|
||||
const handleSaveSchedule = () => {
|
||||
|
||||
// 如果不是编辑,则是添加,如果是编辑,则是更新
|
||||
if(!editStatus.value)
|
||||
{
|
||||
if(!scheduleForm.value.date){
|
||||
ElMessage({
|
||||
type: 'warning',
|
||||
message: '请选择排班日期'
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
if(!scheduleForm.value.timeQuantumId){
|
||||
ElMessage({
|
||||
type: 'warning',
|
||||
message: '请选择排版时段'
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
if(scheduleForm.value.userids.length < 1){
|
||||
ElMessage({
|
||||
type: 'warning',
|
||||
message: '请选择人员'
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
scheduleForm.value.date = formatDate(scheduleForm.value.date); //转换日期
|
||||
|
||||
// 添加人员
|
||||
addSchedule(scheduleForm.value).then((res) => {
|
||||
|
||||
if(res.code === 200)
|
||||
{
|
||||
|
||||
if(!istimeQuantumId.value)
|
||||
{
|
||||
initSelectTimeClick(scheduleForm.value.date, timeList.value);// 获取当天所有时段人员
|
||||
}else{
|
||||
let dateParams = timeList.value.filter(item => item.id === scheduleForm.value.timeQuantumId);
|
||||
selectTimeClick(scheduleForm.value.date, {id:istimeQuantumId.value, startTime:dateParams[0].startTime, endTime:dateParams[0].endTime});// 获取当天所有时段人员
|
||||
}
|
||||
|
||||
scheduleForm.value = {
|
||||
userids: [],
|
||||
roleId: 2,
|
||||
date: '',
|
||||
timeQuantumId: '',
|
||||
}
|
||||
|
||||
drawer.value = false;
|
||||
editStatus.value = false;
|
||||
|
||||
ElMessage({
|
||||
type: 'success',
|
||||
message: '新增成功!'
|
||||
});
|
||||
}
|
||||
|
||||
})
|
||||
}else{
|
||||
|
||||
let params = {
|
||||
id: scheduleForm.value.id,
|
||||
userid: scheduleForm.value.userid,
|
||||
date: scheduleForm.value.date,
|
||||
timeQuantumId: scheduleForm.value.timeQuantumId,
|
||||
status: true
|
||||
};
|
||||
|
||||
// 修改时间段
|
||||
updateSchedule(params).then(res => {
|
||||
|
||||
if(res.code === 200)
|
||||
{
|
||||
|
||||
if(!istimeQuantumId.value)
|
||||
{
|
||||
initSelectTimeClick(scheduleForm.value.date, timeList.value);// 获取当天所有时段人员
|
||||
}else{
|
||||
|
||||
let dateParams = timeList.value.filter(item => item.id === params.timeQuantumId);
|
||||
selectTimeClick(params.date, {id:istimeQuantumId.value, startTime:dateParams[0].startTime, endTime:dateParams[0].endTime});// 获取当天所有时段人员
|
||||
}
|
||||
|
||||
scheduleForm.value = {
|
||||
userids: [],
|
||||
roleId: 2,
|
||||
date: '',
|
||||
timeQuantumId: '',
|
||||
}
|
||||
|
||||
drawer.value = false;
|
||||
editStatus.value = false;
|
||||
|
||||
ElMessage({
|
||||
type: 'success',
|
||||
message: '更新成功!'
|
||||
});
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
};
|
||||
// 取消添加排班人员
|
||||
const handleCancelSchedule = () => {
|
||||
|
||||
drawer.value = false;
|
||||
editStatus.value = false;
|
||||
|
||||
scheduleForm.value = {
|
||||
userids: [],
|
||||
roleId: 2,
|
||||
date: '',
|
||||
timeQuantumId: '',
|
||||
};
|
||||
};
|
||||
|
||||
// 禁止选择之前的日期
|
||||
const disablePastAndSpecificDates = (date) => {
|
||||
// 获取当前日期
|
||||
const currentDate = new Date();
|
||||
|
||||
currentDate.setDate(currentDate.getDate() - 1);
|
||||
|
||||
// 禁止选择今天的前一天及之前的日期(不包含今天)
|
||||
if (date < currentDate) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// 禁止选择特定的未来日期,例如节假日
|
||||
const disabledDates = [new Date(2023, 9, 1), new Date(2023, 10, 1)];
|
||||
return disabledDates.some((disabledDate) => {
|
||||
return (
|
||||
date.getFullYear() === disabledDate.getFullYear() &&
|
||||
date.getMonth() === disabledDate.getMonth() &&
|
||||
date.getDate() === disabledDate.getDate()
|
||||
);
|
||||
});
|
||||
};
|
||||
|
||||
// 获取当天时间段所有人员
|
||||
const initSelectTimeClick = (dateItem, timeList) => {
|
||||
|
||||
let params = {
|
||||
date: dateItem,
|
||||
roleId: 2,
|
||||
}
|
||||
|
||||
userDataList.value = [];
|
||||
|
||||
timeList.map(item => {
|
||||
|
||||
let params = {
|
||||
date: dateItem,
|
||||
roleId: 2,
|
||||
timeQuantumId: item.id
|
||||
}
|
||||
|
||||
// 获取用户数据
|
||||
getScheduleList(params).then((res) => {
|
||||
|
||||
if(res.data.length)
|
||||
{
|
||||
res.data.map(resItem => {
|
||||
|
||||
resItem.date = params.date;
|
||||
resItem.time = item.startTime + '-' + item.endTime;
|
||||
userDataList.value.push(resItem);
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
})
|
||||
|
||||
|
||||
}
|
||||
// 获取用户列表
|
||||
const selectTimeClick = (dateItem, timeItem) => {
|
||||
|
||||
let params = {
|
||||
date: dateItem,
|
||||
roleId: 2,
|
||||
timeQuantumId: timeItem.id
|
||||
};
|
||||
|
||||
userDataList.value = [];
|
||||
|
||||
scheduleForm.value.date = dateItem; // 选中的日期
|
||||
isDate.value = dateItem; // 选中的日期
|
||||
istimeQuantumId.value = timeItem.id; // 选中的时间段id
|
||||
isRangeTime.value = timeItem.startTime + '-' + timeItem.endTime; // 选中的时间段时间
|
||||
|
||||
|
||||
getScheduleList(params).then((res) => {
|
||||
|
||||
res.data.map(resItem => {
|
||||
|
||||
resItem.date = params.date;
|
||||
resItem.time = timeItem.startTime + '-' + timeItem.endTime;
|
||||
userDataList.value.push(resItem);
|
||||
})
|
||||
|
||||
})
|
||||
}
|
||||
|
||||
// 日期格式
|
||||
const formatDate = (date) => {
|
||||
|
||||
if (typeof date === 'string') {
|
||||
// 检查是否已经是格式化的字符串
|
||||
const regex = /^\d{4}-\d{2}-\d{2}$/;
|
||||
if (regex.test(date)) {
|
||||
return date; // 已经是格式化的字符串,直接返回
|
||||
}
|
||||
}
|
||||
|
||||
const year = date.getFullYear();
|
||||
const month = String(date.getMonth() + 1).padStart(2, '0');
|
||||
const day = String(date.getDate()).padStart(2, '0');
|
||||
return `${year}-${month}-${day}`;
|
||||
};
|
||||
|
||||
// 删除排班
|
||||
const delQuantumClick = (row) => {
|
||||
|
||||
let params = {
|
||||
id:row.id,
|
||||
date: row.date
|
||||
};
|
||||
|
||||
// 删除数据
|
||||
delSchedule(params).then((res) => {
|
||||
|
||||
if(res.code === 200)
|
||||
{
|
||||
|
||||
if(!istimeQuantumId.value)
|
||||
{
|
||||
initSelectTimeClick(params.date, timeList.value);// 获取当天所有时段人员
|
||||
}else{
|
||||
let dateParams = timeList.value.filter(item => item.id === istimeQuantumId.value);
|
||||
selectTimeClick(params.date, {id:istimeQuantumId.value, startTime:dateParams[0].startTime, endTime:dateParams[0].endTime});// 获取当天所有时段人员
|
||||
}
|
||||
|
||||
ElMessage({
|
||||
type: 'success',
|
||||
message: '删除成功'
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
// 编辑数据
|
||||
const editClick = (row) => {
|
||||
|
||||
editStatus.value = true;
|
||||
drawer.value = true;
|
||||
|
||||
scheduleForm.value = {...row}
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
initData();
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.mt10 {
|
||||
margin-top: 10px;
|
||||
}
|
||||
.ml20 {
|
||||
margin-left: 20px;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,303 @@
|
||||
<template>
|
||||
|
||||
<el-card class="mt10">
|
||||
|
||||
<el-button type="success" @click="drawer = true">
|
||||
<el-icon style="margin-right: 10px;"><CirclePlus /></el-icon>
|
||||
创建时段
|
||||
</el-button>
|
||||
|
||||
<el-drawer v-model="drawer" :direction="direction" :close-on-click-modal="false" :close-on-press-escape="false" :destroy-on-close="true" :show-close="false">
|
||||
<template #header>
|
||||
<h4 v-if="!editStatus">创建排班</h4>
|
||||
<h4 v-else>更新排班</h4>
|
||||
</template>
|
||||
<template #default>
|
||||
|
||||
<el-form :model="formData" label-width="auto" style="max-width: 600px">
|
||||
|
||||
<el-form-item label="开始时间">
|
||||
<el-time-select
|
||||
v-model="formData.startTime"
|
||||
:max-time="formData.endTime"
|
||||
class="time-select"
|
||||
placeholder="选择开始时间"
|
||||
start="00:00:00"
|
||||
end="23:59:59"
|
||||
step="00:30:00"
|
||||
format="HH:mm:ss"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="结束时间">
|
||||
<el-time-select
|
||||
v-model="formData.endTime"
|
||||
:min-time="formData.startTime"
|
||||
placeholder="结束时间"
|
||||
start="00:00:00"
|
||||
end="23:59:59"
|
||||
step="00:30:00"
|
||||
format="HH:mm:ss"
|
||||
/>
|
||||
</el-form-item>
|
||||
|
||||
|
||||
<div style="display: flex;justify-content: center;margin-top: 50px">
|
||||
<el-button @click="handleCancel">取消</el-button>
|
||||
<el-button type="success" @click="handleSave">确认</el-button>
|
||||
</div>
|
||||
</el-form>
|
||||
</template>
|
||||
|
||||
</el-drawer>
|
||||
|
||||
<el-table
|
||||
ref="table"
|
||||
:data="tableData"
|
||||
:default-sort="{ prop: 'date', order: 'descending' }"
|
||||
:header-cell-style="{ backgroundColor: '#ecf5ff' }"
|
||||
class="mt20"
|
||||
v-loading="loading"
|
||||
>
|
||||
|
||||
<el-table-column
|
||||
label="编号"
|
||||
prop="id"
|
||||
width="120"
|
||||
align="center"
|
||||
show-overflow-tooltip
|
||||
sortable
|
||||
/>
|
||||
|
||||
<el-table-column label="时段" align="center">
|
||||
<template #default="scope">
|
||||
{{scope.row.startTime}}-{{scope.row.endTime}}
|
||||
</template>
|
||||
</el-table-column>
|
||||
|
||||
|
||||
<el-table-column label="操作" width="280px" align="center">
|
||||
<template v-slot:default="scope">
|
||||
<el-button type="info" @click="editClick(scope.row)">编辑</el-button>
|
||||
<el-popconfirm title="确认删除这个时间段吗?" @confirm="delQuantumClick(scope.row)">
|
||||
<template #reference>
|
||||
<el-button type="danger">删除</el-button>
|
||||
</template>
|
||||
</el-popconfirm>
|
||||
</template>
|
||||
</el-table-column>
|
||||
|
||||
</el-table>
|
||||
|
||||
</el-card>
|
||||
</template>
|
||||
<script setup>
|
||||
|
||||
import {nextTick, onMounted, reactive, ref} from 'vue'
|
||||
import {addQuantum, delQuantum, listQuantum, updateQuantum} from "../../api/modules/schedulingTime";
|
||||
import {CirclePlus} from "@element-plus/icons-vue";
|
||||
import {ElMessage} from "element-plus";
|
||||
|
||||
const tableData = ref([])
|
||||
const table = ref()
|
||||
|
||||
const total = ref(0)
|
||||
|
||||
const drawer = ref(false)
|
||||
const editStatus = ref(false)
|
||||
const direction = ref('rtl');
|
||||
const loading = ref(true)
|
||||
|
||||
const formData = ref({
|
||||
startTime: '',
|
||||
endTime:'',
|
||||
flag: true
|
||||
});
|
||||
|
||||
// 初始化数据
|
||||
const initData = () => {
|
||||
|
||||
// 获取排班时段
|
||||
listQuantum().then((res) => {
|
||||
tableData.value = res.data
|
||||
loading.value = false
|
||||
})
|
||||
}
|
||||
|
||||
// 取消
|
||||
const handleCancel = () => {
|
||||
drawer.value = false
|
||||
|
||||
formData.value = {
|
||||
startTime: '',
|
||||
endTime:'',
|
||||
flag: false
|
||||
};
|
||||
}
|
||||
|
||||
// 提交数据
|
||||
const handleSave = () => {
|
||||
|
||||
if(!formData.value.startTime){
|
||||
ElMessage({
|
||||
type: 'warning',
|
||||
message: '请选择开始时间段'
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
if(!formData.value.endTime){
|
||||
ElMessage({
|
||||
type: 'warning',
|
||||
message: '请选择结束时间段'
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
if(!editStatus.value)
|
||||
{
|
||||
// 添加时间段
|
||||
addQuantum(formData.value).then(res => {
|
||||
|
||||
initData();
|
||||
drawer.value = false;
|
||||
|
||||
formData.value = {
|
||||
startTime: '',
|
||||
endTime:'',
|
||||
flag: false
|
||||
};
|
||||
|
||||
ElMessage({
|
||||
type: 'success',
|
||||
message: '添加成功'
|
||||
});
|
||||
|
||||
});
|
||||
}else{
|
||||
|
||||
// 修改数据
|
||||
updateQuantum(formData.value).then((res) => {
|
||||
|
||||
editStatus.value = false;
|
||||
drawer.value = false;
|
||||
|
||||
ElMessage({
|
||||
message: '修改成功',
|
||||
type: 'success',
|
||||
});
|
||||
initData();
|
||||
})
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// 删除时间段
|
||||
const delQuantumClick = (row) => {
|
||||
|
||||
let params = {
|
||||
id:row.id
|
||||
};
|
||||
|
||||
// 删除数据
|
||||
delQuantum(params).then((res) => {
|
||||
|
||||
initData();
|
||||
|
||||
ElMessage({
|
||||
type: 'success',
|
||||
message: '删除成功'
|
||||
});
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
// 编辑数据
|
||||
const editClick = (row) => {
|
||||
|
||||
editStatus.value = true;
|
||||
drawer.value = true;
|
||||
|
||||
formData.value = {...row}
|
||||
}
|
||||
|
||||
// 启用禁用时间段
|
||||
const upStatus = (row) => {
|
||||
|
||||
let formData = {...row}
|
||||
|
||||
// 修改数据
|
||||
updateQuantum(formData).then((res) => {
|
||||
|
||||
if(formData.flag)
|
||||
{
|
||||
ElMessage({
|
||||
message: '启用成功',
|
||||
type: 'success',
|
||||
});
|
||||
}else{
|
||||
ElMessage({
|
||||
message: '禁用成功',
|
||||
type: 'success',
|
||||
});
|
||||
}
|
||||
initData();
|
||||
})
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
initData()
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.mt-4 {
|
||||
float: right;
|
||||
text-align: right;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.demo-form-inline {
|
||||
}
|
||||
|
||||
.dragClass {
|
||||
background: rgba($color: #41c21a, $alpha: 0.5) !important;
|
||||
}
|
||||
|
||||
// 停靠
|
||||
.ghostClass {
|
||||
background: rgba($color: #6cacf5, $alpha: 0.5) !important;
|
||||
}
|
||||
|
||||
// 选择
|
||||
.chosenClass:hover > td {
|
||||
background: rgba($color: #f56c6c, $alpha: 0.5) !important;
|
||||
}
|
||||
|
||||
.icon-view {
|
||||
font-size: 20px;
|
||||
color: #673ab7;
|
||||
margin: 0 10px;
|
||||
}
|
||||
|
||||
.icon-edit {
|
||||
font-size: 20px;
|
||||
color: #2f60c2;
|
||||
margin: 0 10px;
|
||||
}
|
||||
|
||||
.icon-dele {
|
||||
font-size: 20px;
|
||||
color: #ff5722;
|
||||
}
|
||||
|
||||
.btn-color {
|
||||
background: #fff;
|
||||
color: #4c60cc;
|
||||
}
|
||||
|
||||
.expand-title {
|
||||
padding: 10px;
|
||||
color: #919399;
|
||||
font-weight: 600;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,442 @@
|
||||
<template>
|
||||
<el-card class="mt10">
|
||||
|
||||
<el-button type="success" @click="drawer = true">
|
||||
<el-icon style="margin-right: 10px;"><CirclePlus /></el-icon>
|
||||
创建活动
|
||||
</el-button>
|
||||
|
||||
<!-- <el-form :inline="true" :model="formSearch" class="mt10">-->
|
||||
<!-- <el-form-item label="活动名称">-->
|
||||
<!-- <el-input-->
|
||||
<!-- v-model="formSearch.name"-->
|
||||
<!-- style="max-width: 300px"-->
|
||||
<!-- placeholder="模糊查询活动名称"-->
|
||||
<!-- class="input-with-select"-->
|
||||
<!-- >-->
|
||||
<!-- </el-input>-->
|
||||
<!-- </el-form-item>-->
|
||||
|
||||
<!-- <el-form-item>-->
|
||||
<!-- <el-button type="primary" @click="onSearchSubmit">搜索</el-button>-->
|
||||
<!-- </el-form-item>-->
|
||||
|
||||
<!-- </el-form>-->
|
||||
|
||||
<el-drawer v-model="drawer" :direction="direction" :close-on-click-modal="false" :close-on-press-escape="false" :destroy-on-close="true" :show-close="false">
|
||||
<template #header>
|
||||
<h4>创建活动</h4>
|
||||
</template>
|
||||
<template #default>
|
||||
|
||||
<el-form
|
||||
:model="formData"
|
||||
label-width="auto" v-loading="loading">
|
||||
|
||||
<el-form-item label="活动名称">
|
||||
<el-input
|
||||
v-model="formData.title"
|
||||
placeholder="请输入活动名称"
|
||||
/>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="轮播图">
|
||||
<el-upload
|
||||
v-model:file-list="fileBannerList"
|
||||
list-type="picture-card"
|
||||
:before-upload="beforeUpload"
|
||||
:http-request="addUploadBannerFile"
|
||||
:limit="1"
|
||||
:on-exceed="handleBannerExceed"
|
||||
:on-remove="handleBannerRemove"
|
||||
>
|
||||
<el-icon><Plus /></el-icon>
|
||||
</el-upload>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="详情图">
|
||||
<el-upload
|
||||
v-model:file-list="fileDetailList"
|
||||
list-type="picture-card"
|
||||
:before-upload="beforeUpload"
|
||||
:http-request="addUploadDetailFile"
|
||||
:limit="1"
|
||||
:on-exceed="handleDetailExceed"
|
||||
:on-remove="handleDetailRemove"
|
||||
>
|
||||
<el-icon><Plus /></el-icon>
|
||||
</el-upload>
|
||||
</el-form-item>
|
||||
|
||||
</el-form>
|
||||
<div style="display: flex;justify-content: flex-end;margin-top: 50px">
|
||||
<el-button @click="cancelClick">取消</el-button>
|
||||
<el-button type="primary" @click="confirmClick">确认</el-button>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
</el-drawer>
|
||||
|
||||
<el-table
|
||||
ref="table"
|
||||
:data="tableData"
|
||||
:default-sort="{ prop: 'date', order: 'descending' }"
|
||||
:header-cell-style="{ backgroundColor: '#ecf5ff' }"
|
||||
border
|
||||
stripe
|
||||
class="mt10"
|
||||
>
|
||||
|
||||
<el-table-column
|
||||
label="序号"
|
||||
prop="id"
|
||||
width="120"
|
||||
align="center"
|
||||
show-overflow-tooltip
|
||||
sortable
|
||||
/>
|
||||
|
||||
<el-table-column
|
||||
label="活动名称"
|
||||
prop="title"
|
||||
width="600"
|
||||
align="left"
|
||||
show-overflow-tooltip
|
||||
/>
|
||||
|
||||
<el-table-column label="展示图" width="300px" align="center">
|
||||
<template #default="scope">
|
||||
<el-image style="width: 200px; height: 50px" :src="scope.row.thumbnail" lazy />
|
||||
</template>
|
||||
</el-table-column>
|
||||
|
||||
<el-table-column label="详情图" width="300px" align="center">
|
||||
<template #default="scope">
|
||||
<el-image style="width: 300px; height: 200px" :src="scope.row.detailImg" lazy />
|
||||
</template>
|
||||
</el-table-column>
|
||||
|
||||
<el-table-column
|
||||
label="发布时间"
|
||||
prop="createdAt"
|
||||
width="auto"
|
||||
align="center"
|
||||
show-overflow-tooltip
|
||||
sortable
|
||||
/>
|
||||
|
||||
<el-table-column label="操作" width="200px" align="center">
|
||||
<template v-slot:default="scope">
|
||||
<el-popconfirm title="确认删除这张图片吗?" @confirm="deleteData(scope.row)">
|
||||
<template #reference>
|
||||
<el-button type="danger">删除</el-button>
|
||||
</template>
|
||||
</el-popconfirm>
|
||||
</template>
|
||||
</el-table-column>
|
||||
|
||||
</el-table>
|
||||
<el-pagination
|
||||
size="small"
|
||||
background
|
||||
layout="prev, pager, next"
|
||||
:total="tableData.length"
|
||||
class="mt-4 mt10"
|
||||
@current-change="handleCurrentChange"
|
||||
/>
|
||||
</el-card>
|
||||
</template>
|
||||
<script setup>
|
||||
import {
|
||||
Edit,
|
||||
Delete,
|
||||
InfoFilled,
|
||||
View,
|
||||
ArrowDownBold,
|
||||
Plus, CirclePlus,
|
||||
} from '@element-plus/icons-vue'
|
||||
import {nextTick, onMounted, reactive, ref} from 'vue'
|
||||
|
||||
import {ElMessage} from "element-plus";
|
||||
import {addFile} from "../../api/modules/upload";
|
||||
import {getConfigList, updateSystem} from "../../api/modules/system";
|
||||
const direction = ref('rtl');
|
||||
const tableData = ref([])
|
||||
const dataList = ref([])
|
||||
const table = ref()
|
||||
const isExpand = ref(false)
|
||||
|
||||
const dialogVisible = ref(false)
|
||||
const total = ref(0)
|
||||
|
||||
const fileBannerList = ref([]);
|
||||
const fileDetailList = ref([]);
|
||||
|
||||
const loading = ref(false)
|
||||
|
||||
const searchName = ref('');
|
||||
const drawer = ref(false)
|
||||
const queryData = ref({
|
||||
keyWord: '',
|
||||
page: 1,
|
||||
size: 10,
|
||||
})
|
||||
|
||||
const formSearch = ref({
|
||||
searchName:'',
|
||||
});
|
||||
|
||||
const formData = ref({
|
||||
id: 1,
|
||||
title: '',
|
||||
thumbnail:'',
|
||||
detailImg: '',
|
||||
createdAt: '',
|
||||
});
|
||||
|
||||
// 初始化数据
|
||||
const initData = () => {
|
||||
|
||||
getConfigList(queryData.value).then((res) => {
|
||||
|
||||
if(res.data[1].content !== '[]' && res.data[1].content !== null && res.data[0].content !== "")
|
||||
{
|
||||
tableData.value = JSON.parse(res.data[1].content);
|
||||
// 找出最ID
|
||||
const id = Math.max.apply(null, tableData.value.map(item => item.id));
|
||||
formData.value.id = id+1;
|
||||
}
|
||||
|
||||
})
|
||||
}
|
||||
|
||||
// 删除数据
|
||||
const deleteData = (row) => {
|
||||
|
||||
// 过滤掉需要删除的项
|
||||
tableData.value = tableData.value.filter(item => item.id !== row.id);
|
||||
|
||||
let params = {
|
||||
id: 2,
|
||||
content: JSON.stringify(tableData.value)
|
||||
};
|
||||
|
||||
// 更新系统配置
|
||||
updateSystem(params).then((res) => {
|
||||
|
||||
initData();
|
||||
|
||||
// 显示删除成功的消息
|
||||
ElMessage({
|
||||
message: '删除成功',
|
||||
type: 'success'
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
}
|
||||
|
||||
// 提交表单
|
||||
const confirmClick = () => {
|
||||
|
||||
// 检查表单数据完整性
|
||||
if (!formData.value.title) {
|
||||
ElMessage.error('请添加活动名称');
|
||||
return;
|
||||
}
|
||||
|
||||
if (!formData.value.thumbnail) {
|
||||
ElMessage.error('请上传轮播图片');
|
||||
return;
|
||||
}
|
||||
|
||||
if (!formData.value.detailImg) {
|
||||
ElMessage.error('请上传详情图片');
|
||||
return;
|
||||
}
|
||||
|
||||
formData.value.createdAt = formatDate(); // 创建时间
|
||||
|
||||
// 合并旧数据,写入新数据
|
||||
let newData = tableData.value.concat([formData.value]);
|
||||
|
||||
let params = {
|
||||
id: 2,
|
||||
content: JSON.stringify(newData)
|
||||
};
|
||||
|
||||
// 更新系统配置
|
||||
updateSystem(params).then((res) => {
|
||||
|
||||
drawer.value = false;
|
||||
|
||||
formData.value = {
|
||||
id: 1,
|
||||
title: '',
|
||||
thumbnail:'',
|
||||
detailImg: '',
|
||||
createdAt: ''
|
||||
}
|
||||
|
||||
fileBannerList.value = [];
|
||||
fileDetailList.value = [];
|
||||
|
||||
initData();
|
||||
|
||||
ElMessage({
|
||||
message: '添加成功',
|
||||
type: 'success',
|
||||
})
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
// 创建格式化日期的函数
|
||||
const formatDate = () => {
|
||||
|
||||
const date = new Date();
|
||||
const year = date.getFullYear();
|
||||
const month = String(date.getMonth() + 1).padStart(2, '0');
|
||||
const day = String(date.getDate()).padStart(2, '0');
|
||||
const hours = String(date.getHours()).padStart(2, '0');
|
||||
const minutes = String(date.getMinutes()).padStart(2, '0');
|
||||
const seconds = String(date.getSeconds()).padStart(2, '0');
|
||||
|
||||
return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`;
|
||||
}
|
||||
|
||||
// 取消事件
|
||||
const cancelClick = () => {
|
||||
|
||||
drawer.value = false;
|
||||
loading.value = false;
|
||||
formData.value = {
|
||||
id: formData.value.id,
|
||||
title: '',
|
||||
thumbnail:'',
|
||||
detailImg: ''
|
||||
}
|
||||
|
||||
fileBannerList.value = [];
|
||||
fileDetailList.value = [];
|
||||
|
||||
}
|
||||
|
||||
// 图片上传前检测
|
||||
const beforeUpload = (file) => {
|
||||
|
||||
loading.value = true;
|
||||
const maxSize = 5 * 1024 * 1024; // 最大 2MB
|
||||
if (file.size > maxSize) {
|
||||
alert('文件大小不能超过 5MB');
|
||||
loading.value = false;
|
||||
return false;
|
||||
}
|
||||
|
||||
const allowedTypes = ['image/jpeg', 'image/png'];
|
||||
if (!allowedTypes.includes(file.type)) {
|
||||
alert('只允许上传 JPEG 或 PNG 图片');
|
||||
loading.value = false;
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// 轮播图图片上传限制
|
||||
const handleBannerExceed= (files, uploadFiles) => {
|
||||
ElMessage.warning(
|
||||
`轮播图最多可上传1张`
|
||||
)
|
||||
}
|
||||
|
||||
// 详情图片上传限制
|
||||
const handleDetailExceed= (files, uploadFiles) => {
|
||||
ElMessage.warning(
|
||||
`详情图最多可上传1张`
|
||||
)
|
||||
}
|
||||
|
||||
// 轮播图移除
|
||||
const handleBannerRemove = (file, fileList) => {
|
||||
formData.value.thumbnail = '';
|
||||
}
|
||||
|
||||
// 详情图移除
|
||||
const handleDetailRemove = (file, fileList) => {
|
||||
formData.value.detailImg = '';
|
||||
}
|
||||
|
||||
// 轮播图上传
|
||||
const addUploadBannerFile = (params) => {
|
||||
|
||||
const formData = new FormData();
|
||||
formData.append('files', params.file);
|
||||
|
||||
addFile(formData)
|
||||
.then((response) => {
|
||||
handleBannerSuccess(response, params.file, params.fileList);
|
||||
})
|
||||
.catch((error) => {
|
||||
if(!error)
|
||||
{
|
||||
handleError(error, params.file, params.fileList);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// 详情图上传
|
||||
const addUploadDetailFile = (params) => {
|
||||
|
||||
const formData = new FormData();
|
||||
formData.append('files', params.file);
|
||||
|
||||
addFile(formData)
|
||||
.then((response) => {
|
||||
handleDetailSuccess(response, params.file, params.fileList);
|
||||
})
|
||||
.catch((error) => {
|
||||
if(!error)
|
||||
{
|
||||
handleError(error, params.file, params.fileList);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// 处理轮播上传成功的回调
|
||||
const handleBannerSuccess = (response) => {
|
||||
formData.value.thumbnail = response.data[0];
|
||||
loading.value = false;
|
||||
}
|
||||
|
||||
// 处理详情上传成功的回调
|
||||
const handleDetailSuccess = (response) => {
|
||||
formData.value.detailImg = response.data[0];
|
||||
loading.value = false;
|
||||
}
|
||||
|
||||
// 处理上传失败的回调
|
||||
const handleError = (error, file, fileList) => {
|
||||
loading.value = false;
|
||||
console.error('上传失败:', error, file, fileList);
|
||||
}
|
||||
|
||||
const handleCurrentChange = (val) => {
|
||||
queryData.value.size = 10
|
||||
queryData.value.page = val
|
||||
initData(queryData.value)
|
||||
}
|
||||
|
||||
// 搜索事件
|
||||
const onSearchSubmit = ()=> {
|
||||
console.log('点击搜索');
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
initData()
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
|
||||
</style>
|
||||
@@ -0,0 +1,75 @@
|
||||
<template>
|
||||
<el-card>
|
||||
<el-form :model="form" label-width="auto" style="max-width: 600px;">
|
||||
|
||||
<el-form-item label="客服电话">
|
||||
<span>{{ serviceTelephone }}</span>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="修改电话">
|
||||
<el-input v-model="form.content" />
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item>
|
||||
<el-button type="primary" @click="onSubmit">修改</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</el-card>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import {onMounted, reactive, ref} from 'vue'
|
||||
import {getConfigList, updateConfig} from "../../api/modules/system";
|
||||
import {ElMessage} from "element-plus";
|
||||
|
||||
// do not use same name with ref
|
||||
const form = reactive({
|
||||
id: undefined,
|
||||
content: ''
|
||||
})
|
||||
let serviceTelephone = ref("400 000 0000")
|
||||
|
||||
const initData = () => {
|
||||
|
||||
getConfigList().then((res) => {
|
||||
console.log(res.data);
|
||||
|
||||
if (Array.isArray(res.data) && res.data.length > 0) {
|
||||
let customerServicePhoneRecord = res.data.find(item => item.title === "客服电话");
|
||||
if (customerServicePhoneRecord) {
|
||||
// 如果找到了匹配的记录,你可以根据需要进行处理
|
||||
console.log("找到客服电话记录:", customerServicePhoneRecord);
|
||||
// 如果需要,可以将这条记录赋值给某个变量或用于其他操作
|
||||
form.id = customerServicePhoneRecord.id;
|
||||
form.content = customerServicePhoneRecord.content;
|
||||
serviceTelephone.value = customerServicePhoneRecord.content;
|
||||
} else {
|
||||
console.log("未找到客服电话记录");
|
||||
}
|
||||
} else {
|
||||
console.log("res.data 是空或不是一个数组");
|
||||
}
|
||||
}).catch((error) => {
|
||||
console.error("获取配置列表时出错:", error);
|
||||
});
|
||||
}
|
||||
|
||||
const onSubmit = () => {
|
||||
console.log('submit!')
|
||||
updateConfig(form).then((res) => {
|
||||
serviceTelephone.value = form.content;
|
||||
console.log('编辑数据成功')
|
||||
console.log(ElMessage)
|
||||
ElMessage({
|
||||
message: '编辑服务项目成功',
|
||||
type: 'success',
|
||||
})
|
||||
// tableData.value = res.data
|
||||
})
|
||||
}
|
||||
onMounted(() => {
|
||||
initData()
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped></style>
|
||||
@@ -0,0 +1,79 @@
|
||||
<template>
|
||||
<el-card>
|
||||
|
||||
<el-form :model="formData" label-width="auto" style="max-width: 600px;">
|
||||
|
||||
<el-form-item label="账号">
|
||||
<span>admin</span>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item
|
||||
:rules="[
|
||||
{ required: true, message: '请输入密码' },
|
||||
]"
|
||||
label="旧密码">
|
||||
<el-input
|
||||
v-model="formData.oldPassword"
|
||||
style="width: 240px"
|
||||
type="password"
|
||||
placeholder="请输入密码"
|
||||
show-password
|
||||
/>
|
||||
</el-form-item>
|
||||
|
||||
<!-- <el-alert type="info" show-icon :closable="false" style="margin-bottom: 10px">-->
|
||||
<!-- <p>留空则不修改账号名,如果账号修改后,旧账号将不能用!</p>-->
|
||||
<!-- </el-alert>-->
|
||||
<!-- <el-form-item label="修改账号">-->
|
||||
<!-- <el-input v-model="form.name" />-->
|
||||
<!-- </el-form-item>-->
|
||||
|
||||
<el-alert type="info" show-icon :closable="false" style="margin-bottom: 10px">
|
||||
<p>留空则不修改密码,如果密码修改后,旧密码将不能用!</p>
|
||||
</el-alert>
|
||||
<el-form-item label="新密码">
|
||||
<el-input
|
||||
v-model="formData.newPassword"
|
||||
style="width: 240px"
|
||||
type="password"
|
||||
placeholder="请输入新密码"
|
||||
show-password
|
||||
/>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item>
|
||||
<el-button type="primary" @click="onSubmit">修改</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</el-card>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { reactive } from 'vue'
|
||||
import {updateAdmin} from "../../api/modules/config";
|
||||
import {ElMessage} from "element-plus";
|
||||
import {useRouter} from "vue-router";
|
||||
|
||||
const router = useRouter();
|
||||
|
||||
const formData = reactive({
|
||||
oldPassword: '',
|
||||
newPassword: '',
|
||||
})
|
||||
|
||||
const onSubmit = () => {
|
||||
|
||||
// 修改密码
|
||||
updateAdmin(formData).then(res => {
|
||||
|
||||
ElMessage({
|
||||
message: '修改成功,请重新登录',
|
||||
type: 'success',
|
||||
});
|
||||
sessionStorage.removeItem("token");
|
||||
router.push("/login");
|
||||
});
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped></style>
|
||||
@@ -0,0 +1,346 @@
|
||||
<template>
|
||||
<el-card class="mt10">
|
||||
|
||||
<el-button type="success" @click="drawer = true">
|
||||
上传首页图
|
||||
<el-icon class="el-icon--right">
|
||||
<Upload/>
|
||||
</el-icon>
|
||||
</el-button>
|
||||
|
||||
<el-drawer v-model="drawer" :direction="direction" :close-on-click-modal="false" :close-on-press-escape="false" :destroy-on-close="true" :show-close="false">
|
||||
<template #header>
|
||||
<h4>上传图片</h4>
|
||||
</template>
|
||||
<template #default>
|
||||
|
||||
<el-form
|
||||
:model="formData"
|
||||
label-width="auto" v-loading="loading">
|
||||
|
||||
<el-form-item label="序号">
|
||||
<el-input
|
||||
v-model="formData.sort"
|
||||
type="number"
|
||||
placeholder="请输入图片排序"
|
||||
disabled
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="首页图">
|
||||
<el-upload
|
||||
v-model:file-list="fileList"
|
||||
list-type="picture-card"
|
||||
:before-upload="beforeUpload"
|
||||
:http-request="addUploadDetailFile"
|
||||
:limit="1"
|
||||
:on-exceed="handleDetailExceed"
|
||||
:on-remove="handleDetailRemove"
|
||||
>
|
||||
<el-icon><Plus /></el-icon>
|
||||
</el-upload>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<div style="display: flex;justify-content: flex-end;margin-top: 50px">
|
||||
<el-button @click="cancelClick">取消</el-button>
|
||||
<el-button type="primary" @click="confirmClick">确认</el-button>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
</el-drawer>
|
||||
|
||||
<el-table
|
||||
ref="table"
|
||||
:data="tableData"
|
||||
:default-sort="{ prop: 'date', order: 'descending' }"
|
||||
:header-cell-style="{ backgroundColor: '#ecf5ff' }"
|
||||
border
|
||||
stripe
|
||||
class="mt10"
|
||||
v-loading="loading"
|
||||
>
|
||||
|
||||
<el-table-column
|
||||
label="序号"
|
||||
prop="sort"
|
||||
width="120"
|
||||
align="center"
|
||||
show-overflow-tooltip
|
||||
sortable
|
||||
/>
|
||||
|
||||
<el-table-column label="首页图" align="center">
|
||||
<template #default="scope">
|
||||
<el-image style="width: 300px; height: 200px" :src="scope.row.img" lazy/>
|
||||
</template>
|
||||
</el-table-column>
|
||||
|
||||
<el-table-column label="操作" align="center">
|
||||
<template v-slot:default="scope">
|
||||
<el-popconfirm title="确认删除这张图片吗?" @confirm="deleteData(scope.row)">
|
||||
<template #reference>
|
||||
<el-button type="danger">删除</el-button>
|
||||
</template>
|
||||
</el-popconfirm>
|
||||
</template>
|
||||
</el-table-column>
|
||||
|
||||
</el-table>
|
||||
|
||||
</el-card>
|
||||
</template>
|
||||
<script setup>
|
||||
import * as XLSX from 'xlsx'
|
||||
import {
|
||||
Edit,
|
||||
Delete,
|
||||
InfoFilled,
|
||||
View,
|
||||
ArrowDownBold,
|
||||
Plus,
|
||||
} from '@element-plus/icons-vue'
|
||||
import {nextTick, onMounted, reactive, ref} from 'vue'
|
||||
|
||||
import {options} from './options.js'
|
||||
import {getConfigList, updateSystem} from "../../api/modules/system";
|
||||
import {addFile} from "../../api/modules/upload";
|
||||
import {ElMessage} from "element-plus";
|
||||
|
||||
const tableData = ref([])
|
||||
const table = ref()
|
||||
const dialogVisible = ref(false)
|
||||
const total = ref(0)
|
||||
const direction = ref('rtl');
|
||||
const drawer = ref(false)
|
||||
const queryData = ref({
|
||||
keyWord: '',
|
||||
page: 1,
|
||||
size: 10,
|
||||
})
|
||||
|
||||
const fileList = ref([]);
|
||||
const loading = ref(false)
|
||||
const formData = ref({
|
||||
sort: 1,
|
||||
img: ''
|
||||
});
|
||||
|
||||
// 初始化数据
|
||||
const initData = () => {
|
||||
|
||||
getConfigList(queryData.value).then((res) => {
|
||||
|
||||
if(res.data[0].content !== '[]' && res.data[0].content !== null && res.data[0].content !== "")
|
||||
{
|
||||
tableData.value = JSON.parse(res.data[0].content);
|
||||
// 找出最大序号
|
||||
const sort = Math.max.apply(null, tableData.value.map(item => item.sort));
|
||||
formData.value.sort = sort+1;
|
||||
}
|
||||
|
||||
loading.value = false;
|
||||
})
|
||||
}
|
||||
|
||||
// 提交表单
|
||||
const confirmClick = () => {
|
||||
|
||||
// 检查表单数据完整性
|
||||
if (!formData.value.img) {
|
||||
ElMessage.error('请上传图片');
|
||||
return;
|
||||
}
|
||||
|
||||
// 合并旧数据,写入新数据
|
||||
let newData = tableData.value.concat([formData.value]);
|
||||
|
||||
let params = {
|
||||
id: 1,
|
||||
content: JSON.stringify(newData)
|
||||
};
|
||||
|
||||
// 更新系统配置
|
||||
updateSystem(params).then((res) => {
|
||||
|
||||
drawer.value = false;
|
||||
|
||||
fileList.value = [];
|
||||
|
||||
formData.value = {
|
||||
sort: 1,
|
||||
img: ''
|
||||
}
|
||||
|
||||
initData();
|
||||
|
||||
ElMessage({
|
||||
message: '添加成功',
|
||||
type: 'success',
|
||||
})
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
// 取消
|
||||
const cancelClick = () => {
|
||||
|
||||
drawer.value = false;
|
||||
loading.value = false;
|
||||
formData.value = {
|
||||
sort: formData.value.sort,
|
||||
img: ''
|
||||
}
|
||||
|
||||
fileList.value = [];
|
||||
|
||||
}
|
||||
|
||||
|
||||
// 移除数据
|
||||
const deleteData = (row) => {
|
||||
|
||||
// 过滤掉需要删除的项
|
||||
tableData.value = tableData.value.filter(item => item.sort !== row.sort);
|
||||
|
||||
let params = {
|
||||
id: 1,
|
||||
content: JSON.stringify(tableData.value)
|
||||
};
|
||||
|
||||
// 更新系统配置
|
||||
updateSystem(params).then((res) => {
|
||||
|
||||
// 显示删除成功的消息
|
||||
ElMessage({
|
||||
message: '删除成功',
|
||||
type: 'success'
|
||||
});
|
||||
|
||||
initData();
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
// 上传前的钩子
|
||||
const beforeUpload = (file) => {
|
||||
|
||||
loading.value = true;
|
||||
|
||||
const maxSize = 5 * 1024 * 1024; // 最大 2MB
|
||||
if (file.size > maxSize) {
|
||||
alert('文件大小不能超过 5MB');
|
||||
loading.value = false;
|
||||
return false;
|
||||
}
|
||||
|
||||
const allowedTypes = ['image/jpeg', 'image/png'];
|
||||
if (!allowedTypes.includes(file.type)) {
|
||||
alert('只允许上传 JPEG 或 PNG 图片');
|
||||
loading.value = false;
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// 详情图片上传限制
|
||||
const handleDetailExceed= (files, uploadFiles) => {
|
||||
ElMessage.warning(
|
||||
`详情图最多可上传1张`
|
||||
)
|
||||
}
|
||||
|
||||
// 详情图移除
|
||||
const handleDetailRemove = (file, fileList) => {
|
||||
formData.value.img = '';
|
||||
}
|
||||
|
||||
// 详情上传
|
||||
const addUploadDetailFile = (params) => {
|
||||
|
||||
const formData = new FormData();
|
||||
formData.append('files', params.file);
|
||||
|
||||
addFile(formData)
|
||||
.then((response) => {
|
||||
handleDetailSuccess(response, params.file, params.fileList);
|
||||
})
|
||||
.catch((error) => {
|
||||
|
||||
if(error)
|
||||
{
|
||||
handleError(error, params.file, params.fileList);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// 处理详情上传成功的回调
|
||||
const handleDetailSuccess = (response) => {
|
||||
formData.value.img = response.data[0];
|
||||
loading.value = false;
|
||||
}
|
||||
|
||||
// 处理上传失败的回调
|
||||
const handleError = (error, file, fileList) => {
|
||||
loading.value = false;
|
||||
console.error('上传失败:', error, file, fileList);
|
||||
}
|
||||
|
||||
|
||||
onMounted(() => {
|
||||
loading.value = true;
|
||||
initData()
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.mt-4 {
|
||||
float: right;
|
||||
text-align: right;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.demo-form-inline {
|
||||
}
|
||||
|
||||
.dragClass {
|
||||
background: rgba($color: #41c21a, $alpha: 0.5) !important;
|
||||
}
|
||||
|
||||
// 停靠
|
||||
.ghostClass {
|
||||
background: rgba($color: #6cacf5, $alpha: 0.5) !important;
|
||||
}
|
||||
|
||||
// 选择
|
||||
.chosenClass:hover > td {
|
||||
background: rgba($color: #f56c6c, $alpha: 0.5) !important;
|
||||
}
|
||||
|
||||
.icon-view {
|
||||
font-size: 20px;
|
||||
color: #673ab7;
|
||||
margin: 0 10px;
|
||||
}
|
||||
|
||||
.icon-edit {
|
||||
font-size: 20px;
|
||||
color: #2f60c2;
|
||||
margin: 0 10px;
|
||||
}
|
||||
|
||||
.icon-dele {
|
||||
font-size: 20px;
|
||||
color: #ff5722;
|
||||
}
|
||||
|
||||
.btn-color {
|
||||
background: #fff;
|
||||
color: #4c60cc;
|
||||
}
|
||||
|
||||
.expand-title {
|
||||
padding: 10px;
|
||||
color: #919399;
|
||||
font-weight: 600;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,86 @@
|
||||
export const options = [
|
||||
{
|
||||
userId: 1,
|
||||
avatar: 'https://pic.qqans.com/up/2024-6/2024620113729773.jpg',
|
||||
name: '高永建',
|
||||
nickname: '软件开发老高',
|
||||
mobile: '19136448763',
|
||||
date: '2024-10-11 10:11:01'
|
||||
},
|
||||
{
|
||||
userId: 2,
|
||||
avatar: 'https://pic.qqans.com/up/2024-6/2024620113729773.jpg',
|
||||
name: '张三',
|
||||
nickname: '软件开发老高',
|
||||
mobile: '19136448763',
|
||||
date: '2024-10-10 10:11:01'
|
||||
},
|
||||
{
|
||||
userId: 3,
|
||||
avatar: 'https://pic.qqans.com/up/2024-6/2024620113729773.jpg',
|
||||
name: '李四',
|
||||
nickname: '软件开发老高',
|
||||
mobile: '19136448763',
|
||||
date: '2024-10-13 10:11:01'
|
||||
},
|
||||
{
|
||||
userId: 4,
|
||||
avatar: 'https://pic.qqans.com/up/2024-6/2024620113729773.jpg',
|
||||
name: '名称',
|
||||
nickname: '软件开发老高',
|
||||
mobile: '19136448763',
|
||||
date: '2024-10-02 10:11:01'
|
||||
},{
|
||||
userId: 5,
|
||||
avatar: 'https://pic.qqans.com/up/2024-6/2024620113729773.jpg',
|
||||
name: '名称',
|
||||
nickname: '软件开发老高',
|
||||
mobile: '19136448763',
|
||||
date: '2024-10-01 10:11:01'
|
||||
},
|
||||
{
|
||||
userId: 6,
|
||||
avatar: 'https://pic.qqans.com/up/2024-6/2024620113729773.jpg',
|
||||
name: '名称',
|
||||
nickname: '软件开发老高',
|
||||
mobile: '19136448763',
|
||||
date: '2024-10-11 10:11:01'
|
||||
},{
|
||||
userId: 7,
|
||||
avatar: 'https://pic.qqans.com/up/2024-6/2024620113729773.jpg',
|
||||
name: '名称',
|
||||
nickname: '软件开发老高',
|
||||
mobile: '19136448763',
|
||||
date: '2024-10-11 10:11:01'
|
||||
},{
|
||||
userId: 8,
|
||||
avatar: 'https://pic.qqans.com/up/2024-6/2024620113729773.jpg',
|
||||
name: '名称',
|
||||
nickname: '软件开发老高',
|
||||
mobile: '19136448763',
|
||||
date: '2024-10-11 10:11:01'
|
||||
},{
|
||||
userId: 9,
|
||||
avatar: 'https://pic.qqans.com/up/2024-6/2024620113729773.jpg',
|
||||
name: '名称',
|
||||
nickname: '软件开发老高',
|
||||
mobile: '19136448763',
|
||||
date: '2024-10-11 10:11:01'
|
||||
},{
|
||||
userId: 10,
|
||||
avatar: 'https://pic.qqans.com/up/2024-6/2024620113729773.jpg',
|
||||
name: '名称',
|
||||
nickname: '软件开发老高',
|
||||
mobile: '19136448763',
|
||||
date: '2024-10-11 10:11:01'
|
||||
},
|
||||
{
|
||||
userId: 11,
|
||||
avatar: 'https://pic.qqans.com/up/2024-6/2024620113729773.jpg',
|
||||
name: '名称',
|
||||
nickname: '软件开发老高',
|
||||
mobile: '19136448763',
|
||||
date: '2024-10-11 10:11:01'
|
||||
},
|
||||
|
||||
];
|
||||