This commit is contained in:
parent
cd6ef161b9
commit
be76d0b434
@ -12,10 +12,12 @@
|
||||
"@ant-design/icons-vue": "^7.0.1",
|
||||
"ant-design-vue": "4.x",
|
||||
"dayjs": "^1.11.13",
|
||||
"echarts": "^5.5.1",
|
||||
"mande": "^2.0.8",
|
||||
"moment": "^2.30.1",
|
||||
"radash": "^12.1.0",
|
||||
"vue": "^3.4.21",
|
||||
"vue-echarts": "^7.0.3",
|
||||
"vue-router": "4"
|
||||
},
|
||||
"devDependencies": {
|
||||
@ -23,6 +25,7 @@
|
||||
"@vitejs/plugin-vue": "^5.0.4",
|
||||
"unocss": "^0.59.0",
|
||||
"unplugin-vue-components": "^0.26.0",
|
||||
"unplugin-vue-router": "^0.10.7",
|
||||
"vite": "^5.2.0"
|
||||
}
|
||||
}
|
||||
|
36
src/api/articles.js
Normal file
36
src/api/articles.js
Normal file
@ -0,0 +1,36 @@
|
||||
import {mande} from "mande";
|
||||
import {queryRemoveZero} from "@/api/utils.js";
|
||||
|
||||
const articles = mande('/api/v2/articles')
|
||||
|
||||
export const ListArticles = (q) => {
|
||||
const query = queryRemoveZero(q)
|
||||
return articles.get({query})
|
||||
}
|
||||
|
||||
export const GetArticle = (id) =>{
|
||||
return articles.get(`/${id}`)
|
||||
}
|
||||
|
||||
export const UpdateArticle = (article)=>{
|
||||
return articles.patch(article)
|
||||
}
|
||||
|
||||
export const GetBrandsDict=()=>{
|
||||
return articles.get('dict/brand')
|
||||
}
|
||||
export const GetProviderHistory=(id)=>{
|
||||
return articles.get(`/provider/history/${id}`)
|
||||
}
|
||||
|
||||
export const GetSellerHistory=(id)=>{
|
||||
return articles.get(`/seller/history/${id}`)
|
||||
}
|
||||
|
||||
export const UpdateProviderArticle=(article)=>{
|
||||
return articles.patch('provider',article)
|
||||
}
|
||||
|
||||
export const UpdateSellerArticle=(article)=>{
|
||||
return articles.patch('seller',article)
|
||||
}
|
@ -1,25 +1,47 @@
|
||||
import {mande} from "mande";
|
||||
import {queryRemoveZero} from "@/api/utils.js";
|
||||
|
||||
const product = mande('/api/v2/providers')
|
||||
const providers = mande('/api/v2/providers')
|
||||
|
||||
export const ListProviders = (q) => {
|
||||
const query = queryRemoveZero(q)
|
||||
return product.get({query})
|
||||
return providers.get({query})
|
||||
}
|
||||
|
||||
export const CreateProvider = (provider)=>{
|
||||
return product.post(provider)
|
||||
return providers.post(provider)
|
||||
}
|
||||
|
||||
export const UpdateProvider = (provider)=>{
|
||||
return product.put(provider)
|
||||
return providers.put(provider)
|
||||
}
|
||||
|
||||
export const GetProvider = (id) =>{
|
||||
return product.get(`/${id}`)
|
||||
return providers.get(`/${id}`)
|
||||
}
|
||||
|
||||
export const DeleteProvider = (id)=>{
|
||||
return product.delete(`/${id}`)
|
||||
return providers.delete(`/${id}`)
|
||||
}
|
||||
|
||||
export const GetProviderDictStatus = () =>{
|
||||
return providers.get('/dict/status')
|
||||
}
|
||||
|
||||
export const FindProviders= (q) =>{
|
||||
const query = queryRemoveZero(q)
|
||||
return providers.get('find',{query})
|
||||
}
|
||||
|
||||
|
||||
|
||||
const provider = mande('/api/v2/provider')
|
||||
|
||||
// 拉取供应商所有商品价格
|
||||
export const FetchProviderArticles = (providerId)=>{
|
||||
return provider.post(`/${providerId}/pull`)
|
||||
}
|
||||
|
||||
export const FetchProviderArticle = (providerArticle)=>{
|
||||
return provider.post(`/${providerArticle.providerId}/fetch/${providerArticle.id}`)
|
||||
}
|
@ -1,25 +1,48 @@
|
||||
import {mande} from "mande";
|
||||
import {queryRemoveZero} from "@/api/utils.js";
|
||||
|
||||
const product = mande('/api/v2/sellers')
|
||||
const sellers = mande('/api/v2/sellers')
|
||||
|
||||
export const ListSellers = (q) => {
|
||||
const query = queryRemoveZero(q)
|
||||
return product.get({query})
|
||||
return sellers.get({query})
|
||||
}
|
||||
|
||||
export const FindSellers= (q) =>{
|
||||
const query = queryRemoveZero(q)
|
||||
return sellers.get('find',{query})
|
||||
}
|
||||
|
||||
export const CreateSeller = (seller)=>{
|
||||
return product.post(seller)
|
||||
return sellers.post(seller)
|
||||
}
|
||||
|
||||
export const UpdateSeller = (seller)=>{
|
||||
return product.put(seller)
|
||||
return sellers.put(seller)
|
||||
}
|
||||
|
||||
export const GetSeller = (id) =>{
|
||||
return product.get(`/${id}`)
|
||||
return sellers.get(`/${id}`)
|
||||
}
|
||||
|
||||
export const DeleteSeller = (id)=>{
|
||||
return product.delete(`/${id}`)
|
||||
return sellers.delete(`/${id}`)
|
||||
}
|
||||
|
||||
export const GetSellerDictStatus = () =>{
|
||||
return sellers.get('/dict/status')
|
||||
}
|
||||
|
||||
|
||||
const seller = mande('/api/v2/seller')
|
||||
export const FetchSellerArticles = (sellerId)=>{
|
||||
return seller.post(`/${sellerId}/pull`)
|
||||
}
|
||||
|
||||
export const MatchSellerSku = (sellerArticle)=>{
|
||||
return seller.post(`/${sellerArticle.sellerId}/match`, sellerArticle)
|
||||
}
|
||||
|
||||
export const FetchSellerArticle = (sellerArticle)=>{
|
||||
return seller.post(`/${sellerArticle.sellerId}/fetch/${sellerArticle.id}`)
|
||||
}
|
170
src/componse/provider-article/provider-article-panel.vue
Normal file
170
src/componse/provider-article/provider-article-panel.vue
Normal file
@ -0,0 +1,170 @@
|
||||
<template>
|
||||
<a-collapse-panel :key="`${provider.providerId}`" >
|
||||
<template #extra>
|
||||
<div class="flex space-x-4">
|
||||
<div>更新时间:{{dayjs(provider.updatedAt).format('YYYY-MM-DD HH:mm:ss')}}</div>
|
||||
<div v-if="provider.skuID">抓取:<a-switch :checked="!provider.exclude" @click="onClickExclude"/></div>
|
||||
</div>
|
||||
</template>
|
||||
<template #header>
|
||||
<span class="text-lg">{{header}}</span>
|
||||
</template>
|
||||
<div class="flex flex-col space-y-8">
|
||||
<div class="flex space-x-4 ">
|
||||
<div class="w-50">货号:{{provider.pid || '-'}}</div>
|
||||
<!-- <div class="w-50">SpuID: {{provider.spuID || '-'}} </div>-->
|
||||
<div class="w-50">SkuID: {{provider.skuID || '-'}}</div>
|
||||
</div>
|
||||
<div class="flex space-x-4 items-center">
|
||||
<div class="w-50">当前售价:$ {{provider.cost.originalPrice || '-'}}</div>
|
||||
<a-popover v-if="provider.cost.calMark" trigger="hover">
|
||||
<template #content>
|
||||
<span class="whitespace-pre">
|
||||
{{provider.cost.calMark}}
|
||||
</span>
|
||||
</template>
|
||||
<div class="w-50">当前到手价:¥ {{provider.cost.finalPrice || '-'}}</div>
|
||||
</a-popover>
|
||||
<div class="w-50" v-else>当前到手价:¥ {{provider.cost.finalPrice || '-'}}</div>
|
||||
<a-button type="primary" @click="fetchArticle(provider)" :loading="fetchLoading">立即拉取</a-button>
|
||||
</div>
|
||||
<v-chart class="h-400px w-full" :option="options" />
|
||||
</div>
|
||||
</a-collapse-panel>
|
||||
</template>
|
||||
<script setup>
|
||||
import dayjs from "dayjs";
|
||||
import {GetProviderHistory, UpdateProviderArticle} from "@/api/articles.js";
|
||||
import {message} from "ant-design-vue";
|
||||
import {computed, ref, watch} from "vue";
|
||||
import VChart from "vue-echarts";
|
||||
|
||||
|
||||
const props = defineProps({
|
||||
provider: {
|
||||
type: Object,
|
||||
default: () => {
|
||||
return {
|
||||
providerId: '',
|
||||
updatedAt: 0,
|
||||
skuID:'',
|
||||
pid: '',
|
||||
cost: {
|
||||
originalPrice: 0,
|
||||
finalPrice: 0,
|
||||
calMark: ''
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
providerDict:{
|
||||
type: Array,
|
||||
default: () => {
|
||||
return []
|
||||
}
|
||||
},
|
||||
open: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
}
|
||||
})
|
||||
|
||||
const header = computed(() => {
|
||||
return props.providerDict.find(s => s.providerId === props.provider.providerId)?.name
|
||||
})
|
||||
|
||||
const onClickExclude = (checked, e) => {
|
||||
props.provider.exclude = !checked
|
||||
UpdateProviderArticle(props.provider).then(res => {
|
||||
if (res.code !== 200) {
|
||||
message.error(`更新失败:${res.msg}`)
|
||||
} else {
|
||||
message.success('更新成功')
|
||||
}
|
||||
}).catch(err => {
|
||||
message.error('更新失败')
|
||||
console.log(err)
|
||||
})
|
||||
}
|
||||
|
||||
watch(() => props.open, (newVal) => {
|
||||
if (newVal && !(props.provider.historyPrice?.length > 0)) {
|
||||
GetProviderHistory(props.provider.id).then(res => {
|
||||
if (res.code !== 200) {
|
||||
message.error(`获取历史数据失败:${res.msg}`)
|
||||
} else {
|
||||
props.provider.historyPrice = res.data
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
// 图表
|
||||
|
||||
import { use } from 'echarts/core'
|
||||
import { LineChart } from 'echarts/charts'
|
||||
import { GridComponent, TitleComponent, TooltipComponent, LegendComponent } from 'echarts/components'
|
||||
import { CanvasRenderer } from 'echarts/renderers'
|
||||
import {FetchProviderArticle} from "@/api/provider.js";
|
||||
|
||||
use([GridComponent, LineChart, CanvasRenderer, TitleComponent , TooltipComponent, LegendComponent ])
|
||||
|
||||
const options = computed(()=> {
|
||||
return {
|
||||
title: {
|
||||
text: "历史价格",
|
||||
left: "center"
|
||||
},
|
||||
tooltip: {
|
||||
trigger: 'item',
|
||||
formatter: '{a} <br/>{b} : {c}'
|
||||
},
|
||||
legend: {
|
||||
left: 'left'
|
||||
},
|
||||
xAxis: {
|
||||
type: 'category',
|
||||
name: '日期',
|
||||
data: (props.provider.historyPrice|| []).map(p => dayjs(p.createdAt).format('YYYY-MM-DD'))
|
||||
},
|
||||
yAxis: {
|
||||
name: '价格'
|
||||
},
|
||||
series: [
|
||||
{
|
||||
type: 'line',
|
||||
name: '原价',
|
||||
data: (props.provider.historyPrice|| []).map(p => p.originalPrice)
|
||||
},
|
||||
{
|
||||
type: 'line',
|
||||
name: '到手价',
|
||||
data: (props.provider.historyPrice|| []).map(p => p.finalPrice)
|
||||
}
|
||||
]
|
||||
}
|
||||
})
|
||||
|
||||
const fetchLoading = ref(false)
|
||||
// 拉取单个商品价格
|
||||
const fetchArticle = (pArticle) => {
|
||||
fetchLoading.value = true
|
||||
FetchProviderArticle(pArticle).then(res=>{
|
||||
if(res.code !== 200){
|
||||
message.error(`拉取失败:${res.msg}`)
|
||||
}else {
|
||||
message.success('拉取成功')
|
||||
}
|
||||
}).catch(err => {
|
||||
message.error('拉取失败')
|
||||
console.log(err)
|
||||
}).finally(()=>{
|
||||
fetchLoading.value = false
|
||||
})
|
||||
}
|
||||
</script>
|
||||
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
197
src/componse/seller-article/seller-article-panel.vue
Normal file
197
src/componse/seller-article/seller-article-panel.vue
Normal file
@ -0,0 +1,197 @@
|
||||
<template>
|
||||
<a-collapse-panel :key="`${seller.sellerId}`" >
|
||||
<template #extra>
|
||||
<div class="flex space-x-4">
|
||||
<div>更新时间:{{dayjs(seller.updatedAt).format('YYYY-MM-DD HH:mm:ss')}}</div>
|
||||
<div v-if="seller.skuID">抓取:<a-switch :checked="!seller.exclude" @click="onClickExclude(seller)"/></div>
|
||||
</div>
|
||||
</template>
|
||||
<template #header>
|
||||
<span>{{header}}</span>
|
||||
</template>
|
||||
<div class="flex flex-col space-y-4">
|
||||
<div class="flex space-x-4 justify-between">
|
||||
<div class="w-50">货号:{{seller.pid || '-'}}</div>
|
||||
<div class="w-50">SpuID: {{seller.spuID || '-'}} </div>
|
||||
<div class="w-100 flex items-center space-x-4">
|
||||
<div>SkuID: {{seller.skuID || '-'}} </div>
|
||||
<div class="text-red-4 flex items-center space-x-4" v-if="!seller.skuID">
|
||||
<div>(未匹配到销售商sku)</div>
|
||||
<a-button @click="matchSkuID(seller)" :disabled="loading">重新匹配</a-button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex space-x-4 justify-between">
|
||||
<div class="w-50">当前售价:¥ {{seller.sell.originalPrice || '-'}}</div>
|
||||
<a-popover v-if="seller.sell.calMark" trigger="hover">
|
||||
<template #content>
|
||||
<span class="whitespace-pre">
|
||||
{{seller.sell.calMark}}
|
||||
</span>
|
||||
</template>
|
||||
<div class="w-50">当前到手价:¥ {{seller.sell.finalPrice || '-'}}</div>
|
||||
</a-popover>
|
||||
<div class="w-50" v-else>当前到手价:¥ {{seller.sell.finalPrice || '-'}}</div>
|
||||
<a-button v-if="seller.skuID" type="primary" @click="fetchArticle(seller)" :loading="fetchLoading">立即拉取</a-button>
|
||||
</div>
|
||||
<v-chart class="h-400px w-full" :option="options" />
|
||||
</div>
|
||||
</a-collapse-panel>
|
||||
</template>
|
||||
<script setup>
|
||||
import dayjs from "dayjs";
|
||||
import {GetSellerHistory, UpdateSellerArticle} from "@/api/articles.js";
|
||||
import {message} from "ant-design-vue";
|
||||
import {computed, ref, watch} from "vue";
|
||||
|
||||
const props = defineProps({
|
||||
seller: {
|
||||
type: Object,
|
||||
default: () => {
|
||||
return {
|
||||
sellerId: '',
|
||||
updatedAt: 0,
|
||||
spuID: '',
|
||||
skuID:'',
|
||||
pid: '',
|
||||
sell: {
|
||||
originalPrice: 0,
|
||||
finalPrice: 0,
|
||||
calMark: ''
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
sellerDict:{
|
||||
type: Array,
|
||||
default: () => {
|
||||
return []
|
||||
}
|
||||
},
|
||||
open: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
const header = computed(()=>{
|
||||
return props.sellerDict.find(s => s.sellerId === props.seller.sellerId)?.name
|
||||
})
|
||||
|
||||
const onClickExclude = (sellerArticle)=> {
|
||||
sellerArticle.exclude = !sellerArticle.exclude
|
||||
UpdateSellerArticle(sellerArticle).then(res=>{
|
||||
if(res.code !== 200){
|
||||
message.error(`更新失败:${res.msg}`)
|
||||
}else {
|
||||
message.success('更新成功')
|
||||
}
|
||||
}).catch(err => {
|
||||
message.error('更新失败')
|
||||
console.log(err)
|
||||
})
|
||||
}
|
||||
|
||||
watch(()=>props.open, (newVal)=> {
|
||||
if(newVal && !(props.seller.historyPrice?.length > 0)){
|
||||
GetSellerHistory(props.seller.id).then(res=>{
|
||||
if(res.code !== 200){
|
||||
message.error(`获取历史数据失败:${res.msg}`)
|
||||
}else {
|
||||
props.seller.historyPrice = res.data
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
// 图表
|
||||
|
||||
import { use } from 'echarts/core'
|
||||
import { LineChart } from 'echarts/charts'
|
||||
import { GridComponent, TitleComponent, TooltipComponent, LegendComponent } from 'echarts/components'
|
||||
import { CanvasRenderer } from 'echarts/renderers'
|
||||
import VChart from "vue-echarts";
|
||||
import {MatchSellerSku} from "@/api/seller.js";
|
||||
import {FetchSellerArticle} from "@/api/seller.js";
|
||||
|
||||
use([GridComponent, LineChart, CanvasRenderer, TitleComponent , TooltipComponent, LegendComponent ])
|
||||
|
||||
const options = computed(()=> {
|
||||
return {
|
||||
title: {
|
||||
text: "历史价格",
|
||||
left: "center"
|
||||
},
|
||||
tooltip: {
|
||||
trigger: 'item',
|
||||
formatter: '{a} <br/>{b} : {c}'
|
||||
},
|
||||
legend: {
|
||||
left: 'left'
|
||||
},
|
||||
xAxis: {
|
||||
type: 'category',
|
||||
name: '日期',
|
||||
data: (props.seller.historyPrice|| []).map(p => dayjs(p.createdAt).format('YYYY-MM-DD'))
|
||||
},
|
||||
yAxis: {
|
||||
name: '价格'
|
||||
},
|
||||
series: [
|
||||
{
|
||||
type: 'line',
|
||||
name: '原售价',
|
||||
data: (props.seller.historyPrice|| []).map(p => p.originalPrice)
|
||||
},
|
||||
{
|
||||
type: 'line',
|
||||
name: '到手价',
|
||||
data: (props.seller.historyPrice|| []).map(p => p.finalPrice)
|
||||
}
|
||||
]
|
||||
}
|
||||
})
|
||||
|
||||
const loading = ref(false)
|
||||
|
||||
// 重新匹配下sku
|
||||
const matchSkuID = (sellerArticle)=>{
|
||||
loading.value = true
|
||||
MatchSellerSku(sellerArticle).then(res=>{
|
||||
if(res.code !== 200){
|
||||
message.error(`匹配失败:${res.msg}`)
|
||||
}else {
|
||||
message.success('匹配成功')
|
||||
}
|
||||
}).catch(err => {
|
||||
message.error('匹配失败')
|
||||
console.log(err)
|
||||
}).finally(()=>{
|
||||
loading.value = false
|
||||
})
|
||||
}
|
||||
|
||||
const fetchLoading = ref(false)
|
||||
// 拉取单个商品价格
|
||||
const fetchArticle = (sArticle) => {
|
||||
fetchLoading.value = true
|
||||
FetchSellerArticle(sArticle).then(res=>{
|
||||
if(res.code !== 200){
|
||||
message.error(`拉取失败:${res.msg}`)
|
||||
}else {
|
||||
message.success('拉取成功')
|
||||
}
|
||||
}).catch(err => {
|
||||
message.error('拉取失败')
|
||||
console.log(err)
|
||||
}).finally(()=>{
|
||||
fetchLoading.value = false
|
||||
})
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
@ -1,50 +1,15 @@
|
||||
import {createRouter, createWebHashHistory} from "vue-router";
|
||||
import { routes } from 'vue-router/auto-routes';
|
||||
|
||||
const routes = [
|
||||
{
|
||||
path: '/',
|
||||
redirect: '/watcher'
|
||||
},
|
||||
{
|
||||
path: '/watcher',
|
||||
name: 'watcher',
|
||||
component: ()=>import('@/views/Watcher/index.vue')
|
||||
},
|
||||
{
|
||||
path: '/us-coach',
|
||||
name: 'us-coach',
|
||||
component: ()=>import('@/views/Product/USCoachOutlet.vue')
|
||||
},
|
||||
{
|
||||
path: '/cn-coach',
|
||||
name: 'cn-coach',
|
||||
component: ()=>import('@/views/Product/CNCoachOutlet.vue')
|
||||
},
|
||||
{
|
||||
path: '/cn-coach-outlet',
|
||||
name: 'cn-coach-outlet',
|
||||
component: ()=>import('@/views/Product/CNCoachOutlet.vue')
|
||||
},
|
||||
{
|
||||
path: '/pusher',
|
||||
name: 'pusher',
|
||||
component: ()=>import('@/views/Pusher/index.vue')
|
||||
},
|
||||
{
|
||||
path: '/provider',
|
||||
name: 'provider',
|
||||
component: ()=>import('@/views/Provider/index.vue')
|
||||
},
|
||||
{
|
||||
path: '/seller',
|
||||
name: 'seller',
|
||||
component: ()=>import('@/views/Seller/index.vue')
|
||||
}
|
||||
]
|
||||
|
||||
const router = createRouter({
|
||||
history: createWebHashHistory(),
|
||||
routes
|
||||
routes:[
|
||||
...routes,
|
||||
{ path: '/',
|
||||
redirect: '/watcher'
|
||||
},
|
||||
]
|
||||
})
|
||||
|
||||
export default router
|
180
src/views/article/detail.vue
Normal file
180
src/views/article/detail.vue
Normal file
@ -0,0 +1,180 @@
|
||||
<template>
|
||||
<div class="overflow-overlay h-full m-4 bg-white rounded-2 shadow-lg p-8 flex flex-col justify-between space-y-4">
|
||||
<div class="border-solid border-2 border-gray-100 rounded-lg p-4 flex space-x-8">
|
||||
<a-image :src="data.image" :width="200"></a-image>
|
||||
<div class="flex justify-between w-full pr-16">
|
||||
<div class="p-4 flex flex-col justify-between">
|
||||
<div class="text-xl font-bold">名称:{{data.name}}</div>
|
||||
<div class="text-xl font-bold">品牌:{{data.brand}}</div>
|
||||
<div class="text-xl font-bold">货号:{{data.pid}}</div>
|
||||
</div>
|
||||
<div class="p-4 flex flex-col space-y-2 justify-between">
|
||||
<div class="flex space-x-12">
|
||||
<div class="text-xl font-bold">成本价:{{data.costPrice}}</div>
|
||||
<div class="text-xl font-bold">售价:{{data.sellPrice}}</div>
|
||||
</div>
|
||||
<div class="text-xl font-bold">收益率:{{data.rate}}%</div>
|
||||
<div class="flex space-x-4 items-center">
|
||||
<span class="text-xl font-bold mr-4 whitespace-nowrap">备注:</span>
|
||||
<span v-if="!remarkEdit">{{data.remark || '-'}}</span>
|
||||
<EditOutlined v-if="!remarkEdit" class="cursor-pointer" @click="remarkEdit=true" />
|
||||
<a-input v-else v-model:value="data.remark"></a-input>
|
||||
<SaveOutlined v-show="remarkEdit" class="cursor-pointer" @click="onSaveRemark"/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="p-4 flex flex-col space-y-2 justify-between">
|
||||
<div class="text-xl font-bold">更新时间:{{dayjs(data.updatedAt).format('YYYY-MM-DD HH:mm:ss')}}</div>
|
||||
<div class="text-xl font-bold">可购买:
|
||||
<CheckOutlined v-if="data.available" class="text-green-600"/>
|
||||
<CloseOutlined v-else class="text-red-7"/>
|
||||
</div>
|
||||
<div class="text-xl font-bold">
|
||||
抓取:<a-switch :checked="!data.exclude" @click="onClickExclude(data)"/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="h-full overflow-overlay">
|
||||
<a-card
|
||||
class="h-full flex flex-col w-full"
|
||||
:bodyStyle="{height: '100%', flex: '1 1 0%', overflow: 'overlay'}"
|
||||
:tab-list="[{key:'providers',tab:'供应商'},{key:'sellers',tab:'销售商'}]"
|
||||
v-model:active-tab-key="activeTabKey"
|
||||
@tab-change="onTabChange"
|
||||
>
|
||||
<div>
|
||||
<a-collapse v-model:activeKey="openTabKey" >
|
||||
<seller-article-panel v-if="activeTabKey === 'sellers'" v-for="item in data.sellers || []" :key="`${item.sellerId}`"
|
||||
:seller="item" :seller-dict="dict.sellers" :open="openTabKey.indexOf(item.sellerId) > -1">
|
||||
|
||||
</seller-article-panel>
|
||||
<provider-article-panel v-else-if="activeTabKey === 'providers'" v-for="item in data.providers || []" :key="`${item.providerId}`"
|
||||
:provider="item" :provider-dict="dict.providers" :open="openTabKey.indexOf(item.providerId) > -1">
|
||||
|
||||
</provider-article-panel>
|
||||
</a-collapse>
|
||||
</div>
|
||||
</a-card>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</template>
|
||||
<script setup>
|
||||
|
||||
import {onMounted, reactive, ref} from "vue";
|
||||
import {useRoute} from "vue-router";
|
||||
import {GetArticle, GetBrandsDict, UpdateArticle} from "@/api/articles.js";
|
||||
import {message} from "ant-design-vue";
|
||||
import dayjs from "dayjs";
|
||||
import {CheckOutlined, CloseOutlined, EditOutlined, SaveOutlined} from "@ant-design/icons-vue";
|
||||
import {FindSellers} from "@/api/seller.js";
|
||||
import {FindProviders} from "@/api/provider.js";
|
||||
import SellerArticlePanel from "@/componse/seller-article/seller-article-panel.vue";
|
||||
import ProviderArticlePanel from "@/componse/provider-article/provider-article-panel.vue";
|
||||
|
||||
const loading = ref(false)
|
||||
const data = ref({
|
||||
image:'',
|
||||
name:'',
|
||||
providers: [],
|
||||
sellers: [],
|
||||
remark:''
|
||||
})
|
||||
|
||||
onMounted(()=>{
|
||||
load()
|
||||
loadDict()
|
||||
})
|
||||
const route = useRoute()
|
||||
|
||||
const load = ()=>{
|
||||
loading.value = true
|
||||
GetArticle(route.query.id).then(res=>{
|
||||
if(res.code === 200){
|
||||
data.value = res.data
|
||||
}else{
|
||||
message.error(res.msg)
|
||||
}
|
||||
}).catch((error)=>{
|
||||
message.error('获取详情失败')
|
||||
console.log(error)
|
||||
}).finally(()=>{
|
||||
loading.value = false
|
||||
})
|
||||
}
|
||||
|
||||
const onClickExclude=(record)=>{
|
||||
record.exclude = !record.exclude
|
||||
UpdateArticle(record).then(res=>{
|
||||
if (res.code !== 200) {
|
||||
message.error(`${record.exclude ? '关闭':'开启'}失败`)
|
||||
}else {
|
||||
message.success(`${record.exclude ? '关闭':'开启'}成功`)
|
||||
}
|
||||
}).catch(err=>{
|
||||
message.error(`${record.exclude ? '关闭':'开启'}失败`)
|
||||
console.log(err)
|
||||
}).finally(()=>{
|
||||
load()
|
||||
})
|
||||
}
|
||||
|
||||
// 所有字典信息
|
||||
const dict = reactive({
|
||||
brands: [],
|
||||
providers: [],
|
||||
sellers: []
|
||||
})
|
||||
|
||||
const loadDict = ()=>{
|
||||
Promise.all([FindSellers({}), FindProviders({}), GetBrandsDict()]).then(([sellers, providers, brands])=>{
|
||||
if (sellers.code === 200 && providers.code === 200 && brands.code === 200) {
|
||||
dict.sellers = sellers.data
|
||||
dict.providers = providers.data
|
||||
dict.brands = brands.data
|
||||
}else if(sellers.code !== 200){
|
||||
message.error('获取销售商列表失败')
|
||||
}else if(providers.code !== 200){
|
||||
message.error('获取供应商列表失败')
|
||||
}else if(brands.code !== 200){
|
||||
message.error('获取品牌列表失败')
|
||||
}
|
||||
}).catch(err => {
|
||||
message.error('获取字典信息失败')
|
||||
console.log(err)
|
||||
})
|
||||
}
|
||||
|
||||
const activeTabKey = ref('providers')
|
||||
|
||||
const onTabChange = (key)=>{
|
||||
activeTabKey.value = key
|
||||
}
|
||||
|
||||
const openTabKey = ref([])
|
||||
|
||||
|
||||
// 编辑备注
|
||||
|
||||
const remarkEdit = ref(false)
|
||||
|
||||
const onSaveRemark = ()=>{
|
||||
UpdateArticle(data.value).then(res=>{
|
||||
if (res.code !== 200) {
|
||||
message.error('保存失败')
|
||||
}else {
|
||||
message.success('保存成功')
|
||||
}
|
||||
}).catch(err=>{
|
||||
message.error('保存失败')
|
||||
console.log(err)
|
||||
}).finally(()=>{
|
||||
remarkEdit.value = false
|
||||
})
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
</style>
|
205
src/views/article/index.vue
Normal file
205
src/views/article/index.vue
Normal file
@ -0,0 +1,205 @@
|
||||
<template>
|
||||
<div class="h-full m-4 bg-white rounded-2 shadow-lg p-8 flex flex-col justify-between space-y-4">
|
||||
<div class="flex justify-between">
|
||||
<div></div>
|
||||
<div class="flex space-x-4">
|
||||
<a-input placeholder="请输入关键词" v-model:value="query.keyword"></a-input>
|
||||
<a-button type="primary" :disabled="loading" @click="list">搜索</a-button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="h-full border-0 border-t-1 border-solid border-gray-300 pt-4">
|
||||
<a-spin :spinning="loading" :indicator="indicator">
|
||||
<a-table :dataSource="data.list" :columns="columns" :pagination="false" :scroll="{y:1000}" @change="tableChange" rowKey="id">
|
||||
<template #bodyCell="{ column, record }">
|
||||
<template v-if="column.key === 'image'">
|
||||
<a-image :width="75" :src="record.image"/>
|
||||
</template>
|
||||
<template v-else-if="column.key === 'name'">
|
||||
<span class="cursor-pointer text-blue" title="查看详情" @click="toDetail(record.id)">{{record.name}}</span>
|
||||
</template>
|
||||
<template v-else-if="column.key === 'pid'">
|
||||
<span class="cursor-pointer text-blue" title="查看详情" @click="toDetail(record.id)">{{record.pid}}</span>
|
||||
</template>
|
||||
<template v-else-if="column.key === 'rate'">
|
||||
<span>{{record.rate > 0 ? `${record.rate}%`:'--' }}</span>
|
||||
</template>
|
||||
<template v-else-if="column.key === 'available'">
|
||||
<span :class="[record.available ? 'text-green-700':'text-red-500']">{{record.available ? '是':'否'}}</span>
|
||||
</template>
|
||||
<template v-else-if="column.key === 'exclude'">
|
||||
<a-switch :checked="!record.exclude" @click="onClickExclude(record)"/>
|
||||
</template>
|
||||
<template v-else-if="column.key === 'remark'">
|
||||
{{record.remark===''?'-':record.remark}}
|
||||
</template>
|
||||
</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>
|
||||
</template>
|
||||
<script setup>
|
||||
|
||||
import {computed, h, onMounted, reactive, ref, watch} from "vue";
|
||||
import {LoadingOutlined} from "@ant-design/icons-vue";
|
||||
import {message} from "ant-design-vue";
|
||||
import {ListArticles, UpdateArticle} from "@/api/articles.js";
|
||||
import {useRouter} from "vue-router";
|
||||
|
||||
const query = reactive({
|
||||
keyword: "",
|
||||
page: 1,
|
||||
size: 8,
|
||||
pid:'',
|
||||
brand: '',
|
||||
available: null,
|
||||
rate_sort:null,
|
||||
})
|
||||
|
||||
watch(()=>query.available, ()=>{
|
||||
console.log(query)
|
||||
})
|
||||
|
||||
onMounted(()=>{
|
||||
list()
|
||||
})
|
||||
|
||||
const loading = ref(false)
|
||||
// 展示数据
|
||||
const data = reactive({
|
||||
list: [],
|
||||
total: 0
|
||||
})
|
||||
|
||||
const list = () => {
|
||||
loading.value = true
|
||||
ListArticles(query).then(res => {
|
||||
if (res.code !== 200) {
|
||||
message.error(res.msg)
|
||||
}else {
|
||||
data.list = res.data.list
|
||||
data.total = res.data.total
|
||||
}
|
||||
}).finally(()=>{
|
||||
loading.value = false
|
||||
})
|
||||
}
|
||||
|
||||
const columns = computed(()=>[
|
||||
{
|
||||
title: '货号',
|
||||
dataIndex: 'pid',
|
||||
key: 'pid',
|
||||
width: 200
|
||||
},
|
||||
{
|
||||
title: '名称',
|
||||
dataIndex: 'name',
|
||||
ellipsis: true,
|
||||
key: 'name',
|
||||
width: 350
|
||||
},
|
||||
{
|
||||
title: '图片',
|
||||
key: 'image',
|
||||
width: 250
|
||||
},
|
||||
{
|
||||
title: '最低成本价',
|
||||
dataIndex: 'costPrice',
|
||||
key: 'costPrice',
|
||||
width: 150
|
||||
},
|
||||
{
|
||||
title: '最低售价',
|
||||
dataIndex: 'sellPrice',
|
||||
key: 'sellPrice',
|
||||
width: 150
|
||||
},
|
||||
{
|
||||
title: '利润率',
|
||||
key: 'rate',
|
||||
dataIndex: 'rate',
|
||||
width: 150,
|
||||
sorter:true,
|
||||
sortOrder: query.rate_sort
|
||||
},
|
||||
{
|
||||
title: '可购买',
|
||||
key: 'available',
|
||||
dataIndex: 'available',
|
||||
width: 150,
|
||||
filteredValue: query.available,
|
||||
filterMultiple:false,
|
||||
filters:[
|
||||
{
|
||||
text: '可购买',
|
||||
value: true,
|
||||
},
|
||||
{
|
||||
text: '不可购买',
|
||||
value: false,
|
||||
},
|
||||
]
|
||||
},
|
||||
{
|
||||
title: '需要更新',
|
||||
key: 'exclude',
|
||||
width: 150
|
||||
},
|
||||
{
|
||||
title: '备注',
|
||||
key: 'remark',
|
||||
dataIndex: 'remark',
|
||||
ellipsis: true,
|
||||
}
|
||||
])
|
||||
|
||||
|
||||
|
||||
const indicator = h(LoadingOutlined, {
|
||||
style: {
|
||||
fontSize: '32px',
|
||||
},
|
||||
spin: true,
|
||||
});
|
||||
|
||||
const onClickExclude=(record)=>{
|
||||
record.exclude = !record.exclude
|
||||
UpdateArticle(record).then(res=>{
|
||||
if (res.code !== 200) {
|
||||
message.error(`${record.exclude ? '关闭':'开启'}失败`)
|
||||
}else {
|
||||
message.success(`${record.exclude ? '关闭':'开启'}成功`)
|
||||
}
|
||||
}).catch(err=>{
|
||||
message.error(`${record.exclude ? '关闭':'开启'}失败`)
|
||||
console.log(err)
|
||||
})
|
||||
}
|
||||
|
||||
const router = useRouter()
|
||||
// 跳到详情页面
|
||||
const toDetail=(id)=>{
|
||||
router.push({
|
||||
path: '/article/detail',
|
||||
query: {
|
||||
id: id
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
//
|
||||
const tableChange = (pagination, filters, sorter, { action, currentDataSource })=>{
|
||||
if(sorter.columnKey === 'rate') {
|
||||
query.rate_sort = sorter.order
|
||||
}
|
||||
query.available = filters.available
|
||||
list()
|
||||
}
|
||||
|
||||
</script>
|
||||
<style scoped>
|
||||
|
||||
</style>
|
@ -16,7 +16,7 @@ const router = useRouter()
|
||||
|
||||
const onclick = ({key}) => {
|
||||
router.push({
|
||||
name: key
|
||||
path: '/'+key
|
||||
})
|
||||
}
|
||||
|
||||
@ -28,28 +28,11 @@ const items = [
|
||||
title: '蹲货',
|
||||
},
|
||||
{
|
||||
key: 'product',
|
||||
key: 'article',
|
||||
icon: () => h(DollarCircleOutlined),
|
||||
label: '商品',
|
||||
title: '商品',
|
||||
onTitleClick: onclick,
|
||||
children: [
|
||||
{
|
||||
key: 'us-coach',
|
||||
label: '美国coach outlet',
|
||||
title: '美国coach outlet',
|
||||
},
|
||||
{
|
||||
key: 'cn-coach-outlet',
|
||||
label: '中国coach outlet',
|
||||
title: '中国coach outlet',
|
||||
},
|
||||
{
|
||||
key: 'cn-coach',
|
||||
label: '中国coach',
|
||||
title: '中国coach',
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
key: 'pusher',
|
||||
@ -66,8 +49,8 @@ const items = [
|
||||
{
|
||||
key: 'seller',
|
||||
icon: () => h(DeploymentUnitOutlined),
|
||||
label: '出货商',
|
||||
title: '出货商',
|
||||
label: '销售商',
|
||||
title: '销售商',
|
||||
}
|
||||
]
|
||||
|
||||
|
@ -1,7 +1,7 @@
|
||||
<template>
|
||||
<div class="h-full w-full flex flex-col">
|
||||
<Header></Header>
|
||||
<div class="h-full w-full flex">
|
||||
<div class="h-full w-full flex overflow-overlay">
|
||||
<Aside></Aside>
|
||||
<Main></Main>
|
||||
</div>
|
||||
|
@ -16,9 +16,16 @@
|
||||
</template>
|
||||
<template v-else-if="column.dataIndex === 'name'">
|
||||
</template>
|
||||
<template v-else-if="column.key === 'ticker'">
|
||||
<span>{{record.config.ticker}}</span>
|
||||
</template>
|
||||
<template v-else-if="column.key === 'status'">
|
||||
<a-tag :color="getStatusDict(record.status).color">{{getStatusDict(record.status).title}}</a-tag>
|
||||
</template>
|
||||
<template v-else-if="column.key === 'opt'">
|
||||
<a-button type="link" @click="onClickUpdate(record.id)">编辑</a-button>
|
||||
<a-button type="link" danger @click="onClickDelete(record.id)"> 删除</a-button>
|
||||
<a-button type="link" :disabled="['provider_status_normal', 'provider_status_error'].indexOf(getStatusDict(record.status).key) < 0" @click="onClickPull(record)">拉取</a-button>
|
||||
<a-button type="link" :disabled="['provider_status_normal', 'provider_status_error'].indexOf(getStatusDict(record.status).key) < 0" @click="onClickUpdate(record.id)">编辑</a-button>
|
||||
<a-button type="link" :disabled="['provider_status_normal', 'provider_status_error'].indexOf(getStatusDict(record.status).key) < 0" danger @click="onClickDelete(record.id)"> 删除</a-button>
|
||||
</template>
|
||||
</template>
|
||||
</a-table>
|
||||
@ -76,10 +83,18 @@
|
||||
|
||||
<script setup>
|
||||
|
||||
import {h, onMounted, reactive, ref} from "vue";
|
||||
import {CreateProvider, DeleteProvider, GetProvider, ListProviders, UpdateProvider} from "@/api/provider.js";
|
||||
import {message} from "ant-design-vue";
|
||||
import {LoadingOutlined} from "@ant-design/icons-vue";
|
||||
import {createVNode, h, onMounted, reactive, ref} from "vue";
|
||||
import {
|
||||
CreateProvider,
|
||||
DeleteProvider,
|
||||
GetProvider,
|
||||
ListProviders,
|
||||
FetchProviderArticles,
|
||||
UpdateProvider,
|
||||
GetProviderDictStatus
|
||||
} from "@/api/provider.js";
|
||||
import {message, Modal} from "ant-design-vue";
|
||||
import {ExclamationCircleOutlined, LoadingOutlined} from "@ant-design/icons-vue";
|
||||
|
||||
|
||||
const query = reactive({
|
||||
@ -90,6 +105,11 @@ const query = reactive({
|
||||
id: '',
|
||||
})
|
||||
|
||||
// 状态的列表
|
||||
const configs = reactive({
|
||||
status: []
|
||||
})
|
||||
|
||||
const loading = ref(false)
|
||||
// 展示数据
|
||||
const data = reactive({
|
||||
@ -111,10 +131,29 @@ const list = () => {
|
||||
})
|
||||
}
|
||||
|
||||
const loadStatus = () => {
|
||||
GetProviderDictStatus().then(res=>{
|
||||
if (res.code !== 200) {
|
||||
message.error(res.msg)
|
||||
}else {
|
||||
configs.status = res.data
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
const getStatusDict = (value)=>{
|
||||
return configs.status.find(s => s.value === value) || {
|
||||
title: '未知',
|
||||
color: ''
|
||||
}
|
||||
}
|
||||
|
||||
onMounted(()=>{
|
||||
list()
|
||||
loadStatus()
|
||||
})
|
||||
|
||||
//**********添加对话框**********//
|
||||
const addModal = reactive({
|
||||
visible: false,
|
||||
data: {
|
||||
@ -197,6 +236,17 @@ const columns = [
|
||||
key: 'name',
|
||||
width: 250
|
||||
},
|
||||
{
|
||||
title: '抓取时间',
|
||||
key: 'ticker',
|
||||
width: 250
|
||||
},
|
||||
{
|
||||
title: '状态',
|
||||
dataIndex: 'status',
|
||||
key: 'status',
|
||||
width: 250
|
||||
},
|
||||
{
|
||||
title: '备注',
|
||||
dataIndex: 'remark',
|
||||
@ -241,6 +291,29 @@ const onClickDelete=(id)=>{
|
||||
list()
|
||||
})
|
||||
}
|
||||
|
||||
const onClickPull = (record)=>{
|
||||
Modal.confirm({
|
||||
title: '提示',
|
||||
icon: createVNode(ExclamationCircleOutlined),
|
||||
content: `确认拉取${record.name}商品价格?`,
|
||||
onOk() {
|
||||
FetchProviderArticles(record.providerId).then(res=>{
|
||||
if (res.code !== 200) {
|
||||
message.error(res.msg)
|
||||
}else {
|
||||
message.success("开始抓取")
|
||||
}
|
||||
}).catch(err =>{
|
||||
console.log(err)
|
||||
message.error("抓取失败")
|
||||
}).finally(()=>{
|
||||
list()
|
||||
})
|
||||
},
|
||||
centered: true
|
||||
});
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
@ -16,9 +16,16 @@
|
||||
</template>
|
||||
<template v-else-if="column.dataIndex === 'name'">
|
||||
</template>
|
||||
<template v-else-if="column.key === 'status'">
|
||||
<a-tag :color="getStatusDict(record.status).color">{{getStatusDict(record.status).title}}</a-tag>
|
||||
</template>
|
||||
<template v-else-if="column.key === 'ticker'">
|
||||
<span>{{record.config.ticker}}</span>
|
||||
</template>
|
||||
<template v-else-if="column.key === 'opt'">
|
||||
<a-button type="link" @click="onClickUpdate(record.id)">编辑</a-button>
|
||||
<a-button type="link" danger @click="onClickDelete(record.id)"> 删除</a-button>
|
||||
<a-button type="link" :disabled="['seller_status_normal', 'seller_status_error'].indexOf(getStatusDict(record.status).key) < 0" @click="onClickPull(record)">拉取</a-button>
|
||||
<a-button type="link" :disabled="['seller_status_normal', 'seller_status_error'].indexOf(getStatusDict(record.status).key) < 0" @click="onClickUpdate(record.id)">编辑</a-button>
|
||||
<a-button type="link" :disabled="['seller_status_normal', 'seller_status_error'].indexOf(getStatusDict(record.status).key) < 0" danger @click="onClickDelete(record.id)"> 删除</a-button>
|
||||
</template>
|
||||
</template>
|
||||
</a-table>
|
||||
@ -26,7 +33,7 @@
|
||||
</div>
|
||||
<a-pagination :disabled="loading" class="text-right" v-model:current="query.page" :total="data.total" show-less-items @change="list"/>
|
||||
</div>
|
||||
<a-modal v-model:open="addModal.visible" :title="`${addModal.edit ? '编辑' : '添加'}出货商`" @ok="handleOk" centered width="80%">
|
||||
<a-modal v-model:open="addModal.visible" :title="`${addModal.edit ? '编辑' : '添加'}销售商`" @ok="handleOk" centered width="80%">
|
||||
<a-spin :spinning="addModal.loading" :indicator="indicator">
|
||||
<a-form
|
||||
ref="formRef"
|
||||
@ -37,10 +44,10 @@
|
||||
@finish="cleanAddModal"
|
||||
@finishFailed="cleanAddModal"
|
||||
>
|
||||
<a-form-item label="标识" name="sellerId" :rules="[{ required: true, message: '请填写出货商标识' }]">
|
||||
<a-form-item label="标识" name="sellerId" :rules="[{ required: true, message: '请填写销售商标识' }]">
|
||||
<a-input v-model:value="addModal.data.sellerId" placeholder="请输入标识,比如:dewu" :disabled="addModal.edit"/>
|
||||
</a-form-item>
|
||||
<a-form-item label="名称" name="name" :rules="[{ required: true, message: '请填写出货商名称' }]">
|
||||
<a-form-item label="名称" name="name" :rules="[{ required: true, message: '请填写销售商名称' }]">
|
||||
<a-input v-model:value="addModal.data.name" placeholder="请输入名称,比如:得物"/>
|
||||
</a-form-item>
|
||||
<a-form-item label="抓取定时" name="config.ticker">
|
||||
@ -70,10 +77,18 @@
|
||||
|
||||
<script setup>
|
||||
|
||||
import {h, onMounted, reactive, ref} from "vue";
|
||||
import {CreateSeller, DeleteSeller, GetSeller, ListSellers, UpdateSeller} from "@/api/seller.js";
|
||||
import {message} from "ant-design-vue";
|
||||
import {LoadingOutlined} from "@ant-design/icons-vue";
|
||||
import {createVNode, h, onMounted, reactive, ref} from "vue";
|
||||
import {
|
||||
CreateSeller,
|
||||
DeleteSeller,
|
||||
GetSeller,
|
||||
ListSellers,
|
||||
UpdateSeller,
|
||||
FetchSellerArticles,
|
||||
GetSellerDictStatus
|
||||
} from "@/api/seller.js";
|
||||
import {message, Modal} from "ant-design-vue";
|
||||
import {ExclamationCircleOutlined, LoadingOutlined} from "@ant-design/icons-vue";
|
||||
|
||||
|
||||
const query = reactive({
|
||||
@ -84,6 +99,11 @@ const query = reactive({
|
||||
id: '',
|
||||
})
|
||||
|
||||
// 状态的列表
|
||||
const configs = reactive({
|
||||
status: []
|
||||
})
|
||||
|
||||
const loading = ref(false)
|
||||
// 展示数据
|
||||
const data = reactive({
|
||||
@ -105,8 +125,26 @@ const list = () => {
|
||||
})
|
||||
}
|
||||
|
||||
const loadStatus = () => {
|
||||
GetSellerDictStatus().then(res=>{
|
||||
if (res.code !== 200) {
|
||||
message.error(res.msg)
|
||||
}else {
|
||||
configs.status = res.data
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
const getStatusDict = (value)=>{
|
||||
return configs.status.find(s => s.value === value) || {
|
||||
title: '未知',
|
||||
color: ''
|
||||
}
|
||||
}
|
||||
|
||||
onMounted(()=>{
|
||||
list()
|
||||
loadStatus()
|
||||
})
|
||||
|
||||
const addModal = reactive({
|
||||
@ -187,6 +225,17 @@ const columns = [
|
||||
key: 'name',
|
||||
width: 250
|
||||
},
|
||||
{
|
||||
title: '抓取时间',
|
||||
key: 'ticker',
|
||||
width: 250
|
||||
},
|
||||
{
|
||||
title: '状态',
|
||||
dataIndex: 'status',
|
||||
key: 'status',
|
||||
width: 250
|
||||
},
|
||||
{
|
||||
title: '备注',
|
||||
dataIndex: 'remark',
|
||||
@ -231,6 +280,29 @@ const onClickDelete=(id)=>{
|
||||
list()
|
||||
})
|
||||
}
|
||||
|
||||
const onClickPull = (record)=>{
|
||||
Modal.confirm({
|
||||
title: '提示',
|
||||
icon: createVNode(ExclamationCircleOutlined),
|
||||
content: `确认拉取${record.name}商品价格?`,
|
||||
onOk() {
|
||||
FetchSellerArticles(record.sellerId).then(res=>{
|
||||
if (res.code !== 200) {
|
||||
message.error(res.msg)
|
||||
}else {
|
||||
message.success("开始抓取")
|
||||
}
|
||||
}).catch(err =>{
|
||||
console.log(err)
|
||||
message.error("抓取失败")
|
||||
}).finally(()=>{
|
||||
list()
|
||||
})
|
||||
},
|
||||
centered: true
|
||||
});
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
@ -4,12 +4,17 @@ import UnoCSS from 'unocss/vite'
|
||||
import path from 'path'
|
||||
import Components from 'unplugin-vue-components/vite';
|
||||
import { AntDesignVueResolver } from 'unplugin-vue-components/resolvers';
|
||||
import VueRouter from 'unplugin-vue-router/vite';
|
||||
|
||||
|
||||
|
||||
// https://vitejs.dev/config/
|
||||
export default defineConfig({
|
||||
plugins: [
|
||||
vue(),
|
||||
VueRouter({
|
||||
routesFolder: 'src/views'
|
||||
}),
|
||||
UnoCSS(),
|
||||
Components({
|
||||
resolvers: [
|
||||
@ -22,41 +27,6 @@ export default defineConfig({
|
||||
server:{
|
||||
open: true,
|
||||
proxy: {
|
||||
// '/storage/v1/products': {
|
||||
// // target: 'https://ht.timerzz.com:20443/',
|
||||
// target: 'http://192.168.31.163:31828/',
|
||||
// changeOrigin: true,
|
||||
// secure: false,
|
||||
// ws: true,
|
||||
// },
|
||||
// '/storage/v1/push': {
|
||||
// target: 'https://ht-dev.timerzz.com:20443/',
|
||||
// // target: 'http://192.168.31.55:2280/',
|
||||
// changeOrigin: true,
|
||||
// secure: false,
|
||||
// ws: true,
|
||||
// },
|
||||
// '/storage/v1/pushers': {
|
||||
// target: 'https://ht-dev.timerzz.com:20443/',
|
||||
// // target: 'http://192.168.31.55:2280/',
|
||||
// changeOrigin: true,
|
||||
// secure: false,
|
||||
// ws: true,
|
||||
// },
|
||||
// '/storage/v1/watchers': {
|
||||
// target: 'https://ht-dev.timerzz.com:20443/',
|
||||
// // target: 'http://192.168.31.55:2280/',
|
||||
// changeOrigin: true,
|
||||
// secure: false,
|
||||
// ws: true,
|
||||
// },
|
||||
// '/storage/v1/proxies': {
|
||||
// target: 'https://ht-dev.timerzz.com:20443/',
|
||||
// // target: 'http://192.168.31.55:2280/',
|
||||
// changeOrigin: true,
|
||||
// secure: false,
|
||||
// ws: true,
|
||||
// },
|
||||
'/api/v1': {
|
||||
target: 'https://ht.timerzz.com:20443/',
|
||||
// target: 'http://192.168.31.55:2280/',
|
||||
@ -64,12 +34,40 @@ export default defineConfig({
|
||||
secure: false,
|
||||
ws: true,
|
||||
},
|
||||
'/api/v2': {
|
||||
'/api/v2/sellers': {
|
||||
target: 'http://localhost:8081/',
|
||||
// target: 'http://192.168.31.55:2280/',
|
||||
changeOrigin: true,
|
||||
secure: false,
|
||||
ws: true,
|
||||
},
|
||||
'/api/v2/seller': {
|
||||
target: 'http://localhost:8083/',
|
||||
// target: 'http://192.168.31.55:2280/',
|
||||
changeOrigin: true,
|
||||
secure: false,
|
||||
ws: true,
|
||||
},
|
||||
'/api/v2/providers': {
|
||||
target: 'http://localhost:8080/',
|
||||
// target: 'http://192.168.31.55:2280/',
|
||||
changeOrigin: true,
|
||||
secure: false,
|
||||
ws: true,
|
||||
},
|
||||
'/api/v2/provider': {
|
||||
target: 'http://172.21.195.130:8082/',
|
||||
// target: 'http://192.168.31.55:2280/',
|
||||
changeOrigin: true,
|
||||
secure: false,
|
||||
ws: true,
|
||||
},
|
||||
'/api/v2/articles': {
|
||||
target: 'http://localhost:8085/',
|
||||
// target: 'http://192.168.31.55:2280/',
|
||||
changeOrigin: true,
|
||||
secure: false,
|
||||
ws: true,
|
||||
}
|
||||
},
|
||||
},
|
||||
|
Loading…
Reference in New Issue
Block a user