feat:排班视图

This commit is contained in:
dzc committed 2025-09-12 17:36:49 +08:00
1 parent 6ffc58d517
commit 6057f30c7f
11 files changed
+452 -7

No files matched your search

+8
View File
@@ -1,5 +1,13 @@
import request from "/@/utils/request"
export function select(query?: Object) {
return request({
url: '/admin/sys-duty/select',
method: 'get',
params: query
})
}
export function fetchList(query?: Object) {
return request({
url: '/admin/sys-duty/page',
+98
View File
@@ -0,0 +1,98 @@
<template>
<el-select style="width: 100%" v-model="state.selectId" :placeholder="props.placeholder"
:clearable="props.clearable" remote-show-suffix filterable remote :disabled="props.disabled"
:remote-method="remoteSearch" :loading="state.loading" @visible-change="(e: boolean) => e && getOptions()"
@change="change">
<el-option v-for="v in state.options" :key="v.id" :label="v.title" :value="v.id" />
</el-select>
</template>
<script setup lang="ts" name="SelectDuty">
import { reactive } from "vue"
import { useDict } from "/@/hooks/dict"
import * as sysDutyApi from "/@/api/sys/sysDuty"
const emit = defineEmits(["change"])
const selectId = defineModel()
type Props = {
placeholder?: string
disabled?: boolean
clearable?: boolean
autoFirst?: boolean
apiParams?: any
}
const props = withDefaults(defineProps<Props>(), {
placeholder: "请选择",
disabled: false,
clearable: true,
autoFirst: false,
apiParams: {},
})
const queryForm = reactive<any>({
name: "",
current: 1,
size: 10,
})
const state = reactive<any>({
loading: true,
selectId: selectId,
options: [],
hardType: [],
})
watch(
selectId,
(newVal, oldValue) => {
if (newVal == oldValue) return
state.selectId = newVal
getOptions()
},
{ immediate: true },
)
async function getOptions() {
try {
state.loading = true
const _pa = {
...queryForm,
...props.apiParams,
}
const { data } = await sysDutyApi.select(_pa)
state.options = data.records ?? []
if (props.autoFirst && state.options.length != 0 && !state.selectId) {
state.selectId = state.options[0].id
emit("change", state.selectId)
}
const { hard_type } = useDict("hard_type")
state.hardType = hard_type
} finally {
setTimeout(() => (state.loading = false), 500)
}
}
function remoteSearch(params: string) {
if (params != queryForm.name) {
queryForm.name = params
getOptions()
}
}
function clear() {
state.selectId = null
getOptions()
}
function change() {
emit("change", selectId)
}
onMounted(() => {
props.autoFirst && getOptions()
})
</script>
+2
View File
@@ -25,6 +25,7 @@ import TagUserStatus from "/@/components/Tag/TagUserStatus.vue"
import SelectArea from "/@/components/Select/SelectArea.vue"
import SelectDevice from "/@/components/Select/SelectDevice.vue"
import SelectDuty from "/@/components/Select/SelectDuty.vue"
import SelectDutyType from "/@/components/Select/SelectDutyType.vue"
import SelectTreeChannel from "/@/components/Select/SelectTreeChannel.vue"
import SelectTreeChannelRadio from "/@/components/Select/SelectTreeChannelRadio.vue"
@@ -83,6 +84,7 @@ export default {
app.component('SelectArea', SelectArea);
app.component('SelectDevice', SelectDevice);
app.component('SelectDuty', SelectDuty);
app.component('SelectDutyType', SelectDutyType);
app.component('SelectTreeChannel', SelectTreeChannel);
app.component('SelectTreeChannelRadio', SelectTreeChannelRadio);
@@ -0,0 +1,56 @@
<!-- 车辆信息 -->
<template>
<com-title title="过车数据">
<div class="h-[5px]" />
<div v-for="v in 10" class="container flex items-center gap-2 mb-[5px] overflow-y-scroll">
<el-image class="w-[55px] h-[55px] rounded-[2px]"
src="https://fuss10.elemecdn.com/e/5d/4a731a90594a4af544c0c25941171jpeg.jpeg" fit="cover" />
<div class="flex-1 flex flex-col">
<div class="container-content">
<span>车牌号</span>
<span>2023-08-01 10:00:00</span>
</div>
<div class="container-content">
<span>位置</span>
<span>2023-08-01 10:00:00</span>
</div>
<div class="container-content">
<span>抓拍时间</span>
<span>2023-08-01 10:00:00</span>
</div>
</div>
</div>
</com-title>
</template>
<script setup lang="ts" name="carList">
const comTitle = defineAsyncComponent(() => import('./com-title.vue'));
</script>
<style lang="scss" scoped>
.container {
padding: 4px 6px;
background-color: white;
box-shadow: 0px 4px 13px -2px rgba(210, 210, 210, 0.5);
border-radius: 6px;
.container-content {
display: flex;
align-items: center;
span {
display: inline-block;
}
span:nth-child(1) {
min-width: 70px;
}
span:nth-child(2) {
flex: 1;
}
}
}
</style>
@@ -0,0 +1,42 @@
<template>
<div class="title flex justify-between items-center">
<div class="title-text" :class="props.titleDirection == 'left' ? 'justify-start' : 'justify-end'">
<span>{{ props.title }}</span>
</div>
<div class="w-full h-full">
<slot></slot>
</div>
</div>
</template>
<script setup lang="ts" name="comTitle">
type Props = {
title: string
titleDirection: "left" | "right"
}
const props = withDefaults(defineProps<Props>(), {
title: "标题",
titleDirection: "left"
})
</script>
<style lang="scss" scoped>
.title {
display: flex;
flex-direction: column;
align-items: flex-start;
justify-content: center;
&-text {
display: flex;
width: 100%;
padding: 0 10px;
font-size: 18px;
font-weight: bold;
color: #577cf7;
}
}
</style>
@@ -0,0 +1,56 @@
<!-- 车辆信息 -->
<template>
<com-title title="过车数据">
<div class="h-[5px]" />
<div v-for="v in 10" class="container flex items-center gap-2 mb-[5px] overflow-y-scroll">
<el-image class="w-[55px] h-[55px] rounded-[2px]"
src="https://fuss10.elemecdn.com/e/5d/4a731a90594a4af544c0c25941171jpeg.jpeg" fit="cover" />
<div class="flex-1 flex flex-col">
<div class="container-content">
<span>车牌号</span>
<span>2023-08-01 10:00:00</span>
</div>
<div class="container-content">
<span>位置</span>
<span>2023-08-01 10:00:00</span>
</div>
<div class="container-content">
<span>抓拍时间</span>
<span>2023-08-01 10:00:00</span>
</div>
</div>
</div>
</com-title>
</template>
<script setup lang="ts" name="carList">
const comTitle = defineAsyncComponent(() => import('./com-title.vue'));
</script>
<style lang="scss" scoped>
.container {
padding: 4px 6px;
background-color: white;
box-shadow: 0px 4px 13px -2px rgba(210, 210, 210, 0.5);
border-radius: 6px;
.container-content {
display: flex;
align-items: center;
span {
display: inline-block;
}
span:nth-child(1) {
min-width: 70px;
}
span:nth-child(2) {
flex: 1;
}
}
}
</style>
@@ -0,0 +1,133 @@
<!-- 排班视图 -->
<template>
<div class="w-full h-full flex flex-col">
<com-title title="基地周值班表">
<select-duty v-model="refreshForm.dutyId" autoFirst style="width: 250px;" />
</com-title>
<el-table v-if="tableHeaderList && tableHeaderList.length" style="padding-top: 10px;" :data="disposeDataList"
v-loading="state.loading" border :cell-style="tableStyle.cellStyle"
:header-cell-style="tableStyle.headerCellStyle" @sort-change="sortChangeHandle">
<el-table-column v-for="(v, i) in tableHeaderList" width="110" :prop="v.prop" :label="v.label">
<el-table-column v-if="v.children && v.children.length" v-for="(v2, i2) in v.children" :prop="v2.prop"
:label="v2.label" />
</el-table-column>
</el-table>
<el-empty class="w-full h-full flex justify-center items-center" v-else description="暂无排班数据" />
</div>
</template>
<script setup lang="ts" name="dutyTable">
import { BasicTableProps, useTable } from "/@/hooks/table";
import * as sysDutyApi from "/@/api/sys/sysDuty";
import * as sysDutyItemApi from "/@/api/sys/sysDutyItem";
const comTitle = defineAsyncComponent(() => import('./com-title.vue'));
const tableHeaderList = ref<Array<{
prop: string,
label: string,
children: Array<{
prop: string,
label: string,
}>
}>>([])
const refreshForm = reactive<any>({
dutyId: ""
})
const detail = reactive<any>({})
const disposeDataList = ref<any>([])
const state: BasicTableProps = reactive<BasicTableProps>({
createdIsNeed: false,
queryForm: {
dutyId: ""
},
pageList: sysDutyItemApi.fetchList
})
// table hook
const {
getDataList,
currentChangeHandle,
sizeChangeHandle,
sortChangeHandle,
downBlobFile,
tableStyle
} = useTable(state)
watch(
() => refreshForm.dutyId,
(newVal, oldValue) => {
if (newVal == oldValue) return
refresh(newVal)
},
)
const refresh = async (id: string) => {
state.queryForm.dutyId = id
if (!id) return
await getsysDutyData(id)
await getDataList()
const _list: Array<any> = []
if (state.dataList && state.dataList.length) {
for (const v of state.dataList) {
if (!v.rowJson || !v.rowJson.length) continue
const _obj: any = {}
for (const item of v.rowJson) {
_obj[item.key] = item.content
}
_list.push(_obj)
}
}
disposeDataList.value = _list
}
// 初始化表单数据
const getsysDutyData = async (id: string) => {
// 获取数据
try {
const { data } = await sysDutyApi.getObj(id)
Object.assign(detail, data)
const _tableHeaderList: Array<{
prop: string,
label: string,
children: []
}> = []
if (data && data.configJson && data.configJson.length) {
for (let index = 0; index < data.configJson.length; index++) {
const element = data.configJson[index];
if (!element) continue
const isExist = _tableHeaderList.some(v => v.label == element.mainLabel)
if (isExist) continue
const entity = {
prop: element.key,
label: element.mainLabel,
children: []
}
const children = data.configJson.filter((v: any) => v.mainLabel == element.mainLabel) ?? []
if (children.length >= 2) {
entity.prop = ""
entity.children = children.map((v: any) => {
return {
prop: v.key,
label: v.subLabel,
}
})
}
_tableHeaderList.push(entity as any)
}
}
tableHeaderList.value = _tableHeaderList
console.log("tableHeaderList:", tableHeaderList.value);
} catch (error) {
console.log("|初始化表单数据|意外的错误:", error);
}
};
</script>
<style lang="scss" scoped></style>
@@ -0,0 +1,14 @@
<!-- 视频预览 -->
<template>
<div class="w-full h-full pt-[10px]">
<com-title title="监控预览"></com-title>
</div>
</template>
<script setup lang="ts" name="videoPay">
const comTitle = defineAsyncComponent(() => import('./com-title.vue'));
</script>
<style lang="scss" scoped></style>
+40 -3
View File
@@ -1,13 +1,50 @@
<!-- 值班类型 -->
<template>
<div class="layout-padding">
<div class="layout-padding-auto layout-padding-view">
</div>
<el-container>
<el-aside class="aside">
<car-list />
</el-aside>
<el-container>
<el-header class="header">
<duty-table />
</el-header>
<el-main class="main">
<video-pay />
</el-main>
</el-container>
<el-aside class="aside">
<door-list />
</el-aside>
</el-container>
</div>
</template>
<script setup lang="ts" name="dutyPlan">
const CarList = defineAsyncComponent(() => import('./components/car-list.vue'));
const DoorList = defineAsyncComponent(() => import('./components/door-list.vue'));
const DutyTable = defineAsyncComponent(() => import('./components/duty-table.vue'));
const VideoPay = defineAsyncComponent(() => import('./components/video-pay.vue'));
</script>
<style lang="scss" scoped></style>
<style lang="scss" scoped>
.aside {
width: 20%;
height: 100%;
}
.header {
width: 100%;
height: 50%;
padding: 0 10px;
}
.main {
width: 100%;
height: 50%;
padding: 0 10px;
}
</style>
@@ -42,8 +42,7 @@ const queryForm = reactive<any>({
id: ''
})
const detail = reactive<any>({
})
const detail = reactive<any>({})
const state: BasicTableProps = reactive<BasicTableProps>({
createdIsNeed: false,
+2 -2
View File
@@ -32,8 +32,8 @@
:header-cell-style="tableStyle.headerCellStyle" @selection-change="selectionChangHandle"
@sort-change="sortChangeHandle">
<el-table-column type="selection" width="40" align="center" />
<el-table-column type="index" label="#" width="40" />
<el-table-column prop="applyId" label="创建人" />
<el-table-column type="index" label="#" width="80" />
<el-table-column prop="applyUsername" label="创建人" />
<el-table-column prop="title" label="标题" />
<el-table-column prop="remark" label="备注" />
<el-table-column label="排班时间">