ソースを参照

修改弹窗大小
添加校验
添加机器人模块

DESKTOP-NE65RNK\Citrus_limon 1 年間 前
コミット
772095b107
32 ファイル変更1341 行追加357 行削除
  1. 2 1
      .env.development
  2. 80 0
      src/api/index.ts
  3. 65 1
      src/assets/css/index.scss
  4. 8 10
      src/components/PageLayout/Header.vue
  5. 6 0
      src/router/routes.ts
  6. 10 0
      src/utils/index.ts
  7. 69 0
      src/views/bot/manage/components/Copy.vue
  8. 251 0
      src/views/bot/manage/components/Update.vue
  9. 274 0
      src/views/bot/manage/index.vue
  10. 30 22
      src/views/exchange/apikey/components/Batch.vue
  11. 36 31
      src/views/exchange/apikey/components/Update.vue
  12. 3 3
      src/views/exchange/apikey/index.vue
  13. 36 30
      src/views/exchange/manage/components/Update.vue
  14. 52 25
      src/views/quant/manage/components/Update.vue
  15. 30 15
      src/views/quant/manage/components/UpdateParams.vue
  16. 16 9
      src/views/quant/manage/components/UpdateVersion.vue
  17. 9 3
      src/views/quant/manage/components/Version.vue
  18. 0 1
      src/views/quant/manage/index.vue
  19. 29 24
      src/views/server/command/components/Update.vue
  20. 1 1
      src/views/server/command/index.vue
  21. 34 15
      src/views/server/manage/components/Batch.vue
  22. 53 10
      src/views/server/manage/components/Command.vue
  23. 8 2
      src/views/server/manage/components/Pem.vue
  24. 55 0
      src/views/server/manage/components/Result.vue
  25. 33 30
      src/views/server/manage/components/Update.vue
  26. 29 25
      src/views/server/manage/components/UpdatePem.vue
  27. 3 2
      src/views/server/manage/index.vue
  28. 29 24
      src/views/system/organization/components/Update.vue
  29. 30 24
      src/views/system/user/components/Update.vue
  30. 3 2
      src/views/system/webpage/components/Operator.vue
  31. 29 24
      src/views/system/webpage/components/Update.vue
  32. 28 23
      src/views/system/webpage/components/UpdateOperator.vue

+ 2 - 1
.env.development

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

+ 80 - 0
src/api/index.ts

@@ -24,6 +24,56 @@ export const client_upload = (params: any, callback: any) => {
   });
 };
 
+// 机器人管理
+// 机器人管理-机器人列表
+export const get_robot_list = (params: any, callback: any) => {
+  return http.request("/robot/getPage", "get", params).then((data) => {
+    if (data) callback && callback(data);
+  });
+};
+// 机器人管理-复制机器人
+export const copy_robot = (params: any, callback: any) => {
+  return http.request("/robot/copy", "post", params).then((data) => {
+    if (data) callback && callback(data);
+  });
+};
+// 机器人管理-机器人指令
+export const set_robot_status = (params: any, callback: any) => {
+  return http.request("/robot/submitStatus", "post", params).then((data) => {
+    if (data) callback && callback(data);
+  });
+};
+// 机器人管理-重置初始资金
+export const set_robot_reset_capital = (params: any, callback: any) => {
+  return http.request("/robot/resetCapital", "post", params).then((data) => {
+    if (data) callback && callback(data);
+  });
+};
+// 机器人管理-机器人详情
+export const get_robot_find = (params: any, callback: any) => {
+  return http.request("/robot/findById", "get", params).then((data) => {
+    if (data) callback && callback(data);
+  });
+};
+// 机器人管理-添加机器人
+export const add_robot = (params: any, callback: any) => {
+  return http.request("/robot/save", "post", params).then((data) => {
+    if (data) callback && callback(data);
+  });
+};
+// 机器人管理-修改机器人
+export const update_robot = (params: any, callback: any) => {
+  return http.request("/robot/update", "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) => {
+    if (data) callback && callback(data);
+  });
+};
+
 // 策略管理
 // 策略管理-策略列表
 export const get_strategy_list = (params: any, callback: any) => {
@@ -37,6 +87,12 @@ export const get_strategy_list_all = (params: any, callback: any) => {
     if (data) callback && callback(data);
   });
 };
+// 策略管理-策略列表下拉
+export const get_strategy_select = (params: any, callback: any) => {
+  return http.request("/strategy/getSelect", "get", params).then((data) => {
+    if (data) callback && callback(data);
+  });
+};
 // 策略管理-添加策略
 export const add_strategy = (params: any, callback: any) => {
   return http.request("/strategy/save", "post", params).then((data) => {
@@ -61,6 +117,12 @@ export const get_strategy_program_list = (params: any, callback: any) => {
     if (data) callback && callback(data);
   });
 };
+// 策略管理-版本列表下拉
+export const get_strategy_program_select = (params: any, callback: any) => {
+  return http.request("/strategyProgram/getSelect", "get", params).then((data) => {
+    if (data) callback && callback(data);
+  });
+};
 // 策略管理-添加版本
 export const add_strategy_program = (params: any, callback: any) => {
   return http.request("/strategyProgram/save", "post", params).then((data) => {
@@ -91,6 +153,12 @@ export const get_strategy_program_params_list = (params: any, callback: any) =>
     if (data) callback && callback(data);
   });
 };
+// 策略管理-获取版本参数下拉
+export const get_strategy_parameter_select = (params: any, callback: any) => {
+  return http.request("/strategyParameter/getList", "get", params).then((data) => {
+    if (data) callback && callback(data);
+  });
+};
 
 // 交易所
 // 交易所管理-交易所列表
@@ -136,6 +204,12 @@ export const get_apikey_list = (params: any, callback: any) => {
     if (data) callback && callback(data);
   });
 };
+// ApiKey管理-Apikey下拉
+export const get_apikey_select = (params: any, callback: any) => {
+  return http.request("/userProof/getSelect", "get", params).then((data) => {
+    if (data) callback && callback(data);
+  });
+};
 // ApiKey管理-添加Apikey
 export const add_apikey = (params: any, callback: any) => {
   return http.request("/userProof/save", "post", params).then((data) => {
@@ -162,6 +236,12 @@ export const get_server_list = (params: any, callback: any) => {
     if (data) callback && callback(data);
   });
 };
+// 服务器管理-服务器列表下拉
+export const get_server_select = (params: any, callback: any) => {
+  return http.request("/userServer/getSelect", "get", params).then((data) => {
+    if (data) callback && callback(data);
+  });
+};
 // 服务器管理-添加服务器
 export const add_server = (params: any, callback: any) => {
   return http.request("/userServer/save", "post", params).then((data) => {

+ 65 - 1
src/assets/css/index.scss

@@ -5,10 +5,10 @@
   --button-warm-color: #ffb800;
   --button-danger-color: #ff5722;
   --button-checked-color: #16b777;
+  --normal-color: #1e9fff;
   --primary-color: rgb(28, 175, 131);
   --danger-color: #ff5722;
 }
-
 * {
   padding: 0;
   margin: 0;
@@ -113,4 +113,68 @@ body,
       }
     }
   }
+  .custom-operator-wp {
+    padding-bottom: 10px;
+  }
+}
+
+.width-500 {
+  box-sizing: border-box;
+  width: 500px;
+}
+.width-550 {
+  box-sizing: border-box;
+  width: 550px;
+}
+.width-600 {
+  box-sizing: border-box;
+  width: 600px;
+}
+.width-650 {
+  box-sizing: border-box;
+  width: 650px;
+}
+.width-700 {
+  box-sizing: border-box;
+  width: 700px;
+}
+.width-750 {
+  box-sizing: border-box;
+  width: 750px;
+}
+.width-800 {
+  box-sizing: border-box;
+  width: 800px;
+}
+.width-850 {
+  box-sizing: border-box;
+  width: 850px;
+}
+.width-900 {
+  box-sizing: border-box;
+  width: 900px;
+}
+.width-950 {
+  box-sizing: border-box;
+  width: 950px;
+}
+.width-1000 {
+  box-sizing: border-box;
+  width: 1000px;
+}
+.width-1050 {
+  box-sizing: border-box;
+  width: 1050px;
+}
+.width-1100 {
+  box-sizing: border-box;
+  width: 1100px;
+}
+.width-1150 {
+  box-sizing: border-box;
+  width: 1150px;
+}
+.width-1200 {
+  box-sizing: border-box;
+  width: 1200px;
 }

+ 8 - 10
src/components/PageLayout/Header.vue

