Commit 4fce377a by wenyi.chen

Initial commit

parents
# port 端口号
VITE_PORT = 8888
# open 运行 npm run dev 时自动打开浏览器
VITE_OPEN = false
# public path 配置线上环境路径(打包)、本地通过 http-server 访问时,请置空即可
VITE_PUBLIC_PATH = ./
# X-OPENAPI-TOKEN
VITE_X_OPENAPI_TOKEN = 098f6bcd4621d373cade4e832627b4f6
\ No newline at end of file
# 本地环境
ENV = 'development'
# 本地环境接口地址
VITE_API_URL = 'http://localhost:8888/'
\ No newline at end of file
# 线上环境
ENV = 'production'
# 线上环境接口地址
VITE_API_URL = '/element-plus-api_v1'
\ No newline at end of file
*.sh
node_modules
lib
*.md
*.scss
*.woff
*.ttf
.vscode
.idea
dist
mock
public
bin
build
config
index.html
src/assets
\ No newline at end of file
module.exports = {
root: true,
env: {
browser: true,
es2021: true,
node: true,
},
parser: 'vue-eslint-parser',
parserOptions: {
ecmaVersion: 12,
parser: '@typescript-eslint/parser',
sourceType: 'module',
},
extends: ['plugin:vue/vue3-essential', 'plugin:vue/essential', 'eslint:recommended'],
plugins: ['vue', '@typescript-eslint'],
overrides: [
{
files: ['*.ts', '*.tsx', '*.vue'],
rules: {
'no-undef': 'off',
},
},
],
rules: {
// http://eslint.cn/docs/rules/
// https://eslint.vuejs.org/rules/
// https://typescript-eslint.io/rules/no-unused-vars/
'@typescript-eslint/ban-ts-ignore': 'off',
'@typescript-eslint/explicit-function-return-type': 'off',
'@typescript-eslint/no-explicit-any': 'off',
'@typescript-eslint/no-var-requires': 'off',
'@typescript-eslint/no-empty-function': 'off',
'@typescript-eslint/no-use-before-define': 'off',
'@typescript-eslint/ban-ts-comment': 'off',
'@typescript-eslint/ban-types': 'off',
'@typescript-eslint/no-non-null-assertion': 'off',
'@typescript-eslint/explicit-module-boundary-types': 'off',
'@typescript-eslint/no-redeclare': 'error',
'@typescript-eslint/no-non-null-asserted-optional-chain': 'off',
'@typescript-eslint/no-unused-vars': [2],
'vue/custom-event-name-casing': 'off',
'vue/attributes-order': 'off',
'vue/one-component-per-file': 'off',
'vue/html-closing-bracket-newline': 'off',
'vue/max-attributes-per-line': 'off',
'vue/multiline-html-element-content-newline': 'off',
'vue/singleline-html-element-content-newline': 'off',
'vue/attribute-hyphenation': 'off',
'vue/html-self-closing': 'off',
'vue/no-multiple-template-root': 'off',
'vue/require-default-prop': 'off',
'vue/no-v-model-argument': 'off',
'vue/no-arrow-functions-in-watch': 'off',
'vue/no-template-key': 'off',
'vue/no-v-html': 'off',
'vue/comment-directive': 'off',
'vue/no-parsing-error': 'off',
'vue/no-deprecated-v-on-native-modifier': 'off',
'vue/multi-word-component-names': 'off',
'no-useless-escape': 'off',
'no-sparse-arrays': 'off',
'no-prototype-builtins': 'off',
'no-constant-condition': 'off',
'no-use-before-define': 'off',
'no-restricted-globals': 'off',
'no-restricted-syntax': 'off',
'generator-star-spacing': 'off',
'no-unreachable': 'off',
'no-multiple-template-root': 'off',
'no-unused-vars': 'error',
'no-v-model-argument': 'off',
'no-case-declarations': 'off',
'no-console': 'error',
'no-redeclare': 'off',
},
};
.DS_Store
node_modules
/dist
# local env files
.env.local
.env.*.local
# Log files
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
# Editor directories and files
.idea
.vscode
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw?
module.exports = {
// 一行最多多少个字符
printWidth: 150,
// 指定每个缩进级别的空格数
tabWidth: 2,
// 使用制表符而不是空格缩进行
useTabs: true,
// 在语句末尾打印分号
semi: true,
// 使用单引号而不是双引号
singleQuote: true,
// 更改引用对象属性的时间 可选值"<as-needed|consistent|preserve>"
quoteProps: 'as-needed',
// 在JSX中使用单引号而不是双引号
jsxSingleQuote: false,
// 多行时尽可能打印尾随逗号。(例如,单行数组永远不会出现逗号结尾。) 可选值"<none|es5|all>",默认none
trailingComma: 'es5',
// 在对象文字中的括号之间打印空格
bracketSpacing: true,
// jsx 标签的反尖括号需要换行
jsxBracketSameLine: false,
// 在单独的箭头函数参数周围包括括号 always:(x) => x \ avoid:x => x
arrowParens: 'always',
// 这两个选项可用于格式化以给定字符偏移量(分别包括和不包括)开始和结束的代码
rangeStart: 0,
rangeEnd: Infinity,
// 指定要使用的解析器,不需要写文件开头的 @prettier
requirePragma: false,
// 不需要自动在文件开头插入 @prettier
insertPragma: false,
// 使用默认的折行标准 always\never\preserve
proseWrap: 'preserve',
// 指定HTML文件的全局空格敏感度 css\strict\ignore
htmlWhitespaceSensitivity: 'css',
// Vue文件脚本和样式标签缩进
vueIndentScriptAndStyle: false,
// 换行符使用 lf 结尾是 可选值"<auto|lf|crlf|cr>"
endOfLine: 'lf',
};
MIT License
Copyright (c) 2021
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
\ No newline at end of file
#### 🚧 安装 cnpm、yarn
- 复制代码(桌面 cmd 运行) `npm install -g cnpm --registry=https://registry.npm.taobao.org`
- 复制代码(桌面 cmd 运行) `npm install -g yarn`
#### 🏭 环境支持
| Edge | Firefox | Chrome | Safari |
| --------- | ------------ | ----------- | ----------- |
| Edge ≥ 88 | Firefox ≥ 78 | Chrome ≥ 87 | Safari ≥ 13 |
> 由于 Vue3 不再支持 IE11,故而 ElementPlus 也不支持 IE11 及之前版本。
#### ⚡ 使用说明
建议使用 cnpm,因为 yarn 有时会报错。<a href="http://nodejs.cn/" target="_blank">node 版本 > 14.18+/16+</a>
> Vite 不再支持 Node 12 / 13 / 15,因为上述版本已经进入了 EOL 阶段。现在你必须使用 Node 14.18+ / 16+ 版本。
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="utf-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta
name="keywords"
content="企业产品库数据信息管理系统"
/>
<meta
name="description"
content="企业产品库数据信息管理系统"
/>
<link rel="icon" href="/favicon.ico" />
<title>企业产品库数据信息管理系统</title>
</head>
<body>
<div id="app"></div>
<script type="module" src="/src/main.js"></script>
</body>
</html>
{
"compilerOptions": {
"target": "esnext",
"module": "esnext",
"lib": ["esnext", "dom", "dom.iterable", "scripthost"],
"jsx": "preserve",
"isolatedModules": true,
"strict": true,
"allowSyntheticDefaultImports": true,
"esModuleInterop": true,
"experimentalDecorators": true,
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true,
"moduleResolution": "node",
"baseUrl": ".",
"types": ["vite/client"],
"paths": {
"/@/*": ["src/*"]
},
"allowJs": true
}
}
This source diff could not be displayed because it is too large. You can view the blob instead.
{
"name": "dsis",
"version": "1.0.0",
"description": "vue3 vite next admin template js setup",
"author": "gy",
"license": "MIT",
"scripts": {
"dev": "vite --force",
"build": "vite build",
"lint-fix": "eslint --fix --ext .js --ext .jsx --ext .vue src/"
},
"dependencies": {
"@element-plus/icons-vue": "^2.0.10",
"@wangeditor/editor": "^5.1.23",
"@wangeditor/editor-for-vue": "^5.1.12",
"axios": "^1.2.1",
"dayjs": "^1.11.10",
"default-passive-events": "^2.0.0",
"echarts": "^5.5.0",
"element-plus": "2.4.3",
"js-cookie": "^3.0.1",
"lodash": "^4.17.21",
"mitt": "^3.0.0",
"nprogress": "^0.2.0",
"pinia": "^2.0.28",
"qrcodejs2-fixes": "^0.0.2",
"qs": "^6.11.0",
"screenfull": "^6.0.2",
"sortablejs": "^1.15.0",
"vue": "^3.2.45",
"vue-clipboard3": "^2.0.0",
"vue-router": "^4.1.6",
"vue3-scale-box": "^0.1.9"
},
"devDependencies": {
"@vitejs/plugin-legacy": "^4.0.4",
"@vitejs/plugin-vue": "^4.0.0",
"@vue/compiler-sfc": "^3.2.45",
"eslint": "^8.29.0",
"eslint-plugin-vue": "^9.8.0",
"prettier": "^2.8.1",
"sass": "^1.56.2",
"unplugin-auto-import": "^0.12.0",
"vite": "^4.0.0",
"vite-plugin-vue-setup-extend": "^0.4.0",
"vue-eslint-parser": "^9.1.0"
},
"browserslist": [
"> 1%",
"last 2 versions",
"not dead"
],
"bugs": {
"url": "https://gitee.com/lyt-top/vue-next-admin/issues"
},
"engines": {
"node": ">=16.0.0",
"npm": ">= 7.0.0"
},
"keywords": [
"vue",
"vue3",
"vuejs/vue-next",
"vuejs/vue-next-template",
"vuejs/vue-next-template-js",
"element-ui",
"element-plus",
"vue-next-admin",
"next-admin"
],
"repository": {
"type": "git",
"url": "https://gitee.com/lyt-top/vue-next-admin.git"
}
}
<template>
<el-config-provider :size="getGlobalComponentSize" :locale="zhCn">
<router-view v-show="themeConfig.lockScreenTime > 1" />
<!-- <LockScreen v-if="themeConfig.isLockScreen" /> -->
<Setings ref="setingsRef" v-show="themeConfig.lockScreenTime > 1" />
<CloseFull v-if="!themeConfig.isLockScreen" />
<!-- <Upgrade v-if="getVersion" /> -->
</el-config-provider>
</template>
<script setup name="app">
import zhCn from 'element-plus/es/locale/lang/zh-cn';
import { storeToRefs } from 'pinia';
import { useTagsViewRoutes } from '/@/stores/tagsViewRoutes';
import { useThemeConfig } from '/@/stores/themeConfig';
import other from '/@/utils/other';
import { Local, Session } from '/@/utils/storage';
import setIntroduction from '/@/utils/setIconfont';
import mittBus from './utils/mitt';
import { useUserInfo } from '/@/stores/userInfo';
// 引入组件
// const LockScreen = defineAsyncComponent(() => import('/@/layout/lockScreen/index.vue'));
const Setings = defineAsyncComponent(() => import('/@/layout/navBars/breadcrumb/setings.vue'));
const CloseFull = defineAsyncComponent(() => import('/@/layout/navBars/breadcrumb/closeFull.vue'));
const Upgrade = defineAsyncComponent(() => import('/@/layout/upgrade/index.vue'));
// 定义变量内容
const setingsRef = ref();
const route = useRoute();
const stores = useTagsViewRoutes();
const storesThemeConfig = useThemeConfig();
const { themeConfig } = storeToRefs(storesThemeConfig);
// 获取版本号
// const getVersion = computed(() => {
// let isVersion = false;
// if (route.path !== '/login') {
// if ((Local.get('version') && Local.get('version') !== __VERSION__) || !Local.get('version')) isVersion = true;
// }
// return isVersion;
// });
// 获取全局组件大小
const getGlobalComponentSize = computed(() => {
return other.globalComponentSize();
});
// 设置初始化,防止刷新时恢复默认
onBeforeMount(() => {
// 设置批量第三方 icon 图标
// setIntroduction.cssCdn();
// 设置批量第三方 js
setIntroduction.jsCdn();
});
// 页面加载时
onMounted(() => {
useUserInfo().setVersion();
nextTick(() => {
// 监听布局配置弹窗点击打开
mittBus.on('openSetingsDrawer', () => {
setingsRef.value.openDrawer();
});
// 获取缓存中的布局配置
if (Local.get('themeConfig')) {
storesThemeConfig.setThemeConfig({ themeConfig: Local.get('themeConfig') });
document.documentElement.style.cssText = Local.get('themeConfigStyle');
}
// 获取缓存中的全屏配置
if (Session.get('isTagsViewCurrenFull')) {
stores.setCurrenFullscreen(Session.get('isTagsViewCurrenFull'));
}
});
});
// 页面销毁时,关闭监听布局配置/i18n监听
onUnmounted(() => {
mittBus.off('openSetingsDrawer', () => {});
});
// 监听路由的变化,设置网站标题
watch(
() => route.path,
() => {
other.useTitle();
},
{
deep: true,
}
);
</script>
import request from '/@/utils/request';
/**
*
* 质量评估
*/
export function assessmentApi() {
return {
getAssessment: (params) => {
return request({
url: '/quality/assessment/dashboard',
method: 'get',
params: params,
})
},
getMedicalDetail: (params) => {
return request({
url: '/quality/medical/resource',
method: 'get',
params: params,
})
},
getDataCount1(params) {
return request({
url: '/count/dataCount1',
method: 'get',
params: params,
})
},
getDataCount2(params) {
return request({
url: '/count/dataCount2',
method: 'get',
params: params,
})
},
getDataCount3(params) {
return request({
url: '/count/dataCount3',
method: 'get',
params: params,
})
},
getDataCount4(params) {
return request({
url: '/count/dataCount4',
method: 'get',
params: params,
})
},
getDataCount5(params) {
return request({
url: '/count/dataCount5',
method: 'get',
params: params,
})
},
}
}
import request from '/@/utils/request';
/**
*
* 数据仪表盘接口集合
* @method noticeConfig 通知配置
* @method noticeList 通知列表
* @method sendPatient 发送通知给患者
* @method sendDoctor 发送通知给医生
*/
export function dashbordApi() {
return {
getDashbord: (params) => {
return request({
url: '/statistics/dashboard',
method: 'get',
params:params,
});
},
getWorkload: (params) => {
return request({
url: '/statistics/workload/get',
method: 'get',
params:params,
});
}
}
}
import request from '/@/utils/request';
/**
*
* 中心数据
*/
export function prebookApi() {
return {
getPreBook: (params) => {
return request({
url: '/statistics/prebook/get',
method: 'get',
params:params,
});
},
getPreBookFinsih: (params) => {
return request({
url: '/statistics/finished/get',
method: 'get',
params:params,
});
},
getPreBookRatio: (params) => {
return request({
url: '/statistics/daytime/ratio/get',
method: 'get',
params:params,
});
},
}
}
import request from '/@/utils/request';
/**
* (不建议写成 request.post(xxx),因为这样 post 时,无法 params 与 data 同时传参)
*
* 任务相关api接口集合
* @method taskPackageList 任务包列表
* @method taskPackageAdd 发布任务
*/
export function departmentApi() {
return {
departmentList: (data) => {
return request({
url: '/api/department/list',
method: 'post',
data,
});
},
departmentAdd: (data) => {
return request({
url: '/api/department/add',
method: 'post',
data,
});
},
departmentDelete: (data) => {
return request({
url: '/api/department/delete',
method: 'post',
data,
});
},
};
}
import request from '/@/utils/request';
/**
* (不建议写成 request.post(xxx),因为这样 post 时,无法 params 与 data 同时传参)
*
* 任务相关api接口集合
* @method taskPackageList 任务包列表
* @method taskPackageAdd 发布任务
*/
export function enterpriseApi() {
return {
enterpriseList: (data) => {
return request({
url: '/api/enterpriseInfo/list',
method: 'post',
data,
});
},
enterpriseName: (data) => {
return request({
url: '/api/enterpriseInfo/getNameInfo',
method: 'post',
data,
});
},
enterpriseAdd: (data) => {
return request({
url: '/api/enterpriseInfo/add',
method: 'post',
data,
});
},
enterpriseDelete: (data) => {
return request({
url: '/api/enterpriseInfo/delete',
method: 'post',
data,
});
},
enterpriseSave: (data) => {
return request({
url: '/api/enterpriseInfo/save',
method: 'post',
data,
});
},
fileStatus: () => {
return request({
url: '/api/enterprise/fileStatus',
method: 'get',
});
},
};
}
import request from '/@/utils/request';
/**
* (不建议写成 request.post(xxx),因为这样 post 时,无法 params 与 data 同时传参)
*
* 登录api接口集合
* @method signIn 用户登录
* @method signOut 用户退出登录
* @method getUserInfoByToken 根据token获取用户信息
*/
export function useLoginApi() {
return {
signIn: (data) => {
return request({
url: '/api/auth/login',
method: 'post',
data,
});
},
signOut: (data) => {
return request({
url: '/api/logout',
method: 'post',
data,
});
},
getUserInfoByToken: (data) => {
return request({
url: '/api/personalInformation',
method: 'get',
data,
});
},
};
}
import request from '/@/utils/request';
/**
*
* (不建议写成 request.post(xxx),因为这样 post 时,无法 params 与 data 同时传参)
* 后端控制路由,isRequestRoutes 为 true,则开启后端控制路由
* @method getMenu 获取后端动态路由菜单
*/
export function useMenuApi() {
return {
getMenu:() => {
return request({
url: '/api/menu/getMenuInfo',
method: 'get',
});
},
menuAllMenus:() => {
return request({
url: '/api/menu/allMenus',
method: 'get',
});
},
menuAdd: (data) => {
return request({
url: '/api/menu/add',
method: 'post',
data,
});
},
menuEdit: (data) => {
return request({
url: '/api/menu/edit',
method: 'post',
data,
});
},
menuDele: (data) => {
return request({
url: '/api/menu/delete',
method: 'post',
data,
});
},
};
}
import request from '/@/utils/request';
/**
* (不建议写成 request.post(xxx),因为这样 post 时,无法 params 与 data 同时传参)
*
* 任务相关api接口集合
* @method taskPackageList 任务包列表
* @method taskPackageAdd 发布任务
*/
export function productApi() {
return {
list: (data) => {
return request({
url: '/api/product/list',
method: 'post',
data,
});
},
apiAdd: (data) => {
return request({
url: '/api/product/add',
method: 'post',
data,
});
},
apiDelete: (data) => {
return request({
url: '/api/product/delete',
method: 'post',
data,
});
},
apiSave: (data) => {
return request({
url: '/api/product/save',
method: 'post',
data,
});
},
};
}
import request from '/@/utils/request';
/**
*
* 随访记录
*/
export function followApi() {
return {
getFollowTableData: (params) => {
return request({
url: '/followup/get',
method: 'get',
params: params,
});
},
getFollowDetail: (params) => {
return request({
url: '/followup/detail',
method: 'get',
params: params,
});
}
}
}
import request from '/@/utils/request';
/**
*
* 随访记录
*/
export function RecordLogApi() {
return {
getTableData: (params) => {
return request({
url: '/followup/record/get',
method: 'get',
params: params,
});
},
getPatientAiDetail: (params) => {
return request({
url: '/followup/record/detail/get',
method: 'get',
params: params,
});
},
getPatientArtificialDetail: (params) => {
return request({
url: '/followup/record/detail/manual/list',
method: 'get',
params: params,
});
},
getPatientArtificialDetailById: (params) => {
return request({
url: '/followup/record/detail/manual/get',
method: 'get',
params: params,
});
},
getPatientArtificialDetailSeparateByRecordId: (params) => {
return request({
url: '/followup/record/detail/manual/separate',
method: 'get',
params: params,
});
},
getPatientSignConfig: (params) => {
return request({
url: '/followup/patientSignConfig',
method: 'get',
params: params,
});
},
setPatientSign: (data) => {
return request({
url: '/followup/setPatientSign',
method: 'post',
data
});
},
getPatientSign: (data) => {
return request({
url: '/followup/getPatientSign',
method: 'post',
data
});
},
};
}
import request from '/@/utils/request';
/**
*
* 随访数据总览
*/
export function statisticsApi() {
return {
getStatistics: (params) => {
return request({
url: '/followup/statistics/get',
method: 'get',
params:params,
});
},
}
}
import request from '/@/utils/request';
/**
* (不建议写成 request.post(xxx),因为这样 post 时,无法 params 与 data 同时传参)
*
* 系统设置api接口集合
* @method systemUserList 系统用户列表
* @method resetPassword 重置密码
* @method changeStatus 状态变更
* @method changeUserRole 修改用户权限
* @method rolePermissions 绑定角色菜单
* @method addUser 新增职工
* @method editUser 修改职工
*/
export function systemApi() {
return {
systemUserList: (data) => {
return request({
url: '/api/admin/list',
method: 'post',
data,
});
},
resetPassword: (data) => {
return request({
url: '/api/admin/resetPassword',
method: 'post',
data,
});
},
changeStatus: (data) => {
return request({
url: '/api/admin/changeStatus',
method: 'post',
data,
});
},
changeUserRole: (data) => {
return request({
url: '/user/user_role',
method: 'post',
data
});
},
simpleRoleList: () => {
return request({
url: '/role/simple_list',
method: 'get',
});
},
roleList: (data) => {
return request({
url: '/api/menu/allRole',
method: 'post',
data
});
},
rolePermissions: (data) => {
return request({
url: '/api/menu/updateSingleRole',
method: 'post',
data
});
},
addUser: (data) => {
return request({
url: '/api/admin/add',
method: 'post',
data
});
},
editUser: (data) => {
return request({
url: '/api/admin/save',
method: 'post',
data
});
},
editPassword: (data) => {
return request({
url: '/api/user/changePassword',
method: 'post',
data
});
},
bedList: (params) => {
return request({
url: '/bed/list',
method: 'get',
params:params
});
},
disableBed: (data) => {
return request({
url: '/bed/disable',
method: 'post',
data
});
},
enableBed: (data) => {
return request({
url: '/bed/enable',
method: 'post',
data
});
},
hideBed: (data) => {
return request({
url: '/bed/hidden',
method: 'post',
data
});
},
showBed: (data) => {
return request({
url: '/bed/display',
method: 'post',
data
});
},
};
}
import request from '/@/utils/request';
/**
*
* toolsapi接口集合
* @method getVersion 获取服务端版本号
* @method departmentList 科室下拉
* @method patientStatus 患者就诊状态
* @method operationRooms 手术室
* @method hospitalArea 病区
* @method hospitalBeds 获取床位带病人
* @method patientDischargeProblem 获取退出日间问题
* @method doctorList 医生下拉
* @method diagnosisList 诊断下拉
* @method operationList 手术下拉
* @method anesthesiaModeList 麻醉方式下拉
* @method getNoticePatientTemplate 通知患者模板
* @method anesthesiaModeList 通知医生模板
* @method getAnesthesiaDoctorList 麻醉医生列表
*/
export function toolsApi() {
return {
getVersion: () => {
return request({
url: '/api/system/version',
method: 'get',
});
},
departmentList: (params) => {
return request({
url: '/api/department/list',
method: 'get',
params: params
});
},
outDepartmentList: (params) => {
return request({
url: '/tools/outpatient_departments',
method: 'get',
params: params
});
},
patientStatus: () => {
return request({
url: '/tools/patient_status',
method: 'get',
});
},
operationRooms: (params) => {
return request({
url: '/tools/operation_rooms',
method: 'get',
params: params
});
},
hospitalArea: () => {
return request({
url: '/api/system/hospital_area',
method: 'get',
});
},
hospitalBeds: (params) => {
return request({
url: '/tools/get_hospital_bed_in_patient',
method: 'get',
params: params
});
},
patientDischargeProblem: () => {
return request({
url: '/tools/get_patient_discharge_problem',
method: 'get',
});
},
menuTreeList: (data) => {
return request({
url: '/api/menu/singleRole',
method: 'get',
params: data
});
},
doctorList: (data) => {
return request({
url: '/tools/doctor',
method: 'get',
params: data
});
},
diagnosisList: () => {
return request({
url: '/tools/get_diagnosis_list',
method: 'get',
});
},
operationList: (data) => {
return request({
url: '/tools/operation',
method: 'get',
params: data
});
},
anesthesiaModeList: () => {
return request({
url: '/tools/get_anesthesia_mode',
method: 'get',
});
},
getNoticePatientTemplate: (params) => {
return request({
url: '/tools/gen_patient_notice_content',
method: 'get',
params: params
});
},
getNoticeDoctorTemplate: (params) => {
return request({
url: '/tools/gen_doctor_notice_content',
method: 'get',
params: params
});
},
getAnesthesiaDoctorList: (data) => {
return request({
url: '/tools/getAnesthesiologist',
method: 'get',
params: data,
});
},
getPatientStatusList: (data) => {
return request({
url: '/tools/getPatientStatus',
method: 'post',
data,
});
},
};
}
<svg width="100" height="969"
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink" shape-rendering="auto" preserveAspectRatio="none" class="layout-footer-waves">
<g id="Layer_1">
<title>Layer 1</title>
<g transform="rotate(2.18686 -4.09974 506.2)" stroke="null" id="svg_5">
<defs stroke="null" transform="translate(0.00121616 0.0635973) translate(-0.844727 0.185777) translate(-4.69751 0.0674522) translate(4.80739 0.470252) translate(4.47211 0.0666505) translate(-47.1425 4.28569) translate(7.74305 4.90188) translate(-28.5712 -22.857) translate(-0.219291 4.06319) translate(-1.32311 -4.516) translate(22.5706 18.2109) translate(-49.4342 -5.96675) translate(-0.909089 0) translate(0.909089 0) translate(-0.909089 0) translate(-0.909089 0) translate(-0.909089 0) translate(-3.33335 0) translate(0 -3.33335) translate(0 -3.33335) translate(0 -3.33335) translate(0 -3.33335) translate(0 -3.33335) translate(0 -3.33335) translate(0 -3.33335) translate(0 -3.33335) translate(0 -3.33335) translate(0 -3.33335) translate(0 -3.33335) translate(0 -3.33335) translate(0 -3.33335) translate(0 -3.33335) translate(0 -3.33335) translate(0 -3.33335) translate(0 -3.33335) translate(0 -3.33335) translate(0 -3.33335) translate(0 -3.33335) translate(0 -3.33335) translate(0 -3.33335) translate(0 -3.33335) translate(0 -3.33335) translate(0 -3.33335) translate(0 -3.33335) translate(0 -3.33335) translate(0 -3.33335) translate(0 -3.33335) translate(0 -3.33335) translate(0 -3.33335) translate(0 -3.33335) translate(0 -3.33335) translate(0 -3.33335) translate(-7.34665 -0.396492) translate(-1.66666 0) translate(-1.66666 0) translate(-0.12551 2.32567) translate(-0.707827 0.218105) translate(-1.66666 0) translate(1.66666 0) translate(-1.66666 0) translate(-1.66666 0) translate(-1.66666 0) translate(-1.66666 0) translate(-1.66666 0) translate(-2.15731 0.0264734) translate(1.66666 0) translate(-1.66666 0) translate(-1.66666 0) translate(-1.66666 0) translate(-1.66666 0) translate(-1.66666 0) translate(-1.66666 0) translate(-1.66666 0) translate(-1.66666 0) translate(-1.66666 0) translate(-1.66666 0) translate(-1.66666 0) translate(0 -1.66666) translate(0 -1.66666) translate(1.66666 0) translate(0 -1.66666) translate(1.66666 0) translate(0 -1.66666) translate(0 -1.66666) translate(0 -1.66666) translate(1.66666 0) translate(1.66666 0) translate(1.66666 0) translate(0 -1.66666) translate(0 -1.66666) translate(0 -1.66666) translate(0 -1.66666) translate(0 -1.66666) translate(0 -1.66666) translate(0 -1.66666) translate(0 -1.66666) translate(0 -1.66666) translate(0 -1.66666) translate(0 -1.66666) translate(0 -1.66666) translate(0 -1.66666) translate(0 -1.66666) translate(0 -1.66666) translate(0 -1.66666) translate(0 -1.66666) translate(0 -1.66666) translate(0 -1.66666) translate(0 -1.66666) translate(0 -1.66666) translate(0 -1.66666) translate(0 -1.66666) translate(0 -1.66666) translate(0 -1.66666) translate(1.66666 0) translate(1.66666 0) translate(1.66666 0) translate(1.66666 0) translate(1.66666 0) translate(1.66666 0) translate(1.66666 0) translate(1.66666 0) translate(1.66666 0) translate(1.66666 0) translate(1.66666 0) translate(1.66666 0) translate(0 1.66666) translate(0 1.66666) translate(0 1.66666) translate(0 1.66666) translate(0 1.66666) translate(0 1.66666) translate(0 1.66666) translate(0 1.66666) translate(0 1.66666) translate(-1.66666 0) translate(-1.66666 0) translate(-1.66666 0) translate(-1.66666 0) translate(0 -1.66666) translate(0 -1.66666) translate(0 1.66666) translate(0 1.66666) translate(0 1.66666) translate(0 1.66666) translate(0 1.66666) translate(0 1.66666) translate(0 1.66666) translate(0 1.66666) translate(0 1.66666) translate(-1.66666 0) translate(-1.66666 0) translate(-1.66666 0) translate(0 1.66666) translate(0 1.66666) translate(0 1.66666) translate(0 1.66666) translate(0 1.66666) translate(-1.66666 0) translate(-1.66666 0) translate(-1.66666 0) translate(0 1.66666) translate(0 1.66666) translate(0 1.66666) translate(0 1.66666) translate(0 1.66666) translate(0 1.66666) translate(0 1.66666) translate(0 1.66666) translate(-1.66666 0) translate(0 1.66666) translate(-1.66666 0) translate(0 1.66666) translate(0 1.66666) translate(0 1.66666) translate(0 1.66666) translate(0 1.66666) translate(-1.66666 0) translate(-1.66666 0) translate(-1.66666 0) translate(-1.66666 0) translate(0 -1.66666) translate(0 -1.66666) translate(0 1.66666) translate(0 1.66666) translate(0 1.66666) translate(0 1.66666) translate(0 1.66666) translate(0 1.66666) translate(0 1.66666) translate(0 1.66666) translate(0 -1.66666) translate(0 -1.66666) translate(0 -1.66666) translate(-1.66666 0) translate(-1.66666 0) translate(-1.66666 0) translate(-1.66666 0) translate(-1.66666 0) translate(-1.66666 0) translate(-1.66666 0) translate(-1.66666 0) translate(-1.66666 0) translate(-1.56355 1.02885) translate(-7.50004 -25.0001) translate(-3.19245 0.846904) translate(5.00003 -97.5005) translate(-9.62431 -0.829817) translate(27.5002 -220.001) translate(-6.96615 1.62281) translate(1.10472 -2.23004) translate(-9.63847 0.899196) translate(7.04019 0.0560654) translate(-11.25 -16.25) translate(2.11523 2.02597) translate(-27.1193 -20.3394) translate(-10.8798 -0.397069) translate(-44.0688 -110.172) translate(-1.69495 5.08486) translate(-8.57191 2.416) translate(-67.7982 -69.4931) translate(-0.127168 3.4846) translate(-16.9495 -283.057) translate(-4.64027 -0.169348) translate(1 0) translate(71.5022 0) translate(0 127.337) translate(-78.8263 -168.947) scale(0.564803 0.937256) translate(78.8263 168.947) translate(-139.564 1126.64) scale(1 0.752632) translate(139.564 -1126.64) translate(-139.564 190.036) scale(1 0.902375) translate(139.564 -190.036) translate(-139.564 210.595) scale(1.30003 1) translate(139.564 -210.595) translate(-107.355 1517.49) scale(1 1.16096) translate(107.355 -1517.49) translate(-107.355 0.205247) scale(1 1.13183) translate(107.355 -0.205247) translate(-136.758 -338.436) scale(1.50148 1) translate(136.758 338.436) translate(-152.694 -417.354) scale(1.2612 1.21266) translate(152.694 417.354) translate(-160.148 -445.389) scale(1 1.22519) translate(160.148 445.389) translate(-187.477 -380.211) scale(1.1479 0.963065) translate(187.477 380.211) translate(-169.043 -406.673) scale(0.986706 0.876627) translate(169.043 406.673) translate(-166.85 -463.854) scale(1.08382 1.19196) translate(166.85 463.854) translate(-159.592 918.456) scale(0.859634 1.01716) translate(159.592 -918.456) translate(-184.899 -405.678) scale(1.135 1.11385) translate(184.899 405.678) translate(-150.577 -518.346) scale(0.986699 1.09676) translate(150.577 518.346) translate(-155.421 -535.896) scale(1.04426 1.02871) translate(155.421 535.896) translate(-155.064 -536.049) scale(1.0463 1.01338) translate(155.064 536.049) translate(-165.783 -521.136) scale(1.00568 1.01887) translate(165.783 521.136) translate(-170.657 -511.469) scale(1.01013 1.00598) translate(170.657 511.469) translate(-169.334 -508.298) scale(1.09126 1) translate(169.334 508.298) translate(-156.911 -506.897) scale(1 1.06292) translate(156.911 506.897) translate(-163.644 765.54) scale(0.881707 0.600266) translate(163.644 -765.54) translate(45.8836 -37.1657) scale(0.309 0.684766) translate(-45.8836 37.1657) translate(-69.5385 1277.7) scale(0.415094 0.969376) translate(69.5385 -1277.7) translate(-173.403 4.77105) scale(2.29193 1) translate(173.403 -4.77105) translate(-131.465 -21.955) scale(1.62026 0.838547) translate(131.465 21.955) translate(-128.274 -10.6013) scale(0.965372 1.26492) translate(128.274 10.6013) translate(-127.333 -8.29175) scale(0.874565 1.22473) translate(127.333 8.29175) translate(-138.784 -6.2554) scale(1.00883 0.82026) translate(138.784 6.2554) translate(-144.168 -7.53605) scale(1.05722 1.04501) translate(144.168 7.53605) translate(-137.487 -6.97423) scale(1.01704 0.999876) translate(137.487 6.97423)">
<path stroke="null" id="svg_3" d="m-160,44c30,0 58,-18 88,-18s58,18 88,18s58,-18 88,-18s58,18 88,18l0,44l-352,0l0,-44z"/>
</defs>
<g stroke="null" id="svg_6">
<title stroke="null">Layer 1</title>
<g stroke="null" transform="matrix(-0.0217456 0.782606 -0.765199 -0.0222404 584.591 1129.22)" class="layout-footer-waves-g" id="svg_1">
<use stroke="null" href="#svg_3" x="-328.61098" y="405.36815" fill="rgba(211, 239, 255, 1)" id="svg_4" transform="matrix(3.69628 0 0 3.42743 381.903 -787.25)"/>
<use stroke="null" href="#svg_3" x="-333.61098" y="401.76815" fill="rgba(211, 239, 255, 0.5)" id="svg_2" transform="matrix(3.69628 0 0 3.42743 381.903 -787.25)"/>
</g>
</g>
</g>
</g>
</svg>
\ No newline at end of file
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 50" width="64" height="50">
<defs>
<image width="64" height="50" id="img1" href="data:image/png;base64,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"/>
</defs>
<style>
tspan { white-space:pre }
</style>
<use id="Background" href="#img1" x="0" y="0" />
</svg>
\ No newline at end of file
<template>
<div class="top-right-btn" :style="style">
<el-row>
<el-tooltip class="item" effect="dark" :content="showSearch ? '隐藏搜索' : '显示搜索'" placement="top" v-if="search">
<el-button circle icon="ele-Search" @click="toggleSearch()" />
</el-tooltip>
<el-tooltip class="item" effect="dark" content="重置" placement="top">
<el-button circle icon="ele-Refresh" @click="refresh()" />
</el-tooltip>
<el-tooltip class="item" effect="dark" content="显隐列" placement="top" v-if="columns">
<el-button circle icon="ele-Grid" @click="showColumn()" />
</el-tooltip>
</el-row>
<el-dialog :title="title" v-model="open" append-to-body style="text-align:center;width: 40%;" >
<el-transfer
:titles="['显示', '隐藏']"
v-model="value"
:data="columns"
@change="dataChange"
></el-transfer>
</el-dialog>
</div>
</template>
<script setup>
const props = defineProps({
showSearch: {
type: Boolean,
default: true,
},
columns: {
type: Array,
},
search: {
type: Boolean,
default: true,
},
gutter: {
type: Number,
default: 10,
},
})
const emits = defineEmits(['update:showSearch', 'queryTable','toggleSearch']);
// 显隐数据
const value = ref([]);
// 弹出层标题
const title = ref("显示/隐藏");
// 是否显示弹出层
const open = ref(false);
const style = computed(() => {
const ret = {};
if (props.gutter) {
ret.marginRight = `${props.gutter / 2}px`;
}
return ret;
});
// 搜索
function toggleSearch() {
// emits("update:showSearch", !props.showSearch);
emits("search");
}
// 刷新
function refresh() {
emits("queryTable");
}
// 右侧列表元素变化
function dataChange(data) {
for (let item in props.columns) {
const key = props.columns[item].key;
props.columns[item].visible = !data.includes(key);
}
}
// 打开显隐列dialog
function showColumn() {
open.value = true;
}
// 显隐列初始默认隐藏列
for (let item in props.columns) {
if (props.columns[item].visible === false) {
value.value.push(parseInt(item));
}
}
</script>
<style lang='scss' scoped>
:deep(.el-transfer__button) {
border-radius: 50%;
display: block;
margin-left: 0px;
}
:deep(.el-transfer__button:first-child) {
margin-bottom: 10px;
}
.my-el-transfer {
text-align: center;
}
</style>
\ No newline at end of file
<template>
<div class="top-card">
<el-card class="top-card-n" shadow="hover" :style="{ background: props.backgroundColor }">
<template #header>
{{ props.title }}
</template>
<el-row class="top-card-n-content" style="text-align: center">
<h1 style="font-size:30px;text-align: center;width:100%;">{{props.value}}</h1>
</el-row>
<el-row class="top-card-n-content" style="text-align: center">
<h1 v-if="btn" style="font-size:10px;text-align: right;width:100%;">详情>></h1>
</el-row>
</el-card>
</div>
</template>
<script setup name="topcard">
const props = defineProps({
title: {
type: String,
default: () => '标题',
},
value:{
type: String,
default: () => '0',
},
backgroundColor: {
type: String,
default: () => 'linear-gradient(to right, #60b2fb, #6485f6)',
},
btn:{
type: Boolean,
default: true
}
});
// 新增一个响应式属性,用于动态设置背景色
</script>
<style lang="scss" scoped>
.top-card {
&-top-card-n {
&-content {
text-align: center;
}
}
p {
font-size: 20px;
color: #fff;
}
:deep() {
.el-card {
&__header,
&__body {
color: var(--el-color-white) !important;
font-weight: bold;
}
}
.title {
font-size: 20px;
font-weight: bold;
}
}
margin-bottom: 30px;
}
</style>
<template>
<div class="top-card">
<el-card class="top-card-n" shadow="hover" :style="{ background: props.backgroundColor }">
<template #header> {{ props.title }}</template>
<el-row class="top-card-n-content" style="text-align: center">
<el-col :span="item_span" v-for="(item, key) in props.lists">
<p v-html="item.title"></p>
<h1 class="text-ellipsis">{{ item.name }}</h1>
</el-col>
</el-row>
</el-card>
</div>
</template>
<script setup name="topcardOther">
const props = defineProps({
title: {
type: String,
default: () => '标题',
},
lists: {
type: Array,
default: () => [],
},
backgroundColor: {
type: String,
default: () => '#409eff',
},
});
const item_span = 24 / props.lists.length;
// 新增一个响应式属性,用于动态设置背景色
</script>
<style lang="scss" scoped>
.top-card {
&-top-card-n {
&-content {
text-align: center;
}
}
p {
font-size: 20px;
color: white;
}
:deep() {
.el-card {
&__header {
color: white !important;
font-weight: bold;
font-size: 25px !important;
}
&__body {
color: white !important;
font-weight: bold;
}
}
.title {
font-size: 20px;
font-weight: bold;
}
}
margin-bottom: 30px;
}
</style>
<template>
<div class="top-card">
<el-card class="top-card-n" shadow="hover" :style="{ background: props.backgroundColor }">
<template #header>
{{ props.title }}
</template>
<el-row class="top-card-n-content" style="text-align: center">
<el-col :span="item_span" v-for="(item, key) in props.lists">
<p v-html="item.title"></p>
<h1 class="text-ellipsis">{{ item.name }}</h1>
</el-col>
</el-row>
</el-card>
</div>
</template>
<script setup name="topcard">
const props = defineProps({
title: {
type: String,
default: () => '标题',
},
lists: {
type: Array,
default: () => [],
},
backgroundColor: {
type: String,
default: () => 'linear-gradient(to right, #60b2fb, #6485f6)',
},
});
const item_span = 24 / props.lists.length;
// 新增一个响应式属性,用于动态设置背景色
</script>
<style lang="scss" scoped>
.top-card {
&-top-card-n {
&-content {
text-align: center;
}
}
p {
font-size: 20px;
color: #fff;
}
:deep() {
.el-card {
&__header,
&__body {
color: var(--el-color-white) !important;
font-weight: bold;
}
}
.title {
font-size: 20px;
font-weight: bold;
}
}
margin-bottom: 30px;
}
</style>
<template>
<div style="height: 400px" ref="homeLineRef"></div>
</template>
<script setup name="LINE">
import * as echarts from 'echarts';
const props = defineProps({
line_x: {
type: Array,
default: () => [],
},
lineData: {
type: Array,
default: () => [],
},
});
// 引入组件
const homeLineRef = ref();
let line;
//柱状图
const initLineChart = () => {
line = markRaw(echarts.init(homeLineRef.value, ''));
const option = {
title: {
text: '',
subtext: '',
},
tooltip: {
trigger: 'axis',
},
legend: {
data: ['一级', '二级','三级','四级']
},
toolbox: {
show: true,
feature: {
dataView: { show: true, readOnly: false },
magicType: { show: true, type: ['line', 'bar'] },
restore: { show: true },
saveAsImage: { show: true },
},
},
calculable: true,
xAxis: [
{
type: 'category',
// prettier-ignore
data: ['2023-01', '2023-02'],
},
],
yAxis: [
{
type: 'value',
},
],
series: [
{
name: '一级',
type: 'bar',
data: [10, 30, 27, 25, 20, 19, 2],
},
{
name: '二级',
type: 'bar',
data: [50, 60, 35, 40, 30, 45, 2],
},
{
name: '三级',
type: 'bar',
data: [50, 60, 35, 40, 30, 45, 2],
},
{
name: '四级',
type: 'bar',
data: [50, 60, 35, 40, 30, 45, 2],
},
],
};
line.setOption(option);
};
onMounted(() => {
nextTick(()=> {
initLineChart();
});
});
</script>
<template>
<div style="height: 400px" ref="homeLineRef"></div>
</template>
<script setup name="LINE">
import * as echarts from 'echarts';
const props = defineProps({
line_x: {
type: Array,
default: () => [],
},
lineData: {
type: Array,
default: () => [],
},
});
// 引入组件
const homeLineRef = ref();
let line;
//柱状图
const initLineChart = () => {
line = markRaw(echarts.init(homeLineRef.value, ''));
const option = {
title: {
text: '',
},
tooltip: {
trigger: 'axis',
axisPointer: {
type: 'cross',
label: {
backgroundColor: '#6a7985',
},
},
},
toolbox: {
feature: {
saveAsImage: {},
},
},
grid: {
left: '3%',
right: '4%',
bottom: '3%',
containLabel: true,
},
xAxis: [
{
type: 'category',
boundaryGap: false,
data: props.line_x,
},
],
yAxis: [
{
type: 'value',
},
],
series: {
name: '数据1',
type: 'line',
stack: 'Total',
areaStyle: {},
emphasis: {
focus: 'series',
},
data:props.lineData,
}
};
line.setOption(option);
};
onMounted(() => {
nextTick(()=> {
initLineChart();
});
});
</script>
<template>
<div style="height: 400px" ref="homeLineRef"></div>
</template>
<script setup name="LINE">
import * as echarts from 'echarts';
const props = defineProps({
line_x: {
type: Array,
default: () => [],
},
lineData: {
type: Array,
default: () => [],
},
seriesData: {
type: Array,
default: () => [],
},
});
// 引入组件
const homeLineRef = ref();
let line;
//柱状图
const initLineChart = () => {
line = markRaw(echarts.init(homeLineRef.value, ''));
const option = {
tooltip: {
trigger: 'axis',
axisPointer: {
type: 'cross',
crossStyle: {
color: '#999',
},
},
},
toolbox: {
feature: {
dataView: { show: true, readOnly: false },
magicType: { show: true, type: ['line', 'bar'] },
restore: { show: true },
saveAsImage: { show: true },
},
},
legend: {},
xAxis: [
{
type: 'category',
data: props.line_x,
axisPointer: {
type: 'shadow',
},
},
],
yAxis: [
{
type: 'value',
name: '',
min: 0,
max: 250,
interval: 50,
axisLabel: {
formatter: '{value}',
},
},
{
type: 'value',
name: '',
min: 0,
max: 25,
interval: 5,
axisLabel: {
formatter: '{value}',
},
},
],
series: props.seriesData,
};
line.setOption(option);
};
onMounted(() => {
initLineChart();
});
</script>
<template>
<div style="height: 400px" ref="homeLineRef"></div>
</template>
<script setup name="line_rotate">
import * as echarts from 'echarts';
const props = defineProps({
line_x: {
type: Array,
default: () => [],
},
lineData: {
type: Array,
default: () => [],
},
});
// 引入组件
const homeLineRef = ref();
let line;
//柱状图
const initLineChart = () => {
line = markRaw(echarts.init(homeLineRef.value, ''));
const option = {
title: {
text: '',
},
tooltip: {
trigger: 'axis',
axisPointer: {
type: 'cross',
label: {
backgroundColor: '#6a7985',
},
},
},
toolbox: {
feature: {
saveAsImage: {},
},
},
grid: {
left: '3%',
right: '4%',
bottom: '3%',
containLabel: true,
},
xAxis: [
{
type: 'category',
boundaryGap: false,
data: props.line_x,
axisLabel: {
interval: 0, // 强制显示所有文字
rotate: 30, // 文字倾斜角度
margin: 40, // 文字和轴线之间的距离
textStyle: {
align: 'center', // 文字水平对齐方式
baseline: 'top', // 垂直方向上的基准线位置
lineHeight: 30 // 设置标签文字的行高
}
}
},
],
yAxis: [
{
type: 'value',
},
],
series: props.lineData,
};
line.setOption(option);
};
onMounted(() => {
nextTick(()=> {
initLineChart();
});
});
</script>
<template>
<div style="height: 400px" ref="homeLineRef"></div>
</template>
<script setup name="LINE">
import * as echarts from 'echarts';
const props = defineProps({
line_x: {
type: Array,
default: () => [],
},
lineData: {
type: Array,
default: () => [],
},
});
// 引入组件
const homeLineRef = ref();
let line;
//柱状图
const initLineChart = () => {
line = markRaw(echarts.init(homeLineRef.value, ''));
var getname = props.line_x;
var getvalue = props.lineData;
var data = [];
for (var i = 0; i < getname.length; i++) {
data.push({ name: getname[i], value: getvalue[i] });
}
const colorList = ['#51A3FC', '#36C78B', '#FEC279', '#968AF5', '#E790E8', '#E790E1', '#E790E0'];
const option = {
backgroundColor: '',
title: {
text: '',
x: 'left',
textStyle: { fontSize: '15', color: '' },
},
toolbox: {
show: true,
feature: {
mark: { show: true },
dataView: { show: true, readOnly: false },
restore: { show: true },
saveAsImage: { show: true },
},
},
tooltip: { trigger: 'item' },
legend: {
type: 'scroll',
orient: 'vertical',
right: '0%',
left: '65%',
top: 'center',
itemWidth: 14,
itemHeight: 14,
data: getname,
textStyle: {
rich: {
name: {
fontSize: 14,
fontWeight: 400,
width: 200,
height: 35,
padding: [0, 0, 0, 60],
color: '',
},
rate: {
fontSize: 15,
fontWeight: 500,
height: 35,
width: 40,
padding: [0, 0, 0, 30],
color: '',
},
},
},
},
series: [
{
type: 'pie',
radius: ['82', '102'],
center: ['32%', '50%'],
itemStyle: {
color: function (params) {
return colorList[params.dataIndex];
},
},
label: { show: true },
labelLine: { show: true },
data: data,
},
],
};
line.setOption(option);
};
onMounted(() => {
initLineChart();
});
</script>
src/components/BarGraph.vue:
<template>
<div class="echarts-box">
<div id="myEcharts" :style="{ width: this.width, height: this.height }"></div>
</div>
</template>
<script>
import * as echarts from "echarts";
import {onMounted, onUnmounted} from "vue";
export default {
name: "App",
props: ["width", "height"],
setup() {
let myEcharts = echarts;
let chart = null
let setOption = {
title: {
text: getNowDate() + ' 总用电量/小时',
left: 'center',
},
xAxis: {
type: 'category',
data:[]
},
tooltip: {
trigger: 'axis'
},
yAxis: {
type: 'value'
},
// dataZoom: [
// {
// type: 'inside',
// start: 0,
// end: 20
// },
// {
// start: 0,
// end: 20
// }
// ],
series: [
{
data: [],
type: 'line',
smooth: true,
itemStyle: {
normal: {
label: {
show: true,
position: 'top',
formatter: '{c}'
}
}
}
}
]
};
onMounted(() => {
initChart();
});
onUnmounted(() => {
myEcharts.dispose;
});
function setFirstData(arr,arr1){
setOption.xAxis.data = arr
setOption.series[0].data = arr1
console.log(setOption.series[0])
chart.setOption(setOption);
}
function getNowDate (){
var date = new Date();
var year = date.getFullYear() // 年
var month = date.getMonth() + 1; // 月
var day = date.getDate(); // 日
// 给一位数的数据前面加 “0”
if (month >= 1 && month <= 9) {
month = "0" + month;
}
if (day >= 0 && day <= 9) {
day = "0" + day;
}
return year + "年" + month + "月" + day+'日'
}
function initChart() {
chart = myEcharts.init(document.getElementById("myEcharts"), "purple-passion");
chart.setOption(setOption);
window.onresize = function () {
chart.resize();
};
}
return {
initChart,setFirstData
};
}
};
</script>
<template>
<div ref="chartContainer" :style="{ width: this.width, height: this.height }"></div>
</template>
<script>
import { ref, onMounted } from 'vue';
import * as echarts from 'echarts'; // 假设您正在使用ECharts库
export default {
name: 'StackedLineChart',
props: ["width", "height"],
setup() {
const chartContainer = ref(null);
let chartInstance = null;
const chartOptions = {
title: {
text: 'Stacked Line'
},
tooltip: {
trigger: 'axis'
},
legend: {
// data: ['Email', 'Union Ads', 'Video Ads', 'Direct', 'Search Engine']
data: ['电压/V', '电流/A', '平均功率/W', '因数数据', '频率数据/Hz', '当前用电量']
},
grid: {
left: '3%',
right: '4%',
bottom: '3%',
containLabel: true
},
toolbox: {
feature: {
saveAsImage: {}
}
},
xAxis: {
type: 'category',
boundaryGap: false,
data: ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun']
},
yAxis: {
type: 'value'
},
series: [
{
name: '电压/V',
type: 'line',
stack: 'Total',
data: [120, 132, 101, 134, 90, 230, 210]
},
{
name: 'Union Ads',
type: 'line',
stack: 'Total',
data: [220, 182, 191, 234, 290, 330, 310]
},
{
name: 'Video Ads',
type: 'line',
stack: 'Total',
data: [150, 232, 201, 154, 190, 330, 410]
},
{
name: 'Direct',
type: 'line',
stack: 'Total',
data: [320, 332, 301, 334, 390, 330, 320]
},
{
name: 'Search Engine',
type: 'line',
stack: 'Total',
data: [820, 932, 901, 934, 1290, 1330, 1320]
}
]
};
onMounted(() => {
if (chartContainer.value) {
chartInstance = echarts.init(chartContainer.value);
chartInstance.setOption(chartOptions);
}
});
return {
chartContainer
};
},
unmounted() {
if (chartInstance) {
chartInstance.dispose();
}
}
};
</script>
<style scoped>
/* 组件样式 */
</style>
\ No newline at end of file
import * as echarts from 'echarts/core'
/**
引入需要的图表,需要什么就加什么
*/
import { BarChart, LineChart, PieChart } from 'echarts/charts'
// 引入提示框,标题,直角坐标系,数据集,内置数据转换器组件,组件后缀都为 Component
import {
TitleComponent,
TooltipComponent,
GridComponent,
DatasetComponent,
TransformComponent,
LegendComponent,
ToolboxComponent
} from 'echarts/components'
// 标签自动布局,全局过渡动画等特性
import { LabelLayout, UniversalTransition } from 'echarts/features'
// 引入 Canvas 渲染器,注意引入 CanvasRenderer 或者 SVGRenderer 是必须的一步
import { CanvasRenderer } from 'echarts/renderers'
// 注册必须的组件,上面引入的都需要在此注册
echarts.use([
ToolboxComponent,
LegendComponent,
TitleComponent,
TooltipComponent,
GridComponent,
DatasetComponent,
TransformComponent,
LabelLayout,
UniversalTransition,
CanvasRenderer,
BarChart,
LineChart,
PieChart
])
// 导出
export default echarts
<template>
<div class="icon-selector w100 h100">
<el-input
v-model="state.fontIconSearch"
:placeholder="state.fontIconPlaceholder"
:clearable="clearable"
:disabled="disabled"
:size="size"
ref="inputWidthRef"
@clear="onClearFontIcon"
@focus="onIconFocus"
@blur="onIconBlur"
>
<template #prepend>
<SvgIcon
:name="state.fontIconPrefix === '' ? prepend : state.fontIconPrefix"
class="font14"
v-if="state.fontIconPrefix === '' ? prepend?.indexOf('ele-') > -1 : state.fontIconPrefix?.indexOf('ele-') > -1"
/>
<i v-else :class="state.fontIconPrefix === '' ? prepend : state.fontIconPrefix" class="font14"></i>
</template>
</el-input>
<el-popover
placement="bottom"
:width="state.fontIconWidth"
transition="el-zoom-in-top"
popper-class="icon-selector-popper"
trigger="click"
:virtual-ref="inputWidthRef"
virtual-triggering
>
<template #default>
<div class="icon-selector-warp">
<div class="icon-selector-warp-title">{{ title }}</div>
<el-tabs v-model="state.fontIconTabActive" @tab-click="onIconClick">
<el-tab-pane lazy label="ali" name="ali">
<IconList :list="fontIconSheetsFilterList" :empty="emptyDescription" :prefix="state.fontIconPrefix" @get-icon="onColClick" />
</el-tab-pane>
<el-tab-pane lazy label="ele" name="ele">
<IconList :list="fontIconSheetsFilterList" :empty="emptyDescription" :prefix="state.fontIconPrefix" @get-icon="onColClick" />
</el-tab-pane>
<el-tab-pane lazy label="awe" name="awe">
<IconList :list="fontIconSheetsFilterList" :empty="emptyDescription" :prefix="state.fontIconPrefix" @get-icon="onColClick" />
</el-tab-pane>
</el-tabs>
</div>
</template>
</el-popover>
</div>
</template>
<script setup name="iconSelector">
import initIconfont from '/@/utils/getStyleSheets';
import '/@/theme/iconSelector.scss';
// 定义父组件传过来的值
const props = defineProps({
// 输入框前置内容
prepend: {
type: String,
default: () => 'ele-Pointer',
},
// 输入框占位文本
placeholder: {
type: String,
default: () => '请输入内容搜索图标或者选择图标',
},
// 输入框占位文本
size: {
type: String,
default: () => 'default',
},
// 弹窗标题
title: {
type: String,
default: () => '请选择图标',
},
// 禁用
disabled: {
type: Boolean,
default: () => false,
},
// 是否可清空
clearable: {
type: Boolean,
default: () => true,
},
// 自定义空状态描述文字
emptyDescription: {
type: String,
default: () => '无相关图标',
},
// 双向绑定值,默认为 modelValue,
// 参考:https://v3.cn.vuejs.org/guide/migration/v-model.html#%E8%BF%81%E7%A7%BB%E7%AD%96%E7%95%A5
// 参考:https://v3.cn.vuejs.org/guide/component-custom-events.html#%E5%A4%9A%E4%B8%AA-v-model-%E7%BB%91%E5%AE%9A
modelValue: String,
});
// 定义子组件向父组件传值/事件
const emit = defineEmits(['update:modelValue', 'get', 'clear']);
// 引入组件
const IconList = defineAsyncComponent(() => import('/@/components/iconSelector/list.vue'));
// 定义变量内容
const inputWidthRef = ref();
const state = reactive({
fontIconPrefix: '',
fontIconWidth: 0,
fontIconSearch: '',
fontIconPlaceholder: '',
fontIconTabActive: 'ali',
fontIconList: {
ali: [],
ele: [],
awe: [],
},
});
// 处理 input 获取焦点时,modelValue 有值时,改变 input 的 placeholder 值
const onIconFocus = () => {
if (!props.modelValue) return false;
state.fontIconSearch = '';
state.fontIconPlaceholder = props.modelValue;
};
// 处理 input 失去焦点时,为空将清空 input 值,为点击选中图标时,将取原先值
const onIconBlur = () => {
const list = fontIconTabNameList();
setTimeout(() => {
const icon = list.filter((icon) => icon === state.fontIconSearch);
if (icon.length <= 0) state.fontIconSearch = '';
}, 300);
};
// 图标搜索及图标数据显示
const fontIconSheetsFilterList = computed(() => {
const list = fontIconTabNameList();
if (!state.fontIconSearch) return list;
let search = state.fontIconSearch.trim().toLowerCase();
return list.filter((item) => {
if (item.toLowerCase().indexOf(search) !== -1) return item;
});
});
// 根据 tab name 类型设置图标
const fontIconTabNameList = () => {
let iconList = [];
if (state.fontIconTabActive === 'ali') iconList = state.fontIconList.ali;
else if (state.fontIconTabActive === 'ele') iconList = state.fontIconList.ele;
else if (state.fontIconTabActive === 'awe') iconList = state.fontIconList.awe;
return iconList;
};
// 处理 icon 双向绑定数值回显
const initModeValueEcho = () => {
if (props.modelValue === '') return (state.fontIconPlaceholder = props.placeholder);
state.fontIconPlaceholder = props.modelValue;
state.fontIconPrefix = props.modelValue;
};
// 处理 icon 类型,用于回显时,tab 高亮与初始化数据
const initFontIconName = () => {
let name = 'ali';
if (props.modelValue.indexOf('iconfont') > -1) name = 'ali';
else if (props.modelValue.indexOf('ele-') > -1) name = 'ele';
else if (props.modelValue.indexOf('fa') > -1) name = 'awe';
// 初始化 tab 高亮回显
state.fontIconTabActive = name;
return name;
};
// 初始化数据
const initFontIconData = async (name) => {
if (name === 'ali') {
// 阿里字体图标使用 `iconfont xxx`
if (state.fontIconList.ali.length > 0) return;
await initIconfont.ali().then((res) => {
state.fontIconList.ali = res.map((i) => `iconfont ${i}`);
});
} else if (name === 'ele') {
// element plus 图标
if (state.fontIconList.ele.length > 0) return;
await initIconfont.ele().then((res) => {
state.fontIconList.ele = res;
});
} else if (name === 'awe') {
// fontawesome字体图标使用 `fa xxx`
if (state.fontIconList.awe.length > 0) return;
await initIconfont.awe().then((res) => {
state.fontIconList.awe = res.map((i) => `fa ${i}`);
});
}
// 初始化 input 的 placeholder
// 参考(单项数据流):https://cn.vuejs.org/v2/guide/components-props.html?#%E5%8D%95%E5%90%91%E6%95%B0%E6%8D%AE%E6%B5%81
state.fontIconPlaceholder = props.placeholder;
// 初始化双向绑定回显
initModeValueEcho();
};
// 图标点击切换
const onIconClick = (pane) => {
initFontIconData(pane.paneName);
inputWidthRef.value.focus();
};
// 获取当前点击的 icon 图标
const onColClick = (v) => {
state.fontIconPlaceholder = v;
state.fontIconPrefix = v;
emit('get', state.fontIconPrefix);
emit('update:modelValue', state.fontIconPrefix);
inputWidthRef.value.focus();
};
// 清空当前点击的 icon 图标
const onClearFontIcon = () => {
state.fontIconPrefix = '';
emit('clear', state.fontIconPrefix);
emit('update:modelValue', state.fontIconPrefix);
};
// 获取 input 的宽度
const getInputWidth = () => {
nextTick(() => {
state.fontIconWidth = inputWidthRef.value.$el.offsetWidth;
});
};
// 监听页面宽度改变
const initResize = () => {
window.addEventListener('resize', () => {
getInputWidth();
});
};
// 页面加载时
onMounted(() => {
initFontIconData(initFontIconName());
initResize();
getInputWidth();
});
// 监听双向绑定 modelValue 的变化
watch(
() => props.modelValue,
() => {
initModeValueEcho();
initFontIconName();
}
);
</script>
<template>
<div class="icon-selector-warp-row">
<el-scrollbar ref="selectorScrollbarRef">
<el-row :gutter="10" v-if="props.list.length > 0">
<el-col :xs="6" :sm="4" :md="4" :lg="4" :xl="4" v-for="(v, k) in list" :key="k" @click="onColClick(v)">
<div class="icon-selector-warp-item" :class="{ 'icon-selector-active': prefix === v }">
<SvgIcon :name="v" />
</div>
</el-col>
</el-row>
<el-empty :image-size="100" v-if="list.length <= 0" :description="empty"></el-empty>
</el-scrollbar>
</div>
</template>
<script setup name="iconSelectorList">
// 定义父组件传过来的值
const props = defineProps({
// 图标列表数据
list: {
type: Array,
default: () => [],
},
// 自定义空状态描述文字
empty: {
type: String,
default: () => '无相关图标',
},
// 高亮当前选中图标
prefix: {
type: String,
default: () => '',
},
});
// 定义子组件向父组件传值/事件
const emit = defineEmits(['get-icon']);
// 当前 icon 图标点击时
const onColClick = (v) => {
emit('get-icon', v);
};
</script>
<style scoped lang="scss">
.icon-selector-warp-row {
height: 230px;
overflow: hidden;
.el-row {
padding: 15px;
}
.el-scrollbar__bar.is-horizontal {
display: none;
}
.icon-selector-warp-item {
display: flex;
justify-content: center;
align-items: center;
border: 1px solid var(--el-border-color);
border-radius: 5px;
margin-bottom: 10px;
height: 30px;
i {
font-size: 20px;
color: var(--el-text-color-regular);
}
&:hover {
cursor: pointer;
background-color: var(--el-color-primary-light-9);
border: 1px solid var(--el-color-primary-light-5);
i {
color: var(--el-color-primary);
}
}
}
.icon-selector-active {
background-color: var(--el-color-primary-light-9);
border: 1px solid var(--el-color-primary-light-5);
i {
color: var(--el-color-primary);
}
}
}
</style>
<template>
<div class="news-list">
<el-row class="news-body" v-for="(item, index) in newsList" :key="index">
<el-col :span="2">
<div class="news-index" :class="{ 'red-circle': index < 3 }">{{ index + 1 }}</div>
</el-col>
<el-col :span="16">
<p class="news-title">{{ item.title }}</p>
</el-col>
<el-col :span="6">
<p class="news-subtitle">{{ item.subtitle }}</p>
</el-col>
</el-row>
</div>
</template>
<script setup name="card_list">
const props = defineProps({
newsList: {
type: Array,
default: () => [],
},
});
</script>
<style lang="scss" scoped>
.news-list {
height:530px !important;
.news-body {
&:last-child {
border-bottom: none;
}
.news-index {
margin-top: 10px;
display: flex;
align-items: center;
justify-content: center;
width: 20px;
height: 20px;
font-size: 18px;
font-weight: bold;
border-radius: 65%;
border: 2px solid #ccc;
&.red-circle {
color: white;
background-color: red;
border-color: red;
}
}
.news-title {
margin: 10px 0;
font-size: 14px;
line-height: 1.5;
}
.news-subtitle {
margin: 10px 0;
font-size: 8px;
line-height: 1.5;
float: right;
color:rgb(88,89,90)
}
.news-title {
font-weight: bold;
}
}
}
</style>
<template>
<i v-if="isShowIconSvg" class="el-icon" :style="setIconSvgStyle">
<component :is="getIconName" />
</i>
<div v-else-if="isShowIconImg" :style="setIconImgOutStyle">
<img :src="getIconName" :style="setIconSvgInsStyle" />
</div>
<i v-else :class="getIconName" :style="setIconSvgStyle" />
</template>
<script setup name="svgIcon">
// 定义父组件传过来的值
const props = defineProps({
// svg 图标组件名字
name: {
type: String,
},
// svg 大小
size: {
type: Number,
default: () => 14,
},
// svg 颜色
color: {
type: String,
},
});
// 在线链接、本地引入地址前缀
const linesString = ['https', 'http', '/src', '/assets', 'data:image', import.meta.env.VITE_PUBLIC_PATH];
// 获取 icon 图标名称
const getIconName = computed(() => {
return props?.name;
});
// 用于判断 element plus 自带 svg 图标的显示、隐藏
const isShowIconSvg = computed(() => {
return props?.name?.startsWith('ele-');
});
// 用于判断在线链接、本地引入等图标显示、隐藏
const isShowIconImg = computed(() => {
return linesString.find((str) => props.name?.startsWith(str));
});
// 设置图标样式
const setIconSvgStyle = computed(() => {
return `font-size: ${props.size}px;color: ${props.color};`;
});
// 设置图片样式
const setIconImgOutStyle = computed(() => {
return `width: ${props.size}px;height: ${props.size}px;display: inline-block;overflow: hidden;`;
});
// 设置图片样式
const setIconSvgInsStyle = computed(() => {
const filterStyle = [];
const compatibles = ['-webkit', '-ms', '-o', '-moz'];
compatibles.forEach((j) => filterStyle.push(`${j}-filter: drop-shadow(${props.color} 30px 0);`));
return `width: ${props.size}px;height: ${props.size}px;position: relative;left: -${props.size}px;${filterStyle.join('')}`;
});
</script>
<template>
<div>
<audio v-if="fileurl" style="width:100%" @timeupdate="updateProgress" controls="controls" ref="audioRef" >
<source :src="fileurl" type="audio/mpeg" controls="controls"/>
您的浏览器不支持音频播放
</audio>
</div>
</template>
<script>
export default {
props: {
fileurl: {
trpe: String
}
},
data() {
return {
audioStatus: 'play',
audioStart: '0:00',
duration: '0:00',
audioVolume: 0.5,
audioHuds: true
}
},
computed: {
audioIcon() {
if (this.audioHuds) {
return this.audioVolume < 0.01 ? 'checked icon-jingyin' : 'checked icon-shengyin'
} else {
return 'icon-shengyin'
}
}
},
mounted() {
this.fetch()
},
methods: {
turn(second){
this.$refs.audioRef.currentTime = second;
this.$refs.audioRef.play()
},
fetch() {
let that = this
var myVid = this.$refs.audioRef
myVid.loop = false
// 监听音频播放完毕
myVid.addEventListener(
'ended',
function () {
that.audioStatus = 'play' // 显示播放icon
document.getElementById('progressBar').style.width = '0%' // 进度条初始化
},
false
)
if (myVid != null) {
myVid.oncanplay = function () {
that.duration = that.transTime(myVid.duration) // 计算音频时长
}
myVid.volume = 0.5 // 设置音量50%
}
},
// 播放暂停控制
playAudio() {
let recordAudio = this.$refs.audioRef // 获取audio元素
if (recordAudio.paused) {
recordAudio.play()
this.audioStatus = 'pause'
} else {
recordAudio.pause()
this.audioStatus = 'play'
}
},
// 更新进度条与当前播放时间
updateProgress(e) {
var value = e.target.currentTime / e.target.duration
if (document.getElementById('progressBar')) {
document.getElementById('progressBar').style.width = value * 100 + '%'
if (e.target.currentTime === e.target.duration) {
this.audioStatus = 'pause'
}
} else {
this.audioStatus = 'pause'
}
this.audioStart = this.transTime(this.$refs.audioRef.currentTime)
},
/**
* 音频播放时间换算
* @param {number} value - 音频当前播放时间,单位秒
*/
transTime(time) {
var duration = parseInt(time)
var minute = parseInt(duration / 60)
var sec = (duration % 60) + ''
var isM0 = ':'
if (minute === 0) {
minute = '00'
} else if (minute < 10) {
minute = '0' + minute
}
if (sec.length === 1) {
sec = '0' + sec
}
return minute + isM0 + sec
},
setAudioIcon() {
this.audioStatus = 'pause'
},
handleShowMuteIcon(val) {
this.audioVolume = val
}
}
}
</script>
<style lang="scss" scoped>
.volume {
position: relative;
.volume-progress {
position: absolute;
top: -150px;
width: 32px;
height: 140px;
background: #f6f6f6;
border-radius: 4px;
padding-top: 10px;
}
.volume-bar-bg {
margin: 0 auto;
width: 6px;
height: 120px;
background: #dcdcdc;
border-radius: 100px;
flex: 1;
position: relative;
transform: rotate(180deg);
cursor: pointer;
.volume-bar {
width: 6px;
height: 50%;
background: #56bf8b;
border-radius: 100px;
}
}
.checked {
color: #56bf8b;
}
}
.audio-right {
width: 100%;
height: 49px;
line-height: 49px;
background: #f6f6f6;
border-radius: 6px;
display: flex;
padding: 0 15px;
.dialogAudioPlay {
cursor: pointer;
color: #5c5e66;
font-size: 20px;
}
.progress-bar-bg {
background-color: #fff;
flex: 1;
position: relative;
height: 10px;
top: 50%;
transform: translateY(-50%);
margin-top: -1px;
cursor: pointer;
margin: 0 10px;
}
.progress-bar {
background-color: #3698fd;
width: 0%;
height: 10px;
border-radius: 5px;
}
.audio-time {
overflow: hidden;
font-size: 14px;
.audio-length-total {
float: right;
}
.audio-length-current {
float: left;
}
}
}
</style>
import { useUserInfo } from '/@/stores/userInfo';
import { judementSameArr } from '/@/utils/arrayOperation';
/**
* 用户权限指令
* @directive 单个权限验证(v-auth="xxx")
* @directive 多个权限验证,满足一个则显示(v-auths="[xxx,xxx]")
* @directive 多个权限验证,全部满足则显示(v-auth-all="[xxx,xxx]")
*/
export function authDirective(app) {
// 单个权限验证(v-auth="xxx")
app.directive('auth', {
mounted(el, binding) {
const stores = useUserInfo();
if (!stores.userInfos.authBtnList.some((v) => v === binding.value)) el.parentNode.removeChild(el);
},
});
// 多个权限验证,满足一个则显示(v-auths="[xxx,xxx]")
app.directive('auths', {
mounted(el, binding) {
let flag = false;
const stores = useUserInfo();
stores.userInfos.authBtnList.map((val) => {
binding.value.map((v) => {
if (val === v) flag = true;
});
});
if (!flag) el.parentNode.removeChild(el);
},
});
// 多个权限验证,全部满足则显示(v-auth-all="[xxx,xxx]")
app.directive('auth-all', {
mounted(el, binding) {
const stores = useUserInfo();
const flag = judementSameArr(binding.value, stores.userInfos.authBtnList);
if (!flag) el.parentNode.removeChild(el);
},
});
}
import { nextTick } from "vue";
/**
* 按钮波浪指令
* @directive 默认方式:v-waves,如 `<div v-waves></div>`
* @directive 参数方式:v-waves=" |light|red|orange|purple|green|teal",如 `<div v-waves="'light'"></div>`
*/
export function wavesDirective(app) {
app.directive('waves', {
mounted(el, binding) {
el.classList.add('waves-effect');
binding.value && el.classList.add(`waves-${binding.value}`);
function setConvertStyle(obj) {
let style = '';
for (let i in obj) {
if (obj.hasOwnProperty(i)) style += `${i}:${obj[i]};`;
}
return style;
}
function onCurrentClick(e) {
let elDiv = document.createElement('div');
elDiv.classList.add('waves-ripple');
el.appendChild(elDiv);
let styles = {
left: `${e.layerX}px`,
top: `${e.layerY}px`,
opacity: 1,
transform: `scale(${(el.clientWidth / 100) * 10})`,
'transition-duration': `750ms`,
'transition-timing-function': `cubic-bezier(0.250, 0.460, 0.450, 0.940)`,
};
elDiv.setAttribute('style', setConvertStyle(styles));
setTimeout(() => {
elDiv.setAttribute(
'style',
setConvertStyle({
opacity: 0,
transform: styles.transform,
left: styles.left,
top: styles.top,
})
);
setTimeout(() => {
elDiv && el.removeChild(elDiv);
}, 750);
}, 450);
}
el.addEventListener('mousedown', onCurrentClick, false);
},
unmounted(el) {
el.addEventListener('mousedown', () => {});
},
});
}
/**
* 自定义拖动指令
* @description 使用方式:v-drag="[dragDom,dragHeader]",如 `<div v-drag="['.drag-container .el-dialog', '.drag-container .el-dialog__header']"></div>`
* @description dragDom 要拖动的元素,dragHeader 要拖动的 Header 位置
* @link 注意:https://github.com/element-plus/element-plus/issues/522
* @lick 参考:https://blog.csdn.net/weixin_46391323/article/details/105228020?utm_medium=distribute.pc_relevant.none-task-blog-baidujs_title-10&spm=1001.2101.3001.4242
*/
export function dragDirective(app) {
app.directive('drag', {
mounted(el, binding) {
if (!binding.value) return false;
const dragDom = document.querySelector(binding.value[0]);
const dragHeader = document.querySelector(binding.value[1]);
dragHeader.onmouseover = () => (dragHeader.style.cursor = `move`);
function down(e, type) {
// 鼠标按下,计算当前元素距离可视区的距离
const disX = type === 'pc' ? e.clientX - dragHeader.offsetLeft : e.touches[0].clientX - dragHeader.offsetLeft;
const disY = type === 'pc' ? e.clientY - dragHeader.offsetTop : e.touches[0].clientY - dragHeader.offsetTop;
// body当前宽度
const screenWidth = document.body.clientWidth;
// 可见区域高度(应为body高度,可某些环境下无法获取)
const screenHeight = document.documentElement.clientHeight;
// 对话框宽度
const dragDomWidth = dragDom.offsetWidth;
// 对话框高度
const dragDomheight = dragDom.offsetHeight;
const minDragDomLeft = dragDom.offsetLeft;
const maxDragDomLeft = screenWidth - dragDom.offsetLeft - dragDomWidth;
const minDragDomTop = dragDom.offsetTop;
const maxDragDomTop = screenHeight - dragDom.offsetTop - dragDomheight;
// 获取到的值带px 正则匹配替换
let styL = getComputedStyle(dragDom).left;
let styT = getComputedStyle(dragDom).top;
// 注意在ie中 第一次获取到的值为组件自带50% 移动之后赋值为px
if (styL.includes('%')) {
styL = +document.body.clientWidth * (+styL.replace(/\%/g, '') / 100);
styT = +document.body.clientHeight * (+styT.replace(/\%/g, '') / 100);
} else {
styL = +styL.replace(/\px/g, '');
styT = +styT.replace(/\px/g, '');
}
return {
disX,
disY,
minDragDomLeft,
maxDragDomLeft,
minDragDomTop,
maxDragDomTop,
styL,
styT,
};
}
function move(e, type, obj) {
let { disX, disY, minDragDomLeft, maxDragDomLeft, minDragDomTop, maxDragDomTop, styL, styT } = obj;
// 通过事件委托,计算移动的距离
let left = type === 'pc' ? e.clientX - disX : e.touches[0].clientX - disX;
let top = type === 'pc' ? e.clientY - disY : e.touches[0].clientY - disY;
// 边界处理
if (-left > minDragDomLeft) {
left = -minDragDomLeft;
} else if (left > maxDragDomLeft) {
left = maxDragDomLeft;
}
if (-top > minDragDomTop) {
top = -minDragDomTop;
} else if (top > maxDragDomTop) {
top = maxDragDomTop;
}
// 移动当前元素
dragDom.style.cssText += `;left:${left + styL}px;top:${top + styT}px;`;
}
/**
* pc端
* onmousedown 鼠标按下触发事件
* onmousemove 鼠标按下时持续触发事件
* onmouseup 鼠标抬起触发事件
*/
dragHeader.onmousedown = (e) => {
const obj = down(e, 'pc');
document.onmousemove = (e) => {
move(e, 'pc', obj);
};
document.onmouseup = () => {
document.onmousemove = null;
document.onmouseup = null;
};
};
/**
* 移动端
* ontouchstart 当按下手指时,触发ontouchstart
* ontouchmove 当移动手指时,触发ontouchmove
* ontouchend 当移走手指时,触发ontouchend
*/
dragHeader.ontouchstart = (e) => {
const obj = down(e, 'app');
document.ontouchmove = (e) => {
move(e, 'app', obj);
};
document.ontouchend = () => {
document.ontouchmove = null;
document.ontouchend = null;
};
};
},
});
}
/**
* select 组件下拉懒加载
* @param {*} app
*/
export function selectLazy(app) {
function handleEvent() {
}
app.directive('select-lazy', {
mounted(el, binding){
// 获取element-ui定义好的scroll盒子
let select_dom = el.querySelector(".el-select-dropdown .el-select-dropdown__wrap");
select_dom.addEventListener("scroll", function () {
const CONDITION = this.scrollHeight - this.scrollTop <= this.clientHeight;
if (CONDITION) {
binding.value();
}
});
}
})
}
\ No newline at end of file
import { authDirective } from './authDirective';
import { wavesDirective, dragDirective, selectLazy } from '/@/directive/customDirective';
/**
* 导出指令方法:v-xxx
* @methods authDirective 用户权限指令,用法:v-auth
* @methods wavesDirective 按钮波浪指令,用法:v-waves
* @methods dragDirective 自定义拖动指令,用法:v-drag
*/
export function directive(app) {
// 用户权限指令
authDirective(app);
// 按钮波浪指令
wavesDirective(app);
// 自定义拖动指令
dragDirective(app);
selectLazy(app)
}
import { cloneDeep } from "lodash";
export const useLocalPage = () =>{
let originData = []
let temData = []
const setLocalData = (data = []) =>{
originData = data
temData = cloneDeep(data)
}
const getLocalData = (limit = 100)=>{
if(originData.length <= 0) {
return []
}
return originData.splice(0, limit)
}
const filterLocalData = (func) =>{
if(!func){
throw new Error('请输入筛选方法')
}
const tem = cloneDeep(temData)
originData = tem.filter(func)
}
const resetLocalData = ()=>{
originData = cloneDeep(temData)
}
return {
setLocalData,
getLocalData,
filterLocalData,
resetLocalData
}
}
\ No newline at end of file
<template>
<div class="h100" v-show="!isTagsViewCurrenFull">
<el-aside class="layout-aside" :class="setCollapseStyle">
<Logo v-if="setShowLogo" />
<el-scrollbar class="flex-auto" ref="layoutAsideScrollbarRef" @mouseenter="onAsideEnterLeave(true)" @mouseleave="onAsideEnterLeave(false)">
<Vertical :menuList="state.menuList" />
</el-scrollbar>
</el-aside>
</div>
</template>
<script setup name="layoutAside">
import { storeToRefs } from 'pinia';
import pinia from '/@/stores/index';
import { useRoutesList } from '/@/stores/routesList';
import { useThemeConfig } from '/@/stores/themeConfig';
import { useTagsViewRoutes } from '/@/stores/tagsViewRoutes';
import mittBus from '/@/utils/mitt';
// 引入组件
const Logo = defineAsyncComponent(() => import('/@/layout/logo/index.vue'));
const Vertical = defineAsyncComponent(() => import('/@/layout/navMenu/vertical.vue'));
// 定义变量内容
const layoutAsideScrollbarRef = ref();
const stores = useRoutesList();
const storesThemeConfig = useThemeConfig();
const storesTagsViewRoutes = useTagsViewRoutes();
const { routesList } = storeToRefs(stores);
const { themeConfig } = storeToRefs(storesThemeConfig);
const { isTagsViewCurrenFull } = storeToRefs(storesTagsViewRoutes);
const state = reactive({
menuList: [],
clientWidth: 0,
});
// 设置菜单展开/收起时的宽度
const setCollapseStyle = computed(() => {
const { layout, isCollapse, menuBar } = themeConfig.value;
const asideBrTheme = ['#FFFFFF', '#FFF', '#fff', '#ffffff'];
const asideBrColor = asideBrTheme.includes(menuBar) ? 'layout-el-aside-br-color' : '';
// 判断是否是手机端
if (state.clientWidth <= 1000) {
if (isCollapse) {
document.body.setAttribute('class', 'el-popup-parent--hidden');
const asideEle = document.querySelector('.layout-container');
const modeDivs = document.createElement('div');
modeDivs.setAttribute('class', 'layout-aside-mobile-mode');
asideEle.appendChild(modeDivs);
modeDivs.addEventListener('click', closeLayoutAsideMobileMode);
return [asideBrColor, 'layout-aside-mobile', 'layout-aside-mobile-open'];
} else {
// 关闭弹窗
closeLayoutAsideMobileMode();
return [asideBrColor, 'layout-aside-mobile', 'layout-aside-mobile-close'];
}
} else {
if (layout === 'columns') {
// 分栏布局,菜单收起时宽度给 1px
if (isCollapse) return [asideBrColor, 'layout-aside-pc-1'];
else return [asideBrColor, 'layout-aside-pc-220'];
} else {
// 其它布局给 64px
if (isCollapse) return [asideBrColor, 'layout-aside-pc-64'];
else return [asideBrColor, 'layout-aside-pc-220'];
}
}
});
// 设置显示/隐藏 logo
const setShowLogo = computed(() => {
let { layout, isShowLogo } = themeConfig.value;
return (isShowLogo && layout === 'defaults') || (isShowLogo && layout === 'columns');
});
// 关闭移动端蒙版
const closeLayoutAsideMobileMode = () => {
const el = document.querySelector('.layout-aside-mobile-mode');
el?.setAttribute('style', 'animation: error-img-two 0.3s');
setTimeout(() => {
el?.parentNode?.removeChild(el);
}, 300);
const clientWidth = document.body.clientWidth;
if (clientWidth < 1000) themeConfig.value.isCollapse = false;
document.body.setAttribute('class', '');
};
// 设置/过滤路由(非静态路由/是否显示在菜单中)
const setFilterRoutes = () => {
if (themeConfig.value.layout === 'columns') return false;
state.menuList = filterRoutesFun(routesList.value);
};
// 路由过滤递归函数
const filterRoutesFun = (arr) => {
return arr
.filter((item) => !item.meta?.isHide)
.map((item) => {
item = Object.assign({}, item);
if (item.children) item.children = filterRoutesFun(item.children);
return item;
});
};
// 设置菜单导航是否固定(移动端)
const initMenuFixed = (clientWidth) => {
state.clientWidth = clientWidth;
};
// 鼠标移入、移出
const onAsideEnterLeave = (bool) => {
let { layout } = themeConfig.value;
if (layout !== 'columns') return false;
if (!bool) mittBus.emit('restoreDefault');
stores.setColumnsMenuHover(bool);
};
// 页面加载前
onBeforeMount(() => {
initMenuFixed(document.body.clientWidth);
setFilterRoutes();
// 此界面不需要取消监听(mittBus.off('setSendColumnsChildren))
// 因为切换布局时有的监听需要使用,取消了监听,某些操作将不生效
mittBus.on('setSendColumnsChildren', (res) => {
state.menuList = res.children;
});
mittBus.on('setSendClassicChildren', (res) => {
let { layout, isClassicSplitMenu } = themeConfig.value;
if (layout === 'classic' && isClassicSplitMenu) {
state.menuList = [];
state.menuList = res.children;
}
});
mittBus.on('getBreadcrumbIndexSetFilterRoutes', () => {
setFilterRoutes();
});
mittBus.on('layoutMobileResize', (res) => {
initMenuFixed(res.clientWidth);
closeLayoutAsideMobileMode();
});
});
// 监听 themeConfig 配置文件的变化,更新菜单 el-scrollbar 的高度
watch(themeConfig.value, (val) => {
if (val.isShowLogoChange !== val.isShowLogo) {
if (layoutAsideScrollbarRef.value) layoutAsideScrollbarRef.value.update();
}
});
// 监听 pinia 值的变化,动态赋值给菜单中
watch(
pinia.state,
(val) => {
let { layout, isClassicSplitMenu } = val.themeConfig.themeConfig;
if (layout === 'classic' && isClassicSplitMenu) return false;
setFilterRoutes();
},
{
deep: true,
}
);
</script>
<template>
<div class="layout-columns-aside">
<el-scrollbar>
<ul @mouseleave="onColumnsAsideMenuMouseleave()">
<li
v-for="(v, k) in state.columnsAsideList"
:key="k"
@click="onColumnsAsideMenuClick(v, k)"
@mouseenter="onColumnsAsideMenuMouseenter(v, k)"
:ref="
(el) => {
if (el) columnsAsideOffsetTopRefs[k] = el;
}
"
:class="{ 'layout-columns-active': state.liIndex === k, 'layout-columns-hover': state.liHoverIndex === k }"
:title="v.meta.title"
>
<div :class="themeConfig.columnsAsideLayout" v-if="!v.meta.isLink || (v.meta.isLink && v.meta.isIframe)">
<SvgIcon :name="v.meta.icon" />
<div class="columns-vertical-title font12">
{{
v.meta.title && v.meta.title.length >= 4
? v.meta.title.substr(0, themeConfig.columnsAsideLayout === 'columns-vertical' ? 4 : 3)
: v.meta.title
}}
</div>
</div>
<div :class="themeConfig.columnsAsideLayout" v-else>
<a :href="v.meta.isLink" target="_blank">
<SvgIcon :name="v.meta.icon" />
<div class="columns-vertical-title font12">
{{
v.meta.title && v.meta.title.length >= 4
? v.meta.title.substr(0, themeConfig.columnsAsideLayout === 'columns-vertical' ? 4 : 3)
: v.meta.title
}}
</div>
</a>
</div>
</li>
<div ref="columnsAsideActiveRef" :class="themeConfig.columnsAsideStyle"></div>
</ul>
</el-scrollbar>
</div>
</template>
<script setup name="layoutColumnsAside">
import { onBeforeRouteUpdate } from 'vue-router';
import { storeToRefs } from 'pinia';
import pinia from '/@/stores/index';
import { useRoutesList } from '/@/stores/routesList';
import { useThemeConfig } from '/@/stores/themeConfig';
import mittBus from '/@/utils/mitt';
// 定义变量内容
const columnsAsideOffsetTopRefs = ref([]);
const columnsAsideActiveRef = ref();
const stores = useRoutesList();
const storesThemeConfig = useThemeConfig();
const { routesList, isColumnsMenuHover, isColumnsNavHover } = storeToRefs(stores);
const { themeConfig } = storeToRefs(storesThemeConfig);
const route = useRoute();
const router = useRouter();
const state = reactive({
columnsAsideList: [],
liIndex: 0,
liOldIndex: null,
liHoverIndex: null,
liOldPath: null,
difference: 0,
routeSplit: [],
});
// 设置菜单高亮位置移动
const setColumnsAsideMove = (k) => {
state.liIndex = k;
columnsAsideActiveRef.value.style.top = `${columnsAsideOffsetTopRefs.value[k].offsetTop + state.difference}px`;
};
// 菜单高亮点击事件
const onColumnsAsideMenuClick = (v, k) => {
setColumnsAsideMove(k);
let { path, redirect } = v;
if (redirect) router.push(redirect);
else router.push(path);
};
// 鼠标移入时,显示当前的子级菜单
const onColumnsAsideMenuMouseenter = (v, k) => {
if (!themeConfig.value.isColumnsMenuHoverPreload) return false;
let { path } = v;
state.liOldPath = path;
state.liOldIndex = k;
state.liHoverIndex = k;
mittBus.emit('setSendColumnsChildren', setSendChildren(path));
stores.setColumnsMenuHover(false);
stores.setColumnsNavHover(true);
};
// 鼠标移走时,显示原来的子级菜单
const onColumnsAsideMenuMouseleave = async () => {
await stores.setColumnsNavHover(false);
// 添加延时器,防止拿到的 store.state.routesList 值不是最新的
setTimeout(() => {
if (!isColumnsMenuHover && !isColumnsNavHover) mittBus.emit('restoreDefault');
}, 100);
};
// 设置高亮动态位置
const onColumnsAsideDown = (k) => {
nextTick(() => {
setColumnsAsideMove(k);
});
};
// 设置/过滤路由(非静态路由/是否显示在菜单中)
const setFilterRoutes = () => {
state.columnsAsideList = filterRoutesFun(routesList.value);
const resData = setSendChildren(route.path);
if (Object.keys(resData).length <= 0) return false;
onColumnsAsideDown(resData.item?.k);
mittBus.emit('setSendColumnsChildren', resData);
};
// 传送当前子级数据到菜单中
const setSendChildren = (path) => {
const currentPathSplit = path.split('/');
let currentData = { children: [] };
state.columnsAsideList.map((v, k) => {
if (v.path === `/${currentPathSplit[1]}`) {
v['k'] = k;
currentData['item'] = { ...v };
currentData['children'] = [{ ...v }];
if (v.children) currentData['children'] = v.children;
}
});
return currentData;
};
// 路由过滤递归函数
const filterRoutesFun = (arr) => {
return arr
.filter((item) => !item.meta?.isHide)
.map((item) => {
item = Object.assign({}, item);
if (item.children) item.children = filterRoutesFun(item.children);
return item;
});
};
// tagsView 点击时,根据路由查找下标 columnsAsideList,实现左侧菜单高亮
const setColumnsMenuHighlight = (path) => {
state.routeSplit = path.split('/');
state.routeSplit.shift();
const routeFirst = `/${state.routeSplit[0]}`;
const currentSplitRoute = state.columnsAsideList.find((v) => v.path === routeFirst);
if (!currentSplitRoute) return false;
// 延迟拿值,防止取不到
setTimeout(() => {
onColumnsAsideDown(currentSplitRoute.k);
}, 0);
};
// 页面加载时
onMounted(() => {
setFilterRoutes();
// 销毁变量,防止鼠标再次移入时,保留了上次的记录
mittBus.on('restoreDefault', () => {
state.liOldIndex = null;
state.liOldPath = null;
});
});
// 页面卸载时
onUnmounted(() => {
mittBus.off('restoreDefault', () => {});
});
// 路由更新时
onBeforeRouteUpdate((to) => {
setColumnsMenuHighlight(to.path);
mittBus.emit('setSendColumnsChildren', setSendChildren(to.path));
});
// 监听布局配置信息的变化,动态增加菜单高亮位置移动像素
watch(
pinia.state,
(val) => {
val.themeConfig.themeConfig.columnsAsideStyle === 'columnsRound' ? (state.difference = 3) : (state.difference = 0);
if (!val.routesList.isColumnsMenuHover && !val.routesList.isColumnsNavHover) {
state.liHoverIndex = null;
mittBus.emit('setSendColumnsChildren', setSendChildren(route.path));
} else {
state.liHoverIndex = state.liOldIndex;
if (!state.liOldPath) return false;
mittBus.emit('setSendColumnsChildren', setSendChildren(state.liOldPath));
}
},
{
deep: true,
}
);
</script>
<style scoped lang="scss">
.layout-columns-aside {
width: 70px;
height: 100%;
background: var(--next-bg-columnsMenuBar);
ul {
position: relative;
.layout-columns-active {
color: var(--next-bg-columnsMenuBarColor) !important;
transition: 0.3s ease-in-out;
}
.layout-columns-hover {
color: var(--el-color-primary);
a {
color: var(--el-color-primary);
}
}
li {
color: var(--next-bg-columnsMenuBarColor);
width: 100%;
height: 50px;
text-align: center;
display: flex;
cursor: pointer;
position: relative;
z-index: 1;
&:hover {
@extend .layout-columns-hover;
}
.columns-vertical {
margin: auto;
.columns-vertical-title {
padding-top: 1px;
}
}
.columns-horizontal {
display: flex;
height: 50px;
width: 100%;
align-items: center;
padding: 0 5px;
i {
margin-right: 3px;
}
a {
display: flex;
.columns-horizontal-title {
padding-top: 1px;
}
}
}
a {
text-decoration: none;
color: var(--next-bg-columnsMenuBarColor);
}
}
.columns-round {
background: var(--el-color-primary);
color: var(--el-color-white);
position: absolute;
left: 50%;
top: 2px;
height: 44px;
width: 65px;
transform: translateX(-50%);
z-index: 0;
transition: 0.3s ease-in-out;
border-radius: 5px;
}
.columns-card {
@extend .columns-round;
top: 0;
height: 50px;
width: 100%;
border-radius: 0;
}
}
}
</style>
<template>
<el-header class="layout-header" v-show="!isTagsViewCurrenFull">
<NavBarsIndex />
</el-header>
</template>
<script setup name="layoutHeader">
import { storeToRefs } from 'pinia';
import { useTagsViewRoutes } from '/@/stores/tagsViewRoutes';
// 引入组件
const NavBarsIndex = defineAsyncComponent(() => import('/@/layout/navBars/index.vue'));
// 定义变量内容
const storesTagsViewRoutes = useTagsViewRoutes();
const { isTagsViewCurrenFull } = storeToRefs(storesTagsViewRoutes);
</script>
<template>
<el-main class="layout-main" :style="isFixedHeader ? `height: calc(100% - ${setMainHeight})` : `minHeight: calc(100% - ${setMainHeight})`">
<el-scrollbar
ref="layoutMainScrollbarRef"
class="layout-main-scroll layout-backtop-header-fixed"
wrap-class="layout-main-scroll"
view-class="layout-main-scroll"
>
<LayoutParentView />
<LayoutFooter v-if="isFooter" />
</el-scrollbar>
<el-backtop :target="setBacktopClass" />
</el-main>
</template>
<script setup name="layoutMain">
import { storeToRefs } from 'pinia';
import { useTagsViewRoutes } from '/@/stores/tagsViewRoutes';
import { useThemeConfig } from '/@/stores/themeConfig';
import { NextLoading } from '/@/utils/loading';
// 引入组件
const LayoutParentView = defineAsyncComponent(() => import('/@/layout/routerView/parent.vue'));
const LayoutFooter = defineAsyncComponent(() => import('/@/layout/footer/index.vue'));
// 定义变量内容
const layoutMainScrollbarRef = ref();
const route = useRoute();
const storesTagsViewRoutes = useTagsViewRoutes();
const storesThemeConfig = useThemeConfig();
const { themeConfig } = storeToRefs(storesThemeConfig);
const { isTagsViewCurrenFull } = storeToRefs(storesTagsViewRoutes);
// 设置 footer 显示/隐藏
const isFooter = computed(() => {
return themeConfig.value.isFooter && !route.meta.isIframe;
});
// 设置 header 固定
const isFixedHeader = computed(() => {
return themeConfig.value.isFixedHeader;
});
// 设置 Backtop 回到顶部
const setBacktopClass = computed(() => {
if (themeConfig.value.isFixedHeader) return `.layout-backtop-header-fixed .el-scrollbar__wrap`;
else return `.layout-backtop .el-scrollbar__wrap`;
});
// 设置主内容区的高度
const setMainHeight = computed(() => {
if (isTagsViewCurrenFull.value) return '0px';
const { isTagsview, layout } = themeConfig.value;
if (isTagsview && layout !== 'classic') return '85px';
else return '51px';
});
// 页面加载前
onMounted(() => {
NextLoading.done(600);
});
// 暴露变量
defineExpose({
layoutMainScrollbarRef,
});
</script>
<template>
<div class="layout-footer pb15">
<div class="layout-footer-warp">
<div>vue-next-admin</div>
<div class="mt5">深圳市北科瑞声科技股份公司版权所有</div>
</div>
</div>
</template>
<script setup name="layoutFooter">
// 此处需有内容(注释也得),否则缓存将失败
</script>
<style scoped lang="scss">
.layout-footer {
width: 100%;
display: flex;
&-warp {
margin: auto;
color: var(--el-text-color-secondary);
text-align: center;
animation: error-num 0.3s ease;
}
}
</style>
<template>
<component :is="layouts[themeConfig.layout]" />
</template>
<script setup name="layout">
import { storeToRefs } from 'pinia';
import { useThemeConfig } from '/@/stores/themeConfig';
import { Local } from '/@/utils/storage';
import mittBus from '/@/utils/mitt';
// 引入组件
const layouts = {
defaults: defineAsyncComponent(() => import('/@/layout/main/defaults.vue')),
classic: defineAsyncComponent(() => import('/@/layout/main/classic.vue')),
transverse: defineAsyncComponent(() => import('/@/layout/main/transverse.vue')),
columns: defineAsyncComponent(() => import('/@/layout/main/columns.vue')),
};
// 定义变量内容
const storesThemeConfig = useThemeConfig();
const { themeConfig } = storeToRefs(storesThemeConfig);
// 窗口大小改变时(适配移动端)
const onLayoutResize = () => {
if (!Local.get('oldLayout')) Local.set('oldLayout', themeConfig.value.layout);
const clientWidth = document.body.clientWidth;
if (clientWidth < 1000) {
themeConfig.value.isCollapse = false;
mittBus.emit('layoutMobileResize', {
layout: 'defaults',
clientWidth,
});
} else {
mittBus.emit('layoutMobileResize', {
layout: Local.get('oldLayout') ? Local.get('oldLayout') : themeConfig.value.layout,
clientWidth,
});
}
};
// 页面加载前
onBeforeMount(() => {
onLayoutResize();
window.addEventListener('resize', onLayoutResize);
});
// 页面卸载时
onUnmounted(() => {
window.removeEventListener('resize', onLayoutResize);
});
</script>
<template>
<div v-show="state.isShowLockScreen">
<div class="layout-lock-screen-mask"></div>
<div class="layout-lock-screen-img" :class="{ 'layout-lock-screen-filter': state.isShowLoockLogin }"></div>
<div class="layout-lock-screen">
<div
class="layout-lock-screen-date"
ref="layoutLockScreenDateRef"
@mousedown="onDownPc"
@mousemove="onMovePc"
@mouseup="onEnd"
@touchstart.stop="onDownApp"
@touchmove.stop="onMoveApp"
@touchend.stop="onEnd"
>
<div class="layout-lock-screen-date-box">
<div class="layout-lock-screen-date-box-time">
{{ state.time.hm }}<span class="layout-lock-screen-date-box-minutes">{{ state.time.s }}</span>
</div>
<div class="layout-lock-screen-date-box-info">{{ state.time.mdq }}</div>
</div>
<div class="layout-lock-screen-date-top">
<SvgIcon name="ele-Top" />
<div class="layout-lock-screen-date-top-text">上滑解锁</div>
</div>
</div>
<transition name="el-zoom-in-center">
<div v-show="state.isShowLoockLogin" class="layout-lock-screen-login">
<div class="layout-lock-screen-login-box">
<div class="layout-lock-screen-login-box-img">
<!-- <img src="https://img2.baidu.com/it/u=1978192862,2048448374&fm=253&fmt=auto&app=138&f=JPEG?w=504&h=500" /> -->
</div>
<div class="layout-lock-screen-login-box-name">Administrator</div>
<div class="layout-lock-screen-login-box-value">
<el-input
placeholder="请输入密码"
ref="layoutLockScreenInputRef"
v-model="state.lockScreenPassword"
@keyup.enter.native.stop="onLockScreenSubmit()"
>
<template #append>
<el-button @click="onLockScreenSubmit">
<el-icon class="el-input__icon">
<ele-Right />
</el-icon>
</el-button>
</template>
</el-input>
</div>
</div>
<div class="layout-lock-screen-login-icon">
<SvgIcon name="ele-Microphone" :size="20" />
<SvgIcon name="ele-AlarmClock" :size="20" />
<SvgIcon name="ele-SwitchButton" :size="20" />
</div>
</div>
</transition>
</div>
</div>
</template>
<script setup name="layoutLockScreen">
import { formatDate } from '/@/utils/formatTime';
import { Local } from '/@/utils/storage';
import { storeToRefs } from 'pinia';
import { useThemeConfig } from '/@/stores/themeConfig';
// 定义变量内容
const layoutLockScreenDateRef = ref();
const layoutLockScreenInputRef = ref();
const storesThemeConfig = useThemeConfig();
const { themeConfig } = storeToRefs(storesThemeConfig);
const state = reactive({
transparency: 1,
downClientY: 0,
moveDifference: 0,
isShowLoockLogin: false,
isFlags: false,
querySelectorEl: '',
time: {
hm: '',
s: '',
mdq: '',
},
setIntervalTime: 0,
isShowLockScreen: false,
isShowLockScreenIntervalTime: 0,
lockScreenPassword: '',
});
// 鼠标按下 pc
const onDownPc = (down) => {
state.isFlags = true;
state.downClientY = down.clientY;
};
// 鼠标按下 app
const onDownApp = (down) => {
state.isFlags = true;
state.downClientY = down.touches[0].clientY;
};
// 鼠标移动 pc
const onMovePc = (move) => {
state.moveDifference = move.clientY - state.downClientY;
onMove();
};
// 鼠标移动 app
const onMoveApp = (move) => {
state.moveDifference = move.touches[0].clientY - state.downClientY;
onMove();
};
// 鼠标移动事件
const onMove = () => {
if (state.isFlags) {
const el = state.querySelectorEl;
const opacitys = (state.transparency -= 1 / 200);
if (state.moveDifference >= 0) return false;
el.setAttribute('style', `top:${state.moveDifference}px;cursor:pointer;opacity:${opacitys};`);
if (state.moveDifference < -400) {
el.setAttribute('style', `top:${-el.clientHeight}px;cursor:pointer;transition:all 0.3s ease;`);
state.moveDifference = -el.clientHeight;
setTimeout(() => {
el && el.parentNode?.removeChild(el);
}, 300);
}
if (state.moveDifference === -el.clientHeight) {
state.isShowLoockLogin = true;
layoutLockScreenInputRef.value.focus();
}
}
};
// 鼠标松开
const onEnd = () => {
state.isFlags = false;
state.transparency = 1;
if (state.moveDifference >= -400) {
state.querySelectorEl.setAttribute('style', `top:0px;opacity:1;transition:all 0.3s ease;`);
}
};
// 获取要拖拽的初始元素
const initGetElement = () => {
nextTick(() => {
state.querySelectorEl = layoutLockScreenDateRef.value;
});
};
// 时间初始化
const initTime = () => {
state.time.hm = formatDate(new Date(), 'HH:MM');
state.time.s = formatDate(new Date(), 'SS');
state.time.mdq = formatDate(new Date(), 'mm月dd日,WWW');
};
// 时间初始化定时器
const initSetTime = () => {
initTime();
state.setIntervalTime = window.setInterval(() => {
initTime();
}, 1000);
};
// 锁屏时间定时器
const initLockScreen = () => {
if (themeConfig.value.isLockScreen) {
state.isShowLockScreenIntervalTime = window.setInterval(() => {
if (themeConfig.value.lockScreenTime <= 1) {
state.isShowLockScreen = true;
setLocalThemeConfig();
return false;
}
themeConfig.value.lockScreenTime--;
}, 1000);
} else {
clearInterval(state.isShowLockScreenIntervalTime);
}
};
// 存储布局配置
const setLocalThemeConfig = () => {
themeConfig.value.isDrawer = false;
Local.set('themeConfig', themeConfig.value);
};
// 密码输入点击事件
const onLockScreenSubmit = () => {
themeConfig.value.isLockScreen = false;
themeConfig.value.lockScreenTime = 30;
setLocalThemeConfig();
};
// 页面加载时
onMounted(() => {
initGetElement();
initSetTime();
initLockScreen();
});
// 页面卸载时
onUnmounted(() => {
window.clearInterval(state.setIntervalTime);
window.clearInterval(state.isShowLockScreenIntervalTime);
});
</script>
<style scoped lang="scss">
.layout-lock-screen-fixed {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
}
.layout-lock-screen-filter {
filter: blur(1px);
}
.layout-lock-screen-mask {
background: var(--el-color-white);
@extend .layout-lock-screen-fixed;
z-index: 9999990;
}
.layout-lock-screen-img {
@extend .layout-lock-screen-fixed;
background-image: url('https://img-blog.csdnimg.cn/afa9c317667f47d5bea34b85af45979e.png#pic_center');
background-size: 100% 100%;
z-index: 9999991;
}
.layout-lock-screen {
@extend .layout-lock-screen-fixed;
z-index: 9999992;
&-date {
position: absolute;
left: 0;
top: 0;
width: 100%;
height: 100%;
color: var(--el-color-white);
z-index: 9999993;
user-select: none;
&-box {
position: absolute;
left: 30px;
bottom: 50px;
&-time {
font-size: 100px;
color: var(--el-color-white);
}
&-info {
font-size: 40px;
color: var(--el-color-white);
}
&-minutes {
font-size: 16px;
}
}
&-top {
width: 40px;
height: 40px;
line-height: 40px;
border-radius: 100%;
border: 1px solid var(--el-border-color-light, #ebeef5);
background: rgba(255, 255, 255, 0.1);
color: var(--el-color-white);
opacity: 0.8;
position: absolute;
right: 30px;
bottom: 50px;
text-align: center;
overflow: hidden;
transition: all 0.3s ease;
i {
transition: all 0.3s ease;
}
&-text {
opacity: 0;
position: absolute;
top: 150%;
font-size: 12px;
color: var(--el-color-white);
left: 50%;
line-height: 1.2;
transform: translate(-50%, -50%);
transition: all 0.3s ease;
width: 35px;
}
&:hover {
border: 1px solid rgba(255, 255, 255, 0.5);
background: rgba(255, 255, 255, 0.2);
box-shadow: 0 0 12px 0 rgba(255, 255, 255, 0.5);
color: var(--el-color-white);
opacity: 1;
transition: all 0.3s ease;
i {
transform: translateY(-40px);
transition: all 0.3s ease;
}
.layout-lock-screen-date-top-text {
opacity: 1;
top: 50%;
transition: all 0.3s ease;
}
}
}
}
&-login {
position: relative;
z-index: 9999994;
width: 100%;
height: 100%;
left: 0;
top: 0;
display: flex;
flex-direction: column;
justify-content: center;
color: var(--el-color-white);
&-box {
text-align: center;
margin: auto;
&-img {
width: 180px;
height: 180px;
margin: auto;
img {
width: 100%;
height: 100%;
border-radius: 100%;
}
}
&-name {
font-size: 26px;
margin: 15px 0 30px;
}
}
&-icon {
position: absolute;
right: 30px;
bottom: 30px;
i {
font-size: 20px;
margin-left: 15px;
cursor: pointer;
opacity: 0.8;
&:hover {
opacity: 1;
}
}
}
}
}
:deep(.el-input-group__append) {
background: var(--el-color-white);
padding: 0px 15px;
}
:deep(.el-input__inner) {
border-right-color: var(--el-border-color-extra-light);
&:hover {
border-color: var(--el-border-color-extra-light);
}
}
</style>
<template>
<div class="layout-logo" v-if="setShowLogo" @click="onThemeConfigChange">
<!-- <img :src="logoMini" class="layout-logo-medium-img" /> -->
<span style="width: 100%;text-align: center;">企业产品库</span>
</div>
<div class="layout-logo-size" v-else @click="onThemeConfigChange">
<img :src="logoMini" class="layout-logo-size-img" />
</div>
</template>
<script setup name="layoutLogo">
import { storeToRefs } from 'pinia';
import { useThemeConfig } from '/@/stores/themeConfig';
import logoMini from '/@/assets/logo-mini.svg';
// 定义变量内容
const storesThemeConfig = useThemeConfig();
const { themeConfig } = storeToRefs(storesThemeConfig);
// 设置 logo 的显示。classic 经典布局默认显示 logo
const setShowLogo = computed(() => {
let { isCollapse, layout } = themeConfig.value;
return !isCollapse || layout === 'classic' || document.body.clientWidth < 1000;
});
// logo 点击实现菜单展开/收起
const onThemeConfigChange = () => {
if (themeConfig.value.layout === 'transverse') return false;
themeConfig.value.isCollapse = !themeConfig.value.isCollapse;
};
</script>
<style scoped lang="scss">
.layout-logo-medium-img{
width: 30px !important;
}
.layout-logo {
width: 220px;
height: 50px;
display: flex;
align-items: center;
justify-content: left;
box-shadow: rgb(0 21 41 / 2%) 0px 1px 4px;
color: #ffffff;
font-size: 17px;
font-weight: bold;
cursor: pointer;
animation: logoAnimation 0.3s ease-in-out;
span {
white-space: nowrap;
display: inline-block;
}
&:hover {
span {
color: var(--color-primary-light-2);
}
}
&-medium-img {
width: 20px;
margin-right: 5px;
}
}
.layout-logo-size {
width: 100%;
height: 50px;
display: flex;
cursor: pointer;
animation: logoAnimation 0.3s ease-in-out;
&-img {
width: 20px;
margin: auto;
}
&:hover {
img {
animation: logoAnimation 0.3s ease-in-out;
}
}
}
</style>
<template>
<el-container class="layout-container flex-center">
<LayoutHeader />
<el-container class="layout-mian-height-50">
<LayoutAside />
<div class="flex-center layout-backtop">
<LayoutTagsView v-if="isTagsview" />
<LayoutMain ref="layoutMainRef" />
</div>
</el-container>
</el-container>
</template>
<script setup name="layoutClassic">
import { storeToRefs } from 'pinia';
import { useThemeConfig } from '/@/stores/themeConfig';
// 引入组件
const LayoutAside = defineAsyncComponent(() => import('/@/layout/component/aside.vue'));
const LayoutHeader = defineAsyncComponent(() => import('/@/layout/component/header.vue'));
const LayoutMain = defineAsyncComponent(() => import('/@/layout/component/main.vue'));
const LayoutTagsView = defineAsyncComponent(() => import('/@/layout/navBars/tagsView/tagsView.vue'));
// 定义变量内容
const layoutMainRef = ref();
const route = useRoute();
const storesThemeConfig = useThemeConfig();
const { themeConfig } = storeToRefs(storesThemeConfig);
// 判断是否显示 tasgview
const isTagsview = computed(() => {
return themeConfig.value.isTagsview;
});
// 重置滚动条高度,更新子级 scrollbar
const updateScrollbar = () => {
layoutMainRef.value?.layoutMainScrollbarRef.update();
};
// 重置滚动条高度,由于组件是异步引入的
const initScrollBarHeight = () => {
nextTick(() => {
setTimeout(() => {
updateScrollbar();
// '!' not null 断言操作符,不执行运行时检查
layoutMainRef.value.layoutMainScrollbarRef.wrapRef.scrollTop = 0;
}, 500);
});
};
// 页面加载时
onMounted(() => {
initScrollBarHeight();
});
// 监听路由的变化,切换界面时,滚动条置顶
watch(
() => route.path,
() => {
initScrollBarHeight();
}
);
// 监听 themeConfig 配置文件的变化,更新菜单 el-scrollbar 的高度
watch(
themeConfig,
() => {
updateScrollbar();
},
{
deep: true,
}
);
</script>
<template>
<el-container class="layout-container">
<ColumnsAside />
<el-container class="layout-columns-warp layout-container-view h100">
<LayoutAside />
<el-scrollbar ref="layoutScrollbarRef" class="layout-backtop">
<LayoutHeader />
<LayoutMain ref="layoutMainRef" />
</el-scrollbar>
</el-container>
</el-container>
</template>
<script setup name="layoutColumns">
import { storeToRefs } from 'pinia';
import { useThemeConfig } from '/@/stores/themeConfig';
// 引入组件
const LayoutAside = defineAsyncComponent(() => import('/@/layout/component/aside.vue'));
const LayoutHeader = defineAsyncComponent(() => import('/@/layout/component/header.vue'));
const LayoutMain = defineAsyncComponent(() => import('/@/layout/component/main.vue'));
const ColumnsAside = defineAsyncComponent(() => import('/@/layout/component/columnsAside.vue'));
// 定义变量内容
const layoutScrollbarRef = ref('');
const layoutMainRef = ref();
const route = useRoute();
const storesThemeConfig = useThemeConfig();
const { themeConfig } = storeToRefs(storesThemeConfig);
// 重置滚动条高度
const updateScrollbar = () => {
// 更新父级 scrollbar
layoutScrollbarRef.value.update();
// 更新子级 scrollbar
layoutMainRef.value.layoutMainScrollbarRef.update();
};
// 重置滚动条高度,由于组件是异步引入的
const initScrollBarHeight = () => {
nextTick(() => {
setTimeout(() => {
updateScrollbar();
layoutScrollbarRef.value.wrapRef.scrollTop = 0;
layoutMainRef.value.layoutMainScrollbarRef.wrapRef.scrollTop = 0;
}, 500);
});
};
// 页面加载时
onMounted(() => {
initScrollBarHeight();
});
// 监听路由的变化,切换界面时,滚动条置顶
watch(
() => route.path,
() => {
initScrollBarHeight();
}
);
// 监听 themeConfig 配置文件的变化,更新菜单 el-scrollbar 的高度
watch(
themeConfig,
() => {
updateScrollbar();
},
{
deep: true,
}
);
</script>
<template>
<el-container class="layout-container">
<LayoutAside />
<el-container class="layout-container-view h100">
<el-scrollbar ref="layoutScrollbarRef" class="layout-backtop">
<LayoutHeader />
<LayoutMain ref="layoutMainRef" />
</el-scrollbar>
</el-container>
</el-container>
</template>
<script setup name="layoutDefaults">
import { storeToRefs } from 'pinia';
import { useThemeConfig } from '/@/stores/themeConfig';
import { NextLoading } from '/@/utils/loading';
// 引入组件
const LayoutAside = defineAsyncComponent(() => import('/@/layout/component/aside.vue'));
const LayoutHeader = defineAsyncComponent(() => import('/@/layout/component/header.vue'));
const LayoutMain = defineAsyncComponent(() => import('/@/layout/component/main.vue'));
// 定义变量内容
const layoutScrollbarRef = ref('');
const layoutMainRef = ref();
const route = useRoute();
const storesThemeConfig = useThemeConfig();
const { themeConfig } = storeToRefs(storesThemeConfig);
// 重置滚动条高度
const updateScrollbar = () => {
// 更新父级 scrollbar
layoutScrollbarRef.value.update();
// 更新子级 scrollbar
layoutMainRef.value.layoutMainScrollbarRef.update();
};
// 重置滚动条高度,由于组件是异步引入的
const initScrollBarHeight = () => {
nextTick(() => {
setTimeout(() => {
updateScrollbar();
layoutScrollbarRef.value.wrapRef.scrollTop = 0;
layoutMainRef.value.layoutMainScrollbarRef.wrapRef.scrollTop = 0;
}, 500);
});
};
// 页面加载时
onMounted(() => {
initScrollBarHeight();
NextLoading.done(600);
});
// 监听路由的变化,切换界面时,滚动条置顶
watch(
() => route.path,
() => {
initScrollBarHeight();
}
);
// 监听 themeConfig 配置文件的变化,更新菜单 el-scrollbar 的高度
watch(
themeConfig,
() => {
updateScrollbar();
},
{
deep: true,
}
);
</script>
<template>
<el-container class="layout-container flex-center layout-backtop">
<LayoutHeader />
<LayoutMain ref="layoutMainRef" />
</el-container>
</template>
<script setup name="layoutTransverse">
import { storeToRefs } from 'pinia';
import { useThemeConfig } from '/@/stores/themeConfig';
// 引入组件
const LayoutHeader = defineAsyncComponent(() => import('/@/layout/component/header.vue'));
const LayoutMain = defineAsyncComponent(() => import('/@/layout/component/main.vue'));
// 定义变量内容
const layoutMainRef = ref();
const storesThemeConfig = useThemeConfig();
const { themeConfig } = storeToRefs(storesThemeConfig);
const route = useRoute();
// 重置滚动条高度,更新子级 scrollbar
const updateScrollbar = () => {
layoutMainRef.value.layoutMainScrollbarRef.update();
};
// 重置滚动条高度,由于组件是异步引入的
const initScrollBarHeight = () => {
nextTick(() => {
setTimeout(() => {
updateScrollbar();
layoutMainRef.value.layoutMainScrollbarRef.wrapRef.scrollTop = 0;
}, 500);
});
};
// 页面加载时
onMounted(() => {
initScrollBarHeight();
});
// 监听路由的变化,切换界面时,滚动条置顶
watch(
() => route.path,
() => {
initScrollBarHeight();
}
);
// 监听 themeConfig 配置文件的变化,更新菜单 el-scrollbar 的高度
watch(
themeConfig,
() => {
updateScrollbar();
},
{
deep: true,
}
);
</script>
<template>
<div v-if="isShowBreadcrumb" class="layout-navbars-breadcrumb">
<SvgIcon
class="layout-navbars-breadcrumb-icon"
:name="themeConfig.isCollapse ? 'ele-Expand' : 'ele-Fold'"
:size="16"
@click="onThemeConfigChange"
/>
<el-breadcrumb class="layout-navbars-breadcrumb-hide">
<transition-group name="breadcrumb">
<el-breadcrumb-item v-for="(v, k) in state.breadcrumbList" :key="!v.meta.tagsViewName ? v.meta.title : v.meta.tagsViewName">
<span v-if="k === state.breadcrumbList.length - 1" class="layout-navbars-breadcrumb-span">
<SvgIcon :name="v.meta.icon" class="layout-navbars-breadcrumb-iconfont" v-if="themeConfig.isBreadcrumbIcon" />
<div v-if="!v.meta.tagsViewName">{{ v.meta.title }}</div>
<div v-else>{{ v.meta.tagsViewName }}</div>
</span>
<a v-else @click.prevent="onBreadcrumbClick(v)">
<SvgIcon :name="v.meta.icon" class="layout-navbars-breadcrumb-iconfont" v-if="themeConfig.isBreadcrumbIcon" />{{ v.meta.title }}
</a>
</el-breadcrumb-item>
</transition-group>
</el-breadcrumb>
</div>
</template>
<script setup name="layoutBreadcrumb">
import { onBeforeRouteUpdate } from 'vue-router';
import { Local } from '/@/utils/storage';
import other from '/@/utils/other';
import { storeToRefs } from 'pinia';
import { useThemeConfig } from '/@/stores/themeConfig';
import { useRoutesList } from '/@/stores/routesList';
// 定义变量内容
const stores = useRoutesList();
const storesThemeConfig = useThemeConfig();
const { themeConfig } = storeToRefs(storesThemeConfig);
const { routesList } = storeToRefs(stores);
const route = useRoute();
const router = useRouter();
const state = reactive({
breadcrumbList: [],
routeSplit: [],
routeSplitFirst: '',
routeSplitIndex: 1,
});
// 动态设置经典、横向布局不显示
const isShowBreadcrumb = computed(() => {
return true;
initRouteSplit(route.path);
const { layout, isBreadcrumb } = themeConfig.value;
if (layout === 'classic' || layout === 'transverse') return false;
else return isBreadcrumb ? true : false;
});
// 面包屑点击时
const onBreadcrumbClick = (v) => {
const { redirect, path } = v;
if (redirect) router.push(redirect);
else router.push(path);
};
// 展开/收起左侧菜单点击
const onThemeConfigChange = () => {
themeConfig.value.isCollapse = !themeConfig.value.isCollapse;
setLocalThemeConfig();
};
// 存储布局配置
const setLocalThemeConfig = () => {
Local.remove('themeConfig');
Local.set('themeConfig', themeConfig.value);
};
// 处理面包屑数据
const getBreadcrumbList = (arr) => {
arr.forEach((item) => {
state.routeSplit.forEach((v, k, arrs) => {
if (state.routeSplitFirst === item.path) {
state.routeSplitFirst += `/${arrs[state.routeSplitIndex]}`;
state.breadcrumbList.push(item);
state.routeSplitIndex++;
if (item.children) getBreadcrumbList(item.children);
}
});
});
};
// 当前路由字符串切割成数组,并删除第一项空内容
const initRouteSplit = (path) => {
// if (!themeConfig.value.isBreadcrumb) return false;
routesList.value.forEach(item => {
if(path.indexOf(item.path) !== -1){
state.breadcrumbList = [item];
}
})
// state.breadcrumbList = [routesList.value[0]];
state.routeSplit = path.split('/');
state.routeSplit.shift();
state.routeSplitFirst = `/${state.routeSplit[0]}`;
state.routeSplitIndex = 1;
getBreadcrumbList(routesList.value);
// 特殊处理
if (state.routeSplitIndex >= 3 || (route.name === 'notFound' && state.breadcrumbList.length > 1)) state.breadcrumbList.shift();
if (state.breadcrumbList.length > 0) state.breadcrumbList[state.breadcrumbList.length - 1].meta.tagsViewName = other.setTagsViewNameI18n(route);
};
// 页面加载时
onMounted(() => {
initRouteSplit(route.path);
});
// 路由更新时
onBeforeRouteUpdate((to) => {
initRouteSplit(to.path);
});
</script>
<style scoped lang="scss">
.layout-navbars-breadcrumb {
flex: 1;
height: inherit;
display: flex;
align-items: center;
.layout-navbars-breadcrumb-icon {
cursor: pointer;
font-size: 18px;
color: var(--el-color-primary);
height: 100%;
width: 40px;
opacity: 0.8;
&:hover {
opacity: 1;
}
}
.layout-navbars-breadcrumb-span {
display: flex;
opacity: 0.7;
color: var(--el-color-primary);
}
.layout-navbars-breadcrumb-iconfont {
font-size: 14px;
margin-right: 5px;
}
:deep(.el-breadcrumb__separator) {
opacity: 0.7;
color: var(--el-color-primary);
}
:deep(.el-breadcrumb__inner a, .el-breadcrumb__inner.is-link) {
font-weight: unset !important;
color: var(--el-color-primary);
&:hover {
color: var(--el-color-primary) !important;
}
}
}
</style>
<template>
<div class="layout-navbars-close-full" v-if="isTagsViewCurrenFull">
<div class="layout-navbars-close-full-icon">
<SvgIcon name="ele-Close" title="关闭全屏" @click="onCloseFullscreen" />
</div>
</div>
</template>
<script setup name="layoutCloseFull">
import { storeToRefs } from 'pinia';
import { useTagsViewRoutes } from '/@/stores/tagsViewRoutes';
// 定义变量内容
const stores = useTagsViewRoutes();
const { isTagsViewCurrenFull } = storeToRefs(stores);
// 关闭当前全屏
const onCloseFullscreen = () => {
stores.setCurrenFullscreen(false);
};
</script>
<style scoped lang="scss">
.layout-navbars-close-full {
position: fixed;
z-index: 9999999999;
right: -30px;
top: -30px;
.layout-navbars-close-full-icon {
width: 60px;
height: 60px;
border-radius: 100%;
cursor: pointer;
background: rgba(0, 0, 0, 0.1);
transition: all 0.3s ease;
position: relative;
:deep(i) {
position: absolute;
left: 10px;
top: 35px;
color: #333333;
transition: all 0.3s ease;
}
}
&:hover {
transition: all 0.3s ease;
:deep(i) {
color: var(--el-color-primary);
transition: all 0.3s ease;
}
}
}
</style>
<template>
<div v-if="state.dialog.isShowDialog" class="system-role-dialog-container">
<el-dialog :title="state.dialog.title" v-model="state.dialog.isShowDialog" width="500px"
:close-on-click-modal="false">
<el-form ref="roleDialogFormRef" :model="state.ruleForm" size="default" label-width="120px" :rules="state.formRules"
label-position="left" style="min-height: 50px;">
<el-form-item label="原始密码" prop="password">
<el-input v-model="state.ruleForm.password" placeholder="请输入原始密码" type="password" autocomplete="off" :show-password="true"></el-input>
</el-form-item>
<el-form-item label="新密码" prop="newPassword">
<el-input v-model="state.ruleForm.newPassword" placeholder="请输入新密码" type="password"
autocomplete="off" :show-password="true"></el-input>
</el-form-item>
<el-form-item label="确认新密码" prop="confirmNewPassword">
<el-input v-model="state.ruleForm.confirmNewPassword" placeholder="请输入确认新密码" type="password"
autocomplete="off" :show-password="true"></el-input>
</el-form-item>
</el-form>
<template #footer>
<span class="dialog-footer">
<el-button @click="onCancel" size="default">取 消</el-button>
<el-button type="primary" :loading="state.btnLoading" @click="onSubmit" size="default">确认</el-button>
</span>
</template>
</el-dialog>
</div>
</template>
<script setup name="editPasswordDialog">
const validatePass = (rule, value, callback) => {
if (value === '') {
callback(new Error('请输入原始密码'))
} else {
callback()
}
}
const validateNewPass = (rule, value, callback) => {
if (value === '') {
callback(new Error('请输入新密码'))
} else {
if (state.ruleForm.confirmNewPassword !== '') {
if (!roleDialogFormRef.value) return
roleDialogFormRef.value.validateField('confirmNewPassword', () => null)
}
callback()
}
}
const validatePass2 = (rule, value, callback) => {
if (value === '') {
callback(new Error('请输入确认密码'))
} else if (value !== state.ruleForm.newPassword) {
callback(new Error("两次输入不一致"))
} else {
callback()
}
}
import { systemApi } from '/@/api/system'
import { ElMessage } from 'element-plus';
// 定义子组件向父组件传值/事件
const emit = defineEmits(['refresh']);
// 定义变量内容
const roleDialogFormRef = ref();
const state = reactive({
ruleForm: {
password: '',
newPassword: '',
confirmNewPassword: ''
},
formRules: {
password: [{ validator: validatePass, trigger: 'blur' }],
newPassword: [{ validator: validateNewPass, trigger: 'blur' }],
confirmNewPassword: [{ validator: validatePass2, trigger: 'blur' }],
},
dialog: {
isShowDialog: false,
title: '修改密码',
submitTxt: '',
},
btnLoading: false
});
// 打开弹窗
const openDialog = (row) => {
state.dialog.isShowDialog = true;
};
// 关闭弹窗
const closeDialog = () => {
roleDialogFormRef.value.resetFields()
state.dialog.isShowDialog = false;
};
// 取消
const onCancel = () => {
closeDialog();
};
// 提交
const onSubmit = () => {
roleDialogFormRef.value.validate((valid, fields) => {
if (valid) {
state.btnLoading = true
const apiData = JSON.parse(JSON.stringify(state.ruleForm))
systemApi().editPassword(apiData).then(res => {
state.btnLoading = false
ElMessage.success('操作成功');
closeDialog();
setTimeout(() => {
window.location.reload()
}, 1000);
}).catch(() => {
state.btnLoading = false
closeDialog();
})
}
})
};
// 暴露变量
defineExpose({
openDialog,
});
</script>
<style scoped lang="scss"></style>
<template>
<div class="layout-navbars-breadcrumb-index">
<Logo v-if="setIsShowLogo" />
<!-- <Breadcrumb /> -->
<Horizontal :menuList="state.menuList" v-if="isLayoutTransverse" />
<User />
</div>
</template>
<script setup name="layoutBreadcrumbIndex">
import { storeToRefs } from 'pinia';
import { useRoutesList } from '/@/stores/routesList';
import { useThemeConfig } from '/@/stores/themeConfig';
import mittBus from '/@/utils/mitt';
// 引入组件
// const Breadcrumb = defineAsyncComponent(() => import('/@/layout/navBars/breadcrumb/breadcrumb.vue'));
const User = defineAsyncComponent(() => import('/@/layout/navBars/breadcrumb/user.vue'));
const Logo = defineAsyncComponent(() => import('/@/layout/logo/index.vue'));
const Horizontal = defineAsyncComponent(() => import('/@/layout/navMenu/horizontal.vue'));
// 定义变量内容
const stores = useRoutesList();
const storesThemeConfig = useThemeConfig();
const { themeConfig } = storeToRefs(storesThemeConfig);
const { routesList } = storeToRefs(stores);
const route = useRoute();
const state = reactive({
menuList: [],
});
// 设置 logo 显示/隐藏
const setIsShowLogo = computed(() => {
let { isShowLogo, layout } = themeConfig.value;
return (isShowLogo && layout === 'classic') || (isShowLogo && layout === 'transverse');
});
// 设置是否显示横向导航菜单
const isLayoutTransverse = computed(() => {
return true
let { layout, isClassicSplitMenu } = themeConfig.value;
return layout === 'transverse' || (isClassicSplitMenu && layout === 'classic');
});
// 设置/过滤路由(非静态路由/是否显示在菜单中)
const setFilterRoutes = () => {
let { layout, isClassicSplitMenu } = themeConfig.value;
if (layout === 'classic' && isClassicSplitMenu) {
state.menuList = delClassicChildren(filterRoutesFun(routesList.value));
const resData = setSendClassicChildren(route.path);
mittBus.emit('setSendClassicChildren', resData);
} else {
state.menuList = filterRoutesFun(routesList.value);
}
};
// 设置了分割菜单时,删除底下 children
const delClassicChildren = (arr) => {
arr.map((v) => {
if (v.children) delete v.children;
});
return arr;
};
// 路由过滤递归函数
const filterRoutesFun = (arr) => {
return arr
.filter((item) => !item.meta?.isHide)
.map((item) => {
item = Object.assign({}, item);
if (item.children) item.children = filterRoutesFun(item.children);
return item;
});
};
// 传送当前子级数据到菜单中
const setSendClassicChildren = (path) => {
const currentPathSplit = path.split('/');
let currentData = { children: [] };
filterRoutesFun(routesList.value).map((v, k) => {
if (v.path === `/${currentPathSplit[1]}`) {
v['k'] = k;
currentData['item'] = { ...v };
currentData['children'] = [{ ...v }];
if (v.children) currentData['children'] = v.children;
}
});
return currentData;
};
// 页面加载时
onMounted(() => {
setFilterRoutes();
mittBus.on('getBreadcrumbIndexSetFilterRoutes', () => {
setFilterRoutes();
});
});
// 页面卸载时
onUnmounted(() => {
mittBus.off('getBreadcrumbIndexSetFilterRoutes', () => {});
});
</script>
<style scoped lang="scss">
.layout-navbars-breadcrumb-index {
height: 50px;
display: flex;
align-items: center;
background: linear-gradient(268.97deg, rgba(42,121,242,1) 0.71%,rgba(78,171,255,1) 99.38%);
border-bottom: 1px solid var(--next-border-color-light);
}
</style>
This diff is collapsed. Click to expand it.
This diff is collapsed. Click to expand it.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment