diff --git a/base-manager-ui/admin/src/pages/basicset/appmarket/AppMarket.vue b/base-manager-ui/admin/src/pages/basicset/appmarket/AppMarket.vue
index 1efb6a833d93a0d2ee742df5d0e36b60401f325c..54aba9bd713a666a3323a33dcaa527162d578d9f 100644
--- a/base-manager-ui/admin/src/pages/basicset/appmarket/AppMarket.vue
+++ b/base-manager-ui/admin/src/pages/basicset/appmarket/AppMarket.vue
@@ -9,12 +9,9 @@
         @click="handleUpload"
         >涓婁紶閰嶇疆鏂囦欢</a-button
       >
-      <a-tab-pane key="/appmarket/terminalapp" tab="缁堢搴旂敤">
-        <!-- <TerminalApp></TerminalApp> -->
-      </a-tab-pane>
-      <a-tab-pane key="/appmarket/moveapp" tab="绉诲姩绔簲鐢�" force-render>
-        <!-- <MoveApp></MoveApp> -->
-      </a-tab-pane>
+      <a-tab-pane key="/appmarket/terminalapp" tab="缁堢搴旂敤"> </a-tab-pane>
+      <a-tab-pane key="/appmarket/moveapp" tab="绉诲姩绔簲鐢�"> </a-tab-pane>
+      <a-tab-pane key="/appmarket/blackapp" tab="搴旂敤榛戝悕鍗�"> </a-tab-pane>
     </a-tabs>
     <div class="app-out-box flex1">
       <router-view></router-view>
