You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

546 lines
14 KiB
Vue

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

<template>
<ContentWrap>
<el-form
class="-mb-15px"
:model="queryParams"
ref="queryFormRef"
:inline="true"
label-width="80px"
>
<el-form-item label="名称" prop="name">
<el-input
v-model="queryParams.name"
placeholder="请输入名称"
clearable
@keyup.enter="handleQuery"
class="!w-240px"
/>
</el-form-item>
<el-form-item label="备注" prop="remark">
<el-input
v-model="queryParams.remark"
placeholder="请输入备注"
clearable
@keyup.enter="handleQuery"
class="!w-240px"
/>
</el-form-item>
<el-form-item label="启用状态" prop="state">
<el-select
v-model="queryParams.state"
placeholder="请选择启用状态"
clearable
class="!w-240px"
>
<el-option
v-for="dict in getStrDictOptions(DICT_TYPE.COMMON_STATUS)"
:key="dict.value"
:label="dict.label"
:value="dict.value"
/>
</el-select>
</el-form-item>
<el-form-item>
<el-button type="primary" @click="openCreateDialog">
<Icon icon="ep:plus" class="mr-5px" /> 新增
</el-button>
<el-button @click="handleQuery">
<Icon icon="ep:search" class="mr-5px" /> 搜索
</el-button>
<el-button @click="resetQuery">
<Icon icon="ep:refresh" class="mr-5px" /> 重置
</el-button>
</el-form-item>
</el-form>
</ContentWrap>
<ContentWrap>
<div class="dashboard-card-list">
<el-empty v-if="!loading && list.length === 0" description="暂无数据" />
<el-row v-else :gutter="16">
<el-col
v-for="item in list"
:key="item.id"
:xl="6"
:lg="8"
:md="12"
:sm="24"
:xs="24"
class="mb-16px"
>
<el-card shadow="hover" class="dashboard-card" :body-style="{ padding: '0' }">
<div class="dashboard-card-image-wrapper" @click="handlePreview(item)">
<img
class="dashboard-card-image"
:src="item.indexImage || defaultImage"
alt="封面图"
/>
<div class="dashboard-card-state">
<el-tag v-if="item.state === 1" type="success" size="small">
启用
</el-tag>
<el-tag v-else type="info" size="small">
禁用
</el-tag>
</div>
</div>
<div class="dashboard-card-body">
<div class="dashboard-card-main">
<div class="dashboard-card-title" :title="item.name">
{{ item.name || '-' }}
</div>
<div class="dashboard-card-remark" :title="item.remark">
{{ item.remark || '暂无描述' }}
</div>
</div>
<div class="dashboard-card-actions">
<el-popover placement="bottom-end" trigger="click" width="140">
<div class="dashboard-card-menu">
<el-button type="primary" text @click="openEditDialog(item)">
编辑
</el-button>
<el-button type="danger" text @click="handleDelete(item)">
删除
</el-button>
</div>
<template #reference>
<el-button text circle>
<Icon icon="ep:more" />
</el-button>
</template>
</el-popover>
</div>
</div>
</el-card>
</el-col>
</el-row>
<Pagination
v-if="total > 0"
:total="total"
v-model:page="queryParams.pageNo"
v-model:limit="queryParams.pageSize"
@pagination="getList"
/>
</div>
</ContentWrap>
<el-dialog
v-model="createDialogVisible"
:title="dialogMode === 'create' ? '新增数据大屏' : '编辑数据大屏'"
width="600px"
draggable
>
<el-form :model="createForm" ref="createFormRef" label-width="80px" :rules="createFormRules">
<el-form-item label="名称" prop="name">
<el-input v-model="createForm.name" placeholder="请输入名称" />
</el-form-item>
<el-form-item label="大屏类型" prop="type">
<el-select v-model="createForm.type" placeholder="请选择大屏类型" class="!w-240px">
<el-option
v-for="dict in getStrDictOptions('mes_goview_type')"
:key="dict.value"
:label="dict.label"
:value="dict.value"
/>
</el-select>
</el-form-item>
<el-form-item label="产线" prop="orgId">
<el-tree-select
v-model="createForm.orgId"
:data="organizationTree"
:props="lineTreeProps"
filterable
clearable
class="!w-240px"
placeholder="请选择产线"
@change="handleOrgChange"
/>
</el-form-item>
<el-form-item label="设备" v-if="createForm.type === '1'">
<el-select
v-model="selectedDeviceIds"
multiple
:multiple-limit="8"
collapse-tags
collapse-tags-tooltip
placeholder="请选择设备必须选择8个"
clearable
filterable
class="!w-240px"
>
<el-option
v-for="item in deviceList"
:key="item.id"
:label="item.deviceName"
:value="String(item.id)"
/>
</el-select>
</el-form-item>
<el-form-item label="内容">
<el-input
v-model="createForm.content"
type="textarea"
:rows="4"
placeholder="请输入内容"
/>
</el-form-item>
<el-form-item label="备注">
<el-input v-model="createForm.remark" placeholder="请输入备注" />
</el-form-item>
<el-form-item label="启用状态">
<el-switch
v-model="createForm.state"
:active-value="1"
:inactive-value="0"
/>
</el-form-item>
</el-form>
<template #footer>
<el-button @click="createDialogVisible = false">取 消</el-button>
<el-button type="primary" :loading="createLoading" @click="submitDialog">
确 定
</el-button>
</template>
</el-dialog>
</template>
<script setup lang="ts">
import { getStrDictOptions, DICT_TYPE } from '@/utils/dict'
import request from '@/config/axios'
import defaultImage from '@/assets/imgs/logo.png'
import { OrganizationApi } from '@/api/mes/organization'
import { handleTree } from '@/utils/tree'
import { DeviceApi } from '@/api/iot/device'
defineOptions({ name: 'DashboardList' })
const router = useRouter()
const message = useMessage()
interface DashboardItem {
id: number
name: string
remark: string
state: number
indexImage?: string
route?: string
content?: string
type?: string
orgId?: number | string
orgName?: string
deviceIds?: string
}
const loading = ref(false)
const list = ref<DashboardItem[]>([])
const total = ref(0)
const queryParams = reactive({
pageNo: 1,
pageSize: 10,
name: undefined as string | undefined,
remark: undefined as string | undefined,
state: undefined as string | undefined
})
const queryFormRef = ref()
const createDialogVisible = ref(false)
const createLoading = ref(false)
const createFormRef = ref()
const dialogMode = ref<'create' | 'edit'>('create')
const editingId = ref<number | null>(null)
const createForm = reactive({
name: '',
remark: '',
state: 1,
type: '',
orgId: undefined as number | string | undefined,
orgName: '',
deviceIds: '',
indexImage: '',
route: '',
content: ''
})
const createFormRules = reactive({
name: [{ required: true, message: '名称不能为空', trigger: 'blur' }],
type: [{ required: true, message: '大屏类型不能为空', trigger: 'change' }],
orgId: [{ required: true, message: '产线不能为空', trigger: 'change' }]
})
const organizationTree = ref<any[]>([])
const lineTreeProps = {
label: 'name',
children: 'children',
value: 'id'
}
const deviceList = ref<any[]>([])
const selectedDeviceIds = ref<string[]>([])
const getList = async () => {
loading.value = true
try {
const data = await request.get({
url: '/mes/goview/page',
params: queryParams
})
list.value = (data?.list || []) as DashboardItem[]
total.value = data?.total || 0
} finally {
loading.value = false
}
}
const handleQuery = () => {
queryParams.pageNo = 1
getList()
}
const resetQuery = () => {
queryFormRef.value?.resetFields?.()
handleQuery()
}
const handlePreview = (item: DashboardItem) => {
const typeRoute = getRouteByType(item.type)
const route = typeRoute || item.route || ''
if (!route) {
message.error('未配置预览路由')
return
}
const path = route.startsWith('/') ? route : `/${route}`
const queryParams = new URLSearchParams()
if (item.orgId) queryParams.append('orgId', String(item.orgId))
if (item.type === '1' && item.deviceIds) queryParams.append('deviceIds', item.deviceIds)
const queryString = queryParams.toString()
const url = router.resolve(path + (queryString ? `?${queryString}` : '')).href
window.open(url, '_blank')
}
const resetCreateForm = () => {
createForm.name = ''
createForm.remark = ''
createForm.state = 1
createForm.type = ''
createForm.orgId = undefined
createForm.orgName = ''
createForm.deviceIds = ''
createForm.indexImage = ''
createForm.route = ''
createForm.content = ''
selectedDeviceIds.value = []
}
const openCreateDialog = () => {
dialogMode.value = 'create'
editingId.value = null
resetCreateForm()
createDialogVisible.value = true
}
const openEditDialog = (item: DashboardItem) => {
dialogMode.value = 'edit'
editingId.value = item.id
createForm.name = item.name || ''
createForm.remark = item.remark || ''
createForm.state = item.state
createForm.type = item.type || ''
createForm.orgId = item.orgId || undefined
createForm.orgName = item.orgName || ''
createForm.deviceIds = item.deviceIds || ''
createForm.indexImage = item.indexImage || ''
createForm.route = item.route || ''
createForm.content = item.content || ''
selectedDeviceIds.value = createForm.deviceIds
? createForm.deviceIds.split(',').filter((v) => v)
: []
createDialogVisible.value = true
}
const getRouteByType = (type?: string) => {
if (type === '1') return 'iot/report/dashboardPage/Dashboard1'
if (type === '2') return 'iot/report/dashboardPage/Dashboard8'
return ''
}
const submitDialog = async () => {
try {
await createFormRef.value?.validate()
} catch {
return
}
if (createForm.type === '1') {
if (!selectedDeviceIds.value.length) {
message.error('设备不能为空')
return
}
if (selectedDeviceIds.value.length !== 8) {
message.error('设备必须选择8个')
return
}
createForm.deviceIds = selectedDeviceIds.value.join(',')
} else {
createForm.deviceIds = ''
}
if (createForm.orgId) {
const org = findOrgNode(organizationTree.value || [], createForm.orgId)
createForm.orgName = org?.name || ''
} else {
createForm.orgName = ''
}
const route = getRouteByType(createForm.type)
if (route) {
createForm.route = route
}
if (dialogMode.value === 'edit' && !editingId.value) {
message.error('缺少数据编号,无法编辑')
return
}
createLoading.value = true
try {
if (dialogMode.value === 'create') {
await request.post({
url: '/mes/goview/create',
data: createForm
})
message.success('新增成功')
} else {
await request.put({
url: '/mes/goview/update',
data: {
id: editingId.value,
...createForm
}
})
message.success('编辑成功')
}
createDialogVisible.value = false
handleQuery()
} finally {
createLoading.value = false
}
}
const handleDelete = async (item: DashboardItem) => {
if (!item.id) return
try {
await message.delConfirm()
await request.delete({
url: `/mes/goview/delete?id=${item.id}`
})
message.success('删除成功')
await getList()
} catch {}
}
const loadOrganizationTree = async () => {
const data = await OrganizationApi.getOrganizationList()
organizationTree.value = handleTree(data, 'id', 'parentId')
}
const findOrgNode = (nodes: any[], id: any): any | undefined => {
for (const node of nodes) {
if (String(node.id) === String(id)) return node
const children = Array.isArray(node.children) ? node.children : []
const found = findOrgNode(children, id)
if (found) return found
}
return undefined
}
const handleOrgChange = () => {
if (!createForm.orgId) {
createForm.orgName = ''
return
}
const org = findOrgNode(organizationTree.value || [], createForm.orgId)
createForm.orgName = org?.name || ''
}
const loadDeviceList = async () => {
deviceList.value = await DeviceApi.getDeviceList()
}
onMounted(() => {
getList()
loadOrganizationTree()
loadDeviceList()
})
</script>
<style scoped>
.dashboard-card-list {
min-height: 200px;
}
.dashboard-card {
display: flex;
flex-direction: column;
}
.dashboard-card-image-wrapper {
position: relative;
width: 100%;
padding-top: 56.25%;
overflow: hidden;
cursor: pointer;
}
.dashboard-card-image {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
object-fit: contain;
background-color: #0f172a;
}
.dashboard-card-state {
position: absolute;
right: 8px;
bottom: 8px;
}
.dashboard-card-body {
display: flex;
align-items: center;
justify-content: space-between;
padding: 12px 16px;
}
.dashboard-card-main {
min-width: 0;
}
.dashboard-card-title {
font-size: 14px;
font-weight: 600;
color: var(--el-text-color-primary);
margin-bottom: 4px;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.dashboard-card-remark {
font-size: 12px;
color: var(--el-text-color-secondary);
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.dashboard-card-actions {
display: flex;
align-items: center;
margin-left: 8px;
}
.dashboard-card-menu {
min-width: 120px;
}
</style>