Commit f2f0e737 authored by Chen_9g's avatar Chen_9g

admin-cli

parents
# 本地环境
ENV = 'development'
# 本地环境接口地址
VITE_API_URL = 'http://127.0.0.1:8013/api/'
#VITE_API_URL = 'http://docs.shxrtech.com:8013/api/'
# 动态参数加密key,线上环境记得更换
VITE_AES_KEY = '_11111000001111@'
# 线上环境
ENV = 'production'
# 线上环境接口地址
VITE_API_URL = '/api/'
# 动态参数加密key,线上环境记得更换
VITE_AES_KEY = '_11111000001111@'
\ No newline at end of file
NODE_ENV = production
ENV = 'test'
# 线上环境接口地址
VITE_API_URL = '/'
*.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
// @ts-check
const { defineConfig } = require("eslint-define-config");
module.exports = defineConfig({
root: true,
env: {
browser: true,
node: true,
es6: true,
},
parser: "vue-eslint-parser",
parserOptions: {
parser: "@typescript-eslint/parser",
ecmaVersion: 2020,
sourceType: "module",
jsxPragma: "React",
ecmaFeatures: {
jsx: true,
},
},
extends: [
"plugin:vue/vue3-essential",
"plugin:vue/essential",
"plugin:vue/vue3-strongly-recommended",
"plugin:vue/strongly-recommended",
"plugin:vue/recommended",
"plugin:vue/vue3-recommended"],
plugins: ["vue", "@typescript-eslint"],
rules: {
"@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-unused-vars": [
"error",
{
argsIgnorePattern: "^_",
varsIgnorePattern: "^_",
},
],
"no-unused-vars": [
"error",
{
argsIgnorePattern: "^_",
varsIgnorePattern: "^_",
},
],
"no-use-before-define": "off",
"space-before-function-paren": "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/require-default-prop": "off",
"vue/html-self-closing": [
"error",
{
html: {
void: "always",
normal: "never",
component: "always",
},
svg: "always",
math: "always",
},
],
"vue/custom-event-name-casing": "off",
"vue/no-multiple-template-root": "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",
},
});
.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?
package-lock.json
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',
};
<div align="center">
<p align="center">
<a href="https://v3.vuejs.org/" target="_blank">
<img src="https://img.shields.io/badge/vue.js-vue3.x-green" alt="vue">
</a>
<a href="https://element-plus.gitee.io/#/zh-CN/component/changelog" target="_blank">
<img src="https://img.shields.io/badge/element--plus-%3E1.0.0-blue" alt="element plus">
</a>
<a href="https://www.tslang.cn/" target="_blank">
<img src="https://img.shields.io/badge/typescript-%3E4.0.0-blue" alt="typescript">
</a>
<a href="https://vitejs.dev/" target="_blank">
<img src="https://img.shields.io/badge/vite-%3E2.0.0-yellow" alt="vite">
</a>
<a href="https://gitee.com/lyt-top/vue-next-admin/blob/master/LICENSE" target="_blank">
<img src="https://img.shields.io/badge/license-MIT-success" alt="license">
</a>
</p>
<p>&nbsp;</p>
</div>
- 复制代码(桌面 cmd 运行) `npm install -g cnpm --registry=https://registry.npm.taobao.org`
- 复制代码(桌面 cmd 运行) `npm install -g yarn`
#### 🏭 环境支持
| Edge | last 2 versions | last 2 versions | last 2 versions |
| ------------------------------------------------------------------------ | --------------------------------------------------------------------------------- | ------------------------------------------------------------------------------ | ------------------------------------------------------------------------------ |
| ![Edge](https://cdn.jsdelivr.net/npm/@browser-logos/edge/edge_32x32.png) | ![Firefox](https://cdn.jsdelivr.net/npm/@browser-logos/firefox/firefox_32x32.png) | ![Chrome](https://cdn.jsdelivr.net/npm/@browser-logos/chrome/chrome_32x32.png) | ![Safari](https://cdn.jsdelivr.net/npm/@browser-logos/safari/safari_32x32.png) |
> 由于 Vue3 不再支持 IE11,故而 ElementPlus 也不支持 IE11 及之前版本。
#### ⚡ 使用说明
建议使用 cnpm,因为 yarn 有时会报错。<a href="http://nodejs.cn/" target="_blank">node 版本 > 12xx.xx.x</a>
```bash
# 安装依赖
cnpm install
# 运行项目
cnpm run dev
# 打包发布
cnpm run build
```
This diff is collapsed.
<!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>sinra-admin</title>
</head>
<body>
<script>
this.globalThis || (this.globalThis = this);
const ua = navigator.userAgent.toLocaleLowerCase();
if (ua.match(/msie/) != null || ua.match(/trident/) != null) {
window.location.href="ie.html";
}
</script>
<div id="app"></div>
<script type="module" src="/src/main.ts"></script>
</body>
</html>
{
"name": "vue-admin-template",
"version": "0.2.2",
"description": "vue3 vite admin template",
"author": "20220127",
"license": "MIT",
"scripts": {
"dev": "vite --force",
"build": "vite build",
"build:test": "vite build --mode test",
"preview": "vite preview",
"lint-fix": "eslint --fix --ext .js --ext .jsx --ext .vue src/"
},
"dependencies": {
"@element-plus/icons-vue": "^0.2.4",
"@zxcvbn-ts/core": "^2.0.1",
"axios": "^0.24.0",
"crypto-js": "^4.0.0",
"echarts": "^5.2.2",
"element-plus": "^2.0.2",
"element-resize-detector": "^1.2.4",
"js-cookie": "^3.0.1",
"link-components": "0.8.2",
"lodash-es": "^4.17.21",
"mitt": "^3.0.0",
"nprogress": "^0.2.0",
"qrcodejs2-fixes": "^0.0.2",
"screenfull": "^6.0.0",
"sortablejs": "^1.15.0",
"vue": "^3.2.20",
"vue-clipboard3": "^1.0.1",
"vue-router": "^4.0.12",
"vue-types": "^3.0.2",
"vuex": "^4.0.2",
"vxe-table": "^4.1.21",
"xe-utils": "^3.5.2"
},
"devDependencies": {
"@types/axios": "^0.14.0",
"@types/clipboard": "^2.0.1",
"@types/node": "^17.0.2",
"@types/nprogress": "^0.2.0",
"@typescript-eslint/eslint-plugin": "^5.8.0",
"@typescript-eslint/parser": "^5.8.0",
"@vitejs/plugin-vue": "^2.0.1",
"@vitejs/plugin-vue-jsx": "^1.1.3",
"@vue/compiler-sfc": "^3.2.26",
"eslint": "^7.32.0",
"eslint-define-config": "^1.2.3",
"eslint-plugin-vue": "^8.2.0",
"monaco-editor": "^0.33.0",
"prettier": "^2.5.1",
"sass": "^1.45.1",
"sass-loader": "^12.4.0",
"typescript": "^4.5.4",
"vite": "^2.7.4",
"vue-eslint-parser": "^8.0.1",
"vue-picture-cropper": "^0.5.1"
},
"browserslist": [
"> 1%",
"last 2 versions",
"not dead"
],
"bugs": {
"email": ""
},
"engines": {
"node": ">=12.0.0",
"npm": ">= 6.0.0"
},
"keywords": [
"vue",
"vue3",
"element-plus",
"vue-admin"
],
"repository": {
"type": "git",
"url": "https://codeup.aliyun.com/5f3656b8df9df74e36afd6ac/link-admin-template.git"
}
}
<template>
<el-config-provider v-bind="elementConfig">
<router-view />
</el-config-provider>
</template>
<script lang="ts">
import {defineComponent, onBeforeMount, reactive, watch} from 'vue';
import {useRoute} from 'vue-router';
import other from '@/utils/other';
import setIntroduction from '@/utils/setIconfont';
import {ElConfigProvider} from 'element-plus'
export default defineComponent({
name: 'App',
components: { ElConfigProvider},
setup() {
const elementConfig = reactive({
size: other.globalComponentSize
});
const route = useRoute();
// 设置初始化,防止刷新时恢复默认
onBeforeMount(() => {
// 设置批量第三方 icon 图标
setIntroduction.cssCdn();
// 设置批量第三方 js
setIntroduction.jsCdn();
});
// 页面加载时
// 监听路由的变化,设置网站标题
watch(
() => route.path,
() => {
other.useTitle();
}
);
return {
elementConfig
};
},
});
</script>
import request from '@/utils/request';
enum Api {
getBillMetadataUrl = '/bpm/wfApprovalSummary/getBillMetadata',
addFieldItemUrl = '/bpm/wfApprovalSummary/addFieldItem',
listUrl = '/bpm/wfApprovalSummary/list',
deleteFieldItemUrl = '/bpm/wfApprovalSummary/deleteFieldItem/',
saveFieldItemsUrl = '/bpm/wfApprovalSummary/saveFieldItems',
}
export function saveFieldItems(params) {
return request({
url: Api.saveFieldItemsUrl,
method: 'POST',
data: params,
});
}
export function deleteFieldItem(entityPath, name) {
return request({
url: Api.deleteFieldItemUrl + entityPath + '/' + name,
method: 'DELETE',
});
}
export function addFieldItem(params) {
return request({
url: Api.addFieldItemUrl,
method: 'POST',
data: params,
});
}
export function list(params) {
return request({
url: Api.listUrl,
method: 'POST',
data: params,
});
}
export function getBillMetadata(params) {
return request({
url: Api.getBillMetadataUrl,
method: 'POST',
data: params,
});
}
import request from '@/utils/request';
import {guid} from '@/utils/commonFunction'
enum Api {
listUrl = '/bpm/wfOrder/list',
taskListUrl = '/bpm/wfTask/list',
taskTransferUrl = '/bpm/approve/taskTransfer',
jumpTaskUrl = '/bpm/viewApprove/jumpTask',
reloadTaskAssignUrl = '/bpm/viewApprove/reloadTaskAssign',
suspendedOrderUrl = '/bpm/viewApprove/suspendedOrder/',
canCalSuspendedOrderUrl = '/bpm/viewApprove/canCalSuspendedOrder/',
getProcessAllNodesUrl = '/bpm/viewApprove/getProcessAllNodes/',
stopOrderUrl = '/bpm/viewApprove/stopOrder/',
getAllNodeApproveDataUrl = '/bpm/viewApprove/viewAllNodeApproveData/',
getNodeApproveDataUrl = '/bpm/viewApprove/viewNodeApproveData/',
viewProcessUrl = '/bpm/viewApprove/viewProcess/',
}
export function getListData(params) {
return request({
url: Api.listUrl,
method: 'POST',
data: params,
});
}
export function getTaskListData(params) {
return request({
url: Api.taskListUrl,
method: 'POST',
data: params,
});
}
/**
*流程转交
*/
export function taskTransfer(data: {taskId: string, taskTransferUserId: string, taskTransferRemake:string}) {
return request({
url: Api.taskTransferUrl,
method: 'POST',
data: data
});
}
/**
*流程跳转
*/
export function jumpTask(data: {orderId: string, jumpNodeKey: string}) {
return request({
url: Api.jumpTaskUrl,
method: 'POST',
data: data
});
}
/**
* 重新指定参与人
*/
export function reloadTaskAssign(data: {taskId: string, userIds: string[]}) {
return request({
url: Api.reloadTaskAssignUrl,
method: 'POST',
data: data
});
}
/**
* 挂起
*/
export function suspendedOrder(orderId) {
return request({
url: Api.suspendedOrderUrl + orderId,
method: 'GET'
});
}
/**
* 撤销挂起
*/
export function canCalSuspendedOrder(orderId) {
return request({
url: Api.canCalSuspendedOrderUrl + orderId,
method: 'GET'
});
}
/**
* 获取流程实例下所有节点(跳转流程用)
*/
export function getProcessAllNodes(orderId) {
return request({
url: Api.getProcessAllNodesUrl + orderId,
method: 'GET'
});
}
/**
* 终止流程
*/
export function stopOrder(orderId) {
return request({
url: Api.stopOrderUrl + orderId,
method: 'GET'
});
}
/**
* 获取节点审批数据
*/
export function getNodeApproveData(orderId:string, nodeKey?:string) {
let url = Api.getAllNodeApproveDataUrl;
if (nodeKey) {
url = Api.getNodeApproveDataUrl;
}
return request({
url: url + orderId + (nodeKey ? `/${nodeKey}` : ''),
method: 'GET'
});
}
/**
* 查看流程图
* @param orderId
*/
export function viewProcess(orderId) {
return request({
url: Api.viewProcessUrl + orderId,
method: 'GET'
});
}
export function getId() {
return 'XRUYX' + guid();
}
import request from '@/utils/request';
import {guid} from '@/utils/commonFunction'
enum Api {
listUrl = '/bpm/wfProcess/list',
saveUrl = '/bpm/wfProcess/save',
deleteUrl = '/bpm/wfProcess/delete/',
viewUrl = '/bpm/wfProcess/view/',
changeEnableUrl = '/bpm/wfProcess/changeEnable',
processWfImportUrl = '/bpm/wfProcess/importDesign',
}
export function processWfImport(params) {
return request({
url: Api.processWfImportUrl,
method: 'POST',
data: params,
});
}
export function changeEnable(params) {
return request({
url: Api.changeEnableUrl,
method: 'POST',
data: params,
});
}
export function deleteData(id) {
return request({
url: Api.deleteUrl + id,
method: 'DELETE',
});
}
export function view(id) {
return request({
url: Api.viewUrl + id,
method: 'GET',
});
}
export function getListData(params) {
return request({
url: Api.listUrl,
method: 'POST',
data: params,
});
}
export function save(params) {
return request({
url: Api.saveUrl,
method: 'PUT',
data: params,
});
}
export function getId() {
return 'WYIVX' + guid();
}
import request from '@/utils/request';
import {guid} from '@/utils/commonFunction'
enum Api {
listUrl = '/bpm/wfSurrogate/list',
saveUrl = '/bpm/wfSurrogate/save',
deleteUrl = '/bpm/wfSurrogate/delete/',
viewUrl = '/bpm/wfSurrogate/view/',
changeEnableUrl = '/bpm/wfSurrogate/changeEnable',
}
export function changeEnable(params) {
return request({
url: Api.changeEnableUrl,
method: 'POST',
data: params,
});
}
export function deleteData(id) {
return request({
url: Api.deleteUrl + id,
method: 'DELETE',
});
}
export function view(id) {
return request({
url: Api.viewUrl + id,
method: 'GET',
});
}
export function getListData(params) {
return request({
url: Api.listUrl,
method: 'POST',
data: params,
});
}
export function save(params) {
return request({
url: Api.saveUrl,
method: 'PUT',
data: params,
});
}
export function getId() {
return 'ILTMC' + guid();
}
import request from '@/utils/request';
import {guid} from '@/utils/commonFunction'
enum Api {
listUrl = '/bpm/wfType/list',
treeUrl = '/bpm/wfType/tree',
saveUrl = '/bpm/wfType/save',
deleteUrl = '/bpm/wfType/delete/',
viewUrl = '/bpm/wfType/view/',
changeEnableUrl = '/bpm/wfType/changeEnable',
}
export function changeEnable(params) {
return request({
url: Api.changeEnableUrl,
method: 'POST',
data: params,
});
}
export function deleteData(id) {
return request({
url: Api.deleteUrl + id,
method: 'DELETE',
});
}
export function view(id) {
return request({
url: Api.viewUrl + id,
method: 'GET',
});
}
export function getListData(params) {
return request({
url: Api.listUrl,
method: 'POST',
data: params,
});
}
export function getTreeData(params) {
return request({
url: Api.treeUrl,
method: 'POST',
data: params,
});
}
export function save(params) {
return request({
url: Api.saveUrl,
method: 'PUT',
data: params,
});
}
export function getId() {
return 'XFJFB' + guid();
}
import request from '@/utils/request';
enum Api {
todoTaskListUrl = '/bpm/wfTask/todoTaskList',
completeTaskListUrl = '/bpm/wfTask/completeTaskList',
mySenderTaskListUrl = '/bpm/wfTask/mySenderTaskList',
makeTaskListUrl = '/bpm/wfTask/makeTaskList',
}
export function todoTaskList(params) {
return request({
url: Api.todoTaskListUrl,
method: 'POST',
data: params,
});
}
export function completeTaskList(params) {
return request({
url: Api.completeTaskListUrl,
method: 'POST',
data: params,
});
}
export function mySenderTaskList(params) {
return request({
url: Api.mySenderTaskListUrl,
method: 'POST',
data: params,
});
}
export function makeTaskList(params) {
return request({
url: Api.makeTaskListUrl,
method: 'POST',
data: params,
});
}
\ No newline at end of file
import request from '@/utils/request';
import {guid} from '@/utils/commonFunction'
enum Api {
listUrl = '/demo/OrderBill/list',
saveUrl = '/demo/OrderBill/save',
deleteUrl = '/demo/OrderBill/delete/',
viewUrl = '/demo/OrderBill/view/',
changeEnableUrl = '/demo/OrderBill/changeEnable',
}
export function changeEnable(params) {
return request({
url: Api.changeEnableUrl,
method: 'POST',
data: params,
});
}
export function deleteData(id) {
return request({
url: Api.deleteUrl + id,
method: 'DELETE',
});
}
export function view(id) {
return request({
url: Api.viewUrl + id,
method: 'GET',
});
}
export function getListData(params) {
return request({
url: Api.listUrl,
method: 'POST',
data: params,
});
}
export function save(params) {
return request({
url: Api.saveUrl,
method: 'PUT',
data: params,
});
}
export function getId() {
return 'PQTRU' + guid();
}
import request from '@/utils/request';
import {guid} from '@/utils/commonFunction'
enum Api {
listUrl = '/demo/Supper/list',
saveUrl = '/demo/Supper/save',
deleteUrl = '/demo/Supper/delete/',
viewUrl = '/demo/Supper/view/',
changeEnableUrl = '/demo/Supper/changeEnable',
}
export function changeEnable(params) {
return request({
url: Api.changeEnableUrl,
method: 'POST',
data: params,
});
}
export function deleteData(id) {
return request({
url: Api.deleteUrl + id,
method: 'DELETE',
});
}
export function view(id) {
return request({
url: Api.viewUrl + id,
method: 'GET',
});
}
export function getListData(params) {
return request({
url: Api.listUrl,
method: 'POST',
data: params,
});
}
export function save(params) {
return request({
url: Api.saveUrl,
method: 'PUT',
data: params,
});
}
export function getId() {
return 'GMQJG' + guid();
}
import request from '@/utils/request';
import {guid} from '@/utils/commonFunction'
enum Api {
listUrl = '/demo/test/list',
saveUrl = '/demo/test/save',
deleteUrl = '/demo/test/delete/',
viewUrl = '/demo/test/view/',
changeEnableUrl = '/demo/test/changeEnable',
}
export function changeEnable(params) {
return request({
url: Api.changeEnableUrl,
method: 'POST',
data: params,
});
}
export function deleteData(id) {
return request({
url: Api.deleteUrl + id,
method: 'DELETE',
});
}
export function view(id) {
return request({
url: Api.viewUrl + id,
method: 'GET',
});
}
export function getListData(params) {
return request({
url: Api.listUrl,
method: 'POST',
data: params,
});
}
export function save(params) {
return request({
url: Api.saveUrl,
method: 'PUT',
data: params,
});
}
export function getId() {
return 'RUKZF' + guid();
}
import request from '@/utils/request';
enum Api {
getEntityListData = '/dynamicApi/getEntityListData',
getEntityModel = '/dynamicApi/getEntityModel',
getSelectListData = '/dynamicApi/getSelectListData',
getSelectTreeData = '/dynamicApi/getSelectTreeData',
getDictTreeData = '/dynamicApi/getDictGroupTree',
getDictData = '/dynamicApi/getDict',
}
export function getEntityListData(data) {
return request({
url: Api.getEntityListData,
method: 'POST',
data: data
});
}
export function getEntityModel(data) {
return request({
url: Api.getEntityModel,
method: 'POST',
data: data
});
}
export function getSelectListData(data) {
return request({
url: Api.getSelectListData,
method: 'POST',
data: data
});
}
export function getSelectTreeData(data) {
return request({
url: Api.getSelectTreeData,
method: 'POST',
data: data
});
}
export function getDictTreeData(data: {dictGroup: string}) {
return request({
url: Api.getDictTreeData + `/${data.dictGroup}`,
method: 'GET'
});
}
export function getDictData(data: {dictGroup: string}) {
return request({
url: Api.getDictData + `/${data.dictGroup}`,
method: 'GET'
});
}
import request from '@/utils/request';
enum Api {
listUrl = '/file/SysFiles/list',
saveUrl = '/file/SysFiles/save',
deleteUrl = '/file/SysFiles/deleteFile/',
viewUrl = '/file/SysFiles/view/',
changeEnableUrl = '/file/SysFiles/changeEnable',
getFileListUrl = '/file/SysFiles/getFileList',
}
export function changeEnable(params) {
return request({
url: Api.changeEnableUrl,
method: 'POST',
data: params,
});
}
export function deleteData(id) {
return request({
url: Api.deleteUrl + id,
method: 'DELETE',
});
}
export function view(id) {
return request({
url: Api.viewUrl + id,
method: 'GET',
});
}
export function getListData(params) {
return request({
url: Api.listUrl,
method: 'POST',
data: params,
});
}
export function save(params) {
return request({
url: Api.saveUrl,
method: 'PUT',
data: params,
});
}
export function getFileList(data: {billId: string, type: string}) {
return request({
url: Api.getFileListUrl + `/${data.billId || ''}/${data.type || 'MAIN'}`,
method: 'GET',
});
}
import request from '@/utils/request';
enum Api {
listUrl = '/job/Schedu/list',
saveUrl = '/job/Schedu/save',
deleteUrl = '/job/Schedu/delete/',
viewUrl = '/job/Schedu/view/',
runUrl = '/job/Schedu/run/',
pauseUrl = '/job/Schedu/pause/',
resumeUrl = '/job/Schedu/resume/',
}
export function pause(id) {
return request({
url: Api.pauseUrl + id,
method: 'GET',
});
}
export function resume(id) {
return request({
url: Api.resumeUrl + id,
method: 'GET',
});
}
export function run(id) {
return request({
url: Api.runUrl + id,
method: 'GET',
});
}
export function deleteData(id) {
return request({
url: Api.deleteUrl + id,
method: 'DELETE',
});
}
export function view(id) {
return request({
url: Api.viewUrl + id,
method: 'GET',
});
}
export function getListData(params) {
return request({
url: Api.listUrl,
method: 'POST',
data: params,
});
}
export function save(params) {
return request({
url: Api.saveUrl,
method: 'PUT',
data: params,
});
}
import request from '@/utils/request';
enum Api {
listUrl = '/job/Schedulog/list',
saveUrl = '/job/Schedulog/save',
deleteUrl = '/job/Schedulog/delete/',
viewUrl = '/job/Schedulog/view/',
changeEnableUrl = '/job/Schedulog/changeEnable',
}
export function changeEnable(params) {
return request({
url: Api.changeEnableUrl,
method: 'POST',
data: params,
});
}
export function deleteData(id) {
return request({
url: Api.deleteUrl + id,
method: 'DELETE',
});
}
export function view(id) {
return request({
url: Api.viewUrl + id,
method: 'GET',
});
}
export function getListData(params) {
return request({
url: Api.listUrl,
method: 'POST',
data: params,
});
}
export function save(params) {
return request({
url: Api.saveUrl,
method: 'PUT',
data: params,
});
}
import request from '@/utils/request';
/**
* 用户登录
* @param params 要传的参数值
* @returns 返回接口数据
*/
export function login(params: object) {
return request({
url: '/auth/login',
method: 'post',
data: params,
});
}
/**
* 用户退出登录
* @param params 要传的参数值
* @returns 返回接口数据
*/
export function logout(params: object) {
return request({
url: '/auth/logout',
method: 'post',
data: params,
});
}
/**
* 获取菜单路由信息
*/
export function getDynamicRoutes() {
return request({
url: '/sys/Menu/getDynamicRoutes/CUSTOMER_MENU', //CUSTOMER_MENU MANAGE_MENU
method: 'get',
});
}
/**
* 获取验证码
*/
export function captcha(key) {
return request({
url: '/auth/captcha/' + key,
method: 'GET',
});
}
import request from '@/utils/request';
enum Api {
listUrl = '/sys/ApiSecret/list',
saveUrl = '/sys/ApiSecret/save',
deleteUrl = '/sys/ApiSecret/delete/',
viewUrl = '/sys/ApiSecret/view/',
changeEnableUrl = '/sys/ApiSecret/changeEnable',
}
export function changeEnable(params) {
return request({
url: Api.changeEnableUrl,
method: 'POST',
data: params,
});
}
export function deleteData(id) {
return request({
url: Api.deleteUrl + id,
method: 'DELETE',
});
}
export function view(id) {
return request({
url: Api.viewUrl + id,
method: 'GET',
});
}
export function getListData(params) {
return request({
url: Api.listUrl,
method: 'POST',
data: params,
});
}
export function save(params) {
return request({
url: Api.saveUrl,
method: 'PUT',
data: params,
});
}
import request from '@/utils/request';
enum Api {
listUrl = '/sys/CodeRule/list',
saveUrl = '/sys/CodeRule/save',
deleteUrl = '/sys/CodeRule/delete/',
viewUrl = '/sys/CodeRule/view/',
changeEnableUrl = '/sys/CodeRule/changeEnable',
}
export function changeEnable(params) {
return request({
url: Api.changeEnableUrl,
method: 'POST',
data: params,
});
}
export function deleteData(id) {
return request({
url: Api.deleteUrl + id,
method: 'DELETE',
});
}
export function view(id) {
return request({
url: Api.viewUrl + id,
method: 'GET',
});
}
export function getListData(params) {
return request({
url: Api.listUrl,
method: 'POST',
data: params,
});
}
export function save(params) {
return request({
url: Api.saveUrl,
method: 'PUT',
data: params,
});
}
import request from '@/utils/request';
enum Api {
listUrl = '/sys/Dict/list',
saveUrl = '/sys/Dict/save',
deleteUrl = '/sys/Dict/delete/',
viewUrl = '/sys/Dict/view/',
changeEnableUrl = '/sys/Dict/changeEnable',
getDictGroupTreeDataUrl = '/sys/Dict/getDictGroupTreeData',
getDictTreeDataUrl = '/sys/Dict/getDictTreeData',
saveDictUrl = '/sys/Dict/saveDict',
viewDictUrl = '/sys/Dict/viewDict/',
deleteDictUrl = '/sys/Dict/deleteDict/'
}
export function getDictTreeData() {
return request({
url: Api.getDictTreeDataUrl,
method: 'GET',
});
}
export function saveDict(params) {
return request({
url: Api.saveDictUrl,
method: 'PUT',
data: params,
});
}
export function deleteDict(id) {
return request({
url: Api.deleteDictUrl + id,
method: 'DELETE',
});
}
export function viewDict(id) {
return request({
url: Api.viewDictUrl + id,
method: 'GET',
});
}
export function getDictGroupTreeData() {
return request({
url: Api.getDictGroupTreeDataUrl,
method: 'GET',
});
}
export function changeEnable(params) {
return request({
url: Api.changeEnableUrl,
method: 'POST',
data: params,
});
}
export function deleteData(id) {
return request({
url: Api.deleteUrl + id,
method: 'DELETE',
});
}
export function view(id) {
return request({
url: Api.viewUrl + id,
method: 'GET',
});
}
export function getListData(params) {
return request({
url: Api.listUrl,
method: 'POST',
data: params,
});
}
export function save(params) {
return request({
url: Api.saveUrl,
method: 'PUT',
data: params,
});
}
import request from '@/utils/request';
enum Api {
listUrl = '/sys/Menu/list',
saveUrl = '/sys/Menu/save',
deleteUrl = '/sys/Menu/delete/',
viewUrl = '/sys/Menu/view/',
changeEnableUrl = '/sys/Menu/changeEnable',
getMenuTreeDataUrl = '/sys/Menu/getMenuTreeData',
getAllMenuTreeDataUrl = '/sys/Menu/getAllMenuTreeData',
}
export function getMenuTreeData() {
return request({
url: Api.getMenuTreeDataUrl,
method: 'GET',
});
}
export function getAllMenuTreeData() {
return request({
url: Api.getAllMenuTreeDataUrl,
method: 'GET',
});
}
export function changeEnable(params) {
return request({
url: Api.changeEnableUrl,
method: 'POST',
data: params,
});
}
export function deleteData(id) {
return request({
url: Api.deleteUrl + id,
method: 'DELETE',
});
}
export function view(id) {
return request({
url: Api.viewUrl + id,
method: 'GET',
});
}
export function getListData(params) {
return request({
url: Api.listUrl,
method: 'POST',
data: params,
});
}
export function save(params) {
return request({
url: Api.saveUrl,
method: 'PUT',
data: params,
});
}
import request from '@/utils/request';
enum Api {
listUrl = '/sys/Org/list',
saveUrl = '/sys/Org/save',
deleteUrl = '/sys/Org/delete/',
viewUrl = '/sys/Org/view/',
changeEnableUrl = '/sys/Org/changeEnable',
getOrgTreeDataUrl = '/sys/Org/getOrgTreeData',
getOrgPisitionAndPersonTreeDataUrl = '/sys/Org/getOrgPisitionAndPersonTreeData',
}
export function getOrgPisitionAndPersonTreeData(params) {
return request({
url: Api.getOrgPisitionAndPersonTreeDataUrl,
method: 'POST',
data: params,
});
}
export function getOrgTreeData() {
return request({
url: Api.getOrgTreeDataUrl,
method: 'GET',
});
}
export function changeEnable(params) {
return request({
url: Api.changeEnableUrl,
method: 'POST',
data: params,
});
}
export function deleteData(id) {
return request({
url: Api.deleteUrl + id,
method: 'DELETE',
});
}
export function view(id) {
return request({
url: Api.viewUrl + id,
method: 'GET',
});
}
export function getListData(params) {
return request({
url: Api.listUrl,
method: 'POST',
data: params,
});
}
export function save(params) {
return request({
url: Api.saveUrl,
method: 'PUT',
data: params,
});
}
import request from '@/utils/request';
enum Api {
listUrl = '/sys/Parms/list',
saveUrl = '/sys/Parms/save',
deleteUrl = '/sys/Parms/delete/',
viewUrl = '/sys/Parms/view/',
changeEnableUrl = '/sys/Parms/changeEnable',
}
export function changeEnable(params) {
return request({
url: Api.changeEnableUrl,
method: 'POST',
data: params,
});
}
export function deleteData(id) {
return request({
url: Api.deleteUrl + id,
method: 'DELETE',
});
}
export function view(id) {
return request({
url: Api.viewUrl + id,
method: 'GET',
});
}
export function getListData(params) {
return request({
url: Api.listUrl,
method: 'POST',
data: params,
});
}
export function save(params) {
return request({
url: Api.saveUrl,
method: 'PUT',
data: params,
});
}
import request from '@/utils/request';
import {guid} from '@/utils/commonFunction'
enum Api {
listUrl = '/sys/Person/list',
saveUrl = '/sys/Person/save',
deleteUrl = '/sys/Person/delete/',
viewUrl = '/sys/Person/view/',
changeEnableUrl = '/sys/Person/changeEnable',
}
export function changeEnable(params) {
return request({
url: Api.changeEnableUrl,
method: 'POST',
data: params,
});
}
export function deleteData(id) {
return request({
url: Api.deleteUrl + id,
method: 'DELETE',
});
}
export function view(id) {
return request({
url: Api.viewUrl + id,
method: 'GET',
});
}
export function getListData(params) {
return request({
url: Api.listUrl,
method: 'POST',
data: params,
});
}
export function save(params) {
return request({
url: Api.saveUrl,
method: 'PUT',
data: params,
});
}
export function getId() {
return 'QTPNN' + guid();
}
import request from '@/utils/request';
import {guid} from '@/utils/commonFunction'
enum Api {
listUrl = '/sys/Position/list',
saveUrl = '/sys/Position/save',
deleteUrl = '/sys/Position/delete/',
viewUrl = '/sys/Position/view/',
changeEnableUrl = '/sys/Position/changeEnable',
getPositionTreeDataUrl = '/sys/Position/getPositionTreeData',
getOrgPositionTreeDataUrl = '/sys/Position/getOrgPositionTreeData',
}
export function getOrgPositionTreeData() {
return request({
url: Api.getOrgPositionTreeDataUrl,
method: 'GET',
});
}
export function getPositionTreeData() {
return request({
url: Api.getPositionTreeDataUrl,
method: 'GET',
});
}
export function changeEnable(params) {
return request({
url: Api.changeEnableUrl,
method: 'POST',
data: params,
});
}
export function deleteData(id) {
return request({
url: Api.deleteUrl + id,
method: 'DELETE',
});
}
export function view(id) {
return request({
url: Api.viewUrl + id,
method: 'GET',
});
}
export function getListData(params) {
return request({
url: Api.listUrl,
method: 'POST',
data: params,
});
}
export function save(params) {
return request({
url: Api.saveUrl,
method: 'PUT',
data: params,
});
}
export function getId() {
return 'RCGZT' + guid();
}
import request from '@/utils/request';
enum Api {
listUrl = '/sys/Role/list',
saveUrl = '/sys/Role/save',
deleteUrl = '/sys/Role/delete/',
viewUrl = '/sys/Role/view/',
changeEnableUrl = '/sys/Role/changeEnable',
addRoleUserUrl = '/sys/Role/addRoleUser',
deleteRoleUserUrl = '/sys/Role/deleteRoleUser',
roleUserlistUrl = '/sys/Role/roleUserlist',
synPremUrl = '/sys/Role/synPrem/'
}
export function synPrem(serviceName){
return request({
url: Api.synPremUrl + serviceName,
method: 'GET',
});
}
export function deleteRoleUser(params) {
return request({
url: Api.deleteRoleUserUrl,
method: 'POST',
data: params,
});
}
export function addRoleUser(params) {
return request({
url: Api.addRoleUserUrl,
method: 'POST',
data: params,
});
}
export function roleUserlist(params) {
return request({
url: Api.roleUserlistUrl,
method: 'POST',
data: params,
});
}
export function changeEnable(params) {
return request({
url: Api.changeEnableUrl,
method: 'POST',
data: params,
});
}
export function deleteData(id) {
return request({
url: Api.deleteUrl + id,
method: 'DELETE',
});
}
export function view(id) {
return request({
url: Api.viewUrl + id,
method: 'GET',
});
}
export function getListData(params) {
return request({
url: Api.listUrl,
method: 'POST',
data: params,
});
}
export function save(params) {
return request({
url: Api.saveUrl,
method: 'PUT',
data: params,
});
}
import request from '@/utils/request';
import {guid} from '@/utils/commonFunction'
enum Api {
listUrl = '/sys/Tenant/list',
saveUrl = '/sys/Tenant/save',
deleteUrl = '/sys/Tenant/delete/',
viewUrl = '/sys/Tenant/view/',
changeEnableUrl = '/sys/Tenant/changeEnable',
}
export function changeEnable(params) {
return request({
url: Api.changeEnableUrl,
method: 'POST',
data: params,
});
}
export function deleteData(id) {
return request({
url: Api.deleteUrl + id,
method: 'DELETE',
});
}
export function view(id) {
return request({
url: Api.viewUrl + id,
method: 'GET',
});
}
export function getListData(params) {
return request({
url: Api.listUrl,
method: 'POST',
data: params,
});
}
export function save(params) {
return request({
url: Api.saveUrl,
method: 'PUT',
data: params,
});
}
export function getId() {
return 'KKTHB' + guid();
}
import request from '@/utils/request';
enum Api {
listUrl = '/sys/User/list',
saveUrl = '/sys/User/save',
deleteUrl = '/sys/User/delete/',
viewUrl = '/sys/User/view/',
settingPwdUrl = '/sys/User/settingPwd',
changeEnableUrl = '/sys/User/changeEnable',
onlinelistUrl = '/sys/onlineUser/list',
kickOutUserUrl = '/sys/onlineUser/kickOutUser/',
uploadPhotoUrl = '/sys/User/uploadPhoto',
updatePasswordUrl = '/sys/User/updatePwd'
}
export function kickOutUser(token) {
return request({
url: Api.kickOutUserUrl + token,
method: 'GET',
});
}
export function onlinelist(params) {
return request({
url: Api.onlinelistUrl,
method: 'POST',
data: params,
});
}
export function settingPwd(params) {
return request({
url: Api.settingPwdUrl,
method: 'POST',
data: params,
});
}
export function uploadPhoto(params) {
return request({
url: Api.uploadPhotoUrl,
method: 'POST',
data: params,
});
}
export function changeEnable(params) {
return request({
url: Api.changeEnableUrl,
method: 'POST',
data: params,
});
}
export function deleteData(id) {
return request({
url: Api.deleteUrl + id,
method: 'DELETE',
});
}
export function view(id) {
return request({
url: Api.viewUrl + id,
method: 'GET',
});
}
export function getListData(params) {
return request({
url: Api.listUrl,
method: 'POST',
data: params,
});
}
export function save(params) {
return request({
url: Api.saveUrl,
method: 'PUT',
data: params,
});
}
/**
* 修改密码
*/
export function updatePwd(data) {
return request({
url: Api.updatePasswordUrl,
method: 'POST',
data: data,
});
}
import request from '@/utils/request';
enum Api {
listUrl = '/sys/log/list',
saveUrl = '/sys/log/save',
deleteUrl = '/sys/log/delete/',
viewUrl = '/sys/log/view/',
changeEnableUrl = '/sys/log/changeEnable',
}
export function changeEnable(params) {
return request({
url: Api.changeEnableUrl,
method: 'POST',
data: params,
});
}
export function deleteData(id) {
return request({
url: Api.deleteUrl + id,
method: 'DELETE',
});
}
export function view(id) {
return request({
url: Api.viewUrl + id,
method: 'GET',
});
}
export function getListData(params) {
return request({
url: Api.listUrl,
method: 'POST',
data: params,
});
}
export function save(params) {
return request({
url: Api.saveUrl,
method: 'PUT',
data: params,
});
}
import request from '@/utils/request';
enum Api {
listUrl = '/sys/mataData/list',
getMatadataTreeUrl = '/sys/mataData/getMatadataTree',
getMatadataFieldUrl = '/sys/mataData/getMatadataField',
}
export function getMatadataTree() {
return request({
url: Api.getMatadataTreeUrl,
method: 'GET',
});
}
export function getListData(params) {
return request({
url: Api.listUrl,
method: 'POST',
data: params,
});
}
export function getMatadataField(params) {
return request({
url: Api.getMatadataFieldUrl,
method: 'POST',
data: params,
});
}
\ No newline at end of file
<svg width="128" height="128" xmlns="http://www.w3.org/2000/svg"><path d="M121.718 73.272v9.953c3.957-7.584 6.199-16.05 6.199-24.995C127.917 26.079 99.273 0 63.958 0 28.644 0 0 26.079 0 58.23c0 .403.028.806.028 1.21l22.97-25.953h13.34l-19.76 27.187h6.42V53.77l13.728-19.477v49.361H22.998V73.272H2.158c5.951 20.284 23.608 36.208 45.998 41.399-1.44 3.3-5.618 11.263-12.565 12.674-8.607 1.764 23.358.428 46.163-13.178 17.519-4.611 31.938-15.849 39.77-30.513h-13.506V73.272H85.02V59.464l22.998-25.977h13.008l-19.429 27.187h6.421v-7.433l13.727-19.402v39.433h-.027zm-78.24 2.822a10.516 10.516 0 0 1-.996-4.535V44.548c0-1.613.332-3.124.996-4.535a11.66 11.66 0 0 1 2.713-3.68c1.134-1.032 2.49-1.864 4.04-2.468 1.55-.605 3.21-.908 4.982-.908h11.292c1.77 0 3.431.303 4.981.908 1.522.604 2.85 1.41 3.986 2.418l-12.26 16.303v-2.898a1.96 1.96 0 0 0-.665-1.512c-.443-.403-.996-.604-1.66-.604-.665 0-1.218.201-1.661.604a1.96 1.96 0 0 0-.664 1.512v9.071L44.364 77.606a10.556 10.556 0 0 1-.886-1.512zm35.73-4.535c0 1.613-.332 3.124-.997 4.535a11.66 11.66 0 0 1-2.712 3.68c-1.134 1.032-2.49 1.864-4.04 2.469-1.55.604-3.21.907-4.982.907H55.185c-1.77 0-3.431-.303-4.981-.907-1.55-.605-2.906-1.437-4.041-2.47a12.49 12.49 0 0 1-1.384-1.512l13.727-18.217v6.375c0 .605.222 1.109.665 1.512.442.403.996.604 1.66.604.664 0 1.218-.201 1.66-.604a1.96 1.96 0 0 0 .665-1.512V53.87L75.97 36.838c.913.932 1.66 1.99 2.214 3.175.664 1.41.996 2.922.996 4.535v27.011h.028z"/></svg>
\ No newline at end of file
<svg width="128" height="128" xmlns="http://www.w3.org/2000/svg"><path d="M127.88 73.143c0 1.412-.506 2.635-1.518 3.669-1.011 1.033-2.209 1.55-3.592 1.55h-17.887c0 9.296-1.783 17.178-5.35 23.645l16.609 17.044c1.011 1.034 1.517 2.257 1.517 3.67 0 1.412-.506 2.635-1.517 3.668-.958 1.033-2.155 1.55-3.593 1.55-1.438 0-2.635-.517-3.593-1.55l-15.811-16.063a15.49 15.49 0 0 1-1.196 1.06c-.532.434-1.65 1.208-3.353 2.322a50.104 50.104 0 0 1-5.192 2.974c-1.758.87-3.94 1.658-6.546 2.364-2.607.706-5.189 1.06-7.748 1.06V47.044H58.89v73.062c-2.716 0-5.417-.367-8.106-1.102-2.688-.734-5.003-1.631-6.945-2.692a66.769 66.769 0 0 1-5.268-3.179c-1.571-1.057-2.73-1.94-3.476-2.65L33.9 109.34l-14.611 16.877c-1.066 1.14-2.344 1.711-3.833 1.711-1.277 0-2.422-.434-3.434-1.304-1.012-.978-1.557-2.187-1.635-3.627-.079-1.44.333-2.705 1.236-3.794l16.129-18.51c-3.087-6.197-4.63-13.644-4.63-22.342H5.235c-1.383 0-2.58-.517-3.592-1.55S.125 74.545.125 73.132c0-1.412.506-2.635 1.518-3.668 1.012-1.034 2.21-1.55 3.592-1.55h17.887V43.939L9.308 29.833c-1.012-1.033-1.517-2.256-1.517-3.669 0-1.412.505-2.635 1.517-3.668 1.012-1.034 2.21-1.55 3.593-1.55s2.58.516 3.593 1.55l13.813 14.106h67.396l13.814-14.106c1.012-1.034 2.21-1.55 3.592-1.55 1.384 0 2.581.516 3.593 1.55 1.012 1.033 1.518 2.256 1.518 3.668 0 1.413-.506 2.636-1.518 3.67l-13.814 14.105v23.975h17.887c1.383 0 2.58.516 3.593 1.55 1.011 1.033 1.517 2.256 1.517 3.668l-.005.01zM89.552 26.175H38.448c0-7.23 2.489-13.386 7.466-18.469C50.892 2.623 56.92.082 64 .082c7.08 0 13.108 2.541 18.086 7.624 4.977 5.083 7.466 11.24 7.466 18.469z"/></svg>
\ No newline at end of file
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1553478255619" class="icon" style="" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="1799" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><defs><style type="text/css"></style></defs><path d="M329.285097 317.714062l-8.422833 4.428869c-8.78099 4.584412-13.528108 14.84715-11.923564 24.415063 1.644453 4.909823 3.491521 9.864672 5.492084 14.747889 2.030239 4.854565 4.230348 9.652847 6.53688 14.293541 5.621021 7.891737 16.246009 11.824303 25.699312 8.858762l9.041934-2.868327c14.741749-3.860934 31.115672-0.056282 42.62582 11.512195 11.549034 11.526521 15.374152 27.863604 11.549034 42.570561l-2.882654 9.126868c-2.958378 9.438976 0.938372 20.042475 8.830109 25.706475 4.634554 2.328022 9.403161 4.52813 14.323217 6.529717 4.876054 2.043542 9.80839 3.846608 14.739702 5.478781 9.538237 1.603521 19.87363-3.123131 24.414039-11.910261l4.402263-8.388041c7.67889-13.144368 21.915126-22.002107 38.267559-22.002107 16.338107 0 30.547737 8.829086 38.255279 21.931498l4.41352 8.459672c4.584412 8.78713 14.84715 13.513782 24.414039 11.910261 4.91187-1.632173 9.851369-3.462868 14.734586-5.478781 4.882194-2.030239 9.66615-4.201695 14.322194-6.529717 7.891737-5.622044 11.809977-16.253172 8.843412-25.706475l-2.852978-9.041934c-3.859911-14.733563-0.069585-31.085996 11.484565-42.655496 11.55415-11.525498 27.878954-15.372106 42.599214-11.512195l9.097192 2.88163c9.426697 2.952238 20.044522-0.937348 25.693172-8.829086 2.313695-4.656043 4.527107-9.411347 6.54302-14.322194 2.029216-4.883217 3.847631-9.80839 5.495154-14.748912 1.616824-9.581216-3.108804-19.843954-11.911284-24.429389l-8.402367-4.400217c-13.132088-7.665587-21.98778-21.901823-21.98778-38.255279 0-16.32378 8.830109-30.589692 21.974477-38.268582l8.416693-4.443196c8.80248-4.571109 13.528108-14.832823 11.924587-24.400736-1.6465-4.910846-3.479241-9.850345-5.493108-14.733563-2.031263-4.868891-4.202719-9.680477-6.529717-14.308891-5.622044-7.890714-16.253172-11.82328-25.708522-8.842389l-9.05626 2.852978c-14.747889 3.861958-31.071669 0.057305-42.654472-11.512195-11.55415-11.55415-15.344476-27.877931-11.484565-42.612517l2.852978-9.05626c2.966565-9.44-0.951675-20.043499-8.856715-25.692149-4.641717-2.328022-9.397021-4.542456-14.307867-6.544043-4.883217-2.029216-9.82374-3.846608-14.734586-5.465478-9.567913-1.632173-19.872606 3.123131-24.414039 11.895935l-4.400217 8.389064c-7.67889 13.174044-21.931498 22.002107-38.268582 22.002107-16.309454 0-30.576389-8.828063-38.267559-22.002107l-4.387937-8.389064c-4.554736-8.771781-14.8318-13.528108-24.405853-11.895935-4.954849 1.604544-9.873882 3.435239-14.763239 5.4225-4.883217 2.044566-9.688663 4.217045-14.323217 6.545066-7.891737 5.649674-11.808954 16.266475-8.830109 25.735128l2.826372 9.05626c3.882424 14.762215 0.057305 31.085996-11.491729 42.612517-11.510148 11.5695-27.849278 15.373129-42.611493 11.526521l-9.070586-2.867304c-9.44-2.980891-20.063965 0.951675-25.686009 8.842389-2.342348 4.628414-4.52813 9.44-6.53688 14.308891-2.036379 4.882194-3.847631 9.822716-5.492084 14.733563-1.603521 9.581216 3.142573 19.85828 11.923564 24.443715l8.402367 4.400217c13.156648 7.67889 21.986757 21.944801 21.986757 38.268582C351.251388 295.79689 342.421278 310.019823 329.285097 317.714062zM511.977999 171.706687c59.532885 0 107.795075 48.275493 107.795075 107.779725 0 59.490929-48.26219 107.752096-107.795075 107.752096-59.533908 0-107.752096-48.26219-107.752096-107.752096C404.226926 219.98218 452.445114 171.706687 511.977999 171.706687z" p-id="1800" fill="#bfbfbf"></path><path d="M924.647713 689.174212 798.570249 689.174212 798.570249 581.650313c0-26.387997-21.476127-47.850821-47.864124-47.850821L276.2543 533.799492c-26.386974 0-47.851844 21.462824-47.851844 47.850821l0 107.523899L99.345124 689.174212c-20.419052 0-36.95568 16.550954-36.95568 36.948517l0 184.771237c0 20.399609 16.536628 36.962843 36.95568 36.962843l273.965675 0c20.397562 0 36.947494-16.564257 36.947494-36.962843L410.258293 726.122729c0-20.398586-16.550954-36.948517-36.947494-36.948517l-123.103736 0L250.207064 581.650313c0-14.366196 11.68104-26.047236 26.047236-26.047236l474.451826 0c14.364149 0 26.062586 11.68104 26.062586 26.047236l0 107.523899L650.689201 689.174212c-20.412912 0-36.962843 16.550954-36.962843 36.948517l0 184.771237c0 20.399609 16.549931 36.962843 36.962843 36.962843l273.958512 0c20.397562 0 36.96182-16.564257 36.96182-36.962843L961.609533 726.122729C961.609533 705.725166 945.044252 689.174212 924.647713 689.174212z" p-id="1801" fill="#bfbfbf"></path></svg>
\ No newline at end of file
<svg width="128" height="128" xmlns="http://www.w3.org/2000/svg"><path d="M106.133 67.2a4.797 4.797 0 0 0-4.8 4.8c0 .187.014.36.027.533h-.027V118.4H9.6V26.667h50.133c2.654 0 4.8-2.147 4.8-4.8 0-2.654-2.146-4.8-4.8-4.8H9.6a9.594 9.594 0 0 0-9.6 9.6V118.4c0 5.307 4.293 9.6 9.6 9.6h91.733c5.307 0 9.6-4.293 9.6-9.6V72.533h-.026c.013-.173.026-.346.026-.533 0-2.653-2.146-4.8-4.8-4.8z"/><path d="M125.16 13.373L114.587 2.8c-3.747-3.747-9.854-3.72-13.6.027l-52.96 52.96a4.264 4.264 0 0 0-.907 1.36L33.813 88.533c-.746 1.76-.226 3.534.907 4.68 1.133 1.147 2.92 1.667 4.693.92l31.4-13.293c.507-.213.96-.52 1.36-.907l52.96-52.96c3.747-3.746 3.774-9.853.027-13.6zM66.107 72.4l-18.32 7.76 7.76-18.32L92.72 24.667l10.56 10.56L66.107 72.4zm52.226-52.227l-8.266 8.267-10.56-10.56 8.266-8.267.027-.026 10.56 10.56-.027.026z"/></svg>
\ No newline at end of file
<script lang="ts">
import {computed, defineComponent, h, watch, ref, unref, nextTick} from 'vue';
import {CheckboxGroup, RadioGroup, Select, TreeSelect, Cascader} from 'link-components';
import {getSelectListData, getSelectTreeData, getDictData, getDictTreeData} from '@/api/dynamic';
import {basicProps} from './props';
import isEqual from "lodash-es/isEqual";
import {useAttrs} from "element-plus";
import { encodeAes } from '@/utils/cipher';
export default defineComponent({
name: 'BindDict',
components: {
[Select.name]: Select,
[CheckboxGroup.name]: CheckboxGroup,
[RadioGroup.name]: RadioGroup,
[TreeSelect.name]: TreeSelect,
[Cascader.name]: Cascader,
},
inheritAttrs: false,
props: basicProps,
setup(props) {
const attrs = useAttrs();
const comElRef = ref<any>(null);
const computeBindProps = computed(() => {
const obj = {...attrs.value} as any;
if (props.apiType && props.apiType.toLowerCase() === 'dict') {
obj.api = getDictData;
if (['treeselect', 'cascader'].includes(props.type.toLowerCase())) {
obj.api = getDictTreeData;
}
obj.beforeFetch = () => {
return {
dictGroup: props.dictGroup
}
};
} else if(props.apiType && props.apiType.toLowerCase() === 'api'){
obj.beforeFetch = () => {
return {
type: 1,
serviceName: props.serviceName
}
};
} else {
obj.api = getSelectListData;
if (['treeselect', 'cascader'].includes(props.type.toLowerCase())) {
obj.api = getSelectTreeData;
}
obj.beforeFetch = () => {
return {
billVo: encodeAes(props.billVo),
dynFilter: encodeAes(props.dynFilter),
fields: encodeAes(props.fields),
orderCfgDto: props.orderCfgDto,
serviceName: props.serviceName
}
};
}
return obj;
});
const targetCom = computed(() => {
if (!props.type) {
return Select;
}
if (props.type.toLowerCase() === 'checkbox') {
return CheckboxGroup;
} else if (props.type.toLowerCase() === 'radio') {
return RadioGroup;
} else if (props.type.toLowerCase() === 'treeselect') {
return TreeSelect;
} else if (props.type.toLowerCase() === 'cascader') {
return Cascader;
}
return Select;
});
watch(() => computeBindProps.value.beforeFetch(), (value, oldValue) => {
let obj = value;
if (typeof value === "function") {
obj = value()
}
let oldObj = oldValue;
if (typeof oldValue === "function") {
oldObj = oldValue()
}
if (obj && !isEqual(obj, oldObj)) {
nextTick(() => {
setTimeout(() => {
if (unref(comElRef) && unref(comElRef).reload) {
unref(comElRef).reload()
}
}, 300)
})
}
},
{
immediate: false,
deep: true
}
);
const renderCom = () => {
return h(targetCom.value, {
ref: comElRef,
...computeBindProps.value
})
};
return () => renderCom()
}
})
</script>
import type {PropType} from 'vue';
import type {ApiType, DictType, OrderCfg} from "./types";
export const basicProps = {
apiType: {
type: String as PropType<ApiType>,
default: 'Select'
},
type: {
type: String as PropType<DictType>,
default: 'Select'
},
dictGroup: String,
billVo: String,
serviceName: String,
dynFilter: String,
fields: {
type: String,
default: 'id,name'
},
orderCfgDto: Object as PropType<OrderCfg>,
};
export interface OrderCfg {
"field": string,
"order": string
}
// 类型不区分大小写
export type DictType = 'Select' | 'TreeSelect' | 'Cascader' | 'Radio' | 'Checkbox';
export type ApiType = 'Dict' | 'Select';
export interface BindDictProps {
apiType: ApiType
type: DictType,
dictGroup?: string,
// 后端类全路径
billVo?: string,
// 后端服务名
serviceName?: string,
dynFilter?: string,
// 生成字段
fields?: string,
// 排序
orderCfgDto?: IOrderCfg
}
<script lang="ts">
import {computed, defineComponent, h, ref, unref, watch, inject} from 'vue';
import {DataBox} from 'link-components';
import type {TableColumnSchema} from 'link-components';
import {getEntityModel, getEntityListData} from '@/api/dynamic';
import {basicProps} from './props';
import isEqual from "lodash-es/isEqual";
import {useAttrs} from "element-plus";
import {encodeAes} from "@/utils/cipher";
export default defineComponent({
name: 'BindEntity',
components: {
[DataBox.name]: DataBox
},
inheritAttrs: false,
props: basicProps,
setup(props, {expose}) {
const $linkForm = inject('$linkForm', {}) as any;
const $linkFormItem = inject("$linkFormItem") as any;
const comElRef = ref<any>(null);
const attrs = useAttrs();
const tableColumns = ref<TableColumnSchema[]>([]);
const handleMergeColumns = (defColumns:TableColumnSchema[] = []) => {
if (!tableColumns.value.length) {
return defColumns;
}
return tableColumns.value.map(i => {
if (i.field === 'id') {
i.visible = false
}
const obj = defColumns.find(c => c.field === i.field);
if (obj) {
return obj;
}
if (i.width) {
i.minWidth = i.width;
delete i.width
}
return i;
})
};
const computeBindProps = computed(() => {
const obj = {...attrs.value, props: props.props} as any;
obj.api = getEntityListData;
obj.beforeFetch = () => {
return {
billVo: encodeAes(props.billVo),
dynFilter: encodeAes(props.dynFilter),
fields: encodeAes(props.fields),
queryFields: encodeAes(props.queryFields),
orderCfgDto: props.orderCfgDto,
serviceName: props.serviceName
}
};
obj.columns = handleMergeColumns(obj.columns!);
let labelField = props.labelField;
if (props.autoConvertAlias && ($linkForm && $linkForm.formModel) || props.tableRow) {
if (!labelField) {
const field = $linkFormItem?.schema.field;
labelField = field + '_alias'
}
const formModel = $linkForm.formModel || props.tableRow || {};
const {label, value} = obj.props;
obj.valueFormat = (data) => {
if (data) {
return data[value]
}
return ''
};
obj.valueParser = (val, index) => {
if (val) {
const labelAlias = formModel[labelField];
if (!!obj.multiple) {
return {[value]: val, [label]: labelAlias?labelAlias[index]: ''}
}
return {[value]: val, [label]: labelAlias}
}
return ''
};
obj.onConfirmData = (data) => {
if (attrs.value.onConfirmData && typeof attrs.value.onConfirmData === 'function') {
attrs.value.onConfirmData(data)
}
let labelAlias = '';
if (data) {
if (!!obj.multiple) {
labelAlias = data.map(i => i[label])
} else {
labelAlias = data[label]
}
}
formModel[labelField] = labelAlias
};
obj.tableProps = {
radioConfig: {labelField: 'radio', trigger: 'row'},
}
}
return obj;
});
watch(() => computeBindProps.value.beforeFetch(), (value, oldValue) => {
let obj = value;
if (typeof value === "function") {
obj = value()
}
let oldObj = oldValue;
if (typeof oldValue === "function") {
oldObj = oldValue()
}
if (obj && !isEqual(obj, oldObj) && obj.billVo) {
getEntityModel(obj).then((res: any) => {
if (res.code === 200) {
tableColumns.value = res.data || []
} else {
tableColumns.value = []
}
})
}
},
{
immediate: true,
deep: true
}
);
const renderCom = () => {
return h(DataBox, {
ref: comElRef,
...computeBindProps.value
})
};
expose({
open: () => unref(comElRef)?.open(),
close: () => unref(comElRef)?.close()
});
return () => renderCom()
}
})
</script>
import type {PropType} from 'vue';
import type {OrderCfg} from "./types";
import {DataBoxDefines} from "link-components";
export const basicProps = {
billVo: String,
serviceName: String,
dynFilter: String,
fields: {
type: String,
default: 'id,name'
},
// 需要生成查询的字段
queryFields: String,
orderCfgDto: Object as PropType<OrderCfg>,
props: {
type: Object as PropType<DataBoxDefines.Props>,
default: () => ({
label: "name",
value: "id",
})
},
// 自动翻译转换别名
autoConvertAlias: {
type: Boolean,
default: true
},
// 表格行数据
tableRow: {
type: Object
},
labelField: {
type: String,
default: ''
}
};
import {DataBoxProps} from "link-components";
export interface OrderCfg {
"field": string,
"order": 'asc' | 'desc'
}
export interface BindEntityProps extends DataBoxProps {
// 后端类全路径
billVo: string,
// 后端服务名
serviceName?: string,
// 生成字段
fields: string,
dynFilter?: string,
// 需要生成查询的字段
queryFields?: string,
orderCfgDto?: IOrderCfg,
// 自动翻译转换别名
autoConvertAlias: boolean,
// 表格行数据
tableRow?: Object,
// 别名字段名
labelField?: string
}
<template>
<el-select
class="icon-select"
v-model="getIconValue"
:popper-append-to-body="false"
@change="handlerChange"
filterable>
<template #prefix>
<svg-icon :name="getIconValue" :key="getIconValue" style="max-width: 30px" />
</template>
<el-option
v-for="itm in iconList"
:key="itm"
:label="itm"
:value="itm">
<svg-icon :name="itm" />
<span style="margin-left: 10px">{{ itm }}</span>
</el-option>
</el-select>
</template>
<script lang="ts">
import { ref, defineComponent, watch, computed} from "vue";
import {ElSelect, ElOption} from "element-plus";
import SvgIcon from '../SvgIcon/index.vue';
import * as elSvg from '@element-plus/icons-vue';
let icons:string[] = [];
const modules = import.meta.glob('../../assets/svg/*.svg');
for (const path in modules) {
const name = path.split('assets/svg/')[1].split('.svg')[0];
icons.push(name);
}
for (const path in elSvg) {
icons.push('element-' + path);
}
export default defineComponent({
name: 'IconSelect',
components: {
ElSelect,
ElOption,
SvgIcon
},
props: {
modelValue: {
type: String,
default: ''
}
},
emits: ['update:modelValue'],
setup(props, {emit}) {
const iconList = ref(icons);
const newModelValue = ref(props.modelValue);
watch(() => newModelValue.value, (value) => {
emit('update:modelValue', value)
},{
immediate: true,
deep: true
}
);
const getIconValue = computed(()=>{
return newModelValue.value || props.modelValue;
})
const handlerChange = (value: any)=>{
newModelValue.value = value;
}
return {
iconList,
handlerChange,
getIconValue,
newModelValue
}
}
})
</script>
<style lang='scss' scoped>
.icon-select {
width: 100%;
:deep(.el-input__inner) {
padding-left: 40px!important;
}
:deep(.el-input__prefix) {
.svg-icon {
color: var(--el-input-text-color);
}
}
}
</style>
<template>
<div class="prefix-cls relative">
<ElInput
v-bind="{...$attrs, ...$props}"
v-model="innerValueRef"
@change="handleChange"
type="password"
:disabled="disabled"
>
<template #[item]="data" v-for="item in Object.keys($slots)">
<slot :name="item" v-bind="data || {}"></slot>
</template>
</ElInput>
<div :class="`prefix-cls-bar`">
<div :class="`prefix-cls-bar--fill`" :data-score="getPasswordStrength"></div>
</div>
</div>
</template>
<script lang="ts">
import { defineComponent, computed, ref, watch, unref, watchEffect } from 'vue';
import { zxcvbn } from '@zxcvbn-ts/core';
export default defineComponent({
name: 'PasswordInput',
props: {
modelValue: String,
disabled: Boolean,
},
emits: ['score-change', 'change', 'update:modelValue'],
setup(props, { emit }) {
const innerValueRef = ref('');
const getPasswordStrength = computed(() => {
const { disabled } = props;
if (disabled) return -1;
const innerValue = unref(innerValueRef);
console.log(innerValue)
const score = innerValue ? zxcvbn(unref(innerValueRef)).score : -1;
console.log('score', score)
emit('score-change', score);
return score;
});
function handleChange(e: any) {
innerValueRef.value = e
emit('update:modelValue', e)
}
watchEffect(() => {
innerValueRef.value = props.modelValue || '';
});
watch(
() => unref(innerValueRef),
(val) => {
emit('change', val);
}
);
return {
getPasswordStrength,
handleChange,
innerValueRef,
};
},
});
</script>
<style lang="scss" scoped>
//@prefix-cls: ~'@{namespace}-strength-meter';
.prefix-cls {
width: 100%;
&-bar {
position: relative;
height: 6px;
margin: 10px auto 6px;
background-color: var(--el-text-color-disabled);
border-radius: 6px;
&::before,
&::after {
position: absolute;
z-index: 10;
display: block;
width: 20%;
height: inherit;
background-color: transparent;
border-color: var(--el-color-white);
border-style: solid;
border-width: 0 5px 0 5px;
content: '';
}
&::before {
left: 20%;
}
&::after {
right: 20%;
}
&--fill {
position: absolute;
width: 0;
height: inherit;
background-color: transparent;
border-radius: inherit;
transition: width 0.5s ease-in-out, background 0.25s;
&[data-score='0'] {
width: 20%;
background-color: var(--el-color-error);
}
&[data-score='1'] {
width: 40%;
background-color: var(--el-color-error-light-3);
}
&[data-score='2'] {
width: 60%;
background-color: var(--el-color-warning);
}
&[data-score='3'] {
width: 80%;
background-color: var(--el-color-success-light-3)
}
&[data-score='4'] {
width: 100%;
background-color: var(--el-color-success);
}
}
}
}
</style>
<script lang="ts">
import {h, resolveComponent, defineComponent, computed, unref} from 'vue';
import RenderSvg from './src/RenderSvg.vue'
export default defineComponent({
name: 'SvgIcon',
components: {RenderSvg},
props: {
// svg 图标组件名字
name: {
type: String,
},
iconClass: {
type: String,
},
// svg 大小
size: {
type: Number,
},
// svg 颜色
color: {
type: String,
},
},
setup(props) {
const name = unref(computed(()=> {
return props.name || props.iconClass as string
}));
if (name?.indexOf('element') > -1) {
return () => h('i', { class: 'el-icon', style: `--font-size: ${props.size};--color: ${props.color}` }, [h(resolveComponent(name))]);
} else if (name?.indexOf('iconfont') > -1) {
return () => h('i', { class: name, style: `font-size: ${props.size};color: ${props.color}` });
} else if (name) {
return () => h(RenderSvg, {
...props,
name: name
});
}
},
});
</script>
<template>
<svg :class="svgClass" v-bind="$attrs" :style="{color: color}">
<use :xlink:href="iconName" />
</svg>
</template>
<script lang="ts">
import {defineComponent, computed} from "vue";
export default defineComponent({
name: 'RenderSvg',
props: {
name: {
type: String,
required: true
},
iconClass: {
type: String,
},
color: {
type: String,
default: ''
},
size: {
type: [Number, String],
},
},
setup(props) {
const iconName = computed(()=> {
const name = props.name || props.iconClass as string;
return `#icon-${name}`
});
const svgClass = computed(()=> {
const name = props.name || props.iconClass as string;
if (name) {
return `svg-icon icon-${name}`
}
return 'svg-icon'
});
return {
iconName,
svgClass
}
}
})
</script>
<style scoped>
.svg-icon {
width: 1em;
height: 1em;
fill: currentColor;
vertical-align: middle;
}
</style>
import { tryOnMounted, tryOnUnmounted, useDebounceFn } from '@vueuse/core';
interface WindowSizeOptions {
once?: boolean;
immediate?: boolean;
listenerOptions?: AddEventListenerOptions | boolean;
}
export function useWindowSizeFn(fn: any, wait = 150, options?: WindowSizeOptions) {
let handler = () => {
fn();
};
const handleSize = useDebounceFn(handler, wait);
handler = handleSize;
const start = () => {
if (options && options.immediate) {
handler();
}
window.addEventListener('resize', handler);
};
const stop = () => {
window.removeEventListener('resize', handler);
};
tryOnMounted(() => {
start();
});
tryOnUnmounted(() => {
stop();
});
return [start, stop];
}
<template>
<div v-bind="{ ...$attrs }" ref="contentRef" class="link-Content" :style="getSContentStyle">
<slot></slot>
</div>
</template>
<script lang="ts">
import { defineComponent, CSSProperties, computed, ref, unref } from "vue";
import { useWindowSizeFn } from './hooks/useWindowSizeFn';
export default defineComponent({
components:{
},
setup() {
const contentRef = ref(null);
const bodyHeight = ref<Number>(0);
const getSContentStyle = computed((): CSSProperties => {
const contentEl = unref(contentRef) as any;
if(!contentEl){
return {};
}
const height = contentEl.parentNode.parentNode.offsetHeight;
unref(bodyHeight);
return {
height: `${height}px`,
minHeight: `${height}px`,
};
});
useWindowSizeFn(calcDivHeight, 280);
async function calcDivHeight() {
bodyHeight.value = document.body.offsetHeight
}
return {
getSContentStyle,
contentRef,
bodyHeight,
};
},
});
</script>
<style lang="scss" scoped>
</style>
export const permissionModule = {
dashboard:'dashboard', // 首页
};
/**
* 权限控制
* @param module
* @return {{view: string, submit: string, processView: string, pushData: string, save: string, del: string, approvalNoPass: string, changeEnable: string, approvalPass: string}}
*/
export function btnPermission(module) {
return {
add: `${module}.add`, // 新增
del: `${module}.delete`, // 删除
edit: `${module}.edit`, // 修改
view: `${module}.view`, // 查看
save: `${module}.save`, // 保存
submit: `${module}.submit`, // 提交
changeEnable: `${module}.changeEnable`, // 更改启用
approvalNoPass: `${module}.approveNoPass`, // 审批不通过
approvalPass: `${module}.approvePass`, // 审批通过
unApprove: `${module}.unApprove`, // 反审批
processView: `${module}.processView`, // 流程图查看
excelImport: `${module}.excelImport`, // 导入
excelExport: `${module}.excelExport`, // 导出
}
}
import {BindEntityProps} from "@/components/BindEntity/types";
import {serviceNameConst} from "@/config/constant";
export const SystemService = {
bindOrgProps: function (op?: BindEntityProps| Record<string, any>) {
return Object.assign({
serviceName: serviceNameConst.systemService,
type: 'tree',
billVo: 'com.link.jiandanbiao.sys.api.entity.LinkOrgInfo',
fields: 'id,name,code,parentId',
dynFilter: ``,
multiple: false,
queryFields: 'name',
props: {
label: 'name',
value: 'id'
},
columns: [
{
title: 'id',
field: 'id',
hide: true,
},
{
title: 'parentId',
field: 'parentId',
hide: true,
}
]
},op) as BindEntityProps
},
bindUserProps: function (op?: BindEntityProps | Record<string, any>) {
return Object.assign({
serviceName: serviceNameConst.systemService,
billVo: 'com.link.jiandanbiao.sys.api.entity.LinkUserInfo',
fields: 'id,name,code,phone,email',
dynFilter: ``,
multiple: false,
queryFields: 'name,code',
props: {
label: 'name',
value: 'id'
},
columns: [
{
title: 'id',
field: 'id',
hide: true,
}
],
key: 'system-service-user'
},op) as BindEntityProps
}
};
export const BasicService = {
};
export const serviceNameConst = {
systemService: 'link-system-start',
bpmService: 'link-bpm-start',
fileService: 'link-file-start',
jobService: 'link-job-start',
authService: 'link-auth',
};
import type { App } 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) {
app.directive('waves', {
mounted(el, binding) {
el.classList.add('waves-effect');
binding.value && el.classList.add(`waves-${binding.value}`);
function setConvertStyle(obj: { [key: string]: unknown }) {
let style: string = '';
for (let i in obj) {
if (obj.hasOwnProperty(i)) style += `${i}:${obj[i]};`;
}
return style;
}
function onCurrentClick(e: { [key: string]: unknown }) {
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) {
app.directive('drag', {
mounted(el, binding) {
if (!binding.value) return false;
const dragDom = document.querySelector(binding.value[0]) as HTMLElement;
const dragHeader = document.querySelector(binding.value[1]) as HTMLElement;
dragHeader.onmouseover = () => (dragHeader.style.cursor = `move`);
function down(e: any, type: string) {
// 鼠标按下,计算当前元素距离可视区的距离
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: any = getComputedStyle(dragDom).left;
let styT: any = 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: any, type: string, obj: any) {
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;
};
};
},
});
}
import type { App } from 'vue';
import permissionDirective from './permission';
import { wavesDirective, dragDirective } from './custom/index';
/**
* 导出指令方法:v-xxx
* @methods authDirective 用户权限指令,用法:v-auth
* @methods wavesDirective 按钮波浪指令,用法:v-waves
* @methods dragDirective 自定义拖动指令,用法:v-drag
*/
export function directive(app: App) {
// 用户权限指令
permissionDirective(app);
// 按钮波浪指令
wavesDirective(app);
// 自定义拖动指令
dragDirective(app);
}
import type { App } from 'vue';
import { usePermission } from '@/hooks/web/usePermission';
/**
* 用户权限指令
* @directive 多个权限验证,满足一个则显示(v-permission="[xxx]")
* @directive 多个权限验证,全部满足则显示(v-permission-all="[xxx,xxx]")
*/
export default function (app: App) {
const {hasPermission, hasPermissionAll} = usePermission();
// 多个权限验证,满足一个则显示(v-permission="[xxx,xxx]")
app.directive('permission', {
mounted(el, binding) {
if (!hasPermission(binding.value)) el.parentNode.removeChild(el);
},
});
// 多个权限验证,全部满足则显示(v-permission-all="[xxx,xxx]")
app.directive('permission-all', {
mounted(el, binding) {
if (!hasPermissionAll(binding.value)) el.parentNode.removeChild(el);
},
});
}
import {nextTick} from 'vue';
import {store} from '@/store'
import type {Router} from "vue-router";
/**
* 关闭当前菜单且返回上一菜单
*/
export function pageBack(router: Router) {
store.dispatch('tagsView/delView', router.currentRoute.value);
router.go(-1);
}
/**
* 刷新当前页面
*/
export function refreshCurrent(router: Router, query: {[key:string]: any}) {
store.dispatch('tagsView/delCachedView', router.currentRoute.value).then(() => {
nextTick(() => {
const {path} = router.currentRoute.value;
router.replace({
path: '/redirect' + path,
query: query
})
})
})
}
/**
* 拼接详情页路由地址
* @param router
* @param detailName
* @returns
*/
export function goDetailPath(router: any, detailName: any){
const path = router.currentRoute.value.path;
return `${path.substring(0,path.lastIndexOf('/'))}/${detailName}`
}
\ No newline at end of file
import {computed} from 'vue';
import {store} from '@/store';
/**
* 用户权限
*/
export function usePermission () {
const userPermission = computed(() => {
return store.state.permission.permission || []
});
function hasPermissionAll(permission: string[]) {
return permission.every((v: string) => {
return userPermission.value.some((val: string) => val === v);
})
}
function hasPermission(permission: string[]) {
return permission.some((v: string) => {
return userPermission.value.some((val: string) => val === v);
})
}
return {
hasPermission,
hasPermissionAll
}
}
<template>
<el-main class="layout-main">
<el-scrollbar
class="layout-scrollbar"
ref="layoutScrollbarRef"
:style="{padding: currentRouteMeta.isLink && currentRouteMeta.isIframe ? 0 : ''}">
<router-view v-slot="{ Component }">
<keep-alive :include="cachedViewsName">
<component :is="Component" :key="refreshRouterViewKey" />
</keep-alive>
</router-view>
</el-scrollbar>
</el-main>
</template>
<script lang="ts">
import {
computed,
defineComponent,
toRefs,
reactive,
getCurrentInstance,
watch,
onBeforeMount,
nextTick,
onUnmounted
} from 'vue';
import { useStore } from '@/store';
import { useRoute } from 'vue-router';
export default defineComponent({
name: 'AppMain',
setup() {
const { proxy } = getCurrentInstance() as any;
const route = useRoute();
const store = useStore();
const state: any = reactive({
headerHeight: '',
refreshRouterViewKey: null,
cachedViewsName: []
});
// 获取布局配置信息
const getThemeConfig = computed(() => {
return store.state.themeConfig.themeConfig;
});
const currentRouteMeta = computed(() => {
return route.meta;
});
// 获取组件缓存列表(name值)
const cachedViews = computed(() => {
return store.state.tagsView.cachedViews;
});
// 设置 main 的高度
const initHeaderHeight = () => {
let { isTagsview } = store.state.themeConfig.themeConfig;
if (isTagsview) return (state.headerHeight = `84px`);
else return (state.headerHeight = `50px`);
};
// 页面加载前,处理缓存,页面刷新时路由缓存处理
onBeforeMount(() => {
initHeaderHeight();
state.cachedViewsName = cachedViews.value;
proxy.mittBus.on('onTagsViewRefreshRouterView', (fullPath: string) => {
state.cachedViewsName = cachedViews.value.filter((name: string) => route.name !== name);
state.refreshRouterViewKey = null;
nextTick(() => {
state.refreshRouterViewKey = fullPath;
state.cachedViewsName = cachedViews.value;
});
});
});
// 页面卸载时
onUnmounted(() => {
proxy.mittBus.off('onTagsViewRefreshRouterView');
});
// 监听 themeConfig 配置文件的变化,更新菜单 el-scrollbar 的高度
watch(store.state.themeConfig.themeConfig, (val) => {
state.headerHeight = val.isTagsview ? '84px' : '50px';
if (val.isFixedHeaderChange !== val.isFixedHeader) {
if (!proxy.$refs.layoutScrollbarRef) return false;
proxy.$refs.layoutScrollbarRef.update();
}
});
// 监听路由变化,防止 tagsView 多标签时,切换动画消失
watch(
() => route.fullPath,
() => {
state.refreshRouterViewKey = route.fullPath;
}
);
return {
getThemeConfig,
currentRouteMeta,
...toRefs(state),
};
},
});
</script>
<template>
<div class="layout-columns-aside" v-show="!isTagsViewCurrentFull">
<div class="layout-logo-size" v-if="showLogo">
<img :src="logoImg" class="layout-logo-size-img" />
</div>
<el-scrollbar>
<ul>
<li
v-for="(v, k) in computeNavs"
:key="k"
@click="onColumnsAsideMenuClick(v, k)"
:ref="
(el) => {
if (el) columnsAsideOffsetTopRefs[k] = el;
}
"
class="layout-columns-nva-item"
:class="{ 'layout-columns-active': liIndex === k }"
:title="v.meta.title"
>
<div :class="setColumnsAsidelayout" v-if="!v.meta.isLink || (v.meta.isLink && v.meta.isIframe)">
<svg-icon :name="v.meta.icon" />
<div class="columns-vertical-title">
{{
v.meta.title && v.meta.title.length >= 4 ? v.meta.title.substr(0, setColumnsAsidelayout === 'columns-vertical' ? 4 : 3) : v.meta.title
}}
</div>
</div>
<div :class="setColumnsAsidelayout" v-else>
<a :href="v.meta.isLink" target="_blank">
<svg-icon :name="v.meta.icon" />
<div class="columns-vertical-title">
{{
v.meta.title && v.meta.title.length >= 4
? v.meta.title.substr(0, setColumnsAsidelayout === 'columns-vertical' ? 4 : 3)
: v.meta.title
}}
</div>
</a>
</div>
</li>
<div ref="columnsAsideActiveRef" :class="setColumnsAsideStyle"></div>
</ul>
</el-scrollbar>
</div>
</template>
<script lang="ts">
import {computed, nextTick, reactive, ref, toRefs, watch} from 'vue';
import {useRouter} from 'vue-router';
import {useStore} from '@/store';
import {resolvePath} from '@/utils/pathOperation';
import logoImg from '@/assets/default/logo.png'
export default {
name: 'ColumnsAside',
setup() {
const columnsAsideOffsetTopRefs: any = ref([]);
const columnsAsideActiveRef = ref();
const store = useStore();
const router = useRouter();
const state: any = reactive({
liIndex: -1,
routeSplit: [],
isNavClick: false,
});
// 一级导航菜单
const computeNavs = computed(() => {
return store.state.app.navRoutes;
});
const isTagsViewCurrentFull = computed(() => {
return store.state.tagsView.isCurrentViewFull;
});
// 设置分栏高亮风格
const setColumnsAsideStyle = computed(() => {
return store.state.themeConfig.themeConfig.columnsAsideStyle;
});
// 设置分栏布局风格
const setColumnsAsidelayout = computed(() => {
return store.state.themeConfig.themeConfig.columnsAsideLayout;
});
const difference = computed(() => {
return store.state.themeConfig.themeConfig.columnsAsideStyle === 'columnsRound' ? 3 : 0;
});
const showLogo = computed(() => {
let { isShowLogo } = store.state.themeConfig.themeConfig;
return isShowLogo;
});
// 设置菜单高亮位置移动
const setColumnsAsideMove = (k: number) => {
state.liIndex = k;
if (columnsAsideActiveRef.value) {
columnsAsideActiveRef.value.style.top = `${columnsAsideOffsetTopRefs.value[k].offsetTop + difference.value}px`;
}
};
// 菜单高亮点击事件
const onColumnsAsideMenuClick = (v: Object, k: number) => {
state.isNavClick = true;
let { path, redirect } = v as any;
setColumnsAsideMove(k);
store.dispatch('app/setMenuRoutes', getChildren(path));
store.dispatch('app/setCollapse', false);
if (redirect) router.push(redirect);
else router.push(path);
};
// 设置高亮动态位置
const onColumnsAsideDown = (k: number) => {
nextTick(() => {
setColumnsAsideMove(k);
});
};
// 传送当前子级数据到菜单中
const getChildren = (path: string): any[] => {
const currentPathSplit = path.split('/');
let children: any[] = [];
store.state.permission.routes.map((v: any, k: number) => {
if (v.path === `/${currentPathSplit[1]}` || v.redirect === `/${currentPathSplit[1]}`) {
v['k'] = k;
children = [{ ...v }];
if (v.children && v.children.length) {
children = v.children.map((c:any) => {
const obj = {...c};
if (!obj.mate || (!obj.mate.isLink && !obj.mate.isIframe)) {
obj.path = resolvePath(v.path, obj.path);
}
return obj;
});
}
}
});
return children;
};
// tagsView 点击时,根据路由查找下标,实现左侧菜单高亮
const setColumnsMenuHighlight = (path: string) => {
state.routeSplit = path.split('/');
state.routeSplit.shift();
const routeFirst = `/${state.routeSplit[0]}`;
const currentSplitRouteIdx = computeNavs.value.findIndex((v: any) => v.path === routeFirst || v.redirect === routeFirst);
// 延迟拿值,防止取不到
setTimeout(() => {
if (currentSplitRouteIdx !== -1) {
onColumnsAsideDown(currentSplitRouteIdx);
}
store.dispatch('app/setMenuRoutes', getChildren(path));
}, 0);
};
watch(router.currentRoute, (val) => {
if (!state.isNavClick) {
setColumnsMenuHighlight(val.path);
}
state.isNavClick = false
}, {
immediate: true
});
return {
showLogo,
logoImg,
computeNavs,
columnsAsideOffsetTopRefs,
columnsAsideActiveRef,
isTagsViewCurrentFull,
onColumnsAsideDown,
setColumnsAsideStyle,
setColumnsAsidelayout,
onColumnsAsideMenuClick,
...toRefs(state),
};
},
};
</script>
<style scoped lang="scss">
.layout-columns-aside {
width: 70px;
height: 100%;
background: var(--bg-columnsMenuBar);
ul {
position: relative;
li {
color: var(--bg-columnsMenuBarColor);
width: 100%;
height: 50px;
text-align: center;
display: flex;
cursor: pointer;
position: relative;
z-index: 1;
&:hover {
color: var(--color-primary);
a {
color: var(--color-primary);
}
}
.columns-vertical {
margin: auto;
.columns-vertical-title {
font-size: var(--el-font-size-base);
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(--bg-columnsMenuBarColor);
}
}
.layout-columns-active {
color: var(--bg-activeMenuBarColor) !important;
background-color: var(--bg-activeMenuBar);
transition: 0.3s ease-in-out;
}
.columns-round {
color: var(--color-whites);
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;
}
}
.layout-logo-size {
width: 100%;
height: 50px;
display: flex;
cursor: pointer;
animation: logoAnimation 0.3s ease-in-out;
&-img {
width: 46px;
margin: auto;
}
&:hover {
img {
animation: logoAnimation 0.3s ease-in-out;
}
}
}
}
</style>
<template>
<div class="h100 layout-aside" v-show="!isTagsViewCurrentFull">
<Logo v-if="showLogo" />
<el-scrollbar class="flex-auto">
<menubar :menus="computeMenus" />
</el-scrollbar>
</div>
</template>
<script lang="ts">
import {computed, getCurrentInstance, onBeforeMount, reactive} from 'vue';
import {useStore} from '@/store';
import Logo from '../Logo/index.vue';
import Menubar from '../Menubar/index.vue';
import {filterShowMenus} from '../../hooks'
export default {
name: 'Aside',
components: { Logo, Menubar },
setup() {
const { proxy } = getCurrentInstance() as any;
const store = useStore();
const state: any = reactive({
clientWidth: '',
});
// 设置菜单展开/收起时的宽度
const setCollapseStyle = computed(() => {
const collapse = store.state.app.collapse;
const { layout, menuBar } = store.state.themeConfig.themeConfig;
const asideBrColor = ['#FFFFFF', '#ffffff', '#FFF', '#fff'].includes(menuBar) ? 'layout-el-aside-br-color' : '';
// 判断是否是手机端
if (state.clientWidth <= 1000) {
if (collapse) {
document.body.setAttribute('class', 'el-popup-parent--hidden');
const asideEle = document.querySelector('.layout-container') as HTMLElement;
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 (collapse) {
return [asideBrColor, 'layout-aside-pc-1'];
} else {
return [asideBrColor, 'layout-aside-pc-220'];
}
} else {
// 其它布局给 64px
if (collapse) {
return [asideBrColor, 'layout-aside-pc-64'];
} else {
return [asideBrColor, 'layout-aside-pc-220'];
}
}
}
});
// 关闭移动端蒙版
const closeLayoutAsideMobileMode = () => {
const el = document.querySelector('.layout-aside-mobile-mode');
el && el.parentNode?.removeChild(el);
const clientWidth = document.body.clientWidth;
if (clientWidth < 1000) {
store.dispatch('app/setCollapse', false)
}
document.body.setAttribute('class', '');
};
// 设置显示/隐藏 logo
const showLogo = computed(() => {
let { layout, isShowLogo } = store.state.themeConfig.themeConfig;
return isShowLogo && ['defaults', 'columns'].includes(layout);
});
const computeMenus = computed(() => {
let { layout, isClassicSplitMenu } = store.state.themeConfig.themeConfig;
if (layout == 'columns' || (isClassicSplitMenu && layout === 'classic')) {
return store.state.app.menuRoutes
}
return filterShowMenus(store.state.permission.routes)
});
// 获取卡片全屏信息
const isTagsViewCurrentFull = computed(() => {
return store.state.tagsView.isCurrentViewFull;
});
// 设置菜单导航是否固定(移动端)
const initMenuFixed = (clientWidth: number) => {
state.clientWidth = clientWidth;
};
// 页面加载前
onBeforeMount(() => {
initMenuFixed(document.body.clientWidth);
proxy.mittBus.on('layoutMobileResize', (res: any) => {
initMenuFixed(res.clientWidth);
closeLayoutAsideMobileMode();
});
});
return {
computeMenus,
setCollapseStyle,
showLogo,
isTagsViewCurrentFull
};
},
};
</script>
<template>
<div class="layout-logo" v-if="!computeIsCollapse" @click="changeMenuCollapse">
<template v-if="!computeIsColumns">
<img :src="logoImg" class="layout-logo-medium-img" />
<h3 class="layout-logo-title">{{ computeLogoTitle }}</h3>
</template>
<h3 v-else class="layout-logo--center-title">{{ computeLogoTitle }}</h3>
</div>
<div class="layout-logo-size" v-else @click="changeMenuCollapse">
<img :src="logoImg" class="layout-logo-size-img" />
</div>
</template>
<script lang="ts">
import {computed} from 'vue';
import {useStore} from '@/store';
import logoImg from '@/assets/default/logo.png'
import {changeMenuCollapse} from "../../hooks";
export default {
name: 'Logo',
setup() {
const store = useStore();
// 获取布局配置信息
const computeLogoTitle = computed(() => {
return store.state.themeConfig.themeConfig.globalTitle;
});
// 是否分栏布局
const computeIsColumns = computed(() => {
return store.state.themeConfig.themeConfig.layout === 'columns';
});
// 设置 logo 的显示。classic 经典布局默认显示 logo
const computeIsCollapse = computed(() => {
let {layout} = store.state.themeConfig.themeConfig;
if (layout === 'classic') {
return false;
}
return store.state.app.collapse;
});
return {
logoImg,
computeIsCollapse,
computeIsColumns,
computeLogoTitle,
changeMenuCollapse,
};
},
};
</script>
<style scoped lang="scss">
.layout-logo {
height: var(--height-navBar);
display: flex;
align-items: center;
padding-left: 20px;
box-shadow: rgb(0 21 41 / 2%) 0 1px 4px;
color: var(--color-primary);
font-size: 16px;
cursor: pointer;
animation: logoAnimation 0.3s ease-in-out;
&:hover {
span {
color: var(--color-primary-light-2);
}
}
&-medium-img {
width: 70px;
margin-right: 20px;
}
&-title {
position: relative;
&::before {
position: absolute;
left: -10px;
content: '';
top: 2px;
bottom: 2px;
width: 2px;
background-color: var(--color-primary);
}
font-weight: 600;
}
&--center-title {
width: 100%;
height: 100%;
display: inline-block;
line-height: 50px;
vertical-align: middle;
text-align: center;
padding-right: 18px;
position: relative;
white-space: nowrap;
text-overflow: ellipsis;
overflow: hidden;
&::after {
position: absolute;
content: '';
bottom: 2px;
left: 0;
right: 20px;
height: 1px;
background-color: var(--color-primary-light-2);
}
}
}
.layout-logo-size {
width: 100%;
height: var(--height-navBar);
display: flex;
cursor: pointer;
animation: logoAnimation 0.3s ease-in-out;
&-img {
width: 40px;
margin: auto;
}
&:hover {
img {
animation: logoAnimation 0.3s ease-in-out;
}
}
}
</style>
<template>
<template v-if="!item.meta || !item.meta.isHide">
<template v-if="hasOneShowingChild && (!onlyOneChild.children || onlyOneChild.noShowingChildren)">
<menubar-link v-if="onlyOneChild.meta" :to="routePathResolve(onlyOneChild)">
<el-menu-item :index="routePathResolve(onlyOneChild)">
<svg-icon :name="onlyOneChild.meta.icon" />
<span class="el-menu__span">{{ onlyOneChild.meta.title }}</span>
</el-menu-item>
</menubar-link>
</template>
<el-sub-menu v-else :index="routePathResolve(item)">
<template v-if="item.meta" #title>
<svg-icon :name="item.meta.icon" />
<span>{{ item.meta.title }}</span>
</template>
<menubar-item
v-for="child in item.children"
:key="child.path"
:item="child"
:base-path="routePathResolve(child)"
/>
</el-sub-menu>
</template>
</template>
<script lang="ts">
import { defineComponent, computed, PropType } from 'vue';
import type { RouteRecordRaw } from "vue-router";
import MenubarLink from './MenubarLink.vue';
import { resolvePath } from '@/utils/pathOperation';
export default defineComponent({
name: 'MenubarItem',
components: {MenubarLink},
props: {
item: {
type: Object as PropType<RouteRecordRaw>,
required: true
},
basePath: {
type: String,
default: ''
}
},
setup(props) {
const { item } = props as any;
const onlyOneChild: any = computed(() => {
if (item.children) {
const showingChildren = item.children.filter((itm: RouteRecordRaw) => !itm.meta || !itm.meta.isHide);
if (showingChildren.length === 1) {
return showingChildren[0]
}
if (showingChildren.length === 0) {
return { ...item, path: '', noShowingChildren: true };
}
}
return { ...item, path: '', noShowingChildren: true };
});
const hasOneShowingChild = computed(() => {
if (item.children) {
const showingChildren = item.children.filter((itm: RouteRecordRaw) => !itm.meta || !itm.meta.isHide);
return showingChildren.length <= 1;
}
return true
});
const routePathResolve = (routeRecord: RouteRecordRaw) => {
const {path} = routeRecord;
let routePath = path;
if (routePath && routePath.startsWith('http') || routePath.startsWith('/')) {
return routePath
}
let basePath = props.basePath;
if (basePath && basePath.startsWith('http')) {
return basePath
}
return resolvePath(basePath, routePath);
};
return {
onlyOneChild,
hasOneShowingChild,
routePathResolve
};
},
});
</script>
<template>
<component :is="linkProps.is" v-bind="linkProps">
<slot></slot>
</component>
</template>
<script lang="ts">
import {defineComponent, ref} from 'vue'
export default defineComponent({
props: {
to: {
type: String,
required: true
}
},
setup(props) {
let linkProps: Object = ref({});
if (props.to && props.to.startsWith('http')) {
linkProps = {
is: 'a',
href: props.to,
target: '_blank',
rel: 'opener'
}
} else {
linkProps = {
is: 'router-link',
to: props.to
}
}
return {
linkProps
}
},
})
</script>
<template>
<div class="el-menu-horizontal-warp">
<el-scrollbar @wheel.native.prevent="onElMenuHorizontalScroll" ref="elMenuHorizontalScrollRef">
<el-menu :default-active="computedDefaultActive" background-color="transparent" mode="horizontal" :ellipsis="false">
<sidebar-item v-for="(route,index) in menuLists" :key="index" :item="route" :base-path="route.path" />
</el-menu>
</el-scrollbar>
</div>
</template>
<script lang="ts">
import { computed, defineComponent, getCurrentInstance, onMounted, nextTick } from 'vue';
import { useRoute, onBeforeRouteUpdate } from 'vue-router';
import { useStore } from '@/store';
import SidebarItem from './sidebarItem.vue';
export default defineComponent({
name: 'NavMenuHorizontal',
components: { SidebarItem },
props: {
menuList: {
type: Array,
default: () => [],
},
},
setup(props) {
const { proxy } = getCurrentInstance() as any;
const route = useRoute();
const store = useStore();
const computedDefaultActive = computed(() => {
const { path, meta } = route;
if (store.state.themeConfig.themeConfig.layout === 'classic') {
return `/${path.split('/')[1]}`;
} else {
const pathSplit = meta.isDynamic ? (meta.isDynamicPath as string).split('/') : path.split('/');
if (pathSplit.length >= 4 && meta.isHide) {
return pathSplit.splice(0, 3).join('/')
}
return path
}
});
// 获取父级菜单数据
const menuLists = computed(() => {
return props.menuList;
});
// 设置横向滚动条可以鼠标滚轮滚动
const onElMenuHorizontalScroll = (e: any) => {
const eventDelta = e.wheelDelta || -e.deltaY * 40;
proxy.$refs.elMenuHorizontalScrollRef.$refs.wrap.scrollLeft = proxy.$refs.elMenuHorizontalScrollRef.$refs.wrap.scrollLeft + eventDelta / 4;
};
// 初始化数据,页面刷新时,滚动条滚动到对应位置
const initElMenuOffsetLeft = () => {
nextTick(() => {
let els: any = document.querySelector('.el-menu.el-menu--horizontal li.is-active');
if (!els) return false;
proxy.$refs.elMenuHorizontalScrollRef.$refs.wrap.scrollLeft = els.offsetLeft;
});
};
// 路由过滤递归函数
const filterRoutesFun = (arr: Array<object>) => {
return arr
.filter((item: any) => !item.meta.isHide)
.map((item: any) => {
item = Object.assign({}, item);
if (item.children) item.children = filterRoutesFun(item.children);
return item;
});
};
// 传送当前子级数据到菜单中
const setSendClassicChildren = (path: string) => {
const currentPathSplit = path.split('/');
let currentData: any = {};
filterRoutesFun(store.state.permission.routes).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(() => {
initElMenuOffsetLeft();
});
// 路由更新时
onBeforeRouteUpdate((to) => {
// 修复经典布局开启切割菜单时,点击tagsView后左侧导航菜单数据不变的问题
let { layout, isClassicSplitMenu } = store.state.themeConfig.themeConfig;
if (layout === 'classic' && isClassicSplitMenu) {
proxy.mittBus.emit('setSendClassicChildren', setSendClassicChildren(to.path));
}
});
return {
computedDefaultActive,
menuLists,
onElMenuHorizontalScroll,
};
},
});
</script>
<style scoped lang="scss">
.el-menu-horizontal-warp {
flex: 1;
overflow: hidden;
margin-right: 30px;
::v-deep(.el-scrollbar__bar.is-vertical) {
display: none;
}
.el-menu.el-menu--horizontal {
display: flex;
height: 100%;
width: 100%;
box-sizing: border-box;
}
}
</style>
<template>
<div class="el-menu-horizontal-warp" v-if="mode === 'horizontal'">
<el-scrollbar @wheel.native.prevent="onElMenuHorizontalScroll" ref="elMenuHorizontalScrollRef">
<el-menu
:default-active="computeDefaultActive"
background-color="transparent"
mode="horizontal"
:ellipsis="false"
menu-trigger="click"
@select="handleSelectMenu">
<menubar-item v-for="(route,index) in menus" :key="route.path + index" :item="route" :base-path="route.path" />
</el-menu>
</el-scrollbar>
</div>
<template v-else>
<el-menu
:default-active="computeDefaultActive"
background-color="transparent"
:collapse="computeCollapse"
:unique-opened="computeUniqueOpened"
:collapse-transition="false"
:mode="mode"
@select="handleSelectMenu">
<menubar-item v-for="(route,index) in menus" :key="route.path + index" :item="route" :base-path="route.path" />
</el-menu>
</template>
</template>
<script lang="ts">
import { computed, defineComponent, ref, PropType } from 'vue';
import { useRouter } from 'vue-router';
import { useStore } from '@/store';
import MenubarItem from './MenubarItem.vue';
export default defineComponent({
name: 'Menubar',
components: { MenubarItem },
props: {
menus: {
type: Array,
default: () => [],
},
mode: {
type: String as PropType<'horizontal' | 'vertical'>,
default: 'vertical'
}
},
emits: ['menu-select'],
setup(_props, {emit}) {
const elMenuHorizontalScrollRef = ref(null) as any;
const store = useStore();
const { currentRoute } = useRouter();
const computeDefaultActive = computed(() => {
const { meta, path } = currentRoute.value;
if (meta.activeMenu) {
return meta.activeMenu as string
}
return path
});
const computeCollapse = computed(() => {
return store.state.app.collapse;
});
// 获取布局配置信息
const computeUniqueOpened = computed(() => {
return store.state.themeConfig.themeConfig.isUniqueOpened;
});
const onElMenuHorizontalScroll = (e: any) => {
const eventDelta = e.wheelDelta || -e.deltaY * 40;
elMenuHorizontalScrollRef.value.$refs.wrap.scrollLeft = elMenuHorizontalScrollRef.value.$refs.wrap.scrollLeft + eventDelta / 4;
};
const handleSelectMenu = (path: string) => {
emit('menu-select', path);
// if (currentRoute.value.fullPath === path) {
// return;
// } else if (path) {
// push(path)
// }
};
return {
computeDefaultActive,
computeCollapse,
computeUniqueOpened,
elMenuHorizontalScrollRef,
onElMenuHorizontalScroll,
handleSelectMenu
};
},
});
</script>
<style scoped lang="scss">
.el-menu-horizontal-warp {
flex: 1;
overflow: hidden;
margin-right: 30px;
::v-deep(.el-scrollbar__bar.is-vertical) {
display: none;
}
.el-menu.el-menu--horizontal {
display: flex;
height: 100%;
width: 100%;
box-sizing: border-box;
}
}
</style>
import type {RouteRecordRaw} from "vue-router";
export interface INavMenuProp {
menus: RouteRecordRaw[],
mode: 'horizontal' | 'vertical',
}
<template>
<div class="layout-navbars-breadcrumb" v-if="computeShowBreadcrumb">
<svg-icon
class="layout-navbars-breadcrumb-icon"
:name="computeIsCollapse ? 'elementExpand' : 'elementFold'"
@click="changeMenuCollapse"
/>
<el-breadcrumb class="layout-navbars-breadcrumb-hide" separator="/">
<transition-group name="breadcrumb" mode="out-in">
<el-breadcrumb-item v-for="(v, k) in computeBreadcrumbList" :key="v.meta.title">
<span v-if="k === computeBreadcrumbList.length - 1" class="layout-navbars-breadcrumb-span">
{{ v.meta.title }}
</span>
<a v-else @click.prevent="onBreadcrumbClick(v)">
{{ v.meta.title }}
</a>
</el-breadcrumb-item>
</transition-group>
</el-breadcrumb>
</div>
</template>
<script lang="ts">
import {computed} from 'vue';
import { useRoute, useRouter} from 'vue-router';
import {useStore} from '@/store';
import {changeMenuCollapse} from "../../../hooks";
export default {
name: 'Breadcrumb',
setup() {
const store = useStore();
const route = useRoute();
const router = useRouter();
const computeIsCollapse = computed(() => {
return store.state.app.collapse;
});
// 获取布局配置信息
const computeThemeConfig = computed(() => {
return store.state.themeConfig.themeConfig;
});
// 动态设置经典、横向布局不显示
const computeShowBreadcrumb = computed(() => {
const { layout, isBreadcrumb } = computeThemeConfig.value;
if (layout === 'classic' || layout === 'transverse') {
return false;
} else {
return isBreadcrumb;
}
});
const computeBreadcrumbList = computed(() => {
if (!computeShowBreadcrumb.value) {
return []
}
return route.matched.filter(item => {
if (!item.meta || !item.meta.title) {
return false
}
if (item.children && item.children.length === 1) {
return false
}
return true
});
});
// 面包屑点击时
const onBreadcrumbClick = (v: any) => {
const { redirect, path } = v;
if (redirect) router.push(redirect);
else router.push(path);
};
return {
computeIsCollapse,
computeThemeConfig,
computeBreadcrumbList,
computeShowBreadcrumb,
changeMenuCollapse,
onBreadcrumbClick
};
},
};
</script>
<style scoped lang="scss">
.layout-navbars-breadcrumb {
flex: 1;
height: inherit;
display: flex;
align-items: center;
padding-left: 15px;
.layout-navbars-breadcrumb-icon {
cursor: pointer;
font-size: 18px;
margin-right: 15px;
color: var(--bg-topBarColor);
}
.layout-navbars-breadcrumb-span {
opacity: 0.7;
color: var(--bg-topBarColor);
}
::v-deep(.el-breadcrumb__separator) {
opacity: 0.7;
color: var(--bg-topBarColor);
}
}
</style>
<template>
<div class="layout-navbars-breadcrumb-user-news">
<div class="head-box">
<div class="head-box-title">通知</div>
<div class="head-box-btn" v-if="newsList.length > 0" @click="onAllReadClick">全部已读</div>
</div>
<div class="content-box">
<template v-if="newsList.length > 0">
<div class="content-box-item" v-for="(v, k) in newsList" :key="k">
<div>{{ v.label }}</div>
<div class="content-box-msg">
{{ v.value }}
</div>
<div class="content-box-time">{{ v.time }}</div>
</div>
</template>
<el-empty description="暂无通知" v-else />
</div>
<div class="foot-box" @click="onGoToGiteeClick" v-if="newsList.length > 0">前往消息中心</div>
</div>
</template>
<script lang="ts">
import { reactive, toRefs } from 'vue';
export default {
name: 'MessageBox',
setup() {
const state = reactive({
newsList: [
{
label: '关于版本发布的通知',
value: 'sinra-admin,基于 vue3 + CompositionAPI + typescript + vite + element plus,正式发布时间:2022年02月28日!',
time: '2022-01-20',
}
],
});
// 全部已读点击
const onAllReadClick = () => {
state.newsList = [];
};
// 前往通知中心点击
const onGoToGiteeClick = () => {
};
return {
onAllReadClick,
onGoToGiteeClick,
...toRefs(state),
};
},
};
</script>
<style scoped lang="scss">
.layout-navbars-breadcrumb-user-news {
.head-box {
display: flex;
border-bottom: 1px solid #ebeef5;
box-sizing: border-box;
color: #333333;
justify-content: space-between;
height: 35px;
align-items: center;
.head-box-btn {
color: var(--color-primary);
font-size: 13px;
cursor: pointer;
opacity: 0.8;
&:hover {
opacity: 1;
}
}
}
.content-box {
font-size: 13px;
.content-box-item {
padding-top: 12px;
&:last-of-type {
padding-bottom: 12px;
}
.content-box-msg {
color: #999999;
margin-top: 5px;
margin-bottom: 5px;
}
.content-box-time {
color: #999999;
}
}
}
.foot-box {
height: 35px;
color: var(--color-primary);
font-size: 13px;
cursor: pointer;
opacity: 0.8;
display: flex;
align-items: center;
justify-content: center;
border-top: 1px solid #ebeef5;
&:hover {
opacity: 1;
}
}
::v-deep(.el-empty__description p) {
font-size: 13px;
}
}
</style>
<template>
<div class="layout-search-dialog">
<el-dialog v-model="isShowSearch" width="300px" destroy-on-close :modal="false" fullscreen :show-close="false">
<el-autocomplete
v-model="menuQuery"
:fetch-suggestions="menuSearch"
size="large"
placeholder="菜单搜索:支持中文、路由路径"
ref="layoutMenuAutocompleteRef"
@select="onHandleSelect"
@blur="onSearchBlur"
>
<template #prefix>
<el-icon class="el-input__icon">
<elementSearch />
</el-icon>
</template>
<template #default="{ item }">
<div><i :class="item.meta.icon" class="mr10"></i>{{ item.meta.title }}</div>
</template>
</el-autocomplete>
</el-dialog>
</div>
</template>
<script lang="ts">
import { reactive, toRefs, defineComponent, ref, nextTick } from 'vue';
import { useRouter } from 'vue-router';
import { useStore } from '@/store';
export default defineComponent({
name: 'Search',
setup() {
const layoutMenuAutocompleteRef = ref();
const store = useStore();
const router = useRouter();
const state: any = reactive({
isShowSearch: false,
menuQuery: '',
tagsViewList: [],
});
// 搜索弹窗打开
const openSearch = () => {
state.menuQuery = '';
state.isShowSearch = true;
initTageView();
nextTick(() => {
layoutMenuAutocompleteRef.value.focus();
});
};
// 搜索弹窗关闭
const closeSearch = () => {
state.isShowSearch = false;
};
// 菜单搜索数据过滤
const menuSearch = (queryString: any, cb: any) => {
let results = queryString ? state.tagsViewList.filter(createFilter(queryString)) : state.tagsViewList;
cb(results);
};
// 菜单搜索过滤
const createFilter = (queryString: any) => {
return (restaurant: any) => {
return (
restaurant.path.toLowerCase().indexOf(queryString.toLowerCase()) > -1 ||
restaurant.meta.title.toLowerCase().indexOf(queryString.toLowerCase()) > -1
);
};
};
// 初始化菜单数据
const initTageView = () => {
if (state.tagsViewList.length > 0) return false;
store.state.permission.routes.map((v: any) => {
if (!v.meta.isHide) state.tagsViewList.push({ ...v });
});
};
// 当前菜单选中时
const onHandleSelect = (item: any) => {
let { path, redirect } = item;
if (item.meta.isLink && !item.meta.isIframe) window.open(item.meta.isLink);
else if (redirect) router.push(redirect);
else router.push(path);
closeSearch();
};
// input 失去焦点时
const onSearchBlur = () => {
closeSearch();
};
return {
layoutMenuAutocompleteRef,
openSearch,
closeSearch,
menuSearch,
onHandleSelect,
onSearchBlur,
...toRefs(state),
};
},
});
</script>
<style scoped lang="scss">
.layout-search-dialog {
::v-deep(.el-dialog) {
box-shadow: unset !important;
border-radius: 0 !important;
background: rgba(0, 0, 0, 0.5);
}
::v-deep(.el-autocomplete) {
width: 560px;
position: absolute;
top: 100px;
left: 50%;
transform: translateX(-50%);
}
}
</style>
<template>
<link-dialog
@register="registerDialog"
@ok="handleSubmit"
:showFooter="true">
<link-form @register="registerForm">
<template #newPassword="{ model }">
<PasswordInput v-model:modelValue="model.newPassword" :show-password="true" placeholder="请输入新密码" />
</template>
</link-form>
</link-dialog>
</template>
<script lang="ts">
import {defineComponent, nextTick} from 'vue';
import {FormSchema, useDialog, useForm} from 'link-components';
import {ElMessage, ElMessageBox} from 'element-plus';
import {updatePwd} from "@/api/sys/User";
import {useStore} from "@/store";
import {useRouter} from "vue-router";
import PasswordInput from "@/components/PasswordInput/index.vue";
const formSchema: FormSchema[] = [
{
title: '原密码',
field: 'oldPassword',
component: 'Input',
required: true,
componentProps: {
type: 'password',
showPassword: true
}
},
{
title: '新密码',
field: 'newPassword',
component: 'Input',
slot: 'newPassword',
// required: true,
componentProps: {
type: 'password',
showPassword: true
}
},
{
title: '确认密码',
field: 'password',
component: 'Input',
// required: true,
componentProps: {
type: 'password',
showPassword: true
}
},
];
export default defineComponent({
name: 'UpdatePassword',
components: {
PasswordInput
},
setup(_props, {expose}) {
const store = useStore();
const router = useRouter();
const [registerDialog, {open, setProps}] = useDialog({
title: '修改密码',
top: 150,
width: 450,
height: 350
});
const [registerForm, {validate, updateSchema, validateField, getFieldsValue}] = useForm({
schemas: formSchema,
labelWidth: 80,
baseColProps: 24,
});
const validatePass = (_rule: any, value: string, callback: any) => {
if (!value) {
callback(new Error('新密码不能为空'))
} else {
const password = getFieldsValue('password');
if (password !== '') {
validateField('password', () => null)
}
callback()
}
};
const validatePass2 = (_rule: any, value: string, callback: any) => {
if (!value) {
callback(new Error('请再次确认密码'))
} else {
const newPassword = getFieldsValue('newPassword');
if (value !== newPassword) {
callback(new Error("两次密码输入不匹配"))
} else {
callback()
}
}
};
const handleSubmit = async() => {
const form = await validate();
ElMessageBox.confirm('是否确认提交修改?', '确认', {
draggable: true
}).then(() => {
setProps({confirmLoading: true});
updatePwd(form).then((res: any) => {
setProps({confirmLoading: false});
if (res.code === 200) {
ElMessage.success(res.msg);
store.dispatch('user/logout').then(() => {
router.push(`/login?redirect=${router.currentRoute.value.fullPath}`);
location.reload();
})
}
}).catch(() => {
setProps({confirmLoading: false});
})
});
};
expose({
open: async (data) => {
await open(data);
await nextTick();
updateSchema([
{
field: 'newPassword',
rules: [{ required: true, validator: validatePass, trigger: 'blur' }]
},
{
field: 'password',
rules: [{ required: true, validator: validatePass2, trigger: 'blur' }]
},
]);
}
});
return {
registerDialog,
registerForm,
handleSubmit
}
}
})
</script>
<template>
<div class="layout-navbars-breadcrumb-user" :style="{ flex: layoutUserFlexNum }">
<el-dropdown :show-timeout="70" :hide-timeout="50" trigger="click" @command="onComponentSizeChange">
<div class="layout-navbars-breadcrumb-user-icon">
<i class="iconfont icon-ziti" title="组件大小"></i>
</div>
<template #dropdown>
<el-dropdown-menu>
<el-dropdown-item command="default" :disabled="disabledSize === 'default'">默认</el-dropdown-item>
<el-dropdown-item command="large" :disabled="disabledSize === 'large'">超大</el-dropdown-item>
<el-dropdown-item command="small" :disabled="disabledSize === 'small'">小型</el-dropdown-item>
</el-dropdown-menu>
</template>
</el-dropdown>
<div class="layout-navbars-breadcrumb-user-icon" @click="onSearchClick">
<el-icon title="菜单搜索">
<elementSearch />
</el-icon>
</div>
<div class="layout-navbars-breadcrumb-user-icon" @click="onSettingClick">
<i class="icon-skin iconfont" title="布局配置"></i>
</div>
<div class="layout-navbars-breadcrumb-user-icon" @click="isShowUserNewsPopover = !isShowUserNewsPopover">
<el-popover placement="bottom" trigger="click" v-model:visible="isShowUserNewsPopover" :width="300" popper-class="el-popover-pupop-user-news">
<template #reference>
<el-badge :is-dot="true">
<el-icon title="消息">
<elementBell />
</el-icon>
</el-badge>
</template>
<transition name="el-zoom-in-top">
<message-box v-show="isShowUserNewsPopover" />
</transition>
</el-popover>
</div>
<div class="layout-navbars-breadcrumb-user-icon mr10" @click="onScreenClick">
<i class="iconfont" :title="isScreenFull ? '关全屏' : '开全屏'" :class="!isScreenFull ? 'icon-fullscreen' : 'icon-tuichuquanping'"></i>
</div>
<el-dropdown :show-timeout="70" :hide-timeout="50" trigger="click" @command="onHandleCommandClick">
<span class="layout-navbars-breadcrumb-user-link">
<img :src="getPhotoUrl" class="layout-navbars-breadcrumb-user-link-photo mr5" />
{{ getUserInfos.userName === '' ? 'test' : getUserInfos.userName }}
<el-icon class="el-icon--right">
<elementArrowDown />
</el-icon>
</span>
<template #dropdown>
<el-dropdown-menu>
<el-dropdown-item command="password">修改密码</el-dropdown-item>
<el-dropdown-item command="logOut">退出登录</el-dropdown-item>
</el-dropdown-menu>
</template>
</el-dropdown>
<Search ref="searchRef" />
<UpdatePassword ref="passwordRef" />
</div>
</template>
<script lang="ts">
import { ref, getCurrentInstance, computed, reactive, toRefs, onMounted, unref } from 'vue';
import { useRouter } from 'vue-router';
import { ElMessageBox, ElMessage } from 'element-plus';
import screenfull from 'screenfull';
import { useStore } from '@/store';
import { Local } from '@/utils/storage';
import MessageBox from '../MessageBox/index.vue';
import Search from '../Search/index.vue';
import UpdatePassword from '../UpdatePassword/index.vue';
export default {
name: 'UserInfo',
components: { MessageBox, Search, UpdatePassword },
setup() {
const { proxy } = getCurrentInstance() as any;
const router = useRouter();
const store = useStore();
const searchRef = ref();
const passwordRef = ref();
const state = reactive({
isScreenFull: false,
isShowUserNewsPopover: false,
disabledSize: '',
});
// 获取用户信息 vuex
const getUserInfos = computed(() => {
return store.state.user.userInfo;
});
const getPhotoUrl = computed(()=>{
const userInfo = unref(getUserInfos) as any;
const rootUrl = import.meta.env.VITE_API_URL;
return `${rootUrl}sys/User/getUserPhoto/${userInfo.userId}`;
});
// 获取布局配置信息
const getThemeConfig = computed(() => {
return store.state.themeConfig.themeConfig;
});
// 设置分割样式
const layoutUserFlexNum = computed(() => {
let { layout, isClassicSplitMenu } = getThemeConfig.value;
let num;
if (layout === 'defaults' || (layout === 'classic' && !isClassicSplitMenu) || layout === 'columns') num = 1;
else num = null;
return num;
});
// 全屏点击时
const onScreenClick = () => {
if (!screenfull.isEnabled) {
ElMessage.warning('暂不不支持全屏');
return false;
}
screenfull.toggle();
screenfull.on('change', () => {
state.isScreenFull = screenfull.isFullscreen;
});
};
// 布局配置点击时
const onSettingClick = () => {
proxy.mittBus.emit('openSettings');
};
// 下拉菜单点击时
const onHandleCommandClick = (path: string) => {
if (path === 'logOut') {
ElMessageBox({
closeOnClickModal: false,
closeOnPressEscape: false,
title: '提示',
message: '此操作将退出登录, 是否继续?',
showCancelButton: true,
confirmButtonText: '确定',
cancelButtonText: '取消',
beforeClose: (action, instance, done) => {
if (action === 'confirm') {
instance.confirmButtonLoading = true;
instance.confirmButtonText = '退出中';
store.dispatch('user/logout').then(async () => {
await store.dispatch('tagsView/delAllViews');
done();
instance.confirmButtonLoading = false;
});
} else {
done();
}
},
}).then(() => {
router.push(`/login?redirect=${router.currentRoute.value.fullPath}`);
location.reload();
setTimeout(() => {
ElMessage.success('安全退出成功!');
}, 300);
})
.catch(() => {});
} else if (path === 'password') {
passwordRef.value.open({})
} else {
router.push(path);
}
};
// 菜单搜索点击
const onSearchClick = () => {
searchRef.value.openSearch();
};
// 组件大小改变
const onComponentSizeChange = (size) => {
Local.remove('themeConfig');
getThemeConfig.value.globalComponentSize = size;
Local.set('themeConfig', getThemeConfig.value);
initComponentSize();
window.location.reload();
};
// 初始化全局组件大小
const initComponentSize = () => {
switch (Local.get('themeConfig').globalComponentSize) {
case 'default':
state.disabledSize = 'default';
break;
case 'large':
state.disabledSize = 'large';
break;
case 'small':
state.disabledSize = 'small';
break;
}
};
// 页面加载时
onMounted(() => {
if (Local.get('themeConfig')) {
initComponentSize();
}
});
return {
getUserInfos,
onSettingClick,
onHandleCommandClick,
onScreenClick,
onSearchClick,
onComponentSizeChange,
searchRef,
passwordRef,
getPhotoUrl,
layoutUserFlexNum,
...toRefs(state),
};
},
};
</script>
<style scoped lang="scss">
.layout-navbars-breadcrumb-user {
display: flex;
align-items: center;
justify-content: flex-end;
&-link {
height: 100%;
display: flex;
align-items: center;
white-space: nowrap;
cursor: pointer;
&-photo {
width: 25px;
height: 25px;
border-radius: 100%;
}
}
&-icon {
padding: 0 10px;
cursor: pointer;
color: var(--bg-topBarColor);
height: 50px;
line-height: 50px;
display: flex;
align-items: center;
&:hover {
background: rgba(0, 0, 0, 0.04);
i {
display: inline-block;
animation: logoAnimation 0.3s ease-in-out;
}
}
}
::v-deep(.el-dropdown) {
color: var(--bg-topBarColor);
}
::v-deep(.el-badge) {
height: 40px;
line-height: 40px;
display: flex;
align-items: center;
}
::v-deep(.el-badge__content.is-fixed) {
top: 12px;
}
}
</style>
<template>
<el-header class="layout-navbar" v-show="!isTagsViewCurrentFull">
<div class="layout-navbar-container">
<logo v-if="computeShow.logo" />
<Breadcrumb />
<menubar :menus="computeMenus" v-if="computeShow.menu" mode="horizontal" @menu-select="handleMenuSelect" />
<user-info />
</div>
<TagsView v-if="computeShow.tagsView" />
</el-header>
</template>
<script lang="ts">
import {computed} from 'vue';
import { useStore } from '@/store';
import Logo from '../Logo/index.vue';
import Breadcrumb from './Breadcrumb/index.vue';
import UserInfo from './UserInfo/index.vue';
import TagsView from '../TagsView/index.vue';
import Menubar from '../Menubar/index.vue';
import { filterShowMenus } from '../../hooks'
import {resolvePath} from "@/utils/pathOperation";
export default {
name: 'NavBar',
components: { UserInfo, Logo, Breadcrumb, Menubar, TagsView },
setup() {
const store = useStore();
// 是否显示
const computeShow = computed(() => {
let { layout, isTagsview, isClassicSplitMenu, isShowLogo } = store.state.themeConfig.themeConfig;
return {
logo: (isShowLogo && layout === 'classic') || (isShowLogo && layout === 'transverse'),
tagsView: layout !== 'classic' && isTagsview,
menu: layout === 'transverse' || (isClassicSplitMenu && layout === 'classic')
}
});
const computeMenus = computed(() => {
if (!computeShow.value.menu) {
return []
}
const { layout, isClassicSplitMenu } = store.state.themeConfig.themeConfig;
const arr = filterShowMenus(store.state.permission.routes);
if (layout === 'classic' && isClassicSplitMenu) {
return store.state.app.navRoutes;
} else {
return arr;
}
});
// 获取卡片全屏信息
const isTagsViewCurrentFull = computed(() => {
return store.state.tagsView.isCurrentViewFull;
});
const getChildren = (path: string): any[] => {
const currentPathSplit = path.split('/');
let children: any[] = [];
store.state.permission.routes.map((v: any, k: number) => {
if (v.path === `/${currentPathSplit[1] || ''}` || v.redirect === `/${currentPathSplit[1]}`) {
v['k'] = k;
children = [{ ...v }];
if (v.children && v.children.length) {
children = v.children.map((c:any) => {
const obj = {...c};
if (!obj.mate || (!obj.mate.isLink && !obj.mate.isIframe)) {
obj.path = resolvePath(v.path, obj.path);
}
return obj;
});
}
}
});
return children;
};
const handleMenuSelect = (path: string) => {
const { layout, isClassicSplitMenu } = store.state.themeConfig.themeConfig;
if (layout === 'classic' && isClassicSplitMenu) {
store.dispatch('app/setMenuRoutes', getChildren(path));
}
};
return {
isTagsViewCurrentFull,
computeShow,
computeMenus,
handleMenuSelect
};
},
};
</script>
<style scoped lang="scss">
.layout-navbar {
display: flex;
flex-direction: column;
width: 100%;
padding: 0;
height: var(--height-navBar);
&-container {
height: var(--height-navBar);
display: flex;
align-items: center;
padding-right: 15px;
background: var(--bg-topBar);
border-bottom: 1px solid #f1f2f3;
}
}
.has-tags-view:not(.layout-modules--classic) {
.layout-navbar {
height: calc(var(--height-navBar) + var(--height-tagsView));
}
}
</style>
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
import { store } from '@/store/index.ts';
import {computed} from "vue";
import {RouteRecordRaw} from "vue-router";
/**
* 菜单收起|折叠
*/
export function changeMenuCollapse():void {
return store.dispatch('app/changeCollapse')
}
/**
* 过滤出显示的路由
*/
export function filterShowMenus(arr: Array<RouteRecordRaw>): Array<RouteRecordRaw> {
return arr
.filter(item => !item.meta || !item.meta.isHide)
.map(item => {
item = Object.assign({}, item);
if (item.children) {
item.children = filterShowMenus(item.children);
}
return item;
});
}
export function useAppState() {
return {
computeThemeConfig: computed(() => {
return store.state.themeConfig.themeConfig;
}),
computeApp: computed(() => {
return store.state.app;
}),
}
}
This diff is collapsed.
<template>
<el-container class="layout-container flex-center">
<nav-bar />
<el-container class="layout-mian-height-50">
<Aside />
<div class="flex-center layout-backtop">
<tags-view v-if="getThemeConfig.isTagsview" />
<app-main />
</div>
</el-container>
<el-backtop target=".layout-backtop .el-main .el-scrollbar__wrap" />
</el-container>
</template>
<script lang="ts">
import { computed } from 'vue';
import { useStore } from '@/store';
import Aside from '../component/Aside/index.vue';
import NavBar from '../component/Navbar/index.vue';
import AppMain from '../component/AppMain/index.vue';
import TagsView from '../component/TagsView/index.vue';
export default {
name: 'Classic',
components: { Aside, NavBar, AppMain, TagsView },
setup() {
const store = useStore();
// 获取布局配置信息
const getThemeConfig = computed(() => {
return store.state.themeConfig.themeConfig;
});
return {
getThemeConfig,
};
},
};
</script>
This diff is collapsed.
<template>
<el-container class="layout-container flex-center layout-backtop">
<nav-bar />
<app-main />
<el-backtop target=".layout-backtop .el-main .el-scrollbar__wrap" />
</el-container>
</template>
<script lang="ts">
import NavBar from '../component/Navbar/index.vue';
import AppMain from '../component/AppMain/index.vue';
export default {
name: 'LayoutTransverse',
components: { NavBar, AppMain },
};
</script>
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
import type { App } from 'vue';
import 'element-plus/dist/index.css';
import zhCn from 'element-plus/lib/locale/lang/zh-cn';
import ElementPlus from 'element-plus';
export default (app: App) => {
app.use(ElementPlus, {locale: zhCn, zIndex: 1000})
}
This diff is collapsed.
This source diff could not be displayed because it is too large. You can view the blob instead.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
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