foreign-admin-ui/src/views/trade/variety/index.vue

590 lines
16 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<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>