timerzz 8ac08ece08
All checks were successful
Build image / build (push) Successful in 9m18s
feat 增加得物历史价格
2024-05-16 15:23:21 +08:00

376 lines
11 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="h-full m-4 bg-white rounded-2 shadow-lg p-8 flex flex-col justify-between space-y-4 overscroll-auto">
<div class="flex justify-between">
<div class="flex items-center space-x-16">
<div>当前汇率<span class="text-xl">{{spiderCfg.exchangeRate}}</span></div>
<div>当前默认运费<span class="text-xl">{{spiderCfg.freight}}</span></div>
</div>
<div class="flex space-x-4">
<a-input placeholder="请输入关键词" v-model:value="query.keyword" @pressEnter="search"></a-input>
<a-button type="primary" :disabled="loading" @click="search">搜索</a-button>
</div>
</div>
<div class="h-full border-0 border-t-1 border-solid border-gray-300 pt-4 overscroll-auto ">
<a-spin :spinning="loading" :indicator="indicator">
<a-table :dataSource="data.list" :columns="columns" :custom-row="customRow" :pagination="false" @change="tableChange" rowKey="pid" @expand="expand">
<template #bodyCell="{ column, record }">
<template v-if="column.key === 'name'">
<a v-if="record.name !== '' " :href="record.link" target="_blank">{{record.name}}</a>
<span v-else>正在抓取信息</span>
</template>
<template v-else-if="column.key === 'updatedAt'">
<span>{{moment(record.updatedAt).format('YYYY-MM-DD HH:mm:ss')}}</span>
</template>
<template v-else-if="column.key === 'usPrice'">
<div class="flex space items-center">
<span>{{record.usPrice}}</span>
<CaretUpOutlined class="text-red" v-if="record.priceStatus === PRICE_STATUS.UP"/>
<CaretDownOutlined class="text-green" v-if="record.priceStatus === PRICE_STATUS.DOWN"/>
</div>
</template>
<template v-else-if="column.key === 'freight'">
<div class="flex space-x-4">
<a-input-number @click.stop v-if="editData.edit && editData.param==='freight' && editData.data.pid === record.pid" v-model:value="editData.data.freight" @pressEnter="doUpdate"></a-input-number>
<span v-else @dblclick="onEdit(record, 'freight')">{{record.freight}}</span>
<SaveOutlined class="cursor-pointer" v-if="editData.edit && editData.param==='freight' && editData.data.pid === record.pid" v-model:value="editData.data.freight" @click="doUpdate"/>
<!-- <FormOutlined v-else class="cursor-pointer" @click="onEdit(record, 'freight')"/>-->
</div>
</template>
<template v-else-if="column.key === 'dwPrice'">
<div class="flex space-x-4">
<a-input-number @click.stop v-if="editData.edit && editData.param==='dwPrice' && editData.data.pid === record.pid" v-model:value="editData.data.dwPrice" @pressEnter="doUpdate"></a-input-number>
<span v-else @dblclick="onEdit(record, 'dwPrice')">{{record.dwPrice}}</span>
<SaveOutlined class="cursor-pointer" v-if="editData.edit && editData.param==='dwPrice' && editData.data.pid === record.pid" v-model:value="editData.data.dwPrice" @click="doUpdate"/>
<!-- <FormOutlined v-else class="cursor-pointer" @click="onEdit(record, 'dwPrice')"/>-->
</div>
</template>
<template v-else-if="column.key === 'image'">
<a-image
:width="100"
:src="record.image"
/>
</template>
<template v-else-if="column.key === 'cnyPrice'">
<a-popover >
<template #content>
<div class="whitespace-pre">{{record.calMark}}</div>
</template>
<span class="cursor-pointer">{{parseFloat(record.cnyPrice).toFixed(2)}}</span>
</a-popover>
</template>
<template v-else-if="column.key === 'orderable'">
<span :class="[record.orderable ? '':'text-red']">{{record.orderable?'是':'否'}}</span>
</template>
<template v-else-if="column.key === 'opt'">
<a-popover title="" placement="bottom">
<template #content>
<a-button type="text" block @click="onEdit(record, 'modal')">编辑</a-button>
<a-button type="text" block @click="onWatch(record.pid, record.name)">一键蹲货</a-button>
</template>
<SettingOutlined class="cursor-pointer" />
</a-popover>
</template>
</template>
<template #expandedRowRender="{ record }">
<a-spin :spinning="!record.historyPrices">
<div class="px-4">
<div class="text-xl">历史价格</div>
<div class="flex space-x-16 my-2 text-lg" v-for="h in record.historyPrices">
<div>{{moment(h.createdAt).format('YYYY-MM-DD HH:mm:ss')}}</div>
<div>${{h.usPrice}}</div>
</div>
<div class="text-xl mt-4">得物价格CNY</div>
<div v-if="record.dwHistoryPrices?.length>0" class="flex space-x-16 my-2 text-lg" v-for="h in record.dwHistoryPrices">
<div>{{moment(h.createdAt).format('YYYY-MM-DD HH:mm:ss')}}</div>
<div>¥{{h.dwPrice}}</div>
</div>
<div class="text-lg" v-else >暂无</div>
</div>
</a-spin>
</template>
</a-table>
</a-spin>
</div>
<a-pagination :disabled="loading" class="text-right" v-model:current="query.page" v-model:page-size="query.size" :total="data.total" show-less-items @change="list"/>
</div>
<a-modal v-model:open="editData.editModal" @ok="doUpdate">
<template #title>
编辑
</template>
<a-form :model="editData.data" :labelCol="{span: 6}">
<a-form-item label="价格(美元)">
<a-input-number disabled v-model:value="editData.data.usPrice"/>
</a-form-item>
<a-form-item label="成本">
<a-input-number disabled v-model:value="editData.data.cnyPrice"/>
</a-form-item>
<a-form-item label="运费">
<a-input-number v-model:value="editData.data.freight"/>
</a-form-item>
<a-form-item label="得物价格">
<a-input-number v-model:value="editData.data.dwPrice"/>
</a-form-item>
<a-form-item label="备注">
<a-textarea v-model:value="editData.data.remark"/>
</a-form-item>
</a-form>
</a-modal>
</template>
<script setup>
import {computed, h, onMounted, reactive, ref} from "vue";
import {GetProduct, ListProducts, UpdateProduct} from "@/api/product.js";
import moment from "moment/moment.js";
import { clone } from 'radash'
import {LoadingOutlined, SaveOutlined,SettingOutlined,CaretUpOutlined,CaretDownOutlined} from "@ant-design/icons-vue";
import {message, Modal} from "ant-design-vue";
import {CreateWatcher} from "@/api/watcher.js";
import {GetSpiderCfg} from "@/api/spider.js";
onMounted(()=>{
list()
loadSpiderCfg()
})
const query = reactive({
page: 1,
size:6,
rate_sort: null,
disc_sort: null,
orderable: null,
keyword: '',
})
const loading = ref(false)
const data = ref({
total: 0,
list:[]
})
const list = ()=>{
loading.value = true
ListProducts(query).then(res=>{
data.value = res
}).catch(err => {
console.log(err)
}).finally(()=>{
loading.value = false
})
}
const search = ()=>{
query.page = 1
list()
}
const columns = computed(()=>[
{
title: '名称',
dataIndex: 'name',
key: 'name',
width:300,
},
{
title: '货号',
dataIndex: 'pid',
key: 'pid',
},
{
title: '图片',
dataIndex: 'image',
key: 'image',
},
{
title: '价格(美元)',
dataIndex: 'usPrice',
key: 'usPrice',
},
{
title: '折扣力度',
dataIndex: 'discPercent',
key: 'discPercent',
sorter:true,
sortOrder: query.disc_sort
},
{
title: '成本(CNY)',
dataIndex: 'cnyPrice',
key: 'cnyPrice',
},
{
title: '运费(CNY)',
dataIndex: 'freight',
key: 'freight',
},
{
title: '得物(CNY)',
dataIndex: 'dwPrice',
key: 'dwPrice',
},
{
title: '收益率',
dataIndex: 'rate',
key: 'rate',
sorter:true,
sortOrder: query.rate_sort
},
{
title: '可购买',
dataIndex: 'orderable',
key: 'orderable',
filteredValue: query.orderable,
filterMultiple:false,
filters:[
{
text: '可购买',
value: 'true',
},
{
text: '不可购买',
value: 'false',
},
]
},
{
title: '抓取时间',
dataIndex: 'updatedAt',
key: 'updatedAt',
},
{
title: '备注',
dataIndex: 'remark',
key: 'remark',
},
{
title: '操作',
key: 'opt',
},
])
const indicator = h(LoadingOutlined, {
style: {
fontSize: '32px',
},
spin: true,
});
const editData = reactive({
param: 'dwPrice',
data: {
},
edit: false,
editModal: false
})
const onEdit=(record, param) => {
editData.data = clone(record)
editData.param = param
editData.edit = true
if(param === 'modal'){
editData.editModal = true
}
}
const doUpdate=()=>{
if(!editData.data.pid){
message.error('缺少pid')
}
loading.value = true
UpdateProduct(editData.data).then(res=>{
message.success('更新成功')
}).catch(err=>{
message.error('更新失败')
console.log(err)
}).finally(()=>{
list()
editData.edit = false
loading.value = false
editData.editModal = false
})
}
const tableChange = (pagination, filters, sorter, { action, currentDataSource })=>{
if(sorter.columnKey === 'rate') {
query.rate_sort = sorter.order
}else if (sorter.columnKey === 'discPercent'){
query.disc_sort = sorter.order
}
query.orderable = filters.orderable
list()
}
//一键蹲货
const onWatch = (pid, name)=>{
Modal.confirm({
title: '确定要蹲该商品',
content: name,
okText: '确定',
cancelText: '取消',
onOk() {
CreateWatcher({
pid: pid,
remark:'',
pusherIds:[1]
}).then(res=>{
message.success("添加成功")
}).catch(err => {
message.error("添加失败")
console.log(err)
}).finally(()=>{
list()
})
},
});
}
const PRICE_STATUS = {
NO_CHANGE: 0,
UP: 1,
DOWN: 2
}
const spiderCfg = ref({
exchangeRate: 0,
freight: 0
})
const loadSpiderCfg = ()=>{
GetSpiderCfg().then(res=>{
spiderCfg.value = res
}).catch(err=>{
message.error("获取spider配置失败")
})
}
const customRow=(record, index)=>{
return {
onClick: (event) => {
editData.edit = false
}, // 点击行
}
}
const expand = (expanded, record)=>{
if(expanded && !record.historyPrices){
GetProduct(record.pid).then(res=>{
record.historyPrices = res.historyPrices
record.dwHistoryPrices = res.dwHistoryPrices
}).catch(err=>{
message.error("获取历史价格失败")
console.log(err)
})
}
}
</script>
<style scoped>
</style>