feat:军校bug修改
This commit is contained in:
1 parent
55c7d77c11
commit
cdb7496ade
35 files changed
+4043
-202
No files matched your search
@@ -14,4 +14,5 @@ VITE_ADMIN_PROXY_PATH = http://127.0.0.1:36588
|
||||
# VITE_ADMIN_PROXY_PATH = http://172.20.10.2:36588
|
||||
|
||||
# 图片访问基础地址
|
||||
VITE_IMAGE_BASE_PATH = http://123.157.250.162:36588/api
|
||||
# VITE_IMAGE_BASE_PATH = http://123.157.250.162:36588/api
|
||||
VITE_IMAGE_BASE_PATH = http://127.0.0.1:36588/api
|
||||
Generated
+2410
-116
File diff suppressed because it is too large.
Load diff
+5
-1
@@ -9,6 +9,7 @@
|
||||
"build": "cross-env NODE_OPTIONS=--max-old-space-size=4096 vite build",
|
||||
"build:prod": "cross-env NODE_OPTIONS=--max-old-space-size=4096 vite build --mode prod",
|
||||
"build:docker": "cross-env NODE_OPTIONS=--max-old-space-size=4096 vite build --outDir ./docker/dist/",
|
||||
"test:unit": "vitest run",
|
||||
"lint:eslint": "eslint --fix --ext .js,.ts,.vue ./src",
|
||||
"prettier": "prettier --write ."
|
||||
},
|
||||
@@ -75,11 +76,13 @@
|
||||
"@typescript-eslint/parser": "^5.53.0",
|
||||
"@vitejs/plugin-vue": "^4.0.0",
|
||||
"@vue/compiler-sfc": "^3.2.47",
|
||||
"@vue/test-utils": "^2.4.10",
|
||||
"consola": "^2.15.3",
|
||||
"cross-env": "7.0.3",
|
||||
"daisyui": "4.11.1",
|
||||
"eslint": "^8.34.0",
|
||||
"eslint-plugin-vue": "^9.9.0",
|
||||
"jsdom": "^24.1.3",
|
||||
"pinia-plugin-persist": "^1.0.0",
|
||||
"prettier": "2.8.4",
|
||||
"sass": "1.58.3",
|
||||
@@ -91,6 +94,7 @@
|
||||
"vite-plugin-style-import": "^2.0.0",
|
||||
"vite-plugin-top-level-await": "^1.3.0",
|
||||
"vite-plugin-vue-setup-extend": "^0.4.0",
|
||||
"vitest": "^1.6.1",
|
||||
"vue-eslint-parser": "^9.1.0"
|
||||
},
|
||||
"browserslist": [
|
||||
@@ -116,4 +120,4 @@
|
||||
"type": "git",
|
||||
"url": "https://gitee.com/log4j/pig"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -31,6 +31,22 @@ export function delObjs(ids?: Object) {
|
||||
})
|
||||
}
|
||||
|
||||
export function delPlayback(ids?: Object) {
|
||||
return request({
|
||||
url: '/admin/car/carRecord/del/playback',
|
||||
method: 'delete',
|
||||
data: ids
|
||||
})
|
||||
}
|
||||
|
||||
export function recoverPlayback(ids?: Object) {
|
||||
return request({
|
||||
url: '/admin/car/carRecord/recover/playback',
|
||||
method: 'post',
|
||||
data: ids
|
||||
})
|
||||
}
|
||||
|
||||
export function putObj(obj?: Object) {
|
||||
return request({
|
||||
url: '/admin/car/carAccess',
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
import request from "/@/utils/request"
|
||||
|
||||
export function notClockPage(query?: Object) {
|
||||
return request({
|
||||
url: '/admin//clock-record/not-clock-page',
|
||||
method: 'get',
|
||||
params: query
|
||||
})
|
||||
}
|
||||
@@ -105,6 +105,22 @@ export function putObj(obj?: Object) {
|
||||
})
|
||||
}
|
||||
|
||||
export function delPlayback(ids?: Object) {
|
||||
return request({
|
||||
url: '/admin/door/doorRecord/del/playback',
|
||||
method: 'delete',
|
||||
data: ids
|
||||
})
|
||||
}
|
||||
|
||||
export function recoverPlayback(ids?: Object) {
|
||||
return request({
|
||||
url: '/admin/door/doorRecord/recover/playback',
|
||||
method: 'post',
|
||||
data: ids
|
||||
})
|
||||
}
|
||||
|
||||
/* ************************************* start 人员出入次数统计 start ************************************* */
|
||||
|
||||
export function doorTimesPage(obj: any) {
|
||||
@@ -115,6 +131,14 @@ export function doorTimesPage(obj: any) {
|
||||
})
|
||||
}
|
||||
|
||||
export function jglTimesPage(obj: any) {
|
||||
return request({
|
||||
url: '/admin/door/doorTimes/jglPage',
|
||||
method: 'get',
|
||||
params: obj
|
||||
})
|
||||
}
|
||||
|
||||
export function doorTimesExport(obj: any) {
|
||||
return request({
|
||||
url: '/admin/door/doorTimes/export',
|
||||
|
||||
Executable
+41
@@ -0,0 +1,41 @@
|
||||
import request from "/@/utils/request"
|
||||
|
||||
export function fetchList(query?: Object) {
|
||||
return request({
|
||||
url: '/admin/routing-manage/page',
|
||||
method: 'get',
|
||||
params: query
|
||||
})
|
||||
}
|
||||
|
||||
export function addObj(obj?: Object) {
|
||||
return request({
|
||||
url: '/admin/routing-manage',
|
||||
method: 'post',
|
||||
data: obj
|
||||
})
|
||||
}
|
||||
|
||||
export function getObj(id?: string) {
|
||||
return request({
|
||||
url: '/admin/routing-manage/' + id,
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
||||
export function delObjs(ids?: Object) {
|
||||
return request({
|
||||
url: '/admin/routing-manage',
|
||||
method: 'delete',
|
||||
data: ids
|
||||
})
|
||||
}
|
||||
|
||||
export function putObj(obj?: Object) {
|
||||
return request({
|
||||
url: '/admin/routing-manage',
|
||||
method: 'put',
|
||||
data: obj
|
||||
})
|
||||
}
|
||||
|
||||
Executable
+56
@@ -0,0 +1,56 @@
|
||||
import request from "/@/utils/request"
|
||||
|
||||
export function fetchList(query?: Object) {
|
||||
return request({
|
||||
url: '/admin/service-manage/page',
|
||||
method: 'get',
|
||||
params: query
|
||||
})
|
||||
}
|
||||
|
||||
export function addObj(obj?: Object) {
|
||||
return request({
|
||||
url: '/admin/service-manage',
|
||||
method: 'post',
|
||||
data: obj
|
||||
})
|
||||
}
|
||||
|
||||
export function getObj(id?: string) {
|
||||
return request({
|
||||
url: '/admin/service-manage/' + id,
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
||||
export function delObjs(ids?: Object) {
|
||||
return request({
|
||||
url: '/admin/service-manage',
|
||||
method: 'delete',
|
||||
data: ids
|
||||
})
|
||||
}
|
||||
|
||||
export function putObj(obj?: Object) {
|
||||
return request({
|
||||
url: '/admin/service-manage',
|
||||
method: 'put',
|
||||
data: obj
|
||||
})
|
||||
}
|
||||
|
||||
export function start(query?: Object) {
|
||||
return request({
|
||||
url: '/admin/service-manage/start',
|
||||
method: 'get',
|
||||
params: query
|
||||
})
|
||||
}
|
||||
|
||||
export function done(query?: Object) {
|
||||
return request({
|
||||
url: '/admin/service-manage/done',
|
||||
method: 'get',
|
||||
params: query
|
||||
})
|
||||
}
|
||||
@@ -1,11 +1,16 @@
|
||||
<template>
|
||||
<div>
|
||||
<el-button :style="jsonViewStyle" type="text" link @click="clickJsonView">
|
||||
<!-- <div :style="jsonViewStyle" class="json-view">{{ jsonView }}</div> -->
|
||||
<div :style="jsonViewStyle" class="json-view">查看json</div>
|
||||
<div class="json-view-container">
|
||||
<div class="json-view">{{ jsonView }}</div>
|
||||
<div class="json-view-btn">查看json</div>
|
||||
</div>
|
||||
</el-button>
|
||||
<el-dialog append-to-body v-model="jsonViewVisible">
|
||||
<vue-json-pretty :data="jsonData" showLength showLineNumber showIcon :collapsedOnClickBrackets="false" />
|
||||
<div v-if="displayData.type === 'html'" v-html="displayData.content" class="html-preview"></div>
|
||||
<div v-else-if="displayData.type === 'text'" class="text-preview">{{ displayData.content }}</div>
|
||||
<vue-json-pretty v-else :data="displayData.content" showLength showLineNumber showIcon
|
||||
:collapsedOnClickBrackets="false" />
|
||||
<template #footer>
|
||||
<el-button @click="jsonViewVisible = false">关闭</el-button>
|
||||
</template>
|
||||
@@ -24,7 +29,7 @@ type Props = {
|
||||
}
|
||||
|
||||
const state = reactive({
|
||||
jsonData: {},
|
||||
jsonData: null as any,
|
||||
})
|
||||
|
||||
const props = withDefaults(defineProps<Props>(), {
|
||||
@@ -34,12 +39,25 @@ const props = withDefaults(defineProps<Props>(), {
|
||||
|
||||
const jsonViewVisible = ref<boolean>(false)
|
||||
|
||||
const jsonData = computed(() => {
|
||||
// 先用lazyJson
|
||||
if (state.jsonData) return state.jsonData
|
||||
if (!props.json) return ""
|
||||
if (typeof props.json == "string") return JSON.parse(props.json)
|
||||
return props.json
|
||||
const displayData = computed(() => {
|
||||
// 优先使用 lazyJson 返回的数据,如果没有则使用 props.json
|
||||
const raw = state.jsonData || props.json;
|
||||
if (!raw) return { type: 'empty', content: '' };
|
||||
|
||||
let content = raw;
|
||||
if (typeof raw === 'string') {
|
||||
try {
|
||||
content = JSON.parse(raw);
|
||||
return { type: 'json', content };
|
||||
} catch (e) {
|
||||
// 解析 JSON 失败,判断是否包含 HTML 标签
|
||||
if (/<\/?[a-z][\s\S]*>/i.test(raw)) {
|
||||
return { type: 'html', content: raw };
|
||||
}
|
||||
return { type: 'text', content: raw };
|
||||
}
|
||||
}
|
||||
return { type: 'json', content: raw };
|
||||
})
|
||||
|
||||
const jsonView = computed(() => {
|
||||
@@ -56,23 +74,49 @@ const jsonViewStyle = computed(() => {
|
||||
// 点击事件
|
||||
const clickJsonView = async () => {
|
||||
jsonViewVisible.value = true
|
||||
console.log("props:", props);
|
||||
|
||||
if (props.lazyJson) {
|
||||
const string = await props.lazyJson()
|
||||
if (string) {
|
||||
if (typeof string == "string") {
|
||||
state.jsonData = JSON.parse(string)
|
||||
} else {
|
||||
try {
|
||||
const string = await props.lazyJson()
|
||||
if (string) {
|
||||
state.jsonData = string
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("lazyJson error:", error)
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.json-view {
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
.json-view-container {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-items: start;
|
||||
|
||||
.json-view {
|
||||
max-width: 150px;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
.json-view-btn {
|
||||
color: var(--el-color-primary);
|
||||
}
|
||||
}
|
||||
|
||||
.html-preview {
|
||||
max-height: 60vh;
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
.text-preview {
|
||||
max-height: 60vh;
|
||||
overflow: auto;
|
||||
white-space: pre-wrap;
|
||||
word-break: break-all;
|
||||
}
|
||||
</style>
|
||||
@@ -21,6 +21,7 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts" name="select-tree-channel">
|
||||
import { onMounted, reactive, watch } from 'vue'
|
||||
import * as DeviceManageApi from '/@/api/device/deviceManage'
|
||||
|
||||
const emit = defineEmits(["change"])
|
||||
@@ -51,20 +52,34 @@ const queryForm = reactive<any>({
|
||||
})
|
||||
|
||||
const state = reactive<any>({
|
||||
loading: true,
|
||||
loading: false,
|
||||
selectId: selectId,
|
||||
options: [],
|
||||
type: [],
|
||||
loaded: false,
|
||||
})
|
||||
|
||||
let loadOptionsTask: Promise<void> | null = null
|
||||
|
||||
const hasSelectedValue = (value: unknown) => {
|
||||
if (Array.isArray(value)) {
|
||||
return value.some((item) => item !== null && item !== undefined && `${item}`.trim() !== '')
|
||||
}
|
||||
return value !== null && value !== undefined && `${value}`.trim() !== ''
|
||||
}
|
||||
|
||||
const shouldLoadOptionsOnInit = (value: unknown) => props.autoFirst || hasSelectedValue(value)
|
||||
|
||||
watch(
|
||||
selectId,
|
||||
(newVal, oldValue) => {
|
||||
if (newVal == oldValue) return
|
||||
state.selectId = newVal
|
||||
// getOptions()
|
||||
if (shouldLoadOptionsOnInit(newVal)) {
|
||||
void ensureOptionsLoaded()
|
||||
}
|
||||
},
|
||||
{ immediate: true },
|
||||
{ immediate: true, deep: true },
|
||||
)
|
||||
|
||||
const filterNode = (value: string, data: any, node: any) => {
|
||||
@@ -79,7 +94,34 @@ const filterNode = (value: string, data: any, node: any) => {
|
||||
return false
|
||||
}
|
||||
|
||||
async function getOptions() {
|
||||
watch(
|
||||
() => props.apiParams,
|
||||
() => {
|
||||
if (shouldLoadOptionsOnInit(selectId.value) || state.loaded) {
|
||||
state.loaded = false
|
||||
void ensureOptionsLoaded(true)
|
||||
}
|
||||
},
|
||||
{ deep: true },
|
||||
)
|
||||
|
||||
onMounted(() => {
|
||||
if (shouldLoadOptionsOnInit(selectId.value)) {
|
||||
void ensureOptionsLoaded()
|
||||
}
|
||||
})
|
||||
|
||||
async function ensureOptionsLoaded(force = false) {
|
||||
if (state.loaded && !force) return
|
||||
if (loadOptionsTask) return loadOptionsTask
|
||||
loadOptionsTask = getOptions(force).finally(() => {
|
||||
loadOptionsTask = null
|
||||
})
|
||||
return loadOptionsTask
|
||||
}
|
||||
|
||||
async function getOptions(force = false) {
|
||||
if (state.loaded && !force) return
|
||||
try {
|
||||
state.loading = true
|
||||
const _pa = {
|
||||
@@ -88,12 +130,13 @@ async function getOptions() {
|
||||
}
|
||||
const { data } = await DeviceManageApi.channelTree(_pa)
|
||||
state.options = data ?? []
|
||||
state.loaded = true
|
||||
if (props.autoFirst && state.options.length != 0) {
|
||||
state.selectId = state.options[0].id
|
||||
emit("change", state.selectId)
|
||||
}
|
||||
} finally {
|
||||
setTimeout(() => (state.loading = false), 500)
|
||||
state.loading = false
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
<template #label>
|
||||
<div class="flex items-center gap-1">
|
||||
<span>过车间隔</span>
|
||||
<el-tooltip class="box-item" :content="`每前后${form.cleaningData.carGap}分钟同一过车数据算一次`" placement="top">
|
||||
<el-tooltip class="box-item" :content="`每前后${form.cleaningData.carGap}秒同一过车数据算一次`" placement="top">
|
||||
<el-icon :size="16">
|
||||
<InfoFilled />
|
||||
</el-icon>
|
||||
@@ -15,14 +15,14 @@
|
||||
</div>
|
||||
</template>
|
||||
<el-input v-model="form.cleaningData.carGap" placeholder="请输入过车间隔">
|
||||
<template #append>分钟</template>
|
||||
<template #append>秒</template>
|
||||
</el-input>
|
||||
</el-form-item>
|
||||
<el-form-item prop="cleaningData.personGap">
|
||||
<template #label>
|
||||
<div class="flex items-center gap-1">
|
||||
<span>过人间隔</span>
|
||||
<el-tooltip class="box-item" :content="`每前后${form.cleaningData.personGap}分钟同一过人数据算一次`"
|
||||
<el-tooltip class="box-item" :content="`每前后${form.cleaningData.personGap}秒同一过人数据算一次`"
|
||||
placement="top">
|
||||
<el-icon :size="16">
|
||||
<InfoFilled />
|
||||
@@ -31,7 +31,7 @@
|
||||
</div>
|
||||
</template>
|
||||
<el-input v-model="form.cleaningData.personGap" placeholder="请输入过人间隔">
|
||||
<template #append>分钟</template>
|
||||
<template #append>秒</template>
|
||||
</el-input>
|
||||
</el-form-item>
|
||||
<!-- **************************** end 同步间隔配置 end **************************** -->
|
||||
|
||||
@@ -64,25 +64,25 @@
|
||||
</el-form-item>
|
||||
<!-- **************************** end 夜晚出入办公楼 end **************************** -->
|
||||
|
||||
<!-- **************************** start 打卡 start **************************** -->
|
||||
<el-divider class="my-12">打卡配置</el-divider>
|
||||
<!-- **************************** start 出勤 start **************************** -->
|
||||
<el-divider class="my-12">出勤配置</el-divider>
|
||||
<el-form-item prop="personDk.forenoon">
|
||||
<template #label>
|
||||
<div class="flex items-center gap-1">
|
||||
<span>打卡时间(上午)</span>
|
||||
<span>出勤时间(上午)</span>
|
||||
</div>
|
||||
</template>
|
||||
<el-time-picker format="HH:mm" value-format="HH:mm" v-model="form.personDk.forenoon" is-range
|
||||
placeholder="请选择打卡时间(上午)" />
|
||||
placeholder="请选择出勤时间(上午)" />
|
||||
</el-form-item>
|
||||
<el-form-item prop="personDk.afternoon">
|
||||
<template #label>
|
||||
<div class="flex items-center gap-1">
|
||||
<span>打卡时间(下午)</span>
|
||||
<span>出勤时间(下午)</span>
|
||||
</div>
|
||||
</template>
|
||||
<el-time-picker format="HH:mm" value-format="HH:mm" v-model="form.personDk.afternoon" is-range
|
||||
placeholder="请选择打卡时间(下午)" />
|
||||
placeholder="请选择出勤时间(下午)" />
|
||||
</el-form-item>
|
||||
<el-form-item prop="personDk.cameraChannels">
|
||||
<template #label>
|
||||
@@ -108,7 +108,7 @@
|
||||
</template>
|
||||
<select-tree-dept v-model="form.personDk.deptCodes" placeholder="请选择部门" />
|
||||
</el-form-item>
|
||||
<!-- **************************** end 打卡 end **************************** -->
|
||||
<!-- **************************** end 出勤 end **************************** -->
|
||||
|
||||
|
||||
<!-- **************************** start 考勤 start **************************** -->
|
||||
@@ -180,7 +180,7 @@ const form = reactive<any>({
|
||||
alarmTime: [],
|
||||
cameraChannels: [],
|
||||
},
|
||||
// 人员打卡配置
|
||||
// 人员出勤配置
|
||||
personDk: {
|
||||
forenoon: [],
|
||||
afternoon: [],
|
||||
@@ -201,9 +201,9 @@ const dataRules = ref({
|
||||
"personNightCamp.alarmTime": [{ required: true, message: '夜晚出入时间不能为空', trigger: 'blur' }],
|
||||
// 人员晚上出入办公楼
|
||||
"personNightOffice.alarmTime": [{ required: true, message: '夜晚出入时间不能为空', trigger: 'blur' }],
|
||||
// 人员打卡配置
|
||||
"personDk.forenoon": [{ required: true, message: '打卡时间(上午)不能为空', trigger: 'blur' }],
|
||||
"personDk.afternoon": [{ required: true, message: '打卡时间(下午)不能为空', trigger: 'blur' }],
|
||||
// 人员出勤配置
|
||||
"personDk.forenoon": [{ required: true, message: '出勤时间(上午)不能为空', trigger: 'blur' }],
|
||||
"personDk.afternoon": [{ required: true, message: '出勤时间(下午)不能为空', trigger: 'blur' }],
|
||||
// 人员考勤配置
|
||||
"personKq.forenoon": [{ required: true, message: '考勤时间(上午)不能为空', trigger: 'blur' }],
|
||||
"personKq.afternoon": [{ required: true, message: '考勤时间(下午)不能为空', trigger: 'blur' }],
|
||||
|
||||
@@ -89,6 +89,7 @@ import { fetchList, delObjs } from "/@/api/area/areaHouse";
|
||||
import { useMessage, useMessageBox } from "/@/hooks/message";
|
||||
import { useDict } from '/@/hooks/dict';
|
||||
import { usePanel } from "/@/stores/panel"
|
||||
import dayjs from "dayjs";
|
||||
|
||||
const storesPanel = usePanel()
|
||||
|
||||
@@ -119,7 +120,7 @@ const {
|
||||
currentChangeHandle,
|
||||
sizeChangeHandle,
|
||||
sortChangeHandle,
|
||||
downBlobFile,
|
||||
postDownBlobFile,
|
||||
tableStyle
|
||||
} = useTable(state)
|
||||
|
||||
@@ -152,7 +153,8 @@ onBeforeUnmount(() => {
|
||||
|
||||
// 导出excel
|
||||
const exportExcel = () => {
|
||||
downBlobFile('/area/areaHouse/export', Object.assign(state.queryForm, { ids: selectObjs }), 'areaHouse.xlsx')
|
||||
const timeSuffix = dayjs().format('YYYY-MM-DD_HH-mm-ss')
|
||||
postDownBlobFile('/admin/areaHouse/export', Object.assign(state.queryForm, { ids: selectObjs }), `房屋管理_${timeSuffix}.xlsx`)
|
||||
}
|
||||
|
||||
// 多选事件
|
||||
|
||||
@@ -26,6 +26,9 @@
|
||||
v-auth="'admin_areaManage_add'">
|
||||
新 增
|
||||
</el-button>
|
||||
<el-button plain icon="upload-filled" class="ml10" @click="uploadExcelRef.show()">
|
||||
导 入
|
||||
</el-button>
|
||||
<el-button plain :disabled="multiple" icon="Delete" type="primary" v-auth="'admin_areaManage_del'"
|
||||
@click="handleDelete(selectObjs)">
|
||||
删除
|
||||
@@ -77,6 +80,8 @@
|
||||
<form-dialog ref="formDialogRef" @refresh="getDataList(false)" />
|
||||
<area-house-table-dialog ref="areaHouseTableDialogRef" />
|
||||
<area-playback-device-table-dialog ref="areaPlaybackDeviceTableDialogRef" />
|
||||
<upload-excel ref="uploadExcelRef" title="空间导入" url="/admin/areaManage/read-excel"
|
||||
temp-url="/admin/sys-file/local/file/area_import_temp.xlsx" @refreshDataList="getDataList(false)" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -85,6 +90,7 @@ import { BasicTableProps, useTable } from "/@/hooks/table";
|
||||
import { fetchList, delObjs } from "/@/api/area/areaManage";
|
||||
import { useMessage, useMessageBox } from "/@/hooks/message";
|
||||
import { useDict } from '/@/hooks/dict';
|
||||
import dayjs from "dayjs";
|
||||
|
||||
// 引入组件
|
||||
const FormDialog = defineAsyncComponent(() => import('./form.vue'));
|
||||
@@ -99,6 +105,7 @@ const areaPlaybackDeviceTableDialogRef = ref()
|
||||
const bindCameraDialogRef = ref()
|
||||
|
||||
// 搜索变量
|
||||
const uploadExcelRef = ref()
|
||||
const queryRef = ref()
|
||||
const showSearch = ref(true)
|
||||
// 多选变量
|
||||
@@ -116,7 +123,7 @@ const {
|
||||
currentChangeHandle,
|
||||
sizeChangeHandle,
|
||||
sortChangeHandle,
|
||||
downBlobFile,
|
||||
postDownBlobFile,
|
||||
tableStyle
|
||||
} = useTable(state)
|
||||
|
||||
@@ -131,7 +138,8 @@ const resetQuery = () => {
|
||||
|
||||
// 导出excel
|
||||
const exportExcel = () => {
|
||||
downBlobFile('/admin/areaManage/export', Object.assign(state.queryForm, { ids: selectObjs }), 'areaManage.xlsx')
|
||||
const timeSuffix = dayjs().format('YYYY-MM-DD_HH-mm-ss')
|
||||
postDownBlobFile('/admin/areaManage/export', Object.assign(state.queryForm, { ids: selectObjs }), `空间管理_${timeSuffix}.xlsx`)
|
||||
}
|
||||
|
||||
// 多选事件
|
||||
|
||||
@@ -140,12 +140,20 @@
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作" width="100" fixed="right">
|
||||
<el-table-column label="操作" width="180" fixed="right">
|
||||
<template #default="{ row }">
|
||||
<el-button v-if="row.devChnId" icon="VideoCamera" text type="primary"
|
||||
@click="openReplayDialog(row)">
|
||||
<el-button v-if="row.devChnId && row.delPlaybackVideoFlag == '0'" icon="VideoCamera" text
|
||||
type="primary" @click="openReplayDialog(row)">
|
||||
回放
|
||||
</el-button>
|
||||
<el-button v-if="row.delPlaybackVideoFlag == '0'" v-auth="'car_carRecord_del_playback'"
|
||||
icon="delete" text type="primary" @click="delPlaybackVideo(row)">
|
||||
删除回放
|
||||
</el-button>
|
||||
<el-button v-if="row.delPlaybackVideoFlag == '1'" v-auth="'car_carRecord_recover_playback'"
|
||||
icon="delete" text type="primary" @click="recoverPlaybackVideo(row)">
|
||||
恢复回放
|
||||
</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
@@ -164,6 +172,7 @@ import { useDict } from '/@/hooks/dict';
|
||||
import { parseTime } from "/@/utils/formatTime"
|
||||
import { usePanel } from "/@/stores/panel"
|
||||
import { getDefaultAlarmDateList } from "/@/utils/other"
|
||||
import { useMessage, useMessageBox } from "/@/hooks/message";
|
||||
|
||||
const storesPanel = usePanel()
|
||||
|
||||
@@ -261,6 +270,36 @@ function openReplayDialog(row: any) {
|
||||
|
||||
replayVideoDialogRef.value.openDialog('playbackGroup', row.devChnId, { startTime, endTime })
|
||||
}
|
||||
|
||||
async function delPlaybackVideo(row: any) {
|
||||
try {
|
||||
await useMessageBox().confirm('此操作将删除回放功能');
|
||||
} catch {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
await CarAccessApi.delPlayback([row.id]);
|
||||
getDataList();
|
||||
useMessage().success('删除成功');
|
||||
} catch (err: any) {
|
||||
useMessage().error(err.msg);
|
||||
}
|
||||
}
|
||||
|
||||
async function recoverPlaybackVideo(row: any) {
|
||||
try {
|
||||
await useMessageBox().confirm('此操作将恢复回放功能');
|
||||
} catch {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
await CarAccessApi.recoverPlayback([row.id]);
|
||||
getDataList();
|
||||
useMessage().success('恢复成功');
|
||||
} catch (err: any) {
|
||||
useMessage().error(err.msg);
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
|
||||
@@ -221,7 +221,7 @@ async function dataCleaning() {
|
||||
background: 'rgba(0, 0, 0, 0.7)',
|
||||
})
|
||||
try {
|
||||
await carRecordApi.dataCleaning()
|
||||
await carRecordApi.dataCleaning(state.queryForm)
|
||||
} catch (error) {
|
||||
console.error("|清洗数据|意外的错误:", error);
|
||||
} finally {
|
||||
|
||||
@@ -8,12 +8,12 @@
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12" class="mb20">
|
||||
<el-form-item label="设备编码" prop="deviceCode">
|
||||
<el-form-item label="设备编码">
|
||||
<el-input v-model="form.deviceCode" clearable maxlength="100" placeholder="请输入设备编码" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12" class="mb20">
|
||||
<el-form-item label="设备唯一标识码" prop="deviceSn">
|
||||
<el-form-item label="设备唯一标识码">
|
||||
<el-input v-model="form.deviceSn" clearable maxlength="100" placeholder="请输入设备唯一标识码" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
|
||||
@@ -30,6 +30,9 @@
|
||||
v-auth="'admin_deviceManage_add'">
|
||||
新 增
|
||||
</el-button>
|
||||
<el-button plain icon="upload-filled" class="ml10" @click="uploadExcelRef.show()">
|
||||
导 入
|
||||
</el-button>
|
||||
<el-button plain :disabled="multiple" icon="Delete" type="primary" v-auth="'admin_deviceManage_del'"
|
||||
@click="handleDelete(selectObjs)">
|
||||
删除
|
||||
@@ -45,6 +48,7 @@
|
||||
<el-table-column type="selection" width="40" align="center" />
|
||||
<el-table-column type="index" label="#" width="80" />
|
||||
<el-table-column prop="deviceCode" align="left" min-width="100" label="设备编码" />
|
||||
<el-table-column prop="deviceSn" align="left" min-width="150" label="设备唯一标识码" />
|
||||
<el-table-column prop="deviceName" align="left" min-width="150" label="设备名称" />
|
||||
<el-table-column prop="deviceIp" align="left" min-width="150" label="ip" />
|
||||
<el-table-column prop="devicePort" align="left" min-width="100" label="端口" />
|
||||
@@ -92,6 +96,8 @@
|
||||
</draggable-org-Tree>
|
||||
|
||||
</div>
|
||||
<upload-excel ref="uploadExcelRef" title="设备导入" url="/admin/deviceManage/read-excel"
|
||||
temp-url="/admin/sys-file/local/file/device_import_temp.xlsx" @refreshDataList="getDataList(false)" />
|
||||
<!-- 编辑、新增 -->
|
||||
<form-dialog ref="formDialogRef" @refresh="getDataList(false)" />
|
||||
</div>
|
||||
@@ -107,6 +113,8 @@ import dayjs from "dayjs";
|
||||
|
||||
const storesPanel = usePanel()
|
||||
|
||||
const uploadExcelRef = ref()
|
||||
|
||||
// 引入组件
|
||||
const FormDialog = defineAsyncComponent(() => import('../components/deviceForm.vue'));
|
||||
// 定义查询字典
|
||||
|
||||
@@ -153,12 +153,20 @@
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="remark" label="开门失败原因" min-width="200" show-overflow-tooltip />
|
||||
<el-table-column label="操作" min-width="100" fixed="right">
|
||||
<el-table-column label="操作" min-width="180" fixed="right">
|
||||
<template #default="{ row }">
|
||||
<el-button v-if="row.channelCode" icon="VideoCamera" text type="primary"
|
||||
@click="openReplayDialog(row)">
|
||||
<el-button v-if="row.channelCode && row.delPlaybackVideoFlag == '0'" icon="VideoCamera" text
|
||||
type="primary" @click="openReplayDialog(row)">
|
||||
回放
|
||||
</el-button>
|
||||
<el-button v-if="row.delPlaybackVideoFlag == '0'" v-auth="'doorRecord_del_play'" icon="delete"
|
||||
text type="primary" @click="delPlaybackVideo(row)">
|
||||
删除回放
|
||||
</el-button>
|
||||
<el-button v-if="row.delPlaybackVideoFlag == '1'" v-auth="'doorRecord_recover_playback'"
|
||||
icon="delete" text type="primary" @click="recoverPlaybackVideo(row)">
|
||||
恢复回放
|
||||
</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
@@ -177,6 +185,7 @@ import { useDict } from '/@/hooks/dict';
|
||||
import { parseTime } from "/@/utils/formatTime"
|
||||
import { usePanel } from "/@/stores/panel"
|
||||
import { getDefaultAlarmDateList } from "/@/utils/other"
|
||||
import { useMessage, useMessageBox } from "/@/hooks/message";
|
||||
|
||||
const storesPanel = usePanel()
|
||||
|
||||
@@ -277,6 +286,36 @@ function openReplayDialog(row: any) {
|
||||
|
||||
replayVideoDialogRef.value.openDialog('playbackGroup', row.channelCode, { startTime, endTime })
|
||||
}
|
||||
|
||||
async function delPlaybackVideo(row: any) {
|
||||
try {
|
||||
await useMessageBox().confirm('此操作将删除回放功能');
|
||||
} catch {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
await DoorRecordApi.delPlayback([row.id]);
|
||||
getDataList();
|
||||
useMessage().success('删除成功');
|
||||
} catch (err: any) {
|
||||
useMessage().error(err.msg);
|
||||
}
|
||||
}
|
||||
|
||||
async function recoverPlaybackVideo(row: any) {
|
||||
try {
|
||||
await useMessageBox().confirm('此操作将恢复回放功能');
|
||||
} catch {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
await DoorRecordApi.recoverPlayback([row.id]);
|
||||
getDataList();
|
||||
useMessage().success('恢复成功');
|
||||
} catch (err: any) {
|
||||
useMessage().error(err.msg);
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
|
||||
@@ -0,0 +1,133 @@
|
||||
<!-- 未出勤人数 -->
|
||||
<template>
|
||||
<div class="layout-padding">
|
||||
<div class="layout-padding-auto layout-padding-view">
|
||||
<el-row shadow="hover" v-show="showSearch" class="ml10">
|
||||
<el-form :model="state.queryForm" ref="queryRef" :inline="true" @keyup.enter="getDataList">
|
||||
<el-form-item style="width: 250px;" label="姓名或手机号" prop="nameOrPhone">
|
||||
<el-input v-model="state.queryForm.nameOrPhone" placeholder="请输入姓名或手机号" />
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button icon="search" type="primary" @click="getDataList">
|
||||
{{ $t('common.queryBtn') }}
|
||||
</el-button>
|
||||
<el-button icon="Refresh" @click="resetQuery">{{ $t('common.resetBtn') }}</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</el-row>
|
||||
<el-table :data="state.dataList" v-loading="state.loading" border :cell-style="tableStyle.cellStyle"
|
||||
:header-cell-style="tableStyle.headerCellStyle" @selection-change="selectionChangHandle"
|
||||
@sort-change="sortChangeHandle">
|
||||
<el-table-column type="index" label="#" width="80" />
|
||||
<el-table-column prop="code" min-width="100" label="未打卡时间">
|
||||
<template #default="{ row }">
|
||||
<span class="color-danger">{{ state.queryForm.clockDate }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="code" min-width="100" label="人员编号">
|
||||
<template #default="{ row }">
|
||||
<div v-if="row.code">
|
||||
<span v-if="row.code">{{ row.code }}</span>
|
||||
<span v-else class="color-danger">未知</span>
|
||||
</div>
|
||||
<div v-else>
|
||||
<span class="color-danger">访客</span>
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="personName" min-width="100" label="姓名">
|
||||
<template #default="{ row }">
|
||||
<div v-if="row.name">
|
||||
<span v-if="row.name">{{ row.name }}</span>
|
||||
<span v-else class="color-danger">未知</span>
|
||||
</div>
|
||||
<div v-else>
|
||||
<span class="color-danger">访客</span>
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="paperNumber" min-width="100" label="证件号码" show-overflow-tooltip>
|
||||
<template #default="{ row }">
|
||||
<div v-if="row.code">
|
||||
<span v-if="row.paperNumber">{{ row.paperNumber }}</span>
|
||||
<span v-else class="color-danger">未知</span>
|
||||
</div>
|
||||
<div v-else>
|
||||
<span class="color-danger">访客</span>
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="deptName" min-width="100" label="部门名称">
|
||||
<template #default="{ row }">
|
||||
<div v-if="row.code">
|
||||
<span v-if="row.deptName">{{ row.deptName }}</span>
|
||||
<span v-else class="color-danger">未知</span>
|
||||
</div>
|
||||
<div v-else>
|
||||
<span class="color-danger">访客</span>
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<pagination @size-change="sizeChangeHandle" @current-change="currentChangeHandle"
|
||||
v-bind="state.pagination" />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts" name="doorNotReignDialog">
|
||||
import { BasicTableProps, useTable } from "/@/hooks/table";
|
||||
|
||||
import * as clockRecordApi from "/@/api/clock/clock-record";
|
||||
|
||||
const route = useRoute();
|
||||
|
||||
// 搜索变量
|
||||
const queryRef = ref()
|
||||
// 定义变量内容
|
||||
const showSearch = ref(true)
|
||||
// 多选变量
|
||||
const selectObjs = ref([]) as any
|
||||
const multiple = ref(true)
|
||||
|
||||
const state: BasicTableProps = reactive<BasicTableProps>({
|
||||
createdIsNeed: false,
|
||||
pagination: {
|
||||
size: 10,
|
||||
},
|
||||
queryForm: {
|
||||
},
|
||||
pageList: clockRecordApi.notClockPage
|
||||
})
|
||||
|
||||
// table hook
|
||||
const {
|
||||
getDataList,
|
||||
currentChangeHandle,
|
||||
sizeChangeHandle,
|
||||
sortChangeHandle,
|
||||
tableStyle
|
||||
} = useTable(state)
|
||||
|
||||
// 清空搜索条件
|
||||
const resetQuery = () => {
|
||||
// 清空搜索条件
|
||||
queryRef.value?.resetFields()
|
||||
getDataList()
|
||||
}
|
||||
|
||||
// 多选事件
|
||||
const selectionChangHandle = (objs: { id: string }[]) => {
|
||||
selectObjs.value = objs.map(({ id }) => id);
|
||||
multiple.value = !objs.length;
|
||||
};
|
||||
|
||||
onMounted(() => {
|
||||
const time = route?.query?.time
|
||||
if (time) {
|
||||
state.queryForm.clockDate = time
|
||||
getDataList()
|
||||
}
|
||||
})
|
||||
|
||||
</script>
|
||||
@@ -210,6 +210,12 @@ onBeforeMount(() => {
|
||||
}
|
||||
storesPanel.openFromPanel()
|
||||
}
|
||||
if (query && query.action == 'admin') {
|
||||
if (query.params) {
|
||||
Object.assign(state.queryForm, JSON.parse(query.params))
|
||||
getDataList()
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
onBeforeUnmount(() => {
|
||||
@@ -238,7 +244,7 @@ async function dataCleaning() {
|
||||
background: 'rgba(0, 0, 0, 0.7)',
|
||||
})
|
||||
try {
|
||||
await doorRecordApi.dataCleaning({})
|
||||
await doorRecordApi.dataCleaning(state.queryForm)
|
||||
} catch (error) {
|
||||
console.error("|清洗数据|意外的错误", error);
|
||||
} finally {
|
||||
|
||||
@@ -32,15 +32,44 @@
|
||||
<span class="color-primary">{{ row.weekStr }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column min-width="100" align="center" label="打卡人数">
|
||||
<el-table-column min-width="100" align="center" label="出勤人数">
|
||||
<el-table-column prop="reignCount" min-width="100" align="center" label="人闸识别">
|
||||
<template #default="{ row }">
|
||||
<div class="color-success">
|
||||
<span> {{ row.reignCount ?? 0 }}</span>
|
||||
<span>次</span>
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="carReignCount" min-width="100" align="center" label="道闸识别">
|
||||
<template #default="{ row }">
|
||||
<div class="color-success">
|
||||
<span> {{ row.carReignCount ?? 0 }}</span>
|
||||
<span>次</span>
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="totalReignCount" min-width="100" align="center" label="总识别数">
|
||||
<template #default="{ row }">
|
||||
<div class="color-success">
|
||||
<span> {{ row.totalReignCount ?? 0 }}</span>
|
||||
<span>次</span>
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table-column>
|
||||
<el-table-column prop="notReignCount" min-width="100" align="center" label="未出勤人数">
|
||||
<template #default="{ row }">
|
||||
<div class="color-success">
|
||||
<span> {{ row.reignCount }}</span>
|
||||
<div class="not-reign-count color-danger" @click="doNotReignCount(row)">
|
||||
<span> {{ row.notReignCount ?? 0 }}</span>
|
||||
<span>次</span>
|
||||
<span v-if="row.notReignCount" style="font-size: 12px; margin-left: 4px; opacity: 0.8;">
|
||||
(点击查看)
|
||||
</span>
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column min-width="100" align="center" label="异常进入人数">
|
||||
<el-table-column prop="lateCount" min-width="100" align="center" label="异常进入人数">
|
||||
<template #default="{ row }">
|
||||
<div class="color-danger">
|
||||
<span> {{ row.lateCount }}</span>
|
||||
@@ -48,7 +77,7 @@
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column min-width="100" align="center" label="异常外出人数">
|
||||
<el-table-column prop="earlyCount" min-width="100" align="center" label="异常外出人数">
|
||||
<template #default="{ row }">
|
||||
<div class="color-danger">
|
||||
<span> {{ row.earlyCount }}</span>
|
||||
@@ -123,6 +152,13 @@ const exportExcel = () => {
|
||||
downBlobFile('/door/doorRecord/export', Object.assign(state.queryForm, { ids: selectObjs }), 'doorRecord.xlsx')
|
||||
}
|
||||
|
||||
const doNotReignCount = (row: any) => {
|
||||
push({
|
||||
path: "/door/doorNotReign/index",
|
||||
query: { time: row.time }
|
||||
})
|
||||
}
|
||||
|
||||
// 多选事件
|
||||
const selectionChangHandle = (objs: { id: string }[]) => {
|
||||
selectObjs.value = objs.map(({ id }) => id);
|
||||
@@ -131,7 +167,7 @@ const selectionChangHandle = (objs: { id: string }[]) => {
|
||||
|
||||
const getSummaries = (param: any) => {
|
||||
const { columns, data } = param
|
||||
const totalColumns = ["preCount"]
|
||||
const totalColumns = ["reignCount", "carReignCount", "totalReignCount", "notReignCount", "lateCount", "earlyCount"]
|
||||
const totalRow = Array.from({ length: columns.length })
|
||||
totalRow[0] = "合计"
|
||||
columns.forEach((i: any, index: number) => {
|
||||
@@ -158,4 +194,18 @@ function addDays(dateStr: string, days: number) {
|
||||
date.setDate(date.getDate() + days);
|
||||
return date.toISOString().split('T')[0];
|
||||
};
|
||||
</script>
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.not-reign-count {
|
||||
cursor: pointer;
|
||||
display: inline-block;
|
||||
transition: transform 0.2s ease-in-out;
|
||||
|
||||
&:hover {
|
||||
text-decoration: underline;
|
||||
text-decoration-color: var(--el-color-danger);
|
||||
transform: scale(1.1);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -51,7 +51,7 @@
|
||||
</el-table-column>
|
||||
</el-table-column>
|
||||
<el-table-column min-width="100" label="统计">
|
||||
<el-table-column min-width="100" label="打卡次数">
|
||||
<el-table-column min-width="100" label="出勤次数">
|
||||
<template #default="{ row }">
|
||||
<div class="color-danger">
|
||||
<span> {{ row.reignCount ?? 0 }}</span>
|
||||
|
||||
@@ -15,6 +15,12 @@
|
||||
<dict-select v-model="state.queryForm.openType" :options="dh_door_open_type"
|
||||
placeholder="请选择进开门类型" />
|
||||
</el-form-item>
|
||||
<el-form-item style="width: 250px;" label="人员类型" prop="personType">
|
||||
<select-person-car-type areaType="person" v-model="state.queryForm.personType" />
|
||||
</el-form-item>
|
||||
<el-form-item style="width: 250px;" label="区域" prop="areaType">
|
||||
<select-area-type areaType="person" v-model="state.queryForm.areaType" />
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button icon="search" type="primary" @click="getDataList">
|
||||
{{ $t('common.queryBtn') }}
|
||||
|
||||
@@ -0,0 +1,195 @@
|
||||
<template>
|
||||
<div class="layout-padding">
|
||||
<div class="layout-padding-auto layout-padding-view">
|
||||
<el-row shadow="hover" v-show="showSearch" class="ml10">
|
||||
<el-form :model="state.queryForm" ref="queryRef" :inline="true" @keyup.enter="getDataList">
|
||||
<el-form-item style="width: 400px;" label="日期" prop="swingTimeList">
|
||||
<el-date-picker v-model="state.queryForm.swingTimeList" value-format="YYYY-MM-DD HH:mm:ss"
|
||||
type="daterange" />
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button icon="search" type="primary" @click="getDataList">
|
||||
{{ $t('common.queryBtn') }}
|
||||
</el-button>
|
||||
<el-button icon="Refresh" @click="resetQuery">{{ $t('common.resetBtn') }}</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</el-row>
|
||||
<el-table :data="state.dataList" v-loading="state.loading" border :cell-style="tableStyle.cellStyle"
|
||||
:header-cell-style="tableStyle.headerCellStyle" @selection-change="selectionChangHandle"
|
||||
@sort-change="sortChangeHandle" show-summary :summary-method="getSummaries">
|
||||
<el-table-column min-width="140" label="日期">
|
||||
<template #default="{ row }">
|
||||
<span>{{ row.time }}</span>
|
||||
<span> - </span>
|
||||
<span class="color-primary">{{ row.weekStr }}</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="enterCount" align="center" min-width="200" label="进门总次数">
|
||||
<template #default="{ row }">
|
||||
<div class="color-success enter-count" @click="doEnterPage(row)">
|
||||
<span class="color-success">{{ row.enterCount }}</span>
|
||||
<span>次</span>
|
||||
<span v-if="row.enterCount" style="font-size: 12px; margin-left: 4px; opacity: 0.8;">
|
||||
(点击查看)
|
||||
</span>
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="exitCount" align="center" min-width="200" label="出门总次数">
|
||||
<template #default="{ row }">
|
||||
<div class="color-danger exit-count" @click="doExitPage(row)">
|
||||
<span class="color-danger">{{ row.exitCount }}</span>
|
||||
<span>次</span>
|
||||
<span v-if="row.exitCount" style="font-size: 12px; margin-left: 4px; opacity: 0.8;">
|
||||
(点击查看)
|
||||
</span>
|
||||
</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="exitOrExitCount" align="center" min-width="200" label="进/出门总次数">
|
||||
<template #default="{ row }">
|
||||
<span class="color-primary">{{ row.exitOrExitCount }}次</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="insideCount" align="center" min-width="200" label="预计在楼总人数">
|
||||
<template #default="{ row }">
|
||||
<span class="color-primary">{{ row.insideCount }}人</span>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<pagination @size-change="sizeChangeHandle" @current-change="currentChangeHandle"
|
||||
v-bind="state.pagination" />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts" name="jglTotal">
|
||||
import { BasicTableProps, useTable } from "/@/hooks/table";
|
||||
import { useDict } from '/@/hooks/dict';
|
||||
import dayjs from "dayjs"
|
||||
|
||||
const { push } = useRouter()
|
||||
|
||||
import * as DoorRecordApi from "/@/api/door/doorRecord";
|
||||
|
||||
// 搜索变量
|
||||
const queryRef = ref()
|
||||
const showSearch = ref(true)
|
||||
|
||||
// 多选变量
|
||||
const selectObjs = ref([]) as any
|
||||
const multiple = ref(true)
|
||||
|
||||
const state: BasicTableProps = reactive<BasicTableProps>({
|
||||
queryForm: {},
|
||||
pageList: DoorRecordApi.jglTimesPage
|
||||
})
|
||||
|
||||
// table hook
|
||||
const {
|
||||
getDataList,
|
||||
currentChangeHandle,
|
||||
sizeChangeHandle,
|
||||
sortChangeHandle,
|
||||
downBlobFile,
|
||||
tableStyle
|
||||
} = useTable(state)
|
||||
|
||||
// 清空搜索条件
|
||||
const resetQuery = () => {
|
||||
// 清空搜索条件
|
||||
queryRef.value?.resetFields()
|
||||
// 清空多选
|
||||
selectObjs.value = []
|
||||
getDataList()
|
||||
}
|
||||
|
||||
// 多选事件
|
||||
const selectionChangHandle = (objs: { id: string }[]) => {
|
||||
selectObjs.value = objs.map(({ id }) => id);
|
||||
multiple.value = !objs.length;
|
||||
};
|
||||
|
||||
// 去查询进入机关楼人员信息
|
||||
const doEnterPage = (row: any) => {
|
||||
push({
|
||||
path: "/door/doorRecord/index",
|
||||
query: {
|
||||
action: "admin",
|
||||
params: JSON.stringify({
|
||||
personType: '1',
|
||||
areaType: 'BGL',
|
||||
openType: '61',
|
||||
swingTimeList: [
|
||||
dayjs(row.time).startOf('day').format("YYYY-MM-DD HH:mm:ss"),
|
||||
dayjs(row.time).endOf('day').format("YYYY-MM-DD HH:mm:ss"),
|
||||
],
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// 去查询出门机关楼人员信息
|
||||
const doExitPage = (row: any) => {
|
||||
push({
|
||||
path: "/door/doorRecord/index",
|
||||
query: {
|
||||
action: "admin",
|
||||
params: JSON.stringify({
|
||||
// personType: '1',
|
||||
areaType: 'BGL',
|
||||
openType: '57',
|
||||
swingTimeList: [
|
||||
dayjs(row.time).startOf('day').format("YYYY-MM-DD HH:mm:ss"),
|
||||
dayjs(row.time).endOf('day').format("YYYY-MM-DD HH:mm:ss"),
|
||||
],
|
||||
}),
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
const getSummaries = (param: any) => {
|
||||
const { columns, data } = param
|
||||
const totalColumns = ["enterCount", "exitCount", "exitOrExitCount"]
|
||||
const totalRow = Array.from({ length: columns.length })
|
||||
totalRow[0] = "合计"
|
||||
columns.forEach((i: any, index: number) => {
|
||||
if (totalColumns.includes(i.property)) {
|
||||
const total = data
|
||||
.filter((v: any) => v[i.property] != null)
|
||||
.map((v: any) => Number(v[i.property]))
|
||||
.reduce((pren: number, next: number) => pren + next, 0)
|
||||
totalRow[index] = `${total}次`
|
||||
}
|
||||
})
|
||||
return totalRow
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.enter-count {
|
||||
cursor: pointer;
|
||||
display: inline-block;
|
||||
transition: transform 0.2s ease-in-out;
|
||||
|
||||
&:hover {
|
||||
text-decoration: underline;
|
||||
text-decoration-color: var(--el-color-success);
|
||||
transform: scale(1.1);
|
||||
}
|
||||
}
|
||||
|
||||
.exit-count {
|
||||
cursor: pointer;
|
||||
display: inline-block;
|
||||
transition: transform 0.2s ease-in-out;
|
||||
|
||||
&:hover {
|
||||
text-decoration: underline;
|
||||
text-decoration-color: var(--el-color-danger);
|
||||
transform: scale(1.1);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -7,17 +7,12 @@
|
||||
|
||||
<!-- Steps Navigation -->
|
||||
<div class="flex-2 text-center max-w-[600px]">
|
||||
<span
|
||||
v-for="(step, index) in steps"
|
||||
:key="index"
|
||||
class="inline-block px-5 py-2.5 cursor-pointer"
|
||||
<span v-for="(step, index) in steps" :key="index" class="inline-block px-5 py-2.5 cursor-pointer"
|
||||
:class="{ 'border-b-2 border-primary text-primary': activeStep === index }"
|
||||
@click="activeStep = index"
|
||||
>
|
||||
@click="activeStep = index">
|
||||
<span
|
||||
class="mr-1.5 inline-block w-6 h-6 text-base font-normal text-center leading-[22px] border rounded-full"
|
||||
:class="[activeStep === index ? 'bg-primary text-white' : 'border-current']"
|
||||
>
|
||||
:class="[activeStep === index ? 'bg-primary text-white' : 'border-current']">
|
||||
{{ index + 1 }}
|
||||
</span>
|
||||
<span class="text-lg font-medium">{{ $t(step.title) }}</span>
|
||||
@@ -47,18 +42,18 @@
|
||||
|
||||
<div class="text-center">
|
||||
<!-- Success Result -->
|
||||
<el-result v-if="validateFlowStep === 3" icon="success" :title="$t('flow.checkSuccess')" :sub-title="$t('flow.checkSubSuccess')">
|
||||
<el-result v-if="validateFlowStep === 3" icon="success" :title="$t('flow.checkSuccess')"
|
||||
:sub-title="$t('flow.checkSubSuccess')">
|
||||
<template #extra>
|
||||
<el-button type="primary" :loading="isSubmitting" @click="submitFlow">{{ $t('flow.submit') }}</el-button>
|
||||
<el-button type="primary" :loading="isSubmitting" @click="submitFlow">{{ $t('flow.submit')
|
||||
}}</el-button>
|
||||
</template>
|
||||
</el-result>
|
||||
|
||||
<!-- Loading Result -->
|
||||
<el-result
|
||||
v-if="validateErrMsg.length === 0 && validateDialogShow && validatingShow && validateFlowStep < 3"
|
||||
:title="$t('flow.checkIng')"
|
||||
:sub-title="$t('flow.checkSubIng')"
|
||||
>
|
||||
:title="$t('flow.checkIng')" :sub-title="$t('flow.checkSubIng')">
|
||||
<template #icon>
|
||||
<span class="inline-block w-25 h-25" v-loading="true"></span>
|
||||
</template>
|
||||
@@ -68,7 +63,9 @@
|
||||
<el-result v-if="validateErrMsg.length > 0" icon="error" title="检查失败">
|
||||
<template #sub-title>
|
||||
<div v-for="item in validateErrMsg" :key="item" class="text-red-500">
|
||||
<el-icon><WarnTriangleFilled /></el-icon>
|
||||
<el-icon>
|
||||
<WarnTriangleFilled />
|
||||
</el-icon>
|
||||
{{ item }}
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -117,7 +117,7 @@ const getThemeConfig = computed(() => {
|
||||
*/
|
||||
const jumpAdminPanel = () => {
|
||||
const isDev = import.meta.env.MODE == "development"
|
||||
const baseAdminUrl = isDev ? 'http://localhost:5173/device' : 'http://192.168.50.10:7001/device';
|
||||
const baseAdminUrl = isDev ? 'http://localhost:5173/device' : 'http://123.157.250.162:7001/device';
|
||||
const roleCodes = useUserInfoStore.userInfos?.roleCodes ?? []
|
||||
if (!roleCodes || !roleCodes.length) {
|
||||
openPlanUrl(baseAdminUrl, true)
|
||||
|
||||
@@ -51,13 +51,13 @@
|
||||
|
||||
<el-table-column prop="req" label="请求参数" width="250">
|
||||
<template #default="{ row }">
|
||||
<preview-json :json="row.req" />
|
||||
<preview-json :lazyJson="() => getJson(row, 'req')" :json="row.req" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
|
||||
<el-table-column prop="res" label="请求响应" width="250">
|
||||
<template #default="{ row }">
|
||||
<preview-json :json="{ html: row.res }" />
|
||||
<preview-json :lazyJson="() => getJson(row, 'res')" :json="{ html: row.res }" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
|
||||
@@ -123,6 +123,20 @@ const exportExcel = () => {
|
||||
downBlobFile('/third/thirdLog/export', Object.assign(state.queryForm, { ids: selectObjs }), 'thirdLog.xlsx')
|
||||
}
|
||||
|
||||
// 获取日志详情
|
||||
const getJson = async (row: any, key: string) => {
|
||||
try {
|
||||
const { data, code } = await ThirdLogApi.getObj(row.id)
|
||||
if (code != 0) {
|
||||
throw new Error("获取日志详情失败")
|
||||
}
|
||||
return data[key]
|
||||
} catch (error) {
|
||||
console.log("获取日志详情,意外的错误error:", error);
|
||||
return ""
|
||||
}
|
||||
}
|
||||
|
||||
// 多选事件
|
||||
const selectionChangHandle = (objs: { id: string }[]) => {
|
||||
selectObjs.value = objs.map(({ id }) => id);
|
||||
|
||||
Executable
+123
@@ -0,0 +1,123 @@
|
||||
<template>
|
||||
<el-dialog :title="form.id ? '编辑' : '新增'" v-model="visible" :close-on-click-modal="false" draggable>
|
||||
<el-form ref="dataFormRef" :model="form" :rules="dataRules" formDialogRef label-width="90px" v-loading="loading">
|
||||
<el-row :gutter="24">
|
||||
<el-col :span="24" class="mb20">
|
||||
<el-form-item label="标题" prop="title">
|
||||
<el-input v-model="form.title" placeholder="请输入巡检标题" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12" class="mb20">
|
||||
<el-form-item label="巡检人" prop="assigneeId">
|
||||
<select-person v-model="form.assigneeId" placeholder="请选择巡检人" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12" class="mb20">
|
||||
<el-form-item label="巡检时间" prop="routingDate">
|
||||
<el-date-picker v-model="form.routingDate" value-format="YYYY-MM-DD HH:mm:ss" type="datetime"
|
||||
placeholder="请选择巡检时间" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="24" class="mb20">
|
||||
<el-form-item label="描述" prop="description">
|
||||
<el-input type="textarea" v-model="form.description" :autosize="{ minRows: 6 }" placeholder="请输入巡检描述" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<span class="dialog-footer">
|
||||
<el-button @click="visible = false">取消</el-button>
|
||||
<el-button type="primary" @click="onSubmit" :disabled="loading">确认</el-button>
|
||||
</span>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts" name="RoutingManageDialog">
|
||||
import { dayjs } from 'element-plus';
|
||||
import { useDict } from '/@/hooks/dict';
|
||||
import { useMessage } from "/@/hooks/message";
|
||||
import * as routingManageApi from "/@/api/todo/routingManage";
|
||||
import { rule } from '/@/utils/validate';
|
||||
const emit = defineEmits(['refresh']);
|
||||
|
||||
// 定义变量内容
|
||||
const dataFormRef = ref();
|
||||
const visible = ref(false)
|
||||
const loading = ref(false)
|
||||
// 定义字典
|
||||
|
||||
// 提交表单数据
|
||||
const form = reactive({
|
||||
id: '',
|
||||
title: '',
|
||||
description: '',
|
||||
assigneeId: '',
|
||||
assigneeName: '',
|
||||
routingDate: '',
|
||||
});
|
||||
|
||||
const getDefaultRoutingDate = () => dayjs().format('YYYY-MM-DD HH:mm:ss');
|
||||
|
||||
// 定义校验规则
|
||||
const dataRules = ref({
|
||||
title: [{ required: true, message: '标题不能为空', trigger: 'blur' }],
|
||||
routingDate: [{ required: true, message: '巡检时间不能为空', trigger: 'change' }],
|
||||
})
|
||||
|
||||
// 打开弹窗
|
||||
const openDialog = (id: string) => {
|
||||
visible.value = true
|
||||
form.id = ''
|
||||
|
||||
// 重置表单数据
|
||||
nextTick(() => {
|
||||
dataFormRef.value?.resetFields();
|
||||
if (!id) {
|
||||
form.routingDate = getDefaultRoutingDate();
|
||||
}
|
||||
});
|
||||
|
||||
// 获取routingManage信息
|
||||
if (id) {
|
||||
form.id = id
|
||||
getroutingManageData(id)
|
||||
}
|
||||
};
|
||||
|
||||
// 提交
|
||||
const onSubmit = async () => {
|
||||
const valid = await dataFormRef.value.validate().catch(() => { });
|
||||
if (!valid) return false;
|
||||
|
||||
try {
|
||||
loading.value = true;
|
||||
form.id ? await routingManageApi.putObj(form) : await routingManageApi.addObj(form);
|
||||
useMessage().success(form.id ? '修改成功' : '添加成功');
|
||||
visible.value = false;
|
||||
emit('refresh');
|
||||
} catch (err: any) {
|
||||
useMessage().error(err.msg);
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
// 初始化表单数据
|
||||
const getroutingManageData = (id: string) => {
|
||||
// 获取数据
|
||||
loading.value = true
|
||||
routingManageApi.getObj(id).then((res: any) => {
|
||||
Object.assign(form, res.data)
|
||||
}).finally(() => {
|
||||
loading.value = false
|
||||
})
|
||||
};
|
||||
|
||||
// 暴露变量
|
||||
defineExpose({
|
||||
openDialog
|
||||
});
|
||||
</script>
|
||||
Executable
+137
@@ -0,0 +1,137 @@
|
||||
<template>
|
||||
<div class="layout-padding">
|
||||
<div class="layout-padding-auto layout-padding-view">
|
||||
<el-row>
|
||||
<div class="mb8" style="width: 100%">
|
||||
<el-form :model="state.queryForm" ref="queryRef" :inline="true" @keyup.enter="getDataList">
|
||||
<el-form-item style="width: 250px;" label="标题" prop="title">
|
||||
<el-input v-model="state.queryForm.title" placeholder="请输入标题" />
|
||||
</el-form-item>
|
||||
<el-form-item style="width: 480px;" label="巡检创建时间" prop="createTimeList">
|
||||
<el-date-picker v-model="state.queryForm.createTimeList" value-format="YYYY-MM-DD HH:mm:ss"
|
||||
type="datetimerange" />
|
||||
</el-form-item>
|
||||
<el-form-item style="width: 480px;" label="巡检时间" prop="routingDateList">
|
||||
<el-date-picker v-model="state.queryForm.routingDateList" value-format="YYYY-MM-DD HH:mm:ss"
|
||||
type="datetimerange" />
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button icon="search" type="primary" @click="getDataList">
|
||||
{{ $t('common.queryBtn') }}
|
||||
</el-button>
|
||||
<el-button icon="Refresh" @click="resetQuery">
|
||||
{{ $t('common.resetBtn') }}
|
||||
</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<el-button icon="folder-add" type="primary" class="ml10" @click="formDialogRef.openDialog()"
|
||||
v-auth="'admin_routingManage_add'">
|
||||
新 增
|
||||
</el-button>
|
||||
<el-button plain :disabled="multiple" icon="Delete" type="primary" v-auth="'admin_routingManage_del'"
|
||||
@click="handleDelete(selectObjs)">
|
||||
删除
|
||||
</el-button>
|
||||
<!-- :export="'admin_routingManage_export'" -->
|
||||
<right-toolbar v-model:showSearch="showSearch" @exportExcel="exportExcel" class="ml10 mr20"
|
||||
style="float: right;" @queryTable="getDataList"></right-toolbar>
|
||||
</div>
|
||||
</el-row>
|
||||
<el-table :data="state.dataList" v-loading="state.loading" border :cell-style="tableStyle.cellStyle"
|
||||
: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="title" label="标题" show-overflow-tooltip />
|
||||
<el-table-column prop="assigneeName" label="巡检人" show-overflow-tooltip />
|
||||
<el-table-column prop="routingDate" label="巡检时间" width="180" />
|
||||
<el-table-column prop="description" label="备注" show-overflow-tooltip />
|
||||
<el-table-column label="操作" width="150">
|
||||
<template #default="scope">
|
||||
<el-button icon="edit-pen" text type="primary" v-auth="'admin_routingManage_edit'"
|
||||
@click="formDialogRef.openDialog(scope.row.id)">编辑</el-button>
|
||||
<el-button icon="delete" text type="primary" v-auth="'admin_routingManage_del'"
|
||||
@click="handleDelete([scope.row.id])">删除</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<pagination @size-change="sizeChangeHandle" @current-change="currentChangeHandle" v-bind="state.pagination" />
|
||||
</div>
|
||||
|
||||
<!-- 编辑、新增 -->
|
||||
<form-dialog ref="formDialogRef" @refresh="getDataList(false)" />
|
||||
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts" name="systemRoutingManage">
|
||||
import { BasicTableProps, useTable } from "/@/hooks/table";
|
||||
import * as routingManageApi from "/@/api/todo/routingManage";
|
||||
import { useMessage, useMessageBox } from "/@/hooks/message";
|
||||
import { useDict } from '/@/hooks/dict';
|
||||
|
||||
// 引入组件
|
||||
const FormDialog = defineAsyncComponent(() => import('./form.vue'));
|
||||
// 定义查询字典
|
||||
|
||||
// 定义变量内容
|
||||
const formDialogRef = ref()
|
||||
// 搜索变量
|
||||
const queryRef = ref()
|
||||
const showSearch = ref(true)
|
||||
// 多选变量
|
||||
const selectObjs = ref([]) as any
|
||||
const multiple = ref(true)
|
||||
|
||||
const state: BasicTableProps = reactive<BasicTableProps>({
|
||||
queryForm: {},
|
||||
pageList: routingManageApi.fetchList
|
||||
})
|
||||
|
||||
// table hook
|
||||
const {
|
||||
getDataList,
|
||||
currentChangeHandle,
|
||||
sizeChangeHandle,
|
||||
sortChangeHandle,
|
||||
downBlobFile,
|
||||
tableStyle
|
||||
} = useTable(state)
|
||||
|
||||
// 清空搜索条件
|
||||
const resetQuery = () => {
|
||||
// 清空搜索条件
|
||||
queryRef.value?.resetFields()
|
||||
// 清空多选
|
||||
selectObjs.value = []
|
||||
getDataList()
|
||||
}
|
||||
|
||||
// 导出excel
|
||||
const exportExcel = () => {
|
||||
downBlobFile('/admin/routingManage/export', Object.assign(state.queryForm, { ids: selectObjs }), 'routingManage.xlsx')
|
||||
}
|
||||
|
||||
// 多选事件
|
||||
const selectionChangHandle = (objs: { id: string }[]) => {
|
||||
selectObjs.value = objs.map(({ id }) => id);
|
||||
multiple.value = !objs.length;
|
||||
};
|
||||
|
||||
// 删除操作
|
||||
const handleDelete = async (ids: string[]) => {
|
||||
try {
|
||||
await useMessageBox().confirm('此操作将永久删除');
|
||||
} catch {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
await routingManageApi.delObjs(ids);
|
||||
getDataList();
|
||||
useMessage().success('删除成功');
|
||||
} catch (err: any) {
|
||||
useMessage().error(err.msg);
|
||||
}
|
||||
};
|
||||
</script>
|
||||
@@ -0,0 +1,144 @@
|
||||
<template>
|
||||
<el-dialog :title="'详情'" width="70%" v-model="visible" :close-on-click-modal="false" draggable>
|
||||
<div class="header">
|
||||
<div class="title">基本信息</div>
|
||||
<div class="info" v-loading="loading">
|
||||
<div class="info-item">
|
||||
<span>标题:</span>
|
||||
<span>{{ form.title }}</span>
|
||||
</div>
|
||||
<div class="info-item">
|
||||
<span>状态:</span>
|
||||
<span>
|
||||
<dict-tag :options="todo_status" :value="form.status" />
|
||||
</span>
|
||||
</div>
|
||||
<div class="info-item">
|
||||
<span>优先级:</span>
|
||||
<span>
|
||||
<dict-tag :options="todo_priority" :value="form.priority" />
|
||||
</span>
|
||||
</div>
|
||||
<div class="info-item">
|
||||
<span>处理人:</span>
|
||||
<span>{{ form.assigneeName }}</span>
|
||||
</div>
|
||||
<div class="info-item">
|
||||
<span>预计完成时间:</span>
|
||||
<span v-if="form.startDate && form.dueDate">
|
||||
{{ form.startDate }} 至 {{ form.dueDate }}
|
||||
</span>
|
||||
<span class="color-danger" v-else>未设置</span>
|
||||
</div>
|
||||
<div class="info-item">
|
||||
<span>创建时间:</span>
|
||||
<span>{{ form.createTime }}</span>
|
||||
</div>
|
||||
<div class="info-item">
|
||||
<span>完成时间:</span>
|
||||
<span v-if="form.completedDate">{{ form.completedDate }}</span>
|
||||
<span class="color-danger" v-else>未完成</span>
|
||||
</div>
|
||||
<div class="info-item" style="grid-column: 1 / -1;">
|
||||
<span>描述:</span>
|
||||
<span v-html="form.description"></span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</el-dialog>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts" name="ServiceManageDetailDialog">
|
||||
import { useDict } from '/@/hooks/dict';
|
||||
import * as serviceManageApi from "/@/api/todo/serviceManage"
|
||||
|
||||
const visible = ref(false)
|
||||
const loading = ref(false)
|
||||
|
||||
const { todo_priority, todo_status } = useDict("todo_priority", "todo_status")
|
||||
|
||||
const form = reactive<any>({
|
||||
id: '',
|
||||
title: '',
|
||||
description: '',
|
||||
status: '',
|
||||
priority: '',
|
||||
assigneeId: '',
|
||||
assigneeName: '',
|
||||
orgName: '',
|
||||
startDate: '',
|
||||
dueDate: '',
|
||||
completedDate: '',
|
||||
reminderTime: '',
|
||||
remindChannel: '',
|
||||
tags: '',
|
||||
createTime: ''
|
||||
})
|
||||
|
||||
const openDialog = (id: string) => {
|
||||
visible.value = true
|
||||
loading.value = true
|
||||
serviceManageApi.getObj(id).then((res: any) => {
|
||||
Object.assign(form, res.data)
|
||||
}).finally(() => {
|
||||
loading.value = false
|
||||
})
|
||||
};
|
||||
|
||||
defineExpose({
|
||||
openDialog
|
||||
});
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.info {
|
||||
padding: 10px 20px;
|
||||
display: grid;
|
||||
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||
gap: 10px;
|
||||
|
||||
&-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
width: 100%;
|
||||
|
||||
>span:first-child {
|
||||
width: 40%;
|
||||
color: #333;
|
||||
font-size: 13px;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
>span:last-child {
|
||||
flex: 1;
|
||||
color: #666;
|
||||
font-size: 13px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.title {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 10px 20px;
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
|
||||
&::before {
|
||||
content: "";
|
||||
display: block;
|
||||
width: 4px;
|
||||
height: 15px;
|
||||
margin-right: 4px;
|
||||
border-radius: 2px;
|
||||
background: var(--el-color-primary);
|
||||
}
|
||||
}
|
||||
|
||||
.header {
|
||||
width: 100%;
|
||||
background: var(--el-color-white);
|
||||
border: 1px solid var(--el-border-color-light, #ebeef5);
|
||||
border-radius: 4px;
|
||||
}
|
||||
</style>
|
||||
Executable
+168
@@ -0,0 +1,168 @@
|
||||
<template>
|
||||
<el-dialog :title="form.id ? '编辑' : '新增'" v-model="visible" :close-on-click-modal="false" draggable>
|
||||
<el-form ref="dataFormRef" :model="form" :rules="dataRules" formDialogRef label-width="120px" v-loading="loading">
|
||||
<el-row :gutter="24">
|
||||
<el-col :span="24" class="mb20">
|
||||
<el-form-item label="标题" prop="title">
|
||||
<el-input v-model="form.title" placeholder="请输入标题" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="24" class="mb20">
|
||||
<el-form-item label="描述" prop="description">
|
||||
<editor v-if="visible" v-model:get-html="form.description" placeholder="请输入描述" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="24" class="mb20">
|
||||
<el-form-item label="维修设备" prop="devices">
|
||||
<select-tree-channel v-model="form.devices" placeholder="请选择维修设备" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12" class="mb20">
|
||||
<el-form-item label="处理人" prop="assigneeId">
|
||||
<select-person v-model="form.assigneeId" placeholder="请选择处理人" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12" class="mb20">
|
||||
<el-form-item label="优先级" prop="priority">
|
||||
<dict-select v-model="form.priority" defaultValue="1" :options="todo_priority" placeholder="请选择优先级" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="24" class="mb20">
|
||||
<el-form-item label="预计完成时间" prop="date">
|
||||
<el-date-picker v-model="form.date" value-format="YYYY-MM-DD HH:mm:ss" type="datetimerange" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<span class="dialog-footer">
|
||||
<el-button @click="visible = false">取消</el-button>
|
||||
<el-button type="primary" @click="onSubmit" :disabled="loading">确认</el-button>
|
||||
</span>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts" name="ServiceManageDialog">
|
||||
import { dayjs } from 'element-plus';
|
||||
import { useDict } from '/@/hooks/dict';
|
||||
import { useMessage } from "/@/hooks/message";
|
||||
import * as serviceManageApi from '/@/api/todo/serviceManage'
|
||||
import { rule } from '/@/utils/validate';
|
||||
const emit = defineEmits(['refresh']);
|
||||
|
||||
// 定义变量内容
|
||||
const dataFormRef = ref();
|
||||
const visible = ref(false)
|
||||
const loading = ref(false)
|
||||
// 定义字典
|
||||
const { todo_priority } = useDict("todo_priority")
|
||||
// 提交表单数据
|
||||
const defaultForm = {
|
||||
id: '',
|
||||
title: '',
|
||||
devices: [],
|
||||
description: '',
|
||||
status: '',
|
||||
priority: '',
|
||||
assigneeId: '',
|
||||
assigneeName: '',
|
||||
orgName: '',
|
||||
startDate: '',
|
||||
dueDate: '',
|
||||
completedDate: '',
|
||||
date: [],
|
||||
};
|
||||
const form = reactive<any>({ ...defaultForm });
|
||||
|
||||
const getDefaultDateRange = () => {
|
||||
const today = dayjs().format('YYYY-MM-DD');
|
||||
return [`${today} 00:00:00`, `${today} 23:23:23`];
|
||||
}
|
||||
|
||||
// 定义校验规则
|
||||
const dataRules = ref({
|
||||
title: [{ required: true, message: '标题不能为空', trigger: 'blur' }],
|
||||
priority: [{ required: true, message: '优先级不能为空', trigger: 'change' }],
|
||||
})
|
||||
|
||||
// 打开弹窗
|
||||
const openDialog = (id: string) => {
|
||||
Object.assign(form, {
|
||||
...defaultForm,
|
||||
date: id ? [] : getDefaultDateRange(),
|
||||
});
|
||||
visible.value = true
|
||||
|
||||
nextTick(() => {
|
||||
dataFormRef.value?.clearValidate();
|
||||
});
|
||||
|
||||
// 获取serviceManage信息
|
||||
if (id) {
|
||||
form.id = id
|
||||
getserviceManageData(id)
|
||||
}
|
||||
};
|
||||
|
||||
// 提交
|
||||
const onSubmit = async () => {
|
||||
const valid = await dataFormRef.value.validate().catch(() => { });
|
||||
if (!valid) return false;
|
||||
|
||||
try {
|
||||
loading.value = true;
|
||||
const _params = { ...form }
|
||||
if (_params.date && _params.date.length == 2) {
|
||||
_params.startDate = _params.date[0]
|
||||
_params.dueDate = _params.date[1]
|
||||
}
|
||||
// 处理设备列表
|
||||
_params.devices = _params.devices?.join(',') || ''
|
||||
|
||||
form.id ? await serviceManageApi.putObj(_params) : await serviceManageApi.addObj(_params);
|
||||
useMessage().success(form.id ? '修改成功' : '添加成功');
|
||||
visible.value = false;
|
||||
emit('refresh');
|
||||
} catch (err: any) {
|
||||
useMessage().error(err.msg);
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
// 初始化表单数据
|
||||
const getserviceManageData = async (id: string) => {
|
||||
// 获取数据
|
||||
loading.value = true
|
||||
try {
|
||||
const { data } = await serviceManageApi.getObj(id)
|
||||
Object.assign(form, {
|
||||
...data,
|
||||
title: data.title ?? '',
|
||||
description: data.description ?? '',
|
||||
status: data.status ?? '',
|
||||
priority: data.priority ?? '',
|
||||
assigneeId: data.assigneeId ?? '',
|
||||
assigneeName: data.assigneeName ?? '',
|
||||
orgName: data.orgName ?? '',
|
||||
startDate: data.startDate ?? '',
|
||||
dueDate: data.dueDate ?? '',
|
||||
completedDate: data.completedDate ?? '',
|
||||
})
|
||||
form.devices = data.devices ? data.devices.split(',') : []
|
||||
form.date = data.startDate && data.dueDate ? [data.startDate, data.dueDate] : []
|
||||
|
||||
} catch (error) {
|
||||
console.log("初始化表单数据意外的错误error:", error);
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
};
|
||||
|
||||
// 暴露变量
|
||||
defineExpose({
|
||||
openDialog
|
||||
});
|
||||
</script>
|
||||
Executable
+218
@@ -0,0 +1,218 @@
|
||||
<template>
|
||||
<div class="layout-padding">
|
||||
<div class="layout-padding-auto layout-padding-view">
|
||||
<el-row>
|
||||
<div class="mb8" style="width: 100%">
|
||||
<el-form :model="state.queryForm" ref="queryRef" :inline="true" @keyup.enter="getDataList">
|
||||
<el-form-item style="width: 250px;" label="标题" prop="title">
|
||||
<el-input v-model="state.queryForm.title" placeholder="请输入标题" />
|
||||
</el-form-item>
|
||||
<el-form-item style="width: 250px;" label="状态" prop="status">
|
||||
<dict-select v-model="state.queryForm.status" dict-type="todo_status" />
|
||||
</el-form-item>
|
||||
<el-form-item style="width: 250px;" label="优先级" prop="priority">
|
||||
<dict-select v-model="state.queryForm.priority" dict-type="todo_priority" />
|
||||
</el-form-item>
|
||||
<el-form-item style="width: 480px;" label="维修创建时间" prop="createTimeList">
|
||||
<el-date-picker v-model="state.queryForm.createTimeList" value-format="YYYY-MM-DD HH:mm:ss"
|
||||
type="datetimerange" />
|
||||
</el-form-item>
|
||||
<el-form-item style="width: 480px;" label="维修完成时间" prop="completedDateList">
|
||||
<el-date-picker v-model="state.queryForm.completedDateList" value-format="YYYY-MM-DD HH:mm:ss"
|
||||
type="datetimerange" />
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button icon="search" type="primary" @click="getDataList">
|
||||
{{ $t('common.queryBtn') }}
|
||||
</el-button>
|
||||
<el-button icon="Refresh" @click="resetQuery">
|
||||
{{ $t('common.resetBtn') }}
|
||||
</el-button>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<el-button icon="folder-add" type="primary" class="ml10" @click="formDialogRef.openDialog()"
|
||||
v-auth="'admin_serviceManage_add'">
|
||||
新 增
|
||||
</el-button>
|
||||
<el-button plain :disabled="multiple" icon="Delete" type="primary" v-auth="'admin_serviceManage_del'"
|
||||
@click="handleDelete(selectObjs)">
|
||||
删除
|
||||
</el-button>
|
||||
<!-- :export="'admin_serviceManage_export'" -->
|
||||
<right-toolbar v-model:showSearch="showSearch" @exportExcel="exportExcel" class="ml10 mr20"
|
||||
style="float: right;" @queryTable="getDataList"></right-toolbar>
|
||||
</div>
|
||||
</el-row>
|
||||
<el-table :data="state.dataList" v-loading="state.loading" border :cell-style="tableStyle.cellStyle"
|
||||
: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="title" label="标题" show-overflow-tooltip />
|
||||
<el-table-column prop="status" label="状态" width="100">
|
||||
<template #default="{ row }">
|
||||
<dict-tag :options="todo_status" :value="row.status" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="priority" label="优先级" width="100">
|
||||
<template #default="{ row }">
|
||||
<dict-tag :options="todo_priority" :value="row.priority" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="assigneeName" label="处理人" show-overflow-tooltip />
|
||||
<el-table-column prop="startDate" label="预计完成时间" width="320">
|
||||
<template #default="{ row }">
|
||||
<div v-if="row.startDate && row.dueDate">
|
||||
<span>{{ row.startDate }}</span>
|
||||
<span> 至 </span>
|
||||
<span>{{ row.dueDate }}</span>
|
||||
</div>
|
||||
<div class="color-danger" v-else>未设置</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="createTime" label="创建时间" width="180" />
|
||||
<el-table-column prop="completedDate" label="完成时间" width="180">
|
||||
<template #default="{ row }">
|
||||
<div v-if="row.completedDate">{{ row.completedDate }}</div>
|
||||
<div class="color-danger" v-else>未完成</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column fixed="right" label="操作" width="220">
|
||||
<template #default="{ row }">
|
||||
<el-button v-if="['1'].includes(row.status)" icon="VideoPlay" text type="primary"
|
||||
v-auth="'admin_serviceManage_start'" @click="doStart(row)">
|
||||
开始
|
||||
</el-button>
|
||||
<el-button v-if="['2', '4'].includes(row.status)" icon="CircleCheckFilled" text type="primary"
|
||||
v-auth="'admin_serviceManage_done'" @click="doDone(row)">
|
||||
完成
|
||||
</el-button>
|
||||
<el-button v-if="['3', '4'].includes(row.status)" icon="edit-pen" text type="primary"
|
||||
v-auth="'admin_serviceManage_edit'" @click="formDialogRef.openDialog(row.id)">
|
||||
编辑
|
||||
</el-button>
|
||||
<el-button v-if="!['3', '4'].includes(row.status)" icon="InfoFilled" text type="primary"
|
||||
v-auth="'admin_todoRecord_edit'" @click="detailDialogRef.openDialog(row.id)">
|
||||
详情
|
||||
</el-button>
|
||||
<el-button icon="delete" text type="primary" v-auth="'admin_serviceManage_del'"
|
||||
@click="handleDelete([row.id])">
|
||||
删除
|
||||
</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<pagination @size-change="sizeChangeHandle" @current-change="currentChangeHandle" v-bind="state.pagination" />
|
||||
</div>
|
||||
|
||||
<!-- 编辑、新增 -->
|
||||
<form-dialog ref="formDialogRef" @refresh="getDataList(false)" />
|
||||
<!-- 详情 -->
|
||||
<detail-dialog ref="detailDialogRef" />
|
||||
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts" name="systemServiceManage">
|
||||
import { BasicTableProps, useTable } from "/@/hooks/table";
|
||||
import * as serviceManageApi from '/@/api/todo/serviceManage'
|
||||
import { useMessage, useMessageBox } from "/@/hooks/message";
|
||||
import { useDict } from '/@/hooks/dict';
|
||||
|
||||
// 引入组件
|
||||
const FormDialog = defineAsyncComponent(() => import('./form.vue'));
|
||||
const DetailDialog = defineAsyncComponent(() => import('./detail.vue'));
|
||||
// 定义查询字典
|
||||
const { todo_priority, todo_status } = useDict("todo_priority", "todo_status")
|
||||
// 定义变量内容
|
||||
const formDialogRef = ref()
|
||||
const detailDialogRef = ref()
|
||||
// 搜索变量
|
||||
const queryRef = ref()
|
||||
const showSearch = ref(true)
|
||||
// 多选变量
|
||||
const selectObjs = ref([]) as any
|
||||
const multiple = ref(true)
|
||||
|
||||
const state: BasicTableProps = reactive<BasicTableProps>({
|
||||
queryForm: {},
|
||||
pageList: serviceManageApi.fetchList
|
||||
})
|
||||
|
||||
// table hook
|
||||
const {
|
||||
getDataList,
|
||||
currentChangeHandle,
|
||||
sizeChangeHandle,
|
||||
sortChangeHandle,
|
||||
downBlobFile,
|
||||
tableStyle
|
||||
} = useTable(state)
|
||||
|
||||
// 清空搜索条件
|
||||
const resetQuery = () => {
|
||||
// 清空搜索条件
|
||||
queryRef.value?.resetFields()
|
||||
// 清空多选
|
||||
selectObjs.value = []
|
||||
getDataList()
|
||||
}
|
||||
|
||||
// 导出excel
|
||||
const exportExcel = () => {
|
||||
downBlobFile('/admin/serviceManage/export', Object.assign(state.queryForm, { ids: selectObjs }), 'serviceManage.xlsx')
|
||||
}
|
||||
|
||||
// 多选事件
|
||||
const selectionChangHandle = (objs: { id: string }[]) => {
|
||||
selectObjs.value = objs.map(({ id }) => id);
|
||||
multiple.value = !objs.length;
|
||||
};
|
||||
|
||||
// 删除操作
|
||||
const handleDelete = async (ids: string[]) => {
|
||||
try {
|
||||
await useMessageBox().confirm('此操作将永久删除');
|
||||
} catch {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
await serviceManageApi.delObjs(ids);
|
||||
getDataList();
|
||||
useMessage().success('删除成功');
|
||||
} catch (err: any) {
|
||||
useMessage().error(err.msg);
|
||||
}
|
||||
};
|
||||
|
||||
const doStart = async (row: any) => {
|
||||
try {
|
||||
await useMessageBox().confirm(`是否开始${row.title}?`);
|
||||
} catch {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
await serviceManageApi.start({ id: row.id })
|
||||
getDataList();
|
||||
useMessage().success('操作成功');
|
||||
} catch (err: any) {
|
||||
useMessage().error(err.msg);
|
||||
}
|
||||
}
|
||||
|
||||
const doDone = async (row: any) => {
|
||||
try {
|
||||
await useMessageBox().confirm(`是否完成${row.title}?`);
|
||||
} catch {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
await serviceManageApi.done({ id: row.id })
|
||||
getDataList();
|
||||
useMessage().success('操作成功');
|
||||
} catch (err: any) {
|
||||
useMessage().error(err.msg);
|
||||
}
|
||||
}
|
||||
</script>
|
||||
@@ -1,6 +1,6 @@
|
||||
<template>
|
||||
<el-dialog :title="form.id ? '编辑' : '新增'" v-model="visible" :close-on-click-modal="false" draggable>
|
||||
<el-form ref="dataFormRef" :model="form" :rules="dataRules" formDialogRef label-width="90px" v-loading="loading">
|
||||
<el-form ref="dataFormRef" :model="form" :rules="dataRules" formDialogRef label-width="120px" v-loading="loading">
|
||||
<el-row :gutter="24">
|
||||
<el-col :span="24" class="mb20">
|
||||
<el-form-item label="标题" prop="title">
|
||||
@@ -9,7 +9,7 @@
|
||||
</el-col>
|
||||
<el-col :span="24" class="mb20">
|
||||
<el-form-item label="描述" prop="description">
|
||||
<editor v-model:get-html="form.description" placeholder="请输入描述" />
|
||||
<el-input type="textarea" v-model="form.description" :autosize="{ minRows: 6 }" placeholder="请输入描述" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12" class="mb20">
|
||||
@@ -23,7 +23,7 @@
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="24" class="mb20">
|
||||
<el-form-item label="完成时间" prop="date">
|
||||
<el-form-item label="预计完成时间" prop="date">
|
||||
<el-date-picker v-model="form.date" value-format="YYYY-MM-DD HH:mm:ss" type="datetimerange" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
|
||||
@@ -38,8 +38,9 @@
|
||||
@click="handleDelete(selectObjs)">
|
||||
删除
|
||||
</el-button>
|
||||
<right-toolbar v-model:showSearch="showSearch" :export="'admin_todoRecord_export'" @exportExcel="exportExcel"
|
||||
class="ml10 mr20" style="float: right;" @queryTable="getDataList"></right-toolbar>
|
||||
<!-- :export="'admin_todoRecord_export'" -->
|
||||
<right-toolbar v-model:showSearch="showSearch" @exportExcel="exportExcel" class="ml10 mr20"
|
||||
style="float: right;" @queryTable="getDataList"></right-toolbar>
|
||||
</div>
|
||||
</el-row>
|
||||
<el-table :data="state.dataList" v-loading="state.loading" border :cell-style="tableStyle.cellStyle"
|
||||
@@ -76,7 +77,7 @@
|
||||
<div class="color-danger" v-else>未完成</div>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作" width="280">
|
||||
<el-table-column fixed="right" label="操作" width="280">
|
||||
<template #default="{ row }">
|
||||
<el-button v-if="['1'].includes(row.status)" icon="VideoPlay" text type="primary"
|
||||
v-auth="'admin_todoRecord_start'" @click="doStart(row)">
|
||||
@@ -116,6 +117,7 @@ import { BasicTableProps, useTable } from "/@/hooks/table";
|
||||
import * as todoRecordApi from "/@/api/todo/todoRecord"
|
||||
import { useMessage, useMessageBox } from "/@/hooks/message";
|
||||
import { useDict } from '/@/hooks/dict';
|
||||
import { usePanel } from "/@/stores/panel"
|
||||
|
||||
// 引入组件
|
||||
const FormDialog = defineAsyncComponent(() => import('./form.vue'));
|
||||
@@ -132,6 +134,8 @@ const showSearch = ref(true)
|
||||
const selectObjs = ref([]) as any
|
||||
const multiple = ref(true)
|
||||
|
||||
const storesPanel = usePanel()
|
||||
|
||||
const state: BasicTableProps = reactive<BasicTableProps>({
|
||||
queryForm: {},
|
||||
pageList: todoRecordApi.fetchList
|
||||
@@ -213,4 +217,22 @@ const doDone = async (row: any) => {
|
||||
useMessage().error(err.msg);
|
||||
}
|
||||
}
|
||||
|
||||
onBeforeMount(() => {
|
||||
const query: any = useRoute().query
|
||||
if (query && query.action == 'panel') {
|
||||
if (query.params) {
|
||||
Object.assign(state.queryForm, JSON.parse(query.params))
|
||||
getDataList()
|
||||
}
|
||||
storesPanel.openFromPanel()
|
||||
}
|
||||
})
|
||||
|
||||
onBeforeUnmount(() => {
|
||||
const query = useRoute().query
|
||||
if (query && query.action == 'panel') {
|
||||
storesPanel.closeFromPanel()
|
||||
}
|
||||
})
|
||||
</script>
|
||||
Reference in new issue
Block a user