From 089bf5d2378b3c4a61d795b2a92bede2c193b771 Mon Sep 17 00:00:00 2001
From: admin <344137771@qq.com>
Date: Tue, 06 Jan 2026 11:22:58 +0800
Subject: [PATCH] 1

---
 src/views/modules/etf-spots/market-linek.vue |  614 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 614 insertions(+), 0 deletions(-)

diff --git a/src/views/modules/etf-spots/market-linek.vue b/src/views/modules/etf-spots/market-linek.vue
new file mode 100644
index 0000000..0c8f64c
--- /dev/null
+++ b/src/views/modules/etf-spots/market-linek.vue
@@ -0,0 +1,614 @@
+<template>
+  <el-dialog
+    title="当天分时行情K线设置"
+    :close-on-click-modal="false"
+    :visible.sync="visible"
+    width="1200px"
+    @close="handClose"
+  >
+    <el-form
+      :model="dataForm"
+      :rules="dataRule"
+      ref="dataForm"
+      @keyup.enter.native="dataFormSubmit()"
+      label-width="120px"
+    >
+      <el-form-item prop="">
+        <div class="green">请先生成K线,才能保存行情配置</div>
+      </el-form-item>
+
+      <el-row>
+        <el-col :span="7">
+          <el-form-item label="开始时间" prop="openTimeTs">
+            <el-date-picker
+              v-model="dataForm.openTimeTs"
+              type="date"
+              value-format="yyyy-MM-dd"
+              :picker-options="startPickerOptions"
+              @change="handleStartTimeChange"
+              placeholder="选择日期时间"
+            >
+            </el-date-picker>
+          </el-form-item>
+        </el-col>
+        <el-col :span="7">
+          <el-form-item label="结束时间" prop="closeTimeTs">
+            <el-date-picker
+              v-model="dataForm.closeTimeTs"
+              disabled
+              type="date"
+              value-format="yyyy-MM-dd"
+              :picker-options="endPickerOptions"
+              placeholder="选择日期时间"
+            >
+            </el-date-picker>
+          </el-form-item>
+        </el-col>
+        <el-col :span="5">
+          <el-form-item label="交易对" prop="symbol">
+            <el-select
+              v-model="options.symbol"
+              placeholder="请选择"
+              @change="changeVal()"
+            >
+              <el-option
+                v-for="item in options"
+                :key="item.symbol"
+                :label="item.symbol"
+                :value="item.symbol"
+              >
+              </el-option>
+            </el-select>
+          </el-form-item>
+        </el-col>
+        <el-col :span="5">
+          <el-form-item label="">
+            <el-button type="primary" @click="dataFormSubmitOne()"
+              >生成K线</el-button
+            >
+          </el-form-item>
+        </el-col>
+      </el-row>
+      <el-row>
+        <el-col :span="7">
+          <el-form-item label="今日开盘价" prop="openPrice">
+            <el-input
+              v-model="dataForm.openPrice"
+              placeholder="今日开盘价"
+            ></el-input>
+          </el-form-item>
+        </el-col>
+        <el-col :span="7">
+          <el-form-item label="今日收盘价" prop="closePrice">
+            <el-input
+              v-model="dataForm.closePrice"
+              placeholder="今日收盘价"
+            ></el-input>
+          </el-form-item>
+        </el-col>
+        <el-col :span="5">
+          <el-form-item label="控盘策略">
+            <el-select
+              v-model="langug.value"
+              placeholder="请选择"
+              @change="changeVal()"
+            >
+              <el-option
+                v-for="item in langug"
+                :key="item.value"
+                :label="item.label"
+                :value="item.value"
+              >
+              </el-option>
+            </el-select>
+          </el-form-item>
+        </el-col>
+      </el-row>
+      <el-row>
+        <el-col :span="7">
+          <el-form-item label="今日最低价格" prop="low">
+            <el-input
+              v-model="dataForm.low"
+              placeholder="今日最低价格"
+            ></el-input>
+          </el-form-item>
+        </el-col>
+        <el-col :span="7">
+          <el-form-item label="今日最高价格" prop="high">
+            <el-input
+              v-model="dataForm.high"
+              placeholder="今日最高价格"
+            ></el-input>
+          </el-form-item>
+        </el-col>
+      </el-row>
+      <el-row>
+        <el-col :span="5">
+          <el-form-item label="今日总成交量" prop="turnoverLow">
+            <el-input
+              v-model="dataForm.turnoverLow"
+              placeholder="最低"
+            ></el-input>
+          </el-form-item>
+        </el-col>
+        <el-col :span="1">
+          <el-form-item label="" prop="">
+            <el-input disabled placeholder="~" class="spann"></el-input>
+          </el-form-item>
+        </el-col>
+        <el-col :span="5">
+          <el-form-item label="" prop="turnoverHigh">
+            <el-input
+              v-model="dataForm.turnoverHigh"
+              placeholder="最高"
+            ></el-input>
+          </el-form-item>
+        </el-col>
+      </el-row>
+      <el-form-item label="" prop="">
+        <el-button @click="visible = false">取消</el-button>
+        <el-button type="primary" v-if="isShow" @click="dataFormSubmit()"
+          >保存</el-button
+        >
+      </el-form-item>
+    </el-form>
+
+    <div class="mod-subscribe-general">
+      <avue-crud
+        ref="crud"
+        :page.sync="page"
+        :data="dataList"
+        :option="tableOption"
+        @on-load="getDataList"
+      >
+        <!-- <template slot="menuLeft">
+          <el-button
+            type="primary"
+            icon="el-icon-plus"
+            size="small"
+            v-if="isAuth('sys:user:save')"
+            @click.stop="addOrUpdateHandle(options)"
+            >新增</el-button
+          >
+        </template> -->
+        <template slot-scope="scope" slot="openTimeTss">
+          <span>{{ formatTimestamm(scope.row.openTimeTs) }}</span>
+        </template>
+        <template slot-scope="scope" slot="closeTimeTss">
+          <span>{{ formatTimestamm(scope.row.closeTimeTs) }}</span>
+        </template>
+        <template slot-scope="scope" slot="menu">
+          <!-- <el-button
+            type="primary"
+            icon="el-icon-edit"
+            size="small"
+            v-if="isAuth('sys:user:delete')"
+            @click.stop="addOrUpdateHandle(options,scope.row)"
+            >编辑</el-button
+          > -->
+          <el-button
+            type="danger"
+            icon="el-icon-delete"
+            size="small"
+            @click.stop="deleteHandle(scope.row.uuid)"
+            >删除</el-button
+          >
+        </template>
+      </avue-crud>
+      <!-- 新增删除 -->
+      <linKeAdd
+        v-if="linKeAddVisible"
+        ref="linKeAdd"
+        @refreshDataList="getDataList"
+      ></linKeAdd>
+    </div>
+    <echarts ref="addOrUpdate"> </echarts>
+    <span slot="footer" class="dialog-footer">
+      <el-button @click="visible = false">取消</el-button>
+      <el-button type="primary" @click="visible = false">确定</el-button>
+    </span>
+  </el-dialog>
+</template>
+
+<script>
+import { tableOption } from "@/crud/etf-spots/market-linek";
+import linKeAdd from "./linKen-add-or-undata";
+import { treeDataTranslate } from "@/utils";
+import { Debounce } from "@/utils/debounce";
+import { encrypt } from "@/utils/crypto";
+import echarts from "./echats_One.vue";
+export default {
+  data() {
+    return {
+      visible: false,
+      linKeAddVisible: false,
+      menuList: [],
+      dateRange: [],
+      isShow: false,
+      menuListTreeProps: {
+        label: "name",
+        children: "children",
+      },
+      startPickerOptions: {
+        disabledDate: this.disabledStartDate, // 禁用不满足条件的开始日期
+        disabledHours: this.disabledStartHours, // 禁用不满足条件的开始小时
+        disabledMinutes: this.disabledStartMinutes, // 禁用不满足条件的开始分钟
+        shortcuts: [
+          {
+            text: "明天",
+            onClick: () => {
+              const tomorrow = new Date();
+              tomorrow.setDate(tomorrow.getDate() + 1);
+              tomorrow.setHours(4, 0, 0, 0);
+              this.dataForm.openTimeTs = tomorrow;
+              this.handleStartTimeChange();
+            },
+          },
+        ],
+      },
+      endPickerOptions: {
+        disabledDate: this.disabledEndDate, // 禁用不满足条件的结束日期
+      },
+      tableOption: tableOption,
+      page: {
+        total: 0, // 总页数
+        currentPage: 1, // 当前页数
+        pageSize: 10, // 每页显示多少条
+      },
+      dataList: [],
+      row: "", //forex->外汇,commodities->大宗商品,指数/ETF->indices, A-stocks->A股, HK-stocks->港股.US-stocks->美股,cryptos->虚拟货币
+      options: [],
+      linArr: [], //K线数组
+      timeArr: [],
+      langug: [
+        {
+          label: "跟随大盘",
+          value: "1",
+        },
+        {
+          label: "自定义趋势",
+          value: "2",
+        },
+      ], // 控盘策略
+      dataForm: {
+        closePrice: "",
+        closeTimeTs: "",
+        createBy: "",
+        createDate: "",
+        createTime: "",
+        high: "",
+        low: "",
+        openPrice: "",
+        openTimeTs: "",
+        robot_model_uuid: "",
+        strategy: "",
+        symbol: "",
+        turnoverHigh: "",
+        turnoverLow: "",
+        updateBy: "",
+        updateDate: "",
+        updateTime: "",
+        uuid: "",
+      },
+      dataRule: {
+        openTimeTs: [
+          { required: true, message: "开始时间不能为空", trigger: "blur" },
+        ],
+        closeTimeTs: [
+          { required: true, message: "结束时间不能为空", trigger: "blur" },
+        ],
+        openPrice: [
+          { required: true, message: "今日开盘价不能为空", trigger: "blur" },
+        ],
+        closePrice: [
+          { required: true, message: "今日收盘价不能为空", trigger: "blur" },
+        ],
+        low: [
+          { required: true, message: "今日最低价不能为空", trigger: "blur" },
+        ],
+        high: [
+          { required: true, message: "今日最高价不能为空", trigger: "blur" },
+        ],
+        turnoverLow: [
+          { required: true, message: "最低不能为空", trigger: "blur" },
+        ],
+        turnoverHigh: [
+          { required: true, message: "最高不能为空", trigger: "blur" },
+        ],
+        quoteCurrency: [
+          { required: true, message: "结算币种不能为空", trigger: "blur" },
+        ],
+        unitFee: [
+          { required: true, message: "手续费不能为空", trigger: "blur" },
+        ],
+        // sorted: [{ required: true, message: "排序不能为空", trigger: "blur" }],
+        // sorted: [{ required: true, message: "排序不能为空", trigger: "blur" }],
+        // sorted: [{ required: true, message: "排序不能为空", trigger: "blur" }],
+      },
+      tempKey: -666666, // 临时key, 用于解决tree半选中状态项不能传给后台接口问题. # 待优化
+    };
+  },
+  components: {
+    echarts,
+    linKeAdd,
+  },
+  methods: {
+    init(arr) {
+      this.options = arr || [];
+      this.options.symbol = this.options[0].symbol;
+      this.langug.value = this.langug[0].value;
+      this.visible = true;
+    },
+    changeVal(val) {
+      this.$forceUpdate();
+    },
+    addOrUpdateHandle(arr, row) {
+      this.linKeAddVisible = true;
+      this.$nextTick(() => {
+        this.$refs.linKeAdd.init(arr, row);
+      });
+    },
+    disabledStartDate(time) {
+      const today = new Date();
+      today.setHours(0, 0, 0, 0);
+      return time.getTime() <= today.getTime(); // 开始时间只能选择明天及以后的日期
+    },
+    disabledStartHours() {
+      const currentHour = new Date().getHours();
+      return Array.from({ length: 24 }, (_, i) => i).filter(
+        (hour) => hour < 4 || hour > 21 || (hour === 21 && currentHour >= 30) // 开始时间的小时部分只能选择凌晨4点到晚上21点半
+      );
+    },
+    disabledStartMinutes(hour) {
+      const currentHour = new Date().getHours();
+      if (hour === 4 && currentHour < 30) {
+        // 如果选择的小时是4点且当前小时小于30,则禁用分钟选择
+        return Array.from({ length: 60 }, (_, i) => i).filter(
+          (minute) => minute !== 0
+        );
+      } else if (hour === 21 && currentHour >= 30) {
+        // 如果选择的小时是21点且当前小时大于等于30,则禁用分钟选择
+        return Array.from({ length: 60 }, (_, i) => i).filter(
+          (minute) => minute !== 0
+        );
+      } else {
+        // 其他情况不禁用分钟选择
+        return [];
+      }
+    },
+    disabledEndDate(time) {
+      return true; // 禁用结束时间选择
+    },
+    handleStartTimeChange() {
+      // 设置默认的结束时间为开始时间的后一天
+      const startDate = new Date(this.dataForm.openTimeTs);
+      const endDate = new Date(startDate.getTime() + 8.64e7); // 一天的毫秒数
+      this.dataForm.closeTimeTs = endDate;
+    },
+    handClose() {
+      this.$data.dataForm = JSON.parse(
+        JSON.stringify(this.$options.data().dataForm)
+      );
+      this.$nextTick(() => {
+        this.$refs["dataForm"].clearValidate(); // 清除表单验证
+      });
+      this.langug.value = "";
+      this.options.value = "";
+      this.isShow = false;
+    },
+    // 删除
+    deleteHandle(id) {
+      var ids = id
+        ? [id]
+        : this.dataListSelections.map((item) => {
+            return item.orderId;
+          });
+      this.$confirm(`确定进行[${id ? "删除" : "批量删除"}]操作?`, "提示", {
+        confirmButtonText: "确定",
+        cancelButtonText: "取消",
+        type: "warning",
+      })
+        .then(() => {
+          this.$http({
+            url: this.$http.adornUrl(`/etf/klineConfig/delete?ids=` + ids),
+            method: "delete",
+            data: this.$http.adornData(ids, false),
+          }).then(({ data }) => {
+            this.$message({
+              message: "操作成功",
+              type: "success",
+              duration: 1500,
+              onClose: () => {
+                this.getDataList(this.page);
+              },
+            });
+          });
+        })
+        .catch(() => {});
+    },
+    // 获取数据列表
+    getDataList(page, params, done) {
+      this.dataListLoading = true;
+      this.$http({
+        url: this.$http.adornUrl("/etf/klineConfig/pageList"),
+        method: "get",
+        params: this.$http.adornParams(
+          Object.assign(
+            {
+              type: "indices", //type:'forex', //forex->外汇,commodities->大宗商品,指数/ETF->indices, A-stocks->A股, HK-stocks->港股.US-stocks->美股,cryptos->虚拟货币
+              // itemId: this.dataForm.id,
+              current: page == null ? this.page.currentPage : page.currentPage,
+              size: page == null ? this.page.pageSize : page.pageSize,
+            },
+            params
+          )
+        ),
+      }).then(({ data }) => {
+        if (data.code == 0) {
+          console.log("data => " + data);
+          this.dataList = data.data.records;
+          this.page.total = data.data.total;
+        } else {
+          this.$message({
+            message: data.msg,
+            type: "error",
+            duration: 1000,
+            onClose: () => {},
+          });
+        }
+
+        this.dataListLoading = false;
+        if (done) {
+          done();
+        }
+      });
+    },
+
+    formatTimestamp(timestamp) {
+      const date = new Date(timestamp);
+      const year = date.getFullYear();
+      const month = String(date.getMonth() + 1).padStart(2, "0");
+      const day = String(date.getDate()).padStart(2, "0");
+      const hours = String(date.getHours()).padStart(2, "0");
+      const minutes = String(date.getMinutes()).padStart(2, "0");
+      const seconds = String(date.getSeconds()).padStart(2, "0");
+      const milliseconds = String(date.getMilliseconds()).padStart(3, "0");
+
+      const formattedDate = `${year}-${month}-${day} ${hours}:${minutes}:${seconds} ${milliseconds}`;
+      return formattedDate;
+    },
+    formatTimestamm(timestamp) {
+      const date = new Date(timestamp);
+      const year = date.getFullYear();
+      const month = String(date.getMonth() + 1).padStart(2, "0");
+      const day = String(date.getDate()).padStart(2, "0");
+      const hours = String(date.getHours()).padStart(2, "0");
+      const minutes = String(date.getMinutes()).padStart(2, "0");
+      const seconds = String(date.getSeconds()).padStart(2, "0");
+      const milliseconds = String(date.getMilliseconds()).padStart(3, "0");
+
+      const formattedDate = `${year}-${month}-${day}`;
+      return formattedDate;
+    },
+    // K线
+    dataFormSubmitOne: Debounce(function () {
+      this.$refs["dataForm"].validate((valid) => {
+        let a = new Date(this.dataForm.openTimeTs);
+        let b = new Date(this.dataForm.closeTimeTs);
+        let aa = a.getTime();
+        let bb = b.getTime();
+        if (valid) {
+          this.$http({
+            // 生成K线
+            url: this.$http.adornUrl(`/etf/klineConfig/init`),
+            method: "post",
+            data: this.$http.adornData({
+              closePrice: this.dataForm.closePrice,
+              // closeTimeTs: bb,
+              high: this.dataForm.high,
+              low: this.dataForm.low,
+              openPrice: this.dataForm.openPrice,
+              openTimeTs: aa,
+              symbol: this.options.symbol,
+              turnoverHigh: this.dataForm.turnoverHigh,
+              turnoverLow: this.dataForm.turnoverLow,
+              strategy: this.langug.value,
+              // uuid: ""
+            }),
+          }).then(({ data }) => {
+            if (data.code == 0) {
+              this.dataForm.uuid = data.data.modelId;
+              this.timeArr = Object.values(
+                data.data.klineList.reduce((res, item) => {
+                  //res[item.ts] = [item.ts];
+                  res[item.ts] = [this.formatTimestamp(item.ts)];
+                  return res;
+                }, [])
+              );
+              this.linArr = Object.values(
+                data.data.klineList.reduce((res, item) => {
+                  let arr = [item.open, item.close, item.low, item.high];
+                  res.push(arr);
+                  return res;
+                }, [])
+              );
+              this.$nextTick(() => {
+                this.$refs.addOrUpdate.init(this.timeArr, this.linArr);
+              });
+              this.$message({
+                message: "操作成功",
+                type: "success",
+                duration: 1500,
+                onClose: () => {},
+              });
+              this.isShow = true;
+            } else {
+              this.$message({
+                message: data.msg,
+                type: "error",
+                duration: 1500,
+                onClose: () => {},
+              });
+            }
+          });
+        }
+      });
+    }),
+
+    // 表单提交
+    dataFormSubmit: Debounce(function () {
+      this.$refs["dataForm"].validate((valid) => {
+        let a = new Date(this.dataForm.openTimeTs);
+        let b = new Date(this.dataForm.closeTimeTs);
+        let aa = a.getTime();
+        let bb = b.getTime();
+        if (valid) {
+          this.$http({
+            // 修改
+            url: this.$http.adornUrl(`/etf/klineConfig/save`),
+            method: "post",
+            data: this.$http.adornData({
+              closePrice: this.dataForm.closePrice,
+              // closeTimeTs: bb,
+              high: this.dataForm.high,
+              low: this.dataForm.low,
+              openPrice: this.dataForm.openPrice,
+              openTimeTs: aa,
+              symbol: this.options.symbol,
+              turnoverHigh: this.dataForm.turnoverHigh,
+              turnoverLow: this.dataForm.turnoverLow,
+              strategy: this.langug.value,
+              robot_model_uuid: this.dataForm.uuid,
+            }),
+          }).then(({ data }) => {
+            if (data.code == 0) {
+              this.$message({
+                message: "操作成功",
+                type: "success",
+                duration: 1500,
+                onClose: () => {
+                  this.getDataList();
+                  this.$emit("refreshDataList");
+                },
+              });
+            } else {
+              this.$message({
+                message: data.msg,
+                type: "error",
+                duration: 1500,
+                onClose: () => {},
+              });
+            }
+          });
+        }
+      });
+    }),
+  },
+};
+</script>
+<style scoped>
+.spann {
+  width: 40px;
+  margin-left: -60px;
+}
+</style>

--
Gitblit v1.9.3