init
|
@ -0,0 +1,4 @@
|
|||
VITE_BASE_URL=http://192.168.0.17:8001/
|
||||
VITE_PROXY=/api
|
||||
VITE_FILE_URL=https://file.zcloudchina.com/YTHFile
|
||||
|
|
@ -0,0 +1,4 @@
|
|||
public
|
||||
dist
|
||||
package.json
|
||||
!.prettierrc.cjs
|
|
@ -0,0 +1,34 @@
|
|||
module.exports = {
|
||||
env: {
|
||||
browser: true,
|
||||
es2021: true,
|
||||
node: true
|
||||
},
|
||||
extends: [
|
||||
"plugin:vue/vue3-essential",
|
||||
"standard",
|
||||
"@vue/prettier",
|
||||
"eslint:recommended"
|
||||
],
|
||||
parserOptions: {
|
||||
ecmaVersion: "latest",
|
||||
sourceType: "module"
|
||||
},
|
||||
plugins: ["vue"],
|
||||
rules: {
|
||||
"no-console": "warn",
|
||||
"vue/multi-word-component-names": "off",
|
||||
camelcase: "off",
|
||||
eqeqeq: "error",
|
||||
"vue/eqeqeq": "error",
|
||||
"no-unused-vars": [
|
||||
"error",
|
||||
{ vars: "all", args: "after-used", ignoreRestSiblings: false }
|
||||
],
|
||||
"linebreak-style": ["off",'windows'],
|
||||
},
|
||||
globals: {
|
||||
BMapGL: "readonly",
|
||||
JSEncrypt: "readonly",
|
||||
}
|
||||
};
|
|
@ -0,0 +1,24 @@
|
|||
# Logs
|
||||
logs
|
||||
*.log
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
pnpm-debug.log*
|
||||
lerna-debug.log*
|
||||
|
||||
node_modules
|
||||
dist
|
||||
dist-ssr
|
||||
*.local
|
||||
|
||||
# Editor directories and files
|
||||
.vscode/*
|
||||
!.vscode/extensions.json
|
||||
.idea
|
||||
.DS_Store
|
||||
*.suo
|
||||
*.ntvs*
|
||||
*.njsproj
|
||||
*.sln
|
||||
*.sw?
|
|
@ -0,0 +1,4 @@
|
|||
module.exports = {
|
||||
extends: ["@vue/prettier", "plugin:prettier/recommended"],
|
||||
endOfLine: "crlf",
|
||||
};
|
|
@ -0,0 +1,7 @@
|
|||
# Vue 3 + Vite
|
||||
|
||||
This template should help get you started developing with Vue 3 in Vite. The template uses Vue 3 `<script setup>` SFCs, check out the [script setup docs](https://v3.vuejs.org/api/sfc-script-setup.html#sfc-script-setup) to learn more.
|
||||
|
||||
## Recommended IDE Setup
|
||||
|
||||
- [VS Code](https://code.visualstudio.com/) + [Volar](https://marketplace.visualstudio.com/items?itemName=Vue.volar) (and disable Vetur) + [TypeScript Vue Plugin (Volar)](https://marketplace.visualstudio.com/items?itemName=Vue.vscode-typescript-vue-plugin).
|
|
@ -0,0 +1,19 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0, user-scalable=no" />
|
||||
<title>管理平台</title>
|
||||
</head>
|
||||
<body>
|
||||
<div id="app"></div>
|
||||
<noscript>
|
||||
<strong>很抱歉,如果没有启用JavaScript,网站无法正常工作,请启用JavaScript使其正常工作。</strong>
|
||||
</noscript>
|
||||
<script type="text/javascript"
|
||||
src="https://api.map.baidu.com/api?v=1.0&type=webgl&ak=OElqFYoKiAH8KFtph8ftLKF5NlNrbCUr"></script>
|
||||
<script type="text/javascript" src="/jsencrypt.min.js"></script>
|
||||
<script type="module" src="/src/main.js"></script>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,10 @@
|
|||
{
|
||||
"compilerOptions": {
|
||||
"baseUrl": ".",
|
||||
"paths": {
|
||||
"@/*": [
|
||||
"src/*"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,60 @@
|
|||
{
|
||||
"name": "vue3_template",
|
||||
"private": true,
|
||||
"version": "0.0.0",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
"build": "vite build",
|
||||
"preview": "vite preview",
|
||||
"lint": "eslint --ext .js,.vue --fix src .prettierrc.cjs"
|
||||
},
|
||||
"dependencies": {
|
||||
"@element-plus/icons-vue": "^2.1.0",
|
||||
"@icon-park/vue-next": "^1.4.2",
|
||||
"@vueuse/core": "^9.13.0",
|
||||
"@vueuse/integrations": "^10.7.0",
|
||||
"animate.css": "^4.1.1",
|
||||
"axios": "^1.6.3",
|
||||
"dayjs": "^1.11.10",
|
||||
"element-plus": "^2.4.4",
|
||||
"lodash-es": "^4.17.21",
|
||||
"mitt": "^3.0.1",
|
||||
"normalize.css": "^8.0.1",
|
||||
"pinia": "^2.1.7",
|
||||
"pinia-plugin-persistedstate": "^3.2.0",
|
||||
"qrcode": "^1.5.3",
|
||||
"qs": "^6.11.2",
|
||||
"throttle-debounce": "^5.0.0",
|
||||
"v-viewer": "^3.0.11",
|
||||
"vue": "^3.3.13",
|
||||
"vue-router": "^4.2.5",
|
||||
"vue3-print-nb": "^0.1.4",
|
||||
"vue3-puzzle-vcode": "^1.0.16"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@our-patches/postcss-px-to-viewport": "^1.2.0",
|
||||
"@types/node": "^18.18.4",
|
||||
"@vitejs/plugin-basic-ssl": "^1.0.1",
|
||||
"@vitejs/plugin-vue": "^4.4.0",
|
||||
"@vue/eslint-config-prettier": "^7.1.0",
|
||||
"autoprefixer": "^10.4.16",
|
||||
"eslint": "^8.51.0",
|
||||
"eslint-config-prettier": "^8.10.0",
|
||||
"eslint-config-standard": "^17.1.0",
|
||||
"eslint-plugin-import": "^2.28.1",
|
||||
"eslint-plugin-n": "^15.7.0",
|
||||
"eslint-plugin-prettier": "^4.2.1",
|
||||
"eslint-plugin-promise": "^6.1.1",
|
||||
"eslint-plugin-vue": "^9.17.0",
|
||||
"prettier": "^2.8.8",
|
||||
"sass": "^1.69.0",
|
||||
"unplugin-auto-import": "^0.12.2",
|
||||
"unplugin-vue-components": "^0.22.12",
|
||||
"vite": "^4.4.11",
|
||||
"vite-plugin-enhance-log": "^0.5.2",
|
||||
"vite-plugin-eslint": "^1.8.1",
|
||||
"vite-plugin-remove-console": "^2.1.1",
|
||||
"vue-eslint-parser": "^9.3.1"
|
||||
}
|
||||
}
|
|
@ -0,0 +1,22 @@
|
|||
const path = require('path')
|
||||
module.exports = {
|
||||
plugins: {
|
||||
autoprefixer: {
|
||||
overrideBrowserslist: ['Android 4.1', 'iOS 7.1', 'Chrome > 31', 'ff > 31', 'ie >= 8', '> 1%'],
|
||||
grid: true,
|
||||
},
|
||||
// '@our-patches/postcss-px-to-viewport': {
|
||||
// unitToConvert: 'px',
|
||||
// viewportWidth: 1920,
|
||||
// unitPrecision: 3,
|
||||
// viewportUnit: 'vw',
|
||||
// selectorBlackList: ['.ignore', '.hairlines'],
|
||||
// minPixelValue: 1,
|
||||
// mediaQuery: false,
|
||||
// exclude: [/^node_modules$/],
|
||||
// include: [/BI/],
|
||||
// landscapeUnit: 'vw',
|
||||
// landscapeWidth: 750,
|
||||
// }
|
||||
},
|
||||
};
|
|
@ -0,0 +1 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" class="iconify iconify--logos" width="31.88" height="32" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 257"><defs><linearGradient id="IconifyId1813088fe1fbc01fb466" x1="-.828%" x2="57.636%" y1="7.652%" y2="78.411%"><stop offset="0%" stop-color="#41D1FF"></stop><stop offset="100%" stop-color="#BD34FE"></stop></linearGradient><linearGradient id="IconifyId1813088fe1fbc01fb467" x1="43.376%" x2="50.316%" y1="2.242%" y2="89.03%"><stop offset="0%" stop-color="#FFEA83"></stop><stop offset="8.333%" stop-color="#FFDD35"></stop><stop offset="100%" stop-color="#FFA800"></stop></linearGradient></defs><path fill="url(#IconifyId1813088fe1fbc01fb466)" d="M255.153 37.938L134.897 252.976c-2.483 4.44-8.862 4.466-11.382.048L.875 37.958c-2.746-4.814 1.371-10.646 6.827-9.67l120.385 21.517a6.537 6.537 0 0 0 2.322-.004l117.867-21.483c5.438-.991 9.574 4.796 6.877 9.62Z"></path><path fill="url(#IconifyId1813088fe1fbc01fb467)" d="M185.432.063L96.44 17.501a3.268 3.268 0 0 0-2.634 3.014l-5.474 92.456a3.268 3.268 0 0 0 3.997 3.378l24.777-5.718c2.318-.535 4.413 1.507 3.936 3.838l-7.361 36.047c-.495 2.426 1.782 4.5 4.151 3.78l15.304-4.649c2.372-.72 4.652 1.36 4.15 3.788l-11.698 56.621c-.732 3.542 3.979 5.473 5.943 2.437l1.313-2.028l72.516-144.72c1.215-2.423-.88-5.186-3.54-4.672l-25.505 4.922c-2.396.462-4.435-1.77-3.759-4.114l16.646-57.705c.677-2.35-1.37-4.583-3.769-4.113Z"></path></svg>
|
After Width: | Height: | Size: 1.5 KiB |
|
@ -0,0 +1,18 @@
|
|||
<template>
|
||||
<suspense>
|
||||
<template #default>
|
||||
<el-config-provider :locale="zhCn">
|
||||
<router-view />
|
||||
</el-config-provider>
|
||||
</template>
|
||||
<template #fallback>
|
||||
<div>加载中...</div>
|
||||
</template>
|
||||
</suspense>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import zhCn from "element-plus/dist/locale/zh-cn.mjs";
|
||||
</script>
|
||||
|
||||
<style lang="scss"></style>
|
|
@ -0,0 +1,89 @@
|
|||
import router from "./router";
|
||||
import { useRouterStore } from "./pinia/router";
|
||||
import { useMenuStore } from "./pinia/menu";
|
||||
import { useUserStore } from "@/pinia/user";
|
||||
import { cloneDeep } from "lodash-es";
|
||||
import pinia from "./pinia";
|
||||
import children from "@/components/children/index";
|
||||
import { MODEL } from "@/assets/js/constant";
|
||||
import asyncRouter from "@/assets/js/asyncRouter";
|
||||
import { getAsyncRouter } from "@/request/api";
|
||||
// import { getRouteTreeAll } from "@/request/system_management.js";
|
||||
|
||||
const modules = import.meta.glob("./views/**/*.vue"); // 获取到views下所有的vue文件
|
||||
let storageRouter = null; // 用来获取后台拿到的路由
|
||||
|
||||
router.beforeEach(async (to, from, next) => {
|
||||
const routerStore = useRouterStore(pinia);
|
||||
const menuStore = useMenuStore(pinia);
|
||||
const userStore = useUserStore(pinia);
|
||||
// 需要登陆
|
||||
if (to.meta.isLogin !== false) {
|
||||
if (!userStore.getUserInfo.USER_ID) {
|
||||
next("/login");
|
||||
return;
|
||||
}
|
||||
if (!storageRouter) {
|
||||
// 变量里没有储存路由
|
||||
// pinia里没有储存路由,去后台获取路由
|
||||
if (routerStore.getRouters.length === 0) {
|
||||
await getAsyncRouter();
|
||||
// const resData = await getRouteTreeAll();
|
||||
// storageRouter = resData.menuList; // 后台请求得到的路由数据
|
||||
storageRouter = asyncRouter; // 死路由
|
||||
routerStore.setRouters(storageRouter); // 存储路由
|
||||
routerGo(to, next); // 执行路由跳转方法
|
||||
} else {
|
||||
// pinia里储存了路由
|
||||
storageRouter = routerStore.getRouters; // 拿到路由
|
||||
routerGo(to, next); // 执行路由跳转方法
|
||||
}
|
||||
} else {
|
||||
next();
|
||||
}
|
||||
} else {
|
||||
// 不需要登陆,清空储存路由
|
||||
storageRouter = null;
|
||||
routerStore.$reset();
|
||||
menuStore.$reset();
|
||||
next();
|
||||
}
|
||||
});
|
||||
function routerGo(to, next) {
|
||||
const menuStore = useMenuStore(pinia);
|
||||
storageRouter = filterAsyncRouter(cloneDeep(storageRouter)); // 过滤路由
|
||||
for (let i = 0; i < storageRouter.length; i++) {
|
||||
router.addRoute("app", storageRouter[i]); // 动态添加路由
|
||||
}
|
||||
router.addRoute({ path: "/:pathMatch(.*)*", redirect: "/404" }); // 将404路由添加到最后
|
||||
for (let i = 0; i < router.options.routes.length; i++) {
|
||||
if (router.options.routes[i].path === "/") {
|
||||
menuStore.setMenus(
|
||||
router.options.routes[i].children.concat(storageRouter)
|
||||
); // 将路由数据存到一个新的pinia里,做菜单渲染
|
||||
if (!menuStore.getModel) {
|
||||
menuStore.setModel(MODEL["1"]);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
next({ ...to, replace: true }); // 等待addRoute执行完毕跳转路由
|
||||
}
|
||||
|
||||
function filterAsyncRouter(asyncRouterMap) {
|
||||
// 遍历后台传来的路由字符串,转换为组件对象
|
||||
return asyncRouterMap.filter((route) => {
|
||||
if (route.component) {
|
||||
if (route.component === "children") {
|
||||
route.component = children;
|
||||
} else {
|
||||
route.component = modules[`./views/${route.component}.vue`];
|
||||
}
|
||||
}
|
||||
// 如果存在children递归
|
||||
if (route.children && route.children.length) {
|
||||
route.children = filterAsyncRouter(route.children);
|
||||
}
|
||||
return true;
|
||||
});
|
||||
}
|
|
@ -0,0 +1,324 @@
|
|||
// 文字超出几行隐藏,最多5行
|
||||
// 使用超出1行隐藏,如果使用了flex,则需要给父元素设置min-width: 0;
|
||||
@for $i from 1 through 5 {
|
||||
.line-#{$i} {
|
||||
@if $i == 1 {
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
} @else {
|
||||
display: -webkit-box !important;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
word-break: break-all;
|
||||
-webkit-line-clamp: $i;
|
||||
-webkit-box-orient: vertical !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 生成1-50的margin和padding(正负)
|
||||
@for $i from 1 through 50 {
|
||||
.m-#{$i} {
|
||||
margin: #{$i}px;
|
||||
}
|
||||
.mt-#{$i} {
|
||||
margin-top: #{$i}px;
|
||||
}
|
||||
.mr-#{$i} {
|
||||
margin-right: #{$i}px;
|
||||
}
|
||||
.mb-#{$i} {
|
||||
margin-bottom: #{$i}px;
|
||||
}
|
||||
.ml-#{$i} {
|
||||
margin-left: #{$i}px;
|
||||
}
|
||||
.mtb-#{$i} {
|
||||
margin-top: #{$i}px;
|
||||
margin-bottom: #{$i}px;
|
||||
}
|
||||
.mlr-#{$i} {
|
||||
margin-left: #{$i}px;
|
||||
margin-right: #{$i}px;
|
||||
}
|
||||
.p-#{$i} {
|
||||
padding: #{$i}px;
|
||||
}
|
||||
.pt-#{$i} {
|
||||
padding-top: #{$i}px;
|
||||
}
|
||||
.pr-#{$i} {
|
||||
padding-right: #{$i}px;
|
||||
}
|
||||
.pb-#{$i} {
|
||||
padding-bottom: #{$i}px;
|
||||
}
|
||||
.pl-#{$i} {
|
||||
padding-left: #{$i}px;
|
||||
}
|
||||
.ptb-#{$i} {
|
||||
padding-top: #{$i}px;
|
||||
padding-bottom: #{$i}px;
|
||||
}
|
||||
.plr-#{$i} {
|
||||
padding-left: #{$i}px;
|
||||
padding-right: #{$i}px;
|
||||
}
|
||||
.m--#{$i} {
|
||||
margin: -#{$i}px;
|
||||
}
|
||||
.mt--#{$i} {
|
||||
margin-top: -#{$i}px;
|
||||
}
|
||||
.mr--#{$i} {
|
||||
margin-right: -#{$i}px;
|
||||
}
|
||||
.mb--#{$i} {
|
||||
margin-bottom: -#{$i}px;
|
||||
}
|
||||
.ml--#{$i} {
|
||||
margin-left: -#{$i}px;
|
||||
}
|
||||
.mtb--#{$i} {
|
||||
margin-top: -#{$i}px;
|
||||
margin-bottom: -#{$i}px;
|
||||
}
|
||||
.mlr--#{$i} {
|
||||
margin-left: -#{$i}px;
|
||||
margin-right: -#{$i}px;
|
||||
}
|
||||
.p--#{$i} {
|
||||
padding: -#{$i}px;
|
||||
}
|
||||
.pt--#{$i} {
|
||||
padding-top: -#{$i}px;
|
||||
}
|
||||
.pr--#{$i} {
|
||||
padding-right: -#{$i}px;
|
||||
}
|
||||
.pb--#{$i} {
|
||||
padding-bottom: -#{$i}px;
|
||||
}
|
||||
.pl--#{$i} {
|
||||
padding-left: -#{$i}px;
|
||||
}
|
||||
.ptb--#{$i} {
|
||||
padding-top: -#{$i}px;
|
||||
padding-bottom: -#{$i}px;
|
||||
}
|
||||
.plr--#{$i} {
|
||||
padding-left: -#{$i}px;
|
||||
padding-right: -#{$i}px;
|
||||
}
|
||||
}
|
||||
|
||||
* {
|
||||
box-sizing: border-box;
|
||||
font-size: 14px;
|
||||
|
||||
&:not(dd,dl,dt) {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
}
|
||||
|
||||
h1, h2, h3, h4, h5, h6 {
|
||||
font-size: revert;
|
||||
}
|
||||
|
||||
#app {
|
||||
background-color: #030f2f;
|
||||
min-height: 100vh;
|
||||
background-image: url("/src/assets/images/public/bg.jpg");
|
||||
background-repeat: no-repeat;
|
||||
}
|
||||
|
||||
a {
|
||||
text-decoration: none;
|
||||
color: var(--el-color-primary);
|
||||
}
|
||||
|
||||
.end {
|
||||
.el-form-item__content {
|
||||
justify-content: end;
|
||||
}
|
||||
}
|
||||
|
||||
.tc {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.tr {
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.tl {
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.dn {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.text-blue {
|
||||
color: #3b3bff;
|
||||
}
|
||||
|
||||
.text-yellow {
|
||||
color: #bebe05;
|
||||
}
|
||||
|
||||
.text-orange {
|
||||
color: #de9004;
|
||||
}
|
||||
|
||||
.text-red {
|
||||
color: #ff0000;
|
||||
}
|
||||
|
||||
.text-green {
|
||||
color: #0bb20c;
|
||||
}
|
||||
|
||||
.print_use {
|
||||
display: none;
|
||||
}
|
||||
|
||||
img.ml-10:first-child {
|
||||
margin-left: 0;
|
||||
}
|
||||
|
||||
input:-webkit-autofill,
|
||||
input:-webkit-autofill:hover,
|
||||
input:-webkit-autofill:focus,
|
||||
input:-webkit-autofill:active {
|
||||
-webkit-transition-delay: 99999s;
|
||||
-webkit-transition: color 99999s ease-out, background-color 99999s ease-out;
|
||||
}
|
||||
|
||||
div::-webkit-scrollbar {
|
||||
width: 4px;
|
||||
}
|
||||
|
||||
div::-webkit-scrollbar-thumb {
|
||||
border-radius: 10px;
|
||||
-webkit-box-shadow: inset 0 0 5px rgba(0, 0, 0, 0.2);
|
||||
opacity: 0.2;
|
||||
background-color: var(--el-border-color);
|
||||
}
|
||||
|
||||
div::-webkit-scrollbar-track {
|
||||
-webkit-box-shadow: inset 0 0 5px rgba(0, 0, 0, 0.2);
|
||||
border-radius: 0;
|
||||
background-color: var(--el-border-color);
|
||||
}
|
||||
|
||||
.viewer-zoom-in, .viewer-zoom-out, .viewer-one-to-one, .viewer-reset, .viewer-prev, .viewer-play, .viewer-next, .viewer-rotate-left, .viewer-rotate-right, .viewer-flip-horizontal, .viewer-flip-vertical, .viewer-fullscreen, .viewer-fullscreen-exit, .viewer-close {
|
||||
&::before {
|
||||
font-size: unset !important;
|
||||
}
|
||||
}
|
||||
|
||||
.w-e-bar {
|
||||
--w-e-toolbar-bg-color: var(--el-fill-color-blank) !important;
|
||||
border: 1px solid var(--el-border-color);
|
||||
border-bottom: none;
|
||||
--w-e-toolbar-border-color: var(--el-border-color);
|
||||
--w-e-toolbar-color: var(--el-text-color-regular) !important;
|
||||
--w-e-toolbar-disabled-color: var(--el-text-color-regular) !important;
|
||||
--w-e-toolbar-active-bg-color: var(--el-fill-color-light);
|
||||
--w-e-toolbar-active-color: var(--el-color-check);
|
||||
|
||||
.w-e-menu-tooltip-v5:before {
|
||||
color: var(--el-text-color-regular) !important;
|
||||
}
|
||||
|
||||
.w-e-select-list ul {
|
||||
li:hover, .selected {
|
||||
background-color: var(--el-fill-color-light);
|
||||
color: var(--el-color-check);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.w-e-text-container {
|
||||
--w-e-textarea-bg-color: var(--el-fill-color-blank) !important;
|
||||
border: 1px solid var(--el-border-color);
|
||||
--w-e-textarea-color: var(--el-text-color-regular) !important;
|
||||
}
|
||||
|
||||
.w-e-bar-divider {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
.w-e-toolbar {
|
||||
border-bottom: 1px dashed var(--el-border-color-darker);
|
||||
}
|
||||
|
||||
.w-e-bar-item:has([data-menu-key="group-video"]) {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
.w-e-bar-item-menus-container .w-e-bar-item:has([data-menu-key="insertImage"]) {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
.vue-auth-box_ {
|
||||
background: #020f3a !important;
|
||||
border: 1px solid #223765 !important;
|
||||
}
|
||||
|
||||
.vue-puzzle-vcode {
|
||||
background-color: #0000008d !important;
|
||||
}
|
||||
|
||||
.vue-auth-box_ .auth-control_ .range-box {
|
||||
background-color: #04205f !important;
|
||||
box-shadow: none !important;
|
||||
}
|
||||
|
||||
.vue-auth-box_ .auth-control_ .range-box .range-slider .range-btn {
|
||||
background: #3266cb !important;
|
||||
box-shadow: none !important;
|
||||
}
|
||||
|
||||
// 打印时去掉页眉页脚
|
||||
@page {
|
||||
size: auto;
|
||||
margin: 3mm;
|
||||
}
|
||||
|
||||
.page_break{
|
||||
page-break-after: always;
|
||||
}
|
||||
|
||||
@media print {
|
||||
.el-descriptions__label.el-descriptions__cell.is-bordered-label {
|
||||
color: #000 !important;
|
||||
}
|
||||
.el-descriptions {
|
||||
--el-text-color-primary: #000 !important;
|
||||
}
|
||||
|
||||
.el-divider__text {
|
||||
--el-text-color-regular: #000 !important;
|
||||
}
|
||||
|
||||
.print_use {
|
||||
border-collapse: collapse;
|
||||
width: 100%;
|
||||
display: table;
|
||||
|
||||
td, th {
|
||||
border: 1px solid var(--el-border-color);
|
||||
padding: 8px;
|
||||
line-height: 1.6;
|
||||
text-align: center;
|
||||
}
|
||||
}
|
||||
|
||||
.print_no_use {
|
||||
display: none;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,385 @@
|
|||
:root {
|
||||
--el-header-height: 69px;
|
||||
--el-aside-scrollbar-height: calc(100vh - var(--el-header-height));
|
||||
--el-main-scrollbar-height: calc(100vh - var(--el-header-height) - 60px);
|
||||
--el-border-color: #273868 !important; // 边框颜色
|
||||
--el-text-color-regular: #fff !important;
|
||||
--el-fill-color-light: #0e1d44 !important; //hover背景色
|
||||
--el-color-check: #14affe !important; //hover、选中 文字颜色
|
||||
--el-aside-bg-color: #081e42 !important;
|
||||
--el-input-bg-color: #081435 !important;
|
||||
--el-fill-color-blank: #081435 !important; // 背景色
|
||||
--el-border-color-lighter: var(--el-border-color) !important;
|
||||
--el-bullet-frame-bg-color: #08163b !important;
|
||||
}
|
||||
|
||||
.el-button:focus, .el-button:hover {
|
||||
--el-button-hover-text-color: var(--el-button-text-color);
|
||||
--el-button-hover-border-color: var(--el-button-border-color);
|
||||
--el-button-hover-bg-color: var(--el-button-bg-color);
|
||||
}
|
||||
|
||||
.el-header {
|
||||
--el-header-padding: 0 !important;
|
||||
--el-header-height: 69px !important;
|
||||
}
|
||||
|
||||
.el-main {
|
||||
--el-main-padding: 0 !important;
|
||||
}
|
||||
|
||||
.el-aside {
|
||||
--el-aside-width: 250px;
|
||||
background-color: var(--el-aside-bg-color);
|
||||
}
|
||||
|
||||
.el-card {
|
||||
margin: 10px 20px;
|
||||
--el-card-padding: 18px 18px 0 18px !important;
|
||||
--el-card-bg-color: rgba(8, 24, 58, 0.5) !important;
|
||||
--el-card-border-color: var(--el-border-color) !important;
|
||||
color: var(--el-text-color-regular) !important;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.el-popper {
|
||||
--el-bg-color-overlay: var(--el-fill-color-blank) !important;
|
||||
--el-border-color-light: var(--el-border-color) !important;
|
||||
}
|
||||
|
||||
.el-cascader-node:not(.is-disabled):focus, .el-cascader-node:not(.is-disabled):hover {
|
||||
--el-cascader-node-background-hover: var(--el-fill-color-light);
|
||||
color: var(--el-color-check);
|
||||
}
|
||||
|
||||
.el-cascader {
|
||||
--el-cascader-tag-background: #214082 !important;
|
||||
--el-color-info: var(--el-text-color-regular);
|
||||
}
|
||||
|
||||
.el-select-dropdown__item.hover, .el-select-dropdown__item:hover {
|
||||
color: var(--el-color-check);
|
||||
}
|
||||
|
||||
.el-dropdown-menu__item {
|
||||
&:not(.is-disabled):focus {
|
||||
--el-dropdown-menuItem-hover-fill: var(--el-fill-color-light);
|
||||
--el-dropdown-menuItem-hover-color: var(--el-color-check);
|
||||
}
|
||||
}
|
||||
|
||||
.el-menu {
|
||||
border-right: none !important;
|
||||
--el-menu-bg-color: var(--el-aside-bg-color);
|
||||
--el-menu-text-color: var(--el-text-color-regular);
|
||||
--el-menu-hover-text-color: var(--el-text-color-regular);
|
||||
--el-menu-active-color: var(--el-text-color-regular);
|
||||
--el-menu-hover-bg-color: transparent;
|
||||
|
||||
span {
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
}
|
||||
|
||||
.el-sub-menu__title *, .el-menu-item * {
|
||||
vertical-align: middle !important;
|
||||
}
|
||||
|
||||
.el-menu-item {
|
||||
&:hover, &.is-active {
|
||||
background-image: url("/src/assets/images/public/list_on.png");
|
||||
background-repeat: no-repeat;
|
||||
background-size: 100% 100%;
|
||||
}
|
||||
}
|
||||
|
||||
//.el-menu--vertical:not(.el-menu--collapse):not(.el-menu--popup-container) .el-menu-item {
|
||||
// padding-left: calc(var(--el-menu-base-level-padding) + var(--el-menu-level) * var(--el-menu-level-padding) + 20px) !important;
|
||||
//}
|
||||
|
||||
.el-breadcrumb {
|
||||
margin-left: 10px;
|
||||
height: 30px;
|
||||
line-height: 30px !important;
|
||||
|
||||
.el-breadcrumb__inner {
|
||||
--el-text-color-regular: #cacbce;
|
||||
|
||||
a {
|
||||
font-weight: normal;
|
||||
--el-text-color-primary: #cacbce;
|
||||
}
|
||||
}
|
||||
|
||||
.el-breadcrumb__separator {
|
||||
--el-text-color-placeholder: #cacbce;
|
||||
}
|
||||
}
|
||||
|
||||
.el-select, .el-cascader, .el-date-editor.el-input, .el-date-editor.el-input__wrapper, .el-input__wrapper, .el-input-number {
|
||||
width: 100% !important;
|
||||
}
|
||||
|
||||
.el-table {
|
||||
--el-table-bg-color: #071a43 !important;
|
||||
--el-bg-color: #071a43 !important;
|
||||
--el-table-header-bg-color: #0f2049 !important;
|
||||
--el-table-border-color: var(--el-border-color) !important;
|
||||
--el-table-tr-bg-color: #071a43 !important;
|
||||
--el-fill-color-lighter: #111e40 !important;
|
||||
--el-table-text-color: #e0e0e0 !important;
|
||||
--el-table-header-text-color: var(--el-text-color-regular) !important;
|
||||
--el-table-row-hover-bg-color: #0d5aa3 !important;
|
||||
--el-table-current-row-bg-color: #0d5aa3 !important;
|
||||
|
||||
.el-table__cell {
|
||||
text-align: center !important;
|
||||
}
|
||||
|
||||
.el-table__empty-text {
|
||||
--el-text-color-secondary: var(--el-text-color-regular) !important;
|
||||
}
|
||||
}
|
||||
|
||||
.el-pagination {
|
||||
--el-pagination-button-color: var(--el-text-color-regular) !important;
|
||||
--el-pagination-button-disabled-bg-color: var(--el-fill-color-blank) !important;
|
||||
--el-pagination-bg-color: var(--el-fill-color-blank) !important;
|
||||
}
|
||||
|
||||
.el-radio {
|
||||
--el-radio-input-bg-color: #091839 !important;
|
||||
}
|
||||
|
||||
.el-radio__input.is-checked .el-radio__inner {
|
||||
background: var(--el-radio-input-bg-color) !important;
|
||||
border-color: var(--el-color-check);
|
||||
}
|
||||
|
||||
.el-radio__inner {
|
||||
&::after {
|
||||
--el-color-white: var(--el-color-check);
|
||||
}
|
||||
}
|
||||
|
||||
.el-radio__input.is-disabled.is-checked .el-radio__inner {
|
||||
--el-disabled-border-color: var(--el-color-check);
|
||||
|
||||
&::after {
|
||||
--el-text-color-placeholder: var(--el-color-check);
|
||||
}
|
||||
}
|
||||
|
||||
.el-radio__input.is-disabled .el-radio__inner {
|
||||
--el-disabled-bg-color: var(--el-radio-input-bg-color);
|
||||
--el-disabled-border-color: var(--el-border-color);
|
||||
}
|
||||
|
||||
.el-checkbox {
|
||||
--el-checkbox-bg-color: #091839 !important;
|
||||
--el-checkbox-checked-bg-color: #091839 !important;
|
||||
--el-checkbox-checked-input-border-color: var(--el-color-check) !important;
|
||||
}
|
||||
|
||||
.el-checkbox__input.is-disabled .el-checkbox__inner {
|
||||
--el-checkbox-disabled-input-fill: var(--el-checkbox-bg-color);
|
||||
--el-checkbox-disabled-border-color: var(--el-border-color);
|
||||
}
|
||||
|
||||
.el-checkbox__input.is-disabled.is-checked .el-checkbox__inner {
|
||||
--el-checkbox-disabled-checked-input-fill: var(--el-checkbox-bg-color);
|
||||
--el-checkbox-disabled-checked-input-border-color: var(--el-color-check);
|
||||
}
|
||||
|
||||
.el-picker__popper, .el-date-picker {
|
||||
--el-datepicker-border-color: var(--el-border-color) !important;
|
||||
}
|
||||
|
||||
.el-time-panel__btn {
|
||||
--el-text-color-primary: var(--el-text-color-regular);
|
||||
}
|
||||
|
||||
.el-time-spinner__item:hover:not(.is-disabled):not(.is-active) {
|
||||
color: var(--el-color-check);
|
||||
}
|
||||
|
||||
.el-button.is-plain {
|
||||
--el-fill-color-blank: var(--el-fill-color-blank);
|
||||
}
|
||||
|
||||
.el-button.is-text:not(.is-disabled):active {
|
||||
background-color: var(--el-fill-color-light) !important;
|
||||
}
|
||||
|
||||
.el-button.is-text:not(.is-disabled):focus, .el-button.is-text:not(.is-disabled):hover {
|
||||
color: var(--el-color-check);
|
||||
}
|
||||
|
||||
.el-time-spinner__item.is-active:not(.is-disabled) {
|
||||
--el-text-color-primary: var(--el-color-check);
|
||||
}
|
||||
|
||||
.el-picker-panel__icon-btn {
|
||||
color: var(--el-text-color-regular);
|
||||
|
||||
.el-icon {
|
||||
color: var(--el-text-color-regular);
|
||||
}
|
||||
}
|
||||
|
||||
.el-date-range-picker {
|
||||
--el-datepicker-border-color: var(--el-fill-color-light) !important;
|
||||
--el-datepicker-inrange-bg-color: var(--el-fill-color-light) !important;
|
||||
--el-datepicker-inrange-hover-bg-color: var(--el-fill-color-light) !important;
|
||||
}
|
||||
|
||||
.el-date-editor .el-range-separator {
|
||||
color: var(--el-text-color-regular) !important;
|
||||
}
|
||||
|
||||
.el-input.is-disabled .el-input__wrapper {
|
||||
box-shadow: 0 0 0 1px var(--el-border-color) inset !important;
|
||||
}
|
||||
|
||||
.el-select__tags .el-tag--info {
|
||||
background-color: #214082 !important;
|
||||
--el-tag-text-color: var(--el-text-color-regular) !important;
|
||||
}
|
||||
|
||||
.el-tag .el-tag__close:hover {
|
||||
color: var(--el-color-check) !important;
|
||||
background-color: var(--el-fill-color-light) !important;
|
||||
}
|
||||
|
||||
.el-upload--picture-card {
|
||||
--el-fill-color-lighter: var(--el-fill-color-blank) !important;
|
||||
border: 1px solid var(--el-border-color) !important;
|
||||
}
|
||||
|
||||
.el-collapse {
|
||||
--el-collapse-header-text-color: var(--el-text-color-regular) !important;
|
||||
--el-collapse-content-text-color: var(--el-text-color-regular) !important;
|
||||
}
|
||||
|
||||
.el-tabs__item {
|
||||
color: var(--el-text-color-regular) !important;
|
||||
|
||||
&.is-active {
|
||||
color: var(--el-color-primary) !important;
|
||||
}
|
||||
}
|
||||
|
||||
.el-tabs__nav-wrap::after {
|
||||
--el-border-color-light: var(--el-border-color);
|
||||
}
|
||||
|
||||
.el-divider__text {
|
||||
font-size: 16px !important;
|
||||
font-weight: 700 !important;
|
||||
}
|
||||
|
||||
.el-dialog {
|
||||
background: transparent !important;
|
||||
--el-dialog-margin-top: 50px !important;
|
||||
|
||||
.el-dialog__header {
|
||||
background-image: url("/src/assets/images/public/tctitlebg.png");
|
||||
height: 31px;
|
||||
line-height: 31px;
|
||||
padding-left: 30px;
|
||||
background-repeat: no-repeat;
|
||||
background-size: 100% 100%;
|
||||
margin-right: 0 !important;
|
||||
--el-dialog-padding-primary: 0 !important;
|
||||
padding-bottom: 0;
|
||||
|
||||
.el-dialog__title {
|
||||
color: var(--el-text-color-regular);
|
||||
--el-dialog-title-font-size: 14px;
|
||||
}
|
||||
|
||||
.el-dialog__headerbtn {
|
||||
height: 31px;
|
||||
top: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.el-dialog__body, .el-dialog__footer {
|
||||
background-color: var(--el-bullet-frame-bg-color);
|
||||
}
|
||||
}
|
||||
|
||||
.el-drawer {
|
||||
--el-drawer-bg-color: var(--el-bullet-frame-bg-color) !important;
|
||||
|
||||
.el-drawer__header {
|
||||
color: var(--el-text-color-regular);
|
||||
}
|
||||
}
|
||||
|
||||
.el-popper.is-dark {
|
||||
background: var(--el-fill-color-blank) !important;
|
||||
border: 1px solid var(--el-border-color) !important;
|
||||
color: var(--el-text-color-regular) !important;
|
||||
|
||||
.el-popper__arrow::before {
|
||||
background: var(--el-fill-color-blank) !important;
|
||||
border: 1px solid var(--el-border-color) !important;
|
||||
}
|
||||
}
|
||||
|
||||
.el-message-box {
|
||||
background-color: var(--el-fill-color-blank) !important;
|
||||
--el-messagebox-title-color: var(--el-text-color-regular) !important;
|
||||
}
|
||||
|
||||
.el-divider__text {
|
||||
background-color: var(--el-fill-color-blank) !important;
|
||||
color: var(--el-text-color-regular) !important;
|
||||
}
|
||||
|
||||
.el-calendar {
|
||||
--el-calendar-selected-bg-color: var(--el-fill-color-light) !important;
|
||||
}
|
||||
|
||||
.el-steps {
|
||||
--el-text-color-primary: var(--el-text-color-regular);
|
||||
|
||||
.el-step__icon {
|
||||
background: var(--el-fill-color-blank);
|
||||
}
|
||||
}
|
||||
|
||||
.el-descriptions {
|
||||
--el-text-color-primary: var(--el-text-color-regular);
|
||||
}
|
||||
|
||||
.el-descriptions__label {
|
||||
width: 200px;
|
||||
}
|
||||
|
||||
.el-descriptions__content {
|
||||
width: auto;
|
||||
}
|
||||
|
||||
.el-descriptions__title {
|
||||
&:before {
|
||||
content: '';
|
||||
display: inline-block;
|
||||
width: 4px;
|
||||
height: 19px;
|
||||
background-color: #04a9f5;
|
||||
margin-right: 10px;
|
||||
vertical-align: text-bottom;
|
||||
}
|
||||
}
|
||||
|
||||
.el-statistic__content {
|
||||
color: var(--el-text-color-regular) !important;
|
||||
}
|
||||
|
||||
.el-step__title{
|
||||
font-size: 14px !important;;
|
||||
}
|
|
@ -0,0 +1,36 @@
|
|||
//router-view动画
|
||||
.view-leave-active {
|
||||
opacity: 1;
|
||||
transform: scaleY(1);
|
||||
transition: all .5s;
|
||||
transform-origin: center top;
|
||||
}
|
||||
|
||||
.view-enter-active .view-leave-active {
|
||||
transform-origin: center bottom;
|
||||
}
|
||||
|
||||
.view-enter-from, .view-leave-active {
|
||||
opacity: 0;
|
||||
transform: scaleY(0);
|
||||
}
|
||||
|
||||
//面包屑动画
|
||||
.breadcrumb-enter-active,
|
||||
.breadcrumb-leave-active {
|
||||
transition: all .5s;
|
||||
}
|
||||
|
||||
.breadcrumb-enter,
|
||||
.breadcrumb-leave-active {
|
||||
opacity: 0;
|
||||
transform: translateX(20px);
|
||||
}
|
||||
|
||||
.breadcrumb-move {
|
||||
transition: all .5s;
|
||||
}
|
||||
|
||||
.breadcrumb-leave-active {
|
||||
position: absolute;
|
||||
}
|
After Width: | Height: | Size: 128 KiB |
After Width: | Height: | Size: 25 KiB |
After Width: | Height: | Size: 45 KiB |
After Width: | Height: | Size: 14 KiB |
After Width: | Height: | Size: 455 KiB |
After Width: | Height: | Size: 1.4 KiB |
After Width: | Height: | Size: 1019 B |
After Width: | Height: | Size: 1016 B |
After Width: | Height: | Size: 1012 B |
After Width: | Height: | Size: 999 B |
After Width: | Height: | Size: 29 KiB |
After Width: | Height: | Size: 7.6 KiB |
After Width: | Height: | Size: 12 KiB |
After Width: | Height: | Size: 2.3 KiB |
After Width: | Height: | Size: 8.6 KiB |
|
@ -0,0 +1,78 @@
|
|||
import { MODEL } from "@/assets/js/constant";
|
||||
|
||||
export default [
|
||||
{
|
||||
path: "/enterprise_management",
|
||||
redirect: "/enterprise_management/information",
|
||||
meta: { title: "企业管理", model: MODEL["1"] },
|
||||
component: "children",
|
||||
children: [
|
||||
{
|
||||
path: "/enterprise_management/information",
|
||||
redirect: "/enterprise_management/information/info",
|
||||
meta: { title: "企业信息" },
|
||||
component: "children",
|
||||
children: [
|
||||
{
|
||||
path: "/enterprise_management/information/info",
|
||||
meta: { title: "企业信息", isSubMenu: false },
|
||||
component: "children",
|
||||
children: [
|
||||
{
|
||||
path: "",
|
||||
component: "enterprise_management/information/info",
|
||||
},
|
||||
{
|
||||
path: "/enterprise_management/information/info/edit",
|
||||
meta: {
|
||||
title: "编辑",
|
||||
activeMenu: "/enterprise_management/information/info",
|
||||
},
|
||||
component: "enterprise_management/information/edit",
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
path: "/enterprise_management/information/industry_qualifications",
|
||||
meta: { title: "行业资质", isSubMenu: false },
|
||||
component: "children",
|
||||
children: [
|
||||
{
|
||||
path: "",
|
||||
component:
|
||||
"enterprise_management/industry_qualifications/index",
|
||||
},
|
||||
{
|
||||
path: "/enterprise_management/information/industry_qualifications/add",
|
||||
meta: {
|
||||
title: "新增",
|
||||
activeMenu:
|
||||
"/enterprise_management/information/industry_qualifications",
|
||||
},
|
||||
component: "enterprise_management/industry_qualifications/add",
|
||||
},
|
||||
{
|
||||
path: "/enterprise_management/information/industry_qualifications/update",
|
||||
meta: {
|
||||
title: "修改",
|
||||
activeMenu:
|
||||
"/enterprise_management/information/industry_qualifications",
|
||||
},
|
||||
component: "enterprise_management/industry_qualifications/add",
|
||||
},
|
||||
{
|
||||
path: "/enterprise_management/information/industry_qualifications/view",
|
||||
meta: {
|
||||
title: "查看",
|
||||
activeMenu:
|
||||
"/enterprise_management/information/industry_qualifications",
|
||||
},
|
||||
component: "enterprise_management/industry_qualifications/view",
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
];
|
|
@ -0,0 +1,18 @@
|
|||
import { ElMessage } from "element-plus";
|
||||
import { useUserStore } from "@/pinia/user.js";
|
||||
import pinia from "@/pinia/index.js";
|
||||
|
||||
export default {
|
||||
install: (app) => {
|
||||
app.directive("button", {
|
||||
mounted(el, { value }) {
|
||||
const userStore = useUserStore(pinia);
|
||||
if (value) {
|
||||
if (!userStore.getPermissions.includes(value)) {
|
||||
el.parentNode.removeChild(el);
|
||||
}
|
||||
} else ElMessage.error("参数无效,请联系管理员");
|
||||
},
|
||||
});
|
||||
},
|
||||
};
|
|
@ -0,0 +1,23 @@
|
|||
// 将常用的值储存成常量,防止重复使用写错
|
||||
|
||||
// 头部导航条切换的model
|
||||
export const MODEL = {
|
||||
1: "prevention",
|
||||
2: "educationAndTraining",
|
||||
3: "highRisk",
|
||||
4: "monitor",
|
||||
5: "comprehensive",
|
||||
};
|
||||
// 头部导航条
|
||||
export const MENU = [
|
||||
{ title: "双重预防", model: MODEL["1"] },
|
||||
{ title: "教育培训", model: MODEL["2"] },
|
||||
{ title: "高危作业管理", model: MODEL["3"] },
|
||||
{ title: "监测预警", model: MODEL["4"] },
|
||||
{ title: "综合管理", model: MODEL["5"] },
|
||||
];
|
||||
|
||||
export const styleText =
|
||||
'<style type="text/css" media="print">\n' +
|
||||
" @page { size: landscape; }\n" +
|
||||
"</style>";
|
|
@ -0,0 +1,65 @@
|
|||
import {
|
||||
getLearningTrainType,
|
||||
getLevels,
|
||||
getLevelsAndChildrenNumber,
|
||||
getRegulatoryType,
|
||||
} from "@/request/data_dictionary.js";
|
||||
import { ref } from "vue";
|
||||
|
||||
// 监管类型
|
||||
export const layoutFnGetRegulatoryType = async (params) => {
|
||||
const resData = await getRegulatoryType(params);
|
||||
return ref(JSON.parse(resData.varOList.zTreeNodes));
|
||||
};
|
||||
// 企业状态
|
||||
export const layoutFnGetEnterpriseStatus = async () => {
|
||||
const resData = await getLevels({
|
||||
DICTIONARIES_ID: "4d4862f9863b4b0da67f754c49e67ea3",
|
||||
});
|
||||
return ref(resData.list);
|
||||
};
|
||||
// 隶属关系
|
||||
export const layoutFnGetSubordination = async () => {
|
||||
const resData = await getLevels({
|
||||
DICTIONARIES_ID: "1a13f574d4c44cd2ac2034f8e3259f9b",
|
||||
});
|
||||
return ref(resData.list);
|
||||
};
|
||||
// 企业规模
|
||||
export const layoutFnGetEnterpriseScale = async () => {
|
||||
const resData = await getLevels({
|
||||
DICTIONARIES_ID: "37b045e160c04ddba851073b4e510cc9",
|
||||
});
|
||||
return ref(resData.list);
|
||||
};
|
||||
// 培训行业类型
|
||||
export const layoutFnGetTrainingIndustryType = async () => {
|
||||
const resData = await getLearningTrainType({
|
||||
parentId: "052369aa22d242118236cde52d0c67ea",
|
||||
});
|
||||
return ref(JSON.parse(resData.zTreeNodes));
|
||||
};
|
||||
// 培训岗位类型
|
||||
export const layoutFnGetTrainingPostType = async () => {
|
||||
const resData = await getLearningTrainType({
|
||||
parentId: "f6a7c4f5602f46e291d06b1390a3f820",
|
||||
});
|
||||
return ref(JSON.parse(resData.zTreeNodes));
|
||||
};
|
||||
// 培训板块类型
|
||||
export const layoutFnGetTrainingPlateType = async () => {
|
||||
const resData = await getLearningTrainType({
|
||||
parentId: "d538d11e4eec409ab428f5d2f3c67c24",
|
||||
});
|
||||
return ref(JSON.parse(resData.zTreeNodes));
|
||||
};
|
||||
// 无法确定DICTIONARIES_ID的数据字典
|
||||
export const layoutFnGetLevels = async (DICTIONARIES_ID) => {
|
||||
const resData = await getLevels({ DICTIONARIES_ID });
|
||||
return ref(resData.list);
|
||||
};
|
||||
// 无法确定DICTIONARIES_ID的数据字典包括子级数量
|
||||
export const layoutFnGetLevelsAndChildrenNumber = async (DICTIONARIES_ID) => {
|
||||
const resData = await getLevelsAndChildrenNumber({ DICTIONARIES_ID });
|
||||
return ref(resData.list);
|
||||
};
|
|
@ -0,0 +1,2 @@
|
|||
import mitt from "mitt";
|
||||
export default mitt();
|
|
@ -0,0 +1,39 @@
|
|||
import { ref } from "vue";
|
||||
|
||||
const buttonRef = ref(null);
|
||||
const THEAD_HEIGHT = 81.59;
|
||||
const A4_HEIGHT_MM = 297;
|
||||
const A4_HEIGHT_MM_TO_PX_PROPORTION = 3.78;
|
||||
let A4_HEIGHT_PX = A4_HEIGHT_MM * A4_HEIGHT_MM_TO_PX_PROPORTION - THEAD_HEIGHT;
|
||||
let elements = [];
|
||||
const printObj = {
|
||||
id: "printContent",
|
||||
closeCallback() {
|
||||
document.querySelector("#printContent").style.overflow = "hidden";
|
||||
},
|
||||
};
|
||||
const fnPrint = () => {
|
||||
document.querySelector("#printContent").style.overflow = "visible";
|
||||
elements = document.querySelectorAll("#printContent > table > tr");
|
||||
if (!document.querySelector("#printContent > table thead")) {
|
||||
A4_HEIGHT_PX = A4_HEIGHT_PX + THEAD_HEIGHT;
|
||||
}
|
||||
fnIsPaging();
|
||||
buttonRef.value.$el.click();
|
||||
};
|
||||
const fnIsPaging = (index = 0) => {
|
||||
for (let i = index; i < elements.length; i++) {
|
||||
if (
|
||||
elements[i].offsetHeight +
|
||||
elements[i].offsetTop -
|
||||
elements[index].offsetTop >=
|
||||
A4_HEIGHT_PX
|
||||
) {
|
||||
elements[i - 1].setAttribute("class", "page_break");
|
||||
fnIsPaging(i === index ? i + 1 : i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
export { fnPrint, printObj, buttonRef };
|
|
@ -0,0 +1,16 @@
|
|||
import { post } from "@/request/axios";
|
||||
|
||||
// 按钮权限
|
||||
export const useButtonJurisdiction = async (type) => {
|
||||
const keys = `${type}:add,${type}:del,${type}:edit,toExcel`;
|
||||
const resData = await post("/api/head/hasButton", {
|
||||
loading: false,
|
||||
keys,
|
||||
});
|
||||
return {
|
||||
add: resData[`${type}fhadminadd`],
|
||||
del: resData[`${type}fhadmindel`],
|
||||
edit: resData[`${type}fhadminedit`],
|
||||
excel: resData.toExcel,
|
||||
};
|
||||
};
|
|
@ -0,0 +1,25 @@
|
|||
import { ElMessage } from "element-plus";
|
||||
import { getDataType } from "@/assets/js/utils.js";
|
||||
|
||||
export default function (ref, message = "请补全必填项!") {
|
||||
const type = ["Function", "AsyncFunction"];
|
||||
if (!type.includes(getDataType(ref?.value?.validate)))
|
||||
throw new Error("不是有效的Element-Plus Form组件ref!");
|
||||
return new Promise((resolve, reject) => {
|
||||
ref.value.validate((valid) => {
|
||||
if (valid) {
|
||||
resolve(valid);
|
||||
} else {
|
||||
reject(valid);
|
||||
ElMessage.warning(message);
|
||||
setTimeout(() => {
|
||||
const element = document.querySelectorAll(".el-form-item__error")[0];
|
||||
element.scrollIntoView({
|
||||
behavior: "smooth",
|
||||
block: "center",
|
||||
});
|
||||
}, 100);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
|
@ -0,0 +1,77 @@
|
|||
import { nextTick, ref } from "vue";
|
||||
import { getDataType } from "@/assets/js/utils.js";
|
||||
|
||||
/**
|
||||
* @param api {Function} 接口函数
|
||||
* @param options {Object?: {callbackFn, otherParams, immediate, usePagination}} 配置项
|
||||
* @param options.callbackFn {Function?} 回调函数
|
||||
* @param options.otherParams {Object?} 其它接口参数
|
||||
* @param options.immediate {Boolean?} 是否立即执行接口函数(默认是)
|
||||
* @param options.usePagination {Boolean?} 是否使用分页(默认是)
|
||||
* @return {Object} 返回对象包含以下属性:list 表格数据,pagination 分页数据,searchForm 搜索表单数据,tableRef 表格实例,fnGetData 获取数据函数,fnResetPagination 重置分页函数
|
||||
*/
|
||||
|
||||
export default function useListData(api, options = {}) {
|
||||
if (getDataType(api) !== "Function") throw new Error("api必须是一个函数");
|
||||
if (options.immediate && getDataType(options.immediate) !== "Boolean")
|
||||
throw new Error("options.immediate必须是一个布尔值");
|
||||
if (options.usePagination && getDataType(options.usePagination) !== "Boolean")
|
||||
throw new Error("options.usePagination必须是一个布尔值");
|
||||
const immediate = options.immediate ?? true;
|
||||
const usePagination = options.usePagination ?? true;
|
||||
if (!immediate && options.otherParams)
|
||||
throw new Error("options.otherParams只有在immediate为true时才有效");
|
||||
if (
|
||||
immediate &&
|
||||
options.otherParams &&
|
||||
getDataType(options.otherParams) !== "Object"
|
||||
)
|
||||
throw new Error("options.otherParams必须是一个对象");
|
||||
if (options.callbackFn && getDataType(options.callbackFn) !== "Function")
|
||||
throw new Error("options.callbackFn必须是一个函数");
|
||||
const list = ref([]);
|
||||
const pagination = ref({
|
||||
currentPage: 1,
|
||||
pageSize: 10,
|
||||
total: 0,
|
||||
});
|
||||
const searchForm = ref({});
|
||||
const tableRef = ref(null);
|
||||
const fnGetData = async (otherParams = {}) => {
|
||||
const resData = await api({
|
||||
...(usePagination
|
||||
? {
|
||||
currentPage: pagination.value.currentPage,
|
||||
showCount: pagination.value.pageSize,
|
||||
}
|
||||
: {}),
|
||||
...searchForm.value,
|
||||
...(options.otherParams || {}),
|
||||
...(getDataType(otherParams) === "Object" ? otherParams : {}),
|
||||
});
|
||||
list.value = resData.varList;
|
||||
pagination.value.total = resData.page.totalResult;
|
||||
options.callbackFn && options.callbackFn(list.value);
|
||||
};
|
||||
immediate && fnGetData().then();
|
||||
const fnResetPagination = async (otherParams) => {
|
||||
list.value = [];
|
||||
pagination.value = {
|
||||
currentPage: 1,
|
||||
pageSize: 10,
|
||||
total: 0,
|
||||
};
|
||||
await nextTick();
|
||||
await fnGetData(otherParams);
|
||||
tableRef.value && tableRef.value.clearSelection();
|
||||
};
|
||||
return {
|
||||
list,
|
||||
pagination,
|
||||
searchForm,
|
||||
tableRef,
|
||||
fnGetData: async (otherParams) => await fnGetData(otherParams),
|
||||
fnResetPagination: async (otherParams) =>
|
||||
await fnResetPagination(otherParams),
|
||||
};
|
||||
}
|
|
@ -0,0 +1,320 @@
|
|||
import { ElMessage, ElMessageBox } from "element-plus";
|
||||
|
||||
/**
|
||||
* @description 计算序号
|
||||
* @param {Object} pagination 分页数据对象
|
||||
* @param {number | string} pagination.currentPage 当前页
|
||||
* @param {number | string} pagination.pageSize 每页条数
|
||||
* @param {number} index 当页数据的索引值
|
||||
* @return {number} 序号
|
||||
**/
|
||||
export function serialNumber(pagination, index) {
|
||||
return (pagination.currentPage - 1) * pagination.pageSize + (index + 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* @description 字符串数组转数组
|
||||
* @param {string} value 转换的字符串数组
|
||||
* @return {Array} 转换后的数组
|
||||
**/
|
||||
export function toArrayString(value) {
|
||||
// eslint-disable-next-line no-eval
|
||||
return value ? eval(value).map(String) : [];
|
||||
}
|
||||
|
||||
/**
|
||||
* @description 判断文件后缀名是否符合
|
||||
* @param {string} name 文件名字
|
||||
* @param {string} suffix 文件后缀
|
||||
* @return {boolean} 是否符合
|
||||
**/
|
||||
export function interceptTheSuffix(name, suffix) {
|
||||
return (
|
||||
name.substring(name.lastIndexOf("."), name.length).toLowerCase() ===
|
||||
suffix.toLowerCase()
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @description 图片转base64
|
||||
* @param {string} imgUrl 图片地址
|
||||
* @return {Promise} Promise实例,then包含base64编码
|
||||
**/
|
||||
export function image2Base64(imgUrl) {
|
||||
return new Promise((resolve) => {
|
||||
const img = new Image();
|
||||
img.src = imgUrl;
|
||||
img.crossOrigin = "Anonymous";
|
||||
img.onload = function () {
|
||||
const canvas = document.createElement("canvas");
|
||||
canvas.width = img.width;
|
||||
canvas.height = img.height;
|
||||
const ctx = canvas.getContext("2d");
|
||||
ctx.drawImage(img, 0, 0, img.width, img.height);
|
||||
const ext = img.src.substring(img.src.lastIndexOf(".") + 1).toLowerCase();
|
||||
resolve(canvas.toDataURL("image/" + ext));
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @description 判断图片是否可访问成功
|
||||
* @param {string} imgUrl 图片地址
|
||||
* @return {Promise} Promise实例
|
||||
**/
|
||||
export function checkImgExists(imgUrl) {
|
||||
return new Promise((resolve, reject) => {
|
||||
const ImgObj = new Image();
|
||||
ImgObj.src = imgUrl;
|
||||
ImgObj.onload = function (res) {
|
||||
resolve(res);
|
||||
};
|
||||
ImgObj.onerror = function (err) {
|
||||
reject(err);
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @description 获取数据类型
|
||||
* @param {any} data 数据
|
||||
* @return {string} 数据类型
|
||||
**/
|
||||
export function getDataType(data) {
|
||||
return Object.prototype.toString.call(data).slice(8, -1);
|
||||
}
|
||||
|
||||
/**
|
||||
* @description 数组去重
|
||||
* @param {Array<number,string>} arr 去重的数组
|
||||
* @return {Array} 去重后的数组
|
||||
**/
|
||||
export function ArrayDeduplication(arr) {
|
||||
return [...new Set(arr)];
|
||||
}
|
||||
|
||||
/**
|
||||
* @description 数组对象去重
|
||||
* @param {Array} arr 去重的数组
|
||||
* @param {string} name 去重的key
|
||||
* @return {Array} 去重后的数组
|
||||
**/
|
||||
export function ArrayObjectDeduplication(arr, name) {
|
||||
const obj = {};
|
||||
arr = arr.reduce(function (previousValue, currentValue) {
|
||||
if (!obj[currentValue[name]]) {
|
||||
obj[currentValue[name]] = true;
|
||||
previousValue.push(currentValue);
|
||||
}
|
||||
return previousValue;
|
||||
}, []);
|
||||
return arr;
|
||||
}
|
||||
|
||||
/**
|
||||
* @description 查找字符串中指定的值第几次出现的位置
|
||||
* @param {Array} str 查找的字符串数组
|
||||
* @param {string} char 查找的值
|
||||
* @param {number} num 第几次出现
|
||||
* @return {number} 出现的位置
|
||||
**/
|
||||
export function findCharIndex(str, char, num) {
|
||||
let index = str.indexOf(char);
|
||||
for (let i = 0; i < num - 1; i++) {
|
||||
index = str.indexOf(char, index + 1);
|
||||
}
|
||||
return index;
|
||||
}
|
||||
|
||||
/**
|
||||
* @description 生成指定两个值之间的随机数
|
||||
* @param {number} min 最小值
|
||||
* @param {number} max 最大值
|
||||
* @return {number} 随机数
|
||||
**/
|
||||
export function randoms(min, max) {
|
||||
return Math.random() * (max - min + 1) + min;
|
||||
}
|
||||
|
||||
/**
|
||||
* @description 千位分隔符
|
||||
* @param {number | string} num 转换的值
|
||||
* @return {string} 转换后的值
|
||||
**/
|
||||
export function numFormat(num) {
|
||||
if (num) {
|
||||
const numArr = num.toString().split(".");
|
||||
const arr = numArr[0].split("").reverse();
|
||||
let res = [];
|
||||
for (let i = 0; i < arr.length; i++) {
|
||||
if (i % 3 === 0 && i !== 0) {
|
||||
res.push(",");
|
||||
}
|
||||
res.push(arr[i]);
|
||||
}
|
||||
res.reverse();
|
||||
if (numArr[1]) {
|
||||
res = res.join("").concat("." + numArr[1]);
|
||||
} else {
|
||||
res = res.join("");
|
||||
}
|
||||
return res;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @description 验证是否为空
|
||||
* @param {any} value 验证的值
|
||||
* @return {boolean} 是否为空
|
||||
**/
|
||||
export function isEmpty(value) {
|
||||
return (
|
||||
value === undefined ||
|
||||
value === null ||
|
||||
(typeof value === "object" && Object.keys(value).length === 0) ||
|
||||
(typeof value === "string" && value.trim().length === 0)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @description 获取url参数
|
||||
* @param {string} name 获取的key
|
||||
* @return {string} 获取的值
|
||||
**/
|
||||
export function getUrlParam(name) {
|
||||
const reg = new RegExp("(^|&)" + name + "=([^&]*)(&|$)");
|
||||
const r = window.location.search.substr(1).match(reg);
|
||||
if (r != null) return decodeURI(r[2]);
|
||||
return "";
|
||||
}
|
||||
|
||||
/**
|
||||
* @description 数据分页
|
||||
* @param {Array} list 分页的数组
|
||||
* @param {number | string} currentPage 当前页
|
||||
* @param {number | string} pageSize 每页条数
|
||||
* @return {Array} 分页后的数组
|
||||
**/
|
||||
export function paging(list, currentPage, pageSize) {
|
||||
return list.filter((item, index) => {
|
||||
return (
|
||||
index < +currentPage * +pageSize &&
|
||||
index >= (+currentPage - 1) * +pageSize
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @description 获取文件后缀
|
||||
* @param {string} name 文件名
|
||||
* @return {string} 文件后缀
|
||||
**/
|
||||
export function getFileSuffix(name) {
|
||||
return name.substring(name.lastIndexOf(".") + 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* @description 获取文件名称
|
||||
* @param {string} name 文件地址
|
||||
* @return {string} 文件名称
|
||||
**/
|
||||
export function getFileName(name) {
|
||||
return name.substring(name.lastIndexOf("/") + 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* @description 读取txt文档
|
||||
* @param {string} filePah 文档路径
|
||||
* @return {resolve,string} 读取后的内容
|
||||
**/
|
||||
export function readTxtDocument(filePah) {
|
||||
return new Promise((resolve) => {
|
||||
const FILE_URL = import.meta.env.VITE_FILE_URL;
|
||||
const file_url = FILE_URL + filePah;
|
||||
const xhr = new XMLHttpRequest();
|
||||
xhr.open("get", file_url, true);
|
||||
xhr.responseType = "blob";
|
||||
xhr.onload = function (event) {
|
||||
const reader = new FileReader();
|
||||
reader.readAsText(event.target.response, "GB2312");
|
||||
reader.onload = function () {
|
||||
resolve(reader.result);
|
||||
};
|
||||
};
|
||||
xhr.send();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @description 将秒转换成时分秒
|
||||
* @param {string,number} second 需要转换的秒数
|
||||
* @return {string} 转换后的时间
|
||||
**/
|
||||
export function secondConversion(second) {
|
||||
if (!second) return 0;
|
||||
const h = parseInt((second / 60 / 60) % 24, 10);
|
||||
const m = parseInt((second / 60) % 60, 10);
|
||||
const s = parseInt(second % 60, 10);
|
||||
if (h) {
|
||||
return h + "小时" + m + "分钟" + s + "秒";
|
||||
} else {
|
||||
if (m) {
|
||||
return m + "分钟" + s + "秒";
|
||||
} else {
|
||||
return s + "秒";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @description 下载附件
|
||||
* @param {string} filePah 下载路径
|
||||
**/
|
||||
export async function downloadFile(filePah) {
|
||||
const FILE_URL = import.meta.env.VITE_FILE_URL;
|
||||
await ElMessageBox.confirm("确定要下载此文件吗?", { type: "warning" });
|
||||
window.open(FILE_URL + filePah, "_blank");
|
||||
}
|
||||
|
||||
/**
|
||||
* @description 附件添加前缀
|
||||
* @param {Array} list 附件数组
|
||||
* @return {Array} 添加完的数组
|
||||
**/
|
||||
export function addingPrefixToFile(list) {
|
||||
const FILE_URL = import.meta.env.VITE_FILE_URL;
|
||||
for (let i = 0; i < list.length; i++) {
|
||||
list[i].url = FILE_URL + list[i].FILEPATH;
|
||||
list[i].name = getFileName(list[i].FILEPATH);
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
/**
|
||||
* @description 验证重复选择
|
||||
* @param {Array} list 验证的数组
|
||||
* @param {number} index 选择的索引
|
||||
* @param {string} key 验证的字段
|
||||
* @param {string} id 验证的值
|
||||
**/
|
||||
export function verifyDuplicateSelection(list, index, key, id) {
|
||||
if (list.some((item) => item[key] === id)) {
|
||||
ElMessage.warning("不能重复选择");
|
||||
} else {
|
||||
list[index][key] = id;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @description 翻译状态
|
||||
* @param {number | string} status 状态
|
||||
* @param {Array} list 翻译的数组
|
||||
* @return {string} 翻译后的状态
|
||||
**/
|
||||
export function translationStatus(status, list) {
|
||||
for (let i = 0; i < list.length; i++) {
|
||||
if (status === list[i].ID) {
|
||||
return list[i].NAME;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,43 @@
|
|||
<template>
|
||||
<el-card>
|
||||
<div class="top_bg" />
|
||||
<div class="content">
|
||||
<slot />
|
||||
</div>
|
||||
<div class="bottom_bg" />
|
||||
</el-card>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
defineOptions({
|
||||
name: "LayoutCard",
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.el-card {
|
||||
--el-card-padding: 0 !important;
|
||||
}
|
||||
|
||||
.top_bg {
|
||||
margin: auto;
|
||||
width: 1250px;
|
||||
height: 15px;
|
||||
background-image: url("/src/assets/images/public/topguang.png");
|
||||
background-repeat: no-repeat;
|
||||
background-size: 100% 100%;
|
||||
}
|
||||
|
||||
.bottom_bg {
|
||||
margin: auto;
|
||||
width: 1218px;
|
||||
height: 30px;
|
||||
background-image: url("/src/assets/images/public/bottomguang.png");
|
||||
background-repeat: no-repeat;
|
||||
background-size: 100% 100%;
|
||||
}
|
||||
|
||||
.content {
|
||||
padding: 10px;
|
||||
}
|
||||
</style>
|
|
@ -0,0 +1,95 @@
|
|||
<template>
|
||||
<el-cascader
|
||||
ref="cascaderRef"
|
||||
v-model="modelValue"
|
||||
:props="cascaderProps"
|
||||
:show-all-levels="showAllLevels"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { useVModel } from "@vueuse/core";
|
||||
import { ref } from "vue";
|
||||
import {
|
||||
layoutFnGetLevels,
|
||||
layoutFnGetLevelsAndChildrenNumber,
|
||||
} from "@/assets/js/data_dictionary.js";
|
||||
|
||||
defineOptions({
|
||||
name: "LayoutCascader",
|
||||
});
|
||||
const props = defineProps({
|
||||
modelValue: {
|
||||
type: Array,
|
||||
default: () => [],
|
||||
required: true,
|
||||
},
|
||||
id: {
|
||||
type: String,
|
||||
required: true,
|
||||
default: "",
|
||||
},
|
||||
level: {
|
||||
type: [Number, String],
|
||||
default: 3,
|
||||
},
|
||||
checkStrictly: {
|
||||
type: Boolean,
|
||||
default: true,
|
||||
},
|
||||
showAllLevels: {
|
||||
type: Boolean,
|
||||
default: true,
|
||||
},
|
||||
value: {
|
||||
type: String,
|
||||
default: "DICTIONARIES_ID",
|
||||
},
|
||||
joinSeparator: {
|
||||
type: String,
|
||||
default: "/",
|
||||
},
|
||||
});
|
||||
const emits = defineEmits(["update:modelValue"]);
|
||||
const modelValue = useVModel(props, "modelValue", emits);
|
||||
const cascaderRef = ref(null);
|
||||
const cascaderProps = {
|
||||
lazy: true,
|
||||
lazyLoad: async (node, resolve) => {
|
||||
let resData;
|
||||
if (props.checkStrictly)
|
||||
resData = await layoutFnGetLevels(node.data.DICTIONARIES_ID || props.id);
|
||||
else
|
||||
resData = await layoutFnGetLevelsAndChildrenNumber(
|
||||
node.data.DICTIONARIES_ID || props.id
|
||||
);
|
||||
resolve(
|
||||
resData.value.map((item) => {
|
||||
return {
|
||||
DICTIONARIES_ID: item.DICTIONARIES_ID,
|
||||
BIANMA: item.BIANMA,
|
||||
NAME: item.NAME,
|
||||
leaf: props.checkStrictly
|
||||
? node.level >= props.level
|
||||
: item.zcount === 0,
|
||||
};
|
||||
})
|
||||
);
|
||||
},
|
||||
value: props.value,
|
||||
id: "DICTIONARIES_ID",
|
||||
label: "NAME",
|
||||
children: "children",
|
||||
checkStrictly: props.checkStrictly,
|
||||
};
|
||||
const getCheckedNodes = () => {
|
||||
return cascaderRef.value
|
||||
.getCheckedNodes()[0]
|
||||
.pathLabels.join(props.joinSeparator);
|
||||
};
|
||||
defineExpose({
|
||||
getCheckedNodes,
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped></style>
|
|
@ -0,0 +1,9 @@
|
|||
<template>
|
||||
<router-view />
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
defineOptions({
|
||||
name: "children",
|
||||
});
|
||||
</script>
|
|
@ -0,0 +1,37 @@
|
|||
<template>
|
||||
<layout-cascader
|
||||
id="688d2cf1c6cd4dab999a0106e09aec83"
|
||||
v-model="modelValue"
|
||||
ref="cascaderRef"
|
||||
:check-strictly="false"
|
||||
:show-all-levels="false"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref } from "vue";
|
||||
import { useVModel } from "@vueuse/core";
|
||||
import LayoutCascader from "@/components/cascader/index.vue";
|
||||
|
||||
defineOptions({
|
||||
name: "LayoutEconomicType",
|
||||
});
|
||||
const props = defineProps({
|
||||
modelValue: {
|
||||
type: Array,
|
||||
required: true,
|
||||
default: () => [],
|
||||
},
|
||||
});
|
||||
const emits = defineEmits(["update:modelValue"]);
|
||||
const modelValue = useVModel(props, "modelValue", emits);
|
||||
const cascaderRef = ref(null);
|
||||
const getCheckedNodes = () => {
|
||||
return cascaderRef.value.getCheckedNodes();
|
||||
};
|
||||
defineExpose({
|
||||
getCheckedNodes,
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped></style>
|
|
@ -0,0 +1,37 @@
|
|||
<template>
|
||||
<layout-cascader
|
||||
id="f2598ba72e864eadabf0ca4b664d26b9"
|
||||
v-model="modelValue"
|
||||
ref="cascaderRef"
|
||||
:check-strictly="false"
|
||||
:show-all-levels="false"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref } from "vue";
|
||||
import { useVModel } from "@vueuse/core";
|
||||
import LayoutCascader from "@/components/cascader/index.vue";
|
||||
|
||||
defineOptions({
|
||||
name: "LayoutIndustry",
|
||||
});
|
||||
const props = defineProps({
|
||||
modelValue: {
|
||||
type: Array,
|
||||
required: true,
|
||||
default: () => [],
|
||||
},
|
||||
});
|
||||
const emits = defineEmits(["update:modelValue"]);
|
||||
const modelValue = useVModel(props, "modelValue", emits);
|
||||
const cascaderRef = ref(null);
|
||||
const getCheckedNodes = () => {
|
||||
return cascaderRef.value.getCheckedNodes();
|
||||
};
|
||||
defineExpose({
|
||||
getCheckedNodes,
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped></style>
|
|
@ -0,0 +1,83 @@
|
|||
<template>
|
||||
<div
|
||||
style="display: flex; justify-content: space-between; align-items: center"
|
||||
>
|
||||
<div class="breadcrumb">
|
||||
<icon-local theme="filled" size="22" fill="#10a7e5" :strokeWidth="3" />
|
||||
<el-breadcrumb class="app-breadcrumb" separator=">">
|
||||
<transition-group name="breadcrumb">
|
||||
<el-breadcrumb-item
|
||||
v-for="(item, index) in data.breadcrumbList"
|
||||
:key="item.path"
|
||||
>
|
||||
<router-link v-if="index === 0" :to="item.path">
|
||||
{{ item.meta.title }}
|
||||
</router-link>
|
||||
<span v-else-if="index !== data.breadcrumbList.length - 1">
|
||||
{{ item.meta.title }}
|
||||
</span>
|
||||
<span v-else class="no-redirect">{{ item.meta.title }}</span>
|
||||
</el-breadcrumb-item>
|
||||
</transition-group>
|
||||
</el-breadcrumb>
|
||||
</div>
|
||||
<div class="return">
|
||||
<el-button
|
||||
type="primary"
|
||||
text
|
||||
link
|
||||
@click="$router.back()"
|
||||
v-if="
|
||||
route.meta?.activeMenu &&
|
||||
route.path &&
|
||||
route.meta?.activeMenu !== route.path
|
||||
"
|
||||
>
|
||||
返回上一页
|
||||
</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { reactive, watch } from "vue";
|
||||
import { useRoute } from "vue-router";
|
||||
|
||||
defineOptions({
|
||||
name: "LayoutBreadcrumb",
|
||||
});
|
||||
const route = useRoute();
|
||||
const data = reactive({
|
||||
breadcrumbList: [],
|
||||
});
|
||||
const fnGetBreadcrumb = () => {
|
||||
const matched = route.matched.filter((item) => item.meta?.title);
|
||||
data.breadcrumbList = matched.filter(
|
||||
(item) => item.meta?.title && item.meta?.breadcrumb !== false
|
||||
);
|
||||
};
|
||||
fnGetBreadcrumb();
|
||||
watch(
|
||||
() => route,
|
||||
() => fnGetBreadcrumb(),
|
||||
{ deep: true }
|
||||
);
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.breadcrumb {
|
||||
margin-top: 10px;
|
||||
margin-left: 20px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
.no-redirect {
|
||||
color: #ffffff;
|
||||
cursor: text;
|
||||
}
|
||||
}
|
||||
|
||||
.return {
|
||||
margin-right: 20px;
|
||||
}
|
||||
</style>
|
|
@ -0,0 +1,79 @@
|
|||
<template>
|
||||
<el-dialog
|
||||
v-model="visible"
|
||||
title="修改头像"
|
||||
width="600px"
|
||||
:before-close="fnClose"
|
||||
>
|
||||
<el-form :model="form" :rules="rules" label-width="100px" ref="formRef">
|
||||
<el-form-item label="头像" prop="file">
|
||||
<layout-upload
|
||||
v-model:file-list="form.file"
|
||||
accept=".jpg,.jpeg,.png"
|
||||
list-type="picture-card"
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<el-button type="primary" @click="fnSubmit">确认</el-button>
|
||||
<el-button @click="fnClose">关闭</el-button>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import LayoutUpload from "@/components/upload/index.vue";
|
||||
import { debounce } from "throttle-debounce";
|
||||
import useFormValidate from "@/assets/js/useFormValidate.js";
|
||||
import { setAvatar } from "@/request/api.js";
|
||||
import { ElMessage } from "element-plus";
|
||||
import { ref } from "vue";
|
||||
import { useVModels } from "@vueuse/core";
|
||||
import { useUserStore } from "@/pinia/user.js";
|
||||
|
||||
const userStore = useUserStore();
|
||||
const props = defineProps({
|
||||
visible: {
|
||||
type: Boolean,
|
||||
required: true,
|
||||
default: false,
|
||||
},
|
||||
form: {
|
||||
type: Object,
|
||||
required: true,
|
||||
default: () => ({}),
|
||||
},
|
||||
});
|
||||
const emits = defineEmits(["update:visible", "update:form"]);
|
||||
const { visible, form } = useVModels(props, emits);
|
||||
const formRef = ref(null);
|
||||
const rules = {
|
||||
file: [{ required: true, message: "请上传头像", trigger: "change" }],
|
||||
};
|
||||
const fnSubmit = debounce(
|
||||
1000,
|
||||
async () => {
|
||||
await useFormValidate(formRef);
|
||||
if (!form.value.file[0].raw) {
|
||||
ElMessage.warning("没有修改图片,不需要保存");
|
||||
return;
|
||||
}
|
||||
const formData = new FormData();
|
||||
formData.append("FFILE", form.value.file[0].raw);
|
||||
const resData = await setAvatar(formData);
|
||||
fnClose();
|
||||
ElMessage.success("修改成功");
|
||||
userStore.setUserInfo({
|
||||
...userStore.getUserInfo,
|
||||
userPhoto: resData.userPhoto,
|
||||
});
|
||||
},
|
||||
{ atBegin: true }
|
||||
);
|
||||
const fnClose = () => {
|
||||
formRef.value.resetFields();
|
||||
visible.value = false;
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss"></style>
|
|
@ -0,0 +1,130 @@
|
|||
<template>
|
||||
<el-dialog
|
||||
v-model="visible"
|
||||
title="修改信息"
|
||||
width="600px"
|
||||
:before-close="fnClose"
|
||||
>
|
||||
<el-form :model="form" :rules="rules" label-width="100px" ref="formRef">
|
||||
<el-form-item label="用户名" prop="USERNAME">
|
||||
<el-input
|
||||
v-model="form.USERNAME"
|
||||
disabled
|
||||
placeholder="默认用户手机号码..."
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="新密码" prop="newpwd">
|
||||
<el-input v-model="form.newpwd" type="password" autocomplete="off" />
|
||||
</el-form-item>
|
||||
<el-form-item label="确认密码" prop="newpassword1">
|
||||
<el-input
|
||||
v-model="form.newpassword1"
|
||||
type="password"
|
||||
autocomplete="off"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="姓名" prop="NAME">
|
||||
<el-input v-model="form.NAME" placeholder="这里输入姓名..." />
|
||||
</el-form-item>
|
||||
<el-form-item label="邮箱" prop="EMAIL">
|
||||
<el-input v-model="form.EMAIL" placeholder="这里输入邮箱..." />
|
||||
</el-form-item>
|
||||
<el-form-item label="备注" prop="BZ">
|
||||
<el-input
|
||||
:rows="3"
|
||||
v-model="form.BZ"
|
||||
type="textarea"
|
||||
placeholder="这里输入备注..."
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<el-button type="primary" @click="fnSubmit"> 确认修改 </el-button>
|
||||
<el-button @click="fnClose">关闭</el-button>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { getVerifyDuplicateEmail, setUserInfo } from "@/request/api.js";
|
||||
import { useVModels } from "@vueuse/core";
|
||||
import { debounce } from "throttle-debounce";
|
||||
import useFormValidate from "@/assets/js/useFormValidate.js";
|
||||
import { ElMessage } from "element-plus";
|
||||
import { useUserStore } from "@/pinia/user.js";
|
||||
import { ref } from "vue";
|
||||
|
||||
const userStore = useUserStore();
|
||||
const props = defineProps({
|
||||
visible: {
|
||||
type: Boolean,
|
||||
required: true,
|
||||
default: false,
|
||||
},
|
||||
form: {
|
||||
type: Object,
|
||||
required: true,
|
||||
default: () => ({}),
|
||||
},
|
||||
});
|
||||
const emits = defineEmits(["update:visible", "update:form"]);
|
||||
const { visible, form } = useVModels(props, emits);
|
||||
const formRef = ref(null);
|
||||
const validatePass = (rule, value, callback) => {
|
||||
if (form.value.newpwd && !value) {
|
||||
callback(new Error("请再次输入新密码"));
|
||||
} else if (form.value.newpwd && value && value !== form.value.newpwd) {
|
||||
callback(new Error("两次输入密码不一致!"));
|
||||
} else callback();
|
||||
};
|
||||
const validateEmail = async (rule, value, callback) => {
|
||||
if (value) {
|
||||
try {
|
||||
await getVerifyDuplicateEmail({
|
||||
EMAIL: value,
|
||||
USERNAME: form.value.USERNAME,
|
||||
});
|
||||
callback();
|
||||
} catch {
|
||||
callback(new Error("邮箱重复"));
|
||||
}
|
||||
} else callback();
|
||||
};
|
||||
const rules = {
|
||||
NAME: [{ required: true, message: "姓名不能为空", trigger: "blur" }],
|
||||
EMAIL: [
|
||||
{ required: false, message: "请输入邮箱", trigger: "blur" },
|
||||
{
|
||||
pattern: /^([a-zA-Z0-9_-])+@([a-zA-Z0-9_-])+(.[a-zA-Z0-9_-])+/,
|
||||
message: "请输入正确的邮箱",
|
||||
},
|
||||
{ validator: validateEmail, trigger: "blur" },
|
||||
],
|
||||
newpwd: [
|
||||
{ required: false, message: "请输入新密码", trigger: "blur" },
|
||||
{ min: 6, max: 18, message: "密码长度为6-18位", trigger: "blur" },
|
||||
],
|
||||
newpassword1: [{ required: false, validator: validatePass, trigger: "blur" }],
|
||||
};
|
||||
const fnSubmit = debounce(
|
||||
1000,
|
||||
async () => {
|
||||
await useFormValidate(formRef);
|
||||
await setUserInfo({
|
||||
USER_ID: userStore.getUserInfo.USER_ID,
|
||||
OPERATIONTYPE: "1",
|
||||
...form.value,
|
||||
PASSWORD: form.value.newpwd,
|
||||
});
|
||||
fnClose();
|
||||
ElMessage.success("修改成功");
|
||||
},
|
||||
{ atBegin: true }
|
||||
);
|
||||
const fnClose = () => {
|
||||
formRef.value.resetFields();
|
||||
visible.value = false;
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss"></style>
|
|
@ -0,0 +1,270 @@
|
|||
<template>
|
||||
<div class="header">
|
||||
<div class="left">
|
||||
<div class="logo">管理平台</div>
|
||||
<div class="menu">
|
||||
<ul>
|
||||
<template v-for="(item, index) in MENU" :key="index">
|
||||
<li
|
||||
@click="switchMenu(item.model)"
|
||||
:class="{ active: item.model === menuStore.getModel }"
|
||||
>
|
||||
<div v-for="item1 in 4" :key="item1" :class="'horn' + item1" />
|
||||
<div class="title">{{ item.title }}</div>
|
||||
</li>
|
||||
</template>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<div class="user">
|
||||
<el-dropdown
|
||||
trigger="click"
|
||||
placement="bottom-end"
|
||||
@command="dropdownCommand"
|
||||
>
|
||||
<div class="user_info">
|
||||
<el-avatar :size="23" fit="fill" :src="data.avatar" />
|
||||
<span>{{ userStore.getUserInfo.NAME }}</span>
|
||||
<icon-down theme="filled" size="16" fill="#a2c2d3" :strokeWidth="3" />
|
||||
</div>
|
||||
<template #dropdown>
|
||||
<el-dropdown-menu>
|
||||
<el-dropdown-item command="modifyInformation">
|
||||
修改信息
|
||||
</el-dropdown-item>
|
||||
<el-dropdown-item command="modifyAvatar">
|
||||
修改头像
|
||||
</el-dropdown-item>
|
||||
<el-dropdown-item command="signOut">退出</el-dropdown-item>
|
||||
</el-dropdown-menu>
|
||||
</template>
|
||||
</el-dropdown>
|
||||
</div>
|
||||
<update-info
|
||||
v-model:visible="data.userDialog.visible"
|
||||
v-model:form="data.userDialog.form"
|
||||
/>
|
||||
<update-avatar
|
||||
v-model:visible="data.avatarDialog.visible"
|
||||
v-model:form="data.avatarDialog.form"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { onBeforeUnmount, reactive } from "vue";
|
||||
import { useRouter } from "vue-router";
|
||||
import { useMenuStore } from "@/pinia/menu";
|
||||
import { useUserStore } from "@/pinia/user";
|
||||
import { MENU } from "@/assets/js/constant";
|
||||
import { getInfo, getUserInfo, logout } from "@/request/api";
|
||||
import { useMiscellaneousStore } from "@/pinia/miscellaneous.js";
|
||||
import UpdateInfo from "./components/update_info.vue";
|
||||
import UpdateAvatar from "./components/update_avatar.vue";
|
||||
import { checkImgExists, addingPrefixToFile } from "@/assets/js/utils.js";
|
||||
|
||||
defineOptions({
|
||||
name: "LayoutHeader",
|
||||
});
|
||||
const FILE_URL = import.meta.env.VITE_FILE_URL;
|
||||
let webSocket;
|
||||
const router = useRouter();
|
||||
const menuStore = useMenuStore();
|
||||
const userStore = useUserStore();
|
||||
const miscellaneousStore = useMiscellaneousStore();
|
||||
const data = reactive({
|
||||
avatar: "",
|
||||
userDialog: {
|
||||
visible: false,
|
||||
form: {},
|
||||
},
|
||||
avatarDialog: {
|
||||
visible: false,
|
||||
form: {},
|
||||
},
|
||||
});
|
||||
const dropdownCommand = async (command) => {
|
||||
if (command === "signOut") {
|
||||
await fnSignOut();
|
||||
}
|
||||
if (command === "modifyInformation") {
|
||||
await fnGetUserInfo();
|
||||
}
|
||||
if (command === "modifyAvatar") {
|
||||
data.avatarDialog.visible = true;
|
||||
data.avatarDialog.form.file = addingPrefixToFile([
|
||||
{ FILEPATH: userStore.getUserInfo.userPhoto },
|
||||
]);
|
||||
}
|
||||
};
|
||||
const fnGetUserInfo = async () => {
|
||||
const resData = await getUserInfo({
|
||||
USER_ID: userStore.getUserInfo.USER_ID,
|
||||
});
|
||||
data.userDialog.visible = true;
|
||||
data.userDialog.form = resData.pd;
|
||||
};
|
||||
const switchMenu = (model) => {
|
||||
menuStore.setModel(model);
|
||||
};
|
||||
const fnGetInfo = async () => {
|
||||
const resData = await getInfo();
|
||||
try {
|
||||
await checkImgExists(FILE_URL + resData.userPhoto);
|
||||
data.avatar = FILE_URL + resData.userPhoto;
|
||||
} catch {
|
||||
data.avatar = new URL(
|
||||
"../../../assets/images/public/tx.png",
|
||||
import.meta.url
|
||||
).href;
|
||||
}
|
||||
userStore.setUserInfo({
|
||||
...userStore.getUserInfo,
|
||||
...resData,
|
||||
});
|
||||
miscellaneousStore.setOnlineAddress(resData.onlineAdress);
|
||||
webSocket = new WebSocket(encodeURI("ws://" + resData.onlineAdress));
|
||||
webSocket.onopen = () => {
|
||||
webSocket.send("[join]" + resData.USERNAME);
|
||||
};
|
||||
webSocket.onmessage = (message) => {
|
||||
const messageMsg = JSON.parse(message.data);
|
||||
if (messageMsg.type === "goOut") {
|
||||
fnSignOut();
|
||||
} else if (messageMsg.type === "thegoout") {
|
||||
fnSignOut();
|
||||
}
|
||||
};
|
||||
};
|
||||
fnGetInfo();
|
||||
const fnSignOut = async () => {
|
||||
await logout();
|
||||
webSocket && webSocket.close();
|
||||
userStore.$reset();
|
||||
await router.replace("/login");
|
||||
};
|
||||
onBeforeUnmount(() => {
|
||||
webSocket && webSocket.close();
|
||||
webSocket = null;
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.header {
|
||||
background-image: url("/src/assets/images/public/headerbg.png");
|
||||
background-repeat: no-repeat;
|
||||
background-size: 660px 70px;
|
||||
background-color: #030f2f;
|
||||
border-bottom: 1px solid #1f3869;
|
||||
position: relative;
|
||||
|
||||
.left {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
position: relative;
|
||||
z-index: 99;
|
||||
width: max-content;
|
||||
|
||||
.logo {
|
||||
width: 500px;
|
||||
height: var(--el-header-height);
|
||||
line-height: var(--el-header-height);
|
||||
padding-left: 20px;
|
||||
color: #fff;
|
||||
font-size: 20px;
|
||||
font-weight: 700;
|
||||
font-family: cursive;
|
||||
}
|
||||
|
||||
.menu {
|
||||
ul {
|
||||
display: flex;
|
||||
|
||||
li {
|
||||
width: 120px;
|
||||
height: 40px;
|
||||
line-height: 40px;
|
||||
text-align: center;
|
||||
cursor: pointer;
|
||||
background-color: #051748;
|
||||
clip-path: polygon(0% 0%, 80% 0%, 100% 100%, 20% 100%);
|
||||
position: relative;
|
||||
box-shadow: rgba(11, 62, 110, 0.7) 0 0 13px inset;
|
||||
color: #fff;
|
||||
|
||||
&.active {
|
||||
background-image: url("/src/assets/images/public/tguang.png");
|
||||
background-repeat: no-repeat;
|
||||
background-size: 100% 100%;
|
||||
}
|
||||
|
||||
@for $i from 1 through 4 {
|
||||
.horn#{$i} {
|
||||
width: 8px;
|
||||
height: 8px;
|
||||
background-image: url("/src/assets/images/public/menu_horn#{$i}.png");
|
||||
background-repeat: no-repeat;
|
||||
background-size: 100% 100%;
|
||||
position: absolute;
|
||||
z-index: 2;
|
||||
@if $i == 1 {
|
||||
top: 0;
|
||||
left: 2px;
|
||||
}
|
||||
@if $i == 2 {
|
||||
top: 0;
|
||||
right: 21px;
|
||||
}
|
||||
@if $i == 3 {
|
||||
bottom: 0;
|
||||
left: 21px;
|
||||
}
|
||||
@if $i == 4 {
|
||||
bottom: 0;
|
||||
right: 2px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.title {
|
||||
padding-left: 3px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.user {
|
||||
width: 500px;
|
||||
height: 70px;
|
||||
background-image: url("/src/assets/images/public/userbg.jpg");
|
||||
background-repeat: no-repeat;
|
||||
background-size: 100% 100%;
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
align-items: center;
|
||||
position: absolute;
|
||||
right: 0;
|
||||
top: 0;
|
||||
border-bottom: 1px solid #1f3869;
|
||||
|
||||
.el-dropdown {
|
||||
margin-right: 55px;
|
||||
cursor: pointer;
|
||||
position: absolute;
|
||||
z-index: 999;
|
||||
}
|
||||
|
||||
.user_info {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
span:nth-child(2) {
|
||||
color: #ece8e8;
|
||||
margin-left: 10px;
|
||||
margin-right: 10px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
|
@ -0,0 +1,50 @@
|
|||
<template>
|
||||
<el-container>
|
||||
<el-header>
|
||||
<layout-header />
|
||||
</el-header>
|
||||
<el-container>
|
||||
<el-aside>
|
||||
<el-scrollbar style="height: var(--el-aside-scrollbar-height)">
|
||||
<el-menu
|
||||
router
|
||||
unique-opened
|
||||
:default-active="$route.meta.activeMenu || $route.path"
|
||||
>
|
||||
<layout-menu :menus="routes" />
|
||||
</el-menu>
|
||||
</el-scrollbar>
|
||||
</el-aside>
|
||||
<el-main>
|
||||
<layout-breadcrumb v-if="route.meta.isBreadcrumb !== false" />
|
||||
<el-scrollbar style="height: var(--el-main-scrollbar-height)">
|
||||
<router-view v-slot="{ Component }">
|
||||
<transition name="view" mode="out-in">
|
||||
<div :key="$route.path">
|
||||
<component :is="Component"></component>
|
||||
</div>
|
||||
</transition>
|
||||
</router-view>
|
||||
</el-scrollbar>
|
||||
</el-main>
|
||||
</el-container>
|
||||
</el-container>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import LayoutBreadcrumb from "@/components/layout/breadcrumb/index";
|
||||
import LayoutHeader from "@/components/layout/header/index";
|
||||
import LayoutMenu from "@/components/layout/menu/index";
|
||||
import { computed } from "vue";
|
||||
import { useMenuStore } from "@/pinia/menu";
|
||||
import { useRoute } from "vue-router";
|
||||
|
||||
defineOptions({
|
||||
name: "layout",
|
||||
});
|
||||
const menuStore = useMenuStore();
|
||||
const route = useRoute();
|
||||
const routes = computed(() => menuStore.getMenus);
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss"></style>
|
|
@ -0,0 +1,70 @@
|
|||
<template>
|
||||
<template v-for="menu in props.menus" :key="menu.path">
|
||||
<!-- 没有二级导航-->
|
||||
<el-menu-item
|
||||
v-if="fnIsShowMenuItem(menu)"
|
||||
:index="menu.path"
|
||||
:route="{ path: menu.path }"
|
||||
>
|
||||
<component
|
||||
:is="'icon-application-menu'"
|
||||
theme="filled"
|
||||
fill="#a5b2c2"
|
||||
size="18"
|
||||
:strokeWidth="3"
|
||||
style="margin-right: 15px"
|
||||
/>
|
||||
<span>{{ menu.meta?.title }}</span>
|
||||
</el-menu-item>
|
||||
<!-- 有二级导航-->
|
||||
<el-sub-menu v-else-if="fnIsShowSubmenu(menu)" :index="menu.path">
|
||||
<template #title>
|
||||
<component
|
||||
:is="'icon-application-menu'"
|
||||
theme="filled"
|
||||
fill="#a5b2c2"
|
||||
size="18"
|
||||
:strokeWidth="3"
|
||||
style="margin-right: 15px"
|
||||
/>
|
||||
<span>{{ menu.meta?.title }}</span>
|
||||
</template>
|
||||
<!-- 递归调用当前组件生成导航-->
|
||||
<layout-menu :menus="menu.children" />
|
||||
</el-sub-menu>
|
||||
</template>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
defineOptions({
|
||||
name: "LayoutMenu",
|
||||
});
|
||||
const props = defineProps({
|
||||
menus: {
|
||||
type: Array,
|
||||
default: () => [],
|
||||
},
|
||||
});
|
||||
const fnIsShowMenuItem = (menu) => {
|
||||
if (menu.meta?.isMenu === false) {
|
||||
return false;
|
||||
}
|
||||
if (menu.meta?.isSubMenu === false) {
|
||||
return true;
|
||||
} else {
|
||||
return !menu.children || menu.children.length === 0;
|
||||
}
|
||||
};
|
||||
const fnIsShowSubmenu = (menu) => {
|
||||
if (menu.meta?.isMenu === false) {
|
||||
return false;
|
||||
}
|
||||
if (menu.meta?.isSubMenu === false) {
|
||||
return false;
|
||||
} else {
|
||||
return menu.children && menu.children.length > 0;
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss"></style>
|
|
@ -0,0 +1,70 @@
|
|||
<template>
|
||||
<el-tree-select
|
||||
ref="treeSelectRef"
|
||||
v-model="modelValue"
|
||||
:data="data"
|
||||
:props="{
|
||||
value: 'id',
|
||||
children: 'nodes',
|
||||
label: 'name',
|
||||
}"
|
||||
node-key="id"
|
||||
:render-after-expand="false"
|
||||
accordion
|
||||
check-strictly
|
||||
:clearable="true"
|
||||
:disabled="disabled"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import {
|
||||
layoutFnGetTrainingIndustryType,
|
||||
layoutFnGetTrainingPlateType,
|
||||
layoutFnGetTrainingPostType,
|
||||
} from "@/assets/js/data_dictionary";
|
||||
import { ref } from "vue";
|
||||
import { useVModel } from "@vueuse/core";
|
||||
|
||||
defineOptions({
|
||||
name: "LayoutLearningTrainType",
|
||||
});
|
||||
const props = defineProps({
|
||||
modelValue: {
|
||||
type: String,
|
||||
required: true,
|
||||
default: "",
|
||||
},
|
||||
type: {
|
||||
type: String,
|
||||
required: true,
|
||||
default: "",
|
||||
validator: (value) => {
|
||||
if (["industry", "post", "plate"].includes(value)) {
|
||||
return true;
|
||||
} else {
|
||||
throw new Error("type必须是industry、post、plate之一");
|
||||
}
|
||||
},
|
||||
},
|
||||
disabled: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
});
|
||||
const emits = defineEmits(["update:modelValue"]);
|
||||
const modelValue = useVModel(props, "modelValue", emits);
|
||||
const treeSelectRef = ref(null);
|
||||
const getCurrentNode = () => {
|
||||
return treeSelectRef.value.getCurrentNode();
|
||||
};
|
||||
defineExpose({
|
||||
getCurrentNode,
|
||||
});
|
||||
let data = [];
|
||||
if (props.type === "industry") data = await layoutFnGetTrainingIndustryType();
|
||||
if (props.type === "post") data = await layoutFnGetTrainingPostType();
|
||||
if (props.type === "plate") data = await layoutFnGetTrainingPlateType();
|
||||
</script>
|
||||
|
||||
<style scoped></style>
|
|
@ -0,0 +1,55 @@
|
|||
<template>
|
||||
<el-tree-select
|
||||
ref="treeSelectRef"
|
||||
v-model="modelValue"
|
||||
:data="data"
|
||||
node-key="id"
|
||||
:props="{
|
||||
children: 'children',
|
||||
label: 'NAME',
|
||||
}"
|
||||
:render-after-expand="false"
|
||||
accordion
|
||||
check-strictly
|
||||
:clearable="true"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { layoutFnGetRegulatoryType } from "@/assets/js/data_dictionary";
|
||||
import { ref, watchEffect } from "vue";
|
||||
import { useVModel } from "@vueuse/core";
|
||||
|
||||
defineOptions({
|
||||
name: "LayoutRegulatoryType",
|
||||
});
|
||||
const props = defineProps({
|
||||
modelValue: {
|
||||
type: String,
|
||||
required: true,
|
||||
default: "",
|
||||
},
|
||||
territory: {
|
||||
type: Array,
|
||||
required: true,
|
||||
default: () => [],
|
||||
},
|
||||
});
|
||||
const emits = defineEmits(["update:modelValue"]);
|
||||
const modelValue = useVModel(props, "modelValue", emits);
|
||||
const treeSelectRef = ref(null);
|
||||
const data = ref([]);
|
||||
const fnGetData = async () => {
|
||||
const { value } = await layoutFnGetRegulatoryType({
|
||||
PROVINCE: props.territory[0],
|
||||
CITY: props.territory[1],
|
||||
COUNTY: props.territory[2],
|
||||
});
|
||||
data.value = value;
|
||||
};
|
||||
watchEffect(() => {
|
||||
if (props.territory.length > 0) fnGetData();
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped></style>
|
|
@ -0,0 +1,152 @@
|
|||
<template>
|
||||
<el-table
|
||||
ref="tableRef"
|
||||
size="small"
|
||||
:data="props.data"
|
||||
:border="border"
|
||||
:stripe="stripe"
|
||||
:height="height"
|
||||
:max-height="maxHeight"
|
||||
:highlight-current-row="highlightCurrentRow"
|
||||
:row-key="rowKey"
|
||||
:row-class-name="rowClassName"
|
||||
:show-header="showHeader"
|
||||
:show-summary="showSummary"
|
||||
:summary-method="summaryMethod"
|
||||
@row-click="rowClick"
|
||||
@row-dblclick="rowDblclick"
|
||||
>
|
||||
<slot></slot>
|
||||
</el-table>
|
||||
<div class="table_footer">
|
||||
<div>
|
||||
<slot name="button"></slot>
|
||||
</div>
|
||||
<el-pagination
|
||||
v-if="props.showPagination"
|
||||
small
|
||||
:current-page="props.pagination.currentPage || 1"
|
||||
:page-size="props.pagination.pageSize || 10"
|
||||
layout="total, sizes, prev, pager, next, jumper"
|
||||
:total="props.pagination.total || 0"
|
||||
@update:current-page="handleCurrentChange"
|
||||
@update:page-size="handleSizeChange"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref } from "vue";
|
||||
|
||||
defineOptions({
|
||||
name: "LayoutTable",
|
||||
});
|
||||
const props = defineProps({
|
||||
data: {
|
||||
type: Array,
|
||||
default: () => [],
|
||||
},
|
||||
pagination: {
|
||||
type: Object,
|
||||
default: () => ({
|
||||
currentPage: 1,
|
||||
pageSize: 10,
|
||||
total: 0,
|
||||
}),
|
||||
},
|
||||
showPagination: {
|
||||
type: Boolean,
|
||||
default: true,
|
||||
},
|
||||
stripe: {
|
||||
type: Boolean,
|
||||
default: true,
|
||||
},
|
||||
border: {
|
||||
type: Boolean,
|
||||
default: true,
|
||||
},
|
||||
showHeader: {
|
||||
type: Boolean,
|
||||
default: true,
|
||||
},
|
||||
highlightCurrentRow: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
showSummary: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
rowKey: {
|
||||
type: [String, Function],
|
||||
},
|
||||
maxHeight: {
|
||||
type: [String, Number],
|
||||
},
|
||||
height: {
|
||||
type: [String, Number],
|
||||
},
|
||||
rowClassName: {
|
||||
type: Function,
|
||||
},
|
||||
summaryMethod: {
|
||||
type: Function,
|
||||
},
|
||||
});
|
||||
const emits = defineEmits([
|
||||
"update:pagination",
|
||||
"get-data",
|
||||
"row-click",
|
||||
"row-dblclick",
|
||||
]);
|
||||
const tableRef = ref(null);
|
||||
const handleCurrentChange = (val) => {
|
||||
emits("update:pagination", {
|
||||
currentPage: val,
|
||||
pageSize: props.pagination.pageSize,
|
||||
total: props.pagination.total,
|
||||
});
|
||||
emits("get-data");
|
||||
};
|
||||
const handleSizeChange = (val) => {
|
||||
emits("update:pagination", {
|
||||
currentPage: 1,
|
||||
pageSize: val,
|
||||
total: props.pagination.total,
|
||||
});
|
||||
emits("get-data");
|
||||
};
|
||||
const rowClick = (row, column, event) => {
|
||||
emits("row-click", row, column, event);
|
||||
};
|
||||
const rowDblclick = (row, column, event) => {
|
||||
emits("row-dblclick", row, column, event);
|
||||
};
|
||||
const getSelectionRows = () => {
|
||||
return tableRef.value.getSelectionRows();
|
||||
};
|
||||
const clearSelection = () => {
|
||||
return tableRef.value.clearSelection();
|
||||
};
|
||||
const toggleRowSelection = (id, selected) => {
|
||||
for (let i = 0; i < props.data.length; i++) {
|
||||
if (props.data[i][props.rowKey] === id) {
|
||||
tableRef.value.toggleRowSelection(props.data[i], selected);
|
||||
}
|
||||
}
|
||||
};
|
||||
defineExpose({
|
||||
getSelectionRows,
|
||||
clearSelection,
|
||||
toggleRowSelection,
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.table_footer {
|
||||
margin-top: 20px;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
}
|
||||
</style>
|
|
@ -0,0 +1,46 @@
|
|||
<template>
|
||||
<layout-cascader
|
||||
id="e725d2a91b8248f4b8f49889038df7de"
|
||||
v-model="modelValue"
|
||||
ref="cascaderRef"
|
||||
:check-strictly="checkStrictly"
|
||||
:level="level"
|
||||
value="BIANMA"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { useVModel } from "@vueuse/core";
|
||||
import { ref } from "vue";
|
||||
import LayoutCascader from "@/components/cascader/index.vue";
|
||||
|
||||
defineOptions({
|
||||
name: "LayoutTerritory",
|
||||
});
|
||||
const props = defineProps({
|
||||
modelValue: {
|
||||
type: Array,
|
||||
default: () => [],
|
||||
required: true,
|
||||
},
|
||||
level: {
|
||||
type: [Number, String],
|
||||
default: 3,
|
||||
},
|
||||
checkStrictly: {
|
||||
type: Boolean,
|
||||
default: true,
|
||||
},
|
||||
});
|
||||
const emits = defineEmits(["update:modelValue"]);
|
||||
const modelValue = useVModel(props, "modelValue", emits);
|
||||
const cascaderRef = ref(null);
|
||||
const getCheckedNodes = () => {
|
||||
return cascaderRef.value.getCheckedNodes();
|
||||
};
|
||||
defineExpose({
|
||||
getCheckedNodes,
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped></style>
|
|
@ -0,0 +1,170 @@
|
|||
<template>
|
||||
<el-upload
|
||||
style="width: 100%"
|
||||
ref="uploadRef"
|
||||
:file-list="fileList"
|
||||
:action="action"
|
||||
multiple
|
||||
:limit="limit"
|
||||
:list-type="listType"
|
||||
:auto-upload="autoUpload"
|
||||
:disabled="disabled"
|
||||
:accept="accept"
|
||||
:on-remove="onRemove"
|
||||
:before-remove="beforeRemove"
|
||||
:on-change="onChange"
|
||||
:on-exceed="onExceed"
|
||||
:on-preview="onPreview"
|
||||
:http-request="httpRequest"
|
||||
:show-file-list="showFileList"
|
||||
:class="{ hide: fileList.length === limit }"
|
||||
>
|
||||
<icon-plus
|
||||
v-if="listType === 'picture-card'"
|
||||
theme="filled"
|
||||
size="32"
|
||||
fill="#fff"
|
||||
:strokeWidth="3"
|
||||
/>
|
||||
<el-button v-else type="primary">点击选择文件上传</el-button>
|
||||
<template #tip>
|
||||
<div class="ml text-red"><slot name="tip"></slot></div>
|
||||
</template>
|
||||
</el-upload>
|
||||
<el-dialog v-model="visible" title="查看图片">
|
||||
<img
|
||||
:src="imageUrl"
|
||||
alt="Preview Image"
|
||||
style="width: 100%; object-fit: scale-down"
|
||||
/>
|
||||
</el-dialog>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref } from "vue";
|
||||
import { ElMessage, ElMessageBox } from "element-plus";
|
||||
import { setDeleteImg } from "@/request/api";
|
||||
|
||||
defineOptions({
|
||||
name: "LayoutUpload",
|
||||
});
|
||||
const props = defineProps({
|
||||
fileList: {
|
||||
type: Array,
|
||||
default: () => [],
|
||||
required: true,
|
||||
},
|
||||
autoUpload: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
action: {
|
||||
type: String,
|
||||
default: "",
|
||||
},
|
||||
limit: {
|
||||
type: Number,
|
||||
default: 1,
|
||||
},
|
||||
listType: {
|
||||
type: String,
|
||||
default: "text",
|
||||
},
|
||||
accept: {
|
||||
type: String,
|
||||
default: "",
|
||||
},
|
||||
ratio: {
|
||||
type: String,
|
||||
default: "",
|
||||
},
|
||||
disabled: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
deleteToServer: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
showFileList: {
|
||||
type: Boolean,
|
||||
default: true,
|
||||
},
|
||||
httpRequest: {
|
||||
type: Function,
|
||||
},
|
||||
size: {
|
||||
type: Number,
|
||||
default: 0,
|
||||
},
|
||||
});
|
||||
const visible = ref(false);
|
||||
const imageUrl = ref("");
|
||||
const emits = defineEmits(["update:file-list", "preview"]);
|
||||
const uploadRef = ref(null);
|
||||
const onExceed = () => {
|
||||
ElMessage.warning(`最多上传${props.limit}个文件`);
|
||||
};
|
||||
const beforeRemove = async (uploadFile) => {
|
||||
if (props.deleteToServer && uploadFile.IMGFILES_ID) {
|
||||
await ElMessageBox.confirm("确定要删除吗?", {
|
||||
type: "warning",
|
||||
});
|
||||
await setDeleteImg({
|
||||
IMGFILES_ID: uploadFile.IMGFILES_ID,
|
||||
});
|
||||
}
|
||||
};
|
||||
const onRemove = (uploadFile, uploadFiles) => {
|
||||
emits("update:file-list", uploadFiles);
|
||||
};
|
||||
const onChange = (uploadFile, uploadFiles) => {
|
||||
const accept = props.accept && props.accept.split(",");
|
||||
const ratio = props.ratio && props.ratio.split("*");
|
||||
const suffix = uploadFile.raw.name.substring(
|
||||
uploadFile.raw.name.lastIndexOf("."),
|
||||
uploadFile.raw.name.length
|
||||
);
|
||||
const size = props.size * 1024 * 1024;
|
||||
if (ratio) {
|
||||
const img = new Image();
|
||||
img.src = uploadFile.url;
|
||||
img.onload = () => {
|
||||
if (img.width !== +ratio[0] && img.height !== +ratio[1]) {
|
||||
ElMessage.warning(`只能上传${props.ratio}分辨率的图片`);
|
||||
uploadRef.value.handleRemove(uploadFile);
|
||||
}
|
||||
};
|
||||
}
|
||||
if (size) {
|
||||
if (uploadFile.size > size) {
|
||||
ElMessage.warning(`文件大小不能超过${props.size}M`);
|
||||
uploadRef.value.handleRemove(uploadFile);
|
||||
}
|
||||
}
|
||||
if (accept) {
|
||||
if (accept.includes(suffix)) {
|
||||
emits("update:file-list", uploadFiles);
|
||||
} else {
|
||||
ElMessage.warning(`只能上传${props.accept}格式的文件`);
|
||||
uploadRef.value.handleRemove(uploadFile);
|
||||
}
|
||||
} else {
|
||||
emits("update:file-list", uploadFiles);
|
||||
}
|
||||
};
|
||||
const onPreview = (uploadFile) => {
|
||||
if (props.listType === "picture-card") {
|
||||
visible.value = true;
|
||||
imageUrl.value = uploadFile.url;
|
||||
} else {
|
||||
emits("preview", uploadFile);
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.hide :deep(.el-upload--picture-card) {
|
||||
display: none;
|
||||
}
|
||||
</style>
|
|
@ -0,0 +1,212 @@
|
|||
<template>
|
||||
<div class="mi-captcha">
|
||||
<div class="mi-captcha-content">
|
||||
<!-- 没有进行验证-->
|
||||
<div
|
||||
class="mi-captcha-radar"
|
||||
v-if="!verificationPass"
|
||||
@click="verificationShow = true"
|
||||
>
|
||||
<div class="mi-captcha-radar-ready">
|
||||
<div class="mi-captcha-radar-ring" />
|
||||
<div class="mi-captcha-radar-dot" />
|
||||
</div>
|
||||
<div class="mi-captcha-radar-tip">点击按钮进行验证</div>
|
||||
</div>
|
||||
<!-- 验证通过-->
|
||||
<div
|
||||
class="mi-captcha-radar mi-captcha-radar-pass"
|
||||
v-if="verificationPass"
|
||||
>
|
||||
<div class="mi-captcha-radar-success mi-captcha-radar-success-icon">
|
||||
<span role="img" aria-label="verified">
|
||||
<svg
|
||||
focusable="false"
|
||||
class=""
|
||||
data-icon="verified"
|
||||
width="1em"
|
||||
height="1em"
|
||||
fill="currentColor"
|
||||
aria-hidden="true"
|
||||
viewBox="64 64 896 896"
|
||||
>
|
||||
<path
|
||||
d="M447.8 588.8l-7.3-32.5c-.2-1-.6-1.9-1.1-2.7a7.94 7.94 0 00-11.1-2.2L405 567V411c0-4.4-3.6-8-8-8h-81c-4.4 0-8 3.6-8 8v36c0 4.4 3.6 8 8 8h37v192.4a8 8 0 0012.7 6.5l79-56.8c2.6-1.9 3.8-5.1 3.1-8.3zm-56.7-216.6l.2.2c3.2 3 8.3 2.8 11.3-.5l24.1-26.2a8.1 8.1 0 00-.3-11.2l-53.7-52.1a8 8 0 00-11.2.1l-24.7 24.7c-3.1 3.1-3.1 8.2.1 11.3l54.2 53.7z"
|
||||
/>
|
||||
<path
|
||||
d="M866.9 169.9L527.1 54.1C523 52.7 517.5 52 512 52s-11 .7-15.1 2.1L157.1 169.9c-8.3 2.8-15.1 12.4-15.1 21.2v482.4c0 8.8 5.7 20.4 12.6 25.9L499.3 968c3.5 2.7 8 4.1 12.6 4.1s9.2-1.4 12.6-4.1l344.7-268.6c6.9-5.4 12.6-17 12.6-25.9V191.1c.2-8.8-6.6-18.3-14.9-21.2zM810 654.3L512 886.5 214 654.3V226.7l298-101.6 298 101.6v427.6z"
|
||||
/>
|
||||
<path
|
||||
d="M452 297v36c0 4.4 3.6 8 8 8h108v274h-38V405c0-4.4-3.6-8-8-8h-35c-4.4 0-8 3.6-8 8v210h-31c-4.4 0-8 3.6-8 8v37c0 4.4 3.6 8 8 8h244c4.4 0 8-3.6 8-8v-37c0-4.4-3.6-8-8-8h-72V493h58c4.4 0 8-3.6 8-8v-35c0-4.4-3.6-8-8-8h-58V341h63c4.4 0 8-3.6 8-8v-36c0-4.4-3.6-8-8-8H460c-4.4 0-8 3.6-8 8z"
|
||||
/>
|
||||
</svg>
|
||||
</span>
|
||||
</div>
|
||||
<div class="mi-captcha-radar-tip">通过验证</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<verification
|
||||
:show="verificationShow"
|
||||
@success="verificationSuccess"
|
||||
@close="verificationClose"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import Verification from "vue3-puzzle-vcode";
|
||||
import { ref } from "vue";
|
||||
|
||||
defineOptions({
|
||||
name: "LayoutVerification",
|
||||
});
|
||||
defineProps({
|
||||
verificationPass: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
});
|
||||
const emits = defineEmits(["update:verificationPass"]);
|
||||
const verificationShow = ref(false);
|
||||
const verificationClose = () => {
|
||||
verificationShow.value = false;
|
||||
};
|
||||
const verificationSuccess = () => {
|
||||
emits("update:verificationPass", true);
|
||||
verificationClose();
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.mi-captcha {
|
||||
width: 100%;
|
||||
flex: 1;
|
||||
height: 2.625rem;
|
||||
font-family: "Pingfang SC", "Microsoft YaHei", "Monospaced Number",
|
||||
"Chinese Quote", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto,
|
||||
"PingFang SC", "Hiragino Sans GB", "Helvetica Neue", Helvetica, Arial,
|
||||
sans-serif;
|
||||
}
|
||||
|
||||
.mi-captcha-content {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.mi-captcha-radar {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: flex-start;
|
||||
flex-direction: row;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
background-color: #1d1e23;
|
||||
background-image: linear-gradient(315deg, #2e63d8 0%, #21438e 74%);
|
||||
border: 1px solid #2056cf;
|
||||
cursor: pointer;
|
||||
min-width: 10rem;
|
||||
position: relative;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.mi-captcha-radar-ready,
|
||||
.mi-captcha-radar-success {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
flex-direction: row;
|
||||
flex-wrap: nowrap;
|
||||
position: relative;
|
||||
transition: all 0.4s ease;
|
||||
width: 1.875rem;
|
||||
height: 1.875rem;
|
||||
margin: 0.375rem;
|
||||
}
|
||||
|
||||
.mi-captcha-radar-ring,
|
||||
.mi-captcha-radar-dot {
|
||||
position: absolute;
|
||||
border-radius: 50%;
|
||||
transform: scale(0.4);
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
box-shadow: inset 0 0 0 1px #2c67ec;
|
||||
background-image: linear-gradient(0, rgba(0, 0, 0, 0) 50%, #fff 50%),
|
||||
linear-gradient(0, #fff 50%, rgba(0, 0, 0, 0) 50%);
|
||||
}
|
||||
|
||||
.mi-captcha-radar-dot {
|
||||
background: #2c67ec;
|
||||
}
|
||||
|
||||
.mi-captcha-radar-ring {
|
||||
animation: mi-anim-wait 1s infinite;
|
||||
transform: scale(1);
|
||||
}
|
||||
|
||||
.mi-captcha-radar-success {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
flex-direction: row;
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
.mi-captcha-radar-success-icon {
|
||||
color: #f6ca9d;
|
||||
animation-name: mi-captcha-success;
|
||||
animation-timing-function: ease;
|
||||
animation-iteration-count: 1;
|
||||
animation-delay: 0.5s;
|
||||
animation-duration: 0.4s;
|
||||
font-size: 1.25rem;
|
||||
}
|
||||
|
||||
.mi-captcha-radar-tip {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: flex-start;
|
||||
flex-direction: row;
|
||||
height: 2.625rem;
|
||||
padding-left: 0.125rem;
|
||||
font-size: 0.875rem;
|
||||
color: #fff;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.mi-captcha-radar-pass .mi-captcha-radar-tip {
|
||||
color: #f6ca9d;
|
||||
}
|
||||
|
||||
@keyframes mi-captcha-success {
|
||||
25% {
|
||||
transform: rotate(25deg);
|
||||
}
|
||||
100% {
|
||||
transform: rotate(-360deg);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes mi-anim-wait {
|
||||
60% {
|
||||
transform: scale(0.75);
|
||||
}
|
||||
}
|
||||
|
||||
:deep {
|
||||
.vue-auth-box_ {
|
||||
background: #2e63d8 !important;
|
||||
border: 1px solid #2752b3 !important;
|
||||
}
|
||||
.mi-captcha-radar-pass .mi-captcha-radar-tip {
|
||||
color: #ffffff !important;
|
||||
}
|
||||
.mi-captcha-radar-success-icon {
|
||||
color: #ffffff !important;
|
||||
}
|
||||
}
|
||||
</style>
|
|
@ -0,0 +1,41 @@
|
|||
import { createApp } from "vue";
|
||||
import "@/assets/css/common.scss";
|
||||
import "@/assets/css/transition.scss";
|
||||
import "@/assets/css/element.scss";
|
||||
import "dayjs/locale/zh-cn";
|
||||
import App from "./App";
|
||||
import pinia from "./pinia";
|
||||
import router from "./router";
|
||||
import "normalize.css";
|
||||
import "animate.css";
|
||||
import "viewerjs/dist/viewer.css";
|
||||
import VueViewer from "v-viewer";
|
||||
import print from "vue3-print-nb";
|
||||
import button from "@/assets/js/button";
|
||||
import "./addRouters";
|
||||
import "element-plus/es/components/loading/style/css";
|
||||
import "element-plus/es/components/message/style/css";
|
||||
import "element-plus/es/components/message-box/style/css";
|
||||
import "element-plus/es/components/notification/style/css";
|
||||
import { install } from "@icon-park/vue-next/es/all";
|
||||
import LayoutTable from "@/components/table/index.vue";
|
||||
import LayoutCard from "@/components/card/index.vue";
|
||||
import ElDialog from "element-plus/es/components/dialog/index";
|
||||
|
||||
ElDialog.props.closeOnClickModal.default = false;
|
||||
ElDialog.props.closeOnPressEscape.default = false;
|
||||
const app = createApp(App);
|
||||
app.component("layout-table", LayoutTable);
|
||||
app.component("layout-card", LayoutCard);
|
||||
install(app, "icon");
|
||||
app
|
||||
.use(pinia)
|
||||
.use(router)
|
||||
.use(VueViewer, {
|
||||
defaultOptions: {
|
||||
zIndex: 9999,
|
||||
},
|
||||
})
|
||||
.use(print)
|
||||
.use(button)
|
||||
.mount("#app");
|
|
@ -0,0 +1,7 @@
|
|||
import { createPinia } from "pinia";
|
||||
import piniaPersistedstate from "pinia-plugin-persistedstate";
|
||||
|
||||
const pinia = createPinia();
|
||||
pinia.use(piniaPersistedstate);
|
||||
|
||||
export default pinia;
|
|
@ -0,0 +1,30 @@
|
|||
import { defineStore } from "pinia";
|
||||
|
||||
export const useMenuStore = defineStore("menuStore", {
|
||||
state: () => ({
|
||||
menusAll: [],
|
||||
menus: [],
|
||||
model: "",
|
||||
}),
|
||||
getters: {
|
||||
getMenus: (state) => state.menus,
|
||||
getModel: (state) => state.model,
|
||||
},
|
||||
actions: {
|
||||
setMenus(menus) {
|
||||
this.menusAll = menus;
|
||||
},
|
||||
setModel(model) {
|
||||
this.model = model;
|
||||
this.menus = [];
|
||||
for (let i = 0; i < this.menusAll.length; i++) {
|
||||
if (this.menusAll[i].meta.model === model) {
|
||||
this.menus.push(this.menusAll[i]);
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
persist: {
|
||||
storage: window.sessionStorage,
|
||||
},
|
||||
});
|
|
@ -0,0 +1,18 @@
|
|||
import { defineStore } from "pinia";
|
||||
|
||||
export const useMiscellaneousStore = defineStore("miscellaneousStore", {
|
||||
state: () => ({
|
||||
onlineAddress: false,
|
||||
}),
|
||||
getters: {
|
||||
getOnlineAddress: (state) => state.onlineAddress,
|
||||
},
|
||||
actions: {
|
||||
setOnlineAddress(onlineAddress) {
|
||||
this.onlineAddress = onlineAddress;
|
||||
},
|
||||
},
|
||||
persist: {
|
||||
storage: window.sessionStorage,
|
||||
},
|
||||
});
|
|
@ -0,0 +1,18 @@
|
|||
import { defineStore } from "pinia";
|
||||
|
||||
export const useRouterStore = defineStore("routerStore", {
|
||||
state: () => ({
|
||||
routers: [],
|
||||
}),
|
||||
getters: {
|
||||
getRouters: (state) => state.routers,
|
||||
},
|
||||
actions: {
|
||||
setRouters(routers) {
|
||||
this.routers = routers;
|
||||
},
|
||||
},
|
||||
persist: {
|
||||
storage: window.sessionStorage,
|
||||
},
|
||||
});
|
|
@ -0,0 +1,18 @@
|
|||
import { defineStore } from "pinia";
|
||||
|
||||
export const useUserStore = defineStore("userStore", {
|
||||
state: () => ({
|
||||
userInfo: {},
|
||||
}),
|
||||
getters: {
|
||||
getUserInfo: (state) => state.userInfo,
|
||||
},
|
||||
actions: {
|
||||
setUserInfo(userInfo) {
|
||||
this.userInfo = userInfo;
|
||||
},
|
||||
},
|
||||
persist: {
|
||||
storage: window.sessionStorage,
|
||||
},
|
||||
});
|
|
@ -0,0 +1,17 @@
|
|||
import { post, upload } from "./axios";
|
||||
|
||||
export const Login = (params) => post("/admin/check", params); // 登录
|
||||
export const logout = (params) => post("/main/logout", params); // 退出登录
|
||||
export const getAsyncRouter = (params) => post("/main/index", params); // 获取动态路由
|
||||
export const getUserInfo = (params) => post("/user/goEditMyInfo", params); // 获取用户信息
|
||||
export const setUserInfo = (params) => post("/user/editUserOwn", params); // 修改用户信息
|
||||
export const getVerifyDuplicateEmail = (params) =>
|
||||
post("/user/hasEmail", params); // 验证邮箱重复
|
||||
export const getVerifyDeduplicationUser = (params) =>
|
||||
post("/user/hasUser", params); // 用户名去重
|
||||
export const setAvatar = (params) => upload("/photo/saveNew", params); // 修改头像
|
||||
export const getInfo = (params) =>
|
||||
post("/head/getInfo", { loading: false, ...params }); // 获取用户信息
|
||||
export const setUploadImg = (params) => upload("/imgfiles/add", params); // 上传附件
|
||||
export const setDeleteImg = (params) => post("/imgfiles/delete", params); // 删除附件
|
||||
export const getViewImg = (params) => post("/imgfiles/listImgs", params); // 查看图片
|
|
@ -0,0 +1,153 @@
|
|||
import axios from "axios";
|
||||
import { ElLoading, ElMessage } from "element-plus";
|
||||
import router from "../router";
|
||||
import QS from "qs";
|
||||
|
||||
let loading = null;
|
||||
|
||||
function startLoading() {
|
||||
loading = ElLoading.service({
|
||||
lock: true,
|
||||
text: "加载中...",
|
||||
background: "rgba(0, 0, 0, 0.5)",
|
||||
});
|
||||
}
|
||||
|
||||
function endLoading() {
|
||||
loading && loading.close();
|
||||
}
|
||||
|
||||
axios.defaults.baseURL = import.meta.env[
|
||||
import.meta.env.DEV ? "VITE_PROXY" : "VITE_BASE_URL"
|
||||
];
|
||||
axios.defaults.timeout = 1000 * 60 * 10;
|
||||
// axios.defaults.withCredentials = true;
|
||||
axios.interceptors.request.use(
|
||||
(config) => {
|
||||
if (config.method === "get" || config.method === "GET") {
|
||||
if (QS.parse(config.params)?.loading !== "false") startLoading();
|
||||
}
|
||||
if (config.method === "post" || config.method === "POST") {
|
||||
if (QS.parse(config.data)?.loading !== "false") startLoading();
|
||||
}
|
||||
return config;
|
||||
},
|
||||
(error) => Promise.reject(error)
|
||||
);
|
||||
|
||||
axios.interceptors.response.use(
|
||||
(config) => {
|
||||
if (config.config.method === "get" || config.config.method === "GET") {
|
||||
if (QS.parse(config.config.params)?.loading !== "false") endLoading();
|
||||
}
|
||||
if (config.config.method === "post" || config.config.method === "POST") {
|
||||
if (QS.parse(config.config.data)?.loading !== "false") endLoading();
|
||||
}
|
||||
return config;
|
||||
},
|
||||
(error) => {
|
||||
console.log(error);
|
||||
if (error && error.response) {
|
||||
switch (error.response.status) {
|
||||
case 0:
|
||||
case 302:
|
||||
endLoading();
|
||||
ElMessage.error("登录失效,请重新登陆");
|
||||
router.push("/login").then();
|
||||
break;
|
||||
default:
|
||||
error.message = `连接错误${error.response.status}`;
|
||||
import.meta.env.DEV &&
|
||||
ElMessage.error(`连接错误${error.response.status}`);
|
||||
}
|
||||
} else {
|
||||
error.message = "连接到服务器失败";
|
||||
endLoading();
|
||||
ElMessage.error("登录失效,请重新登陆");
|
||||
router.push("/login").then();
|
||||
}
|
||||
return Promise.reject(error.message);
|
||||
}
|
||||
);
|
||||
|
||||
export function post(url, params) {
|
||||
return new Promise((resolve, reject) => {
|
||||
axios
|
||||
.post(url, QS.stringify(params), {
|
||||
headers: {
|
||||
"Content-Type": "application/x-www-form-urlencoded",
|
||||
},
|
||||
})
|
||||
.then((res) => {
|
||||
if (res.data.result === "success") {
|
||||
resolve(res.data);
|
||||
} else {
|
||||
ElMessage.error(
|
||||
res.data.msg ||
|
||||
res.data.resMsg ||
|
||||
res.data.resultStr ||
|
||||
"系统开小差了"
|
||||
);
|
||||
reject(res.data);
|
||||
}
|
||||
})
|
||||
.catch((err) => {
|
||||
reject(err);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
export function get(url, params) {
|
||||
return new Promise((resolve, reject) => {
|
||||
axios
|
||||
.get(url, {
|
||||
headers: {
|
||||
"Content-Type": "application/x-www-form-urlencoded",
|
||||
},
|
||||
params: QS.stringify(params),
|
||||
})
|
||||
.then((res) => {
|
||||
if (res.data.result === "success") {
|
||||
resolve(res.data);
|
||||
} else {
|
||||
ElMessage.error(
|
||||
res.data.msg ||
|
||||
res.data.resMsg ||
|
||||
res.data.resultStr ||
|
||||
"系统开小差了"
|
||||
);
|
||||
reject(res.data);
|
||||
}
|
||||
})
|
||||
.catch((err) => {
|
||||
reject(err);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
export function upload(url, params) {
|
||||
return new Promise((resolve, reject) => {
|
||||
axios
|
||||
.post(url, params, {
|
||||
headers: {
|
||||
"Content-Type": "multipart/form-data",
|
||||
},
|
||||
})
|
||||
.then((res) => {
|
||||
if (res.data.result === "success") {
|
||||
resolve(res.data);
|
||||
} else {
|
||||
ElMessage.error(
|
||||
res.data.msg ||
|
||||
res.data.resMsg ||
|
||||
res.data.resultStr ||
|
||||
"系统开小差了"
|
||||
);
|
||||
reject(res.data);
|
||||
}
|
||||
})
|
||||
.catch((err) => {
|
||||
reject(err);
|
||||
});
|
||||
});
|
||||
}
|
|
@ -0,0 +1,26 @@
|
|||
import { post } from "@/request/axios.js";
|
||||
|
||||
// 获取数据字典
|
||||
export const getLevels = (params) =>
|
||||
post("/dictionaries/getLevels", {
|
||||
loading: false,
|
||||
...params,
|
||||
});
|
||||
// 获取数据字典包括子级数量
|
||||
export const getLevelsAndChildrenNumber = (params) =>
|
||||
post("/dictionaries/getLevelsAndSCount", {
|
||||
loading: false,
|
||||
...params,
|
||||
});
|
||||
// 监管类型
|
||||
export const getRegulatoryType = (params) =>
|
||||
post("/corptype/corptypelist", {
|
||||
loading: false,
|
||||
...params,
|
||||
});
|
||||
// 获取在线学习培训类型
|
||||
export const getLearningTrainType = (params) =>
|
||||
post("/dictionaries/listDictToParId", {
|
||||
loading: false,
|
||||
...params,
|
||||
});
|
|
@ -0,0 +1,14 @@
|
|||
import { post, upload } from "@/request/axios.js";
|
||||
|
||||
export const getEnterpriseInfo = (params) => post("/corpinfo/goEdit", params); // 获取企业信息
|
||||
export const setEnterpriseInfo = (params) => upload("/corpinfo/edit", params); // 修改企业信息
|
||||
export const getIndustryQualificationsList = (params) =>
|
||||
post("/qualifications/list", params); // 行业资质列表
|
||||
export const setIndustryQualificationsDelete = (params) =>
|
||||
post("/qualifications/delete", params); // 行业资质删除
|
||||
export const getIndustryQualificationsView = (params) =>
|
||||
post("/qualifications/goEdit", params); // 行业资质查看
|
||||
export const setIndustryQualificationsAdd = (params) =>
|
||||
post("/qualifications/add", params); // 行业资质添加
|
||||
export const setIndustryQualificationsEdit = (params) =>
|
||||
post("/qualifications/edit", params); // 行业资质修改
|
|
@ -0,0 +1,51 @@
|
|||
import { createRouter, createWebHashHistory } from "vue-router";
|
||||
import layout from "../components/layout/index.vue";
|
||||
// import children from "../components/children/index.vue";
|
||||
|
||||
const routes = [
|
||||
{
|
||||
path: "/login",
|
||||
name: "/login",
|
||||
meta: { title: "登录", isLogin: false },
|
||||
component: () => import("@/views/login/index"),
|
||||
},
|
||||
{
|
||||
path: "/",
|
||||
name: "app",
|
||||
redirect: "/index",
|
||||
meta: { title: "首页" },
|
||||
component: layout,
|
||||
children: [
|
||||
{
|
||||
path: "/index",
|
||||
name: "/index",
|
||||
meta: {
|
||||
title: "首页",
|
||||
breadcrumb: false,
|
||||
isMenu: false,
|
||||
isSubMenu: false,
|
||||
},
|
||||
component: () => import("@/views/index/index"),
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
path: "/404",
|
||||
name: "/404",
|
||||
meta: { title: "404", isBreadcrumb: false, isMenu: false },
|
||||
component: () => import("@/views/404"),
|
||||
},
|
||||
];
|
||||
const router = createRouter({
|
||||
history: createWebHashHistory(import.meta.env.BASE_URL),
|
||||
routes,
|
||||
scrollBehavior(to, from, savedPosition) {
|
||||
if (savedPosition) {
|
||||
return savedPosition;
|
||||
} else {
|
||||
return { left: 0, top: 0 };
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
export default router;
|
|
@ -0,0 +1,565 @@
|
|||
<template>
|
||||
<div class="containerer">
|
||||
<div class="container container-star">
|
||||
<div class="star-1" v-for="item in 30" :key="item"></div>
|
||||
<div class="star-2" v-for="item in 30" :key="item"></div>
|
||||
</div>
|
||||
<div class="container container-bird">
|
||||
<div class="bird bird-anim">
|
||||
<div class="bird-container">
|
||||
<div class="wing wing-left">
|
||||
<div class="wing-left-top"></div>
|
||||
</div>
|
||||
<div class="wing wing-right">
|
||||
<div class="wing-right-top"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="bird bird-anim">
|
||||
<div class="bird-container">
|
||||
<div class="wing wing-left">
|
||||
<div class="wing-left-top"></div>
|
||||
</div>
|
||||
<div class="wing wing-right">
|
||||
<div class="wing-right-top"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="bird bird-anim">
|
||||
<div class="bird-container">
|
||||
<div class="wing wing-left">
|
||||
<div class="wing-left-top"></div>
|
||||
</div>
|
||||
<div class="wing wing-right">
|
||||
<div class="wing-right-top"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="bird bird-anim">
|
||||
<div class="bird-container">
|
||||
<div class="wing wing-left">
|
||||
<div class="wing-left-top"></div>
|
||||
</div>
|
||||
<div class="wing wing-right">
|
||||
<div class="wing-right-top"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="bird bird-anim">
|
||||
<div class="bird-container">
|
||||
<div class="wing wing-left">
|
||||
<div class="wing-left-top"></div>
|
||||
</div>
|
||||
<div class="wing wing-right">
|
||||
<div class="wing-right-top"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="bird bird-anim">
|
||||
<div class="bird-container">
|
||||
<div class="wing wing-left">
|
||||
<div class="wing-left-top"></div>
|
||||
</div>
|
||||
<div class="wing wing-right">
|
||||
<div class="wing-right-top"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="container-title">
|
||||
<div class="title">
|
||||
<div class="number">4</div>
|
||||
<div class="moon">
|
||||
<div class="face">
|
||||
<div class="mouth"></div>
|
||||
<div class="eyes">
|
||||
<div class="eye-left"></div>
|
||||
<div class="eye-right"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="number">4</div>
|
||||
</div>
|
||||
<div class="subtitle">抱歉,您访问的页面不存在!</div>
|
||||
<button class="backbtn" @click="router.push({ name: '/index' })">
|
||||
返回首页
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script setup>
|
||||
import { useRouter } from "vue-router";
|
||||
const router = useRouter();
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
@import url("https://fonts.googleapis.com/css?family=Lato|Russo+One");
|
||||
*,
|
||||
*:after,
|
||||
*:before {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
body {
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
}
|
||||
.container {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100vh;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
//stars
|
||||
.container-star {
|
||||
background-image: linear-gradient(
|
||||
to bottom,
|
||||
#04112b 0%,
|
||||
#474c88 70%,
|
||||
#a871d6 100%
|
||||
);
|
||||
&:after {
|
||||
background: radial-gradient(
|
||||
ellipse at center,
|
||||
rgba(255, 255, 255, 0) 0%,
|
||||
rgba(255, 255, 255, 0) 40%,
|
||||
rgba(15, 10, 38, 0.2) 100%
|
||||
);
|
||||
content: "";
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
}
|
||||
}
|
||||
.star-1 {
|
||||
position: absolute;
|
||||
border-radius: 50%;
|
||||
background-color: #ffffff;
|
||||
animation: twinkle 5s infinite ease-in-out;
|
||||
&:after {
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
transform: rotate(90deg);
|
||||
content: "";
|
||||
position: absolute;
|
||||
background-color: #fff;
|
||||
border-radius: 50%;
|
||||
}
|
||||
&:before {
|
||||
background: radial-gradient(
|
||||
ellipse at center,
|
||||
rgba(255, 255, 255, 0.5) 0%,
|
||||
rgba(255, 255, 255, 0) 60%,
|
||||
rgba(255, 255, 255, 0) 100%
|
||||
);
|
||||
position: absolute;
|
||||
border-radius: 50%;
|
||||
content: "";
|
||||
top: -20%;
|
||||
left: -50%;
|
||||
}
|
||||
}
|
||||
@for $i from 1 through (30) {
|
||||
$top: random(100) + vh;
|
||||
$left: random(100) + vw;
|
||||
$size: random(6) + 3px;
|
||||
.star-1:nth-of-type(#{$i}) {
|
||||
top: $top;
|
||||
left: $left;
|
||||
width: $size;
|
||||
height: calc($size / 3);
|
||||
animation-delay: random(5) + s;
|
||||
&:before {
|
||||
width: $size * 2;
|
||||
height: $size * 2;
|
||||
top: -250%;
|
||||
}
|
||||
}
|
||||
}
|
||||
.star-2 {
|
||||
position: absolute;
|
||||
border-radius: 50%;
|
||||
background-color: #ffffff;
|
||||
animation: twinkle 5s infinite ease-in-out;
|
||||
}
|
||||
@for $i from 31 through (60) {
|
||||
$top: random(100) + vh;
|
||||
$left: random(100) + vw;
|
||||
$size: random(3) + 1px;
|
||||
.star-2:nth-of-type(#{$i}) {
|
||||
top: $top;
|
||||
left: $left;
|
||||
width: $size;
|
||||
height: $size;
|
||||
animation-delay: random(5) + s;
|
||||
&:before {
|
||||
width: $size * 2;
|
||||
height: $size * 2;
|
||||
top: -250%;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//text and moon
|
||||
.container-title {
|
||||
width: 600px;
|
||||
height: 450px;
|
||||
left: 50%;
|
||||
top: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
position: absolute;
|
||||
color: white;
|
||||
line-height: 1;
|
||||
font-weight: 700;
|
||||
text-align: center;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
flex-direction: column;
|
||||
display: flex;
|
||||
}
|
||||
.title > * {
|
||||
display: inline-block;
|
||||
font-size: 200px;
|
||||
}
|
||||
.number {
|
||||
text-shadow: 20px 20px 20px rgba(0, 0, 0, 0.2);
|
||||
padding: 0 0.2em;
|
||||
}
|
||||
.subtitle {
|
||||
font-size: 24px;
|
||||
margin-top: 3em;
|
||||
text-shadow: 4px 4px 4px rgba(0, 0, 0, 0.2);
|
||||
font-weight: normal;
|
||||
}
|
||||
button {
|
||||
font-size: 14px;
|
||||
margin-top: 2em;
|
||||
padding: 0.5em 1em;
|
||||
letter-spacing: 1px;
|
||||
color: white;
|
||||
background-color: transparent;
|
||||
border: 0;
|
||||
cursor: pointer;
|
||||
z-index: 999;
|
||||
border: 1px solid white;
|
||||
border-radius: 5px;
|
||||
text-shadow: 4px 4px 4px rgba(0, 0, 0, 0.2);
|
||||
transition: opacity 0.2s ease;
|
||||
&:hover {
|
||||
opacity: 0.7;
|
||||
}
|
||||
&:focus {
|
||||
outline: 0;
|
||||
}
|
||||
}
|
||||
.moon {
|
||||
position: relative;
|
||||
border-radius: 50%;
|
||||
width: 160px;
|
||||
height: 160px;
|
||||
z-index: 2;
|
||||
background-color: #fff;
|
||||
box-shadow: 0 0 10px #fff, 0 0 20px #fff, 0 0 30px #fff, 0 0 40px #fff,
|
||||
0 0 70px #fff, 0 0 80px #fff, 0 0 100px #ff1177;
|
||||
animation: rotate 5s ease-in-out infinite;
|
||||
.face {
|
||||
top: 60%;
|
||||
left: 47%;
|
||||
position: absolute;
|
||||
.mouth {
|
||||
border-top-left-radius: 50%;
|
||||
border-bottom-right-radius: 50%;
|
||||
border-top-right-radius: 50%;
|
||||
background-color: #5c3191;
|
||||
width: 25px;
|
||||
height: 25px;
|
||||
position: absolute;
|
||||
animation: snore 5s ease-in-out infinite;
|
||||
transform: rotate(45deg);
|
||||
box-shadow: inset -4px -4px 4px rgba(0, 0, 0, 0.3);
|
||||
}
|
||||
.eyes {
|
||||
position: absolute;
|
||||
top: -30px;
|
||||
left: -30px;
|
||||
.eye-left,
|
||||
.eye-right {
|
||||
border: 4px solid #5c3191;
|
||||
width: 30px;
|
||||
height: 15px;
|
||||
border-bottom-left-radius: 100px;
|
||||
border-bottom-right-radius: 100px;
|
||||
border-top: 0;
|
||||
position: absolute;
|
||||
&:before,
|
||||
&:after {
|
||||
content: "";
|
||||
position: absolute;
|
||||
border-radius: 50%;
|
||||
width: 4px;
|
||||
height: 4px;
|
||||
background-color: #5c3191;
|
||||
top: -2px;
|
||||
left: -4px;
|
||||
}
|
||||
&:after {
|
||||
left: auto;
|
||||
right: -4px;
|
||||
}
|
||||
}
|
||||
.eye-right {
|
||||
left: 50px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//birds
|
||||
.container-bird {
|
||||
perspective: 2000px;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
bottom: 0;
|
||||
right: 0;
|
||||
}
|
||||
.bird {
|
||||
position: absolute;
|
||||
z-index: 1000;
|
||||
left: 50%;
|
||||
top: 50%;
|
||||
height: 40px;
|
||||
width: 50px;
|
||||
transform: translate3d(-100vw, 0, 0) rotateY(90deg);
|
||||
transform-style: preserve-3d;
|
||||
}
|
||||
.bird-container {
|
||||
left: 0;
|
||||
top: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
transform-style: preserve-3d;
|
||||
transform: translate3d(50px, 30px, -300px);
|
||||
}
|
||||
.wing {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
border-radius: 3px;
|
||||
transform-style: preserve-3d;
|
||||
transform-origin: center bottom;
|
||||
z-index: 300;
|
||||
}
|
||||
.wing-left {
|
||||
background: linear-gradient(to bottom, #a58dc4 0%, #7979a8 100%);
|
||||
transform: translate3d(0, 0, 0) rotateX(-30deg);
|
||||
animation: wingLeft 1.3s cubic-bezier(0.45, 0, 0.5, 0.95) infinite;
|
||||
}
|
||||
.wing-right {
|
||||
background: linear-gradient(to bottom, #d9d3e2 0%, #b8a5d1 100%);
|
||||
transform: translate3d(0, 0, 0) rotateX(-30deg);
|
||||
animation: wingRight 1.3s cubic-bezier(0.45, 0, 0.5, 0.95) infinite;
|
||||
}
|
||||
.wing-right-top,
|
||||
.wing-left-top {
|
||||
border-right: 25px solid transparent;
|
||||
border-left: 25px solid transparent;
|
||||
top: -20px;
|
||||
width: 100%;
|
||||
position: absolute;
|
||||
transform-origin: 100% 100%;
|
||||
}
|
||||
.wing-right-top {
|
||||
border-bottom: 20px solid #b8a5d1;
|
||||
transform: translate3d(0, 0, 0) rotateX(60deg);
|
||||
animation: wingRightTop 1.3s cubic-bezier(0.45, 0, 0.5, 0.95) infinite;
|
||||
}
|
||||
.wing-left-top {
|
||||
border-bottom: 20px solid #7979a8;
|
||||
transform: translate3d(0, 0, 0) rotateX(-60deg);
|
||||
animation: wingLeftTop 1.3s cubic-bezier(0.45, 0, 0.5, 0.95) infinite;
|
||||
}
|
||||
.bird-anim:nth-child(1) {
|
||||
animation: bird1 30s linear infinite forwards;
|
||||
}
|
||||
.bird-anim:nth-child(2) {
|
||||
animation: bird2 30s linear infinite forwards;
|
||||
animation-delay: 3s;
|
||||
z-index: -1;
|
||||
}
|
||||
.bird-anim:nth-child(3) {
|
||||
animation: bird3 30s linear infinite forwards;
|
||||
animation-delay: 5s;
|
||||
}
|
||||
.bird-anim:nth-child(4) {
|
||||
animation: bird4 30s linear infinite forwards;
|
||||
animation-delay: 7s;
|
||||
}
|
||||
.bird-anim:nth-child(5) {
|
||||
animation: bird5 30s linear infinite forwards;
|
||||
animation-delay: 14s;
|
||||
}
|
||||
.bird-anim:nth-child(6) {
|
||||
animation: bird6 30s linear infinite forwards;
|
||||
animation-delay: 10s;
|
||||
z-index: -1;
|
||||
}
|
||||
|
||||
//keyframes
|
||||
@keyframes rotate {
|
||||
0%,
|
||||
100% {
|
||||
transform: rotate(-8deg);
|
||||
}
|
||||
50% {
|
||||
transform: rotate(0deg);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes snore {
|
||||
0%,
|
||||
100% {
|
||||
transform: scale(1) rotate(30deg);
|
||||
}
|
||||
50% {
|
||||
transform: scale(0.5) rotate(30deg);
|
||||
border-bottom-left-radius: 50%;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes twinkle {
|
||||
0%,
|
||||
100% {
|
||||
opacity: 0.7;
|
||||
}
|
||||
50% {
|
||||
opacity: 0.3;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes wingLeft {
|
||||
0%,
|
||||
100% {
|
||||
transform: translate3d(0, 0, 0) rotateX(-50deg);
|
||||
}
|
||||
50% {
|
||||
transform: translate3d(0, -20px, 0) rotateX(-130deg);
|
||||
background: linear-gradient(to bottom, #d9d3e2 0%, #b8a5d1 100%);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes wingLeftTop {
|
||||
0%,
|
||||
100% {
|
||||
transform: translate3d(0, 0, 0) rotateX(-10deg);
|
||||
}
|
||||
50% {
|
||||
transform: translate3d(0px, 0px, 0) rotateX(-40deg);
|
||||
border-bottom: 20px solid #b8a5d1;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes wingRight {
|
||||
0%,
|
||||
100% {
|
||||
transform: translate3d(0, 0, 0) rotateX(50deg);
|
||||
}
|
||||
50% {
|
||||
transform: translate3d(0, -20px, 0) rotateX(130deg);
|
||||
background: linear-gradient(to bottom, #a58dc4 0%, #7979a8 100%);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes wingRightTop {
|
||||
0%,
|
||||
100% {
|
||||
transform: translate3d(0, 0, 0) rotateX(10deg);
|
||||
}
|
||||
50% {
|
||||
transform: translate3d(0px, 0px, 0px) rotateX(40deg);
|
||||
border-bottom: 20px solid #7979a8;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes bird1 {
|
||||
0% {
|
||||
transform: translate3d(-120vw, -20px, -1000px) rotateY(-40deg) rotateX(0deg);
|
||||
}
|
||||
100% {
|
||||
transform: translate3d(100vw, -40vh, 1000px) rotateY(-40deg) rotateX(0deg);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes bird2 {
|
||||
0%,
|
||||
15% {
|
||||
transform: translate3d(100vw, -300px, -1000px) rotateY(10deg) rotateX(0deg);
|
||||
}
|
||||
100% {
|
||||
transform: translate3d(-100vw, -20px, -1000px) rotateY(10deg) rotateX(0deg);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes bird3 {
|
||||
0% {
|
||||
transform: translate3d(100vw, -50vh, 100px) rotateY(-5deg) rotateX(-20deg);
|
||||
}
|
||||
100% {
|
||||
transform: translate3d(-100vw, -10vh, 100px) rotateY(-5deg) rotateX(-20deg);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes bird4 {
|
||||
0% {
|
||||
transform: translate3d(100vw, 30vh, 200px) rotateY(-5deg) rotateX(10deg);
|
||||
}
|
||||
100% {
|
||||
transform: translate3d(-100vw, -30vh, 200px) rotateY(-5deg) rotateX(10deg);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes bird5 {
|
||||
0%,
|
||||
5% {
|
||||
transform: translate3d(100vw, 30vh, 400px) rotateY(-15deg) rotateX(-10deg);
|
||||
}
|
||||
100% {
|
||||
transform: translate3d(-100vw, 10vh, 400px) rotateY(-15deg) rotateX(-10deg);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes bird6 {
|
||||
0%,
|
||||
10% {
|
||||
transform: translate3d(-100vw, 20vh, -500px) rotateY(15deg) rotateX(10deg);
|
||||
}
|
||||
100% {
|
||||
transform: translate3d(100vw, 40vh, -800px) rotateY(5deg) rotateX(10deg);
|
||||
}
|
||||
}
|
||||
@media screen and (max-width: 580px) {
|
||||
.container-404 {
|
||||
width: 100%;
|
||||
}
|
||||
.number {
|
||||
font-size: 100px;
|
||||
}
|
||||
.subtitle {
|
||||
font-size: 20px;
|
||||
padding: 0 1em;
|
||||
}
|
||||
.moon {
|
||||
width: 100px;
|
||||
height: 100px;
|
||||
}
|
||||
.face {
|
||||
transform: scale(0.7);
|
||||
}
|
||||
}
|
||||
</style>
|
|
@ -0,0 +1,107 @@
|
|||
<template>
|
||||
<layout-card>
|
||||
<el-form
|
||||
ref="formRef"
|
||||
:rules="data.rules"
|
||||
:model="data.form"
|
||||
label-width="110px"
|
||||
>
|
||||
<el-form-item label="证书名称" prop="NAME">
|
||||
<el-input v-model="data.form.NAME" />
|
||||
</el-form-item>
|
||||
<el-form-item label="证书有效期" prop="VALIDITYTIME">
|
||||
<el-date-picker
|
||||
v-model="data.form.VALIDITYTIME"
|
||||
format="YYYY-MM-DD"
|
||||
value-format="YYYY-MM-DD"
|
||||
/>
|
||||
</el-form-item>
|
||||
<el-form-item label="证书编号" prop="NUMBER">
|
||||
<el-input v-model="data.form.NUMBER" />
|
||||
</el-form-item>
|
||||
<el-form-item label="备注" prop="DESCR">
|
||||
<el-input autosize v-model="data.form.DESCR" type="textarea" />
|
||||
</el-form-item>
|
||||
<el-form-item label="证书图片" prop="file">
|
||||
<layout-upload
|
||||
v-model:file-list="data.form.file"
|
||||
accept=".jpg,.jpeg,.png"
|
||||
list-type="picture-card"
|
||||
delete-to-server
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<div class="tc mt">
|
||||
<el-button type="primary" @click="fnSubmit">提交</el-button>
|
||||
</div>
|
||||
</layout-card>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import LayoutCard from "@/components/card/index.vue";
|
||||
import LayoutUpload from "@/components/upload/index.vue";
|
||||
import { reactive, ref } from "vue";
|
||||
import {
|
||||
getIndustryQualificationsView,
|
||||
setIndustryQualificationsAdd,
|
||||
setIndustryQualificationsEdit,
|
||||
} from "@/request/prevention/enterprise_management.js";
|
||||
import { debounce } from "throttle-debounce";
|
||||
import { ElMessage } from "element-plus";
|
||||
import { setUploadImg } from "@/request/api.js";
|
||||
import { useRouter, useRoute } from "vue-router";
|
||||
import { addingPrefixToFile } from "@/assets/js/utils.js";
|
||||
import useFormValidate from "@/assets/js/useFormValidate.js";
|
||||
|
||||
const router = useRouter();
|
||||
const route = useRoute();
|
||||
const { QUALIFICATIONS_ID } = route.query;
|
||||
const formRef = ref(null);
|
||||
const data = reactive({
|
||||
form: {
|
||||
NAME: "",
|
||||
VALIDITYTIME: "",
|
||||
NUMBER: "",
|
||||
DESCR: "",
|
||||
file: [],
|
||||
verifyFileUrl: "12",
|
||||
},
|
||||
rules: {
|
||||
NAME: [{ required: true, message: "名称不能为空", trigger: "blur" }],
|
||||
VALIDITYTIME: [
|
||||
{ required: true, message: "证书有效期不能为空", trigger: "blur" },
|
||||
],
|
||||
NUMBER: [{ required: true, message: "证书编号不能为空", trigger: "blur" }],
|
||||
file: [{ required: true, message: "证书图片不能为空", trigger: "blur" }],
|
||||
},
|
||||
});
|
||||
const fnGetData = async () => {
|
||||
if (!QUALIFICATIONS_ID) return;
|
||||
const resData = await getIndustryQualificationsView({ QUALIFICATIONS_ID });
|
||||
data.form = resData.pd;
|
||||
data.form.file = addingPrefixToFile(resData.hImgs);
|
||||
};
|
||||
fnGetData();
|
||||
const fnSubmit = debounce(
|
||||
1000,
|
||||
async () => {
|
||||
await useFormValidate(formRef);
|
||||
const resData = !QUALIFICATIONS_ID
|
||||
? await setIndustryQualificationsAdd({ ...data.form })
|
||||
: await setIndustryQualificationsEdit({ ...data.form });
|
||||
const formData = new FormData();
|
||||
for (let i = 0; i < data.form.file.length; i++) {
|
||||
if (data.form.file[i].raw)
|
||||
formData.append("FFILE", data.form.file[i].raw);
|
||||
}
|
||||
formData.append("FOREIGN_KEY", resData.pd.QUALIFICATIONS_ID);
|
||||
formData.append("TYPE", 6);
|
||||
await setUploadImg(formData);
|
||||
ElMessage.success("保存成功");
|
||||
router.back();
|
||||
},
|
||||
{ atBegin: true }
|
||||
);
|
||||
</script>
|
||||
|
||||
<style scoped></style>
|
|
@ -0,0 +1,170 @@
|
|||
<template>
|
||||
<div>
|
||||
<el-card>
|
||||
<el-form
|
||||
:model="searchForm"
|
||||
label-width="130px"
|
||||
@submit.prevent="fnResetPaginationTransfer"
|
||||
>
|
||||
<el-row>
|
||||
<el-col :span="6">
|
||||
<el-form-item label="证书名称/证书编号" prop="KEYWORDS">
|
||||
<el-input
|
||||
v-model="searchForm.KEYWORDS"
|
||||
placeholder="请输入关键字"
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="6">
|
||||
<el-form-item label="证书有效期" prop="dates">
|
||||
<el-date-picker
|
||||
v-model="searchForm.dates"
|
||||
type="daterange"
|
||||
range-separator="至"
|
||||
start-placeholder="开始日期"
|
||||
end-placeholder="结束日期"
|
||||
format="YYYY-MM-DD"
|
||||
value-format="YYYY-MM-DD"
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label-width="10px">
|
||||
<el-button type="primary" native-type="submit">搜索</el-button>
|
||||
<el-button native-type="reset" @click="fnResetPagination">
|
||||
重置
|
||||
</el-button>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-form>
|
||||
</el-card>
|
||||
<layout-card>
|
||||
<layout-table
|
||||
:data="list"
|
||||
v-model:pagination="pagination"
|
||||
@get-data="fnGetDataTransfer"
|
||||
>
|
||||
<el-table-column label="序号" width="70">
|
||||
<template v-slot="{ $index }">
|
||||
{{ serialNumber(pagination, $index) }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="证书名称" prop="NAME" />
|
||||
<el-table-column label="证书有效期" prop="VALIDITYTIME" />
|
||||
<el-table-column label="证书编号" prop="NUMBER" />
|
||||
<el-table-column label="照片">
|
||||
<template v-slot="{ row }">
|
||||
<el-tooltip placement="top">
|
||||
<template #content>
|
||||
<template v-if="row.imgs.length > 0">
|
||||
<img
|
||||
v-for="item in row.imgs"
|
||||
:key="item.IMGFILES_ID"
|
||||
:src="FILE_URL + item.FILEPATH"
|
||||
width="100"
|
||||
height="100"
|
||||
alt=""
|
||||
/>
|
||||
</template>
|
||||
<span v-else>暂无图片</span>
|
||||
</template>
|
||||
<el-tag>预览</el-tag>
|
||||
</el-tooltip>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作" width="150">
|
||||
<template v-slot="{ row }">
|
||||
<el-button
|
||||
type="primary"
|
||||
text
|
||||
link
|
||||
@click="
|
||||
router.push({
|
||||
path: '/enterprise_management/information/industry_qualifications/view',
|
||||
query: { QUALIFICATIONS_ID: row.QUALIFICATIONS_ID },
|
||||
})
|
||||
"
|
||||
>
|
||||
查看
|
||||
</el-button>
|
||||
<el-button
|
||||
type="primary"
|
||||
text
|
||||
link
|
||||
@click="
|
||||
router.push({
|
||||
path: '/enterprise_management/information/industry_qualifications/update',
|
||||
query: { QUALIFICATIONS_ID: row.QUALIFICATIONS_ID },
|
||||
})
|
||||
"
|
||||
>
|
||||
修改
|
||||
</el-button>
|
||||
<el-button
|
||||
type="primary"
|
||||
text
|
||||
link
|
||||
@click="fnDelete(row.QUALIFICATIONS_ID)"
|
||||
>
|
||||
删除
|
||||
</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<template #button>
|
||||
<el-button
|
||||
type="primary"
|
||||
@click="
|
||||
router.push({
|
||||
path: '/enterprise_management/information/industry_qualifications/add',
|
||||
})
|
||||
"
|
||||
>
|
||||
新增
|
||||
</el-button>
|
||||
</template>
|
||||
</layout-table>
|
||||
</layout-card>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { serialNumber } from "@/assets/js/utils.js";
|
||||
import { debounce } from "throttle-debounce";
|
||||
import { ElMessage, ElMessageBox } from "element-plus";
|
||||
import {
|
||||
getIndustryQualificationsList,
|
||||
setIndustryQualificationsDelete,
|
||||
} from "@/request/prevention/enterprise_management.js";
|
||||
import { useRouter } from "vue-router";
|
||||
import useListData from "@/assets/js/useListData.js";
|
||||
|
||||
const FILE_URL = import.meta.env.VITE_FILE_URL;
|
||||
const router = useRouter();
|
||||
const listData = useListData(getIndustryQualificationsList);
|
||||
const { list, pagination, searchForm, fnGetData, fnResetPagination } = listData;
|
||||
const fnGetDataTransfer = async () => {
|
||||
await fnGetData({
|
||||
STARTTIME: searchForm.value.dates?.[0],
|
||||
ENDTIME: searchForm.value.dates?.[1],
|
||||
});
|
||||
};
|
||||
const fnResetPaginationTransfer = async () => {
|
||||
await fnResetPagination({
|
||||
STARTTIME: searchForm.value.dates?.[0],
|
||||
ENDTIME: searchForm.value.dates?.[1],
|
||||
});
|
||||
};
|
||||
const fnDelete = debounce(
|
||||
1000,
|
||||
async (QUALIFICATIONS_ID) => {
|
||||
await ElMessageBox.confirm("确定要删除吗?", { type: "warning" });
|
||||
await setIndustryQualificationsDelete({ QUALIFICATIONS_ID });
|
||||
ElMessage.success("删除成功");
|
||||
await fnResetPaginationTransfer();
|
||||
},
|
||||
{ atBegin: true }
|
||||
);
|
||||
</script>
|
||||
|
||||
<style scoped></style>
|
|
@ -0,0 +1,51 @@
|
|||
<template>
|
||||
<layout-card>
|
||||
<el-descriptions :column="2" border>
|
||||
<el-descriptions-item label="证书名称">
|
||||
{{ data.info.NAME }}
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="证书有效期">
|
||||
{{ data.info.VALIDITYTIME }}
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="证书编号">
|
||||
{{ data.info.NUMBER }}
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="备注">
|
||||
{{ data.info.DESCR }}
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="证书图片">
|
||||
<img
|
||||
v-viewer
|
||||
v-for="item in data.info.file"
|
||||
:key="item.FILEPATH"
|
||||
:src="item.url"
|
||||
width="100"
|
||||
height="100"
|
||||
class="ml"
|
||||
/>
|
||||
</el-descriptions-item>
|
||||
</el-descriptions>
|
||||
</layout-card>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import LayoutCard from "@/components/card/index.vue";
|
||||
import { getIndustryQualificationsView } from "@/request/prevention/enterprise_management.js";
|
||||
import { reactive } from "vue";
|
||||
import { useRoute } from "vue-router";
|
||||
import { addingPrefixToFile } from "@/assets/js/utils.js";
|
||||
|
||||
const route = useRoute();
|
||||
const { QUALIFICATIONS_ID } = route.query;
|
||||
const data = reactive({
|
||||
info: {},
|
||||
});
|
||||
const fnGetData = async () => {
|
||||
const resData = await getIndustryQualificationsView({ QUALIFICATIONS_ID });
|
||||
data.info = resData.pd;
|
||||
data.info.file = addingPrefixToFile(resData.hImgs);
|
||||
};
|
||||
fnGetData();
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped></style>
|
|
@ -0,0 +1,721 @@
|
|||
<template>
|
||||
<div>
|
||||
<layout-card>
|
||||
<el-form
|
||||
ref="formRef"
|
||||
:model="data.form"
|
||||
:rules="data.rules"
|
||||
label-width="210px"
|
||||
>
|
||||
<el-row>
|
||||
<el-col :span="24">
|
||||
<el-divider content-position="left">基本信息</el-divider>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="企业名称" prop="CORP_NAME">
|
||||
<el-input v-model="data.form.CORP_NAME" disabled />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="邮政编码">
|
||||
<el-input
|
||||
v-model="data.form.POSTAL_CODE"
|
||||
placeholder="请输入内容"
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="统一社会信用代码" prop="CODE">
|
||||
<el-input v-model="data.form.CODE" placeholder="请输入内容" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="市行业监管部门" prop="INDUSTRY_DEPARTMENTName">
|
||||
<el-input
|
||||
disabled
|
||||
v-model="data.form.INDUSTRY_DEPARTMENTName"
|
||||
placeholder="请输入内容"
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="属地" prop="POSSESSION">
|
||||
<layout-territory
|
||||
ref="territoryCascaderRef"
|
||||
v-model="data.form.POSSESSION"
|
||||
:level="4"
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="监管类型" prop="REGULARTYPE">
|
||||
<layout-regulatory-type
|
||||
v-model="data.form.REGULARTYPE"
|
||||
:territory="data.form.POSSESSION"
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="所属行业" prop="INDUSTRYALL">
|
||||
<layout-industry
|
||||
ref="industryCascaderRef"
|
||||
v-model="data.form.INDUSTRYALL"
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="经济类型" prop="ECO_TYPEALL">
|
||||
<layout-economic-type
|
||||
ref="economicTypeCascaderRef"
|
||||
v-model="data.form.ECO_TYPEALL"
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="企事业单位经营地址" prop="ADDRESS_BUSINESS">
|
||||
<el-input
|
||||
v-model="data.form.ADDRESS_BUSINESS"
|
||||
placeholder="请输入内容"
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="企业状态" prop="CORP_STATE">
|
||||
<el-select v-model="data.form.CORP_STATE">
|
||||
<el-option
|
||||
v-for="item in enterpriseStatus"
|
||||
:key="item.DICTIONARIES_ID"
|
||||
:label="item.NAME"
|
||||
:value="item.DICTIONARIES_ID"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="经度/纬度" prop="LONGITUDE">
|
||||
<div style="flex: 1; display: flex">
|
||||
<el-input
|
||||
:model-value="
|
||||
(data.form.LONGITUDE ?? '') +
|
||||
'-' +
|
||||
(data.form.LATITUDE ?? '')
|
||||
"
|
||||
disabled
|
||||
style="flex: 1"
|
||||
/>
|
||||
<el-button
|
||||
type="primary"
|
||||
class="ml-10"
|
||||
@click="fnMapDialogVisible"
|
||||
>
|
||||
地图定位
|
||||
</el-button>
|
||||
</div>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="主要负责人" prop="CONTACTS">
|
||||
<el-input v-model="data.form.CONTACTS" placeholder="请输入内容" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="主要负责人电话" prop="CONTACTS_PHONE">
|
||||
<el-input
|
||||
v-model="data.form.CONTACTS_PHONE"
|
||||
placeholder="请输入内容"
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="法定代表人">
|
||||
<el-input v-model="data.form.LR_NAME" placeholder="请输入内容" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="法人手机号" prop="LR_MOBILE">
|
||||
<el-input v-model="data.form.LR_PHONE" placeholder="请输入内容" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="占地面积(㎡)">
|
||||
<el-input
|
||||
v-model="data.form.AREA_COVERED"
|
||||
placeholder="请输入内容"
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="职工人数(人)">
|
||||
<el-input
|
||||
v-model="data.form.EMPLOYEES"
|
||||
placeholder="请输入内容"
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="成立时间">
|
||||
<el-date-picker
|
||||
v-model="data.form.CREATE_DATE"
|
||||
value-format="YYYY-MM-DD"
|
||||
format="YYYY-MM-DD"
|
||||
type="date"
|
||||
placeholder="请选择"
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="注册资金(万元)">
|
||||
<el-input
|
||||
v-model="data.form.REGCAPITAL"
|
||||
placeholder="请输入内容"
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="资产总额(万元)">
|
||||
<el-input
|
||||
v-model="data.form.TOTALASSETS"
|
||||
placeholder="请输入内容"
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
|
||||
<el-col :span="12">
|
||||
<el-form-item label="隶属关系">
|
||||
<el-select v-model="data.form.SUBORDINATION">
|
||||
<el-option
|
||||
v-for="item in subordination"
|
||||
:key="item.DICTIONARIES_ID"
|
||||
:label="item.NAME"
|
||||
:value="item.DICTIONARIES_ID"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="规模" prop="SCALE">
|
||||
<el-select v-model="data.form.SCALE" clearable>
|
||||
<el-option
|
||||
v-for="item in enterpriseScale"
|
||||
:key="item.DICTIONARIES_ID"
|
||||
:label="item.NAME"
|
||||
:value="item.DICTIONARIES_ID"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="是否规模以上" prop="SCALE_TYPE">
|
||||
<el-radio-group v-model="data.form.SCALE_TYPE">
|
||||
<el-radio :label="0">否</el-radio>
|
||||
<el-radio :label="1">是</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="培训行业类型" prop="TRAINTYPE">
|
||||
<layout-learning-train-type
|
||||
v-model="data.form.TRAINTYPE"
|
||||
type="industry"
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="企业可新建用户数量" prop="USERS_NUM">
|
||||
<el-input
|
||||
v-model="data.form.USERS_NUM"
|
||||
disabled
|
||||
placeholder="请输入内容"
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="四色图类型">
|
||||
<el-radio v-model="data.form.FOURTYPE" :label="1">
|
||||
平面四色图
|
||||
</el-radio>
|
||||
<el-radio
|
||||
v-model="data.form.FOURTYPE"
|
||||
:label="2"
|
||||
@change="fnProhibitSelect"
|
||||
>
|
||||
3D图
|
||||
</el-radio>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="24" v-if="data.form.FOURTYPE === 1">
|
||||
<el-form-item label="四色图" prop="four_images">
|
||||
<layout-upload
|
||||
v-model:file-list="data.form.four_images"
|
||||
accept=".jpg,.jpeg,.png"
|
||||
list-type="picture-card"
|
||||
ratio="1480*640"
|
||||
delete-to-server
|
||||
>
|
||||
<template #tip> 四色图:请上传1480*640分辨率的图片 </template>
|
||||
</layout-upload>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="24">
|
||||
<el-form-item label="营业执照" prop="bus_images">
|
||||
<layout-upload
|
||||
v-model:file-list="data.form.bus_images"
|
||||
accept=".jpg,.jpeg,.png"
|
||||
list-type="picture-card"
|
||||
:limit="99"
|
||||
delete-to-server
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="24">
|
||||
<el-divider content-position="left">安全负责人信息</el-divider>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="姓名" prop="SAFETY_NAME">
|
||||
<el-input
|
||||
v-model="data.form.SAFETY_NAME"
|
||||
placeholder="请输入内容"
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="职务" prop="SAFETY_POST">
|
||||
<el-input
|
||||
v-model="data.form.SAFETY_POST"
|
||||
placeholder="请输入内容"
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="单位电话" prop="SAFETY_NUMBER">
|
||||
<el-input
|
||||
v-model="data.form.SAFETY_NUMBER"
|
||||
placeholder="请输入内容"
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="手机号码" prop="SAFETY_PHONE">
|
||||
<el-input
|
||||
v-model="data.form.SAFETY_PHONE"
|
||||
placeholder="请输入内容"
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="24">
|
||||
<el-divider content-position="left">企业相关属性</el-divider>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="有无职业卫生信息" prop="WHETHER_HYGIENE">
|
||||
<el-radio-group v-model="data.form.WHETHER_HYGIENE">
|
||||
<el-radio :label="0">无</el-radio>
|
||||
<el-radio :label="1">有</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="有无重大危险源" prop="WHETHER_HAZARDS">
|
||||
<el-radio-group v-model="data.form.WHETHER_HAZARDS">
|
||||
<el-radio :label="0">无</el-radio>
|
||||
<el-radio :label="1">有</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item
|
||||
label="是否有稀缺大型应急物资或设施"
|
||||
prop="WHETHER_SCARCE"
|
||||
>
|
||||
<el-radio-group v-model="data.form.WHETHER_SCARCE">
|
||||
<el-radio :label="0">否</el-radio>
|
||||
<el-radio :label="1">是</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="是否涉及危化品" prop="WHETHER_CHEMICALS">
|
||||
<el-radio-group v-model="data.form.WHETHER_CHEMICALS">
|
||||
<el-radio :label="0">否</el-radio>
|
||||
<el-radio :label="1">是</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="有无特种设备" prop="WHETHER_SPECIALEQUIPMENT">
|
||||
<el-radio-group v-model="data.form.WHETHER_SPECIALEQUIPMENT">
|
||||
<el-radio :label="0">无</el-radio>
|
||||
<el-radio :label="1">有</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="有无特种作业人员" prop="WHETHER_SPECIALPEOPLE">
|
||||
<el-radio-group v-model="data.form.WHETHER_SPECIALPEOPLE">
|
||||
<el-radio :label="0">无</el-radio>
|
||||
<el-radio :label="1">有</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="是否涉及煤气" prop="WHETHER_COALGAS">
|
||||
<el-radio-group v-model="data.form.WHETHER_COALGAS">
|
||||
<el-radio :label="0">否</el-radio>
|
||||
<el-radio :label="1">是</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="是否属于消防重点单位" prop="WHETHER_FIRE">
|
||||
<el-radio-group v-model="data.form.WHETHER_FIRE">
|
||||
<el-radio :label="0">否</el-radio>
|
||||
<el-radio :label="1">是</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="是否在受限空间作业" prop="WHETHER_CONFINED">
|
||||
<el-radio-group v-model="data.form.WHETHER_CONFINED">
|
||||
<el-radio :label="0">否</el-radio>
|
||||
<el-radio :label="1">是</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="是否存在涉爆粉尘作业" prop="WHETHER_POWDER">
|
||||
<el-radio-group v-model="data.form.WHETHER_POWDER">
|
||||
<el-radio :label="0">否</el-radio>
|
||||
<el-radio :label="1">是</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="是否涉及防雷防静电" prop="WHETHER_LIGHTNING">
|
||||
<el-radio-group v-model="data.form.WHETHER_LIGHTNING">
|
||||
<el-radio :label="0">否</el-radio>
|
||||
<el-radio :label="1">是</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="是否持有放射源" prop="WHETHER_ACTINOGEN">
|
||||
<el-radio-group v-model="data.form.WHETHER_ACTINOGEN">
|
||||
<el-radio :label="0">否</el-radio>
|
||||
<el-radio :label="1">是</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="是否涉及液氨制冷" prop="WHETHER_LIQUIDAMMONIA">
|
||||
<el-radio-group v-model="data.form.WHETHER_LIQUIDAMMONIA">
|
||||
<el-radio :label="0">否</el-radio>
|
||||
<el-radio :label="1">是</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="是否涉及危化品管道" prop="WHETHER_PIPELINE">
|
||||
<el-radio-group v-model="data.form.WHETHER_PIPELINE">
|
||||
<el-radio :label="0">否</el-radio>
|
||||
<el-radio :label="1">是</el-radio>
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-form>
|
||||
<div class="tc mt-10">
|
||||
<el-button @click="back">取 消</el-button>
|
||||
<el-button type="primary" @click="fnSubmit">确 定</el-button>
|
||||
</div>
|
||||
</layout-card>
|
||||
<el-dialog title="请选择位置" v-model="data.mapDialog.visible">
|
||||
<el-form label-position="right" label-width="50px">
|
||||
<el-row>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="坐标">
|
||||
<el-input disabled v-model="data.mapDialog.lng" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label-width="10px">
|
||||
<el-input disabled v-model="data.mapDialog.lat" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-form>
|
||||
<div
|
||||
v-loading="data.mapDialog.loading"
|
||||
element-loading-text="地图正在加载中..."
|
||||
element-loading-background="rgba(0, 0, 0, 0.5)"
|
||||
>
|
||||
<div style="width: 100%; height: 500px" id="map_container" />
|
||||
</div>
|
||||
<template #footer>
|
||||
<el-button @click="fnMapDialogVisible">取消</el-button>
|
||||
<el-button type="primary" @click="fnMapDialogConfirm">确定</el-button>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { nextTick, onBeforeUnmount, reactive, ref } from "vue";
|
||||
import LayoutTerritory from "@/components/territory/index.vue";
|
||||
import LayoutRegulatoryType from "@/components/regulatory_type/index.vue";
|
||||
import LayoutIndustry from "@/components/industry/index.vue";
|
||||
import LayoutEconomicType from "@/components/economic_type/index.vue";
|
||||
import LayoutLearningTrainType from "@/components/learning_train_type/index.vue";
|
||||
import LayoutUpload from "@/components/upload/index.vue";
|
||||
import {
|
||||
layoutFnGetEnterpriseScale,
|
||||
layoutFnGetEnterpriseStatus,
|
||||
layoutFnGetSubordination,
|
||||
} from "@/assets/js/data_dictionary.js";
|
||||
import { ElMessage } from "element-plus";
|
||||
import { useRouter } from "vue-router";
|
||||
import { debounce } from "throttle-debounce";
|
||||
import useFormValidate from "@/assets/js/useFormValidate.js";
|
||||
import {
|
||||
getEnterpriseInfo,
|
||||
setEnterpriseInfo,
|
||||
} from "@/request/prevention/enterprise_management.js";
|
||||
import { addingPrefixToFile } from "@/assets/js/utils.js";
|
||||
|
||||
let mapInstance;
|
||||
const router = useRouter();
|
||||
const formRef = ref(null);
|
||||
const territoryCascaderRef = ref(null);
|
||||
const industryCascaderRef = ref(null);
|
||||
const economicTypeCascaderRef = ref(null);
|
||||
const data = reactive({
|
||||
form: {
|
||||
CORP_NAME: "",
|
||||
POSTAL_CODE: "",
|
||||
CODE: "",
|
||||
INDUSTRY_DEPARTMENTName: "",
|
||||
INDUSTRY_DEPARTMENT: "",
|
||||
POSSESSION: [],
|
||||
REGULARTYPE: "",
|
||||
INDUSTRYALL: [],
|
||||
ECO_TYPEALL: [],
|
||||
ADDRESS_BUSINESS: "",
|
||||
CORP_STATE: "",
|
||||
LONGITUDE: "",
|
||||
LATITUDE: "",
|
||||
CONTACTS: "",
|
||||
CONTACTS_PHONE: "",
|
||||
LR_NAME: "",
|
||||
LR_MOBILE: "",
|
||||
AREA_COVERED: "",
|
||||
EMPLOYEES: "",
|
||||
CREATE_DATE: "",
|
||||
REGCAPITAL: "",
|
||||
TOTALASSETS: "",
|
||||
SUBORDINATION: "",
|
||||
SCALE: "",
|
||||
SCALE_TYPE: "",
|
||||
TRAINTYPE: [],
|
||||
USERS_NUM: "",
|
||||
FOURTYPE: "",
|
||||
four_images: [],
|
||||
bus_images: [],
|
||||
SAFETY_NAME: "",
|
||||
SAFETY_POST: "",
|
||||
SAFETY_NUMBER: "",
|
||||
SAFETY_PHONE: "",
|
||||
WHETHER_HYGIENE: "",
|
||||
WHETHER_HAZARDS: "",
|
||||
WHETHER_SCARCE: "",
|
||||
WHETHER_CHEMICALS: "",
|
||||
WHETHER_SPECIALEQUIPMENT: "",
|
||||
WHETHER_SPECIALPEOPLE: "",
|
||||
WHETHER_COALGAS: "",
|
||||
WHETHER_FIRE: "",
|
||||
WHETHER_CONFINED: "",
|
||||
WHETHER_POWDER: "",
|
||||
WHETHER_LIGHTNING: "",
|
||||
WHETHER_ACTINOGEN: "",
|
||||
WHETHER_LIQUIDAMMONIA: "",
|
||||
WHETHER_PIPELINE: "",
|
||||
},
|
||||
rules: {
|
||||
CORP_NAME: [
|
||||
{ required: true, message: "企业名称不能为空", trigger: "blur" },
|
||||
],
|
||||
CODE: [
|
||||
{ required: true, message: "统一社会信用代码不能为空", trigger: "blur" },
|
||||
{
|
||||
pattern: /^[^_IOZSVa-z\W]{2}\d{6}[^_IOZSVa-z\W]{10}$/,
|
||||
message: "请输入正确的统一社会信用代码",
|
||||
},
|
||||
],
|
||||
POSSESSION: [{ required: true, message: "属地不能为空", trigger: "blur" }],
|
||||
INDUSTRYALL: [
|
||||
{ required: true, message: "所属行业不能为空", trigger: "blur" },
|
||||
],
|
||||
ECO_TYPEALL: [
|
||||
{ required: true, message: "经济类型不能为空", trigger: "blur" },
|
||||
],
|
||||
ADDRESS_BUSINESS: [
|
||||
{
|
||||
required: true,
|
||||
message: "企事业单位经营地址不能为空",
|
||||
trigger: "blur",
|
||||
},
|
||||
],
|
||||
CORP_STATE: [
|
||||
{ required: true, message: "企业状态不能为空", trigger: "blur" },
|
||||
],
|
||||
LONGITUDE: [{ required: true, message: "经度不能为空", trigger: "blur" }],
|
||||
LATITUDE: [{ required: true, message: "纬度不能为空", trigger: "blur" }],
|
||||
CONTACTS: [
|
||||
{ required: true, message: "主要负责人不能为空", trigger: "blur" },
|
||||
],
|
||||
CONTACTS_PHONE: [
|
||||
{ required: true, message: "主要负责人电话不能为空", trigger: "blur" },
|
||||
{ min: 11, max: 11, message: "请输入11位手机号码", trigger: "blur" },
|
||||
{
|
||||
pattern:
|
||||
/^(13[0-9]|14[01456879]|15[0-35-9]|16[2567]|17[0-8]|18[0-9]|19[0-35-9])\d{8}$/,
|
||||
message: "请输入正确的手机号码",
|
||||
},
|
||||
],
|
||||
TRAINTYPE: [
|
||||
{ required: true, message: "培训行业类型不能为空", trigger: "blur" },
|
||||
],
|
||||
bus_images: [
|
||||
{ required: true, message: "营业执照不能为空", trigger: "blur" },
|
||||
],
|
||||
LR_MOBILE: [
|
||||
{ min: 11, max: 11, message: "请输入11位手机号码", trigger: "blur" },
|
||||
{
|
||||
pattern:
|
||||
/^(13[0-9]|14[01456879]|15[0-35-9]|16[2567]|17[0-8]|18[0-9]|19[0-35-9])\d{8}$/,
|
||||
message: "请输入正确的手机号码",
|
||||
},
|
||||
],
|
||||
SAFETY_PHONE: [
|
||||
{ min: 11, max: 11, message: "请输入11位手机号码", trigger: "blur" },
|
||||
{
|
||||
pattern:
|
||||
/^(13[0-9]|14[01456879]|15[0-35-9]|16[2567]|17[0-8]|18[0-9]|19[0-35-9])\d{8}$/,
|
||||
message: "请输入正确的手机号码",
|
||||
},
|
||||
],
|
||||
SAFETY_NUMBER: [
|
||||
{ min: 11, max: 11, message: "请输入11位手机号码", trigger: "blur" },
|
||||
{
|
||||
pattern:
|
||||
/^(13[0-9]|14[01456879]|15[0-35-9]|16[2567]|17[0-8]|18[0-9]|19[0-35-9])\d{8}$/,
|
||||
message: "请输入正确的手机号码",
|
||||
},
|
||||
],
|
||||
},
|
||||
mapDialog: {
|
||||
visible: false,
|
||||
loading: true,
|
||||
lat: "",
|
||||
lng: "",
|
||||
},
|
||||
});
|
||||
const fnGetData = async () => {
|
||||
const resData = await getEnterpriseInfo();
|
||||
data.form = resData.pd;
|
||||
data.form.bus_images = addingPrefixToFile(resData.busImgs);
|
||||
data.form.four_images = addingPrefixToFile(resData.fourImgs);
|
||||
data.form.OLDFOURTYPE = resData.pd.FOURTYPE;
|
||||
data.form.FOURTYPE = resData.pd.FOURTYPE || 1;
|
||||
};
|
||||
fnGetData();
|
||||
const enterpriseStatus = await layoutFnGetEnterpriseStatus();
|
||||
const subordination = await layoutFnGetSubordination();
|
||||
const enterpriseScale = await layoutFnGetEnterpriseScale();
|
||||
const fnMapInit = () => {
|
||||
mapInstance = new window.BMapGL.Map("map_container");
|
||||
mapInstance.centerAndZoom(
|
||||
new window.BMapGL.Point(
|
||||
data.form.LONGITUDE || "116.3972282409668",
|
||||
data.form.LATITUDE || "39.90960456049752"
|
||||
),
|
||||
16
|
||||
);
|
||||
mapInstance.enableScrollWheelZoom(true);
|
||||
mapInstance.setMapStyleV2({
|
||||
styleId: "6f501abeb2a0cc0d961d110b9407b127",
|
||||
});
|
||||
data.mapDialog.loading = false;
|
||||
mapInstance.addEventListener("click", function (event) {
|
||||
data.mapDialog.lat = event.latlng.lat;
|
||||
data.mapDialog.lng = event.latlng.lng;
|
||||
});
|
||||
};
|
||||
const fnMapDialogVisible = async () => {
|
||||
data.mapDialog.visible = !data.mapDialog.visible;
|
||||
data.mapDialog.lat = data.form.LATITUDE;
|
||||
data.mapDialog.lng = data.form.LONGITUDE;
|
||||
if (!mapInstance) {
|
||||
await nextTick();
|
||||
fnMapInit();
|
||||
}
|
||||
};
|
||||
const fnMapDialogConfirm = () => {
|
||||
data.form.LATITUDE = data.mapDialog.lat;
|
||||
data.form.LONGITUDE = data.mapDialog.lng;
|
||||
fnMapDialogVisible();
|
||||
};
|
||||
const fnProhibitSelect = () => {
|
||||
ElMessage.error("请联系管理员制作3D图");
|
||||
data.form.FOURTYPE = 1;
|
||||
};
|
||||
const back = () => {
|
||||
router.back();
|
||||
};
|
||||
const fnSubmit = debounce(
|
||||
1000,
|
||||
async () => {
|
||||
await useFormValidate(formRef);
|
||||
data.form.PROVINCE = data.form.POSSESSION[0] || "";
|
||||
data.form.CITY = data.form.POSSESSION[1] || "";
|
||||
data.form.COUNTY = data.form.POSSESSION[2] || "";
|
||||
data.form.VILLAGE = data.form.POSSESSION[3] || "";
|
||||
data.form.STREET = data.form.POSSESSION[4] || "";
|
||||
data.form.COMPANY_AREA = territoryCascaderRef.value.getCheckedNodes();
|
||||
data.form.SMALL = "2";
|
||||
data.form.ECO_TYPE = data.form.ECO_TYPEALL[0] || "";
|
||||
data.form.ECO_TYPE2 = data.form.ECO_TYPEALL[1] || "";
|
||||
data.form.ECO_TYPE_NAME =
|
||||
economicTypeCascaderRef.value.getCheckedNodes()[0];
|
||||
data.form.CORP_TYPE = data.form.INDUSTRYALL[0] || "";
|
||||
data.form.CORP_TYPE2 = data.form.INDUSTRYALL[1] || "";
|
||||
data.form.CORP_TYPE3 = data.form.INDUSTRYALL[2] || "";
|
||||
data.form.CORP_TYPE4 = data.form.INDUSTRYALL[3] || "";
|
||||
data.form.CORP_TYPE_NAME = industryCascaderRef.value.getCheckedNodes()[0];
|
||||
const formData = new FormData();
|
||||
Object.keys(data.form).forEach((key) => {
|
||||
formData.append(key, data.form[key]);
|
||||
});
|
||||
for (let i = 0; i < data.form.bus_images.length; i++) {
|
||||
data.form.bus_images[i].raw &&
|
||||
formData.append("imgFiles", data.form.bus_images[i].raw);
|
||||
}
|
||||
for (let i = 0; i < data.form.four_images.length; i++) {
|
||||
data.form.four_images[i].raw &&
|
||||
formData.append("fourFiles", data.form.four_images[i].raw);
|
||||
}
|
||||
formData.append(
|
||||
"EDITFOURTYPE",
|
||||
data.form.FOURTYPE === data.form.OLDFOURTYPE
|
||||
);
|
||||
await setEnterpriseInfo(formData);
|
||||
back();
|
||||
},
|
||||
{ atBegin: true }
|
||||
);
|
||||
onBeforeUnmount(() => {
|
||||
if (mapInstance) {
|
||||
mapInstance.destroy();
|
||||
mapInstance = null;
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss"></style>
|
|
@ -0,0 +1,259 @@
|
|||
<template>
|
||||
<layout-card>
|
||||
<el-divider content-position="left">基本信息</el-divider>
|
||||
<el-descriptions :column="2" border>
|
||||
<el-descriptions-item label="企业名称">
|
||||
{{ data.info.CORP_NAME }}
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="邮政编码">
|
||||
{{ data.info.POSTAL_CODE }}
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="统一社会信用代码">
|
||||
{{ data.info.CODE }}
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="市行业监管部门">
|
||||
{{ data.info.INDUSTRY_DEPARTMENTName }}
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="属地">
|
||||
{{ data.info.COMPANY_AREA }}
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="监管类型">
|
||||
{{ data.info.REGULARTYPE_NAME }}
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="经济类型">
|
||||
{{ data.info.ECO_TYPE_NAME }}
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="所属行业">
|
||||
{{ data.info.CORP_TYPE_NAME }}
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="企事业单位经营地址">
|
||||
{{ data.info.ADDRESS_BUSINESS }}
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="企业状态">
|
||||
{{ data.info.CORP_STATE_NAME }}
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="经度">
|
||||
{{ data.info.LONGITUDE }}
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="纬度">
|
||||
{{ data.info.LATITUDE }}
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="主要负责人">
|
||||
{{ data.info.CONTACTS }}
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="主要负责人电话">
|
||||
{{ data.info.CONTACTS_PHONE }}
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="法定代表人">
|
||||
{{ data.info.LR_NAME }}
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="法人手机号">
|
||||
{{ data.info.LR_PHONE }}
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="占地面积(㎡)">
|
||||
{{ data.info.AREA_COVERED }}
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="职工人数(人)">
|
||||
{{ data.info.EMPLOYEES }}
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="成立时间">
|
||||
{{ data.info.CREATE_DATE }}
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="注册资金(万元)">
|
||||
{{ data.info.REGCAPITAL }}
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="资产总额(万元)">
|
||||
{{ data.info.TOTALASSETS }}
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="隶属关系">
|
||||
{{ data.info.SUBORDINATIONNAME }}
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="规模">
|
||||
{{ data.info.SCALE_NAME }}
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="是否规模以上">
|
||||
<template v-if="data.info.SCALE_TYPE === 0">否</template>
|
||||
<template v-if="data.info.SCALE_TYPE === 1">是</template>
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="培训行业类型">
|
||||
{{ data.info.TRAINTYPE_NAME }}
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="企业可新建用户数量">
|
||||
{{ data.info.USERS_NUM }}
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item
|
||||
label="四色图"
|
||||
v-if="data.info.FOURTYPE === 1"
|
||||
:span="2"
|
||||
>
|
||||
<img
|
||||
v-viewer
|
||||
v-for="item in data.four_images"
|
||||
:src="item.url"
|
||||
:key="item.IMGFILES_ID"
|
||||
width="100"
|
||||
height="100"
|
||||
class="ml-10"
|
||||
/>
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="营业执照" :span="2">
|
||||
<img
|
||||
v-viewer
|
||||
v-for="item in data.bus_images"
|
||||
:src="item.url"
|
||||
:key="item.IMGFILES_ID"
|
||||
width="100"
|
||||
height="100"
|
||||
class="ml-10"
|
||||
/>
|
||||
</el-descriptions-item>
|
||||
</el-descriptions>
|
||||
<el-divider content-position="left">安全负责人信息</el-divider>
|
||||
<el-descriptions :column="2" border>
|
||||
<el-descriptions-item label="姓名">
|
||||
{{ data.info.SAFETY_NAME }}
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="职务">
|
||||
{{ data.info.SAFETY_POST }}
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="单位电话">
|
||||
{{ data.info.SAFETY_NUMBER }}
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="手机号码">
|
||||
{{ data.info.SAFETY_PHONE }}
|
||||
</el-descriptions-item>
|
||||
</el-descriptions>
|
||||
<el-divider content-position="left">企业相关属性</el-divider>
|
||||
<el-descriptions :column="2" border>
|
||||
<el-descriptions-item label="有无职业卫生信息">
|
||||
<template v-if="data.info.WHETHER_HYGIENE === 0">无</template>
|
||||
<template v-if="data.info.WHETHER_HYGIENE === 1">有</template>
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="有无重大危险源">
|
||||
<template v-if="data.info.WHETHER_HAZARDS === 0">无</template>
|
||||
<template v-if="data.info.WHETHER_HAZARDS === 1">有</template>
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="是否有稀缺大型应急物资或设施">
|
||||
<template v-if="data.info.WHETHER_SCARCE === 0">否</template>
|
||||
<template v-if="data.info.WHETHER_SCARCE === 1">是</template>
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="是否涉及危化品">
|
||||
<template v-if="data.info.WHETHER_CHEMICALS === 0">否</template>
|
||||
<template v-if="data.info.WHETHER_CHEMICALS === 1">是</template>
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="有无特种设备">
|
||||
<template v-if="data.info.WHETHER_SPECIALEQUIPMENT === 0">无</template>
|
||||
<template v-if="data.info.WHETHER_SPECIALEQUIPMENT === 1">有</template>
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="有无特种作业人员">
|
||||
<template v-if="data.info.WHETHER_SPECIALPEOPLE === 0">无</template>
|
||||
<template v-if="data.info.WHETHER_SPECIALPEOPLE === 1">有</template>
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="是否涉及煤气">
|
||||
<template v-if="data.info.WHETHER_COALGAS === 0">否</template>
|
||||
<template v-if="data.info.WHETHER_COALGAS === 1">是</template>
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="是否属于消防重点单位">
|
||||
<template v-if="data.info.WHETHER_FIRE === 0">否</template>
|
||||
<template v-if="data.info.WHETHER_FIRE === 1">是</template>
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="是否在受限空间作业">
|
||||
<template v-if="data.info.WHETHER_CONFINED === 0">否</template>
|
||||
<template v-if="data.info.WHETHER_CONFINED === 1">是</template>
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="是否存在涉爆粉尘作业">
|
||||
<template v-if="data.info.WHETHER_POWDER === 0">否</template>
|
||||
<template v-if="data.info.WHETHER_POWDER === 1">是</template>
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="是否涉及防雷防静电">
|
||||
<template v-if="data.info.WHETHER_LIGHTNING === 0">否</template>
|
||||
<template v-if="data.info.WHETHER_LIGHTNING === 1">是</template>
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="是否持有放射源">
|
||||
<template v-if="data.info.WHETHER_ACTINOGEN === 0">否</template>
|
||||
<template v-if="data.info.WHETHER_ACTINOGEN === 1">是</template>
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="是否涉及液氨制冷">
|
||||
<template v-if="data.info.WHETHER_LIQUIDAMMONIA === 0">否</template>
|
||||
<template v-if="data.info.WHETHER_LIQUIDAMMONIA === 1">是</template>
|
||||
</el-descriptions-item>
|
||||
<el-descriptions-item label="是否涉及危化品管道">
|
||||
<template v-if="data.info.WHETHER_PIPELINE === 0">否</template>
|
||||
<template v-if="data.info.WHETHER_PIPELINE === 1">是</template>
|
||||
</el-descriptions-item>
|
||||
</el-descriptions>
|
||||
<div class="tc mt-10">
|
||||
<el-button
|
||||
type="primary"
|
||||
@click="
|
||||
router.push({ path: '/enterprise_management/information/info/edit' })
|
||||
"
|
||||
>
|
||||
修改
|
||||
</el-button>
|
||||
<el-button @click="fnQrCode">二维码</el-button>
|
||||
</div>
|
||||
<el-dialog
|
||||
v-model="data.qrCodeDialog.visible"
|
||||
title="查看二维码"
|
||||
width="600px"
|
||||
>
|
||||
<div id="printContainer" style="border: 1px dashed #ccc">
|
||||
<div class="tc mt-20">
|
||||
<h1>{{ data.info.CORP_NAME }}</h1>
|
||||
</div>
|
||||
<div class="tc mt-20 mb-20">
|
||||
<img :src="data.qrCodeDialog.src" alt="" />
|
||||
</div>
|
||||
</div>
|
||||
<template #footer>
|
||||
<el-button @click="fnQrCodeDialogChangeShow">关 闭</el-button>
|
||||
<el-button v-print="'#printContainer'" type="primary">打 印</el-button>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</layout-card>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { getEnterpriseInfo } from "@/request/prevention/enterprise_management.js";
|
||||
import { addingPrefixToFile } from "@/assets/js/utils.js";
|
||||
import { reactive } from "vue";
|
||||
import { useRouter } from "vue-router";
|
||||
import { useQRCode } from "@vueuse/integrations/useQRCode";
|
||||
import { ElMessage } from "element-plus";
|
||||
|
||||
const router = useRouter();
|
||||
const data = reactive({
|
||||
info: {},
|
||||
bus_images: [],
|
||||
four_images: [],
|
||||
qrCodeDialog: {
|
||||
visible: false,
|
||||
src: "",
|
||||
},
|
||||
});
|
||||
const fnGetData = async () => {
|
||||
const resData = await getEnterpriseInfo();
|
||||
data.info = resData.pd;
|
||||
data.bus_images = addingPrefixToFile(resData.busImgs);
|
||||
data.four_images = addingPrefixToFile(resData.fourImgs);
|
||||
};
|
||||
fnGetData();
|
||||
const fnQrCodeDialogChangeShow = () => {
|
||||
data.qrCodeDialog.visible = !data.qrCodeDialog.visible;
|
||||
};
|
||||
const fnQrCode = () => {
|
||||
if (data.info.CORPINFO_ID) {
|
||||
fnQrCodeDialogChangeShow();
|
||||
// TODO: 扫码跳转到企业信息页面
|
||||
data.qrCodeDialog.src = useQRCode("https://vueuse.org", {
|
||||
width: 200,
|
||||
height: 200,
|
||||
margin: 1,
|
||||
correctLevel: "H",
|
||||
});
|
||||
} else ElMessage.error("请重新获取二维码");
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss"></style>
|
|
@ -0,0 +1,7 @@
|
|||
<template>
|
||||
<div></div>
|
||||
</template>
|
||||
|
||||
<script setup></script>
|
||||
|
||||
<style lang="scss" scoped></style>
|
|
@ -0,0 +1,184 @@
|
|||
<template>
|
||||
<div class="login">
|
||||
<div class="main">
|
||||
<div class="title">管理平台</div>
|
||||
<div class="form">
|
||||
<el-form
|
||||
ref="formRef"
|
||||
:model="data.form"
|
||||
:rules="data.rules"
|
||||
@submit.prevent="fnLogin"
|
||||
>
|
||||
<el-form-item prop="username">
|
||||
<el-input
|
||||
v-model="data.form.username"
|
||||
placeholder="请输入用户名"
|
||||
tabindex="1"
|
||||
>
|
||||
<template #prepend>
|
||||
<icon-people theme="filled" size="16" fill="#909399" />
|
||||
</template>
|
||||
</el-input>
|
||||
</el-form-item>
|
||||
<el-form-item prop="password">
|
||||
<el-input
|
||||
type="password"
|
||||
v-model="data.form.password"
|
||||
placeholder="请输入密码"
|
||||
tabindex="2"
|
||||
>
|
||||
<template #prepend>
|
||||
<icon-lock
|
||||
theme="filled"
|
||||
size="16"
|
||||
fill="#909399"
|
||||
:strokeWidth="3"
|
||||
/>
|
||||
</template>
|
||||
</el-input>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<verification v-model:verification-pass="verificationPass" />
|
||||
</el-form-item>
|
||||
<el-form-item class="button">
|
||||
<el-button native-type="submit">登录</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { reactive, ref } from "vue";
|
||||
import { useRouter } from "vue-router";
|
||||
import { ElMessage } from "element-plus";
|
||||
import Verification from "@/components/verification/index";
|
||||
import { useUserStore } from "@/pinia/user";
|
||||
import { Login } from "@/request/api";
|
||||
import { debounce } from "throttle-debounce";
|
||||
import useFormValidate from "@/assets/js/useFormValidate.js";
|
||||
|
||||
const router = useRouter();
|
||||
const formRef = ref(null);
|
||||
const verificationPass = ref(false);
|
||||
const userStore = useUserStore();
|
||||
const data = reactive({
|
||||
form: {
|
||||
username: "",
|
||||
password: "",
|
||||
},
|
||||
rules: {
|
||||
username: [{ required: true, message: "请输入用户名", trigger: "blur" }],
|
||||
password: [{ required: true, message: "请输入密码", trigger: "blur" }],
|
||||
},
|
||||
});
|
||||
|
||||
const fnLogin = debounce(
|
||||
1000,
|
||||
() => {
|
||||
if (import.meta.env.DEV) {
|
||||
fnSubmitLogin();
|
||||
return;
|
||||
}
|
||||
if (verificationPass.value) {
|
||||
fnSubmitLogin();
|
||||
} else {
|
||||
ElMessage.warning("请进行登录验证");
|
||||
}
|
||||
},
|
||||
{ atBegin: true }
|
||||
);
|
||||
const fnSubmitLogin = async () => {
|
||||
await useFormValidate(formRef, "请输入用户名密码");
|
||||
// eslint-disable-next-line no-undef
|
||||
const jsencrypt = new JSEncrypt();
|
||||
jsencrypt.setPublicKey(
|
||||
"MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDUoHAavCikaZxjlDM6Km8cX+ye78F4oF39AcEfnE1p2Yn9pJ9WFxYZ4Vkh6F8SKMi7k4nYsKceqB1RwG996SvHQ5C3pM3nbXCP4K15ad6QhN4a7lzlbLhiJcyIKszvvK8ncUDw8mVQ0j/2mwxv05yH6LN9OKU6Hzm1ninpWeE+awIDAQAB"
|
||||
);
|
||||
const KEYDATA = jsencrypt.encrypt(
|
||||
"zcloudchina" + data.form.username + ",zy," + data.form.password
|
||||
);
|
||||
|
||||
const resData = await Login({
|
||||
KEYDATA,
|
||||
SOURCE: 1,
|
||||
});
|
||||
await userStore.setUserInfo({
|
||||
...userStore.getUserInfo,
|
||||
...resData,
|
||||
});
|
||||
await router.replace("/index");
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.login {
|
||||
width: 100%;
|
||||
max-width: 1920px;
|
||||
margin: 0 auto;
|
||||
height: 100vh;
|
||||
position: relative;
|
||||
background: url("../../assets/images/public/loginbg.jpg") no-repeat top center;
|
||||
background-size: 100% 100%;
|
||||
|
||||
.main {
|
||||
width: 600px;
|
||||
padding-top: 280px;
|
||||
margin: 0 auto;
|
||||
|
||||
.title {
|
||||
width: 100%;
|
||||
text-align: center;
|
||||
font-size: 36px;
|
||||
color: #ffffff;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.form {
|
||||
width: 478px;
|
||||
min-height: 290px;
|
||||
padding: 20px 40px;
|
||||
border-radius: 5px;
|
||||
background: rgba(8, 22, 59, 0.36);
|
||||
border: 1px solid rgba(31, 58, 136, 0.9);
|
||||
margin: 60px auto 0;
|
||||
box-shadow: 0 0 20px rgba(47, 85, 124, 0.2) inset;
|
||||
|
||||
.title {
|
||||
color: #666;
|
||||
text-align: center;
|
||||
font-weight: 700;
|
||||
font-size: 18px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.el-form-item {
|
||||
width: 325px;
|
||||
margin: 20px auto;
|
||||
|
||||
.el-input {
|
||||
height: 40px;
|
||||
}
|
||||
}
|
||||
|
||||
.button {
|
||||
.el-button {
|
||||
background: #0948d5;
|
||||
border: 1px solid #276aff;
|
||||
height: 45px;
|
||||
width: 100%;
|
||||
color: #ffffff;
|
||||
margin-top: 10px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
:deep {
|
||||
.el-input-group__prepend {
|
||||
padding-top: 5px !important;
|
||||
}
|
||||
}
|
||||
</style>
|
|
@ -0,0 +1,92 @@
|
|||
import { defineConfig, loadEnv } from "vite";
|
||||
import vue from "@vitejs/plugin-vue";
|
||||
import eslintPlugin from "vite-plugin-eslint";
|
||||
import AutoImport from "unplugin-auto-import/vite";
|
||||
import Components from "unplugin-vue-components/vite";
|
||||
import {
|
||||
ElementPlusResolver,
|
||||
VantResolver,
|
||||
} from "unplugin-vue-components/resolvers";
|
||||
// import basicSsl from "@vitejs/plugin-basic-ssl";
|
||||
import removeConsole from "vite-plugin-remove-console";
|
||||
import EnhanceLog from "vite-plugin-enhance-log";
|
||||
|
||||
export default ({ mode }) => {
|
||||
return defineConfig({
|
||||
plugins: [
|
||||
vue(),
|
||||
eslintPlugin(),
|
||||
removeConsole({
|
||||
includes: [
|
||||
"assert",
|
||||
"clear",
|
||||
"count",
|
||||
"countReset",
|
||||
"createTask",
|
||||
"debug",
|
||||
"dir",
|
||||
"dirxml",
|
||||
"error",
|
||||
"group",
|
||||
"groupCollapsed",
|
||||
"groupEnd",
|
||||
"info",
|
||||
"log",
|
||||
"profile",
|
||||
"profileEnd",
|
||||
"table",
|
||||
"time",
|
||||
"timeEnd",
|
||||
"timeLog",
|
||||
"timeStamp",
|
||||
"trace",
|
||||
"warn",
|
||||
],
|
||||
}),
|
||||
AutoImport({
|
||||
resolvers: [ElementPlusResolver()],
|
||||
}),
|
||||
Components({
|
||||
resolvers: [ElementPlusResolver(), VantResolver()],
|
||||
}),
|
||||
EnhanceLog({
|
||||
preTip: "🚀🚀🚀🚀🚀🚀🚀🚀🚀🚀",
|
||||
}),
|
||||
// basicSsl(),
|
||||
],
|
||||
server: {
|
||||
// https: true,
|
||||
host: true, // 本机的局域网IP,不然其他人无法通过IP访问到,0.0.0.0或true会自动获取本机的IP
|
||||
port: 8099, // 端口号
|
||||
open: true, // 是否自动打开浏览器
|
||||
proxy: {
|
||||
[loadEnv(mode, process.cwd()).VITE_PROXY]: {
|
||||
target: loadEnv(mode, process.cwd()).VITE_BASE_URL,
|
||||
changeOrigin: true,
|
||||
ws: true,
|
||||
rewrite: (path) => path.replace(/^\/api/, ""),
|
||||
},
|
||||
},
|
||||
},
|
||||
resolve: {
|
||||
alias: {
|
||||
"@": "/src", // 别名,@代表src目录
|
||||
},
|
||||
extensions: [".mjs", ".js", ".ts", ".jsx", ".tsx", ".json", ".vue"], // 引入文件时哪些后缀名可以不写
|
||||
},
|
||||
build: {
|
||||
rollupOptions: {
|
||||
// 打包多个入口文件
|
||||
// input: {
|
||||
// admin: path.resolve(__dirname, "index.html"),
|
||||
// 其它入口文件路径需要为src/views/*/index.html
|
||||
// },
|
||||
output: {
|
||||
chunkFileNames: "static/js/[name]-[hash].js",
|
||||
entryFileNames: "static/js/[name]-[hash].js",
|
||||
assetFileNames: "static/[ext]/name-[hash].[ext]",
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
};
|