Conflicts:
	package-lock.json
This commit is contained in:
zouzhibing 2023-01-09 18:44:31 +08:00
commit 9c4dc55bd2
19 changed files with 1068 additions and 9647 deletions

9705
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -25,7 +25,7 @@
"core-js": "^3.6.5",
"dayjs": "^1.11.4",
"echarts": "^5.3.1",
"element-plus": "^2.2.21",
"element-plus": "^2.2.28",
"exceljs": "^4.3.0",
"file-saver": "^2.0.5",
"fuse.js": "^6.6.2",
@ -50,6 +50,7 @@
"vue-qr": "^4.0.6",
"vue-router": "^4.1.6",
"vue-splitpane": "^1.0.6",
"vue3-text-clamp": "^0.1.1",
"vuedraggable": "^4.1.0",
"vuex": "^4.0.0-0",
"xlsx": "^0.18.5"

View File

@ -381,4 +381,96 @@ export const menuData =[
},
]
export const dictionaryData = [
{
"id": 1,
"name": "性别",
"keyCode":'sex',
"createTime": "2011-02-25 18:37:39",
"remark": "性别",
},
{
"id": 2,
"name": "证件类型",
"keyCode":'idType',
"createTime": "2011-02-25 18:37:39",
"remark": "证件类型",
}
]
export const dictionaryDetailData = [
{
"id": 1,
"keyCode":'sex',
"name": "性别",
"createTime": "2011-02-25 18:37:39",
"remark": "性别",
"children":[
{
"id": 11,
"name": "男",
"key":1,
"pid":1,
"createTime": "2011-02-25 18:37:39",
"remark": "男",
},
{
"id": 12,
"name": "女",
"key":0,
"pid":1,
"createTime": "2011-02-25 18:37:39",
"remark": "女",
}
]
},
{
"id": 2,
"keyCode":'idType',
"name": "证件类型",
"createTime": "2011-02-25 18:37:39",
"remark": "证件类型",
"children":[
{
"id": 21,
"name": "身份证",
"key":1,
"pid":2,
"createTime": "2011-02-25 18:37:39",
"remark": "身份证",
},
{
"id": 22,
"name": "社保卡",
"key":2,
"pid":2,
"createTime": "2011-02-25 18:37:39",
"remark": "社保卡",
},
{
"id": 23,
"name": "驾驶证",
"key":3,
"pid":2,
"createTime": "2011-02-25 18:37:39",
"remark": "驾驶证",
},
{
"id": 24,
"name": "护照",
"key":4,
"pid":2,
"createTime": "2011-02-25 18:37:39",
"remark": "护照",
},
{
"id": 25,
"name": "工作证",
"key":5,
"pid":2,
"createTime": "2011-02-25 18:37:39",
"remark": "工作证",
}
]
}
]

View File

@ -37,6 +37,12 @@ const functionPageRouter = [{
name: 'fullscreen',
meta: { title: '元素 全屏', keepAlive: true , icon: 'MenuIcon'}
},
{
path: 'information-list',
component: () => import('@/views/functionPage/informationList/index.vue'),
name: 'informationList',
meta: { title: '信息列表', keepAlive: true , icon: 'MenuIcon'}
},
]
}]

View File

@ -95,7 +95,14 @@ const othersRouter = [{
component: () => import('@/views/other/count/index.vue'),
name: 'count',
meta: { title: '数字动画', icon: 'MenuIcon' }
}
},
{
path: 'text-clamp',
component: () => import('@/views/other/textClamp/index.vue'),
name: 'text-clamp',
meta: { title: '多行文本省略', icon: 'MenuIcon' }
},
]
}]

View File

@ -36,6 +36,12 @@ const systemRouter = [{
name: 'menu',
meta: { title: '菜单管理', icon: 'MenuIcon'}
},
{
path: 'dictionary',
component: () => import('@/views/system/dictionary/index.vue'),
name: 'dictionary',
meta: { title: '字典管理', icon: 'MenuIcon'}
},
]
}]

View File