diff --git a/base-manager-ui/admin/src/pages/basicset/appmarket/components/BlackApp.vue b/base-manager-ui/admin/src/pages/basicset/appmarket/components/BlackApp.vue
new file mode 100644
index 0000000000000000000000000000000000000000..ca08c09a633df65cc03ae6cdd5d346744273862a
--- /dev/null
+++ b/base-manager-ui/admin/src/pages/basicset/appmarket/components/BlackApp.vue
@@ -0,0 +1,483 @@
+<template>
+  <div class="black-app">
+    <div class="left">
+      <div class="header">
+        <h3 class="titel">璁惧搴旂敤榛戝悕鍗�</h3>
+        <div class="control">
+          <a-button type="danger" @click="handleDelAll"> 鎵归噺绉婚櫎 </a-button>
+          <div class="business-control">
+            <a-space>
+              <!-- <a-select
+                style="min-width: 120px"
+                v-model="leftHallSearch"
+                showSearch
+                optionFilterProp="label"
+              >
+                <a-select-option value="" label="鍏ㄩ儴搴旂敤"> </a-select-option>
+                <a-select-option
+                  v-for="v in appList"
+                  :key="v.id"
+                  :value="v.id"
+                  :label="v.name"
+                >
+                  {{ v.name }}
+                </a-select-option>
+              </a-select> -->
+              <a-input-search
+                placeholder="璇疯緭鍏ュ簲鐢ㄥ悕绉版悳绱�"
+                enter-button="鎼滅储"
+                v-model="leftSearch.name"
+                @search="onSearchLeft"
+                allowClear
+              />
+            </a-space>
+          </div>
+        </div>
+      </div>
+      <div class="table-content">
+        <!-- 琛ㄦ牸 -->
+        <a-table
+          bordered
+          :loading="leftLoading"
+          size="middle"
+          :scroll="{ y: 550 }"
+          :pagination="{
+            showTotal: (total) => `鍏� ${total} 鏉,
+            current: leftCurrent,
+            total: leftTotal,
+            pageSize: leftSize,
+            showSizeChanger: true,
+            showQuickJumper: true,
+            pageSizeOptions: pageSizeOptions,
+            onChange: changeLeft,
+            onShowSizeChange: showSizeChange,
+          }"
+          :columns="leftColumns"
+          :data-source="blackApp"
+          :row-selection="{
+            selectedRowKeys: selectedLeftRowKeys,
+            onChange: onSelectChange,
+          }"
+          :rowKey="(record) => record.id"
+        >
+          <template slot="num" slot-scope="text, record, index">
+            <span>
+              {{ (leftCurrent - 1) * leftSize + index + 1 }}
+            </span>
+          </template>
+          <template slot="action" slot-scope="text">
+            <a href="javascript:;" class="delete" @click="handleDel(text.id)"
+              >绉婚櫎</a
+            >
+          </template>
+        </a-table>
+      </div>
+    </div>
+    <!-- 鍙� -->
+    <div class="right">
+      <div class="header">
+        <h3 class="titel">绔欑偣璁惧</h3>
+        <div class="control">
+          <div>
+            <!-- <a-button type="primary" @click="handleAddAll"> 鎵归噺鍔犲叆 </a-button> -->
+          </div>
+          <div class="business-control">
+            <a-space>
+              <a-select
+                style="min-width: 120px"
+                v-model="rightSearch.type"
+                showSearch
+                optionFilterProp="label"
+              >
+                <a-select-option value="" label="鍏ㄩ儴绫诲瀷"
+                  >鍏ㄩ儴绫诲瀷
+                </a-select-option>
+                <a-select-option
+                  v-for="(v, i) in devType"
+                  :key="i"
+                  :value="v"
+                  :label="v"
+                >
+                  {{ v }}
+                </a-select-option>
+              </a-select>
+              <a-input-search
+                placeholder="璇疯緭鍏ヨ澶囩紪鐮佹悳绱�"
+                enter-button="鎼滅储"
+                v-model="rightSearch.deviceCode"
+                @search="onSearch"
+                allowClear
+              />
+            </a-space>
+          </div>
+        </div>
+      </div>
+      <div class="table-content">
+        <!-- 琛ㄦ牸 -->
+        <a-table
+          bordered
+          :scroll="{ y: 550 }"
+          :loading="rightLoading"
+          :pagination="{
+            showTotal: (total) => `鍏� ${total} 鏉,
+            current: rightCurrent,
+            total: rightTotal,
+            pageSize: rightSize,
+            showSizeChanger: true,
+            showQuickJumper: true,
+            pageSizeOptions: pageSizeOptions,
+            onChange: changeRight,
+            onShowSizeChange: showSizeChangeRight,
+          }"
+          size="middle"
+          :columns="rightColumns"
+          :data-source="deviceList"
+          :rowKey="(record) => record.id"
+        >
+          <template slot="num" slot-scope="text, record, index">
+            <span>
+              {{ (rightCurrent - 1) * rightSize + index + 1 }}
+            </span>
+          </template>
+          <template slot="deviceStatus" slot-scope="text">
+            <a-tag color="blue" v-if="text.deviceStatus == 0"> 鏈縺娲� </a-tag>
+            <a-tag color="red" v-else-if="text.deviceStatus == 1"> 绂荤嚎 </a-tag>
+            <a-tag color="green" v-else-if="text.deviceStatus == 2">
+              鍦ㄧ嚎
+            </a-tag>
+          </template>
+
+          <template slot="action" slot-scope="text">
+            <a-space size="middle">
+              <a class="jion" @click="handleIn(text)">鍔犲叆榛戝悕鍗�</a>
+            </a-space>
+          </template>
+        </a-table>
+      </div>
+    </div>
+    <!-- 娣诲姞榛戝悕鍗� -->
+    <DevToBlack
+      :addVisile.sync="visible"
+      ref="DevToBlack"
+      @addSuccess="addSuccess"
+    ></DevToBlack>
+  </div>
+</template>
+  
+  <script>
+import {
+  getDeviceList,
+  getBlackAppList,
+  deleteBlackapp,
+} from "@/services/market";
+import local from "@/utils/local";
+import DevToBlack from "../modal/DevToBlack.vue";
+const devType = [
+  "鎺掗槦鏈�",
+  "绐楀彛灞�",
+  "鍛煎彨鍣�",
+  "闆嗕腑鏄剧ず灞�",
+  "璇勪环鍣�",
+  "淇℃伅鍙戝竷灞�",
+  "瀵艰鏈�",
+  "鑷姪鏈嶅姟缁堢",
+  "濉崟鏈�",
+  "涓€鐮侀€�",
+  "LED閫氬睆",
+  "鍙栦欢鏌�",
+  "鑳岄潬鑳岃瘎浠疯澶�",
+  "瀛樺彇浠�",
+  "妗岄潰寮忚嚜鍔╂湇鍔$粓绔�",
+];
+
+export default {
+  components: { DevToBlack },
+  data() {
+    const leftColumns = [
+      {
+        title: "搴忓彿",
+        width: "50px",
+        scopedSlots: { customRender: "num" },
+      },
+      {
+        title: "搴旂敤鍚嶇О",
+        dataIndex: "appName",
+      },
+      {
+        title: "璁惧鍚嶇О",
+        customRender: (text) => {
+          return text.deviceName || "--";
+        },
+      },
+      {
+        title: "璁惧缂栫爜",
+        dataIndex: "deviceCode",
+      },
+      {
+        title: "鎿嶄綔",
+        width: "110px",
+        scopedSlots: {
+          customRender: "action",
+        },
+      },
+    ];
+    const rightColumns = [
+      {
+        title: "搴忓彿",
+        key: "id",
+        width: "50px",
+        scopedSlots: { customRender: "num" },
+      },
+      {
+        title: "璁惧鍚嶇О",
+        customRender: (text) => {
+          return text.deviceName || "--";
+        },
+      },
+      {
+        title: "璁惧缂栫爜",
+        dataIndex: "deviceCode",
+      },
+      {
+        title: "璁惧绫诲瀷",
+        dataIndex: "productName",
+      },
+      {
+        title: "璁惧鐘舵€�",
+        width: "10%",
+        scopedSlots: { customRender: "deviceStatus" },
+      },
+
+      {
+        title: "鎿嶄綔",
+        width: "110px",
+        scopedSlots: {
+          customRender: "action",
+        },
+      },
+    ];
+    return {
+      devType,
+      leftColumns,
+      rightColumns,
+      leftLoading: false,
+      rightLoading: false,
+      leftSearch: {
+        name: "",
+      },
+      rightSearch: {
+        deviceCode: "",
+        type: "",
+      },
+      selectedLeftRowKeys: [],
+      selectedRowKeys: [],
+      selectedRows: [],
+      visible: false,
+      leftCurrent: 1,
+      rightCurrent: 1,
+      leftTotal: 0,
+      rightTotal: 0,
+      leftSize: 10,
+      rightSize: 10,
+      pageSizeOptions: ["10", "30", "50", "100"],
+      siteId: local.getLocal("siteId"), // 绔欑偣id
+      deviceList: [], // 绔欑偣璁惧鍒楄〃
+      blackApp: [], // 搴旂敤榛戝悕鍗曞垪琛�
+    };
+  },
+  created() {
+    this.getBlackAppList();
+    this.getDeviceList();
+  },
+  methods: {
+    // 鑾峰彇璁惧榛戝悕鍗�
+    async getBlackAppList() {
+      this.leftLoading = true;
+      let res = await getBlackAppList({
+        page: this.leftCurrent,
+        size: this.leftSize,
+        appName: `%${this.leftSearch.name}%`,
+        siteId: this.siteId,
+      });
+      if (res.data.code == 1) {
+        let { data, total } = res.data.data;
+        if (!data.length && this.leftCurrent > 1) {
+          this.leftCurrent -= 1;
+          this.getBlackAppList();
+        }
+        this.blackApp = data;
+        this.leftTotal = total;
+      }
+      this.leftLoading = false;
+    },
+    // 鑾峰彇绔欑偣璁惧鍒楄〃
+    async getDeviceList() {
+      this.rightLoading = true;
+      let res = await getDeviceList({
+        page: this.rightCurrent,
+        size: this.rightSize,
+        siteId: this.siteId,
+        deviceCode: `%${this.rightSearch.deviceCode}%`,
+        productName: `%${this.rightSearch.type}%`,
+      });
+      if (res.data.code == 1) {
+        let { data, total } = res.data.data;
+        this.deviceList = data;
+        this.rightTotal = total;
+        console.log(data);
+      }
+      this.rightLoading = false;
+    },
+    // 宸﹁竟鎼滅储
+    onSearchLeft() {
+      this.leftCurrent = 1;
+      this.selectedLeftRowKeys = [];
+      this.getBlackAppList();
+    },
+    // 鍒犻櫎
+    handleDel(id) {
+      let _this = this;
+      this.$confirm({
+        title: "绯荤粺鎻愮ず",
+        content: "纭畾瑕佺Щ闄ゆ墍閫夋暟鎹悧锛�",
+        okText: "纭畾",
+        okType: "danger",
+        cancelText: "鍙栨秷",
+        centered: true,
+        icon: "exclamation-circle",
+        maskClosable: true,
+        async onOk() {
+          let res = await deleteBlackapp({ id });
+          let { code, msg } = res.data;
+          if (code == 1) {
+            _this.$message.success(msg);
+            _this.selectedLeftRowKeys = [];
+            _this.getBlackAppList();
+          }
+        },
+        onCancel() {
+          console.log("Cancel");
+        },
+      });
+    },
+    // 宸﹁竟閫変腑
+    onSelectChange(key) {
+      this.selectedLeftRowKeys = key;
+    },
+    // 鎵归噺鍒犻櫎
+    handleDelAll() {
+      if (!this.selectedLeftRowKeys.length) {
+        this.$message.warning("璇峰厛鍕鹃€夋暟鎹�");
+        return;
+      }
+      let ids = this.selectedLeftRowKeys.join(",");
+      this.handleDel(ids);
+    },
+    // 鑾峰彇鎵归噺鍔犲叆id
+    // onRightSelectChange(keys, rows) {
+    //   this.selectedRowKeys = keys;
+    //   const res = new Map();
+    //   this.selectedRows = [...this.selectedRows, ...rows]
+    //     .filter((v) => {
+    //       return !res.has(v.id) && res.set(v.id, 1);
+    //     })
+    //     .filter((v) => {
+    //       return this.selectedRowKeys.some((val) => v.id == val);
+    //     });
+    // },
+
+    // 鎵归噺鍔犲叆
+    // handleAddAll() {
+    //   if (!this.selectedRows.length) {
+    //     this.$message.warning("璇峰厛鍕鹃€夋暟鎹�");
+    //     return;
+    //   }
+    //   this.handleIn(this.selectedRows);
+    // },
+
+    // 宸︾炕椤�
+    changeLeft(cur) {
+      this.leftCurrent = cur;
+      this.getBlackAppList();
+    },
+    // 宸﹁竟鏀瑰彉姣忛〉鏄剧ず鏁伴噺
+    showSizeChange(current, size) {
+      this.leftCurrent = current;
+      this.leftSize = size;
+      this.getBlackAppList();
+    },
+    // 鍙崇炕椤�
+    changeRight(cur) {
+      this.rightCurrent = cur;
+      this.getDeviceList();
+    },
+    // 鍙宠竟鏀瑰彉鏄剧ず鏁伴噺
+    showSizeChangeRight(current, size) {
+      this.rightCurrent = current;
+      this.rightSize = size;
+      this.getDeviceList();
+    },
+    // 鎼滅储
+    async onSearch() {
+      this.rightCurrent = 1;
+      this.getDeviceList();
+    },
+    // 鍔犲叆
+    handleIn(row) {
+      this.$refs.DevToBlack.onAdd(row);
+      this.visible = true;
+    },
+    // 鍔犲叆鎴愬姛
+    addSuccess() {
+      this.selectedRowKeys = [];
+      this.selectedRows = [];
+      this.getBlackAppList();
+      //   this.getDeviceList();
+    },
+  },
+};
+</script>
+  
+  <style lang="less" scoped>
+.black-app {
+  width: 100%;
+  height: 100%;
+  display: flex;
+  position: relative;
+  &::after {
+    content: "";
+    width: 1px;
+    height: 100%;
+    position: absolute;
+    background-color: #eeeeee;
+    top: 0px;
+    left: 50%;
+  }
+  .left,
+  .right {
+    width: 50%;
+    .header {
+      height: 100px;
+      .titel {
+        margin-bottom: 15px;
+      }
+    }
+  }
+  .left {
+    padding-right: 15px;
+  }
+  .right {
+    padding-left: 15px;
+  }
+  .control {
+    width: 100%;
+    display: flex;
+    justify-content: space-between;
+  }
+}
+
+.add-btn {
+  background-color: #04cb8f;
+  color: #fff;
+}
+</style>
\ No newline at end of file
diff --git a/base-manager-ui/admin/src/pages/basicset/appmarket/modal/DevToBlack.vue b/base-manager-ui/admin/src/pages/basicset/appmarket/modal/DevToBlack.vue
new file mode 100644
index 0000000000000000000000000000000000000000..00a3951b243362d115f48c966bbd1ddaba2c7e3f
--- /dev/null
+++ b/base-manager-ui/admin/src/pages/basicset/appmarket/modal/DevToBlack.vue
@@ -0,0 +1,138 @@
+<template>
+  <div>
+    <a-modal
+      v-model="Visible"
+      :maskClosable="false"
+      title="鏂板搴旂敤榛戝悕鍗�"
+      @cancel="handleClose"
+      destroyOnClose
+      centered
+    >
+      <template slot="footer">
+        <a-button @click="handleReset">閲嶇疆</a-button>
+        <a-button type="primary" @click="handleOk">纭畾</a-button>
+      </template>
+      <a-form-model
+        ref="form"
+        :model="form"
+        :rules="rules"
+        :label-col="{ span: 4 }"
+        :wrapper-col="{ span: 20 }"
+      >
+        <a-form-model-item label="搴旂敤" prop="appId">
+          <a-select
+            @change="handleChange"
+            labelInValue
+            placeholder="璇烽€夋嫨搴旂敤"
+            v-model="selectInfo"
+          >
+            <a-select-option v-for="v in appList" :key="v.id" :value="v.id">
+              {{ v.appName }}
+            </a-select-option>
+          </a-select>
+        </a-form-model-item>
+      </a-form-model>
+    </a-modal>
+  </div>
+</template>
+      
+<script>
+import { saveBlackApp, getAppList } from "@/services/market";
+export default {
+  props: {
+    addVisile: {
+      type: Boolean,
+      require: true,
+      default: false,
+    },
+  },
+  components: {},
+  data() {
+    return {
+      selectInfo: undefined,
+      form: {
+        siteId: "", // 绔欑偣id
+        deviceId: "", // 璁惧id
+        deviceCode: "", // 璁惧缂栫爜
+        deviceName: "", // 璁惧鍚嶇О
+        appId: "", // 搴旂敤id
+        appName: "", // 搴旂敤鍚嶇О
+        appCode: "", // 搴旂敤缂栫爜
+      },
+      appList: [], // app鍒楄〃
+      devList: [], // 璁惧鍒楄〃
+      rules: {
+        appId: [{ required: true, message: "璇烽€夋嫨搴旂敤", trigger: "change" }],
+      },
+    };
+  },
+  computed: {
+    Visible: {
+      get() {
+        return this.addVisile;
+      },
+      set(val) {
+        this.$emit("update:addVisile", val);
+      },
+    },
+  },
+  created() {
+    this.getAppList();
+  },
+  methods: {
+    // 鑾峰彇搴旂敤鍒楄〃
+    async getAppList() {
+      let res = await getAppList({
+        page: 1,
+        size: -1,
+        type: 1,
+      });
+      if (res.data.code == 1) {
+        let { data } = res.data.data;
+        this.appList = data;
+      }
+    },
+    // 鍒囨崲閫夋嫨
+    handleChange(row) {
+      this.form.appId = row.key;
+      this.form.appName = row.label;
+    },
+    // 鏂板
+    onAdd(row) {
+      this.form.siteId = row.siteId;
+      this.form.deviceId = row.id;
+      this.form.deviceCode = row.deviceCode;
+      this.form.deviceName = row.deviceName;
+    },
+
+    // 鍏抽棴寮圭獥
+    handleClose() {
+      this.selectInfo = undefined;
+      this.$refs.form.resetFields();
+      this.Visible = false;
+    },
+    // 淇濆瓨
+    handleOk() {
+      this.$refs.form.validate(async (valid) => {
+        if (valid) {
+          let res = await saveBlackApp(this.form);
+          let { code, msg } = res.data;
+          if (code == 1) {
+            this.$message.success(msg);
+            this.$emit("addSuccess");
+            this.handleClose();
+          }
+        }
+      });
+    },
+    // 閲嶇疆
+    handleReset() {
+      this.selectInfo = undefined;
+      this.$refs.form.resetFields();
+    },
+  },
+};
+</script>
+      
+  <style lang="less" scoped>
+</style>
\ No newline at end of file
diff --git a/base-manager-ui/admin/src/router/config.js b/base-manager-ui/admin/src/router/config.js
index f581aa02cac75306abfb6bde4665a86e11eede77..83270c96822c438f80bac626654278dea9b216e4 100644
--- a/base-manager-ui/admin/src/router/config.js
+++ b/base-manager-ui/admin/src/router/config.js
@@ -256,6 +256,14 @@ const options = {
                     // keepAlive: true,
                   },
                 },
