提交 a5e77a08 authored 作者: Administrator's avatar Administrator

更新

上级 e59f1c1e
# 开发环境
VITE_APP_TITLE = "永信达商城系统"
VITE_APP_PORT = 3003
# 请求接口
VITE_APP_BASE_URL = "/admin"
# 开发环境
VITE_APP_TITLE = "永信达商城系统"
VITE_APP_PORT = 3003
# 请求接口
VITE_APP_BASE_URL = "/admin"
VITE_APP_BASE_IMG = ""
\ No newline at end of file
#生产环境
VITE_APP_TITLE = "永信达商城系统"
VITE_APP_PORT = 3002
# 请求接口
VITE_APP_BASE_URL = "/admin"
#生产环境
VITE_APP_TITLE = "永信达商城系统"
VITE_APP_PORT = 3002
# 请求接口
VITE_APP_BASE_URL = "/admin"
VITE_APP_BASE_IMG = ""
\ No newline at end of file
node_modules
.DS_Store
dist
dist-ssr
*.local
/dist
# local env files
.env.local
.env.*.local
# Log files
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
# Editor directories and files
.idea
.vscode
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw?
node_modules
.DS_Store
dist
dist-ssr
*.local
/dist
# local env files
.env.local
.env.*.local
# Log files
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
# Editor directories and files
.idea
.vscode
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw?
module.exports = {
// 字符串使用单引号
singleQuote: true,
// 每行末尾自动添加分号
semi: true,
// tab缩进大小,默认为2
tabWidth: 2,
};
module.exports = {
// 字符串使用单引号
singleQuote: true,
// 每行末尾自动添加分号
semi: true,
// tab缩进大小,默认为2
tabWidth: 2,
};
FROM nginx
COPY dist/ /var/www/html/
FROM nginx
COPY dist/ /var/www/html/
COPY nginx/default.conf /etc/nginx/conf.d/default.conf
\ No newline at end of file
# starbos-ui
#### Description
星支平台前端框架,
#### Software Architecture
Software architecture description
#### Installation
1. xxxx
2. xxxx
3. xxxx
#### Instructions
1. xxxx
2. xxxx
3. xxxx
#### Contribution
1. Fork the repository
2. Create Feat_xxx branch
3. Commit your code
4. Create Pull Request
#### Gitee Feature
1. You can use Readme\_XXX.md to support different languages, such as Readme\_en.md, Readme\_zh.md
2. Gitee blog [blog.gitee.com](https://blog.gitee.com)
3. Explore open source project [https://gitee.com/explore](https://gitee.com/explore)
4. The most valuable open source project [GVP](https://gitee.com/gvp)
5. The manual of Gitee [https://gitee.com/help](https://gitee.com/help)
6. The most popular members [https://gitee.com/gitee-stars/](https://gitee.com/gitee-stars/)
# starbos-ui
#### Description
星支平台前端框架,
#### Software Architecture
Software architecture description
#### Installation
1. xxxx
2. xxxx
3. xxxx
#### Instructions
1. xxxx
2. xxxx
3. xxxx
#### Contribution
1. Fork the repository
2. Create Feat_xxx branch
3. Commit your code
4. Create Pull Request
#### Gitee Feature
1. You can use Readme\_XXX.md to support different languages, such as Readme\_en.md, Readme\_zh.md
2. Gitee blog [blog.gitee.com](https://blog.gitee.com)
3. Explore open source project [https://gitee.com/explore](https://gitee.com/explore)
4. The most valuable open source project [GVP](https://gitee.com/gvp)
5. The manual of Gitee [https://gitee.com/help](https://gitee.com/help)
6. The most popular members [https://gitee.com/gitee-stars/](https://gitee.com/gitee-stars/)
# starbos-ui
#### 介绍
永信达商城前端框架,基于vite +vue3.0+typescript+element-plus开发
## 项目安装
```
yarn install
```
### 项目编译及启动
```
yarn dev
```
### 编译部署
```
yarn build
```
### 行格式化
```
yarn lint
```
### 自定义配置
See [Configuration Reference](https://cli.vuejs.org/config/).
# starbos-ui
#### 介绍
永信达商城前端框架,基于vite +vue3.0+typescript+element-plus开发
## 项目安装
```
yarn install
```
### 项目编译及启动
```
yarn dev
```
### 编译部署
```
yarn build
```
### 行格式化
```
yarn lint
```
### 自定义配置
See [Configuration Reference](https://cli.vuejs.org/config/).
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<link rel="icon" href="/favicon.ico" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>永信达后台管理系统</title>
</head>
<body>
<div id="app"></div>
<script type="module" src="/src/main.ts"></script>
</body>
</html>
<style>
html,
body,
#app {
padding: 0px;
margin: 0px;
height: 100%;
width: 100%;
}
</style>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<link rel="icon" href="/favicon.ico" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>永信达后台管理系统</title>
</head>
<body>
<div id="app"></div>
<script type="module" src="/src/main.ts"></script>
</body>
</html>
<style>
html,
body,
#app {
padding: 0px;
margin: 0px;
height: 100%;
width: 100%;
}
</style>
server {
listen 80;
client_max_body_size 100m;
#charset koi8-r;
access_log /var/log/nginx/host.access.log main;
error_log /var/log/nginx/error.log error;
location / {
root /var/www/html/;
index index.html index.htm;
if (!-e \$request_filename) {
rewrite ^(.*)\$ /index.html?s=\$1 last;
break;
}
}
location /admin {
proxy_pass http://yongxingda ;
}
location /wx {
proxy_pass http://yongxingda;
}
server {
listen 80;
client_max_body_size 100m;
#charset koi8-r;
access_log /var/log/nginx/host.access.log main;
error_log /var/log/nginx/error.log error;
location / {
root /var/www/html/;
index index.html index.htm;
if (!-e \$request_filename) {
rewrite ^(.*)\$ /index.html?s=\$1 last;
break;
}
}
location /admin {
proxy_pass http://yongxingda ;
}
location /wx {
proxy_pass http://yongxingda;
}
}
\ No newline at end of file
{
"name": "starbos-ui",
"version": "1.0.0",
"scripts": {
"dev": "vite",
"build": "vite build",
"serve": "vite preview --open",
"tsc": "vue-tsc --noEmit"
},
"dependencies": {
"@element-plus/icons-vue": "^1.1.4",
"@wangeditor/editor": "^5.1.22",
"@wangeditor/editor-for-vue": "^5.1.12",
"axios": "^0.24.0",
"echarts": "^5.4.0",
"element-plus": "^2.2.12",
"eslint": "^8.4.1",
"eslint-plugin-vue": "^8.2.0",
"lodash": "^4.17.21",
"moment": "^2.29.1",
"nprogress": "^0.2.0",
"qs": "^6.10.3",
"socket.io-client": "^4.5.0",
"vue": "^3.2.33",
"vue-demi": "^0.13.1",
"vue-i18n": "^9.1.10",
"vue-router": "4",
"vue-socket.io": "^3.0.10",
"vuex": "^4.0.2"
},
"devDependencies": {
"@types/lodash": "^4.14.182",
"@types/node": "^16.11.12",
"@types/qs": "^6.9.7",
"@vitejs/plugin-vue": "^2.3.1",
"node-sass": "^7.0.0",
"sass": "^1.44.0",
"sass-loader": "^12.4.0",
"terser": "^5.16.1",
"typescript": "^4.4.3",
"unplugin-auto-import": "^0.7.1",
"unplugin-icons": "^0.14.3",
"unplugin-vue-components": "^0.19.3",
"vite": "^2.6.14",
"vite-plugin-inspect": "^0.5.0",
"vite-plugin-svg-icons": "^2.0.1",
"vue-tsc": "^0.3.0"
},
"license": "ISC"
}
{
"name": "starbos-ui",
"version": "1.0.0",
"scripts": {
"dev": "vite",
"build": "vite build",
"serve": "vite preview --open",
"tsc": "vue-tsc --noEmit"
},
"dependencies": {
"@element-plus/icons-vue": "^1.1.4",
"@wangeditor/editor": "^5.1.22",
"@wangeditor/editor-for-vue": "^5.1.12",
"axios": "^0.24.0",
"echarts": "^5.4.0",
"element-plus": "^2.2.12",
"eslint": "^8.4.1",
"eslint-plugin-vue": "^8.2.0",
"lodash": "^4.17.21",
"moment": "^2.29.1",
"nprogress": "^0.2.0",
"qs": "^6.10.3",
"socket.io-client": "^4.5.0",
"vue": "^3.2.33",
"vue-demi": "^0.13.1",
"vue-i18n": "^9.1.10",
"vue-router": "4",
"vue-socket.io": "^3.0.10",
"vuex": "^4.0.2"
},
"devDependencies": {
"@types/lodash": "^4.14.182",
"@types/node": "^16.11.12",
"@types/qs": "^6.9.7",
"@vitejs/plugin-vue": "^2.3.1",
"node-sass": "^7.0.0",
"sass": "^1.44.0",
"sass-loader": "^12.4.0",
"terser": "^5.16.1",
"typescript": "^4.4.3",
"unplugin-auto-import": "^0.7.1",
"unplugin-icons": "^0.14.3",
"unplugin-vue-components": "^0.19.3",
"vite": "^2.6.14",
"vite-plugin-inspect": "^0.5.0",
"vite-plugin-svg-icons": "^2.0.1",
"vue-tsc": "^0.3.0"
},
"license": "ISC"
}
module.exports = {
root: true,
parserOptions: {
// 对Babel解析器的包装使其与 ESLint 兼容。
// parser: 'babel-eslint',
// 代码是 ECMAScript 模块
sourceType: "module",
},
parser: "vue-eslint-parser",
env: {
browser: true,
node: true,
es6: true,
},
extends: [
// https://github.com/vuejs/eslint-plugin-vue
"plugin:vue/vue3-essential",
"plugin:vue/vue3-strongly-recommended",
"plugin:vue/vue3-recommended",
],
rules: {
"no-console": "off",
"comma-dangle": [2, "never"], //禁用使用拖尾逗号
"vue/html-self-closing": [
"error",
{
html: {
void: "never",
normal: "always",
component: "always",
},
"vue/multi-word-component-names": [
"off",
{
ignores: [],
},
], //允许组件单词
},
],
},
};
module.exports = {
root: true,
parserOptions: {
// 对Babel解析器的包装使其与 ESLint 兼容。
// parser: 'babel-eslint',
// 代码是 ECMAScript 模块
sourceType: "module",
},
parser: "vue-eslint-parser",
env: {
browser: true,
node: true,
es6: true,
},
extends: [
// https://github.com/vuejs/eslint-plugin-vue
"plugin:vue/vue3-essential",
"plugin:vue/vue3-strongly-recommended",
"plugin:vue/vue3-recommended",
],
rules: {
"no-console": "off",
"comma-dangle": [2, "never"], //禁用使用拖尾逗号
"vue/html-self-closing": [
"error",
{
html: {
void: "never",
normal: "always",
component: "always",
},
"vue/multi-word-component-names": [
"off",
{
ignores: [],
},
], //允许组件单词
},
],
},
};
<script lang="ts">
import { ElConfigProvider } from "element-plus";
import zhCn from "element-plus/lib/locale/lang/zh-cn";
export default {
components: {
[ElConfigProvider.name]: ElConfigProvider,
},
setup() {
let locale = zhCn;
return {
locale,
};
},
};
</script>
<template>
<el-config-provider :locale="locale">
<router-view></router-view>
</el-config-provider>
</template>
<style lang="scss">
.margin-bottom-10 {
margin-bottom: 10px;
}
/* 1px边框适配 */
.border-top-1px {
position: relative;
&::after {
content: "";
position: absolute;
top: 0;
left: 0;
width: 100%;
border-top: 1px solid #e1e1e1;
transform: scaleY(0.5);
}
}
.border-bottom-1px {
position: relative;
&::after {
content: "";
position: absolute;
bottom: 0;
left: 0;
width: 100%;
border-bottom: 1px solid #e0e0e0;
transform: scaleY(0.4);
}
}
.border-left-1px {
position: relative;
&::after {
content: "";
position: absolute;
bottom: 0;
left: 0;
height: 100%;
border-left: 1px solid #e0e0e0;
transform: scaleX(0.4);
}
}
/* 1px边框适配end */
/* flex布局 */
/* 两端对齐 */
.space-between {
display: flex;
align-items: center;
justify-content: space-between;
}
/* 垂直居中对齐 */
.flex-center-h {
display: flex;
align-items: center;
}
/* 垂直水平居中对齐 */
.flex-center {
display: flex;
align-items: center;
justify-content: center;
}
/* 上下贴边 子class 宽度需100% */
.align-content {
display: flex;
flex-flow: wrap;
align-content: space-between;
}
/* flex布局end */
.font12 {
font-size: 12px;
}
.font14 {
font-size: 14px;
}
.font16 {
font-size: 16px;
}
/* 超出省略... */
.over1 {
overflow: hidden;
text-overflow: ellipsis;
word-break: break-all;
display: -webkit-box;
-webkit-line-clamp: 1;
-webkit-box-orient: vertical;
display: -moz-box;
-moz-line-clamp: 1 !important;
-moz-box-orient: vertical;
}
.over2 {
overflow: hidden;
text-overflow: ellipsis;
word-break: break-all;
display: -webkit-box;
-webkit-line-clamp: 2;
-webkit-box-orient: vertical;
display: -moz-box;
-moz-line-clamp: 2 !important;
-moz-box-orient: vertical;
}
.over3 {
overflow: hidden;
text-overflow: ellipsis;
word-break: break-all;
display: -webkit-box;
-webkit-line-clamp: 3;
-webkit-box-orient: vertical;
display: -moz-box;
-moz-line-clamp: 3 !important;
-moz-box-orient: vertical;
}
/* 超出省略...end */
</style>
<script lang="ts">
import { ElConfigProvider } from "element-plus";
import zhCn from "element-plus/lib/locale/lang/zh-cn";
export default {
components: {
[ElConfigProvider.name]: ElConfigProvider,
},
setup() {
let locale = zhCn;
return {
locale,
};
},
};
</script>
<template>
<el-config-provider :locale="locale">
<router-view></router-view>
</el-config-provider>
</template>
<style lang="scss">
.margin-bottom-10 {
margin-bottom: 10px;
}
/* 1px边框适配 */
.border-top-1px {
position: relative;
&::after {
content: "";
position: absolute;
top: 0;
left: 0;
width: 100%;
border-top: 1px solid #e1e1e1;
transform: scaleY(0.5);
}
}
.border-bottom-1px {
position: relative;
&::after {
content: "";
position: absolute;
bottom: 0;
left: 0;
width: 100%;
border-bottom: 1px solid #e0e0e0;
transform: scaleY(0.4);
}
}
.border-left-1px {
position: relative;
&::after {
content: "";
position: absolute;
bottom: 0;
left: 0;
height: 100%;
border-left: 1px solid #e0e0e0;
transform: scaleX(0.4);
}
}
/* 1px边框适配end */
/* flex布局 */
/* 两端对齐 */
.space-between {
display: flex;
align-items: center;
justify-content: space-between;
}
/* 垂直居中对齐 */
.flex-center-h {
display: flex;
align-items: center;
}
/* 垂直水平居中对齐 */
.flex-center {
display: flex;
align-items: center;
justify-content: center;
}
/* 上下贴边 子class 宽度需100% */
.align-content {
display: flex;
flex-flow: wrap;
align-content: space-between;
}
/* flex布局end */
.font12 {
font-size: 12px;
}
.font14 {
font-size: 14px;
}
.font16 {
font-size: 16px;
}
/* 超出省略... */
.over1 {
overflow: hidden;
text-overflow: ellipsis;
word-break: break-all;
display: -webkit-box;
-webkit-line-clamp: 1;
-webkit-box-orient: vertical;
display: -moz-box;
-moz-line-clamp: 1 !important;
-moz-box-orient: vertical;
}
.over2 {
overflow: hidden;
text-overflow: ellipsis;
word-break: break-all;
display: -webkit-box;
-webkit-line-clamp: 2;
-webkit-box-orient: vertical;
display: -moz-box;
-moz-line-clamp: 2 !important;
-moz-box-orient: vertical;
}
.over3 {
overflow: hidden;
text-overflow: ellipsis;
word-break: break-all;
display: -webkit-box;
-webkit-line-clamp: 3;
-webkit-box-orient: vertical;
display: -moz-box;
-moz-line-clamp: 3 !important;
-moz-box-orient: vertical;
}
/* 超出省略...end */
</style>
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" width="1em" height="1em" preserveAspectRatio="xMidYMid meet" viewBox="0 0 20 20"><path d="M3 5h14V3H3v2zm12 8V7H5v6h10zM3 17h14v-2H3v2z" fill="currentColor"></path></svg>
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" width="1em" height="1em" preserveAspectRatio="xMidYMid meet" viewBox="0 0 20 20"><path d="M3 5h14V3H3v2zm12 8V7H5v6h10zM3 17h14v-2H3v2z" fill="currentColor"></path></svg>
<svg t="1612248825727" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="2133" width="128" height="128"><path d="M219.274 937.929c-55.89 0-101.359-45.502-101.359-101.427V251.879c0-57.479 46.708-106.035 101.996-106.035h396.04v56.951H219.91c-23.74 0-45.359 23.392-45.359 49.084v584.623c0 24.686 20.064 44.77 44.722 44.77h584.26c25.918 0 49.515-21.604 49.515-45.332V439.9h56.958v396.04c0 54.329-49.755 101.989-106.473 101.989h-584.26z m17.556-90.656c-9.038 0-17.174-4.225-22.316-11.593-6.02-8.628-7.114-19.995-3-31.177l105.543-213.708c1.945-10.565 6.485-19.543 13.23-26.288l378.709-378.955c9.95-9.95 23.172-15.428 37.237-15.428 14.052 0 27.281 5.478 37.245 15.421l86.067 86.144c9.943 9.943 15.42 23.18 15.42 37.258s-5.477 27.308-15.427 37.244L490.823 725.166c-6.888 6.902-15.804 11.34-27.26 13.572L251.396 843.835c-4.91 2.274-9.82 3.438-14.566 3.438z m28.453-68.798c-1.41 2.87-1.267 6.06 0.328 8.642 1.541 2.493 4.24 3.985 7.21 3.985 1.487 0 3.02-0.363 4.555-1.081l126.928-76.892-61.958-62.047-77.063 127.393z m107.864-176.703l80.432 80.507 269.79-269.961-81.015-79.932-269.207 269.386z m373.079-364.883c-5.43 0-10.546 2.123-14.394 5.985l-47.646 47.631 79.323 81.679 48.769-48.823c7.937-7.937 7.937-20.872-0.007-28.836l-51.617-51.651c-3.869-3.862-8.991-5.985-14.428-5.985zM534.497 790.903v-56.952h252.986v56.952H534.497z m122.518-98.017v-56.952h130.468v56.952H657.015zM240.44 447.844v-56.959h130.467v56.959H240.44z m0-98.024v-56.952h252.985v56.952H240.44z" p-id="2134" fill="#666666"></path></svg>
<svg t="1612248825727" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="2133" width="128" height="128"><path d="M219.274 937.929c-55.89 0-101.359-45.502-101.359-101.427V251.879c0-57.479 46.708-106.035 101.996-106.035h396.04v56.951H219.91c-23.74 0-45.359 23.392-45.359 49.084v584.623c0 24.686 20.064 44.77 44.722 44.77h584.26c25.918 0 49.515-21.604 49.515-45.332V439.9h56.958v396.04c0 54.329-49.755 101.989-106.473 101.989h-584.26z m17.556-90.656c-9.038 0-17.174-4.225-22.316-11.593-6.02-8.628-7.114-19.995-3-31.177l105.543-213.708c1.945-10.565 6.485-19.543 13.23-26.288l378.709-378.955c9.95-9.95 23.172-15.428 37.237-15.428 14.052 0 27.281 5.478 37.245 15.421l86.067 86.144c9.943 9.943 15.42 23.18 15.42 37.258s-5.477 27.308-15.427 37.244L490.823 725.166c-6.888 6.902-15.804 11.34-27.26 13.572L251.396 843.835c-4.91 2.274-9.82 3.438-14.566 3.438z m28.453-68.798c-1.41 2.87-1.267 6.06 0.328 8.642 1.541 2.493 4.24 3.985 7.21 3.985 1.487 0 3.02-0.363 4.555-1.081l126.928-76.892-61.958-62.047-77.063 127.393z m107.864-176.703l80.432 80.507 269.79-269.961-81.015-79.932-269.207 269.386z m373.079-364.883c-5.43 0-10.546 2.123-14.394 5.985l-47.646 47.631 79.323 81.679 48.769-48.823c7.937-7.937 7.937-20.872-0.007-28.836l-51.617-51.651c-3.869-3.862-8.991-5.985-14.428-5.985zM534.497 790.903v-56.952h252.986v56.952H534.497z m122.518-98.017v-56.952h130.468v56.952H657.015zM240.44 447.844v-56.959h130.467v56.959H240.44z m0-98.024v-56.952h252.985v56.952H240.44z" p-id="2134" fill="#666666"></path></svg>
<svg t="1615363536977" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="16426" width="256" height="256"><path d="M938.688 341.312v519.808c0 42.816-34.752 77.568-77.568 77.568H162.88c-42.816 0-77.568-34.752-77.568-77.568V341.312h853.376zM402.112 483.584H389.12a32.32 32.32 0 0 0-31.808 26.496l-0.512 5.76v215.424c0 15.808 11.392 29.056 26.496 31.744l5.76 0.576h12.992a32.32 32.32 0 0 0 31.808-26.56l0.512-5.76V515.84a32.32 32.32 0 0 0-32.32-32.32z m232.704 0H563.84a50.88 50.88 0 0 0-10.88 1.152 32.384 32.384 0 0 0-40.32 25.344L512 515.84v215.424a32.32 32.32 0 0 0 64.128 5.76l0.512-5.76v-74.368h58.24c46.336 0 83.968-38.784 83.968-86.656 0-47.872-37.632-86.656-84.032-86.656z m0 66.624l3.904 0.448a19.84 19.84 0 0 1 15.488 19.584 19.84 19.84 0 0 1-14.912 19.456l-4.48 0.512H576.64v-40h58.24z m226.304-464.896c42.816 0 77.568 34.752 77.568 77.568v121.6H85.312v-121.6c0-42.816 34.752-77.568 77.568-77.568h698.24z" p-id="16427"></path></svg>
<svg t="1615363536977" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="16426" width="256" height="256"><path d="M938.688 341.312v519.808c0 42.816-34.752 77.568-77.568 77.568H162.88c-42.816 0-77.568-34.752-77.568-77.568V341.312h853.376zM402.112 483.584H389.12a32.32 32.32 0 0 0-31.808 26.496l-0.512 5.76v215.424c0 15.808 11.392 29.056 26.496 31.744l5.76 0.576h12.992a32.32 32.32 0 0 0 31.808-26.56l0.512-5.76V515.84a32.32 32.32 0 0 0-32.32-32.32z m232.704 0H563.84a50.88 50.88 0 0 0-10.88 1.152 32.384 32.384 0 0 0-40.32 25.344L512 515.84v215.424a32.32 32.32 0 0 0 64.128 5.76l0.512-5.76v-74.368h58.24c46.336 0 83.968-38.784 83.968-86.656 0-47.872-37.632-86.656-84.032-86.656z m0 66.624l3.904 0.448a19.84 19.84 0 0 1 15.488 19.584 19.84 19.84 0 0 1-14.912 19.456l-4.48 0.512H576.64v-40h58.24z m226.304-464.896c42.816 0 77.568 34.752 77.568 77.568v121.6H85.312v-121.6c0-42.816 34.752-77.568 77.568-77.568h698.24z" p-id="16427"></path></svg>
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1543827393750" class="icon" style="" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="4695" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><defs><style type="text/css">@font-face { font-family: rbicon; src: url("chrome-extension://dipiagiiohfljcicegpgffpbnjmgjcnf/fonts/rbicon.woff2") format("woff2"); font-weight: normal; font-style: normal; }
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1543827393750" class="icon" style="" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="4695" xmlns:xlink="http://www.w3.org/1999/xlink" width="200" height="200"><defs><style type="text/css">@font-face { font-family: rbicon; src: url("chrome-extension://dipiagiiohfljcicegpgffpbnjmgjcnf/fonts/rbicon.woff2") format("woff2"); font-weight: normal; font-style: normal; }
</style></defs><path d="M64 64V640H896V64H64zM0 0h960v704H0V0z" p-id="4696"></path><path d="M192 896H768v64H192zM448 640H512v256h-64z" p-id="4697"></path><path d="M479.232 561.604267l309.9904-348.330667-47.803733-42.5472-259.566934 291.669333L303.957333 240.008533 163.208533 438.6048l52.224 37.009067 91.6224-129.28z" p-id="4698"></path></svg>
\ No newline at end of file
<svg t="1615365233977" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="31529" width="256" height="256"><path d="M375.466667 614.4V341.333333c0-40.96 27.306667-68.266667 68.266666-68.266666h136.533334c40.96 0 68.266667 27.306667 68.266666 68.266666v273.066667h191.146667c6.826667 0 20.48 6.826667 27.306667 6.826667 13.653333 13.653333 13.653333 34.133333 0 47.786666l-327.68 327.68c-13.653333 13.653333-34.133333 13.653333-47.786667 0L163.84 675.84c-6.826667-6.826667-13.653333-20.48-13.653333-27.306667 0-20.48 13.653333-34.133333 34.133333-34.133333H375.466667z m34.133333-477.866667h204.8c20.48 0 34.133333 13.653333 34.133333 34.133334s-13.653333 34.133333-34.133333 34.133333h-204.8c-20.48 0-34.133333-13.653333-34.133333-34.133333s13.653333-34.133333 34.133333-34.133334z m0-136.533333h204.8c20.48 0 34.133333 13.653333 34.133333 34.133333s-13.653333 34.133333-34.133333 34.133334h-204.8C389.12 68.266667 375.466667 54.613333 375.466667 34.133333s13.653333-34.133333 34.133333-34.133333z" p-id="31530"></path></svg>
<svg t="1615365233977" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="31529" width="256" height="256"><path d="M375.466667 614.4V341.333333c0-40.96 27.306667-68.266667 68.266666-68.266666h136.533334c40.96 0 68.266667 27.306667 68.266666 68.266666v273.066667h191.146667c6.826667 0 20.48 6.826667 27.306667 6.826667 13.653333 13.653333 13.653333 34.133333 0 47.786666l-327.68 327.68c-13.653333 13.653333-34.133333 13.653333-47.786667 0L163.84 675.84c-6.826667-6.826667-13.653333-20.48-13.653333-27.306667 0-20.48 13.653333-34.133333 34.133333-34.133333H375.466667z m34.133333-477.866667h204.8c20.48 0 34.133333 13.653333 34.133333 34.133334s-13.653333 34.133333-34.133333 34.133333h-204.8c-20.48 0-34.133333-13.653333-34.133333-34.133333s13.653333-34.133333 34.133333-34.133334z m0-136.533333h204.8c20.48 0 34.133333 13.653333 34.133333 34.133333s-13.653333 34.133333-34.133333 34.133334h-204.8C389.12 68.266667 375.466667 54.613333 375.466667 34.133333s13.653333-34.133333 34.133333-34.133333z" p-id="31530"></path></svg>
<svg t="1612242856287" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="5203" width="128" height="128"><path d="M422.4 395.392l-27.2-327.68L256.768 194.88 114.688 64 65.472 109.12 207.616 240 69.184 367.168 422.4 395.392zM960 66.752 604.096 91.776l138.112 127.488L600.064 350.08l49.216 45.312 142.016-130.88 138.112 127.552L960 66.752zM423.936 675.392 374.848 630.016l-142.144 130.88L94.592 633.344 64 958.656l355.84-25.024-138.112-127.488L423.936 675.392zM958.528 914.88l-142.016-130.88 138.368-127.104L601.472 628.608l27.328 327.744 138.432-127.232L909.248 960 958.528 914.88z" p-id="5204" fill="#666666"></path></svg>
<svg t="1612242856287" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="5203" width="128" height="128"><path d="M422.4 395.392l-27.2-327.68L256.768 194.88 114.688 64 65.472 109.12 207.616 240 69.184 367.168 422.4 395.392zM960 66.752 604.096 91.776l138.112 127.488L600.064 350.08l49.216 45.312 142.016-130.88 138.112 127.552L960 66.752zM423.936 675.392 374.848 630.016l-142.144 130.88L94.592 633.344 64 958.656l355.84-25.024-138.112-127.488L423.936 675.392zM958.528 914.88l-142.016-130.88 138.368-127.104L601.472 628.608l27.328 327.744 138.432-127.232L909.248 960 958.528 914.88z" p-id="5204" fill="#666666"></path></svg>
body {
font-family: Avenir, Helvetica, Arial, sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
color: #2c3e50;
width: 100%;
height: 100%;
margin: 0;
padding: 0;
}
html,
body,
#app {
padding: 0px;
margin: 0px;
height: 100%;
body {
font-family: Avenir, Helvetica, Arial, sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
color: #2c3e50;
width: 100%;
height: 100%;
margin: 0;
padding: 0;
}
html,
body,
#app {
padding: 0px;
margin: 0px;
height: 100%;
}
\ No newline at end of file
.router {
margin: 10px 5px;
}
.app-container {
padding: 20px;
}
.filter-container {
padding-bottom: 10px;
.filter-item {
display: inline-block;
vertical-align: middle;
margin-bottom: 10px;
margin-right: 10px;
}
}
.avatar-uploader .avatar {
width: 178px;
height: 178px;
display: block;
}
.avatar-uploader .el-upload {
border: 1px dashed #dcdfe6;
border-radius: 6px;
cursor: pointer;
position: relative;
overflow: hidden;
transition: 0.2s;
}
.avatar-uploader .el-upload:hover {
/* // border-color: var(--el-color-primary); */
}
.el-icon.avatar-uploader-icon {
font-size: 28px;
color: #8c939d;
width: 178px;
height: 178px;
text-align: center;
}
.router {
margin: 10px 5px;
}
.app-container {
padding: 20px;
}
.filter-container {
padding-bottom: 10px;
.filter-item {
display: inline-block;
vertical-align: middle;
margin-bottom: 10px;
margin-right: 10px;
}
}
.avatar-uploader .avatar {
width: 178px;
height: 178px;
display: block;
}
.avatar-uploader .el-upload {
border: 1px dashed #dcdfe6;
border-radius: 6px;
cursor: pointer;
position: relative;
overflow: hidden;
transition: 0.2s;
}
.avatar-uploader .el-upload:hover {
/* // border-color: var(--el-color-primary); */
}
.el-icon.avatar-uploader-icon {
font-size: 28px;
color: #8c939d;
width: 178px;
height: 178px;
text-align: center;
}
$color-primary: #007aff;
@import "./body.scss";
@import "./common.scss";
$color-primary: #007aff;
@import "./body.scss";
@import "./common.scss";
@import "./layout.scss";
\ No newline at end of file
// 设置全局布局样式文件
@mixin appBaseBox {
display: flex;
justify-content: flex-start;
}
// 父盒子高度
.set-common-head-fixed-container{
height:90px;// 预留菜单总高度,后期可能修改
}
// 顶部菜单盒子
.common-head-fixed-container{
position: fixed;
top: 0px;
left: 0px;
width: 100%;
z-index: 9;
}
.show-mobile{
padding: 0 5px;
background-color: #2c3e50;
.same-cell{
color: #fff;
}
}
// 头部菜单做媒体查询,适应所有屏幕大小,自定义宽度和样式变化
// 1、超小屏幕下,小于768px 布局容器的宽度为100%
@media screen and (max-width:767px){
.show-pc{
display: none;
}
.show-mobile{
display: block;
@include appBaseBox();
// background-color: red;
}
}
// 2、小屏幕下,大于等于768px 布局容器改为750px
@media screen and (min-width: 750px){
.show-pc{
display: none;
}
.show-mobile{
display: block;
@include appBaseBox();
// background-color: red;
}
}
// 3、中等屏幕下, 大于等于992px 布局容器改为970
@media screen and (min-width:992px){
.show-pc{
display: block;
// background-color: orange;
}
.show-mobile{
display: none;
}
}
// 4、大屏幕下,大于等于1200px, 布局容器改为1170px
@media screen and (min-width: 1200px){
.show-pc{
display: block;
// background-color: yellow;
}
.show-mobile{
display: none;
}
// 设置全局布局样式文件
@mixin appBaseBox {
display: flex;
justify-content: flex-start;
}
// 父盒子高度
.set-common-head-fixed-container{
height:90px;// 预留菜单总高度,后期可能修改
}
// 顶部菜单盒子
.common-head-fixed-container{
position: fixed;
top: 0px;
left: 0px;
width: 100%;
z-index: 9;
}
.show-mobile{
padding: 0 5px;
background-color: #2c3e50;
.same-cell{
color: #fff;
}
}
// 头部菜单做媒体查询,适应所有屏幕大小,自定义宽度和样式变化
// 1、超小屏幕下,小于768px 布局容器的宽度为100%
@media screen and (max-width:767px){
.show-pc{
display: none;
}
.show-mobile{
display: block;
@include appBaseBox();
// background-color: red;
}
}
// 2、小屏幕下,大于等于768px 布局容器改为750px
@media screen and (min-width: 750px){
.show-pc{
display: none;
}
.show-mobile{
display: block;
@include appBaseBox();
// background-color: red;
}
}
// 3、中等屏幕下, 大于等于992px 布局容器改为970
@media screen and (min-width:992px){
.show-pc{
display: block;
// background-color: orange;
}
.show-mobile{
display: none;
}
}
// 4、大屏幕下,大于等于1200px, 布局容器改为1170px
@media screen and (min-width: 1200px){
.show-pc{
display: block;
// background-color: yellow;
}
.show-mobile{
display: none;
}
}
\ No newline at end of file
// Generated by 'unplugin-auto-import'
// We suggest you to commit this file into source control
declare global {
const computed: typeof import('vue')['computed']
const createApp: typeof import('vue')['createApp']
const customRef: typeof import('vue')['customRef']
const defineAsyncComponent: typeof import('vue')['defineAsyncComponent']
const defineComponent: typeof import('vue')['defineComponent']
const effectScope: typeof import('vue')['effectScope']
const EffectScope: typeof import('vue')['EffectScope']
const getCurrentInstance: typeof import('vue')['getCurrentInstance']
const getCurrentScope: typeof import('vue')['getCurrentScope']
const h: typeof import('vue')['h']
const inject: typeof import('vue')['inject']
const isReadonly: typeof import('vue')['isReadonly']
const isRef: typeof import('vue')['isRef']
const markRaw: typeof import('vue')['markRaw']
const nextTick: typeof import('vue')['nextTick']
const onActivated: typeof import('vue')['onActivated']
const onBeforeMount: typeof import('vue')['onBeforeMount']
const onBeforeUnmount: typeof import('vue')['onBeforeUnmount']
const onBeforeUpdate: typeof import('vue')['onBeforeUpdate']
const onDeactivated: typeof import('vue')['onDeactivated']
const onErrorCaptured: typeof import('vue')['onErrorCaptured']
const onMounted: typeof import('vue')['onMounted']
const onRenderTracked: typeof import('vue')['onRenderTracked']
const onRenderTriggered: typeof import('vue')['onRenderTriggered']
const onScopeDispose: typeof import('vue')['onScopeDispose']
const onServerPrefetch: typeof import('vue')['onServerPrefetch']
const onUnmounted: typeof import('vue')['onUnmounted']
const onUpdated: typeof import('vue')['onUpdated']
const provide: typeof import('vue')['provide']
const reactive: typeof import('vue')['reactive']
const readonly: typeof import('vue')['readonly']
const ref: typeof import('vue')['ref']
const resolveComponent: typeof import('vue')['resolveComponent']
const shallowReactive: typeof import('vue')['shallowReactive']
const shallowReadonly: typeof import('vue')['shallowReadonly']
const shallowRef: typeof import('vue')['shallowRef']
const toRaw: typeof import('vue')['toRaw']
const toRef: typeof import('vue')['toRef']
const toRefs: typeof import('vue')['toRefs']
const triggerRef: typeof import('vue')['triggerRef']
const unref: typeof import('vue')['unref']
const useAttrs: typeof import('vue')['useAttrs']
const useCssModule: typeof import('vue')['useCssModule']
const useCssVars: typeof import('vue')['useCssVars']
const useSlots: typeof import('vue')['useSlots']
const watch: typeof import('vue')['watch']
const watchEffect: typeof import('vue')['watchEffect']
}
export {}
// Generated by 'unplugin-auto-import'
// We suggest you to commit this file into source control
declare global {
const computed: typeof import('vue')['computed']
const createApp: typeof import('vue')['createApp']
const customRef: typeof import('vue')['customRef']
const defineAsyncComponent: typeof import('vue')['defineAsyncComponent']
const defineComponent: typeof import('vue')['defineComponent']
const effectScope: typeof import('vue')['effectScope']
const EffectScope: typeof import('vue')['EffectScope']
const getCurrentInstance: typeof import('vue')['getCurrentInstance']
const getCurrentScope: typeof import('vue')['getCurrentScope']
const h: typeof import('vue')['h']
const inject: typeof import('vue')['inject']
const isReadonly: typeof import('vue')['isReadonly']
const isRef: typeof import('vue')['isRef']
const markRaw: typeof import('vue')['markRaw']
const nextTick: typeof import('vue')['nextTick']
const onActivated: typeof import('vue')['onActivated']
const onBeforeMount: typeof import('vue')['onBeforeMount']
const onBeforeUnmount: typeof import('vue')['onBeforeUnmount']
const onBeforeUpdate: typeof import('vue')['onBeforeUpdate']
const onDeactivated: typeof import('vue')['onDeactivated']
const onErrorCaptured: typeof import('vue')['onErrorCaptured']
const onMounted: typeof import('vue')['onMounted']
const onRenderTracked: typeof import('vue')['onRenderTracked']
const onRenderTriggered: typeof import('vue')['onRenderTriggered']
const onScopeDispose: typeof import('vue')['onScopeDispose']
const onServerPrefetch: typeof import('vue')['onServerPrefetch']
const onUnmounted: typeof import('vue')['onUnmounted']
const onUpdated: typeof import('vue')['onUpdated']
const provide: typeof import('vue')['provide']
const reactive: typeof import('vue')['reactive']
const readonly: typeof import('vue')['readonly']
const ref: typeof import('vue')['ref']
const resolveComponent: typeof import('vue')['resolveComponent']
const shallowReactive: typeof import('vue')['shallowReactive']
const shallowReadonly: typeof import('vue')['shallowReadonly']
const shallowRef: typeof import('vue')['shallowRef']
const toRaw: typeof import('vue')['toRaw']
const toRef: typeof import('vue')['toRef']
const toRefs: typeof import('vue')['toRefs']
const triggerRef: typeof import('vue')['triggerRef']
const unref: typeof import('vue')['unref']
const useAttrs: typeof import('vue')['useAttrs']
const useCssModule: typeof import('vue')['useCssModule']
const useCssVars: typeof import('vue')['useCssVars']
const useSlots: typeof import('vue')['useSlots']
const watch: typeof import('vue')['watch']
const watchEffect: typeof import('vue')['watchEffect']
}
export {}
// generated by unplugin-vue-components
// We suggest you to commit this file into source control
// Read more: https://github.com/vuejs/vue-next/pull/3399
import '@vue/runtime-core'
declare module '@vue/runtime-core' {
export interface GlobalComponents {
ElAside: typeof import('element-plus/es')['ElAside']
ElAvatar: typeof import('element-plus/es')['ElAvatar']
ElBreadcrumb: typeof import('element-plus/es')['ElBreadcrumb']
ElBreadcrumbItem: typeof import('element-plus/es')['ElBreadcrumbItem']
ElButton: typeof import('element-plus/es')['ElButton']
ElCard: typeof import('element-plus/es')['ElCard']
ElCol: typeof import('element-plus/es')['ElCol']
ElContainer: typeof import('element-plus/es')['ElContainer']
ElDatePicker: typeof import('element-plus/es')['ElDatePicker']
ElDialog: typeof import('element-plus/es')['ElDialog']
ElDivider: typeof import('element-plus/es')['ElDivider']
ElDrawer: typeof import('element-plus/es')['ElDrawer']
ElDropdown: typeof import('element-plus/es')['ElDropdown']
ElDropdownItem: typeof import('element-plus/es')['ElDropdownItem']
ElDropdownMenu: typeof import('element-plus/es')['ElDropdownMenu']
ElEmpty: typeof import('element-plus/es')['ElEmpty']
ElForm: typeof import('element-plus/es')['ElForm']
ElFormItem: typeof import('element-plus/es')['ElFormItem']
ElHeader: typeof import('element-plus/es')['ElHeader']
ElIcon: typeof import('element-plus/es')['ElIcon']
ElInput: typeof import('element-plus/es')['ElInput']
ElMain: typeof import('element-plus/es')['ElMain']
ElMenu: typeof import('element-plus/es')['ElMenu']
ElMenuItem: typeof import('element-plus/es')['ElMenuItem']
ElOption: typeof import('element-plus/es')['ElOption']
ElPopconfirm: typeof import('element-plus/es')['ElPopconfirm']
ElPagination: typeof import('element-plus/es')['ElPagination']
ElRadio: typeof import('element-plus/es')['ElRadio']
ElRadioGroup: typeof import('element-plus/es')['ElRadioGroup']
ElRow: typeof import('element-plus/es')['ElRow']
ElSelect: typeof import('element-plus/es')['ElSelect']
ElSubMenu: typeof import('element-plus/es')['ElSubMenu']
ElTable: typeof import('element-plus/es')['ElTable']
ElTableColumn: typeof import('element-plus/es')['ElTableColumn']
ElTabPane: typeof import('element-plus/es')['ElTabPane']
ElTabs: typeof import('element-plus/es')['ElTabs']
ElTree: typeof import('element-plus/es')['ElTree']
ElTreeSelect: typeof import('element-plus/es')['ElTreeSelect']
ElUpload: typeof import('element-plus/es')['ElUpload']
Form: typeof import('./components/Form.vue')['default']
RouterLink: typeof import('vue-router')['RouterLink']
RouterView: typeof import('vue-router')['RouterView']
}
}
export {}
// generated by unplugin-vue-components
// We suggest you to commit this file into source control
// Read more: https://github.com/vuejs/vue-next/pull/3399
import '@vue/runtime-core'
declare module '@vue/runtime-core' {
export interface GlobalComponents {
ElAside: typeof import('element-plus/es')['ElAside']
ElAvatar: typeof import('element-plus/es')['ElAvatar']
ElBreadcrumb: typeof import('element-plus/es')['ElBreadcrumb']
ElBreadcrumbItem: typeof import('element-plus/es')['ElBreadcrumbItem']
ElButton: typeof import('element-plus/es')['ElButton']
ElCard: typeof import('element-plus/es')['ElCard']
ElCol: typeof import('element-plus/es')['ElCol']
ElContainer: typeof import('element-plus/es')['ElContainer']
ElDatePicker: typeof import('element-plus/es')['ElDatePicker']
ElDialog: typeof import('element-plus/es')['ElDialog']
ElDivider: typeof import('element-plus/es')['ElDivider']
ElDrawer: typeof import('element-plus/es')['ElDrawer']
ElDropdown: typeof import('element-plus/es')['ElDropdown']
ElDropdownItem: typeof import('element-plus/es')['ElDropdownItem']
ElDropdownMenu: typeof import('element-plus/es')['ElDropdownMenu']
ElEmpty: typeof import('element-plus/es')['ElEmpty']
ElForm: typeof import('element-plus/es')['ElForm']
ElFormItem: typeof import('element-plus/es')['ElFormItem']
ElHeader: typeof import('element-plus/es')['ElHeader']
ElIcon: typeof import('element-plus/es')['ElIcon']
ElInput: typeof import('element-plus/es')['ElInput']
ElMain: typeof import('element-plus/es')['ElMain']
ElMenu: typeof import('element-plus/es')['ElMenu']
ElMenuItem: typeof import('element-plus/es')['ElMenuItem']
ElOption: typeof import('element-plus/es')['ElOption']
ElPopconfirm: typeof import('element-plus/es')['ElPopconfirm']
ElPagination: typeof import('element-plus/es')['ElPagination']
ElRadio: typeof import('element-plus/es')['ElRadio']
ElRadioGroup: typeof import('element-plus/es')['ElRadioGroup']
ElRow: typeof import('element-plus/es')['ElRow']
ElSelect: typeof import('element-plus/es')['ElSelect']
ElSubMenu: typeof import('element-plus/es')['ElSubMenu']
ElTable: typeof import('element-plus/es')['ElTable']
ElTableColumn: typeof import('element-plus/es')['ElTableColumn']
ElTabPane: typeof import('element-plus/es')['ElTabPane']
ElTabs: typeof import('element-plus/es')['ElTabs']
ElTree: typeof import('element-plus/es')['ElTree']
ElTreeSelect: typeof import('element-plus/es')['ElTreeSelect']
ElUpload: typeof import('element-plus/es')['ElUpload']
Form: typeof import('./components/Form.vue')['default']
RouterLink: typeof import('vue-router')['RouterLink']
RouterView: typeof import('vue-router')['RouterView']
}
}
export {}
<template>
<svg aria-hidden="true" class="svg-icon">
<use :xlink:href="symbolId" :fill="color" />
</svg>
</template>
<script setup lang="ts">
import { computed } from 'vue';
const props = defineProps({
prefix: {
type: String,
default: 'icon',
},
iconClass: {
type: String,
required: false
},
color: {
type: String,
default: ''
}
})
const symbolId = computed(() => `#${props.prefix}-${props.iconClass}`);
</script>
<style scoped>
.svg-icon {
width: 1em;
height: 1em;
vertical-align: -0.15em;
overflow: hidden;
fill: currentColor;
}
<template>
<svg aria-hidden="true" class="svg-icon">
<use :xlink:href="symbolId" :fill="color" />
</svg>
</template>
<script setup lang="ts">
import { computed } from 'vue';
const props = defineProps({
prefix: {
type: String,
default: 'icon',
},
iconClass: {
type: String,
required: false
},
color: {
type: String,
default: ''
}
})
const symbolId = computed(() => `#${props.prefix}-${props.iconClass}`);
</script>
<style scoped>
.svg-icon {
width: 1em;
height: 1em;
vertical-align: -0.15em;
overflow: hidden;
fill: currentColor;
}
</style>
\ No newline at end of file
<!-- 此处注意,使用 v-model="visible"绑定的形式
上线会报错
修改为如下方式
:model-value="visible"
-->
<template>
<el-dialog
:title='title'
:model-value="visible"
:before-close="onClose"
append-to-body
:width="width + 'px'"
>
<div class="container" :style="{height:height + 'px'}">
<slot name="content"></slot>
</div>
<template #footer>
<span class="dialog-footer">
<el-button type='danger' @click="onClose">取消</el-button>
<el-button type="primary" @click="onConfirm">确定</el-button>
</span>
</template>
</el-dialog>
</template>
<script setup lang='ts'>
import { ref, reactive } from 'vue'
const props = defineProps({
title: {//弹框标题
type: String,
default: '标题'
},
visible: { //控制弹框的展示和影藏
type: Boolean,
default: false
},
width: {
type: Number,
default: 600
},
height: {
type: Number,
default: 250
}
})
const emit = defineEmits(['onClose','onConfirm'])
//定义弹框的关闭
const onClose = () =>{
emit('onClose')
}
//定义弹框的确定
const onConfirm = () =>{
emit('onConfirm')
}
</script>
<style lang="scss" scope>
.container {
overflow-x: initial;
overflow-y: auto;
}
.el-dialog {
border-top-left-radius: 7px !important;
border-top-right-radius: 7px !important;
.el-dialog__header {
border-top-left-radius: 7px !important;
border-top-right-radius: 7px !important;
background-color: #1890ff !important;
.el-dialog__title {
color: #fff;
font-size: 16px;
font-weight: 600;
}
.el-dialog__close {
color: #fff;
}
}
.el-dialog__body {
padding: 10px;
}
.el-dialog__footer {
border-top: 1px solid #e8eaec !important;
padding: 10px;
}
}
<!-- 此处注意,使用 v-model="visible"绑定的形式
上线会报错
修改为如下方式
:model-value="visible"
-->
<template>
<el-dialog
:title='title'
:model-value="visible"
:before-close="onClose"
append-to-body
:width="width + 'px'"
>
<div class="container" :style="{height:height + 'px'}">
<slot name="content"></slot>
</div>
<template #footer>
<span class="dialog-footer">
<el-button type='danger' @click="onClose">取消</el-button>
<el-button type="primary" @click="onConfirm">确定</el-button>
</span>
</template>
</el-dialog>
</template>
<script setup lang='ts'>
import { ref, reactive } from 'vue'
const props = defineProps({
title: {//弹框标题
type: String,
default: '标题'
},
visible: { //控制弹框的展示和影藏
type: Boolean,
default: false
},
width: {
type: Number,
default: 600
},
height: {
type: Number,
default: 250
}
})
const emit = defineEmits(['onClose','onConfirm'])
//定义弹框的关闭
const onClose = () =>{
emit('onClose')
}
//定义弹框的确定
const onConfirm = () =>{
emit('onConfirm')
}
</script>
<style lang="scss" scope>
.container {
overflow-x: initial;
overflow-y: auto;
}
.el-dialog {
border-top-left-radius: 7px !important;
border-top-right-radius: 7px !important;
.el-dialog__header {
border-top-left-radius: 7px !important;
border-top-right-radius: 7px !important;
background-color: #1890ff !important;
.el-dialog__title {
color: #fff;
font-size: 16px;
font-weight: 600;
}
.el-dialog__close {
color: #fff;
}
}
.el-dialog__body {
padding: 10px;
}
.el-dialog__footer {
border-top: 1px solid #e8eaec !important;
padding: 10px;
}
}
</style>
\ No newline at end of file
<!-- 下拉选择筛选 -->
<template>
<el-dropdown ref="dropdown" :hide-on-click="false" trigger="contextmenu">
<span class="el-dropdown-link" @click="dropData.show">
{{title}}<el-icon class="el-icon--right"><arrow-down /></el-icon>
</span>
<template #dropdown>
<div class="space-between" style="width: 120px; padding: 10px 20px">
<el-checkbox
v-model="dropData.checkAll"
:indeterminate="dropData.isIndeterminate"
@change="dropData.handleCheckAllChange"
>全选</el-checkbox
>
<el-button type="primary" size="small" @click="dropData.confirm">确定</el-button>
</div>
<div style="height: 400px;width: 160px; overflow: auto">
<el-checkbox-group
v-model="dropData.checkedList"
@change="dropData.handleCheckedChange"
>
<el-dropdown-menu>
<el-dropdown-item v-for="(item, index) in list" :key="index">
<el-checkbox :label="item">
<div class="over1">
{{item[prop]}}
</div>
</el-checkbox>
</el-dropdown-item>
</el-dropdown-menu>
</el-checkbox-group>
</div>
</template>
</el-dropdown>
</template>
<script lang="ts">
import { reactive, ref } from 'vue'
export default {
name: 'DropDownCheck',
props: {
modValue: {
type: Array,
default: null,
required: true
},
title: {
type: String,
default: ''
},
list: {
type: Array,
default: null
},
prop: {
type: String,
default: ''
}
},
setup(props, context) {
const dropdown = ref()
const dropData = reactive({
checkAll: false,
isIndeterminate: true,
checkedList: props.modValue,
handleCheckAllChange: (val: boolean) => {
dropData.checkedList = val ? props.list : []
dropData.isIndeterminate = false
context.emit('update:modValue', dropData.checkedList)
},
handleCheckedChange: (value: string[]) => {
const checkedCount = value.length
dropData.checkAll = checkedCount === props.list.length
dropData.isIndeterminate = checkedCount > 0 && checkedCount < props.list.length
context.emit('update:modValue', dropData.checkedList)
},
show: () => {
dropdown.value.handleOpen()
},
confirm: () => {
dropdown.value.handleClose()
context.emit('onConfirm', props.title)
}
})
return {
dropData,
dropdown
}
}
}
</script>
<style>
</style>
<!-- 下拉选择筛选 -->
<template>
<el-dropdown ref="dropdown" :hide-on-click="false" trigger="contextmenu">
<span class="el-dropdown-link" @click="dropData.show">
{{title}}<el-icon class="el-icon--right"><arrow-down /></el-icon>
</span>
<template #dropdown>
<div class="space-between" style="width: 120px; padding: 10px 20px">
<el-checkbox
v-model="dropData.checkAll"
:indeterminate="dropData.isIndeterminate"
@change="dropData.handleCheckAllChange"
>全选</el-checkbox
>
<el-button type="primary" size="small" @click="dropData.confirm">确定</el-button>
</div>
<div style="height: 400px;width: 160px; overflow: auto">
<el-checkbox-group
v-model="dropData.checkedList"
@change="dropData.handleCheckedChange"
>
<el-dropdown-menu>
<el-dropdown-item v-for="(item, index) in list" :key="index">
<el-checkbox :label="item">
<div class="over1">
{{item[prop]}}
</div>
</el-checkbox>
</el-dropdown-item>
</el-dropdown-menu>
</el-checkbox-group>
</div>
</template>
</el-dropdown>
</template>
<script lang="ts">
import { reactive, ref } from 'vue'
export default {
name: 'DropDownCheck',
props: {
modValue: {
type: Array,
default: null,
required: true
},
title: {
type: String,
default: ''
},
list: {
type: Array,
default: null
},
prop: {
type: String,
default: ''
}
},
setup(props, context) {
const dropdown = ref()
const dropData = reactive({
checkAll: false,
isIndeterminate: true,
checkedList: props.modValue,
handleCheckAllChange: (val: boolean) => {
dropData.checkedList = val ? props.list : []
dropData.isIndeterminate = false
context.emit('update:modValue', dropData.checkedList)
},
handleCheckedChange: (value: string[]) => {
const checkedCount = value.length
dropData.checkAll = checkedCount === props.list.length
dropData.isIndeterminate = checkedCount > 0 && checkedCount < props.list.length
context.emit('update:modValue', dropData.checkedList)
},
show: () => {
dropdown.value.handleOpen()
},
confirm: () => {
dropdown.value.handleClose()
context.emit('onConfirm', props.title)
}
})
return {
dropData,
dropdown
}
}
}
</script>
<style>
</style>
<template>
<div>
<div ref="commonEchartRef" :style="{ height: height, width: width }"></div>
</div>
</template>
<script setup lang='ts'>
import { ref, onMounted, watchEffect } from 'vue'
import useEcharts from '@/hooks/useEcharts';
//接收父组件传递的参数
//withDefaults:设置默认值
const props = withDefaults(defineProps<{
width?: string,
height: string,
optios: any
}>(), {
width: '100%',
height: '360px'
})
//定义ref属性
const commonEchartRef = ref<HTMLElement>()
onMounted(() => {
//叹号:断定commonEchartRef.value存在
const { setOptions, resize } = useEcharts(commonEchartRef.value!)
watchEffect(() => {
setOptions(props.optios)
})
//自适应
window.addEventListener('resize', () => {
resize();
})
})
</script>
<style scoped lang='scss'>
<template>
<div>
<div ref="commonEchartRef" :style="{ height: height, width: width }"></div>
</div>
</template>
<script setup lang='ts'>
import { ref, onMounted, watchEffect } from 'vue'
import useEcharts from '@/hooks/useEcharts';
//接收父组件传递的参数
//withDefaults:设置默认值
const props = withDefaults(defineProps<{
width?: string,
height: string,
optios: any
}>(), {
width: '100%',
height: '360px'
})
//定义ref属性
const commonEchartRef = ref<HTMLElement>()
onMounted(() => {
//叹号:断定commonEchartRef.value存在
const { setOptions, resize } = useEcharts(commonEchartRef.value!)
watchEffect(() => {
setOptions(props.optios)
})
//自适应
window.addEventListener('resize', () => {
resize();
})
})
</script>
<style scoped lang='scss'>
</style>
\ No newline at end of file
<template>
<el-form>
<el-form-item></el-form-item>
</el-form>
</template>
<script setup lang="ts">
</script>
<template>
<el-form>
<el-form-item></el-form-item>
</el-form>
</template>
<script setup lang="ts">
</script>
<style scoped></style>
\ No newline at end of file
<template>
<el-dialog v-model="htmlData.htmlVisible" :title="htmlData.title">
<div v-html="htmlData.details" />
</el-dialog>
</template>
<script lang="ts" setup>
let props = defineProps({
htmlData: {
type: Object,
default: {
htmlVisible: {
type: Boolean,
default: false
},
title: {
type: String,
default: ''
},
details: {
type: String,
default: '<p></p>'
}
}
}
})
<template>
<el-dialog v-model="htmlData.htmlVisible" :title="htmlData.title">
<div v-html="htmlData.details" />
</el-dialog>
</template>
<script lang="ts" setup>
let props = defineProps({
htmlData: {
type: Object,
default: {
htmlVisible: {
type: Boolean,
default: false
},
title: {
type: String,
default: ''
},
details: {
type: String,
default: '<p></p>'
}
}
}
})
</script>
\ No newline at end of file
<template>
<el-table :data="tableData" style="width: 100%" height="400px">
<el-table-column
v-for="(item, index) in tableTitle"
:key="index"
:label="item.lable"
>
<template #default="scope">
<div style="display: flex; align-items: center">
<el-input v-model="scope.row[item.prop]" placeholder="Please input" />
</div>
</template>
</el-table-column>
<el-table-column label="操作" width="180px" align="center" fixed="right">
<template #default="scope">
<div style="display: flex; align-items: center; justify-content: center">
<slot v-bind="scope"></slot>
</div>
</template>
</el-table-column>
</el-table>
<div class="flex-center-h" style="margin-top: 10px">
<el-input v-model="addInput" placeholder="输入名称" style="width: 300px">
<template #prepend>
<el-icon><Plus /></el-icon>
</template>
</el-input>
<el-button type="primary" style="margin-left: 10px" @click="add">新增</el-button>
</div>
</template>
<script lang="ts">
import { ref } from 'vue'
export default {
name: 'InputTable',
props: {
tableTitle: {
type: Array,
default: null
},
tableData: {
type: Array,
default: null
}
},
emits: ['onAdd'],
setup(props, context) {
const addInput = ref('')
const add = ref( async () => {
await context.emit('onAdd', addInput.value)
addInput.value = ''
})
return {
add,
addInput
}
}
}
</script>
<template>
<el-table :data="tableData" style="width: 100%" height="400px">
<el-table-column
v-for="(item, index) in tableTitle"
:key="index"
:label="item.lable"
>
<template #default="scope">
<div style="display: flex; align-items: center">
<el-input v-model="scope.row[item.prop]" placeholder="Please input" />
</div>
</template>
</el-table-column>
<el-table-column label="操作" width="180px" align="center" fixed="right">
<template #default="scope">
<div style="display: flex; align-items: center; justify-content: center">
<slot v-bind="scope"></slot>
</div>
</template>
</el-table-column>
</el-table>
<div class="flex-center-h" style="margin-top: 10px">
<el-input v-model="addInput" placeholder="输入名称" style="width: 300px">
<template #prepend>
<el-icon><Plus /></el-icon>
</template>
</el-input>
<el-button type="primary" style="margin-left: 10px" @click="add">新增</el-button>
</div>
</template>
<script lang="ts">
import { ref } from 'vue'
export default {
name: 'InputTable',
props: {
tableTitle: {
type: Array,
default: null
},
tableData: {
type: Array,
default: null
}
},
emits: ['onAdd'],
setup(props, context) {
const addInput = ref('')
const add = ref( async () => {
await context.emit('onAdd', addInput.value)
addInput.value = ''
})
return {
add,
addInput
}
}
}
</script>
<template>
<div style="margin-top: 20px;">
<el-pagination v-model:currentPage="pageData.pageNo" v-model:page-size="pageData.pageSize"
:page-sizes="[10, 15, 20]" :small="pageData.small" :disabled="pageData.disabled"
:background="pageData.background" layout="->, total, sizes, prev, pager, next, jumper" :total="total"
:hide-on-single-page="pageData.hide" @size-change="handleSizeChange"
@current-change="handleCurrentChange" />
</div>
</template>
<script setup lang='ts'>
let props = defineProps({
pageData: {
type: Object,
default: {
pageNo: {
type: Number,
default: 1
},
pageSize: {
type: Number,
default: 10
},
small: {
type: Boolean,
default: false
},
disabled: {
type: Boolean,
default: false
},
background: {
type: String || Boolean,
default: '' || false
},
hide: {
type: Boolean,
default: false
}
}
},
total: {
type: Number,
default: 0
}
})
let emits = defineEmits(['sizeChange', 'currentChange'])
// 新条数
const handleSizeChange = (val: number) => {
emits('sizeChange', val)
}
// // 新页数
const handleCurrentChange = (val: number) => {
emits('currentChange', val)
}
</script>
<style lang = "scss" scoped>
<template>
<div style="margin-top: 20px;">
<el-pagination v-model:currentPage="pageData.pageNo" v-model:page-size="pageData.pageSize"
:page-sizes="[10, 15, 20]" :small="pageData.small" :disabled="pageData.disabled"
:background="pageData.background" layout="->, total, sizes, prev, pager, next, jumper" :total="total"
:hide-on-single-page="pageData.hide" @size-change="handleSizeChange"
@current-change="handleCurrentChange" />
</div>
</template>
<script setup lang='ts'>
let props = defineProps({
pageData: {
type: Object,
default: {
pageNo: {
type: Number,
default: 1
},
pageSize: {
type: Number,
default: 10
},
small: {
type: Boolean,
default: false
},
disabled: {
type: Boolean,
default: false
},
background: {
type: String || Boolean,
default: '' || false
},
hide: {
type: Boolean,
default: false
}
}
},
total: {
type: Number,
default: 0
}
})
let emits = defineEmits(['sizeChange', 'currentChange'])
// 新条数
const handleSizeChange = (val: number) => {
emits('sizeChange', val)
}
// // 新页数
const handleCurrentChange = (val: number) => {
emits('currentChange', val)
}
</script>
<style lang = "scss" scoped>
</style>
\ No newline at end of file
<template>
<div style="margin-top: 20px;">
<el-pagination v-model:currentPage="pageData.page" v-model:page-size="pageData.limit"
:page-sizes="[10, 15, 20]" :small="pageData.small" :disabled="pageData.disabled"
:background="pageData.background" layout="->, total, sizes, prev, pager, next, jumper" :total="total"
:hide-on-single-page="pageData.hide" @size-change="handleSizeChange"
@current-change="handleCurrentChange" />
</div>
</template>
<script setup lang='ts'>
let props = defineProps({
pageData: {
type: Object,
default: {
page: {
type: Number,
default: 1
},
limit: {
type: Number,
default: 10
},
small: {
type: Boolean,
default: false
},
disabled: {
type: Boolean,
default: false
},
background: {
type: String || Boolean,
default: '' || false
},
hide: {
type: Boolean,
default: false
}
}
},
total: {
type: Number,
default: 0
}
})
let emits = defineEmits(['sizeChange', 'currentChange'])
// 新条数
const handleSizeChange = (val: number) => {
emits('sizeChange', val)
}
// // 新页数
const handleCurrentChange = (val: number) => {
emits('currentChange', val)
}
</script>
<style lang = "scss" scoped>
<template>
<div style="margin-top: 20px;">
<el-pagination v-model:currentPage="pageData.page" v-model:page-size="pageData.limit"
:page-sizes="[10, 15, 20]" :small="pageData.small" :disabled="pageData.disabled"
:background="pageData.background" layout="->, total, sizes, prev, pager, next, jumper" :total="total"
:hide-on-single-page="pageData.hide" @size-change="handleSizeChange"
@current-change="handleCurrentChange" />
</div>
</template>
<script setup lang='ts'>
let props = defineProps({
pageData: {
type: Object,
default: {
page: {
type: Number,
default: 1
},
limit: {
type: Number,
default: 10
},
small: {
type: Boolean,
default: false
},
disabled: {
type: Boolean,
default: false
},
background: {
type: String || Boolean,
default: '' || false
},
hide: {
type: Boolean,
default: false
}
}
},
total: {
type: Number,
default: 0
}
})
let emits = defineEmits(['sizeChange', 'currentChange'])
// 新条数
const handleSizeChange = (val: number) => {
emits('sizeChange', val)
}
// // 新页数
const handleCurrentChange = (val: number) => {
emits('currentChange', val)
}
</script>
<style lang = "scss" scoped>
</style>
\ No newline at end of file
<template>
<div style="border: 1px solid #ccc">
<Toolbar style="border-bottom: 1px solid #ccc" :editor="editorRef" :defaultConfig="toolbarConfig" :mode="mode" />
<Editor style="height: 400px; overflow-y: hidden;" v-model="valueHtml" :defaultConfig="editorConfig" :mode="mode"
@onCreated="handleCreated" @onChange="handleChange" />
</div>
</template>
<script setup lang="ts">
import '@wangeditor/editor/dist/css/style.css' // 引入 css
import { onBeforeUnmount, shallowRef, computed } from 'vue'
import { Editor, Toolbar } from '@wangeditor/editor-for-vue'
import { createStorage } from "@/services/api/storage";
type InsertFnType = (url: string, alt?: string, href?: string) => void
type InsertFnTypeVideo = (url: string, poster?: string) => void
const props = defineProps({
valueHtml: {
type: String,
default: ''
}
})
const valueHtml = computed({
get: () => props.valueHtml,
set: (value) => emits("htmlChange", value),
})
const mode = shallowRef('mode')
const emits = defineEmits(['htmlChange'])
// 编辑器实例,必须用 shallowRef
const editorRef = shallowRef()
const toolbarConfig = {}
const editorConfig: any = { placeholder: '请输入内容...', MENU_CONF: {} }
// 自定义上传图片
editorConfig.MENU_CONF['uploadImage'] = {
customUpload(file: File, insertFn: InsertFnTypeVideo) {
console.log(file);
const formData = new FormData()
formData.append('file', file)
createStorage(formData).then((res: any) => {
if (res.code === 200) {
insertFn(res.data.url)
}
})
},
}
// // 插入图片
// editorConfig.MENU_CONF['insertImage'] = {
// onInsertedImage(imageNode: ImageElement | null) { // TS 语法
// if (imageNode == null) return
// const { src, alt, url, href } = imageNode
// console.log('inserted image', src, alt, url, href)
// },
// checkImage: customCheckImageFn, // 也支持 async 函数
// parseImageSrc: customParseImageSrc, // 也支持 async 函数
// }
// 自定义上传视频
editorConfig.MENU_CONF['uploadVideo'] = {
customUpload(file: File, insertFn: InsertFnType) {
console.log(file);
const formData = new FormData()
formData.append('file', file)
createStorage(formData).then((res: any) => {
if (res.code === 200) {
insertFn(res.data.url)
}
})
},
}
// 组件销毁时,也及时销毁编辑器
onBeforeUnmount(() => {
const editor = editorRef.value
if (editor == null) return
editor.destroy()
})
const handleCreated = (editor: any) => {
editorRef.value = editor // 记录 editor 实例,重要!
}
const handleChange = (editor: any) => {
const html = editor.getHtml()
emits('htmlChange', html)
}
<template>
<div style="border: 1px solid #ccc">
<Toolbar style="border-bottom: 1px solid #ccc" :editor="editorRef" :defaultConfig="toolbarConfig" :mode="mode" />
<Editor style="height: 400px; overflow-y: hidden;" v-model="valueHtml" :defaultConfig="editorConfig" :mode="mode"
@onCreated="handleCreated" @onChange="handleChange" />
</div>
</template>
<script setup lang="ts">
import '@wangeditor/editor/dist/css/style.css' // 引入 css
import { onBeforeUnmount, shallowRef, computed } from 'vue'
import { Editor, Toolbar } from '@wangeditor/editor-for-vue'
import { createStorage } from "@/services/api/storage";
type InsertFnType = (url: string, alt?: string, href?: string) => void
type InsertFnTypeVideo = (url: string, poster?: string) => void
const props = defineProps({
valueHtml: {
type: String,
default: ''
}
})
const valueHtml = computed({
get: () => props.valueHtml,
set: (value) => emits("htmlChange", value),
})
const mode = shallowRef('mode')
const emits = defineEmits(['htmlChange'])
// 编辑器实例,必须用 shallowRef
const editorRef = shallowRef()
const toolbarConfig = {}
const editorConfig: any = { placeholder: '请输入内容...', MENU_CONF: {} }
// 自定义上传图片
editorConfig.MENU_CONF['uploadImage'] = {
customUpload(file: File, insertFn: InsertFnTypeVideo) {
console.log(file);
const formData = new FormData()
formData.append('file', file)
createStorage(formData).then((res: any) => {
if (res.code === 200) {
insertFn(res.data.url)
}
})
},
}
// // 插入图片
// editorConfig.MENU_CONF['insertImage'] = {
// onInsertedImage(imageNode: ImageElement | null) { // TS 语法
// if (imageNode == null) return
// const { src, alt, url, href } = imageNode
// console.log('inserted image', src, alt, url, href)
// },
// checkImage: customCheckImageFn, // 也支持 async 函数
// parseImageSrc: customParseImageSrc, // 也支持 async 函数
// }
// 自定义上传视频
editorConfig.MENU_CONF['uploadVideo'] = {
customUpload(file: File, insertFn: InsertFnType) {
console.log(file);
const formData = new FormData()
formData.append('file', file)
createStorage(formData).then((res: any) => {
if (res.code === 200) {
insertFn(res.data.url)
}
})
},
}
// 组件销毁时,也及时销毁编辑器
onBeforeUnmount(() => {
const editor = editorRef.value
if (editor == null) return
editor.destroy()
})
const handleCreated = (editor: any) => {
editorRef.value = editor // 记录 editor 实例,重要!
}
const handleChange = (editor: any) => {
const html = editor.getHtml()
emits('htmlChange', html)
}
</script>
\ No newline at end of file
//自定义按钮权限指令
import { Directive } from 'vue'
import { store } from '@/store/index';
export const permission: Directive = {
mounted(el, binding) {
//value按钮上的权限
const { value } = binding;
//获取用户所有的权限
const permissions = store.getters['user/getPermissions'];
console.log('所有的权限')
console.log(permissions)
//判断传递进来的按钮权限,是否存在
if (value && value instanceof Array && value.length > 0) {
const permissionRoles = value;
//判断传递进来的按钮权限字段,是否存在当前用户的permissions
const hasPermission = permissions.some((role) => {
return permissionRoles.includes(role)
})
if (!hasPermission) { //没有权限时,影藏
el.style.display = 'none'
}
} else {
throw new Error('当前没有权限!')
}
}
//自定义按钮权限指令
import { Directive } from 'vue'
import { store } from '@/store/index';
export const permission: Directive = {
mounted(el, binding) {
//value按钮上的权限
const { value } = binding;
//获取用户所有的权限
const permissions = store.getters['user/getPermissions'];
console.log('所有的权限')
console.log(permissions)
//判断传递进来的按钮权限,是否存在
if (value && value instanceof Array && value.length > 0) {
const permissionRoles = value;
//判断传递进来的按钮权限字段,是否存在当前用户的permissions
const hasPermission = permissions.some((role) => {
return permissionRoles.includes(role)
})
if (!hasPermission) { //没有权限时,影藏
el.style.display = 'none'
}
} else {
throw new Error('当前没有权限!')
}
}
}
\ No newline at end of file
/**
*请求结果定义
*
* @enum {number}
*/
enum ResultEnum {
SUCCESS = 0,
ERROR = 1,
TIMEOUT = 401,
TYPE = 'success',
}
/**
*请求类型
*
* @enum {number}
*/
enum RequestEnum {
GET = 'GET',
POST = 'POST',
PUT = 'PUT',
DELETE = 'DELETE',
}
/**
*请求类型
*
* @enum {number}
*/
enum ContentTypeEnum {
// json
JSON = 'application/json;charset=UTF-8',
// form-data qs
FORM_URLENCODED = 'application/x-www-form-urlencoded;charset=UTF-8',
// form-data upload
FORM_DATA = 'multipart/form-data;charset=UTF-8',
}
/**
*请求配置
*
* @enum {number}
*/
enum ConfigEnum {
// TOKEN
TOKEN = 'X-Access-Token',
}
/**
*请求结果定义
*
* @enum {number}
*/
enum ResultEnum {
SUCCESS = 0,
ERROR = 1,
TIMEOUT = 401,
TYPE = 'success',
}
/**
*请求类型
*
* @enum {number}
*/
enum RequestEnum {
GET = 'GET',
POST = 'POST',
PUT = 'PUT',
DELETE = 'DELETE',
}
/**
*请求类型
*
* @enum {number}
*/
enum ContentTypeEnum {
// json
JSON = 'application/json;charset=UTF-8',
// form-data qs
FORM_URLENCODED = 'application/x-www-form-urlencoded;charset=UTF-8',
// form-data upload
FORM_DATA = 'multipart/form-data;charset=UTF-8',
}
/**
*请求配置
*
* @enum {number}
*/
enum ConfigEnum {
// TOKEN
TOKEN = 'X-Access-Token',
}
export { RequestEnum, ResultEnum, ContentTypeEnum, ConfigEnum }
\ No newline at end of file
import * as httpEnum from '@/enums/httpEnum'
import * as roleEnum from '@/enums/roleEnum'
import * as httpEnum from '@/enums/httpEnum'
import * as roleEnum from '@/enums/roleEnum'
export {httpEnum,roleEnum}
\ No newline at end of file
export enum RoleEnum {
// super admin
SUPER = 'super',
// tester
TEST = 'test',
export enum RoleEnum {
// super admin
SUPER = 'super',
// tester
TEST = 'test',
}
\ No newline at end of file
/// <reference types="vite/client" />
declare module '*.vue' {
import { DefineComponent } from 'vue'
// eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/ban-types
const component: DefineComponent<{}, {}, any>
export default component
}
// 环境变量智能提示配置
interface ImportMetaEnv {
VITE_APP_TITLE: string,
VITE_APP_PORT: string,
VITE_APP_BASE_URL: string
}
// 申明进度条依赖模块
/// <reference types="vite/client" />
declare module '*.vue' {
import { DefineComponent } from 'vue'
// eslint-disable-next-line @typescript-eslint/no-explicit-any, @typescript-eslint/ban-types
const component: DefineComponent<{}, {}, any>
export default component
}
// 环境变量智能提示配置
interface ImportMetaEnv {
VITE_APP_TITLE: string,
VITE_APP_PORT: string,
VITE_APP_BASE_URL: string
}
// 申明进度条依赖模块
declare module 'nprogress';
\ No newline at end of file
import CustomTable from '@/table/index.vue'
import CustomFormItem from '@/table/components/edit.vue'
import CustomInputTable from '@/components/inputTable/index.vue'
import { App } from 'vue'
const components: any = {
CustomTable,
CustomFormItem,
CustomInputTable
}
const GlobalComponents = (app: App) => {
Object.keys(components).forEach((key) => {
app.component(`${key}`, components[key])
})
}
export default GlobalComponents
import CustomTable from '@/table/index.vue'
import CustomFormItem from '@/table/components/edit.vue'
import CustomInputTable from '@/components/inputTable/index.vue'
import { App } from 'vue'
const components: any = {
CustomTable,
CustomFormItem,
CustomInputTable
}
const GlobalComponents = (app: App) => {
Object.keys(components).forEach((key) => {
app.component(`${key}`, components[key])
})
}
export default GlobalComponents
import {reactive} from 'vue'
import { DialogModel } from "@/type/BastType";
export default function useDialog(){
//定义弹框属性
const dialog = reactive<DialogModel>({
title:'',
visible:false,
width:630,
height:280
})
//展示
const onShow = () =>{
dialog.visible = true;
}
//关闭
const onClose = () =>{
dialog.visible =false;
}
//确定
const onConfirm = () =>{
dialog.visible = false;
}
return {
dialog,
onShow,
onClose,
onConfirm
}
import {reactive} from 'vue'
import { DialogModel } from "@/type/BastType";
export default function useDialog(){
//定义弹框属性
const dialog = reactive<DialogModel>({
title:'',
visible:false,
width:630,
height:280
})
//展示
const onShow = () =>{
dialog.visible = true;
}
//关闭
const onClose = () =>{
dialog.visible =false;
}
//确定
const onConfirm = () =>{
dialog.visible = false;
}
return {
dialog,
onShow,
onClose,
onConfirm
}
}
\ No newline at end of file
import * as echarts from 'echarts'
export default function useEcharts(el:HTMLElement){
//初始化echarts
const echartsInstance = echarts.init(el);
//设置options
const setOptions = (options:any) =>{
echartsInstance.setOption(options)
}
//自适应监听
const resize = () =>{
echartsInstance.resize()
}
return{
setOptions,
resize
}
import * as echarts from 'echarts'
export default function useEcharts(el:HTMLElement){
//初始化echarts
const echartsInstance = echarts.init(el);
//设置options
const setOptions = (options:any) =>{
echartsInstance.setOption(options)
}
//自适应监听
const resize = () =>{
echartsInstance.resize()
}
return{
setOptions,
resize
}
}
\ No newline at end of file
import { getCurrentInstance, ComponentInternalInstance } from "vue-demi";
export default function useInstance() {
// 获取当前组件实例,proxy生产和开发环境可以用,开发环境下可以使用ctx,生产环境不允许ctx,会引起获取不到实例
const { appContext, proxy } = getCurrentInstance() as ComponentInternalInstance
const global = appContext.config.globalProperties;
return {
proxy,
global
}
import { getCurrentInstance, ComponentInternalInstance } from "vue-demi";
export default function useInstance() {
// 获取当前组件实例,proxy生产和开发环境可以用,开发环境下可以使用ctx,生产环境不允许ctx,会引起获取不到实例
const { appContext, proxy } = getCurrentInstance() as ComponentInternalInstance
const global = appContext.config.globalProperties;
return {
proxy,
global
}
}
\ No newline at end of file
export default {
// 路由国际化
route: {
dashboard: 'Dashboard',
},
// 登录页面国际化
login: {
title: 'starbos-system',
username: 'Username',
password: 'Password'
},
export default {
// 路由国际化
route: {
dashboard: 'Dashboard',
},
// 登录页面国际化
login: {
title: 'starbos-system',
username: 'Username',
password: 'Password'
},
}
\ No newline at end of file
// 自定义国际化配置
import { createI18n } from 'vue-i18n'
import { cacheStorage } from '@/utils/cacheStorage'
// 本地语言包
import enLocale from './en'
import zhCnLocale from './zh-cn'
const messages = {
'zh-cn': {
...zhCnLocale
},
en: {
...enLocale
}
}
/**
* 获取当前系统使用语言字符串
*
* @returns zh-cn|en ...
*/
export const getLanguage = () => {
// 本地缓存获取
let language = cacheStorage.getLocalStorage('language')
if (language) {
return language
}
// 浏览器使用语言
language = navigator.language.toLowerCase()
const locales = Object.keys(messages)
for (const locale of locales) {
if (language.indexOf(locale) > -1) {
return locale
}
}
return 'zh-cn'
}
const i18n = createI18n({
locale: getLanguage(),
messages: messages
})
// 自定义国际化配置
import { createI18n } from 'vue-i18n'
import { cacheStorage } from '@/utils/cacheStorage'
// 本地语言包
import enLocale from './en'
import zhCnLocale from './zh-cn'
const messages = {
'zh-cn': {
...zhCnLocale
},
en: {
...enLocale
}
}
/**
* 获取当前系统使用语言字符串
*
* @returns zh-cn|en ...
*/
export const getLanguage = () => {
// 本地缓存获取
let language = cacheStorage.getLocalStorage('language')
if (language) {
return language
}
// 浏览器使用语言
language = navigator.language.toLowerCase()
const locales = Object.keys(messages)
for (const locale of locales) {
if (language.indexOf(locale) > -1) {
return locale
}
}
return 'zh-cn'
}
const i18n = createI18n({
locale: getLanguage(),
messages: messages
})
export default i18n
\ No newline at end of file
export default {
// 路由国际化
route: {
dashboard: '首页',
},
// 登录页面国际化
login: {
title: '永信达商城系统',
username: '账号',
password: '密码'
}
export default {
// 路由国际化
route: {
dashboard: '首页',
},
// 登录页面国际化
login: {
title: '永信达商城系统',
username: '账号',
password: '密码'
}
}
\ No newline at end of file
<template>
<Menu></Menu>
</template>
<script setup lang="ts">
import Menu from '@/components/Menu.vue'
</script>
<style lang="scss" scoped>
<template>
<Menu></Menu>
</template>
<script setup lang="ts">
import Menu from '@/components/Menu.vue'
</script>
<style lang="scss" scoped>
</style>
\ No newline at end of file
<template>
<div>
</div>
</template>
<script lang="ts">
export default {
setup () {
return {}
}
}
</script>
<style lang="scss" scoped>
<template>
<div>
</div>
</template>
<script lang="ts">
export default {
setup () {
return {}
}
}
</script>
<style lang="scss" scoped>
</style>
\ No newline at end of file
<template>
<el-breadcrumb separator="/">
<el-breadcrumb-item v-for="item in tabs">{{ item.meta.title }}</el-breadcrumb-item>
</el-breadcrumb>
</template>
<script setup lang='ts'>
import { ref, watch, Ref } from 'vue'
import { useRoute, RouteLocationMatched } from 'vue-router';
//定义面包屑导航数据
const tabs: Ref<RouteLocationMatched[]> = ref([]);
const route = useRoute();
const getBredcurm = () => {
//获取所有有meta和title
let mached = route.matched.filter(item => item.meta && item.meta.title);
// 判断第一个是否是首页,如果不是,构造一个
const first = mached[0];
if(first && first.path !== '/dashboard'){
//构造一个
mached = [{path: '/dashboard',meta:{title:'首页'}} as any].concat(mached);
}
//设置面包屑导航数据
tabs.value = mached;
}
getBredcurm();
//路由发生变化,重新获取面包屑导航数据
watch(() => route.path, () => getBredcurm());
</script>
<style scoped lang='scss'>
<template>
<el-breadcrumb separator="/">
<el-breadcrumb-item v-for="item in tabs">{{ item.meta.title }}</el-breadcrumb-item>
</el-breadcrumb>
</template>
<script setup lang='ts'>
import { ref, watch, Ref } from 'vue'
import { useRoute, RouteLocationMatched } from 'vue-router';
//定义面包屑导航数据
const tabs: Ref<RouteLocationMatched[]> = ref([]);
const route = useRoute();
const getBredcurm = () => {
//获取所有有meta和title
let mached = route.matched.filter(item => item.meta && item.meta.title);
// 判断第一个是否是首页,如果不是,构造一个
const first = mached[0];
if(first && first.path !== '/dashboard'){
//构造一个
mached = [{path: '/dashboard',meta:{title:'首页'}} as any].concat(mached);
}
//设置面包屑导航数据
tabs.value = mached;
}
getBredcurm();
//路由发生变化,重新获取面包屑导航数据
watch(() => route.path, () => getBredcurm());
</script>
<style scoped lang='scss'>
</style>
\ No newline at end of file
<template>
<el-icon @click="changeIcon" class="fa-icons">
<component :is="status ? Expand : Fold" />
</el-icon>
</template>
<script setup lang='ts'>
import {computed } from 'vue'
import { Fold, Expand } from '@element-plus/icons-vue'
import { useStore } from '@/store/index'
const store = useStore()
const status = computed(()=>{
// return store.getters['getCollapse']
return store.getters['menu/getCollapse']
})
//图标切换
const changeIcon = ()=>{
console.log(store)
// store.commit('setCollapse',!status.value)
store.commit('menu/setCollapse',!status.value)
}
</script>
<style scoped lang='scss'>
.fa-icons {
display: flex;
align-items: center;
font-size: 20px;
color: #303133;
cursor: pointer;
margin-right: 15px;
}
<template>
<el-icon @click="changeIcon" class="fa-icons">
<component :is="status ? Expand : Fold" />
</el-icon>
</template>
<script setup lang='ts'>
import {computed } from 'vue'
import { Fold, Expand } from '@element-plus/icons-vue'
import { useStore } from '@/store/index'
const store = useStore()
const status = computed(()=>{
// return store.getters['getCollapse']
return store.getters['menu/getCollapse']
})
//图标切换
const changeIcon = ()=>{
console.log(store)
// store.commit('setCollapse',!status.value)
store.commit('menu/setCollapse',!status.value)
}
</script>
<style scoped lang='scss'>
.fa-icons {
display: flex;
align-items: center;
font-size: 20px;
color: #303133;
cursor: pointer;
margin-right: 15px;
}
</style>
\ No newline at end of file
<template>
<div style='display: flex;align-items: center;'>
<Collapse></Collapse>
<BredCum></BredCum>
</div>
<UserInfo></UserInfo>
</template>
<script setup lang="ts">
import UserInfo from './UserInfo.vue';
import Collapse from './Collapse.vue';
import BredCum from './BredCum.vue';
<template>
<div style='display: flex;align-items: center;'>
<Collapse></Collapse>
<BredCum></BredCum>
</div>
<UserInfo></UserInfo>
</template>
<script setup lang="ts">
import UserInfo from './UserInfo.vue';
import Collapse from './Collapse.vue';
import BredCum from './BredCum.vue';
</script>
\ No newline at end of file
<template>
<el-dropdown placement="bottom-start">
<span class="el-dropdown-link">
<el-avatar :size="45" fit="contain" :src="avatar" />
</span>
<template #dropdown>
<el-dropdown-menu>
<!-- <el-dropdown-item @click="userInfo">个人信息</el-dropdown-item> -->
<el-dropdown-item @click="loginOut">退出登录</el-dropdown-item>
</el-dropdown-menu>
</template>
</el-dropdown>
</template>
<script setup lang='ts'>
import useInstance from '@/hooks/useInstance';
import { cleanSession, getToken } from '@/utils/auth';
import { store } from '@/store/index'
import { ref, reactive } from 'vue'
import router from '@/router';
const { global } = useInstance()
const avatar = store.state.user.userAvatar
//退出登录
const loginOut = async () => {
window.location.href = "/#/login";
cleanSession();
// let confirm = await global.$myconfirm('确定退出登录吗?')
// if (confirm) {
// let parm = {
// token: getToken()
// }
// let res = await loginOutApi(parm)
// if (res && res.code == 200) {
// //跳到登录
// window.location.href = "/login";
// //清空session
// cleanSession();
// }
// }
}
//还原数据
// const restore = async () => {
// let confirm = await global.$myconfirm('确定还原数据吗?')
// if (confirm) {
// let res = await restoreApi();
// if (res && res.code == 200) {
// //信息提示
// global.$message({ message: res.message, type: 'success' })
// }
// }
// }
//跳转个人信息
const userInfo = async () => {
router.push({ path: '/user' })
}
</script>
<style scoped lang='scss'>
<template>
<el-dropdown placement="bottom-start">
<span class="el-dropdown-link">
<el-avatar :size="45" fit="contain" :src="avatar" />
</span>
<template #dropdown>
<el-dropdown-menu>
<!-- <el-dropdown-item @click="userInfo">个人信息</el-dropdown-item> -->
<el-dropdown-item @click="loginOut">退出登录</el-dropdown-item>
</el-dropdown-menu>
</template>
</el-dropdown>
</template>
<script setup lang='ts'>
import useInstance from '@/hooks/useInstance';
import { cleanSession, getToken } from '@/utils/auth';
import { store } from '@/store/index'
import { ref, reactive } from 'vue'
import router from '@/router';
const { global } = useInstance()
const avatar = store.state.user.userAvatar
//退出登录
const loginOut = async () => {
window.location.href = "/#/login";
cleanSession();
// let confirm = await global.$myconfirm('确定退出登录吗?')
// if (confirm) {
// let parm = {
// token: getToken()
// }
// let res = await loginOutApi(parm)
// if (res && res.code == 200) {
// //跳到登录
// window.location.href = "/login";
// //清空session
// cleanSession();
// }
// }
}
//还原数据
// const restore = async () => {
// let confirm = await global.$myconfirm('确定还原数据吗?')
// if (confirm) {
// let res = await restoreApi();
// if (res && res.code == 200) {
// //信息提示
// global.$message({ message: res.message, type: 'success' })
// }
// }
// }
//跳转个人信息
const userInfo = async () => {
router.push({ path: '/user' })
}
</script>
<style scoped lang='scss'>
</style>
\ No newline at end of file
<template>
<el-container class="layout">
<el-aside width="auto" class="asside">
<MenuBar></MenuBar>
</el-aside>
<el-container>
<el-header class="header">
<Header></Header>
</el-header>
<el-main class="main">
<Tabs style="padding-top: 10px"></Tabs>
<keep-alive>
<router-view></router-view>
</keep-alive>
</el-main>
</el-container>
</el-container>
</template>
<script setup lang="ts">
import Header from '@/layout/header/Header.vue';
import MenuBar from '@/layout/menu/MenuBar.vue';
import Tabs from './tabs/Tabs.vue';
</script>
<style lang="scss" scoped>
.layout {
height: 100%;
.asside {
background-color: #304156;
}
.header {
height: 50px;
border-bottom: 1px solid #e5e5e5;
// background-color: blueviolet;
display: flex;
align-items: center;
justify-content: space-between;
}
.main {
padding-top: 0px;
padding-left: 0px !important;
padding-right: 0px !important;
}
}
<template>
<el-container class="layout">
<el-aside width="auto" class="asside">
<MenuBar></MenuBar>
</el-aside>
<el-container>
<el-header class="header">
<Header></Header>
</el-header>
<el-main class="main">
<Tabs style="padding-top: 10px"></Tabs>
<keep-alive>
<router-view></router-view>
</keep-alive>
</el-main>
</el-container>
</el-container>
</template>
<script setup lang="ts">
import Header from '@/layout/header/Header.vue';
import MenuBar from '@/layout/menu/MenuBar.vue';
import Tabs from './tabs/Tabs.vue';
</script>
<style lang="scss" scoped>
.layout {
height: 100%;
.asside {
background-color: #304156;
}
.header {
height: 50px;
border-bottom: 1px solid #e5e5e5;
// background-color: blueviolet;
display: flex;
align-items: center;
justify-content: space-between;
}
.main {
padding-top: 0px;
padding-left: 0px !important;
padding-right: 0px !important;
}
}
</style>
\ No newline at end of file
<template>
<MenuLogo class="layout-logo" v-if="!isCollapse"></MenuLogo>
<el-menu :default-active="activeIdex" class="el-menu-vertical-startbos" :collapse="isCollapse" @open="handleOpen"
@close="handleClose" background-color="#304156" router>
<MenuItem :menuList="menuList">
</MenuItem>
</el-menu>
</template>
<script setup lang="ts">
import { reactive, computed } from 'vue'
import { useRoute } from 'vue-router'
import { useStore } from '@/store/index'
import MenuItem from './MenuItem.vue'
import MenuLogo from '@/layout/menu/MenuLogo.vue'
// setup语法糖中 定义的数据和方法,直接可以在模板中使用,无需要 return
const store = useStore()
// store.commit('menu/setCollapse')
// store.dispatch('menu/getMenuList')
// store.getters['user/getPermissions']
//当前路由
const route = useRoute();
const activeIdex = computed(() => {
const { path } = route;
return path;
})
//菜单数据
const menuList = computed(() => {
return store.getters['menu/getMenuList']
})
const isCollapse = computed(() => {
return store.getters['menu/getCollapse']
})
const handleOpen = (key: string, keyPath: string[]) => {
// console.log(key, keyPath)
}
const handleClose = (key: string, keyPath: string[]) => {
// console.log(key, keyPath)
}
</script>
<style scoped>
@keyframes logoAnimation {
0% {
transform: scale(0);
}
50% {
transform: scale(1);
}
100% {
transform: scale(1);
}
}
.layout-logo {
animation: logoAnimation 1s ease-out;
}
.el-menu-vertical-startbos:not(.el-menu--collapse) {
width: 230px;
min-height: 400px;
}
.el-menu {
border-right: none;
}
:deep(.el-sub-menu .el-sub-menu__title) {
color: #f4f4f5 !important;
}
:deep(.el-menu .el-menu-item) {
color: #bfcbd9;
}
/* 菜单点中文字的颜色 */
:deep(.el-menu-item.is-active) {
color: #409eff !important;
}
/* 当前打开菜单的所有子菜单颜色 */
:deep(.is-opened .el-menu-item) {
background-color: #1f2d3d !important;
}
/* 鼠标移动菜单的颜色 */
:deep(.el-menu-item:hover) {
background-color: #001528 !important;
}
<template>
<MenuLogo class="layout-logo" v-if="!isCollapse"></MenuLogo>
<el-menu :default-active="activeIdex" class="el-menu-vertical-startbos" :collapse="isCollapse" @open="handleOpen"
@close="handleClose" background-color="#304156" router>
<MenuItem :menuList="menuList">
</MenuItem>
</el-menu>
</template>
<script setup lang="ts">
import { reactive, computed } from 'vue'
import { useRoute } from 'vue-router'
import { useStore } from '@/store/index'
import MenuItem from './MenuItem.vue'
import MenuLogo from '@/layout/menu/MenuLogo.vue'
// setup语法糖中 定义的数据和方法,直接可以在模板中使用,无需要 return
const store = useStore()
// store.commit('menu/setCollapse')
// store.dispatch('menu/getMenuList')
// store.getters['user/getPermissions']
//当前路由
const route = useRoute();
const activeIdex = computed(() => {
const { path } = route;
return path;
})
//菜单数据
const menuList = computed(() => {
return store.getters['menu/getMenuList']
})
const isCollapse = computed(() => {
return store.getters['menu/getCollapse']
})
const handleOpen = (key: string, keyPath: string[]) => {
// console.log(key, keyPath)
}
const handleClose = (key: string, keyPath: string[]) => {
// console.log(key, keyPath)
}
</script>
<style scoped>
@keyframes logoAnimation {
0% {
transform: scale(0);
}
50% {
transform: scale(1);
}
100% {
transform: scale(1);
}
}
.layout-logo {
animation: logoAnimation 1s ease-out;
}
.el-menu-vertical-startbos:not(.el-menu--collapse) {
width: 230px;
min-height: 400px;
}
.el-menu {
border-right: none;
}
:deep(.el-sub-menu .el-sub-menu__title) {
color: #f4f4f5 !important;
}
:deep(.el-menu .el-menu-item) {
color: #bfcbd9;
}
/* 菜单点中文字的颜色 */
:deep(.el-menu-item.is-active) {
color: #409eff !important;
}
/* 当前打开菜单的所有子菜单颜色 */
:deep(.is-opened .el-menu-item) {
background-color: #1f2d3d !important;
}
/* 鼠标移动菜单的颜色 */
:deep(.el-menu-item:hover) {
background-color: #001528 !important;
}
</style>
\ No newline at end of file
<template>
<template v-for="menu in menuList" :key="menu.path">
<el-sub-menu v-if="menu.children && menu.children.length > 0" :index="menu.path">
<template #title>
<el-icon>
<component class="icons" :is="menu.meta.icon" />
</el-icon>
<span>{{ menu.meta.title }}</span>
</template>
<menu-item :menuList="menu.children"></menu-item>
</el-sub-menu>
<el-menu-item style="color: #f4f4f5" v-else :index="menu.path">
<i v-if="menu.meta.icon && menu.meta.icon.includes('el-icon')" :class="menu.meta.icon"></i>
<el-icon v-if="menu.meta.icon">
<component class="icons" :is="menu.meta.icon" />
</el-icon>
<template #title>{{ menu.meta.title }}</template>
</el-menu-item>
</template>
</template>
<script setup lang="ts">
defineProps(['menuList'])
</script>
<style scoped>
.icons {
width: 24px;
height: 18px;
margin-right: 5px;
}
<template>
<template v-for="menu in menuList" :key="menu.path">
<el-sub-menu v-if="menu.children && menu.children.length > 0" :index="menu.path">
<template #title>
<el-icon>
<component class="icons" :is="menu.meta.icon" />
</el-icon>
<span>{{ menu.meta.title }}</span>
</template>
<menu-item :menuList="menu.children"></menu-item>
</el-sub-menu>
<el-menu-item style="color: #f4f4f5" v-else :index="menu.path">
<i v-if="menu.meta.icon && menu.meta.icon.includes('el-icon')" :class="menu.meta.icon"></i>
<el-icon v-if="menu.meta.icon">
<component class="icons" :is="menu.meta.icon" />
</el-icon>
<template #title>{{ menu.meta.title }}</template>
</el-menu-item>
</template>
</template>
<script setup lang="ts">
defineProps(['menuList'])
</script>
<style scoped>
.icons {
width: 24px;
height: 18px;
margin-right: 5px;
}
</style>
\ No newline at end of file
<template>
<div class="logo">
<img src="@/assets/logo.png" alt="logo" />
<span class="title">永信达商城</span>
</div>
</template>
<script setup lang="ts">
</script>
<style lang="scss" scoped>
.logo {
background-color: #2b2f3a;
height: 50px;
border: none;
line-height: 50px;
display: flex;
align-items: center;
padding-left: 15px;
color: #fff;
img {
width: 64px;
height: 32px;
margin-right: 12px;
}
span {
font-weight: 600;
line-height: 50px;
font-size: 16px;
font-family: Avenir, Helvetica Neue, Arial, Helvetica, sans-serif;
vertical-align: middle;
}
}
<template>
<div class="logo">
<img src="@/assets/logo.png" alt="logo" />
<span class="title">永信达商城</span>
</div>
</template>
<script setup lang="ts">
</script>
<style lang="scss" scoped>
.logo {
background-color: #2b2f3a;
height: 50px;
border: none;
line-height: 50px;
display: flex;
align-items: center;
padding-left: 15px;
color: #fff;
img {
width: 64px;
height: 32px;
margin-right: 12px;
}
span {
font-weight: 600;
line-height: 50px;
font-size: 16px;
font-family: Avenir, Helvetica Neue, Arial, Helvetica, sans-serif;
vertical-align: middle;
}
}
</style>
\ No newline at end of file
<template>
<el-tabs v-model="activeTab" @tab-click="clickBtn" type="card" closable @tab-remove="removeTab">
<el-tab-pane v-for="item in tabsList" :key="item.path" :label="item.title" :name="item.path"></el-tab-pane>
</el-tabs>
</template>
<script setup lang='ts'>
import { ref, computed, watch, onMounted } from 'vue'
import { useStore } from '@/store';
import { useRoute, useRouter } from 'vue-router';
import { ITabe } from '@/store/types';
const route = useRoute();
const router = useRouter();
const store = useStore();
//获取tabs数据
const tabsList = computed(() => {
// return store.getters['getTabs']
return store.getters['tabs/getTabs']
})
//当前激活的选项卡,他跟当前激活的路由是一样的;
const activeTab = ref('');
const setActiveTab = () => {
activeTab.value = route.path;
}
//删除选项卡
const removeTab = (targetName: string) => {
if (targetName === '/dashboard') return;
//选项卡数据列表
const tabs = tabsList.value;
//当前激活的选项卡
let activeName = activeTab.value;
if (activeName === targetName) {
tabs.forEach((tab: ITabe, index: number) => {
if (tab.path === targetName) {
const nextTab = tabs[index + 1] || tabs[index - 1]
if (nextTab) {
activeName = nextTab.path
}
}
})
}
//重新设置当前激活的选项卡
activeTab.value = activeName
//重新设置选项卡数据
store.state.tabs.tabsList = tabs.filter((tab: ITabe) => tab.path !== targetName)
//跳转路由
router.push({ path: activeName })
}
//添加选项卡
const addTab = () => {
//从当前路由获取path和title
const { path, meta } = route;
//通过vuex设置
const tab: ITabe = {
path: path,
title: meta.title as string
}
// store.commit('addTabe', tab);
store.commit('tabs/addTabe', tab)
}
//监听路由的变化
watch(() => route.path, () => {
//设置激活的选项卡
setActiveTab();
//把当前路由添加到选项卡数据
addTab();
})
//解决刷新数据丢失的问题
const beforeRefresh = () => {
if (route.path != '/login') {
window.addEventListener('beforeunload', () => {
sessionStorage.setItem('tabsView', JSON.stringify(tabsList.value))
})
let tabSesson = sessionStorage.getItem("tabsView");
if (tabSesson) {
let oldTabs = JSON.parse(tabSesson);
if (oldTabs.length > 0) {
store.state.tabs.tabsList = oldTabs;
}
}
}
}
onMounted(() => {
//解决刷新选项卡数据丢失的问题
beforeRefresh();
//设置激活的选项卡
setActiveTab();
//把当前路由添加到选项卡数据
addTab();
})
//选项卡点击事件
const clickBtn = (tab: any) => {
const { props } = tab;
//跳转路由
router.push({ path: props.name })
}
</script>
<style scoped lang='scss'>
:deep(.el-tabs__header) {
margin: 0px;
}
:deep(.el-tabs__item) {
height: 26px !important;
line-height: 26px !important;
text-align: center !important;
border: 1px solid #d8dce5 !important;
margin: 0px 3px !important;
color: #495060;
font-size: 12px !important;
padding: 0px 10px !important;
}
:deep(.el-tabs__nav) {
border: none !important;
}
:deep(.is-active) {
border-bottom: 1px solid transparent !important;
border: 1px solid #42b983 !important;
background-color: #42b983 !important;
color: #fff !important;
}
:deep(.el-tabs__item:hover) {
color: #495060 !important;
}
:deep(.is-active:hover) {
color: #fff !important;
}
<template>
<el-tabs v-model="activeTab" @tab-click="clickBtn" type="card" closable @tab-remove="removeTab">
<el-tab-pane v-for="item in tabsList" :key="item.path" :label="item.title" :name="item.path"></el-tab-pane>
</el-tabs>
</template>
<script setup lang='ts'>
import { ref, computed, watch, onMounted } from 'vue'
import { useStore } from '@/store';
import { useRoute, useRouter } from 'vue-router';
import { ITabe } from '@/store/types';
const route = useRoute();
const router = useRouter();
const store = useStore();
//获取tabs数据
const tabsList = computed(() => {
// return store.getters['getTabs']
return store.getters['tabs/getTabs']
})
//当前激活的选项卡,他跟当前激活的路由是一样的;
const activeTab = ref('');
const setActiveTab = () => {
activeTab.value = route.path;
}
//删除选项卡
const removeTab = (targetName: string) => {
if (targetName === '/dashboard') return;
//选项卡数据列表
const tabs = tabsList.value;
//当前激活的选项卡
let activeName = activeTab.value;
if (activeName === targetName) {
tabs.forEach((tab: ITabe, index: number) => {
if (tab.path === targetName) {
const nextTab = tabs[index + 1] || tabs[index - 1]
if (nextTab) {
activeName = nextTab.path
}
}
})
}
//重新设置当前激活的选项卡
activeTab.value = activeName
//重新设置选项卡数据
store.state.tabs.tabsList = tabs.filter((tab: ITabe) => tab.path !== targetName)
//跳转路由
router.push({ path: activeName })
}
//添加选项卡
const addTab = () => {
//从当前路由获取path和title
const { path, meta } = route;
//通过vuex设置
const tab: ITabe = {
path: path,
title: meta.title as string
}
// store.commit('addTabe', tab);
store.commit('tabs/addTabe', tab)
}
//监听路由的变化
watch(() => route.path, () => {
//设置激活的选项卡
setActiveTab();
//把当前路由添加到选项卡数据
addTab();
})
//解决刷新数据丢失的问题
const beforeRefresh = () => {
if (route.path != '/login') {
window.addEventListener('beforeunload', () => {
sessionStorage.setItem('tabsView', JSON.stringify(tabsList.value))
})
let tabSesson = sessionStorage.getItem("tabsView");
if (tabSesson) {
let oldTabs = JSON.parse(tabSesson);
if (oldTabs.length > 0) {
store.state.tabs.tabsList = oldTabs;
}
}
}
}
onMounted(() => {
//解决刷新选项卡数据丢失的问题
beforeRefresh();
//设置激活的选项卡
setActiveTab();
//把当前路由添加到选项卡数据
addTab();
})
//选项卡点击事件
const clickBtn = (tab: any) => {
const { props } = tab;
//跳转路由
router.push({ path: props.name })
}
</script>
<style scoped lang='scss'>
:deep(.el-tabs__header) {
margin: 0px;
}
:deep(.el-tabs__item) {
height: 26px !important;
line-height: 26px !important;
text-align: center !important;
border: 1px solid #d8dce5 !important;
margin: 0px 3px !important;
color: #495060;
font-size: 12px !important;
padding: 0px 10px !important;
}
:deep(.el-tabs__nav) {
border: none !important;
}
:deep(.is-active) {
border-bottom: 1px solid transparent !important;
border: 1px solid #42b983 !important;
background-color: #42b983 !important;
color: #fff !important;
}
:deep(.el-tabs__item:hover) {
color: #495060 !important;
}
:deep(.is-active:hover) {
color: #fff !important;
}
</style>
\ No newline at end of file
import { createApp } from 'vue'
import App from './App.vue'
// 注入路由
import router from './router';
import { start, close } from '@/utils/nprogress'
import { getToken, cleanSession, getMenu } from '@/utils/auth'
import { elNotificaton } from '@/utils/customemessage'
//import '@/mock' //生产时请注释掉,
import { filterAsyncRoutes } from '@/store/modules/menu'
import { store, key } from '@/store'
import * as ElIconModules from '@element-plus/icons-vue'
//全局添加组件
// 全局注入ui组件库,按需引入迁移到vite.config.ts配置
import ElementPlus from 'element-plus'
import 'element-plus/dist/index.css'
import { CopyData, resetForm } from './utils/common';
import LqConfirm from './utils/LqConfirm'
//引入按钮权限
import { permission } from './directives/permission'
//引入socket
import SocketService from '@/socket/main'
// SocketService.Instance.connect();
// 国际化
import i18n from "@/lang/index";
//注册svg图标脚本
import 'virtual:svg-icons-register'
import GlobalComponents from './global-components';
const whiteList = ['/login']; //白名单
// 设置路由导航守卫
router.beforeEach(async (to, from, next) => {
//设置标题
if (to.meta.title) {
document.title = String(to.meta.title)
}
else {
document.title = import.meta.env.VITE_APP_TITLE;
}
//获取token
let token = getToken();
if (token) {
if (to.path === '/login' || to.path === '/') {
next({ path: '/' })
} else {
if (store.state.menu.menuList.length == 1) {
try {
await store.dispatch('user/getInfo')
await store.dispatch('menu/getMenuList', router)
next({ ...to, replace: true })
} catch (error) {
cleanSession();
next({ path: '/login' })
}
} else {
next()
}
}
} else {
//判断是否在白名单中
if (whiteList.indexOf(to.path) !== -1 || to.path === '/import/lic') { //存在白名单,放行
next();
} else { //不存在,登录
next({ path: '/login' })
}
}
})
const app = createApp(App);
Object.keys(ElIconModules).forEach((key) => {
app.component(key, ElIconModules[key as keyof typeof ElIconModules])
});
app.use(store, key)
app.use(router);
app.use(ElementPlus)
app.use(i18n);
app.mount('#app');
//注册
app.directive('permission', permission);
GlobalComponents(app)
//清空表单
app.config.globalProperties.$resetForm = resetForm;
//对象复制
app.config.globalProperties.$objCoppy = CopyData;
//确定弹框
app.config.globalProperties.$lqconfirm = LqConfirm;
// 路由加载结束后执行
router.afterEach(() => {
close()
})
import { createApp } from 'vue'
import App from './App.vue'
// 注入路由
import router from './router';
import { start, close } from '@/utils/nprogress'
import { getToken, cleanSession, getMenu } from '@/utils/auth'
import { elNotificaton } from '@/utils/customemessage'
//import '@/mock' //生产时请注释掉,
import { filterAsyncRoutes } from '@/store/modules/menu'
import { store, key } from '@/store'
import * as ElIconModules from '@element-plus/icons-vue'
//全局添加组件
// 全局注入ui组件库,按需引入迁移到vite.config.ts配置
import ElementPlus from 'element-plus'
import 'element-plus/dist/index.css'
import { CopyData, resetForm } from './utils/common';
import LqConfirm from './utils/LqConfirm'
//引入按钮权限
import { permission } from './directives/permission'
//引入socket
import SocketService from '@/socket/main'
// SocketService.Instance.connect();
// 国际化
import i18n from "@/lang/index";
//注册svg图标脚本
import 'virtual:svg-icons-register'
import GlobalComponents from './global-components';
const whiteList = ['/login']; //白名单
// 设置路由导航守卫
router.beforeEach(async (to, from, next) => {
//设置标题
if (to.meta.title) {
document.title = String(to.meta.title)
}
else {
document.title = import.meta.env.VITE_APP_TITLE;
}
//获取token
let token = getToken();
if (token) {
if (to.path === '/login' || to.path === '/') {
next({ path: '/' })
} else {
if (store.state.menu.menuList.length == 1) {
try {
await store.dispatch('user/getInfo')
await store.dispatch('menu/getMenuList', router)
next({ ...to, replace: true })
} catch (error) {
cleanSession();
next({ path: '/login' })
}
} else {
next()
}
}
} else {
//判断是否在白名单中
if (whiteList.indexOf(to.path) !== -1 || to.path === '/import/lic') { //存在白名单,放行
next();
} else { //不存在,登录
next({ path: '/login' })
}
}
})
const app = createApp(App);
Object.keys(ElIconModules).forEach((key) => {
app.component(key, ElIconModules[key as keyof typeof ElIconModules])
});
app.use(store, key)
app.use(router);
app.use(ElementPlus)
app.use(i18n);
app.mount('#app');
//注册
app.directive('permission', permission);
GlobalComponents(app)
//清空表单
app.config.globalProperties.$resetForm = resetForm;
//对象复制
app.config.globalProperties.$objCoppy = CopyData;
//确定弹框
app.config.globalProperties.$lqconfirm = LqConfirm;
// 路由加载结束后执行
router.afterEach(() => {
close()
})
import { createRouter, createWebHashHistory, RouteRecordRaw } from "vue-router";
import Layout from "@/layout/index.vue";
const routes: Array<RouteRecordRaw> = [
{
path: "/",
component: Layout,
redirect: "/dashboard",
children: [
{
path: "/dashboard",
component: () => import("@/views/dashboard/index.vue"),
name: "dashboard",
meta: {
title: "首页",
icon: "#icondashboard",
},
},
],
},
{
path: "/commodity",
component: Layout,
children: [
{
path: "/commodity/edit",
component: () => import("@/views/commodityManage/edit.vue"),
name: "edit",
meta: {
title: "商品编辑",
icon: "",
},
},
],
},
{
path: "/login",
name: "login",
component: () => import("@/views/login/login.vue"),
},
{
path: "/import/lic",
name: "importLic",
component: () => import("@/views/login/importLic.vue"),
},
{
path: "/404",
name: "404",
component: () => import("@/views/exception/404.vue"),
},
// {
// path: '/:catchAll(.*)',
// redirect: '/404'
// }
];
export const asyncRoutes: Array<RouteRecordRaw> = [];
const router = createRouter({
history: createWebHashHistory(),
// routes = routes:routes
routes,
});
export function addRoutes(routes: any) {
router.addRoute(routes);
}
export default router;
import { createRouter, createWebHashHistory, RouteRecordRaw } from "vue-router";
import Layout from "@/layout/index.vue";
const routes: Array<RouteRecordRaw> = [
{
path: "/",
component: Layout,
redirect: "/dashboard",
children: [
{
path: "/dashboard",
component: () => import("@/views/dashboard/index.vue"),
name: "dashboard",
meta: {
title: "首页",
icon: "#icondashboard",
},
},
],
},
{
path: "/commodity",
component: Layout,
children: [
{
path: "/commodity/edit",
component: () => import("@/views/commodityManage/edit.vue"),
name: "edit",
meta: {
title: "商品编辑",
icon: "",
},
},
],
},
{
path: "/login",
name: "login",
component: () => import("@/views/login/login.vue"),
},
{
path: "/import/lic",
name: "importLic",
component: () => import("@/views/login/importLic.vue"),
},
{
path: "/404",
name: "404",
component: () => import("@/views/exception/404.vue"),
},
// {
// path: '/:catchAll(.*)',
// redirect: '/404'
// }
];
export const asyncRoutes: Array<RouteRecordRaw> = [];
const router = createRouter({
history: createWebHashHistory(),
// routes = routes:routes
routes,
});
export function addRoutes(routes: any) {
router.addRoute(routes);
}
export default router;
import { request } from '../config'
export function listGoods(params: any) {
return request({
url: '/goods/list',
method: 'get',
params
})
}
export function deleteGoods(data: any) {
return request({
url: '/goods/delete',
method: 'post',
data
})
}
export function publishGoods(data: any) {
return request({
url: '/goods/create',
method: 'post',
data
})
}
export function detailGoods(id: number) {
return request({
url: '/goods/detail',
method: 'get',
params: { id }
})
}
export function editGoods(data: any) {
return request({
url: '/goods/update',
method: 'post',
data
})
}
export function listCatAndBrand() {
return request({
url: '/goods/catAndBrand',
method: 'get'
})
}
export function xlsxDownload(key: string) {
return request({
url: '/storage/download/' + key,
method: 'get',
responseType: 'blob'
})
}
import { request } from '../config'
export function listGoods(params: any) {
return request({
url: '/goods/list',
method: 'get',
params
})
}
export function deleteGoods(data: any) {
return request({
url: '/goods/delete',
method: 'post',
data
})
}
export function publishGoods(data: any) {
return request({
url: '/goods/create',
method: 'post',
data
})
}
export function detailGoods(id: number) {
return request({
url: '/goods/detail',
method: 'get',
params: { id }
})
}
export function editGoods(data: any) {
return request({
url: '/goods/update',
method: 'post',
data
})
}
export function listCatAndBrand() {
return request({
url: '/goods/catAndBrand',
method: 'get'
})
}
export function xlsxDownload(key: string) {
return request({
url: '/storage/download/' + key,
method: 'get',
responseType: 'blob'
})
}
import router from '@/router'
import axios, { AxiosInstance } from 'axios'
import { ElMessage } from 'element-plus'
// api路径
const API_URL: string = import.meta.env.VITE_APP_BASE_URL
const service: AxiosInstance = axios.create({
timeout: 15000
})
// 请求拦截
service.interceptors.request.use(
config => {
const token = sessionStorage.getItem('token')
if (token) {
config.headers = { 'Authorization': `${token}`, ...config.headers }
}
return config
}
)
// 数据返回拦截
service.interceptors.response.use(
res => {
// 数据请求成功后 相应操作
const responseData = res.data
if (responseData.code !== 200) {
ElMessage.error(responseData.msg)
}
return responseData
},
error => {
// 数据请求失败后的相应操作
const response = error.response;
// 根据返回的code值来做不同的处理(和后端约定)
switch (response.status) {
case 401:
if (response.data.code === 911) {
window.location.href = "/#/login";
sessionStorage.clear()
return ElMessage.error('请重新登录')
} else if (response.data.code === 913) {
router.push({ path: '/import/lic' })
return ElMessage.error('请重新导入lic文件')
}
ElMessage.error('错误请求')
break;
case 403:
// 没有权限
ElMessage.error('没有权限')
break;
case 500:
// 服务端错误
ElMessage.error('服务端错误(500)')
break;
case 503:
// 服务端错误
ElMessage.error('服务端错误(503)')
break;
default:
ElMessage.error(`服务端错误${response.status}`)
break;
}
return error
}
)
interface ParamData {
url: string;
method?: 'post' | 'get' | 'delete' | 'put';
data?: any;
params?: any;
headers?: any;
responseType?: any;
}
export function request(params: ParamData) {
if (params.url.indexOf('http') === -1) {
params.url = API_URL + params.url
}
return service(params)
}
import router from '@/router'
import axios, { AxiosInstance } from 'axios'
import { ElMessage } from 'element-plus'
// api路径
const API_URL: string = import.meta.env.VITE_APP_BASE_URL
const service: AxiosInstance = axios.create({
timeout: 15000
})
// 请求拦截
service.interceptors.request.use(
config => {
const token = sessionStorage.getItem('token')
if (token) {
config.headers = { 'Authorization': `${token}`, ...config.headers }
}
return config
}
)
// 数据返回拦截
service.interceptors.response.use(
res => {
// 数据请求成功后 相应操作
const responseData = res.data
if (responseData.code !== 200) {
ElMessage.error(responseData.msg)
}
return responseData
},
error => {
// 数据请求失败后的相应操作
const response = error.response;
// 根据返回的code值来做不同的处理(和后端约定)
switch (response.status) {
case 401:
if (response.data.code === 911) {
window.location.href = "/#/login";
sessionStorage.clear()
return ElMessage.error('请重新登录')
} else if (response.data.code === 913) {
router.push({ path: '/import/lic' })
return ElMessage.error('请重新导入lic文件')
}
ElMessage.error('错误请求')
break;
case 403:
// 没有权限
ElMessage.error('没有权限')
break;
case 500:
// 服务端错误
ElMessage.error('服务端错误(500)')
break;
case 503:
// 服务端错误
ElMessage.error('服务端错误(503)')
break;
default:
ElMessage.error(`服务端错误${response.status}`)
break;
}
return error
}
)
interface ParamData {
url: string;
method?: 'post' | 'get' | 'delete' | 'put';
data?: any;
params?: any;
headers?: any;
responseType?: any;
}
export function request(params: ParamData) {
if (params.url.indexOf('http') === -1) {
params.url = API_URL + params.url
}
return service(params)
}
import { request } from '../config'
export function getBankList(params: any) {
return request({
url: '/bank_info',
method: 'get',
params,
})
}
export function addBank(data: any) {
return request({
url: '/bank_info',
method: 'post',
data,
})
}
export function updateBank(data: any) {
return request({
url: '/bank_info',
method: 'put',
data,
})
}
export function deleteBank(id: any) {
return request({
url: '/bank_info',
method: 'delete',
params: { id },
})
}
import { request } from '../config'
export function getBankList(params: any) {
return request({
url: '/bank_info',
method: 'get',
params,
})
}
export function addBank(data: any) {
return request({
url: '/bank_info',
method: 'post',
data,
})
}
export function updateBank(data: any) {
return request({
url: '/bank_info',
method: 'put',
data,
})
}
export function deleteBank(id: any) {
return request({
url: '/bank_info',
method: 'delete',
params: { id },
})
}
import { request } from '../config'
export function listMall() {
return request({
url: '/config/mall',
method: 'get'
})
}
export function updateMall(data: any) {
return request({
url: '/config/mall',
method: 'post',
data
})
}
export function listExpress() {
return request({
url: '/config/express',
method: 'get'
})
}
export function updateExpress(data: any) {
return request({
url: '/config/express',
method: 'post',
data
})
}
export function listOrder() {
return request({
url: '/config/order',
method: 'get'
})
}
export function updateOrder(data: any) {
return request({
url: '/config/order',
method: 'post',
data
})
}
export function listWx() {
return request({
url: '/config/wx',
method: 'get'
})
}
export function updateWx(data: any) {
return request({
url: '/config/wx',
method: 'post',
data
})
}
// 税率
export function taxConfig() {
return request({
url: '/config/tax',
method: 'get'
})
}
export function saveTaxConfig(data: any) {
return request({
url: '/config/tax',
method: 'post',
data
})
}
import { request } from '../config'
export function listMall() {
return request({
url: '/config/mall',
method: 'get'
})
}
export function updateMall(data: any) {
return request({
url: '/config/mall',
method: 'post',
data
})
}
export function listExpress() {
return request({
url: '/config/express',
method: 'get'
})
}
export function updateExpress(data: any) {
return request({
url: '/config/express',
method: 'post',
data
})
}
export function listOrder() {
return request({
url: '/config/order',
method: 'get'
})
}
export function updateOrder(data: any) {
return request({
url: '/config/order',
method: 'post',
data
})
}
export function listWx() {
return request({
url: '/config/wx',
method: 'get'
})
}
export function updateWx(data: any) {
return request({
url: '/config/wx',
method: 'post',
data
})
}
// 税率
export function taxConfig() {
return request({
url: '/config/tax',
method: 'get'
})
}
export function saveTaxConfig(data: any) {
return request({
url: '/config/tax',
method: 'post',
data
})
}
import { request } from '../config'
export function getPayList() {
return request({
url: '/pay/list',
method: 'get'
})
}
export function updatePay(data: any) {
return request({
url: '/pay/display',
method: 'put',
data
})
}
import { request } from '../config'
export function getPayList() {
return request({
url: '/pay/list',
method: 'get'
})
}
export function updatePay(data: any) {
return request({
url: '/pay/display',
method: 'put',
data
})
}
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
差异被折叠。
This source diff could not be displayed because it is too large. You can view the blob instead.
Markdown 格式
0%
您添加了 0 到此讨论。请谨慎行事。
请先完成此评论的编辑!
注册 或者 后发表评论