@ -0,0 +1,100 @@
<template>
<el-card header="信息列表" class="m-list">
<el-row :gutter="20">
<el-col :span="6" v-for="item in 20">
<el-card class="inner" >
<div class="header">
<div class="title">
<div class="border"></div>
<div>年度考核目标</div>
<div class="text">张三 | 2022-12-30 10:38:24</div>
</div>
</div>
<div class="footer">
<div class="flex-center item-child">
<el-icon><View /></el-icon>
<span class="text">预览</span>
</div>
<div class="flex-center item-child">
<el-icon><Edit /></el-icon>
<span class="text">编辑</span>
</div>
<div class="flex-center item-child">
<el-icon><Delete /></el-icon>
<span class="text">删除</span>
</div>
</div>
</el-card>
</el-col>
</el-row>
<Pagination/>
</el-card>
</template>
<script lang="ts" setup>
import Pagination from './Pagination'
</script>
<style lang="scss" scoped>
.m-list{
margin-top: 10px;
.header{
padding: 20px;
position: relative;
display: flex;
.text{
margin-top: 10px;
font-size: 12px;
color: #D7D7D7;
}
.title{
position: relative;
padding-left: 20px;
}
}
.border{
left: 0;
position: absolute;
border-left:2px solid $primaryColor;
height: 100%;
}
.footer{
border-top: 1px solid #e4e7ed;
display: flex;
justify-content: space-between;
.text{
margin-left: 6px;
}
.item-child{
flex: 1;
padding: 10px 0;
color: #7F7F7F;
position: relative;
cursor: pointer;
font-size: 12px;
}
.item-child:after{
position: absolute;
right: 0;
content: '';
display: block;
height: 20px;
border-right:1px solid #e4e7ed;
}
.item-child:last-child:after{
display: none;
}
}
.inner{
::v-deep(.el-card__body){
padding: 0;
}
}
::v-deep(.el-col){
margin-bottom: 20px;
}
}
</style>

View File

@ -0,0 +1,35 @@
<template>
<div class="pagination">
<el-pagination
v-model:currentPage="currentPage"
:page-size="10"
background
layout="total, sizes, prev, pager, next, jumper"
:total="1000"
@size-change="handleSizeChange"
@current-change="handleCurrentChange"
/>
</div>
</template>
<script lang="ts" setup>
import {ref} from "vue";
const currentPage = ref(1)
const handleSizeChange = (val: number) => {
console.log(`${val} items per page`)
}
const handleCurrentChange = (val: number) => {
currentPage.value = val
}
</script>
<style lang="scss" scoped>
.pagination{
width: 100%;
display: flex;
justify-content: center;
}
</style>

View File

@ -0,0 +1,30 @@
<template>
<el-card>
<el-form :inline="true" :model="formInline" >
<el-form-item label="名称">
<el-input v-model="formInline.name" placeholder="请输入名称" />
</el-form-item>
<el-form-item>
<el-button type="primary" @click="onSubmit">搜索</el-button>
</el-form-item>
</el-form>
</el-card>
</template>
<script lang="ts" setup>
import { reactive } from 'vue'
const formInline = reactive({
name: '',
})
const onSubmit = () => {
console.log('submit!')
}
</script>
<style lang="scss" scoped>
::v-deep(.el-form-item){
margin-bottom: 0;
}
</style>

View File

@ -0,0 +1,69 @@
<template>
<div class="item-card">
<div class="zb-pro-checkcard" v-for="(item,index) in 5" @click="check(item,index)"
:key="item"
:class="{
'single-active':active===index
}"
>
<div class="pro-checkcard-content flex-justify-between">
<div>今日访问量</div>
<div class="num">561</div>
</div>
</div>
</div>
</template>
<script lang="ts" setup>
import {ref} from "vue";
let active = ref(0)
const check = (item,index)=>{
active.value = index
}
</script>
<style lang="scss" scoped>
.item-card{
display: flex;
flex-wrap: wrap;
}
.zb-pro-checkcard{
box-shadow: 0 2px 12px 0 #0000001a;
width: 19.2%;
margin-right: 1%;
margin-bottom: 10px;
cursor: pointer;
background: white;
position: relative;
.pro-checkcard-content{
width: 100%;
height: 100%;
padding: 20px 20px;
box-sizing: border-box;
}
.num{
font-weight: bold;
font-size: 16px;
}
.tool{
position: absolute;
top: 0;
right: 0;
.el-icon-error{
color: #F56C6C;
font-size: 16px;
}
}
}
.zb-pro-checkcard:nth-child(5n+5){
margin-right: 0;
}
.single-active{
background: #409eff;
color: white;
}
</style>

