590 lines
16 KiB
Vue
590 lines
16 KiB
Vue
<template>
|
||
<div class="variety table_form-container">
|
||
<!-- 搜索 -->
|
||
<VarietySearch :search-form="searchForm" :buttonLoading="loading" :search-options="searchOptions"
|
||
@search="handleSearch" @reset="handleReset">
|
||
<template #button="{ form }">
|
||
<el-button type="primary" :disabled="!selectedRow" @click="handleEdit()" v-auth="'contractVariety_editVariety'">
|
||
编辑
|
||
</el-button>
|
||
<el-button type="primary" @click="handleAdd()" v-auth="'contractVariety_createVariety'">
|
||
添加
|
||
</el-button>
|
||
<el-button type="primary" :loading="syncLoading" @click="handleSyncVariety()" v-auth="'contractVariety_createVariety'">
|
||
同步合约品种
|
||
</el-button>
|
||
<el-button type="primary" :disabled="!selectedRow" @click="handleSetWeekendTradeTime()" v-auth="'contractVariety_editVariety'">
|
||
设置周末交易时间
|
||
</el-button>
|
||
</template>
|
||
</VarietySearch>
|
||
|
||
<!-- 表格 -->
|
||
<VarietyTable :table-data="varietyTree" :columns="varietyTableColumnsOptions" row-key="id"
|
||
@row-click="handleRowClick" :loading="loading">
|
||
<template #onlineStatus="{ row }">
|
||
{{ ['', '上线', '下线'][row.onlineStatus] }}
|
||
</template>
|
||
<template #tradeStatus="{ row }">
|
||
{{ ['', '开启', '禁用'][row.tradeStatus] }}
|
||
</template>
|
||
</VarietyTable>
|
||
</div>
|
||
<!-- dialog -->
|
||
<VarietyDialog :visible="dialogVisible" :title="dialogTitle" :type="dialogType" :button-loading="submitLoading"
|
||
@confirm="handleSubmit" @cancel="handleCancel" @close="handleClose">
|
||
<VarietyForm :form-data="varietyForm" :form-rules="varietyFormRules" :form-items="dialogFormItemOptions"
|
||
:options-map="varietyFormOptionsMap" :row-gutter="rowGutter" :col-span="colSpan" ref="varietyFormRef">
|
||
<template #upload="{ formData }">
|
||
<el-upload ref="uploadRef" v-model:file-list="fileList" class="upload-demo" :http-request="handleUpload"
|
||
:before-upload="beforeUpload" :limit="1" :on-exceed="handleExceed">
|
||
<el-button type="primary">点击上传</el-button>
|
||
<template #tip>
|
||
<div class="el-upload__tip">jpg/png格式 图片大小不能超过200KB</div>
|
||
</template>
|
||
<img v-if="dialogType === 'edit'" :src="formData.imageUrl" alt="预览图片" style="width: 100px; height: 100px" />
|
||
</el-upload>
|
||
</template>
|
||
<template #isDefault="{ formData }">
|
||
<el-radio-group v-model="formData.isDefault">
|
||
<el-radio :value="1">是</el-radio>
|
||
<el-radio :value="2">否</el-radio>
|
||
</el-radio-group>
|
||
</template>
|
||
</VarietyForm>
|
||
</VarietyDialog>
|
||
<!-- dialog表格 -->
|
||
<VarietyDialog :visible="tradingHoursVisible" title="设置周末交易时间" @confirm="handleSubmitTradingHours" @cancel="handleCancelTradingHours" @close="handleCloseTradingHours">
|
||
<VarietyForm ref="tradingHoursFormRef" :form-data="tradingHoursForm" :form-rules="tradingHoursFormRules" :form-items="tradingHoursFormItemOptions" :row-gutter="rowGutter" :col-span="colSpan">
|
||
</VarietyForm>
|
||
</VarietyDialog>
|
||
</template>
|
||
|
||
<script setup lang="ts">
|
||
|
||
defineOptions({
|
||
name: 'Variety'
|
||
})
|
||
// 组件
|
||
import { usePageLoading, useButtonLoading } from '@/composables/useLoading'
|
||
const { loading, startLoading, stopLoading } = usePageLoading()
|
||
const { buttonLoading: submitLoading, startButtonLoading: startSubmitLoading, stopButtonLoading: stopSubmitLoading } = useButtonLoading()
|
||
|
||
import VarietySearch from '@/components/common/Search.vue'
|
||
import VarietyTable from '@/components/common/Table.vue'
|
||
import VarietyDialog from '@/components/common/Dialog.vue'
|
||
import VarietyForm from '@/components/common/Form.vue'
|
||
import { ElMessage } from 'element-plus'
|
||
|
||
// 配置以及表单验证规则
|
||
import { search, tableColumns, DialogFormItem, tradingHoursFormItem } from './options'
|
||
import { rules, tradingHoursRules } from './rules'
|
||
|
||
// 接口
|
||
import {
|
||
getVarietyListApi,
|
||
getTradeGroupListApi,
|
||
editVarietyApi,
|
||
createVarietyApi,
|
||
syncVarietyApi,
|
||
getVarietyEnumsApi,
|
||
editTradingHoursApi,
|
||
} from '@/api/modules/trade/variety'
|
||
// 上传图片
|
||
import { uploadImageApi } from '@/api/modules/upload'
|
||
|
||
/**
|
||
* @description: 定义搜索数据
|
||
*/
|
||
const searchForm = ref({
|
||
varietyName: '',
|
||
})
|
||
const searchOptions = ref(search)
|
||
|
||
/**
|
||
* @description: 定义表格数据
|
||
*/
|
||
const varietyTree = ref([])
|
||
const varietyTableColumnsOptions = ref(tableColumns)
|
||
|
||
/**
|
||
* @description: 定义弹窗数据
|
||
*/
|
||
const dialogVisible = ref(false)
|
||
const dialogTitle = ref('')
|
||
const dialogType = ref('')
|
||
|
||
/**
|
||
* @description: 定义弹窗表单数据
|
||
*/
|
||
const varietyForm = ref({
|
||
// 品种名称
|
||
varietyName: '',
|
||
// 品种代码
|
||
varietyCode: '',
|
||
// 线上状态
|
||
onlineStatus: 1,
|
||
// 交易状态
|
||
tradeStatus: 1,
|
||
// 最小点差
|
||
minPoint: '',
|
||
// 最大点差
|
||
maxPoint: '',
|
||
// 合约大小(乘数)
|
||
contractSize: '',
|
||
// 最小波动
|
||
minFlux: '',
|
||
// 单位
|
||
unit: '',
|
||
// 多头库存费
|
||
longStockFee: '',
|
||
// 空头库存费
|
||
shortStockFee: '',
|
||
// 止盈水平
|
||
profitLevel: '',
|
||
|
||
id: '',
|
||
groupId: '',
|
||
// 预付款对冲
|
||
prepaymentHedge: '',
|
||
// 预付款对冲比例
|
||
prepaymentPercent: '',
|
||
// 小数位
|
||
decimalPlaces: '',
|
||
// 点差小数位
|
||
pointDecimalPlaces: '',
|
||
// 类型
|
||
type: '',
|
||
// 上传图片地址
|
||
imageUrl: '',
|
||
// 库存费收取时间
|
||
feeInventoryTime: '',
|
||
// 交易分组
|
||
tradeGroup: '',
|
||
// 是否默认
|
||
isDefault: '',
|
||
})
|
||
const varietyFormRef = ref<any>()
|
||
const varietyFormRules = ref(rules())
|
||
const dialogFormItemOptions = ref(DialogFormItem)
|
||
const varietyFormOptionsMap = ref({
|
||
group_id: [],
|
||
type: [],
|
||
})
|
||
|
||
/**
|
||
* @description: 定义周末交易时间弹窗数据
|
||
*/
|
||
const tradingHoursVisible = ref(false)
|
||
/**
|
||
* @description: 定义周末交易时间弹窗表单引用
|
||
*/
|
||
const tradingHoursFormRef = ref<any>()
|
||
/**
|
||
* @description: 定义周末交易时间弹窗表单数据
|
||
*/
|
||
const tradingHoursForm = ref({
|
||
// 品种id
|
||
id: '',
|
||
// 周末交易时间
|
||
saturday_start_time: '',
|
||
saturday_end_time: '',
|
||
sunday_start_time: '',
|
||
sunday_end_time: '',
|
||
})
|
||
/**
|
||
* @description: 定义周末交易时间弹窗表单验证规则
|
||
*/
|
||
const tradingHoursFormRules = ref(tradingHoursRules)
|
||
const tradingHoursFormItemOptions = ref(tradingHoursFormItem)
|
||
// 选中行
|
||
const selectedRow = ref<any>(null)
|
||
const rowGutter = ref(20)
|
||
const colSpan = ref(12)
|
||
|
||
const fileList = ref([])
|
||
|
||
// 类型枚举列表
|
||
const typeList = ref<any[]>([])
|
||
|
||
// 获取品种枚举
|
||
const getVarietyEnums = async () => {
|
||
const res = await getVarietyEnumsApi()
|
||
if (res) {
|
||
typeList.value = res.map((item: any) => ({
|
||
label: item.name,
|
||
value: item.type,
|
||
}))
|
||
varietyFormOptionsMap.value.type = typeList.value as any
|
||
}
|
||
}
|
||
|
||
/**
|
||
* @description: 定义公共请求数据方法
|
||
*/
|
||
|
||
//获取品种列表
|
||
const getVarietyList = async () => {
|
||
startLoading()
|
||
const params = {
|
||
name: searchForm.value.varietyName,
|
||
}
|
||
try {
|
||
const data = await getVarietyListApi(params)
|
||
console.log('data', data)
|
||
//格式化表格数据
|
||
varietyTree.value = data.map((item: any) => ({
|
||
...item,
|
||
id: item.id,
|
||
varietyName: item.name,
|
||
varietyCode: item.code,
|
||
onlineStatus: item.online_status,
|
||
tradeStatus: item.status,
|
||
minPoint: item.point_diff,
|
||
maxPoint: item.point_diff_max,
|
||
contractSize: item.multiplier,
|
||
minFlux: item.undulate_min,
|
||
unit: item.unit,
|
||
tradeGroup: item.group.name,
|
||
longStockFee: item.fee_inventory_long,
|
||
shortStockFee: item.fee_inventory_short,
|
||
profitLevel: item.stop_loss_level,
|
||
type: typeList.value.find((t: any) => t.value === item.type)?.label || '',
|
||
image: item.image,
|
||
}))
|
||
} finally {
|
||
stopLoading()
|
||
}
|
||
}
|
||
|
||
// 获取合约品种分组列表
|
||
const getTradeGroupList = async () => {
|
||
const data = await getTradeGroupListApi()
|
||
varietyFormOptionsMap.value.group_id = data.map((item: any) => ({
|
||
label: item.name,
|
||
value: item.id,
|
||
}))
|
||
}
|
||
|
||
/**
|
||
* @description: 页面加载完成需要请求的数据
|
||
*/
|
||
onMounted(() => {
|
||
getVarietyEnums()
|
||
getVarietyList()
|
||
getTradeGroupList()
|
||
})
|
||
|
||
/**
|
||
* @description: 定义搜索方法
|
||
*/
|
||
const handleSearch = async () => {
|
||
getVarietyList()
|
||
}
|
||
const handleReset = () => {
|
||
searchForm.value = {
|
||
varietyName: '',
|
||
}
|
||
handleSearch()
|
||
}
|
||
|
||
// 同步合约品种
|
||
const syncLoading = ref(false)
|
||
const handleSyncVariety = async () => {
|
||
syncLoading.value = true
|
||
try {
|
||
await syncVarietyApi()
|
||
} finally {
|
||
syncLoading.value = false
|
||
}
|
||
}
|
||
|
||
const handleEdit = () => {
|
||
getTradeGroupList()
|
||
const row = selectedRow.value
|
||
|
||
if (row.image) {
|
||
fileList.value = [
|
||
{
|
||
name: row.image.split('/').pop() || 'existing.jpg',
|
||
url: row.image,
|
||
status: 'success',
|
||
},
|
||
]
|
||
} else {
|
||
fileList.value = []
|
||
}
|
||
|
||
varietyForm.value = {
|
||
...row,
|
||
id: row.id,
|
||
group_id: row.group_id || row.group.id,
|
||
// 预付款对冲
|
||
prepaymentHedge: row.bail_hedge,
|
||
// 预付款对冲比例
|
||
prepaymentPercent: row.bail_percent,
|
||
// 小数位
|
||
decimalPlaces: row.decimal_place,
|
||
// 点差小数位
|
||
pointDecimalPlaces: row.point_diff_decimal_place,
|
||
imageUrl: row.image,
|
||
feeInventoryTime: row.fee_inventory_time,
|
||
tradeStatus: row.status,
|
||
onlineStatus: row.online_status,
|
||
// 是否默认
|
||
isDefault: row.is_default,
|
||
} as any
|
||
|
||
console.log('varietyForm.value', varietyForm.value)
|
||
|
||
//数据赋值完成后执行弹窗操作
|
||
dialogVisible.value = true
|
||
dialogTitle.value = '编辑合约品种'
|
||
dialogType.value = 'edit'
|
||
dialogFormItemOptions.value[4]!.disabled = true
|
||
}
|
||
|
||
const handleAdd = () => {
|
||
dialogVisible.value = true
|
||
dialogTitle.value = '添加合约品种'
|
||
dialogType.value = 'add'
|
||
dialogFormItemOptions.value[4]!.disabled = false
|
||
varietyForm.value = {
|
||
varietyName: '',
|
||
varietyCode: '',
|
||
onlineStatus: 1,
|
||
tradeStatus: 1,
|
||
minPoint: '',
|
||
maxPoint: '',
|
||
contractSize: '',
|
||
minFlux: '',
|
||
unit: '',
|
||
longStockFee: '',
|
||
shortStockFee: '',
|
||
profitLevel: '',
|
||
id: '',
|
||
groupId: '',
|
||
prepaymentHedge: '',
|
||
prepaymentPercent: '',
|
||
decimalPlaces: '',
|
||
pointDecimalPlaces: '',
|
||
type: '',
|
||
imageUrl: '',
|
||
feeInventoryTime: '',
|
||
tradeGroup: '',
|
||
// 是否默认
|
||
isDefault: 2,
|
||
}
|
||
fileList.value = []
|
||
}
|
||
|
||
/**
|
||
* @description: 上传图片
|
||
*/
|
||
// 自定义上传方法,调用你封装的接口
|
||
const handleUpload = async (options: any) => {
|
||
const { file } = options
|
||
try {
|
||
const data = await uploadImageApi(file)
|
||
varietyForm.value.imageUrl = data.url
|
||
|
||
// 通知 el-upload 上传成功
|
||
options.onSuccess(data)
|
||
} catch (err) {
|
||
console.error('上传失败', err)
|
||
}
|
||
}
|
||
|
||
const uploadRef = ref()
|
||
const handleExceed = (files: File[]) => {
|
||
// 先清空已有文件列表
|
||
if (uploadRef.value) {
|
||
uploadRef.value.clearFiles()
|
||
}
|
||
// 手动触发新文件的上传
|
||
const file = files[0]
|
||
if (file) {
|
||
uploadRef.value?.handleStart(file) // 手动加入文件队列
|
||
// 手动调用上传(因为 http-request 是自定义的)
|
||
handleUpload({ file, onSuccess: () => { }, onError: () => { } })
|
||
}
|
||
}
|
||
|
||
// 上传前校验:格式 + 大小(200KB = 200 * 1024)
|
||
const beforeUpload = (file: File) => {
|
||
// 严格校验后缀名
|
||
const fileName = file.name.toLowerCase()
|
||
const isJpgOrPng =
|
||
fileName.endsWith('.jpg') || fileName.endsWith('.jpeg') || fileName.endsWith('.png')
|
||
|
||
if (!isJpgOrPng) {
|
||
ElMessage.error('上传图片只能是 JPG/PNG 格式,且必须包含后缀名!')
|
||
return false
|
||
}
|
||
|
||
// 校验 MIME 类型
|
||
const isTypeValid = file.type === 'image/jpeg' || file.type === 'image/png'
|
||
if (!isTypeValid) {
|
||
ElMessage.error('图片MIME类型不正确,请重新选择!')
|
||
return false
|
||
}
|
||
|
||
// 校验大小 (200KB)
|
||
const isSizeValid = file.size / 1024 < 200
|
||
if (!isSizeValid) {
|
||
ElMessage.error('图片大小不能超过 200KB!')
|
||
return false
|
||
}
|
||
|
||
return true
|
||
}
|
||
|
||
/**
|
||
* @description: 定义弹窗方法
|
||
*/
|
||
const handleSubmit = async () => {
|
||
startSubmitLoading()
|
||
try {
|
||
await varietyFormRef.value?.validate()
|
||
console.log(varietyForm.value)
|
||
const params = {
|
||
id: varietyForm.value.id,
|
||
code: varietyForm.value.varietyCode,
|
||
name: varietyForm.value.varietyName,
|
||
point_diff: varietyForm.value.minPoint,
|
||
fee_inventory_short: varietyForm.value.shortStockFee,
|
||
status: varietyForm.value.tradeStatus,
|
||
bail_hedge: varietyForm.value.prepaymentHedge,
|
||
bail_percent: varietyForm.value.prepaymentPercent,
|
||
decimal_place: varietyForm.value.decimalPlaces,
|
||
stop_loss_level: varietyForm.value.profitLevel,
|
||
point_diff_max: varietyForm.value.maxPoint,
|
||
fee_inventory_long: varietyForm.value.longStockFee,
|
||
point_diff_decimal_place: varietyForm.value.pointDecimalPlaces,
|
||
unit: varietyForm.value.unit,
|
||
multiplier: varietyForm.value.contractSize,
|
||
undulate_min: varietyForm.value.minFlux,
|
||
online_status: varietyForm.value.onlineStatus,
|
||
type: Number(varietyForm.value.type),
|
||
image: varietyForm.value.imageUrl,
|
||
fee_inventory_time: varietyForm.value.feeInventoryTime,
|
||
group_id: varietyForm.value.group_id,
|
||
}
|
||
console.log(params)
|
||
|
||
if (dialogType.value === 'edit') {
|
||
console.log(params)
|
||
if (params.point_diff_max < params.point_diff)
|
||
return ElMessage.warning('最大点差不能小于最小点差')
|
||
await editVarietyApi({
|
||
...params,
|
||
is_default: varietyForm.value.isDefault,
|
||
})
|
||
await getVarietyList()
|
||
} else {
|
||
await createVarietyApi({
|
||
...params,
|
||
is_default: varietyForm.value.isDefault,
|
||
})
|
||
await getVarietyList()
|
||
}
|
||
dialogVisible.value = false
|
||
} finally {
|
||
stopSubmitLoading()
|
||
}
|
||
}
|
||
const handleCancel = () => {
|
||
dialogVisible.value = false
|
||
}
|
||
const handleClose = () => {
|
||
fileList.value = []
|
||
dialogVisible.value = false
|
||
}
|
||
|
||
/**
|
||
* @description: 定义弹窗表格方法
|
||
*/
|
||
// 点击行事件
|
||
const handleRowClick = (row: any) => {
|
||
if (!row) {
|
||
selectedRow.value = null
|
||
return
|
||
}
|
||
selectedRow.value = row
|
||
}
|
||
|
||
/**
|
||
* @description: 设置周末交易时间
|
||
*/
|
||
const handleSetWeekendTradeTime = async () => {
|
||
if (!selectedRow.value) {
|
||
ElMessage.error('请选择合约品种')
|
||
return
|
||
}
|
||
tradingHoursVisible.value = true
|
||
tradingHoursForm.value.id = selectedRow.value.id
|
||
}
|
||
|
||
/**
|
||
* @description: 提交周末交易时间
|
||
*/
|
||
const handleSubmitTradingHours = async () => {
|
||
startSubmitLoading()
|
||
try {
|
||
await tradingHoursFormRef.value?.validate()
|
||
console.log(tradingHoursForm.value)
|
||
const params = {
|
||
id: tradingHoursForm.value.id,
|
||
saturday_start_time: tradingHoursForm.value.saturday_start_time,
|
||
saturday_end_time: tradingHoursForm.value.saturday_end_time,
|
||
sunday_start_time: tradingHoursForm.value.sunday_start_time,
|
||
sunday_end_time: tradingHoursForm.value.sunday_end_time,
|
||
}
|
||
console.log(params)
|
||
await editTradingHoursApi(params)
|
||
await getVarietyList()
|
||
tradingHoursVisible.value = false
|
||
} finally {
|
||
stopSubmitLoading()
|
||
}
|
||
}
|
||
/**
|
||
* @description: 取消周末交易时间
|
||
*/
|
||
const handleCancelTradingHours = () => {
|
||
tradingHoursVisible.value = false
|
||
tradingHoursForm.value = {
|
||
id: '',
|
||
saturday_start_time: '',
|
||
saturday_end_time: '',
|
||
sunday_start_time: '',
|
||
sunday_end_time: '',
|
||
}
|
||
}
|
||
const handleCloseTradingHours = () => {
|
||
tradingHoursVisible.value = false
|
||
tradingHoursForm.value = {
|
||
id: '',
|
||
saturday_start_time: '',
|
||
saturday_end_time: '',
|
||
sunday_start_time: '',
|
||
sunday_end_time: '',
|
||
}
|
||
}
|
||
|
||
</script>
|
||
|
||
<style scoped lang="scss">
|
||
.search-container {
|
||
:deep(.el-form) {
|
||
.el-form-item {
|
||
flex: none;
|
||
}
|
||
}
|
||
}
|
||
|
||
:deep(.el-upload) {
|
||
flex-direction: column;
|
||
}
|
||
</style>
|