| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379 |
- <script setup>
- import { requestClient } from '#/api/request';
- import { Button, message, Form, InputNumber, RadioGroup, Radio, Checkbox, Drawer, Input, Switch } from 'ant-design-vue';
- import { ref, reactive, computed, watch, onMounted, onUnmounted } from 'vue';
- import dayjs from 'dayjs';
- // import MatchCard from '../components/match_card.vue';
- import SolutionItem from '../components/solution_item.vue';
- import { useContentsPositionStore } from '@vben/stores';
- const contentsPositionStore = useContentsPositionStore();
- const solutions = ref([]);
- const markCount = ref({ all: 0, rollball: 0, today: 0, early: 0 });
- const selectedSolutions = reactive([]);
- const totalProfit = ref({});
- const loopActive = ref(false);
- const loopTimer = ref(null);
- const updateTimer = ref(null);
- const minProfitRate = ref(2);
- const marketType = ref(-1);
- const dataType = ref(0);
- const showLower = ref(false);
- const searchValue = ref('');
- const updateLoaderHide = ref(null);
- const totalProfitVisible = ref(false);
- const fixFloat = (number, x = 2) => {
- return parseFloat(number.toFixed(x));
- }
- const headerStyle = computed(() => {
- return {
- position: contentsPositionStore.position,
- top: contentsPositionStore.top,
- left: contentsPositionStore.left,
- width: contentsPositionStore.width,
- paddingLeft: contentsPositionStore.paddingLeft,
- }
- });
- const solutionsList = computed(() => {
- return solutions.value.map(item => {
- const selected = selectedSolutions.findIndex(sol => sol.id == item.id) >= 0;
- const topSolutions = item.solutions.slice(0, 10);
- return { ...item, solutions: topSolutions, selected };
- });
- });
- const getSolutions = async () => {
- try {
- const mk = marketType.value;
- const tp = dataType.value;
- const sk = searchValue.value.trim();
- const win_min = !!sk ? -99999 : minProfitRate.value * 100;
- const with_events = true;
- const show_lower = showLower.value;
- const data = await requestClient.get('/pstery/get_games_solutions', { params: { win_min, mk, tp, sk, with_events, show_lower } });
- return data;
- }
- catch (error) {
- console.error('Failed to fetch solutions:', error);
- message.error('获取中单方案失败');
- return [];
- }
- }
- const calcTotalProfit = async () => {
- const sids = selectedSolutions.map(item => item.sid);
- try {
- const totalProfit = await requestClient.post('/pstery/calc_total_profit', {sids});
- return totalProfit;
- }
- catch (error) {
- console.error('Failed to calc total profit:', error);
- message.error('计算综合利润失败');
- return {};
- }
- }
- const updateSolutions = async (showLoading=false) => {
- clearTimeout(loopTimer.value);
- if (showLoading && !updateLoaderHide.value) {
- updateLoaderHide.value = message.loading('数据加载中...', 0);
- }
- getSolutions()
- .then(({ gamesSolutions, mkCount }) => {
- solutions.value = gamesSolutions ?? [];
- markCount.value = mkCount;
- })
- .catch(error => {
- console.error('Failed to update solutions:', error);
- message.error('获取中单方案失败');
- })
- .finally(() => {
- updateLoaderHide.value?.();
- updateLoaderHide.value = null;
- if (loopActive.value) {
- loopTimer.value = setTimeout(() => {
- updateSolutions();
- }, 1000 * 10);
- }
- });
- }
- const showTotalProfit = async () => {
- totalProfit.value = await calcTotalProfit();
- totalProfitVisible.value = true;
- const { profit } = totalProfit.value;
- console.log('profit', profit);
- };
- const toggleSolution = (data) => {
- // console.log('toggleSolution', data);
- const { id, sid } = data;
- const findIndex = selectedSolutions.findIndex(item => item.id == id);
- if (findIndex >= 0) {
- if (selectedSolutions[findIndex].sid == sid) {
- selectedSolutions.splice(findIndex, 1);
- }
- else {
- selectedSolutions.splice(findIndex, 1, data);
- }
- }
- else if (selectedSolutions.length < 2) {
- selectedSolutions.push(data);
- }
- else {
- selectedSolutions.splice(1, 1, data);
- }
- if (selectedSolutions.length == 2) {
- showTotalProfit();
- }
- }
- const setLocalStorage = (key, value) => {
- localStorage.setItem(key, JSON.stringify(value));
- }
- const getLocalStorage = (key) => {
- const value = localStorage.getItem(key);
- return value ? JSON.parse(value) : null;
- }
- watch(searchValue, (newVal, oldVal) => {
- if (newVal.trim() == oldVal.trim()) {
- return;
- }
- clearTimeout(updateTimer.value);
- updateTimer.value = setTimeout(() => {
- updateSolutions();
- }, 1000);
- });
- watch(minProfitRate, (newVal) => {
- clearTimeout(updateTimer.value);
- updateTimer.value = setTimeout(() => {
- setLocalStorage('minProfitRate', newVal);
- updateSolutions();
- }, 1000);
- });
- watch(marketType, (newVal) => {
- if (!updateLoaderHide.value) {
- updateLoaderHide.value = message.loading('数据更新中...', 0);
- }
- clearTimeout(updateTimer.value);
- updateTimer.value = setTimeout(() => {
- setLocalStorage('marketType', newVal);
- updateSolutions();
- }, 1000);
- });
- watch(dataType, (newVal) => {
- if (!updateLoaderHide.value) {
- updateLoaderHide.value = message.loading('数据更新中...', 0);
- }
- clearTimeout(updateTimer.value);
- updateTimer.value = setTimeout(() => {
- setLocalStorage('dataType', newVal);
- updateSolutions();
- }, 1000);
- });
- watch(showLower, (newVal) => {
- if (!updateLoaderHide.value) {
- updateLoaderHide.value = message.loading('数据更新中...', 0);
- }
- clearTimeout(updateTimer.value);
- updateTimer.value = setTimeout(() => {
- setLocalStorage('showLower', newVal);
- updateSolutions();
- }, 1000);
- });
- onMounted(() => {
- loopActive.value = true;
- const min_win_rate = getLocalStorage('minProfitRate');
- const mk = getLocalStorage('marketType');
- const tp = getLocalStorage('dataType');
- const show_lower = getLocalStorage('showLower');
- if (min_win_rate !== null) {
- minProfitRate.value = min_win_rate;
- }
- if (mk !== null) {
- marketType.value = mk;
- }
- if (tp !== null) {
- dataType.value = tp;
- }
- if (show_lower !== null) {
- showLower.value = show_lower;
- }
- setTimeout(() => {
- updateSolutions(true);
- }, 100);
- });
- onUnmounted(() => {
- loopActive.value = false;
- });
- </script>
- <template>
- <div class="solution-container">
- <div class="contents-header transition-all duration-200" :style="headerStyle">
- <div class="solution-options">
- <Form layout="inline" class="sol-opt-container">
- <Form.Item label="比赛类型" class="sol-opt-item">
- <RadioGroup v-model:value="marketType">
- <Radio :value="-1">全部({{ markCount?.all ?? 0 }})</Radio>
- <Radio :value="2">滚球({{ markCount?.rollball ?? 0 }})</Radio>
- <Radio :value="1">今日({{ markCount?.today ?? 0 }})</Radio>
- <Radio :value="0">早盘({{ markCount?.early ?? 0 }})</Radio>
- </RadioGroup>
- </Form.Item>
- <Form.Item label="盘口类型" class="sol-opt-item">
- <RadioGroup v-model:value="dataType">
- <Radio :value="0">全部</Radio>
- <Radio :value="1">让球</Radio>
- <Radio :value="2">大小</Radio>
- </RadioGroup>
- </Form.Item>
- <Form.Item label="最小利润率(%)" class="sol-opt-item input-item" :class="{ 'disabled': !!searchValue.trim() }">
- <InputNumber class="number-input" size="small" max="100" min="-100" step="0.1" placeholder="最小利润率(%)" v-model:value="minProfitRate"/>
- </Form.Item>
- <Form.Item label="显示低赔盘" class="sol-opt-item input-item">
- <Switch v-model:checked="showLower" />
- </Form.Item>
- <Form.Item class="sol-opt-item input-item">
- <Input class="search-input" placeholder="搜索联赛/球队" :allowClear="true" v-model:value="searchValue"/>
- </Form.Item>
- </Form>
- </div>
- <div class="solution-header">
- <span>PS</span>
- <span><Checkbox>OB</Checkbox></span>
- <span><Checkbox>HG</Checkbox></span>
- <span><Checkbox>IM</Checkbox></span>
- <!-- <em>利润</em> -->
- </div>
- </div>
- <div class="solution-list" v-if="solutionsList.length">
- <SolutionItem v-for="(solution, index) in solutionsList"
- :key="solution.id"
- :serial="index+1"
- :id="solution.id"
- :mk="solution.mk"
- :rel="solution.rel"
- :selected="solution.selected"
- :solutions="solution.solutions"
- @toggle="toggleSolution"
- />
- </div>
- <div class="list-empty" v-else>暂无数据</div>
- </div>
- </template>
- <style lang="scss" scoped>
- .contents-header {
- position: fixed;
- top: 0;
- left: 0;
- z-index: 201;
- width: 100%;
- border-bottom: 1px solid hsl(var(--border));
- background-color: hsl(var(--background));
- }
- .solution-options {
- position: relative;
- display: flex;
- align-items: center;
- padding: 5px 20px;
- border-bottom: 1px solid hsl(var(--border));
- }
- .sol-opt-container {
- flex-grow: 1;
- // justify-content: flex-end;
- }
- .sol-opt-item {
- margin-inline-end: 0 !important;
- &:nth-child(2) {
- margin-inline-start: auto;
- }
- &:nth-child(n+3) {
- padding-inline-start: 15px;
- border-left: 1px solid hsl(var(--border));
- }
- &.input-item:not(:last-child) {
- padding-inline-end: 15px;
- }
- &.disabled {
- opacity: 0.5;
- * {
- text-decoration: line-through;
- }
- }
- .search-input, .number-input {
- height: 28px;
- }
- .search-input {
- width: 150px;
- }
- .number-input {
- display: inline-flex;
- width: 60px;
- align-items: center;
- }
- }
- .solution-header {
- position: relative;
- display: flex;
- align-items: center;
- height: 30px;
- padding: 0 20px;
- span,
- em {
- display: block;
- text-align: center;
- }
- span {
- flex: 1;
- }
- // em {
- // width: 80px;
- // font-style: normal;
- // }
- }
- .solution-container {
- padding-top: 74px;
- }
- .solution-list {
- padding: 20px;
- overflow: hidden;
- }
- .list-empty {
- text-align: center;
- padding: 10px;
- font-size: 18px;
- color: hsl(var(--foreground) / 0.7);
- }
- </style>
|