master
BINGWU 7 months ago
parent 3bccecd710
commit 9a4f8bcc5f

@ -0,0 +1,49 @@
/*
* @Author: BINGWU
* @Date: 2024-01-20 23:24:08
* @LastEditors: BINGWU HuJiaCheng2003@163.com
* @LastEditTime: 2024-01-23 03:07:54
* @FilePath: \bingwu-admin\.eslintrc.cjs
* @Describe:
* @Mark: (˶ ˶)
*/
/* eslint-env node */
require('@rushstack/eslint-patch/modern-module-resolution')
module.exports = {
root: true,
extends: [
'plugin:vue/vue3-essential',
'eslint:recommended',
'@vue/eslint-config-prettier/skip-formatting'
],
parserOptions: {
ecmaVersion: 'latest'
},
rules: {
'prettier/prettier': [
'warn',
{
singleQuote: true, // 单引号
semi: false, // 无分号
printWidth: 80, // 每行宽度至多80字符
trailingComma: 'none', // 不加对象|数组最后逗号
endOfLine: 'auto' // 换行符号不限制win mac 不一致)
}
],
'vue/multi-word-component-names': [
'warn',
{
ignores: ['index'] // vue组件名称多单词组成忽略index.vue
}
],
'vue/no-setup-props-destructure': ['off'], // 关闭 props 解构的校验
// 💡 添加未定义变量错误提示
'no-undef': 'error'
},
globals: {
ElMessage: 'readonly',
ElMessageBox: 'readonly',
ElLoading: 'readonly'
}
}

30
app/.gitignore vendored

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

@ -0,0 +1,8 @@
{
"$schema": "https://json.schemastore.org/prettierrc",
"semi": false,
"tabWidth": 2,
"singleQuote": true,
"printWidth": 100,
"trailingComma": "none"
}

@ -0,0 +1,8 @@
{
"recommendations": [
"Vue.volar",
"Vue.vscode-typescript-vue-plugin",
"dbaeumer.vscode-eslint",
"esbenp.prettier-vscode"
]
}

