Переглянути джерело

Merge branch 'master' of http://39.106.8.246:3003/web/wanda-adm

YaolongHan 4 роки тому
батько
коміт
441d45b95d

+ 3 - 2
package.json

@@ -13,15 +13,16 @@
     "test:unit": "jest --clearCache && vue-cli-service test:unit"
   },
   "dependencies": {
+    "@babel/runtime": "^7.12.5",
     "@persagy-web/base": "2.2.1",
+    "@persagy-web/big": "2.2.48",
     "@persagy-web/draw": "2.2.12",
     "@persagy-web/graph": "2.2.44",
-    "@persagy-web/big": "2.2.48",
-    "@babel/runtime": "^7.12.5",
     "axios": "^0.21.1",
     "core-js": "^3.6.5",
     "element-ui": "^2.15.0",
     "js-cookie": "^2.2.1",
+    "moment": "^2.29.1",
     "normalize.css": "^8.0.1",
     "nprogress": "^0.2.0",
     "path-to-regexp": "^6.2.0",

+ 35 - 0
src/api/datacenter.ts

@@ -4,6 +4,21 @@
 import httputils from "@/api/httputils";
 const baseApi = "/datacenter";
 
+// 创建项目
+export function projectCreate(postParams: any): any {
+    return httputils.postJson(`${ baseApi }/object/project/create`, postParams);
+}
+
+// 查询项目信息
+export function projectQuery(postParams: any): any {
+    return httputils.postJson(`${ baseApi }/object/project/query`, postParams);
+}
+
+// 更新项目信息
+export function projectUpdate(postParams: any): any {
+    return httputils.postJson(`${ baseApi }/object/project/update`, postParams);
+}
+
 // 查询建筑信息
 export function buildingQuery(postParams: any): any {
     return httputils.postJson(`${ baseApi }/object/building/query`, postParams);
@@ -42,6 +57,16 @@ export function queryEquip(postParams: any): any {
     return httputils.postJson(`${ baseApi }/object/equip/query`, postParams)
 }
 
+// 创建设备信息
+export function createEquip(postParams: any): any {
+    return httputils.postJson(`${ baseApi }/object/equip/create`, postParams)
+}
+
+// 更新设备信息
+export function updateEquip(postParams: any): any {
+    return httputils.postJson(`${ baseApi }/object/equip/update`, postParams)
+}
+
 // 查询系统信息
 export function querySystem(postParams: any): any {
     return httputils.postJson(`${ baseApi }/object/system/query`, postParams)
@@ -76,6 +101,16 @@ export function queryCountSystem(postParams: any): any {
     return httputils.postJson(`${ baseApi }/object/system/count`, postParams)
 }
 
+// 创建系统信息
+export function createSystem(postParams: any): any {
+    return httputils.postJson(`${ baseApi }/object/system/create`, postParams)
+}
+
+// 更新系统信息
+export function updateSystem(postParams: any): any {
+    return httputils.postJson(`${ baseApi }/object/system/update`, postParams)
+}
+
 // 查询空间统计
 export function queryCountSpace(postParams: any): any {
     return httputils.postJson(`${ baseApi }/object/zone/count`, postParams)

+ 11 - 1
src/api/imageservice.ts

@@ -26,4 +26,14 @@ export function getUploadId(params: any): any {
  */
 export function mergeMultipart(params: any): any {
     return httputils.fetchJson(`${baseUrl}/common/merge_multipart`, params, {});
-}
+}
+
+/**
+ * 图片上传地址
+ */
+export const imageUploadUrl = `${baseUrl}/common/image_upload?systemId=dataPlatform&secret=9e0891a7a8c8e885&overwrite=true&key=`;
+
+/**
+ * 图片预览地址
+ */
+export const imageGetUrl = `${baseUrl}/common/image_get?systemId=dataPlatform&key=`;

+ 30 - 18
src/components/business/baseDataForm.vue

@@ -1,6 +1,6 @@
 <template>
     <el-row :gutter="20" class="data-form">
-        <el-form label-position="top" label-width="300px" :model="form">
+        <el-form label-position="top" label-width="300px" :model="form" :rules="rules" ref="form">
             <template v-if="Object.keys(objectHeaders).length > 0">
                 <div v-for="obj in objectHeaders" :key="obj.name">
                     <p class="title"> {{ obj.name }}</p>
@@ -8,18 +8,17 @@
                         <el-col :span="item.dataType == 'ATTACHMENT'? 24:8"
                                 :key="item.path"
                                 v-for="item in obj.data"
-                                :style="{ 'height':item.dataType == 'ATTACHMENT'? 'auto':'93px' }"
-                        >
-                            <el-form-item :label="item.name">
+                                :style="{ 'height':item.dataType == 'ATTACHMENT'? 'auto':'93px' }">
+                            <el-form-item :label="item.name" :prop="item.code">
+                                <el-input
+                                    v-if="item.path == 'classification' || item.path=='buildingSign' || item.path=='floorSign'"
+                                    v-model="form[item.path]"
+                                    disabled/>
                                 <el-input
                                     :disabled="!item.editable"
-                                    v-if="item.dataType == 'STRING'"
+                                    v-else-if="item.dataType == 'STRING'"
                                     v-model="form[item.path]"/>
-                                <el-input
-                                    v-else-if="item.path == 'classification'"
-                                    v-model="form[item.path]"
-                                    disabled
-                                />
+
                                 <el-input
                                     v-else-if="item.dataType == 'DOUBLE' || item.dataType == 'INTEGER'"
                                     type="number"
@@ -48,13 +47,15 @@
                                     v-else-if="item.dataType == 'DATETIME'"
                                     v-model="form[item.path]"
                                     placeholder="选择日期"
+                                    format="yyyy 年 MM 月 dd 日"
+                                    value-format="yyyy-MM-dd"
                                     type="date">
                                 </el-date-picker>
                                 <uploadImgs
                                     v-else-if="item.dataType == 'ATTACHMENT'&& item.path === 'infos.Pic'"
                                     :readOnly="false"
                                     v-model="form[item.path]"
-                                    :keysArr="Array.isArray(buildData[item.path]) ? buildData[item.path]:[]"
+                                    :keysArr="Array.isArray(form[item.path]) ? form[item.path]:[]"
                                     :context-key="item.path"
                                     :isShow="1"
                                     @change="changeItem"/>
@@ -64,7 +65,7 @@
                                     v-model="form[item.path]"
                                     :readOnly="false"
                                     :isShow="1"
-                                    :keys-arr="Array.isArray(buildData[item.path]) ? buildData[item.path]:[] "
+                                    :keys-arr="Array.isArray(form[item.path]) ? form[item.path]:[] "
                                     :show-file-list="false"
                                     :context-key="item.path"
                                     @change="changeItem"/>
@@ -111,9 +112,14 @@ function flattenKeys(obj: any) {
     components: { uploadFiles, uploadImgs }
 })
 export default class extends Vue {
+    rules = {
+        'localName': [{ required: true, message: '请填写本地名称', trigger: 'blur' }],
+        'localId': [{ required: true, message: '请填写本地编码', trigger: 'blur' }],
+        'buildingSign': [{ required: true, message: '请填写建筑', trigger: 'blur' }],
+        'floorSign': [{ required: true, message: '请填写本地楼层', trigger: 'blur' }]
+    }
     header = {}
     form = {}
-    buildData = {}
     @Prop({ default: Object }) objectHeaders ?: {}
     @Prop({ default: Object }) currRowContent ?: {}
 
@@ -126,6 +132,7 @@ export default class extends Vue {
         })
 
     }
+
     changeItem(val) {
         console.log(val)
         let _key = Object.keys(val)[0] + '';
@@ -133,6 +140,16 @@ export default class extends Vue {
 
     }
 
+    submitForm(callback) {
+        this.$refs.form.validate((valid) => {
+            if (valid) {
+                callback()
+            } else {
+                return false;
+            }
+        });
+    }
+
 }
 
 </script>
@@ -140,11 +157,6 @@ export default class extends Vue {
 <style scoped lang="scss">
 $border: 1px solid #E1E7EA;
 .data-form {
-    //.el-upload-dragger {
-    //    width: auto;
-    //    height: auto;
-    //}
-
     .title {
         border-left: 7px solid;
         text-indent: 10px;

+ 38 - 23
src/components/business/dataForm.vue

@@ -1,11 +1,12 @@
 <template>
     <el-row :gutter="20" class="data-form">
-        <el-form label-position="top" label-width="300px" :model="form">
+        <el-form label-position="top" ref="form" label-width="300px" :model="form" :rules="rules">
             <template v-if="Object.keys(header).length > 0">
                 <p class="title">基础信息</p>
                 <el-row :gutter="18">
                     <el-col :span="8" :key="item.path" v-for="item in header.basicInfos.data">
                         <el-form-item
+                            :prop="item.path"
                             :label="item.name">
                             <el-input v-model="form[item.path]"/>
                         </el-form-item>
@@ -17,7 +18,7 @@
                         <el-tab-pane v-for="(item,index) in paneMsg" :key="index" :label="item"
                                      :name="index.toString()"/>
                     </el-tabs>
-                    <div class="table" style="height: 200px;overflow: auto">
+                    <div class="table" style="height: 194px;overflow: auto">
                         <el-row :gutter="18">
                             <el-col :span="item.dataType == 'ATTACHMENT'? 24:8"
                                     :key="item.path"
@@ -58,13 +59,15 @@
                                         v-else-if="item.dataType == 'DATETIME'"
                                         v-model="form[item.path]"
                                         placeholder="选择日期"
+                                        format="yyyy 年 MM 月 dd 日"
+                                        value-format="yyyy-MM-dd"
                                         type="date">
                                     </el-date-picker>
                                     <uploadImgs
                                         v-else-if="item.dataType == 'ATTACHMENT'&& item.path === 'infos.Pic'"
                                         :readOnly="false"
                                         v-model="form[item.path]"
-                                        :keysArr="Array.isArray(buildData[item.path]) ? buildData[item.path]:[]"
+                                        :keysArr="Array.isArray(form[item.path]) ? form[item.path]:[]"
                                         :context-key="item.path"
                                         :isShow="1"
                                         @change="changeItem"/>
@@ -74,7 +77,7 @@
                                         v-model="form[item.path]"
                                         :readOnly="false"
                                         :isShow="1"
-                                        :keys-arr="Array.isArray(buildData[item.path]) ? buildData[item.path]:[] "
+                                        :keys-arr="Array.isArray(form[item.path]) ? form[item.path]:[] "
                                         :show-file-list="false"
                                         :context-key="item.path"
                                         @change="changeItem"/>
@@ -124,14 +127,19 @@ function flattenKeys(obj: any) {
     components: { uploadFiles, uploadImgs }
 })
 export default class extends Vue {
+    rules = {
+        bimTypeId: [{ required: true, message: "请填写BIM构件编码", trigger: 'blur' }],
+        systemCategory: [{ required: true, message: "请填写系统分类", trigger: 'blur' }],
+        codeName: [{ required: true, message: "请填写设备类型名称", trigger: 'blur' }],
+        localId: [{ required: true, message: "请填写本地编码", trigger: 'blur' }],
+    }
     // tabs数据
-    paneMsg = ['设计阶段维护', '施工阶段维护', '运维阶段维护']
-    activeName = ""
+    activeName = 0
     header = {}
     form = {}
+    paneMsg = []
     // 默认当前阶段
-    currentHeader = this.paneMsg[0]
-    buildData = {}
+    currentHeader = ''
     @Prop({ default: Object }) deviceHeaders ?: {}
     @Prop({ default: Object }) currRowContent ?: {}
 
@@ -148,24 +156,21 @@ export default class extends Vue {
     @Watch('deviceHeaders', { immediate: true, deep: true })
     changeHeaders() {
         this.$nextTick(() => {
-            let header = []
+            let pic = [], base = []
             if (typeof this.deviceHeaders == 'object' && Object.keys(this.deviceHeaders).length > 0) {
+                this.paneMsg = this.deviceHeaders.dictStages.map(i => i.name)
+                let currentHeader = this.paneMsg[0]
+                this.currentHeader = this.currentHeader || currentHeader
                 this.deviceHeaders.dictStages.length > 0 && this.deviceHeaders.dictStages.forEach(item => {
                     if (this.currentHeader == item.name) {
-                        item.infos && item.infos.forEach(val => header.push(val))
-                    }
-                })
-                header.map(item => {
-                    const category = ['ENUM', 'MENUM', 'BOOLEAN']
-                    if (item.category == 'STATIC' && item.dataType.includes(category) && item.dataSource) {
-                        item.props = {
-                            value: 'code',
-                            label: 'name',
-                            children: 'content',
-                            checkStrictly: true
-                        }
+                        item.infos && item.infos.forEach(val => {
+                            if (val.dataType == 'ATTACHMENT') {
+                                pic.push(val)
+                            } else {
+                                base.push(val)
+                            }
+                        })
                     }
-                    return item
                 })
             }
             this.header = {
@@ -175,7 +180,7 @@ export default class extends Vue {
                 },
                 dictStages: {
                     name: this.currentHeader,
-                    data: header
+                    data: pic.length > 0 ? [...base, ...pic] : [...base]
                 }
             }
         })
@@ -192,6 +197,16 @@ export default class extends Vue {
         this.currentHeader = val.label
         this.changeHeaders()
     }
+
+    submitForm(callback) {
+        this.$refs.form.validate((valid) => {
+            if (valid) {
+                callback()
+            } else {
+                return false;
+            }
+        });
+    }
 }
 
 </script>

+ 51 - 11
src/components/public/adm-multi-table.vue

@@ -12,15 +12,17 @@
         >
             <el-table-column prop="date" label="编辑" width="80" align="center" fixed>
                 <template slot-scope="scope">
-                    <span class="el-icon-edit el-input__icon" style="cursor: pointer" @click="handleEdit(scope.$index, scope.row)"> </span>
+                    <span class="el-icon-edit el-input__icon" style="cursor: pointer"
+                          @click="handleEdit(scope.$index, scope.row)"> </span>
                 </template>
             </el-table-column>
             <el-table-column v-for="item in headersStage" :key="item.name" :label="item.name" align="center">
                 <el-table-column
                     v-for="(child, index) in item.data"
                     :key="index"
-                    :label="child.unit ? child.name + ' (' + child.unit + ')': child.name"
+                    :label="child.unit ? child.name + ' (' + child.unit + ')' : child.name"
                     :prop="child.path"
+                    :formatter="formatContent"
                     align="center"
                 >
                 </el-table-column>
@@ -31,8 +33,6 @@
 </template>
 
 <script lang="ts">
-
-
 import { ElTable } from "element-ui/types/table";
 import { Component, Emit, Prop, Ref, Vue, Watch } from "vue-property-decorator";
 @Component({
@@ -43,26 +43,66 @@ export default class extends Vue {
     // 表格数据
     @Prop({ default: Array }) tableData?: [];
     // 表头数据
-    @Prop({ default: Object }) headersStage?: {};
+    @Prop({ default: Object }) headersStage?: any;
     // 当前页头文字
     @Prop({ default: "" }) currentHeader?: string;
 
-    @Emit('handleCurrentEdit')
-    handleEdit(index, row) {
-        return row
+    @Emit("handleCurrentEdit")
+    handleEdit(index: number, row: any) {
+        return row;
     }
 
+    get headerInfoMap() {
+        if (this.headersStage?.dictStages?.data?.length) {
+            const headerInfoMap: any = {};
+            this.headersStage.dictStages.data.forEach((item: any) => {
+                headerInfoMap[item.path] = item;
+            });
+            return headerInfoMap;
+        } else {
+            return {};
+        }
+    }
 
-    created() {
+    /**
+     * 表格特殊内容格式化处理
+     */
+    private formatContent(row: any, column: any, cellValue: any) {
+        const info = this.headerInfoMap[column.property];
+        if (info && cellValue) { //有值且获取到表头信息
+            const dataSource = info?.dataSource ? info.dataSource : [];
+            if (info.dataType === "ENUM" || info.dataType === "BOOLEAN") { //内容为单选枚举和布尔类型
+                const text = dataSource.find((item: any) => {
+                    return item.code === cellValue;
+                })?.name;
+                return text;
+            } else if (info.dataType === "MENUM" && cellValue instanceof Array && cellValue.length) { //内容为多选枚举且值为数组
+                const textList = dataSource.map((item: any) => {
+                    if (
+                        cellValue.find((value: string) => {
+                            return item?.code === value;
+                        })
+                    ) {
+                        return item?.name;
+                    }
+                }).filter((item: any) => item).join(",");
+                return textList;
+            } else if (info.dataType === "ATTACHMENT") { // 内容为附件类型
+                return cellValue.length + '个' || ''
+            } else {
+                return cellValue;
+            }
+        }
+        return cellValue;
     }
 
     @Watch("headersStage", { immediate: true, deep: true })
     onCurrentHeaderChange() {
         this.$nextTick(() => {
-            if (this.tableDom?.doLayout)
-                this.tableDom.doLayout();
+            if (this.tableDom?.doLayout) this.tableDom.doLayout();
         });
     }
+
 }
 </script>
 

+ 3 - 0
src/main.ts

@@ -16,6 +16,9 @@ import '@/utils/directive' //自定义指令
 import "@/icons/components";
 import '@/assets/iconfont/iconfont.css'
 import "@/permission";
+import moment from "moment";
+Vue.prototype.moment = moment;
+moment.locale('zh-cn')
 
 Vue.use(uploader);
 Vue.use(ElementUI, { size: "small", zIndex: 1000 });

+ 1 - 2
src/router/index.ts

@@ -165,7 +165,6 @@ export default new Router({
                 }
             ]
         },
-
         {
             path: "/scene",
             component: Layout,
@@ -223,7 +222,7 @@ export default new Router({
             meta: {
                 title: "综合管理与其他",
                 icon: "icon-zongheguanliyuqita"
-            }
+            },
         },
         {
             path: "/error",

+ 3 - 0
src/store/modules/app.ts

@@ -33,6 +33,9 @@ class App extends VuexModule implements IAppState {
     public uploaderList: any[] = [];
     public device = DeviceType.Desktop;
 
+    // 文件服务器基础路径-下载文件
+    mapBaseUrl = '/image-service/common/file_get?systemId=revit&key='
+
     @Mutation
     private TOGGLE_SIDEBAR(withoutAnimation: boolean) {
         this.sidebar.opened = !this.sidebar.opened;

+ 41 - 0
src/utils/graph/LocationScene.ts

@@ -0,0 +1,41 @@
+import { SMouseButton, SMouseEvent } from "@persagy-web/base/lib";
+import { FloorScene } from "./FloorScene";
+import { MarkerItem } from "./MarkItem";
+
+/**
+ * 维护设备位置
+*/
+export class LocationScene extends FloorScene {
+    markList: MarkerItem[] = [];
+    marking: boolean = true;
+
+    onMouseDown(event: SMouseEvent): boolean {
+        console.log(event);
+        if (this.marking && event.buttons == SMouseButton.LeftButton) {
+            this.clearMark();
+            this.addMarker({ x: event.x, y: event.y })
+        }
+        return super.onMouseDown(event)
+    }
+
+    clearMark(): void {
+        this.markList.forEach(t => {
+            this.removeItem(t)
+        })
+        this.markList = [];
+        this.view!!.update()
+    }
+
+    /**
+     * 添加标志到scene中
+     *
+     * @param   marker  标志对象
+     */
+    addMarker(marker: any): void {
+        let mark = new MarkerItem(null, marker);
+        mark.selectable = true
+        mark.moveTo(marker.x, marker.y);
+        this.markList.push(mark);
+        this.addItem(mark);
+    } // Function addMarker()
+}

Різницю між файлами не показано, бо вона завелика
+ 74 - 0
src/utils/graph/MarkItem.ts


BIN
src/utils/graph/img/mark.png


+ 226 - 0
src/views/maintain/device/components/deviceGraph.vue

@@ -0,0 +1,226 @@
+<template>
+    <div id="deviceGraph">
+        <div>
+            <el-button size="small" style="float:left;padding:7px" @click="goBack">
+                <i class="iconfont icon-fanhui"></i>
+            </el-button>
+            <el-cascader clearable ref="graphCascader" placeholder="请选择建筑楼层" :options="graphOptions" v-model="graphBuilding" filterable size="small"
+                @change="changeGraphCascader" style="margin-left: 12px" :props="graphProps"></el-cascader>
+        </div>
+        <div class="graphContainer" ref="graphContainer">
+            <div v-show="floorKey" v-loading="canvasLoading">
+                <canvas id="deviceCanvas" :width="canvasWidth" :height="canvasHeight" tabindex="0"></canvas>
+            </div>
+            <div v-show="!floorKey">
+                <p style="text-align:center;padding-top: 220px;">
+                    暂无数据
+                </p>
+            </div>
+        </div>
+    </div>
+</template>
+<script lang="ts">
+import { Vue, Component, Watch, Prop, Emit } from "vue-property-decorator";
+import { FloorView } from "@/utils/graph/FloorView";
+import { LocationScene } from "@/utils/graph/LocationScene";
+import { SObject } from "@persagy-web/base/lib";
+import { buildingQuery } from "@/api/datacenter";
+import { AppModule } from "@/store/modules/app";
+@Component({
+    components: {},
+})
+export default class deviceGraph extends Vue {
+    @Prop({
+        type: Object,
+        default: () => {
+            return {};
+        },
+    })
+    equip?: any;
+
+    canvasWidth = 800;
+    canvasHeight = 800;
+    view: FloorView | null = null;
+    scene: LocationScene | null = null;
+    floorKey = "";
+    canvasLoading = false;
+    graphProps = {
+        label: "localName",
+        value: "id",
+        children: "floor",
+    };
+    graphOptions = [];
+    graphBuilding = [];
+    floorToMap = {};
+    floor = {};
+    hasLocation = false;
+
+    get mapBaseUrl() {
+        return AppModule.mapBaseUrl;
+    }
+
+    created() {
+        this.getFloor();
+    }
+    // 挂载
+    mounted(): void {
+        this.canvasWidth = this.$refs.graphContainer.offsetWidth;
+        this.canvasHeight = this.$refs.graphContainer.offsetHeight;
+    }
+    // 设置设备默认位置
+    initDeviceLocation() {
+        if (this.equip && this.equip.buildingId && this.equip.floorId) {
+            this.graphBuilding = [this.equip.buildingId, this.equip.floorId];
+            this.changeGraphCascader(this.graphBuilding, true);
+        }
+    }
+    getFloor() {
+        const pa = {
+            pageSize: 1000,
+            orders: "localName asc",
+            cascade: [{ name: "floor", orders: "floorSequenceId desc" }],
+        };
+        buildingQuery(pa).then((res) => {
+            this.floorToMap = {};
+            try {
+                this.graphOptions = res.content.map((t) => {
+                    if (!t.floor) {
+                        t.floor = [];
+                    }
+                    t.floor.forEach((floor) => {
+                        this.floorToMap[floor.id] = floor;
+                    });
+                    return t;
+                });
+            } catch (err) {
+                this.graphOptions = [];
+            }
+            this.initDeviceLocation();
+        });
+    }
+    changeGraphCascader(val: [], flag = false) {
+        this.hasLocation = flag;
+        if (
+            this.graphBuilding.length > 1 &&
+            this.floorToMap[this.graphBuilding[1]]
+        ) {
+            this.getData(this.floorToMap[this.graphBuilding[1]]);
+        } else {
+            this.noMap();
+        }
+    }
+    getData(floor: any) {
+        this.canvasLoading = true;
+        if (floor.infos && floor.infos.floorMap) {
+            this.floor = floor;
+            const url = this.mapBaseUrl + floor.infos.floorMap;
+            if (url != this.floorKey) {
+                this.floorKey = this.mapBaseUrl + floor.infos.floorMap;
+                this.getGraph();
+            }
+        } else {
+            this.noMap();
+        }
+    }
+    getGraph() {
+        const scene = new LocationScene();
+        this.canvasLoading = true;
+        this.clearGraphy();
+        this.scene = null;
+        scene.loadUrl(this.floorKey).then((res) => {
+            if (this.view) {
+                this.view.scene = scene;
+            }
+            this.scene = scene;
+            this.getGraphSuc(res);
+        });
+    }
+    // 获取底图成功
+    getGraphSuc(res: any): void {
+        this.canvasLoading = false;
+        if (res == "error") {
+            this.noMap();
+            this.$message.warning("数据解析异常");
+            return;
+        }
+        if (this.view) {
+            this.view.fitSceneToView();
+            this.view.minScale = this.view.scale;
+        }
+        if (this.$refs.canvasFun) {
+            // @ts-ignore
+            this.$refs.canvasFun.everyScale = this.view.scale;
+        }
+        if (this.scene) {
+            this.scene.isSpaceSelectable = false;
+        }
+        if (this.hasLocation) {
+            this.resetLocation();
+        }
+        this.canvasLoading = false;
+    }
+    noMap() {
+        this.floorKey = "";
+        this.canvasLoading = false;
+    }
+    // 清除canvas
+    clearGraphy() {
+        if (this.view) {
+            this.view.scene = null;
+            return;
+        }
+        this.view = new FloorView("deviceCanvas");
+    }
+    // 保存
+    getLocation() {
+        const arr = this.scene?.markList || [];
+        if (arr.length) {
+            const obj = {
+                x: arr[0].x | 0,
+                y: -arr[0].y | 0,
+                z: 0,
+                buildingId: this.graphBuilding[0],
+                floorId: this.graphBuilding[1],
+            };
+            if (
+                this.equip &&
+                this.equip.locationJson &&
+                this.equip.locationJson.z
+            ) {
+                obj.z = this.equip.locationJson.z;
+            }
+            return obj;
+        }
+        return undefined;
+    }
+    // 重置
+    resetLocation() {
+        this.scene?.clearMark();
+        if (this.equip.locationJson && this.scene) {
+            this.scene.addMarker({
+                x: this.equip.locationJson.x,
+                y: -this.equip.locationJson.y,
+            });
+        }
+    }
+    // 返回
+    @Emit("goBack")
+    goBack() {
+        return;
+    }
+}
+</script>
+<style lang="scss">
+#deviceGraph {
+    min-width: 800px;
+    height: 440px;
+    .graphContainer {
+        width: 100%;
+        overflow: hidden;
+        height: calc(100% - 4px);
+        canvas {
+            margin: 0 auto;
+        }
+    }
+}
+</style>

+ 134 - 33
src/views/maintain/device/index.vue

@@ -15,7 +15,9 @@
             </el-tabs>
             <div class="table" v-loading="loading">
                 <template v-if="deviceType.length > 0">
-                    <admMultiTable :currentHeader="currentHeader" @handleCurrentEdit="handleCurrentEdit"
+                    <admMultiTable :currentHeader="currentHeader"
+                                   @handleCurrentEdit="handleCurrentEdit"
+
                                    :tableData="tableData" :headersStage="headersStage"/>
                     <Pagination v-if="tableData.length > 0" :paginationList="paginationList"
                                 @handleCurrentChange="handleCurrentChange"
@@ -49,20 +51,19 @@
                             <el-cascader :options="list" clearable v-model="deviceVal"
                                          :props="optionProps"
                                          class="adm-select"></el-cascader>
-
                         </div>
                         <el-button type="primary" class="fr" @click="handleNext">下一步</el-button>
                     </template>
                     <template v-else-if="displayLocation && deviceVal">
-                        <div class="dialog-button">
-                            <el-button type="primary">保存</el-button>
-                            <el-button type="default">取消</el-button>
-                            <el-button type="default">重置</el-button>
-                        </div>
+                        <deviceGraph ref="deviceGraph" :equip="curEquip" @goBack="goBack"/>
+                        <span slot="footer" class="dialog-footer">
+                            <el-button type="primary" @click="saveLocation">确定</el-button>
+                            <el-button type="default" @click="resetLocation">重置</el-button>
+                        </span>
                     </template>
                     <template v-else>
                         <div>
-                            <dataForm :deviceHeaders="deviceHeaders" ref="dataForm" :currRowContent="currRowContent"/>
+                            <dataForm :deviceHeaders="deviceHeaders" ref="dataForm"/>
                         </div>
                         <span slot="footer" class="dialog-footer">
                          <el-button type="primary" @click="handleDataForm">确 定</el-button>
@@ -71,17 +72,15 @@
                     </template>
                 </template>
                 <template v-else>
-                    <template v-if="maintain == '维护'">
-                        <div class="dialog-button">
-                            <el-button type="primary">保存</el-button>
-                            <el-button type="default">取消</el-button>
-                            <el-button type="default">重置</el-button>
-                        </div>
+                    <template v-if="displayLocation && deviceVal">
+                        <deviceGraph ref="deviceGraph" :equip="curEquip" @goBack="goBack"/>
+                        <span slot="footer" class="dialog-footer">
+                            <el-button type="primary" @click="saveLocation">确定</el-button>
+                            <el-button type="default" @click="resetLocation">重置</el-button>
+                        </span>
                     </template>
                     <template v-else>
-
-                            <dataForm :deviceHeaders="deviceHeaders" ref="dataForm" :currRowContent="currRowContent"/>
-
+                        <dataForm :deviceHeaders="deviceHeaders"  ref="dataForm" :currRowContent="currRowContent"/>
                         <span slot="footer" class="dialog-footer">
                          <el-button type="primary" @click="handleDataForm">确 定</el-button>
                          <el-button @click="handlePosition">维护位置</el-button>
@@ -98,14 +97,15 @@
 import { Component, Vue, Watch } from "vue-property-decorator";
 import { AdmMultiTable, AdmSearch, dataForm, Pagination, Statistics } from '../components/index'
 import { allDevice, BeatchQueryParam, dictInfo } from "@/api/equipComponent";
-import { queryCount, queryEquip } from "@/api/datacenter";
+import { createEquip, queryCount, queryEquip, updateEquip } from "@/api/datacenter";
 import { UserModule } from "@/store/modules/user";
+import deviceGraph from "./components/deviceGraph.vue"
 import tools from "@/utils/maintain"
 
 
 @Component({
     name: 'adm-device',
-    components: { Statistics, AdmSearch, AdmMultiTable, Pagination, dataForm }
+    components: { Statistics, AdmSearch, AdmMultiTable, Pagination, dataForm, deviceGraph }
 })
 export default class extends Vue {
 
@@ -142,9 +142,9 @@ export default class extends Vue {
     // 弹窗开关
     dialogVisible = false
     // tabs数据
-    paneMsg = ['设计阶段维护', '施工阶段维护', '运维阶段维护']
+    paneMsg = []
     // 当前tabs值
-    activeName = ""
+    activeName = 0
     // 分页
     paginationList = {
         page: 1,
@@ -157,7 +157,7 @@ export default class extends Vue {
     // 弹窗 title
     deviceMsg = '添加设备'
     // 默认当前阶段
-    currentHeader = this.paneMsg[0]
+    currentHeader = ''
     // 主体数据
     tableData = []
     codeToDataSource = {}
@@ -168,6 +168,8 @@ export default class extends Vue {
     status = '添加'
     // 维护位置开关
     maintain = ''
+    // 传到维护位置的设备信息
+    curEquip = {}
 
     // 项目id
     get projectId(): string {
@@ -194,7 +196,6 @@ export default class extends Vue {
         })
     }
 
-
     handleChangeDevice() {
         if (this.deviceType[1]) {
             this.loading = true
@@ -212,12 +213,12 @@ export default class extends Vue {
                 param2.filters += `;codeName contain '${ this.inputSearch }' or systemCategory contain '${ this.inputSearch }' or bimTypeId contain '${ this.inputSearch }' or localId contain '${ this.inputSearch }'`
             }
             let promise = new Promise(resolve => {
-                dictInfo(param).then(res => {
+                dictInfo(param).then((res: []) => {
                     resolve(res)
                 })
             })
             let promise2 = new Promise(resolve => {
-                queryEquip(param2).then(res => {
+                queryEquip(param2).then((res: []) => {
                     resolve(res)
                 })
             })
@@ -226,6 +227,8 @@ export default class extends Vue {
                 // 类型下信息点,默认设计阶段
                 this.headerInformation = res[0] // 获取表头
                 this.tableData = res[1].content // 主体数据
+                this.paneMsg = res[0].dictStages.map(i => i.name)
+                this.currentHeader = this.paneMsg[this.activeName]
                 this.headerStage()
                 this.paginationList.total = res[1].total
             })
@@ -249,11 +252,17 @@ export default class extends Vue {
     }
 
     headerStage() {
-        let header: [] = []
+        let pic = [], base = []
         if (Object.keys(this.headerInformation).length > 0) {
             this.headerInformation.dictStages.forEach(item => {
                 if (this.currentHeader == item.name) {
-                    item.infos && item.infos.forEach(val => header.push(val))
+                    item.infos && item.infos.forEach(val => {
+                        if (val.dataType == 'ATTACHMENT') {
+                            pic.push(val)
+                        } else {
+                            base.push(val)
+                        }
+                    })
                 }
             })
         }
@@ -264,11 +273,11 @@ export default class extends Vue {
             },
             dictStages: {
                 name: this.currentHeader,
-                data: header
+                data: pic.length > 0 ? [...base, ...pic] : [...base]
             }
         }
         // 信息点集合
-        this.all = [...this.headerInformation.basicInfos, ...header]
+        this.all = [...this.headersStage.basicInfos.data, ...this.headersStage.dictStages.data]
         this.codeToDataSource = {}
         this.all.forEach(item => {
             if (item.dataSource) {
@@ -328,7 +337,10 @@ export default class extends Vue {
                                         tools.setDataForKey(
                                             item,
                                             head.path,
-                                            child.error
+                                            child.error ? child.value
+                                                ? "表号功能号格式错误"
+                                                : "表号功能号不存在"
+                                                : "暂未采集到实时数据"
                                         );
                                     }
                                 });
@@ -370,21 +382,39 @@ export default class extends Vue {
     addDevice() {
         this.status = '添加'
         this.dialogVisible = true
+        this.currRowContent = {}
     }
 
 
     // 维护位置
     handlePosition() {
+        this.$refs.dataForm.submitForm(this.handlePositionSave)
+    }
+
+    handlePositionSave() {
+        this.curEquip = tools.formatData(this.$refs.dataForm.form)
         this.displayLocation = true
     }
 
     // 添加 事件处理
     handleDataForm() {
-        console.log(this.$refs.dataForm.form)
+        this.$refs.dataForm.submitForm(this.handleDataFormSave)
+    }
+
+    handleDataFormSave() {
+        const eq = tools.formatData(this.$refs.dataForm.form);
+        if (eq.id) {
+            //更新
+            this.handleUpdateEquip(eq);
+        } else {
+            eq.classCode = this.deviceVal[1]
+            // 创建
+            this.handleCreateEquip(eq);
+        }
     }
 
     // 编辑当前行
-    handleCurrentEdit(val) {
+    handleCurrentEdit(val: object) {
         this.status = '编辑'
         this.currRowContent = val
         this.handleNext()
@@ -402,6 +432,76 @@ export default class extends Vue {
         this.displayLocation = false
     }
 
+    // 取消
+    cancelLocation() {
+        // @ts-ignore
+        this.$refs.deviceGraph.cancelLocation()
+    }
+
+    // 保存
+    saveLocation() {
+        // @ts-ignore
+        const data = this.$refs.deviceGraph.getLocation()
+        if (data) {
+            this.curEquip.bimLocation = `${ data.x },${ data.y },${ data.z }`;
+            this.curEquip.buildingId = data.buildingId;
+            this.curEquip.floorId = data.floorId;
+        }
+        if (this.curEquip.id) {
+            //更新
+            this.handleUpdateEquip(this.curEquip);
+        } else {
+            this.curEquip.classCode = this.deviceVal[1]
+            // 创建
+            this.handleCreateEquip(this.curEquip);
+        }
+    }
+
+    // 更新设备
+    handleUpdateEquip(obj) {
+        let pa;
+        if (Array.isArray(obj)) {
+            pa = { content: obj }
+        } else {
+            pa = { content: [obj] }
+        }
+        updateEquip(pa).then(res => {
+            if (res.result == 'success') {
+                this.$message.success('更新成功');
+                this.dialogVisible = false;
+                this.handleChangeDevice()
+            }
+        })
+    }
+
+    // 创建设备
+    handleCreateEquip(obj: any) {
+        let pa;
+        if (Array.isArray(obj)) {
+            pa = { content: obj }
+        } else {
+            pa = { content: [obj] }
+        }
+        createEquip(pa).then(res => {
+            if (res.result == 'success') {
+                this.$message.success('创建成功');
+                this.dialogVisible = false;
+                this.handleChangeDevice()
+            }
+        })
+    }
+
+    // 重置
+    resetLocation() {
+        // @ts-ignore
+        this.$refs.deviceGraph.resetLocation()
+    }
+
+    // 返回
+    goBack() {
+        this.displayLocation = false
+    }
+
     @Watch("deviceType", { immediate: true, deep: true })
     handleDeviceMsg() {
         this.deviceVal = this.deviceType
@@ -544,7 +644,8 @@ $border: 1px solid #E1E7EA;
 
 .el-dialog {
     .el-dialog__body {
-        max-height: 500px !important;
+        padding: 20px;
+        max-height: 643px !important;
         min-height: 100px;
         overflow-y: auto;
         overflow-x: hidden;

+ 12 - 0
src/views/maintain/relationship/components/tableHeader.ts

@@ -0,0 +1,12 @@
+const tableHeader = {
+    '设备所在建筑': ['建筑', 'BIM构件名称', '系统分类', '设备类型名称', '设备本地编码'],
+    '设备所在楼层': ['建筑', '楼层', 'BIM构件名称', '系统分类', '设备类型名称', '设备本地编码'],
+    '设备所在功能空间': ['空间本地名称', '空间本地编码', '建筑', '楼层', 'BIM构件名称', '系统分类', '设备类型名称', '设备本地编码'],
+    '设备服务功能空间': ['空间本地名称', '空间本地编码', '建筑', '楼层', 'BIM构件名称', '系统分类', '设备类型名称', '设备本地编码'],
+    '设备所在供电空间': ['空间本地名称', '空间本地编码', '建筑', '楼层', 'BIM构件名称', '系统分类', '设备类型名称', '设备本地编码'],
+    '空间所在建筑': ['建筑', '空间本地名称', '空间本地编码', '楼层'],
+    '空间所在楼层': ['建筑', '楼层', '空间本地名称', '空间本地编码'],
+    '空间服务空间': ['空间本地名称', '空间本地编码', '建筑', '楼层', '空间本地名称', '空间本地编码', '建筑', '楼层'],
+    '系统下的设备': ['系统分类', '分类名称', '系统编码', 'BIM构件名称', '系统分类', '设备类型名称', '设备本地编码'],
+}
+export default tableHeader

+ 12 - 15
src/views/maintain/relationship/index.vue

@@ -1,7 +1,7 @@
 <template>
     <div class="relationship" v-loading="loading">
         <template v-for="(item,index) in content">
-            <p class="title" :key="index">架构从属关系</p>
+            <p class="title" >架构从属关系</p>
             <section v-for="(child,childIndex) in item.childGraphicTypeList" :key="childIndex">
                 <p class="ship-title">{{ child.graphTypeName }}
                     <i class="iconfont icon-xiangmuguanli"></i>
@@ -45,12 +45,10 @@
                                 </el-col>
                             </el-row>
                             <article class="ship-card-time">
-                                <span v-show="relation.computationalState === 3">关系计算中...</span>
-                                <span v-show="relation.computationalState === 5">计算失败</span>
-                                <span
-                                    v-show="relation.computationalState !== 3  && relation.computingTime">最后一次计算时间 {{
-                                        relation.computingTime
-                                    }}</span>
+                                <span v-if="relation.computationalState === 3">关系计算中...</span>
+                                <span v-else-if="relation.computationalState === 5">计算失败</span>
+                                <span v-else-if="relation.computationalState !== 3  && relation.computingTime">最后一次计算时间 {{ relation.computingTime }}</span>
+                                <span v-else-if="relation.times">最后一次计算时间 {{ relation.times }}</span>
                                 <span style="float: right">
                                 <el-tooltip
                                     content="启动计算"
@@ -60,7 +58,6 @@
                                         :is-dot="relation.computationalState === 2 || relation.computationalState === 5">
                                         <el-button
                                             @click="computed(relation)"
-                                            :disabled="!relation.source && !relation.automatic"
                                             circle
                                             type=""
                                             plain
@@ -100,6 +97,7 @@
 <script lang="ts">
 import { Component, Vue } from "vue-property-decorator";
 import { relationshipCount, relationshipOverview } from "@/api/datacenter"
+import moment from "moment";
 @Component({
     name: "relationShip"
 })
@@ -159,15 +157,13 @@ export default class extends Vue {
         });
     }
 
-    computed() {
-
+    computed(relation) {
+        this.$set(relation, 'times', moment(Date.now()).format("YYYY-MM-DD HH:mm:ss"))
     }
 
-    manual() {
-        this.$router.push({
-            path: 'relation',
-            meta: [{ title: '222', hidden: true }]
-        })
+    manual(relation) {
+        console.log(relation.relationTypeName)
+        this.$router.push({ path: 'relation', query:relation})
     }
 }
 </script>
@@ -251,6 +247,7 @@ export default class extends Vue {
         span {
             font-size: 12px;
             color: #C3C7CB;
+            line-height: 3;
         }
 
         .sm-button {

+ 6 - 45
src/views/maintain/relationship/relation/components/table.vue

@@ -8,51 +8,10 @@
         height="100%"
     >
         <el-table-column
-            prop="Name"
-            label="空间本地名称"
-            align="left"
-            min-width="140"
-        >
-        </el-table-column>
-        <el-table-column
-            prop="LocalID"
-            label="空间本地编码"
-            align="left"
-            min-width="140"
-        >
-        </el-table-column>
-        <el-table-column
-            prop="LocalName"
-            label="建筑-楼层"
-            align="left"
-            min-width="140"
-        >
-        </el-table-column>
-        <el-table-column
-            prop="CADID"
-            label="BIM构件名称"
-            align="left"
-            min-width="100"
-        >
-        </el-table-column>
-        <el-table-column
-            class-name="td-bl"
-            prop="cName"
-            label="系统分类"
-            width="180"
-            align="left"
-        >
-        </el-table-column>
-
-        <el-table-column
-            prop="cLocalName"
-            label="设备类型名称"
-            align="left"
-        >
-        </el-table-column>
-        <el-table-column
+            v-for="(item,index) in tableHeader"
+            :key="index"
             prop="cCADID"
-            label="设备本地编码"
+            :label="item"
             align="left"
         >
         </el-table-column>
@@ -69,12 +28,14 @@
 </template>
 
 <script lang="ts">
-import { Component, Vue } from "vue-property-decorator";
+import { Component, Prop, Vue } from "vue-property-decorator";
 
 @Component({
     name: 'relation-table'
 })
 export default class extends Vue {
+
+    @Prop({default:Array}) tableHeader?:[]
     tableData = []
 
     objectSpanMethod() {

+ 14 - 5
src/views/maintain/relationship/relation/index.vue

@@ -19,7 +19,7 @@
                 <AdmSearch/>
             </el-col>
         </el-row>
-        <relationTable/>
+        <relationTable :tableHeader="tableHeader"/>
     </div>
 </template>
 
@@ -27,13 +27,20 @@
 import { Component, Vue } from "vue-property-decorator";
 import { AdmSearch } from "./../../components"
 import relationTable from "./components/table"
+import tableHeader from "@/views/maintain/relationship/components/tableHeader";
 @Component({
     name: 'relation',
-    components: { AdmSearch,relationTable }
+    components: { AdmSearch, relationTable }
 })
 export default class extends Vue {
     mainObject = ''
+    //表头数据
+    tableHeader = []
 
+    created() {
+        let relationTypeName = this.$route.query.relationTypeName
+        this.tableHeader = tableHeader[relationTypeName]
+    }
 }
 </script>
 
@@ -47,10 +54,12 @@ export default class extends Vue {
         overflow: hidden;
         border-bottom: 1px solid #E1E7EA;
     }
-    .content{
+
+    .content {
         padding: 12px;
-        .object{
-            p{
+
+        .object {
+            p {
                 border-left: 7px solid #555555;
                 text-indent: 10px;
                 margin-bottom: 10px;

+ 7 - 2
src/views/maintain/space/components/spaceGraph.vue

@@ -82,6 +82,7 @@ import { SObject } from "@persagy-web/base/lib";
 import { ItemColor } from "@persagy-web/big/lib";
 import { SSpaceItem } from "@persagy-web/big/lib/items/floor/SSpaceItem";
 import { SZoneItem } from "@persagy-web/big/lib/items/floor/ZoneItem";
+import { AppModule } from "@/store/modules/app";
 
 @Component({
     components: { canvasFun, createBSP },
@@ -91,7 +92,6 @@ export default class spaceGraph extends Vue {
     canvasHeight = 800;
     view: FloorView | null = null;
     scene: FloorScene | null = null;
-    mapBaseUrl = "/image-service/common/file_get?systemId=revit&key=";
     canvasLoading = false;
     type = 1;
     config = {
@@ -113,6 +113,11 @@ export default class spaceGraph extends Vue {
     BIMIDToSID = {};
     BIMIDToSIName = {};
     sourceIdToISP = {};
+
+    get mapBaseUrl() {
+        return AppModule.mapBaseUrl;
+    }
+
     // 挂载
     mounted(): void {
         this.canvasWidth = this.$refs.graphContainer.offsetWidth;
@@ -120,7 +125,7 @@ export default class spaceGraph extends Vue {
         this.getGraph();
     }
     // 父组件调用查询楼层底图
-    getData(floor, zoneType) {
+    getData(floor: any, zoneType: any) {
         this.curZoneType = zoneType[zoneType.length - 1];
         this.canvasLoading = true;
         if (floor.infos && floor.infos.floorMap) {

+ 36 - 37
src/views/maintain/space/index.vue

@@ -83,7 +83,7 @@ export default class spaceIndex extends Vue {
     loading = false;
     private statisticsMsg = {
         title: "全部空间",
-        total: 93640,
+        total: 0,
     };
     currentHeader = "租赁系统";
     activeName = "table";
@@ -110,11 +110,11 @@ export default class spaceIndex extends Vue {
             children: [
                 {
                     value: "FireZone",
-                    label: "防火分区",
+                    label: "防火空间",
                 },
                 {
                     value: "PowerSupplyZone",
-                    label: "供电分区",
+                    label: "供电空间",
                 },
             ],
         },
@@ -246,8 +246,6 @@ export default class spaceIndex extends Vue {
                 });
             });
             this.options = data;
-            let arr = data.filter((item) => item.children);
-            console.log(arr);
         });
 
         const pa = {
@@ -276,12 +274,9 @@ export default class spaceIndex extends Vue {
             }
         });
     }
+
     //改变楼层
-    changeCascader(value) {
-        //todo delete
-        // if (value && value.length > 0  && this.building[0] != 'all') {
-        //     this.buildingLabel = this.$refs['floorCascader'].getCheckedNodes()[0].pathLabels
-        // }
+    changeCascader() {
         if (this.building.length > 0) {
             this.loading = true;
             let param = {
@@ -310,20 +305,19 @@ export default class spaceIndex extends Vue {
                         break;
                     case "all":
                         delete param2.filters;
-
                         break;
                 }
             } else if (this.building.length === 2) {
                 switch (this.building[1]) {
                     case "noKnow":
-                        param2.filters = `buildingId='${this.building[0]}';floorId='isnull'`;
+                        param2.filters = `buildingId='${ this.building[0] }';floorId='isnull'`;
 
                         break;
                     case "all":
-                        param2.filters = `buildingId='${this.building[0]}'`;
+                        param2.filters = `buildingId='${ this.building[0] }'`;
                         break;
                     default:
-                        param2.filters = `buildingId='${this.building[0]}';floorId='${this.building[1]}'`;
+                        param2.filters = `buildingId='${ this.building[0] }';floorId='${ this.building[1] }'`;
                 }
             }
 
@@ -393,11 +387,6 @@ export default class spaceIndex extends Vue {
                     if (item.outline) {
                         delete item.outline;
                     }
-                    // const category = ['ENUM', 'MENUM', 'BOOLEAN']
-                    // if (item.category == 'STATIC' && item.dataType.includes(category) && item.dataSource) {
-                    //     console.log(item, item.path)
-                    // }
-
                     // item = {
                     //     ...item,
                     //     outline: JSON.stringify(item.outline),
@@ -408,11 +397,6 @@ export default class spaceIndex extends Vue {
                 //  列表信息展示,获取动态数据
                 this.codeToDataSource = {};
                 this.all.forEach((item) => {
-                    const category = ['ENUM', 'MENUM', 'BOOLEAN']
-                    console.log(item.category,item.dataType)
-                    if (item.category == 'STATIC' && item.dataType.includes(category) && item.dataSource) {
-                        console.log(21)
-                    }
                     if (item.dataSource) {
                         try {
                             this.codeToDataSource[item.code] = {};
@@ -424,7 +408,6 @@ export default class spaceIndex extends Vue {
                             console.log(e);
                         }
                     }
-                    console.log(this.codeToDataSource,' this.codeToDataSource')
                 });
                 this.getBatch(this.tableData);
             });
@@ -432,12 +415,15 @@ export default class spaceIndex extends Vue {
             console.log("void");
         }
     }
+
     // 图建筑楼层修改
-    changeGraphCascader(value) {
+    changeGraphCascader() {
         this.toGraph();
     }
+
     // 动态信息点
     getBatch(data) {
+
         let param = {
             groupCode: "WD",
             appId: "datacenter",
@@ -482,7 +468,10 @@ export default class spaceIndex extends Vue {
                                         tools.setDataForKey(
                                             item,
                                             head.path,
-                                            child.error
+                                            child.error ? child.value
+                                                ? "表号功能号格式错误"
+                                                : "表号功能号不存在"
+                                                : "暂未采集到实时数据"
                                         );
                                     }
                                 });
@@ -513,17 +502,23 @@ export default class spaceIndex extends Vue {
 
     // 添加/编辑 事件处理
     handleDataForm() {
+        this.$refs.baseDataForm.submitForm(this.handleDataFormSave)
+    }
+
+    handleDataFormSave() {
         let from = tools.formatData(this.$refs.baseDataForm.form)
         let param = {
             content: [from]
         }
         updateZone(param).then(res => {
-            res.result == 'success' && this.$message.success('更新成功')
-            this.dialogVisible = false
-            // this.getData();
-            // this.dataCount();
-            // 默认显示全部建筑信息
-            this.changeCascader();
+            if (res.result == 'success') {
+                this.$message.success('更新成功')
+                this.dialogVisible = false
+                // 默认显示全部建筑信息
+                this.changeCascader();
+            }
+
+
         })
     }
 
@@ -539,11 +534,12 @@ export default class spaceIndex extends Vue {
     }
 
     // 编辑当前行
-    handleCurrentEdit(val) {
-        // this.next = false
+    handleCurrentEdit(val: object) {
         this.currRowContent = val
+        let pic = this.headersStage.dictStages.data.filter(i => i.dataType == 'ATTACHMENT')
+        let base = this.headersStage.dictStages.data.filter(i => i.dataType != 'ATTACHMENT')
+        this.headersStage.dictStages.data = pic.length > 0 ? [...base, ...pic] : [...base]
         this.spaceHeaders = this.headersStage
-        // this.handleNext()
         this.dialogVisible = true
     }
 
@@ -620,9 +616,11 @@ export default class spaceIndex extends Vue {
                     margin-left: 16px;
                 }
             }
+
             .borderBottom {
                 border-bottom: 1px solid #e1e7ea;
             }
+
             .graph {
                 height: calc(100% - 64px);
             }
@@ -640,7 +638,8 @@ export default class spaceIndex extends Vue {
 .adm-space {
     .el-dialog {
         .el-dialog__body {
-            max-height: 500px !important;
+            padding: 20px;
+            max-height: 643px !important;
             min-height: 100px;
             overflow-y: auto;
             overflow-x: hidden;

+ 76 - 35
src/views/maintain/system/index.vue

@@ -14,9 +14,11 @@
         <div class="content">
             <div v-loading="loading" class="table">
                 <template v-if="systemType.length > 0">
-                    <admMultiTable @handleCurrentEdit="handleCurrentEdit"
-                                   :currentHeader="currentHeader"
-                                   :headersStage="headersStage" :tableData="tableData"/>
+                    <admMultiTable
+                        @handleCurrentEdit="handleCurrentEdit"
+                        :currentHeader="currentHeader"
+                        :headersStage="headersStage"
+                        :tableData="tableData"/>
                     <Pagination
                         :paginationList="paginationList"
                         @handleCurrentChange="handleCurrentChange"
@@ -32,7 +34,7 @@
             </div>
         </div>
         <!--        添加/编辑 系统-->
-        <el-dialog :title=" systemMsg" :visible.sync="dialogVisible" @close="close">
+        <el-dialog :title="systemMsg" :visible.sync="dialogVisible" @close="close">
             <template v-if="next">
                 <div class="align" style="height: 400px">
                     <span class="text">系统类别</span>
@@ -58,9 +60,9 @@
 </template>
 
 <script lang="ts">
-import { Component, Vue } from "vue-property-decorator";
+import { Component, Vue, Watch } from "vue-property-decorator";
 import { AdmMultiTable, AdmSearch, baseDataForm, Pagination, Statistics } from "../components/index";
-import { dictQuery, queryCountSystem, querySystem } from "@/api/datacenter";
+import { createSystem, dictQuery, queryCountSystem, querySystem, updateSystem } from "@/api/datacenter";
 import tools from "@/utils/maintain";
 import { UserModule } from "@/store/modules/user";
 import { allSystem, BeatchQueryParam } from "@/api/equipComponent";
@@ -159,7 +161,7 @@ export default class extends Vue {
                 });
             }
         });
-        if (param.data.length) {
+        if (param.data.length >0 ) {
             BeatchQueryParam(param).then((res) => {
                 this.tableData = data.map((item) => {
                     res.data.map((child) => {
@@ -177,7 +179,10 @@ export default class extends Vue {
                             } else {
                                 this.all.map((head) => {
                                     if (head.code == child.infoCode) {
-                                        tools.setDataForKey(item, head.path, child.error);
+                                        tools.setDataForKey(item, head.path, child.error ? child.value
+                                            ? "表号功能号格式错误"
+                                            : "表号功能号不存在"
+                                            : "暂未采集到实时数据");
                                     }
                                 });
                             }
@@ -208,23 +213,31 @@ export default class extends Vue {
 
     // 添加设备
     addDevice() {
+        this.systemMsg = '添加系统'
+        this.currRowContent = {}
         this.dialogVisible = true;
     }
 
     //下一步事件
     async handleNext() {
-        if (this.systemVal[1] || this.systemType[1]) {
+        if (this.systemVal[1]) {
             this.next = false
             let param = {
-                type: this.systemVal[1] || this.systemType[1],
+                type: this.systemVal[1],
                 orders: "sort asc, name desc",
             };
 
             await dictQuery(param).then(res => {
                 let { basicInfos, dictStages } = this.informationArrangement(res.content)
                 // 添加系统分类
-                dictStages = dictStages.map((item) => {
+                let pic = [], base = []
+                dictStages.map((item) => {
                     item = { ...item, classification: this.systemLabel };
+                    if (item.dataType == 'ATTACHMENT') {
+                        pic.push(item)
+                    } else {
+                        base.push(item)
+                    }
                     return item;
                 });
                 this.systemHeaders = {
@@ -234,7 +247,7 @@ export default class extends Vue {
                     },
                     dictStages: {
                         name: "运维阶段",
-                        data: dictStages,
+                        data: pic.length > 0 ? [...base, ...pic] : [...base],
                     },
                 };
             })
@@ -245,12 +258,18 @@ export default class extends Vue {
 
     // close
     close() {
+        if (this.systemType) {
+            this.systemVal = this.systemType
+        } else {
+            this.systemVal = ''
+        }
         this.next = true;
     }
 
     // 编辑当前行
     handleCurrentEdit(val) {
         this.next = false
+        this.systemMsg = '编辑系统'
         this.currRowContent = val
         this.handleNext()
         this.dialogVisible = true
@@ -258,7 +277,7 @@ export default class extends Vue {
 
     //信息点重组
     informationArrangement(arr: []): object {
-        let basicInfos = [{ path: "classification", name: "系统分类" }],
+        let basicInfos = [{ path: "classification", name: "系统分类"}],
             dictStages: any[] = [];
         this.all = arr;
         arr.forEach((item: any) => {
@@ -275,7 +294,7 @@ export default class extends Vue {
         }
     }
 
-    private changeSystemList() {
+    changeSystemList() {
         if (this.systemType[1]) {
             let systemLabel = this.$refs["floorCascader"].getCheckedNodes()[0].pathLabels;
             this.systemLabel = systemLabel[1];
@@ -288,7 +307,7 @@ export default class extends Vue {
                 filters: this.systemType[1] ? `classCode='${ this.systemType[1] }'` : undefined,
                 pageNumber: this.paginationList.page,
                 pageSize: this.paginationList.size,
-                orders: "createTime desc, id asc",
+                orders: "createTime asc, id asc",
                 projectId: this.projectId,
             };
             if (this.inputSearch != "") {
@@ -309,8 +328,6 @@ export default class extends Vue {
                 this.loading = false;
                 // 类型下信息点,重组数据
                 let { basicInfos, dictStages } = this.informationArrangement(res[0].content)
-
-
                 this.headersStage = {
                     basicInfos: {
                         name: "基础信息台账",
@@ -328,32 +345,52 @@ export default class extends Vue {
                     item = { ...item, classification: this.systemLabel };
                     return item;
                 });
+                this.getBatch(this.tableData);
             });
-            // 动态信息点
-            this.codeToDataSource = {};
-            this.all.forEach((item) => {
-                if (item.dataSource) {
-                    try {
-                        this.codeToDataSource[item.code] = {};
-                        item.dataSource.forEach((dic) => {
-                            this.codeToDataSource[item.code][dic.code] = dic.name;
-                        });
-                    } catch (e) {
-                        console.log(e);
-                    }
-                }
-            });
-            this.getBatch(this.tableData);
         } else {
             console.log("void");
         }
     }
 
     // 添加/编辑 事件处理
-    handleDataForm() {
-        console.log(this.$refs.baseDataForm.form)
+    async handleDataForm() {
+        this.$refs.baseDataForm.submitForm(this.handleDataFormSave)
+
     }
 
+    handleDataFormSave() {
+        let from = tools.formatData(this.$refs.baseDataForm.form)
+        //编辑
+        if (Object.keys(this.currRowContent).length > 0) {
+            let param = {
+                content: [from]
+            }
+            updateSystem(param).then(res => {
+                res.result == 'success' && this.$message.success('更新成功')
+                this.dialogVisible = false
+                setTimeout(() => this.changeSystemList())
+            })
+        } else {
+            //添加
+            from = {
+                ...from,
+                classCode: this.systemVal[1]
+            }
+            let param = {
+                content: [from]
+            }
+            createSystem(param).then(res => {
+                res.result == 'success' && this.$message.success('添加成功')
+                this.dialogVisible = false
+                this.changeSystemList()
+            })
+        }
+    }
+
+    @Watch("systemType", { immediate: true, deep: true })
+    handleDeviceMsg() {
+        this.systemVal = this.systemType
+    }
 }
 </script>
 
@@ -441,7 +478,8 @@ $border: 1px solid #e1e7ea;
 
     .el-dialog {
         .el-dialog__body {
-            max-height: 500px !important;
+            padding: 20px;
+            max-height: 643px !important;
             min-height: 100px;
             overflow-y: auto;
             overflow-x: hidden;
@@ -451,6 +489,9 @@ $border: 1px solid #e1e7ea;
     .el-select, .el-date-editor.el-input, .el-date-editor.el-input__inner {
         width: 100%;
     }
+    .el-dialog__header {
+        border-bottom: 1px solid #D8D8D8;
+    }
 
     .fr {
         float: right;

+ 1 - 1
src/views/manage/build/components/AddFloorDialog/index.vue

@@ -279,7 +279,7 @@ export default class extends Vue {
     }
 }
 </script>
-<style lang="scss">
+<style lang="scss" scoped>
 #addFloorDialog {
     .floorModle {
         display: flex;

+ 1 - 1
src/views/manage/build/components/FloorTable/index.vue

@@ -10,7 +10,7 @@
         >
             <el-table-column align="center" label="楼层本地名" width="100">
                 <template slot-scope="scope">
-                    <!-- <i class="el-icon-info" style="cursor: pointer" @click="toPathControl(scope.row)"></i> -->
+                    <i class="el-icon-info" style="cursor: pointer" @click="toPathControl(scope.row)"></i>
                     <span style="margin-left: 10px">{{ scope.row.FloorName }}</span>
                 </template>
             </el-table-column>

+ 2 - 2
src/views/manage/build/components/ModelLog/index.vue

@@ -11,8 +11,9 @@
                         <el-table :data="filterlogData" stripe height="300px" style="width: 100%">
                             <el-table-column prop="Version" label="版本" width="50"></el-table-column>
                             <el-table-column prop="ModelName" label="模型文件名" width="240"></el-table-column>
-                            <el-table-column prop="AcceptTime" label="上传时间"></el-table-column>
+                            <el-table-column prop="AcceptTime" label="上传时间" width="135"></el-table-column>
                             <el-table-column prop="UserName" label="上传人"></el-table-column>
+                            <el-table-column prop="Note" :show-overflow-tooltip="true" label="备注" width="150"></el-table-column>
                             <el-table-column prop="address" label="操作">
                                 <template slot-scope="scope">
                                     <el-button
@@ -29,7 +30,6 @@
                             </el-table-column>
                         </el-table>
                     </el-tab-pane>
-                    <el-tab-pane label="工程改造" name="second"></el-tab-pane>
                 </el-tabs>
             </div>
         </el-dialog>

+ 6 - 21
src/views/manage/build/components/RepliceModel/index.vue

@@ -4,7 +4,7 @@
     <!-- 新增楼层 -->
     <div id="replaceModel">
         <el-dialog title="替换模型" :visible.sync="repliceModelVisible" width="650px" :before-close="handleClose">
-            <el-form ref="form" :model="form" label-width="170px">
+            <el-form ref="form" :model="form" label-width="80px">
                 <el-form-item label="模型文件:">
                     <el-upload
                         class="upload-demo"
@@ -25,21 +25,8 @@
                         <el-option v-for="item in revitVersionList" :key="`revit-${item.value}`" :label="item.label" :value="item.value"></el-option>
                     </el-select>
                 </el-form-item> -->
-                <el-form-item label="选择模型文件替换原因:">
-                    <ul>
-                        <li>
-                            <el-radio :disabled="isChioce" v-model="form.ReplaceReason" label="0">之前模型画错要修改</el-radio>
-                        </li>
-                        <li>
-                            <el-radio :disabled="isChioce" v-model="form.ReplaceReason" label="1">因工程改造,更新模型文件</el-radio>
-                        </li>
-                    </ul>
-                </el-form-item>
-                <el-form-item>
-                    <div class="dateTime" v-show="form.ReplaceReason === '1'">
-                        <p>工程改造竣工时间:</p>
-                        <el-date-picker v-model="form.finishTime" type="date" placeholder="选择日期"></el-date-picker>
-                    </div>
+                <el-form-item label="备注:">
+                    <el-input type="textarea" :rows="3" v-model="form.Note"> </el-input>
                 </el-form-item>
             </el-form>
             <div slot="footer" class="dialog-footer">
@@ -92,8 +79,7 @@ export default class extends Vue {
     private form: any = {
         revitVersion: "2017", //revit默认版本
         file: null, //上传文件
-        ReplaceReason: "0",
-        finishTime: "",
+        Note: "",
     };
     // revit版本选项
     private revitVersionList = [
@@ -110,8 +96,6 @@ export default class extends Vue {
             label: "2018",
         },
     ];
-    // 是否可以选择替换原因
-    private isChioce = true;
     // 上传楼层列表
     private fileList = [];
 
@@ -129,7 +113,7 @@ export default class extends Vue {
                 FloorName: this.replaceModelItem.FloorName,
                 FolderId: this.replaceModelItem.FolderId,
                 RevitVersion: this.form.revitVersion,
-                Note: this.replaceModelItem.Note,
+                Note: this.form.Note,
                 ProjectId: this.projectId,
                 ReplaceReason: 0,
                 Size: this.form.file.size,
@@ -156,6 +140,7 @@ export default class extends Vue {
      */
     private handleRemove() {
         this.form.file = null;
+        this.form.Note = "";
         this.fileList = [];
     }
 

+ 13 - 0
src/views/management/mappingRelation/index.vue

@@ -0,0 +1,13 @@
+<template>
+    <div>映射关系维护</div>
+</template>
+
+<script>
+export default {
+    name: 'index'
+}
+</script>
+
+<style scoped>
+
+</style>

+ 222 - 0
src/views/project/components/AddProjectDialog/index.vue

@@ -0,0 +1,222 @@
+<template>
+    <!-- 新增/修改项目文件 -->
+    <div id="addProjectDialog">
+        <el-dialog
+            :title="dialogType === 'create' ? '添加项目' : '修改项目'"
+            :visible.sync="addProjectVisible"
+            width="570px"
+            :before-close="handleClose"
+        >
+            <el-form :model="form" :rules="rules" ref="ruleForm">
+                <el-form-item label="" prop="localName">
+                    <el-input v-model="form.localName" placeholder="请输入项目名称" autocomplete="off"></el-input>
+                </el-form-item>
+                <el-form-item label="">
+                    <el-upload class="avatar-uploader" accept="image/*" action="#" :show-file-list="false" :http-request="uploadImg">
+                        <img v-if="form.picInfo.key" :src="form.picInfo.key | getImage" class="avatar" />
+                        <i v-else class="el-icon-plus avatar-uploader-icon"></i>
+                    </el-upload>
+                </el-form-item>
+            </el-form>
+            <div slot="footer" class="dialog-footer">
+                <el-button @click="handleClose">取 消</el-button>
+                <el-button type="primary" @click="handleClick">确 定</el-button>
+            </div>
+        </el-dialog>
+    </div>
+</template>
+<script lang="ts">
+import { Vue, Component, Prop, Emit, Watch, Ref } from "vue-property-decorator";
+import tools from "@/utils/maintain";
+import { imageUploadUrl, imageGetUrl } from "@/api/imageservice";
+import { projectCreate, projectUpdate } from "@/api/datacenter";
+import { ElForm } from "element-ui/types/form";
+
+@Component({
+    name: "AddProjectDialog",
+    components: {},
+    filters: {
+        getImage(key: string | undefined) {
+            if (key) {
+                return `${imageGetUrl}${key}`;
+            } else {
+                return "";
+            }
+        },
+    },
+})
+export default class extends Vue {
+    @Prop() private addProjectVisible!: boolean;
+    @Prop() private dialogType!: string;
+    @Prop() private formData!: any;
+
+    @Ref("ruleForm") readonly ruleForm!: ElForm;
+
+    @Emit("update:addProjectVisible")
+    handleClose() {
+        return false;
+    }
+
+    private form: any = {
+        localName: "",
+        picInfo: {},
+    };
+    private rules: any = {
+        localName: [{ required: true, message: "请输入项目名称", trigger: "blur" }],
+    };
+
+    /**
+     * 自定义图片上传
+     */
+    private uploadImg(param: any) {
+        const elFile = param.file;
+        const fileReader = new FileReader();
+        fileReader.readAsDataURL(elFile); //读取图片
+
+        const ftype = elFile.type;
+        const fname = elFile.name;
+        const uid = elFile.uid;
+        const createTime = tools.formatDate(new Date());
+
+        fileReader.addEventListener("load", () => {
+            // 读取完成
+            const res = fileReader.result;
+            //将canvas的base64位图片转换成图片png的file
+            const blob = this.dataURLtoBlob(res, ftype);
+            //将其转换成file对象
+            const file = new File([blob], fname, {
+                type: ftype,
+                lastModified: Date.now(),
+            }); //blob转file
+
+            // try sending
+            const reader = new FileReader();
+            const fileType = file.name.split(".");
+            const imgType = fileType[fileType.length - 1];
+            const uploadKey = `${uid}.${imgType}`;
+            reader.onloadend = () => {
+                // 这个事件在读取结束后,无论成功或者失败都会触发
+                if (reader.error) {
+                    console.error("读取文件失败:", reader.error);
+                } else {
+                    // 构造 XMLHttpRequest 对象,发送文件 Binary 数据
+                    const xhr = new XMLHttpRequest();
+                    xhr.open("POST", `${imageUploadUrl}${uploadKey}`);
+                    xhr.send(reader.result);
+                    xhr.onreadystatechange = () => {
+                        if (xhr.readyState == 4) {
+                            if (xhr.status == 200) {
+                                this.form.picInfo = {
+                                    name: uid,
+                                    key: uploadKey,
+                                    systemId: "dataPlatform",
+                                    type: "image",
+                                    createTime: createTime,
+                                };
+                            }
+                        }
+                    };
+                }
+            };
+            reader.readAsArrayBuffer(file);
+        });
+    }
+
+    private dataURLtoBlob(dataURI: any, type: string) {
+        const binary = atob(dataURI.split(",")[1]);
+        const array = [];
+        for (let i = 0; i < binary.length; i++) {
+            array.push(binary.charCodeAt(i));
+        }
+        return new Blob([new Uint8Array(array)], { type: type });
+    }
+
+    /**
+     * 点击确定(添加/修改项目)
+     */
+    private handleClick() {
+        this.ruleForm.validate(async (valid) => {
+            if (valid) {
+                if (this.dialogType === "create") {
+                    //创建项目
+                    const params: any = {
+                        id: `Pj${new Date().getTime()}`,
+                        localName: this.form.localName,
+                    };
+                    if (this.form.picInfo?.key) {
+                        params.infos = {
+                            projPic: [this.form.picInfo],
+                        };
+                    }
+                    const res = await projectCreate({content:[params]});
+                    if (res.result === "success") {
+                        this.handleClose();
+                        this.$emit("update:projectList");
+                        this.$message.success("创建成功!");
+                    } else {
+                        this.$message.success(`创建失败!失败原因:${res.message}`);
+                    }
+                } else if (this.dialogType === "update" && this.formData?.id) {
+                    //修改项目
+                    const params: any = {
+                        id: this.formData.id,
+                        localName: this.form.localName,
+                    };
+                    if (this.form.picInfo?.key) {
+                        params.infos = {
+                            projPic: [this.form.picInfo],
+                        };
+                    }
+                    const res = await projectUpdate({content:[params]});
+                    if (res.result === "success") {
+                        this.$emit("update:projectList");
+                        this.handleClose();
+                        this.$message.success("修改成功!");
+                    } else {
+                        this.$message.success(`修改失败!失败原因:${res.message}`);
+                    }
+                }
+            } else {
+                return false;
+            }
+        });
+    }
+
+    @Watch("addProjectVisible")
+    onVisibleChanged(val: boolean) {
+        if (val) {
+            this.form.localName = this.formData?.localName ? this.formData.localName : "";
+            this.form.picInfo = this.formData?.infos?.projPic?.length ? this.formData.infos.projPic[0] : {};
+            if (this.formData?.id) this.form.id = this.formData.id;
+        }
+    }
+}
+</script>
+<style lang="scss" scoped>
+::v-deep .el-dialog__body {
+    padding: 30px 120px;
+    .avatar-uploader .el-upload {
+        border: 1px dashed #d9d9d9;
+        border-radius: 6px;
+        cursor: pointer;
+        position: relative;
+        overflow: hidden;
+    }
+    .avatar-uploader .el-upload:hover {
+        border-color: #409eff;
+    }
+    .avatar-uploader-icon {
+        font-size: 28px;
+        color: #8c939d;
+        width: 120px;
+        height: 120px;
+        line-height: 120px;
+        text-align: center;
+    }
+    .avatar {
+        width: 120px;
+        height: 120px;
+        display: block;
+    }
+}
+</style>

+ 68 - 40
src/views/project/index.vue

@@ -1,5 +1,5 @@
 <template>
-    <div class="project-box" style="display: none;">
+    <div class="project-box">
         <el-row style="padding-bottom: 16px">
             <el-input placeholder="请输入关键字" style="width: 240px" clearable @keyup.enter.native="ChangeKeyWord(keyWord)" v-model="keyWord">
                 <i slot="prefix" class="el-input__icon el-icon-search" style="cursor: pointer" @click="ChangeKeyWord(keyWord)"></i>
@@ -10,64 +10,59 @@
             <el-row :gutter="16">
                 <el-col :span="6" v-for="item in projectList" :key="item.id">
                     <el-card shadow="always" class="project-card" :body-style="{ padding: '0px' }">
-                        <el-image style="width: 100%; height: 187px" :src="item.url">
+                        <el-image style="width: 100%; height: 187px" :src="item | getImage">
                             <div slot="error" class="image-slot">
                                 <i class="iconfont icon-wandajituan"></i>
                             </div>
                         </el-image>
                         <div style="padding: 16px">
-                            <span class="project-name" :title="item.name">{{ item.name }}</span>
-                            <span class="iconfont icon-shanchu"></span>
-                            <span class="iconfont icon-bianji"></span>
+                            <span class="project-name" :title="item.localName">{{ item.localName }}</span>
+                            <span class="iconfont icon-shanchu" @click="deleteProject(item)"></span>
+                            <span class="iconfont icon-bianji" @click="updateProject(item)"></span>
                         </div>
                     </el-card>
                 </el-col>
             </el-row>
         </div>
+
+        <!-- 新增项目 -->
+        <add-project-dialog
+            :addProjectVisible.sync="addProjectVisible"
+            @update:projectList="ChangeKeyWord"
+            :dialogType="dialogType"
+            :formData="form"
+        ></add-project-dialog>
     </div>
 </template>
 
 <script lang="ts">
 import { Component, Vue } from "vue-property-decorator";
 import { UserModule } from "../../store/modules/user";
+import { projectQuery } from "@/api/datacenter";
+import { imageGetUrl } from "@/api/imageservice";
+import AddProjectDialog from "./components/AddProjectDialog/index.vue"; //新增项目弹窗
 
 @Component({
     name: "index",
+    components: {
+        AddProjectDialog,
+    },
+    filters: {
+        getImage(item: any) {
+            if (item?.infos?.projPic?.[0]?.key) {
+                return `${imageGetUrl}${item.infos.projPic[0].key}`;
+            } else {
+                return "";
+            }
+        },
+    },
 })
 export default class extends Vue {
     private keyWord = "";
-    private projectList: any = [
-        {
-            id: "1",
-            name: "龙岗万达龙岗万达龙岗万达龙岗万达龙岗万达龙岗万达龙岗万达龙岗万达龙岗万达龙岗万达龙岗万达龙岗万达",
-            logo: "",
-        },
-        {
-            id: "2",
-            name: "龙岗万达",
-            logo: "",
-        },
-        {
-            id: "3",
-            name: "龙岗万达",
-            logo: "",
-        },
-        {
-            id: "4",
-            name: "龙岗万达",
-            logo: "",
-        },
-        {
-            id: "5",
-            name: "龙岗万达",
-            logo: "",
-        },
-        {
-            id: "6",
-            name: "龙岗万达",
-            logo: "",
-        },
-    ];
+    private projectList: any = [];
+    private addProjectVisible = false;
+    private dialogType = "create";
+    private form: any = {};
 
     get projectId() {
         return UserModule.projectId;
@@ -77,15 +72,48 @@ export default class extends Vue {
      * 检索项目
      * @param 检索关键字
      */
-    private ChangeKeyWord(keyWord: string) {
-        console.log(keyWord);
+    private async ChangeKeyWord(keyWord?: string) {
+        const params: any = {
+            pageNumber: 1,
+            pageSize: 2000,
+        };
+        if (keyWord) params.filters = `localName contain '${keyWord}'`;
+        const res = await projectQuery(params);
+        if (res.result === "success" && res.content.length) {
+            this.projectList = res.content;
+        } else {
+            this.projectList = [];
+        }
     }
 
     /**
      * 添加项目
      */
     private addProject() {
-        console.log("添加项目");
+        this.dialogType = "create";
+        this.form = {};
+        this.addProjectVisible = true;
+    }
+
+    /**
+     * 修改项目
+     */
+    private updateProject(item: any) {
+        this.dialogType = "update";
+        this.form = item;
+        this.addProjectVisible = true;
+    }
+
+    /**
+     * 删除项目
+     */
+    private deleteProject(item: any) {
+        this.$message.info("删除接口暂不提供!");
+        console.log("删除项目", item.id);
+    }
+
+    created() {
+        this.ChangeKeyWord();
     }
 }
 </script>