@@ -34,16 +34,14 @@ defineProps({
 });
 
 const handeLoginOut = () => {
-  client_logout({}, (data: any) => {
-    if (data.code == 200) {
-      window.sessionStorage.removeItem("_4L_TOKEN");
-      window.sessionStorage.removeItem("_4L_MENU_PATH");
-      window.sessionStorage.removeItem("_4L_S_MENU_PATH");
-      window.sessionStorage.removeItem("_4L_TAG_LIST");
-      window.sessionStorage.removeItem("_4L_API_LIST");
-      window.sessionStorage.removeItem("_4L_MENU_COLLAPSE");
-      router.push({ path: "/login" });
-    }
+  client_logout({}, () => {
+    window.sessionStorage.removeItem("_4L_TOKEN");
+    window.sessionStorage.removeItem("_4L_MENU_PATH");
+    window.sessionStorage.removeItem("_4L_S_MENU_PATH");
+    window.sessionStorage.removeItem("_4L_TAG_LIST");
+    window.sessionStorage.removeItem("_4L_API_LIST");
+    window.sessionStorage.removeItem("_4L_MENU_COLLAPSE");
+    router.push({ path: "/login" });
   });
 };
 </script>

+ 6 - 0
src/router/routes.ts

@@ -22,6 +22,12 @@ const routes: Array<RouteRecordRaw> = [
         component: () => import("@/views/quant/manage/index.vue"),
         meta: { title: "策略管理", keepAlive: true },
       },
+      {
+        path: "/bot/manage",
+        name: "BotManage",
+        component: () => import("@/views/bot/manage/index.vue"),
+        meta: { title: "机器人管理", keepAlive: true },
+      },
       {
         path: "/exchange/manage",
         name: "ExchangeManage",

+ 10 - 0
src/utils/index.ts

@@ -25,3 +25,13 @@ export const generateAvatar = (text: string, backgroundColor?: string, textColor
 
   return dataUrl;
 };
+
+export const timeConverts = (timestamp: number) => {
+  if (!timestamp) return "";
+  let now_timestamp = +new Date();
+  let diff_timestamp = now_timestamp - timestamp;
+  if (diff_timestamp / 1000 < 59) return `${Math.ceil(diff_timestamp / 1000)}秒前`;
+  if (diff_timestamp / 1000 / 60 < 59) return `${Math.ceil(diff_timestamp / 1000 / 60)}分钟前`;
+  if (diff_timestamp / 1000 / 60 / 60 < 23) return `${Math.ceil(diff_timestamp / 1000 / 60 / 60)}小时前`;
+  return `${Math.ceil(diff_timestamp / 1000 / 60 / 60 / 24)}天前`;
+};

+ 69 - 0
src/views/bot/manage/components/Copy.vue

@@ -0,0 +1,69 @@
+<template>
+  <lay-layer :title="modelConfig.title" v-model="modelConfig.visible" area="auto" :btn="operator">
+    <div class="width-500" style="padding: 20px">
+      <lay-form :model="modelParams" ref="modelFormRef" required>
+        <lay-form-item label="复制数量" prop="copyNum">
+          <lay-input v-model="modelParams.copyNum" type="number" :max="50" :min="1" />
+        </lay-form-item>
+      </lay-form>
+    </div>
+  </lay-layer>
+</template>
+
+<script lang="ts" setup>
+import { ref, reactive, getCurrentInstance } from "vue";
+import { copy_robot } from "@/api";
+
+const { proxy }: any = getCurrentInstance();
+
+interface ModelConfig {
+  title: string;
+  visible: boolean;
+  loading: boolean;
+}
+interface ModelParams {
+  robotId?: string;
+  copyNum?: number;
+}
+
+let modelParams = ref<ModelParams>({});
+let modelConfig: ModelConfig = reactive({ title: "", visible: false, loading: false });
+
+let handleResult = reactive<{ resolve?: any; reject?: any }>({});
+
+const show = (params?: any) => {
+  modelConfig.visible = true;
+  modelConfig.title = "复制机器人";
+  modelParams.value = { robotId: params, copyNum: 1 };
+  return new Promise(async (resolve, reject) => {
+    handleResult.resolve = resolve;
+    handleResult.reject = reject;
+  });
+};
+
+const operator = reactive([
+  {
+    text: "确认",
+    callback: () => {
+      modelConfig.loading = true;
+      const params = { ...modelParams.value };
+      copy_robot(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>

+ 251 - 0
src/views/bot/manage/components/Update.vue

@@ -0,0 +1,251 @@
+<template>
+  <lay-layer :title="modelConfig.title" v-model="modelConfig.visible" area="auto" :btn="operator">
+    <div class="width-800" style="padding: 20px">
+      <lay-form :model="modelParams" ref="modelFormRef" required>
+        <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="processNum">
+              <lay-input v-model="modelParams.processNum" />
+            </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-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 == 4" v-model="modelParams.robotConfigs[item.code]" :options="apikeyList" :show-search="true" />
+                </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 { 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";
+
+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;
+  processNum?: 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();
+
+const show = async (params?: any) => {
+  initData();
+  get_server_list();
+  get_strategy_list();
+  get_apikey();
+  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) => {
+      if (item.valType == 2) {
+        robotConfigs[item.code] = item.val == "true" ? true : false;
+      } else if (item.valType == 4) {
+        robotConfigs[item.code] = parseInt(item.val);
+      } else {
+        robotConfigs[item.code] = item.val;
+      }
+    });
+
+    modelParams.value = { ...params, robotConfigs };
+    console.log(modelParams.value);
+    strategyHandleChange(modelParams.value.strategyId);
+    strategyProgramHandleChange(modelParams.value.strategyProgramId);
+  } else {
+    modelParams.value = { robotConfigs: {} };
+  }
+  return new Promise(async (resolve, reject) => {
+    handleResult.resolve = resolve;
+    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 = {};
+  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;
+  });
+};
+// 获取策略版本参数下拉
+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) => {
+  const params = {
+    id: value.id,
+    name: value.name,
+    serverId: value.serverId,
+    strategyId: value.strategyId,
+    strategyProgramId: value.strategyProgramId,
+    startAmount: value.startAmount,
+    processNum: value.processNum,
+    remark: value.remark,
+  };
+  const robotConfigs = strategyParameterList.value.map((item: any) => ({ name: item.name, code: item.code, val: value.robotConfigs[item.code], valType: item.valType }));
+  return { ...params, robotConfigs };
+};
+
+const initData = () => {
+  serverList.value = [];
+  strategyList.value = [];
+  strategyProgramList.value = [];
+  strategyParameterList.value = [];
+  apikeyList.value = [];
+};
+
+const operator = reactive([
+  {
+    text: "确认",
+    callback: () => {
+      modelFormRef.value.validate((isValidate: boolean) => {
+        if (isValidate) {
+          modelConfig.loading = true;
+          const params = handleParams(modelParams.value);
+          if (modelConfig.isUpdate) {
+            update_robot(params, (data: any) => {
+              modelConfig.loading = false;
+              if (data.code == 200) {
+                proxy.$message("编辑成功!");
+                modelConfig.visible = false;
+                handleResult.resolve(true);
+              }
+            });
+          } else {
+            add_robot(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>

+ 274 - 0
src/views/bot/manage/index.vue

@@ -0,0 +1,274 @@
+<template>
+  <lay-card class="custom-card">
+    <template v-slot:title>
+      <span class="card-title">机器人管理</span>
+    </template>
+    <template v-slot:extra>
+      <lay-button class="card-button" v-if="apiList?.includes('/userServer/save')" @click="handleCopyRobot(selectedKeys)">复制机器人</lay-button>
+      <lay-button class="card-button" v-if="apiList?.includes('/userServer/save')" @click="handleUpdate(0)">添加</lay-button>
+    </template>
+
+    <template v-slot:body>
+      <div class="custom-form-layout">
+        <lay-form class="form-wp" :model="pageParams" mode="inline">
+          <lay-form-item label="名称" prop="name">
+            <lay-input v-model="pageParams.name" />
+          </lay-form-item>
+          <div class="form-button-wp">
+            <lay-button @click="getPageInfo(true)">搜索</lay-button>
+          </div>
+        </lay-form>
+      </div>
+      <div class="custom-operator-wp">
+        <lay-space>
+          <div v-html="collectInfo" class="collect-wp" />
+          <div>
+            <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>
+      </div>
+      <div>
+        <lay-table
+          :page="tablePage"
+          :columns="columns"
+          size="sm"
+          resize
+          id="id"
+          :data-source="dataSource"
+          v-model:selected-keys="selectedKeys"
+          :loading="pageConfig.loading"
+          @change="handleCurrentChange"
+        >
+          <template v-slot:earningRate="{ row }">
+            <span :class="{ 'primary-color': row.earningRate > 0, 'danger-color': row.earningRate < 0 }">{{ row.earningRate }}%</span>
+          </template>
+          <template v-slot:status="{ row }">
+            <lay-space v-if="row.status == 'RUNNING'">
+              <lay-badge type="dot" theme="blue" ripple />
+              <span>{{ ROBOT_STATUS[row.status] }}</span>
+            </lay-space>
+            <lay-space v-else-if="row.status == 'ERROR'">
+              <lay-badge type="dot" ripple />
+              <span>{{ ROBOT_STATUS[row.status] }}</span>
+            </lay-space>
+            <lay-space v-else>
+              <lay-badge type="dot" theme="orange" ripple />
+              <span>{{ ROBOT_STATUS[row.status] }}</span>
+            </lay-space>
+          </template>
+          <template v-slot:ip="{ row }">
+            {{ `${row.serverIp}:${row.processNum}` }}
+          </template>
+          <template v-slot:lastReportTime="{ row }">
+            <span :class="{ 'danger-color': timeConverts(row.lastReportTime).indexOf('秒') == -1 }">{{ row.lastReportTime ? timeConverts(row.lastReportTime) : "未通讯" }}</span>
+          </template>
+          <template v-slot:updateTime="{ row }">
+            <span>{{ timeConverts(row.updateTime) }}</span>
+          </template>
+          <template v-slot:operator="{ row }">
+            <lay-space>
+              <TableButton v-if="apiList?.includes('/userServer/testConnect') && ['STOPPED', 'STOP_PENDING', 'ERROR'].includes(row.status)" text="开机" @click="handleStatus([row.id], 'RUN')" />
+              <TableButton
+                v-if="apiList?.includes('/userServer/testConnect') && ['RUNNING', 'START_PENDING', 'RESTART_PENDING'].includes(row.status)"
+                text="停机"
+                @click="handleStatus([row.id], 'STOP')"
+              />
+              <TableButton v-if="apiList?.includes('/userServer/delete')" text="编辑" @click="handleUpdate(row)" />
+              <TableButton
+                v-if="apiList?.includes('/userServer/update') && ['RUNNING', 'START_PENDING', 'RESTART_PENDING'].includes(row.status)"
+                text="重启"
+                @click="handleStatus([row.id], 'RESTART')"
+              />
+              <TableButton v-if="apiList?.includes('/userServer/update') && ['STOPPED', 'STOP_PENDING', 'ERROR'].includes(row.status)" text="删除" @click="handleDelete(row)" />
+            </lay-space>
+          </template>
+        </lay-table>
+      </div>
+    </template>
+  </lay-card>
+  <Update ref="updateRef" />
+  <Copy ref="copyRef" />
+</template>
+
+<script lang="ts" setup name="BotManage">
+import { ref, reactive, getCurrentInstance, onBeforeUnmount } from "vue";
+import Update from "./components/Update.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";
+
+const ROBOT_STATUS: any = reactive({
+  STOPPED: "已停止",
+  STOP_PENDING: "停止中",
+  RUNNING: "运行中",
+  START_PENDING: "启动中",
+  RESTART_PENDING: "重启中",
+  DOWNLOADING: "下载中",
+  ERROR: "错误",
+});
+
+const { proxy }: any = getCurrentInstance();
+const updateRef = ref();
+const copyRef = ref();
+
+const apiList = ref(window.sessionStorage.getItem("_4L_API_LIST"));
+
+interface PageConfig {
+  loading: boolean;
+}
+
+let pageConfig: PageConfig = reactive({
+  loading: false,
+});
+
+interface FormItem {
+  pageNum?: Number;
+  pageSize?: Number;
+  name?: String;
+}
+const pageParams: FormItem = reactive({ pageNum: 1, pageSize: 10 });
+
+interface TablePage {
+  current: number;
+  limit: number;
+  total: number;
+}
+const tablePage: TablePage = reactive({ current: 1, limit: 10, total: 0 });
+const columns = ref([
+  { title: "选项", width: "44px", type: "checkbox" },
+  { title: "ID", key: "id" },
+  { title: "名称", key: "name" },
+  { title: "起始", key: "startAmount", align: "center" },
+  { title: "收益", key: "earningRate", customSlot: "earningRate", align: "center" },
+  { title: "状态", key: "status", customSlot: "status", align: "center" },
+  { title: "参数", key: "configs", ellipsisTooltip: true },
+  { title: "IP:编号", key: "ip", customSlot: "ip" },
+  { title: "通讯", key: "lastReportTime", customSlot: "lastReportTime", align: "center" },
+  { title: "修改", key: "updateTime", customSlot: "updateTime", align: "center" },
+  {
+    title: "操作",
+    customSlot: "operator",
+    key: "operator",
+    ignoreExport: true,
+  },
+]);
+
+let dataSource = ref([]);
+let selectedKeys = ref([]);
+let collectInfo = ref(`[0/0] 利润:<span class="primary-color">0(0%)</span> 初始:<span class="primary-color">0</span> 现有:<span class="primary-color">0</span>`);
+// 请求机器人列表
+const getPageInfo = (isSearch?: boolean) => {
+  if (isSearch) pageParams.pageNum = 1;
+  pageConfig.loading = true;
+  get_robot_list(pageParams, (data: any) => {
+    pageConfig.loading = false;
+    if (data.code == 200) {
+      dataSource.value = data.data.list;
+      tablePage.total = data.data.total;
+      handleShowInfo(data.data);
+    }
+  });
+};
+getPageInfo();
+let refreshInterval = setInterval(() => {
+  get_robot_list(pageParams, (data: any) => {
+    if (data.code == 200) {
+      dataSource.value = data.data.list;
+      tablePage.total = data.data.total;
+      handleShowInfo(data.data);
+    }
+  });
+}, 2000);
+
+const handleShowInfo = (info: any) => {
+  document.title = `[${info.runNum || 0}/${info.total || 0}] 利润:${info.income || 0}(${info.incomeRate || 0}%)
+    初始:${info.startAmount || 0} 现有:${info.nowAmount || 0}`;
+  collectInfo.value = `[${info.runNum || 0}/${info.total || 0}] 利润:<span class="primary-color">${info.income || 0}(${info.incomeRate || 0}%)</span>
+    初始:<span class="primary-color">${info.startAmount || 0}</span> 现有:<span class="primary-color">${info.nowAmount || 0}</span>`;
+};
+
+const handleCopyRobot = async (ids: any) => {
+  if (ids.length > 1 || ids.length == 0) return proxy.$message(`请勾选1个要复制的机器!`, 7);
+  const result = await copyRef.value.show(ids[0]);
+  if (result) getPageInfo();
+};
+
+const handleResetCapital = async (ids: any) => {
+  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) {
+      proxy.$message(`执行成功!`);
+    }
+  });
+};
+
+const handleStatus = (ids: any, status: String) => {
+  if (ids.length == 0) return proxy.$message(`请先选择要执行命令机器!`, 7);
+  const params = { robotIds: ids, status };
+  set_robot_status(params, (data: any) => {
+    if (data.code == 200) {
+      proxy.$message(`执行成功!`);
+    }
+  });
+};
+
+const handleUpdate = async (value?: any) => {
+  const result = await updateRef.value.show(value);
+  if (result) getPageInfo();
+};
+
+// 删除机器人
+const handleDelete = async (value: any) => {
+  let result = await proxy.$waitingConfirm("是否确认删除该机器人?");
+  if (!result) return;
+  let params = [value.id];
+  pageConfig.loading = true;
+  delete_robot(params, (data: any) => {
+    pageConfig.loading = false;
+    if (data.code == 200) {
+      proxy.$message(`删除成功!`);
+      getPageInfo();
+    }
+  });
+};
+// 分页设置
+const handleCurrentChange = (val: any) => {
+  pageParams.pageNum = val.current;
+  getPageInfo();
+};
+
+onBeforeUnmount(() => {
+  document.title = '两江资本—无敌中控';
+  clearInterval(refreshInterval);
+});
+</script>
+
+<style lang="scss" scoped>
+.custom-operator-wp {
+  .collect-wp {
+    line-height: 24px;
+    padding: 0 4px;
+    border: 1px solid var(--normal-color);
+    color: var(--normal-color);
+    :deep(.primary-color) {
+      color: var(--primary-color);
+    }
+  }
+}
+.custom-form-layout {
+  font-size: 12px !important;
+}
+.primary-color {
+  color: var(--primary-color);
+}
+.danger-color {
+  color: var(--danger-color);
+}
+</style>

+ 30 - 22
src/views/exchange/apikey/components/Batch.vue

@@ -1,16 +1,16 @@
 <template>
-  <lay-layer :title="modelConfig.title" v-model="modelConfig.visible" :area="['1000px', '550px']" :close="handleClose">
-    <div style="padding: 20px">
-      <lay-form :model="modelParams" ref="modelFormRef">
-        <lay-form-item prop="remark">
-          <lay-textarea placeholder="交易所  ApiKey名称  Api  Key  Pass" v-model="modelParams.info" rows="12" />
+  <lay-layer :title="modelConfig.title" v-model="modelConfig.visible" area="auto" :close="handleClose">
+    <div class="width-1000" style="padding: 20px">
+      <lay-form :model="modelParams" ref="modelFormRef" required>
+        <lay-form-item label="批量信息" prop="info">
+          <lay-textarea placeholder="交易所编码  ApiKey名称  Api  Key  Pass" v-model="modelParams.info" rows="12" />
         </lay-form-item>
       </lay-form>
       <div class="tips_wp">
-        <div class="tips">一行一个,空格分割,格式:交易所 名称 Api Key Pass</div>
+        <div class="tips">一行一个,空格分割,格式:交易所编码 名称 Api Key Pass</div>
         <div class="tips">推荐用XLS表格弄好直接复制进去~</div>
         <div class="tips danger">
-          交易所请注意大小写!!支持列表:
+          交易所编码请注意大小写!!支持列表:
           <lay-space>
             <span v-for="item in exchangeList">"{{ item.code }}"</span>
           </lay-space>
@@ -19,14 +19,11 @@
       <div class="run_wp">
         <lay-button type="primary" @click="handleRun()">运 行</lay-button>
       </div>
-      <div>
+      <div class="run_result_wp">
         <div v-for="item in addResultList">
           <lay-space>
-            <span>{{ exchangeList.find((exchagne: any) => exchagne.exchangeId == item.exchangeId)?.name }}</span>
+            <span>{{ exchangeList.find((exchagne: any) => exchagne.exchangeId == item.exchangeId)?.code }}</span>
             <span>{{ item.name }}</span>
-            <span>{{ item.api }}</span>
-            <span>{{ item.key }}</span>
-            <span>{{ item.pass }}</span>
             <span :class="item.status == 1 ? 'primary_color' : 'danger_color'">{{ item.status == 1 ? "添加成功" : "添加失败" }}</span>
           </lay-space>
         </div>
@@ -41,6 +38,8 @@ import { add_apikey, get_exchange_list_all } from "@/api";
 
 const { proxy }: any = getCurrentInstance();
 
+const modelFormRef = ref();
+
 interface ModelConfig {
   title: string;
   visible: boolean;
@@ -94,21 +93,25 @@ const handleInfo = (value: string) => {
     return {
       exchangeId: exchangeInfo.exchangeId,
       name: data[1],
-      api: data[2],
-      key: data[3],
+      accessKey: data[2],
+      secretKey: data[3],
       pass: data[4],
     };
   });
   return isRquest ? result : false;
 };
 const handleRun = () => {
-  const params = handleInfo(modelParams.value.info || "");
-  if (!params) return;
-  modelConfig.loading = true;
-  add_apikey(params, (data: any) => {
-    modelConfig.loading = false;
-    if (data.code == 200) {
-      addResultList.value = addResultList.value.concat(data.data);
+  modelFormRef.value.validate((isValidate: boolean) => {
+    if (isValidate) {
+      const params = handleInfo(modelParams.value.info || "");
+      if (!params) return;
+      modelConfig.loading = true;
+      add_apikey(params, (data: any) => {
+        modelConfig.loading = false;
+        if (data.code == 200) {
+          addResultList.value = addResultList.value.concat(data.data);
+        }
+      });
     }
   });
 };
@@ -130,10 +133,15 @@ defineExpose({ show });
 .run_wp {
   padding: 10px 0;
 }
+.tips_wp,
+.run_wp,
+.run_result_wp {
+  padding-left: 110px;
+}
 .primary_color {
   color: var(--primary-color);
 }
 .danger_color {
   color: var(--danger-color);
 }
-</style>
+</style>

+ 36 - 31
src/views/exchange/apikey/components/Update.vue

@@ -1,6 +1,6 @@
 <template>
-  <lay-layer :title="modelConfig.title" v-model="modelConfig.visible" :area="['500px', '600px']" :btn="operator">
-    <div style="padding: 20px">
+  <lay-layer :title="modelConfig.title" v-model="modelConfig.visible" area="auto" :btn="operator">
+    <div class="width-500" style="padding: 20px">
       <lay-form :model="modelParams" ref="modelFormRef" required>
         <lay-form-item label="交易所名称" prop="exchangeId">
           <lay-select v-model="modelParams.exchangeId">
@@ -10,16 +10,16 @@
         <lay-form-item label="ApiKey名称" prop="name">
           <lay-input v-model="modelParams.name" />
         </lay-form-item>
-        <lay-form-item label="Api" prop="api">
-          <lay-input v-model="modelParams.api" />
+        <lay-form-item label="Api" prop="accessKey">
+          <lay-input v-model="modelParams.accessKey" />
         </lay-form-item>
-        <lay-form-item label="Key" prop="key">
-          <lay-input v-model="modelParams.key" />
+        <lay-form-item label="Key" prop="secretKey">
+          <lay-input v-model="modelParams.secretKey" />
         </lay-form-item>
-        <lay-form-item label="Pass" prop="pass">
+        <lay-form-item label="Pass">
           <lay-input v-model="modelParams.pass" />
         </lay-form-item>
-        <lay-form-item label="备注" prop="remark">
+        <lay-form-item label="备注">
           <lay-textarea placeholder="请输入备注" v-model="modelParams.remark" />
         </lay-form-item>
       </lay-form>
@@ -33,6 +33,8 @@ import { add_apikey, update_apikey, get_exchange_list_all } from "@/api";
 
 const { proxy }: any = getCurrentInstance();
 
+const modelFormRef = ref();
+
 interface ModelConfig {
   title: string;
   visible: boolean;
@@ -42,8 +44,8 @@ interface ModelConfig {
 interface ModelParams {
   exchangeId?: string;
   name?: string;
-  api?: string;
-  key?: string;
+  accessKey?: string;
+  secretKey?: string;
   pass?: string;
   remark?: string;
 }
@@ -80,28 +82,31 @@ const operator = reactive([
   {
     text: "确认",
     callback: () => {
-      modelConfig.loading = true;
-      if (modelConfig.isUpdate) {
-        const params = [{ ...modelParams.value }];
-        update_apikey(params, (data: any) => {
-          modelConfig.loading = false;
-          if (data.code == 200) {
-            proxy.$message("编辑成功!");
-            modelConfig.visible = false;
-            handleResult.resolve(true);
-          }
-        });
-      } else {
-        const params = [{ ...modelParams.value }];
-        add_apikey(params, (data: any) => {
-          modelConfig.loading = false;
-          if (data.code == 200) {
-            proxy.$message("添加成功!");
-            modelConfig.visible = false;
-            handleResult.resolve(true);
+      modelFormRef.value.validate((isValidate: boolean) => {
+        if (isValidate) {
+          const params = [{ ...modelParams.value }];
+          modelConfig.loading = true;
+          if (modelConfig.isUpdate) {
+            update_apikey(params, (data: any) => {
+              modelConfig.loading = false;
+              if (data.code == 200) {
+                proxy.$message("编辑成功!");
+                modelConfig.visible = false;
+                handleResult.resolve(true);
+              }
+            });
+          } else {
+            add_apikey(params, (data: any) => {
+              modelConfig.loading = false;
+              if (data.code == 200) {
+                proxy.$message("添加成功!");
+                modelConfig.visible = false;
+                handleResult.resolve(true);
+              }
+            });
           }
-        });
-      }
+        }
+      });
     },
   },
   {

+ 3 - 3
src/views/exchange/apikey/index.vue

@@ -71,9 +71,9 @@ interface TablePage {
 const tablePage: TablePage = reactive({ current: 1, limit: 10, total: 0 });
 const columns = ref([
   { title: "名称", key: "name" },
-  { title: "ApiKey", key: "name", ellipsisTooltip: true },
-  { title: "API", key: "api", ellipsisTooltip: true },
-  { title: "KEY", key: "key", ellipsisTooltip: true },
+  { title: "交易所", key: "exchangeName", ellipsisTooltip: true },
+  { title: "API", key: "accessKey", ellipsisTooltip: true },
+  { title: "KEY", key: "secretKey", ellipsisTooltip: true },
   { title: "PASS", key: "pass", ellipsisTooltip: true },
   { title: "备注", key: "remark", ellipsisTooltip: true },
   { title: "更新时间", key: "updateTime", ellipsisTooltip: true },

+ 36 - 30
src/views/exchange/manage/components/Update.vue

@@ -1,6 +1,6 @@
 <template>
-  <lay-layer :title="modelConfig.title" v-model="modelConfig.visible" :area="['500px', '350px']" :btn="operator">
-    <div style="padding: 20px">
+  <lay-layer :title="modelConfig.title" v-model="modelConfig.visible" area="auto" :btn="operator">
+    <div class="width-500" style="padding: 20px">
       <lay-form :model="modelParams" ref="modelFormRef" required>
         <lay-form-item label="交易所名称" prop="name">
           <lay-input v-model="modelParams.name" />
@@ -19,6 +19,8 @@ import { add_exchange, update_exchange } from "@/api";
 
 const { proxy }: any = getCurrentInstance();
 
+const modelFormRef = ref();
+
 interface ModelConfig {
   title: string;
   visible: boolean;
@@ -51,35 +53,39 @@ const operator = reactive([
   {
     text: "确认",
     callback: () => {
-      modelConfig.loading = true;
-      if (modelConfig.isUpdate) {
-        const params = {
-          exchangeId: modelParams.value.exchangeId,
-          name: modelParams.value.name?.trim(),
-          code: modelParams.value.code?.trim(),
-        };
-        update_exchange(params, (data: any) => {
-          modelConfig.loading = false;
-          if (data.code == 200) {
-            proxy.$message("编辑成功!");
-            modelConfig.visible = false;
-            handleResult.resolve(true);
-          }
-        });
-      } else {
-        const params = {
-          name: modelParams.value.name?.trim(),
-          code: modelParams.value.code?.trim(),
-        };
-        add_exchange(params, (data: any) => {
-          modelConfig.loading = false;
-          if (data.code == 200) {
-            proxy.$message("添加成功!");
-            modelConfig.visible = false;
-            handleResult.resolve(true);
+      modelFormRef.value.validate((isValidate: boolean) => {
+        if (isValidate) {
+          if (modelConfig.isUpdate) {
+            const params = {
+              exchangeId: modelParams.value.exchangeId,
+              name: modelParams.value.name?.trim(),
+              code: modelParams.value.code?.trim(),
+            };
+            update_exchange(params, (data: any) => {
+              modelConfig.loading = false;
+              if (data.code == 200) {
+                proxy.$message("编辑成功!");
+                modelConfig.visible = false;
+                handleResult.resolve(true);
+              }
+            });
+          } else {
+            const params = {
+              name: modelParams.value.name?.trim(),
+              code: modelParams.value.code?.trim(),
+            };
+            modelConfig.loading = true;
+            add_exchange(params, (data: any) => {
+              modelConfig.loading = false;
+              if (data.code == 200) {
+                proxy.$message("添加成功!");
+                modelConfig.visible = false;
+                handleResult.resolve(true);
+              }
+            });
           }
-        });
-      }
+        }
+      });
     },
   },
   {

+ 52 - 25
src/views/quant/manage/components/Update.vue

@@ -1,11 +1,16 @@
 <template>
-  <lay-layer :title="modelConfig.title" v-model="modelConfig.visible" :area="['500px', '450px']" :btn="operator">
-    <div style="padding: 20px">
+  <lay-layer :title="modelConfig.title" v-model="modelConfig.visible" area="auto" :btn="operator">
+    <div class="width-500" style="padding: 20px">
       <lay-form :model="modelParams" ref="modelFormRef" required>
         <lay-form-item label="策略名称" prop="name">
           <lay-input v-model="modelParams.name" />
         </lay-form-item>
-        <lay-form-item label="备注" prop="remark">
+        <lay-form-item label="组织" prop="groupIds">
+          <lay-select v-model="modelParams.groupIds" multiple>
+            <lay-select-option v-for="item in groupSelectList" :value="item.id" :label="item.value" />
+          </lay-select>
+        </lay-form-item>
+        <lay-form-item label="备注">
           <lay-textarea placeholder="请输入备注" v-model="modelParams.remark" />
         </lay-form-item>
       </lay-form>
@@ -15,10 +20,12 @@
 
 <script lang="ts" setup>
 import { ref, reactive, getCurrentInstance } from "vue";
-import { add_strategy, update_strategy} from "@/api";
+import { add_strategy, update_strategy, get_group_select_list } from "@/api";
 
 const { proxy }: any = getCurrentInstance();
 
+const modelFormRef = ref();
+
 interface ModelConfig {
   title: string;
   visible: boolean;
@@ -28,15 +35,22 @@ interface ModelConfig {
 interface ModelParams {
   id?: string;
   name?: string;
+  groupIds?: Array<string>;
   remark?: string;
 }
+interface GroupList {
+  id?: string;
+  value?: string;
+}
 
 let modelParams = ref<ModelParams>({});
 let modelConfig: ModelConfig = reactive({ title: "", visible: false, isUpdate: false, loading: false });
+let groupSelectList = ref<Array<GroupList>>([]);
 
 let handleResult = reactive<{ resolve?: any; reject?: any }>({});
 
 const show = (params?: any) => {
+  getGroupList();
   modelConfig.visible = true;
   modelConfig.isUpdate = !!params;
   modelConfig.title = modelConfig.isUpdate ? "编辑策略" : "添加策略";
@@ -47,32 +61,45 @@ const show = (params?: any) => {
   });
 };
 
+const getGroupList = () => {
+  modelConfig.loading = false;
+  get_group_select_list({}, (data: any) => {
+    modelConfig.loading = true;
+    if (data.code == 200) {
+      groupSelectList.value = data.data;
+    }
+  });
+};
+
 const operator = reactive([
   {
     text: "确认",
     callback: () => {
-      modelConfig.loading = true;
-      if (modelConfig.isUpdate) {
-        const params = { ...modelParams.value };
-        update_strategy(params, (data: any) => {
-          modelConfig.loading = false;
-          if (data.code == 200) {
-            proxy.$message("编辑成功!");
-            modelConfig.visible = false;
-            handleResult.resolve(true);
-          }
-        });
-      } else {
-        const params = { ...modelParams.value };
-        add_strategy(params, (data: any) => {
-          modelConfig.loading = false;
-          if (data.code == 200) {
-            proxy.$message("添加成功!");
-            modelConfig.visible = false;
-            handleResult.resolve(true);
+      modelFormRef.value.validate((isValidate: boolean) => {
+        if (isValidate) {
+          const params = { ...modelParams.value };
+          modelConfig.loading = true;
+          if (modelConfig.isUpdate) {
+            update_strategy(params, (data: any) => {
+              modelConfig.loading = false;
+              if (data.code == 200) {
+                proxy.$message("编辑成功!");
+                modelConfig.visible = false;
+                handleResult.resolve(true);
+              }
+            });
+          } else {
+            add_strategy(params, (data: any) => {
+              modelConfig.loading = false;
+              if (data.code == 200) {
+                proxy.$message("添加成功!");
+                modelConfig.visible = false;
+                handleResult.resolve(true);
+              }
+            });
           }
-        });
-      }
+        }
+      });
     },
   },
   {

+ 30 - 15
src/views/quant/manage/components/UpdateParams.vue

@@ -1,6 +1,6 @@
 <template>
-  <lay-layer :title="modelConfig.title" v-model="modelConfig.visible" :area="['500px', '450px']" :btn="operator">
-    <div style="padding: 20px">
+  <lay-layer :title="modelConfig.title" v-model="modelConfig.visible" area="auto" :btn="operator">
+    <div class="width-500" style="padding: 20px">
       <lay-form :model="modelParams" ref="modelFormRef" required>
         <lay-form-item label="名称" prop="name">
           <lay-input v-model="modelParams.name" />
@@ -13,10 +13,11 @@
             <lay-select-option v-for="item in paramsTypeList" :value="item.key" :label="item.value" />
           </lay-select>
         </lay-form-item>
-        <lay-form-item v-if="modelParams.valType == 3" label="可选值" prop="contentVal">
+        <lay-form-item v-if="modelParams.valType == 3" prop="contentVal" label="可选值">
           <lay-input v-model="modelParams.contentVal" />
+          <div class="input-hint">选择项逗号分割 例:aaa,bbb,ccc</div>
         </lay-form-item>
-        <lay-form-item v-if="modelParams.valType != 4" label="默认值" prop="defaultVal">
+        <lay-form-item v-if="modelParams.valType != 4" label="默认值">
           <lay-input v-model="modelParams.defaultVal" />
         </lay-form-item>
       </lay-form>
@@ -27,9 +28,12 @@
 <script lang="ts" setup>
 import { ref, reactive } from "vue";
 
+const modelFormRef = ref();
+
 const paramsTypeList = [
-  { key: 1, value: "String" },
-  { key: 2, value: "Number" },
+  { key: 0, value: "String" },
+  { key: 1, value: "Number" },
+  { key: 2, value: "Boolean" },
   { key: 3, value: "List" },
   { key: 4, value: "ApiKey" },
 ];
@@ -68,15 +72,19 @@ const operator = reactive([
   {
     text: "确认",
     callback: () => {
-      if (modelConfig.isUpdate) {
-        const params = { ...modelParams.value };
-        modelConfig.visible = false;
-        handleResult.resolve(params);
-      } else {
-        const params = { ...modelParams.value };
-        modelConfig.visible = false;
-        handleResult.resolve(params);
-      }
+      modelFormRef.value.validate((isValidate: boolean) => {
+        if (isValidate) {
+          if (modelConfig.isUpdate) {
+            const params = { ...modelParams.value };
+            modelConfig.visible = false;
+            handleResult.resolve(params);
+          } else {
+            const params = { ...modelParams.value };
+            modelConfig.visible = false;
+            handleResult.resolve(params);
+          }
+        }
+      });
     },
   },
   {
@@ -89,3 +97,10 @@ const operator = reactive([
 ]);
 defineExpose({ show });
 </script>
+
+<style lang="scss" scoped>
+.input-hint {
+  font-size: 12px;
+  color: #909399;
+}
+</style>

+ 16 - 9
src/views/quant/manage/components/UpdateVersion.vue

@@ -1,6 +1,6 @@
 <template>
-  <lay-layer :title="modelConfig.title" v-model="modelConfig.visible" :area="['500px', '550px']" :btn="modelConfig.step == 1 ? operator1 : operator2">
-    <div v-show="modelConfig.step == 1" style="padding: 20px">
+  <lay-layer :title="modelConfig.title" v-model="modelConfig.visible" area="auto" :btn="modelConfig.step == 1 ? operator1 : operator2">
+    <div class="width-500" v-show="modelConfig.step == 1" style="padding: 20px">
       <lay-form :model="modelParams" ref="modelFormRef" required>
         <lay-form-item label="版本名称" prop="name">
           <lay-input v-model="modelParams.name" />
@@ -8,15 +8,15 @@
         <lay-form-item label="版本文件" prop="path">
           <DragUpload v-model="modelParams.path" />
         </lay-form-item>
-        <lay-form-item label="更新说明" prop="updateInstructions">
+        <lay-form-item label="更新说明">
           <lay-textarea placeholder="请输入更新说明" v-model="modelParams.updateInstructions" />
         </lay-form-item>
       </lay-form>
     </div>
-    <div v-show="modelConfig.step == 2" style="padding: 20px">
+    <div class="width-1000" v-show="modelConfig.step == 2" style="padding: 20px">
       <lay-card class="custom-card">
         <div class="operator-wp">
-          <lay-button class="custom-button-primary" @click="handleUpVersion()">引用上个版本</lay-button>
+          <lay-button class="custom-button-primary" v-if="!modelConfig.isUpdate && versionNumber" @click="handleUpVersion()">引用上个版本参数</lay-button>
           <lay-button class="custom-button-primary" @click="handleUpdate()">添加参数</lay-button>
         </div>
         <div class="table-wp">
@@ -34,8 +34,8 @@
         </div>
       </lay-card>
     </div>
-    <UpdateParams ref="updateParams" />
   </lay-layer>
+  <UpdateParams ref="updateParams" />
 </template>
 
 <script lang="ts" setup>
@@ -46,6 +46,8 @@ import UpdateParams from "./UpdateParams.vue";
 import { add_strategy_program, update_strategy_program, get_strategy_program_params_list } from "@/api";
 
 const { proxy }: any = getCurrentInstance();
+
+const modelFormRef = ref();
 const updateParams = ref();
 
 interface ModelConfig {
@@ -95,8 +97,9 @@ const columns = ref([
   },
 ]);
 let dataSource = ref<Array<any>>([]);
-
-const show = (params: any, version?: any) => {
+let versionNumber = ref<number>(0);
+const show = (params: any, number: number, version?: any) => {
+  versionNumber.value = number;
   modelConfig.visible = true;
   modelConfig.step = 1;
   modelConfig.isUpdate = !!version;
@@ -144,7 +147,11 @@ const operator1 = reactive([
   {
     text: "下一步",
     callback: () => {
-      modelConfig.step = 2;
+      modelFormRef.value.validate((isValidate: boolean) => {
+        if (isValidate) {
+          modelConfig.step = 2;
+        }
+      });
     },
   },
   {

+ 9 - 3
src/views/quant/manage/components/Version.vue

@@ -1,6 +1,6 @@
 <template>
-  <lay-layer :title="modelConfig.title" v-model="modelConfig.visible" :area="['1200px', '600px']">
-    <lay-card class="custom-card">
+  <lay-layer :title="modelConfig.title" v-model="modelConfig.visible" area="auto">
+    <lay-card class="custom-card width-1200">
       <div class="operator-wp">
         <lay-button class="custom-button-primary" v-if="apiList?.includes('/strategyProgram/save')" @click="handleUpdate()">添加版本</lay-button>
       </div>
@@ -96,7 +96,7 @@ const getPageInfo = (isSearch?: boolean) => {
 
 // 添加/编辑操作
 const handleUpdate = async (value?: any) => {
-  const result = await updateVersionRef.value.show(modelParams, value);
+  const result = await updateVersionRef.value.show(modelParams, dataSource.value.length, value);
   if (result) getPageInfo();
 };
 
@@ -140,3 +140,9 @@ const handleCurrentChange = (val: any) => {
 };
 defineExpose({ show });
 </script>
+
+<style lang="scss" scoped>
+.custom-card{
+  padding-bottom: 60px;
+}
+</style>

+ 0 - 1
src/views/quant/manage/index.vue

@@ -4,7 +4,6 @@
       <span class="card-title">策略管理</span>
     </template>
     <template v-slot:extra>
-      <lay-button class="card-button" @click="handleUpdate()">添加策略</lay-button>
       <lay-button class="card-button" v-if="apiList?.includes('/strategy/save')" @click="handleUpdate()">添加策略</lay-button>
     </template>
 

+ 29 - 24
src/views/server/command/components/Update.vue

@@ -1,6 +1,6 @@
 <template>
-  <lay-layer :title="modelConfig.title" v-model="modelConfig.visible" :area="['550px', '650px']" :btn="operator">
-    <div style="padding: 20px">
+  <lay-layer :title="modelConfig.title" v-model="modelConfig.visible" area="auto" :btn="operator">
+    <div class="width-500" style="padding: 20px">
       <lay-form :model="modelParams" ref="modelFormRef" required>
         <lay-form-item label="名称" prop="title">
           <lay-input v-model="modelParams.title" />
@@ -13,7 +13,7 @@
         <lay-form-item label="Code" prop="code">
           <lay-textarea v-model="modelParams.code" rows="10" />
         </lay-form-item>
-        <lay-form-item label="备注" prop="remark">
+        <lay-form-item label="备注">
           <lay-textarea v-model="modelParams.remark" />
         </lay-form-item>
       </lay-form>
@@ -27,6 +27,8 @@ import { add_instruct, update_instruct } from "@/api";
 
 const { proxy }: any = getCurrentInstance();
 
+const modelFormRef = ref();
+
 interface ModelConfig {
   title: string;
   visible: boolean;
@@ -61,28 +63,31 @@ const operator = reactive([
   {
     text: "确认",
     callback: () => {
-      modelConfig.loading = true;
-      if (modelConfig.isUpdate) {
-        const params = { ...modelParams.value };
-        update_instruct(params, (data: any) => {
-          modelConfig.loading = false;
-          if (data.code == 200) {
-            proxy.$message("编辑成功!");
-            modelConfig.visible = false;
-            handleResult.resolve(true);
-          }
-        });
-      } else {
-        const params = { ...modelParams.value };
-        add_instruct(params, (data: any) => {
-          modelConfig.loading = false;
-          if (data.code == 200) {
-            proxy.$message("添加成功!");
-            modelConfig.visible = false;
-            handleResult.resolve(true);
+      modelFormRef.value.validate((isValidate: boolean) => {
+        if (isValidate) {
+          const params = { ...modelParams.value };
+          modelConfig.loading = true;
+          if (modelConfig.isUpdate) {
+            update_instruct(params, (data: any) => {
+              modelConfig.loading = false;
+              if (data.code == 200) {
+                proxy.$message("编辑成功!");
+                modelConfig.visible = false;
+                handleResult.resolve(true);
+              }
+            });
+          } else {
+            add_instruct(params, (data: any) => {
+              modelConfig.loading = false;
+              if (data.code == 200) {
+                proxy.$message("添加成功!");
+                modelConfig.visible = false;
+                handleResult.resolve(true);
+              }
+            });
           }
-        });
-      }
+        }
+      });
     },
   },
   {

+ 1 - 1
src/views/server/command/index.vue

@@ -70,7 +70,7 @@ interface TablePage {
 const tablePage: TablePage = reactive({ current: 1, limit: 10, total: 0 });
 const columns = ref([
   { title: "名称", key: "title" },
-  { title: "CODE", key: "code" },
+  { title: "CODE", key: "code", ellipsisTooltip: true },
   { title: "系统类型", key: "osType", customSlot: "osType" },
   { title: "描述", key: "remark", ellipsisTooltip: true },
   { title: "更新时间", key: "updateTime" },

+ 34 - 15
src/views/server/manage/components/Batch.vue

@@ -1,14 +1,14 @@
 <template>
-  <lay-layer :title="modelConfig.title" v-model="modelConfig.visible" :area="['1000px', '750px']">
-    <div style="padding: 20px">
-      <lay-form :model="modelParams" ref="modelFormRef">
-        <div style="width: 500px;">
+  <lay-layer :title="modelConfig.title" v-model="modelConfig.visible" area="auto" @close="handleClose()">
+    <div class="width-1000" style="padding: 20px">
+      <lay-form :model="modelParams" ref="modelFormRef" required>
+        <div style="width: 500px">
           <lay-form-item label="操作系统" prop="osType">
             <lay-select v-model="modelParams.osType">
               <lay-select-option :value="0" label="Ubuntu" />
             </lay-select>
           </lay-form-item>
-          <lay-form-item label="登录方式" prop="code">
+          <lay-form-item label="登录方式" prop="loginType">
             <lay-radio v-model="modelParams.loginType" name="action" :value="0" label="账号密码" />
             <lay-radio v-model="modelParams.loginType" name="action" :value="1" label="PEM" />
           </lay-form-item>
@@ -24,7 +24,7 @@
             </lay-select>
           </lay-form-item>
         </div>
-        <lay-form-item prop="remark">
+        <lay-form-item label="批量信息" prop="info">
           <lay-textarea placeholder="服务器名称 IP PROT" v-model="modelParams.info" rows="12" />
         </lay-form-item>
       </lay-form>
@@ -35,7 +35,7 @@
       <div class="run_wp">
         <lay-button type="primary" @click="handleRun()">运 行</lay-button>
       </div>
-      <div>
+      <div class="run_result_wp">
         <div v-for="item in addResultList">
           <lay-space>
             <span>{{ item.name }}</span>
@@ -53,6 +53,8 @@
 import { ref, reactive } from "vue";
 import { add_server, get_pem_list_all } from "@/api";
 
+const modelFormRef = ref();
+
 interface ModelConfig {
   title: string;
   visible: boolean;
@@ -79,7 +81,7 @@ let addResultList = ref<Array<any>>([]);
 const show = () => {
   get_pem_list();
   modelConfig.visible = true;
-  modelConfig.title = "批量添加ApiKey";
+  modelConfig.title = "批量添加服务器";
   modelParams.value = { osType: 0, loginType: 0 };
   return new Promise(async (resolve, reject) => {
     handleResult.resolve = resolve;
@@ -115,16 +117,27 @@ const handleInfo = (value: string) => {
   return result;
 };
 const handleRun = () => {
-  const params = handleInfo(modelParams.value.info || "");
-  if (!params) return;
-  modelConfig.loading = true;
-  add_server(params, (data: any) => {
-    modelConfig.loading = false;
-    if (data.code == 200) {
-      addResultList.value = addResultList.value.concat(data.data);
+  modelFormRef.value.validate((isValidate: boolean) => {
+    if (isValidate) {
+      const params = handleInfo(modelParams.value.info || "");
+      if (!params) return;
+      modelConfig.loading = true;
+      add_server(params, (data: any) => {
+        modelConfig.loading = false;
+        if (data.code == 200) {
+          addResultList.value = addResultList.value.concat(data.data);
+        }
+      });
     }
   });
 };
+
+const handleClose = () => {
+  modelConfig.visible = false;
+  modelConfig.loading = false;
+  handleResult.resolve(!!addResultList.value.length);
+};
+
 defineExpose({ show });
 </script>
 
@@ -140,6 +153,12 @@ defineExpose({ show });
 .run_wp {
   padding: 10px 0;
 }
+
+.tips_wp,
+.run_wp,
+.run_result_wp {
+  padding-left: 110px;
+}
 .primary_color {
   color: var(--primary-color);
 }

+ 53 - 10
src/views/server/manage/components/Command.vue

@@ -1,6 +1,6 @@
 <template>
-  <lay-layer :title="modelConfig.title" v-model="modelConfig.visible" :area="['500px', '450px']" :btn="operator">
-    <div style="padding: 20px">
+  <lay-layer :title="modelConfig.title" v-model="modelConfig.visible" area="auto" :btn="operator">
+    <div class="width-500" style="padding: 20px">
       <lay-form :model="modelParams" ref="modelFormRef" required>
         <lay-form-item label="执行指令" prop="commandId">
           <lay-select v-model="modelParams.commandId">
@@ -8,16 +8,31 @@
           </lay-select>
         </lay-form-item>
       </lay-form>
+      <div class="result-wp" v-if="resultList.length > 0">
+        <div class="result-title">结果列表</div>
+        <div class="result-content">
+          <div class="result-item" v-for="item in resultList">
+            <span class="name">{{ item.serverName }}</span> <TableButton text="查看" @click="showResult(item)" />
+          </div>
+        </div>
+      </div>
     </div>
+    <Result ref="resultRef" />
   </lay-layer>
 </template>
 
 <script lang="ts" setup>
 import { ref, reactive, getCurrentInstance } from "vue";
+import { layer } from "@layui/layui-vue";
+import TableButton from "@/components/TableButton.vue";
+import Result from "./Result.vue";
 import { run_execut_server, get_instruct_list_all } from "@/api";
 
 const { proxy }: any = getCurrentInstance();
 
+const modelFormRef = ref();
+const resultRef = ref();
+
 interface ModelConfig {
   title: string;
   visible: boolean;
@@ -35,6 +50,7 @@ let handleResult = reactive<{ resolve?: any; reject?: any }>({});
 
 let commandList = ref();
 let userServerId = ref();
+let resultList = ref<Array<any>>([]);
 
 const show = (params?: any) => {
   get_instruct_list();
@@ -42,6 +58,7 @@ const show = (params?: any) => {
   modelConfig.title = "执行指令";
   userServerId.value = params;
   modelParams.value = {};
+  resultList.value = [];
   return new Promise(async (resolve, reject) => {
     handleResult.resolve = resolve;
     handleResult.reject = reject;
@@ -57,18 +74,25 @@ const get_instruct_list = () => {
   });
 };
 
+const showResult = async (result: any) => {
+  await resultRef.value.show(result);
+};
+
 const operator = reactive([
   {
     text: "执行",
     callback: () => {
-      const params = { serverList: userServerId.value, ...modelParams.value };
-      modelConfig.loading = true;
-      run_execut_server(params, (data: any) => {
-        modelConfig.loading = false;
-        if (data.code == 200) {
-          proxy.$message("执行成功!");
-          modelConfig.visible = false;
-          handleResult.resolve(true);
+      modelFormRef.value.validate((isValidate: boolean) => {
+        if (isValidate) {
+          const params = { serverList: userServerId.value, ...modelParams.value };
+          let loading = layer.load();
+          run_execut_server(params, (data: any) => {
+            layer.close(loading);
+            if (data.code == 200) {
+              proxy.$message("执行成功!");
+              resultList.value = data.data;
+            }
+          });
         }
       });
     },
@@ -83,3 +107,22 @@ const operator = reactive([
 ]);
 defineExpose({ show });
 </script>
+<style lang="scss" scoped>
+.result-wp {
+  padding: 0 20px;
+  .result-title {
+    font-weight: bold;
+  }
+  .result-content {
+    padding-top: 20px;
+    .result-item {
+      padding: 4px 0;
+      display: flex;
+      justify-content: space-between;
+      .name {
+        max-width: 200px;
+      }
+    }
+  }
+}
+</style>

+ 8 - 2
src/views/server/manage/components/Pem.vue

@@ -1,6 +1,6 @@
 <template>
-  <lay-layer :title="modelConfig.title" v-model="modelConfig.visible" :area="['1200px', '600px']">
-    <lay-card class="custom-card max-height">
+  <lay-layer :title="modelConfig.title" v-model="modelConfig.visible" area="auto">
+    <lay-card class="custom-card max-height width-1200">
       <div class="operator-wp">
         <lay-button class="custom-button-primary" v-if="apiList?.includes('/serverAuth/save')" @click="handleUpdate()">添加PEM</lay-button>
       </div>
@@ -137,3 +137,9 @@ const handleCurrentChange = (val: any) => {
 
 defineExpose({ show });
 </script>
+
+<style lang="scss" scoped>
+.custom-card{
+  padding-bottom: 60px;
+}
+</style>

+ 55 - 0
src/views/server/manage/components/Result.vue

@@ -0,0 +1,55 @@
+<template>
+  <lay-layer :title="modelConfig.title" v-model="modelConfig.visible" area="auto">
+    <div class="width-800" style="padding: 20px">
+      <div class="result-name">服务器名称:{{ modelParams.serverName }}</div>
+      <div class="result-content">
+        <div class="content-title">执行结果</div>
+        <div class="content" v-html="modelParams.result?.replace(/\n/g, '<br />')"></div>
+      </div>
+    </div>
+  </lay-layer>
+</template>
+
+<script lang="ts" setup>
+import { ref, reactive } from "vue";
+
+interface ModelConfig {
+  title: string;
+  visible: boolean;
+  isUpdate: boolean;
+  loading: boolean;
+}
+interface ModelParams {
+  result?: string;
+  serverId?: number;
+  serverName?: string;
+}
+
+let modelParams = ref<ModelParams>({});
+let modelConfig: ModelConfig = reactive({ title: "", visible: false, isUpdate: false, loading: false });
+
+const show = (params?: any) => {
+  modelConfig.visible = true;
+  modelConfig.title = "执行结果";
+  modelParams.value = { ...params };
+};
+
+defineExpose({ show });
+</script>
+<style lang="scss" scoped>
+.result-name {
+  font-weight: bold;
+}
+.result-content {
+  padding-top: 10px;
+  .content-title {
+    font-weight: bold;
+    padding-bottom: 4px;
+  }
+  .content {
+    padding: 10px;
+    background-color: black;
+    color: white;
+  }
+}
+</style>

+ 33 - 30
src/views/server/manage/components/Update.vue

@@ -1,6 +1,6 @@
 <template>
-  <lay-layer :title="modelConfig.title" v-model="modelConfig.visible" :area="['500px', '650px']" :btn="operator">
-    <div style="padding: 20px">
+  <lay-layer :title="modelConfig.title" v-model="modelConfig.visible" area="auto" :btn="operator">
+    <div class="width-500" style="padding: 20px">
       <lay-form :model="modelParams" ref="modelFormRef" required>
         <lay-form-item label="名称" prop="name">
           <lay-input v-model="modelParams.name" />
@@ -16,7 +16,7 @@
             <lay-select-option :value="0" label="Ubuntu" />
           </lay-select>
         </lay-form-item>
-        <lay-form-item label="登录方式" prop="code">
+        <lay-form-item label="登录方式" prop="loginType">
           <lay-radio v-model="modelParams.loginType" name="action" :value="0" label="账号密码" />
           <lay-radio v-model="modelParams.loginType" name="action" :value="1" label="PEM" />
         </lay-form-item>
@@ -31,7 +31,7 @@
             <lay-select-option v-for="item in pemList" :value="item.id" :label="item.value" />
           </lay-select>
         </lay-form-item>
-        <lay-form-item label="备注" prop="textarea">
+        <lay-form-item label="备注">
           <lay-textarea v-model="modelParams.remark" />
         </lay-form-item>
       </lay-form>
@@ -45,6 +45,8 @@ import { add_server, update_server, get_pem_list_all } from "@/api";
 
 const { proxy }: any = getCurrentInstance();
 
+const modelFormRef = ref();
+
 interface ModelConfig {
   title: string;
   visible: boolean;
@@ -75,7 +77,7 @@ const show = (type: number, params?: any) => {
   get_pem_list();
   modelConfig.visible = true;
   modelConfig.isUpdate = !!params && type != 2;
-  modelConfig.title = type == 1 ? "编辑服务器" : "添加服务器";
+  modelConfig.title = type == 1 ? "编辑服务器" : type == 2 ? "复制服务器" : "添加服务器";
   modelParams.value = type != 0 ? { ...params } : { loginType: 0, osType: 0 };
   if (type == 2) delete modelParams.value.userServerId;
   return new Promise(async (resolve, reject) => {
@@ -96,32 +98,33 @@ const operator = reactive([
   {
     text: "确认",
     callback: () => {
-      modelConfig.loading = true;
-      if (modelConfig.isUpdate) {
-        const params = { ...modelParams.value };
-        if (params.loginType == 0) delete params.pemId;
-        if (params.loginType == 1) delete params.passwordComplex;
-        update_server(params, (data: any) => {
-          modelConfig.loading = false;
-          if (data.code == 200) {
-            proxy.$message("编辑成功!");
-            modelConfig.visible = false;
-            handleResult.resolve(true);
-          }
-        });
-      } else {
-        const params = { ...modelParams.value };
-        if (params.loginType == 0) delete params.pemId;
-        if (params.loginType == 1) delete params.passwordComplex;
-        add_server([params], (data: any) => {
-          modelConfig.loading = false;
-          if (data.code == 200) {
-            proxy.$message("添加成功!");
-            modelConfig.visible = false;
-            handleResult.resolve(true);
+      modelFormRef.value.validate((isValidate: boolean) => {
+        if (isValidate) {
+          const params = { ...modelParams.value };
+          if (params.loginType == 0) delete params.pemId;
+          if (params.loginType == 1) delete params.passwordComplex;
+          modelConfig.loading = true;
+          if (modelConfig.isUpdate) {
+            update_server(params, (data: any) => {
+              modelConfig.loading = false;
+              if (data.code == 200) {
+                proxy.$message("编辑成功!");
+                modelConfig.visible = false;
+                handleResult.resolve(true);
+              }
+            });
+          } else {
+            add_server([params], (data: any) => {
+              modelConfig.loading = false;
+              if (data.code == 200) {
+                proxy.$message("添加成功!");
+                modelConfig.visible = false;
+                handleResult.resolve(true);
+              }
+            });
           }
-        });
-      }
+        }
+      });
     },
   },
   {

+ 29 - 25
src/views/server/manage/components/UpdatePem.vue

@@ -1,6 +1,6 @@
 <template>
-  <lay-layer :title="modelConfig.title" v-model="modelConfig.visible" :area="['500px', '550px']" :btn="operator">
-    <div style="padding: 20px">
+  <lay-layer :title="modelConfig.title" v-model="modelConfig.visible" area="" :btn="operator">
+    <div class="width-500" style="padding: 20px">
       <lay-form :model="modelParams" ref="modelFormRef" required>
         <lay-form-item label="名称" prop="name">
           <lay-input v-model="modelParams.name" />
@@ -8,7 +8,7 @@
         <lay-form-item label="PEM" prop="val">
           <DragUpload v-model="modelParams.val" :status="uploadStatus" :beforeUpload="handleUpload" />
         </lay-form-item>
-        <lay-form-item label="备注" prop="remark">
+        <lay-form-item label="备注">
           <lay-textarea v-model="modelParams.remark" />
         </lay-form-item>
       </lay-form>
@@ -23,6 +23,8 @@ import { add_pem, update_pem } from "@/api";
 
 const { proxy }: any = getCurrentInstance();
 
+const modelFormRef = ref();
+
 interface ModelConfig {
   title: string;
   visible: boolean;
@@ -78,29 +80,31 @@ const operator = reactive([
   {
     text: "确认",
     callback: () => {
-      modelConfig.loading = true;
-      console.log(modelParams.value);
-      if (modelConfig.isUpdate) {
-        const params = { ...modelParams.value };
-        update_pem(params, (data: any) => {
-          modelConfig.loading = false;
-          if (data.code == 200) {
-            proxy.$message("编辑成功!");
-            modelConfig.visible = false;
-            handleResult.resolve(true);
-          }
-        });
-      } else {
-        const params = { ...modelParams.value };
-        add_pem(params, (data: any) => {
-          modelConfig.loading = false;
-          if (data.code == 200) {
-            proxy.$message("添加成功!");
-            modelConfig.visible = false;
-            handleResult.resolve(true);
+      modelFormRef.value.validate((isValidate: boolean) => {
+        if (isValidate) {
+          const params = { ...modelParams.value };
+          modelConfig.loading = true;
+          if (modelConfig.isUpdate) {
+            update_pem(params, (data: any) => {
+              modelConfig.loading = false;
+              if (data.code == 200) {
+                proxy.$message("编辑成功!");
+                modelConfig.visible = false;
+                handleResult.resolve(true);
+              }
+            });
+          } else {
+            add_pem(params, (data: any) => {
+              modelConfig.loading = false;
+              if (data.code == 200) {
+                proxy.$message("添加成功!");
+                modelConfig.visible = false;
+                handleResult.resolve(true);
+              }
+            });
           }
-        });
-      }
+        }
+      });
     },
   },
   {

+ 3 - 2
src/views/server/manage/index.vue

@@ -144,8 +144,9 @@ const handleCommand = async (value?: any) => {
   if (value) {
     userServerId = [value.userServerId];
   } else {
-    userServerId = selectedKeys.value
+    userServerId = selectedKeys.value;
   }
+  if (userServerId.length <= 0) return proxy.$message(`请先选择要执行命令服务器!`, 7);
   await commandRef.value.show(userServerId);
 };
 
@@ -156,7 +157,7 @@ const handleTest = async (value: any) => {
   test_connect_server(params, (data: any) => {
     pageConfig.loading = false;
     if (data.code == 200) {
-      proxy.$message(`连接成功!`);
+      data.data ? proxy.$message(`连接成功!`) : proxy.$message(`连接失败!`, 2);
     }
   });
 };

+ 29 - 24
src/views/system/organization/components/Update.vue

@@ -1,6 +1,6 @@
 <template>
-  <lay-layer :title="modelConfig.title" v-model="modelConfig.visible" :area="['500px', '450px']" :btn="operator">
-    <div style="padding: 20px">
+  <lay-layer :title="modelConfig.title" v-model="modelConfig.visible" area="auto" :btn="operator">
+    <div class="width-500" style="padding: 20px">
       <lay-form :model="modelParams" ref="modelFormRef" required>
         <lay-form-item label="组织名称" prop="groupName">
           <lay-input v-model="modelParams.groupName" />
@@ -8,7 +8,7 @@
         <lay-form-item label="组织编码" prop="code">
           <lay-input v-model="modelParams.code" />
         </lay-form-item>
-        <lay-form-item label="描述" prop="describe">
+        <lay-form-item label="描述">
           <lay-textarea placeholder="请输入描述" v-model="modelParams.describe" />
         </lay-form-item>
       </lay-form>
@@ -22,6 +22,8 @@ import { add_group, update_group } from "@/api";
 
 const { proxy }: any = getCurrentInstance();
 
+const modelFormRef = ref();
+
 interface ModelConfig {
   title: string;
   visible: boolean;
@@ -58,28 +60,31 @@ const operator = ref([
   {
     text: "确认",
     callback: () => {
-      modelConfig.loading = true;
-      if (modelConfig.isUpdate) {
-        const params = { ...modelParams.value };
-        update_group(params, (data: any) => {
-          modelConfig.loading = false;
-          if (data.code == 200) {
-            proxy.$message("编辑成功!");
-            modelConfig.visible = false;
-            handleResult.resolve(true);
-          }
-        });
-      } else {
-        const params = { ...modelParams.value };
-        add_group(params, (data: any) => {
-          modelConfig.loading = false;
-          if (data.code == 200) {
-            proxy.$message("添加成功!");
-            modelConfig.visible = false;
-            handleResult.resolve(true);
+      modelFormRef.value.validate((isValidate: boolean) => {
+        if (isValidate) {
+          const params = { ...modelParams.value };
+          modelConfig.loading = true;
+          if (modelConfig.isUpdate) {
+            update_group(params, (data: any) => {
+              modelConfig.loading = false;
+              if (data.code == 200) {
+                proxy.$message("编辑成功!");
+                modelConfig.visible = false;
+                handleResult.resolve(true);
+              }
+            });
+          } else {
+            add_group(params, (data: any) => {
+              modelConfig.loading = false;
+              if (data.code == 200) {
+                proxy.$message("添加成功!");
+                modelConfig.visible = false;
+                handleResult.resolve(true);
+              }
+            });
           }
-        });
-      }
+        }
+      });
     },
   },
   {

+ 30 - 24
src/views/system/user/components/Update.vue

@@ -1,6 +1,6 @@
 <template>
-  <lay-layer :title="modelConfig.title" v-model="modelConfig.visible" :area="['500px', '450px']" :btn="operator">
-    <div style="padding: 20px">
+  <lay-layer :title="modelConfig.title" v-model="modelConfig.visible" area="auto" :btn="operator">
+    <div class="width-500" style="padding: 20px">
       <lay-form :model="modelParams" ref="modelFormRef" required>
         <lay-form-item label="昵称" prop="name">
           <lay-input v-model="modelParams.name" />
@@ -13,7 +13,7 @@
             <lay-select-option v-for="item in groupSelectList" :value="item.id" :label="item.value" />
           </lay-select>
         </lay-form-item>
-        <lay-form-item label="备注" prop="remark">
+        <lay-form-item label="备注">
           <lay-textarea placeholder="请输入备注" v-model="modelParams.remark" />
         </lay-form-item>
       </lay-form>
@@ -27,6 +27,8 @@ import { add_user, update_user, get_group_select_list } from "@/api";
 
 const { proxy }: any = getCurrentInstance();
 
+const modelFormRef = ref();
+
 interface ModelConfig {
   title: string;
   visible: boolean;
@@ -76,28 +78,32 @@ const operator = reactive([
   {
     text: "确认",
     callback: () => {
-      modelConfig.loading = true;
-      if (modelConfig.isUpdate) {
-        const params = { ...modelParams.value };
-        update_user(params, (data: any) => {
-          modelConfig.loading = false;
-          if (data.code == 200) {
-            proxy.$message("编辑成功!");
-            modelConfig.visible = false;
-            handleResult.resolve(true);
-          }
-        });
-      } else {
-        const params = { ...modelParams.value };
-        add_user(params, (data: any) => {
-          modelConfig.loading = false;
-          if (data.code == 200) {
-            proxy.$confirm(`新用户密码为:${data.data}`);
-            modelConfig.visible = false;
-            handleResult.resolve(true);
+      modelFormRef.value.validate((isValidate: boolean) => {
+        if (isValidate) {
+          modelConfig.loading = true;
+          if (modelConfig.isUpdate) {
+            const params = { ...modelParams.value };
+            update_user(params, (data: any) => {
+              modelConfig.loading = false;
+              if (data.code == 200) {
+                proxy.$message("编辑成功!");
+                modelConfig.visible = false;
+                handleResult.resolve(true);
+              }
+            });
+          } else {
+            const params = { ...modelParams.value };
+            add_user(params, (data: any) => {
+              modelConfig.loading = false;
+              if (data.code == 200) {
+                proxy.$confirm(`新用户密码为:${data.data}`);
+                modelConfig.visible = false;
+                handleResult.resolve(true);
+              }
+            });
           }
-        });
-      }
+        }
+      });
     },
   },
   {

+ 3 - 2
src/views/system/webpage/components/Operator.vue

@@ -1,6 +1,6 @@
 <template>
-  <lay-layer :title="modelConfig.title" v-model="modelConfig.visible" :area="['1200px', '700px']">
-    <lay-card class="custom-card">
+  <lay-layer :title="modelConfig.title" v-model="modelConfig.visible" area="auto">
+    <lay-card class="custom-card width-1000">
       <div class="operator-wp">
         <lay-button class="custom-button-primary" v-if="apiList?.includes('/menu/save')" @click="handleUpdate()">添加</lay-button>
       </div>
@@ -122,6 +122,7 @@ defineExpose({ show });
 
 <style lang="scss" scoped>
 .custom-card {
+  padding-bottom: 60px;
   height: 100%;
 }
 .operator-wp {

+ 29 - 24
src/views/system/webpage/components/Update.vue

@@ -1,6 +1,6 @@
 <template>
-  <lay-layer :title="modelConfig.title" v-model="modelConfig.visible" :area="['500px', '450px']" :btn="operator">
-    <div style="padding: 20px">
+  <lay-layer :title="modelConfig.title" v-model="modelConfig.visible" area="auto" :btn="operator">
+    <div class="width-500" style="padding: 20px">
       <lay-form :model="modelParams" ref="modelFormRef">
         <lay-form-item label="页面名称" prop="name" required>
           <lay-input v-model="modelParams.name" />
@@ -29,6 +29,8 @@ import { add_menu, update_menu } from "@/api";
 
 const { proxy }: any = getCurrentInstance();
 
+const modelFormRef = ref();
+
 interface ModelConfig {
   title: string;
   visible: boolean;
@@ -81,29 +83,32 @@ const operator = reactive([
   {
     text: "确认",
     callback: () => {
-      modelConfig.loading = true;
-      if (modelConfig.isUpdate) {
-        const params = { ...modelParams.value };
-        update_menu(params, (data: any) => {
-          modelConfig.loading = false;
-          if (data.code == 200) {
-            proxy.$message("编辑成功!");
-            modelConfig.visible = false;
-            handleResult.resolve(true);
-          }
-        });
-      } else {
-        const params = { ...modelParams.value };
-        delete params.menuId;
-        add_menu(params, (data: any) => {
-          modelConfig.loading = false;
-          if (data.code == 200) {
-            proxy.$message("添加成功!");
-            modelConfig.visible = false;
-            handleResult.resolve(true);
+      modelFormRef.value.validate((isValidate: boolean) => {
+        if (isValidate) {
+          const params = { ...modelParams.value };
+          modelConfig.loading = true;
+          if (modelConfig.isUpdate) {
+            update_menu(params, (data: any) => {
+              modelConfig.loading = false;
+              if (data.code == 200) {
+                proxy.$message("编辑成功!");
+                modelConfig.visible = false;
+                handleResult.resolve(true);
+              }
+            });
+          } else {
+            delete params.menuId;
+            add_menu(params, (data: any) => {
+              modelConfig.loading = false;
+              if (data.code == 200) {
+                proxy.$message("添加成功!");
+                modelConfig.visible = false;
+                handleResult.resolve(true);
+              }
+            });
           }
-        });
-      }
+        }
+      });
     },
   },
   {

+ 28 - 23
src/views/system/webpage/components/UpdateOperator.vue

@@ -1,6 +1,6 @@
 <template>
-  <lay-layer :title="modelConfig.title" v-model="modelConfig.visible" :area="['500px', '450px']" :btn="operator">
-    <div style="padding: 20px">
+  <lay-layer :title="modelConfig.title" v-model="modelConfig.visible" area="auto" :btn="operator">
+    <div class="width-500" style="padding: 20px">
       <lay-form :model="modelParams" ref="modelFormRef" required>
         <lay-form-item label="操作名称" prop="name">
           <lay-input v-model="modelParams.name" />
@@ -22,6 +22,8 @@ import { add_menu, update_menu } from "@/api";
 
 const { proxy }: any = getCurrentInstance();
 
+const modelFormRef = ref();
+
 interface ModelConfig {
   title: string;
   visible: boolean;
@@ -60,28 +62,31 @@ const operator = reactive([
   {
     text: "确认",
     callback: () => {
-      modelConfig.loading = true;
-      if (modelConfig.isUpdate) {
-        const params = { ...modelParams.value };
-        update_menu(params, (data: any) => {
-          modelConfig.loading = false;
-          if (data.code == 200) {
-            proxy.$message("编辑成功!");
-            modelConfig.visible = false;
-            handleResult.resolve(true);
-          }
-        });
-      } else {
-        const params = { ...modelParams.value };
-        add_menu(params, (data: any) => {
-          modelConfig.loading = false;
-          if (data.code == 200) {
-            proxy.$message("添加成功!");
-            modelConfig.visible = false;
-            handleResult.resolve(true);
+      modelFormRef.value.validate((isValidate: boolean) => {
+        if (isValidate) {
+          const params = { ...modelParams.value };
+          modelConfig.loading = true;
+          if (modelConfig.isUpdate) {
+            update_menu(params, (data: any) => {
+              modelConfig.loading = false;
+              if (data.code == 200) {
+                proxy.$message("编辑成功!");
+                modelConfig.visible = false;
+                handleResult.resolve(true);
+              }
+            });
+          } else {
+            add_menu(params, (data: any) => {
+              modelConfig.loading = false;
+              if (data.code == 200) {
+                proxy.$message("添加成功!");
+                modelConfig.visible = false;
+                handleResult.resolve(true);
+              }
+            });
           }
-        });
-      }
+        }
+      });
     },
   },
   {