From 5d3741e95ab37d93ab256c557c65d6a39417adb3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=86=8A=E7=8C=AB?= <13581431+xiongmao1988@user.noreply.gitee.com> Date: Thu, 22 Feb 2024 20:48:08 +0800 Subject: [PATCH] =?UTF-8?q?=E5=BC=B9=E7=AA=97?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- env.d.ts | 7 + package.json | 4 +- src/api/acl/user/index.ts | 53 ++++ src/api/acl/user/type.ts | 55 ++++ src/assets/css/variable.scss | 4 +- src/components/Websocket/index.vue | 159 ++++++++++ src/components/user-info.vue | 15 +- src/main.ts | 1 + src/stores/modules/types/types.ts | 22 ++ src/stores/modules/user.ts | 112 +++++++ src/utils/request.ts | 69 ++++- src/utils/storage.ts | 78 +++++ src/utils/time.ts | 14 +- src/views/home/index.vue | 1 + src/views/index.vue | 5 +- src/views/login/login.vue | 273 +++++++++--------- src/views/logs-manage/chart/login-chart.vue | 6 +- src/views/logs-manage/logs-manage.vue | 2 +- .../permissions-manage/form/doctor-form.vue | 18 +- .../remote-manage/part/remote-dialog.vue | 1 + .../remote-manage/part/remote-item-part.vue | 10 +- src/views/remote-manage/part/remote-part.vue | 12 +- src/views/system-manage/index.vue | 1 - 23 files changed, 763 insertions(+), 159 deletions(-) create mode 100644 src/api/acl/user/index.ts create mode 100644 src/api/acl/user/type.ts create mode 100644 src/components/Websocket/index.vue create mode 100644 src/stores/modules/types/types.ts create mode 100644 src/stores/modules/user.ts create mode 100644 src/utils/storage.ts diff --git a/env.d.ts b/env.d.ts index b608774..cbdffb8 100644 --- a/env.d.ts +++ b/env.d.ts @@ -1,3 +1,10 @@ /// +//解决ts文件引入vue文件出现红色警告问题 +declare module '*.vue' { + import { defineComponent } from 'vue' + const Component: ReturnType + export default Component +} +/// declare module 'element-plus/dist/locale/zh-cn.mjs' declare module 'mockjs' \ No newline at end of file diff --git a/package.json b/package.json index e76120e..6a287bb 100644 --- a/package.json +++ b/package.json @@ -13,6 +13,7 @@ "axios": "^1.3.3", "echarts": "^5.4.1", "element-plus": "2.3.1", + "js-cookie": "^3.0.5", "pinia": "^2.1.7", "sass": "^1.58.3", "uuid": "^9.0.1", @@ -22,7 +23,8 @@ "xlsx": "^0.18.5" }, "devDependencies": { - "@types/node": "^18.11.12", + "@types/js-cookie": "^3.0.6", + "@types/node": "^18.19.17", "@types/uuid": "^9.0.8", "@vitejs/plugin-vue": "^4.0.0", "@vue/tsconfig": "^0.1.3", diff --git a/src/api/acl/user/index.ts b/src/api/acl/user/index.ts new file mode 100644 index 0000000..ce37782 --- /dev/null +++ b/src/api/acl/user/index.ts @@ -0,0 +1,53 @@ +/* +//用户管理模块的接口 +import request from '@/utils/request' +import type { + UserResponseData, + User, + AllRoleResponseData, + SetRoleData, +} from './type' +//枚举地址 +enum API { + //获取全部已有用户账号信息 + ALLUSER_URL = '/admin/acl/user/', + //添加一个新的用户账号 + ADDUSER_URL = '/admin/acl/user/save', + //更新已有的用户账号 + UPDATEUSER_URL = '/admin/acl/user/update', + //获取全部职位,当前账号拥有的职位接口 + ALLROLEURL = '/admin/acl/user/toAssign/', + //给已有的用户分配角色接口 + SETROLE_URL = '/admin/acl/user/doAssignRole', + //删除某一个账号 + DELETEUSER_URL = '/admin/acl/user/remove/', + //批量删除的接口 + DELETEALLUSER_URL = '/admin/acl/user/batchRemove', +} +//获取用户账号信息的接口 +export const reqUserInfo = (page: number, limit: number, username: string) => + request.get( + API.ALLUSER_URL + `${page}/${limit}/?username=${username}`, + ) +//添加用户与更新已有用户的接口 +export const reqAddOrUpdateUser = (data: User) => { + //携带参数有ID更新 + if (data.id) { + return request.put(API.UPDATEUSER_URL, data) + } else { + return request.post(API.ADDUSER_URL, data) + } +} +//获取全部职位以及包含当前用户的已有的职位 +export const reqAllRole = (userId: number) => + request.get(API.ALLROLEURL + userId) +//分配职位 +export const reqSetUserRole = (data: SetRoleData) => + request.post(API.SETROLE_URL, data) +//删除某一个账号的信息 +export const reqRemoveUser = (userId: number) => + request.delete(API.DELETEUSER_URL + userId) +//批量删除的接口 +export const reqSelectUser = (idList: number[]) => + request.delete(API.DELETEALLUSER_URL, { data: idList }) +*/ diff --git a/src/api/acl/user/type.ts b/src/api/acl/user/type.ts new file mode 100644 index 0000000..e5a486a --- /dev/null +++ b/src/api/acl/user/type.ts @@ -0,0 +1,55 @@ +/* +//账号信息的ts类型 +export interface ResponseData { + code: number + message: string + ok: boolean +} +//代表一个账号信息的ts类型 +export interface User { + id?: number + createTime?: string + updateTime?: string + username?: string + password?: string + name?: string + phone?: null + roleName?: string +} +//数组包含全部的用户信息 +export type Records = User[] +//获取全部用户信息接口返回的数据ts类型 +export interface UserResponseData extends ResponseData { + data: { + records: Records + total: number + size: number + current: number + pages: number + } +} + +//代表一个职位的ts类型 +export interface RoleData { + id?: number + createTime?: string + updateTime?: string + roleName: string + remark: null +} +//全部职位的列表 +export type AllRole = RoleData[] +//获取全部职位的接口返回的数据ts类型 +export interface AllRoleResponseData extends ResponseData { + data: { + assignRoles: AllRole + allRolesList: AllRole + } +} + +//给用户分配职位接口携带参数的ts类型 +export interface SetRoleData { + roleIdList: number[] + userId: number +} +*/ diff --git a/src/assets/css/variable.scss b/src/assets/css/variable.scss index 1fe7f44..e16614d 100644 --- a/src/assets/css/variable.scss +++ b/src/assets/css/variable.scss @@ -1,9 +1,9 @@ :root { - --main-color: #006080; + --main-color: #085a75; --el-color-primary: var(--main-color); --el-font-size-base: 16px; } -$main-color: #006080; +$main-color: #085a75; $red: #ea3323; $border-color: #EBEEF5; $border1-color: #E4E7ED; diff --git a/src/components/Websocket/index.vue b/src/components/Websocket/index.vue new file mode 100644 index 0000000..6b2ed61 --- /dev/null +++ b/src/components/Websocket/index.vue @@ -0,0 +1,159 @@ + + + + diff --git a/src/components/user-info.vue b/src/components/user-info.vue index 4e182ab..3c06e49 100644 --- a/src/components/user-info.vue +++ b/src/components/user-info.vue @@ -78,16 +78,23 @@ const validatorPassword = (rule: any, value: any, callback: any) => { } const validatorPhone = (rule: any, value: any, callback: any) => { - if (value.length == 11) { - callback(); + var isPhone = /^1(3\d|4[5-9]|5[0-35-9]|6[2567]|7[0-8]|8\d|9[0-35-9])\d{8}$/; + + + if (value.indexOf('****') >= 0) { + return callback().trim(); + } + + if (!isPhone.test(value)) { + callback(new Error('请输入合法手机号')); } else { - callback(new Error('手机号码长度为11位')); + callback(); } } const rules = reactive({ phone: [ - { required: true, validator:validatorPhone, trigger: 'change' }, + { required: true, validator:validatorPhone, trigger: 'blur' }, ], name: [ { required: true, message: '请输入姓名', trigger: 'blur' }, diff --git a/src/main.ts b/src/main.ts index c003204..12d5d89 100644 --- a/src/main.ts +++ b/src/main.ts @@ -8,6 +8,7 @@ import * as ElementPlusIconsVue from '@element-plus/icons-vue' import { ElDialog } from 'element-plus' import TableAbility from '@/components/table-ability.vue' + import 'element-plus/dist/index.css'; import './assets/css/global.scss'; import './assets/font/iconfont.css'; diff --git a/src/stores/modules/types/types.ts b/src/stores/modules/types/types.ts new file mode 100644 index 0000000..be0da0f --- /dev/null +++ b/src/stores/modules/types/types.ts @@ -0,0 +1,22 @@ +/* +import type { RouteRecordRaw } from 'vue-router' +import type { CategoryObj } from '@/api/product/attr/type' +//定义小仓库数据state类型 +export interface UserState { + token: string | null + menuRoutes: RouteRecordRaw[] + username: string + avatar: string + buttons: string[] +} + +//定义分类仓库state对象的ts类型 +export interface CategoryState { + c1Id: string | number + c1Arr: CategoryObj[] + c2Arr: CategoryObj[] + c2Id: string | number + c3Arr: CategoryObj[] + c3Id: string | number +} +*/ diff --git a/src/stores/modules/user.ts b/src/stores/modules/user.ts new file mode 100644 index 0000000..16c4c17 --- /dev/null +++ b/src/stores/modules/user.ts @@ -0,0 +1,112 @@ +/* +//创建用户相关的小仓库 +import { defineStore } from 'pinia' +//引入接口 +import { reqLogin, reqUserInfo, reqLogout } from '@/api/user' +import type { + loginFormData, + loginResponseData, + userInfoReponseData, +} from '@/api/user/type' +import type { UserState } from './types/type' +//引入操作本地存储的工具方法 +import { SET_TOKEN, GET_TOKEN, REMOVE_TOKEN } from '@/utils/token' +//引入路由(常量路由) +import { constantRoute, asnycRoute, anyRoute } from '@/router/routes' + +//引入深拷贝方法 +//@ts-expect-error +import cloneDeep from 'lodash/cloneDeep' +import router from '@/router' +//用于过滤当前用户需要展示的异步路由 +function filterAsyncRoute(asnycRoute: any, routes: any) { + return asnycRoute.filter((item: any) => { + if (routes.includes(item.name)) { + if (item.children && item.children.length > 0) { + //硅谷333账号:product\trademark\attr\sku + item.children = filterAsyncRoute(item.children, routes) + } + return true + } + }) +} + +//创建用户小仓库 +const useUserStore = defineStore('User', { + //小仓库存储数据地方 + state: (): UserState => { + return { + token: GET_TOKEN(), //用户唯一标识token + menuRoutes: constantRoute, //仓库存储生成菜单需要数组(路由) + username: '', + avatar: '', + //存储当前用户是否包含某一个按钮 + buttons: [], + } + }, + //异步|逻辑的地方 + actions: { + //用户登录的方法 + async userLogin(data: loginFormData) { + //登录请求 + const result: loginResponseData = await reqLogin(data) + //登录请求:成功200->token + //登录请求:失败201->登录失败错误的信息 + if (result.code == 200) { + //pinia仓库存储一下token + //由于pinia|vuex存储数据其实利用js对象 + this.token = result.data as string + //本地存储持久化存储一份 + SET_TOKEN(result.data as string) + //能保证当前async函数返回一个成功的promise + return 'ok' + } else { + return Promise.reject(new Error(result.data)) + } + }, + //获取用户信息方法 + async userInfo() { + //获取用户信息进行存储仓库当中[用户头像、名字] + const result: userInfoReponseData = await reqUserInfo() + //如果获取用户信息成功,存储一下用户信息 + if (result.code == 200) { + this.username = result.data.name + this.avatar = result.data.avatar + this.buttons = result.data.buttons + //计算当前用户需要展示的异步路由 + const userAsyncRoute = filterAsyncRoute( + cloneDeep(asnycRoute), + result.data.routes, + ) + //菜单需要的数据整理完毕 + this.menuRoutes = [...constantRoute, ...userAsyncRoute, anyRoute] + //目前路由器管理的只有常量路由:用户计算完毕异步路由、任意路由动态追加 + ;[...userAsyncRoute, anyRoute].forEach((route: any) => { + router.addRoute(route) + }) + return 'ok' + } else { + return Promise.reject(new Error(result.message)) + } + }, + //退出登录 + async userLogout() { + //退出登录请求 + const result: any = await reqLogout() + if (result.code == 200) { + //目前没有mock接口:退出登录接口(通知服务器本地用户唯一标识失效) + this.token = '' + this.username = '' + this.avatar = '' + REMOVE_TOKEN() + return 'ok' + } else { + return Promise.reject(new Error(result.message)) + } + }, + }, + getters: {}, +}) +//对外暴露获取小仓库方法 +export default useUserStore +*/ diff --git a/src/utils/request.ts b/src/utils/request.ts index 983fab6..41ae871 100644 --- a/src/utils/request.ts +++ b/src/utils/request.ts @@ -1,4 +1,10 @@ +//进行axios二次封装:使用请求与响应拦截器 import axios from "axios"; +/* +import { ElMessage } from 'element-plus' +//引入用户相关的仓库 +import useUserStore from '@/stores/modules/user' +*/ export const HOST = 'http://localhost:9999'; const BASE_URL = import.meta.env.BASE_URL @@ -29,4 +35,65 @@ export const getData = (url: string, params?: any) => { } export const postData = (url: string, params?: any) => { return axios.post(url, params) -} \ No newline at end of file +} + + +/* +//第一步:利用axios对象的create方法,去创建axios实例(其他的配置:基础路径、超时的时间) +const request = axios.create({ + //基础路径 + baseURL: import.meta.env.VITE_APP_BASE_API, //基础路径上会携带/api + timeout: 5000, //超时的时间的设置 +}) +//第二步:request实例添加请求与响应拦截器 +request.interceptors.request.use((config) => { + //获取用户相关的小仓库:获取仓库内部token,登录成功以后携带给服务器 + const userStore = useUserStore() + if (userStore.token) { + config.headers.token = userStore.token + } + //config配置对象,headers属性请求头,经常给服务器端携带公共参数 + //返回配置对象 + return config +}) + +//第三步:响应拦截器 +request.interceptors.response.use( + (response) => { + //成功回调 + //简化数据 + return response.data + }, + (error) => { + //失败回调:处理http网络错误的 + //定义一个变量:存储网络错误信息 + let message = '' + //http状态码 + const status = error.response.status + switch (status) { + case 401: + message = 'TOKEN过期' + break + case 403: + message = '无权访问' + break + case 404: + message = '请求地址错误' + break + case 500: + message = '服务器出现问题' + break + default: + message = '网络出现问题' + break + } + //提示错误信息 + ElMessage({ + type: 'error', + message, + }) + return Promise.reject(error) + }, +) +//对外暴露 +export default request*/ diff --git a/src/utils/storage.ts b/src/utils/storage.ts new file mode 100644 index 0000000..8aebe3a --- /dev/null +++ b/src/utils/storage.ts @@ -0,0 +1,78 @@ +/* +import Cookies from 'js-cookie'; + +/!** + * window.localStorage 浏览器永久缓存 + * @method set 设置永久缓存 + * @method get 获取永久缓存 + * @method remove 移除永久缓存 + * @method clear 移除全部永久缓存 + *!/ +export const Local = { + // 查看 v2.4.3版本更新日志 + setKey(key: string) { + // @ts-ignore + return `${__NEXT_NAME__}:${key}`; + }, + // 设置永久缓存 + set(key: string, val: T) { + window.localStorage.setItem(Local.setKey(key), JSON.stringify(val)); + }, + // 获取永久缓存 + get(key: string) { + let json = window.localStorage.getItem(Local.setKey(key)); + return JSON.parse(json); + }, + // 移除永久缓存 + remove(key: string) { + window.localStorage.removeItem(Local.setKey(key)); + }, + // 移除全部永久缓存 + clear() { + window.localStorage.clear(); + }, +}; + +/!** + * window.sessionStorage 浏览器临时缓存 + * @method set 设置临时缓存 + * @method get 获取临时缓存 + * @method remove 移除临时缓存 + * @method clear 移除全部临时缓存 + *!/ +export const Session = { + // 设置临时缓存 + set(key: string, val: any) { + if (key === 'token' || key === 'refresh_token') { + Cookies.set(key, val); + } + window.sessionStorage.setItem(key, JSON.stringify(val)); + }, + // 获取临时缓存 + get(key: string) { + if (key === 'token' || key === 'refresh_token') return Cookies.get(key); + let json = window.sessionStorage.getItem(key); + return JSON.parse(json); + }, + // 移除临时缓存 + remove(key: string) { + if (key === 'token' || key === 'refresh_token') return Cookies.remove(key); + window.sessionStorage.removeItem(key); + }, + // 移除全部临时缓存 + clear() { + Cookies.remove('token'); + Cookies.remove('refresh_token'); + Cookies.remove('tenantId'); + window.sessionStorage.clear(); + }, + // 获取当前存储的 token + getToken() { + return this.get('token'); + }, + // 获取当前的租户 + getTenant() { + return Local.get('tenantId') ? Local.get('tenantId') : 1; + }, +}; +*/ diff --git a/src/utils/time.ts b/src/utils/time.ts index f29506f..9135352 100644 --- a/src/utils/time.ts +++ b/src/utils/time.ts @@ -4,14 +4,20 @@ export const getTime = () => { //通过内置构造函数Date const hours = new Date().getHours() //情况的判断 - if (hours <= 9) { + if (hours < 9) { message = '早上' - } else if (hours <= 12) { + } else if (hours < 12) { message = '上午' - } else if (hours <= 18) { + } else if (hours < 14) { + message = '中午' + } else if (hours < 17) { message = '下午' - } else { + } else if (hours < 19) { + message = '傍晚' + } else if (hours < 22) { message = '晚上' + } else { + message = '夜里' } return message } diff --git a/src/views/home/index.vue b/src/views/home/index.vue index 8d0aa26..400e49b 100644 --- a/src/views/home/index.vue +++ b/src/views/home/index.vue @@ -68,6 +68,7 @@ import TimeBarChart from "./time-bar-chart.vue"; import WeekCalendar from "./week-calendar.vue"; import SystemLogs from "@/components/system-logs.vue"; + const router = useRouter() const userInfo = useLoginStore().getlogin() diff --git a/src/views/index.vue b/src/views/index.vue index 17af06f..6651388 100644 --- a/src/views/index.vue +++ b/src/views/index.vue @@ -207,15 +207,16 @@ function fn1(res:any) { align-items: center; flex-shrink: 0; + .menu-item { cursor: pointer; display: flex; align-items: center; - font-size: 17px; + font-size: 20px; font-weight: 600; color: $main-color; height: 70px; - padding: 0 10px; + padding: 0 15px; margin: 0; transition: all .5s; -webkit-transition: all .5s; diff --git a/src/views/login/login.vue b/src/views/login/login.vue index 5347df2..7b8b8dd 100644 --- a/src/views/login/login.vue +++ b/src/views/login/login.vue @@ -8,114 +8,114 @@ - - - - 密码登录 - 验证码登录 - - - - - - - - - - - - - - - - - - - - - - 登录 - 注册账号 - - - - - - - - {{ loginParams.phoneArea }} - - - - - {{ item - }} - - - - - - - - - - - {{ loginParams.sendText }} - - - - 登录 + + + + 密码登录 + 验证码登录 + + + + + + + + + + + + + + + + + + + + + + 登录 + 注册账号 + + + + + + + + {{ loginParams.phoneArea }} + + + + + {{ item + }} + + + + + + + + + + + {{ loginParams.sendText }} + + + + 登录 + + + + + + 新用户申请 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 看不清? + + + + - - - - - 新用户申请 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 看不清? - - - - - -