大屏项目初始化

This commit is contained in:
2026-03-07 19:08:40 +08:00
parent b7bfdfd2bc
commit a052721622
22 changed files with 1203 additions and 85 deletions

View File

@@ -0,0 +1,12 @@
import request from '@/utils/request'
/**
* 获取指标信息列表
*/
export function getHomeModuleList(params) {
return request({
url: '/biz/homeModule/list',
method: 'get',
params: params
})
}

View File

@@ -325,9 +325,10 @@ const handleFullScreen = () => {
}
const handleBigScreen = () => {
if (!isMounted.value) return
const baseUrl = window.location.origin + "/#/bigScreen";
window.open(baseUrl, '_blank');
const { href } = router.resolve({
path: "/bigScreen"
});
window.open(href, '_blank');
}
const handleCommand = (cmd) => {

View File

@@ -3,10 +3,46 @@ import Login from '@/views/Login.vue'
import Page404 from '@/views/error/404.vue'
import Layout from '@/components/Layout/index.vue'
import Dashboard from '@/views/desktop/index.vue'
import bigScreen from '@/views/screen/index.vue'
import BigScreen from '@/views/screen/index.vue'
import BigScreenWork from '@/views/screen/Work/index.vue'
import { useUserStore } from '@/stores/user'
import { ElMessage } from 'element-plus'
const bigScreenModules = import.meta.glob('../views/screen/**/{index,list}.vue', {
eager: false,
import: 'default'
})
const generateBigScreenRoutes = () => {
const routes = []
Object.entries(bigScreenModules).forEach(([filePath, module]) => {
if (filePath === '../views/screen/index.vue') return;
const routePath = filePath
.replace('../views', '')
.replace('.vue', '')
.toLowerCase();
const finalPath = routePath;
const routeName = finalPath
.split('/')
.filter(Boolean)
.map(seg => seg.charAt(0).toUpperCase() + seg.slice(1))
.join('')
|| 'SystemRoleIndex';
routes.push({
path: finalPath,
name: routeName,
component: module,
meta: { requiresAuth: true }
})
})
return routes
}
const modules = import.meta.glob('../views/**/{index,list}.vue', {
eager: false,
import: 'default'
@@ -18,10 +54,10 @@ const generateRoutes = () => {
Object.entries(modules).forEach(([filePath, module]) => {
const excludePaths = [
'views/Login.vue',
'views/desktop/index.vue',
'views/error/',
'views/desktop/index.vue',
'views/error/',
'views/screen/',
]
]
if (excludePaths.some(path => filePath.includes(path))) {
return
}
@@ -40,7 +76,8 @@ const generateRoutes = () => {
routes.push({
path: routePath,
name: routeName || 'SystemRoleIndex',
component: module
component: module,
meta: { requiresAuth: true }
})
})
@@ -56,9 +93,22 @@ const routes = [
},
{
path: '/bigScreen',
name: 'bigScreen',
component: bigScreen,
meta: { requiresAuth: true }
name: 'BigScreen',
component: BigScreen,
meta: { requiresAuth: true },
children: [
{
path: '/bigScreenWork',
name: 'BigScreenWork',
component: BigScreenWork
},
...generateBigScreenRoutes(),
{
path: '/:pathMatch(.*)*',
name: 'BigScreen404',
component: Page404
}
]
},
{
path: '/',

View File

@@ -0,0 +1,147 @@
<template>
<el-form
:model="formData"
:rules="formRules"
ref="formRef"
label-width="100px"
class="dialog-form-container"
>
<div class="form-row">
<div class="form-col">
<el-form-item label="模块名称" prop="moduleName">
<el-input
v-model="formData.moduleName"
placeholder="请输入模块名称"
clearable
:disabled="isEdit"
/>
</el-form-item>
</div>
<div class="form-col">
<el-form-item label="模块编码" prop="moduleCode">
<el-input
v-model="formData.moduleCode"
placeholder="请输入模块编码"
clearable
:disabled="isEdit"
/>
</el-form-item>
</div>
</div>
<div class="form-row">
<div class="form-col">
<el-form-item label="大屏标题" prop="titleName">
<el-input
v-model="formData.titleName"
placeholder="请输入大屏标题"
clearable
:disabled="isEdit"
/>
</el-form-item>
</div>
<div class="form-col">
<el-form-item label="路由地址" prop="path">
<el-input
v-model="formData.path"
placeholder="请输入路由地址"
clearable
:disabled="isEdit"
/>
</el-form-item>
</div>
</div>
<div class="form-row">
<div class="form-col">
<el-form-item label="模块描述" prop="remark">
<el-input
v-model="formData.remark"
placeholder="请输入模块描述"
clearable
:disabled="isEdit"
/>
</el-form-item>
</div>
<div class="form-col">
<el-form-item label="模块状态" prop="ustatus">
<el-select
v-model="formData.ustatus"
placeholder="请选择模块状态"
clearable
>
<el-option label="停用" value="0" />
<el-option label="在用" value="1" />
<el-option label="锁定" value="2" />
</el-select>
</el-form-item>
</div>
</div>
</el-form>
</template>
<script setup>
import { ref, onMounted } from 'vue'
const props = defineProps({
formData: {
type: Object,
required: true,
default: () => ({
moduleName: '',
ustatus: '1',
})
},
isEdit: {
type: Boolean,
default: false
}
})
const formRef = ref(null)
const formRules = {
moduleName: [ { required: true, message: '请输入角色名称', trigger: 'blur' } ],
ustatus: [ { required: true, message: '请选择角色状态', trigger: 'change' } ],
}
const validate = async () => {
if (!formRef.value) return false
try {
const valid = await formRef.value.validate()
return valid
} catch (error) {
console.error('表单验证失败:', error)
return false
}
}
const resetForm = () => {
if (formRef.value) formRef.value.resetFields()
}
defineExpose({ validate, resetForm })
</script>
<style scoped>
.dialog-form-container {
width: 100%;
padding: 16px;
margin: 0;
border: 1px solid rgba(64, 158, 255, 0.15);
border-radius: 8px;
box-sizing: border-box;
}
.form-row {
display: flex;
flex-wrap: wrap;
width: 100%;
margin: 0 0 16px 0;
gap: 20px;
}
.form-row:last-child { margin-bottom: 0; }
.form-col { flex: 1; min-width: 180px; }
</style>

View File

@@ -0,0 +1,156 @@
<template>
<ResizablePage
:defaultWidth="200"
:minWidth="150"
:maxWidth="420"
bgColor="#fff"
>
<template #sidebar>
<div class="sidebar-content">
<FilterSelect
:list-data="listData"
node-key="groupId"
label-key="groupName"
@item-click="handleItemClick"
@edit="handleEdit"
@delete="handleDelete"
@view="handleView"
/>
</div>
</template>
<template #main>
<div class="main-content">
<vUser @refresh-role-list="getListData" />
</div>
</template>
</ResizablePage>
<PDialog
v-model="dialogVisible"
title="模块授权"
:loading="saveLoading"
@close="handleDialogClose"
@reset="handleDialogReset"
@confirm="handleSave"
>
<vRole ref="formComponentRef" :form-data="roleForm" />
</PDialog>
</template>
<script setup>
import { ref, onMounted } from 'vue'
import { ElMessage, ElMessageBox, ElNotification } from 'element-plus'
import ResizablePage from '@/components/Table/proResizable.vue'
import FilterSelect from '@/components/Table/proFilterSelect.vue'
import PDialog from '@/components/Dialog/proDialog.vue'
import { getHomeModuleGroupList } from '@/api/bizModule'
import vUser from './list.vue'
import vRole from './role.vue'
const FormValues = ref({
menuId: ''
});
const listData = ref([]);
const saveLoading = ref(false)
const dialogVisible = ref(false)
const roleForm = ref({})
const formComponentRef = ref(null)
const getListData = async () => {
try {
const res = await getHomeModuleGroupList({});
console.log(res )
// listData.value = res || []
} catch (error) {
console.log(error);
}
}
const handleItemClick = (item) => {
console.log('选中了:', item)
}
const handleEdit = (item) => {
roleForm.value = { ...item }
dialogVisible.value = true
}
const handleDelete = (item) => {
ElMessageBox.confirm('确定要删除当前角色吗?', '删除确认', {
type: 'warning',
closeOnClickModal: false,
showClose: false
})
.then(async () => {
const reqParams = {
roleId: item.roleId
}
const res = await getHomeRoleDelete(reqParams);
ElMessage.success(res.msg);
getListData();
})
.catch(() => {
ElMessage.info('已取消删除角色操作');
});
}
const handleView = (item) => {
console.log('查看', item)
}
const handleDialogClose = () => {
roleForm.value = {}
dialogVisible.value = false
}
const handleDialogReset = () => {
if (formComponentRef.value) {
formComponentRef.value.resetForm()
ElMessage.info('表单已重置')
}
}
const handleSave = async () => {
if (formComponentRef.value) {
const isValid = await formComponentRef.value.validate()
if (!isValid) {
return
}
const selectedMenuIds = formComponentRef.value.getSelectedMenuIds()
roleForm.value.menuIds = selectedMenuIds
}
const reqParams = {
... roleForm.value
}
saveLoading.value = true
const res = await getHomeRoleAssign(reqParams);
setTimeout(() => {
ElMessage.success(res.msg)
saveLoading.value = false
dialogVisible.value = false
getListData()
}, 500)
}
onMounted(() => {
getListData();
})
</script>
<style scoped>
.sidebar-content {
width: 100%;
height: 100%;
}
.main-content {
padding: 0px;
height: 100%;
}
</style>

View File

@@ -0,0 +1,277 @@
<template>
<div class="data-manage-page">
<CSearch
@search="handleSearch"
@reset="handleReset"
>
<el-form-item label="用户名称:" class="search-item">
<el-input
v-model="searchForm.uname"
placeholder="请输入用户名称"
clearable
class="search-input"
/>
</el-form-item>
<el-form-item label="登录账户:" class="search-item">
<el-input
v-model="searchForm.userName"
placeholder="请输入登录账户"
clearable
class="search-input"
/>
</el-form-item>
<el-form-item label="用户状态:" class="search-item">
<el-select
v-model="searchForm.ustatus"
placeholder="请选择用户状态"
clearable
class="search-select"
>
<el-option label="停用" value="0" />
<el-option label="在用" value="1" />
<el-option label="锁定" value="2" />
</el-select>
</el-form-item>
</CSearch>
<div class="main-wrapper">
<div class="main-section">
<div class="action-section">
<el-button type="primary" icon="Plus" @click="handleAdd">
新增
</el-button>
<el-button type="success" icon="Download" @click="handleExport">
导出
</el-button>
</div>
<STable
:table-data="tableData"
:loading="loading"
:pagination="pagination"
@size-change="handleSizeChange"
@current-change="handleCurrentChange"
>
<template #columns>
<el-table-column prop="createTime" label="记录日期" width="180" fixed="left" />
<el-table-column prop="userName" label="登录账户" />
<el-table-column prop="uname" label="用户名称" />
<el-table-column prop="sex" label="性别" />
<el-table-column prop="email" label="电子邮箱" width="225" show-overflow-tooltip="true" />
<el-table-column prop="phone" label="联系电话" />
<el-table-column prop="ustatus" label="状态" min-width="100" align="center">
<template #default="scope">
<el-tag
:type="scope.row.ustatus === '1' ? 'success' : scope.row.ustatus === '2' ? 'warning' : 'danger'"
>
{{ getStatusText(scope.row.ustatus) }}
</el-tag>
</template>
</el-table-column>
</template>
</STable>
</div>
</div>
<PDialog
v-model="dialogVisible"
:loading="saveLoading"
:title="isEdit ? '编辑数据' : '新增数据'"
@close="handleDialogClose"
@reset="handleDialogReset"
@confirm="handleSave"
>
<VForm ref="formComponentRef" :form-data="formData" :is-edit="isEdit" />
</PDialog>
</div>
</template>
<script setup>
import { ref, reactive, onMounted } from 'vue'
import { ElMessage } from 'element-plus'
import { Plus, Download, Edit, Delete } from '@element-plus/icons-vue'
import { getHomeUserList } from '@/api/bizUser'
import { getHomeRoleSave } from '@/api/bizRole'
import CSearch from '@/components/Search/proSearch.vue'
import STable from '@/components/Table/proTable.vue'
import PDialog from '@/components/Dialog/proDialog.vue'
import VForm from './form.vue'
const formComponentRef = ref(null)
const emit = defineEmits(['refresh-role-list'])
const loading = ref(false)
const saveLoading = ref(false)
const searchForm = reactive({
uname: '',
ustatus: '',
userName: ''
})
const pagination = reactive({
pageNum: 1,
pageSize: 20,
total: 0
})
const tableData = ref([])
const dialogVisible = ref(false)
const isEdit = ref(false)
const formData = ref({})
async function getDataList() {
loading.value = true
try {
const reqParmas = {
... searchForm,
pageNum: pagination.pageNum,
pageSize: pagination.pageSize,
}
const res = await getHomeUserList(reqParmas);
pagination.total = res.total;
tableData.value = res.list || [];
} catch (error) {
console.error('获取数据失败:', error);
tableData.value = []
} finally {
loading.value = false
}
}
const getStatusText = (status) => {
const statusMap = {
'0': '停用',
'1': '在用',
'2': '锁定'
}
return statusMap[status] || '未知'
}
const handleSearch = () => {
pagination.pageNum = 1
getDataList()
}
const handleReset = () => {
Object.assign(searchForm, {
uname: '',
ustatus: '',
userName: ''
})
pagination.pageNum = 1
getDataList()
}
const handleAdd = () => {
isEdit.value = false
formData.value = {}
dialogVisible.value = true
}
const handleEdit = (row) => {
isEdit.value = true
formData.value = { ...row }
dialogVisible.value = true
}
const handleExport = () => {
ElMessage.success('开始导出数据...')
}
const handleSizeChange = (val) => {
pagination.pageSize = val
getDataList()
}
const handleCurrentChange = (val) => {
pagination.pageNum = val
getDataList()
}
const handleDialogClose = () => {
formData.value = {}
isEdit.value = false
dialogVisible.value = false
}
const handleDialogReset = () => {
if (formComponentRef.value) {
formComponentRef.value.resetForm()
ElMessage.info('表单已重置')
}
}
const handleSave = async () => {
if (formComponentRef.value) {
const isValid = await formComponentRef.value.validate()
if (!isValid) {
ElMessage.warning('表单验证失败,请检查必填项')
return
}
}
try {
saveLoading.value = true;
const reqParams = {
...formData.value
}
const res = await getHomeRoleSave(reqParams);
ElMessage.success(res.msg)
dialogVisible.value = false
emit('refresh-role-list')
} catch (error) {
console.error('获取数据失败:', error);
} finally {
saveLoading.value = false;
}
}
onMounted(() => {
getDataList()
})
</script>
<style scoped>
.data-manage-page {
width: 100%;
height: 100%;
padding: 4px;
box-sizing: border-box;
display: flex;
flex-direction: column;
overflow: hidden !important;
}
.main-wrapper {
flex: 1;
width: 100%;
overflow: hidden;
min-height: 0;
border: 1px solid #e5e7eb;
border-radius: 6px;
margin: 0 !important;
padding: 0;
box-sizing: border-box;
}
.main-section {
width: 100%;
height: 100%;
display: flex;
flex-direction: column;
overflow: hidden;
}
.action-section {
padding: 12px 16px;
border-bottom: 1px solid #e5e7eb;
display: flex;
gap: 8px;
flex-shrink: 0;
}
:deep(.el-button .el-icon) {
margin-right: 4px;
}
</style>

View File

@@ -0,0 +1,246 @@
<template>
<div class="role-auth-container">
<el-form
:model="formData"
class="role-info-form"
label-width="80px"
inline
>
<el-form-item label="角色编号:" class="form-col">
<el-input
v-model="formData.roleId"
disabled
class="info-input"
/>
</el-form-item>
<el-form-item label="角色名称:" class="form-col">
<el-input
v-model="formData.roleName"
class="info-input"
/>
</el-form-item>
</el-form>
<div class="menu-tree-wrapper">
</div>
</div>
</template>
<script setup>
import { ref, reactive, onMounted, defineProps, defineExpose, watch } from 'vue'
import { ElMessage } from 'element-plus'
import { getTreeMenuList } from '@/api/bizMenu'
import { getHomeRoleMenus } from '@/api/bizRole'
const props = defineProps({
formData: {
type: Object,
required: true,
default: () => ({
roleId: '',
roleName: '',
menuIds: []
})
}
})
const menuTreeData = ref([])
const loading = ref(false)
const menuTreeRef = ref(null)
const selectedMenuIds = ref([])
const getLeafNodeIds = (treeData, leafIds = []) => {
treeData.forEach(node => {
if (!node.children || node.children.length === 0) {
leafIds.push(String(node.id))
} else {
getLeafNodeIds(node.children, leafIds)
}
})
return leafIds
}
const filterOnlyLeafIds = (allIds, treeData) => {
const allLeafIds = getLeafNodeIds(treeData)
return allIds.filter(id => allLeafIds.includes(id))
}
const getTreeListData = async () => {
try {
const res = await getTreeMenuList()
menuTreeData.value = res ?? []
return menuTreeData.value
} catch (error) {
console.error('加载菜单树失败:', error)
menuTreeData.value = []
return []
}
}
const getRoleMenuIds = async () => {
try {
const reqParams = { roleId: props.formData.roleId }
const res = await getHomeRoleMenus(reqParams)
return (res ?? []).map(item => String(item?.menuId)).filter(id => id)
} catch (error) {
console.error('加载角色菜单权限失败:', error)
return []
}
}
const treeProps = reactive({
label: 'label',
children: 'children',
disabled: 'disabled'
})
function getIndeterminateKeys(node) {
let keys = []
if (node.indeterminate) {
keys.push(node.key)
}
if (node.childNodes) {
node.childNodes.forEach(c => keys.push(...getIndeterminateKeys(c)))
}
return keys
}
function getCheckedAndIndeterminateKeys() {
if (!menuTreeRef.value) return []
const checked = menuTreeRef.value.getCheckedKeys(false)
const indeterminate = getIndeterminateKeys(menuTreeRef.value.root)
return [...new Set([...checked, ...indeterminate])]
}
onMounted(async () => {
loading.value = true
try {
const treeData = await getTreeListData()
const roleMenuIds = await getRoleMenuIds()
const propsMenuIds = (props.formData?.menuIds ?? []).map(id => String(id)).filter(id => id)
const allMenuIds = roleMenuIds.length > 0 ? roleMenuIds : propsMenuIds
const leafMenuIds = filterOnlyLeafIds(allMenuIds, treeData)
if (leafMenuIds.length > 0) {
selectedMenuIds.value = [...leafMenuIds]
setTimeout(() => {
if (menuTreeRef.value && treeData.length) {
menuTreeRef.value.setCheckedKeys(leafMenuIds, false)
}
}, 300)
}
} catch (error) {
console.error('初始化权限数据失败:', error)
} finally {
loading.value = false
}
})
watch(() => props.formData.menuIds, (newVal) => {
if (!newVal || !newVal.length || !menuTreeRef.value || !menuTreeData.value.length) return
const allMenuIds = newVal.map(id => String(id)).filter(id => id)
const leafMenuIds = filterOnlyLeafIds(allMenuIds, menuTreeData.value)
if (leafMenuIds.length) {
selectedMenuIds.value = [...leafMenuIds]
menuTreeRef.value.setCheckedKeys(leafMenuIds, false)
}
}, { deep: true })
const handleCheck = () => {
if (!menuTreeRef.value) return
selectedMenuIds.value = menuTreeRef.value.getCheckedKeys(false)
}
const handleCheckChange = () => {
handleCheck()
}
const validate = async () => {
if (selectedMenuIds.value.length === 0) {
ElMessage.warning('请至少选择一个菜单权限!')
return false
}
return true
}
const resetForm = () => {
if (menuTreeRef.value) {
menuTreeRef.value.setCheckedKeys([])
selectedMenuIds.value = []
}
}
defineExpose({
validate,
resetForm,
getSelectedMenuIds: () => getCheckedAndIndeterminateKeys(),
getLeafMenuIds: () => menuTreeRef.value ? menuTreeRef.value.getCheckedKeys(true) : []
})
</script>
<style scoped>
.role-auth-container {
width: 100%;
padding: 16px;
border: 1px solid rgba(64, 158, 255, 0.15);
border-radius: 8px;
box-sizing: border-box;
background: #fff;
}
.role-info-form {
margin-bottom: 16px;
padding-bottom: 12px;
border-bottom: 1px solid #e5e7eb;
width: 100%;
display: flex;
gap: 20px;
}
.form-col {
flex: 1;
min-width: 200px;
}
.info-input {
width: 100%;
}
:deep(.role-info-form .el-form-item) {
margin-bottom: 0;
width: 100%;
}
:deep(.role-info-form .el-input.is-disabled .el-input__inner) {
background-color: #f5f7fa;
color: #666;
}
.menu-tree-wrapper {
width: 100%;
}
.tree-content {
width: 100%;
height: 400px;
overflow-y: auto;
border: 1px solid #e5e7eb;
border-radius: 4px;
padding: 8px;
box-sizing: border-box;
}
:deep(.el-tree) {
--el-tree-node-content-hover-bg-color: #f5f7fa;
}
:deep(.el-tree-node__content) {
height: 36px;
line-height: 36px;
}
.tree-content::-webkit-scrollbar {
width: 6px;
}
.tree-content::-webkit-scrollbar-thumb {
background-color: #dcdfe6;
border-radius: 3px;
}
</style>

View File

@@ -9,17 +9,17 @@
<div class="erp-section erp-upper-section">
<div class="erp-col erp-col-1-3">
<div class="erp-card">
<ChartV01 :formParams="props.formParams" />
<ChartV01 :formParams="FormValues" />
</div>
</div>
<div class="erp-col erp-col-1-3">
<div class="erp-card">
<ChartV02 :formParams="props.formParams" />
<ChartV02 :formParams="FormValues" />
</div>
</div>
<div class="erp-col erp-col-1-3">
<div class="erp-card">
<ChartV03 :formParams="props.formParams" />
<ChartV03 :formParams="FormValues" />
</div>
</div>
</div>
@@ -29,19 +29,19 @@
<div class="erp-inner-layout">
<div class="erp-col erp-col-1-2">
<div class="erp-card">
<ChartV04 :formParams="props.formParams" />
<ChartV04 :formParams="FormValues" />
</div>
</div>
<div class="erp-col erp-col-1-2">
<div class="erp-card">
<ChartV05 :formParams="props.formParams" />
<ChartV05 :formParams="FormValues" />
</div>
</div>
</div>
</div>
<div class="erp-col erp-col-1-2">
<div class="erp-card">
<ChartV06 :formParams="props.formParams" />
<ChartV06 :formParams="FormValues" />
</div>
</div>
</div>
@@ -51,19 +51,19 @@
<div class="erp-inner-layout">
<div class="erp-col erp-col-1-2">
<div class="erp-card">
<ChartV07 :formParams="props.formParams" />
<ChartV07 :formParams="FormValues" />
</div>
</div>
<div class="erp-col erp-col-1-2">
<div class="erp-card">
<ChartV08 :formParams="props.formParams" />
<ChartV08 :formParams="FormValues" />
</div>
</div>
</div>
</div>
<div class="erp-col erp-col-1-2">
<div class="erp-card">
<ChartV09 :formParams="props.formParams" />
<ChartV09 :formParams="FormValues" />
</div>
</div>
</div>
@@ -72,6 +72,7 @@
<script setup>
import { ref, watch } from 'vue';
import { useRoute } from 'vue-router';
import ChartTop from './components/ChartTop.vue'
import ChartV01 from './components/ChartV01.vue';
import ChartV02 from './components/ChartV02.vue';
@@ -83,15 +84,17 @@ import ChartV07 from './components/ChartV07.vue';
import ChartV08 from './components/ChartV08.vue';
import ChartV09 from './components/ChartV09.vue';
const props = defineProps({
formParams: {
type: Object,
default: () => ({})
}
const route = useRoute();
const FormValues = ref({
reqParam: route.query.year
});
watch(
() => props.formParams,
() => route.query.year,
(newVal) => {
FormValues.value.reqParam = newVal;
},
{
deep: true,
immediate: true

View File

@@ -0,0 +1,8 @@
<template>
</template>
<script>
</script>
<style>
</style>

View File

@@ -9,34 +9,34 @@
<div class="work-section work-main-section">
<div class="work-col work-col-1-3 work-left-col">
<div class="work-card work-card-1-3">
<ChartV01 :formParams="props.formParams" />
<ChartV01 :formParams="FormValues" />
</div>
<div class="work-card work-card-1-3">
<ChartV02 :formParams="props.formParams" />
<ChartV02 :formParams="FormValues" />
</div>
<div class="work-card work-card-1-3">
<ChartV03 :formParams="props.formParams" />
<ChartV03 :formParams="FormValues" />
</div>
</div>
<div class="work-col work-col-1-3 work-middle-col">
<div class="work-card work-card-2-3">
<ChartV04 :formParams="props.formParams" />
<ChartV04 :formParams="FormValues" />
</div>
<div class="work-card work-card-1-3">
<ChartV05 :formParams="props.formParams" />
<ChartV05 :formParams="FormValues" />
</div>
</div>
<div class="work-col work-col-1-3 work-right-col">
<div class="work-card work-card-1-3">
<ChartV06 :formParams="props.formParams" />
<ChartV06 :formParams="FormValues" />
</div>
<div class="work-card work-card-1-3">
<ChartV07 :formParams="props.formParams" />
<ChartV07 :formParams="FormValues" />
</div>
<div class="work-card work-card-1-3">
<ChartV08 :formParams="props.formParams" />
<ChartV08 :formParams="FormValues" />
</div>
</div>
</div>
@@ -45,6 +45,7 @@
<script setup>
import { ref, watch } from 'vue';
import { useRoute } from 'vue-router';
import ChartTop from './components/ChartTop.vue'
import ChartV01 from './components/ChartV01.vue';
import ChartV02 from './components/ChartV02.vue';
@@ -55,16 +56,17 @@ import ChartV06 from './components/ChartV06.vue';
import ChartV07 from './components/ChartV07.vue';
import ChartV08 from './components/ChartV08.vue';
const props = defineProps({
formParams: {
type: Object,
default: () => ({})
}
const route = useRoute();
const FormValues = ref({
reqParam: route.query.year
});
watch(
() => props.formParams,
(newVal) => {},
() => route.query.year,
(newVal) => {
FormValues.value.reqParam = newVal;
},
{
deep: true,
immediate: true

View File

@@ -8,12 +8,19 @@
<div class="tabs-left">
<div
class="tab-item"
v-for="tab in allTabs"
:key="tab.moduleCode"
:class="{ active: activeTab === tab.moduleCode }"
@click="switchTab(tab.moduleCode)"
:class="{ active: isHome }"
@click="goHome"
>
<span>{{ tab.moduleName }}</span>
<span>首页</span>
</div>
<div
class="tab-item"
v-for="item in allTabs"
:key="item.moduleCode"
:class="{ active: isCurrentTab(item.path) }"
@click="switchTabByRoute(item)"
>
<span>{{ item.moduleName }}</span>
</div>
</div>
<div class="query-group">
@@ -27,60 +34,105 @@
</div>
</div>
</header>
<main class="screen-content">
<div v-if="activeTab === 'work'" class="screen-page">
<WorkIndex :formParams="FormValues" />
</div>
<div v-else-if="activeTab === 'werp'" class="screen-page">
<ErpIndex :formParams="FormValues" />
<div v-if="isHome" class="screen-page">
<HomePage />
</div>
<router-view v-else v-slot="{ Component }">
<div class="screen-page" v-if="Component">
<component :is="Component" />
</div>
</router-view>
</main>
</div>
</template>
<script setup>
import { ref, onMounted } from 'vue';
import ErpIndex from './Erp/index.vue';
import WorkIndex from './Work/index.vue';
import { ref, onMounted, computed } from 'vue';
import { useRouter, useRoute } from 'vue-router';
import { ElMessage } from 'element-plus';
import { getHomeModuleList } from '@/api/bizApi'
import HomePage from './Home/index.vue'
const screenTitle = ref('个人数字化分析看板');
const router = useRouter();
const route = useRoute();
const HOME_TITLE = "个人数字化分析看板";
const screenTitle = ref(HOME_TITLE);
const currentYear = new Date().getFullYear().toString();
const FormValues = ref({
reqParam: currentYear
const allTabs = ref([])
const queryDate = ref(currentYear);
const getTitle = (title) => {
screenTitle.value = title;
};
const isHome = computed(() => {
return route.path === '/bigScreen' || route.path === '/';
});
const allTabs = ref([])
const activeTab = ref('work')
const queryDate = ref();
const goHome = () => {
screenTitle.value = HOME_TITLE;
router.push('/bigScreen').catch(() => {});
};
const switchTab = (key) => {
activeTab.value = key
}
const isCurrentTab = (routePath) => {
return route.path === routePath;
};
const switchTabByRoute = (item) => {
getTitle(item.titleName);
if (!item.path) {
ElMessage.warning('该模块暂无对应路由');
return;
}
router.push({
path: item.path,
query: {
year: queryDate.value,
...route.query
}
}).catch(err => {
ElMessage.error('页面切换失败,请重试');
});
};
const handleQuery = () => {
FormValues.value.reqParam = queryDate.value;
}
if (!queryDate.value) {
ElMessage.warning('请选择查询年份');
return;
}
router.push({
path: route.path,
query: {
...route.query,
year: queryDate.value
}
});
};
async function getList() {
try {
const res = await getHomeModuleList()
allTabs.value = res || []
const res = await getHomeModuleList();
allTabs.value = res || [];
} catch (error) {
console.error(error)
allTabs.value = []
console.error('获取模块列表失败:', error);
allTabs.value = [];
}
}
const initApp = () =>{
queryDate.value = currentYear;
getList();
}
const initApp = () => {
getList();
};
onMounted(() => {
initApp();
router.afterEach((to) => {
if (to.query.year) {
queryDate.value = to.query.year;
}
});
});
</script>
@@ -325,6 +377,14 @@ onMounted(() => {
overflow: hidden;
}
.empty-page {
display: flex;
align-items: center;
justify-content: center;
color: #e0e6ff;
font-size: 18px;
}
@media (max-width: 1600px) {
.main-title {
font-size: 24px;

View File

@@ -9,7 +9,6 @@ import com.mini.mybigscreen.Model.Message;
import com.mini.mybigscreen.Model.Result;
import com.mini.mybigscreen.Model.TreeMenu;
import com.mini.mybigscreen.biz.domain.HomeMenu;
import com.mini.mybigscreen.biz.domain.HomeModule;
import com.mini.mybigscreen.biz.domain.HomeRoleMenu;
import com.mini.mybigscreen.biz.mapper.HomeMenuMapper;
import com.mini.mybigscreen.biz.service.HomeMenuService;

View File

@@ -2,12 +2,16 @@ package com.mini.mybigscreen.biz.controller;
import com.mini.mybigscreen.Model.Message;
import com.mini.mybigscreen.Model.Result;
import com.mini.mybigscreen.biz.service.HomeModuleGroupService;
import com.mini.mybigscreen.biz.service.HomeModuleService;
import jakarta.annotation.Resource;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
/**
* <p>
* 前端控制器
* 前端控制器
* </p>
*
* @author gaoxq
@@ -17,6 +21,13 @@ import org.springframework.web.bind.annotation.RestController;
@RequestMapping("/biz/homeModule")
public class HomeModuleController {
@Resource
private HomeModuleService moduleService;
@GetMapping("list")
public Result<?> getList() {
return Result.success(moduleService.list());
}
public Result<Message> save() {
return Result.success(new Message("数据新增成功", 200));

View File

@@ -0,0 +1,31 @@
package com.mini.mybigscreen.biz.controller;
import com.mini.mybigscreen.Model.Result;
import com.mini.mybigscreen.biz.service.HomeModuleGroupService;
import jakarta.annotation.Resource;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
/**
* <p>
* 前端控制器
* </p>
*
* @author gaoxq
* @since 2026-03-07
*/
@RestController
@RequestMapping("/biz/homeModuleGroup")
public class HomeModuleGroupController {
@Resource
private HomeModuleGroupService moduleGroupService;
@GetMapping("list")
public Result<?> getList(){
return Result.success(moduleGroupService.list());
}
}

View File

@@ -4,12 +4,8 @@ import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import java.io.Serial;
import java.io.Serializable;
import java.time.LocalDateTime;
import com.mini.mybigscreen.Model.BaseEntity;
import lombok.Getter;
import lombok.Setter;
@@ -19,14 +15,13 @@ import lombok.Setter;
* </p>
*
* @author gaoxq
* @since 2026-02-27
* @since 2026-03-07
*/
@Getter
@Setter
@TableName("biz_home_module")
public class HomeModule extends BaseEntity implements Serializable {
public class HomeModule implements Serializable {
@Serial
private static final long serialVersionUID = 1L;
@TableField("create_time")
@@ -47,9 +42,18 @@ public class HomeModule extends BaseEntity implements Serializable {
@TableField("module_code")
private String moduleCode;
@TableField("path")
private String path;
@TableField("title_name")
private String titleName;
/**
* 说明描述
*/
@TableField("remark")
private String remark;
@TableField("ustatus")
private String ustatus;
}

View File

@@ -0,0 +1,38 @@
package com.mini.mybigscreen.biz.domain;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import java.io.Serializable;
import java.time.LocalDateTime;
import lombok.Getter;
import lombok.Setter;
/**
* <p>
*
* </p>
*
* @author gaoxq
* @since 2026-03-07
*/
@Getter
@Setter
@TableName("biz_home_module_group")
public class HomeModuleGroup implements Serializable {
private static final long serialVersionUID = 1L;
@TableField("create_time")
private LocalDateTime createTime;
@TableId(value = "group_id", type = IdType.AUTO)
private String groupId;
@TableField("group_name")
private String groupName;
@TableField("ustatus")
private Integer ustatus;
}

View File

@@ -0,0 +1,16 @@
package com.mini.mybigscreen.biz.mapper;
import com.github.yulichang.base.MPJBaseMapper;
import com.mini.mybigscreen.biz.domain.HomeModuleGroup;
/**
* <p>
* Mapper 接口
* </p>
*
* @author gaoxq
* @since 2026-03-07
*/
public interface HomeModuleGroupMapper extends MPJBaseMapper<HomeModuleGroup> {
}

View File

@@ -9,7 +9,7 @@ import com.mini.mybigscreen.biz.domain.HomeModule;
* </p>
*
* @author gaoxq
* @since 2026-02-27
* @since 2026-03-07
*/
public interface HomeModuleMapper extends MPJBaseMapper<HomeModule> {

View File

@@ -0,0 +1,16 @@
package com.mini.mybigscreen.biz.service;
import com.mini.mybigscreen.biz.domain.HomeModuleGroup;
import com.baomidou.mybatisplus.extension.service.IService;
/**
* <p>
* 服务类
* </p>
*
* @author gaoxq
* @since 2026-03-07
*/
public interface HomeModuleGroupService extends IService<HomeModuleGroup> {
}

View File

@@ -0,0 +1,20 @@
package com.mini.mybigscreen.biz.service.impl;
import com.mini.mybigscreen.biz.domain.HomeModuleGroup;
import com.mini.mybigscreen.biz.mapper.HomeModuleGroupMapper;
import com.mini.mybigscreen.biz.service.HomeModuleGroupService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.springframework.stereotype.Service;
/**
* <p>
* 服务实现类
* </p>
*
* @author gaoxq
* @since 2026-03-07
*/
@Service
public class HomeModuleGroupServiceImpl extends ServiceImpl<HomeModuleGroupMapper, HomeModuleGroup> implements HomeModuleGroupService {
}

View File

@@ -0,0 +1,18 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.mini.mybigscreen.biz.mapper.HomeModuleGroupMapper">
<!-- 通用查询映射结果 -->
<resultMap id="BaseResultMap" type="com.mini.mybigscreen.biz.domain.HomeModuleGroup">
<id column="group_id" property="groupId" />
<result column="create_time" property="createTime" />
<result column="group_name" property="groupName" />
<result column="ustatus" property="ustatus" />
</resultMap>
<!-- 通用查询结果列 -->
<sql id="Base_Column_List">
create_time, group_id, group_name, ustatus
</sql>
</mapper>

View File

@@ -8,12 +8,15 @@
<result column="create_time" property="createTime" />
<result column="module_name" property="moduleName" />
<result column="module_code" property="moduleCode" />
<result column="path" property="path" />
<result column="title_name" property="titleName" />
<result column="remark" property="remark" />
<result column="ustatus" property="ustatus" />
</resultMap>
<!-- 通用查询结果列 -->
<sql id="Base_Column_List">
create_time, module_id, module_name, module_code, remark
create_time, module_id, module_name, module_code, path, title_name, remark, ustatus
</sql>
</mapper>