Browse Source

feat: 导入功能,完善设备类别过滤。

main
betaqi 3 weeks ago
parent
commit
ceb5b84c45
  1. 5
      src/api/module/engineering/index.d.ts
  2. 16
      src/api/module/engineering/index.ts
  3. 115
      src/views/engineering/components/create-engineering-dlg.vue
  4. 2
      src/views/engineering/components/device-dlg.vue
  5. 55
      src/views/engineering/index.vue

5
src/api/module/engineering/index.d.ts vendored

@ -5,3 +5,8 @@ export interface IEngineeringOV {
isCreate: boolean isCreate: boolean
} }
export interface IEngineeringImport {
projectName: string
description?: string
file: File
}

16
src/api/module/engineering/index.ts

@ -1,6 +1,6 @@
import { globalServer } from '../index' import { globalServer } from '../index'
import type { IEngineeringOV } from './index.d' import type { IEngineeringImport, IEngineeringOV } from './index.d'
export const createEngineering = (params: IEngineeringOV) => { export const createEngineering = (params: IEngineeringOV) => {
return globalServer({ return globalServer({
@ -33,6 +33,18 @@ export const deleteEngineeringBatch = (names: string[]) => {
return globalServer({ return globalServer({
url: 'project/delete-list', url: 'project/delete-list',
method: 'delete', method: 'delete',
params: names.join(','), params: {
names: names.join(',')
},
})
}
export const importEngineering = (params: IEngineeringImport) => {
return globalServer({
url: '/project/import',
method: 'POST',
data: params,
headers: { 'Content-Type': 'multipart/form-data' },
timeout: 0,
}) })
} }

115
src/views/engineering/components/create-engineering-dlg.vue

@ -17,6 +17,29 @@
clearable clearable
/> />
</el-row> </el-row>
<el-row v-if="actionType === 'import'">
<div class="label">工程文件</div>
<el-upload
class="flex-1"
action=""
:auto-upload="false"
:limit="1"
:on-change="handleFileChange"
accept=".prj"
drag
:on-remove="handleRemove"
>
<el-icon class="el-icon--upload !text-3xl !mb-2 !text-gray-400">
<upload-filled />
</el-icon>
<div class="el-upload__text text-xs text-gray-500 mt-2">
拖拽文件或<em class="text-blue-500 not-italic cursor-pointer hover:underline"
>点击上传</em
>
<div class="mt-1 opacity-70">支持文件类型为.prj</div>
</div>
</el-upload>
</el-row>
<el-row> <el-row>
<div class="label">工程描述</div> <div class="label">工程描述</div>
<el-input <el-input
@ -34,23 +57,25 @@
<script setup lang="ts"> <script setup lang="ts">
import { cloneDeep } from 'lodash-es' import { cloneDeep } from 'lodash-es'
import { useRouter } from 'vue-router' import { UploadFilled } from '@element-plus/icons-vue'
import { useMessage } from '@/composables/useMessage' import { useMessage } from '@/composables/useMessage'
import EdfsDialog from '@/components/Edfs-dialog.vue' import EdfsDialog from '@/components/Edfs-dialog.vue'
import type { IEngineeringOV } from '@/api/module/engineering/index.d' import type { IEngineeringOV } from '@/api/module/engineering/index.d'
import { createEngineering } from '@/api/module/engineering' import { createEngineering, importEngineering } from '@/api/module/engineering'
import { validateName } from '@/utils/validate' import type { UploadFile } from 'element-plus'
const message = useMessage() const message = useMessage()
const router = useRouter()
type ActionType = 'create' | 'import'
const actionType = ref<ActionType>('create')
const props = defineProps<{ const props = defineProps<{
existingNames?: string[] existingNames?: string[]
}>() }>()
const emit = defineEmits<{ const emit = defineEmits<{
'on-save': [name: string] 'on-save': [name: string, actionType: ActionType]
}>() }>()
const fromData: IEngineeringOV = { const fromData: IEngineeringOV = {
@ -59,33 +84,86 @@ const fromData: IEngineeringOV = {
isCreate: true, isCreate: true,
} }
const file = ref<UploadFile | null>(null)
function handleRemove() {
file.value = null
}
async function handleFileChange(fileUpload: UploadFile) {
if (!fileUpload.raw) return
const isPrj = fileUpload.name.toLowerCase().endsWith('.prj')
// const isLt5M = fileUpload.size! / 1024 / 1024 < 5
if (!isPrj) {
message.warning('请上传 .prj 格式的文件')
file.value = null
return
}
// if (!isLt5M) {
// message.warning(' 5MB')
// file.value = null
// return
// }
file.value = fileUpload
}
const form = ref(cloneDeep(fromData)) const form = ref(cloneDeep(fromData))
const visible = ref(false) const visible = ref(false)
const btnLoading = ref(false) const btnLoading = ref(false)
function open() { function open(type: ActionType) {
actionType.value = type
visible.value = true visible.value = true
} }
async function onSave() { async function onSave() {
if (verifyData()) return if (verifyData()) return
btnLoading.value = true btnLoading.value = true
try { if (actionType.value === 'create') {
await createEngineering(form.value) await createdEngineering()
message.success('创建成功') } else {
emit('on-save', form.value.name) await importSave()
close() }
} catch (error) {
message.error('创建失败')
} finally {
btnLoading.value = false btnLoading.value = false
}
async function importSave() {
if (!file.value?.raw) return
const res = await importEngineering({
projectName: form.value.name,
description: form.value.description,
file: file.value?.raw,
})
if (res.code !== 0) {
message.error(res?.msg || '导入失败')
return
} }
message.success('导入成功')
emit('on-save', form.value.name, actionType.value)
close()
}
async function createdEngineering() {
const res = await createEngineering(form.value)
if (res.code != 0) {
message.error(res?.msg || '创建失败')
return
}
message.success('创建成功')
emit('on-save', form.value.name, actionType.value)
close()
} }
function close() { function close() {
visible.value = false visible.value = false
form.value = cloneDeep(fromData) form.value = cloneDeep(fromData)
file.value = null
} }
function verifyData() { function verifyData() {
@ -99,6 +177,11 @@ function verifyData() {
return true return true
} }
if (actionType.value === 'import' && !file.value?.raw) {
message.warning('请上传工程文件')
return true
}
return false return false
} }

2
src/views/engineering/components/device-dlg.vue

@ -110,7 +110,7 @@ const computedChannels = computed(() => {
}) })
const computedCategories = computed(() => { const computedCategories = computed(() => {
return props.categories[form.value.channel] return props.categories[form.value.channel]?.filter(r => r.status) ?? []
}) })
const form = ref<IDeviceFormData>(cloneDeep(FormData)) const form = ref<IDeviceFormData>(cloneDeep(FormData))

55
src/views/engineering/index.vue

@ -1,19 +1,27 @@
<template> <template>
<EdfsWrap class="wh-full" :title="'工程列表'" :use-scroll-bar="false"> <EdfsWrap :title="'工程列表'" :use-scroll-bar="false" class="wh-full">
<template #title-right> <template #title-right>
<div v-if="!isBatchMode"> <div v-if="!isBatchMode">
<el-button type="danger" plain @click="enterBatchMode"> 批量删除 </el-button> <el-button plain type="danger" @click="enterBatchMode">批量删除</el-button>
</div> </div>
<div v-else> <div v-else>
<el-button <el-button
type="danger"
:disabled="selectedItems.length === 0" :disabled="selectedItems.length === 0"
type="danger"
@click="handleBatchDelete" @click="handleBatchDelete"
> >
确认删除 确认删除
</el-button> </el-button>
<el-button @click="exitBatchMode">取消</el-button> <el-button @click="exitBatchMode">取消</el-button>
</div> </div>
<el-button
v-if="!isBatchMode"
plain
type="primary"
class="ml-2"
@click="importEngineering"
>导入工程</el-button
>
</template> </template>
<div class="engineering-list-container"> <div class="engineering-list-container">
<el-checkbox-group v-model="selectedItems" class="engineering-grid"> <el-checkbox-group v-model="selectedItems" class="engineering-grid">
@ -30,21 +38,21 @@
<div class="card-header"> <div class="card-header">
<div class="flex items-center gap-2 flex-1 overflow-hidden"> <div class="flex items-center gap-2 flex-1 overflow-hidden">
<el-checkbox v-if="isBatchMode" :value="item.name" @click.stop> <el-checkbox v-if="isBatchMode" :value="item.name" @click.stop>
<span class="card-title" :title="item.name" <span :title="item.name" class="card-title"
>工程名称{{ item.name }}</span >工程名称{{ item.name }}</span
> >
</el-checkbox> </el-checkbox>
<div v-else class="h-32 leading-32px"> <div v-else class="h-32 leading-32px">
<span class="card-title" :title="item.name" <span :title="item.name" class="card-title"
>工程名称{{ item.name }}</span >工程名称{{ item.name }}</span
> >
</div> </div>
</div> </div>
<el-button <el-button
link
type="danger"
v-if="!isBatchMode" v-if="!isBatchMode"
:icon="Delete" :icon="Delete"
link
type="danger"
@click="handleDelete(item)" @click="handleDelete(item)"
/> />
</div> </div>
@ -52,12 +60,12 @@
<p class="card-desc">工程描述{{ item.description }}</p> <p class="card-desc">工程描述{{ item.description }}</p>
</div> </div>
<div class="card-footer"> <div class="card-footer">
<el-button type="primary" text @click="handleDownload(item)" <el-button text type="primary" @click="handleDownload(item)"
>下载工程</el-button >下载工程
> </el-button>
<el-button type="primary" text @click="enterEngineering(item)" <el-button text type="primary" @click="enterEngineering(item)"
>进入工程</el-button >进入工程
> </el-button>
</div> </div>
</div> </div>
</el-checkbox-group> </el-checkbox-group>
@ -72,13 +80,13 @@
/> />
</template> </template>
<script setup lang="ts"> <script lang="ts" setup>
import { useRouter } from 'vue-router' import { useRouter } from 'vue-router'
import { ElMessage, ElMessageBox } from 'element-plus' import { ElMessage, ElMessageBox } from 'element-plus'
import EdfsWrap from '@/components/Edfs-wrap.vue' import EdfsWrap from '@/components/Edfs-wrap.vue'
import type { IEngineeringOV } from '@/api/module/engineering/index.d' import type { IEngineeringOV } from '@/api/module/engineering/index.d'
import CreateEngineeringDlg from './components/create-engineering-dlg.vue' import CreateEngineeringDlg from './components/create-engineering-dlg.vue'
import { Plus, Delete } from '@element-plus/icons-vue' import { Delete, Plus } from '@element-plus/icons-vue'
import { useEngineeringStore } from '@/stores/engineering' import { useEngineeringStore } from '@/stores/engineering'
import { storeToRefs } from 'pinia' import { storeToRefs } from 'pinia'
import { deleteEngineering, deleteEngineeringBatch } from '@/api/module/engineering' import { deleteEngineering, deleteEngineeringBatch } from '@/api/module/engineering'
@ -102,10 +110,18 @@ onMounted(() => {
}) })
function addEngineering() { function addEngineering() {
createEngineeringDlgRef.value?.open() createEngineeringDlgRef.value?.open('create')
}
function importEngineering() {
createEngineeringDlgRef.value?.open('import')
} }
function handleSave(name: string) { function handleSave(name: string, actionType: string) {
if (actionType === 'import') {
engineeringStore.fetchEngineeringList()
return
}
enterEngineering({ name, description: '', isCreate: true }) enterEngineering({ name, description: '', isCreate: true })
} }
@ -193,7 +209,7 @@ function handleBatchDelete() {
ElMessage.success('删除成功') ElMessage.success('删除成功')
selectedItems.value = [] selectedItems.value = []
isBatchMode.value = false isBatchMode.value = false
engineeringStore.fetchEngineeringList() await engineeringStore.fetchEngineeringList()
} }
}) })
.catch(() => {}) .catch(() => {})
@ -209,7 +225,7 @@ function exitBatchMode() {
} }
</script> </script>
<style scoped lang="scss"> <style lang="scss" scoped>
.engineering-list-container { .engineering-list-container {
height: 100%; height: 100%;
overflow-y: auto; overflow-y: auto;
@ -257,6 +273,7 @@ function exitBatchMode() {
.card-body { .card-body {
flex: 1; flex: 1;
margin-bottom: 16px; margin-bottom: 16px;
.card-desc { .card-desc {
font-size: 14px; font-size: 14px;
color: #606266; color: #606266;

Loading…
Cancel
Save