Compare commits

...

3 Commits

Author SHA1 Message Date
c907d4c1f5 feat 添加查询库存工具
All checks were successful
Build image / build (push) Successful in 31s
2024-11-21 17:21:17 +08:00
f038069f58 feat 添加新的商品 2024-11-21 15:44:37 +08:00
225446f201 feat 供应商添加拉取库存 2024-11-21 14:35:50 +08:00
12 changed files with 331 additions and 25 deletions

View File

@ -42,6 +42,18 @@ export const FetchProviderArticles = (providerId)=>{
return provider.post(`/${providerId}/pull`)
}
export const FetchProviderArticle = (providerArticle)=>{
return provider.post(`/${providerArticle.providerId}/fetch/${providerArticle.id}`)
export const FetchProviderArticlePrice = (providerArticle)=>{
return provider.post(`/${providerArticle.providerId}/price/fetch/${providerArticle.id}`)
}
export const FetchProviderArticleAts = (providerArticle)=>{
return provider.post(`/${providerArticle.providerId}/ats/fetch/${providerArticle.skuID}`)
}
export const GetProviderArticleAts = (providerId, pid)=>{
return provider.get(`/${providerId}/ats/${pid}`)
}
export const FetchProviderArticleDetail = (providerId, pid)=>{
return provider.post(`/${providerId}/detail/fetch/${pid}`)
}

View File

@ -0,0 +1,115 @@
<template>
<a-modal v-model:open="open" title="添加商品" @ok="handleOk" centered @cancel="onCancel" :maskClosable="false" :keyboard="false" :confirmLoading="confirmLoading">
<a-form
:model="data"
:label-col="{ span: 6 }"
:wrapper-col="{ span: 16 }"
autocomplete="off"
ref="refForm"
>
<a-form-item
label="供应商"
name="providerId"
:rules="[{ required: true, message: '请选择供应商' }]"
>
<a-select v-model:value="data.providerId">
<a-select-option v-for="provider in providers" :value="provider.providerId">{{provider.name}}</a-select-option>
</a-select>
</a-form-item>
<a-form-item
label="产品id"
name="pid"
:rules="[{ required: true, message: '请输入产品id' }]"
>
<a-input v-model:value="data.pid" />
</a-form-item>
</a-form>
</a-modal>
</template>
<script setup>
import {computed, onMounted, reactive, ref} from "vue";
import {FetchProviderArticleDetail, FindProviders} from "@/api/provider.js";
import {message} from "ant-design-vue";
onMounted(()=>{
loadProviders()
})
const props = defineProps({
visible: {
type: Boolean,
default: false
}
})
const emits = defineEmits(['update:visible', 'reload'])
const open = computed({
get(){
return props.visible
},
set(value){
emits('update:visible', value)
}
})
const handleOk = ()=>{
if(refForm.value){
refForm.value.validate().then(values=>{
confirmLoading.value = true
FetchProviderArticleDetail(data.providerId, data.pid).then(res=>{
if(res.code === 200){
message.success("添加成功")
}else{
message.error(res.msg)
}
}).catch(err => {
message.error("添加失败")
console.log(err)
}).finally(()=>{
confirmLoading.value = false
emits('update:visible', false)
})
})
}else{
message.error('refForm未获取')
}
}
const data = reactive({
pid: '',
providerId: '',
})
const providers = ref([])
const loadProviders = ()=>{
FindProviders({}).then(res=>{
if(res.code !== 200) {
message.error('获取供应商列表失败')
console.log(res)
}else{
providers.value = res.data
}
}).catch(err=>{
message.error('获取供应商列表失败')
console.log(err)
})
}
const refForm = ref(null)
const onCancel=()=>{
if (refForm.value){
refForm.value.resetFields()
}
}
const confirmLoading = ref(false)
</script>
<style scoped>
</style>

View File

@ -8,7 +8,9 @@
</div>
</template>
<template #header>
<span class="text-lg">{{header}}</span>
<div>
<span class="text-lg">{{header}}</span>
</div>
</template>
<div class="flex flex-col space-y-8">
<div class="flex space-x-4 ">
@ -22,17 +24,25 @@
</div>
<div class="flex space-x-4 items-center">
<div class="w-full">当前售价$ {{provider.cost.originalPrice || '-'}}</div>
<a-popover v-if="provider.cost.calMark" trigger="hover">
<template #content>
<div v-if="provider.cost.calMark" class="w-full flex items-center space-x-4">
<a-popover trigger="hover">
<template #content>
<span class="whitespace-pre">
{{provider.cost.calMark}}
</span>
</template>
<div class="w-full">当前到手价 {{provider.cost.finalPrice || '-'}}</div>
</a-popover>
<div class="w-full" v-else>当前到手价 {{provider.cost.finalPrice || '-'}}</div>
<div class="w-full">
<a-button type="primary" @click="fetchArticle(provider)" :loading="fetchLoading">立即拉取</a-button>
</template>
<div>当前到手价 {{provider.cost.finalPrice.toFixed(2) || '-'}}</div>
</a-popover>
<a-button type="primary" @click="fetchArticle(provider)" :loading="fetchLoading">拉取价格</a-button>
</div>
<div class="w-full flex items-center space-x-4" v-else>
<div>当前到手价 {{provider.cost.finalPrice.toFixed(2) || '-'}}</div>
<a-button type="primary" @click="fetchArticle(provider)" :loading="fetchLoading">拉取价格</a-button>
</div>
<div class="w-full flex items-center space-x-4">
<div>库存{{provider.ast }}</div>
<a-button type="primary" @click="fetchArticleAts(provider)" :loading="fetchLoading">拉取库存</a-button>
</div>
</div>
<v-chart class="h-400px w-full" :option="options" />
@ -112,7 +122,7 @@ 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";
import {FetchProviderArticleAts, FetchProviderArticlePrice} from "@/api/provider.js";
use([GridComponent, LineChart, CanvasRenderer, TitleComponent , TooltipComponent, LegendComponent ])
@ -156,7 +166,7 @@ const fetchLoading = ref(false)
//
const fetchArticle = (pArticle) => {
fetchLoading.value = true
FetchProviderArticle(pArticle).then(res=>{
FetchProviderArticlePrice(pArticle).then(res=>{
if(res.code !== 200){
message.error(`拉取失败:${res.msg}`)
}else {
@ -167,8 +177,28 @@ const fetchArticle = (pArticle) => {
console.log(err)
}).finally(()=>{
fetchLoading.value = false
emits('fetched')
})
}
const fetchArticleAts = (pArticle) => {
fetchLoading.value = true
FetchProviderArticleAts(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
emits('fetched')
})
}
const emits = defineEmits(['fetched'])
</script>

View File

@ -0,0 +1,112 @@
<template>
<a-card bordered hoverable class="w-150px" @click="open=true">
<template #title>
<div class="text-xxl">
查询库存
</div>
</template>
<div class="flex space-x-2 items-center">
<CopyrightCircleOutlined class="text-lg" />
<span>库存</span>
</div>
</a-card>
<a-modal v-model:open="open" title="查询库存" @ok="handleOk" centered @cancel="onCancel" :maskClosable="false" :keyboard="false" :confirmLoading="confirmLoading">
<a-form
:model="data"
:label-col="{ span: 6 }"
:wrapper-col="{ span: 16 }"
autocomplete="off"
ref="refForm"
>
<a-form-item
label="供应商"
name="providerId"
:rules="[{ required: true, message: '请选择供应商' }]"
>
<a-select v-model:value="data.providerId">
<a-select-option v-for="provider in providers" :value="provider.providerId">{{provider.name}}</a-select-option>
</a-select>
</a-form-item>
<a-form-item
label="产品id"
name="pid"
:rules="[{ required: true, message: '请输入产品id' }]"
>
<a-input v-model:value="data.pid" />
</a-form-item>
</a-form>
</a-modal>
</template>
<script setup>
import {CopyrightCircleOutlined} from "@ant-design/icons-vue";
import {onMounted, reactive, ref} from "vue";
import {FindProviders, GetProviderArticleAts} from "@/api/provider.js";
import {message} from "ant-design-vue";
onMounted(()=>{
loadProviders()
})
const open = ref(false)
const handleOk = ()=>{
if(refForm.value){
refForm.value.validate().then(values=>{
confirmLoading.value = true
GetProviderArticleAts(data.providerId, data.pid).then(res=>{
if(res.code === 200){
message.success(`库存为${res.data}`)
}else{
message.error(res.msg)
}
}).catch(err => {
message.error("添加失败")
console.log(err)
}).finally(()=>{
confirmLoading.value = false
open.value = false
})
})
}else{
message.error('refForm未获取')
}
}
const data = reactive({
pid: '',
providerId: '',
})
const providers = ref([])
const loadProviders = ()=>{
FindProviders({}).then(res=>{
if(res.code !== 200) {
message.error('获取供应商列表失败')
console.log(res)
}else{
providers.value = res.data
}
}).catch(err=>{
message.error('获取供应商列表失败')
console.log(err)
})
}
const refForm = ref(null)
const onCancel=()=>{
if (refForm.value){
refForm.value.resetFields()
}
}
const confirmLoading = ref(false)
</script>
<style scoped>
</style>

View File

@ -8,4 +8,8 @@ html,body,#app {
::-webkit-scrollbar {
width: 0;
background: transparent;
}
.ant-collapse-header{
align-items: center !important;
}

View File

@ -1,6 +1,6 @@
import { createApp } from 'vue'
import './css/base.css'
import App from './App.vue'
import './css/base.css'
import 'virtual:uno.css'
import router from "@/routers/index.js";

View File

@ -1,7 +1,6 @@
import {createRouter, createWebHashHistory} from "vue-router";
import { routes } from 'vue-router/auto-routes';
const router = createRouter({
history: createWebHashHistory(),
routes:[

View File

@ -51,7 +51,10 @@
</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="item" :provider-dict="dict.providers"
:open="openTabKey.indexOf(item.providerId) > -1"
@fetched="load"
>
</provider-article-panel>
</a-collapse>

View File

@ -1,7 +1,9 @@
<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>
<a-button type="primary" @click="fetchArticle.visible=true">添加商品</a-button>
</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>
@ -46,6 +48,7 @@
</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>
<fetch-article-panel v-model:visible="fetchArticle.visible" @reload="list"></fetch-article-panel>
</template>
<script setup>
@ -54,6 +57,7 @@ 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";
import FetchArticlePanel from "@/componse/fetch-article/fetch-article-panel.vue";
const query = reactive({
keyword: "",
@ -203,6 +207,12 @@ const tableChange = (pagination, filters, sorter, { action, currentDataSource })
list()
}
//
const fetchArticle = reactive({
visible: false,
})
</script>
<style scoped>

View File

@ -8,7 +8,7 @@
></a-menu>
</template>
<script setup>
import {AccountBookOutlined, BellOutlined, DollarCircleOutlined, HddOutlined, DeploymentUnitOutlined} from "@ant-design/icons-vue";
import {AccountBookOutlined, BellOutlined, DollarCircleOutlined, HddOutlined, DeploymentUnitOutlined, ToolOutlined} from "@ant-design/icons-vue";
import {h} from "vue";
import {useRouter} from "vue-router";
@ -51,6 +51,12 @@ const items = [
icon: () => h(DeploymentUnitOutlined),
label: '销售商',
title: '销售商',
},
{
key: 'tools',
icon: () => h(ToolOutlined),
label: '工具',
title: '工具',
}
]

15
src/views/tools/index.vue Normal file
View File

@ -0,0 +1,15 @@
<template>
<div class="h-full m-4 bg-gray-50 rounded-2 shadow-lg p-8 flex flex-col justify-between space-y-4">
<a-flex wrap="wrap" >
<call-ats-tool></call-ats-tool>
</a-flex>
</div>
</template>
<script setup>
import CallAtsTool from "@/componse/tools/call-ats-tool.vue";
</script>
<style scoped>
</style>

View File

@ -35,36 +35,36 @@ export default defineConfig({
ws: true,
},
'/api/v2/sellers': {
target: 'http://localhost:8081/',
// target: 'http://192.168.31.55:2280/',
// target: 'http://localhost:8081/',
target: 'https://ht.timerzz.com:20443/',
changeOrigin: true,
secure: false,
ws: true,
},
'/api/v2/seller': {
target: 'http://localhost:8083/',
// target: 'http://192.168.31.55:2280/',
// target: 'http://localhost:8083/',
target: 'https://ht.timerzz.com:20443/',
changeOrigin: true,
secure: false,
ws: true,
},
'/api/v2/providers': {
target: 'http://localhost:8080/',
// target: 'http://192.168.31.55:2280/',
// target: 'https://ht.timerzz.com:20443/',
changeOrigin: true,
secure: false,
ws: true,
},
'/api/v2/provider': {
target: 'http://172.21.195.130:8082/',
// target: 'http://192.168.31.55:2280/',
// target: 'https://ht.timerzz.com:20443/',
changeOrigin: true,
secure: false,
ws: true,
},
'/api/v2/articles': {
// target: 'https://ht.timerzz.com:20443/',
target: 'http://localhost:8085/',
// target: 'http://192.168.31.55:2280/',
changeOrigin: true,
secure: false,
ws: true,