+                {
+                  path: "blackapp",
+                  component: () =>
+                    import("@/pages/basicset/appmarket/components/BlackApp"),
+                  meta: {
+                    invisible: true,
+                  },
+                },
               ],
             },
             {
diff --git a/base-manager-ui/admin/src/services/basicsetApi.js b/base-manager-ui/admin/src/services/basicsetApi.js
index 1c2cd4dc388dd2e29851b65a49a1f26bc6d2d581..8d3a6c7a778db4286b23406d2f14290371871a45 100644
--- a/base-manager-ui/admin/src/services/basicsetApi.js
+++ b/base-manager-ui/admin/src/services/basicsetApi.js
@@ -355,4 +355,14 @@ module.exports = {
     batchSave: `${BASE_URL}/base/window/hall/batchSave`,
     delete: `${BASE_URL}/base/window/hall/delete`,
   },
+  // 璁惧
+  device: {
+    list: `${BASE_URL}/base/device/list`,
+  },
+  // 搴旂敤榛戝悕鍗�
+  blackapp: {
+    list: `${BASE_URL}/base/device/blackapp/list`,
+    save: `${BASE_URL}/base/device/blackapp/save`,
+    delete: `${BASE_URL}/device/blackapp/delete`,
+  },
 };
diff --git a/base-manager-ui/admin/src/services/market.js b/base-manager-ui/admin/src/services/market.js
index 2ec19886cacd669cb92d5f0154b462f70205069c..177015373c09acdf56dc99743bc1f01897f97d88 100644
--- a/base-manager-ui/admin/src/services/market.js
+++ b/base-manager-ui/admin/src/services/market.js
@@ -4,6 +4,8 @@ import {
   appField,
   templete,
   version,
+  device,
+  blackapp,
 } from "@/services/basicsetApi";
 
 import { request, METHOD } from "@/utils/request";
@@ -136,3 +138,29 @@ export async function usedVersion(data) {
 export async function previewVersion(data) {
   return request(version.preview, METHOD.GET, data);
 }
+
+/**
+ *  璁惧鍒楄〃
+ */
+export async function getDeviceList(data) {
+  return request(device.list, METHOD.POST, data);
+}
+
+/**
+ *  璁惧搴旂敤榛戝悕鍗�
+ */
+
+// 鑾峰彇搴旂敤榛戝悕鍗曞垪琛�
+export async function getBlackAppList(data) {
+  return request(blackapp.list, METHOD.POST, data);
+}
+
+// 淇濆瓨
+export async function saveBlackApp(data) {
+  return request(blackapp.save, METHOD.POST, data);
+}
+
+// 鍒犻櫎
+export async function deleteBlackapp(data) {
+  return request(blackapp.delete, METHOD.GET, data);
+}