“feat:数字农业产业运营平台”

This commit is contained in:
wangzenghua 2025-01-20 08:09:54 +00:00
parent 0407bb6f87
commit 930ce1f1f3
173 changed files with 25971 additions and 0 deletions

25
.gitignore vendored Normal file
View File

@ -0,0 +1,25 @@
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
lerna-debug.log*
node_modules
dist
dist-ssr
.history
*.local
# Editor directories and files
.vscode/*
!.vscode/extensions.json
.idea
.DS_Store
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw?

9
main/.editorconfig Normal file
View File

@ -0,0 +1,9 @@
root = true
[*.{js,jsx,ts,tsx,vue}]
charset = utf-8
indent_style = space
indent_size = 2
insert_final_newline = true
trim_trailing_whitespace = true
end_of_line = auto

8
main/.env.development Normal file
View File

@ -0,0 +1,8 @@
# 开发环境
VITE_PORT = 9000
VITE_APP_NAME = 'daimp-front-main'
VITE_APP_BASE_API = "https://mock.mengxuegu.com/mock/664ef7fee45d2156fa209ee4/api-qiankun"
VITE_APP_BASE_URL = 'http://192.168.18.158:9080'
VITE_APP_SUB_VUE = '//localhost:9526/sub-vue/'
VITE_APP_SUB_ADMIN = '//localhost:9527/sub-admin/'

6
main/.env.production Normal file
View File

@ -0,0 +1,6 @@
# 正式环境
VITE_APP_NAME = 'daimp-front-main'
VITE_APP_BASE_API = ""
VITE_APP_BASE_URL = ''
VITE_APP_SUB_VUE = '//localhost:9526/sub-vue/'
VITE_APP_SUB_ADMIN = '//localhost:9527/sub-admin/'

14
main/.eslintignore Normal file
View File

@ -0,0 +1,14 @@
*.sh
*.md
*.woff
*.ttf
.vscode
.idea
.husky
.local
dist
src/assets
node_modules
Dockerfile
stats.html
tailwind.config.js

48
main/.eslintrc.cjs Normal file
View File

@ -0,0 +1,48 @@
module.exports = {
env: {
browser: true,
es2021: true,
node: true,
},
extends: [
'eslint-config-prettier',
'eslint:recommended',
// 'plugin:@typescript-eslint/recommended',
'plugin:vue/vue3-recommended',
'plugin:vue/vue3-essential',
'plugin:prettier/recommended',
],
overrides: [
{
env: {
node: true,
},
files: ['.eslintrc.{js,cjs}'],
parserOptions: {
sourceType: 'script',
},
},
],
parserOptions: {
ecmaVersion: 'latest',
sourceType: 'module',
requireConfigFile: false,
parser: '@babel/eslint-parser',
// parser: '@typescript-eslint/parser',
},
plugins: ['vue', 'prettier'],
globals: {
defineProps: 'readonly',
defineEmits: 'readonly',
defineExpose: 'readonly',
withDefaults: 'readonly',
},
// 这里时配置规则的,自己看情况配置
rules: {
'prettier/prettier': 'error',
'no-debugger': 'off',
'no-unused-vars': 'off',
'vue/multi-word-component-names': 'off',
'vue/no-unused-vars': 'off',
},
};

25
main/.gitignore vendored Normal file
View File

@ -0,0 +1,25 @@
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
lerna-debug.log*
node_modules
dist
dist-ssr
.history
*.local
# Editor directories and files
.vscode/*
!.vscode/extensions.json
.idea
.DS_Store
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw?

20
main/.prettierignore Normal file
View File

@ -0,0 +1,20 @@
## OS
.DS_Store
node_modules
.idea
.editorconfig
package-lock.json
.npmrc
# Ignored suffix
*.log
*.md
*.svg
*.png
*ignore
## Local
## Built-files
.cache
dist

21
main/.prettierrc.cjs Normal file
View File

@ -0,0 +1,21 @@
module.exports = {
printWidth: 150,
tabWidth: 2,
useTabs: false,
semi: true,
singleQuote: true,
quoteProps: 'as-needed',
jsxSingleQuote: false,
trailingComma: 'es5',
bracketSpacing: true,
jsxBracketSameLine: false,
arrowParens: 'always',
rangeStart: 0,
rangeEnd: Infinity,
requirePragma: false,
insertPragma: false,
proseWrap: 'preserve',
htmlWhitespaceSensitivity: 'css',
vueIndentScriptAndStyle: false,
endOfLine: 'auto',
};

17
main/.stylelintignore Normal file
View File

@ -0,0 +1,17 @@
# .stylelintignore
# 旧的不需打包的样式库
*.min.css
# 其他类型文件
*.js
*.jpg
*.png
*.eot
*.ttf
*.woff
*.json
# 测试和打包目录
/dist/*
/node_modules/*
/src/assets/*

124
main/.stylelintrc.cjs Normal file
View File

@ -0,0 +1,124 @@
module.exports = {
root: true,
plugins: ['stylelint-order', 'stylelint-scss'],
extends: [
'stylelint-config-standard',
'stylelint-config-standard-scss',
'stylelint-config-prettier',
'stylelint-config-html/vue',
'stylelint-config-recommended-vue',
'stylelint-config-recommended-scss',
],
overrides: [
{
files: ['**/*.{html,vue}'],
customSyntax: 'postcss-html',
},
],
rules: {
indentation: 2,
'selector-pseudo-element-no-unknown': [
true,
{
ignorePseudoElements: ['v-deep', ':deep'],
},
],
'number-leading-zero': 'always',
'no-descending-specificity': null,
'function-url-quotes': 'always',
'string-quotes': 'single',
'unit-case': null,
'color-hex-case': 'lower',
'color-hex-length': 'long',
'rule-empty-line-before': 'never',
'font-family-no-missing-generic-family-keyword': null,
'selector-type-no-unknown': null,
'block-opening-brace-space-before': 'always',
'at-rule-no-unknown': null,
'no-duplicate-selectors': null,
'property-no-unknown': null,
'no-empty-source': null,
'selector-class-pattern': null,
'keyframes-name-pattern': null,
'selector-pseudo-class-no-unknown': [true, { ignorePseudoClasses: ['global', 'deep'] }],
'function-no-unknown': null,
'order/properties-order': [
'position',
'top',
'right',
'bottom',
'left',
'z-index',
'display',
'justify-content',
'align-items',
'float',
'clear',
'overflow',
'overflow-x',
'overflow-y',
'margin',
'margin-top',
'margin-right',
'margin-bottom',
'margin-left',
'padding',
'padding-top',
'padding-right',
'padding-bottom',
'padding-left',
'width',
'min-width',
'max-width',
'height',
'min-height',
'max-height',
'font-size',
'font-family',
'font-weight',
'border',
'border-style',
'border-width',
'border-color',
'border-top',
'border-top-style',
'border-top-width',
'border-top-color',
'border-right',
'border-right-style',
'border-right-width',
'border-right-color',
'border-bottom',
'border-bottom-style',
'border-bottom-width',
'border-bottom-color',
'border-left',
'border-left-style',
'border-left-width',
'border-left-color',
'border-radius',
'text-align',
'text-justify',
'text-indent',
'text-overflow',
'text-decoration',
'white-space',
'color',
'background',
'background-position',
'background-repeat',
'background-size',
'background-color',
'background-clip',
'opacity',
'filter',
'list-style',
'outline',
'visibility',
'box-shadow',
'text-shadow',
'resize',
'transition',
],
},
};

4
main/README.md Normal file
View File

@ -0,0 +1,4 @@
# 英壹集团

13
main/index.html Normal file
View File

@ -0,0 +1,13 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="/logo.png" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title></title>
</head>
<body>
<div id="root"></div>
<script type="module" src="/src/main.js"></script>
</body>
</html>

10
main/jsconfig.json Normal file
View File

@ -0,0 +1,10 @@
{
"compilerOptions": {
"baseUrl": ".",
"jsx": "preserve",
"paths": {
"@/*": ["src/*"],
},
},
"exclude": ["node_modules", "dist"],
}

82
main/package.json Normal file
View File

@ -0,0 +1,82 @@
{
"name": "daimp-front-main",
"private": true,
"version": "0.0.0",
"type": "module",
"scripts": {
"dev": "vite --mode development",
"build": "vite build --mode production",
"preview": "vite preview",
"format": "prettier --write 'src/**/*.{vue,ts,tsx,js,jsx,css,less,scss,json,md}'",
"eslint": "npx eslint --init",
"lint": "npm run lint:script && npm run lint:style",
"lint:style": "stylelint 'src/**/*.{vue,scss,css,sass,less}' --fix",
"lint:script": "eslint --ext .js,.ts,.tsx,.vue --fix --quiet ./src",
"commit": "git add -A && czg && git push",
"prepare": "husky install"
},
"dependencies": {
"@element-plus/icons-vue": "^2.3.1",
"axios": "^1.6.5",
"dayjs": "^1.11.11",
"element-plus": "^2.7.3",
"file-saver": "^2.0.5",
"js-base64": "^3.7.7",
"js-cookie": "^3.0.5",
"jszip": "^3.10.1",
"jszip-utils": "^0.1.0",
"lib-flexible-computer": "^1.0.2",
"lodash": "^4.17.21",
"nprogress": "^0.2.0",
"path-browserify": "^1.0.1",
"pinia": "^2.1.7",
"pinia-plugin-persistedstate": "^3.2.1",
"postcss-pxtorem": "^6.1.0",
"qiankun": "^2.10.16",
"screenfull": "^6.0.2",
"vite-plugin-svg-icons": "^2.0.1",
"vue": "^3.5.11",
"vue-router": "^4.2.5"
},
"devDependencies": {
"@babel/core": "^7.23.7",
"@babel/eslint-parser": "^7.23.3",
"@types/path-browserify": "^1.0.2",
"@vitejs/plugin-vue": "^4.5.2",
"@wangeditor/editor": "^5.1.23",
"@wangeditor/editor-for-vue": "^5.1.12",
"autoprefixer": "^10.4.17",
"eslint": "^8.56.0",
"eslint-config-prettier": "^9.1.0",
"eslint-plugin-prettier": "^5.1.3",
"eslint-plugin-vue": "^9.20.1",
"husky": "^9.0.6",
"lint-staged": "^15.2.0",
"postcss": "^8.4.33",
"postcss-html": "^1.6.0",
"postcss-import": "^16.0.0",
"prettier": "^3.2.4",
"sass": "^1.70.0",
"stylelint": "^16.2.0",
"stylelint-config-html": "^1.1.0",
"stylelint-config-prettier": "^9.0.5",
"stylelint-config-rational-order": "^0.1.2",
"stylelint-config-recommended": "^14.0.0",
"stylelint-config-recommended-scss": "^14.0.0",
"stylelint-config-recommended-vue": "^1.5.0",
"stylelint-config-standard": "^36.0.0",
"stylelint-config-standard-scss": "^13.0.0",
"stylelint-order": "^6.0.4",
"stylelint-scss": "^6.1.0",
"terser": "^5.27.0",
"unplugin-auto-import": "^0.17.3",
"unplugin-vue-components": "^0.26.0",
"vite": "^5.0.8",
"vite-plugin-compression": "^0.5.1",
"vite-plugin-eslint": "^1.8.1",
"vite-plugin-html": "^3.2.2",
"vite-plugin-progress": "^0.0.7",
"vite-plugin-qiankun": "^1.0.15",
"vite-plugin-vue-setup-extend": "^0.4.0"
}
}

BIN
main/public/logo.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

1
main/public/vite.svg Normal file
View File

@ -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

45
main/src/App.vue Normal file
View File

@ -0,0 +1,45 @@
<template>
<div id="app">
<!-- <el-button v-for="item in microApps" :key="item.activeRule" type="primary" @click="gotoPage(item)">{{ item.name }}</el-button> -->
<router-view />
</div>
</template>
<script setup name="App">
import { reactive, provide, nextTick } from 'vue';
import { microApps } from '@/micro/app';
import actions from '@/micro/actions';
const state = reactive({
isRouterAlive: true,
});
const gotoPage = (row) => {
const data = {};
switch (row.name) {
case 'sub-vue':
data.title = 'sub-vue';
break;
case 'sub-admin':
data.title = 'sub-admin';
break;
}
//
actions.setGlobalState({
data: data,
});
window.history.pushState({}, row.name, row.activeRule);
};
const reload = () => {
state.isRouterAlive = false;
nextTick(() => {
state.isRouterAlive = true;
});
};
provide('reload', reload);
</script>
<style lang="scss">
@import './styles/style.scss';
</style>

33
main/src/apis/login.js Normal file
View File

@ -0,0 +1,33 @@
import request from '@/utils/axios';
/**
* @Title: 登录
*/
export function Login(data, token) {
return request('/self/login', {
method: 'POST',
data,
headers: {
'Fairies-Captcha-Token': token,
},
});
}
/**
* @Title: 登出
*/
export function LogOut() {
return request('/self/logout', {
method: 'POST',
});
}
/**
* @Title: 验证码
*/
export function GetCaptcha() {
return request('/self/captcha', {
method: 'GET',
responseType: 'arraybuffer',
});
}

View File