View File

@ -0,0 +1,18 @@
<template>
<div class="m-check-card app-container">
<SingleCheck/>
<Search/>
<List/>
</div>
</template>
<script lang="ts" setup>
import SingleCheck from './components/SingleCheck'
import Search from './components/Search'
import List from './components/List'
</script>
<style lang="scss" scoped>
</style>

View File

View File

@ -0,0 +1,32 @@
<template>
<PageWrapLayout>
<div class="text-clamp-wrap">
<text-clamp :text="str" :max-lines="3">
<template #after="{ toggle, expanded, clamped }">
<el-button type="primary" v-if="expanded || clamped" @click="toggle">{{ expanded?'收起':'展开' }}</el-button>
</template>
</text-clamp>
</div>
</PageWrapLayout>
</template>
<script lang="ts" setup>
import TextClamp from 'vue3-text-clamp';
const str = `Vue (读音 /vjuː/,类似于 view) 是一套用于构建用户界面的渐进式框架。与其它大型框架不同的是Vue 被设计为可以自底向上逐层应用。Vue 的核心库只关注视图层不仅易于上手还便于与第三方库或既有项目整合。另一方面当与现代化的工具链以及各种支持类库结合使用时Vue 也完全能够为复杂的单页应用提供驱动。`
const expand = ()=>{
}
</script>
<style lang="scss" scoped>
.text-clamp-wrap{
padding: 20px;
width: 300px;
background: #f8f8f8;
border: 1px solid #f1f1f1;
border-radius: 2px;
line-height: 2;
word-break: break-all;
}
</style>

View File

@ -0,0 +1,110 @@
<template>
<el-card class="m-dept-side" >
<div class="title">字典管理</div>
<el-button type="primary" @click="addDictsort">
<el-icon color="#fff"><Plus /></el-icon><span style="margin-left: 8px"></span>
</el-button>
<el-input v-model="filterText" placeholder="输入关键字进行过滤" class="filter-search"/>
<div class="filter-tree">
<el-scrollbar class="scrollbar">
<el-tree
ref="treeRef"
:data="tableData"
:props="defaultProps"
default-expand-all
:filter-node-method="filterNode"
>
<template #default="{ node, data }">
<span class="custom-tree-node" @click="selectAction(node, data)">
<span>{{ node.label }}</span>
<span v-if="data.id!=null">
<el-button @click.stop="editDictsort(data)" type="primary" link>编辑</el-button>
<el-button style="margin-left: 6px" type="danger" link @click.stop="remove(node, data)" >删除</el-button>
</span>
</span>
</template>
</el-tree>
</el-scrollbar>
</div>
<DictsortDialog ref="dictsortDialog"/>
</el-card>
</template>
<script lang="ts" setup>
import {onBeforeMount, ref, watch} from 'vue'
import { ElMessageBox, ElTree } from "element-plus";
import DictsortDialog from './dictsortDialog.vue'
import { dictionaryData } from '@/mock/system'
const emit = defineEmits(['change'])
const tableData = ref<Tree[]>(dictionaryData)
const dictsortDialog = ref(null)
interface Tree {
id: string
name: string
createTime?:string
remark?:string
children?: Tree[]
}
onBeforeMount( () => {
let allObj = {"id":null, "name": "全部"}
tableData.value = [allObj,...dictionaryData]
});
const filterText = ref('')
const treeRef = ref<InstanceType<typeof ElTree>>()
const defaultProps = {
children: 'children',
label: 'name',
value:'id'
}
//
watch(filterText, (val) => {
treeRef.value!.filter(val)
})
//
const filterNode = (value: string, data: Tree) => {
console.log(data)
if (!value) return true
return data.name.includes(value)
}
const addDictsort = ()=>{
dictsortDialog.value.show()
}
const editDictsort = (item)=>{
dictsortDialog.value.show(item)
}
const selectAction = (node, data)=>{
emit('change',data)
console.log('node, data============',node, data)
}
const remove = (node,data)=>{
ElMessageBox.confirm('你确定要删除当前项吗?', '温馨提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning',
draggable: true,
})
.then(() => {})
.catch(() => {})
console.log('data===',node,data)
}
</script>
<style lang="scss" scoped>
@import "../index.scss";
</style>

