@@ -1,11 +1,3 @@ | |||
<!-- | |||
* @Author: whyafterme | |||
* @Date: 2023-01-12 10:22:37 | |||
* @LastEditTime: 2023-01-14 15:20:35 | |||
* @LastEditors: whyafterme | |||
* @Description: | |||
* @FilePath: \forest\index.html | |||
--> | |||
<!DOCTYPE html> | |||
<html lang="en"> | |||
<head> |
@@ -0,0 +1,117 @@ | |||
import { defAxios as request } from '@/utils/http' | |||
/** | |||
* @description: 查询仓库列表 | |||
* @param {Object} params | |||
* @return {Object} | |||
*/ | |||
export function materialList(params) { | |||
return request({ | |||
url: '/warehouse/list', | |||
method: 'get', | |||
params | |||
}) | |||
} | |||
/** | |||
* @description: 分页查询仓库列表 | |||
* @param {Object} params | |||
* @return {Object} | |||
*/ | |||
export function materialPage(params) { | |||
return request({ | |||
url: '/warehouse/page/list', | |||
method: 'get', | |||
params | |||
}) | |||
} | |||
/** | |||
* @description: 添加仓库 | |||
* @param {String} params | |||
* @return {*} | |||
*/ | |||
export function createMaterial(data) { | |||
return request({ | |||
url: '/warehouse/add', | |||
method: 'post', | |||
data | |||
}) | |||
} | |||
/** | |||
* @description: 编辑仓库 | |||
* @param {String} params | |||
* @return {*} | |||
*/ | |||
export function updateMaterial(data) { | |||
return request({ | |||
url: '/warehouse/edit', | |||
method: 'put', | |||
data | |||
}) | |||
} | |||
/** | |||
* @description: 删除仓库 | |||
* @param {String} params | |||
* @return {*} | |||
*/ | |||
export function deleteMaterial(ids) { | |||
return request({ | |||
url: `/warehouse/delete/${ids}`, | |||
method: 'delete' | |||
}) | |||
} | |||
/** | |||
* @description: 根据仓库查询物资列表 | |||
* @param {String} params | |||
* @return {*} | |||
*/ | |||
export function goodsList(params) { | |||
return request({ | |||
url: `/goods/list/by/warehouseid`, | |||
method: 'get', | |||
params | |||
}) | |||
} | |||
/** | |||
* @description: 添加物资 | |||
* @param {String} params | |||
* @return {*} | |||
*/ | |||
export function createGoods(data) { | |||
return request({ | |||
url: '/goods/add', | |||
method: 'post', | |||
data | |||
}) | |||
} | |||
/** | |||
* @description: 编辑物资 | |||
* @param {String} params | |||
* @return {*} | |||
*/ | |||
export function updateGoods(data) { | |||
return request({ | |||
url: '/goods/edit', | |||
method: 'put', | |||
data | |||
}) | |||
} | |||
/** | |||
* @description: 删除物资 | |||
* @param {String} params | |||
* @return {*} | |||
*/ | |||
export function deleteGoods(ids) { | |||
return request({ | |||
url: `/goods/delete/${ids}`, | |||
method: 'delete' | |||
}) | |||
} | |||
@@ -0,0 +1,66 @@ | |||
import { defAxios as request } from '@/utils/http' | |||
/** | |||
* @description: 查询摄像头列表 | |||
* @param {Object} params | |||
* @return {Object} | |||
*/ | |||
export function cameraList(params) { | |||
return request({ | |||
url: '/camera/list', | |||
method: 'get', | |||
params | |||
}) | |||
} | |||
/** | |||
* @description: 分页查询摄像头列表 | |||
* @param {Object} params | |||
* @return {Object} | |||
*/ | |||
export function cameraPage(params) { | |||
return request({ | |||
url: '/camera/page', | |||
method: 'get', | |||
params | |||
}) | |||
} | |||
/** | |||
* @description: 添加摄像头 | |||
* @param {String} params | |||
* @return {*} | |||
*/ | |||
export function createCamera(data) { | |||
return request({ | |||
url: '/camera/add', | |||
method: 'post', | |||
data | |||
}) | |||
} | |||
/** | |||
* @description: 编辑摄像头 | |||
* @param {String} params | |||
* @return {*} | |||
*/ | |||
export function updateCamera(data) { | |||
return request({ | |||
url: '/camera/edit', | |||
method: 'put', | |||
data | |||
}) | |||
} | |||
/** | |||
* @description: 删除摄像头 | |||
* @param {String} params | |||
* @return {*} | |||
*/ | |||
export function deleteCamera(ids) { | |||
return request({ | |||
url: `/camera/delete/${ids}`, | |||
method: 'delete' | |||
}) | |||
} | |||
@@ -0,0 +1,77 @@ | |||
import { defAxios as request } from '@/utils/http' | |||
/** | |||
* @description: 查询仓库列表 | |||
* @param {Object} params | |||
* @return {Object} | |||
*/ | |||
export function materialList(params) { | |||
return request({ | |||
url: '/warehouse/list', | |||
method: 'get', | |||
params | |||
}) | |||
} | |||
/** | |||
* @description: 分页查询预警列表 | |||
* @param {Object} params | |||
* @return {Object} | |||
*/ | |||
export function firePage(params) { | |||
return request({ | |||
url: '/warning/index', | |||
method: 'get', | |||
params | |||
}) | |||
} | |||
/** | |||
* @description: 查询预警问题详情 | |||
* @param {Number} id | |||
* @return {Object} | |||
*/ | |||
export function fireDetail(id) { | |||
return request({ | |||
url: `/warning/details/${id}`, | |||
method: 'get' | |||
}) | |||
} | |||
/** | |||
* @description: 预警心跳 | |||
* @param {String} params | |||
* @return {*} | |||
*/ | |||
export function fireHeart() { | |||
return request({ | |||
url: '/warning/notice', | |||
method: 'get' | |||
}) | |||
} | |||
/** | |||
* @description: 预警通知与处理 | |||
* @param {String} params | |||
* @return {*} | |||
*/ | |||
export function updateWarning(id, status) { | |||
return request({ | |||
url: `/warning/status/${id}/${status}`, | |||
method: 'put', | |||
hideMessage: true | |||
}) | |||
} | |||
/** | |||
* @description: 删除仓库 | |||
* @param {String} params | |||
* @return {*} | |||
*/ | |||
export function deleteMaterial(ids) { | |||
return request({ | |||
url: `/warehouse/delete/${ids}`, | |||
method: 'delete' | |||
}) | |||
} | |||
@@ -27,7 +27,6 @@ export default defineComponent({ | |||
return value | |||
}) | |||
const getSwitchProps = computed(() => { | |||
console.log(111) | |||
return { | |||
...unref(props) | |||
} |
@@ -38,7 +38,7 @@ class Dialog { | |||
negativeText: '取消', | |||
onPositiveClick: option.confirm, | |||
onNegativeClick: option.cancel, | |||
onMaskClick: option.cancel, | |||
onMaskClick: option.mask ?? option.cancel, | |||
...option | |||
}) | |||
} |
@@ -3,6 +3,7 @@ import { usePermissionStore } from '@/store/modules/permission.js' | |||
import { NOT_FOUND_ROUTE, REDIRECT_ROUTE } from '@/router/routes' | |||
import { getQuestion } from '@/utils/question.js' | |||
import { getQuestionList } from '@/utils/dictionary.js' | |||
import { getTimer, timerHeart } from '@/utils/global.js' | |||
import { getToken } from '@/utils/token' | |||
const WHITE_LIST = ['/login', '/redirect'] | |||
@@ -18,8 +19,10 @@ export function createPermissionGuard(router) { | |||
const hasUsers = !!Object.keys(userStore.userInfoMsg).length | |||
const hasRoutes = !!permissionStore.permissionRoutes.length | |||
const hasQuestions = getQuestion() | |||
const hasTimerHearts = getTimer() | |||
if (!hasUsers) { await userStore.getUserInfo() } | |||
if (!hasQuestions) { await getQuestionList() } | |||
if (!hasTimerHearts) { await timerHeart() } | |||
if (hasRoutes) { | |||
next() | |||
} else { |
@@ -78,6 +78,30 @@ export const MENU_STATUS = [ | |||
{ label: '停用', value: 2 } | |||
] | |||
export const MONITOR_OPTIONS = [ | |||
{ label: '枪机', value: 1 }, | |||
{ label: '球机', value: 2 } | |||
] | |||
export const MATERIAL_TYPE = [ | |||
{ label: '个人防护装备', value: 1 }, | |||
{ label: '搜救装备', value: 2 }, | |||
{ label: '医疗及防疫设备及常用应急药品', value: 3 }, | |||
{ label: '应急照明设备', value: 4 }, | |||
{ label: '灭火处置设备', value: 5 } | |||
] | |||
export const EARLY_SOURCE = [ | |||
{ label: '无人机巡检', value: 1 }, | |||
{ label: '监控识别', value: 2 }, | |||
{ label: '人工上报', value: 3 } | |||
] | |||
export const FIRE_STATUS = [ | |||
{ label: '待确认', value: 1 }, | |||
{ label: '确认', value: 2 }, | |||
{ label: '忽略', value: 3 } | |||
] | |||
export const getQuestionList = async function() { | |||
const res = await getQuestionType() | |||
if (res.code === 0) { |
@@ -0,0 +1,69 @@ | |||
import { getToken } from './token.js' | |||
import { fireHeart, updateWarning } from '@/api/early/fire.js' | |||
import { router } from '@/router/index.js' | |||
import { ref } from 'vue' | |||
const hasModal = ref(false) | |||
const hasTimer = ref(false) | |||
export const getTimer = () => { | |||
return hasTimer.value | |||
} | |||
export const timerHeart = async function() { | |||
if (getToken()) { | |||
hasTimer.value = true | |||
const res = await fireHeart() | |||
if (res.code === 0) { | |||
const list = res.data ?? [] | |||
const ids = list.map(item => item.id) | |||
const dealList = [] | |||
if (res.data && !hasModal.value) { | |||
hasModal.value = true | |||
$dialog.confirm( | |||
{ | |||
type: 'success', | |||
title: '火情通知', | |||
showIcon: false, | |||
maskClosable: false, | |||
positiveText: '处理', | |||
negativeText: '忽略', | |||
content: `发现疑似火灾,请及时处理`, | |||
confirm: () => { | |||
router.push({ path: '/' }) | |||
hasModal.value = false | |||
ids.forEach(item => { | |||
dealList.push(dealWarnings(item, 2)) | |||
}) | |||
Promise.all(dealList) | |||
}, | |||
cancel: () => { | |||
hasModal.value = false | |||
ids.forEach(item => { | |||
dealList.push(dealWarnings(item, 3)) | |||
}) | |||
Promise.all(dealList) | |||
}, | |||
mask: () => { | |||
} | |||
} | |||
) | |||
} | |||
setTimeout(() => { | |||
timerHeart() | |||
}, 5000) | |||
} | |||
} else { | |||
hasModal.value = false | |||
hasTimer.value = false | |||
} | |||
} | |||
const dealWarnings = function(id, status) { | |||
return new Promise((resolve, reject) => { | |||
updateWarning(id, status) | |||
.then(res => { | |||
resolve() | |||
}) | |||
}) | |||
} |
@@ -35,7 +35,7 @@ export function setupInterceptor(service) { | |||
service.interceptors.response.use( | |||
(response) => { | |||
const { method } = response?.config | |||
const { method, hideMessage = false } = response?.config | |||
const { code } = response?.data | |||
const { currentRoute } = router | |||
switch (code) { | |||
@@ -43,7 +43,7 @@ export function setupInterceptor(service) { | |||
$message.error(response.data.msg) | |||
break | |||
case 0: | |||
if (method !== 'get') { | |||
if (method !== 'get' && !hideMessage) { | |||
$message.success(response.data.msg) | |||
} | |||
break |
@@ -0,0 +1,61 @@ | |||
<template> | |||
<div class="custom-form"> | |||
<n-grid x-gap="12" :cols="2"> | |||
<n-gi> | |||
<n-form-item class="hidden__feedback"> | |||
<n-input v-model:value="longitude" placeholder="请输入经度" clearable @blur="handleBlur" /> | |||
</n-form-item> | |||
</n-gi> | |||
<n-gi> | |||
<n-form-item class="hidden__feedback"> | |||
<n-input v-model:value="latitude" placeholder="请输入纬度" clearable @blur="handleBlur" /> | |||
</n-form-item> | |||
</n-gi> | |||
</n-grid> | |||
</div> | |||
</template> | |||
<script> | |||
import { reactive, watch, toRefs } from 'vue' | |||
export default { | |||
name: 'CustomForm', | |||
props: { | |||
value: { | |||
type: Object, | |||
default: () => {} | |||
} | |||
}, | |||
emits: ['done'], | |||
setup(props, { emit }) { | |||
const data = reactive({ | |||
longitude: '', | |||
latitude: '', | |||
...props.value | |||
}) | |||
// watch(data, (val) => { | |||
// emit('done', val) | |||
// }) | |||
const handleBlur = () => { | |||
emit('done', data) | |||
} | |||
return { | |||
...toRefs(data), | |||
handleBlur | |||
} | |||
} | |||
} | |||
</script> | |||
<style scoped lang='scss'> | |||
.custom-form{ | |||
width: 100%; | |||
} | |||
::v-deep(.hidden__feedback){ | |||
.n-form-item-feedback-wrapper{ | |||
display: none; | |||
} | |||
} | |||
</style> |
@@ -0,0 +1,145 @@ | |||
<template> | |||
<n-drawer v-bind="getDrawerOptions" @update:show="handleDrawerColse"> | |||
<n-drawer-content closable title="物资管理"> | |||
<HeadSearch :info="search" @search="handleSearch" @reset="handleSearch" /> | |||
<DataTable | |||
ref="tableRef" | |||
:columns="columns" | |||
:row-key="(row) => row.id" | |||
:request="loadDataTable" | |||
size="large" | |||
> | |||
<template #tableTitle> | |||
<n-button type="primary" @click="handleModal"> 新建 </n-button> | |||
</template> | |||
</DataTable> | |||
<!-- 新增、编辑弹窗 --> | |||
<MaterialModal v-if="modalShow" v-model:visible="modalShow" :type="modalType" :warehouse-id="data.id" :data="rowData" @reload="handleSearch" /> | |||
</n-drawer-content> | |||
</n-drawer> | |||
</template> | |||
<script> | |||
import table from '../tools/materialTable.js' | |||
import { search } from '../tools/materialSearch.js' | |||
import HeadSearch from '@/components/Search/index.vue' | |||
import DataTable from '@/components/DataTable/index.vue' | |||
import MaterialModal from './MaterialModal.vue' | |||
import { defineComponent, reactive, unref, toRefs, computed, watch } from 'vue' | |||
import { goodsList } from '@/api/basic/material.js' | |||
export default defineComponent({ | |||
name: 'LiveDrawer', | |||
components: { HeadSearch, MaterialModal, DataTable }, | |||
props: { | |||
/* 可见 */ | |||
visible: { | |||
type: Boolean, | |||
default: false | |||
}, | |||
/* 选中的数据 */ | |||
data: { | |||
type: Object, | |||
default: () => {} | |||
} | |||
}, | |||
emits: { | |||
'update:visible': null | |||
}, | |||
setup(props, { emit }) { | |||
const data = reactive({ | |||
...toRefs(search), | |||
...toRefs(table), | |||
warehouseId: null | |||
}) | |||
/* 获取抽屉的信息 */ | |||
const getDrawerOptions = computed(() => { | |||
return { | |||
show: props.visible, | |||
width: '100%', | |||
placement: 'right' | |||
} | |||
}) | |||
/* 关闭抽屉 */ | |||
function handleDrawerColse() { | |||
emit('update:visible', false) | |||
} | |||
const loadDataTable = async(res) => { | |||
const _params = { | |||
...unref(data.searchParams), | |||
warehouseId: props.data.id, | |||
...res | |||
} | |||
return await goodsList(_params) | |||
} | |||
// 新增 | |||
function handleModal() { | |||
data.rowData = null | |||
data.modalType = 'create' | |||
data.modalShow = true | |||
} | |||
return { | |||
...toRefs(data), | |||
getDrawerOptions, | |||
handleDrawerColse, | |||
loadDataTable, | |||
handleModal | |||
} | |||
} | |||
}) | |||
</script> | |||
<style scoped lang='scss'> | |||
.report__container{ | |||
width: 900px; | |||
margin: 0 auto 40px auto; | |||
.report__item{ | |||
margin-bottom: 40px; | |||
.report__item--title{ | |||
font-size: 18px; | |||
color: #333333; | |||
line-height: 44px; | |||
} | |||
.n-grid{ | |||
border-top: 1px solid rgba(216, 216, 216, 1); | |||
border-left: 1px solid rgba(216, 216, 216, 1); | |||
>div{ | |||
position: relative; | |||
text-align: center; | |||
font-size: 14px; | |||
color: #333333; | |||
line-height: 20px; | |||
padding: 6px 12px; | |||
word-wrap: break-word; | |||
word-break: normal; | |||
border-right: 1px solid rgba(216, 216, 216, 1); | |||
border-bottom: 1px solid rgba(216, 216, 216, 1); | |||
&:nth-child(2n-1){ | |||
span{ | |||
position: absolute; | |||
left: 50%; | |||
top: 50%; | |||
transform: translate(-50%,-50%); | |||
} | |||
background: rgba(228, 231, 237, 1); | |||
} | |||
} | |||
} | |||
} | |||
.report__operate{ | |||
text-align: center; | |||
} | |||
} | |||
::v-deep(.n-image){ | |||
width: 100%; | |||
img{ | |||
width: 100%; | |||
} | |||
} | |||
</style> |
@@ -0,0 +1,140 @@ | |||
<template> | |||
<Modal | |||
:options="getModalOptions" | |||
:on-positive-click="handleConfirm" | |||
:on-negative-click="handleClose" | |||
:on-close="handleClose" | |||
> | |||
<template #Context> | |||
<n-form | |||
ref="formRef" | |||
:model="materialForm" | |||
:rules="materialRules" | |||
:label-width="100" | |||
label-placement="left" | |||
require-mark-placement="left" | |||
:disabled="disabled" | |||
> | |||
<template v-for="(item,index) in getFormOptions" :key="index"> | |||
<n-form-item :label="item.label" :path="item.key"> | |||
<n-input v-if="item.type === 'input'" v-model:value="materialForm[item.key]" v-bind="item.props" /> | |||
<n-select v-if="item.type === 'select'" v-model:value="materialForm[item.key]" v-bind="item.props" /> | |||
</n-form-item> | |||
</template> | |||
</n-form> | |||
</template> | |||
</Modal> | |||
</template> | |||
<script> | |||
import { form } from '../tools/materialForm.js' | |||
import { defineComponent, ref, reactive, computed, toRefs } from 'vue' | |||
import Modal from '@/components/Modal/index.vue' | |||
import { createGoods, updateGoods } from '@/api/basic/material.js' | |||
export default defineComponent({ | |||
name: 'UserModal', | |||
components: { Modal }, | |||
props: { | |||
visible: { | |||
type: Boolean, | |||
default: false | |||
}, | |||
type: { | |||
type: String, | |||
default: 'create' | |||
}, | |||
warehouseId: { | |||
type: Number, | |||
default: null | |||
}, | |||
data: { | |||
type: Object, | |||
default: () => null | |||
} | |||
}, | |||
emits: { | |||
'update:visible': null, | |||
'reload': null | |||
}, | |||
setup(props, { emit }) { | |||
const MODAL_TYPE = { | |||
'create': '新增物资', | |||
'preview': '物资详情', | |||
'update': '编辑物资' | |||
} | |||
const { materialForm, materialRules } = form | |||
const formRef = ref() | |||
const data = reactive({ | |||
materialForm: { | |||
...materialForm, | |||
...props.data, | |||
warehouseId: props.warehouseId | |||
}, | |||
materialRules: { | |||
...materialRules | |||
}, | |||
disabled: props.type === 'preview' | |||
}) | |||
const getModalOptions = computed(() => { | |||
return { | |||
show: props.visible, | |||
title: MODAL_TYPE[props.type], | |||
negativeText: '取消', | |||
positiveText: '确认' | |||
} | |||
}) | |||
const getFormOptions = computed(() => { | |||
return { | |||
...form.formItem | |||
} | |||
}) | |||
function handleConfirm() { | |||
formRef.value?.validate((errors) => { | |||
if (!errors) { | |||
const params = { | |||
...data.materialForm | |||
} | |||
if (params.id) { | |||
updateGoods(params) | |||
.then(res => { | |||
if (res.code === 0) { | |||
emit('reload') | |||
handleClose() | |||
} | |||
}) | |||
} else { | |||
/* 新增 */ | |||
createGoods(params) | |||
.then(res => { | |||
if (res.code === 0) { | |||
emit('reload') | |||
handleClose() | |||
} | |||
}) | |||
} | |||
} else { | |||
$message.error('请完善必填信息') | |||
} | |||
}) | |||
} | |||
/* 关闭弹窗 */ | |||
const handleClose = () => { | |||
emit('update:visible', false) | |||
} | |||
return { | |||
...toRefs(data), | |||
formRef, | |||
getModalOptions, | |||
getFormOptions, | |||
handleConfirm, | |||
handleClose | |||
} | |||
} | |||
}) | |||
</script> | |||
<style scoped lang='scss'> | |||
</style> |
@@ -0,0 +1,157 @@ | |||
<template> | |||
<Modal | |||
:options="getModalOptions" | |||
:on-positive-click="handleConfirm" | |||
:on-negative-click="handleClose" | |||
:on-close="handleClose" | |||
> | |||
<template #Context> | |||
<n-form | |||
ref="formRef" | |||
:model="materialForm" | |||
:rules="materialRules" | |||
:label-width="100" | |||
label-placement="left" | |||
require-mark-placement="left" | |||
:disabled="disabled" | |||
> | |||
<template v-for="(item,index) in getFormOptions" :key="index"> | |||
<n-form-item :label="item.label" :path="item.key"> | |||
<n-input v-if="item.type === 'input'" v-model:value="materialForm[item.key]" v-bind="item.props" /> | |||
<CustomForm v-if="item.type === 'customPos'" v-model:value="materialForm" @done="validatePartial" /> | |||
</n-form-item> | |||
</template> | |||
</n-form> | |||
</template> | |||
</Modal> | |||
</template> | |||
<script> | |||
import { form, changeMessage } from '../tools/form.js' | |||
import { defineComponent, ref, reactive, computed, toRefs } from 'vue' | |||
import Modal from '@/components/Modal/index.vue' | |||
import CustomForm from './CustomForm.vue' | |||
import { createMaterial, updateMaterial } from '@/api/basic/material.js' | |||
export default defineComponent({ | |||
name: 'UserModal', | |||
components: { Modal, CustomForm }, | |||
props: { | |||
visible: { | |||
type: Boolean, | |||
default: false | |||
}, | |||
type: { | |||
type: String, | |||
default: 'create' | |||
}, | |||
data: { | |||
type: Object, | |||
default: () => null | |||
} | |||
}, | |||
emits: { | |||
'update:visible': null, | |||
'reload': null | |||
}, | |||
setup(props, { emit }) { | |||
const MODAL_TYPE = { | |||
'create': '新建仓库', | |||
'preview': '仓库详情', | |||
'update': '编辑仓库' | |||
} | |||
const { materialForm, materialRules } = form | |||
const formRef = ref() | |||
const data = reactive({ | |||
materialForm: { | |||
...materialForm, | |||
...props.data, | |||
customPos: props?.data?.id ? 'pass' : null | |||
}, | |||
materialRules: { | |||
...materialRules | |||
}, | |||
disabled: props.type === 'preview' | |||
}) | |||
const getModalOptions = computed(() => { | |||
return { | |||
show: props.visible, | |||
title: MODAL_TYPE[props.type], | |||
negativeText: '取消', | |||
positiveText: '确认' | |||
} | |||
}) | |||
const getFormOptions = computed(() => { | |||
return { | |||
...form.formItem | |||
} | |||
}) | |||
const validatePartial = (form) => { | |||
data.materialForm.customPos = null | |||
if (form.longitude && form.latitude) { | |||
data.materialForm.customPos = 'pass' | |||
const { latitude, longitude } = form | |||
data.materialForm.longitude = Number(longitude) | |||
data.materialForm.latitude = Number(latitude) | |||
} | |||
formRef.value?.validate( | |||
(errors) => { | |||
if (errors) { | |||
console.error(1111) | |||
} | |||
}, | |||
(rule) => { | |||
return rule?.key === 'custom' | |||
} | |||
) | |||
} | |||
function handleConfirm() { | |||
formRef.value?.validate((errors) => { | |||
if (!errors) { | |||
const params = { | |||
...data.materialForm | |||
} | |||
if (params.id) { | |||
updateMaterial(params) | |||
.then(res => { | |||
if (res.code === 0) { | |||
emit('reload') | |||
handleClose() | |||
} | |||
}) | |||
} else { | |||
/* 新增 */ | |||
createMaterial(params) | |||
.then(res => { | |||
if (res.code === 0) { | |||
emit('reload') | |||
handleClose() | |||
} | |||
}) | |||
} | |||
} else { | |||
$message.error('请完善必填信息') | |||
} | |||
}) | |||
} | |||
/* 关闭弹窗 */ | |||
const handleClose = () => { | |||
emit('update:visible', false) | |||
} | |||
return { | |||
...toRefs(data), | |||
formRef, | |||
getModalOptions, | |||
getFormOptions, | |||
validatePartial, | |||
handleConfirm, | |||
handleClose | |||
} | |||
} | |||
}) | |||
</script> | |||
<style scoped lang='scss'> | |||
</style> |
@@ -0,0 +1,100 @@ | |||
<template> | |||
<div> | |||
<n-card> | |||
<headSearch :info="search" @search="handleSearch" @reset="handleSearch" /> | |||
<data-table | |||
ref="tableRef" | |||
:columns="columns" | |||
:row-key="(row) => row.id" | |||
:request="loadDataTable" | |||
size="large" | |||
@update:checked-row-keys="handleCheck" | |||
> | |||
<template #tableTitle> | |||
<n-button type="primary" @click="handleModal"> 新建 </n-button> | |||
</template> | |||
</data-table> | |||
</n-card> | |||
</div> | |||
<!-- 新增、编辑弹窗 --> | |||
<StoreModal v-if="modalShow" v-model:visible="modalShow" :type="modalType" :data="rowData" @reload="handleSearch" /> | |||
<MaterialDrawer v-model:visible="drawerShow" :data="rowData" /> | |||
</template> | |||
<script> | |||
import { search } from './tools/search.js' | |||
import table from './tools/table.js' | |||
import headSearch from '@/components/Search/index.vue' | |||
import dataTable from '@/components/DataTable/index.vue' | |||
import StoreModal from './components/StoreModal.vue' | |||
import MaterialDrawer from './components/MaterialDrawer.vue' | |||
import { materialPage } from '@/api/basic/material.js' | |||
import { unref, ref, toRefs, reactive, onUnmounted } from 'vue' | |||
export default { | |||
name: 'MenuPage', | |||
components: { dataTable, StoreModal, MaterialDrawer, headSearch }, | |||
setup() { | |||
const data = reactive({ | |||
...toRefs(table), | |||
...toRefs(search) | |||
}) | |||
const loadDataTable = async(res) => { | |||
const _params = { | |||
...unref(data.searchParams), | |||
...res | |||
} | |||
return await materialPage(_params) | |||
} | |||
// 新增 | |||
function handleModal() { | |||
data.rowData = null | |||
data.modalType = 'create' | |||
data.modalShow = true | |||
} | |||
// 选择表格数据 | |||
const selectedIds = ref([]) | |||
function handleCheck(rowKeys) { | |||
selectedIds.value = rowKeys | |||
} | |||
// 批量删除 | |||
function deleteComplex() { | |||
if (selectedIds.value.length) { | |||
data.deleteData(selectedIds.value) | |||
} else { | |||
$message.warning('请至少选中一条数据') | |||
} | |||
} | |||
onUnmounted(() => { | |||
data.searchParams = null | |||
}) | |||
return { | |||
...toRefs(data), | |||
loadDataTable, | |||
handleModal, | |||
selectedIds, | |||
deleteComplex, | |||
handleCheck | |||
} | |||
}, | |||
methods: { | |||
} | |||
} | |||
</script> | |||
<style scoped lang='scss'> | |||
</style> | |||
<style scoped lang='scss'> | |||
.n-button + .n-button { | |||
margin-left: 10px; | |||
} | |||
</style> |
@@ -0,0 +1,29 @@ | |||
import { reactive } from 'vue' | |||
export const form = reactive({ | |||
materialForm: { | |||
warehouseName: null, | |||
customPos: null | |||
}, | |||
materialRules: { | |||
warehouseName: [{ required: true, message: '请输入仓库名称', trigger: 'blur' }], | |||
customPos: [{ key: 'custom', required: true, message: '请输入仓库位置', trigger: ['blur', 'change'] }] | |||
}, | |||
formItem: [ | |||
{ type: 'input', key: 'warehouseName', label: '仓库名称', props: { maxlength: '20', placeholder: '请输入仓库名称', clearable: true }}, | |||
{ type: 'customPos', key: 'customPos', label: '仓库位置' } | |||
] | |||
}) | |||
export const changeMessage = (type) => { | |||
const list = form.materialRules.customPos | |||
switch (type) { | |||
case 'lng': | |||
list[0].message = '请输入经度' | |||
break | |||
case 'lat': | |||
list[0].message = '请输入纬度' | |||
break | |||
default: | |||
list[0].message = '请输入经度' | |||
} | |||
} |
@@ -0,0 +1,22 @@ | |||
import { reactive } from 'vue' | |||
import { MATERIAL_TYPE } from '@/utils/dictionary.js' | |||
export const form = reactive({ | |||
materialForm: { | |||
goodsName: null, | |||
goodsType: null, | |||
goodsStock: null, | |||
goodsAction: null | |||
}, | |||
materialRules: { | |||
goodsName: [{ required: true, message: '请输入物资名称', trigger: 'blur' }], | |||
goodsType: [{ required: true, message: '请选择物资类型', type: 'number', trigger: 'blur' }], | |||
goodsStock: [{ required: true, message: '请输入物资库存', trigger: 'blur' }] | |||
}, | |||
formItem: [ | |||
{ type: 'input', key: 'goodsName', label: '物资名称', props: { maxlength: '20', placeholder: '请输入物资名称', clearable: true }}, | |||
{ type: 'select', key: 'goodsType', label: '物资类型', props: { options: MATERIAL_TYPE, placeholder: '请选择物资类型' }}, | |||
{ type: 'input', key: 'goodsStock', label: '物资库存', props: { maxlength: '20', placeholder: '请输入物资库存,如12箱', clearable: true }}, | |||
{ type: 'input', key: 'goodsAction', label: '物资作用', props: { type: 'textarea', autosize: { maxlength: '200', minRows: 3, maxRows: 3 }}} | |||
] | |||
}) | |||
@@ -0,0 +1,22 @@ | |||
import { reactive } from 'vue' | |||
import { MATERIAL_TYPE } from '@/utils/dictionary.js' | |||
export const search = reactive({ | |||
search: [ | |||
{ | |||
label: '物资名称', | |||
key: 'goodsName', | |||
props: { | |||
placeholder: '请输入物资名称' | |||
} | |||
}, | |||
{ | |||
label: '物资类型', | |||
type: 'select', | |||
key: 'goodsType', | |||
props: { | |||
placeholder: '请选择物资类型', | |||
options: MATERIAL_TYPE | |||
} | |||
} | |||
] | |||
}) |
@@ -0,0 +1,142 @@ | |||
import { h, ref, reactive } from 'vue' | |||
import TableImage from '@/components/DataTable/tools/Image.vue' | |||
import TableTags from '@/components/DataTable/tools/Tags.vue' | |||
import TableSwitch from '@/components/DataTable/tools/Switch.vue' | |||
import TableAction from '@/components/DataTable/tools/Action.vue' | |||
import { deleteGoods } from '@/api/basic/material.js' | |||
/* 注册table */ | |||
const tableRef = ref() | |||
const searchParams = ref() | |||
function handleSearch(params) { | |||
searchParams.value = { ...params } | |||
tableRef.value.reFetch({ searchParams }) | |||
} | |||
/** | |||
* @description: 获取数据及操作 | |||
* @param {*} row 单行数据 | |||
* @param {*} type 操作类型 create:创建,preview:预览,edit:编辑 | |||
* @return {*} | |||
*/ | |||
function getRowData(row, type) { | |||
data.rowData = row | |||
data.modalType = type | |||
data.modalShow = true | |||
} | |||
/** | |||
* @description: 删除物资接口 | |||
* @param {Array} ids 物资id集合 | |||
* @return {*} | |||
*/ | |||
function deleteData(ids) { | |||
deleteGoods(ids).then((res) => { | |||
if (res.code === 0) { | |||
handleSearch() | |||
} | |||
}).catch(e => { | |||
console.log(e) | |||
}) | |||
} | |||
const data = reactive({ | |||
tableRef, | |||
searchParams, | |||
rowData: {}, | |||
modalType: 'create', | |||
modalShow: false, | |||
handleSearch, | |||
deleteData, | |||
columns: [ | |||
{ | |||
title: '序号', | |||
key: 'key', | |||
render: (_, index) => { | |||
return `${index + 1}` | |||
}, | |||
align: 'center' | |||
}, | |||
{ | |||
title: '物资名称', | |||
key: 'goodsName', | |||
align: 'center' | |||
}, | |||
{ | |||
title: '物资类型', | |||
key: 'goodsType', | |||
align: 'center' | |||
}, | |||
{ | |||
title: '物资库存', | |||
key: 'goodsStock', | |||
align: 'center' | |||
}, | |||
{ | |||
title: '物资作用', | |||
key: 'goodsAction', | |||
align: 'center' | |||
}, | |||
{ | |||
title: '编辑人', | |||
key: 'updateUser', | |||
align: 'center' | |||
}, | |||
{ | |||
title: '编辑时间', | |||
key: 'updateTime', | |||
align: 'center', | |||
width: 160 | |||
}, | |||
// { | |||
// title: '角色', | |||
// key: 'roleList', | |||
// align: 'center', | |||
// render(row) { | |||
// return h(TableTags, { | |||
// data: row.roles, | |||
// rowKey: 'name' | |||
// }) | |||
// } | |||
// }, | |||
{ | |||
title: '操作', | |||
align: 'center', | |||
width: 150, | |||
fixed: 'right', | |||
render(row) { | |||
return h(TableAction, { | |||
actions: [ | |||
{ | |||
label: '修改', | |||
type: 'button', | |||
props: { | |||
type: 'primary', | |||
text: true, | |||
onClick: getRowData.bind(null, row, 'update') | |||
}, | |||
auth: 'basic_list' | |||
}, | |||
{ | |||
label: '删除', | |||
type: 'popconfirm', | |||
auth: 'basic_list', | |||
tip: '确定删除这条数据吗?', | |||
props: { | |||
onPositiveClick: deleteData.bind(null, [row.id]) | |||
}, | |||
ButtonProps: { | |||
text: true, | |||
type: 'primary' | |||
} | |||
} | |||
], | |||
align: 'center' | |||
}) | |||
} | |||
} | |||
] | |||
}) | |||
export default data |
@@ -0,0 +1,12 @@ | |||
import { reactive } from 'vue' | |||
export const search = reactive({ | |||
search: [ | |||
{ | |||
label: '仓库名称', | |||
key: 'warehouseName', | |||
props: { | |||
placeholder: '请输入仓库名称' | |||
} | |||
} | |||
] | |||
}) |
@@ -0,0 +1,140 @@ | |||
import { h, ref, reactive } from 'vue' | |||
import TableAction from '@/components/DataTable/tools/Action.vue' | |||
import { deleteMaterial } from '@/api/basic/material.js' | |||
/* 注册table */ | |||
const tableRef = ref() | |||
const searchParams = ref() | |||
function handleSearch(params) { | |||
searchParams.value = { ...params } | |||
tableRef.value.reFetch({ searchParams }) | |||
} | |||
/** | |||
* @description: 获取数据及操作 | |||
* @param {*} row 单行数据 | |||
* @param {*} type 操作类型 create:创建,preview:预览,edit:编辑 | |||
* @return {*} | |||
*/ | |||
function getRowData(row, type) { | |||
data.rowData = row | |||
data.modalType = type | |||
data.modalShow = true | |||
} | |||
/** | |||
* @description: 获取数据及操作 | |||
* @param {*} row 单行数据 | |||
* @param {*} type 操作类型 create:创建,preview:预览,edit:编辑 | |||
* @return {*} | |||
*/ | |||
function openDrawer(row, type) { | |||
data.rowData = row | |||
data.drawerShow = true | |||
} | |||
/** | |||
* @description: 删除用户接口 | |||
* @param {Array} ids 用户id集合 | |||
* @return {*} | |||
*/ | |||
function deleteData(ids) { | |||
deleteMaterial(ids).then((res) => { | |||
if (res.code === 0) { | |||
handleSearch() | |||
} | |||
}).catch(e => { | |||
console.log(e) | |||
}) | |||
} | |||
const data = reactive({ | |||
tableRef, | |||
searchParams, | |||
rowData: {}, | |||
modalType: 'create', | |||
modalShow: false, | |||
drawerShow: false, | |||
handleSearch, | |||
deleteData, | |||
columns: [ | |||
{ | |||
title: '序号', | |||
key: 'key', | |||
render: (_, index) => { | |||
return `${index + 1}` | |||
}, | |||
align: 'center' | |||
}, | |||
{ | |||
title: '仓库名称', | |||
key: 'warehouseName', | |||
align: 'center' | |||
}, | |||
{ | |||
title: '仓库位置', | |||
key: 'location', | |||
align: 'center' | |||
}, | |||
{ | |||
title: '编辑人', | |||
key: 'updateUser', | |||
align: 'center' | |||
}, | |||
{ | |||
title: '编辑时间', | |||
key: 'updateTime', | |||
align: 'center', | |||
width: 160 | |||
}, | |||
{ | |||
title: '操作', | |||
align: 'center', | |||
width: 150, | |||
fixed: 'right', | |||
render(row) { | |||
return h(TableAction, { | |||
actions: [ | |||
{ | |||
label: '修改', | |||
type: 'button', | |||
props: { | |||
type: 'primary', | |||
text: true, | |||
onClick: getRowData.bind(null, row, 'update') | |||
}, | |||
auth: 'basic_list' | |||
}, | |||
{ | |||
label: '物资管理', | |||
type: 'button', | |||
props: { | |||
type: 'primary', | |||
text: true, | |||
onClick: openDrawer.bind(null, row) | |||
}, | |||
auth: 'basic_list' | |||
}, | |||
{ | |||
label: '删除', | |||
type: 'popconfirm', | |||
auth: 'basic_list', | |||
tip: '确定删除这条数据吗?', | |||
props: { | |||
onPositiveClick: deleteData.bind(null, [row.id]) | |||
}, | |||
ButtonProps: { | |||
text: true, | |||
type: 'primary' | |||
} | |||
} | |||
], | |||
align: 'center' | |||
}) | |||
} | |||
} | |||
] | |||
}) | |||
export default data |
@@ -0,0 +1,68 @@ | |||
<template> | |||
<div class="custom-form"> | |||
<n-grid x-gap="12" :cols="2"> | |||
<n-gi> | |||
<n-form-item class="hidden__feedback"> | |||
<n-input v-model:value="longitude" placeholder="请输入经度" clearable @blur="handleBlur" /> | |||
</n-form-item> | |||
</n-gi> | |||
<n-gi> | |||
<n-form-item class="hidden__feedback"> | |||
<n-input v-model:value="latitude" placeholder="请输入纬度" clearable @blur="handleBlur" /> | |||
</n-form-item> | |||
</n-gi> | |||
</n-grid> | |||
<!-- <n-grid x-gap="12" :cols="1"> | |||
<n-gi> | |||
<n-form-item class="hidden__feedback"> | |||
<n-input v-model:value="address" placeholder="请输入安装位置" clearable @blur="handleBlur" /> | |||
</n-form-item> | |||
</n-gi> | |||
</n-grid> --> | |||
</div> | |||
</template> | |||
<script> | |||
import { reactive, watch, toRefs } from 'vue' | |||
export default { | |||
name: 'CustomForm', | |||
props: { | |||
value: { | |||
type: Object, | |||
default: () => {} | |||
} | |||
}, | |||
emits: ['done'], | |||
setup(props, { emit }) { | |||
const data = reactive({ | |||
longitude: '', | |||
latitude: '', | |||
...props.value | |||
}) | |||
// watch(data, (val) => { | |||
// emit('done', val) | |||
// }) | |||
const handleBlur = () => { | |||
emit('done', data) | |||
} | |||
return { | |||
...toRefs(data), | |||
handleBlur | |||
} | |||
} | |||
} | |||
</script> | |||
<style scoped lang='scss'> | |||
.custom-form{ | |||
width: 100%; | |||
} | |||
::v-deep(.hidden__feedback){ | |||
.n-form-item-feedback-wrapper{ | |||
display: none; | |||
} | |||
} | |||
</style> |
@@ -0,0 +1,158 @@ | |||
<template> | |||
<Modal | |||
:options="getModalOptions" | |||
:on-positive-click="handleConfirm" | |||
:on-negative-click="handleClose" | |||
:on-close="handleClose" | |||
> | |||
<template #Context> | |||
<n-form | |||
ref="formRef" | |||
:model="monitorForm" | |||
:rules="monitorRules" | |||
:label-width="100" | |||
label-placement="left" | |||
require-mark-placement="left" | |||
:disabled="disabled" | |||
> | |||
<template v-for="(item,index) in getFormOptions" :key="index"> | |||
<n-form-item :label="item.label" :path="item.key"> | |||
<n-input v-if="item.type === 'input'" v-model:value="monitorForm[item.key]" v-bind="item.props" /> | |||
<n-select v-if="item.type === 'select'" v-model:value="monitorForm[item.key]" v-bind="item.props" /> | |||
<CustomForm v-if="item.type === 'customPos'" v-model:value="monitorForm" @done="validatePartial" /> | |||
</n-form-item> | |||
</template> | |||
</n-form> | |||
</template> | |||
</Modal> | |||
</template> | |||
<script> | |||
import { form, changeMessage } from '../tools/form.js' | |||
import { defineComponent, ref, reactive, computed, toRefs } from 'vue' | |||
import Modal from '@/components/Modal/index.vue' | |||
import CustomForm from './CustomForm.vue' | |||
import { createCamera, updateCamera } from '@/api/basic/monitor.js' | |||
export default defineComponent({ | |||
name: 'UserModal', | |||
components: { Modal, CustomForm }, | |||
props: { | |||
visible: { | |||
type: Boolean, | |||
default: false | |||
}, | |||
type: { | |||
type: String, | |||
default: 'create' | |||
}, | |||
data: { | |||
type: Object, | |||
default: () => null | |||
} | |||
}, | |||
emits: { | |||
'update:visible': null, | |||
'reload': null | |||
}, | |||
setup(props, { emit }) { | |||
const MODAL_TYPE = { | |||
'create': '新建设备', | |||
'preview': '设备详情', | |||
'update': '编辑设备' | |||
} | |||
const { monitorForm, monitorRules } = form | |||
const formRef = ref() | |||
const data = reactive({ | |||
monitorForm: { | |||
...monitorForm, | |||
...props.data, | |||
customPos: props?.data?.id ? 'pass' : null | |||
}, | |||
monitorRules: { | |||
...monitorRules | |||
}, | |||
disabled: props.type === 'preview' | |||
}) | |||
const getModalOptions = computed(() => { | |||
return { | |||
show: props.visible, | |||
title: MODAL_TYPE[props.type], | |||
negativeText: '取消', | |||
positiveText: '确认' | |||
} | |||
}) | |||
const getFormOptions = computed(() => { | |||
return { | |||
...form.formItem | |||
} | |||
}) | |||
const validatePartial = (form) => { | |||
data.monitorForm.customPos = null | |||
if (form.longitude && form.latitude) { | |||
data.monitorForm.customPos = 'pass' | |||
const { latitude, longitude } = form | |||
data.monitorForm.longitude = Number(longitude) | |||
data.monitorForm.latitude = Number(latitude) | |||
} | |||
formRef.value?.validate( | |||
(errors) => { | |||
if (errors) { | |||
console.error(1111) | |||
} | |||
}, | |||
(rule) => { | |||
return rule?.key === 'custom' | |||
} | |||
) | |||
} | |||
function handleConfirm() { | |||
formRef.value?.validate((errors) => { | |||
if (!errors) { | |||
const params = { | |||
...data.monitorForm | |||
} | |||
if (params.id) { | |||
updateCamera(params) | |||
.then(res => { | |||
if (res.code === 0) { | |||
emit('reload') | |||
handleClose() | |||
} | |||
}) | |||
} else { | |||
/* 新增 */ | |||
createCamera(params) | |||
.then(res => { | |||
if (res.code === 0) { | |||
emit('reload') | |||
handleClose() | |||
} | |||
}) | |||
} | |||
} else { | |||
$message.error('请完善必填信息') | |||
} | |||
}) | |||
} | |||
/* 关闭弹窗 */ | |||
const handleClose = () => { | |||
emit('update:visible', false) | |||
} | |||
return { | |||
...toRefs(data), | |||
formRef, | |||
getModalOptions, | |||
getFormOptions, | |||
validatePartial, | |||
handleConfirm, | |||
handleClose | |||
} | |||
} | |||
}) | |||
</script> | |||
<style scoped lang='scss'> | |||
</style> |
@@ -0,0 +1,96 @@ | |||
<template> | |||
<div> | |||
<n-card> | |||
<headSearch :info="search" @search="handleSearch" @reset="handleSearch" /> | |||
<data-table | |||
ref="tableRef" | |||
:columns="columns" | |||
:row-key="(row) => row.id" | |||
:request="loadDataTable" | |||
size="large" | |||
@update:checked-row-keys="handleCheck" | |||
> | |||
<template #tableTitle> | |||
<n-button type="primary" @click="handleModal"> 新建 </n-button> | |||
</template> | |||
</data-table> | |||
</n-card> | |||
</div> | |||
<!-- 新增、编辑弹窗 --> | |||
<MonitorModal v-if="modalShow" v-model:visible="modalShow" :type="modalType" :data="rowData" @reload="handleSearch" /> | |||
</template> | |||
<script> | |||
import { search } from './tools/search.js' | |||
import table from './tools/table.js' | |||
import headSearch from '@/components/Search/index.vue' | |||
import dataTable from '@/components/DataTable/index.vue' | |||
import MonitorModal from './components/MonitorModal.vue' | |||
import { cameraPage } from '@/api/basic/monitor.js' | |||
import { unref, ref, toRefs, reactive, onUnmounted } from 'vue' | |||
export default { | |||
name: 'MenuPage', | |||
components: { dataTable, MonitorModal, headSearch }, | |||
setup() { | |||
const data = reactive({ | |||
...toRefs(table), | |||
...toRefs(search) | |||
}) | |||
const loadDataTable = async(res) => { | |||
const _params = { | |||
...unref(data.searchParams), | |||
...res | |||
} | |||
return await cameraPage(_params) | |||
} | |||
// 新增 | |||
function handleModal() { | |||
data.rowData = null | |||
data.modalType = 'create' | |||
data.modalShow = true | |||
} | |||
// 选择表格数据 | |||
const selectedIds = ref([]) | |||
function handleCheck(rowKeys) { | |||
selectedIds.value = rowKeys | |||
} | |||
// 批量删除 | |||
function deleteComplex() { | |||
if (selectedIds.value.length) { | |||
data.deleteData(selectedIds.value) | |||
} else { | |||
$message.warning('请至少选中一条数据') | |||
} | |||
} | |||
onUnmounted(() => { | |||
data.searchParams = null | |||
}) | |||
return { | |||
...toRefs(data), | |||
loadDataTable, | |||
handleModal, | |||
selectedIds, | |||
deleteComplex, | |||
handleCheck | |||
} | |||
}, | |||
methods: { | |||
} | |||
} | |||
</script> | |||
<style scoped lang='scss'> | |||
</style> | |||
<style scoped lang='scss'> | |||
.n-button + .n-button { | |||
margin-left: 10px; | |||
} | |||
</style> |
@@ -0,0 +1,43 @@ | |||
import { reactive } from 'vue' | |||
import { MONITOR_OPTIONS } from '@/utils/dictionary.js' | |||
export const form = reactive({ | |||
monitorForm: { | |||
cameraName: null, | |||
cameraType: null, | |||
customPos: null, | |||
longitude: null, | |||
latitude: null, | |||
flvUrl: null, | |||
remark: null | |||
}, | |||
monitorRules: { | |||
cameraName: [{ required: true, message: '请输入设备名称', trigger: 'blur' }], | |||
cameraType: [{ required: true, message: '请选择设备机型', type: 'number', trigger: 'blur' }], | |||
customPos: [{ key: 'custom', required: true, message: '请输入经纬度', trigger: ['blur', 'change'] }], | |||
flvUrl: [{ required: true, message: '请输入摄像头地址', trigger: 'blur', pattern: /^https:\/\/(([a-zA-Z0-9_-])+(\.)?)*(:\d+)?(\/((\.)?(\?)?=?&?[a-zA-Z0-9_-](\?)?)*)*$/i }] | |||
}, | |||
formItem: [ | |||
{ type: 'input', key: 'cameraName', label: '设备名称', props: { maxlength: '20', placeholder: '请输入设备名称', clearable: true }}, | |||
{ type: 'select', key: 'cameraType', label: '设备机型', props: { options: MONITOR_OPTIONS, placeholder: '请选择设备机型' }}, | |||
{ type: 'customPos', key: 'customPos', label: '安装位置' }, | |||
{ type: 'input', key: 'flvUrl', label: '摄像头地址', props: { maxlength: '20', placeholder: '请输入https://', clearable: true }}, | |||
{ type: 'input', key: 'remark', label: '备注', props: { type: 'textarea', autosize: { maxlength: '200', minRows: 3, maxRows: 3 }}} | |||
] | |||
}) | |||
export const changeMessage = (type) => { | |||
const list = form.monitorRules.customPos | |||
switch (type) { | |||
case 'lng': | |||
list[0].message = '请输入经度' | |||
break | |||
case 'lat': | |||
list[0].message = '请输入纬度' | |||
break | |||
case 'address': | |||
list[0].message = '请输入安装位置' | |||
break | |||
default: | |||
list[0].message = '请输入经纬度' | |||
} | |||
} |
@@ -0,0 +1,13 @@ | |||
import { reactive } from 'vue' | |||
export const search = reactive({ | |||
search: [ | |||
{ | |||
label: '设备名称', | |||
key: 'cameraName', | |||
props: { | |||
placeholder: '请输入设备名称' | |||
} | |||
} | |||
] | |||
}) |
@@ -0,0 +1,140 @@ | |||
import { h, ref, reactive } from 'vue' | |||
import TableTags from '@/components/DataTable/tools/Tags.vue' | |||
import TableAction from '@/components/DataTable/tools/Action.vue' | |||
import { MONITOR_OPTIONS } from '@/utils/dictionary.js' | |||
import { deleteCamera } from '@/api/basic/monitor.js' | |||
/* 注册table */ | |||
const tableRef = ref() | |||
const searchParams = ref() | |||
function handleSearch(params) { | |||
searchParams.value = { ...params } | |||
tableRef.value.reFetch({ searchParams }) | |||
} | |||
/** | |||
* @description: 获取数据及操作 | |||
* @param {*} row 单行数据 | |||
* @param {*} type 操作类型 create:创建,preview:预览,edit:编辑 | |||
* @return {*} | |||
*/ | |||
function getRowData(row, type) { | |||
data.rowData = row | |||
data.modalType = type | |||
data.modalShow = true | |||
} | |||
/** | |||
* @description: 删除用户接口 | |||
* @param {Array} ids 用户id集合 | |||
* @return {*} | |||
*/ | |||
function deleteData(ids) { | |||
deleteCamera(ids).then((res) => { | |||
if (res.code === 0) { | |||
handleSearch() | |||
} | |||
}).catch(e => { | |||
console.log(e) | |||
}) | |||
} | |||
const data = reactive({ | |||
tableRef, | |||
searchParams, | |||
rowData: {}, | |||
modalType: 'create', | |||
modalShow: false, | |||
handleSearch, | |||
deleteData, | |||
columns: [ | |||
{ | |||
title: '序号', | |||
key: 'key', | |||
render: (_, index) => { | |||
return `${index + 1}` | |||
}, | |||
align: 'center' | |||
}, | |||
{ | |||
title: '设备名称', | |||
key: 'cameraName', | |||
align: 'center' | |||
}, | |||
{ | |||
title: '设备机型', | |||
key: 'cameraType', | |||
align: 'center', | |||
render(row) { | |||
return h(TableTags, { | |||
data: row.cameraType, | |||
filters: MONITOR_OPTIONS | |||
}) | |||
} | |||
}, | |||
{ | |||
title: '所在位置', | |||
key: 'location', | |||
align: 'center', | |||
width: 160 | |||
}, | |||
{ | |||
title: '备注', | |||
key: 'remark', | |||
align: 'center', | |||
width: 160 | |||
}, | |||
{ | |||
title: '编辑时间', | |||
key: 'updateTime', | |||
align: 'center', | |||
width: 160 | |||
}, | |||
{ | |||
title: '编辑人', | |||
key: 'updateUser', | |||
align: 'center', | |||
width: 160 | |||
}, | |||
{ | |||
title: '操作', | |||
align: 'center', | |||
width: 150, | |||
fixed: 'right', | |||
render(row) { | |||
return h(TableAction, { | |||
actions: [ | |||
{ | |||
label: '修改', | |||
type: 'button', | |||
props: { | |||
type: 'primary', | |||
text: true, | |||
onClick: getRowData.bind(null, row, 'update') | |||
}, | |||
auth: 'basic_list' | |||
}, | |||
{ | |||
label: '删除', | |||
type: 'popconfirm', | |||
auth: 'basic_list', | |||
tip: '确定删除这条数据吗?', | |||
props: { | |||
onPositiveClick: deleteData.bind(null, [row.id]) | |||
}, | |||
ButtonProps: { | |||
text: true, | |||
type: 'primary' | |||
} | |||
} | |||
], | |||
align: 'center' | |||
}) | |||
} | |||
} | |||
] | |||
}) | |||
export default data |
@@ -0,0 +1,124 @@ | |||
<template> | |||
<Modal | |||
:options="getModalOptions" | |||
:on-positive-click="handleConfirm" | |||
:on-negative-click="handleClose" | |||
:on-close="handleClose" | |||
> | |||
<template #Context> | |||
<n-grid x-gap="12" :cols="2"> | |||
<n-gi> | |||
<!-- :src="fireDetail.fileOriginalUrl" --> | |||
<n-image | |||
width="340" | |||
src="https://07akioni.oss-cn-beijing.aliyuncs.com/07akioni.jpeg" | |||
/> | |||
</n-gi> | |||
<n-gi> | |||
<div class="flex__content"> | |||
<p class="content">火灾位置:{{ fireDetail.location }}</p> | |||
<p class="content">发现方式:{{ fireDetail.discoveryWay }}</p> | |||
<p class="content">上报时间:{{ fireDetail.createTime }}</p> | |||
</div> | |||
</n-gi> | |||
</n-grid> | |||
<p class="title">火灾核实记录</p> | |||
<div> | |||
<p v-for="(item,index) in fireDetail.warningRecordVOList" :key="index" class="content"> | |||
{{ `${item.recordStartTime} ${item.airportName}执行任务,` }} | |||
</p> | |||
</div> | |||
<p class="title">处理记录</p> | |||
<p class="content">{{ fireDetail.checkResult }}</p> | |||
<p class="content">处理人:{{ fireDetail.checkUser }}</p> | |||
<p class="content">处理时间:{{ fireDetail.checkTime }}</p> | |||
</template> | |||
</Modal> | |||
</template> | |||
<script> | |||
import { defineComponent, ref, reactive, computed, toRefs, onMounted } from 'vue' | |||
import Modal from '@/components/Modal/index.vue' | |||
import { fireDetail } from '@/api/early/fire.js' | |||
export default defineComponent({ | |||
name: 'UserModal', | |||
components: { Modal }, | |||
props: { | |||
visible: { | |||
type: Boolean, | |||
default: false | |||
}, | |||
type: { | |||
type: String, | |||
default: 'create' | |||
}, | |||
data: { | |||
type: Object, | |||
default: () => null | |||
} | |||
}, | |||
emits: { | |||
'update:visible': null, | |||
'reload': null | |||
}, | |||
setup(props, { emit }) { | |||
const data = reactive({ | |||
fireDetail: { | |||
} | |||
}) | |||
const getModalOptions = computed(() => { | |||
return { | |||
show: props.visible, | |||
width: 800, | |||
title: '火灾隐患', | |||
negativeText: '取消', | |||
positiveText: '确认' | |||
} | |||
}) | |||
const handleClose = () => { | |||
emit('update:visible', false) | |||
} | |||
onMounted(async() => { | |||
const res = await fireDetail(props.data.id) | |||
if (res.code === 0) { | |||
data.fireDetail = res.data | |||
} else { | |||
handleClose() | |||
} | |||
}) | |||
return { | |||
...toRefs(data), | |||
getModalOptions, | |||
handleClose | |||
} | |||
} | |||
}) | |||
</script> | |||
<style scoped lang='scss'> | |||
.n-grid{ | |||
margin-bottom: 20px; | |||
} | |||
.flex__content{ | |||
display: flex; | |||
flex-direction: column; | |||
padding-top: 20px; | |||
.content{ | |||
font-size: 18px; | |||
} | |||
} | |||
.title{ | |||
font-size: 18px; | |||
font-weight: bold; | |||
margin: 5px 0; | |||
border-bottom: 1px dashed rgba(0,0,0,0.3); | |||
} | |||
.content{ | |||
padding: 0 5px; | |||
font-size: 16px; | |||
} | |||
</style> |
@@ -0,0 +1,64 @@ | |||
<template> | |||
<div> | |||
<n-card> | |||
<HeadSearch :info="search" @search="handleSearch" @reset="handleSearch" /> | |||
<DataTable | |||
ref="tableRef" | |||
:columns="columns" | |||
:row-key="(row) => row.id" | |||
:request="loadDataTable" | |||
size="large" | |||
/> | |||
</n-card> | |||
</div> | |||
<!-- 新增、编辑弹窗 --> | |||
<FireModal v-if="modalShow" v-model:visible="modalShow" :type="modalType" :data="rowData" @reload="handleSearch" /> | |||
</template> | |||
<script> | |||
import { search } from './tools/search.js' | |||
import table from './tools/table.js' | |||
import HeadSearch from '@/components/Search/index.vue' | |||
import DataTable from '@/components/DataTable/index.vue' | |||
import FireModal from './components/FireModal.vue' | |||
import { firePage } from '@/api/early/fire.js' | |||
import { unref, toRefs, reactive, onUnmounted } from 'vue' | |||
export default { | |||
name: 'MenuPage', | |||
components: { HeadSearch, DataTable, FireModal }, | |||
setup() { | |||
const data = reactive({ | |||
...toRefs(table), | |||
...toRefs(search) | |||
}) | |||
const loadDataTable = async(res) => { | |||
const _params = { | |||
...unref(data.searchParams), | |||
...res | |||
} | |||
return await firePage(_params) | |||
} | |||
onUnmounted(() => { | |||
data.searchParams = null | |||
}) | |||
return { | |||
...toRefs(data), | |||
loadDataTable | |||
} | |||
}, | |||
methods: { | |||
} | |||
} | |||
</script> | |||
<style scoped lang='scss'> | |||
</style> | |||
<style scoped lang='scss'> | |||
.n-button + .n-button { | |||
margin-left: 10px; | |||
} | |||
</style> |
@@ -0,0 +1,36 @@ | |||
import { reactive } from 'vue' | |||
import { EARLY_SOURCE, FIRE_STATUS } from '@/utils/dictionary.js' | |||
export const search = reactive({ | |||
search: [ | |||
{ | |||
label: '预警来源', | |||
key: 'discoveryWay', | |||
type: 'select', | |||
props: { | |||
placeholder: '请选择预警来源', | |||
options: EARLY_SOURCE | |||
} | |||
}, | |||
{ | |||
label: '火灾状态', | |||
key: 'status', | |||
type: 'select', | |||
props: { | |||
placeholder: '请选择火灾状态', | |||
options: FIRE_STATUS | |||
} | |||
}, | |||
{ | |||
label: '预警时间', | |||
key: 'time', | |||
type: 'date', | |||
value: null, | |||
props: { | |||
type: 'daterange', | |||
valueFormat: 'yyyy-MM-dd', | |||
format: 'yyyy-MM-dd' | |||
} | |||
} | |||
] | |||
}) |
@@ -0,0 +1,95 @@ | |||
import { h, ref, reactive } from 'vue' | |||
import TableAction from '@/components/DataTable/tools/Action.vue' | |||
/* 注册table */ | |||
const tableRef = ref() | |||
const searchParams = ref() | |||
function handleSearch(params) { | |||
searchParams.value = { ...params } | |||
if (params?.time?.length) { | |||
searchParams.value = { | |||
...params, | |||
startTime: params.time[0], | |||
endTime: params.time[1] | |||
} | |||
} | |||
delete searchParams.value.time | |||
tableRef.value.reFetch({ searchParams }) | |||
} | |||
/** | |||
* @description: 获取数据及操作 | |||
* @param {*} row 单行数据 | |||
* @param {*} type 操作类型 create:创建,preview:预览,edit:编辑 | |||
* @return {*} | |||
*/ | |||
function getRowData(row, type) { | |||
data.rowData = row | |||
data.modalType = type | |||
data.modalShow = true | |||
} | |||
const data = reactive({ | |||
tableRef, | |||
searchParams, | |||
rowData: {}, | |||
modalType: 'create', | |||
modalShow: false, | |||
handleSearch, | |||
columns: [ | |||
{ | |||
title: '序号', | |||
key: 'key', | |||
render: (_, index) => { | |||
return `${index + 1}` | |||
}, | |||
align: 'center' | |||
}, | |||
{ | |||
title: '预警时间', | |||
key: 'waringTime', | |||
align: 'center' | |||
}, | |||
{ | |||
title: '预警来源', | |||
key: 'discoverWayName', | |||
align: 'center' | |||
}, | |||
{ | |||
title: '火灾位置', | |||
key: 'location', | |||
align: 'center' | |||
}, | |||
{ | |||
title: '火灾核实', | |||
key: 'statusName', | |||
align: 'center' | |||
}, | |||
{ | |||
title: '操作', | |||
align: 'center', | |||
width: 150, | |||
fixed: 'right', | |||
render(row) { | |||
return h(TableAction, { | |||
actions: [ | |||
{ | |||
label: '详情', | |||
type: 'button', | |||
props: { | |||
type: 'primary', | |||
text: true, | |||
onClick: getRowData.bind(null, row, 'update') | |||
}, | |||
auth: 'basic_list' | |||
} | |||
], | |||
align: 'center' | |||
}) | |||
} | |||
} | |||
] | |||
}) | |||
export default data |