|
@@ -11,12 +11,6 @@
|
|
|
<el-form-item label="股票名称" prop="stockName">
|
|
<el-form-item label="股票名称" prop="stockName">
|
|
|
<el-input v-model="queryParams.stockName" placeholder="请输入股票名称" clearable @keyup.enter="handleQuery" />
|
|
<el-input v-model="queryParams.stockName" placeholder="请输入股票名称" clearable @keyup.enter="handleQuery" />
|
|
|
</el-form-item>
|
|
</el-form-item>
|
|
|
- <el-form-item label="池类型" prop="poolType">
|
|
|
|
|
- <el-select v-model="queryParams.poolType" placeholder="请选择池类型" clearable>
|
|
|
|
|
- <el-option label="超短池" :value="1" />
|
|
|
|
|
- <el-option label="强势池" :value="2" />
|
|
|
|
|
- </el-select>
|
|
|
|
|
- </el-form-item>
|
|
|
|
|
<el-form-item label="状态" prop="status">
|
|
<el-form-item label="状态" prop="status">
|
|
|
<el-select v-model="queryParams.status" placeholder="请选择状态" clearable>
|
|
<el-select v-model="queryParams.status" placeholder="请选择状态" clearable>
|
|
|
<el-option label="有效" :value="1" />
|
|
<el-option label="有效" :value="1" />
|
|
@@ -46,18 +40,15 @@
|
|
|
<el-card shadow="hover">
|
|
<el-card shadow="hover">
|
|
|
<template #header>
|
|
<template #header>
|
|
|
<el-row :gutter="10" class="mb-2">
|
|
<el-row :gutter="10" class="mb-2">
|
|
|
- <el-col :span="1.5">
|
|
|
|
|
- <el-button v-has-permi="['stock:info:add']" type="primary" plain icon="Plus" @click="handleAddStock">添加股票</el-button>
|
|
|
|
|
- </el-col>
|
|
|
|
|
- <el-col :span="1.5">
|
|
|
|
|
- <el-button type="success" plain icon="Refresh" @click="refreshQuotes" :loading="refreshing">刷新行情</el-button>
|
|
|
|
|
- </el-col>
|
|
|
|
|
<el-col :span="1.5">
|
|
<el-col :span="1.5">
|
|
|
<el-button v-has-permi="['stock:pool:remove']" type="danger" plain icon="Delete" :disabled="multiple" @click="handleDelete">移除</el-button>
|
|
<el-button v-has-permi="['stock:pool:remove']" type="danger" plain icon="Delete" :disabled="multiple" @click="handleDelete">移除</el-button>
|
|
|
</el-col>
|
|
</el-col>
|
|
|
<el-col :span="1.5">
|
|
<el-col :span="1.5">
|
|
|
<el-button v-has-permi="['stock:pool:export']" type="warning" plain icon="Download" @click="handleExport" :loading="exporting">导出</el-button>
|
|
<el-button v-has-permi="['stock:pool:export']" type="warning" plain icon="Download" @click="handleExport" :loading="exporting">导出</el-button>
|
|
|
</el-col>
|
|
</el-col>
|
|
|
|
|
+ <el-col :span="1.5">
|
|
|
|
|
+ <el-button type="info" plain icon="Refresh" @click="refreshQuotes" :loading="refreshing">刷新行情</el-button>
|
|
|
|
|
+ </el-col>
|
|
|
<right-toolbar v-model:show-search="showSearch" @query-table="getList"></right-toolbar>
|
|
<right-toolbar v-model:show-search="showSearch" @query-table="getList"></right-toolbar>
|
|
|
</el-row>
|
|
</el-row>
|
|
|
</template>
|
|
</template>
|
|
@@ -66,11 +57,6 @@
|
|
|
<el-table-column type="selection" width="50" align="center" />
|
|
<el-table-column type="selection" width="50" align="center" />
|
|
|
<el-table-column label="股票代码" align="center" prop="stockCode" />
|
|
<el-table-column label="股票代码" align="center" prop="stockCode" />
|
|
|
<el-table-column label="股票名称" align="center" prop="stockName" />
|
|
<el-table-column label="股票名称" align="center" prop="stockName" />
|
|
|
- <el-table-column label="池类型" align="center" prop="poolTypeName">
|
|
|
|
|
- <template #default="scope">
|
|
|
|
|
- <el-tag :type="scope.row.poolType === 1 ? 'danger' : 'warning'">{{ scope.row.poolTypeName }}</el-tag>
|
|
|
|
|
- </template>
|
|
|
|
|
- </el-table-column>
|
|
|
|
|
<el-table-column label="当前价" align="center" prop="currentPrice">
|
|
<el-table-column label="当前价" align="center" prop="currentPrice">
|
|
|
<template #default="scope">
|
|
<template #default="scope">
|
|
|
<span :class="getPriceClass(scope.row)">{{ formatPrice(scope.row.currentPrice) }}</span>
|
|
<span :class="getPriceClass(scope.row)">{{ formatPrice(scope.row.currentPrice) }}</span>
|
|
@@ -112,36 +98,12 @@
|
|
|
|
|
|
|
|
<pagination v-show="total > 0" v-model:page="queryParams.pageNum" v-model:limit="queryParams.pageSize" :total="total" @pagination="getList" />
|
|
<pagination v-show="total > 0" v-model:page="queryParams.pageNum" v-model:limit="queryParams.pageSize" :total="total" @pagination="getList" />
|
|
|
</el-card>
|
|
</el-card>
|
|
|
-
|
|
|
|
|
- <!-- 添加股票到stock_info对话框 -->
|
|
|
|
|
- <el-dialog v-model="dialog.visible" :title="dialog.title" width="500px" append-to-body>
|
|
|
|
|
- <el-form ref="formRef" :model="form" :rules="rules" label-width="80px">
|
|
|
|
|
- <el-form-item label="股票代码" prop="stockCode">
|
|
|
|
|
- <el-input v-model="form.stockCode" placeholder="请输入6位股票代码" maxlength="6" />
|
|
|
|
|
- </el-form-item>
|
|
|
|
|
- <el-form-item label="股票名称" prop="stockName">
|
|
|
|
|
- <el-input v-model="form.stockName" placeholder="请输入股票名称" maxlength="64" />
|
|
|
|
|
- </el-form-item>
|
|
|
|
|
- <el-form-item label="市场" prop="market">
|
|
|
|
|
- <el-select v-model="form.market" placeholder="请选择市场">
|
|
|
|
|
- <el-option label="上海" value="SH" />
|
|
|
|
|
- <el-option label="深圳" value="SZ" />
|
|
|
|
|
- </el-select>
|
|
|
|
|
- </el-form-item>
|
|
|
|
|
- </el-form>
|
|
|
|
|
- <template #footer>
|
|
|
|
|
- <el-button type="primary" @click="submitForm">确 定</el-button>
|
|
|
|
|
- <el-button @click="cancel">取 消</el-button>
|
|
|
|
|
- </template>
|
|
|
|
|
- </el-dialog>
|
|
|
|
|
</div>
|
|
</div>
|
|
|
</template>
|
|
</template>
|
|
|
|
|
|
|
|
-
|
|
|
|
|
-<script setup name="StockPool" lang="ts">
|
|
|
|
|
-import { ref, reactive, onMounted, onUnmounted, onActivated, getCurrentInstance } from 'vue';
|
|
|
|
|
|
|
+<script setup name="ShortPool" lang="ts">
|
|
|
|
|
+import { ref, onMounted, onUnmounted, onActivated, onDeactivated, getCurrentInstance } from 'vue';
|
|
|
import { listStockPool, removeFromPool } from '@/api/stock/pool';
|
|
import { listStockPool, removeFromPool } from '@/api/stock/pool';
|
|
|
-import { addStockInfo } from '@/api/stock/info';
|
|
|
|
|
|
|
|
|
|
const { proxy } = getCurrentInstance() as ComponentInternalInstance;
|
|
const { proxy } = getCurrentInstance() as ComponentInternalInstance;
|
|
|
|
|
|
|
@@ -155,10 +117,9 @@ const multiple = ref(true);
|
|
|
const total = ref(0);
|
|
const total = ref(0);
|
|
|
|
|
|
|
|
let refreshTimer: ReturnType<typeof setTimeout> | null = null;
|
|
let refreshTimer: ReturnType<typeof setTimeout> | null = null;
|
|
|
|
|
+let isPageVisible = true; // 页面是否可见
|
|
|
|
|
|
|
|
const queryFormRef = ref<ElFormInstance>();
|
|
const queryFormRef = ref<ElFormInstance>();
|
|
|
-const formRef = ref<ElFormInstance>();
|
|
|
|
|
-
|
|
|
|
|
const dateRange = ref<string[]>([]);
|
|
const dateRange = ref<string[]>([]);
|
|
|
|
|
|
|
|
const queryParams = ref({
|
|
const queryParams = ref({
|
|
@@ -166,31 +127,22 @@ const queryParams = ref({
|
|
|
pageSize: 20,
|
|
pageSize: 20,
|
|
|
stockCode: '',
|
|
stockCode: '',
|
|
|
stockName: '',
|
|
stockName: '',
|
|
|
- poolType: undefined as number | undefined,
|
|
|
|
|
|
|
+ poolType: 1, // 固定为超短池
|
|
|
status: 1 as number | undefined,
|
|
status: 1 as number | undefined,
|
|
|
startDate: undefined as string | undefined,
|
|
startDate: undefined as string | undefined,
|
|
|
endDate: undefined as string | undefined
|
|
endDate: undefined as string | undefined
|
|
|
});
|
|
});
|
|
|
|
|
|
|
|
-const dialog = reactive({ visible: false, title: '' });
|
|
|
|
|
-const form = ref<any>({});
|
|
|
|
|
-
|
|
|
|
|
-const rules = {
|
|
|
|
|
- stockCode: [
|
|
|
|
|
- { required: true, message: '股票代码不能为空', trigger: 'blur' },
|
|
|
|
|
- { pattern: /^\d{6}$/, message: '股票代码必须是6位数字', trigger: 'blur' }
|
|
|
|
|
- ],
|
|
|
|
|
- stockName: [{ required: true, message: '股票名称不能为空', trigger: 'blur' }],
|
|
|
|
|
- market: [{ required: true, message: '市场不能为空', trigger: 'change' }]
|
|
|
|
|
-};
|
|
|
|
|
|
|
|
|
|
/** 获取随机刷新间隔 (2000-3000ms) */
|
|
/** 获取随机刷新间隔 (2000-3000ms) */
|
|
|
const getRandomInterval = () => 2000 + Math.random() * 1000;
|
|
const getRandomInterval = () => 2000 + Math.random() * 1000;
|
|
|
|
|
|
|
|
/** 启动自动刷新 */
|
|
/** 启动自动刷新 */
|
|
|
const startAutoRefresh = () => {
|
|
const startAutoRefresh = () => {
|
|
|
|
|
+ if (!isPageVisible) return; // 页面不可见时不启动
|
|
|
stopAutoRefresh();
|
|
stopAutoRefresh();
|
|
|
refreshTimer = setTimeout(async () => {
|
|
refreshTimer = setTimeout(async () => {
|
|
|
|
|
+ if (!isPageVisible) return; // 再次检查
|
|
|
await refreshQuotesOnly();
|
|
await refreshQuotesOnly();
|
|
|
startAutoRefresh();
|
|
startAutoRefresh();
|
|
|
}, getRandomInterval());
|
|
}, getRandomInterval());
|
|
@@ -204,7 +156,18 @@ const stopAutoRefresh = () => {
|
|
|
}
|
|
}
|
|
|
};
|
|
};
|
|
|
|
|
|
|
|
-/** 查询列表(首次加载或翻页) */
|
|
|
|
|
|
|
+/** 处理页面可见性变化 */
|
|
|
|
|
+const handleVisibilityChange = () => {
|
|
|
|
|
+ if (document.hidden) {
|
|
|
|
|
+ isPageVisible = false;
|
|
|
|
|
+ stopAutoRefresh();
|
|
|
|
|
+ } else {
|
|
|
|
|
+ isPageVisible = true;
|
|
|
|
|
+ startAutoRefresh();
|
|
|
|
|
+ }
|
|
|
|
|
+};
|
|
|
|
|
+
|
|
|
|
|
+/** 查询列表 */
|
|
|
const getList = async () => {
|
|
const getList = async () => {
|
|
|
loading.value = true;
|
|
loading.value = true;
|
|
|
stopAutoRefresh();
|
|
stopAutoRefresh();
|
|
@@ -214,28 +177,25 @@ const getList = async () => {
|
|
|
total.value = res.total || 0;
|
|
total.value = res.total || 0;
|
|
|
startAutoRefresh();
|
|
startAutoRefresh();
|
|
|
} catch (error) {
|
|
} catch (error) {
|
|
|
- console.error('获取股票池列表失败:', error);
|
|
|
|
|
|
|
+ console.error('获取超短池列表失败:', error);
|
|
|
} finally {
|
|
} finally {
|
|
|
loading.value = false;
|
|
loading.value = false;
|
|
|
}
|
|
}
|
|
|
};
|
|
};
|
|
|
|
|
|
|
|
-/** 局部刷新行情数据(不显示loading) */
|
|
|
|
|
|
|
+/** 局部刷新行情数据 */
|
|
|
const refreshQuotesOnly = async () => {
|
|
const refreshQuotesOnly = async () => {
|
|
|
if (poolList.value.length === 0) return;
|
|
if (poolList.value.length === 0) return;
|
|
|
try {
|
|
try {
|
|
|
const res = await listStockPool(queryParams.value);
|
|
const res = await listStockPool(queryParams.value);
|
|
|
const newData = res.rows || [];
|
|
const newData = res.rows || [];
|
|
|
- // 局部更新行情字段
|
|
|
|
|
poolList.value.forEach((item) => {
|
|
poolList.value.forEach((item) => {
|
|
|
const newItem = newData.find((n: any) => n.id === item.id);
|
|
const newItem = newData.find((n: any) => n.id === item.id);
|
|
|
if (newItem) {
|
|
if (newItem) {
|
|
|
item.currentPrice = newItem.currentPrice;
|
|
item.currentPrice = newItem.currentPrice;
|
|
|
item.changePercent = newItem.changePercent;
|
|
item.changePercent = newItem.changePercent;
|
|
|
- item.changeAmount = newItem.changeAmount;
|
|
|
|
|
item.turnoverRate = newItem.turnoverRate;
|
|
item.turnoverRate = newItem.turnoverRate;
|
|
|
item.tradeAmount = newItem.tradeAmount;
|
|
item.tradeAmount = newItem.tradeAmount;
|
|
|
- item.yesterdayClose = newItem.yesterdayClose;
|
|
|
|
|
item.profitPercent = newItem.profitPercent;
|
|
item.profitPercent = newItem.profitPercent;
|
|
|
}
|
|
}
|
|
|
});
|
|
});
|
|
@@ -256,16 +216,16 @@ const refreshQuotes = async () => {
|
|
|
}
|
|
}
|
|
|
};
|
|
};
|
|
|
|
|
|
|
|
-/** 导出(获取全表数据) */
|
|
|
|
|
|
|
+/** 导出 */
|
|
|
const handleExport = async () => {
|
|
const handleExport = async () => {
|
|
|
exporting.value = true;
|
|
exporting.value = true;
|
|
|
try {
|
|
try {
|
|
|
proxy?.download('stock/pool/export', {
|
|
proxy?.download('stock/pool/export', {
|
|
|
stockCode: queryParams.value.stockCode,
|
|
stockCode: queryParams.value.stockCode,
|
|
|
stockName: queryParams.value.stockName,
|
|
stockName: queryParams.value.stockName,
|
|
|
- poolType: queryParams.value.poolType,
|
|
|
|
|
|
|
+ poolType: 1,
|
|
|
status: queryParams.value.status
|
|
status: queryParams.value.status
|
|
|
- }, `stock_pool_${new Date().getTime()}.xlsx`);
|
|
|
|
|
|
|
+ }, `short_pool_${new Date().getTime()}.xlsx`);
|
|
|
} finally {
|
|
} finally {
|
|
|
setTimeout(() => { exporting.value = false; }, 1000);
|
|
setTimeout(() => { exporting.value = false; }, 1000);
|
|
|
}
|
|
}
|
|
@@ -274,7 +234,6 @@ const handleExport = async () => {
|
|
|
/** 搜索按钮 */
|
|
/** 搜索按钮 */
|
|
|
const handleQuery = () => {
|
|
const handleQuery = () => {
|
|
|
queryParams.value.pageNum = 1;
|
|
queryParams.value.pageNum = 1;
|
|
|
- // 处理日期范围
|
|
|
|
|
if (dateRange.value && dateRange.value.length === 2) {
|
|
if (dateRange.value && dateRange.value.length === 2) {
|
|
|
queryParams.value.startDate = dateRange.value[0];
|
|
queryParams.value.startDate = dateRange.value[0];
|
|
|
queryParams.value.endDate = dateRange.value[1];
|
|
queryParams.value.endDate = dateRange.value[1];
|
|
@@ -289,7 +248,6 @@ const handleQuery = () => {
|
|
|
const resetQuery = () => {
|
|
const resetQuery = () => {
|
|
|
queryFormRef.value?.resetFields();
|
|
queryFormRef.value?.resetFields();
|
|
|
dateRange.value = [];
|
|
dateRange.value = [];
|
|
|
- queryParams.value.poolType = undefined;
|
|
|
|
|
queryParams.value.status = 1;
|
|
queryParams.value.status = 1;
|
|
|
queryParams.value.startDate = undefined;
|
|
queryParams.value.startDate = undefined;
|
|
|
queryParams.value.endDate = undefined;
|
|
queryParams.value.endDate = undefined;
|
|
@@ -302,24 +260,6 @@ const handleSelectionChange = (selection: any[]) => {
|
|
|
multiple.value = !selection.length;
|
|
multiple.value = !selection.length;
|
|
|
};
|
|
};
|
|
|
|
|
|
|
|
-/** 添加股票按钮(添加到stock_info表) */
|
|
|
|
|
-const handleAddStock = () => {
|
|
|
|
|
- reset();
|
|
|
|
|
- dialog.visible = true;
|
|
|
|
|
- dialog.title = '添加股票信息';
|
|
|
|
|
-};
|
|
|
|
|
-
|
|
|
|
|
-/** 提交表单(添加股票到stock_info) */
|
|
|
|
|
-const submitForm = () => {
|
|
|
|
|
- formRef.value?.validate(async (valid: boolean) => {
|
|
|
|
|
- if (valid) {
|
|
|
|
|
- await addStockInfo(form.value);
|
|
|
|
|
- proxy?.$modal.msgSuccess('添加成功');
|
|
|
|
|
- dialog.visible = false;
|
|
|
|
|
- }
|
|
|
|
|
- });
|
|
|
|
|
-};
|
|
|
|
|
-
|
|
|
|
|
/** 删除按钮 */
|
|
/** 删除按钮 */
|
|
|
const handleDelete = async (row?: any) => {
|
|
const handleDelete = async (row?: any) => {
|
|
|
const poolIds = row?.id ? [row.id] : ids.value;
|
|
const poolIds = row?.id ? [row.id] : ids.value;
|
|
@@ -329,18 +269,6 @@ const handleDelete = async (row?: any) => {
|
|
|
await getList();
|
|
await getList();
|
|
|
};
|
|
};
|
|
|
|
|
|
|
|
-/** 取消按钮 */
|
|
|
|
|
-const cancel = () => {
|
|
|
|
|
- dialog.visible = false;
|
|
|
|
|
- reset();
|
|
|
|
|
-};
|
|
|
|
|
-
|
|
|
|
|
-/** 表单重置 */
|
|
|
|
|
-const reset = () => {
|
|
|
|
|
- form.value = { stockCode: '', stockName: '', market: undefined };
|
|
|
|
|
- formRef.value?.resetFields();
|
|
|
|
|
-};
|
|
|
|
|
-
|
|
|
|
|
/** 格式化价格 */
|
|
/** 格式化价格 */
|
|
|
const formatPrice = (val: any) => (val != null ? Number(val).toFixed(2) : '-');
|
|
const formatPrice = (val: any) => (val != null ? Number(val).toFixed(2) : '-');
|
|
|
|
|
|
|
@@ -360,15 +288,22 @@ const getChangeClass = (val: any) => {
|
|
|
};
|
|
};
|
|
|
|
|
|
|
|
onMounted(() => {
|
|
onMounted(() => {
|
|
|
|
|
+ document.addEventListener('visibilitychange', handleVisibilityChange);
|
|
|
getList();
|
|
getList();
|
|
|
});
|
|
});
|
|
|
|
|
|
|
|
onActivated(() => {
|
|
onActivated(() => {
|
|
|
- // 页面被激活时(从其他页面切换回来)刷新数据
|
|
|
|
|
|
|
+ isPageVisible = true;
|
|
|
getList();
|
|
getList();
|
|
|
});
|
|
});
|
|
|
|
|
|
|
|
|
|
+onDeactivated(() => {
|
|
|
|
|
+ isPageVisible = false;
|
|
|
|
|
+ stopAutoRefresh();
|
|
|
|
|
+});
|
|
|
|
|
+
|
|
|
onUnmounted(() => {
|
|
onUnmounted(() => {
|
|
|
|
|
+ document.removeEventListener('visibilitychange', handleVisibilityChange);
|
|
|
stopAutoRefresh();
|
|
stopAutoRefresh();
|
|
|
});
|
|
});
|
|
|
</script>
|
|
</script>
|