@ -0,0 +1,43 @@
# bingwu-admin
This template should help get you started developing with Vue 3 in Vite.
## Recommended IDE Setup
[VSCode](https://code.visualstudio.com/) + [Volar](https://marketplace.visualstudio.com/items?itemName=Vue.volar) (and disable Vetur) + [TypeScript Vue Plugin (Volar)](https://marketplace.visualstudio.com/items?itemName=Vue.vscode-typescript-vue-plugin).
## Customize configuration
See [Vite Configuration Reference](https://vitejs.dev/config/).
## Project Setup
```sh
pnpm install
```
### Compile and Hot-Reload for Development
```sh
pnpm dev
```
### Compile and Minify for Production
```sh
pnpm build
```
### Lint with [ESLint](https://eslint.org/)
```sh
pnpm lint
```
# 参考项目
vben:[GitHub地址](https://github.com/vbenjs/vue-vben-admin)
vue-element-admin:[GitHub地址](https://github.com/PanJiaChen/vue-element-admin)
ruo-yi:[GitHub地址](https://github.com/yangzongzhuan/RuoYi-Vue3)

@ -0,0 +1,61 @@
/* eslint-disable */
/* prettier-ignore */
// @ts-nocheck
// Generated by unplugin-vue-components
// Read more: https://github.com/vuejs/core/pull/3399
export {}
declare module 'vue' {
export interface GlobalComponents {
AvatarCom: typeof import('./src/components/AvatarCom.vue')['default']
BaseTableCom: typeof import('./src/components/table/BaseTableCom.vue')['default']
CollapseAsideCom: typeof import('./src/components/utils/CollapseAsideCom.vue')['default']
DarkSwitchCom: typeof import('./src/components/utils/DarkSwitchCom.vue')['default']
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']
ElConfigProvider: typeof import('element-plus/es')['ElConfigProvider']
ElContainer: typeof import('element-plus/es')['ElContainer']
ElDialog: typeof import('element-plus/es')['ElDialog']
ElDivider: typeof import('element-plus/es')['ElDivider']
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']
ElImage: typeof import('element-plus/es')['ElImage']
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']
ElPagination: typeof import('element-plus/es')['ElPagination']
ElProgress: typeof import('element-plus/es')['ElProgress']
ElSelect: typeof import('element-plus/es')['ElSelect']
ElSubMenu: typeof import('element-plus/es')['ElSubMenu']
ElSwitch: typeof import('element-plus/es')['ElSwitch']
ElTable: typeof import('element-plus/es')['ElTable']
ElTableColumn: typeof import('element-plus/es')['ElTableColumn']
ElTag: typeof import('element-plus/es')['ElTag']
ElUpload: typeof import('element-plus/es')['ElUpload']
ExportExcelCom: typeof import('./src/components/excel/ExportExcelCom.vue')['default']
ImportExcelCom: typeof import('./src/components/excel/ImportExcelCom.vue')['default']
MyEcharts: typeof import('./src/components/echarts/MyEcharts.vue')['default']
PreviewPictureCom: typeof import('./src/components/PreviewPictureCom.vue')['default']
ProgressCom: typeof import('./src/components/progress/ProgressCom.vue')['default']
ProgressLiteCom: typeof import('./src/components/progress/ProgressLiteCom.vue')['default']
RouterLink: typeof import('vue-router')['RouterLink']
RouterView: typeof import('vue-router')['RouterView']
SettingCom: typeof import('./src/components/SettingCom.vue')['default']
SwitchLanguageCom: typeof import('./src/components/utils/SwitchLanguageCom.vue')['default']
UploadFileCom: typeof import('./src/components/file/UploadFileCom.vue')['default']
}
export interface ComponentCustomProperties {
vLoading: typeof import('element-plus/es')['ElLoadingDirective']
}
}

@ -0,0 +1,23 @@
<!--
* @Author: BINGWU
* @Date: 2024-04-10 16:15:37
* @LastEditors: BINGWU HuJiaCheng2003@163.com
* @LastEditTime: 2024-04-11 17:08:12
* @FilePath: \app\index.html
* @Describe:
* @Mark: ૮(˶ᵔ ᵕ ᵔ˶)ა
-->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<link rel="icon" href="/favicon.ico">
<link rel="stylesheet" href="//at.alicdn.com/t/c/font_4503951_gwy78199vxn.css">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>bingwu-admin</title>
</head>
<body>
<div id="app"></div>
<script type="module" src="/src/main.js"></script>
</body>
</html>

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

@ -0,0 +1,42 @@
{
"name": "bingwu-admin",
"version": "0.0.0",
"private": true,
"type": "module",
"scripts": {
"dev": "vite",
"build": "vite build",
"preview": "vite preview",
"lint": "eslint . --ext .vue,.js,.jsx,.cjs,.mjs --fix --ignore-path .gitignore",
"format": "prettier --write src/"
},
"dependencies": {
"@element-plus/icons-vue": "^2.3.1",
"@vueuse/core": "^10.7.2",
"@wangeditor/editor": "^5.1.23",
"@wangeditor/editor-for-vue": "^5.1.12",
"axios": "^1.6.7",
"cos-js-sdk-v5": "^1.6.0",
"echarts": "^5.4.3",
"element-plus": "^2.5.3",
"normalize.css": "^8.0.1",
"pinia": "^2.1.7",
"pinia-plugin-persistedstate": "^3.2.1",
"vue": "^3.3.11",
"vue-i18n": "^9.9.0",
"vue-router": "^4.2.5",
"xlsx": "https://cdn.sheetjs.com/xlsx-0.20.0/xlsx-0.20.0.tgz"
},
"devDependencies": {
"@rushstack/eslint-patch": "^1.3.3",
"@vitejs/plugin-vue": "^4.5.2",
"@vue/eslint-config-prettier": "^8.0.0",
"eslint": "^8.49.0",
"eslint-plugin-vue": "^9.17.0",
"prettier": "^3.0.3",
"sass": "^1.70.0",
"unplugin-auto-import": "^0.17.3",
"unplugin-vue-components": "^0.26.0",
"vite": "^5.0.10"
}
}

File diff suppressed because it is too large Load Diff

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

@ -0,0 +1,45 @@
<!--
* @Author: BINGWU
* @Date: 2024-01-20 23:24:08
* @LastEditors: BINGWU HuJiaCheng2003@163.com
* @LastEditTime: 2024-01-23 23:18:53
* @FilePath: \bingwu-admin\src\App.vue
* @Describe:
* @Mark: (˶ ˶)
-->
<script setup>
import { computed, ref } from 'vue'
import zhCn from 'element-plus/dist/locale/zh-cn.mjs'
import en from 'element-plus/dist/locale/en.mjs'
import { RouterView } from 'vue-router'
import { getStateStore } from './stores'
import { useI18n } from 'vue-i18n'
const myLocale = useI18n().locale
const stateStore = getStateStore()
myLocale.value = stateStore.currentLang
// element
const langArr = ref([
{
lang: 'zh-cn',
locale: zhCn
},
{
lang: 'en-us',
locale: en
}
])
const locale = computed(() => {
const item = langArr.value.find(
(item) => item.lang === stateStore.currentLang
)
return item.locale
})
</script>
<template>
<el-config-provider :locale="locale">
<RouterView />
</el-config-provider>
</template>
<style scoped></style>

@ -0,0 +1,10 @@
import http from '@/utils/http'
const getUser = () => {
return http.get('/user/get', {
params: {
_id: '66136094633f0524859aa797'
}
})
}
export { getUser }

Binary file not shown.

After

Width:  |  Height:  |  Size: 74 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 104 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 202 KiB

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 261.76 226.69"><path d="M161.096.001l-30.225 52.351L100.647.001H-.005l130.877 226.688L261.749.001z" fill="#41b883"/><path d="M161.096.001l-30.225 52.351L100.647.001H52.346l78.526 136.01L209.398.001z" fill="#34495e"/></svg>

After

Width:  |  Height:  |  Size: 276 B

@ -0,0 +1,40 @@
<template>
<div class="avatar">
<div class="left">
<el-avatar
:size="28"
src="https://fuss10.elemecdn.com/e/5d/4a731a90594a4af544c0c25941171jpeg.jpeg"
/>
<el-dropdown trigger="click">
<span class="el-dropdown-link">
<el-icon class="el-icon--right"><CaretBottom /></el-icon>
</span>
<template #dropdown>
<el-dropdown-menu>
<el-dropdown-item icon="Plus">Action 1</el-dropdown-item>
<el-dropdown-item icon="CirclePlusFilled">
Action 2
</el-dropdown-item>
<el-dropdown-item icon="CirclePlus">Action 3</el-dropdown-item>
<el-dropdown-item icon="Check">Action 4</el-dropdown-item>
<el-dropdown-item icon="CircleCheck">Action 5</el-dropdown-item>
</el-dropdown-menu>
</template>
</el-dropdown>
</div>
</div>
</template>
<script setup></script>
<style lang="scss" scoped>
.avatar {
height: 100%;
display: flex;
align-items: center;
margin: 0 14px 0 12px;
}
:deep(.el-dropdown) {
transform: translateY(15px);
}
</style>

@ -0,0 +1,113 @@
<!--
* @Author: BINGWU
* @Date: 2024-02-17 16:40:17
* @LastEditors: BINGWU HuJiaCheng2003@163.com
* @LastEditTime: 2024-02-25 16:52:35
* @FilePath: \vue-movie\bingwu-admin\src\components\PreviewPictureCom.vue
* @Describe:
* @Mark: (˶ ˶)
-->
<template>
<div class="preview-picture">
<el-upload
class="avatar-uploader"
:show-file-list="false"
:auto-upload="false"
:on-change="uploaderFile"
>
<img v-if="imageUrl" :src="imageUrl" class="avatar" />
<el-icon v-else class="avatar-uploader-icon"><Plus /></el-icon>
</el-upload>
</div>
</template>
<script setup>
import { ref } from 'vue'
// base64
const imageUrl = ref('')
//
const file = ref(null)
const uploaderFile = async (imagefile) => {
file.value = imagefile
await fileToBase64(imagefile)
.then((base64Url) => {
imageUrl.value = base64Url
})
.catch((error) => {
ElMessage.error(error)
})
}
const fileToBase64 = (imagefile) => {
return new Promise((resolve, reject) => {
const reader = new FileReader()
reader.readAsDataURL(imagefile.raw)
reader.onload = () => {
const base64Url = reader.result
resolve(base64Url)
}
//
reader.onerror = function () {
reject(new Error('Failed to load file'))
}
})
}
// url
const imageUrlFn = () => {
return {
getImageUrl() {
return imageUrl
},
setImageUrl(newImageUrl) {
imageUrl.value = newImageUrl
}
}
}
//
const fileFn = () => {
return {
getFile() {
return file
},
setFile(newFile) {
file.value = newFile
}
}
}
defineExpose({
imageUrlFn,
fileFn
})
</script>
<style lang="scss" scoped>
:deep(.avatar-uploader .avatar) {
width: 178px;
height: 178px;
display: block;
}
:deep(.avatar-uploader .el-upload) {
border: 1px dashed var(--el-border-color);
border-radius: 6px;
cursor: pointer;
position: relative;
overflow: hidden;
transition: var(--el-transition-duration-fast);
}
:deep(.avatar-uploader .el-upload:hover) {
border-color: var(--el-color-primary);
}
:deep(.el-icon.avatar-uploader-icon) {
font-size: 28px;
color: #8c939d;
width: 178px;
height: 178px;
text-align: center;
}
</style>

@ -0,0 +1,30 @@
<!--
* @Author: BINGWU
* @Date: 2024-01-24 18:02:40
* @LastEditors: BINGWU HuJiaCheng2003@163.com
* @LastEditTime: 2024-01-29 18:51:38
* @FilePath: \bingwu-admin\src\components\SettingCom.vue
* @Describe:
* @Mark: (˶ ˶)
-->
<template>
<div class="setting">
<el-dropdown trigger="click">
<el-icon :size="20"><Setting /></el-icon>
<template #dropdown>
<el-dropdown-menu>
<el-dropdown-item>
<span style="margin-right: 8px">黑暗模式</span>
<DarkSwitchCom></DarkSwitchCom>
</el-dropdown-item>
</el-dropdown-menu>
</template>
</el-dropdown>
</div>
</template>
<script setup>
import DarkSwitchCom from '@/components/utils/DarkSwitchCom.vue'
</script>
<style lang="scss" scoped></style>

@ -0,0 +1,44 @@
<template>
<div class="Echarts">
<div id="main" style="width: 600px; height: 400px"></div>
</div>
</template>
<script setup>
import { onMounted } from 'vue'
import * as echarts from 'echarts'
const myEcharts = () => {
// domecharts
var myChart = echarts.init(document.getElementById('main'))
//
var option = {
title: {
text: 'ECharts 入门示例'
},
tooltip: {},
legend: {
data: ['销量']
},
xAxis: {
data: ['衬衫', '羊毛衫', '雪纺衫', '裤子', '高跟鞋', '袜子']
},
yAxis: {},
series: [
{
name: '销量',
type: 'bar',
data: [5, 20, 36, 10, 10, 20]
}
]
}
// 使
myChart.setOption(option)
}
onMounted(() => {
myEcharts()
})
</script>
<style></style>

@ -0,0 +1,121 @@
<!--
* @Author: BINGWU
* @Date: 2024-01-30 22:31:39
* @LastEditors: BINGWU HuJiaCheng2003@163.com
* @LastEditTime: 2024-01-31 23:06:46
* @FilePath: \bingwu-admin\src\components\excel\ExportExcelCom.vue
* @Describe:
* @Mark: (˶ ˶)
-->
<template>
<div class="export-excel">
<el-button
type="primary"
icon="Download"
@click="
() => {
formData.fileName = ''
formData.fileExtension = ''
dialogVisible = true
}
"
>导出文件</el-button
>
<el-dialog v-model="dialogVisible" title="导出" width="30%">
<el-form :model="formData" :rules="rules" ref="formRef">
<el-form-item label="文件名称">
<el-input
v-model="formData.fileName"
placeholder="默认为excelFile"
clearable
/>
</el-form-item>
<el-form-item label="文件后缀" prop="fileExtension">
<el-select
v-model="formData.fileExtension"
placeholder="请选择文件后缀"
clearable
>
<el-option label="xlsx" value="xlsx" />
<el-option label="xls" value="xls" />
</el-select>
</el-form-item>
</el-form>
<template #footer>
<span class="dialog-footer">
<el-button @click="dialogVisible = false">取消</el-button>
<el-button type="primary" @click="handleConfirm"> </el-button>
</span>
</template>
</el-dialog>
</div>
</template>
<script setup>
// xlsx
import { utils, writeFileXLSX } from 'xlsx'
import { ref, reactive } from 'vue'
const dialogVisible = ref(false)
const emits = defineEmits(['handleExportData'])
const formData = reactive({
fileName: '',
fileExtension: ''
})
const formRef = ref(null)
const rules = ref({
fileExtension: [
{ required: true, message: '请选择文件后缀', trigger: 'blur' }
]
})
const handleConfirm = () => {
formRef.value.validate((valid) => {
if (valid) {
emits(
'handleExportData',
exportFile,
formData.fileName,
formData.fileExtension
)
// exportFile(props.exportData, formData.fileName, formData.fileExtension)
dialogVisible.value = false
}
})
}
// excel
const exportFile = (exportData, fileName, fileExtension) => {
if (exportData.length === 0) {
ElMessage({
showClose: true,
message: '没有要导出的数据',
type: 'error'
})
} else {
if (!fileName) {
fileName = 'excelFile'
}
// utils.json_to_sheet()
const ws = utils.json_to_sheet(exportData)
const wb = utils.book_new()
// utils.book_append_sheet(1, 2, 3)
// 1 utils.json_to_sheet(arr)
// 2 utils.book_new()
// 3
utils.book_append_sheet(wb, ws, 'Data')
// writeFileXLSX(1, 2)
// 1 utils.book_new()
// 2 excel xlsx : 'abc.xlsx' '.xlsx'
writeFileXLSX(wb, `${fileName}.${fileExtension}`)
dialogVisible.value = !dialogVisible.value
}
}
</script>
<style lang="scss" scoped></style>

@ -0,0 +1,115 @@
<!--
* @Author: BINGWU
* @Date: 2024-01-30 22:31:26
* @LastEditors: BINGWU HuJiaCheng2003@163.com
* @LastEditTime: 2024-01-31 21:31:30
* @FilePath: \bingwu-admin\src\components\excel\ImportExcelCom.vue
* @Describe:
* @Mark: (˶ ˶)
-->
<template>
<div class="import-excel">
<UploadFileCom
ref="uploadFileComRef"
accept=".xls,.xlsx"
@handleConfirm="handleImport"
>
<template #tip>文件格式要求为xls,xlsx</template>
</UploadFileCom>
</div>
</template>
<script setup>
// xlsx
// import { utils, writeFileXLSX } from 'xlsx'
// xslxjson
import XLSX from 'xlsx'
import UploadFileCom from '@/components/file/UploadFileCom.vue'
import { ref } from 'vue'
const uploadFileComRef = ref(null)
const emits = defineEmits(['importData'])
const handleImport = async (selectFiles) => {
if (selectFiles.length === 0) {
ElMessage.error('导入失败,当前没有要导入的文件')
} else {
let existErrorFileType = false
selectFiles.forEach((item) => {
let fileExtension = item.raw.name.substring(
item.raw.name.lastIndexOf('.') + 1
)
if (!(fileExtension === 'xlsx' || fileExtension === 'xls')) {
existErrorFileType = true
}
})
if (existErrorFileType) {
ElMessage.error('导入失败,存在格式不为xls/xlsx的文件类型')
} else {
//
const excelFilesData = await getFilesDataSync(selectFiles)
emits('importData', excelFilesData)
uploadFileComRef.value.offDialog()
}
}
}
const readFileSync = (file) => {
return new Promise((resolve, reject) => {
const reader = new FileReader()
reader.readAsBinaryString(file.raw)
reader.onload = (event) => {
resolve(event.target.result)
}
reader.onerror = (event) => {
reject(event.target.error)
}
})
}
const getFilesDataSync = async (selectFiles) => {
let excelFilesData = []
for (const file of selectFiles) {
try {
const data = await readFileSync(file)
const excelData = XLSX.read(data, { type: 'binary' })
const wsname = excelData.SheetNames[0]
const content = XLSX.utils.sheet_to_json(excelData.Sheets[wsname])
excelFilesData = excelFilesData.concat(content)
} catch (error) {
console.error('Error reading file:', error)
}
}
return excelFilesData
}
// const getFilesData = (selectFiles) => {
// let excelFilesData = []
// selectFiles.forEach(async (file) => {
// // fileReader
// const reader = new FileReader()
// //
// reader.readAsBinaryString(file.raw)
// reader.onload = (re) => {
// //
// const data = re.target.result
// // xlsx
// const excelData = XLSX.read(data, {
// type: 'binary'
// })
// // 0 excel
// // wsname
// const wsname = excelData.SheetNames[0]
// console.log('wsname', wsname)
// const content = XLSX.utils.sheet_to_json(excelData.Sheets[wsname])
// excelFilesData = excelFilesData.concat(content)
// console.log('11111', excelFilesData)
// }
// })
// return excelFilesData
// }
</script>
<style lang="scss" scoped></style>

@ -0,0 +1,90 @@
<!--
* @Author: BINGWU
* @Date: 2024-01-30 22:31:39
* @LastEditors: BINGWU HuJiaCheng2003@163.com
* @LastEditTime: 2024-02-26 17:42:24
* @FilePath: \bingwu-admin\src\components\file\UploadFileCom.vue
* @Describe:
* @Mark: (˶ ˶)
-->
<template>
<div class="export-file">
<el-button type="primary" icon="Upload" @click="dialogVisible = true">
上传文件
</el-button>
<el-dialog v-model="dialogVisible" title="上传文件" width="30%">
<el-upload
ref="uploadRef"
:auto-upload="false"
:on-change="
(_, files) => {
selectFiles = files
}
"
drag
:accept="props.accept"
>
<el-icon size="30"><upload-filled /></el-icon>
<div class="el-upload__text">将文件拖到此处或 <em>点击上传</em></div>
<template #tip>
<div class="tip"><slot name="tip"></slot></div>
</template>
</el-upload>
<template #footer>
<span>
<el-button @click="dialogVisible = false">取消</el-button>
<el-button
type="primary"
@click="
() => {
emits('handleConfirm', selectFiles)
}
"
>
确认
</el-button>
</span>
</template>
</el-dialog>
</div>
</template>
<script setup>
import { ref } from 'vue'
const selectFiles = ref([])
const dialogVisible = ref(false)
const uploadRef = ref(null)
const emits = defineEmits(['handleConfirm'])
const getSelectFiles = () => {
return selectFiles.value
}
const props = defineProps({
//
accept: {
type: String
}
})
//
const clearFiles = () => {
selectFiles.value = []
uploadRef.value.clearFiles()
}
const offDialog = () => {
dialogVisible.value = !dialogVisible.value
}
defineExpose({
getSelectFiles,
offDialog,
clearFiles
})
</script>
<style lang="scss" scoped>
.export-file {
width: 20%;
.tip {
margin-top: 10px;
font-size: 14px;
}
}
</style>

@ -0,0 +1,77 @@
<template>
<div class="progress-lite">
<!-- 假性进度条组件 -->
<el-dialog
v-model="dialogVisible"
title="上传中"
width="500"
:show-close="false"
>
<div class="demo-progress">
<template v-if="props.progressType.type1">
<el-progress
:percentage="percentage"
:stroke-width="22"
striped
:status="status"
:duration="80"
striped-flow
:text-inside="true"
/>
</template>
<template v-if="props.progressType.type2">
<el-progress
:percentage="percentage"
:indeterminate="true"
:status="status"
:stroke-width="16"
/>
</template>
<template v-if="props.progressType.type3"
><el-progress
type="dashboard"
:percentage="percentage"
:status="status"
>
<template #default="{ percentage }">
<span class="percentage-value">{{ percentage }}%</span>
</template>
</el-progress></template
>
</div>
</el-dialog>
</div>
</template>
<script setup>
import { useProgress } from '@/hooks/progress'
const props = defineProps({
//
progressType: {
type: Object,
default: () => {
return {
type1: true,
type2: false,
type3: false
}
}
}
})
const {
percentage,
status,
dialogVisible,
changePercentage,
offProgress,
openProgress
} = useProgress()
defineExpose({
changePercentage,
offProgress,
openProgress
})
</script>
<style lang="scss" scoped></style>

@ -0,0 +1,126 @@
<template>
<div class="progress-lite">
<!-- 假性进度条组件 -->
<el-dialog
v-model="dialogVisible"
title="上传中"
width="500"
:show-close="false"
>
<div class="demo-progress">
<template v-if="props.progressType.type1">
<el-progress
:percentage="percentage"
:stroke-width="22"
striped
:status="status"
:duration="80"
striped-flow
:text-inside="true"
/>
</template>
<template v-if="props.progressType.type2">
<el-progress
:percentage="percentage"
:indeterminate="true"
:status="status"
:stroke-width="16"
/>
</template>
<template v-if="props.progressType.type3"
><el-progress
type="dashboard"
:percentage="percentage"
:status="status"
>
<template #default="{ percentage }">
<span class="percentage-value">{{ percentage }}%</span>
</template>
</el-progress></template
>
</div>
</el-dialog>
</div>
</template>
<script setup>
//
import { ref } from 'vue'
const percentage = ref(0)
const status = ref('')
const dialogVisible = ref(false)
const props = defineProps({
//
time: {
type: Number,
default: 1000
},
// (100)
increment: {
type: Number,
default: 10
},
//
progressType: {
type: Object,
default: () => {
return {
type1: true,
type2: false,
type3: false
}
}
}
})
const start = () => {
percentage.value = 0
status.value = ''
dialogVisible.value = true
const startWork = setInterval(() => {
// percentage 10099
percentage.value =
percentage.value + props.increment >= 100
? 100
: percentage.value + props.increment
}, props.time)
return startWork
}
const end = async (startWork, newState = 'success', message = '上传成功') => {
clearInterval(startWork)
if (newState == 'success') {
//
const endWork = setInterval(() => {
if (percentage.value >= 100) {
percentage.value = 100
clearInterval(endWork)
status.value = 'success'
ElMessage({
message,
type: 'success'
})
}
// percentage 100100
percentage.value =
percentage.value + props.increment >= 100
? 100
: percentage.value + props.increment
}, 80)
} else {
//
ElMessage.error(`${message}`)
status.value = 'exception'
}
//
setTimeout(() => {
dialogVisible.value = false
}, 2000)
}
defineExpose({
start,
end
})
</script>
<style lang="scss" scoped></style>

@ -0,0 +1,212 @@
<template>
<div class="base-table">
<el-table
:data="props.tableData"
v-loading="tableLoading"
@selection-change="handleSelectionChange"
ref="tableRef"
>
<el-table-column type="selection" width="55" v-if="props.showSelect" />
<el-table-column
v-for="(item, index) in props.columnData"
:prop="item.prop"
:label="item.label"
:key="index"
:width="item.width"
:sortable="item.sortable"
>
<!-- tag列 -->
<template #default="{ row }" v-if="item?.tagColumn">
<el-tag :type="row[item.prop].tagType">{{
row[item.prop].tagName
}}</el-tag>
</template>
<!-- 图片列 -->
<template #default="{ row }" v-else-if="item?.pictureColumn">
<el-image
style="
width: 100px;
height: 100px;
display: flex;
align-items: center;
justify-content: center;
"
:src="row[item.prop]"
fit
lazy
>
<template #error>
<el-icon size="30"><Picture /></el-icon>
</template>
</el-image>
</template>
<!-- 开关列 -->
<template #default="{ row }" v-else-if="item?.switchColumn">
<el-switch
v-model="row[item.prop]"
size="large"
:loading="switchLoading"
:before-change="beforeSwitchChange"
@change="
(switchState) => {
emits('updateSwitchState', switchState)
}
"
/>
</template>
</el-table-column>
<!-- 操作列 -->
<el-table-column label="操作" v-if="props?.dropdownData">
<template #default="{ row }">
<el-dropdown
trigger="click"
@command="
(command) => {
handleCommand(command, row)
}
"
>
<el-button type="primary" icon="Operation">操作 </el-button>
<template #dropdown>
<el-dropdown-menu>
<template
v-for="(item, index) in props.dropdownData"
:key="index"
>
<!-- 权限控制 item.auth-->
<div v-permission="item.auth">
<el-dropdown-item
:command="item.command"
:icon="item.icon"
>{{ item.actionName }}</el-dropdown-item
>
</div>
</template>
</el-dropdown-menu>
</template>
</el-dropdown>
</template>
</el-table-column>
<template #empty>
<el-empty description="没有数据" />
</template>
</el-table>
<div class="pagination" v-if="props.showPagination">
<el-pagination
v-model:current-page="paginationData.currentPage"
v-model:page-size="paginationData.pageSize"
:page-sizes="props.pageSizes"
layout="total, sizes, prev, pager, next, jumper"
:total="props.total"
@size-change="handleSizeChange"
@current-change="handleCurrentChange"
/>
</div>
</div>
</template>
<script setup>
import { ref } from 'vue'
import { useTable } from '@/hooks/table'
const props = defineProps({
columnData: {
type: Array,
default: () => {
return [
{
prop: 'columnProp1',
label: '列1',
width: '100'
},
{
prop: 'columnProp2',
label: '列2',
width: '100'
},
{
prop: 'columnProp3',
label: '列3',
width: '120'
},
{
prop: 'columnProp4',
label: '列4'
}
]
}
},
tableData: {
type: Array
},
dropdownData: {
type: Array
},
//
messageBoxContent: {
type: String,
default: '默认内容'
},
//
messageBoxTitle: {
type: String,
default: '默认标题'
},
//
showSelect: {
type: Boolean,
default: false
},
//
pageSizes: {
type: Array,
default: () => {
return [5, 10, 15]
}
},
//
total: {
type: Number
},
//
showPagination: {
type: Boolean,
default: false
}
})
const tableRef = ref(null)
const emits = defineEmits(['updateSwitchState'], ['updateTableData'])
const {
tableLoading,
switchLoading,
changeTableLoading,
changeSwitchLoading,
beforeSwitchChange,
handleCommand,
paginationData,
handleCurrentChange,
handleSizeChange
} = useTable(props, emits)
const getSelectRows = () => {
return tableRef.value.getSelectionRows()
}
const getPaginationData = () => {
return paginationData.value
}
defineExpose({
changeTableLoading,
changeSwitchLoading,
getSelectRows,
getPaginationData
})
</script>
<style lang="scss" scoped>
.base-table {
.pagination {
margin-top: 40px;
display: flex;
align-items: center;
justify-content: center;
}
}
</style>

@ -0,0 +1,22 @@
<!--
* @Author: BINGWU
* @Date: 2024-01-23 18:29:12
* @LastEditors: BINGWU HuJiaCheng2003@163.com
* @LastEditTime: 2024-02-10 21:46:46
* @FilePath: \bingwu-admin\src\components\utils\CollapseAsideCom.vue
* @Describe:
* @Mark: (˶ ˶)
-->
<template>
<div class="collapse-aside" @click="stateStore.updateCollapse">
<el-icon :size="20" v-if="stateStore.isCollapse"><Expand /></el-icon>
<el-icon :size="20" v-else><Fold /></el-icon>
</div>
</template>
<script setup>
import { getStateStore } from '@/stores/index'
const stateStore = getStateStore()
</script>
<style lang="scss" scoped></style>

@ -0,0 +1,38 @@
<!--
* @Author: BINGWU
* @Date: 2024-01-23 17:57:13
* @LastEditors: BINGWU HuJiaCheng2003@163.com
* @LastEditTime: 2024-01-23 18:24:27
* @FilePath: \bingwu-admin\src\components\utils\DarkSwitchCom.vue
* @Describe:
* @Mark: (˶ ˶)
-->
<template>
<div class="dark-switch">
<el-switch
v-model="switchState"
style="--el-switch-on-color: #2c2c2c"
@change="toggleDark"
size="large"
>
<template #active-action>
<el-icon><Moon /></el-icon>
</template>
<template #inactive-action>
<el-icon><Sunny /></el-icon>
</template>
</el-switch>
</div>
</template>
<script setup>
import { Moon, Sunny } from '@element-plus/icons-vue'
import { useDark, useToggle } from '@vueuse/core'
import { ref } from 'vue'
const toggleDark = useToggle(useDark())
const switchState = ref(false)
const colorName = localStorage.getItem('vueuse-color-scheme')
switchState.value = colorName === 'light' ? false : true
</script>
<style lang="scss" scoped></style>

@ -0,0 +1,38 @@
<!--
* @Author: BINGWU
* @Date: 2024-01-23 21:13:17
* @LastEditors: BINGWU HuJiaCheng2003@163.com
* @LastEditTime: 2024-02-10 23:52:44
* @FilePath: \bingwu-admin\src\components\utils\SwitchLanguageCom.vue
* @Describe:
* @Mark: (˶ ˶)
-->
<template>
<div class="switch-language">
<el-dropdown trigger="click" @command="changeLang">
<el-icon> <i class="icon iconfont icon-yuyan1"></i></el-icon>
<template #dropdown>
<el-dropdown-menu>
<el-dropdown-item command="zh-cn">中文</el-dropdown-item>
<el-dropdown-item command="en-us">English</el-dropdown-item>
</el-dropdown-menu>
</template>
</el-dropdown>
</div>
</template>
<script setup>
import { getStateStore } from '@/stores'
const stateStore = getStateStore()
const changeLang = (lang) => {
stateStore.updateCurrentLang(lang)
//
location.reload()
}
</script>
<style lang="scss" scoped>
.switch-language {
margin-right: 12px;
}
</style>

@ -0,0 +1,46 @@
/*
* @Author: BINGWU
* @Date: 2024-03-27 00:33:27
* @LastEditors: BINGWU HuJiaCheng2003@163.com
* @LastEditTime: 2024-03-27 00:37:44
* @FilePath: \bingwu-admin\src\hooks\progress.js
* @Describe:
* @Mark: (˶ ˶)
*/
import { ref } from 'vue'
export const useProgress = () => {
const percentage = ref(0)
const status = ref('')
const dialogVisible = ref(false)
const changePercentage = (newPercentage) => {
percentage.value = newPercentage
}
const offProgress = (newState = 'success', message = '上传成功') => {
setTimeout(() => {
dialogVisible.value = false
}, 1200)
if (newState === 'success') {
status.value = 'success'
ElMessage({
message,
type: 'success'
})
} else {
ElMessage.error(`${message}`)
status.value = 'exception'
}
}
const openProgress = () => {
status.value = ''
percentage.value = 0
dialogVisible.value = true
}
return {
percentage,
status,
dialogVisible,
changePercentage,
offProgress,
openProgress
}
}

@ -0,0 +1,77 @@
/*
* @Author: BINGWU
* @Date: 2024-03-26 22:39:33
* @LastEditors: BINGWU HuJiaCheng2003@163.com
* @LastEditTime: 2024-03-26 23:10:08
* @FilePath: \bingwu-admin\src\hooks\table.js
* @Describe:
* @Mark: (˶ ˶)
*/
import { ref } from 'vue'
export const useTable = (props, emits) => {
const tableLoading = ref(false)
const switchLoading = ref(false)
const changeTableLoading = () => {
tableLoading.value = !tableLoading.value
}
const paginationData = ref({
currentPage: 1,
pageSize: props.pageSizes[0]
})
const changeSwitchLoading = () => {
switchLoading.value = !switchLoading.value
}
const beforeSwitchChange = () => {
return ElMessageBox.confirm(
props.messageBoxContent,
props.messageBoxTitle,
{
confirmButtonText: '确认',
cancelButtonText: '取消',
type: 'warning'
}
)
.then(() => {
return new Promise((resolve) => {
ElMessage.success('变更成功')
return resolve(true)
})
})
.catch(() => {
return new Promise((_, reject) => {
ElMessage.warning('取消变更')
return reject()
})
})
}
const handleCommand = (command, row) => {
const dropdownItem = props.dropdownData.find((item) => {
return item.command === command
})
dropdownItem.handleAction(row)
}
const handleCurrentChange = (currentPage) => {
paginationData.value.currentPage = currentPage
// 传入当前页码容量大小和当前页码
emits('updateTableData', paginationData.value.pageSize, currentPage)
}
const handleSizeChange = (pageSize) => {
// 当前页的数据容量改变重置页码为1
paginationData.value.pageSize = pageSize
paginationData.value.currentPage = 1
// 传入当前页面容量大小和当前页码
emits('updateTableData', pageSize, paginationData.value.currentPage)
}
return {
tableLoading,
switchLoading,
changeTableLoading,
changeSwitchLoading,
beforeSwitchChange,
handleCommand,
paginationData,
handleCurrentChange,
handleSizeChange
}
}

@ -0,0 +1,23 @@
/*
* @Author: BINGWU
* @Date: 2024-01-23 22:17:57
* @LastEditors: BINGWU HuJiaCheng2003@163.com
* @LastEditTime: 2024-01-23 22:45:53
* @FilePath: \bingwu-admin\src\language\index.js
* @Describe:
* @Mark: (˶ ˶)
*/
import { createI18n } from 'vue-i18n'
import zhCN from './locales/zh-CN'
import enUS from './locales/en-US'
const i18n = createI18n({
legacy: false,
locale: 'zh-cn', // 默认显示语言
messages: {
'zh-cn': zhCN,
'en-us': enUS
}
})
export default i18n

@ -0,0 +1,29 @@
/*
* @Author: BINGWU
* @Date: 2024-01-23 22:17:32
* @LastEditors: BINGWU HuJiaCheng2003@163.com
* @LastEditTime: 2024-01-23 22:17:40
* @FilePath: \bingwu-admin\src\language\locales\en_US.js
* @Describe:
* @Mark: (˶ ˶)
*/
export default {
common: {
more: 'Look More'
},
leftMenus: {
'/': 'Home',
Home: 'Home',
home: 'Home'
},
headMenus: {
subTitle: 'Organization service platform',
userName: 'ZhangSan',
name: 'name',
menu1: 'international'
},
login: {
personal_center: 'personal center',
sign_out: 'sign out'
}
}

@ -0,0 +1,29 @@
/*
* @Author: BINGWU
* @Date: 2024-01-23 22:16:55
* @LastEditors: BINGWU HuJiaCheng2003@163.com
* @LastEditTime: 2024-01-23 22:17:09
* @FilePath: \bingwu-admin\src\language\locales\zh.js
* @Describe:
* @Mark: (˶ ˶)
*/
export default {
common: {
more: '查看更多'
},
leftMenus: {
'/': '首页',
Home: '首页',
home: '首页'
},
headMenus: {
subTitle: '机构服务平台',
userName: '张三',
name: '姓名',
menu1: '国际化'
},
login: {
personal_center: '个人中心',
sign_out: '退出'
}
}

@ -0,0 +1,47 @@
<!--
* @Author: BINGWU
* @Date: 2024-01-23 03:50:29
* @LastEditors: BINGWU HuJiaCheng2003@163.com
* @LastEditTime: 2024-03-28 17:44:22
* @FilePath: \bingwu-admin\src\layout\IndexCom.vue
* @Describe:
* @Mark: (˶ ˶)
-->
<template>
<div class="index">
<el-container>
<el-aside style="width: auto">
<AsideCom></AsideCom>
</el-aside>
<el-container style="height: 100vh">
<el-header style="height: 6%">
<HeaderCom></HeaderCom>
</el-header>
<el-divider style="margin: 0" />
<el-main style="height: 94%">
<main class="content">
<RouterView />
</main>
</el-main>
</el-container>
</el-container>
</div>
</template>
<script setup>
import { RouterView } from 'vue-router'
import HeaderCom from './components/HeaderCom.vue'
import AsideCom from './components/AsideCom.vue'
</script>
<style lang="scss" scoped>
.index {
.content {
height: 100%;
box-shadow: rgba(0, 0, 0, 0.35) 0px 5px 15px;
padding: 30px;
overflow: auto;
box-sizing: border-box;
}
}
</style>

@ -0,0 +1,238 @@
<!--
* @Author: BINGWU
* @Date: 2024-01-23 03:46:33
* @LastEditors: BINGWU HuJiaCheng2003@163.com
* @LastEditTime: 2024-02-16 20:57:57
* @FilePath: \bingwu-admin\src\layout\components\AsideCom.vue
* @Describe:
* @Mark: (˶ ˶)
-->
<template>
<div class="aside">
<el-menu
style="height: 100%"
:collapse="stateStore.isCollapse"
active-text-color="#ffd04b"
:default-active="$route.path"
router
>
<!-- 静态侧边栏 -->
<!-- <el-menu-item index="/home">
<el-icon> <i class="icon iconfont icon-home"></i></el-icon>
<template #title>首页</template>
</el-menu-item>
<el-menu-item index="/rich-text">
<el-icon> <i class="icon iconfont icon-fuwenben2"></i></el-icon>
<template #title>富文本编辑器</template>
</el-menu-item>
<el-menu-item index="/excel">
<el-icon> <i class="icon iconfont icon-excel"></i></el-icon>
<template #title>excel(导出/导入)</template>
</el-menu-item>
<el-menu-item index="/file">
<el-icon> <i class="icon iconfont icon-wenjian"></i></el-icon>
<template #title>文件上传</template>
</el-menu-item>
<el-menu-item index="/permission">
<el-icon> <i class="icon iconfont icon-quanxian"></i></el-icon>
<template #title>按钮权限管理</template>
</el-menu-item>
<el-menu-item index="/international">
<el-icon> <i class="icon iconfont icon-yuyan1"></i></el-icon>
<template #title>国际化</template>
</el-menu-item>
<el-sub-menu index="/error">
<template #title>
<el-icon> <i class="icon iconfont icon-cuowu"></i></el-icon>
<span>错误页面</span>
</template>
<el-menu-item index="/error/not-find1">错误页面1</el-menu-item>
<el-menu-item index="/error/not-find2">错误页面2</el-menu-item>
<el-menu-item index="/error/not-find3">错误页面3</el-menu-item>
</el-sub-menu>
<el-sub-menu index="/table">
<template #title>
<el-icon> <i class="icon iconfont icon-biaoge"></i></el-icon>
<span>表格</span>
</template>
<el-menu-item index="/table/main-table1">表格1</el-menu-item>
<el-menu-item index="/table/main-table2">表格2</el-menu-item>
</el-sub-menu>
<el-sub-menu index="/menu">
<template #title>
<el-icon> <i class="icon iconfont icon-cuowu"></i></el-icon>
<span>多级菜单</span>
</template>
<el-sub-menu>
<template #title>菜单1</template>
<el-menu-item index="/menu/menu-1-1">菜单1-1</el-menu-item>
<el-menu-item index="menu-1-2">菜单1-2</el-menu-item>
<el-sub-menu index="menu-1-3">
<template #title>菜单1-3</template>
<el-menu-item index="/menu/menu-1-1/menu-1-3-1"
>菜单1-3-1</el-menu-item
>
<el-menu-item index="menu-1-3-2">菜单1-3-2</el-menu-item>
</el-sub-menu>
</el-sub-menu>
</el-sub-menu> -->
<!-- 动态侧边栏 -->
<template v-for="item in userStore.asideData" :key="item">
<!-- 多级菜单 -->
<el-sub-menu
v-if="item.children && item.children.length > 0"
:index="item.index"
>
<template #title>
<el-icon><i :class="item.icon"></i></el-icon>
<span>{{ item.name }}</span>
</template>
<!-- 开始循环遍历children -->
<template v-for="child in item.children" :key="child">
<!-- 如果是三级菜单 -->
<template v-if="child.sons && child.sons.length > 0">
<el-sub-menu :index="child.index">
<template #title>{{ child.name }}</template>
<!-- 三级菜单的item -->
<el-menu-item
v-for="son in child.sons"
:key="son"
:index="son.index"
>{{ son.name }}</el-menu-item
>
</el-sub-menu></template
>
<!-- 二级菜单的item -->
<template v-else>
<el-menu-item :index="child.index">{{ child.name }}</el-menu-item>
</template>
</template>
</el-sub-menu>
<!-- 单级 -->
<el-menu-item v-else :index="item.index">
<el-icon> <i :class="item.icon"></i></el-icon>
<template #title>{{ item.name }}</template>
</el-menu-item>
</template>
</el-menu>
</div>
</template>
<script setup>
import { getStateStore, getUserStore } from '@/stores/index'
const stateStore = getStateStore()
const userStore = getUserStore()
// const data = [
// {
// index: '/home',
// icon: 'icon iconfont icon-home',
// name: ''
// },
// {
// index: '/rich-text',
// icon: 'icon iconfont icon-fuwenben2',
// name: ''
// },
// {
// index: '/excel',
// icon: 'icon iconfont icon-excel',
// name: 'excel(/)'
// },
// {
// index: '/file',
// icon: 'icon iconfont icon-wenjian',
// name: ''
// },
// {
// index: '/permission',
// icon: 'icon iconfont icon-quanxian',
// name: ''
// },
// {
// index: '/international',
// icon: 'icon iconfont icon-yuyan1',
// name: ''
// },
// //
// {
// index: '/table',
// icon: 'icon iconfont icon-biaoge',
// name: '',
// children: [
// {
// index: '/table/main-table1',
// name: '1'
// },
// {
// index: '/table/main-table2',
// name: '2'
// }
// ]
// },
// //
// {
// index: '/error',
// icon: 'icon iconfont icon-cuowu',
// name: '',
// children: [
// {
// index: '/error/not-find1',
// name: '1'
// },
// {
// index: '/error/not-find2',
// name: '2'
// },
// {
// index: '/error/not-find3',
// name: '3'
// }
// ]
// },
// //
// {
// index: '/menu',
// icon: 'icon iconfont icon-caidan',
// name: '',
// children: [
// {
// index: '/menu/menu-1-1',
// name: '1-1'
// },
// {
// index: '/menu/menu-1-2',
// name: '1-2'
// },
// {
// index: '/menu/menu-1-3',
// name: '1-3',
// sons: [
// {
// index: '/menu/menu-1-1/menu-1-3-1',
// name: '1-3-1'
// },
// {
// index: '/menu/menu-1-1/menu-1-3-2',
// name: '1-3-2'
// }
// ]
// }
// ]
// }
// ]
</script>
<style lang="scss" scoped>
.aside {
height: 100vh;
}
</style>

@ -0,0 +1,58 @@
<!--
* @Author: BINGWU
* @Date: 2024-01-23 03:46:53
* @LastEditors: BINGWU HuJiaCheng2003@163.com
* @LastEditTime: 2024-02-11 00:01:53
* @FilePath: \bingwu-admin\src\layout\components\HeaderCom.vue
* @Describe:
* @Mark: (˶ ˶)
-->
<template>
<div class="header">
<div class="left">
<CollapseAsideCom></CollapseAsideCom>
<el-breadcrumb
separator-icon="ArrowRight"
style="margin-left: 24px; transform: translateY(-1px)"
>
<el-breadcrumb-item
v-for="item in $route.matched"
:key="item"
:to="item.path"
>{{ item.meta.name }}</el-breadcrumb-item
>
</el-breadcrumb>
</div>
<div class="right">
<SwitchLanguageCom></SwitchLanguageCom>
<AvatarCom></AvatarCom>
<SettingCom></SettingCom>
</div>
</div>
</template>
<script setup>
import CollapseAsideCom from '@/components/utils/CollapseAsideCom.vue'
import SwitchLanguageCom from '@/components/utils/SwitchLanguageCom.vue'
import SettingCom from '@/components/SettingCom.vue'
import AvatarCom from '@/components/AvatarCom.vue'
</script>
<style lang="scss" scoped>
.header {
height: 100%;
display: flex;
justify-content: space-between;
align-items: center;
.left {
height: 100%;
display: flex;
align-items: center;
}
.right {
height: 100%;
display: flex;
align-items: center;
}
}
</style>

@ -0,0 +1,28 @@
/*
* @Author: BINGWU
* @Date: 2024-01-20 23:24:08
* @LastEditors: BINGWU HuJiaCheng2003@163.com
* @LastEditTime: 2024-02-15 14:29:07
* @FilePath: \bingwu-admin\src\main.js
* @Describe:
* @Mark: (˶ ˶)
*/
import 'element-plus/theme-chalk/dark/css-vars.css'
import 'normalize.css'
import { createApp } from 'vue'
import { createPinia } from 'pinia'
import piniaPluginPersistedstate from 'pinia-plugin-persistedstate'
import i18n from './language'
import App from './App.vue'
import router from './router'
import * as ElementPlusIconsVue from '@element-plus/icons-vue'
// 导入插件
import permission from './plugins/permission'
const app = createApp(App)
for (const [key, component] of Object.entries(ElementPlusIconsVue)) {
app.component(key, component)
}
const pinia = createPinia()
pinia.use(piniaPluginPersistedstate)
app.use(pinia).use(router).use(i18n).use(permission).mount('#app')

@ -0,0 +1,24 @@
import { getUserStore } from '@/stores'
// 判断权限的插件
export default {
install: (app, options) => {
// 权限内容存在user中
const userStroe = getUserStore()
console.log('我的permission插件')
// 使用自定义指令
app.directive('permission', {
mounted(el, binding) {
console.log('options', options)
console.log('binding', binding)
let permission = binding.value // 获取到传入 v-permission的值
if (permission) {
let hasPermission = userStroe.hasPermission(permission)
if (!hasPermission) {
// 没有权限 移除Dom元素
el.parentNode && el.parentNode.removeChild(el)
}
}
}
})
}
}

@ -0,0 +1,161 @@
/*
* ..Author: BINGWU
* ..Date: 2024-01-20 23:24:08
* ..LastEditors: BINGWU HuJiaCheng2003..163.com
* ..LastEditTime: 2024-02-15 19:48:22
* ..FilePath: \bingwu-admin\src\router\index.js
* ..Describe:
* ..Mark: (˶ ˶)
*/
import { createRouter, createWebHistory } from 'vue-router'
// 页面组件的文件路径
const modules = import.meta.glob('../views/**/**.vue')
import { getUserStore } from '@/stores'
const router = createRouter({
history: createWebHistory(import.meta.env.BASE_URL),
routes: [
{
path: '/',
component: () => import('../layout/IndexCom.vue'),
meta: {
name: '首页'
},
name: 'main',
children: [
// {
// path: 'home',
// name: 'home',
// component: () => import('../views/HomeView.vue')
// },
// {
// path: 'international',
// name: 'international',
// component: () => import('../views/InternationalView.vue'),
// meta: {
// name: '国际化'
// }
// },
// {
// path: 'table/main-table1',
// name: 'main-table',
// component: () => import('../views/table/MainTableView.vue'),
// meta: {
// name: '表格1'
// }
// },
// {
// path: 'excel',
// name: 'excel',
// component: () => import('../views/ExcelView.vue'),
// meta: {
// name: 'excel'
// }
// },
// {
// path: 'file',
// name: 'file',
// component: () => import('../views/FileView.vue'),
// meta: {
// name: 'file'
// }
// },
// {
// path: 'rich-text',
// name: 'rich-text',
// component: () => import('../views/RichTextView.vue'),
// meta: {
// name: 'rich-text'
// }
// },
// {
// path: 'permission',
// name: 'permission',
// component: () => import('../views/PermissionView.vue'),
// meta: {
// name: 'permission'
// }
// },
// // 三级路由
// {
// path: 'menu/menu-1-1',
// name: 'child-menu',
// component: () => import('../views/FatherMenu/FatherMenuView.vue'),
// meta: {
// name: '菜单1'
// }
// },
// {
// path: '/menu/menu-1-1/menu-1-3-1',
// name: 'father-menu',
// component: () =>
// import('../views/FatherMenu/ChildMenu/ChildMenuView.vue'),
// meta: {
// name: '菜单1'
// }
// }
]
},
{
path: '/error/not-find1',
name: 'not-find1',
component: () => import('../views/Error/NotFindView1.vue'),
meta: {
name: 'not-find1'
}
},
{
path: '/error/not-find2',
name: 'not-find2',
component: () => import('../views/Error/NotFindView2.vue'),
meta: {
name: 'not-find2'
}
},
{
path: '/error/not-find3',
name: 'not-find3',
component: () => import('../views/Error/NotFindView3.vue'),
meta: {
name: 'not-find3'
}
},
// 访问不存在的路由所跳转的页面
// {
// path: '/:catchAll(.*)',
// redirect: '/error/not-find2'
// },
{
path: '/login',
name: 'login',
component: () => import('../views/LoginView.vue'),
meta: {
name: 'login'
}
},
{
path: '/test',
name: 'test',
component: () => import('../views/test/TestView.vue'),
meta: {
name: 'test'
}
}
]
})
router.beforeEach((to, from) => {
if (to.name !== 'login') {
if (!getUserStore().token) {
return { name: 'login' }
}
}
// 处理动态路由刷新白屏
if (!to.redirectedFrom) {
getUserStore().addUserRoute(router, modules)
return { ...to, replace: true }
} else {
return true
}
})
export default router

@ -0,0 +1,9 @@
import useStateStore from './modules/state'
import useUserStore from './modules/user'
const getStateStore = () => {
return useStateStore()
}
const getUserStore = () => {
return useUserStore()
}
export { getStateStore, getUserStore }

@ -0,0 +1,21 @@
import { ref } from 'vue'
import { defineStore } from 'pinia'
const useStateStore = defineStore(
'state',
() => {
const isCollapse = ref(false)
const updateCollapse = () => {
isCollapse.value = !isCollapse.value
}
const currentLang = ref('zh-cn')
const updateCurrentLang = (lang) => {
currentLang.value = lang
}
return { isCollapse, updateCollapse, currentLang, updateCurrentLang }
},
{
persist: true
}
)
export default useStateStore

@ -0,0 +1,519 @@
/*
* @Author: BINGWU
* @Date: 2024-02-15 14:18:43
* @LastEditors: BINGWU HuJiaCheng2003@163.com
* @LastEditTime: 2024-04-11 17:38:14
* @FilePath: \app\src\stores\modules\user.js
* @Describe:
* @Mark: (˶ ˶)
*/
import { ref } from 'vue'
import { defineStore } from 'pinia'
const useUserStore = defineStore(
'user',
() => {
// 模拟后台返回的token
const token = ref('')
const setToken = (newToken) => {
token.value = newToken
}
// 模拟后台返回的侧边栏数据
const asideData = ref([
{
index: '/home',
icon: 'icon iconfont icon-shouye',
name: '首页'
},
//
{
index: '/user-manage',
icon: 'icon iconfont icon-guanliyuan_jiaoseguanli',
name: '用户管理'
},
{
index: '/employee-manage',
icon: 'icon iconfont icon-yuangongguanli',
name: '职工管理'
},
{
index: '/reward-manage',
icon: 'icon iconfont icon-wodejixiao',
name: '绩效管理'
},
// 二级
{
index: '/feedback',
icon: 'icon iconfont icon-xinxifankui',
name: '员工反馈与投诉管理',
children: [
{
index: '/feedback/manage',
name: '投诉管理'
},
{
index: '/feedback/view',
name: '员工反馈'
}
]
},
{
index: '/request',
icon: 'icon iconfont icon-qingjiatiao',
name: '请假管理',
children: [
{
index: '/request/manage',
name: '请假信息管理'
},
{
index: '/request/view',
name: '个人请假信息'
}
]
},
{
index: '/person',
icon: 'icon iconfont icon-geren',
name: '个人信息管理',
children: [
{
index: '/person/password',
name: '修改密码'
},
{
index: '/person/avatar',
name: '修改个人信息'
}
]
}
])
// 模拟后台返回的路由数据
const routerData = ref([
{
path: 'home',
name: 'home',
meta: {
// 路由组件的路径
url: '../views/HomeView.vue'
}
},
{
path: 'international',
name: 'international',
meta: {
name: '国际化',
url: '../views/InternationalView.vue'
}
},
{
path: 'table/main-table1',
name: 'main-table',
meta: {
name: '表格1',
url: '../views/table/MainTableView.vue'
}
},
{
path: 'excel',
name: 'excel',
meta: {
name: 'excel',
url: '../views/ExcelView.vue'
}
},
{
path: 'file',
name: 'file',
meta: {
name: 'file',
url: '../views/FileView.vue'
}
},
{
path: 'rich-text',
name: 'rich-text',
meta: {
name: 'rich-text',
url: '../views/RichTextView.vue'
}
},
{
path: 'permission',
name: 'permission',
meta: {
name: 'permission',
url: '../views/PermissionView.vue'
}
},
{
path: 'tx-yun',
name: 'tx-yun',
meta: {
name: 'tx-yun',
url: '../views/TxView.vue'
}
},
{
path: 'progress',
name: 'progress',
meta: {
name: 'progress',
url: '../views/ProgressView.vue'
}
},
{
path: 'preview-picture',
name: 'preview-picture',
meta: {
name: 'preview-picture',
url: '../views/PreviewPictureView.vue'
}
},
// 三级路由
{
path: 'menu/menu-1-1',
name: 'child-menu',
meta: {
name: '菜单1-1',
url: '../views/FatherMenu/FatherMenuView.vue'
}
},
{
path: '/menu/menu-1-1/menu-1-3-1',
name: 'father-menu',
meta: {
name: '菜单1-3-1',
url: '../views/FatherMenu/ChildMenu/ChildMenuView.vue'
}
},
//
{
path: 'user-manage',
name: 'user-manage',
meta: {
// 路由组件的路径
url: '../views/UserManageView.vue'
}
},
{
path: 'employee-manage',
name: 'employee-manage',
meta: {
// 路由组件的路径
url: '../views/EmployeeManageView.vue'
}
},
{
path: 'reward-manage',
name: 'reward-manage',
meta: {
// 路由组件的路径
url: '../views/RewardManageView.vue'
}
},
// 二级
{
path: 'feedback/manage',
name: 'feedback-manage',
meta: {
// 路由组件的路径
url: '../views/feedback/FeedbackManageView.vue'
}
},
{
path: 'feedback/view',
name: 'feedback-view',
meta: {
// 路由组件的路径
url: '../views/feedback/FeedbackView.vue'
}
},
{
path: 'request/manage',
name: 'request-manage',
meta: {
// 路由组件的路径
url: '../views/request/RequestManageView.vue'
}
},
{
path: 'request/view',
name: 'request-view',
meta: {
// 路由组件的路径
url: '../views/request/RequestView.vue'
}
},
{
path: 'person/password',
name: 'person-password',
meta: {
// 路由组件的路径
url: '../views/PersonInfo/ChangePasswordView.vue'
}
},
{
path: 'person/avatar',
name: 'person-avatar',
meta: {
// 路由组件的路径
url: '../views/PersonInfo/ChangeAvatarView.vue'
}
}
])
// 动态添加路由
const addUserRoute = (router, modules) => {
routerData.value.forEach((item) => {
router.addRoute('main', {
...item,
// item.url 匹配对应路由文件
component: modules[item.meta.url]
})
})
}
// 模拟后台返回的权限数组
const authArr = ref(['a', 'b', 'c', 'd', 'view', 'delete', 'edit'])
// 判断权限
const hasPermission = (auth) => {
return authArr.value.indexOf(auth) !== -1
}
// 切换用户
const switchUser = (username) => {
if (username === 'cxk') {
asideData.value = [
{
index: '/home',
icon: 'icon iconfont icon-home',
name: '首页'
},
{
index: '/rich-text',
icon: 'icon iconfont icon-fuwenben2',
name: '富文本编辑器'
},
{
index: '/excel',
icon: 'icon iconfont icon-excel',
name: 'excel(导出/导入)'
},
{
index: '/file',
icon: 'icon iconfont icon-wenjian',
name: '文件上传'
},
{
index: '/permission',
icon: 'icon iconfont icon-quanxian',
name: '按钮权限管理'
},
{
index: '/international',
icon: 'icon iconfont icon-yuyan1',
name: '国际化'
}
]
routerData.value = [
{
path: 'home',
name: 'home',
meta: {
// 路由组件的路径
url: '../views/HomeView.vue'
}
},
{
path: 'international',
name: 'international',
meta: {
name: '国际化',
url: '../views/InternationalView.vue'
}
},
{
path: 'excel',
name: 'excel',
meta: {
name: 'excel',
url: '../views/ExcelView.vue'
}
},
{
path: 'file',
name: 'file',
meta: {
name: 'file',
url: '../views/FileView.vue'
}
},
{
path: 'rich-text',
name: 'rich-text',
meta: {
name: 'rich-text',
url: '../views/RichTextView.vue'
}
},
{
path: 'permission',
name: 'permission',
meta: {
name: 'permission',
url: '../views/PermissionView.vue'
}
},
{
path: 'home',
name: 'home',
meta: {
// 路由组件的路径
url: '../views/HomeView.vue'
}
}
]
authArr.value = ['a', 'b']
} else {
asideData.value = [
{
index: '/permission',
icon: 'icon iconfont icon-quanxian',
name: '按钮权限管理'
},
// 二级
{
index: '/table',
icon: 'icon iconfont icon-biaoge',
name: '表格',
children: [
{
index: '/table/main-table1',
name: '表格1'
},
{
index: '/table/main-table2',
name: '表格2'
}
]
},
// 二级
{
index: '/error',
icon: 'icon iconfont icon-cuowu',
name: '错误页面',
children: [
{
index: '/error/not-find1',
name: '错误页面1'
},
{
index: '/error/not-find2',
name: '错误页面2'
},
{
index: '/error/not-find3',
name: '错误页面3'
}
]
},
// 三级
{
index: '/menu',
icon: 'icon iconfont icon-caidan',
name: '多级菜单',
children: [
{
index: '/menu/menu-1-1',
name: '菜单1-1'
},
{
index: '/menu/menu-1-2',
name: '菜单1-2'
},
{
index: '/menu/menu-1-3',
name: '菜单1-3',
sons: [
{
index: '/menu/menu-1-1/menu-1-3-1',
name: '菜单1-3-1'
},
{
index: '/menu/menu-1-1/menu-1-3-2',
name: '菜单1-3-2'
}
]
}
]
}
]
routerData.value = [
{
path: 'permission',
name: 'permission',
meta: {
name: 'permission',
url: '../views/PermissionView.vue'
}
},
{
path: 'table/main-table1',
name: 'main-table',
meta: {
name: '表格1',
url: '../views/table/MainTableView.vue'
}
},
// 三级路由
{
path: 'menu/menu-1-1',
name: 'child-menu',
meta: {
name: '菜单1-1',
url: '../views/FatherMenu/FatherMenuView.vue'
}
},
{
path: '/menu/menu-1-1/menu-1-3-1',
name: 'father-menu',
meta: {
name: '菜单1-3-1',
url: '../views/FatherMenu/ChildMenu/ChildMenuView.vue'
}
}
]
authArr.value = ['c', 'd']
}
}
return {
authArr,
hasPermission,
addUserRoute,
routerData,
setToken,
token,
asideData,
switchUser
}
},
{
persist: true
}
)
export default useUserStore

@ -0,0 +1,43 @@
/*
* @Author: BINGWU
* @Date: 2024-01-23 21:37:43
* @LastEditors: BINGWU HuJiaCheng2003@163.com
* @LastEditTime: 2024-04-11 16:27:14
* @FilePath: \app\src\utils\http.js
* @Describe:
* @Mark: (˶ ˶)
*/
import axios from 'axios'
const instance = axios.create({
baseURL: '/api'
})
// 添加请求拦截器
instance.interceptors.request.use(
(config) => {
// 在发送请求之前做些什么
config.headers.Authorization =
'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjI3MTI3Mjk3MTIsImlhdCI6MTcxMjcyOTcxMywiZGF0YSI6eyJ1c2VybmFtZSI6IkFsZ2ZkZ2RmZ2RpY2UiLCJwYXNzd29yZCI6IjEyZ2RmZ2QzIiwicm9sZV9kYXRhIjoiZGVmYXVsdCJ9fQ.cLo2NZ01z2umdbmnDBBuyekFrT8yTyvzBv8gHifzhKU'
return config
},
(error) => {
// 对请求错误做些什么
return Promise.reject(error)
}
)
// 添加响应拦截器
instance.interceptors.response.use(
(response) => {
// 2xx 范围内的状态码都会触发该函数。
// 对响应数据做点什么
return response
},
(error) => {
// 超出 2xx 范围的状态码都会触发该函数。
// 对响应错误做点什么
return Promise.reject(error)
}
)
export default instance

@ -0,0 +1,74 @@
/*
* @Author: BINGWU
* @Date: 2024-02-25 23:51:53
* @LastEditors: BINGWU HuJiaCheng2003@163.com
* @LastEditTime: 2024-03-27 00:16:03
* @FilePath: \bingwu-admin\src\utils\tencentCloud.js
* @Describe:
* @Mark: (˶ ˶)
*/
import COS from 'cos-js-sdk-v5' //引入腾讯云
//创建对象这里用的我的腾讯cos密钥
const cos = new COS({
SecretId: 'AKIDlSClKj183qKdnvw7A0a2MUQH3kMn2pUv',
SecretKey: 'e681kMoBYuCJG9virFg5Nhsz46A9wXfc'
})
const smallFileUpload = (file, fileName = '', updateProgress) =>
new Promise((resolve, reject) => {
cos.putObject(
{
Bucket: 'cloudshoping-1318477772' /* 填入您自己的存储桶,必须字段 */,
Region: 'ap-nanjing' /* 存储桶所在地域例如ap-beijing必须字段 */,
Key: fileName
? fileName
: file.raw
.name /* 存储在桶里的对象键例如1.jpga/b/test.txt必须字段 */,
StorageClass: 'STANDARD',
Body: file.raw,
onProgress: function (progressData) {
updateProgress(parseInt(100 * progressData.percent))
}
},
(err, data) => {
if (err) {
reject(err)
} else {
const url = 'https://' + data.Location
resolve(url)
}
}
)
})
const largeFileUpload = (file, fileName = '', updateProgress) =>
new Promise((resolve, reject) => {
cos.uploadFile(
{
Bucket: 'cloudshoping-1318477772' /* 填入您自己的存储桶,必须字段 */,
Region: 'ap-nanjing' /* 存储桶所在地域例如ap-beijing必须字段 */,
Key: fileName
? fileName
: file.raw
.name /* 存储在桶里的对象键例如1.jpga/b/test.txt必须字段 */,
Body: file.raw,
SliceSize:
1024 * 1024 * 5 /* 触发分块上传的阈值超过5MB使用分块上传非必须 */,
onProgress: function (progressData) {
/* 非必须 */
console.log(JSON.stringify(progressData))
updateProgress(parseInt(100 * progressData.percent))
}
},
function (err, data) {
console.log(err || data)
if (err) {
reject(err)
} else {
const url = 'https://' + data.Location
resolve(url)
}
}
)
})
export { smallFileUpload, largeFileUpload }

@ -0,0 +1,7 @@
<template>
<div>职工信息管理</div>
</template>
<script setup></script>
<style lang="scss" scoped></style>

@ -0,0 +1,73 @@
<template>
<div class="page-404">
<div class="main-container">
<div class="img-container container-item">
<img src="../../assets/error/notfind1/static/404.svg" />
</div>
<div class="text-container container-item">
<div class="code">404</div>
<div class="msg">你查看的页面貌似丢失了呢...</div>
<div class="action">返回首页,查看更多内容.</div>
</div>
</div>
</div>
</template>
<script setup></script>
<style lang="scss" scoped>
.page-404 {
width: 100vw;
height: 100vh;
background-color: rgba(223, 223, 255, 0.39);
display: flex;
justify-content: center;
align-items: center;
}
.main-container {
width: 80%;
max-width: 1000px;
max-height: 500px;
min-width: 600px;
background-color: white;
font-size: 0;
border-radius: 20px;
box-shadow: 0 0 50px 0 rgba(146, 146, 146, 0.63);
}
.main-container .container-item {
display: inline-block;
vertical-align: middle;
width: 50%;
}
.main-container .img-container {
background-color: rgba(253, 216, 168, 0.692);
border-top-left-radius: 20px;
border-bottom-left-radius: 20px;
}
.main-container .text-container .code {
font-size: clamp(150px, 20vw, 200px);
font-family: 'Arial Narrow';
color: rgb(86, 86, 253);
font-weight: bolder;
text-align: center;
}
.main-container .text-container .msg {
font-size: 18px;
text-align: center;
font-weight: 200;
margin-bottom: 20px;
}
.main-container .text-container .action {
font-size: 15px;
font-weight: 200;
text-align: center;
text-decoration-line: underline;
cursor: pointer;
}
</style>

@ -0,0 +1,157 @@
<!--
* @Author: BINGWU
* @Date: 2024-02-10 22:19:03
* @LastEditors: BINGWU HuJiaCheng2003@163.com
* @LastEditTime: 2024-02-10 22:57:10
* @FilePath: \bingwu-admin\src\views\Error\NotFindView2.vue
* @Describe:
* @Mark: (˶ ˶)
-->
<template>
<div class="content">
<div class="page-404">
<h1>404</h1>
<p>You are lost this page</p>
<p class="info">
you've reached the edge of universe.The Page you've requested , can't
befound. Don't Worry , you can return to previous page.
</p>
<button class="go-home btn">GO HOME</button>
<button class="back btn">BACK</button>
</div>
<div class="astronaut">
<img
src="../../assets/error/notfind2/astronaut.png"
class="astronaut-img"
alt=""
/>
</div>
</div>
</template>
<script setup></script>
<style lang="scss" scoped>
@font-face {
font-family: dogica;
src: url(../../assets/error/notfind2/font/dogica.ttf);
}
* {
padding: 0;
margin: 0;
}
.content {
width: 100vw;
height: 100vh;
overflow: hidden;
background: url(../../assets/error/notfind2/bg.png);
background-attachment: fixed;
background-size: cover;
.astronaut {
transform: rotate(0deg) translate(0, -18%);
animation-name: PAnimation;
animation-direction: alternate;
animation-timing-function: linear;
animation-iteration-count: infinite;
animation-duration: 3s;
}
.page-404 {
width: 40vw;
font-family: dogica;
z-index: 1;
h1 {
font-size: 5vw;
margin-bottom: 30px;
}
p {
font-size: 1vw;
margin: 10px 0;
line-height: 2vw;
}
.btn {
width: 10vw;
height: 50px;
border-radius: 30px;
margin-top: 10px;
cursor: pointer;
transition: 0.3s;
background-color: #fff;
color: #000;
border: 2px solid #000;
font-size: 1vw;
&:hover {
background-color: #000;
color: #fff;
}
}
}
}
@media (min-width: 1024px) {
.content {
position: relative;
display: flex;
.astronaut {
position: absolute;
left: 50%;
top: 50%;
}
.page-404 {
width: 40vw;
position: absolute;
left: 10%;
top: 40%;
transform: translateY(-50%);
}
}
}
@media (max-width: 1024px) {
.content {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
.page-404 {
width: 100%;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
margin-top: 10vh;
h1 {
font-size: 10vw;
margin-bottom: 30px;
}
p {
font-size: 3vw;
line-height: 6vw;
}
.info {
display: none;
}
.btn {
width: 30vw;
}
}
}
.astronaut {
width: 100%;
display: flex;
justify-content: center;
.astronaut-img {
width: 70%;
height: 80%;
transform: translateY(40%);
}
}
}
@keyframes PAnimation {
0% {
transform: rotate(0deg) translate(0, -15%);
}
100% {
transform: rotate(10deg) translate(0, -18%);
}
}
</style>

@ -0,0 +1,97 @@
<!--
* @Author: BINGWU
* @Date: 2024-02-10 23:16:48
* @LastEditors: BINGWU HuJiaCheng2003@163.com
* @LastEditTime: 2024-02-10 23:16:53
* @FilePath: \bingwu-admin\src\views\Error\NotFindView3.vue
* @Describe:
* @Mark: (˶ ˶)
-->
<template>
<div class="page-404">
<div class="number">404</div>
<div class="text"><span>页面找不到了</span><br />可能已经被删除</div>
</div>
</template>
<script setup></script>
<style lang="scss" scoped>
.page-404 {
margin: 0;
padding: 0;
display: -webkit-box;
display: flex;
-webkit-box-orient: horizontal;
-webkit-box-direction: normal;
flex-flow: row wrap;
align-content: center;
-webkit-box-pack: center;
justify-content: center;
}
div {
width: 100%;
text-align: center;
}
.number {
margin: 100px 0 0 0;
background: #fff;
position: relative;
font: 900 30vmin 'Consolas';
letter-spacing: 5vmin;
text-shadow:
2px -1px 0 #000,
4px -2px 0 #0a0a0a,
6px -3px 0 #0f0f0f,
8px -4px 0 #141414,
10px -5px 0 #1a1a1a,
12px -6px 0 #1f1f1f,
14px -7px 0 #242424,
16px -8px 0 #292929;
}
.number::before {
background-color: #673ab7;
background-image: radial-gradient(
closest-side at 50% 50%,
#ffc107 100%,
rgba(0, 0, 0, 0)
),
radial-gradient(closest-side at 50% 50%, #e91e63 100%, rgba(0, 0, 0, 0));
background-repeat: repeat-x;
background-size: 40vmin 40vmin;
background-position:
-100vmin 20vmin,
100vmin -25vmin;
width: 100%;
height: 100%;
mix-blend-mode: screen;
-webkit-animation: moving 10s linear infinite both;
animation: moving 10s linear infinite both;
display: block;
position: absolute;
content: '';
}
@-webkit-keyframes moving {
to {
background-position:
100vmin 20vmin,
-100vmin -25vmin;
}
}
@keyframes moving {
to {
background-position:
100vmin 20vmin,
-100vmin -25vmin;
}
}
.text {
font: 400 5vmin 'Courgette';
}
.text span {
font-size: 10vmin;
}
</style>

@ -0,0 +1,88 @@
<template>
<div class="excel">
<h3>导出</h3>
<ExportExcelCom @handleExportData="handleExportData"></ExportExcelCom>
<h3>导入</h3>
<ImportExcelCom @importData="importData"></ImportExcelCom>
<h3>表格</h3>
<BaseTableCom
:columnData="columnData"
:showSelect="true"
:tableData="tableData"
ref="baseTableComRef"
></BaseTableCom>
</div>
</template>
<script setup>
import ExportExcelCom from '@/components/excel/ExportExcelCom.vue'
import ImportExcelCom from '@/components/excel/ImportExcelCom.vue'
import BaseTableCom from '@/components/table/BaseTableCom.vue'
import { ref } from 'vue'
const baseTableComRef = ref(null)
//
const importData = (excelFilesData) => {
console.log('已被导入的文件的数据', excelFilesData)
ElMessage({
showClose: true,
message: '处理导入的数据',
type: 'success'
})
tableData.value = tableData.value.concat(excelFilesData)
}
//
let exportData = []
//
const columnData = [
{
prop: 'age',
label: '年龄'
},
{
prop: 'name',
label: '性别'
},
{
prop: 'hobby',
label: '爱好'
},
{
prop: 'project',
label: '专业'
}
]
const tableData = ref([
{
age: 18,
name: '永远18岁',
hobby: '夹声音',
project: '唱歌'
},
{
age: 19,
name: '永远18岁',
hobby: '夹声音',
project: '唱歌'
},
{
age: 20,
name: '永远18岁',
hobby: '夹声音',
project: '唱歌'
},
{
age: 21,
name: '永远18岁',
hobby: '夹声音',
project: '唱歌'
}
])
//
const handleExportData = (exportFile, fileName, fileExtension) => {
// exportData
exportData = baseTableComRef.value.getSelectRows()
exportFile(exportData, fileName, fileExtension)
}
</script>
<style lang="scss" scoped></style>

@ -0,0 +1,20 @@
<!--
* @Author: BINGWU
* @Date: 2024-02-01 00:31:58
* @LastEditors: BINGWU HuJiaCheng2003@163.com
* @LastEditTime: 2024-02-10 21:21:20
* @FilePath: \bingwu-admin\src\views\FatherMenu\ChiladMenu\ChildMenuView.vue
* @Describe:
* @Mark: (˶ ˶)
-->
<template>
<div>
ChildMenu
<!-- <RouterView /> -->
</div>
</template>
<script setup>
</script>
<style lang="scss" scoped></style>

@ -0,0 +1,16 @@
<!--
* @Author: BINGWU
* @Date: 2024-02-01 00:31:24
* @LastEditors: BINGWU HuJiaCheng2003@163.com
* @LastEditTime: 2024-02-01 00:32:18
* @FilePath: \bingwu-admin\src\views\FatherMenu\FatherMenu.vue
* @Describe:
* @Mark: (˶ ˶)
-->
<template>
<div>FatherMenu</div>
</template>
<script setup></script>
<style lang="scss" scoped></style>

@ -0,0 +1,54 @@
<template>
<div class="file">
<UploadFileCom
ref="uploadFileComRef"
accept=".jpg,.jpeg,.png"
@handleConfirm="handleConfirm"
>
<template #tip>选择文件的提示</template>
</UploadFileCom>
<div style="margin: 50px 0">
<el-button
@click="
() => {
const files = uploadFileComRef.getSelectFiles()
console.log('files:', files)
}
"
>获取已被选择的文件</el-button
>
</div>
<ProgressLiteCom ref="progressLiteComRef"></ProgressLiteCom>
</div>
</template>
<script setup>
import UploadFileCom from '@/components/file/UploadFileCom.vue'
import ProgressLiteCom from '@/components/progress/ProgressLiteCom.vue'
import { ref, toRaw } from 'vue'
const uploadFileComRef = ref([])
const progressLiteComRef = ref(null)
//
const handleConfirm = (selectFiles) => {
// toRawproxy
console.log('confirm', toRaw(selectFiles))
uploadFileComRef.value.offDialog()
ElMessage({
showClose: true,
message: '点击了确认按钮',
type: 'success'
})
//
const startWork = progressLiteComRef.value.start()
setTimeout(() => {
progressLiteComRef.value.end(startWork)
}, 3000)
}
</script>
<style lang="scss" scoped>
.file {
width: 30%;
}
</style>

@ -0,0 +1,19 @@
<!--
* @Author: BINGWU
* @Date: 2024-01-20 23:24:08
* @LastEditors: BINGWU HuJiaCheng2003@163.com
* @LastEditTime: 2024-02-16 23:44:10
* @FilePath: \bingwu-admin\src\views\HomeView.vue
* @Describe:
* @Mark: (˶ ˶)
-->
<template>
<div class="home">
<MyEcharts></MyEcharts>
</div>
</template>
<script setup>
import MyEcharts from '@/components/echarts/MyEcharts.vue'
</script>
<style lang="scss" scoped></style>

@ -0,0 +1,29 @@
<!--
* @Author: BINGWU
* @Date: 2024-01-24 17:35:53
* @LastEditors: BINGWU HuJiaCheng2003@163.com
* @LastEditTime: 2024-02-10 23:50:21
* @FilePath: \bingwu-admin\src\views\InternationalView.vue
* @Describe:
* @Mark: (˶ ˶)
-->
<template>
<div class="international">
<h1>国际化展示(局部国际化)</h1>
<h4>自定义内容国际化</h4>
<div>
<span>{{ $t('headMenus.name') }}:</span>
<span>{{ $t('headMenus.userName') }}</span>
</div>
<h4>element-plus组件的国际化</h4>
<div style="width: 30%">
<el-table mb-1 :data="[]" />
<el-pagination :total="100" />
</div>
<i class="icon iconfont icon-yuyan"></i>
</div>
</template>
<script setup></script>
<style lang="scss" scoped></style>

@ -0,0 +1,30 @@
<template>
<div class="login">
<div class="content">
<el-button type="primary" @click="login"></el-button>
</div>
</div>
</template>
<script setup>
import { useRouter } from 'vue-router'
import { getUserStore } from '@/stores'
const modules = import.meta.glob('../views/**/**.vue')
const router = useRouter()
const userStore = getUserStore()
const login = () => {
userStore.addUserRoute(router, modules)
userStore.setToken('my-token')
router.push('/home')
}
</script>
<style lang="scss" scoped>
.login {
display: flex;
justify-content: center;
.content {
margin-top: 100px;
}
}
</style>

@ -0,0 +1,55 @@
<!--
* @Author: BINGWU
* @Date: 2024-02-15 14:28:56
* @LastEditors: BINGWU HuJiaCheng2003@163.com
* @LastEditTime: 2024-02-16 21:12:52
* @FilePath: \bingwu-admin\src\views\PermissionView.vue
* @Describe:
* @Mark: (˶ ˶)
-->
<template>
<div class="permission">
<h1>按钮的权限管理</h1>
<!-- 四个按钮有a,b,c,d权限 -->
<el-button v-permission="'a'">a</el-button>
<el-button v-permission="'b'">b</el-button>
<el-button v-permission="'c'">c</el-button>
<el-button v-permission="'d'">d</el-button>
<hr />
<h4>用户切换</h4>
<el-button
type="primary"
@click="
() => {
swictUser('cxk')
}
"
>切换为用户cxk</el-button
>
<el-button
type="primary"
@click="
() => {
swictUser('admin')
}
"
>切换为用户admin</el-button
>
</div>
</template>
<script setup>
import { getUserStore } from '@/stores'
const userStore = getUserStore()
const swictUser = (usename) => {
userStore.switchUser(usename)
//
location.reload()
ElMessage({
message: `切换为用户${usename}`,
type: 'success'
})
}
</script>
<style lang="scss" scoped></style>

@ -0,0 +1,7 @@
<template>
<div>avatar管理</div>
</template>
<script setup></script>
<style lang="scss" scoped></style>

@ -0,0 +1,7 @@
<template>
<div>密码管理</div>
</template>
<script setup></script>
<style lang="scss" scoped></style>

@ -0,0 +1,36 @@
<!--
* @Author: BINGWU
* @Date: 2024-02-17 16:32:46
* @LastEditors: BINGWU HuJiaCheng2003@163.com
* @LastEditTime: 2024-02-17 18:01:52
* @FilePath: \bingwu-admin\src\views\PreviewPictureView.vue
* @Describe:
* @Mark: (˶ ˶)
-->
<template>
<div class="preview-picture">
<PreviewPictureCom ref="previewPictureComRef"> </PreviewPictureCom>
<div style="margin-top: 60px">
<el-button @click="getFileData" type="primary">获取图片文件</el-button>
</div>
</div>
</template>
<script setup>
import PreviewPictureCom from '@/components/PreviewPictureCom.vue'
import { ref } from 'vue'
const previewPictureComRef = ref(null)
const getFileData = () => {
const fileData = previewPictureComRef.value.fileFn().getFile()
const url = previewPictureComRef.value.imageUrlFn().getImageUrl()
console.log('file-data: ', fileData)
console.log('url: ', url.value)
previewPictureComRef.value
.imageUrlFn()
.setImageUrl(
'https://fuss10.elemecdn.com/e/5d/4a731a90594a4af544c0c25941171jpeg.jpeg'
)
}
</script>
<style lang="scss" scoped></style>

@ -0,0 +1,54 @@
<!--
* @Author: BINGWU
* @Date: 2024-02-17 19:59:25
* @LastEditors: BINGWU HuJiaCheng2003@163.com
* @LastEditTime: 2024-02-22 21:25:38
* @FilePath: \bingwu-admin\src\views\ProgressView.vue
* @Describe:
* @Mark: (˶ ˶)
-->
<template>
<div class="progress">
<h5>进度条</h5>
<ProgressLiteCom
ref="progressLiteComRef"
:time="data.time"
:increment="data.increment"
:progress-type="data.progressType"
></ProgressLiteCom>
<div style="margin-top: 60px">
<el-button @click="test" type="primary">测试</el-button>
</div>
</div>
</template>
<script setup>
import ProgressLiteCom from '@/components/progress/ProgressLiteCom.vue'
import { ref } from 'vue'
const progressLiteComRef = ref(null)
const data = {
//
time: 500,
// (100)
increment: 8,
//
progressType: {
type1: true,
type2: true,
type3: true
}
}
const test = () => {
//
const startWork = progressLiteComRef.value.start()
setTimeout(() => {
progressLiteComRef.value.end(startWork, 'error', '上传失败')
}, 4000)
}
</script>
<style lang="scss" scoped>
.progress {
width: 20%;
}
</style>

@ -0,0 +1,7 @@
<template>
<div>REWARD</div>
</template>
<script setup></script>
<style lang="scss" scoped></style>

@ -0,0 +1,73 @@
<!--
* @Author: BINGWU
* @Date: 2024-02-10 22:40:07
* @LastEditors: BINGWU HuJiaCheng2003@163.com
* @LastEditTime: 2024-02-16 21:59:15
* @FilePath: \bingwu-admin\src\views\RichTextView.vue
* @Describe:
* @Mark: (˶ ˶)
-->
<template>
<div class="rich-text">
<div style="border: 1px solid #ccc">
<Toolbar
style="border-bottom: 1px solid #ccc"
:editor="editorRef"
:defaultConfig="toolbarConfig"
:mode="mode"
/>
<Editor
style="height: 500px; overflow-y: hidden"
v-model="valueHtml"
:defaultConfig="editorConfig"
:mode="mode"
@onCreated="handleCreated"
/>
</div>
</div>
</template>
<script setup>
import '@wangeditor/editor/dist/css/style.css' // css
import { onBeforeUnmount, ref, shallowRef, onMounted } from 'vue'
import { Editor, Toolbar } from '@wangeditor/editor-for-vue'
// shallowRef
const editorRef = shallowRef()
// HTML
const valueHtml = ref('<p>hello</p>')
// ajax
onMounted(() => {
setTimeout(() => {
valueHtml.value = '<p>模拟 Ajax 异步设置内容</p>'
}, 1500)
})
const toolbarConfig = {}
const editorConfig = { placeholder: '请输入内容...', MENU_CONF: {} }
editorConfig.MENU_CONF['uploadImage'] = {
server: '/api/upload-image',
fieldName: 'custom-field-name'
}
//
onBeforeUnmount(() => {
const editor = editorRef.value
if (editor == null) return
editor.destroy()
})
const handleCreated = (editor) => {
editorRef.value = editor // editor
}
const mode = 'default'
</script>
<style lang="scss" scoped>
.rich-text {
width: 50%;
}
</style>

@ -0,0 +1,75 @@
<template>
<div>
<h1>腾讯储存桶测试</h1>
<el-upload
class="avatar-uploader"
:show-file-list="false"
:auto-upload="false"
:on-change="uploaderFile"
>
<img v-if="imageUrl" :src="imageUrl" class="avatar" />
<el-icon v-else class="avatar-uploader-icon"><Plus /></el-icon>
</el-upload>
<div style="margin-top: 60px">
<el-button @click="submit" type="primary">提交</el-button>
</div>
<h5>进度条</h5>
<ProgressCom ref="progressComRef"></ProgressCom>
</div>
</template>
<script setup>
import ProgressCom from '@/components/progress/ProgressCom.vue'
import { smallFileUpload, largeFileUpload } from '@/utils/tencentCloud'
import { ref } from 'vue'
//
const progressComRef = ref(null)
const uploadFile = ref(null)
const imageUrl = ref('')
const uploaderFile = (file) => {
console.log('file', file)
uploadFile.value = file
}
const update = (data) => {
progressComRef.value.changePercentage(data)
}
const submit = () => {
//
progressComRef.value.openProgress()
smallFileUpload(uploadFile.value, '', update).then((res) => {
progressComRef.value.offProgress()
imageUrl.value = res
})
}
</script>
<style lang="scss" scoped>
:deep(.avatar-uploader .avatar) {
width: 178px;
height: 178px;
display: block;
}
:deep(.avatar-uploader .el-upload) {
border: 1px dashed var(--el-border-color);
border-radius: 6px;
cursor: pointer;
position: relative;
overflow: hidden;
transition: var(--el-transition-duration-fast);
}
:deep(.avatar-uploader .el-upload:hover) {
border-color: var(--el-color-primary);
}
:deep(.el-icon.avatar-uploader-icon) {
font-size: 28px;
color: #8c939d;
width: 178px;
height: 178px;
text-align: center;
}
</style>

@ -0,0 +1,16 @@
<!--
* @Author: BINGWU
* @Date: 2024-04-11 16:53:10
* @LastEditors: BINGWU HuJiaCheng2003@163.com
* @LastEditTime: 2024-04-11 16:53:18
* @FilePath: \app\src\views\UserManageView.vue
* @Describe:
* @Mark: (˶ ˶)
-->
<template>
<div>用户管理</div>
</template>
<script setup></script>
<style lang="scss" scoped></style>

@ -0,0 +1,7 @@
<template>
<div>feedback管理</div>
</template>
<script setup></script>
<style lang="scss" scoped></style>

@ -0,0 +1,7 @@
<template>
<div>员工反馈</div>
</template>
<script setup></script>
<style lang="scss" scoped></style>

@ -0,0 +1,7 @@
<template>
<div>请假信息管理</div>
</template>
<script setup></script>
<style lang="scss" scoped></style>

@ -0,0 +1,16 @@
<!--
* @Author: BINGWU
* @Date: 2024-04-11 17:27:48
* @LastEditors: BINGWU HuJiaCheng2003@163.com
* @LastEditTime: 2024-04-11 17:28:00
* @FilePath: \app\src\views\request\RequestView.vue
* @Describe:
* @Mark: (˶ ˶)
-->
<template>
<div>请假请假</div>
</template>
<script setup></script>
<style lang="scss" scoped></style>

@ -0,0 +1,195 @@
<template>
<div class="main-table">
<BaseTableCom
:tableData="tableData"
:columnData="columnData"
:dropdownData="dropdownData"
:showSelect="true"
:pageSizes="[10, 15, 20]"
:total="100"
:showPagination="true"
messageBoxContent="确认要切换状态吗?"
messageBoxTitle="警告"
@updateSwitchState="updateSwitchState"
@updateTableData="updateTableData"
ref="baseTableComRef"
></BaseTableCom>
<div style="margin-top: 40px">
<el-button
@click="
() => {
// /
baseTableComRef.changeTableLoading()
}
"
>表格加载动画</el-button
>
<el-button
@click="
() => {
//
const rows = baseTableComRef.getSelectRows()
console.log('被勾选的行', rows)
}
"
>获取被勾选的行</el-button
>
<el-button
@click="
() => {
//
const paginationData = baseTableComRef.getPaginationData()
console.log('分页数据', paginationData)
}
"
>获取分页数据</el-button
>
</div>
</div>
</template>
<script setup>
import BaseTableCom from '@/components/table/BaseTableCom.vue'
import { ref } from 'vue'
const baseTableComRef = ref(null)
const tableData = [
{
// tag
// tag prop
tag: {
// tagNametagType
tagName: '成功',
tagType: 'success'
},
//
// picture prop
picture:
'https://fuss10.elemecdn.com/e/5d/4a731a90594a4af544c0c25941171jpeg.jpeg',
name: 'Tom1',
//
switch: false
},
{
tag: {
tagName: '信息',
tagType: 'info'
},
name: 'Tom2',
picture: 'https://placekitten.com/800/600',
switch: true
},
{
tag: {
tagName: '警告',
tagType: 'warning'
},
name: 'Tom3',
picture: 'https://placebear.com/800/600',
switch: true
},
{
tag: {
tagName: '默认',
tagType: 'default'
},
name: 'Tom4',
picture:
'https://loremflickr.com/cache/resized/65535_53040484749_6fd5663cd5_c_800_600_nofilter.jpg',
switch: false
},
{
tag: {
tagName: '危险',
tagType: 'danger'
},
name: 'Tom5',
picture:
'https://fastly.picsum.photos/id/875/800/600.jpg?hmac=NXX-SwYycWXsbCWcJJnivoEGENe8Mp4fvuHqATxgOSY',
switch: false
}
]
//
const dropdownData = [
{
command: 'command1',
handleAction: (row) => {
console.log('command1', row)
},
actionName: '删除',
// element
icon: 'Delete',
//
auth: 'delete'
},
{
command: 'command2',
handleAction: (row) => {
console.log('command2', row)
},
actionName: '查看',
icon: 'View',
auth: 'view'
},
{
command: 'conmmand3',
handleAction: (row) => {
console.log('command3', row)
},
actionName: '修改',
icon: 'Edit',
auth: 'edit'
}
]
//
const columnData = [
//
{
prop: 'picture',
label: '图片列',
// 使
pictureColumn: true
},
{
prop: 'name',
label: '普通数据列',
// px
width: '200',
// 使
sortable: true
},
// tag
{
prop: 'tag',
label: 'tag列',
// 使tag
tagColumn: true
},
//
{
prop: 'switch',
label: '开关列',
// 使
switchColumn: true
}
]
//
const updateSwitchState = (switchState) => {
console.log('改变后的switch状态', switchState)
//
baseTableComRef.value.changeSwitchLoading()
//
setTimeout(() => {
baseTableComRef.value.changeSwitchLoading()
}, 1000)
}
//
const updateTableData = (pageSize, currentPage) => {
console.log('页码', currentPage)
console.log('页面数据容量', pageSize)
}
</script>
<style lang="scss" scoped></style>

@ -0,0 +1,39 @@
<!--
* @Author: BINGWU
* @Date: 2024-02-16 18:42:11
* @LastEditors: BINGWU HuJiaCheng2003@163.com
* @LastEditTime: 2024-02-17 19:50:10
* @FilePath: \bingwu-admin\src\views\test\TestView.vue
* @Describe:
* @Mark: (˶ ˶)
-->
<template>
<div class="test">
<ProgressLiteCom ref="progressLiteComRef"></ProgressLiteCom>
<el-button type="primary" @click="test"></el-button>
</div>
</template>
<script setup>
import ProgressLiteCom from '@/components/progress/ProgressLiteCom.vue'
import { ref, onMounted } from 'vue'
import { getUser } from '@/api/user'
const progressLiteComRef = ref(null)
const test = () => {
const startWork = progressLiteComRef.value.start()
setTimeout(() => {
progressLiteComRef.value.end(startWork)
}, 5000)
}
onMounted(async () => {
await getUser().then((res) => {
console.log(res)
})
})
</script>
<style lang="scss" scoped>
.test {
padding-top: 40px;
}
</style>

@ -0,0 +1,43 @@
/*
* @Author: BINGWU
* @Date: 2024-01-20 23:24:08
* @LastEditors: BINGWU HuJiaCheng2003@163.com
* @LastEditTime: 2024-01-29 18:09:25
* @FilePath: \bingwu-admin\vite.config.js
* @Describe:
* @Mark: (˶ ˶)
*/
import { fileURLToPath, URL } from 'node:url'
import { defineConfig } from 'vite'
import AutoImport from 'unplugin-auto-import/vite'
import Components from 'unplugin-vue-components/vite'
import { ElementPlusResolver } from 'unplugin-vue-components/resolvers'
import vue from '@vitejs/plugin-vue'
// https://vitejs.dev/config/
export default defineConfig({
plugins: [
vue(),
AutoImport({
resolvers: [ElementPlusResolver()]
}),
Components({
resolvers: [ElementPlusResolver()]
})
],
resolve: {
alias: {
'@': fileURLToPath(new URL('./src', import.meta.url))
}
},
server: {
open: true, // 是否主动唤醒浏览器,
proxy: {
'/api': {
target: ' http://localhost:6699',
changeOrigin: true,
rewrite: (path) => path.replace(/^\/api/, '') // 不可以省略rewrite
}
}
}
})

@ -1,18 +1 @@
def execute_before(func):
def wrapper(*args, **kwargs):
# 在这里执行需要提前运行的代码
print("Executing code before the function")
# 然后调用原始函数
return func(*args, **kwargs)
return wrapper
@execute_before
def my_function(age):
print("My function")
print(age)
# 调用my_function将会先执行装饰器中的代码然后再执行my_function
my_function(9999)
print('运行成功')

@ -4,7 +4,7 @@ from mongoengine import Document, fields, DateTimeField
class FeedbackModel(Document):
content = fields.StringField(max_length=30)
content = fields.StringField(max_length=400)
employeeName = fields.StringField(max_length=30)
status = fields.StringField(max_length=30)
date = DateTimeField(default=datetime.now)

Loading…
Cancel
Save