View File

@ -0,0 +1,160 @@
<template>
<div class="m-user-table">
<div class="header">
<el-form :inline="true" :model="formInline" ref="ruleFormRef">
<el-form-item label="名称" prop="username">
<el-input v-model="formInline.username" placeholder="请输入名称"/>
</el-form-item>
<el-form-item>
<el-button type="primary" @click="onSubmit" :icon="Search">查询</el-button>
<el-button @click="reset(ruleFormRef)">重置</el-button>
</el-form-item>
</el-form>
</div>
<div class="footer">
<div class="util">
<el-button type="primary" @click="addHandler">
<el-icon><Plus /></el-icon>
新增字典项
</el-button>
</div>
<div class="table-inner">
<el-table
v-loading="loading"
:data="tableData" style="width: 100%;height: 100%" border>
<el-table-column prop="id" label="id" align="center" width="100"/>
<el-table-column prop="name" label="名称" align="center" width="100"/>
<el-table-column prop="key" label="键值" align="center"/>
<el-table-column prop="remark"
:show-overflow-tooltip="true"
width="180"
label="描述" align="center"/>
<el-table-column prop="createTime" label="创建时间" align="center" width="180"/>
<el-table-column prop="operator" label="操作" width="200px" align="center" fixed="right">
<template #default="scope">
<el-button type="primary" size="small" icon="Edit" @click="editHandler(scope.row)">
编辑
</el-button>
<el-button @click="del(scope.row)" type="danger" size="small" icon="Delete">
删除
</el-button>
</template>
</el-table-column>
</el-table>
</div>
<div class="pagination">
<el-pagination
v-model:currentPage="currentPage1"
:page-size="10"
background
layout="total, sizes, prev, pager, next, jumper"
:total="1000"
@size-change="handleSizeChange"
@current-change="handleCurrentChange"
/>
</div>
</div>
<DictionaryEntryDialog ref="dictionaryEntryDialog"/>
</div>
</template>
<script lang="ts" setup>
import { ElMessageBox, ElMessage, FormInstance } from 'element-plus'
import {Search } from '@element-plus/icons-vue'
import {onMounted, reactive, ref} from 'vue'
import { dictionaryDetailData } from '@/mock/system'
import UserDialog from './userDialog.vue'
import DictionaryEntryDialog from './dictionaryEntryDialog.vue'
const tableData = ref(dictionaryDetailData[0].children)
const dialogVisible = ref(false)
const dictionaryEntryDialog = ref()
const ruleFormRef = ref<FormInstance>()
const formInline = reactive({})
const loading = ref(true)
const currentPage1 = ref(1)
const onSubmit = () => {
console.log('submit!', formInline)
loading.value = true
setTimeout(()=>{
loading.value = false
},1000)
}
const reset = (formEl: FormInstance | undefined) => {
loading.value = true
setTimeout(()=>{
loading.value = false
},1000)
}
const getList = (data)=>{
loading.value = true
if(!data.id){
tableData.value = []
}else {
let obj = dictionaryDetailData.find(item=>item.id===data.id)
tableData.value = obj.children
}
setTimeout(()=>{
loading.value = false
},500)
}
const addHandler = () => {
dictionaryEntryDialog.value.show()
}
const editHandler = (row) => {
dictionaryEntryDialog.value.show(row)
}
const del = (row) => {
ElMessageBox.confirm('你确定要删除当前项吗?', '温馨提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning',
draggable: true,
})
.then(() => {})
.catch(() => {})
}
const changeStatus = (row) => {
ElMessageBox.confirm(
`确定要${!row.status ? '禁用' : '启用'} ${row.username} 账户吗?`,
'温馨提示',
{
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning',
},
)
.then(async () => {})
.catch(() => {
row.status = !row.status
})
}
const handleSizeChange = (val: number) => {
console.log(`${val} items per page`)
}
const handleCurrentChange = (val: number) => {
currentPage1.value = val
}
onMounted(()=>{
setTimeout(()=>{
loading.value = false
},1000)
})
defineExpose({
getList
})
</script>
<style lang="scss" scoped>
@import "../index";
</style>