@ -0,0 +1,539 @@
/* Logo 字体 */
@font-face {
font-family: "iconfont logo";
src: url('https://at.alicdn.com/t/font_985780_km7mi63cihi.eot?t=1545807318834');
src: url('https://at.alicdn.com/t/font_985780_km7mi63cihi.eot?t=1545807318834#iefix') format('embedded-opentype'),
url('https://at.alicdn.com/t/font_985780_km7mi63cihi.woff?t=1545807318834') format('woff'),
url('https://at.alicdn.com/t/font_985780_km7mi63cihi.ttf?t=1545807318834') format('truetype'),
url('https://at.alicdn.com/t/font_985780_km7mi63cihi.svg?t=1545807318834#iconfont') format('svg');
}
.logo {
font-family: "iconfont logo";
font-size: 160px;
font-style: normal;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
/* tabs */
.nav-tabs {
position: relative;
}
.nav-tabs .nav-more {
position: absolute;
right: 0;
bottom: 0;
height: 42px;
line-height: 42px;
color: #666;
}
#tabs {
border-bottom: 1px solid #eee;
}
#tabs li {
cursor: pointer;
width: 100px;
height: 40px;
line-height: 40px;
text-align: center;
font-size: 16px;
border-bottom: 2px solid transparent;
position: relative;
z-index: 1;
margin-bottom: -1px;
color: #666;
}
#tabs .active {
border-bottom-color: #f00;
color: #222;
}
.tab-container .content {
display: none;
}
/* 页面布局 */
.main {
padding: 30px 100px;
width: 960px;
margin: 0 auto;
}
.main .logo {
color: #333;
text-align: left;
margin-bottom: 30px;
line-height: 1;
height: 110px;
margin-top: -50px;
overflow: hidden;
*zoom: 1;
}
.main .logo a {
font-size: 160px;
color: #333;
}
.helps {
margin-top: 40px;
}
.helps pre {
padding: 20px;
margin: 10px 0;
border: solid 1px #e7e1cd;
background-color: #fffdef;
overflow: auto;
}
.icon_lists {
width: 100% !important;
overflow: hidden;
*zoom: 1;
}
.icon_lists li {
width: 100px;
margin-bottom: 10px;
margin-right: 20px;
text-align: center;
list-style: none !important;
cursor: default;
}
.icon_lists li .code-name {
line-height: 1.2;
}
.icon_lists .icon {
display: block;
height: 100px;
line-height: 100px;
font-size: 42px;
margin: 10px auto;
color: #333;
-webkit-transition: font-size 0.25s linear, width 0.25s linear;
-moz-transition: font-size 0.25s linear, width 0.25s linear;
transition: font-size 0.25s linear, width 0.25s linear;
}
.icon_lists .icon:hover {
font-size: 100px;
}
.icon_lists .svg-icon {
/* 通过设置 font-size 来改变图标大小 */
width: 1em;
/* 图标和文字相邻时,垂直对齐 */
vertical-align: -0.15em;
/* 通过设置 color 来改变 SVG 的颜色/fill */
fill: currentColor;
/* path stroke 溢出 viewBox 部分在 IE 下会显示
normalize.css 中也包含这行 */
overflow: hidden;
}
.icon_lists li .name,
.icon_lists li .code-name {
color: #666;
}
/* markdown 样式 */
.markdown {
color: #666;
font-size: 14px;
line-height: 1.8;
}
.highlight {
line-height: 1.5;
}
.markdown img {
vertical-align: middle;
max-width: 100%;
}
.markdown h1 {
color: #404040;
font-weight: 500;
line-height: 40px;
margin-bottom: 24px;
}
.markdown h2,
.markdown h3,
.markdown h4,
.markdown h5,
.markdown h6 {
color: #404040;
margin: 1.6em 0 0.6em 0;
font-weight: 500;
clear: both;
}
.markdown h1 {
font-size: 28px;
}
.markdown h2 {
font-size: 22px;
}
.markdown h3 {
font-size: 16px;
}
.markdown h4 {
font-size: 14px;
}
.markdown h5 {
font-size: 12px;
}
.markdown h6 {
font-size: 12px;
}
.markdown hr {
height: 1px;
border: 0;
background: #e9e9e9;
margin: 16px 0;
clear: both;
}
.markdown p {
margin: 1em 0;
}
.markdown>p,
.markdown>blockquote,
.markdown>.highlight,
.markdown>ol,
.markdown>ul {
width: 80%;
}
.markdown ul>li {
list-style: circle;
}
.markdown>ul li,
.markdown blockquote ul>li {
margin-left: 20px;
padding-left: 4px;
}
.markdown>ul li p,
.markdown>ol li p {
margin: 0.6em 0;
}
.markdown ol>li {
list-style: decimal;
}
.markdown>ol li,
.markdown blockquote ol>li {
margin-left: 20px;
padding-left: 4px;
}
.markdown code {
margin: 0 3px;
padding: 0 5px;
background: #eee;
border-radius: 3px;
}
.markdown strong,
.markdown b {
font-weight: 600;
}
.markdown>table {
border-collapse: collapse;
border-spacing: 0px;
empty-cells: show;
border: 1px solid #e9e9e9;
width: 95%;
margin-bottom: 24px;
}
.markdown>table th {
white-space: nowrap;
color: #333;
font-weight: 600;
}
.markdown>table th,
.markdown>table td {
border: 1px solid #e9e9e9;
padding: 8px 16px;
text-align: left;
}
.markdown>table th {
background: #F7F7F7;
}
.markdown blockquote {
font-size: 90%;
color: #999;
border-left: 4px solid #e9e9e9;
padding-left: 0.8em;
margin: 1em 0;
}
.markdown blockquote p {
margin: 0;
}
.markdown .anchor {
opacity: 0;
transition: opacity 0.3s ease;
margin-left: 8px;
}
.markdown .waiting {
color: #ccc;
}
.markdown h1:hover .anchor,
.markdown h2:hover .anchor,
.markdown h3:hover .anchor,
.markdown h4:hover .anchor,
.markdown h5:hover .anchor,
.markdown h6:hover .anchor {
opacity: 1;
display: inline-block;
}
.markdown>br,
.markdown>p>br {
clear: both;
}
.hljs {
display: block;
background: white;
padding: 0.5em;
color: #333333;
overflow-x: auto;
}
.hljs-comment,
.hljs-meta {
color: #969896;
}
.hljs-string,
.hljs-variable,
.hljs-template-variable,
.hljs-strong,
.hljs-emphasis,
.hljs-quote {
color: #df5000;
}
.hljs-keyword,
.hljs-selector-tag,
.hljs-type {
color: #a71d5d;
}
.hljs-literal,
.hljs-symbol,
.hljs-bullet,
.hljs-attribute {
color: #0086b3;
}
.hljs-section,
.hljs-name {
color: #63a35c;
}
.hljs-tag {
color: #333333;
}
.hljs-title,
.hljs-attr,
.hljs-selector-id,
.hljs-selector-class,
.hljs-selector-attr,
.hljs-selector-pseudo {
color: #795da3;
}
.hljs-addition {
color: #55a532;
background-color: #eaffea;
}
.hljs-deletion {
color: #bd2c00;
background-color: #ffecec;
}
.hljs-link {
text-decoration: underline;
}
/* 代码高亮 */
/* PrismJS 1.15.0
https://prismjs.com/download.html#themes=prism&languages=markup+css+clike+javascript */
/**
* prism.js default theme for JavaScript, CSS and HTML
* Based on dabblet (http://dabblet.com)
* @author Lea Verou
*/
code[class*="language-"],
pre[class*="language-"] {
color: black;
background: none;
text-shadow: 0 1px white;
font-family: Consolas, Monaco, 'Andale Mono', 'Ubuntu Mono', monospace;
text-align: left;
white-space: pre;
word-spacing: normal;
word-break: normal;
word-wrap: normal;
line-height: 1.5;
-moz-tab-size: 4;
-o-tab-size: 4;
tab-size: 4;
-webkit-hyphens: none;
-moz-hyphens: none;
-ms-hyphens: none;
hyphens: none;
}
pre[class*="language-"]::-moz-selection,
pre[class*="language-"] ::-moz-selection,
code[class*="language-"]::-moz-selection,
code[class*="language-"] ::-moz-selection {
text-shadow: none;
background: #b3d4fc;
}
pre[class*="language-"]::selection,
pre[class*="language-"] ::selection,
code[class*="language-"]::selection,
code[class*="language-"] ::selection {
text-shadow: none;
background: #b3d4fc;
}
@media print {
code[class*="language-"],
pre[class*="language-"] {
text-shadow: none;
}
}
/* Code blocks */
pre[class*="language-"] {
padding: 1em;
margin: .5em 0;
overflow: auto;
}
:not(pre)>code[class*="language-"],
pre[class*="language-"] {
background: #f5f2f0;
}
/* Inline code */
:not(pre)>code[class*="language-"] {
padding: .1em;
border-radius: .3em;
white-space: normal;
}
.token.comment,
.token.prolog,
.token.doctype,
.token.cdata {
color: slategray;
}
.token.punctuation {
color: #999;
}
.namespace {
opacity: .7;
}
.token.property,
.token.tag,
.token.boolean,
.token.number,
.token.constant,
.token.symbol,
.token.deleted {
color: #905;
}
.token.selector,
.token.attr-name,
.token.string,
.token.char,
.token.builtin,
.token.inserted {
color: #690;
}
.token.operator,
.token.entity,
.token.url,
.language-css .token.string,
.style .token.string {
color: #9a6e3a;
background: hsla(0, 0%, 100%, .5);
}
.token.atrule,
.token.attr-value,
.token.keyword {
color: #07a;
}
.token.function,
.token.class-name {
color: #DD4A68;
}
.token.regex,
.token.important,
.token.variable {
color: #e90;
}
.token.important,
.token.bold {
font-weight: bold;
}
.token.italic {
font-style: italic;
}
.token.entity {
cursor: help;
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,14 @@
@font-face {
font-family: "iconfont"; /* Project id 4709068 */
src: url('iconfont.woff2?t=1729754563434') format('woff2'),
url('iconfont.woff?t=1729754563434') format('woff'),
url('iconfont.ttf?t=1729754563434') format('truetype');
}
.iconfont {
font-family: "iconfont" !important;
font-size: 16px;
font-style: normal;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,408 @@
{
"id": "4709068",
"name": "sub-admin",
"font_family": "iconfont",
"css_prefix_text": "icon-",
"description": "react/vue项目后台管理平台",
"glyphs": [
{
"icon_id": "588769",
"name": "待付款",
"font_class": "pay",
"unicode": "e626",
"unicode_decimal": 58918
},
{
"icon_id": "653976",
"name": "已完成",
"font_class": "complete",
"unicode": "e60d",
"unicode_decimal": 58893
},
{
"icon_id": "1001767",
"name": "修改密码",
"font_class": "edit",
"unicode": "e618",
"unicode_decimal": 58904
},
{
"icon_id": "1068811",
"name": "待发货",
"font_class": "deliver",
"unicode": "e622",
"unicode_decimal": 58914
},
{
"icon_id": "1313074",
"name": "待收货",
"font_class": "receive",
"unicode": "e611",
"unicode_decimal": 58897
},
{
"icon_id": "2121739",
"name": "品牌",
"font_class": "brand",
"unicode": "e60f",
"unicode_decimal": 58895
},
{
"icon_id": "2846231",
"name": "商品规格",
"font_class": "sku",
"unicode": "e607",
"unicode_decimal": 58887
},
{
"icon_id": "3978297",
"name": "退出",
"font_class": "exit",
"unicode": "e641",
"unicode_decimal": 58945
},
{
"icon_id": "8295386",
"name": "商品类型",
"font_class": "type",
"unicode": "e87f",
"unicode_decimal": 59519
},
{
"icon_id": "8875707",
"name": "收货地址",
"font_class": "address",
"unicode": "e666",
"unicode_decimal": 58982
},
{
"icon_id": "12797032",
"name": "售后",
"font_class": "post-sale",
"unicode": "e63c",
"unicode_decimal": 58940
},
{
"icon_id": "21673621",
"name": "商品分类",
"font_class": "category",
"unicode": "e602",
"unicode_decimal": 58882
},
{
"icon_id": "3703028",
"name": "文章管理",
"font_class": "article",
"unicode": "e662",
"unicode_decimal": 58978
},
{
"icon_id": "1218184",
"name": "销售明细",
"font_class": "data4",
"unicode": "e60c",
"unicode_decimal": 58892
},
{
"icon_id": "2230090",
"name": "销售明细",
"font_class": "data5",
"unicode": "e6be",
"unicode_decimal": 59070
},
{
"icon_id": "6882983",
"name": "充值记录",
"font_class": "recharge-record",
"unicode": "e614",
"unicode_decimal": 58900
},
{
"icon_id": "34611004",
"name": "充值规则",
"font_class": "recharge-rule",
"unicode": "e628",
"unicode_decimal": 58920
},
{
"icon_id": "15562252",
"name": "用户画像",
"font_class": "user-profile",
"unicode": "e783",
"unicode_decimal": 59267
},
{
"icon_id": "18747445",
"name": "成就",
"font_class": "achieve",
"unicode": "e616",
"unicode_decimal": 58902
},
{
"icon_id": "33848542",
"name": "我的-段位",
"font_class": "activity-level",
"unicode": "e61a",
"unicode_decimal": 58906
},
{
"icon_id": "20406821",
"name": "皮肤",
"font_class": "skins",
"unicode": "e790",
"unicode_decimal": 59280
},
{
"icon_id": "2214847",
"name": "积分商城",
"font_class": "data1",
"unicode": "e996",
"unicode_decimal": 59798
},
{
"icon_id": "14233304",
"name": "价值投资",
"font_class": "data2",
"unicode": "e661",
"unicode_decimal": 58977
},
{
"icon_id": "23059951",
"name": "费用统计",
"font_class": "data3",
"unicode": "e632",
"unicode_decimal": 58930
},
{
"icon_id": "2199049",
"name": "数据报表",
"font_class": "data",
"unicode": "e64e",
"unicode_decimal": 58958
},
{
"icon_id": "36257316",
"name": "游戏管理",
"font_class": "game",
"unicode": "e6d0",
"unicode_decimal": 59088
},
{
"icon_id": "11913396",
"name": "banner",
"font_class": "banner",
"unicode": "e613",
"unicode_decimal": 58899
},
{
"icon_id": "35264323",
"name": "核销码核销",
"font_class": "verification",
"unicode": "e601",
"unicode_decimal": 58881
},
{
"icon_id": "6514128",
"name": "结算管理",
"font_class": "balance",
"unicode": "e6b9",
"unicode_decimal": 59065
},
{
"icon_id": "12025983",
"name": "退货退款",
"font_class": "refund",
"unicode": "e7af",
"unicode_decimal": 59311
},
{
"icon_id": "1207908",
"name": "wechat",
"font_class": "wechat",
"unicode": "e681",
"unicode_decimal": 59009
},
{
"icon_id": "27188513",
"name": "alipay",
"font_class": "alipay",
"unicode": "e61e",
"unicode_decimal": 58910
},
{
"icon_id": "11111017",
"name": "会员",
"font_class": "user",
"unicode": "e67f",
"unicode_decimal": 59007
},
{
"icon_id": "630079",
"name": "我的优惠券",
"font_class": "coupon",
"unicode": "e65a",
"unicode_decimal": 58970
},
{
"icon_id": "2046370",
"name": "会员等级",
"font_class": "level",
"unicode": "e7d8",
"unicode_decimal": 59352
},
{
"icon_id": "2569868",
"name": "活动",
"font_class": "activity",
"unicode": "e67b",
"unicode_decimal": 59003
},
{
"icon_id": "2681698",
"name": "门店",
"font_class": "shop",
"unicode": "e60a",
"unicode_decimal": 58890
},
{
"icon_id": "2811147",
"name": "会员",
"font_class": "member",
"unicode": "e640",
"unicode_decimal": 58944
},
{
"icon_id": "4560182",
"name": "会员充值",
"font_class": "recharge",
"unicode": "e799",
"unicode_decimal": 59289
},
{
"icon_id": "5880283",
"name": "营销",
"font_class": "marketing",
"unicode": "e765",
"unicode_decimal": 59237
},
{
"icon_id": "6982618",
"name": "商品规格",
"font_class": "goods-sku",
"unicode": "e6d7",
"unicode_decimal": 59095
},
{
"icon_id": "7307041",
"name": "商家入驻",
"font_class": "store",
"unicode": "e62b",
"unicode_decimal": 58923
},
{
"icon_id": "11639867",
"name": "小店商品库",
"font_class": "goods-store",
"unicode": "e6c6",
"unicode_decimal": 59078
},
{
"icon_id": "13872198",
"name": "商家",
"font_class": "storer",
"unicode": "e64a",
"unicode_decimal": 58954
},
{
"icon_id": "577335",
"name": "订单",
"font_class": "order",
"unicode": "e737",
"unicode_decimal": 59191
},
{
"icon_id": "736503",
"name": "权限",
"font_class": "permission",
"unicode": "e612",
"unicode_decimal": 58898
},
{
"icon_id": "1727271",
"name": "商品",
"font_class": "goods",
"unicode": "e889",
"unicode_decimal": 59529
},
{
"icon_id": "7587933",
"name": "菜单",
"font_class": "menu",
"unicode": "e60e",
"unicode_decimal": 58894
},
{
"icon_id": "12758820",
"name": "字典类型",
"font_class": "dict-type",
"unicode": "e652",
"unicode_decimal": 58962
},
{
"icon_id": "13768112",
"name": "字典",
"font_class": "dictionary",
"unicode": "e600",
"unicode_decimal": 58880
},
{
"icon_id": "37734141",
"name": "角色",
"font_class": "role",
"unicode": "e604",
"unicode_decimal": 58884
},
{
"icon_id": "1727563",
"name": "全屏",
"font_class": "fullscreen",
"unicode": "e8fa",
"unicode_decimal": 59642
},
{
"icon_id": "1727566",
"name": "退出全屏",
"font_class": "exit-fullscreen",
"unicode": "e8fb",
"unicode_decimal": 59643
},
{
"icon_id": "11641852",
"name": "表格",
"font_class": "table",
"unicode": "e615",
"unicode_decimal": 58901
},
{
"icon_id": "20104468",
"name": "测试",
"font_class": "test",
"unicode": "e610",
"unicode_decimal": 58896
},
{
"icon_id": "26686335",
"name": "中英文",
"font_class": "lang",
"unicode": "e649",
"unicode_decimal": 58953
},
{
"icon_id": "37702310",
"name": "文字大小",
"font_class": "size",
"unicode": "e660",
"unicode_decimal": 58976
}
]
}

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 140 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 73 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 42 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 48 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.7 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 194 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 167 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 77 KiB

View File

@ -0,0 +1 @@
<svg width="128" height="128" xmlns="http://www.w3.org/2000/svg"><path d="M126.713 90.023c.858.985 1.287 2.134 1.287 3.447v29.553c0 1.423-.429 2.6-1.287 3.53-.858.93-1.907 1.395-3.146 1.395H97.824c-1.145 0-2.146-.465-3.004-1.395-.858-.93-1.287-2.107-1.287-3.53V93.47c0-.875.19-1.696.572-2.462.382-.766.906-1.368 1.573-1.806a3.84 3.84 0 0 1 2.146-.657h9.725V69.007a3.84 3.84 0 0 0-.43-1.806 3.569 3.569 0 0 0-1.143-1.313 2.714 2.714 0 0 0-1.573-.492h-36.47v23.149h9.725c1.144 0 2.145.492 3.004 1.478.858.985 1.287 2.134 1.287 3.447v29.553c0 .876-.191 1.696-.573 2.463-.38.766-.905 1.368-1.573 1.806a3.84 3.84 0 0 1-2.145.656H51.915a3.84 3.84 0 0 1-2.145-.656c-.668-.438-1.216-1.04-1.645-1.806a4.96 4.96 0 0 1-.644-2.463V93.47c0-1.313.43-2.462 1.288-3.447.858-.986 1.907-1.478 3.146-1.478h9.582v-23.15h-37.9c-.953 0-1.74.356-2.359 1.068-.62.711-.93 1.56-.93 2.544v19.538h9.726c1.239 0 2.264.492 3.074 1.478.81.985 1.216 2.134 1.216 3.447v29.553c0 1.423-.405 2.6-1.216 3.53-.81.93-1.835 1.395-3.074 1.395H4.29c-.476 0-.93-.082-1.358-.246a4.1 4.1 0 0 1-1.144-.657 4.658 4.658 0 0 1-.93-1.067 5.186 5.186 0 0 1-.643-1.395 5.566 5.566 0 0 1-.215-1.56V93.47c0-.437.048-.875.143-1.313a3.95 3.95 0 0 1 .429-1.15c.19-.328.429-.656.715-.984.286-.329.572-.602.858-.821.286-.22.62-.383 1.001-.493.382-.11.763-.164 1.144-.164h9.726V61.619c0-.985.31-1.833.93-2.544.619-.712 1.358-1.068 2.216-1.068h44.335V39.62h-9.582c-1.24 0-2.288-.492-3.146-1.477a5.09 5.09 0 0 1-1.287-3.448V5.14c0-1.423.429-2.627 1.287-3.612.858-.985 1.907-1.477 3.146-1.477h25.743c.763 0 1.478.246 2.145.739a5.17 5.17 0 0 1 1.573 1.888c.382.766.573 1.587.573 2.462v29.553c0 1.313-.43 2.463-1.287 3.448-.859.985-1.86 1.477-3.004 1.477h-9.725v18.389h42.762c.954 0 1.74.355 2.36 1.067.62.711.93 1.56.93 2.545v26.925h9.582c1.239 0 2.288.492 3.146 1.478z"/></svg>

After

Width:  |  Height:  |  Size: 1.8 KiB

View File

@ -0,0 +1 @@
<svg width="130" height="130" xmlns="http://www.w3.org/2000/svg"><path d="M63.444 64.996c20.633 0 37.359-14.308 37.359-31.953 0-17.649-16.726-31.952-37.359-31.952-20.631 0-37.36 14.303-37.358 31.952 0 17.645 16.727 31.953 37.359 31.953zM80.57 75.65H49.434c-26.652 0-48.26 18.477-48.26 41.27v2.664c0 9.316 21.608 9.325 48.26 9.325H80.57c26.649 0 48.256-.344 48.256-9.325v-2.663c0-22.794-21.605-41.271-48.256-41.271z" stroke="#979797"/></svg>

After

Width:  |  Height:  |  Size: 440 B

View File

@ -0,0 +1 @@
<svg width="128" height="110" xmlns="http://www.w3.org/2000/svg"><path d="M86.635 33.334c1.467 0 2.917.113 4.358.283C87.078 14.392 67.58.111 45.321.111 20.44.111.055 17.987.055 40.687c0 13.104 6.781 23.863 18.115 32.209l-4.527 14.352 15.82-8.364c5.666 1.182 10.207 2.395 15.858 2.395 1.42 0 2.829-.073 4.227-.189-.886-3.19-1.398-6.53-1.398-9.996 0-20.845 16.98-37.76 38.485-37.76zm-24.34-12.936c3.407 0 5.665 2.363 5.665 5.954 0 3.576-2.258 5.97-5.666 5.97-3.392 0-6.795-2.395-6.795-5.97 0-3.591 3.403-5.954 6.795-5.954zM30.616 32.323c-3.393 0-6.818-2.395-6.818-5.971 0-3.591 3.425-5.954 6.818-5.954 3.392 0 5.65 2.363 5.65 5.954 0 3.576-2.258 5.97-5.65 5.97z"/><path d="M127.945 70.52c0-19.075-18.108-34.623-38.448-34.623-21.537 0-38.5 15.548-38.5 34.623 0 19.108 16.963 34.622 38.5 34.622 4.508 0 9.058-1.2 13.584-2.395l12.414 7.167-3.404-11.923c9.087-7.184 15.854-16.712 15.854-27.471zm-50.928-5.97c-2.254 0-4.53-2.362-4.53-4.773 0-2.378 2.276-4.771 4.53-4.771 3.422 0 5.665 2.393 5.665 4.771 0 2.41-2.243 4.773-5.665 4.773zm24.897 0c-2.24 0-4.498-2.362-4.498-4.773 0-2.378 2.258-4.771 4.498-4.771 3.392 0 5.665 2.393 5.665 4.771 0 2.41-2.273 4.773-5.665 4.773z"/></svg>

After

Width:  |  Height:  |  Size: 1.1 KiB

View File

@ -0,0 +1,183 @@
<!--
* @Description:
* @Author: zenghua.wang
* @Date: 2024-03-24 11:04:52
* @LastEditors: zenghua.wang 1048523306@qq.com
* @LastEditTime: 2025-01-20 09:34:23
-->
<template>
<section class="rich-editor">
<Toolbar class="rich-editor-toolbar" :editor="refEditor" :default-config="options.toolbarConfig" :mode="mode" />
<Editor
v-model="valueHtml"
class="rich-editor-toolbar"
:style="styleEditor"
:default-config="options.editorConfig"
:mode="mode"
@on-created="handleCreated"
@on-change="handleChange"
@on-destroyed="handleDestroyed"
@on-focus="handleFocus"
@on-blur="handleBlur"
/>
</section>
</template>
<script>
import '@wangeditor/editor/dist/css/style.css'; // css
import { shallowRef, ref, computed, nextTick, onBeforeUnmount, onMounted } from 'vue';
import { Editor, Toolbar } from '@wangeditor/editor-for-vue';
import { isEmpty } from '@/utils';
// import { CommonUpload, UploadImageFromEditor } from '@/apis/common';
const { VITE_APP_OSS_URL } = import.meta.env;
export default {
name: 'CustomRichEditor',
components: { Editor, Toolbar },
props: {
value: {
type: String,
default: '',
},
mode: {
type: String,
default: 'default', //'default' 'simple'
},
readOnly: {
type: Boolean,
default: false,
},
options: {
type: Object,
default: () => {
return {
toolbarConfig: {},
editorConfig: {
placeholder: '请输入内容...',
readOnly: false,
MENU_CONF: {
uploadImage: {
server: '',
base64LimitSize: 10 * 1024, //10kb
maxFileSize: 10 * 1024 * 1024, //10M
maxNumberOfFiles: 10,
allowedFileTypes: ['image/*'],
async customUpload(file, insertFn) {
// if (isEmpty(file.name)) return;
// const data = {
// key: file.name,
// size: file.size,
// contentType: file.type,
// expiresInSecond: 3600,
// };
// const res = await UploadImageFromEditor(data);
// if (res.code === 200) {
// await CommonUpload({
// url: res.data.url,
// file,
// });
// insertFn(VITE_APP_OSS_URL + res.data.key, file.name, res.data.key);
// }
},
},
},
},
};
},
},
},
emits: ['focus', 'blur', 'change'],
setup(props, cxt) {
const refEditor = shallowRef();
const valueHtml = ref('');
const styleEditor = computed(() => {
return {
height: props.options.contentHeight || '300px',
};
});
/**
* 创建
* @param {*} editor
*/
const handleCreated = (editor) => {
refEditor.value = editor;
props.readOnly ? editor.disable() : editor.enable();
};
/**
* 组件内容变化
* @param {*} editor
*/
const handleChange = (editor) => {
cxt.emit('change', editor);
};
/**
* 组件销毁
* @param {*} editor
*/
const handleDestroyed = (editor) => {
valueHtml.value = '';
};
/**
* 光标处于编辑区
* @param {*} editor
*/
const handleFocus = (editor) => {
cxt.emit('focus', editor);
};
/**
* 光标离开编辑区
* @param {*} editor
*/
const handleBlur = (editor) => {
cxt.emit('blur', editor);
};
/**
* 挂载
*/
onMounted(() => {
nextTick(() => {
if (props?.value) {
valueHtml.value = props.value;
}
});
});
/**
* 组件销毁时也及时销毁编辑器
*/
onBeforeUnmount(() => {
if (!refEditor?.value) return;
refEditor.value.destroy();
});
return {
refEditor,
valueHtml,
styleEditor,
handleCreated,
handleChange,
handleDestroyed,
handleFocus,
handleBlur,
};
},
};
</script>
<style lang="scss" scoped>
.rich-editor {
border: 1px solid $color-border;
z-index: 9999;
&-toolbar {
border-bottom: 1px solid $color-border;
:deep(.w-e-bar-divider) {
width: 0;
}
}
&-editor {
overflow-y: hidden;
height: 200px;
}
}
</style>

View File

@ -0,0 +1,5 @@
import SvgIcon from './svg-icon';
import CustomRichEditor from './custom-rich-editor';
export { SvgIcon, CustomRichEditor };

View File

@ -0,0 +1,81 @@
<template>
<div v-if="isExternal" :style="styleExternalIcon" class="svg-external-icon svg-icon" />
<svg v-else :class="svgClass" aria-hidden="true" :style="styleSize()">
<use :xlink:href="iconName" />
<title v-if="title">{{ title }}</title>
</svg>
</template>
<script setup name="svg-icon">
import { computed } from 'vue';
import { setPx } from '@/utils';
const props = defineProps({
// svg
name: {
type: String,
required: true,
},
size: {
type: [Number, String],
default: 16,
},
// class,svgclass,svg
className: {
type: String,
default: '',
},
title: {
type: String,
default: '',
},
});
//
const isExternal = computed(() => {
return /^(https?:|mailto:|tel:)/.test(props.name);
});
//svg
const iconName = computed(() => {
return `#icon-${props.name}`;
});
//svg
const svgClass = computed(() => {
if (props.className) {
return 'svg-icon ' + props.className;
} else {
return 'svg-icon';
}
});
const styleSize = () => {
return {
fontSize: setPx(props.size),
};
};
const styleExternalIcon = computed(() => {
return {
mask: `url(${props.name}) no-repeat 50% 50%`,
'-webkit-mask': `url(${props.name}) no-repeat 50% 50%`,
};
});
</script>
<style scoped>
.svg-icon {
width: 1em;
height: 1em;
vertical-align: -0.15em;
fill: currentColor;
overflow: hidden;
}
.svg-external-icon {
background-color: currentColor;
mask-size: cover !important;
display: inline-block;
}
</style>

219
main/src/config/index.js Normal file
View File

@ -0,0 +1,219 @@
export const GenKey = (key, prefix = 'EGGY_TEAM_STORE_') => {
return prefix ? prefix + key : key;
};
export const CONSTANTS = {
PREFIX: 'EGGY_TEAM_STORE_',
PRIMARY: '#409eff',
// 总部商品可编辑属性
EDITLIST: ['amount', 'originalPrice', 'activityPrice', 'usePoints', 'points', 'status', 'sortOrder', 'perUseValue', 'displayChannelList'],
// 活动管理
LATITUDE: [
{
label: '副本【x】通关计分榜',
value: 10,
},
{
label: '副本【x】速通榜',
value: 11,
},
{
label: '副本【x】大师榜失误最少',
value: 12,
},
{
label: '副本【x】巅峰榜',
value: 13,
},
{
label: '最强闯关计分榜',
value: 14,
},
{
label: '挑战模式通关计分榜',
value: 20,
},
{
label: '挑战模式第【x】关速通榜',
value: 21,
},
{
label: '挑战模式大师榜(失误最少)',
value: 22,
},
{
label: '挑战模式巅峰榜',
value: 23,
},
],
// 维度说明
LATITUDETIPS: {
default: '榜单常驻,用户所有的游戏通关数据得分累计排列显示,不管用户参与什么模式,是否通关,只要游戏结束时候的分数累计进行排行',
10: '在榜单发起时间内,指定某个副本的通关分数总和(不通关则不计入总分)',
11: '在榜单发起时间内,指定某个副本的通关时间最快(只刷新计入最快的那次)',
12: '在榜单发起时间内,指定某个副本的扣分最少(只刷新计入扣分最少的那次)',
13: '在榜单发起时间内,指定某个副本的得分最高(只刷新计入得分最高的那次)',
14: '在榜单发起时间内,任意副本的得分总和排名(不通关则不计入总分)',
20: '在榜单发起时间内,挑战模式的通关分数总和(不通关则不计入总分)',
21: '在榜单发起时间内,指定挑战模式的某些关卡的通关时间最快(只刷新计入最快的那次)',
22: '在榜单发起时间内,挑战模式的扣分最少(只刷新计入扣分最少的那次)',
23: '在榜单发起时间内,挑战模式的得分最高(只刷新计入得分最高的那次)',
},
// 支付方式
PAYTYPE: {
prop: 'payType',
label: '支付方式',
type: 'select',
dicData: [
{
label: '余额',
value: 1,
},
{
label: '微信',
value: 2,
},
{
label: '支付宝',
value: 3,
},
{
label: '商家券',
value: 4,
},
{
label: '平台券',
value: 5,
},
{
label: '商场券',
value: 6,
},
{
label: '积分',
value: 7,
},
{
label: '美团',
value: 8,
},
{
label: '现金',
value: 9,
},
{
label: '抖音',
value: 10,
},
{
label: '小红书',
value: 11,
},
{
label: '线下优惠券',
value: 12,
},
{
label: '首单立减',
value: 13,
},
{
label: '其它一',
value: 20,
},
{
label: '其它二',
value: 21,
},
{
label: '其它三',
value: 22,
},
],
formatter: (row) => {
let value = '';
switch (row.payType) {
case 1: {
value = '余额';
break;
}
case 2: {
value = '微信';
break;
}
case 3: {
value = '支付宝';
break;
}
case 4: {
value = '商家券';
break;
}
case 5: {
value = '平台券';
break;
}
case 6: {
value = '商场券';
break;
}
case 7: {
value = '积分';
break;
}
case 8: {
value = '美团';
break;
}
case 9: {
value = '现金';
break;
}
case 10: {
value = '抖音';
break;
}
case 11: {
value = '小红书';
break;
}
case 12: {
value = '线下优惠券';
break;
}
case 13: {
value = '首单立减';
break;
}
case 20: {
value = '其它一';
break;
}
case 21: {
value = '其它二';
break;
}
case 22: {
value = '其它三';
break;
}
}
return value;
},
},
// 支付分类
PAY_CATEGORIES: [
{ prop: 'weixin', name: '微信' },
{ prop: 'alipay', name: '支付宝' },
{ prop: 'cash', name: '现金' },
{ prop: 'balance', name: '余额' },
{ prop: 'market', name: '商场券' },
{ prop: 'platform', name: '平台券' },
{ prop: 'store', name: '门店券' },
{ prop: 'meituan', name: '美团' },
{ prop: 'tiktok', name: '抖音' },
{ prop: 'xiaohongshu', name: '小红书' },
{ prop: 'offline', name: '线下优惠券' },
{ prop: 'firstOrder', name: '首单立减' },
],
};

View File

@ -0,0 +1,29 @@
/**
* @Description: 按钮权限
* @Author: zenghua.wang
* @Date: 2022-08-30 09:42:47
* @LastEditors: zenghua.wang
* @LastEditTime: 2024-06-08 19:50:47
*/
import { useUserStore } from '@/store/modules/user';
export function useAuth(app) {
app.directive('auth', (el, binding) => {
const { value } = binding;
const all_permission = '*:*:*';
const UserStore = useUserStore();
const permissions = UserStore.getAuths() ?? [];
if (value && value instanceof Array && value.length > 0) {
const permissionFlag = value;
const hasAuth = permissions.some((permission) => {
return all_permission === permission || permissionFlag.includes(permission);
});
if (!hasAuth) {
el.parentNode && el.parentNode.removeChild(el);
}
} else {
// console.log('button permissions are not supported.');
}
});
}

View File

@ -0,0 +1,8 @@
import { useAuth } from './auth';
/**
* 指令v-auth
*/
export const registerDirective = (app) => {
useAuth(app);
};

5
main/src/hooks/index.js Normal file
View File

@ -0,0 +1,5 @@
import { getCurrentInstance } from 'vue';
export const useApp = () => {
return getCurrentInstance().appContext?.config?.globalProperties;
};

View File

@ -0,0 +1,22 @@
import { h } from 'vue';
const wrapperMap = new Map();
export const useWrapComponents = (Component, route) => {
let wrapper;
if (Component) {
const wrapperName = route.name;
if (wrapperMap.has(wrapperName)) {
wrapper = wrapperMap.get(wrapperName);
} else {
wrapper = {
name: wrapperName,
render() {
return h('div', { className: 'layout' }, Component);
},
};
wrapperMap.set(wrapperName, wrapper);
}
return h(wrapper);
}
};

View File

@ -0,0 +1,3 @@
<template>
<router-view />
</template>

View File

@ -0,0 +1,36 @@
<!--
* @Description:
* @Author: zenghua.wang
* @Date: 2023-06-20 14:29:45
* @LastEditors: zenghua.wang 1048523306@qq.com
* @LastEditTime: 2025-01-17 16:36:25
-->
<template>
<router-view v-slot="{ Component, route }">
<transition name="fade-slide" mode="out-in" appear>
<keep-alive v-if="isReload" :include="cacheRoutes">
<component :is="useWrapComponents(Component, route)" :key="route.path" />
</keep-alive>
</transition>
</router-view>
</template>
<script setup name="layout">
import { computed } from 'vue';
import { useSettingStore } from '@/store/modules/setting';
import { usePermissionStore } from '@/store/modules/permission';
import { useWrapComponents } from '@/hooks/useWrapComponents';
const SettingStore = useSettingStore();
const PermissionStore = usePermissionStore();
const cacheRoutes = computed(() => PermissionStore.keepAliveRoutes);
const isReload = computed(() => SettingStore.isReload);
</script>
<style lang="scss" scoped>
.layout {
width: 100%;
height: 100%;
box-sizing: border-box;
}
</style>

15
main/src/main.js Normal file
View File

@ -0,0 +1,15 @@
import 'virtual:svg-icons-register';
import { createApp } from 'vue';
import App from './App.vue';
import router from './router';
import pinia from './store';
import ElementPlus from 'element-plus';
import 'element-plus/dist/index.css';
import { registerGlobalMicroApps } from './micro';
import { registerElIcons } from './plugins/icon';
import './utils/permission';
const app = createApp(App);
app.use(pinia).use(router).use(ElementPlus).mount('#root');
registerElIcons(app);
registerGlobalMicroApps();

28
main/src/micro/actions.js Normal file
View File

@ -0,0 +1,28 @@
import { initGlobalState } from 'qiankun';
import { reactive } from 'vue';
const initialState = reactive({
user: {
name: 'admin',
},
menus: [],
auths: [],
});
const actions = initGlobalState(initialState);
actions.onGlobalStateChange((newState, prev) => {
console.log('main change', newState, prev);
for (const key in newState) {
initialState[key] = newState[key];
}
});
// 有key表示取globalState下的某个子级对象
// 无key表示取全部
actions.getGlobalState = (key) => {
return key ? initialState[key] : initialState;
};
export default actions;

30
main/src/micro/app.js Normal file
View File

@ -0,0 +1,30 @@
import actions from './actions';
const { VITE_APP_SUB_VUE, VITE_APP_SUB_ADMIN } = import.meta.env;
export const microApps = [
{
name: 'suv-vue',
entry: VITE_APP_SUB_VUE,
activeRule: '/sub-vue/',
title: '管理后台',
},
{
name: 'sub-admin',
entry: VITE_APP_SUB_ADMIN,
activeRule: '/sub-admin/',
},
];
const apps = microApps.map((item) => {
return {
...item,
container: '#app',
props: {
routerBase: item.activeRule,
getGlobalState: actions.getGlobalState,
},
};
});
export default apps;

40
main/src/micro/index.js Normal file
View File

@ -0,0 +1,40 @@
import { registerMicroApps, setDefaultMountApp, start, addGlobalUncaughtErrorHandler } from 'qiankun';
import microApps from './app';
const registerGlobalMicroApps = () => {
// 给子应用配置加上loader方法
const apps = microApps.map((item) => {
// console.log('registerGlobalMicroApps==', item);
return {
...item,
};
});
registerMicroApps(apps, {
beforeLoad: (app) => {
console.log('before load', app);
switch (app.name) {
case 'sub-vue':
document.title = 'sub-vue';
break;
case 'sub-admin':
document.title = 'sub-admin';
break;
}
},
beforeMount: [
(app) => {
console.log('before mount', app.name);
},
],
});
start({
prefetch: false, // 取消预加载
sandbox: { experimentalStyleIsolation: true },
});
// addGlobalUncaughtErrorHandler((event) => console.log(event));
};
export { registerGlobalMicroApps };

View File

@ -0,0 +1,8 @@
// import * as components from '../../../global/components';
// // 全局注册组件
// export const registerGlobalComponents = (app) => {
// Object.keys(components).forEach((key) => {
// app.component(key, components[key]);
// });
// };

8
main/src/plugins/icon.js Normal file
View File

@ -0,0 +1,8 @@
import * as ElIconsModules from '@element-plus/icons-vue';
// 全局注册element-plus icon图标组件
export const registerElIcons = (app) => {
Object.keys(ElIconsModules).forEach((key) => {
app.component(key, ElIconsModules[key]);
});
};

43
main/src/router/index.js Normal file
View File

@ -0,0 +1,43 @@
import { createRouter, createWebHashHistory, createWebHistory } from 'vue-router';
import Layout from '@/layouts/index.vue';
export const constantRoutes = [
{
path: '/login',
name: 'login',
component: () => import('@/views/login/index.vue'),
hidden: true,
meta: {
title: '登录',
icon: 'Login',
},
},
{
path: '/',
name: 'layout',
component: Layout,
redirect: '/platform',
meta: { title: '平台入口', icon: 'House' },
children: [
{
path: '/platform',
component: () => import('@/views/index.vue'),
name: 'platform',
meta: { title: '平台入口', icon: 'House' },
},
],
},
];
export const notFoundRouter = {
path: '/:pathMatch(.*)',
name: 'notFound',
redirect: '/404',
};
const router = createRouter({
history: createWebHistory(),
routes: constantRoutes,
});
export default router;

18
main/src/store/index.js Normal file
View File

@ -0,0 +1,18 @@
import { defineStore, createPinia } from 'pinia';
import piniaPluginPersistedstate from 'pinia-plugin-persistedstate';
export const Store = defineStore({
id: 'globalState',
state: () => ({}),
getters: {},
actions: {},
persist: {
key: 'globalState',
storage: window.sessionStorage, //localstorage
},
});
const pinia = createPinia();
pinia.use(piniaPluginPersistedstate);
export default pinia;

View File

@ -0,0 +1,52 @@
import { defineStore } from 'pinia';
import { constantRoutes, notFoundRouter } from '@/router';
import { createAsyncRoutes, filterAsyncRoutes, filterKeepAlive } from '@/utils/router';
import { useUserStore } from '@/store/modules/user';
import { getTree } from '@/utils';
export const usePermissionStore = defineStore({
id: 'permissionStore',
state: () => ({
// 路由
routes: [],
// 动态路由
asyncRoutes: [],
// 缓存路由
cacheRoutes: {},
}),
getters: {
permissionRoutes: (state) => {
return state.routes;
},
keepAliveRoutes: (state) => {
return filterKeepAlive(state.asyncRoutes);
},
},
actions: {
generateRoutes(roles) {
return new Promise((resolve) => {
// 在这判断是否有权限,哪些角色拥有哪些权限
const UserStore = useUserStore();
this.asyncRoutes = createAsyncRoutes(getTree(UserStore.getMenus()));
let accessedRoutes;
if (roles && roles.length && !roles.includes('admin')) {
accessedRoutes = filterAsyncRoutes(this.asyncRoutes, roles);
} else {
accessedRoutes = this.asyncRoutes || [];
}
accessedRoutes = accessedRoutes.concat(notFoundRouter);
this.routes = constantRoutes.concat(accessedRoutes);
resolve(accessedRoutes);
});
},
clearRoutes() {
this.routes = [];
this.asyncRoutes = [];
this.cacheRoutes = [];
},
getCacheRoutes() {
this.cacheRoutes = filterKeepAlive(this.asyncRoutes);
return this.cacheRoutes;
},
},
});

View File

@ -0,0 +1,69 @@
import { defineStore } from 'pinia';
import { CONSTANTS } from '@/config';
export const useSettingStore = defineStore({
id: 'settingStore',
state: () => ({
// menu 是否收缩
isCollapse: true,
//
withoutAnimation: false,
device: 'desktop',
// 刷新当前页
isReload: true,
// 主题设置
themeConfig: {
// 显示设置
showSetting: false,
// 菜单展示模式 默认 vertical horizontal / vertical /columns
mode: 'vertical',
// tagsView 是否展示 默认展示
showTag: true,
// 页脚
footer: true,
// 深色模式 切换暗黑模式
isDark: false,
// 显示侧边栏Logo
showLogo: true,
// 主题颜色
primary: CONSTANTS.PRIMARY,
// element组件大小
globalComSize: 'default',
// 是否只保持一个子菜单的展开
uniqueOpened: true,
// 固定header
fixedHeader: true,
// 灰色模式
gray: false,
// 色弱模式
weak: false,
},
}),
getters: {},
actions: {
// 设置主题
setThemeConfig({ key, val }) {
this.themeConfig[key] = val;
},
// 切换 Collapse
setCollapse(value) {
this.isCollapse = value;
this.withoutAnimation = false;
},
// 关闭侧边栏
closeSideBar({ withoutAnimation }) {
this.isCollapse = false;
this.withoutAnimation = withoutAnimation;
},
toggleDevice(device) {
this.device = device;
},
// 刷新
setReload() {
this.isReload = false;
setTimeout(() => {
this.isReload = true;
}, 50);
},
},
});

View File

@ -0,0 +1,104 @@
import { defineStore } from 'pinia';
import router from '@/router';
export const useTagsViewStore = defineStore({
id: 'tagsViewStore',
state: () => ({
activeTabsValue: '/home',
visitedViews: [],
cachedViews: [],
}),
getters: {},
actions: {
setTabsMenuValue(val) {
this.activeTabsValue = val;
},
addView(view) {
this.addVisitedView(view);
},
removeView(routes) {
return new Promise((resolve) => {
this.visitedViews = this.visitedViews.filter((item) => !routes.includes(item.path));
resolve(null);
});
},
addVisitedView(view) {
this.setTabsMenuValue(view.path);
if (this.visitedViews.some((v) => v.path === view.path)) return;
this.visitedViews.push(
Object.assign({}, view, {
title: view.meta.title || 'no-name',
})
);
if (view.meta.keepAlive) {
this.cachedViews.push(view.name);
}
},
delView(activeTabPath) {
return new Promise((resolve) => {
this.delVisitedView(activeTabPath);
this.delCachedView(activeTabPath);
resolve({
visitedViews: [...this.visitedViews],
cachedViews: [...this.cachedViews],
});
});
},
toLastView(activeTabPath) {
const index = this.visitedViews.findIndex((item) => item.path === activeTabPath);
const nextTab = this.visitedViews[index + 1] || this.visitedViews[index - 1];
if (!nextTab) return;
router.push(nextTab.path);
this.addVisitedView(nextTab);
},
delVisitedView(path) {
return new Promise((resolve) => {
this.visitedViews = this.visitedViews.filter((v) => {
return v.path !== path || v.meta.affix;
});
this.cachedViews = this.cachedViews.filter((v) => {
return v.path !== path || v.meta.affix;
});
resolve([...this.visitedViews]);
});
},
delCachedView(view) {
return new Promise((resolve) => {
const index = this.cachedViews.indexOf(view.name);
index > -1 && this.cachedViews.splice(index, 1);
resolve([...this.cachedViews]);
});
},
clearVisitedView() {
this.delAllViews();
},
delAllViews() {
return new Promise((resolve) => {
this.visitedViews = this.visitedViews.filter((v) => v.meta.affix);
this.cachedViews = this.visitedViews.filter((v) => v.meta.affix);
resolve([...this.visitedViews]);
});
},
delOtherViews(path) {
this.visitedViews = this.visitedViews.filter((item) => {
return item.path === path || item.meta.affix;
});
this.cachedViews = this.visitedViews.filter((item) => {
return item.path === path || item.meta.affix;
});
},
goHome() {
this.activeTabsValue = '/home';
router.push({ path: '/home' });
},
updateVisitedView(view) {
for (let v of this.visitedViews) {
if (v.path === view.path) {
v = Object.assign(v, view);
break;
}
}
},
},
});

View File

@ -0,0 +1,65 @@
import { defineStore } from 'pinia';
import { GenKey } from '@/config';
import { isEmpty, encode, decode } from '@/utils';
export const useUserStore = defineStore({
id: GenKey('USER_STATE'),
state: () => ({
token: null,
userInfo: {},
currentOrg: null,
orgList: [],
menus: [],
}),
getters: {},
actions: {
setToken(token) {
this.token = token;
},
hasToken() {
return !isEmpty(this.token);
},
setUserInfo(userInfo) {
this.userInfo = encode(JSON.stringify(userInfo), true);
},
getUserInfo() {
return !isEmpty(this.userInfo) ? JSON.parse(decode(this.userInfo, true)) : {};
},
setOrgList(orgList) {
this.orgList = encode(JSON.stringify(orgList), true);
},
getOrgList() {
return !isEmpty(this.orgList) ? JSON.parse(decode(this.orgList, true)) : [];
},
setCurrentOrg(org) {
this.currentOrg = org;
},
getCurrentOrg() {
const list = this.getOrgList().filter((item) => {
return item.id === this.currentOrg;
});
return !isEmpty(list) ? list[0] : {};
},
setMenus(menus) {
this.menus = encode(JSON.stringify(menus), true);
},
getMenus() {
return !isEmpty(this.menus) ? JSON.parse(decode(this.menus, true)) : [];
},
logout() {
this.token = null;
this.userInfo = {};
this.currentOrg = null;
this.orgList = [];
this.menus = [];
localStorage.removeItem(GenKey('USER_STATE'));
},
clear() {
localStorage.removeItem(GenKey('USER_STATE'));
},
},
persist: {
key: GenKey('USER_STATE'),
storage: window.localStorage,
},
});

View File

@ -0,0 +1,283 @@
html {
margin: 0;
padding: 0;
width: 100%;
height: 100%;
border: 0;
text-size-adjust: 100%;
-webkit-tap-highlight-color: transparent;
}
a,
abbr,
acronym,
address,
article,
aside,
blockquote,
caption,
code,
del,
dfn,
dialog,
header,
footer,
nav,
object,
section,
body,
dd,
div,
dl,
dt,
em,
img,
fieldset,
figure,
form,
h1,
h2,
h3,
h4,
h5,
h6,
hgroup,
iframe,
legend,
p,
pre,
q,
span,
tbody,
tfoot,
thead,
ul,
ol,
li {
margin: 0;
padding: 0;
border: 0;
list-style: none;
vertical-align: baseline;
}
article,
aside,
details,
dialog,
figcaption,
figure,
footer,
header,
hgroup,
menu,
nav,
section {
display: block;
}
body {
width: 100%;
height: 100%;
font-size: 12px;
font-family: 'Source Han Sans CN, Source Han Sans CN-Regular', 'Helvetica Neue', Helvetica, Arial, 'PingFang SC', 'Hiragino Sans GB', 'Heiti SC', 'Microsoft YaHei', 'WenQuanYi Micro Hei', sans-serif;
color: #323232;
// background: #000;
}
img {
vertical-align: bottom;
border: 0;
}
// ::input-placeholder {
// color: #999999;
// }
::placeholder {
color: #999999;
}
// :input-placeholder {
// color: #cccccc;
// }
button::-moz-focus-inner,
input::-moz-focus-inner {
padding: 0;
border: 0;
}
textarea {
overflow: auto;
}
button:focus,
input:focus,
select:focus,
textarea:focus {
outline: 0;
}
input::-ms-clear {
display: none;
}
article,
aside,
details,
figcaption,
figure,
footer,
header,
hgroup,
main,
nav,
section,
summary {
display: block;
}
audio,
canvas,
progress,
video {
display: inline-block;
vertical-align: baseline;
}
audio:not([controls]) {
display: none;
height: 0;
}
[hidden],
template {
display: none;
}
a {
text-decoration: none;
color: #323232;
background: none;
cursor: pointer;
}
a:active,
a:hover {
outline: 0;
}
a:focus {
outline: thin dotted;
outline: 5px auto -webkit-focus-ring-color;
outline-offset: -2px;
}
abbr[title] {
border-bottom: 1px dotted;
}
b,
strong {
font-weight: 700;
}
dfn {
font-style: italic;
}
mark {
color: #000000;
background: #ffff00;
}
small {
font-size: 80%;
}
sub,
sup {
position: relative;
font-size: 75%;
line-height: 0;
vertical-align: baseline;
}
sup {
top: -0.5em;
}
sub {
bottom: -0.25em;
}
svg:not(:root) {
overflow: hidden;
}
figure {
margin: 1em 40px;
}
hr {
box-sizing: content-box;
height: 0;
}
pre {
overflow: auto;
}
code,
kbd,
pre,
samp {
font-size: 1em;
font-family: monospace;
}
button,
input,
optgroup,
select,
textarea {
margin: 0;
font: inherit;
}
button {
overflow: visible;
}
button,
select {
text-transform: none;
}
button,
html input[type='button'],
input[type='reset'],
input[type='submit'] {
appearance: button;
cursor: pointer;
}
button[disabled],
html input[disabled] {
cursor: default;
}
button::-moz-focus-inner,
input::-moz-focus-inner {
padding: 0;
border: 0;
}
input {
margin: 0;
padding: 0;
line-height: normal;
}
input[type='checkbox'],
input[type='radio'] {
box-sizing: border-box;
padding: 0;
}
input[type='number']::-webkit-inner-spin-button,
input[type='number']::-webkit-outer-spin-button {
height: auto;
}
input[type='search'] {
box-sizing: content-box;
appearance: textfield;
}
input[type='search']::-webkit-search-cancel-button,
input[type='search']::-webkit-search-decoration {
appearance: none;
}
fieldset {
margin: 0 2px;
padding: 0.35em 0.625em 0.75em;
border: 1px solid silver;
}
legend {
padding: 0;
border: 0;
}
optgroup {
font-weight: 700;
}
table {
border-spacing: 0;
border-collapse: collapse;
}
td,
th {
padding: 0;
}

View File

@ -0,0 +1,94 @@
@use "sass:list";
.flex {
&-row {
display: flex;
flex-direction: row;
}
&-column {
display: flex;
flex-direction: column;
}
}
@each $type, $colors in $color-types {
.text-#{$type} {
color: list.nth($colors, 1);
}
}
.text-center {
text-align: center;
}
.text-left {
text-align: left;
}
.text-right {
text-align: right;
}
.custom {
&-page {
display: flex;
justify-content: center;
.el-pagination {
font-size: 24px;
}
.el-pagination .btn-next.is-active,
.el-pagination .btn-prev.is-active,
.el-pagination .el-pager li.is-active {
color: $color-primary !important;
}
.el-pagination .btn-next,
.el-pagination .btn-prev,
.el-pagination .el-pager li {
margin: 0 12px !important;
font-size: 24px;
color: #999;
}
.el-pagination .btn-next,
.el-pagination .btn-prev {
// border: 1px dashed #ddd;
&:hover {
color: #000 !important;
}
}
.el-pagination .btn-next .el-icon,
.el-pagination .btn-prev .el-icon {
font-size: 32px !important;
font-weight: 500 !important;
}
.el-pagination .el-pager li {
&:hover {
font-weight: 500;
color: $color-primary !important;
}
}
&-jumper {
margin-left: 40px;
display: flex;
flex-direction: row;
align-items: center;
span {
font-size: 24px;
color: #999;
}
.el-input {
width: 50px !important;
margin: 0 20px;
font-size: 24px;
}
}
}
}

View File

@ -0,0 +1,56 @@
// color
$legacy-ie: 10;
$color-primary: #b10304;
$color-success: #13ce66;
$color-warning: #f7ba2a;
$color-danger: #ff4949;
$color-info: #50bfff;
$color-secondary: #2e90fe;
$color-white: #ffffff;
$color-black: #1f2d3d;
$color-black-light: #324057;
$color-black-lighter: #475669;
$color-blue-light: #5da9ff;
$color-blue-lighter: #5da9ff;
$color-black: #000000;
$color-silver: #8492a6;
$color-silver-light: #99a9bf;
$color-silver-lighter: #c0ccda;
$color-gray: #d3dce6;
$color-gray-light: #e5e9f2;
$color-gray-lighter: #eff2f7;
$color-333: #333333;
$color-666: #333333;
$color-999: #999999;
$color-border-gray: #d1dbe5;
$color-input-border: #dcdfe6;
$color-border: $color-border-gray;
$color-types: (
primary: (
$color-primary,
#4db3ff,
#1d90e6
),
info: (
$color-info,
#73ccff,
#48ace6
),
success: (
$color-success,
#42d885,
#11b95c
),
warning: (
$color-warning,
#f9c855,
#dea726
),
danger: (
$color-danger,
#ff6d6d,
#e64242
)
);
@import "utils/utils";

View File

@ -0,0 +1,13 @@
@import 'common/base.scss';
@import 'common/define.scss';
// @import "@/assets/fonts/iconfont.css";
#root,
#app {
position: relative;
width: 100%;
height: 100%;
font-family: Avenir, sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}

