feat:政务服务
This commit is contained in:
parent
5de65851f7
commit
93c928a191
6
sub-government-affairs-service/.env.development
Normal file
6
sub-government-affairs-service/.env.development
Normal file
@ -0,0 +1,6 @@
|
||||
# 开发环境
|
||||
VITE_PORT = 9528
|
||||
VITE_MODE = 'DEV'
|
||||
VITE_APP_NAME = 'sub-government-affairs-service'
|
||||
VITE_APP_BASE_API = '/apis'
|
||||
VITE_APP_BASE_URL = 'http://localhost:8080/'
|
5
sub-government-affairs-service/.env.production
Normal file
5
sub-government-affairs-service/.env.production
Normal file
@ -0,0 +1,5 @@
|
||||
# 生产环境
|
||||
VITE_MODE = 'PRO'
|
||||
VITE_APP_NAME = 'sub-government-affairs-service'
|
||||
VITE_APP_BASE_API = 'https://www.localhost.com/8080/api/'
|
||||
VITE_APP_BASE_URL = 'https://www.localhost.com/8080/'
|
13
sub-government-affairs-service/index.html
Normal file
13
sub-government-affairs-service/index.html
Normal file
@ -0,0 +1,13 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<link rel="icon" type="image/svg+xml" href="/logo.png" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>政务服务</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="app"></div>
|
||||
<script type="module" src="/src/main.js"></script>
|
||||
</body>
|
||||
</html>
|
76
sub-government-affairs-service/package.json
Normal file
76
sub-government-affairs-service/package.json
Normal file
@ -0,0 +1,76 @@
|
||||
{
|
||||
"name": "government-affairs-service",
|
||||
"private": true,
|
||||
"version": "0.0.1",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "vite --mode development",
|
||||
"build": "vite build --mode production",
|
||||
"build:dev": "vite build --mode dev",
|
||||
"build:qa": "vite build --mode qa",
|
||||
"preview": "vite preview",
|
||||
"format": "prettier --write 'src/**/*.{vue,ts,tsx,js,jsx,css,less,scss,json,md}'",
|
||||
"eslint": "npx eslint --init",
|
||||
"lint": "npm run lint:script && npm run lint:style",
|
||||
"lint:style": "stylelint 'src/**/*.{vue,scss,css,sass,less}' --fix",
|
||||
"lint:script": "eslint --ext .js,.ts,.tsx,.vue --fix --quiet ./src"
|
||||
},
|
||||
"dependencies": {
|
||||
"@element-plus/icons-vue": "^2.3.1",
|
||||
"@smallwei/avue": "^3.6.2",
|
||||
"@wangeditor/editor": "^5.1.23",
|
||||
"@wangeditor/editor-for-vue": "^5.1.12",
|
||||
"axios": "^1.6.5",
|
||||
"echarts": "^5.5.0",
|
||||
"element-plus": "^2.7.2",
|
||||
"js-base64": "^3.7.6",
|
||||
"lodash": "^4.17.21",
|
||||
"moment": "^2.30.1",
|
||||
"nprogress": "^0.2.0",
|
||||
"path-browserify": "^1.0.1",
|
||||
"pinia": "^2.1.7",
|
||||
"pinia-plugin-persistedstate": "^3.2.1",
|
||||
"screenfull": "^6.0.2",
|
||||
"vue": "^3.3.11",
|
||||
"vue-router": "^4.2.5"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/core": "^7.23.7",
|
||||
"@babel/eslint-parser": "^7.23.3",
|
||||
"@types/path-browserify": "^1.0.2",
|
||||
"@vitejs/plugin-vue": "^4.5.2",
|
||||
"autoprefixer": "^10.4.17",
|
||||
"eslint": "^8.56.0",
|
||||
"eslint-config-prettier": "^9.1.0",
|
||||
"eslint-plugin-prettier": "^5.1.3",
|
||||
"eslint-plugin-vue": "^9.20.1",
|
||||
"mockjs": "^1.1.0",
|
||||
"postcss": "^8.4.33",
|
||||
"postcss-html": "^1.6.0",
|
||||
"postcss-import": "^16.0.0",
|
||||
"prettier": "^3.2.4",
|
||||
"sass": "^1.70.0",
|
||||
"stylelint": "^16.2.0",
|
||||
"stylelint-config-html": "^1.1.0",
|
||||
"stylelint-config-prettier": "^9.0.5",
|
||||
"stylelint-config-rational-order": "^0.1.2",
|
||||
"stylelint-config-recommended": "^14.0.0",
|
||||
"stylelint-config-recommended-scss": "^14.0.0",
|
||||
"stylelint-config-recommended-vue": "^1.5.0",
|
||||
"stylelint-config-standard": "^36.0.0",
|
||||
"stylelint-config-standard-scss": "^13.0.0",
|
||||
"stylelint-order": "^6.0.4",
|
||||
"stylelint-scss": "^6.1.0",
|
||||
"terser": "^5.27.0",
|
||||
"unplugin-auto-import": "^0.17.3",
|
||||
"unplugin-vue-components": "^0.26.0",
|
||||
"vite": "^5.0.8",
|
||||
"vite-plugin-compression": "^0.5.1",
|
||||
"vite-plugin-eslint": "^1.8.1",
|
||||
"vite-plugin-mock": "^3.0.1",
|
||||
"vite-plugin-progress": "^0.0.7",
|
||||
"vite-plugin-qiankun": "^1.0.15",
|
||||
"vite-plugin-svg-icons": "^2.0.1",
|
||||
"vite-plugin-vue-setup-extend": "^0.4.0"
|
||||
}
|
||||
}
|
BIN
sub-government-affairs-service/public/images/avatar.gif
Normal file
BIN
sub-government-affairs-service/public/images/avatar.gif
Normal file
Binary file not shown.
After Width: | Height: | Size: 6.2 KiB |
BIN
sub-government-affairs-service/public/logo.png
Normal file
BIN
sub-government-affairs-service/public/logo.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 12 KiB |
27
sub-government-affairs-service/src/App.vue
Normal file
27
sub-government-affairs-service/src/App.vue
Normal file
@ -0,0 +1,27 @@
|
||||
<!--
|
||||
* @Description:
|
||||
* @Author: zenghua.wang
|
||||
* @Date: 2024-01-24 18:54:01
|
||||
* @LastEditors: zenghua.wang
|
||||
* @LastEditTime: 2024-01-26 22:57:34
|
||||
-->
|
||||
<template>
|
||||
<el-config-provider :size="size" :locale="zhCn">
|
||||
<router-view />
|
||||
</el-config-provider>
|
||||
</template>
|
||||
|
||||
<script setup name="app">
|
||||
import { computed } from 'vue';
|
||||
import { useSettingStore } from '@/store/modules/setting';
|
||||
// 配置element中文
|
||||
import zhCn from 'element-plus/es/locale/lang/zh-cn';
|
||||
|
||||
const SettingStore = useSettingStore();
|
||||
// 配置全局组件大小
|
||||
const size = computed(() => SettingStore.themeConfig.globalComSize);
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
@import './styles/style.scss';
|
||||
</style>
|
43
sub-government-affairs-service/src/apis/login.js
Normal file
43
sub-government-affairs-service/src/apis/login.js
Normal file
@ -0,0 +1,43 @@
|
||||
import request from '@/utils/axios';
|
||||
|
||||
/**
|
||||
* @Title: 登录
|
||||
*/
|
||||
export function Login(params, token) {
|
||||
return request('/store/login', {
|
||||
method: 'POST',
|
||||
data: null,
|
||||
params: params,
|
||||
headers: {
|
||||
'fairies-auth-token': token,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @Title: 登出
|
||||
*/
|
||||
export function LogOut() {
|
||||
return request('/store/logout', {
|
||||
method: 'POST',
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @Title: 验证码
|
||||
*/
|
||||
export function GetCaptcha() {
|
||||
return request('/store/login-captcha', {
|
||||
method: 'GET',
|
||||
responseType: 'arraybuffer',
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @Title: 获取菜单
|
||||
*/
|
||||
export function GetMenus() {
|
||||
return request('/store/self/menu', {
|
||||
method: 'GET',
|
||||
});
|
||||
}
|
53
sub-government-affairs-service/src/apis/user.js
Normal file
53
sub-government-affairs-service/src/apis/user.js
Normal file
@ -0,0 +1,53 @@
|
||||
import request from '@/utils/axios';
|
||||
|
||||
/**
|
||||
* @Title: 列表
|
||||
*/
|
||||
export function GetEntityList(data, params) {
|
||||
return request('/store/user/query-user', {
|
||||
method: 'POST',
|
||||
data: data,
|
||||
params: params,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @Title: 新增
|
||||
*/
|
||||
export function AddEntity(data, params = {}) {
|
||||
return request('/store/user/add-user', {
|
||||
method: 'POST',
|
||||
data: data,
|
||||
params: params,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @Title: 修改
|
||||
*/
|
||||
export function UpdateEntity(data, params = {}) {
|
||||
return request('/store/user/update-user', {
|
||||
method: 'PUT',
|
||||
data: data,
|
||||
params: params,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @Title: 删除
|
||||
*/
|
||||
export function DeleteEntity(data, params) {
|
||||
return request('/store/user/del-user', {
|
||||
method: 'DELETE',
|
||||
data: data,
|
||||
params: params,
|
||||
});
|
||||
}
|
||||
|
||||
export function UploadAvatar(data, params) {
|
||||
return request('/store/self/generate-avatar-pic-upload-url', {
|
||||
method: 'POST',
|
||||
data: data,
|
||||
params: params,
|
||||
});
|
||||
}
|
539
sub-government-affairs-service/src/assets/fonts/demo.css
Normal file
539
sub-government-affairs-service/src/assets/fonts/demo.css
Normal file
@ -0,0 +1,539 @@
|
||||
/* Logo 字体 */
|
||||
@font-face {
|
||||
font-family: "iconfont logo";
|
||||
src: url('https://at.alicdn.com/t/font_985780_km7mi63cihi.eot?t=1545807318834');
|
||||
src: url('https://at.alicdn.com/t/font_985780_km7mi63cihi.eot?t=1545807318834#iefix') format('embedded-opentype'),
|
||||
url('https://at.alicdn.com/t/font_985780_km7mi63cihi.woff?t=1545807318834') format('woff'),
|
||||
url('https://at.alicdn.com/t/font_985780_km7mi63cihi.ttf?t=1545807318834') format('truetype'),
|
||||
url('https://at.alicdn.com/t/font_985780_km7mi63cihi.svg?t=1545807318834#iconfont') format('svg');
|
||||
}
|
||||
|
||||
.logo {
|
||||
font-family: "iconfont logo";
|
||||
font-size: 160px;
|
||||
font-style: normal;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
}
|
||||
|
||||
/* tabs */
|
||||
.nav-tabs {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.nav-tabs .nav-more {
|
||||
position: absolute;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
height: 42px;
|
||||
line-height: 42px;
|
||||
color: #666;
|
||||
}
|
||||
|
||||
#tabs {
|
||||
border-bottom: 1px solid #eee;
|
||||
}
|
||||
|
||||
#tabs li {
|
||||
cursor: pointer;
|
||||
width: 100px;
|
||||
height: 40px;
|
||||
line-height: 40px;
|
||||
text-align: center;
|
||||
font-size: 16px;
|
||||
border-bottom: 2px solid transparent;
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
margin-bottom: -1px;
|
||||
color: #666;
|
||||
}
|
||||
|
||||
|
||||
#tabs .active {
|
||||
border-bottom-color: #f00;
|
||||
color: #222;
|
||||
}
|
||||
|
||||
.tab-container .content {
|
||||
display: none;
|
||||
}
|
||||
|
||||
/* 页面布局 */
|
||||
.main {
|
||||
padding: 30px 100px;
|
||||
width: 960px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
.main .logo {
|
||||
color: #333;
|
||||
text-align: left;
|
||||
margin-bottom: 30px;
|
||||
line-height: 1;
|
||||
height: 110px;
|
||||
margin-top: -50px;
|
||||
overflow: hidden;
|
||||
*zoom: 1;
|
||||
}
|
||||
|
||||
.main .logo a {
|
||||
font-size: 160px;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.helps {
|
||||
margin-top: 40px;
|
||||
}
|
||||
|
||||
.helps pre {
|
||||
padding: 20px;
|
||||
margin: 10px 0;
|
||||
border: solid 1px #e7e1cd;
|
||||
background-color: #fffdef;
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
.icon_lists {
|
||||
width: 100% !important;
|
||||
overflow: hidden;
|
||||
*zoom: 1;
|
||||
}
|
||||
|
||||
.icon_lists li {
|
||||
width: 100px;
|
||||
margin-bottom: 10px;
|
||||
margin-right: 20px;
|
||||
text-align: center;
|
||||
list-style: none !important;
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
.icon_lists li .code-name {
|
||||
line-height: 1.2;
|
||||
}
|
||||
|
||||
.icon_lists .icon {
|
||||
display: block;
|
||||
height: 100px;
|
||||
line-height: 100px;
|
||||
font-size: 42px;
|
||||
margin: 10px auto;
|
||||
color: #333;
|
||||
-webkit-transition: font-size 0.25s linear, width 0.25s linear;
|
||||
-moz-transition: font-size 0.25s linear, width 0.25s linear;
|
||||
transition: font-size 0.25s linear, width 0.25s linear;
|
||||
}
|
||||
|
||||
.icon_lists .icon:hover {
|
||||
font-size: 100px;
|
||||
}
|
||||
|
||||
.icon_lists .svg-icon {
|
||||
/* 通过设置 font-size 来改变图标大小 */
|
||||
width: 1em;
|
||||
/* 图标和文字相邻时,垂直对齐 */
|
||||
vertical-align: -0.15em;
|
||||
/* 通过设置 color 来改变 SVG 的颜色/fill */
|
||||
fill: currentColor;
|
||||
/* path 和 stroke 溢出 viewBox 部分在 IE 下会显示
|
||||
normalize.css 中也包含这行 */
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.icon_lists li .name,
|
||||
.icon_lists li .code-name {
|
||||
color: #666;
|
||||
}
|
||||
|
||||
/* markdown 样式 */
|
||||
.markdown {
|
||||
color: #666;
|
||||
font-size: 14px;
|
||||
line-height: 1.8;
|
||||
}
|
||||
|
||||
.highlight {
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
.markdown img {
|
||||
vertical-align: middle;
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
.markdown h1 {
|
||||
color: #404040;
|
||||
font-weight: 500;
|
||||
line-height: 40px;
|
||||
margin-bottom: 24px;
|
||||
}
|
||||
|
||||
.markdown h2,
|
||||
.markdown h3,
|
||||
.markdown h4,
|
||||
.markdown h5,
|
||||
.markdown h6 {
|
||||
color: #404040;
|
||||
margin: 1.6em 0 0.6em 0;
|
||||
font-weight: 500;
|
||||
clear: both;
|
||||
}
|
||||
|
||||
.markdown h1 {
|
||||
font-size: 28px;
|
||||
}
|
||||
|
||||
.markdown h2 {
|
||||
font-size: 22px;
|
||||
}
|
||||
|
||||
.markdown h3 {
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.markdown h4 {
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.markdown h5 {
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.markdown h6 {
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.markdown hr {
|
||||
height: 1px;
|
||||
border: 0;
|
||||
background: #e9e9e9;
|
||||
margin: 16px 0;
|
||||
clear: both;
|
||||
}
|
||||
|
||||
.markdown p {
|
||||
margin: 1em 0;
|
||||
}
|
||||
|
||||
.markdown>p,
|
||||
.markdown>blockquote,
|
||||
.markdown>.highlight,
|
||||
.markdown>ol,
|
||||
.markdown>ul {
|
||||
width: 80%;
|
||||
}
|
||||
|
||||
.markdown ul>li {
|
||||
list-style: circle;
|
||||
}
|
||||
|
||||
.markdown>ul li,
|
||||
.markdown blockquote ul>li {
|
||||
margin-left: 20px;
|
||||
padding-left: 4px;
|
||||
}
|
||||
|
||||
.markdown>ul li p,
|
||||
.markdown>ol li p {
|
||||
margin: 0.6em 0;
|
||||
}
|
||||
|
||||
.markdown ol>li {
|
||||
list-style: decimal;
|
||||
}
|
||||
|
||||
.markdown>ol li,
|
||||
.markdown blockquote ol>li {
|
||||
margin-left: 20px;
|
||||
padding-left: 4px;
|
||||
}
|
||||
|
||||
.markdown code {
|
||||
margin: 0 3px;
|
||||
padding: 0 5px;
|
||||
background: #eee;
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
||||
.markdown strong,
|
||||
.markdown b {
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.markdown>table {
|
||||
border-collapse: collapse;
|
||||
border-spacing: 0px;
|
||||
empty-cells: show;
|
||||
border: 1px solid #e9e9e9;
|
||||
width: 95%;
|
||||
margin-bottom: 24px;
|
||||
}
|
||||
|
||||
.markdown>table th {
|
||||
white-space: nowrap;
|
||||
color: #333;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.markdown>table th,
|
||||
.markdown>table td {
|
||||
border: 1px solid #e9e9e9;
|
||||
padding: 8px 16px;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.markdown>table th {
|
||||
background: #F7F7F7;
|
||||
}
|
||||
|
||||
.markdown blockquote {
|
||||
font-size: 90%;
|
||||
color: #999;
|
||||
border-left: 4px solid #e9e9e9;
|
||||
padding-left: 0.8em;
|
||||
margin: 1em 0;
|
||||
}
|
||||
|
||||
.markdown blockquote p {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.markdown .anchor {
|
||||
opacity: 0;
|
||||
transition: opacity 0.3s ease;
|
||||
margin-left: 8px;
|
||||
}
|
||||
|
||||
.markdown .waiting {
|
||||
color: #ccc;
|
||||
}
|
||||
|
||||
.markdown h1:hover .anchor,
|
||||
.markdown h2:hover .anchor,
|
||||
.markdown h3:hover .anchor,
|
||||
.markdown h4:hover .anchor,
|
||||
.markdown h5:hover .anchor,
|
||||
.markdown h6:hover .anchor {
|
||||
opacity: 1;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.markdown>br,
|
||||
.markdown>p>br {
|
||||
clear: both;
|
||||
}
|
||||
|
||||
|
||||
.hljs {
|
||||
display: block;
|
||||
background: white;
|
||||
padding: 0.5em;
|
||||
color: #333333;
|
||||
overflow-x: auto;
|
||||
}
|
||||
|
||||
.hljs-comment,
|
||||
.hljs-meta {
|
||||
color: #969896;
|
||||
}
|
||||
|
||||
.hljs-string,
|
||||
.hljs-variable,
|
||||
.hljs-template-variable,
|
||||
.hljs-strong,
|
||||
.hljs-emphasis,
|
||||
.hljs-quote {
|
||||
color: #df5000;
|
||||
}
|
||||
|
||||
.hljs-keyword,
|
||||
.hljs-selector-tag,
|
||||
.hljs-type {
|
||||
color: #a71d5d;
|
||||
}
|
||||
|
||||
.hljs-literal,
|
||||
.hljs-symbol,
|
||||
.hljs-bullet,
|
||||
.hljs-attribute {
|
||||
color: #0086b3;
|
||||
}
|
||||
|
||||
.hljs-section,
|
||||
.hljs-name {
|
||||
color: #63a35c;
|
||||
}
|
||||
|
||||
.hljs-tag {
|
||||
color: #333333;
|
||||
}
|
||||
|
||||
.hljs-title,
|
||||
.hljs-attr,
|
||||
.hljs-selector-id,
|
||||
.hljs-selector-class,
|
||||
.hljs-selector-attr,
|
||||
.hljs-selector-pseudo {
|
||||
color: #795da3;
|
||||
}
|
||||
|
||||
.hljs-addition {
|
||||
color: #55a532;
|
||||
background-color: #eaffea;
|
||||
}
|
||||
|
||||
.hljs-deletion {
|
||||
color: #bd2c00;
|
||||
background-color: #ffecec;
|
||||
}
|
||||
|
||||
.hljs-link {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
/* 代码高亮 */
|
||||
/* PrismJS 1.15.0
|
||||
https://prismjs.com/download.html#themes=prism&languages=markup+css+clike+javascript */
|
||||
/**
|
||||
* prism.js default theme for JavaScript, CSS and HTML
|
||||
* Based on dabblet (http://dabblet.com)
|
||||
* @author Lea Verou
|
||||
*/
|
||||
code[class*="language-"],
|
||||
pre[class*="language-"] {
|
||||
color: black;
|
||||
background: none;
|
||||
text-shadow: 0 1px white;
|
||||
font-family: Consolas, Monaco, 'Andale Mono', 'Ubuntu Mono', monospace;
|
||||
text-align: left;
|
||||
white-space: pre;
|
||||
word-spacing: normal;
|
||||
word-break: normal;
|
||||
word-wrap: normal;
|
||||
line-height: 1.5;
|
||||
|
||||
-moz-tab-size: 4;
|
||||
-o-tab-size: 4;
|
||||
tab-size: 4;
|
||||
|
||||
-webkit-hyphens: none;
|
||||
-moz-hyphens: none;
|
||||
-ms-hyphens: none;
|
||||
hyphens: none;
|
||||
}
|
||||
|
||||
pre[class*="language-"]::-moz-selection,
|
||||
pre[class*="language-"] ::-moz-selection,
|
||||
code[class*="language-"]::-moz-selection,
|
||||
code[class*="language-"] ::-moz-selection {
|
||||
text-shadow: none;
|
||||
background: #b3d4fc;
|
||||
}
|
||||
|
||||
pre[class*="language-"]::selection,
|
||||
pre[class*="language-"] ::selection,
|
||||
code[class*="language-"]::selection,
|
||||
code[class*="language-"] ::selection {
|
||||
text-shadow: none;
|
||||
background: #b3d4fc;
|
||||
}
|
||||
|
||||
@media print {
|
||||
|
||||
code[class*="language-"],
|
||||
pre[class*="language-"] {
|
||||
text-shadow: none;
|
||||
}
|
||||
}
|
||||
|
||||
/* Code blocks */
|
||||
pre[class*="language-"] {
|
||||
padding: 1em;
|
||||
margin: .5em 0;
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
:not(pre)>code[class*="language-"],
|
||||
pre[class*="language-"] {
|
||||
background: #f5f2f0;
|
||||
}
|
||||
|
||||
/* Inline code */
|
||||
:not(pre)>code[class*="language-"] {
|
||||
padding: .1em;
|
||||
border-radius: .3em;
|
||||
white-space: normal;
|
||||
}
|
||||
|
||||
.token.comment,
|
||||
.token.prolog,
|
||||
.token.doctype,
|
||||
.token.cdata {
|
||||
color: slategray;
|
||||
}
|
||||
|
||||
.token.punctuation {
|
||||
color: #999;
|
||||
}
|
||||
|
||||
.namespace {
|
||||
opacity: .7;
|
||||
}
|
||||
|
||||
.token.property,
|
||||
.token.tag,
|
||||
.token.boolean,
|
||||
.token.number,
|
||||
.token.constant,
|
||||
.token.symbol,
|
||||
.token.deleted {
|
||||
color: #905;
|
||||
}
|
||||
|
||||
.token.selector,
|
||||
.token.attr-name,
|
||||
.token.string,
|
||||
.token.char,
|
||||
.token.builtin,
|
||||
.token.inserted {
|
||||
color: #690;
|
||||
}
|
||||
|
||||
.token.operator,
|
||||
.token.entity,
|
||||
.token.url,
|
||||
.language-css .token.string,
|
||||
.style .token.string {
|
||||
color: #9a6e3a;
|
||||
background: hsla(0, 0%, 100%, .5);
|
||||
}
|
||||
|
||||
.token.atrule,
|
||||
.token.attr-value,
|
||||
.token.keyword {
|
||||
color: #07a;
|
||||
}
|
||||
|
||||
.token.function,
|
||||
.token.class-name {
|
||||
color: #DD4A68;
|
||||
}
|
||||
|
||||
.token.regex,
|
||||
.token.important,
|
||||
.token.variable {
|
||||
color: #e90;
|
||||
}
|
||||
|
||||
.token.important,
|
||||
.token.bold {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.token.italic {
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
.token.entity {
|
||||
cursor: help;
|
||||
}
|
1223
sub-government-affairs-service/src/assets/fonts/demo_index.html
Normal file
1223
sub-government-affairs-service/src/assets/fonts/demo_index.html
Normal file
File diff suppressed because it is too large
Load Diff
195
sub-government-affairs-service/src/assets/fonts/iconfont.css
Normal file
195
sub-government-affairs-service/src/assets/fonts/iconfont.css
Normal file
@ -0,0 +1,195 @@
|
||||
@font-face {
|
||||
font-family: "iconfont"; /* Project id 4425172 */
|
||||
src: url('iconfont.woff2?t=1725879404188') format('woff2'),
|
||||
url('iconfont.woff?t=1725879404188') format('woff'),
|
||||
url('iconfont.ttf?t=1725879404188') format('truetype');
|
||||
}
|
||||
|
||||
.iconfont {
|
||||
font-family: "iconfont" !important;
|
||||
font-size: 16px;
|
||||
font-style: normal;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
}
|
||||
|
||||
.icon-data4:before {
|
||||
content: "\e60c";
|
||||
}
|
||||
|
||||
.icon-data5:before {
|
||||
content: "\e6be";
|
||||
}
|
||||
|
||||
.icon-recharge-record:before {
|
||||
content: "\e614";
|
||||
}
|
||||
|
||||
.icon-recharge-rule:before {
|
||||
content: "\e628";
|
||||
}
|
||||
|
||||
.icon-user-profile:before {
|
||||
content: "\e783";
|
||||
}
|
||||
|
||||
.icon-achieve:before {
|
||||
content: "\e616";
|
||||
}
|
||||
|
||||
.icon-activity-level:before {
|
||||
content: "\e61a";
|
||||
}
|
||||
|
||||
.icon-skins:before {
|
||||
content: "\e790";
|
||||
}
|
||||
|
||||
.icon-data1:before {
|
||||
content: "\e996";
|
||||
}
|
||||
|
||||
.icon-data2:before {
|
||||
content: "\e661";
|
||||
}
|
||||
|
||||
.icon-data3:before {
|
||||
content: "\e632";
|
||||
}
|
||||
|
||||
.icon-data:before {
|
||||
content: "\e64e";
|
||||
}
|
||||
|
||||
.icon-game:before {
|
||||
content: "\e6d0";
|
||||
}
|
||||
|
||||
.icon-banner:before {
|
||||
content: "\e613";
|
||||
}
|
||||
|
||||
.icon-verification:before {
|
||||
content: "\e601";
|
||||
}
|
||||
|
||||
.icon-balance:before {
|
||||
content: "\e6b9";
|
||||
}
|
||||
|
||||
.icon-refund:before {
|
||||
content: "\e7af";
|
||||
}
|
||||
|
||||
.icon-wechat:before {
|
||||
content: "\e681";
|
||||
}
|
||||
|
||||
.icon-alipay:before {
|
||||
content: "\e61e";
|
||||
}
|
||||
|
||||
.icon-user:before {
|
||||
content: "\e67f";
|
||||
}
|
||||
|
||||
.icon-coupon:before {
|
||||
content: "\e65a";
|
||||
}
|
||||
|
||||
.icon-level:before {
|
||||
content: "\e7d8";
|
||||
}
|
||||
|
||||
.icon-activity:before {
|
||||
content: "\e67b";
|
||||
}
|
||||
|
||||
.icon-shop:before {
|
||||
content: "\e60a";
|
||||
}
|
||||
|
||||
.icon-member:before {
|
||||
content: "\e640";
|
||||
}
|
||||
|
||||
.icon-recharge:before {
|
||||
content: "\e799";
|
||||
}
|
||||
|
||||
.icon-marketing:before {
|
||||
content: "\e765";
|
||||
}
|
||||
|
||||
.icon-goods-sku:before {
|
||||
content: "\e6d7";
|
||||
}
|
||||
|
||||
.icon-store:before {
|
||||
content: "\e62b";
|
||||
}
|
||||
|
||||
.icon-goods-store:before {
|
||||
content: "\e6c6";
|
||||
}
|
||||
|
||||
.icon-storer:before {
|
||||
content: "\e64a";
|
||||
}
|
||||
|
||||
.icon-order:before {
|
||||
content: "\e737";
|
||||
}
|
||||
|
||||
.icon-permission:before {
|
||||
content: "\e612";
|
||||
}
|
||||
|
||||
.icon-goods:before {
|
||||
content: "\e889";
|
||||
}
|
||||
|
||||
.icon-menu:before {
|
||||
content: "\e60e";
|
||||
}
|
||||
|
||||
.icon-dict-type:before {
|
||||
content: "\e652";
|
||||
}
|
||||
|
||||
.icon-dictionary:before {
|
||||
content: "\e600";
|
||||
}
|
||||
|
||||
.icon-role:before {
|
||||
content: "\e604";
|
||||
}
|
||||
|
||||
.icon-fullscreen:before {
|
||||
content: "\e8fa";
|
||||
}
|
||||
|
||||
.icon-exit-fullscreen:before {
|
||||
content: "\e8fb";
|
||||
}
|
||||
|
||||
.icon-table:before {
|
||||
content: "\e615";
|
||||
}
|
||||
|
||||
.icon-test:before {
|
||||
content: "\e610";
|
||||
}
|
||||
|
||||
.icon-lang:before {
|
||||
content: "\e649";
|
||||
}
|
||||
|
||||
.icon-demo:before {
|
||||
content: "\e6ee";
|
||||
}
|
||||
|
||||
.icon-size:before {
|
||||
content: "\e660";
|
||||
}
|
||||
|
File diff suppressed because one or more lines are too long
324
sub-government-affairs-service/src/assets/fonts/iconfont.json
Normal file
324
sub-government-affairs-service/src/assets/fonts/iconfont.json
Normal file
@ -0,0 +1,324 @@
|
||||
{
|
||||
"id": "4425172",
|
||||
"name": "sub-vue",
|
||||
"font_family": "iconfont",
|
||||
"css_prefix_text": "icon-",
|
||||
"description": "react/vue项目后台管理平台",
|
||||
"glyphs": [
|
||||
{
|
||||
"icon_id": "1218184",
|
||||
"name": "销售明细",
|
||||
"font_class": "data4",
|
||||
"unicode": "e60c",
|
||||
"unicode_decimal": 58892
|
||||
},
|
||||
{
|
||||
"icon_id": "2230090",
|
||||
"name": "销售明细",
|
||||
"font_class": "data5",
|
||||
"unicode": "e6be",
|
||||
"unicode_decimal": 59070
|
||||
},
|
||||
{
|
||||
"icon_id": "6882983",
|
||||
"name": "充值记录",
|
||||
"font_class": "recharge-record",
|
||||
"unicode": "e614",
|
||||
"unicode_decimal": 58900
|
||||
},
|
||||
{
|
||||
"icon_id": "34611004",
|
||||
"name": "充值规则",
|
||||
"font_class": "recharge-rule",
|
||||
"unicode": "e628",
|
||||
"unicode_decimal": 58920
|
||||
},
|
||||
{
|
||||
"icon_id": "15562252",
|
||||
"name": "用户画像",
|
||||
"font_class": "user-profile",
|
||||
"unicode": "e783",
|
||||
"unicode_decimal": 59267
|
||||
},
|
||||
{
|
||||
"icon_id": "18747445",
|
||||
"name": "成就",
|
||||
"font_class": "achieve",
|
||||
"unicode": "e616",
|
||||
"unicode_decimal": 58902
|
||||
},
|
||||
{
|
||||
"icon_id": "33848542",
|
||||
"name": "我的-段位",
|
||||
"font_class": "activity-level",
|
||||
"unicode": "e61a",
|
||||
"unicode_decimal": 58906
|
||||
},
|
||||
{
|
||||
"icon_id": "20406821",
|
||||
"name": "皮肤",
|
||||
"font_class": "skins",
|
||||
"unicode": "e790",
|
||||
"unicode_decimal": 59280
|
||||
},
|
||||
{
|
||||
"icon_id": "2214847",
|
||||
"name": "个人中心-积分商城",
|
||||
"font_class": "data1",
|
||||
"unicode": "e996",
|
||||
"unicode_decimal": 59798
|
||||
},
|
||||
{
|
||||
"icon_id": "14233304",
|
||||
"name": "价值投资",
|
||||
"font_class": "data2",
|
||||
"unicode": "e661",
|
||||
"unicode_decimal": 58977
|
||||
},
|
||||
{
|
||||
"icon_id": "23059951",
|
||||
"name": "费用统计",
|
||||
"font_class": "data3",
|
||||
"unicode": "e632",
|
||||
"unicode_decimal": 58930
|
||||
},
|
||||
{
|
||||
"icon_id": "2199049",
|
||||
"name": "数据报表",
|
||||
"font_class": "data",
|
||||
"unicode": "e64e",
|
||||
"unicode_decimal": 58958
|
||||
},
|
||||
{
|
||||
"icon_id": "36257316",
|
||||
"name": "游戏管理",
|
||||
"font_class": "game",
|
||||
"unicode": "e6d0",
|
||||
"unicode_decimal": 59088
|
||||
},
|
||||
{
|
||||
"icon_id": "11913396",
|
||||
"name": "banner",
|
||||
"font_class": "banner",
|
||||
"unicode": "e613",
|
||||
"unicode_decimal": 58899
|
||||
},
|
||||
{
|
||||
"icon_id": "35264323",
|
||||
"name": "核销码核销",
|
||||
"font_class": "verification",
|
||||
"unicode": "e601",
|
||||
"unicode_decimal": 58881
|
||||
},
|
||||
{
|
||||
"icon_id": "6514128",
|
||||
"name": "结算管理",
|
||||
"font_class": "balance",
|
||||
"unicode": "e6b9",
|
||||
"unicode_decimal": 59065
|
||||
},
|
||||
{
|
||||
"icon_id": "12025983",
|
||||
"name": "退货退款",
|
||||
"font_class": "refund",
|
||||
"unicode": "e7af",
|
||||
"unicode_decimal": 59311
|
||||
},
|
||||
{
|
||||
"icon_id": "1207908",
|
||||
"name": "wechat",
|
||||
"font_class": "wechat",
|
||||
"unicode": "e681",
|
||||
"unicode_decimal": 59009
|
||||
},
|
||||
{
|
||||
"icon_id": "27188513",
|
||||
"name": "alipay",
|
||||
"font_class": "alipay",
|
||||
"unicode": "e61e",
|
||||
"unicode_decimal": 58910
|
||||
},
|
||||
{
|
||||
"icon_id": "11111017",
|
||||
"name": "会员",
|
||||
"font_class": "user",
|
||||
"unicode": "e67f",
|
||||
"unicode_decimal": 59007
|
||||
},
|
||||
{
|
||||
"icon_id": "630079",
|
||||
"name": "我的优惠券",
|
||||
"font_class": "coupon",
|
||||
"unicode": "e65a",
|
||||
"unicode_decimal": 58970
|
||||
},
|
||||
{
|
||||
"icon_id": "2046370",
|
||||
"name": "会员等级",
|
||||
"font_class": "level",
|
||||
"unicode": "e7d8",
|
||||
"unicode_decimal": 59352
|
||||
},
|
||||
{
|
||||
"icon_id": "2569868",
|
||||
"name": "活动",
|
||||
"font_class": "activity",
|
||||
"unicode": "e67b",
|
||||
"unicode_decimal": 59003
|
||||
},
|
||||
{
|
||||
"icon_id": "2681698",
|
||||
"name": "门店",
|
||||
"font_class": "shop",
|
||||
"unicode": "e60a",
|
||||
"unicode_decimal": 58890
|
||||
},
|
||||
{
|
||||
"icon_id": "2811147",
|
||||
"name": "会员",
|
||||
"font_class": "member",
|
||||
"unicode": "e640",
|
||||
"unicode_decimal": 58944
|
||||
},
|
||||
{
|
||||
"icon_id": "4560182",
|
||||
"name": "会员充值",
|
||||
"font_class": "recharge",
|
||||
"unicode": "e799",
|
||||
"unicode_decimal": 59289
|
||||
},
|
||||
{
|
||||
"icon_id": "5880283",
|
||||
"name": "营销",
|
||||
"font_class": "marketing",
|
||||
"unicode": "e765",
|
||||
"unicode_decimal": 59237
|
||||
},
|
||||
{
|
||||
"icon_id": "6982618",
|
||||
"name": "商品规格",
|
||||
"font_class": "goods-sku",
|
||||
"unicode": "e6d7",
|
||||
"unicode_decimal": 59095
|
||||
},
|
||||
{
|
||||
"icon_id": "7307041",
|
||||
"name": "商家入驻",
|
||||
"font_class": "store",
|
||||
"unicode": "e62b",
|
||||
"unicode_decimal": 58923
|
||||
},
|
||||
{
|
||||
"icon_id": "11639867",
|
||||
"name": "小店商品库",
|
||||
"font_class": "goods-store",
|
||||
"unicode": "e6c6",
|
||||
"unicode_decimal": 59078
|
||||
},
|
||||
{
|
||||
"icon_id": "13872198",
|
||||
"name": "商家列表",
|
||||
"font_class": "storer",
|
||||
"unicode": "e64a",
|
||||
"unicode_decimal": 58954
|
||||
},
|
||||
{
|
||||
"icon_id": "577335",
|
||||
"name": "订单",
|
||||
"font_class": "order",
|
||||
"unicode": "e737",
|
||||
"unicode_decimal": 59191
|
||||
},
|
||||
{
|
||||
"icon_id": "736503",
|
||||
"name": "权限",
|
||||
"font_class": "permission",
|
||||
"unicode": "e612",
|
||||
"unicode_decimal": 58898
|
||||
},
|
||||
{
|
||||
"icon_id": "1727271",
|
||||
"name": "06商品-线性",
|
||||
"font_class": "goods",
|
||||
"unicode": "e889",
|
||||
"unicode_decimal": 59529
|
||||
},
|
||||
{
|
||||
"icon_id": "7587933",
|
||||
"name": "菜单",
|
||||
"font_class": "menu",
|
||||
"unicode": "e60e",
|
||||
"unicode_decimal": 58894
|
||||
},
|
||||
{
|
||||
"icon_id": "12758820",
|
||||
"name": "dictionary",
|
||||
"font_class": "dict-type",
|
||||
"unicode": "e652",
|
||||
"unicode_decimal": 58962
|
||||
},
|
||||
{
|
||||
"icon_id": "13768112",
|
||||
"name": "dictionary",
|
||||
"font_class": "dictionary",
|
||||
"unicode": "e600",
|
||||
"unicode_decimal": 58880
|
||||
},
|
||||
{
|
||||
"icon_id": "37734141",
|
||||
"name": "new-role",
|
||||
"font_class": "role",
|
||||
"unicode": "e604",
|
||||
"unicode_decimal": 58884
|
||||
},
|
||||
{
|
||||
"icon_id": "1727563",
|
||||
"name": "327全屏",
|
||||
"font_class": "fullscreen",
|
||||
"unicode": "e8fa",
|
||||
"unicode_decimal": 59642
|
||||
},
|
||||
{
|
||||
"icon_id": "1727566",
|
||||
"name": "328退出全屏",
|
||||
"font_class": "exit-fullscreen",
|
||||
"unicode": "e8fb",
|
||||
"unicode_decimal": 59643
|
||||
},
|
||||
{
|
||||
"icon_id": "11641852",
|
||||
"name": "表格",
|
||||
"font_class": "table",
|
||||
"unicode": "e615",
|
||||
"unicode_decimal": 58901
|
||||
},
|
||||
{
|
||||
"icon_id": "20104468",
|
||||
"name": "测试",
|
||||
"font_class": "test",
|
||||
"unicode": "e610",
|
||||
"unicode_decimal": 58896
|
||||
},
|
||||
{
|
||||
"icon_id": "26686335",
|
||||
"name": "中英文",
|
||||
"font_class": "lang",
|
||||
"unicode": "e649",
|
||||
"unicode_decimal": 58953
|
||||
},
|
||||
{
|
||||
"icon_id": "30012547",
|
||||
"name": "方案列表-默认",
|
||||
"font_class": "demo",
|
||||
"unicode": "e6ee",
|
||||
"unicode_decimal": 59118
|
||||
},
|
||||
{
|
||||
"icon_id": "37702310",
|
||||
"name": "文字大小",
|
||||
"font_class": "size",
|
||||
"unicode": "e660",
|
||||
"unicode_decimal": 58976
|
||||
}
|
||||
]
|
||||
}
|
BIN
sub-government-affairs-service/src/assets/fonts/iconfont.ttf
Normal file
BIN
sub-government-affairs-service/src/assets/fonts/iconfont.ttf
Normal file
Binary file not shown.
BIN
sub-government-affairs-service/src/assets/fonts/iconfont.woff
Normal file
BIN
sub-government-affairs-service/src/assets/fonts/iconfont.woff
Normal file
Binary file not shown.
BIN
sub-government-affairs-service/src/assets/fonts/iconfont.woff2
Normal file
BIN
sub-government-affairs-service/src/assets/fonts/iconfont.woff2
Normal file
Binary file not shown.
10
sub-government-affairs-service/src/config/index.js
Normal file
10
sub-government-affairs-service/src/config/index.js
Normal file
@ -0,0 +1,10 @@
|
||||
const { VITE_APP_NAME } = import.meta.env;
|
||||
|
||||
export const GenKey = (key, prefix = `${VITE_APP_NAME}_`) => {
|
||||
return prefix ? prefix + key : key;
|
||||
};
|
||||
|
||||
export const CONSTANTS = {
|
||||
PREFIX: `${VITE_APP_NAME}_`,
|
||||
PRIMARY: '#409eff',
|
||||
};
|
32
sub-government-affairs-service/src/directives/auth.js
Normal file
32
sub-government-affairs-service/src/directives/auth.js
Normal file
@ -0,0 +1,32 @@
|
||||
/**
|
||||
* @Description: 按钮权限
|
||||
* @Author: zenghua.wang
|
||||
* @Date: 2022-08-30 09:42:47
|
||||
* @LastEditors: zenghua.wang
|
||||
* @LastEditTime: 2024-01-30 13:59:50
|
||||
*/
|
||||
|
||||
// import { CACHE_KEY, useCache } from '@/hooks/web/useCache';
|
||||
|
||||
export function useAuth(app) {
|
||||
app.directive('auth', (el, binding) => {
|
||||
// const { wsCache } = useCache();
|
||||
const { value } = binding;
|
||||
const all_permission = '*:*:*';
|
||||
const permissions = []; //wsCache.get(CACHE_KEY.USER).permissions;
|
||||
|
||||
if (value && value instanceof Array && value.length > 0) {
|
||||
const permissionFlag = value;
|
||||
|
||||
const hasAuth = permissions.some((permission) => {
|
||||
return all_permission === permission || permissionFlag.includes(permission);
|
||||
});
|
||||
|
||||
if (!hasAuth) {
|
||||
el.parentNode && el.parentNode.removeChild(el);
|
||||
}
|
||||
} else {
|
||||
// throw new Error('no auth to access it.');
|
||||
}
|
||||
});
|
||||
}
|
9
sub-government-affairs-service/src/directives/index.js
Normal file
9
sub-government-affairs-service/src/directives/index.js
Normal file
@ -0,0 +1,9 @@
|
||||
import { useAuth } from './auth';
|
||||
|
||||
/**
|
||||
* 指令:v-xxx
|
||||
* @methods auth 按钮权限,用法: v-auth
|
||||
*/
|
||||
export const registerDirective = (app) => {
|
||||
useAuth(app);
|
||||
};
|
5
sub-government-affairs-service/src/hooks/index.js
Normal file
5
sub-government-affairs-service/src/hooks/index.js
Normal file
@ -0,0 +1,5 @@
|
||||
import { getCurrentInstance } from 'vue';
|
||||
|
||||
export const useApp = () => {
|
||||
return getCurrentInstance().appContext?.config?.globalProperties;
|
||||
};
|
@ -0,0 +1,22 @@
|
||||
import { h } from 'vue';
|
||||
|
||||
const wrapperMap = new Map();
|
||||
|
||||
export const useWrapComponents = (Component, route) => {
|
||||
let wrapper;
|
||||
if (Component) {
|
||||
const wrapperName = route.name;
|
||||
if (wrapperMap.has(wrapperName)) {
|
||||
wrapper = wrapperMap.get(wrapperName);
|
||||
} else {
|
||||
wrapper = {
|
||||
name: wrapperName,
|
||||
render() {
|
||||
return h('div', { className: 'layout-main-inner' }, Component);
|
||||
},
|
||||
};
|
||||
wrapperMap.set(wrapperName, wrapper);
|
||||
}
|
||||
return h(wrapper);
|
||||
}
|
||||
};
|
4
sub-government-affairs-service/src/layouts/Views.vue
Normal file
4
sub-government-affairs-service/src/layouts/Views.vue
Normal file
@ -0,0 +1,4 @@
|
||||
<template>
|
||||
<router-view />
|
||||
</template>
|
||||
<script setup name="layout-views"></script>
|
@ -0,0 +1,100 @@
|
||||
<!--
|
||||
* @Description:
|
||||
* @Author: zenghua.wang
|
||||
* @Date: 2023-06-20 14:29:45
|
||||
* @LastEditors: zenghua.wang “1048523306@qq.com”
|
||||
* @LastEditTime: 2025-01-17 10:33:54
|
||||
-->
|
||||
<template>
|
||||
<el-dropdown class="layout-avatar">
|
||||
<span class="el-dropdown-link">
|
||||
<el-avatar :size="30" class="avatar" :src="userInfo.avatar || '/images/avatar.gif'" />
|
||||
<span class="layout-avatar-name">{{ userInfo.name || '游客' }}</span>
|
||||
<el-icon class="el-icon--right">
|
||||
<arrow-down />
|
||||
</el-icon>
|
||||
</span>
|
||||
<template #dropdown>
|
||||
<el-dropdown-menu>
|
||||
<!-- <el-dropdown-item :command="0"> 当前角色:管理员 </el-dropdown-item> -->
|
||||
<el-dropdown-item :command="3" divided @click="showUserInfo">
|
||||
<el-icon><Edit /></el-icon>个人信息
|
||||
</el-dropdown-item>
|
||||
<el-dropdown-item :command="4" divided @click="showPassword">
|
||||
<el-icon><Edit /></el-icon>修改密码
|
||||
</el-dropdown-item>
|
||||
<el-dropdown-item :command="5" divided @click="logOut">
|
||||
<el-icon><SwitchButton /></el-icon>退出登录
|
||||
</el-dropdown-item>
|
||||
</el-dropdown-menu>
|
||||
</template>
|
||||
</el-dropdown>
|
||||
|
||||
<!-- <user-info ref="userInfoRef" /> -->
|
||||
<!-- <edit-password ref="passwordRef" /> -->
|
||||
</template>
|
||||
|
||||
<script setup name="layout-avatar">
|
||||
import { computed, ref } from 'vue';
|
||||
import { useRouter } from 'vue-router';
|
||||
import { ElMessage, ElMessageBox } from 'element-plus';
|
||||
import { useUserStore } from '@/store/modules/user';
|
||||
import { useTagsViewStore } from '@/store/modules/tagsView';
|
||||
import { usePermissionStore } from '@/store/modules/permission';
|
||||
// import UserInfo from '@/views/system/user/info.vue';
|
||||
// import EditPassword from '@/views/system/user/password.vue';
|
||||
import { LogOut } from '@/apis/login';
|
||||
|
||||
const router = useRouter();
|
||||
const UserStore = useUserStore();
|
||||
const TagsViewStore = useTagsViewStore();
|
||||
const PermissionStore = usePermissionStore();
|
||||
|
||||
// 用户信息
|
||||
const userInfo = computed(() => UserStore.getUserInfo());
|
||||
const userInfoRef = ref(null);
|
||||
const passwordRef = ref(null);
|
||||
|
||||
const showUserInfo = () => {
|
||||
userInfoRef?.value && userInfoRef.value.show();
|
||||
};
|
||||
|
||||
const showPassword = () => {
|
||||
passwordRef?.value && passwordRef.value.show();
|
||||
};
|
||||
|
||||
const logOut = async () => {
|
||||
ElMessageBox.confirm('您是否确认退出登录?', '温馨提示', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning',
|
||||
}).then(async () => {
|
||||
LogOut().then((res) => {
|
||||
if (res.code === 200) {
|
||||
UserStore.logout();
|
||||
TagsViewStore.clearVisitedView();
|
||||
PermissionStore.clearRoutes();
|
||||
router.push({ path: '/login' });
|
||||
ElMessage({
|
||||
type: 'success',
|
||||
message: '退出登录成功!',
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.layout-avatar {
|
||||
&-name {
|
||||
margin-left: 8px;
|
||||
@include ellipsis();
|
||||
}
|
||||
.el-dropdown-link {
|
||||
@include flex-row();
|
||||
align-items: center;
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
</style>
|
@ -0,0 +1,38 @@
|
||||
<!--
|
||||
* @Description:
|
||||
* @Author: zenghua.wang
|
||||
* @Date: 2023-06-20 14:29:45
|
||||
* @LastEditors: zenghua.wang
|
||||
* @LastEditTime: 2024-01-26 23:04:14
|
||||
-->
|
||||
<template>
|
||||
<el-breadcrumb class="layout-breadcrumb" separator="/">
|
||||
<transition-group name="breadcrumb">
|
||||
<el-breadcrumb-item v-if="matched[0].meta.title !== '首页'" key="home" :to="{ path: '/' }">
|
||||
<div class="layout-breadcrumb-item">
|
||||
<span class="layout-breadcrumb-title">首页</span>
|
||||
</div>
|
||||
</el-breadcrumb-item>
|
||||
<el-breadcrumb-item v-for="(item, index) in matched" :key="item.name">
|
||||
<span v-if="item.redirect === 'noRedirect' || index == matched.length - 1" class="no-redirect">{{ item.meta.title }}</span>
|
||||
<a v-else @click.prevent="handleLink(item)">{{ item.meta.title }}</a>
|
||||
</el-breadcrumb-item>
|
||||
</transition-group>
|
||||
</el-breadcrumb>
|
||||
</template>
|
||||
|
||||
<script setup name="layout-breadcrumb">
|
||||
import { computed } from 'vue';
|
||||
import { useRoute, useRouter } from 'vue-router';
|
||||
|
||||
const route = useRoute();
|
||||
const router = useRouter();
|
||||
|
||||
const handleLink = (item) => {
|
||||
router.push({
|
||||
path: item.path,
|
||||
});
|
||||
};
|
||||
|
||||
const matched = computed(() => route.matched.filter((item) => item.meta && item.meta.title && item.meta.breadcrumb !== false));
|
||||
</script>
|
@ -0,0 +1,42 @@
|
||||
<!--
|
||||
* @Description:
|
||||
* @Author: zenghua.wang
|
||||
* @Date: 2023-06-20 14:29:45
|
||||
* @LastEditors: zenghua.wang
|
||||
* @LastEditTime: 2024-01-26 23:04:29
|
||||
-->
|
||||
<template>
|
||||
<div class="layout-hamburger" @click="handleCollapse">
|
||||
<el-icon v-if="isCollapse" class="icon"><expand /></el-icon>
|
||||
<el-icon v-else class="icon"><fold /></el-icon>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup name="layout-hamburger">
|
||||
import { computed } from 'vue';
|
||||
import { useSettingStore } from '@/store/modules/setting';
|
||||
|
||||
const SettingStore = useSettingStore();
|
||||
const isCollapse = computed(() => !SettingStore.isCollapse);
|
||||
|
||||
const handleCollapse = () => {
|
||||
SettingStore.setCollapse(isCollapse.value);
|
||||
};
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
.layout-hamburger {
|
||||
padding: 0px 15px;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
&:hover {
|
||||
background: rgba(0, 0, 0, 0.025);
|
||||
}
|
||||
|
||||
.icon {
|
||||
font-size: 24px;
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
</style>
|
@ -0,0 +1,114 @@
|
||||
<!--
|
||||
* @Description:
|
||||
* @Author: zenghua.wang
|
||||
* @Date: 2023-06-20 14:29:45
|
||||
* @LastEditors: zenghua.wang
|
||||
* @LastEditTime: 2024-02-19 21:11:20
|
||||
-->
|
||||
<template>
|
||||
<div class="layout-header-placeholder" :style="stylePlaceholder()"></div>
|
||||
<div
|
||||
class="layout-header"
|
||||
:class="{
|
||||
'fixed-header': themeConfig.fixedHeader,
|
||||
collapse: themeConfig.fixedHeader && isCollapse,
|
||||
'no-collapse': themeConfig.fixedHeader && !isCollapse,
|
||||
}"
|
||||
>
|
||||
<div class="layout-header-inner">
|
||||
<div class="layout-header-left">
|
||||
<Hamburger />
|
||||
<Breadcrumb />
|
||||
</div>
|
||||
<div class="layout-header-right">
|
||||
<Size class="layout-header-tool" />
|
||||
<ScreenFull class="layout-header-tool" />
|
||||
<Avatar class="layout-header-tool" />
|
||||
</div>
|
||||
</div>
|
||||
<TagsView v-if="themeConfig.showTag" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup name="layout-header">
|
||||
import { computed } from 'vue';
|
||||
import { useSettingStore } from '@/store/modules/setting';
|
||||
import { setPx } from '@/utils';
|
||||
import Hamburger from '../Hamburger';
|
||||
import Breadcrumb from '../Breadcrumb';
|
||||
import Size from '../Size';
|
||||
import ScreenFull from '../ScreenFull';
|
||||
import Avatar from '../Avatar';
|
||||
import TagsView from '../TagsView';
|
||||
|
||||
const SettingStore = useSettingStore();
|
||||
|
||||
// 主题配置
|
||||
const themeConfig = computed(() => SettingStore.themeConfig);
|
||||
const isCollapse = computed(() => !SettingStore.isCollapse);
|
||||
|
||||
const stylePlaceholder = () => {
|
||||
return { height: themeConfig.value.showTag ? setPx(90) : setPx(50) };
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.layout-header {
|
||||
width: 100%;
|
||||
background: white;
|
||||
transition: width 0.28s;
|
||||
flex-shrink: 0;
|
||||
box-sizing: border-box;
|
||||
box-shadow: 0 1px 4px rgb(0 21 41 / 8%);
|
||||
|
||||
&.fixed-header {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
right: 0;
|
||||
z-index: 9;
|
||||
}
|
||||
&.collapse {
|
||||
width: calc(100% - 60px);
|
||||
}
|
||||
&.no-collapse {
|
||||
width: calc(100% - 210px);
|
||||
}
|
||||
|
||||
&-placeholder {
|
||||
height: 50px;
|
||||
}
|
||||
|
||||
&-inner {
|
||||
@include flex-row();
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
height: 50px;
|
||||
width: 100%;
|
||||
padding: 0 10px 0 0;
|
||||
border-bottom: 1px solid #eee;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
&-left {
|
||||
@include flex-row();
|
||||
align-items: center;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
&-right {
|
||||
@include flex-row();
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
&-tool {
|
||||
padding: 4px;
|
||||
margin-right: 10px;
|
||||
cursor: pointer;
|
||||
|
||||
:deep(.el-icon) {
|
||||
font-size: 20px;
|
||||
color: #333;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
@ -0,0 +1,45 @@
|
||||
<!--
|
||||
* @Description:
|
||||
* @Author: zenghua.wang
|
||||
* @Date: 2023-06-20 14:29:45
|
||||
* @LastEditors: zenghua.wang “1048523306@qq.com”
|
||||
* @LastEditTime: 2025-01-17 11:55:43
|
||||
-->
|
||||
<template>
|
||||
<div class="logo">
|
||||
<!-- <img src="/images/logo.png" class="logo-picture" /> -->
|
||||
<h2 v-show="!isCollapse" class="logo-title">管理系统</h2>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup name="logo">
|
||||
defineProps({
|
||||
isCollapse: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.logo {
|
||||
overflow: hidden;
|
||||
@include flex-row();
|
||||
align-items: center;
|
||||
padding: 7px 5px;
|
||||
margin-bottom: 2px;
|
||||
box-shadow: 0 1px 4px rgba(0, 21, 41, 0.08);
|
||||
transition: all 0.28s;
|
||||
justify-content: center;
|
||||
|
||||
&-picture {
|
||||
width: 70px;
|
||||
height: 35px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
&-title {
|
||||
color: $color-primary;
|
||||
}
|
||||
}
|
||||
</style>
|
@ -0,0 +1,48 @@
|
||||
<!--
|
||||
* @Description:
|
||||
* @Author: zenghua.wang
|
||||
* @Date: 2023-06-20 14:29:45
|
||||
* @LastEditors: zenghua.wang
|
||||
* @LastEditTime: 2024-02-05 15:47:56
|
||||
-->
|
||||
<template>
|
||||
<div class="layout-main">
|
||||
<router-view v-slot="{ Component, route }">
|
||||
<transition name="fade-slide" mode="out-in" appear>
|
||||
<keep-alive v-if="isReload" :include="cacheRoutes">
|
||||
<component :is="useWrapComponents(Component, route)" :key="route.path" />
|
||||
</keep-alive>
|
||||
</transition>
|
||||
</router-view>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup name="layout-main">
|
||||
import { computed } from 'vue';
|
||||
import { useSettingStore } from '@/store/modules/setting';
|
||||
import { usePermissionStore } from '@/store/modules/permission';
|
||||
import { useWrapComponents } from '@/hooks/useWrapComponents';
|
||||
|
||||
const SettingStore = useSettingStore();
|
||||
const PermissionStore = usePermissionStore();
|
||||
const cacheRoutes = computed(() => PermissionStore.keepAliveRoutes);
|
||||
const isReload = computed(() => SettingStore.isReload);
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.layout-main {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
@include scrollable();
|
||||
box-sizing: border-box;
|
||||
|
||||
&-inner {
|
||||
width: 100%;
|
||||
padding: 10px;
|
||||
box-sizing: border-box;
|
||||
background-color: #f5f5f5;
|
||||
}
|
||||
}
|
||||
</style>
|
@ -0,0 +1,23 @@
|
||||
<!--
|
||||
* @Description:
|
||||
* @Author: zenghua.wang
|
||||
* @Date: 2024-01-27 16:02:43
|
||||
* @LastEditors: zenghua.wang
|
||||
* @LastEditTime: 2024-04-12 21:12:01
|
||||
-->
|
||||
<template>
|
||||
<el-icon v-if="icon.includes('icon')" :class="`iconfont ${icon}`" :size="size" />
|
||||
<el-icon v-else :size="size"> <component :is="icon" /></el-icon>
|
||||
</template>
|
||||
<script setup name="layout-icon">
|
||||
defineProps({
|
||||
icon: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
size: {
|
||||
type: Number,
|
||||
default: 20,
|
||||
},
|
||||
});
|
||||
</script>
|
@ -0,0 +1,44 @@
|
||||
<!--
|
||||
* @Description:
|
||||
* @Author: zenghua.wang
|
||||
* @Date: 2023-06-20 14:29:45
|
||||
* @LastEditors: zenghua.wang
|
||||
* @LastEditTime: 2024-01-27 09:29:36
|
||||
-->
|
||||
<template>
|
||||
<component :is="type" v-bind="linkProps(to)">
|
||||
<slot />
|
||||
</component>
|
||||
</template>
|
||||
|
||||
<script setup name="layout-link">
|
||||
import { computed } from 'vue';
|
||||
import { isExternal } from '@/utils/validate';
|
||||
|
||||
const props = defineProps({
|
||||
to: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
});
|
||||
|
||||
const type = computed(() => {
|
||||
if (isExternal(props.to)) {
|
||||
return 'a';
|
||||
}
|
||||
return 'router-link';
|
||||
});
|
||||
|
||||
const linkProps = (to) => {
|
||||
if (isExternal(to)) {
|
||||
return {
|
||||
href: to,
|
||||
target: '_blank',
|
||||
rel: 'noopener',
|
||||
};
|
||||
}
|
||||
return {
|
||||
to: to,
|
||||
};
|
||||
};
|
||||
</script>
|
@ -0,0 +1,81 @@
|
||||
<!--
|
||||
* @Description:
|
||||
* @Author: zenghua.wang
|
||||
* @Date: 2023-06-20 14:29:45
|
||||
* @LastEditors: zenghua.wang
|
||||
* @LastEditTime: 2024-01-27 16:07:37
|
||||
-->
|
||||
<template>
|
||||
<template v-if="!item.hidden">
|
||||
<template v-if="!item.alwaysShow && hasOneShowingChild(item.children, item)">
|
||||
<layout-link v-if="onlyOneChild.meta" :to="onlyOneChild.path">
|
||||
<el-menu-item :index="onlyOneChild.path">
|
||||
<layout-icon :size="20" :icon="onlyOneChild?.meta?.icon" />
|
||||
<template #title>{{ onlyOneChild.meta && onlyOneChild.meta?.title }}</template>
|
||||
</el-menu-item>
|
||||
</layout-link>
|
||||
</template>
|
||||
<el-sub-menu v-else :index="item.path" teleported>
|
||||
<template #title>
|
||||
<layout-icon :size="20" :icon="item?.meta?.icon" />
|
||||
<span>{{ item.meta && item.meta?.title }}</span>
|
||||
</template>
|
||||
<sub-item v-for="child in item.children" :key="child.path" :item="child" />
|
||||
</el-sub-menu>
|
||||
</template>
|
||||
</template>
|
||||
|
||||
<script setup name="sub-item">
|
||||
import { ref } from 'vue';
|
||||
// import { isExternal } from '@/utils/validate.js';
|
||||
import LayoutLink from './Link';
|
||||
import LayoutIcon from './Icon';
|
||||
// import path from 'path-browserify';
|
||||
|
||||
defineProps({
|
||||
item: {
|
||||
type: Object,
|
||||
required: true,
|
||||
},
|
||||
basePath: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
});
|
||||
|
||||
const onlyOneChild = ref(null);
|
||||
const hasOneShowingChild = (children = [], parent) => {
|
||||
const showingChildren = children.filter((item) => {
|
||||
// 过滤掉需要隐藏的菜单
|
||||
if (item.hidden) {
|
||||
return false;
|
||||
} else {
|
||||
// 临时设置(如果只有一个显示子项,则将使用)
|
||||
onlyOneChild.value = item;
|
||||
return true;
|
||||
}
|
||||
});
|
||||
|
||||
// 当只有一个子路由器时,默认情况下会显示该子路由器
|
||||
if (showingChildren.length === 1) {
|
||||
return true;
|
||||
}
|
||||
// 如果没有要显示的子路由器,则显示父路由器
|
||||
if (showingChildren.length === 0) {
|
||||
onlyOneChild.value = { ...parent, noShowingChildren: true };
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
};
|
||||
|
||||
// const resolvePath = (routePath) => {
|
||||
// if (isExternal(routePath)) {
|
||||
// return routePath;
|
||||
// }
|
||||
// if (isExternal(props.basePath)) {
|
||||
// return props.basePath;
|
||||
// }
|
||||
// return path.resolve(props.basePath, routePath);
|
||||
// };
|
||||
</script>
|
@ -0,0 +1,44 @@
|
||||
<!--
|
||||
* @Description:
|
||||
* @Author: zenghua.wang
|
||||
* @Date: 2023-06-20 14:29:45
|
||||
* @LastEditors: zenghua.wang
|
||||
* @LastEditTime: 2024-01-27 15:54:43
|
||||
-->
|
||||
<template>
|
||||
<div class="layout-screenful">
|
||||
<el-tooltip effect="dark" :content="isFullscreen ? '取消全屏' : '全屏'" placement="bottom">
|
||||
<span @click="handleFullScreen">
|
||||
<el-icon :class="['iconfont', isFullscreen ? 'icon-exit-fullscreen' : 'icon-fullscreen']"></el-icon>
|
||||
</span>
|
||||
</el-tooltip>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup name="layout-screenful">
|
||||
import { onMounted, onUnmounted, ref } from 'vue';
|
||||
import screenfull from 'screenfull';
|
||||
import { ElMessage } from 'element-plus';
|
||||
|
||||
const isFullscreen = ref(false);
|
||||
|
||||
const handleChange = () => {
|
||||
isFullscreen.value = screenfull.isFullscreen;
|
||||
};
|
||||
|
||||
const handleFullScreen = () => {
|
||||
if (!screenfull.isEnabled) {
|
||||
ElMessage.warning('you browser can not work');
|
||||
return false;
|
||||
}
|
||||
screenfull.toggle();
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
screenfull.isEnabled && screenfull.on('change', handleChange);
|
||||
});
|
||||
|
||||
onUnmounted(() => {
|
||||
screenfull.isEnabled && screenfull.off('change', handleChange);
|
||||
});
|
||||
</script>
|
@ -0,0 +1,91 @@
|
||||
<!--
|
||||
* @Description:
|
||||
* @Author: zenghua.wang
|
||||
* @Date: 2024-01-27 20:01:45
|
||||
* @LastEditors: zenghua.wang
|
||||
* @LastEditTime: 2024-03-30 14:32:07
|
||||
-->
|
||||
<template>
|
||||
<div class="layout-sider" :class="{ 'has-logo': themeConfig.showLogo }">
|
||||
<Logo v-if="themeConfig.showLogo" :is-collapse="isCollapse" />
|
||||
<el-scrollbar wrap-class="layout-sider-scrollbar">
|
||||
<el-menu
|
||||
class="layout-sider-menu"
|
||||
background-color="#fff"
|
||||
text-color="#333"
|
||||
active-text-color=""
|
||||
:default-active="activeMenu"
|
||||
:unique-opened="SettingStore.themeConfig.uniqueOpened"
|
||||
:collapse-transition="false"
|
||||
:collapse="isCollapse"
|
||||
>
|
||||
<SubItem v-for="item in permissionRoutes" :key="item.path" :item="item" />
|
||||
</el-menu>
|
||||
</el-scrollbar>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup name="layout-sider">
|
||||
import { computed } from 'vue';
|
||||
import { useRoute } from 'vue-router';
|
||||
import Logo from '../Logo';
|
||||
import SubItem from '../Menu/SubItem';
|
||||
import { useSettingStore } from '@/store/modules/setting';
|
||||
import { usePermissionStore } from '@/store/modules/permission';
|
||||
|
||||
const route = useRoute();
|
||||
const PermissionStore = usePermissionStore();
|
||||
const SettingStore = useSettingStore();
|
||||
|
||||
// 是否折叠
|
||||
const isCollapse = computed(() => !SettingStore.isCollapse);
|
||||
|
||||
// 设置
|
||||
const themeConfig = computed(() => SettingStore.themeConfig);
|
||||
|
||||
// 获取路由
|
||||
const permissionRoutes = computed(() => PermissionStore.permissionRoutes);
|
||||
|
||||
const activeMenu = computed(() => {
|
||||
const { meta, path } = route;
|
||||
if (meta.activeMenu) {
|
||||
return meta.activeMenu;
|
||||
}
|
||||
return path;
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.layout-sider {
|
||||
overflow: hidden;
|
||||
position: fixed;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
z-index: 98;
|
||||
width: 210px;
|
||||
height: 100%;
|
||||
background-color: #fff;
|
||||
box-shadow: 0 1px 4px rgba(0, 21, 41, 0.08);
|
||||
transition: width 0.28s;
|
||||
|
||||
&-scrollbar {
|
||||
height: calc(100vh - 60px) !important;
|
||||
}
|
||||
:deep(.el-scrollbar__wrap) {
|
||||
height: calc(100vh - 60px) !important;
|
||||
}
|
||||
|
||||
&-menu {
|
||||
border-right: 0;
|
||||
|
||||
:deep(.el-menu-item.is-active) {
|
||||
background-color: #f5f5f5;
|
||||
}
|
||||
|
||||
&:not(.el-menu--collapse) {
|
||||
height: 100%;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
@ -0,0 +1,40 @@
|
||||
<!--
|
||||
* @Description: layout
|
||||
* @Author: zenghua.wang
|
||||
* @Date: 2024-01-26 20:13:37
|
||||
* @LastEditors: zenghua.wang
|
||||
* @LastEditTime: 2024-01-27 15:35:52
|
||||
-->
|
||||
<template>
|
||||
<el-dropdown trigger="hover" @command="setAssemblySize">
|
||||
<el-icon class="iconfont icon-size"></el-icon>
|
||||
<template #dropdown>
|
||||
<el-dropdown-menu>
|
||||
<el-dropdown-item v-for="item in assemblySizeList" :key="item" :disabled="globalComSize === item" :command="item">
|
||||
{{ assemblySizeListCh[item] }}
|
||||
</el-dropdown-item>
|
||||
</el-dropdown-menu>
|
||||
</template>
|
||||
</el-dropdown>
|
||||
</template>
|
||||
|
||||
<script setup name="layout-size">
|
||||
import { reactive, computed } from 'vue';
|
||||
import { useSettingStore } from '@/store/modules/setting';
|
||||
const SettingStore = useSettingStore();
|
||||
|
||||
const globalComSize = computed(() => SettingStore.themeConfig.globalComSize);
|
||||
|
||||
const assemblySizeListCh = reactive({
|
||||
default: '默认',
|
||||
large: '大型',
|
||||
small: '小型',
|
||||
});
|
||||
|
||||
const assemblySizeList = reactive(['default', 'large', 'small']);
|
||||
|
||||
const setAssemblySize = (item) => {
|
||||
if (globalComSize.value === item) return;
|
||||
SettingStore.setThemeConfig({ key: 'globalComSize', val: item });
|
||||
};
|
||||
</script>
|
@ -0,0 +1,56 @@
|
||||
<template>
|
||||
<div>
|
||||
<el-dropdown trigger="hover">
|
||||
<el-button size="small" type="primary">
|
||||
<span>更多</span>
|
||||
<el-icon class="el-icon--right"><arrow-down /></el-icon>
|
||||
</el-button>
|
||||
<template #dropdown>
|
||||
<el-dropdown-menu>
|
||||
<el-dropdown-item @click="refresh"
|
||||
><el-icon :size="14"><Refresh /></el-icon> 刷新当页</el-dropdown-item
|
||||
>
|
||||
<el-dropdown-item @click="closeCurrentTab"
|
||||
><el-icon :size="14"><FolderRemove /></el-icon> 关闭当前</el-dropdown-item
|
||||
>
|
||||
<el-dropdown-item @click="closeOtherTab"
|
||||
><el-icon :size="14"><Close /></el-icon>关闭其他</el-dropdown-item
|
||||
>
|
||||
<el-dropdown-item @click="closeAllTab"
|
||||
><el-icon :size="14"><FolderDelete /></el-icon>关闭所有</el-dropdown-item
|
||||
>
|
||||
</el-dropdown-menu>
|
||||
</template>
|
||||
</el-dropdown>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup name="layout-tags-view-more">
|
||||
import { useRoute } from 'vue-router';
|
||||
import { useSettingStore } from '@/store/modules/setting';
|
||||
import { useTagsViewStore } from '@/store/modules/tagsView';
|
||||
|
||||
const route = useRoute();
|
||||
const SettingStore = useSettingStore();
|
||||
const TagsViewStore = useTagsViewStore();
|
||||
const refresh = () => {
|
||||
SettingStore.setReload();
|
||||
};
|
||||
|
||||
// 关闭当前
|
||||
const closeCurrentTab = () => {
|
||||
TagsViewStore.toLastView(route.path);
|
||||
TagsViewStore.delView(route.path);
|
||||
};
|
||||
|
||||
// 关闭其他
|
||||
const closeOtherTab = async () => {
|
||||
TagsViewStore.delOtherViews(route.path);
|
||||
};
|
||||
|
||||
// 关闭所有 去首页
|
||||
const closeAllTab = async () => {
|
||||
await TagsViewStore.delAllViews();
|
||||
TagsViewStore.goHome();
|
||||
};
|
||||
</script>
|
@ -0,0 +1,164 @@
|
||||
<template>
|
||||
<div class="layout-tags-view">
|
||||
<div class="layout-tags-view-inner">
|
||||
<el-tabs v-model="activeTabsValue" type="card" @tab-click="tabClick" @tab-remove="removeTab">
|
||||
<el-tab-pane
|
||||
v-for="item in visitedViews"
|
||||
:key="item.path"
|
||||
:path="item.path"
|
||||
:label="item.title"
|
||||
:name="item.path"
|
||||
:closable="!(item.meta && item.meta.affix)"
|
||||
>
|
||||
<template #label>
|
||||
<el-icon v-if="item.icon" class="tabs-icon">
|
||||
<component :is="item.icon"></component>
|
||||
</el-icon>
|
||||
{{ item.title }}
|
||||
</template>
|
||||
</el-tab-pane>
|
||||
</el-tabs>
|
||||
</div>
|
||||
<div class="layout-tags-view-more">
|
||||
<MoreButton />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script setup name="layout-tags-view">
|
||||
import { computed, watch, ref, onMounted } from 'vue';
|
||||
import { useRoute, useRouter } from 'vue-router';
|
||||
import path from 'path-browserify';
|
||||
import { useTagsViewStore } from '@/store/modules/tagsView';
|
||||
import { usePermissionStore } from '@/store/modules/permission';
|
||||
import MoreButton from './More';
|
||||
|
||||
const route = useRoute();
|
||||
const router = useRouter();
|
||||
const TagsViewStore = useTagsViewStore();
|
||||
const PermissionStore = usePermissionStore();
|
||||
const visitedViews = computed(() => TagsViewStore.visitedViews);
|
||||
const routes = computed(() => PermissionStore.routes);
|
||||
|
||||
const addTags = () => {
|
||||
const { name } = route;
|
||||
if (name === 'Login') {
|
||||
return;
|
||||
}
|
||||
if (name) {
|
||||
TagsViewStore.addView(route);
|
||||
}
|
||||
return false;
|
||||
};
|
||||
let affixTags = ref([]);
|
||||
function filterAffixTags(routes, basePath = '/') {
|
||||
let tags = [];
|
||||
routes.forEach((route) => {
|
||||
if (route.meta && route.meta.affix) {
|
||||
const tagPath = path.resolve(basePath, route.path);
|
||||
tags.push({
|
||||
fullPath: tagPath,
|
||||
path: tagPath,
|
||||
name: route.name,
|
||||
meta: { ...route.meta },
|
||||
});
|
||||
}
|
||||
if (route.children) {
|
||||
const tempTags = filterAffixTags(route.children, route.path);
|
||||
if (tempTags.length >= 1) {
|
||||
tags = [...tags, ...tempTags];
|
||||
}
|
||||
}
|
||||
});
|
||||
return tags;
|
||||
}
|
||||
const initTags = () => {
|
||||
let routesNew = routes.value;
|
||||
let affixTag = (affixTags.value = filterAffixTags(routesNew));
|
||||
for (const tag of affixTag) {
|
||||
if (tag.name) {
|
||||
TagsViewStore.addVisitedView(tag);
|
||||
}
|
||||
}
|
||||
};
|
||||
onMounted(() => {
|
||||
initTags();
|
||||
addTags();
|
||||
});
|
||||
watch(route, () => {
|
||||
addTags();
|
||||
});
|
||||
|
||||
const activeTabsValue = computed({
|
||||
get: () => {
|
||||
return TagsViewStore.activeTabsValue;
|
||||
},
|
||||
set: (val) => {
|
||||
TagsViewStore.setTabsMenuValue(val);
|
||||
},
|
||||
});
|
||||
|
||||
function toLastView(activeTabPath) {
|
||||
let index = visitedViews.value.findIndex((item) => item.path === activeTabPath);
|
||||
const nextTab = visitedViews.value[index + 1] || visitedViews.value[index - 1];
|
||||
if (!nextTab) return;
|
||||
router.push(nextTab.path);
|
||||
TagsViewStore.addVisitedView(nextTab);
|
||||
}
|
||||
|
||||
const tabClick = (tabItem) => {
|
||||
let path = tabItem.props.name;
|
||||
router.push(path);
|
||||
};
|
||||
|
||||
const isActive = (path) => {
|
||||
return path === route.path;
|
||||
};
|
||||
|
||||
const removeTab = async (activeTabPath) => {
|
||||
if (isActive(activeTabPath)) {
|
||||
toLastView(activeTabPath);
|
||||
}
|
||||
await TagsViewStore.delView(activeTabPath);
|
||||
};
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
.layout-tags-view {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding-left: 10px;
|
||||
padding-right: 10px;
|
||||
background: white;
|
||||
|
||||
&-inner {
|
||||
flex: 1;
|
||||
overflow: hidden;
|
||||
box-sizing: border-box;
|
||||
|
||||
.el-tabs--card :deep(.el-tabs__header) {
|
||||
box-sizing: border-box;
|
||||
height: 40px;
|
||||
padding: 0 10px;
|
||||
margin: 0;
|
||||
}
|
||||
:deep(.el-tabs) {
|
||||
.el-tabs__nav {
|
||||
border: none;
|
||||
}
|
||||
.el-tabs__header .el-tabs__item {
|
||||
border: none;
|
||||
color: #ccc;
|
||||
}
|
||||
.el-tabs__header .el-tabs__item.is-active {
|
||||
color: $color-primary;
|
||||
border-bottom: 2px solid $color-primary;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&-more {
|
||||
height: 100%;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
}
|
||||
</style>
|
57
sub-government-affairs-service/src/layouts/index.vue
Normal file
57
sub-government-affairs-service/src/layouts/index.vue
Normal file
@ -0,0 +1,57 @@
|
||||
<!--
|
||||
* @Description: layout
|
||||
* @Author: zenghua.wang
|
||||
* @Date: 2024-01-26 20:13:37
|
||||
* @LastEditors: zenghua.wang
|
||||
* @LastEditTime: 2024-02-05 16:03:31
|
||||
-->
|
||||
<template>
|
||||
<div
|
||||
class="basic-layout"
|
||||
:class="{
|
||||
hideSider: !SettingStore.isCollapse,
|
||||
}"
|
||||
>
|
||||
<Sider />
|
||||
<div class="basic-layout-container">
|
||||
<Header />
|
||||
<Main />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup name="layout">
|
||||
import { useSettingStore } from '@/store/modules/setting';
|
||||
|
||||
import Sider from './component/Sider';
|
||||
import Header from './component/Header';
|
||||
import Main from './component/Main';
|
||||
const SettingStore = useSettingStore();
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.basic-layout {
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
min-width: 1200px;
|
||||
|
||||
&.hideSider {
|
||||
:deep(.layout-sider) {
|
||||
width: 60px !important;
|
||||
}
|
||||
|
||||
.basic-layout-container {
|
||||
margin-left: 60px !important;
|
||||
}
|
||||
}
|
||||
|
||||
&-container {
|
||||
position: relative;
|
||||
@include flex-column();
|
||||
height: 100%;
|
||||
margin-left: 210px;
|
||||
box-sizing: border-box;
|
||||
transition: margin-left 0.28s;
|
||||
}
|
||||
}
|
||||
</style>
|
28
sub-government-affairs-service/src/main.js
Normal file
28
sub-government-affairs-service/src/main.js
Normal file
@ -0,0 +1,28 @@
|
||||
/**
|
||||
* @Description:
|
||||
* @Author: zenghua.wang
|
||||
* @Date: 2024-01-24 17:14:41
|
||||
* @LastEditors: zenghua.wang
|
||||
* @LastEditTime: 2024-03-22 10:11:34
|
||||
*/
|
||||
import 'virtual:svg-icons-register';
|
||||
import { createApp } from 'vue';
|
||||
import App from './App.vue';
|
||||
import router from './router';
|
||||
import pinia from './store';
|
||||
import ElementPlus from 'element-plus';
|
||||
import 'element-plus/dist/index.css';
|
||||
import Avue from '@smallwei/avue';
|
||||
import '@smallwei/avue/lib/index.css';
|
||||
import './utils/permission';
|
||||
import { registerDirective } from './directives';
|
||||
import { registerGlobalComponents } from './plugins/globalComponents';
|
||||
import { registerElIcons } from './plugins/icon';
|
||||
import { registerMicroApps } from './plugins/micro';
|
||||
|
||||
const app = createApp(App);
|
||||
app.use(pinia).use(router).use(ElementPlus).use(Avue);
|
||||
registerGlobalComponents(app);
|
||||
registerElIcons(app);
|
||||
registerDirective(app);
|
||||
registerMicroApps(app);
|
@ -0,0 +1,8 @@
|
||||
import * as components from "../../../main/src/components";
|
||||
|
||||
// 全局注册组件
|
||||
export const registerGlobalComponents = (app) => {
|
||||
Object.keys(components).forEach((key) => {
|
||||
app.component(key, components[key]);
|
||||
});
|
||||
};
|
8
sub-government-affairs-service/src/plugins/icon.js
Normal file
8
sub-government-affairs-service/src/plugins/icon.js
Normal file
@ -0,0 +1,8 @@
|
||||
import * as ElIconsModules from '@element-plus/icons-vue';
|
||||
|
||||
// 全局注册element-plus icon图标组件
|
||||
export const registerElIcons = (app) => {
|
||||
Object.keys(ElIconsModules).forEach((key) => {
|
||||
app.component(key, ElIconsModules[key]);
|
||||
});
|
||||
};
|
25
sub-government-affairs-service/src/plugins/micro.js
Normal file
25
sub-government-affairs-service/src/plugins/micro.js
Normal file
@ -0,0 +1,25 @@
|
||||
import { renderWithQiankun, qiankunWindow } from 'vite-plugin-qiankun/dist/helper';
|
||||
|
||||
export const registerMicroApps = (app) => {
|
||||
const initQiankun = () => {
|
||||
renderWithQiankun({
|
||||
bootstrap() {
|
||||
console.log('bootstrap');
|
||||
},
|
||||
mount(props) {
|
||||
console.log('mount', props);
|
||||
render(props);
|
||||
},
|
||||
update(props) {
|
||||
console.log('update', props);
|
||||
},
|
||||
unmount(props) {
|
||||
console.log('unmount', props);
|
||||
},
|
||||
});
|
||||
};
|
||||
const render = ({ container }) => {
|
||||
app.mount(container ? container : '#app');
|
||||
};
|
||||
qiankunWindow.__POWERED_BY_QIANKUN__ ? initQiankun() : render({});
|
||||
};
|
62
sub-government-affairs-service/src/router/index.js
Normal file
62
sub-government-affairs-service/src/router/index.js
Normal file
@ -0,0 +1,62 @@
|
||||
/*
|
||||
* @Description: router
|
||||
* @Author: zenghua.wang
|
||||
* @Date: 2023-06-20 11:48:41
|
||||
* @LastEditors: zenghua.wang “1048523306@qq.com”
|
||||
* @LastEditTime: 2025-01-21 14:27:43
|
||||
*/
|
||||
import { createRouter, createWebHistory, createWebHashHistory } from 'vue-router';
|
||||
import { qiankunWindow } from 'vite-plugin-qiankun/dist/helper';
|
||||
import Layout from '@/layouts/index.vue';
|
||||
|
||||
import resourceRouter from './modules/resource';
|
||||
|
||||
const { VITE_APP_NAME } = import.meta.env;
|
||||
|
||||
export const constantRoutes = [
|
||||
{
|
||||
path: '/404',
|
||||
name: '404',
|
||||
component: () => import('@/views/error/404.vue'),
|
||||
hidden: true,
|
||||
},
|
||||
{
|
||||
path: '/403',
|
||||
name: '403',
|
||||
component: () => import('@/views/error/403.vue'),
|
||||
hidden: true,
|
||||
},
|
||||
{
|
||||
path: '/',
|
||||
name: 'layout',
|
||||
component: Layout,
|
||||
redirect: '/home',
|
||||
meta: { title: '政务服务', icon: 'House' },
|
||||
children: [
|
||||
{
|
||||
path: '/home',
|
||||
component: () => import('@/views/home/index.vue'),
|
||||
name: 'home',
|
||||
meta: { title: '政务服务主页', icon: 'House' },
|
||||
},
|
||||
],
|
||||
},
|
||||
...resourceRouter,
|
||||
];
|
||||
|
||||
/**
|
||||
* @Title notFoundRouter(找不到路由)
|
||||
*/
|
||||
export const notFoundRouter = {
|
||||
path: '/:pathMatch(.*)',
|
||||
name: 'notFound',
|
||||
redirect: '/404',
|
||||
};
|
||||
|
||||
const router = createRouter({
|
||||
history: createWebHistory(qiankunWindow.__POWERED_BY_QIANKUN__ ? `/${VITE_APP_NAME}/` : '/'), // history
|
||||
// history: createWebHashHistory(), // hash
|
||||
routes: constantRoutes,
|
||||
});
|
||||
|
||||
export default router;
|
@ -0,0 +1,68 @@
|
||||
import Layout from '@/layouts/index.vue';
|
||||
import Views from '@/layouts/Views.vue';
|
||||
|
||||
export default [
|
||||
{
|
||||
path: '/resource',
|
||||
name: 'resource',
|
||||
component: Layout,
|
||||
redirect: '/resource-map',
|
||||
meta: { title: '土地资源管理管理', icon: 'icon-test' },
|
||||
children: [
|
||||
{
|
||||
path: '/resource-map',
|
||||
component: () => import('@/views/resource/map/index.vue'),
|
||||
name: 'resource-map',
|
||||
meta: { title: '土地资源一张图', icon: 'Document' },
|
||||
},
|
||||
{
|
||||
path: '/resource-info',
|
||||
name: 'resource-info-manager',
|
||||
component: Views,
|
||||
meta: { title: '土地信息管理', icon: 'Document' },
|
||||
redirect: '/land',
|
||||
children: [
|
||||
{
|
||||
path: '/land',
|
||||
component: () => import('@/views/resource/land/index.vue'),
|
||||
name: 'land',
|
||||
meta: { title: '土地资源', icon: 'Document' },
|
||||
},
|
||||
{
|
||||
path: '/land-fill',
|
||||
name: 'land-fill',
|
||||
component: () => import('@/views/resource/land/index.vue'),
|
||||
meta: { title: '土地信息填报审核', icon: 'Document' },
|
||||
},
|
||||
{
|
||||
path: '/land-fill-review',
|
||||
name: 'land-fill-review',
|
||||
component: () => import('@/views/resource/land/index.vue'),
|
||||
meta: { title: '土地流转信息填报审核', icon: 'Document' },
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
path: '/land-monitor',
|
||||
name: 'land-monitor',
|
||||
component: Views,
|
||||
meta: { title: '土地监管', icon: 'Document' },
|
||||
redirect: '/land-patrol',
|
||||
children: [
|
||||
{
|
||||
path: '/land-patrol',
|
||||
component: () => import('@/views/resource/land/index.vue'),
|
||||
name: 'land-patrol',
|
||||
meta: { title: '土地使用巡查', icon: 'Document' },
|
||||
},
|
||||
{
|
||||
path: '/land-handle',
|
||||
name: 'land-handle',
|
||||
component: () => import('@/views/resource/land/index.vue'),
|
||||
meta: { title: '土地违法处理', icon: 'Document' },
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
18
sub-government-affairs-service/src/store/index.js
Normal file
18
sub-government-affairs-service/src/store/index.js
Normal file
@ -0,0 +1,18 @@
|
||||
import { defineStore, createPinia } from 'pinia';
|
||||
import piniaPluginPersistedstate from 'pinia-plugin-persistedstate';
|
||||
|
||||
export const Store = defineStore({
|
||||
id: 'globalState',
|
||||
state: () => ({}),
|
||||
getters: {},
|
||||
actions: {},
|
||||
persist: {
|
||||
key: 'globalState',
|
||||
storage: window.sessionStorage, //localstorage
|
||||
},
|
||||
});
|
||||
|
||||
const pinia = createPinia();
|
||||
pinia.use(piniaPluginPersistedstate);
|
||||
|
||||
export default pinia;
|
@ -0,0 +1,52 @@
|
||||
import { defineStore } from 'pinia';
|
||||
import { constantRoutes, notFoundRouter } from '@/router';
|
||||
import { createAsyncRoutes, filterAsyncRoutes, filterKeepAlive } from '@/utils/router';
|
||||
import { useUserStore } from '@/store/modules/user';
|
||||
import { getTree } from '@/utils';
|
||||
|
||||
export const usePermissionStore = defineStore({
|
||||
id: 'permissionStore',
|
||||
state: () => ({
|
||||
// 路由
|
||||
routes: [],
|
||||
// 动态路由
|
||||
asyncRoutes: [],
|
||||
// 缓存路由
|
||||
cacheRoutes: {},
|
||||
}),
|
||||
getters: {
|
||||
permissionRoutes: (state) => {
|
||||
return state.routes;
|
||||
},
|
||||
keepAliveRoutes: (state) => {
|
||||
return filterKeepAlive(state.asyncRoutes);
|
||||
},
|
||||
},
|
||||
actions: {
|
||||
generateRoutes(roles) {
|
||||
return new Promise((resolve) => {
|
||||
// 在这判断是否有权限,哪些角色拥有哪些权限
|
||||
const UserStore = useUserStore();
|
||||
this.asyncRoutes = createAsyncRoutes(getTree(UserStore.getMenus()));
|
||||
let accessedRoutes;
|
||||
if (roles && roles.length && !roles.includes('admin')) {
|
||||
accessedRoutes = filterAsyncRoutes(this.asyncRoutes, roles);
|
||||
} else {
|
||||
accessedRoutes = this.asyncRoutes || [];
|
||||
}
|
||||
accessedRoutes = accessedRoutes.concat(notFoundRouter);
|
||||
this.routes = constantRoutes.concat(accessedRoutes);
|
||||
resolve(accessedRoutes);
|
||||
});
|
||||
},
|
||||
clearRoutes() {
|
||||
this.routes = [];
|
||||
this.asyncRoutes = [];
|
||||
this.cacheRoutes = [];
|
||||
},
|
||||
getCacheRoutes() {
|
||||
this.cacheRoutes = filterKeepAlive(this.asyncRoutes);
|
||||
return this.cacheRoutes;
|
||||
},
|
||||
},
|
||||
});
|
69
sub-government-affairs-service/src/store/modules/setting.js
Normal file
69
sub-government-affairs-service/src/store/modules/setting.js
Normal file
@ -0,0 +1,69 @@
|
||||
import { defineStore } from 'pinia';
|
||||
import { CONSTANTS } from '@/config';
|
||||
|
||||
export const useSettingStore = defineStore({
|
||||
id: 'settingStore',
|
||||
state: () => ({
|
||||
// menu 是否收缩
|
||||
isCollapse: true,
|
||||
//
|
||||
withoutAnimation: false,
|
||||
device: 'desktop',
|
||||
// 刷新当前页
|
||||
isReload: true,
|
||||
// 主题设置
|
||||
themeConfig: {
|
||||
// 显示设置
|
||||
showSetting: false,
|
||||
// 菜单展示模式 默认 vertical horizontal / vertical /columns
|
||||
mode: 'vertical',
|
||||
// tagsView 是否展示 默认展示
|
||||
showTag: true,
|
||||
// 页脚
|
||||
footer: true,
|
||||
// 深色模式 切换暗黑模式
|
||||
isDark: false,
|
||||
// 显示侧边栏Logo
|
||||
showLogo: true,
|
||||
// 主题颜色
|
||||
primary: CONSTANTS.PRIMARY,
|
||||
// element组件大小
|
||||
globalComSize: 'default',
|
||||
// 是否只保持一个子菜单的展开
|
||||
uniqueOpened: true,
|
||||
// 固定header
|
||||
fixedHeader: true,
|
||||
// 灰色模式
|
||||
gray: false,
|
||||
// 色弱模式
|
||||
weak: false,
|
||||
},
|
||||
}),
|
||||
getters: {},
|
||||
actions: {
|
||||
// 设置主题
|
||||
setThemeConfig({ key, val }) {
|
||||
this.themeConfig[key] = val;
|
||||
},
|
||||
// 切换 Collapse
|
||||
setCollapse(value) {
|
||||
this.isCollapse = value;
|
||||
this.withoutAnimation = false;
|
||||
},
|
||||
// 关闭侧边栏
|
||||
closeSideBar({ withoutAnimation }) {
|
||||
this.isCollapse = false;
|
||||
this.withoutAnimation = withoutAnimation;
|
||||
},
|
||||
toggleDevice(device) {
|
||||
this.device = device;
|
||||
},
|
||||
// 刷新
|
||||
setReload() {
|
||||
this.isReload = false;
|
||||
setTimeout(() => {
|
||||
this.isReload = true;
|
||||
}, 50);
|
||||
},
|
||||
},
|
||||
});
|
104
sub-government-affairs-service/src/store/modules/tagsView.js
Normal file
104
sub-government-affairs-service/src/store/modules/tagsView.js
Normal file
@ -0,0 +1,104 @@
|
||||
import { defineStore } from 'pinia';
|
||||
import router from '@/router';
|
||||
|
||||
export const useTagsViewStore = defineStore({
|
||||
id: 'tagsViewStore',
|
||||
state: () => ({
|
||||
activeTabsValue: '/home',
|
||||
visitedViews: [],
|
||||
cachedViews: [],
|
||||
}),
|
||||
getters: {},
|
||||
actions: {
|
||||
setTabsMenuValue(val) {
|
||||
this.activeTabsValue = val;
|
||||
},
|
||||
addView(view) {
|
||||
this.addVisitedView(view);
|
||||
},
|
||||
removeView(routes) {
|
||||
return new Promise((resolve) => {
|
||||
this.visitedViews = this.visitedViews.filter((item) => !routes.includes(item.path));
|
||||
resolve(null);
|
||||
});
|
||||
},
|
||||
addVisitedView(view) {
|
||||
this.setTabsMenuValue(view.path);
|
||||
if (this.visitedViews.some((v) => v.path === view.path)) return;
|
||||
|
||||
this.visitedViews.push(
|
||||
Object.assign({}, view, {
|
||||
title: view.meta.title || 'no-name',
|
||||
})
|
||||
);
|
||||
if (view.meta.keepAlive) {
|
||||
this.cachedViews.push(view.name);
|
||||
}
|
||||
},
|
||||
delView(activeTabPath) {
|
||||
return new Promise((resolve) => {
|
||||
this.delVisitedView(activeTabPath);
|
||||
this.delCachedView(activeTabPath);
|
||||
resolve({
|
||||
visitedViews: [...this.visitedViews],
|
||||
cachedViews: [...this.cachedViews],
|
||||
});
|
||||
});
|
||||
},
|
||||
toLastView(activeTabPath) {
|
||||
const index = this.visitedViews.findIndex((item) => item.path === activeTabPath);
|
||||
const nextTab = this.visitedViews[index + 1] || this.visitedViews[index - 1];
|
||||
if (!nextTab) return;
|
||||
router.push(nextTab.path);
|
||||
this.addVisitedView(nextTab);
|
||||
},
|
||||
delVisitedView(path) {
|
||||
return new Promise((resolve) => {
|
||||
this.visitedViews = this.visitedViews.filter((v) => {
|
||||
return v.path !== path || v.meta.affix;
|
||||
});
|
||||
this.cachedViews = this.cachedViews.filter((v) => {
|
||||
return v.path !== path || v.meta.affix;
|
||||
});
|
||||
resolve([...this.visitedViews]);
|
||||
});
|
||||
},
|
||||
delCachedView(view) {
|
||||
return new Promise((resolve) => {
|
||||
const index = this.cachedViews.indexOf(view.name);
|
||||
index > -1 && this.cachedViews.splice(index, 1);
|
||||
resolve([...this.cachedViews]);
|
||||
});
|
||||
},
|
||||
clearVisitedView() {
|
||||
this.delAllViews();
|
||||
},
|
||||
delAllViews() {
|
||||
return new Promise((resolve) => {
|
||||
this.visitedViews = this.visitedViews.filter((v) => v.meta.affix);
|
||||
this.cachedViews = this.visitedViews.filter((v) => v.meta.affix);
|
||||
resolve([...this.visitedViews]);
|
||||
});
|
||||
},
|
||||
delOtherViews(path) {
|
||||
this.visitedViews = this.visitedViews.filter((item) => {
|
||||
return item.path === path || item.meta.affix;
|
||||
});
|
||||
this.cachedViews = this.visitedViews.filter((item) => {
|
||||
return item.path === path || item.meta.affix;
|
||||
});
|
||||
},
|
||||
goHome() {
|
||||
this.activeTabsValue = '/home';
|
||||
router.push({ path: '/home' });
|
||||
},
|
||||
updateVisitedView(view) {
|
||||
for (let v of this.visitedViews) {
|
||||
if (v.path === view.path) {
|
||||
v = Object.assign(v, view);
|
||||
break;
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
});
|
65
sub-government-affairs-service/src/store/modules/user.js
Normal file
65
sub-government-affairs-service/src/store/modules/user.js
Normal file
@ -0,0 +1,65 @@
|
||||
import { defineStore } from 'pinia';
|
||||
import { GenKey } from '@/config';
|
||||
import { isEmpty, encode, decode } from '@/utils';
|
||||
|
||||
export const useUserStore = defineStore({
|
||||
id: GenKey('USER_STATE'),
|
||||
state: () => ({
|
||||
token: null,
|
||||
userInfo: {},
|
||||
currentOrg: null,
|
||||
orgList: [],
|
||||
menus: [],
|
||||
}),
|
||||
getters: {},
|
||||
actions: {
|
||||
setToken(token) {
|
||||
this.token = token;
|
||||
},
|
||||
hasToken() {
|
||||
return !isEmpty(this.token);
|
||||
},
|
||||
setUserInfo(userInfo) {
|
||||
this.userInfo = encode(JSON.stringify(userInfo), true);
|
||||
},
|
||||
getUserInfo() {
|
||||
return !isEmpty(this.userInfo) ? JSON.parse(decode(this.userInfo, true)) : {};
|
||||
},
|
||||
setOrgList(orgList) {
|
||||
this.orgList = encode(JSON.stringify(orgList), true);
|
||||
},
|
||||
getOrgList() {
|
||||
return !isEmpty(this.orgList) ? JSON.parse(decode(this.orgList, true)) : [];
|
||||
},
|
||||
setCurrentOrg(org) {
|
||||
this.currentOrg = org;
|
||||
},
|
||||
getCurrentOrg() {
|
||||
const list = this.getOrgList().filter((item) => {
|
||||
return item.id === this.currentOrg;
|
||||
});
|
||||
return !isEmpty(list) ? list[0] : {};
|
||||
},
|
||||
setMenus(menus) {
|
||||
this.menus = encode(JSON.stringify(menus), true);
|
||||
},
|
||||
getMenus() {
|
||||
return !isEmpty(this.menus) ? JSON.parse(decode(this.menus, true)) : [];
|
||||
},
|
||||
logout() {
|
||||
this.token = null;
|
||||
this.userInfo = {};
|
||||
this.currentOrg = null;
|
||||
this.orgList = [];
|
||||
this.menus = [];
|
||||
localStorage.removeItem(GenKey('USER_STATE'));
|
||||
},
|
||||
clear() {
|
||||
localStorage.removeItem(GenKey('USER_STATE'));
|
||||
},
|
||||
},
|
||||
persist: {
|
||||
key: GenKey('USER_STATE'),
|
||||
storage: window.localStorage,
|
||||
},
|
||||
});
|
283
sub-government-affairs-service/src/styles/common/base.scss
Normal file
283
sub-government-affairs-service/src/styles/common/base.scss
Normal file
@ -0,0 +1,283 @@
|
||||
html {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
border: 0;
|
||||
text-size-adjust: 100%;
|
||||
-webkit-tap-highlight-color: transparent;
|
||||
}
|
||||
a,
|
||||
abbr,
|
||||
acronym,
|
||||
address,
|
||||
article,
|
||||
aside,
|
||||
blockquote,
|
||||
caption,
|
||||
code,
|
||||
del,
|
||||
dfn,
|
||||
dialog,
|
||||
header,
|
||||
footer,
|
||||
nav,
|
||||
object,
|
||||
section,
|
||||
body,
|
||||
dd,
|
||||
div,
|
||||
dl,
|
||||
dt,
|
||||
em,
|
||||
img,
|
||||
fieldset,
|
||||
figure,
|
||||
form,
|
||||
h1,
|
||||
h2,
|
||||
h3,
|
||||
h4,
|
||||
h5,
|
||||
h6,
|
||||
hgroup,
|
||||
iframe,
|
||||
legend,
|
||||
p,
|
||||
pre,
|
||||
q,
|
||||
span,
|
||||
tbody,
|
||||
tfoot,
|
||||
thead,
|
||||
ul,
|
||||
ol,
|
||||
li {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
border: 0;
|
||||
list-style: none;
|
||||
vertical-align: baseline;
|
||||
}
|
||||
article,
|
||||
aside,
|
||||
details,
|
||||
dialog,
|
||||
figcaption,
|
||||
figure,
|
||||
footer,
|
||||
header,
|
||||
hgroup,
|
||||
menu,
|
||||
nav,
|
||||
section {
|
||||
display: block;
|
||||
}
|
||||
body {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
font-size: 12px;
|
||||
font-family: 'Helvetica Neue', Helvetica, Arial, 'PingFang SC', 'Hiragino Sans GB', 'Heiti SC', 'Microsoft YaHei', 'WenQuanYi Micro Hei', sans-serif;
|
||||
color: #323232;
|
||||
|
||||
// background: #000;
|
||||
}
|
||||
img {
|
||||
vertical-align: bottom;
|
||||
border: 0;
|
||||
}
|
||||
|
||||
// ::input-placeholder {
|
||||
// color: #999999;
|
||||
// }
|
||||
::placeholder {
|
||||
color: #999999;
|
||||
}
|
||||
|
||||
// :input-placeholder {
|
||||
// color: #cccccc;
|
||||
// }
|
||||
button::-moz-focus-inner,
|
||||
input::-moz-focus-inner {
|
||||
padding: 0;
|
||||
border: 0;
|
||||
}
|
||||
textarea {
|
||||
overflow: auto;
|
||||
}
|
||||
button:focus,
|
||||
input:focus,
|
||||
select:focus,
|
||||
textarea:focus {
|
||||
outline: 0;
|
||||
}
|
||||
input::-ms-clear {
|
||||
display: none;
|
||||
}
|
||||
article,
|
||||
aside,
|
||||
details,
|
||||
figcaption,
|
||||
figure,
|
||||
footer,
|
||||
header,
|
||||
hgroup,
|
||||
main,
|
||||
nav,
|
||||
section,
|
||||
summary {
|
||||
display: block;
|
||||
}
|
||||
audio,
|
||||
canvas,
|
||||
progress,
|
||||
video {
|
||||
display: inline-block;
|
||||
vertical-align: baseline;
|
||||
}
|
||||
audio:not([controls]) {
|
||||
display: none;
|
||||
height: 0;
|
||||
}
|
||||
[hidden],
|
||||
template {
|
||||
display: none;
|
||||
}
|
||||
a {
|
||||
text-decoration: none;
|
||||
color: #323232;
|
||||
background: none;
|
||||
cursor: pointer;
|
||||
}
|
||||
a:active,
|
||||
a:hover {
|
||||
outline: 0;
|
||||
}
|
||||
a:focus {
|
||||
outline: thin dotted;
|
||||
outline: 5px auto -webkit-focus-ring-color;
|
||||
outline-offset: -2px;
|
||||
}
|
||||
abbr[title] {
|
||||
border-bottom: 1px dotted;
|
||||
}
|
||||
b,
|
||||
strong {
|
||||
font-weight: 700;
|
||||
}
|
||||
dfn {
|
||||
font-style: italic;
|
||||
}
|
||||
mark {
|
||||
color: #000000;
|
||||
background: #ffff00;
|
||||
}
|
||||
small {
|
||||
font-size: 80%;
|
||||
}
|
||||
sub,
|
||||
sup {
|
||||
position: relative;
|
||||
font-size: 75%;
|
||||
line-height: 0;
|
||||
vertical-align: baseline;
|
||||
}
|
||||
sup {
|
||||
top: -0.5em;
|
||||
}
|
||||
sub {
|
||||
bottom: -0.25em;
|
||||
}
|
||||
svg:not(:root) {
|
||||
overflow: hidden;
|
||||
}
|
||||
figure {
|
||||
margin: 1em 40px;
|
||||
}
|
||||
hr {
|
||||
box-sizing: content-box;
|
||||
height: 0;
|
||||
}
|
||||
pre {
|
||||
overflow: auto;
|
||||
}
|
||||
code,
|
||||
kbd,
|
||||
pre,
|
||||
samp {
|
||||
font-size: 1em;
|
||||
font-family: monospace;
|
||||
}
|
||||
button,
|
||||
input,
|
||||
optgroup,
|
||||
select,
|
||||
textarea {
|
||||
margin: 0;
|
||||
font: inherit;
|
||||
}
|
||||
button {
|
||||
overflow: visible;
|
||||
}
|
||||
button,
|
||||
select {
|
||||
text-transform: none;
|
||||
}
|
||||
button,
|
||||
html input[type='button'],
|
||||
input[type='reset'],
|
||||
input[type='submit'] {
|
||||
appearance: button;
|
||||
cursor: pointer;
|
||||
}
|
||||
button[disabled],
|
||||
html input[disabled] {
|
||||
cursor: default;
|
||||
}
|
||||
button::-moz-focus-inner,
|
||||
input::-moz-focus-inner {
|
||||
padding: 0;
|
||||
border: 0;
|
||||
}
|
||||
input {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
line-height: normal;
|
||||
}
|
||||
input[type='checkbox'],
|
||||
input[type='radio'] {
|
||||
box-sizing: border-box;
|
||||
padding: 0;
|
||||
}
|
||||
input[type='number']::-webkit-inner-spin-button,
|
||||
input[type='number']::-webkit-outer-spin-button {
|
||||
height: auto;
|
||||
}
|
||||
input[type='search'] {
|
||||
box-sizing: content-box;
|
||||
appearance: textfield;
|
||||
}
|
||||
input[type='search']::-webkit-search-cancel-button,
|
||||
input[type='search']::-webkit-search-decoration {
|
||||
appearance: none;
|
||||
}
|
||||
fieldset {
|
||||
margin: 0 2px;
|
||||
padding: 0.35em 0.625em 0.75em;
|
||||
border: 1px solid silver;
|
||||
}
|
||||
legend {
|
||||
padding: 0;
|
||||
border: 0;
|
||||
}
|
||||
optgroup {
|
||||
font-weight: 700;
|
||||
}
|
||||
table {
|
||||
border-spacing: 0;
|
||||
border-collapse: collapse;
|
||||
}
|
||||
td,
|
||||
th {
|
||||
padding: 0;
|
||||
}
|
126
sub-government-affairs-service/src/styles/common/define.scss
Normal file
126
sub-government-affairs-service/src/styles/common/define.scss
Normal file
@ -0,0 +1,126 @@
|
||||
.flex {
|
||||
&-row {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
}
|
||||
&-column {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
}
|
||||
|
||||
.custom-table {
|
||||
padding: 10px;
|
||||
background: #fff;
|
||||
border-radius: 10px;
|
||||
}
|
||||
|
||||
.custom-form {
|
||||
&__title {
|
||||
padding: 10px 0;
|
||||
font-size: 15px;
|
||||
font-weight: bold;
|
||||
color: $color-333;
|
||||
|
||||
&::before {
|
||||
@include icon-space();
|
||||
width: 4px;
|
||||
background: $color-primary;
|
||||
height: 1.15em;
|
||||
vertical-align: -0.25em;
|
||||
margin-right: 8px;
|
||||
}
|
||||
}
|
||||
&__panel {
|
||||
padding: 16px 0;
|
||||
}
|
||||
&__input {
|
||||
width: 450px !important;
|
||||
}
|
||||
&__rich {
|
||||
overflow: hidden;
|
||||
width: 800px;
|
||||
}
|
||||
&__picture {
|
||||
display: inline-block;
|
||||
padding: 15px;
|
||||
vertical-align: top;
|
||||
&-wrap {
|
||||
margin-bottom: 30px;
|
||||
}
|
||||
}
|
||||
&__avatar {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
width: 100px;
|
||||
height: 100px;
|
||||
border: 1px solid #eeeeee;
|
||||
}
|
||||
&__img {
|
||||
max-height: 100%;
|
||||
}
|
||||
&__uploader {
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
overflow: hidden;
|
||||
width: 160px;
|
||||
height: 160px;
|
||||
border: 1px solid;
|
||||
border-radius: 4px;
|
||||
text-align: center;
|
||||
color: $color-input-border;
|
||||
background-color: var(--el-fill-color-lighter);
|
||||
cursor: pointer;
|
||||
vertical-align: top;
|
||||
&:hover {
|
||||
color: $color-primary;
|
||||
}
|
||||
&__img,
|
||||
&__icon {
|
||||
position: absolute !important;
|
||||
top: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
margin: auto;
|
||||
}
|
||||
&__img {
|
||||
width: 100%;
|
||||
}
|
||||
&__icon {
|
||||
height: 1em;
|
||||
font-size: 28px !important;
|
||||
text-align: center;
|
||||
line-height: 1;
|
||||
}
|
||||
&__remove {
|
||||
position: absolute;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
padding: 5px 10px;
|
||||
font-size: 18px;
|
||||
}
|
||||
.el-upload {
|
||||
display: block;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
&-inline {
|
||||
display: inline-block;
|
||||
text-align: center;
|
||||
& + & {
|
||||
margin-left: 10px;
|
||||
}
|
||||
}
|
||||
}
|
||||
&__tips {
|
||||
padding-left: 3px;
|
||||
font-size: 12px;
|
||||
color: #999;
|
||||
}
|
||||
&__note {
|
||||
font-size: 12px;
|
||||
color: $color-danger;
|
||||
}
|
||||
}
|
61
sub-government-affairs-service/src/styles/global.scss
Normal file
61
sub-government-affairs-service/src/styles/global.scss
Normal file
@ -0,0 +1,61 @@
|
||||
// color
|
||||
$legacy-ie: 10;
|
||||
$color-primary: #20a0ff;
|
||||
$color-success: #13ce66;
|
||||
$color-warning: #f7ba2a;
|
||||
$color-danger: #ff4949;
|
||||
$color-info: #50bfff;
|
||||
$color-secondary: #2e90fe;
|
||||
$color-white: #ffffff;
|
||||
$color-black: #1f2d3d;
|
||||
$color-black-light: #324057;
|
||||
$color-black-lighter: #475669;
|
||||
$color-blue-light: #5da9ff;
|
||||
$color-blue-lighter: #5da9ff;
|
||||
$color-black: #000000;
|
||||
$color-silver: #8492a6;
|
||||
$color-silver-light: #99a9bf;
|
||||
$color-silver-lighter: #c0ccda;
|
||||
$color-gray: #d3dce6;
|
||||
$color-gray-light: #e5e9f2;
|
||||
$color-gray-lighter: #eff2f7;
|
||||
$color-333: #333333;
|
||||
$color-666: #333333;
|
||||
$color-999: #999999;
|
||||
$color-border-gray: #d1dbe5;
|
||||
$color-input-border: #dcdfe6;
|
||||
$color-border: $color-border-gray;
|
||||
$color-types: (
|
||||
primary: (
|
||||
$color-primary,
|
||||
#4db3ff,
|
||||
#1d90e6,
|
||||
),
|
||||
info: (
|
||||
$color-info,
|
||||
#73ccff,
|
||||
#48ace6,
|
||||
),
|
||||
success: (
|
||||
$color-success,
|
||||
#42d885,
|
||||
#11b95c,
|
||||
),
|
||||
warning: (
|
||||
$color-warning,
|
||||
#f9c855,
|
||||
#dea726,
|
||||
),
|
||||
danger: (
|
||||
$color-danger,
|
||||
#ff6d6d,
|
||||
#e64242,
|
||||
),
|
||||
gray: (
|
||||
$color-999,
|
||||
#999999,
|
||||
#9d9d9d,
|
||||
),
|
||||
);
|
||||
|
||||
@import 'utils/utils';
|
19
sub-government-affairs-service/src/styles/style.scss
Normal file
19
sub-government-affairs-service/src/styles/style.scss
Normal file
@ -0,0 +1,19 @@
|
||||
@import 'common/base.scss';
|
||||
@import 'common/define.scss';
|
||||
@import '@/assets/fonts/iconfont.css';
|
||||
|
||||
#app {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
font-family: Avenir, sans-serif;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
background-color: #f5f5f5;
|
||||
}
|
||||
|
||||
.el-dialog__body {
|
||||
overflow: hidden auto;
|
||||
height: auto;
|
||||
max-height: calc(100vh - 130px);
|
||||
}
|
31
sub-government-affairs-service/src/styles/utils/_bem.scss
Normal file
31
sub-government-affairs-service/src/styles/utils/_bem.scss
Normal file
@ -0,0 +1,31 @@
|
||||
/// Block Element
|
||||
/// @access public
|
||||
/// @param {String} $element - Element's name
|
||||
@mixin element($element) {
|
||||
&__#{$element} {
|
||||
@content;
|
||||
}
|
||||
}
|
||||
|
||||
/// @alias element
|
||||
@mixin e($element) {
|
||||
@include element($element) {
|
||||
@content;
|
||||
}
|
||||
}
|
||||
|
||||
/// Block Modifier
|
||||
/// @access public
|
||||
/// @param {String} $modifier - Modifier's name
|
||||
@mixin modifier($modifier) {
|
||||
&_#{$modifier} {
|
||||
@content;
|
||||
}
|
||||
}
|
||||
|
||||
/// @alias modifier
|
||||
@mixin m($modifier) {
|
||||
@include modifier($modifier) {
|
||||
@content;
|
||||
}
|
||||
}
|
@ -0,0 +1,30 @@
|
||||
@mixin ellipsis($lines: 1, $line-height: 0) {
|
||||
overflow: hidden;
|
||||
|
||||
@if $lines == 1 {
|
||||
@if $legacy-ie <= 8 {
|
||||
word-wrap: normal; // for ie
|
||||
}
|
||||
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
} @else {
|
||||
display: flexbox;
|
||||
-webkit-line-clamp: $lines;
|
||||
-webkit-box-orient: vertical;
|
||||
|
||||
@if value-of($line-height) == 0 {
|
||||
@error 'line-height is required when clamp muti lines';
|
||||
}
|
||||
|
||||
@if unitless($line-height) or unit($line-height) == '%' {
|
||||
$line-height: value-of($line-height) * 1em;
|
||||
}
|
||||
|
||||
max-height: $line-height * $lines;
|
||||
|
||||
// &:after {
|
||||
// content: " ...";
|
||||
// }
|
||||
}
|
||||
}
|
@ -0,0 +1,5 @@
|
||||
@mixin scrollable() {
|
||||
overflow-x: hidden;
|
||||
overflow-y: auto;
|
||||
-webkit-overflow-scrolling: touch;
|
||||
}
|
@ -0,0 +1,34 @@
|
||||
/// Mixin to customize scrollbars
|
||||
/// Beware, this does not work in all browsers
|
||||
/// @author Hugo Giraudel
|
||||
/// @param {Length} $size - Horizontal scrollbar's height and vertical scrollbar's width
|
||||
/// @param {Color} $foreground-color - Scrollbar's color
|
||||
/// @param {Color} $background-color [mix($foreground-color, white, 50%)] - Scrollbar's color
|
||||
/// @example scss - Scrollbar styling
|
||||
/// @include scrollbars(.5em, slategray);
|
||||
@mixin scrollbars($size, $foreground-color, $background-color: mix($foreground-color, white, 50%)) {
|
||||
// For Google Chrome
|
||||
::-webkit-scrollbar {
|
||||
width: $size;
|
||||
height: $size;
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-thumb {
|
||||
background: $foreground-color;
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-track {
|
||||
background: $background-color;
|
||||
}
|
||||
|
||||
// For Internet Explorer
|
||||
body {
|
||||
scrollbar-face-color: $foreground-color;
|
||||
scrollbar-track-color: $background-color;
|
||||
}
|
||||
}
|
||||
|
||||
/// alias
|
||||
@mixin scrollbar($size, $foreground-color, $background-color: mix($foreground-color, white, 50%)) {
|
||||
@include scrollbars($size, $foreground-color, $background-color: mix($foreground-color, white, 50%));
|
||||
}
|
@ -0,0 +1,11 @@
|
||||
@function value-of($value) {
|
||||
@if type-of($value) == 'number' and not unitless($value) {
|
||||
@return $value / ($value * 0 + 1);
|
||||
}
|
||||
@return $value;
|
||||
}
|
||||
|
||||
// alias
|
||||
@function strip-unit($value) {
|
||||
@return value-of($value);
|
||||
}
|
21
sub-government-affairs-service/src/styles/utils/utils.scss
Normal file
21
sub-government-affairs-service/src/styles/utils/utils.scss
Normal file
@ -0,0 +1,21 @@
|
||||
@import 'bem';
|
||||
@import 'ellipsis';
|
||||
@import 'scrollable';
|
||||
@import 'scrollbar';
|
||||
@import 'value-of';
|
||||
|
||||
@mixin flex-row() {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
}
|
||||
|
||||
@mixin flex-column() {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
@mixin icon-space() {
|
||||
display: inline-block;
|
||||
content: '';
|
||||
background-size: 100%;
|
||||
}
|
106
sub-government-affairs-service/src/utils/axios.js
Normal file
106
sub-government-affairs-service/src/utils/axios.js
Normal file
@ -0,0 +1,106 @@
|
||||
/*
|
||||
* @Descripttion:
|
||||
* @Author: zenghua.wang
|
||||
* @Date: 2022-02-23 21:12:37
|
||||
* @LastEditors: zenghua.wang
|
||||
* @LastEditTime: 2024-01-06 11:29:36
|
||||
*/
|
||||
import axios from 'axios';
|
||||
import { ElNotification } from 'element-plus';
|
||||
import router from '@/router';
|
||||
import { isEmpty } from '@/utils';
|
||||
import { useUserStore } from '@/store/modules/user';
|
||||
|
||||
const { VITE_APP_BASE_API, VITE_APP_UPLOAD_API } = import.meta.env;
|
||||
|
||||
/**
|
||||
* 创建axios实例
|
||||
*/
|
||||
const publicAxios = axios.create({
|
||||
baseURL: VITE_APP_BASE_API, // API请求的默认前缀
|
||||
timeout: 30000,
|
||||
});
|
||||
/**
|
||||
* 异常拦截处理器
|
||||
* @param error
|
||||
* @returns
|
||||
*/
|
||||
const errorHandler = async (error) => {
|
||||
const { response } = error;
|
||||
const UserStore = useUserStore();
|
||||
if (response && response.status) {
|
||||
switch (response.status) {
|
||||
case 401:
|
||||
await UserStore.logout();
|
||||
router.push('/login');
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
return Promise.reject(error?.response?.data);
|
||||
};
|
||||
/**
|
||||
* 请求拦截器
|
||||
*/
|
||||
publicAxios.interceptors.request.use(async (config) => {
|
||||
const UserStore = useUserStore();
|
||||
config.baseURL = config.isUpload ? VITE_APP_UPLOAD_API : VITE_APP_BASE_API;
|
||||
if (UserStore.hasToken()) {
|
||||
config.headers['fairies-auth-token'] = config.headers['fairies-auth-token'] ?? UserStore.token;
|
||||
config.headers['fairies-org-id'] = UserStore.currentOrg;
|
||||
config.headers['cache-control'] = 'no-cache';
|
||||
config.headers.Pragma = 'no-cache';
|
||||
if (config?.isUpload) {
|
||||
config.headers['Content-Type'] = config.uploadType;
|
||||
}
|
||||
}
|
||||
if (config.method === 'POST' || config.method === 'DELETE') {
|
||||
config.headers.Accept = 'application/json';
|
||||
}
|
||||
return config;
|
||||
}, errorHandler);
|
||||
|
||||
/**
|
||||
* 返回结果处理
|
||||
* @param res
|
||||
* @returns
|
||||
*/
|
||||
const formatResult = (res) => {
|
||||
const code = res.data.code || res.status;
|
||||
switch (code) {
|
||||
case 200:
|
||||
case 0:
|
||||
// code === 0 或 200 代表没有错误
|
||||
return res.data || res.data.data || res;
|
||||
case 500:
|
||||
case 1:
|
||||
// code === 1 或 500 代表存在错误
|
||||
ElNotification.error(res.data.message);
|
||||
break;
|
||||
default:
|
||||
ElNotification.error(res.data.message);
|
||||
break;
|
||||
}
|
||||
};
|
||||
/**
|
||||
* 响应拦截器
|
||||
*/
|
||||
publicAxios.interceptors.response.use((response) => {
|
||||
const { config } = response;
|
||||
if (config?.responseType) {
|
||||
return response;
|
||||
}
|
||||
const token = response?.headers['fairies-auth-token'];
|
||||
if (!isEmpty(token)) {
|
||||
const UserStore = useUserStore();
|
||||
UserStore.setToken(token);
|
||||
}
|
||||
const result = formatResult(response);
|
||||
if (result) {
|
||||
return result;
|
||||
}
|
||||
throw new Error(response.data.message);
|
||||
}, errorHandler);
|
||||
|
||||
export default publicAxios;
|
353
sub-government-affairs-service/src/utils/index.js
Normal file
353
sub-government-affairs-service/src/utils/index.js
Normal file
@ -0,0 +1,353 @@
|
||||
/*
|
||||
* @Descripttion:
|
||||
* @Author: zenghua.wang
|
||||
* @Date: 2022-02-23 21:12:37
|
||||
* @LastEditors: wzh 1048523306@qq.com
|
||||
* @LastEditTime: 2024-09-09 18:46:56
|
||||
*/
|
||||
import lodash from 'lodash';
|
||||
import moment from 'moment';
|
||||
import { Base64 } from 'js-base64';
|
||||
|
||||
/**
|
||||
* @Title 防抖:指在一定时间内,多次触发同一个事件,只执行最后一次操作
|
||||
* @param {*} fn
|
||||
* @param {*} delay
|
||||
* @returns
|
||||
*/
|
||||
export function debounce(fn, delay) {
|
||||
let timer = null;
|
||||
return function (...args) {
|
||||
clearTimeout(timer);
|
||||
timer = setTimeout(() => {
|
||||
fn.apply(this, args);
|
||||
}, delay);
|
||||
};
|
||||
}
|
||||
/**
|
||||
* @Title 节流:指在一定时间内,多次触发同一个事件,只执行第一次操作
|
||||
* @param {*} fn
|
||||
* @param {*} delay
|
||||
* @returns
|
||||
*/
|
||||
export function throttle(fn, delay) {
|
||||
let timer = null;
|
||||
return function (...args) {
|
||||
if (!timer) {
|
||||
timer = setTimeout(() => {
|
||||
fn.apply(this, args);
|
||||
timer = null;
|
||||
}, delay);
|
||||
}
|
||||
};
|
||||
}
|
||||
/**
|
||||
* @Title 判断是否 empty,返回ture
|
||||
* @param {*} val:null 'null' undefined 'undefined' 0 '0' "" 返回true
|
||||
* @returns
|
||||
*/
|
||||
export const isEmpty = (val) => {
|
||||
if (val && parseInt(val) === 0) return false;
|
||||
if (typeof val === 'undefined' || val === 'null' || val == null || val === 'undefined' || val === undefined || val === '') {
|
||||
return true;
|
||||
} else if (typeof val === 'object' && Object.keys(val).length === 0) {
|
||||
return true;
|
||||
} else if (val instanceof Array && val.length === 0) {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
};
|
||||
/**
|
||||
* @Title 深度拷贝对象
|
||||
* @param {*} obj
|
||||
* @returns
|
||||
*/
|
||||
export const deepClone = (obj = {}) => {
|
||||
return lodash.cloneDeep(obj);
|
||||
};
|
||||
/**
|
||||
* @Title 将number转换为px
|
||||
* @param {*} val
|
||||
* @returns
|
||||
*/
|
||||
export const setPx = (val) => {
|
||||
if (isEmpty(val)) return '';
|
||||
val = val + '';
|
||||
if (val.indexOf('%') === -1) {
|
||||
val = val + 'px';
|
||||
}
|
||||
return val;
|
||||
};
|
||||
/**
|
||||
* @Tilte 设置属性默认值
|
||||
* @param {*} options
|
||||
* @param {*} prop
|
||||
* @param {*} defaultVal
|
||||
* @returns
|
||||
*/
|
||||
export const setDefaultOption = (options, prop, defaultVal) => {
|
||||
return options[prop] === undefined ? defaultVal : options.prop;
|
||||
};
|
||||
/**
|
||||
* 设置字典值
|
||||
* @param {*} columns
|
||||
* @param {*} key
|
||||
* @param {*} data
|
||||
* @returns
|
||||
*/
|
||||
export const setDicData = (columns, key, data = []) => {
|
||||
if (isEmpty(data)) return;
|
||||
const len = columns.length;
|
||||
for (let i = 0; i < len; i++) {
|
||||
if (columns[i]?.prop === key) {
|
||||
columns[i]['dicData'] = data;
|
||||
break;
|
||||
}
|
||||
}
|
||||
};
|
||||
/**
|
||||
* 求字段lable
|
||||
* @param {*} tree
|
||||
* @returns
|
||||
*/
|
||||
export const setDicLabel = (dicData, value) => {
|
||||
let label = value;
|
||||
if (isEmpty(dicData)) return label;
|
||||
const len = dicData.length;
|
||||
for (let i = 0; i < len; i++) {
|
||||
if (dicData[i]?.value === value) {
|
||||
label = dicData[i].label;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return label;
|
||||
};
|
||||
/**
|
||||
* 加密
|
||||
* @param {*} n
|
||||
* @returns
|
||||
*/
|
||||
export const encode = (n, flag = false) => {
|
||||
if (flag) {
|
||||
return (
|
||||
((e) => {
|
||||
let t = e.length.toString();
|
||||
for (let n = 10 - t.length; n > 0; n--) t = '0' + t;
|
||||
return t;
|
||||
})(n) +
|
||||
((e) => {
|
||||
const t = Base64.encode(e).split('');
|
||||
for (let n = 0; n < Math.floor(e.length / 100 + 1); n++) t.splice(100 * n + 1, 0, 3);
|
||||
return t.join('');
|
||||
})(n)
|
||||
);
|
||||
}
|
||||
return n;
|
||||
};
|
||||
/**
|
||||
* 解密
|
||||
* @param {*} e
|
||||
* @returns
|
||||
*/
|
||||
export const decode = (e, flag = false) => {
|
||||
if (flag) {
|
||||
try {
|
||||
const t = Number(e.substr(0, 10));
|
||||
const n = e.substr(10).split('');
|
||||
for (let i = 0, s = 0; s < Math.floor(t / 100) + 1; s++) {
|
||||
n.splice(100 * s + 1 - i, 1);
|
||||
i++;
|
||||
}
|
||||
const o = Base64.decode(n.join(''));
|
||||
return o;
|
||||
} catch (error) {
|
||||
return e;
|
||||
}
|
||||
}
|
||||
return e;
|
||||
};
|
||||
/**
|
||||
* @Title 图片转base64
|
||||
* @param {*} file
|
||||
* @returns
|
||||
*/
|
||||
export const imageToBase64 = (file) => {
|
||||
return new Promise((resolve, reject) => {
|
||||
const reader = new FileReader();
|
||||
reader.readAsDataURL(file);
|
||||
reader.onload = () => {
|
||||
resolve(reader.result);
|
||||
};
|
||||
reader.onerror = reject;
|
||||
});
|
||||
};
|
||||
/**
|
||||
* @Title bufferToBase64
|
||||
* @param {*} buffer
|
||||
* @returns
|
||||
*/
|
||||
export const bufferToBase64 = (buffer) => {
|
||||
return 'data:image/jpeg;base64,' + window.btoa(new Uint8Array(buffer).reduce((data, byte) => data + String.fromCharCode(byte), ''));
|
||||
};
|
||||
/**
|
||||
* @Title 将array转化为树
|
||||
* @param tree
|
||||
* @returns
|
||||
*/
|
||||
export const getTree = (tree = []) => {
|
||||
tree.forEach((item) => {
|
||||
delete item.children;
|
||||
});
|
||||
const map = {};
|
||||
tree.forEach((item) => {
|
||||
map[item.id] = item;
|
||||
});
|
||||
const arr = [];
|
||||
tree.forEach((item) => {
|
||||
const parent = map[item.parentId];
|
||||
if (parent) {
|
||||
(parent.children || (parent.children = [])).push(item);
|
||||
} else {
|
||||
arr.push(item);
|
||||
}
|
||||
});
|
||||
return arr;
|
||||
};
|
||||
/**
|
||||
* @Title 获取路由中的参数
|
||||
* @param name
|
||||
* @returns
|
||||
*/
|
||||
export const getUrlQuery = (name) => {
|
||||
const url = window.location.href;
|
||||
const hash = url.substring(url.indexOf('#') + 1);
|
||||
const searchIndex = hash.indexOf('?');
|
||||
const search = searchIndex !== -1 ? hash.substring(searchIndex + 1) : '';
|
||||
const usp = new URLSearchParams(search);
|
||||
return usp.get(name);
|
||||
};
|
||||
/**
|
||||
* @Title 获取静态资源文件
|
||||
* @param {*} url
|
||||
* @returns
|
||||
*/
|
||||
export const getAssetsFile = (url) => {
|
||||
return new URL(`../assets/images/${url}`, import.meta.url);
|
||||
};
|
||||
/**
|
||||
* @Title 替换图片url字段值
|
||||
* @param {*} url
|
||||
* @returns
|
||||
*/
|
||||
export const setUploadField = (url) => {
|
||||
if (isEmpty(url) || url.includes('http')) return null;
|
||||
return url;
|
||||
};
|
||||
/**
|
||||
* 合并列
|
||||
* @param {*} param0
|
||||
* @param {*} options
|
||||
* @param {*} rowList
|
||||
* @returns
|
||||
*/
|
||||
export const setSpan = ({ rowIndex, columnIndex }, options, rowList) => {
|
||||
const columnList = deepClone(options.columns).filter((item) => !item.hide);
|
||||
const conditions = [];
|
||||
const getColspan = (column, conditions) => {
|
||||
const len = rowList.length;
|
||||
const arr = [];
|
||||
for (let i = 0; i < len; i++) {
|
||||
let colspan = 1;
|
||||
for (let j = i + 1; j < len; j++) {
|
||||
const bool = conditions.every((col) => {
|
||||
return rowList[i][col] === rowList[j][col];
|
||||
});
|
||||
if (bool && rowList[i][column] === rowList[j][column]) {
|
||||
colspan += 1;
|
||||
arr[i] = colspan;
|
||||
arr[j] = 0;
|
||||
if (j === len - 1) i = j;
|
||||
} else {
|
||||
colspan = 1;
|
||||
if (!arr[i]) arr[i] = colspan;
|
||||
i = j;
|
||||
arr[j] = colspan;
|
||||
}
|
||||
}
|
||||
}
|
||||
return arr;
|
||||
};
|
||||
if (rowList.length <= 1) {
|
||||
return {
|
||||
rowspan: 1,
|
||||
colspan: 1,
|
||||
};
|
||||
} else {
|
||||
for (let i = 0, columnLen = columnList.length; i < columnLen; i++) {
|
||||
if (columnList[i].isColspan) {
|
||||
const arr = getColspan(columnList[i].prop, conditions);
|
||||
const index = options.selection ? (options.index ? i + 2 : i + 1) : options.index ? i + 1 : i;
|
||||
conditions.push(columnList[i].prop);
|
||||
if (columnIndex === index) {
|
||||
for (let j = 0, rowLen = rowList.length; j < rowLen; j++) {
|
||||
if (rowIndex === j) {
|
||||
return {
|
||||
rowspan: arr[j],
|
||||
colspan: arr[j] > 0 ? 1 : 0,
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
/**
|
||||
* @Title: a链接方式文件下载
|
||||
* @param {void} content:
|
||||
* @param {void} fileName:
|
||||
* @return {void}
|
||||
*/
|
||||
export const dowloadLink = (content, fileName) => {
|
||||
const blob = new Blob([content]);
|
||||
if ('download' in document.createElement('a')) {
|
||||
const elink = document.createElement('a');
|
||||
elink.download = fileName;
|
||||
elink.style.display = 'none';
|
||||
elink.href = URL.createObjectURL(blob);
|
||||
document.body.appendChild(elink);
|
||||
elink.click();
|
||||
URL.revokeObjectURL(elink.href);
|
||||
document.body.removeChild(elink);
|
||||
} else {
|
||||
navigator.msSaveBlob(blob, fileName);
|
||||
}
|
||||
};
|
||||
/**
|
||||
* @Title 模拟休眠
|
||||
* @param {*} duration
|
||||
* @returns
|
||||
*/
|
||||
export const sleep = (duration = 0) =>
|
||||
new Promise((resolve) => {
|
||||
setTimeout(resolve, duration);
|
||||
});
|
||||
/**
|
||||
* 日期格式化
|
||||
* @param {*} date
|
||||
* @param {*} format
|
||||
* @returns
|
||||
*/
|
||||
export const dateFormat = (datetime, type = 'yyyy-MM-dd') => {
|
||||
return moment(datetime).format(type);
|
||||
};
|
||||
/**
|
||||
* 上日/周/月/年
|
||||
*/
|
||||
export const lastDate = (last = 0, date = 'month', type = 'yyyy-MM-dd') => {
|
||||
if (date === 'day') {
|
||||
return moment().subtract(last, 'day').endOf('day').format(type);
|
||||
}
|
||||
return moment().subtract(last, date).format(type);
|
||||
};
|
58
sub-government-affairs-service/src/utils/permission.js
Normal file
58
sub-government-affairs-service/src/utils/permission.js
Normal file
@ -0,0 +1,58 @@
|
||||
/**
|
||||
* @Description: 路由权限
|
||||
* @Author: zenghua.wang
|
||||
* @Date: 2022-01-26 22:04:31
|
||||
* @LastEditors: zenghua.wang
|
||||
* @LastEditTime: 2024-02-26 13:54:43
|
||||
*/
|
||||
import NProgress from 'nprogress';
|
||||
import 'nprogress/nprogress.css';
|
||||
import router from '@/router';
|
||||
import { useUserStore } from '@/store/modules/user';
|
||||
import { usePermissionStore } from '@/store/modules/permission';
|
||||
|
||||
NProgress.configure({ showSpinner: false });
|
||||
|
||||
const whiteList = ['/login', '/auth-redirect']; // 设置白名单
|
||||
|
||||
router.beforeEach(async (to, from, next) => {
|
||||
NProgress.start();
|
||||
if (typeof to.meta.title === 'string') {
|
||||
document.title = '政务服务 | ' + to.meta.title;
|
||||
}
|
||||
|
||||
const userStore = useUserStore();
|
||||
const hasToken = true;
|
||||
|
||||
if (hasToken) {
|
||||
if (to.path === '/login') {
|
||||
// 如果已登录,请重定向到主页
|
||||
next({ path: '/' });
|
||||
} else {
|
||||
try {
|
||||
const PermissionStore = usePermissionStore();
|
||||
// 路由添加进去了没有及时更新 需要重新进去一次拦截
|
||||
if (!PermissionStore.routes.length) {
|
||||
// 获取权限列表进行接口访问 因为这里页面要切换权限
|
||||
const accessRoutes = await PermissionStore.generateRoutes(userStore.roles);
|
||||
accessRoutes.forEach((item) => router.addRoute(item)); // 动态添加访问路由表
|
||||
next({ ...to, replace: true }); // 这里相当于push到一个页面 不在进入路由拦截
|
||||
} else {
|
||||
next(); // 如果不传参数就会重新执行路由拦截,重新进到这里
|
||||
}
|
||||
} catch (error) {
|
||||
next(`/login?redirect=${to.path}`);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (whiteList.indexOf(to.path) !== -1) {
|
||||
next();
|
||||
} else {
|
||||
next(`/login?redirect=${to.path}`);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
router.afterEach(() => {
|
||||
NProgress.done();
|
||||
});
|
111
sub-government-affairs-service/src/utils/router.js
Normal file
111
sub-government-affairs-service/src/utils/router.js
Normal file
@ -0,0 +1,111 @@
|
||||
/**
|
||||
* @Description: 路由方法
|
||||
* @Author: zenghua.wang
|
||||
* @Date: 2022-01-26 21:55:58
|
||||
* @LastEditors: zenghua.wang
|
||||
* @LastEditTime: 2024-04-13 21:51:35
|
||||
*/
|
||||
import path from 'path-browserify';
|
||||
import Layout from '@/layouts/index.vue';
|
||||
import Views from '@/layouts/Views.vue';
|
||||
import { isEmpty } from './index';
|
||||
|
||||
const modules = import.meta.glob('../views/**/**.vue');
|
||||
|
||||
/**
|
||||
* 创建路由菜单
|
||||
* @param {*} menus
|
||||
*/
|
||||
export function createAsyncRoutes(menus = [], isLayout = true) {
|
||||
if (isEmpty(menus)) return menus;
|
||||
const res = [];
|
||||
menus.forEach((menu) => {
|
||||
const tmp = {
|
||||
id: menu.id,
|
||||
parentId: menu.parentId,
|
||||
path: menu.path,
|
||||
component: isEmpty(menu.component)
|
||||
? isLayout
|
||||
? Layout
|
||||
: Views
|
||||
: modules[/* @vite-ignore */ `../views/${menu.component.replace('/views/', '')}`],
|
||||
redirect: menu.redirect,
|
||||
name: menu.name,
|
||||
meta: {
|
||||
title: menu.title,
|
||||
icon: menu?.icon || 'icon-demo',
|
||||
keepAlive: menu.keepAlive,
|
||||
},
|
||||
children: menu.children,
|
||||
};
|
||||
if (tmp.children) {
|
||||
tmp.children = createAsyncRoutes(tmp.children, false);
|
||||
}
|
||||
res.push(tmp);
|
||||
});
|
||||
return res;
|
||||
}
|
||||
|
||||
/**
|
||||
* 通过递归过滤异步路由表
|
||||
* @param routes asyncRoutes
|
||||
* @param roles
|
||||
*/
|
||||
export function filterAsyncRoutes(routes, roles) {
|
||||
const res = [];
|
||||
routes.forEach((route) => {
|
||||
const tmp = { ...route };
|
||||
if (hasPermission(roles, tmp)) {
|
||||
if (tmp.children) {
|
||||
tmp.children = filterAsyncRoutes(tmp.children, roles);
|
||||
}
|
||||
res.push(tmp);
|
||||
}
|
||||
});
|
||||
return res;
|
||||
}
|
||||
|
||||
/**
|
||||
* 使用 meta.role 来确定当前用户是否具有权限
|
||||
* @param roles
|
||||
* @param route
|
||||
*/
|
||||
export function hasPermission(roles, route) {
|
||||
if (route.meta && route.meta.roles) {
|
||||
return roles.some((role) => route.meta.roles.includes(role));
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 使用递归,过滤需要缓存的路由
|
||||
* @param {*} routers
|
||||
* @returns
|
||||
*/
|
||||
export function filterKeepAlive(routers) {
|
||||
const cacheRouter = [];
|
||||
const loop = (routers) => {
|
||||
routers.forEach((item) => {
|
||||
if (item.meta?.keepAlive && item.name) {
|
||||
cacheRouter.push(item.name);
|
||||
}
|
||||
if (item.children && item.children.length) {
|
||||
loop(item.children);
|
||||
}
|
||||
});
|
||||
};
|
||||
loop(routers);
|
||||
return cacheRouter;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {*} routers
|
||||
* @param {*} pathUrl
|
||||
*/
|
||||
export function handleRoutes(routers, pathUrl = '') {
|
||||
routers.forEach((item) => {
|
||||
item.path = path.resolve(pathUrl, item.path);
|
||||
});
|
||||
}
|
184
sub-government-affairs-service/src/utils/validate.js
Normal file
184
sub-government-affairs-service/src/utils/validate.js
Normal file
@ -0,0 +1,184 @@
|
||||
/**
|
||||
* @Description: 验证
|
||||
* @Author: zenghua.wang
|
||||
* @Date: 2022-01-25 21:08:52
|
||||
* @LastEditors: zenghua.wang
|
||||
* @LastEditTime: 2024-01-26 22:22:58
|
||||
*/
|
||||
/**
|
||||
* @param {string} path
|
||||
* @returns {Boolean}
|
||||
*/
|
||||
export function isExternal(path) {
|
||||
return /^(https?:|mailto:|tel:)/.test(path);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} str
|
||||
* @returns {Boolean}
|
||||
*/
|
||||
export function validUsername(str) {
|
||||
const valid_map = ['admin', 'editor'];
|
||||
return valid_map.indexOf(str.trim()) >= 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} url
|
||||
* @returns {Boolean}
|
||||
*/
|
||||
export function validURL(url) {
|
||||
const reg =
|
||||
/^(https?|ftp):\/\/([a-zA-Z0-9.-]+(:[a-zA-Z0-9.&%$-]+)*@)*((25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9][0-9]?)(\.(25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9]?[0-9])){3}|([a-zA-Z0-9-]+\.)*[a-zA-Z0-9-]+\.(com|edu|gov|int|mil|net|org|biz|arpa|info|name|pro|aero|coop|museum|[a-zA-Z]{2}))(:[0-9]+)*(\/($|[a-zA-Z0-9.,?'\\+&%$#=~_-]+))*$/;
|
||||
return reg.test(url);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} str
|
||||
* @returns {Boolean}
|
||||
*/
|
||||
export function validLowerCase(str) {
|
||||
const reg = /^[a-z]+$/;
|
||||
return reg.test(str);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} str
|
||||
* @returns {Boolean}
|
||||
*/
|
||||
export function validUpperCase(str) {
|
||||
const reg = /^[A-Z]+$/;
|
||||
return reg.test(str);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} str
|
||||
* @returns {Boolean}
|
||||
*/
|
||||
export function validAlphabets(str) {
|
||||
const reg = /^[A-Za-z]+$/;
|
||||
return reg.test(str);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} email
|
||||
* @returns {Boolean}
|
||||
*/
|
||||
export function validEmail(email) {
|
||||
const reg =
|
||||
/^(([^<>()[\]\\.,;:\s@"]+(\.[^<>()[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/;
|
||||
return reg.test(email);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} str
|
||||
* @returns {Boolean}
|
||||
*/
|
||||
export function isString(str) {
|
||||
if (typeof str === 'string' || str instanceof String) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {Array} arg
|
||||
* @returns {Boolean}
|
||||
*/
|
||||
export function isArray(arg) {
|
||||
if (typeof Array.isArray === 'undefined') {
|
||||
return Object.prototype.toString.call(arg) === '[object Array]';
|
||||
}
|
||||
return Array.isArray(arg);
|
||||
}
|
||||
|
||||
/**
|
||||
* 手机号码
|
||||
* @param val 当前值字符串
|
||||
* @returns 返回 true: 手机号码正确
|
||||
*/
|
||||
export function verifyPhone(val) {
|
||||
// false: 手机号码不正确
|
||||
if (!/^((12[0-9])|(13[0-9])|(14[5|7])|(15([0-3]|[5-9]))|(18[0|1,5-9]))\d{8}$/.test(val)) return false;
|
||||
// true: 手机号码正确
|
||||
else return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* 匹配文字变色(搜索时)
|
||||
* @param val 当前值字符串
|
||||
* @param text 要处理的字符串值
|
||||
* @param color 搜索到时字体高亮颜色
|
||||
* @returns 返回处理后的字符串
|
||||
*/
|
||||
export function verifyTextColor(val, text = '', color = 'red') {
|
||||
// 返回内容,添加颜色
|
||||
const v = text.replace(new RegExp(val, 'gi'), `<span style='color: ${color}'>${val}</span>`);
|
||||
// 返回结果
|
||||
return v;
|
||||
}
|
||||
|
||||
/**
|
||||
* 身份证号, 支持1/2代(15位/18位数字)
|
||||
* @param val 当前值字符串
|
||||
* @returns 返回 true: 身份证正确
|
||||
*/
|
||||
export function verifyIdCard(val) {
|
||||
const regx = /(^\d{8}(0\d|10|11|12)([0-2]\d|30|31)\d{3}$)|(^\d{6}(18|19|20)\d{2}(0\d|10|11|12)([0-2]\d|30|31)\d{3}(\d|X|x)$)/;
|
||||
return regx.test(val);
|
||||
}
|
||||
|
||||
/**
|
||||
* 网址
|
||||
* @param val 当前值字符串
|
||||
* @returns 返回 true: 网址正确
|
||||
*/
|
||||
export function verifyWebsite(val) {
|
||||
const regx = /^((https?|ftp):\/\/)?([\da-z.-]+)\.([a-z.]{2,6})(\/\w\.-]*)*\/?/;
|
||||
return regx.test(val);
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否html标签
|
||||
* @param val 当前值字符串
|
||||
* @returns 返回 true: 是否html标签
|
||||
*/
|
||||
export function verifyHtml(val) {
|
||||
const regx = /<(.*)>.*<\/\1>|<(.*) \/>/;
|
||||
return regx.test(val);
|
||||
}
|
||||
|
||||
/**
|
||||
* 日期
|
||||
* @param val 当前值字符串
|
||||
* @returns 返回 true: 是否日期
|
||||
*/
|
||||
export function verifyDate(val) {
|
||||
const regx =
|
||||
/^(?:(?!0000)[0-9]{4}-(?:(?:0[1-9]|1[0-2])-(?:0[1-9]|1[0-9]|2[0-8])|(?:0[13-9]|1[0-2])-(?:29|30)|(?:0[13578]|1[02])-31)|(?:[0-9]{2}(?:0[48]|[2468][048]|[13579][26])|(?:0[48]|[2468][048]|[13579][26])00)-02-29)$/;
|
||||
return regx.test(val);
|
||||
}
|
||||
|
||||
/**
|
||||
* 邮箱
|
||||
* @param val 当前值字符串
|
||||
* @returns 返回 true: 邮箱是否正确
|
||||
*/
|
||||
export function verifyEmail(val) {
|
||||
const regx = /^[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$/;
|
||||
return regx.test(val);
|
||||
}
|
||||
|
||||
/**
|
||||
* 验证校验器函数封装
|
||||
* @param verifyPhone 验证函数
|
||||
* @param message 提示
|
||||
*/
|
||||
export function validatorMethod(verifyPhone, message) {
|
||||
return (rule, value, callback) => {
|
||||
if (!verifyPhone(value)) {
|
||||
callback(new Error(message));
|
||||
} else {
|
||||
callback();
|
||||
}
|
||||
};
|
||||
}
|
12
sub-government-affairs-service/src/views/error/403.vue
Normal file
12
sub-government-affairs-service/src/views/error/403.vue
Normal file
@ -0,0 +1,12 @@
|
||||
<template>
|
||||
<el-result icon="error" title="403" sub-title="Sorry, request error">
|
||||
<template #extra>
|
||||
<el-button type="primary" @click="push('/')">返回</el-button>
|
||||
</template>
|
||||
</el-result>
|
||||
</template>
|
||||
|
||||
<script setup name="error">
|
||||
import { useRouter } from 'vue-router';
|
||||
const { push } = useRouter();
|
||||
</script>
|
12
sub-government-affairs-service/src/views/error/404.vue
Normal file
12
sub-government-affairs-service/src/views/error/404.vue
Normal file
@ -0,0 +1,12 @@
|
||||
<template>
|
||||
<el-result icon="error" title="404" sub-title="Sorry, request error">
|
||||
<template #extra>
|
||||
<el-button type="primary" @click="push('/')">返回</el-button>
|
||||
</template>
|
||||
</el-result>
|
||||
</template>
|
||||
|
||||
<script setup name="error">
|
||||
import { useRouter } from 'vue-router';
|
||||
const { push } = useRouter();
|
||||
</script>
|
6
sub-government-affairs-service/src/views/home/index.vue
Normal file
6
sub-government-affairs-service/src/views/home/index.vue
Normal file
@ -0,0 +1,6 @@
|
||||
<template>
|
||||
<div style="width: 100%; height: 100%">
|
||||
<div>政务服务</div>
|
||||
</div>
|
||||
</template>
|
||||
<script setup name="home"></script>
|
195
sub-government-affairs-service/src/views/resource/land/index.vue
Normal file
195
sub-government-affairs-service/src/views/resource/land/index.vue
Normal file
@ -0,0 +1,195 @@
|
||||
<template>
|
||||
<div class="custom-table">
|
||||
<avue-crud
|
||||
v-model="state.form"
|
||||
v-model:search="state.query"
|
||||
v-model:page="state.page"
|
||||
:table-loading="state.loading"
|
||||
:data="state.data"
|
||||
:option="state.options"
|
||||
@refresh-change="refreshChange"
|
||||
@search-reset="searchChange"
|
||||
@search-change="searchChange"
|
||||
@row-click="rowClick"
|
||||
@row-save="rowSave"
|
||||
@row-update="rowUpdate"
|
||||
@row-del="rowDel"
|
||||
></avue-crud>
|
||||
</div>
|
||||
</template>
|
||||
<script setup>
|
||||
import { reactive } from 'vue';
|
||||
import { useApp } from '@/hooks';
|
||||
import { sleep } from '@/utils';
|
||||
|
||||
import Mock from 'mockjs';
|
||||
const res = Mock.mock({
|
||||
'data|20': [
|
||||
{
|
||||
id: '@increment(100000)',
|
||||
name: '@ctitle(5,10)',
|
||||
'area|100-1000': 100,
|
||||
type: '耕地',
|
||||
tag: '龙津河周边',
|
||||
address: '耿马镇白塔社区',
|
||||
createTime: '@datetime("yyyy-MM-dd HH:mm:ss")',
|
||||
updateTime: '@datetime("yyyy-MM-dd HH:mm:ss")',
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
const app = useApp();
|
||||
const state = reactive({
|
||||
loading: false,
|
||||
query: {},
|
||||
form: {},
|
||||
options: {
|
||||
selection: true,
|
||||
align: 'center',
|
||||
headerAlign: 'center',
|
||||
gridBtn: false,
|
||||
addBtn: true,
|
||||
viewBtn: true,
|
||||
editBtn: true,
|
||||
delBtn: false,
|
||||
gutter: 20,
|
||||
labelWidth: 150,
|
||||
column: [
|
||||
{
|
||||
label: '土地编号',
|
||||
prop: 'id',
|
||||
fixed: true,
|
||||
display: false,
|
||||
},
|
||||
{
|
||||
label: '土地名称',
|
||||
prop: 'name',
|
||||
fixed: true,
|
||||
search: true,
|
||||
rules: {
|
||||
required: true,
|
||||
message: '请输入',
|
||||
trigger: 'blur',
|
||||
},
|
||||
},
|
||||
{
|
||||
label: '土地面积',
|
||||
prop: 'area',
|
||||
rules: {
|
||||
required: true,
|
||||
message: '请输入',
|
||||
trigger: 'blur',
|
||||
},
|
||||
},
|
||||
{
|
||||
label: '土地类型',
|
||||
prop: 'type',
|
||||
},
|
||||
{
|
||||
label: '坐落位置',
|
||||
prop: 'tag',
|
||||
type: 'textarea',
|
||||
width: 200,
|
||||
span: 24,
|
||||
rows: 4,
|
||||
overHidden: true,
|
||||
rules: {
|
||||
required: true,
|
||||
message: '请输入',
|
||||
trigger: 'blur',
|
||||
},
|
||||
},
|
||||
{
|
||||
label: '区域',
|
||||
prop: 'address',
|
||||
type: 'textarea',
|
||||
width: 200,
|
||||
span: 24,
|
||||
rows: 4,
|
||||
overHidden: true,
|
||||
rules: {
|
||||
required: true,
|
||||
message: '请输入',
|
||||
trigger: 'blur',
|
||||
},
|
||||
},
|
||||
{
|
||||
label: '信息录入时间',
|
||||
prop: 'createTime',
|
||||
type: 'datetime',
|
||||
format: 'YYYY-MM-DD HH:mm:ss',
|
||||
valueFormat: 'YYYY-MM-DD HH:mm:ss',
|
||||
width: 200,
|
||||
display: false,
|
||||
},
|
||||
{
|
||||
label: '信息更新时间',
|
||||
prop: 'updateTime',
|
||||
type: 'datetime',
|
||||
format: 'YYYY-MM-DD HH:mm:ss',
|
||||
valueFormat: 'YYYY-MM-DD HH:mm:ss',
|
||||
width: 200,
|
||||
display: false,
|
||||
},
|
||||
],
|
||||
},
|
||||
page: {
|
||||
total: 0,
|
||||
currentPage: 1,
|
||||
pageSize: 10,
|
||||
},
|
||||
data: [],
|
||||
});
|
||||
|
||||
const loadData = async () => {
|
||||
state.loading = true;
|
||||
await sleep(500);
|
||||
state.data = res.data;
|
||||
state.page = {
|
||||
total: 20,
|
||||
currentPage: 1,
|
||||
pageSize: 10,
|
||||
};
|
||||
state.loading = false;
|
||||
};
|
||||
|
||||
loadData();
|
||||
|
||||
// 搜索
|
||||
const searchChange = (params, done) => {
|
||||
console.log('search==', params);
|
||||
if (done) done();
|
||||
state.query = params;
|
||||
state.page.currentPage = 1;
|
||||
loadData();
|
||||
app.$message.success('搜索成功');
|
||||
};
|
||||
|
||||
// 刷新
|
||||
const refreshChange = () => {
|
||||
loadData();
|
||||
app.$message.success('刷新成功');
|
||||
};
|
||||
|
||||
// row
|
||||
const rowClick = (row, event, column) => {
|
||||
app.$message.success(JSON.stringify(row));
|
||||
};
|
||||
|
||||
// 新增
|
||||
const rowSave = (row, done, loading) => {
|
||||
console.log('add=', row);
|
||||
};
|
||||
|
||||
// 编辑
|
||||
const rowUpdate = (row, index, done, loading) => {
|
||||
console.log('update=', row);
|
||||
done(row);
|
||||
};
|
||||
|
||||
// 删除
|
||||
const rowDel = (row, index, done) => {
|
||||
console.log('del=', row);
|
||||
done(row);
|
||||
};
|
||||
</script>
|
@ -0,0 +1,6 @@
|
||||
<template>
|
||||
<div style="width: 100%; height: 100%">
|
||||
<div>土地资源一张图</div>
|
||||
</div>
|
||||
</template>
|
||||
<script setup name="home"></script>
|
116
sub-government-affairs-service/vite.config.js
Normal file
116
sub-government-affairs-service/vite.config.js
Normal file
@ -0,0 +1,116 @@
|
||||
/*
|
||||
* @Descripttion:
|
||||
* @Author: zenghua.wang
|
||||
* @Date: 2022-09-18 21:24:29
|
||||
* @LastEditors: zenghua.wang “1048523306@qq.com”
|
||||
* @LastEditTime: 2025-01-21 14:11:58
|
||||
*/
|
||||
|
||||
import { defineConfig, loadEnv } from 'vite';
|
||||
import vue from '@vitejs/plugin-vue';
|
||||
import qiankun from 'vite-plugin-qiankun';
|
||||
import eslintPlugin from 'vite-plugin-eslint';
|
||||
import vueSetupExtend from 'vite-plugin-vue-setup-extend';
|
||||
import { createSvgIconsPlugin } from 'vite-plugin-svg-icons';
|
||||
import compression from 'vite-plugin-compression';
|
||||
import { viteMockServe } from 'vite-plugin-mock';
|
||||
import AutoImport from 'unplugin-auto-import/vite';
|
||||
import Components from 'unplugin-vue-components/vite';
|
||||
import postcssImport from 'postcss-import';
|
||||
import autoprefixer from 'autoprefixer';
|
||||
import { resolve } from 'path';
|
||||
|
||||
const useDevMode = true;
|
||||
|
||||
export default defineConfig(({ command, mode }) => {
|
||||
console.log('vite.config.js', command, mode, loadEnv(mode, process.cwd()));
|
||||
const { VITE_PORT, VITE_APP_NAME, VITE_APP_BASE_API, VITE_APP_BASE_URL, VITE_APP_UPLOAD_API, VITE_APP_UPLOAD_URL } = loadEnv(mode, process.cwd());
|
||||
const config = {
|
||||
base: './',
|
||||
build: {
|
||||
target: 'ESNext',
|
||||
outDir: 'dist',
|
||||
minify: 'terser',
|
||||
},
|
||||
server: {
|
||||
host: '0.0.0.0',
|
||||
port: VITE_PORT,
|
||||
open: true,
|
||||
https: false,
|
||||
headers: {
|
||||
'Access-Control-Allow-Origin': '*',
|
||||
},
|
||||
proxy: {
|
||||
[VITE_APP_BASE_API]: {
|
||||
target: VITE_APP_BASE_URL,
|
||||
changeOrigin: true,
|
||||
rewrite: (path) => path.replace(/^\/apis/, ''),
|
||||
},
|
||||
[VITE_APP_UPLOAD_API]: {
|
||||
target: VITE_APP_UPLOAD_URL,
|
||||
changeOrigin: true,
|
||||
rewrite: (path) => path.replace(/^\/uploadApis/, ''),
|
||||
},
|
||||
},
|
||||
},
|
||||
resolve: {
|
||||
alias: {
|
||||
'@': resolve(__dirname, 'src'),
|
||||
},
|
||||
extensions: ['.js', '.vue', '.json', '.ts'],
|
||||
},
|
||||
css: {
|
||||
preprocessorOptions: {
|
||||
scss: {
|
||||
additionalData: '@import "@/styles/global.scss";',
|
||||
api: 'modern-compiler',
|
||||
},
|
||||
},
|
||||
postcss: {
|
||||
plugins: [
|
||||
postcssImport,
|
||||
autoprefixer({
|
||||
overrideBrowserslist: ['> 1%', 'last 2 versions'],
|
||||
}),
|
||||
],
|
||||
},
|
||||
},
|
||||
plugins: [
|
||||
vue(),
|
||||
qiankun(VITE_APP_NAME, { useDevMode }),
|
||||
vueSetupExtend(),
|
||||
eslintPlugin({
|
||||
include: ['src/**/*.ts', 'src/**/*.vue', 'src/*.ts', 'src/*.vue'],
|
||||
}),
|
||||
Components({
|
||||
dirs: ['src/components'],
|
||||
extensions: ['vue', 'js', 'jsx', 'ts', 'tsx'],
|
||||
resolvers: [],
|
||||
}),
|
||||
compression(),
|
||||
AutoImport({
|
||||
include: [/\.[tj]s?$/, /\.vue$/],
|
||||
imports: ['vue', 'vue-router'],
|
||||
}),
|
||||
createSvgIconsPlugin({
|
||||
iconDirs: [resolve(process.cwd(), 'src/assets/svgs')],
|
||||
symbolId: 'icon-[name]',
|
||||
}),
|
||||
viteMockServe({
|
||||
mockPath: 'src/mock',
|
||||
watchFiles: true,
|
||||
localEnabled: command === 'dev',
|
||||
prodEnabled: false,
|
||||
}),
|
||||
],
|
||||
};
|
||||
if (mode === 'production') {
|
||||
config.build.terserOptions = {
|
||||
compress: {
|
||||
drop_console: true,
|
||||
drop_debugger: true,
|
||||
},
|
||||
};
|
||||
}
|
||||
return config;
|
||||
});
|
6682
sub-government-affairs-service/yarn.lock
Normal file
6682
sub-government-affairs-service/yarn.lock
Normal file
File diff suppressed because it is too large
Load Diff
Loading…
x
Reference in New Issue
Block a user