View File

@ -0,0 +1,106 @@
<template>
<el-dialog @close="close" v-model="dialogVisible" :title="title" width="50%">
<el-form
ref="ruleFormRef"
:model="ruleForm"
:rules="rules"
label-width="100px"
>
<el-form-item label="所属字典" prop="pid">
<el-cascader
v-model="ruleForm.pid"
style="width: 100%"
:options="dictionaryData"
:props="cascaderProps" clearable />
</el-form-item>
<el-form-item label="字典项名称" prop="name">
<el-input v-model="ruleForm.name" placeholder="请输入字典项名称"/>
</el-form-item>
<el-form-item label="字典项键值" prop="key">
<el-input v-model="ruleForm.key" placeholder="请输入字典项键值"/>
</el-form-item>
<el-form-item label="字典项描述" prop="remark" >
<el-input v-model="ruleForm.remark"
type="textarea"
placeholder="请输入字典项描述"/>
</el-form-item>
</el-form>
<template #footer>
<span class="dialog-footer">
<el-button @click="dialogVisible = false">取消</el-button>
<el-button type="primary" @click="handleClose(ruleFormRef)">确定</el-button>
</span>
</template>
</el-dialog>
</template>
<script lang="ts" setup>
import { ElMessageBox, ElMessage, FormInstance } from 'element-plus'
import {reactive, ref} from "vue";
const ruleFormRef = ref<FormInstance>()
const dialogVisible = ref<boolean>(false)
const title = ref('新增字典项')
import { dictionaryData } from '@/mock/system'
const cascaderProps = {
value: 'id',
label: 'name',
checkStrictly: true,
}
const rules = reactive({
name: [
{ required: true, message: '请输入名称', trigger: 'blur' },
{ min: 3, max: 5, message: '长度在 3 到 5 个字符', trigger: 'blur' },
],
key: [{required: true, message: '请输入字典项键值', trigger: 'blur',},],
pid: [{required: true, message: '请选择所属字典', trigger: 'change',},],
})
const ruleForm = reactive({
pid: '',
name: null,
remark: '',
key: null,
})
function close() {
ruleFormRef.value.resetFields()
Object.keys(ruleForm).forEach(key=>{
ruleForm[key] = null
})
}
const show = (item={})=>{
console.log('======item=======',item)
title.value = '新增字典项'
if(item.pid){
title.value = '编辑字典项'
Object.keys(item).forEach(key=>{
ruleForm[key] = item[key]
})
}
dialogVisible.value = true
}
const handleClose = async (done: () => void) => {
await ruleFormRef.value.validate((valid, fields) => {
if (valid) {
dialogVisible.value = false
console.log('submit!', ruleForm)
} else {
console.log('error submit!', fields)
}
})
}
defineExpose({
show,
})
</script>
<style lang="scss" scoped>
</style>

View File