View File

@ -0,0 +1,31 @@
/// Block Element
/// @access public
/// @param {String} $element - Element's name
@mixin element($element) {
&__#{$element} {
@content;
}
}
/// @alias element
@mixin e($element) {
@include element($element) {
@content;
}
}
/// Block Modifier
/// @access public
/// @param {String} $modifier - Modifier's name
@mixin modifier($modifier) {
&_#{$modifier} {
@content;
}
}
/// @alias modifier
@mixin m($modifier) {
@include modifier($modifier) {
@content;
}
}

View File

@ -0,0 +1,30 @@
@mixin ellipsis($lines: 1, $line-height: 0) {
overflow: hidden;
@if $lines == 1 {
@if $legacy-ie <= 8 {
word-wrap: normal; // for ie
}
text-overflow: ellipsis;
white-space: nowrap;
} @else {
display: flexbox;
-webkit-line-clamp: $lines;
-webkit-box-orient: vertical;
@if value-of($line-height) == 0 {
@error 'line-height is required when clamp muti lines';
}
@if unitless($line-height) or unit($line-height) == '%' {
$line-height: value-of($line-height) * 1em;
}
max-height: $line-height * $lines;
// &:after {
// content: " ...";
// }
}
}

View File

@ -0,0 +1,5 @@
@mixin scrollable() {
overflow-x: hidden;
overflow-y: auto;
-webkit-overflow-scrolling: touch;
}

