This commit is contained in:
2025-03-12 16:35:54 +08:00
parent 1069707953
commit 443778cf36
2 changed files with 779 additions and 310 deletions

View File

@ -150,7 +150,7 @@
/>
<!-- 添加或修改对话框 -->
<el-dialog :title="title" :visible.sync="open" width="1200px" :close-on-click-modal="false">
<el-dialog :title="title" :visible.sync="open" width="1300px" :close-on-click-modal="false">
<el-form ref="form" :model="form" :rules="rules" label-width="80px">
<el-form-item label="模板名称" prop="name">
<el-input v-model="form.name" placeholder="模板名称" />
@ -428,109 +428,151 @@
@click="onCalculate"
>计算</el-button></el-col>
</el-row>
<div v-for="(item, index) in inForm.ext" :key="index" class="exts">
<el-row v-if="item.addType === 1" :gutter="20" style="margin-bottom: 20px;">
<el-col :span="5">
<div style="margin-bottom: 8px;">{{ item.addType === 1 ? '加仓类型' : '减仓类型' }}</div>
<el-radio-group v-model="item.orderType" size="mini">
<el-radio label="LIMIT">限价</el-radio>
<el-radio label="MARKET"></el-radio>
</el-radio-group>
</el-col>
<el-col :span="5">
<div style="margin-bottom: 8px;">{{ item.addType === 1 ? '加仓方式' : '减仓方式' }}</div>
<el-radio-group v-model="item.addPositionType" size="mini">
<el-radio :label="1">百分比</el-radio>
<el-radio :label="2" :disabled="item.addType === 2">实际金额</el-radio>
</el-radio-group>
</el-col>
<el-form ref="extForm" :model="inForm" label-position="top" :rules="inRules" class="ext-form">
<div v-for="(item, index) in inForm.ext" :key="index" class="exts">
<el-row v-if="item.addType === 1" :gutter="20" style="margin-bottom: 20px;">
<el-col :span="5">
<div style="margin-bottom: 8px;">{{ item.addType === 1 ? '加仓类型' : '减仓类型' }}</div>
<el-radio-group v-model="item.orderType" size="mini">
<el-radio label="LIMIT"></el-radio>
<el-radio label="MARKET">市价</el-radio>
</el-radio-group>
</el-col>
<el-col :span="5">
<div style="margin-bottom: 8px;">{{ item.addType === 1 ? '加仓方式' : '减仓方式' }}</div>
<el-radio-group v-model="item.addPositionType" size="mini">
<el-radio :label="1">百分比</el-radio>
<el-radio :label="2" :disabled="item.addType === 2">实际金额</el-radio>
</el-radio-group>
</el-col>
</el-row>
<el-row :gutter="20">
<el-col :span="3">{{ item.addType === 1 ? '加仓下跌百分比' : '减仓下跌百分比' }}
<el-input
v-model.number="item.priceRatio"
min="0"
size="mini"
type="number"
:placeholder="item.addType === 1 ? '加仓下跌百分比' : '减仓下跌百分比'"
/></el-col>
<el-col :span="3">{{ item.addType === 1 ? '加仓数值' : '减仓百分比' }}
<el-input
v-model.number="item.addPositionVal"
min="0"
size="mini"
type="number"
:placeholder="item.addType === 1 ? '加仓数值' : '减仓百分比'"
/>
</el-col>
<el-col :span="6">
<span>止盈/止损百分比</span>
<div>
<el-input
v-model="item.takeProfitRatio"
min="0"
style="width:50%;"
size="mini"
type="number"
placeholder="止盈百分比"
>
<template v-if="form.type === 1" #append>{{ ext[index] ? ext[index].reTakeProfitRatio : 0
}}</template>
</el-input>
<el-input
v-model.number="item.stopLossRatio"
style="width:42%;"
size="mini"
type="number"
placeholder="止损百分比"
/>
</div>
</el-col>
<!-- <el-col :span="3">止损百分比<el-input v-model.number="item.stopLossRatio" size="mini" type="number" placeholder="止损百分比" /></el-col> -->
<template v-if="item.addType === 1">
</el-row>
<el-row :gutter="20">
<el-col :span="3">
止盈数量百分比<el-input
v-model.number="item.takeProfitNumRatio"
size="mini"
type="number"
placeholder="止盈数量百分比"
/>
<!-- {{ item.addType === 1 ? '加仓下跌百分比' : '减仓下跌百分比' }} -->
<el-form-item
:prop="'ext.' + index + '.priceRatio'"
:rules="inRules.priceRatio"
class="ext-form-item"
:label="item.addType === 1 ? '加仓下跌百分比' : '减仓下跌百分比'"
>
<el-input
v-model.number="item.priceRatio"
min="0"
size="mini"
type="number"
:placeholder="item.addType === 1 ? '加仓下跌百分比' : '减仓下跌百分比'"
/>
</el-form-item>
</el-col>
<el-col :span="3">
<!-- {{ item.addType === 1 ? '加仓数值' : '减仓百分比' }} -->
<el-form-item
:prop="'ext.' + index + '.addPositionVal'"
:label="item.addType === 1 ? '加仓数值' : '减仓百分比'"
:rules="inRules.addPositionVal"
class="ext-form-item"
>
<el-input
v-model.number="item.addPositionVal"
min="0"
size="mini"
type="number"
:placeholder="item.addType === 1 ? '加仓数值' : '减仓百分比'"
/>
</el-form-item>
</el-col>
<el-col :span="6">
<span>第二止盈/止损百分比</span>
<div>
<el-input
v-model.number="item.tpTpPriceRatio"
min="0"
style="width:42%"
size="mini"
type="number"
placeholder="第二止盈价百分比"
/>
<el-input
v-model.number="item.tpSlPriceRatio"
min="0"
style="width:42%"
size="mini"
type="number"
placeholder="第二止损价百分比"
/>
</div>
<!-- <span>止盈/止损百分比</span> -->
<el-form-item
label="止盈/止损百分比"
:prop="'ext.' + index + '.takeProfitRatio'"
:rules="[{ validator: validateTakeProfitAndStopLoss, trigger: 'blur' }]"
class="ext-form-item"
>
<div>
<el-input
v-model="item.takeProfitRatio"
min="0"
style="width:50%;"
size="mini"
type="number"
placeholder="止盈百分比"
>
<template v-if="form.type === 1" #append>{{ ext[index] ? ext[index].reTakeProfitRatio : 0
}}</template>
</el-input>
<el-input
v-model.number="item.stopLossRatio"
style="width:42%;"
size="mini"
type="number"
placeholder="止损百分比"
/>
</div>
</el-form-item>
</el-col>
</template>
<el-col :span="3">
<div style="margin-bottom: 14px;" /><el-button
size="mini"
type="danger"
@click="onAddExt(1, index)"
>删除</el-button>
</el-col>
</el-row>
</div>
<!-- <el-col :span="3">止损百分比<el-input v-model.number="item.stopLossRatio" size="mini" type="number" placeholder="止损百分比" /></el-col> -->
<template v-if="item.addType === 1">
<el-col :span="3">
<!-- 止盈数量百分比 -->
<el-form-item
label="止盈数量百分比"
:prop="'ext.' + index + '.takeProfitNumRatio'"
:rules="inRules.takeProfitNumRatio"
class="ext-form-item"
>
<el-input
v-model.number="item.takeProfitNumRatio"
size="mini"
type="number"
placeholder="止盈数量百分比"
/>
</el-form-item>
</el-col>
<el-col :span="6">
<!-- <span>第二止盈/止损百分比</span> -->
<el-form-item
label="第二止盈/止损百分比"
:prop="'ext.' + index + '.tpTpPriceRatio'"
:rules="[{ validator: validateTp, trigger: 'blur' }]"
class="ext-form-item"
>
<div>
<el-input
v-model.number="item.tpTpPriceRatio"
min="0"
style="width:42%"
size="mini"
type="number"
placeholder="第二止盈价百分比"
/>
<el-input
v-model.number="item.tpSlPriceRatio"
min="0"
style="width:42%"
size="mini"
type="number"
placeholder="第二止损价百分比"
/>
</div>
</el-form-item>
</el-col>
</template>
<el-col :span="3">
<div style="margin-bottom: 14px;" /><el-button
size="mini"
type="danger"
@click="onAddExt(1, index)"
>删除</el-button>
</el-col>
</el-row>
</div>
</el-form>
<!-- <el-form-item label="是否保存模板" prop="save_template">
<el-radio-group v-model="inForm.save_template">
<el-radio label="0"></el-radio>
@ -636,17 +678,53 @@ export default {
percenter: undefined,
symbolGroups: [],
inRules: {
reduce_price: [{ required: true, message: '主单亏损减仓百分比不能为空', trigger: 'blur' }],
reduce_num: [{ required: true, message: '主单减仓数量百分比不能为空', trigger: 'blur' }],
reduce_take_profit: [{ required: true, message: '主单减仓后止盈价百分比不能为空', trigger: 'blur' }],
// reduce_stop_price: [{ required: true, message: '主单减仓后止损价不能为空', trigger: 'blur' }],
buy_price: [{ required: true, message: '购买金额不能为空', trigger: 'blur' }],
profit: [{ required: true, message: '请输入止盈百分比', trigger: 'blur' },
{ validator: this.validateProfit, trigger: 'blur' }],
reduce_price: [
{ required: true, message: '主单亏损减仓百分比不能为空', trigger: 'blur' },
{ validator: this.validateReducePrice, trigger: 'blur' }],
reduce_num: [
{ required: true, message: '主单减仓数量百分比不能为空', trigger: 'blur' },
{ validator: this.validateReduceNum, trigger: 'blur' }],
reduce_take_profit: [
{ required: true, message: '主单减仓后止盈价百分比不能为空', trigger: 'blur' },
{ validator: this.validateReduceTakeProfit, trigger: 'blur' }],
buy_price: [
{ required: true, message: '购买金额不能为空', trigger: 'blur' },
{ validator: this.validateBuyPrice, trigger: 'blur' }
],
exchange_type: [{ required: true, message: '交易所不能为空', trigger: 'blur' }],
api_id: [{ required: true, message: 'api用户不能为空', trigger: 'blur' }],
symbol: [{ required: true, message: '交易对不能为空', trigger: 'blur' }],
symbol_group_id: [{ required: true, message: '交易对组不能为空', trigger: 'blur' }],
quote_symbol: [{ required: true, message: '计货币不能为空', trigger: 'blur' }],
order_sn: [{ required: true, message: '订单号不能为空', trigger: 'blur' }]
quote_symbol: [{ required: true, message: '计货币不能为空', trigger: 'blur' }],
order_sn: [{ required: true, message: '订单号不能为空', trigger: 'blur' }],
profit_num_ratio: [
{ required: true, message: '止盈数量百分比不能为空', trigger: 'blur' },
{ validator: this.validateProfitNumRatio, trigger: 'blur' }
],
profit_tp_tp_price_ratio: [{ validator: this.validateTptpPriceRatio, trigger: 'blur' }],
priceRatio: [{ required: true, message: '下跌百分比不能为空', trigger: 'blur' }, { validator: this.validateExtPriceRatio, trigger: 'blur' }],
addPositionVal: [
{ required: true, message: '追加仓位不能为空', trigger: 'blur' },
{ validator: this.validateAddPosition, trigger: 'blur' }],
takeProfitRatio: [
{ required: true, message: '请输入止盈百分比', trigger: 'blur' },
{ type: 'number', message: '止盈百分比必须为数字', trigger: 'blur' },
{ min: 0, message: '止盈百分比不能小于 0', trigger: 'blur' }
],
stopLossRatio: [
{ required: true, message: '请输入止损百分比', trigger: 'blur' },
{ type: 'number', message: '止损百分比必须为数字', trigger: 'blur' },
{ min: 0, message: '止损百分比不能小于 0', trigger: 'blur' }
],
takeProfitNumRatio: [
{ required: true, message: '止盈数量不能为空', trigger: 'blur' },
{ validator: this.validateExtTakeProfitNumRatio, trigger: 'blur' }
],
tpTpPriceRatio: [{ required: true, message: '第二止盈价格百分比不能为空', trigger: 'blur' }],
slSlPriceRatio: [{ required: true, message: '第二止损价格百分比不能为空', trigger: 'blur' }]
},
inForm: {},
searchLoding: false,
@ -688,6 +766,145 @@ export default {
})
},
methods: {
validateExtTakeProfitNumRatio(rule, value, callback) {
if (value === 0 || value === undefined || value === null || value === '') {
callback(new Error('止盈数量百分比必须大于0'))
} else {
callback()
}
},
validateReducePrice(rule, value, callback) {
if (value === 0 || value === undefined || value === null || value === '') {
callback(new Error('亏损百分比必须大于0'))
} else {
callback()
}
},
validateBuyPrice(rule, value, callback) {
if (value === 0) {
callback(new Error('购买金额必须大于0'))
} else {
callback()
}
},
validateReduceTakeProfit(rule, value, callback) {
if (this.inForm.reduce_num > 0 && this.inForm.reduce_num < 100 && this.inForm.reduce_take_profit === 0) {
callback(new Error('主单减仓后止盈必须大于0'))
} else if (this.inForm.reduce_num === 100 && this.inForm.reduce_take_profit > 0) {
callback(new Error('主单减仓全部后无需止盈'))
} else {
callback()
}
},
/** 主单减仓数量 */
validateReduceNum(rule, value, callback) {
if (value <= 0) {
callback(new Error('主单减仓数量必须大于0'))
} else {
callback()
}
},
/** 主单止盈数量 */
validateProfitNumRatio(rule, value, callback) {
if (value <= 0) {
callback(new Error('止盈数量比例必须大于0'))
} else {
callback()
}
},
/** 验证第二止盈止损 */
validateTptpPriceRatio(rule, value, callback) {
if (!this.inForm) {
callback(new Error('表单未初始化'))
return
}
if (this.inForm.profit_num_ratio > 0 && this.inForm.profit_num_ratio < 100 && (!value || value <= 0)) {
callback(new Error('第二止盈百分比不能为空且必须大于 0'))
} else if (this.inForm.profit_num_ratio === 100 && value > 0) {
callback(new Error('第一止盈已经全部止盈了,不需要第二止盈百分比'))
} else {
callback()
}
},
validateProfit(rule, value, callback) {
if (this.inForm.profit === '' || this.inForm.profit === undefined || this.inForm.profit === null) {
callback(new Error('止盈百分比不能为空'))
} else if (this.inForm.profit <= 0) {
callback(new Error('止盈百分比不能小于0'))
} else {
callback()
}
},
validateExtPriceRatio(rule, value, callback) {
const index = rule.fullField.split('.')[1] // 获取索引
const item = this.inForm.ext[index]
const message = item.addType === 1 ? '加仓下跌百分比' : '减仓下跌百分比'
if (item.priceRatio === '') {
callback(new Error(message + '不能为空'))
} else if (isNaN(item.priceRatio)) {
callback(new Error('必须输入数字'))
} else if (item.priceRatio < 0) {
callback(new Error('百分比不能小于 0'))
}
if (index === 0 && item.priceRatio <= this.inForm.reduce_price) {
callback(new Error('下跌百分比不能小于上一节点'))
} else if (index > 0 && item.priceRatio <= this.inForm.ext[index - 1].priceRatio) {
callback(new Error('下跌百分比不能小于上一节点'))
} else {
callback()
}
},
validateAddPosition(rule, value, callback) {
const index = rule.fullField.split('.')[1] // 获取索引
const item = this.inForm.ext[index]
const message = item.addType === 1 ? '加仓数值' : '减仓百分比'
if (item.addPositionVal === '') {
callback(new Error(message + '不能为空'))
} else if (isNaN(item.addPositionVal)) {
callback(new Error('必须输入数字'))
} else if (item.addPositionVal <= 0) {
callback(new Error(message + '必须大于 0'))
} else {
callback() // 校验通过
}
},
// 自定义校验逻辑
validateTakeProfitAndStopLoss(rule, value, callback) {
const index = rule.fullField.split('.')[1] // 获取索引
const item = this.inForm.ext[index]
if (item.takeProfitRatio === '' || item.stopLossRatio === '') {
callback(new Error('止盈和止损百分比都不能为空'))
} else if (isNaN(item.takeProfitRatio) || isNaN(item.stopLossRatio)) {
callback(new Error('必须输入数字'))
} else if (item.takeProfitRatio < 0 || item.stopLossRatio < 0) {
callback(new Error('百分比不能小于 0'))
} else if (item.takeProfitRatio === 0) {
callback(new Error('止盈百分比不能为0'))
} else {
callback() // 校验通过
}
},
// 自定义 第二止盈止损校验
validateTp(rule, value, callback) {
const index = rule.fullField.split('.')[1] // 获取索引
const item = this.inForm.ext[index]
if (item.tpTpPriceRatio === '' || item.tpSlPriceRatio === '') {
callback(new Error('第二止盈、止损百分比不能为空'))
} else if (isNaN(item.tpTpPriceRatio) || isNaN(item.tpSlPriceRatio)) {
callback(new Error('必须输入数字'))
} else if (item.tpTpPriceRatio < 0 || item.tpSlPriceRatio < 0) {
callback(new Error('百分比不能小于 0'))
} else if (item.takeProfitNumRatio < 100 && item.takeProfitNumRatio > 0 && item.tpTpPriceRatio <= 0) {
callback(new Error('第二止盈百分比必须大于0'))
} else {
callback() // 校验通过
}
},
onChangeSymbolType() {
if (this.inForm.symbol_type === 1) {
this.inForm.cover_type = 0
@ -747,7 +964,7 @@ export default {
takeProfitRatio: 0,
stopLossRatio: 0,
// 止盈数量百分比 止盈后止盈价格百分比 止盈后止损价格百分比
takeProfitNumRatio: 0,
takeProfitNumRatio: 100,
tpTpPriceRatio: 0,
tpSlPriceRatio: 0
@ -812,7 +1029,7 @@ export default {
res = (1 + (this.percenter / 100)) * Number(this.inForm.price)
}
const [num, decimal] = String(res).split('.')
console.log(num, decimal, '?')
// console.log(num, decimal, '?')
if (decimal) {
this.inForm.price = decimal.length > 4 ? `${num}.${decimal.substring(0, 4)}` : res
} else {
@ -854,7 +1071,8 @@ export default {
userId: undefined,
params: undefined,
type: 1,
switch: '0'
switch: '0',
ext: []
}
this.resetForm('form')
},
@ -922,37 +1140,53 @@ export default {
},
/** 提交按钮 */
submitForm: function() {
this.$refs['form'].validate(valid => {
if (valid) {
this.inForm.price = this.inForm.price ? String(this.inForm.price) : ''
if (this.form.id !== undefined) {
this.$refs['inForm'].validate(valided => {
if (!valided) return false
const params = JSON.stringify({
...this.inForm,
reduce_price: this.inForm.reduce_price || 0,
reduce_num: this.inForm.reduce_num || 0,
reduce_take_profit: this.inForm.reduce_take_profit || 0,
reduce_stop_price: this.inForm.reduce_stop_price || 0,
symbol_group_id: String(this.inForm.symbol_group_id),
profit_num_ratio: this.inForm.profit_num_ratio || 100,
profit_tp_tp_price_ratio: this.inForm.profit_tp_tp_price_ratio || 0,
profit_tp_sl_price_ratio: this.inForm.profit_tp_sl_price_ratio || 0,
api_id: this.inForm.api_id.toString(),
hedge_trigger_percent: Number(this.inForm.hedge_trigger_percent),
hedge_trigger_percent_max: Number(this.inForm.hedge_trigger_percent_max),
ext: Array.isArray(this.inForm.ext)
? this.inForm.ext.map(item => ({
...item,
stopLossRatio: item.stopLossRatio || 0,
takeProfitNumRatio: item.takeProfitNumRatio || 0,
tpTpPriceRatio: item.tpTpPriceRatio || 0,
tpSlPriceRatio: item.tpSlPriceRatio || 0
}))
: []
})
console.log('this.inForm', this.inForm)
Promise.all([
this.$refs['inForm'].validate(), // 验证第一个表单
this.$refs['extForm'].validate() // 验证第二个表单
])
.then((formValid) => {
if (formValid.every(valid => valid)) {
this.inForm.price = this.inForm.price ? String(this.inForm.price) : ''
if (this.form.id !== undefined) {
this.$refs['inForm'].validate(valided => {
if (!valided) return false
const params = JSON.stringify({
...this.inForm,
reduce_price: this.inForm.reduce_price || 0,
reduce_num: this.inForm.reduce_num || 0,
reduce_take_profit: this.inForm.reduce_take_profit || 0,
reduce_stop_price: this.inForm.reduce_stop_price || 0,
symbol_group_id: String(this.inForm.symbol_group_id),
profit_num_ratio: this.inForm.profit_num_ratio || 100,
profit_tp_tp_price_ratio: this.inForm.profit_tp_tp_price_ratio || 0,
profit_tp_sl_price_ratio: this.inForm.profit_tp_sl_price_ratio || 0,
api_id: this.inForm.api_id.toString(),
hedge_trigger_percent: Number(this.inForm.hedge_trigger_percent),
hedge_trigger_percent_max: Number(this.inForm.hedge_trigger_percent_max),
ext: Array.isArray(this.inForm.ext)
? this.inForm.ext.map(item => ({
...item,
stopLossRatio: item.stopLossRatio || 0,
takeProfitNumRatio: item.takeProfitNumRatio || 0,
tpTpPriceRatio: item.tpTpPriceRatio || 0,
tpSlPriceRatio: item.tpSlPriceRatio || 0
}))
: []
})
updateLineOrderTemplateLogs({ ...this.form, params }).then(response => {
updateLineOrderTemplateLogs({ ...this.form, params }).then(response => {
if (response.code === 200) {
this.msgSuccess(response.msg)
this.open = false
this.getList()
} else {
this.msgError(response.msg)
}
})
})
} else {
addLineOrderTemplateLogs({ ...this.inForm, symbol_group_id: String(this.inForm.symbol_group_id), api_id: this.inForm.api_id.toString() }).then(response => {
if (response.code === 200) {
this.msgSuccess(response.msg)
this.open = false
@ -961,20 +1195,9 @@ export default {
this.msgError(response.msg)
}
})
})
} else {
addLineOrderTemplateLogs({ ...this.inForm, symbol_group_id: String(this.inForm.symbol_group_id), api_id: this.inForm.api_id.toString() }).then(response => {
if (response.code === 200) {
this.msgSuccess(response.msg)
this.open = false
this.getList()
} else {
this.msgError(response.msg)
}
})
}
}
}
})
})
},
/** 删除按钮操作 */
handleDelete(row) {
@ -1033,6 +1256,20 @@ export default {
}
}
::v-deep.ext-form-item {
.el-form-item__label {
padding: 0px !important;
}
}
.el-form--label-top .el-form-item__label {
padding: 0px !important;
}
.ext-form .el-form-item__content .el-input-group {
vertical-align: middle;
}
.page {
.pagination-container {
padding: 0 !important;

View File

@ -711,109 +711,146 @@
@click="onCalculate"
>计算</el-button></el-col>
</el-row>
<div v-for="(item, index) in form.ext" :key="index" class="exts">
<el-row v-if="item.addType === 1" :gutter="20" style="margin-bottom: 20px;">
<el-col :span="4">
<div style="margin-bottom: 8px;">{{ item.addType === 1 ? '加仓类型' : '减仓类型' }}</div>
<el-radio-group v-model="item.orderType" size="mini">
<el-radio label="LIMIT">限价</el-radio>
<el-radio label="MARKET">市价</el-radio>
</el-radio-group>
</el-col>
<el-col :span="4">
<div style="margin-bottom: 8px;">{{ item.addType === 1 ? '加仓方式' : '减仓方式' }}</div>
<el-radio-group v-model="item.addPositionType" size="mini">
<el-radio :label="1">百分比</el-radio>
<el-radio :label="2" :disabled="item.addType === 2">实际金额</el-radio>
</el-radio-group>
</el-col>
<el-form ref="extForm" :model="form" label-position="top" :rules="rules" class="ext-form">
<div v-for="(item, index) in form.ext" :key="index" class="exts">
</el-row>
<el-row :gutter="20">
<el-col :span="3">{{ item.addType === 1 ? '加仓下跌百分比' : '减仓下跌百分比' }}
<el-input
v-model.number="item.priceRatio"
min="0"
size="mini"
type="number"
:placeholder="item.addType === 1 ? '加仓下跌百分比' : '减仓下跌百分比'"
/></el-col>
<el-col :span="3">{{ item.addType === 1 ? '加仓数值' : '减仓百分比' }}
<el-input
v-model.number="item.addPositionVal"
min="0"
size="mini"
type="number"
:placeholder="item.addType === 1 ? '加仓数值' : '减仓百分比'"
/>
</el-col>
<el-col :span="6">
<span>止盈/止损百分比</span>
<div>
<el-input
v-model.number="item.takeProfitRatio"
min="0"
style="width:50%;"
size="mini"
type="number"
placeholder="止盈百分比"
>
<template v-if="title !== '批量添加'" #append>{{ ext[index] ? ext[index].reTakeProfitRatio : 0
}}</template>
</el-input>
<el-input
v-model.number="item.stopLossRatio"
style="width:42%;"
size="mini"
type="number"
placeholder="止损百分比"
/>
</div>
</el-col>
<!-- <el-col :span="3">止损百分比<el-input v-model.number="item.stopLossRatio" size="mini" type="number" placeholder="止损百分比" /></el-col> -->
<el-row v-if="item.addType === 1" :gutter="20" style="margin-bottom: 20px;">
<el-col :span="4">
<div style="margin-bottom: 8px;">{{ item.addType === 1 ? '加仓类型' : '减仓类型' }}</div>
<el-radio-group v-model="item.orderType" size="mini">
<el-radio label="LIMIT">限价</el-radio>
<el-radio label="MARKET">市价</el-radio>
</el-radio-group>
</el-col>
<el-col :span="4">
<div style="margin-bottom: 8px;">{{ item.addType === 1 ? '加仓方式' : '减仓方式' }}</div>
<el-radio-group v-model="item.addPositionType" size="mini">
<el-radio :label="1">百分比</el-radio>
<el-radio :label="2" :disabled="item.addType === 2">实际金额</el-radio>
</el-radio-group>
</el-col>
<template v-if="item.addType === 1">
</el-row>
<el-row :gutter="20">
<el-col :span="3">
止盈数量百分比<el-input
v-model.number="item.takeProfitNumRatio"
size="mini"
type="number"
placeholder="止盈数量百分比"
/>
<el-form-item
:prop="'ext.' + index + '.priceRatio'"
:rules="rules.priceRatio"
class="ext-form-item"
:label="item.addType === 1 ? '加仓下跌百分比' : '减仓下跌百分比'"
>
<el-input
v-model.number="item.priceRatio"
min="0"
size="mini"
type="number"
:placeholder="item.addType === 1 ? '加仓下跌百分比' : '减仓下跌百分比'"
/>
</el-form-item>
</el-col>
<el-col :span="3">
<el-form-item
:prop="'ext.' + index + '.addPositionVal'"
:label="item.addType === 1 ? '加仓数值' : '减仓百分比'"
:rules="rules.addPositionVal"
class="ext-form-item"
>
<el-input
v-model.number="item.addPositionVal"
min="0"
size="mini"
type="number"
:placeholder="item.addType === 1 ? '加仓数值' : '减仓百分比'"
/>
</el-form-item>
</el-col>
<el-col :span="6">
<span>第二止盈/止损百分比</span>
<div>
<el-input
v-model.number="item.tpTpPriceRatio"
min="0"
style="width:42%"
size="mini"
type="number"
placeholder="止盈后止盈价百分比"
/>
<el-input
v-model.number="item.tpSlPriceRatio"
min="0"
style="width:42%"
size="mini"
type="number"
placeholder="止盈后止损价百分比"
/>
</div>
<el-form-item
label="止盈/止损百分比"
:prop="'ext.' + index + '.takeProfitRatio'"
:rules="[{ validator: validateTakeProfitAndStopLoss, trigger: 'blur' }]"
class="ext-form-item"
>
<div>
<el-input
v-model.number="item.takeProfitRatio"
min="0"
style="width:50%;"
size="mini"
type="number"
placeholder="止盈百分比"
:rules="rules.takeProfitRatio"
>
<template v-if="title !== '批量添加'" #append>{{ ext[index] ? ext[index].reTakeProfitRatio : 0
}}</template>
</el-input>
<el-input
v-model.number="item.stopLossRatio"
style="width:42%;"
size="mini"
type="number"
placeholder="止损百分比"
:rules="rules.stopLossRatio"
/>
</div>
</el-form-item>
</el-col>
</template>
<el-col :span="3">
<div style="margin-bottom: 14px;" /><el-button
size="mini"
type="danger"
@click="onAddExt(1, index)"
>删除</el-button>
</el-col>
</el-row>
</div>
<template v-if="item.addType === 1">
<el-col :span="3">
<el-form-item
label="止盈数量百分比"
:prop="'ext.' + index + '.takeProfitNumRatio'"
:rules="rules.takeProfitNumRatio"
class="ext-form-item"
>
<el-input
v-model.number="item.takeProfitNumRatio"
size="mini"
type="number"
placeholder="止盈数量百分比"
/>
</el-form-item>
</el-col>
<el-col :span="6">
<el-form-item
label="第二止盈/止损百分比"
:prop="'ext.' + index + '.tpTpPriceRatio'"
:rules="[{ validator: validateTp, trigger: 'blur' }]"
class="ext-form-item"
>
<div>
<el-input
v-model.number="item.tpTpPriceRatio"
min="0"
style="width:42%"
size="mini"
type="number"
placeholder="止盈后止盈价百分比"
/>
<el-input
v-model.number="item.tpSlPriceRatio"
min="0"
style="width:42%"
size="mini"
type="number"
placeholder="止盈后止损价百分比"
/>
</div>
</el-form-item>
</el-col>
</template>
<el-col :span="3">
<div style="margin-bottom: 14px;" /><el-button
size="mini"
type="danger"
@click="onAddExt(1, index)"
>删除</el-button>
</el-col>
</el-row>
</div>
</el-form>
<el-form-item label="是否保存模板" prop="save_template">
<el-radio-group v-model="form.save_template">
<el-radio label="0"></el-radio>
@ -1118,7 +1155,7 @@
v-for="(item, index) in sideList"
:key="'position_side' + index"
:label="item.value"
:disabled="item.value=='SELL'&&positionForm.close_type==1"
:disabled="item.value === 'SELL' && positionForm.close_type === 1"
>{{ item.label }}</el-radio>
</el-radio-group>
</el-form-item>
@ -1331,18 +1368,51 @@ export default {
},
// 表单校验
rules: {
reduce_price: [{ required: true, message: '主单亏损减仓百分比不能为空', trigger: 'blur' }],
reduce_num: [{ required: true, message: '主单减仓数量百分比不能为空', trigger: 'blur' }],
reduce_take_profit: [{ required: true, message: '主单减仓后止盈价百分比不能为空', trigger: 'blur' }],
// reduce_stop_price: [{ required: true, message: '主单减仓后止损价不能为空', trigger: 'blur' }],
buy_price: [{ required: true, message: '购买金额不能为空', trigger: 'blur' }],
profit: [{ required: true, message: '请输入止盈百分比', trigger: 'blur' }, { validator: this.validateProfit, trigger: 'blur' }],
reduce_price: [
{ required: true, message: '主单亏损减仓百分比不能为空', trigger: 'blur' },
{ validator: this.validateReducePrice, trigger: 'blur' }],
reduce_num: [
{ required: true, message: '主单减仓数量百分比不能为空', trigger: 'blur' },
{ validator: this.validateReduceNum, trigger: 'blur' }],
reduce_take_profit: [
{ required: true, message: '主单减仓后止盈价百分比不能为空', trigger: 'blur' },
{ validator: this.validateReduceTakeProfit, trigger: 'blur' }],
buy_price: [
{ required: true, message: '购买金额不能为空', trigger: 'blur' },
{ validator: this.validateBuyPrice, trigger: 'blur' }
],
exchange_type: [{ required: true, message: '交易所不能为空', trigger: 'blur' }],
api_id: [{ required: true, message: 'api用户不能为空', trigger: 'blur' }],
symbol: [{ required: true, message: '交易对不能为空', trigger: 'blur' }],
symbol_group_id: [{ required: true, message: '交易对组不能为空', trigger: 'blur' }],
quote_symbol: [{ required: true, message: '计价货币不能为空', trigger: 'blur' }],
order_sn: [{ required: true, message: '订单号不能为空', trigger: 'blur' }],
profit_num_ratio: [{ required: true, message: '止盈数量百分比不能为空', trigger: 'blur' }]
profit_num_ratio: [
{ required: true, message: '止盈数量百分比不能为空', trigger: 'blur' },
{ validator: this.validateProfitNumRatio, trigger: 'blur' }
],
profit_tp_tp_price_ratio: [{ validator: this.validateTptpPriceRatio, trigger: 'blur' }],
priceRatio: [{ validator: this.validateExtPriceRatio, trigger: 'blur' }],
addPositionVal: [
{ required: true, message: '追加仓位不能为空', trigger: 'blur' },
{ validator: this.validateAddPosition, trigger: 'blur' }],
takeProfitRatio: [
{ required: true, message: '请输入止盈百分比', trigger: 'blur' },
{ type: 'number', message: '止盈百分比必须为数字', trigger: 'blur' },
{ min: 0, message: '止盈百分比不能小于 0', trigger: 'blur' }
],
stopLossRatio: [
{ required: true, message: '请输入止损百分比', trigger: 'blur' },
{ type: 'number', message: '止损百分比必须为数字', trigger: 'blur' },
{ min: 0, message: '止损百分比不能小于 0', trigger: 'blur' }
],
takeProfitNumRatio: [{ required: true, message: '止盈数量不能为空', trigger: 'blur' },
{ validator: this.validateExtTakeProfitNumRatio, trigger: 'blur' }
],
tpTpPriceRatio: [{ required: true, message: '第二止盈价格百分比不能为空', trigger: 'blur' }],
slSlPriceRatio: [{ required: true, message: '第二止损价格百分比不能为空', trigger: 'blur' }]
},
currentExpandId: undefined,
cacheRefresh: {},
@ -1382,7 +1452,6 @@ export default {
positionRules: {
exchangeType: [{ required: true, message: '交易所不能为空', trigger: 'blur' }],
api_id: [{ required: true, message: 'api用户不能为空', trigger: 'blur' }]
// symbol: [{ required: true, message: '交易对不能为空', trigger: 'blur' }]
},
searchLoding: false,
// 取消委托
@ -1523,7 +1592,7 @@ export default {
takeProfitRatio: 0,
stopLossRatio: 0,
// 止盈数量百分比 止盈后止盈价格百分比 止盈后止损价格百分比
takeProfitNumRatio: 0,
takeProfitNumRatio: 100,
tpTpPriceRatio: 0,
tpSlPriceRatio: 0
@ -1914,6 +1983,146 @@ export default {
}
this.resetForm('form')
},
validateExtTakeProfitNumRatio(rule, value, callback) {
if (value === 0 || value === undefined || value === null || value === '') {
callback(new Error('止盈数量百分比必须大于0'))
} else {
callback()
}
},
validateReducePrice(rule, value, callback) {
if (value === 0 || value === undefined || value === null || value === '') {
callback(new Error('亏损百分比必须大于0'))
} else {
callback()
}
},
validateBuyPrice(rule, value, callback) {
if (value === 0) {
callback(new Error('购买金额必须大于0'))
} else {
callback()
}
},
validateReduceTakeProfit(rule, value, callback) {
if (this.form.reduce_num > 0 && this.form.reduce_num < 100 && this.form.reduce_take_profit === 0) {
callback(new Error('主单减仓后止盈必须大于0'))
} else if (this.form.reduce_num === 100 && this.form.reduce_take_profit > 0) {
callback(new Error('主单减仓全部后无需止盈'))
} else {
callback()
}
},
/** 主单减仓数量 */
validateReduceNum(rule, value, callback) {
if (value <= 0) {
callback(new Error('主单减仓数量必须大于0'))
} else {
callback()
}
},
/** 主单止盈数量 */
validateProfitNumRatio(rule, value, callback) {
if (value <= 0) {
callback(new Error('止盈数量比例必须大于0'))
} else {
callback()
}
},
/** 验证第二止盈止损 */
validateTptpPriceRatio(rule, value, callback) {
if (!this.form) {
callback(new Error('表单未初始化'))
return
}
if (this.form.profit_num_ratio > 0 && this.form.profit_num_ratio < 100 && (!value || value <= 0)) {
callback(new Error('第二止盈百分比不能为空且必须大于 0'))
} else if (this.form.profit_num_ratio === 100 && value > 0) {
callback(new Error('第一止盈已经全部止盈了,不需要第二止盈百分比'))
} else {
callback()
}
},
validateProfit(rule, value, callback) {
console.log(this.form)
if (this.form.profit === '' || this.form.profit === undefined || this.form.profit === null) {
callback(new Error('止盈百分比不能为空'))
} else if (this.form.profit <= 0) {
callback(new Error('止盈百分比不能小于0'))
} else {
callback()
}
},
validateExtPriceRatio(rule, value, callback) {
const index = rule.fullField.split('.')[1] // 获取索引
const item = this.form.ext[index]
const message = item.addType === 1 ? '加仓下跌百分比' : '减仓下跌百分比'
if (item.priceRatio === '') {
callback(new Error(message + '不能为空'))
} else if (isNaN(item.priceRatio)) {
callback(new Error('必须输入数字'))
} else if (item.priceRatio < 0) {
callback(new Error('百分比不能小于 0'))
}
if (index === 0 && item.priceRatio <= this.form.reduce_price) {
callback(new Error('下跌百分比不能小于上一节点'))
} else if (index > 0 && item.priceRatio <= this.form.ext[index - 1].priceRatio) {
callback(new Error('下跌百分比不能小于上一节点'))
} else {
callback()
}
},
validateAddPosition(rule, value, callback) {
const index = rule.fullField.split('.')[1] // 获取索引
const item = this.form.ext[index]
const message = item.addType === 1 ? '加仓数值' : '减仓百分比'
if (item.addPositionVal === '') {
callback(new Error(message + '不能为空'))
} else if (isNaN(item.addPositionVal)) {
callback(new Error('必须输入数字'))
} else if (item.addPositionVal <= 0) {
callback(new Error(message + '必须大于 0'))
} else {
callback() // 校验通过
}
},
// 自定义校验逻辑
validateTakeProfitAndStopLoss(rule, value, callback) {
const index = rule.fullField.split('.')[1] // 获取索引
const item = this.form.ext[index]
if (item.takeProfitRatio === '' || item.stopLossRatio === '') {
callback(new Error('止盈和止损百分比都不能为空'))
} else if (isNaN(item.takeProfitRatio) || isNaN(item.stopLossRatio)) {
callback(new Error('必须输入数字'))
} else if (item.takeProfitRatio < 0 || item.stopLossRatio < 0) {
callback(new Error('百分比不能小于 0'))
} else if (item.takeProfitRatio === 0) {
callback(new Error('止盈百分比不能为0'))
} else {
callback() // 校验通过
}
},
// 自定义 第二止盈止损校验
validateTp(rule, value, callback) {
const index = rule.fullField.split('.')[1] // 获取索引
const item = this.form.ext[index]
if (item.tpTpPriceRatio === '' || item.tpSlPriceRatio === '') {
callback(new Error('第二止盈、止损百分比不能为空'))
} else if (isNaN(item.tpTpPriceRatio) || isNaN(item.tpSlPriceRatio)) {
callback(new Error('必须输入数字'))
} else if (item.tpTpPriceRatio < 0 || item.tpSlPriceRatio < 0) {
callback(new Error('百分比不能小于 0'))
} else if (item.takeProfitNumRatio < 100 && item.takeProfitNumRatio > 0 && item.tpTpPriceRatio <= 0) {
callback(new Error('第二止盈百分比必须大于0'))
} else {
callback() // 校验通过
}
},
getImgList: function() {
this.form[this.fileIndex] = this.$refs['fileChoose'].resultList[0].fullUrl
},
@ -1971,75 +2180,84 @@ export default {
})
},
/** 提交按钮 */
submitForm: function() {
this.$refs['form'].validate(valid => {
if (valid) {
this.formLoading = true
if (this.form.price) {
this.form.price = String(this.form.price)
}
submitForm: async function() {
const formValid = await Promise.all([
this.$refs['form'].validate(), // 验证第一个表单
this.$refs['extForm'].validate() // 验证第二个表单
])
if (this.form.id !== undefined) {
updateLinePreOrder(this.form).then(response => {
if (response.code === 200) {
this.msgSuccess(response.msg)
this.open = false
this.getList()
} else {
this.msgError(response.msg)
}
}).finally(() => {
this.formLoading = false
})
} else {
if (this.title === '批量添加') {
const params = JSON.parse(JSON.stringify({
...this.form,
symbol_group_id: String(this.form.symbol_group_id),
api_id: this.form.api_id.toString(),
profit_num_ratio: this.form.profit_num_ratio || 100,
profit_tp_tp_price_ratio: this.form.profit_tp_tp_price_ratio || 0,
profit_tp_sl_price_ratio: this.form.profit_tp_sl_price_ratio || 0
}))
delete params.symbol
batchAddOrder(params).then(response => {
if (response.code === 200) {
this.msgSuccess(response.msg)
this.open = false
this.getList()
} else {
this.msgError(response.msg)
}
}).finally(() => {
this.formLoading = false
})
return false
console.log(formValid)
if (formValid.every(valid => valid)) {
this.formLoading = true
if (this.form.price) {
this.form.price = String(this.form.price)
}
if (this.form.id !== undefined) {
updateLinePreOrder(this.form).then(response => {
if (response.code === 200) {
this.msgSuccess(response.msg)
this.open = false
this.getList()
} else {
this.msgError(response.msg)
}
addOrder({
}).finally(() => {
this.formLoading = false
})
} else {
if (this.title === '批量添加') {
const params = JSON.parse(JSON.stringify({
...this.form,
reduce_price: this.form.reduce_price || 0,
reduce_num: this.form.reduce_num || 0,
reduce_take_profit: this.form.reduce_take_profit || 0,
reduce_stop_price: this.form.reduce_stop_price || 0,
symbol_group_id: String(this.form.symbol_group_id),
api_id: this.form.api_id.toString(),
profit_num_ratio: this.form.profit_num_ratio || 100,
profit_tp_tp_price_ratio: this.form.profit_tp_tp_price_ratio || 0,
profit_tp_sl_price_ratio: this.form.profit_tp_sl_price_ratio || 0,
api_id: this.form.api_id.toString()
}).then(response => {
profit_tp_sl_price_ratio: this.form.profit_tp_sl_price_ratio || 0
}))
delete params.symbol
batchAddOrder(params).then(response => {
if (response.code === 200) {
this.msgSuccess(response.msg)
this.open = false
this.getList()
} else {
this.formLoadng = false
this.msgError(response.msg)
}
}).finally(() => {
this.formLoading = false
})
return false
}
addOrder({
...this.form,
reduce_price: this.form.reduce_price || 0,
reduce_num: this.form.reduce_num || 0,
reduce_take_profit: this.form.reduce_take_profit || 0,
reduce_stop_price: this.form.reduce_stop_price || 0,
profit_num_ratio: this.form.profit_num_ratio || 100,
profit_tp_tp_price_ratio: this.form.profit_tp_tp_price_ratio || 0,
profit_tp_sl_price_ratio: this.form.profit_tp_sl_price_ratio || 0,
api_id: this.form.api_id.toString()
}).then(response => {
if (response.code === 200) {
this.msgSuccess(response.msg)
this.open = false
this.getList()
} else {
this.formLoadng = false
this.msgError(response.msg)
}
}).finally(() => {
this.formLoading = false
})
}
})
}
// this.$refs['form'].validate(valid => {
// if (valid) {
// }
// })
},
onCancel() {
this.cancelOpen = true
@ -2162,6 +2380,20 @@ export default {
}
}
::v-deep.ext-form-item {
.el-form-item__label {
padding: 0px !important;
}
}
.el-form--label-top .el-form-item__label {
padding: 0px !important;
}
.ext-form .el-form-item__content .el-input-group {
vertical-align: middle;
}
.page {
.pagination-container {
padding: 0 !important;