@ -0,0 +1,104 @@
<template>
<el-dialog @close="close" v-model="dialogVisible" :title="title" width="50%">
<el-form
ref="ruleFormRef"
:model="ruleForm"
:rules="rules"
label-width="100px"
>
<el-form-item label="父级字典">
<el-cascader
v-model="ruleForm.pid"
style="width: 100%"
:options="dictionaryData"
placeholder="请选择父级字典,默认为根字典"
:props="cascaderProps" clearable />
</el-form-item>
<el-form-item label="字典名称" prop="name">
<el-input v-model="ruleForm.name" placeholder="请输入字典项名称"/>
</el-form-item>
<el-form-item label="字典编码" prop="keyCode">
<el-input v-model="ruleForm.keyCode" placeholder="请输入字典编码"/>
</el-form-item>
<el-form-item label="字典描述" prop="remark" >
<el-input v-model="ruleForm.remark"
type="textarea"
placeholder="请输入字典项描述"/>
</el-form-item>
</el-form>
<template #footer>
<span class="dialog-footer">
<el-button @click="dialogVisible = false">取消</el-button>
<el-button type="primary" @click="handleClose(ruleFormRef)">确定</el-button>
</span>
</template>
</el-dialog>
</template>
<script lang="ts" setup>
import { ElMessageBox, ElMessage, FormInstance } from 'element-plus'
import {reactive, ref} from "vue";
const ruleFormRef = ref<FormInstance>()
const dialogVisible = ref<boolean>(false)
const title = ref('新增字典项')
import { dictionaryData } from '@/mock/system'
const cascaderProps = {
value: 'id',
label: 'name',
checkStrictly: true,
}
const rules = reactive({
name: [
{ required: true, message: '请输入名称', trigger: 'blur' },
{ min: 3, max: 5, message: '长度在 3 到 5 个字符', trigger: 'blur' },
],
keyCode: [{required: true, message: '请输入字典编码', trigger: 'blur',},],
})
const ruleForm = reactive({
id: '',
name: null,
remark: '',
keyCode: null,
})
function close() {
ruleFormRef.value.resetFields()
Object.keys(ruleForm).forEach(key=>{
ruleForm[key] = null
})
}
const show = (item={})=>{
title.value = '新增字典项'
if(item.id){
title.value = '编辑字典项'
Object.keys(item).forEach(key=>{
ruleForm[key] = item[key]
})
}
dialogVisible.value = true
}
const handleClose = async (done: () => void) => {
await ruleFormRef.value.validate((valid, fields) => {
if (valid) {
dialogVisible.value = false
console.log('submit!', ruleForm)
} else {
console.log('error submit!', fields)
}
})
}
defineExpose({
show,
})
</script>
<style lang="scss" scoped>
</style>

View File

@ -0,0 +1,108 @@
.m-user{
display: flex;
flex-direction: row;
}
.m-user-table{
display: flex;
flex-direction: column;
flex: 1;
position: relative;
width: calc(100% - 230px);
.header{
display: flex;
padding: 16px 16px 0px 16px;
margin-bottom: 16px;
border-radius: 4px;
background: white;
box-shadow: 0 0 12px rgb(0 0 0 / 5%);
}
.footer{
flex: 1;
display: flex;
padding: 16px;
flex-direction: column;
border-radius: 4px;
overflow: hidden;
background: white;
box-shadow: 0 0 12px rgb(0 0 0 / 5%);
position: relative;
box-sizing: border-box;
.util{
margin-bottom: 15px;
display: flex;
justify-content: flex-end;
flex-shrink: 0;
}
.table-inner{
flex: 1;
position: relative;
}
.table{
position: absolute;
left: 0;
top: 0;
width: 100%;
height: 100%
}
}
.pagination{
width: 100%;
display: flex;
justify-content: flex-end;
padding-top: 20px;
box-sizing: border-box;
flex-shrink: 0;
}
}
.custom-tree-node{
flex: 1;
display: flex;
align-items: center;
justify-content: space-between;
}
.m-dept-side{
box-sizing: border-box;
width: 220px;
height: 100%;
padding: 18px;
margin-right: 10px;
flex-shrink: 0;
::v-deep(.el-card__body){
width: 100%;
height: 100%;
display: flex;
flex-direction: column;
overflow: hidden;
padding: 0!important;
.el-tree-node__content{
height: 33px;
}
.el-tree{
}
}
.filter-search{
flex-shrink: 0;
margin-bottom: 10px;
margin-top: 10px;
}
.title{
flex-shrink: 0;
margin: 0 0 15px;
font-size: 18px;
font-weight: 700;
}
.scrollbar{
position: absolute;
width: 100%;
height: 100%;
overflow: auto;
}
.filter-tree{
flex: 1;
overflow: hidden;
position: relative;
}
}

View File

@ -0,0 +1,22 @@
<template>
<div class="app-container m-user">
<Side @change="changeAction"/>
<Table ref="table"/>
</div>
</template>
<script lang="ts" setup>
import Table from './components/Table.vue'
import Side from './components/Side.vue'
import { ref } from "vue";
const table = ref()
const changeAction = (data)=>{
table.value.getList(data)
}
</script>
<style scoped lang="scss">
@import "./index";
</style>