View File

@ -0,0 +1,34 @@
/// Mixin to customize scrollbars
/// Beware, this does not work in all browsers
/// @author Hugo Giraudel
/// @param {Length} $size - Horizontal scrollbar's height and vertical scrollbar's width
/// @param {Color} $foreground-color - Scrollbar's color
/// @param {Color} $background-color [mix($foreground-color, white, 50%)] - Scrollbar's color
/// @example scss - Scrollbar styling
/// @include scrollbars(.5em, slategray);
@mixin scrollbars($size, $foreground-color, $background-color: mix($foreground-color, white, 50%)) {
// For Google Chrome
::-webkit-scrollbar {
width: $size;
height: $size;
}
::-webkit-scrollbar-thumb {
background: $foreground-color;
}
::-webkit-scrollbar-track {
background: $background-color;
}
// For Internet Explorer
body {
scrollbar-face-color: $foreground-color;
scrollbar-track-color: $background-color;
}
}
/// alias
@mixin scrollbar($size, $foreground-color, $background-color: mix($foreground-color, white, 50%)) {
@include scrollbars($size, $foreground-color, $background-color: mix($foreground-color, white, 50%));
}

View File

@ -0,0 +1,11 @@
@function value-of($value) {
@if type-of($value) == 'number' and not unitless($value) {
@return $value / ($value * 0 + 1);
}
@return $value;
}
// alias
@function strip-unit($value) {
@return value-of($value);
}

