feat 添加供应商页面
All checks were successful
Build image / build (push) Successful in 29s

This commit is contained in:
timerzz 2024-08-27 14:26:41 +08:00
parent ed36aad468
commit 8eeb624ea2
9 changed files with 306 additions and 13 deletions

BIN
bun.lockb

Binary file not shown.

View File

@ -11,6 +11,7 @@
"dependencies": { "dependencies": {
"@ant-design/icons-vue": "^7.0.1", "@ant-design/icons-vue": "^7.0.1",
"ant-design-vue": "4.x", "ant-design-vue": "4.x",
"dayjs": "^1.11.13",
"mande": "^2.0.8", "mande": "^2.0.8",
"moment": "^2.30.1", "moment": "^2.30.1",
"radash": "^12.1.0", "radash": "^12.1.0",

View File

@ -1,14 +1,8 @@
import {mande} from "mande"; import {mande} from "mande";
import {queryRemoveZero} from "@/api/utils.js";
const product = mande('/api/v1/products') const product = mande('/api/v1/products')
const queryRemoveZero = (query)=>{
let q = {}
Object.keys(query).forEach(key => {
query[key] === 'undefined' || query[key] === null || query[key] === '' || (q[key] = query[key])
})
return q
}
export const ListProducts = (query) => { export const ListProducts = (query) => {
const q = queryRemoveZero(query) const q = queryRemoveZero(query)
return product.get({query:q}) return product.get({query:q})

25
src/api/provider.js Normal file
View File

@ -0,0 +1,25 @@
import {mande} from "mande";
import {queryRemoveZero} from "@/api/utils.js";
const product = mande('/api/v2/providers')
export const ListProviders = (q) => {
const query = queryRemoveZero(q)
return product.get({query})
}
export const CreateProvider = (provider)=>{
return product.post(provider)
}
export const UpdateProvider = (provider)=>{
return product.put(provider)
}
export const GetProvider = (id) =>{
return product.get(`/${id}`)
}
export const DeleteProvider = (id)=>{
return product.delete(`/${id}`)
}

7
src/api/utils.js Normal file
View File

@ -0,0 +1,7 @@
export const queryRemoveZero = (query)=>{
let q = {}
Object.keys(query).forEach(key => {
query[key] === 'undefined' || query[key] === null || query[key] === '' || (q[key] = query[key])
})
return q
}

View File

@ -30,6 +30,11 @@ const routes = [
name: 'pusher', name: 'pusher',
component: ()=>import('@/views/Pusher/index.vue') component: ()=>import('@/views/Pusher/index.vue')
}, },
{
path: '/provider',
name: 'provider',
component: ()=>import('@/views/Provider/index.vue')
}
] ]
const router = createRouter({ const router = createRouter({

View File

@ -0,0 +1,248 @@
<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">
<a-button type="primary" @click="onClickCreate" :disabled="loading">添加</a-button>
<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">
<template #bodyCell="{ column, record }">
<template v-if="column.dataIndex === 'mark'">
<span>{{record.mark || '-'}}</span>
</template>
<template v-else-if="column.dataIndex === 'name'">
</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>
</template>
</template>
</a-table>
</a-spin>
</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-spin :spinning="addModal.loading" :indicator="indicator">
<a-form
ref="formRef"
:model="addModal.data"
:label-col="{ span: 4 }"
:wrapper-col="{ span: 16 }"
autocomplete="off"
@finish="cleanAddModal"
@finishFailed="cleanAddModal"
>
<a-form-item label="标识" name="providerId" :rules="[{ required: true, message: '请填写供应商标识' }]">
<a-input v-model:value="addModal.data.providerId" placeholder="请输入标识比如us-coach-outlet" :disabled="addModal.edit"/>
</a-form-item>
<a-form-item label="名称" name="name" :rules="[{ required: true, message: '请填写供应商名称' }]">
<a-input v-model:value="addModal.data.name" placeholder="请输入名称比如美国coach奥莱"/>
</a-form-item>
<a-form-item label="抓取定时" name="config.ticker">
<a-time-picker valueFormat="HH:mm" v-model:value="addModal.data.config.ticker" format="HH:mm" :allowClear="false" />
</a-form-item>
<a-form-item label="汇率" name="config.exchangeRate">
<a-input-number v-model:value="addModal.data.config.exchangeRate"></a-input-number>
</a-form-item>
<a-form-item label="运费" name="config.freight">
<a-input-number v-model:value="addModal.data.config.freight"></a-input-number>
</a-form-item>
<a-form-item label="成本计算" >
<div class="flex flex-col space-y-4">
<div class="flex justify-between items-center">
<div class="text-sm">(原价originalPrice 汇率exchangeRate 运费freight)</div>
<a-button class="w-16" type="primary" @click="addModal.data.calculateProcess.push({ name: '', condition: '', process: ''})">添加</a-button>
</div>
<div class="flex space-x-4" v-for="(item, index) in addModal.data.calculateProcess" :key="index">
<a-form-item-rest><a-input v-model:value="addModal.data.calculateProcess[index].name" placeholder="名称"/></a-form-item-rest>
<a-form-item-rest><a-input v-model:value="addModal.data.calculateProcess[index].condition" placeholder="条件"/></a-form-item-rest>
<a-form-item-rest><a-input v-model:value="addModal.data.calculateProcess[index].process" placeholder="计算公式"/></a-form-item-rest>
<a-button danger type="primary" @click="addModal.data.calculateProcess.splice(index,1)">-</a-button>
</div>
</div>
</a-form-item>
<a-form-item label="备注" name="mark">
<a-textarea v-model:value="addModal.data.mark" />
</a-form-item>
</a-form>
</a-spin>
</a-modal>
</template>
<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";
const query = reactive({
keyword: "",
page: 1,
size: 10,
providerId: '',
id: '',
})
const loading = ref(false)
//
const data = reactive({
list: [],
total: 0
})
const list = () => {
loading.value = true
ListProviders(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
})
}
onMounted(()=>{
list()
})
const addModal = reactive({
visible: false,
data: {
providerId: '',
name: '',
mark: '',
config: {
ticker: '03:00',
exchangeRate: 7.2,
freight: 0,
},
calculateProcess: []
},
loading: false,
edit: false //
})
const formRef = ref()
const cleanAddModal=()=>{
addModal.data = {
providerId: '',
name: '',
mark: '',
config: {
ticker: '03:00',
exchangeRate: 7.2,
freight: 0,
},
calculateProcess: []
}
formRef.value.clearValidate()
addModal.visible = false
}
const handleOk = ()=>{
formRef.value.validateFields().then(res=>{
for(let process of addModal.data.calculateProcess){
if(process.name === '' || process.process === ''){
message.error("名称或计算公式不能为空")
return
}
}
const api = addModal.edit ? UpdateProvider : CreateProvider
api(addModal.data).then(res=>{
if (res.code !== 200) {
message.error(res.msg)
}else {
message.success(`${addModal.edit ? '编辑' : '添加'}成功`)
}
}).catch(err => {
message.error(`${addModal.edit ? '编辑' : '添加'}失败`)
console.log(err)
}).finally(()=>{
list()
cleanAddModal()
})
})
}
const indicator = h(LoadingOutlined, {
style: {
fontSize: '32px',
},
spin: true,
});
const columns = [
{
title: '标识',
dataIndex: 'providerId',
key: 'providerId',
width: 200
},
{
title: '名称',
dataIndex: 'name',
key: 'name',
width: 250
},
{
title: '备注',
dataIndex: 'mark',
key: 'mark',
},
{
title: '操作',
key: 'opt',
}
]
const onClickCreate=()=>{
addModal.edit = false
addModal.visible = true
}
const onClickUpdate=(id)=>{
loading.value = true
GetProvider(id).then(res=>{
if (res.code !== 200) {
message.error(res.msg)
}else {
addModal.data = res.data
addModal.edit = true
addModal.visible = true
}
}).finally(()=>{
loading.value = false
})
}
const onClickDelete=(id)=>{
loading.value = true
DeleteProvider(id).then(res=>{
if (res.code !== 200) {
message.error(res.msg)
}else {
message.success("删除成功")
}
}).finally(()=>{
loading.value = false
list()
})
}
</script>
<style scoped>
</style>

View File

@ -8,7 +8,7 @@
></a-menu> ></a-menu>
</template> </template>
<script setup> <script setup>
import {AccountBookOutlined, BellOutlined, DollarCircleOutlined} from "@ant-design/icons-vue"; import {AccountBookOutlined, BellOutlined, DollarCircleOutlined, HddOutlined} from "@ant-design/icons-vue";
import {h} from "vue"; import {h} from "vue";
import {useRouter} from "vue-router"; import {useRouter} from "vue-router";
@ -57,6 +57,12 @@ const items = [
label: '推送', label: '推送',
title: '推送', title: '推送',
}, },
{
key: 'provider',
icon: () => h(HddOutlined),
label: '供应商',
title: '供应商',
}
] ]

View File

@ -22,35 +22,35 @@ export default defineConfig({
server:{ server:{
open: true, open: true,
proxy: { proxy: {
// '/api/v1/products': { // '/storage/v1/products': {
// // target: 'https://ht.timerzz.com:20443/', // // target: 'https://ht.timerzz.com:20443/',
// target: 'http://192.168.31.163:31828/', // target: 'http://192.168.31.163:31828/',
// changeOrigin: true, // changeOrigin: true,
// secure: false, // secure: false,
// ws: true, // ws: true,
// }, // },
// '/api/v1/push': { // '/storage/v1/push': {
// target: 'https://ht-dev.timerzz.com:20443/', // target: 'https://ht-dev.timerzz.com:20443/',
// // target: 'http://192.168.31.55:2280/', // // target: 'http://192.168.31.55:2280/',
// changeOrigin: true, // changeOrigin: true,
// secure: false, // secure: false,
// ws: true, // ws: true,
// }, // },
// '/api/v1/pushers': { // '/storage/v1/pushers': {
// target: 'https://ht-dev.timerzz.com:20443/', // target: 'https://ht-dev.timerzz.com:20443/',
// // target: 'http://192.168.31.55:2280/', // // target: 'http://192.168.31.55:2280/',
// changeOrigin: true, // changeOrigin: true,
// secure: false, // secure: false,
// ws: true, // ws: true,
// }, // },
// '/api/v1/watchers': { // '/storage/v1/watchers': {
// target: 'https://ht-dev.timerzz.com:20443/', // target: 'https://ht-dev.timerzz.com:20443/',
// // target: 'http://192.168.31.55:2280/', // // target: 'http://192.168.31.55:2280/',
// changeOrigin: true, // changeOrigin: true,
// secure: false, // secure: false,
// ws: true, // ws: true,
// }, // },
// '/api/v1/proxies': { // '/storage/v1/proxies': {
// target: 'https://ht-dev.timerzz.com:20443/', // target: 'https://ht-dev.timerzz.com:20443/',
// // target: 'http://192.168.31.55:2280/', // // target: 'http://192.168.31.55:2280/',
// changeOrigin: true, // changeOrigin: true,
@ -64,6 +64,13 @@ export default defineConfig({
secure: false, secure: false,
ws: true, ws: true,
}, },
'/api/v2': {
target: 'http://localhost:8081/',
// target: 'http://192.168.31.55:2280/',
changeOrigin: true,
secure: false,
ws: true,
}
}, },
}, },
resolve: { resolve: {