Forráskód Böngészése

添加批量设置参数
添加复制参数

DESKTOP-NE65RNK\Citrus_limon 1 éve
szülő
commit
0a1b489d7a

+ 2 - 2
.env.development

@@ -1,2 +1,2 @@
-# VITE_API_BASE_URL = "http://cc.skyfffire.com/api"
-VITE_API_BASE_URL = "http://192.168.1.2:81"
+VITE_API_BASE_URL = "http://cc.skyfffire.com/api"
+# VITE_API_BASE_URL = "http://192.168.1.2:81"

+ 7 - 0
src/api/index.ts

@@ -107,6 +107,13 @@ export const update_robot = (params: any, callback: any) => {
     if (data) callback && callback(data);
   });
 };
+// 机器人管理-修改机器人
+export const update_robot_batch = (params: any, callback: any) => {
+  return http.request("/robot/updateBatch", "post", params).then((data) => {
+    if (data) callback && callback(data);
+  });
+};
+
 // 机器人管理-删除机器人
 export const delete_robot = (params: any, callback: any) => {
   return http.request("/robot/delete", "post", params).then((data) => {

+ 1 - 1
src/utils/request.ts

@@ -17,7 +17,7 @@ const checkStatus = (response: any) => {
         window.sessionStorage.removeItem("_4L_MENU_COLLAPSE");
         router.push({ path: "/login" });
       }
-      if (response.data.code == 500) {
+      if (response.data.code == 700) {
         layer.msg(`${response.data.msg}`, { time: 0, icon: 2, closeBtn: true });
       } else {
         layer.msg(`${response.data.msg}`, { time: 1000, icon: 2 });

+ 240 - 0
src/views/bot/manage/components/BatchUpdate.vue

@@ -0,0 +1,240 @@
+<template>
+  <lay-layer :title="modelConfig.title" v-model="modelConfig.visible" area="auto" :btn="operator">
+    <div class="width-800 custom-layer" style="padding: 20px">
+      <lay-form :model="modelParams" ref="modelFormRef">
+        <lay-row space="10">
+          <lay-col md="12" sm="12" xs="24">
+            <lay-form-item label="名称" prop="name">
+              <lay-input v-model="modelParams.name" />
+            </lay-form-item>
+          </lay-col>
+          <lay-col md="12" sm="12" xs="24">
+            <lay-form-item label="初始金额" prop="startAmount">
+              <lay-input v-model="modelParams.startAmount" />
+            </lay-form-item>
+          </lay-col>
+          <lay-col md="12" sm="12" xs="24">
+            <lay-form-item label="服务器" prop="serverId">
+              <lay-select v-model="modelParams.serverId" :show-search="true">
+                <lay-select-option v-for="item in serverList" :value="item.id" :label="item.value" />
+              </lay-select>
+            </lay-form-item>
+          </lay-col>
+          <lay-col md="12" sm="12" xs="24">
+            <lay-form-item label="通讯端口" prop="callPort">
+              <lay-input v-model="modelParams.callPort" />
+            </lay-form-item>
+          </lay-col>
+          <lay-col md="12" sm="12" xs="24">
+            <lay-form-item label="策略" prop="strategyId">
+              <lay-select v-model="modelParams.strategyId" @change="strategyHandleChange">
+                <lay-select-option v-for="item in strategyList" :value="item.id" :label="item.value" />
+              </lay-select>
+            </lay-form-item>
+          </lay-col>
+          <lay-col md="12" sm="12" xs="24">
+            <lay-form-item label="策略版本" prop="strategyProgramId">
+              <lay-select v-model="modelParams.strategyProgramId" @change="strategyProgramHandleChange">
+                <lay-select-option v-for="item in strategyProgramList" :value="item.id" :label="item.value" />
+              </lay-select>
+            </lay-form-item>
+          </lay-col>
+          <div>
+            <template v-for="item in strategyParameterList">
+              <lay-col md="12" sm="12" xs="24">
+                <lay-form-item :label="item.name" :prop="`robotConfigs.${item.code}`">
+                  <lay-input v-if="item.valType == 0" v-model="modelParams.robotConfigs[item.code]" />
+                  <lay-input type="number" v-if="item.valType == 1" v-model="modelParams.robotConfigs[item.code]" />
+                  <lay-switch v-if="item.valType == 2" v-model="modelParams.robotConfigs[item.code]" />
+                  <lay-select v-if="item.valType == 3" v-model="modelParams.robotConfigs[item.code]" :show-search="true">
+                    <lay-select-option v-for="items in item.contentVal.split(',')" :value="items" :label="items" />
+                  </lay-select>
+                  <lay-select v-if="item.valType == 5" v-model="modelParams.robotConfigs[item.code]" :options="apikeyList" :show-search="true" />
+                </lay-form-item>
+                <lay-form-item v-if="item.code == 'open'" label="开仓递增" :prop="`robotConfigs.openIntervals`">
+                  <lay-input v-model="modelParams.robotConfigs.openIntervals" />
+                </lay-form-item>
+                <lay-form-item v-if="item.code == 'close'" label="平仓递增" :prop="`robotConfigs.closeIntervals`">
+                  <lay-input v-model="modelParams.robotConfigs.closeIntervals" />
+                </lay-form-item>
+              </lay-col>
+            </template>
+          </div>
+        </lay-row>
+      </lay-form>
+    </div>
+  </lay-layer>
+</template>
+
+<script lang="ts" setup>
+import { ref, reactive, getCurrentInstance } from "vue";
+import { update_robot_batch, get_server_select, get_strategy_select, get_strategy_program_select, get_strategy_parameter_select, get_apikey_select } from "@/api";
+
+const { proxy }: any = getCurrentInstance();
+
+const modelFormRef = ref();
+
+interface ModelConfig {
+  title: string;
+  visible: boolean;
+  isUpdate: boolean;
+  loading: boolean;
+}
+interface ModelParams {
+  id?: number;
+  name?: string;
+  serverId?: number;
+  strategyId?: number;
+  strategyProgramId?: number;
+  startAmount?: number;
+  callPort?: number;
+  remark?: string;
+  robotConfigs?: any;
+}
+
+let modelParams = ref<ModelParams>({});
+let modelConfig: ModelConfig = reactive({ title: "", visible: false, isUpdate: false, loading: false });
+
+let handleResult = reactive<{ resolve?: any; reject?: any }>({});
+
+let serverList = ref();
+let strategyList = ref();
+let strategyProgramList = ref();
+let strategyParameterList = ref();
+let apikeyList = ref();
+
+let botDetailList = ref();
+
+const show = async (params?: any) => {
+  initData();
+  get_server_list();
+  get_strategy_list();
+  get_apikey();
+  modelConfig.visible = true;
+  modelConfig.title = "批量设置机器人";
+
+  modelParams.value = { strategyId: params[0].strategyId, strategyProgramId: params[0].strategyProgramId, robotConfigs: {} };
+  botDetailList.value = params;
+  strategyHandleChange(modelParams.value.strategyId);
+  strategyProgramHandleChange(modelParams.value.strategyProgramId);
+  return new Promise(async (resolve, reject) => {
+    handleResult.resolve = resolve;
+    handleResult.reject = reject;
+  });
+};
+// 获取服务器列表
+const get_server_list = () => {
+  const params = {};
+  get_server_select(params, (data: any) => {
+    serverList.value = data.data;
+  });
+};
+// 获取策略列表
+const get_strategy_list = () => {
+  const params = {};
+  get_strategy_select(params, (data: any) => {
+    strategyList.value = data.data;
+  });
+};
+// 获取策略版本列表
+const get_strategy_program_list = (value: number) => {
+  const params = { strategyId: value };
+  get_strategy_program_select(params, (data: any) => {
+    strategyProgramList.value = data.data;
+  });
+};
+// 获取策略版本参数下拉
+const get_strategy_parameter = (value: number) => {
+  const params = { programId: value };
+  get_strategy_parameter_select(params, (data: any) => {
+    strategyParameterList.value = data.data;
+  });
+};
+// 获取APIKEY参数下拉
+const get_apikey = () => {
+  const params = {};
+  get_apikey_select(params, (data: any) => {
+    apikeyList.value = data.data.map((item: any) => ({ label: item.value, value: item.id }));
+  });
+};
+
+// 选择策略操作
+const strategyHandleChange = (e: any) => {
+  get_strategy_program_list(e);
+};
+// 选择策略版本操作
+const strategyProgramHandleChange = (e: any) => {
+  get_strategy_parameter(e);
+};
+
+const handleParams = (value: any) => {
+  let openIntervals = 0;
+  let closeIntervals = 0;
+  return botDetailList.value.map((item: any) => {
+    let configs = item.configList.map((item: any) => {
+      delete item.deleted;
+      delete item.id;
+      delete item.robotId;
+      let result = { ...item, val: value.robotConfigs[item.code] || item.val };
+      if (result.code == "open") {
+        result.val = result.val * 1 + openIntervals * 1;
+        openIntervals += value.robotConfigs.openIntervals * 1;
+      }
+      if (result.code == "close") {
+        result.val = result.val * 1 + closeIntervals * 1;
+        closeIntervals += value.robotConfigs.closeIntervals * 1;
+      }
+      return result;
+    });
+    return {
+      id: item.id,
+      name: value.name || item.name,
+      serverId: value.serverId || item.serverId,
+      strategyId: value.strategyId || item.strategyId,
+      strategyProgramId: value.strategyProgramId || item.strategyProgramId,
+      callPort: value.callPort || item.callPort,
+      startAmount: value.startAmount || item.startAmount,
+      remark: item.remark,
+      robotConfigs: configs,
+    };
+  });
+};
+
+const initData = () => {
+  serverList.value = [];
+  strategyList.value = [];
+  strategyProgramList.value = [];
+  strategyParameterList.value = [];
+  apikeyList.value = [];
+};
+
+const operator = reactive([
+  {
+    text: "确认",
+    callback: () => {
+      modelFormRef.value.validate((isValidate: boolean) => {
+        if (isValidate) {
+          const params = handleParams(modelParams.value);
+          modelConfig.loading = true;
+          update_robot_batch(params, (data: any) => {
+            modelConfig.loading = false;
+            if (data.code == 200) {
+              proxy.$message("批量修改成功!");
+              modelConfig.visible = false;
+              handleResult.resolve(true);
+            }
+          });
+        }
+      });
+    },
+  },
+  {
+    text: "取消",
+    callback: () => {
+      modelConfig.visible = false;
+      handleResult.resolve(false);
+    },
+  },
+]);
+defineExpose({ show });
+</script>

+ 2 - 13
src/views/bot/manage/components/Update.vue

@@ -62,7 +62,7 @@
 
 <script lang="ts" setup>
 import { ref, reactive, getCurrentInstance } from "vue";
-import { add_robot, update_robot, get_server_select, get_strategy_select, get_strategy_program_select, get_strategy_parameter_select, get_apikey_select, get_robot_find } from "@/api";
+import { add_robot, update_robot, get_server_select, get_strategy_select, get_strategy_program_select, get_strategy_parameter_select, get_apikey_select } from "@/api";
 
 const { proxy }: any = getCurrentInstance();
 
@@ -105,11 +105,9 @@ const show = async (params?: any) => {
   modelConfig.visible = true;
   modelConfig.isUpdate = !!params;
   modelConfig.title = !!params ? "编辑机器人" : "添加机器人";
-
   if (modelConfig.isUpdate) {
-    let detail: any = await get_robot_detail(params.id);
     let robotConfigs: any = {};
-    detail.robotConfigs.map((item: any) => {
+    params.configList.map((item: any) => {
       if (item.valType == 2) {
         robotConfigs[item.code] = item.val == "true" ? true : false;
       } else if (item.valType == 5) {
@@ -130,15 +128,6 @@ const show = async (params?: any) => {
     handleResult.reject = reject;
   });
 };
-// 获取机器人详情
-const get_robot_detail = (value: any) => {
-  return new Promise((resolve) => {
-    const params = { id: value };
-    get_robot_find(params, (data: any) => {
-      resolve(data.data);
-    });
-  });
-};
 // 获取服务器列表
 const get_server_list = () => {
   const params = {};

+ 79 - 7
src/views/bot/manage/index.vue

@@ -21,15 +21,21 @@
         </lay-form>
       </div>
       <div class="custom-operator-wp">
-        <lay-space>
-          <div v-html="collectInfo" class="collect-wp" />
-          <div>
+        <div class="custom-group collect-wp" v-html="collectInfo" />
+        <div class="custom-group">
+          <lay-space>
             <lay-button :border="'green'" size="xs" @click="handleStatus(selectedKeys, 'RUN')">开机</lay-button>
             <lay-button :border="'green'" size="xs" @click="handleStatus(selectedKeys, 'RESTART')">重启</lay-button>
             <lay-button :border="'red'" size="xs" @click="handleStatus(selectedKeys, 'STOP')">停机</lay-button>
             <lay-button :border="'green'" size="xs" @click="handleResetCapital(selectedKeys)">复位本金</lay-button>
-          </div>
-        </lay-space>
+          </lay-space>
+        </div>
+        <div class="custom-group">
+          <lay-space>
+            <lay-button :border="'green'" size="xs" @click="handleBatchUpdate(selectedKeys)">批量设置</lay-button>
+            <lay-button :border="'green'" size="xs" @click="handleCopyParams(selectedKeys)">复制参数</lay-button>
+          </lay-space>
+        </div>
       </div>
       <div>
         <lay-table
@@ -46,6 +52,12 @@
           <template v-slot:name="{ row }">
             <span class="normal-color" @click="jumpDetail(row)">{{ row.name }}</span>
           </template>
+          <template v-slot:account="{ row }">
+            <span>{{ JSON.parse(row.configsJson).account }}</span>
+          </template>
+          <template v-slot:pair="{ row }">
+            <span>{{ JSON.parse(row.configsJson).pair }}</span>
+          </template>
           <template v-slot:earningRate="{ row }">
             <span :class="{ 'primary-color': row.earningRate > 0, 'danger-color': row.earningRate < 0 }">{{ row.earningRate }}%</span>
           </template>
@@ -93,6 +105,7 @@
       </div>
     </template>
   </lay-card>
+  <BatchUpdate ref="batchUpdateRef" />
   <Update ref="updateRef" />
   <Copy ref="copyRef" />
   <Transfers ref="transfersRef" />
@@ -100,12 +113,13 @@
 
 <script lang="ts" setup name="BotManage">
 import { ref, reactive, getCurrentInstance, onBeforeUnmount } from "vue";
+import BatchUpdate from "./components/BatchUpdate.vue";
 import Update from "./components/Update.vue";
 import Transfers from "./components/Transfers.vue";
 import Copy from "./components/Copy.vue";
 import TableButton from "@/components/TableButton.vue";
 import { timeConverts } from "@/utils/index";
-import { get_robot_list, delete_robot, set_robot_status, set_robot_reset_capital } from "@/api";
+import { get_robot_list, delete_robot, set_robot_status, set_robot_reset_capital, update_robot_batch } from "@/api";
 
 const ROBOT_STATUS: any = reactive({
   STOPPED: "已停止",
@@ -118,6 +132,7 @@ const ROBOT_STATUS: any = reactive({
 });
 
 const { proxy }: any = getCurrentInstance();
+const batchUpdateRef = ref();
 const updateRef = ref();
 const copyRef = ref();
 const transfersRef = ref();
@@ -149,6 +164,8 @@ const columns = ref([
   { title: "选项", width: "44px", type: "checkbox" },
   { title: "ID", width: "60px", key: "id" },
   { title: "名称", width: "90px", key: "name", customSlot: "name", ellipsisTooltip: true },
+  { title: "账户", width: "90px", key: "account", customSlot: "account", ellipsisTooltip: true },
+  { title: "币对", width: "90px", key: "pair", customSlot: "pair", align: "center" },
   { title: "起始", width: "90px", key: "startAmount", align: "center" },
   { title: "收益", width: "90px", key: "earningRate", customSlot: "earningRate", align: "center" },
   { title: "状态", width: "90px", key: "status", customSlot: "status", align: "center" },
@@ -214,11 +231,60 @@ const handleCopyRobot = async (ids: any) => {
   const result = await copyRef.value.show(ids[0]);
   if (result) getPageInfo();
 };
+// 批量修改
+const handleBatchUpdate = async (ids: any) => {
+  if (ids.length == 0) return proxy.$message(`请先选择要设置机器!`, 7);
+  const botList = dataSource.value.filter((item: any) => ids.includes(item.id));
+  const result = await batchUpdateRef.value.show(botList);
+  if (result) getPageInfo();
+};
+// 复制参数
+const handleCopyParams = async (ids: any) => {
+  if (ids.length == 0) return proxy.$message(`请先选择要复制参数机器!`, 7);
+  if (ids.length == 1) return proxy.$message(`请先至少选择两台机器!`, 7);
+  const firstBot: any = dataSource.value.find((item: any) => item.id == ids[0]);
+  const otherBot = dataSource.value.filter((item: any) => ids.includes(item.id) && item.id != ids[0]);
+  const result = await proxy.$waitingConfirm(`是否确认要把机器"${firstBot.name}"参数复制给"${otherBot.map((item: any) => item.name).join(",")}"?`);
+  if (!result) return;
+  const configs = firstBot.configList.filter((item: any) => {
+    delete item.deleted;
+    delete item.id;
+    delete item.robotId;
+    return ![4, 5].includes(item.valType);
+  });
+  const params = otherBot.map((item: any) => {
+    const botConfigs: any = item.configList.filter((item: any) => {
+      delete item.deleted;
+      delete item.id;
+      delete item.robotId;
+      return [4, 5].includes(item.valType);
+    });
+    return {
+      id: item.id,
+      name: item.name,
+      serverId: item.serverId,
+      strategyId: item.strategyId,
+      strategyProgramId: item.strategyProgramId,
+      callPort: item.callPort,
+      startAmount: item.startAmount,
+      remark: item.remark,
+      robotConfigs: [...configs, ...botConfigs],
+    };
+  });
+  pageConfig.loading = true;
+  update_robot_batch(params, (data: any) => {
+    pageConfig.loading = false;
+    if (data.code == 200) {
+      proxy.$message(`复制成功!`);
+      getPageInfo();
+    }
+  });
+};
 
 const handleResetCapital = async (ids: any) => {
+  if (ids.length == 0) return proxy.$message(`请先选择要复位本金机器!`, 7);
   let result = await proxy.$waitingConfirm("是否确认要复位机器本金?");
   if (!result) return;
-  if (ids.length == 0) return proxy.$message(`请先选择要复位本金机器!`, 7);
   const params = ids;
   set_robot_reset_capital(params, (data: any) => {
     if (data.code == 200) {
@@ -271,6 +337,12 @@ onBeforeUnmount(() => {
 
 <style lang="scss" scoped>
 .custom-operator-wp {
+  .custom-group {
+    margin-bottom: 6px;
+    margin-right: 14px;
+    display: inline-block;
+    vertical-align: middle;
+  }
   .collect-wp {
     line-height: 24px;
     padding: 0 4px;