View File

@ -0,0 +1,21 @@
@import 'bem';
@import 'ellipsis';
@import 'scrollable';
@import 'scrollbar';
@import 'value-of';
@mixin flex-row() {
display: flex;
flex-direction: row;
}
@mixin flex-column() {
display: flex;
flex-direction: column;
}
@mixin icon-space() {
display: inline-block;
content: '';
background-size: 100%;
}

99
main/src/utils/axios.js Normal file
View File

@ -0,0 +1,99 @@
/*
* @Descripttion:
* @Author: zenghua.wang
* @Date: 2022-02-23 21:12:37
* @LastEditors: wzh 1048523306@qq.com
* @LastEditTime: 2024-12-18 15:10:48
*/
import axios from 'axios';
import { ElNotification } from 'element-plus';
import router from '@/router';
import { useUserStore } from '@/store/modules/user';
const { VITE_APP_BASE_API, VITE_APP_UPLOAD_API } = import.meta.env;
/**
* 创建axios实例
*/
const publicAxios = axios.create({
baseURL: VITE_APP_BASE_API,
timeout: 10000,
});
/**
* 异常拦截处理器
* @param error
* @returns
*/
const errorHandler = async (error) => {
const { response } = error;
const UserStore = useUserStore();
if (response && response.status) {
switch (response.status) {
case 401:
case 403:
UserStore.logout();
router.push('/login');
break;
default:
break;
}
}
return Promise.reject(error?.response?.data);
};
/**
* 请求拦截器
*/
publicAxios.interceptors.request.use(async (config) => {
const UserStore = useUserStore();
config.baseURL = config?.isUpload ? VITE_APP_UPLOAD_API : config?.isLocal ? '' : VITE_APP_BASE_API;
if (UserStore.hasToken()) {
config.headers['fairies-auth-token'] = UserStore.token;
config.headers['cache-control'] = 'no-cache';
config.headers.Pragma = 'no-cache';
if (config?.isUpload) {
config.headers['Content-Type'] = config.uploadType;
}
}
if (config.method === 'POST' || config.method === 'DELETE') {
config.headers.Accept = 'application/json';
}
return config;
}, errorHandler);
/**
* 返回结果处理
* @param res
* @returns
*/
const formatResult = (res) => {
const code = res.data.code || res.status;
switch (code) {
case 200:
case 0:
// code === 0 或 200 代表没有错误
return res.data || res.data.data || res;
case 500:
case 1:
// code === 1 或 500 代表存在错误
// ElNotification.error(res.data.msg);
break;
default:
ElNotification.error(res.data.msg);
break;
}
};
/**
* 响应拦截器
*/
publicAxios.interceptors.response.use((response) => {
const { config } = response;
if (config?.responseType) {
return response;
}
const result = formatResult(response);
if (result) {
return result;
}
throw new Error(response.data.msg);
}, errorHandler);
export default publicAxios;

