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