490
main/src/utils/index.js Normal file
View File

@ -0,0 +1,490 @@
/*
* @Descripttion:
* @Author: zenghua.wang
* @Date: 2022-02-23 21:12:37
* @LastEditors: wzh 1048523306@qq.com
* @LastEditTime: 2024-12-17 11:55:31
*/
import dayjs from 'dayjs';
import { cloneDeep } from 'lodash';
import { Base64 } from 'js-base64';
import JsZip from 'jszip';
import JsZipUtils from 'jszip-utils';
import { saveAs } from 'file-saver';
/**
* @Title 防抖指在一定时间内多次触发同一个事件只执行最后一次操作
* @param {*} fn
* @param {*} delay
* @returns
*/
export function debounce(fn, delay) {
let timer = null;
return function (...args) {
clearTimeout(timer);
timer = setTimeout(() => {
fn.apply(this, args);
}, delay);
};
}
/**
* @Title 节流指在一定时间内多次触发同一个事件只执行第一次操作
* @param {*} fn
* @param {*} delay
* @returns
*/
export function throttle(fn, delay) {
let timer = null;
return function (...args) {
if (!timer) {
timer = setTimeout(() => {
fn.apply(this, args);
timer = null;
}, delay);
}
};
}
/**
* @Title 判断是否 empty,返回ture
* @param {*} val:null 'null' undefined 'undefined' 0 '0' "" 返回true
* @returns
*/
export const isEmpty = (val) => {
if (val && parseInt(val) === 0) return false;
if (typeof val === 'undefined' || val === 'null' || val == null || val === 'undefined' || val === undefined || val === '') {
return true;
} else if (typeof val === 'object' && Object.keys(val).length === 0) {
return true;
} else if (val instanceof Array && val.length === 0) {
return true;
} else {
return false;
}
};
/**
* @Title 深度拷贝对象
* @param {*} obj
* @returns
*/
export const deepClone = (obj = {}) => {
return cloneDeep(obj);
};
/**
* @Title 将number转换为px
* @param {*} val
* @returns
*/
export const setPx = (val) => {
if (isEmpty(val)) return '';
return typeof val === 'number' ? `${val}px` : val;
};
/**
* @Title 设置字典值
* @param {*} columns
* @param {*} key
* @param {*} data
* @returns
*/
export const setDicData = (columns, key, data = []) => {
if (isEmpty(data)) return;
const len = columns.length;
for (let i = 0; i < len; i++) {
if (columns[i]?.prop === key) {
columns[i]['dicData'] = data;
break;
}
}
};
/**
* @Title 求字段lable
* @param {*} tree
* @returns
*/
export const setDicLabel = (dicData, value) => {
let label = value;
if (isEmpty(dicData)) return label;
const len = dicData.length;
for (let i = 0; i < len; i++) {
if (dicData[i]?.value === value) {
label = dicData[i].label;
break;
}
}
return label;
};
/**
* @Title 数组交集
* @param {*} arr1
* @param {*} arr2
* @returns
*/
export const intersectionArray = (arr1 = [], arr2 = []) => {
return arr1.filter((item) => arr2.includes(item));
};
/**
* @Title 数组并集
* @param {*} arr1
* @param {*} arr2
* @returns
*/
export const unionArray = (arr1 = [], arr2 = []) => {
return Array.from(new Set([...arr1, ...arr2]));
};
/**
* @Title 数组差集
* @param {*} arr1
* @param {*} arr2
* @returns
*/
export const differenceArray = (arr1 = [], arr2 = []) => {
const s = new Set(arr2);
return arr1.filter((x) => !s.has(x));
};
/**
* @Title 加密
* @param {*} n
* @returns
*/
export const encode = (n, flag = false) => {
if (flag) {
return (
((e) => {
let t = e.length.toString();
for (let n = 10 - t.length; n > 0; n--) t = '0' + t;
return t;
})(n) +
((e) => {
const t = Base64.encode(e).split('');
for (let n = 0; n < Math.floor(e.length / 100 + 1); n++) t.splice(100 * n + 1, 0, 3);
return t.join('');
})(n)
);
}
return n;
};
/**
* @Title 解密
* @param {*} e
* @returns
*/
export const decode = (e, flag = false) => {
if (flag) {
try {
const t = Number(e.substr(0, 10));
const n = e.substr(10).split('');
for (let i = 0, s = 0; s < Math.floor(t / 100) + 1; s++) {
n.splice(100 * s + 1 - i, 1);
i++;
}
const o = Base64.decode(n.join(''));
return o;
} catch (error) {
return e;
}
}
return e;
};
/**
* @Title 图片转base64
* @param {*} file
* @returns
*/
export const imageToBase64 = (file) => {
return new Promise((resolve, reject) => {
const reader = new FileReader();
reader.readAsDataURL(file);
reader.onload = () => {
resolve(reader.result);
};
reader.onerror = reject;
});
};
/**
* @Title bufferToBase64
* @param {*} buffer
* @returns
*/
export const bufferToBase64 = (buffer) => {
return 'data:image/jpeg;base64,' + window.btoa(new Uint8Array(buffer).reduce((data, byte) => data + String.fromCharCode(byte), ''));
};
/**
* @Title blob转json
* @param {*} file
* @returns
*/
export const blobToJSON = (blob) => {
return new Promise((resolve, reject) => {
const reader = new FileReader();
reader.readAsText(blob, 'utf-8');
reader.onload = () => {
const res = !isEmpty(reader.result) ? JSON.parse(reader.result) : reader.result;
resolve(res);
};
reader.onerror = reject;
});
};
/**
* @Title 将array转化为树
* @param tree
* @returns
*/
export const getTree = (tree = []) => {
tree.forEach((item) => {
delete item.children;
});
const map = {};
tree.forEach((item) => {
map[item.id] = item;
});
const arr = [];
tree.forEach((item) => {
const parent = map[item.parentId];
if (parent) {
(parent.children || (parent.children = [])).push(item);
} else {
arr.push(item);
}
});
return arr;
};
/**
* @Title 获取路由中的参数
* @param name
* @returns
*/
export const getUrlQuery = (name) => {
const url = window.location.href;
const hash = url.substring(url.indexOf('#') + 1);
const searchIndex = hash.indexOf('?');
const search = searchIndex !== -1 ? hash.substring(searchIndex + 1) : '';
const usp = new URLSearchParams(search);
return usp.get(name);
};
/**
* @Title 将字符串url参数转换为Object
* @param {*} url
* @returns
*/
export const param2Obj = (url) => {
return (url) => Object.fromEntries(new URLSearchParams(url));
};
/**
* @Title 将Object参数转换为字符串
* @param {*} json
* @returns
*/
export const obj2Param = (json) => {
if (!json) return '';
return Object.keys(json)
.map((key) => {
if (isEmpty(json[key])) return '';
return encodeURIComponent(key) + '=' + encodeURIComponent(json[key]);
})
.join('&');
};
/**
* @Title 获取静态资源文件
* @param {*} url
* @returns
*/
export const getAssetsFile = (url) => {
return new URL(`../assets/images/${url}`, import.meta.url);
};
/**
* 文件下载
* @param {*} url
* @param {*} fileName
* @returns
*/
export const dowloadFile = async (options = { url: '', files: null, fileName: '' }) => {
const { url, files, fileName } = options;
const chunkSize = 1 * 1024 * 1024;
const jszip = new JsZip();
function chunkFile(file, chunkSize) {
let chunks = [];
let fileSize = file.size;
let currentPos = 0;
while (currentPos < fileSize) {
let endPos = currentPos + chunkSize;
if (endPos > fileSize) {
endPos = fileSize;
}
chunks.push(file.slice(currentPos, endPos));
currentPos = endPos;
}
return chunks;
}
function dowloadLink(content, fileName) {
const blob = new Blob([content]);
if ('download' in document.createElement('a')) {
const elink = document.createElement('a');
elink.download = fileName;
elink.style.display = 'none';
elink.href = URL.createObjectURL(blob);
document.body.appendChild(elink);
elink.click();
URL.revokeObjectURL(elink.href);
document.body.removeChild(elink);
} else {
navigator.msSaveBlob(blob, fileName);
}
}
return new Promise((resolve, reject) => {
if (!isEmpty(options?.url)) {
JsZipUtils.getBinaryContent(url, (err, file) => {
if (err) {
return reject(err);
}
jszip
.loadAsync(file)
.then((zip) => {
return zip.generateAsync({ type: 'blob' });
})
.then((blob) => {
saveAs(blob, `${fileName}.zip`);
});
});
} else {
// if (files.type === 'binary/octet-stream') {
// dowloadLink(files, fileName);
// return;
// }
let chunks = null;
if (files.size > chunkSize) {
chunks = chunkFile(files, chunkSize);
}
const fileType = files.type.split('/')[1];
const newFile = files.type === 'binary/octet-stream' ? fileName : `${fileName}.${fileType}`;
if (chunks) {
let count = 1;
chunks.forEach(function (chunk) {
jszip.file(newFile, chunk, { binary: true });
count++;
});
} else {
jszip.file(`${fileName}.${fileType}`, files, { binary: true });
}
jszip.generateAsync({ type: 'blob' }).then((blob) => {
saveAs(blob, `${fileName}.zip`);
resolve(true);
});
}
});
};
/**
* @Title 模拟休眠
* @param {*} duration
* @returns
*/
export const sleep = (duration = 0) =>
new Promise((resolve) => {
setTimeout(resolve, duration);
});
/**
* @Title 创建id
* @param {*} prefix
* @returns
*/
export const createId = (prefix) => {
const val = Date.now() + Math.ceil(Math.random() * 99999);
return isEmpty(prefix) ? val : prefix + '-' + val;
};
/**
* @Title 生成数据
* @param {*} duration
* @returns
*/
export const mockData = (item = {}, len = 1) => {
const list = [];
for (let i = 0; i < len; i++) {
let temp = { ...item, id: createId() };
list.push(temp);
}
return list;
};
/**
* @Title 日期格式化
* @param {*} date
* @param {*} format
* @returns
*/
export const dateFormat = (datetime, formater = 'YYYY-MM-DD hh:mm:ss') => {
if (datetime instanceof Date || datetime) {
return dayjs(datetime).format(formater);
} else {
return null;
}
};
/**
* @Title 字符串转日期
* @param {*} str
* @returns
*/
export const toDate = (str) => {
return !isEmpty(str) ? dayjs(str) : dayjs();
};
/**
* @Title 字符串转日期
* @param {*} str
* @returns
*/
export const getDate = (num, type, formater = 'YYYY-MM-DD', start = true) => {
const date = dayjs().subtract(num, type);
return start ? date.startOf(type).format(formater) : date.endOf(type).format(formater);
};
/**
* @Title: 获取时间差
* @param start
* @param end
* @param type
* @returns
*/
export const getDiffTime = (start, end, type) => {
const startTime = dayjs(start);
const endTime = dayjs(end);
const duration = endTime.diff(startTime);
let diff = 0;
switch (type) {
case 'DD': {
diff = duration / (1000 * 60 * 60 * 24);
break;
}
case 'HH': {
diff = duration / (1000 * 60 * 60);
break;
}
case 'mm': {
diff = duration / (1000 * 60);
break;
}
}
return Math.round(diff);
};
/**
* @Title: 开始日期
* @param last
* @param type
* @param formater
* @returns
*/
export const startDate = (num, type = 'month', formater = 'YYYY-MM-DD HH:mm:ss') => {
if (num === 'now') return dayjs().format(formater);
if (typeof num === 'string') return dayjs(num).startOf(type).format(formater);
return num === 0 ? dayjs().startOf(type).format(formater) : dayjs().subtract(num, type).startOf(type).format(formater);
};
/**
* @Title: 结束日期
* @param num
* @param type
* @param formater
* @returns
*/
export const endDate = (num = 0, type = 'month', formater = 'YYYY-MM-DD HH:mm:ss') => {
if (num === 'now') return dayjs().format(formater);
if (typeof num === 'string') return dayjs(num).endOf(type).format(formater);
return num === 0 ? dayjs().endOf(type).format(formater) : dayjs().subtract(num, type).endOf(type).format(formater);
};

View File

@ -0,0 +1,84 @@
/**
* @Description: 路由权限
* @Author: zenghua.wang
* @Date: 2022-01-26 22:04:31
* @LastEditors: zenghua.wang
* @LastEditTime: 2024-05-22 13:36:31
*/
import NProgress from 'nprogress';
import 'nprogress/nprogress.css';
import router from '@/router';
import { useUserStore } from '@/store/modules/user';
import { usePermissionStore } from '@/store/modules/permission';
NProgress.configure({ showSpinner: false });
const { VITE_APP_NAME } = import.meta.env;
const whiteList = ['/login'];
router.beforeEach(async (to, from, next) => {
// 解决三级菜单页面缓存问题
// if (to.matched && to.matched.length > 1) {
// for (let i = 0; i < to.matched.length; i++) {
// const element = to.matched[i];
// if (element.components.default.name === 'ViewBox') {
// to.matched.splice(i, 1);
// }
// }
// }
NProgress.start();
if (typeof to.meta.title === 'string') {
document.title = VITE_APP_NAME + ' | ' + to.meta.title;
}
const userStore = useUserStore();
const hasToken = true; //userStore.hasToken();
if (hasToken) {
if (to.path === '/login') {
next({ path: '/' });
} else {
try {
const PermissionStore = usePermissionStore();
if (!PermissionStore.routes.length) {
const accessRoutes = await PermissionStore.generateRoutes(userStore.roles);
accessRoutes.forEach((item) => router.addRoute(item));
next({ ...to, replace: true });
} else {
next();
}
} catch (error) {
next(`/login?redirect=${to.path}`);
}
}
} else {
if (whiteList.indexOf(to.path) !== -1) {
next();
} else {
next(`/login?redirect=${to.path}`);
}
}
// const PermissionStore = usePermissionStore();
// console.log('===', PermissionStore.routes);
// if (!PermissionStore.routes.length) {
// const accessRoutes = await PermissionStore.getRoutes();
// accessRoutes.forEach((item) => router.addRoute(item));
// next({ ...to, replace: true });
// } else {
// next();
// }
});
router.afterEach((to) => {
// qiankun子应用跳转回主应用时判断#app是否还有渲染的子应用,如若没有则重新渲染主应用
// setTimeout(() => {
// if (to.path === '/') {
// if (window.wocwin_qiankun) {
// window.wocwin_qiankun = null;
// }
// }
// }, 300);
NProgress.done();
});

107
main/src/utils/router.js Normal file
View File

@ -0,0 +1,107 @@
/**
* @Description: 路由方法
* @Author: zenghua.wang
* @Date: 2022-01-26 21:55:58
* @LastEditors: zenghua.wang
* @LastEditTime: 2024-04-14 11:03:08
*/
import path from 'path-browserify';
import Views from '@/layouts/Views.vue';
import { isEmpty } from './index';
const modules = import.meta.glob('../views/**/**.vue');
/**
* 创建路由菜单
* @param {*} menus
*/
export function createAsyncRoutes(menus = []) {
if (isEmpty(menus)) return menus;
const res = [];
menus.forEach((menu) => {
const tmp = {
id: menu.id,
parentId: menu.parentId,
path: menu.path,
component: isEmpty(menu.component) ? Views : modules[/* @vite-ignore */ `../views/${menu.component.replace('/views/', '')}`],
redirect: menu.redirect ?? null,
name: menu.name,
meta: {
title: menu.title ?? '',
icon: menu.icon ?? '',
keepAlive: menu.keepAlive ?? false,
},
children: menu.children,
hidden: menu.hidden ?? false,
};
if (tmp.children) {
tmp.children = createAsyncRoutes(tmp.children, false);
}
res.push(tmp);
});
return res;
}
/**
* 通过递归过滤异步路由表
* @param routes asyncRoutes
* @param roles
*/
export function filterAsyncRoutes(routes, roles) {
const res = [];
routes.forEach((route) => {
const tmp = { ...route };
if (hasPermission(roles, tmp)) {
if (tmp.children) {
tmp.children = filterAsyncRoutes(tmp.children, roles);
}
res.push(tmp);
}
});
return res;
}
/**
* 使用 meta.role 来确定当前用户是否具有权限
* @param roles
* @param route
*/
export function hasPermission(roles, route) {
if (route.meta && route.meta.roles) {
return roles.some((role) => route.meta.roles.includes(role));
} else {
return false;
}
}
/**
* 使用递归过滤需要缓存的路由
* @param {*} routers
* @returns
*/
export function filterKeepAlive(routers) {
const cacheRouter = [];
const loop = (routers) => {
routers.forEach((item) => {
if (item.meta?.keepAlive && item.name) {
cacheRouter.push(item.name);
}
if (item.children && item.children.length) {
loop(item.children);
}
});
};
loop(routers);
return cacheRouter;
}
/**
*
* @param {*} routers
* @param {*} pathUrl
*/
export function handleRoutes(routers, pathUrl = '') {
routers.forEach((item) => {
item.path = path.resolve(pathUrl, item.path);
});
}

10
main/src/views/index.vue Normal file
View File

@ -0,0 +1,10 @@
<template>
<div class="platform">
<h2>平台首页</h2>
</div>
</template>
<script setup>
import { reactive } from 'vue';
const state = reactive({});
</script>

View File

@ -0,0 +1,5 @@
<template>
<div>登录</div>
</template>
<script setup></script>

136
main/vite.config.js Normal file
View File

@ -0,0 +1,136 @@
import { defineConfig, loadEnv } from 'vite';
import vue from '@vitejs/plugin-vue';
import qiankun from 'vite-plugin-qiankun';
import eslintPlugin from 'vite-plugin-eslint';
import vueSetupExtend from 'vite-plugin-vue-setup-extend';
import compression from 'vite-plugin-compression';
import { createSvgIconsPlugin } from 'vite-plugin-svg-icons';
import { createHtmlPlugin } from 'vite-plugin-html';
// import { viteMockServe } from 'vite-plugin-mock';
import AutoImport from 'unplugin-auto-import/vite';
import Components from 'unplugin-vue-components/vite';
import postcssImport from 'postcss-import';
import autoprefixer from 'autoprefixer';
// import postCssPxToRem from 'postcss-pxtorem';
import { resolve } from 'path';
export default defineConfig(({ command, mode }) => {
const { VITE_PORT, VITE_APP_NAME } = loadEnv(mode, process.cwd());
const config = {
base: './',
build: {
target: 'ESNext',
outDir: 'dist',
minify: 'terser',
},
server: {
host: '0.0.0.0',
port: VITE_PORT,
open: true,
https: false,
headers: {
'Access-Control-Allow-Origin': '*',
},
proxy: {
// [VITE_APP_BASE_API]: {
// target: VITE_APP_BASE_URL,
// changeOrigin: true,
// rewrite: (path) => path.replace(/^\/apis/, ''),
// },
'^/api': {
target: process.env.VUE_APP_BASE_API, // 开发环境
changeOrigin: true,
pathRewrite: {
'^/api': '',
},
'^/admin_api': {
target: 'https://mock.mengxuegu.com/mock/65d00eb6351bbd02cf3398e3/api',
changeOrigin: true,
pathRewrite: {
'^/admin_api': '',
},
},
'^/v2api': {
target: 'https://mock.mengxuegu.com/mock/663f2f7737199f49537c350f/api-v2',
changeOrigin: true,
pathRewrite: {
'^/v2api': '',
},
},
},
},
},
resolve: {
alias: {
'@': resolve(__dirname, 'src'),
},
extensions: ['.js', '.vue', '.json', '.ts'],
},
css: {
preprocessorOptions: {
scss: {
additionalData: '@import "@/styles/global.scss";',
api: 'modern-compiler',
},
},
postcss: {
plugins: [
postcssImport,
autoprefixer({
overrideBrowserslist: ['> 1%', 'last 2 versions'],
}),
// postCssPxToRem({
// rootValue: 192,
// selectorBlackList: [],
// propList: ['*'],
// exclude: /node_modules/i,
// }),
],
},
},
plugins: [
vue(),
qiankun(),
vueSetupExtend(),
createHtmlPlugin({
inject: {
data: {
web_title: VITE_APP_NAME,
},
},
}),
eslintPlugin({
include: ['src/**/*.ts', 'src/**/*.vue', 'src/*.ts', 'src/*.vue'],
}),
Components({
dirs: ['../global/components', 'src/components'],
extensions: ['vue', 'js', 'jsx', 'ts', 'tsx'],
resolvers: [],
}),
compression(),
AutoImport({
include: [/\.[tj]s?$/, /\.vue$/],
imports: ['vue', 'vue-router'],
}),
createSvgIconsPlugin({
iconDirs: [resolve(process.cwd(), 'src/assets/svgs')],
symbolId: 'icon-[name]',
}),
// viteMockServe({
// mockPath: 'src/mock',
// watchFiles: true,
// localEnabled: command === 'dev',
// prodEnabled: false,
// }),
],
};
if (mode === 'production') {
config.build.terserOptions = {
compress: {
drop_console: true,
drop_debugger: true,
},
};
}
return config;
});

7036
main/yarn.lock Normal file

File diff suppressed because it is too large Load Diff

32
package.json Normal file
View File

@ -0,0 +1,32 @@
{
"name": "daimp-front",
"version": "1.0.0",
"description": "数字农业产业管理平台(Digital-Agriculture-Industry-Management-Platform)",
"scripts": {
"install:all": "npm-run-all install:* ",
"install": "npm-run-all --serial install:*",
"install:main": "cd main && yarn install",
"install:sub-admin": "cd sub-admin && yarn install",
"install:sub-vue": "cd sub-vue && yarn install",
"dev": "npm-run-all --parallel dev:*",
"dev:main": "cd main && yarn dev",
"dev:sub-admin": "cd sub-app && yarn dev",
"dev:sub-vue": "cd sub-vue && yarn dev",
"build": "npm-run-all --serial build:*",
"build:main": "cd main && yarn build",
"build:sub-admin": "cd sub-admin && yarn build",
"build:sub-vue": "cd sub-vue && yarn build",
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [
"vue3",
"vite",
"qiankun"
],
"author": "",
"license": "MIT",
"dependencies": {},
"devDependencies": {
"npm-run-all": "^4.1.5"
}
}

9
sub-admin/.editorconfig Normal file
View File

@ -0,0 +1,9 @@
root = true
[*.{js,jsx,ts,tsx,vue}]
charset = utf-8
indent_style = space
indent_size = 2
insert_final_newline = true
trim_trailing_whitespace = true
end_of_line = auto

View File

@ -0,0 +1,6 @@
# 开发环境
VITE_PORT = 9527
VITE_MODE = 'DEV'
VITE_APP_NAME = 'sub-admin'
VITE_APP_BASE_API = '/apis'
VITE_APP_BASE_URL = 'http://localhost:8080/'

View File

@ -0,0 +1,5 @@
# 生产环境
VITE_MODE = 'PRO'
VITE_APP_NAME = 'sub-vue'
VITE_APP_BASE_API = 'https://www.localhost.com/8080/api/'
VITE_APP_BASE_URL = 'https://www.localhost.com/8080/'

14
sub-admin/.eslintignore Normal file
View File

@ -0,0 +1,14 @@
*.sh
*.md
*.woff
*.ttf
.vscode
.idea
.husky
.local
dist
src/assets
node_modules
Dockerfile
stats.html
tailwind.config.js

62
sub-admin/.eslintrc.cjs Normal file
View File

@ -0,0 +1,62 @@
/*
* @Descripttion: .eslintrc.cjs
* 在VSCode中安装ESLint插件编写过程中检测代码质量
* ESLint 代码质量校验相关配置
* 这里使用prettier作为代码格式化工具用ESLint做代码质检
* 相关配置使用下面extends扩展先做默认设置
* .prettierrc.cjs文件中配置好后格式化规则会以.prettierrc.cjs作为最终格式所以不建议在本文件中做代码格式化相关配置
* 相关prettier配置ESLint会默认加载为代码质检 格式化以prettier为主
* 在本配置文件中只做代码质量约束规范配置
* @Author: zenghua.wang
* @Date: 2022-09-22 15:53:58
* @LastEditors: zenghua.wang
* @LastEditTime: 2024-03-22 10:19:39
*/
module.exports = {
env: {
browser: true,
es2021: true,
node: true,
},
extends: [
'eslint-config-prettier',
'eslint:recommended',
// 'plugin:@typescript-eslint/recommended',
'plugin:vue/vue3-recommended',
'plugin:vue/vue3-essential',
'plugin:prettier/recommended',
],
overrides: [
{
env: {
node: true,
},
files: ['.eslintrc.{js,cjs}'],
parserOptions: {
sourceType: 'script',
},
},
],
parserOptions: {
ecmaVersion: 'latest',
sourceType: 'module',
requireConfigFile: false,
parser: '@babel/eslint-parser',
// parser: '@typescript-eslint/parser',
},
plugins: ['vue', 'prettier'],
globals: {
defineProps: 'readonly',
defineEmits: 'readonly',
defineExpose: 'readonly',
withDefaults: 'readonly',
},
// 这里时配置规则的,自己看情况配置
rules: {
'prettier/prettier': 'error',
'no-debugger': 'off',
'no-unused-vars': 'off',
'vue/no-unused-vars': 'off',
'vue/multi-word-component-names': 'off',
},
};

116
sub-admin/.gitignore vendored Normal file
View File

@ -0,0 +1,116 @@
# Logs
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
lerna-debug.log*
# Diagnostic reports (https://nodejs.org/api/report.html)
report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
# Runtime data
pids
*.pid
*.seed
*.pid.lock
# Directory for instrumented libs generated by jscoverage/JSCover
lib-cov
# Coverage directory used by tools like istanbul
coverage
*.lcov
# nyc test coverage
.nyc_output
# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
.grunt
# Bower dependency directory (https://bower.io/)
bower_components
# node-waf configuration
.lock-wscript
# Compiled binary addons (https://nodejs.org/api/addons.html)
build/Release
# Dependency directories
node_modules/
jspm_packages/
# Snowpack dependency directory (https://snowpack.dev/)
web_modules/
# TypeScript cache
*.tsbuildinfo
# Optional npm cache directory
.npm
# Optional eslint cache
.eslintcache
# Microbundle cache
.rpt2_cache/
.rts2_cache_cjs/
.rts2_cache_es/
.rts2_cache_umd/
# Optional REPL history
.node_repl_history
# Output of 'npm pack'
*.tgz
# Yarn Integrity file
.yarn-integrity
# dotenv environment variables file
.env
.env.test
# parcel-bundler cache (https://parceljs.org/)
.cache
.parcel-cache
# Next.js build output
.next
out
# Nuxt.js build / generate output
.nuxt
dist
# Gatsby files
.cache/
# Comment in the public line in if your project uses Gatsby and not Next.js
# https://nextjs.org/blog/next-9-1#public-directory-support
# public
# vuepress build output
.vuepress/dist
# Serverless directories
.serverless/
# FuseBox cache
.fusebox/
# DynamoDB Local files
.dynamodb/
# TernJS port file
.tern-port
# Stores VSCode versions used for testing VSCode extensions
.vscode-test
# yarn v2
.yarn/cache
.yarn/unplugged
.yarn/build-state.yml
.yarn/install-state.gz
.pnp.*

20
sub-admin/.prettierignore Normal file
View File

@ -0,0 +1,20 @@
## OS
.DS_Store
node_modules
.idea
.editorconfig
package-lock.json
.npmrc
# Ignored suffix
*.log
*.md
*.svg
*.png
*ignore
## Local
## Built-files
.cache
dist

52
sub-admin/.prettierrc.cjs Normal file
View File

@ -0,0 +1,52 @@
/*
* @Descripttion: .prettierrc.cjs
* 在VSCode中安装prettier插件 打开插件配置填写`.prettierrc.js` 将本文件作为其代码格式化规范
* 在本文件中修改格式化规则不会同时触发改变ESLint代码检查所以每次修改本文件需要重启VSCodeESLint检查才能同步代码格式化
* 需要相应的代码格式化规范请自行查阅配置下面为默认项目配置
* @Author: zenghua.wang
* @Date: 2022-09-22 15:53:58
* @LastEditors: zenghua.wang
* @LastEditTime: 2024-01-24 19:22:25
*/
module.exports = {
// 一行最多多少个字符
printWidth: 150,
// 指定每个缩进级别的空格数
tabWidth: 2,
// 使用制表符而不是空格缩进行
useTabs: false,
// 在语句末尾是否需要分号
semi: true,
// 是否使用单引号
singleQuote: true,
// 更改引用对象属性的时间 可选值"<as-needed|consistent|preserve>"
quoteProps: 'as-needed',
// 在JSX中使用单引号而不是双引号
jsxSingleQuote: false,
// 多行时尽可能打印尾随逗号。(例如,单行数组永远不会出现逗号结尾。) 可选值"<none|es5|all>"默认none
trailingComma: 'es5',
// 在对象文字中的括号之间打印空格
bracketSpacing: true,
// jsx 标签的反尖括号需要换行
jsxBracketSameLine: false,
// 在单独的箭头函数参数周围包括括号 always(x) => x \ avoidx => x
arrowParens: 'always',
// 这两个选项可用于格式化以给定字符偏移量(分别包括和不包括)开始和结束的代码
rangeStart: 0,
rangeEnd: Infinity,
// 指定要使用的解析器,不需要写文件开头的 @prettier
requirePragma: false,
// 不需要自动在文件开头插入 @prettier
insertPragma: false,
// 使用默认的折行标准 always\never\preserve
proseWrap: 'preserve',
// 指定HTML文件的全局空格敏感度 css\strict\ignore
htmlWhitespaceSensitivity: 'css',
// Vue文件脚本和样式标签缩进
vueIndentScriptAndStyle: false,
//在 windows 操作系统中换行符通常是回车 (CR) 加换行分隔符 (LF),也就是回车换行(CRLF)
//然而在 Linux 和 Unix 中只使用简单的换行分隔符 (LF)。
//对应的控制字符为 "\n" (LF) 和 "\r\n"(CRLF)。auto意为保持现有的行尾
// 换行符使用 lf 结尾是 可选值"<auto|lf|crlf|cr>"
endOfLine: 'auto',
};

View File

@ -0,0 +1,17 @@
# .stylelintignore
# 旧的不需打包的样式库
*.min.css
# 其他类型文件
*.js
*.jpg
*.png
*.eot
*.ttf
*.woff
*.json
# 测试和打包目录
/dist/*
/node_modules/*
/src/assets/*

131
sub-admin/.stylelintrc.cjs Normal file
View File

@ -0,0 +1,131 @@
/*
* @Descripttion: .stylelintrc.cjs
* @Author: zenghua.wang
* @Date: 2022-09-22 15:53:58
* @LastEditors: zenghua.wang
* @LastEditTime: 2024-01-24 18:49:26
*/
module.exports = {
root: true,
plugins: ['stylelint-order', 'stylelint-scss'],
extends: [
'stylelint-config-standard',
'stylelint-config-standard-scss',
'stylelint-config-prettier',
'stylelint-config-html/vue',
'stylelint-config-recommended-vue',
'stylelint-config-recommended-scss'
],
overrides: [
{
files: ['**/*.{html,vue}'],
customSyntax: 'postcss-html'
}
],
rules: {
indentation: 2,
'selector-pseudo-element-no-unknown': [
true,
{
ignorePseudoElements: ['v-deep', ':deep']
}
],
'number-leading-zero': 'always',
'no-descending-specificity': null,
'function-url-quotes': 'always',
'string-quotes': 'single',
'unit-case': null,
'color-hex-case': 'lower',
'color-hex-length': 'long',
'rule-empty-line-before': 'never',
'font-family-no-missing-generic-family-keyword': null,
'selector-type-no-unknown': null,
'block-opening-brace-space-before': 'always',
'at-rule-no-unknown': null,
'no-duplicate-selectors': null,
'property-no-unknown': null,
'no-empty-source': null,
'selector-class-pattern': null,
'keyframes-name-pattern': null,
'selector-pseudo-class-no-unknown': [true, { ignorePseudoClasses: ['global', 'deep'] }],
'function-no-unknown': null,
'order/properties-order': [
'position',
'top',
'right',
'bottom',
'left',
'z-index',
'display',
'justify-content',
'align-items',
'float',
'clear',
'overflow',
'overflow-x',
'overflow-y',
'margin',
'margin-top',
'margin-right',
'margin-bottom',
'margin-left',
'padding',
'padding-top',
'padding-right',
'padding-bottom',
'padding-left',
'width',
'min-width',
'max-width',
'height',
'min-height',
'max-height',
'font-size',
'font-family',
'font-weight',
'border',
'border-style',
'border-width',
'border-color',
'border-top',
'border-top-style',
'border-top-width',
'border-top-color',
'border-right',
'border-right-style',
'border-right-width',
'border-right-color',
'border-bottom',
'border-bottom-style',
'border-bottom-width',
'border-bottom-color',
'border-left',
'border-left-style',
'border-left-width',
'border-left-color',
'border-radius',
'text-align',
'text-justify',
'text-indent',
'text-overflow',
'text-decoration',
'white-space',
'color',
'background',
'background-position',
'background-repeat',
'background-size',
'background-color',
'background-clip',
'opacity',
'filter',
'list-style',
'outline',
'visibility',
'box-shadow',
'text-shadow',
'resize',
'transition'
]
}
};

13
sub-admin/index.html Normal file
View File

@ -0,0 +1,13 @@
<!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" />
<title>EGGY TEAM</title>
</head>
<body>
<div id="app"></div>
<script type="module" src="/src/main.js"></script>
</body>
</html>

77
sub-admin/package.json Normal file
View File

@ -0,0 +1,77 @@
{
"name": "sub-admin",
"private": true,
"version": "0.0.1",
"type": "module",
"scripts": {
"dev": "vite --mode development",
"build": "vite build --mode production",
"build:dev": "vite build --mode dev",
"build:qa": "vite build --mode qa",
"preview": "vite preview",
"format": "prettier --write 'src/**/*.{vue,ts,tsx,js,jsx,css,less,scss,json,md}'",
"eslint": "npx eslint --init",
"lint": "npm run lint:script && npm run lint:style",
"lint:style": "stylelint 'src/**/*.{vue,scss,css,sass,less}' --fix",
"lint:script": "eslint --ext .js,.ts,.tsx,.vue --fix --quiet ./src"
},
"dependencies": {
"@element-plus/icons-vue": "^2.3.1",
"@smallwei/avue": "^3.6.2",
"@wangeditor/editor": "^5.1.23",
"@wangeditor/editor-for-vue": "^5.1.12",
"axios": "^1.6.5",
"echarts": "^5.5.0",
"element-plus": "^2.7.2",
"js-base64": "^3.7.6",
"lodash": "^4.17.21",
"moment": "^2.30.1",
"nprogress": "^0.2.0",
"path-browserify": "^1.0.1",
"pinia": "^2.1.7",
"pinia-plugin-persistedstate": "^3.2.1",
"screenfull": "^6.0.2",
"vue": "^3.3.11",
"vue-router": "^4.2.5",
"vue3-custom-component": "1.1.17"
},
"devDependencies": {
"@babel/core": "^7.23.7",
"@babel/eslint-parser": "^7.23.3",
"@types/path-browserify": "^1.0.2",
"@vitejs/plugin-vue": "^4.5.2",
"autoprefixer": "^10.4.17",
"eslint": "^8.56.0",
"eslint-config-prettier": "^9.1.0",
"eslint-plugin-prettier": "^5.1.3",
"eslint-plugin-vue": "^9.20.1",
"mockjs": "^1.1.0",
"postcss": "^8.4.33",
"postcss-html": "^1.6.0",
"postcss-import": "^16.0.0",
"prettier": "^3.2.4",
"sass": "^1.70.0",
"stylelint": "^16.2.0",
"stylelint-config-html": "^1.1.0",
"stylelint-config-prettier": "^9.0.5",
"stylelint-config-rational-order": "^0.1.2",
"stylelint-config-recommended": "^14.0.0",
"stylelint-config-recommended-scss": "^14.0.0",
"stylelint-config-recommended-vue": "^1.5.0",
"stylelint-config-standard": "^36.0.0",
"stylelint-config-standard-scss": "^13.0.0",
"stylelint-order": "^6.0.4",
"stylelint-scss": "^6.1.0",
"terser": "^5.27.0",
"unplugin-auto-import": "^0.17.3",
"unplugin-vue-components": "^0.26.0",
"vite": "^5.0.8",
"vite-plugin-compression": "^0.5.1",
"vite-plugin-eslint": "^1.8.1",
"vite-plugin-mock": "^3.0.1",
"vite-plugin-progress": "^0.0.7",
"vite-plugin-qiankun": "^1.0.15",
"vite-plugin-svg-icons": "^2.0.1",
"vite-plugin-vue-setup-extend": "^0.4.0"
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.2 KiB

View File

@ -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

27
sub-admin/src/App.vue Normal file
View File

@ -0,0 +1,27 @@
<!--
* @Description:
* @Author: zenghua.wang
* @Date: 2024-01-24 18:54:01
* @LastEditors: zenghua.wang
* @LastEditTime: 2024-01-26 22:57:34
-->
<template>
<el-config-provider :size="size" :locale="zhCn">
<router-view />
</el-config-provider>
</template>
<script setup name="app">
import { computed } from 'vue';
import { useSettingStore } from '@/store/modules/setting';
// element
import zhCn from 'element-plus/es/locale/lang/zh-cn';
const SettingStore = useSettingStore();
//
const size = computed(() => SettingStore.themeConfig.globalComSize);
</script>
<style lang="scss">
@import './styles/style.scss';
</style>

View File

@ -0,0 +1,27 @@
import request from '@/utils/axios';
import { isEmpty } from '@/utils';
const { VITE_MODE, VITE_APP_UPLOAD_URL } = import.meta.env;
/**
* @Title: 上传图片
*/
export function CommonUpload(data) {
const url = !isEmpty(data?.url) ? (VITE_MODE === 'PRO' ? data?.url : data?.url.replace(VITE_APP_UPLOAD_URL, '')) : '/upload';
return request(url, {
method: 'PUT',
isUpload: true,
uploadType: data.file.type,
data: data.file,
});
}
/**
* @Title: 上传编辑器图片
*/
export function UploadImageFromEditor(data) {
return request('/store/rich-text/generate-pic-upload-url', {
method: 'POST',
data: data,
});
}

Some files were not shown because too many files have changed in this diff Show More